From a5d41c016b1304bd9ae2bad9f4883c674ce7a3e2 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Wed, 1 May 2024 07:23:59 +0000 Subject: [PATCH 001/134] union model --- .../diffusion/opensora/modeling_opensora.py | 590 ++++++++++++++ opensora/models/diffusion/opensora/modules.py | 739 ++++++++++++++++++ .../{ => utils}/transport/__init__.py | 0 .../{ => utils}/transport/integrators.py | 0 .../diffusion/{ => utils}/transport/path.py | 0 .../{ => utils}/transport/transport.py | 0 .../diffusion/{ => utils}/transport/utils.py | 0 .../diffusion => utils}/diffusion/__init__.py | 0 .../diffusion/diffusion_utils.py | 0 .../diffusion/gaussian_diffusion.py | 0 .../diffusion/gaussian_diffusion_t2v.py | 0 .../diffusion => utils}/diffusion/respace.py | 0 .../diffusion/timestep_sampler.py | 0 opensora/utils/utils.py | 6 + pyproject.toml | 4 +- 15 files changed, 1337 insertions(+), 2 deletions(-) create mode 100644 opensora/models/diffusion/opensora/modeling_opensora.py create mode 100644 opensora/models/diffusion/opensora/modules.py rename opensora/models/diffusion/{ => utils}/transport/__init__.py (100%) rename opensora/models/diffusion/{ => utils}/transport/integrators.py (100%) rename opensora/models/diffusion/{ => utils}/transport/path.py (100%) rename opensora/models/diffusion/{ => utils}/transport/transport.py (100%) rename opensora/models/diffusion/{ => utils}/transport/utils.py (100%) rename opensora/{models/diffusion => utils}/diffusion/__init__.py (100%) rename opensora/{models/diffusion => utils}/diffusion/diffusion_utils.py (100%) rename opensora/{models/diffusion => utils}/diffusion/gaussian_diffusion.py (100%) rename opensora/{models/diffusion => utils}/diffusion/gaussian_diffusion_t2v.py (100%) rename opensora/{models/diffusion => utils}/diffusion/respace.py (100%) rename opensora/{models/diffusion => utils}/diffusion/timestep_sampler.py (100%) diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py new file mode 100644 index 000000000..8e0d431df --- /dev/null +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -0,0 +1,590 @@ +import os +import numpy as np +from torch import nn +import torch +from einops import rearrange, repeat +from typing import Any, Dict, Optional, Tuple +from torch.nn import functional as F +from diffusers.models.transformer_2d import Transformer2DModelOutput +from diffusers.utils import is_torch_version, deprecate +from diffusers.configuration_utils import ConfigMixin, register_to_config +from diffusers.models.modeling_utils import ModelMixin +from diffusers.models.normalization import AdaLayerNormSingle +from diffusers.models.embeddings import PixArtAlphaTextProjection +from opensora.models.diffusion.opensora.modules import PatchEmbed3D, PatchEmbed2D, BasicTransformerBlock +from opensora.utils.utils import to_2tuple + +class OpenSoraT2V(ModelMixin, ConfigMixin): + """ + A 2D Transformer model for image-like data. + + Parameters: + num_attention_heads (`int`, *optional*, defaults to 16): The number of heads to use for multi-head attention. + attention_head_dim (`int`, *optional*, defaults to 88): The number of channels in each head. + in_channels (`int`, *optional*): + The number of channels in the input and output (specify if the input is **continuous**). + num_layers (`int`, *optional*, defaults to 1): The number of layers of Transformer blocks to use. + dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use. + cross_attention_dim (`int`, *optional*): The number of `encoder_hidden_states` dimensions to use. + sample_size (`int`, *optional*): The width of the latent images (specify if the input is **discrete**). + This is fixed during training since it is used to learn a number of position embeddings. + num_vector_embeds (`int`, *optional*): + The number of classes of the vector embeddings of the latent pixels (specify if the input is **discrete**). + Includes the class for the masked latent pixel. + activation_fn (`str`, *optional*, defaults to `"geglu"`): Activation function to use in feed-forward. + num_embeds_ada_norm ( `int`, *optional*): + The number of diffusion steps used during training. Pass if at least one of the norm_layers is + `AdaLayerNorm`. This is fixed during training since it is used to learn a number of embeddings that are + added to the hidden states. + + During inference, you can denoise for up to but not more steps than `num_embeds_ada_norm`. + attention_bias (`bool`, *optional*): + Configure if the `TransformerBlocks` attention should contain a bias parameter. + """ + + _supports_gradient_checkpointing = True + + @register_to_config + def __init__( + self, + num_attention_heads: int = 16, + attention_head_dim: int = 88, + in_channels: Optional[int] = None, + out_channels: Optional[int] = None, + num_layers: int = 1, + dropout: float = 0.0, + norm_num_groups: int = 32, + cross_attention_dim: Optional[int] = None, + attention_bias: bool = False, + sample_size: Optional[int] = None, + sample_size_t: Optional[int] = None, + num_vector_embeds: Optional[int] = None, + patch_size: Optional[int] = None, + patch_size_t: Optional[int] = None, + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + use_linear_projection: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_type: str = "layer_norm", # 'layer_norm', 'ada_norm', 'ada_norm_zero', 'ada_norm_single', 'ada_norm_continuous', 'layer_norm_i2vgen' + norm_elementwise_affine: bool = True, + norm_eps: float = 1e-5, + attention_type: str = "default", + caption_channels: int = None, + interpolation_scale: float = None, + interpolation_scale_t: float = None, + use_additional_conditions: Optional[bool] = None, + attention_mode: str = 'xformers', + ): + super().__init__() + + # Validate inputs. + if patch_size is not None: + if norm_type not in ["ada_norm", "ada_norm_zero", "ada_norm_single"]: + raise NotImplementedError( + f"Forward pass is not implemented when `patch_size` is not None and `norm_type` is '{norm_type}'." + ) + elif norm_type in ["ada_norm", "ada_norm_zero"] and num_embeds_ada_norm is None: + raise ValueError( + f"When using a `patch_size` and this `norm_type` ({norm_type}), `num_embeds_ada_norm` cannot be None." + ) + + # Set some common variables used across the board. + self.use_linear_projection = use_linear_projection + self.interpolation_scale = interpolation_scale + self.caption_channels = caption_channels + self.num_attention_heads = num_attention_heads + self.attention_head_dim = attention_head_dim + self.inner_dim = self.config.num_attention_heads * self.config.attention_head_dim + self.in_channels = in_channels + self.out_channels = in_channels if out_channels is None else out_channels + self.gradient_checkpointing = False + use_additional_conditions = False + # if use_additional_conditions is None: + # if norm_type == "ada_norm_single" and sample_size == 128: + # use_additional_conditions = True + # else: + # use_additional_conditions = False + self.use_additional_conditions = use_additional_conditions + + # 1. Transformer2DModel can process both standard continuous images of shape `(batch_size, num_channels, width, height)` as well as quantized image embeddings of shape `(batch_size, num_image_vectors)` + # Define whether input is continuous or discrete depending on configuration + assert in_channels is not None and patch_size is not None + + if norm_type == "layer_norm" and num_embeds_ada_norm is not None: + deprecation_message = ( + f"The configuration file of this model: {self.__class__} is outdated. `norm_type` is either not set or" + " incorrectly set to `'layer_norm'`. Make sure to set `norm_type` to `'ada_norm'` in the config." + " Please make sure to update the config accordingly as leaving `norm_type` might led to incorrect" + " results in future versions. If you have downloaded this checkpoint from the Hugging Face Hub, it" + " would be very nice if you could open a Pull request for the `transformer/config.json` file" + ) + deprecate("norm_type!=num_embeds_ada_norm", "1.0.0", deprecation_message, standard_warn=False) + norm_type = "ada_norm" + + # 2. Initialize the right blocks. + # Initialize the output blocks and other projection blocks when necessary. + self._init_patched_inputs(norm_type=norm_type) + + def _init_patched_inputs(self, norm_type): + assert self.config.sample_size_t is not None, "Transformer3DModel over patched input must provide sample_size_t" + assert self.config.sample_size is not None, "Transformer3DModel over patched input must provide sample_size" + assert not (self.config.sample_size_t == 1 and self.config.patch_size_t == 2), "Image do not need patchfy in t-dim" + + self.num_frames = self.config.sample_size_t + + self.config.sample_size = to_2tuple(self.config.sample_size) + self.height = self.config.sample_size[0] + self.width = self.config.sample_size[1] + self.patch_size_t = self.config.patch_size_t + self.patch_size = self.config.patch_size + sample_size_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t // 16 + interpolation_scale_t = ( + self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else max(sample_size_t, 1) + ) + interpolation_scale = ( + self.config.interpolation_scale if self.config.interpolation_scale is not None else max(self.config.sample_size[0] // 64, 1), + self.config.interpolation_scale if self.config.interpolation_scale is not None else max(self.config.sample_size[1] // 64, 1), + ) + + if self.config.sample_size_t > 1: + self.pos_embed = PatchEmbed3D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + else: + self.pos_embed = PatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + + self.transformer_blocks = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim, + self.config.num_attention_heads, + self.config.attention_head_dim, + dropout=self.config.dropout, + cross_attention_dim=self.config.cross_attention_dim, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.num_layers) + ] + ) + + if self.config.norm_type != "ada_norm_single": + self.norm_out = nn.LayerNorm(self.inner_dim, elementwise_affine=False, eps=1e-6) + self.proj_out_1 = nn.Linear(self.inner_dim, 2 * self.inner_dim) + self.proj_out_2 = nn.Linear( + self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels + ) + elif self.config.norm_type == "ada_norm_single": + self.norm_out = nn.LayerNorm(self.inner_dim, elementwise_affine=False, eps=1e-6) + self.scale_shift_table = nn.Parameter(torch.randn(2, self.inner_dim) / self.inner_dim**0.5) + self.proj_out = nn.Linear( + self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels + ) + + # PixArt-Alpha blocks. + self.adaln_single = None + if self.config.norm_type == "ada_norm_single": + # TODO(Sayak, PVP) clean this, for now we use sample size to determine whether to use + # additional conditions until we find better name + self.adaln_single = AdaLayerNormSingle( + self.inner_dim, use_additional_conditions=self.use_additional_conditions + ) + + self.caption_projection = None + if self.caption_channels is not None: + self.caption_projection = PixArtAlphaTextProjection( + in_features=self.caption_channels, hidden_size=self.inner_dim + ) + + def _set_gradient_checkpointing(self, module, value=False): + if hasattr(module, "gradient_checkpointing"): + module.gradient_checkpointing = value + + def forward( + self, + hidden_states: torch.Tensor, + encoder_hidden_states: Optional[torch.Tensor] = None, + timestep: Optional[torch.LongTensor] = None, + added_cond_kwargs: Dict[str, torch.Tensor] = None, + class_labels: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + attention_mask: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + use_image_num: Optional[int] = 0, + return_dict: bool = True, + ): + """ + The [`Transformer2DModel`] forward method. + + Args: + hidden_states (`torch.LongTensor` of shape `(batch size, num latent pixels)` if discrete, `torch.FloatTensor` of shape `(batch size, channel, height, width)` if continuous): + Input `hidden_states`. + encoder_hidden_states ( `torch.FloatTensor` of shape `(batch size, sequence len, embed dims)`, *optional*): + Conditional embeddings for cross attention layer. If not given, cross-attention defaults to + self-attention. + timestep ( `torch.LongTensor`, *optional*): + Used to indicate denoising step. Optional timestep to be applied as an embedding in `AdaLayerNorm`. + class_labels ( `torch.LongTensor` of shape `(batch size, num classes)`, *optional*): + Used to indicate class labels conditioning. Optional class labels to be applied as an embedding in + `AdaLayerZeroNorm`. + cross_attention_kwargs ( `Dict[str, Any]`, *optional*): + A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under + `self.processor` in + [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py). + attention_mask ( `torch.Tensor`, *optional*): + An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask + is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large + negative values to the attention scores corresponding to "discard" tokens. + encoder_attention_mask ( `torch.Tensor`, *optional*): + Cross-attention mask applied to `encoder_hidden_states`. Two formats supported: + + * Mask `(batch, sequence_length)` True = keep, False = discard. + * Bias `(batch, 1, sequence_length)` 0 = keep, -10000 = discard. + + If `ndim == 2`: will be interpreted as a mask, then converted into a bias consistent with the format + above. This bias will be added to the cross-attention scores. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] instead of a plain + tuple. + + Returns: + If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a + `tuple` where the first element is the sample tensor. + """ + batch_size, c, frame, h, w = hidden_states.shape + frame = frame - use_image_num # 21-4=17 + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + print.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. + # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. + # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. + # expects mask of shape: + # [batch, key_tokens] + # adds singleton query_tokens dimension: + # [batch, 1, key_tokens] + # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: + # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) + # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + if attention_mask is not None and attention_mask.ndim == 4: + # assume that mask is expressed as: + # (1 = keep, 0 = discard) + # convert mask into a bias that can be added to attention scores: + # (keep = +0, discard = -10000.0) + # b, frame+use_image_num, h, w -> a video with images + # b, 1, h, w -> only images + attention_mask = attention_mask.to(hidden_states.dtype) + attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w + attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w + + if attention_mask_vid.numel() > 0: + attention_mask_vid_first_frame = attention_mask_vid[:, :1].repeat(1, self.patch_size_t-1, 1, 1) + attention_mask_vid = torch.cat([attention_mask_vid_first_frame, attention_mask_vid], dim=1) + attention_mask_vid = attention_mask_vid.unsqueeze(1) # b 1 t h w + attention_mask_vid = F.max_pool3d(attention_mask_vid, kernel_size=(self.patch_size_t, self.patch_size, self.patch_size), + stride=(self.patch_size_t, self.patch_size, self.patch_size)) + attention_mask_vid = rearrange(attention_mask_vid, 'b 1 t h w -> (b 1) 1 (t h w)') + if attention_mask_img.numel() > 0: + attention_mask_img = F.max_pool2d(attention_mask_img, kernel_size=(self.patch_size, self.patch_size), stride=(self.patch_size, self.patch_size)) + attention_mask_img = rearrange(attention_mask_img, 'b i h w -> (b i) 1 (h w)') + + attention_mask_vid = (1 - attention_mask_vid.bool().to(hidden_states.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None + attention_mask_img = (1 - attention_mask_img.bool().to(hidden_states.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None + + # import ipdb;ipdb.set_trace() + if frame == 1 and use_image_num == 0: + attention_mask_img = attention_mask_vid + attention_mask_vid = None + # convert encoder_attention_mask to a bias the same way we do for attention_mask + if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: + # b, 1+use_image_num, l -> a video with images + # b, 1, l -> only images + encoder_attention_mask = (1 - encoder_attention_mask.to(hidden_states.dtype)) * -10000.0 + in_t = encoder_attention_mask.shape[1] + encoder_attention_mask_vid = encoder_attention_mask[:, :in_t-use_image_num] # b, 1, l + encoder_attention_mask_vid = rearrange(encoder_attention_mask_vid, 'b 1 l -> (b 1) 1 l') if encoder_attention_mask_vid.numel() > 0 else None + + encoder_attention_mask_img = encoder_attention_mask[:, in_t-use_image_num:] # b, use_image_num, l + encoder_attention_mask_img = rearrange(encoder_attention_mask_img, 'b i l -> (b i) 1 l') if encoder_attention_mask_img.numel() > 0 else None + + if frame == 1 and use_image_num == 0: + encoder_attention_mask_img = encoder_attention_mask_vid + encoder_attention_mask_vid = None + + # 1. Input + height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, \ + timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img = self._operate_on_patched_inputs( + hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num + ) + + frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy + # 2. Blocks + + for block in self.transformer_blocks: + if self.training and self.gradient_checkpointing: + + def create_custom_forward(module, return_dict=None): + def custom_forward(*inputs): + if return_dict is not None: + return module(*inputs, return_dict=return_dict) + else: + return module(*inputs) + + return custom_forward + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + if hidden_states_vid is not None: + hidden_states_vid = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + hidden_states_vid, + attention_mask_vid, + encoder_hidden_states_vid, + encoder_attention_mask_vid, + timestep_vid, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + if hidden_states_img is not None: + hidden_states_img = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + hidden_states_img, + attention_mask_img, + encoder_hidden_states_img, + encoder_attention_mask_img, + timestep_img, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + if hidden_states_vid is not None: + hidden_states_vid = block( + hidden_states_vid, + attention_mask=attention_mask_vid, + encoder_hidden_states=encoder_hidden_states_vid, + encoder_attention_mask=encoder_attention_mask_vid, + timestep=timestep_vid, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + if hidden_states_img is not None: + hidden_states_img = block( + hidden_states_img, + attention_mask=attention_mask_img, + encoder_hidden_states=encoder_hidden_states_img, + encoder_attention_mask=encoder_attention_mask_img, + timestep=timestep_img, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + + # 3. Output + output_vid, output_img = None, None + if hidden_states_vid is not None: + output_vid = self._get_output_for_patched_inputs( + hidden_states=hidden_states_vid, + timestep=timestep_vid, + class_labels=class_labels, + embedded_timestep=embedded_timestep_vid, + num_frames=frame, + height=height, + width=width, + ) # b c t h w + if hidden_states_img is not None: + output_img = self._get_output_for_patched_inputs( + hidden_states=hidden_states_img, + timestep=timestep_img, + class_labels=class_labels, + embedded_timestep=embedded_timestep_img, + num_frames=1, + height=height, + width=width, + ) # b c 1 h w + if use_image_num != 0: + output_img = rearrange(output_img, '(b i) c 1 h w -> b c i h w', i=use_image_num) + + if output_vid is not None and output_img is not None: + output = torch.cat([output_vid, output_img], dim=2) + elif output_vid is not None: + output = output_vid + elif output_img is not None: + output = output_img + + if not return_dict: + return (output,) + + return Transformer2DModelOutput(sample=output) + + + def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): + # batch_size = hidden_states.shape[0] + hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states, frame) + timestep_vid, timestep_img = None, None + embedded_timestep_vid, embedded_timestep_img = None, None + encoder_hidden_states_vid, encoder_hidden_states_img = None, None + + if self.adaln_single is not None: + if self.use_additional_conditions and added_cond_kwargs is None: + raise ValueError( + "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." + ) + timestep, embedded_timestep = self.adaln_single( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=hidden_states.dtype + ) # b 6d, b d + if hidden_states_vid is None: + timestep_img = timestep + embedded_timestep_img = embedded_timestep + else: + timestep_vid = timestep + embedded_timestep_vid = embedded_timestep + if hidden_states_img is not None: + timestep_img = repeat(timestep, 'b d -> (b i) d', i=use_image_num).contiguous() + embedded_timestep_img = repeat(embedded_timestep, 'b d -> (b i) d', i=use_image_num).contiguous() + + if self.caption_projection is not None: + encoder_hidden_states = self.caption_projection(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + # import ipdb;ipdb.set_trace() + if hidden_states_vid is None: + encoder_hidden_states_img = rearrange(encoder_hidden_states, 'b 1 l d -> (b 1) l d') + else: + encoder_hidden_states_vid = rearrange(encoder_hidden_states[:, :1], 'b 1 l d -> (b 1) l d') + if hidden_states_img is not None: + encoder_hidden_states_img = rearrange(encoder_hidden_states[:, 1:], 'b i l d -> (b i) l d') + + + return hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img + + + + def _get_output_for_patched_inputs( + self, hidden_states, timestep, class_labels, embedded_timestep, num_frames, height=None, width=None + ): + if self.config.norm_type != "ada_norm_single": + conditioning = self.transformer_blocks[0].norm1.emb( + timestep, class_labels, hidden_dtype=hidden_states.dtype + ) + shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] + hidden_states = self.proj_out_2(hidden_states) + elif self.config.norm_type == "ada_norm_single": + shift, scale = (self.scale_shift_table[None] + embedded_timestep[:, None]).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) + # Modulation + hidden_states = hidden_states * (1 + scale) + shift + hidden_states = self.proj_out(hidden_states) + hidden_states = hidden_states.squeeze(1) + + # unpatchify + if self.adaln_single is None: + height = width = int(hidden_states.shape[1] ** 0.5) + hidden_states = hidden_states.reshape( + shape=(-1, num_frames, height, width, self.patch_size_t, self.patch_size, self.patch_size, self.out_channels) + ) + hidden_states = torch.einsum("nthwopqc->nctohpwq", hidden_states) + output = hidden_states.reshape( + shape=(-1, self.out_channels, num_frames * self.patch_size_t, height * self.patch_size, width * self.patch_size) + ) + if output.shape[2] > 1: + output = output[:, :, 1:] + return output + + +def OpenSoraT2V_S_122(**kwargs): + return OpenSoraT2V(num_layers=28, attention_head_dim=64, num_attention_heads=18, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + +def OpenSoraT2V_S_222(**kwargs): + return OpenSoraT2V(num_layers=28, attention_head_dim=64, num_attention_heads=18, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + +OpenSora_models = { + "OpenSoraT2V-S/222": OpenSoraT2V_S_222, +} + +if __name__ == '__main__': + from opensora.models.ae import ae_channel_config, ae_stride_config + from opensora.models.ae import getae, getae_wrapper + from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper + + args = type('args', (), + { + 'ae': 'CausalVAEModel_4x8x8', + 'attention_mode': 'xformers', + 'use_rope': False, + 'model_max_length': 300, + 'max_image_size': 512, + 'num_frames': 1, + 'use_image_num': 0, + 'compress_kv_factor': 1 + } + ) + b = 2 + c = 4 + cond_c = 4096 + num_timesteps = 1000 + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) + if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: + args.video_length = video_length = (args.num_frames - 1) // ae_stride_t + 1 + else: + args.video_length = video_length = args.num_frames // ae_stride_t + + device = torch.device('cuda:0') + model = OpenSoraT2V_S_122(in_channels=4, + out_channels=8, + sample_size=latent_size, + sample_size_t=video_length).to(device) + # try: + # ckpt = torch.load(r"t2v.pt", map_location='cpu')['model'] + # model.load_state_dict(ckpt) + # except Exception as e: + # print(e) + print(model) + + x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w).to(device) + cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) + attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w)).to(device) # B L or B 1+num_images L + cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L + timestep = torch.randint(0, 1000, (b,), device=device) + model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) + with torch.no_grad(): + output = model(**model_kwargs) + # print(output) + + + + diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py new file mode 100644 index 000000000..9566ae7ef --- /dev/null +++ b/opensora/models/diffusion/opensora/modules.py @@ -0,0 +1,739 @@ +from einops import rearrange +from torch import nn +import torch +import numpy as np + +from einops import rearrange, repeat +from typing import Any, Dict, Optional, Tuple +from diffusers.utils.torch_utils import maybe_allow_in_graph +from typing import Any, Dict, Optional + +import torch +import torch.nn.functional as F +from torch import nn +import diffusers +from diffusers.utils import deprecate, logging +from diffusers.utils.torch_utils import maybe_allow_in_graph +from diffusers.models.attention import FeedForward, GatedSelfAttentionDense +from diffusers.models.attention_processor import Attention as Attention_ +from diffusers.models.embeddings import SinusoidalPositionalEmbedding +from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm + +logger = logging.get_logger(__name__) + +def get_3d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid height and width return: pos_embed: [grid_size*grid_size, embed_dim] or + [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + grid_t = np.arange(grid_size[0], dtype=np.float32) / (grid_size[0] / base_size[0]) / interpolation_scale[0] + grid_h = np.arange(grid_size[1], dtype=np.float32) / (grid_size[1] / base_size[1]) / interpolation_scale[1] + grid_w = np.arange(grid_size[2], dtype=np.float32) / (grid_size[2] / base_size[2]) / interpolation_scale[2] + grid = np.meshgrid(grid_w, grid_h, grid_t) # here w goes first + grid = np.stack(grid, axis=0) + + grid = grid.reshape([3, 1, grid_size[2], grid_size[1], grid_size[0]]) + pos_embed = get_3d_sincos_pos_embed_from_grid(embed_dim, grid) + # import ipdb;ipdb.set_trace() + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + + +def get_3d_sincos_pos_embed_from_grid(embed_dim, grid): + if embed_dim % 3 != 0: + raise ValueError("embed_dim must be divisible by 3") + + # import ipdb;ipdb.set_trace() + # use 1/3 of dimensions to encode grid_t/h/w + emb_t = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[0]) # (T*H*W, D/3) + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[1]) # (T*H*W, D/3) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[2]) # (T*H*W, D/3) + + emb = np.concatenate([emb_t, emb_h, emb_w], axis=1) # (T*H*W, D) + return emb + + +def get_2d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid height and width return: pos_embed: [grid_size*grid_size, embed_dim] or + [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid_h = np.arange(grid_size[0], dtype=np.float32) / (grid_size[0] / base_size[0]) / interpolation_scale[0] + grid_w = np.arange(grid_size[1], dtype=np.float32) / (grid_size[1] / base_size[1]) / interpolation_scale[1] + grid = np.meshgrid(grid_w, grid_h) # here w goes first + grid = np.stack(grid, axis=0) + + grid = grid.reshape([2, 1, grid_size[1], grid_size[0]]) + pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + + +def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + # use 1/3 of dimensions to encode grid_t/h/w + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2) + + emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) + return emb + +def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): + """ + embed_dim: output dimension for each position pos: a list of positions to be encoded: size (M,) out: (M, D) + """ + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + omega = np.arange(embed_dim // 2, dtype=np.float64) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + + pos = pos.reshape(-1) # (M,) + out = np.einsum("m,d->md", pos, omega) # (M, D/2), outer product + + emb_sin = np.sin(out) # (M, D/2) + emb_cos = np.cos(out) # (M, D/2) + + emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) + return emb + + +class PatchEmbed2D(nn.Module): + """2D Image to Patch Embedding but with 3D position embedding""" + + def __init__( + self, + num_frames=1, + height=224, + width=224, + patch_size_t=1, + patch_size=16, + in_channels=3, + embed_dim=768, + layer_norm=False, + flatten=True, + bias=True, + interpolation_scale=(1, 1), + interpolation_scale_t=1, + ): + super().__init__() + assert num_frames == 1 + self.flatten = flatten + self.layer_norm = layer_norm + + self.proj = nn.Conv2d( + in_channels, embed_dim, kernel_size=(patch_size, patch_size), stride=(patch_size, patch_size), bias=bias + ) + if layer_norm: + self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) + else: + self.norm = None + + self.patch_size_t = patch_size_t + self.patch_size = patch_size + # See: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 + + self.height, self.width = height // patch_size, width // patch_size + self.base_size = (height // patch_size, width // patch_size) + self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + pos_embed = get_2d_sincos_pos_embed( + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) + + + def forward(self, latent, num_frames): + # import ipdb;ipdb.set_trace() + video_latent = None + # b c 1 h w + assert latent.shape[-3] == 1 and num_frames == 1 + height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size + + + latent = latent.squeeze(2) # b c 1 h w -> b c h w + latent = self.proj(latent) + + if self.flatten: + latent = latent.flatten(2).transpose(1, 2) # BCHW -> BNC + if self.layer_norm: + latent = self.norm(latent) + + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed + image_latent = (latent + pos_embed).to(latent.dtype) + return video_latent, image_latent + + +class PatchEmbed3D(nn.Module): + """3D Video and 2D Image to Patch Embedding""" + + def __init__( + self, + num_frames=17, + height=224, + width=224, + patch_size_t=2, + patch_size=16, + in_channels=3, + embed_dim=768, + layer_norm=False, + flatten=True, + bias=True, + interpolation_scale=(1, 1), + interpolation_scale_t=1, + ): + super().__init__() + assert num_frames > 1 + self.flatten = flatten + self.layer_norm = layer_norm + + self.proj = nn.Conv3d( + in_channels, embed_dim, kernel_size=(patch_size_t, patch_size, patch_size), stride=(patch_size_t, patch_size, patch_size), bias=bias + ) + if layer_norm: + self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) + else: + self.norm = None + + self.patch_size_t = patch_size_t + self.patch_size = patch_size + # See: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 + + self.height, self.width = height // patch_size, width // patch_size + if num_frames % 2 == 1: + self.num_frames = (num_frames - 1) // patch_size_t + 1 + self.base_size = ((num_frames - 1) // patch_size_t + 1, height // patch_size, width // patch_size) + else: + self.num_frames = num_frames // patch_size_t + self.base_size = (num_frames // patch_size_t, height // patch_size, width // patch_size) + self.interpolation_scale = (interpolation_scale_t, interpolation_scale[0], interpolation_scale[1]) + pos_embed_video = get_3d_sincos_pos_embed( + embed_dim, (self.num_frames, self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed_video", torch.from_numpy(pos_embed_video).float().unsqueeze(0), persistent=False) + + pos_embed_image = get_3d_sincos_pos_embed( + embed_dim, (1, self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed_image", torch.from_numpy(pos_embed_image).float().unsqueeze(0), persistent=False) + + + def forward(self, latent, num_frames): + # b c v+i h w + # import ipdb;ipdb.set_trace() + height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size + # bcvhw, bcihw + video_latent, image_latent = latent[:, :, :num_frames], latent[:, :, num_frames:] + if num_frames % 2 == 1: + num_frames = (num_frames - 1) // self.patch_size_t + 1 + else: + num_frames = num_frames // self.patch_size_t + + first_frame_repeat = video_latent[:, :, :1].repeat(1, 1, self.patch_size_t-1, 1, 1) + video_latent = torch.cat([first_frame_repeat, video_latent], dim=2) + video_latent = self.proj(video_latent) + + if image_latent.numel() > 0: + image_latent = image_latent.repeat_interleave(self.patch_size, dim=2) # b c i h w -> b c 2i h w + image_latent = self.proj(image_latent) # b c 2i h w -> b c i h w + + if self.flatten: + video_latent = rearrange(video_latent, 'b c t h w -> b (t h w) c') + if image_latent.numel() > 0: + image_latent = rearrange(image_latent, 'b c i h w -> (b i) (h w) c') + if self.layer_norm: + video_latent = self.norm(video_latent) + if image_latent.numel() > 0: + image_latent = self.norm(image_latent) + + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.num_frames != num_frames or self.height != height or self.width != width: + pos_embed_video = get_3d_sincos_pos_embed( + embed_dim=self.pos_embed_video.shape[-1], + grid_size=(num_frames, height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed_video = torch.from_numpy(pos_embed_video) + pos_embed_video = pos_embed_video.float().unsqueeze(0).to(latent.device) + else: + pos_embed_video = self.pos_embed_video + video_latent = (video_latent + pos_embed_video).to(latent.dtype) + + if image_latent.numel() > 0: + if self.height != height or self.width != width: + pos_embed_image = get_3d_sincos_pos_embed( + embed_dim=self.pos_embed_image.shape[-1], + grid_size=(1, height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed_image = torch.from_numpy(pos_embed_image) + pos_embed_image = pos_embed_image.float().unsqueeze(0).to(latent.device) + else: + pos_embed_image = self.pos_embed_image + image_latent = (image_latent + pos_embed_image).to(latent.dtype) + else: + image_latent = None + return video_latent, image_latent + + +class Attention(Attention_): + def __init__(self, attention_mode, **kwags): + processor = AttnProcessor2_0(attention_mode) + super().__init__(processor=processor, **kwags) + +class AttnProcessor2_0: + r""" + Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). + """ + + def __init__(self, attention_mode): + self.attention_mode = attention_mode + if not hasattr(F, "scaled_dot_product_attention"): + raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + def __call__( + self, + attn: Attention, + hidden_states: torch.FloatTensor, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + temb: Optional[torch.FloatTensor] = None, + *args, + **kwargs, + ) -> torch.FloatTensor: + if len(args) > 0 or kwargs.get("scale", None) is not None: + deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." + deprecate("scale", "1.0.0", deprecation_message) + + residual = hidden_states + if attn.spatial_norm is not None: + hidden_states = attn.spatial_norm(hidden_states, temb) + + input_ndim = hidden_states.ndim + + if input_ndim == 4: + batch_size, channel, height, width = hidden_states.shape + hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) + + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + + if attention_mask is not None: + attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + + if attn.group_norm is not None: + hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) + + query = attn.to_q(hidden_states) + + if encoder_hidden_states is None: + encoder_hidden_states = hidden_states + elif attn.norm_cross: + encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + + key = attn.to_k(encoder_hidden_states) + value = attn.to_v(encoder_hidden_states) + + inner_dim = key.shape[-1] + head_dim = inner_dim // attn.heads + + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + if self.attention_mode == 'flash': + assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + + # linear proj + hidden_states = attn.to_out[0](hidden_states) + # dropout + hidden_states = attn.to_out[1](hidden_states) + + if input_ndim == 4: + hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) + + if attn.residual_connection: + hidden_states = hidden_states + residual + + hidden_states = hidden_states / attn.rescale_output_factor + + return hidden_states + + + + + +@maybe_allow_in_graph +class BasicTransformerBlock(nn.Module): + r""" + A basic Transformer block. + + Parameters: + dim (`int`): The number of channels in the input and output. + num_attention_heads (`int`): The number of heads to use for multi-head attention. + attention_head_dim (`int`): The number of channels in each head. + dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use. + cross_attention_dim (`int`, *optional*): The size of the encoder_hidden_states vector for cross attention. + activation_fn (`str`, *optional*, defaults to `"geglu"`): Activation function to be used in feed-forward. + num_embeds_ada_norm (: + obj: `int`, *optional*): The number of diffusion steps used during training. See `Transformer2DModel`. + attention_bias (: + obj: `bool`, *optional*, defaults to `False`): Configure if the attentions should contain a bias parameter. + only_cross_attention (`bool`, *optional*): + Whether to use only cross-attention layers. In this case two cross attention layers are used. + double_self_attention (`bool`, *optional*): + Whether to use two self-attention layers. In this case no cross attention layers are used. + upcast_attention (`bool`, *optional*): + Whether to upcast the attention computation to float32. This is useful for mixed precision training. + norm_elementwise_affine (`bool`, *optional*, defaults to `True`): + Whether to use learnable elementwise affine parameters for normalization. + norm_type (`str`, *optional*, defaults to `"layer_norm"`): + The normalization layer to use. Can be `"layer_norm"`, `"ada_norm"` or `"ada_norm_zero"`. + final_dropout (`bool` *optional*, defaults to False): + Whether to apply a final dropout after the last feed-forward layer. + attention_type (`str`, *optional*, defaults to `"default"`): + The type of attention to use. Can be `"default"` or `"gated"` or `"gated-text-image"`. + positional_embeddings (`str`, *optional*, defaults to `None`): + The type of positional embeddings to apply to. + num_positional_embeddings (`int`, *optional*, defaults to `None`): + The maximum number of positional embeddings to apply. + """ + + def __init__( + self, + dim: int, + num_attention_heads: int, + attention_head_dim: int, + dropout=0.0, + cross_attention_dim: Optional[int] = None, + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + attention_bias: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_elementwise_affine: bool = True, + norm_type: str = "layer_norm", # 'layer_norm', 'ada_norm', 'ada_norm_zero', 'ada_norm_single', 'ada_norm_continuous', 'layer_norm_i2vgen' + norm_eps: float = 1e-5, + final_dropout: bool = False, + attention_type: str = "default", + positional_embeddings: Optional[str] = None, + num_positional_embeddings: Optional[int] = None, + ada_norm_continous_conditioning_embedding_dim: Optional[int] = None, + ada_norm_bias: Optional[int] = None, + ff_inner_dim: Optional[int] = None, + ff_bias: bool = True, + attention_out_bias: bool = True, + attention_mode: str = "xformers", + ): + super().__init__() + self.only_cross_attention = only_cross_attention + + # We keep these boolean flags for backward-compatibility. + self.use_ada_layer_norm_zero = (num_embeds_ada_norm is not None) and norm_type == "ada_norm_zero" + self.use_ada_layer_norm = (num_embeds_ada_norm is not None) and norm_type == "ada_norm" + self.use_ada_layer_norm_single = norm_type == "ada_norm_single" + self.use_layer_norm = norm_type == "layer_norm" + self.use_ada_layer_norm_continuous = norm_type == "ada_norm_continuous" + + if norm_type in ("ada_norm", "ada_norm_zero") and num_embeds_ada_norm is None: + raise ValueError( + f"`norm_type` is set to {norm_type}, but `num_embeds_ada_norm` is not defined. Please make sure to" + f" define `num_embeds_ada_norm` if setting `norm_type` to {norm_type}." + ) + + self.norm_type = norm_type + self.num_embeds_ada_norm = num_embeds_ada_norm + + if positional_embeddings and (num_positional_embeddings is None): + raise ValueError( + "If `positional_embedding` type is defined, `num_positition_embeddings` must also be defined." + ) + + if positional_embeddings == "sinusoidal": + self.pos_embed = SinusoidalPositionalEmbedding(dim, max_seq_length=num_positional_embeddings) + else: + self.pos_embed = None + + # Define 3 blocks. Each block has its own normalization layer. + # 1. Self-Attn + if norm_type == "ada_norm": + self.norm1 = AdaLayerNorm(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_zero": + self.norm1 = AdaLayerNormZero(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_continuous": + self.norm1 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "rms_norm", + ) + else: + self.norm1 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + + self.attn1 = Attention( + query_dim=dim, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + cross_attention_dim=cross_attention_dim if only_cross_attention else None, + upcast_attention=upcast_attention, + out_bias=attention_out_bias, + attention_mode=attention_mode, + ) + + # 2. Cross-Attn + if cross_attention_dim is not None or double_self_attention: + # We currently only use AdaLayerNormZero for self attention where there will only be one attention block. + # I.e. the number of returned modulation chunks from AdaLayerZero would not make sense if returned during + # the second cross attention block. + if norm_type == "ada_norm": + self.norm2 = AdaLayerNorm(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_continuous": + self.norm2 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "rms_norm", + ) + else: + self.norm2 = nn.LayerNorm(dim, norm_eps, norm_elementwise_affine) + + self.attn2 = Attention( + query_dim=dim, + cross_attention_dim=cross_attention_dim if not double_self_attention else None, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + upcast_attention=upcast_attention, + out_bias=attention_out_bias, + attention_mode=attention_mode, + ) # is self-attn if encoder_hidden_states is none + else: + self.norm2 = None + self.attn2 = None + + # 3. Feed-forward + if norm_type == "ada_norm_continuous": + self.norm3 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "layer_norm", + ) + + elif norm_type in ["ada_norm_zero", "ada_norm", "layer_norm", "ada_norm_continuous"]: + self.norm3 = nn.LayerNorm(dim, norm_eps, norm_elementwise_affine) + elif norm_type == "layer_norm_i2vgen": + self.norm3 = None + + self.ff = FeedForward( + dim, + dropout=dropout, + activation_fn=activation_fn, + final_dropout=final_dropout, + inner_dim=ff_inner_dim, + bias=ff_bias, + ) + + # 4. Fuser + if attention_type == "gated" or attention_type == "gated-text-image": + self.fuser = GatedSelfAttentionDense(dim, cross_attention_dim, num_attention_heads, attention_head_dim) + + # 5. Scale-shift for PixArt-Alpha. + if norm_type == "ada_norm_single": + self.scale_shift_table = nn.Parameter(torch.randn(6, dim) / dim**0.5) + + # let chunk size default to None + self._chunk_size = None + self._chunk_dim = 0 + + def set_chunk_feed_forward(self, chunk_size: Optional[int], dim: int = 0): + # Sets chunk feed-forward + self._chunk_size = chunk_size + self._chunk_dim = dim + + def forward( + self, + hidden_states: torch.FloatTensor, + attention_mask: Optional[torch.FloatTensor] = None, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + encoder_attention_mask: Optional[torch.FloatTensor] = None, + timestep: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + class_labels: Optional[torch.LongTensor] = None, + added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None, + ) -> torch.FloatTensor: + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + logger.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + + # Notice that normalization is always applied before the real computation in the following blocks. + # 0. Self-Attention + batch_size = hidden_states.shape[0] + + if self.norm_type == "ada_norm": + norm_hidden_states = self.norm1(hidden_states, timestep) + elif self.norm_type == "ada_norm_zero": + norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.norm1( + hidden_states, timestep, class_labels, hidden_dtype=hidden_states.dtype + ) + elif self.norm_type in ["layer_norm", "layer_norm_i2vgen"]: + norm_hidden_states = self.norm1(hidden_states) + elif self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm1(hidden_states, added_cond_kwargs["pooled_text_emb"]) + elif self.norm_type == "ada_norm_single": + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( + self.scale_shift_table[None] + timestep.reshape(batch_size, 6, -1) + ).chunk(6, dim=1) + norm_hidden_states = self.norm1(hidden_states) + norm_hidden_states = norm_hidden_states * (1 + scale_msa) + shift_msa + norm_hidden_states = norm_hidden_states.squeeze(1) + else: + raise ValueError("Incorrect norm used") + + if self.pos_embed is not None: + norm_hidden_states = self.pos_embed(norm_hidden_states) + + # 1. Prepare GLIGEN inputs + cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} + gligen_kwargs = cross_attention_kwargs.pop("gligen", None) + + attn_output = self.attn1( + norm_hidden_states, + encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, + attention_mask=attention_mask, + **cross_attention_kwargs, + ) + if self.norm_type == "ada_norm_zero": + attn_output = gate_msa.unsqueeze(1) * attn_output + elif self.norm_type == "ada_norm_single": + attn_output = gate_msa * attn_output + + hidden_states = attn_output + hidden_states + if hidden_states.ndim == 4: + hidden_states = hidden_states.squeeze(1) + + # 1.2 GLIGEN Control + if gligen_kwargs is not None: + hidden_states = self.fuser(hidden_states, gligen_kwargs["objs"]) + + # 3. Cross-Attention + if self.attn2 is not None: + if self.norm_type == "ada_norm": + norm_hidden_states = self.norm2(hidden_states, timestep) + elif self.norm_type in ["ada_norm_zero", "layer_norm", "layer_norm_i2vgen"]: + norm_hidden_states = self.norm2(hidden_states) + elif self.norm_type == "ada_norm_single": + # For PixArt norm2 isn't applied here: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L70C1-L76C103 + norm_hidden_states = hidden_states + elif self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm2(hidden_states, added_cond_kwargs["pooled_text_emb"]) + else: + raise ValueError("Incorrect norm") + + if self.pos_embed is not None and self.norm_type != "ada_norm_single": + norm_hidden_states = self.pos_embed(norm_hidden_states) + + attn_output = self.attn2( + norm_hidden_states, + encoder_hidden_states=encoder_hidden_states, + attention_mask=encoder_attention_mask, + **cross_attention_kwargs, + ) + hidden_states = attn_output + hidden_states + + # 4. Feed-forward + # i2vgen doesn't have this norm 🤷‍♂️ + if self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm3(hidden_states, added_cond_kwargs["pooled_text_emb"]) + elif not self.norm_type == "ada_norm_single": + norm_hidden_states = self.norm3(hidden_states) + + if self.norm_type == "ada_norm_zero": + norm_hidden_states = norm_hidden_states * (1 + scale_mlp[:, None]) + shift_mlp[:, None] + + if self.norm_type == "ada_norm_single": + norm_hidden_states = self.norm2(hidden_states) + norm_hidden_states = norm_hidden_states * (1 + scale_mlp) + shift_mlp + + if self._chunk_size is not None: + # "feed_forward_chunk_size" can be used to save memory + ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) + else: + ff_output = self.ff(norm_hidden_states) + + if self.norm_type == "ada_norm_zero": + ff_output = gate_mlp.unsqueeze(1) * ff_output + elif self.norm_type == "ada_norm_single": + ff_output = gate_mlp * ff_output + + hidden_states = ff_output + hidden_states + if hidden_states.ndim == 4: + hidden_states = hidden_states.squeeze(1) + + return hidden_states diff --git a/opensora/models/diffusion/transport/__init__.py b/opensora/models/diffusion/utils/transport/__init__.py similarity index 100% rename from opensora/models/diffusion/transport/__init__.py rename to opensora/models/diffusion/utils/transport/__init__.py diff --git a/opensora/models/diffusion/transport/integrators.py b/opensora/models/diffusion/utils/transport/integrators.py similarity index 100% rename from opensora/models/diffusion/transport/integrators.py rename to opensora/models/diffusion/utils/transport/integrators.py diff --git a/opensora/models/diffusion/transport/path.py b/opensora/models/diffusion/utils/transport/path.py similarity index 100% rename from opensora/models/diffusion/transport/path.py rename to opensora/models/diffusion/utils/transport/path.py diff --git a/opensora/models/diffusion/transport/transport.py b/opensora/models/diffusion/utils/transport/transport.py similarity index 100% rename from opensora/models/diffusion/transport/transport.py rename to opensora/models/diffusion/utils/transport/transport.py diff --git a/opensora/models/diffusion/transport/utils.py b/opensora/models/diffusion/utils/transport/utils.py similarity index 100% rename from opensora/models/diffusion/transport/utils.py rename to opensora/models/diffusion/utils/transport/utils.py diff --git a/opensora/models/diffusion/diffusion/__init__.py b/opensora/utils/diffusion/__init__.py similarity index 100% rename from opensora/models/diffusion/diffusion/__init__.py rename to opensora/utils/diffusion/__init__.py diff --git a/opensora/models/diffusion/diffusion/diffusion_utils.py b/opensora/utils/diffusion/diffusion_utils.py similarity index 100% rename from opensora/models/diffusion/diffusion/diffusion_utils.py rename to opensora/utils/diffusion/diffusion_utils.py diff --git a/opensora/models/diffusion/diffusion/gaussian_diffusion.py b/opensora/utils/diffusion/gaussian_diffusion.py similarity index 100% rename from opensora/models/diffusion/diffusion/gaussian_diffusion.py rename to opensora/utils/diffusion/gaussian_diffusion.py diff --git a/opensora/models/diffusion/diffusion/gaussian_diffusion_t2v.py b/opensora/utils/diffusion/gaussian_diffusion_t2v.py similarity index 100% rename from opensora/models/diffusion/diffusion/gaussian_diffusion_t2v.py rename to opensora/utils/diffusion/gaussian_diffusion_t2v.py diff --git a/opensora/models/diffusion/diffusion/respace.py b/opensora/utils/diffusion/respace.py similarity index 100% rename from opensora/models/diffusion/diffusion/respace.py rename to opensora/utils/diffusion/respace.py diff --git a/opensora/models/diffusion/diffusion/timestep_sampler.py b/opensora/utils/diffusion/timestep_sampler.py similarity index 100% rename from opensora/models/diffusion/diffusion/timestep_sampler.py rename to opensora/utils/diffusion/timestep_sampler.py diff --git a/opensora/utils/utils.py b/opensora/utils/utils.py index ba94b6aa1..af3daf334 100644 --- a/opensora/utils/utils.py +++ b/opensora/utils/utils.py @@ -15,6 +15,7 @@ from torch import inf from PIL import Image from typing import Union, Iterable +import collections from collections import OrderedDict from torch.utils.tensorboard import SummaryWriter @@ -32,6 +33,11 @@ _tensor_or_tensors = Union[torch.Tensor, Iterable[torch.Tensor]] +def to_2tuple(x): + if isinstance(x, collections.abc.Iterable): + return x + return (x, x) + def find_model(model_name): """ Finds a pre-trained Latte model, downloading it if necessary. Alternatively, loads a model from a local path. diff --git a/pyproject.toml b/pyproject.toml index 5a3624fd0..65e252411 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,14 +14,14 @@ classifiers = [ ] dependencies = [ "torch==2.1.0", "torchvision==0.16.0", - "transformers==4.39.1", "accelerate==0.28.0", + "transformers==4.40.1", "accelerate==0.29.3", "tokenizers==0.19.1", "diffusers@git+https://github.com/huggingface/diffusers", "albumentations==1.4.0", "av==11.0.0", "decord==0.6.0", "einops==0.7.0", "fastapi==0.110.0", "gdown==5.1.0", "h5py==3.10.0", "idna==3.6", 'imageio==2.34.0', "matplotlib==3.7.5", "numpy==1.24.4", "omegaconf==2.1.1", "opencv-python==4.9.0.80", "opencv-python-headless==4.9.0.80", "pandas==2.0.3", "pillow==10.2.0", "pydub==0.25.1", "pytorch-lightning==2.2.1", "pytorchvideo==0.1.5", "PyYAML==6.0.1", "regex==2023.12.25", "requests==2.31.0", "scikit-learn==1.3.2", "scipy==1.10.1", "six==1.16.0", "test-tube==0.7.5", "timm==0.9.16", "torchdiffeq==0.2.3", "torchmetrics==1.3.2", "tqdm==4.66.2", "urllib3==2.2.1", "uvicorn==0.27.1", - "diffusers==0.27.2", "scikit-video==1.1.11", "imageio-ffmpeg==0.4.9", "sentencepiece==0.1.99", "beautifulsoup4==4.12.3", + "scikit-video==1.1.11", "imageio-ffmpeg==0.4.9", "sentencepiece==0.1.99", "beautifulsoup4==4.12.3", "ftfy==6.1.3", "moviepy==1.0.3", "wandb==0.16.3", "tensorboard==2.14.0", "pydantic==2.6.4", "gradio==4.0.0", "xformers==0.0.22.post7" ] From f07d16634fd93ff6389fe3b88d11eaa68f94d40b Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 2 May 2024 10:55:16 +0800 Subject: [PATCH 002/134] update dataset, train_t2v, mask_loss --- opensora/dataset/__init__.py | 1 - opensora/dataset/feature_datasets.py | 213 ----- opensora/dataset/t2v_datasets.py | 98 ++- opensora/models/diffusion/__init__.py | 4 +- .../diffusion/opensora/modeling_opensora.py | 33 +- opensora/sample/pipeline_opensora.py | 822 +++++++++++++++++ opensora/sample/sample_t2v_pixsig.py | 147 ++++ opensora/train/train_t2v.py | 208 ++--- opensora/train/train_t2v_feature.py | 787 ----------------- opensora/train/train_t2v_t5_feature.py | 825 ------------------ scripts/text_condition/sample_image.sh | 2 +- .../train_videoae_65x1024x1024.sh | 39 - .../train_videoae_65x512x512_d64.sh | 39 - .../train_videoae_65x512x512_rope.sh | 40 - 14 files changed, 1135 insertions(+), 2123 deletions(-) delete mode 100644 opensora/dataset/feature_datasets.py create mode 100644 opensora/sample/pipeline_opensora.py create mode 100644 opensora/sample/sample_t2v_pixsig.py delete mode 100644 opensora/train/train_t2v_feature.py delete mode 100644 opensora/train/train_t2v_t5_feature.py delete mode 100644 scripts/text_condition/train_videoae_65x1024x1024.sh delete mode 100644 scripts/text_condition/train_videoae_65x512x512_d64.sh delete mode 100644 scripts/text_condition/train_videoae_65x512x512_rope.sh diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 5b64611d5..0b61af602 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -1,7 +1,6 @@ from torchvision.transforms import Compose from transformers import AutoTokenizer -from .feature_datasets import T2V_Feature_dataset, T2V_T5_Feature_dataset from torchvision import transforms from torchvision.transforms import Lambda diff --git a/opensora/dataset/feature_datasets.py b/opensora/dataset/feature_datasets.py deleted file mode 100644 index 74362528a..000000000 --- a/opensora/dataset/feature_datasets.py +++ /dev/null @@ -1,213 +0,0 @@ -import json -import os -import torch -import random -import torch.utils.data as data - -import numpy as np -from glob import glob -from PIL import Image -from torch.utils.data import Dataset -from tqdm import tqdm - -from opensora.dataset.transform import center_crop, RandomCropVideo -from opensora.utils.dataset_utils import DecordInit - - -class T2V_Feature_dataset(Dataset): - def __init__(self, args, temporal_sample): - - self.video_folder = args.video_folder - self.num_frames = args.video_length - self.temporal_sample = temporal_sample - - print('Building dataset...') - if os.path.exists('samples_430k.json'): - with open('samples_430k.json', 'r') as f: - self.samples = json.load(f) - else: - self.samples = self._make_dataset() - with open('samples_430k.json', 'w') as f: - json.dump(self.samples, f, indent=2) - - self.use_image_num = args.use_image_num - self.use_img_from_vid = args.use_img_from_vid - if self.use_image_num != 0 and not self.use_img_from_vid: - self.img_cap_list = self.get_img_cap_list() - - def _make_dataset(self): - all_mp4 = list(glob(os.path.join(self.video_folder, '**', '*.mp4'), recursive=True)) - # all_mp4 = all_mp4[:1000] - samples = [] - for i in tqdm(all_mp4): - video_id = os.path.basename(i).split('.')[0] - ae = os.path.split(i)[0].replace('data_split_tt', 'lb_causalvideovae444_feature') - ae = os.path.join(ae, f'{video_id}_causalvideovae444.npy') - if not os.path.exists(ae): - continue - - t5 = os.path.split(i)[0].replace('data_split_tt', 'lb_t5_feature') - cond_list = [] - cond_llava = os.path.join(t5, f'{video_id}_t5_llava_fea.npy') - mask_llava = os.path.join(t5, f'{video_id}_t5_llava_mask.npy') - if os.path.exists(cond_llava) and os.path.exists(mask_llava): - llava = dict(cond=cond_llava, mask=mask_llava) - cond_list.append(llava) - cond_sharegpt4v = os.path.join(t5, f'{video_id}_t5_sharegpt4v_fea.npy') - mask_sharegpt4v = os.path.join(t5, f'{video_id}_t5_sharegpt4v_mask.npy') - if os.path.exists(cond_sharegpt4v) and os.path.exists(mask_sharegpt4v): - sharegpt4v = dict(cond=cond_sharegpt4v, mask=mask_sharegpt4v) - cond_list.append(sharegpt4v) - if len(cond_list) > 0: - sample = dict(ae=ae, t5=cond_list) - samples.append(sample) - return samples - - def __len__(self): - return len(self.samples) - - def __getitem__(self, idx): - # try: - sample = self.samples[idx] - ae, t5 = sample['ae'], sample['t5'] - t5 = random.choice(t5) - video_origin = np.load(ae)[0] # C T H W - _, total_frames, _, _ = video_origin.shape - # Sampling video frames - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - assert end_frame_ind - start_frame_ind >= self.num_frames - select_video_idx = np.linspace(start_frame_ind, end_frame_ind - 1, num=self.num_frames, dtype=int) # start, stop, num=50 - # print('select_video_idx', total_frames, select_video_idx) - video = video_origin[:, select_video_idx] # C num_frames H W - video = torch.from_numpy(video) - - cond = torch.from_numpy(np.load(t5['cond']))[0] # L - cond_mask = torch.from_numpy(np.load(t5['mask']))[0] # L D - - if self.use_image_num != 0 and self.use_img_from_vid: - select_image_idx = np.random.randint(0, total_frames, self.use_image_num) - # print('select_image_idx', total_frames, self.use_image_num, select_image_idx) - images = video_origin[:, select_image_idx] # c, num_img, h, w - images = torch.from_numpy(images) - video = torch.cat([video, images], dim=1) # c, num_frame+num_img, h, w - cond = torch.stack([cond] * (1+self.use_image_num)) # 1+self.use_image_num, l - cond_mask = torch.stack([cond_mask] * (1+self.use_image_num)) # 1+self.use_image_num, l - elif self.use_image_num != 0 and not self.use_img_from_vid: - images, captions = self.img_cap_list[idx] - raise NotImplementedError - else: - pass - - return video, cond, cond_mask - # except Exception as e: - # print(f'Error with {e}, {sample}') - # return self.__getitem__(random.randint(0, self.__len__() - 1)) - - def get_img_cap_list(self): - raise NotImplementedError - - - - -class T2V_T5_Feature_dataset(Dataset): - def __init__(self, args, transform, temporal_sample): - - self.video_folder = args.video_folder - self.num_frames = args.num_frames - self.transform = transform - self.temporal_sample = temporal_sample - self.v_decoder = DecordInit() - - print('Building dataset...') - if os.path.exists('samples_430k.json'): - with open('samples_430k.json', 'r') as f: - self.samples = json.load(f) - self.samples = [dict(ae=i['ae'].replace('lb_causalvideovae444_feature', 'data_split_1024').replace('_causalvideovae444.npy', '.mp4'), t5=i['t5']) for i in self.samples] - else: - self.samples = self._make_dataset() - with open('samples_430k.json', 'w') as f: - json.dump(self.samples, f, indent=2) - - self.use_image_num = args.use_image_num - self.use_img_from_vid = args.use_img_from_vid - if self.use_image_num != 0 and not self.use_img_from_vid: - self.img_cap_list = self.get_img_cap_list() - - def _make_dataset(self): - all_mp4 = list(glob(os.path.join(self.video_folder, '**', '*.mp4'), recursive=True)) - # all_mp4 = all_mp4[:1000] - samples = [] - for i in tqdm(all_mp4): - video_id = os.path.basename(i).split('.')[0] - # ae = os.path.split(i)[0].replace('data_split', 'lb_causalvideovae444_feature') - # ae = os.path.join(ae, f'{video_id}_causalvideovae444.npy') - ae = i - if not os.path.exists(ae): - continue - - t5 = os.path.split(i)[0].replace('data_split_1024', 'lb_t5_feature') - cond_list = [] - cond_llava = os.path.join(t5, f'{video_id}_t5_llava_fea.npy') - mask_llava = os.path.join(t5, f'{video_id}_t5_llava_mask.npy') - if os.path.exists(cond_llava) and os.path.exists(mask_llava): - llava = dict(cond=cond_llava, mask=mask_llava) - cond_list.append(llava) - cond_sharegpt4v = os.path.join(t5, f'{video_id}_t5_sharegpt4v_fea.npy') - mask_sharegpt4v = os.path.join(t5, f'{video_id}_t5_sharegpt4v_mask.npy') - if os.path.exists(cond_sharegpt4v) and os.path.exists(mask_sharegpt4v): - sharegpt4v = dict(cond=cond_sharegpt4v, mask=mask_sharegpt4v) - cond_list.append(sharegpt4v) - if len(cond_list) > 0: - sample = dict(ae=ae, t5=cond_list) - samples.append(sample) - return samples - - def __len__(self): - return len(self.samples) - - def __getitem__(self, idx): - try: - sample = self.samples[idx] - ae, t5 = sample['ae'], sample['t5'] - t5 = random.choice(t5) - - video = self.decord_read(ae) - video = self.transform(video) # T C H W -> T C H W - video = video.transpose(0, 1) # T C H W -> C T H W - total_frames = video.shape[1] - cond = torch.from_numpy(np.load(t5['cond']))[0] # L - cond_mask = torch.from_numpy(np.load(t5['mask']))[0] # L D - - if self.use_image_num != 0 and self.use_img_from_vid: - select_image_idx = np.random.randint(0, total_frames, self.use_image_num) - # print('select_image_idx', total_frames, self.use_image_num, select_image_idx) - images = video.numpy()[:, select_image_idx] # c, num_img, h, w - images = torch.from_numpy(images) - video = torch.cat([video, images], dim=1) # c, num_frame+num_img, h, w - cond = torch.stack([cond] * (1+self.use_image_num)) # 1+self.use_image_num, l - cond_mask = torch.stack([cond_mask] * (1+self.use_image_num)) # 1+self.use_image_num, l - elif self.use_image_num != 0 and not self.use_img_from_vid: - images, captions = self.img_cap_list[idx] - raise NotImplementedError - else: - pass - - return video, cond, cond_mask - except Exception as e: - print(f'Error with {e}, {sample}') - return self.__getitem__(random.randint(0, self.__len__() - 1)) - - def decord_read(self, path): - decord_vr = self.v_decoder(path) - total_frames = len(decord_vr) - # Sampling video frames - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - # assert end_frame_ind - start_frame_ind >= self.num_frames - frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - video_data = decord_vr.get_batch(frame_indice).asnumpy() - video_data = torch.from_numpy(video_data) - video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) - return video_data - - def get_img_cap_list(self): - raise NotImplementedError \ No newline at end of file diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index c548a453c..f4cc7c623 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -23,51 +23,53 @@ def random_video_noise(t, c, h, w): class T2V_dataset(Dataset): def __init__(self, args, transform, temporal_sample, tokenizer): - self.video_folder = args.video_folder + self.image_data = args.image_data + self.video_data = args.video_data self.num_frames = args.num_frames self.transform = transform self.temporal_sample = temporal_sample self.tokenizer = tokenizer self.model_max_length = args.model_max_length - self.video_folder = args.video_folder self.v_decoder = DecordInit() - with open(args.video_data_path, 'r') as f: - self.vid_cap_list = json.load(f) + self.vid_cap_list = self.get_vid_cap_list() + self.use_image_num = args.use_image_num self.use_img_from_vid = args.use_img_from_vid if self.use_image_num != 0 and not self.use_img_from_vid: - self.image_folder = args.image_folder - self.image_data_path = args.image_data_path self.img_cap_list = self.get_img_cap_list() + def __len__(self): return len(self.vid_cap_list) def __getitem__(self, idx): - try: - video_data = self.get_video(idx) - image_data = {} - if self.use_image_num != 0 and self.use_img_from_vid: - image_data = self.get_image_from_video(video_data) - elif self.use_image_num != 0 and not self.use_img_from_vid: - image_data = self.get_image(idx) - else: - raise NotImplementedError - return dict(video_data=video_data, image_data=image_data) - except Exception as e: - print(f'Error with {e}, {self.vid_cap_list[idx]}') - return self.__getitem__(random.randint(0, self.__len__() - 1)) + # try: + video_data = self.get_video(idx) + image_data = {} + if self.use_image_num != 0 and self.use_img_from_vid: + image_data = self.get_image_from_video(video_data) + elif self.use_image_num != 0 and not self.use_img_from_vid: + image_data = self.get_image(idx) + else: + raise NotImplementedError + return dict(video_data=video_data, image_data=image_data) + # except Exception as e: + # print(f'Error with {e}, {self.vid_cap_list[idx]}') + # return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): # video = random.choice([random_video_noise(65, 3, 720, 360) * 255, random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 720)]) - # print('random shape', video.shape) + # # print('random shape', video.shape) # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = opj(self.video_folder, self.vid_cap_list[idx]['path']) - video = self.decord_read(video_path, self.vid_cap_list[idx]['frame_idx']) + video_path = self.vid_cap_list[idx]['path'] + frame_idx = self.vid_cap_list[idx]['frame_idx'] + video = self.decord_read(video_path, frame_idx) video = self.transform(video) # T C H W -> T C H W + # video = torch.rand(65, 3, 512, 512) + video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'][0] @@ -97,7 +99,7 @@ def get_image(self, idx): idx = idx % len(self.img_cap_list) # out of range image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - image = [Image.open(os.path.join(self.image_folder, i['path'])).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] @@ -122,18 +124,18 @@ def get_image(self, idx): cond_mask = torch.cat(cond_mask) # self.use_image_num, l return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) - def tv_read(self, path): - vframes, aframes, info = torchvision.io.read_video(filename=path, pts_unit='sec', output_format='TCHW') - total_frames = len(vframes) + # def tv_read(self, path): + # vframes, aframes, info = torchvision.io.read_video(filename=path, pts_unit='sec', output_format='TCHW') + # total_frames = len(vframes) - # Sampling video frames - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - # assert end_frame_ind - start_frame_ind >= self.num_frames - frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) + # # Sampling video frames + # start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) + # # assert end_frame_ind - start_frame_ind >= self.num_frames + # frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - video = vframes[frame_indice] # (T, C, H, W) + # video = vframes[frame_indice] # (T, C, H, W) - return video + # return video def decord_read(self, path, frame_idx=None): decord_vr = self.v_decoder(path) @@ -146,14 +148,38 @@ def decord_read(self, path, frame_idx=None): start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) # assert end_frame_ind - start_frame_ind >= self.num_frames frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) + frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) video_data = decord_vr.get_batch(frame_indice).asnumpy() video_data = torch.from_numpy(video_data) video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) return video_data + + def get_vid_cap_list(self): + vid_cap_lists = [] + with open(self.video_data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + # print(folder_anno) + for folder, anno in folder_anno: + with open(anno, 'r') as f: + vid_cap_list = json.load(f) + print(f'Building {anno}...') + for i in tqdm(range(len(vid_cap_list))): + vid_cap_list[i]['path'] = opj(folder, vid_cap_list[i]['path']) + vid_cap_lists += vid_cap_list + return vid_cap_lists + def get_img_cap_list(self): - with open(self.image_data_path, 'r') as f: - image_data = json.load(f) - image_data = [image_data[i: i+self.use_image_num] for i in range(0, len(image_data), self.use_image_num)] - return image_data[:-1] # drop last to avoid error length \ No newline at end of file + img_cap_lists = [] + with open(self.image_data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + for folder, anno in folder_anno: + with open(anno, 'r') as f: + img_cap_list = json.load(f) + print(f'Building {anno}...') + for i in tqdm(range(len(img_cap_list))): + img_cap_list[i]['path'] = opj(folder, img_cap_list[i]['path']) + img_cap_lists += img_cap_list + img_cap_lists = [img_cap_lists[i: i+self.use_image_num] for i in range(0, len(img_cap_lists), self.use_image_num)] + return img_cap_lists[:-1] # drop last to avoid error length \ No newline at end of file diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index bf3abffa9..a68cd3d9a 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -1,7 +1,7 @@ - from .latte.modeling_latte import Latte_models - +from .opensora.modeling_opensora import OpenSora_models Diffusion_models = {} Diffusion_models.update(Latte_models) +Diffusion_models.update(OpenSora_models) \ No newline at end of file diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 8e0d431df..a8d9d00c4 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -295,6 +295,7 @@ def forward( # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + attention_mask_vid, attention_mask_img = None, None if attention_mask is not None and attention_mask.ndim == 4: # assume that mask is expressed as: # (1 = keep, 0 = discard) @@ -325,6 +326,7 @@ def forward( attention_mask_img = attention_mask_vid attention_mask_vid = None # convert encoder_attention_mask to a bias the same way we do for attention_mask + # import ipdb;ipdb.set_trace() if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: # b, 1+use_image_num, l -> a video with images # b, 1, l -> only images @@ -523,11 +525,11 @@ def _get_output_for_patched_inputs( def OpenSoraT2V_S_122(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=64, num_attention_heads=18, patch_size_t=1, patch_size=2, + return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_S_222(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=64, num_attention_heads=18, patch_size_t=2, patch_size=2, + return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) OpenSora_models = { @@ -566,12 +568,27 @@ def OpenSoraT2V_S_222(**kwargs): model = OpenSoraT2V_S_122(in_channels=4, out_channels=8, sample_size=latent_size, - sample_size_t=video_length).to(device) - # try: - # ckpt = torch.load(r"t2v.pt", map_location='cpu')['model'] - # model.load_state_dict(ckpt) - # except Exception as e: - # print(e) + sample_size_t=video_length, + activation_fn="gelu-approximate", + attention_bias=True, + attention_type="default", + double_self_attention=False, + norm_elementwise_affine=False, + norm_eps=1e-06, + norm_num_groups=32, + num_vector_embeds=None, + only_cross_attention=False, + upcast_attention=False, + use_linear_projection=False, + use_additional_conditions=False).to(device) + try: + import ipdb;ipdb.set_trace() + path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--PixArt-alpha--PixArt-Sigma-XL-2-512-MS/snapshots/786c445c97ddcc0eb2faa157b131ac71ee1935a2/transformer/diffusion_pytorch_model.safetensors" + from safetensors.torch import load_file as safe_load + ckpt = safe_load(path, device="cpu") + model.load_state_dict(ckpt) + except Exception as e: + print(e) print(model) x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w).to(device) diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py new file mode 100644 index 000000000..daea92ec6 --- /dev/null +++ b/opensora/sample/pipeline_opensora.py @@ -0,0 +1,822 @@ +# Copyright 2024 PixArt-Sigma Authors and The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import html +import inspect +import re +import urllib.parse as ul +from typing import Callable, List, Optional, Tuple, Union + +import torch +from transformers import T5EncoderModel, T5Tokenizer + +from diffusers.image_processor import PixArtImageProcessor +from diffusers.models import AutoencoderKL, Transformer2DModel +from diffusers.schedulers import DPMSolverMultistepScheduler +from diffusers.utils import ( + BACKENDS_MAPPING, + deprecate, + is_bs4_available, + is_ftfy_available, + logging, + replace_example_docstring, +) +from diffusers.utils.torch_utils import randn_tensor +from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput +from diffusers.pipelines.pixart_alpha.pipeline_pixart_sigma import ASPECT_RATIO_256_BIN, ASPECT_RATIO_512_BIN, ASPECT_RATIO_1024_BIN, ASPECT_RATIO_2048_BIN + + +logger = logging.get_logger(__name__) # pylint: disable=invalid-name + +if is_bs4_available(): + from bs4 import BeautifulSoup + +if is_ftfy_available(): + import ftfy + + +EXAMPLE_DOC_STRING = """ + Examples: + ```py + >>> import torch + >>> from diffusers import PixArtSigmaPipeline + + >>> # You can replace the checkpoint id with "PixArt-alpha/PixArt-Sigma-XL-2-512-MS" too. + >>> pipe = PixArtSigmaPipeline.from_pretrained( + ... "PixArt-alpha/PixArt-Sigma-XL-2-1024-MS", torch_dtype=torch.float16 + ... ) + >>> # Enable memory optimizations. + >>> # pipe.enable_model_cpu_offload() + + >>> prompt = "A small cactus with a happy face in the Sahara desert." + >>> image = pipe(prompt).images[0] + ``` +""" + + +# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps +def retrieve_timesteps( + scheduler, + num_inference_steps: Optional[int] = None, + device: Optional[Union[str, torch.device]] = None, + timesteps: Optional[List[int]] = None, + **kwargs, +): + """ + Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles + custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`. + + Args: + scheduler (`SchedulerMixin`): + The scheduler to get timesteps from. + num_inference_steps (`int`): + The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps` + must be `None`. + device (`str` or `torch.device`, *optional*): + The device to which the timesteps should be moved to. If `None`, the timesteps are not moved. + timesteps (`List[int]`, *optional*): + Custom timesteps used to support arbitrary spacing between timesteps. If `None`, then the default + timestep spacing strategy of the scheduler is used. If `timesteps` is passed, `num_inference_steps` + must be `None`. + + Returns: + `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the + second element is the number of inference steps. + """ + if timesteps is not None: + accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys()) + if not accepts_timesteps: + raise ValueError( + f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom" + f" timestep schedules. Please check whether you are using the correct scheduler." + ) + scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs) + timesteps = scheduler.timesteps + num_inference_steps = len(timesteps) + else: + scheduler.set_timesteps(num_inference_steps, device=device, **kwargs) + timesteps = scheduler.timesteps + return timesteps, num_inference_steps + + +class OpenSoraPipeline(DiffusionPipeline): + r""" + Pipeline for text-to-image generation using PixArt-Sigma. + """ + + bad_punct_regex = re.compile( + r"[" + + "#®•©™&@·º½¾¿¡§~" + + r"\)" + + r"\(" + + r"\]" + + r"\[" + + r"\}" + + r"\{" + + r"\|" + + "\\" + + r"\/" + + r"\*" + + r"]{1,}" + ) # noqa + + _optional_components = ["tokenizer", "text_encoder"] + model_cpu_offload_seq = "text_encoder->transformer->vae" + + def __init__( + self, + tokenizer: T5Tokenizer, + text_encoder: T5EncoderModel, + vae: AutoencoderKL, + transformer: Transformer2DModel, + scheduler: DPMSolverMultistepScheduler, + ): + super().__init__() + + self.register_modules( + tokenizer=tokenizer, text_encoder=text_encoder, vae=vae, transformer=transformer, scheduler=scheduler + ) + + # self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) + self.image_processor = PixArtImageProcessor(vae_scale_factor=self.vae.vae_scale_factor[1:]) + + # Copied from diffusers.pipelines.pixart_alpha.pipeline_pixart_alpha.PixArtAlphaPipeline.encode_prompt + def encode_prompt( + self, + prompt: Union[str, List[str]], + do_classifier_free_guidance: bool = True, + negative_prompt: str = "", + num_images_per_prompt: int = 1, + device: Optional[torch.device] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + clean_caption: bool = False, + max_sequence_length: int = 120, + **kwargs, + ): + r""" + Encodes the prompt into text encoder hidden states. + + Args: + prompt (`str` or `List[str]`, *optional*): + prompt to be encoded + negative_prompt (`str` or `List[str]`, *optional*): + The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds` + instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`). For + PixArt-Alpha, this should be "". + do_classifier_free_guidance (`bool`, *optional*, defaults to `True`): + whether to use classifier free guidance or not + num_images_per_prompt (`int`, *optional*, defaults to 1): + number of images that should be generated per prompt + device: (`torch.device`, *optional*): + torch device to place the resulting embeddings on + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. For PixArt-Alpha, it's should be the embeddings of the "" + string. + clean_caption (`bool`, defaults to `False`): + If `True`, the function will preprocess and clean the provided caption before encoding. + max_sequence_length (`int`, defaults to 120): Maximum sequence length to use for the prompt. + """ + + if "mask_feature" in kwargs: + deprecation_message = "The use of `mask_feature` is deprecated. It is no longer used in any computation and that doesn't affect the end results. It will be removed in a future version." + deprecate("mask_feature", "1.0.0", deprecation_message, standard_warn=False) + + if device is None: + device = self._execution_device + + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + + # See Section 3.1. of the paper. + max_length = max_sequence_length + + if prompt_embeds is None: + prompt = self._text_preprocessing(prompt, clean_caption=clean_caption) + text_inputs = self.tokenizer( + prompt, + padding="max_length", + max_length=max_length, + truncation=True, + add_special_tokens=True, + return_tensors="pt", + ) + text_input_ids = text_inputs.input_ids + untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids + + if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal( + text_input_ids, untruncated_ids + ): + removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_length - 1 : -1]) + logger.warning( + "The following part of your input was truncated because CLIP can only handle sequences up to" + f" {max_length} tokens: {removed_text}" + ) + + prompt_attention_mask = text_inputs.attention_mask + prompt_attention_mask = prompt_attention_mask.to(device) + + prompt_embeds = self.text_encoder(text_input_ids.to(device), attention_mask=prompt_attention_mask) + prompt_embeds = prompt_embeds[0] + + if self.text_encoder is not None: + dtype = self.text_encoder.dtype + elif self.transformer is not None: + dtype = self.transformer.dtype + else: + dtype = None + + prompt_embeds = prompt_embeds.to(dtype=dtype, device=device) + + bs_embed, seq_len, _ = prompt_embeds.shape + # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method + prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1) + prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1) + prompt_attention_mask = prompt_attention_mask.view(bs_embed, -1) + prompt_attention_mask = prompt_attention_mask.repeat(num_images_per_prompt, 1) + + # get unconditional embeddings for classifier free guidance + if do_classifier_free_guidance and negative_prompt_embeds is None: + uncond_tokens = [negative_prompt] * batch_size + uncond_tokens = self._text_preprocessing(uncond_tokens, clean_caption=clean_caption) + max_length = prompt_embeds.shape[1] + uncond_input = self.tokenizer( + uncond_tokens, + padding="max_length", + max_length=max_length, + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors="pt", + ) + negative_prompt_attention_mask = uncond_input.attention_mask + negative_prompt_attention_mask = negative_prompt_attention_mask.to(device) + + negative_prompt_embeds = self.text_encoder( + uncond_input.input_ids.to(device), attention_mask=negative_prompt_attention_mask + ) + negative_prompt_embeds = negative_prompt_embeds[0] + + if do_classifier_free_guidance: + # duplicate unconditional embeddings for each generation per prompt, using mps friendly method + seq_len = negative_prompt_embeds.shape[1] + + negative_prompt_embeds = negative_prompt_embeds.to(dtype=dtype, device=device) + + negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1) + negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1) + + negative_prompt_attention_mask = negative_prompt_attention_mask.view(bs_embed, -1) + negative_prompt_attention_mask = negative_prompt_attention_mask.repeat(num_images_per_prompt, 1) + else: + negative_prompt_embeds = None + negative_prompt_attention_mask = None + + return prompt_embeds, prompt_attention_mask, negative_prompt_embeds, negative_prompt_attention_mask + + # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs + def prepare_extra_step_kwargs(self, generator, eta): + # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature + # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers. + # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502 + # and should be between [0, 1] + + accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys()) + extra_step_kwargs = {} + if accepts_eta: + extra_step_kwargs["eta"] = eta + + # check if the scheduler accepts generator + accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys()) + if accepts_generator: + extra_step_kwargs["generator"] = generator + return extra_step_kwargs + + # Copied from diffusers.pipelines.pixart_alpha.pipeline_pixart_alpha.PixArtAlphaPipeline.check_inputs + def check_inputs( + self, + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds=None, + negative_prompt_embeds=None, + prompt_attention_mask=None, + negative_prompt_attention_mask=None, + ): + if num_frames <= 0: + raise ValueError(f"`num_frames` have to be positive but is {num_frames}.") + if height % 8 != 0 or width % 8 != 0: + raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.") + + if (callback_steps is None) or ( + callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0) + ): + raise ValueError( + f"`callback_steps` has to be a positive integer but is {callback_steps} of type" + f" {type(callback_steps)}." + ) + + if prompt is not None and prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to" + " only forward one of the two." + ) + elif prompt is None and prompt_embeds is None: + raise ValueError( + "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined." + ) + elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)): + raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}") + + if prompt is not None and negative_prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `prompt`: {prompt} and `negative_prompt_embeds`:" + f" {negative_prompt_embeds}. Please make sure to only forward one of the two." + ) + + if negative_prompt is not None and negative_prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:" + f" {negative_prompt_embeds}. Please make sure to only forward one of the two." + ) + + if prompt_embeds is not None and prompt_attention_mask is None: + raise ValueError("Must provide `prompt_attention_mask` when specifying `prompt_embeds`.") + + if negative_prompt_embeds is not None and negative_prompt_attention_mask is None: + raise ValueError("Must provide `negative_prompt_attention_mask` when specifying `negative_prompt_embeds`.") + + if prompt_embeds is not None and negative_prompt_embeds is not None: + if prompt_embeds.shape != negative_prompt_embeds.shape: + raise ValueError( + "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but" + f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`" + f" {negative_prompt_embeds.shape}." + ) + if prompt_attention_mask.shape != negative_prompt_attention_mask.shape: + raise ValueError( + "`prompt_attention_mask` and `negative_prompt_attention_mask` must have the same shape when passed directly, but" + f" got: `prompt_attention_mask` {prompt_attention_mask.shape} != `negative_prompt_attention_mask`" + f" {negative_prompt_attention_mask.shape}." + ) + + # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._text_preprocessing + def _text_preprocessing(self, text, clean_caption=False): + if clean_caption and not is_bs4_available(): + logger.warning(BACKENDS_MAPPING["bs4"][-1].format("Setting `clean_caption=True`")) + logger.warning("Setting `clean_caption` to False...") + clean_caption = False + + if clean_caption and not is_ftfy_available(): + logger.warning(BACKENDS_MAPPING["ftfy"][-1].format("Setting `clean_caption=True`")) + logger.warning("Setting `clean_caption` to False...") + clean_caption = False + + if not isinstance(text, (tuple, list)): + text = [text] + + def process(text: str): + if clean_caption: + text = self._clean_caption(text) + text = self._clean_caption(text) + else: + text = text.lower().strip() + return text + + return [process(t) for t in text] + + # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._clean_caption + def _clean_caption(self, caption): + caption = str(caption) + caption = ul.unquote_plus(caption) + caption = caption.strip().lower() + caption = re.sub("", "person", caption) + # urls: + caption = re.sub( + r"\b((?:https?:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", # noqa + "", + caption, + ) # regex for urls + caption = re.sub( + r"\b((?:www:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", # noqa + "", + caption, + ) # regex for urls + # html: + caption = BeautifulSoup(caption, features="html.parser").text + + # @ + caption = re.sub(r"@[\w\d]+\b", "", caption) + + # 31C0—31EF CJK Strokes + # 31F0—31FF Katakana Phonetic Extensions + # 3200—32FF Enclosed CJK Letters and Months + # 3300—33FF CJK Compatibility + # 3400—4DBF CJK Unified Ideographs Extension A + # 4DC0—4DFF Yijing Hexagram Symbols + # 4E00—9FFF CJK Unified Ideographs + caption = re.sub(r"[\u31c0-\u31ef]+", "", caption) + caption = re.sub(r"[\u31f0-\u31ff]+", "", caption) + caption = re.sub(r"[\u3200-\u32ff]+", "", caption) + caption = re.sub(r"[\u3300-\u33ff]+", "", caption) + caption = re.sub(r"[\u3400-\u4dbf]+", "", caption) + caption = re.sub(r"[\u4dc0-\u4dff]+", "", caption) + caption = re.sub(r"[\u4e00-\u9fff]+", "", caption) + ####################################################### + + # все виды тире / all types of dash --> "-" + caption = re.sub( + r"[\u002D\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E3B\u2E40\u301C\u3030\u30A0\uFE31\uFE32\uFE58\uFE63\uFF0D]+", # noqa + "-", + caption, + ) + + # кавычки к одному стандарту + caption = re.sub(r"[`´«»“”¨]", '"', caption) + caption = re.sub(r"[‘’]", "'", caption) + + # " + caption = re.sub(r""?", "", caption) + # & + caption = re.sub(r"&", "", caption) + + # ip adresses: + caption = re.sub(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", " ", caption) + + # article ids: + caption = re.sub(r"\d:\d\d\s+$", "", caption) + + # \n + caption = re.sub(r"\\n", " ", caption) + + # "#123" + caption = re.sub(r"#\d{1,3}\b", "", caption) + # "#12345.." + caption = re.sub(r"#\d{5,}\b", "", caption) + # "123456.." + caption = re.sub(r"\b\d{6,}\b", "", caption) + # filenames: + caption = re.sub(r"[\S]+\.(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)", "", caption) + + # + caption = re.sub(r"[\"\']{2,}", r'"', caption) # """AUSVERKAUFT""" + caption = re.sub(r"[\.]{2,}", r" ", caption) # """AUSVERKAUFT""" + + caption = re.sub(self.bad_punct_regex, r" ", caption) # ***AUSVERKAUFT***, #AUSVERKAUFT + caption = re.sub(r"\s+\.\s+", r" ", caption) # " . " + + # this-is-my-cute-cat / this_is_my_cute_cat + regex2 = re.compile(r"(?:\-|\_)") + if len(re.findall(regex2, caption)) > 3: + caption = re.sub(regex2, " ", caption) + + caption = ftfy.fix_text(caption) + caption = html.unescape(html.unescape(caption)) + + caption = re.sub(r"\b[a-zA-Z]{1,3}\d{3,15}\b", "", caption) # jc6640 + caption = re.sub(r"\b[a-zA-Z]+\d+[a-zA-Z]+\b", "", caption) # jc6640vc + caption = re.sub(r"\b\d+[a-zA-Z]+\d+\b", "", caption) # 6640vc231 + + caption = re.sub(r"(worldwide\s+)?(free\s+)?shipping", "", caption) + caption = re.sub(r"(free\s)?download(\sfree)?", "", caption) + caption = re.sub(r"\bclick\b\s(?:for|on)\s\w+", "", caption) + caption = re.sub(r"\b(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)(\simage[s]?)?", "", caption) + caption = re.sub(r"\bpage\s+\d+\b", "", caption) + + caption = re.sub(r"\b\d*[a-zA-Z]+\d+[a-zA-Z]+\d+[a-zA-Z\d]*\b", r" ", caption) # j2d1a2a... + + caption = re.sub(r"\b\d+\.?\d*[xх×]\d+\.?\d*\b", "", caption) + + caption = re.sub(r"\b\s+\:\s+", r": ", caption) + caption = re.sub(r"(\D[,\./])\b", r"\1 ", caption) + caption = re.sub(r"\s+", " ", caption) + + caption.strip() + + caption = re.sub(r"^[\"\']([\w\W]+)[\"\']$", r"\1", caption) + caption = re.sub(r"^[\'\_,\-\:;]", r"", caption) + caption = re.sub(r"[\'\_,\-\:\-\+]$", r"", caption) + caption = re.sub(r"^\.\S+$", "", caption) + + return caption.strip() + + # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents + def prepare_latents(self, batch_size, num_channels_latents, num_frames, height, width, dtype, device, generator, latents=None): + shape = ( + batch_size, + num_channels_latents, + ((int(num_frames) - 1) // self.vae.vae_scale_factor[0] + 1) if int(num_frames) % 2 == 1 else int(num_frames) // self.vae.vae_scale_factor[0], + int(height) // self.vae.vae_scale_factor[1], + int(width) // self.vae.vae_scale_factor[2], + ) + if isinstance(generator, list) and len(generator) != batch_size: + raise ValueError( + f"You have passed a list of generators of length {len(generator)}, but requested an effective batch" + f" size of {batch_size}. Make sure the batch size matches the length of the generators." + ) + + if latents is None: + latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype) + else: + latents = latents.to(device) + + # scale the initial noise by the standard deviation required by the scheduler + latents = latents * self.scheduler.init_noise_sigma + return latents + + @torch.no_grad() + @replace_example_docstring(EXAMPLE_DOC_STRING) + def __call__( + self, + prompt: Union[str, List[str]] = None, + negative_prompt: str = "", + num_inference_steps: int = 20, + timesteps: List[int] = None, + guidance_scale: float = 4.5, + num_images_per_prompt: Optional[int] = 1, + num_frames: Optional[int] = None, + height: Optional[int] = None, + width: Optional[int] = None, + eta: float = 0.0, + generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, + latents: Optional[torch.FloatTensor] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + output_type: Optional[str] = "pil", + return_dict: bool = True, + callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, + callback_steps: int = 1, + clean_caption: bool = True, + use_resolution_binning: bool = True, + max_sequence_length: int = 300, + **kwargs, + ) -> Union[ImagePipelineOutput, Tuple]: + """ + Function invoked when calling the pipeline for generation. + + Args: + prompt (`str` or `List[str]`, *optional*): + The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. + instead. + negative_prompt (`str` or `List[str]`, *optional*): + The prompt or prompts not to guide the image generation. If not defined, one has to pass + `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is + less than `1`). + num_inference_steps (`int`, *optional*, defaults to 100): + The number of denoising steps. More denoising steps usually lead to a higher quality image at the + expense of slower inference. + timesteps (`List[int]`, *optional*): + Custom timesteps to use for the denoising process. If not defined, equal spaced `num_inference_steps` + timesteps are used. Must be in descending order. + guidance_scale (`float`, *optional*, defaults to 4.5): + Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). + `guidance_scale` is defined as `w` of equation 2. of [Imagen + Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > + 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, + usually at the expense of lower image quality. + num_images_per_prompt (`int`, *optional*, defaults to 1): + The number of images to generate per prompt. + height (`int`, *optional*, defaults to self.unet.config.sample_size): + The height in pixels of the generated image. + width (`int`, *optional*, defaults to self.unet.config.sample_size): + The width in pixels of the generated image. + eta (`float`, *optional*, defaults to 0.0): + Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to + [`schedulers.DDIMScheduler`], will be ignored for others. + generator (`torch.Generator` or `List[torch.Generator]`, *optional*): + One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) + to make generation deterministic. + latents (`torch.FloatTensor`, *optional*): + Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image + generation. Can be used to tweak the same generation with different prompts. If not provided, a latents + tensor will ge generated by sampling using the supplied random `generator`. + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + prompt_attention_mask (`torch.FloatTensor`, *optional*): Pre-generated attention mask for text embeddings. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not + provided, negative_prompt_embeds will be generated from `negative_prompt` input argument. + negative_prompt_attention_mask (`torch.FloatTensor`, *optional*): + Pre-generated attention mask for negative text embeddings. + output_type (`str`, *optional*, defaults to `"pil"`): + The output format of the generate image. Choose between + [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~pipelines.stable_diffusion.IFPipelineOutput`] instead of a plain tuple. + callback (`Callable`, *optional*): + A function that will be called every `callback_steps` steps during inference. The function will be + called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. + callback_steps (`int`, *optional*, defaults to 1): + The frequency at which the `callback` function will be called. If not specified, the callback will be + called at every step. + clean_caption (`bool`, *optional*, defaults to `True`): + Whether or not to clean the caption before creating embeddings. Requires `beautifulsoup4` and `ftfy` to + be installed. If the dependencies are not installed, the embeddings will be created from the raw + prompt. + use_resolution_binning (`bool` defaults to `True`): + If set to `True`, the requested height and width are first mapped to the closest resolutions using + `ASPECT_RATIO_1024_BIN`. After the produced latents are decoded into images, they are resized back to + the requested resolution. Useful for generating non-square images. + max_sequence_length (`int` defaults to 120): Maximum sequence length to use with the `prompt`. + + Examples: + + Returns: + [`~pipelines.ImagePipelineOutput`] or `tuple`: + If `return_dict` is `True`, [`~pipelines.ImagePipelineOutput`] is returned, otherwise a `tuple` is + returned where the first element is a list with the generated images + """ + # 1. Check inputs. Raise error if not correct + height = height or self.transformer.config.sample_size[0] * self.vae_scale_factor[1] + width = width or self.transformer.config.sample_size[1] * self.vae_scale_factor[2] + if use_resolution_binning: + # if self.transformer.config.sample_size == 256: + # aspect_ratio_bin = ASPECT_RATIO_2048_BIN + # elif self.transformer.config.sample_size == 128: + # aspect_ratio_bin = ASPECT_RATIO_1024_BIN + # elif self.transformer.config.sample_size == 64: + aspect_ratio_bin = ASPECT_RATIO_512_BIN + # elif self.transformer.config.sample_size == 32: + # aspect_ratio_bin = ASPECT_RATIO_256_BIN + # else: + # raise ValueError("Invalid sample size") + orig_height, orig_width = height, width + height, width = self.image_processor.classify_height_width_bin(height, width, ratios=aspect_ratio_bin) + + self.check_inputs( + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds, + negative_prompt_embeds, + prompt_attention_mask, + negative_prompt_attention_mask, + ) + + # 2. Default height and width to transformer + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + + device = self._execution_device + + # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) + # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` + # corresponds to doing no classifier free guidance. + do_classifier_free_guidance = guidance_scale > 1.0 + + # 3. Encode input prompt + ( + prompt_embeds, + prompt_attention_mask, + negative_prompt_embeds, + negative_prompt_attention_mask, + ) = self.encode_prompt( + prompt, + do_classifier_free_guidance, + negative_prompt=negative_prompt, + num_images_per_prompt=num_images_per_prompt, + device=device, + prompt_embeds=prompt_embeds, + negative_prompt_embeds=negative_prompt_embeds, + prompt_attention_mask=prompt_attention_mask, + negative_prompt_attention_mask=negative_prompt_attention_mask, + clean_caption=clean_caption, + max_sequence_length=max_sequence_length, + ) + if do_classifier_free_guidance: + prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) + prompt_attention_mask = torch.cat([negative_prompt_attention_mask, prompt_attention_mask], dim=0) + + # 4. Prepare timesteps + timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device, timesteps) + + # 5. Prepare latents. + latent_channels = self.transformer.config.in_channels + latents = self.prepare_latents( + batch_size * num_images_per_prompt, + latent_channels, + num_frames, + height, + width, + prompt_embeds.dtype, + device, + generator, + latents, + ) + + # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline + extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) + + # 6.1 Prepare micro-conditions. + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + + # 7. Denoising loop + num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) + + with self.progress_bar(total=num_inference_steps) as progress_bar: + for i, t in enumerate(timesteps): + latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) + + current_timestep = t + if not torch.is_tensor(current_timestep): + # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can + # This would be a good case for the `match` statement (Python 3.10+) + is_mps = latent_model_input.device.type == "mps" + if isinstance(current_timestep, float): + dtype = torch.float32 if is_mps else torch.float64 + else: + dtype = torch.int32 if is_mps else torch.int64 + current_timestep = torch.tensor([current_timestep], dtype=dtype, device=latent_model_input.device) + elif len(current_timestep.shape) == 0: + current_timestep = current_timestep[None].to(latent_model_input.device) + # broadcast to batch dimension in a way that's compatible with ONNX/Core ML + current_timestep = current_timestep.expand(latent_model_input.shape[0]) + + # import ipdb;ipdb.set_trace() + if prompt_embeds.ndim == 3: + prompt_embeds = prompt_embeds.unsqueeze(1) # b l d -> b 1 l d + if prompt_attention_mask.ndim == 2: + prompt_attention_mask = prompt_attention_mask.unsqueeze(1) # b l -> b 1 l + # predict noise model_output + noise_pred = self.transformer( + latent_model_input, + encoder_hidden_states=prompt_embeds, + encoder_attention_mask=prompt_attention_mask, + timestep=current_timestep, + added_cond_kwargs=added_cond_kwargs, + return_dict=False, + )[0] + + # perform guidance + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + # learned sigma + if self.transformer.config.out_channels // 2 == latent_channels: + noise_pred = noise_pred.chunk(2, dim=1)[0] + else: + noise_pred = noise_pred + + # compute previous image: x_t -> x_t-1 + latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] + + # call the callback, if provided + if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): + progress_bar.update() + if callback is not None and i % callback_steps == 0: + step_idx = i // getattr(self.scheduler, "order", 1) + callback(step_idx, t, latents) + # import ipdb;ipdb.set_trace() + latents = latents.squeeze(2) + if not output_type == "latent": + image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False)[0] + if use_resolution_binning: + image = self.image_processor.resize_and_crop_tensor(image, orig_width, orig_height) + else: + image = latents + + if not output_type == "latent": + image = self.image_processor.postprocess(image, output_type=output_type) + + # Offload all models + self.maybe_free_model_hooks() + + if not return_dict: + return (image,) + + return ImagePipelineOutput(images=image) diff --git a/opensora/sample/sample_t2v_pixsig.py b/opensora/sample/sample_t2v_pixsig.py new file mode 100644 index 000000000..d328d2282 --- /dev/null +++ b/opensora/sample/sample_t2v_pixsig.py @@ -0,0 +1,147 @@ +import math +import os +import torch +import argparse +import torchvision + +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler +from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel +from omegaconf import OmegaConf +from torchvision.utils import save_image +from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer + +import os, sys + +from opensora.models.ae import ae_stride_config, getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V_S_122 +from opensora.models.text_encoder import get_text_enc +from opensora.utils.utils import save_video_grid + +sys.path.append(os.path.split(sys.path[0])[0]) +from pipeline_opensora import OpenSoraPipeline + +import imageio + + +def main(args): + # torch.manual_seed(args.seed) + weight_dtype = torch.float16 + device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + # transformer_model = Transformer2DModel.from_pretrained( + # "PixArt-alpha/PixArt-Sigma-XL-2-512-MS", + # subfolder='transformer', cache_dir=args.cache_dir, + # torch_dtype=weight_dtype, + # use_safetensors=True, low_cpu_mem_usage=True + # ) + latent_size = (64, 64) + latent_size_t = 1 + transformer_model = OpenSoraT2V_S_122(in_channels=4, + out_channels=8, + sample_size=latent_size, + sample_size_t=latent_size_t, + activation_fn="gelu-approximate", + attention_bias=True, + attention_type="default", + double_self_attention=False, + norm_elementwise_affine=False, + norm_eps=1e-06, + norm_num_groups=32, + num_vector_embeds=None, + only_cross_attention=False, + upcast_attention=False, + use_linear_projection=False, + use_additional_conditions=False).to(dtype=weight_dtype) + print(2) + path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--PixArt-alpha--PixArt-Sigma-XL-2-512-MS/snapshots/786c445c97ddcc0eb2faa157b131ac71ee1935a2/transformer/diffusion_pytorch_model.safetensors" + from safetensors.torch import load_file as safe_load + ckpt = safe_load(path, device="cpu") + transformer_model.load_state_dict(ckpt) + print(args.text_encoder_name) + text_encoder = T5EncoderModel.from_pretrained('/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = T5Tokenizer.from_pretrained('/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir) + print(3) + vae = AutoencoderKL.from_pretrained("/remote-home1/yeyang/dev3d/Open-Sora-Plan/vae", torch_dtype=torch.float16) + vae.vae_scale_factor = (4, 8, 8) + print(1) + # set eval mode + transformer_model.eval() + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler() + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler() + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model) + pipeline.to(device) + prompt = "A small cactus with a happy face in the Sahara desert." + image = pipeline(prompt, + num_frames=1, + height=512, + width=512, + num_inference_steps=20, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + ).images[0] + image.save("./catcus.png") + image = pipeline(prompt, + num_frames=1, + height=512, + width=512, + num_inference_steps=50, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + ).images[0] + image.save("./catcus50.png") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') + parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) + parser.add_argument("--image_size", type=int, default=512) + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") + parser.add_argument("--guidance_scale", type=float, default=7.5) + parser.add_argument("--sample_method", type=str, default="DPMSolverMultistep") + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument("--fps", type=int, default=24) + parser.add_argument("--run_time", type=int, default=0) + parser.add_argument("--text_prompt", nargs='+') + parser.add_argument('--force_images', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + args = parser.parse_args() + + main(args) \ No newline at end of file diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index b18828f6b..20095181e 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -14,14 +14,13 @@ import shutil from pathlib import Path from typing import Optional - +import gc import numpy as np from einops import rearrange from tqdm import tqdm from dataclasses import field, dataclass from torch.utils.data import DataLoader from copy import deepcopy - import accelerate import torch from torch.nn import functional as F @@ -35,7 +34,7 @@ from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer import diffusers -from diffusers import DDPMScheduler, PNDMScheduler +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler from diffusers.optimization import get_scheduler from diffusers.training_utils import EMAModel, compute_snr from diffusers.utils import check_min_version, is_wandb_available @@ -43,59 +42,71 @@ from opensora.dataset import getdataset, ae_denorm from opensora.models.ae import getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.models.diffusion.diffusion import create_diffusion_T as create_diffusion +from opensora.utils.diffusion import create_diffusion_T as create_diffusion from opensora.models.diffusion.latte.modeling_latte import LatteT2V from opensora.models.text_encoder import get_text_enc, get_text_warpper from opensora.utils.dataset_utils import Collate from opensora.models.ae import ae_stride_config, ae_channel_config from opensora.models.diffusion import Diffusion_models +from opensora.sample.pipeline_opensora import OpenSoraPipeline # Will error if the minimal version of diffusers is not installed. Remove at your own risks. check_min_version("0.24.0") logger = get_logger(__name__) -def generate_timestep_weights(args, num_timesteps): - weights = torch.ones(num_timesteps) - - # Determine the indices to bias - num_to_bias = int(args.timestep_bias_portion * num_timesteps) - - if args.timestep_bias_strategy == "later": - bias_indices = slice(-num_to_bias, None) - elif args.timestep_bias_strategy == "earlier": - bias_indices = slice(0, num_to_bias) - elif args.timestep_bias_strategy == "range": - # Out of the possible 1000 timesteps, we might want to focus on eg. 200-500. - range_begin = args.timestep_bias_begin - range_end = args.timestep_bias_end - if range_begin < 0: - raise ValueError( - "When using the range strategy for timestep bias, you must provide a beginning timestep greater or equal to zero." +@torch.inference_mode() +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step): + validation_prompt = [ + "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", + "The majestic beauty of a waterfall cascading down a cliff into a serene lake." + ] + logger.info(f"Running validation....\n") + model = accelerator.unwrap_model(model) + # scheduler = PNDMScheduler() + scheduler = DPMSolverMultistepScheduler() + videogen_pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model).to(device=accelerator.device) + videos = [] + for prompt in validation_prompt: + logger.info('Processing the ({}) prompt'.format(prompt)) + video = videogen_pipeline(prompt, + video_length=args.num_frames, + height=args.max_image_size, + width=args.max_image_size, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + # enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + ).video + videos.append(video[0]) + # import ipdb;ipdb.set_trace() + gc.collect() + torch.cuda.empty_cache() + videos = torch.stack(videos).numpy() + videos = rearrange(videos, 'b t h w c -> b t c h w') + for tracker in accelerator.trackers: + if tracker.name == "tensorboard": + np_videos = np.stack([np.asarray(vid) for vid in videos]) + tracker.writer.add_video("validation", np_videos, global_step, fps=10) + if tracker.name == "wandb": + import wandb + tracker.log( + { + "validation": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=10) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ] + } ) - if range_end > num_timesteps: - raise ValueError( - "When using the range strategy for timestep bias, you must provide an ending timestep smaller than the number of timesteps." - ) - bias_indices = slice(range_begin, range_end) - else: # 'none' or any other string - return weights - if args.timestep_bias_multiplier <= 0: - return ValueError( - "The parameter --timestep_bias_multiplier is not intended to be used to disable the training of specific timesteps." - " If it was intended to disable timestep bias, use `--timestep_bias_strategy none` instead." - " A timestep bias multiplier less than or equal to 0 is not allowed." - ) - - # Apply the bias - weights[bias_indices] *= args.timestep_bias_multiplier - - # Normalize - weights /= weights.sum() - - return weights - + del videogen_pipeline + gc.collect() + torch.cuda.empty_cache() ################################################################################# # Training Loop # ################################################################################# @@ -115,7 +126,6 @@ def main(args): if args.report_to == "wandb": if not is_wandb_available(): raise ImportError("Make sure to install wandb if you want to use it for logging during training.") - import wandb # Make one log on every process with the configuration for debugging. logging.basicConfig( @@ -165,6 +175,7 @@ def main(args): text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w args.ae_stride = args.ae_stride_h @@ -174,16 +185,17 @@ def main(args): args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." - assert args.max_image_size % ae_stride_h == 0, f"Image size must be divisible by ae_stride_h, but found max_image_size ({args.max_image_size}), ae_stride_h ({ae_stride_h})." + assert args.max_image_size % ae_stride_h == 0, f"Image size must be divisible by ae_stride_h, but found max_image_size ({args.max_image_size}), ae_stride_h ({ae_stride_h})." args.stride_t = ae_stride_t * patch_size_t args.stride = ae_stride_h * patch_size_h latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) + ae.latent_size = latent_size - if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: - args.video_length = video_length = args.num_frames // ae_stride_t + 1 + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 else: - video_length = args.num_frames // ae_stride_t + latent_size_t = args.num_frames // ae_stride_t model = Diffusion_models[args.model]( in_channels=ae_channel_config[args.ae], out_channels=ae_channel_config[args.ae] * 2, @@ -191,6 +203,7 @@ def main(args): # cross_attention_dim=1152, attention_bias=True, sample_size=latent_size, + sample_size_t=latent_size_t, num_vector_embeds=None, activation_fn="gelu-approximate", num_embeds_ada_norm=1000, @@ -202,11 +215,10 @@ def main(args): norm_elementwise_affine=False, norm_eps=1e-6, attention_type='default', - video_length=video_length, attention_mode=args.attention_mode, - compress_kv_factor=args.compress_kv_factor, - use_rope=args.use_rope, - model_max_length=args.model_max_length, + # compress_kv_factor=args.compress_kv_factor, + # use_rope=args.use_rope, + # model_max_length=args.model_max_length, ) model.gradient_checkpointing = args.gradient_checkpointing @@ -240,7 +252,7 @@ def main(args): # Create EMA for the unet. if args.use_ema: ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), model_cls=LatteT2V, model_config=ema_model.config) + ema_model = EMAModel(ema_model.parameters(), model_cls=Diffusion_models[args.model], model_config=ema_model.config) # `accelerate` 0.16.0 will have better support for customized saving if version.parse(accelerate.__version__) >= version.parse("0.16.0"): @@ -268,7 +280,7 @@ def load_model_hook(models, input_dir): model = models.pop() # load diffusers style into model - load_model = LatteT2V.from_pretrained(input_dir, subfolder="model") + load_model = Diffusion_models[args.model].from_pretrained(input_dir, subfolder="model") model.register_to_config(**load_model.config) model.load_state_dict(load_model.state_dict()) @@ -408,20 +420,19 @@ def load_model_hook(models, input_dir): x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 - attn_mask = attn_mask.to(accelerator.device) # B L or B 1+num_images L - input_ids = input_ids.to(accelerator.device) # B L or B 1+num_images L - cond_mask = cond_mask.to(accelerator.device) # B L or B 1+num_images L - print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + attn_mask = attn_mask.to(accelerator.device) # B T+num_images L + input_ids = input_ids.to(accelerator.device) # B 1+num_images L + cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) with torch.no_grad(): # use for loop to avoid OOM, because T5 is too huge... - B, _, _ = input_ids.shape # B T+num_images L b 1+4, L + B, _, _ = input_ids.shape # B 1+num_images L cond = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D # Map input images to latent space + normalize latents if args.use_image_num == 0: x = ae.encode(x) # B C T H W - cond = text_enc(input_ids, cond_mask) # B L -> B L D else: videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] videos = ae.encode(videos) # B C T H W @@ -534,68 +545,7 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu ema_model.copy_to(model.parameters()) if args.enable_tracker: - validation_prompt = "The majestic beauty of a waterfall cascading down a cliff into a serene lake." - logger.info(f"Running validation...Using DDPM naive sampling...\n" - f"Generating {args.num_validation_videos} videos with prompt: {validation_prompt}") - with torch.no_grad(): - # create pipeline - ae_ = getae_wrapper(args.ae)(args.ae_path).to(accelerator.device).eval() - if args.enable_tiling: - ae_.vae.enable_tiling() - ae_.vae.tile_overlap_factor = args.tile_overlap_factor - # text_enc_ = get_text_enc(args).to(accelerator.device).eval() - model_ = LatteT2V.from_pretrained(save_path, subfolder="model").to(accelerator.device).eval() - diffusion_ = create_diffusion(str(250)) - tokenizer_ = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir='./cache_dir') - videos = [] - for idx in range(args.num_validation_videos): - with torch.autocast(device_type='cuda', dtype=weight_dtype): - z = torch.randn(1, model_.in_channels, video_length, - latent_size[0], latent_size[1], device=accelerator.device) - text_tokens_and_mask = tokenizer_( - validation_prompt, - max_length=args.model_max_length, - padding='max_length', - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors='pt' - ) - input_ids = text_tokens_and_mask['input_ids'].to(accelerator.device) - cond_mask = text_tokens_and_mask['attention_mask'].to(accelerator.device) - # cond = text_enc_(input_ids, cond_mask) # B L D - cond = text_enc(input_ids, cond_mask) # B L D - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=None, encoder_attention_mask=cond_mask) - sample_fn = model_.forward - # Sample images: - samples = diffusion_.p_sample_loop( - sample_fn, z.shape, z, clip_denoised=False, model_kwargs=model_kwargs, progress=True, - device=accelerator.device - ) - samples = ae_.decode(samples) - # Save and display images: - video = (ae_denorm[args.ae](samples[0]) * 255).add_(0.5).clamp_(0, 255).to( - dtype=torch.uint8).cpu().contiguous() # t c h w - videos.append(video) - - videos = torch.stack(videos).numpy() - for tracker in accelerator.trackers: - if tracker.name == "tensorboard": - np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video("validation", np_videos, global_step, fps=10) - if tracker.name == "wandb": - tracker.log( - { - "validation": [ - wandb.Video(video, caption=f"{i}: {validation_prompt}", fps=10) - for i, video in enumerate(videos) - ] - } - ) - - # del ae_, text_enc_, model_, diffusion_, tokenizer_ - del ae_, model_, diffusion_, tokenizer_ - torch.cuda.empty_cache() + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step) accelerator.wait_for_everyone() accelerator.end_training() @@ -604,10 +554,8 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--dataset", type=str, required=True) - parser.add_argument("--video_data_path", type=str, required=True) - parser.add_argument("--video_folder", type=str, default='') - parser.add_argument("--image_data_path", type=str, default='') - parser.add_argument("--image_folder", type=str, default='') + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') parser.add_argument("--sample_rate", type=int, default=1) parser.add_argument("--num_frames", type=int, default=17) parser.add_argument("--max_image_size", type=int, default=512) @@ -630,15 +578,11 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=10.0) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--use_deepspeed", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") - parser.add_argument( - "--num_validation_videos", - type=int, - default=2, - help="Number of images that should be generated during validation with `validation_prompt`.", - ) parser.add_argument( "--output_dir", type=str, diff --git a/opensora/train/train_t2v_feature.py b/opensora/train/train_t2v_feature.py deleted file mode 100644 index fbd2ea4c3..000000000 --- a/opensora/train/train_t2v_feature.py +++ /dev/null @@ -1,787 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. - -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. - -""" -A minimal training script for DiT using PyTorch DDP. -""" -import argparse -import logging -import math -import os -import shutil -from pathlib import Path -from typing import Optional - -import numpy as np -from einops import rearrange -from tqdm import tqdm -from dataclasses import field, dataclass -from torch.utils.data import DataLoader -from copy import deepcopy - -import accelerate -import torch -from torch.nn import functional as F -import transformers -from accelerate import Accelerator -from accelerate.logging import get_logger -from accelerate.utils import ProjectConfiguration, set_seed -from huggingface_hub import create_repo -from packaging import version -from tqdm.auto import tqdm -from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer - -import diffusers -from diffusers import DDPMScheduler, PNDMScheduler -from diffusers.optimization import get_scheduler -from diffusers.training_utils import EMAModel, compute_snr -from diffusers.utils import check_min_version, is_wandb_available - -from opensora.dataset import getdataset, ae_denorm -from opensora.models.ae import getae, getae_wrapper -from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.models.diffusion.diffusion import create_diffusion_T as create_diffusion -from opensora.models.diffusion.latte.modeling_latte import LatteT2V -from opensora.models.text_encoder import get_text_enc -from opensora.utils.dataset_utils import Collate -from opensora.models.ae import ae_stride_config, ae_channel_config -from opensora.models.diffusion import Diffusion_models - -# Will error if the minimal version of diffusers is not installed. Remove at your own risks. -check_min_version("0.24.0") -logger = get_logger(__name__) - - -def generate_timestep_weights(args, num_timesteps): - weights = torch.ones(num_timesteps) - - # Determine the indices to bias - num_to_bias = int(args.timestep_bias_portion * num_timesteps) - - if args.timestep_bias_strategy == "later": - bias_indices = slice(-num_to_bias, None) - elif args.timestep_bias_strategy == "earlier": - bias_indices = slice(0, num_to_bias) - elif args.timestep_bias_strategy == "range": - # Out of the possible 1000 timesteps, we might want to focus on eg. 200-500. - range_begin = args.timestep_bias_begin - range_end = args.timestep_bias_end - if range_begin < 0: - raise ValueError( - "When using the range strategy for timestep bias, you must provide a beginning timestep greater or equal to zero." - ) - if range_end > num_timesteps: - raise ValueError( - "When using the range strategy for timestep bias, you must provide an ending timestep smaller than the number of timesteps." - ) - bias_indices = slice(range_begin, range_end) - else: # 'none' or any other string - return weights - if args.timestep_bias_multiplier <= 0: - return ValueError( - "The parameter --timestep_bias_multiplier is not intended to be used to disable the training of specific timesteps." - " If it was intended to disable timestep bias, use `--timestep_bias_strategy none` instead." - " A timestep bias multiplier less than or equal to 0 is not allowed." - ) - - # Apply the bias - weights[bias_indices] *= args.timestep_bias_multiplier - - # Normalize - weights /= weights.sum() - - return weights - - -################################################################################# -# Training Loop # -################################################################################# - -def main(args): - logging_dir = Path(args.output_dir, args.logging_dir) - - accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) - - accelerator = Accelerator( - gradient_accumulation_steps=args.gradient_accumulation_steps, - mixed_precision=args.mixed_precision, - log_with=args.report_to, - project_config=accelerator_project_config, - ) - - if args.report_to == "wandb": - if not is_wandb_available(): - raise ImportError("Make sure to install wandb if you want to use it for logging during training.") - import wandb - - # Make one log on every process with the configuration for debugging. - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO, - ) - logger.info(accelerator.state, main_process_only=False) - if accelerator.is_local_main_process: - transformers.utils.logging.set_verbosity_warning() - diffusers.utils.logging.set_verbosity_info() - else: - transformers.utils.logging.set_verbosity_error() - diffusers.utils.logging.set_verbosity_error() - - # If passed along, set the training seed now. - if args.seed is not None: - set_seed(args.seed) - - # Handle the repository creation - if accelerator.is_main_process: - if args.output_dir is not None: - os.makedirs(args.output_dir, exist_ok=True) - - # if args.push_to_hub: - # repo_id = create_repo( - # repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token - # ).repo_id - - # Create model: - - diffusion = create_diffusion(timestep_respacing="") # default: 1000 steps, linear noise schedule - # ae = getae(args).eval() - # text_enc = get_text_enc(args).eval() - - ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w - args.ae_stride = args.ae_stride_h - patch_size = args.model[-3:] - patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) - args.patch_size = patch_size_h - args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w - assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" - assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" - # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." - assert args.max_image_size % ae_stride_h == 0, f"Image size must be divisible by ae_stride_h, but found max_image_size ({args.max_image_size}), ae_stride_h ({ae_stride_h})." - - latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) - - if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: - args.video_length = video_length = args.num_frames // ae_stride_t + 1 - else: - args.video_length = video_length = args.num_frames // ae_stride_t - model = Diffusion_models[args.model]( - in_channels=ae_channel_config[args.ae], - out_channels=ae_channel_config[args.ae] * 2, - # caption_channels=4096, - # cross_attention_dim=1152, - attention_bias=True, - sample_size=latent_size, - num_vector_embeds=None, - activation_fn="gelu-approximate", - num_embeds_ada_norm=1000, - use_linear_projection=False, - only_cross_attention=False, - double_self_attention=False, - upcast_attention=False, - # norm_type="ada_norm_single", - norm_elementwise_affine=False, - norm_eps=1e-6, - attention_type='default', - video_length=video_length, - attention_mode=args.attention_mode, - # compress_kv=args.compress_kv - ) - model.gradient_checkpointing = args.gradient_checkpointing - - # # use pretrained model? - if args.pretrained: - if 'safetensors' in args.pretrained: - from safetensors.torch import load_file as safe_load - checkpoint = safe_load(args.pretrained, device="cpu") - else: - checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] - model_state_dict = model.state_dict() - missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) - logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') - logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') - # load from pixart-alpha - # pixelart_alpha = torch.load(args.pretrained, map_location='cpu')['state_dict'] - # checkpoint = {} - # for k, v in pixelart_alpha.items(): - # if 'x_embedder' in k or 't_embedder' in k or 'y_embedder' in k: - # checkpoint[k] = v - # if k.startswith('blocks'): - # k_spilt = k.split('.') - # blk_id = str(int(k_spilt[1]) * 2) - # k_spilt[1] = blk_id - # new_k = '.'.join(k_spilt) - # checkpoint[new_k] = v - # missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) - # logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)} keys from {args.pretrained}!') - - # Freeze vae and text encoders. - # ae.requires_grad_(False) - # text_enc.requires_grad_(False) - # Set model as trainable. - model.train() - - # For mixed precision training we cast all non-trainable weigths to half-precision - # as these weights are only used for inference, keeping weights in full precision is not required. - weight_dtype = torch.float32 - if accelerator.mixed_precision == "fp16": - weight_dtype = torch.float16 - elif accelerator.mixed_precision == "bf16": - weight_dtype = torch.bfloat16 - - # Move unet, vae and text_encoder to device and cast to weight_dtype - # The VAE is in float32 to avoid NaN losses. - # ae.to(accelerator.device, dtype=torch.float32) - # text_enc.to(accelerator.device, dtype=weight_dtype) - - # Create EMA for the unet. - if args.use_ema: - ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), model_cls=LatteT2V, model_config=ema_model.config) - - # `accelerate` 0.16.0 will have better support for customized saving - if version.parse(accelerate.__version__) >= version.parse("0.16.0"): - # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format - def save_model_hook(models, weights, output_dir): - if accelerator.is_main_process: - if args.use_ema: - ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) - - for i, model in enumerate(models): - model.save_pretrained(os.path.join(output_dir, "model")) - if weights: # Don't pop if empty - # make sure to pop weight so that corresponding model is not saved again - weights.pop() - - def load_model_hook(models, input_dir): - if args.use_ema: - load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), LatteT2V) - ema_model.load_state_dict(load_model.state_dict()) - ema_model.to(accelerator.device) - del load_model - - for i in range(len(models)): - # pop models so that they are not loaded again - model = models.pop() - - # load diffusers style into model - load_model = LatteT2V.from_pretrained(input_dir, subfolder="model") - model.register_to_config(**load_model.config) - - model.load_state_dict(load_model.state_dict()) - del load_model - - accelerator.register_save_state_pre_hook(save_model_hook) - accelerator.register_load_state_pre_hook(load_model_hook) - - # Enable TF32 for faster training on Ampere GPUs, - # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices - if args.allow_tf32: - torch.backends.cuda.matmul.allow_tf32 = True - - if args.scale_lr: - args.learning_rate = ( - args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes - ) - - # Use 8-bit Adam for lower memory usage or to fine-tune the model in 16GB GPUs - if args.use_8bit_adam: - try: - import bitsandbytes as bnb - except ImportError: - raise ImportError( - "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." - ) - - optimizer_class = bnb.optim.AdamW8bit - else: - optimizer_class = torch.optim.AdamW - - # Optimizer creation - params_to_optimize = model.parameters() - optimizer = optimizer_class( - params_to_optimize, - lr=args.learning_rate, - betas=(args.adam_beta1, args.adam_beta2), - weight_decay=args.adam_weight_decay, - eps=args.adam_epsilon, - ) - - # Setup data: - train_dataset = getdataset(args) - train_dataloader = torch.utils.data.DataLoader( - train_dataset, - shuffle=True, - # collate_fn=Collate(args), # TODO: do not enable dynamic mask in this point - batch_size=args.train_batch_size, - num_workers=args.dataloader_num_workers, - ) - - # Scheduler and math around the number of training steps. - overrode_max_train_steps = False - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if args.max_train_steps is None: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - overrode_max_train_steps = True - - lr_scheduler = get_scheduler( - args.lr_scheduler, - optimizer=optimizer, - num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, - num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, - ) - - # Prepare everything with our `accelerator`. - model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( - model, optimizer, train_dataloader, lr_scheduler - ) - - # We need to recalculate our total training steps as the size of the training dataloader may have changed. - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if overrode_max_train_steps: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - # Afterwards we recalculate our number of training epochs - args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) - - # We need to initialize the trackers we use, and also store our configuration. - # The trackers initializes automatically on the main process. - if accelerator.is_main_process: - accelerator.init_trackers(args.output_dir, config=vars(args)) - - # Train! - total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps - - logger.info("***** Running training *****") - logger.info(f" Num examples = {len(train_dataset)}") - logger.info(f" Num Epochs = {args.num_train_epochs}") - logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") - logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") - logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") - logger.info(f" Total optimization steps = {args.max_train_steps}") - global_step = 0 - first_epoch = 0 - - # Potentially load in the weights and states from a previous save - if args.resume_from_checkpoint: - if args.resume_from_checkpoint != "latest": - path = os.path.basename(args.resume_from_checkpoint) - else: - # Get the most recent checkpoint - dirs = os.listdir(args.output_dir) - dirs = [d for d in dirs if d.startswith("checkpoint")] - dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) - path = dirs[-1] if len(dirs) > 0 else None - - if path is None: - accelerator.print( - f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." - ) - args.resume_from_checkpoint = None - initial_global_step = 0 - else: - accelerator.print(f"Resuming from checkpoint {path}") - accelerator.load_state(os.path.join(args.output_dir, path)) - global_step = int(path.split("-")[1]) - - initial_global_step = global_step - first_epoch = global_step // num_update_steps_per_epoch - - else: - initial_global_step = 0 - - progress_bar = tqdm( - range(0, args.max_train_steps), - initial=initial_global_step, - desc="Steps", - # Only show the progress bar once on each machine. - disable=not accelerator.is_local_main_process, - ) - - for epoch in range(first_epoch, args.num_train_epochs): - train_loss = 0.0 - for step, (x, cond, cond_mask) in enumerate(train_dataloader): - with accelerator.accumulate(model): - # Sample noise that we'll add to the latents - x = x.to(accelerator.device) # B C T H W - # attn_mask = attn_mask.to(device) # B T H W - # assert torch.all(attn_mask.bool()), 'do not enable dynamic input' - attn_mask = None - cond = cond.to(accelerator.device) # B L or B 1+num_images L - cond_mask = cond_mask.to(accelerator.device) # B L or B 1+num_images L - # print(args.use_image_num, x.shape, cond.shape, cond_mask.shape, cond_mask) - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) - t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) - loss_dict = diffusion.training_losses(model, x, t, model_kwargs) - loss = loss_dict["loss"].mean() - - # Gather the losses across all processes for logging (if we use distributed training). - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - train_loss += avg_loss.item() / args.gradient_accumulation_steps - - # Backpropagate - accelerator.backward(loss) - if accelerator.sync_gradients: - params_to_clip = model.parameters() - accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) - optimizer.step() - lr_scheduler.step() - optimizer.zero_grad() - - # Checks if the accelerator has performed an optimization step behind the scenes - if accelerator.sync_gradients: - progress_bar.update(1) - global_step += 1 - accelerator.log({"train_loss": train_loss}, step=global_step) - train_loss = 0.0 - - if args.use_deepspeed or accelerator.is_main_process: - if global_step % args.checkpointing_steps == 0: - # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` - if args.checkpoints_total_limit is not None: - checkpoints = os.listdir(args.output_dir) - checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] - checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) - - # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints - if len(checkpoints) >= args.checkpoints_total_limit: - num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 - removing_checkpoints = checkpoints[0:num_to_remove] - - logger.info( - f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" - ) - logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") - - for removing_checkpoint in removing_checkpoints: - removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) - shutil.rmtree(removing_checkpoint) - - save_path = os.path.join(args.output_dir, f"checkpoint-{global_step}") - accelerator.save_state(save_path) - logger.info(f"Saved state to {save_path}") - - logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} - progress_bar.set_postfix(**logs) - - if global_step >= args.max_train_steps: - break - - if accelerator.is_main_process: - validation_prompt = "The majestic beauty of a waterfall cascading down a cliff into a serene lake. The camera angle provides a bird's eye view of the waterfall." - if global_step % args.checkpointing_steps == 0: - logger.info(f"Running validation... \n" - f"Generating {args.num_validation_videos} videos with prompt: {validation_prompt}") - if args.use_ema: - # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - - if args.enable_tracker: - with torch.no_grad(): - # create pipeline - ae_ = getae_wrapper(args.ae)(args.ae_path).to(accelerator.device).eval() - if args.enable_tiling: - ae_.vae.enable_tiling() - ae_.vae.tile_overlap_factor = args.tile_overlap_factor - text_enc_ = get_text_enc(args).to(accelerator.device).eval() - model_ = LatteT2V.from_pretrained(save_path, subfolder="model").to(accelerator.device).eval() - diffusion_ = create_diffusion(str(250)) - tokenizer_ = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir='./cache_dir') - videos = [] - for idx in range(args.num_validation_videos): - with torch.autocast(device_type='cuda', dtype=weight_dtype): - z = torch.randn(1, model_.in_channels, video_length, - latent_size[0], latent_size[1], device=accelerator.device) - text_tokens_and_mask = tokenizer_( - validation_prompt, - max_length=args.model_max_length, - padding='max_length', - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors='pt' - ) - input_ids = text_tokens_and_mask['input_ids'].to(accelerator.device) - cond_mask = text_tokens_and_mask['attention_mask'].to(accelerator.device) - cond = text_enc_(input_ids, cond_mask) # B L D - # cond = text_enc(input_ids, cond_mask) # B L D - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=None, encoder_attention_mask=cond_mask) - sample_fn = model_.forward - # Sample images: - samples = diffusion_.p_sample_loop( - sample_fn, z.shape, z, clip_denoised=False, model_kwargs=model_kwargs, progress=True, - device=accelerator.device - ) - samples = ae_.decode(samples) - # Save and display images: - video = (ae_denorm[args.ae](samples[0]) * 255).add_(0.5).clamp_(0, 255).to( - dtype=torch.uint8).cpu().contiguous() # t c h w - videos.append(video) - - videos = torch.stack(videos).numpy() - for tracker in accelerator.trackers: - if tracker.name == "tensorboard": - np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video("validation", np_videos, global_step, fps=10) - if tracker.name == "wandb": - tracker.log( - { - "validation": [ - wandb.Video(video, caption=f"{i}: {validation_prompt}", fps=10) - for i, video in enumerate(videos) - ] - } - ) - - del ae_, text_enc_, model_, diffusion_, tokenizer_ - # del ae_, model_, diffusion_, tokenizer_ - torch.cuda.empty_cache() - - accelerator.wait_for_everyone() - accelerator.end_training() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--dataset", type=str, required=True) - parser.add_argument("--data_path", type=str, required=True) - parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="DiT-XL/122") - parser.add_argument("--num_classes", type=int, default=1000) - parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--sample_rate", type=int, default=4) - parser.add_argument("--num_frames", type=int, default=16) - parser.add_argument("--max_image_size", type=int, default=128) - parser.add_argument("--dynamic_frames", action="store_true") - parser.add_argument("--compress_kv", action="store_true") - parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="math") - parser.add_argument("--pretrained", type=str, default=None) - - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) - parser.add_argument('--enable_tiling', action='store_true') - - parser.add_argument("--video_folder", type=str, default='') - parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') - parser.add_argument("--model_max_length", type=int, default=120) - - parser.add_argument("--enable_tracker", action="store_true") - parser.add_argument("--use_image_num", type=int, default=0) - parser.add_argument("--use_img_from_vid", action="store_true") - parser.add_argument("--use_deepspeed", action="store_true") - parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") - parser.add_argument( - "--num_validation_videos", - type=int, - default=2, - help="Number of images that should be generated during validation with `validation_prompt`.", - ) - parser.add_argument( - "--output_dir", - type=str, - default=None, - help="The output directory where the model predictions and checkpoints will be written.", - ) - parser.add_argument( - "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader." - ) - parser.add_argument("--num_train_epochs", type=int, default=100) - parser.add_argument( - "--max_train_steps", - type=int, - default=None, - help="Total number of training steps to perform. If provided, overrides num_train_epochs.", - ) - parser.add_argument( - "--checkpointing_steps", - type=int, - default=500, - help=( - "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" - " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" - " training using `--resume_from_checkpoint`." - ), - ) - parser.add_argument( - "--checkpoints_total_limit", - type=int, - default=None, - help=("Max number of checkpoints to store."), - ) - parser.add_argument( - "--resume_from_checkpoint", - type=str, - default=None, - help=( - "Whether training should be resumed from a previous checkpoint. Use a path saved by" - ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' - ), - ) - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument( - "--gradient_checkpointing", - action="store_true", - help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", - ) - parser.add_argument( - "--learning_rate", - type=float, - default=1e-4, - help="Initial learning rate (after the potential warmup period) to use.", - ) - parser.add_argument( - "--scale_lr", - action="store_true", - default=False, - help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.", - ) - parser.add_argument( - "--lr_scheduler", - type=str, - default="constant", - help=( - 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' - ' "constant", "constant_with_warmup"]' - ), - ) - parser.add_argument( - "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." - ) - parser.add_argument( - "--timestep_bias_strategy", - type=str, - default="none", - choices=["earlier", "later", "range", "none"], - help=( - "The timestep bias strategy, which may help direct the model toward learning low or high frequency details." - " Choices: ['earlier', 'later', 'range', 'none']." - " The default is 'none', which means no bias is applied, and training proceeds normally." - " The value of 'later' will increase the frequency of the model's final training timesteps." - ), - ) - parser.add_argument( - "--timestep_bias_multiplier", - type=float, - default=1.0, - help=( - "The multiplier for the bias. Defaults to 1.0, which means no bias is applied." - " A value of 2.0 will double the weight of the bias, and a value of 0.5 will halve it." - ), - ) - parser.add_argument( - "--timestep_bias_begin", - type=int, - default=0, - help=( - "When using `--timestep_bias_strategy=range`, the beginning (inclusive) timestep to bias." - " Defaults to zero, which equates to having no specific bias." - ), - ) - parser.add_argument( - "--timestep_bias_end", - type=int, - default=1000, - help=( - "When using `--timestep_bias_strategy=range`, the final timestep (inclusive) to bias." - " Defaults to 1000, which is the number of timesteps that Stable Diffusion is trained on." - ), - ) - parser.add_argument( - "--timestep_bias_portion", - type=float, - default=0.25, - help=( - "The portion of timesteps to bias. Defaults to 0.25, which 25% of timesteps will be biased." - " A value of 0.5 will bias one half of the timesteps. The value provided for `--timestep_bias_strategy` determines" - " whether the biased portions are in the earlier or later timesteps." - ), - ) - parser.add_argument( - "--snr_gamma", - type=float, - default=None, - help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. " - "More details here: https://arxiv.org/abs/2303.09556.", - ) - parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") - parser.add_argument( - "--allow_tf32", - action="store_true", - help=( - "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" - " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" - ), - ) - parser.add_argument( - "--dataloader_num_workers", - type=int, - default=10, - help=( - "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process." - ), - ) - parser.add_argument( - "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes." - ) - parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") - parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") - parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") - parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") - parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") - parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.") - parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.") - parser.add_argument( - "--prediction_type", - type=str, - default=None, - help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.", - ) - parser.add_argument( - "--hub_model_id", - type=str, - default=None, - help="The name of the repository to keep in sync with the local `output_dir`.", - ) - parser.add_argument( - "--logging_dir", - type=str, - default="logs", - help=( - "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" - " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." - ), - ) - parser.add_argument( - "--report_to", - type=str, - default="tensorboard", - help=( - 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' - ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' - ), - ) - parser.add_argument( - "--mixed_precision", - type=str, - default=None, - choices=["no", "fp16", "bf16"], - help=( - "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" - " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" - " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." - ), - ) - parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") - parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") - - args = parser.parse_args() - main(args) diff --git a/opensora/train/train_t2v_t5_feature.py b/opensora/train/train_t2v_t5_feature.py deleted file mode 100644 index a8f3543e0..000000000 --- a/opensora/train/train_t2v_t5_feature.py +++ /dev/null @@ -1,825 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. - -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. - -""" -A minimal training script for DiT using PyTorch DDP. -""" -import argparse -import logging -import math -import os -import shutil -import sys -from pathlib import Path -from typing import Optional - -import numpy as np -from PIL import Image -from einops import rearrange -from tqdm import tqdm -from dataclasses import field, dataclass -from torch.utils.data import DataLoader -from copy import deepcopy - -import accelerate -import torch -from torch.nn import functional as F -import transformers -from accelerate import Accelerator -from accelerate.logging import get_logger -from accelerate.utils import ProjectConfiguration, set_seed -from huggingface_hub import create_repo -from packaging import version -from tqdm.auto import tqdm -from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer - -import diffusers -from diffusers import DDPMScheduler, PNDMScheduler -from diffusers.optimization import get_scheduler -from diffusers.training_utils import EMAModel, compute_snr -from diffusers.utils import check_min_version, is_wandb_available - -from examples.rec_imvi_vae import custom_to_video -from opensora.dataset import getdataset, ae_denorm -from opensora.models.ae import getae, getae_wrapper -from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.models.diffusion.diffusion import create_diffusion_T as create_diffusion -from opensora.models.diffusion.latte.modeling_latte import LatteT2V -from opensora.models.text_encoder import get_text_enc -from opensora.utils.dataset_utils import Collate -from opensora.models.ae import ae_stride_config, ae_channel_config -from opensora.models.diffusion import Diffusion_models - -# Will error if the minimal version of diffusers is not installed. Remove at your own risks. -check_min_version("0.24.0") -logger = get_logger(__name__) - - -def generate_timestep_weights(args, num_timesteps): - weights = torch.ones(num_timesteps) - - # Determine the indices to bias - num_to_bias = int(args.timestep_bias_portion * num_timesteps) - - if args.timestep_bias_strategy == "later": - bias_indices = slice(-num_to_bias, None) - elif args.timestep_bias_strategy == "earlier": - bias_indices = slice(0, num_to_bias) - elif args.timestep_bias_strategy == "range": - # Out of the possible 1000 timesteps, we might want to focus on eg. 200-500. - range_begin = args.timestep_bias_begin - range_end = args.timestep_bias_end - if range_begin < 0: - raise ValueError( - "When using the range strategy for timestep bias, you must provide a beginning timestep greater or equal to zero." - ) - if range_end > num_timesteps: - raise ValueError( - "When using the range strategy for timestep bias, you must provide an ending timestep smaller than the number of timesteps." - ) - bias_indices = slice(range_begin, range_end) - else: # 'none' or any other string - return weights - if args.timestep_bias_multiplier <= 0: - return ValueError( - "The parameter --timestep_bias_multiplier is not intended to be used to disable the training of specific timesteps." - " If it was intended to disable timestep bias, use `--timestep_bias_strategy none` instead." - " A timestep bias multiplier less than or equal to 0 is not allowed." - ) - - # Apply the bias - weights[bias_indices] *= args.timestep_bias_multiplier - - # Normalize - weights /= weights.sum() - - return weights - - -################################################################################# -# Training Loop # -################################################################################# - -def main(args): - logging_dir = Path(args.output_dir, args.logging_dir) - - accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) - - accelerator = Accelerator( - gradient_accumulation_steps=args.gradient_accumulation_steps, - mixed_precision=args.mixed_precision, - log_with=args.report_to, - project_config=accelerator_project_config, - ) - - if args.report_to == "wandb": - if not is_wandb_available(): - raise ImportError("Make sure to install wandb if you want to use it for logging during training.") - import wandb - - # Make one log on every process with the configuration for debugging. - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO, - ) - logger.info(accelerator.state, main_process_only=False) - if accelerator.is_local_main_process: - transformers.utils.logging.set_verbosity_warning() - diffusers.utils.logging.set_verbosity_info() - else: - transformers.utils.logging.set_verbosity_error() - diffusers.utils.logging.set_verbosity_error() - - # If passed along, set the training seed now. - if args.seed is not None: - set_seed(args.seed) - - # Handle the repository creation - if accelerator.is_main_process: - if args.output_dir is not None: - os.makedirs(args.output_dir, exist_ok=True) - - # if args.push_to_hub: - # repo_id = create_repo( - # repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token - # ).repo_id - - # Create model: - - diffusion = create_diffusion(timestep_respacing="") # default: 1000 steps, linear noise schedule - ae = getae_wrapper(args.ae)(args.ae_path).eval() - if args.enable_tiling: - ae.vae.enable_tiling() - ae.vae.tile_overlap_factor = args.tile_overlap_factor - # text_enc = get_text_enc(args).eval() - - ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w - args.ae_stride = args.ae_stride_h - patch_size = args.model[-3:] - patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) - args.patch_size = patch_size_h - args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w - assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" - assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" - # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." - assert args.max_image_size % ae_stride_h == 0, f"Image size must be divisible by ae_stride_h, but found max_image_size ({args.max_image_size}), ae_stride_h ({ae_stride_h})." - - latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) - - if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: - args.video_length = video_length = args.num_frames // ae_stride_t + 1 - else: - args.video_length = video_length = args.num_frames // ae_stride_t - model = Diffusion_models[args.model]( - in_channels=ae_channel_config[args.ae], - out_channels=ae_channel_config[args.ae] * 2, - # caption_channels=4096, - # cross_attention_dim=1152, - attention_bias=True, - sample_size=latent_size, - num_vector_embeds=None, - activation_fn="gelu-approximate", - num_embeds_ada_norm=1000, - use_linear_projection=False, - only_cross_attention=False, - double_self_attention=False, - upcast_attention=False, - # norm_type="ada_norm_single", - norm_elementwise_affine=False, - norm_eps=1e-6, - attention_type='default', - video_length=video_length, - attention_mode=args.attention_mode, - # compress_kv=args.compress_kv - ) - model.gradient_checkpointing = args.gradient_checkpointing - - # # use pretrained model? - if args.pretrained: - if 'safetensors' in args.pretrained: - from safetensors.torch import load_file as safe_load - checkpoint = safe_load(args.pretrained, device="cpu") - else: - checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] - model_state_dict = model.state_dict() - missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) - logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') - logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') - # load from pixart-alpha - # pixelart_alpha = torch.load(args.pretrained, map_location='cpu')['state_dict'] - # checkpoint = {} - # for k, v in pixelart_alpha.items(): - # if 'x_embedder' in k or 't_embedder' in k or 'y_embedder' in k: - # checkpoint[k] = v - # if k.startswith('blocks'): - # k_spilt = k.split('.') - # blk_id = str(int(k_spilt[1]) * 2) - # k_spilt[1] = blk_id - # new_k = '.'.join(k_spilt) - # checkpoint[new_k] = v - # missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) - # logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)} keys from {args.pretrained}!') - - # Freeze vae and text encoders. - ae.requires_grad_(False) - # text_enc.requires_grad_(False) - # Set model as trainable. - model.train() - - # For mixed precision training we cast all non-trainable weigths to half-precision - # as these weights are only used for inference, keeping weights in full precision is not required. - weight_dtype = torch.float32 - if accelerator.mixed_precision == "fp16": - weight_dtype = torch.float16 - elif accelerator.mixed_precision == "bf16": - weight_dtype = torch.bfloat16 - - # Move unet, vae and text_encoder to device and cast to weight_dtype - # The VAE is in float32 to avoid NaN losses. - # ae.to(accelerator.device, dtype=torch.float32) - ae.to(accelerator.device, dtype=weight_dtype) - model.to(accelerator.device, dtype=weight_dtype) - # text_enc.to(accelerator.device, dtype=weight_dtype) - - # Create EMA for the unet. - if args.use_ema: - ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), model_cls=LatteT2V, model_config=ema_model.config) - - # `accelerate` 0.16.0 will have better support for customized saving - if version.parse(accelerate.__version__) >= version.parse("0.16.0"): - # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format - def save_model_hook(models, weights, output_dir): - if accelerator.is_main_process: - if args.use_ema: - ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) - - for i, model in enumerate(models): - model.save_pretrained(os.path.join(output_dir, "model")) - if weights: # Don't pop if empty - # make sure to pop weight so that corresponding model is not saved again - weights.pop() - - def load_model_hook(models, input_dir): - if args.use_ema: - load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), LatteT2V) - ema_model.load_state_dict(load_model.state_dict()) - ema_model.to(accelerator.device) - del load_model - - for i in range(len(models)): - # pop models so that they are not loaded again - model = models.pop() - - # load diffusers style into model - load_model = LatteT2V.from_pretrained(input_dir, subfolder="model") - model.register_to_config(**load_model.config) - - model.load_state_dict(load_model.state_dict()) - del load_model - - accelerator.register_save_state_pre_hook(save_model_hook) - accelerator.register_load_state_pre_hook(load_model_hook) - - # Enable TF32 for faster training on Ampere GPUs, - # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices - if args.allow_tf32: - torch.backends.cuda.matmul.allow_tf32 = True - - if args.scale_lr: - args.learning_rate = ( - args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes - ) - - # Use 8-bit Adam for lower memory usage or to fine-tune the model in 16GB GPUs - if args.use_8bit_adam: - try: - import bitsandbytes as bnb - except ImportError: - raise ImportError( - "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." - ) - - optimizer_class = bnb.optim.AdamW8bit - else: - optimizer_class = torch.optim.AdamW - - # Optimizer creation - params_to_optimize = model.parameters() - optimizer = optimizer_class( - params_to_optimize, - lr=args.learning_rate, - betas=(args.adam_beta1, args.adam_beta2), - weight_decay=args.adam_weight_decay, - eps=args.adam_epsilon, - ) - - # Setup data: - train_dataset = getdataset(args) - train_dataloader = torch.utils.data.DataLoader( - train_dataset, - shuffle=True, - # collate_fn=Collate(args), # TODO: do not enable dynamic mask in this point - batch_size=args.train_batch_size, - num_workers=args.dataloader_num_workers, - ) - - # Scheduler and math around the number of training steps. - overrode_max_train_steps = False - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if args.max_train_steps is None: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - overrode_max_train_steps = True - - lr_scheduler = get_scheduler( - args.lr_scheduler, - optimizer=optimizer, - num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, - num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, - ) - - # Prepare everything with our `accelerator`. - model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( - model, optimizer, train_dataloader, lr_scheduler - ) - - # We need to recalculate our total training steps as the size of the training dataloader may have changed. - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if overrode_max_train_steps: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - # Afterwards we recalculate our number of training epochs - args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) - - # We need to initialize the trackers we use, and also store our configuration. - # The trackers initializes automatically on the main process. - if accelerator.is_main_process: - accelerator.init_trackers(args.output_dir, config=vars(args)) - - # Train! - total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps - - logger.info("***** Running training *****") - logger.info(f" Num examples = {len(train_dataset)}") - logger.info(f" Num Epochs = {args.num_train_epochs}") - logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") - logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") - logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") - logger.info(f" Total optimization steps = {args.max_train_steps}") - global_step = 0 - first_epoch = 0 - - # Potentially load in the weights and states from a previous save - if args.resume_from_checkpoint: - if args.resume_from_checkpoint != "latest": - path = os.path.basename(args.resume_from_checkpoint) - else: - # Get the most recent checkpoint - dirs = os.listdir(args.output_dir) - dirs = [d for d in dirs if d.startswith("checkpoint")] - dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) - path = dirs[-1] if len(dirs) > 0 else None - - if path is None: - accelerator.print( - f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." - ) - args.resume_from_checkpoint = None - initial_global_step = 0 - else: - accelerator.print(f"Resuming from checkpoint {path}") - accelerator.load_state(os.path.join(args.output_dir, path)) - global_step = int(path.split("-")[1]) - - initial_global_step = global_step - first_epoch = global_step // num_update_steps_per_epoch - - else: - initial_global_step = 0 - - progress_bar = tqdm( - range(0, args.max_train_steps), - initial=initial_global_step, - desc="Steps", - # Only show the progress bar once on each machine. - disable=not accelerator.is_local_main_process, - ) - - for epoch in range(first_epoch, args.num_train_epochs): - train_loss = 0.0 - for step, (x, cond, cond_mask) in enumerate(train_dataloader): - with accelerator.accumulate(model): - # Sample noise that we'll add to the latents - x = x.to(accelerator.device) # B C T H W - # print(x.dtype) - # attn_mask = attn_mask.to(device) # B T H W - # assert torch.all(attn_mask.bool()), 'do not enable dynamic input' - attn_mask = None - cond = cond.to(accelerator.device, dtype=weight_dtype) # B L or B 1+num_images L - cond_mask = cond_mask.to(accelerator.device) # B L or B 1+num_images L - - with torch.no_grad(): - # Map input images to latent space + normalize latents - if args.use_image_num == 0: - x = ae.encode(x.to(dtype=weight_dtype)) # B C T H W - else: - videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] - videos = ae.encode(videos.to(dtype=weight_dtype)) # B C T H W - - # videos = ae.decode(videos.to(dtype=weight_dtype))[0] - # videos = videos.transpose(0, 1) - # custom_to_video(videos.to(torch.float32), fps=24, output_file='tmp.mp4') - - images = rearrange(images, 'b c t h w -> (b t) c 1 h w') - images = ae.encode(images.to(dtype=weight_dtype)) - - # images = ae.decode(images.to(dtype=weight_dtype)) - # x = images[0, 0, :, :, :].to(torch.float32) - # x = x.squeeze() - # x = x.detach().cpu().numpy() - # x = np.clip(x, -1, 1) - # x = (x + 1) / 2 - # x = (255 * x).astype(np.uint8) - # x = x.transpose(1, 2, 0) - # image = Image.fromarray(x) - # image.save('tmp.jpg') - # sys.exit() - - images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) - x = torch.cat([videos, images], dim=2) - - # print(args.use_image_num, x.shape, cond.shape, cond_mask.shape, cond_mask) - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) - t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) - loss_dict = diffusion.training_losses(model, x, t, model_kwargs) - loss = loss_dict["loss"].mean() - - # Gather the losses across all processes for logging (if we use distributed training). - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - train_loss += avg_loss.item() / args.gradient_accumulation_steps - - # Backpropagate - accelerator.backward(loss) - if accelerator.sync_gradients: - params_to_clip = model.parameters() - accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) - optimizer.step() - lr_scheduler.step() - optimizer.zero_grad() - - # Checks if the accelerator has performed an optimization step behind the scenes - if accelerator.sync_gradients: - progress_bar.update(1) - global_step += 1 - accelerator.log({"train_loss": train_loss}, step=global_step) - train_loss = 0.0 - - if args.use_deepspeed or accelerator.is_main_process: - if global_step % args.checkpointing_steps == 0: - # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` - if args.checkpoints_total_limit is not None: - checkpoints = os.listdir(args.output_dir) - checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] - checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) - - # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints - if len(checkpoints) >= args.checkpoints_total_limit: - num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 - removing_checkpoints = checkpoints[0:num_to_remove] - - logger.info( - f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" - ) - logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") - - for removing_checkpoint in removing_checkpoints: - removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) - shutil.rmtree(removing_checkpoint) - - save_path = os.path.join(args.output_dir, f"checkpoint-{global_step}") - accelerator.save_state(save_path) - logger.info(f"Saved state to {save_path}") - - logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} - progress_bar.set_postfix(**logs) - - if global_step >= args.max_train_steps: - break - - if accelerator.is_main_process: - validation_prompt = "The majestic beauty of a waterfall cascading down a cliff into a serene lake. The camera angle provides a bird's eye view of the waterfall." - if global_step % args.checkpointing_steps == 0: - logger.info(f"Running validation... \n" - f"Generating {args.num_validation_videos} videos with prompt: {validation_prompt}") - if args.use_ema: - # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - if args.enable_tracker: - with torch.no_grad(): - # create pipeline - ae_ = getae_wrapper(args.ae)(args.ae_path).to(accelerator.device).eval() - if args.enable_tiling: - ae_.vae.enable_tiling() - ae_.vae.tile_overlap_factor = args.tile_overlap_factor - text_enc_ = get_text_enc(args).to(accelerator.device).eval() - model_ = LatteT2V.from_pretrained(save_path, subfolder="model").to(accelerator.device).eval() - diffusion_ = create_diffusion(str(500)) - tokenizer_ = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir='./cache_dir') - videos = [] - for idx in range(args.num_validation_videos): - with torch.autocast(device_type='cuda', dtype=weight_dtype): - z = torch.randn(1, model_.in_channels, video_length, - latent_size[0], latent_size[1], device=accelerator.device) - text_tokens_and_mask = tokenizer_( - validation_prompt, - max_length=args.model_max_length, - padding='max_length', - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors='pt' - ) - input_ids = text_tokens_and_mask['input_ids'].to(accelerator.device) - cond_mask = text_tokens_and_mask['attention_mask'].to(accelerator.device) - cond = text_enc_(input_ids, cond_mask) # B L D - # cond = text_enc(input_ids, cond_mask) # B L D - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=None, encoder_attention_mask=cond_mask) - sample_fn = model_.forward - # Sample images: - samples = diffusion_.p_sample_loop( - sample_fn, z.shape, z, clip_denoised=False, model_kwargs=model_kwargs, progress=True, - device=accelerator.device - ) - samples = ae_.decode(samples) - # Save and display images: - video = (ae_denorm[args.ae](samples[0]) * 255).add_(0.5).clamp_(0, 255).to(dtype=torch.uint8).cpu().contiguous() # t c h w - videos.append(video) - - videos = torch.stack(videos).numpy() - for tracker in accelerator.trackers: - if tracker.name == "tensorboard": - np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video("validation", np_videos, global_step, fps=24) - if tracker.name == "wandb": - tracker.log( - { - "validation": [ - wandb.Video(video, caption=f"{i}: {validation_prompt}", fps=24) - for i, video in enumerate(videos) - ] - } - ) - - del ae_, text_enc_, model_, diffusion_, tokenizer_ - # del ae_, model_, diffusion_, tokenizer_ - torch.cuda.empty_cache() - - accelerator.wait_for_everyone() - accelerator.end_training() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--dataset", type=str, required=True) - parser.add_argument("--data_path", type=str, required=True) - parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="DiT-XL/122") - parser.add_argument("--num_classes", type=int, default=1000) - parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--sample_rate", type=int, default=4) - parser.add_argument("--num_frames", type=int, default=16) - parser.add_argument("--max_image_size", type=int, default=128) - parser.add_argument("--dynamic_frames", action="store_true") - parser.add_argument("--compress_kv", action="store_true") - parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="math") - parser.add_argument("--pretrained", type=str, default=None) - - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) - parser.add_argument('--enable_tiling', action='store_true') - - parser.add_argument("--video_folder", type=str, default='') - parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') - parser.add_argument("--model_max_length", type=int, default=120) - - parser.add_argument("--use_image_num", type=int, default=0) - parser.add_argument("--use_img_from_vid", action="store_true") - parser.add_argument("--enable_tracker", action="store_true") - parser.add_argument("--use_deepspeed", action="store_true") - parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") - parser.add_argument( - "--num_validation_videos", - type=int, - default=2, - help="Number of images that should be generated during validation with `validation_prompt`.", - ) - parser.add_argument( - "--output_dir", - type=str, - default=None, - help="The output directory where the model predictions and checkpoints will be written.", - ) - parser.add_argument( - "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader." - ) - parser.add_argument("--num_train_epochs", type=int, default=100) - parser.add_argument( - "--max_train_steps", - type=int, - default=None, - help="Total number of training steps to perform. If provided, overrides num_train_epochs.", - ) - parser.add_argument( - "--checkpointing_steps", - type=int, - default=500, - help=( - "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" - " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" - " training using `--resume_from_checkpoint`." - ), - ) - parser.add_argument( - "--checkpoints_total_limit", - type=int, - default=None, - help=("Max number of checkpoints to store."), - ) - parser.add_argument( - "--resume_from_checkpoint", - type=str, - default=None, - help=( - "Whether training should be resumed from a previous checkpoint. Use a path saved by" - ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' - ), - ) - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument( - "--gradient_checkpointing", - action="store_true", - help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", - ) - parser.add_argument( - "--learning_rate", - type=float, - default=1e-4, - help="Initial learning rate (after the potential warmup period) to use.", - ) - parser.add_argument( - "--scale_lr", - action="store_true", - default=False, - help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.", - ) - parser.add_argument( - "--lr_scheduler", - type=str, - default="constant", - help=( - 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' - ' "constant", "constant_with_warmup"]' - ), - ) - parser.add_argument( - "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." - ) - parser.add_argument( - "--timestep_bias_strategy", - type=str, - default="none", - choices=["earlier", "later", "range", "none"], - help=( - "The timestep bias strategy, which may help direct the model toward learning low or high frequency details." - " Choices: ['earlier', 'later', 'range', 'none']." - " The default is 'none', which means no bias is applied, and training proceeds normally." - " The value of 'later' will increase the frequency of the model's final training timesteps." - ), - ) - parser.add_argument( - "--timestep_bias_multiplier", - type=float, - default=1.0, - help=( - "The multiplier for the bias. Defaults to 1.0, which means no bias is applied." - " A value of 2.0 will double the weight of the bias, and a value of 0.5 will halve it." - ), - ) - parser.add_argument( - "--timestep_bias_begin", - type=int, - default=0, - help=( - "When using `--timestep_bias_strategy=range`, the beginning (inclusive) timestep to bias." - " Defaults to zero, which equates to having no specific bias." - ), - ) - parser.add_argument( - "--timestep_bias_end", - type=int, - default=1000, - help=( - "When using `--timestep_bias_strategy=range`, the final timestep (inclusive) to bias." - " Defaults to 1000, which is the number of timesteps that Stable Diffusion is trained on." - ), - ) - parser.add_argument( - "--timestep_bias_portion", - type=float, - default=0.25, - help=( - "The portion of timesteps to bias. Defaults to 0.25, which 25% of timesteps will be biased." - " A value of 0.5 will bias one half of the timesteps. The value provided for `--timestep_bias_strategy` determines" - " whether the biased portions are in the earlier or later timesteps." - ), - ) - parser.add_argument( - "--snr_gamma", - type=float, - default=None, - help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. " - "More details here: https://arxiv.org/abs/2303.09556.", - ) - parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") - parser.add_argument( - "--allow_tf32", - action="store_true", - help=( - "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" - " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" - ), - ) - parser.add_argument( - "--dataloader_num_workers", - type=int, - default=10, - help=( - "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process." - ), - ) - parser.add_argument( - "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes." - ) - parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") - parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") - parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") - parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") - parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") - parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.") - parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.") - parser.add_argument( - "--prediction_type", - type=str, - default=None, - help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.", - ) - parser.add_argument( - "--hub_model_id", - type=str, - default=None, - help="The name of the repository to keep in sync with the local `output_dir`.", - ) - parser.add_argument( - "--logging_dir", - type=str, - default="logs", - help=( - "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" - " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." - ), - ) - parser.add_argument( - "--report_to", - type=str, - default="tensorboard", - help=( - 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' - ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' - ), - ) - parser.add_argument( - "--mixed_precision", - type=str, - default=None, - choices=["no", "fp16", "bf16"], - help=( - "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" - " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" - " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." - ), - ) - parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") - parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") - - args = parser.parse_args() - main(args) \ No newline at end of file diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/sample_image.sh index 303dac6d7..8d929997e 100644 --- a/scripts/text_condition/sample_image.sh +++ b/scripts/text_condition/sample_image.sh @@ -1,4 +1,4 @@ -CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v.py \ +CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v_pixsig.py \ --model_path LanguageBind/Open-Sora-Plan-v1.0.0 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ diff --git a/scripts/text_condition/train_videoae_65x1024x1024.sh b/scripts/text_condition/train_videoae_65x1024x1024.sh deleted file mode 100644 index 6b5d4fb09..000000000 --- a/scripts/text_condition/train_videoae_65x1024x1024.sh +++ /dev/null @@ -1,39 +0,0 @@ -export WANDB_KEY="" -export ENTITY="" -export PROJECT="1024" -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model LatteT2V-XL/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "../../Open-Sora-Plan/cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "../../Open-Sora-Plan/CausalVAEModel_4x8x8/" \ - --video_data_path "../../Open-Sora-Plan/sharegpt4v_path_cap_64x512x512_mixkit.json" \ - --video_folder /remote-home1/dataset/data_split_tt \ - --image_data_path "../../../dataset/image_114054.json" \ - --image_folder "../../../dataset/picture" \ - --sample_rate 1 \ - --num_frames 65 \ - --max_image_size 1024 \ - --gradient_checkpointing \ - --attention_mode xformers \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-05 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="tensorboard" \ - --checkpointing_steps=100 \ - --output_dir="1024" \ - --allow_tf32 \ - --pretrained ../../65x512x512/diffusion_pytorch_model.safetensors \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 4 \ - --use_img_from_vid \ - --enable_tiling diff --git a/scripts/text_condition/train_videoae_65x512x512_d64.sh b/scripts/text_condition/train_videoae_65x512x512_d64.sh deleted file mode 100644 index d93ea8773..000000000 --- a/scripts/text_condition/train_videoae_65x512x512_d64.sh +++ /dev/null @@ -1,39 +0,0 @@ -export WANDB_KEY="" -export ENTITY="" -export PROJECT="512_d64" -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model LatteT2V-D64-XL/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "../../Open-Sora-Plan/cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "../../Open-Sora-Plan/CausalVAEModel_4x8x8/" \ - --video_data_path "../../Open-Sora-Plan/sharegpt4v_path_cap_64x512x512_mixkit.json" \ - --video_folder /remote-home1/dataset/data_split_tt \ - --image_data_path "../../../dataset/image_114054.json" \ - --image_folder "../../../dataset/picture" \ - --sample_rate 1 \ - --num_frames 65 \ - --max_image_size 512 \ - --gradient_checkpointing \ - --attention_mode xformers \ - --train_batch_size=4 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-05 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="tensorboard" \ - --checkpointing_steps=200 \ - --output_dir="512_d64" \ - --allow_tf32 \ - --pretrained t2v.pt \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 4 \ - --use_img_from_vid \ - --enable_tiling diff --git a/scripts/text_condition/train_videoae_65x512x512_rope.sh b/scripts/text_condition/train_videoae_65x512x512_rope.sh deleted file mode 100644 index dcd9a87b2..000000000 --- a/scripts/text_condition/train_videoae_65x512x512_rope.sh +++ /dev/null @@ -1,40 +0,0 @@ -export WANDB_KEY="" -export ENTITY="" -export PROJECT="512_rope_abs" -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model LatteT2V-XL/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "../../Open-Sora-Plan/cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "../../Open-Sora-Plan/CausalVAEModel_4x8x8/" \ - --video_data_path "../../Open-Sora-Plan/sharegpt4v_path_cap_64x512x512_mixkit.json" \ - --video_folder /remote-home1/dataset/data_split_tt \ - --image_data_path "../../../dataset/image_114054.json" \ - --image_folder "../../../dataset/picture" \ - --sample_rate 1 \ - --num_frames 65 \ - --max_image_size 512 \ - --gradient_checkpointing \ - --attention_mode xformers \ - --train_batch_size=4 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-05 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="tensorboard" \ - --checkpointing_steps=200 \ - --output_dir="512_rope_abs" \ - --allow_tf32 \ - --pretrained 512_useimg/checkpoint-5500/model/diffusion_pytorch_model.safetensors \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 4 \ - --use_img_from_vid \ - --enable_tiling \ - --use_rope From c6a486c5431b64d7d1c3b33f99a7b4c235e7e028 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 2 May 2024 12:11:25 +0800 Subject: [PATCH 003/134] load pretrained weight --- opensora/dataset/t2v_datasets.py | 16 ++++++----- .../diffusion/opensora/modeling_opensora.py | 13 ++++++--- opensora/train/train_t2v.py | 8 ++++-- .../train_videoae_65x512x512.sh | 27 +++++++++---------- scripts/train_data/image_data.txt | 1 + scripts/train_data/video_data.txt | 2 ++ 6 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 scripts/train_data/image_data.txt create mode 100644 scripts/train_data/video_data.txt diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index f4cc7c623..224439c83 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -45,14 +45,16 @@ def __len__(self): def __getitem__(self, idx): # try: - video_data = self.get_video(idx) - image_data = {} - if self.use_image_num != 0 and self.use_img_from_vid: - image_data = self.get_image_from_video(video_data) - elif self.use_image_num != 0 and not self.use_img_from_vid: - image_data = self.get_image(idx) + video_data, image_data = {}, {} + if self.num_frames != 1: + video_data = self.get_video(idx) + if self.use_image_num != 0: + if self.use_img_from_vid: + image_data = self.get_image_from_video(video_data) + else: + image_data = self.get_image(idx) else: - raise NotImplementedError + video_data = self.get_image(idx) # 1 frame video as image return dict(video_data=video_data, image_data=image_data) # except Exception as e: # print(f'Error with {e}, {self.vid_cap_list[idx]}') diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index a8d9d00c4..acc34c669 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -548,7 +548,7 @@ def OpenSoraT2V_S_222(**kwargs): 'use_rope': False, 'model_max_length': 300, 'max_image_size': 512, - 'num_frames': 1, + 'num_frames': 17, 'use_image_num': 0, 'compress_kv_factor': 1 } @@ -565,7 +565,7 @@ def OpenSoraT2V_S_222(**kwargs): args.video_length = video_length = args.num_frames // ae_stride_t device = torch.device('cuda:0') - model = OpenSoraT2V_S_122(in_channels=4, + model = OpenSoraT2V_S_222(in_channels=4, out_channels=8, sample_size=latent_size, sample_size_t=video_length, @@ -582,11 +582,16 @@ def OpenSoraT2V_S_222(**kwargs): use_linear_projection=False, use_additional_conditions=False).to(device) try: - import ipdb;ipdb.set_trace() path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--PixArt-alpha--PixArt-Sigma-XL-2-512-MS/snapshots/786c445c97ddcc0eb2faa157b131ac71ee1935a2/transformer/diffusion_pytorch_model.safetensors" from safetensors.torch import load_file as safe_load ckpt = safe_load(path, device="cpu") - model.load_state_dict(ckpt) + import ipdb;ipdb.set_trace() + if ckpt['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and ckpt['pos_embed.proj.weight'].ndim == 4: + repeat = model.pos_embed.proj.weight.shape[2] + ckpt['pos_embed.proj.weight'] = ckpt['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + del ckpt['proj_out.weight'], ckpt['proj_out.bias'] + msg = model.load_state_dict(ckpt, strict=False) + print(msg) except Exception as e: print(e) print(model) diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 20095181e..2e470cc36 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -224,10 +224,14 @@ def main(args): # # use pretrained model? if args.pretrained: - if 'safetensors' in args.pretrained: + if 'safetensors' in args.pretrained: # pixart series from safetensors.torch import load_file as safe_load checkpoint = safe_load(args.pretrained, device="cpu") - else: + if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + repeat = model.pos_embed.proj.weight.shape[2] + checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] model_state_dict = model.state_dict() missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) diff --git a/scripts/text_condition/train_videoae_65x512x512.sh b/scripts/text_condition/train_videoae_65x512x512.sh index 30414c65e..3d4f67123 100644 --- a/scripts/text_condition/train_videoae_65x512x512.sh +++ b/scripts/text_condition/train_videoae_65x512x512.sh @@ -1,25 +1,22 @@ export WANDB_KEY="" export ENTITY="" -export PROJECT="512_useimg" +export PROJECT="512_2node_bs2" accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v.py \ - --model LatteT2V-XL/122 \ + --model OpenSoraT2V-XL/222 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "../../Open-Sora-Plan/cache_dir" \ + --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "../../Open-Sora-Plan/CausalVAEModel_4x8x8/" \ - --video_data_path "../../Open-Sora-Plan/sharegpt4v_path_cap_64x512x512_mixkit.json" \ - --video_folder /remote-home1/dataset/data_split_tt \ - --image_data_path "../../../dataset/image_114054.json" \ - --image_folder "../../../dataset/picture" \ + --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ --sample_rate 1 \ --num_frames 65 \ --max_image_size 512 \ --gradient_checkpointing \ --attention_mode xformers \ - --train_batch_size=4 \ + --train_batch_size=2 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -27,12 +24,14 @@ accelerate launch \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="tensorboard" \ + --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="512_useimg" \ + --output_dir="512_2node_bs2" \ --allow_tf32 \ - --pretrained t2v.pt \ --use_deepspeed \ --model_max_length 300 \ --use_image_num 4 \ - --enable_tiling + --enable_tiling \ + --use_img_from_vid \ + --pretrained /dxyl_data02/PixArt-Sigma-XL-2-512-MS.safetensors \ + --enable_tracker diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt new file mode 100644 index 000000000..ef54628f5 --- /dev/null +++ b/scripts/train_data/image_data.txt @@ -0,0 +1 @@ +ttt \ No newline at end of file diff --git a/scripts/train_data/video_data.txt b/scripts/train_data/video_data.txt new file mode 100644 index 000000000..f95abc801 --- /dev/null +++ b/scripts/train_data/video_data.txt @@ -0,0 +1,2 @@ +/dxyl_data02/mixkit,/dxyl_data02/anno_jsons/tmp_video_mixkit_65f_54735.json +/dxyl_data02/mixkit,/dxyl_data02/anno_jsons/tmp_video_mixkit_65f_54735.json \ No newline at end of file From 680edcd76475783f224c1af74680cf7d25a0d845 Mon Sep 17 00:00:00 2001 From: yeyang <2267330597@qq.com> Date: Sun, 5 May 2024 06:13:11 +0000 Subject: [PATCH 004/134] fix dataset --- .deepspeed_env | 40 +++++++++++ opensora/dataset/t2v_datasets.py | 54 ++++++++------- .../diffusion/opensora/modeling_opensora.py | 17 ++--- opensora/sample/pipeline_opensora.py | 44 +++++-------- opensora/sample/sample_t2v_pixsig.py | 66 +++++++++++-------- opensora/train/train_t2v.py | 57 ++++++++++------ opensora/utils/dataset_utils.py | 59 ++++++++++++----- .../deepspeed_zero2_config.yaml | 2 +- .../multi_node_example.yaml | 2 +- scripts/text_condition/sample_image.sh | 6 +- .../text_condition/train_image_1x512x512.sh | 39 +++++++++++ ...5x512x512.sh => train_video_65x512x512.sh} | 22 +++---- scripts/train_data/image_data copy.txt | 4 ++ scripts/train_data/image_data.txt | 5 +- scripts/train_data/video_data.txt | 3 +- 15 files changed, 282 insertions(+), 138 deletions(-) create mode 100644 .deepspeed_env create mode 100644 scripts/text_condition/train_image_1x512x512.sh rename scripts/text_condition/{train_videoae_65x512x512.sh => train_video_65x512x512.sh} (60%) create mode 100644 scripts/train_data/image_data copy.txt diff --git a/.deepspeed_env b/.deepspeed_env new file mode 100644 index 000000000..93aee338e --- /dev/null +++ b/.deepspeed_env @@ -0,0 +1,40 @@ +USER=yeyang +XDG_SESSION_TYPE=tty +SHLVL=1 +LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64: +MOTD_SHOWN=pam +HOME=/home/yeyang +CONDA_SHLVL=4 +OLDPWD=/remote-home1/yeyang/dev3d/Open-Sora-Plan/scripts +SSH_TTY=/dev/pts/17 +DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1001/bus +https_proxy=127.0.0.1:8887 +LOGNAME=yeyang +http_proxy=127.0.0.1:8887 +_=/usr/bin/sh +XDG_SESSION_CLASS=user +WANDB_KEY=953e958793b218efb850fa194e85843e2c3bd88b +TERM=xterm-256color +XDG_SESSION_ID=4972 +PATH=/usr/local/cuda-11.8/bin:/remote-home1/yeyang/miniconda3/envs/dev3d/bin:/remote-home1/yeyang/miniconda3/condabin:/usr/bin/v2ray:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin +ENTITY=linbin +PROJECT=test_img +XDG_RUNTIME_DIR=/run/user/1001 +LANG=en_US.UTF-8 +CONDA_PREFIX_1=/remote-home1/yeyang/miniconda3 +CONDA_PREFIX_2=/remote-home1/yeyang/miniconda3/envs/dev3d +CONDA_PYTHON_EXE=/remote-home1/yeyang/miniconda3/bin/python +CONDA_PREFIX_3=/remote-home1/yeyang/miniconda3/envs/opensora +SHELL=/bin/bash +CONDA_DEFAULT_ENV=dev3d +PWD=/remote-home1/yeyang/dev3d/Open-Sora-Plan +CUDA_HOME=/usr/local/cuda-11.8 +CONDA_EXE=/remote-home1/yeyang/miniconda3/bin/conda +XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop +CONDA_PREFIX=/remote-home1/yeyang/miniconda3/envs/dev3d +CUDA_PATH=/usr/local/cuda-11.8 +PYTHONPATH=/remote-home1/yeyang/dev3d/Open-Sora-Plan +ACCELERATE_MIXED_PRECISION=no +ACCELERATE_CONFIG_DS_FIELDS=deepspeed_config_file,deepspeed_hostfile +ACCELERATE_USE_DEEPSPEED=true +ACCELERATE_DEEPSPEED_CONFIG_FILE=scripts/accelerate_configs/zero2.json diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 224439c83..62f1d6f6a 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -26,23 +26,29 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.image_data = args.image_data self.video_data = args.video_data self.num_frames = args.num_frames + self.use_image_num = args.use_image_num + self.use_img_from_vid = args.use_img_from_vid self.transform = transform self.temporal_sample = temporal_sample self.tokenizer = tokenizer self.model_max_length = args.model_max_length self.v_decoder = DecordInit() - self.vid_cap_list = self.get_vid_cap_list() - - self.use_image_num = args.use_image_num - self.use_img_from_vid = args.use_img_from_vid - if self.use_image_num != 0 and not self.use_img_from_vid: + if self.num_frames != 1: + self.vid_cap_list = self.get_vid_cap_list() + if self.use_image_num != 0 and not self.use_img_from_vid: + self.img_cap_list = self.get_img_cap_list() + else: self.img_cap_list = self.get_img_cap_list() + def __len__(self): - return len(self.vid_cap_list) - + if self.num_frames != 1: + return len(self.vid_cap_list) + else: + return len(self.img_cap_list) + def __getitem__(self, idx): # try: video_data, image_data = {}, {} @@ -54,7 +60,7 @@ def __getitem__(self, idx): else: image_data = self.get_image(idx) else: - video_data = self.get_image(idx) # 1 frame video as image + image_data = self.get_image(idx) # 1 frame video as image return dict(video_data=video_data, image_data=image_data) # except Exception as e: # print(f'Error with {e}, {self.vid_cap_list[idx]}') @@ -73,7 +79,7 @@ def get_video(self, idx): # video = torch.rand(65, 3, 512, 512) video = video.transpose(0, 1) # T C H W -> C T H W - text = self.vid_cap_list[idx]['cap'][0] + text = self.vid_cap_list[idx]['cap'] text = text_preprocessing(text) text_tokens_and_mask = self.tokenizer( @@ -126,19 +132,22 @@ def get_image(self, idx): cond_mask = torch.cat(cond_mask) # self.use_image_num, l return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) - # def tv_read(self, path): - # vframes, aframes, info = torchvision.io.read_video(filename=path, pts_unit='sec', output_format='TCHW') - # total_frames = len(vframes) - - # # Sampling video frames - # start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - # # assert end_frame_ind - start_frame_ind >= self.num_frames - # frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - - # video = vframes[frame_indice] # (T, C, H, W) + def tv_read(self, path, frame_idx=None): + vframes, aframes, info = torchvision.io.read_video(filename=path, pts_unit='sec', output_format='TCHW') + total_frames = len(vframes) + if frame_idx is None: + start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) + else: + start_frame_ind, end_frame_ind = frame_idx.split(':') + start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) + # assert end_frame_ind - start_frame_ind >= self.num_frames + frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) + # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) - # return video + video = vframes[frame_indice] # (T, C, H, W) + return video + def decord_read(self, path, frame_idx=None): decord_vr = self.v_decoder(path) total_frames = len(decord_vr) @@ -150,7 +159,7 @@ def decord_read(self, path, frame_idx=None): start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) # assert end_frame_ind - start_frame_ind >= self.num_frames frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) + # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) video_data = decord_vr.get_batch(frame_indice).asnumpy() video_data = torch.from_numpy(video_data) @@ -173,6 +182,7 @@ def get_vid_cap_list(self): return vid_cap_lists def get_img_cap_list(self): + use_image_num = self.use_image_num if self.use_image_num != 0 else 1 img_cap_lists = [] with open(self.image_data, 'r') as f: folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] @@ -183,5 +193,5 @@ def get_img_cap_list(self): for i in tqdm(range(len(img_cap_list))): img_cap_list[i]['path'] = opj(folder, img_cap_list[i]['path']) img_cap_lists += img_cap_list - img_cap_lists = [img_cap_lists[i: i+self.use_image_num] for i in range(0, len(img_cap_lists), self.use_image_num)] + img_cap_lists = [img_cap_lists[i: i+use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] return img_cap_lists[:-1] # drop last to avoid error length \ No newline at end of file diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index acc34c669..98e90d1ae 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -232,8 +232,8 @@ def _set_gradient_checkpointing(self, module, value=False): def forward( self, hidden_states: torch.Tensor, - encoder_hidden_states: Optional[torch.Tensor] = None, timestep: Optional[torch.LongTensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, added_cond_kwargs: Dict[str, torch.Tensor] = None, class_labels: Optional[torch.LongTensor] = None, cross_attention_kwargs: Dict[str, Any] = None, @@ -303,7 +303,7 @@ def forward( # (keep = +0, discard = -10000.0) # b, frame+use_image_num, h, w -> a video with images # b, 1, h, w -> only images - attention_mask = attention_mask.to(hidden_states.dtype) + attention_mask = attention_mask.to(self.dtype) attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w @@ -318,8 +318,8 @@ def forward( attention_mask_img = F.max_pool2d(attention_mask_img, kernel_size=(self.patch_size, self.patch_size), stride=(self.patch_size, self.patch_size)) attention_mask_img = rearrange(attention_mask_img, 'b i h w -> (b i) 1 (h w)') - attention_mask_vid = (1 - attention_mask_vid.bool().to(hidden_states.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None - attention_mask_img = (1 - attention_mask_img.bool().to(hidden_states.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None + attention_mask_vid = (1 - attention_mask_vid.bool().to(self.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None + attention_mask_img = (1 - attention_mask_img.bool().to(self.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None # import ipdb;ipdb.set_trace() if frame == 1 and use_image_num == 0: @@ -330,7 +330,7 @@ def forward( if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: # b, 1+use_image_num, l -> a video with images # b, 1, l -> only images - encoder_attention_mask = (1 - encoder_attention_mask.to(hidden_states.dtype)) * -10000.0 + encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 in_t = encoder_attention_mask.shape[1] encoder_attention_mask_vid = encoder_attention_mask[:, :in_t-use_image_num] # b, 1, l encoder_attention_mask_vid = rearrange(encoder_attention_mask_vid, 'b 1 l -> (b 1) 1 l') if encoder_attention_mask_vid.numel() > 0 else None @@ -453,7 +453,7 @@ def custom_forward(*inputs): def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): # batch_size = hidden_states.shape[0] - hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states, frame) + hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states.to(self.dtype), frame) timestep_vid, timestep_img = None, None embedded_timestep_vid, embedded_timestep_img = None, None encoder_hidden_states_vid, encoder_hidden_states_img = None, None @@ -464,7 +464,7 @@ def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, times "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." ) timestep, embedded_timestep = self.adaln_single( - timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=hidden_states.dtype + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype ) # b 6d, b d if hidden_states_vid is None: timestep_img = timestep @@ -496,7 +496,7 @@ def _get_output_for_patched_inputs( ): if self.config.norm_type != "ada_norm_single": conditioning = self.transformer_blocks[0].norm1.emb( - timestep, class_labels, hidden_dtype=hidden_states.dtype + timestep, class_labels, hidden_dtype=self.dtype ) shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] @@ -533,6 +533,7 @@ def OpenSoraT2V_S_222(**kwargs): norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) OpenSora_models = { + "OpenSoraT2V-S/122": OpenSoraT2V_S_122, "OpenSoraT2V-S/222": OpenSoraT2V_S_222, } diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index daea92ec6..95b8e51a9 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -21,7 +21,6 @@ import torch from transformers import T5EncoderModel, T5Tokenizer -from diffusers.image_processor import PixArtImageProcessor from diffusers.models import AutoencoderKL, Transformer2DModel from diffusers.schedulers import DPMSolverMultistepScheduler from diffusers.utils import ( @@ -34,7 +33,6 @@ ) from diffusers.utils.torch_utils import randn_tensor from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput -from diffusers.pipelines.pixart_alpha.pipeline_pixart_sigma import ASPECT_RATIO_256_BIN, ASPECT_RATIO_512_BIN, ASPECT_RATIO_1024_BIN, ASPECT_RATIO_2048_BIN logger = logging.get_logger(__name__) # pylint: disable=invalid-name @@ -149,7 +147,6 @@ def __init__( ) # self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) - self.image_processor = PixArtImageProcessor(vae_scale_factor=self.vae.vae_scale_factor[1:]) # Copied from diffusers.pipelines.pixart_alpha.pipeline_pixart_alpha.PixArtAlphaPipeline.encode_prompt def encode_prompt( @@ -199,7 +196,7 @@ def encode_prompt( deprecate("mask_feature", "1.0.0", deprecation_message, standard_warn=False) if device is None: - device = self._execution_device + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') if prompt is not None and isinstance(prompt, str): batch_size = 1 @@ -653,21 +650,8 @@ def __call__( returned where the first element is a list with the generated images """ # 1. Check inputs. Raise error if not correct - height = height or self.transformer.config.sample_size[0] * self.vae_scale_factor[1] - width = width or self.transformer.config.sample_size[1] * self.vae_scale_factor[2] - if use_resolution_binning: - # if self.transformer.config.sample_size == 256: - # aspect_ratio_bin = ASPECT_RATIO_2048_BIN - # elif self.transformer.config.sample_size == 128: - # aspect_ratio_bin = ASPECT_RATIO_1024_BIN - # elif self.transformer.config.sample_size == 64: - aspect_ratio_bin = ASPECT_RATIO_512_BIN - # elif self.transformer.config.sample_size == 32: - # aspect_ratio_bin = ASPECT_RATIO_256_BIN - # else: - # raise ValueError("Invalid sample size") - orig_height, orig_width = height, width - height, width = self.image_processor.classify_height_width_bin(height, width, ratios=aspect_ratio_bin) + height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] + width = width or self.transformer.config.sample_size[1] * self.vae.vae_scale_factor[2] self.check_inputs( prompt, @@ -689,8 +673,8 @@ def __call__( batch_size = len(prompt) else: batch_size = prompt_embeds.shape[0] - - device = self._execution_device + # import ipdb;ipdb.set_trace() + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` @@ -802,17 +786,12 @@ def __call__( step_idx = i // getattr(self.scheduler, "order", 1) callback(step_idx, t, latents) # import ipdb;ipdb.set_trace() - latents = latents.squeeze(2) + # latents = latents.squeeze(2) if not output_type == "latent": - image = self.vae.decode(latents / self.vae.config.scaling_factor, return_dict=False)[0] - if use_resolution_binning: - image = self.image_processor.resize_and_crop_tensor(image, orig_width, orig_height) + image = self.decode_latents(latents) else: image = latents - if not output_type == "latent": - image = self.image_processor.postprocess(image, output_type=output_type) - # Offload all models self.maybe_free_model_hooks() @@ -820,3 +799,12 @@ def __call__( return (image,) return ImagePipelineOutput(images=image) + + + def decode_latents(self, latents): + video = self.vae.decode(latents) + # video = self.vae.decode(latents / 0.18215) + # video = rearrange(video, 'b c t h w -> b t c h w').contiguous() + video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() + # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 + return video diff --git a/opensora/sample/sample_t2v_pixsig.py b/opensora/sample/sample_t2v_pixsig.py index d328d2282..a09e1bc5d 100644 --- a/opensora/sample/sample_t2v_pixsig.py +++ b/opensora/sample/sample_t2v_pixsig.py @@ -31,13 +31,18 @@ def main(args): # torch.manual_seed(args.seed) weight_dtype = torch.float16 - device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + device = torch.device(args.device) # transformer_model = Transformer2DModel.from_pretrained( # "PixArt-alpha/PixArt-Sigma-XL-2-512-MS", # subfolder='transformer', cache_dir=args.cache_dir, # torch_dtype=weight_dtype, # use_safetensors=True, low_cpu_mem_usage=True # ) + vae = getae_wrapper(args.ae)(args.ae_path).to(dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] latent_size = (64, 64) latent_size_t = 1 transformer_model = OpenSoraT2V_S_122(in_channels=4, @@ -56,18 +61,18 @@ def main(args): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False).to(dtype=weight_dtype) - print(2) - path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--PixArt-alpha--PixArt-Sigma-XL-2-512-MS/snapshots/786c445c97ddcc0eb2faa157b131ac71ee1935a2/transformer/diffusion_pytorch_model.safetensors" + # print(2) + path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/test_img/checkpoint-16000/model/diffusion_pytorch_model.safetensors" from safetensors.torch import load_file as safe_load ckpt = safe_load(path, device="cpu") transformer_model.load_state_dict(ckpt) print(args.text_encoder_name) text_encoder = T5EncoderModel.from_pretrained('/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', low_cpu_mem_usage=True, torch_dtype=weight_dtype) tokenizer = T5Tokenizer.from_pretrained('/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir) - print(3) - vae = AutoencoderKL.from_pretrained("/remote-home1/yeyang/dev3d/Open-Sora-Plan/vae", torch_dtype=torch.float16) - vae.vae_scale_factor = (4, 8, 8) - print(1) + # print(3) + # vae = AutoencoderKL.from_pretrained("/remote-home1/yeyang/dev3d/Open-Sora-Plan/vae", torch_dtype=torch.float16) + + # print(1) # set eval mode transformer_model.eval() vae.eval() @@ -100,27 +105,31 @@ def main(args): scheduler=scheduler, transformer=transformer_model) pipeline.to(device) - prompt = "A small cactus with a happy face in the Sahara desert." - image = pipeline(prompt, - num_frames=1, - height=512, - width=512, - num_inference_steps=20, - guidance_scale=args.guidance_scale, - num_images_per_prompt=1, - mask_feature=True, - ).images[0] - image.save("./catcus.png") - image = pipeline(prompt, - num_frames=1, - height=512, - width=512, - num_inference_steps=50, - guidance_scale=args.guidance_scale, - num_images_per_prompt=1, - mask_feature=True, - ).images[0] - image.save("./catcus50.png") + + + if not os.path.exists(args.save_img_path): + os.makedirs(args.save_img_path) + prompts = [ + "A small cactus with a happy face in the Sahara desert.", + "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", + "The majestic beauty of a waterfall cascading down a cliff into a serene lake.", + ] + for prompt in prompts: + videos = pipeline(prompt, + num_frames=1, + height=512, + width=512, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device + ).images + ext = 'jpg' + videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, + prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=1, normalize=True, value_range=(0, 1)) # t c h w if __name__ == "__main__": @@ -128,6 +137,7 @@ def main(args): parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) parser.add_argument("--image_size", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 2e470cc36..0d6f551fb 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -58,6 +58,7 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step): validation_prompt = [ + "A small cactus with a happy face in the Sahara desert.", "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", "The majestic beauty of a waterfall cascading down a cliff into a serene lake." ] @@ -65,7 +66,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh model = accelerator.unwrap_model(model) # scheduler = PNDMScheduler() scheduler = DPMSolverMultistepScheduler() - videogen_pipeline = OpenSoraPipeline(vae=vae, + opensora_pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, scheduler=scheduler, @@ -73,16 +74,16 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh videos = [] for prompt in validation_prompt: logger.info('Processing the ({}) prompt'.format(prompt)) - video = videogen_pipeline(prompt, - video_length=args.num_frames, + video = opensora_pipeline(prompt, + num_frames=args.num_frames, height=args.max_image_size, width=args.max_image_size, num_inference_steps=args.num_sampling_steps, guidance_scale=args.guidance_scale, - # enable_temporal_attentions=True, + enable_temporal_attentions=True, num_images_per_prompt=1, mask_feature=True, - ).video + ).images videos.append(video[0]) # import ipdb;ipdb.set_trace() gc.collect() @@ -91,20 +92,38 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh videos = rearrange(videos, 'b t h w c -> b t c h w') for tracker in accelerator.trackers: if tracker.name == "tensorboard": - np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video("validation", np_videos, global_step, fps=10) + if videos.shape[1] == 1: + assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + np_images = np.stack([np.asarray(img) for img in images]) + tracker.writer.add_images("validation", np_images, global_step, dataformats="NHWC") + else: + np_videos = np.stack([np.asarray(vid) for vid in videos]) + tracker.writer.add_video("validation", np_videos, global_step, fps=10) if tracker.name == "wandb": import wandb - tracker.log( - { - "validation": [ - wandb.Video(video, caption=f"{i}: {prompt}", fps=10) - for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) - ] - } - ) - - del videogen_pipeline + if videos.shape[1] == 1: + assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + tracker.log( + { + "validation": [ + wandb.Image(image, caption=f"{i}: {prompt}") + for i, (image, prompt) in enumerate(zip(images, validation_prompt)) + ] + } + ) + else: + tracker.log( + { + "validation": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=10) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ] + } + ) + + del opensora_pipeline gc.collect() torch.cuda.empty_cache() ################################################################################# @@ -230,7 +249,7 @@ def main(args): if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: repeat = model.pos_embed.proj.weight.shape[2] checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) - del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] else: # latest stage training weight checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] model_state_dict = model.state_dict() @@ -583,7 +602,7 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=10.0) + parser.add_argument('--guidance_scale', type=float, default=4.5) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--use_deepspeed", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 8ab656446..348b7249b 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -61,11 +61,15 @@ def __init__(self, args): self.max_1hw = (1, self.max_image_size, self.max_image_size) def package(self, batch): - batch_tubes_vid = [i['video_data']['video'] for i in batch] # b [c t h w] - input_ids_vid = torch.stack([i['video_data']['input_ids'] for i in batch]) # b 1 l - cond_mask_vid = torch.stack([i['video_data']['cond_mask'] for i in batch]) # b 1 l + + batch_tubes_vid, input_ids_vid, cond_mask_vid = None, None, None batch_tubes_img, input_ids_img, cond_mask_img = None, None, None - if self.use_image_num != 0: + # import ipdb;ipdb.set_trace() + if self.num_frames > 1: + batch_tubes_vid = [i['video_data']['video'] for i in batch] # b [c t h w] + input_ids_vid = torch.stack([i['video_data']['input_ids'] for i in batch]) # b 1 l + cond_mask_vid = torch.stack([i['video_data']['cond_mask'] for i in batch]) # b 1 l + if self.num_frames == 1 or self.use_image_num != 0: batch_tubes_img = [j for i in batch for j in i['image_data']['image']] # b*num_img [c 1 h w] input_ids_img = torch.stack([i['image_data']['input_ids'] for i in batch]) # b image_num l cond_mask_img = torch.stack([i['image_data']['cond_mask'] for i in batch]) # b image_num l @@ -76,15 +80,17 @@ def __call__(self, batch): ds_stride = self.ae_stride * self.patch_size t_ds_stride = self.ae_stride_t * self.patch_size_t - if self.use_image_num == 0: + if self.num_frames > 1 and self.use_image_num == 0: pad_batch_tubes, attention_mask = self.process(batch_tubes_vid, t_ds_stride, ds_stride, self.max_thw, self.ae_stride_thw, self.patch_size_thw, extra_1=True) # attention_mask: b t h w - input_ids, cond_mask = input_ids_vid.squeeze(1), cond_mask_vid.squeeze(1) # b 1 l -> b l - else: + # input_ids, cond_mask = input_ids_vid.squeeze(1), cond_mask_vid.squeeze(1) # b 1 l -> b l + input_ids, cond_mask = input_ids_vid, cond_mask_vid # b 1 l + elif self.num_frames > 1 and self.use_image_num != 0: pad_batch_tubes_vid, attention_mask_vid = self.process(batch_tubes_vid, t_ds_stride, ds_stride, self.max_thw, self.ae_stride_thw, self.patch_size_thw, extra_1=True) # attention_mask_vid: b t h w + pad_batch_tubes_img, attention_mask_img = self.process(batch_tubes_img, 1, ds_stride, self.max_1hw, self.ae_stride_1hw, self.patch_size_1hw, extra_1=False) pad_batch_tubes_img = rearrange(pad_batch_tubes_img, '(b i) c 1 h w -> b c i h w', i=self.use_image_num) @@ -94,6 +100,13 @@ def __call__(self, batch): attention_mask = torch.cat([attention_mask_vid, attention_mask_img], dim=1) # b t+num_img h w input_ids = torch.cat([input_ids_vid, input_ids_img], dim=1) # b 1+num_img hw cond_mask = torch.cat([cond_mask_vid, cond_mask_img], dim=1) # b 1+num_img hw + else: + # import ipdb;ipdb.set_trace() + pad_batch_tubes_img, attention_mask_img = self.process(batch_tubes_img, 1, ds_stride, + self.max_1hw, self.ae_stride_1hw, self.patch_size_1hw, extra_1=False) + pad_batch_tubes = rearrange(pad_batch_tubes_img, '(b i) c 1 h w -> b c i h w', i=1) + attention_mask = rearrange(attention_mask_img, '(b i) 1 h w -> b i h w', i=1) + input_ids, cond_mask = input_ids_img, cond_mask_img # b 1 l return pad_batch_tubes, attention_mask, input_ids, cond_mask def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, patch_size_thw, extra_1): @@ -114,14 +127,30 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p pad_batch_tubes = torch.stack(pad_batch_tubes, dim=0) # make attention_mask - first_channel_first_frame, first_channel_other_frame = pad_batch_tubes[:, :1, :1], pad_batch_tubes[:, :1, 1:] # first channel to make attention_mask - attention_mask_first_frame = F.max_pool3d(first_channel_first_frame, kernel_size=(1, *ae_stride_thw[1:]), stride=(1, *ae_stride_thw[1:])) - if first_channel_other_frame.numel() != 0: - attention_mask_other_frame = F.max_pool3d(first_channel_other_frame, kernel_size=ae_stride_thw, stride=ae_stride_thw) - attention_mask = torch.cat([attention_mask_first_frame, attention_mask_other_frame], dim=2) - else: - attention_mask = attention_mask_first_frame - attention_mask = attention_mask[:, 0].bool().float() # b t h w, do not channel + # first_channel_first_frame, first_channel_other_frame = pad_batch_tubes[:, :1, :1], pad_batch_tubes[:, :1, 1:] # first channel to make attention_mask + # attention_mask_first_frame = F.max_pool3d(first_channel_first_frame, kernel_size=(1, *ae_stride_thw[1:]), stride=(1, *ae_stride_thw[1:])) + # if first_channel_other_frame.numel() != 0: + # attention_mask_other_frame = F.max_pool3d(first_channel_other_frame, kernel_size=ae_stride_thw, stride=ae_stride_thw) + # attention_mask = torch.cat([attention_mask_first_frame, attention_mask_other_frame], dim=2) + # else: + # attention_mask = attention_mask_first_frame + # attention_mask = attention_mask[:, 0].bool().float() # b t h w, do not channel + + max_tube_size = [pad_max_t, pad_max_h, pad_max_w] + max_latent_size = [((max_tube_size[0]-1) // ae_stride_thw[0] + 1) if extra_1 else (max_tube_size[0] // ae_stride_thw[0]), + max_tube_size[1] // ae_stride_thw[1], + max_tube_size[2] // ae_stride_thw[2]] + valid_latent_size = [[int(math.ceil((i[1]-1) / ae_stride_thw[0])) + 1 if extra_1 else int(math.ceil(i[1] / ae_stride_thw[0])), + int(math.ceil(i[2] / ae_stride_thw[1])), + int(math.ceil(i[3] / ae_stride_thw[2]))] for i in batch_input_size] + attention_mask = [F.pad(torch.ones(i), + (0, max_latent_size[2] - i[2], + 0, max_latent_size[1] - i[1], + 0, max_latent_size[0] - i[0]), value=0) for i in valid_latent_size] + attention_mask = torch.stack(attention_mask) # b t h w + + + # max_tube_size = [pad_max_t, pad_max_h, pad_max_w] # max_latent_size = [((max_tube_size[0]-1) // ae_stride_thw[0] + 1) if extra_1 else (max_tube_size[0] // ae_stride_thw[0]), # max_tube_size[1] // ae_stride_thw[1], diff --git a/scripts/accelerate_configs/deepspeed_zero2_config.yaml b/scripts/accelerate_configs/deepspeed_zero2_config.yaml index 43ec1e80c..2b8cfd2ad 100644 --- a/scripts/accelerate_configs/deepspeed_zero2_config.yaml +++ b/scripts/accelerate_configs/deepspeed_zero2_config.yaml @@ -10,4 +10,4 @@ main_training_function: main num_machines: 1 num_processes: 8 gpu_ids: 0,1,2,3,4,5,6,7 -use_cpu: false \ No newline at end of file +use_cpu: false diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index 4e3cac79c..c384324ba 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -2,7 +2,7 @@ compute_environment: LOCAL_MACHINE distributed_type: DEEPSPEED deepspeed_config: deepspeed_config_file: scripts/accelerate_configs/zero2.json - deepspeed_hostfile: /remote-home1/yeyang/Open-Sora-Plan/scripts/accelerate_configs/hostfile + deepspeed_hostfile: scripts/accelerate_configs/hostfile fsdp_config: {} machine_rank: 0 main_process_ip: 10.10.10.55 diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/sample_image.sh index 8d929997e..4aa2eae9f 100644 --- a/scripts/text_condition/sample_image.sh +++ b/scripts/text_condition/sample_image.sh @@ -3,10 +3,12 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v_pixsig.py \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ + --ae_path /remote-home1/yeyang/CausalVAEModel_4x8x8 \ --version 65x512x512 \ --save_img_path "./sample_images/prompt_list_0" \ --fps 24 \ - --guidance_scale 7.5 \ - --num_sampling_steps 250 \ + --guidance_scale 4.5 \ + --sample_method "DPMSolverMultistep" \ + --num_sampling_steps 50 \ --enable_tiling \ --force_images diff --git a/scripts/text_condition/train_image_1x512x512.sh b/scripts/text_condition/train_image_1x512x512.sh new file mode 100644 index 000000000..0d4f98b21 --- /dev/null +++ b/scripts/text_condition/train_image_1x512x512.sh @@ -0,0 +1,39 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export ENTITY="linbin" +export PROJECT="test_img" +#export HF_ENDPOINT="https://hf-mirror.com" +#export HF_DATASETS_OFFLINE=1 +#export TRANSFORMERS_OFFLINE=1 +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-S/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_image_size 512 \ + --gradient_checkpointing \ + --attention_mode xformers \ + --train_batch_size=64 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-05 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=1000 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="test_img" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 300 \ + --use_image_num 0 \ + --enable_tiling \ + --pretrained /remote-home1/yeyang/PixArt-Sigma-XL-2-512-MS.safetensors \ + --enable_tracker diff --git a/scripts/text_condition/train_videoae_65x512x512.sh b/scripts/text_condition/train_video_65x512x512.sh similarity index 60% rename from scripts/text_condition/train_videoae_65x512x512.sh rename to scripts/text_condition/train_video_65x512x512.sh index 3d4f67123..8e3076cdc 100644 --- a/scripts/text_condition/train_videoae_65x512x512.sh +++ b/scripts/text_condition/train_video_65x512x512.sh @@ -1,23 +1,24 @@ -export WANDB_KEY="" -export ENTITY="" -export PROJECT="512_2node_bs2" +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export ENTITY="linbin" +export PROJECT="test_img" accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/debug.yaml \ opensora/train/train_t2v.py \ - --model OpenSoraT2V-XL/222 \ + --model OpenSoraT2V-S/222 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ + --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ --num_frames 65 \ --max_image_size 512 \ --gradient_checkpointing \ --attention_mode xformers \ --train_batch_size=2 \ - --dataloader_num_workers 10 \ + --dataloader_num_workers 0 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=2e-05 \ @@ -25,13 +26,12 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="512_2node_bs2" \ + --checkpointing_steps=5 \ + --output_dir="test_img" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 300 \ --use_image_num 4 \ --enable_tiling \ - --use_img_from_vid \ - --pretrained /dxyl_data02/PixArt-Sigma-XL-2-512-MS.safetensors \ + --pretrained /remote-home1/yeyang/PixArt-Sigma-XL-2-512-MS.safetensors \ --enable_tracker diff --git a/scripts/train_data/image_data copy.txt b/scripts/train_data/image_data copy.txt new file mode 100644 index 000000000..f980a9204 --- /dev/null +++ b/scripts/train_data/image_data copy.txt @@ -0,0 +1,4 @@ +/remote-home1/dataset/human_images,/remote-home1/dataset/anno_jsons/human_images_162094.json +/remote-home1/dataset/sam,/remote-home1/dataset/anno_jsons/sam_image_11185255.json +/remote-home1/dataset/anytext3m,/remote-home1/dataset/anno_jsons/anytext_en_1886137.json +/remote-home1/dataset/tuzhan_mj,/remote-home1/dataset/anno_jsons/tuzhan_mj_1712571.json \ No newline at end of file diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index ef54628f5..fafc1d803 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1 +1,4 @@ -ttt \ No newline at end of file +/remote-home1/dataset/human_images,/remote-home1/dataset/anno_jsons/human_images_162094.json +/remote-home1/dataset/tuzhan_mj,/remote-home1/dataset/anno_jsons/tuzhan_mj_1712571.json +/remote-home1/dataset/anytext3m,/remote-home1/dataset/anno_jsons/anytext_en_1886137.json +/remote-home1/dataset/sam,/remote-home1/dataset/anno_jsons/sam_image_11185255.json \ No newline at end of file diff --git a/scripts/train_data/video_data.txt b/scripts/train_data/video_data.txt index f95abc801..6223c6714 100644 --- a/scripts/train_data/video_data.txt +++ b/scripts/train_data/video_data.txt @@ -1,2 +1 @@ -/dxyl_data02/mixkit,/dxyl_data02/anno_jsons/tmp_video_mixkit_65f_54735.json -/dxyl_data02/mixkit,/dxyl_data02/anno_jsons/tmp_video_mixkit_65f_54735.json \ No newline at end of file +/remote-home1/dataset/all_mixkit,/remote-home1/dataset/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file From e74624200a6755e6c3020c0fdff15058ebd0f372 Mon Sep 17 00:00:00 2001 From: yeyang <2267330597@qq.com> Date: Sun, 5 May 2024 08:03:10 +0000 Subject: [PATCH 005/134] inference any res. --- .deepspeed_env | 40 ----------- examples/prompt_list_0.txt | 1 + opensora/dataset/t2v_datasets.py | 30 ++++---- opensora/sample/pipeline_opensora.py | 23 +++--- opensora/sample/sample_t2v_pixsig.py | 72 ++++++------------- opensora/train/train_t2v.py | 13 +++- scripts/accelerate_configs/hostfile | 2 + .../multi_node_example.yaml | 4 +- scripts/text_condition/sample_image.sh | 8 ++- .../text_condition/train_image_1x512x512.sh | 2 +- 10 files changed, 76 insertions(+), 119 deletions(-) delete mode 100644 .deepspeed_env diff --git a/.deepspeed_env b/.deepspeed_env deleted file mode 100644 index 93aee338e..000000000 --- a/.deepspeed_env +++ /dev/null @@ -1,40 +0,0 @@ -USER=yeyang -XDG_SESSION_TYPE=tty -SHLVL=1 -LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64: -MOTD_SHOWN=pam -HOME=/home/yeyang -CONDA_SHLVL=4 -OLDPWD=/remote-home1/yeyang/dev3d/Open-Sora-Plan/scripts -SSH_TTY=/dev/pts/17 -DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1001/bus -https_proxy=127.0.0.1:8887 -LOGNAME=yeyang -http_proxy=127.0.0.1:8887 -_=/usr/bin/sh -XDG_SESSION_CLASS=user -WANDB_KEY=953e958793b218efb850fa194e85843e2c3bd88b -TERM=xterm-256color -XDG_SESSION_ID=4972 -PATH=/usr/local/cuda-11.8/bin:/remote-home1/yeyang/miniconda3/envs/dev3d/bin:/remote-home1/yeyang/miniconda3/condabin:/usr/bin/v2ray:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin -ENTITY=linbin -PROJECT=test_img -XDG_RUNTIME_DIR=/run/user/1001 -LANG=en_US.UTF-8 -CONDA_PREFIX_1=/remote-home1/yeyang/miniconda3 -CONDA_PREFIX_2=/remote-home1/yeyang/miniconda3/envs/dev3d -CONDA_PYTHON_EXE=/remote-home1/yeyang/miniconda3/bin/python -CONDA_PREFIX_3=/remote-home1/yeyang/miniconda3/envs/opensora -SHELL=/bin/bash -CONDA_DEFAULT_ENV=dev3d -PWD=/remote-home1/yeyang/dev3d/Open-Sora-Plan -CUDA_HOME=/usr/local/cuda-11.8 -CONDA_EXE=/remote-home1/yeyang/miniconda3/bin/conda -XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop -CONDA_PREFIX=/remote-home1/yeyang/miniconda3/envs/dev3d -CUDA_PATH=/usr/local/cuda-11.8 -PYTHONPATH=/remote-home1/yeyang/dev3d/Open-Sora-Plan -ACCELERATE_MIXED_PRECISION=no -ACCELERATE_CONFIG_DS_FIELDS=deepspeed_config_file,deepspeed_hostfile -ACCELERATE_USE_DEEPSPEED=true -ACCELERATE_DEEPSPEED_CONFIG_FILE=scripts/accelerate_configs/zero2.json diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index 6c91d3c3e..6c8ad8382 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -1,3 +1,4 @@ +A small cactus with a happy face in the Sahara desert. A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues. A quiet beach at dawn, the waves softly lapping at the shore, pink and orange hues painting the sky, offering a moment of solitude and reflection. The majestic beauty of a waterfall cascading down a cliff into a serene lake. diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 62f1d6f6a..a778a802c 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -50,21 +50,21 @@ def __len__(self): return len(self.img_cap_list) def __getitem__(self, idx): - # try: - video_data, image_data = {}, {} - if self.num_frames != 1: - video_data = self.get_video(idx) - if self.use_image_num != 0: - if self.use_img_from_vid: - image_data = self.get_image_from_video(video_data) - else: - image_data = self.get_image(idx) - else: - image_data = self.get_image(idx) # 1 frame video as image - return dict(video_data=video_data, image_data=image_data) - # except Exception as e: - # print(f'Error with {e}, {self.vid_cap_list[idx]}') - # return self.__getitem__(random.randint(0, self.__len__() - 1)) + try: + video_data, image_data = {}, {} + if self.num_frames != 1: + video_data = self.get_video(idx) + if self.use_image_num != 0: + if self.use_img_from_vid: + image_data = self.get_image_from_video(video_data) + else: + image_data = self.get_image(idx) + else: + image_data = self.get_image(idx) # 1 frame video as image + return dict(video_data=video_data, image_data=image_data) + except Exception as e: + print(f'Error with {e}, {self.vid_cap_list[idx]}') + return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): # video = random.choice([random_video_noise(65, 3, 720, 360) * 255, random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 720)]) diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 95b8e51a9..36927cfa2 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -17,7 +17,7 @@ import re import urllib.parse as ul from typing import Callable, List, Optional, Tuple, Union - +import math import torch from transformers import T5EncoderModel, T5Tokenizer @@ -519,15 +519,15 @@ def _clean_caption(self, caption): caption = re.sub(r"^\.\S+$", "", caption) return caption.strip() - + # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents def prepare_latents(self, batch_size, num_channels_latents, num_frames, height, width, dtype, device, generator, latents=None): shape = ( batch_size, num_channels_latents, - ((int(num_frames) - 1) // self.vae.vae_scale_factor[0] + 1) if int(num_frames) % 2 == 1 else int(num_frames) // self.vae.vae_scale_factor[0], - int(height) // self.vae.vae_scale_factor[1], - int(width) // self.vae.vae_scale_factor[2], + (math.ceil((int(num_frames) - 1) / self.vae.vae_scale_factor[0]) + 1) if int(num_frames) % 2 == 1 else math.ceil(int(num_frames) / self.vae.vae_scale_factor[0]), + math.ceil(int(height) / self.vae.vae_scale_factor[1]), + math.ceil(int(width) / self.vae.vae_scale_factor[2]), ) if isinstance(generator, list) and len(generator) != batch_size: raise ValueError( @@ -542,6 +542,8 @@ def prepare_latents(self, batch_size, num_channels_latents, num_frames, height, # scale the initial noise by the standard deviation required by the scheduler latents = latents * self.scheduler.init_noise_sigma + + return latents @torch.no_grad() @@ -650,6 +652,7 @@ def __call__( returned where the first element is a list with the generated images """ # 1. Check inputs. Raise error if not correct + num_frames = num_frames or self.transformer.config.sample_size_t * self.vae.vae_scale_factor[0] height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] width = width or self.transformer.config.sample_size[1] * self.vae.vae_scale_factor[2] @@ -755,9 +758,13 @@ def __call__( prompt_embeds = prompt_embeds.unsqueeze(1) # b l d -> b 1 l d if prompt_attention_mask.ndim == 2: prompt_attention_mask = prompt_attention_mask.unsqueeze(1) # b l -> b 1 l + # prepare attention_mask. + # b c t h w -> b t h w + attention_mask = torch.ones_like(latent_model_input)[:, 0] # predict noise model_output noise_pred = self.transformer( latent_model_input, + attention_mask=attention_mask, encoder_hidden_states=prompt_embeds, encoder_attention_mask=prompt_attention_mask, timestep=current_timestep, @@ -788,7 +795,9 @@ def __call__( # import ipdb;ipdb.set_trace() # latents = latents.squeeze(2) if not output_type == "latent": + # b t h w c image = self.decode_latents(latents) + image = image[:, :num_frames, :height, :width] else: image = latents @@ -803,8 +812,6 @@ def __call__( def decode_latents(self, latents): video = self.vae.decode(latents) - # video = self.vae.decode(latents / 0.18215) - # video = rearrange(video, 'b c t h w -> b t c h w').contiguous() - video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() + video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 return video diff --git a/opensora/sample/sample_t2v_pixsig.py b/opensora/sample/sample_t2v_pixsig.py index a09e1bc5d..f8bf2487b 100644 --- a/opensora/sample/sample_t2v_pixsig.py +++ b/opensora/sample/sample_t2v_pixsig.py @@ -18,12 +18,11 @@ from opensora.models.ae import ae_stride_config, getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V_S_122 +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V from opensora.models.text_encoder import get_text_enc from opensora.utils.utils import save_video_grid -sys.path.append(os.path.split(sys.path[0])[0]) -from pipeline_opensora import OpenSoraPipeline +from opensora.sample.pipeline_opensora import OpenSoraPipeline import imageio @@ -32,47 +31,19 @@ def main(args): # torch.manual_seed(args.seed) weight_dtype = torch.float16 device = torch.device(args.device) - # transformer_model = Transformer2DModel.from_pretrained( - # "PixArt-alpha/PixArt-Sigma-XL-2-512-MS", - # subfolder='transformer', cache_dir=args.cache_dir, - # torch_dtype=weight_dtype, - # use_safetensors=True, low_cpu_mem_usage=True - # ) + vae = getae_wrapper(args.ae)(args.ae_path).to(dtype=weight_dtype) if args.enable_tiling: vae.vae.enable_tiling() vae.vae.tile_overlap_factor = args.tile_overlap_factor vae.vae_scale_factor = ae_stride_config[args.ae] - latent_size = (64, 64) - latent_size_t = 1 - transformer_model = OpenSoraT2V_S_122(in_channels=4, - out_channels=8, - sample_size=latent_size, - sample_size_t=latent_size_t, - activation_fn="gelu-approximate", - attention_bias=True, - attention_type="default", - double_self_attention=False, - norm_elementwise_affine=False, - norm_eps=1e-06, - norm_num_groups=32, - num_vector_embeds=None, - only_cross_attention=False, - upcast_attention=False, - use_linear_projection=False, - use_additional_conditions=False).to(dtype=weight_dtype) - # print(2) - path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/test_img/checkpoint-16000/model/diffusion_pytorch_model.safetensors" - from safetensors.torch import load_file as safe_load - ckpt = safe_load(path, device="cpu") - transformer_model.load_state_dict(ckpt) - print(args.text_encoder_name) - text_encoder = T5EncoderModel.from_pretrained('/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', low_cpu_mem_usage=True, torch_dtype=weight_dtype) - tokenizer = T5Tokenizer.from_pretrained('/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir) - # print(3) - # vae = AutoencoderKL.from_pretrained("/remote-home1/yeyang/dev3d/Open-Sora-Plan/vae", torch_dtype=torch.float16) + + transformer_model = OpenSoraT2V.from_pretrained(args.model_path, low_cpu_mem_usage=True, device_map=None, torch_dtype=weight_dtype) + # print(transformer_model) + + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - # print(1) # set eval mode transformer_model.eval() vae.eval() @@ -109,16 +80,18 @@ def main(args): if not os.path.exists(args.save_img_path): os.makedirs(args.save_img_path) - prompts = [ - "A small cactus with a happy face in the Sahara desert.", - "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", - "The majestic beauty of a waterfall cascading down a cliff into a serene lake.", - ] - for prompt in prompts: + + if not isinstance(args.text_prompt, list): + args.text_prompt = [args.text_prompt] + if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): + text_prompt = open(args.text_prompt[0], 'r').readlines() + text_prompt = [i.strip() for i in text_prompt] + + for prompt in text_prompt: videos = pipeline(prompt, - num_frames=1, - height=512, - width=512, + num_frames=args.num_frames, + height=args.height, + width=args.width, num_inference_steps=args.num_sampling_steps, guidance_scale=args.guidance_scale, num_images_per_prompt=1, @@ -136,7 +109,9 @@ def main(args): parser = argparse.ArgumentParser() parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) - parser.add_argument("--image_size", type=int, default=512) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) parser.add_argument("--device", type=str, default='cuda:0') parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') @@ -149,7 +124,6 @@ def main(args): parser.add_argument("--fps", type=int, default=24) parser.add_argument("--run_time", type=int, default=0) parser.add_argument("--text_prompt", nargs='+') - parser.add_argument('--force_images', action='store_true') parser.add_argument('--tile_overlap_factor', type=float, default=0.25) parser.add_argument('--enable_tiling', action='store_true') args = parser.parse_args() diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 0d6f551fb..4027d7d8d 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -60,7 +60,18 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh validation_prompt = [ "A small cactus with a happy face in the Sahara desert.", "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", - "The majestic beauty of a waterfall cascading down a cliff into a serene lake." + "The majestic beauty of a waterfall cascading down a cliff into a serene lake.", + "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", + "Lego model, future rocket station, intricate details, high resolution, unreal engine, UHD", + "A litter of golden retriever puppies playing in the snow. Their heads pop out of the snow, covered in.", + "Close-up photos of models, hazy light and shadow, laser metal hair accessories, soft and beautiful, light gold pupils, white eyelashes, low saturation, real skin details, clear pores and fine lines, light reflection and refraction, ultra-clear, cinematography, award-winning works.", + "Full body shot, a French woman, Photography, French Streets background, backlighting, rim light, Fujifilm.", + "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", + "Modern luxury contemporary luxury home interiors house, in the style of mimicking ruined materials, ray tracing, haunting houses, and stone, capture the essence of nature, gray and bronze, dynamic outdoor shots.", + "One giant, sharp, metal square mirror in the center of the frame, four young people on the foreground, background sunny palm oil planation, tropical, realistic style, photography, nostalgic, green tone, mysterious, dreamy, bright color.", + "A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures.", + "Eiffel Tower was Made up of more than 2 million translucent straws to look like a cloud, with the bell tower at the top of the building, Michel installed huge foam-making machines in the forest to blow huge amounts of unpredictable wet clouds in the building's classic architecture.", + "A curvy timber house near a sea, designed by Zaha Hadid, represent the image of a cold, modern architecture, at night, white lighting, highly detailed.", ] logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index a22693099..f38fe5999 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,2 +1,4 @@ gpu55 slots=8 # your server name and GPU in total gpu117 slots=8 +gpu147 slots=8 +gpu176 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index c384324ba..6162b87ea 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -8,8 +8,8 @@ machine_rank: 0 main_process_ip: 10.10.10.55 main_process_port: 29501 main_training_function: main -num_machines: 2 -num_processes: 16 +num_machines: 4 +num_processes: 32 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/sample_image.sh index 4aa2eae9f..ce5b166c5 100644 --- a/scripts/text_condition/sample_image.sh +++ b/scripts/text_condition/sample_image.sh @@ -1,5 +1,5 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v_pixsig.py \ - --model_path LanguageBind/Open-Sora-Plan-v1.0.0 \ + --model_path test_img/checkpoint-6000/model \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ @@ -7,8 +7,10 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v_pixsig.py \ --version 65x512x512 \ --save_img_path "./sample_images/prompt_list_0" \ --fps 24 \ + --num_frames 1 \ + --height 256 \ + --width 512 \ --guidance_scale 4.5 \ --sample_method "DPMSolverMultistep" \ --num_sampling_steps 50 \ - --enable_tiling \ - --force_images + --enable_tiling diff --git a/scripts/text_condition/train_image_1x512x512.sh b/scripts/text_condition/train_image_1x512x512.sh index 0d4f98b21..5b74b7cf9 100644 --- a/scripts/text_condition/train_image_1x512x512.sh +++ b/scripts/text_condition/train_image_1x512x512.sh @@ -25,7 +25,7 @@ accelerate launch \ --max_train_steps=1000000 \ --learning_rate=2e-05 \ --lr_scheduler="constant" \ - --lr_warmup_steps=1000 \ + --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ From 92220bf1de920360b55d4831ba251d414b9f5388 Mon Sep 17 00:00:00 2001 From: yeyang <2267330597@qq.com> Date: Sun, 5 May 2024 08:03:30 +0000 Subject: [PATCH 006/134] inference any res. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 95d0f4204..22dd9f7fb 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,5 @@ sample_videos/ 1024* debug* private* -caption* \ No newline at end of file +caption* +.deepspeed_env \ No newline at end of file From b3fa7a548cf5d15ee14f12ae6e43524e68edca9b Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Sun, 5 May 2024 09:02:16 +0000 Subject: [PATCH 007/134] update --- scripts/text_condition/train_image_1x512x512.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/text_condition/train_image_1x512x512.sh b/scripts/text_condition/train_image_1x512x512.sh index 5b74b7cf9..b900a7f17 100644 --- a/scripts/text_condition/train_image_1x512x512.sh +++ b/scripts/text_condition/train_image_1x512x512.sh @@ -1,11 +1,11 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" export ENTITY="linbin" -export PROJECT="test_img" +export PROJECT="512ms_lr2e-5_bs64_4node" #export HF_ENDPOINT="https://hf-mirror.com" #export HF_DATASETS_OFFLINE=1 #export TRANSFORMERS_OFFLINE=1 accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -29,7 +29,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="test_img" \ + --output_dir="512ms_lr2e-5_bs64_4node" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 300 \ From 2ac285c8df1a5a51557ff4ea8af8ebda3ce6e02d Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Sat, 18 May 2024 12:30:26 +0000 Subject: [PATCH 008/134] check 3d attn --- .gitignore | 5 +- opensora/dataset/__init__.py | 12 +++- opensora/dataset/t2v_datasets.py | 4 +- .../diffusion/opensora/modeling_opensora.py | 58 +++++++++++++++++-- opensora/models/diffusion/opensora/modules.py | 2 +- opensora/models/text_encoder/__init__.py | 3 +- opensora/train/train_t2v.py | 8 ++- scripts/text_condition/sample_image.sh | 8 +-- .../text_condition/train_image_1x512x512.sh | 3 +- .../text_condition/train_image_1x512x512_b.sh | 38 ++++++++++++ .../text_condition/train_image_1x512x512_l.sh | 38 ++++++++++++ .../text_condition/train_image_1x512x512_s.sh | 38 ++++++++++++ .../text_condition/train_video_1x512x512.sh | 39 +++++++++++++ 13 files changed, 235 insertions(+), 21 deletions(-) create mode 100644 scripts/text_condition/train_image_1x512x512_b.sh create mode 100644 scripts/text_condition/train_image_1x512x512_l.sh create mode 100644 scripts/text_condition/train_image_1x512x512_s.sh create mode 100644 scripts/text_condition/train_video_1x512x512.sh diff --git a/.gitignore b/.gitignore index 22dd9f7fb..f46cb4a52 100644 --- a/.gitignore +++ b/.gitignore @@ -29,4 +29,7 @@ sample_videos/ debug* private* caption* -.deepspeed_env \ No newline at end of file +.deepspeed_env +256* +sample* +taming* \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 0b61af602..8ce1d75fe 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -46,13 +46,19 @@ def getdataset(args): temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x norm_fun = ae_norm[args.ae] if args.dataset == 't2v': + if args.multi_scale: + resize = [ + LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), + SpatialStrideCropVideo(args.stride) + ] + else: + resize = [CenterCropResizeVideo(args.max_image_size), ] transform = transforms.Compose([ ToTensorVideo(), - LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), - SpatialStrideCropVideo(args.stride), + *resize, # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained(r"/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index a778a802c..03da39049 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -63,7 +63,7 @@ def __getitem__(self, idx): image_data = self.get_image(idx) # 1 frame video as image return dict(video_data=video_data, image_data=image_data) except Exception as e: - print(f'Error with {e}, {self.vid_cap_list[idx]}') + print(f'Error with {e}') return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): @@ -194,4 +194,4 @@ def get_img_cap_list(self): img_cap_list[i]['path'] = opj(folder, img_cap_list[i]['path']) img_cap_lists += img_cap_list img_cap_lists = [img_cap_lists[i: i+use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] - return img_cap_lists[:-1] # drop last to avoid error length \ No newline at end of file + return img_cap_lists[:-1] # drop last to avoid error length diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 98e90d1ae..956f77c6c 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -130,7 +130,7 @@ def __init__( def _init_patched_inputs(self, norm_type): assert self.config.sample_size_t is not None, "Transformer3DModel over patched input must provide sample_size_t" assert self.config.sample_size is not None, "Transformer3DModel over patched input must provide sample_size" - assert not (self.config.sample_size_t == 1 and self.config.patch_size_t == 2), "Image do not need patchfy in t-dim" + #assert not (self.config.sample_size_t == 1 and self.config.patch_size_t == 2), "Image do not need patchfy in t-dim" self.num_frames = self.config.sample_size_t @@ -524,17 +524,65 @@ def _get_output_for_patched_inputs( return output +# def OpenSoraT2V_S_122(**kwargs): +# return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + def OpenSoraT2V_S_122(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + return OpenSoraT2V(num_layers=24, attention_head_dim=64, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1024, **kwargs) + +def OpenSoraT2V_B_122(**kwargs): + return OpenSoraT2V(num_layers=24, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + +def OpenSoraT2V_L_122(**kwargs): + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + +def OpenSoraT2V_XL_122(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) + +def OpenSoraT2V_XXL_122(**kwargs): + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) + +# def OpenSoraT2V_S_222(**kwargs): +# return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_S_222(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + return OpenSoraT2V(num_layers=24, attention_head_dim=64, num_attention_heads=16, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1024, **kwargs) + +def OpenSoraT2V_B_222(**kwargs): + return OpenSoraT2V(num_layers=24, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + +def OpenSoraT2V_L_222(**kwargs): + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + +def OpenSoraT2V_XL_222(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) + +def OpenSoraT2V_XXL_222(**kwargs): + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) OpenSora_models = { "OpenSoraT2V-S/122": OpenSoraT2V_S_122, + "OpenSoraT2V-B/122": OpenSoraT2V_B_122, + "OpenSoraT2V-L/122": OpenSoraT2V_L_122, + "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, + "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, "OpenSoraT2V-S/222": OpenSoraT2V_S_222, + "OpenSoraT2V-B/222": OpenSoraT2V_B_222, + "OpenSoraT2V-L/222": OpenSoraT2V_L_222, + "OpenSoraT2V-XL/222": OpenSoraT2V_XL_222, + "OpenSoraT2V-XXL/222": OpenSoraT2V_XXL_222, } if __name__ == '__main__': diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 9566ae7ef..ad9e784e6 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -364,7 +364,7 @@ def __call__( encoder_hidden_states = hidden_states elif attn.norm_cross: encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) - + # import ipdb;ipdb.set_trace() key = attn.to_k(encoder_hidden_states) value = attn.to_v(encoder_hidden_states) diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 28cf771bb..6e4df9ccb 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -9,7 +9,8 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(r"/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 4027d7d8d..ada35b950 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -452,7 +452,8 @@ def load_model_hook(models, input_dir): with accelerator.accumulate(model): # Sample noise that we'll add to the latents - + if not args.multi_scale: + assert torch.all(attn_mask) x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 attn_mask = attn_mask.to(accelerator.device) # B T+num_images L input_ids = input_ids.to(accelerator.device) # B 1+num_images L @@ -612,8 +613,9 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') - parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=4.5) + parser.add_argument("--num_sampling_steps", type=int, default=20) + parser.add_argument('--guidance_scale', type=float, default=2.5) + parser.add_argument("--multi_scale", action="store_true") parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--use_deepspeed", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/sample_image.sh index ce5b166c5..b6ca3e3d8 100644 --- a/scripts/text_condition/sample_image.sh +++ b/scripts/text_condition/sample_image.sh @@ -1,5 +1,5 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v_pixsig.py \ - --model_path test_img/checkpoint-6000/model \ + --model_path 512ms_lr2e-5_bs64_4node/checkpoint-71000/model \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ @@ -8,9 +8,9 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v_pixsig.py \ --save_img_path "./sample_images/prompt_list_0" \ --fps 24 \ --num_frames 1 \ - --height 256 \ + --height 512 \ --width 512 \ - --guidance_scale 4.5 \ + --guidance_scale 2.5 \ --sample_method "DPMSolverMultistep" \ - --num_sampling_steps 50 \ + --num_sampling_steps 20 \ --enable_tiling diff --git a/scripts/text_condition/train_image_1x512x512.sh b/scripts/text_condition/train_image_1x512x512.sh index b900a7f17..95d43bec0 100644 --- a/scripts/text_condition/train_image_1x512x512.sh +++ b/scripts/text_condition/train_image_1x512x512.sh @@ -36,4 +36,5 @@ accelerate launch \ --use_image_num 0 \ --enable_tiling \ --pretrained /remote-home1/yeyang/PixArt-Sigma-XL-2-512-MS.safetensors \ - --enable_tracker + --enable_tracker \ + --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_image_1x512x512_b.sh b/scripts/text_condition/train_image_1x512x512_b.sh new file mode 100644 index 000000000..6f40a4a93 --- /dev/null +++ b/scripts/text_condition/train_image_1x512x512_b.sh @@ -0,0 +1,38 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export ENTITY="linbin" +export PROJECT="256_lr1e-4_bs64_1node_b" +#export HF_ENDPOINT="https://hf-mirror.com" +#export HF_DATASETS_OFFLINE=1 +#export TRANSFORMERS_OFFLINE=1 +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-B/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_image_size 256 \ + --attention_mode xformers \ + --train_batch_size=64 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-04 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="256_lr1e-4_bs64_1node_b" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 300 \ + --use_image_num 0 \ + --enable_tiling \ + --enable_tracker \ + --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_image_1x512x512_l.sh b/scripts/text_condition/train_image_1x512x512_l.sh new file mode 100644 index 000000000..7d26e6f49 --- /dev/null +++ b/scripts/text_condition/train_image_1x512x512_l.sh @@ -0,0 +1,38 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export ENTITY="linbin" +export PROJECT="256_lr1e-4_bs64_1node_l" +#export HF_ENDPOINT="https://hf-mirror.com" +#export HF_DATASETS_OFFLINE=1 +#export TRANSFORMERS_OFFLINE=1 +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-L/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_image_size 256 \ + --attention_mode xformers \ + --train_batch_size=32 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-04 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="256_lr1e-4_bs64_1node_l" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 300 \ + --use_image_num 0 \ + --enable_tiling \ + --enable_tracker \ + --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_image_1x512x512_s.sh b/scripts/text_condition/train_image_1x512x512_s.sh new file mode 100644 index 000000000..a36b9e9e0 --- /dev/null +++ b/scripts/text_condition/train_image_1x512x512_s.sh @@ -0,0 +1,38 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export ENTITY="linbin" +export PROJECT="256_lr1e-4_bs64_1node_s" +#export HF_ENDPOINT="https://hf-mirror.com" +#export HF_DATASETS_OFFLINE=1 +#export TRANSFORMERS_OFFLINE=1 +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-S/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_image_size 256 \ + --attention_mode xformers \ + --train_batch_size=64 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-04 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="256_lr1e-4_bs64_1node_s" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 300 \ + --use_image_num 0 \ + --enable_tiling \ + --enable_tracker \ + --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_video_1x512x512.sh b/scripts/text_condition/train_video_1x512x512.sh new file mode 100644 index 000000000..fb99153e1 --- /dev/null +++ b/scripts/text_condition/train_video_1x512x512.sh @@ -0,0 +1,39 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export ENTITY="linbin" +export PROJECT="256_lr1e-4_bs64_4node" +# export HF_ENDPOINT="https://hf-mirror.com" +# export HF_DATASETS_OFFLINE=1 +# export TRANSFORMERS_OFFLINE=1 +# export NCCL_P2P_DISABLE=1 +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-S/222 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_image_size 256 \ + --attention_mode xformers \ + --train_batch_size=64 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-04 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="256_lr1e-4_bs64_4node" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 300 \ + --use_image_num 0 \ + --enable_tiling \ + --enable_tracker \ + --resume_from_checkpoint "latest" From c7866d45f1ecc4ce9ca8bdbb41c6fa2dbfb7e19f Mon Sep 17 00:00:00 2001 From: root <2267330597@qq.com> Date: Thu, 23 May 2024 11:23:20 +0000 Subject: [PATCH 009/134] fix init bug --- .gitignore | 5 +- opensora/dataset/__init__.py | 5 +- opensora/dataset/t2v_datasets.py | 5 +- opensora/models/ae/imagebase/__init__.py | 5 +- opensora/models/ae/imagebase/vae/vae.py | 9 +- opensora/models/ae/videobase/__init__.py | 3 + .../diffusion/opensora/modeling_opensora.py | 116 ++++++++---------- opensora/models/diffusion/opensora/modules.py | 32 +++-- opensora/models/text_encoder/__init__.py | 2 +- opensora/sample/pipeline_opensora.py | 2 +- opensora/sample/sample_t2v.py | 14 +-- opensora/sample/sample_t2v_pixsig.py | 67 ++++++++-- opensora/train/train_t2v.py | 46 ++++--- opensora/utils/dataset_utils.py | 1 + .../utils/diffusion/gaussian_diffusion_t2v.py | 2 +- scripts/accelerate_configs/hostfile | 6 +- scripts/accelerate_configs/hostfile1 | 2 + .../multi_node_example.yaml | 8 +- .../multi_node_example1.yaml | 18 +++ scripts/text_condition/sample_video.sh | 18 +-- .../text_condition/train_image_1x512x512.sh | 40 ------ .../text_condition/train_image_1x512x512_b.sh | 38 ------ .../text_condition/train_image_1x512x512_l.sh | 38 ------ .../text_condition/train_video_1x512x512.sh | 39 ------ .../text_condition/train_video_65x512x512.sh | 33 ++--- ...2_s.sh => train_video_65x512x512_noimg.sh} | 27 ++-- scripts/train_data/image_data copy.txt | 4 - scripts/train_data/image_data.txt | 8 +- scripts/train_data/image_data_debug.txt | 1 + scripts/train_data/video_data.txt | 4 +- scripts/train_data/video_data_513.txt | 3 + scripts/train_data/video_data_debug.txt | 1 + 32 files changed, 273 insertions(+), 329 deletions(-) create mode 100644 scripts/accelerate_configs/hostfile1 create mode 100644 scripts/accelerate_configs/multi_node_example1.yaml delete mode 100644 scripts/text_condition/train_image_1x512x512.sh delete mode 100644 scripts/text_condition/train_image_1x512x512_b.sh delete mode 100644 scripts/text_condition/train_image_1x512x512_l.sh delete mode 100644 scripts/text_condition/train_video_1x512x512.sh rename scripts/text_condition/{train_image_1x512x512_s.sh => train_video_65x512x512_noimg.sh} (56%) delete mode 100644 scripts/train_data/image_data copy.txt create mode 100644 scripts/train_data/image_data_debug.txt create mode 100644 scripts/train_data/video_data_513.txt create mode 100644 scripts/train_data/video_data_debug.txt diff --git a/.gitignore b/.gitignore index f46cb4a52..69227dc8d 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,7 @@ caption* .deepspeed_env 256* sample* -taming* \ No newline at end of file +taming* +*test* +sft* +flash* \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 8ce1d75fe..45ba4796e 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -9,6 +9,7 @@ ae_norm = { + 'CausalVAEModel_2x8x8': Lambda(lambda x: 2. * x - 1.), 'CausalVAEModel_4x8x8': Lambda(lambda x: 2. * x - 1.), 'CausalVQVAEModel_4x4x4': Lambda(lambda x: x - 0.5), 'CausalVQVAEModel_4x8x8': Lambda(lambda x: x - 0.5), @@ -26,6 +27,7 @@ } ae_denorm = { + 'CausalVAEModel_2x8x8': lambda x: (x + 1.) / 2., 'CausalVAEModel_4x8x8': lambda x: (x + 1.) / 2., 'CausalVQVAEModel_4x4x4': lambda x: x + 0.5, 'CausalVQVAEModel_4x8x8': lambda x: x + 0.5, @@ -59,6 +61,7 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - tokenizer = AutoTokenizer.from_pretrained(r"/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 03da39049..809fac7a7 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -177,7 +177,10 @@ def get_vid_cap_list(self): vid_cap_list = json.load(f) print(f'Building {anno}...') for i in tqdm(range(len(vid_cap_list))): - vid_cap_list[i]['path'] = opj(folder, vid_cap_list[i]['path']) + path = opj(folder, vid_cap_list[i]['path']) + if os.path.exists(path.replace('.mp4', '_resize_1080p.mp4')): + path = path.replace('.mp4', '_resize_1080p.mp4') + vid_cap_list[i]['path'] = path vid_cap_lists += vid_cap_list return vid_cap_lists diff --git a/opensora/models/ae/imagebase/__init__.py b/opensora/models/ae/imagebase/__init__.py index 12eeb327f..5f96ae00d 100644 --- a/opensora/models/ae/imagebase/__init__.py +++ b/opensora/models/ae/imagebase/__init__.py @@ -2,10 +2,11 @@ from .vae.vae import SDVAEWrapper from .vqvae.vqvae import SDVQVAEWrapper -vae = ['stabilityai/sd-vae-ft-mse', 'stabilityai/sd-vae-ft-ema'] +vae = ['stabilityai/sdxl-vae', 'stabilityai/sd-vae-ft-mse', 'stabilityai/sd-vae-ft-ema'] vqvae = ['vqgan_imagenet_f16_1024', 'vqgan_imagenet_f16_16384', 'vqgan_gumbel_f8'] imagebase_ae_stride = { + 'stabilityai/sdxl-vae': [1, 8, 8], 'stabilityai/sd-vae-ft-mse': [1, 8, 8], 'stabilityai/sd-vae-ft-ema': [1, 8, 8], 'vqgan_imagenet_f16_1024': [1, 16, 16], @@ -14,6 +15,7 @@ } imagebase_ae_channel = { + 'stabilityai/sdxl-vae': 4, 'stabilityai/sd-vae-ft-mse': 4, 'stabilityai/sd-vae-ft-ema': 4, 'vqgan_imagenet_f16_1024': -1, @@ -22,6 +24,7 @@ } imagebase_ae = { + 'stabilityai/sdxl-vae': HFVAEWrapper, 'stabilityai/sd-vae-ft-mse': HFVAEWrapper, 'stabilityai/sd-vae-ft-ema': HFVAEWrapper, 'vqgan_imagenet_f16_1024': SDVQVAEWrapper, diff --git a/opensora/models/ae/imagebase/vae/vae.py b/opensora/models/ae/imagebase/vae/vae.py index 4f197ae12..3806a3105 100644 --- a/opensora/models/ae/imagebase/vae/vae.py +++ b/opensora/models/ae/imagebase/vae/vae.py @@ -12,20 +12,25 @@ def encode(self, x): # b c h w if x.ndim == 5: b, c, t, h, w = x.shape x = rearrange(x, 'b c t h w -> (b t) c h w').contiguous() - x = self.vae.encode(x).latent_dist.sample().mul_(0.18215) + x = self.vae.encode(x).latent_dist.sample().mul_(getattr(self.vae.config, 'scaling_factor', 0.18215)) if t != 0: x = rearrange(x, '(b t) c h w -> b c t h w', t=t).contiguous() return x def decode(self, x): t = 0 + # import ipdb;ipdb.set_trace() if x.ndim == 5: b, c, t, h, w = x.shape x = rearrange(x, 'b c t h w -> (b t) c h w').contiguous() - x = self.vae.decode(x / 0.18215).sample + x = self.vae.decode(x / getattr(self.vae.config, 'scaling_factor', 0.18215)).sample if t != 0: x = rearrange(x, '(b t) c h w -> b t c h w', t=t).contiguous() return x + def dtype(self): + return self.vae.dtype + + class SDVAEWrapper(nn.Module): def __init__(self): super(SDVAEWrapper, self).__init__() diff --git a/opensora/models/ae/videobase/__init__.py b/opensora/models/ae/videobase/__init__.py index 8c70a5651..68bc77aa4 100644 --- a/opensora/models/ae/videobase/__init__.py +++ b/opensora/models/ae/videobase/__init__.py @@ -15,6 +15,7 @@ videobase_ae_stride = { + 'CausalVAEModel_2x8x8': [2, 8, 8], 'CausalVAEModel_4x8x8': [4, 8, 8], 'CausalVQVAEModel_4x4x4': [4, 4, 4], 'CausalVQVAEModel_4x8x8': [4, 8, 8], @@ -28,6 +29,7 @@ } videobase_ae_channel = { + 'CausalVAEModel_2x8x8': 4, 'CausalVAEModel_4x8x8': 4, 'CausalVQVAEModel_4x4x4': 4, 'CausalVQVAEModel_4x8x8': 4, @@ -41,6 +43,7 @@ } videobase_ae = { + 'CausalVAEModel_2x8x8': CausalVAEModelWrapper, 'CausalVAEModel_4x8x8': CausalVAEModelWrapper, 'CausalVQVAEModel_4x4x4': CausalVQVAEModelWrapper, 'CausalVQVAEModel_4x8x8': CausalVQVAEModelWrapper, diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 956f77c6c..046d7523a 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -133,45 +133,43 @@ def _init_patched_inputs(self, norm_type): #assert not (self.config.sample_size_t == 1 and self.config.patch_size_t == 2), "Image do not need patchfy in t-dim" self.num_frames = self.config.sample_size_t - self.config.sample_size = to_2tuple(self.config.sample_size) self.height = self.config.sample_size[0] self.width = self.config.sample_size[1] self.patch_size_t = self.config.patch_size_t self.patch_size = self.config.patch_size - sample_size_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t // 16 + sample_size_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 interpolation_scale_t = ( - self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else max(sample_size_t, 1) + self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else sample_size_t ) interpolation_scale = ( - self.config.interpolation_scale if self.config.interpolation_scale is not None else max(self.config.sample_size[0] // 64, 1), - self.config.interpolation_scale if self.config.interpolation_scale is not None else max(self.config.sample_size[1] // 64, 1), + self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[0] / 32, + self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[1] / 32, + ) + # if self.config.sample_size_t > 1: + # self.pos_embed = PatchEmbed3D( + # num_frames=self.config.sample_size_t, + # height=self.config.sample_size[0], + # width=self.config.sample_size[1], + # patch_size_t=self.config.patch_size_t, + # patch_size=self.config.patch_size, + # in_channels=self.in_channels, + # embed_dim=self.inner_dim, + # interpolation_scale=interpolation_scale, + # interpolation_scale_t=interpolation_scale_t, + # ) + # else: + self.pos_embed = PatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, ) - - if self.config.sample_size_t > 1: - self.pos_embed = PatchEmbed3D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - patch_size_t=self.config.patch_size_t, - patch_size=self.config.patch_size, - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - ) - else: - self.pos_embed = PatchEmbed2D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - patch_size_t=self.config.patch_size_t, - patch_size=self.config.patch_size, - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - ) self.transformer_blocks = nn.ModuleList( [ @@ -280,6 +278,7 @@ def forward( If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a `tuple` where the first element is the sample tensor. """ + # import ipdb;ipdb.set_trace() batch_size, c, frame, h, w = hidden_states.shape frame = frame - use_image_num # 21-4=17 if cross_attention_kwargs is not None: @@ -296,6 +295,7 @@ def forward( # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) attention_mask_vid, attention_mask_img = None, None + # import ipdb;ipdb.set_trace() if attention_mask is not None and attention_mask.ndim == 4: # assume that mask is expressed as: # (1 = keep, 0 = discard) @@ -321,7 +321,6 @@ def forward( attention_mask_vid = (1 - attention_mask_vid.bool().to(self.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None attention_mask_img = (1 - attention_mask_img.bool().to(self.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None - # import ipdb;ipdb.set_trace() if frame == 1 and use_image_num == 0: attention_mask_img = attention_mask_vid attention_mask_vid = None @@ -353,6 +352,7 @@ def forward( frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy # 2. Blocks + # import ipdb;ipdb.set_trace() for block in self.transformer_blocks: if self.training and self.gradient_checkpointing: @@ -366,7 +366,7 @@ def custom_forward(*inputs): return custom_forward ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} - + # import ipdb;ipdb.set_trace() if hidden_states_vid is not None: hidden_states_vid = torch.utils.checkpoint.checkpoint( create_custom_forward(block), @@ -379,6 +379,7 @@ def custom_forward(*inputs): class_labels, **ckpt_kwargs, ) + # import ipdb;ipdb.set_trace() if hidden_states_img is not None: hidden_states_img = torch.utils.checkpoint.checkpoint( create_custom_forward(block), @@ -453,6 +454,7 @@ def custom_forward(*inputs): def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): # batch_size = hidden_states.shape[0] + # import ipdb;ipdb.set_trace() hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states.to(self.dtype), frame) timestep_vid, timestep_img = None, None embedded_timestep_vid, embedded_timestep_img = None, None @@ -478,7 +480,6 @@ def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, times if self.caption_projection is not None: encoder_hidden_states = self.caption_projection(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d - # import ipdb;ipdb.set_trace() if hidden_states_vid is None: encoder_hidden_states_img = rearrange(encoder_hidden_states, 'b 1 l d -> (b 1) l d') else: @@ -493,7 +494,8 @@ def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, times def _get_output_for_patched_inputs( self, hidden_states, timestep, class_labels, embedded_timestep, num_frames, height=None, width=None - ): + ): + # import ipdb;ipdb.set_trace() if self.config.norm_type != "ada_norm_single": conditioning = self.transformer_blocks[0].norm1.emb( timestep, class_labels, hidden_dtype=self.dtype @@ -519,42 +521,20 @@ def _get_output_for_patched_inputs( output = hidden_states.reshape( shape=(-1, self.out_channels, num_frames * self.patch_size_t, height * self.patch_size, width * self.patch_size) ) - if output.shape[2] > 1: + # import ipdb;ipdb.set_trace() + if output.shape[2] % 2 == 0: output = output[:, :, 1:] return output -# def OpenSoraT2V_S_122(**kwargs): -# return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, -# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) - def OpenSoraT2V_S_122(**kwargs): - return OpenSoraT2V(num_layers=24, attention_head_dim=64, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1024, **kwargs) - -def OpenSoraT2V_B_122(**kwargs): - return OpenSoraT2V(num_layers=24, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) - -def OpenSoraT2V_L_122(**kwargs): - return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) - -def OpenSoraT2V_XL_122(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) - -def OpenSoraT2V_XXL_122(**kwargs): - return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) - -# def OpenSoraT2V_S_222(**kwargs): -# return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_S_222(**kwargs): - return OpenSoraT2V(num_layers=24, attention_head_dim=64, num_attention_heads=16, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1024, **kwargs) + return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + def OpenSoraT2V_B_222(**kwargs): return OpenSoraT2V(num_layers=24, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, @@ -574,10 +554,10 @@ def OpenSoraT2V_XXL_222(**kwargs): OpenSora_models = { "OpenSoraT2V-S/122": OpenSoraT2V_S_122, - "OpenSoraT2V-B/122": OpenSoraT2V_B_122, - "OpenSoraT2V-L/122": OpenSoraT2V_L_122, - "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, - "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, + # "OpenSoraT2V-B/122": OpenSoraT2V_B_122, + # "OpenSoraT2V-L/122": OpenSoraT2V_L_122, + # "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, + # "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, "OpenSoraT2V-S/222": OpenSoraT2V_S_222, "OpenSoraT2V-B/222": OpenSoraT2V_B_222, "OpenSoraT2V-L/222": OpenSoraT2V_L_222, @@ -634,7 +614,7 @@ def OpenSoraT2V_XXL_222(**kwargs): path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--PixArt-alpha--PixArt-Sigma-XL-2-512-MS/snapshots/786c445c97ddcc0eb2faa157b131ac71ee1935a2/transformer/diffusion_pytorch_model.safetensors" from safetensors.torch import load_file as safe_load ckpt = safe_load(path, device="cpu") - import ipdb;ipdb.set_trace() + # import ipdb;ipdb.set_trace() if ckpt['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and ckpt['pos_embed.proj.weight'].ndim == 4: repeat = model.pos_embed.proj.weight.shape[2] ckpt['pos_embed.proj.weight'] = ckpt['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index ad9e784e6..1b947b01e 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -131,7 +131,7 @@ def __init__( interpolation_scale_t=1, ): super().__init__() - assert num_frames == 1 + # assert num_frames == 1 self.flatten = flatten self.layer_norm = layer_norm @@ -151,6 +151,7 @@ def __init__( self.height, self.width = height // patch_size, width // patch_size self.base_size = (height // patch_size, width // patch_size) self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + # import ipdb;ipdb.set_trace() pos_embed = get_2d_sincos_pos_embed( embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale ) @@ -158,24 +159,24 @@ def __init__( def forward(self, latent, num_frames): - # import ipdb;ipdb.set_trace() - video_latent = None + b, _, _, _, _ = latent.shape + video_latent, image_latent = None, None # b c 1 h w - assert latent.shape[-3] == 1 and num_frames == 1 + # assert latent.shape[-3] == 1 and num_frames == 1 height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size - - - latent = latent.squeeze(2) # b c 1 h w -> b c h w + latent = rearrange(latent, 'b c t h w -> (b t) c h w') latent = self.proj(latent) if self.flatten: - latent = latent.flatten(2).transpose(1, 2) # BCHW -> BNC + latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C if self.layer_norm: latent = self.norm(latent) # Interpolate positional embeddings if needed. # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) if self.height != height or self.width != width: + # import ipdb;ipdb.set_trace() + raise NotImplementedError pos_embed = get_2d_sincos_pos_embed( embed_dim=self.pos_embed.shape[-1], grid_size=(height, width), @@ -186,7 +187,12 @@ def forward(self, latent, num_frames): pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) else: pos_embed = self.pos_embed - image_latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) + + latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] + video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent.numel() > 0 else None + image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent.numel() > 0 else None return video_latent, image_latent @@ -261,7 +267,7 @@ def forward(self, latent, num_frames): video_latent = self.proj(video_latent) if image_latent.numel() > 0: - image_latent = image_latent.repeat_interleave(self.patch_size, dim=2) # b c i h w -> b c 2i h w + image_latent = image_latent.repeat_interleave(self.patch_size_t, dim=2) # b c i h w -> b c 2i h w image_latent = self.proj(image_latent) # b c 2i h w -> b c i h w if self.flatten: @@ -376,8 +382,12 @@ def __call__( key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + attention_mask = None # the output of sdp = (batch, num_heads, seq_len, head_dim) # TODO: add support for attn.scale when we move to Torch 2.1 + # import ipdb;ipdb.set_trace() + # print(query.shape) if self.attention_mode == 'flash': assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): @@ -635,6 +645,7 @@ def forward( # 0. Self-Attention batch_size = hidden_states.shape[0] + # import ipdb;ipdb.set_trace() if self.norm_type == "ada_norm": norm_hidden_states = self.norm1(hidden_states, timestep) elif self.norm_type == "ada_norm_zero": @@ -646,6 +657,7 @@ def forward( elif self.norm_type == "ada_norm_continuous": norm_hidden_states = self.norm1(hidden_states, added_cond_kwargs["pooled_text_emb"]) elif self.norm_type == "ada_norm_single": + # import ipdb;ipdb.set_trace() shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( self.scale_shift_table[None] + timestep.reshape(batch_size, 6, -1) ).chunk(6, dim=1) diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 6e4df9ccb..9ed2ec7f4 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -10,7 +10,7 @@ def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = T5EncoderModel.from_pretrained(r"/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 36927cfa2..c1324675d 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -655,7 +655,6 @@ def __call__( num_frames = num_frames or self.transformer.config.sample_size_t * self.vae.vae_scale_factor[0] height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] width = width or self.transformer.config.sample_size[1] * self.vae.vae_scale_factor[2] - self.check_inputs( prompt, num_frames, @@ -811,6 +810,7 @@ def __call__( def decode_latents(self, latents): + video = self.vae.decode(latents) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index bfb743b65..90f51ea7a 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -32,20 +32,20 @@ def main(args): # torch.manual_seed(args.seed) torch.set_grad_enabled(False) device = "cuda" if torch.cuda.is_available() else "cpu" - - # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir='cache_dir').to(device, dtype=torch.float16) - vae = getae_wrapper(args.ae)(args.ae_path).to(device, dtype=torch.float16) + dtype = torch.float16 + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir='cache_dir').to(device, dtype=dtype) + vae = getae_wrapper(args.ae)(args.ae_path).to(device, dtype=dtype) if args.enable_tiling: vae.vae.enable_tiling() vae.vae.tile_overlap_factor = args.tile_overlap_factor # Load model: - # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, torch_dtype=torch.float16).to(device) - transformer_model = LatteT2V.from_pretrained(args.model_path, low_cpu_mem_usage=False, device_map=None, torch_dtype=torch.float16).to(device) + # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, torch_dtype=dtype).to(device) + transformer_model = LatteT2V.from_pretrained(args.model_path, low_cpu_mem_usage=False, device_map=None, torch_dtype=dtype).to(device) print(transformer_model.config) transformer_model.force_images = args.force_images tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, torch_dtype=torch.float16).to(device) + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, torch_dtype=dtype).to(device) video_length, image_size = transformer_model.config.video_length, args.image_size latent_size = (image_size // ae_stride_config[args.ae][1], image_size // ae_stride_config[args.ae][2]) @@ -86,7 +86,7 @@ def main(args): text_encoder=text_encoder, tokenizer=tokenizer, scheduler=scheduler, - transformer=transformer_model).to(device=device) + transformer=transformer_model).to(device=device, dtype=dtype) # videogen_pipeline.enable_xformers_memory_efficient_attention() if not os.path.exists(args.save_img_path): diff --git a/opensora/sample/sample_t2v_pixsig.py b/opensora/sample/sample_t2v_pixsig.py index f8bf2487b..cf1806b43 100644 --- a/opensora/sample/sample_t2v_pixsig.py +++ b/opensora/sample/sample_t2v_pixsig.py @@ -33,17 +33,32 @@ def main(args): device = torch.device(args.device) vae = getae_wrapper(args.ae)(args.ae_path).to(dtype=weight_dtype) - if args.enable_tiling: - vae.vae.enable_tiling() - vae.vae.tile_overlap_factor = args.tile_overlap_factor + # if args.enable_tiling: + # vae.vae.enable_tiling() + # vae.vae.tile_overlap_factor = args.tile_overlap_factor vae.vae_scale_factor = ae_stride_config[args.ae] transformer_model = OpenSoraT2V.from_pretrained(args.model_path, low_cpu_mem_usage=True, device_map=None, torch_dtype=weight_dtype) - # print(transformer_model) - + + # ckpt = transformer_model.state_dict() + # from safetensors.torch import load_file + # file_path = "PixArt-Sigma-XL-2-256.safetensors" + # ckptsf = load_file(file_path) + # file_path = "/dxyl_code02/dev3d/Open-Sora-Plan/image_test_ckpt/diffusion_pytorch_model.bin" + # ckptbin = torch.load(file_path) + # # print(transformer_model) + # for k, v in ckpt.items(): + # assert torch.allclose(v, ckptsf[k]) and torch.allclose(v, ckptbin[k]) + # import ipdb;ipdb.set_trace() + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # text_encoder = T5EncoderModel.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/text_encoder", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + # tokenizer = T5Tokenizer.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/tokenizer", cache_dir=args.cache_dir) + + # set eval mode transformer_model.eval() vae.eval() @@ -87,6 +102,7 @@ def main(args): text_prompt = open(args.text_prompt[0], 'r').readlines() text_prompt = [i.strip() for i in text_prompt] + video_grids = [] for prompt in text_prompt: videos = pipeline(prompt, num_frames=args.num_frames, @@ -96,13 +112,40 @@ def main(args): guidance_scale=args.guidance_scale, num_images_per_prompt=1, mask_feature=True, - device=args.device + device=args.device, + max_sequence_length=50, ).images - ext = 'jpg' - videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w - save_image(videos / 255.0, os.path.join(args.save_img_path, - prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), - nrow=1, normalize=True, value_range=(0, 1)) # t c h w + try: + if args.num_frames == 1: + ext = 'jpg' + videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, + prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=1, normalize=True, value_range=(0, 1)) # t c h w + + else: + ext = 'mp4' + imageio.mimwrite( + os.path.join( + args.save_img_path, + prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' + ), videos[0], + fps=args.fps, quality=9) # highest quality is 10, lowest is 0 + except: + print('Error when saving {}'.format(prompt)) + video_grids.append(videos) + video_grids = torch.cat(video_grids, dim=0) + + + # torchvision.io.write_video(args.save_img_path + '_%04d' % args.run_time + '-.mp4', video_grids, fps=6) + if args.num_frames == 1: + save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) + else: + video_grids = save_video_grid(video_grids) + imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=9) + + print('save path {}'.format(args.save_img_path)) if __name__ == "__main__": @@ -119,7 +162,7 @@ def main(args): parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") parser.add_argument("--guidance_scale", type=float, default=7.5) - parser.add_argument("--sample_method", type=str, default="DPMSolverMultistep") + parser.add_argument("--sample_method", type=str, default="PNDM") parser.add_argument("--num_sampling_steps", type=int, default=50) parser.add_argument("--fps", type=int, default=24) parser.add_argument("--run_time", type=int, default=0) diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index ada35b950..c3fd0faaa 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -59,19 +59,19 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step): validation_prompt = [ "A small cactus with a happy face in the Sahara desert.", - "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", + # "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", "The majestic beauty of a waterfall cascading down a cliff into a serene lake.", - "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", - "Lego model, future rocket station, intricate details, high resolution, unreal engine, UHD", - "A litter of golden retriever puppies playing in the snow. Their heads pop out of the snow, covered in.", - "Close-up photos of models, hazy light and shadow, laser metal hair accessories, soft and beautiful, light gold pupils, white eyelashes, low saturation, real skin details, clear pores and fine lines, light reflection and refraction, ultra-clear, cinematography, award-winning works.", - "Full body shot, a French woman, Photography, French Streets background, backlighting, rim light, Fujifilm.", - "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", - "Modern luxury contemporary luxury home interiors house, in the style of mimicking ruined materials, ray tracing, haunting houses, and stone, capture the essence of nature, gray and bronze, dynamic outdoor shots.", - "One giant, sharp, metal square mirror in the center of the frame, four young people on the foreground, background sunny palm oil planation, tropical, realistic style, photography, nostalgic, green tone, mysterious, dreamy, bright color.", - "A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures.", - "Eiffel Tower was Made up of more than 2 million translucent straws to look like a cloud, with the bell tower at the top of the building, Michel installed huge foam-making machines in the forest to blow huge amounts of unpredictable wet clouds in the building's classic architecture.", - "A curvy timber house near a sea, designed by Zaha Hadid, represent the image of a cold, modern architecture, at night, white lighting, highly detailed.", + # "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", + # "Lego model, future rocket station, intricate details, high resolution, unreal engine, UHD", + # "A litter of golden retriever puppies playing in the snow. Their heads pop out of the snow, covered in.", + # "Close-up photos of models, hazy light and shadow, laser metal hair accessories, soft and beautiful, light gold pupils, white eyelashes, low saturation, real skin details, clear pores and fine lines, light reflection and refraction, ultra-clear, cinematography, award-winning works.", + # "Full body shot, a French woman, Photography, French Streets background, backlighting, rim light, Fujifilm.", + # "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", + # "Modern luxury contemporary luxury home interiors house, in the style of mimicking ruined materials, ray tracing, haunting houses, and stone, capture the essence of nature, gray and bronze, dynamic outdoor shots.", + # "One giant, sharp, metal square mirror in the center of the frame, four young people on the foreground, background sunny palm oil planation, tropical, realistic style, photography, nostalgic, green tone, mysterious, dreamy, bright color.", + # "A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures.", + # "Eiffel Tower was Made up of more than 2 million translucent straws to look like a cloud, with the bell tower at the top of the building, Michel installed huge foam-making machines in the forest to blow huge amounts of unpredictable wet clouds in the building's classic architecture.", + # "A curvy timber house near a sea, designed by Zaha Hadid, represent the image of a cold, modern architecture, at night, white lighting, highly detailed.", ] logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) @@ -87,6 +87,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh logger.info('Processing the ({}) prompt'.format(prompt)) video = opensora_pipeline(prompt, num_frames=args.num_frames, + # num_frames=1, height=args.max_image_size, width=args.max_image_size, num_inference_steps=args.num_sampling_steps, @@ -94,6 +95,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh enable_temporal_attentions=True, num_images_per_prompt=1, mask_feature=True, + max_sequence_length=50, ).images videos.append(video[0]) # import ipdb;ipdb.set_trace() @@ -114,8 +116,9 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh if tracker.name == "wandb": import wandb if videos.shape[1] == 1: - assert args.num_frames == 1 + # assert args.num_frames == 1 images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + # import ipdb;ipdb.set_trace() tracker.log( { "validation": [ @@ -256,11 +259,13 @@ def main(args): if args.pretrained: if 'safetensors' in args.pretrained: # pixart series from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() checkpoint = safe_load(args.pretrained, device="cpu") if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") repeat = model.pos_embed.proj.weight.shape[2] checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) - del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] else: # latest stage training weight checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] model_state_dict = model.state_dict() @@ -461,9 +466,16 @@ def load_model_hook(models, input_dir): # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) with torch.no_grad(): + # import ipdb;ipdb.set_trace() # use for loop to avoid OOM, because T5 is too huge... - B, _, _ = input_ids.shape # B 1+num_images L - cond = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + B, N, L = input_ids.shape # B 1+num_images L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D + cond = cond.reshape(B, N, L, -1) # Map input images to latent space + normalize latents if args.use_image_num == 0: @@ -614,7 +626,7 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--num_sampling_steps", type=int, default=20) - parser.add_argument('--guidance_scale', type=float, default=2.5) + parser.add_argument('--guidance_scale', type=float, default=2.0) parser.add_argument("--multi_scale", action="store_true") parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--use_deepspeed", action="store_true") diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 348b7249b..831b2ed6b 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -110,6 +110,7 @@ def __call__(self, batch): return pad_batch_tubes, attention_mask, input_ids, cond_mask def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, patch_size_thw, extra_1): + # import ipdb;ipdb.set_trace() # pad to max multiple of ds_stride batch_input_size = [i.shape for i in batch_tubes] # [(c t h w), (c t h w)] max_t, max_h, max_w = max_thw diff --git a/opensora/utils/diffusion/gaussian_diffusion_t2v.py b/opensora/utils/diffusion/gaussian_diffusion_t2v.py index b99b0d097..bf1a316b3 100644 --- a/opensora/utils/diffusion/gaussian_diffusion_t2v.py +++ b/opensora/utils/diffusion/gaussian_diffusion_t2v.py @@ -777,7 +777,7 @@ def training_losses(self, model, x_start, t, model_kwargs=None, noise=None): #the output shape of uncondition or class condition latte is not the same as the latte_t2v #BFCHW vs BCFHW B, C, F = x_t.shape[:3] - assert model_output[0].shape == (B, C * 2, F, *x_t.shape[3:]) + assert model_output[0].shape == (B, C * 2, F, *x_t.shape[3:]), f"{model_output[0].shape} != ({B}, {C} * 2, {F}, {x_t.shape[3:]})" #model_output, model_var_values = th.split(model_output, C, dim=2) model_output, model_var_values = th.split(model_output[0], C, dim=1) diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index f38fe5999..abdea9260 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,4 +1,2 @@ -gpu55 slots=8 # your server name and GPU in total -gpu117 slots=8 -gpu147 slots=8 -gpu176 slots=8 \ No newline at end of file +node117 slots=8 # your server name and GPU in total +node100 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile1 b/scripts/accelerate_configs/hostfile1 new file mode 100644 index 000000000..a0f5bf466 --- /dev/null +++ b/scripts/accelerate_configs/hostfile1 @@ -0,0 +1,2 @@ +node120 slots=8 +node106 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index 6162b87ea..428df440e 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -5,11 +5,11 @@ deepspeed_config: deepspeed_hostfile: scripts/accelerate_configs/hostfile fsdp_config: {} machine_rank: 0 -main_process_ip: 10.10.10.55 -main_process_port: 29501 +main_process_ip: 10.0.10.100 +main_process_port: 29502 main_training_function: main -num_machines: 4 -num_processes: 32 +num_machines: 2 +num_processes: 16 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/accelerate_configs/multi_node_example1.yaml b/scripts/accelerate_configs/multi_node_example1.yaml new file mode 100644 index 000000000..40dafcbd0 --- /dev/null +++ b/scripts/accelerate_configs/multi_node_example1.yaml @@ -0,0 +1,18 @@ +compute_environment: LOCAL_MACHINE +distributed_type: DEEPSPEED +deepspeed_config: + deepspeed_config_file: scripts/accelerate_configs/zero2.json + deepspeed_hostfile: scripts/accelerate_configs/hostfile1 +fsdp_config: {} +machine_rank: 0 +main_process_ip: 10.0.10.106 +main_process_port: 29502 +main_training_function: main +num_machines: 2 +num_processes: 16 +rdzv_backend: static +same_network: true +tpu_env: [] +tpu_use_cluster: false +tpu_use_sudo: false +use_cpu: false diff --git a/scripts/text_condition/sample_video.sh b/scripts/text_condition/sample_video.sh index fb6b7bd10..c508baf3b 100644 --- a/scripts/text_condition/sample_video.sh +++ b/scripts/text_condition/sample_video.sh @@ -1,14 +1,16 @@ -CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v.py \ - --model_path /remote-home1/yeyang/dev/Open-Sora-Plan/debug_mask_loss/checkpoint-500/model \ +CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v_pixsig.py \ + --model_path image_test_ckpt \ --version 65x512x512 \ - --image_size 512 \ - --cache_dir "/remote-home1/yeyang/sora4.8/Open-Sora-Plan/cache_dir" \ + --num_frames 1 \ + --height 256 \ + --width 256 \ + --cache_dir "cache_dir" \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/sora4.8/Open-Sora-Plan/CausalVAEModel_4x8x8/" \ + --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ --save_img_path "./sample_videos" \ --fps 24 \ - --guidance_scale 10.0 \ - --num_sampling_steps 50 \ - --enable_tiling + --guidance_scale 2.0 \ + --num_sampling_steps 20 \ + --enable_tiling diff --git a/scripts/text_condition/train_image_1x512x512.sh b/scripts/text_condition/train_image_1x512x512.sh deleted file mode 100644 index 95d43bec0..000000000 --- a/scripts/text_condition/train_image_1x512x512.sh +++ /dev/null @@ -1,40 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -export ENTITY="linbin" -export PROJECT="512ms_lr2e-5_bs64_4node" -#export HF_ENDPOINT="https://hf-mirror.com" -#export HF_DATASETS_OFFLINE=1 -#export TRANSFORMERS_OFFLINE=1 -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-S/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_image_size 512 \ - --gradient_checkpointing \ - --attention_mode xformers \ - --train_batch_size=64 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-05 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="512ms_lr2e-5_bs64_4node" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 0 \ - --enable_tiling \ - --pretrained /remote-home1/yeyang/PixArt-Sigma-XL-2-512-MS.safetensors \ - --enable_tracker \ - --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_image_1x512x512_b.sh b/scripts/text_condition/train_image_1x512x512_b.sh deleted file mode 100644 index 6f40a4a93..000000000 --- a/scripts/text_condition/train_image_1x512x512_b.sh +++ /dev/null @@ -1,38 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -export ENTITY="linbin" -export PROJECT="256_lr1e-4_bs64_1node_b" -#export HF_ENDPOINT="https://hf-mirror.com" -#export HF_DATASETS_OFFLINE=1 -#export TRANSFORMERS_OFFLINE=1 -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-B/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_image_size 256 \ - --attention_mode xformers \ - --train_batch_size=64 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-04 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="256_lr1e-4_bs64_1node_b" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 0 \ - --enable_tiling \ - --enable_tracker \ - --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_image_1x512x512_l.sh b/scripts/text_condition/train_image_1x512x512_l.sh deleted file mode 100644 index 7d26e6f49..000000000 --- a/scripts/text_condition/train_image_1x512x512_l.sh +++ /dev/null @@ -1,38 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -export ENTITY="linbin" -export PROJECT="256_lr1e-4_bs64_1node_l" -#export HF_ENDPOINT="https://hf-mirror.com" -#export HF_DATASETS_OFFLINE=1 -#export TRANSFORMERS_OFFLINE=1 -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-L/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_image_size 256 \ - --attention_mode xformers \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-04 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="256_lr1e-4_bs64_1node_l" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 0 \ - --enable_tiling \ - --enable_tracker \ - --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_video_1x512x512.sh b/scripts/text_condition/train_video_1x512x512.sh deleted file mode 100644 index fb99153e1..000000000 --- a/scripts/text_condition/train_video_1x512x512.sh +++ /dev/null @@ -1,39 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -export ENTITY="linbin" -export PROJECT="256_lr1e-4_bs64_4node" -# export HF_ENDPOINT="https://hf-mirror.com" -# export HF_DATASETS_OFFLINE=1 -# export TRANSFORMERS_OFFLINE=1 -# export NCCL_P2P_DISABLE=1 -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-S/222 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_image_size 256 \ - --attention_mode xformers \ - --train_batch_size=64 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-04 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="256_lr1e-4_bs64_4node" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 0 \ - --enable_tiling \ - --enable_tracker \ - --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_video_65x512x512.sh b/scripts/text_condition/train_video_65x512x512.sh index 8e3076cdc..40c39c611 100644 --- a/scripts/text_condition/train_video_65x512x512.sh +++ b/scripts/text_condition/train_video_65x512x512.sh @@ -1,37 +1,42 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="test_img" +export PROJECT="65x256x256_bs2x72_2img_lr1e-4_488vae_3dattn_xformers" +export NCCL_SOCKET_IFNAME=ibp25s0 +export NCCL_P2P_DISABLE=1 +export PDSH_PATH_TYPE=ssh accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v.py \ - --model OpenSoraT2V-S/222 \ + --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ --video_data "scripts/train_data/video_data.txt" \ --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ --num_frames 65 \ - --max_image_size 512 \ + --max_image_size 256 \ --gradient_checkpointing \ --attention_mode xformers \ --train_batch_size=2 \ - --dataloader_num_workers 0 \ + --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-05 \ + --learning_rate=1e-04 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=5 \ - --output_dir="test_img" \ + --report_to="tensorboard" \ + --checkpointing_steps=500 \ + --output_dir="65x256x256_bs2x72_2img_lr1e-4_488vae_3dattn_xformers" \ --allow_tf32 \ --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 4 \ + --model_max_length 50 \ + --use_image_num 2 \ --enable_tiling \ - --pretrained /remote-home1/yeyang/PixArt-Sigma-XL-2-512-MS.safetensors \ - --enable_tracker + --pretrained PixArt-Alpha-XL-2-256.safetensors \ + --enable_tracker \ + --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_image_1x512x512_s.sh b/scripts/text_condition/train_video_65x512x512_noimg.sh similarity index 56% rename from scripts/text_condition/train_image_1x512x512_s.sh rename to scripts/text_condition/train_video_65x512x512_noimg.sh index a36b9e9e0..fff099e98 100644 --- a/scripts/text_condition/train_image_1x512x512_s.sh +++ b/scripts/text_condition/train_video_65x512x512_noimg.sh @@ -1,24 +1,27 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="256_lr1e-4_bs64_1node_s" -#export HF_ENDPOINT="https://hf-mirror.com" -#export HF_DATASETS_OFFLINE=1 -#export TRANSFORMERS_OFFLINE=1 +export PROJECT="65x256x256_bs2x32_0img_lr1e-4_488vae_3dattn_xformers" +export NCCL_SOCKET_IFNAME=ibp25s0 +export NCCL_P2P_DISABLE=1 +export PDSH_PATH_TYPE=ssh accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example1.yaml \ opensora/train/train_t2v.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ - --num_frames 1 \ + --num_frames 65 \ --max_image_size 256 \ + --gradient_checkpointing \ --attention_mode xformers \ - --train_batch_size=64 \ + --train_batch_size=2 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -26,13 +29,13 @@ accelerate launch \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="wandb" \ + --report_to="tensorboard" \ --checkpointing_steps=500 \ - --output_dir="256_lr1e-4_bs64_1node_s" \ + --output_dir="65x256x256_bs2x32_0img_lr1e-4_488vae_3dattn_xformers" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 300 \ --use_image_num 0 \ --enable_tiling \ - --enable_tracker \ - --resume_from_checkpoint "latest" + --pretrained PixArt-Alpha-XL-2-256.safetensors \ + --enable_tracker diff --git a/scripts/train_data/image_data copy.txt b/scripts/train_data/image_data copy.txt deleted file mode 100644 index f980a9204..000000000 --- a/scripts/train_data/image_data copy.txt +++ /dev/null @@ -1,4 +0,0 @@ -/remote-home1/dataset/human_images,/remote-home1/dataset/anno_jsons/human_images_162094.json -/remote-home1/dataset/sam,/remote-home1/dataset/anno_jsons/sam_image_11185255.json -/remote-home1/dataset/anytext3m,/remote-home1/dataset/anno_jsons/anytext_en_1886137.json -/remote-home1/dataset/tuzhan_mj,/remote-home1/dataset/anno_jsons/tuzhan_mj_1712571.json \ No newline at end of file diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index fafc1d803..753b15ff1 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,4 +1,4 @@ -/remote-home1/dataset/human_images,/remote-home1/dataset/anno_jsons/human_images_162094.json -/remote-home1/dataset/tuzhan_mj,/remote-home1/dataset/anno_jsons/tuzhan_mj_1712571.json -/remote-home1/dataset/anytext3m,/remote-home1/dataset/anno_jsons/anytext_en_1886137.json -/remote-home1/dataset/sam,/remote-home1/dataset/anno_jsons/sam_image_11185255.json \ No newline at end of file +/dxyl_data02/datasets/tuzhan_mj,/dxyl_data02/anno_jsons/tuzhan_mj_1712571.json +/dxyl_data02/datasets/images,/dxyl_data02/anno_jsons/human_images_162094.json +/dxyl_data02/datasets/anytext3m,/dxyl_data02/anno_jsons/anytext_en_1886137.json +/dxyl_data02/datasets/sam,/dxyl_data02/anno_jsons/sam_image_11185255.json \ No newline at end of file diff --git a/scripts/train_data/image_data_debug.txt b/scripts/train_data/image_data_debug.txt new file mode 100644 index 000000000..3d601e8b5 --- /dev/null +++ b/scripts/train_data/image_data_debug.txt @@ -0,0 +1 @@ +/dxyl_data02/datasets/images,/dxyl_data02/anno_jsons/human_images_162094.json \ No newline at end of file diff --git a/scripts/train_data/video_data.txt b/scripts/train_data/video_data.txt index 6223c6714..7f9c55548 100644 --- a/scripts/train_data/video_data.txt +++ b/scripts/train_data/video_data.txt @@ -1 +1,3 @@ -/remote-home1/dataset/all_mixkit,/remote-home1/dataset/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/dxyl_data02/datasets/pixabay_v2,/dxyl_data02/anno_jsons/video_pixabay_65f_601513.json +/dxyl_data02/datasets/pexels,/dxyl_data02/anno_jsons/video_pexel_65f_3832666.json +/dxyl_data02/datasets/mixkit,/dxyl_data02/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file diff --git a/scripts/train_data/video_data_513.txt b/scripts/train_data/video_data_513.txt new file mode 100644 index 000000000..bdab87c8a --- /dev/null +++ b/scripts/train_data/video_data_513.txt @@ -0,0 +1,3 @@ +/dxyl_data02/datasets/pixabay_v2,/dxyl_data02/anno_jsons/video_pixabay_513f_51483.json +/dxyl_data02/datasets/mixkit,/dxyl_data02/anno_jsons/video_mixkit_513f_1997.json +/dxyl_data02/datasets/pexels,/dxyl_data02/anno_jsons/video_pexel_513f_271782.json \ No newline at end of file diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt new file mode 100644 index 000000000..54958b2a1 --- /dev/null +++ b/scripts/train_data/video_data_debug.txt @@ -0,0 +1 @@ +/dxyl_data02/datasets/mixkit,/dxyl_data02/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file From 3a63b5177a370133485dd268dd09dfea5a8befcb Mon Sep 17 00:00:00 2001 From: root <2267330597@qq.com> Date: Thu, 23 May 2024 14:57:52 +0000 Subject: [PATCH 010/134] add temp embed --- .gitignore | 3 +- opensora/models/diffusion/opensora/modules.py | 51 +++++++++++++++++-- opensora/train/train_t2v.py | 24 ++++++--- .../text_condition/train_video_65x512x512.sh | 8 +-- .../train_video_65x512x512_noimg.sh | 15 +++--- 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 69227dc8d..35280dc9d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,5 @@ sample* taming* *test* sft* -flash* \ No newline at end of file +flash* +65x256* \ No newline at end of file diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 1b947b01e..106a80e26 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -91,6 +91,22 @@ def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) return emb +def get_1d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid return: pos_embed: [grid_size, embed_dim] or + [1+grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid = np.arange(grid_size, dtype=np.float32) / (grid_size / base_size) / interpolation_scale + pos_embed = get_1d_sincos_pos_embed_from_grid(embed_dim, grid) # (H*W, D/2) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): """ embed_dim: output dimension for each position pos: a list of positions to be encoded: size (M,) out: (M, D) @@ -151,12 +167,17 @@ def __init__( self.height, self.width = height // patch_size, width // patch_size self.base_size = (height // patch_size, width // patch_size) self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) - # import ipdb;ipdb.set_trace() pos_embed = get_2d_sincos_pos_embed( embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale ) self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) + self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.interpolation_scale_t = interpolation_scale_t + temp_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_embed", torch.from_numpy(temp_embed).float().unsqueeze(0), persistent=False) + self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) def forward(self, latent, num_frames): b, _, _, _, _ = latent.shape @@ -187,12 +208,36 @@ def forward(self, latent, num_frames): pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) else: pos_embed = self.pos_embed + + + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + raise NotImplementedError + temp_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_embed = torch.from_numpy(temp_embed) + temp_embed = temp_embed.float().unsqueeze(0).to(latent.device) + else: + temp_embed = self.temp_embed + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] - video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent.numel() > 0 else None - image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent.numel() > 0 else None + + # import ipdb;ipdb.set_trace() + temp_embed = temp_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + # print('raw value:', self.temp_embed_gate, 'tanh:', self.temp_embed_gate.tanh()) + video_latent = (video_latent + temp_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + image_latent = (image_latent + temp_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + + + video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None + image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None return video_latent, image_latent diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index c3fd0faaa..d80b85fdb 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -58,8 +58,8 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step): validation_prompt = [ - "A small cactus with a happy face in the Sahara desert.", - # "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", + # "A small cactus with a happy face in the Sahara desert.", + "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", "The majestic beauty of a waterfall cascading down a cliff into a serene lake.", # "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", # "Lego model, future rocket station, intricate details, high resolution, unreal engine, UHD", @@ -119,23 +119,23 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh # assert args.num_frames == 1 images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') # import ipdb;ipdb.set_trace() - tracker.log( - { + logs = { "validation": [ wandb.Image(image, caption=f"{i}: {prompt}") for i, (image, prompt) in enumerate(zip(images, validation_prompt)) ] } - ) else: - tracker.log( - { + logs = { "validation": [ wandb.Video(video, caption=f"{i}: {prompt}", fps=10) for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) ] } - ) + # import ipdb;ipdb.set_trace() + # if hasattr(model.pos_embed, 'temp_embed_gate'): + # logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + tracker.log(logs) del opensora_pipeline gc.collect() @@ -585,6 +585,14 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu break if accelerator.is_main_process: + + for tracker in accelerator.trackers: + if tracker.name == "wandb": + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log({'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + if global_step % args.checkpointing_steps == 0: if args.use_ema: # Store the UNet parameters temporarily and load the EMA parameters to perform inference. diff --git a/scripts/text_condition/train_video_65x512x512.sh b/scripts/text_condition/train_video_65x512x512.sh index 40c39c611..15da4fd5e 100644 --- a/scripts/text_condition/train_video_65x512x512.sh +++ b/scripts/text_condition/train_video_65x512x512.sh @@ -1,12 +1,12 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -export WANDB_MODE="offline" +# export WANDB_MODE="offline" export ENTITY="linbin" export PROJECT="65x256x256_bs2x72_2img_lr1e-4_488vae_3dattn_xformers" export NCCL_SOCKET_IFNAME=ibp25s0 -export NCCL_P2P_DISABLE=1 +# export NCCL_P2P_DISABLE=1 export PDSH_PATH_TYPE=ssh accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -29,7 +29,7 @@ accelerate launch \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="tensorboard" \ + --report_to="wandb" \ --checkpointing_steps=500 \ --output_dir="65x256x256_bs2x72_2img_lr1e-4_488vae_3dattn_xformers" \ --allow_tf32 \ diff --git a/scripts/text_condition/train_video_65x512x512_noimg.sh b/scripts/text_condition/train_video_65x512x512_noimg.sh index fff099e98..042329d48 100644 --- a/scripts/text_condition/train_video_65x512x512_noimg.sh +++ b/scripts/text_condition/train_video_65x512x512_noimg.sh @@ -1,12 +1,12 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -export WANDB_MODE="offline" +# export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="65x256x256_bs2x32_0img_lr1e-4_488vae_3dattn_xformers" +export PROJECT="65x256x256_bs2x8_0img_lr1e-4_488vae_3dattn_xformers" export NCCL_SOCKET_IFNAME=ibp25s0 export NCCL_P2P_DISABLE=1 export PDSH_PATH_TYPE=ssh accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example1.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -29,13 +29,14 @@ accelerate launch \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="tensorboard" \ + --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="65x256x256_bs2x32_0img_lr1e-4_488vae_3dattn_xformers" \ + --output_dir="65x256x256_bs2x8_0img_lr1e-4_488vae_3dattn_xformers" \ --allow_tf32 \ --use_deepspeed \ - --model_max_length 300 \ + --model_max_length 150 \ --use_image_num 0 \ --enable_tiling \ --pretrained PixArt-Alpha-XL-2-256.safetensors \ - --enable_tracker + --enable_tracker \ + --resume_from_checkpoint "latest" From c051120200b6f4561cfe2debc70f4cd2428eac56 Mon Sep 17 00:00:00 2001 From: root <2267330597@qq.com> Date: Sat, 25 May 2024 02:44:15 +0000 Subject: [PATCH 011/134] fixed train log --- opensora/dataset/t2v_datasets.py | 3 +- .../diffusion/opensora/modeling_opensora.py | 4 +- opensora/models/diffusion/opensora/modules.py | 6 +-- opensora/sample/sample_t2v_pixsig.py | 4 +- opensora/train/train_t2v.py | 35 ++++++--------- scripts/accelerate_configs/hostfile | 10 ++++- .../multi_node_example.yaml | 4 +- scripts/text_condition/sample_video.sh | 16 +++---- .../text_condition/train_video_65x512x512.sh | 14 +++--- .../text_condition/train_video_65x512x512_.sh | 43 +++++++++++++++++++ 10 files changed, 90 insertions(+), 49 deletions(-) create mode 100644 scripts/text_condition/train_video_65x512x512_.sh diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 809fac7a7..9d41277e0 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -156,7 +156,8 @@ def decord_read(self, path, frame_idx=None): start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) else: start_frame_ind, end_frame_ind = frame_idx.split(':') - start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) + # start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) + start_frame_ind, end_frame_ind = int(start_frame_ind), int(start_frame_ind) + self.num_frames # assert end_frame_ind - start_frame_ind >= self.num_frames frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 046d7523a..bb601f1a6 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -143,8 +143,8 @@ def _init_patched_inputs(self, norm_type): self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else sample_size_t ) interpolation_scale = ( - self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[0] / 32, - self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[1] / 32, + self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[0] / 64, + self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[1] / 64, ) # if self.config.sample_size_t > 1: # self.pos_embed = PatchEmbed3D( diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 106a80e26..697c9a47d 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -193,11 +193,11 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) + # import ipdb;ipdb.set_trace() # Interpolate positional embeddings if needed. # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) if self.height != height or self.width != width: - # import ipdb;ipdb.set_trace() - raise NotImplementedError + # raise NotImplementedError pos_embed = get_2d_sincos_pos_embed( embed_dim=self.pos_embed.shape[-1], grid_size=(height, width), @@ -212,7 +212,7 @@ def forward(self, latent, num_frames): if self.num_frames != num_frames: # import ipdb;ipdb.set_trace() - raise NotImplementedError + # raise NotImplementedError temp_embed = get_1d_sincos_pos_embed( embed_dim=self.temp_embed.shape[-1], grid_size=num_frames, diff --git a/opensora/sample/sample_t2v_pixsig.py b/opensora/sample/sample_t2v_pixsig.py index cf1806b43..0249f2632 100644 --- a/opensora/sample/sample_t2v_pixsig.py +++ b/opensora/sample/sample_t2v_pixsig.py @@ -38,7 +38,7 @@ def main(args): # vae.vae.tile_overlap_factor = args.tile_overlap_factor vae.vae_scale_factor = ae_stride_config[args.ae] - transformer_model = OpenSoraT2V.from_pretrained(args.model_path, low_cpu_mem_usage=True, device_map=None, torch_dtype=weight_dtype) + transformer_model = OpenSoraT2V.from_pretrained(args.model_path, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) # ckpt = transformer_model.state_dict() # from safetensors.torch import load_file @@ -113,7 +113,7 @@ def main(args): num_images_per_prompt=1, mask_feature=True, device=args.device, - max_sequence_length=50, + max_sequence_length=100, ).images try: if args.num_frames == 1: diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index d80b85fdb..ecaf0c239 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -58,21 +58,9 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step): validation_prompt = [ - # "A small cactus with a happy face in the Sahara desert.", - "A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", - "The majestic beauty of a waterfall cascading down a cliff into a serene lake.", - # "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", - # "Lego model, future rocket station, intricate details, high resolution, unreal engine, UHD", - # "A litter of golden retriever puppies playing in the snow. Their heads pop out of the snow, covered in.", - # "Close-up photos of models, hazy light and shadow, laser metal hair accessories, soft and beautiful, light gold pupils, white eyelashes, low saturation, real skin details, clear pores and fine lines, light reflection and refraction, ultra-clear, cinematography, award-winning works.", - # "Full body shot, a French woman, Photography, French Streets background, backlighting, rim light, Fujifilm.", - # "Over the shoulder game perspective, game screen of Diablo 4, Inside the gorgeous palace is the wet ground, The necromancer knelt before the king, and a horde of skeletons he summoned stood at his side, cinematic light.", - # "Modern luxury contemporary luxury home interiors house, in the style of mimicking ruined materials, ray tracing, haunting houses, and stone, capture the essence of nature, gray and bronze, dynamic outdoor shots.", - # "One giant, sharp, metal square mirror in the center of the frame, four young people on the foreground, background sunny palm oil planation, tropical, realistic style, photography, nostalgic, green tone, mysterious, dreamy, bright color.", - # "A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures.", - # "Eiffel Tower was Made up of more than 2 million translucent straws to look like a cloud, with the bell tower at the top of the building, Michel installed huge foam-making machines in the forest to blow huge amounts of unpredictable wet clouds in the building's classic architecture.", - # "A curvy timber house near a sea, designed by Zaha Hadid, represent the image of a cold, modern architecture, at night, white lighting, highly detailed.", - ] + "a cat wearing sunglasses and working as a lifeguard at pool.", + "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." + ] logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) # scheduler = PNDMScheduler() @@ -133,8 +121,8 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh ] } # import ipdb;ipdb.set_trace() - # if hasattr(model.pos_embed, 'temp_embed_gate'): - # logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + if hasattr(model.pos_embed, 'temp_embed_gate'): + logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) tracker.log(logs) del opensora_pipeline @@ -588,10 +576,11 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu for tracker in accelerator.trackers: if tracker.name == "wandb": - if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): - tracker.log({'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) - elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): - tracker.log({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + if global_step % args.checkpointing_steps != 0: + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log({'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) if global_step % args.checkpointing_steps == 0: if args.use_ema: @@ -633,8 +622,8 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') - parser.add_argument("--num_sampling_steps", type=int, default=20) - parser.add_argument('--guidance_scale', type=float, default=2.0) + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=7.5) parser.add_argument("--multi_scale", action="store_true") parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--use_deepspeed", action="store_true") diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index abdea9260..d00a7549c 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,2 +1,10 @@ +node100 slots=8 node117 slots=8 # your server name and GPU in total -node100 slots=8 \ No newline at end of file +node99 slots=8 +node118 slots=8 +node119 slots=8 +node120 slots=8 +node121 slots=8 +node122 slots=8 +node104 slots=8 +node106 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index 428df440e..da100f544 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -8,8 +8,8 @@ machine_rank: 0 main_process_ip: 10.0.10.100 main_process_port: 29502 main_training_function: main -num_machines: 2 -num_processes: 16 +num_machines: 10 +num_processes: 80 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/text_condition/sample_video.sh b/scripts/text_condition/sample_video.sh index c508baf3b..465e2a4c0 100644 --- a/scripts/text_condition/sample_video.sh +++ b/scripts/text_condition/sample_video.sh @@ -1,16 +1,16 @@ CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v_pixsig.py \ - --model_path image_test_ckpt \ + --model_path image_test_ckpt512 \ --version 65x512x512 \ --num_frames 1 \ - --height 256 \ - --width 256 \ + --height 320 \ + --width 240 \ --cache_dir "cache_dir" \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ - --save_img_path "./sample_videos" \ + --ae stabilityai/sd-vae-ft-mse \ + --ae_path "alpha_vae" \ + --save_img_path "./sample_videos320_240" \ --fps 24 \ - --guidance_scale 2.0 \ - --num_sampling_steps 20 \ + --guidance_scale 4.0 \ + --num_sampling_steps 50 \ --enable_tiling diff --git a/scripts/text_condition/train_video_65x512x512.sh b/scripts/text_condition/train_video_65x512x512.sh index 15da4fd5e..fc4d3829a 100644 --- a/scripts/text_condition/train_video_65x512x512.sh +++ b/scripts/text_condition/train_video_65x512x512.sh @@ -1,9 +1,10 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="65x256x256_bs2x72_2img_lr1e-4_488vae_3dattn_xformers" +export PROJECT="65x256x256_bs2x80_4img_lr2e-5_488vae_3dattn_xformers" +export NCCL_DEBUG=DEBUG export NCCL_SOCKET_IFNAME=ibp25s0 -# export NCCL_P2P_DISABLE=1 +export NCCL_P2P_DISABLE=1 export PDSH_PATH_TYPE=ssh accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example.yaml \ @@ -19,23 +20,22 @@ accelerate launch \ --sample_rate 1 \ --num_frames 65 \ --max_image_size 256 \ - --gradient_checkpointing \ --attention_mode xformers \ --train_batch_size=2 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=1e-04 \ + --learning_rate=2e-05 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="65x256x256_bs2x72_2img_lr1e-4_488vae_3dattn_xformers" \ + --output_dir="65x256x256_bs2x80_4img_lr2e-5_488vae_3dattn_xformers" \ --allow_tf32 \ --use_deepspeed \ - --model_max_length 50 \ - --use_image_num 2 \ + --model_max_length 300 \ + --use_image_num 4 \ --enable_tiling \ --pretrained PixArt-Alpha-XL-2-256.safetensors \ --enable_tracker \ diff --git a/scripts/text_condition/train_video_65x512x512_.sh b/scripts/text_condition/train_video_65x512x512_.sh new file mode 100644 index 000000000..02d6fdefc --- /dev/null +++ b/scripts/text_condition/train_video_65x512x512_.sh @@ -0,0 +1,43 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="65x256x256_bs2x8_2img_lr1e-4_488vae_3dattn_xformers_" + +export NCCL_SOCKET_IFNAME=ibp25s0 +export NCCL_P2P_DISABLE=1 +export PDSH_PATH_TYPE=ssh +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-S/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 65 \ + --max_image_size 256 \ + --gradient_checkpointing \ + --attention_mode xformers \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-04 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="65x256x256_bs2x8_2img_lr1e-4_488vae_3dattn_xformers_" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 150 \ + --use_image_num 2 \ + --enable_tiling \ + --pretrained PixArt-Alpha-XL-2-256.safetensors \ + --enable_tracker \ + --resume_from_checkpoint "latest" From 86a3346e972b3d89e6f2beb1fbe4ede904612152 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Tue, 28 May 2024 07:17:46 +0000 Subject: [PATCH 012/134] update dev3d --- .gitignore | 3 +- opensora/dataset/__init__.py | 3 +- opensora/models/text_encoder/__init__.py | 2 +- opensora/sample/pipeline_opensora.py | 3 +- opensora/train/train_t2v.py | 31 +++++++++---- opensora/utils/diffusion/diffusion_utils.py | 23 ++++++++++ .../utils/diffusion/gaussian_diffusion_t2v.py | 15 ++++++- scripts/accelerate_configs/hostfile1 | 2 - .../multi_node_example1.yaml | 18 -------- scripts/text_condition/sample_video.sh | 6 +-- .../text_condition/train_video_65x512x512.sh | 28 ++++++------ .../text_condition/train_video_65x512x512_.sh | 43 ------------------- .../train_video_65x512x512_noimg.sh | 42 ------------------ scripts/train_data/video_data_debug.txt | 2 +- 14 files changed, 82 insertions(+), 139 deletions(-) delete mode 100644 scripts/accelerate_configs/hostfile1 delete mode 100644 scripts/accelerate_configs/multi_node_example1.yaml delete mode 100644 scripts/text_condition/train_video_65x512x512_.sh delete mode 100644 scripts/text_condition/train_video_65x512x512_noimg.sh diff --git a/.gitignore b/.gitignore index 35280dc9d..cc2e5f3a9 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,5 @@ taming* *test* sft* flash* -65x256* \ No newline at end of file +65x256* +alpha_vae \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 45ba4796e..ec4136b62 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -61,7 +61,6 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - tokenizer = AutoTokenizer.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 9ed2ec7f4..36bb38325 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -10,7 +10,7 @@ def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = T5EncoderModel.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(r"/remote-home1/yeyang/5.25dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index c1324675d..6a06d88b9 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -810,8 +810,7 @@ def __call__( def decode_latents(self, latents): - - video = self.vae.decode(latents) + video = self.vae.decode(latents.to(self.vae.vae.dtype)) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 return video diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index ecaf0c239..f82fab35a 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -43,6 +43,7 @@ from opensora.models.ae import getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper from opensora.utils.diffusion import create_diffusion_T as create_diffusion +from opensora.utils.diffusion.diffusion_utils import compute_snr from opensora.models.diffusion.latte.modeling_latte import LatteT2V from opensora.models.text_encoder import get_text_enc, get_text_warpper from opensora.utils.dataset_utils import Collate @@ -76,8 +77,8 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh video = opensora_pipeline(prompt, num_frames=args.num_frames, # num_frames=1, - height=args.max_image_size, - width=args.max_image_size, + height=args.max_height, + width=args.max_width, num_inference_steps=args.num_sampling_steps, guidance_scale=args.guidance_scale, enable_temporal_attentions=True, @@ -206,11 +207,13 @@ def main(args): args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." - assert args.max_image_size % ae_stride_h == 0, f"Image size must be divisible by ae_stride_h, but found max_image_size ({args.max_image_size}), ae_stride_h ({ae_stride_h})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + args.stride_t = ae_stride_t * patch_size_t args.stride = ae_stride_h * patch_size_h - latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) ae.latent_size = latent_size if args.num_frames % 2 == 1: @@ -262,7 +265,7 @@ def main(args): logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') # Freeze vae and text encoders. - ae.requires_grad_(False) + ae.vae.requires_grad_(False) text_enc.requires_grad_(False) # Set model as trainable. model.train() @@ -271,7 +274,7 @@ def main(args): # Move unet, vae and text_encoder to device and cast to weight_dtype # The VAE is in float32 to avoid NaN losses. # ae.to(accelerator.device, dtype=torch.float32) - ae.to(accelerator.device, dtype=weight_dtype) + ae.vae.to(accelerator.device, dtype=weight_dtype) # ae.to(accelerator.device) text_enc.to(accelerator.device, dtype=weight_dtype) # text_enc.to(accelerator.device) @@ -517,7 +520,18 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) - loss_dict = diffusion.training_losses(model, x, t, model_kwargs) + + + # loss_dict = diffusion.training_losses(model, x, t, model_kwargs) + if args.snr_gamma is not None: + snr = compute_snr(t, torch.from_numpy(diffusion.alphas_cumprod)) + mse_loss_weights = (torch.stack([snr, args.snr_gamma * torch.ones_like(t)], dim=1).min(dim=1)[0] / snr) + # print(t, mse_loss_weights) + loss_dict = diffusion.training_losses(model, x, t, model_kwargs, mse_loss_weights=mse_loss_weights) + else: + loss_dict = diffusion.training_losses(model, x, t, model_kwargs) + + loss = loss_dict["loss"].mean() # Gather the losses across all processes for logging (if we use distributed training). @@ -602,7 +616,8 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--image_data", type=str, default='') parser.add_argument("--sample_rate", type=int, default=1) parser.add_argument("--num_frames", type=int, default=17) - parser.add_argument("--max_image_size", type=int, default=512) + parser.add_argument("--max_height", type=int, default=512) + parser.add_argument("--max_width", type=int, default=512) parser.add_argument("--use_img_from_vid", action="store_true") parser.add_argument("--use_image_num", type=int, default=0) parser.add_argument("--model_max_length", type=int, default=300) diff --git a/opensora/utils/diffusion/diffusion_utils.py b/opensora/utils/diffusion/diffusion_utils.py index e493a6a3e..2a68fc9a9 100644 --- a/opensora/utils/diffusion/diffusion_utils.py +++ b/opensora/utils/diffusion/diffusion_utils.py @@ -6,6 +6,29 @@ import torch as th import numpy as np +def compute_snr(timesteps, alphas_cumprod): + """ + Computes SNR as per https://github.com/TiankaiHang/Min-SNR-Diffusion-Training/blob/521b624bd70c67cee4bdf49225915f5945a872e3/guided_diffusion/gaussian_diffusion.py#L847-L849 + """ + + sqrt_alphas_cumprod = alphas_cumprod**0.5 + sqrt_one_minus_alphas_cumprod = (1.0 - alphas_cumprod) ** 0.5 + + # Expand the tensors. + # Adapted from https://github.com/TiankaiHang/Min-SNR-Diffusion-Training/blob/521b624bd70c67cee4bdf49225915f5945a872e3/guided_diffusion/gaussian_diffusion.py#L1026 + sqrt_alphas_cumprod = sqrt_alphas_cumprod.to(device=timesteps.device)[timesteps].float() + while len(sqrt_alphas_cumprod.shape) < len(timesteps.shape): + sqrt_alphas_cumprod = sqrt_alphas_cumprod[..., None] + alpha = sqrt_alphas_cumprod.expand(timesteps.shape) + + sqrt_one_minus_alphas_cumprod = sqrt_one_minus_alphas_cumprod.to(device=timesteps.device)[timesteps].float() + while len(sqrt_one_minus_alphas_cumprod.shape) < len(timesteps.shape): + sqrt_one_minus_alphas_cumprod = sqrt_one_minus_alphas_cumprod[..., None] + sigma = sqrt_one_minus_alphas_cumprod.expand(timesteps.shape) + + # Compute SNR. + snr = (alpha / sigma) ** 2 + return snr def normal_kl(mean1, logvar1, mean2, logvar2): """ diff --git a/opensora/utils/diffusion/gaussian_diffusion_t2v.py b/opensora/utils/diffusion/gaussian_diffusion_t2v.py index bf1a316b3..31b05330c 100644 --- a/opensora/utils/diffusion/gaussian_diffusion_t2v.py +++ b/opensora/utils/diffusion/gaussian_diffusion_t2v.py @@ -19,6 +19,11 @@ def mean_flat(tensor): """ return tensor.mean(dim=list(range(1, len(tensor.shape)))) +def mean_flat_reweight(tensor, weights): + """ + Take the mean over all non-batch dimensions. + """ + return tensor.mean(dim=list(range(1, len(tensor.shape)))) * weights class ModelMeanType(enum.Enum): """ @@ -727,7 +732,7 @@ def _vb_terms_bpd( output = th.where((t == 0), decoder_nll, kl) return {"output": output, "pred_xstart": out["pred_xstart"]} - def training_losses(self, model, x_start, t, model_kwargs=None, noise=None): + def training_losses(self, model, x_start, t, model_kwargs=None, noise=None, mse_loss_weights=None): """ Compute training losses for a single timestep. :param model: the model to evaluate loss on. @@ -806,8 +811,14 @@ def training_losses(self, model, x_start, t, model_kwargs=None, noise=None): ModelMeanType.EPSILON: noise, }[self.model_mean_type] assert model_output.shape == target.shape == x_start.shape - terms["mse"] = mean_flat(((target - model_output) ** 2) * mask) + + # terms["mse"] = mean_flat(((target - model_output) ** 2) * mask) # import ipdb;ipdb.set_trace() + if mse_loss_weights is not None: + terms["mse"] = mean_flat_reweight(((target - model_output) ** 2) * mask, mse_loss_weights) + else: + terms["mse"] = mean_flat(((target - model_output) ** 2) * mask) + if "vb" in terms: terms["loss"] = terms["mse"] + terms["vb"] else: diff --git a/scripts/accelerate_configs/hostfile1 b/scripts/accelerate_configs/hostfile1 deleted file mode 100644 index a0f5bf466..000000000 --- a/scripts/accelerate_configs/hostfile1 +++ /dev/null @@ -1,2 +0,0 @@ -node120 slots=8 -node106 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example1.yaml b/scripts/accelerate_configs/multi_node_example1.yaml deleted file mode 100644 index 40dafcbd0..000000000 --- a/scripts/accelerate_configs/multi_node_example1.yaml +++ /dev/null @@ -1,18 +0,0 @@ -compute_environment: LOCAL_MACHINE -distributed_type: DEEPSPEED -deepspeed_config: - deepspeed_config_file: scripts/accelerate_configs/zero2.json - deepspeed_hostfile: scripts/accelerate_configs/hostfile1 -fsdp_config: {} -machine_rank: 0 -main_process_ip: 10.0.10.106 -main_process_port: 29502 -main_training_function: main -num_machines: 2 -num_processes: 16 -rdzv_backend: static -same_network: true -tpu_env: [] -tpu_use_cluster: false -tpu_use_sudo: false -use_cpu: false diff --git a/scripts/text_condition/sample_video.sh b/scripts/text_condition/sample_video.sh index 465e2a4c0..bb429175f 100644 --- a/scripts/text_condition/sample_video.sh +++ b/scripts/text_condition/sample_video.sh @@ -2,14 +2,14 @@ CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v_pixsig.py \ --model_path image_test_ckpt512 \ --version 65x512x512 \ --num_frames 1 \ - --height 320 \ - --width 240 \ + --height 512 \ + --width 512 \ --cache_dir "cache_dir" \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae stabilityai/sd-vae-ft-mse \ --ae_path "alpha_vae" \ - --save_img_path "./sample_videos320_240" \ + --save_img_path "./sample_videos512_512" \ --fps 24 \ --guidance_scale 4.0 \ --num_sampling_steps 50 \ diff --git a/scripts/text_condition/train_video_65x512x512.sh b/scripts/text_condition/train_video_65x512x512.sh index fc4d3829a..4f9a6792b 100644 --- a/scripts/text_condition/train_video_65x512x512.sh +++ b/scripts/text_condition/train_video_65x512x512.sh @@ -1,21 +1,20 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="65x256x256_bs2x80_4img_lr2e-5_488vae_3dattn_xformers" -export NCCL_DEBUG=DEBUG -export NCCL_SOCKET_IFNAME=ibp25s0 -export NCCL_P2P_DISABLE=1 -export PDSH_PATH_TYPE=ssh +export PROJECT="debug" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# 65x256x256_bs2x80_4img_lr2e-5_488vae_3dattn_xformers accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/debug.yaml \ opensora/train/train_t2v.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data.txt" \ + --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ --num_frames 65 \ @@ -25,18 +24,19 @@ accelerate launch \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-05 \ + --learning_rate=2e-5 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="65x256x256_bs2x80_4img_lr2e-5_488vae_3dattn_xformers" \ + --checkpointing_steps=100 \ + --output_dir="debug" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 300 \ - --use_image_num 4 \ + --use_image_num 0 \ --enable_tiling \ - --pretrained PixArt-Alpha-XL-2-256.safetensors \ + --pretrained PixArt-Alpha-XL-2-512.safetensors \ --enable_tracker \ - --resume_from_checkpoint "latest" + --resume_from_checkpoint "latest" \ + --snr_gamma 5.0 diff --git a/scripts/text_condition/train_video_65x512x512_.sh b/scripts/text_condition/train_video_65x512x512_.sh deleted file mode 100644 index 02d6fdefc..000000000 --- a/scripts/text_condition/train_video_65x512x512_.sh +++ /dev/null @@ -1,43 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="65x256x256_bs2x8_2img_lr1e-4_488vae_3dattn_xformers_" - -export NCCL_SOCKET_IFNAME=ibp25s0 -export NCCL_P2P_DISABLE=1 -export PDSH_PATH_TYPE=ssh -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-S/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 65 \ - --max_image_size 256 \ - --gradient_checkpointing \ - --attention_mode xformers \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-04 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="65x256x256_bs2x8_2img_lr1e-4_488vae_3dattn_xformers_" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 150 \ - --use_image_num 2 \ - --enable_tiling \ - --pretrained PixArt-Alpha-XL-2-256.safetensors \ - --enable_tracker \ - --resume_from_checkpoint "latest" diff --git a/scripts/text_condition/train_video_65x512x512_noimg.sh b/scripts/text_condition/train_video_65x512x512_noimg.sh deleted file mode 100644 index 042329d48..000000000 --- a/scripts/text_condition/train_video_65x512x512_noimg.sh +++ /dev/null @@ -1,42 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="65x256x256_bs2x8_0img_lr1e-4_488vae_3dattn_xformers" -export NCCL_SOCKET_IFNAME=ibp25s0 -export NCCL_P2P_DISABLE=1 -export PDSH_PATH_TYPE=ssh -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-S/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/dxyl_data02/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 65 \ - --max_image_size 256 \ - --gradient_checkpointing \ - --attention_mode xformers \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-04 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="65x256x256_bs2x8_0img_lr1e-4_488vae_3dattn_xformers" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 150 \ - --use_image_num 0 \ - --enable_tiling \ - --pretrained PixArt-Alpha-XL-2-256.safetensors \ - --enable_tracker \ - --resume_from_checkpoint "latest" diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt index 54958b2a1..6223c6714 100644 --- a/scripts/train_data/video_data_debug.txt +++ b/scripts/train_data/video_data_debug.txt @@ -1 +1 @@ -/dxyl_data02/datasets/mixkit,/dxyl_data02/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/remote-home1/dataset/all_mixkit,/remote-home1/dataset/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file From 11b08845667991246c8680c19f019028c1414724 Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Wed, 29 May 2024 22:51:51 +0800 Subject: [PATCH 013/134] support 2+1d --- opensora/dataset/__init__.py | 5 +- opensora/dataset/t2v_datasets.py | 28 ++++--- opensora/dataset/transform.py | 32 ++++++-- opensora/dataset/ucf101.py | 80 ------------------- .../models/diffusion/latte/modeling_latte.py | 67 +++++++++++----- opensora/models/diffusion/latte/modules.py | 75 ++++++++++++++++- .../diffusion/opensora/modeling_opensora.py | 65 ++++++++------- opensora/models/diffusion/opensora/modules.py | 4 +- opensora/models/text_encoder/__init__.py | 4 +- opensora/train/train_t2v.py | 16 ++-- opensora/utils/dataset_utils.py | 7 +- ...5x512x512.sh => train_video21d_65x240p.sh} | 33 +++++--- .../text_condition/train_video3d_65x240p.sh | 51 ++++++++++++ scripts/train_data/image_data_debug.txt | 2 +- scripts/train_data/video_data_debug.txt | 2 +- 15 files changed, 292 insertions(+), 179 deletions(-) delete mode 100644 opensora/dataset/ucf101.py rename scripts/text_condition/{train_video_65x512x512.sh => train_video21d_65x240p.sh} (56%) create mode 100644 scripts/text_condition/train_video3d_65x240p.sh diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index ec4136b62..9a1d0e3c1 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -54,13 +54,14 @@ def getdataset(args): SpatialStrideCropVideo(args.stride) ] else: - resize = [CenterCropResizeVideo(args.max_image_size), ] + resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] transform = transforms.Compose([ ToTensorVideo(), *resize, # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained("/storage/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 9d41277e0..3a3a6889c 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -67,16 +67,22 @@ def __getitem__(self, idx): return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): - # video = random.choice([random_video_noise(65, 3, 720, 360) * 255, random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 720)]) + # video = random.choice([random_video_noise(65, 3, 336, 448), random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 480)]) # # print('random shape', video.shape) # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = self.vid_cap_list[idx]['path'] - frame_idx = self.vid_cap_list[idx]['frame_idx'] - video = self.decord_read(video_path, frame_idx) - video = self.transform(video) # T C H W -> T C H W - # video = torch.rand(65, 3, 512, 512) + # video_path = self.vid_cap_list[idx]['path'] + # frame_idx = self.vid_cap_list[idx]['frame_idx'] + # video = self.decord_read(video_path, frame_idx) + + # h, w = video.shape[-2:] + # assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' + # t = video.shape[0] + # video = video[:(t - 1) // 4 * 4 + 1] + # video = self.transform(video) # T C H W -> T C H W + + video = torch.rand(65, 3, 240, 320) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] @@ -107,10 +113,12 @@ def get_image(self, idx): idx = idx % len(self.img_cap_list) # out of range image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] - image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] - image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] - image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + # image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + # image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + # image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + # image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + + image = [torch.rand(1, 3, 512, 512) for i in image_data] image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] caps = [i['cap'] for i in image_data] diff --git a/opensora/dataset/transform.py b/opensora/dataset/transform.py index bb89c2c85..50c0212b5 100644 --- a/opensora/dataset/transform.py +++ b/opensora/dataset/transform.py @@ -107,6 +107,25 @@ def center_crop_using_short_edge(clip): return crop(clip, i, j, th, tw) + +def center_crop_th_tw(clip, th, tw): + if not _is_tensor_video_clip(clip): + raise ValueError("clip should be a 4D torch.tensor") + + # import ipdb;ipdb.set_trace() + h, w = clip.size(-2), clip.size(-1) + tr = th / tw + if h / w > tr: + new_h = int(w * tr) + new_w = w + else: + new_h = h + new_w = int(h / tr) + + i = int(round((h - new_h) / 2.0)) + j = int(round((w - new_w) / 2.0)) + return crop(clip, i, j, new_h, new_w) + def random_shift_crop(clip): ''' Slide along the long edge, with the short edge as crop size @@ -290,12 +309,9 @@ def __init__( size, interpolation_mode="bilinear", ): - if isinstance(size, tuple): - if len(size) != 2: - raise ValueError(f"size should be tuple (height, width), instead got {size}") - self.size = size - else: - self.size = (size, size) + if len(size) != 2: + raise ValueError(f"size should be tuple (height, width), instead got {size}") + self.size = size self.interpolation_mode = interpolation_mode @@ -307,7 +323,9 @@ def __call__(self, clip): torch.tensor: scale resized / center cropped video clip. size is (T, C, crop_size, crop_size) """ - clip_center_crop = center_crop_using_short_edge(clip) + # clip_center_crop = center_crop_using_short_edge(clip) + clip_center_crop = center_crop_th_tw(clip, self.size[0], self.size[1]) + # import ipdb;ipdb.set_trace() clip_center_crop_resize = resize(clip_center_crop, target_size=self.size, interpolation_mode=self.interpolation_mode) return clip_center_crop_resize diff --git a/opensora/dataset/ucf101.py b/opensora/dataset/ucf101.py deleted file mode 100644 index c368976fa..000000000 --- a/opensora/dataset/ucf101.py +++ /dev/null @@ -1,80 +0,0 @@ -import math -import os - -import decord -import numpy as np -import torch -import torchvision -from decord import VideoReader, cpu -from torch.utils.data import Dataset -from torchvision.transforms import Compose, Lambda, ToTensor -from torchvision.transforms._transforms_video import NormalizeVideo, RandomCropVideo, RandomHorizontalFlipVideo -from pytorchvideo.transforms import ApplyTransformToKey, ShortSideScale, UniformTemporalSubsample -from torch.nn import functional as F -import random - -from opensora.utils.dataset_utils import DecordInit - - -class UCF101(Dataset): - def __init__(self, args, transform, temporal_sample): - self.data_path = args.data_path - self.num_frames = args.num_frames - self.transform = transform - self.temporal_sample = temporal_sample - self.v_decoder = DecordInit() - - self.classes = sorted(os.listdir(self.data_path)) - self.class_to_idx = {cls_name: idx for idx, cls_name in enumerate(self.classes)} - self.samples = self._make_dataset() - - - def _make_dataset(self): - dataset = [] - for class_name in self.classes: - class_path = os.path.join(self.data_path, class_name) - for fname in os.listdir(class_path): - if fname.endswith('.avi'): - item = (os.path.join(class_path, fname), self.class_to_idx[class_name]) - dataset.append(item) - return dataset - - def __len__(self): - return len(self.samples) - - def __getitem__(self, idx): - video_path, label = self.samples[idx] - try: - video = self.tv_read(video_path) - video = self.transform(video) # T C H W -> T C H W - video = video.transpose(0, 1) # T C H W -> C T H W - return video, label - except Exception as e: - print(f'Error with {e}, {video_path}') - return self.__getitem__(random.randint(0, self.__len__()-1)) - - def tv_read(self, path): - vframes, aframes, info = torchvision.io.read_video(filename=path, pts_unit='sec', output_format='TCHW') - total_frames = len(vframes) - - # Sampling video frames - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - # assert end_frame_ind - start_frame_ind >= self.num_frames - frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - video = vframes[frame_indice] # (T, C, H, W) - - return video - - def decord_read(self, path): - decord_vr = self.v_decoder(path) - total_frames = len(decord_vr) - # Sampling video frames - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - # assert end_frame_ind - start_frame_ind >= self.num_frames - frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - - video_data = decord_vr.get_batch(frame_indice).asnumpy() - video_data = torch.from_numpy(video_data) - video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) - return video_data - diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index 340acb6dc..78f8b289d 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -12,6 +12,7 @@ from diffusers.configuration_utils import ConfigMixin, register_to_config from diffusers.models.modeling_utils import ModelMixin from diffusers.models.lora import LoRACompatibleConv, LoRACompatibleLinear +from opensora.utils.utils import to_2tuple import torch import torch.nn.functional as F @@ -56,7 +57,6 @@ class LatteT2V(ModelMixin, ConfigMixin): def __init__( self, num_attention_heads: int = 16, - patch_size_t: int = 1, attention_head_dim: int = 88, in_channels: Optional[int] = None, out_channels: Optional[int] = None, @@ -66,8 +66,10 @@ def __init__( cross_attention_dim: Optional[int] = None, attention_bias: bool = False, sample_size: Optional[int] = None, + sample_size_t: Optional[int] = None, num_vector_embeds: Optional[int] = None, patch_size: Optional[int] = None, + patch_size_t: Optional[int] = None, activation_fn: str = "geglu", num_embeds_ada_norm: Optional[int] = None, use_linear_projection: bool = False, @@ -79,19 +81,23 @@ def __init__( norm_eps: float = 1e-5, attention_type: str = "default", caption_channels: int = None, - video_length: int = 16, attention_mode: str = 'flash', use_rope: bool = False, model_max_length: int = 300, rope_scaling_type: str = 'linear', compress_kv_factor: int = 1, + interpolation_scale_h: float = None, + interpolation_scale_w: float = None, + interpolation_scale_t: float = None, ): super().__init__() + self.interpolation_scale_t = interpolation_scale_t + self.interpolation_scale_h = interpolation_scale_h + self.interpolation_scale_w = interpolation_scale_w self.use_linear_projection = use_linear_projection self.num_attention_heads = num_attention_heads self.attention_head_dim = attention_head_dim inner_dim = num_attention_heads * attention_head_dim - self.video_length = video_length self.use_rope = use_rope self.model_max_length = model_max_length self.compress_kv_factor = compress_kv_factor @@ -127,34 +133,44 @@ def __init__( self.height = sample_size[0] self.width = sample_size[1] - self.patch_size = patch_size - interpolation_scale_2d = self.config.sample_size[0] // 64 # => 64 (= 512 pixart) has interpolation scale 1 - interpolation_scale_2d = max(interpolation_scale_2d, 1) + self.num_frames = self.config.sample_size_t + self.config.sample_size = to_2tuple(self.config.sample_size) + self.height = self.config.sample_size[0] + self.width = self.config.sample_size[1] + self.patch_size_t = self.config.patch_size_t + self.patch_size = self.config.patch_size + interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 + interpolation_scale_t = ( + self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t + ) + interpolation_scale = ( + self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 30, + self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 40, + ) self.pos_embed = PatchEmbed( height=sample_size[0], width=sample_size[1], patch_size=patch_size, in_channels=in_channels, embed_dim=inner_dim, - interpolation_scale=interpolation_scale_2d, + interpolation_scale=interpolation_scale, ) - - # define temporal positional embedding - if self.config.video_length % 2 == 1: - interpolation_scale_1d = (self.config.video_length - 1) // 16 # => 16 (= 16 Latte) has interpolation scale 1 - else: - interpolation_scale_1d = self.config.video_length // 16 # => 16 (= 16 Latte) has interpolation scale 1 - # interpolation_scale_1d = self.config.video_length // 5 # - interpolation_scale_1d = max(interpolation_scale_1d, 1) - temp_pos_embed = get_1d_sincos_pos_embed(inner_dim, video_length, interpolation_scale=interpolation_scale_1d) # 1152 hidden size + # # define temporal positional embedding + # if self.config.video_length % 2 == 1: + # interpolation_scale_1d = (self.config.video_length - 1) // 16 # => 16 (= 16 Latte) has interpolation scale 1 + # else: + # interpolation_scale_1d = self.config.video_length // 16 # => 16 (= 16 Latte) has interpolation scale 1 + # # interpolation_scale_1d = self.config.video_length // 5 # + # interpolation_scale_1d = max(interpolation_scale_1d, 1) + temp_pos_embed = get_1d_sincos_pos_embed(inner_dim, self.config.sample_size_t, interpolation_scale=interpolation_scale_t) # 1152 hidden size self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) rope_scaling = None if self.use_rope: self.position_getter_2d = PositionGetter2D() self.position_getter_1d = PositionGetter1D() - rope_scaling = dict(type=rope_scaling_type, factor_2d=interpolation_scale_2d, factor_1d=interpolation_scale_1d) + rope_scaling = dict(type=rope_scaling_type, factor_2d=interpolation_scale, factor_1d=interpolation_scale_t) # 3. Define transformers blocks, spatial attention self.transformer_blocks = nn.ModuleList( @@ -338,6 +354,7 @@ def forward( # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + # import ipdb;ipdb.set_trace() if attention_mask is None: attention_mask = torch.ones((input_batch_size, frame+use_image_num, h, w), device=hidden_states.device, dtype=hidden_states.dtype) attention_mask = self.vae_to_diff_mask(attention_mask, use_image_num) @@ -399,7 +416,7 @@ def forward( encoder_hidden_states = torch.cat([encoder_hidden_states_video, encoder_hidden_states_image], dim=1) encoder_hidden_states_spatial = rearrange(encoder_hidden_states, 'b f t d -> (b f) t d').contiguous() else: - encoder_hidden_states_spatial = repeat(encoder_hidden_states, 'b t d -> (b f) t d', f=frame).contiguous() + encoder_hidden_states_spatial = repeat(encoder_hidden_states, 'b 1 t d -> (b f) t d', f=frame).contiguous() # prepare timesteps for spatial and temporal block timestep_spatial = repeat(timestep, 'b d -> (b f) d', f=frame + use_image_num).contiguous() @@ -593,13 +610,19 @@ def from_pretrained_2d(cls, pretrained_model_path, subfolder=None, **kwargs): def LatteT2V_XL_122(**kwargs): return LatteT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) -def LatteT2V_D64_XL_122(**kwargs): - return LatteT2V(num_layers=28, attention_head_dim=64, num_attention_heads=18, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + +# def LatteT2V_D64_XL_122(**kwargs): +# return LatteT2V(num_layers=28, attention_head_dim=64, num_attention_heads=18, patch_size_t=1, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + +def LatteT2V_122(**kwargs): + return LatteT2V(num_layers=28, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) Latte_models = { "LatteT2V-XL/122": LatteT2V_XL_122, - "LatteT2V-D64-XL/122": LatteT2V_D64_XL_122, + # "LatteT2V-D64-XL/122": LatteT2V_D64_XL_122, + "LatteT2V/122": LatteT2V_122, } if __name__ == '__main__': diff --git a/opensora/models/diffusion/latte/modules.py b/opensora/models/diffusion/latte/modules.py index 224a0922d..95ea5387d 100644 --- a/opensora/models/diffusion/latte/modules.py +++ b/opensora/models/diffusion/latte/modules.py @@ -20,7 +20,7 @@ from dataclasses import dataclass -from opensora.models.diffusion.utils.pos_embed import get_2d_sincos_pos_embed, RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D +from opensora.models.diffusion.utils.pos_embed import RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D if is_xformers_available(): import xformers @@ -29,6 +29,75 @@ xformers = None +def get_2d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid height and width return: pos_embed: [grid_size*grid_size, embed_dim] or + [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid_h = np.arange(grid_size[0], dtype=np.float32) / (grid_size[0] / base_size[0]) / interpolation_scale[0] + grid_w = np.arange(grid_size[1], dtype=np.float32) / (grid_size[1] / base_size[1]) / interpolation_scale[1] + grid = np.meshgrid(grid_w, grid_h) # here w goes first + grid = np.stack(grid, axis=0) + + grid = grid.reshape([2, 1, grid_size[1], grid_size[0]]) + pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + + +def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + # use 1/3 of dimensions to encode grid_t/h/w + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2) + + emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) + return emb + +def get_1d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid return: pos_embed: [grid_size, embed_dim] or + [1+grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid = np.arange(grid_size, dtype=np.float32) / (grid_size / base_size) / interpolation_scale + pos_embed = get_1d_sincos_pos_embed_from_grid(embed_dim, grid) # (H*W, D/2) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + +def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): + """ + embed_dim: output dimension for each position pos: a list of positions to be encoded: size (M,) out: (M, D) + """ + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + omega = np.arange(embed_dim // 2, dtype=np.float64) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + + pos = pos.reshape(-1) # (M,) + out = np.einsum("m,d->md", pos, omega) # (M, D/2), outer product + + emb_sin = np.sin(out) # (M, D/2) + emb_cos = np.cos(out) # (M, D/2) + + emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) + return emb + class CombinedTimestepSizeEmbeddings(nn.Module): """ For PixArt-Alpha. @@ -137,10 +206,10 @@ def __init__( # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 self.height, self.width = height // patch_size, width // patch_size - self.base_size = height // patch_size + self.base_size = (height // patch_size, width // patch_size) self.interpolation_scale = interpolation_scale pos_embed = get_2d_sincos_pos_embed( - embed_dim, int(num_patches**0.5), base_size=self.base_size, interpolation_scale=self.interpolation_scale + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale ) self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index bb601f1a6..65563ff41 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -72,7 +72,8 @@ def __init__( norm_eps: float = 1e-5, attention_type: str = "default", caption_channels: int = None, - interpolation_scale: float = None, + interpolation_scale_h: float = None, + interpolation_scale_w: float = None, interpolation_scale_t: float = None, use_additional_conditions: Optional[bool] = None, attention_mode: str = 'xformers', @@ -92,7 +93,9 @@ def __init__( # Set some common variables used across the board. self.use_linear_projection = use_linear_projection - self.interpolation_scale = interpolation_scale + self.interpolation_scale_t = interpolation_scale_t + self.interpolation_scale_h = interpolation_scale_h + self.interpolation_scale_w = interpolation_scale_w self.caption_channels = caption_channels self.num_attention_heads = num_attention_heads self.attention_head_dim = attention_head_dim @@ -138,13 +141,13 @@ def _init_patched_inputs(self, norm_type): self.width = self.config.sample_size[1] self.patch_size_t = self.config.patch_size_t self.patch_size = self.config.patch_size - sample_size_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 + interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 interpolation_scale_t = ( - self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else sample_size_t + self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t ) interpolation_scale = ( - self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[0] / 64, - self.config.interpolation_scale if self.config.interpolation_scale is not None else self.config.sample_size[1] / 64, + self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 30, + self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 40, ) # if self.config.sample_size_t > 1: # self.pos_embed = PatchEmbed3D( @@ -522,8 +525,8 @@ def _get_output_for_patched_inputs( shape=(-1, self.out_channels, num_frames * self.patch_size_t, height * self.patch_size, width * self.patch_size) ) # import ipdb;ipdb.set_trace() - if output.shape[2] % 2 == 0: - output = output[:, :, 1:] + # if output.shape[2] % 2 == 0: + # output = output[:, :, 1:] return output @@ -531,38 +534,42 @@ def OpenSoraT2V_S_122(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) -def OpenSoraT2V_S_222(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) +def OpenSoraT2V_B_122(**kwargs): + return OpenSoraT2V(num_layers=48, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) +# def OpenSoraT2V_S_222(**kwargs): +# return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) -def OpenSoraT2V_B_222(**kwargs): - return OpenSoraT2V(num_layers=24, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) -def OpenSoraT2V_L_222(**kwargs): - return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) +# def OpenSoraT2V_B_222(**kwargs): +# return OpenSoraT2V(num_layers=24, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + +# def OpenSoraT2V_L_222(**kwargs): +# return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) -def OpenSoraT2V_XL_222(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) +# def OpenSoraT2V_XL_222(**kwargs): +# return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) -def OpenSoraT2V_XXL_222(**kwargs): - return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) +# def OpenSoraT2V_XXL_222(**kwargs): +# return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) OpenSora_models = { "OpenSoraT2V-S/122": OpenSoraT2V_S_122, - # "OpenSoraT2V-B/122": OpenSoraT2V_B_122, + "OpenSoraT2V-B/122": OpenSoraT2V_B_122, # "OpenSoraT2V-L/122": OpenSoraT2V_L_122, # "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, # "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, - "OpenSoraT2V-S/222": OpenSoraT2V_S_222, - "OpenSoraT2V-B/222": OpenSoraT2V_B_222, - "OpenSoraT2V-L/222": OpenSoraT2V_L_222, - "OpenSoraT2V-XL/222": OpenSoraT2V_XL_222, - "OpenSoraT2V-XXL/222": OpenSoraT2V_XXL_222, + # "OpenSoraT2V-S/222": OpenSoraT2V_S_222, + # "OpenSoraT2V-B/222": OpenSoraT2V_B_222, + # "OpenSoraT2V-L/222": OpenSoraT2V_L_222, + # "OpenSoraT2V-XL/222": OpenSoraT2V_XL_222, + # "OpenSoraT2V-XXL/222": OpenSoraT2V_XXL_222, } if __name__ == '__main__': diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 697c9a47d..1cc612752 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -432,9 +432,9 @@ def __call__( # the output of sdp = (batch, num_heads, seq_len, head_dim) # TODO: add support for attn.scale when we move to Torch 2.1 # import ipdb;ipdb.set_trace() - # print(query.shape) + # print(attention_mask) if self.attention_mode == 'flash': - assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): hidden_states = F.scaled_dot_product_attention( query, key, value, dropout_p=0.0, is_causal=False diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 36bb38325..cc39b0329 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -10,8 +10,8 @@ def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = T5EncoderModel.from_pretrained(r"/remote-home1/yeyang/5.25dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() - + self.text_enc = T5EncoderModel.from_pretrained("/storage/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() + def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] return text_encoder_embs.detach() diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index f82fab35a..f609c6129 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -240,12 +240,15 @@ def main(args): norm_eps=1e-6, attention_type='default', attention_mode=args.attention_mode, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + interpolation_scale_t=args.interpolation_scale_t, # compress_kv_factor=args.compress_kv_factor, # use_rope=args.use_rope, # model_max_length=args.model_max_length, ) model.gradient_checkpointing = args.gradient_checkpointing - + # # use pretrained model? if args.pretrained: if 'safetensors' in args.pretrained: # pixart series @@ -615,12 +618,12 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--video_data", type=str, required='') parser.add_argument("--image_data", type=str, default='') parser.add_argument("--sample_rate", type=int, default=1) - parser.add_argument("--num_frames", type=int, default=17) - parser.add_argument("--max_height", type=int, default=512) - parser.add_argument("--max_width", type=int, default=512) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) parser.add_argument("--use_img_from_vid", action="store_true") parser.add_argument("--use_image_num", type=int, default=0) - parser.add_argument("--model_max_length", type=int, default=300) + parser.add_argument("--model_max_length", type=int, default=512) parser.add_argument('--enable_8bit_t5', action='store_true') parser.add_argument('--tile_overlap_factor', type=float, default=0.25) @@ -629,6 +632,9 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") parser.add_argument('--use_rope', action='store_true') parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") parser.add_argument("--pretrained", type=str, default=None) diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 831b2ed6b..34e9e1a9d 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -44,7 +44,8 @@ def pad_to_multiple(number, ds_stride): class Collate: def __init__(self, args): - self.max_image_size = args.max_image_size + self.max_height = args.max_height + self.max_width = args.max_width self.ae_stride = args.ae_stride self.ae_stride_t = args.ae_stride_t self.ae_stride_thw = (self.ae_stride_t, self.ae_stride, self.ae_stride) @@ -57,8 +58,8 @@ def __init__(self, args): self.num_frames = args.num_frames self.use_image_num = args.use_image_num - self.max_thw = (self.num_frames, self.max_image_size, self.max_image_size) - self.max_1hw = (1, self.max_image_size, self.max_image_size) + self.max_thw = (self.num_frames, self.max_height, self.max_width) + self.max_1hw = (1, self.max_height, self.max_width) def package(self, batch): diff --git a/scripts/text_condition/train_video_65x512x512.sh b/scripts/text_condition/train_video21d_65x240p.sh similarity index 56% rename from scripts/text_condition/train_video_65x512x512.sh rename to scripts/text_condition/train_video21d_65x240p.sh index 4f9a6792b..1c315ad1a 100644 --- a/scripts/text_condition/train_video_65x512x512.sh +++ b/scripts/text_condition/train_video21d_65x240p.sh @@ -1,26 +1,37 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" export PROJECT="debug" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 -# 65x256x256_bs2x80_4img_lr2e-5_488vae_3dattn_xformers +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v.py \ - --model OpenSoraT2V-S/122 \ + --model LatteT2V-XL/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/remote-home1/yeyang/CausalVAEModel_4x8x8/" \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ --num_frames 65 \ - --max_image_size 256 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ --attention_mode xformers \ - --train_batch_size=2 \ + --gradient_checkpointing \ + --train_batch_size=4 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -28,7 +39,7 @@ accelerate launch \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="wandb" \ + --report_to="tensorboard" \ --checkpointing_steps=100 \ --output_dir="debug" \ --allow_tf32 \ @@ -36,7 +47,5 @@ accelerate launch \ --model_max_length 300 \ --use_image_num 0 \ --enable_tiling \ - --pretrained PixArt-Alpha-XL-2-512.safetensors \ --enable_tracker \ - --resume_from_checkpoint "latest" \ - --snr_gamma 5.0 + --snr_gamma 5.0 \ No newline at end of file diff --git a/scripts/text_condition/train_video3d_65x240p.sh b/scripts/text_condition/train_video3d_65x240p.sh new file mode 100644 index 000000000..e989a82c0 --- /dev/null +++ b/scripts/text_condition/train_video3d_65x240p.sh @@ -0,0 +1,51 @@ +# export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="debug" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-B/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ + --sample_rate 1 \ + --num_frames 65 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=4 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="tensorboard" \ + --checkpointing_steps=100 \ + --output_dir="debug" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tiling \ + --enable_tracker \ + --snr_gamma 5.0 diff --git a/scripts/train_data/image_data_debug.txt b/scripts/train_data/image_data_debug.txt index 3d601e8b5..bbf00d70f 100644 --- a/scripts/train_data/image_data_debug.txt +++ b/scripts/train_data/image_data_debug.txt @@ -1 +1 @@ -/dxyl_data02/datasets/images,/dxyl_data02/anno_jsons/human_images_162094.json \ No newline at end of file +/dxyl_data02/datasets/images,/storage/anno_jsons/human_images_162094.json \ No newline at end of file diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt index 6223c6714..0be549e08 100644 --- a/scripts/train_data/video_data_debug.txt +++ b/scripts/train_data/video_data_debug.txt @@ -1 +1 @@ -/remote-home1/dataset/all_mixkit,/remote-home1/dataset/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/remote-home1/dataset/all_mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file From 1b31355415410a40c7b0266d8fd07d66ed69cca1 Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Wed, 29 May 2024 22:53:57 +0800 Subject: [PATCH 014/134] remove rand --- opensora/dataset/t2v_datasets.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 3a3a6889c..fec9e810b 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -72,17 +72,17 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - # video_path = self.vid_cap_list[idx]['path'] - # frame_idx = self.vid_cap_list[idx]['frame_idx'] - # video = self.decord_read(video_path, frame_idx) - - # h, w = video.shape[-2:] - # assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' - # t = video.shape[0] - # video = video[:(t - 1) // 4 * 4 + 1] - # video = self.transform(video) # T C H W -> T C H W + video_path = self.vid_cap_list[idx]['path'] + frame_idx = self.vid_cap_list[idx]['frame_idx'] + video = self.decord_read(video_path, frame_idx) + + h, w = video.shape[-2:] + assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' + t = video.shape[0] + video = video[:(t - 1) // 4 * 4 + 1] + video = self.transform(video) # T C H W -> T C H W - video = torch.rand(65, 3, 240, 320) + # video = torch.rand(65, 3, 240, 320) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] @@ -113,12 +113,12 @@ def get_image(self, idx): idx = idx % len(self.img_cap_list) # out of range image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - # image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] - # image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] - # image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] - # image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] - image = [torch.rand(1, 3, 512, 512) for i in image_data] + # image = [torch.rand(1, 3, 512, 512) for i in image_data] image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] caps = [i['cap'] for i in image_data] From c2ad2c0705ba7dde0c33b500c4c52dcc13867535 Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Thu, 30 May 2024 15:51:44 +0800 Subject: [PATCH 015/134] fix ema --- opensora/dataset/t2v_datasets.py | 20 +++++++++---------- opensora/models/diffusion/__init__.py | 5 +++++ .../models/diffusion/latte/modeling_latte.py | 6 ++++++ .../diffusion/opensora/modeling_opensora.py | 5 +++++ opensora/train/train_t2v.py | 20 ++++++++++++++----- .../text_condition/train_video21d_65x240p.sh | 10 ++++++---- 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index fec9e810b..094096690 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -72,17 +72,17 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = self.vid_cap_list[idx]['path'] - frame_idx = self.vid_cap_list[idx]['frame_idx'] - video = self.decord_read(video_path, frame_idx) - - h, w = video.shape[-2:] - assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' - t = video.shape[0] - video = video[:(t - 1) // 4 * 4 + 1] - video = self.transform(video) # T C H W -> T C H W + # video_path = self.vid_cap_list[idx]['path'] + # frame_idx = self.vid_cap_list[idx]['frame_idx'] + # video = self.decord_read(video_path, frame_idx) + + # h, w = video.shape[-2:] + # assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' + # t = video.shape[0] + # video = video[:(t - 1) // 4 * 4 + 1] + # video = self.transform(video) # T C H W -> T C H W - # video = torch.rand(65, 3, 240, 320) + video = torch.rand(65, 3, 240, 320) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index a68cd3d9a..ed15d61e4 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -3,5 +3,10 @@ Diffusion_models = {} Diffusion_models.update(Latte_models) Diffusion_models.update(OpenSora_models) +from .latte.modeling_latte import Latte_models_class +from .opensora.modeling_opensora import OpenSora_models_class +Diffusion_models_class = {} +Diffusion_models_class.update(Latte_models_class) +Diffusion_models_class.update(OpenSora_models_class) \ No newline at end of file diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index 78f8b289d..b62bdd9d4 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -625,6 +625,12 @@ def LatteT2V_122(**kwargs): "LatteT2V/122": LatteT2V_122, } +Latte_models_class = { + "LatteT2V-XL/122": LatteT2V, + # "LatteT2V-D64-XL/122": LatteT2V_D64_XL_122, + "LatteT2V/122": LatteT2V, +} + if __name__ == '__main__': from opensora.models.ae import ae_channel_config, ae_stride_config from opensora.models.ae import getae, getae_wrapper diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 65563ff41..f31c88f6f 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -572,6 +572,11 @@ def OpenSoraT2V_B_122(**kwargs): # "OpenSoraT2V-XXL/222": OpenSoraT2V_XXL_222, } +OpenSora_models_class = { + "OpenSoraT2V-S/122": OpenSoraT2V, + "OpenSoraT2V-B/122": OpenSoraT2V, +} + if __name__ == '__main__': from opensora.models.ae import ae_channel_config, ae_stride_config from opensora.models.ae import getae, getae_wrapper diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index f609c6129..084c12dd5 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -48,7 +48,7 @@ from opensora.models.text_encoder import get_text_enc, get_text_warpper from opensora.utils.dataset_utils import Collate from opensora.models.ae import ae_stride_config, ae_channel_config -from opensora.models.diffusion import Diffusion_models +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class from opensora.sample.pipeline_opensora import OpenSoraPipeline # Will error if the minimal version of diffusers is not installed. Remove at your own risks. @@ -248,7 +248,7 @@ def main(args): # model_max_length=args.model_max_length, ) model.gradient_checkpointing = args.gradient_checkpointing - + # # use pretrained model? if args.pretrained: if 'safetensors' in args.pretrained: # pixart series @@ -285,7 +285,8 @@ def main(args): # Create EMA for the unet. if args.use_ema: ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), model_cls=Diffusion_models[args.model], model_config=ema_model.config) + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) # `accelerate` 0.16.0 will have better support for customized saving if version.parse(accelerate.__version__) >= version.parse("0.16.0"): @@ -303,7 +304,7 @@ def save_model_hook(models, weights, output_dir): def load_model_hook(models, input_dir): if args.use_ema: - load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), LatteT2V) + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) ema_model.load_state_dict(load_model.state_dict()) ema_model.to(accelerator.device) del load_model @@ -313,7 +314,7 @@ def load_model_hook(models, input_dir): model = models.pop() # load diffusers style into model - load_model = Diffusion_models[args.model].from_pretrained(input_dir, subfolder="model") + load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") model.register_to_config(**load_model.config) model.load_state_dict(load_model.state_dict()) @@ -383,6 +384,8 @@ def load_model_hook(models, input_dir): model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( model, optimizer, train_dataloader, lr_scheduler ) + if args.use_ema: + ema_model.to(accelerator.device) # We need to recalculate our total training steps as the size of the training dataloader may have changed. num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) @@ -552,6 +555,8 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu # Checks if the accelerator has performed an optimization step behind the scenes if accelerator.sync_gradients: + if args.use_ema: + ema_model.step(model.parameters()) progress_bar.update(1) global_step += 1 accelerator.log({"train_loss": train_loss}, step=global_step) @@ -608,6 +613,10 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu if args.enable_tracker: log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step) + if args.use_ema: + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + accelerator.wait_for_everyone() accelerator.end_training() @@ -635,6 +644,7 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu parser.add_argument('--interpolation_scale_h', type=float, default=1.0) parser.add_argument('--interpolation_scale_w', type=float, default=1.0) parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument("--ema_start_step", type=int, default=0) parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") parser.add_argument("--pretrained", type=str, default=None) diff --git a/scripts/text_condition/train_video21d_65x240p.sh b/scripts/text_condition/train_video21d_65x240p.sh index 1c315ad1a..2484bf811 100644 --- a/scripts/text_condition/train_video21d_65x240p.sh +++ b/scripts/text_condition/train_video21d_65x240p.sh @@ -12,7 +12,7 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/debug.yaml \ opensora/train/train_t2v.py \ --model LatteT2V-XL/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -32,7 +32,7 @@ accelerate launch \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=4 \ - --dataloader_num_workers 10 \ + --dataloader_num_workers 0 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=2e-5 \ @@ -40,7 +40,7 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="tensorboard" \ - --checkpointing_steps=100 \ + --checkpointing_steps=1 \ --output_dir="debug" \ --allow_tf32 \ --use_deepspeed \ @@ -48,4 +48,6 @@ accelerate launch \ --use_image_num 0 \ --enable_tiling \ --enable_tracker \ - --snr_gamma 5.0 \ No newline at end of file + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ No newline at end of file From cac33a4344913949d152739e97e295ce404f956e Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Thu, 30 May 2024 22:54:52 +0800 Subject: [PATCH 016/134] fix ema log & test train/test --- opensora/dataset/t2v_datasets.py | 22 +- .../diffusion/opensora/modeling_opensora.py | 6 +- opensora/sample/pipeline_videogen.py | 759 ------------------ opensora/sample/sample_t2v.py | 116 ++- opensora/sample/sample_t2v_pixsig.py | 174 ---- opensora/train/train_t2v.py | 30 +- scripts/accelerate_configs/hostfile | 12 +- scripts/accelerate_configs/hostfile1 | 2 + .../multi_node_example.yaml | 6 +- .../multi_node_example1.yaml | 18 + scripts/text_condition/sample_image.sh | 16 - .../text_condition/train_video21d_65x240p.sh | 19 +- .../text_condition/train_video3d_65x240p.sh | 17 +- scripts/train_data/video_data_debug.txt | 2 +- 14 files changed, 131 insertions(+), 1068 deletions(-) delete mode 100644 opensora/sample/pipeline_videogen.py delete mode 100644 opensora/sample/sample_t2v_pixsig.py create mode 100644 scripts/accelerate_configs/hostfile1 create mode 100644 scripts/accelerate_configs/multi_node_example1.yaml delete mode 100644 scripts/text_condition/sample_image.sh diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 094096690..33f2f4a83 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -63,7 +63,7 @@ def __getitem__(self, idx): image_data = self.get_image(idx) # 1 frame video as image return dict(video_data=video_data, image_data=image_data) except Exception as e: - print(f'Error with {e}') + # print(f'Error with {e}') return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): @@ -72,17 +72,17 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - # video_path = self.vid_cap_list[idx]['path'] - # frame_idx = self.vid_cap_list[idx]['frame_idx'] - # video = self.decord_read(video_path, frame_idx) - - # h, w = video.shape[-2:] - # assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' - # t = video.shape[0] - # video = video[:(t - 1) // 4 * 4 + 1] - # video = self.transform(video) # T C H W -> T C H W + video_path = self.vid_cap_list[idx]['path'] + frame_idx = self.vid_cap_list[idx]['frame_idx'] + video = self.decord_read(video_path, frame_idx) + + h, w = video.shape[-2:] + assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' + t = video.shape[0] + video = video[:(t - 1) // 4 * 4 + 1] + video = self.transform(video) # T C H W -> T C H W - video = torch.rand(65, 3, 240, 320) + # video = torch.rand(65, 3, 240, 320) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index f31c88f6f..eaf5846d8 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -131,8 +131,8 @@ def __init__( self._init_patched_inputs(norm_type=norm_type) def _init_patched_inputs(self, norm_type): - assert self.config.sample_size_t is not None, "Transformer3DModel over patched input must provide sample_size_t" - assert self.config.sample_size is not None, "Transformer3DModel over patched input must provide sample_size" + assert self.config.sample_size_t is not None, "OpenSoraT2V over patched input must provide sample_size_t" + assert self.config.sample_size is not None, "OpenSoraT2V over patched input must provide sample_size" #assert not (self.config.sample_size_t == 1 and self.config.patch_size_t == 2), "Image do not need patchfy in t-dim" self.num_frames = self.config.sample_size_t @@ -535,7 +535,7 @@ def OpenSoraT2V_S_122(**kwargs): norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_122(**kwargs): - return OpenSoraT2V(num_layers=48, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) # def OpenSoraT2V_S_222(**kwargs): diff --git a/opensora/sample/pipeline_videogen.py b/opensora/sample/pipeline_videogen.py deleted file mode 100644 index 303414b8c..000000000 --- a/opensora/sample/pipeline_videogen.py +++ /dev/null @@ -1,759 +0,0 @@ -# All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import html -import inspect -import re -import urllib.parse as ul -from typing import Callable, List, Optional, Tuple, Union - -import torch -import einops -from einops import rearrange -from transformers import T5EncoderModel, T5Tokenizer - -from diffusers.image_processor import VaeImageProcessor -from diffusers.models import AutoencoderKL, Transformer2DModel -from diffusers.schedulers import DPMSolverMultistepScheduler -from diffusers.utils import ( - BACKENDS_MAPPING, - is_bs4_available, - is_ftfy_available, - logging, - replace_example_docstring, -) -from diffusers.utils.torch_utils import randn_tensor -from diffusers.pipelines.pipeline_utils import DiffusionPipeline -from diffusers.utils import BaseOutput -from dataclasses import dataclass - -logger = logging.get_logger(__name__) # pylint: disable=invalid-name - -if is_bs4_available(): - from bs4 import BeautifulSoup - -if is_ftfy_available(): - import ftfy - -EXAMPLE_DOC_STRING = """ - Examples: - ```py - >>> import torch - >>> from diffusers import PixArtAlphaPipeline - - >>> # You can replace the checkpoint id with "PixArt-alpha/PixArt-XL-2-512x512" too. - >>> pipe = PixArtAlphaPipeline.from_pretrained("PixArt-alpha/PixArt-XL-2-1024-MS", torch_dtype=torch.float16) - >>> # Enable memory optimizations. - >>> pipe.enable_model_cpu_offload() - - >>> prompt = "A small cactus with a happy face in the Sahara desert." - >>> image = pipe(prompt).images[0] - ``` -""" - - -@dataclass -class VideoPipelineOutput(BaseOutput): - video: torch.Tensor - - -class VideoGenPipeline(DiffusionPipeline): - r""" - Pipeline for text-to-image generation using PixArt-Alpha. - - This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods the - library implements for all the pipelines (such as downloading or saving, running on a particular device, etc.) - - Args: - vae ([`AutoencoderKL`]): - Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations. - text_encoder ([`T5EncoderModel`]): - Frozen text-encoder. PixArt-Alpha uses - [T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel), specifically the - [t5-v1_1-xxl](https://huggingface.co/PixArt-alpha/PixArt-alpha/tree/main/t5-v1_1-xxl) variant. - tokenizer (`T5Tokenizer`): - Tokenizer of class - [T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer). - transformer ([`Transformer2DModel`]): - A text conditioned `Transformer2DModel` to denoise the encoded image latents. - scheduler ([`SchedulerMixin`]): - A scheduler to be used in combination with `transformer` to denoise the encoded image latents. - """ - bad_punct_regex = re.compile( - r"[" + "#®•©™&@·º½¾¿¡§~" + "\)" + "\(" + "\]" + "\[" + "\}" + "\{" + "\|" + "\\" + "\/" + "\*" + r"]{1,}" - ) # noqa - - _optional_components = ["tokenizer", "text_encoder"] - model_cpu_offload_seq = "text_encoder->transformer->vae" - - def __init__( - self, - tokenizer: T5Tokenizer, - text_encoder: T5EncoderModel, - vae: AutoencoderKL, - transformer: Transformer2DModel, - scheduler: DPMSolverMultistepScheduler, - ): - super().__init__() - - self.register_modules( - tokenizer=tokenizer, text_encoder=text_encoder, vae=vae, transformer=transformer, scheduler=scheduler - ) - - # self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) - - # Adapted from https://github.com/PixArt-alpha/PixArt-alpha/blob/master/diffusion/model/utils.py - def mask_text_embeddings(self, emb, mask): - if emb.shape[0] == 1: - keep_index = mask.sum().item() - return emb[:, :, :keep_index, :], keep_index # 1, 120, 4096 -> 1 7 4096 - else: - masked_feature = emb * mask[:, None, :, None] # 1 120 4096 - return masked_feature, emb.shape[2] - - # Adapted from diffusers.pipelines.deepfloyd_if.pipeline_if.encode_prompt - def encode_prompt( - self, - prompt: Union[str, List[str]], - do_classifier_free_guidance: bool = True, - negative_prompt: str = "", - num_images_per_prompt: int = 1, - device: Optional[torch.device] = None, - prompt_embeds: Optional[torch.FloatTensor] = None, - negative_prompt_embeds: Optional[torch.FloatTensor] = None, - clean_caption: bool = False, - mask_feature: bool = True, - ): - r""" - Encodes the prompt into text encoder hidden states. - - Args: - prompt (`str` or `List[str]`, *optional*): - prompt to be encoded - negative_prompt (`str` or `List[str]`, *optional*): - The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds` - instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`). For - PixArt-Alpha, this should be "". - do_classifier_free_guidance (`bool`, *optional*, defaults to `True`): - whether to use classifier free guidance or not - num_images_per_prompt (`int`, *optional*, defaults to 1): - number of images that should be generated per prompt - device: (`torch.device`, *optional*): - torch device to place the resulting embeddings on - prompt_embeds (`torch.FloatTensor`, *optional*): - Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not - provided, text embeddings will be generated from `prompt` input argument. - negative_prompt_embeds (`torch.FloatTensor`, *optional*): - Pre-generated negative text embeddings. For PixArt-Alpha, it's should be the embeddings of the "" - string. - clean_caption (bool, defaults to `False`): - If `True`, the function will preprocess and clean the provided caption before encoding. - mask_feature: (bool, defaults to `True`): - If `True`, the function will mask the text embeddings. - """ - embeds_initially_provided = prompt_embeds is not None and negative_prompt_embeds is not None - - if device is None: - device = self.text_encoder.device or self._execution_device - - if prompt is not None and isinstance(prompt, str): - batch_size = 1 - elif prompt is not None and isinstance(prompt, list): - batch_size = len(prompt) - else: - batch_size = prompt_embeds.shape[0] - - # See Section 3.1. of the paper. - max_length = 300 - - if prompt_embeds is None: - prompt = self._text_preprocessing(prompt, clean_caption=clean_caption) - text_inputs = self.tokenizer( - prompt, - padding="max_length", - max_length=max_length, - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors="pt", - ) - text_input_ids = text_inputs.input_ids - untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids - - if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal( - text_input_ids, untruncated_ids - ): - removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_length - 1: -1]) - logger.warning( - "The following part of your input was truncated because the model can only handle sequences up to" - f" {max_length} tokens: {removed_text}" - ) - - attention_mask = text_inputs.attention_mask.to(device) - prompt_embeds_attention_mask = attention_mask - - prompt_embeds = self.text_encoder(text_input_ids.to(device), attention_mask=attention_mask) - prompt_embeds = prompt_embeds[0] - else: - prompt_embeds_attention_mask = torch.ones_like(prompt_embeds) - - if self.text_encoder is not None: - dtype = self.text_encoder.dtype - elif self.transformer is not None: - dtype = self.transformer.dtype - else: - dtype = None - - prompt_embeds = prompt_embeds.to(dtype=dtype, device=device) - - bs_embed, seq_len, _ = prompt_embeds.shape - # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method - prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1) - prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1) - prompt_embeds_attention_mask = prompt_embeds_attention_mask.view(bs_embed, -1) - prompt_embeds_attention_mask = prompt_embeds_attention_mask.repeat(num_images_per_prompt, 1) - - # get unconditional embeddings for classifier free guidance - if do_classifier_free_guidance and negative_prompt_embeds is None: - uncond_tokens = [negative_prompt] * batch_size - uncond_tokens = self._text_preprocessing(uncond_tokens, clean_caption=clean_caption) - max_length = prompt_embeds.shape[1] - uncond_input = self.tokenizer( - uncond_tokens, - padding="max_length", - max_length=max_length, - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors="pt", - ) - attention_mask = uncond_input.attention_mask.to(device) - - negative_prompt_embeds = self.text_encoder( - uncond_input.input_ids.to(device), - attention_mask=attention_mask, - ) - negative_prompt_embeds = negative_prompt_embeds[0] - - if do_classifier_free_guidance: - # duplicate unconditional embeddings for each generation per prompt, using mps friendly method - seq_len = negative_prompt_embeds.shape[1] - - negative_prompt_embeds = negative_prompt_embeds.to(dtype=dtype, device=device) - - negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1) - negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1) - - # For classifier free guidance, we need to do two forward passes. - # Here we concatenate the unconditional and text embeddings into a single batch - # to avoid doing two forward passes - else: - negative_prompt_embeds = None - - # print(prompt_embeds.shape) # 1 120 4096 - # print(negative_prompt_embeds.shape) # 1 120 4096 - - # Perform additional masking. - if mask_feature and not embeds_initially_provided: - prompt_embeds = prompt_embeds.unsqueeze(1) - masked_prompt_embeds, keep_indices = self.mask_text_embeddings(prompt_embeds, prompt_embeds_attention_mask) - masked_prompt_embeds = masked_prompt_embeds.squeeze(1) - masked_negative_prompt_embeds = ( - negative_prompt_embeds[:, :keep_indices, :] if negative_prompt_embeds is not None else None - ) - - # import torch.nn.functional as F - - # padding = (0, 0, 0, 113) # (左, 右, 下, 上) - # masked_prompt_embeds_ = F.pad(masked_prompt_embeds, padding, "constant", 0) - # masked_negative_prompt_embeds_ = F.pad(masked_negative_prompt_embeds, padding, "constant", 0) - - # print(masked_prompt_embeds == masked_prompt_embeds_[:, :masked_negative_prompt_embeds.shape[1], ...]) - - return masked_prompt_embeds, masked_negative_prompt_embeds - # return masked_prompt_embeds_, masked_negative_prompt_embeds_ - - return prompt_embeds, negative_prompt_embeds - - # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs - def prepare_extra_step_kwargs(self, generator, eta): - # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature - # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers. - # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502 - # and should be between [0, 1] - - accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys()) - extra_step_kwargs = {} - if accepts_eta: - extra_step_kwargs["eta"] = eta - - # check if the scheduler accepts generator - accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys()) - if accepts_generator: - extra_step_kwargs["generator"] = generator - return extra_step_kwargs - - def check_inputs( - self, - prompt, - height, - width, - negative_prompt, - callback_steps, - prompt_embeds=None, - negative_prompt_embeds=None, - ): - if height % 8 != 0 or width % 8 != 0: - raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.") - - if (callback_steps is None) or ( - callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0) - ): - raise ValueError( - f"`callback_steps` has to be a positive integer but is {callback_steps} of type" - f" {type(callback_steps)}." - ) - - if prompt is not None and prompt_embeds is not None: - raise ValueError( - f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to" - " only forward one of the two." - ) - elif prompt is None and prompt_embeds is None: - raise ValueError( - "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined." - ) - elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)): - raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}") - - if prompt is not None and negative_prompt_embeds is not None: - raise ValueError( - f"Cannot forward both `prompt`: {prompt} and `negative_prompt_embeds`:" - f" {negative_prompt_embeds}. Please make sure to only forward one of the two." - ) - - if negative_prompt is not None and negative_prompt_embeds is not None: - raise ValueError( - f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:" - f" {negative_prompt_embeds}. Please make sure to only forward one of the two." - ) - - if prompt_embeds is not None and negative_prompt_embeds is not None: - if prompt_embeds.shape != negative_prompt_embeds.shape: - raise ValueError( - "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but" - f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`" - f" {negative_prompt_embeds.shape}." - ) - - # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._text_preprocessing - def _text_preprocessing(self, text, clean_caption=False): - if clean_caption and not is_bs4_available(): - logger.warn(BACKENDS_MAPPING["bs4"][-1].format("Setting `clean_caption=True`")) - logger.warn("Setting `clean_caption` to False...") - clean_caption = False - - if clean_caption and not is_ftfy_available(): - logger.warn(BACKENDS_MAPPING["ftfy"][-1].format("Setting `clean_caption=True`")) - logger.warn("Setting `clean_caption` to False...") - clean_caption = False - - if not isinstance(text, (tuple, list)): - text = [text] - - def process(text: str): - if clean_caption: - text = self._clean_caption(text) - text = self._clean_caption(text) - else: - text = text.lower().strip() - return text - - return [process(t) for t in text] - - # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._clean_caption - def _clean_caption(self, caption): - caption = str(caption) - caption = ul.unquote_plus(caption) - caption = caption.strip().lower() - caption = re.sub("", "person", caption) - # urls: - caption = re.sub( - r"\b((?:https?:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", - # noqa - "", - caption, - ) # regex for urls - caption = re.sub( - r"\b((?:www:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", - # noqa - "", - caption, - ) # regex for urls - # html: - caption = BeautifulSoup(caption, features="html.parser").text - - # @ - caption = re.sub(r"@[\w\d]+\b", "", caption) - - # 31C0—31EF CJK Strokes - # 31F0—31FF Katakana Phonetic Extensions - # 3200—32FF Enclosed CJK Letters and Months - # 3300—33FF CJK Compatibility - # 3400—4DBF CJK Unified Ideographs Extension A - # 4DC0—4DFF Yijing Hexagram Symbols - # 4E00—9FFF CJK Unified Ideographs - caption = re.sub(r"[\u31c0-\u31ef]+", "", caption) - caption = re.sub(r"[\u31f0-\u31ff]+", "", caption) - caption = re.sub(r"[\u3200-\u32ff]+", "", caption) - caption = re.sub(r"[\u3300-\u33ff]+", "", caption) - caption = re.sub(r"[\u3400-\u4dbf]+", "", caption) - caption = re.sub(r"[\u4dc0-\u4dff]+", "", caption) - caption = re.sub(r"[\u4e00-\u9fff]+", "", caption) - ####################################################### - - # все виды тире / all types of dash --> "-" - caption = re.sub( - r"[\u002D\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E3B\u2E40\u301C\u3030\u30A0\uFE31\uFE32\uFE58\uFE63\uFF0D]+", - # noqa - "-", - caption, - ) - - # кавычки к одному стандарту - caption = re.sub(r"[`´«»“”¨]", '"', caption) - caption = re.sub(r"[‘’]", "'", caption) - - # " - caption = re.sub(r""?", "", caption) - # & - caption = re.sub(r"&", "", caption) - - # ip adresses: - caption = re.sub(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", " ", caption) - - # article ids: - caption = re.sub(r"\d:\d\d\s+$", "", caption) - - # \n - caption = re.sub(r"\\n", " ", caption) - - # "#123" - caption = re.sub(r"#\d{1,3}\b", "", caption) - # "#12345.." - caption = re.sub(r"#\d{5,}\b", "", caption) - # "123456.." - caption = re.sub(r"\b\d{6,}\b", "", caption) - # filenames: - caption = re.sub(r"[\S]+\.(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)", "", caption) - - # - caption = re.sub(r"[\"\']{2,}", r'"', caption) # """AUSVERKAUFT""" - caption = re.sub(r"[\.]{2,}", r" ", caption) # """AUSVERKAUFT""" - - caption = re.sub(self.bad_punct_regex, r" ", caption) # ***AUSVERKAUFT***, #AUSVERKAUFT - caption = re.sub(r"\s+\.\s+", r" ", caption) # " . " - - # this-is-my-cute-cat / this_is_my_cute_cat - regex2 = re.compile(r"(?:\-|\_)") - if len(re.findall(regex2, caption)) > 3: - caption = re.sub(regex2, " ", caption) - - caption = ftfy.fix_text(caption) - caption = html.unescape(html.unescape(caption)) - - caption = re.sub(r"\b[a-zA-Z]{1,3}\d{3,15}\b", "", caption) # jc6640 - caption = re.sub(r"\b[a-zA-Z]+\d+[a-zA-Z]+\b", "", caption) # jc6640vc - caption = re.sub(r"\b\d+[a-zA-Z]+\d+\b", "", caption) # 6640vc231 - - caption = re.sub(r"(worldwide\s+)?(free\s+)?shipping", "", caption) - caption = re.sub(r"(free\s)?download(\sfree)?", "", caption) - caption = re.sub(r"\bclick\b\s(?:for|on)\s\w+", "", caption) - caption = re.sub(r"\b(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)(\simage[s]?)?", "", caption) - caption = re.sub(r"\bpage\s+\d+\b", "", caption) - - caption = re.sub(r"\b\d*[a-zA-Z]+\d+[a-zA-Z]+\d+[a-zA-Z\d]*\b", r" ", caption) # j2d1a2a... - - caption = re.sub(r"\b\d+\.?\d*[xх×]\d+\.?\d*\b", "", caption) - - caption = re.sub(r"\b\s+\:\s+", r": ", caption) - caption = re.sub(r"(\D[,\./])\b", r"\1 ", caption) - caption = re.sub(r"\s+", " ", caption) - - caption.strip() - - caption = re.sub(r"^[\"\']([\w\W]+)[\"\']$", r"\1", caption) - caption = re.sub(r"^[\'\_,\-\:;]", r"", caption) - caption = re.sub(r"[\'\_,\-\:\-\+]$", r"", caption) - caption = re.sub(r"^\.\S+$", "", caption) - - return caption.strip() - - # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents - def prepare_latents(self, batch_size, num_channels_latents, video_length, height, width, dtype, device, generator, - latents=None): - shape = ( - batch_size, num_channels_latents, video_length, self.vae.latent_size[0], self.vae.latent_size[1]) - if isinstance(generator, list) and len(generator) != batch_size: - raise ValueError( - f"You have passed a list of generators of length {len(generator)}, but requested an effective batch" - f" size of {batch_size}. Make sure the batch size matches the length of the generators." - ) - - if latents is None: - latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype) - else: - latents = latents.to(device) - - # scale the initial noise by the standard deviation required by the scheduler - latents = latents * self.scheduler.init_noise_sigma - return latents - - @torch.no_grad() - @replace_example_docstring(EXAMPLE_DOC_STRING) - def __call__( - self, - prompt: Union[str, List[str]] = None, - negative_prompt: str = "", - num_inference_steps: int = 20, - timesteps: List[int] = None, - guidance_scale: float = 4.5, - num_images_per_prompt: Optional[int] = 1, - video_length: Optional[int] = None, - height: Optional[int] = None, - width: Optional[int] = None, - eta: float = 0.0, - generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, - latents: Optional[torch.FloatTensor] = None, - prompt_embeds: Optional[torch.FloatTensor] = None, - negative_prompt_embeds: Optional[torch.FloatTensor] = None, - output_type: Optional[str] = "pil", - return_dict: bool = True, - callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, - callback_steps: int = 1, - clean_caption: bool = True, - mask_feature: bool = True, - enable_temporal_attentions: bool = True, - ) -> Union[VideoPipelineOutput, Tuple]: - """ - Function invoked when calling the pipeline for generation. - - Args: - prompt (`str` or `List[str]`, *optional*): - The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. - instead. - negative_prompt (`str` or `List[str]`, *optional*): - The prompt or prompts not to guide the image generation. If not defined, one has to pass - `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is - less than `1`). - num_inference_steps (`int`, *optional*, defaults to 100): - The number of denoising steps. More denoising steps usually lead to a higher quality image at the - expense of slower inference. - timesteps (`List[int]`, *optional*): - Custom timesteps to use for the denoising process. If not defined, equal spaced `num_inference_steps` - timesteps are used. Must be in descending order. - guidance_scale (`float`, *optional*, defaults to 7.0): - Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). - `guidance_scale` is defined as `w` of equation 2. of [Imagen - Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > - 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, - usually at the expense of lower image quality. - num_images_per_prompt (`int`, *optional*, defaults to 1): - The number of images to generate per prompt. - height (`int`, *optional*, defaults to self.unet.config.sample_size): - The height in pixels of the generated image. - width (`int`, *optional*, defaults to self.unet.config.sample_size): - The width in pixels of the generated image. - eta (`float`, *optional*, defaults to 0.0): - Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to - [`schedulers.DDIMScheduler`], will be ignored for others. - generator (`torch.Generator` or `List[torch.Generator]`, *optional*): - One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) - to make generation deterministic. - latents (`torch.FloatTensor`, *optional*): - Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image - generation. Can be used to tweak the same generation with different prompts. If not provided, a latents - tensor will ge generated by sampling using the supplied random `generator`. - prompt_embeds (`torch.FloatTensor`, *optional*): - Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not - provided, text embeddings will be generated from `prompt` input argument. - negative_prompt_embeds (`torch.FloatTensor`, *optional*): - Pre-generated negative text embeddings. For PixArt-Alpha this negative prompt should be "". If not - provided, negative_prompt_embeds will be generated from `negative_prompt` input argument. - output_type (`str`, *optional*, defaults to `"pil"`): - The output format of the generate image. Choose between - [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. - return_dict (`bool`, *optional*, defaults to `True`): - Whether or not to return a [`~pipelines.stable_diffusion.IFPipelineOutput`] instead of a plain tuple. - callback (`Callable`, *optional*): - A function that will be called every `callback_steps` steps during inference. The function will be - called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. - callback_steps (`int`, *optional*, defaults to 1): - The frequency at which the `callback` function will be called. If not specified, the callback will be - called at every step. - clean_caption (`bool`, *optional*, defaults to `True`): - Whether or not to clean the caption before creating embeddings. Requires `beautifulsoup4` and `ftfy` to - be installed. If the dependencies are not installed, the embeddings will be created from the raw - prompt. - mask_feature (`bool` defaults to `True`): If set to `True`, the text embeddings will be masked. - - Examples: - - Returns: - [`~pipelines.ImagePipelineOutput`] or `tuple`: - If `return_dict` is `True`, [`~pipelines.ImagePipelineOutput`] is returned, otherwise a `tuple` is - returned where the first element is a list with the generated images - """ - # 1. Check inputs. Raise error if not correct - # height = height or self.transformer.config.sample_size * self.vae_scale_factor - # width = width or self.transformer.config.sample_size * self.vae_scale_factor - self.check_inputs( - prompt, height, width, negative_prompt, callback_steps, prompt_embeds, negative_prompt_embeds - ) - - # 2. Default height and width to transformer - if prompt is not None and isinstance(prompt, str): - batch_size = 1 - elif prompt is not None and isinstance(prompt, list): - batch_size = len(prompt) - else: - batch_size = prompt_embeds.shape[0] - - device = self.text_encoder.device or self._execution_device - - # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) - # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` - # corresponds to doing no classifier free guidance. - do_classifier_free_guidance = guidance_scale > 1.0 - - # 3. Encode input prompt - prompt_embeds, negative_prompt_embeds = self.encode_prompt( - prompt, - do_classifier_free_guidance, - negative_prompt=negative_prompt, - num_images_per_prompt=num_images_per_prompt, - device=device, - prompt_embeds=prompt_embeds, - negative_prompt_embeds=negative_prompt_embeds, - clean_caption=clean_caption, - mask_feature=mask_feature, - ) - if do_classifier_free_guidance: - prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) - - # 4. Prepare timesteps - self.scheduler.set_timesteps(num_inference_steps, device=device) - timesteps = self.scheduler.timesteps - - # 5. Prepare latents. - latent_channels = self.transformer.config.in_channels - latents = self.prepare_latents( - batch_size * num_images_per_prompt, - latent_channels, - video_length, - height, - width, - prompt_embeds.dtype, - device, - generator, - latents, - ) - - # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline - extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) - - # 6.1 Prepare micro-conditions. - added_cond_kwargs = {"resolution": None, "aspect_ratio": None} - # if self.transformer.config.sample_size == 128: - # resolution = torch.tensor([height, width]).repeat(batch_size * num_images_per_prompt, 1) - # aspect_ratio = torch.tensor([float(height / width)]).repeat(batch_size * num_images_per_prompt, 1) - # resolution = resolution.to(dtype=prompt_embeds.dtype, device=device) - # aspect_ratio = aspect_ratio.to(dtype=prompt_embeds.dtype, device=device) - # added_cond_kwargs = {"resolution": resolution, "aspect_ratio": aspect_ratio} - - # 7. Denoising loop - num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) - - with self.progress_bar(total=num_inference_steps) as progress_bar: - for i, t in enumerate(timesteps): - latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents - latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) - - current_timestep = t - if not torch.is_tensor(current_timestep): - # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can - # This would be a good case for the `match` statement (Python 3.10+) - is_mps = latent_model_input.device.type == "mps" - if isinstance(current_timestep, float): - dtype = torch.float32 if is_mps else torch.float64 - else: - dtype = torch.int32 if is_mps else torch.int64 - current_timestep = torch.tensor([current_timestep], dtype=dtype, device=latent_model_input.device) - elif len(current_timestep.shape) == 0: - current_timestep = current_timestep[None].to(latent_model_input.device) - # broadcast to batch dimension in a way that's compatible with ONNX/Core ML - current_timestep = current_timestep.expand(latent_model_input.shape[0]) - - # predict noise model_output - noise_pred = self.transformer( - latent_model_input, - encoder_hidden_states=prompt_embeds, - timestep=current_timestep, - added_cond_kwargs=added_cond_kwargs, - enable_temporal_attentions=enable_temporal_attentions, - return_dict=False, - )[0] - - # perform guidance - if do_classifier_free_guidance: - noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) - noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) - - # learned sigma - if self.transformer.config.out_channels // 2 == latent_channels: - noise_pred = noise_pred.chunk(2, dim=1)[0] - else: - noise_pred = noise_pred - - # compute previous image: x_t -> x_t-1 - latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] - - # call the callback, if provided - if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): - progress_bar.update() - if callback is not None and i % callback_steps == 0: - step_idx = i // getattr(self.scheduler, "order", 1) - callback(step_idx, t, latents) - - if not output_type == 'latents': - video = self.decode_latents(latents) - else: - video = latents - return VideoPipelineOutput(video=video) - - # Offload all models - self.maybe_free_model_hooks() - - if not return_dict: - return (video,) - - return VideoPipelineOutput(video=video) - - def decode_latents(self, latents): - video = self.vae.decode(latents) - # video = self.vae.decode(latents / 0.18215) - # video = rearrange(video, 'b c t h w -> b t c h w').contiguous() - video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() - # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 - return video diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 90f51ea7a..77d2b96a5 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -9,7 +9,7 @@ HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler -from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder +from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel from omegaconf import OmegaConf from torchvision.utils import save_image from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer @@ -18,44 +18,41 @@ from opensora.models.ae import ae_stride_config, getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V from opensora.models.diffusion.latte.modeling_latte import LatteT2V + from opensora.models.text_encoder import get_text_enc from opensora.utils.utils import save_video_grid -sys.path.append(os.path.split(sys.path[0])[0]) -from pipeline_videogen import VideoGenPipeline +from opensora.sample.pipeline_opensora import OpenSoraPipeline import imageio def main(args): # torch.manual_seed(args.seed) - torch.set_grad_enabled(False) - device = "cuda" if torch.cuda.is_available() else "cpu" - dtype = torch.float16 - # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir='cache_dir').to(device, dtype=dtype) - vae = getae_wrapper(args.ae)(args.ae_path).to(device, dtype=dtype) + weight_dtype = torch.float16 + device = torch.device(args.device) + + vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + # vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) if args.enable_tiling: vae.vae.enable_tiling() vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] - # Load model: - # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, torch_dtype=dtype).to(device) - transformer_model = LatteT2V.from_pretrained(args.model_path, low_cpu_mem_usage=False, device_map=None, torch_dtype=dtype).to(device) - print(transformer_model.config) - transformer_model.force_images = args.force_images - tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, torch_dtype=dtype).to(device) - - video_length, image_size = transformer_model.config.video_length, args.image_size - latent_size = (image_size // ae_stride_config[args.ae][1], image_size // ae_stride_config[args.ae][2]) - vae.latent_size = latent_size - if args.force_images: - video_length = 1 - ext = 'jpg' + if args.model_3d: + transformer_model = OpenSoraT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: - ext = 'mp4' + transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # set eval mode transformer_model.eval() vae.eval() @@ -81,49 +78,47 @@ def main(args): scheduler = DEISMultistepScheduler() elif args.sample_method == 'KDPM2AncestralDiscrete': ######### scheduler = KDPM2AncestralDiscreteScheduler() - print('videogen_pipeline', device) - videogen_pipeline = VideoGenPipeline(vae=vae, - text_encoder=text_encoder, - tokenizer=tokenizer, - scheduler=scheduler, - transformer=transformer_model).to(device=device, dtype=dtype) - # videogen_pipeline.enable_xformers_memory_efficient_attention() - + + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model) + pipeline.to(device) + + if not os.path.exists(args.save_img_path): os.makedirs(args.save_img_path) - - video_grids = [] + if not isinstance(args.text_prompt, list): args.text_prompt = [args.text_prompt] if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): text_prompt = open(args.text_prompt[0], 'r').readlines() - args.text_prompt = [i.strip() for i in text_prompt] - for prompt in args.text_prompt: - print('Processing the ({}) prompt'.format(prompt)) - videos = videogen_pipeline(prompt, - video_length=video_length, - height=image_size, - width=image_size, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - enable_temporal_attentions=not args.force_images, - num_images_per_prompt=1, - mask_feature=True, - ).video + text_prompt = [i.strip() for i in text_prompt] + + video_grids = [] + for idx, prompt in enumerate(text_prompt): + videos = pipeline(prompt, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device, + max_sequence_length=100, + ).images try: - if args.force_images: + if args.num_frames == 1: + ext = 'jpg' videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w - save_image(videos / 255.0, os.path.join(args.save_img_path, - prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), - nrow=1, normalize=True, value_range=(0, 1)) # t c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, f'{idx}.{ext}'), nrow=1, normalize=True, value_range=(0, 1)) # t c h w else: + ext = 'mp4' imageio.mimwrite( - os.path.join( - args.save_img_path, - prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' - ), videos[0], - fps=args.fps, quality=9) # highest quality is 10, lowest is 0 + os.path.join(args.save_img_path, f'{idx}.{ext}'), videos[0], fps=args.fps, quality=6) # highest quality is 10, lowest is 0 except: print('Error when saving {}'.format(prompt)) video_grids.append(videos) @@ -131,7 +126,7 @@ def main(args): # torchvision.io.write_video(args.save_img_path + '_%04d' % args.run_time + '-.mp4', video_grids, fps=6) - if args.force_images: + if args.num_frames == 1: save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) else: @@ -140,14 +135,15 @@ def main(args): print('save path {}'.format(args.save_img_path)) - # save_videos_grid(video, f"./{prompt}.gif") - if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) - parser.add_argument("--image_size", type=int, default=512) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') @@ -159,9 +155,9 @@ def main(args): parser.add_argument("--fps", type=int, default=24) parser.add_argument("--run_time", type=int, default=0) parser.add_argument("--text_prompt", nargs='+') - parser.add_argument('--force_images', action='store_true') parser.add_argument('--tile_overlap_factor', type=float, default=0.25) parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--model_3d', action='store_true') args = parser.parse_args() main(args) \ No newline at end of file diff --git a/opensora/sample/sample_t2v_pixsig.py b/opensora/sample/sample_t2v_pixsig.py deleted file mode 100644 index 0249f2632..000000000 --- a/opensora/sample/sample_t2v_pixsig.py +++ /dev/null @@ -1,174 +0,0 @@ -import math -import os -import torch -import argparse -import torchvision - -from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, - EulerDiscreteScheduler, DPMSolverMultistepScheduler, - HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, - DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) -from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler -from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel -from omegaconf import OmegaConf -from torchvision.utils import save_image -from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer - -import os, sys - -from opensora.models.ae import ae_stride_config, getae, getae_wrapper -from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V -from opensora.models.text_encoder import get_text_enc -from opensora.utils.utils import save_video_grid - -from opensora.sample.pipeline_opensora import OpenSoraPipeline - -import imageio - - -def main(args): - # torch.manual_seed(args.seed) - weight_dtype = torch.float16 - device = torch.device(args.device) - - vae = getae_wrapper(args.ae)(args.ae_path).to(dtype=weight_dtype) - # if args.enable_tiling: - # vae.vae.enable_tiling() - # vae.vae.tile_overlap_factor = args.tile_overlap_factor - vae.vae_scale_factor = ae_stride_config[args.ae] - - transformer_model = OpenSoraT2V.from_pretrained(args.model_path, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - - # ckpt = transformer_model.state_dict() - # from safetensors.torch import load_file - # file_path = "PixArt-Sigma-XL-2-256.safetensors" - # ckptsf = load_file(file_path) - # file_path = "/dxyl_code02/dev3d/Open-Sora-Plan/image_test_ckpt/diffusion_pytorch_model.bin" - # ckptbin = torch.load(file_path) - # # print(transformer_model) - # for k, v in ckpt.items(): - # assert torch.allclose(v, ckptsf[k]) and torch.allclose(v, ckptbin[k]) - # import ipdb;ipdb.set_trace() - - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) - tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - - - # text_encoder = T5EncoderModel.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/text_encoder", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) - # tokenizer = T5Tokenizer.from_pretrained(r"/dxyl_code02/dev3d/Open-Sora-Plan/tokenizer", cache_dir=args.cache_dir) - - - # set eval mode - transformer_model.eval() - vae.eval() - text_encoder.eval() - - if args.sample_method == 'DDIM': ######### - scheduler = DDIMScheduler() - elif args.sample_method == 'EulerDiscrete': - scheduler = EulerDiscreteScheduler() - elif args.sample_method == 'DDPM': ############# - scheduler = DDPMScheduler() - elif args.sample_method == 'DPMSolverMultistep': - scheduler = DPMSolverMultistepScheduler() - elif args.sample_method == 'DPMSolverSinglestep': - scheduler = DPMSolverSinglestepScheduler() - elif args.sample_method == 'PNDM': - scheduler = PNDMScheduler() - elif args.sample_method == 'HeunDiscrete': ######## - scheduler = HeunDiscreteScheduler() - elif args.sample_method == 'EulerAncestralDiscrete': - scheduler = EulerAncestralDiscreteScheduler() - elif args.sample_method == 'DEISMultistep': - scheduler = DEISMultistepScheduler() - elif args.sample_method == 'KDPM2AncestralDiscrete': ######### - scheduler = KDPM2AncestralDiscreteScheduler() - - pipeline = OpenSoraPipeline(vae=vae, - text_encoder=text_encoder, - tokenizer=tokenizer, - scheduler=scheduler, - transformer=transformer_model) - pipeline.to(device) - - - if not os.path.exists(args.save_img_path): - os.makedirs(args.save_img_path) - - if not isinstance(args.text_prompt, list): - args.text_prompt = [args.text_prompt] - if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): - text_prompt = open(args.text_prompt[0], 'r').readlines() - text_prompt = [i.strip() for i in text_prompt] - - video_grids = [] - for prompt in text_prompt: - videos = pipeline(prompt, - num_frames=args.num_frames, - height=args.height, - width=args.width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - num_images_per_prompt=1, - mask_feature=True, - device=args.device, - max_sequence_length=100, - ).images - try: - if args.num_frames == 1: - ext = 'jpg' - videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w - save_image(videos / 255.0, os.path.join(args.save_img_path, - prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), - nrow=1, normalize=True, value_range=(0, 1)) # t c h w - - else: - ext = 'mp4' - imageio.mimwrite( - os.path.join( - args.save_img_path, - prompt.replace(' ', '_')[:100] + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' - ), videos[0], - fps=args.fps, quality=9) # highest quality is 10, lowest is 0 - except: - print('Error when saving {}'.format(prompt)) - video_grids.append(videos) - video_grids = torch.cat(video_grids, dim=0) - - - # torchvision.io.write_video(args.save_img_path + '_%04d' % args.run_time + '-.mp4', video_grids, fps=6) - if args.num_frames == 1: - save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), - nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) - else: - video_grids = save_video_grid(video_grids) - imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=9) - - print('save path {}'.format(args.save_img_path)) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') - parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) - parser.add_argument("--num_frames", type=int, default=1) - parser.add_argument("--height", type=int, default=512) - parser.add_argument("--width", type=int, default=512) - parser.add_argument("--device", type=str, default='cuda:0') - parser.add_argument("--cache_dir", type=str, default='./cache_dir') - parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') - parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') - parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') - parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") - parser.add_argument("--guidance_scale", type=float, default=7.5) - parser.add_argument("--sample_method", type=str, default="PNDM") - parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument("--fps", type=int, default=24) - parser.add_argument("--run_time", type=int, default=0) - parser.add_argument("--text_prompt", nargs='+') - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) - parser.add_argument('--enable_tiling', action='store_true') - args = parser.parse_args() - - main(args) \ No newline at end of file diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 084c12dd5..52bbf09ea 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -57,7 +57,7 @@ @torch.inference_mode() -def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step): +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): validation_prompt = [ "a cat wearing sunglasses and working as a lifeguard at pool.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." @@ -98,10 +98,10 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh assert args.num_frames == 1 images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') np_images = np.stack([np.asarray(img) for img in images]) - tracker.writer.add_images("validation", np_images, global_step, dataformats="NHWC") + tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") else: np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video("validation", np_videos, global_step, fps=10) + tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) if tracker.name == "wandb": import wandb if videos.shape[1] == 1: @@ -109,22 +109,22 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') # import ipdb;ipdb.set_trace() logs = { - "validation": [ + f"{'ema_' if ema else ''}validation": [ wandb.Image(image, caption=f"{i}: {prompt}") for i, (image, prompt) in enumerate(zip(images, validation_prompt)) ] } else: logs = { - "validation": [ - wandb.Video(video, caption=f"{i}: {prompt}", fps=10) + f"{'ema_' if ema else ''}validation": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=30) for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) ] } # import ipdb;ipdb.set_trace() if hasattr(model.pos_embed, 'temp_embed_gate'): logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) - tracker.log(logs) + tracker.log(logs, step=global_step) del opensora_pipeline gc.collect() @@ -605,17 +605,17 @@ def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'outpu tracker.log({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) if global_step % args.checkpointing_steps == 0: - if args.use_ema: - # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - + if args.enable_tracker: log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step) - if args.use_ema: - # Switch back to the original UNet parameters. - ema_model.restore(model.parameters()) + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) accelerator.wait_for_everyone() accelerator.end_training() diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index d00a7549c..f1cdde2c8 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,10 +1,2 @@ -node100 slots=8 -node117 slots=8 # your server name and GPU in total -node99 slots=8 -node118 slots=8 -node119 slots=8 -node120 slots=8 -node121 slots=8 -node122 slots=8 -node104 slots=8 -node106 slots=8 \ No newline at end of file +node030 slots=8 +node031 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile1 b/scripts/accelerate_configs/hostfile1 new file mode 100644 index 000000000..18d214630 --- /dev/null +++ b/scripts/accelerate_configs/hostfile1 @@ -0,0 +1,2 @@ +node032 slots=8 +node033 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index da100f544..cfe5c39c9 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -5,11 +5,11 @@ deepspeed_config: deepspeed_hostfile: scripts/accelerate_configs/hostfile fsdp_config: {} machine_rank: 0 -main_process_ip: 10.0.10.100 +main_process_ip: 100.64.24.30 main_process_port: 29502 main_training_function: main -num_machines: 10 -num_processes: 80 +num_machines: 2 +num_processes: 16 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/accelerate_configs/multi_node_example1.yaml b/scripts/accelerate_configs/multi_node_example1.yaml new file mode 100644 index 000000000..6912a17b1 --- /dev/null +++ b/scripts/accelerate_configs/multi_node_example1.yaml @@ -0,0 +1,18 @@ +compute_environment: LOCAL_MACHINE +distributed_type: DEEPSPEED +deepspeed_config: + deepspeed_config_file: scripts/accelerate_configs/zero2.json + deepspeed_hostfile: scripts/accelerate_configs/hostfile1 +fsdp_config: {} +machine_rank: 0 +main_process_ip: 100.64.24.32 +main_process_port: 29502 +main_training_function: main +num_machines: 2 +num_processes: 16 +rdzv_backend: static +same_network: true +tpu_env: [] +tpu_use_cluster: false +tpu_use_sudo: false +use_cpu: false diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/sample_image.sh deleted file mode 100644 index b6ca3e3d8..000000000 --- a/scripts/text_condition/sample_image.sh +++ /dev/null @@ -1,16 +0,0 @@ -CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v_pixsig.py \ - --model_path 512ms_lr2e-5_bs64_4node/checkpoint-71000/model \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --text_prompt examples/prompt_list_0.txt \ - --ae CausalVAEModel_4x8x8 \ - --ae_path /remote-home1/yeyang/CausalVAEModel_4x8x8 \ - --version 65x512x512 \ - --save_img_path "./sample_images/prompt_list_0" \ - --fps 24 \ - --num_frames 1 \ - --height 512 \ - --width 512 \ - --guidance_scale 2.5 \ - --sample_method "DPMSolverMultistep" \ - --num_sampling_steps 20 \ - --enable_tiling diff --git a/scripts/text_condition/train_video21d_65x240p.sh b/scripts/text_condition/train_video21d_65x240p.sh index 2484bf811..18ebdf17c 100644 --- a/scripts/text_condition/train_video21d_65x240p.sh +++ b/scripts/text_condition/train_video21d_65x240p.sh @@ -1,7 +1,7 @@ -# export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="debug" +export PROJECT="test21d_2node_65x240p_bs4_snr5_ema0_pt" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,7 +12,7 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v.py \ --model LatteT2V-XL/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -32,22 +32,23 @@ accelerate launch \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=4 \ - --dataloader_num_workers 0 \ + --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=2e-5 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="tensorboard" \ - --checkpointing_steps=1 \ - --output_dir="debug" \ + --report_to="wandb" \ + --checkpointing_steps=100 \ + --output_dir="test21d_2node_65x240p_bs4_snr5_ema0_pt" \ --allow_tf32 \ --use_deepspeed \ - --model_max_length 300 \ + --model_max_length 512 \ --use_image_num 0 \ --enable_tiling \ --enable_tracker \ --snr_gamma 5.0 \ --use_ema \ - --ema_start_step 0 \ No newline at end of file + --ema_start_step 0 \ + --pretrained /storage/t2v.pt \ No newline at end of file diff --git a/scripts/text_condition/train_video3d_65x240p.sh b/scripts/text_condition/train_video3d_65x240p.sh index e989a82c0..6165d1d7b 100644 --- a/scripts/text_condition/train_video3d_65x240p.sh +++ b/scripts/text_condition/train_video3d_65x240p.sh @@ -1,7 +1,7 @@ -# export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="debug" +export PROJECT="test3d_2node_65x240p_bs4_snr5_ema0_pt" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,9 +12,9 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example1.yaml \ opensora/train/train_t2v.py \ - --model OpenSoraT2V-B/122 \ + --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ @@ -39,13 +39,16 @@ accelerate launch \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ - --report_to="tensorboard" \ + --report_to="wandb" \ --checkpointing_steps=100 \ - --output_dir="debug" \ + --output_dir="test3d_2node_65x240p_bs4_snr5_ema0_pt" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 512 \ --use_image_num 0 \ --enable_tiling \ --enable_tracker \ - --snr_gamma 5.0 + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --pretrained PixArt-Alpha-XL-2-512.safetensors diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt index 0be549e08..1ace10925 100644 --- a/scripts/train_data/video_data_debug.txt +++ b/scripts/train_data/video_data_debug.txt @@ -1 +1 @@ -/remote-home1/dataset/all_mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/storage/mixkit/LanguageBind/Open-Sora-Plan-v1.1.0/all_mixkit/mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file From 046af989cce0e4918154d6111a2b7726d67e6c70 Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Fri, 31 May 2024 17:32:57 +0800 Subject: [PATCH 017/134] =?UTF-8?q?=E8=AE=AD=E7=BB=83=E6=8E=A8=E7=90=86?= =?UTF-8?q?=E9=80=82=E9=85=8Dnpu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/prompt_list_0.txt | 9 +- opensora/dataset/__init__.py | 4 +- .../causal_vae/modeling_causalvae.py | 11 +- .../models/ae/videobase/modules/attention.py | 4 +- opensora/models/ae/videobase/modules/conv.py | 24 +- .../ae/videobase/modules/resnet_block.py | 8 +- .../ae/videobase/modules/updownsample.py | 83 +++- .../models/diffusion/latte/modeling_latte.py | 5 +- opensora/models/diffusion/latte/modules.py | 109 +++-- .../diffusion/opensora/modeling_opensora.py | 12 +- opensora/models/diffusion/opensora/modules.py | 68 +-- opensora/models/text_encoder/__init__.py | 5 +- opensora/npu_config.py | 329 +++++++++++++++ opensora/sample/sample_t2v.py | 233 +++++++---- opensora/train/train_t2v.py | 390 ++++++++++-------- .../multi_node_example_on_npu.yaml | 13 + scripts/text_condition/sample_video_on_npu.sh | 22 + .../train_video21d_65x240p_on_npu.sh | 46 +++ .../train_video3d_65x240p_on_npu.sh | 46 +++ scripts/train_data/image_data_on_npu.txt | 4 + scripts/train_data/video_data_on_npu.txt | 3 + 21 files changed, 1064 insertions(+), 364 deletions(-) create mode 100644 opensora/npu_config.py create mode 100644 scripts/accelerate_configs/multi_node_example_on_npu.yaml create mode 100644 scripts/text_condition/sample_video_on_npu.sh create mode 100644 scripts/text_condition/train_video21d_65x240p_on_npu.sh create mode 100644 scripts/text_condition/train_video3d_65x240p_on_npu.sh create mode 100644 scripts/train_data/image_data_on_npu.txt create mode 100644 scripts/train_data/video_data_on_npu.txt diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index 6c8ad8382..53b4d0975 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -1,6 +1,13 @@ -A small cactus with a happy face in the Sahara desert. A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues. A quiet beach at dawn, the waves softly lapping at the shore, pink and orange hues painting the sky, offering a moment of solitude and reflection. +A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about. +Several giant wooly mammoths approach treading through a snowy meadow, their long wooly fur lightly blows in the wind as they walk, snow covered trees and dramatic snow capped mountains in the distance, mid afternoon light with wispy clouds and a sun high in the distance creates a warm glow, the low camera view is stunning capturing the large furry mammal with beautiful photography, depth of field. +A movie trailer featuring the adventures of the 30 year old space man wearing a red wool knitted motorcycle helmet, blue sky, salt desert, cinematic style, shot on 35mm film, vivid colors. +Drone view of waves crashing against the rugged cliffs along Big Sur’s garay point beach. The crashing blue waters create white-tipped waves, while the golden light of the setting sun illuminates the rocky shore. A small island with a lighthouse sits in the distance, and green shrubbery covers the cliff’s edge. The steep drop from the road down to the beach is a dramatic feat, with the cliff’s edges jutting out over the sea. This is a view that captures the raw beauty of the coast and the rugged landscape of the Pacific Coast Highway. +Animated scene features a close-up of a short fluffy monster kneeling beside a melting red candle. The art style is 3D and realistic, with a focus on lighting and texture. The mood of the painting is one of wonder and curiosity, as the monster gazes at the flame with wide eyes and open mouth. Its pose and expression convey a sense of innocence and playfulness, as if it is exploring the world around it for the first time. The use of warm colors and dramatic lighting further enhances the cozy atmosphere of the image. +A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures. +This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. +Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. The majestic beauty of a waterfall cascading down a cliff into a serene lake. Sunset over the sea. a cat wearing sunglasses and working as a lifeguard at pool. diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 9a1d0e3c1..1c6310e51 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -61,7 +61,7 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - tokenizer = AutoTokenizer.from_pretrained("/storage/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained("/storage/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py index 0b5566c37..7af7c659e 100644 --- a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py +++ b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py @@ -1,3 +1,4 @@ +from opensora.npu_config import npu_config from ..modeling_videobase import VideoBaseAE_PL from ..modules import Normalize from ..modules.ops import nonlinearity @@ -129,7 +130,8 @@ def forward(self, x): h = self.mid.attn_1(h) h = self.mid.block_2(h) - h = self.norm_out(h) + # h = self.norm_out(h) + h = npu_config.run_group_norm(self.norm_out, h) h = nonlinearity(h) h = self.conv_out(h) return h @@ -243,9 +245,12 @@ def forward(self, z): if hasattr(self.up[i_level], "time_upsample"): h = self.up[i_level].time_upsample(h) - h = self.norm_out(h) + # h = self.norm_out(h) + h = npu_config.run_group_norm(self.norm_out, h) h = nonlinearity(h) - h = self.conv_out(h) + # h = self.conv_out(h) + h_dtype = h.dtype + h = npu_config.run_conv3d(self.conv_out, h, h_dtype) return h diff --git a/opensora/models/ae/videobase/modules/attention.py b/opensora/models/ae/videobase/modules/attention.py index c3b9bb19b..0ee64d55c 100644 --- a/opensora/models/ae/videobase/modules/attention.py +++ b/opensora/models/ae/videobase/modules/attention.py @@ -1,4 +1,5 @@ import torch.nn as nn +from opensora.npu_config import npu_config from .normalize import Normalize from .conv import CausalConv3d import torch @@ -91,7 +92,8 @@ def __init__(self, in_channels): def forward(self, x): h_ = x - h_ = self.norm(h_) + # h_ = self.norm(h_) + h_ = npu_config.run_group_norm(self.norm, h_) q = self.q(h_) k = self.k(h_) v = self.v(h_) diff --git a/opensora/models/ae/videobase/modules/conv.py b/opensora/models/ae/videobase/modules/conv.py index 5a4c8ae27..43d85f2c5 100644 --- a/opensora/models/ae/videobase/modules/conv.py +++ b/opensora/models/ae/videobase/modules/conv.py @@ -6,6 +6,8 @@ from .ops import cast_tuple from einops import rearrange from .ops import video_to_image +from opensora.npu_config import npu_config + class Conv2d(nn.Conv2d): def __init__( @@ -56,6 +58,7 @@ def __init__( padding[0] = 0 stride = cast_tuple(stride, 3) self.conv = nn.Conv3d(chan_in, chan_out, self.kernel_size, stride=stride, padding=padding) + self.pad = nn.ReplicationPad2d((0, 0, self.time_kernel_size - 1, 0)) self._init_weights(init_method) def _init_weights(self, init_method): @@ -90,9 +93,18 @@ def _init_weights(self, init_method): nn.init.constant_(self.conv.bias, 0) def forward(self, x): - # 1 + 16 16 as video, 1 as image - first_frame_pad = x[:, :, :1, :, :].repeat( - (1, 1, self.time_kernel_size - 1, 1, 1) - ) # b c t h w - x = torch.concatenate((first_frame_pad, x), dim=2) # 3 + 16 - return self.conv(x) \ No newline at end of file + if npu_config.on_npu: + x_dtype = x.dtype + x = x.to(torch.float16) + n, c, d, h, w = x.shape + x = x.reshape(n * c, d, h * w) + x = self.pad(x) + x = x.reshape(n, c, -1, h, w) + return npu_config.run_conv3d(self.conv, x, x_dtype) + else: + # 1 + 16 16 as video, 1 as image + first_frame_pad = x[:, :, :1, :, :].repeat( + (1, 1, self.time_kernel_size - 1, 1, 1) + ) # b c t h w + x = torch.concatenate((first_frame_pad, x), dim=2) # 3 + 16 + return self.conv(x) \ No newline at end of file diff --git a/opensora/models/ae/videobase/modules/resnet_block.py b/opensora/models/ae/videobase/modules/resnet_block.py index 189766a5b..8691ec0a3 100644 --- a/opensora/models/ae/videobase/modules/resnet_block.py +++ b/opensora/models/ae/videobase/modules/resnet_block.py @@ -5,6 +5,8 @@ from .ops import nonlinearity, video_to_image from .conv import CausalConv3d from .block import Block +from opensora.npu_config import npu_config + class ResnetBlock2D(Block): def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, @@ -71,10 +73,12 @@ def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, dropo def forward(self, x): h = x - h = self.norm1(h) + # h = self.norm1(h) + h = npu_config.run_group_norm(self.norm1, h) h = nonlinearity(h) h = self.conv1(h) - h = self.norm2(h) + # h = self.norm2(h) + h = npu_config.run_group_norm(self.norm2, h) h = nonlinearity(h) h = self.dropout(h) h = self.conv2(h) diff --git a/opensora/models/ae/videobase/modules/updownsample.py b/opensora/models/ae/videobase/modules/updownsample.py index 9e3d489ae..f4e542dbe 100644 --- a/opensora/models/ae/videobase/modules/updownsample.py +++ b/opensora/models/ae/videobase/modules/updownsample.py @@ -9,6 +9,8 @@ from .conv import CausalConv3d from einops import rearrange from .block import Block +from opensora.npu_config import npu_config + class Upsample(Block): def __init__(self, in_channels, out_channels): @@ -42,9 +44,15 @@ def __init__(self, in_channels, out_channels): @video_to_image def forward(self, x): if self.with_conv: - pad = (0,1,0,1) - x = torch.nn.functional.pad(x, pad, mode="constant", value=0) - x = self.conv(x) + pad = (0, 1, 0, 1) + if npu_config.on_npu: + x_dtype = x.dtype + x = x.to(torch.bfloat16) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = npu_config.run_conv3d(self.conv, x, x_dtype) + else: + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) else: x = torch.nn.functional.avg_pool2d(x, kernel_size=2, stride=2) return x @@ -115,14 +123,26 @@ def __init__( ): super().__init__() self.kernel_size = kernel_size - self.conv = nn.AvgPool3d((kernel_size,1,1), stride=(2,1,1)) - + if npu_config.on_npu: + self.avg_pool = nn.AvgPool2d((kernel_size, 1), stride=(2, 1)) + self.pad = nn.ReplicationPad3d((0, 0, 0, 0, self.kernel_size - 1, 0)) + else: + self.avg_pool = nn.AvgPool3d((kernel_size, 1, 1), stride=(2, 1, 1)) + def forward(self, x): - first_frame_pad = x[:, :, :1, :, :].repeat( - (1, 1, self.kernel_size - 1, 1, 1) - ) - x = torch.concatenate((first_frame_pad, x), dim=2) - return self.conv(x) + if npu_config.on_npu: + n, c, d, h, w = x.shape + x = self.pad(x) + x = x.view(n * c, -1, h * w) + pooled = self.avg_pool(x) + output = pooled.view(n, c, -1, h, w) + return output + else: + first_frame_pad = x[:, :, :1, :, :].repeat( + (1, 1, self.kernel_size - 1, 1, 1) + ) + x = torch.concatenate((first_frame_pad, x), dim=2) + return self.avg_pool(x) class TimeUpsample2x(Block): def __init__( @@ -148,7 +168,11 @@ def __init__( ): super().__init__() self.kernel_size = cast_tuple(kernel_size, 3) - self.avg_pool = nn.AvgPool3d((kernel_size,1,1), stride=(2,1,1)) + if npu_config.on_npu: + self.avg_pool = nn.AvgPool2d((kernel_size, 1), stride=(2, 1)) + self.pad = nn.ReplicationPad3d((0, 0, 0, 0, kernel_size - 1, 0)) + else: + self.avg_pool = nn.AvgPool3d((kernel_size, 1, 1), stride=(2, 1, 1)) self.conv = nn.Conv3d( in_channels, out_channels, self.kernel_size, stride=(2,1,1), padding=(0,1,1) ) @@ -156,11 +180,21 @@ def __init__( def forward(self, x): alpha = torch.sigmoid(self.mix_factor) - first_frame_pad = x[:, :, :1, :, :].repeat( - (1, 1, self.kernel_size[0] - 1, 1, 1) - ) - x = torch.concatenate((first_frame_pad, x), dim=2) - return alpha * self.avg_pool(x) + (1 - alpha) * self.conv(x) + if npu_config.on_npu: + n, c, d, h, w = x.shape + x_dtype = x.dtype + x = x.to(torch.float16) + x = self.pad(x) + pad_x = x.view(n, c, -1, h, w) + avg_x = self.avg_pool(x.view(n * c, -1, h * w)).view(n, c, -1, h, w).to(x_dtype) + conv_x = npu_config.run_conv3d(self.conv, pad_x, x_dtype) + return alpha * avg_x + (1 - alpha) * conv_x + else: + first_frame_pad = x[:, :, :1, :, :].repeat( + (1, 1, self.kernel_size[0] - 1, 1, 1) + ) + x = torch.concatenate((first_frame_pad, x), dim=2) + return alpha * self.avg_pool(x) + (1 - alpha) * self.conv(x) class TimeUpsampleRes2x(nn.Module): def __init__( @@ -180,7 +214,13 @@ def forward(self, x): alpha = torch.sigmoid(self.mix_factor) if x.size(2) > 1: x,x_= x[:,:,:1],x[:,:,1:] - x_= F.interpolate(x_, scale_factor=(2,1,1), mode='trilinear') + if npu_config.on_npu: + x_dtype = x_.dtype + x_ = x_.to(torch.float16) + x_ = F.interpolate(x_, scale_factor=(2, 1, 1), mode='trilinear') + x_ = x_.to(x_dtype) + else: + x_= F.interpolate(x_, scale_factor=(2,1,1), mode='trilinear') x = torch.concat([x, x_], dim=2) return alpha * x + (1-alpha) * self.conv(x) @@ -230,7 +270,14 @@ def __init__( def forward(self, x): if x.size(2) > 1: x,x_= x[:,:,:1],x[:,:,1:] - x_= F.interpolate(x_, scale_factor=(2,1,1), mode='trilinear') + if npu_config.on_npu: + x_dtype = x_.dtype + x_ = x_.to(torch.float16) + x_= F.interpolate(x_, scale_factor=(2,1,1), mode='trilinear') + x_ = x_.to(x_dtype) + else: + x_= F.interpolate(x_, scale_factor=(2,1,1), mode='trilinear') + x = torch.concat([x, x_], dim=2) alpha = torch.sigmoid(self.mix_factor) return alpha * x + (1 - alpha) * self.conv(self.attn(self.res(x))) diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index b62bdd9d4..51401622a 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -13,7 +13,7 @@ from diffusers.models.modeling_utils import ModelMixin from diffusers.models.lora import LoRACompatibleConv, LoRACompatibleLinear from opensora.utils.utils import to_2tuple - +from opensora.npu_config import npu_config import torch import torch.nn.functional as F from torch import nn @@ -279,6 +279,7 @@ def make_attn_mask(self, attention_mask, frame, dtype): # (keep = +0, discard = -10000.0) attention_mask = (1 - attention_mask.to(dtype)) * -10000.0 attention_mask = attention_mask.to(self.dtype) + attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) return attention_mask def vae_to_diff_mask(self, attention_mask, use_image_num): @@ -382,6 +383,8 @@ def forward( encoder_attention_mask = rearrange(encoder_attention_mask, 'b n l -> (b n) l').contiguous().unsqueeze(1) encoder_attention_mask = encoder_attention_mask.to(self.dtype) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-2]) + # Retrieve lora scale. lora_scale = cross_attention_kwargs.get("scale", 1.0) if cross_attention_kwargs is not None else 1.0 diff --git a/opensora/models/diffusion/latte/modules.py b/opensora/models/diffusion/latte/modules.py index 95ea5387d..f87b38934 100644 --- a/opensora/models/diffusion/latte/modules.py +++ b/opensora/models/diffusion/latte/modules.py @@ -21,7 +21,11 @@ from dataclasses import dataclass from opensora.models.diffusion.utils.pos_embed import RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D - +try: + import torch_npu +except: + pass +from opensora.npu_config import npu_config, set_run_dtype if is_xformers_available(): import xformers import xformers.ops @@ -866,13 +870,13 @@ def prepare_attention_mask( # remaining_length: int = target_length - current_length # TODO: re-enable tests/models/test_models_unet_2d_condition.py#test_model_xattn_padding attention_mask = F.pad(attention_mask, (0, target_length), value=0.0) - - if out_dim == 3: - if attention_mask.shape[0] < batch_size * head_size: - attention_mask = attention_mask.repeat_interleave(head_size, dim=0) - elif out_dim == 4: - attention_mask = attention_mask.unsqueeze(1) - attention_mask = attention_mask.repeat_interleave(head_size, dim=1) + if not npu_config.on_npu: + if out_dim == 3: + if attention_mask.shape[0] < batch_size * head_size: + attention_mask = attention_mask.repeat_interleave(head_size, dim=0) + elif out_dim == 4: + attention_mask = attention_mask.unsqueeze(1) + attention_mask = attention_mask.repeat_interleave(head_size, dim=1) return attention_mask @@ -999,7 +1003,10 @@ def __call__( attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) # scaled_dot_product_attention expects attention_mask shape to be # (batch, heads, source_length, target_length) - attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + if npu_config.on_npu: + attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) + else: + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) @@ -1019,47 +1026,59 @@ def __call__( inner_dim = key.shape[-1] head_dim = inner_dim // attn.heads - - query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - - key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - - if self.use_rope: - # require the shape of (batch_size x nheads x ntokens x dim) - if position_q.ndim == 3: - query = self.rope2d(query, position_q) - elif position_q.ndim == 2: - query = self.rope1d(query, position_q) - else: - raise NotImplementedError - if position_k.ndim == 3: - key = self.rope2d(key, position_k) - elif position_k.ndim == 2: - key = self.rope1d(key, position_k) + if npu_config.on_npu: + if npu_config.enable_FA and query.dtype == torch.float32: + dtype = torch.bfloat16 else: - raise NotImplementedError + dtype = None - # the output of sdp = (batch, num_heads, seq_len, head_dim) - # TODO: add support for attn.scale when we move to Torch 2.1 - if self.attention_mode == 'flash': - assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): - hidden_states = F.scaled_dot_product_attention( - query, key, value, dropout_p=0.0, is_causal=False - ) - elif self.attention_mode == 'xformers': - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + with set_run_dtype(query, dtype): + query, key, value = npu_config.set_current_run_dtype([query, key, value]) + hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", + head_dim, attn.heads) + + hidden_states = npu_config.restore_dtype(hidden_states) + else: + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + if position_q.ndim == 3: + query = self.rope2d(query, position_q) + elif position_q.ndim == 2: + query = self.rope1d(query, position_q) + else: + raise NotImplementedError + if position_k.ndim == 3: + key = self.rope2d(key, position_k) + elif position_k.ndim == 2: + key = self.rope1d(key, position_k) + else: + raise NotImplementedError + + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + if self.attention_mode == 'flash': + assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': hidden_states = F.scaled_dot_product_attention( query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False ) - elif self.attention_mode == 'math': - hidden_states = F.scaled_dot_product_attention( - query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False - ) - else: - raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') - hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) hidden_states = hidden_states.to(query.dtype) # linear proj diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index eaf5846d8..c17672717 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -13,7 +13,7 @@ from diffusers.models.embeddings import PixArtAlphaTextProjection from opensora.models.diffusion.opensora.modules import PatchEmbed3D, PatchEmbed2D, BasicTransformerBlock from opensora.utils.utils import to_2tuple - +from opensora.npu_config import npu_config class OpenSoraT2V(ModelMixin, ConfigMixin): """ A 2D Transformer model for image-like data. @@ -344,6 +344,16 @@ def forward( encoder_attention_mask_img = encoder_attention_mask_vid encoder_attention_mask_vid = None + if attention_mask_vid is not None: + attention_mask_vid = npu_config.get_attention_mask(attention_mask_vid, attention_mask_vid.shape[-1]) + encoder_attention_mask_vid = npu_config.get_attention_mask(encoder_attention_mask_vid, + attention_mask_vid.shape[-2]) + if attention_mask_img is not None: + attention_mask_img = npu_config.get_attention_mask(attention_mask_img, attention_mask_img.shape[-1]) + encoder_attention_mask_img = npu_config.get_attention_mask(encoder_attention_mask_img, + attention_mask_img.shape[-2]) + + # 1. Input height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size added_cond_kwargs = {"resolution": None, "aspect_ratio": None} diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 1cc612752..fc6746839 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -18,7 +18,11 @@ from diffusers.models.attention_processor import Attention as Attention_ from diffusers.models.embeddings import SinusoidalPositionalEmbedding from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm - +try: + import torch_npu +except: + pass +from opensora.npu_config import npu_config, set_run_dtype logger = logging.get_logger(__name__) def get_3d_sincos_pos_embed( @@ -422,36 +426,48 @@ def __call__( inner_dim = key.shape[-1] head_dim = inner_dim // attn.heads - query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + if npu_config.on_npu: + if npu_config.enable_FA and query.dtype == torch.float32: + dtype = torch.bfloat16 + else: + dtype = None - key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + with set_run_dtype(query, dtype): + query, key, value = npu_config.set_current_run_dtype([query, key, value]) + hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", + head_dim, attn.heads) - if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible - attention_mask = None - # the output of sdp = (batch, num_heads, seq_len, head_dim) - # TODO: add support for attn.scale when we move to Torch 2.1 - # import ipdb;ipdb.set_trace() - # print(attention_mask) - if self.attention_mode == 'flash': - assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): - hidden_states = F.scaled_dot_product_attention( - query, key, value, dropout_p=0.0, is_causal=False - ) - elif self.attention_mode == 'xformers': - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = npu_config.restore_dtype(hidden_states) + else: + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + attention_mask = None + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + # import ipdb;ipdb.set_trace() + # print(attention_mask) + if self.attention_mode == 'flash': + assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': hidden_states = F.scaled_dot_product_attention( query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False ) - elif self.attention_mode == 'math': - hidden_states = F.scaled_dot_product_attention( - query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False - ) - else: - raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') - - hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) hidden_states = hidden_states.to(query.dtype) # linear proj diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index cc39b0329..28cf771bb 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -9,9 +9,8 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = T5EncoderModel.from_pretrained("/storage/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() - + self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] return text_encoder_embs.detach() diff --git a/opensora/npu_config.py b/opensora/npu_config.py new file mode 100644 index 000000000..f91ac0c91 --- /dev/null +++ b/opensora/npu_config.py @@ -0,0 +1,329 @@ +import math +import mmap +import os +import pickle +import random +import numpy as np +import torch +import subprocess + +try: + import torch_npu + + npu_is_available = True + from torch_npu.contrib import transfer_to_npu +except: + npu_is_available = False + +from contextlib import contextmanager +from opensora.acceleration.parallel_states import enable_LCCL, hccl_info, lccl_info + + +def compress_video(input_file, output_file, out_size): + """使用 ffmpeg 压缩视频文件。""" + command = [ + 'ffmpeg', + '-i', input_file, + '-vf', f"scale='min({out_size},iw)':'min({out_size},ih)':force_original_aspect_ratio=decrease", + '-c:v', 'libx264', + '-crf', '18', + '-preset', 'slow', + '-c:a', 'copy', + output_file + ] + subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + +@contextmanager +def set_run_dtype(x, dtype=None): + # 保存原始环境变量的值(如果存在) + npu_config.original_run_dtype = x.dtype + # 设置环境变量为指定的值 + npu_config.current_run_dtype = dtype + try: + # Yield control back to the body of the `with` statement + yield + finally: + # 恢复原始的环境变量值 + npu_config.current_run_dtype = None + npu_config.original_run_dtype = None + + +class NPUConfig: + N_NPU_PER_NODE = 8 + + def __init__(self): + self.on_npu = npu_is_available + self.node_world_size = self.N_NPU_PER_NODE + self.profiling = False + self.profiling_step = 5 + self.enable_FA = True + self.enable_FP32 = False + self.load_pickle = True + self.use_small_dataset = False + self.current_run_dtype = None + self.original_run_dtype = None + + if self.enable_FA and self.enable_FP32: + self.inf_float = -10000.0 + else: + self.inf_float = -10000.0 + + if self.use_small_dataset: + self.load_pickle = False + + self._loss = [] + self.work_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + self.pickle_save_path = f"{self.work_path}/pickles" + self.mm = dict() + + if self.on_npu: + torch_npu.npu.set_compile_mode(jit_compile=False) + + if "RANK" in os.environ: + self.rank = int(os.environ["RANK"]) + self.world_size = int(os.environ["WORLD_SIZE"]) + torch_npu.npu.set_device(self.get_local_rank()) + else: + self.rank = torch.cuda.current_device() + self.world_size = self.N_NPU_PER_NODE + self.print_with_rank(f"The npu_config.on_npu is {self.on_npu}") + + def get_attention_mask(self, attention_mask, repeat_num): + if self.on_npu and attention_mask is not None: + attention_mask = attention_mask.repeat(1, repeat_num, 1) + if npu_config.enable_FA: + attention_mask = attention_mask.to(torch.bool) + return attention_mask + def set_current_run_dtype(self, variables): + if variables[0].dtype != self.current_run_dtype and self.current_run_dtype is not None: + for index, var in enumerate(variables): + variables[index] = var.to(self.current_run_dtype) + return tuple(variables) + + def restore_dtype(self, x): + if x.dtype != self.original_run_dtype and self.original_run_dtype is not None: + x = x.to(self.original_run_dtype) + return x + + def get_sp_size(self): + if enable_LCCL: + sp_size = lccl_info.world_size + else: + sp_size = hccl_info.world_size + return sp_size + + def get_output_video_path(self, name): + os.makedirs(f"{self.work_path}/output_videos", exist_ok=True) + return f"{self.work_path}/output_videos/{name}" + + def get_node_size(self): + return self.world_size / self.node_world_size + + def get_local_rank(self): + return self.rank % self.N_NPU_PER_NODE + + def get_pickle_path(self, file_name): + return f"{self.pickle_save_path}/{file_name}_local_n63" + + def free_mm(self): + for key, value in self.mm.items(): + value.close() + self.mm.clear() + + def __del__(self): + self.free_mm() + + def try_load_pickle(self, file_name, function): + file_name = self.get_pickle_path(file_name) + if os.path.exists(file_name) and self.load_pickle: + with open(file_name, 'rb') as file: + # self.mm[file_name] = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) + # # 使用 mmap 进行数据读取 + # loaded_data = pickle.loads(self.mm[file_name][:]) + loaded_data = pickle.load(file) + return loaded_data + else: + data = function() + if not self.use_small_dataset: + if self.rank % self.N_NPU_PER_NODE == 0: + # 只需要rank0保存文件 + os.makedirs(self.pickle_save_path, exist_ok=True) + with open(file_name, 'wb') as file: + pickle.dump(data, file, pickle.HIGHEST_PROTOCOL) + return data + + def try_get_vid_path(self, file, out_size=1024): + output_file = file.rsplit(".", 1)[0] + f"_resize{out_size}.mp4" + if not os.path.exists(output_file): + return file + # compress_video(file, output_file, out_size) + return output_file + + def npu_format_cast(self, x): + return torch_npu.npu_format_cast(x, 2) + + def calc_grad_norm(self, model): + # 计算并打印梯度范数 + # model_engine = accelerator.deepspeed_engine_wrapped.engine + # gradients = model_engine.get_gradients() + # grad_norm = get_grad_norm(gradients) + # 计算并打印梯度范数 + grad_norm = 0 + n_grad = 0 + # for name, param in model.named_parameters(): + # grad_data = deepspeed.utils.safe_get_full_grad(param) + # # self.print_tensor_stats(grad_data, name=name) + # + # if grad_data is not None: + # param_norm = grad_data.norm(2) + # grad_norm += param_norm.item() ** 2 + # n_grad += 1 + # grad_norm = (grad_norm / n_grad) ** (1. / 2) + + return grad_norm + + def _run(self, operator, x, tmp_dtype, out_dtype=None, out_nd_format=False): + if self.on_npu: + if out_dtype is None: + out_dtype = x.dtype + + with torch.cuda.amp.autocast(enabled=False): + x = operator.to(tmp_dtype)(x.to(tmp_dtype)) + x = x.to(out_dtype) + if out_nd_format: + return self.npu_format_cast(x) + else: + return x + else: + return operator(x) + + def run_group_norm(self, operator, x): + return self._run(operator, x, torch.float32) + + def print_tensor_stats(self, tensor, name="Tensor", rank=None): + if rank and rank != self.rank: + return + + if tensor is None: + self.print_msg(f"Tensor {name} is None.") + return + + x_dtype = tensor.dtype + tensor = tensor.to(torch.bfloat16) + max_val = tensor.max().item() + min_val = tensor.min().item() + abs_max_val = min(abs(max_val), abs(min_val)) + mean_val = tensor.mean().item() + median_val = tensor.median().item() + std_val = tensor.std().item() + shape = tensor.shape + self.print_msg( + f"{name} - Max: {max_val}, Min: {min_val}, Mean: {mean_val}, AbsMax: {abs_max_val}," + f"Median: {median_val}, Std: {std_val}, Shape: {shape}, Type: {x_dtype}") + + def run_conv3d(self, operator, x, out_dtype): + return self._run(operator, x, torch.float16, out_dtype, out_nd_format=True) + + def run_pool_2d(self, operator, x): + return self._run(operator, x, torch.float16) + + def seed_everything(self, seed=0): + random.seed(seed) + np.random.seed(seed) + torch.manual_seed(seed) + torch.cuda.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + + def print_with_rank(self, msg, rank=0, save=False): + if self.rank == rank: + print(f"{msg}", flush=True) + if save: + self._loss.append(msg) + + def print_msg(self, msg, on=True, rank=None): + if on: + if self.rank == rank or rank is None: + print(f"[RANK-{self.rank}]: {msg}", flush=True) + + def save_loss(self, filename, rank=0): + if self.rank == rank: + import json + with open(filename, 'w') as file: + json.dump(self._loss, file, indent=4) + + def run_attention(self, query, key, value, atten_mask, input_layout, head_dim, head_num): + if self.enable_FA: + hidden_states = torch_npu.npu_fusion_attention(query, key, value, + atten_mask=atten_mask, + input_layout=input_layout, + scale=1 / math.sqrt(head_dim), + head_num=head_num)[0] + else: + hidden_states = self.scaled_dot_product_attention(query, key, value, + atten_mask=atten_mask, + input_layout=input_layout, + scale=1 / math.sqrt(head_dim), + head_num=head_num) + return hidden_states + + def scaled_dot_product_attention(self, query, key, value, input_layout, head_num=None, + atten_mask=None, scale=None, dropout_p=0.0, is_causal=False) -> torch.Tensor: + # L, S = query.size(-2), key.size(-2) + def trans_tensor_shape(x, layout, head_num): + if layout == "BSH": + batch = x.shape[0] + x = x.view(batch, -1, head_num, x.shape[-1] // head_num).transpose(1, 2).contiguous() + elif layout == "SBH": + batch = x.shape[1] + x = x.view(-1, batch * head_num, x.shape[-1] // head_num).transpose(0, 1).contiguous() + x = x.view(batch, head_num, -1, x.shape[-1]) + return x + + query = trans_tensor_shape(query, input_layout, head_num) + key = trans_tensor_shape(key, input_layout, head_num) + value = trans_tensor_shape(value, input_layout, head_num) + + attn_weight = query @ key.transpose(-2, -1) * scale + attn_bias = torch.zeros_like(attn_weight, dtype=query.dtype, device=query.device) + if is_causal: + assert atten_mask is None + temp_mask = torch.zeros_like(attn_weight, dtype=torch.bool, device=query.device).tril(diagonal=0) + attn_bias.masked_fill_(temp_mask.logical_not(), npu_config.inf_float) + attn_bias.to(query.dtype) + + if atten_mask is not None: + assert (not self.enable_FA) and atten_mask.dtype != torch.bool, \ + "attention_mask must not be bool type when use this function" + + attn_weight += attn_bias + attn_weight = torch.softmax(attn_weight, dim=-1) + attn_weight = torch.dropout(attn_weight, dropout_p, train=True) + output = attn_weight @ value + if input_layout == "BSH": + output = output.transpose(1, 2).contiguous().view(output.shape[0], -1, head_num * output.shape[-1]) + else: + output = output.view(output.shape[0] * head_num, -1, output.shape[-1]).transpose(0, 1).contiguous() + output = output.view(output.shape[0], -1, head_num * output.shape[-1]) + return output + + def print_tensor_with_rank(self, name, tensor, rank=[0], dim_print_cnt=[]): + if type(rank) is not list: + rank = [rank] + if self.rank in rank: + def print_dim(tensor_, indices): + if tensor_.dim() == len(indices): + return '{0:10.5f} '.format(tensor[tuple(indices)].detach().item()) + else: + cur_dim = len(indices) + ret = '' + for x in range(0, tensor_.size(cur_dim), tensor_.size(cur_dim) // dim_print_cnt[cur_dim]): + ret += print_dim(tensor_, indices + [x]) + return ret + '\n' + + print(name, tensor.size(), self.rank, '\n', print_dim(tensor, [])) + + +npu_config = NPUConfig() diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 77d2b96a5..f95450ed8 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -3,6 +3,7 @@ import torch import argparse import torchvision +import torch.distributed as dist from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, EulerDiscreteScheduler, DPMSolverMultistepScheduler, @@ -27,111 +28,96 @@ from opensora.sample.pipeline_opensora import OpenSoraPipeline import imageio - - -def main(args): - # torch.manual_seed(args.seed) - weight_dtype = torch.float16 - device = torch.device(args.device) - - vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) - # vae = getae_wrapper(args.ae)(args.ae_path) - vae.vae = vae.vae.to(device=device, dtype=weight_dtype) - if args.enable_tiling: - vae.vae.enable_tiling() - vae.vae.tile_overlap_factor = args.tile_overlap_factor - vae.vae_scale_factor = ae_stride_config[args.ae] - +try: + import torch_npu +except: + pass +import time +from opensora.npu_config import npu_config + +def load_t2v_checkpoint(model_path): if args.model_3d: - transformer_model = OpenSoraT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = OpenSoraT2V.from_pretrained(model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: - transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - # transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = LatteT2V.from_pretrained(model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + print(transformer_model.config) - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) - tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - - # set eval mode transformer_model.eval() - vae.eval() - text_encoder.eval() - - if args.sample_method == 'DDIM': ######### - scheduler = DDIMScheduler() - elif args.sample_method == 'EulerDiscrete': - scheduler = EulerDiscreteScheduler() - elif args.sample_method == 'DDPM': ############# - scheduler = DDPMScheduler() - elif args.sample_method == 'DPMSolverMultistep': - scheduler = DPMSolverMultistepScheduler() - elif args.sample_method == 'DPMSolverSinglestep': - scheduler = DPMSolverSinglestepScheduler() - elif args.sample_method == 'PNDM': - scheduler = PNDMScheduler() - elif args.sample_method == 'HeunDiscrete': ######## - scheduler = HeunDiscreteScheduler() - elif args.sample_method == 'EulerAncestralDiscrete': - scheduler = EulerAncestralDiscreteScheduler() - elif args.sample_method == 'DEISMultistep': - scheduler = DEISMultistepScheduler() - elif args.sample_method == 'KDPM2AncestralDiscrete': ######### - scheduler = KDPM2AncestralDiscreteScheduler() - pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, scheduler=scheduler, - transformer=transformer_model) - pipeline.to(device) + transformer=transformer_model).to(device) - - if not os.path.exists(args.save_img_path): - os.makedirs(args.save_img_path) - + return pipeline + +def get_latest_path(): + # Get the most recent checkpoint + dirs = os.listdir(args.model_path) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + return path +def run_model_and_save_images(pipeline, model_path): + video_grids = [] if not isinstance(args.text_prompt, list): args.text_prompt = [args.text_prompt] if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): text_prompt = open(args.text_prompt[0], 'r').readlines() - text_prompt = [i.strip() for i in text_prompt] + args.text_prompt = [i.strip() for i in text_prompt] - video_grids = [] - for idx, prompt in enumerate(text_prompt): + checkpoint_name = f"{os.path.basename(model_path)}" + + for index, prompt in enumerate(args.text_prompt): + if index % npu_config.N_NPU_PER_NODE != local_rank: + continue + print('Processing the ({}) prompt'.format(prompt)) videos = pipeline(prompt, - num_frames=args.num_frames, - height=args.height, - width=args.width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - num_images_per_prompt=1, - mask_feature=True, - device=args.device, - max_sequence_length=100, - ).images + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device, + max_sequence_length=100, + ).images + print(videos.shape) try: if args.num_frames == 1: - ext = 'jpg' videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w - save_image(videos / 255.0, os.path.join(args.save_img_path, f'{idx}.{ext}'), nrow=1, normalize=True, value_range=(0, 1)) # t c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, + f'{args.sample_method}_{index}_{checkpoint_name}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=1, normalize=True, value_range=(0, 1)) # t c h w else: - ext = 'mp4' imageio.mimwrite( - os.path.join(args.save_img_path, f'{idx}.{ext}'), videos[0], fps=args.fps, quality=6) # highest quality is 10, lowest is 0 + os.path.join( + args.save_img_path, f'{args.sample_method}_{index}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' + ), videos[0], + fps=args.fps, quality=9, codec='libx264', output_params=['-threads', '20']) # highest quality is 10, lowest is 0 except: print('Error when saving {}'.format(prompt)) video_grids.append(videos) - video_grids = torch.cat(video_grids, dim=0) + video_grids = torch.cat(video_grids, dim=0).cuda() + shape = list(video_grids.shape) + shape[0] *= world_size + gathered_tensor = torch.zeros(shape, dtype=video_grids.dtype, device=device) + dist.all_gather_into_tensor(gathered_tensor, video_grids) + video_grids = gathered_tensor.cpu() + def get_file_name(): + return os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') - # torchvision.io.write_video(args.save_img_path + '_%04d' % args.run_time + '-.mp4', video_grids, fps=6) if args.num_frames == 1: - save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + save_image(video_grids / 255.0, get_file_name(), nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) else: video_grids = save_video_grid(video_grids) - imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=9) + imageio.mimwrite(get_file_name(), video_grids, fps=args.fps, quality=9) print('save path {}'.format(args.save_img_path)) @@ -160,4 +146,101 @@ def main(args): parser.add_argument('--model_3d', action='store_true') args = parser.parse_args() - main(args) \ No newline at end of file + npu_config.print_msg(args) + + # 初始化分布式环境 + local_rank = int(os.getenv('RANK', 0)) + world_size = int(os.getenv('WORLD_SIZE', 1)) + if npu_config.on_npu: + torch_npu.npu.set_device(local_rank) + dist.init_process_group(backend='hccl', init_method='env://', world_size=8, rank=local_rank) + else: + dist.init_process_group(backend='nccl', init_method='env://', world_size=8, rank=local_rank) + + # torch.manual_seed(args.seed) + weight_dtype = torch.float16 + device = torch.cuda.current_device() + + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] + + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, + low_cpu_mem_usage=True, torch_dtype=weight_dtype).to(device) + tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # set eval mode + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler() + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler() + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + + + if not os.path.exists(args.save_img_path): + os.makedirs(args.save_img_path, exist_ok=True) + + if args.num_frames == 1: + video_length = 1 + ext = 'jpg' + else: + ext = 'mp4' + + latest_path = None + save_img_path = args.save_img_path + while True: + cur_path = get_latest_path() + if cur_path == latest_path: + time.sleep(80) + continue + + time.sleep(60) + latest_path = cur_path + npu_config.print_msg(f"The latest_path is {latest_path}") + full_path = f"{args.model_path}/{latest_path}/model" + pipeline = load_t2v_checkpoint(full_path) + + if npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = "/home/image_data/shebin/npu_profiling_t2v" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=10000, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + run_model_and_save_images(pipeline, latest_path) + prof.step() + else: + run_model_and_save_images(pipeline, latest_path) \ No newline at end of file diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 52bbf09ea..a6139a846 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -18,6 +18,12 @@ import numpy as np from einops import rearrange from tqdm import tqdm +try: + import torch_npu +except: + pass +from opensora.npu_config import npu_config +import time from dataclasses import field, dataclass from torch.utils.data import DataLoader from copy import deepcopy @@ -84,7 +90,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh enable_temporal_attentions=True, num_images_per_prompt=1, mask_feature=True, - max_sequence_length=50, + max_sequence_length=50, ).images videos.append(video[0]) # import ipdb;ipdb.set_trace() @@ -96,7 +102,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh if tracker.name == "tensorboard": if videos.shape[1] == 1: assert args.num_frames == 1 - images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') np_images = np.stack([np.asarray(img) for img in images]) tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") else: @@ -106,7 +112,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh import wandb if videos.shape[1] == 1: # assert args.num_frames == 1 - images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') # import ipdb;ipdb.set_trace() logs = { f"{'ema_' if ema else ''}validation": [ @@ -129,6 +135,11 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh del opensora_pipeline gc.collect() torch.cuda.empty_cache() + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss ################################################################################# # Training Loop # ################################################################################# @@ -136,6 +147,8 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh def main(args): logging_dir = Path(args.output_dir, args.logging_dir) + npu_config.print_msg(args) + # npu_config.seed_everything() accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) accelerator = Accelerator( @@ -192,7 +205,7 @@ def main(args): if args.enable_tiling: ae.vae.enable_tiling() ae.vae.tile_overlap_factor = args.tile_overlap_factor - + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() @@ -240,12 +253,12 @@ def main(args): norm_eps=1e-6, attention_type='default', attention_mode=args.attention_mode, - interpolation_scale_h=args.interpolation_scale_h, - interpolation_scale_w=args.interpolation_scale_w, - interpolation_scale_t=args.interpolation_scale_t, - # compress_kv_factor=args.compress_kv_factor, - # use_rope=args.use_rope, - # model_max_length=args.model_max_length, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + interpolation_scale_t=args.interpolation_scale_t, + # compress_kv_factor=args.compress_kv_factor, + # use_rope=args.use_rope, + # model_max_length=args.model_max_length, ) model.gradient_checkpointing = args.gradient_checkpointing @@ -285,7 +298,7 @@ def main(args): # Create EMA for the unet. if args.use_ema: ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) # `accelerate` 0.16.0 will have better support for customized saving @@ -397,7 +410,7 @@ def load_model_hook(models, input_dir): # We need to initialize the trackers we use, and also store our configuration. # The trackers initializes automatically on the main process. if accelerator.is_main_process: - accelerator.init_trackers(args.output_dir, config=vars(args)) + accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) # Train! total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps @@ -447,176 +460,193 @@ def load_model_hook(models, input_dir): # Only show the progress bar once on each machine. disable=not accelerator.is_local_main_process, ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_model.step(model.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + one_step_duration = end_time - start_time + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + npu_config.print_msg( + f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) + progress_info.train_loss = 0.0 + + if args.use_deepspeed or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(x, model_kwargs, prof): + global start_time + start_time = time.time() + t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) + + if args.snr_gamma is not None: + snr = compute_snr(t, torch.from_numpy(diffusion.alphas_cumprod)) + mse_loss_weights = (torch.stack([snr, args.snr_gamma * torch.ones_like(t)], dim=1).min(dim=1)[0] / snr) + # print(t, mse_loss_weights) + loss_dict = diffusion.training_losses(model, x, t, model_kwargs, mse_loss_weights=mse_loss_weights) + else: + loss_dict = diffusion.training_losses(model, x, t, model_kwargs) + + loss = loss_dict["loss"].mean() + + # Backpropagate + accelerator.backward(loss) + torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1) + if accelerator.sync_gradients: + params_to_clip = model.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + if prof is not None: + prof.step() + + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + for tracker in accelerator.trackers: + if tracker.name == "wandb": + if progress_info.global_step % args.checkpointing_steps != 0: + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + + if progress_info.global_step % args.checkpointing_steps == 0: + + if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step) - for epoch in range(first_epoch, args.num_train_epochs): - train_loss = 0.0 - for step, (x, attn_mask, input_ids, cond_mask) in enumerate(train_dataloader): - with accelerator.accumulate(model): - # Sample noise that we'll add to the latents - - if not args.multi_scale: - assert torch.all(attn_mask) - x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 - attn_mask = attn_mask.to(accelerator.device) # B T+num_images L - input_ids = input_ids.to(accelerator.device) # B 1+num_images L - cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L - # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) - - with torch.no_grad(): - # import ipdb;ipdb.set_trace() - # use for loop to avoid OOM, because T5 is too huge... - B, N, L = input_ids.shape # B 1+num_images L - # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D - - # use batch inference - input_ids_ = input_ids.reshape(-1, L) - cond_mask_ = cond_mask.reshape(-1, L) - cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D - cond = cond.reshape(B, N, L, -1) - - # Map input images to latent space + normalize latents - if args.use_image_num == 0: - x = ae.encode(x) # B C T H W - else: - videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] - videos = ae.encode(videos) # B C T H W - - - def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'output_video.mp4') -> None: - from examples.rec_imvi_vae import array_to_video - x = x.detach().cpu() - x = torch.clamp(x, -1, 1) - x = (x + 1) / 2 - x = x.permute(1, 2, 3, 0).numpy() - x = (255*x).astype(np.uint8) - array_to_video(x, fps=fps, output_file=output_file) - return - - # videos = ae.decode(videos.to(dtype=weight_dtype))[0] - # videos = videos.transpose(0, 1) - # custom_to_video(videos.to(torch.float32), fps=24, output_file='tmp.mp4') - # sys.exit() - - images = rearrange(images, 'b c t h w -> (b t) c 1 h w') - images = ae.encode(images) - - # import ipdb;ipdb.set_trace() - # images = ae.decode(images.to(dtype=weight_dtype)) - # for idx in range(args.use_image_num): - # x = images[idx, 0, :, :, :].to(torch.float32) - # x = x.squeeze() - # x = x.detach().cpu().numpy() - # x = np.clip(x, -1, 1) - # x = (x + 1) / 2 - # x = (255 * x).astype(np.uint8) - # x = x.transpose(1, 2, 0) - # from PIL import Image - # image = Image.fromarray(x) - # image.save(f'tmp{idx}.jpg') - # import sys - # sys.exit() - - - images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) - x = torch.cat([videos, images], dim=2) # b c 17+4, h, w - - - - # print('(x.shape, attn_mask.shape, cond.shape, cond_mask.shape', x.shape, attn_mask.shape, cond.shape, cond_mask.shape) - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) - t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) - - - # loss_dict = diffusion.training_losses(model, x, t, model_kwargs) - if args.snr_gamma is not None: - snr = compute_snr(t, torch.from_numpy(diffusion.alphas_cumprod)) - mse_loss_weights = (torch.stack([snr, args.snr_gamma * torch.ones_like(t)], dim=1).min(dim=1)[0] / snr) - # print(t, mse_loss_weights) - loss_dict = diffusion.training_losses(model, x, t, model_kwargs, mse_loss_weights=mse_loss_weights) - else: - loss_dict = diffusion.training_losses(model, x, t, model_kwargs) - - - loss = loss_dict["loss"].mean() - - # Gather the losses across all processes for logging (if we use distributed training). - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - train_loss += avg_loss.item() / args.gradient_accumulation_steps - - # Backpropagate - accelerator.backward(loss) - if accelerator.sync_gradients: - params_to_clip = model.parameters() - accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) - optimizer.step() - lr_scheduler.step() - optimizer.zero_grad() - - # Checks if the accelerator has performed an optimization step behind the scenes - if accelerator.sync_gradients: if args.use_ema: - ema_model.step(model.parameters()) - progress_bar.update(1) - global_step += 1 - accelerator.log({"train_loss": train_loss}, step=global_step) - train_loss = 0.0 - - if args.use_deepspeed or accelerator.is_main_process: - if global_step % args.checkpointing_steps == 0: - # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` - if args.checkpoints_total_limit is not None: - checkpoints = os.listdir(args.output_dir) - checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] - checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) - - # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints - if len(checkpoints) >= args.checkpoints_total_limit: - num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 - removing_checkpoints = checkpoints[0:num_to_remove] - - logger.info( - f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" - ) - logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") - - for removing_checkpoint in removing_checkpoints: - removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) - shutil.rmtree(removing_checkpoint) - - save_path = os.path.join(args.output_dir, f"checkpoint-{global_step}") - accelerator.save_state(save_path) - logger.info(f"Saved state to {save_path}") - - logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} - progress_bar.set_postfix(**logs) - - if global_step >= args.max_train_steps: - break - - if accelerator.is_main_process: - - for tracker in accelerator.trackers: - if tracker.name == "wandb": - if global_step % args.checkpointing_steps != 0: - if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): - tracker.log({'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) - elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): - tracker.log({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) - - if global_step % args.checkpointing_steps == 0: - - if args.enable_tracker: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step) - - if args.use_ema: - # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step, ema=True) - # Switch back to the original UNet parameters. - ema_model.restore(model.parameters()) - + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + + return loss + def train_one_step(step_, data_item_, prof_=None): + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 + attn_mask = attn_mask.to(accelerator.device) # B T+num_images L + input_ids = input_ids.to(accelerator.device) # B 1+num_images L + cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1+num_images L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D + cond = cond.reshape(B, N, L, -1) + + # Map input images to latent space + normalize latents + if args.use_image_num == 0: + x = ae.encode(x) # B C T H W + else: + videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] + videos = ae.encode(videos) # B C T H W + images = rearrange(images, 'b c t h w -> (b t) c 1 h w') + images = ae.encode(images) + images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + + # print('(x.shape, attn_mask.shape, cond.shape, cond_mask.shape', x.shape, attn_mask.shape, cond.shape, cond_mask.shape) + with accelerator.accumulate(model): + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + + if train_one_step(step, data_item, prof_): + break + + if step >= 2: + npu_config.free_mm() + + if npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/shebin/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() accelerator.wait_for_everyone() accelerator.end_training() diff --git a/scripts/accelerate_configs/multi_node_example_on_npu.yaml b/scripts/accelerate_configs/multi_node_example_on_npu.yaml new file mode 100644 index 000000000..17fd420ac --- /dev/null +++ b/scripts/accelerate_configs/multi_node_example_on_npu.yaml @@ -0,0 +1,13 @@ +compute_environment: LOCAL_MACHINE +distributed_type: MULTI_GPU +fsdp_config: {} +main_process_port: 29501 +main_training_function: main +num_machines: 2 +num_processes: 16 +rdzv_backend: static +same_network: true +tpu_env: [] +tpu_use_cluster: false +tpu_use_sudo: false +use_cpu: false \ No newline at end of file diff --git a/scripts/text_condition/sample_video_on_npu.sh b/scripts/text_condition/sample_video_on_npu.sh new file mode 100644 index 000000000..4686e8577 --- /dev/null +++ b/scripts/text_condition/sample_video_on_npu.sh @@ -0,0 +1,22 @@ +WEIGHT_PATH="/home/opensora/shebin/pre_weights/" + +export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" +export MASTER_PORT=12359 + +torchrun --nproc_per_node=8 opensora/sample/sample_t2v.py \ + --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ + --version ${NUM_FRAME}x512x512 \ + --num_frames 1 \ + --height 512 \ + --width 512 \ + --cache_dir "./cache_dir" \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --text_prompt examples/prompt_list_0.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --save_img_path "/home/image_data/yancen/sample_videos/${PROJECT_NAME}" \ + --fps 24 \ + --guidance_scale 4.0 \ + --num_sampling_steps 50 \ + --enable_tiling \ + --model_3d diff --git a/scripts/text_condition/train_video21d_65x240p_on_npu.sh b/scripts/text_condition/train_video21d_65x240p_on_npu.sh new file mode 100644 index 000000000..f7c44206b --- /dev/null +++ b/scripts/text_condition/train_video21d_65x240p_on_npu.sh @@ -0,0 +1,46 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +env +export WANDB_MODE='offline' + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v.py \ + --model LatteT2V-XL/122 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=20 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --pretrained "${WEIGHT_PATH}/t2v.pt" \ No newline at end of file diff --git a/scripts/text_condition/train_video3d_65x240p_on_npu.sh b/scripts/text_condition/train_video3d_65x240p_on_npu.sh new file mode 100644 index 000000000..fa6db6654 --- /dev/null +++ b/scripts/text_condition/train_video3d_65x240p_on_npu.sh @@ -0,0 +1,46 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +env +export WANDB_MODE='offline' + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-S/122 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=20 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --pretrained /home/opensora/yancen/Open-Sora-Plan-dev/PixArt-alpha/transformer/diffusion_pytorch_model.safetensors diff --git a/scripts/train_data/image_data_on_npu.txt b/scripts/train_data/image_data_on_npu.txt new file mode 100644 index 000000000..382fd914c --- /dev/null +++ b/scripts/train_data/image_data_on_npu.txt @@ -0,0 +1,4 @@ +/home/local_dataset/image_data_obs/AnyWord-3M/,/home/opensora/captions/linbin_captions/anytext_en_1886137.json +/home/local_dataset_n63/image_data_obs/mj_unzip_files,/home/opensora/captions/linbin_captions/tuzhan_mj_1712571.json +/home/local_dataset_n63/image_data_obs/sa_unzip_files,/home/opensora/captions/linbin_captions/sam_image_11185255.json +/home/local_dataset_n63/image_data_obs/images/,/home/opensora/captions/linbin_captions/human_images_162094.json \ No newline at end of file diff --git a/scripts/train_data/video_data_on_npu.txt b/scripts/train_data/video_data_on_npu.txt new file mode 100644 index 000000000..2f4e96f5a --- /dev/null +++ b/scripts/train_data/video_data_on_npu.txt @@ -0,0 +1,3 @@ +/home/local_dataset_n63/video_data_obs/pexel,/home/opensora/captions/linbin_captions/video_pexels_513f_271782.json +/home/local_dataset_n63/video_data_obs/pixabay_v2,/home/opensora/captions/linbin_captions/video_pixabay_513f_51483.json +/home/local_dataset_n63/video_data_obs/mixkit_resize1024/mixkit/,/home/opensora/captions/linbin_captions/video_mixkit_513f_1997.json \ No newline at end of file From 87ee95fc45ab349b9c154a9bce8858b9b16e2c83 Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Sat, 1 Jun 2024 15:57:47 +0800 Subject: [PATCH 018/134] check gpu --- .../causal_vae/modeling_causalvae.py | 30 ++- .../models/ae/videobase/modules/attention.py | 13 +- opensora/models/ae/videobase/modules/conv.py | 9 +- .../ae/videobase/modules/resnet_block.py | 19 +- .../ae/videobase/modules/updownsample.py | 21 +- .../models/diffusion/latte/modeling_latte.py | 13 +- opensora/models/diffusion/latte/modules.py | 17 +- .../diffusion/opensora/modeling_opensora.py | 11 +- opensora/models/diffusion/opensora/modules.py | 9 +- opensora/npu_config.py | 2 +- opensora/sample/sample_t2v.py | 233 ++++++------------ opensora/train/train_t2v.py | 21 +- scripts/text_condition/sample_video.sh | 16 -- scripts/text_condition/sample_video_on_npu.sh | 4 +- .../text_condition/train_video21d_65x240p.sh | 4 +- .../text_condition/train_video3d_65x240p.sh | 10 +- 16 files changed, 196 insertions(+), 236 deletions(-) delete mode 100644 scripts/text_condition/sample_video.sh diff --git a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py index 7af7c659e..f2101fcd0 100644 --- a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py +++ b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py @@ -1,4 +1,9 @@ -from opensora.npu_config import npu_config +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None from ..modeling_videobase import VideoBaseAE_PL from ..modules import Normalize from ..modules.ops import nonlinearity @@ -129,9 +134,11 @@ def forward(self, x): h = self.mid.block_1(h) h = self.mid.attn_1(h) h = self.mid.block_2(h) - - # h = self.norm_out(h) - h = npu_config.run_group_norm(self.norm_out, h) + + if npu_config is None: + h = self.norm_out(h) + else: + h = npu_config.run_group_norm(self.norm_out, h) h = nonlinearity(h) h = self.conv_out(h) return h @@ -244,13 +251,16 @@ def forward(self, z): h = self.up[i_level].upsample(h) if hasattr(self.up[i_level], "time_upsample"): h = self.up[i_level].time_upsample(h) - - # h = self.norm_out(h) - h = npu_config.run_group_norm(self.norm_out, h) + if npu_config is None: + h = self.norm_out(h) + else: + h = npu_config.run_group_norm(self.norm_out, h) h = nonlinearity(h) - # h = self.conv_out(h) - h_dtype = h.dtype - h = npu_config.run_conv3d(self.conv_out, h, h_dtype) + if npu_config is None: + h = self.conv_out(h) + else: + h_dtype = h.dtype + h = npu_config.run_conv3d(self.conv_out, h, h_dtype) return h diff --git a/opensora/models/ae/videobase/modules/attention.py b/opensora/models/ae/videobase/modules/attention.py index 0ee64d55c..73c4c3793 100644 --- a/opensora/models/ae/videobase/modules/attention.py +++ b/opensora/models/ae/videobase/modules/attention.py @@ -1,5 +1,10 @@ import torch.nn as nn -from opensora.npu_config import npu_config +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None from .normalize import Normalize from .conv import CausalConv3d import torch @@ -92,8 +97,10 @@ def __init__(self, in_channels): def forward(self, x): h_ = x - # h_ = self.norm(h_) - h_ = npu_config.run_group_norm(self.norm, h_) + if npu_config is None: + h_ = self.norm(h_) + else: + h_ = npu_config.run_group_norm(self.norm, h_) q = self.q(h_) k = self.k(h_) v = self.v(h_) diff --git a/opensora/models/ae/videobase/modules/conv.py b/opensora/models/ae/videobase/modules/conv.py index 43d85f2c5..7f786c318 100644 --- a/opensora/models/ae/videobase/modules/conv.py +++ b/opensora/models/ae/videobase/modules/conv.py @@ -6,7 +6,12 @@ from .ops import cast_tuple from einops import rearrange from .ops import video_to_image -from opensora.npu_config import npu_config +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None class Conv2d(nn.Conv2d): @@ -93,7 +98,7 @@ def _init_weights(self, init_method): nn.init.constant_(self.conv.bias, 0) def forward(self, x): - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: x_dtype = x.dtype x = x.to(torch.float16) n, c, d, h, w = x.shape diff --git a/opensora/models/ae/videobase/modules/resnet_block.py b/opensora/models/ae/videobase/modules/resnet_block.py index 8691ec0a3..1553832dc 100644 --- a/opensora/models/ae/videobase/modules/resnet_block.py +++ b/opensora/models/ae/videobase/modules/resnet_block.py @@ -5,7 +5,12 @@ from .ops import nonlinearity, video_to_image from .conv import CausalConv3d from .block import Block -from opensora.npu_config import npu_config +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None class ResnetBlock2D(Block): @@ -73,12 +78,16 @@ def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, dropo def forward(self, x): h = x - # h = self.norm1(h) - h = npu_config.run_group_norm(self.norm1, h) + if npu_config is None: + h = self.norm1(h) + else: + h = npu_config.run_group_norm(self.norm1, h) h = nonlinearity(h) h = self.conv1(h) - # h = self.norm2(h) - h = npu_config.run_group_norm(self.norm2, h) + if npu_config is None: + h = self.norm2(h) + else: + h = npu_config.run_group_norm(self.norm2, h) h = nonlinearity(h) h = self.dropout(h) h = self.conv2(h) diff --git a/opensora/models/ae/videobase/modules/updownsample.py b/opensora/models/ae/videobase/modules/updownsample.py index f4e542dbe..ea54252b4 100644 --- a/opensora/models/ae/videobase/modules/updownsample.py +++ b/opensora/models/ae/videobase/modules/updownsample.py @@ -9,7 +9,12 @@ from .conv import CausalConv3d from einops import rearrange from .block import Block -from opensora.npu_config import npu_config +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None class Upsample(Block): @@ -45,7 +50,7 @@ def __init__(self, in_channels, out_channels): def forward(self, x): if self.with_conv: pad = (0, 1, 0, 1) - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: x_dtype = x.dtype x = x.to(torch.bfloat16) x = torch.nn.functional.pad(x, pad, mode="constant", value=0) @@ -123,14 +128,14 @@ def __init__( ): super().__init__() self.kernel_size = kernel_size - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: self.avg_pool = nn.AvgPool2d((kernel_size, 1), stride=(2, 1)) self.pad = nn.ReplicationPad3d((0, 0, 0, 0, self.kernel_size - 1, 0)) else: self.avg_pool = nn.AvgPool3d((kernel_size, 1, 1), stride=(2, 1, 1)) def forward(self, x): - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: n, c, d, h, w = x.shape x = self.pad(x) x = x.view(n * c, -1, h * w) @@ -168,7 +173,7 @@ def __init__( ): super().__init__() self.kernel_size = cast_tuple(kernel_size, 3) - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: self.avg_pool = nn.AvgPool2d((kernel_size, 1), stride=(2, 1)) self.pad = nn.ReplicationPad3d((0, 0, 0, 0, kernel_size - 1, 0)) else: @@ -180,7 +185,7 @@ def __init__( def forward(self, x): alpha = torch.sigmoid(self.mix_factor) - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: n, c, d, h, w = x.shape x_dtype = x.dtype x = x.to(torch.float16) @@ -214,7 +219,7 @@ def forward(self, x): alpha = torch.sigmoid(self.mix_factor) if x.size(2) > 1: x,x_= x[:,:,:1],x[:,:,1:] - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: x_dtype = x_.dtype x_ = x_.to(torch.float16) x_ = F.interpolate(x_, scale_factor=(2, 1, 1), mode='trilinear') @@ -270,7 +275,7 @@ def __init__( def forward(self, x): if x.size(2) > 1: x,x_= x[:,:,:1],x[:,:,1:] - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: x_dtype = x_.dtype x_ = x_.to(torch.float16) x_= F.interpolate(x_, scale_factor=(2,1,1), mode='trilinear') diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index 51401622a..d4bb469f5 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -13,7 +13,12 @@ from diffusers.models.modeling_utils import ModelMixin from diffusers.models.lora import LoRACompatibleConv, LoRACompatibleLinear from opensora.utils.utils import to_2tuple -from opensora.npu_config import npu_config +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None import torch import torch.nn.functional as F from torch import nn @@ -279,7 +284,8 @@ def make_attn_mask(self, attention_mask, frame, dtype): # (keep = +0, discard = -10000.0) attention_mask = (1 - attention_mask.to(dtype)) * -10000.0 attention_mask = attention_mask.to(self.dtype) - attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) + if npu_config is not None: + attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) return attention_mask def vae_to_diff_mask(self, attention_mask, use_image_num): @@ -383,7 +389,8 @@ def forward( encoder_attention_mask = rearrange(encoder_attention_mask, 'b n l -> (b n) l').contiguous().unsqueeze(1) encoder_attention_mask = encoder_attention_mask.to(self.dtype) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-2]) + if npu_config is not None: + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-2]) # Retrieve lora scale. lora_scale = cross_attention_kwargs.get("scale", 1.0) if cross_attention_kwargs is not None else 1.0 diff --git a/opensora/models/diffusion/latte/modules.py b/opensora/models/diffusion/latte/modules.py index f87b38934..40ff6aef9 100644 --- a/opensora/models/diffusion/latte/modules.py +++ b/opensora/models/diffusion/latte/modules.py @@ -23,9 +23,11 @@ from opensora.models.diffusion.utils.pos_embed import RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D try: import torch_npu + from opensora.npu_config import npu_config, set_run_dtype except: - pass -from opensora.npu_config import npu_config, set_run_dtype + torch_npu = None + npu_config = None + set_run_dtype = None if is_xformers_available(): import xformers import xformers.ops @@ -870,7 +872,7 @@ def prepare_attention_mask( # remaining_length: int = target_length - current_length # TODO: re-enable tests/models/test_models_unet_2d_condition.py#test_model_xattn_padding attention_mask = F.pad(attention_mask, (0, target_length), value=0.0) - if not npu_config.on_npu: + if npu_config is None or not npu_config.on_npu: if out_dim == 3: if attention_mask.shape[0] < batch_size * head_size: attention_mask = attention_mask.repeat_interleave(head_size, dim=0) @@ -999,11 +1001,12 @@ def __call__( hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape ) + # import ipdb;ipdb.set_trace() if attention_mask is not None: attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) # scaled_dot_product_attention expects attention_mask shape to be # (batch, heads, source_length, target_length) - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) else: attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) @@ -1026,7 +1029,7 @@ def __call__( inner_dim = key.shape[-1] head_dim = inner_dim // attn.heads - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: if npu_config.enable_FA and query.dtype == torch.float32: dtype = torch.bfloat16 else: @@ -1394,7 +1397,7 @@ def forward( # 2. Prepare GLIGEN inputs cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} gligen_kwargs = cross_attention_kwargs.pop("gligen", None) - + # import ipdb;ipdb.set_trace() attn_output = self.attn1( norm_hidden_states, encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, @@ -1689,7 +1692,7 @@ def forward( # 2. Prepare GLIGEN inputs cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} gligen_kwargs = cross_attention_kwargs.pop("gligen", None) - + # import ipdb;ipdb.set_trace() attn_output = self.attn1( norm_hidden_states, encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index c17672717..21f244bfa 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -13,7 +13,12 @@ from diffusers.models.embeddings import PixArtAlphaTextProjection from opensora.models.diffusion.opensora.modules import PatchEmbed3D, PatchEmbed2D, BasicTransformerBlock from opensora.utils.utils import to_2tuple -from opensora.npu_config import npu_config +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None class OpenSoraT2V(ModelMixin, ConfigMixin): """ A 2D Transformer model for image-like data. @@ -344,11 +349,11 @@ def forward( encoder_attention_mask_img = encoder_attention_mask_vid encoder_attention_mask_vid = None - if attention_mask_vid is not None: + if npu_config is not None and attention_mask_vid is not None: attention_mask_vid = npu_config.get_attention_mask(attention_mask_vid, attention_mask_vid.shape[-1]) encoder_attention_mask_vid = npu_config.get_attention_mask(encoder_attention_mask_vid, attention_mask_vid.shape[-2]) - if attention_mask_img is not None: + if npu_config is not None and attention_mask_img is not None: attention_mask_img = npu_config.get_attention_mask(attention_mask_img, attention_mask_img.shape[-1]) encoder_attention_mask_img = npu_config.get_attention_mask(encoder_attention_mask_img, attention_mask_img.shape[-2]) diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index fc6746839..5d2c9aed2 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -20,9 +20,11 @@ from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm try: import torch_npu + from opensora.npu_config import npu_config, set_run_dtype except: - pass -from opensora.npu_config import npu_config, set_run_dtype + torch_npu = None + npu_config = None + set_run_dtype = None logger = logging.get_logger(__name__) def get_3d_sincos_pos_embed( @@ -235,6 +237,7 @@ def forward(self, latent, num_frames): # import ipdb;ipdb.set_trace() temp_embed = temp_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + # temp_embed = temp_embed.unsqueeze(2) # print('raw value:', self.temp_embed_gate, 'tanh:', self.temp_embed_gate.tanh()) video_latent = (video_latent + temp_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None image_latent = (image_latent + temp_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None @@ -426,7 +429,7 @@ def __call__( inner_dim = key.shape[-1] head_dim = inner_dim // attn.heads - if npu_config.on_npu: + if npu_config is not None and npu_config.on_npu: if npu_config.enable_FA and query.dtype == torch.float32: dtype = torch.bfloat16 else: diff --git a/opensora/npu_config.py b/opensora/npu_config.py index f91ac0c91..ec39b998c 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -16,7 +16,7 @@ npu_is_available = False from contextlib import contextmanager -from opensora.acceleration.parallel_states import enable_LCCL, hccl_info, lccl_info +# from opensora.acceleration.parallel_states import enable_LCCL, hccl_info, lccl_info def compress_video(input_file, output_file, out_size): diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index f95450ed8..3e1fc3372 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -3,7 +3,6 @@ import torch import argparse import torchvision -import torch.distributed as dist from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, EulerDiscreteScheduler, DPMSolverMultistepScheduler, @@ -28,96 +27,111 @@ from opensora.sample.pipeline_opensora import OpenSoraPipeline import imageio -try: - import torch_npu -except: - pass -import time -from opensora.npu_config import npu_config - -def load_t2v_checkpoint(model_path): + + +def main(args): + # torch.manual_seed(args.seed) + weight_dtype = torch.float16 + device = torch.device(args.device) + + vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + # vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] + if args.model_3d: - transformer_model = OpenSoraT2V.from_pretrained(model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = OpenSoraT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: - transformer_model = LatteT2V.from_pretrained(model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - print(transformer_model.config) + transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # set eval mode transformer_model.eval() + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler(clip_sample=False) + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler(clip_sample=False) + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, scheduler=scheduler, - transformer=transformer_model).to(device) - - return pipeline + transformer=transformer_model) + pipeline.to(device) -def get_latest_path(): - # Get the most recent checkpoint - dirs = os.listdir(args.model_path) - dirs = [d for d in dirs if d.startswith("checkpoint")] - dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) - path = dirs[-1] if len(dirs) > 0 else None - - return path -def run_model_and_save_images(pipeline, model_path): - video_grids = [] + + if not os.path.exists(args.save_img_path): + os.makedirs(args.save_img_path) + if not isinstance(args.text_prompt, list): args.text_prompt = [args.text_prompt] if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): text_prompt = open(args.text_prompt[0], 'r').readlines() - args.text_prompt = [i.strip() for i in text_prompt] - - checkpoint_name = f"{os.path.basename(model_path)}" + text_prompt = [i.strip() for i in text_prompt] - for index, prompt in enumerate(args.text_prompt): - if index % npu_config.N_NPU_PER_NODE != local_rank: - continue - print('Processing the ({}) prompt'.format(prompt)) + video_grids = [] + for idx, prompt in enumerate(text_prompt): videos = pipeline(prompt, - num_frames=args.num_frames, - height=args.height, - width=args.width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - num_images_per_prompt=1, - mask_feature=True, - device=args.device, - max_sequence_length=100, - ).images - print(videos.shape) + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device, + max_sequence_length=100, + ).images try: if args.num_frames == 1: + ext = 'jpg' videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w - save_image(videos / 255.0, os.path.join(args.save_img_path, - f'{args.sample_method}_{index}_{checkpoint_name}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), - nrow=1, normalize=True, value_range=(0, 1)) # t c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, f'{idx}.{ext}'), nrow=1, normalize=True, value_range=(0, 1)) # t c h w else: + ext = 'mp4' imageio.mimwrite( - os.path.join( - args.save_img_path, f'{args.sample_method}_{index}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' - ), videos[0], - fps=args.fps, quality=9, codec='libx264', output_params=['-threads', '20']) # highest quality is 10, lowest is 0 + os.path.join(args.save_img_path, f'{idx}.{ext}'), videos[0], fps=args.fps, quality=6) # highest quality is 10, lowest is 0 except: print('Error when saving {}'.format(prompt)) video_grids.append(videos) + video_grids = torch.cat(video_grids, dim=0) - video_grids = torch.cat(video_grids, dim=0).cuda() - shape = list(video_grids.shape) - shape[0] *= world_size - gathered_tensor = torch.zeros(shape, dtype=video_grids.dtype, device=device) - dist.all_gather_into_tensor(gathered_tensor, video_grids) - video_grids = gathered_tensor.cpu() - def get_file_name(): - return os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') + # torchvision.io.write_video(args.save_img_path + '_%04d' % args.run_time + '-.mp4', video_grids, fps=6) if args.num_frames == 1: - save_image(video_grids / 255.0, get_file_name(), + save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) else: video_grids = save_video_grid(video_grids) - imageio.mimwrite(get_file_name(), video_grids, fps=args.fps, quality=9) + imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=9) print('save path {}'.format(args.save_img_path)) @@ -146,101 +160,4 @@ def get_file_name(): parser.add_argument('--model_3d', action='store_true') args = parser.parse_args() - npu_config.print_msg(args) - - # 初始化分布式环境 - local_rank = int(os.getenv('RANK', 0)) - world_size = int(os.getenv('WORLD_SIZE', 1)) - if npu_config.on_npu: - torch_npu.npu.set_device(local_rank) - dist.init_process_group(backend='hccl', init_method='env://', world_size=8, rank=local_rank) - else: - dist.init_process_group(backend='nccl', init_method='env://', world_size=8, rank=local_rank) - - # torch.manual_seed(args.seed) - weight_dtype = torch.float16 - device = torch.cuda.current_device() - - # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) - vae = getae_wrapper(args.ae)(args.ae_path) - vae.vae = vae.vae.to(device=device, dtype=weight_dtype) - if args.enable_tiling: - vae.vae.enable_tiling() - vae.vae.tile_overlap_factor = args.tile_overlap_factor - vae.vae_scale_factor = ae_stride_config[args.ae] - - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, - low_cpu_mem_usage=True, torch_dtype=weight_dtype).to(device) - tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - - # set eval mode - vae.eval() - text_encoder.eval() - - if args.sample_method == 'DDIM': ######### - scheduler = DDIMScheduler() - elif args.sample_method == 'EulerDiscrete': - scheduler = EulerDiscreteScheduler() - elif args.sample_method == 'DDPM': ############# - scheduler = DDPMScheduler() - elif args.sample_method == 'DPMSolverMultistep': - scheduler = DPMSolverMultistepScheduler() - elif args.sample_method == 'DPMSolverSinglestep': - scheduler = DPMSolverSinglestepScheduler() - elif args.sample_method == 'PNDM': - scheduler = PNDMScheduler() - elif args.sample_method == 'HeunDiscrete': ######## - scheduler = HeunDiscreteScheduler() - elif args.sample_method == 'EulerAncestralDiscrete': - scheduler = EulerAncestralDiscreteScheduler() - elif args.sample_method == 'DEISMultistep': - scheduler = DEISMultistepScheduler() - elif args.sample_method == 'KDPM2AncestralDiscrete': ######### - scheduler = KDPM2AncestralDiscreteScheduler() - - - if not os.path.exists(args.save_img_path): - os.makedirs(args.save_img_path, exist_ok=True) - - if args.num_frames == 1: - video_length = 1 - ext = 'jpg' - else: - ext = 'mp4' - - latest_path = None - save_img_path = args.save_img_path - while True: - cur_path = get_latest_path() - if cur_path == latest_path: - time.sleep(80) - continue - - time.sleep(60) - latest_path = cur_path - npu_config.print_msg(f"The latest_path is {latest_path}") - full_path = f"{args.model_path}/{latest_path}/model" - pipeline = load_t2v_checkpoint(full_path) - - if npu_config.on_npu and npu_config.profiling: - experimental_config = torch_npu.profiler._ExperimentalConfig( - profiler_level=torch_npu.profiler.ProfilerLevel.Level1, - aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization - ) - profile_output_path = "/home/image_data/shebin/npu_profiling_t2v" - os.makedirs(profile_output_path, exist_ok=True) - - with torch_npu.profiler.profile( - activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], - with_stack=True, - record_shapes=True, - profile_memory=True, - experimental_config=experimental_config, - schedule=torch_npu.profiler.schedule(wait=10000, warmup=0, active=1, repeat=1, - skip_first=0), - on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") - ) as prof: - run_model_and_save_images(pipeline, latest_path) - prof.step() - else: - run_model_and_save_images(pipeline, latest_path) \ No newline at end of file + main(args) \ No newline at end of file diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index a6139a846..ece3cb282 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -20,9 +20,11 @@ from tqdm import tqdm try: import torch_npu + from opensora.npu_config import npu_config except: + torch_npu = None + npu_config = None pass -from opensora.npu_config import npu_config import time from dataclasses import field, dataclass from torch.utils.data import DataLoader @@ -147,7 +149,8 @@ def __init__(self, global_step, train_loss=0.0): def main(args): logging_dir = Path(args.output_dir, args.logging_dir) - npu_config.print_msg(args) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) # npu_config.seed_everything() accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) @@ -471,9 +474,10 @@ def sync_gradients_info(loss): end_time = time.time() one_step_duration = end_time - start_time accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) - npu_config.print_msg( - f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", - rank=0) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg( + f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) progress_info.train_loss = 0.0 if args.use_deepspeed or accelerator.is_main_process: @@ -601,7 +605,6 @@ def train_one_step(step_, data_item_, prof_=None): images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) x = torch.cat([videos, images], dim=2) # b c 17+4, h, w - # print('(x.shape, attn_mask.shape, cond.shape, cond_mask.shape', x.shape, attn_mask.shape, cond.shape, cond_mask.shape) with accelerator.accumulate(model): model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) @@ -623,10 +626,10 @@ def train_all_epoch(prof_=None): if train_one_step(step, data_item, prof_): break - if step >= 2: + if step >= 2 and torch_npu is not None and npu_config is not None: npu_config.free_mm() - if npu_config.on_npu and npu_config.profiling: + if npu_config is not None and npu_config.on_npu and npu_config.profiling: experimental_config = torch_npu.profiler._ExperimentalConfig( profiler_level=torch_npu.profiler.ProfilerLevel.Level1, aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization @@ -684,7 +687,7 @@ def train_all_epoch(prof_=None): parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=7.5) + parser.add_argument('--guidance_scale', type=float, default=5.0) parser.add_argument("--multi_scale", action="store_true") parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--use_deepspeed", action="store_true") diff --git a/scripts/text_condition/sample_video.sh b/scripts/text_condition/sample_video.sh deleted file mode 100644 index bb429175f..000000000 --- a/scripts/text_condition/sample_video.sh +++ /dev/null @@ -1,16 +0,0 @@ -CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v_pixsig.py \ - --model_path image_test_ckpt512 \ - --version 65x512x512 \ - --num_frames 1 \ - --height 512 \ - --width 512 \ - --cache_dir "cache_dir" \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --text_prompt examples/prompt_list_0.txt \ - --ae stabilityai/sd-vae-ft-mse \ - --ae_path "alpha_vae" \ - --save_img_path "./sample_videos512_512" \ - --fps 24 \ - --guidance_scale 4.0 \ - --num_sampling_steps 50 \ - --enable_tiling diff --git a/scripts/text_condition/sample_video_on_npu.sh b/scripts/text_condition/sample_video_on_npu.sh index 4686e8577..a4e4dd6a3 100644 --- a/scripts/text_condition/sample_video_on_npu.sh +++ b/scripts/text_condition/sample_video_on_npu.sh @@ -3,10 +3,10 @@ WEIGHT_PATH="/home/opensora/shebin/pre_weights/" export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" export MASTER_PORT=12359 -torchrun --nproc_per_node=8 opensora/sample/sample_t2v.py \ +torchrun --nproc_per_node=8 opensora/sample/sample_t2v_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ --version ${NUM_FRAME}x512x512 \ - --num_frames 1 \ + --num_frames ${NUM_FRAME} \ --height 512 \ --width 512 \ --cache_dir "./cache_dir" \ diff --git a/scripts/text_condition/train_video21d_65x240p.sh b/scripts/text_condition/train_video21d_65x240p.sh index 18ebdf17c..6b2a62675 100644 --- a/scripts/text_condition/train_video21d_65x240p.sh +++ b/scripts/text_condition/train_video21d_65x240p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="test21d_2node_65x240p_bs4_snr5_ema0_pt" +export PROJECT="testnpu21d" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -41,7 +41,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=100 \ - --output_dir="test21d_2node_65x240p_bs4_snr5_ema0_pt" \ + --output_dir="testnpu21d" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 512 \ diff --git a/scripts/text_condition/train_video3d_65x240p.sh b/scripts/text_condition/train_video3d_65x240p.sh index 6165d1d7b..1136d80ea 100644 --- a/scripts/text_condition/train_video3d_65x240p.sh +++ b/scripts/text_condition/train_video3d_65x240p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="test3d_2node_65x240p_bs4_snr5_ema0_pt" +export PROJECT="testnpu3d" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,7 +12,7 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example1.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -41,7 +41,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=100 \ - --output_dir="test3d_2node_65x240p_bs4_snr5_ema0_pt" \ + --output_dir="testnpu3d" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 512 \ @@ -51,4 +51,6 @@ accelerate launch \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --pretrained PixArt-Alpha-XL-2-512.safetensors + --pretrained PixArt-Alpha-XL-2-512.safetensors \ + --guidance_scale 7.5 \ + --resume_from_checkpoint "latest" From 3b3c34d3a165626e84706c13f23e24e9d9504790 Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Sun, 2 Jun 2024 23:21:42 +0800 Subject: [PATCH 019/134] udit --- opensora/models/diffusion/__init__.py | 7 +- .../models/diffusion/udit/modeling_udit.py | 811 +++++++++++++++++ opensora/models/diffusion/udit/modules.py | 857 ++++++++++++++++++ opensora/train/train_t2v.py | 2 +- .../text_condition/train_udit3d_61x240p.sh | 55 ++ 5 files changed, 1729 insertions(+), 3 deletions(-) create mode 100644 opensora/models/diffusion/udit/modeling_udit.py create mode 100644 opensora/models/diffusion/udit/modules.py create mode 100644 scripts/text_condition/train_udit3d_61x240p.sh diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index ed15d61e4..0144a701f 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -1,12 +1,15 @@ from .latte.modeling_latte import Latte_models from .opensora.modeling_opensora import OpenSora_models +from .udit.modeling_udit import UDiT_models Diffusion_models = {} Diffusion_models.update(Latte_models) Diffusion_models.update(OpenSora_models) +Diffusion_models.update(UDiT_models) + from .latte.modeling_latte import Latte_models_class from .opensora.modeling_opensora import OpenSora_models_class +from .udit.modeling_udit import UDiT_models_class Diffusion_models_class = {} Diffusion_models_class.update(Latte_models_class) -Diffusion_models_class.update(OpenSora_models_class) - +Diffusion_models_class.update(UDiT_models_class) \ No newline at end of file diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py new file mode 100644 index 000000000..ed9a815a2 --- /dev/null +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -0,0 +1,811 @@ +import os +import numpy as np +from torch import nn +import torch +from einops import rearrange, repeat +from typing import Any, Dict, Optional, Tuple +from torch.nn import functional as F +from diffusers.models.transformer_2d import Transformer2DModelOutput +from diffusers.utils import is_torch_version, deprecate +from diffusers.configuration_utils import ConfigMixin, register_to_config +from diffusers.models.modeling_utils import ModelMixin +from diffusers.models.normalization import AdaLayerNormSingle +from diffusers.models.embeddings import PixArtAlphaTextProjection +from opensora.models.diffusion.udit.modules import Upsample3d, Downsample3d, OverlapPatchEmbed3D, BasicTransformerBlock +from opensora.utils.utils import to_2tuple +import math + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + +class UDiTT2V(ModelMixin, ConfigMixin): + """ + A 2D Transformer model for image-like data. + + Parameters: + num_attention_heads (`int`, *optional*, defaults to 16): The number of heads to use for multi-head attention. + attention_head_dim (`int`, *optional*, defaults to 88): The number of channels in each head. + in_channels (`int`, *optional*): + The number of channels in the input and output (specify if the input is **continuous**). + num_layers (`int`, *optional*, defaults to 1): The number of layers of Transformer blocks to use. + dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use. + cross_attention_dim (`int`, *optional*): The number of `encoder_hidden_states` dimensions to use. + sample_size (`int`, *optional*): The width of the latent images (specify if the input is **discrete**). + This is fixed during training since it is used to learn a number of position embeddings. + num_vector_embeds (`int`, *optional*): + The number of classes of the vector embeddings of the latent pixels (specify if the input is **discrete**). + Includes the class for the masked latent pixel. + activation_fn (`str`, *optional*, defaults to `"geglu"`): Activation function to use in feed-forward. + num_embeds_ada_norm ( `int`, *optional*): + The number of diffusion steps used during training. Pass if at least one of the norm_layers is + `AdaLayerNorm`. This is fixed during training since it is used to learn a number of embeddings that are + added to the hidden states. + + During inference, you can denoise for up to but not more steps than `num_embeds_ada_norm`. + attention_bias (`bool`, *optional*): + Configure if the `TransformerBlocks` attention should contain a bias parameter. + """ + + _supports_gradient_checkpointing = True + + @register_to_config + def __init__( + self, + num_attention_heads: int = 16, + attention_head_dim: int = 88, + in_channels: Optional[int] = None, + out_channels: Optional[int] = None, + num_layers: int = 1, + dropout: float = 0.0, + norm_num_groups: int = 32, + cross_attention_dim: Optional[int] = None, + attention_bias: bool = False, + sample_size: Optional[int] = None, + sample_size_t: Optional[int] = None, + patch_size: Optional[int] = None, + patch_size_t: Optional[int] = None, + num_vector_embeds: Optional[int] = None, + down_factor: Optional[int] = 2, + depth: Optional[list] = [2, 5, 8, 5, 2], + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + use_linear_projection: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_type: str = "layer_norm", # 'layer_norm', 'ada_norm', 'ada_norm_zero', 'ada_norm_single', 'ada_norm_continuous', 'layer_norm_i2vgen' + norm_elementwise_affine: bool = True, + norm_eps: float = 1e-5, + attention_type: str = "default", + caption_channels: int = None, + interpolation_scale_h: float = None, + interpolation_scale_w: float = None, + interpolation_scale_t: float = None, + use_additional_conditions: Optional[bool] = None, + mlp_ratio: float = 2.0, + attention_mode: str = 'xformers', + ): + super().__init__() + + # Set some common variables used across the board. + self.use_linear_projection = use_linear_projection + self.interpolation_scale_t = interpolation_scale_t + self.interpolation_scale_h = interpolation_scale_h + self.interpolation_scale_w = interpolation_scale_w + self.caption_channels = caption_channels + self.num_attention_heads = num_attention_heads + self.attention_head_dim = attention_head_dim + self.inner_dim = self.config.num_attention_heads * self.config.attention_head_dim + self.in_channels = in_channels + self.out_channels = in_channels if out_channels is None else out_channels + self.gradient_checkpointing = False + use_additional_conditions = False + # if use_additional_conditions is None: + # if norm_type == "ada_norm_single" and sample_size == 128: + # use_additional_conditions = True + # else: + # use_additional_conditions = False + self.use_additional_conditions = use_additional_conditions + + # 1. Transformer2DModel can process both standard continuous images of shape `(batch_size, num_channels, width, height)` as well as quantized image embeddings of shape `(batch_size, num_image_vectors)` + # Define whether input is continuous or discrete depending on configuration + assert in_channels is not None + + if norm_type == "layer_norm" and num_embeds_ada_norm is not None: + deprecation_message = ( + f"The configuration file of this model: {self.__class__} is outdated. `norm_type` is either not set or" + " incorrectly set to `'layer_norm'`. Make sure to set `norm_type` to `'ada_norm'` in the config." + " Please make sure to update the config accordingly as leaving `norm_type` might led to incorrect" + " results in future versions. If you have downloaded this checkpoint from the Hugging Face Hub, it" + " would be very nice if you could open a Pull request for the `transformer/config.json` file" + ) + deprecate("norm_type!=num_embeds_ada_norm", "1.0.0", deprecation_message, standard_warn=False) + norm_type = "ada_norm" + + # 2. Initialize the right blocks. + # Initialize the output blocks and other projection blocks when necessary. + self._init_patched_inputs(norm_type=norm_type) + + def _init_patched_inputs(self, norm_type): + assert self.config.sample_size_t is not None, "OpenSoraT2V over patched input must provide sample_size_t" + assert self.config.sample_size is not None, "OpenSoraT2V over patched input must provide sample_size" + + self.num_frames = self.config.sample_size_t + self.config.sample_size = to_2tuple(self.config.sample_size) + self.height = self.config.sample_size[0] + self.width = self.config.sample_size[1] + interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 + interpolation_scale_t = ( + self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t + ) + interpolation_scale = ( + self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 30, + self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 40, + ) + self.pos_embed = OverlapPatchEmbed3D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + + down_factor = down_factor if isinstance(self.config.down_factor, list) else [self.config.down_factor] * 5 + + self.encoder_level_1 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim, + self.config.num_attention_heads, + self.config.attention_head_dim, + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + down_factor=down_factor[0], + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[0]) + ] + ) + self.down1_2 = Downsample3d(self.inner_dim) + + self.encoder_level_2 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 2, + self.config.num_attention_heads, + self.config.attention_head_dim * 2, + num_frames=self.config.sample_size_t // 2, + height=math.ceil(self.config.sample_size[0] / 2), + width=math.ceil(self.config.sample_size[1] / 2), + down_factor=down_factor[1], + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 2, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[1]) + ] + ) + self.down2_3 = Downsample3d(self.inner_dim * 2) + + self.latent = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 4, + self.config.num_attention_heads, + self.config.attention_head_dim * 4, + num_frames=self.config.sample_size_t // 4, + height=math.ceil(math.ceil(self.config.sample_size[0] / 2) / 2), + width=math.ceil(math.ceil(self.config.sample_size[1] / 2) / 2), + down_factor=down_factor[2], + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 4, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[2]) + ] + ) + + self.up3_2 = Upsample3d(int(self.inner_dim * 4)) ## From Level 4 to Level 3 + self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) + self.decoder_level_2 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 2, + self.config.num_attention_heads, + self.config.attention_head_dim * 2, + num_frames=self.config.sample_size_t // 2, + height=math.ceil(self.config.sample_size[0] / 2), + width=math.ceil(self.config.sample_size[1] / 2), + down_factor=down_factor[3], + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 2, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[3]) + ] + ) + + self.up2_1 = Upsample3d(int(self.inner_dim * 2)) ## From Level 4 to Level 3 + self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 2), bias=True) + self.decoder_level_1 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 2, + self.config.num_attention_heads, + self.config.attention_head_dim * 2, + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + down_factor=down_factor[4], + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 2, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[4]) + ] + ) + + if self.config.norm_type != "ada_norm_single": + self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) + self.proj_out_1 = nn.Linear(2 * self.inner_dim, 2 * self.inner_dim) + self.proj_out_2 = nn.Linear( + 2 * self.inner_dim, self.out_channels + ) + elif self.config.norm_type == "ada_norm_single": + self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) + self.scale_shift_table = nn.Parameter(torch.randn(2, 2 * self.inner_dim) / (2 * self.inner_dim)**0.5) + self.proj_out = nn.Linear( + 2 * self.inner_dim, self.out_channels + ) + + # PixArt-Alpha blocks. + # self.adaln_single = None + # if self.config.norm_type == "ada_norm_single": + # TODO(Sayak, PVP) clean this, for now we use sample size to determine whether to use + # additional conditions until we find better name + self.adaln_single_1 = AdaLayerNormSingle( + self.inner_dim, use_additional_conditions=self.use_additional_conditions + ) + self.adaln_single_2 = AdaLayerNormSingle( + self.inner_dim * 2, use_additional_conditions=self.use_additional_conditions + ) + self.adaln_single_3 = AdaLayerNormSingle( + self.inner_dim * 4, use_additional_conditions=self.use_additional_conditions + ) + + # self.caption_projection = None + # if self.caption_channels is not None: + self.caption_projection_1 = PixArtAlphaTextProjection( + in_features=self.caption_channels, hidden_size=self.inner_dim + ) + self.caption_projection_2 = PixArtAlphaTextProjection( + in_features=self.caption_channels, hidden_size=self.inner_dim * 2 + ) + self.caption_projection_3 = PixArtAlphaTextProjection( + in_features=self.caption_channels, hidden_size=self.inner_dim * 4 + ) + + def _set_gradient_checkpointing(self, module, value=False): + if hasattr(module, "gradient_checkpointing"): + module.gradient_checkpointing = value + + def forward( + self, + hidden_states: torch.Tensor, + timestep: Optional[torch.LongTensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + added_cond_kwargs: Dict[str, torch.Tensor] = None, + class_labels: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + attention_mask: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + use_image_num: Optional[int] = 0, + return_dict: bool = True, + ): + """ + The [`Transformer2DModel`] forward method. + + Args: + hidden_states (`torch.LongTensor` of shape `(batch size, num latent pixels)` if discrete, `torch.FloatTensor` of shape `(batch size, channel, height, width)` if continuous): + Input `hidden_states`. + encoder_hidden_states ( `torch.FloatTensor` of shape `(batch size, sequence len, embed dims)`, *optional*): + Conditional embeddings for cross attention layer. If not given, cross-attention defaults to + self-attention. + timestep ( `torch.LongTensor`, *optional*): + Used to indicate denoising step. Optional timestep to be applied as an embedding in `AdaLayerNorm`. + class_labels ( `torch.LongTensor` of shape `(batch size, num classes)`, *optional*): + Used to indicate class labels conditioning. Optional class labels to be applied as an embedding in + `AdaLayerZeroNorm`. + cross_attention_kwargs ( `Dict[str, Any]`, *optional*): + A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under + `self.processor` in + [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py). + attention_mask ( `torch.Tensor`, *optional*): + An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask + is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large + negative values to the attention scores corresponding to "discard" tokens. + encoder_attention_mask ( `torch.Tensor`, *optional*): + Cross-attention mask applied to `encoder_hidden_states`. Two formats supported: + + * Mask `(batch, sequence_length)` True = keep, False = discard. + * Bias `(batch, 1, sequence_length)` 0 = keep, -10000 = discard. + + If `ndim == 2`: will be interpreted as a mask, then converted into a bias consistent with the format + above. This bias will be added to the cross-attention scores. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] instead of a plain + tuple. + + Returns: + If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a + `tuple` where the first element is the sample tensor. + """ + batch_size, c, frame, height, width = hidden_states.shape + assert frame % 2 == 0 and use_image_num == 0 + frame = frame - use_image_num # 21-4=17 + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + print.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. + # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. + # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. + # expects mask of shape: + # [batch, key_tokens] + # adds singleton query_tokens dimension: + # [batch, 1, key_tokens] + # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: + # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) + # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + + if attention_mask is not None and attention_mask.ndim == 4: + # assume that mask is expressed as: + # (1 = keep, 0 = discard) + # convert mask into a bias that can be added to attention scores: + # (keep = +0, discard = -10000.0) + # b, frame+use_image_num, h, w -> a video with images + # b, 1, h, w -> only images + attention_mask = attention_mask.to(self.dtype) + attention_mask = attention_mask.unsqueeze(1) # b 1 t h w + attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') + + attention_bias = (1 - attention_mask.bool().to(self.dtype)) * -10000.0 + + # convert encoder_attention_mask to a bias the same way we do for attention_mask + if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: + # b, 1, l -> only video + encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 + + + if npu_config is not None and attention_mask is not None: + attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-2]) + + + # 1. Input + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + hidden_states, encoder_hidden_states_1, encoder_hidden_states_2, encoder_hidden_states_3, \ + timestep_1, timestep_2, timestep_3, \ + embedded_timestep_1, embedded_timestep_2, embedded_timestep_3 = self._operate_on_patched_inputs( + hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num + ) + + # encoder_1 + out_enc_level1 = hidden_states + + + def create_custom_forward(module, return_dict=None): + def custom_forward(*inputs): + if return_dict is not None: + return module(*inputs, return_dict=return_dict) + else: + return module(*inputs) + + return custom_forward + + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + for block in self.encoder_level_1: + out_enc_level1 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_enc_level1, + attention_bias, + encoder_hidden_states_1, + encoder_attention_mask, + timestep_1, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for block in self.encoder_level_1: + out_enc_level1 = block( + out_enc_level1, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_1, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_1, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + pad_h_1, pad_w_1 = height % 2, width % 2 + + inp_enc_level2, attention_bias, attention_mask = self.down1_2(out_enc_level1, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) + frame, height, width = frame // 2, (height + pad_h_1) // 2, (width + pad_w_1) // 2 + + # encoder_2 + out_enc_level2 = inp_enc_level2 + + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for block in self.encoder_level_2: + out_enc_level2 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_enc_level2, + attention_bias, + encoder_hidden_states_2, + encoder_attention_mask, + timestep_2, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for block in self.encoder_level_2: + out_enc_level2 = block( + out_enc_level2, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_2, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_2, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + pad_h_2, pad_w_2 = height % 2, width % 2 + + inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) + frame, height, width = frame // 2, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + + # latent + latent = inp_enc_level3 + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for block in self.latent: + latent = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + latent, + attention_bias, + encoder_hidden_states_3, + encoder_attention_mask, + timestep_3, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for block in self.latent: + latent = block( + latent, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_3, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_3, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + + # decoder_2 + + inp_dec_level2, attention_bias, attention_mask = self.up3_2(latent, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) + frame, height, width = frame * 2, height * 2 - pad_h_2, width * 2 - pad_w_2 + inp_dec_level2 = torch.cat([inp_dec_level2, out_enc_level2], 2) + inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) + out_dec_level2 = inp_dec_level2 + + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for block in self.decoder_level_2: + out_dec_level2 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_dec_level2, + attention_bias, + encoder_hidden_states_2, + encoder_attention_mask, + timestep_2, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for block in self.decoder_level_2: + out_dec_level2 = block( + out_dec_level2, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_2, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_2, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + + # decoder_1 + + inp_dec_level1, attention_bias, attention_mask = self.up2_1(out_dec_level2, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) + frame, height, width = frame * 2, height * 2 - pad_h_1, width * 2 - pad_w_1 + inp_dec_level1 = torch.cat([inp_dec_level1, out_enc_level1], 2) + inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) + out_dec_level1 = inp_dec_level1 + + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for block in self.decoder_level_1: + out_dec_level1 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_dec_level1, + attention_bias, + encoder_hidden_states_2, + encoder_attention_mask, + timestep_2, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for block in self.decoder_level_1: + out_dec_level1 = block( + out_dec_level1, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_2, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_2, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + + # 3. Output + output = self._get_output_for_patched_inputs( + hidden_states=out_dec_level1, + timestep=timestep_2, + class_labels=class_labels, + embedded_timestep=embedded_timestep_2, + num_frames=frame, + height=height, + width=width, + ) # b c t h w + + if not return_dict: + return (output,) + + return Transformer2DModelOutput(sample=output) + + + def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): + # batch_size = hidden_states.shape[0] + hidden_states = self.pos_embed(hidden_states.to(self.dtype)) + + if self.use_additional_conditions and added_cond_kwargs is None: + raise ValueError( + "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." + ) + timestep_1, embedded_timestep_1 = self.adaln_single_1( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + timestep_2, embedded_timestep_2 = self.adaln_single_2( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + timestep_3, embedded_timestep_3 = self.adaln_single_3( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + + encoder_hidden_states_1 = self.caption_projection_1(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + encoder_hidden_states_1 = rearrange(encoder_hidden_states_1[:, :1], 'b 1 l d -> (b 1) l d') + encoder_hidden_states_2 = self.caption_projection_2(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + encoder_hidden_states_2 = rearrange(encoder_hidden_states_2[:, :1], 'b 1 l d -> (b 1) l d') + encoder_hidden_states_3 = self.caption_projection_3(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + encoder_hidden_states_3 = rearrange(encoder_hidden_states_3[:, :1], 'b 1 l d -> (b 1) l d') + + + return hidden_states, encoder_hidden_states_1, encoder_hidden_states_2, encoder_hidden_states_3, \ + timestep_1, timestep_2, timestep_3, embedded_timestep_1, embedded_timestep_2, embedded_timestep_3 + + + + def _get_output_for_patched_inputs( + self, hidden_states, timestep, class_labels, embedded_timestep, num_frames, height=None, width=None + ): + if self.config.norm_type != "ada_norm_single": + conditioning = self.transformer_blocks[0].norm1.emb( + timestep, class_labels, hidden_dtype=self.dtype + ) + shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] + hidden_states = self.proj_out_2(hidden_states) + elif self.config.norm_type == "ada_norm_single": + shift, scale = (self.scale_shift_table[None] + embedded_timestep[:, None]).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) + # Modulation + hidden_states = hidden_states * (1 + scale) + shift + hidden_states = self.proj_out(hidden_states) + hidden_states = hidden_states.squeeze(1) + + # unpatchify + hidden_states = hidden_states.reshape( + shape=(-1, num_frames, height, width, self.out_channels) + ) + output = torch.einsum("nthwc->ncthw", hidden_states) + return output + + +def UDiTT2V_S_111(**kwargs): + return UDiTT2V(down_factor=2, depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, + patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_B_111(**kwargs): + return UDiTT2V(down_factor=2, depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, + patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_L_111(**kwargs): + return UDiTT2V(down_factor=2, depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=16, + patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_XL_111(**kwargs): + return UDiTT2V(down_factor=2, depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=16, + patch_size_t=1, patch_size=1, mlp_ratio=4, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_XXL_111(**kwargs): + return UDiTT2V(down_factor=2, depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=24, + patch_size_t=1, patch_size=1, mlp_ratio=4, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +UDiT_models = { + "UDiTT2V-S/111": UDiTT2V_S_111, # 0.33B + "UDiTT2V-B/111": UDiTT2V_B_111, # 1.3B + "UDiTT2V-L/111": UDiTT2V_L_111, # 2B + "UDiTT2V-XL/111": UDiTT2V_XL_111, # 3.3B + "UDiTT2V-XXL/111": UDiTT2V_XXL_111, # 7.3B +} + +UDiT_models_class = { + "UDiTT2V-S/111": UDiTT2V, + "UDiTT2V-B/111": UDiTT2V, + "UDiTT2V-L/111": UDiTT2V, + "UDiTT2V-XL/111": UDiTT2V, + "UDiTT2V-XXL/111": UDiTT2V, +} + +if __name__ == '__main__': + import sys + from opensora.models.ae import ae_channel_config, ae_stride_config + from opensora.models.ae import getae, getae_wrapper + from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper + + args = type('args', (), + { + 'ae': 'CausalVAEModel_4x8x8', + 'attention_mode': 'xformers', + 'use_rope': False, + 'model_max_length': 300, + 'max_image_size': 512, + 'num_frames': 61, + 'use_image_num': 0, + 'compress_kv_factor': 1 + } + ) + b = 2 + c = 4 + cond_c = 4096 + num_timesteps = 1000 + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) + if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: + num_frames = (args.num_frames - 1) // ae_stride_t + 1 + else: + num_frames = args.num_frames // ae_stride_t + + device = torch.device('cuda:1') + model = UDiTT2V_XXL_111(in_channels=4, + out_channels=8, + sample_size=latent_size, + sample_size_t=num_frames, + activation_fn="gelu-approximate", + attention_bias=True, + attention_type="default", + double_self_attention=False, + norm_elementwise_affine=False, + norm_eps=1e-06, + norm_num_groups=32, + num_vector_embeds=None, + only_cross_attention=False, + upcast_attention=False, + use_linear_projection=False, + use_additional_conditions=False).to(device) + + print(model) + print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') + sys.exit() + x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w).to(device) + cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) + attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w)).to(device) # B L or B 1+num_images L + cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L + timestep = torch.randint(0, 1000, (b,), device=device) + model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) + with torch.no_grad(): + output = model(**model_kwargs) + print(output[0].shape) + + + + diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py new file mode 100644 index 000000000..f2e04e46a --- /dev/null +++ b/opensora/models/diffusion/udit/modules.py @@ -0,0 +1,857 @@ +from einops import rearrange +from torch import nn +import torch +import numpy as np + +from einops import rearrange, repeat +from typing import Any, Dict, Optional, Tuple +from diffusers.utils.torch_utils import maybe_allow_in_graph +from typing import Any, Dict, Optional + +import torch +import torch.nn.functional as F +from torch import nn +import diffusers +from diffusers.utils import deprecate, logging +from diffusers.utils.torch_utils import maybe_allow_in_graph +from diffusers.models.attention import GatedSelfAttentionDense +from diffusers.models.attention_processor import Attention as Attention_ +from diffusers.models.embeddings import SinusoidalPositionalEmbedding +from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm +try: + import torch_npu + from opensora.npu_config import npu_config, set_run_dtype +except: + torch_npu = None + npu_config = None + set_run_dtype = None +logger = logging.get_logger(__name__) + +def get_3d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid height and width return: pos_embed: [grid_size*grid_size, embed_dim] or + [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + grid_t = np.arange(grid_size[0], dtype=np.float32) / (grid_size[0] / base_size[0]) / interpolation_scale[0] + grid_h = np.arange(grid_size[1], dtype=np.float32) / (grid_size[1] / base_size[1]) / interpolation_scale[1] + grid_w = np.arange(grid_size[2], dtype=np.float32) / (grid_size[2] / base_size[2]) / interpolation_scale[2] + grid = np.meshgrid(grid_w, grid_h, grid_t) # here w goes first + grid = np.stack(grid, axis=0) + + grid = grid.reshape([3, 1, grid_size[2], grid_size[1], grid_size[0]]) + pos_embed = get_3d_sincos_pos_embed_from_grid(embed_dim, grid) + + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + + +def get_3d_sincos_pos_embed_from_grid(embed_dim, grid): + if embed_dim % 3 != 0: + raise ValueError("embed_dim must be divisible by 3") + + # use 1/3 of dimensions to encode grid_t/h/w + emb_t = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[0]) # (T*H*W, D/3) + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[1]) # (T*H*W, D/3) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[2]) # (T*H*W, D/3) + + emb = np.concatenate([emb_t, emb_h, emb_w], axis=1) # (T*H*W, D) + return emb + + +def get_2d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid height and width return: pos_embed: [grid_size*grid_size, embed_dim] or + [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid_h = np.arange(grid_size[0], dtype=np.float32) / (grid_size[0] / base_size[0]) / interpolation_scale[0] + grid_w = np.arange(grid_size[1], dtype=np.float32) / (grid_size[1] / base_size[1]) / interpolation_scale[1] + grid = np.meshgrid(grid_w, grid_h) # here w goes first + grid = np.stack(grid, axis=0) + + grid = grid.reshape([2, 1, grid_size[1], grid_size[0]]) + pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + + +def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + # use 1/3 of dimensions to encode grid_t/h/w + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2) + + emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) + return emb + +def get_1d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid return: pos_embed: [grid_size, embed_dim] or + [1+grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid = np.arange(grid_size, dtype=np.float32) / (grid_size / base_size) / interpolation_scale + pos_embed = get_1d_sincos_pos_embed_from_grid(embed_dim, grid) # (H*W, D/2) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + +def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): + """ + embed_dim: output dimension for each position pos: a list of positions to be encoded: size (M,) out: (M, D) + """ + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + omega = np.arange(embed_dim // 2, dtype=np.float64) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + + pos = pos.reshape(-1) # (M,) + out = np.einsum("m,d->md", pos, omega) # (M, D/2), outer product + + emb_sin = np.sin(out) # (M, D/2) + emb_cos = np.cos(out) # (M, D/2) + + emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) + return emb + + +class OverlapPatchEmbed3D(nn.Module): + """2D Image to Patch Embedding but with 3D position embedding""" + + def __init__( + self, + num_frames=1, + height=224, + width=224, + in_channels=3, + embed_dim=768, + layer_norm=False, + flatten=True, + bias=True, + interpolation_scale=(1, 1), + interpolation_scale_t=1, + ): + super().__init__() + # assert num_frames == 1 + self.flatten = flatten + self.layer_norm = layer_norm + + self.proj = nn.Conv3d(in_channels, embed_dim, kernel_size=3, stride=1, padding=1, bias=bias) + if layer_norm: + self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) + else: + self.norm = None + + # See: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 + + self.height, self.width = height, width + self.base_size = (height, width) + self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + pos_embed = get_2d_sincos_pos_embed( + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) + + self.num_frames = num_frames + self.base_size_t = num_frames + self.interpolation_scale_t = interpolation_scale_t + temp_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_embed", torch.from_numpy(temp_embed).float().unsqueeze(0), persistent=False) + + def forward(self, latent): + b, _, num_frames, _, _ = latent.shape + height, width = latent.shape[-2], latent.shape[-1] + latent = self.proj(latent) # b c t h w + + if self.flatten: + latent = rearrange(latent, 'b c t h w -> (b t) (h w) c') # B C T H W -> BT N C + if self.layer_norm: + latent = self.norm(latent) + + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed + + + if self.num_frames != num_frames: + # raise NotImplementedError + temp_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_embed = torch.from_numpy(temp_embed) + temp_embed = temp_embed.float().unsqueeze(0).to(latent.device) + else: + temp_embed = self.temp_embed + + latent = (latent + pos_embed).to(latent.dtype) + + latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + + temp_embed = temp_embed.unsqueeze(2) + latent = (latent + temp_embed).to(latent.dtype) + latent = rearrange(latent, 'b t n c -> b (t n) c') + return latent + + + +class Attention(Attention_): + def __init__(self, downsampler=False, dim=768, num_frames=8, height=16, width=16, + down_factor=2, down_shortcut=True, attention_mode='xformers', **kwags): + processor = AttnProcessor2_0(downsampler=False, dim=768, num_frames=8, height=16, width=16, + down_factor=2, down_shortcut=True, attention_mode='xformers') + super().__init__(processor=processor, **kwags) + +class DownSampler(nn.Module): + def __init__(self, *args, **kwargs): + ''' Required kwargs: down_factor, downsampler''' + super().__init__() + self.down_factor = kwargs.pop('down_factor') + self.down_shortcut = kwargs.pop('down_shortcut') + self.t = kwargs.pop('num_frames') + self.h = kwargs.pop('height') + self.w = kwargs.pop('width') + self.layer = nn.Conv3d(*args, **kwargs) + + def forward(self, x): + x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) + x = self.layer(x) + (x if self.down_shortcut else 0) + return rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) d (t h w)', dt=self.down_factor, dh=self.down_factor, dw=self.down_factor) + + +class AttnProcessor2_0: + r""" + Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). + """ + + def __init__(self, downsampler=False, dim=768, num_frames=8, height=16, width=16, + down_factor=2, down_shortcut=True, attention_mode='xformers'): + self.attention_mode = attention_mode + self.downsampler = None + if downsampler: # downsampler + self.t = num_frames + self.h = height + self.w = width + downsampler_ker_size = 5 + downsampler_padding = (5 - 1) // 2 + self.downsampler = DownSampler(dim, dim, kernel_size=downsampler_ker_size, stride=1, + padding=downsampler_padding, groups=dim, down_factor=down_factor, + down_shortcut=down_shortcut, num_frames=num_frames, height=height, width=width) + + if not hasattr(F, "scaled_dot_product_attention"): + raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + def __call__( + self, + attn: Attention, + hidden_states: torch.FloatTensor, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + temb: Optional[torch.FloatTensor] = None, + *args, + **kwargs, + ) -> torch.FloatTensor: + if len(args) > 0 or kwargs.get("scale", None) is not None: + deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." + deprecate("scale", "1.0.0", deprecation_message) + + if self.downsampler is not None: + hidden_states = self.downsampler(hidden_states) + + residual = hidden_states + if attn.spatial_norm is not None: + hidden_states = attn.spatial_norm(hidden_states, temb) + + input_ndim = hidden_states.ndim + + # if input_ndim == 4: + # batch_size, channel, height, width = hidden_states.shape + # hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) + + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + + if attention_mask is not None: + attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + + if attn.group_norm is not None: + hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) + + query = attn.to_q(hidden_states) + + if encoder_hidden_states is None: + encoder_hidden_states = hidden_states + elif attn.norm_cross: + encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + + key = attn.to_k(encoder_hidden_states) + value = attn.to_v(encoder_hidden_states) + + inner_dim = key.shape[-1] + head_dim = inner_dim // attn.heads + + if npu_config is not None and npu_config.on_npu: + if npu_config.enable_FA and query.dtype == torch.float32: + dtype = torch.bfloat16 + else: + dtype = None + + with set_run_dtype(query, dtype): + query, key, value = npu_config.set_current_run_dtype([query, key, value]) + hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", + head_dim, attn.heads) + + hidden_states = npu_config.restore_dtype(hidden_states) + else: + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + attention_mask = None + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + + if self.attention_mode == 'flash': + assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + + # linear proj + hidden_states = attn.to_out[0](hidden_states) + # dropout + hidden_states = attn.to_out[1](hidden_states) + + if input_ndim == 4: + hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) + + if attn.residual_connection: + hidden_states = hidden_states + residual + + hidden_states = hidden_states / attn.rescale_output_factor + + if self.downsampler is not None: + dt = self.downsampler.down_factor + dh = self.downsampler.down_factor + dw = self.downsampler.down_factor + hidden_states = rearrange(hidden_states, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', + t=self.t//dt, h=self.h//dh, w=self.w//dw, dt=dt, dh=dh, dw=dw) + + return hidden_states + + +class PixelUnshuffle3d(nn.Module): + def __init__(self, ratio): + super().__init__() + self.r = ratio + + def forward(self, x): + b, c, t, h, w = x.shape + assert t % self.r == 0 and h % self.r == 0 and w % self.r == 0 + if self.r > 1: + x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r, r2=self.r, r3=self.r) + return x + +class PixelShuffle3d(nn.Module): + def __init__(self, ratio): + super().__init__() + self.r = ratio + + def forward(self, x): + b, c, t, h, w = x.shape + assert c % (self.r*self.r*self.r) == 0 + if self.r > 1: + x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r, r2=self.r, r3=self.r) + return x + +class Downsample3d(nn.Module): + def __init__(self, n_feat): + super(Downsample3d, self).__init__() + + self.body = nn.Sequential(nn.Conv3d(n_feat, n_feat // 4, kernel_size=3, stride=1, padding=1, bias=False), + PixelUnshuffle3d(2)) + + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) + x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0), mode='reflect') + x = self.body(x) + x = rearrange(x, 'b d t h w -> b (t h w) d') + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=frames, h=height, w=width) + attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h, 0, 0)) + attention_mask = F.max_pool3d(attention_mask, kernel_size=2, stride=2) + attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + + if npu_config is not None and attention_mask is not None: + attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + + return x, attention_bias, attention_mask + +class Upsample3d(nn.Module): + def __init__(self, n_feat): + super(Upsample3d, self).__init__() + + self.body = nn.Sequential(nn.Conv3d(n_feat, n_feat * 4, kernel_size=3, stride=1, padding=1, bias=False), + PixelShuffle3d(2)) + + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) + x = self.body(x) + x = x[:, :, :, :height*2-pad_h, :width*2-pad_w] + x = rearrange(x, 'b d t h w -> b (t h w) d') + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=frames, h=height, w=width) + attention_mask = attention_mask.repeat_interleave(2, -1).repeat_interleave(2, -2).repeat_interleave(2, -3) + attention_mask = attention_mask[:, :, :, :height*2-pad_h, :width*2-pad_w] + attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + if npu_config is not None and attention_mask is not None: + attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + + return x, attention_bias, attention_mask + + + +class FeedForward(nn.Module): + def __init__(self, dim, ffn_expansion_factor, bias=True, rep=False, + num_frames=8, height=16, width=16): + super(FeedForward, self).__init__() + self.t = num_frames + self.h = height + self.w = width + + self.rep = rep + self.bias = bias + + self.hidden_features = hidden_features = int(dim * ffn_expansion_factor) + + if rep: + self.project_in = nn.Sequential(nn.Conv3d(dim, hidden_features // 2, kernel_size=1, bias=bias), + nn.Conv3d(hidden_features // 2, hidden_features, kernel_size=1, bias=bias)) + + self.dwconv = nn.ModuleList([ + nn.Conv3d(hidden_features, hidden_features, kernel_size=5, stride=1, padding=2, dilation=1, + groups=hidden_features, bias=bias), + nn.Conv3d(hidden_features, hidden_features, kernel_size=3, stride=1, padding=1, dilation=1, + groups=hidden_features, bias=bias), + nn.Conv3d(hidden_features, hidden_features, kernel_size=1, stride=1, padding=0, dilation=1, + groups=hidden_features, bias=bias) + ]) + + self.project_out = nn.Sequential(nn.Conv3d(hidden_features, hidden_features // 2, kernel_size=1, bias=bias), + nn.Conv3d(hidden_features // 2, dim, kernel_size=1, bias=bias)) + + else: + self.project_in = nn.Conv3d(dim, hidden_features, kernel_size=1, bias=bias) + self.dwconv = nn.Conv3d(hidden_features, hidden_features, kernel_size=5, stride=1, padding=2, + groups=hidden_features, bias=bias) + self.project_out = nn.Conv3d(hidden_features, dim, kernel_size=1, bias=bias) + + def forward(self, x): + x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) + x = self.project_in(x) + x = F.gelu(x) + if self.rep: + out = x + for module in self.dwconv: + out = out + module(x) + else: + out = self.dwconv(x) + x = self.project_out(out) + x = rearrange(x, 'b d t h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + return x + + +@maybe_allow_in_graph +class BasicTransformerBlock(nn.Module): + r""" + A basic Transformer block. + + Parameters: + dim (`int`): The number of channels in the input and output. + num_attention_heads (`int`): The number of heads to use for multi-head attention. + attention_head_dim (`int`): The number of channels in each head. + dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use. + cross_attention_dim (`int`, *optional*): The size of the encoder_hidden_states vector for cross attention. + activation_fn (`str`, *optional*, defaults to `"geglu"`): Activation function to be used in feed-forward. + num_embeds_ada_norm (: + obj: `int`, *optional*): The number of diffusion steps used during training. See `Transformer2DModel`. + attention_bias (: + obj: `bool`, *optional*, defaults to `False`): Configure if the attentions should contain a bias parameter. + only_cross_attention (`bool`, *optional*): + Whether to use only cross-attention layers. In this case two cross attention layers are used. + double_self_attention (`bool`, *optional*): + Whether to use two self-attention layers. In this case no cross attention layers are used. + upcast_attention (`bool`, *optional*): + Whether to upcast the attention computation to float32. This is useful for mixed precision training. + norm_elementwise_affine (`bool`, *optional*, defaults to `True`): + Whether to use learnable elementwise affine parameters for normalization. + norm_type (`str`, *optional*, defaults to `"layer_norm"`): + The normalization layer to use. Can be `"layer_norm"`, `"ada_norm"` or `"ada_norm_zero"`. + final_dropout (`bool` *optional*, defaults to False): + Whether to apply a final dropout after the last feed-forward layer. + attention_type (`str`, *optional*, defaults to `"default"`): + The type of attention to use. Can be `"default"` or `"gated"` or `"gated-text-image"`. + positional_embeddings (`str`, *optional*, defaults to `None`): + The type of positional embeddings to apply to. + num_positional_embeddings (`int`, *optional*, defaults to `None`): + The maximum number of positional embeddings to apply. + """ + + def __init__( + self, + dim: int, + num_attention_heads: int, + attention_head_dim: int, + dropout=0.0, + cross_attention_dim: Optional[int] = None, + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + attention_bias: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_elementwise_affine: bool = True, + norm_type: str = "layer_norm", # 'layer_norm', 'ada_norm', 'ada_norm_zero', 'ada_norm_single', 'ada_norm_continuous', 'layer_norm_i2vgen' + norm_eps: float = 1e-5, + final_dropout: bool = False, + attention_type: str = "default", + positional_embeddings: Optional[str] = None, + num_positional_embeddings: Optional[int] = None, + ada_norm_continous_conditioning_embedding_dim: Optional[int] = None, + ada_norm_bias: Optional[int] = None, + ff_inner_dim: Optional[int] = None, + ff_bias: bool = True, + attention_out_bias: bool = True, + attention_mode: str = "xformers", + num_frames: int = 16, + height: int = 32, + width: int = 32, + down_factor: int = 2, + mlp_ratio: float = 2.0, + ): + super().__init__() + self.only_cross_attention = only_cross_attention + + self.t = num_frames + self.h = height + self.w = width + self.down_factor = down_factor + # We keep these boolean flags for backward-compatibility. + self.use_ada_layer_norm_zero = (num_embeds_ada_norm is not None) and norm_type == "ada_norm_zero" + self.use_ada_layer_norm = (num_embeds_ada_norm is not None) and norm_type == "ada_norm" + self.use_ada_layer_norm_single = norm_type == "ada_norm_single" + self.use_layer_norm = norm_type == "layer_norm" + self.use_ada_layer_norm_continuous = norm_type == "ada_norm_continuous" + + if norm_type in ("ada_norm", "ada_norm_zero") and num_embeds_ada_norm is None: + raise ValueError( + f"`norm_type` is set to {norm_type}, but `num_embeds_ada_norm` is not defined. Please make sure to" + f" define `num_embeds_ada_norm` if setting `norm_type` to {norm_type}." + ) + + self.norm_type = norm_type + self.num_embeds_ada_norm = num_embeds_ada_norm + + if positional_embeddings and (num_positional_embeddings is None): + raise ValueError( + "If `positional_embedding` type is defined, `num_positition_embeddings` must also be defined." + ) + + if positional_embeddings == "sinusoidal": + self.pos_embed = SinusoidalPositionalEmbedding(dim, max_seq_length=num_positional_embeddings) + else: + self.pos_embed = None + + # Define 3 blocks. Each block has its own normalization layer. + # 1. Self-Attn + if norm_type == "ada_norm": + self.norm1 = AdaLayerNorm(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_zero": + self.norm1 = AdaLayerNormZero(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_continuous": + self.norm1 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "rms_norm", + ) + else: + self.norm1 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + + self.attn1 = Attention( + query_dim=dim, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + cross_attention_dim=cross_attention_dim if only_cross_attention else None, + upcast_attention=upcast_attention, + out_bias=attention_out_bias, + downsampler=True, + dim=dim, + num_frames=num_frames, + height=height, + width=width, + down_factor=down_factor, + down_shortcut=True, + attention_mode=attention_mode, + ) + + # 2. Cross-Attn + if cross_attention_dim is not None or double_self_attention: + # We currently only use AdaLayerNormZero for self attention where there will only be one attention block. + # I.e. the number of returned modulation chunks from AdaLayerZero would not make sense if returned during + # the second cross attention block. + if norm_type == "ada_norm": + self.norm2 = AdaLayerNorm(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_continuous": + self.norm2 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "rms_norm", + ) + else: + self.norm2 = nn.LayerNorm(dim, norm_eps, norm_elementwise_affine) + + self.attn2 = Attention( + query_dim=dim, + cross_attention_dim=cross_attention_dim if not double_self_attention else None, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + upcast_attention=upcast_attention, + out_bias=attention_out_bias, + downsampler=False, + attention_mode=attention_mode, + ) # is self-attn if encoder_hidden_states is none + else: + self.norm2 = None + self.attn2 = None + + # 3. Feed-forward + if norm_type == "ada_norm_continuous": + self.norm3 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "layer_norm", + ) + + elif norm_type in ["ada_norm_zero", "ada_norm", "layer_norm", "ada_norm_continuous"]: + self.norm3 = nn.LayerNorm(dim, norm_eps, norm_elementwise_affine) + elif norm_type == "layer_norm_i2vgen": + self.norm3 = None + + self.ff = FeedForward( + dim, + ffn_expansion_factor=mlp_ratio, + rep=True, + num_frames=num_frames, + height=height, + width=width, + ) + + # 4. Fuser + if attention_type == "gated" or attention_type == "gated-text-image": + self.fuser = GatedSelfAttentionDense(dim, cross_attention_dim, num_attention_heads, attention_head_dim) + + # 5. Scale-shift for PixArt-Alpha. + if norm_type == "ada_norm_single": + self.scale_shift_table = nn.Parameter(torch.randn(6, dim) / dim**0.5) + + # let chunk size default to None + self._chunk_size = None + self._chunk_dim = 0 + + def set_chunk_feed_forward(self, chunk_size: Optional[int], dim: int = 0): + # Sets chunk feed-forward + self._chunk_size = chunk_size + self._chunk_dim = dim + + def forward( + self, + hidden_states: torch.FloatTensor, + attention_mask: Optional[torch.FloatTensor] = None, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + encoder_attention_mask: Optional[torch.FloatTensor] = None, + timestep: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + class_labels: Optional[torch.LongTensor] = None, + added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None, + ) -> torch.FloatTensor: + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + logger.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + + # Notice that normalization is always applied before the real computation in the following blocks. + # 0. Self-Attention + batch_size = hidden_states.shape[0] + + if self.norm_type == "ada_norm": + norm_hidden_states = self.norm1(hidden_states, timestep) + elif self.norm_type == "ada_norm_zero": + norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.norm1( + hidden_states, timestep, class_labels, hidden_dtype=hidden_states.dtype + ) + elif self.norm_type in ["layer_norm", "layer_norm_i2vgen"]: + norm_hidden_states = self.norm1(hidden_states) + elif self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm1(hidden_states, added_cond_kwargs["pooled_text_emb"]) + elif self.norm_type == "ada_norm_single": + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( + self.scale_shift_table[None] + timestep.reshape(batch_size, 6, -1) + ).chunk(6, dim=1) + norm_hidden_states = self.norm1(hidden_states) + norm_hidden_states = norm_hidden_states * (1 + scale_msa) + shift_msa + norm_hidden_states = norm_hidden_states.squeeze(1) + else: + raise ValueError("Incorrect norm used") + + if self.pos_embed is not None: + norm_hidden_states = self.pos_embed(norm_hidden_states) + + # 1. Prepare GLIGEN inputs + cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} + gligen_kwargs = cross_attention_kwargs.pop("gligen", None) + + attn_output = self.attn1( + norm_hidden_states, + encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, + attention_mask=attention_mask, + **cross_attention_kwargs, + ) + if self.norm_type == "ada_norm_zero": + attn_output = gate_msa.unsqueeze(1) * attn_output + elif self.norm_type == "ada_norm_single": + attn_output = gate_msa * attn_output + + hidden_states = attn_output + hidden_states + if hidden_states.ndim == 4: + hidden_states = hidden_states.squeeze(1) + + # 1.2 GLIGEN Control + if gligen_kwargs is not None: + hidden_states = self.fuser(hidden_states, gligen_kwargs["objs"]) + + # 3. Cross-Attention + if self.attn2 is not None: + if self.norm_type == "ada_norm": + norm_hidden_states = self.norm2(hidden_states, timestep) + elif self.norm_type in ["ada_norm_zero", "layer_norm", "layer_norm_i2vgen"]: + norm_hidden_states = self.norm2(hidden_states) + elif self.norm_type == "ada_norm_single": + # For PixArt norm2 isn't applied here: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L70C1-L76C103 + norm_hidden_states = hidden_states + elif self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm2(hidden_states, added_cond_kwargs["pooled_text_emb"]) + else: + raise ValueError("Incorrect norm") + + if self.pos_embed is not None and self.norm_type != "ada_norm_single": + norm_hidden_states = self.pos_embed(norm_hidden_states) + + attn_output = self.attn2( + norm_hidden_states, + encoder_hidden_states=encoder_hidden_states, + attention_mask=encoder_attention_mask, + **cross_attention_kwargs, + ) + hidden_states = attn_output + hidden_states + + # 4. Feed-forward + # i2vgen doesn't have this norm 🤷‍♂️ + if self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm3(hidden_states, added_cond_kwargs["pooled_text_emb"]) + elif not self.norm_type == "ada_norm_single": + norm_hidden_states = self.norm3(hidden_states) + + if self.norm_type == "ada_norm_zero": + norm_hidden_states = norm_hidden_states * (1 + scale_mlp[:, None]) + shift_mlp[:, None] + + if self.norm_type == "ada_norm_single": + norm_hidden_states = self.norm2(hidden_states) + norm_hidden_states = norm_hidden_states * (1 + scale_mlp) + shift_mlp + + if self._chunk_size is not None: + # "feed_forward_chunk_size" can be used to save memory + ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) + else: + ff_output = self.ff(norm_hidden_states) + + if self.norm_type == "ada_norm_zero": + ff_output = gate_mlp.unsqueeze(1) * ff_output + elif self.norm_type == "ada_norm_single": + ff_output = gate_mlp * ff_output + + hidden_states = ff_output + hidden_states + if hidden_states.ndim == 4: + hidden_states = hidden_states.squeeze(1) + + return hidden_states diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index ece3cb282..32c121f07 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -577,7 +577,7 @@ def train_one_step(step_, data_item_, prof_=None): if not args.multi_scale: assert torch.all(attn_mask) x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 - attn_mask = attn_mask.to(accelerator.device) # B T+num_images L + attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W input_ids = input_ids.to(accelerator.device) # B 1+num_images L cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) diff --git a/scripts/text_condition/train_udit3d_61x240p.sh b/scripts/text_condition/train_udit3d_61x240p.sh new file mode 100644 index 000000000..f8033c7d0 --- /dev/null +++ b/scripts/text_condition/train_udit3d_61x240p.sh @@ -0,0 +1,55 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="testnpuudit" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v.py \ + --model UDiTT2V-B/111 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ + --sample_rate 1 \ + --num_frames 61 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=4 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="testnpuudit" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tiling \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 5.0 \ + --resume_from_checkpoint "latest" From 7a1b6dc5279ff9ded6e0c2b2a1522610e96cecd5 Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Tue, 4 Jun 2024 00:04:59 +0800 Subject: [PATCH 020/134] support train image --- examples/convert_pixart_to_latte.py | 21 ++++ examples/rec_imvi_vae.py | 101 +++--------------- opensora/dataset/t2v_datasets.py | 12 +-- opensora/models/diffusion/__init__.py | 1 + .../models/diffusion/latte/modeling_latte.py | 78 ++++++++++---- opensora/models/diffusion/latte/modules.py | 3 +- .../diffusion/opensora/modeling_opensora.py | 36 ++++--- opensora/models/diffusion/opensora/modules.py | 30 +++--- opensora/train/train_t2v.py | 1 + opensora/utils/dataset_utils.py | 2 - scripts/causalvae/reconstruction.sh | 10 +- scripts/text_condition/train_image_256x256.sh | 53 +++++++++ .../text_condition/train_udit3d_61x240p.sh | 15 ++- .../text_condition/train_video21d_65x240p.sh | 19 ++-- .../text_condition/train_video3d_65x240p.sh | 18 ++-- 15 files changed, 219 insertions(+), 181 deletions(-) create mode 100644 examples/convert_pixart_to_latte.py create mode 100644 scripts/text_condition/train_image_256x256.sh diff --git a/examples/convert_pixart_to_latte.py b/examples/convert_pixart_to_latte.py new file mode 100644 index 000000000..c76f52b69 --- /dev/null +++ b/examples/convert_pixart_to_latte.py @@ -0,0 +1,21 @@ +import torch + +from safetensors.torch import load_file as safe_load + +path = "diffusion_pytorch_model.safetensors" +ckpt = safe_load(path, device="cpu") +new_ckpt = {} +for k, v in ckpt.items(): + if 'transformer_blocks' in k: + split = k.split('.') + idx = int(split[1]) + if idx % 2 == 0: + split[1] = str(idx//2) + else: + split[0] = 'temporal_transformer_blocks' + split[1] = str((idx-1)//2) + new_k = '.'.join(split) + else: + new_k = k + new_ckpt[new_k] = v +torch.save(new_ckpt, 'convert_weight.pt') \ No newline at end of file diff --git a/examples/rec_imvi_vae.py b/examples/rec_imvi_vae.py index 35315157f..2fd83ee8a 100644 --- a/examples/rec_imvi_vae.py +++ b/examples/rec_imvi_vae.py @@ -12,56 +12,13 @@ from torch.nn import functional as F from pytorchvideo.transforms import ShortSideScale from torchvision.transforms import Lambda, Compose - import sys sys.path.append(".") from opensora.models.ae import getae_wrapper -from opensora.dataset.transform import CenterCropVideo, resize +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo from opensora.models.ae.videobase import CausalVAEModel -def process_in_chunks( - video_data: torch.Tensor, - model: torch.nn.Module, - chunk_size: int, - overlap: int, - device: str, -): - assert (chunk_size + overlap - 1) % 4 == 0 - num_frames = video_data.size(2) - output_chunks = [] - - start = 0 - while start < num_frames: - end = min(start + chunk_size, num_frames) - if start + chunk_size + overlap < num_frames: - end += overlap - chunk = video_data[:, :, start:end, :, :] - - with torch.no_grad(): - chunk = chunk.to(device) - latents = model.encode(chunk) - recon_chunk = model.decode(latents.half()).cpu().float() # b t c h w - recon_chunk = recon_chunk.permute(0, 2, 1, 3, 4) - - if output_chunks: - overlap_step = min(overlap, recon_chunk.shape[2]) - overlap_tensor = ( - output_chunks[-1][:, :, -overlap_step:] * 1 / 4 - + recon_chunk[:, :, :overlap_step] * 3 / 4 - ) - output_chunks[-1] = torch.cat( - (output_chunks[-1][:, :, :-overlap], overlap_tensor), dim=2 - ) - if end < num_frames: - output_chunks.append(recon_chunk[:, :, overlap:]) - else: - output_chunks.append(recon_chunk[:, :, :, :, :]) - else: - output_chunks.append(recon_chunk) - start += chunk_size - return torch.cat(output_chunks, dim=2).permute(0, 2, 1, 3, 4) - def array_to_video(image_array: npt.NDArray, fps: float = 30.0, output_file: str = 'output_video.mp4') -> None: height, width, channels = image_array[0].shape @@ -109,38 +66,12 @@ def read_video(video_path: str, num_frames: int, sample_rate: int) -> torch.Tens return video_data -class ResizeVideo: - def __init__( - self, - size, - interpolation_mode="bilinear", - ): - self.size = size - - self.interpolation_mode = interpolation_mode - - def __call__(self, clip): - _, _, h, w = clip.shape - if w < h: - new_h = int(math.floor((float(h) / w) * self.size)) - new_w = self.size - else: - new_h = self.size - new_w = int(math.floor((float(w) / h) * self.size)) - return torch.nn.functional.interpolate( - clip, size=(new_h, new_w), mode=self.interpolation_mode, align_corners=False, antialias=True - ) - - def __repr__(self) -> str: - return f"{self.__class__.__name__}(size={self.size}, interpolation_mode={self.interpolation_mode}" - - -def preprocess(video_data: torch.Tensor, short_size: int = 128, crop_size: Optional[int] = None) -> torch.Tensor: +def preprocess(video_data: torch.Tensor, height: int = 128, width: int = 128) -> torch.Tensor: transform = Compose( [ - Lambda(lambda x: ((x / 255.0) * 2 - 1)), - ResizeVideo(size=short_size), - CenterCropVideo(crop_size) if crop_size is not None else Lambda(lambda x: x), + ToTensorVideo(), + CenterCropResizeVideo((height, width)), + Lambda(lambda x: 2. * x - 1.) ] ) @@ -163,17 +94,14 @@ def main(args: argparse.Namespace): vae = vae.half() with torch.no_grad(): - x_vae = preprocess(read_video(args.video_path, args.num_frames, args.sample_rate), args.resolution, - args.crop_size) + x_vae = preprocess(read_video(args.video_path, args.num_frames, args.sample_rate), args.height, + args.width) x_vae = x_vae.to(device, dtype=torch.float16) # b c t h w - if args.enable_time_chunk: - video_recon = process_in_chunks(x_vae, vae, 7, 2, device) - else: - latents = vae.encode(x_vae) - latents = latents.to(torch.float16) - video_recon = vae.decode(latents) # b t c h w - - if video_recon.shape[2] == 1: + latents = vae.encode(x_vae) + latents = latents.to(torch.float16) + video_recon = vae.decode(latents) # b t c h w + + if video_recon.shape[1] == 1: x = video_recon[0, 0, :, :, :] x = x.squeeze() x = x.detach().cpu().numpy() @@ -195,14 +123,13 @@ def main(args: argparse.Namespace): parser.add_argument('--ae_path', type=str, default='') parser.add_argument('--model_path', type=str, default='results/pretrained') parser.add_argument('--fps', type=int, default=30) - parser.add_argument('--resolution', type=int, default=336) - parser.add_argument('--crop_size', type=int, default=None) + parser.add_argument('--height', type=int, default=336) + parser.add_argument('--width', type=int, default=336) parser.add_argument('--num_frames', type=int, default=100) parser.add_argument('--sample_rate', type=int, default=1) parser.add_argument('--device', type=str, default="cuda") parser.add_argument('--tile_overlap_factor', type=float, default=0.25) parser.add_argument('--enable_tiling', action='store_true') - parser.add_argument('--enable_time_chunk', action='store_true') args = parser.parse_args() main(args) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 33f2f4a83..b70b3d08e 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -82,7 +82,7 @@ def get_video(self, idx): video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W - # video = torch.rand(65, 3, 240, 320) + # video = torch.rand(509, 3, 240, 320) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] @@ -113,12 +113,12 @@ def get_image(self, idx): idx = idx % len(self.img_cap_list) # out of range image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] - image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] - image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] - image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + # image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + # image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + # image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + # image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] - # image = [torch.rand(1, 3, 512, 512) for i in image_data] + image = [torch.rand(1, 3, 256, 256) for i in image_data] image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] caps = [i['cap'] for i in image_data] diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index 0144a701f..2a96d0b97 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -11,5 +11,6 @@ from .udit.modeling_udit import UDiT_models_class Diffusion_models_class = {} Diffusion_models_class.update(Latte_models_class) +Diffusion_models_class.update(OpenSora_models_class) Diffusion_models_class.update(UDiT_models_class) \ No newline at end of file diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index d4bb469f5..9cfb3b4c8 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -361,9 +361,10 @@ def forward( # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) - # import ipdb;ipdb.set_trace() if attention_mask is None: attention_mask = torch.ones((input_batch_size, frame+use_image_num, h, w), device=hidden_states.device, dtype=hidden_states.dtype) + else: + attention_mask = attention_mask.to(hidden_states.dtype) attention_mask = self.vae_to_diff_mask(attention_mask, use_image_num) dtype = attention_mask.dtype attention_mask_compress = F.max_pool2d(attention_mask.float(), kernel_size=self.compress_kv_factor, stride=self.compress_kv_factor) @@ -617,7 +618,7 @@ def from_pretrained_2d(cls, pretrained_model_path, subfolder=None, **kwargs): return model # depth = num_layers * 2 -def LatteT2V_XL_122(**kwargs): +def LatteT2V_S_122(**kwargs): return LatteT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) @@ -625,20 +626,27 @@ def LatteT2V_XL_122(**kwargs): # return LatteT2V(num_layers=28, attention_head_dim=64, num_attention_heads=18, patch_size_t=1, patch_size=2, # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) -def LatteT2V_122(**kwargs): - return LatteT2V(num_layers=28, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) +# def LatteT2V_122(**kwargs): +# return LatteT2V(num_layers=28, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + + +def LatteT2V_L_122(**kwargs): + return LatteT2V(num_layers=16, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) Latte_models = { - "LatteT2V-XL/122": LatteT2V_XL_122, + "LatteT2V-S/122": LatteT2V_S_122, # "LatteT2V-D64-XL/122": LatteT2V_D64_XL_122, - "LatteT2V/122": LatteT2V_122, + # "LatteT2V/122": LatteT2V_122, + "LatteT2V-L/122": LatteT2V_L_122, } Latte_models_class = { - "LatteT2V-XL/122": LatteT2V, + "LatteT2V-S/122": LatteT2V, # "LatteT2V-D64-XL/122": LatteT2V_D64_XL_122, "LatteT2V/122": LatteT2V, + "LatteT2V-L/122": LatteT2V, } if __name__ == '__main__': @@ -652,9 +660,10 @@ def LatteT2V_122(**kwargs): 'attention_mode': 'xformers', 'use_rope': False, 'model_max_length': 300, - 'max_image_size': 512, + 'max_height': 512, + 'max_width': 512, 'num_frames': 65, - 'use_image_num': 16, + 'use_image_num': 4, 'compress_kv_factor': 1 } ) @@ -663,20 +672,21 @@ def LatteT2V_122(**kwargs): cond_c = 4096 num_timesteps = 1000 ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: - args.video_length = video_length = (args.num_frames - 1) // ae_stride_t + 1 + num_frames = (args.num_frames - 1) // ae_stride_t + 1 else: - video_length = args.num_frames // ae_stride_t + num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:6') - model = LatteT2V_D64_XL_122( + model = LatteT2V_L_122( in_channels=ae_channel_config[args.ae], out_channels=ae_channel_config[args.ae] * 2, # caption_channels=4096, # cross_attention_dim=1152, attention_bias=True, sample_size=latent_size, + sample_size_t=num_frames, num_vector_embeds=None, activation_fn="gelu-approximate", num_embeds_ada_norm=1000, @@ -688,22 +698,46 @@ def LatteT2V_122(**kwargs): norm_elementwise_affine=False, norm_eps=1e-6, attention_type='default', - video_length=video_length, attention_mode=args.attention_mode, compress_kv_factor=args.compress_kv_factor, use_rope=args.use_rope, model_max_length=args.model_max_length, ).to(device) - # try: - # ckpt = torch.load(r"t2v.pt", map_location='cpu')['model'] - # model.load_state_dict(ckpt) - # except Exception as e: - # print(e) + + try: + path = "/storage/ongoing/Open-Sora-Plan/testimg_/checkpoint-100/model/diffusion_pytorch_model.safetensors" + from safetensors.torch import load_file as safe_load + ckpt = safe_load(path, device="cpu") + # import ipdb;ipdb.set_trace() + new_ckpt = {} + for k, v in ckpt.items(): + if 'transformer_blocks' in k: + split = k.split('.') + idx = int(split[1]) + if idx % 2 == 0: + split[1] = str(idx//2) + else: + split[0] = 'temporal_transformer_blocks' + split[1] = str((idx-1)//2) + new_k = '.'.join(split) + else: + new_k = k + new_ckpt[new_k] = v + + # import ipdb;ipdb.set_trace() + # if ckpt['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and ckpt['pos_embed.proj.weight'].ndim == 4: + # repeat = model.pos_embed.proj.weight.shape[2] + # ckpt['pos_embed.proj.weight'] = ckpt['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del ckpt['proj_out.weight'], ckpt['proj_out.bias'] + msg = model.load_state_dict(new_ckpt, strict=False) + print(msg) + except Exception as e: + print(e) print(model) - x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w).to(device) + x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) - attn_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.max_image_size//ae_stride_h//2, args.max_image_size//ae_stride_w//2)).to(device) # B L or B 1+num_images L + attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L timestep = torch.randint(0, 1000, (b,), device=device) model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, diff --git a/opensora/models/diffusion/latte/modules.py b/opensora/models/diffusion/latte/modules.py index 40ff6aef9..b0de16ed8 100644 --- a/opensora/models/diffusion/latte/modules.py +++ b/opensora/models/diffusion/latte/modules.py @@ -170,7 +170,7 @@ def __init__(self, in_features, hidden_size, num_tokens=120): self.linear_1 = nn.Linear(in_features=in_features, out_features=hidden_size, bias=True) self.act_1 = nn.GELU(approximate="tanh") self.linear_2 = nn.Linear(in_features=hidden_size, out_features=hidden_size, bias=True) - self.register_buffer("y_embedding", nn.Parameter(torch.randn(num_tokens, in_features) / in_features**0.5)) + # self.register_buffer("y_embedding", nn.Parameter(torch.randn(num_tokens, in_features) / in_features**0.5)) def forward(self, caption, force_drop_ids=None): hidden_states = self.linear_1(caption) @@ -1692,7 +1692,6 @@ def forward( # 2. Prepare GLIGEN inputs cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} gligen_kwargs = cross_attention_kwargs.pop("gligen", None) - # import ipdb;ipdb.set_trace() attn_output = self.attn1( norm_hidden_states, encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 21f244bfa..25c0309e5 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -286,7 +286,6 @@ def forward( If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a `tuple` where the first element is the sample tensor. """ - # import ipdb;ipdb.set_trace() batch_size, c, frame, h, w = hidden_states.shape frame = frame - use_image_num # 21-4=17 if cross_attention_kwargs is not None: @@ -303,7 +302,6 @@ def forward( # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) attention_mask_vid, attention_mask_img = None, None - # import ipdb;ipdb.set_trace() if attention_mask is not None and attention_mask.ndim == 4: # assume that mask is expressed as: # (1 = keep, 0 = discard) @@ -472,7 +470,6 @@ def custom_forward(*inputs): def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): # batch_size = hidden_states.shape[0] - # import ipdb;ipdb.set_trace() hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states.to(self.dtype), frame) timestep_vid, timestep_img = None, None embedded_timestep_vid, embedded_timestep_img = None, None @@ -553,6 +550,10 @@ def OpenSoraT2V_B_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) +def OpenSoraT2V_L_122(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + # def OpenSoraT2V_S_222(**kwargs): # return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) @@ -577,7 +578,7 @@ def OpenSoraT2V_B_122(**kwargs): OpenSora_models = { "OpenSoraT2V-S/122": OpenSoraT2V_S_122, "OpenSoraT2V-B/122": OpenSoraT2V_B_122, - # "OpenSoraT2V-L/122": OpenSoraT2V_L_122, + "OpenSoraT2V-L/122": OpenSoraT2V_L_122, # "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, # "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, # "OpenSoraT2V-S/222": OpenSoraT2V_S_222, @@ -590,6 +591,9 @@ def OpenSoraT2V_B_122(**kwargs): OpenSora_models_class = { "OpenSoraT2V-S/122": OpenSoraT2V, "OpenSoraT2V-B/122": OpenSoraT2V, + "OpenSoraT2V-L/122": OpenSoraT2V, + # "OpenSoraT2V-S/222": OpenSoraT2V, + # "OpenSoraT2V-B/222": OpenSoraT2V, } if __name__ == '__main__': @@ -603,8 +607,9 @@ def OpenSoraT2V_B_122(**kwargs): 'attention_mode': 'xformers', 'use_rope': False, 'model_max_length': 300, - 'max_image_size': 512, - 'num_frames': 17, + 'max_height': 512, + 'max_width': 512, + 'num_frames': 1, 'use_image_num': 0, 'compress_kv_factor': 1 } @@ -614,17 +619,17 @@ def OpenSoraT2V_B_122(**kwargs): cond_c = 4096 num_timesteps = 1000 ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: - args.video_length = video_length = (args.num_frames - 1) // ae_stride_t + 1 + num_frames = (args.num_frames - 1) // ae_stride_t + 1 else: - args.video_length = video_length = args.num_frames // ae_stride_t + num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:0') - model = OpenSoraT2V_S_222(in_channels=4, + model = OpenSoraT2V_L_122(in_channels=4, out_channels=8, sample_size=latent_size, - sample_size_t=video_length, + sample_size_t=num_frames, activation_fn="gelu-approximate", attention_bias=True, attention_type="default", @@ -637,8 +642,9 @@ def OpenSoraT2V_B_122(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False).to(device) + try: - path = "/remote-home1/yeyang/dev3d/Open-Sora-Plan/cache_dir/models--PixArt-alpha--PixArt-Sigma-XL-2-512-MS/snapshots/786c445c97ddcc0eb2faa157b131ac71ee1935a2/transformer/diffusion_pytorch_model.safetensors" + path = "PixArt-Alpha-XL-2-512.safetensors" from safetensors.torch import load_file as safe_load ckpt = safe_load(path, device="cpu") # import ipdb;ipdb.set_trace() @@ -651,10 +657,10 @@ def OpenSoraT2V_B_122(**kwargs): except Exception as e: print(e) print(model) - - x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w).to(device) + print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B') + x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) - attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w)).to(device) # B L or B 1+num_images L + attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L timestep = torch.randint(0, 1000, (b,), device=device) model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 5d2c9aed2..85b730854 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -181,9 +181,9 @@ def __init__( self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t self.interpolation_scale_t = interpolation_scale_t - temp_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) - self.register_buffer("temp_embed", torch.from_numpy(temp_embed).float().unsqueeze(0), persistent=False) - self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) + temp_pos_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) + # self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) def forward(self, latent, num_frames): b, _, _, _, _ = latent.shape @@ -219,32 +219,34 @@ def forward(self, latent, num_frames): if self.num_frames != num_frames: # import ipdb;ipdb.set_trace() # raise NotImplementedError - temp_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_embed.shape[-1], + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], grid_size=num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t, ) - temp_embed = torch.from_numpy(temp_embed) - temp_embed = temp_embed.float().unsqueeze(0).to(latent.device) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) else: - temp_embed = self.temp_embed + temp_pos_embed = self.temp_pos_embed latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] - # import ipdb;ipdb.set_trace() - temp_embed = temp_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - # temp_embed = temp_embed.unsqueeze(2) - # print('raw value:', self.temp_embed_gate, 'tanh:', self.temp_embed_gate.tanh()) - video_latent = (video_latent + temp_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - image_latent = (image_latent + temp_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None + + if num_frames == 1 and image_latent is None: + image_latent = video_latent + video_latent = None return video_latent, image_latent diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 32c121f07..a3d01ee1e 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -377,6 +377,7 @@ def load_model_hook(models, input_dir): train_dataloader = torch.utils.data.DataLoader( train_dataset, shuffle=True, + pin_memory=True, collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 34e9e1a9d..1cc016d25 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -111,7 +111,6 @@ def __call__(self, batch): return pad_batch_tubes, attention_mask, input_ids, cond_mask def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, patch_size_thw, extra_1): - # import ipdb;ipdb.set_trace() # pad to max multiple of ds_stride batch_input_size = [i.shape for i in batch_tubes] # [(c t h w), (c t h w)] max_t, max_h, max_w = max_thw @@ -168,5 +167,4 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p # 0, max_patchify_latent_size[1] - i[1], # 0, max_patchify_latent_size[0] - i[0]), value=0) for i in valid_patchify_latent_size] # attention_mask = torch.stack(attention_mask) # b t h w - return pad_batch_tubes, attention_mask diff --git a/scripts/causalvae/reconstruction.sh b/scripts/causalvae/reconstruction.sh index 3e89d2c28..648103164 100644 --- a/scripts/causalvae/reconstruction.sh +++ b/scripts/causalvae/reconstruction.sh @@ -1,11 +1,11 @@ CUDA_VISIBLE_DEVICES=0 python examples/rec_imvi_vae.py \ - --ae_path "../../Open-Sora-Plan/CausalVAEModel_4x8x8/" \ - --video_path origin_tiger.mp4 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_path /storage/mixkit/LanguageBind/Open-Sora-Plan-v1.1.0/all_mixkit/mixkit/Trains/mixkit-three-boys-reflected-in-the-mirrors-of-a-prism-34524.mp4 \ --rec_path rec.mp4 \ --device cuda \ --sample_rate 1 \ - --num_frames 65 \ - --resolution 512 \ - --crop_size 512 \ + --num_frames 61 \ + --height 512 \ + --width 512 \ --ae CausalVAEModel_4x8x8 \ --enable_tiling \ No newline at end of file diff --git a/scripts/text_condition/train_image_256x256.sh b/scripts/text_condition/train_image_256x256.sh new file mode 100644 index 000000000..da0cab8bb --- /dev/null +++ b/scripts/text_condition/train_image_256x256.sh @@ -0,0 +1,53 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="testimg_" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-L/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=100 \ + --output_dir="testimg_" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 2.5 diff --git a/scripts/text_condition/train_udit3d_61x240p.sh b/scripts/text_condition/train_udit3d_61x240p.sh index f8033c7d0..f4175a2ba 100644 --- a/scripts/text_condition/train_udit3d_61x240p.sh +++ b/scripts/text_condition/train_udit3d_61x240p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="testnpuudit" +export PROJECT="testnpuudit_" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,7 +12,7 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v.py \ --model UDiTT2V-B/111 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -23,7 +23,7 @@ accelerate launch \ --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ - --num_frames 61 \ + --num_frames 509 \ --max_height 240 \ --max_width 320 \ --interpolation_scale_t 1.0 \ @@ -31,17 +31,17 @@ accelerate launch \ --interpolation_scale_w 0.5 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=4 \ + --train_batch_size=2 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ + --learning_rate=1e-4 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="testnpuudit" \ + --output_dir="testnpuudit_" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 512 \ @@ -51,5 +51,4 @@ accelerate launch \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 5.0 \ - --resume_from_checkpoint "latest" + --guidance_scale 2.0 diff --git a/scripts/text_condition/train_video21d_65x240p.sh b/scripts/text_condition/train_video21d_65x240p.sh index 6b2a62675..9f105c222 100644 --- a/scripts/text_condition/train_video21d_65x240p.sh +++ b/scripts/text_condition/train_video21d_65x240p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="testnpu21d" +export PROJECT="testnpu21d_" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,9 +12,9 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v.py \ - --model LatteT2V-XL/122 \ + --model LatteT2V/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ @@ -23,15 +23,15 @@ accelerate launch \ --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ - --num_frames 65 \ - --max_height 240 \ - --max_width 320 \ + --num_frames 509 \ + --max_height 480 \ + --max_width 640 \ --interpolation_scale_t 1.0 \ --interpolation_scale_h 0.5 \ --interpolation_scale_w 0.5 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=4 \ + --train_batch_size=1 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -41,7 +41,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=100 \ - --output_dir="testnpu21d" \ + --output_dir="testnpu21d_" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 512 \ @@ -50,5 +50,4 @@ accelerate launch \ --enable_tracker \ --snr_gamma 5.0 \ --use_ema \ - --ema_start_step 0 \ - --pretrained /storage/t2v.pt \ No newline at end of file + --ema_start_step 0 \ No newline at end of file diff --git a/scripts/text_condition/train_video3d_65x240p.sh b/scripts/text_condition/train_video3d_65x240p.sh index 1136d80ea..b98ae1a32 100644 --- a/scripts/text_condition/train_video3d_65x240p.sh +++ b/scripts/text_condition/train_video3d_65x240p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="testnpu3d" +export PROJECT="testnpu3d_" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,7 +12,7 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ @@ -23,15 +23,15 @@ accelerate launch \ --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ - --num_frames 65 \ - --max_height 240 \ - --max_width 320 \ + --num_frames 509 \ + --max_height 480 \ + --max_width 640 \ --interpolation_scale_t 1.0 \ --interpolation_scale_h 0.5 \ --interpolation_scale_w 0.5 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=4 \ + --train_batch_size=1 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -41,7 +41,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=100 \ - --output_dir="testnpu3d" \ + --output_dir="testnpu3d_" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 512 \ @@ -51,6 +51,4 @@ accelerate launch \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --pretrained PixArt-Alpha-XL-2-512.safetensors \ - --guidance_scale 7.5 \ - --resume_from_checkpoint "latest" + --guidance_scale 7.5 From 7194f800c54c4eaac36b445b7b7ec07dc60cbe2e Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Tue, 4 Jun 2024 00:06:40 +0800 Subject: [PATCH 021/134] del random image --- opensora/dataset/t2v_datasets.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index b70b3d08e..c101917b9 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -113,12 +113,12 @@ def get_image(self, idx): idx = idx % len(self.img_cap_list) # out of range image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - # image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] - # image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] - # image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] - # image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] - image = [torch.rand(1, 3, 256, 256) for i in image_data] + # image = [torch.rand(1, 3, 256, 256) for i in image_data] image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] caps = [i['cap'] for i in image_data] From 9ebe0cd70e56a92a86e12a4a9cbece426cbfe7fa Mon Sep 17 00:00:00 2001 From: New User <2267330597@qq.com> Date: Tue, 4 Jun 2024 00:18:00 +0800 Subject: [PATCH 022/134] sample image --- opensora/sample/sample_t2v.py | 15 +++++++++------ scripts/text_condition/train_image_256x256.sh | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 3e1fc3372..0e61d97ce 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -34,20 +34,23 @@ def main(args): weight_dtype = torch.float16 device = torch.device(args.device) - vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) - # vae = getae_wrapper(args.ae)(args.ae_path) + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) vae.vae = vae.vae.to(device=device, dtype=weight_dtype) if args.enable_tiling: vae.vae.enable_tiling() vae.vae.tile_overlap_factor = args.tile_overlap_factor vae.vae_scale_factor = ae_stride_config[args.ae] + # if args.model_3d: + # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # else: + # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + if args.model_3d: - transformer_model = OpenSoraT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: - transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - # transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) diff --git a/scripts/text_condition/train_image_256x256.sh b/scripts/text_condition/train_image_256x256.sh index da0cab8bb..b63bb7956 100644 --- a/scripts/text_condition/train_image_256x256.sh +++ b/scripts/text_condition/train_image_256x256.sh @@ -40,7 +40,7 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=100 \ + --checkpointing_steps=10 \ --output_dir="testimg_" \ --allow_tf32 \ --use_deepspeed \ From d105f40b5eb3bee7da4e2d3f3dda5e06110c11eb Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Wed, 5 Jun 2024 09:19:29 +0800 Subject: [PATCH 023/134] fix dataset and model on npu --- opensora/dataset/t2v_datasets.py | 71 +++++- opensora/dataset/virtual_disk.py | 230 ++++++++++++++++++ .../diffusion/opensora/modeling_opensora.py | 21 +- opensora/models/diffusion/opensora/modules.py | 11 +- .../models/diffusion/udit/modeling_udit.py | 21 +- opensora/models/diffusion/udit/modules.py | 72 ++++-- opensora/models/text_encoder/__init__.py | 14 +- opensora/train/train_t2v.py | 8 +- scripts/text_condition/sample_video_on_npu.sh | 10 +- .../train_image_256x256_on_npu.sh | 46 ++++ .../train_udit3d_61x240p_on_npu.sh | 46 ++++ .../train_video21d_65x240p_on_npu.sh | 6 +- scripts/train_data/image_data_on_npu.txt | 2 +- scripts/train_data/video_data_on_npu.txt | 4 +- 14 files changed, 495 insertions(+), 67 deletions(-) create mode 100644 opensora/dataset/virtual_disk.py create mode 100644 scripts/text_condition/train_image_256x256_on_npu.sh create mode 100644 scripts/text_condition/train_udit3d_61x240p_on_npu.sh diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index c101917b9..f3c3628f5 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -1,3 +1,11 @@ +try: + import torch_npu + from opensora.npu_config import npu_config + from opensora.dataset.virtual_disk import VirtualDisk +except: + torch_npu = None + npu_config = None +import glob import json import os, io, csv, math, random import numpy as np @@ -15,7 +23,18 @@ from opensora.utils.dataset_utils import DecordInit from opensora.utils.utils import text_preprocessing +def filter_json_by_existed_files(directory, data, postfix=".mp4"): + # 构建搜索模式,以匹配指定后缀的文件 + pattern = os.path.join(directory, '**', f'*{postfix}') + mp4_files = glob.glob(pattern, recursive=True) # 使用glob查找所有匹配的文件 + # 使用文件的绝对路径构建集合 + mp4_files_set = set(os.path.abspath(path) for path in mp4_files) + + # 过滤数据条目,只保留路径在mp4文件集合中的条目 + filtered_items = [item for item in data if item['path'] in mp4_files_set] + + return filtered_items def random_video_noise(t, c, h, w): vid = torch.rand(t, c, h, w) * 255.0 vid = vid.to(torch.uint8) @@ -41,7 +60,11 @@ def __init__(self, args, transform, temporal_sample, tokenizer): else: self.img_cap_list = self.get_img_cap_list() - + if npu_config is not None: + self.virtual_disk = VirtualDisk(f"/mnt/pandas_70m_{npu_config.get_local_rank()}") + self.n_used_elements = 0 + self.elements = list(range(self.__len__())) + print(f"n_elements: {len(self.elements)}") def __len__(self): if self.num_frames != 1: @@ -50,6 +73,9 @@ def __len__(self): return len(self.img_cap_list) def __getitem__(self, idx): + if npu_config is not None: + idx = self.elements[self.n_used_elements % len(self.elements)] + self.n_used_elements += 1 try: video_data, image_data = {}, {} if self.num_frames != 1: @@ -73,7 +99,7 @@ def get_video(self, idx): # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) video_path = self.vid_cap_list[idx]['path'] - frame_idx = self.vid_cap_list[idx]['frame_idx'] + frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] @@ -157,7 +183,11 @@ def tv_read(self, path, frame_idx=None): return video def decord_read(self, path, frame_idx=None): + if npu_config is not None: + path = self.virtual_disk.get_data(path) decord_vr = self.v_decoder(path) + if npu_config is not None: + self.virtual_disk.del_data(path) total_frames = len(decord_vr) # Sampling video frames if frame_idx is None: @@ -182,8 +212,20 @@ def get_vid_cap_list(self): folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] # print(folder_anno) for folder, anno in folder_anno: - with open(anno, 'r') as f: - vid_cap_list = json.load(f) + if npu_config is None: + with open(anno, 'r') as f: + vid_cap_list = json.load(f) + else: + # List all files in the directory + files = [f for f in os.listdir(anno) if os.path.isfile(os.path.join(anno, f))] + + # Read the contents of the first chunk + vid_cap_list = [] + for file in files[npu_config.rank::npu_config.world_size]: + print(os.path.join(anno, file)) + with open(os.path.join(anno, file), 'r') as f: + vid_cap_list.extend(json.load(f)) + print(f'Building {anno}...') for i in tqdm(range(len(vid_cap_list))): path = opj(folder, vid_cap_list[i]['path']) @@ -193,8 +235,7 @@ def get_vid_cap_list(self): vid_cap_lists += vid_cap_list return vid_cap_lists - def get_img_cap_list(self): - use_image_num = self.use_image_num if self.use_image_num != 0 else 1 + def read_images(self): img_cap_lists = [] with open(self.image_data, 'r') as f: folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] @@ -204,6 +245,22 @@ def get_img_cap_list(self): print(f'Building {anno}...') for i in tqdm(range(len(img_cap_list))): img_cap_list[i]['path'] = opj(folder, img_cap_list[i]['path']) + if npu_config is not None: + img_cap_list = filter_json_by_existed_files(folder, img_cap_list, postfix=".jpg") img_cap_lists += img_cap_list - img_cap_lists = [img_cap_lists[i: i+use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] return img_cap_lists[:-1] # drop last to avoid error length + + def get_img_cap_list(self): + use_image_num = self.use_image_num if self.use_image_num != 0 else 1 + if npu_config is None: + img_cap_lists = self.read_images() + img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + else: + img_cap_lists = npu_config.try_load_pickle("img_cap_lists", self.read_images) + img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] + return img_cap_lists + + + + diff --git a/opensora/dataset/virtual_disk.py b/opensora/dataset/virtual_disk.py new file mode 100644 index 000000000..5865a31b9 --- /dev/null +++ b/opensora/dataset/virtual_disk.py @@ -0,0 +1,230 @@ +import subprocess +import json +import pickle +from collections import OrderedDict +from opensora.npu_config import npu_config + +import sys +import os + +class SuppressStdout: + _instance = None + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = super(SuppressStdout, cls).__new__(cls, *args, **kwargs) + return cls._instance + + def __enter__(self): + self._original_stdout = sys.stdout + sys.stdout = open(os.devnull, 'w') + + def __exit__(self, exc_type, exc_value, traceback): + sys.stdout.close() + sys.stdout = self._original_stdout + +# 创建单例 + + +class ObsConnection: + """ + AK, SK, STS_TOKEN临时密钥有效时效云计算网站最长为24h + buckets & object: https://uconsole.ccaicc.com/#/mgt/modelarts -> 对象控制台 + keys & tokens: https://uconsole.ccaicc.com/#/mgt/modelarts -> 对象控制台 -> 获取访问密匙(AK 和 SK) + """ + def __init__(self): + with open(f"{npu_config.work_path}/scripts/train_data/key.json", "r") as f: + key = json.load(f) + self.AK = key["AK"] + self.SK = key["SK"] + self.endpoint = key["EP"] + self.bucket = "sora" + self.suppress_stdout = SuppressStdout() + + def connect(self, obs): + config_command = [ + obs, 'config', + '-i=' + self.AK, + '-k=' + self.SK, + '-e=' + self.endpoint + ] + result = subprocess.run(config_command, capture_output=True, text=True) + if result.returncode != 0: + print(f"Failed to configure obsutil: {result.stderr}") + else: + print("Successfully configured obsutil") + +class VirtualDisk: + """ + :param storage_dir: 内存虚拟磁盘的挂载点路径。 + :param size: 内存虚拟磁盘的大小,例如 '1G'。 + :param obs: linux 系统里面obs具体位置 + :param connection: 抽象出obs连接管理 + """ + def __init__(self, storage_dir, size="1G", obs="/home/opensora/obsutil_linux_arm64_5.5.12/obsutil"): + self.obs = obs + self.connection = ObsConnection() + self.connection.connect(obs) + os.makedirs(storage_dir, exist_ok=True) + self.storage_dir = storage_dir + self.size = self._convert_size_to_bytes(size) + if not self.is_tmpfs_mounted(): + self.create_ramdisk() + else: + print(f"{self.storage_dir} is already mounted as tmpfs.") + self.index_file = os.path.join(self.storage_dir, 'index.pkl') + self.index = self.load_index() + self.lru = OrderedDict() + self.current_size = self.get_total_storage_size() # 初始化时计算总大小 + + def _convert_size_to_bytes(self, size): + unit = size[-1].upper() + size_value = int(size[:-1]) + if unit == 'K': + return size_value * 1024 + elif unit == 'M': + return size_value * 1024 ** 2 + elif unit == 'G': + return size_value * 1024 ** 3 + else: + raise ValueError("Invalid size unit. Use K, M, or G.") + + """ + 创建并挂载一个 tmpfs 类型的内存虚拟磁盘。 + """ + def create_ramdisk(self): + try: + # 如果挂载点目录不存在,创建它 + if not os.path.exists(self.storage_dir): + os.makedirs(self.storage_dir) + # 挂载 tmpfs 到挂载点 + subprocess.run(['sudo', 'mount', '-t', 'tmpfs', '-o', f'size={self.size}', 'tmpfs', self.storage_dir], check=True) + print(f"Successfully mounted tmpfs on {self.storage_dir} with size {self.size}.") + + except subprocess.CalledProcessError as e: + print(f"Failed to mount tmpfs: {e}") + except Exception as e: + print(f"An error occurred: {e}") + + def load_index(self): + """ + 加载索引文件。 + :return: 索引字典。 + """ + if os.path.exists(self.index_file): + with open(self.index_file, 'rb') as f: + return pickle.load(f) + return {} + + def save_index(self): + """ + 保存索引文件。 + """ + with open(self.index_file, 'wb') as f: + pickle.dump(self.index, f) + + """ + 取消挂载内存虚拟磁盘。 + + :param storage_dir: 内存虚拟磁盘的挂载点路径。 + """ + def unmount_ramdisk(self): + try: + # 确保没有进程在使用挂载点后取消挂载 + subprocess.run(['sudo', 'umount', self.storage_dir], check=True) + print(f"Successfully unmounted tmpfs from {self.storage_dir}.") + except subprocess.CalledProcessError as e: + print(f"Failed to unmount tmpfs: {e}") + except Exception as e: + print(f"An error occurred: {e}") + + """ + 检查挂载点是否已经被挂载为 tmpfs。 + :param storage_dir: 挂载点路径。 + :return: 如果已挂载为 tmpfs,返回 True;否则返回 False。 + """ + def is_tmpfs_mounted(self): + try: + result = subprocess.run(['mountpoint', '-q', self.storage_dir], check=False) + if result.returncode == 0: + return True + return False + except Exception as e: + print(f"An error occurred while checking if tmpfs is mounted: {e}") + return False + + def get_data(self, key): + """ + 获取存储在本地磁盘上的数据。如果数据不存在,通过 obsutil 从远端获取并存储。 + :param key: 数据的唯一键。 + :return: 数据。 + """ + # if key in self.index: + # data_file = self.index[key] + # if os.path.exists(data_file): + # self.lru.move_to_end(key) + # with open(data_file, 'rb') as f: + # # print(f"Successfully get {key} from local") + # return pickle.load(f) + + + # 如果数据不存在,使用 obsutil 从远端获取 + object_name = key # 假设 key 对应于远端对象名称 + local_path = os.path.join(self.storage_dir, key) + + with self.connection.suppress_stdout: + self.download_and_convert_to_pickle(self.connection.bucket, object_name, local_path) + + # 保存数据的位置 + # self.index[key] = local_path + # self.save_index() + # self.lru[key] = local_path + # + # file_size = os.path.getsize(local_path) + # self.current_size += file_size + + # self.ensure_storage_limit() + + return local_path + + def del_data(self, local_path): + os.remove(local_path) + + def download_and_convert_to_pickle(self, bucket, object_name, local_path): + """ + 使用 obsutil 从 OBS 下载文件并转换为 pickle 格式存储到本地路径。 + :param bucket: OBS 存储桶名称。 + :param object_name: OBS 中的对象名称。 + :param local_path: 本地文件路径。 + """ + # try: + # 下载文件到local_path路径 + subprocess.run([self.obs, 'cp', f'obs://{bucket}/{object_name}', local_path], check=True) + # print(f"Successfully downloaded obs://{bucket}/{object_name} to {local_path}.") + + # except subprocess.CalledProcessError as e: + # print(f"Failed to download obs://{bucket}/{object_name} to {local_path}: {e}") + + def ensure_storage_limit(self): + """ + 确保存储总大小不超过虚拟磁盘大小,超出时根据LRU策略删除最旧的文件。 + """ + while self.current_size > self.size: + oldest_key, oldest_path = self.lru.popitem(last=False) + file_size = os.path.getsize(oldest_path) + os.remove(oldest_path) + del self.index[oldest_key] + self.save_index() + print(f"Removed {oldest_key} to free up {file_size} bytes.") + self.current_size -= file_size + + def get_total_storage_size(self): + """ + 获取当前所有存储文件的总大小。 + :return: 总大小(字节)。 + """ + total_size = 0 + for path in self.lru.values(): + if os.path.exists(path): + total_size += os.path.getsize(path) + return total_size \ No newline at end of file diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 25c0309e5..a91d1c562 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -347,14 +347,14 @@ def forward( encoder_attention_mask_img = encoder_attention_mask_vid encoder_attention_mask_vid = None - if npu_config is not None and attention_mask_vid is not None: - attention_mask_vid = npu_config.get_attention_mask(attention_mask_vid, attention_mask_vid.shape[-1]) - encoder_attention_mask_vid = npu_config.get_attention_mask(encoder_attention_mask_vid, - attention_mask_vid.shape[-2]) - if npu_config is not None and attention_mask_img is not None: - attention_mask_img = npu_config.get_attention_mask(attention_mask_img, attention_mask_img.shape[-1]) - encoder_attention_mask_img = npu_config.get_attention_mask(encoder_attention_mask_img, - attention_mask_img.shape[-2]) + if npu_config is not None and attention_mask_vid is not None: + attention_mask_vid = npu_config.get_attention_mask(attention_mask_vid, attention_mask_vid.shape[-1]) + encoder_attention_mask_vid = npu_config.get_attention_mask(encoder_attention_mask_vid, + attention_mask_vid.shape[-2]) + if npu_config is not None and attention_mask_img is not None: + attention_mask_img = npu_config.get_attention_mask(attention_mask_img, attention_mask_img.shape[-1]) + encoder_attention_mask_img = npu_config.get_attention_mask(encoder_attention_mask_img, + attention_mask_img.shape[-2]) # 1. Input @@ -367,7 +367,6 @@ def forward( frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy # 2. Blocks - # import ipdb;ipdb.set_trace() for block in self.transformer_blocks: if self.training and self.gradient_checkpointing: @@ -547,8 +546,8 @@ def OpenSoraT2V_S_122(**kwargs): norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_122(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) def OpenSoraT2V_L_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 85b730854..736bd0eb5 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -410,10 +410,13 @@ def __call__( ) if attention_mask is not None: - attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) - # scaled_dot_product_attention expects attention_mask shape to be - # (batch, heads, source_length, target_length) - attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + if npu_config is None: + attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + else: + attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py index ed9a815a2..6c7f5342c 100644 --- a/opensora/models/diffusion/udit/modeling_udit.py +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -441,10 +441,8 @@ def forward( if npu_config is not None and attention_mask is not None: - attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-2]) - + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-1]) # 1. Input added_cond_kwargs = {"resolution": None, "aspect_ratio": None} @@ -501,6 +499,10 @@ def custom_forward(*inputs): # encoder_2 out_enc_level2 = inp_enc_level2 + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -533,6 +535,10 @@ def custom_forward(*inputs): inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) frame, height, width = frame // 2, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + # latent latent = inp_enc_level3 if self.training and self.gradient_checkpointing: @@ -571,6 +577,11 @@ def custom_forward(*inputs): inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) out_dec_level2 = inp_dec_level2 + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + + print(out_dec_level2.size(), attention_bias.size(), encoder_hidden_states_2.size(), encoder_attention_mask.size()) if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -607,6 +618,10 @@ def custom_forward(*inputs): inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) out_dec_level1 = inp_dec_level1 + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py index f2e04e46a..91bc78efc 100644 --- a/opensora/models/diffusion/udit/modules.py +++ b/opensora/models/diffusion/udit/modules.py @@ -180,7 +180,11 @@ def __init__( def forward(self, latent): b, _, num_frames, _, _ = latent.shape height, width = latent.shape[-2], latent.shape[-1] - latent = self.proj(latent) # b c t h w + if npu_config is None: + latent = self.proj(latent) # b c t h w + else: + latent_dtype = latent.dtype + latent = npu_config.run_conv3d(self.proj, latent, latent_dtype) if self.flatten: latent = rearrange(latent, 'b c t h w -> (b t) (h w) c') # B C T H W -> BT N C @@ -247,7 +251,11 @@ def __init__(self, *args, **kwargs): def forward(self, x): x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) - x = self.layer(x) + (x if self.down_shortcut else 0) + if npu_config is None: + x = self.layer(x) + (x if self.down_shortcut else 0) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) return rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) d (t h w)', dt=self.down_factor, dh=self.down_factor, dw=self.down_factor) @@ -305,10 +313,13 @@ def __call__( ) if attention_mask is not None: - attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) - # scaled_dot_product_attention expects attention_mask shape to be - # (batch, heads, source_length, target_length) - attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + if npu_config is None: + attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + else: + attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) @@ -426,7 +437,11 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0), mode='reflect') - x = self.body(x) + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.body, x, x_dtype) x = rearrange(x, 'b d t h w -> b (t h w) d') attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=frames, h=height, w=width) @@ -435,11 +450,6 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 - - if npu_config is not None and attention_mask is not None: - attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - return x, attention_bias, attention_mask class Upsample3d(nn.Module): @@ -451,7 +461,11 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) - x = self.body(x) + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.body, x, x_dtype) x = x[:, :, :, :height*2-pad_h, :width*2-pad_w] x = rearrange(x, 'b d t h w -> b (t h w) d') @@ -460,10 +474,6 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): attention_mask = attention_mask[:, :, :, :height*2-pad_h, :width*2-pad_w] attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 - - if npu_config is not None and attention_mask is not None: - attention_mask = npu_config.get_attention_mask(attention_mask, attention_mask.shape[-1]) - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) return x, attention_bias, attention_mask @@ -506,15 +516,27 @@ def __init__(self, dim, ffn_expansion_factor, bias=True, rep=False, def forward(self, x): x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) - x = self.project_in(x) - x = F.gelu(x) - if self.rep: - out = x - for module in self.dwconv: - out = out + module(x) + if npu_config is None: + x = self.project_in(x) + x = F.gelu(x) + if self.rep: + out = x + for module in self.dwconv: + out = out + module(x) + else: + out = self.dwconv(x) + x = self.project_out(out) else: - out = self.dwconv(x) - x = self.project_out(out) + x_dtype = x.dtype + x = npu_config.run_conv3d(self.project_in, x, torch.float16) + x = F.gelu(x) + if self.rep: + out = x + for module in self.dwconv: + out = out + npu_config.run_conv3d(module, x, torch.float16) + else: + out = npu_config.run_conv3d(self.dwconv, x, torch.float16) + x = npu_config.run_conv3d(self.project_out, out, x_dtype) x = rearrange(x, 'b d t h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) return x diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 28cf771bb..c64753f20 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -37,12 +37,22 @@ def forward(self, input_ids, attention_mask): def get_text_enc(args): """deprecation""" - text_enc = text_encoder.get(args.text_encoder_name, None) + encoder_key = None + for key in text_encoder.keys(): + if key in args.text_encoder_name: + encoder_key = key + break + text_enc = text_encoder.get(encoder_key, None) assert text_enc is not None return text_enc(args) def get_text_warpper(text_encoder_name): """deprecation""" - text_enc = text_encoder.get(text_encoder_name, None) + encoder_key = None + for key in text_encoder.keys(): + if key in text_encoder_name: + encoder_key = key + break + text_enc = text_encoder.get(encoder_key, None) assert text_enc is not None return text_enc diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index a3d01ee1e..6f522839a 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -454,6 +454,9 @@ def load_model_hook(models, input_dir): initial_global_step = global_step first_epoch = global_step // num_update_steps_per_epoch + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size + else: initial_global_step = 0 @@ -564,8 +567,9 @@ def run(x, model_kwargs, prof): # Store the UNet parameters temporarily and load the EMA parameters to perform inference. ema_model.store(model.parameters()) ema_model.copy_to(model.parameters()) - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step, ema=True) + if npu_config is None: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) # Switch back to the original UNet parameters. ema_model.restore(model.parameters()) diff --git a/scripts/text_condition/sample_video_on_npu.sh b/scripts/text_condition/sample_video_on_npu.sh index a4e4dd6a3..a889f2087 100644 --- a/scripts/text_condition/sample_video_on_npu.sh +++ b/scripts/text_condition/sample_video_on_npu.sh @@ -3,12 +3,11 @@ WEIGHT_PATH="/home/opensora/shebin/pre_weights/" export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" export MASTER_PORT=12359 -torchrun --nproc_per_node=8 opensora/sample/sample_t2v_npu.py \ +torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ - --version ${NUM_FRAME}x512x512 \ --num_frames ${NUM_FRAME} \ - --height 512 \ - --width 512 \ + --height 240 \ + --width 320 \ --cache_dir "./cache_dir" \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ @@ -18,5 +17,4 @@ torchrun --nproc_per_node=8 opensora/sample/sample_t2v_npu.py \ --fps 24 \ --guidance_scale 4.0 \ --num_sampling_steps 50 \ - --enable_tiling \ - --model_3d + --enable_tiling diff --git a/scripts/text_condition/train_image_256x256_on_npu.sh b/scripts/text_condition/train_image_256x256_on_npu.sh new file mode 100644 index 000000000..91f54d692 --- /dev/null +++ b/scripts/text_condition/train_image_256x256_on_npu.sh @@ -0,0 +1,46 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +env +export WANDB_MODE='offline' + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-B/122 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=8 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 2.5 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_udit3d_61x240p_on_npu.sh b/scripts/text_condition/train_udit3d_61x240p_on_npu.sh new file mode 100644 index 000000000..bdfd49954 --- /dev/null +++ b/scripts/text_condition/train_udit3d_61x240p_on_npu.sh @@ -0,0 +1,46 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +env +export WANDB_MODE='offline' + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v.py \ + --model UDiTT2V-B/111 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=20 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 2.0 diff --git a/scripts/text_condition/train_video21d_65x240p_on_npu.sh b/scripts/text_condition/train_video21d_65x240p_on_npu.sh index f7c44206b..21df4ba89 100644 --- a/scripts/text_condition/train_video21d_65x240p_on_npu.sh +++ b/scripts/text_condition/train_video21d_65x240p_on_npu.sh @@ -8,7 +8,7 @@ accelerate launch \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v.py \ - --model LatteT2V-XL/122 \ + --model LatteT2V-S/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ @@ -34,11 +34,11 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=20 \ + --checkpointing_steps=500 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ - --use_image_num 0 \ + --use_image_num 2 \ --enable_tiling \ --snr_gamma 5.0 \ --use_ema \ diff --git a/scripts/train_data/image_data_on_npu.txt b/scripts/train_data/image_data_on_npu.txt index 382fd914c..7c5f1e470 100644 --- a/scripts/train_data/image_data_on_npu.txt +++ b/scripts/train_data/image_data_on_npu.txt @@ -1,4 +1,4 @@ /home/local_dataset/image_data_obs/AnyWord-3M/,/home/opensora/captions/linbin_captions/anytext_en_1886137.json -/home/local_dataset_n63/image_data_obs/mj_unzip_files,/home/opensora/captions/linbin_captions/tuzhan_mj_1712571.json +/home/local_dataset_n63/image_data_obs/mj_unzip_files,/home/opensora/captions/linbin_captions/tuzhan_mj_4615265.json /home/local_dataset_n63/image_data_obs/sa_unzip_files,/home/opensora/captions/linbin_captions/sam_image_11185255.json /home/local_dataset_n63/image_data_obs/images/,/home/opensora/captions/linbin_captions/human_images_162094.json \ No newline at end of file diff --git a/scripts/train_data/video_data_on_npu.txt b/scripts/train_data/video_data_on_npu.txt index 2f4e96f5a..73bc34e5f 100644 --- a/scripts/train_data/video_data_on_npu.txt +++ b/scripts/train_data/video_data_on_npu.txt @@ -1,3 +1 @@ -/home/local_dataset_n63/video_data_obs/pexel,/home/opensora/captions/linbin_captions/video_pexels_513f_271782.json -/home/local_dataset_n63/video_data_obs/pixabay_v2,/home/opensora/captions/linbin_captions/video_pixabay_513f_51483.json -/home/local_dataset_n63/video_data_obs/mixkit_resize1024/mixkit/,/home/opensora/captions/linbin_captions/video_mixkit_513f_1997.json \ No newline at end of file +20240426/sdg/panda70m,/home/video_data/panda70m \ No newline at end of file From f682a188ade4d1da9c63b887668744d9dc43faf6 Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Thu, 6 Jun 2024 15:31:59 +0800 Subject: [PATCH 024/134] support panda70m-dataset --- opensora/dataset/t2v_datasets.py | 110 ++++++++---------- opensora/npu_config.py | 4 +- opensora/train/train_t2v.py | 8 +- .../train_video21d_65x240p_on_npu.sh | 8 +- scripts/train_data/video_data_on_npu.txt | 2 +- 5 files changed, 60 insertions(+), 72 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index f3c3628f5..d4eaede67 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -1,7 +1,9 @@ +import time +import traceback + try: import torch_npu from opensora.npu_config import npu_config - from opensora.dataset.virtual_disk import VirtualDisk except: torch_npu = None npu_config = None @@ -23,6 +25,7 @@ from opensora.utils.dataset_utils import DecordInit from opensora.utils.utils import text_preprocessing + def filter_json_by_existed_files(directory, data, postfix=".mp4"): # 构建搜索模式,以匹配指定后缀的文件 pattern = os.path.join(directory, '**', f'*{postfix}') @@ -35,11 +38,14 @@ def filter_json_by_existed_files(directory, data, postfix=".mp4"): filtered_items = [item for item in data if item['path'] in mp4_files_set] return filtered_items + + def random_video_noise(t, c, h, w): vid = torch.rand(t, c, h, w) * 255.0 vid = vid.to(torch.uint8) return vid + class T2V_dataset(Dataset): def __init__(self, args, transform, temporal_sample, tokenizer): self.image_data = args.image_data @@ -59,19 +65,23 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.img_cap_list = self.get_img_cap_list() else: self.img_cap_list = self.get_img_cap_list() - + if npu_config is not None: - self.virtual_disk = VirtualDisk(f"/mnt/pandas_70m_{npu_config.get_local_rank()}") self.n_used_elements = 0 self.elements = list(range(self.__len__())) print(f"n_elements: {len(self.elements)}") + print(f"video length: {len(self.vid_cap_list)}") + print(f"image length: {len(self.img_cap_list)}") + + def set_checkpoint(self, n_used_elements): + self.n_used_elements = n_used_elements def __len__(self): if self.num_frames != 1: return len(self.vid_cap_list) else: return len(self.img_cap_list) - + def __getitem__(self, idx): if npu_config is not None: idx = self.elements[self.n_used_elements % len(self.elements)] @@ -90,6 +100,10 @@ def __getitem__(self, idx): return dict(video_data=video_data, image_data=image_data) except Exception as e: # print(f'Error with {e}') + # 打印异常堆栈 + print(f"Caught an exception! {self.vid_cap_list[idx]}") + traceback.print_exc() + traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): @@ -97,17 +111,17 @@ def get_video(self, idx): # # print('random shape', video.shape) # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - + video_path = self.vid_cap_list[idx]['path'] frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] - assert h / w <= 16 / 16 and h / w >= 9 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 9/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' + assert h / w <= 16 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 8/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' t = video.shape[0] video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W - + # video = torch.rand(509, 3, 240, 320) video = video.transpose(0, 1) # T C H W -> C T H W @@ -128,9 +142,9 @@ def get_video(self, idx): return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) def get_image_from_video(self, video_data): - select_image_idx = np.linspace(0, self.num_frames-1, self.use_image_num, dtype=int) + select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) assert self.num_frames >= self.use_image_num - image = [video_data['video'][:, i:i+1] for i in select_image_idx] # num_img [c, 1, h, w] + image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) @@ -138,10 +152,10 @@ def get_image_from_video(self, video_data): def get_image(self, idx): idx = idx % len(self.img_cap_list) # out of range image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - - image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] - image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] - image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] # image = [torch.rand(1, 3, 256, 256) for i in image_data] @@ -181,13 +195,9 @@ def tv_read(self, path, frame_idx=None): video = vframes[frame_indice] # (T, C, H, W) return video - + def decord_read(self, path, frame_idx=None): - if npu_config is not None: - path = self.virtual_disk.get_data(path) decord_vr = self.v_decoder(path) - if npu_config is not None: - self.virtual_disk.del_data(path) total_frames = len(decord_vr) # Sampling video frames if frame_idx is None: @@ -205,62 +215,40 @@ def decord_read(self, path, frame_idx=None): video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) return video_data - - def get_vid_cap_list(self): - vid_cap_lists = [] - with open(self.video_data, 'r') as f: - folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] - # print(folder_anno) - for folder, anno in folder_anno: - if npu_config is None: - with open(anno, 'r') as f: - vid_cap_list = json.load(f) - else: - # List all files in the directory - files = [f for f in os.listdir(anno) if os.path.isfile(os.path.join(anno, f))] - - # Read the contents of the first chunk - vid_cap_list = [] - for file in files[npu_config.rank::npu_config.world_size]: - print(os.path.join(anno, file)) - with open(os.path.join(anno, file), 'r') as f: - vid_cap_list.extend(json.load(f)) - - print(f'Building {anno}...') - for i in tqdm(range(len(vid_cap_list))): - path = opj(folder, vid_cap_list[i]['path']) - if os.path.exists(path.replace('.mp4', '_resize_1080p.mp4')): - path = path.replace('.mp4', '_resize_1080p.mp4') - vid_cap_list[i]['path'] = path - vid_cap_lists += vid_cap_list - return vid_cap_lists - - def read_images(self): - img_cap_lists = [] - with open(self.image_data, 'r') as f: + def read_jsons(self, data, postfix=".jpg"): + cap_lists = [] + with open(data, 'r') as f: folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] for folder, anno in folder_anno: with open(anno, 'r') as f: - img_cap_list = json.load(f) + sub_list = json.load(f) print(f'Building {anno}...') - for i in tqdm(range(len(img_cap_list))): - img_cap_list[i]['path'] = opj(folder, img_cap_list[i]['path']) + for i in tqdm(range(len(sub_list))): + sub_list[i]['path'] = opj(folder, sub_list[i]['path']) if npu_config is not None: - img_cap_list = filter_json_by_existed_files(folder, img_cap_list, postfix=".jpg") - img_cap_lists += img_cap_list - return img_cap_lists[:-1] # drop last to avoid error length + sub_list = filter_json_by_existed_files(folder, sub_list, postfix=postfix) + cap_lists += sub_list + return cap_lists[:-1] # drop last to avoid error length def get_img_cap_list(self): use_image_num = self.use_image_num if self.use_image_num != 0 else 1 if npu_config is None: - img_cap_lists = self.read_images() + img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] else: - img_cap_lists = npu_config.try_load_pickle("img_cap_lists", self.read_images) + img_cap_lists = npu_config.try_load_pickle("img_cap_lists", + lambda: self.read_jsons(self.image_data, postfix=".jpg")) img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] return img_cap_lists + def get_vid_cap_list(self): + if npu_config is None: + vid_cap_lists = self.read_jsons(self.video_data, postfix=".mp4") + else: + vid_cap_lists = npu_config.try_load_pickle("vid_cap_lists5", + lambda: self.read_jsons(self.video_data, postfix=".mp4")) + npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") + vid_cap_lists = vid_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] - - + return vid_cap_lists diff --git a/opensora/npu_config.py b/opensora/npu_config.py index ec39b998c..f83cfd71e 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -118,13 +118,13 @@ def get_output_video_path(self, name): return f"{self.work_path}/output_videos/{name}" def get_node_size(self): - return self.world_size / self.node_world_size + return self.world_size // self.node_world_size def get_local_rank(self): return self.rank % self.N_NPU_PER_NODE def get_pickle_path(self, file_name): - return f"{self.pickle_save_path}/{file_name}_local_n63" + return f"/home/opensora/yancen/Open-Sora-Plan-dev/pickles/{file_name}_local_n63" def free_mm(self): for key, value in self.mm.items(): diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 6f522839a..8693c815a 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -453,13 +453,13 @@ def load_model_hook(models, input_dir): initial_global_step = global_step first_epoch = global_step // num_update_steps_per_epoch - - if npu_config is not None: - train_dataset.n_used_elements = global_step * args.train_batch_size - else: initial_global_step = 0 + if npu_config is not None: + # train_dataset.n_used_elements = global_step * args.train_batch_size + train_dataset.set_checkpoint(global_step * args.train_batch_size) + progress_bar = tqdm( range(0, args.max_train_steps), initial=initial_global_step, diff --git a/scripts/text_condition/train_video21d_65x240p_on_npu.sh b/scripts/text_condition/train_video21d_65x240p_on_npu.sh index 21df4ba89..9e1545b37 100644 --- a/scripts/text_condition/train_video21d_65x240p_on_npu.sh +++ b/scripts/text_condition/train_video21d_65x240p_on_npu.sh @@ -29,16 +29,16 @@ accelerate launch \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ - --lr_scheduler="constant" \ + --learning_rate=4e-5 \ + --lr_scheduler="cosine" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=500 \ + --checkpointing_steps=250 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ - --use_image_num 2 \ + --use_image_num 0 \ --enable_tiling \ --snr_gamma 5.0 \ --use_ema \ diff --git a/scripts/train_data/video_data_on_npu.txt b/scripts/train_data/video_data_on_npu.txt index 73bc34e5f..b29eada06 100644 --- a/scripts/train_data/video_data_on_npu.txt +++ b/scripts/train_data/video_data_on_npu.txt @@ -1 +1 @@ -20240426/sdg/panda70m,/home/video_data/panda70m \ No newline at end of file +/home/local_dataset_n20/video_obs/panda70m/,/home/opensora/captions/linbin_captions/step2_merge_downloaded_videos_v2.json \ No newline at end of file From 0fa2c06c4ce50f6c7a3dc0850d060184ffa38f2f Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Fri, 7 Jun 2024 17:35:39 +0800 Subject: [PATCH 025/134] support panda70m-dataset --- opensora/dataset/t2v_datasets.py | 17 +++++++++++++++-- opensora/train/train_t2v.py | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index d4eaede67..50d92c459 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -63,13 +63,18 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.vid_cap_list = self.get_vid_cap_list() if self.use_image_num != 0 and not self.use_img_from_vid: self.img_cap_list = self.get_img_cap_list() + else: + self.img_cap_list = [] else: self.img_cap_list = self.get_img_cap_list() + self.vid_cap_list = [] if npu_config is not None: self.n_used_elements = 0 self.elements = list(range(self.__len__())) + random.shuffle(self.elements) print(f"n_elements: {len(self.elements)}") + print(f"video length: {len(self.vid_cap_list)}") print(f"image length: {len(self.img_cap_list)}") @@ -107,17 +112,19 @@ def __getitem__(self, idx): return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): + # npu_config.print_msg(f"current idx is {idx}") # video = random.choice([random_video_noise(65, 3, 336, 448), random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 480)]) # # print('random shape', video.shape) # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) video_path = self.vid_cap_list[idx]['path'] + assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] - assert h / w <= 16 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 8/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' + assert h / w <= 16 / 16 and h / w >= 4 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 4/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' t = video.shape[0] video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W @@ -248,7 +255,13 @@ def get_vid_cap_list(self): else: vid_cap_lists = npu_config.try_load_pickle("vid_cap_lists5", lambda: self.read_jsons(self.video_data, postfix=".mp4")) - npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") + # npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") vid_cap_lists = vid_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] + vid_cap_lists_final = [] + for item in vid_cap_lists: + if os.path.exists(item['path']) and os.path.getsize(item['path']) > 10240: + vid_cap_lists_final.append(item) + vid_cap_lists = vid_cap_lists_final + npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") return vid_cap_lists diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index 8693c815a..b8a80264b 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -151,7 +151,7 @@ def main(args): if torch_npu is not None and npu_config is not None: npu_config.print_msg(args) - # npu_config.seed_everything() + npu_config.seed_everything() accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) accelerator = Accelerator( From a728ba7ac11912fc2613ddfd082c67fc50b9f1ae Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Sun, 9 Jun 2024 04:51:57 +0000 Subject: [PATCH 026/134] add cfg/snr/noise offset --- opensora/dataset/__init__.py | 4 +- opensora/dataset/t2v_datasets.py | 7 +- .../diffusion/opensora/modeling_opensora.py | 99 +- opensora/models/diffusion/opensora/modules.py | 479 +++++++-- opensora/models/text_encoder/__init__.py | 3 +- opensora/sample/sample_t2v.py | 5 +- opensora/train/train_t2v.py | 7 +- opensora/train/train_t2v_edm.py | 923 +++++++++++++++++ opensora/train/train_t2v_new.py | 958 ++++++++++++++++++ scripts/accelerate_configs/hostfile | 4 +- scripts/accelerate_configs/hostfile1 | 6 +- scripts/accelerate_configs/hostfile2 | 2 + .../multi_node_example.yaml | 4 +- .../multi_node_example1.yaml | 8 +- .../multi_node_example2.yaml | 18 + .../text_condition/new_train_image_256x256.sh | 54 + .../text_condition/train_ds_image_256x256.sh | 55 + .../train_ds_video3d_65x240p.sh | 55 + scripts/text_condition/train_image_256x256.sh | 19 +- .../text_condition/train_image_256x256_edm.sh | 53 + .../text_condition/train_video3d_65x240p.sh | 5 +- scripts/train_data/image_data.txt | 7 +- scripts/train_data/image_data_debug.txt | 2 +- scripts/train_data/video_data_513.txt | 4 +- scripts/train_data/video_data_debug.txt | 2 +- 25 files changed, 2650 insertions(+), 133 deletions(-) create mode 100644 opensora/train/train_t2v_edm.py create mode 100644 opensora/train/train_t2v_new.py create mode 100644 scripts/accelerate_configs/hostfile2 create mode 100644 scripts/accelerate_configs/multi_node_example2.yaml create mode 100644 scripts/text_condition/new_train_image_256x256.sh create mode 100644 scripts/text_condition/train_ds_image_256x256.sh create mode 100644 scripts/text_condition/train_ds_video3d_65x240p.sh create mode 100644 scripts/text_condition/train_image_256x256_edm.sh diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 1c6310e51..d50f16674 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -61,7 +61,7 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - # tokenizer = AutoTokenizer.from_pretrained("/storage/dev3d/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 50d92c459..8d3c34ca3 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -57,6 +57,7 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.temporal_sample = temporal_sample self.tokenizer = tokenizer self.model_max_length = args.model_max_length + self.cfg = args.cfg self.v_decoder = DecordInit() if self.num_frames != 1: @@ -124,17 +125,17 @@ def get_video(self, idx): video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] - assert h / w <= 16 / 16 and h / w >= 4 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 4/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' + assert h / w <= 16 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 8/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' t = video.shape[0] video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W - + # video = torch.rand(509, 3, 240, 320) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] - text = text_preprocessing(text) + text = text_preprocessing(text) if random.random() > self.cfg else "" text_tokens_and_mask = self.tokenizer( text, max_length=self.model_max_length, diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index a91d1c562..2153d942e 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -11,7 +11,7 @@ from diffusers.models.modeling_utils import ModelMixin from diffusers.models.normalization import AdaLayerNormSingle from diffusers.models.embeddings import PixArtAlphaTextProjection -from opensora.models.diffusion.opensora.modules import PatchEmbed3D, PatchEmbed2D, BasicTransformerBlock +from opensora.models.diffusion.opensora.modules import OverlapPatchEmbed3D, OverlapPatchEmbed2D, PatchEmbed2D, BasicTransformerBlock from opensora.utils.utils import to_2tuple try: import torch_npu @@ -82,6 +82,7 @@ def __init__( interpolation_scale_t: float = None, use_additional_conditions: Optional[bool] = None, attention_mode: str = 'xformers', + downsampler: str = None, ): super().__init__() @@ -101,6 +102,7 @@ def __init__( self.interpolation_scale_t = interpolation_scale_t self.interpolation_scale_h = interpolation_scale_h self.interpolation_scale_w = interpolation_scale_w + self.downsampler = downsampler self.caption_channels = caption_channels self.num_attention_heads = num_attention_heads self.attention_head_dim = attention_head_dim @@ -167,17 +169,43 @@ def _init_patched_inputs(self, norm_type): # interpolation_scale_t=interpolation_scale_t, # ) # else: - self.pos_embed = PatchEmbed2D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - patch_size_t=self.config.patch_size_t, - patch_size=self.config.patch_size, - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - ) + if self.config.downsampler is not None and len(self.config.downsampler) == 9 and self.config.patch_size == 1 and self.config.patch_size == 1: + self.pos_embed = OverlapPatchEmbed3D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + elif self.config.downsampler is not None and len(self.config.downsampler) == 7 and self.config.patch_size == 1 and self.config.patch_size == 1: + self.pos_embed = OverlapPatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + + else: + self.pos_embed = PatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) self.transformer_blocks = nn.ModuleList( [ @@ -198,6 +226,7 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + downsampler=self.config.downsampler, ) for _ in range(self.config.num_layers) ] @@ -358,14 +387,14 @@ def forward( # 1. Input + frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, \ timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img = self._operate_on_patched_inputs( hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num ) - - frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy # 2. Blocks # import ipdb;ipdb.set_trace() for block in self.transformer_blocks: @@ -392,6 +421,9 @@ def custom_forward(*inputs): timestep_vid, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) # import ipdb;ipdb.set_trace() @@ -405,6 +437,9 @@ def custom_forward(*inputs): timestep_img, cross_attention_kwargs, class_labels, + 1, + height, + width, **ckpt_kwargs, ) else: @@ -417,6 +452,9 @@ def custom_forward(*inputs): timestep=timestep_vid, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) if hidden_states_img is not None: hidden_states_img = block( @@ -427,6 +465,9 @@ def custom_forward(*inputs): timestep=timestep_img, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=1, + height=height, + width=width, ) # 3. Output @@ -540,14 +581,25 @@ def _get_output_for_patched_inputs( # output = output[:, :, 1:] return output +def OpenSoraT2V_S_111(**kwargs): + return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + +def OpenSoraT2V_B_111(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + +def OpenSoraT2V_L_111(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) def OpenSoraT2V_S_122(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_122(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_L_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, @@ -578,6 +630,9 @@ def OpenSoraT2V_L_122(**kwargs): "OpenSoraT2V-S/122": OpenSoraT2V_S_122, "OpenSoraT2V-B/122": OpenSoraT2V_B_122, "OpenSoraT2V-L/122": OpenSoraT2V_L_122, + "OpenSoraT2V-S/111": OpenSoraT2V_S_111, + "OpenSoraT2V-B/111": OpenSoraT2V_B_111, + "OpenSoraT2V-L/111": OpenSoraT2V_L_111, # "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, # "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, # "OpenSoraT2V-S/222": OpenSoraT2V_S_222, @@ -591,8 +646,9 @@ def OpenSoraT2V_L_122(**kwargs): "OpenSoraT2V-S/122": OpenSoraT2V, "OpenSoraT2V-B/122": OpenSoraT2V, "OpenSoraT2V-L/122": OpenSoraT2V, - # "OpenSoraT2V-S/222": OpenSoraT2V, - # "OpenSoraT2V-B/222": OpenSoraT2V, + "OpenSoraT2V-S/111": OpenSoraT2V, + "OpenSoraT2V-B/111": OpenSoraT2V, + "OpenSoraT2V-L/111": OpenSoraT2V, } if __name__ == '__main__': @@ -625,7 +681,7 @@ def OpenSoraT2V_L_122(**kwargs): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:0') - model = OpenSoraT2V_L_122(in_channels=4, + model = OpenSoraT2V_B_111(in_channels=4, out_channels=8, sample_size=latent_size, sample_size_t=num_frames, @@ -640,7 +696,8 @@ def OpenSoraT2V_L_122(**kwargs): only_cross_attention=False, upcast_attention=False, use_linear_projection=False, - use_additional_conditions=False).to(device) + use_additional_conditions=False, + downsampler=None).to(device) try: path = "PixArt-Alpha-XL-2-512.safetensors" @@ -666,7 +723,7 @@ def OpenSoraT2V_L_122(**kwargs): encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) with torch.no_grad(): output = model(**model_kwargs) - # print(output) + print(output[0].shape) diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 736bd0eb5..fc8917e68 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -7,7 +7,7 @@ from typing import Any, Dict, Optional, Tuple from diffusers.utils.torch_utils import maybe_allow_in_graph from typing import Any, Dict, Optional - +import re import torch import torch.nn.functional as F from torch import nn @@ -236,9 +236,9 @@ def forward(self, latent, num_frames): video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - temp_pos_embed = temp_pos_embed.unsqueeze(2) - video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + # temp_pos_embed = temp_pos_embed.unsqueeze(2) + # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None @@ -250,15 +250,16 @@ def forward(self, latent, num_frames): return video_latent, image_latent -class PatchEmbed3D(nn.Module): - """3D Video and 2D Image to Patch Embedding""" + +class OverlapPatchEmbed3D(nn.Module): + """2D Image to Patch Embedding but with 3D position embedding""" def __init__( self, - num_frames=17, + num_frames=1, height=224, width=224, - patch_size_t=2, + patch_size_t=1, patch_size=16, in_channels=3, embed_dim=768, @@ -269,12 +270,12 @@ def __init__( interpolation_scale_t=1, ): super().__init__() - assert num_frames > 1 + assert patch_size_t == 1 and patch_size == 1 self.flatten = flatten self.layer_norm = layer_norm self.proj = nn.Conv3d( - in_channels, embed_dim, kernel_size=(patch_size_t, patch_size, patch_size), stride=(patch_size_t, patch_size, patch_size), bias=bias + in_channels, embed_dim, kernel_size=3, padding=1, stride=1, bias=bias ) if layer_norm: self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) @@ -287,96 +288,302 @@ def __init__( # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 self.height, self.width = height // patch_size, width // patch_size - if num_frames % 2 == 1: - self.num_frames = (num_frames - 1) // patch_size_t + 1 - self.base_size = ((num_frames - 1) // patch_size_t + 1, height // patch_size, width // patch_size) - else: - self.num_frames = num_frames // patch_size_t - self.base_size = (num_frames // patch_size_t, height // patch_size, width // patch_size) - self.interpolation_scale = (interpolation_scale_t, interpolation_scale[0], interpolation_scale[1]) - pos_embed_video = get_3d_sincos_pos_embed( - embed_dim, (self.num_frames, self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + self.base_size = (height // patch_size, width // patch_size) + self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + pos_embed = get_2d_sincos_pos_embed( + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale ) - self.register_buffer("pos_embed_video", torch.from_numpy(pos_embed_video).float().unsqueeze(0), persistent=False) + self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) - pos_embed_image = get_3d_sincos_pos_embed( - embed_dim, (1, self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale - ) - self.register_buffer("pos_embed_image", torch.from_numpy(pos_embed_image).float().unsqueeze(0), persistent=False) - + self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.interpolation_scale_t = interpolation_scale_t + temp_pos_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) + # self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) def forward(self, latent, num_frames): - # b c v+i h w - # import ipdb;ipdb.set_trace() + b, _, _, _, _ = latent.shape + video_latent, image_latent = None, None + # b c 1 h w + # assert latent.shape[-3] == 1 and num_frames == 1 height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size - # bcvhw, bcihw - video_latent, image_latent = latent[:, :, :num_frames], latent[:, :, num_frames:] - if num_frames % 2 == 1: - num_frames = (num_frames - 1) // self.patch_size_t + 1 + # latent = rearrange(latent, 'b c t h w -> (b t) c h w') + latent = self.proj(latent) + + if self.flatten: + # latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C + latent = rearrange(latent, 'b c t h w -> (b t) (h w) c ') + if self.layer_norm: + latent = self.norm(latent) + + # import ipdb;ipdb.set_trace() + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed + + + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) else: - num_frames = num_frames // self.patch_size_t + temp_pos_embed = self.temp_pos_embed + + latent = (latent + pos_embed).to(latent.dtype) - first_frame_repeat = video_latent[:, :, :1].repeat(1, 1, self.patch_size_t-1, 1, 1) - video_latent = torch.cat([first_frame_repeat, video_latent], dim=2) - video_latent = self.proj(video_latent) + latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] + + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + # temp_pos_embed = temp_pos_embed.unsqueeze(2) + # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + + + video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None + image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None + + if num_frames == 1 and image_latent is None: + image_latent = video_latent + video_latent = None + return video_latent, image_latent + + + +class OverlapPatchEmbed2D(nn.Module): + """2D Image to Patch Embedding but with 3D position embedding""" + + def __init__( + self, + num_frames=1, + height=224, + width=224, + patch_size_t=1, + patch_size=16, + in_channels=3, + embed_dim=768, + layer_norm=False, + flatten=True, + bias=True, + interpolation_scale=(1, 1), + interpolation_scale_t=1, + ): + super().__init__() + assert patch_size_t == 1 and patch_size == 1 + self.flatten = flatten + self.layer_norm = layer_norm - if image_latent.numel() > 0: - image_latent = image_latent.repeat_interleave(self.patch_size_t, dim=2) # b c i h w -> b c 2i h w - image_latent = self.proj(image_latent) # b c 2i h w -> b c i h w + self.proj = nn.Conv2d( + in_channels, embed_dim, kernel_size=3, padding=1, stride=1, bias=bias + ) + if layer_norm: + self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) + else: + self.norm = None + + self.patch_size_t = patch_size_t + self.patch_size = patch_size + # See: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 + + self.height, self.width = height // patch_size, width // patch_size + self.base_size = (height // patch_size, width // patch_size) + self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + pos_embed = get_2d_sincos_pos_embed( + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) + + self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.interpolation_scale_t = interpolation_scale_t + temp_pos_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) + # self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) + + def forward(self, latent, num_frames): + b, _, _, _, _ = latent.shape + video_latent, image_latent = None, None + # b c 1 h w + # assert latent.shape[-3] == 1 and num_frames == 1 + height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size + latent = rearrange(latent, 'b c t h w -> (b t) c h w') + latent = self.proj(latent) if self.flatten: - video_latent = rearrange(video_latent, 'b c t h w -> b (t h w) c') - if image_latent.numel() > 0: - image_latent = rearrange(image_latent, 'b c i h w -> (b i) (h w) c') + latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C if self.layer_norm: - video_latent = self.norm(video_latent) - if image_latent.numel() > 0: - image_latent = self.norm(image_latent) + latent = self.norm(latent) + # import ipdb;ipdb.set_trace() # Interpolate positional embeddings if needed. # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.num_frames != num_frames or self.height != height or self.width != width: - pos_embed_video = get_3d_sincos_pos_embed( - embed_dim=self.pos_embed_video.shape[-1], - grid_size=(num_frames, height, width), + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), base_size=self.base_size, interpolation_scale=self.interpolation_scale, ) - pos_embed_video = torch.from_numpy(pos_embed_video) - pos_embed_video = pos_embed_video.float().unsqueeze(0).to(latent.device) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) else: - pos_embed_video = self.pos_embed_video - video_latent = (video_latent + pos_embed_video).to(latent.dtype) - - if image_latent.numel() > 0: - if self.height != height or self.width != width: - pos_embed_image = get_3d_sincos_pos_embed( - embed_dim=self.pos_embed_image.shape[-1], - grid_size=(1, height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed_image = torch.from_numpy(pos_embed_image) - pos_embed_image = pos_embed_image.float().unsqueeze(0).to(latent.device) - else: - pos_embed_image = self.pos_embed_image - image_latent = (image_latent + pos_embed_image).to(latent.dtype) + pos_embed = self.pos_embed + + + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) else: - image_latent = None + temp_pos_embed = self.temp_pos_embed + + latent = (latent + pos_embed).to(latent.dtype) + + latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] + + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + # temp_pos_embed = temp_pos_embed.unsqueeze(2) + # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + + + video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None + image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None + + if num_frames == 1 and image_latent is None: + image_latent = video_latent + video_latent = None return video_latent, image_latent - class Attention(Attention_): - def __init__(self, attention_mode, **kwags): - processor = AttnProcessor2_0(attention_mode) + def __init__(self, downsampler, attention_mode, **kwags): + processor = AttnProcessor2_0(attention_mode=attention_mode) super().__init__(processor=processor, **kwags) + self.downsampler = None + if downsampler: # downsampler k155_s122 + downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 + down_factor = list(re.search(r's(\d{2,3})', downsampler).group(1)) + downsampler_ker_size = [int(i) for i in downsampler_ker_size] + downsampler_padding = [(i - 1) // 2 for i in downsampler_ker_size] + down_factor = [int(i) for i in down_factor] + + if len(downsampler_ker_size) == 2: + self.downsampler = DownSampler2d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + down_shortcut=True) + elif len(downsampler_ker_size) == 3: + self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + down_shortcut=True) + + + + +class DownSampler3d(nn.Module): + def __init__(self, *args, **kwargs): + ''' Required kwargs: down_factor, downsampler''' + super().__init__() + self.down_factor = kwargs.pop('down_factor') + self.down_shortcut = kwargs.pop('down_shortcut') + self.layer = nn.Conv3d(*args, **kwargs) + + def forward(self, x, attention_mask, t, h, w): + # import ipdb;ipdb.set_trace() + b = x.shape[0] + x = rearrange(x, 'b (t h w) d -> b d t h w', t=t, h=h, w=w) + if npu_config is None: + x = self.layer(x) + (x if self.down_shortcut else 0) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) (t h w) d', + t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=t, h=h, w=w) + attention_mask = rearrange(attention_mask, 'b 1 (t dt) (h dh) (w dw) -> (b dt dh dw) 1 (t h w)', + t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + return x, attention_mask + def reverse(self, x, t, h, w): + # import ipdb;ipdb.set_trace() + x = rearrange(x, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', + t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + return x + + +class DownSampler2d(nn.Module): + def __init__(self, *args, **kwargs): + ''' Required kwargs: down_factor, downsampler''' + super().__init__() + self.down_factor = kwargs.pop('down_factor') + self.down_shortcut = kwargs.pop('down_shortcut') + self.layer = nn.Conv2d(*args, **kwargs) + def forward(self, x, attention_mask, t, h, w): + # import ipdb;ipdb.set_trace() + b = x.shape[0] + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) + if npu_config is None: + x = self.layer(x) + (x if self.down_shortcut else 0) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', + h=h//self.down_factor[0], w=w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=h, w=w) + attention_mask = rearrange(attention_mask, 'b 1 (h dh) (w dw) -> (b dh dw) 1 (h w)', + h=h//self.down_factor[0], w=w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + return x, attention_mask + def reverse(self, x, t, h, w): + # import ipdb;ipdb.set_trace() + x = rearrange(x, '(b t dh dw) (h w) d -> b (t h dh w dw) d', + t=t, h=h//self.down_factor[0], w=w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + return x + class AttnProcessor2_0: r""" Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). """ - def __init__(self, attention_mode): + def __init__(self, attention_mode='xformers'): + self.attention_mode = attention_mode if not hasattr(F, "scaled_dot_product_attention"): raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") @@ -388,6 +595,9 @@ def __call__( encoder_hidden_states: Optional[torch.FloatTensor] = None, attention_mask: Optional[torch.FloatTensor] = None, temb: Optional[torch.FloatTensor] = None, + frame: int = 8, + height: int = 16, + width: int = 16, *args, **kwargs, ) -> torch.FloatTensor: @@ -395,7 +605,12 @@ def __call__( deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." deprecate("scale", "1.0.0", deprecation_message) + + if attn.downsampler is not None: + hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask, t=frame, h=height, w=width) + residual = hidden_states + if attn.spatial_norm is not None: hidden_states = attn.spatial_norm(hidden_states, temb) @@ -491,11 +706,97 @@ def __call__( hidden_states = hidden_states / attn.rescale_output_factor + if attn.downsampler is not None: + hidden_states = attn.downsampler.reverse(hidden_states, t=frame, h=height, w=width) return hidden_states +class FeedForward_Conv3d(nn.Module): + def __init__(self, downsampler, dim, hidden_features, bias=True): + super(FeedForward_Conv3d, self).__init__() + + self.bias = bias + + self.project_in = nn.Linear(dim, hidden_features, bias=bias) + + self.dwconv = nn.ModuleList([ + nn.Conv3d(hidden_features, hidden_features, kernel_size=(5, 5, 5), stride=1, padding=(2, 2, 2), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv3d(hidden_features, hidden_features, kernel_size=(3, 3, 3), stride=1, padding=(1, 1, 1), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv3d(hidden_features, hidden_features, kernel_size=(1, 1, 1), stride=1, padding=(0, 0, 0), dilation=1, + groups=hidden_features, bias=bias) + ]) + + self.project_out = nn.Linear(hidden_features, dim, bias=bias) + + + def forward(self, x, t, h, w): + # import ipdb;ipdb.set_trace() + if npu_config is None: + x = self.project_in(x) + x = rearrange(x, 'b (t h w) d -> b d t h w', t=t, h=h, w=w) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + module(x) + out = rearrange(out, 'b d t h w -> b (t h w) d', t=t, h=h, w=w) + x = self.project_out(out) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.project_in, x, torch.float16) + x = rearrange(x, 'b (t h w) d -> b d t h w', t=t, h=h, w=w) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + npu_config.run_conv3d(module, x, torch.float16) + out = rearrange(out, 'b d t h w -> b (t h w) d', t=t, h=h, w=w) + x = npu_config.run_conv3d(self.project_out, out, x_dtype) + return x + + +class FeedForward_Conv2d(nn.Module): + def __init__(self, downsampler, dim, hidden_features, bias=True): + super(FeedForward_Conv2d, self).__init__() + + self.bias = bias + + self.project_in = nn.Linear(dim, hidden_features, bias=bias) + self.dwconv = nn.ModuleList([ + nn.Conv2d(hidden_features, hidden_features, kernel_size=(5, 5), stride=1, padding=(2, 2), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv2d(hidden_features, hidden_features, kernel_size=(3, 3), stride=1, padding=(1, 1), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv2d(hidden_features, hidden_features, kernel_size=(1, 1), stride=1, padding=(0, 0), dilation=1, + groups=hidden_features, bias=bias) + ]) + + self.project_out = nn.Linear(hidden_features, dim, bias=bias) + + + def forward(self, x, t, h, w): + # import ipdb;ipdb.set_trace() + if npu_config is None: + x = self.project_in(x) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + module(x) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) + x = self.project_out(out) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.project_in, x, torch.float16) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + npu_config.run_conv2d(module, x, torch.float16) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) + x = npu_config.run_conv2d(self.project_out, out, x_dtype) + return x @maybe_allow_in_graph class BasicTransformerBlock(nn.Module): @@ -559,9 +860,11 @@ def __init__( ff_bias: bool = True, attention_out_bias: bool = True, attention_mode: str = "xformers", + downsampler: bool = False, ): super().__init__() self.only_cross_attention = only_cross_attention + self.downsampler = downsampler # We keep these boolean flags for backward-compatibility. self.use_ada_layer_norm_zero = (num_embeds_ada_norm is not None) and norm_type == "ada_norm_zero" @@ -617,6 +920,7 @@ def __init__( upcast_attention=upcast_attention, out_bias=attention_out_bias, attention_mode=attention_mode, + downsampler=downsampler, ) # 2. Cross-Attn @@ -648,6 +952,7 @@ def __init__( upcast_attention=upcast_attention, out_bias=attention_out_bias, attention_mode=attention_mode, + downsampler=False ) # is self-attn if encoder_hidden_states is none else: self.norm2 = None @@ -669,6 +974,23 @@ def __init__( elif norm_type == "layer_norm_i2vgen": self.norm3 = None + # if downsampler: + # downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 + # if len(downsampler_ker_size) == 3: + # self.ff = FeedForward_Conv3d( + # downsampler, + # dim, + # 2 * dim, + # bias=ff_bias, + # ) + # elif len(downsampler_ker_size) == 2: + # self.ff = FeedForward_Conv2d( + # downsampler, + # dim, + # 2 * dim, + # bias=ff_bias, + # ) + # else: self.ff = FeedForward( dim, dropout=dropout, @@ -704,6 +1026,9 @@ def forward( timestep: Optional[torch.LongTensor] = None, cross_attention_kwargs: Dict[str, Any] = None, class_labels: Optional[torch.LongTensor] = None, + frame: int = None, + height: int = None, + width: int = None, added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None, ) -> torch.FloatTensor: if cross_attention_kwargs is not None: @@ -746,7 +1071,7 @@ def forward( attn_output = self.attn1( norm_hidden_states, encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, - attention_mask=attention_mask, + attention_mask=attention_mask, frame=frame, height=height, width=width, **cross_attention_kwargs, ) if self.norm_type == "ada_norm_zero": @@ -802,9 +1127,13 @@ def forward( norm_hidden_states = self.norm2(hidden_states) norm_hidden_states = norm_hidden_states * (1 + scale_mlp) + shift_mlp - if self._chunk_size is not None: - # "feed_forward_chunk_size" can be used to save memory - ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) + # if self._chunk_size is not None: + # # "feed_forward_chunk_size" can be used to save memory + # ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) + # else: + + if self.downsampler: + ff_output = self.ff(norm_hidden_states, t=frame, h=height, w=width) else: ff_output = self.ff(norm_hidden_states) diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index c64753f20..6cb136ef7 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -9,7 +9,8 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 0e61d97ce..d6a7837c2 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -81,7 +81,10 @@ def main(args): scheduler = DEISMultistepScheduler() elif args.sample_method == 'KDPM2AncestralDiscrete': ######### scheduler = KDPM2AncestralDiscreteScheduler() - + elif args.sample_method == 'EulerDiscreteSVD': + scheduler = EulerDiscreteScheduler.from_pretrained("stabilityai/stable-video-diffusion-img2vid", + subfolder="scheduler", cache_dir=args.cache_dir) + pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py index b8a80264b..7d7820ae5 100644 --- a/opensora/train/train_t2v.py +++ b/opensora/train/train_t2v.py @@ -259,12 +259,13 @@ def main(args): interpolation_scale_h=args.interpolation_scale_h, interpolation_scale_w=args.interpolation_scale_w, interpolation_scale_t=args.interpolation_scale_t, + downsampler=args.downsampler, # compress_kv_factor=args.compress_kv_factor, # use_rope=args.use_rope, # model_max_length=args.model_max_length, ) model.gradient_checkpointing = args.gradient_checkpointing - + # # use pretrained model? if args.pretrained: if 'safetensors' in args.pretrained: # pixart series @@ -420,12 +421,14 @@ def load_model_hook(models, input_dir): total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps logger.info("***** Running training *****") + logger.info(f" Model = {model}") logger.info(f" Num examples = {len(train_dataset)}") logger.info(f" Num Epochs = {args.num_train_epochs}") logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") global_step = 0 first_epoch = 0 @@ -682,7 +685,9 @@ def train_all_epoch(prof_=None): parser.add_argument('--interpolation_scale_h', type=float, default=1.0) parser.add_argument('--interpolation_scale_w', type=float, default=1.0) parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument('--cfg', type=float, default=0.1) parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--downsampler", type=str, default=None) parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") parser.add_argument("--pretrained", type=str, default=None) diff --git a/opensora/train/train_t2v_edm.py b/opensora/train/train_t2v_edm.py new file mode 100644 index 000000000..86e3b32c5 --- /dev/null +++ b/opensora/train/train_t2v_edm.py @@ -0,0 +1,923 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +import logging +import math +import os +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + pass +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import ProjectConfiguration, set_seed +from huggingface_hub import create_repo +from packaging import version +from tqdm.auto import tqdm +from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler, EulerDiscreteScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.utils.diffusion import create_diffusion_T as create_diffusion +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.utils.dataset_utils import Collate +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.sample.pipeline_opensora import OpenSoraPipeline + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + + +@torch.inference_mode() +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): + validation_prompt = [ + "a cat wearing sunglasses and working as a lifeguard at pool.", + "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." + ] + logger.info(f"Running validation....\n") + model = accelerator.unwrap_model(model) + # scheduler = PNDMScheduler() + # scheduler = DPMSolverMultistepScheduler() + scheduler = EulerDiscreteScheduler.from_pretrained("stabilityai/stable-video-diffusion-img2vid", + subfolder="scheduler", cache_dir=args.cache_dir) + opensora_pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model).to(device=accelerator.device) + videos = [] + for prompt in validation_prompt: + logger.info('Processing the ({}) prompt'.format(prompt)) + video = opensora_pipeline(prompt, + num_frames=args.num_frames, + # num_frames=1, + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=50, + ).images + videos.append(video[0]) + # import ipdb;ipdb.set_trace() + gc.collect() + torch.cuda.empty_cache() + videos = torch.stack(videos).numpy() + videos = rearrange(videos, 'b t h w c -> b t c h w') + for tracker in accelerator.trackers: + if tracker.name == "tensorboard": + if videos.shape[1] == 1: + assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + np_images = np.stack([np.asarray(img) for img in images]) + tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") + else: + np_videos = np.stack([np.asarray(vid) for vid in videos]) + tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) + if tracker.name == "wandb": + import wandb + if videos.shape[1] == 1: + # assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + # import ipdb;ipdb.set_trace() + logs = { + f"{'ema_' if ema else ''}validation": [ + wandb.Image(image, caption=f"{i}: {prompt}") + for i, (image, prompt) in enumerate(zip(images, validation_prompt)) + ] + } + else: + logs = { + f"{'ema_' if ema else ''}validation": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=30) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ] + } + # import ipdb;ipdb.set_trace() + if hasattr(model.pos_embed, 'temp_embed_gate'): + logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + tracker.log(logs, step=global_step) + + del opensora_pipeline + gc.collect() + torch.cuda.empty_cache() + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + logging_dir = Path(args.output_dir, args.logging_dir) + + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) + # npu_config.seed_everything() + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # if args.push_to_hub: + # repo_id = create_repo( + # repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token + # ).repo_id + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + diffusion = create_diffusion(timestep_respacing="") # default: 1000 steps, linear noise schedule + kwargs = {} + ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae], + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + interpolation_scale_t=args.interpolation_scale_t, + downsampler=args.downsampler, + # compress_kv_factor=args.compress_kv_factor, + # use_rope=args.use_rope, + # model_max_length=args.model_max_length, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # # use pretrained model? + if args.pretrained: + if 'safetensors' in args.pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + checkpoint = safe_load(args.pretrained, device="cpu") + if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + repeat = model.pos_embed.proj.weight.shape[2] + checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] + model_state_dict = model.state_dict() + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # Freeze vae and text encoders. + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + # Set model as trainable. + model.train() + + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + # ae.to(accelerator.device, dtype=torch.float32) + ae.vae.to(accelerator.device, dtype=weight_dtype) + # ae.to(accelerator.device) + text_enc.to(accelerator.device, dtype=weight_dtype) + # text_enc.to(accelerator.device) + + # Create EMA for the unet. + if args.use_ema: + ema_model = deepcopy(model) + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) + ema_model.load_state_dict(load_model.state_dict()) + ema_model.to(accelerator.device) + del load_model + + for i in range(len(models)): + # pop models so that they are not loaded again + model = models.pop() + + # load diffusers style into model + load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + # Use 8-bit Adam for lower memory usage or to fine-tune the model in 16GB GPUs + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + # Optimizer creation + params_to_optimize = model.parameters() + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + # Setup data: + train_dataset = getdataset(args) + train_dataloader = torch.utils.data.DataLoader( + train_dataset, + shuffle=True, + pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + model, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_model.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + if accelerator.is_main_process: + accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {model}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_model.step(model.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + one_step_duration = end_time - start_time + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg( + f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) + progress_info.train_loss = 0.0 + + if args.use_deepspeed or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(latents, model_kwargs, prof, P_std=0.6, P_mean=1.7): + global start_time + start_time = time.time() + + # Add noise to the latents according to the noise magnitude at each timestep + # (this is the forward diffusion process) #[bsz, f, c, h , w] + bsz = latents.shape[0] + rnd_normal = torch.randn([bsz, 1, 1, 1, 1], device=latents.device) + sigma = (rnd_normal * P_std + P_mean).exp() + c_skip = 1 / (sigma**2 + 1) + c_out = -sigma / (sigma**2 + 1) ** 0.5 + c_in = 1 / (sigma**2 + 1) ** 0.5 + c_noise = (sigma.log() / 4).reshape([bsz]) + loss_weight = (sigma ** 2 + 1) / sigma ** 2 + + noisy_latents = c_in * (latents + torch.randn_like(latents) * sigma) + + model_pred = model( + noisy_latents, + c_noise, + **model_kwargs + )[0] + + predict_x0 = c_out * model_pred + c_skip * noisy_latents + loss = ((predict_x0 - latents)**2 * loss_weight).mean() + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = model.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + if prof is not None: + prof.step() + + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + for tracker in accelerator.trackers: + if tracker.name == "wandb": + if progress_info.global_step % args.checkpointing_steps != 0: + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + + if progress_info.global_step % args.checkpointing_steps == 0: + + if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step) + + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + if npu_config is None: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + + return loss + def train_one_step(step_, data_item_, prof_=None): + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 + attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W + input_ids = input_ids.to(accelerator.device) # B 1+num_images L + cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1+num_images L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D + cond = cond.reshape(B, N, L, -1) + + # Map input images to latent space + normalize latents + if args.use_image_num == 0: + x = ae.encode(x) # B C T H W + else: + videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] + videos = ae.encode(videos) # B C T H W + images = rearrange(images, 'b c t h w -> (b t) c 1 h w') + images = ae.encode(images) + images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + + with accelerator.accumulate(model): + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + + if train_one_step(step, data_item, prof_): + break + + if step >= 2 and torch_npu is not None and npu_config is not None: + npu_config.free_mm() + + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/shebin/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--model_max_length", type=int, default=512) + + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--downsampler", type=str, default=None) + + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--use_deepspeed", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument( + "--output_dir", + type=str, + default=None, + help="The output directory where the model predictions and checkpoints will be written.", + ) + parser.add_argument( + "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader." + ) + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument( + "--max_train_steps", + type=int, + default=None, + help="Total number of training steps to perform. If provided, overrides num_train_epochs.", + ) + parser.add_argument( + "--checkpointing_steps", + type=int, + default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument( + "--checkpoints_total_limit", + type=int, + default=None, + help=("Max number of checkpoints to store."), + ) + parser.add_argument( + "--resume_from_checkpoint", + type=str, + default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument( + "--gradient_accumulation_steps", + type=int, + default=1, + help="Number of updates steps to accumulate before performing a backward/update pass.", + ) + parser.add_argument( + "--gradient_checkpointing", + action="store_true", + help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", + ) + parser.add_argument( + "--learning_rate", + type=float, + default=1e-4, + help="Initial learning rate (after the potential warmup period) to use.", + ) + parser.add_argument( + "--scale_lr", + action="store_true", + default=False, + help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.", + ) + parser.add_argument( + "--lr_scheduler", + type=str, + default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument( + "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." + ) + parser.add_argument( + "--timestep_bias_strategy", + type=str, + default="none", + choices=["earlier", "later", "range", "none"], + help=( + "The timestep bias strategy, which may help direct the model toward learning low or high frequency details." + " Choices: ['earlier', 'later', 'range', 'none']." + " The default is 'none', which means no bias is applied, and training proceeds normally." + " The value of 'later' will increase the frequency of the model's final training timesteps." + ), + ) + parser.add_argument( + "--timestep_bias_multiplier", + type=float, + default=1.0, + help=( + "The multiplier for the bias. Defaults to 1.0, which means no bias is applied." + " A value of 2.0 will double the weight of the bias, and a value of 0.5 will halve it." + ), + ) + parser.add_argument( + "--timestep_bias_begin", + type=int, + default=0, + help=( + "When using `--timestep_bias_strategy=range`, the beginning (inclusive) timestep to bias." + " Defaults to zero, which equates to having no specific bias." + ), + ) + parser.add_argument( + "--timestep_bias_end", + type=int, + default=1000, + help=( + "When using `--timestep_bias_strategy=range`, the final timestep (inclusive) to bias." + " Defaults to 1000, which is the number of timesteps that Stable Diffusion is trained on." + ), + ) + parser.add_argument( + "--timestep_bias_portion", + type=float, + default=0.25, + help=( + "The portion of timesteps to bias. Defaults to 0.25, which 25% of timesteps will be biased." + " A value of 0.5 will bias one half of the timesteps. The value provided for `--timestep_bias_strategy` determines" + " whether the biased portions are in the earlier or later timesteps." + ), + ) + parser.add_argument( + "--snr_gamma", + type=float, + default=None, + help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. " + "More details here: https://arxiv.org/abs/2303.09556.", + ) + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument( + "--allow_tf32", + action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument( + "--dataloader_num_workers", + type=int, + default=10, + help=( + "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process." + ), + ) + parser.add_argument( + "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes." + ) + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") + parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.") + parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.") + parser.add_argument( + "--prediction_type", + type=str, + default=None, + help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.", + ) + parser.add_argument( + "--hub_model_id", + type=str, + default=None, + help="The name of the repository to keep in sync with the local `output_dir`.", + ) + parser.add_argument( + "--logging_dir", + type=str, + default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument( + "--report_to", + type=str, + default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + parser.add_argument( + "--mixed_precision", + type=str, + default=None, + choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + + args = parser.parse_args() + main(args) diff --git a/opensora/train/train_t2v_new.py b/opensora/train/train_t2v_new.py new file mode 100644 index 000000000..d2891b4ba --- /dev/null +++ b/opensora/train/train_t2v_new.py @@ -0,0 +1,958 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +import logging +import math +import os +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + pass +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import ProjectConfiguration, set_seed +from huggingface_hub import create_repo +from packaging import version +from tqdm.auto import tqdm +from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.utils.diffusion import create_diffusion_T as create_diffusion +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.utils.dataset_utils import Collate +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.sample.pipeline_opensora import OpenSoraPipeline + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + + +@torch.inference_mode() +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): + validation_prompt = [ + "a cat wearing sunglasses and working as a lifeguard at pool.", + "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." + ] + logger.info(f"Running validation....\n") + model = accelerator.unwrap_model(model) + # scheduler = PNDMScheduler() + scheduler = DPMSolverMultistepScheduler() + opensora_pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model).to(device=accelerator.device) + videos = [] + for prompt in validation_prompt: + logger.info('Processing the ({}) prompt'.format(prompt)) + video = opensora_pipeline(prompt, + num_frames=args.num_frames, + # num_frames=1, + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=50, + ).images + videos.append(video[0]) + # import ipdb;ipdb.set_trace() + gc.collect() + torch.cuda.empty_cache() + videos = torch.stack(videos).numpy() + videos = rearrange(videos, 'b t h w c -> b t c h w') + for tracker in accelerator.trackers: + if tracker.name == "tensorboard": + if videos.shape[1] == 1: + assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + np_images = np.stack([np.asarray(img) for img in images]) + tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") + else: + np_videos = np.stack([np.asarray(vid) for vid in videos]) + tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) + if tracker.name == "wandb": + import wandb + if videos.shape[1] == 1: + # assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + # import ipdb;ipdb.set_trace() + logs = { + f"{'ema_' if ema else ''}validation": [ + wandb.Image(image, caption=f"{i}: {prompt}") + for i, (image, prompt) in enumerate(zip(images, validation_prompt)) + ] + } + else: + logs = { + f"{'ema_' if ema else ''}validation": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=30) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ] + } + # import ipdb;ipdb.set_trace() + if hasattr(model.pos_embed, 'temp_embed_gate'): + logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + tracker.log(logs, step=global_step) + + del opensora_pipeline + gc.collect() + torch.cuda.empty_cache() + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + logging_dir = Path(args.output_dir, args.logging_dir) + + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) + # npu_config.seed_everything() + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # if args.push_to_hub: + # repo_id = create_repo( + # repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token + # ).repo_id + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + diffusion = create_diffusion(timestep_respacing="") # default: 1000 steps, linear noise schedule + kwargs = {} + ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae], + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + interpolation_scale_t=args.interpolation_scale_t, + downsampler=args.downsampler, + # compress_kv_factor=args.compress_kv_factor, + # use_rope=args.use_rope, + # model_max_length=args.model_max_length, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # # use pretrained model? + if args.pretrained: + if 'safetensors' in args.pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + checkpoint = safe_load(args.pretrained, device="cpu") + if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + repeat = model.pos_embed.proj.weight.shape[2] + checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] + model_state_dict = model.state_dict() + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # Freeze vae and text encoders. + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + # Set model as trainable. + model.train() + + noise_scheduler = DDPMScheduler() + noise_scheduler.config.prediction_type == "v_prediction" + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + # ae.to(accelerator.device, dtype=torch.float32) + ae.vae.to(accelerator.device, dtype=weight_dtype) + # ae.to(accelerator.device) + text_enc.to(accelerator.device, dtype=weight_dtype) + # text_enc.to(accelerator.device) + + # Create EMA for the unet. + if args.use_ema: + ema_model = deepcopy(model) + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) + ema_model.load_state_dict(load_model.state_dict()) + ema_model.to(accelerator.device) + del load_model + + for i in range(len(models)): + # pop models so that they are not loaded again + model = models.pop() + + # load diffusers style into model + load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + # Use 8-bit Adam for lower memory usage or to fine-tune the model in 16GB GPUs + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + # Optimizer creation + params_to_optimize = model.parameters() + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + # Setup data: + train_dataset = getdataset(args) + train_dataloader = torch.utils.data.DataLoader( + train_dataset, + shuffle=True, + pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + model, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_model.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + if accelerator.is_main_process: + accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {model}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_model.step(model.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + one_step_duration = end_time - start_time + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg( + f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) + progress_info.train_loss = 0.0 + + if args.use_deepspeed or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(model_input, model_kwargs, prof): + global start_time + start_time = time.time() + # t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) + + noise = torch.randn_like(model_input) + if args.noise_offset: + # https://www.crosslabs.org//blog/diffusion-with-offset-noise + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1), device=model_input.device) + + bsz = model_input.shape[0] + # Sample a random timestep for each image without bias. + timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + + # Add noise to the model input according to the noise magnitude at each timestep + # (this is the forward diffusion process) + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) + + model_pred = model( + noisy_model_input, + timesteps, + **model_kwargs + )[0] + + # Get the target for loss depending on the prediction type + if args.prediction_type is not None: + # set prediction_type of scheduler if defined + noise_scheduler.register_to_config(prediction_type=args.prediction_type) + + if noise_scheduler.config.prediction_type == "epsilon": + target = noise + elif noise_scheduler.config.prediction_type == "v_prediction": + target = noise_scheduler.get_velocity(model_input, noise, timesteps) + elif noise_scheduler.config.prediction_type == "sample": + # We set the target to latents here, but the model_pred will return the noise sample prediction. + target = model_input + # We will have to subtract the noise residual from the prediction to get the target sample. + model_pred = model_pred - noise + else: + raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + + if args.snr_gamma is None: + loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + else: + # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. + # Since we predict the noise instead of x_0, the original formulation is slightly changed. + # This is discussed in Section 4.2 of the same paper. + snr = compute_snr(noise_scheduler, timesteps) + mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min( + dim=1 + )[0] + if noise_scheduler.config.prediction_type == "epsilon": + mse_loss_weights = mse_loss_weights / snr + elif noise_scheduler.config.prediction_type == "v_prediction": + mse_loss_weights = mse_loss_weights / (snr + 1) + + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights + loss = loss.mean() + + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = model.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + if prof is not None: + prof.step() + + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + for tracker in accelerator.trackers: + if tracker.name == "wandb": + if progress_info.global_step % args.checkpointing_steps != 0: + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + + if progress_info.global_step % args.checkpointing_steps == 0: + + if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step) + + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + if npu_config is None: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + + return loss + def train_one_step(step_, data_item_, prof_=None): + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 + attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W + input_ids = input_ids.to(accelerator.device) # B 1+num_images L + cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1+num_images L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D + cond = cond.reshape(B, N, L, -1) + + # Map input images to latent space + normalize latents + if args.use_image_num == 0: + x = ae.encode(x) # B C T H W + else: + videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] + videos = ae.encode(videos) # B C T H W + images = rearrange(images, 'b c t h w -> (b t) c 1 h w') + images = ae.encode(images) + images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + + with accelerator.accumulate(model): + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + + if train_one_step(step, data_item, prof_): + break + + if step >= 2 and torch_npu is not None and npu_config is not None: + npu_config.free_mm() + + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/shebin/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--model_max_length", type=int, default=512) + + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--downsampler", type=str, default=None) + + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--use_deepspeed", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument( + "--output_dir", + type=str, + default=None, + help="The output directory where the model predictions and checkpoints will be written.", + ) + parser.add_argument( + "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader." + ) + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument( + "--max_train_steps", + type=int, + default=None, + help="Total number of training steps to perform. If provided, overrides num_train_epochs.", + ) + parser.add_argument( + "--checkpointing_steps", + type=int, + default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument( + "--checkpoints_total_limit", + type=int, + default=None, + help=("Max number of checkpoints to store."), + ) + parser.add_argument( + "--resume_from_checkpoint", + type=str, + default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument( + "--gradient_accumulation_steps", + type=int, + default=1, + help="Number of updates steps to accumulate before performing a backward/update pass.", + ) + parser.add_argument( + "--gradient_checkpointing", + action="store_true", + help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", + ) + parser.add_argument( + "--learning_rate", + type=float, + default=1e-4, + help="Initial learning rate (after the potential warmup period) to use.", + ) + parser.add_argument( + "--scale_lr", + action="store_true", + default=False, + help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.", + ) + parser.add_argument( + "--lr_scheduler", + type=str, + default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument( + "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." + ) + parser.add_argument( + "--timestep_bias_strategy", + type=str, + default="none", + choices=["earlier", "later", "range", "none"], + help=( + "The timestep bias strategy, which may help direct the model toward learning low or high frequency details." + " Choices: ['earlier', 'later', 'range', 'none']." + " The default is 'none', which means no bias is applied, and training proceeds normally." + " The value of 'later' will increase the frequency of the model's final training timesteps." + ), + ) + parser.add_argument( + "--timestep_bias_multiplier", + type=float, + default=1.0, + help=( + "The multiplier for the bias. Defaults to 1.0, which means no bias is applied." + " A value of 2.0 will double the weight of the bias, and a value of 0.5 will halve it." + ), + ) + parser.add_argument( + "--timestep_bias_begin", + type=int, + default=0, + help=( + "When using `--timestep_bias_strategy=range`, the beginning (inclusive) timestep to bias." + " Defaults to zero, which equates to having no specific bias." + ), + ) + parser.add_argument( + "--timestep_bias_end", + type=int, + default=1000, + help=( + "When using `--timestep_bias_strategy=range`, the final timestep (inclusive) to bias." + " Defaults to 1000, which is the number of timesteps that Stable Diffusion is trained on." + ), + ) + parser.add_argument( + "--timestep_bias_portion", + type=float, + default=0.25, + help=( + "The portion of timesteps to bias. Defaults to 0.25, which 25% of timesteps will be biased." + " A value of 0.5 will bias one half of the timesteps. The value provided for `--timestep_bias_strategy` determines" + " whether the biased portions are in the earlier or later timesteps." + ), + ) + parser.add_argument( + "--snr_gamma", + type=float, + default=None, + help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. " + "More details here: https://arxiv.org/abs/2303.09556.", + ) + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument( + "--allow_tf32", + action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument( + "--dataloader_num_workers", + type=int, + default=10, + help=( + "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process." + ), + ) + parser.add_argument( + "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes." + ) + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") + parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.") + parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.") + parser.add_argument( + "--prediction_type", + type=str, + default=None, + help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.", + ) + parser.add_argument( + "--hub_model_id", + type=str, + default=None, + help="The name of the repository to keep in sync with the local `output_dir`.", + ) + parser.add_argument( + "--logging_dir", + type=str, + default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument( + "--report_to", + type=str, + default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + parser.add_argument( + "--mixed_precision", + type=str, + default=None, + choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + + args = parser.parse_args() + main(args) diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index f1cdde2c8..d1ae926f8 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,2 +1,4 @@ node030 slots=8 -node031 slots=8 \ No newline at end of file +node028 slots=8 +node064 slots=8 +node033 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile1 b/scripts/accelerate_configs/hostfile1 index 18d214630..9dd1526fd 100644 --- a/scripts/accelerate_configs/hostfile1 +++ b/scripts/accelerate_configs/hostfile1 @@ -1,2 +1,4 @@ -node032 slots=8 -node033 slots=8 \ No newline at end of file +node031 slots=8 +node027 slots=8 +node034 slots=8 +node035 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile2 b/scripts/accelerate_configs/hostfile2 new file mode 100644 index 000000000..8ee64692c --- /dev/null +++ b/scripts/accelerate_configs/hostfile2 @@ -0,0 +1,2 @@ +node032 slots=8 +node033 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index cfe5c39c9..cea7297b1 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -8,8 +8,8 @@ machine_rank: 0 main_process_ip: 100.64.24.30 main_process_port: 29502 main_training_function: main -num_machines: 2 -num_processes: 16 +num_machines: 4 +num_processes: 32 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/accelerate_configs/multi_node_example1.yaml b/scripts/accelerate_configs/multi_node_example1.yaml index 6912a17b1..155956d0b 100644 --- a/scripts/accelerate_configs/multi_node_example1.yaml +++ b/scripts/accelerate_configs/multi_node_example1.yaml @@ -5,11 +5,11 @@ deepspeed_config: deepspeed_hostfile: scripts/accelerate_configs/hostfile1 fsdp_config: {} machine_rank: 0 -main_process_ip: 100.64.24.32 -main_process_port: 29502 +main_process_ip: 100.64.24.31 +main_process_port: 29503 main_training_function: main -num_machines: 2 -num_processes: 16 +num_machines: 4 +num_processes: 32 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/accelerate_configs/multi_node_example2.yaml b/scripts/accelerate_configs/multi_node_example2.yaml new file mode 100644 index 000000000..b1c7dad43 --- /dev/null +++ b/scripts/accelerate_configs/multi_node_example2.yaml @@ -0,0 +1,18 @@ +compute_environment: LOCAL_MACHINE +distributed_type: DEEPSPEED +deepspeed_config: + deepspeed_config_file: scripts/accelerate_configs/zero2.json + deepspeed_hostfile: scripts/accelerate_configs/hostfile2 +fsdp_config: {} +machine_rank: 0 +main_process_ip: 100.64.24.32 +main_process_port: 29502 +main_training_function: main +num_machines: 2 +num_processes: 16 +rdzv_backend: static +same_network: true +tpu_env: [] +tpu_use_cluster: false +tpu_use_sudo: false +use_cpu: false diff --git a/scripts/text_condition/new_train_image_256x256.sh b/scripts/text_condition/new_train_image_256x256.sh new file mode 100644 index 000000000..406cb41d7 --- /dev/null +++ b/scripts/text_condition/new_train_image_256x256.sh @@ -0,0 +1,54 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11_vpre" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example2.yaml \ + opensora/train/train_t2v_new.py \ + --model OpenSoraT2V-B/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=32 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11_vpre" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 2.5 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_ds_image_256x256.sh b/scripts/text_condition/train_ds_image_256x256.sh new file mode 100644 index 000000000..d4c958335 --- /dev/null +++ b/scripts/text_condition/train_ds_image_256x256.sh @@ -0,0 +1,55 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs16_4node_lr1e-4_snr5_ema" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example1.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-B/111 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs16_4node_lr1e-4_snr5_ema_ps11_ds22" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 2.5 \ + --downsampler "k55_s22" \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_ds_video3d_65x240p.sh b/scripts/text_condition/train_ds_video3d_65x240p.sh new file mode 100644 index 000000000..4b9051a31 --- /dev/null +++ b/scripts/text_condition/train_ds_video3d_65x240p.sh @@ -0,0 +1,55 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="testnpu3d_" +# export HF_DATASETS_OFFLINE=1 +# export TRANSFORMERS_OFFLINE=1 +# NCCL setting +# export NCCL_PXN_DISABLE=0 +# export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_IB_GID_INDEX=3 +# export NCCL_ALGO=Ring +# export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v.py \ + --model OpenSoraT2V-L/111 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ + --sample_rate 1 \ + --num_frames 253 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=100 \ + --output_dir="testnpu3d_" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tiling \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 7.5 \ + --downsampler "k333_s444" \ diff --git a/scripts/text_condition/train_image_256x256.sh b/scripts/text_condition/train_image_256x256.sh index b63bb7956..31143878f 100644 --- a/scripts/text_condition/train_image_256x256.sh +++ b/scripts/text_condition/train_image_256x256.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="testimg_" +export PROJECT="bs16_4node_lr1e-4_snr5_ema" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,16 +12,16 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v.py \ - --model OpenSoraT2V-L/122 \ + --model OpenSoraT2V-B/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ --num_frames 1 \ --max_height 256 \ @@ -35,13 +35,13 @@ accelerate launch \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ + --learning_rate=1e-4 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=10 \ - --output_dir="testimg_" \ + --checkpointing_steps=500 \ + --output_dir="bs16_4node_lr1e-4_snr5_ema_ps22_ds11" \ --allow_tf32 \ --use_deepspeed \ --model_max_length 512 \ @@ -50,4 +50,5 @@ accelerate launch \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 2.5 + --guidance_scale 2.5 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_image_256x256_edm.sh b/scripts/text_condition/train_image_256x256_edm.sh new file mode 100644 index 000000000..85e7b4063 --- /dev/null +++ b/scripts/text_condition/train_image_256x256_edm.sh @@ -0,0 +1,53 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11_edm" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example2.yaml \ + opensora/train/train_t2v_edm.py \ + --model OpenSoraT2V-B/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=32 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs32_2node_lr1e-4_snr5_ema_ps22_ds11_edm" \ + --allow_tf32 \ + --use_deepspeed \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --use_ema \ + --ema_start_step 0 \ + --guidance_scale 2.5 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_video3d_65x240p.sh b/scripts/text_condition/train_video3d_65x240p.sh index b98ae1a32..441a2faf5 100644 --- a/scripts/text_condition/train_video3d_65x240p.sh +++ b/scripts/text_condition/train_video3d_65x240p.sh @@ -23,7 +23,7 @@ accelerate launch \ --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ - --num_frames 509 \ + --num_frames 253 \ --max_height 480 \ --max_width 640 \ --interpolation_scale_t 1.0 \ @@ -51,4 +51,5 @@ accelerate launch \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 7.5 + --guidance_scale 5.0 \ + --resume_from_checkpoint="latest" diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index 753b15ff1..851a50d81 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,4 +1,3 @@ -/dxyl_data02/datasets/tuzhan_mj,/dxyl_data02/anno_jsons/tuzhan_mj_1712571.json -/dxyl_data02/datasets/images,/dxyl_data02/anno_jsons/human_images_162094.json -/dxyl_data02/datasets/anytext3m,/dxyl_data02/anno_jsons/anytext_en_1886137.json -/dxyl_data02/datasets/sam,/dxyl_data02/anno_jsons/sam_image_11185255.json \ No newline at end of file +/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json +/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json +/storage/dataset/image/anytext3m,/storage/anno_jsons/anytext_en_1886137.json \ No newline at end of file diff --git a/scripts/train_data/image_data_debug.txt b/scripts/train_data/image_data_debug.txt index bbf00d70f..0c7df18fc 100644 --- a/scripts/train_data/image_data_debug.txt +++ b/scripts/train_data/image_data_debug.txt @@ -1 +1 @@ -/dxyl_data02/datasets/images,/storage/anno_jsons/human_images_162094.json \ No newline at end of file +/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json \ No newline at end of file diff --git a/scripts/train_data/video_data_513.txt b/scripts/train_data/video_data_513.txt index bdab87c8a..796c7d1f3 100644 --- a/scripts/train_data/video_data_513.txt +++ b/scripts/train_data/video_data_513.txt @@ -1,3 +1 @@ -/dxyl_data02/datasets/pixabay_v2,/dxyl_data02/anno_jsons/video_pixabay_513f_51483.json -/dxyl_data02/datasets/mixkit,/dxyl_data02/anno_jsons/video_mixkit_513f_1997.json -/dxyl_data02/datasets/pexels,/dxyl_data02/anno_jsons/video_pexel_513f_271782.json \ No newline at end of file +/storage/dataset/mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt index 1ace10925..796c7d1f3 100644 --- a/scripts/train_data/video_data_debug.txt +++ b/scripts/train_data/video_data_debug.txt @@ -1 +1 @@ -/storage/mixkit/LanguageBind/Open-Sora-Plan-v1.1.0/all_mixkit/mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/storage/dataset/mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file From 5322144a1cd700e3944ec57fa9da92142509e471 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Sun, 9 Jun 2024 23:11:55 +0800 Subject: [PATCH 027/134] update train_t2v and model --- .gitignore | 3 +- opensora/dataset/__init__.py | 4 +- opensora/dataset/t2v_datasets.py | 23 +- .../diffusion/opensora/modeling_opensora.py | 21 +- opensora/models/diffusion/opensora/modules.py | 38 +- opensora/models/text_encoder/__init__.py | 5 +- opensora/sample/transport_sample.py | 203 ---- opensora/train/train_t2v.py | 909 ----------------- ...rain_t2v_new.py => train_t2v_diffusers.py} | 325 +++--- opensora/train/train_t2v_edm.py | 923 ------------------ opensora/utils/dataset_utils.py | 4 +- opensora/utils/diffusion/__init__.py | 87 -- opensora/utils/diffusion/diffusion_utils.py | 111 --- .../utils/diffusion/gaussian_diffusion.py | 881 ----------------- .../utils/diffusion/gaussian_diffusion_t2v.py | 915 ----------------- opensora/utils/diffusion/respace.py | 198 ---- opensora/utils/diffusion/timestep_sampler.py | 150 --- pyproject.toml | 2 +- .../text_condition/new_train_image_256x256.sh | 54 - .../text_condition/train_ds_image_256x256.sh | 3 +- .../train_ds_video3d_65x240p.sh | 31 +- scripts/text_condition/train_image_256x256.sh | 13 +- .../text_condition/train_image_256x256_edm.sh | 53 - .../train_image_256x256_on_npu.sh | 4 +- scripts/text_condition/train_imageae.sh | 34 - .../text_condition/train_udit3d_61x240p.sh | 54 - .../train_udit3d_61x240p_on_npu.sh | 46 - .../text_condition/train_video21d_65x240p.sh | 8 +- .../train_video21d_65x240p_on_npu.sh | 7 +- .../text_condition/train_video3d_65x240p.sh | 6 +- .../train_video3d_65x240p_on_npu.sh | 7 +- 31 files changed, 199 insertions(+), 4923 deletions(-) delete mode 100644 opensora/sample/transport_sample.py delete mode 100644 opensora/train/train_t2v.py rename opensora/train/{train_t2v_new.py => train_t2v_diffusers.py} (83%) delete mode 100644 opensora/train/train_t2v_edm.py delete mode 100644 opensora/utils/diffusion/__init__.py delete mode 100644 opensora/utils/diffusion/diffusion_utils.py delete mode 100644 opensora/utils/diffusion/gaussian_diffusion.py delete mode 100644 opensora/utils/diffusion/gaussian_diffusion_t2v.py delete mode 100644 opensora/utils/diffusion/respace.py delete mode 100644 opensora/utils/diffusion/timestep_sampler.py delete mode 100644 scripts/text_condition/new_train_image_256x256.sh delete mode 100644 scripts/text_condition/train_image_256x256_edm.sh delete mode 100644 scripts/text_condition/train_imageae.sh delete mode 100644 scripts/text_condition/train_udit3d_61x240p.sh delete mode 100644 scripts/text_condition/train_udit3d_61x240p_on_npu.sh diff --git a/.gitignore b/.gitignore index cc2e5f3a9..3b6cea2e8 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,5 @@ taming* sft* flash* 65x256* -alpha_vae \ No newline at end of file +alpha_vae +new_bs32* \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index d50f16674..b3430fd09 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -61,7 +61,7 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 8d3c34ca3..345293921 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -130,7 +130,7 @@ def get_video(self, idx): video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W - # video = torch.rand(509, 3, 240, 320) + # video = torch.rand(253, 3, 720, 1280) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] @@ -173,6 +173,7 @@ def get_image(self, idx): text = [text_preprocessing(cap) for cap in caps] input_ids, cond_mask = [], [] for t in text: + t = t if random.random() > self.cfg else "" text_tokens_and_mask = self.tokenizer( t, max_length=self.model_max_length, @@ -188,22 +189,6 @@ def get_image(self, idx): cond_mask = torch.cat(cond_mask) # self.use_image_num, l return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) - def tv_read(self, path, frame_idx=None): - vframes, aframes, info = torchvision.io.read_video(filename=path, pts_unit='sec', output_format='TCHW') - total_frames = len(vframes) - if frame_idx is None: - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - else: - start_frame_ind, end_frame_ind = frame_idx.split(':') - start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) - # assert end_frame_ind - start_frame_ind >= self.num_frames - frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) - - video = vframes[frame_indice] # (T, C, H, W) - - return video - def decord_read(self, path, frame_idx=None): decord_vr = self.v_decoder(path) total_frames = len(decord_vr) @@ -236,7 +221,7 @@ def read_jsons(self, data, postfix=".jpg"): if npu_config is not None: sub_list = filter_json_by_existed_files(folder, sub_list, postfix=postfix) cap_lists += sub_list - return cap_lists[:-1] # drop last to avoid error length + return cap_lists def get_img_cap_list(self): use_image_num = self.use_image_num if self.use_image_num != 0 else 1 @@ -248,7 +233,7 @@ def get_img_cap_list(self): lambda: self.read_jsons(self.image_data, postfix=".jpg")) img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] - return img_cap_lists + return img_cap_lists[:-1] # drop last to avoid error length def get_vid_cap_list(self): if npu_config is None: diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 2153d942e..76d43f3df 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -110,6 +110,7 @@ def __init__( self.in_channels = in_channels self.out_channels = in_channels if out_channels is None else out_channels self.gradient_checkpointing = False + self.config.hidden_size = self.inner_dim use_additional_conditions = False # if use_additional_conditions is None: # if norm_type == "ada_norm_single" and sample_size == 128: @@ -169,7 +170,7 @@ def _init_patched_inputs(self, norm_type): # interpolation_scale_t=interpolation_scale_t, # ) # else: - if self.config.downsampler is not None and len(self.config.downsampler) == 9 and self.config.patch_size == 1 and self.config.patch_size == 1: + if self.config.downsampler is not None and len(self.config.downsampler) == 9: self.pos_embed = OverlapPatchEmbed3D( num_frames=self.config.sample_size_t, height=self.config.sample_size[0], @@ -181,7 +182,7 @@ def _init_patched_inputs(self, norm_type): interpolation_scale=interpolation_scale, interpolation_scale_t=interpolation_scale_t, ) - elif self.config.downsampler is not None and len(self.config.downsampler) == 7 and self.config.patch_size == 1 and self.config.patch_size == 1: + elif self.config.downsampler is not None and len(self.config.downsampler) == 7: self.pos_embed = OverlapPatchEmbed2D( num_frames=self.config.sample_size_t, height=self.config.sample_size[0], @@ -610,9 +611,9 @@ def OpenSoraT2V_L_122(**kwargs): # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) -# def OpenSoraT2V_B_222(**kwargs): -# return OpenSoraT2V(num_layers=24, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) +def OpenSoraT2V_B_222(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) # def OpenSoraT2V_L_222(**kwargs): # return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, @@ -636,7 +637,7 @@ def OpenSoraT2V_L_122(**kwargs): # "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, # "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, # "OpenSoraT2V-S/222": OpenSoraT2V_S_222, - # "OpenSoraT2V-B/222": OpenSoraT2V_B_222, + "OpenSoraT2V-B/222": OpenSoraT2V_B_222, # "OpenSoraT2V-L/222": OpenSoraT2V_L_222, # "OpenSoraT2V-XL/222": OpenSoraT2V_XL_222, # "OpenSoraT2V-XXL/222": OpenSoraT2V_XXL_222, @@ -649,6 +650,8 @@ def OpenSoraT2V_L_122(**kwargs): "OpenSoraT2V-S/111": OpenSoraT2V, "OpenSoraT2V-B/111": OpenSoraT2V, "OpenSoraT2V-L/111": OpenSoraT2V, + + "OpenSoraT2V-B/222": OpenSoraT2V, } if __name__ == '__main__': @@ -664,7 +667,7 @@ def OpenSoraT2V_L_122(**kwargs): 'model_max_length': 300, 'max_height': 512, 'max_width': 512, - 'num_frames': 1, + 'num_frames': 61, 'use_image_num': 0, 'compress_kv_factor': 1 } @@ -681,7 +684,7 @@ def OpenSoraT2V_L_122(**kwargs): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:0') - model = OpenSoraT2V_B_111(in_channels=4, + model = OpenSoraT2V_B_222(in_channels=4, out_channels=8, sample_size=latent_size, sample_size_t=num_frames, @@ -697,7 +700,7 @@ def OpenSoraT2V_L_122(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler=None).to(device) + downsampler='k333_s222').to(device) try: path = "PixArt-Alpha-XL-2-512.safetensors" diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index fc8917e68..7817dffda 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -270,12 +270,12 @@ def __init__( interpolation_scale_t=1, ): super().__init__() - assert patch_size_t == 1 and patch_size == 1 + # assert patch_size_t == 1 and patch_size == 1 self.flatten = flatten self.layer_norm = layer_norm self.proj = nn.Conv3d( - in_channels, embed_dim, kernel_size=3, padding=1, stride=1, bias=bias + in_channels, embed_dim, kernel_size=(patch_size_t, patch_size, patch_size), stride=(patch_size_t, patch_size, patch_size), bias=bias ) if layer_norm: self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) @@ -388,12 +388,12 @@ def __init__( interpolation_scale_t=1, ): super().__init__() - assert patch_size_t == 1 and patch_size == 1 + assert patch_size_t == 1 self.flatten = flatten self.layer_norm = layer_norm self.proj = nn.Conv2d( - in_channels, embed_dim, kernel_size=3, padding=1, stride=1, bias=bias + in_channels, embed_dim, kernel_size=(patch_size, patch_size), stride=(patch_size, patch_size), bias=bias ) if layer_norm: self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) @@ -975,21 +975,21 @@ def __init__( self.norm3 = None # if downsampler: - # downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 - # if len(downsampler_ker_size) == 3: - # self.ff = FeedForward_Conv3d( - # downsampler, - # dim, - # 2 * dim, - # bias=ff_bias, - # ) - # elif len(downsampler_ker_size) == 2: - # self.ff = FeedForward_Conv2d( - # downsampler, - # dim, - # 2 * dim, - # bias=ff_bias, - # ) + downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 + # if len(downsampler_ker_size) == 3: + # self.ff = FeedForward_Conv3d( + # downsampler, + # dim, + # 2 * dim, + # bias=ff_bias, + # ) + # elif len(downsampler_ker_size) == 2: + # self.ff = FeedForward_Conv2d( + # downsampler, + # dim, + # 4 * dim, + # bias=ff_bias, + # ) # else: self.ff = FeedForward( dim, diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 6cb136ef7..5dc05548b 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -9,8 +9,8 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] @@ -31,6 +31,7 @@ def forward(self, input_ids, attention_mask): text_encoder = { + 'google/umt5-xxl': T5Wrapper, 'DeepFloyd/t5-v1_1-xxl': T5Wrapper, 'openai/clip-vit-large-patch14': CLIPWrapper } diff --git a/opensora/sample/transport_sample.py b/opensora/sample/transport_sample.py deleted file mode 100644 index b88b55206..000000000 --- a/opensora/sample/transport_sample.py +++ /dev/null @@ -1,203 +0,0 @@ -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. - -""" -Sample new images from a pre-trained SiT. -""" -import os -import sys - -from opensora.dataset import ae_denorm -from opensora.models.ae import ae_channel_config, getae, ae_stride_config -from opensora.models.diffusion import Diffusion_models -from opensora.models.diffusion.transport import create_transport, Sampler -from opensora.utils.utils import find_model - -import torch -import argparse - -from einops import rearrange -import imageio - -torch.backends.cuda.matmul.allow_tf32 = True -torch.backends.cudnn.allow_tf32 = True - - - -def main(mode, args): - # Setup PyTorch: - # torch.manual_seed(args.seed) - torch.set_grad_enabled(False) - device = "cuda" if torch.cuda.is_available() else "cpu" - - using_cfg = args.cfg_scale > 1.0 - - # Load model: - latent_size = args.image_size // ae_stride_config[args.ae][1] - args.latent_size = latent_size - model = Diffusion_models[args.model]( - input_size=latent_size, - num_classes=args.num_classes, - in_channels=ae_channel_config[args.ae], - extras=args.extras - ).to(device) - - if args.use_compile: - model = torch.compile(model) - - # a pre-trained model or load a custom Latte checkpoint from train.py: - ckpt_path = args.ckpt - state_dict = find_model(ckpt_path) - model.load_state_dict(state_dict) - - model.eval() # important! - transport = create_transport( - args.path_type, - args.prediction, - args.loss_weight, - args.train_eps, - args.sample_eps - ) - sampler = Sampler(transport) - if mode == "ODE": - if args.likelihood: - assert args.cfg_scale == 1, "Likelihood is incompatible with guidance" - sample_fn = sampler.sample_ode_likelihood( - sampling_method=args.sampling_method, - num_steps=args.num_sampling_steps, - atol=args.atol, - rtol=args.rtol, - ) - else: - sample_fn = sampler.sample_ode( - sampling_method=args.sampling_method, - num_steps=args.num_sampling_steps, - atol=args.atol, - rtol=args.rtol, - reverse=args.reverse - ) - elif mode == "SDE": - sample_fn = sampler.sample_sde( - sampling_method=args.sampling_method, - diffusion_form=args.diffusion_form, - diffusion_norm=args.diffusion_norm, - last_step=args.last_step, - last_step_size=args.last_step_size, - num_steps=args.num_sampling_steps, - ) - - ae = getae(args).to(device) - - if args.use_fp16: - print('WARNING: using half percision for inferencing!') - ae.to(dtype=torch.float16) - model.to(dtype=torch.float16) - - # Labels to condition the model with (feel free to change): - - # Create sampling noise: - if args.use_fp16: - z = torch.randn(1, args.num_frames // ae_stride_config[args.ae][0], model.in_channels, latent_size, latent_size, dtype=torch.float16, device=device) # b c f h w - else: - z = torch.randn(1, args.num_frames // ae_stride_config[args.ae][0], model.in_channels, latent_size, latent_size, device=device) - - # Setup classifier-free guidance: - if using_cfg: - z = torch.cat([z, z], 0) - y = torch.randint(0, args.num_classes, (1,), device=device) - y_null = torch.tensor([args.num_classes] * 1, device=device) - y = torch.cat([y, y_null], dim=0) - model_kwargs = dict(y=y, cfg_scale=args.cfg_scale, use_fp16=args.use_fp16) - forward_fn = model.forward_with_cfg - else: - forward_fn = model.forward - model_kwargs = dict(y=None, use_fp16=args.use_fp16) - - # Sample images: - samples = sample_fn(z, forward_fn, **model_kwargs)[-1] - - if args.use_fp16: - samples = samples.to(dtype=torch.float16) - samples = ae.decode(samples) - - # Save and display images: - if not os.path.exists(args.save_video_path): - os.makedirs(args.save_video_path) - - - video_ = (ae_denorm[args.ae](samples[0]) * 255).add_(0.5).clamp_(0, 255).to(dtype=torch.uint8).cpu().permute(0, 2, 3, 1).contiguous() - video_save_path = os.path.join(args.save_video_path, 'sample' + '.mp4') - print(video_save_path) - imageio.mimwrite(video_save_path, video_, fps=args.fps, quality=9) - print('save path {}'.format(args.save_video_path)) - - -def none_or_str(value): - if value == 'None': - return None - return value - -def parse_transport_args(parser): - group = parser.add_argument_group("Transport arguments") - group.add_argument("--path-type", type=str, default="Linear", choices=["Linear", "GVP", "VP"]) - group.add_argument("--prediction", type=str, default="velocity", choices=["velocity", "score", "noise"]) - group.add_argument("--loss-weight", type=none_or_str, default=None, choices=[None, "velocity", "likelihood"]) - group.add_argument("--sample-eps", type=float) - group.add_argument("--train-eps", type=float) - -def parse_ode_args(parser): - group = parser.add_argument_group("ODE arguments") - group.add_argument("--sampling-method", type=str, default="dopri5", help="blackbox ODE solver methods; for full list check https://github.com/rtqichen/torchdiffeq") - group.add_argument("--atol", type=float, default=1e-6, help="Absolute tolerance") - group.add_argument("--rtol", type=float, default=1e-3, help="Relative tolerance") - group.add_argument("--reverse", action="store_true") - group.add_argument("--likelihood", action="store_true") - -def parse_sde_args(parser): - group = parser.add_argument_group("SDE arguments") - group.add_argument("--sampling-method", type=str, default="Euler", choices=["Euler", "Heun"]) - group.add_argument("--diffusion-form", type=str, default="sigma", \ - choices=["constant", "SBDM", "sigma", "linear", "decreasing", "increasing-decreasing"],\ - help="form of diffusion coefficient in the SDE") - group.add_argument("--diffusion-norm", type=float, default=1.0) - group.add_argument("--last-step", type=none_or_str, default="Mean", choices=[None, "Mean", "Tweedie", "Euler"],\ - help="form of last step taken in the SDE") - group.add_argument("--last-step-size", type=float, default=0.04, \ - help="size of the last step taken") - -if __name__ == "__main__": - if len(sys.argv) < 2: - print("Usage: program.py [options]") - sys.exit(1) - - mode = sys.argv[1] - - assert mode[:2] != "--", "Usage: program.py [options]" - assert mode in ["ODE", "SDE"], "Invalid mode. Please choose 'ODE' or 'SDE'" - - parser = argparse.ArgumentParser() - parser.add_argument("--ckpt", type=str, default="") - parser.add_argument("--model", type=str, default='Latte-XL/122') - parser.add_argument("--ae", type=str, default='stabilityai/sd-vae-ft-mse') - parser.add_argument("--save-video-path", type=str, default="./sample_videos/") - parser.add_argument("--fps", type=int, default=10) - parser.add_argument("--num-classes", type=int, default=101) - parser.add_argument("--num-frames", type=int, default=16) - parser.add_argument("--image-size", type=int, default=256, choices=[256, 512]) - parser.add_argument("--extras", type=int, default=1) - parser.add_argument("--num-sampling-steps", type=int, default=250) - parser.add_argument("--cfg-scale", type=float, default=1.0) - parser.add_argument("--use-fp16", action="store_true") - parser.add_argument("--use-compile", action="store_true") - parser.add_argument("--sample-method", type=str, default='ddpm') - - parse_transport_args(parser) - if mode == "ODE": - parse_ode_args(parser) - # Further processing for ODE - elif mode == "SDE": - parse_sde_args(parser) - # Further processing for SDE - - args = parser.parse_known_args()[0] - main(mode, args) diff --git a/opensora/train/train_t2v.py b/opensora/train/train_t2v.py deleted file mode 100644 index 7d7820ae5..000000000 --- a/opensora/train/train_t2v.py +++ /dev/null @@ -1,909 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. - -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. - -""" -A minimal training script for DiT using PyTorch DDP. -""" -import argparse -import logging -import math -import os -import shutil -from pathlib import Path -from typing import Optional -import gc -import numpy as np -from einops import rearrange -from tqdm import tqdm -try: - import torch_npu - from opensora.npu_config import npu_config -except: - torch_npu = None - npu_config = None - pass -import time -from dataclasses import field, dataclass -from torch.utils.data import DataLoader -from copy import deepcopy -import accelerate -import torch -from torch.nn import functional as F -import transformers -from accelerate import Accelerator -from accelerate.logging import get_logger -from accelerate.utils import ProjectConfiguration, set_seed -from huggingface_hub import create_repo -from packaging import version -from tqdm.auto import tqdm -from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer - -import diffusers -from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler -from diffusers.optimization import get_scheduler -from diffusers.training_utils import EMAModel, compute_snr -from diffusers.utils import check_min_version, is_wandb_available - -from opensora.dataset import getdataset, ae_denorm -from opensora.models.ae import getae, getae_wrapper -from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.utils.diffusion import create_diffusion_T as create_diffusion -from opensora.utils.diffusion.diffusion_utils import compute_snr -from opensora.models.diffusion.latte.modeling_latte import LatteT2V -from opensora.models.text_encoder import get_text_enc, get_text_warpper -from opensora.utils.dataset_utils import Collate -from opensora.models.ae import ae_stride_config, ae_channel_config -from opensora.models.diffusion import Diffusion_models, Diffusion_models_class -from opensora.sample.pipeline_opensora import OpenSoraPipeline - -# Will error if the minimal version of diffusers is not installed. Remove at your own risks. -check_min_version("0.24.0") -logger = get_logger(__name__) - - -@torch.inference_mode() -def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): - validation_prompt = [ - "a cat wearing sunglasses and working as a lifeguard at pool.", - "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." - ] - logger.info(f"Running validation....\n") - model = accelerator.unwrap_model(model) - # scheduler = PNDMScheduler() - scheduler = DPMSolverMultistepScheduler() - opensora_pipeline = OpenSoraPipeline(vae=vae, - text_encoder=text_encoder, - tokenizer=tokenizer, - scheduler=scheduler, - transformer=model).to(device=accelerator.device) - videos = [] - for prompt in validation_prompt: - logger.info('Processing the ({}) prompt'.format(prompt)) - video = opensora_pipeline(prompt, - num_frames=args.num_frames, - # num_frames=1, - height=args.max_height, - width=args.max_width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - enable_temporal_attentions=True, - num_images_per_prompt=1, - mask_feature=True, - max_sequence_length=50, - ).images - videos.append(video[0]) - # import ipdb;ipdb.set_trace() - gc.collect() - torch.cuda.empty_cache() - videos = torch.stack(videos).numpy() - videos = rearrange(videos, 'b t h w c -> b t c h w') - for tracker in accelerator.trackers: - if tracker.name == "tensorboard": - if videos.shape[1] == 1: - assert args.num_frames == 1 - images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') - np_images = np.stack([np.asarray(img) for img in images]) - tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") - else: - np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) - if tracker.name == "wandb": - import wandb - if videos.shape[1] == 1: - # assert args.num_frames == 1 - images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') - # import ipdb;ipdb.set_trace() - logs = { - f"{'ema_' if ema else ''}validation": [ - wandb.Image(image, caption=f"{i}: {prompt}") - for i, (image, prompt) in enumerate(zip(images, validation_prompt)) - ] - } - else: - logs = { - f"{'ema_' if ema else ''}validation": [ - wandb.Video(video, caption=f"{i}: {prompt}", fps=30) - for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) - ] - } - # import ipdb;ipdb.set_trace() - if hasattr(model.pos_embed, 'temp_embed_gate'): - logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) - tracker.log(logs, step=global_step) - - del opensora_pipeline - gc.collect() - torch.cuda.empty_cache() - -class ProgressInfo: - def __init__(self, global_step, train_loss=0.0): - self.global_step = global_step - self.train_loss = train_loss -################################################################################# -# Training Loop # -################################################################################# - -def main(args): - logging_dir = Path(args.output_dir, args.logging_dir) - - if torch_npu is not None and npu_config is not None: - npu_config.print_msg(args) - npu_config.seed_everything() - accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) - - accelerator = Accelerator( - gradient_accumulation_steps=args.gradient_accumulation_steps, - mixed_precision=args.mixed_precision, - log_with=args.report_to, - project_config=accelerator_project_config, - ) - - if args.report_to == "wandb": - if not is_wandb_available(): - raise ImportError("Make sure to install wandb if you want to use it for logging during training.") - - # Make one log on every process with the configuration for debugging. - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO, - ) - logger.info(accelerator.state, main_process_only=False) - if accelerator.is_local_main_process: - transformers.utils.logging.set_verbosity_warning() - diffusers.utils.logging.set_verbosity_info() - else: - transformers.utils.logging.set_verbosity_error() - diffusers.utils.logging.set_verbosity_error() - - # If passed along, set the training seed now. - if args.seed is not None: - set_seed(args.seed) - - # Handle the repository creation - if accelerator.is_main_process: - if args.output_dir is not None: - os.makedirs(args.output_dir, exist_ok=True) - - # if args.push_to_hub: - # repo_id = create_repo( - # repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token - # ).repo_id - - # For mixed precision training we cast all non-trainable weigths to half-precision - # as these weights are only used for inference, keeping weights in full precision is not required. - weight_dtype = torch.float32 - if accelerator.mixed_precision == "fp16": - weight_dtype = torch.float16 - elif accelerator.mixed_precision == "bf16": - weight_dtype = torch.bfloat16 - - # Create model: - diffusion = create_diffusion(timestep_respacing="") # default: 1000 steps, linear noise schedule - kwargs = {} - ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() - if args.enable_tiling: - ae.vae.enable_tiling() - ae.vae.tile_overlap_factor = args.tile_overlap_factor - - kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} - text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() - - ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) - assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" - args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w - args.ae_stride = args.ae_stride_h - patch_size = args.model[-3:] - patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) - args.patch_size = patch_size_h - args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w - assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" - # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." - assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." - assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." - - - args.stride_t = ae_stride_t * patch_size_t - args.stride = ae_stride_h * patch_size_h - latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) - ae.latent_size = latent_size - - if args.num_frames % 2 == 1: - args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 - else: - latent_size_t = args.num_frames // ae_stride_t - model = Diffusion_models[args.model]( - in_channels=ae_channel_config[args.ae], - out_channels=ae_channel_config[args.ae] * 2, - # caption_channels=4096, - # cross_attention_dim=1152, - attention_bias=True, - sample_size=latent_size, - sample_size_t=latent_size_t, - num_vector_embeds=None, - activation_fn="gelu-approximate", - num_embeds_ada_norm=1000, - use_linear_projection=False, - only_cross_attention=False, - double_self_attention=False, - upcast_attention=False, - # norm_type="ada_norm_single", - norm_elementwise_affine=False, - norm_eps=1e-6, - attention_type='default', - attention_mode=args.attention_mode, - interpolation_scale_h=args.interpolation_scale_h, - interpolation_scale_w=args.interpolation_scale_w, - interpolation_scale_t=args.interpolation_scale_t, - downsampler=args.downsampler, - # compress_kv_factor=args.compress_kv_factor, - # use_rope=args.use_rope, - # model_max_length=args.model_max_length, - ) - model.gradient_checkpointing = args.gradient_checkpointing - - # # use pretrained model? - if args.pretrained: - if 'safetensors' in args.pretrained: # pixart series - from safetensors.torch import load_file as safe_load - # import ipdb;ipdb.set_trace() - checkpoint = safe_load(args.pretrained, device="cpu") - if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: - logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") - repeat = model.pos_embed.proj.weight.shape[2] - checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) - # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] - else: # latest stage training weight - checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] - model_state_dict = model.state_dict() - missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) - logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') - logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') - - # Freeze vae and text encoders. - ae.vae.requires_grad_(False) - text_enc.requires_grad_(False) - # Set model as trainable. - model.train() - - - # Move unet, vae and text_encoder to device and cast to weight_dtype - # The VAE is in float32 to avoid NaN losses. - # ae.to(accelerator.device, dtype=torch.float32) - ae.vae.to(accelerator.device, dtype=weight_dtype) - # ae.to(accelerator.device) - text_enc.to(accelerator.device, dtype=weight_dtype) - # text_enc.to(accelerator.device) - - # Create EMA for the unet. - if args.use_ema: - ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, - model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) - - # `accelerate` 0.16.0 will have better support for customized saving - if version.parse(accelerate.__version__) >= version.parse("0.16.0"): - # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format - def save_model_hook(models, weights, output_dir): - if accelerator.is_main_process: - if args.use_ema: - ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) - - for i, model in enumerate(models): - model.save_pretrained(os.path.join(output_dir, "model")) - if weights: # Don't pop if empty - # make sure to pop weight so that corresponding model is not saved again - weights.pop() - - def load_model_hook(models, input_dir): - if args.use_ema: - load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) - ema_model.load_state_dict(load_model.state_dict()) - ema_model.to(accelerator.device) - del load_model - - for i in range(len(models)): - # pop models so that they are not loaded again - model = models.pop() - - # load diffusers style into model - load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") - model.register_to_config(**load_model.config) - - model.load_state_dict(load_model.state_dict()) - del load_model - - accelerator.register_save_state_pre_hook(save_model_hook) - accelerator.register_load_state_pre_hook(load_model_hook) - - # Enable TF32 for faster training on Ampere GPUs, - # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices - if args.allow_tf32: - torch.backends.cuda.matmul.allow_tf32 = True - - if args.scale_lr: - args.learning_rate = ( - args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes - ) - - # Use 8-bit Adam for lower memory usage or to fine-tune the model in 16GB GPUs - if args.use_8bit_adam: - try: - import bitsandbytes as bnb - except ImportError: - raise ImportError( - "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." - ) - - optimizer_class = bnb.optim.AdamW8bit - else: - optimizer_class = torch.optim.AdamW - - # Optimizer creation - params_to_optimize = model.parameters() - optimizer = optimizer_class( - params_to_optimize, - lr=args.learning_rate, - betas=(args.adam_beta1, args.adam_beta2), - weight_decay=args.adam_weight_decay, - eps=args.adam_epsilon, - ) - - # Setup data: - train_dataset = getdataset(args) - train_dataloader = torch.utils.data.DataLoader( - train_dataset, - shuffle=True, - pin_memory=True, - collate_fn=Collate(args), - batch_size=args.train_batch_size, - num_workers=args.dataloader_num_workers, - ) - - # Scheduler and math around the number of training steps. - overrode_max_train_steps = False - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if args.max_train_steps is None: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - overrode_max_train_steps = True - - lr_scheduler = get_scheduler( - args.lr_scheduler, - optimizer=optimizer, - num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, - num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, - ) - - # Prepare everything with our `accelerator`. - model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( - model, optimizer, train_dataloader, lr_scheduler - ) - if args.use_ema: - ema_model.to(accelerator.device) - - # We need to recalculate our total training steps as the size of the training dataloader may have changed. - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if overrode_max_train_steps: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - # Afterwards we recalculate our number of training epochs - args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) - - # We need to initialize the trackers we use, and also store our configuration. - # The trackers initializes automatically on the main process. - if accelerator.is_main_process: - accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) - - # Train! - total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps - - logger.info("***** Running training *****") - logger.info(f" Model = {model}") - logger.info(f" Num examples = {len(train_dataset)}") - logger.info(f" Num Epochs = {args.num_train_epochs}") - logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") - logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") - logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") - logger.info(f" Total optimization steps = {args.max_train_steps}") - logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") - global_step = 0 - first_epoch = 0 - - # Potentially load in the weights and states from a previous save - if args.resume_from_checkpoint: - if args.resume_from_checkpoint != "latest": - path = os.path.basename(args.resume_from_checkpoint) - else: - # Get the most recent checkpoint - dirs = os.listdir(args.output_dir) - dirs = [d for d in dirs if d.startswith("checkpoint")] - dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) - path = dirs[-1] if len(dirs) > 0 else None - - if path is None: - accelerator.print( - f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." - ) - args.resume_from_checkpoint = None - initial_global_step = 0 - else: - accelerator.print(f"Resuming from checkpoint {path}") - accelerator.load_state(os.path.join(args.output_dir, path)) - global_step = int(path.split("-")[1]) - - initial_global_step = global_step - first_epoch = global_step // num_update_steps_per_epoch - else: - initial_global_step = 0 - - if npu_config is not None: - # train_dataset.n_used_elements = global_step * args.train_batch_size - train_dataset.set_checkpoint(global_step * args.train_batch_size) - - progress_bar = tqdm( - range(0, args.max_train_steps), - initial=initial_global_step, - desc="Steps", - # Only show the progress bar once on each machine. - disable=not accelerator.is_local_main_process, - ) - progress_info = ProgressInfo(global_step, train_loss=0.0) - - def sync_gradients_info(loss): - # Checks if the accelerator has performed an optimization step behind the scenes - if args.use_ema: - ema_model.step(model.parameters()) - progress_bar.update(1) - progress_info.global_step += 1 - end_time = time.time() - one_step_duration = end_time - start_time - accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) - if torch_npu is not None and npu_config is not None: - npu_config.print_msg( - f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", - rank=0) - progress_info.train_loss = 0.0 - - if args.use_deepspeed or accelerator.is_main_process: - if progress_info.global_step % args.checkpointing_steps == 0: - # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` - if args.checkpoints_total_limit is not None: - checkpoints = os.listdir(args.output_dir) - checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] - checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) - - # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints - if len(checkpoints) >= args.checkpoints_total_limit: - num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 - removing_checkpoints = checkpoints[0:num_to_remove] - - logger.info( - f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" - ) - logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") - - for removing_checkpoint in removing_checkpoints: - removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) - shutil.rmtree(removing_checkpoint) - - save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") - accelerator.save_state(save_path) - logger.info(f"Saved state to {save_path}") - - logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} - progress_bar.set_postfix(**logs) - - def run(x, model_kwargs, prof): - global start_time - start_time = time.time() - t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) - - if args.snr_gamma is not None: - snr = compute_snr(t, torch.from_numpy(diffusion.alphas_cumprod)) - mse_loss_weights = (torch.stack([snr, args.snr_gamma * torch.ones_like(t)], dim=1).min(dim=1)[0] / snr) - # print(t, mse_loss_weights) - loss_dict = diffusion.training_losses(model, x, t, model_kwargs, mse_loss_weights=mse_loss_weights) - else: - loss_dict = diffusion.training_losses(model, x, t, model_kwargs) - - loss = loss_dict["loss"].mean() - - # Backpropagate - accelerator.backward(loss) - torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1) - if accelerator.sync_gradients: - params_to_clip = model.parameters() - accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) - optimizer.step() - lr_scheduler.step() - optimizer.zero_grad() - if prof is not None: - prof.step() - - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps - - if accelerator.sync_gradients: - sync_gradients_info(loss) - - if accelerator.is_main_process: - for tracker in accelerator.trackers: - if tracker.name == "wandb": - if progress_info.global_step % args.checkpointing_steps != 0: - if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): - tracker.log( - {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) - elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): - tracker.log( - {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) - - if progress_info.global_step % args.checkpointing_steps == 0: - - if args.enable_tracker: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step) - - if args.use_ema: - # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - if npu_config is None: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step, ema=True) - # Switch back to the original UNet parameters. - ema_model.restore(model.parameters()) - - return loss - def train_one_step(step_, data_item_, prof_=None): - train_loss = 0.0 - x, attn_mask, input_ids, cond_mask = data_item_ - # Sample noise that we'll add to the latents - - if not args.multi_scale: - assert torch.all(attn_mask) - x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 - attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W - input_ids = input_ids.to(accelerator.device) # B 1+num_images L - cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L - # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) - - with torch.no_grad(): - # import ipdb;ipdb.set_trace() - # use for loop to avoid OOM, because T5 is too huge... - B, N, L = input_ids.shape # B 1+num_images L - # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D - - # use batch inference - input_ids_ = input_ids.reshape(-1, L) - cond_mask_ = cond_mask.reshape(-1, L) - cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D - cond = cond.reshape(B, N, L, -1) - - # Map input images to latent space + normalize latents - if args.use_image_num == 0: - x = ae.encode(x) # B C T H W - else: - videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] - videos = ae.encode(videos) # B C T H W - images = rearrange(images, 'b c t h w -> (b t) c 1 h w') - images = ae.encode(images) - images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) - x = torch.cat([videos, images], dim=2) # b c 17+4, h, w - - with accelerator.accumulate(model): - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) - run(x, model_kwargs, prof_) - - if progress_info.global_step >= args.max_train_steps: - return True - - return False - - def train_all_epoch(prof_=None): - for epoch in range(first_epoch, args.num_train_epochs): - progress_info.train_loss = 0.0 - if progress_info.global_step >= args.max_train_steps: - return True - - for step, data_item in enumerate(train_dataloader): - - if train_one_step(step, data_item, prof_): - break - - if step >= 2 and torch_npu is not None and npu_config is not None: - npu_config.free_mm() - - if npu_config is not None and npu_config.on_npu and npu_config.profiling: - experimental_config = torch_npu.profiler._ExperimentalConfig( - profiler_level=torch_npu.profiler.ProfilerLevel.Level1, - aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization - ) - profile_output_path = f"/home/image_data/shebin/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" - os.makedirs(profile_output_path, exist_ok=True) - - with torch_npu.profiler.profile( - activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], - with_stack=True, - record_shapes=True, - profile_memory=True, - experimental_config=experimental_config, - schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, - skip_first=0), - on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") - ) as prof: - train_all_epoch(prof) - else: - train_all_epoch() - accelerator.wait_for_everyone() - accelerator.end_training() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--dataset", type=str, required=True) - parser.add_argument("--video_data", type=str, required='') - parser.add_argument("--image_data", type=str, default='') - parser.add_argument("--sample_rate", type=int, default=1) - parser.add_argument("--num_frames", type=int, default=65) - parser.add_argument("--max_height", type=int, default=320) - parser.add_argument("--max_width", type=int, default=240) - parser.add_argument("--use_img_from_vid", action="store_true") - parser.add_argument("--use_image_num", type=int, default=0) - parser.add_argument("--model_max_length", type=int, default=512) - - parser.add_argument('--enable_8bit_t5', action='store_true') - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) - parser.add_argument('--enable_tiling', action='store_true') - parser.add_argument("--compress_kv", action="store_true") - parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") - parser.add_argument('--use_rope', action='store_true') - parser.add_argument('--compress_kv_factor', type=int, default=1) - parser.add_argument('--interpolation_scale_h', type=float, default=1.0) - parser.add_argument('--interpolation_scale_w', type=float, default=1.0) - parser.add_argument('--interpolation_scale_t', type=float, default=1.0) - parser.add_argument('--cfg', type=float, default=0.1) - parser.add_argument("--ema_start_step", type=int, default=0) - parser.add_argument("--downsampler", type=str, default=None) - - parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") - parser.add_argument("--pretrained", type=str, default=None) - parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') - parser.add_argument("--cache_dir", type=str, default='./cache_dir') - - parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=5.0) - parser.add_argument("--multi_scale", action="store_true") - parser.add_argument("--enable_tracker", action="store_true") - parser.add_argument("--use_deepspeed", action="store_true") - parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") - parser.add_argument( - "--output_dir", - type=str, - default=None, - help="The output directory where the model predictions and checkpoints will be written.", - ) - parser.add_argument( - "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader." - ) - parser.add_argument("--num_train_epochs", type=int, default=100) - parser.add_argument( - "--max_train_steps", - type=int, - default=None, - help="Total number of training steps to perform. If provided, overrides num_train_epochs.", - ) - parser.add_argument( - "--checkpointing_steps", - type=int, - default=500, - help=( - "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" - " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" - " training using `--resume_from_checkpoint`." - ), - ) - parser.add_argument( - "--checkpoints_total_limit", - type=int, - default=None, - help=("Max number of checkpoints to store."), - ) - parser.add_argument( - "--resume_from_checkpoint", - type=str, - default=None, - help=( - "Whether training should be resumed from a previous checkpoint. Use a path saved by" - ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' - ), - ) - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument( - "--gradient_checkpointing", - action="store_true", - help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", - ) - parser.add_argument( - "--learning_rate", - type=float, - default=1e-4, - help="Initial learning rate (after the potential warmup period) to use.", - ) - parser.add_argument( - "--scale_lr", - action="store_true", - default=False, - help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.", - ) - parser.add_argument( - "--lr_scheduler", - type=str, - default="constant", - help=( - 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' - ' "constant", "constant_with_warmup"]' - ), - ) - parser.add_argument( - "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." - ) - parser.add_argument( - "--timestep_bias_strategy", - type=str, - default="none", - choices=["earlier", "later", "range", "none"], - help=( - "The timestep bias strategy, which may help direct the model toward learning low or high frequency details." - " Choices: ['earlier', 'later', 'range', 'none']." - " The default is 'none', which means no bias is applied, and training proceeds normally." - " The value of 'later' will increase the frequency of the model's final training timesteps." - ), - ) - parser.add_argument( - "--timestep_bias_multiplier", - type=float, - default=1.0, - help=( - "The multiplier for the bias. Defaults to 1.0, which means no bias is applied." - " A value of 2.0 will double the weight of the bias, and a value of 0.5 will halve it." - ), - ) - parser.add_argument( - "--timestep_bias_begin", - type=int, - default=0, - help=( - "When using `--timestep_bias_strategy=range`, the beginning (inclusive) timestep to bias." - " Defaults to zero, which equates to having no specific bias." - ), - ) - parser.add_argument( - "--timestep_bias_end", - type=int, - default=1000, - help=( - "When using `--timestep_bias_strategy=range`, the final timestep (inclusive) to bias." - " Defaults to 1000, which is the number of timesteps that Stable Diffusion is trained on." - ), - ) - parser.add_argument( - "--timestep_bias_portion", - type=float, - default=0.25, - help=( - "The portion of timesteps to bias. Defaults to 0.25, which 25% of timesteps will be biased." - " A value of 0.5 will bias one half of the timesteps. The value provided for `--timestep_bias_strategy` determines" - " whether the biased portions are in the earlier or later timesteps." - ), - ) - parser.add_argument( - "--snr_gamma", - type=float, - default=None, - help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. " - "More details here: https://arxiv.org/abs/2303.09556.", - ) - parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") - parser.add_argument( - "--allow_tf32", - action="store_true", - help=( - "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" - " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" - ), - ) - parser.add_argument( - "--dataloader_num_workers", - type=int, - default=10, - help=( - "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process." - ), - ) - parser.add_argument( - "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes." - ) - parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") - parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") - parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") - parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") - parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") - parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.") - parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.") - parser.add_argument( - "--prediction_type", - type=str, - default=None, - help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.", - ) - parser.add_argument( - "--hub_model_id", - type=str, - default=None, - help="The name of the repository to keep in sync with the local `output_dir`.", - ) - parser.add_argument( - "--logging_dir", - type=str, - default="logs", - help=( - "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" - " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." - ), - ) - parser.add_argument( - "--report_to", - type=str, - default="tensorboard", - help=( - 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' - ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' - ), - ) - parser.add_argument( - "--mixed_precision", - type=str, - default=None, - choices=["no", "fp16", "bf16"], - help=( - "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" - " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" - " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." - ), - ) - parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") - parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") - - args = parser.parse_args() - main(args) diff --git a/opensora/train/train_t2v_new.py b/opensora/train/train_t2v_diffusers.py similarity index 83% rename from opensora/train/train_t2v_new.py rename to opensora/train/train_t2v_diffusers.py index d2891b4ba..bd02d9647 100644 --- a/opensora/train/train_t2v_new.py +++ b/opensora/train/train_t2v_diffusers.py @@ -35,7 +35,7 @@ import transformers from accelerate import Accelerator from accelerate.logging import get_logger -from accelerate.utils import ProjectConfiguration, set_seed +from accelerate.utils import DistributedType, ProjectConfiguration, set_seed from huggingface_hub import create_repo from packaging import version from tqdm.auto import tqdm @@ -50,7 +50,6 @@ from opensora.dataset import getdataset, ae_denorm from opensora.models.ae import getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.utils.diffusion import create_diffusion_T as create_diffusion from opensora.models.diffusion.latte.modeling_latte import LatteT2V from opensora.models.text_encoder import get_text_enc, get_text_warpper from opensora.utils.dataset_utils import Collate @@ -187,11 +186,6 @@ def main(args): if args.output_dir is not None: os.makedirs(args.output_dir, exist_ok=True) - # if args.push_to_hub: - # repo_id = create_repo( - # repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token - # ).repo_id - # For mixed precision training we cast all non-trainable weigths to half-precision # as these weights are only used for inference, keeping weights in full precision is not required. weight_dtype = torch.float32 @@ -201,7 +195,6 @@ def main(args): weight_dtype = torch.bfloat16 # Create model: - diffusion = create_diffusion(timestep_respacing="") # default: 1000 steps, linear noise schedule kwargs = {} ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() if args.enable_tiling: @@ -290,14 +283,11 @@ def main(args): model.train() noise_scheduler = DDPMScheduler() - noise_scheduler.config.prediction_type == "v_prediction" # Move unet, vae and text_encoder to device and cast to weight_dtype # The VAE is in float32 to avoid NaN losses. # ae.to(accelerator.device, dtype=torch.float32) ae.vae.to(accelerator.device, dtype=weight_dtype) - # ae.to(accelerator.device) text_enc.to(accelerator.device, dtype=weight_dtype) - # text_enc.to(accelerator.device) # Create EMA for the unet. if args.use_ema: @@ -350,28 +340,67 @@ def load_model_hook(models, input_dir): args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes ) - # Use 8-bit Adam for lower memory usage or to fine-tune the model in 16GB GPUs - if args.use_8bit_adam: + + params_to_optimize = model.parameters() + # Optimizer creation + if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): + logger.warning( + f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]." + "Defaulting to adamW" + ) + args.optimizer = "adamw" + + if args.use_8bit_adam and not args.optimizer.lower() == "adamw": + logger.warning( + f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " + f"set to {args.optimizer.lower()}" + ) + + if args.optimizer.lower() == "adamw": + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + if args.optimizer.lower() == "prodigy": try: - import bitsandbytes as bnb + import prodigyopt except ImportError: - raise ImportError( - "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." - ) + raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`") - optimizer_class = bnb.optim.AdamW8bit - else: - optimizer_class = torch.optim.AdamW + optimizer_class = prodigyopt.Prodigy - # Optimizer creation - params_to_optimize = model.parameters() - optimizer = optimizer_class( - params_to_optimize, - lr=args.learning_rate, - betas=(args.adam_beta1, args.adam_beta2), - weight_decay=args.adam_weight_decay, - eps=args.adam_epsilon, - ) + if args.learning_rate <= 0.1: + logger.warning( + "Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0" + ) + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + beta3=args.prodigy_beta3, + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + decouple=args.prodigy_decouple, + use_bias_correction=args.prodigy_use_bias_correction, + safeguard_warmup=args.prodigy_safeguard_warmup, + ) # Setup data: train_dataset = getdataset(args) @@ -487,7 +516,8 @@ def sync_gradients_info(loss): rank=0) progress_info.train_loss = 0.0 - if args.use_deepspeed or accelerator.is_main_process: + # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. + if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: if progress_info.global_step % args.checkpointing_steps == 0: # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` if args.checkpoints_total_limit is not None: @@ -519,7 +549,6 @@ def sync_gradients_info(loss): def run(model_input, model_kwargs, prof): global start_time start_time = time.time() - # t = torch.randint(0, diffusion.num_timesteps, (x.shape[0],), device=accelerator.device) noise = torch.randn_like(model_input) if args.noise_offset: @@ -539,7 +568,6 @@ def run(model_input, model_kwargs, prof): timesteps, **model_kwargs )[0] - # Get the target for loss depending on the prediction type if args.prediction_type is not None: # set prediction_type of scheduler if defined @@ -713,6 +741,8 @@ def train_all_epoch(prof_=None): if __name__ == "__main__": parser = argparse.ArgumentParser() + + # dataset & dataloader parser.add_argument("--dataset", type=str, required=True) parser.add_argument("--video_data", type=str, required='') parser.add_argument("--image_data", type=str, default='') @@ -723,7 +753,13 @@ def train_all_epoch(prof_=None): parser.add_argument("--use_img_from_vid", action="store_true") parser.add_argument("--use_image_num", type=int, default=0) parser.add_argument("--model_max_length", type=int, default=512) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + # text encoder & vae & diffusion model + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") parser.add_argument('--enable_8bit_t5', action='store_true') parser.add_argument('--tile_overlap_factor', type=float, default=0.25) parser.add_argument('--enable_tiling', action='store_true') @@ -734,225 +770,96 @@ def train_all_epoch(prof_=None): parser.add_argument('--interpolation_scale_h', type=float, default=1.0) parser.add_argument('--interpolation_scale_w', type=float, default=1.0) parser.add_argument('--interpolation_scale_t', type=float, default=1.0) - parser.add_argument('--cfg', type=float, default=0.1) - parser.add_argument("--ema_start_step", type=int, default=0) parser.add_argument("--downsampler", type=str, default=None) - - parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") - parser.add_argument("--pretrained", type=str, default=None) parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + + # diffusion setting + parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + # validation & logs parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=5.0) - parser.add_argument("--multi_scale", action="store_true") + parser.add_argument('--guidance_scale', type=float, default=2.5) parser.add_argument("--enable_tracker", action="store_true") - parser.add_argument("--use_deepspeed", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") - parser.add_argument( - "--output_dir", - type=str, - default=None, - help="The output directory where the model predictions and checkpoints will be written.", - ) - parser.add_argument( - "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader." - ) - parser.add_argument("--num_train_epochs", type=int, default=100) - parser.add_argument( - "--max_train_steps", - type=int, - default=None, - help="Total number of training steps to perform. If provided, overrides num_train_epochs.", - ) - parser.add_argument( - "--checkpointing_steps", - type=int, - default=500, + parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--checkpointing_steps", type=int, default=500, help=( "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" " training using `--resume_from_checkpoint`." ), ) - parser.add_argument( - "--checkpoints_total_limit", - type=int, - default=None, - help=("Max number of checkpoints to store."), - ) - parser.add_argument( - "--resume_from_checkpoint", - type=str, - default=None, + parser.add_argument("--resume_from_checkpoint", type=str, default=None, help=( "Whether training should be resumed from a previous checkpoint. Use a path saved by" ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' ), ) - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument( - "--gradient_checkpointing", - action="store_true", - help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", - ) - parser.add_argument( - "--learning_rate", - type=float, - default=1e-4, - help="Initial learning rate (after the potential warmup period) to use.", - ) - parser.add_argument( - "--scale_lr", - action="store_true", - default=False, - help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.", - ) - parser.add_argument( - "--lr_scheduler", - type=str, - default="constant", - help=( - 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' - ' "constant", "constant_with_warmup"]' - ), - ) - parser.add_argument( - "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." - ) - parser.add_argument( - "--timestep_bias_strategy", - type=str, - default="none", - choices=["earlier", "later", "range", "none"], - help=( - "The timestep bias strategy, which may help direct the model toward learning low or high frequency details." - " Choices: ['earlier', 'later', 'range', 'none']." - " The default is 'none', which means no bias is applied, and training proceeds normally." - " The value of 'later' will increase the frequency of the model's final training timesteps." - ), - ) - parser.add_argument( - "--timestep_bias_multiplier", - type=float, - default=1.0, + parser.add_argument("--logging_dir", type=str, default="logs", help=( - "The multiplier for the bias. Defaults to 1.0, which means no bias is applied." - " A value of 2.0 will double the weight of the bias, and a value of 0.5 will halve it." + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." ), ) - parser.add_argument( - "--timestep_bias_begin", - type=int, - default=0, + parser.add_argument("--report_to", type=str, default="tensorboard", help=( - "When using `--timestep_bias_strategy=range`, the beginning (inclusive) timestep to bias." - " Defaults to zero, which equates to having no specific bias." + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' ), ) - parser.add_argument( - "--timestep_bias_end", - type=int, - default=1000, - help=( - "When using `--timestep_bias_strategy=range`, the final timestep (inclusive) to bias." - " Defaults to 1000, which is the number of timesteps that Stable Diffusion is trained on." - ), + # optimizer & scheduler + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") + parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--prodigy_beta3", type=float, default=None, + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", ) - parser.add_argument( - "--timestep_bias_portion", - type=float, - default=0.25, + parser.add_argument("--lr_scheduler", type=str, default="constant", help=( - "The portion of timesteps to bias. Defaults to 0.25, which 25% of timesteps will be biased." - " A value of 0.5 will bias one half of the timesteps. The value provided for `--timestep_bias_strategy` determines" - " whether the biased portions are in the earlier or later timesteps." + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' ), ) - parser.add_argument( - "--snr_gamma", - type=float, - default=None, - help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. " - "More details here: https://arxiv.org/abs/2303.09556.", - ) - parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") - parser.add_argument( - "--allow_tf32", - action="store_true", + parser.add_argument("--allow_tf32", action="store_true", help=( "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" ), ) - parser.add_argument( - "--dataloader_num_workers", - type=int, - default=10, - help=( - "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process." - ), - ) - parser.add_argument( - "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes." - ) - parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") - parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") - parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") - parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") - parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") - parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.") - parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.") - parser.add_argument( - "--prediction_type", - type=str, - default=None, - help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.", - ) - parser.add_argument( - "--hub_model_id", - type=str, - default=None, - help="The name of the repository to keep in sync with the local `output_dir`.", - ) - parser.add_argument( - "--logging_dir", - type=str, - default="logs", - help=( - "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" - " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." - ), - ) - parser.add_argument( - "--report_to", - type=str, - default="tensorboard", - help=( - 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' - ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' - ), - ) - parser.add_argument( - "--mixed_precision", - type=str, - default=None, - choices=["no", "fp16", "bf16"], + parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], help=( "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." ), ) + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") - parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") args = parser.parse_args() main(args) diff --git a/opensora/train/train_t2v_edm.py b/opensora/train/train_t2v_edm.py deleted file mode 100644 index 86e3b32c5..000000000 --- a/opensora/train/train_t2v_edm.py +++ /dev/null @@ -1,923 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. - -# This source code is licensed under the license found in the -# LICENSE file in the root directory of this source tree. - -""" -A minimal training script for DiT using PyTorch DDP. -""" -import argparse -import logging -import math -import os -import shutil -from pathlib import Path -from typing import Optional -import gc -import numpy as np -from einops import rearrange -from tqdm import tqdm -try: - import torch_npu - from opensora.npu_config import npu_config -except: - torch_npu = None - npu_config = None - pass -import time -from dataclasses import field, dataclass -from torch.utils.data import DataLoader -from copy import deepcopy -import accelerate -import torch -from torch.nn import functional as F -import transformers -from accelerate import Accelerator -from accelerate.logging import get_logger -from accelerate.utils import ProjectConfiguration, set_seed -from huggingface_hub import create_repo -from packaging import version -from tqdm.auto import tqdm -from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer - -import diffusers -from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler, EulerDiscreteScheduler -from diffusers.optimization import get_scheduler -from diffusers.training_utils import EMAModel, compute_snr -from diffusers.utils import check_min_version, is_wandb_available - -from opensora.dataset import getdataset, ae_denorm -from opensora.models.ae import getae, getae_wrapper -from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.utils.diffusion import create_diffusion_T as create_diffusion -from opensora.models.diffusion.latte.modeling_latte import LatteT2V -from opensora.models.text_encoder import get_text_enc, get_text_warpper -from opensora.utils.dataset_utils import Collate -from opensora.models.ae import ae_stride_config, ae_channel_config -from opensora.models.diffusion import Diffusion_models, Diffusion_models_class -from opensora.sample.pipeline_opensora import OpenSoraPipeline - -# Will error if the minimal version of diffusers is not installed. Remove at your own risks. -check_min_version("0.24.0") -logger = get_logger(__name__) - - -@torch.inference_mode() -def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): - validation_prompt = [ - "a cat wearing sunglasses and working as a lifeguard at pool.", - "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." - ] - logger.info(f"Running validation....\n") - model = accelerator.unwrap_model(model) - # scheduler = PNDMScheduler() - # scheduler = DPMSolverMultistepScheduler() - scheduler = EulerDiscreteScheduler.from_pretrained("stabilityai/stable-video-diffusion-img2vid", - subfolder="scheduler", cache_dir=args.cache_dir) - opensora_pipeline = OpenSoraPipeline(vae=vae, - text_encoder=text_encoder, - tokenizer=tokenizer, - scheduler=scheduler, - transformer=model).to(device=accelerator.device) - videos = [] - for prompt in validation_prompt: - logger.info('Processing the ({}) prompt'.format(prompt)) - video = opensora_pipeline(prompt, - num_frames=args.num_frames, - # num_frames=1, - height=args.max_height, - width=args.max_width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - enable_temporal_attentions=True, - num_images_per_prompt=1, - mask_feature=True, - max_sequence_length=50, - ).images - videos.append(video[0]) - # import ipdb;ipdb.set_trace() - gc.collect() - torch.cuda.empty_cache() - videos = torch.stack(videos).numpy() - videos = rearrange(videos, 'b t h w c -> b t c h w') - for tracker in accelerator.trackers: - if tracker.name == "tensorboard": - if videos.shape[1] == 1: - assert args.num_frames == 1 - images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') - np_images = np.stack([np.asarray(img) for img in images]) - tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") - else: - np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) - if tracker.name == "wandb": - import wandb - if videos.shape[1] == 1: - # assert args.num_frames == 1 - images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') - # import ipdb;ipdb.set_trace() - logs = { - f"{'ema_' if ema else ''}validation": [ - wandb.Image(image, caption=f"{i}: {prompt}") - for i, (image, prompt) in enumerate(zip(images, validation_prompt)) - ] - } - else: - logs = { - f"{'ema_' if ema else ''}validation": [ - wandb.Video(video, caption=f"{i}: {prompt}", fps=30) - for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) - ] - } - # import ipdb;ipdb.set_trace() - if hasattr(model.pos_embed, 'temp_embed_gate'): - logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) - tracker.log(logs, step=global_step) - - del opensora_pipeline - gc.collect() - torch.cuda.empty_cache() - -class ProgressInfo: - def __init__(self, global_step, train_loss=0.0): - self.global_step = global_step - self.train_loss = train_loss -################################################################################# -# Training Loop # -################################################################################# - -def main(args): - logging_dir = Path(args.output_dir, args.logging_dir) - - if torch_npu is not None and npu_config is not None: - npu_config.print_msg(args) - # npu_config.seed_everything() - accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) - - accelerator = Accelerator( - gradient_accumulation_steps=args.gradient_accumulation_steps, - mixed_precision=args.mixed_precision, - log_with=args.report_to, - project_config=accelerator_project_config, - ) - - if args.report_to == "wandb": - if not is_wandb_available(): - raise ImportError("Make sure to install wandb if you want to use it for logging during training.") - - # Make one log on every process with the configuration for debugging. - logging.basicConfig( - format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", - datefmt="%m/%d/%Y %H:%M:%S", - level=logging.INFO, - ) - logger.info(accelerator.state, main_process_only=False) - if accelerator.is_local_main_process: - transformers.utils.logging.set_verbosity_warning() - diffusers.utils.logging.set_verbosity_info() - else: - transformers.utils.logging.set_verbosity_error() - diffusers.utils.logging.set_verbosity_error() - - # If passed along, set the training seed now. - if args.seed is not None: - set_seed(args.seed) - - # Handle the repository creation - if accelerator.is_main_process: - if args.output_dir is not None: - os.makedirs(args.output_dir, exist_ok=True) - - # if args.push_to_hub: - # repo_id = create_repo( - # repo_id=args.hub_model_id or Path(args.output_dir).name, exist_ok=True, token=args.hub_token - # ).repo_id - - # For mixed precision training we cast all non-trainable weigths to half-precision - # as these weights are only used for inference, keeping weights in full precision is not required. - weight_dtype = torch.float32 - if accelerator.mixed_precision == "fp16": - weight_dtype = torch.float16 - elif accelerator.mixed_precision == "bf16": - weight_dtype = torch.bfloat16 - - # Create model: - diffusion = create_diffusion(timestep_respacing="") # default: 1000 steps, linear noise schedule - kwargs = {} - ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() - if args.enable_tiling: - ae.vae.enable_tiling() - ae.vae.tile_overlap_factor = args.tile_overlap_factor - - kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} - text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() - - ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) - assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" - args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w - args.ae_stride = args.ae_stride_h - patch_size = args.model[-3:] - patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) - args.patch_size = patch_size_h - args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w - assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" - # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." - assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." - assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." - - - args.stride_t = ae_stride_t * patch_size_t - args.stride = ae_stride_h * patch_size_h - latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) - ae.latent_size = latent_size - - if args.num_frames % 2 == 1: - args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 - else: - latent_size_t = args.num_frames // ae_stride_t - model = Diffusion_models[args.model]( - in_channels=ae_channel_config[args.ae], - out_channels=ae_channel_config[args.ae], - # caption_channels=4096, - # cross_attention_dim=1152, - attention_bias=True, - sample_size=latent_size, - sample_size_t=latent_size_t, - num_vector_embeds=None, - activation_fn="gelu-approximate", - num_embeds_ada_norm=1000, - use_linear_projection=False, - only_cross_attention=False, - double_self_attention=False, - upcast_attention=False, - # norm_type="ada_norm_single", - norm_elementwise_affine=False, - norm_eps=1e-6, - attention_type='default', - attention_mode=args.attention_mode, - interpolation_scale_h=args.interpolation_scale_h, - interpolation_scale_w=args.interpolation_scale_w, - interpolation_scale_t=args.interpolation_scale_t, - downsampler=args.downsampler, - # compress_kv_factor=args.compress_kv_factor, - # use_rope=args.use_rope, - # model_max_length=args.model_max_length, - ) - model.gradient_checkpointing = args.gradient_checkpointing - - # # use pretrained model? - if args.pretrained: - if 'safetensors' in args.pretrained: # pixart series - from safetensors.torch import load_file as safe_load - # import ipdb;ipdb.set_trace() - checkpoint = safe_load(args.pretrained, device="cpu") - if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: - logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") - repeat = model.pos_embed.proj.weight.shape[2] - checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) - # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] - else: # latest stage training weight - checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] - model_state_dict = model.state_dict() - missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) - logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') - logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') - - # Freeze vae and text encoders. - ae.vae.requires_grad_(False) - text_enc.requires_grad_(False) - # Set model as trainable. - model.train() - - # Move unet, vae and text_encoder to device and cast to weight_dtype - # The VAE is in float32 to avoid NaN losses. - # ae.to(accelerator.device, dtype=torch.float32) - ae.vae.to(accelerator.device, dtype=weight_dtype) - # ae.to(accelerator.device) - text_enc.to(accelerator.device, dtype=weight_dtype) - # text_enc.to(accelerator.device) - - # Create EMA for the unet. - if args.use_ema: - ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, - model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) - - # `accelerate` 0.16.0 will have better support for customized saving - if version.parse(accelerate.__version__) >= version.parse("0.16.0"): - # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format - def save_model_hook(models, weights, output_dir): - if accelerator.is_main_process: - if args.use_ema: - ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) - - for i, model in enumerate(models): - model.save_pretrained(os.path.join(output_dir, "model")) - if weights: # Don't pop if empty - # make sure to pop weight so that corresponding model is not saved again - weights.pop() - - def load_model_hook(models, input_dir): - if args.use_ema: - load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) - ema_model.load_state_dict(load_model.state_dict()) - ema_model.to(accelerator.device) - del load_model - - for i in range(len(models)): - # pop models so that they are not loaded again - model = models.pop() - - # load diffusers style into model - load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") - model.register_to_config(**load_model.config) - - model.load_state_dict(load_model.state_dict()) - del load_model - - accelerator.register_save_state_pre_hook(save_model_hook) - accelerator.register_load_state_pre_hook(load_model_hook) - - # Enable TF32 for faster training on Ampere GPUs, - # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices - if args.allow_tf32: - torch.backends.cuda.matmul.allow_tf32 = True - - if args.scale_lr: - args.learning_rate = ( - args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes - ) - - # Use 8-bit Adam for lower memory usage or to fine-tune the model in 16GB GPUs - if args.use_8bit_adam: - try: - import bitsandbytes as bnb - except ImportError: - raise ImportError( - "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." - ) - - optimizer_class = bnb.optim.AdamW8bit - else: - optimizer_class = torch.optim.AdamW - - # Optimizer creation - params_to_optimize = model.parameters() - optimizer = optimizer_class( - params_to_optimize, - lr=args.learning_rate, - betas=(args.adam_beta1, args.adam_beta2), - weight_decay=args.adam_weight_decay, - eps=args.adam_epsilon, - ) - - # Setup data: - train_dataset = getdataset(args) - train_dataloader = torch.utils.data.DataLoader( - train_dataset, - shuffle=True, - pin_memory=True, - collate_fn=Collate(args), - batch_size=args.train_batch_size, - num_workers=args.dataloader_num_workers, - ) - - # Scheduler and math around the number of training steps. - overrode_max_train_steps = False - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if args.max_train_steps is None: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - overrode_max_train_steps = True - - lr_scheduler = get_scheduler( - args.lr_scheduler, - optimizer=optimizer, - num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, - num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, - ) - - # Prepare everything with our `accelerator`. - model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( - model, optimizer, train_dataloader, lr_scheduler - ) - if args.use_ema: - ema_model.to(accelerator.device) - - # We need to recalculate our total training steps as the size of the training dataloader may have changed. - num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) - if overrode_max_train_steps: - args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch - # Afterwards we recalculate our number of training epochs - args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) - - # We need to initialize the trackers we use, and also store our configuration. - # The trackers initializes automatically on the main process. - if accelerator.is_main_process: - accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) - - # Train! - total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps - - logger.info("***** Running training *****") - logger.info(f" Model = {model}") - logger.info(f" Num examples = {len(train_dataset)}") - logger.info(f" Num Epochs = {args.num_train_epochs}") - logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") - logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") - logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") - logger.info(f" Total optimization steps = {args.max_train_steps}") - logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") - global_step = 0 - first_epoch = 0 - - # Potentially load in the weights and states from a previous save - if args.resume_from_checkpoint: - if args.resume_from_checkpoint != "latest": - path = os.path.basename(args.resume_from_checkpoint) - else: - # Get the most recent checkpoint - dirs = os.listdir(args.output_dir) - dirs = [d for d in dirs if d.startswith("checkpoint")] - dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) - path = dirs[-1] if len(dirs) > 0 else None - - if path is None: - accelerator.print( - f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." - ) - args.resume_from_checkpoint = None - initial_global_step = 0 - else: - accelerator.print(f"Resuming from checkpoint {path}") - accelerator.load_state(os.path.join(args.output_dir, path)) - global_step = int(path.split("-")[1]) - - initial_global_step = global_step - first_epoch = global_step // num_update_steps_per_epoch - - if npu_config is not None: - train_dataset.n_used_elements = global_step * args.train_batch_size - - else: - initial_global_step = 0 - - progress_bar = tqdm( - range(0, args.max_train_steps), - initial=initial_global_step, - desc="Steps", - # Only show the progress bar once on each machine. - disable=not accelerator.is_local_main_process, - ) - progress_info = ProgressInfo(global_step, train_loss=0.0) - - def sync_gradients_info(loss): - # Checks if the accelerator has performed an optimization step behind the scenes - if args.use_ema: - ema_model.step(model.parameters()) - progress_bar.update(1) - progress_info.global_step += 1 - end_time = time.time() - one_step_duration = end_time - start_time - accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) - if torch_npu is not None and npu_config is not None: - npu_config.print_msg( - f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", - rank=0) - progress_info.train_loss = 0.0 - - if args.use_deepspeed or accelerator.is_main_process: - if progress_info.global_step % args.checkpointing_steps == 0: - # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` - if args.checkpoints_total_limit is not None: - checkpoints = os.listdir(args.output_dir) - checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] - checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) - - # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints - if len(checkpoints) >= args.checkpoints_total_limit: - num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 - removing_checkpoints = checkpoints[0:num_to_remove] - - logger.info( - f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" - ) - logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") - - for removing_checkpoint in removing_checkpoints: - removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) - shutil.rmtree(removing_checkpoint) - - save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") - accelerator.save_state(save_path) - logger.info(f"Saved state to {save_path}") - - logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} - progress_bar.set_postfix(**logs) - - def run(latents, model_kwargs, prof, P_std=0.6, P_mean=1.7): - global start_time - start_time = time.time() - - # Add noise to the latents according to the noise magnitude at each timestep - # (this is the forward diffusion process) #[bsz, f, c, h , w] - bsz = latents.shape[0] - rnd_normal = torch.randn([bsz, 1, 1, 1, 1], device=latents.device) - sigma = (rnd_normal * P_std + P_mean).exp() - c_skip = 1 / (sigma**2 + 1) - c_out = -sigma / (sigma**2 + 1) ** 0.5 - c_in = 1 / (sigma**2 + 1) ** 0.5 - c_noise = (sigma.log() / 4).reshape([bsz]) - loss_weight = (sigma ** 2 + 1) / sigma ** 2 - - noisy_latents = c_in * (latents + torch.randn_like(latents) * sigma) - - model_pred = model( - noisy_latents, - c_noise, - **model_kwargs - )[0] - - predict_x0 = c_out * model_pred + c_skip * noisy_latents - loss = ((predict_x0 - latents)**2 * loss_weight).mean() - - # Gather the losses across all processes for logging (if we use distributed training). - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps - - # Backpropagate - accelerator.backward(loss) - if accelerator.sync_gradients: - params_to_clip = model.parameters() - accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) - optimizer.step() - lr_scheduler.step() - optimizer.zero_grad() - if prof is not None: - prof.step() - - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps - - if accelerator.sync_gradients: - sync_gradients_info(loss) - - if accelerator.is_main_process: - for tracker in accelerator.trackers: - if tracker.name == "wandb": - if progress_info.global_step % args.checkpointing_steps != 0: - if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): - tracker.log( - {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) - elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): - tracker.log( - {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) - - if progress_info.global_step % args.checkpointing_steps == 0: - - if args.enable_tracker: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step) - - if args.use_ema: - # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - if npu_config is None: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step, ema=True) - # Switch back to the original UNet parameters. - ema_model.restore(model.parameters()) - - return loss - def train_one_step(step_, data_item_, prof_=None): - train_loss = 0.0 - x, attn_mask, input_ids, cond_mask = data_item_ - # Sample noise that we'll add to the latents - - if not args.multi_scale: - assert torch.all(attn_mask) - x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 - attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W - input_ids = input_ids.to(accelerator.device) # B 1+num_images L - cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L - # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) - - with torch.no_grad(): - # import ipdb;ipdb.set_trace() - # use for loop to avoid OOM, because T5 is too huge... - B, N, L = input_ids.shape # B 1+num_images L - # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D - - # use batch inference - input_ids_ = input_ids.reshape(-1, L) - cond_mask_ = cond_mask.reshape(-1, L) - cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D - cond = cond.reshape(B, N, L, -1) - - # Map input images to latent space + normalize latents - if args.use_image_num == 0: - x = ae.encode(x) # B C T H W - else: - videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] - videos = ae.encode(videos) # B C T H W - images = rearrange(images, 'b c t h w -> (b t) c 1 h w') - images = ae.encode(images) - images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) - x = torch.cat([videos, images], dim=2) # b c 17+4, h, w - - with accelerator.accumulate(model): - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) - run(x, model_kwargs, prof_) - - if progress_info.global_step >= args.max_train_steps: - return True - - return False - - def train_all_epoch(prof_=None): - for epoch in range(first_epoch, args.num_train_epochs): - progress_info.train_loss = 0.0 - if progress_info.global_step >= args.max_train_steps: - return True - - for step, data_item in enumerate(train_dataloader): - - if train_one_step(step, data_item, prof_): - break - - if step >= 2 and torch_npu is not None and npu_config is not None: - npu_config.free_mm() - - if npu_config is not None and npu_config.on_npu and npu_config.profiling: - experimental_config = torch_npu.profiler._ExperimentalConfig( - profiler_level=torch_npu.profiler.ProfilerLevel.Level1, - aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization - ) - profile_output_path = f"/home/image_data/shebin/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" - os.makedirs(profile_output_path, exist_ok=True) - - with torch_npu.profiler.profile( - activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], - with_stack=True, - record_shapes=True, - profile_memory=True, - experimental_config=experimental_config, - schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, - skip_first=0), - on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") - ) as prof: - train_all_epoch(prof) - else: - train_all_epoch() - accelerator.wait_for_everyone() - accelerator.end_training() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--dataset", type=str, required=True) - parser.add_argument("--video_data", type=str, required='') - parser.add_argument("--image_data", type=str, default='') - parser.add_argument("--sample_rate", type=int, default=1) - parser.add_argument("--num_frames", type=int, default=65) - parser.add_argument("--max_height", type=int, default=320) - parser.add_argument("--max_width", type=int, default=240) - parser.add_argument("--use_img_from_vid", action="store_true") - parser.add_argument("--use_image_num", type=int, default=0) - parser.add_argument("--model_max_length", type=int, default=512) - - parser.add_argument('--enable_8bit_t5', action='store_true') - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) - parser.add_argument('--enable_tiling', action='store_true') - parser.add_argument("--compress_kv", action="store_true") - parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") - parser.add_argument('--use_rope', action='store_true') - parser.add_argument('--compress_kv_factor', type=int, default=1) - parser.add_argument('--interpolation_scale_h', type=float, default=1.0) - parser.add_argument('--interpolation_scale_w', type=float, default=1.0) - parser.add_argument('--interpolation_scale_t', type=float, default=1.0) - parser.add_argument('--cfg', type=float, default=0.1) - parser.add_argument("--ema_start_step", type=int, default=0) - parser.add_argument("--downsampler", type=str, default=None) - - parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") - parser.add_argument("--pretrained", type=str, default=None) - parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") - parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') - parser.add_argument("--cache_dir", type=str, default='./cache_dir') - - parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=5.0) - parser.add_argument("--multi_scale", action="store_true") - parser.add_argument("--enable_tracker", action="store_true") - parser.add_argument("--use_deepspeed", action="store_true") - parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") - parser.add_argument( - "--output_dir", - type=str, - default=None, - help="The output directory where the model predictions and checkpoints will be written.", - ) - parser.add_argument( - "--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader." - ) - parser.add_argument("--num_train_epochs", type=int, default=100) - parser.add_argument( - "--max_train_steps", - type=int, - default=None, - help="Total number of training steps to perform. If provided, overrides num_train_epochs.", - ) - parser.add_argument( - "--checkpointing_steps", - type=int, - default=500, - help=( - "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" - " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" - " training using `--resume_from_checkpoint`." - ), - ) - parser.add_argument( - "--checkpoints_total_limit", - type=int, - default=None, - help=("Max number of checkpoints to store."), - ) - parser.add_argument( - "--resume_from_checkpoint", - type=str, - default=None, - help=( - "Whether training should be resumed from a previous checkpoint. Use a path saved by" - ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' - ), - ) - parser.add_argument( - "--gradient_accumulation_steps", - type=int, - default=1, - help="Number of updates steps to accumulate before performing a backward/update pass.", - ) - parser.add_argument( - "--gradient_checkpointing", - action="store_true", - help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.", - ) - parser.add_argument( - "--learning_rate", - type=float, - default=1e-4, - help="Initial learning rate (after the potential warmup period) to use.", - ) - parser.add_argument( - "--scale_lr", - action="store_true", - default=False, - help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.", - ) - parser.add_argument( - "--lr_scheduler", - type=str, - default="constant", - help=( - 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' - ' "constant", "constant_with_warmup"]' - ), - ) - parser.add_argument( - "--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler." - ) - parser.add_argument( - "--timestep_bias_strategy", - type=str, - default="none", - choices=["earlier", "later", "range", "none"], - help=( - "The timestep bias strategy, which may help direct the model toward learning low or high frequency details." - " Choices: ['earlier', 'later', 'range', 'none']." - " The default is 'none', which means no bias is applied, and training proceeds normally." - " The value of 'later' will increase the frequency of the model's final training timesteps." - ), - ) - parser.add_argument( - "--timestep_bias_multiplier", - type=float, - default=1.0, - help=( - "The multiplier for the bias. Defaults to 1.0, which means no bias is applied." - " A value of 2.0 will double the weight of the bias, and a value of 0.5 will halve it." - ), - ) - parser.add_argument( - "--timestep_bias_begin", - type=int, - default=0, - help=( - "When using `--timestep_bias_strategy=range`, the beginning (inclusive) timestep to bias." - " Defaults to zero, which equates to having no specific bias." - ), - ) - parser.add_argument( - "--timestep_bias_end", - type=int, - default=1000, - help=( - "When using `--timestep_bias_strategy=range`, the final timestep (inclusive) to bias." - " Defaults to 1000, which is the number of timesteps that Stable Diffusion is trained on." - ), - ) - parser.add_argument( - "--timestep_bias_portion", - type=float, - default=0.25, - help=( - "The portion of timesteps to bias. Defaults to 0.25, which 25% of timesteps will be biased." - " A value of 0.5 will bias one half of the timesteps. The value provided for `--timestep_bias_strategy` determines" - " whether the biased portions are in the earlier or later timesteps." - ), - ) - parser.add_argument( - "--snr_gamma", - type=float, - default=None, - help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. " - "More details here: https://arxiv.org/abs/2303.09556.", - ) - parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") - parser.add_argument( - "--allow_tf32", - action="store_true", - help=( - "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" - " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" - ), - ) - parser.add_argument( - "--dataloader_num_workers", - type=int, - default=10, - help=( - "Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process." - ), - ) - parser.add_argument( - "--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes." - ) - parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam optimizer.") - parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam optimizer.") - parser.add_argument("--adam_weight_decay", type=float, default=1e-2, help="Weight decay to use.") - parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer") - parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") - parser.add_argument("--push_to_hub", action="store_true", help="Whether or not to push the model to the Hub.") - parser.add_argument("--hub_token", type=str, default=None, help="The token to use to push to the Model Hub.") - parser.add_argument( - "--prediction_type", - type=str, - default=None, - help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.", - ) - parser.add_argument( - "--hub_model_id", - type=str, - default=None, - help="The name of the repository to keep in sync with the local `output_dir`.", - ) - parser.add_argument( - "--logging_dir", - type=str, - default="logs", - help=( - "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" - " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." - ), - ) - parser.add_argument( - "--report_to", - type=str, - default="tensorboard", - help=( - 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' - ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' - ), - ) - parser.add_argument( - "--mixed_precision", - type=str, - default=None, - choices=["no", "fp16", "bf16"], - help=( - "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" - " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" - " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." - ), - ) - parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") - parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") - - args = parser.parse_args() - main(args) diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 1cc016d25..b5920b3bb 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -114,10 +114,10 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p # pad to max multiple of ds_stride batch_input_size = [i.shape for i in batch_tubes] # [(c t h w), (c t h w)] max_t, max_h, max_w = max_thw - pad_max_t, pad_max_h, pad_max_w = pad_to_multiple(max_t-1 if extra_1 else max_t, t_ds_stride), \ + pad_max_t, pad_max_h, pad_max_w = pad_to_multiple(max_t-1+self.ae_stride_t if extra_1 else max_t, t_ds_stride), \ pad_to_multiple(max_h, ds_stride), \ pad_to_multiple(max_w, ds_stride) - pad_max_t = pad_max_t + 1 if extra_1 else pad_max_t + pad_max_t = pad_max_t + 1 - self.ae_stride_t if extra_1 else pad_max_t each_pad_t_h_w = [[pad_max_t - i.shape[1], pad_max_h - i.shape[2], pad_max_w - i.shape[3]] for i in batch_tubes] diff --git a/opensora/utils/diffusion/__init__.py b/opensora/utils/diffusion/__init__.py deleted file mode 100644 index 04b2bd3d8..000000000 --- a/opensora/utils/diffusion/__init__.py +++ /dev/null @@ -1,87 +0,0 @@ -# Modified from OpenAI's diffusion repos -# GLIDE: https://github.com/openai/glide-text2im/blob/main/glide_text2im/gaussian_diffusion.py -# ADM: https://github.com/openai/guided-diffusion/blob/main/guided_diffusion -# IDDPM: https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py - -from .respace import SpacedDiffusion, space_timesteps, SpacedDiffusion_T - - -def create_diffusion( - timestep_respacing, - noise_schedule="linear", - use_kl=False, - sigma_small=False, - predict_xstart=False, - learn_sigma=True, - # learn_sigma=False, - rescale_learned_sigmas=False, - diffusion_steps=1000 -): - from . import gaussian_diffusion as gd - betas = gd.get_named_beta_schedule(noise_schedule, diffusion_steps) - if use_kl: - loss_type = gd.LossType.RESCALED_KL - elif rescale_learned_sigmas: - loss_type = gd.LossType.RESCALED_MSE - else: - loss_type = gd.LossType.MSE - if timestep_respacing is None or timestep_respacing == "": - timestep_respacing = [diffusion_steps] - return SpacedDiffusion( - use_timesteps=space_timesteps(diffusion_steps, timestep_respacing), - betas=betas, - model_mean_type=( - gd.ModelMeanType.EPSILON if not predict_xstart else gd.ModelMeanType.START_X - ), - model_var_type=( - ( - gd.ModelVarType.FIXED_LARGE - if not sigma_small - else gd.ModelVarType.FIXED_SMALL - ) - if not learn_sigma - else gd.ModelVarType.LEARNED_RANGE - ), - loss_type=loss_type - # rescale_timesteps=rescale_timesteps, - ) - -def create_diffusion_T( - timestep_respacing, - noise_schedule="linear", - use_kl=False, - sigma_small=False, - predict_xstart=False, - learn_sigma=True, - # learn_sigma=False, - rescale_learned_sigmas=False, - diffusion_steps=1000 -): - from . import gaussian_diffusion_t2v as gd - betas = gd.get_named_beta_schedule(noise_schedule, diffusion_steps) - if use_kl: - loss_type = gd.LossType.RESCALED_KL - elif rescale_learned_sigmas: - loss_type = gd.LossType.RESCALED_MSE - else: - loss_type = gd.LossType.MSE - if timestep_respacing is None or timestep_respacing == "": - timestep_respacing = [diffusion_steps] - return SpacedDiffusion_T( - use_timesteps=space_timesteps(diffusion_steps, timestep_respacing), - betas=betas, - model_mean_type=( - gd.ModelMeanType.EPSILON if not predict_xstart else gd.ModelMeanType.START_X - ), - model_var_type=( - ( - gd.ModelVarType.FIXED_LARGE - if not sigma_small - else gd.ModelVarType.FIXED_SMALL - ) - if not learn_sigma - else gd.ModelVarType.LEARNED_RANGE - ), - loss_type=loss_type - # rescale_timesteps=rescale_timesteps, - ) diff --git a/opensora/utils/diffusion/diffusion_utils.py b/opensora/utils/diffusion/diffusion_utils.py deleted file mode 100644 index 2a68fc9a9..000000000 --- a/opensora/utils/diffusion/diffusion_utils.py +++ /dev/null @@ -1,111 +0,0 @@ -# Modified from OpenAI's diffusion repos -# GLIDE: https://github.com/openai/glide-text2im/blob/main/glide_text2im/gaussian_diffusion.py -# ADM: https://github.com/openai/guided-diffusion/blob/main/guided_diffusion -# IDDPM: https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py - -import torch as th -import numpy as np - -def compute_snr(timesteps, alphas_cumprod): - """ - Computes SNR as per https://github.com/TiankaiHang/Min-SNR-Diffusion-Training/blob/521b624bd70c67cee4bdf49225915f5945a872e3/guided_diffusion/gaussian_diffusion.py#L847-L849 - """ - - sqrt_alphas_cumprod = alphas_cumprod**0.5 - sqrt_one_minus_alphas_cumprod = (1.0 - alphas_cumprod) ** 0.5 - - # Expand the tensors. - # Adapted from https://github.com/TiankaiHang/Min-SNR-Diffusion-Training/blob/521b624bd70c67cee4bdf49225915f5945a872e3/guided_diffusion/gaussian_diffusion.py#L1026 - sqrt_alphas_cumprod = sqrt_alphas_cumprod.to(device=timesteps.device)[timesteps].float() - while len(sqrt_alphas_cumprod.shape) < len(timesteps.shape): - sqrt_alphas_cumprod = sqrt_alphas_cumprod[..., None] - alpha = sqrt_alphas_cumprod.expand(timesteps.shape) - - sqrt_one_minus_alphas_cumprod = sqrt_one_minus_alphas_cumprod.to(device=timesteps.device)[timesteps].float() - while len(sqrt_one_minus_alphas_cumprod.shape) < len(timesteps.shape): - sqrt_one_minus_alphas_cumprod = sqrt_one_minus_alphas_cumprod[..., None] - sigma = sqrt_one_minus_alphas_cumprod.expand(timesteps.shape) - - # Compute SNR. - snr = (alpha / sigma) ** 2 - return snr - -def normal_kl(mean1, logvar1, mean2, logvar2): - """ - Compute the KL divergence between two gaussians. - Shapes are automatically broadcasted, so batches can be compared to - scalars, among other use cases. - """ - tensor = None - for obj in (mean1, logvar1, mean2, logvar2): - if isinstance(obj, th.Tensor): - tensor = obj - break - assert tensor is not None, "at least one argument must be a Tensor" - - # Force variances to be Tensors. Broadcasting helps convert scalars to - # Tensors, but it does not work for th.exp(). - logvar1, logvar2 = [ - x if isinstance(x, th.Tensor) else th.tensor(x).to(tensor) - for x in (logvar1, logvar2) - ] - - return 0.5 * ( - -1.0 - + logvar2 - - logvar1 - + th.exp(logvar1 - logvar2) - + ((mean1 - mean2) ** 2) * th.exp(-logvar2) - ) - - -def approx_standard_normal_cdf(x): - """ - A fast approximation of the cumulative distribution function of the - standard normal. - """ - return 0.5 * (1.0 + th.tanh(np.sqrt(2.0 / np.pi) * (x + 0.044715 * th.pow(x, 3)))) - - -def continuous_gaussian_log_likelihood(x, *, means, log_scales): - """ - Compute the log-likelihood of a continuous Gaussian distribution. - :param x: the targets - :param means: the Gaussian mean Tensor. - :param log_scales: the Gaussian log stddev Tensor. - :return: a tensor like x of log probabilities (in nats). - """ - centered_x = x - means - inv_stdv = th.exp(-log_scales) - normalized_x = centered_x * inv_stdv - log_probs = th.distributions.Normal(th.zeros_like(x), th.ones_like(x)).log_prob(normalized_x) - return log_probs - - -def discretized_gaussian_log_likelihood(x, *, means, log_scales): - """ - Compute the log-likelihood of a Gaussian distribution discretizing to a - given image. - :param x: the target images. It is assumed that this was uint8 values, - rescaled to the range [-1, 1]. - :param means: the Gaussian mean Tensor. - :param log_scales: the Gaussian log stddev Tensor. - :return: a tensor like x of log probabilities (in nats). - """ - assert x.shape == means.shape == log_scales.shape - centered_x = x - means - inv_stdv = th.exp(-log_scales) - plus_in = inv_stdv * (centered_x + 1.0 / 255.0) - cdf_plus = approx_standard_normal_cdf(plus_in) - min_in = inv_stdv * (centered_x - 1.0 / 255.0) - cdf_min = approx_standard_normal_cdf(min_in) - log_cdf_plus = th.log(cdf_plus.clamp(min=1e-12)) - log_one_minus_cdf_min = th.log((1.0 - cdf_min).clamp(min=1e-12)) - cdf_delta = cdf_plus - cdf_min - log_probs = th.where( - x < -0.999, - log_cdf_plus, - th.where(x > 0.999, log_one_minus_cdf_min, th.log(cdf_delta.clamp(min=1e-12))), - ) - assert log_probs.shape == x.shape - return log_probs diff --git a/opensora/utils/diffusion/gaussian_diffusion.py b/opensora/utils/diffusion/gaussian_diffusion.py deleted file mode 100644 index 7fc3d43a7..000000000 --- a/opensora/utils/diffusion/gaussian_diffusion.py +++ /dev/null @@ -1,881 +0,0 @@ -# Modified from OpenAI's diffusion repos -# GLIDE: https://github.com/openai/glide-text2im/blob/main/glide_text2im/gaussian_diffusion.py -# ADM: https://github.com/openai/guided-diffusion/blob/main/guided_diffusion -# IDDPM: https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py - - -import math - -import numpy as np -import torch as th -import enum - -from .diffusion_utils import discretized_gaussian_log_likelihood, normal_kl - - -def mean_flat(tensor): - """ - Take the mean over all non-batch dimensions. - """ - return tensor.mean(dim=list(range(1, len(tensor.shape)))) - - -class ModelMeanType(enum.Enum): - """ - Which type of output the model predicts. - """ - - PREVIOUS_X = enum.auto() # the model predicts x_{t-1} - START_X = enum.auto() # the model predicts x_0 - EPSILON = enum.auto() # the model predicts epsilon - - -class ModelVarType(enum.Enum): - """ - What is used as the model's output variance. - The LEARNED_RANGE option has been added to allow the model to predict - values between FIXED_SMALL and FIXED_LARGE, making its job easier. - """ - - LEARNED = enum.auto() - FIXED_SMALL = enum.auto() - FIXED_LARGE = enum.auto() - LEARNED_RANGE = enum.auto() - - -class LossType(enum.Enum): - MSE = enum.auto() # use raw MSE loss (and KL when learning variances) - RESCALED_MSE = ( - enum.auto() - ) # use raw MSE loss (with RESCALED_KL when learning variances) - KL = enum.auto() # use the variational lower-bound - RESCALED_KL = enum.auto() # like KL, but rescale to estimate the full VLB - - def is_vb(self): - return self == LossType.KL or self == LossType.RESCALED_KL - - -def _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, warmup_frac): - betas = beta_end * np.ones(num_diffusion_timesteps, dtype=np.float64) - warmup_time = int(num_diffusion_timesteps * warmup_frac) - betas[:warmup_time] = np.linspace(beta_start, beta_end, warmup_time, dtype=np.float64) - return betas - - -def get_beta_schedule(beta_schedule, *, beta_start, beta_end, num_diffusion_timesteps): - """ - This is the deprecated API for creating beta schedules. - See get_named_beta_schedule() for the new library of schedules. - """ - if beta_schedule == "quad": - betas = ( - np.linspace( - beta_start ** 0.5, - beta_end ** 0.5, - num_diffusion_timesteps, - dtype=np.float64, - ) - ** 2 - ) - elif beta_schedule == "linear": - betas = np.linspace(beta_start, beta_end, num_diffusion_timesteps, dtype=np.float64) - elif beta_schedule == "warmup10": - betas = _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, 0.1) - elif beta_schedule == "warmup50": - betas = _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, 0.5) - elif beta_schedule == "const": - betas = beta_end * np.ones(num_diffusion_timesteps, dtype=np.float64) - elif beta_schedule == "jsd": # 1/T, 1/(T-1), 1/(T-2), ..., 1 - betas = 1.0 / np.linspace( - num_diffusion_timesteps, 1, num_diffusion_timesteps, dtype=np.float64 - ) - else: - raise NotImplementedError(beta_schedule) - assert betas.shape == (num_diffusion_timesteps,) - return betas - - -def get_named_beta_schedule(schedule_name, num_diffusion_timesteps): - """ - Get a pre-defined beta schedule for the given name. - The beta schedule library consists of beta schedules which remain similar - in the limit of num_diffusion_timesteps. - Beta schedules may be added, but should not be removed or changed once - they are committed to maintain backwards compatibility. - """ - if schedule_name == "linear": - # Linear schedule from Ho et al, extended to work for any number of - # diffusion steps. - scale = 1000 / num_diffusion_timesteps - return get_beta_schedule( - "linear", - beta_start=scale * 0.0001, - beta_end=scale * 0.02, - num_diffusion_timesteps=num_diffusion_timesteps, - ) - elif schedule_name == "squaredcos_cap_v2": - return betas_for_alpha_bar( - num_diffusion_timesteps, - lambda t: math.cos((t + 0.008) / 1.008 * math.pi / 2) ** 2, - ) - else: - raise NotImplementedError(f"unknown beta schedule: {schedule_name}") - - -def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar, max_beta=0.999): - """ - Create a beta schedule that discretizes the given alpha_t_bar function, - which defines the cumulative product of (1-beta) over time from t = [0,1]. - :param num_diffusion_timesteps: the number of betas to produce. - :param alpha_bar: a lambda that takes an argument t from 0 to 1 and - produces the cumulative product of (1-beta) up to that - part of the diffusion process. - :param max_beta: the maximum beta to use; use values lower than 1 to - prevent singularities. - """ - betas = [] - for i in range(num_diffusion_timesteps): - t1 = i / num_diffusion_timesteps - t2 = (i + 1) / num_diffusion_timesteps - betas.append(min(1 - alpha_bar(t2) / alpha_bar(t1), max_beta)) - return np.array(betas) - - -class GaussianDiffusion: - """ - Utilities for training and sampling diffusion models. - Original ported from this codebase: - https://github.com/hojonathanho/diffusion/blob/1e0dceb3b3495bbe19116a5e1b3596cd0706c543/diffusion_tf/diffusion_utils_2.py#L42 - :param betas: a 1-D numpy array of betas for each diffusion timestep, - starting at T and going to 1. - """ - - def __init__( - self, - *, - betas, - model_mean_type, - model_var_type, - loss_type - ): - - self.model_mean_type = model_mean_type - self.model_var_type = model_var_type - self.loss_type = loss_type - - # Use float64 for accuracy. - betas = np.array(betas, dtype=np.float64) - self.betas = betas - assert len(betas.shape) == 1, "betas must be 1-D" - assert (betas > 0).all() and (betas <= 1).all() - - self.num_timesteps = int(betas.shape[0]) - - alphas = 1.0 - betas - self.alphas_cumprod = np.cumprod(alphas, axis=0) - self.alphas_cumprod_prev = np.append(1.0, self.alphas_cumprod[:-1]) - self.alphas_cumprod_next = np.append(self.alphas_cumprod[1:], 0.0) - assert self.alphas_cumprod_prev.shape == (self.num_timesteps,) - - # calculations for diffusion q(x_t | x_{t-1}) and others - self.sqrt_alphas_cumprod = np.sqrt(self.alphas_cumprod) - self.sqrt_one_minus_alphas_cumprod = np.sqrt(1.0 - self.alphas_cumprod) - self.log_one_minus_alphas_cumprod = np.log(1.0 - self.alphas_cumprod) - self.sqrt_recip_alphas_cumprod = np.sqrt(1.0 / self.alphas_cumprod) - self.sqrt_recipm1_alphas_cumprod = np.sqrt(1.0 / self.alphas_cumprod - 1) - - # calculations for posterior q(x_{t-1} | x_t, x_0) - self.posterior_variance = ( - betas * (1.0 - self.alphas_cumprod_prev) / (1.0 - self.alphas_cumprod) - ) - # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain - self.posterior_log_variance_clipped = np.log( - np.append(self.posterior_variance[1], self.posterior_variance[1:]) - ) if len(self.posterior_variance) > 1 else np.array([]) - - self.posterior_mean_coef1 = ( - betas * np.sqrt(self.alphas_cumprod_prev) / (1.0 - self.alphas_cumprod) - ) - self.posterior_mean_coef2 = ( - (1.0 - self.alphas_cumprod_prev) * np.sqrt(alphas) / (1.0 - self.alphas_cumprod) - ) - - def q_mean_variance(self, x_start, t): - """ - Get the distribution q(x_t | x_0). - :param x_start: the [N x C x ...] tensor of noiseless inputs. - :param t: the number of diffusion steps (minus 1). Here, 0 means one step. - :return: A tuple (mean, variance, log_variance), all of x_start's shape. - """ - mean = _extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start - variance = _extract_into_tensor(1.0 - self.alphas_cumprod, t, x_start.shape) - log_variance = _extract_into_tensor(self.log_one_minus_alphas_cumprod, t, x_start.shape) - return mean, variance, log_variance - - def q_sample(self, x_start, t, noise=None): - """ - Diffuse the data for a given number of diffusion steps. - In other words, sample from q(x_t | x_0). - :param x_start: the initial data batch. - :param t: the number of diffusion steps (minus 1). Here, 0 means one step. - :param noise: if specified, the split-out normal noise. - :return: A noisy version of x_start. - """ - if noise is None: - noise = th.randn_like(x_start) - assert noise.shape == x_start.shape - return ( - _extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start - + _extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise - ) - - def q_posterior_mean_variance(self, x_start, x_t, t): - """ - Compute the mean and variance of the diffusion posterior: - q(x_{t-1} | x_t, x_0) - """ - assert x_start.shape == x_t.shape - posterior_mean = ( - _extract_into_tensor(self.posterior_mean_coef1, t, x_t.shape) * x_start - + _extract_into_tensor(self.posterior_mean_coef2, t, x_t.shape) * x_t - ) - posterior_variance = _extract_into_tensor(self.posterior_variance, t, x_t.shape) - posterior_log_variance_clipped = _extract_into_tensor( - self.posterior_log_variance_clipped, t, x_t.shape - ) - assert ( - posterior_mean.shape[0] - == posterior_variance.shape[0] - == posterior_log_variance_clipped.shape[0] - == x_start.shape[0] - ) - return posterior_mean, posterior_variance, posterior_log_variance_clipped - - def p_mean_variance(self, model, x, t, clip_denoised=True, denoised_fn=None, model_kwargs=None): - """ - Apply the model to get p(x_{t-1} | x_t), as well as a prediction of - the initial x, x_0. - :param model: the model, which takes a signal and a batch of timesteps - as input. - :param x: the [N x C x ...] tensor at time t. - :param t: a 1-D Tensor of timesteps. - :param clip_denoised: if True, clip the denoised signal into [-1, 1]. - :param denoised_fn: if not None, a function which applies to the - x_start prediction before it is used to sample. Applies before - clip_denoised. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :return: a dict with the following keys: - - 'mean': the model mean output. - - 'variance': the model variance output. - - 'log_variance': the log of 'variance'. - - 'pred_xstart': the prediction for x_0. - """ - if model_kwargs is None: - model_kwargs = {} - - B, F, C = x.shape[:3] - assert t.shape == (B,) - model_output = model(x, t, **model_kwargs) - # try: - # model_output = model_output.sample # for tav unet - # except: - # model_output = model(x, t, **model_kwargs) - if isinstance(model_output, tuple): - model_output, extra = model_output - else: - extra = None - - if self.model_var_type in [ModelVarType.LEARNED, ModelVarType.LEARNED_RANGE]: - assert model_output.shape == (B, F, C * 2, *x.shape[3:]) - model_output, model_var_values = th.split(model_output, C, dim=2) - min_log = _extract_into_tensor(self.posterior_log_variance_clipped, t, x.shape) - max_log = _extract_into_tensor(np.log(self.betas), t, x.shape) - # The model_var_values is [-1, 1] for [min_var, max_var]. - frac = (model_var_values + 1) / 2 - model_log_variance = frac * max_log + (1 - frac) * min_log - model_variance = th.exp(model_log_variance) - else: - model_variance, model_log_variance = { - # for fixedlarge, we set the initial (log-)variance like so - # to get a better decoder log likelihood. - ModelVarType.FIXED_LARGE: ( - np.append(self.posterior_variance[1], self.betas[1:]), - np.log(np.append(self.posterior_variance[1], self.betas[1:])), - ), - ModelVarType.FIXED_SMALL: ( - self.posterior_variance, - self.posterior_log_variance_clipped, - ), - }[self.model_var_type] - model_variance = _extract_into_tensor(model_variance, t, x.shape) - model_log_variance = _extract_into_tensor(model_log_variance, t, x.shape) - - def process_xstart(x): - if denoised_fn is not None: - x = denoised_fn(x) - if clip_denoised: - return x.clamp(-1, 1) - return x - - if self.model_mean_type == ModelMeanType.START_X: - pred_xstart = process_xstart(model_output) - else: - pred_xstart = process_xstart( - self._predict_xstart_from_eps(x_t=x, t=t, eps=model_output) - ) - model_mean, _, _ = self.q_posterior_mean_variance(x_start=pred_xstart, x_t=x, t=t) - - assert model_mean.shape == model_log_variance.shape == pred_xstart.shape == x.shape - return { - "mean": model_mean, - "variance": model_variance, - "log_variance": model_log_variance, - "pred_xstart": pred_xstart, - "extra": extra, - } - - def _predict_xstart_from_eps(self, x_t, t, eps): - assert x_t.shape == eps.shape - return ( - _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - - _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * eps - ) - - def _predict_eps_from_xstart(self, x_t, t, pred_xstart): - return ( - _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - pred_xstart - ) / _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) - - def condition_mean(self, cond_fn, p_mean_var, x, t, model_kwargs=None): - """ - Compute the mean for the previous step, given a function cond_fn that - computes the gradient of a conditional log probability with respect to - x. In particular, cond_fn computes grad(log(p(y|x))), and we want to - condition on y. - This uses the conditioning strategy from Sohl-Dickstein et al. (2015). - """ - gradient = cond_fn(x, t, **model_kwargs) - new_mean = p_mean_var["mean"].float() + p_mean_var["variance"] * gradient.float() - return new_mean - - def condition_score(self, cond_fn, p_mean_var, x, t, model_kwargs=None): - """ - Compute what the p_mean_variance output would have been, should the - model's score function be conditioned by cond_fn. - See condition_mean() for details on cond_fn. - Unlike condition_mean(), this instead uses the conditioning strategy - from Song et al (2020). - """ - alpha_bar = _extract_into_tensor(self.alphas_cumprod, t, x.shape) - - eps = self._predict_eps_from_xstart(x, t, p_mean_var["pred_xstart"]) - eps = eps - (1 - alpha_bar).sqrt() * cond_fn(x, t, **model_kwargs) - - out = p_mean_var.copy() - out["pred_xstart"] = self._predict_xstart_from_eps(x, t, eps) - out["mean"], _, _ = self.q_posterior_mean_variance(x_start=out["pred_xstart"], x_t=x, t=t) - return out - - def p_sample( - self, - model, - x, - t, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - ): - """ - Sample x_{t-1} from the model at the given timestep. - :param model: the model to sample from. - :param x: the current tensor at x_{t-1}. - :param t: the value of t, starting at 0 for the first diffusion step. - :param clip_denoised: if True, clip the x_start prediction to [-1, 1]. - :param denoised_fn: if not None, a function which applies to the - x_start prediction before it is used to sample. - :param cond_fn: if not None, this is a gradient function that acts - similarly to the model. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :return: a dict containing the following keys: - - 'sample': a random sample from the model. - - 'pred_xstart': a prediction of x_0. - """ - out = self.p_mean_variance( - model, - x, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - model_kwargs=model_kwargs, - ) - noise = th.randn_like(x) - nonzero_mask = ( - (t != 0).float().view(-1, *([1] * (len(x.shape) - 1))) - ) # no noise when t == 0 - if cond_fn is not None: - out["mean"] = self.condition_mean(cond_fn, out, x, t, model_kwargs=model_kwargs) - sample = out["mean"] + nonzero_mask * th.exp(0.5 * out["log_variance"]) * noise - return {"sample": sample, "pred_xstart": out["pred_xstart"]} - - def p_sample_loop( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - ): - """ - Generate samples from the model. - :param model: the model module. - :param shape: the shape of the samples, (N, C, H, W). - :param noise: if specified, the noise from the encoder to sample. - Should be of the same shape as `shape`. - :param clip_denoised: if True, clip x_start predictions to [-1, 1]. - :param denoised_fn: if not None, a function which applies to the - x_start prediction before it is used to sample. - :param cond_fn: if not None, this is a gradient function that acts - similarly to the model. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :param device: if specified, the device to create the samples on. - If not specified, use a model parameter's device. - :param progress: if True, show a tqdm progress bar. - :return: a non-differentiable batch of samples. - """ - final = None - for sample in self.p_sample_loop_progressive( - model, - shape, - noise=noise, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - device=device, - progress=progress, - ): - final = sample - return final["sample"] - - def p_sample_loop_progressive( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - ): - """ - Generate samples from the model and yield intermediate samples from - each timestep of diffusion. - Arguments are the same as p_sample_loop(). - Returns a generator over dicts, where each dict is the return value of - p_sample(). - """ - if device is None: - device = next(model.parameters()).device - assert isinstance(shape, (tuple, list)) - if noise is not None: - img = noise - else: - img = th.randn(*shape, device=device) - indices = list(range(self.num_timesteps))[::-1] - - if progress: - # Lazy import so that we don't depend on tqdm. - from tqdm.auto import tqdm - - indices = tqdm(indices) - - for i in indices: - t = th.tensor([i] * shape[0], device=device) - with th.no_grad(): - out = self.p_sample( - model, - img, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - ) - yield out - img = out["sample"] - - def ddim_sample( - self, - model, - x, - t, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - eta=0.0, - ): - """ - Sample x_{t-1} from the model using DDIM. - Same usage as p_sample(). - """ - out = self.p_mean_variance( - model, - x, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - model_kwargs=model_kwargs, - ) - if cond_fn is not None: - out = self.condition_score(cond_fn, out, x, t, model_kwargs=model_kwargs) - - # Usually our model outputs epsilon, but we re-derive it - # in case we used x_start or x_prev prediction. - eps = self._predict_eps_from_xstart(x, t, out["pred_xstart"]) - - alpha_bar = _extract_into_tensor(self.alphas_cumprod, t, x.shape) - alpha_bar_prev = _extract_into_tensor(self.alphas_cumprod_prev, t, x.shape) - sigma = ( - eta - * th.sqrt((1 - alpha_bar_prev) / (1 - alpha_bar)) - * th.sqrt(1 - alpha_bar / alpha_bar_prev) - ) - # Equation 12. - noise = th.randn_like(x) - mean_pred = ( - out["pred_xstart"] * th.sqrt(alpha_bar_prev) - + th.sqrt(1 - alpha_bar_prev - sigma ** 2) * eps - ) - nonzero_mask = ( - (t != 0).float().view(-1, *([1] * (len(x.shape) - 1))) - ) # no noise when t == 0 - sample = mean_pred + nonzero_mask * sigma * noise - return {"sample": sample, "pred_xstart": out["pred_xstart"]} - - def ddim_reverse_sample( - self, - model, - x, - t, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - eta=0.0, - ): - """ - Sample x_{t+1} from the model using DDIM reverse ODE. - """ - assert eta == 0.0, "Reverse ODE only for deterministic path" - out = self.p_mean_variance( - model, - x, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - model_kwargs=model_kwargs, - ) - if cond_fn is not None: - out = self.condition_score(cond_fn, out, x, t, model_kwargs=model_kwargs) - # Usually our model outputs epsilon, but we re-derive it - # in case we used x_start or x_prev prediction. - eps = ( - _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x.shape) * x - - out["pred_xstart"] - ) / _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x.shape) - alpha_bar_next = _extract_into_tensor(self.alphas_cumprod_next, t, x.shape) - - # Equation 12. reversed - mean_pred = out["pred_xstart"] * th.sqrt(alpha_bar_next) + th.sqrt(1 - alpha_bar_next) * eps - - return {"sample": mean_pred, "pred_xstart": out["pred_xstart"]} - - def ddim_sample_loop( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - eta=0.0, - ): - """ - Generate samples from the model using DDIM. - Same usage as p_sample_loop(). - """ - final = None - for sample in self.ddim_sample_loop_progressive( - model, - shape, - noise=noise, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - device=device, - progress=progress, - eta=eta, - ): - final = sample - return final["sample"] - - def ddim_sample_loop_progressive( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - eta=0.0, - ): - """ - Use DDIM to sample from the model and yield intermediate samples from - each timestep of DDIM. - Same usage as p_sample_loop_progressive(). - """ - if device is None: - device = next(model.parameters()).device - assert isinstance(shape, (tuple, list)) - if noise is not None: - img = noise - else: - img = th.randn(*shape, device=device) - indices = list(range(self.num_timesteps))[::-1] - - if progress: - # Lazy import so that we don't depend on tqdm. - from tqdm.auto import tqdm - - indices = tqdm(indices) - - for i in indices: - t = th.tensor([i] * shape[0], device=device) - with th.no_grad(): - out = self.ddim_sample( - model, - img, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - eta=eta, - ) - yield out - img = out["sample"] - - def _vb_terms_bpd( - self, model, x_start, x_t, t, clip_denoised=True, model_kwargs=None - ): - """ - Get a term for the variational lower-bound. - The resulting units are bits (rather than nats, as one might expect). - This allows for comparison to other papers. - :return: a dict with the following keys: - - 'output': a shape [N] tensor of NLLs or KLs. - - 'pred_xstart': the x_0 predictions. - """ - true_mean, _, true_log_variance_clipped = self.q_posterior_mean_variance( - x_start=x_start, x_t=x_t, t=t - ) - out = self.p_mean_variance( - model, x_t, t, clip_denoised=clip_denoised, model_kwargs=model_kwargs - ) - kl = normal_kl( - true_mean, true_log_variance_clipped, out["mean"], out["log_variance"] - ) - kl = mean_flat(kl) / np.log(2.0) - - decoder_nll = -discretized_gaussian_log_likelihood( - x_start, means=out["mean"], log_scales=0.5 * out["log_variance"] - ) - assert decoder_nll.shape == x_start.shape - decoder_nll = mean_flat(decoder_nll) / np.log(2.0) - - # At the first timestep return the decoder NLL, - # otherwise return KL(q(x_{t-1}|x_t,x_0) || p(x_{t-1}|x_t)) - output = th.where((t == 0), decoder_nll, kl) - return {"output": output, "pred_xstart": out["pred_xstart"]} - - def training_losses(self, model, x_start, t, model_kwargs=None, noise=None): - """ - Compute training losses for a single timestep. - :param model: the model to evaluate loss on. - :param x_start: the [N x C x ...] tensor of inputs. - :param t: a batch of timestep indices. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :param noise: if specified, the specific Gaussian noise to try to remove. - :return: a dict with the key "loss" containing a tensor of shape [N]. - Some mean or variance settings may also have other keys. - """ - if model_kwargs is None: - model_kwargs = {} - if noise is None: - noise = th.randn_like(x_start) - x_t = self.q_sample(x_start, t, noise=noise) - - terms = {} - - if self.loss_type == LossType.KL or self.loss_type == LossType.RESCALED_KL: - terms["loss"] = self._vb_terms_bpd( - model=model, - x_start=x_start, - x_t=x_t, - t=t, - clip_denoised=False, - model_kwargs=model_kwargs, - )["output"] - if self.loss_type == LossType.RESCALED_KL: - terms["loss"] *= self.num_timesteps - elif self.loss_type == LossType.MSE or self.loss_type == LossType.RESCALED_MSE: - model_output = model(x_t, t, **model_kwargs) - # try: - # model_output = model(x_t, t, **model_kwargs).sample # for tav unet - # except: - # model_output = model(x_t, t, **model_kwargs) - - if self.model_var_type in [ - ModelVarType.LEARNED, - ModelVarType.LEARNED_RANGE, - ]: - B, F, C = x_t.shape[:3] - assert model_output.shape == (B, F, C * 2, *x_t.shape[3:]) - model_output, model_var_values = th.split(model_output, C, dim=2) - # Learn the variance using the variational bound, but don't let - # it affect our mean prediction. - frozen_out = th.cat([model_output.detach(), model_var_values], dim=2) - terms["vb"] = self._vb_terms_bpd( - model=lambda *args, r=frozen_out: r, - x_start=x_start, - x_t=x_t, - t=t, - clip_denoised=False, - )["output"] - if self.loss_type == LossType.RESCALED_MSE: - # Divide by 1000 for equivalence with initial implementation. - # Without a factor of 1/1000, the VB term hurts the MSE term. - terms["vb"] *= self.num_timesteps / 1000.0 - - target = { - ModelMeanType.PREVIOUS_X: self.q_posterior_mean_variance( - x_start=x_start, x_t=x_t, t=t - )[0], - ModelMeanType.START_X: x_start, - ModelMeanType.EPSILON: noise, - }[self.model_mean_type] - assert model_output.shape == target.shape == x_start.shape - terms["mse"] = mean_flat((target - model_output) ** 2) - if "vb" in terms: - terms["loss"] = terms["mse"] + terms["vb"] - else: - terms["loss"] = terms["mse"] - else: - raise NotImplementedError(self.loss_type) - - return terms - - def _prior_bpd(self, x_start): - """ - Get the prior KL term for the variational lower-bound, measured in - bits-per-dim. - This term can't be optimized, as it only depends on the encoder. - :param x_start: the [N x C x ...] tensor of inputs. - :return: a batch of [N] KL values (in bits), one per batch element. - """ - batch_size = x_start.shape[0] - t = th.tensor([self.num_timesteps - 1] * batch_size, device=x_start.device) - qt_mean, _, qt_log_variance = self.q_mean_variance(x_start, t) - kl_prior = normal_kl( - mean1=qt_mean, logvar1=qt_log_variance, mean2=0.0, logvar2=0.0 - ) - return mean_flat(kl_prior) / np.log(2.0) - - def calc_bpd_loop(self, model, x_start, clip_denoised=True, model_kwargs=None): - """ - Compute the entire variational lower-bound, measured in bits-per-dim, - as well as other related quantities. - :param model: the model to evaluate loss on. - :param x_start: the [N x C x ...] tensor of inputs. - :param clip_denoised: if True, clip denoised samples. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :return: a dict containing the following keys: - - total_bpd: the total variational lower-bound, per batch element. - - prior_bpd: the prior term in the lower-bound. - - vb: an [N x T] tensor of terms in the lower-bound. - - xstart_mse: an [N x T] tensor of x_0 MSEs for each timestep. - - mse: an [N x T] tensor of epsilon MSEs for each timestep. - """ - device = x_start.device - batch_size = x_start.shape[0] - - vb = [] - xstart_mse = [] - mse = [] - for t in list(range(self.num_timesteps))[::-1]: - t_batch = th.tensor([t] * batch_size, device=device) - noise = th.randn_like(x_start) - x_t = self.q_sample(x_start=x_start, t=t_batch, noise=noise) - # Calculate VLB term at the current timestep - with th.no_grad(): - out = self._vb_terms_bpd( - model, - x_start=x_start, - x_t=x_t, - t=t_batch, - clip_denoised=clip_denoised, - model_kwargs=model_kwargs, - ) - vb.append(out["output"]) - xstart_mse.append(mean_flat((out["pred_xstart"] - x_start) ** 2)) - eps = self._predict_eps_from_xstart(x_t, t_batch, out["pred_xstart"]) - mse.append(mean_flat((eps - noise) ** 2)) - - vb = th.stack(vb, dim=1) - xstart_mse = th.stack(xstart_mse, dim=1) - mse = th.stack(mse, dim=1) - - prior_bpd = self._prior_bpd(x_start) - total_bpd = vb.sum(dim=1) + prior_bpd - return { - "total_bpd": total_bpd, - "prior_bpd": prior_bpd, - "vb": vb, - "xstart_mse": xstart_mse, - "mse": mse, - } - - -def _extract_into_tensor(arr, timesteps, broadcast_shape): - """ - Extract values from a 1-D numpy array for a batch of indices. - :param arr: the 1-D numpy array. - :param timesteps: a tensor of indices into the array to extract. - :param broadcast_shape: a larger shape of K dimensions with the batch - dimension equal to the length of timesteps. - :return: a tensor of shape [batch_size, 1, ...] where the shape has K dims. - """ - res = th.from_numpy(arr).to(device=timesteps.device)[timesteps].float() - while len(res.shape) < len(broadcast_shape): - res = res[..., None] - return res + th.zeros(broadcast_shape, device=timesteps.device) diff --git a/opensora/utils/diffusion/gaussian_diffusion_t2v.py b/opensora/utils/diffusion/gaussian_diffusion_t2v.py deleted file mode 100644 index 31b05330c..000000000 --- a/opensora/utils/diffusion/gaussian_diffusion_t2v.py +++ /dev/null @@ -1,915 +0,0 @@ -# Modified from OpenAI's diffusion repos -# GLIDE: https://github.com/openai/glide-text2im/blob/main/glide_text2im/gaussian_diffusion.py -# ADM: https://github.com/openai/guided-diffusion/blob/main/guided_diffusion -# IDDPM: https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py - - -import math - -import numpy as np -import torch as th -import enum - -from .diffusion_utils import discretized_gaussian_log_likelihood, normal_kl - - -def mean_flat(tensor): - """ - Take the mean over all non-batch dimensions. - """ - return tensor.mean(dim=list(range(1, len(tensor.shape)))) - -def mean_flat_reweight(tensor, weights): - """ - Take the mean over all non-batch dimensions. - """ - return tensor.mean(dim=list(range(1, len(tensor.shape)))) * weights - -class ModelMeanType(enum.Enum): - """ - Which type of output the model predicts. - """ - - PREVIOUS_X = enum.auto() # the model predicts x_{t-1} - START_X = enum.auto() # the model predicts x_0 - EPSILON = enum.auto() # the model predicts epsilon - - -class ModelVarType(enum.Enum): - """ - What is used as the model's output variance. - The LEARNED_RANGE option has been added to allow the model to predict - values between FIXED_SMALL and FIXED_LARGE, making its job easier. - """ - - LEARNED = enum.auto() - FIXED_SMALL = enum.auto() - FIXED_LARGE = enum.auto() - LEARNED_RANGE = enum.auto() - - -class LossType(enum.Enum): - MSE = enum.auto() # use raw MSE loss (and KL when learning variances) - RESCALED_MSE = ( - enum.auto() - ) # use raw MSE loss (with RESCALED_KL when learning variances) - KL = enum.auto() # use the variational lower-bound - RESCALED_KL = enum.auto() # like KL, but rescale to estimate the full VLB - - def is_vb(self): - return self == LossType.KL or self == LossType.RESCALED_KL - - -def _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, warmup_frac): - betas = beta_end * np.ones(num_diffusion_timesteps, dtype=np.float64) - warmup_time = int(num_diffusion_timesteps * warmup_frac) - betas[:warmup_time] = np.linspace(beta_start, beta_end, warmup_time, dtype=np.float64) - return betas - - -def get_beta_schedule(beta_schedule, *, beta_start, beta_end, num_diffusion_timesteps): - """ - This is the deprecated API for creating beta schedules. - See get_named_beta_schedule() for the new library of schedules. - """ - if beta_schedule == "quad": - betas = ( - np.linspace( - beta_start ** 0.5, - beta_end ** 0.5, - num_diffusion_timesteps, - dtype=np.float64, - ) - ** 2 - ) - elif beta_schedule == "linear": - betas = np.linspace(beta_start, beta_end, num_diffusion_timesteps, dtype=np.float64) - elif beta_schedule == "warmup10": - betas = _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, 0.1) - elif beta_schedule == "warmup50": - betas = _warmup_beta(beta_start, beta_end, num_diffusion_timesteps, 0.5) - elif beta_schedule == "const": - betas = beta_end * np.ones(num_diffusion_timesteps, dtype=np.float64) - elif beta_schedule == "jsd": # 1/T, 1/(T-1), 1/(T-2), ..., 1 - betas = 1.0 / np.linspace( - num_diffusion_timesteps, 1, num_diffusion_timesteps, dtype=np.float64 - ) - else: - raise NotImplementedError(beta_schedule) - assert betas.shape == (num_diffusion_timesteps,) - return betas - - -def get_named_beta_schedule(schedule_name, num_diffusion_timesteps): - """ - Get a pre-defined beta schedule for the given name. - The beta schedule library consists of beta schedules which remain similar - in the limit of num_diffusion_timesteps. - Beta schedules may be added, but should not be removed or changed once - they are committed to maintain backwards compatibility. - """ - if schedule_name == "linear": - # Linear schedule from Ho et al, extended to work for any number of - # diffusion steps. - scale = 1000 / num_diffusion_timesteps - return get_beta_schedule( - "linear", - beta_start=scale * 0.0001, - beta_end=scale * 0.02, - num_diffusion_timesteps=num_diffusion_timesteps, - ) - elif schedule_name == "squaredcos_cap_v2": - return betas_for_alpha_bar( - num_diffusion_timesteps, - lambda t: math.cos((t + 0.008) / 1.008 * math.pi / 2) ** 2, - ) - else: - raise NotImplementedError(f"unknown beta schedule: {schedule_name}") - - -def betas_for_alpha_bar(num_diffusion_timesteps, alpha_bar, max_beta=0.999): - """ - Create a beta schedule that discretizes the given alpha_t_bar function, - which defines the cumulative product of (1-beta) over time from t = [0,1]. - :param num_diffusion_timesteps: the number of betas to produce. - :param alpha_bar: a lambda that takes an argument t from 0 to 1 and - produces the cumulative product of (1-beta) up to that - part of the diffusion process. - :param max_beta: the maximum beta to use; use values lower than 1 to - prevent singularities. - """ - betas = [] - for i in range(num_diffusion_timesteps): - t1 = i / num_diffusion_timesteps - t2 = (i + 1) / num_diffusion_timesteps - betas.append(min(1 - alpha_bar(t2) / alpha_bar(t1), max_beta)) - return np.array(betas) - - -class GaussianDiffusion_T: - """ - Utilities for training and sampling diffusion models. - Original ported from this codebase: - https://github.com/hojonathanho/diffusion/blob/1e0dceb3b3495bbe19116a5e1b3596cd0706c543/diffusion_tf/diffusion_utils_2.py#L42 - :param betas: a 1-D numpy array of betas for each diffusion timestep, - starting at T and going to 1. - """ - - def __init__( - self, - *, - betas, - model_mean_type, - model_var_type, - loss_type - ): - - self.model_mean_type = model_mean_type - self.model_var_type = model_var_type - self.loss_type = loss_type - - # Use float64 for accuracy. - betas = np.array(betas, dtype=np.float64) - self.betas = betas - assert len(betas.shape) == 1, "betas must be 1-D" - assert (betas > 0).all() and (betas <= 1).all() - - self.num_timesteps = int(betas.shape[0]) - - alphas = 1.0 - betas - self.alphas_cumprod = np.cumprod(alphas, axis=0) - self.alphas_cumprod_prev = np.append(1.0, self.alphas_cumprod[:-1]) - self.alphas_cumprod_next = np.append(self.alphas_cumprod[1:], 0.0) - assert self.alphas_cumprod_prev.shape == (self.num_timesteps,) - - # calculations for diffusion q(x_t | x_{t-1}) and others - self.sqrt_alphas_cumprod = np.sqrt(self.alphas_cumprod) - self.sqrt_one_minus_alphas_cumprod = np.sqrt(1.0 - self.alphas_cumprod) - self.log_one_minus_alphas_cumprod = np.log(1.0 - self.alphas_cumprod) - self.sqrt_recip_alphas_cumprod = np.sqrt(1.0 / self.alphas_cumprod) - self.sqrt_recipm1_alphas_cumprod = np.sqrt(1.0 / self.alphas_cumprod - 1) - - # calculations for posterior q(x_{t-1} | x_t, x_0) - self.posterior_variance = ( - betas * (1.0 - self.alphas_cumprod_prev) / (1.0 - self.alphas_cumprod) - ) - # below: log calculation clipped because the posterior variance is 0 at the beginning of the diffusion chain - self.posterior_log_variance_clipped = np.log( - np.append(self.posterior_variance[1], self.posterior_variance[1:]) - ) if len(self.posterior_variance) > 1 else np.array([]) - - self.posterior_mean_coef1 = ( - betas * np.sqrt(self.alphas_cumprod_prev) / (1.0 - self.alphas_cumprod) - ) - self.posterior_mean_coef2 = ( - (1.0 - self.alphas_cumprod_prev) * np.sqrt(alphas) / (1.0 - self.alphas_cumprod) - ) - - def q_mean_variance(self, x_start, t): - """ - Get the distribution q(x_t | x_0). - :param x_start: the [N x C x ...] tensor of noiseless inputs. - :param t: the number of diffusion steps (minus 1). Here, 0 means one step. - :return: A tuple (mean, variance, log_variance), all of x_start's shape. - """ - mean = _extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start - variance = _extract_into_tensor(1.0 - self.alphas_cumprod, t, x_start.shape) - log_variance = _extract_into_tensor(self.log_one_minus_alphas_cumprod, t, x_start.shape) - return mean, variance, log_variance - - def q_sample(self, x_start, t, noise=None): - """ - Diffuse the data for a given number of diffusion steps. - In other words, sample from q(x_t | x_0). - :param x_start: the initial data batch. - :param t: the number of diffusion steps (minus 1). Here, 0 means one step. - :param noise: if specified, the split-out normal noise. - :return: A noisy version of x_start. - """ - if noise is None: - noise = th.randn_like(x_start) - assert noise.shape == x_start.shape - return ( - _extract_into_tensor(self.sqrt_alphas_cumprod, t, x_start.shape) * x_start - + _extract_into_tensor(self.sqrt_one_minus_alphas_cumprod, t, x_start.shape) * noise - ) - - def q_posterior_mean_variance(self, x_start, x_t, t): - """ - Compute the mean and variance of the diffusion posterior: - q(x_{t-1} | x_t, x_0) - """ - assert x_start.shape == x_t.shape - posterior_mean = ( - _extract_into_tensor(self.posterior_mean_coef1, t, x_t.shape) * x_start - + _extract_into_tensor(self.posterior_mean_coef2, t, x_t.shape) * x_t - ) - posterior_variance = _extract_into_tensor(self.posterior_variance, t, x_t.shape) - posterior_log_variance_clipped = _extract_into_tensor( - self.posterior_log_variance_clipped, t, x_t.shape - ) - assert ( - posterior_mean.shape[0] - == posterior_variance.shape[0] - == posterior_log_variance_clipped.shape[0] - == x_start.shape[0] - ) - return posterior_mean, posterior_variance, posterior_log_variance_clipped - - def p_mean_variance(self, model, x, t, clip_denoised=True, denoised_fn=None, model_kwargs=None): - """ - Apply the model to get p(x_{t-1} | x_t), as well as a prediction of - the initial x, x_0. - :param model: the model, which takes a signal and a batch of timesteps - as input. - :param x: the [N x C x ...] tensor at time t. - :param t: a 1-D Tensor of timesteps. - :param clip_denoised: if True, clip the denoised signal into [-1, 1]. - :param denoised_fn: if not None, a function which applies to the - x_start prediction before it is used to sample. Applies before - clip_denoised. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :return: a dict with the following keys: - - 'mean': the model mean output. - - 'variance': the model variance output. - - 'log_variance': the log of 'variance'. - - 'pred_xstart': the prediction for x_0. - """ - if model_kwargs is None: - model_kwargs = {} - - #B, F, C = x.shape[:3] - B, C, F = x.shape[:3] - assert t.shape == (B,) - model_output = model(x, t, **model_kwargs) - - try: - model_output.shape - except: - model_output = model_output[0] - # try: - # model_output = model_output.sample # for tav unet - # except: - # model_output = model(x, t, **model_kwargs) - if isinstance(model_output, tuple): - model_output, extra = model_output - else: - extra = None - - if self.model_var_type in [ModelVarType.LEARNED, ModelVarType.LEARNED_RANGE]: - #assert model_output.shape == (B, F, C * 2, *x.shape[3:]) - #model_output, model_var_values = th.split(model_output, C, dim=2) - #the output shape of uncondition or class condition latte is not the same as the latte_t2v - #BFCHW vs BCFHW - assert model_output.shape == (B, C * 2, F, *x.shape[3:]), f'model_output.shape ({model_output.shape}), != {(B, C * 2, F, *x.shape[3:])}' - model_output, model_var_values = th.split(model_output, C, dim=1) - min_log = _extract_into_tensor(self.posterior_log_variance_clipped, t, x.shape) - max_log = _extract_into_tensor(np.log(self.betas), t, x.shape) - # The model_var_values is [-1, 1] for [min_var, max_var]. - frac = (model_var_values + 1) / 2 - model_log_variance = frac * max_log + (1 - frac) * min_log - model_variance = th.exp(model_log_variance) - else: - model_variance, model_log_variance = { - # for fixedlarge, we set the initial (log-)variance like so - # to get a better decoder log likelihood. - ModelVarType.FIXED_LARGE: ( - np.append(self.posterior_variance[1], self.betas[1:]), - np.log(np.append(self.posterior_variance[1], self.betas[1:])), - ), - ModelVarType.FIXED_SMALL: ( - self.posterior_variance, - self.posterior_log_variance_clipped, - ), - }[self.model_var_type] - model_variance = _extract_into_tensor(model_variance, t, x.shape) - model_log_variance = _extract_into_tensor(model_log_variance, t, x.shape) - - def process_xstart(x): - if denoised_fn is not None: - x = denoised_fn(x) - if clip_denoised: - return x.clamp(-1, 1) - return x - - if self.model_mean_type == ModelMeanType.START_X: - pred_xstart = process_xstart(model_output) - else: - pred_xstart = process_xstart( - self._predict_xstart_from_eps(x_t=x, t=t, eps=model_output) - ) - model_mean, _, _ = self.q_posterior_mean_variance(x_start=pred_xstart, x_t=x, t=t) - - assert model_mean.shape == model_log_variance.shape == pred_xstart.shape == x.shape - return { - "mean": model_mean, - "variance": model_variance, - "log_variance": model_log_variance, - "pred_xstart": pred_xstart, - "extra": extra, - } - - def _predict_xstart_from_eps(self, x_t, t, eps): - assert x_t.shape == eps.shape - return ( - _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - - _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) * eps - ) - - def _predict_eps_from_xstart(self, x_t, t, pred_xstart): - return ( - _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x_t.shape) * x_t - pred_xstart - ) / _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x_t.shape) - - def condition_mean(self, cond_fn, p_mean_var, x, t, model_kwargs=None): - """ - Compute the mean for the previous step, given a function cond_fn that - computes the gradient of a conditional log probability with respect to - x. In particular, cond_fn computes grad(log(p(y|x))), and we want to - condition on y. - This uses the conditioning strategy from Sohl-Dickstein et al. (2015). - """ - gradient = cond_fn(x, t, **model_kwargs) - new_mean = p_mean_var["mean"].float() + p_mean_var["variance"] * gradient.float() - return new_mean - - def condition_score(self, cond_fn, p_mean_var, x, t, model_kwargs=None): - """ - Compute what the p_mean_variance output would have been, should the - model's score function be conditioned by cond_fn. - See condition_mean() for details on cond_fn. - Unlike condition_mean(), this instead uses the conditioning strategy - from Song et al (2020). - """ - alpha_bar = _extract_into_tensor(self.alphas_cumprod, t, x.shape) - - eps = self._predict_eps_from_xstart(x, t, p_mean_var["pred_xstart"]) - eps = eps - (1 - alpha_bar).sqrt() * cond_fn(x, t, **model_kwargs) - - out = p_mean_var.copy() - out["pred_xstart"] = self._predict_xstart_from_eps(x, t, eps) - out["mean"], _, _ = self.q_posterior_mean_variance(x_start=out["pred_xstart"], x_t=x, t=t) - return out - - def p_sample( - self, - model, - x, - t, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - ): - """ - Sample x_{t-1} from the model at the given timestep. - :param model: the model to sample from. - :param x: the current tensor at x_{t-1}. - :param t: the value of t, starting at 0 for the first diffusion step. - :param clip_denoised: if True, clip the x_start prediction to [-1, 1]. - :param denoised_fn: if not None, a function which applies to the - x_start prediction before it is used to sample. - :param cond_fn: if not None, this is a gradient function that acts - similarly to the model. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :return: a dict containing the following keys: - - 'sample': a random sample from the model. - - 'pred_xstart': a prediction of x_0. - """ - out = self.p_mean_variance( - model, - x, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - model_kwargs=model_kwargs, - ) - noise = th.randn_like(x) - nonzero_mask = ( - (t != 0).float().view(-1, *([1] * (len(x.shape) - 1))) - ) # no noise when t == 0 - if cond_fn is not None: - out["mean"] = self.condition_mean(cond_fn, out, x, t, model_kwargs=model_kwargs) - sample = out["mean"] + nonzero_mask * th.exp(0.5 * out["log_variance"]) * noise - return {"sample": sample, "pred_xstart": out["pred_xstart"]} - - def p_sample_loop( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - ): - """ - Generate samples from the model. - :param model: the model module. - :param shape: the shape of the samples, (N, C, H, W). - :param noise: if specified, the noise from the encoder to sample. - Should be of the same shape as `shape`. - :param clip_denoised: if True, clip x_start predictions to [-1, 1]. - :param denoised_fn: if not None, a function which applies to the - x_start prediction before it is used to sample. - :param cond_fn: if not None, this is a gradient function that acts - similarly to the model. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :param device: if specified, the device to create the samples on. - If not specified, use a model parameter's device. - :param progress: if True, show a tqdm progress bar. - :return: a non-differentiable batch of samples. - """ - final = None - for sample in self.p_sample_loop_progressive( - model, - shape, - noise=noise, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - device=device, - progress=progress, - ): - final = sample - return final["sample"] - - def p_sample_loop_progressive( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - ): - """ - Generate samples from the model and yield intermediate samples from - each timestep of diffusion. - Arguments are the same as p_sample_loop(). - Returns a generator over dicts, where each dict is the return value of - p_sample(). - """ - if device is None: - device = next(model.parameters()).device - assert isinstance(shape, (tuple, list)) - if noise is not None: - img = noise - else: - img = th.randn(*shape, device=device) - indices = list(range(self.num_timesteps))[::-1] - - if progress: - # Lazy import so that we don't depend on tqdm. - from tqdm.auto import tqdm - - indices = tqdm(indices) - - for i in indices: - t = th.tensor([i] * shape[0], device=device) - with th.no_grad(): - out = self.p_sample( - model, - img, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - ) - yield out - img = out["sample"] - - def ddim_sample( - self, - model, - x, - t, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - eta=0.0, - ): - """ - Sample x_{t-1} from the model using DDIM. - Same usage as p_sample(). - """ - out = self.p_mean_variance( - model, - x, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - model_kwargs=model_kwargs, - ) - if cond_fn is not None: - out = self.condition_score(cond_fn, out, x, t, model_kwargs=model_kwargs) - - # Usually our model outputs epsilon, but we re-derive it - # in case we used x_start or x_prev prediction. - eps = self._predict_eps_from_xstart(x, t, out["pred_xstart"]) - - alpha_bar = _extract_into_tensor(self.alphas_cumprod, t, x.shape) - alpha_bar_prev = _extract_into_tensor(self.alphas_cumprod_prev, t, x.shape) - sigma = ( - eta - * th.sqrt((1 - alpha_bar_prev) / (1 - alpha_bar)) - * th.sqrt(1 - alpha_bar / alpha_bar_prev) - ) - # Equation 12. - noise = th.randn_like(x) - mean_pred = ( - out["pred_xstart"] * th.sqrt(alpha_bar_prev) - + th.sqrt(1 - alpha_bar_prev - sigma ** 2) * eps - ) - nonzero_mask = ( - (t != 0).float().view(-1, *([1] * (len(x.shape) - 1))) - ) # no noise when t == 0 - sample = mean_pred + nonzero_mask * sigma * noise - return {"sample": sample, "pred_xstart": out["pred_xstart"]} - - def ddim_reverse_sample( - self, - model, - x, - t, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - eta=0.0, - ): - """ - Sample x_{t+1} from the model using DDIM reverse ODE. - """ - assert eta == 0.0, "Reverse ODE only for deterministic path" - out = self.p_mean_variance( - model, - x, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - model_kwargs=model_kwargs, - ) - if cond_fn is not None: - out = self.condition_score(cond_fn, out, x, t, model_kwargs=model_kwargs) - # Usually our model outputs epsilon, but we re-derive it - # in case we used x_start or x_prev prediction. - eps = ( - _extract_into_tensor(self.sqrt_recip_alphas_cumprod, t, x.shape) * x - - out["pred_xstart"] - ) / _extract_into_tensor(self.sqrt_recipm1_alphas_cumprod, t, x.shape) - alpha_bar_next = _extract_into_tensor(self.alphas_cumprod_next, t, x.shape) - - # Equation 12. reversed - mean_pred = out["pred_xstart"] * th.sqrt(alpha_bar_next) + th.sqrt(1 - alpha_bar_next) * eps - - return {"sample": mean_pred, "pred_xstart": out["pred_xstart"]} - - def ddim_sample_loop( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - eta=0.0, - ): - """ - Generate samples from the model using DDIM. - Same usage as p_sample_loop(). - """ - final = None - for sample in self.ddim_sample_loop_progressive( - model, - shape, - noise=noise, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - device=device, - progress=progress, - eta=eta, - ): - final = sample - return final["sample"] - - def ddim_sample_loop_progressive( - self, - model, - shape, - noise=None, - clip_denoised=True, - denoised_fn=None, - cond_fn=None, - model_kwargs=None, - device=None, - progress=False, - eta=0.0, - ): - """ - Use DDIM to sample from the model and yield intermediate samples from - each timestep of DDIM. - Same usage as p_sample_loop_progressive(). - """ - if device is None: - device = next(model.parameters()).device - assert isinstance(shape, (tuple, list)) - if noise is not None: - img = noise - else: - img = th.randn(*shape, device=device) - indices = list(range(self.num_timesteps))[::-1] - - if progress: - # Lazy import so that we don't depend on tqdm. - from tqdm.auto import tqdm - - indices = tqdm(indices) - - for i in indices: - t = th.tensor([i] * shape[0], device=device) - with th.no_grad(): - out = self.ddim_sample( - model, - img, - t, - clip_denoised=clip_denoised, - denoised_fn=denoised_fn, - cond_fn=cond_fn, - model_kwargs=model_kwargs, - eta=eta, - ) - yield out - img = out["sample"] - - def _vb_terms_bpd( - self, model, x_start, x_t, t, clip_denoised=True, model_kwargs=None, mask=1.0, - ): - """ - Get a term for the variational lower-bound. - The resulting units are bits (rather than nats, as one might expect). - This allows for comparison to other papers. - :return: a dict with the following keys: - - 'output': a shape [N] tensor of NLLs or KLs. - - 'pred_xstart': the x_0 predictions. - """ - # import ipdb;ipdb.set_trace() - true_mean, _, true_log_variance_clipped = self.q_posterior_mean_variance( - x_start=x_start, x_t=x_t, t=t - ) - out = self.p_mean_variance( - model, x_t, t, clip_denoised=clip_denoised, model_kwargs=model_kwargs - ) - kl = normal_kl( - true_mean, true_log_variance_clipped, out["mean"], out["log_variance"] - ) - kl = mean_flat(kl * mask) / np.log(2.0) - - decoder_nll = -discretized_gaussian_log_likelihood( - x_start, means=out["mean"], log_scales=0.5 * out["log_variance"] - ) - assert decoder_nll.shape == x_start.shape - decoder_nll = mean_flat(decoder_nll * mask) / np.log(2.0) - - # At the first timestep return the decoder NLL, - # otherwise return KL(q(x_{t-1}|x_t,x_0) || p(x_{t-1}|x_t)) - output = th.where((t == 0), decoder_nll, kl) - return {"output": output, "pred_xstart": out["pred_xstart"]} - - def training_losses(self, model, x_start, t, model_kwargs=None, noise=None, mse_loss_weights=None): - """ - Compute training losses for a single timestep. - :param model: the model to evaluate loss on. - :param x_start: the [N x C x ...] tensor of inputs. - :param t: a batch of timestep indices. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :param noise: if specified, the specific Gaussian noise to try to remove. - :return: a dict with the key "loss" containing a tensor of shape [N]. - Some mean or variance settings may also have other keys. - """ - if model_kwargs is None: - model_kwargs = {} - mask = 1.0 - else: - mask = model_kwargs['attention_mask'].unsqueeze(1) # b t h w -> b 1 t h w - - if noise is None: - noise = th.randn_like(x_start) - x_t = self.q_sample(x_start, t, noise=noise) - - terms = {} - if self.loss_type == LossType.KL or self.loss_type == LossType.RESCALED_KL: - terms["loss"] = self._vb_terms_bpd( - model=model, - x_start=x_start, - x_t=x_t, - t=t, - clip_denoised=False, - model_kwargs=model_kwargs, - )["output"] - if self.loss_type == LossType.RESCALED_KL: - terms["loss"] *= self.num_timesteps - elif self.loss_type == LossType.MSE or self.loss_type == LossType.RESCALED_MSE: - model_output = model(x_t, t, **model_kwargs) - # try: - # model_output = model(x_t, t, **model_kwargs).sample # for tav unet - # except: - # model_output = model(x_t, t, **model_kwargs) - - if self.model_var_type in [ - ModelVarType.LEARNED, - ModelVarType.LEARNED_RANGE, - ]: - #B, F, C = x_t.shape[:3] - #assert model_output.shape == (B, F, C * 2, *x_t.shape[3:]) - #the output shape of uncondition or class condition latte is not the same as the latte_t2v - #BFCHW vs BCFHW - B, C, F = x_t.shape[:3] - assert model_output[0].shape == (B, C * 2, F, *x_t.shape[3:]), f"{model_output[0].shape} != ({B}, {C} * 2, {F}, {x_t.shape[3:]})" - #model_output, model_var_values = th.split(model_output, C, dim=2) - model_output, model_var_values = th.split(model_output[0], C, dim=1) - - # Learn the variance using the variational bound, but don't let - # it affect our mean prediction. - #frozen_out = th.cat([model_output.detach(), model_var_values], dim=2) - frozen_out = th.cat([model_output.detach(), model_var_values], dim=1) - terms["vb"] = self._vb_terms_bpd( - model=lambda *args, r=frozen_out: r, - x_start=x_start, - x_t=x_t, - t=t, - clip_denoised=False, - mask=mask - )["output"] - if self.loss_type == LossType.RESCALED_MSE: - # Divide by 1000 for equivalence with initial implementation. - # Without a factor of 1/1000, the VB term hurts the MSE term. - terms["vb"] *= self.num_timesteps / 1000.0 - - target = { - ModelMeanType.PREVIOUS_X: self.q_posterior_mean_variance( - x_start=x_start, x_t=x_t, t=t - )[0], - ModelMeanType.START_X: x_start, - ModelMeanType.EPSILON: noise, - }[self.model_mean_type] - assert model_output.shape == target.shape == x_start.shape - - # terms["mse"] = mean_flat(((target - model_output) ** 2) * mask) - # import ipdb;ipdb.set_trace() - if mse_loss_weights is not None: - terms["mse"] = mean_flat_reweight(((target - model_output) ** 2) * mask, mse_loss_weights) - else: - terms["mse"] = mean_flat(((target - model_output) ** 2) * mask) - - if "vb" in terms: - terms["loss"] = terms["mse"] + terms["vb"] - else: - terms["loss"] = terms["mse"] - else: - raise NotImplementedError(self.loss_type) - - return terms - - def _prior_bpd(self, x_start): - """ - Get the prior KL term for the variational lower-bound, measured in - bits-per-dim. - This term can't be optimized, as it only depends on the encoder. - :param x_start: the [N x C x ...] tensor of inputs. - :return: a batch of [N] KL values (in bits), one per batch element. - """ - batch_size = x_start.shape[0] - t = th.tensor([self.num_timesteps - 1] * batch_size, device=x_start.device) - qt_mean, _, qt_log_variance = self.q_mean_variance(x_start, t) - kl_prior = normal_kl( - mean1=qt_mean, logvar1=qt_log_variance, mean2=0.0, logvar2=0.0 - ) - return mean_flat(kl_prior) / np.log(2.0) - - def calc_bpd_loop(self, model, x_start, clip_denoised=True, model_kwargs=None): - """ - Compute the entire variational lower-bound, measured in bits-per-dim, - as well as other related quantities. - :param model: the model to evaluate loss on. - :param x_start: the [N x C x ...] tensor of inputs. - :param clip_denoised: if True, clip denoised samples. - :param model_kwargs: if not None, a dict of extra keyword arguments to - pass to the model. This can be used for conditioning. - :return: a dict containing the following keys: - - total_bpd: the total variational lower-bound, per batch element. - - prior_bpd: the prior term in the lower-bound. - - vb: an [N x T] tensor of terms in the lower-bound. - - xstart_mse: an [N x T] tensor of x_0 MSEs for each timestep. - - mse: an [N x T] tensor of epsilon MSEs for each timestep. - """ - device = x_start.device - batch_size = x_start.shape[0] - - vb = [] - xstart_mse = [] - mse = [] - for t in list(range(self.num_timesteps))[::-1]: - t_batch = th.tensor([t] * batch_size, device=device) - noise = th.randn_like(x_start) - x_t = self.q_sample(x_start=x_start, t=t_batch, noise=noise) - # Calculate VLB term at the current timestep - with th.no_grad(): - out = self._vb_terms_bpd( - model, - x_start=x_start, - x_t=x_t, - t=t_batch, - clip_denoised=clip_denoised, - model_kwargs=model_kwargs, - ) - vb.append(out["output"]) - xstart_mse.append(mean_flat((out["pred_xstart"] - x_start) ** 2)) - eps = self._predict_eps_from_xstart(x_t, t_batch, out["pred_xstart"]) - mse.append(mean_flat((eps - noise) ** 2)) - - vb = th.stack(vb, dim=1) - xstart_mse = th.stack(xstart_mse, dim=1) - mse = th.stack(mse, dim=1) - - prior_bpd = self._prior_bpd(x_start) - total_bpd = vb.sum(dim=1) + prior_bpd - return { - "total_bpd": total_bpd, - "prior_bpd": prior_bpd, - "vb": vb, - "xstart_mse": xstart_mse, - "mse": mse, - } - - -def _extract_into_tensor(arr, timesteps, broadcast_shape): - """ - Extract values from a 1-D numpy array for a batch of indices. - :param arr: the 1-D numpy array. - :param timesteps: a tensor of indices into the array to extract. - :param broadcast_shape: a larger shape of K dimensions with the batch - dimension equal to the length of timesteps. - :return: a tensor of shape [batch_size, 1, ...] where the shape has K dims. - """ - res = th.from_numpy(arr).to(device=timesteps.device)[timesteps].float() - while len(res.shape) < len(broadcast_shape): - res = res[..., None] - return res + th.zeros(broadcast_shape, device=timesteps.device) diff --git a/opensora/utils/diffusion/respace.py b/opensora/utils/diffusion/respace.py deleted file mode 100644 index aed6ed77f..000000000 --- a/opensora/utils/diffusion/respace.py +++ /dev/null @@ -1,198 +0,0 @@ -# Modified from OpenAI's diffusion repos -# GLIDE: https://github.com/openai/glide-text2im/blob/main/glide_text2im/gaussian_diffusion.py -# ADM: https://github.com/openai/guided-diffusion/blob/main/guided_diffusion -# IDDPM: https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py -import torch -import numpy as np -import torch as th - -from .gaussian_diffusion import GaussianDiffusion -from .gaussian_diffusion_t2v import GaussianDiffusion_T - - -def space_timesteps(num_timesteps, section_counts): - """ - Create a list of timesteps to use from an original diffusion process, - given the number of timesteps we want to take from equally-sized portions - of the original process. - For example, if there's 300 timesteps and the section counts are [10,15,20] - then the first 100 timesteps are strided to be 10 timesteps, the second 100 - are strided to be 15 timesteps, and the final 100 are strided to be 20. - If the stride is a string starting with "ddim", then the fixed striding - from the DDIM paper is used, and only one section is allowed. - :param num_timesteps: the number of diffusion steps in the original - process to divide up. - :param section_counts: either a list of numbers, or a string containing - comma-separated numbers, indicating the step count - per section. As a special case, use "ddimN" where N - is a number of steps to use the striding from the - DDIM paper. - :return: a set of diffusion steps from the original process to use. - """ - if isinstance(section_counts, str): - if section_counts.startswith("ddim"): - desired_count = int(section_counts[len("ddim") :]) - for i in range(1, num_timesteps): - if len(range(0, num_timesteps, i)) == desired_count: - return set(range(0, num_timesteps, i)) - raise ValueError( - f"cannot create exactly {num_timesteps} steps with an integer stride" - ) - section_counts = [int(x) for x in section_counts.split(",")] - size_per = num_timesteps // len(section_counts) - extra = num_timesteps % len(section_counts) - start_idx = 0 - all_steps = [] - for i, section_count in enumerate(section_counts): - size = size_per + (1 if i < extra else 0) - if size < section_count: - raise ValueError( - f"cannot divide section of {size} steps into {section_count}" - ) - if section_count <= 1: - frac_stride = 1 - else: - frac_stride = (size - 1) / (section_count - 1) - cur_idx = 0.0 - taken_steps = [] - for _ in range(section_count): - taken_steps.append(start_idx + round(cur_idx)) - cur_idx += frac_stride - all_steps += taken_steps - start_idx += size - return set(all_steps) - - -class SpacedDiffusion(GaussianDiffusion): - """ - A diffusion process which can skip steps in a base diffusion process. - :param use_timesteps: a collection (sequence or set) of timesteps from the - original diffusion process to retain. - :param kwargs: the kwargs to create the base diffusion process. - """ - - def __init__(self, use_timesteps, **kwargs): - self.use_timesteps = set(use_timesteps) - self.timestep_map = [] - self.original_num_steps = len(kwargs["betas"]) - - base_diffusion = GaussianDiffusion(**kwargs) # pylint: disable=missing-kwoa - last_alpha_cumprod = 1.0 - new_betas = [] - for i, alpha_cumprod in enumerate(base_diffusion.alphas_cumprod): - if i in self.use_timesteps: - new_betas.append(1 - alpha_cumprod / last_alpha_cumprod) - last_alpha_cumprod = alpha_cumprod - self.timestep_map.append(i) - kwargs["betas"] = np.array(new_betas) - super().__init__(**kwargs) - - def p_mean_variance( - self, model, *args, **kwargs - ): # pylint: disable=signature-differs - return super().p_mean_variance(self._wrap_model(model), *args, **kwargs) - - # @torch.compile - def training_losses( - self, model, *args, **kwargs - ): # pylint: disable=signature-differs - return super().training_losses(self._wrap_model(model), *args, **kwargs) - - def condition_mean(self, cond_fn, *args, **kwargs): - return super().condition_mean(self._wrap_model(cond_fn), *args, **kwargs) - - def condition_score(self, cond_fn, *args, **kwargs): - return super().condition_score(self._wrap_model(cond_fn), *args, **kwargs) - - def _wrap_model(self, model): - if isinstance(model, _WrappedModel): - return model - return _WrappedModel( - model, self.timestep_map, self.original_num_steps - ) - - def _scale_timesteps(self, t): - # Scaling is done by the wrapped model. - return t - - -class _WrappedModel: - def __init__(self, model, timestep_map, original_num_steps): - self.model = model - self.timestep_map = timestep_map - # self.rescale_timesteps = rescale_timesteps - self.original_num_steps = original_num_steps - - def __call__(self, x, ts, **kwargs): - map_tensor = th.tensor(self.timestep_map, device=ts.device, dtype=ts.dtype) - new_ts = map_tensor[ts] - # if self.rescale_timesteps: - # new_ts = new_ts.float() * (1000.0 / self.original_num_steps) - return self.model(x, new_ts, **kwargs) - -class SpacedDiffusion_T(GaussianDiffusion_T): - """ - A diffusion process which can skip steps in a base diffusion process. - :param use_timesteps: a collection (sequence or set) of timesteps from the - original diffusion process to retain. - :param kwargs: the kwargs to create the base diffusion process. - """ - - def __init__(self, use_timesteps, **kwargs): - self.use_timesteps = set(use_timesteps) - self.timestep_map = [] - self.original_num_steps = len(kwargs["betas"]) - - base_diffusion = GaussianDiffusion(**kwargs) # pylint: disable=missing-kwoa - last_alpha_cumprod = 1.0 - new_betas = [] - for i, alpha_cumprod in enumerate(base_diffusion.alphas_cumprod): - if i in self.use_timesteps: - new_betas.append(1 - alpha_cumprod / last_alpha_cumprod) - last_alpha_cumprod = alpha_cumprod - self.timestep_map.append(i) - kwargs["betas"] = np.array(new_betas) - super().__init__(**kwargs) - - def p_mean_variance( - self, model, *args, **kwargs - ): # pylint: disable=signature-differs - return super().p_mean_variance(self._wrap_model(model), *args, **kwargs) - - # @torch.compile - def training_losses( - self, model, *args, **kwargs - ): # pylint: disable=signature-differs - return super().training_losses(self._wrap_model(model), *args, **kwargs) - - def condition_mean(self, cond_fn, *args, **kwargs): - return super().condition_mean(self._wrap_model(cond_fn), *args, **kwargs) - - def condition_score(self, cond_fn, *args, **kwargs): - return super().condition_score(self._wrap_model(cond_fn), *args, **kwargs) - - def _wrap_model(self, model): - if isinstance(model, _WrappedModel): - return model - return _WrappedModel( - model, self.timestep_map, self.original_num_steps - ) - - def _scale_timesteps(self, t): - # Scaling is done by the wrapped model. - return t - - -class _WrappedModel: - def __init__(self, model, timestep_map, original_num_steps): - self.model = model - self.timestep_map = timestep_map - # self.rescale_timesteps = rescale_timesteps - self.original_num_steps = original_num_steps - - def __call__(self, x, ts, **kwargs): - map_tensor = th.tensor(self.timestep_map, device=ts.device, dtype=ts.dtype) - new_ts = map_tensor[ts] - # if self.rescale_timesteps: - # new_ts = new_ts.float() * (1000.0 / self.original_num_steps) - return self.model(x, new_ts, **kwargs) \ No newline at end of file diff --git a/opensora/utils/diffusion/timestep_sampler.py b/opensora/utils/diffusion/timestep_sampler.py deleted file mode 100644 index a3f369847..000000000 --- a/opensora/utils/diffusion/timestep_sampler.py +++ /dev/null @@ -1,150 +0,0 @@ -# Modified from OpenAI's diffusion repos -# GLIDE: https://github.com/openai/glide-text2im/blob/main/glide_text2im/gaussian_diffusion.py -# ADM: https://github.com/openai/guided-diffusion/blob/main/guided_diffusion -# IDDPM: https://github.com/openai/improved-diffusion/blob/main/improved_diffusion/gaussian_diffusion.py - -from abc import ABC, abstractmethod - -import numpy as np -import torch as th -import torch.distributed as dist - - -def create_named_schedule_sampler(name, diffusion): - """ - Create a ScheduleSampler from a library of pre-defined samplers. - :param name: the name of the sampler. - :param diffusion: the diffusion object to sample for. - """ - if name == "uniform": - return UniformSampler(diffusion) - elif name == "loss-second-moment": - return LossSecondMomentResampler(diffusion) - else: - raise NotImplementedError(f"unknown schedule sampler: {name}") - - -class ScheduleSampler(ABC): - """ - A distribution over timesteps in the diffusion process, intended to reduce - variance of the objective. - By default, samplers perform unbiased importance sampling, in which the - objective's mean is unchanged. - However, subclasses may override sample() to change how the resampled - terms are reweighted, allowing for actual changes in the objective. - """ - - @abstractmethod - def weights(self): - """ - Get a numpy array of weights, one per diffusion step. - The weights needn't be normalized, but must be positive. - """ - - def sample(self, batch_size, device): - """ - Importance-sample timesteps for a batch. - :param batch_size: the number of timesteps. - :param device: the torch device to save to. - :return: a tuple (timesteps, weights): - - timesteps: a tensor of timestep indices. - - weights: a tensor of weights to scale the resulting losses. - """ - w = self.weights() - p = w / np.sum(w) - indices_np = np.random.choice(len(p), size=(batch_size,), p=p) - indices = th.from_numpy(indices_np).long().to(device) - weights_np = 1 / (len(p) * p[indices_np]) - weights = th.from_numpy(weights_np).float().to(device) - return indices, weights - - -class UniformSampler(ScheduleSampler): - def __init__(self, diffusion): - self.diffusion = diffusion - self._weights = np.ones([diffusion.num_timesteps]) - - def weights(self): - return self._weights - - -class LossAwareSampler(ScheduleSampler): - def update_with_local_losses(self, local_ts, local_losses): - """ - Update the reweighting using losses from a model. - Call this method from each rank with a batch of timesteps and the - corresponding losses for each of those timesteps. - This method will perform synchronization to make sure all of the ranks - maintain the exact same reweighting. - :param local_ts: an integer Tensor of timesteps. - :param local_losses: a 1D Tensor of losses. - """ - batch_sizes = [ - th.tensor([0], dtype=th.int32, device=local_ts.device) - for _ in range(dist.get_world_size()) - ] - dist.all_gather( - batch_sizes, - th.tensor([len(local_ts)], dtype=th.int32, device=local_ts.device), - ) - - # Pad all_gather batches to be the maximum batch size. - batch_sizes = [x.item() for x in batch_sizes] - max_bs = max(batch_sizes) - - timestep_batches = [th.zeros(max_bs).to(local_ts) for bs in batch_sizes] - loss_batches = [th.zeros(max_bs).to(local_losses) for bs in batch_sizes] - dist.all_gather(timestep_batches, local_ts) - dist.all_gather(loss_batches, local_losses) - timesteps = [ - x.item() for y, bs in zip(timestep_batches, batch_sizes) for x in y[:bs] - ] - losses = [x.item() for y, bs in zip(loss_batches, batch_sizes) for x in y[:bs]] - self.update_with_all_losses(timesteps, losses) - - @abstractmethod - def update_with_all_losses(self, ts, losses): - """ - Update the reweighting using losses from a model. - Sub-classes should override this method to update the reweighting - using losses from the model. - This method directly updates the reweighting without synchronizing - between workers. It is called by update_with_local_losses from all - ranks with identical arguments. Thus, it should have deterministic - behavior to maintain state across workers. - :param ts: a list of int timesteps. - :param losses: a list of float losses, one per timestep. - """ - - -class LossSecondMomentResampler(LossAwareSampler): - def __init__(self, diffusion, history_per_term=10, uniform_prob=0.001): - self.diffusion = diffusion - self.history_per_term = history_per_term - self.uniform_prob = uniform_prob - self._loss_history = np.zeros( - [diffusion.num_timesteps, history_per_term], dtype=np.float64 - ) - self._loss_counts = np.zeros([diffusion.num_timesteps], dtype=np.int) - - def weights(self): - if not self._warmed_up(): - return np.ones([self.diffusion.num_timesteps], dtype=np.float64) - weights = np.sqrt(np.mean(self._loss_history ** 2, axis=-1)) - weights /= np.sum(weights) - weights *= 1 - self.uniform_prob - weights += self.uniform_prob / len(weights) - return weights - - def update_with_all_losses(self, ts, losses): - for t, loss in zip(ts, losses): - if self._loss_counts[t] == self.history_per_term: - # Shift out the oldest loss term. - self._loss_history[t, :-1] = self._loss_history[t, 1:] - self._loss_history[t, -1] = loss - else: - self._loss_history[t, self._loss_counts[t]] = loss - self._loss_counts[t] += 1 - - def _warmed_up(self): - return (self._loss_counts == self.history_per_term).all() diff --git a/pyproject.toml b/pyproject.toml index 65e252411..68ce7807a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ classifiers = [ ] dependencies = [ "torch==2.1.0", "torchvision==0.16.0", - "transformers==4.40.1", "accelerate==0.29.3", "tokenizers==0.19.1", "diffusers@git+https://github.com/huggingface/diffusers", + "transformers==4.40.1", "accelerate==0.29.3", "tokenizers==0.19.1", "diffusers==0.28.0", "albumentations==1.4.0", "av==11.0.0", "decord==0.6.0", "einops==0.7.0", "fastapi==0.110.0", "gdown==5.1.0", "h5py==3.10.0", "idna==3.6", 'imageio==2.34.0', "matplotlib==3.7.5", "numpy==1.24.4", "omegaconf==2.1.1", "opencv-python==4.9.0.80", "opencv-python-headless==4.9.0.80", "pandas==2.0.3", "pillow==10.2.0", diff --git a/scripts/text_condition/new_train_image_256x256.sh b/scripts/text_condition/new_train_image_256x256.sh deleted file mode 100644 index 406cb41d7..000000000 --- a/scripts/text_condition/new_train_image_256x256.sh +++ /dev/null @@ -1,54 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11_vpre" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example2.yaml \ - opensora/train/train_t2v_new.py \ - --model OpenSoraT2V-B/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 256 \ - --max_width 256 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11_vpre" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --guidance_scale 2.5 \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_ds_image_256x256.sh b/scripts/text_condition/train_ds_image_256x256.sh index d4c958335..3bf3c9384 100644 --- a/scripts/text_condition/train_ds_image_256x256.sh +++ b/scripts/text_condition/train_ds_image_256x256.sh @@ -43,13 +43,12 @@ accelerate launch \ --checkpointing_steps=500 \ --output_dir="bs16_4node_lr1e-4_snr5_ema_ps11_ds22" \ --allow_tf32 \ - --use_deepspeed \ --model_max_length 512 \ --use_image_num 0 \ --enable_tracker \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 2.5 \ + --cfg 0.1 \ --downsampler "k55_s22" \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_ds_video3d_65x240p.sh b/scripts/text_condition/train_ds_video3d_65x240p.sh index 4b9051a31..42b387f01 100644 --- a/scripts/text_condition/train_ds_video3d_65x240p.sh +++ b/scripts/text_condition/train_ds_video3d_65x240p.sh @@ -2,19 +2,19 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" export PROJECT="testnpu3d_" -# export HF_DATASETS_OFFLINE=1 -# export TRANSFORMERS_OFFLINE=1 +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 # NCCL setting -# export NCCL_PXN_DISABLE=0 -# export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_IB_GID_INDEX=3 -# export NCCL_ALGO=Ring -# export OMP_NUM_THREADS=1 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 accelerate launch \ --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-L/111 \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-B/222 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ @@ -24,8 +24,8 @@ accelerate launch \ --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ --num_frames 253 \ - --max_height 480 \ - --max_width 640 \ + --max_height 720 \ + --max_width 1280 \ --interpolation_scale_t 1.0 \ --interpolation_scale_h 0.5 \ --interpolation_scale_w 0.5 \ @@ -40,16 +40,15 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=100 \ + --checkpointing_steps=500 \ --output_dir="testnpu3d_" \ --allow_tf32 \ - --use_deepspeed \ --model_max_length 512 \ --use_image_num 0 \ - --enable_tiling \ --enable_tracker \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 7.5 \ - --downsampler "k333_s444" \ + --cfg 0.1 \ + --downsampler "k333_s434" \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_image_256x256.sh b/scripts/text_condition/train_image_256x256.sh index 31143878f..92c856f6b 100644 --- a/scripts/text_condition/train_image_256x256.sh +++ b/scripts/text_condition/train_image_256x256.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs16_4node_lr1e-4_snr5_ema" +export PROJECT="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,8 +12,8 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v.py \ + --config_file scripts/accelerate_configs/multi_node_example2.yaml \ + opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-B/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ @@ -31,7 +31,7 @@ accelerate launch \ --interpolation_scale_w 0.5 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=16 \ + --train_batch_size=32 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -41,14 +41,13 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="bs16_4node_lr1e-4_snr5_ema_ps22_ds11" \ + --output_dir="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11" \ --allow_tf32 \ - --use_deepspeed \ --model_max_length 512 \ --use_image_num 0 \ --enable_tracker \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 2.5 \ + --cfg 0.1 \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_image_256x256_edm.sh b/scripts/text_condition/train_image_256x256_edm.sh deleted file mode 100644 index 85e7b4063..000000000 --- a/scripts/text_condition/train_image_256x256_edm.sh +++ /dev/null @@ -1,53 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11_edm" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example2.yaml \ - opensora/train/train_t2v_edm.py \ - --model OpenSoraT2V-B/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 256 \ - --max_width 256 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="bs32_2node_lr1e-4_snr5_ema_ps22_ds11_edm" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --use_ema \ - --ema_start_step 0 \ - --guidance_scale 2.5 \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_image_256x256_on_npu.sh b/scripts/text_condition/train_image_256x256_on_npu.sh index 91f54d692..33385574b 100644 --- a/scripts/text_condition/train_image_256x256_on_npu.sh +++ b/scripts/text_condition/train_image_256x256_on_npu.sh @@ -7,7 +7,7 @@ accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ - opensora/train/train_t2v.py \ + opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-B/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ @@ -42,5 +42,5 @@ accelerate launch \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 2.5 \ + --cfg 0.1 \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_imageae.sh b/scripts/text_condition/train_imageae.sh deleted file mode 100644 index fff9c8afe..000000000 --- a/scripts/text_condition/train_imageae.sh +++ /dev/null @@ -1,34 +0,0 @@ -export WANDB_KEY="" -export ENTITY="" -export PROJECT="t2v-f16s3-img4-128-imgvae188-bf16-gc-xformers" -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model LatteT2V-XL/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --dataset t2v \ - --ae stabilityai/sd-vae-ft-mse \ - --data_path /remote-home1/dataset/sharegpt4v_path_cap_.json \ - --video_folder /remote-home1/dataset/data_split \ - --sample_rate 1 \ - --num_frames 17 \ - --max_image_size 256 \ - --gradient_checkpointing \ - --attention_mode xformers \ - --train_batch_size=4 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-05 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="t2v-f17-256-img4-imagevae488-bf16-ckpt-xformers-bs4-lr2e-5-t5" \ - --allow_tf32 \ - --pretrained t2v.pt \ - --use_deepspeed \ - --model_max_length 300 \ - --use_image_num 4 \ - --use_img_from_vid diff --git a/scripts/text_condition/train_udit3d_61x240p.sh b/scripts/text_condition/train_udit3d_61x240p.sh deleted file mode 100644 index f4175a2ba..000000000 --- a/scripts/text_condition/train_udit3d_61x240p.sh +++ /dev/null @@ -1,54 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="testnpuudit_" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ - --model UDiTT2V-B/111 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --sample_rate 1 \ - --num_frames 509 \ - --max_height 240 \ - --max_width 320 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="testnpuudit_" \ - --allow_tf32 \ - --use_deepspeed \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tiling \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --guidance_scale 2.0 diff --git a/scripts/text_condition/train_udit3d_61x240p_on_npu.sh b/scripts/text_condition/train_udit3d_61x240p_on_npu.sh deleted file mode 100644 index bdfd49954..000000000 --- a/scripts/text_condition/train_udit3d_61x240p_on_npu.sh +++ /dev/null @@ -1,46 +0,0 @@ -export PROJECT=$PROJECT_NAME -WEIGHT_PATH="/home/opensora/shebin/pre_weights/" -env -export WANDB_MODE='offline' - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ - --machine_rank=${MACHINE_RANK} \ - --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ - opensora/train/train_t2v.py \ - --model UDiTT2V-B/111 \ - --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ - --cache_dir "../cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ - --video_data "./scripts/train_data/video_data_on_npu.txt" \ - --image_data "./scripts/train_data/image_data_on_npu.txt" \ - --sample_rate 1 \ - --num_frames ${NUM_FRAME} \ - --max_height 240 \ - --max_width 320 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=20 \ - --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --guidance_scale 2.0 diff --git a/scripts/text_condition/train_video21d_65x240p.sh b/scripts/text_condition/train_video21d_65x240p.sh index 9f105c222..a387d4213 100644 --- a/scripts/text_condition/train_video21d_65x240p.sh +++ b/scripts/text_condition/train_video21d_65x240p.sh @@ -13,7 +13,7 @@ export OMP_NUM_THREADS=1 accelerate launch \ --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ + opensora/train/train_t2v_diffusers.py \ --model LatteT2V/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ @@ -43,11 +43,11 @@ accelerate launch \ --checkpointing_steps=100 \ --output_dir="testnpu21d_" \ --allow_tf32 \ - --use_deepspeed \ --model_max_length 512 \ --use_image_num 0 \ - --enable_tiling \ --enable_tracker \ --snr_gamma 5.0 \ --use_ema \ - --ema_start_step 0 \ No newline at end of file + --ema_start_step 0 \ + --cfg 0.1 \ + --resume_from_checkpoint="latest" \ No newline at end of file diff --git a/scripts/text_condition/train_video21d_65x240p_on_npu.sh b/scripts/text_condition/train_video21d_65x240p_on_npu.sh index 9e1545b37..465223d07 100644 --- a/scripts/text_condition/train_video21d_65x240p_on_npu.sh +++ b/scripts/text_condition/train_video21d_65x240p_on_npu.sh @@ -7,7 +7,7 @@ accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ - opensora/train/train_t2v.py \ + opensora/train/train_t2v_diffusers.py \ --model LatteT2V-S/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ @@ -39,8 +39,9 @@ accelerate launch \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ - --enable_tiling \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --pretrained "${WEIGHT_PATH}/t2v.pt" \ No newline at end of file + --pretrained "${WEIGHT_PATH}/t2v.pt" \ + --cfg 0.1 \ + --resume_from_checkpoint="latest" \ No newline at end of file diff --git a/scripts/text_condition/train_video3d_65x240p.sh b/scripts/text_condition/train_video3d_65x240p.sh index 441a2faf5..b6e29549c 100644 --- a/scripts/text_condition/train_video3d_65x240p.sh +++ b/scripts/text_condition/train_video3d_65x240p.sh @@ -13,7 +13,7 @@ export OMP_NUM_THREADS=1 accelerate launch \ --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v.py \ + opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ @@ -43,13 +43,11 @@ accelerate launch \ --checkpointing_steps=100 \ --output_dir="testnpu3d_" \ --allow_tf32 \ - --use_deepspeed \ --model_max_length 512 \ --use_image_num 0 \ - --enable_tiling \ --enable_tracker \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --guidance_scale 5.0 \ + --cfg 0.1 \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_video3d_65x240p_on_npu.sh b/scripts/text_condition/train_video3d_65x240p_on_npu.sh index fa6db6654..ea4246e26 100644 --- a/scripts/text_condition/train_video3d_65x240p_on_npu.sh +++ b/scripts/text_condition/train_video3d_65x240p_on_npu.sh @@ -7,7 +7,7 @@ accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ - opensora/train/train_t2v.py \ + opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-S/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ @@ -39,8 +39,9 @@ accelerate launch \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ - --enable_tiling \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --pretrained /home/opensora/yancen/Open-Sora-Plan-dev/PixArt-alpha/transformer/diffusion_pytorch_model.safetensors + --pretrained /home/opensora/yancen/Open-Sora-Plan-dev/PixArt-alpha/transformer/diffusion_pytorch_model.safetensors \ + --cfg 0.1 \ + --resume_from_checkpoint="latest" From 787ad61cf12af68ba6f45c8c14be64d0dcd4eb7d Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Mon, 10 Jun 2024 21:34:51 +0800 Subject: [PATCH 028/134] fix bugs of data preprocessing when set fixed seed --- opensora/adaptor/__init__.py | 0 opensora/adaptor/bf16_optimizer.py | 452 +++ opensora/adaptor/stage_1_and_2.py | 2490 +++++++++++++++++ opensora/adaptor/utils.py | 1043 +++++++ opensora/dataset/t2v_datasets.py | 26 +- opensora/npu_config.py | 17 +- opensora/utils/dataset_utils.py | 2 +- .../train_image_256x256_on_npu.sh | 2 +- .../train_video21d_65x240p_on_npu.sh | 3 +- 9 files changed, 4025 insertions(+), 10 deletions(-) create mode 100644 opensora/adaptor/__init__.py create mode 100644 opensora/adaptor/bf16_optimizer.py create mode 100644 opensora/adaptor/stage_1_and_2.py create mode 100644 opensora/adaptor/utils.py diff --git a/opensora/adaptor/__init__.py b/opensora/adaptor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/opensora/adaptor/bf16_optimizer.py b/opensora/adaptor/bf16_optimizer.py new file mode 100644 index 000000000..dc1ba3de7 --- /dev/null +++ b/opensora/adaptor/bf16_optimizer.py @@ -0,0 +1,452 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +from collections import OrderedDict +import torch +import sys +import os +from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors +from deepspeed import comm as dist +from deepspeed.runtime.constants import PIPE_REPLICATED +from deepspeed.runtime import ZeROOptimizer +from packaging import version as pkg_version + +from deepspeed.git_version_info import version +from deepspeed.runtime.utils import (get_global_norm_of_tensors, clip_tensors_by_global_norm, DummyOptim, + align_dense_tensors, all_gather_dp_groups, bwc_tensor_model_parallel_rank, + is_model_parallel_parameter, see_memory_usage, graph_process) + +from deepspeed.utils import link_hp_params, fragment_address +from deepspeed.checkpoint import enable_universal_checkpoint +from deepspeed.checkpoint.constants import (DS_VERSION, PARTITION_COUNT, BASE_OPTIMIZER_STATE, + SINGLE_PARTITION_OF_FP32_GROUPS, CLIP_GRAD, GROUP_PADDINGS, + PARAM_SLICE_MAPPINGS) + +setattr(sys.modules[__name__], 'fragment_address', fragment_address) + + +class BF16_Optimizer(ZeROOptimizer): + + def __init__(self, + init_optimizer, + param_names, + mpu=None, + clip_grad=0.0, + norm_type=2, + allgather_bucket_size=5000000000, + dp_process_group=None, + timers=None, + grad_acc_dtype=None, + graph_harvesting=False): + super().__init__() + see_memory_usage('begin bf16_optimizer', force=True) + self.timers = timers + self.optimizer = init_optimizer + self.param_names = param_names + self.using_real_optimizer = not isinstance(self.optimizer, DummyOptim) + + assert grad_acc_dtype in [torch.float32, torch.bfloat16 + ], f"BF16Optimizer: Unsupported gradient accumulation data type: {grad_acc_dtype}" + self.grad_acc_dtype = grad_acc_dtype + + self.clip_grad = clip_grad + self.norm_type = norm_type + self.mpu = mpu + self.allgather_bucket_size = int(allgather_bucket_size) + self.dp_process_group = dp_process_group + self.dp_rank = dist.get_rank(group=self.dp_process_group) + self.real_dp_process_group = [dp_process_group for i in range(len(self.optimizer.param_groups))] + + # Use torch (un)flatten ops + self.flatten = _flatten_dense_tensors + self.unflatten = _unflatten_dense_tensors + + #align nccl all-gather send buffers to 4-bye boundary + self.nccl_start_alignment_factor = 512 + print("Set nccl_start_alignment_factor = 52...")# 4-byte alignment/sizeof(fp16) = 2 + + # Build BF16/FP32 groups + self.bf16_groups = [] + self.bf16_groups_flat = [] + self.bf16_partitioned_groups = [] + + self.fp32_groups_flat_partition = [] + + # Maintain different fp32 gradients views for convenience + self.fp32_groups_gradients = [] + self.fp32_groups_gradient_dict = {} + self.fp32_groups_gradients_flat = [] + self.fp32_groups_actual_gradients_flat = [] + self.fp32_groups_gradient_flat_partition = [] + self.fp32_groups_has_gradients = [] + + self.group_paddings = [] + self.graph_harvesting = graph_harvesting + if self.using_real_optimizer: + self._setup_for_real_optimizer() + + see_memory_usage('end bf16_optimizer', force=True) + + def _setup_for_real_optimizer(self): + dp_world_size = dist.get_world_size(group=self.dp_process_group) + self.partition_count = [dp_world_size for i in range(len(self.optimizer.param_groups))] + + for i, param_group in enumerate(self.optimizer.param_groups): + see_memory_usage(f'before initializing group {i}', force=True) + + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + + # grab the original list + trainable_parameters = [param for param in param_group['params'] if param.requires_grad] + self.bf16_groups.append(trainable_parameters) + + # create flat bf16 params + self.bf16_groups_flat.append( + self._flatten_dense_tensors_aligned(self.bf16_groups[i], + self.nccl_start_alignment_factor * dp_world_size)) + + # Make bf16 params point to flat tensor storage + self._update_storage_to_flattened_tensor(tensor_list=self.bf16_groups[i], + flat_tensor=self.bf16_groups_flat[i]) + + # divide flat weights into equal sized partitions + partition_size = self.bf16_groups_flat[i].numel() // dp_world_size + bf16_dp_partitions = [ + self.bf16_groups_flat[i].narrow(0, dp_index * partition_size, partition_size) + for dp_index in range(dp_world_size) + ] + self.bf16_partitioned_groups.append(bf16_dp_partitions) + + # create fp32 params partition + self.fp32_groups_flat_partition.append(bf16_dp_partitions[partition_id].clone().float().detach()) + self.fp32_groups_flat_partition[i].requires_grad = True + + num_elem_list = [t.numel() for t in self.bf16_groups[i]] + + # create fp32 gradients + self.fp32_groups_gradients_flat.append( + torch.zeros_like(self.bf16_groups_flat[i], dtype=self.grad_acc_dtype)) + + # track individual fp32 gradients for entire model + fp32_gradients = self._split_flat_tensor(flat_tensor=self.fp32_groups_gradients_flat[i], + num_elem_list=num_elem_list) + self.fp32_groups_gradients.append(fp32_gradients) + self.fp32_groups_gradient_dict[i] = fp32_gradients + + # flat tensor corresponding to actual fp32 gradients (i.e., minus alignment padding) + length_without_padding = sum(num_elem_list) + self.fp32_groups_actual_gradients_flat.append( + torch.narrow(self.fp32_groups_gradients_flat[i], 0, 0, length_without_padding)) + + # flat tensor corresponding to gradient partition + self.fp32_groups_gradient_flat_partition.append( + torch.narrow(self.fp32_groups_gradients_flat[i], 0, partition_id * partition_size, partition_size)) + + # track fp32 gradient updates + self.fp32_groups_has_gradients.append([False] * len(self.bf16_groups[i])) + + # Record padding required for alignment + if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1: + padding = self.bf16_groups_flat[i].numel() - length_without_padding + else: + padding = 0 + + self.group_paddings.append(padding) + + # update optimizer param groups to reference fp32 params partition + param_group['params'] = [self.fp32_groups_flat_partition[i]] + + see_memory_usage(f'after initializing group {i}', force=True) + + see_memory_usage('before initialize_optimizer', force=True) + self.initialize_optimizer_states() + see_memory_usage('end initialize_optimizer', force=True) + + # Need optimizer states initialized before linking lp to optimizer state + self._link_all_hp_params() + self._enable_universal_checkpoint() + self._param_slice_mappings = self._create_param_mapping() + + def _enable_universal_checkpoint(self): + for lp_param_group in self.bf16_groups: + enable_universal_checkpoint(param_list=lp_param_group) + + def _create_param_mapping(self): + param_mapping = [] + for i, _ in enumerate(self.optimizer.param_groups): + param_mapping_per_group = OrderedDict() + for lp in self.bf16_groups[i]: + if lp._hp_mapping is not None: + lp_name = self.param_names[lp] + param_mapping_per_group[lp_name] = lp._hp_mapping.get_hp_fragment_address() + param_mapping.append(param_mapping_per_group) + + return param_mapping + + def _link_all_hp_params(self): + dp_world_size = dist.get_world_size(group=self.dp_process_group) + for i, _ in enumerate(self.optimizer.param_groups): + # Link bf16 and fp32 params in partition + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_size = self.bf16_groups_flat[i].numel() // dp_world_size + flat_hp_partition = self.fp32_groups_flat_partition[i] + link_hp_params(lp_param_list=self.bf16_groups[i], + flat_hp_partition=flat_hp_partition, + gradient_dict=self.fp32_groups_gradient_dict, + offload_gradient_dict=None, + use_offload=False, + param_group_index=i, + partition_start=partition_id * partition_size, + partition_size=partition_size, + partition_optimizer_state=self.optimizer.state[flat_hp_partition], + dp_group=self.real_dp_process_group[i]) + + def initialize_optimizer_states(self): + """Take an optimizer step with zero-valued gradients to allocate internal + optimizer state. + + This helps prevent memory fragmentation by allocating optimizer state at the + beginning of training instead of after activations have been allocated. + """ + for param_partition, grad_partition in zip(self.fp32_groups_flat_partition, + self.fp32_groups_gradient_flat_partition): + # In case of grad acc dtype different than FP32, need to cast to high precision. + param_partition.grad = grad_partition.to( + param_partition.dtype) if grad_partition.dtype != param_partition.dtype else grad_partition + + self.optimizer.step() + + if self.grad_acc_dtype is not torch.float32: + for param_partition in self.fp32_groups_flat_partition: + param_partition.grad = None + + self.clear_hp_grads() + + def _split_flat_tensor(self, flat_tensor, num_elem_list): + assert sum(num_elem_list) <= flat_tensor.numel() + tensor_list = [] + offset = 0 + for num_elem in num_elem_list: + dense_tensor = torch.narrow(flat_tensor, 0, offset, num_elem) + tensor_list.append(dense_tensor) + offset += num_elem + + return tensor_list + + def _update_storage_to_flattened_tensor(self, tensor_list, flat_tensor): + updated_params = self.unflatten(flat_tensor, tensor_list) + for p, q in zip(tensor_list, updated_params): + p.data = q.data + + def _flatten_dense_tensors_aligned(self, tensor_list, alignment): + return self.flatten(align_dense_tensors(tensor_list, alignment)) + + @torch.no_grad() + def step(self, closure=None): + if closure is not None: + raise NotImplementedError(f'{self.__class__} does not support closure.') + + all_groups_norm = get_global_norm_of_tensors(input_tensors=self.get_grads_for_norm(), + mpu=self.mpu, + norm_type=self.norm_type, + use_graph=self.graph_harvesting) + self._global_grad_norm = all_groups_norm + + assert all_groups_norm > 0. + if self.clip_grad > 0.: + clip_tensors_by_global_norm(input_tensors=self.get_grads_for_norm(for_clipping=True), + max_norm=self.clip_grad, + global_norm=all_groups_norm, + mpu=self.mpu, + use_graph=self.graph_harvesting) + + self.optimizer.step() + + self.update_lp_params() + + self.clear_hp_grads() + + def backward(self, loss, update_hp_grads=True, clear_lp_grads=False, **bwd_kwargs): + """Perform a backward pass and copy the low-precision gradients to the + high-precision copy. + + We copy/accumulate to the high-precision grads now to prevent accumulating in the + bf16 grads after successive backward() calls (i.e., grad accumulation steps > 1) + + The low-precision grads are deallocated during this procedure. + """ + self.clear_lp_grads() + loss.backward(**bwd_kwargs) + + if update_hp_grads: + self.update_hp_grads(clear_lp_grads=clear_lp_grads) + + @torch.no_grad() + def update_hp_grads(self, clear_lp_grads=False): + + def _update_hp_grads_func(clear_lp_grads=False): + for i, group in enumerate(self.bf16_groups): + for j, lp in enumerate(group): + if lp.grad is None: + continue + hp_grad = self.fp32_groups_gradients[i][j] + assert hp_grad is not None, \ + f'high precision param has no gradient, lp param_id = {id(lp)} group_info = [{i}][{j}]' + hp_grad.data.add_(lp.grad.data.to(hp_grad.dtype).view(hp_grad.shape)) + lp._hp_grad = hp_grad + self.fp32_groups_has_gradients[i][j] = True + # clear gradients + if clear_lp_grads: + lp.grad._zero() + + if self.graph_harvesting: + graph_process(False, _update_hp_grads_func, clear_lp_grads) + else: + _update_hp_grads_func(clear_lp_grads) + #cpu op + for i, group in enumerate(self.bf16_groups): + for j, lp in enumerate(group): + if lp.grad is None: + continue + self.fp32_groups_has_gradients[i][j] = True + + @torch.no_grad() + def get_grads_for_reduction(self): + return self.fp32_groups_gradients_flat + + @torch.no_grad() + def get_grads_for_norm(self, for_clipping=False): + grads = [] + tensor_mp_rank = bwc_tensor_model_parallel_rank(mpu=self.mpu) + for i, group in enumerate(self.bf16_groups): + for j, lp in enumerate(group): + if not for_clipping: + if hasattr(lp, PIPE_REPLICATED) and lp.ds_pipe_replicated: + continue + + if not (tensor_mp_rank == 0 or is_model_parallel_parameter(lp)): + continue + + if not self.fp32_groups_has_gradients[i][j]: + continue + + grads.append(self.fp32_groups_gradients[i][j]) + + return grads + + @torch.no_grad() + def update_lp_params(self): + for i, (bf16_partitions, + fp32_partition) in enumerate(zip(self.bf16_partitioned_groups, self.fp32_groups_flat_partition)): + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + bf16_partitions[partition_id].data.copy_(fp32_partition.data) + # print_rank_0(f'update_lp_params {i=} {partition_id=}', force=True) + # if i == 0: + # print_rank_0(f'{fp32_partition[:10]=}', force=True) + + all_gather_dp_groups(groups_flat=self.bf16_groups_flat, + partitioned_param_groups=self.bf16_partitioned_groups, + dp_process_group=self.real_dp_process_group, + start_alignment_factor=self.nccl_start_alignment_factor, + allgather_bucket_size=self.allgather_bucket_size) + + def clear_hp_grads(self): + for flat_gradients in self.fp32_groups_gradients_flat: + flat_gradients.zero_() + + for i, group in enumerate(self.fp32_groups_gradients): + self.fp32_groups_has_gradients[i] = [False] * len(group) + + def clear_lp_grads(self): + for group in self.bf16_groups: + for param in group: + if param.grad is not None: + # Using zero_() fixed memory address for graph replay + param.grad.zero_() + + def state_dict(self): + state_dict = {} + state_dict[CLIP_GRAD] = self.clip_grad + state_dict[BASE_OPTIMIZER_STATE] = self.optimizer.state_dict() + state_dict[SINGLE_PARTITION_OF_FP32_GROUPS] = self.fp32_groups_flat_partition + state_dict[GROUP_PADDINGS] = self.group_paddings + state_dict[PARTITION_COUNT] = self.partition_count + state_dict[DS_VERSION] = version + state_dict[PARAM_SLICE_MAPPINGS] = self._param_slice_mappings + + return state_dict + + # Restore base optimizer fp32 weights bfloat16 weights + def _restore_from_bit16_weights(self): + for i, group in enumerate(self.bf16_groups): + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + for bf16_partitions, fp32_partition in zip(self.bf16_partitioned_groups, self.fp32_groups_flat_partition): + fp32_partition.data.copy_(bf16_partitions[partition_id].data) + + def refresh_fp32_params(self): + self._restore_from_bit16_weights() + + def load_state_dict(self, + state_dict_list, + checkpoint_folder, + load_optimizer_states=True, + load_from_fp32_weights=False, + load_serial=None): + if checkpoint_folder: + self._load_universal_checkpoint(checkpoint_folder, load_optimizer_states, load_from_fp32_weights) + else: + self._load_legacy_checkpoint(state_dict_list, load_optimizer_states, load_from_fp32_weights) + + def _load_legacy_checkpoint(self, state_dict_list, load_optimizer_states=True, load_from_fp32_weights=False): + + dp_rank = dist.get_rank(group=self.dp_process_group) + current_rank_sd = state_dict_list[dp_rank] + + ckpt_version = current_rank_sd.get(DS_VERSION, False) + assert ckpt_version, f"Empty ds_version in checkpoint, not clear how to proceed" + ckpt_version = pkg_version.parse(ckpt_version) + + self.clip_grad = current_rank_sd.get(CLIP_GRAD, self.clip_grad) + + if load_optimizer_states: + self.optimizer.load_state_dict(current_rank_sd[BASE_OPTIMIZER_STATE]) + + if load_from_fp32_weights: + for current, saved in zip(self.fp32_groups_flat_partition, + current_rank_sd[SINGLE_PARTITION_OF_FP32_GROUPS]): + src_tensor = _get_padded_tensor(saved, current.numel()) + current.data.copy_(src_tensor.data) + + if load_optimizer_states: + self._link_all_hp_params() + + def _load_universal_checkpoint(self, checkpoint_folder, load_optimizer_states, load_from_fp32_weights): + self._load_hp_checkpoint_state(checkpoint_folder) + + @property + def param_groups(self): + """Forward the wrapped optimizer's parameters.""" + return self.optimizer.param_groups + + def _load_hp_checkpoint_state(self, checkpoint_dir): + checkpoint_dir = os.path.join(checkpoint_dir, "zero") + tp_rank = bwc_tensor_model_parallel_rank(mpu=self.mpu) + tp_world_size = self.mpu.get_slice_parallel_world_size() + + for i, _ in enumerate(self.optimizer.param_groups): + for lp in self.bf16_groups[i]: + if lp._hp_mapping is not None: + #print(f"Loading {self.param_names[lp]} {tp_rank=} {tp_world_size=}") + lp.load_hp_checkpoint_state(os.path.join(checkpoint_dir, self.param_names[lp]), tp_rank, + tp_world_size) + + +def _get_padded_tensor(src_tensor, size): + if src_tensor.numel() >= size: + return src_tensor + padded_tensor = torch.zeros(size, dtype=src_tensor.dtype, device=src_tensor.device) + slice_tensor = torch.narrow(padded_tensor, 0, 0, src_tensor.numel()) + slice_tensor.data.copy_(src_tensor.data) + return padded_tensor diff --git a/opensora/adaptor/stage_1_and_2.py b/opensora/adaptor/stage_1_and_2.py new file mode 100644 index 000000000..d7337268e --- /dev/null +++ b/opensora/adaptor/stage_1_and_2.py @@ -0,0 +1,2490 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +import torch +import os +from deepspeed import comm as dist +from packaging import version as pkg_version +from collections import OrderedDict +from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors + +from deepspeed.runtime import ZeROOptimizer +from deepspeed.runtime.fp16.loss_scaler import CreateLossScaler +from deepspeed.runtime.utils import (bwc_tensor_model_parallel_rank, get_global_norm, empty_cache, see_memory_usage, + inf, is_model_parallel_parameter, align_dense_tensors, all_gather_dp_groups) + +from deepspeed.runtime.zero.config import ZeroStageEnum +from deepspeed.runtime.zero.offload_config import OffloadDeviceEnum +from deepspeed.ops.adam import DeepSpeedCPUAdam +from deepspeed.utils import logger +from deepspeed.moe.utils import is_moe_param +from deepspeed.git_version_info import version + +from deepspeed.runtime.constants import PIPE_REPLICATED +from deepspeed.accelerator import get_accelerator + +from deepspeed.checkpoint.constants import (DS_VERSION, GROUP_PADDINGS, PARTITION_COUNT, LOSS_SCALER, + SINGLE_PARTITION_OF_FP32_GROUPS, BASE_OPTIMIZER_STATE, + BASE_OPTIMIZER_STATE_STEP, CLIP_GRAD, ZERO_STAGE, PARAM_SLICE_MAPPINGS) +from deepspeed.utils import link_hp_params +from deepspeed.checkpoint import enable_universal_checkpoint + +from deepspeed.utils import groups +# Toggle this to true to enable correctness test +# with gradient partitioning and without +pg_correctness_test = False + +OPTIMIZER_ALLGATHER_TIMER = 'optimizer_allgather' +OPTIMIZER_GRADIENTS_TIMER = 'optimizer_gradients' +OPTIMIZER_STEP_TIMER = 'optimizer_step' +OPTIMIZER_TIMERS = [OPTIMIZER_ALLGATHER_TIMER, OPTIMIZER_GRADIENTS_TIMER, OPTIMIZER_STEP_TIMER] + + +def input(msg): + return + + +def split_half_float_double(tensors): + device_type = get_accelerator().device_name() + dtypes = [ + "torch.{}.HalfTensor".format(device_type), "torch.{}.FloatTensor".format(device_type), + "torch.{}.DoubleTensor".format(device_type), "torch.{}.BFloat16Tensor".format(device_type) + ] + buckets = [] + for i, dtype in enumerate(dtypes): + bucket = [t for t in tensors if t.type() == dtype] + if bucket: + buckets.append(bucket) + return buckets + + +def isclose(a, b, rtol=1e-09, atol=0.0): + return abs(a - b) <= max(rtol * max(abs(a), abs(b)), atol) + + +def lcm(x, y): + from fractions import gcd # or can import gcd from `math` in Python 3 + return x * y // gcd(x, y) + + +def get_alignment_padding(tensor_list, alignment): + num_elements = sum([tensor.numel() for tensor in tensor_list]) + remainder = num_elements % alignment + return (alignment - remainder) if remainder else remainder + + +def move_to_cpu(tensor_list): + for tensor in tensor_list: + tensor.data = tensor.data.cpu() + + +def print_rank_msg(msg): + print(f"rank {dist.get_rank()} - {msg}") + + +def _get_padded_tensor(src_tensor, size): + if src_tensor.numel() >= size: + return src_tensor + padded_tensor = torch.zeros(size, dtype=src_tensor.dtype, device=src_tensor.device) + slice_tensor = torch.narrow(padded_tensor, 0, 0, src_tensor.numel()) + slice_tensor.data.copy_(src_tensor.data) + return padded_tensor + + +class DeepSpeedZeroOptimizer(ZeROOptimizer): + """ + DeepSpeedZeroOptimizer designed to reduce the memory footprint + required for training large deep learning models. + + For more details please see ZeRO: Memory Optimization Towards Training A Trillion Parameter Models + https://arxiv.org/abs/1910.02054 + + For usage examples, refer to TODO: DeepSpeed Tutorial + + """ + + def __init__(self, + init_optimizer, + param_names, + timers, + static_loss_scale=1.0, + dynamic_loss_scale=False, + dynamic_loss_args=None, + verbose=True, + contiguous_gradients=True, + reduce_bucket_size=500000000, + use_multi_rank_bucket_allreduce=True, + allgather_bucket_size=5000000000, + dp_process_group=None, + expert_parallel_group=None, + expert_data_parallel_group=None, + reduce_scatter=True, + overlap_comm=False, + offload_optimizer_config=None, + mpu=None, + clip_grad=0.0, + gradient_accumulation_dtype=torch.float32, + communication_data_type=torch.float16, + postscale_gradients=True, + gradient_predivide_factor=1.0, + gradient_accumulation_steps=1, + ignore_unused_parameters=True, + partition_grads=True, + round_robin_gradients=False, + has_moe_layers=False, + fp16_master_weights_and_gradients=False, + elastic_checkpoint=False): + + if offload_optimizer_config is not None and offload_optimizer_config.device != OffloadDeviceEnum.none: + self.cpu_offload = True + self.cpu_offload_pin_memory = offload_optimizer_config.pin_memory + else: + self.cpu_offload = False + self.cpu_offload_pin_memory = False + + if dist.get_rank() == 0: + logger.info(f"Reduce bucket size {reduce_bucket_size}") + logger.info(f"Allgather bucket size {allgather_bucket_size}") + logger.info(f"CPU Offload: {self.cpu_offload}") + logger.info(f'Round robin gradient partitioning: {round_robin_gradients}') + # The fused optimizer does all the work. We need this layer for two reason: + # 1. maintain same user API from apex.fp16_utils + # 2. keep common stuff here in case we need to add ne552w fused optimizer later + + self.elastic_checkpoint = elastic_checkpoint + self.param_names = param_names + self.mpu = mpu + # differences from apex.fp16_utils: + # - assume all model params in fp16 + # - assume all params requires grad + # - flat by groups, not keeping state. TODO: remove state explicitly? + # - master grad and unflat master weight never exist. TODO: a way to save out unflat master? + if not get_accelerator().is_available(): + raise SystemError("Accelerator is not detected, cannot perform low precision training (e.g., fp16, bf16).") + self.optimizer = init_optimizer + + # Use torch (un)flatten ops + self.flatten = _flatten_dense_tensors + self.unflatten = _unflatten_dense_tensors + + # ZeRO stage 1 (False) or 2 (True) + self.partition_gradients = partition_grads + self.zero_stage_string = "ZeRO-2" if partition_grads else "ZeRO-1" + + self.timers = timers + + self.reduce_scatter = reduce_scatter + + self.overlap_comm = overlap_comm + + self.deepspeed_adam_offload = self.cpu_offload + + self.device = get_accelerator().current_device_name() if not self.cpu_offload else 'cpu' + + self.dp_process_group = dp_process_group + self.sequence_parallel_size = groups._get_sequence_parallel_world_size() + #expert parallel group + self.ep_process_group = expert_parallel_group + + #data parallel group for experts + self.expert_dp_process_group = expert_data_parallel_group + + #data parallel size for non-experts + dp_size = dist.get_world_size(group=self.dp_process_group) + + #For MoE models this maybe different for different param group + #It will be modified during MoE setup later in the init + self.real_dp_process_group = [dp_process_group for i in range(len(self.optimizer.param_groups))] + self.partition_count = [dp_size for i in range(len(self.optimizer.param_groups))] + + self.is_gradient_accumulation_boundary = True + + # CPU-Offload requires contiguous gradients + self.contiguous_gradients = contiguous_gradients or self.cpu_offload + + self.has_moe_layers = has_moe_layers + if self.has_moe_layers: + self._configure_moe_settings() + self._global_grad_norm = 0. + + if mpu is None: + self.model_parallel_group = None + self.model_parallel_world_size = 1 + self.model_parallel_rank = 0 + else: + self.model_parallel_group = mpu.get_model_parallel_group() + self.model_parallel_world_size = mpu.get_model_parallel_world_size() + self.model_parallel_rank = bwc_tensor_model_parallel_rank(mpu) + + self.overflow = False + self.clip_grad = clip_grad + self.communication_data_type = communication_data_type + self.gradient_predivide_factor = gradient_predivide_factor + self.postscale_gradients = postscale_gradients + self.gradient_accumulation_steps = gradient_accumulation_steps + self.micro_step_id = 0 + self.ignore_unused_parameters = ignore_unused_parameters + self.round_robin_gradients = round_robin_gradients + + self.extra_large_param_to_reduce = None + self.fp16_master_weights_and_gradients = fp16_master_weights_and_gradients + + if self.fp16_master_weights_and_gradients: + assert self.cpu_offload and type(self.optimizer) in [DeepSpeedCPUAdam], \ + f"fp16_master_and_gradients requires optimizer to support keeping fp16 master and gradients while keeping the optimizer states in fp32."\ + f"Currently only supported using ZeRO-Offload with DeepSpeedCPUAdam. But current setting is ZeRO-Offload:{self.cpu_offload} and optimizer type {type(self.optimizer)}." \ + f"Either disable fp16_master_weights_and_gradients or enable {self.zero_stage_string} Offload with DeepSpeedCPUAdam." + + if self.reduce_scatter: + valid_reduce_scatter_dtypes = (torch.float16, torch.bfloat16, torch.float32) + assert self.communication_data_type in valid_reduce_scatter_dtypes, f"{self.zero_stage_string} supports {valid_reduce_scatter_dtypes} communication_data_type with reduce scatter enabled. Got: '{self.communication_data_type}'" + assert self.gradient_predivide_factor == 1.0, "gradient_predivide_factor != 1.0 is not yet supported with {self.zero_stage_string} with reduce scatter enabled" + assert self.postscale_gradients, "pre-scale gradients is not yet supported with {self.zero_stage_string} with reduce scatter enabled" + + # param flattened by groups + self.bit16_groups = [] + self.bit16_groups_flat = [] + + # param partitioned by data parallel degree + # this will contain a list of equal sized tensors + # each of which will be updated by a different process + self.parallel_partitioned_bit16_groups = [] + + # a single 32-bit partition of the parallel partitioned parameters + # that this process will update + self.single_partition_of_fp32_groups = [] + + # param partition info + + # These are the parameters in each group that will not be updated by this process directly + self.params_not_in_partition = [] + + # These are the parameters that will be updated by this process directly + self.params_in_partition = [] + + # Offset from the first parameter in the self.params_in_partition + # the parameter boundaries may not align with partition boundaries + # so we need to keep track of the offset + self.first_offset = [] + + # number of elements per partition in each group + self.partition_size = [] + + # align nccl all-gather send buffers to 4-byte boundary + self.nccl_start_alignment_factor = 512 # 4-byte alignment/sizeof(fp16) = 2 + print("Set nccl_start_alignment_factor = 52...") + + assert ( + allgather_bucket_size % self.nccl_start_alignment_factor == 0 + ), f"allgather_bucket_size must be a multiple of nccl_start_alignment_factor, {self.nccl_start_alignment_factor} " + + self.all_reduce_print = False + self.dtype = self.optimizer.param_groups[0]['params'][0].dtype + self.gradient_accumulation_dtype = gradient_accumulation_dtype + + if self.dtype != self.gradient_accumulation_dtype: + self.use_separate_grad_accum = True + else: + self.use_separate_grad_accum = False + if self.use_separate_grad_accum and not self.partition_gradients: + self.use_grad_accum_attribute = True + else: + self.use_grad_accum_attribute = False + + self.round_robin_bit16_groups = [] + self.round_robin_bit16_indices = [] + + # Use different parallel to do all_to_all_reduce related things + # padding on each partition for alignment purposes + self.groups_padding = [] + # loop to deal with groups + for i, param_group in enumerate(self.optimizer.param_groups): + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + + # push this group to list before modify + # TODO: Explore simplification that avoids the extra book-keeping by pushing the reordered group + trainable_parameters = [] + for param in param_group['params']: + if param.requires_grad: + param.grad_accum = None + trainable_parameters.append(param) + self.bit16_groups.append(trainable_parameters) + + # not sure why apex was cloning the weights before flattening + # removing cloning here + + see_memory_usage(f"Before moving param group {i} to CPU") + # move all the parameters to cpu to free up GPU space for creating flat buffer + move_to_cpu(self.bit16_groups[i]) + empty_cache() + see_memory_usage(f"After moving param group {i} to CPU", force=False) + + # Reorder group parameters for load balancing of gradient partitioning during backward among ranks. + # This ensures that gradients are reduced in a fashion such that ownership round robins among the ranks. + # For example, rather than 3 gradients (g_n+2, g_n+1, g_n) that are reduced consecutively belonging + # to the same rank, instead they will belong to 3 ranks (r_m+2, r_m+1, r_m). + if self.round_robin_gradients: + round_robin_tensors, round_robin_indices = self._round_robin_reorder( + self.bit16_groups[i], dist.get_world_size(group=self.real_dp_process_group[i])) + else: + round_robin_tensors = self.bit16_groups[i] + round_robin_indices = list(range(len(self.bit16_groups[i]))) + + self.round_robin_bit16_groups.append(round_robin_tensors) + self.round_robin_bit16_indices.append(round_robin_indices) + + # create flat buffer in CPU and move to GPU + self.bit16_groups_flat.append( + self.flatten_dense_tensors_aligned( + self.round_robin_bit16_groups[i], + self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i])).to( + get_accelerator().current_device_name())) + see_memory_usage(f"After flattening and moving param group {i} to GPU", force=False) + + # Record padding required for alignment + if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1: + padding = self.bit16_groups_flat[i].numel() - sum( + [t.numel() for t in self.round_robin_bit16_groups[i]]) + else: + padding = 0 + self.groups_padding.append(padding) + + if dist.get_rank(group=self.real_dp_process_group[i]) == 0: + see_memory_usage(f"After Flattening and after emptying param group {i} cache", force=False) + + # set model bit16 weight to slices of flattened buffer + self._update_model_bit16_weights(i) + + # divide the flat weights into near equal partition equal to the data parallel degree + # each process will compute on a different part of the partition + data_parallel_partitions = self.get_data_parallel_partitions(self.bit16_groups_flat[i], i) + self.parallel_partitioned_bit16_groups.append(data_parallel_partitions) + + # verify that data partition start locations are 4-byte aligned + for partitioned_data in data_parallel_partitions: + assert (partitioned_data.data_ptr() % (2 * self.nccl_start_alignment_factor) == 0) + + # A partition of the fp32 master weights that will be updated by this process. + # Note that the params in single_partition_of_fp32_groups is cloned and detached + # from the origin params of the model. + if not fp16_master_weights_and_gradients: + self.single_partition_of_fp32_groups.append(self.parallel_partitioned_bit16_groups[i][partition_id].to( + self.device).clone().float().detach()) + else: + self.single_partition_of_fp32_groups.append(self.parallel_partitioned_bit16_groups[i][partition_id].to( + self.device).clone().half().detach()) + + # Set local optimizer to have flat params of its own partition. + # After this, the local optimizer will only contain its own partition of params. + # In that case, the local optimizer only saves the states(momentum, variance, etc.) related to its partition's params(zero stage1). + self.single_partition_of_fp32_groups[ + i].requires_grad = True # keep this in case internal optimizer uses it + param_group['params'] = [self.single_partition_of_fp32_groups[i]] + + partition_size = len(self.bit16_groups_flat[i]) / dist.get_world_size(group=self.real_dp_process_group[i]) + params_in_partition, params_not_in_partition, first_offset = self.get_partition_info( + self.round_robin_bit16_groups[i], partition_size, partition_id) + + self.partition_size.append(partition_size) + self.params_in_partition.append(params_in_partition) + self.params_not_in_partition.append(params_not_in_partition) + self.first_offset.append(first_offset) + + self.reduce_bucket_size = int(reduce_bucket_size) + self.use_multi_rank_bucket_allreduce = use_multi_rank_bucket_allreduce + self.allgather_bucket_size = int(allgather_bucket_size) + + self.reduction_stream = None if get_accelerator().is_synchronized_device() else get_accelerator().Stream() + #self.copy_grad_stream = get_accelerator().Stream() + self.callback_queued = False + + self.param_dict = {} + + # map between param_id and bool to specify if a param is in this partition + self.is_param_in_current_partition = {} + + self.grads_in_ipg_bucket = [] + self.params_in_ipg_bucket = [] + self.elements_in_ipg_bucket = 0 + self.params_already_reduced = [] + self._release_ipg_buffers() + self.previous_reduced_grads = None + self.ipg_bucket_has_moe_params = False + + # simplified param id + self.param_id = {} + + #interesting code: unique ids being assigned to individual parameters + largest_param_numel = 0 + count = 0 + for i, params_group in enumerate(self.bit16_groups): + for param in params_group: + unique_id = id(param) + self.param_id[unique_id] = count + self.param_dict[count] = param + self.params_already_reduced.append(False) + if param.numel() > largest_param_numel: + largest_param_numel = param.numel() + count = count + 1 + + for param_group in self.params_in_partition: + for param in param_group: + self.is_param_in_current_partition[self.get_param_id(param)] = True + + for param_group in self.params_not_in_partition: + for param in param_group: + self.is_param_in_current_partition[self.get_param_id(param)] = False + + if self.cpu_offload: + self.accumulated_grads_in_cpu = {} + self.norm_for_param_grads = {} + self.local_overflow = False + self.grad_position = {} + self.temp_grad_buffer_for_cpu_offload = torch.zeros(largest_param_numel, + device=self.device, + dtype=self.dtype) + if self.cpu_offload_pin_memory: + self.temp_grad_buffer_for_cpu_offload = get_accelerator().pin_memory( + self.temp_grad_buffer_for_cpu_offload) + self.temp_grad_buffer_for_gpu_offload = torch.zeros(largest_param_numel, + device=get_accelerator().current_device_name(), + dtype=self.dtype) + for i, params_group in enumerate(self.bit16_groups): + self.get_grad_position(i, self.params_in_partition[i], self.first_offset[i], self.partition_size[i]) + + # mapping from parameter to partition that it belongs to + self.param_to_partition_ids = {} + + # stores if a partition has been reduced in this step + self.is_partition_reduced = {} + + # number of grads in partition that still need to be computed + self.remaining_grads_in_partition = {} + + # total number of grads in partition + self.total_grads_in_partition = {} + + # stores if a grad in a partition has been computed or not + self.is_grad_computed = {} + + # stores the offset at which a parameter gradient needs to be inserted in a partition + self.grad_partition_insertion_offset = {} + + # the offset in the gradient at which it must be inserted at the beginning of the partition + self.grad_start_offset = {} + + # will store the averaged gradients required by this partition + self.averaged_gradients = {} + + # For cpu_offload, will store the averaged gradients required by this partition + self.offload_gradient_dict = {} + + # store index of first parameter in each partition + self.first_param_index_in_partition = {} + + # initializes all data structures for implementing gradient partitioning + self.initialize_gradient_partitioning_data_structures() + + # resets the data structure value for the next backward propagation + self.reset_partition_gradient_structures() + + # creates backward hooks for gradient partitioning + if self.partition_gradients or self.overlap_comm: + self.create_reduce_and_remove_grad_hooks() + + self.custom_loss_scaler = False + self.external_loss_scale = None + + # we may have a way of fusing dynamic scale. Do not support for now + self.loss_scaler = CreateLossScaler(dtype=self.dtype, + static_loss_scale=static_loss_scale, + dynamic_scaling=dynamic_loss_scale, + dynamic_loss_args=dynamic_loss_args) + self.dynamic_loss_scale = self.loss_scaler.dynamic + + if self.dtype != torch.float16: + # Only fp16 should use dynamic loss scaling + assert self.loss_scaler.cur_scale == 1.0 + assert not self.dynamic_loss_scale + + see_memory_usage("Before initializing optimizer states", force=True) + self.initialize_optimizer_states() + see_memory_usage("After initializing optimizer states", force=True) + + if dist.get_rank() == 0: + logger.info(f"optimizer state initialized") + + if dist.get_rank(group=self.dp_process_group) == 0: + see_memory_usage(f"After initializing ZeRO optimizer", force=True) + + self._link_all_hp_params() + self._enable_universal_checkpoint() + self._param_slice_mappings = self._create_param_mapping() + + def _enable_universal_checkpoint(self): + for lp_param_group in self.bit16_groups: + enable_universal_checkpoint(param_list=lp_param_group) + + def _create_param_mapping(self): + param_mapping = [] + for i, _ in enumerate(self.optimizer.param_groups): + param_mapping_per_group = OrderedDict() + for lp in self.bit16_groups[i]: + if lp._hp_mapping is not None: + lp_name = self.param_names[lp] + param_mapping_per_group[lp_name] = lp._hp_mapping.get_hp_fragment_address() + param_mapping.append(param_mapping_per_group) + + return param_mapping + + def _link_all_hp_params(self): + dp_world_size = dist.get_world_size(group=self.dp_process_group) + if self.cpu_offload: + self._get_offload_gradient_dict() + + for i, _ in enumerate(self.optimizer.param_groups): + # Link bit16 and fp32 params in partition + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_size = self.bit16_groups_flat[i].numel() // dp_world_size + flat_hp_partition = self.single_partition_of_fp32_groups[i] + link_hp_params(lp_param_list=self.bit16_groups[i], + flat_hp_partition=flat_hp_partition, + gradient_dict=self.averaged_gradients, + offload_gradient_dict=self.offload_gradient_dict, + use_offload=self.cpu_offload, + param_group_index=i, + partition_start=partition_id * partition_size, + partition_size=partition_size, + partition_optimizer_state=self.optimizer.state[flat_hp_partition], + dp_group=self.real_dp_process_group[i]) + + def is_moe_group(self, group): + return 'moe' in group and group['moe'] + + def _configure_moe_settings(self): + # if we're using ZeRO stage 2, ensure contiguous gradients are used + if self.partition_gradients: + assert self.contiguous_gradients, "Contiguous Gradients in ZeRO Stage 2 must be set to True for MoE. Other code paths are not tested with MoE" + # NOTE: To run ZeRO stage 1 with MoE, we need to set self.contiguous_gradients to True or ignore the assertion + if not self.partition_gradients and not self.contiguous_gradients: + logger.warn( + "ZeRO Stage 1 has not been thoroughly tested with MoE. This configuration is still experimental.") + assert self.reduce_scatter, "Reduce Scatter in ZeRO Stage 2 must be set to True for MoE. Other code paths are not tested with MoE" + + assert any( + [self.is_moe_group(group) for group in self.optimizer.param_groups] + ), "The model has moe layers, but None of the param groups are marked as MoE. Create a param group with 'moe' key set to True before creating optimizer" + self.is_moe_param_group = [] + for i, group in enumerate(self.optimizer.param_groups): + if self.is_moe_group(group): + assert all([is_moe_param(param) + for param in group['params']]), "All params in MoE group must be MoE params" + self.real_dp_process_group[i] = self.expert_dp_process_group[group['name']] + self.partition_count[i] = dist.get_world_size(group=self.expert_dp_process_group[group['name']]) + self.is_moe_param_group.append(True) + else: + self.is_moe_param_group.append(False) + + assert self.expert_dp_process_group is not None, "Expert data parallel group should be configured with MoE" + assert self.ep_process_group is not None, "Expert parallel group should be configured with MoE" + + def _update_model_bit16_weights(self, group_index): + updated_params = self.unflatten(self.bit16_groups_flat[group_index], + self.round_robin_bit16_groups[group_index]) + for p, q in zip(self.round_robin_bit16_groups[group_index], updated_params): + p.data = q.data + + # set model fp16 weight to slices of reordered flattened buffer + for param_index, param in enumerate(self.bit16_groups[group_index]): + new_index = self.round_robin_bit16_indices[group_index][param_index] + param.data = self.round_robin_bit16_groups[group_index][new_index].data + + def _round_robin_reorder(self, tensor_list, num_partitions): + + # disable round robin if need to debug something + # return tensor_list, list(range(len(tensor_list))) + + partition_tensors = {} + + for i, tensor in enumerate(tensor_list): + j = i % num_partitions + if not j in partition_tensors: + partition_tensors[j] = [] + partition_tensors[j].append((i, tensor)) + + reordered_tensors = [] + reordered_indices = {} + + for partition_index in partition_tensors.keys(): + for i, (original_index, tensor) in enumerate(partition_tensors[partition_index]): + reordered_indices[original_index] = len(reordered_tensors) + reordered_tensors.append(tensor) + + return reordered_tensors, reordered_indices + + def _release_ipg_buffers(self): + if self.contiguous_gradients: + self.ipg_buffer = None + self.grads_in_partition = None + self.grads_in_partition_offset = 0 + + def initialize_optimizer_states(self): + + for i, group in enumerate(self.bit16_groups): + single_grad_partition = torch.zeros(int(self.partition_size[i]), + dtype=self.single_partition_of_fp32_groups[i].dtype, + device=self.device) + self.single_partition_of_fp32_groups[i].grad = get_accelerator().pin_memory( + single_grad_partition) if self.cpu_offload_pin_memory else single_grad_partition + + # Initialize the optimizer states with the flattened fp32 partition. + # State initialization for the Adagrad optimizer occurs at construction as opposed to other optimizers + # which do lazy initialization of the state at the first call to step. + if isinstance(self.optimizer, torch.optim.Adagrad): + self.optimizer = torch.optim.Adagrad(self.single_partition_of_fp32_groups, **self.optimizer.defaults) + else: + self.optimizer.step() + + if not self.cpu_offload: + for group in self.single_partition_of_fp32_groups: + group.grad = None #class init + + return + + ######################################################################### + #################### ZeRO Stage 1 - reduce gradients #################### + ######################################################################### + def reduce_gradients(self, pipeline_parallel=False): + world_size = dist.get_world_size(self.dp_process_group) + my_rank = dist.get_rank(self.dp_process_group) + + # with PP we must create ipg buffer, since backward is handled outside zero + if pipeline_parallel and self.contiguous_gradients: + self.ipg_buffer = [] + buf_0 = torch.empty(int(self.reduce_bucket_size), + dtype=self.dtype, + device=get_accelerator().current_device_name()) + self.ipg_buffer.append(buf_0) + self.ipg_index = 0 + + if not self.overlap_comm: + for i, group in enumerate(self.bit16_groups): + for param in group: + grad_reduc = self.get_gradient_for_reduction(param) + if grad_reduc is not None: + self.reduce_ready_partitions_and_remove_grads(param, i) + # reduce any pending grads in either hook/non-hook case + self.overlapping_partition_gradients_reduce_epilogue() + + ######################################################################### + #########################ZeRO Partition Gradients######################## + ######################################################################### + + def get_first_param_index(self, group_id, param_group, partition_id): + for index, param in enumerate(param_group): + param_id = self.get_param_id(param) + if partition_id in self.param_to_partition_ids[group_id][param_id]: + return index + return None + + def initialize_gradient_partitioning_data_structures(self): + + for i, param_group in enumerate(self.round_robin_bit16_groups): + total_partitions = dist.get_world_size(group=self.real_dp_process_group[i]) + + self.param_to_partition_ids[i] = {} + self.is_partition_reduced[i] = {} + self.total_grads_in_partition[i] = {} + self.remaining_grads_in_partition[i] = {} + self.is_grad_computed[i] = {} + self.grad_partition_insertion_offset[i] = {} + self.grad_start_offset[i] = {} + self.first_param_index_in_partition[i] = {} + + for partition_id in range(total_partitions): + self.is_grad_computed[i][partition_id] = {} + self.grad_partition_insertion_offset[i][partition_id] = {} + self.grad_start_offset[i][partition_id] = {} + self.total_grads_in_partition[i][partition_id] = 0 + self.initialize_gradient_partition(i, param_group, partition_id) + self.is_partition_reduced[i][partition_id] = False + self.first_param_index_in_partition[i][partition_id] = self.get_first_param_index( + i, param_group, partition_id) + + def independent_gradient_partition_epilogue(self): + self.report_ipg_memory_usage(f"In ipg_epilogue before reduce_ipg_grads", 0) + self.reduce_ipg_grads() + self.report_ipg_memory_usage(f"In ipg_epilogue after reduce_ipg_grads", 0) + + # if dist.get_rank() == 0: + # logger.info("Params already reduced %s", self.params_already_reduced) + for i in range(len(self.params_already_reduced)): + self.params_already_reduced[i] = False + + if self.overlap_comm: + get_accelerator().synchronize() + # It is safe to clear previously reduced grads of other partitions + self._clear_previous_reduced_grads() + + if self.cpu_offload is False: + for i, _ in enumerate(self.bit16_groups): + + if not i in self.averaged_gradients or self.averaged_gradients[i] is None: + self.averaged_gradients[i] = self.get_flat_partition( + self.params_in_partition[i], + self.first_offset[i], + self.partition_size[i], + dtype=self.gradient_accumulation_dtype, + device=get_accelerator().current_device_name(), + return_tensor_list=True) + else: + avg_new = self.get_flat_partition(self.params_in_partition[i], + self.first_offset[i], + self.partition_size[i], + dtype=self.gradient_accumulation_dtype, + device=get_accelerator().current_device_name(), + return_tensor_list=True) + + for accumulated_grad, new_avg_grad in zip(self.averaged_gradients[i], avg_new): + accumulated_grad.add_(new_avg_grad) + + self._release_ipg_buffers() + + # No need to keep the gradients anymore. + # All gradients required by the step + # are in self.averaged_gradients + self.zero_grad(set_to_none=True) + see_memory_usage(f"End ipg_epilogue") + + # resets all partition to no reduced + # sets remaining grads to the total number of grads in each partition + # set is grad computed to false for all grads in partition + def reset_partition_gradient_structures(self): + for i, _ in enumerate(self.bit16_groups): + total_partitions = dist.get_world_size(group=self.real_dp_process_group[i]) + for partition_id in range(total_partitions): + self.is_partition_reduced[i][partition_id] = False + self.remaining_grads_in_partition[i][partition_id] = self.total_grads_in_partition[i][partition_id] + + for param_id in self.is_grad_computed[i][partition_id]: + self.is_grad_computed[i][partition_id][param_id] = False + + def initialize_gradient_partition(self, i, param_group, partition_id): + + def set_key_value_list(dictionary, key, value): + if key in dictionary: + dictionary[key].append(value) + else: + dictionary[key] = [value] + + def increment_value(dictionary, key): + if key in dictionary: + dictionary[key] += 1 + else: + dictionary[key] = 1 + + partition_size = self.partition_size[i] + + start_index = partition_size * partition_id + end_index = partition_size * (partition_id + 1) + + current_index = 0 + first_offset = 0 + + for param in param_group: + + param_size = param.numel() + param_id = self.get_param_id(param) + + if start_index <= current_index < end_index: + set_key_value_list(self.param_to_partition_ids[i], param_id, partition_id) + increment_value(self.total_grads_in_partition[i], partition_id) + + self.is_grad_computed[i][partition_id][param_id] = False + + self.grad_partition_insertion_offset[i][partition_id][param_id] = current_index - start_index + self.grad_start_offset[i][partition_id][param_id] = 0 + + elif current_index < start_index < (current_index + param_size): + assert (first_offset == 0 + ), "This can happen either zero or only once as this must be the first tensor in the partition" + first_offset = start_index - current_index + + set_key_value_list(self.param_to_partition_ids[i], param_id, partition_id) + increment_value(self.total_grads_in_partition[i], partition_id) + + self.is_grad_computed[i][partition_id][param_id] = False + + self.grad_partition_insertion_offset[i][partition_id][param_id] = 0 + self.grad_start_offset[i][partition_id][param_id] = first_offset + + current_index = current_index + param_size + + def overlapping_partition_gradients_reduce_epilogue(self): + self.independent_gradient_partition_epilogue() + + def fill_grad_accum_attribute(self): + for group in self.bit16_groups: + for param in group: + if param.grad is not None: + if param.grad_accum is None: + param.grad_accum = param.grad.to(self.gradient_accumulation_dtype) + else: + param.grad_accum.add_( + param.grad.to(self.gradient_accumulation_dtype).view(param.grad_accum.shape)) + param.grad = None + + def get_gradient_for_reduction(self, param): + if self.use_grad_accum_attribute: + return param.grad_accum.to(self.dtype) if param.grad_accum is not None else None + else: + return param.grad + + def get_param_gradient_attribute(self, param): + return param.grad_accum if self.use_grad_accum_attribute else param.grad + + # Clear the tensor the reduction gradient attribute is pointing to + def clear_grad_attribute(self, param): + if self.use_grad_accum_attribute: + param.grad_accum = None + else: + param.grad = None + + def create_reduce_and_remove_grad_hooks(self): + self.grad_accs = [] + for i, param_group in enumerate(self.bit16_groups): + for param in param_group: + if param.requires_grad: + + def wrapper(param, i): + param_tmp = param.expand_as(param) + grad_acc = param_tmp.grad_fn.next_functions[0][0] + + def reduce_partition_and_remove_grads(*notneeded): + self.reduce_ready_partitions_and_remove_grads(param, i) + + grad_acc.register_hook(reduce_partition_and_remove_grads) + self.grad_accs.append(grad_acc) + + wrapper(param, i) + + def get_param_id(self, param): + unique_id = id(param) + return self.param_id[unique_id] + + def report_ipg_memory_usage(self, tag, param_elems): + elem_count = self.elements_in_ipg_bucket + param_elems + percent_of_bucket_size = (100.0 * elem_count) // self.reduce_bucket_size + see_memory_usage( + f"{tag}: elems in_bucket {self.elements_in_ipg_bucket} param {param_elems} max_percent {percent_of_bucket_size}" + ) + + # create a flat tensor aligned at the alignment boundary + def flatten_dense_tensors_aligned(self, tensor_list, alignment): + return self.flatten(align_dense_tensors(tensor_list, alignment)) + + ############### Independent Partition Gradient ######################## + def reduce_independent_p_g_buckets_and_remove_grads(self, param, i): + + grad_reduc = self.get_gradient_for_reduction(param) + if self.elements_in_ipg_bucket + param.numel() > self.reduce_bucket_size: + self.report_ipg_memory_usage("In ipg_remove_grads before reduce_ipg_grads", param.numel()) + self.reduce_ipg_grads() + if self.contiguous_gradients and self.overlap_comm: + # Swap ipg_index between 0 and 1 + self.ipg_index = 1 - self.ipg_index + self.report_ipg_memory_usage("In ipg_remove_grads after reduce_ipg_grads", param.numel()) + + param_id = self.get_param_id(param) + assert self.params_already_reduced[param_id] == False, \ + f"The parameter {param_id} has already been reduced. \ + Gradient computed twice for this partition. \ + Multiple gradient reduction is currently not supported" + + if self.contiguous_gradients: + if param.numel() > self.reduce_bucket_size: + self.extra_large_param_to_reduce = param + else: + # keeping the gradients contiguous to prevent memory fragmentation, and avoid flattening + new_grad_tensor = self.ipg_buffer[self.ipg_index].narrow(0, self.elements_in_ipg_bucket, param.numel()) + new_grad_tensor.copy_(grad_reduc.view(-1)) + grad_reduc.data = new_grad_tensor.data.view_as(grad_reduc) + + self.elements_in_ipg_bucket += param.numel() + + assert grad_reduc is not None, f"rank {dist.get_rank()} - Invalid to reduce Param {param_id} with None gradient" + + self.grads_in_ipg_bucket.append(grad_reduc) + self.params_in_ipg_bucket.append((i, param, param_id)) + + #make sure the average tensor function knows how to average the gradients + if is_moe_param(param): + self.ipg_bucket_has_moe_params = True + + self.report_ipg_memory_usage("End ipg_remove_grads", 0) + + def print_rank_0(self, message): + if dist.get_rank() == 0: + logger.info(message) + + def gradient_reduction_w_predivide(self, tensor): + + dp_world_size = dist.get_world_size(group=self.dp_process_group) + + tensor_to_allreduce = tensor + + if self.communication_data_type != tensor.dtype: + tensor_to_allreduce = tensor.to(self.communication_data_type) + + if self.postscale_gradients: + if self.gradient_predivide_factor != 1.0: + tensor_to_allreduce.mul_(1. / self.gradient_predivide_factor) + + dist.all_reduce(tensor_to_allreduce, group=self.dp_process_group) + + if self.gradient_predivide_factor != dp_world_size: + tensor_to_allreduce.mul_(self.gradient_predivide_factor / + (dp_world_size / float(self.sequence_parallel_size))) + else: + tensor_to_allreduce.div_(dp_world_size / float(self.sequence_parallel_size)) + dist.all_reduce(tensor_to_allreduce, group=self.dp_process_group) + + if self.communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce: + tensor.copy_(tensor_to_allreduce) + + return tensor + + def allreduce_and_copy_with_multiple_ranks(self, + small_bucket, + log=None, + divide=True, + process_group=None, + bucket_ranks=None): + process_group = self.dp_process_group if process_group is None else process_group + allreduced = self.allreduce_bucket(small_bucket, log=log, divide=divide, process_group=process_group) + for buf, synced, bucket_rank in zip(small_bucket, self.unflatten(allreduced, small_bucket), bucket_ranks): + if dist.get_rank(group=process_group) == bucket_rank: + buf.copy_(synced) + + def allreduce_and_scatter(self, bucket, numel_per_bucket=500000000, log=None, divide=True, process_group=None): + small_bucket = [] + small_bucket_ranks = [] + numel = 0 + allreduce_sizes = [] + + for i, bucket_elem in enumerate(bucket): + rank, tensor = bucket_elem + small_bucket.append(tensor) + small_bucket_ranks.append(rank) + numel = numel + tensor.numel() + if numel > numel_per_bucket: + self.allreduce_and_copy_with_multiple_ranks(small_bucket, + log=None, + divide=divide, + process_group=process_group, + bucket_ranks=small_bucket_ranks) + small_bucket = [] + small_bucket_ranks = [] + numel = 0 + + if len(small_bucket) > 0: + self.allreduce_and_copy_with_multiple_ranks(small_bucket, + log=None, + divide=divide, + process_group=process_group, + bucket_ranks=small_bucket_ranks) + + def average_tensor(self, tensor): + if self.overlap_comm: + stream = self.reduction_stream + if not get_accelerator().is_synchronized_device(): + stream.wait_stream(get_accelerator().current_stream()) + else: + stream = get_accelerator().current_stream() + + with get_accelerator().stream(stream): + if not self.reduce_scatter: + self.gradient_reduction_w_predivide(tensor) + return + + # Accumulate destination ranks and bucket offsets for each gradient slice. + # Note: potential future optimization, record access pattern of parameters + # in backward pass and partition gradients w.r.t. access pattern so that our + # bucket is guaranteed to be contiguous w.r.t. ranks + rank_and_offsets = [] + real_dp_process_group = [] + curr_size = 0 + prev_id, prev_process_group = -1, None + + process_group = self.dp_process_group + # count = 0 + for i, param, param_id in self.params_in_ipg_bucket: + + process_group = self.dp_process_group + grad_reduc = self.get_gradient_for_reduction(param) + #Averages gradients at parameter level if ipg has a moe param + #Otherwise averaging is done at the entire buffer level at the end of the loop + # MoE param have different groups + if self.ipg_bucket_has_moe_params: + process_group = self.expert_dp_process_group[param.group_name] if is_moe_param( + param) else self.dp_process_group + grad_reduc.data.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size)) + + partition_ids = self.param_to_partition_ids[i][param_id] + assert all([p_id < dist.get_world_size(group=process_group) for p_id in partition_ids + ]), f"world size {dist.get_world_size(group=process_group)} and p_ids: {partition_ids}" + partition_size = self.partition_size[i] + # Get all partition ids + their offsets + partition_ids_w_offsets = [] + for partition_id in partition_ids: + offset = self.grad_start_offset[i][partition_id][param_id] + partition_ids_w_offsets.append((partition_id, offset)) + partition_ids_w_offsets.sort(key=lambda t: t[1]) + + # Calculate rank and offsets for grad slices + for idx in range(len(partition_ids_w_offsets)): + partition_id, offset = partition_ids_w_offsets[idx] + + # if dist.get_rank() == 0 and count < 100: + # print(f"Rank {dist.get_rank()} rank offset id {idx} calculated dp size {dist.get_world_size(group=process_group)} real dp size {dist.get_world_size(self.real_dp_process_group[i])} and dst: {partition_id}") + # count += 1 + + # Calculate numel for grad slice depending on partition location + if idx == len(partition_ids_w_offsets) - 1: + # Last partition_id uses its own offset + numel = param.numel() - offset + else: + # Set numel to next partition's offset + numel = partition_ids_w_offsets[idx + 1][1] - offset + + # Merge bucket ranges if they belong to the same rank + if partition_id == prev_id and process_group == prev_process_group: + prev_pid, prev_size, prev_numel = rank_and_offsets[-1] + rank_and_offsets[-1] = (prev_pid, prev_size, prev_numel + numel) + else: + rank_and_offsets.append((partition_id, curr_size, numel)) + real_dp_process_group.append(process_group) + curr_size += numel + prev_id, prev_process_group = partition_id, process_group + + if not self.ipg_bucket_has_moe_params: + tensor.div_(dist.get_world_size(group=self.dp_process_group) / float(self.sequence_parallel_size)) + + buckets = {} + for i, (dst, bucket_offset, numel) in enumerate(rank_and_offsets): + grad_slice = tensor.narrow(0, int(bucket_offset), int(numel)) + bucket_key = real_dp_process_group[i] if self.use_multi_rank_bucket_allreduce else ( + dst, real_dp_process_group[i]) + if bucket_key not in buckets: + buckets[bucket_key] = [] + if self.use_multi_rank_bucket_allreduce: + buckets[bucket_key].append((dst, grad_slice)) + else: + buckets[bucket_key].append(grad_slice) + + for bucket_key in buckets: + if self.use_multi_rank_bucket_allreduce: + self.allreduce_and_scatter(buckets[bucket_key], + numel_per_bucket=self.reduce_bucket_size, + divide=self.ipg_bucket_has_moe_params, + process_group=bucket_key) + else: + dst, process_group = bucket_key + self.allreduce_no_retain(buckets[bucket_key], + numel_per_bucket=self.reduce_bucket_size, + rank=dst, + divide=self.ipg_bucket_has_moe_params, + process_group=process_group) + + ############################################################################## + ############################# CPU Offload Methods############################# + ############################################################################## + def get_grad_position(self, group_id, tensor_list, first_offset, partition_size): + current_offset = 0 + + for i, tensor in enumerate(tensor_list): + param_id = self.get_param_id(tensor) + param_start_offset = 0 + + num_elements = tensor.numel() + + # we need to offset to get to the right element + if i == 0 and first_offset > 0: + tensor_offset = first_offset + num_elements = num_elements - tensor_offset + param_start_offset = first_offset + + # we dont need all elements of the tensor + if num_elements > (partition_size - current_offset): + num_elements = partition_size - current_offset + + self.grad_position[param_id] = [ + int(group_id), int(param_start_offset), + int(current_offset), int(num_elements) + ] + current_offset += num_elements + + def update_overflow_tracker_for_param_grad(self, param): + grad_accum = self.get_param_gradient_attribute(param) + if grad_accum is not None and self._has_inf_or_nan(grad_accum.data): + self.local_overflow = True + + def _get_offload_gradient_dict(self): + for param_group_index, _ in enumerate(self.optimizer.param_groups): + self.offload_gradient_dict[param_group_index] = [] + for lp_param in self.params_in_partition[param_group_index]: + param_id = self.get_param_id(lp_param) + [_, _, dest_offset, num_elements] = self.grad_position[param_id] + dest_tensor = self.single_partition_of_fp32_groups[param_group_index].grad.view(-1).narrow( + 0, dest_offset, num_elements) + self.offload_gradient_dict[param_group_index].append(dest_tensor) + + def async_accumulate_grad_in_cpu_via_gpu(self, param): + param_id = self.get_param_id(param) + + [i, source_offset, dest_offset, num_elements] = self.grad_position[param_id] + + # copy to a preexisiting buffer to avoid memory allocation penalty + dest_buffer = self.temp_grad_buffer_for_gpu_offload.view(-1).narrow(0, 0, param.numel()) + + #buffer for storing gradients for this parameter in CPU + def buffer_to_accumulate_to_in_cpu(): + if not self.fp16_master_weights_and_gradients: + buffer = torch.zeros(param.numel(), dtype=param.dtype, device=self.device) + return get_accelerator().pin_memory(buffer) if self.cpu_offload_pin_memory else buffer + else: + return self.single_partition_of_fp32_groups[i].grad.view(-1).narrow(0, dest_offset, num_elements) + + #accumulate gradients into param.grad_accum or parts of it that belongs to this partition + def accumulate_gradients(): + grad_accum = self.get_param_gradient_attribute(param) + if not self.fp16_master_weights_and_gradients: + dest_buffer.copy_(self.accumulated_grads_in_cpu[param_id].view(-1), non_blocking=True) + grad_accum.data.view(-1).add_(dest_buffer) + else: + dest_buffer.narrow(0, source_offset, + num_elements).copy_(self.accumulated_grads_in_cpu[param_id].view(-1), + non_blocking=True) + grad_accum.data.view(-1).narrow(0, source_offset, + num_elements).add_(dest_buffer.narrow(0, source_offset, num_elements)) + + #move accumulated gradients back to CPU + def copy_gradients_to_cpu(): + grad_accum = self.get_param_gradient_attribute(param) + if not self.fp16_master_weights_and_gradients: + self.accumulated_grads_in_cpu[param_id].data.copy_(grad_accum.data.view(-1), non_blocking=True) + else: + self.accumulated_grads_in_cpu[param_id].data.copy_(grad_accum.data.view(-1).narrow( + 0, source_offset, num_elements), + non_blocking=True) + + if param_id not in self.accumulated_grads_in_cpu: + self.accumulated_grads_in_cpu[param_id] = buffer_to_accumulate_to_in_cpu() + + if self.micro_step_id > 0: + accumulate_gradients() + + # at the boundary we will send 32bit directly + if not self.is_gradient_accumulation_boundary: + copy_gradients_to_cpu() + + def set_norm_for_param_grad(self, param): + param_id = self.get_param_id(param) + grad_accum = self.get_param_gradient_attribute(param) + accumulated_grad = self.accumulated_grads_in_cpu[ + param_id] if self.gradient_accumulation_steps > 1 else grad_accum + + [i, source_offset, dest_offset, num_elements] = self.grad_position[param_id] + + start = source_offset + accumulated_grad = accumulated_grad.view(-1).narrow(0, start, num_elements) + + self.norm_for_param_grads[param_id] = accumulated_grad.data.double().norm(2) + + def set_norm_for_param_grad_in_gpu(self, param): + param_id = self.get_param_id(param) + grad_accum = self.get_param_gradient_attribute(param) + if grad_accum is None: + accumulated_grad = param.grad + else: + accumulated_grad = grad_accum + + [i, source_offset, dest_offset, num_elements] = self.grad_position[param_id] + + start = source_offset + accumulated_grad = accumulated_grad.view(-1).narrow(0, start, num_elements) + + self.norm_for_param_grads[param_id] = accumulated_grad.data.double().norm(2) + + def async_inplace_copy_grad_to_fp32_buffer_from_gpu(self, param): + param_id = self.get_param_id(param) + + [i, source_offset, dest_offset, num_elements] = self.grad_position[param_id] + + dest_tensor = self.single_partition_of_fp32_groups[i].grad.view(-1).narrow(0, dest_offset, num_elements) + + grad_accum = self.get_param_gradient_attribute(param) + if grad_accum is None: + src_tensor = grad_accum.view(-1).narrow(0, source_offset, num_elements) + else: + src_tensor = grad_accum.view(-1).narrow(0, source_offset, num_elements) + if not self.fp16_master_weights_and_gradients: + src_tensor = src_tensor.float() + + dest_tensor.copy_(src_tensor, non_blocking=True) + param.grad = None #offload only + + def complete_grad_norm_calculation_for_cpu_offload(self, params): + total_norm = 0.0 + norm_type = 2.0 + for p in params: + # Pipeline parallelism may replicate parameters. Avoid multi-counting. + if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated: + continue + + if is_model_parallel_parameter(p) or (self.model_parallel_rank == 0): + param_id = self.get_param_id(p) + # as some model have trainable parameters but skipped in training, + # their backward hooks in self.create_reduce_and_remove_grad_hooks() will not run, + # so they have no norm_for_param_grads + if param_id in self.norm_for_param_grads: + param_norm = self.norm_for_param_grads[param_id] + total_norm += param_norm.item()**2 + else: + # As unused parameters in modules may not be expected sometimes, + # add an explicit error msg when it occurred and an option to + # avoid the error + assert self.ignore_unused_parameters, """ + This assert indicates that your module has parameters that + were not used in producing loss. + You can avoid this assert by + (1) enable ignore_unused_parameters option in zero_optimization config; + (2) making sure all trainable parameters and `forward` function + outputs participate in calculating loss. + """ + + # Sum across all model parallel GPUs. + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=self.dp_process_group) + + self._model_parallel_all_reduce(tensor=total_norm_cuda, op=dist.ReduceOp.SUM) + + total_norm = total_norm_cuda[0].item()**(1. / norm_type) + + if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm: + total_norm = -1 + + return total_norm + + ############################################################################################ + def copy_grads_in_partition(self, param): + if self.cpu_offload: + + if self.gradient_accumulation_steps > 1: + self.async_accumulate_grad_in_cpu_via_gpu(param) + + if self.is_gradient_accumulation_boundary: + self.set_norm_for_param_grad_in_gpu(param) + + self.update_overflow_tracker_for_param_grad(param) + + self.async_inplace_copy_grad_to_fp32_buffer_from_gpu(param) + + return + #print(f"ID {self.get_param_id(param)} grad norm {param.grad.norm()}") + if self.grads_in_partition is None: + self.grads_in_partition_offset = 0 + total_size = 0 + for group in self.params_in_partition: + for param_in_partition in group: + total_size += param_in_partition.numel() + + see_memory_usage(f"before copying {total_size} gradients into partition") + self.grads_in_partition = torch.empty(int(total_size), + dtype=self.dtype, + device=get_accelerator().current_device_name()) + see_memory_usage(f"after copying {total_size} gradients into partition") + + grad_reduc = self.get_gradient_for_reduction(param) + # The allreduce buffer will be rewritten. Copy the gradients in partition to a new buffer + new_grad_tensor = self.grads_in_partition.view(-1).narrow(0, self.grads_in_partition_offset, param.numel()) + new_grad_tensor.copy_(grad_reduc.view(-1)) + grad_reduc.data = new_grad_tensor.data.view_as(grad_reduc) + #print(f"Grad norm after copy to contiguous_buffer {param.grad.data.norm()}") + self.grads_in_partition_offset += param.numel() + + def reduce_ipg_grads(self): + if self.contiguous_gradients: + if self.extra_large_param_to_reduce is not None: + assert len(self.params_in_ipg_bucket) == 1, "more than 1 param in ipg bucket, this shouldn't happen" + _, _, param_id = self.params_in_ipg_bucket[0] + assert self.get_param_id(self.extra_large_param_to_reduce + ) == param_id, "param in ipg bucket does not match extra-large param" + extra_large_grad_reduc = self.get_gradient_for_reduction(self.extra_large_param_to_reduce) + self.average_tensor(extra_large_grad_reduc.view(-1)) + self.extra_large_param_to_reduce = None + else: + self.average_tensor(self.ipg_buffer[self.ipg_index]) + else: + self.buffered_reduce_fallback(None, + self.grads_in_ipg_bucket, + elements_per_buffer=self.elements_in_ipg_bucket) + + if self.overlap_comm: + stream = self.reduction_stream + elif self.cpu_offload: + # TODO: copy_grad_stream is disabled because of race with reduce. This hurts perf and should be fixed. + # get_accelerator().synchronize() + # stream = self.copy_grad_stream + stream = get_accelerator().current_stream() + else: + stream = get_accelerator().current_stream() + + with get_accelerator().stream(stream): + for _, param, param_id in self.params_in_ipg_bucket: + + assert self.params_already_reduced[param_id] == False, \ + f"The parameter {param_id} has already been reduced. \ + Gradient computed twice for this partition. \ + Multiple gradient reduction is currently not supported" + + self.params_already_reduced[param_id] = True + if self.partition_gradients: + if not self.is_param_in_current_partition[param_id]: + if self.overlap_comm and self.contiguous_gradients is False: + # Clear grads of other partitions during the next reduction + # to avoid clearing them before the reduction is complete. + if self.previous_reduced_grads is None: + self.previous_reduced_grads = [] + self.previous_reduced_grads.append(param) + else: + self.clear_grad_attribute(param) + elif self.contiguous_gradients: + self.copy_grads_in_partition(param) + else: # zero stage 1 - partition only optimizer state + if self.contiguous_gradients and self.is_param_in_current_partition[param_id]: + self.copy_grads_in_partition(param) + + self.grads_in_ipg_bucket = [] + self.params_in_ipg_bucket = [] + self.ipg_bucket_has_moe_params = False + self.elements_in_ipg_bucket = 0 + ##################################################################### + + def reduce_ready_partitions_and_remove_grads(self, param, i): + if self.partition_gradients or self.is_gradient_accumulation_boundary: + self.reduce_independent_p_g_buckets_and_remove_grads(param, i) + + def zero_reduced_gradients(self, partition_id, i): + + def are_all_related_partitions_reduced(params_id): + for partition_id in self.param_to_partition_ids[i][params_id]: + if not self.is_partition_reduced[i][partition_id]: + return False + return True + + for params_id in self.is_grad_computed[i][partition_id]: + if are_all_related_partitions_reduced(params_id): + self.param_dict[params_id].grad = None # dead code + + def flatten_and_print(self, message, tensors, start=0, n=5): + flatten_tensor = self.flatten(tensors) + + def print_func(): + logger.info(flatten_tensor.contiguous().view(-1).narrow(0, start, n)) + + self.sequential_execution(print_func, message) + + def get_grads_to_reduce(self, i, partition_id): + + def get_reducible_portion(key): + grad = self.param_dict[key].grad + total_elements = grad.numel() + start = self.grad_start_offset[i][partition_id][key] + num_elements = min(total_elements - start, + self.partition_size[i] - self.grad_partition_insertion_offset[i][partition_id][key]) + if not pg_correctness_test: + if num_elements == total_elements: + return grad + else: + return grad.contiguous().view(-1).narrow(0, int(start), int(num_elements)) + else: + if num_elements == total_elements: + return grad.clone() + else: + return grad.clone().contiguous().view(-1).narrow(0, int(start), int(num_elements)) + + grads_to_reduce = [] + for key in self.is_grad_computed[i][partition_id]: + grad = get_reducible_portion(key) + grads_to_reduce.append(grad) + return grads_to_reduce + + def sequential_execution(self, function, message, group=None): + if group is None: + group = self.dp_process_group + if dist.get_rank(group=group) == 0: + logger.info(message) + for id in range(dist.get_world_size(group=group)): + if id == dist.get_rank(group=group): + function() + dist.barrier(group=group) + + def set_none_gradients_to_zero(self, i, partition_id): + for param_id in self.is_grad_computed[i][partition_id]: + param = self.param_dict[param_id] + if param.grad is None: + param.grad = torch.zero_like(param) + + ######################Reduction Related Methods############################## + def allreduce_bucket(self, bucket, rank=None, log=None, divide=True, process_group=None): + rank = None + tensor = self.flatten(bucket) + + process_group = self.dp_process_group if process_group is None else process_group + + tensor_to_allreduce = tensor + + # if pg_correctness_test or self.sequence_parallel_size > 1: + # communication_data_type = torch.float32 + # else: + # communication_data_type = self.communication_data_type + communication_data_type = torch.float32 + if communication_data_type != tensor.dtype: + tensor_to_allreduce = tensor.to(communication_data_type) + + # if divide: + # tensor_to_allreduce.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size)) + + tensor_to_allreduce = tensor_to_allreduce.contiguous() + # print("call tensor_to_allreduce.contiguous() ....") + + # if rank is None: + # # "All Reducing" + # dist.all_reduce(tensor_to_allreduce, group=process_group) + # else: + # global_rank = dist.get_global_rank(process_group, rank) + # dist.reduce(tensor_to_allreduce, global_rank, group=process_group) + + if communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce: + if rank is None or rank == dist.get_rank(group=process_group): + tensor.copy_(tensor_to_allreduce) + + return tensor + + def _clear_previous_reduced_grads(self): + if self.previous_reduced_grads is not None: + for param in self.previous_reduced_grads: + self.clear_grad_attribute(param) + self.previous_reduced_grads = None + + # if rank is specified do a reduction instead of an allreduce + def allreduce_and_copy(self, small_bucket, rank=None, log=None, divide=True, process_group=None): + process_group = self.dp_process_group if process_group is None else process_group + if self.overlap_comm: + get_accelerator().synchronize() + # It is safe to clear the previously reduced grads of other partitions + self._clear_previous_reduced_grads() + stream = self.reduction_stream + else: + stream = get_accelerator().current_stream() + + with get_accelerator().stream(stream): + allreduced = self.allreduce_bucket( + small_bucket, + rank=rank, + log=log, + divide=divide, + process_group=process_group, + ) + if rank is None or rank == dist.get_rank(group=self.dp_process_group): + for buf, synced in zip(small_bucket, self.unflatten(allreduced, small_bucket)): + buf.copy_(synced) + + def allreduce_no_retain( + self, + bucket, + numel_per_bucket=500000000, + rank=None, + log=None, + divide=True, + process_group=None, + ): + small_bucket = [] + numel = 0 + for tensor in bucket: + small_bucket.append(tensor) + numel = numel + tensor.numel() + if numel > numel_per_bucket: + self.allreduce_and_copy(small_bucket, rank=rank, log=None, divide=divide, process_group=process_group) + small_bucket = [] + numel = 0 + + if len(small_bucket) > 0: + self.allreduce_and_copy(small_bucket, rank=rank, log=log, divide=divide, process_group=process_group) + + # allows using reduction of gradients instead of using all_reduce + + def buffered_reduce_fallback(self, rank, grads, elements_per_buffer=500000000, log=None): + split_buckets = split_half_float_double(grads) + + for i, bucket in enumerate(split_buckets): + self.allreduce_no_retain(bucket, numel_per_bucket=elements_per_buffer, rank=rank, log=log) + + ############################################################################# + ############################################################################# + ############################################################################# + + # views the tensor as multiple partitions and returns + # those partitions + def get_data_parallel_partitions(self, tensor, group_id): + partitions = [] + + dp = dist.get_world_size(group=self.real_dp_process_group[group_id]) + # dp_id = dist.get_rank(group=self.real_dp_process_group[group_id]) + + total_num_elements = tensor.numel() + + base_size = total_num_elements // dp + remaining = total_num_elements % dp + + start = 0 + for id in range(dp): + partition_size = base_size + if id < remaining: + partition_size = partition_size + 1 + partitions.append(tensor.narrow(0, start, partition_size)) + start = start + partition_size + return partitions + + def get_partition_info(self, tensor_list, partition_size, partition_id): + params_in_partition = [] + params_not_in_partition = [] + + start_index = partition_size * partition_id + end_index = partition_size * (partition_id + 1) + + current_index = 0 + first_offset = 0 + + for tensor in tensor_list: + + tensor_size = tensor.numel() + + if start_index <= current_index < end_index: + params_in_partition.append(tensor) + + elif current_index < start_index < (current_index + tensor_size): + params_in_partition.append(tensor) + + assert (first_offset == 0 + ), "This can happen either zero or only once as this must be the first tensor in the partition" + first_offset = start_index - current_index + + else: + params_not_in_partition.append(tensor) + + current_index = current_index + tensor_size + + return params_in_partition, params_not_in_partition, first_offset + + def zero_grad(self, set_to_none=True): + """ + Zero FP16 parameter grads. + """ + # FP32 grad should never exist. + # For speed, set model fp16 grad to None by default + # zero all pointers to grad tensors + for group in self.bit16_groups: + for p in group: + if set_to_none: + p.grad = None # epilogue and in step + p.grad_accum = None + else: + if p.grad is not None: + p.grad.detach_() + p.grad.zero_() + + def _model_parallel_all_reduce(self, tensor, op): + """ Perform all reduce within model parallel group, if any. + """ + if self.model_parallel_group is None or self.model_parallel_world_size == 1: + pass + else: + dist.all_reduce(tensor=tensor, op=op, group=self.model_parallel_group) + + def get_grad_norm_direct(self, gradients, params, norm_type=2): + """Clips gradient norm of an iterable of parameters. + + This is adapted from torch.nn.utils.clip_grad.clip_grad_norm_ and + added functionality to handle model parallel parameters. Note that + the gradients are modified in place. + + Arguments: + parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a + single Tensor that will have gradients normalized + max_norm (float or int): max norm of the gradients + norm_type (float or int): type of the used p-norm. Can be ``'inf'`` for + infinity norm. + + Returns: + Total norm of the parameters (viewed as a single vector). + """ + norm_type = float(norm_type) + if norm_type == inf: + total_norm = max(g.data.abs().max() for g in gradients) + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.MAX, group=self.dp_process_group) + + # Take max across all GPUs. + self._model_parallel_all_reduce(tensor=total_norm_cuda, op=dist.ReduceOp.MAX) + total_norm = total_norm_cuda[0].item() + else: + total_norm = 0.0 + # if dist.get_rank() == 0: + # logger.info(f"Total Norm beginning {total_norm}") + for g, p in zip(gradients, params): + # Pipeline parallelism may replicate parameters. Avoid multi-counting. + if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated: + continue + if is_model_parallel_parameter(p) or (self.model_parallel_rank == 0): + param_norm = g.data.double().norm(2) + total_norm += param_norm.item()**2 + # Sum across all model parallel GPUs. + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=self.dp_process_group) + + self._model_parallel_all_reduce(tensor=total_norm_cuda, op=dist.ReduceOp.SUM) + + total_norm = total_norm_cuda[0].item()**(1. / norm_type) + + if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm: + total_norm = -1 + + return total_norm + + # creates a flat fused tensor from the tensor list starting at the first_offset + # in the first tensor of the list. If there are not enough elements in the tensor + # list then the flat tensor will be padded with zeros + def get_flat_partition(self, tensor_list, first_offset, partition_size, dtype, device, return_tensor_list=False): + flat_tensor_list = [] + current_size = 0 + + for i, tensor in enumerate(tensor_list): + grad_accum = self.get_param_gradient_attribute(tensor) + if grad_accum is None: + grad_accum = torch.zeros_like(tensor, dtype=dtype) + + tensor = grad_accum + num_elements = tensor.numel() + tensor_offset = 0 + + # we need to offset to get to the right element + if i == 0 and first_offset > 0: + tensor_offset = first_offset + num_elements = num_elements - tensor_offset + + # we dont need all elements of the tensor + if num_elements > (partition_size - current_size): + num_elements = partition_size - current_size + + # we need a narrow view of the tensor based on the tensor offset and number of elements that + # we need from this tensor + if tensor_offset > 0 or num_elements < tensor.numel(): + flat_tensor_list.append(tensor.contiguous().view(-1).narrow(0, int(tensor_offset), int(num_elements))) + else: + flat_tensor_list.append(tensor) + + current_size = current_size + num_elements + + # this means its the last partition and does not align with the dp boundary. We need to pad before flattening + if current_size < partition_size: + flat_tensor_list.append(torch.zeros(int(partition_size - current_size), dtype=dtype, device=device)) + + if return_tensor_list: + return flat_tensor_list + + return self.flatten(flat_tensor_list) + + def free_grad_in_param_list(self, param_list): + for p in param_list: + p.grad = None # in step + p.grad_accum = None + + def reset_cpu_buffers(self): + self.norm_for_param_grads = {} + self.local_overflow = False + + def set_lr(self, lr): + """Set the learning rate.""" + for param_group in self.optimizer.param_groups: + param_group["lr"] = lr + + def get_lr(self): + """Return the current learning rate.""" + return self.optimizer.param_groups[0]["lr"] + + def override_loss_scale(self, loss_scale): + if loss_scale != self.external_loss_scale: + logger.info(f'[deepspeed] setting loss scale from {self.external_loss_scale} -> {loss_scale}') + self.custom_loss_scaler = True + self.external_loss_scale = loss_scale + + def scaled_global_norm(self, norm_type=2): + assert norm_type == 2, "only L2 norm supported" + norm_groups = [] + for i, group in enumerate(self.bit16_groups): + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + if self.cpu_offload: + norm_groups.append(self.complete_grad_norm_calculation_for_cpu_offload(self.params_in_partition[i])) + single_grad_partition = self.single_partition_of_fp32_groups[i].grad + else: + norm_groups.append(self.get_grad_norm_direct(self.averaged_gradients[i], self.params_in_partition[i])) + + if self.has_moe_layers: + self._average_expert_grad_norms(norm_groups) + + # note that the get_global_norm function only supports l2 norm + return get_global_norm(norm_list=norm_groups) + + def get_bit16_param_group(self, group_no): + bit16_partitions = self.parallel_partitioned_bit16_groups[group_no] + partition_id = dist.get_rank(group=self.real_dp_process_group[group_no]) + return [bit16_partitions[dist.get_rank(group=self.real_dp_process_group[group_no])]] + + def _optimizer_step(self, group_no): + original_param_groups = self.optimizer.param_groups + self.optimizer.param_groups = [original_param_groups[group_no]] + # Disabling this as the C++ side copy & synchronize is not working correctly + #from deepspeed.ops.adam import DeepSpeedCPUAdam + #if type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half: + # self.optimizer.step(fp16_param_groups=[self.get_bit16_param_group(group_no)]) + #else: + # self.optimizer.step() + self.optimizer.step() + self.optimizer.param_groups = original_param_groups + + def step(self, closure=None): + """ + Not supporting closure. + """ + self.micro_step_id = -1 + + see_memory_usage(f"In step before checking overflow") + + # First compute norm for all group so we know if there is overflow + if self.dtype == torch.float16: + self.check_overflow() + + prev_scale = self.loss_scale + self._update_scale(self.overflow) + if self.overflow: + see_memory_usage('After overflow before clearing gradients') + self.zero_grad(set_to_none=True) + if self.cpu_offload: + self.reset_cpu_buffers() + else: + self.averaged_gradients = {} + + see_memory_usage('After overflow after clearing gradients') + + for timer in OPTIMIZER_TIMERS: + self.timers(timer).start() + self.timers(timer).stop() + return + + # Step 1:- Calculate gradient norm using bit-16 grads + see_memory_usage('Before norm calculation') + scaled_global_grad_norm = self.scaled_global_norm() + self._global_grad_norm = scaled_global_grad_norm / prev_scale + see_memory_usage('After norm before optimizer') + + # Step 2:- run optimizer and upscaling simultaneously + for i, group in enumerate(self.bit16_groups): + self.timers(OPTIMIZER_GRADIENTS_TIMER).start() + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + if self.cpu_offload: + single_grad_partition = self.single_partition_of_fp32_groups[i].grad + self.unscale_and_clip_grads([single_grad_partition], scaled_global_grad_norm) + + self.timers(OPTIMIZER_GRADIENTS_TIMER).stop() + self.timers(OPTIMIZER_STEP_TIMER).start() + self._optimizer_step(i) + + # Disabled, this is not currently working + #from deepspeed.ops.adam import DeepSpeedCPUAdam + #if not (type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half): + # bit16_partitions = self.parallel_partitioned_bit16_groups[i] + # fp32_partition = self.single_partition_of_fp32_groups[i] + # bit16_partitions[partition_id].data.copy_(fp32_partition.data) + bit16_partitions = self.parallel_partitioned_bit16_groups[i] + fp32_partition = self.single_partition_of_fp32_groups[i] + bit16_partitions[partition_id].data.copy_(fp32_partition.data) + + self.timers(OPTIMIZER_STEP_TIMER).stop() + else: + # free gradients for all the parameters that are not updated by this process(ZeRO stage2) + self.free_grad_in_param_list(self.params_not_in_partition[i]) + + # create a flat gradients for parameters updated by this process + # If we are last partition, ensure we have same size grads and partition size, if not pad with zero tensors + if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1: + single_grad_partition = self.flatten_dense_tensors_aligned( + self.averaged_gradients[i], + int(self.partition_size[i])).to(self.single_partition_of_fp32_groups[i].dtype) + else: + single_grad_partition = self.flatten(self.averaged_gradients[i]).to( + self.single_partition_of_fp32_groups[i].dtype) + assert single_grad_partition.numel() == self.partition_size[i], \ + "averaged gradients have different number of elements that partition size {} {} {} {}".format( + single_grad_partition.numel(), self.partition_size[i], i, partition_id) + + self.single_partition_of_fp32_groups[i].grad = single_grad_partition + # release all the gradient since we have already created a necessary copy in dp_grad_partition(ZeRO stage2) + self.free_grad_in_param_list(self.params_in_partition[i]) + + self.averaged_gradients[i] = None + + self.unscale_and_clip_grads([single_grad_partition], scaled_global_grad_norm) + + self.timers(OPTIMIZER_GRADIENTS_TIMER).stop() + + # Step 3:- run the optimizer if no offloading + self.timers(OPTIMIZER_STEP_TIMER).start() + self._optimizer_step(i) + # Step 4:- get rid of the fp32 gradients. Not needed anymore + self.single_partition_of_fp32_groups[i].grad = None + del single_grad_partition + bit16_partitions = self.parallel_partitioned_bit16_groups[i] + fp32_partition = self.single_partition_of_fp32_groups[i] + bit16_partitions[partition_id].data.copy_(fp32_partition.data) + self.timers(OPTIMIZER_STEP_TIMER).stop() + + see_memory_usage('After optimizer before all-gather') + if self.cpu_offload: + self.reset_cpu_buffers() + + self.timers(OPTIMIZER_ALLGATHER_TIMER).start() + # Gather the updated weights from everyone. + # Then all partitions of the model parameters are updated and ready for next round forward. + all_gather_dp_groups(groups_flat=self.bit16_groups_flat, + partitioned_param_groups=self.parallel_partitioned_bit16_groups, + dp_process_group=self.real_dp_process_group, + start_alignment_factor=self.nccl_start_alignment_factor, + allgather_bucket_size=self.allgather_bucket_size) + self.timers(OPTIMIZER_ALLGATHER_TIMER).stop() + + # TODO: we probably don't need this? just to be safe + for i in range(len(self.bit16_groups)): + self._update_model_bit16_weights(i) + + self.timers.log(OPTIMIZER_TIMERS) + see_memory_usage('After zero_optimizer step') + + return + + @torch.no_grad() + def update_lp_params(self): + for i, (bit16_partitions, fp32_partition) in enumerate( + zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)): + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + bit16_partitions[partition_id].data.copy_(fp32_partition.data) + # print_rank_0(f'update_lp_params {i=} {partition_id=}', force=True) + # if i == 0: + # print_rank_0(f'{fp32_partition[:10]=}', force=True) + all_gather_dp_groups(groups_flat=self.bit16_groups_flat, + partitioned_param_groups=self.parallel_partitioned_bit16_groups, + dp_process_group=self.real_dp_process_group, + start_alignment_factor=self.nccl_start_alignment_factor, + allgather_bucket_size=self.allgather_bucket_size) + + def _average_expert_grad_norms(self, norm_groups): + for i, norm in enumerate(norm_groups): + if self.is_moe_param_group[i]: + scaled_norm = norm * 1.0 / float(dist.get_world_size(group=self.real_dp_process_group[i])) + scaled_norm_tensor = torch.tensor(scaled_norm, + device=get_accelerator().device_name(), + dtype=torch.float) + dist.all_reduce(scaled_norm_tensor, group=self.real_dp_process_group[i]) + norm_groups[i] = scaled_norm_tensor.item() + + def unscale_and_clip_grads(self, grad_groups_flat, total_norm): + # compute combined scale factor for this group + combined_scale = self.loss_scale + if self.clip_grad > 0.: + # norm is in fact norm*scale + clip = ((total_norm / self.loss_scale) + 1e-6) / self.clip_grad + if clip > 1: + combined_scale = clip * self.loss_scale + + for grad in grad_groups_flat: + if isinstance(grad, list): + sub_partitions = grad + for g in sub_partitions: + g.data.mul_(1. / combined_scale) + else: + grad.data.mul_(1. / combined_scale) + + def _check_overflow(self, partition_gradients=True): + self.overflow = self.has_overflow(partition_gradients) + + # `params` is a list / generator of torch.Variable + def has_overflow_serial(self, params, is_grad_list=False): + for p in params: + if p.grad is not None and self._has_inf_or_nan(p.grad.data): + return True + + return False + + def has_overflow_partitioned_grads_serial(self): + for i in range(len(self.bit16_groups)): + for j, grad in enumerate(self.averaged_gradients[i]): + if grad is not None and self._has_inf_or_nan(grad.data, j): + return True + return False + + def has_overflow(self, partition_gradients=True): + if partition_gradients: + overflow = self.local_overflow if self.cpu_offload else self.has_overflow_partitioned_grads_serial() + overflow_gpu = get_accelerator().ByteTensor([overflow]) + '''This will capture overflow across all data parallel and expert parallel process + Since expert parallel process are a subset of data parallel process''' + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=self.dp_process_group) + + else: + params = [] + for group in self.bit16_groups: + for param in group: + params.append(param) + + overflow = self.has_overflow_serial(params, is_grad_list=partition_gradients) + overflow_gpu = get_accelerator().ByteTensor([overflow]) + + # Since each model parallel GPU carries only part of the model, + # make sure overflow flag is synced across all the model parallel GPUs + self._model_parallel_all_reduce(tensor=overflow_gpu, op=dist.ReduceOp.MAX) + + overflow = overflow_gpu[0].item() + return bool(overflow) + + # `x` is a torch.Tensor + @staticmethod + def _has_inf_or_nan(x, j=None): + try: + # if x is half, the .float() incurs an additional deep copy, but it's necessary if + # Pytorch's .sum() creates a one-element tensor of the same type as x + # (which is true for some recent version of pytorch). + cpu_sum = float(x.float().sum()) + # More efficient version that can be used if .sum() returns a Python scalar + # cpu_sum = float(x.sum()) + except RuntimeError as instance: + # We want to check if inst is actually an overflow exception. + # RuntimeError could come from a different error. + # If so, we still want the exception to propagate. + if "value cannot be converted" not in instance.args[0]: + raise + return True + else: + if cpu_sum == float('inf') or cpu_sum == -float('inf') or cpu_sum != cpu_sum: + return True + return False + + def backward(self, loss, retain_graph=False): + """ + :attr:`backward` performs the following steps: + + 1. fp32_loss = loss.float() + 2. scaled_loss = fp32_loss*loss_scale + 3. scaled_loss.backward(), which accumulates scaled gradients into the ``.grad`` attributes of the model's fp16 leaves + """ + self.micro_step_id += 1 + + if self.contiguous_gradients: + self.ipg_buffer = [] + buf_0 = torch.empty(int(self.reduce_bucket_size), + dtype=self.dtype, + device=get_accelerator().current_device_name()) + self.ipg_buffer.append(buf_0) + + # Use double buffers to avoid data access conflict when overlap_comm is enabled. + if self.overlap_comm: + buf_1 = torch.empty(int(self.reduce_bucket_size), + dtype=self.dtype, + device=get_accelerator().current_device_name()) + self.ipg_buffer.append(buf_1) + self.ipg_index = 0 + + if self.custom_loss_scaler: + scaled_loss = self.external_loss_scale * loss + scaled_loss.backward() + else: + self.loss_scaler.backward(loss.float(), retain_graph=retain_graph) + + # Only for Stage 1, Mode 2 + if self.use_grad_accum_attribute: + self.fill_grad_accum_attribute() + + def check_overflow(self, partition_gradients=True): + self._check_overflow(partition_gradients) + + def _update_scale(self, has_overflow=False): + self.loss_scaler.update_scale(has_overflow) + + # Promote state so it can be retrieved or set via "fp16_optimizer_instance.state" + def _get_state(self): + return self.optimizer.state + + def _set_state(self, value): + self.optimizer.state = value + + state = property(_get_state, _set_state) + + # Promote param_groups so it can be retrieved or set via "fp16_optimizer_instance.param_groups" + # (for example, to adjust the learning rate) + def _get_param_groups(self): + return self.optimizer.param_groups + + def _set_param_groups(self, value): + self.optimizer.param_groups = value + + param_groups = property(_get_param_groups, _set_param_groups) + + # Promote loss scale so it can be retrieved or set via "fp16_optimizer_instance.loss_scale" + def _get_loss_scale(self): + if self.custom_loss_scaler: + return self.external_loss_scale + else: + return self.loss_scaler.cur_scale + + def _set_loss_scale(self, value): + self.loss_scaler.cur_scale = value + + loss_scale = property(_get_loss_scale, _set_loss_scale) + cur_scale = property(_get_loss_scale, _set_loss_scale) + + # Return group tensor after removing paddings that are added for alignment to DP world size. + # This method works on the assumption that each group contains a single flattened tensor. + def _get_groups_without_padding(self, groups_with_padding): + groups_without_padding = [] + for i, group in enumerate(groups_with_padding): + lean_length = group.numel() - self.groups_padding[i] + groups_without_padding.append(group[:lean_length]) + + return groups_without_padding + + # Return optimizer state after removing paddings that are added for alignment. + def _get_state_without_padding(self, state_with_padding, padding): + lean_state = {} + for key, value in state_with_padding.items(): + if torch.is_tensor(value): + lean_length = value.numel() - padding + lean_state[key] = value[:lean_length] + else: + lean_state[key] = value + + return lean_state + + # Return base optimizer states. + # This method assumes that each param group contains a single flattened tensor. + def _get_base_optimizer_state(self): + optimizer_groups_state = [] + for i, group in enumerate(self.optimizer.param_groups): + p = group['params'][0] + lean_optimizer_state = self._get_state_without_padding(self.optimizer.state[p], self.groups_padding[i]) + optimizer_groups_state.append(lean_optimizer_state) + + return optimizer_groups_state + + def state_dict(self): + """ + Returns a dict containing the current state of this :class:`FP16_Optimizer` instance. + This dict contains attributes of :class:`FP16_Optimizer`, as well as the state_dict + of the contained Pytorch optimizer. + Example:: + checkpoint = {} + checkpoint['model'] = model.state_dict() + checkpoint['optimizer'] = optimizer.state_dict() + torch.save(checkpoint, "saved.pth") + """ + state_dict = {} + state_dict[LOSS_SCALER] = self.loss_scaler + state_dict['dynamic_loss_scale'] = self.dynamic_loss_scale + state_dict['overflow'] = self.overflow + state_dict[CLIP_GRAD] = self.clip_grad + + if self.elastic_checkpoint: + state_dict[BASE_OPTIMIZER_STATE] = self._get_base_optimizer_state() + + if "step" in self.optimizer.param_groups[0]: + # Assuming "step" is the only item that changes through training iterations + assert all(group["step"] == self.optimizer.param_groups[0]["step"] + for group in self.optimizer.param_groups), "All param groups must have the same step value" + state_dict[BASE_OPTIMIZER_STATE_STEP] = self.optimizer.param_groups[0]["step"] + else: + state_dict[BASE_OPTIMIZER_STATE] = self.optimizer.state_dict() + + # Remove paddings for DP alignment to enable loading for other alignment values + fp32_groups_without_padding = self._get_groups_without_padding(self.single_partition_of_fp32_groups) + state_dict[SINGLE_PARTITION_OF_FP32_GROUPS] = fp32_groups_without_padding + + state_dict[ + ZERO_STAGE] = ZeroStageEnum.gradients if self.partition_gradients else ZeroStageEnum.optimizer_states + state_dict[GROUP_PADDINGS] = self.groups_padding + state_dict[PARTITION_COUNT] = self.partition_count + + state_dict[DS_VERSION] = version + state_dict[PARAM_SLICE_MAPPINGS] = self._param_slice_mappings + + return state_dict + + # Restore base optimizer fp32 weights from elastic checkpoint by: + # 1) Merging fp32 weights from checkpoints of all partitions + # 2) Extracting fp32 weights for current partition from merged weights + # 3) Using extracted weights to update base optimizer weights directly. + def _restore_from_elastic_fp32_weights(self, all_state_dict): + merged_single_partition_of_fp32_groups = [] + + for i in range(len(self.single_partition_of_fp32_groups)): + partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + merged_partitions = [sd[SINGLE_PARTITION_OF_FP32_GROUPS][i] for sd in all_state_dict] + if self.is_moe_group(self.optimizer.param_groups[i]): + ranks = self.get_ep_ranks(group_name=self.optimizer.param_groups[i]['name']) + merged_partitions = [merged_partitions[i] for i in ranks] + flat_merged_partitions = self.flatten_dense_tensors_aligned( + merged_partitions, + self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i])) + dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, i) + merged_single_partition_of_fp32_groups.append(dp_partitions[partition_id]) + + for current, saved in zip(self.single_partition_of_fp32_groups, merged_single_partition_of_fp32_groups): + current.data.copy_(saved.data) + + # Restore base optimizer fp32 weights from ZeRO fp16 or bfloat16 weights + def _restore_from_bit16_weights(self): + for group_id, (bit16_partitions, fp32_partition) in enumerate( + zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)): + partition_id = dist.get_rank(group=self.real_dp_process_group[group_id]) + fp32_partition.data.copy_(bit16_partitions[partition_id].data) + + # Refresh the fp32 master params from the fp16 or bfloat16 copies. + def refresh_fp32_params(self): + self._restore_from_bit16_weights() + + # Extract optimizer state for current partition from merged states of all partitions + def _partition_base_optimizer_state(self, state_key, all_partition_states, group_id): + partition_id = dist.get_rank(group=self.real_dp_process_group[group_id]) + alignment = dist.get_world_size(group=self.real_dp_process_group[group_id]) + if torch.is_tensor(all_partition_states[0]): + flat_merged_partitions = self.flatten_dense_tensors_aligned(all_partition_states, alignment) + dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, group_id) + return dp_partitions[partition_id] + else: + # Assume non-tensor states are not partitioned and equal across ranks, so return first one + return all_partition_states[0] + + def _restore_base_optimizer_state(self, base_optimizer_group_states): + if type(base_optimizer_group_states) == dict: + base_optimizer_group_states = base_optimizer_group_states['state'] + for i, group in enumerate(self.optimizer.param_groups): + p = group['params'][0] + for key, saved in base_optimizer_group_states[i].items(): + if torch.is_tensor(self.optimizer.state[p][key]): + dst_tensor = self.optimizer.state[p][key] + src_tensor = _get_padded_tensor(saved, dst_tensor.numel()) + self.optimizer.state[p][key].data.copy_(src_tensor.data) + else: + self.optimizer.state[p][key] = saved + + def get_ep_ranks(self, rank=0, group_name=None): + from deepspeed.utils import groups + expert_parallel_size_ = groups._get_expert_parallel_world_size(group_name) + world_size = groups._get_data_parallel_world_size() + rank = groups._get_expert_parallel_rank(group_name) + ranks = range(rank, world_size, expert_parallel_size_) + return list(ranks) + + # Restore base optimizer state from elastic checkpoint by + # 1) Merging optimizer state from checkpoints of all partitions + # 2) Extracting optimizer state for current partition from the merged state + # 3) Using the extracted value to directly update the base optimizer. + def _restore_elastic_base_optimizer_state(self, all_state_dict): + base_optimizer_group_states = [] + for i in range(len(self.optimizer.param_groups)): + partition_states = {} + all_partition_group_states = [sd[BASE_OPTIMIZER_STATE][i] for sd in all_state_dict] + + if self.is_moe_group(self.optimizer.param_groups[i]): + ranks = self.get_ep_ranks(group_name=self.optimizer.param_groups[i]['name']) + all_partition_group_states = [all_partition_group_states[i] for i in ranks] + + for key in all_partition_group_states[0].keys(): + all_partition_states = [all_states[key] for all_states in all_partition_group_states] + partition_states[key] = self._partition_base_optimizer_state(key, all_partition_states, i) + base_optimizer_group_states.append(partition_states) + + self._restore_base_optimizer_state(base_optimizer_group_states) + + # Restore step + if BASE_OPTIMIZER_STATE_STEP in all_state_dict[0]: + assert all(sd[BASE_OPTIMIZER_STATE_STEP] == all_state_dict[0][BASE_OPTIMIZER_STATE_STEP] + for sd in all_state_dict), "State dicts of all partitions must have the same step value" + loaded_param_groups_step = all_state_dict[0][BASE_OPTIMIZER_STATE_STEP] + for param_group in self.optimizer.param_groups: + param_group['step'] = loaded_param_groups_step + + def load_state_dict(self, + state_dict_list, + load_optimizer_states=True, + load_from_fp32_weights=False, + checkpoint_folder=None, + load_serial=None): + if checkpoint_folder: + self._load_universal_checkpoint(checkpoint_folder, load_optimizer_states, load_from_fp32_weights) + else: + self._load_legacy_checkpoint(state_dict_list, load_optimizer_states, load_from_fp32_weights) + + def _load_universal_checkpoint(self, checkpoint_folder, load_optimizer_states, load_from_fp32_weights): + self._load_hp_checkpoint_state(checkpoint_folder) + + @property + def param_groups(self): + """Forward the wrapped optimizer's parameters.""" + return self.optimizer.param_groups + + def _load_hp_checkpoint_state(self, checkpoint_dir): + checkpoint_dir = os.path.join(checkpoint_dir, "zero") + optim_state_path = os.path.join(checkpoint_dir, "optimizer_state.pt") + assert os.path.isfile( + optim_state_path), f'{optim_state_path} containing optimizer global state is missing! Cannot proceed.' + optim_sd = torch.load(optim_state_path) + self._load_global_state(optim_sd) + + tp_rank = bwc_tensor_model_parallel_rank(mpu=self.mpu) + tp_world_size = self.mpu.get_slice_parallel_world_size() if hasattr(self.mpu, "get_slice_parallel_world_size") \ + else self.mpu.get_tensor_model_parallel_world_size() + + for i, _ in enumerate(self.optimizer.param_groups): + for lp in self.bit16_groups[i]: + if lp._hp_mapping is not None: + #print(f"Loading {self.param_names[lp]} {tp_rank=} {tp_world_size=}") + lp.load_hp_checkpoint_state(os.path.join(checkpoint_dir, self.param_names[lp]), tp_rank, + tp_world_size) + + def _load_global_state(self, sd): + self.loss_scaler = sd.get(LOSS_SCALER, self.loss_scaler) + self.dynamic_loss_scale = sd.get('dynamic_loss_scale', self.dynamic_loss_scale) + self.overflow = sd.get('overflow', self.overflow) + self.clip_grad = sd.get(CLIP_GRAD, self.clip_grad) + + ckpt_version = sd.get(DS_VERSION, False) + assert ckpt_version, f"Empty ds_version in checkpoint, not clear how to proceed" + ckpt_version = pkg_version.parse(ckpt_version) + + # zero stage 1 mode + if not self.partition_gradients: + required_version = pkg_version.parse("0.3.17") + error_str = f"ZeRO stage 1 changed in {required_version} and is not backwards compatible " \ + "with older stage 1 checkpoints. If you'd like to load an old ZeRO-1 checkpoint " \ + "please use an older version of DeepSpeed (<= 0.5.8) and set 'legacy_stage1': true in your zero config json." + assert required_version <= ckpt_version, f"Old version: {ckpt_version} {error_str}" + + def _load_legacy_checkpoint(self, state_dict_list, load_optimizer_states=True, load_from_fp32_weights=False): + r"""Loading ZeRO checkpoint + + Arguments: + state_dict_list: List of all saved ZeRO checkpoints, one for each saved partition. + Note that the number of saved partitions may differ from number of loading partitions to support + changing GPU count, specifically DP world size, between saving and loading checkpoints. + load_optimizer_states: Boolean indicating whether or not to load base optimizer states + load_from_fp32_weights: Boolean indicating whether to initialize fp32 master weights from fp32 + copies in checkpoints (no precision loss) or from model's fp16 copies (with precision loss). + """ + """ + Loads a state_dict created by an earlier call to state_dict(). + If ``fp16_optimizer_instance`` was constructed from some ``init_optimizer``, + whose parameters in turn came from ``model``, it is expected that the user + will call ``model.load_state_dict()`` before + ``fp16_optimizer_instance.load_state_dict()`` is called. + Example:: + model = torch.nn.Linear(D_in, D_out).to(get_accelerator().device_name()).half() + optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) + optimizer = FP16_Optimizer(optimizer, static_loss_scale = 128.0) + ... + checkpoint = torch.load("saved.pth") + model.load_state_dict(checkpoint['model']) + optimizer.load_state_dict(checkpoint['optimizer']) + """ + + # I think it should actually be ok to reload the optimizer before the model. + dp_rank = dist.get_rank(group=self.dp_process_group) + current_rank_sd = state_dict_list[dp_rank] + self._load_global_state(current_rank_sd) + + ckpt_is_rigid = isinstance(current_rank_sd[BASE_OPTIMIZER_STATE], dict) + + # padding is always at the last rank/partition + # if DP=1024 and param-group elems=16 -> padding will be 1024-16 across all but one rank + # scenario-1 (shrink): saving w. 4 gpus -> loading w. 2 gpus + # scenario-2 (expand): saving w. 2 gpus -> loading w. 4 gpus + # if load_optimizer_states: + # if new_dp_size: + # self.strip_padding() + # self.add_padding_w_new_dp_size() + # self.optimizer.load_state_dict(current_rank_sd[BASE_OPTIMIZER_STATE]) + + if load_optimizer_states: + if ckpt_is_rigid: + # loading rigid ckpt into either rigid or elastic exec + self.optimizer.load_state_dict(current_rank_sd[BASE_OPTIMIZER_STATE]) + else: + if self.elastic_checkpoint: + # loading elastic into elastic exec + self._restore_elastic_base_optimizer_state(state_dict_list) + else: + # loading an elastic checkpoint into rigid exec + self._restore_base_optimizer_state(current_rank_sd[BASE_OPTIMIZER_STATE]) + + # At this point, the optimizer's references to the model's fp32 parameters are up to date. + # The optimizer's hyperparameters and internal buffers are also up to date. + # However, the fp32 master copies of the model's fp16 params stored by the optimizer are still + # out of date. There are two options. + # 1: Refresh the master params from the model's fp16 params. + # This requires less storage but incurs precision loss. + # 2: Save and restore the fp32 master copies separately. + # We choose option 1 if changing DP degree and option 2 otherwise. + # + # Pytorch Optimizer.load_state_dict casts saved buffers (e.g. momentum) to the type and device + # of their associated parameters, because it's possible those buffers might not exist yet in + # the current optimizer instance. In our case, as long as the current FP16_Optimizer has been + # constructed in the same way as the one whose state_dict we are loading, the same master params + # are guaranteed to exist, so we can just copy_() from the saved master params. + + if load_from_fp32_weights: + # option 2 from above + if self.elastic_checkpoint and not ckpt_is_rigid: + self._restore_from_elastic_fp32_weights(state_dict_list) + else: + # For non-elastic checkpoint, simply copying from saved weights of current rank is sufficient. + for current, saved in zip(self.single_partition_of_fp32_groups, + current_rank_sd[SINGLE_PARTITION_OF_FP32_GROUPS]): + src_tensor = _get_padded_tensor(saved, current.numel()) + current.data.copy_(src_tensor.data) + else: + # option 1 from above + self._restore_from_bit16_weights() + + if load_optimizer_states: + self._link_all_hp_params() + + +def _handle_overflow(cpu_sum, x, i): + import math + rank = dist.get_rank() + if rank == 0: + t_i = -1 + for v_i, v in enumerate(x.data.contiguous().view(-1)): + if not math.isfinite(float(v)): + t_i = v_i + break + logger.info(f"rank {rank} detected overflow {cpu_sum} in tensor {i}:{t_i} shape {x.shape}") + + +def estimate_zero2_model_states_mem_needs(total_params, + num_gpus_per_node=1, + num_nodes=1, + cpu_offload=True, + additional_buffer_factor=1.5): + + total_gpus = num_nodes * num_gpus_per_node + + if cpu_offload: + gpu_mem = 2 * total_params + cpu_mem = total_params * max(4 * total_gpus, 16) * additional_buffer_factor + else: + gpu_mem = 4 * total_params + int(16 * total_params / total_gpus) + cpu_mem = total_params * 4 * num_gpus_per_node * additional_buffer_factor + + return int(cpu_mem), int(gpu_mem) + + +def model_to_params(model): + # shared params calculated only once + total_params = sum(dict((p.data_ptr(), p.numel()) for p in model.parameters()).values()) + return total_params + + +def estimate_zero2_model_states_mem_needs_all_live(model, + num_gpus_per_node=1, + num_nodes=1, + additional_buffer_factor=1.5): + """ + Print out estimates on memory usage requirements for ZeRO 2 params, optim states and gradients + for a given ``model`` and hardware setup. + + If you have an actual model object, use this function and everything will be derived + automatically. + + If it's a hypothetical model, use ``estimate_zero2_model_states_mem_needs_all_cold`` where you have to pass + the ``total_params`` explicitly. + + Args: + - ``model``: ``nn.Module`` object + - ``num_gpus_per_node``: how many gpus per node (defaults to 1) + - ``num_nodes``: how many nodes (defaults to 1), + - ``additional_buffer_factor``: estimation factor (defaults to 1.5): + + """ + + total_params = model_to_params(model) + + estimate_zero2_model_states_mem_needs_all_cold(total_params=total_params, + num_gpus_per_node=num_gpus_per_node, + num_nodes=num_nodes, + additional_buffer_factor=additional_buffer_factor) + + +def estimate_zero2_model_states_mem_needs_all_cold(total_params, + num_gpus_per_node=1, + num_nodes=1, + additional_buffer_factor=1.5): + """ + Print out estimates on memory usage requirements for ZeRO 2 params, optim states and gradients + for a given ``model`` and hardware setup. + + If it's a hypothetical model, use this function where you have to pass + the ``total_params`` and ``largest_layer_params`` explicitly. + + If you have an actual model object, use ``estimate_zero2_model_states_mem_needs_all_live`` and everything + will be derived automatically. + + Args: + - ``total_params``: total model params + - ``num_gpus_per_node``: how many gpus per node (defaults to 1) + - ``num_nodes``: how many nodes (defaults to 1), + - ``additional_buffer_factor``: estimation factor (defaults to 1.5): + + """ + + def format_options(cpu_offload): + enabled = [] + device = f'{OffloadDeviceEnum.cpu:4}' if cpu_offload else "none" + enabled.append(f"offload_optimizer={device}") + return ", ".join(enabled) + + nodes_str = "nodes" if num_nodes > 1 else "node" + gpus_str = "GPUs" if num_gpus_per_node > 1 else "GPU" + print("Estimated memory needed for params, optim states and gradients for a:\n" + f"HW: Setup with {num_nodes} {nodes_str}, {num_gpus_per_node} {gpus_str} per node.\n" + f"SW: Model with {int(total_params/1e6)}M total params.") + print(" per CPU | per GPU | Options") + for cpu_offload in [True, False]: + cpu_mem, gpu_mem = estimate_zero2_model_states_mem_needs(total_params=total_params, + num_gpus_per_node=num_gpus_per_node, + num_nodes=num_nodes, + cpu_offload=cpu_offload, + additional_buffer_factor=additional_buffer_factor) + + options_str = format_options(cpu_offload=cpu_offload) + print(f" {cpu_mem/2**30:7.2f}GB | {gpu_mem/2**30:6.2f}GB | {options_str}") diff --git a/opensora/adaptor/utils.py b/opensora/adaptor/utils.py new file mode 100644 index 000000000..f16c0ec4d --- /dev/null +++ b/opensora/adaptor/utils.py @@ -0,0 +1,1043 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team +""" +Copyright NVIDIA/Megatron + +Helper functions and classes from multiple sources. +""" + +from collections.abc import Iterable +from deepspeed.moe.utils import is_moe_param +import os +import psutil +import gc +from math import sqrt +from packaging import version as pkg_version + +import torch +from deepspeed import comm as dist + +try: + from torch._six import inf +except ModuleNotFoundError: + from torch import inf + +from deepspeed.utils import groups, logger +from deepspeed.runtime.constants import PIPE_REPLICATED +from numpy import prod +from deepspeed.accelerator import get_accelerator + +from deepspeed.module_inject.policy import transpose +from torch.nn import functional as F + +torch_memory_reserved = get_accelerator().memory_reserved +torch_max_memory_reserved = get_accelerator().max_memory_reserved + + +class DummyOptim(): + """ + Dummy optimizer presents model parameters as a param group, this is + primarily used to allow ZeRO-3 without an optimizer + """ + + def __init__(self, params): + self.param_groups = [] + self.param_groups.append({'params': params}) + + +graph_cache = {} + + +def graph_process(replay_first_step, func, *args, **kwargs): + # `func` should only contain operations on the GPU + # Please ensure that the memory address of the data required by 'func' remains constant + if func.__name__ not in graph_cache: + cuda_stream = get_accelerator().Stream() + cuda_stream.wait_stream(get_accelerator().current_stream()) + with get_accelerator().stream(cuda_stream): + func(*args, **kwargs) + get_accelerator().current_stream().wait_stream(cuda_stream) + graph_cache[func.__name__] = get_accelerator().create_graph() + with get_accelerator().capture_to_graph(graph_cache[func.__name__]): + func(*args, **kwargs) + if replay_first_step: + get_accelerator().replay_graph(graph_cache[func.__name__]) + else: + get_accelerator().replay_graph(graph_cache[func.__name__]) + + +def noop_decorator(func): + return func + + +class noop_context(object): + + def __init__(self): + pass + + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +def ensure_directory_exists(filename): + """Create the directory path to ``filename`` if it does not already exist. + + Args: + filename (str): A file path. + """ + dirname = os.path.dirname(filename) + os.makedirs(dirname, exist_ok=True) + + +def set_random_seed(seed): + """Set the random seed for common PRNGs used during training: random, numpy, and torch. + + Args: + seed (int): the seed to use + """ + import numpy + import random + random.seed(seed) + numpy.random.seed(seed) + torch.manual_seed(seed) + + +def is_model_parallel_parameter(p) -> bool: + if hasattr(p, 'model_parallel') and p.model_parallel: + return True + + if hasattr(p, 'tensor_model_parallel') and p.tensor_model_parallel: + return True + + return False + + +def bwc_tensor_model_parallel_rank(mpu=None): + """Backwards-compatible way of querying the tensor model parallel rank from + an ``mpu`` object. + + *Tensor* model parallelism means that tensors are physically split across + processes. This contrasts with *pipeline* model parallelism, in which the + layers are partitioned but tensors left intact. + + The API for tensor model parallelism has changed across versions and this + helper provides a best-effort implementation across versions of ``mpu`` + objects. The preferred mechanism is + ``mpu.get_tensor_model_parallel_rank()``. + + This should "just work" with both Megatron-LM and DeepSpeed's pipeline + parallelism. + + Args: + mpu (model parallel unit, optional): The tensor model parallel rank. + If ``mpu=None``, returns 0. Defaults to ``None``. + + Returns: + int: the rank + """ + if mpu is None: + # No model parallelism in easy :) + return 0 + + if hasattr(mpu, 'get_tensor_model_parallel_rank'): + # New Megatron and DeepSpeed convention (post pipeline-parallelism release) + return mpu.get_tensor_model_parallel_rank() + elif hasattr(mpu, 'get_slice_parallel_rank'): + # Some DeepSpeed + pipeline parallelism versions + return mpu.get_slice_parallel_rank() + else: + # Deprecated Megatron and DeepSpeed convention + return mpu.get_model_parallel_rank() + + +def copy_to_device(item, device, criterion_func): + """ + Return a copy of tensor on specified device. + Works on individual tensors, and tensors contained/nested in lists, tuples, and dicts. + Parameters: + item: tensor to copy or (possibly nested) container of tensors to copy. + device: target device + criterion_func: Function to restrict copy operation to items meet criterion + + Returns: + None + """ + if criterion_func(item): + return item.to(device) + elif isinstance(item, list): + return [copy_to_device(v, device, criterion_func) for v in item] + elif isinstance(item, tuple): + return tuple([copy_to_device(v, device, criterion_func) for v in item]) + elif isinstance(item, dict): + return {k: copy_to_device(v, device, criterion_func) for k, v in item.items()} + else: + return item + + +def move_to_device(item, device, criterion_func): + """ + Move tensor on to specified device by changing the storage. + Works on individual tensors, and tensors contained/nested in lists, tuples, and dicts. + Parameters: + item: tensor to move or (possibly nested) container of tensors to move. + device: target device + criterion_func: Function to restrict move operation to items meet criterion + + Returns: + None + """ + if criterion_func(item): + device_copy = item.to(device) + item.data = device_copy.data + return item + elif isinstance(item, list): + return [move_to_device(v, device, criterion_func) for v in item] + elif isinstance(item, tuple): + return tuple([move_to_device(v, device, criterion_func) for v in item]) + elif isinstance(item, dict): + return {k: move_to_device(v, device, criterion_func) for k, v in item.items()} + else: + return item + + +class CheckOverflow(object): + '''Checks for overflow in gradient across parallel process''' + + def __init__(self, param_groups=None, mpu=None, zero_reduce_scatter=False, deepspeed=None): + self.mpu = mpu + self.params = [] if param_groups else None + self.zero_reduce_scatter = zero_reduce_scatter + self.deepspeed = deepspeed + self.has_moe_params = False + if param_groups: + for group in param_groups: + for param in group: + self.params.append(param) + if is_moe_param(param): + self.has_moe_params = True + + def check_using_norm(self, norm_group, reduce_overflow=True): + # TODO: I don't think reduce_overflow is needed if mpu is None + overflow = -1 in norm_group + overflow_gpu = get_accelerator().FloatTensor([overflow]) + if self.has_moe_params: + # In this case, we need to do an all_reduce across + # the expert_parallel_group, so that if there was + # an overflow due to expert weights, we detect it + + # Only need to check groups.get_largest_expert_parallel_group() + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=groups._get_max_expert_parallel_group()) + if self.mpu is not None: + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=self.mpu.get_model_parallel_group()) + elif reduce_overflow: + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX) + dist.barrier() + overflow = overflow_gpu[0].item() + return bool(overflow) + + def check(self, param_groups=None): + params = [] + has_moe_params = False + if param_groups is None: + params = self.params + has_moe_params = self.has_moe_params + else: + assert param_groups is not None, \ + "self.params and param_groups both cannot be none" + + for group in param_groups: + for param in group: + params.append(param) + if is_moe_param(param): + has_moe_params = True + + return self.has_overflow(params, has_moe_params=has_moe_params) + + # `params` is a list / generator of torch.Variable + def has_overflow_serial(self, params): + for i, p in enumerate(params): + if p.grad is not None and self._has_inf_or_nan(p.grad.data, i): + return True + return False + + def has_overflow(self, params, has_moe_params=None): + if has_moe_params is None: + has_moe_params = self.has_moe_params + overflow = self.has_overflow_serial(params) + # Since each model parallel GPU carries only part of the model, + # make sure overflow flag is synced across all the model parallel GPUs + overflow_gpu = get_accelerator().ByteTensor([overflow]) + # deepspeed.comm.all_reduce(overflow_gpu, + # op=deepspeed.comm.ReduceOp.MAX, + # group=mpu.get_model_parallel_group()) + if has_moe_params: + # All reduce this across expert_parallel_group, so that if an expert + # overflows, we detect it here + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=groups._get_max_expert_parallel_group()) + if self.zero_reduce_scatter: + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=dist.get_world_group()) + elif self.mpu is not None: + if self.deepspeed is not None: + using_pipeline = hasattr(self.deepspeed, 'pipeline_enable_backward_allreduce') + if (using_pipeline and self.deepspeed.pipeline_enable_backward_allreduce is False) or ( + not using_pipeline and self.deepspeed.enable_backward_allreduce is False): + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=self.mpu.get_data_parallel_group()) + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=self.mpu.get_model_parallel_group()) + elif self.deepspeed is not None and self.deepspeed.enable_backward_allreduce is False: + dist.all_reduce(overflow_gpu, op=dist.ReduceOp.MAX, group=dist.get_world_group()) + + overflow = overflow_gpu[0].item() + return bool(overflow) + + # `x` is a torch.Tensor + @staticmethod + def _has_inf_or_nan(x, i): + try: + # if x is half, the .float() incurs an additional deep copy, but it's necessary if + # Pytorch's .sum() creates a one-element tensor of the same type as x + # (which is true for some recent version of pytorch). + cpu_sum = float(x.float().sum()) + # More efficient version that can be used if .sum() returns a Python scalar + # cpu_sum = float(x.sum()) + except RuntimeError as instance: + # We want to check if inst is actually an overflow exception. + # RuntimeError could come from a different error. + # If so, we still want the exception to propagate. + if "value cannot be converted" not in instance.args[0]: + raise + return True + else: + if cpu_sum == float('inf') or cpu_sum == -float('inf') or cpu_sum != cpu_sum: + return True + return False + + +def _handle_overflow(cpu_sum, x, i): + import math + rank = dist.get_rank() + if rank == 0: + t_i = -1 + for v_i, v in enumerate(x.data.contiguous().view(-1)): + if not math.isfinite(float(v)): + t_i = v_i + break + logger.info(f"rank {rank} detected overflow {cpu_sum} in tensor {i}:{t_i} shape {x.shape}") + + +def get_global_norm(norm_list): + """ Compute total from a list of norms + """ + total_norm = 0.0 + for norm in norm_list: + total_norm += norm**2.0 + # logger.info(f'norm_list = {norm_list} global = {sqrt(total_norm)}') + return sqrt(total_norm) + + +def clip_grad_norm_(parameters, max_norm, norm_type=2, mpu=None): + """Clips gradient norm of an iterable of parameters. + + This has been adapted from Nvidia megatron. We add norm averaging + to consider MoE params when calculating norm as they will result + in different norms across different ranks. + + This is adapted from torch.nn.utils.clip_grad.clip_grad_norm_ and + added functionality to handle model parallel parameters. Note that + the gradients are modified in place. + + Arguments: + parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a + single Tensor that will have gradients normalized + max_norm (float or int): max norm of the gradients + norm_type (float or int): type of the used p-norm. Can be ``'inf'`` for + infinity norm. + + Returns: + Total norm of the parameters (viewed as a single vector). + """ + if isinstance(parameters, torch.Tensor): + parameters = [parameters] + parameters = list(filter(lambda p: p.grad is not None, parameters)) + max_norm = float(max_norm) + norm_type = float(norm_type) + if norm_type == inf: + total_norm = max(p.grad.data.abs().max() for p in parameters) + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + # Take max across all GPUs. + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.MAX, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item() + else: + total_norm = 0 + for p in parameters: + if mpu is not None: + if (mpu.get_model_parallel_rank() == 0) or is_model_parallel_parameter(p): + param_norm = p.grad.data.norm(norm_type) + total_norm += param_norm.item()**norm_type + else: + param_norm = p.grad.data.float().norm(norm_type) + total_norm += param_norm.item()**norm_type + + # Sum across all model parallel GPUs. + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item()**(1. / norm_type) + + # Need to average total_norm across different GPUs due to the presence of moe params + pg = groups._get_data_parallel_group() + scaled_norm = total_norm * 1.0 / float(dist.get_world_size(group=pg)) + + scaled_norm_tensor = get_accelerator().FloatTensor([float(scaled_norm)]) + dist.all_reduce(scaled_norm_tensor, group=pg) + total_norm = scaled_norm_tensor.item() + + clip_coef = max_norm / (total_norm + 1e-6) + if clip_coef < 1: + for p in parameters: + p.grad.data.mul_(clip_coef) + return total_norm + + +def get_grad_norm(parameters, norm_type=2, mpu=None): + """Get grad norm of an iterable of parameters. + + This is adapted from torch.nn.utils.clip_grad.clip_grad_norm_ and + added functionality to handle model parallel parameters. Note that + the gradients are modified in place. Taken from Nvidia Megatron. + + Arguments: + parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a + single Tensor that will have gradients normalized + norm_type (float or int): type of the used p-norm. Can be ``'inf'`` for + infinity norm. + + Returns: + Total norm of the parameters (viewed as a single vector). + """ + if isinstance(parameters, torch.Tensor): + parameters = [parameters] + parameters = list(filter(lambda p: p.grad is not None, parameters)) + + norm_type = float(norm_type) + if norm_type == inf: + total_norm = max(p.grad.data.abs().max() for p in parameters) + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + # Take max across all GPUs. + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.MAX, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item() + else: + total_norm = 0. + tensor_mp_rank = bwc_tensor_model_parallel_rank(mpu=mpu) + for p in parameters: + # Pipeline parallelism may replicate parameters. Avoid multi-counting. + if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated: + continue + + # Filter to avoid over-counting replicated tensors from tensor + # model parallelism + if (tensor_mp_rank > 0) and not is_model_parallel_parameter(p): + continue + + param_norm = p.grad.data.float().norm(norm_type) + total_norm += param_norm.item()**norm_type + + # Sum across all model parallel GPUs. + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item()**(1. / norm_type) + + if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm: + total_norm = -1 + + return total_norm + + +def get_grad_zeros(parameters, mpu=None): + """Compute the number of grads with zero values. + + This is adapted from get_grad_norm + + Arguments: + parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a + single Tensor that will have gradients normalized + + Returns: + Total number of params with zero values (viewed as a single vector). + """ + if isinstance(parameters, torch.Tensor): + parameters = [parameters] + parameters = list(filter(lambda p: p.grad is not None, parameters)) + + total_zeros = 0. + tensor_mp_rank = bwc_tensor_model_parallel_rank(mpu=mpu) + for p in parameters: + # Pipeline parallelism may replicate parameters. Avoid multi-counting. + if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated: + continue + + # Filter to avoid over-counting replicated tensors from tensor + # model parallelism + if (tensor_mp_rank > 0) and not is_model_parallel_parameter(p): + continue + + count_zeros = p.grad.numel() - torch.count_nonzero(p.grad) + total_zeros += count_zeros.item() + + # Sum across all model parallel GPUs. + total_zeros_cuda = get_accelerator().FloatTensor([float(total_zeros)]) + if mpu is not None: + dist.all_reduce(total_zeros_cuda, op=dist.ReduceOp.SUM, group=mpu.get_model_parallel_group()) + total_zeros = total_zeros_cuda[0].item() + + return total_zeros + + +def get_weight_norm(parameters, norm_type=2, mpu=None): + """Get norm of an iterable of parameters. + + This is adapted from torch.nn.utils.clip_grad.clip_grad_norm_ and + added functionality to handle model parallel parameters. Note that + the gradients are modified in place. Taken from Nvidia Megatron. + + Arguments: + parameters (Iterable[Tensor] or Tensor): an iterable of Tensors or a + single Tensor that will have gradients normalized + norm_type (float or int): type of the used p-norm. Can be ``'inf'`` for + infinity norm. + + Returns: + Total norm of the parameters (viewed as a single vector). + -1 if the norm value is NaN or Inf. + """ + if isinstance(parameters, torch.Tensor): + parameters = [parameters] + + norm_type = float(norm_type) + if norm_type == inf: + total_norm = max(p.data.abs().max() for p in parameters) + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + # Take max across all GPUs. + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.MAX, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item() + else: + total_norm = 0. + tensor_mp_rank = bwc_tensor_model_parallel_rank(mpu=mpu) + for p in parameters: + # Pipeline parallelism may replicate parameters. Avoid multi-counting. + if hasattr(p, PIPE_REPLICATED) and p.ds_pipe_replicated: + continue + + # Filter to avoid over-counting replicated tensors from tensor + # model parallelism + if (tensor_mp_rank > 0) and not is_model_parallel_parameter(p): + continue + + param_norm = p.data.float().norm(norm_type) + total_norm += param_norm**norm_type + + # Sum across all model parallel GPUs. + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item()**(1. / norm_type) + + if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm: + total_norm = -1 + + return total_norm + + +def prefix_sum_inc(weights): + """ Compute an inclusive prefix sum. + + Example: + >>> prefix_sum_inc([3,4,5]) + [3, 7, 12] + """ + weights_ = [w for w in weights] + for x in range(1, len(weights_)): + weights_[x] += weights_[x - 1] + return weights_ + + +def partition_uniform(num_items, num_parts): + import numpy + parts = [0] * (num_parts + 1) + # First check for the trivial edge case + if num_items <= num_parts: + for p in range(num_parts + 1): + parts[p] = min(p, num_items) + return parts + + chunksize = num_items // num_parts + residual = num_items - (chunksize * num_parts) + + parts = numpy.arange(0, (num_parts + 1) * chunksize, chunksize) + + for i in range(residual): + parts[i + 1:] += 1 + parts = parts.tolist() + + return parts + + +def partition_balanced(weights, num_parts): + """ + use dynamic programming solve `The Linear Partition Problem`. + see https://www8.cs.umu.se/kurser/TDBAfl/VT06/algorithms/BOOK/BOOK2/NODE45.HTM + """ + import numpy as np + n = len(weights) + m = num_parts + + if n <= m: + return partition_uniform(n, m) + + dp_max = np.full((n + 1, m + 1), np.inf) + dp_min = np.full((n + 1, m + 1), np.inf) + dp_cost = np.full((n + 1, m + 1), np.inf) + position = np.zeros((n + 1, m + 1), dtype=int) + prefix_sum = np.zeros((n + 1)) + prefix_sum[1:] = np.cumsum(weights) + + dp_max[0, 0] = 0 + dp_cost[0, 0] = 0 + for i in range(1, n + 1): + for j in range(1, min(i, m) + 1): + for k in range(i): + max_sum = max(dp_max[k, j - 1], prefix_sum[i] - prefix_sum[k]) + min_sum = min(dp_min[k, j - 1], prefix_sum[i] - prefix_sum[k]) + cost = max_sum - min_sum + if dp_cost[i, j] >= cost: + dp_cost[i, j] = cost + dp_max[i, j] = max_sum + dp_min[i, j] = min_sum + position[i, j] = k + + parts = [n] + for i in reversed(range(1, m + 1)): + parts.append(position[parts[-1], i]) + parts.reverse() + + return parts + + +class PartitionedTensor: + + def __init__(self, tensor, group, partition_meta=None): + super().__init__() + + self.group = group + self.num_parts = dist.get_world_size(group=self.group) + self.rank = dist.get_rank(group=self.group) + self.orig_size = list(tensor.size()) + self.orig_device = tensor.device + self.local_data, self.partition = self._partition_tensor(tensor) + self.even_split = tensor.numel() % self.num_parts == 0 + + @classmethod + def from_meta(cls, meta, local_part, group, device=get_accelerator().device_name()): + assert meta.dtype == torch.long + dummy = torch.ones(dist.get_world_size(group=group)) + part_obj = cls(tensor=dummy, group=group) + + meta = meta.tolist() + + # [N, list0, ..., listN-1] + part_obj.orig_size = meta[1:(1 + meta[0])] + meta = meta[1 + meta[0]:] + + part_obj.orig_device = device + part_obj.local_data = local_part.detach() + + part_obj.group = group + + # Partition is encoded like the rowptr of a CSR matrix: + # [num_parts, rank, 0, part_1, ..., part_num_parts] + # TODO: support shuffle between different partition granularities + assert part_obj.num_parts == meta[0] + assert part_obj.rank == meta[1] + part_obj.partition = meta[2:] # length num_parts+1 + + return part_obj + + def _partition_tensor(self, tensor): + partition = partition_uniform(num_items=tensor.numel(), num_parts=self.num_parts) + start = partition[self.rank] + length = partition[self.rank + 1] - start + tensor_part = tensor.detach().contiguous().view(-1).narrow(0, start=start, length=length).clone() + + return tensor_part, partition + + def full(self, device=None): + if device is None: + device = self.orig_device + + # Allocate the full tensor as a flat buffer. + full_numel = prod(self.full_size()) + flat_tensor = torch.zeros([full_numel], dtype=self.local_data.dtype, device=device) + if self.even_split: + # Collect the full tensor + dist.all_gather_into_tensor(flat_tensor, self.local_data, group=self.group) + else: + for part_id in range(self.num_parts): + part_size = self.partition[part_id + 1] - self.partition[part_id] + buf = flat_tensor.narrow(0, start=self.partition[part_id], length=part_size) + if part_id == self.rank: + buf.copy_(self.local_data) + dist.broadcast(buf, part_id, self.group) + return flat_tensor.view(self.full_size()).clone().detach() + + def to_meta(self): + """Returns a torch.LongTensor that encodes partitioning information. + + Can be used along with ``data()`` to serialize a ``PartitionedTensor`` for + communication. + + Returns: + torch.LongTensor: a tensor encoding the meta-information for the partitioning + """ + meta = [] + meta.append(len(self.orig_size)) + meta += list(self.orig_size) + meta.append(self.num_parts) + meta.append(self.rank) + meta += self.partition + return torch.LongTensor(data=meta).to(self.orig_device) + + def data(self): + return self.local_data + + def local_size(self): + return self.local_data.size() + + def full_size(self): + return self.orig_size + + +mem_alloced = 0 +mem_cached = 0 + + +def memory_status(msg, print_rank=-1, reset_max=False): + global mem_alloced, mem_cached + + rank = dist.get_rank() + if print_rank != -1 and rank != print_rank: + return + + get_accelerator().synchronize() + + if reset_max: + get_accelerator().reset_max_memory_cached() + get_accelerator().reset_max_memory_allocated() + + new_alloced = get_accelerator().memory_allocated() + new_cached = get_accelerator().memory_cached() + + delta_alloced = new_alloced - mem_alloced + delta_cached = new_cached - mem_cached + + mem_cached = new_cached + mem_alloced = new_alloced + + max_alloced = get_accelerator().max_memory_allocated() + max_cached = get_accelerator().max_memory_cached() + + # convert to GB for printing + new_alloced /= 1024**3 + new_cached /= 1024**3 + delta_alloced /= 1024**3 + delta_cached /= 1024**3 + max_alloced /= 1024**3 + max_cached /= 1024**3 + + print( + f'RANK={rank} MEMSTATS', msg, f'device={get_accelerator().current_device_name()} ' + f'current alloc={new_alloced:0.4f}GB (delta={delta_alloced:0.4f}GB max={max_alloced:0.4f}GB) ' + f'current cache={new_cached:0.4f}GB (delta={delta_cached:0.4f}GB max={max_cached:0.4f}GB)') + + +def get_ma_status(): + if dist.is_initialized() and not dist.get_rank() == 0: + return 0 + return get_accelerator().memory_allocated() + + +def empty_cache(): + get_accelerator().empty_cache() + get_accelerator().reset_peak_memory_stats() + + +def see_memory_usage(message, force=False): + if not force: + return + if dist.is_initialized() and not dist.get_rank() == 0: + return + + # python doesn't do real-time garbage collection so do it explicitly to get the correct RAM reports + gc.collect() + + # Print message except when distributed but not rank 0 + logger.info(message) + logger.info(f"MA {round(get_accelerator().memory_allocated() / (1024 * 1024 * 1024),2 )} GB \ + Max_MA {round(get_accelerator().max_memory_allocated() / (1024 * 1024 * 1024),2)} GB \ + CA {round(torch_memory_reserved() / (1024 * 1024 * 1024),2)} GB \ + Max_CA {round(torch_max_memory_reserved() / (1024 * 1024 * 1024))} GB ") + + vm_stats = psutil.virtual_memory() + used_GB = round(((vm_stats.total - vm_stats.available) / (1024**3)), 2) + logger.info(f'CPU Virtual Memory: used = {used_GB} GB, percent = {vm_stats.percent}%') + + # get the peak memory to report correct data, so reset the counter for the next call + get_accelerator().reset_peak_memory_stats() + + +def call_to_str(base, *args, **kwargs): + """Construct a string representation of a call. + + Args: + base (str): name of the call + args (tuple, optional): args to ``base`` + kwargs (dict, optional): kwargs supplied to ``base`` + + Returns: + str: A string representation of base(*args, **kwargs) + """ + name = f'{base}(' + if args: + name += ', '.join(repr(arg) for arg in args) + if kwargs: + name += ', ' + if kwargs: + name += ', '.join(f'{key}={repr(arg)}' for key, arg in kwargs.items()) + name += ')' + return name + + +def get_only_unique_item(items): + item_set = set(items) + if len(item_set) != 1: + raise RuntimeError(f"expected there to be only one unique element in {items}") + unique_item, = item_set + + return unique_item + + +def clip_gradients(parameters, max_norm=1.0, global_grad_norm=None, mpu=None, eps=1e-6): + """Clip the gradient of a list of parameters. + Args: + parameters: List of parameters whose .grad will be clipped. + global_grad_norm (float, optional): Precomputed gradient norm. Defaults to None. + mpu (optional): model parallelism unit. Defaults to None. + eps (float, optional): epsilon value added to grad norm. Defaults to 1e-6 + Returns: + float: the global gradient norm + """ + if global_grad_norm is None: + global_grad_norm = get_grad_norm(parameters, mpu=mpu) + clip_coef = max_norm / (global_grad_norm + eps) + if clip_coef < 1: + for p in parameters: + p.grad.detach().mul_(clip_coef) + return global_grad_norm + + +def get_global_norm_of_tensors(input_tensors, norm_type=2, mpu=None, use_graph=False): + """Get norm of an iterable of tensors. + + This is adapted from torch.nn.utils.clip_grad.clip_grad_norm_ and + added functionality to handle model parallel parameters. Taken from Nvidia Megatron. + + Arguments: + input_tensors (Iterable[Tensor]): an iterable of Tensors will have norm computed + norm_type (float or int): type of the used p-norm. Can be ``'inf'`` for + infinity norm. + + Returns: + Total norm of the tensors (viewed as a single vector). + """ + assert isinstance(input_tensors, Iterable), f'expected Iterable type not {type(input_tensors)}' + assert all([torch.is_tensor(t) for t in input_tensors]), f'expected list of only tensors' + + norm_type = float(norm_type) + if norm_type == inf: + total_norm = max(t.data.abs().max() for t in input_tensors) + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.MAX, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item() + else: + if use_graph: + if 'norm_tensors_compute_buffer' not in graph_cache: + graph_cache['norm_tensors_compute_buffer'] = [t.data.float().norm(norm_type) for t in input_tensors] + compute_buffer = graph_cache['norm_tensors_compute_buffer'] + + def _norm_tensors(tensor_list, _compute_buffer, _norm_type): + for i, t in enumerate(tensor_list): + _compute_buffer[i].data.copy_(t.data.float().norm(_norm_type)**_norm_type) + if i != 0: + _compute_buffer[0].data.add_(_compute_buffer[i].data) + + graph_process(False, _norm_tensors, input_tensors, compute_buffer, norm_type) + + total_norm = compute_buffer[0] + else: + total_norm = sum([t.data.float().norm(norm_type).item()**norm_type for t in input_tensors]) + + total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]).detach() + if mpu is not None: + dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=mpu.get_model_parallel_group()) + total_norm = total_norm_cuda[0].item()**(1. / norm_type) + + if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm: + total_norm = -1 + + return total_norm + + +def clip_tensors_by_global_norm(input_tensors, max_norm=1.0, global_norm=None, mpu=None, eps=1e-6, use_graph=False): + """Clip list of tensors by global norm. + Args: + input_tensors: List of tensors to be clipped + global_norm (float, optional): Precomputed norm. Defaults to None. + mpu (optional): model parallelism unit. Defaults to None. + eps (float, optional): epsilon value added to grad norm. Defaults to 1e-6 + Returns: + float: the global norm + """ + if global_norm is None: + global_norm = get_global_norm_of_tensors(input_tensors, mpu=mpu, use_graph=use_graph) + clip_coef = max_norm / (global_norm + eps) + if clip_coef < 1: + if use_graph: + + def clip_tensors(_tensor_list, _clip_coef_tensor): + for t in _tensor_list: + t.detach().mul_(_clip_coef_tensor) + + if 'clip_coef_tensor' not in graph_cache: + # Alloc memory + graph_cache['clip_coef_tensor'] = torch.tensor(clip_coef, + dtype=torch.float32).to(get_accelerator().device_name()) + clip_coef_tensor = graph_cache['clip_coef_tensor'] + clip_coef_tensor.copy_(torch.tensor(clip_coef, dtype=torch.float32)) + graph_process(False, clip_tensors, input_tensors, clip_coef_tensor) + + else: + for t in input_tensors: + t.detach().mul_(clip_coef) + return global_norm + + +def align_dense_tensors(tensor_list, alignment): + num_elements = sum(t.numel() for t in tensor_list) + remaining = num_elements % alignment + + if remaining: + elements_to_add = alignment - remaining + pad_tensor = torch.zeros(elements_to_add, device=tensor_list[0].device, dtype=tensor_list[0].dtype) + padded_tensor_list = tensor_list + [pad_tensor] + else: + padded_tensor_list = tensor_list + + return padded_tensor_list + + +def all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, dp_process_group): + for group_id, (group_flat, partitioned_params) in enumerate(zip(groups_flat, partitioned_param_groups)): + partition_id = dist.get_rank(group=dp_process_group[group_id]) + dp_world_size = dist.get_world_size(group=dp_process_group[group_id]) + if dp_world_size == 1: + # no groups share optimizer states + # pipeline parallel with bf16 will default call this even if dp size = 1. + continue + # print("call contiguous for all_gather_into_tensor_dp_groups") + dist.all_gather_into_tensor(group_flat, partitioned_params[partition_id].contiguous(), dp_process_group[group_id]) + + +def all_gather_dp_groups(groups_flat, partitioned_param_groups, dp_process_group, start_alignment_factor, + allgather_bucket_size): + if dist.has_all_gather_into_tensor(): + return all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, dp_process_group) + + for group_id, partitioned_params in enumerate(partitioned_param_groups): + # Sequential AllGather Best of both worlds + partition_id = dist.get_rank(group=dp_process_group[group_id]) + dp_world_size = dist.get_world_size(group=dp_process_group[group_id]) + + if dp_world_size == 1: + # no groups share optimizer states + # pipeline parallel with bf16 will default call this even if dp size = 1. + continue + num_shards = max(1, partitioned_params[partition_id].numel() * dp_world_size // allgather_bucket_size) + + shard_size = partitioned_params[partition_id].numel() // num_shards + + # Enforce nccl/rccl alignment of start location of each shard + shard_size = shard_size - (shard_size % start_alignment_factor) + + num_elements = shard_size + + assert shard_size * num_shards <= partitioned_params[partition_id].numel() + + for shard_id in range(num_shards): + + if shard_id == (num_shards - 1): + num_elements = partitioned_params[partition_id].numel() - shard_id * shard_size + + shard_list = [] + for dp_id in range(dp_world_size): + curr_shard = partitioned_params[dp_id].narrow(0, shard_id * shard_size, num_elements).detach() + shard_list.append(curr_shard) + + + dist.all_gather(shard_list, shard_list[partition_id], dp_process_group[group_id]) + + +class TLinear(torch.nn.Linear): + + def __init__(self, orig_layer, name=""): + self.name = name + super().__init__(orig_layer.weight.shape[1], orig_layer.weight.shape[0], bias=(orig_layer.bias is not None)) + self.weight.data = transpose(orig_layer.weight.data) + self.bias = orig_layer.bias + self._fwd_func = self._fwd_bias_add if self.bias is not None else self._fwd + + def _fwd(self, input): + return F.linear(input, self.weight) + + def _fwd_bias_add(self, input): + return F.linear(input, self.weight, bias=self.bias) + + def forward(self, input): + return self._fwd_func(input) + + +def get_inactive_params(param_list): + from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus + return [param for param in param_list if (hasattr(param, 'ds_id') and \ + param.ds_status == ZeroParamStatus.NOT_AVAILABLE)] + + +def required_torch_version(min_version=None, max_version=None): + assert min_version or max_version, "Must provide a min_version or max_version argument" + + torch_version = pkg_version.parse(torch.__version__) + + if min_version and pkg_version.parse(str(min_version)) > torch_version: + return False + + if max_version and pkg_version.parse(str(max_version)) < torch_version: + return False + + return True diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 345293921..0cd523107 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -19,6 +19,7 @@ import torch import torchvision.transforms as transforms from torch.utils.data.dataset import Dataset +from torch.utils.data import DataLoader, Dataset, get_worker_info from tqdm import tqdm from PIL import Image @@ -73,6 +74,8 @@ def __init__(self, args, transform, temporal_sample, tokenizer): if npu_config is not None: self.n_used_elements = 0 self.elements = list(range(self.__len__())) + self.worker_elements = None + random.shuffle(self.elements) print(f"n_elements: {len(self.elements)}") @@ -90,8 +93,20 @@ def __len__(self): def __getitem__(self, idx): if npu_config is not None: - idx = self.elements[self.n_used_elements % len(self.elements)] - self.n_used_elements += 1 + worker_info = get_worker_info() + if worker_info is None: # single-process data loading, return a regular index + idx = self.elements[self.n_used_elements % len(self.elements)] + self.n_used_elements += 1 + else: # in a worker process + # split workload + if self.worker_elements is None: + per_worker = int(math.ceil(len(self.elements) / float(worker_info.num_workers))) + worker_id = worker_info.id + start = worker_id * per_worker + end = min(start + per_worker, len(self.elements)) + self.worker_elements = self.elements[start:end] + idx = self.worker_elements[self.n_used_elements % len(self.worker_elements)] + self.n_used_elements += 1 try: video_data, image_data = {}, {} if self.num_frames != 1: @@ -107,7 +122,8 @@ def __getitem__(self, idx): except Exception as e: # print(f'Error with {e}') # 打印异常堆栈 - print(f"Caught an exception! {self.vid_cap_list[idx]}") + if idx in self.vid_cap_list: + print(f"Caught an exception! {self.vid_cap_list[idx]}") traceback.print_exc() traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) @@ -125,11 +141,11 @@ def get_video(self, idx): video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] - assert h / w <= 16 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 8/16 are supported. But found ratio is {round(h/w, 2)} with the shape of {video.shape}' + assert h / w <= 16 / 16 and h / w >= 4 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 4/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' t = video.shape[0] video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W - + # video = torch.rand(253, 3, 720, 1280) video = video.transpose(0, 1) # T C H W -> C T H W diff --git a/opensora/npu_config.py b/opensora/npu_config.py index f83cfd71e..ac1404cb7 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -6,7 +6,7 @@ import numpy as np import torch import subprocess - +import torch.distributed as dist try: import torch_npu @@ -79,6 +79,18 @@ def __init__(self): if self.on_npu: torch_npu.npu.set_compile_mode(jit_compile=False) + import deepspeed.runtime.bf16_optimizer as optimizer + from opensora.adaptor.bf16_optimizer import BF16_Optimizer + optimizer.BF16_Optimizer.__init__ = BF16_Optimizer.__init__ + + import deepspeed.runtime.zero.stage_1_and_2 as stage_1_and_2 + from opensora.adaptor.stage_1_and_2 import DeepSpeedZeroOptimizer + stage_1_and_2.DeepSpeedZeroOptimizer.__init__ = DeepSpeedZeroOptimizer.__init__ + stage_1_and_2.DeepSpeedZeroOptimizer.allreduce_bucket = DeepSpeedZeroOptimizer.allreduce_bucket + + import deepspeed.runtime.utils as utils + from opensora.adaptor.utils import all_gather_into_tensor_dp_groups + utils.all_gather_into_tensor_dp_groups = all_gather_into_tensor_dp_groups if "RANK" in os.environ: self.rank = int(os.environ["RANK"]) @@ -228,7 +240,8 @@ def run_conv3d(self, operator, x, out_dtype): def run_pool_2d(self, operator, x): return self._run(operator, x, torch.float16) - def seed_everything(self, seed=0): + def seed_everything(self, seed=100): + seed += self.rank random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index b5920b3bb..ffc34c997 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -144,7 +144,7 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p valid_latent_size = [[int(math.ceil((i[1]-1) / ae_stride_thw[0])) + 1 if extra_1 else int(math.ceil(i[1] / ae_stride_thw[0])), int(math.ceil(i[2] / ae_stride_thw[1])), int(math.ceil(i[3] / ae_stride_thw[2]))] for i in batch_input_size] - attention_mask = [F.pad(torch.ones(i), + attention_mask = [F.pad(torch.ones(i, dtype=pad_batch_tubes.dtype), (0, max_latent_size[2] - i[2], 0, max_latent_size[1] - i[1], 0, max_latent_size[0] - i[0]), value=0) for i in valid_latent_size] diff --git a/scripts/text_condition/train_image_256x256_on_npu.sh b/scripts/text_condition/train_image_256x256_on_npu.sh index 33385574b..07ab8ce1f 100644 --- a/scripts/text_condition/train_image_256x256_on_npu.sh +++ b/scripts/text_condition/train_image_256x256_on_npu.sh @@ -29,7 +29,7 @@ accelerate launch \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ + --learning_rate=1e-4 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ diff --git a/scripts/text_condition/train_video21d_65x240p_on_npu.sh b/scripts/text_condition/train_video21d_65x240p_on_npu.sh index 465223d07..ded5e0728 100644 --- a/scripts/text_condition/train_video21d_65x240p_on_npu.sh +++ b/scripts/text_condition/train_video21d_65x240p_on_npu.sh @@ -26,10 +26,11 @@ accelerate launch \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=1 \ - --dataloader_num_workers 10 \ + --dataloader_num_workers 15 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=4e-5 \ + --seed=10 \ --lr_scheduler="cosine" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ From b97dcc5a22abf44e9d33fb581f9c7878448c5c6b Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Wed, 12 Jun 2024 18:46:20 +0800 Subject: [PATCH 029/134] fix precision-problem of deepspeed on npu --- opensora/adaptor/bf16_optimizer.py | 9 +- opensora/adaptor/engine.py | 3559 +++++++++++++++++ opensora/adaptor/stage_1_and_2.py | 35 +- opensora/adaptor/utils.py | 4 +- opensora/dataset/t2v_datasets.py | 4 +- .../models/diffusion/latte/modeling_latte.py | 1 + opensora/models/diffusion/opensora/modules.py | 7 +- opensora/npu_config.py | 30 +- opensora/sample/sample_t2v_on_npu.py | 251 ++ opensora/train/train_t2v_diffusers.py | 237 +- .../multi_node_deepspeed.yaml | 16 + scripts/accelerate_configs/zero2_npu.json | 27 + .../text_condition/train_ds_image_256x256.sh | 2 +- .../train_image_256x256_on_npu.sh | 6 +- .../train_video21d_65x240p_on_npu.sh | 2 +- 15 files changed, 4046 insertions(+), 144 deletions(-) create mode 100644 opensora/adaptor/engine.py create mode 100644 opensora/sample/sample_t2v_on_npu.py create mode 100644 scripts/accelerate_configs/multi_node_deepspeed.yaml create mode 100644 scripts/accelerate_configs/zero2_npu.json diff --git a/opensora/adaptor/bf16_optimizer.py b/opensora/adaptor/bf16_optimizer.py index dc1ba3de7..f0ef553e0 100644 --- a/opensora/adaptor/bf16_optimizer.py +++ b/opensora/adaptor/bf16_optimizer.py @@ -26,6 +26,9 @@ setattr(sys.modules[__name__], 'fragment_address', fragment_address) +def bin_flatten(tensors): + return _flatten_dense_tensors([tensor.contiguous() for tensor in tensors]) + class BF16_Optimizer(ZeROOptimizer): @@ -40,7 +43,10 @@ def __init__(self, timers=None, grad_acc_dtype=None, graph_harvesting=False): - super().__init__() + # super().__init__() + # base_class = ZeROOptimizer.__bases__[0] + # # 直接调用基类的 __init__ 方法 + # base_class.__init__() see_memory_usage('begin bf16_optimizer', force=True) self.timers = timers self.optimizer = init_optimizer @@ -65,7 +71,6 @@ def __init__(self, #align nccl all-gather send buffers to 4-bye boundary self.nccl_start_alignment_factor = 512 - print("Set nccl_start_alignment_factor = 52...")# 4-byte alignment/sizeof(fp16) = 2 # Build BF16/FP32 groups self.bf16_groups = [] diff --git a/opensora/adaptor/engine.py b/opensora/adaptor/engine.py new file mode 100644 index 000000000..484ebe19f --- /dev/null +++ b/opensora/adaptor/engine.py @@ -0,0 +1,3559 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 + +# DeepSpeed Team + +import os +import re +import stat +import torch +import hashlib +from collections import defaultdict, OrderedDict, deque +from shutil import copyfile +import gc + +from torch.nn.modules import Module +from torch.nn.parameter import Parameter +from torch.optim import Optimizer +from torch.optim.lr_scheduler import _LRScheduler +from torch._utils import _flatten_dense_tensors, _unflatten_dense_tensors + +from typing import Callable, Dict, Union, Iterable + +import deepspeed + +from deepspeed import comm as dist +from deepspeed.runtime.utils import see_memory_usage, DummyOptim +from deepspeed.runtime.zero.offload_config import OffloadDeviceEnum +from deepspeed.runtime.zero.stage_1_and_2 import DeepSpeedZeroOptimizer +from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus +from deepspeed.runtime.zero.utils import is_zero_supported_optimizer, ZeRORuntimeException +from deepspeed.runtime.zero.parameter_offload import DeepSpeedZeRoOffload +from deepspeed.runtime.zero.config import ZERO_OPTIMIZATION + +from deepspeed.runtime.fp16.fused_optimizer import FP16_Optimizer +from deepspeed.runtime.fp16.unfused_optimizer import FP16_UnfusedOptimizer +from deepspeed.runtime.bf16_optimizer import BF16_Optimizer + +from deepspeed.runtime.config import DEEPSPEED_OPTIMIZERS, \ + ADAGRAD_OPTIMIZER, ADAM_OPTIMIZER, ADAMW_OPTIMIZER, LAMB_OPTIMIZER, ONEBIT_ADAM_OPTIMIZER, ONEBIT_LAMB_OPTIMIZER, \ + TORCH_ADAM_PARAM, ADAM_W_MODE, ADAM_W_MODE_DEFAULT, ZERO_ONE_ADAM_OPTIMIZER, MUADAM_OPTIMIZER, MUADAMW_OPTIMIZER, \ + MUSGD_OPTIMIZER, LION_OPTIMIZER + +from deepspeed.runtime.dataloader import DeepSpeedDataLoader +from deepspeed.runtime.constants import \ + ROUTE_TRAIN, ROUTE_PREDICT, ROUTE_EVAL, \ + PLD_THETA, PLD_GAMMA, BFLOAT16, FP16, AMP, GRADIENT_ACCUMULATION_STEPS, \ + DATA_PARALLEL_GROUP, GLOBAL_RANK +from deepspeed.runtime.zero.config import ZeroStageEnum +from deepspeed.compression import compression_scheduler +from deepspeed.compression.constants import \ + WEIGHT_QUANTIZE_IN_FORWARD_ENABLED, \ + WEIGHT_QUANTIZATION, SHARED_PARAMETERS, \ + WEIGHT_QUANTIZE_ENABLED, \ + WEIGHT_QUANTIZE_GROUPS, \ + WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE, \ + WEIGHT_QUANTIZE_CHANGE_RATIO, \ + WEIGHT_QUANTIZE_TYPE, \ + WEIGHT_QUANTIZE_ROUNDING, \ + WEIGHT_QUANTIZE_VERBOSE, \ + WEIGHT_QUANTIZE_KERNEL +from deepspeed.checkpoint.constants import OPTIMIZER_STATE_DICT, FROZEN_PARAM_FRAGMENTS +from deepspeed.runtime.sparse_tensor import SparseTensor + +from deepspeed.runtime import lr_schedules +from deepspeed.utils import groups +from deepspeed.utils import logger, log_dist, instrument_w_nvtx +from deepspeed.utils.timer import NoopTimer, ThroughputTimer, SynchronizedWallClockTimer, \ + FORWARD_MICRO_TIMER, BACKWARD_MICRO_TIMER, BACKWARD_INNER_MICRO_TIMER, BACKWARD_REDUCE_MICRO_TIMER, \ + STEP_MICRO_TIMER, \ + FORWARD_GLOBAL_TIMER, BACKWARD_GLOBAL_TIMER, BACKWARD_INNER_GLOBAL_TIMER, BACKWARD_REDUCE_GLOBAL_TIMER, \ + STEP_GLOBAL_TIMER +from deepspeed.utils.debug import debug_extract_module_and_param_names +from deepspeed.monitor.monitor import MonitorMaster +from deepspeed.runtime.progressive_layer_drop import ProgressiveLayerDrop +from deepspeed.runtime.utils import clip_grad_norm_ +from deepspeed.runtime.eigenvalue import Eigenvalue +from deepspeed.runtime.data_pipeline.constants import DATA_SAMPLING, \ + DATA_ROUTING, DATA_SAMPLING_ENABLED, CURRICULUM_LEARNING, \ + CURRICULUM_LEARNING_ENABLED, DATA_SAMPLING_NUM_WORKERS, RANDOM_LTD, \ + RANDOM_LTD_ENABLED, RANDOM_LTD_LAYER_ID, RANDOM_LTD_LAYER_NUM, \ + RANDOM_LTD_LAYER_TOKEN_LR_SCHEDULE, RANDOM_LTD_LAYER_TOKEN_LR_ENABLED, \ + RANDOM_LTD_GLOBAL_BATCH_SIZE, RANDOM_LTD_MICRO_BATCH_SIZE, DATA_EFFICIENCY +from deepspeed.runtime.data_pipeline.curriculum_scheduler import CurriculumScheduler +from deepspeed.runtime.data_pipeline.data_routing.scheduler import RandomLTDScheduler +from deepspeed.runtime.data_pipeline.data_routing.helper import remove_random_ltd_state_dict +from deepspeed.runtime.data_pipeline.data_routing.basic_layer import RandomLayerTokenDrop + +from deepspeed.runtime.checkpoint_engine.torch_checkpoint_engine import TorchCheckpointEngine +from deepspeed.utils.zero_to_fp32 import get_fp32_state_dict_from_zero_checkpoint + +from deepspeed.runtime.pipe.module import PipelineModule +from deepspeed.runtime.utils import get_ma_status +from deepspeed.ops.adam import FusedAdam +from deepspeed.moe.sharded_moe import TopKGate, MOELayer +from deepspeed.moe.layer import MoE +from deepspeed.moe.utils import is_moe_param +from deepspeed.git_version_info import version + +from deepspeed.profiling.flops_profiler.profiler import FlopsProfiler +from deepspeed.utils.logging import print_json_dist, print_configuration + +from deepspeed.accelerator import get_accelerator + +from deepspeed.runtime.config import DtypeEnum + +MEMORY_OPT_ALLREDUCE_SIZE = 500000000 + +DeepSpeedOptimizerCallable = \ + Callable[[Union[Iterable[Parameter], Dict[str, Iterable]]], Optimizer] +DeepSpeedSchedulerCallable = Callable[[Optimizer], _LRScheduler] + +try: + import apex + from apex import amp + APEX_INSTALLED = True +except ImportError: + # Fail silently so we don't spam logs unnecessarily if user isn't using amp + APEX_INSTALLED = False + + +def split_half_float_double_sparse(tensors): + device_type = get_accelerator().device_name() + supported_types = [ + "torch.{}.HalfTensor".format(device_type), "torch.{}.FloatTensor".format(device_type), + "torch.{}.DoubleTensor".format(device_type), "torch.{}.BFloat16Tensor".format(device_type), + SparseTensor.type() + ] + + for t in tensors: + assert t.type() in supported_types, f"attempting to reduce an unsupported grad type: {t.type()}" + + buckets = [] + for i, dtype in enumerate(supported_types): + bucket = [t for t in tensors if t.type() == dtype] + if bucket: + buckets.append((dtype, bucket)) + return buckets + + +class EngineTimers(object): + r"""Wallclock timers for DeepSpeedEngine""" + + def __init__(self, enable_micro_timers, enable_global_timers): + self.forward_timers = [] + self.backward_timers = [] + self.backward_inner_timers = [] + self.backward_reduce_timers = [] + self.step_timers = [] + self.global_timers = [] + self.micro_timers = [] + + if enable_micro_timers: + self.forward_timers += [FORWARD_MICRO_TIMER] + self.backward_timers += [BACKWARD_MICRO_TIMER] + self.backward_inner_timers += [BACKWARD_INNER_MICRO_TIMER] + self.backward_reduce_timers += [BACKWARD_REDUCE_MICRO_TIMER] + self.step_timers += [STEP_MICRO_TIMER] + self.micro_timers += [ + FORWARD_MICRO_TIMER, BACKWARD_MICRO_TIMER, BACKWARD_INNER_MICRO_TIMER, BACKWARD_REDUCE_MICRO_TIMER, + STEP_MICRO_TIMER + ] + + if enable_global_timers: + self.forward_timers += [FORWARD_GLOBAL_TIMER] + self.backward_timers += [BACKWARD_GLOBAL_TIMER] + self.backward_inner_timers += [BACKWARD_INNER_GLOBAL_TIMER] + self.backward_reduce_timers += [BACKWARD_REDUCE_GLOBAL_TIMER] + self.step_timers += [STEP_GLOBAL_TIMER] + self.global_timers += [ + FORWARD_GLOBAL_TIMER, BACKWARD_GLOBAL_TIMER, BACKWARD_INNER_GLOBAL_TIMER, BACKWARD_REDUCE_GLOBAL_TIMER, + STEP_GLOBAL_TIMER + ] + + +class DeepSpeedEngine(Module): + r"""DeepSpeed engine for training.""" + + def __init__( + self, + args, + model, + optimizer=None, + model_parameters=None, + training_data=None, + lr_scheduler=None, + mpu=None, + dist_init_required=None, + collate_fn=None, + config=None, + config_class=None, + dont_change_device=False, + ): + super(DeepSpeedEngine, self).__init__() + self.dont_change_device = dont_change_device + self.client_optimizer = optimizer + self.client_lr_scheduler = lr_scheduler + self.training_data = training_data + self.collate_fn = collate_fn + self.mpu = mpu + self.all_to_all_group = None + self.data_parallel_group = None + self.global_steps = 0 + self.global_samples = 0 + self.micro_steps = 0 + self.skipped_steps = 0 + self.gradient_average = True + self.warn_unscaled_loss = True + self.config = config + self._config = config_class + self.loaded_checkpoint_mp_world_size = None + self.loaded_checkpoint_dp_world_size = None + self.enable_backward_allreduce = True + self.progressive_layer_drop = None + self.eigenvalue = None + self.block_eigenvalue = None + self.gas_boundary_ctr = 0 + self.dist_backend = get_accelerator().communication_backend_name() + self.has_moe_layers = False + self.num_experts = [] + self.gate_modules = [] + self.moe_layers = [] + self._step_applied = False + self._global_grad_norm = None + self.use_ds_comm = False # False --> Use torch.dist, True --> Use ds.comm backend. + + self.checkpoint_engine = None + + self._is_gradient_accumulation_boundary = None + self.scale_wrt_gas = None + self.losses = 0.0 + + # for debug purposes - can then debug print: debug_get_module_name(module) + debug_extract_module_and_param_names(model) + + self._do_args_sanity_check(args) + self._configure_with_arguments(args, mpu) + self._do_sanity_check() + see_memory_usage(f"DeepSpeed Engine: After args sanity test", force=self.memory_breakdown()) + if mpu is not None: + if self.elasticity_enabled(): + if not self.is_elastic_model_parallel_supported(): + assert not self.elasticity_enabled(), ("Elasticity is not currently supported" + " with model parallelism.") + + self._set_distributed_vars(args) + + dist.configure(self._config) + + self.monitor = MonitorMaster(self._config.monitor_config) + + see_memory_usage( + f"DeepSpeed Engine: Before configure distributed model", + force=self.memory_breakdown(), + ) + + self.pipeline_parallelism = isinstance(model, PipelineModule) + + # Configure distributed model + self._configure_distributed_model(model) + + # needed for zero_to_fp32 weights reconstruction to remap nameless data to state_dict + self.param_names = {param: name for name, param in model.named_parameters()} + + self._get_model_parameters() + + see_memory_usage(f"DeepSpeed Engine: After configure distributed model") + + # Configure wall clock timers + self.timers = SynchronizedWallClockTimer() + # Throughput timer + self.tput_timer = ThroughputTimer( + batch_size=self.train_batch_size(), + steps_per_output=self.steps_per_print(), + monitor_memory=False, + ) + + log_dist(f"DeepSpeed Flops Profiler Enabled: {self.flops_profiler_enabled()}", ranks=[0]) + + if self.flops_profiler_enabled(): + self.flops_profiler = FlopsProfiler(self.module, self, self.flops_profiler_recompute_fwd_factor()) + + if training_data: + self.training_dataloader = self.deepspeed_io(training_data) + else: + self.training_dataloader = None + + # Configure optimizer and scheduler + self.optimizer = None + self.basic_optimizer = None + self.lr_scheduler = None + has_optimizer = False + + if optimizer or self.optimizer_name(): + has_optimizer = True + # If no parameters given by init default to module parameters + if model_parameters is None: + model_parameters = self.module.parameters() + + # Convert model parameters from generator to list + if not isinstance(model_parameters, list): + model_parameters = list(model_parameters) + + if has_optimizer: + self._configure_optimizer(optimizer, model_parameters) + self._configure_lr_scheduler(lr_scheduler) + self._report_progress(0) + elif self.zero_optimization(): + # no optim selected but zero is enabled + self.optimizer = self._configure_zero_optimizer(optimizer=None) + elif self.bfloat16_enabled(): + self.optimizer = self._configure_bf16_optimizer(optimizer=None) + + # Hook optimizer for snip_momentum pruning + if hasattr(model, 'pruners'): + from deepspeed.compression.helper import rewrite_optimizer_step + self.optimizer.pruners = model.pruners + rewrite_optimizer_step(self.optimizer) + + # Bookkeeping for sparse support + self.sparse_tensor_module_names = set() + # if self.sparse_gradients_enabled(): + for name, module in self.module.named_modules(): + if isinstance(module, (torch.nn.Embedding, torch.nn.EmbeddingBag)) and self.sparse_gradients_enabled(): + self.sparse_tensor_module_names.add(name + ".weight") + logger.info("Will convert {} to sparse tensor during training".format(name)) + + self.save_non_zero_checkpoint = False + self.save_zero_checkpoint = False + if not isinstance(self.optimizer, DeepSpeedZeRoOffload): + self._configure_checkpointing(dist_init_required) + + if self.eigenvalue_enabled(): + self.eigenvalue = self._configure_eigenvalue() + + if self.pld_enabled(): + self.progressive_layer_drop = self._configure_progressive_layer_drop() + + if self.curriculum_enabled_legacy(): + self.curriculum_scheduler_legacy = self._configure_curriculum_scheduler_legacy() + + if self.random_ltd_enabled(): + random_ltd_config = self.random_ltd_config() + random_ltd_config[RANDOM_LTD_GLOBAL_BATCH_SIZE] = self.train_batch_size() + random_ltd_config[RANDOM_LTD_MICRO_BATCH_SIZE] = self.train_micro_batch_size_per_gpu() + self.random_ltd_scheduler = self._configure_random_ltd_scheduler(random_ltd_config) + + # Engine timers + + self.engine_timers = EngineTimers(enable_micro_timers=self.wall_clock_breakdown(), + enable_global_timers=self.wall_clock_breakdown() + or self.flops_profiler_enabled()) + + if self.global_rank == 0: + self._config.print("DeepSpeedEngine configuration") + if self.dump_state(): + print_configuration(self, "DeepSpeedEngine") + + # Use torch (un)flatten ops + self.flatten = _flatten_dense_tensors + self.unflatten = _unflatten_dense_tensors + + def destroy(self): + if self.optimizer is not None and hasattr(self.optimizer, 'destroy'): + self.optimizer.destroy() + + def _get_model_parameters(self): + if self.autotuning_profile_model_info(): + self.autotuning_model_info = {} + num_params = 0 + trainable_num_params = 0 + + for p in self.module.parameters(): + # since user code might call deepspeed.zero.Init() before deepspeed.initialize(), need to check the attribute to check if the parameter is partitioned in zero 3 already or not + n = 0 + if hasattr(p, "ds_tensor"): # if the parameter is partitioned in zero 3 + n += p.ds_numel + else: # if the parameter is not partitioned in zero 3 yet + n += p.numel() + num_params += n + if p.requires_grad: + trainable_num_params += n + if self.global_rank == 0: + self.autotuning_model_info["num_params"] = num_params * self.mp_world_size + self.autotuning_model_info["trainable_num_params"] = trainable_num_params * self.mp_world_size + + logger.info(f"model parameter = {num_params}") + + def get_batch_info(self): + """Get all training batch related settings. + Returns: + train_batch_size (int): The effective training batch size. This is the amount of data + samples that leads to one step of model update. + train_micro_batch_size_per_gpu (int): Batch size to be processed by one GPU in one + step (without gradient accumulation). + gradient_accumulation_steps (int): Number of training steps to accumulate gradients + before averaging and applying them. + """ + return ( + self.train_batch_size, + self.train_micro_batch_size_per_gpu, + self.gradient_accumulation_steps, + ) + + def set_train_batch_size(self, train_batch_size): + """Adjust the global batch size by increasing or decreasing the number of + micro-batches (i.e., gradient accumulation steps). The size of each micro-batch + (i.e., ``train_micro_batch_size_per_gpu``) is not changed. + Args: + train_batch_size (int): The new global batch size for training. + Raises: + ValueError: if ``train_batch_size`` is not divisible by the + configured micro-batch size and data parallelism. + """ + if train_batch_size % (self.train_micro_batch_size_per_gpu() * self.dp_world_size) != 0: + #print(f'{train_batch_size=} {self.train_micro_batch_size_per_gpu()=} {self.dp_world_size=}') + raise ValueError(f'Train batch size must be divisible by micro-batch data parallelism') + new_gas = train_batch_size // (self.train_micro_batch_size_per_gpu() * self.dp_world_size) + # overwrite config + self._config.train_batch_size = train_batch_size + self._config.gradient_accumulation_steps = new_gas + + def set_train_micro_batch_size(self, micro_batch_size): + """Adjust the micro batch size(i.e., the micro batch size in every data parallel group), + while keep the gradient accumulation steps the same. + Args: + micro_batch_size (int): The new micro batch size for training. + """ + # overwrite config + new_global_batch_size = micro_batch_size * self._config.gradient_accumulation_steps * self.dp_world_size + self._config.train_batch_size = new_global_batch_size + self._config.train_micro_batch_size_per_gpu = micro_batch_size + + def set_data_post_process_func(self, post_process_func): + if self.training_dataloader is not None: + self.training_dataloader.post_process_func = post_process_func + + def set_custom_curriculum_learning_schedule(self, schedule_func_dict): + if self.training_dataloader is not None and self.curriculum_learning_enabled(): + self.training_dataloader.data_sampler.set_custom_curriculum_learning_schedule(schedule_func_dict) + + def get_global_grad_norm(self) -> float: + """Return the 2-norm of all gradients. If there is model parallelism, + the norm will be global. + The computed norm will be cached and reused until the next step() pass. + .. note:: + In the presence of model parallelism, this is a collective call + and acts as a barrier among ``mpu.get_model_parallel_group()``. + Returns: + float: norm + """ + return self._global_grad_norm + + def __getattr__(self, name): + """ + Pass through attributes defined in the model if they are not overridden by ds-engine. + """ + + _module = {} + if "module" in self.__dict__: + _module = self.__dict__['module'] + if name in dir(self): + return getattr(self, name) + elif name in dir(_module): + return getattr(_module, name) + else: + raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") + + def checkpoint_tag_validation_enabled(self): + return self._config.checkpoint_tag_validation_enabled + + def checkpoint_tag_validation_fail(self): + return self._config.checkpoint_tag_validation_fail + + def elasticity_enabled(self): + return self._config.elasticity_enabled + + def is_elastic_model_parallel_supported(self): + if self.elasticity_enabled(): + # Add code for finding number of GPUs per node automatically + if self._config.num_gpus_per_node % self._config.elastic_model_parallel_size == 0: + return True + else: + return False + + def pld_enabled(self): + return self._config.pld_enabled + + def pld_params(self): + return self._config.pld_params + + def pld_theta(self): + return self.pld_params()[PLD_THETA] + + def pld_gamma(self): + return self.pld_params()[PLD_GAMMA] + + def eigenvalue_enabled(self): + return self._config.eigenvalue_enabled + + def eigenvalue_verbose(self): + return self._config.eigenvalue_verbose + + def eigenvalue_max_iter(self): + return self._config.eigenvalue_max_iter + + def eigenvalue_tol(self): + return self._config.eigenvalue_tol + + def eigenvalue_stability(self): + return self._config.eigenvalue_stability + + def eigenvalue_gas_boundary_resolution(self): + return self._config.eigenvalue_gas_boundary_resolution + + def eigenvalue_layer_name(self): + return self._config.eigenvalue_layer_name + + def eigenvalue_layer_num(self): + return self._config.eigenvalue_layer_num + + def curriculum_enabled_legacy(self): + return self._config.curriculum_enabled_legacy + + def curriculum_params_legacy(self): + return self._config.curriculum_params_legacy + + def data_efficiency_enabled(self): + return self._config.data_efficiency_enabled + + def data_efficiency_config(self): + return self._config.data_efficiency_config + + def data_sampling_enabled(self): + return self._config.data_efficiency_config[DATA_SAMPLING][DATA_SAMPLING_ENABLED] + + def data_sampling_config(self): + return self._config.data_efficiency_config[DATA_SAMPLING] + + def curriculum_learning_enabled(self): + return self._config.data_efficiency_config[DATA_SAMPLING][CURRICULUM_LEARNING][CURRICULUM_LEARNING_ENABLED] + + def curriculum_learning_config(self): + return self._config.data_efficiency_config[DATA_SAMPLING][CURRICULUM_LEARNING] + + def random_ltd_enabled(self): + return self._config.data_efficiency_config[DATA_ROUTING][RANDOM_LTD][RANDOM_LTD_ENABLED] + + def random_ltd_config(self): + return self._config.data_efficiency_config[DATA_ROUTING][RANDOM_LTD] + + def random_ltd_initialize(self): + assert self.random_ltd_enabled() + random_ltd_config = self.random_ltd_config() + random_ltd_queue = deque([x for x in sorted(random_ltd_config[RANDOM_LTD_LAYER_ID])]) + count = 0 + for name, layer in self.module.named_modules(): + if isinstance(layer, RandomLayerTokenDrop): + if len(random_ltd_queue) != 0 and str(random_ltd_queue[0]) in name: ###[1,2,3] + layer.init_config(random_ltd_config, self.random_ltd_scheduler, count) + random_ltd_queue.popleft() + count += 1 + + if random_ltd_config[RANDOM_LTD_LAYER_NUM] != count: + raise ValueError(f'random_ltd_layer_num {random_ltd_config[RANDOM_LTD_LAYER_NUM]} must be \ + equivalent to the len of random_ltd_layer_id {count}') + + if random_ltd_config[RANDOM_LTD_LAYER_TOKEN_LR_SCHEDULE][RANDOM_LTD_LAYER_TOKEN_LR_ENABLED]: + assert self.client_lr_scheduler is None + raise ValueError(f'not yet support') + #self.lr_scheduler = lr_schedules.WarmupLayerTokenDecayLR(self.optimizer, self.random_ltd_scheduler) + + def wall_clock_breakdown(self): + return self._config.wall_clock_breakdown + + def flops_profiler_enabled(self): + return self._config.flops_profiler_config.enabled or self.autotuning_enabled() + + def flops_profiler_recompute_fwd_factor(self): + return self._config.flops_profiler_config.recompute_fwd_factor + + def flops_profiler_profile_step(self): + step = self._config.flops_profiler_config.profile_step + if self._config.autotuning_config.enabled: + step = self.autotuning_start_profile_step() + return step + + def flops_profiler_module_depth(self): + return self._config.flops_profiler_config.module_depth + + def flops_profiler_top_modules(self): + return self._config.flops_profiler_config.top_modules + + def flops_profiler_detailed(self): + if self._config.autotuning_config.enabled: + return False + return self._config.flops_profiler_config.detailed + + def flops_profiler_output_file(self): + return self._config.flops_profiler_config.output_file + + def memory_breakdown(self): + return self._config.memory_breakdown + + def autotuning_enabled(self): + return self._config.autotuning_config.enabled + + def autotuning_start_profile_step(self): + return self._config.autotuning_config.start_profile_step + + def autotuning_end_profile_step(self): + return self._config.autotuning_config.end_profile_step + + def autotuning_metric_path(self): + path = self._config.autotuning_config.metric_path + if not path: + path = os.path.join(os.getcwd(), "autotuning_metric.json") + return path + + def autotuning_model_info_path(self): + path = self._config.autotuning_config.model_info_path + if not path: + path = os.path.join(os.getcwd(), "autotuning_model_info.json") + return path + + def autotuning_metric(self): + return self._config.autotuning_config.metric + + def autotuning_profile_model_info(self): + return self.autotuning_enabled( + ) and self._config.autotuning_config.model_info and self._config.autotuning_config.model_info.get( + "profile", False) + + def sparse_gradients_enabled(self): + return self._config.sparse_gradients_enabled + + def train_batch_size(self): + return self._config.train_batch_size + + def train_micro_batch_size_per_gpu(self): + return self._config.train_micro_batch_size_per_gpu + + def optimizer_name(self): + return (self.client_optimizer.__class__.__name__ if self.client_optimizer else self._config.optimizer_name) + + def optimizer_params(self): + return self._config.optimizer_params + + def optimizer_legacy_fusion(self): + return self._config.optimizer_legacy_fusion + + def scheduler_name(self): + return self._config.scheduler_name + + def scheduler_params(self): + return self._config.scheduler_params + + def quantize_training(self): + return ( + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS] + [WEIGHT_QUANTIZE_IN_FORWARD_ENABLED], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_ENABLED], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_GROUPS], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS] + [WEIGHT_QUANTIZE_FP16_MIXED_QUANTIZE], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_CHANGE_RATIO], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_TYPE], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_ROUNDING], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_VERBOSE], + self._config.compression_config[WEIGHT_QUANTIZATION][SHARED_PARAMETERS][WEIGHT_QUANTIZE_KERNEL], + ) + + def zero_optimization(self): + return self._config.zero_enabled + + def zero_allow_untested_optimizer(self): + return self._config.zero_allow_untested_optimizer + + def zero_force_ds_cpu_optimizer(self): + return self._config.zero_force_ds_cpu_optimizer + + def zero_reduce_scatter(self): + return self._config.zero_config.reduce_scatter + + def zero_overlap_comm(self): + return self._config.zero_config.overlap_comm + + def zero_offload_optimizer(self): + return self._config.zero_config.offload_optimizer + + def zero_offload_param(self): + return self._config.zero_config.offload_param + + def zero_use_cpu_optimizer(self): + if self._config.zero_config.offload_optimizer is not None: + return self._config.zero_config.offload_optimizer.device in [OffloadDeviceEnum.cpu, OffloadDeviceEnum.nvme] + return False + + def zero_cpu_offload(self): + if self._config.zero_config.offload_optimizer is not None: + return self._config.zero_config.offload_optimizer.device == OffloadDeviceEnum.cpu + return False + + def zero_partial_offload(self): + return getattr(self._config.zero_config.offload_optimizer, "ratio", 1.0) + + def zero_sub_group_size(self): + return self._config.zero_config.sub_group_size + + def zero_optimization_stage(self): + return self._config.zero_optimization_stage + + def mics_shard_size(self): + return self._config.mics_shard_size + + def zero_reduce_bucket_size(self): + return self._config.zero_config.reduce_bucket_size + + def zero_multi_rank_bucket_allreduce(self): + return self._config.zero_config.use_multi_rank_bucket_allreduce + + def zero_allgather_bucket_size(self): + return self._config.zero_config.allgather_bucket_size + + def zero_optimization_partition_gradients(self): + return self.zero_optimization_stage() >= ZeroStageEnum.gradients + + def zero_optimization_partition_weights(self): + return self.zero_optimization_stage() >= ZeroStageEnum.weights + + def is_first_weights_partition_group(self): + ret = True if self.mics_shard_size() < 0 \ + and self.zero_optimization_partition_weights() else False + if self.mics_shard_size() > 0 and self.global_rank < self.mics_shard_size(): + ret = True + return ret + + def zero_contiguous_gradients(self): + return self._config.zero_config.contiguous_gradients + + def zero_load_from_fp32_weights(self): + return self._config.zero_config.load_from_fp32_weights + + def zero_elastic_checkpoint(self): + return self._config.zero_config.elastic_checkpoint + + def zero_max_live_parameters(self): + return self._config.zero_config.max_live_parameters + + def zero_max_reuse_distance(self): + return self._config.zero_config.max_reuse_distance + + def zero_prefetch_bucket_size(self): + return self._config.zero_config.prefetch_bucket_size + + def zero_param_persistence_threshold(self): + return self._config.zero_config.param_persistence_threshold + + def zero_model_persistence_threshold(self): + return self._config.zero_config.model_persistence_threshold + + def zero_gather_16bit_weights_on_model_save(self): + return self._config.zero_config.gather_16bit_weights_on_model_save + + def zero_grad_hooks(self): + return self._config.zero_config.grad_hooks + + def zero_legacy_stage1(self): + return self._config.zero_config.legacy_stage1 + + def zero_ignore_unused_parameters(self): + return self._config.zero_config.ignore_unused_parameters + + def graph_harvesting(self): + return self._config.graph_harvesting + + def fp16_enabled(self): + return self._config.fp16_enabled + + def bfloat16_enabled(self): + return self._config.bfloat16_enabled + + def fp16_master_weights_and_gradients(self): + return self._config.fp16_master_weights_and_gradients + + def amp_enabled(self): + return self._config.amp_enabled + + def amp_params(self): + return self._config.amp_params + + def fp16_auto_cast(self): + return self._config.fp16_auto_cast + + def loss_scale(self): + return self._config.loss_scale + + def gradient_accumulation_steps(self): + return self._config.gradient_accumulation_steps + + def use_node_local_storage(self): + return self._config.use_node_local_storage + + def load_universal_checkpoint(self): + return self._config.load_universal_checkpoint + + @property + def communication_data_type(self): + res = self._config.communication_data_type + if res is not None: + return res + + if self.fp16_enabled(): + return torch.float16 + + if self.bfloat16_enabled(): + return torch.bfloat16 + + return torch.float32 + + @communication_data_type.setter + def communication_data_type(self, value): + self._config.communication_data_type = value + + def postscale_gradients(self): + return not self._config.prescale_gradients + + def gradient_predivide_factor(self): + return self._config.gradient_predivide_factor + + def steps_per_print(self): + return self._config.steps_per_print + + def zero_allgather_partitions(self): + return self._config.zero_config.allgather_partitions + + def zero_round_robin_gradients(self): + return self._config.zero_config.round_robin_gradients + + def zero_hpz_partition_size(self): + return self._config.zero_config.zero_hpz_partition_size + + def zero_quantized_weights(self): + return self._config.zero_config.zero_quantized_weights + + def zero_quantized_nontrainable_weights(self): + return self._config.zero_config.zero_quantized_nontrainable_weights + + def zero_quantized_gradients(self): + return self._config.zero_config.zero_quantized_gradients + + def dump_state(self): + return self._config.dump_state + + def gradient_clipping(self): + return self._config.gradient_clipping + + def dynamic_loss_scale(self): + return self._config.loss_scale == 0 + + def initial_dynamic_scale(self): + return self._config.initial_dynamic_scale + + def dynamic_loss_scale_args(self): + return self._config.dynamic_loss_scale_args + + def swap_tensor_config(self): + return self._config.swap_tensor_config + + def aio_config(self): + return self._config.aio_config + + def get_data_types(self): + model_dtype = torch.float32 + if self.fp16_enabled(): + model_dtype = torch.float16 + elif self.bfloat16_enabled(): + model_dtype = torch.bfloat16 + + if self._config.grad_accum_dtype is None: + if model_dtype == torch.bfloat16 and not self.zero_optimization(): + grad_accum_dtype = torch.float32 + else: + grad_accum_dtype = model_dtype + else: + grad_accum_dtype = DtypeEnum(self._config.grad_accum_dtype).value + + return (model_dtype, grad_accum_dtype) + + def _optimizer_has_ckpt_event_prologue(self): + return self.optimizer is not None and hasattr(self.optimizer, 'checkpoint_event_prologue') + + def _optimizer_has_ckpt_event_epilogue(self): + return self.optimizer is not None and hasattr(self.optimizer, 'checkpoint_event_epilogue') + + def _configure_lr_scheduler(self, client_lr_scheduler): + # First check for scheduler in json configuration + lr_scheduler = self._scheduler_from_config(self.optimizer) + if lr_scheduler: + log_dist(f"DeepSpeed using configured LR scheduler = {self.scheduler_name()}", ranks=[0]) + self.lr_scheduler = lr_scheduler + else: + if isinstance(client_lr_scheduler, Callable): + log_dist('DeepSpeed using client callable to create LR scheduler', ranks=[0]) + self.lr_scheduler = client_lr_scheduler(self.basic_optimizer) + else: + log_dist('DeepSpeed using client LR scheduler', ranks=[0]) + self.lr_scheduler = client_lr_scheduler + + log_dist(f'DeepSpeed LR Scheduler = {self.lr_scheduler}', ranks=[0]) + + def _configure_checkpointing(self, dist_init_required): + self.checkpoint_engine = TorchCheckpointEngine() + + if self._config is not None and self._config.nebula_config.enabled: + try: + from deepspeed.runtime.checkpoint_engine.nebula_checkpoint_engine import \ + NebulaCheckpointEngine + self.checkpoint_engine = NebulaCheckpointEngine(config_params=self._config.nebula_config) + except ImportError as err: + logger.error(f"No torch_nebula was found! Will fall back to torch.save. Details: {err}") + self.checkpoint_engine = TorchCheckpointEngine() + + dp_rank = groups._get_sequence_data_parallel_rank() + + rank = self.local_rank if self.use_node_local_storage() else dp_rank + + # only the first data parallel process needs to store the model checkpoint + # if you want to use node local storage this must be done by rank 0 on each + # node + self.save_non_zero_checkpoint = (rank == 0) or (self.zero_optimization_partition_weights() + and self.is_first_weights_partition_group()) + + if self.zero_optimization() or self.bfloat16_enabled(): + param_rank = dist.get_rank(group=self.optimizer.dp_process_group) + + # Only the first parameter parallel process needs to store the + # optimizer state checkpoints for zero + self.save_zero_checkpoint = param_rank == dp_rank + + def _scheduler_from_config(self, optimizer): + scheduler_name = self.scheduler_name() + if scheduler_name is not None: + if hasattr(lr_schedules, scheduler_name): + scheduler = getattr(lr_schedules, scheduler_name) + else: + assert hasattr(torch.optim.lr_scheduler, + scheduler_name), f"DeepSpeed does not recognize LR scheduler {scheduler_name}" + + scheduler = getattr(torch.optim.lr_scheduler, scheduler_name) + + scheduler_params = self.scheduler_params() + instantiated_scheduler = scheduler(optimizer, **scheduler_params) + return instantiated_scheduler + else: + return None + + def _set_distributed_vars(self, args): + device_rank = args.device_rank if args is not None and hasattr(args, 'device_rank') else self.local_rank + if device_rank >= 0: + get_accelerator().set_device(device_rank) + self.device = torch.device(get_accelerator().device_name(), device_rank) + self.world_size = dist.get_world_size() + self.global_rank = dist.get_rank() + else: + self.world_size = 1 + self.global_rank = 0 + self.device = torch.device(get_accelerator().device_name()) + + # Configure based on command line arguments + def _configure_with_arguments(self, args, mpu): + # After the distributed backend is initialized we are guaranteed the LOCAL_RANK + # environment variable is set. We must align args.local_rank to this value for + # backwards compatibility with scripts relying on [args|self].local_rank containing + # the correct local rank info. _do_args_sanity_check will ensure this is the case. + + if "OMPI_COMM_WORLD_LOCAL_RANK" in os.environ: + ompi_local_rank = os.environ.get("OMPI_COMM_WORLD_LOCAL_RANK") + local_rank = os.environ.get('LOCAL_RANK', ompi_local_rank) + assert ompi_local_rank == local_rank, f"LOCAL_RANK ({local_rank}) != OMPI_COMM_WORLD_LOCAL_RANK ({ompi_local_rank}), " \ + "not sure how to proceed as we're seeing conflicting local rank info." + os.environ['LOCAL_RANK'] = local_rank + + self.local_rank = int(os.environ['LOCAL_RANK']) + if hasattr(args, 'local_rank'): + args.local_rank = self.local_rank + + # Validate command line arguments + def _do_args_sanity_check(self, args): + assert "LOCAL_RANK" in os.environ or "OMPI_COMM_WORLD_LOCAL_RANK" in os.environ, "DeepSpeed requires the LOCAL_RANK environment " \ + "variable, it is set by the deepspeed launcher, deepspeed.init_distributed, or the torch's launcher. If using a " \ + "different launcher please ensure LOCAL_RANK is set prior to initializing deepspeed." + + if hasattr(args, 'local_rank') and args.local_rank is not None: + assert isinstance(args.local_rank, + int), f"args.local_rank of {args.local_rank} is an unknown type {type(args.local_rank)}" + if args.local_rank >= 0: + env_local_rank = int(os.environ.get("LOCAL_RANK")) + assert ( + env_local_rank == args.local_rank + ), f"Mismatch in local rank setting, args.local_rank={args.local_rank} but env['LOCAL_RANK']={env_local_rank}." + + def _is_supported_optimizer(self, optimizer_name): + return (optimizer_name in DEEPSPEED_OPTIMIZERS or getattr(torch.optim, optimizer_name, None) is not None) + + def _supported_optims(self): + FairseqOptimizer = None + try: + from fairseq.optim.fairseq_optimizer import FairseqOptimizer + except ImportError: + pass + + expected_optim_types = [Optimizer] + if FairseqOptimizer: + # fairseq optims are not torch.optim objects + expected_optim_types.append(FairseqOptimizer) + return expected_optim_types + + # Validate configuration based on command line arguments + def _do_sanity_check(self): + expected_optim_types = self._supported_optims() + expected_optim_types += [type(None), Callable] + assert isinstance(self.client_optimizer, tuple(expected_optim_types)), \ + f'Client Optimizer is of unexpected type {type(self.client_optimizer)}' + + if not self.client_optimizer: + if self.optimizer_name() is not None: + assert self._is_supported_optimizer( + self.optimizer_name()), "{} is not a supported DeepSpeed Optimizer".format(self.optimizer_name()) + + if (self.optimizer_name() == LAMB_OPTIMIZER or self.optimizer_name() == ONEBIT_LAMB_OPTIMIZER): + assert (self.dynamic_loss_scale()), "DeepSpeed {} optimizer requires dynamic loss scaling".format( + self.optimizer_name()) + + # Detect invalid combinations of client optimizer and client scheduler + if isinstance(self.client_lr_scheduler, _LRScheduler): + assert isinstance(self.client_optimizer, Optimizer), \ + f'Client Optimizer (type = {type(self.client_optimizer)} is not instantiated but Client LR Scheduler is instantiated' + + def _broadcast_model(self): + + def is_replicated(p): + if hasattr(p, "ds_status") and p.ds_status is not ZeroParamStatus.AVAILABLE: + return False + return True + + for p in self.module.parameters(): + # Broadcast the model for different parameters + if is_moe_param(p): + if torch.is_tensor(p) and is_replicated(p): + dist.broadcast(p, + groups._get_expert_broadcast_src_rank(p.group_name), + group=self.expert_data_parallel_group[p.group_name]) + else: + if torch.is_tensor(p) and is_replicated(p): + dist.broadcast(p, groups._get_broadcast_src_rank(), group=self.seq_data_parallel_group) + + @staticmethod + def __check_params(model: Module, dtype: torch.dtype) -> None: + return + if not all(param.dtype == dtype for param in model.parameters()) and dist.get_rank() == 0: + raise ValueError(f"{dtype} is enabled but the following parameters have dtype that is " + f"not {dtype}: " + f"{[(n, p.dtype) for n, p in model.named_parameters() if p.dtype != dtype]}") + + def _set_client_model(self, model): + # register client model in _modules so that nn.module methods work correctly + modules = self.__dict__.get('_modules') + modules['module'] = model + # register module attribute in engine but avoid getattr + self.__dict__['module'] = model + + def _configure_distributed_model(self, model): + self._set_client_model(model) + is_zero_init_model = self.zero_optimization_partition_weights() and any( + [hasattr(param, "ds_id") for param in self.module.parameters()]) + + if self.fp16_enabled(): + if is_zero_init_model: + self.__check_params(self.module, torch.half) + self.module.half() + elif self.bfloat16_enabled(): + if is_zero_init_model: + self.__check_params(self.module, torch.bfloat16) + self.module.bfloat16() + else: + self.__check_params(self.module, torch.float) + + # zero.Init() handles device placement of model + if not (self.dont_change_device or is_zero_init_model): + self.module.to(self.device) + + # MoE related initialization + for _, module in self.module.named_modules(): + if isinstance(module, MoE): + self.has_moe_layers = True + self.num_experts.append(module.num_experts) + + if self.has_moe_layers: + for _, module in self.module.named_modules(): + if isinstance(module, TopKGate): + self.gate_modules.append(module) + if self.wall_clock_breakdown(): + module.wall_clock_breakdown = True + if isinstance(module, MOELayer): + self.moe_layers.append(module) + if self.wall_clock_breakdown(): + module.wall_clock_breakdown = True + + # Pass the mpu from here to groups. For subsequent use, just query groups + if self.mpu is not None: + groups.mpu = self.mpu + + # Set deepspeed parallelism spec. for the model including expert parallelism + for _, module in self.module.named_modules(): + if hasattr(module, 'set_deepspeed_parallelism'): + module.set_deepspeed_parallelism(self._config.use_data_before_expert_parallel_) + + # Query the groups module to get information about various parallel groups + self.local_all_to_all_group = None + if self.zero_quantized_gradients(): + log_dist("Using quantized gradients", ranks=[0]) + self.local_all_to_all_group = groups._get_local_all_to_all_group() + self.data_parallel_group = groups._get_data_parallel_group() + self.dp_world_size = groups._get_data_parallel_world_size() + self.seq_data_parallel_group = groups._get_sequence_data_parallel_group() + self.seq_dp_world_size = groups._get_sequence_data_parallel_world_size() + self.mp_world_size = groups._get_model_parallel_world_size() + self.expert_parallel_group = groups._get_expert_parallel_group_dict() + self.expert_data_parallel_group = groups._get_expert_data_parallel_group_dict() + self.sequence_parallel_size = groups._get_sequence_parallel_world_size() + if self.sequence_parallel_size > 1: + self.communication_data_type = self._config.seq_parallel_communication_data_type + + if not (self.amp_enabled() or is_zero_init_model): + self._broadcast_model() + + # check if parameters are duplicated in optimizer param_groups + def _check_for_duplicates(self, optimizer): + for name, param in self.module.named_parameters(): + param_id = id(param) + + def ids_list(group): + return [id(param) for param in group] + + occurrence = sum([ + ids_list(group['params']).count(param_id) if param_id in ids_list(group['params']) else 0 + for group in optimizer.param_groups + ]) + assert occurrence <= 1, f"Parameter with name: {name} occurs multiple times in optimizer.param_groups. Make sure it only appears once to prevent undefined behavior." + + def _do_optimizer_sanity_check(self, basic_optimizer): + model_dtype, grad_accum_dtype = self.get_data_types() + zero_enabled = self.zero_optimization() + amp_enabled = self.amp_enabled() + # config based assertions + assert ( + not (amp_enabled and zero_enabled) + ), "Amp and ZeRO are not currently compatible, please use (legacy) fp16 mode which performs similar to amp opt_mode=O2" + if zero_enabled: + if not is_zero_supported_optimizer(basic_optimizer): + assert ( + self.zero_allow_untested_optimizer() + ), 'You are using an untested ZeRO Optimizer. Please add <"zero_allow_untested_optimizer": true> in the configuration file to use it.' + + if self.global_rank == 0: + logger.warning("**** You are using ZeRO with an untested optimizer, proceed with caution *****") + if model_dtype == torch.bfloat16 and grad_accum_dtype == torch.float32 and self.zero_optimization_stage( + ) == 1 and not self.zero_cpu_offload(): + return BFLOAT16 + return ZERO_OPTIMIZATION + elif amp_enabled: + if model_dtype != grad_accum_dtype: + raise NotImplementedError( + "Model data type and gradient accumulation data type must be equal to use Amp") + if model_dtype == torch.bfloat16 or model_dtype == torch.float16: + raise NotImplementedError("Cannot enable both amp with (legacy) fp16 or bfloat16 mode") + try: + logger.info("Initializing Apex amp from: {}".format(amp.__path__)) + except NameError: + # If apex/amp is available it will be imported above + raise RuntimeError("Unable to import apex/amp, please make sure it is installed") + return AMP + # data type checks + elif model_dtype == grad_accum_dtype: + if model_dtype == torch.bfloat16: + if self.pipeline_parallelism: + logger.warning( + "**** BF16 gradient accumulation is not safe numerically with large number of accumulation steps, proceed with caution *****" + ) + return BFLOAT16 + else: + raise NotImplementedError( + "Bfloat16 wrapper must use a gradient accumulation type of fp32, enable ZeRO to use Bfloat16 gradient accumulation" + ) + if model_dtype == torch.float16: + return FP16 + # else optimizer_wrapper = None + elif model_dtype == torch.bfloat16 and grad_accum_dtype == torch.float32: + return BFLOAT16 + else: + raise NotImplementedError("unsupported mix of model dtype and gradient accumulation type") + + return None + + # Configure optimizer + def _configure_optimizer(self, client_optimizer, model_parameters): + if client_optimizer is None: + basic_optimizer = self._configure_basic_optimizer(model_parameters) + log_dist(f"Using DeepSpeed Optimizer param name {self.optimizer_name()} as basic optimizer", ranks=[0]) + else: + if isinstance(client_optimizer, tuple(self._supported_optims())): + basic_optimizer = client_optimizer + log_dist('Using client Optimizer as basic optimizer', ranks=[0]) + else: + basic_optimizer = client_optimizer(model_parameters) + log_dist('Using client callable to create basic optimizer', ranks=[0]) + + if self.zero_use_cpu_optimizer() and not isinstance(basic_optimizer, deepspeed.ops.adam.DeepSpeedCPUAdam): + if self.zero_force_ds_cpu_optimizer(): + msg = f'You are using ZeRO-Offload with a client provided optimizer ({type(basic_optimizer)}) which in most cases will yield poor performance. Please either use deepspeed.ops.adam.DeepSpeedCPUAdam or set an optimizer in your ds-config (https://www.deepspeed.ai/docs/config-json/#optimizer-parameters). If you really want to use a custom optimizer w. ZeRO-Offload and understand the performance impacts you can also set <"zero_force_ds_cpu_optimizer": false> in your configuration file.' + raise ZeRORuntimeException(msg) + + basic_optimizer.param_groups[:] = [pg for pg in basic_optimizer.param_groups if len(pg["params"]) != 0] + log_dist("Removing param_group that has no 'params' in the basic Optimizer", ranks=[0]) + + self._check_for_duplicates(basic_optimizer) + + self.basic_optimizer = basic_optimizer + log_dist("DeepSpeed Basic Optimizer = {}".format(basic_optimizer.__class__.__name__), ranks=[0]) + + optimizer_wrapper = self._do_optimizer_sanity_check(basic_optimizer) + + if optimizer_wrapper == ZERO_OPTIMIZATION: + self.optimizer = self._configure_zero_optimizer(basic_optimizer) + elif optimizer_wrapper == AMP: + amp_params = self.amp_params() + log_dist(f"Initializing AMP with these params: {amp_params}", ranks=[0]) + model, self.optimizer = amp.initialize(self.module, basic_optimizer, **amp_params) + self._set_client_model(model) + self._broadcast_model() + # TODO: maybe need to broadcast experts differently? + elif optimizer_wrapper == FP16: + self.optimizer = self._configure_fp16_optimizer(basic_optimizer) + elif optimizer_wrapper == BFLOAT16: + self.optimizer = self._configure_bf16_optimizer(basic_optimizer) + else: + self.optimizer = basic_optimizer + + log_dist("DeepSpeed Final Optimizer = {}".format(self.optimizer_name()), ranks=[0]) + + self.compression_scheduler = self._configure_compression_scheduler() + self.quantizer = self._configure_quantization() + + def _configure_basic_optimizer(self, model_parameters): + optimizer_parameters = self.optimizer_params() + if optimizer_parameters is None: + optimizer_parameters = {} + # print(optimizer_parameters.keys()) + if "max_grad_norm" in optimizer_parameters.keys(): + raise ValueError( + "'max_grad_norm' is not supported as an optimizer parameter, please switch to using the deepspeed parameter 'gradient_clipping' see: https://www.deepspeed.ai/docs/config-json/#gradient-clipping for more details" + ) + + if self.optimizer_name() in [ADAM_OPTIMIZER, ADAMW_OPTIMIZER]: + torch_adam = optimizer_parameters.pop(TORCH_ADAM_PARAM, False) + adam_w_mode = optimizer_parameters.pop(ADAM_W_MODE, ADAM_W_MODE_DEFAULT) + + # Optimizer name of Adam forces AdamW logic unless adam_w_mode is explicitly set + effective_adam_w_mode = self.optimizer_name() == ADAMW_OPTIMIZER or adam_w_mode + + if torch_adam: + if not effective_adam_w_mode: + optimizer = torch.optim.Adam(model_parameters, **optimizer_parameters) + else: + optimizer = torch.optim.AdamW(model_parameters, **optimizer_parameters) + else: + if self.zero_use_cpu_optimizer(): + from deepspeed.ops.adam import DeepSpeedCPUAdam + optimizer = DeepSpeedCPUAdam(model_parameters, + **optimizer_parameters, + adamw_mode=effective_adam_w_mode) + else: + from deepspeed.ops.adam import FusedAdam + + optimizer = FusedAdam( + model_parameters, + **optimizer_parameters, + adam_w_mode=effective_adam_w_mode, + ) + + elif self.optimizer_name() == ADAGRAD_OPTIMIZER: + if self.zero_use_cpu_optimizer(): + from deepspeed.ops.adagrad import DeepSpeedCPUAdagrad + optimizer = DeepSpeedCPUAdagrad(model_parameters, **optimizer_parameters) + else: + optimizer = torch.optim.Adagrad(model_parameters, **optimizer_parameters) + elif self.optimizer_name() == LAMB_OPTIMIZER: + from deepspeed.ops.lamb import FusedLamb + + optimizer = FusedLamb(model_parameters, **optimizer_parameters) + elif self.optimizer_name() == ONEBIT_ADAM_OPTIMIZER: + assert not self.zero_optimization(), "1bit-Adam is not compatible with ZeRO" + from deepspeed.runtime.fp16.onebit.adam import OnebitAdam + + optimizer = OnebitAdam(model_parameters, self, **optimizer_parameters) + if not self.fp16_enabled(): + logger.warning(f"Currently the convergence of 1-bit Adam is only verified under FP16") + elif self.optimizer_name() == ZERO_ONE_ADAM_OPTIMIZER: + assert not self.zero_optimization(), "0/1 Adam is not compatible with ZeRO" + from deepspeed.runtime.fp16.onebit.zoadam import ZeroOneAdam + + optimizer = ZeroOneAdam(model_parameters, self, **optimizer_parameters) + if not self.fp16_enabled(): + logger.warning(f'Currently the convergence of 0/1 Adam is only verified under FP16') + elif self.optimizer_name() == ONEBIT_LAMB_OPTIMIZER: + assert not self.zero_optimization(), "1bit-Lamb is not compatible with ZeRO" + from deepspeed.runtime.fp16.onebit.lamb import OnebitLamb + + optimizer = OnebitLamb(model_parameters, self, **optimizer_parameters) + if not self.fp16_enabled(): + logger.warning(f"Currently the convergence of 1-bit Lamb is only verified under FP16") + elif self.optimizer_name() == LION_OPTIMIZER: + if self.zero_use_cpu_optimizer(): + from deepspeed.ops.lion import DeepSpeedCPULion + optimizer = DeepSpeedCPULion(model_parameters, **optimizer_parameters) + else: + from deepspeed.ops.lion import FusedLion + optimizer = FusedLion(model_parameters, **optimizer_parameters) + elif self.optimizer_name() == MUADAM_OPTIMIZER: + try: + from mup import MuAdam + except ImportError: + logger.error(f"Install mup to use MuAdam optimizer") + optimizer = MuAdam(model_parameters, **optimizer_parameters) + elif self.optimizer_name() == MUADAMW_OPTIMIZER: + try: + from mup import MuAdamW + except ImportError: + logger.error(f"Install mup to use MuAdamW optimizer") + optimizer = MuAdamW(model_parameters, **optimizer_parameters) + elif self.optimizer_name() == MUSGD_OPTIMIZER: + try: + from mup import MuSGD + except ImportError: + logger.error(f"Install mup to use MuSGD optimizer") + optimizer = MuSGD(model_parameters, **optimizer_parameters) + else: + torch_optimizer = getattr(torch.optim, self.optimizer_name()) + optimizer = torch_optimizer(model_parameters, **optimizer_parameters) + return optimizer + + def _configure_compression_scheduler(self): + return compression_scheduler(self.module, self._config.compression_config) + + def _configure_random_ltd_scheduler(self, configs): + return RandomLTDScheduler(configs) + + def _configure_quantization(self): + ( + quantize_weight_in_forward, + quantize_enabled, + q_groups, + q_mixed_fp16, + q_change_ratio, + q_type, + q_rounding, + q_verbose, + use_quantizer_kernel, + ) = self.quantize_training() + if quantize_enabled and not quantize_weight_in_forward: + assert self.fp16_enabled( + ), "MoQ (quantize in optimization step) weight quantization is only supported for FP16" + quantizer = None + if quantize_enabled and not quantize_weight_in_forward: + from deepspeed.runtime.quantize import Quantizer + + quantizer = Quantizer( + q_groups, + q_mixed_fp16, + q_change_ratio, + q_type, + q_rounding, + q_verbose, + self.eigenvalue_enabled(), + use_quantizer_kernel, + self.eigenvalue_layer_num() if self.eigenvalue_enabled() else 0, + ) + return quantizer + + def _configure_fp16_optimizer(self, optimizer): + initial_dynamic_scale = self.initial_dynamic_scale() + dynamic_loss_args = self.dynamic_loss_scale_args() + clip_grad = self.gradient_clipping() + if APEX_INSTALLED: + fused_opts = (apex.optimizers.FusedAdam, FusedAdam) + else: + fused_opts = FusedAdam + if isinstance(optimizer, fused_opts) \ + or self.optimizer_name() in [ONEBIT_ADAM_OPTIMIZER, ZERO_ONE_ADAM_OPTIMIZER]: + if self.dynamic_loss_scale(): + log_dist(f'Creating fp16 optimizer with dynamic loss scale', ranks=[0]) + timers = self.timers if self.wall_clock_breakdown() else NoopTimer() + optimizer = FP16_Optimizer( + optimizer, + deepspeed=self, + dynamic_loss_scale=True, + initial_dynamic_scale=initial_dynamic_scale, + dynamic_loss_args=dynamic_loss_args, + mpu=self.mpu, + clip_grad=clip_grad, + fused_adam_legacy=self.optimizer_legacy_fusion(), + timers=timers, + has_moe_layers=self.has_moe_layers, + ) + else: + log_dist(f'Creating fp16 optimizer with static loss scale: {self.loss_scale()}', ranks=[0]) + optimizer = FP16_Optimizer( + optimizer, + deepspeed=self, + static_loss_scale=self.loss_scale(), + mpu=self.mpu, + clip_grad=clip_grad, + fused_adam_legacy=self.optimizer_legacy_fusion(), + has_moe_layers=self.has_moe_layers, + ) + else: + log_dist(f'Creating fp16 unfused optimizer with dynamic loss scale', ranks=[0]) + optimizer = FP16_UnfusedOptimizer( + optimizer, + deepspeed=self, + static_loss_scale=self.loss_scale(), + dynamic_loss_scale=self.dynamic_loss_scale(), + dynamic_loss_args=dynamic_loss_args, + mpu=self.mpu, + clip_grad=clip_grad, + fused_lamb_legacy=self.optimizer_name() == LAMB_OPTIMIZER, + ) + + return optimizer + + def _configure_bf16_optimizer(self, optimizer): + clip_grad = self.gradient_clipping() + + if optimizer is None: + optimizer = DummyOptim(list(self.module.parameters())) + + log_dist('Creating BF16 optimizer', ranks=[0]) + + timers = self.timers if self.wall_clock_breakdown() else NoopTimer() + optimizer = BF16_Optimizer(optimizer, + self.param_names, + mpu=self.mpu, + clip_grad=clip_grad, + allgather_bucket_size=self.zero_allgather_bucket_size(), + dp_process_group=self.seq_data_parallel_group, + timers=timers, + grad_acc_dtype=self.get_data_types()[1], + graph_harvesting=self.graph_harvesting()) + + return optimizer + + def _configure_zero_optimizer(self, optimizer): + zero_stage = self.zero_optimization_stage() + + mics_shard_size = self.mics_shard_size() + model_dtype, gradient_accumulation_dtype = self.get_data_types() + + timers = self.timers if self.wall_clock_breakdown() else NoopTimer() + + if optimizer is None: + optimizer = DummyOptim(list(self.module.parameters())) + + if self.zero_legacy_stage1(): + raise Exception( + "The deprecated version of ZeRO Stage 1 is not supported in deepspeed >= 0.5.9. Please downgrade to a version less than 0.5.9 if you need to use this deprecated version of ZeRO." + ) + + if zero_stage <= ZeroStageEnum.gradients: + overlap_comm = self.zero_overlap_comm() + contiguous_gradients = self.zero_contiguous_gradients() + round_robin_gradients = self.zero_round_robin_gradients() + assert not isinstance(optimizer, DummyOptim), "zero stage {} requires an optimizer".format(zero_stage) + + log_dist(f'Creating {model_dtype} ZeRO stage {zero_stage} optimizer', ranks=[0]) + # Overlap and contiguous grads are meaningless in stage 1 and are ignored + if zero_stage == ZeroStageEnum.optimizer_states: + overlap_comm = False + round_robin_gradients = False + # Non-MoE requires contiguous grads to be disabled w. stage 1 + if not self.has_moe_layers: + contiguous_gradients = False + + if isinstance(self.module, PipelineModule): + if overlap_comm: + logger.warning("Pipeline parallelism does not support overlapped communication, will be disabled.") + overlap_comm = False + optimizer = DeepSpeedZeroOptimizer( + optimizer, + self.param_names, + timers=timers, + static_loss_scale=self.loss_scale(), + dynamic_loss_scale=self.dynamic_loss_scale(), + dynamic_loss_args=self.dynamic_loss_scale_args(), + clip_grad=self.gradient_clipping(), + contiguous_gradients=contiguous_gradients, + reduce_bucket_size=self.zero_reduce_bucket_size(), + use_multi_rank_bucket_allreduce=self.zero_multi_rank_bucket_allreduce(), + allgather_bucket_size=self.zero_allgather_bucket_size(), + dp_process_group=self.seq_data_parallel_group, + expert_parallel_group=self.expert_parallel_group if self.has_moe_layers else None, + expert_data_parallel_group=self.expert_data_parallel_group if self.has_moe_layers else None, + reduce_scatter=self.zero_reduce_scatter(), + overlap_comm=overlap_comm, + offload_optimizer_config=self.zero_offload_optimizer(), + mpu=self.mpu, + postscale_gradients=self.postscale_gradients(), + gradient_predivide_factor=self.gradient_predivide_factor(), + gradient_accumulation_steps=self.gradient_accumulation_steps(), + ignore_unused_parameters=self.zero_ignore_unused_parameters(), + partition_grads=zero_stage == ZeroStageEnum.gradients, + round_robin_gradients=round_robin_gradients, + has_moe_layers=self.has_moe_layers, + fp16_master_weights_and_gradients=self.fp16_master_weights_and_gradients(), + gradient_accumulation_dtype=gradient_accumulation_dtype, + communication_data_type=self.communication_data_type, + elastic_checkpoint=self.zero_elastic_checkpoint()) + + elif zero_stage == ZeroStageEnum.weights: + assert not self.has_moe_layers, "MoE not supported with Stage 3" + if isinstance(optimizer, DummyOptim): + log_dist("Creating ZeRO Offload", ranks=[0]) + zero_param_parallel_group = groups._get_zero_param_intra_parallel_group() + if self.zero_hpz_partition_size() > 1 and zero_param_parallel_group is None: + self._set_zero_group_parallelism() + zero_param_parallel_group = groups._get_zero_param_intra_parallel_group() + optimizer = DeepSpeedZeRoOffload( + self.module, + timers=timers, + ds_config=self.config, + overlap_comm=self.zero_overlap_comm(), + prefetch_bucket_size=self.zero_prefetch_bucket_size(), + max_reuse_distance=self.zero_max_reuse_distance(), + max_live_parameters=self.zero_max_live_parameters(), + param_persistence_threshold=self.zero_param_persistence_threshold(), + model_persistence_threshold=self.zero_model_persistence_threshold(), + offload_param_config=self.zero_offload_param(), + mpu=self.mpu, + zero_param_parallel_group=zero_param_parallel_group, + zero_quantized_weights=self.zero_quantized_weights(), + zero_quantized_nontrainable_weights=self.zero_quantized_nontrainable_weights(), + ) + else: + log_dist( + f'Creating fp16 ZeRO stage {zero_stage} optimizer,' + f' MiCS is enabled {mics_shard_size>0},' + f' Hierarchical params gather {self._config.mics_hierarchial_params_gather}', + ranks=[0]) + if mics_shard_size > 0: + return self._return_mics_optimizer(optimizer, timers) + + log_dist(f'Creating {model_dtype} ZeRO stage {zero_stage} optimizer', ranks=[0]) + from deepspeed.runtime.zero.stage3 import DeepSpeedZeroOptimizer_Stage3 + optimizer = DeepSpeedZeroOptimizer_Stage3( + self.module, + optimizer, + timers=timers, + ds_config=self.config, + static_loss_scale=self.loss_scale(), + dynamic_loss_scale=self.dynamic_loss_scale(), + dynamic_loss_args=self.dynamic_loss_scale_args(), + clip_grad=self.gradient_clipping(), + contiguous_gradients=self.zero_contiguous_gradients(), + reduce_bucket_size=self.zero_reduce_bucket_size(), + prefetch_bucket_size=self.zero_prefetch_bucket_size(), + max_reuse_distance=self.zero_max_reuse_distance(), + max_live_parameters=self.zero_max_live_parameters(), + param_persistence_threshold=self.zero_param_persistence_threshold(), + model_persistence_threshold=self.zero_model_persistence_threshold(), + dp_process_group=self.seq_data_parallel_group, + all2all_process_group=self.local_all_to_all_group, + reduce_scatter=self.zero_reduce_scatter(), + overlap_comm=self.zero_overlap_comm(), + offload_optimizer_config=self.zero_offload_optimizer(), + offload_param_config=self.zero_offload_param(), + sub_group_size=self.zero_sub_group_size(), + offload_ratio=self.zero_partial_offload(), + mpu=self.mpu, + postscale_gradients=self.postscale_gradients(), + gradient_predivide_factor=self.gradient_predivide_factor(), + gradient_accumulation_steps=self.gradient_accumulation_steps(), + aio_config=self.aio_config(), + gradient_accumulation_dtype=gradient_accumulation_dtype, + communication_data_type=self.communication_data_type, + zero_hpz_partition_size=self.zero_hpz_partition_size(), + zero_quantized_weights=self.zero_quantized_weights(), + zero_quantized_nontrainable_weights=self.zero_quantized_nontrainable_weights(), + ) + + else: + raise NotImplementedError("ZeRO stage {} not implemented".format(zero_stage)) + + return optimizer + + def _return_mics_optimizer(self, basic_optimizer, timers): + from deepspeed.runtime.zero.mics import MiCS_Optimizer + model_dtype, gradient_accumulation_dtype = self.get_data_types() + optimizer = MiCS_Optimizer(self.module, + basic_optimizer, + timers=timers, + ds_config=self.config, + static_loss_scale=self.loss_scale(), + dynamic_loss_scale=self.dynamic_loss_scale(), + dynamic_loss_args=self.dynamic_loss_scale_args(), + clip_grad=self.gradient_clipping(), + contiguous_gradients=self.zero_contiguous_gradients(), + reduce_bucket_size=self.zero_reduce_bucket_size(), + prefetch_bucket_size=self.zero_prefetch_bucket_size(), + max_reuse_distance=self.zero_max_reuse_distance(), + max_live_parameters=self.zero_max_live_parameters(), + param_persistence_threshold=self.zero_param_persistence_threshold(), + model_persistence_threshold=self.zero_model_persistence_threshold(), + dp_process_group=self.seq_data_parallel_group, + reduce_scatter=self.zero_reduce_scatter(), + overlap_comm=self.zero_overlap_comm(), + offload_optimizer_config=self.zero_offload_optimizer(), + offload_param_config=self.zero_offload_param(), + sub_group_size=self.zero_sub_group_size(), + mpu=self.mpu, + postscale_gradients=self.postscale_gradients(), + gradient_predivide_factor=self.gradient_predivide_factor(), + gradient_accumulation_steps=self.gradient_accumulation_steps(), + aio_config=self.aio_config(), + gradient_accumulation_dtype=gradient_accumulation_dtype, + communication_data_type=self.communication_data_type) + return optimizer + + def _configure_eigenvalue(self): + eigenvalue = Eigenvalue( + verbose=self.eigenvalue_verbose(), + max_iter=self.eigenvalue_max_iter(), + tol=self.eigenvalue_tol(), + stability=self.eigenvalue_stability(), + gas_boundary_resolution=self.eigenvalue_gas_boundary_resolution(), + layer_name=self.eigenvalue_layer_name(), + layer_num=self.eigenvalue_layer_num(), + ) + + return eigenvalue + + def _configure_progressive_layer_drop(self): + pld = ProgressiveLayerDrop(theta=self.pld_theta(), gamma=self.pld_gamma()) + + return pld + + def _configure_curriculum_scheduler_legacy(self): + scheduler = CurriculumScheduler(self.curriculum_params_legacy()) + return scheduler + + @staticmethod + def is_map_style_dataset(obj): + return hasattr(obj, "__getitem__") and hasattr(obj, "__len__") + + @staticmethod + def is_iterable_style_dataset(obj): + return isinstance(obj, torch.utils.data.IterableDataset) # hasattr(obj, "__iter__") should work as well + + def dataloader_drop_last(self): + return self._config.dataloader_drop_last + + def was_step_applied(self) -> bool: + """Returns True if the latest ``step()`` produced in parameter updates. + Note that a ``False`` return is not an error condition. Steps are frequently + no-ops, such as between gradient accumulation boundaries or when overflows + occur. + Returns: + bool: Whether the latest ``step()`` modified model parameters. + """ + return self._step_applied + + def deepspeed_io(self, + dataset, + batch_size=None, + route=ROUTE_TRAIN, + pin_memory=True, + data_sampler=None, + collate_fn=None, + num_local_io_workers=None): + if not (self.is_map_style_dataset(dataset) or self.is_iterable_style_dataset(dataset)): + raise ValueError("Training data must be a torch Dataset") + + if batch_size is None: + batch_size = self.train_micro_batch_size_per_gpu() + + if collate_fn is None: + collate_fn = self.collate_fn + + # Currently we only use timer in train route + deepspeed_io_timer = None + if route == ROUTE_TRAIN: + deepspeed_io_timer = self.tput_timer + + # If mpu is provided, forward world size and parallel rank to sampler. + data_parallel_world_size = self.dp_world_size + data_parallel_rank = self.global_rank + if self.mpu is not None: + data_parallel_world_size = self.mpu.get_data_parallel_world_size() + data_parallel_rank = self.mpu.get_data_parallel_rank() + + if data_sampler is None and (route == ROUTE_PREDICT or route == ROUTE_EVAL): + data_sampler = torch.utils.data.DistributedSampler( + dataset, + num_replicas=data_parallel_world_size, + rank=data_parallel_rank, + shuffle=False, + ) + + deepspeed_dataloader_config = {} + if self.curriculum_learning_enabled(): + deepspeed_dataloader_config = { + CURRICULUM_LEARNING: self.curriculum_learning_enabled(), + DATA_EFFICIENCY: self.data_efficiency_config(), + DATA_PARALLEL_GROUP: self.data_parallel_group, + GRADIENT_ACCUMULATION_STEPS: self.gradient_accumulation_steps(), + GLOBAL_RANK: self.global_rank, + DATA_SAMPLING_NUM_WORKERS: self.data_sampling_config()[DATA_SAMPLING_NUM_WORKERS] + } + + return DeepSpeedDataLoader(dataset=dataset, + batch_size=batch_size, + pin_memory=pin_memory, + collate_fn=collate_fn, + local_rank=self.local_rank, + tput_timer=deepspeed_io_timer, + num_local_io_workers=num_local_io_workers, + data_sampler=data_sampler, + data_parallel_world_size=data_parallel_world_size, + data_parallel_rank=data_parallel_rank, + dataloader_drop_last=self.dataloader_drop_last(), + deepspeed_dataloader_config=deepspeed_dataloader_config) + + def train(self, mode=True): + r"""""" + + self.warn_unscaled_loss = True + self.module.train(mode) + + def eval(self): + r"""""" + + self.warn_unscaled_loss = True + self.module.train(False) + + def _scale_loss_by_gas(self, prescaled_loss): + if isinstance(prescaled_loss, torch.Tensor): + scaled_loss = prescaled_loss / self.gradient_accumulation_steps() + elif isinstance(prescaled_loss, tuple) or isinstance(prescaled_loss, list): + scaled_loss = [] + for l in prescaled_loss: + if isinstance(l, torch.Tensor): + scaled_loss.append(l / self.gradient_accumulation_steps()) + else: + scaled_loss.append(l) + else: + scaled_loss = prescaled_loss + if self.warn_unscaled_loss: + logger.warning(f"DeepSpeed unable to scale loss because of type: {type(prescaled_loss)}") + self.warn_unscaled_loss = False + + return scaled_loss + + @instrument_w_nvtx + def forward(self, *inputs, **kwargs): + r"""Execute forward propagation + Arguments: + *inputs: Variable length input list + **kwargs: variable length keyword arguments + """ + + if self.autotuning_profile_model_info(): + ma = get_ma_status() + else: + see_memory_usage("Engine before forward", force=self.memory_breakdown()) + + flops_profiler_active = (self.flops_profiler_enabled() + and self.global_steps == self.flops_profiler_profile_step() and self.global_rank == 0) + + # used to check quantization happens at step 0! + if self.global_steps == 0 and hasattr(self, "compression_scheduler"): + self.compression_scheduler.step(step_zero_check=True) + if self.quantizer: + tensor_to_quantize = self.optimizer.bit16_groups if self.zero_optimization_stage( + ) == 2 else self.optimizer.fp16_groups + if self.compression_scheduler.weight_quantization_enabled: + self.quantizer.quantize( + tensor_to_quantize, + (self.optimizer.overflow if self.fp16_enabled() else False), + self.eigenvalue_enabled(), + None, + ) + + if flops_profiler_active: + self.flops_profiler.start_profile(ignore_list=None) + + if self.module.training: + if self.progressive_layer_drop: + kwargs.update(self.progressive_layer_drop.get_state()) + + if self.__class__.__name__ != "PipelineEngine": + # TODO: The above if condition is a HACK since for PipelineEngine + # it's difficult to inject argument in forward pass. + if self.module.training and self.curriculum_enabled_legacy(): + self.curriculum_scheduler_legacy.update_difficulty(self.global_steps + 1) + if self.curriculum_params_legacy()["curriculum_type"] == "seqlen": + kwargs.update({"curriculum_seqlen": self.curriculum_scheduler_legacy.get_current_difficulty()}) + + if self.module.training and self.random_ltd_enabled(): + self.random_ltd_scheduler.update_seq(self.global_steps) + + if self.zero_optimization_partition_weights(): + # Enable automated discovery of external parameters by indicating that + # we are in a forward pass. + for module in self.module.modules(): + module._parameters._in_forward = True + + self._start_timers(self.engine_timers.forward_timers) + + if self.training_dataloader is None: + self.tput_timer.start() + + if self.fp16_auto_cast(): + inputs = self._cast_inputs_half(inputs) + # print(f"RANK[{self.global_rank}] self.fp16_auto_cast() is {self.fp16_auto_cast()}") + + loss = self.module(*inputs, **kwargs) + + # print(f"RANK[{self.global_rank}]'s loss is {loss}") + + if self.zero_optimization_partition_weights(): + # Disable automated discovery of external parameters + for module in self.module.modules(): + module._parameters._in_forward = False + + self._stop_timers(self.engine_timers.forward_timers) + + if flops_profiler_active: + self.flops_profiler.stop_profile() + + if self.autotuning_profile_model_info(): + activation_mem = get_ma_status() - ma + self.autotuning_model_info["activation_mem_per_gpu"] = activation_mem + print_json_dist(self.autotuning_model_info, [0], path=self.autotuning_model_info_path()) + exit() + else: + see_memory_usage("Engine after forward", force=self.memory_breakdown()) + return loss + + def _cast_inputs_half(self, inputs): + if isinstance(inputs, (list, tuple)): + new_inputs = [] + for v in inputs: + new_inputs.append(self._cast_inputs_half(v)) + return inputs.__class__(new_inputs) + elif isinstance(inputs, dict): + new_inputs = {} + for k, v in inputs.items(): + new_inputs[k] = self._cast_inputs_half(v) + return new_inputs + elif hasattr(inputs, 'half'): + return inputs.half() + else: + return inputs + + def print_forward_breakdown(self, fwd_time): + gate_time = 0.0 + moe_time = 0.0 + falltoall = 0.0 + salltoall = 0.0 + + for gate in self.gate_modules: + #logger.info(f"Individual TopK gate time: {gate.gate_time:.2f} ms") + gate_time += gate.gate_time + + for l in self.moe_layers: + #logger.info(f"MoE layer; total: {l.time_moe:.2f} ms, first alltoall: {l.time_falltoall:.2f}, second alltoall: {l.time_salltoall:.2f}") + moe_time += l.time_moe + falltoall += l.time_falltoall + salltoall += l.time_salltoall + + # TODO: Allreduce/average them across ranks for more accurate timing. + + # if deepspeed.comm.get_rank() == 0: + log_dist( + f"time (ms) | fwd: {fwd_time:.2f} (fwd_moe: {moe_time:.2f}, 1st_a2a: {falltoall:.2f}, 2nd_a2a: {salltoall:.2f}, top_k: {gate_time:.2f})", + ranks=[0]) + + @instrument_w_nvtx + def allreduce_gradients(self, bucket_size=MEMORY_OPT_ALLREDUCE_SIZE): + assert not (self.bfloat16_enabled() and self.pipeline_parallelism), \ + f'allreduce_gradients() is not valid when bfloat+pipeline_parallelism is enabled' + + # Pass (PP) gas boundary flag to optimizer (required for zero) + self.optimizer.is_gradient_accumulation_boundary = self.is_gradient_accumulation_boundary() + # ZeRO stage >= 2 communicates during non gradient accumulation boundaries as well + if self.zero_optimization_partition_gradients(): + self.optimizer.overlapping_partition_gradients_reduce_epilogue() + + # Communicate only at gradient accumulation boundaries + elif self.is_gradient_accumulation_boundary(): + if self.zero_optimization_stage() == ZeroStageEnum.optimizer_states and hasattr( + self.optimizer, 'reduce_gradients'): + self.optimizer.reduce_gradients(pipeline_parallel=self.pipeline_parallelism) + else: + self.buffered_allreduce_fallback(elements_per_buffer=bucket_size) + + @instrument_w_nvtx + def backward(self, loss, allreduce_gradients=True, release_loss=False, retain_graph=False, scale_wrt_gas=True): + r"""Execute backward pass on the loss + Arguments: + loss: Torch tensor on which to execute backward propagation + allreduce_gradients: is deprecated, ignored, and will soon be removed' + retain_graph: bool, default: false + forward on user defined choice of retain_graph + """ + + see_memory_usage("Engine before backward", force=self.memory_breakdown()) + + if self.scale_wrt_gas is not None: + scale_wrt_gas = self.scale_wrt_gas + + if not allreduce_gradients: + logger.warning(f"Argument `allreduce_gradients` is deprecated, ignored, and will soon be removed") + + # scale loss w.r.t. gradient accumulation if needed + if self.gradient_accumulation_steps() > 1 and scale_wrt_gas: + loss = self._scale_loss_by_gas(loss.float()) + + # Log training loss + self.losses += loss.mean().item() + if self.monitor.enabled: + if self.is_gradient_accumulation_boundary(): + if self.global_rank == 0: + self.summary_events = [( + f"Train/Samples/train_loss", + self.losses, + self.global_samples, + )] + self.monitor.write_events(self.summary_events) + + self._start_timers(self.engine_timers.backward_timers) + + assert self.optimizer is not None and not isinstance(self.optimizer, DummyOptim), \ + "must provide optimizer during init in order to use backward" + + self._start_timers(self.engine_timers.backward_inner_timers) + + if self.zero_optimization(): + self.optimizer.is_gradient_accumulation_boundary = self.is_gradient_accumulation_boundary() + self.optimizer.backward(loss, retain_graph=retain_graph) + elif self.amp_enabled(): + # AMP requires delaying unscale when inside gradient accumulation boundaries + # https://nvidia.github.io/apex/advanced.html#gradient-accumulation-across-iterations + delay_unscale = not self.is_gradient_accumulation_boundary() + with amp.scale_loss(loss, self.optimizer, delay_unscale=delay_unscale) as scaled_loss: + scaled_loss.backward(retain_graph=retain_graph) + elif self.fp16_enabled(): + if self.eigenvalue_enabled(): + self.optimizer.backward(loss, create_graph=True, retain_graph=True) + else: + self.optimizer.backward(loss, retain_graph=retain_graph) + elif self.bfloat16_enabled(): + self.optimizer.backward(loss) + else: + if self.eigenvalue_enabled(): + loss.backward(create_graph=True, retain_graph=True) + else: + loss.backward(retain_graph=retain_graph) + + self._stop_timers(self.engine_timers.backward_inner_timers) + + self._start_timers(self.engine_timers.backward_reduce_timers) + + if allreduce_gradients and self.enable_backward_allreduce: + # Traditional code path that allreduces the module parameter grads + self.allreduce_gradients() + + self._stop_timers(self.engine_timers.backward_reduce_timers) + + self._stop_timers(self.engine_timers.backward_timers) + + if release_loss: + # loss.data = None + pass + + see_memory_usage("Engine after backward", force=self.memory_breakdown()) + + return loss + + def is_gradient_accumulation_boundary(self): + """ + Query whether the current micro-batch is at the boundary of + gradient accumulation, and thus will trigger gradient reductions and + an optimizer step. + + Returns: + bool: if the current step is a gradient accumulation boundary. + + """ + if self._is_gradient_accumulation_boundary is None: + return (self.micro_steps + 1) % \ + self.gradient_accumulation_steps() == 0 + else: + return self._is_gradient_accumulation_boundary + + def set_gradient_accumulation_boundary(self, is_boundary): + """ + Manually overrides the DeepSpeed engine's gradient accumulation boundary state, this is an optional + feature and should be used with care. The state should be set before to the intended + value before each forward/backward. The final forward/backward should have the + boundary state set to True. This style allows client code to only call engine.step() once after all + the gradient accumulation passes are complete. See example below: + .. code-block:: python + engine.set_gradient_accumulation_boundary(False) + for _ in range(gradient_accumulation_steps - 1): + micro_batch = next(data_loader) + loss = engine(micro_batch) + engine.backward(loss) + engine.set_gradient_accumulation_boundary(True) + micro_batch = next(data_loader) + loss = engine(micro_batch) + engine.backward(loss) + engine.step() + Arguments: + is_boundary (bool): are we at a gradient accumulation boundary or not? + """ + self._is_gradient_accumulation_boundary = is_boundary + self.optimizer.is_gradient_accumulation_boundary = is_boundary + + def zero_grad(self): + """ + Zero parameter grads. + """ + for param_name, param in self.module.named_parameters(): + param.grad = None + + def clip_fp32_gradients(self): + clip_grad_norm_(parameters=self.module.parameters(), max_norm=self.gradient_clipping(), mpu=self.mpu) + + def _take_model_step(self, lr_kwargs, block_eigenvalue={}): + if self.gradient_clipping() > 0.0: + if not (self.fp16_enabled() or self.bfloat16_enabled() or self.amp_enabled() or self.zero_optimization()): + self.clip_fp32_gradients() + elif self.amp_enabled(): + # AMP's recommended way of doing clipping + # https://nvidia.github.io/apex/advanced.html#gradient-clipping + master_params = amp.master_params(self.optimizer) + clip_grad_norm_(parameters=master_params, max_norm=self.gradient_clipping(), mpu=self.mpu) + self.optimizer.step() + + if hasattr(self.optimizer, '_global_grad_norm'): + self._global_grad_norm = self.optimizer._global_grad_norm + + # Quantize the updated parameter if there is no overflow + if self.quantizer: + tensor_to_quantize = self.optimizer.bit16_groups if self.zero_optimization_stage( + ) == 2 else self.optimizer.fp16_groups + if self.compression_scheduler.weight_quantization_enabled: + self.quantizer.quantize( + tensor_to_quantize, + (self.optimizer.overflow if self.fp16_enabled() else False), + self.eigenvalue_enabled(), + block_eigenvalue, + ) + # zero grad in basic optimizer could be unreliable and may not exhibit + # the behavior that we want + if self.bfloat16_enabled(): + # TODO: Temporary until bf16_optimizer and zero_optimizer are integrated + if self.zero_optimization() and hasattr(self.optimizer, "zero_grad"): + self.optimizer.zero_grad() + else: + pass + elif self.zero_optimization() or self.fp16_enabled() or self.amp_enabled(): + self.optimizer.zero_grad() + else: + self.zero_grad() + + report_progress = self.global_rank == 0 if self.global_rank else True + + # Check overflow here since in DS fp16 optimizer, the overflow is updated in above step() function. + overflow = False + if hasattr(self.optimizer, "overflow"): + overflow = self.optimizer.overflow + self._step_applied = not overflow + + if overflow: + self.skipped_steps += 1 + else: + self.compression_scheduler.step() + if self.lr_scheduler is not None: + try: + self.lr_scheduler.step(**(lr_kwargs or {})) + except TypeError: + # XXX Hack to work with Megatron 2.0 and DeepSpeed pipelines. + # We don't currently have a way to specify lr_kwargs from + # pipe_engine.train_batch() + self.lr_scheduler.step(self.train_batch_size()) + + if report_progress and (self.global_steps + 1) % self.steps_per_print() == 0: + self._report_progress(self.global_steps + 1) + + self.losses = 0.0 + self.global_steps += 1 + self.global_samples += self.train_batch_size() + + def step(self, lr_kwargs=None): + r"""Execute the weight update step after forward and backward propagation + on effective_train_batch. + """ + see_memory_usage("Engine before step", force=self.memory_breakdown()) + + # Check early because self.global_steps is incremented at some point here. + # TODO: Delay self.global_steps increment until very end of this function. + flops_profiler_active = self.flops_profiler_enabled( + ) and self.global_steps == self.flops_profiler_profile_step() and self.global_rank == 0 + + self._start_timers(self.engine_timers.step_timers) + + assert self.optimizer is not None and not isinstance(self.optimizer, DummyOptim), \ + "must provide optimizer during init in order to use step" + + report_progress = False + + self._step_applied = False # assume False, will flip to True + + # Update the model when we reach gradient accumulation boundaries + if self.is_gradient_accumulation_boundary(): + self.gas_boundary_ctr += 1 + + if (self.eigenvalue_enabled() and (self.gas_boundary_ctr % self.eigenvalue_gas_boundary_resolution() == 0) + and self.quantizer.any_precision_switch()): + log_dist(f"computing eigenvalue...", ranks=[0]) + self.block_eigenvalue = self.eigenvalue.compute_eigenvalue(self.module, self.device, + self.optimizer.cur_scale) + + if self.progressive_layer_drop: + self.progressive_layer_drop.update_state(self.global_steps) + + if (self.eigenvalue_enabled() and not self.gas_boundary_ctr % self.eigenvalue_gas_boundary_resolution() + and self.quantizer.any_precision_switch()): + self._take_model_step(lr_kwargs, self.block_eigenvalue) + else: + self._take_model_step(lr_kwargs) + + report_progress = self.global_rank == 0 if self.global_rank else True + + self.tput_timer.stop(global_step=self.is_gradient_accumulation_boundary(), report_speed=report_progress) + + self._stop_timers(self.engine_timers.step_timers) + + # Log learning rate + if self.monitor.enabled: + if self.is_gradient_accumulation_boundary(): + if self.global_rank == 0: + self.summary_events = [(f"Train/Samples/lr", self.get_lr()[0], self.global_samples)] + + if self.fp16_enabled() and hasattr(self.optimizer, "cur_scale"): + self.summary_events.append(( + f"Train/Samples/loss_scale", + self.optimizer.cur_scale, + self.global_samples, + )) + + if (self.eigenvalue_enabled() + and not self.gas_boundary_ctr % self.eigenvalue_gas_boundary_resolution()): + ev_values = self.block_eigenvalue.values() + for i in range(len(ev_values)): + self.summary_events.append(( + f"Train/Eigenvalues/ModelBlockParam_{i}", + self.ev_values[i][0], + self.global_samples, + )) + self.monitor.write_events(self.summary_events) + + # Check flops profiling + if flops_profiler_active: + if self.autotuning_enabled(): + self.flops = self.flops_profiler.get_total_flops() * 3 + self.fwd_duration = self.flops_profiler.get_total_duration() + else: + self.flops_profiler.print_model_profile( + profile_step=self.global_steps, + module_depth=self.flops_profiler_module_depth(), + top_modules=self.flops_profiler_top_modules(), + detailed=self.flops_profiler_detailed(), + output_file=self.flops_profiler_output_file(), + ) + self.flops_profiler.end_profile() + + if self.autotuning_enabled() and self.global_steps == (self.autotuning_end_profile_step() + 1): + self._autotuning_exit() + + if self.wall_clock_breakdown(): + # Log micro timing and reset + self.timers.log(names=self.engine_timers.micro_timers, memory_breakdown=self.memory_breakdown()) + + if self.wall_clock_breakdown() or self.flops_profiler_enabled(): + # Log global timing and reset + if self.is_gradient_accumulation_boundary(): + if self.monitor.enabled: + self._write_monitor() + + if self.has_moe_layers: + fwd_time = self.timers(FORWARD_GLOBAL_TIMER).elapsed(reset=False) + self.print_forward_breakdown(fwd_time=fwd_time) + + self.timers.log(self.engine_timers.global_timers) + + self.micro_steps += 1 + see_memory_usage("Engine after step", force=self.memory_breakdown()) + + def _start_timers(self, timer_names): + for name in timer_names: + self.timers(name).start() + + def _stop_timers(self, timer_names): + record = self.is_gradient_accumulation_boundary() and \ + self.flops_profiler_enabled() and \ + (self.global_steps >= self.flops_profiler_profile_step()) + for name in timer_names: + self.timers(name).stop(record=record) + + def _autotuning_exit(self): + if self.global_rank == 0: + msg = self.timers.get_mean([ + FORWARD_GLOBAL_TIMER, + BACKWARD_GLOBAL_TIMER, + STEP_GLOBAL_TIMER, + ], reset=False) + titer = 0.0 + titer += msg[FORWARD_GLOBAL_TIMER] if FORWARD_GLOBAL_TIMER in msg else 0 + titer += msg[BACKWARD_GLOBAL_TIMER] if BACKWARD_GLOBAL_TIMER in msg else 0 + titer += msg[STEP_GLOBAL_TIMER] if STEP_GLOBAL_TIMER in msg else 0 + titer *= self.gradient_accumulation_steps() + msg["latency"] = titer + msg["FLOPS_per_gpu"] = self.flops * 1_000_000 * self.gradient_accumulation_steps() / titer + msg["throughput"] = self.train_batch_size() * 1_000_000 / \ + msg["latency"] + print_json_dist(msg, [0], path=self.autotuning_metric_path()) + log_dist( + f"Wrote metrics to {self.autotuning_metric_path()}, {os.path.abspath(self.autotuning_metric_path())}", + ranks=[0]) + import atexit + atexit.register(print, "Autotuning: done with running current ds config.") + exit() + + def _write_monitor(self): + if self.global_rank == 0: + self.summary_events = [ + ( + f"Train/Samples/elapsed_time_ms_forward", + self.timers(FORWARD_GLOBAL_TIMER).elapsed(reset=False), + self.global_samples, + ), + ( + f"Train/Samples/elapsed_time_ms_backward", + self.timers(BACKWARD_GLOBAL_TIMER).elapsed(reset=False), + self.global_samples, + ), + ( + f"Train/Samples/elapsed_time_ms_backward_inner", + self.timers(BACKWARD_INNER_GLOBAL_TIMER).elapsed(reset=False), + self.global_samples, + ), + ( + f"Train/Samples/elapsed_time_ms_backward_allreduce", + self.timers(BACKWARD_REDUCE_GLOBAL_TIMER).elapsed(reset=False), + self.global_samples, + ), + ( + f"Train/Samples/elapsed_time_ms_step", + self.timers(STEP_GLOBAL_TIMER).elapsed(reset=False), + self.global_samples, + ), + ] + self.monitor.write_events(self.summary_events) + + def _get_optimizer_param(self, param_name): + result = [] + if not self.optimizer: + return result + for group in self.optimizer.param_groups: + if param_name in group: + result.append(group[param_name]) + else: + result.append(0.0) + return result + + def get_lr(self): + return self._get_optimizer_param("lr") + + def get_type(self): + return self._get_optimizer_param("type") + + def get_mom(self): + if self.optimizer_name() in ["SGD", "RMSprop"]: + return self._get_optimizer_param("momentum") + else: + return self._get_optimizer_param("betas") + + def get_pld_theta(self): + if self.progressive_layer_drop: + return self.progressive_layer_drop.get_theta() + else: + return None + + def _report_progress(self, step): + lr = self.get_lr() + mom = self.get_mom() + log_dist(f"step={step}, skipped={self.skipped_steps}, lr={lr}, mom={mom}", ranks=[0]) + + def allreduce_bucket(self, bucket, dp_group): + tensor = self.flatten(bucket) + + tensor_to_allreduce = tensor + + if self.communication_data_type != tensor.dtype: + tensor_to_allreduce = tensor.to(self.communication_data_type) + + if self.postscale_gradients(): + if self.gradient_predivide_factor() != 1.0: + tensor_to_allreduce.mul_(1.0 / self.gradient_predivide_factor()) + + dist.all_reduce(tensor_to_allreduce, group=dp_group) + if self.gradient_average: + if self.gradient_predivide_factor() != dist.get_world_size(group=dp_group): + tensor_to_allreduce.mul_(self.gradient_predivide_factor() / dist.get_world_size(group=dp_group)) + else: + tensor_to_allreduce.mul_(1. / dist.get_world_size(group=dp_group)) + dist.all_reduce(tensor_to_allreduce, group=dp_group) + + if self.communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce: + tensor.copy_(tensor_to_allreduce) + + return tensor + + def allreduce_and_copy(self, small_bucket, dp_group): + allreduced = self.allreduce_bucket(small_bucket, dp_group) + for buf, synced in zip(small_bucket, self.unflatten(allreduced, small_bucket)): + buf.copy_(synced) + + def allreduce_no_retain(self, bucket, dp_group, numel_per_bucket=500000000): + small_bucket = [] + numel = 0 + for tensor in bucket: + small_bucket.append(tensor) + numel = numel + tensor.numel() + if numel > numel_per_bucket: + self.allreduce_and_copy(small_bucket, dp_group) + small_bucket = [] + numel = 0 + if len(small_bucket) > 0: + self.allreduce_and_copy(small_bucket, dp_group) + + def _get_gradients_for_reduction(self): + non_expert_grads = [] + expert_grads = {} + if self.has_moe_layers: + for key in self.expert_data_parallel_group.keys(): + expert_grads[key] = [] + + for param_name, param in self.module.named_parameters(): + if not param.requires_grad: + continue + + if param.grad is None: + # In cases where there is an imbalance of empty grads across + # ranks we must create empty grads, this will ensure that every + # rank is reducing the same size. In some cases it may make + # sense in the future to support the ability to average not + # w.r.t. world size but with a different value. + param.grad = torch.zeros(param.size(), dtype=param.dtype, device=param.device) + + grad_data = param.grad.data + if param_name in self.sparse_tensor_module_names or grad_data.is_sparse: + # Call param.grad without data to avoid problem with setting of updated grads + grad_data = SparseTensor(param.grad) + + if is_moe_param(param): + expert_grads[param.group_name].append(grad_data) + else: + non_expert_grads.append(grad_data) + + return non_expert_grads, expert_grads + + def _reduce_non_expert_gradients(self, grads, elements_per_buffer): + split_buckets = split_half_float_double_sparse(grads) + for _, bucket_tuple in enumerate(split_buckets): + bucket_type, bucket = bucket_tuple + + if self.pipeline_parallelism: + dp_group = self.mpu.get_data_parallel_group() + else: + dp_group = groups._get_sequence_data_parallel_group() + + if bucket_type == SparseTensor.type(): + self.sparse_allreduce_no_retain(bucket, dp_group=dp_group) + else: + self.allreduce_no_retain(bucket, dp_group=dp_group, numel_per_bucket=elements_per_buffer) + + def _reduce_expert_gradients(self, expert_grads, elements_per_buffer): + for ep_name, expert_grads_group in expert_grads.items(): + expert_split_buckets = split_half_float_double_sparse(expert_grads_group) + for i, bucket_tuple in enumerate(expert_split_buckets): + bucket_type, bucket = bucket_tuple + if bucket_type == SparseTensor.type(): + self.sparse_allreduce_no_retain(bucket, groups._get_expert_data_parallel_group(ep_name)) + else: + # Separate between diff groups + self.allreduce_no_retain(bucket, + dp_group=groups._get_expert_data_parallel_group(ep_name), + numel_per_bucket=elements_per_buffer) + + def buffered_allreduce_fallback(self, grads=None, elements_per_buffer=500000000): + if grads is None: + non_expert_grads, expert_grads = self._get_gradients_for_reduction() + else: + assert not self.has_moe_layers, "attempting to reduce grads in unsupported way w.r.t. MoE" + non_expert_grads = grads + + self._reduce_non_expert_gradients(non_expert_grads, elements_per_buffer) + + if self.has_moe_layers: + self._reduce_expert_gradients(expert_grads, elements_per_buffer) + + def sparse_allreduce_no_retain(self, bucket, dp_group): + allreduced_sparses = self.sparse_allreduce_bucket(bucket, dp_group) + # Densify sparse tensor and copy back to original location + for tensor in allreduced_sparses: + if tensor.is_sparse: + tensor.orig_dense_tensor.data = tensor.to_coo_tensor() + else: + tensor.orig_dense_tensor.copy_(tensor.to_dense()) + + def sparse_allreduce_bucket(self, bucket, dp_group): + sparse_list = [] + for sparse in bucket: + sparse_list.append(self.sparse_allreduce(sparse, dp_group)) + return sparse_list + + def sparse_allreduce(self, sparse, dp_group): + original_data_type = sparse.values.dtype + if self.communication_data_type != sparse.values.dtype: + if self.communication_data_type in (torch.float16, torch.bfloat16): + indices = sparse.indices.to(torch.int32) + else: + indices = sparse.indices + values = sparse.values.to(self.communication_data_type) + else: + indices = sparse.indices + values = sparse.values + + if self.postscale_gradients(): + if self.gradient_average: + values.mul_(self.gradient_predivide_factor() / + (dist.get_world_size(group=dp_group) / float(self.sequence_parallel_size))) + else: + values.mul_(1. / (dist.get_world_size(group=dp_group) / float(self.sequence_parallel_size))) + + indices_device_list = self.sparse_all_gather(indices, dp_group) + values_device_list = self.sparse_all_gather(values, dp_group) + + sparse.indices = torch.cat(indices_device_list).to(torch.long) + sparse.values = torch.cat(values_device_list).to(original_data_type) + return sparse + + def sparse_all_gather(self, value, dp_group): + my_size = torch.LongTensor([value.size()[0]]).to(self.device) + all_sizes = self.all_gather_scalar(my_size, dp_group) + max_size = torch.cat(all_sizes).max() + fill_size = max_size - my_size + + assert value.dim() in [1, 2] + if value.dim() == 1: + if fill_size > 0: + value = torch.cat([value, value.new_empty(fill_size)]) + tensor_list = [value.new_empty(max_size) for _ in range(dist.get_world_size(group=dp_group))] + else: + if fill_size > 0: + value = torch.cat([value, value.new_empty(fill_size, value.size()[1])]) + tensor_list = [ + value.new_empty(max_size, + value.size()[1]) for _ in range(dist.get_world_size(group=dp_group)) + ] + + dist.all_gather(tensor_list, value, group=dp_group) + tensors = [] + for dev_idx, t in enumerate(tensor_list): + size = all_sizes[dev_idx][0] + tensors.append(t.index_select(0, torch.arange(size, dtype=torch.long, device=self.device))) + + return tensors + + def all_gather_scalar(self, value, dp_group): + tensor_list = [value.new_zeros(value.size()) for _ in range(dist.get_world_size(group=dp_group))] + dist.all_gather(tensor_list, value, group=dp_group) + return tensor_list + + def module_state_dict(self, destination=None, prefix="", keep_vars=False, exclude_frozen_parameters=False): + sd = self.module.state_dict(destination, prefix, keep_vars) + + # Remove frozen parameter weights from state_dict if specified + if exclude_frozen_parameters: + for n, p in self.module.named_parameters(): + if not p.requires_grad and n in sd: + del sd[n] + + if self.random_ltd_enabled(): + sd = remove_random_ltd_state_dict(sd) + return sd + + @staticmethod + def load_moe_state_dict(checkpoint_path, + tag, + state_dict, + old_moe_load, + model=None, + mpu=None, + num_experts=1, + checkpoint_engine=TorchCheckpointEngine()): + if old_moe_load: + expp_rank = groups._get_expert_data_parallel_rank(groups._get_max_expert_size_name()) + + num_local_experts = max(num_experts) // groups._get_expert_parallel_world_size( + groups._get_max_expert_size_name()) + for local_expert_id in range(num_local_experts): + global_expert_id = expp_rank * num_local_experts + local_expert_id + expert_state_dict = checkpoint_engine.load( + DeepSpeedEngine._get_expert_ckpt_name( + checkpoint_path, + -1, # -1 means ignore layer_id + global_expert_id, + tag, + mpu), + map_location=torch.device('cpu')) + + # Updating global -> local expert ids + moe_str_prefix = '.deepspeed_moe.experts.deepspeed_experts.' + for key in list(expert_state_dict.keys()): + local_key = key.replace(f'{moe_str_prefix}{global_expert_id}', + f'{moe_str_prefix}{local_expert_id}') + expert_state_dict[local_key] = expert_state_dict.pop(key) + state_dict.update(expert_state_dict) + + else: + moe_layer_id = 0 + for n_module, module in model.named_modules(): + if isinstance(module, MoE): # and deepspeed.comm.get_rank() == 0: + group_name = module.expert_group_name + num_local_experts = module.num_local_experts + expp_rank = groups._get_expert_parallel_rank(group_name) + # loop all local_experts + for local_expert_id in range(num_local_experts): + global_expert_id = expp_rank * num_local_experts + local_expert_id + expert_state_dict = checkpoint_engine.load(DeepSpeedEngine._get_expert_ckpt_name( + checkpoint_path, moe_layer_id, global_expert_id, tag, mpu), + map_location=torch.device('cpu')) + # print(expert_state_dict.keys()) + # Updating global -> local expert ids + moe_str_prefix = '.deepspeed_moe.experts.deepspeed_experts.' + for key in list(expert_state_dict.keys()): + local_key = key.replace(f'{moe_str_prefix}{global_expert_id}', + f'{moe_str_prefix}{local_expert_id}') + expert_state_dict[local_key] = expert_state_dict.pop(key) + state_dict.update(expert_state_dict) + moe_layer_id += 1 + + def load_module_state_dict(self, checkpoint, strict=True, custom_load_fn=None, fetch_z3_params=False): + if fetch_z3_params: + params_to_fetch = [ + p for p in self.module.parameters() + if hasattr(p, 'ds_id') and p.ds_status == ZeroParamStatus.NOT_AVAILABLE + ] + else: + params_to_fetch = [] + + with deepspeed.zero.GatheredParameters(params_to_fetch, modifier_rank=0): + module_state_dict = checkpoint['module'] + if custom_load_fn: + custom_load_fn(src=module_state_dict, dst=self.module) + else: + self.module.load_state_dict( + module_state_dict, # TODO + strict=strict) + + if checkpoint.get(FROZEN_PARAM_FRAGMENTS, None) is not None: + saved_frozen_params = checkpoint[FROZEN_PARAM_FRAGMENTS] + for param in self.module.parameters(): + if param.requires_grad: + continue + if param not in self.param_names: + raise ValueError(f"failed to find frozen {param} in named params") + name = self.param_names[param] + if hasattr(param, 'ds_id'): + param.ds_tensor.data.copy_(saved_frozen_params[name].data) + else: + param.data.copy_(saved_frozen_params[name].data) + + def _get_zero_ckpt_prefix(self, dp_rank, bf16_mode): + return f'{"bf16_" if bf16_mode else ""}zero_pp_rank_{dp_rank}' + + def _get_rank_zero_ckpt_name(self, checkpoints_path, tag, mp_rank, dp_rank, bf16_mode): + file_prefix = self._get_zero_ckpt_prefix(dp_rank, bf16_mode=bf16_mode) + zero_ckpt_name = os.path.join( + checkpoints_path, + str(tag), + f"{file_prefix}_mp_rank_{mp_rank:02d}_optim_states.pt", + ) + return zero_ckpt_name + + def _get_zero_ckpt_name(self, checkpoints_path, tag): + mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank() + pp_rank = dist.get_rank(group=self.optimizer.dp_process_group) + bf16_mode = self.bfloat16_enabled() + return self._get_rank_zero_ckpt_name(checkpoints_path, tag, mp_rank, pp_rank, bf16_mode) + + def _get_ckpt_name(self, checkpoints_path, tag, mp_placeholder=None): + if mp_placeholder is not None: + mp_rank_str = mp_placeholder + else: + mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank() + mp_rank_str = f"{mp_rank:02d}" + + if self.zero_optimization_partition_weights(): + filename = "zero_pp_rank_{}".format(dist.get_rank(group=self.optimizer.dp_process_group)) + ckpt_name = os.path.join( + checkpoints_path, + str(tag), + f"{filename}_mp_rank_{mp_rank_str}_model_states.pt", + ) + else: + ckpt_name = os.path.join( + checkpoints_path, + str(tag), + "mp_rank_" + mp_rank_str + "_model_states.pt", + ) + return ckpt_name + + def _get_optimizer_ckpt_name(self, checkpoints_path, tag, expp_rank): + mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank() + ckpt_name = os.path.join(checkpoints_path, str(tag), + f'expp_rank_{expp_rank}_mp_rank_{mp_rank:02d}_optim_states.pt') + return ckpt_name + + @staticmethod + def _get_expert_ckpt_name(checkpoints_path, layer_id, expert_id, tag, mpu=None): + mp_rank = 0 if mpu is None else mpu.get_model_parallel_rank() + if layer_id <= -1: + # Used to support old checkpoint loading + ckpt_name = os.path.join(checkpoints_path, '' if tag is None else str(tag), + f'expert_{expert_id}_mp_rank_{mp_rank:02d}_model_states.pt') + else: + # Used to support new checkpoint loading + ckpt_name = os.path.join(checkpoints_path, '' if tag is None else str(tag), + f'layer_{layer_id}_expert_{expert_id}_mp_rank_{mp_rank:02d}_model_states.pt') + return ckpt_name + + def _get_all_ckpt_names(self, checkpoints_path, tag): + # It is required that (checkpoints_path, tag) are consistent among all ranks. + ckpt_file_pattern = self._get_ckpt_name(checkpoints_path, tag, mp_placeholder="*") + import glob + + ckpt_files = glob.glob(ckpt_file_pattern) + ckpt_files.sort() + return ckpt_files + + def load_checkpoint(self, + load_dir, + tag=None, + load_module_strict=True, + load_optimizer_states=True, + load_lr_scheduler_states=True, + load_module_only=False, + custom_load_fn=None): + """ + Load training checkpoint + + Arguments: + load_dir: Required. Directory to load the checkpoint from + tag: Checkpoint tag used as a unique identifier for checkpoint, if not provided will attempt to load tag in 'latest' file + load_module_strict: Optional. Boolean to strictly enforce that the keys in state_dict of module and checkpoint match. + load_optimizer_states: Optional. Boolean to load the training optimizer states from Checkpoint. Ex. ADAM's momentum and variance + load_lr_scheduler_states: Optional. Boolean to add the learning rate scheduler states from Checkpoint. + load_module_only: Optional. Boolean to load only the model weights from the checkpoint. Ex. warmstarting. + custom_load_fn: Optional. Custom model load function. + + Returns: + A tuple of ``load_path`` and ``client_state``. + *``load_path``: Path of the loaded checkpoint. ``None`` if loading the checkpoint failed. + *``client_state``: State dictionary used for loading required training states in the client code. + + Important: under ZeRO3, one cannot load checkpoint with ``engine.load_checkpoint()`` right + after ``engine.save_checkpoint()``. It is because ``engine.module`` is partitioned, and + ``load_checkpoint()`` wants a pristine model. If insisting to do so, please reinitialize engine + before ``load_checkpoint()``. + + """ + + if tag is None: + latest_tag = "latest_universal" if self.load_universal_checkpoint() else "latest" + latest_path = os.path.join(load_dir, latest_tag) + if os.path.isfile(latest_path): + with open(latest_path, "r") as fd: + tag = fd.read().strip() + else: + if self.load_universal_checkpoint(): + raise ValueError(f'Invalid for universal checkpoint: {latest_path} does not exist') + else: + logger.warning( + f"Unable to find latest file at {latest_path}, if trying to load latest " + "checkpoint please ensure this file exists or pass an explicit checkpoint tag when loading a checkpoint." + ) + return None, None + + if self._optimizer_has_ckpt_event_prologue(): + # Prepare for checkpoint load by ensuring all parameters are partitioned + self.optimizer.checkpoint_event_prologue() + + load_path, client_states = self._load_checkpoint(load_dir, + tag, + load_module_strict=load_module_strict, + load_optimizer_states=load_optimizer_states, + load_lr_scheduler_states=load_lr_scheduler_states, + load_module_only=load_module_only, + custom_load_fn=custom_load_fn) + + load_zero_checkpoint = load_optimizer_states and load_path is not None and (self.zero_optimization() + or self.bfloat16_enabled()) + if load_zero_checkpoint: + success = self._load_zero_checkpoint(load_dir, tag, load_optimizer_states=load_optimizer_states) + if not success: + self.optimizer._restore_from_bit16_weights() + + if self._optimizer_has_ckpt_event_epilogue(): + self.optimizer.checkpoint_event_epilogue() + + if self.load_universal_checkpoint(): + self.optimizer.update_lp_params() + if load_zero_checkpoint: + self.update_optimizer_step(step=client_states['iteration'] + 1) + + return load_path, client_states + + def _load_checkpoint(self, + load_dir, + tag, + load_module_strict=True, + load_optimizer_states=True, + load_lr_scheduler_states=True, + load_module_only=False, + custom_load_fn=None): + + from deepspeed.runtime.state_dict_factory import SDLoaderFactory + + ckpt_list = self._get_all_ckpt_names(load_dir, tag) + sd_loader = SDLoaderFactory.get_sd_loader(ckpt_list, checkpoint_engine=self.checkpoint_engine) + + is_pipe_parallel = isinstance(self.module, PipelineModule) + + mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank() + load_path, checkpoint, _ = sd_loader.load(self.mp_world_size, mp_rank, is_pipe_parallel=is_pipe_parallel) + + if checkpoint is None: + return None, None + + fetch_z3_params = False + if self.zero_optimization_partition_weights() and not load_optimizer_states: + checkpoint['module'] = get_fp32_state_dict_from_zero_checkpoint(load_dir) + fetch_z3_params = True + + if is_pipe_parallel: + # Pipeline parallelism uses this to load its own checkpoint files. + self._curr_ckpt_path = os.path.join(load_dir, tag) + + if self.has_moe_layers: + # print(checkpoint.keys()) + old_moe_load = False + if not isinstance(checkpoint['num_experts'], list): + old_moe_load = True + DeepSpeedEngine.load_moe_state_dict(load_dir, + tag, + state_dict=checkpoint['module'], + old_moe_load=old_moe_load, + model=self.module, + mpu=self.mpu, + num_experts=self.num_experts, + checkpoint_engine=self.checkpoint_engine) + if not self.load_universal_checkpoint(): + self.load_module_state_dict(checkpoint=checkpoint, + strict=load_module_strict, + custom_load_fn=custom_load_fn, + fetch_z3_params=fetch_z3_params) + + self.loaded_checkpoint_dp_world_size = checkpoint['dp_world_size'] + + optim_checkpoint = None + if load_module_only: + deepspeed_states = ['module'] + if self.optimizer is not None and self.fp16_enabled(): + self.optimizer.refresh_fp32_params() + else: + has_zero_optimizer_state = self.zero_optimization() or self.bfloat16_enabled() + if load_optimizer_states and self.optimizer is not None and not has_zero_optimizer_state: + if self.has_moe_layers: + largest_group_name = groups._get_max_expert_size_name() + expp_rank = groups._get_expert_parallel_rank(largest_group_name) + optim_load_path = self._get_optimizer_ckpt_name(load_dir, tag, expp_rank) + optim_checkpoint = self.checkpoint_engine.load(optim_load_path, map_location=torch.device('cpu')) + else: + optim_checkpoint = checkpoint + + if self.fp16_enabled() or self.bfloat16_enabled(): + self.optimizer.load_state_dict(optim_checkpoint['optimizer'], + load_optimizer_states=load_optimizer_states) + else: + optim_checkpoint = checkpoint + + self.optimizer.load_state_dict(optim_checkpoint['optimizer']) + + if load_lr_scheduler_states and self.lr_scheduler is not None: + self.lr_scheduler.load_state_dict(checkpoint['lr_scheduler']) + + if self.random_ltd_enabled() and self.random_ltd_scheduler is not None and 'random_ltd' in checkpoint: + self.random_ltd_scheduler.load_state_dict(checkpoint['random_ltd']) + + if self.training_dataloader is not None and self.curriculum_learning_enabled( + ) and 'data_sampler' in checkpoint: + self.training_dataloader.data_sampler.load_state_dict(checkpoint['data_sampler']) + + def get_sparse_tensor_module_names(original_set, loaded_set, original_parameters, loaded_parameters): + result = set() + + for name in original_set: + if name in loaded_parameters and name not in loaded_set: + continue # parameter existed in previous model and was not sparse + result.add(name) + + for name in loaded_set: + if name in original_parameters: + result.add(name) # parameter exists in both configs and it was sparse + + return result + + if 'sparse_tensor_module_names' in checkpoint: + sparse_tensor_module_names = checkpoint['sparse_tensor_module_names'] + elif 'csr_tensor_module_names' in checkpoint: + sparse_tensor_module_names = checkpoint['csr_tensor_module_names'] + else: + sparse_tensor_module_names = None + if sparse_tensor_module_names is not None: + if load_module_strict: + self.sparse_tensor_module_names = sparse_tensor_module_names + else: + self.sparse_tensor_module_names = get_sparse_tensor_module_names( + self.sparse_tensor_module_names, sparse_tensor_module_names, + dict(self.module.named_parameters()), checkpoint["module"]) + + self.global_steps = checkpoint['global_steps'] + self.global_samples = checkpoint.get('global_samples', self.global_steps * self.train_batch_size()) + self.skipped_steps = checkpoint['skipped_steps'] + self.loaded_checkpoint_mp_world_size = checkpoint['mp_world_size'] + deepspeed_states = [ + 'module', 'sparse_tensor_module_names', 'skipped_steps', 'global_steps', 'dp_world_size', + 'mp_world_size', 'data_sampler', 'random_ltd' + ] + client_state = {} + + if load_lr_scheduler_states: + deepspeed_states.append('lr_scheduler') + if load_optimizer_states: + deepspeed_states.append('optimizer') + + client_state = {key: value for key, value in checkpoint.items() if not key in deepspeed_states} + + if optim_checkpoint is not None: + client_state['optimizer'] = optim_checkpoint['optimizer'] + + return load_path, client_state + + def _load_zero_checkpoint(self, load_dir, tag, load_optimizer_states=True): + + load_serial = None + # When use loading checkpoint serial, checkpoint loading start from local rank 0, + # all other local rank would be paused, waiting for its rank-1 peer ready and its notification. + if self._config.zero_config.pipeline_loading_checkpoint: + assert self.zero_optimization_stage( + ) == ZeroStageEnum.weights, "Only stage3 support for pipeline checkpoint loading" + load_serial = torch.zeros(1).to(self.device) + if dist.get_local_rank() != 0: + dist.recv(tensor=load_serial, src=dist.get_rank() - 1) + if self.load_universal_checkpoint(): + zero_sd_list = None + checkpoint_folder = f'{os.path.join(load_dir, tag)}' + else: + if load_optimizer_states and self.seq_dp_world_size != self.loaded_checkpoint_dp_world_size: + raise ZeRORuntimeException("The checkpoint being loaded used a DP " \ + f"world size of {self.loaded_checkpoint_dp_world_size} but the " \ + f"current world size is {self.seq_dp_world_size}. Automatic adjustment " \ + "of ZeRO's optimizer state partitioning with a new world size is not " \ + "currently supported.") + checkpoint_folder = None + zero_sd_list = self._get_all_zero_checkpoints(load_dir, tag) + if zero_sd_list is None: + return False + + self.optimizer.load_state_dict(state_dict_list=zero_sd_list, + load_optimizer_states=load_optimizer_states, + load_from_fp32_weights=self.zero_load_from_fp32_weights(), + checkpoint_folder=checkpoint_folder, + load_serial=load_serial) + + if self.load_universal_checkpoint(): + logger.info(f'loaded universal zero checkpoints from {checkpoint_folder} for rank {self.global_rank}') + else: + logger.info(f"loading {len(zero_sd_list)} zero partition checkpoints for rank {self.global_rank}") + return True + + def update_optimizer_step(self, step): + + def set_step(d): + if isinstance(d['step'], torch.Tensor): + d['step'] = torch.tensor(step, dtype=d['step'].dtype, device=d['step'].device) + else: + d['step'] = step + + optimizer = self.optimizer + base_optimizer = optimizer.optimizer + state = base_optimizer.state + for group in optimizer.param_groups: + if 'step' in group: + set_step(group) + for p in group['params']: + if p in state and len(state[p]) > 0 and 'step' in state[p]: + set_step(state[p]) + + def _get_mp_rank_zero_checkpoint_names(self, load_dir, tag, mp_rank, dp_world_size, bf16_mode): + zero_ckpt_names = [] + for dp_rank in range(dp_world_size): + ckpt_name = self._get_rank_zero_ckpt_name(checkpoints_path=load_dir, + tag=tag, + mp_rank=mp_rank, + dp_rank=dp_rank, + bf16_mode=bf16_mode) + zero_ckpt_names.append(ckpt_name) + + return zero_ckpt_names + + def _get_all_zero_checkpoint_names(self, load_dir, tag, bf16_mode): + mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank() + zero_ckpt_names = self._get_mp_rank_zero_checkpoint_names(load_dir=load_dir, + tag=tag, + mp_rank=mp_rank, + dp_world_size=self.loaded_checkpoint_dp_world_size, + bf16_mode=bf16_mode) + for i, ckpt_name in enumerate(zero_ckpt_names): + if not os.path.exists(ckpt_name): + # transparently handle the old file pattern for optim_states + if "optim_states.pt" in ckpt_name: + ckpt_name_try = ckpt_name.replace("_optim_states.pt", "optim_states.pt") + if os.path.exists(ckpt_name_try): + zero_ckpt_names[i] = ckpt_name_try + continue + + return zero_ckpt_names + + def _get_all_zero_checkpoint_state_dicts(self, zero_ckpt_names): + zero_sd_list = [] + for i, ckpt_name in enumerate(zero_ckpt_names): + _state = None + if ckpt_name is None: + _state = {OPTIMIZER_STATE_DICT: None} + # Fully load state for current rank + elif self.zero_elastic_checkpoint() or dist.get_rank(group=self.optimizer.dp_process_group) == i: + _state = self.checkpoint_engine.load( + ckpt_name, + map_location='cpu', + ) + else: + _state = {OPTIMIZER_STATE_DICT: None} + zero_sd_list.append(_state) + + zero_optimizer_sd = [sd[OPTIMIZER_STATE_DICT] for sd in zero_sd_list] + logger.info(f"successfully read {len(zero_optimizer_sd)} ZeRO state_dicts for rank {self.global_rank}") + return zero_optimizer_sd + + def _get_all_zero_checkpoints(self, load_dir, tag): + for bf16_mode in [self.bfloat16_enabled(), not self.bfloat16_enabled()]: + zero_ckpt_names = self._get_all_zero_checkpoint_names(load_dir, tag, bf16_mode) + if zero_ckpt_names is not None: + # Warn if loading checkpoint of different bit16 type + if bf16_mode is not self.bfloat16_enabled(): + checkpoint_bit16 = BFLOAT16 if bf16_mode else FP16 + engine_bit16 = BFLOAT16 if self.bfloat16_enabled() else FP16 + logger.warn(f'Loading {checkpoint_bit16} zero checkpoints into {engine_bit16} training engine') + return self._get_all_zero_checkpoint_state_dicts(zero_ckpt_names) + + return None + + def _checkpoint_tag_validation(self, tag): + if self.checkpoint_tag_validation_enabled(): + s_hash = hashlib.sha1(tag.encode()) + bhash = torch.ByteTensor([s_hash.digest()]).flatten().to(self.device) + max_bhash = bhash.clone() + min_bhash = bhash.clone() + dist.all_reduce(max_bhash, op=dist.ReduceOp.MAX) + dist.all_reduce(min_bhash, op=dist.ReduceOp.MIN) + valid = all(min_bhash == bhash) and all(max_bhash == bhash) + msg = (f"[rank={dist.get_rank()}] The checkpoint tag name '{tag}' is not consistent across " + "all ranks. Including rank unique information in checkpoint tag could cause issues when " + "restoring with different world sizes.") + if self.checkpoint_tag_validation_fail(): + assert valid, msg + elif not valid: + logger.warning(msg) + + def save_checkpoint(self, save_dir, tag=None, client_state={}, save_latest=True, exclude_frozen_parameters=False): + """Save training checkpoint + + Arguments: + save_dir: Required. Directory for saving the checkpoint + tag: Optional. Checkpoint tag used as a unique identifier for the checkpoint, global step is + used if not provided. Tag name must be the same across all ranks. + client_state: Optional. State dictionary used for saving required training states in the client code. + save_latest: Optional. Save a file 'latest' pointing to the latest saved checkpoint. + exclude_frozen_parameters: Optional. Exclude frozen parameters from checkpointed state. + Important: all processes must call this method and not just the process with rank 0. It is + because each process needs to save its master weights and scheduler+optimizer states. This + method will hang waiting to synchronize with other processes if it's called just for the + process with rank 0. + + """ + if self._optimizer_has_ckpt_event_prologue(): + # Custom preparation for checkpoint save, if applicable + self.optimizer.checkpoint_event_prologue() + + rank = self.local_rank if self.use_node_local_storage() else self.global_rank + + # This is to make sure the checkpoint names are created without collision + # There seems to be issue creating them in parallel + + # Ensure save_dir directory exists + if rank == 0: + self.checkpoint_engine.makedirs(save_dir, exist_ok=True) + dist.barrier() + + if tag is None: + tag = f"global_step{self.global_steps}" + + # Ensure tag is a string + tag = str(tag) + self.checkpoint_engine.create(tag) + + # Ensure checkpoint tag is consistent across ranks + self._checkpoint_tag_validation(tag) + + if self.has_moe_layers: + self.save_non_zero_checkpoint = False + self._create_checkpoint_file(save_dir, tag, False) + self._save_moe_checkpoint(save_dir, + tag, + client_state=client_state, + exclude_frozen_parameters=exclude_frozen_parameters) + + # We distribute the task of saving layer checkpoint files among + # data parallel instances, so all procs should call _save_checkpoint. + # All procs then call module_state_dict(), but only procs of data + # parallel rank 0 save the general model params. + if not self.has_moe_layers: + self._create_checkpoint_file(save_dir, tag, False) + self._save_checkpoint(save_dir, + tag, + client_state=client_state, + exclude_frozen_parameters=exclude_frozen_parameters) + + if self.save_zero_checkpoint: + self._create_zero_checkpoint_files(save_dir, tag) + self._save_zero_checkpoint(save_dir, tag) + + if self._optimizer_has_ckpt_event_epilogue(): + self.optimizer.checkpoint_event_epilogue() + + # Save latest checkpoint tag + self.checkpoint_engine.commit(tag) + if save_latest and rank == 0: + with open(os.path.join(save_dir, 'latest'), 'w') as fd: + fd.write(tag) + + dist.barrier() + + return True + + def _get_non_moe_state_dict(self, full_state_dict): + """ + Get the state dict of the non-moe layers + """ + for key in list(full_state_dict.keys()): + if 'expert' in key and 'moe.gate.wg.weight' not in key: + full_state_dict.pop(key) + + return full_state_dict + + def _save_moe_checkpoint(self, save_dir, tag, client_state={}, exclude_frozen_parameters=False): + save_path = self._get_ckpt_name(save_dir, tag) + # A hack to save the checkpointing directory. Pipeline parallelism overrides + # module_state_dict() and uses this path to save the model. module_state_dict() + # then instead just returns None. + + # Using layer_#_export_# to save the model's expert state_dict + moe_layer_id = 0 + for n_module, module in self.module.named_modules(): + if isinstance(module, MoE): # and deepspeed.comm.get_rank() == 0: + group_name = module.expert_group_name + num_local_experts = module.num_local_experts + expp_rank = groups._get_expert_parallel_rank(group_name) + exp_dp_rank = groups._get_expert_data_parallel_rank(group_name) + # print(expp_rank, exp_dp_rank) + if exp_dp_rank != 0: + moe_layer_id += 1 + continue + + # get all moe parameters + moe_state_dict = {} + for n, p in module.state_dict().items(): + if 'expert' in n and 'moe.gate.wg.weight' not in n: + moe_state_dict[n_module + '.' + n] = p + moe_str_prefix = '.deepspeed_moe.experts.deepspeed_experts.' + # print(moe_state_dict.keys()) # until now, everything is fine. So the bug happens at next few lines + # Reorder the moe name rank, so that each checkpoint only has one expert + experts_state_dict = defaultdict(dict) + for key in list(moe_state_dict.keys()): + m = re.match(f".*{moe_str_prefix}([0-9]+).*", key) + + local_expert_id = None + if not m: + logger.warn(f'No expert found in key {key}.') + else: + local_expert_id = m.group(1) + + global_expert_id = expp_rank * \ + num_local_experts + int(local_expert_id) + expert_key = key.replace(f'{moe_str_prefix}{local_expert_id}', + f'{moe_str_prefix}{global_expert_id}') + # truncating extra tensor (shared) storage + truncated = moe_state_dict.pop(key).clone().detach() + experts_state_dict[str(global_expert_id)][expert_key] = truncated + + # let save the moe parameters + for global_expert_id, expert_state_dict in experts_state_dict.items(): + # save the moe parameters + moe_save_path = self._get_expert_ckpt_name(save_dir, moe_layer_id, global_expert_id, tag, self.mpu) + if self.random_ltd_enabled(): + expert_state_dict = remove_random_ltd_state_dict(expert_state_dict) + self.checkpoint_engine.save(expert_state_dict, moe_save_path) + moe_layer_id += 1 + + self._curr_ckpt_path = os.path.join(save_dir, tag) + + largest_group_name = groups._get_max_expert_size_name() + expp_rank = groups._get_expert_parallel_rank(largest_group_name) + exp_dp_rank = groups._get_expert_data_parallel_rank(largest_group_name) + + # In the case of E + D parallelism, only the + # first expert parallel group should save the expert weights + # since each expert parallel group is a copy of the model's experts + if exp_dp_rank != 0: + return + + # Save optimizer states. They are different across each exp parallel rank. + optimizer_state = { + 'optimizer': self.optimizer.state_dict() if self.optimizer and not self.zero_optimization() else None + } + # TODO: why use BufferedWriter not the path + file_path = self._get_optimizer_ckpt_name(save_dir, tag, expp_rank) + self.checkpoint_engine.save(optimizer_state, file_path) + + # get non-moe parameters + model_state_dict = self._get_non_moe_state_dict( + self.module_state_dict(exclude_frozen_parameters=exclude_frozen_parameters)) + + if expp_rank == 0: + # TODO: update num experts info,.. in checkpoint + state = { + 'module': + model_state_dict, + 'lr_scheduler': + self.lr_scheduler.state_dict() if self.lr_scheduler is not None else None, + 'data_sampler': + self.training_dataloader.data_sampler.state_dict() if + (self.training_dataloader is not None and self.curriculum_learning_enabled()) else None, + 'random_ltd': + self.random_ltd_scheduler.state_dict() if self.random_ltd_enabled() else None, + 'sparse_tensor_module_names': + self.sparse_tensor_module_names, + 'skipped_steps': + self.skipped_steps, + 'global_steps': + self.global_steps, + 'global_samples': + self.global_samples, + 'dp_world_size': + self.dp_world_size, + 'mp_world_size': + self.mp_world_size, + 'num_experts': + self.num_experts + } + state.update(client_state) + logger.info(f'Saving model checkpoint: {save_path}') + self.checkpoint_engine.save(state, save_path) + + def _create_checkpoint_file(self, save_dir, tag, zero_checkpoint): + name_function = (self._get_zero_ckpt_name if zero_checkpoint else self._get_ckpt_name) + try: + checkpoint_name = name_function(save_dir, tag) + path = os.path.dirname(checkpoint_name) + self.checkpoint_engine.makedirs(path, exist_ok=True) + except: + logger.error(f"Failed saving model checkpoint to {save_dir} with tag {tag}") + return False + + return True + + def _create_zero_checkpoint_files(self, save_dir, tag): + success = True + # zero checkpoint files are created sequentially + for rank in range(dist.get_world_size(self.optimizer.dp_process_group)): + if rank == self.global_rank: + success = self._create_checkpoint_file(save_dir, tag, True) + + dist.barrier(group=self.optimizer.dp_process_group) + + return success + + def _save_checkpoint(self, save_dir, tag, client_state={}, exclude_frozen_parameters=False): + + save_path = self._get_ckpt_name(save_dir, tag) + + zero_optimizer_state = self.zero_optimization() or self.bfloat16_enabled() + + save_frozen_param = self.zero_optimization_partition_gradients() and not exclude_frozen_parameters + + # A hack to save the checkpointing directory. Pipeline parallelism overrides + # module_state_dict() and uses this path to save the model. module_state_dict() + # then instead just returns None. The module_state_dict() implementation in + # PipelineEngine expects the save path to be set in self._curr_ckpt_path. + self._curr_ckpt_path = os.path.join(save_dir, tag) + module = self.module_state_dict(exclude_frozen_parameters=exclude_frozen_parameters) + self._curr_ckpt_path = None + + state = dict(module=module, + buffer_names=self._get_buffer_names(), + optimizer=self.optimizer.state_dict() if self.optimizer and not zero_optimizer_state else None, + param_shapes=self._get_zero_param_shapes() if self.optimizer and zero_optimizer_state else None, + frozen_param_shapes=self._get_zero_frozen_param_attributes(self._get_param_shape_func) + if save_frozen_param else None, + shared_params=self._get_shared_params() if self.optimizer and zero_optimizer_state else None, + frozen_param_fragments=self._get_zero_frozen_param_attributes(self._get_param_fragment_func) + if save_frozen_param else None, + lr_scheduler=self.lr_scheduler.state_dict() if self.lr_scheduler is not None else None, + data_sampler=self.training_dataloader.data_sampler.state_dict() if + (self.training_dataloader is not None and self.curriculum_learning_enabled()) else None, + random_ltd=self.random_ltd_scheduler.state_dict() if self.random_ltd_enabled() else None, + sparse_tensor_module_names=self.sparse_tensor_module_names, + skipped_steps=self.skipped_steps, + global_steps=self.global_steps, + global_samples=self.global_samples, + dp_world_size=self.seq_dp_world_size, + mp_world_size=self.mp_world_size, + ds_config=self.config, + ds_version=version) + state.update(client_state) + + if self.save_non_zero_checkpoint: + log_dist(message=f'Saving model checkpoint: {save_path}', ranks=[0, 1]) + self.checkpoint_engine.save(state, save_path) + + def _get_buffer_names(self): + buffer_names = [] + + # we save buffer names so that we could extract later the real buffers from the saved + # state_dict["module"] in the non-zero checkpoint - the buffers are already there but they + # are intermixed with param placeholders + + # have to traverse the tree to be able to skip non-persistent buffers + def get_layer_named_buffers(module, prefix=""): + for name, buf in module.named_buffers(recurse=False): + if buf is not None and name not in module._non_persistent_buffers_set: + buffer_names.append(prefix + name) + + for name, child in module.named_children(): + if child is not None: + get_layer_named_buffers(child, prefix + name + ".") + + get_layer_named_buffers(self.module, prefix="") + + return buffer_names + + def _get_param_shape_func(self, param): + return param.ds_shape if hasattr(param, 'ds_id') else param.shape + + def _get_param_fragment_func(self, param): + return param.ds_tensor.detach().cpu() if hasattr(param, 'ds_id') else param.detach().cpu() + + def _get_zero_frozen_param_attributes(self, attr_func): + frozen_param_fragments = OrderedDict() + + for param in self.module.parameters(): + if param.requires_grad: + continue + if param not in self.param_names: + raise ValueError(f"failed to find frozen {param} in named params") + name = self.param_names[param] + frozen_param_fragments[name] = attr_func(param) + + return frozen_param_fragments + + def _get_zero_param_shapes(self): + """Returns a dict of name to shape mapping, only for the flattened fp32 weights saved by the + optimizer. the names are exactly as in state_dict. The order is absolutely important, since + the saved data is just flattened data with no identifiers and requires reconstruction in the + same order it was saved. + We can't rely on self.module.named_parameters() to get the saved tensors, as some params + will be missing and others unsaved and then it'd be impossible to reconstruct state_dict + from the flattened weights. + optimizer.bit16_groups seems to be the easiest to use as it's in all zeroX versions. + """ + param_group_shapes = [] + cnt = 0 + numel = 0 + + # zero2 started using a round_robin_bit16_groups which is a shuffled version of bit16_groups - + # if we don't use it, we get parameters ordered incorrectly + if hasattr(self.optimizer, "round_robin_bit16_groups"): + bit16_groups = self.optimizer.round_robin_bit16_groups + elif self.bfloat16_enabled() and hasattr(self.optimizer, "bf16_groups"): + bit16_groups = self.optimizer.bf16_groups + else: + bit16_groups = self.optimizer.bit16_groups if self.zero_optimization_stage( + ) == 2 else self.optimizer.fp16_groups + + for bit16_group in bit16_groups: + param_shapes = OrderedDict() + for param in bit16_group: + cnt += 1 + numel += param.ds_numel if hasattr(param, "ds_numel") else param.numel() + shape = param.ds_shape if hasattr(param, "ds_shape") else param.shape + if param not in self.param_names: + raise ValueError(f"failed to find optimizer param in named params") + name = self.param_names[param] + param_shapes[name] = shape + + # uncomment to debug zero_to_fp32.py problems + # if self.global_rank == 0: print(f"saving param {name} {shape} (numel={shape.numel()})") + param_group_shapes.append(param_shapes) + # if self.global_rank == 0: print(f"Total saved {numel} numels in {cnt} params") + + return param_group_shapes + + def _get_shared_params(self): + """ + Returns a dict of shared params, which can later be used to reconstruct the original state dict, + e.g. in `zero_to_fp32`. Each dict entry is a pair of param names, where the key is the name + of the variable that isn't stored and the value is the actual param holding data. + """ + shared_index = {} + shared_params_by_full_name = {} + + is_zero3_model = (self.zero_optimization_partition_weights() + and any(hasattr(param, "ds_id") for param in self.module.parameters())) + + def get_layer_state_dict(module, prefix=""): + # handle params + for name, param in module.named_parameters(recurse=False): + if param is None or (is_zero3_model and not hasattr(param, "ds_id")): + continue + key = prefix + name + + # When weights are manged by stage 3, we can't rely on param.data_ptr() as it will be reused + # as weights get gathered and reduced, but param.ds_id is unique across all zero weights + # (and shared params will have the same param.ds_id) + param_id = param.ds_id if is_zero3_model else param.data_ptr() + + if param_id in shared_index: + # shared weights + #print(f"`{key}` is shared with `{shared_index[param_id]}`") + shared_params_by_full_name[key] = shared_index[param_id] + else: + shared_index[param_id] = key + + for name, child in module.named_children(): + if child is not None: + get_layer_state_dict(child, prefix + name + ".") + + if dist.get_rank() == 0: + get_layer_state_dict(self.module, prefix="") + + return shared_params_by_full_name + + def _copy_recovery_script(self, save_path): + base_dir = os.path.dirname(os.path.dirname(__file__)) + script = "zero_to_fp32.py" + src = os.path.join(base_dir, "utils", script) + dst = os.path.join(save_path, script) + #logger.info(f"creating recovery script {dst}") + copyfile(src, dst) + self._change_recovery_script_permissions(dst) + + def _change_recovery_script_permissions(self, dst): + # make executable (safeguard for file shares - Azure as example) + try: + os.chmod(dst, os.stat(dst).st_mode | stat.S_IEXEC) + except (FileNotFoundError, PermissionError) as e: + #this message is used in unit test TestZeRONonDistributed + logger.info( + f'Warning: Could not change permissions for {dst} due to error: {e}. Continuing without changing permissions.' + ) + + def _save_zero_checkpoint(self, save_path, tag): + zero_checkpoint_name = self._get_zero_ckpt_name(save_path, tag) + zero_sd = dict(optimizer_state_dict=self.optimizer.state_dict(), ds_config=self.config, ds_version=version) + self.checkpoint_engine.save(zero_sd, zero_checkpoint_name) + + if self.global_rank == 0: + self._copy_recovery_script(save_path) + ckpt_type = 'zero' if self.zero_optimization() else 'bf16_zero' + logger.info(f'{ckpt_type} checkpoint saved {zero_checkpoint_name}') + + def _zero3_consolidated_16bit_state_dict(self): + """ + Get a full non-partitioned state_dict with fp16 weights on cpu. + Important: this function must be called on all ranks and not just rank 0. + This is similar to nn.Module.state_dict (modelled after _save_to_state_dict), but: + 1. consolidates the weights from different partitions on gpu0 + 2. works on one layer at a time to require as little gpu0 memory as possible, by + moving the already consolidated weights to cpu + 3. takes care to keep the shared params shared when gradually copying the params to cpu + Returns: + a consolidated fp16 ``state_dict`` on cpu on rank 0, ``None`` on other ranks + """ + if not self.zero_optimization_partition_weights(): + raise ValueError("this function requires ZeRO-3 mode") + + state_dict = OrderedDict() if dist.get_rank() == 0 else None + shared_params = {} + + def get_layer_state_dict(module, prefix=""): + # gather one layer at a time to be memory-efficient + # must use modifier_rank=0 to release GPU memory after each layer gathered + #see_memory_usage("before GatheredParameters", force=True) + with deepspeed.zero.GatheredParameters(list(module.parameters(recurse=False)), modifier_rank=0): + if dist.get_rank() == 0: + # handle params + for name, param in module.named_parameters(recurse=False): + if param is None: + continue + key = prefix + name + # can't rely on param.data_ptr() as it will be reused as weights gets + # gathered and reduced, but param.ds_id is unique across all zero weights + # (and shared params will have the same param.ds_id) + if param.ds_id in shared_params: + # shared weights + #print(f"`{key}` is shared with `{shared_params[param.ds_id]}`") + state_dict[key] = state_dict[shared_params[param.ds_id]] + else: + state_dict[key] = param.detach().cpu() + shared_params[param.ds_id] = key + #print(f"param {param.ds_id} {param.shape} {key} ") + + # now buffers - not sure if need to take care of potentially shared weights here + for name, buf in module.named_buffers(recurse=False): + if (buf is not None and name not in module._non_persistent_buffers_set): + state_dict[prefix + name] = buf.detach().cpu() + #see_memory_usage("after GatheredParameters", force=True) + + for name, child in module.named_children(): + if child is not None: + get_layer_state_dict(child, prefix + name + ".") + + # Prepare for checkpoint save by ensuring all parameters are partitioned + if self._optimizer_has_ckpt_event_prologue(): + self.optimizer.checkpoint_event_prologue() + + see_memory_usage("before get_layer_state_dict", force=False) + get_layer_state_dict(self.module, prefix="") + see_memory_usage("after get_layer_state_dict", force=False) + + if self._optimizer_has_ckpt_event_epilogue(): + self.optimizer.checkpoint_event_epilogue() + + return state_dict + + def save_fp16_model(self, save_dir, save_filename="pytorch_model.bin"): + """has been renamed to save_16bit_model, keeping this around for backwards + compatibility""" + return self.save_16bit_model(save_dir, save_filename) + + def save_16bit_model(self, save_dir, save_filename="pytorch_model.bin"): + """ + Save 16bit model weights + + This method saves the 16bit model weights at the desired destination. + + Arguments: + save_dir: Required. Directory for saving the model + save_filename: Optional. Filename to save to. Defaults to ``pytorch_model.bin`` + + Returns: + ``True`` when a model has been saved, ``False`` otherwise. It will not be saved if + stage3_gather_16bit_weights_on_model_save is ``False``. + + Important: all processes must call this method and not just the process with rank 0. It is + because the processes need to work in sync to gather the weights. This method will hang + waiting to synchronize with other processes if it's called just for the process with rank 0. + + """ + + path = os.path.join(save_dir, save_filename) + + if self.zero_optimization_partition_weights(): + if self.zero_gather_16bit_weights_on_model_save(): + # consolidation is expensive in time and memory and therefore isn't a default + state_dict = self._zero3_consolidated_16bit_state_dict() + else: + # the model will be bogus if not consolidated so don't confuse the user by saving it + logger.info( + f"Did not save the model {path} because `stage3_gather_16bit_weights_on_model_save` is False") + return False + else: + state_dict = self.module.state_dict() + + tag = f"global_step{self.global_steps}" + tag = str(tag) + self.checkpoint_engine.create(tag) + + if dist.get_rank() == 0: + self.checkpoint_engine.makedirs(save_dir, exist_ok=True) + logger.info(f"Saving model weights to {path}, tag: {tag}") + self.checkpoint_engine.save(state_dict, path) + + self.checkpoint_engine.commit(tag) + + return True + + def empty_partition_cache(self): + """ + Release GPU memory consumed by offloaded model parameters. + """ + if hasattr(self.optimizer, 'empty_partition_cache'): + self.optimizer.empty_partition_cache() + gc.collect() + get_accelerator().empty_cache() diff --git a/opensora/adaptor/stage_1_and_2.py b/opensora/adaptor/stage_1_and_2.py index d7337268e..a0d618b6e 100644 --- a/opensora/adaptor/stage_1_and_2.py +++ b/opensora/adaptor/stage_1_and_2.py @@ -5,6 +5,7 @@ import torch import os +import pdb from deepspeed import comm as dist from packaging import version as pkg_version from collections import OrderedDict @@ -274,7 +275,6 @@ def __init__(self, # align nccl all-gather send buffers to 4-byte boundary self.nccl_start_alignment_factor = 512 # 4-byte alignment/sizeof(fp16) = 2 - print("Set nccl_start_alignment_factor = 52...") assert ( allgather_bucket_size % self.nccl_start_alignment_factor == 0 @@ -1447,26 +1447,23 @@ def allreduce_bucket(self, bucket, rank=None, log=None, divide=True, process_gro tensor_to_allreduce = tensor - # if pg_correctness_test or self.sequence_parallel_size > 1: - # communication_data_type = torch.float32 - # else: - # communication_data_type = self.communication_data_type - communication_data_type = torch.float32 + if pg_correctness_test or self.sequence_parallel_size > 1: + communication_data_type = torch.float32 + else: + communication_data_type = self.communication_data_type + if communication_data_type != tensor.dtype: tensor_to_allreduce = tensor.to(communication_data_type) - # if divide: - # tensor_to_allreduce.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size)) - - tensor_to_allreduce = tensor_to_allreduce.contiguous() - # print("call tensor_to_allreduce.contiguous() ....") + if divide: + tensor_to_allreduce.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size)) - # if rank is None: - # # "All Reducing" - # dist.all_reduce(tensor_to_allreduce, group=process_group) - # else: - # global_rank = dist.get_global_rank(process_group, rank) - # dist.reduce(tensor_to_allreduce, global_rank, group=process_group) + if rank is None: + # "All Reducing" + dist.all_reduce(tensor_to_allreduce, group=process_group) + else: + global_rank = dist.get_global_rank(process_group, rank) + dist.reduce(tensor_to_allreduce, global_rank, group=process_group) if communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce: if rank is None or rank == dist.get_rank(group=process_group): @@ -1867,6 +1864,10 @@ def step(self, closure=None): self.reset_cpu_buffers() self.timers(OPTIMIZER_ALLGATHER_TIMER).start() + + # if dist.get_rank(group=self.dp_process_group) == 0: + # pdb.set_trace() # 或者使用其他调试工具 + # Gather the updated weights from everyone. # Then all partitions of the model parameters are updated and ready for next round forward. all_gather_dp_groups(groups_flat=self.bit16_groups_flat, diff --git a/opensora/adaptor/utils.py b/opensora/adaptor/utils.py index f16c0ec4d..fda283889 100644 --- a/opensora/adaptor/utils.py +++ b/opensora/adaptor/utils.py @@ -967,8 +967,8 @@ def all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, dp_p def all_gather_dp_groups(groups_flat, partitioned_param_groups, dp_process_group, start_alignment_factor, allgather_bucket_size): - if dist.has_all_gather_into_tensor(): - return all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, dp_process_group) + # if dist.has_all_gather_into_tensor(): + # return all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, dp_process_group) for group_id, partitioned_params in enumerate(partitioned_param_groups): # Sequential AllGather Best of both worlds diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 0cd523107..e62fe0e53 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -124,8 +124,8 @@ def __getitem__(self, idx): # 打印异常堆栈 if idx in self.vid_cap_list: print(f"Caught an exception! {self.vid_cap_list[idx]}") - traceback.print_exc() - traceback.print_stack() + # traceback.print_exc() + # traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) def get_video(self, idx): diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index 9cfb3b4c8..580b5a335 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -94,6 +94,7 @@ def __init__( interpolation_scale_h: float = None, interpolation_scale_w: float = None, interpolation_scale_t: float = None, + downsampler: str = None, ): super().__init__() self.interpolation_scale_t = interpolation_scale_t diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 7817dffda..352bb6420 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -236,10 +236,9 @@ def forward(self, latent, num_frames): video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - # temp_pos_embed = temp_pos_embed.unsqueeze(2) - # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None - + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None diff --git a/opensora/npu_config.py b/opensora/npu_config.py index ac1404cb7..cbb07d259 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -16,6 +16,7 @@ npu_is_available = False from contextlib import contextmanager +import types # from opensora.acceleration.parallel_states import enable_LCCL, hccl_info, lccl_info @@ -78,19 +79,21 @@ def __init__(self): self.mm = dict() if self.on_npu: + import deepspeed + import sys torch_npu.npu.set_compile_mode(jit_compile=False) - import deepspeed.runtime.bf16_optimizer as optimizer + + import deepspeed.runtime.utils as utils + from opensora.adaptor.utils import all_gather_dp_groups, all_gather_into_tensor_dp_groups + utils.all_gather_dp_groups = all_gather_dp_groups + + import deepspeed.runtime.bf16_optimizer as bf16_optimizer from opensora.adaptor.bf16_optimizer import BF16_Optimizer - optimizer.BF16_Optimizer.__init__ = BF16_Optimizer.__init__ + self.replace_methods(bf16_optimizer.BF16_Optimizer, BF16_Optimizer) - import deepspeed.runtime.zero.stage_1_and_2 as stage_1_and_2 from opensora.adaptor.stage_1_and_2 import DeepSpeedZeroOptimizer - stage_1_and_2.DeepSpeedZeroOptimizer.__init__ = DeepSpeedZeroOptimizer.__init__ - stage_1_and_2.DeepSpeedZeroOptimizer.allreduce_bucket = DeepSpeedZeroOptimizer.allreduce_bucket - - import deepspeed.runtime.utils as utils - from opensora.adaptor.utils import all_gather_into_tensor_dp_groups - utils.all_gather_into_tensor_dp_groups = all_gather_into_tensor_dp_groups + import deepspeed.runtime.zero.stage_1_and_2 as stage_1_and_2 + self.replace_methods(stage_1_and_2.DeepSpeedZeroOptimizer, DeepSpeedZeroOptimizer, ['_has_inf_or_nan']) if "RANK" in os.environ: self.rank = int(os.environ["RANK"]) @@ -101,6 +104,15 @@ def __init__(self): self.world_size = self.N_NPU_PER_NODE self.print_with_rank(f"The npu_config.on_npu is {self.on_npu}") + def replace_methods(self, target_class, source_class, skip_fcns=[]): + for attr_name in dir(source_class): + attr_value = getattr(source_class, attr_name) + if isinstance(attr_value, (staticmethod, classmethod)) or attr_name in skip_fcns: + print(f"skip replace {attr_name}") + continue + elif isinstance(attr_value, types.FunctionType): + setattr(target_class, attr_name, attr_value) + def get_attention_mask(self, attention_mask, repeat_num): if self.on_npu and attention_mask is not None: attention_mask = attention_mask.repeat(1, repeat_num, 1) diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py new file mode 100644 index 000000000..02d64efe0 --- /dev/null +++ b/opensora/sample/sample_t2v_on_npu.py @@ -0,0 +1,251 @@ +import math +import os +import torch +import argparse +import torchvision +import torch.distributed as dist + +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler +from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel +from omegaconf import OmegaConf +from torchvision.utils import save_image +from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer + +import os, sys + +from opensora.models.ae import ae_stride_config, getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V +from opensora.models.diffusion.latte.modeling_latte import LatteT2V + +from opensora.models.text_encoder import get_text_enc +from opensora.utils.utils import save_video_grid + +from opensora.sample.pipeline_opensora import OpenSoraPipeline + +import imageio +try: + import torch_npu +except: + pass +import time +from opensora.npu_config import npu_config + +def load_t2v_checkpoint(model_path): + if args.model_3d: + transformer_model = OpenSoraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) + else: + transformer_model = LatteT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, + device_map=None, torch_dtype=weight_dtype) + print(transformer_model.config) + + # set eval mode + transformer_model.eval() + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model).to(device) + + return pipeline + +def get_latest_path(): + # Get the most recent checkpoint + dirs = os.listdir(args.model_path) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + return path +def run_model_and_save_images(pipeline, model_path): + video_grids = [] + if not isinstance(args.text_prompt, list): + args.text_prompt = [args.text_prompt] + if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): + text_prompt = open(args.text_prompt[0], 'r').readlines() + args.text_prompt = [i.strip() for i in text_prompt] + + checkpoint_name = f"{os.path.basename(model_path)}" + + for index, prompt in enumerate(args.text_prompt): + if index % npu_config.N_NPU_PER_NODE != local_rank: + continue + print('Processing the ({}) prompt'.format(prompt)) + videos = pipeline(prompt, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device, + max_sequence_length=100, + ).images + print(videos.shape) + try: + if args.num_frames == 1: + videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, + f'{args.sample_method}_{index}_{checkpoint_name}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=1, normalize=True, value_range=(0, 1)) # t c h w + + else: + imageio.mimwrite( + os.path.join( + args.save_img_path, f'{args.sample_method}_{index}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' + ), videos[0], + fps=args.fps, quality=9, codec='libx264', output_params=['-threads', '20']) # highest quality is 10, lowest is 0 + except: + print('Error when saving {}'.format(prompt)) + video_grids.append(videos) + + video_grids = torch.cat(video_grids, dim=0).cuda() + shape = list(video_grids.shape) + shape[0] *= world_size + gathered_tensor = torch.zeros(shape, dtype=video_grids.dtype, device=device) + dist.all_gather_into_tensor(gathered_tensor, video_grids.contiguous()) + video_grids = gathered_tensor.cpu() + # video_grids = video_grids.repeat(world_size, 1, 1, 1) + # output = torch.zeros(video_grids.shape, dtype=video_grids.dtype, device=device) + # dist.all_to_all_single(output, video_grids) + # video_grids = output.cpu() + def get_file_name(): + return os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') + + if args.num_frames == 1: + save_image(video_grids / 255.0, get_file_name(), + nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) + else: + video_grids = save_video_grid(video_grids) + imageio.mimwrite(get_file_name(), video_grids, fps=args.fps, quality=9) + + print('save path {}'.format(args.save_img_path)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') + parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") + parser.add_argument("--guidance_scale", type=float, default=7.5) + parser.add_argument("--sample_method", type=str, default="PNDM") + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument("--fps", type=int, default=24) + parser.add_argument("--run_time", type=int, default=0) + parser.add_argument("--text_prompt", nargs='+') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--model_3d', action='store_true') + args = parser.parse_args() + + npu_config.print_msg(args) + + # 初始化分布式环境 + local_rank = int(os.getenv('RANK', 0)) + world_size = int(os.getenv('WORLD_SIZE', 1)) + if npu_config.on_npu: + torch_npu.npu.set_device(local_rank) + dist.init_process_group(backend='hccl', init_method='env://', world_size=8, rank=local_rank) + + # torch.manual_seed(args.seed) + weight_dtype = torch.float16 + device = torch.cuda.current_device() + + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] + + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, + low_cpu_mem_usage=True, torch_dtype=weight_dtype).to(device) + tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # set eval mode + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler() + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler() + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + + + if not os.path.exists(args.save_img_path): + os.makedirs(args.save_img_path, exist_ok=True) + + if args.num_frames == 1: + video_length = 1 + ext = 'jpg' + else: + ext = 'mp4' + + latest_path = None + save_img_path = args.save_img_path + while True: + cur_path = get_latest_path() + if cur_path == latest_path: + time.sleep(80) + continue + + time.sleep(120) + latest_path = cur_path + npu_config.print_msg(f"The latest_path is {latest_path}") + full_path = f"{args.model_path}/{latest_path}/model_ema" + pipeline = load_t2v_checkpoint(full_path) + + if npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = "/home/image_data/shebin/npu_profiling_t2v" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=10000, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + run_model_and_save_images(pipeline, latest_path) + prof.step() + else: + run_model_and_save_images(pipeline, latest_path) \ No newline at end of file diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index bd02d9647..b51025264 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -18,6 +18,7 @@ import numpy as np from einops import rearrange from tqdm import tqdm + try: import torch_npu from opensora.npu_config import npu_config @@ -67,7 +68,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh validation_prompt = [ "a cat wearing sunglasses and working as a lifeguard at pool.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." - ] + ] logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) # scheduler = PNDMScheduler() @@ -81,17 +82,17 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh for prompt in validation_prompt: logger.info('Processing the ({}) prompt'.format(prompt)) video = opensora_pipeline(prompt, - num_frames=args.num_frames, - # num_frames=1, - height=args.max_height, - width=args.max_width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - enable_temporal_attentions=True, - num_images_per_prompt=1, - mask_feature=True, - max_sequence_length=50, - ).images + num_frames=args.num_frames, + # num_frames=1, + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=50, + ).images videos.append(video[0]) # import ipdb;ipdb.set_trace() gc.collect() @@ -104,7 +105,8 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh assert args.num_frames == 1 images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') np_images = np.stack([np.asarray(img) for img in images]) - tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") + tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, + dataformats="NHWC") else: np_videos = np.stack([np.asarray(vid) for vid in videos]) tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) @@ -115,18 +117,18 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') # import ipdb;ipdb.set_trace() logs = { - f"{'ema_' if ema else ''}validation": [ - wandb.Image(image, caption=f"{i}: {prompt}") - for i, (image, prompt) in enumerate(zip(images, validation_prompt)) - ] - } + f"{'ema_' if ema else ''}validation": [ + wandb.Image(image, caption=f"{i}: {prompt}") + for i, (image, prompt) in enumerate(zip(images, validation_prompt)) + ] + } else: logs = { - f"{'ema_' if ema else ''}validation": [ - wandb.Video(video, caption=f"{i}: {prompt}", fps=30) - for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) - ] - } + f"{'ema_' if ema else ''}validation": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=30) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ] + } # import ipdb;ipdb.set_trace() if hasattr(model.pos_embed, 'temp_embed_gate'): logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) @@ -136,10 +138,13 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh gc.collect() torch.cuda.empty_cache() + class ProgressInfo: def __init__(self, global_step, train_loss=0.0): self.global_step = global_step self.train_loss = train_loss + + ################################################################################# # Training Loop # ################################################################################# @@ -149,7 +154,7 @@ def main(args): if torch_npu is not None and npu_config is not None: npu_config.print_msg(args) - # npu_config.seed_everything() + npu_config.seed_everything() accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) accelerator = Accelerator( @@ -218,7 +223,6 @@ def main(args): assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." - args.stride_t = ae_stride_t * patch_size_t args.stride = ae_stride_h * patch_size_h latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) @@ -251,30 +255,36 @@ def main(args): interpolation_scale_h=args.interpolation_scale_h, interpolation_scale_w=args.interpolation_scale_w, interpolation_scale_t=args.interpolation_scale_t, - downsampler=args.downsampler, + downsampler=args.downsampler, # compress_kv_factor=args.compress_kv_factor, # use_rope=args.use_rope, # model_max_length=args.model_max_length, ) model.gradient_checkpointing = args.gradient_checkpointing - + # # use pretrained model? if args.pretrained: if 'safetensors' in args.pretrained: # pixart series from safetensors.torch import load_file as safe_load # import ipdb;ipdb.set_trace() checkpoint = safe_load(args.pretrained, device="cpu") - if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: - logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint[ + 'pos_embed.proj.weight'].ndim == 4: + logger.info( + f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") repeat = model.pos_embed.proj.weight.shape[2] - checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, + repeat, 1, + 1) / float( + repeat) # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] else: # latest stage training weight checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] model_state_dict = model.state_dict() missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') - logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + logger.info( + f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') # Freeze vae and text encoders. ae.vae.requires_grad_(False) @@ -311,7 +321,8 @@ def save_model_hook(models, weights, output_dir): def load_model_hook(models, input_dir): if args.use_ema: - load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), + Diffusion_models_class[args.model]) ema_model.load_state_dict(load_model.state_dict()) ema_model.to(accelerator.device) del load_model @@ -340,7 +351,6 @@ def load_model_hook(models, input_dir): args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes ) - params_to_optimize = model.parameters() # Optimizer creation if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): @@ -355,7 +365,7 @@ def load_model_hook(models, input_dir): f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " f"set to {args.optimizer.lower()}" ) - + if args.optimizer.lower() == "adamw": if args.use_8bit_adam: try: @@ -407,7 +417,7 @@ def load_model_hook(models, input_dir): train_dataloader = torch.utils.data.DataLoader( train_dataset, shuffle=True, - pin_memory=True, + pin_memory=True, collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, @@ -511,9 +521,9 @@ def sync_gradients_info(loss): one_step_duration = end_time - start_time accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) if torch_npu is not None and npu_config is not None: - npu_config.print_msg( - f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, train_loss={progress_info.train_loss}, time_cost={one_step_duration}", - rank=0) + npu_config.print_msg(f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, " + f"train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) progress_info.train_loss = 0.0 # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. @@ -553,7 +563,8 @@ def run(model_input, model_kwargs, prof): noise = torch.randn_like(model_input) if args.noise_offset: # https://www.crosslabs.org//blog/diffusion-with-offset-noise - noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1), device=model_input.device) + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1), + device=model_input.device) bsz = model_input.shape[0] # Sample a random timestep for each image without bias. @@ -604,7 +615,6 @@ def run(model_input, model_kwargs, prof): loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights loss = loss.mean() - # Gather the losses across all processes for logging (if we use distributed training). avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps @@ -654,6 +664,7 @@ def run(model_input, model_kwargs, prof): ema_model.restore(model.parameters()) return loss + def train_one_step(step_, data_item_, prof_=None): train_loss = 0.0 x, attn_mask, input_ids, cond_mask = data_item_ @@ -688,7 +699,7 @@ def train_one_step(step_, data_item_, prof_=None): images = rearrange(images, 'b c t h w -> (b t) c 1 h w') images = ae.encode(images) images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) - x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w with accelerator.accumulate(model): model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, @@ -719,7 +730,7 @@ def train_all_epoch(prof_=None): profiler_level=torch_npu.profiler.ProfilerLevel.Level1, aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization ) - profile_output_path = f"/home/image_data/shebin/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + profile_output_path = f"/home/image_data/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" os.makedirs(profile_output_path, exist_ok=True) with torch_npu.profiler.profile( @@ -755,8 +766,10 @@ def train_all_epoch(prof_=None): parser.add_argument("--model_max_length", type=int, default=512) parser.add_argument("--multi_scale", action="store_true") parser.add_argument('--cfg', type=float, default=0.1) - parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") - parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + parser.add_argument("--dataloader_num_workers", type=int, default=10, + help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, + help="Batch size (per device) for the training dataloader.") # text encoder & vae & diffusion model parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") @@ -776,88 +789,106 @@ def train_all_epoch(prof_=None): parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--pretrained", type=str, default=None) - parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") - + parser.add_argument("--gradient_checkpointing", action="store_true", + help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + # diffusion setting - parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--snr_gamma", type=float, default=None, + help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") parser.add_argument("--ema_start_step", type=int, default=0) parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") - parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + parser.add_argument("--prediction_type", type=str, default=None, + help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") # validation & logs parser.add_argument("--num_sampling_steps", type=int, default=50) parser.add_argument('--guidance_scale', type=float, default=2.5) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") - parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") - parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--output_dir", type=str, default=None, + help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, + help=("Max number of checkpoints to store.")) parser.add_argument("--checkpointing_steps", type=int, default=500, - help=( - "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" - " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" - " training using `--resume_from_checkpoint`." - ), - ) + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) parser.add_argument("--resume_from_checkpoint", type=str, default=None, - help=( - "Whether training should be resumed from a previous checkpoint. Use a path saved by" - ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' - ), - ) + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) parser.add_argument("--logging_dir", type=str, default="logs", - help=( - "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" - " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." - ), - ) + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) parser.add_argument("--report_to", type=str, default="tensorboard", - help=( - 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' - ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' - ), - ) + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) # optimizer & scheduler parser.add_argument("--num_train_epochs", type=int, default=100) - parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") - parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") - parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') - parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") - parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") - parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") - parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") - parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") - parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--max_train_steps", type=int, default=None, + help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, + help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", + help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, + help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, + help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, + help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", + help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, + help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, + help="The beta2 parameter for the Adam and Prodigy optimizers.") parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") - parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") - parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") - parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") - parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, + help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, + help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, + help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, + help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") parser.add_argument("--prodigy_beta3", type=float, default=None, - help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " - "uses the value of square root of beta2. Ignored if optimizer is adamW", - ) + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", + ) parser.add_argument("--lr_scheduler", type=str, default="constant", - help=( - 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' - ' "constant", "constant_with_warmup"]' - ), - ) + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) parser.add_argument("--allow_tf32", action="store_true", - help=( - "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" - " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" - ), - ) + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], - help=( - "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" - " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" - " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." - ), - ) + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") diff --git a/scripts/accelerate_configs/multi_node_deepspeed.yaml b/scripts/accelerate_configs/multi_node_deepspeed.yaml new file mode 100644 index 000000000..02bd0e6fd --- /dev/null +++ b/scripts/accelerate_configs/multi_node_deepspeed.yaml @@ -0,0 +1,16 @@ +compute_environment: LOCAL_MACHINE +distributed_type: DEEPSPEED +deepspeed_config: + deepspeed_config_file: scripts/accelerate_configs/zero2_npu.json + deepspeed_multinode_launcher: standard +fsdp_config: {} +main_process_port: 29501 +main_training_function: main +num_machines: 2 +num_processes: 16 +rdzv_backend: static +same_network: true +tpu_env: [] +tpu_use_cluster: false +tpu_use_sudo: false +use_cpu: false \ No newline at end of file diff --git a/scripts/accelerate_configs/zero2_npu.json b/scripts/accelerate_configs/zero2_npu.json new file mode 100644 index 000000000..3bdccd6d5 --- /dev/null +++ b/scripts/accelerate_configs/zero2_npu.json @@ -0,0 +1,27 @@ +{ + "fp16": { + "enabled": false, + "loss_scale": 0, + "loss_scale_window": 1000, + "initial_scale_power": 16, + "hysteresis": 2, + "min_loss_scale": 1 + }, + "bf16": { + "enabled": "auto" + }, + "communication_data_type": "fp32", + "gradient_clipping": 1.0, + "train_micro_batch_size_per_gpu": "auto", + "train_batch_size": "auto", + "gradient_accumulation_steps": "auto", + "zero_optimization": { + "stage": 2, + "overlap_comm": true, + "allgather_partitions": true, + "allgather_bucket_size": 67108864, + "reduce_scatter": true, + "contiguous_gradients": true, + "reduce_bucket_size": 67108864 + } +} \ No newline at end of file diff --git a/scripts/text_condition/train_ds_image_256x256.sh b/scripts/text_condition/train_ds_image_256x256.sh index 3bf3c9384..5579783c1 100644 --- a/scripts/text_condition/train_ds_image_256x256.sh +++ b/scripts/text_condition/train_ds_image_256x256.sh @@ -13,7 +13,7 @@ export OMP_NUM_THREADS=1 accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example1.yaml \ - opensora/train/train_t2v.py \ + opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-B/111 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ diff --git a/scripts/text_condition/train_image_256x256_on_npu.sh b/scripts/text_condition/train_image_256x256_on_npu.sh index 07ab8ce1f..16d7660cf 100644 --- a/scripts/text_condition/train_image_256x256_on_npu.sh +++ b/scripts/text_condition/train_image_256x256_on_npu.sh @@ -4,11 +4,11 @@ env export WANDB_MODE='offline' accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ + --config_file scripts/accelerate_configs/multi_node_deepspeed.yaml \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-B/122 \ + --model OpenSoraT2V-L/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ @@ -34,7 +34,7 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=500 \ + --checkpointing_steps=1000 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ diff --git a/scripts/text_condition/train_video21d_65x240p_on_npu.sh b/scripts/text_condition/train_video21d_65x240p_on_npu.sh index ded5e0728..c5fbca834 100644 --- a/scripts/text_condition/train_video21d_65x240p_on_npu.sh +++ b/scripts/text_condition/train_video21d_65x240p_on_npu.sh @@ -35,7 +35,7 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=250 \ + --checkpointing_steps=1000 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ From 477e03359fda211af7fc3d8045b267c155df99d4 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Wed, 12 Jun 2024 19:04:51 +0800 Subject: [PATCH 030/134] update udit --- .gitignore | 4 +- examples/convert_udit_to_videoudit.py | 22 + examples/rec_image.py | 43 +- examples/{rec_imvi_vae.py => rec_video.py} | 13 +- examples/rec_video_vae.py | 275 ---------- opensora/dataset/__init__.py | 2 + opensora/dataset/t2v_datasets.py | 8 +- opensora/models/ae/videobase/__init__.py | 3 + .../models/ae/videobase/modeling_videobase.py | 2 +- .../models/ae/videobase/modules/__init__.py | 2 +- opensora/models/ae/videobase/modules/conv.py | 16 +- .../ae/videobase/modules/resnet_block.py | 42 +- .../models/diffusion/latte/modeling_latte.py | 48 +- .../diffusion/opensora/modeling_opensora.py | 8 +- opensora/models/diffusion/opensora/modules.py | 34 +- .../models/diffusion/udit/modeling_udit.py | 264 ++++++---- opensora/models/diffusion/udit/modules.py | 470 +++++++++++++----- opensora/models/text_encoder/__init__.py | 4 +- scripts/causalvae/rec_image.sh | 8 + scripts/causalvae/rec_video.sh | 11 + scripts/causalvae/reconstruction.sh | 11 - scripts/text_condition/sample_image.sh | 18 + .../text_condition/train_ds_image_256x256.sh | 18 +- ...65x240p.sh => train_ds_video3d_61x240p.sh} | 0 .../text_condition/train_imageudit_480p.sh | 54 ++ ...d_65x240p.sh => train_video21d_61x240p.sh} | 0 ...pu.sh => train_video21d_61x240p_on_npu.sh} | 0 ...3d_65x240p.sh => train_video3d_61x240p.sh} | 0 ...npu.sh => train_video3d_61x240p_on_npu.sh} | 0 .../train_videoudit_125x480p.sh | 55 ++ 30 files changed, 845 insertions(+), 590 deletions(-) create mode 100644 examples/convert_udit_to_videoudit.py rename examples/{rec_imvi_vae.py => rec_video.py} (90%) delete mode 100644 examples/rec_video_vae.py create mode 100644 scripts/causalvae/rec_image.sh create mode 100644 scripts/causalvae/rec_video.sh delete mode 100644 scripts/causalvae/reconstruction.sh create mode 100644 scripts/text_condition/sample_image.sh rename scripts/text_condition/{train_ds_video3d_65x240p.sh => train_ds_video3d_61x240p.sh} (100%) create mode 100644 scripts/text_condition/train_imageudit_480p.sh rename scripts/text_condition/{train_video21d_65x240p.sh => train_video21d_61x240p.sh} (100%) rename scripts/text_condition/{train_video21d_65x240p_on_npu.sh => train_video21d_61x240p_on_npu.sh} (100%) rename scripts/text_condition/{train_video3d_65x240p.sh => train_video3d_61x240p.sh} (100%) rename scripts/text_condition/{train_video3d_65x240p_on_npu.sh => train_video3d_61x240p_on_npu.sh} (100%) create mode 100644 scripts/text_condition/train_videoudit_125x480p.sh diff --git a/.gitignore b/.gitignore index 3b6cea2e8..62a0ebb0b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,11 +31,11 @@ private* caption* .deepspeed_env 256* -sample* +sample_images/ taming* *test* sft* flash* 65x256* alpha_vae -new_bs32* \ No newline at end of file +*bs32* \ No newline at end of file diff --git a/examples/convert_udit_to_videoudit.py b/examples/convert_udit_to_videoudit.py new file mode 100644 index 000000000..4f2b1893a --- /dev/null +++ b/examples/convert_udit_to_videoudit.py @@ -0,0 +1,22 @@ +import torch + +from safetensors.torch import load_file as safe_load + +path = "diffusion_pytorch_model.safetensors" +ckpt = safe_load(path, device="cpu") +new_ckpt = {} +k_size = 3 +for k, v in ckpt.items(): + if 'pos_embed.proj.weight' in k: + new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + elif 'attn1.downsampler.layer.weight' in k: + new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + elif 'body.0.weight' in k and 'down' in k: + in_c = v.shape[0] + new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 + elif 'body.0.weight' in k and 'up' in k: + new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 + else: + new_v = v + new_ckpt[k] = new_v +torch.save(new_ckpt, 'convert_weight.pt') \ No newline at end of file diff --git a/examples/rec_image.py b/examples/rec_image.py index a8e95cb30..32d606f08 100644 --- a/examples/rec_image.py +++ b/examples/rec_image.py @@ -2,17 +2,17 @@ sys.path.append(".") from PIL import Image import torch -from torchvision.transforms import ToTensor, Compose, Resize, Normalize +from torchvision.transforms import ToTensor, Compose, Resize, Normalize, Lambda from torch.nn import functional as F -from opensora.models.ae.videobase import CausalVAEModel import argparse import numpy as np +from opensora.models.ae import getae_wrapper def preprocess(video_data: torch.Tensor, short_size: int = 128) -> torch.Tensor: transform = Compose( [ ToTensor(), - Normalize((0.5), (0.5)), + Lambda(lambda x: 2. * x - 1.), Resize(size=short_size), ] ) @@ -22,19 +22,26 @@ def preprocess(video_data: torch.Tensor, short_size: int = 128) -> torch.Tensor: def main(args: argparse.Namespace): image_path = args.image_path - resolution = args.resolution + short_size = args.short_size device = args.device + kwarg = {} - vqvae = CausalVAEModel.load_from_checkpoint(args.ckpt) - vqvae.eval() - vqvae = vqvae.to(device) + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir='cache_dir', **kwarg).to(device) + vae = getae_wrapper(args.ae)(args.ae_path, **kwarg).to(device) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.eval() + vae = vae.to(device) + vae = vae.half() with torch.no_grad(): - x_vae = preprocess(Image.open(image_path), resolution) - x_vae = x_vae.to(device) - latents = vqvae.encode(x_vae) - recon = vqvae.decode(latents.sample()) - x = recon[0, :, 0, :, :] + x_vae = preprocess(Image.open(image_path), short_size) + x_vae = x_vae.to(device, dtype=torch.float16) # b c t h w + latents = vae.encode(x_vae) + latents = latents.to(torch.float16) + image_recon = vae.decode(latents) # b t c h w + x = image_recon[0, 0, :, :, :] x = x.squeeze() x = x.detach().cpu().numpy() x = np.clip(x, -1, 1) @@ -47,11 +54,15 @@ def main(args: argparse.Namespace): if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('--image-path', type=str, default='') - parser.add_argument('--rec-path', type=str, default='') - parser.add_argument('--ckpt', type=str, default='') - parser.add_argument('--resolution', type=int, default=336) + parser.add_argument('--image_path', type=str, default='') + parser.add_argument('--rec_path', type=str, default='') + parser.add_argument('--ae', type=str, default='') + parser.add_argument('--ae_path', type=str, default='') + parser.add_argument('--model_path', type=str, default='results/pretrained') + parser.add_argument('--short_size', type=int, default=336) parser.add_argument('--device', type=str, default='cuda') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') args = parser.parse_args() main(args) diff --git a/examples/rec_imvi_vae.py b/examples/rec_video.py similarity index 90% rename from examples/rec_imvi_vae.py rename to examples/rec_video.py index 2fd83ee8a..82b113634 100644 --- a/examples/rec_imvi_vae.py +++ b/examples/rec_video.py @@ -101,18 +101,7 @@ def main(args: argparse.Namespace): latents = latents.to(torch.float16) video_recon = vae.decode(latents) # b t c h w - if video_recon.shape[1] == 1: - x = video_recon[0, 0, :, :, :] - x = x.squeeze() - x = x.detach().cpu().numpy() - x = np.clip(x, -1, 1) - x = (x + 1) / 2 - x = (255 * x).astype(np.uint8) - x = x.transpose(1, 2, 0) - image = Image.fromarray(x) - image.save(args.rec_path.replace('mp4', 'jpg')) - else: - custom_to_video(video_recon[0], fps=args.fps, output_file=args.rec_path) + custom_to_video(video_recon[0], fps=args.fps, output_file=args.rec_path) if __name__ == '__main__': diff --git a/examples/rec_video_vae.py b/examples/rec_video_vae.py deleted file mode 100644 index 65266ed53..000000000 --- a/examples/rec_video_vae.py +++ /dev/null @@ -1,275 +0,0 @@ -import random -import argparse -import cv2 -from tqdm import tqdm -import numpy as np -import numpy.typing as npt -import torch -from decord import VideoReader, cpu -from torch.nn import functional as F -from pytorchvideo.transforms import ShortSideScale -from torchvision.transforms import Lambda, Compose -from torchvision.transforms._transforms_video import CenterCropVideo -import sys -from torch.utils.data import Dataset, DataLoader, Subset -import os - -sys.path.append(".") -from opensora.models.ae.videobase import CausalVAEModel -import torch.nn as nn - - -def array_to_video( - image_array: npt.NDArray, fps: float = 30.0, output_file: str = "output_video.mp4" -) -> None: - height, width, channels = image_array[0].shape - fourcc = cv2.VideoWriter_fourcc(*"mp4v") - video_writer = cv2.VideoWriter(output_file, fourcc, float(fps), (width, height)) - - for image in image_array: - image_rgb = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) - video_writer.write(image_rgb) - - video_writer.release() - - -def custom_to_video( - x: torch.Tensor, fps: float = 2.0, output_file: str = "output_video.mp4" -) -> None: - x = x.detach().cpu() - x = torch.clamp(x, -1, 1) - x = (x + 1) / 2 - x = x.permute(1, 2, 3, 0).float().numpy() - x = (255 * x).astype(np.uint8) - array_to_video(x, fps=fps, output_file=output_file) - return - - -def read_video(video_path: str, num_frames: int, sample_rate: int) -> torch.Tensor: - decord_vr = VideoReader(video_path, ctx=cpu(0), num_threads=8) - total_frames = len(decord_vr) - sample_frames_len = sample_rate * num_frames - - if total_frames > sample_frames_len: - s = 0 - e = s + sample_frames_len - num_frames = num_frames - else: - s = 0 - e = total_frames - num_frames = int(total_frames / sample_frames_len * num_frames) - print( - f"sample_frames_len {sample_frames_len}, only can sample {num_frames * sample_rate}", - video_path, - total_frames, - ) - - frame_id_list = np.linspace(s, e - 1, num_frames, dtype=int) - video_data = decord_vr.get_batch(frame_id_list).asnumpy() - video_data = torch.from_numpy(video_data) - video_data = video_data.permute(3, 0, 1, 2) # (T, H, W, C) -> (C, T, H, W) - return video_data - - -class RealVideoDataset(Dataset): - def __init__( - self, - real_video_dir, - num_frames, - sample_rate=1, - crop_size=None, - resolution=128, - ) -> None: - super().__init__() - self.real_video_files = self._combine_without_prefix(real_video_dir) - self.num_frames = num_frames - self.sample_rate = sample_rate - self.crop_size = crop_size - self.short_size = resolution - - def __len__(self): - return len(self.real_video_files) - - def __getitem__(self, index): - if index >= len(self): - raise IndexError - real_video_file = self.real_video_files[index] - real_video_tensor = self._load_video(real_video_file) - video_name = os.path.basename(real_video_file) - return {'video': real_video_tensor, 'file_name': video_name } - - def _load_video(self, video_path): - num_frames = self.num_frames - sample_rate = self.sample_rate - decord_vr = VideoReader(video_path, ctx=cpu(0)) - total_frames = len(decord_vr) - sample_frames_len = sample_rate * num_frames - - if total_frames > sample_frames_len: - s = 0 - e = s + sample_frames_len - num_frames = num_frames - else: - s = 0 - e = total_frames - num_frames = int(total_frames / sample_frames_len * num_frames) - print( - f"sample_frames_len {sample_frames_len}, only can sample {num_frames * sample_rate}", - video_path, - total_frames, - ) - - frame_id_list = np.linspace(s, e - 1, num_frames, dtype=int) - video_data = decord_vr.get_batch(frame_id_list).asnumpy() - video_data = torch.from_numpy(video_data) - video_data = video_data.permute(3, 0, 1, 2) - return _preprocess( - video_data, short_size=self.short_size, crop_size=self.crop_size - ) - - def _combine_without_prefix(self, folder_path, prefix="."): - folder = [] - for name in os.listdir(folder_path): - if name[0] == prefix: - continue - folder.append(os.path.join(folder_path, name)) - folder.sort() - return folder - -def resize(x, resolution): - height, width = x.shape[-2:] - aspect_ratio = width / height - if width <= height: - new_width = resolution - new_height = int(resolution / aspect_ratio) - else: - new_height = resolution - new_width = int(resolution * aspect_ratio) - resized_x = F.interpolate(x, size=(new_height, new_width), mode='bilinear', align_corners=True, antialias=True) - return resized_x - -def _preprocess(video_data, short_size=128, crop_size=None): - transform = Compose( - [ - Lambda(lambda x: ((x / 255.0) * 2 - 1)), - Lambda(lambda x: resize(x, short_size)), - ( - CenterCropVideo(crop_size=crop_size) - if crop_size is not None - else Lambda(lambda x: x) - ), - ] - ) - video_outputs = transform(video_data) - video_outputs = _format_video_shape(video_outputs) - return video_outputs - - -def _format_video_shape(video, time_compress=4, spatial_compress=8): - time = video.shape[1] - height = video.shape[2] - width = video.shape[3] - new_time = ( - (time - (time - 1) % time_compress) - if (time - 1) % time_compress != 0 - else time - ) - new_height = ( - (height - (height) % spatial_compress) - if height % spatial_compress != 0 - else height - ) - new_width = ( - (width - (width) % spatial_compress) if width % spatial_compress != 0 else width - ) - return video[:, :new_time, :new_height, :new_width] - - -@torch.no_grad() -def main(args: argparse.Namespace): - real_video_dir = args.real_video_dir - generated_video_dir = args.generated_video_dir - ckpt = args.ckpt - sample_rate = args.sample_rate - resolution = args.resolution - crop_size = args.crop_size - num_frames = args.num_frames - sample_rate = args.sample_rate - device = args.device - sample_fps = args.sample_fps - batch_size = args.batch_size - num_workers = args.num_workers - subset_size = args.subset_size - - if not os.path.exists(args.generated_video_dir): - os.makedirs(args.generated_video_dir, exist_ok=True) - - data_type = torch.bfloat16 - - # ---- Load Model ---- - device = args.device - vqvae = CausalVAEModel.from_pretrained(args.ckpt) - vqvae = vqvae.to(device).to(data_type) - if args.enable_tiling: - vqvae.enable_tiling() - vqvae.tile_overlap_factor = args.tile_overlap_factor - # ---- Load Model ---- - - # ---- Prepare Dataset ---- - dataset = RealVideoDataset( - real_video_dir=real_video_dir, - num_frames=num_frames, - sample_rate=sample_rate, - crop_size=crop_size, - resolution=resolution, - ) - - if subset_size: - indices = range(subset_size) - dataset = Subset(dataset, indices=indices) - - dataloader = DataLoader( - dataset, batch_size=batch_size, pin_memory=True, num_workers=num_workers - ) - # ---- Prepare Dataset - - # ---- Inference ---- - for batch in tqdm(dataloader): - x, file_names = batch['video'], batch['file_name'] - x = x.to(device=device, dtype=data_type) # b c t h w - latents = vqvae.encode(x).sample().to(data_type) - video_recon = vqvae.decode(latents) - for idx, video in enumerate(video_recon): - output_path = os.path.join(generated_video_dir, file_names[idx]) - if args.output_origin: - os.makedirs(os.path.join(generated_video_dir, "origin/"), exist_ok=True) - origin_output_path = os.path.join(generated_video_dir, "origin/", file_names[idx]) - custom_to_video( - x[idx], fps=sample_fps / sample_rate, output_file=origin_output_path - ) - custom_to_video( - video, fps=sample_fps / sample_rate, output_file=output_path - ) - # ---- Inference ---- - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--real_video_dir", type=str, default="") - parser.add_argument("--generated_video_dir", type=str, default="") - parser.add_argument("--ckpt", type=str, default="") - parser.add_argument("--sample_fps", type=int, default=30) - parser.add_argument("--resolution", type=int, default=336) - parser.add_argument("--crop_size", type=int, default=None) - parser.add_argument("--num_frames", type=int, default=17) - parser.add_argument("--sample_rate", type=int, default=1) - parser.add_argument("--batch_size", type=int, default=1) - parser.add_argument("--num_workers", type=int, default=8) - parser.add_argument("--subset_size", type=int, default=None) - parser.add_argument("--tile_overlap_factor", type=float, default=0.25) - parser.add_argument('--enable_tiling', action='store_true') - parser.add_argument('--output_origin', action='store_true') - parser.add_argument("--device", type=str, default="cuda") - - args = parser.parse_args() - main(args) - diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index b3430fd09..e6763bc9e 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -9,6 +9,7 @@ ae_norm = { + 'CausalVAEModel_D8_4x8x8': Lambda(lambda x: 2. * x - 1.), 'CausalVAEModel_2x8x8': Lambda(lambda x: 2. * x - 1.), 'CausalVAEModel_4x8x8': Lambda(lambda x: 2. * x - 1.), 'CausalVQVAEModel_4x4x4': Lambda(lambda x: x - 0.5), @@ -27,6 +28,7 @@ } ae_denorm = { + 'CausalVAEModel_D8_4x8x8': lambda x: (x + 1.) / 2., 'CausalVAEModel_2x8x8': lambda x: (x + 1.) / 2., 'CausalVAEModel_4x8x8': lambda x: (x + 1.) / 2., 'CausalVQVAEModel_4x4x4': lambda x: x + 0.5, diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 0cd523107..4c3367895 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -135,10 +135,10 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = self.vid_cap_list[idx]['path'] - assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" - frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None - video = self.decord_read(video_path, frame_idx) + # video_path = self.vid_cap_list[idx]['path'] + # assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" + # frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None + # video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] assert h / w <= 16 / 16 and h / w >= 4 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 4/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' diff --git a/opensora/models/ae/videobase/__init__.py b/opensora/models/ae/videobase/__init__.py index 68bc77aa4..fd85a5d4a 100644 --- a/opensora/models/ae/videobase/__init__.py +++ b/opensora/models/ae/videobase/__init__.py @@ -17,6 +17,7 @@ videobase_ae_stride = { 'CausalVAEModel_2x8x8': [2, 8, 8], 'CausalVAEModel_4x8x8': [4, 8, 8], + 'CausalVAEModel_D8_4x8x8': [4, 8, 8], 'CausalVQVAEModel_4x4x4': [4, 4, 4], 'CausalVQVAEModel_4x8x8': [4, 8, 8], 'VQVAEModel_4x4x4': [4, 4, 4], @@ -31,6 +32,7 @@ videobase_ae_channel = { 'CausalVAEModel_2x8x8': 4, 'CausalVAEModel_4x8x8': 4, + 'CausalVAEModel_D8_4x8x8': 8, 'CausalVQVAEModel_4x4x4': 4, 'CausalVQVAEModel_4x8x8': 4, 'VQVAEModel_4x4x4': 4, @@ -45,6 +47,7 @@ videobase_ae = { 'CausalVAEModel_2x8x8': CausalVAEModelWrapper, 'CausalVAEModel_4x8x8': CausalVAEModelWrapper, + 'CausalVAEModel_D8_4x8x8': CausalVAEModelWrapper, 'CausalVQVAEModel_4x4x4': CausalVQVAEModelWrapper, 'CausalVQVAEModel_4x8x8': CausalVQVAEModelWrapper, 'VQVAEModel_4x4x4': VQVAEModelWrapper, diff --git a/opensora/models/ae/videobase/modeling_videobase.py b/opensora/models/ae/videobase/modeling_videobase.py index 0b2274ec5..f4f397f5d 100644 --- a/opensora/models/ae/videobase/modeling_videobase.py +++ b/opensora/models/ae/videobase/modeling_videobase.py @@ -77,4 +77,4 @@ def from_pretrained(cls, pretrained_model_name_or_path: Optional[Union[str, os.P model.init_from_ckpt(last_ckpt_file) return model else: - return super().from_pretrained(pretrained_model_name_or_path, **kwargs) \ No newline at end of file + return super().from_pretrained(pretrained_model_name_or_path, low_cpu_mem_usage=False, device_map=None, **kwargs) \ No newline at end of file diff --git a/opensora/models/ae/videobase/modules/__init__.py b/opensora/models/ae/videobase/modules/__init__.py index 61ca11dec..49052f8ed 100644 --- a/opensora/models/ae/videobase/modules/__init__.py +++ b/opensora/models/ae/videobase/modules/__init__.py @@ -9,7 +9,7 @@ ) from .conv import CausalConv3d, Conv2d from .normalize import GroupNorm, Normalize -from .resnet_block import ResnetBlock2D, ResnetBlock3D +from .resnet_block import ResnetBlock2D, ResnetBlock3D, ResnetBlock3D_GC from .updownsample import ( SpatialDownsample2x, SpatialUpsample2x, diff --git a/opensora/models/ae/videobase/modules/conv.py b/opensora/models/ae/videobase/modules/conv.py index 7f786c318..1dbc4a9e5 100644 --- a/opensora/models/ae/videobase/modules/conv.py +++ b/opensora/models/ae/videobase/modules/conv.py @@ -6,6 +6,7 @@ from .ops import cast_tuple from einops import rearrange from .ops import video_to_image +from torch.utils.checkpoint import checkpoint try: import torch_npu from opensora.npu_config import npu_config @@ -112,4 +113,17 @@ def forward(self, x): (1, 1, self.time_kernel_size - 1, 1, 1) ) # b c t h w x = torch.concatenate((first_frame_pad, x), dim=2) # 3 + 16 - return self.conv(x) \ No newline at end of file + return self.conv(x) + + + +class CausalConv3d_GC(CausalConv3d): + def __init__(self, chan_in, chan_out, kernel_size: Union[int, Tuple[int]], init_method="random", **kwargs): + super().__init__(chan_in, chan_out, kernel_size, init_method, **kwargs) + def forward(self, x): + # 1 + 16 16 as video, 1 as image + first_frame_pad = x[:, :, :1, :, :].repeat( + (1, 1, self.time_kernel_size - 1, 1, 1) + ) # b c t h w + x = torch.concatenate((first_frame_pad, x), dim=2) # 3 + 16 + return checkpoint(self.conv, x) \ No newline at end of file diff --git a/opensora/models/ae/videobase/modules/resnet_block.py b/opensora/models/ae/videobase/modules/resnet_block.py index 1553832dc..5d3bf67ad 100644 --- a/opensora/models/ae/videobase/modules/resnet_block.py +++ b/opensora/models/ae/videobase/modules/resnet_block.py @@ -3,8 +3,9 @@ from einops import rearrange, pack, unpack from .normalize import Normalize from .ops import nonlinearity, video_to_image -from .conv import CausalConv3d +from .conv import CausalConv3d, CausalConv3d_GC from .block import Block +from torch.utils.checkpoint import checkpoint try: import torch_npu from opensora.npu_config import npu_config @@ -91,6 +92,45 @@ def forward(self, x): h = nonlinearity(h) h = self.dropout(h) h = self.conv2(h) + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + x = self.conv_shortcut(x) + else: + x = self.nin_shortcut(x) + return x + h + + + +class ResnetBlock3D_GC(Block): + def __init__(self, *, in_channels, out_channels=None, conv_shortcut=False, dropout): + super().__init__() + self.in_channels = in_channels + self.out_channels = in_channels if out_channels is None else out_channels + self.use_conv_shortcut = conv_shortcut + + self.norm1 = Normalize(in_channels) + self.conv1 = CausalConv3d(in_channels, out_channels, 3, padding=1) + self.norm2 = Normalize(out_channels) + self.dropout = torch.nn.Dropout(dropout) + self.conv2 = CausalConv3d(out_channels, out_channels, 3, padding=1) + if self.in_channels != self.out_channels: + if self.use_conv_shortcut: + self.conv_shortcut = CausalConv3d(in_channels, out_channels, 3, padding=1) + else: + self.nin_shortcut = CausalConv3d(in_channels, out_channels, 1, padding=0) + + def forward(self, x): + return checkpoint(self._forward, x, use_reentrant=True) + + def _forward(self, x): + h = x + h = self.norm1(h) + h = nonlinearity(h) + h = self.conv1(h) + h = self.norm2(h) + h = nonlinearity(h) + h = self.dropout(h) + h = self.conv2(h) if self.in_channels != self.out_channels: if self.use_conv_shortcut: x = self.conv_shortcut(x) diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index 9cfb3b4c8..cce8aa778 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -638,14 +638,12 @@ def LatteT2V_L_122(**kwargs): Latte_models = { "LatteT2V-S/122": LatteT2V_S_122, # "LatteT2V-D64-XL/122": LatteT2V_D64_XL_122, - # "LatteT2V/122": LatteT2V_122, "LatteT2V-L/122": LatteT2V_L_122, } Latte_models_class = { "LatteT2V-S/122": LatteT2V, # "LatteT2V-D64-XL/122": LatteT2V_D64_XL_122, - "LatteT2V/122": LatteT2V, "LatteT2V-L/122": LatteT2V, } @@ -679,7 +677,7 @@ def LatteT2V_L_122(**kwargs): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:6') - model = LatteT2V_L_122( + model = LatteT2V_S_122( in_channels=ae_channel_config[args.ae], out_channels=ae_channel_config[args.ae] * 2, # caption_channels=4096, @@ -705,30 +703,26 @@ def LatteT2V_L_122(**kwargs): ).to(device) try: - path = "/storage/ongoing/Open-Sora-Plan/testimg_/checkpoint-100/model/diffusion_pytorch_model.safetensors" - from safetensors.torch import load_file as safe_load - ckpt = safe_load(path, device="cpu") - # import ipdb;ipdb.set_trace() - new_ckpt = {} - for k, v in ckpt.items(): - if 'transformer_blocks' in k: - split = k.split('.') - idx = int(split[1]) - if idx % 2 == 0: - split[1] = str(idx//2) - else: - split[0] = 'temporal_transformer_blocks' - split[1] = str((idx-1)//2) - new_k = '.'.join(split) - else: - new_k = k - new_ckpt[new_k] = v - - # import ipdb;ipdb.set_trace() - # if ckpt['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and ckpt['pos_embed.proj.weight'].ndim == 4: - # repeat = model.pos_embed.proj.weight.shape[2] - # ckpt['pos_embed.proj.weight'] = ckpt['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) - # del ckpt['proj_out.weight'], ckpt['proj_out.bias'] + path = "/storage/t2v.pt" + # from safetensors.torch import load_file as safe_load + # ckpt = safe_load(path, device="cpu") + # new_ckpt = {} + # for k, v in ckpt.items(): + # if 'transformer_blocks' in k: + # split = k.split('.') + # idx = int(split[1]) + # if idx % 2 == 0: + # split[1] = str(idx//2) + # else: + # split[0] = 'temporal_transformer_blocks' + # split[1] = str((idx-1)//2) + # new_k = '.'.join(split) + # else: + # new_k = k + # new_ckpt[new_k] = v + + new_ckpt = torch.load(path)['model'] + import ipdb;ipdb.set_trace() msg = model.load_state_dict(new_ckpt, strict=False) print(msg) except Exception as e: diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 76d43f3df..a4279e30f 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -667,13 +667,13 @@ def OpenSoraT2V_B_222(**kwargs): 'model_max_length': 300, 'max_height': 512, 'max_width': 512, - 'num_frames': 61, + 'num_frames': 1, 'use_image_num': 0, 'compress_kv_factor': 1 } ) b = 2 - c = 4 + c = 8 cond_c = 4096 num_timesteps = 1000 ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] @@ -684,7 +684,7 @@ def OpenSoraT2V_B_222(**kwargs): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:0') - model = OpenSoraT2V_B_222(in_channels=4, + model = OpenSoraT2V_B_122(in_channels=c, out_channels=8, sample_size=latent_size, sample_size_t=num_frames, @@ -700,7 +700,7 @@ def OpenSoraT2V_B_222(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k333_s222').to(device) + downsampler='k33_s11').to(device) try: path = "PixArt-Alpha-XL-2-512.safetensors" diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 7817dffda..72581690a 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -860,7 +860,7 @@ def __init__( ff_bias: bool = True, attention_out_bias: bool = True, attention_mode: str = "xformers", - downsampler: bool = False, + downsampler: str = None, ): super().__init__() self.only_cross_attention = only_cross_attention @@ -974,7 +974,7 @@ def __init__( elif norm_type == "layer_norm_i2vgen": self.norm3 = None - # if downsampler: + if downsampler: downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 # if len(downsampler_ker_size) == 3: # self.ff = FeedForward_Conv3d( @@ -984,21 +984,21 @@ def __init__( # bias=ff_bias, # ) # elif len(downsampler_ker_size) == 2: - # self.ff = FeedForward_Conv2d( - # downsampler, - # dim, - # 4 * dim, - # bias=ff_bias, - # ) - # else: - self.ff = FeedForward( - dim, - dropout=dropout, - activation_fn=activation_fn, - final_dropout=final_dropout, - inner_dim=ff_inner_dim, - bias=ff_bias, - ) + self.ff = FeedForward_Conv2d( + downsampler, + dim, + 4 * dim, + bias=ff_bias, + ) + else: + self.ff = FeedForward( + dim, + dropout=dropout, + activation_fn=activation_fn, + final_dropout=final_dropout, + inner_dim=ff_inner_dim, + bias=ff_bias, + ) # 4. Fuser if attention_type == "gated" or attention_type == "gated-text-image": diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py index 6c7f5342c..eca8d74bc 100644 --- a/opensora/models/diffusion/udit/modeling_udit.py +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -11,10 +11,10 @@ from diffusers.models.modeling_utils import ModelMixin from diffusers.models.normalization import AdaLayerNormSingle from diffusers.models.embeddings import PixArtAlphaTextProjection -from opensora.models.diffusion.udit.modules import Upsample3d, Downsample3d, OverlapPatchEmbed3D, BasicTransformerBlock +from opensora.models.diffusion.udit.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock from opensora.utils.utils import to_2tuple import math - +import re try: import torch_npu from opensora.npu_config import npu_config @@ -69,7 +69,7 @@ def __init__( patch_size: Optional[int] = None, patch_size_t: Optional[int] = None, num_vector_embeds: Optional[int] = None, - down_factor: Optional[int] = 2, + mlp_ratio: int = 4, depth: Optional[list] = [2, 5, 8, 5, 2], activation_fn: str = "geglu", num_embeds_ada_norm: Optional[int] = None, @@ -86,12 +86,13 @@ def __init__( interpolation_scale_w: float = None, interpolation_scale_t: float = None, use_additional_conditions: Optional[bool] = None, - mlp_ratio: float = 2.0, attention_mode: str = 'xformers', + downsampler: str = 'k333_s222' ): super().__init__() # Set some common variables used across the board. + self.downsampler = downsampler self.use_linear_projection = use_linear_projection self.interpolation_scale_t = interpolation_scale_t self.interpolation_scale_h = interpolation_scale_h @@ -146,28 +147,58 @@ def _init_patched_inputs(self, norm_type): self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 30, self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 40, ) - self.pos_embed = OverlapPatchEmbed3D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - ) - - down_factor = down_factor if isinstance(self.config.down_factor, list) else [self.config.down_factor] * 5 + # down_factor = list(re.search(r's(\d{2,3})', self.downsampler).group(1)) + # down_factor = [int(i) for i in down_factor] + # down_factor = down_factor if isinstance(self.config.down_factor, list) else [self.config.down_factor] * 5 + # down_factor = [2] * len(self.config.depth) + + if self.config.downsampler is not None and len(self.config.downsampler) == 9: + is_video_model = True + self.pos_embed = OverlapPatchEmbed3D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + elif self.config.downsampler is not None and len(self.config.downsampler) == 7: + is_video_model = False + self.pos_embed = OverlapPatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, + (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, + (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] + + for i in range((len(self.config.depth)-1)//2): + t = layer_thw[i][0] // 2 if layer_thw[i][0] != 1 else 1 + h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention + w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 + layer_thw.append([t, h, w]) + self.encoder_level_1 = nn.ModuleList( [ BasicTransformerBlock( self.inner_dim, self.config.num_attention_heads, self.config.attention_head_dim, - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - down_factor=down_factor[0], + num_frames=layer_thw[0][0], + height=layer_thw[0][1], + width=layer_thw[0][2], + downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, cross_attention_dim=self.inner_dim, @@ -186,18 +217,18 @@ def _init_patched_inputs(self, norm_type): for _ in range(self.config.depth[0]) ] ) - self.down1_2 = Downsample3d(self.inner_dim) - + self.down1_2 = Downsample3d(self.inner_dim) if is_video_model else Downsample2d(self.inner_dim) + self.encoder_level_2 = nn.ModuleList( [ BasicTransformerBlock( self.inner_dim * 2, self.config.num_attention_heads, self.config.attention_head_dim * 2, - num_frames=self.config.sample_size_t // 2, - height=math.ceil(self.config.sample_size[0] / 2), - width=math.ceil(self.config.sample_size[1] / 2), - down_factor=down_factor[1], + num_frames=layer_thw[1][0], + height=layer_thw[1][1], + width=layer_thw[1][2], + downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, cross_attention_dim=self.inner_dim * 2, @@ -216,7 +247,7 @@ def _init_patched_inputs(self, norm_type): for _ in range(self.config.depth[1]) ] ) - self.down2_3 = Downsample3d(self.inner_dim * 2) + self.down2_3 = Downsample3d(self.inner_dim * 2) if is_video_model else Downsample2d(self.inner_dim * 2) self.latent = nn.ModuleList( [ @@ -224,10 +255,10 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 4, self.config.num_attention_heads, self.config.attention_head_dim * 4, - num_frames=self.config.sample_size_t // 4, - height=math.ceil(math.ceil(self.config.sample_size[0] / 2) / 2), - width=math.ceil(math.ceil(self.config.sample_size[1] / 2) / 2), - down_factor=down_factor[2], + num_frames=layer_thw[2][0], + height=layer_thw[2][1], + width=layer_thw[2][2], + downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, cross_attention_dim=self.inner_dim * 4, @@ -247,7 +278,7 @@ def _init_patched_inputs(self, norm_type): ] ) - self.up3_2 = Upsample3d(int(self.inner_dim * 4)) ## From Level 4 to Level 3 + self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) self.decoder_level_2 = nn.ModuleList( [ @@ -255,10 +286,10 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 2, self.config.num_attention_heads, self.config.attention_head_dim * 2, - num_frames=self.config.sample_size_t // 2, - height=math.ceil(self.config.sample_size[0] / 2), - width=math.ceil(self.config.sample_size[1] / 2), - down_factor=down_factor[3], + num_frames=layer_thw[1][0], + height=layer_thw[1][1], + width=layer_thw[1][2], + downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, cross_attention_dim=self.inner_dim * 2, @@ -278,7 +309,7 @@ def _init_patched_inputs(self, norm_type): ] ) - self.up2_1 = Upsample3d(int(self.inner_dim * 2)) ## From Level 4 to Level 3 + self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 2), bias=True) self.decoder_level_1 = nn.ModuleList( [ @@ -286,10 +317,10 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 2, self.config.num_attention_heads, self.config.attention_head_dim * 2, - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - down_factor=down_factor[4], + num_frames=layer_thw[0][0], + height=layer_thw[0][1], + width=layer_thw[0][2], + downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, cross_attention_dim=self.inner_dim * 2, @@ -313,13 +344,13 @@ def _init_patched_inputs(self, norm_type): self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) self.proj_out_1 = nn.Linear(2 * self.inner_dim, 2 * self.inner_dim) self.proj_out_2 = nn.Linear( - 2 * self.inner_dim, self.out_channels + 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels ) elif self.config.norm_type == "ada_norm_single": self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) self.scale_shift_table = nn.Parameter(torch.randn(2, 2 * self.inner_dim) / (2 * self.inner_dim)**0.5) self.proj_out = nn.Linear( - 2 * self.inner_dim, self.out_channels + 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels ) # PixArt-Alpha blocks. @@ -405,7 +436,6 @@ def forward( `tuple` where the first element is the sample tensor. """ batch_size, c, frame, height, width = hidden_states.shape - assert frame % 2 == 0 and use_image_num == 0 frame = frame - use_image_num # 21-4=17 if cross_attention_kwargs is not None: if cross_attention_kwargs.get("scale", None) is not None: @@ -428,8 +458,14 @@ def forward( # (keep = +0, discard = -10000.0) # b, frame+use_image_num, h, w -> a video with images # b, 1, h, w -> only images + pad_h_0, pad_w_0 = height % (self.config.patch_size * 2), width % (self.config.patch_size * 2) + + hidden_states = F.pad(hidden_states, (0, pad_w_0, 0, pad_h_0, 0, 0), mode='reflect') attention_mask = attention_mask.to(self.dtype) attention_mask = attention_mask.unsqueeze(1) # b 1 t h w + attention_mask = F.pad(attention_mask, (0, pad_w_0, 0, pad_h_0, 0, 0)) + attention_mask = F.max_pool3d(attention_mask, kernel_size=(self.config.patch_size_t, self.config.patch_size, self.config.patch_size), + stride=(self.config.patch_size_t, self.config.patch_size, self.config.patch_size)) attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') attention_bias = (1 - attention_mask.bool().to(self.dtype)) * -10000.0 @@ -444,6 +480,7 @@ def forward( attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-1]) + # 1. Input added_cond_kwargs = {"resolution": None, "aspect_ratio": None} hidden_states, encoder_hidden_states_1, encoder_hidden_states_2, encoder_hidden_states_3, \ @@ -451,9 +488,8 @@ def forward( embedded_timestep_1, embedded_timestep_2, embedded_timestep_3 = self._operate_on_patched_inputs( hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num ) - - # encoder_1 - out_enc_level1 = hidden_states + frame, height, width = frame // self.config.patch_size_t, \ + (height + pad_h_0) // (self.config.patch_size), (width + pad_w_0) // (self.config.patch_size) def create_custom_forward(module, return_dict=None): @@ -465,6 +501,9 @@ def custom_forward(*inputs): return custom_forward + # encoder_1 + out_enc_level1 = hidden_states + # import ipdb;ipdb.set_trace() if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -491,10 +530,10 @@ def custom_forward(*inputs): cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, ) - pad_h_1, pad_w_1 = height % 2, width % 2 + pad_h_1, pad_w_1 = height % 4, width % 4 inp_enc_level2, attention_bias, attention_mask = self.down1_2(out_enc_level1, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) - frame, height, width = frame // 2, (height + pad_h_1) // 2, (width + pad_w_1) // 2 + frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_1) // 2, (width + pad_w_1) // 2 # encoder_2 out_enc_level2 = inp_enc_level2 @@ -530,10 +569,11 @@ def custom_forward(*inputs): cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, ) - pad_h_2, pad_w_2 = height % 2, width % 2 + pad_h_2, pad_w_2 = height % 4, width % 4 + # import ipdb;ipdb.set_trace() inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) - frame, height, width = frame // 2, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 if npu_config is not None and attention_mask is not None: attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) @@ -571,8 +611,9 @@ def custom_forward(*inputs): # decoder_2 + # import ipdb;ipdb.set_trace() inp_dec_level2, attention_bias, attention_mask = self.up3_2(latent, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) - frame, height, width = frame * 2, height * 2 - pad_h_2, width * 2 - pad_w_2 + frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_2, width * 2 - pad_w_2 inp_dec_level2 = torch.cat([inp_dec_level2, out_enc_level2], 2) inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) out_dec_level2 = inp_dec_level2 @@ -581,7 +622,6 @@ def custom_forward(*inputs): attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - print(out_dec_level2.size(), attention_bias.size(), encoder_hidden_states_2.size(), encoder_attention_mask.size()) if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -612,8 +652,9 @@ def custom_forward(*inputs): # decoder_1 + # import ipdb;ipdb.set_trace() inp_dec_level1, attention_bias, attention_mask = self.up2_1(out_dec_level2, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) - frame, height, width = frame * 2, height * 2 - pad_h_1, width * 2 - pad_w_1 + frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_1, width * 2 - pad_w_1 inp_dec_level1 = torch.cat([inp_dec_level1, out_enc_level1], 2) inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) out_dec_level1 = inp_dec_level1 @@ -650,6 +691,7 @@ def custom_forward(*inputs): class_labels=class_labels, ) + # import ipdb;ipdb.set_trace() # 3. Output output = self._get_output_for_patched_inputs( hidden_states=out_dec_level1, @@ -660,7 +702,9 @@ def custom_forward(*inputs): height=height, width=width, ) # b c t h w - + + frame, height, width = frame * self.config.patch_size_t, height * self.config.patch_size - pad_h_0, width * self.config.patch_size - pad_w_0 + output = output[:, :, :frame, :height, :width] if not return_dict: return (output,) @@ -669,7 +713,7 @@ def custom_forward(*inputs): def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): # batch_size = hidden_states.shape[0] - hidden_states = self.pos_embed(hidden_states.to(self.dtype)) + hidden_states = self.pos_embed(hidden_states.to(self.dtype), frame) if self.use_additional_conditions and added_cond_kwargs is None: raise ValueError( @@ -716,46 +760,75 @@ def _get_output_for_patched_inputs( hidden_states = self.proj_out(hidden_states) hidden_states = hidden_states.squeeze(1) - # unpatchify + # # unpatchify + # hidden_states = hidden_states.reshape( + # shape=(-1, num_frames, height, width, self.out_channels) + # ) + # output = torch.einsum("nthwc->ncthw", hidden_states) + # return output + # unpatchify hidden_states = hidden_states.reshape( - shape=(-1, num_frames, height, width, self.out_channels) + shape=(-1, num_frames, height, width, self.config.patch_size_t, self.config.patch_size, self.config.patch_size, self.config.out_channels) + ) + hidden_states = torch.einsum("nthwopqc->nctohpwq", hidden_states) + output = hidden_states.reshape( + shape=(-1, self.config.out_channels, num_frames * self.config.patch_size_t, height * self.config.patch_size, width * self.config.patch_size) ) - output = torch.einsum("nthwc->ncthw", hidden_states) + # import ipdb;ipdb.set_trace() + # if output.shape[2] % 2 == 0: + # output = output[:, :, 1:] return output - def UDiTT2V_S_111(**kwargs): - return UDiTT2V(down_factor=2, depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, - patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_B_111(**kwargs): - return UDiTT2V(down_factor=2, depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, - patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_111(**kwargs): - return UDiTT2V(down_factor=2, depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=16, - patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_L_211(**kwargs): + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_L_122(**kwargs): + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_L_222(**kwargs): + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_XL_111(**kwargs): - return UDiTT2V(down_factor=2, depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=16, - patch_size_t=1, patch_size=1, mlp_ratio=4, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + return UDiTT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_XXL_111(**kwargs): - return UDiTT2V(down_factor=2, depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=24, - patch_size_t=1, patch_size=1, mlp_ratio=4, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + return UDiTT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) UDiT_models = { - "UDiTT2V-S/111": UDiTT2V_S_111, # 0.33B - "UDiTT2V-B/111": UDiTT2V_B_111, # 1.3B - "UDiTT2V-L/111": UDiTT2V_L_111, # 2B - "UDiTT2V-XL/111": UDiTT2V_XL_111, # 3.3B - "UDiTT2V-XXL/111": UDiTT2V_XXL_111, # 7.3B + "UDiTT2V-S/111": UDiTT2V_S_111, # 0.19B 0.3B if video + "UDiTT2V-B/111": UDiTT2V_B_111, # 0.73B 1.2B if video + "UDiTT2V-L/111": UDiTT2V_L_111, # 2.3B 3.4B if video + "UDiTT2V-L/211": UDiTT2V_L_211, # 2.3B 3.4B if video + "UDiTT2V-L/122": UDiTT2V_L_122, # 2.3B 3.4B if video + "UDiTT2V-L/222": UDiTT2V_L_222, # 2.3B 3.4B if video + "UDiTT2V-XL/111": UDiTT2V_XL_111, # 5.1B 7B if video + "UDiTT2V-XXL/111": UDiTT2V_XXL_111, # 9.4B 11.3B if video } UDiT_models_class = { "UDiTT2V-S/111": UDiTT2V, "UDiTT2V-B/111": UDiTT2V, "UDiTT2V-L/111": UDiTT2V, + "UDiTT2V-L/211": UDiTT2V, + "UDiTT2V-L/122": UDiTT2V, + "UDiTT2V-L/222": UDiTT2V, "UDiTT2V-XL/111": UDiTT2V, "UDiTT2V-XXL/111": UDiTT2V, } @@ -772,8 +845,9 @@ def UDiTT2V_XXL_111(**kwargs): 'attention_mode': 'xformers', 'use_rope': False, 'model_max_length': 300, - 'max_image_size': 512, - 'num_frames': 61, + 'max_height': 480, + 'max_width': 640, + 'num_frames': 125, 'use_image_num': 0, 'compress_kv_factor': 1 } @@ -783,15 +857,15 @@ def UDiTT2V_XXL_111(**kwargs): cond_c = 4096 num_timesteps = 1000 ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - latent_size = (args.max_image_size // ae_stride_h, args.max_image_size // ae_stride_w) + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: num_frames = (args.num_frames - 1) // ae_stride_t + 1 else: num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:1') - model = UDiTT2V_XXL_111(in_channels=4, - out_channels=8, + model = UDiTT2V_L_122(in_channels=c, + out_channels=c, sample_size=latent_size, sample_size_t=num_frames, activation_fn="gelu-approximate", @@ -805,14 +879,38 @@ def UDiTT2V_XXL_111(**kwargs): only_cross_attention=False, upcast_attention=False, use_linear_projection=False, - use_additional_conditions=False).to(device) + use_additional_conditions=False, + downsampler='k333_s222').to(device) print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') - sys.exit() - x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w).to(device) + try: + path = "/storage/ongoing/new/Open-Sora-Plan/bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22/checkpoint-50/model_ema/diffusion_pytorch_model.safetensors" + from safetensors.torch import load_file as safe_load + ckpt = safe_load(path, device="cpu") + new_ckpt = {} + k_size = 3 + for k, v in ckpt.items(): + if 'pos_embed.proj.weight' in k: + new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + elif 'attn1.downsampler.layer.weight' in k: + new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + elif 'body.0.weight' in k and 'down' in k: + in_c = v.shape[0] + new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 + elif 'body.0.weight' in k and 'up' in k: + new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 + else: + new_v = v + new_ckpt[k] = new_v + msg = model.load_state_dict(new_ckpt, strict=False) + print(msg) + except Exception as e: + print(e) + # import sys;sys.exit() + x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) - attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_image_size//ae_stride_h, args.max_image_size//ae_stride_w)).to(device) # B L or B 1+num_images L + attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L timestep = torch.randint(0, 1000, (b,), device=device) model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py index 91bc78efc..36f6a43d6 100644 --- a/opensora/models/diffusion/udit/modules.py +++ b/opensora/models/diffusion/udit/modules.py @@ -18,6 +18,7 @@ from diffusers.models.attention_processor import Attention as Attention_ from diffusers.models.embeddings import SinusoidalPositionalEmbedding from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm +import re try: import torch_npu from opensora.npu_config import npu_config, set_run_dtype @@ -133,6 +134,8 @@ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): return emb + + class OverlapPatchEmbed3D(nn.Module): """2D Image to Patch Embedding but with 3D position embedding""" @@ -141,6 +144,8 @@ def __init__( num_frames=1, height=224, width=224, + patch_size_t=1, + patch_size=16, in_channels=3, embed_dim=768, layer_norm=False, @@ -150,47 +155,164 @@ def __init__( interpolation_scale_t=1, ): super().__init__() - # assert num_frames == 1 + # assert patch_size_t == 1 and patch_size == 1 self.flatten = flatten self.layer_norm = layer_norm - - self.proj = nn.Conv3d(in_channels, embed_dim, kernel_size=3, stride=1, padding=1, bias=bias) + self.proj = nn.Conv3d( + in_channels, embed_dim, kernel_size=3, padding=1, stride=(patch_size_t, patch_size, patch_size), bias=bias + ) if layer_norm: self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) else: self.norm = None + self.patch_size_t = patch_size_t + self.patch_size = patch_size # See: # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 - - self.height, self.width = height, width - self.base_size = (height, width) + self.height, self.width = height // patch_size, width // patch_size + self.base_size = (height // patch_size, width // patch_size) self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) pos_embed = get_2d_sincos_pos_embed( embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale ) self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) - self.num_frames = num_frames - self.base_size_t = num_frames + self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t self.interpolation_scale_t = interpolation_scale_t - temp_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) - self.register_buffer("temp_embed", torch.from_numpy(temp_embed).float().unsqueeze(0), persistent=False) + temp_pos_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) + # self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) + + def forward(self, latent, num_frames): + b, _, _, _, _ = latent.shape + video_latent, image_latent = None, None + # b c 1 h w + # assert latent.shape[-3] == 1 and num_frames == 1 + num_frames = latent.shape[-3] // self.patch_size_t + height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size + # latent = rearrange(latent, 'b c t h w -> (b t) c h w') + latent = self.proj(latent) - def forward(self, latent): - b, _, num_frames, _, _ = latent.shape - height, width = latent.shape[-2], latent.shape[-1] - if npu_config is None: - latent = self.proj(latent) # b c t h w + if self.flatten: + # latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C + latent = rearrange(latent, 'b c t h w -> (b t) (h w) c ') + if self.layer_norm: + latent = self.norm(latent) + + # import ipdb;ipdb.set_trace() + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed + + + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed + + latent = (latent + pos_embed).to(latent.dtype) + + latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + video_latent = latent + + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) + + video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') + return video_latent + + + +class OverlapPatchEmbed2D(nn.Module): + """2D Image to Patch Embedding but with 3D position embedding""" + + def __init__( + self, + num_frames=1, + height=224, + width=224, + patch_size_t=1, + patch_size=16, + in_channels=3, + embed_dim=768, + layer_norm=False, + flatten=True, + bias=True, + interpolation_scale=(1, 1), + interpolation_scale_t=1, + ): + super().__init__() + assert patch_size_t == 1 + self.flatten = flatten + self.layer_norm = layer_norm + + self.proj = nn.Conv2d( + in_channels, embed_dim, kernel_size=3, padding=1, stride=(patch_size, patch_size), bias=bias + ) + if layer_norm: + self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) else: - latent_dtype = latent.dtype - latent = npu_config.run_conv3d(self.proj, latent, latent_dtype) + self.norm = None + + self.patch_size_t = patch_size_t + self.patch_size = patch_size + # See: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 + + self.height, self.width = height // patch_size, width // patch_size + self.base_size = (height // patch_size, width // patch_size) + self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + pos_embed = get_2d_sincos_pos_embed( + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) + + self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.interpolation_scale_t = interpolation_scale_t + temp_pos_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) + # self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) + + def forward(self, latent, num_frames): + b, _, _, _, _ = latent.shape + video_latent, image_latent = None, None + # b c 1 h w + # assert latent.shape[-3] == 1 and num_frames == 1 + num_frames = latent.shape[-3] // self.patch_size_t + height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size + latent = rearrange(latent, 'b c t h w -> (b t) c h w') + latent = self.proj(latent) if self.flatten: - latent = rearrange(latent, 'b c t h w -> (b t) (h w) c') # B C T H W -> BT N C + latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C if self.layer_norm: latent = self.norm(latent) + # import ipdb;ipdb.set_trace() # Interpolate positional embeddings if needed. # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) if self.height != height or self.width != width: @@ -208,37 +330,56 @@ def forward(self, latent): if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() # raise NotImplementedError - temp_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_embed.shape[-1], + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], grid_size=num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t, ) - temp_embed = torch.from_numpy(temp_embed) - temp_embed = temp_embed.float().unsqueeze(0).to(latent.device) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) else: - temp_embed = self.temp_embed + temp_pos_embed = self.temp_pos_embed latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + image_latent = latent - temp_embed = temp_embed.unsqueeze(2) - latent = (latent + temp_embed).to(latent.dtype) - latent = rearrange(latent, 'b t n c -> b (t n) c') - return latent - + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + # temp_pos_embed = temp_pos_embed.unsqueeze(2) + # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None + + return image_latent class Attention(Attention_): - def __init__(self, downsampler=False, dim=768, num_frames=8, height=16, width=16, - down_factor=2, down_shortcut=True, attention_mode='xformers', **kwags): - processor = AttnProcessor2_0(downsampler=False, dim=768, num_frames=8, height=16, width=16, - down_factor=2, down_shortcut=True, attention_mode='xformers') + def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attention_mode='xformers', **kwags): + processor = AttnProcessor2_0(attention_mode='xformers') super().__init__(processor=processor, **kwags) + self.downsampler = None + if downsampler: # downsampler k155_s122 + downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 + down_factor = list(re.search(r's(\d{2,3})', downsampler).group(1)) + downsampler_ker_size = [int(i) for i in downsampler_ker_size] + downsampler_padding = [(i - 1) // 2 for i in downsampler_ker_size] + down_factor = [int(i) for i in down_factor] + + if len(downsampler_ker_size) == 2: + self.downsampler = DownSampler2d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + down_shortcut=True, num_frames=num_frames, height=height, width=width) + elif len(downsampler_ker_size) == 3: + self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + down_shortcut=True, num_frames=num_frames, height=height, width=width) + -class DownSampler(nn.Module): +class DownSampler3d(nn.Module): def __init__(self, *args, **kwargs): ''' Required kwargs: down_factor, downsampler''' super().__init__() @@ -249,35 +390,77 @@ def __init__(self, *args, **kwargs): self.w = kwargs.pop('width') self.layer = nn.Conv3d(*args, **kwargs) - def forward(self, x): + def forward(self, x, attention_mask): + b = x.shape[0] x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) if npu_config is None: x = self.layer(x) + (x if self.down_shortcut else 0) else: x_dtype = x.dtype x = npu_config.run_conv3d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) - return rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) d (t h w)', dt=self.down_factor, dh=self.down_factor, dw=self.down_factor) + x = rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) (t h w) d', + t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=self.t, h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (t dt) (h dh) (w dw) -> (b dt dh dw) 1 (t h w)', + t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + return x, attention_mask + def reverse(self, x): + # import ipdb;ipdb.set_trace() + x = rearrange(x, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', + t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + return x + + +class DownSampler2d(nn.Module): + def __init__(self, *args, **kwargs): + ''' Required kwargs: down_factor, downsampler''' + super().__init__() + self.down_factor = kwargs.pop('down_factor') + self.down_shortcut = kwargs.pop('down_shortcut') + self.t = kwargs.pop('num_frames') + self.h = kwargs.pop('height') + self.w = kwargs.pop('width') + self.layer = nn.Conv2d(*args, **kwargs) + + def forward(self, x, attention_mask): + # import ipdb;ipdb.set_trace() + b = x.shape[0] + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) + if npu_config is None: + x = self.layer(x) + (x if self.down_shortcut else 0) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', + h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (h dh) (w dw) -> (b dh dw) 1 (h w)', + h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + return x, attention_mask + def reverse(self, x): + # import ipdb;ipdb.set_trace() + x = rearrange(x, '(b t dh dw) (h w) d -> b (t h dh w dw) d', + t=self.t, h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + return x + class AttnProcessor2_0: r""" Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). """ - def __init__(self, downsampler=False, dim=768, num_frames=8, height=16, width=16, - down_factor=2, down_shortcut=True, attention_mode='xformers'): + def __init__(self, attention_mode='xformers'): self.attention_mode = attention_mode - self.downsampler = None - if downsampler: # downsampler - self.t = num_frames - self.h = height - self.w = width - downsampler_ker_size = 5 - downsampler_padding = (5 - 1) // 2 - self.downsampler = DownSampler(dim, dim, kernel_size=downsampler_ker_size, stride=1, - padding=downsampler_padding, groups=dim, down_factor=down_factor, - down_shortcut=down_shortcut, num_frames=num_frames, height=height, width=width) - if not hasattr(F, "scaled_dot_product_attention"): raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") @@ -295,8 +478,8 @@ def __call__( deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." deprecate("scale", "1.0.0", deprecation_message) - if self.downsampler is not None: - hidden_states = self.downsampler(hidden_states) + if attn.downsampler is not None: + hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask) residual = hidden_states if attn.spatial_norm is not None: @@ -393,38 +576,48 @@ def __call__( hidden_states = hidden_states / attn.rescale_output_factor - if self.downsampler is not None: - dt = self.downsampler.down_factor - dh = self.downsampler.down_factor - dw = self.downsampler.down_factor - hidden_states = rearrange(hidden_states, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', - t=self.t//dt, h=self.h//dh, w=self.w//dw, dt=dt, dh=dh, dw=dw) + if attn.downsampler is not None: + hidden_states = attn.downsampler.reverse(hidden_states) return hidden_states -class PixelUnshuffle3d(nn.Module): - def __init__(self, ratio): +class PixelUnshuffle(nn.Module): + def __init__(self, ratio, ratio_t=None): super().__init__() self.r = ratio + self.r_t = ratio_t if ratio_t else ratio def forward(self, x): - b, c, t, h, w = x.shape - assert t % self.r == 0 and h % self.r == 0 and w % self.r == 0 - if self.r > 1: - x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r, r2=self.r, r3=self.r) + if self.r_t is not None and self.r_t != 1: + b, c, t, h, w = x.shape + assert t % self.r_t == 0 and h % self.r == 0 and w % self.r == 0 + if self.r > 1: + x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r_t, r2=self.r, r3=self.r) + else: + b, c, h, w = x.shape + assert h % self.r == 0 and w % self.r == 0 + if self.r > 1: + x = rearrange(x, 'b c (h r2) (w r3) -> b (c r2 r3) h w', r2=self.r, r3=self.r) return x -class PixelShuffle3d(nn.Module): - def __init__(self, ratio): +class PixelShuffle(nn.Module): + def __init__(self, ratio, ratio_t=None): super().__init__() self.r = ratio + self.r_t = ratio_t if ratio_t else ratio def forward(self, x): - b, c, t, h, w = x.shape - assert c % (self.r*self.r*self.r) == 0 - if self.r > 1: - x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r, r2=self.r, r3=self.r) + if self.r_t is not None and self.r_t != 1: + b, c, t, h, w = x.shape + assert c % (self.r_t*self.r*self.r) == 0 + if self.r > 1: + x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r_t, r2=self.r, r3=self.r) + else: + b, c, h, w = x.shape + assert c % (self.r*self.r) == 0 + if self.r > 1: + x = rearrange(x, 'b (c r2 r3) h w -> b c (h r2) (w r3)', r2=self.r, r3=self.r) return x class Downsample3d(nn.Module): @@ -432,7 +625,7 @@ def __init__(self, n_feat): super(Downsample3d, self).__init__() self.body = nn.Sequential(nn.Conv3d(n_feat, n_feat // 4, kernel_size=3, stride=1, padding=1, bias=False), - PixelUnshuffle3d(2)) + PixelUnshuffle(2, 2)) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) @@ -457,7 +650,7 @@ def __init__(self, n_feat): super(Upsample3d, self).__init__() self.body = nn.Sequential(nn.Conv3d(n_feat, n_feat * 4, kernel_size=3, stride=1, padding=1, bias=False), - PixelShuffle3d(2)) + PixelShuffle(2, 2)) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) @@ -478,69 +671,102 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): return x, attention_bias, attention_mask +class Downsample2d(nn.Module): + def __init__(self, n_feat): + super(Downsample2d, self).__init__() + + self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), + PixelUnshuffle(2, 1)) + + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.body, x, x_dtype) + x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) + attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) + attention_mask = F.max_pool2d(attention_mask, kernel_size=2, stride=2) + attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + return x, attention_bias, attention_mask + +class Upsample2d(nn.Module): + def __init__(self, n_feat): + super(Upsample2d, self).__init__() + + self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), + PixelShuffle(2, 1)) -class FeedForward(nn.Module): - def __init__(self, dim, ffn_expansion_factor, bias=True, rep=False, - num_frames=8, height=16, width=16): - super(FeedForward, self).__init__() + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.body, x, x_dtype) + x = x[:, :, :height*2-pad_h, :width*2-pad_w] + x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) + attention_mask = attention_mask.repeat_interleave(2, -1).repeat_interleave(2, -2) + attention_mask = attention_mask[:, :, :height*2-pad_h, :width*2-pad_w] + attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + return x, attention_bias, attention_mask + + +class FeedForward_Conv2d(nn.Module): + def __init__(self, downsampler, dim, hidden_features, bias=True, num_frames=8, height=16, width=16): + super(FeedForward_Conv2d, self).__init__() + self.t = num_frames self.h = height self.w = width - - self.rep = rep self.bias = bias - self.hidden_features = hidden_features = int(dim * ffn_expansion_factor) + self.project_in = nn.Linear(dim, hidden_features, bias=bias) - if rep: - self.project_in = nn.Sequential(nn.Conv3d(dim, hidden_features // 2, kernel_size=1, bias=bias), - nn.Conv3d(hidden_features // 2, hidden_features, kernel_size=1, bias=bias)) + self.dwconv = nn.ModuleList([ + nn.Conv2d(hidden_features, hidden_features, kernel_size=(5, 5), stride=1, padding=(2, 2), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv2d(hidden_features, hidden_features, kernel_size=(3, 3), stride=1, padding=(1, 1), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv2d(hidden_features, hidden_features, kernel_size=(1, 1), stride=1, padding=(0, 0), dilation=1, + groups=hidden_features, bias=bias) + ]) - self.dwconv = nn.ModuleList([ - nn.Conv3d(hidden_features, hidden_features, kernel_size=5, stride=1, padding=2, dilation=1, - groups=hidden_features, bias=bias), - nn.Conv3d(hidden_features, hidden_features, kernel_size=3, stride=1, padding=1, dilation=1, - groups=hidden_features, bias=bias), - nn.Conv3d(hidden_features, hidden_features, kernel_size=1, stride=1, padding=0, dilation=1, - groups=hidden_features, bias=bias) - ]) + self.project_out = nn.Linear(hidden_features, dim, bias=bias) - self.project_out = nn.Sequential(nn.Conv3d(hidden_features, hidden_features // 2, kernel_size=1, bias=bias), - nn.Conv3d(hidden_features // 2, dim, kernel_size=1, bias=bias)) - - else: - self.project_in = nn.Conv3d(dim, hidden_features, kernel_size=1, bias=bias) - self.dwconv = nn.Conv3d(hidden_features, hidden_features, kernel_size=5, stride=1, padding=2, - groups=hidden_features, bias=bias) - self.project_out = nn.Conv3d(hidden_features, dim, kernel_size=1, bias=bias) def forward(self, x): - x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) + # import ipdb;ipdb.set_trace() if npu_config is None: x = self.project_in(x) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) x = F.gelu(x) - if self.rep: - out = x - for module in self.dwconv: - out = out + module(x) - else: - out = self.dwconv(x) + out = x + for module in self.dwconv: + out = out + module(x) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) x = self.project_out(out) else: x_dtype = x.dtype - x = npu_config.run_conv3d(self.project_in, x, torch.float16) + x = npu_config.run_conv2d(self.project_in, x, torch.float16) x = F.gelu(x) - if self.rep: - out = x - for module in self.dwconv: - out = out + npu_config.run_conv3d(module, x, torch.float16) - else: - out = npu_config.run_conv3d(self.dwconv, x, torch.float16) - x = npu_config.run_conv3d(self.project_out, out, x_dtype) - x = rearrange(x, 'b d t h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + out = x + for module in self.dwconv: + out = out + npu_config.run_conv2d(module, x, torch.float16) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + x = npu_config.run_conv2d(self.project_out, out, x_dtype) return x - @maybe_allow_in_graph class BasicTransformerBlock(nn.Module): r""" @@ -606,8 +832,8 @@ def __init__( num_frames: int = 16, height: int = 32, width: int = 32, - down_factor: int = 2, - mlp_ratio: float = 2.0, + downsampler: str = None, + mlp_ratio: int = 4, ): super().__init__() self.only_cross_attention = only_cross_attention @@ -615,7 +841,6 @@ def __init__( self.t = num_frames self.h = height self.w = width - self.down_factor = down_factor # We keep these boolean flags for backward-compatibility. self.use_ada_layer_norm_zero = (num_embeds_ada_norm is not None) and norm_type == "ada_norm_zero" self.use_ada_layer_norm = (num_embeds_ada_norm is not None) and norm_type == "ada_norm" @@ -669,13 +894,10 @@ def __init__( cross_attention_dim=cross_attention_dim if only_cross_attention else None, upcast_attention=upcast_attention, out_bias=attention_out_bias, - downsampler=True, - dim=dim, + downsampler=downsampler, num_frames=num_frames, height=height, width=width, - down_factor=down_factor, - down_shortcut=True, attention_mode=attention_mode, ) @@ -707,7 +929,7 @@ def __init__( bias=attention_bias, upcast_attention=upcast_attention, out_bias=attention_out_bias, - downsampler=False, + downsampler=None, attention_mode=attention_mode, ) # is self-attn if encoder_hidden_states is none else: @@ -730,10 +952,10 @@ def __init__( elif norm_type == "layer_norm_i2vgen": self.norm3 = None - self.ff = FeedForward( + self.ff = FeedForward_Conv2d( + downsampler, dim, - ffn_expansion_factor=mlp_ratio, - rep=True, + hidden_features=mlp_ratio * dim, num_frames=num_frames, height=height, width=width, diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 5dc05548b..001aaaa6d 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -9,8 +9,8 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/scripts/causalvae/rec_image.sh b/scripts/causalvae/rec_image.sh new file mode 100644 index 000000000..74d1a48a8 --- /dev/null +++ b/scripts/causalvae/rec_image.sh @@ -0,0 +1,8 @@ +CUDA_VISIBLE_DEVICES=0 python examples/rec_image.py \ + --ae_path "/storage/dataset/6_10_latent8" \ + --image_path /storage/dataset/image/anytext3m/ocr_data/Art/images/gt_5544.jpg \ + --rec_path rec.jpg \ + --device cuda \ + --short_size 512 \ + --ae CausalVAEModel_D8_4x8x8 \ + --enable_tiling \ No newline at end of file diff --git a/scripts/causalvae/rec_video.sh b/scripts/causalvae/rec_video.sh new file mode 100644 index 000000000..2809287be --- /dev/null +++ b/scripts/causalvae/rec_video.sh @@ -0,0 +1,11 @@ +CUDA_VISIBLE_DEVICES=0 python examples/rec_video.py \ + --ae_path "/storage/dataset/6_10_latent8" \ + --video_path /storage/dataset/mixkit/Trains/mixkit-train-passing-the-rails-4462_resize1080p.mp4 \ + --rec_path rec.mp4 \ + --device cuda \ + --sample_rate 1 \ + --num_frames 61 \ + --height 512 \ + --width 512 \ + --ae CausalVAEModel_D8_4x8x8 \ + --enable_tiling diff --git a/scripts/causalvae/reconstruction.sh b/scripts/causalvae/reconstruction.sh deleted file mode 100644 index 648103164..000000000 --- a/scripts/causalvae/reconstruction.sh +++ /dev/null @@ -1,11 +0,0 @@ -CUDA_VISIBLE_DEVICES=0 python examples/rec_imvi_vae.py \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_path /storage/mixkit/LanguageBind/Open-Sora-Plan-v1.1.0/all_mixkit/mixkit/Trains/mixkit-three-boys-reflected-in-the-mirrors-of-a-prism-34524.mp4 \ - --rec_path rec.mp4 \ - --device cuda \ - --sample_rate 1 \ - --num_frames 61 \ - --height 512 \ - --width 512 \ - --ae CausalVAEModel_4x8x8 \ - --enable_tiling \ No newline at end of file diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/sample_image.sh new file mode 100644 index 000000000..93625a5a1 --- /dev/null +++ b/scripts/text_condition/sample_image.sh @@ -0,0 +1,18 @@ +CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_lr1e-4_snr5_ema_ps11_ds22/checkpoint-8000/model \ + --version 65x512x512 \ + --num_frames 1 \ + --height 256 \ + --width 256 \ + --cache_dir "cache_dir" \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --text_prompt examples/prompt_list_0.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ + --save_img_path "./sample_image256_256_ps22_ds11_ckpt8000" \ + --fps 24 \ + --guidance_scale 2.0 \ + --num_sampling_steps 20 \ + --enable_tiling \ + --sample_method DDPM \ + --model_3d \ No newline at end of file diff --git a/scripts/text_condition/train_ds_image_256x256.sh b/scripts/text_condition/train_ds_image_256x256.sh index 3bf3c9384..d44b0114f 100644 --- a/scripts/text_condition/train_ds_image_256x256.sh +++ b/scripts/text_condition/train_ds_image_256x256.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs16_4node_lr1e-4_snr5_ema" +export PROJECT="bs32_2node_lr1e-4_snr5_ema_ps22_k33_s11_4convffn_8dvae" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -12,14 +12,14 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example1.yaml \ - opensora/train/train_t2v.py \ - --model OpenSoraT2V-B/111 \ + --config_file scripts/accelerate_configs/multi_node_example2.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-B/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --ae CausalVAEModel_D8_4x8x8 \ + --ae_path "/storage/dataset/6_10_latent8" \ --video_data "scripts/train_data/video_data.txt" \ --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ @@ -31,7 +31,7 @@ accelerate launch \ --interpolation_scale_w 0.5 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=16 \ + --train_batch_size=32 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -41,7 +41,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="bs16_4node_lr1e-4_snr5_ema_ps11_ds22" \ + --output_dir="bs32_2node_lr1e-4_snr5_ema_ps22_k33_s11_4convffn_8dvae" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ @@ -50,5 +50,5 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ - --downsampler "k55_s22" \ + --downsampler "k33_s11" \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_ds_video3d_65x240p.sh b/scripts/text_condition/train_ds_video3d_61x240p.sh similarity index 100% rename from scripts/text_condition/train_ds_video3d_65x240p.sh rename to scripts/text_condition/train_ds_video3d_61x240p.sh diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/train_imageudit_480p.sh new file mode 100644 index 000000000..d73ca9d3c --- /dev/null +++ b/scripts/text_condition/train_imageudit_480p.sh @@ -0,0 +1,54 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example2.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=50 \ + --output_dir="bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_video21d_65x240p.sh b/scripts/text_condition/train_video21d_61x240p.sh similarity index 100% rename from scripts/text_condition/train_video21d_65x240p.sh rename to scripts/text_condition/train_video21d_61x240p.sh diff --git a/scripts/text_condition/train_video21d_65x240p_on_npu.sh b/scripts/text_condition/train_video21d_61x240p_on_npu.sh similarity index 100% rename from scripts/text_condition/train_video21d_65x240p_on_npu.sh rename to scripts/text_condition/train_video21d_61x240p_on_npu.sh diff --git a/scripts/text_condition/train_video3d_65x240p.sh b/scripts/text_condition/train_video3d_61x240p.sh similarity index 100% rename from scripts/text_condition/train_video3d_65x240p.sh rename to scripts/text_condition/train_video3d_61x240p.sh diff --git a/scripts/text_condition/train_video3d_65x240p_on_npu.sh b/scripts/text_condition/train_video3d_61x240p_on_npu.sh similarity index 100% rename from scripts/text_condition/train_video3d_65x240p_on_npu.sh rename to scripts/text_condition/train_video3d_61x240p_on_npu.sh diff --git a/scripts/text_condition/train_videoudit_125x480p.sh b/scripts/text_condition/train_videoudit_125x480p.sh new file mode 100644 index 000000000..721179d83 --- /dev/null +++ b/scripts/text_condition/train_videoudit_125x480p.sh @@ -0,0 +1,55 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="testudit_" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/debug.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ + --sample_rate 1 \ + --num_frames 125 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 0 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="testudit_" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" From 2a2c52e747d76ade2643e39cee287c2861de4885 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Wed, 12 Jun 2024 19:27:46 +0800 Subject: [PATCH 031/134] fix random video --- opensora/dataset/t2v_datasets.py | 8 ++++---- opensora/models/text_encoder/__init__.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 4c3367895..0cd523107 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -135,10 +135,10 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - # video_path = self.vid_cap_list[idx]['path'] - # assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" - # frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None - # video = self.decord_read(video_path, frame_idx) + video_path = self.vid_cap_list[idx]['path'] + assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" + frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None + video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] assert h / w <= 16 / 16 and h / w >= 4 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 4/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 001aaaa6d..5dc05548b 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -9,8 +9,8 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] From c458f2af5a51fa80c6b11df4146920d7385a8801 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Wed, 12 Jun 2024 22:30:49 +0800 Subject: [PATCH 032/134] support 3d vae & Chinese & prodigy --- .../models/ae/videobase/modules/__init__.py | 6 +- .../ae/videobase/modules/updownsample.py | 51 ++++++++++++++++ opensora/models/text_encoder/__init__.py | 2 +- opensora/train/train_t2v_diffusers.py | 3 +- scripts/accelerate_configs/hostfile1 | 6 +- scripts/accelerate_configs/hostfile2 | 4 +- .../multi_node_example1.yaml | 6 +- .../multi_node_example2.yaml | 2 +- .../text_condition/train_imageudit_480p.sh | 8 +-- .../train_imageudit_480p_prodigy.sh | 60 +++++++++++++++++++ 10 files changed, 131 insertions(+), 17 deletions(-) create mode 100644 scripts/text_condition/train_imageudit_480p_prodigy.sh diff --git a/opensora/models/ae/videobase/modules/__init__.py b/opensora/models/ae/videobase/modules/__init__.py index 49052f8ed..a2e27cd03 100644 --- a/opensora/models/ae/videobase/modules/__init__.py +++ b/opensora/models/ae/videobase/modules/__init__.py @@ -20,5 +20,9 @@ TimeDownsampleRes2x, TimeUpsampleRes2x, TimeDownsampleResAdv2x, - TimeUpsampleResAdv2x + TimeUpsampleResAdv2x, + Spatial2x3DDownsample, + Spatial2x3DUpsample, + Spatial2xTime2x3DDownsample, + Spatial2xTime2x3DUpsample ) diff --git a/opensora/models/ae/videobase/modules/updownsample.py b/opensora/models/ae/videobase/modules/updownsample.py index ea54252b4..dcd83edba 100644 --- a/opensora/models/ae/videobase/modules/updownsample.py +++ b/opensora/models/ae/videobase/modules/updownsample.py @@ -286,3 +286,54 @@ def forward(self, x): x = torch.concat([x, x_], dim=2) alpha = torch.sigmoid(self.mix_factor) return alpha * x + (1 - alpha) * self.conv(self.attn(self.res(x))) + + + +class Spatial2xTime2x3DDownsample(Block): + def __init__(self, in_channels, out_channels): + super().__init__() + # no asymmetric padding in torch conv, must do it ourselves + self.conv = CausalConv3d(in_channels, out_channels, kernel_size=3, padding=0, stride=2) + + def forward(self, x): + pad = (0,1,0,1,0,0) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + return x + +class Spatial2x3DDownsample(Block): + def __init__(self, in_channels, out_channels): + super().__init__() + # no asymmetric padding in torch conv, must do it ourselves + self.conv = CausalConv3d(in_channels, out_channels, kernel_size=3, padding=0, stride=(1,2,2)) + + def forward(self, x): + pad = (0,1,0,1,0,0) + x = torch.nn.functional.pad(x, pad, mode="constant", value=0) + x = self.conv(x) + return x + +class Spatial2x3DUpsample(Block): + def __init__(self, in_channels, out_channels): + super().__init__() + # no asymmetric padding in torch conv, must do it ourselves + self.conv = CausalConv3d(in_channels, out_channels, kernel_size=3, padding=1) + + def forward(self, x): + x = F.interpolate(x, scale_factor=(1,2,2), mode='trilinear') + return self.conv(x) + +class Spatial2xTime2x3DUpsample(Block): + def __init__(self, in_channels, out_channels): + super().__init__() + # no asymmetric padding in torch conv, must do it ourselves + self.conv = CausalConv3d(in_channels, out_channels, kernel_size=3, padding=1) + def forward(self, x): + if x.size(2) > 1: + x,x_= x[:,:,:1],x[:,:,1:] + x_= F.interpolate(x_, scale_factor=(2,2,2), mode='trilinear') + x = F.interpolate(x, scale_factor=(1,2,2), mode='trilinear') + x = torch.concat([x, x_], dim=2) + else: + x = F.interpolate(x, scale_factor=(2,2), mode='trilinear') + return self.conv(x) \ No newline at end of file diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 5dc05548b..983643533 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -10,7 +10,7 @@ def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37', cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--umt5-xxl/snapshots/66cb9e7e85526fe440a945569e42c72fb6cbc0ad', cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index bd02d9647..13b171e76 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -67,7 +67,8 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh validation_prompt = [ "a cat wearing sunglasses and working as a lifeguard at pool.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." - ] + "一只戴着墨镜、在泳池当救生员的猫。", + "一只海龟游过珊瑚礁。"] logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) # scheduler = PNDMScheduler() diff --git a/scripts/accelerate_configs/hostfile1 b/scripts/accelerate_configs/hostfile1 index 9dd1526fd..2beea768e 100644 --- a/scripts/accelerate_configs/hostfile1 +++ b/scripts/accelerate_configs/hostfile1 @@ -1,4 +1,2 @@ -node031 slots=8 -node027 slots=8 -node034 slots=8 -node035 slots=8 \ No newline at end of file +node030 slots=8 +node012 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile2 b/scripts/accelerate_configs/hostfile2 index 8ee64692c..b46a32f09 100644 --- a/scripts/accelerate_configs/hostfile2 +++ b/scripts/accelerate_configs/hostfile2 @@ -1,2 +1,2 @@ -node032 slots=8 -node033 slots=8 \ No newline at end of file +node033 slots=8 +node034 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example1.yaml b/scripts/accelerate_configs/multi_node_example1.yaml index 155956d0b..ad03b2964 100644 --- a/scripts/accelerate_configs/multi_node_example1.yaml +++ b/scripts/accelerate_configs/multi_node_example1.yaml @@ -5,11 +5,11 @@ deepspeed_config: deepspeed_hostfile: scripts/accelerate_configs/hostfile1 fsdp_config: {} machine_rank: 0 -main_process_ip: 100.64.24.31 +main_process_ip: 100.64.24.30 main_process_port: 29503 main_training_function: main -num_machines: 4 -num_processes: 32 +num_machines: 2 +num_processes: 16 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/accelerate_configs/multi_node_example2.yaml b/scripts/accelerate_configs/multi_node_example2.yaml index b1c7dad43..5a0ca9e59 100644 --- a/scripts/accelerate_configs/multi_node_example2.yaml +++ b/scripts/accelerate_configs/multi_node_example2.yaml @@ -5,7 +5,7 @@ deepspeed_config: deepspeed_hostfile: scripts/accelerate_configs/hostfile2 fsdp_config: {} machine_rank: 0 -main_process_ip: 100.64.24.32 +main_process_ip: 100.64.24.33 main_process_port: 29502 main_training_function: main num_machines: 2 diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/train_imageudit_480p.sh index d73ca9d3c..bc96c724a 100644 --- a/scripts/text_condition/train_imageudit_480p.sh +++ b/scripts/text_condition/train_imageudit_480p.sh @@ -15,7 +15,7 @@ accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example2.yaml \ opensora/train/train_t2v_diffusers.py \ --model UDiTT2V-L/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --text_encoder_name google/umt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ @@ -31,7 +31,7 @@ accelerate launch \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=16 \ + --train_batch_size=32 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -40,8 +40,8 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=50 \ - --output_dir="bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22" \ + --checkpointing_steps=500 \ + --output_dir="bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22_umt5" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ diff --git a/scripts/text_condition/train_imageudit_480p_prodigy.sh b/scripts/text_condition/train_imageudit_480p_prodigy.sh new file mode 100644 index 000000000..c9c6aba5e --- /dev/null +++ b/scripts/text_condition/train_imageudit_480p_prodigy.sh @@ -0,0 +1,60 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22_prodigy" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example1.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name google/umt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=32 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --optimizer="prodigy" \ + --learning_rate=1.0 \ + --prodigy_safeguard_warmup=True \ + --prodigy_use_bias_correction=True \ + --adam_beta1=0.9 \ + --adam_beta2=0.99 \ + --adam_weight_decay=0.01 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22_prodigy_umt5" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" From 50416f33cf761adeccacddb42325ab8e1e633f2a Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Wed, 12 Jun 2024 23:24:36 +0800 Subject: [PATCH 033/134] fix eval prompt --- opensora/train/train_t2v_diffusers.py | 2 +- scripts/text_condition/train_imageudit_480p_prodigy.sh | 1 + scripts/train_data/image_data.txt | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 13b171e76..7dd2bb2e6 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -66,7 +66,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): validation_prompt = [ "a cat wearing sunglasses and working as a lifeguard at pool.", - "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." + "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene.", "一只戴着墨镜、在泳池当救生员的猫。", "一只海龟游过珊瑚礁。"] logger.info(f"Running validation....\n") diff --git a/scripts/text_condition/train_imageudit_480p_prodigy.sh b/scripts/text_condition/train_imageudit_480p_prodigy.sh index c9c6aba5e..4edac02bf 100644 --- a/scripts/text_condition/train_imageudit_480p_prodigy.sh +++ b/scripts/text_condition/train_imageudit_480p_prodigy.sh @@ -5,6 +5,7 @@ export PROJECT="bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22_prodigy" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting +export PDSH_RCMD_TYPE=ssh export NCCL_PXN_DISABLE=0 export NCCL_IB_QPS_PER_CONNECTION=4 export NCCL_IB_GID_INDEX=3 diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index 851a50d81..d29f051f9 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,3 +1,3 @@ /storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json /storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json -/storage/dataset/image/anytext3m,/storage/anno_jsons/anytext_en_1886137.json \ No newline at end of file +/storage/dataset/image/anytext3m,/storage/anno_jsons/anytext_all_3509994.json \ No newline at end of file From f050f899649819ffe570051b23ee0fccf32b1fc6 Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Thu, 13 Jun 2024 10:47:14 +0800 Subject: [PATCH 034/134] make sure contiguous when performing concat or communication --- opensora/adaptor/bf16_optimizer.py | 5 +++-- opensora/adaptor/stage_1_and_2.py | 6 +++++- opensora/adaptor/utils.py | 2 +- opensora/sample/sample_t2v_on_npu.py | 2 +- scripts/accelerate_configs/zero2_npu.json | 6 ++---- scripts/text_condition/train_image_256x256_on_npu.sh | 5 +++-- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/opensora/adaptor/bf16_optimizer.py b/opensora/adaptor/bf16_optimizer.py index f0ef553e0..681eb827b 100644 --- a/opensora/adaptor/bf16_optimizer.py +++ b/opensora/adaptor/bf16_optimizer.py @@ -26,7 +26,8 @@ setattr(sys.modules[__name__], 'fragment_address', fragment_address) -def bin_flatten(tensors): + +def contigous_flatten(tensors): return _flatten_dense_tensors([tensor.contiguous() for tensor in tensors]) @@ -66,7 +67,7 @@ def __init__(self, self.real_dp_process_group = [dp_process_group for i in range(len(self.optimizer.param_groups))] # Use torch (un)flatten ops - self.flatten = _flatten_dense_tensors + self.flatten = contigous_flatten self.unflatten = _unflatten_dense_tensors #align nccl all-gather send buffers to 4-bye boundary diff --git a/opensora/adaptor/stage_1_and_2.py b/opensora/adaptor/stage_1_and_2.py index a0d618b6e..80689c616 100644 --- a/opensora/adaptor/stage_1_and_2.py +++ b/opensora/adaptor/stage_1_and_2.py @@ -94,6 +94,9 @@ def _get_padded_tensor(src_tensor, size): return padded_tensor +def contigous_flatten(tensors): + return _flatten_dense_tensors([tensor.contiguous() for tensor in tensors]) + class DeepSpeedZeroOptimizer(ZeROOptimizer): """ DeepSpeedZeroOptimizer designed to reduce the memory footprint @@ -167,7 +170,7 @@ def __init__(self, self.optimizer = init_optimizer # Use torch (un)flatten ops - self.flatten = _flatten_dense_tensors + self.flatten = contigous_flatten self.unflatten = _unflatten_dense_tensors # ZeRO stage 1 (False) or 2 (True) @@ -1458,6 +1461,7 @@ def allreduce_bucket(self, bucket, rank=None, log=None, divide=True, process_gro if divide: tensor_to_allreduce.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size)) + tensor_to_allreduce = tensor_to_allreduce.contiguous() if rank is None: # "All Reducing" dist.all_reduce(tensor_to_allreduce, group=process_group) diff --git a/opensora/adaptor/utils.py b/opensora/adaptor/utils.py index fda283889..96c9ce81a 100644 --- a/opensora/adaptor/utils.py +++ b/opensora/adaptor/utils.py @@ -1001,7 +1001,7 @@ def all_gather_dp_groups(groups_flat, partitioned_param_groups, dp_process_group shard_list.append(curr_shard) - dist.all_gather(shard_list, shard_list[partition_id], dp_process_group[group_id]) + dist.all_gather(shard_list, shard_list[partition_id].contiguous(), dp_process_group[group_id]) class TLinear(torch.nn.Linear): diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py index 02d64efe0..dd7604eb6 100644 --- a/opensora/sample/sample_t2v_on_npu.py +++ b/opensora/sample/sample_t2v_on_npu.py @@ -221,7 +221,7 @@ def get_file_name(): time.sleep(80) continue - time.sleep(120) + time.sleep(180) latest_path = cur_path npu_config.print_msg(f"The latest_path is {latest_path}") full_path = f"{args.model_path}/{latest_path}/model_ema" diff --git a/scripts/accelerate_configs/zero2_npu.json b/scripts/accelerate_configs/zero2_npu.json index 3bdccd6d5..6f1ecdf2c 100644 --- a/scripts/accelerate_configs/zero2_npu.json +++ b/scripts/accelerate_configs/zero2_npu.json @@ -18,10 +18,8 @@ "zero_optimization": { "stage": 2, "overlap_comm": true, - "allgather_partitions": true, - "allgather_bucket_size": 67108864, - "reduce_scatter": true, + "allgather_bucket_size": 536870912, "contiguous_gradients": true, - "reduce_bucket_size": 67108864 + "reduce_bucket_size": 536870912 } } \ No newline at end of file diff --git a/scripts/text_condition/train_image_256x256_on_npu.sh b/scripts/text_condition/train_image_256x256_on_npu.sh index 16d7660cf..7c2f5cff2 100644 --- a/scripts/text_condition/train_image_256x256_on_npu.sh +++ b/scripts/text_condition/train_image_256x256_on_npu.sh @@ -8,7 +8,7 @@ accelerate launch \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-L/122 \ + --model OpenSoraT2V-S/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ @@ -30,7 +30,8 @@ accelerate launch \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=1e-4 \ - --lr_scheduler="constant" \ + --lr_scheduler="cosine" \ + --seed=10 \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ From b7a51f2c0cb25cb13dd03642d787741846468310 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 13 Jun 2024 10:59:45 +0800 Subject: [PATCH 035/134] fix t5 mismatch --- .../diffusion/opensora/modeling_opensora.py | 22 ++++++++--------- .../models/diffusion/udit/modeling_udit.py | 14 +++++------ opensora/models/text_encoder/__init__.py | 16 ++++++++++--- opensora/sample/sample_t2v.py | 9 +++++-- opensora/train/train_t2v_diffusers.py | 24 +++++++++---------- scripts/causalvae/rec_image.sh | 2 +- scripts/causalvae/rec_video.sh | 2 +- scripts/text_condition/sample_image.sh | 14 +++++------ scripts/text_condition/sample_video.sh | 18 ++++++++++++++ .../text_condition/train_imageudit_480p.sh | 17 ++++++------- 10 files changed, 86 insertions(+), 52 deletions(-) create mode 100644 scripts/text_condition/sample_video.sh diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index a4279e30f..caa5cd700 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -584,48 +584,48 @@ def _get_output_for_patched_inputs( def OpenSoraT2V_S_111(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_111(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_L_111(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2560, **kwargs) def OpenSoraT2V_S_122(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_L_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2560, **kwargs) # def OpenSoraT2V_S_222(**kwargs): # return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) +# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_222(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2048, **kwargs) # def OpenSoraT2V_L_222(**kwargs): # return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) +# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2560, **kwargs) # def OpenSoraT2V_XL_222(**kwargs): # return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) +# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=4096, **kwargs) # def OpenSoraT2V_XXL_222(**kwargs): # return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) +# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=5120, **kwargs) OpenSora_models = { "OpenSoraT2V-S/122": OpenSoraT2V_S_122, diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py index eca8d74bc..ccb2172ef 100644 --- a/opensora/models/diffusion/udit/modeling_udit.py +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -781,31 +781,31 @@ def _get_output_for_patched_inputs( def UDiTT2V_S_111(**kwargs): return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_B_111(**kwargs): return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_L_111(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_L_211(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_L_122(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_L_222(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_XL_111(**kwargs): return UDiTT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_XXL_111(**kwargs): return UDiTT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 983643533..7d503f43c 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -1,6 +1,6 @@ import torch from torch import nn -from transformers import T5EncoderModel, CLIPModel, CLIPProcessor +from transformers import CLIPModel, CLIPProcessor from opensora.utils.utils import get_precision @@ -9,8 +9,15 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = T5EncoderModel.from_pretrained(r'/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--umt5-xxl/snapshots/66cb9e7e85526fe440a945569e42c72fb6cbc0ad', cache_dir=args.cache_dir, **kwargs).eval() + if 'umt5' in self.model_name: + from transformers import UMT5EncoderModel + self.text_enc = UMT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + elif 'mt5' in self.model_name: + from transformers import MT5EncoderModel + self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + elif 't5' in self.model_name: + from transformers import T5EncoderModel + self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] @@ -31,6 +38,9 @@ def forward(self, input_ids, attention_mask): text_encoder = { + 'google/mt5-xl': T5Wrapper, + 'google/mt5-xxl': T5Wrapper, + 'google/umt5-xl': T5Wrapper, 'google/umt5-xxl': T5Wrapper, 'DeepFloyd/t5-v1_1-xxl': T5Wrapper, 'openai/clip-vit-large-patch14': CLIPWrapper diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index d6a7837c2..53b96ea4d 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -20,6 +20,7 @@ from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.diffusion.udit.modeling_udit import UDiTT2V from opensora.models.text_encoder import get_text_enc from opensora.utils.utils import save_video_grid @@ -48,9 +49,13 @@ def main(args): # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) if args.model_3d: - transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, + # low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = UDiTT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: - transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, + device_map=None, torch_dtype=weight_dtype) text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 9ccfd4d47..0ae0f5300 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -82,17 +82,17 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh for prompt in validation_prompt: logger.info('Processing the ({}) prompt'.format(prompt)) video = opensora_pipeline(prompt, - num_frames=args.num_frames, - # num_frames=1, - height=args.max_height, - width=args.max_width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - enable_temporal_attentions=True, - num_images_per_prompt=1, - mask_feature=True, - max_sequence_length=50, - ).images + num_frames=args.num_frames, + # num_frames=1, + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=150, + ).images videos.append(video[0]) # import ipdb;ipdb.set_trace() gc.collect() @@ -790,7 +790,7 @@ def train_all_epoch(prof_=None): # validation & logs parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=2.5) + parser.add_argument('--guidance_scale', type=float, default=5.0) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") diff --git a/scripts/causalvae/rec_image.sh b/scripts/causalvae/rec_image.sh index 74d1a48a8..7987b1566 100644 --- a/scripts/causalvae/rec_image.sh +++ b/scripts/causalvae/rec_image.sh @@ -1,5 +1,5 @@ CUDA_VISIBLE_DEVICES=0 python examples/rec_image.py \ - --ae_path "/storage/dataset/6_10_latent8" \ + --ae_path "/storage/dataset/test140k" \ --image_path /storage/dataset/image/anytext3m/ocr_data/Art/images/gt_5544.jpg \ --rec_path rec.jpg \ --device cuda \ diff --git a/scripts/causalvae/rec_video.sh b/scripts/causalvae/rec_video.sh index 2809287be..6a1b4c85f 100644 --- a/scripts/causalvae/rec_video.sh +++ b/scripts/causalvae/rec_video.sh @@ -1,5 +1,5 @@ CUDA_VISIBLE_DEVICES=0 python examples/rec_video.py \ - --ae_path "/storage/dataset/6_10_latent8" \ + --ae_path "/storage/dataset/test140k" \ --video_path /storage/dataset/mixkit/Trains/mixkit-train-passing-the-rails-4462_resize1080p.mp4 \ --rec_path rec.mp4 \ --device cuda \ diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/sample_image.sh index 93625a5a1..ba03fd8bd 100644 --- a/scripts/text_condition/sample_image.sh +++ b/scripts/text_condition/sample_image.sh @@ -1,18 +1,18 @@ CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_lr1e-4_snr5_ema_ps11_ds22/checkpoint-8000/model \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22_umt5/checkpoint-19000/model \ --version 65x512x512 \ --num_frames 1 \ - --height 256 \ - --width 256 \ + --height 480 \ + --width 640 \ --cache_dir "cache_dir" \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --text_encoder_name google/umt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/CausalVAEModel_4x8x8" \ - --save_img_path "./sample_image256_256_ps22_ds11_ckpt8000" \ + --save_img_path "./sample_image480p_ps22_ds11_udit_ckpt19k" \ --fps 24 \ - --guidance_scale 2.0 \ - --num_sampling_steps 20 \ + --guidance_scale 5.0 \ + --num_sampling_steps 50 \ --enable_tiling \ --sample_method DDPM \ --model_3d \ No newline at end of file diff --git a/scripts/text_condition/sample_video.sh b/scripts/text_condition/sample_video.sh new file mode 100644 index 000000000..93625a5a1 --- /dev/null +++ b/scripts/text_condition/sample_video.sh @@ -0,0 +1,18 @@ +CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_lr1e-4_snr5_ema_ps11_ds22/checkpoint-8000/model \ + --version 65x512x512 \ + --num_frames 1 \ + --height 256 \ + --width 256 \ + --cache_dir "cache_dir" \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --text_prompt examples/prompt_list_0.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ + --save_img_path "./sample_image256_256_ps22_ds11_ckpt8000" \ + --fps 24 \ + --guidance_scale 2.0 \ + --num_sampling_steps 20 \ + --enable_tiling \ + --sample_method DDPM \ + --model_3d \ No newline at end of file diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/train_imageudit_480p.sh index bc96c724a..a4b5099c4 100644 --- a/scripts/text_condition/train_imageudit_480p.sh +++ b/scripts/text_condition/train_imageudit_480p.sh @@ -1,9 +1,9 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 +export PROJECT="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps11_ds11_udit22_umt5xl" +# export HF_DATASETS_OFFLINE=1 +# export TRANSFORMERS_OFFLINE=1 # NCCL setting export NCCL_PXN_DISABLE=0 export NCCL_IB_QPS_PER_CONNECTION=4 @@ -12,14 +12,14 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example2.yaml \ + --config_file scripts/accelerate_configs/debug.yaml \ opensora/train/train_t2v_diffusers.py \ --model UDiTT2V-L/122 \ - --text_encoder_name google/umt5-xxl \ + --text_encoder_name google/umt5-xl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --ae_path "/storage/dataset/test140k" \ --video_data "scripts/train_data/video_data.txt" \ --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ @@ -31,7 +31,7 @@ accelerate launch \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=32 \ + --train_batch_size=16 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -41,7 +41,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22_umt5" \ + --output_dir="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps11_ds11_udit22_umt5xl" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ @@ -50,5 +50,6 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ + --noise_offset 0.02 \ --downsampler "k33_s22" \ --resume_from_checkpoint="latest" From 9591a7b1825efa8c1a7e242c0cb2b5b742f147f8 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 13 Jun 2024 03:45:33 +0000 Subject: [PATCH 036/134] fix noise offset --- opensora/train/train_t2v_diffusers.py | 2 +- scripts/accelerate_configs/hostfile | 6 +++--- scripts/text_condition/train_imageudit_480p.sh | 11 ++++++----- scripts/train_data/image_data.txt | 3 ++- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 0ae0f5300..5f36971f8 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -555,7 +555,7 @@ def run(model_input, model_kwargs, prof): noise = torch.randn_like(model_input) if args.noise_offset: # https://www.crosslabs.org//blog/diffusion-with-offset-noise - noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1), + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1), device=model_input.device) bsz = model_input.shape[0] diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index d1ae926f8..832256920 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,4 +1,4 @@ node030 slots=8 -node028 slots=8 -node064 slots=8 -node033 slots=8 \ No newline at end of file +node012 slots=8 +node033 slots=8 +node034 slots=8 \ No newline at end of file diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/train_imageudit_480p.sh index a4b5099c4..760349ee8 100644 --- a/scripts/text_condition/train_imageudit_480p.sh +++ b/scripts/text_condition/train_imageudit_480p.sh @@ -1,10 +1,11 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps11_ds11_udit22_umt5xl" -# export HF_DATASETS_OFFLINE=1 -# export TRANSFORMERS_OFFLINE=1 +export PROJECT="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_umt5xl" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 # NCCL setting +export PDSH_RCMD_TYPE=ssh export NCCL_PXN_DISABLE=0 export NCCL_IB_QPS_PER_CONNECTION=4 export NCCL_IB_GID_INDEX=3 @@ -12,7 +13,7 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v_diffusers.py \ --model UDiTT2V-L/122 \ --text_encoder_name google/umt5-xl \ @@ -41,7 +42,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps11_ds11_udit22_umt5xl" \ + --output_dir="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_umt5xl" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index d29f051f9..b404a2d2a 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,3 +1,4 @@ /storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json /storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json -/storage/dataset/image/anytext3m,/storage/anno_jsons/anytext_all_3509994.json \ No newline at end of file +/storage/dataset/image/anytext3m,/storage/anno_jsons/anytext_all_3509994.json +/storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json \ No newline at end of file From a56889e47b602b35bb555e37efcdc69bb8742b06 Mon Sep 17 00:00:00 2001 From: lb203 <62638829+LinB203@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:28:49 +0800 Subject: [PATCH 037/134] fix vae decoder for image --- opensora/models/ae/videobase/modules/updownsample.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opensora/models/ae/videobase/modules/updownsample.py b/opensora/models/ae/videobase/modules/updownsample.py index dcd83edba..a0fa3dbfb 100644 --- a/opensora/models/ae/videobase/modules/updownsample.py +++ b/opensora/models/ae/videobase/modules/updownsample.py @@ -335,5 +335,5 @@ def forward(self, x): x = F.interpolate(x, scale_factor=(1,2,2), mode='trilinear') x = torch.concat([x, x_], dim=2) else: - x = F.interpolate(x, scale_factor=(2,2), mode='trilinear') - return self.conv(x) \ No newline at end of file + x = F.interpolate(x, scale_factor=(1,2,2), mode='trilinear') + return self.conv(x) From a59766cf6bcbb2cb46c1c30d40eb9d8adaab2db2 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 13 Jun 2024 10:59:02 +0000 Subject: [PATCH 038/134] del umt5 --- opensora/models/text_encoder/__init__.py | 5 +---- opensora/train/train_t2v_diffusers.py | 5 +++-- scripts/causalvae/rec_image.sh | 2 +- scripts/causalvae/rec_video.sh | 8 ++++---- scripts/text_condition/train_imageudit_480p.sh | 6 +++--- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 7d503f43c..1a8232624 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -9,10 +9,7 @@ class T5Wrapper(nn.Module): def __init__(self, args, **kwargs): super(T5Wrapper, self).__init__() self.model_name = args.text_encoder_name - if 'umt5' in self.model_name: - from transformers import UMT5EncoderModel - self.text_enc = UMT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - elif 'mt5' in self.model_name: + if 'mt5' in self.model_name: from transformers import MT5EncoderModel self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 5f36971f8..532c1d651 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -66,13 +66,14 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): validation_prompt = [ + '在游泳馆里充当救生员的戴眼镜的猫', + '在珊瑚礁旁边游过一只海龟', "a cat wearing sunglasses and working as a lifeguard at pool.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." ] logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) - # scheduler = PNDMScheduler() - scheduler = DPMSolverMultistepScheduler() + scheduler = DDPMScheduler() opensora_pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, diff --git a/scripts/causalvae/rec_image.sh b/scripts/causalvae/rec_image.sh index 7987b1566..3c65c54b3 100644 --- a/scripts/causalvae/rec_image.sh +++ b/scripts/causalvae/rec_image.sh @@ -4,5 +4,5 @@ CUDA_VISIBLE_DEVICES=0 python examples/rec_image.py \ --rec_path rec.jpg \ --device cuda \ --short_size 512 \ - --ae CausalVAEModel_D8_4x8x8 \ + --ae CausalVAEModel_4x8x8 \ --enable_tiling \ No newline at end of file diff --git a/scripts/causalvae/rec_video.sh b/scripts/causalvae/rec_video.sh index 6a1b4c85f..7f7ffcb10 100644 --- a/scripts/causalvae/rec_video.sh +++ b/scripts/causalvae/rec_video.sh @@ -4,8 +4,8 @@ CUDA_VISIBLE_DEVICES=0 python examples/rec_video.py \ --rec_path rec.mp4 \ --device cuda \ --sample_rate 1 \ - --num_frames 61 \ - --height 512 \ - --width 512 \ - --ae CausalVAEModel_D8_4x8x8 \ + --num_frames 125 \ + --height 480 \ + --width 640 \ + --ae CausalVAEModel_4x8x8 \ --enable_tiling diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/train_imageudit_480p.sh index 760349ee8..0a80193e7 100644 --- a/scripts/text_condition/train_imageudit_480p.sh +++ b/scripts/text_condition/train_imageudit_480p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_umt5xl" +export PROJECT="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_mt5xl" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -33,7 +33,7 @@ accelerate launch \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=16 \ - --dataloader_num_workers 10 \ + --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=1e-4 \ @@ -42,7 +42,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_umt5xl" \ + --output_dir="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_mt5xl" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ From 003d68ac56a638958ead205d023dd269c6c5b798 Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Thu, 13 Jun 2024 20:59:33 +0800 Subject: [PATCH 039/134] change folder of scritps --- opensora/adaptor/utils.py | 1 - opensora/sample/sample_t2v_on_npu.py | 2 +- ...pu.yaml => multi_node_example_by_ddp.yaml} | 0 ...l => multi_node_example_by_deepspeed.yaml} | 0 .../text_condition/{ => gpu}/sample_image.sh | 0 .../text_condition/{ => gpu}/sample_video.sh | 0 .../{ => gpu}/train_ds_image_256x256.sh | 0 .../{ => gpu}/train_ds_video3d_61x240p.sh | 0 .../{ => gpu}/train_image_256x256.sh | 0 .../{ => gpu}/train_imageudit_480p.sh | 0 .../{ => gpu}/train_imageudit_480p_prodigy.sh | 0 .../{ => gpu}/train_video21d_61x240p.sh | 0 .../{ => gpu}/train_video3d_61x240p.sh | 0 .../{ => gpu}/train_videoudit_125x480p.sh | 0 scripts/text_condition/npu/sample_image.sh | 22 ++++++++ .../sample_video.sh} | 12 +++-- .../train_image_256x256.sh} | 10 ++-- .../npu/train_imageudit_480p.sh | 51 +++++++++++++++++++ .../train_video21d_nx240p.sh} | 9 ++-- .../train_video3d_nx240p.sh} | 18 ++++--- .../npu/train_videoudit_nx480p.sh | 51 +++++++++++++++++++ 21 files changed, 154 insertions(+), 22 deletions(-) rename scripts/accelerate_configs/{multi_node_example_on_npu.yaml => multi_node_example_by_ddp.yaml} (100%) rename scripts/accelerate_configs/{multi_node_deepspeed.yaml => multi_node_example_by_deepspeed.yaml} (100%) rename scripts/text_condition/{ => gpu}/sample_image.sh (100%) rename scripts/text_condition/{ => gpu}/sample_video.sh (100%) rename scripts/text_condition/{ => gpu}/train_ds_image_256x256.sh (100%) rename scripts/text_condition/{ => gpu}/train_ds_video3d_61x240p.sh (100%) rename scripts/text_condition/{ => gpu}/train_image_256x256.sh (100%) rename scripts/text_condition/{ => gpu}/train_imageudit_480p.sh (100%) rename scripts/text_condition/{ => gpu}/train_imageudit_480p_prodigy.sh (100%) rename scripts/text_condition/{ => gpu}/train_video21d_61x240p.sh (100%) rename scripts/text_condition/{ => gpu}/train_video3d_61x240p.sh (100%) rename scripts/text_condition/{ => gpu}/train_videoudit_125x480p.sh (100%) create mode 100644 scripts/text_condition/npu/sample_image.sh rename scripts/text_condition/{sample_video_on_npu.sh => npu/sample_video.sh} (67%) rename scripts/text_condition/{train_image_256x256_on_npu.sh => npu/train_image_256x256.sh} (82%) create mode 100644 scripts/text_condition/npu/train_imageudit_480p.sh rename scripts/text_condition/{train_video21d_61x240p_on_npu.sh => npu/train_video21d_nx240p.sh} (87%) rename scripts/text_condition/{train_video3d_61x240p_on_npu.sh => npu/train_video3d_nx240p.sh} (77%) create mode 100644 scripts/text_condition/npu/train_videoudit_nx480p.sh diff --git a/opensora/adaptor/utils.py b/opensora/adaptor/utils.py index 96c9ce81a..fbdd87e67 100644 --- a/opensora/adaptor/utils.py +++ b/opensora/adaptor/utils.py @@ -1000,7 +1000,6 @@ def all_gather_dp_groups(groups_flat, partitioned_param_groups, dp_process_group curr_shard = partitioned_params[dp_id].narrow(0, shard_id * shard_size, num_elements).detach() shard_list.append(curr_shard) - dist.all_gather(shard_list, shard_list[partition_id].contiguous(), dp_process_group[group_id]) diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py index dd7604eb6..094c9c4c8 100644 --- a/opensora/sample/sample_t2v_on_npu.py +++ b/opensora/sample/sample_t2v_on_npu.py @@ -232,7 +232,7 @@ def get_file_name(): profiler_level=torch_npu.profiler.ProfilerLevel.Level1, aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization ) - profile_output_path = "/home/image_data/shebin/npu_profiling_t2v" + profile_output_path = "/home/image_data/npu_profiling_t2v" os.makedirs(profile_output_path, exist_ok=True) with torch_npu.profiler.profile( diff --git a/scripts/accelerate_configs/multi_node_example_on_npu.yaml b/scripts/accelerate_configs/multi_node_example_by_ddp.yaml similarity index 100% rename from scripts/accelerate_configs/multi_node_example_on_npu.yaml rename to scripts/accelerate_configs/multi_node_example_by_ddp.yaml diff --git a/scripts/accelerate_configs/multi_node_deepspeed.yaml b/scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml similarity index 100% rename from scripts/accelerate_configs/multi_node_deepspeed.yaml rename to scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml diff --git a/scripts/text_condition/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh similarity index 100% rename from scripts/text_condition/sample_image.sh rename to scripts/text_condition/gpu/sample_image.sh diff --git a/scripts/text_condition/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh similarity index 100% rename from scripts/text_condition/sample_video.sh rename to scripts/text_condition/gpu/sample_video.sh diff --git a/scripts/text_condition/train_ds_image_256x256.sh b/scripts/text_condition/gpu/train_ds_image_256x256.sh similarity index 100% rename from scripts/text_condition/train_ds_image_256x256.sh rename to scripts/text_condition/gpu/train_ds_image_256x256.sh diff --git a/scripts/text_condition/train_ds_video3d_61x240p.sh b/scripts/text_condition/gpu/train_ds_video3d_61x240p.sh similarity index 100% rename from scripts/text_condition/train_ds_video3d_61x240p.sh rename to scripts/text_condition/gpu/train_ds_video3d_61x240p.sh diff --git a/scripts/text_condition/train_image_256x256.sh b/scripts/text_condition/gpu/train_image_256x256.sh similarity index 100% rename from scripts/text_condition/train_image_256x256.sh rename to scripts/text_condition/gpu/train_image_256x256.sh diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/gpu/train_imageudit_480p.sh similarity index 100% rename from scripts/text_condition/train_imageudit_480p.sh rename to scripts/text_condition/gpu/train_imageudit_480p.sh diff --git a/scripts/text_condition/train_imageudit_480p_prodigy.sh b/scripts/text_condition/gpu/train_imageudit_480p_prodigy.sh similarity index 100% rename from scripts/text_condition/train_imageudit_480p_prodigy.sh rename to scripts/text_condition/gpu/train_imageudit_480p_prodigy.sh diff --git a/scripts/text_condition/train_video21d_61x240p.sh b/scripts/text_condition/gpu/train_video21d_61x240p.sh similarity index 100% rename from scripts/text_condition/train_video21d_61x240p.sh rename to scripts/text_condition/gpu/train_video21d_61x240p.sh diff --git a/scripts/text_condition/train_video3d_61x240p.sh b/scripts/text_condition/gpu/train_video3d_61x240p.sh similarity index 100% rename from scripts/text_condition/train_video3d_61x240p.sh rename to scripts/text_condition/gpu/train_video3d_61x240p.sh diff --git a/scripts/text_condition/train_videoudit_125x480p.sh b/scripts/text_condition/gpu/train_videoudit_125x480p.sh similarity index 100% rename from scripts/text_condition/train_videoudit_125x480p.sh rename to scripts/text_condition/gpu/train_videoudit_125x480p.sh diff --git a/scripts/text_condition/npu/sample_image.sh b/scripts/text_condition/npu/sample_image.sh new file mode 100644 index 000000000..5fa44dec0 --- /dev/null +++ b/scripts/text_condition/npu/sample_image.sh @@ -0,0 +1,22 @@ +WEIGHT_PATH="/home/opensora/pre_weights/" + +export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" +export MASTER_PORT=12359 + +torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ + --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ + --num_frames ${NUM_FRAME} \ + --height 480 \ + --width 640 \ + --cache_dir "./cache_dir" \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --text_prompt examples/prompt_list_0.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ + --fps 24 \ + --guidance_scale 5.0 \ + --num_sampling_steps 50 \ + --enable_tiling \ + --sample_method DDPM \ + --model_3d \ No newline at end of file diff --git a/scripts/text_condition/sample_video_on_npu.sh b/scripts/text_condition/npu/sample_video.sh similarity index 67% rename from scripts/text_condition/sample_video_on_npu.sh rename to scripts/text_condition/npu/sample_video.sh index a889f2087..9e0d7d5e4 100644 --- a/scripts/text_condition/sample_video_on_npu.sh +++ b/scripts/text_condition/npu/sample_video.sh @@ -1,4 +1,4 @@ -WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +WEIGHT_PATH="/home/opensora/pre_weights/" export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" export MASTER_PORT=12359 @@ -12,9 +12,11 @@ torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ - --save_img_path "/home/image_data/yancen/sample_videos/${PROJECT_NAME}" \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ --fps 24 \ - --guidance_scale 4.0 \ + --guidance_scale 2.0 \ --num_sampling_steps 50 \ - --enable_tiling + --enable_tiling \ + --sample_method DDPM \ + --model_3d diff --git a/scripts/text_condition/train_image_256x256_on_npu.sh b/scripts/text_condition/npu/train_image_256x256.sh similarity index 82% rename from scripts/text_condition/train_image_256x256_on_npu.sh rename to scripts/text_condition/npu/train_image_256x256.sh index 7c2f5cff2..bceec313b 100644 --- a/scripts/text_condition/train_image_256x256_on_npu.sh +++ b/scripts/text_condition/npu/train_image_256x256.sh @@ -1,19 +1,21 @@ export PROJECT=$PROJECT_NAME -WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +WEIGHT_PATH="/home/opensora/pre_weights/" env export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_deepspeed.yaml \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-S/122 \ + --model OpenSoraT2V-L/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --ae_path "${WEIGHT_PATH}/test140k/" \ --video_data "./scripts/train_data/video_data_on_npu.txt" \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ diff --git a/scripts/text_condition/npu/train_imageudit_480p.sh b/scripts/text_condition/npu/train_imageudit_480p.sh new file mode 100644 index 000000000..383bb62d0 --- /dev/null +++ b/scripts/text_condition/npu/train_imageudit_480p.sh @@ -0,0 +1,51 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=8 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="cosine" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=1000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_video21d_61x240p_on_npu.sh b/scripts/text_condition/npu/train_video21d_nx240p.sh similarity index 87% rename from scripts/text_condition/train_video21d_61x240p_on_npu.sh rename to scripts/text_condition/npu/train_video21d_nx240p.sh index c5fbca834..6e14288ab 100644 --- a/scripts/text_condition/train_video21d_61x240p_on_npu.sh +++ b/scripts/text_condition/npu/train_video21d_nx240p.sh @@ -1,10 +1,12 @@ export PROJECT=$PROJECT_NAME -WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +WEIGHT_PATH="/home/opensora/pre_weights/" env export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ @@ -13,7 +15,7 @@ accelerate launch \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --ae_path "${WEIGHT_PATH}/test140k/" \ --video_data "./scripts/train_data/video_data_on_npu.txt" \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ @@ -35,6 +37,7 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ + --seed=10 \ --checkpointing_steps=1000 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ diff --git a/scripts/text_condition/train_video3d_61x240p_on_npu.sh b/scripts/text_condition/npu/train_video3d_nx240p.sh similarity index 77% rename from scripts/text_condition/train_video3d_61x240p_on_npu.sh rename to scripts/text_condition/npu/train_video3d_nx240p.sh index ea4246e26..137cb7ca4 100644 --- a/scripts/text_condition/train_video3d_61x240p_on_npu.sh +++ b/scripts/text_condition/npu/train_video3d_nx240p.sh @@ -1,19 +1,21 @@ export PROJECT=$PROJECT_NAME -WEIGHT_PATH="/home/opensora/shebin/pre_weights/" +WEIGHT_PATH="/home/opensora/pre_weights/" env export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example_on_npu.yaml \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-S/122 \ + --model OpenSoraT2V-L/122 \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --ae_path "${WEIGHT_PATH}/test140k/" \ --video_data "./scripts/train_data/video_data_on_npu.txt" \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ @@ -29,12 +31,13 @@ accelerate launch \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ - --lr_scheduler="constant" \ + --learning_rate=1e-4 \ + --lr_scheduler="cosine" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=20 \ + --seed=10 \ + --checkpointing_steps=1000 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ @@ -42,6 +45,5 @@ accelerate launch \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ - --pretrained /home/opensora/yancen/Open-Sora-Plan-dev/PixArt-alpha/transformer/diffusion_pytorch_model.safetensors \ --cfg 0.1 \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_videoudit_nx480p.sh b/scripts/text_condition/npu/train_videoudit_nx480p.sh new file mode 100644 index 000000000..39d9849e7 --- /dev/null +++ b/scripts/text_condition/npu/train_videoudit_nx480p.sh @@ -0,0 +1,51 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="cosine" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=1000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" From 0802761bdc6b87cb149c424d9f0110567302562a Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Thu, 13 Jun 2024 21:07:15 +0800 Subject: [PATCH 040/134] set default env:SAMPLE_SAVE_PATH for sample scripts --- scripts/text_condition/npu/sample_image.sh | 4 ++++ scripts/text_condition/npu/sample_video.sh | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/text_condition/npu/sample_image.sh b/scripts/text_condition/npu/sample_image.sh index 5fa44dec0..d2356a8d2 100644 --- a/scripts/text_condition/npu/sample_image.sh +++ b/scripts/text_condition/npu/sample_image.sh @@ -3,6 +3,10 @@ WEIGHT_PATH="/home/opensora/pre_weights/" export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" export MASTER_PORT=12359 +if [ -z "$SAMPLE_SAVE_PATH" ]; then + export SAMPLE_SAVE_PATH="/home/image_data/sample_videos" +fi + torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ --num_frames ${NUM_FRAME} \ diff --git a/scripts/text_condition/npu/sample_video.sh b/scripts/text_condition/npu/sample_video.sh index 9e0d7d5e4..ad4d95862 100644 --- a/scripts/text_condition/npu/sample_video.sh +++ b/scripts/text_condition/npu/sample_video.sh @@ -3,11 +3,15 @@ WEIGHT_PATH="/home/opensora/pre_weights/" export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" export MASTER_PORT=12359 +if [ -z "$SAMPLE_SAVE_PATH" ]; then + export SAMPLE_SAVE_PATH="/home/image_data/sample_videos" +fi + torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ --num_frames ${NUM_FRAME} \ - --height 240 \ - --width 320 \ + --height 480 \ + --width 640 \ --cache_dir "./cache_dir" \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ From 80bb32652bd710c639a8f83c59e5089414994d0e Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Fri, 14 Jun 2024 17:30:16 +0800 Subject: [PATCH 041/134] support imageudit on npu --- .../models/diffusion/udit/modeling_udit.py | 25 +-------- opensora/models/diffusion/udit/modules.py | 56 ++++++++----------- opensora/npu_config.py | 10 ++++ opensora/sample/sample_t2v_on_npu.py | 5 +- scripts/text_condition/npu/sample_image.sh | 4 +- .../npu/train_imageudit_480p.sh | 6 +- 6 files changed, 42 insertions(+), 64 deletions(-) diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py index ccb2172ef..d78bef8f7 100644 --- a/opensora/models/diffusion/udit/modeling_udit.py +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -476,11 +476,6 @@ def forward( encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-1]) - - # 1. Input added_cond_kwargs = {"resolution": None, "aspect_ratio": None} hidden_states, encoder_hidden_states_1, encoder_hidden_states_2, encoder_hidden_states_3, \ @@ -538,10 +533,6 @@ def custom_forward(*inputs): # encoder_2 out_enc_level2 = inp_enc_level2 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -575,10 +566,6 @@ def custom_forward(*inputs): inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - # latent latent = inp_enc_level3 if self.training and self.gradient_checkpointing: @@ -618,10 +605,6 @@ def custom_forward(*inputs): inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) out_dec_level2 = inp_dec_level2 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -659,10 +642,6 @@ def custom_forward(*inputs): inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) out_dec_level1 = inp_dec_level1 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -796,8 +775,8 @@ def UDiTT2V_L_211(**kwargs): mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) def UDiTT2V_L_122(**kwargs): - return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_222(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py index 36f6a43d6..e9d894f05 100644 --- a/opensora/models/diffusion/udit/modules.py +++ b/opensora/models/diffusion/udit/modules.py @@ -193,7 +193,11 @@ def forward(self, latent, num_frames): num_frames = latent.shape[-3] // self.patch_size_t height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size # latent = rearrange(latent, 'b c t h w -> (b t) c h w') - latent = self.proj(latent) + if npu_config is None: + latent = self.proj(latent) + else: + latent_dtype = latent.dtype + latent = npu_config.run_conv3d(self.proj, latent, latent_dtype) if self.flatten: # latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C @@ -431,11 +435,7 @@ def forward(self, x, attention_mask): # import ipdb;ipdb.set_trace() b = x.shape[0] x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) - if npu_config is None: - x = self.layer(x) + (x if self.down_shortcut else 0) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = self.layer(x) + (x if self.down_shortcut else 0) x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) @@ -503,6 +503,9 @@ def __call__( attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) else: attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) + attention_mask = attention_mask.repeat(1, 1, hidden_states.shape[1], 1) + if npu_config.enable_FA: + attention_mask = attention_mask.to(torch.bool) if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) @@ -680,17 +683,16 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) - x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') if npu_config is None: - x = self.body(x) + x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.body, x, x_dtype) + x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h), mode='reflect') + x = self.body(x) x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) - attention_mask = F.max_pool2d(attention_mask, kernel_size=2, stride=2) + attention_mask = F.max_pool2d(attention_mask.float(), kernel_size=2, stride=2) attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 @@ -705,11 +707,7 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) - if npu_config is None: - x = self.body(x) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.body, x, x_dtype) + x = self.body(x) x = x[:, :, :height*2-pad_h, :width*2-pad_w] x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) @@ -747,24 +745,14 @@ def __init__(self, downsampler, dim, hidden_features, bias=True, num_frames=8, h def forward(self, x): # import ipdb;ipdb.set_trace() - if npu_config is None: - x = self.project_in(x) - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) - x = F.gelu(x) - out = x - for module in self.dwconv: - out = out + module(x) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) - x = self.project_out(out) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.project_in, x, torch.float16) - x = F.gelu(x) - out = x - for module in self.dwconv: - out = out + npu_config.run_conv2d(module, x, torch.float16) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) - x = npu_config.run_conv2d(self.project_out, out, x_dtype) + x = self.project_in(x) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + module(x) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + x = self.project_out(out) return x @maybe_allow_in_graph diff --git a/opensora/npu_config.py b/opensora/npu_config.py index cbb07d259..cd794e219 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -252,6 +252,16 @@ def run_conv3d(self, operator, x, out_dtype): def run_pool_2d(self, operator, x): return self._run(operator, x, torch.float16) + def run_pad_2d(self, operator, x, pad, mode="constant"): + if self.on_npu: + x_dtype = x.dtype + x = x.to(torch.float16) + x = operator(x, pad, mode) + x = x.to(x_dtype) + else: + x = operator(x, pad, mode) + return x + def seed_everything(self, seed=100): seed += self.rank random.seed(seed) diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py index 094c9c4c8..76b45bb35 100644 --- a/opensora/sample/sample_t2v_on_npu.py +++ b/opensora/sample/sample_t2v_on_npu.py @@ -19,6 +19,7 @@ from opensora.models.ae import ae_stride_config, getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.udit.modeling_udit import UDiTT2V from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V from opensora.models.diffusion.latte.modeling_latte import LatteT2V @@ -37,7 +38,7 @@ def load_t2v_checkpoint(model_path): if args.model_3d: - transformer_model = OpenSoraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + transformer_model = UDiTT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: @@ -86,7 +87,7 @@ def run_model_and_save_images(pipeline, model_path): num_images_per_prompt=1, mask_feature=True, device=args.device, - max_sequence_length=100, + max_sequence_length=512, ).images print(videos.shape) try: diff --git a/scripts/text_condition/npu/sample_image.sh b/scripts/text_condition/npu/sample_image.sh index d2356a8d2..927693213 100644 --- a/scripts/text_condition/npu/sample_image.sh +++ b/scripts/text_condition/npu/sample_image.sh @@ -12,11 +12,11 @@ torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --num_frames ${NUM_FRAME} \ --height 480 \ --width 640 \ - --cache_dir "./cache_dir" \ + --cache_dir "../cache_dir" \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/test140k/" \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ --fps 24 \ --guidance_scale 5.0 \ diff --git a/scripts/text_condition/npu/train_imageudit_480p.sh b/scripts/text_condition/npu/train_imageudit_480p.sh index 383bb62d0..45911e9de 100644 --- a/scripts/text_condition/npu/train_imageudit_480p.sh +++ b/scripts/text_condition/npu/train_imageudit_480p.sh @@ -15,7 +15,7 @@ accelerate launch \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/test140k/" \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ --video_data "./scripts/train_data/video_data_on_npu.txt" \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ @@ -27,7 +27,7 @@ accelerate launch \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=8 \ + --train_batch_size=4 \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -37,7 +37,7 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=1000 \ + --checkpointing_steps=250 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ From 895aed87b6ef65ba59329c0c7db38e8e52a74d48 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Sun, 16 Jun 2024 11:17:52 +0000 Subject: [PATCH 042/134] stable loss --- .gitignore | 3 +- opensora/dataset/__init__.py | 4 +- opensora/dataset/t2v_datasets.py | 9 +- opensora/models/diffusion/__init__.py | 4 + .../diffusion/opensora/modeling_opensora.py | 22 +- opensora/models/diffusion/opensora/modules.py | 4 +- .../models/diffusion/udit/modeling_udit.py | 16 +- opensora/models/diffusion/udit/modules.py | 10 +- .../udit_ultra/modeling_udit_ultra.py | 960 ++++++++++++++ .../models/diffusion/udit_ultra/modules.py | 1107 +++++++++++++++++ opensora/models/text_encoder/__init__.py | 3 + opensora/sample/sample_t2v.py | 6 +- opensora/train/train_t2v_diffusers.py | 29 +- opensora/utils/dataset_utils.py | 2 + scripts/accelerate_configs/hostfile | 4 +- scripts/accelerate_configs/zero2.json | 4 +- scripts/text_condition/gpu/sample_image.sh | 6 +- .../gpu/train_ds_image_256x256.sh | 20 +- .../text_condition/train_imageudit_240p.sh | 56 + .../train_imageudit_240p_debug.sh | 55 + .../train_imageudit_240p_new.sh | 57 + .../text_condition/train_imageudit_480p.sh | 0 22 files changed, 2325 insertions(+), 56 deletions(-) create mode 100644 opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py create mode 100644 opensora/models/diffusion/udit_ultra/modules.py create mode 100644 scripts/text_condition/train_imageudit_240p.sh create mode 100644 scripts/text_condition/train_imageudit_240p_debug.sh create mode 100644 scripts/text_condition/train_imageudit_240p_new.sh create mode 100644 scripts/text_condition/train_imageudit_480p.sh diff --git a/.gitignore b/.gitignore index 62a0ebb0b..14285d661 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ sft* flash* 65x256* alpha_vae -*bs32* \ No newline at end of file +*bs32* +*bs64* \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index e6763bc9e..30d89308b 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -63,7 +63,9 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index e62fe0e53..76391eec1 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -179,10 +179,17 @@ def get_image(self, idx): image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + + for i in image: + assert not torch.any(torch.isnan(i)), 'before transform0' image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + for i in image: + assert not torch.any(torch.isnan(i)), 'before transform1' image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] - # image = [torch.rand(1, 3, 256, 256) for i in image_data] + for i in image: + assert not torch.any(torch.isnan(i)), 'after transform' + # image = [torch.rand(1, 3, 480, 640) for i in image_data] image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] caps = [i['cap'] for i in image_data] diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index 2a96d0b97..2a103b9ff 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -1,16 +1,20 @@ from .latte.modeling_latte import Latte_models from .opensora.modeling_opensora import OpenSora_models from .udit.modeling_udit import UDiT_models +from .udit_ultra.modeling_udit_ultra import UDiT_Ultra_models Diffusion_models = {} Diffusion_models.update(Latte_models) Diffusion_models.update(OpenSora_models) Diffusion_models.update(UDiT_models) +Diffusion_models.update(UDiT_Ultra_models) from .latte.modeling_latte import Latte_models_class from .opensora.modeling_opensora import OpenSora_models_class from .udit.modeling_udit import UDiT_models_class +from .udit_ultra.modeling_udit_ultra import UDiT_Ultra_models_class Diffusion_models_class = {} Diffusion_models_class.update(Latte_models_class) Diffusion_models_class.update(OpenSora_models_class) Diffusion_models_class.update(UDiT_models_class) +Diffusion_models_class.update(UDiT_Ultra_models_class) \ No newline at end of file diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index caa5cd700..a4279e30f 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -584,48 +584,48 @@ def _get_output_for_patched_inputs( def OpenSoraT2V_S_111(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=1152, **kwargs) + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_111(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2048, **kwargs) + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_L_111(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2560, **kwargs) + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) def OpenSoraT2V_S_122(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=1152, **kwargs) + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2048, **kwargs) + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_L_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2560, **kwargs) + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) # def OpenSoraT2V_S_222(**kwargs): # return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=1152, **kwargs) +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) def OpenSoraT2V_B_222(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2048, **kwargs) + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) # def OpenSoraT2V_L_222(**kwargs): # return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=2560, **kwargs) +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) # def OpenSoraT2V_XL_222(**kwargs): # return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=4096, **kwargs) +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) # def OpenSoraT2V_XXL_222(**kwargs): # return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, -# norm_type="ada_norm_single", caption_channels=2048, cross_attention_dim=5120, **kwargs) +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) OpenSora_models = { "OpenSoraT2V-S/122": OpenSoraT2V_S_122, diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 159305db8..6acd8a2b9 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -503,7 +503,6 @@ def __init__(self, downsampler, attention_mode, **kwags): self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True) - @@ -641,7 +640,6 @@ def __call__( encoder_hidden_states = hidden_states elif attn.norm_cross: encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) - # import ipdb;ipdb.set_trace() key = attn.to_k(encoder_hidden_states) value = attn.to_v(encoder_hidden_states) @@ -662,8 +660,8 @@ def __call__( hidden_states = npu_config.restore_dtype(hidden_states) else: query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py index d78bef8f7..68e134d6c 100644 --- a/opensora/models/diffusion/udit/modeling_udit.py +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -487,6 +487,7 @@ def forward( (height + pad_h_0) // (self.config.patch_size), (width + pad_w_0) // (self.config.patch_size) + assert not torch.any(torch.isnan(hidden_states)), 'after _operate_on_patched_inputs' def create_custom_forward(module, return_dict=None): def custom_forward(*inputs): if return_dict is not None: @@ -670,7 +671,7 @@ def custom_forward(*inputs): class_labels=class_labels, ) - # import ipdb;ipdb.set_trace() + assert not torch.any(torch.isnan(out_dec_level1)), 'after out_dec_level1' # 3. Output output = self._get_output_for_patched_inputs( hidden_states=out_dec_level1, @@ -682,6 +683,7 @@ def custom_forward(*inputs): width=width, ) # b c t h w + assert not torch.any(torch.isnan(output)), 'after output' frame, height, width = frame * self.config.patch_size_t, height * self.config.patch_size - pad_h_0, width * self.config.patch_size - pad_w_0 output = output[:, :, :frame, :height, :width] if not return_dict: @@ -760,19 +762,19 @@ def _get_output_for_patched_inputs( def UDiTT2V_S_111(**kwargs): return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_B_111(**kwargs): return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_111(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_211(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_122(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, @@ -780,11 +782,11 @@ def UDiTT2V_L_122(**kwargs): def UDiTT2V_L_222(**kwargs): return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_XL_111(**kwargs): return UDiTT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_XXL_111(**kwargs): return UDiTT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py index e9d894f05..df8813fb0 100644 --- a/opensora/models/diffusion/udit/modules.py +++ b/opensora/models/diffusion/udit/modules.py @@ -382,6 +382,9 @@ def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attentio padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True, num_frames=num_frames, height=height, width=width) + self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + class DownSampler3d(nn.Module): def __init__(self, *args, **kwargs): @@ -541,6 +544,11 @@ def __call__( key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + # qk norm + query = attn.q_norm(query) + key = attn.k_norm(key) + query = query * (head_dim ** -0.5) + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible attention_mask = None # the output of sdp = (batch, num_heads, seq_len, head_dim) @@ -1056,7 +1064,6 @@ def forward( **cross_attention_kwargs, ) hidden_states = attn_output + hidden_states - # 4. Feed-forward # i2vgen doesn't have this norm 🤷‍♂️ if self.norm_type == "ada_norm_continuous": @@ -1076,7 +1083,6 @@ def forward( ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) else: ff_output = self.ff(norm_hidden_states) - if self.norm_type == "ada_norm_zero": ff_output = gate_mlp.unsqueeze(1) * ff_output elif self.norm_type == "ada_norm_single": diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py new file mode 100644 index 000000000..a0abdfd62 --- /dev/null +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -0,0 +1,960 @@ +import os +import numpy as np +from torch import nn +import torch +from einops import rearrange, repeat +from typing import Any, Dict, Optional, Tuple +from torch.nn import functional as F +from diffusers.models.transformer_2d import Transformer2DModelOutput +from diffusers.utils import is_torch_version, deprecate +from diffusers.configuration_utils import ConfigMixin, register_to_config +from diffusers.models.modeling_utils import ModelMixin +from diffusers.models.normalization import AdaLayerNormSingle +from diffusers.models.embeddings import PixArtAlphaTextProjection +from opensora.models.diffusion.udit.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock +from opensora.utils.utils import to_2tuple +import math +import re +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + + +class UDiTUltraT2V(ModelMixin, ConfigMixin): + """ + A 2D Transformer model for image-like data. + + Parameters: + num_attention_heads (`int`, *optional*, defaults to 16): The number of heads to use for multi-head attention. + attention_head_dim (`int`, *optional*, defaults to 88): The number of channels in each head. + in_channels (`int`, *optional*): + The number of channels in the input and output (specify if the input is **continuous**). + num_layers (`int`, *optional*, defaults to 1): The number of layers of Transformer blocks to use. + dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use. + cross_attention_dim (`int`, *optional*): The number of `encoder_hidden_states` dimensions to use. + sample_size (`int`, *optional*): The width of the latent images (specify if the input is **discrete**). + This is fixed during training since it is used to learn a number of position embeddings. + num_vector_embeds (`int`, *optional*): + The number of classes of the vector embeddings of the latent pixels (specify if the input is **discrete**). + Includes the class for the masked latent pixel. + activation_fn (`str`, *optional*, defaults to `"geglu"`): Activation function to use in feed-forward. + num_embeds_ada_norm ( `int`, *optional*): + The number of diffusion steps used during training. Pass if at least one of the norm_layers is + `AdaLayerNorm`. This is fixed during training since it is used to learn a number of embeddings that are + added to the hidden states. + + During inference, you can denoise for up to but not more steps than `num_embeds_ada_norm`. + attention_bias (`bool`, *optional*): + Configure if the `TransformerBlocks` attention should contain a bias parameter. + """ + + _supports_gradient_checkpointing = True + + @register_to_config + def __init__( + self, + num_attention_heads: int = 16, + attention_head_dim: int = 88, + in_channels: Optional[int] = None, + out_channels: Optional[int] = None, + num_layers: int = 1, + dropout: float = 0.0, + norm_num_groups: int = 32, + cross_attention_dim: Optional[int] = None, + attention_bias: bool = False, + sample_size: Optional[int] = None, + sample_size_t: Optional[int] = None, + patch_size: Optional[int] = None, + patch_size_t: Optional[int] = None, + num_vector_embeds: Optional[int] = None, + mlp_ratio: int = 4, + depth: Optional[list] = [2, 5, 8, 5, 2], + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + use_linear_projection: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_type: str = "layer_norm", # 'layer_norm', 'ada_norm', 'ada_norm_zero', 'ada_norm_single', 'ada_norm_continuous', 'layer_norm_i2vgen' + norm_elementwise_affine: bool = True, + norm_eps: float = 1e-5, + attention_type: str = "default", + caption_channels: int = None, + interpolation_scale_h: float = None, + interpolation_scale_w: float = None, + interpolation_scale_t: float = None, + use_additional_conditions: Optional[bool] = None, + attention_mode: str = 'xformers', + downsampler: str = 'k333_s222' + ): + super().__init__() + + # Set some common variables used across the board. + self.downsampler = downsampler + self.use_linear_projection = use_linear_projection + self.interpolation_scale_t = interpolation_scale_t + self.interpolation_scale_h = interpolation_scale_h + self.interpolation_scale_w = interpolation_scale_w + self.caption_channels = caption_channels + self.num_attention_heads = num_attention_heads + self.attention_head_dim = attention_head_dim + self.inner_dim = self.config.num_attention_heads * self.config.attention_head_dim + self.in_channels = in_channels + self.out_channels = in_channels if out_channels is None else out_channels + self.gradient_checkpointing = False + use_additional_conditions = False + # if use_additional_conditions is None: + # if norm_type == "ada_norm_single" and sample_size == 128: + # use_additional_conditions = True + # else: + # use_additional_conditions = False + self.use_additional_conditions = use_additional_conditions + + # 1. Transformer2DModel can process both standard continuous images of shape `(batch_size, num_channels, width, height)` as well as quantized image embeddings of shape `(batch_size, num_image_vectors)` + # Define whether input is continuous or discrete depending on configuration + assert in_channels is not None + + if norm_type == "layer_norm" and num_embeds_ada_norm is not None: + deprecation_message = ( + f"The configuration file of this model: {self.__class__} is outdated. `norm_type` is either not set or" + " incorrectly set to `'layer_norm'`. Make sure to set `norm_type` to `'ada_norm'` in the config." + " Please make sure to update the config accordingly as leaving `norm_type` might led to incorrect" + " results in future versions. If you have downloaded this checkpoint from the Hugging Face Hub, it" + " would be very nice if you could open a Pull request for the `transformer/config.json` file" + ) + deprecate("norm_type!=num_embeds_ada_norm", "1.0.0", deprecation_message, standard_warn=False) + norm_type = "ada_norm" + + # 2. Initialize the right blocks. + # Initialize the output blocks and other projection blocks when necessary. + self._init_patched_inputs(norm_type=norm_type) + + def _init_patched_inputs(self, norm_type): + assert self.config.sample_size_t is not None, "OpenSoraT2V over patched input must provide sample_size_t" + assert self.config.sample_size is not None, "OpenSoraT2V over patched input must provide sample_size" + + self.num_frames = self.config.sample_size_t + self.config.sample_size = to_2tuple(self.config.sample_size) + self.height = self.config.sample_size[0] + self.width = self.config.sample_size[1] + interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 + interpolation_scale_t = ( + self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t + ) + interpolation_scale = ( + self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 30, + self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 40, + ) + + # down_factor = list(re.search(r's(\d{2,3})', self.downsampler).group(1)) + # down_factor = [int(i) for i in down_factor] + # down_factor = down_factor if isinstance(self.config.down_factor, list) else [self.config.down_factor] * 5 + # down_factor = [2] * len(self.config.depth) + + if self.config.downsampler is not None and len(self.config.downsampler) == 9: + is_video_model = True + self.pos_embed = OverlapPatchEmbed3D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + elif self.config.downsampler is not None and len(self.config.downsampler) == 7: + is_video_model = False + self.pos_embed = OverlapPatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + ) + layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, + (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, + (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] + + for i in range((len(self.config.depth)-1)//2): + t = layer_thw[i][0] // 2 if layer_thw[i][0] != 1 else 1 + h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention + w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 + layer_thw.append([t, h, w]) + + self.encoder_level_1 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim, + self.config.num_attention_heads, + self.config.attention_head_dim, + num_frames=layer_thw[0][0], + height=layer_thw[0][1], + width=layer_thw[0][2], + downsampler=self.config.downsampler, + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[0]) + ] + ) + self.down1_2 = Downsample3d(self.inner_dim) if is_video_model else Downsample2d(self.inner_dim) + + self.encoder_level_2 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 2, + self.config.num_attention_heads, + self.config.attention_head_dim * 2, + num_frames=layer_thw[1][0], + height=layer_thw[1][1], + width=layer_thw[1][2], + downsampler=self.config.downsampler, + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 2, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[1]) + ] + ) + self.down2_3 = Downsample3d(self.inner_dim * 2) if is_video_model else Downsample2d(self.inner_dim * 2) + + self.latent = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 4, + self.config.num_attention_heads, + self.config.attention_head_dim * 4, + num_frames=layer_thw[2][0], + height=layer_thw[2][1], + width=layer_thw[2][2], + downsampler=self.config.downsampler, + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 4, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[2]) + ] + ) + + self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 + # self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) + self.reduce_chan_level2_norm = nn.ModuleList( + [nn.LayerNorm(int(self.inner_dim * 4)) for i in range(self.config.depth[3])] + ) + self.reduce_chan_level2 = nn.ModuleList( + [nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) for i in range(self.config.depth[3])] + ) + self.decoder_level_2 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 2, + self.config.num_attention_heads, + self.config.attention_head_dim * 2, + num_frames=layer_thw[1][0], + height=layer_thw[1][1], + width=layer_thw[1][2], + downsampler=self.config.downsampler, + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 2, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[3]) + ] + ) + + self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 + # self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 2), bias=True) + self.reduce_chan_level1_norm = nn.ModuleList( + [nn.LayerNorm(int(self.inner_dim * 2))] + [nn.LayerNorm(int(self.inner_dim * 3)) for i in range(self.config.depth[4]-1)] + ) + self.reduce_chan_level1 = nn.ModuleList( + [nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 2), bias=True)] + [nn.Linear(int(self.inner_dim * 3), int(self.inner_dim * 2), bias=True) for i in range(self.config.depth[4]-1)] + ) + self.decoder_level_1 = nn.ModuleList( + [ + BasicTransformerBlock( + self.inner_dim * 2, + self.config.num_attention_heads, + self.config.attention_head_dim * 2, + num_frames=layer_thw[0][0], + height=layer_thw[0][1], + width=layer_thw[0][2], + downsampler=self.config.downsampler, + mlp_ratio=self.config.mlp_ratio, + dropout=self.config.dropout, + cross_attention_dim=self.inner_dim * 2, + activation_fn=self.config.activation_fn, + num_embeds_ada_norm=self.config.num_embeds_ada_norm, + attention_bias=self.config.attention_bias, + only_cross_attention=self.config.only_cross_attention, + double_self_attention=self.config.double_self_attention, + upcast_attention=self.config.upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=self.config.norm_elementwise_affine, + norm_eps=self.config.norm_eps, + attention_type=self.config.attention_type, + attention_mode=self.config.attention_mode, + ) + for _ in range(self.config.depth[4]) + ] + ) + + if self.config.norm_type != "ada_norm_single": + self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) + self.proj_out_1 = nn.Linear(2 * self.inner_dim, 2 * self.inner_dim) + self.proj_out_2 = nn.Linear( + 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels + ) + elif self.config.norm_type == "ada_norm_single": + self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) + self.scale_shift_table = nn.Parameter(torch.randn(2, 2 * self.inner_dim) / (2 * self.inner_dim)**0.5) + self.proj_out = nn.Linear( + 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels + ) + + # PixArt-Alpha blocks. + # self.adaln_single = None + # if self.config.norm_type == "ada_norm_single": + # TODO(Sayak, PVP) clean this, for now we use sample size to determine whether to use + # additional conditions until we find better name + self.adaln_single_1 = AdaLayerNormSingle( + self.inner_dim, use_additional_conditions=self.use_additional_conditions + ) + self.adaln_single_2 = AdaLayerNormSingle( + self.inner_dim * 2, use_additional_conditions=self.use_additional_conditions + ) + self.adaln_single_3 = AdaLayerNormSingle( + self.inner_dim * 4, use_additional_conditions=self.use_additional_conditions + ) + + # self.caption_projection = None + # if self.caption_channels is not None: + self.caption_projection_1 = PixArtAlphaTextProjection( + in_features=self.caption_channels, hidden_size=self.inner_dim + ) + self.caption_projection_2 = PixArtAlphaTextProjection( + in_features=self.caption_channels, hidden_size=self.inner_dim * 2 + ) + self.caption_projection_3 = PixArtAlphaTextProjection( + in_features=self.caption_channels, hidden_size=self.inner_dim * 4 + ) + + def _set_gradient_checkpointing(self, module, value=False): + if hasattr(module, "gradient_checkpointing"): + module.gradient_checkpointing = value + + def forward( + self, + hidden_states: torch.Tensor, + timestep: Optional[torch.LongTensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + added_cond_kwargs: Dict[str, torch.Tensor] = None, + class_labels: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + attention_mask: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + use_image_num: Optional[int] = 0, + return_dict: bool = True, + ): + """ + The [`Transformer2DModel`] forward method. + + Args: + hidden_states (`torch.LongTensor` of shape `(batch size, num latent pixels)` if discrete, `torch.FloatTensor` of shape `(batch size, channel, height, width)` if continuous): + Input `hidden_states`. + encoder_hidden_states ( `torch.FloatTensor` of shape `(batch size, sequence len, embed dims)`, *optional*): + Conditional embeddings for cross attention layer. If not given, cross-attention defaults to + self-attention. + timestep ( `torch.LongTensor`, *optional*): + Used to indicate denoising step. Optional timestep to be applied as an embedding in `AdaLayerNorm`. + class_labels ( `torch.LongTensor` of shape `(batch size, num classes)`, *optional*): + Used to indicate class labels conditioning. Optional class labels to be applied as an embedding in + `AdaLayerZeroNorm`. + cross_attention_kwargs ( `Dict[str, Any]`, *optional*): + A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under + `self.processor` in + [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py). + attention_mask ( `torch.Tensor`, *optional*): + An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask + is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large + negative values to the attention scores corresponding to "discard" tokens. + encoder_attention_mask ( `torch.Tensor`, *optional*): + Cross-attention mask applied to `encoder_hidden_states`. Two formats supported: + + * Mask `(batch, sequence_length)` True = keep, False = discard. + * Bias `(batch, 1, sequence_length)` 0 = keep, -10000 = discard. + + If `ndim == 2`: will be interpreted as a mask, then converted into a bias consistent with the format + above. This bias will be added to the cross-attention scores. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] instead of a plain + tuple. + + Returns: + If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a + `tuple` where the first element is the sample tensor. + """ + batch_size, c, frame, height, width = hidden_states.shape + frame = frame - use_image_num # 21-4=17 + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + print.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. + # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. + # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. + # expects mask of shape: + # [batch, key_tokens] + # adds singleton query_tokens dimension: + # [batch, 1, key_tokens] + # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: + # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) + # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + + if attention_mask is not None and attention_mask.ndim == 4: + # assume that mask is expressed as: + # (1 = keep, 0 = discard) + # convert mask into a bias that can be added to attention scores: + # (keep = +0, discard = -10000.0) + # b, frame+use_image_num, h, w -> a video with images + # b, 1, h, w -> only images + pad_h_0, pad_w_0 = height % (self.config.patch_size * 2), width % (self.config.patch_size * 2) + + hidden_states = F.pad(hidden_states, (0, pad_w_0, 0, pad_h_0, 0, 0), mode='reflect') + attention_mask = attention_mask.to(self.dtype) + attention_mask = attention_mask.unsqueeze(1) # b 1 t h w + attention_mask = F.pad(attention_mask, (0, pad_w_0, 0, pad_h_0, 0, 0)) + attention_mask = F.max_pool3d(attention_mask, kernel_size=(self.config.patch_size_t, self.config.patch_size, self.config.patch_size), + stride=(self.config.patch_size_t, self.config.patch_size, self.config.patch_size)) + attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') + + attention_bias = (1 - attention_mask.bool().to(self.dtype)) * -10000.0 + + # convert encoder_attention_mask to a bias the same way we do for attention_mask + if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: + # b, 1, l -> only video + encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 + + + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-1]) + + # 1. Input + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + hidden_states, encoder_hidden_states_1, encoder_hidden_states_2, encoder_hidden_states_3, \ + timestep_1, timestep_2, timestep_3, \ + embedded_timestep_1, embedded_timestep_2, embedded_timestep_3 = self._operate_on_patched_inputs( + hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num + ) + frame, height, width = frame // self.config.patch_size_t, \ + (height + pad_h_0) // (self.config.patch_size), (width + pad_w_0) // (self.config.patch_size) + + def create_custom_forward(module, return_dict=None): + def custom_forward(*inputs): + if return_dict is not None: + return module(*inputs, return_dict=return_dict) + else: + return module(*inputs) + + return custom_forward + + # encoder_1 + out_enc_level1 = hidden_states + # import ipdb;ipdb.set_trace() + out_enc_level1_skips = [] + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + for block in self.encoder_level_1: + out_enc_level1 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_enc_level1, + attention_bias, + encoder_hidden_states_1, + encoder_attention_mask, + timestep_1, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + out_enc_level1_skips.append(out_enc_level1) + else: + for block in self.encoder_level_1: + out_enc_level1 = block( + out_enc_level1, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_1, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_1, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + out_enc_level1_skips.append(out_enc_level1) + pad_h_1, pad_w_1 = height % 4, width % 4 + + inp_enc_level2, attention_bias, attention_mask = self.down1_2(out_enc_level1, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) + frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_1) // 2, (width + pad_w_1) // 2 + + # encoder_2 + out_enc_level2 = inp_enc_level2 + + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + out_enc_level2_skips = [] + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for block in self.encoder_level_2: + out_enc_level2 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_enc_level2, + attention_bias, + encoder_hidden_states_2, + encoder_attention_mask, + timestep_2, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + out_enc_level2_skips.append(out_enc_level2) + + else: + for block in self.encoder_level_2: + out_enc_level2 = block( + out_enc_level2, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_2, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_2, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + out_enc_level2_skips.append(out_enc_level2) + pad_h_2, pad_w_2 = height % 4, width % 4 + + # import ipdb;ipdb.set_trace() + inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) + frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + + # latent + latent = inp_enc_level3 + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for block in self.latent: + latent = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + latent, + attention_bias, + encoder_hidden_states_3, + encoder_attention_mask, + timestep_3, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for block in self.latent: + latent = block( + latent, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_3, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_3, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + # decoder_2 + + # import ipdb;ipdb.set_trace() + inp_dec_level2, attention_bias, attention_mask = self.up3_2(latent, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) + frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_2, width * 2 - pad_w_2 + # inp_dec_level2 = torch.cat([inp_dec_level2, out_enc_level2], 2) + # inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) + out_dec_level2 = inp_dec_level2 + + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for idx, block in enumerate(self.decoder_level_2): + inp_dec_level2 = out_enc_level2_skips.pop() + inp_dec_level2 = torch.cat([inp_dec_level2, out_dec_level2], 2) + inp_dec_level2 = self.reduce_chan_level2_norm[idx](inp_dec_level2) + inp_dec_level2 = self.reduce_chan_level2[idx](inp_dec_level2) + out_dec_level2 = inp_dec_level2 + out_dec_level2 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_dec_level2, + attention_bias, + encoder_hidden_states_2, + encoder_attention_mask, + timestep_2, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for idx, block in enumerate(self.decoder_level_2): + inp_dec_level2 = out_enc_level2_skips.pop() + inp_dec_level2 = torch.cat([inp_dec_level2, out_dec_level2], 2) + inp_dec_level2 = self.reduce_chan_level2_norm[idx](inp_dec_level2) + inp_dec_level2 = self.reduce_chan_level2[idx](inp_dec_level2) + out_dec_level2 = inp_dec_level2 + out_dec_level2 = block( + out_dec_level2, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_2, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_2, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + + # decoder_1 + + # import ipdb;ipdb.set_trace() + inp_dec_level1, attention_bias, attention_mask = self.up2_1(out_dec_level2, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) + frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_1, width * 2 - pad_w_1 + # inp_dec_level1 = torch.cat([inp_dec_level1, out_enc_level1], 2) + # inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) + out_dec_level1 = inp_dec_level1 + + if npu_config is not None and attention_mask is not None: + attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) + + if self.training and self.gradient_checkpointing: + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + + for idx, block in enumerate(self.decoder_level_1): + inp_dec_level1 = out_enc_level1_skips.pop() + inp_dec_level1 = torch.cat([inp_dec_level1, out_dec_level1], 2) + inp_dec_level1 = self.reduce_chan_level1_norm[idx](inp_dec_level1) + inp_dec_level1 = self.reduce_chan_level1[idx](inp_dec_level1) + out_dec_level1 = inp_dec_level1 + out_dec_level1 = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + out_dec_level1, + attention_bias, + encoder_hidden_states_2, + encoder_attention_mask, + timestep_2, + cross_attention_kwargs, + class_labels, + **ckpt_kwargs, + ) + else: + for idx, block in enumerate(self.decoder_level_1): + inp_dec_level1 = out_enc_level1_skips.pop() + inp_dec_level1 = torch.cat([inp_dec_level1, out_dec_level1], 2) + inp_dec_level1 = self.reduce_chan_level1_norm[idx](inp_dec_level1) + inp_dec_level1 = self.reduce_chan_level1[idx](inp_dec_level1) + out_dec_level1 = inp_dec_level1 + out_dec_level1 = block( + out_dec_level1, + attention_mask=attention_bias, + encoder_hidden_states=encoder_hidden_states_2, + encoder_attention_mask=encoder_attention_mask, + timestep=timestep_2, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + ) + + # import ipdb;ipdb.set_trace() + # 3. Output + output = self._get_output_for_patched_inputs( + hidden_states=out_dec_level1, + timestep=timestep_2, + class_labels=class_labels, + embedded_timestep=embedded_timestep_2, + num_frames=frame, + height=height, + width=width, + ) # b c t h w + + frame, height, width = frame * self.config.patch_size_t, height * self.config.patch_size - pad_h_0, width * self.config.patch_size - pad_w_0 + output = output[:, :, :frame, :height, :width] + if not return_dict: + return (output,) + + return Transformer2DModelOutput(sample=output) + + + def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): + # batch_size = hidden_states.shape[0] + hidden_states = self.pos_embed(hidden_states.to(self.dtype), frame) + + if self.use_additional_conditions and added_cond_kwargs is None: + raise ValueError( + "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." + ) + timestep_1, embedded_timestep_1 = self.adaln_single_1( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + timestep_2, embedded_timestep_2 = self.adaln_single_2( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + timestep_3, embedded_timestep_3 = self.adaln_single_3( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + + encoder_hidden_states_1 = self.caption_projection_1(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + encoder_hidden_states_1 = rearrange(encoder_hidden_states_1[:, :1], 'b 1 l d -> (b 1) l d') + encoder_hidden_states_2 = self.caption_projection_2(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + encoder_hidden_states_2 = rearrange(encoder_hidden_states_2[:, :1], 'b 1 l d -> (b 1) l d') + encoder_hidden_states_3 = self.caption_projection_3(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + encoder_hidden_states_3 = rearrange(encoder_hidden_states_3[:, :1], 'b 1 l d -> (b 1) l d') + + + return hidden_states, encoder_hidden_states_1, encoder_hidden_states_2, encoder_hidden_states_3, \ + timestep_1, timestep_2, timestep_3, embedded_timestep_1, embedded_timestep_2, embedded_timestep_3 + + + + def _get_output_for_patched_inputs( + self, hidden_states, timestep, class_labels, embedded_timestep, num_frames, height=None, width=None + ): + if self.config.norm_type != "ada_norm_single": + conditioning = self.transformer_blocks[0].norm1.emb( + timestep, class_labels, hidden_dtype=self.dtype + ) + shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] + hidden_states = self.proj_out_2(hidden_states) + elif self.config.norm_type == "ada_norm_single": + shift, scale = (self.scale_shift_table[None] + embedded_timestep[:, None]).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) + # Modulation + hidden_states = hidden_states * (1 + scale) + shift + hidden_states = self.proj_out(hidden_states) + hidden_states = hidden_states.squeeze(1) + + # # unpatchify + # hidden_states = hidden_states.reshape( + # shape=(-1, num_frames, height, width, self.out_channels) + # ) + # output = torch.einsum("nthwc->ncthw", hidden_states) + # return output + # unpatchify + hidden_states = hidden_states.reshape( + shape=(-1, num_frames, height, width, self.config.patch_size_t, self.config.patch_size, self.config.patch_size, self.config.out_channels) + ) + hidden_states = torch.einsum("nthwopqc->nctohpwq", hidden_states) + output = hidden_states.reshape( + shape=(-1, self.config.out_channels, num_frames * self.config.patch_size_t, height * self.config.patch_size, width * self.config.patch_size) + ) + # import ipdb;ipdb.set_trace() + # if output.shape[2] % 2 == 0: + # output = output[:, :, 1:] + return output + +def UDiTUltraT2V_S_111(**kwargs): + return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +def UDiTUltraT2V_B_111(**kwargs): + return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +def UDiTUltraT2V_L_111(**kwargs): + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +def UDiTUltraT2V_L_211(**kwargs): + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +def UDiTUltraT2V_L_122(**kwargs): + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +def UDiTUltraT2V_L_222(**kwargs): + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +def UDiTUltraT2V_XL_111(**kwargs): + return UDiTUltraT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +def UDiTUltraT2V_XXL_111(**kwargs): + return UDiTUltraT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + +UDiT_Ultra_models = { + "UDiTUltraT2V-S/111": UDiTUltraT2V_S_111, # 0.19B 0.3B if video + "UDiTUltraT2V-B/111": UDiTUltraT2V_B_111, # 0.73B 1.2B if video + "UDiTUltraT2V-L/111": UDiTUltraT2V_L_111, # 2.3B 3.4B if video + "UDiTUltraT2V-L/211": UDiTUltraT2V_L_211, # 2.3B 3.4B if video + "UDiTUltraT2V-L/122": UDiTUltraT2V_L_122, # 2.3B 3.4B if video + "UDiTUltraT2V-L/222": UDiTUltraT2V_L_222, # 2.3B 3.4B if video + "UDiTUltraT2V-XL/111": UDiTUltraT2V_XL_111, # 5.1B 7B if video + "UDiTUltraT2V-XXL/111": UDiTUltraT2V_XXL_111, # 9.4B 11.3B if video +} + +UDiT_Ultra_models_class = { + "UDiTUltraT2V-S/111": UDiTUltraT2V, + "UDiTUltraT2V-B/111": UDiTUltraT2V, + "UDiTUltraT2V-L/111": UDiTUltraT2V, + "UDiTUltraT2V-L/211": UDiTUltraT2V, + "UDiTUltraT2V-L/122": UDiTUltraT2V, + "UDiTUltraT2V-L/222": UDiTUltraT2V, + "UDiTUltraT2V-XL/111": UDiTUltraT2V, + "UDiTUltraT2V-XXL/111": UDiTUltraT2V, +} + +if __name__ == '__main__': + import sys + from opensora.models.ae import ae_channel_config, ae_stride_config + from opensora.models.ae import getae, getae_wrapper + from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper + + args = type('args', (), + { + 'ae': 'CausalVAEModel_4x8x8', + 'attention_mode': 'xformers', + 'use_rope': False, + 'model_max_length': 300, + 'max_height': 480, + 'max_width': 640, + 'num_frames': 125, + 'use_image_num': 0, + 'compress_kv_factor': 1 + } + ) + b = 2 + c = 4 + cond_c = 4096 + num_timesteps = 1000 + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: + num_frames = (args.num_frames - 1) // ae_stride_t + 1 + else: + num_frames = args.num_frames // ae_stride_t + + device = torch.device('cuda:1') + model = UDiTUltraT2V_L_122(in_channels=c, + out_channels=c, + sample_size=latent_size, + sample_size_t=num_frames, + activation_fn="gelu-approximate", + attention_bias=True, + attention_type="default", + double_self_attention=False, + norm_elementwise_affine=False, + norm_eps=1e-06, + norm_num_groups=32, + num_vector_embeds=None, + only_cross_attention=False, + upcast_attention=False, + use_linear_projection=False, + use_additional_conditions=False, + downsampler='k333_s222').to(device) + + print(model) + print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') + try: + path = "/storage/ongoing/new/Open-Sora-Plan/bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22/checkpoint-50/model_ema/diffusion_pytorch_model.safetensors" + from safetensors.torch import load_file as safe_load + ckpt = safe_load(path, device="cpu") + new_ckpt = {} + k_size = 3 + for k, v in ckpt.items(): + if 'pos_embed.proj.weight' in k: + new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + elif 'attn1.downsampler.layer.weight' in k: + new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + elif 'body.0.weight' in k and 'down' in k: + in_c = v.shape[0] + new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 + elif 'body.0.weight' in k and 'up' in k: + new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 + else: + new_v = v + new_ckpt[k] = new_v + msg = model.load_state_dict(new_ckpt, strict=False) + print(msg) + except Exception as e: + print(e) + # import sys;sys.exit() + x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) + cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) + attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L + cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L + timestep = torch.randint(0, 1000, (b,), device=device) + model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) + with torch.no_grad(): + output = model(**model_kwargs) + print(output[0].shape) + + + + diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py new file mode 100644 index 000000000..9d2e20242 --- /dev/null +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -0,0 +1,1107 @@ +from einops import rearrange +from torch import nn +import torch +import numpy as np + +from einops import rearrange, repeat +from typing import Any, Dict, Optional, Tuple +from diffusers.utils.torch_utils import maybe_allow_in_graph +from typing import Any, Dict, Optional + +import torch +import torch.nn.functional as F +from torch import nn +import diffusers +from diffusers.utils import deprecate, logging +from diffusers.utils.torch_utils import maybe_allow_in_graph +from diffusers.models.attention import GatedSelfAttentionDense +from diffusers.models.attention_processor import Attention as Attention_ +from diffusers.models.embeddings import SinusoidalPositionalEmbedding +from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm +import re +try: + import torch_npu + from opensora.npu_config import npu_config, set_run_dtype +except: + torch_npu = None + npu_config = None + set_run_dtype = None +logger = logging.get_logger(__name__) + +def get_3d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid height and width return: pos_embed: [grid_size*grid_size, embed_dim] or + [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + grid_t = np.arange(grid_size[0], dtype=np.float32) / (grid_size[0] / base_size[0]) / interpolation_scale[0] + grid_h = np.arange(grid_size[1], dtype=np.float32) / (grid_size[1] / base_size[1]) / interpolation_scale[1] + grid_w = np.arange(grid_size[2], dtype=np.float32) / (grid_size[2] / base_size[2]) / interpolation_scale[2] + grid = np.meshgrid(grid_w, grid_h, grid_t) # here w goes first + grid = np.stack(grid, axis=0) + + grid = grid.reshape([3, 1, grid_size[2], grid_size[1], grid_size[0]]) + pos_embed = get_3d_sincos_pos_embed_from_grid(embed_dim, grid) + + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + + +def get_3d_sincos_pos_embed_from_grid(embed_dim, grid): + if embed_dim % 3 != 0: + raise ValueError("embed_dim must be divisible by 3") + + # use 1/3 of dimensions to encode grid_t/h/w + emb_t = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[0]) # (T*H*W, D/3) + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[1]) # (T*H*W, D/3) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 3, grid[2]) # (T*H*W, D/3) + + emb = np.concatenate([emb_t, emb_h, emb_w], axis=1) # (T*H*W, D) + return emb + + +def get_2d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid height and width return: pos_embed: [grid_size*grid_size, embed_dim] or + [1+grid_size*grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid_h = np.arange(grid_size[0], dtype=np.float32) / (grid_size[0] / base_size[0]) / interpolation_scale[0] + grid_w = np.arange(grid_size[1], dtype=np.float32) / (grid_size[1] / base_size[1]) / interpolation_scale[1] + grid = np.meshgrid(grid_w, grid_h) # here w goes first + grid = np.stack(grid, axis=0) + + grid = grid.reshape([2, 1, grid_size[1], grid_size[0]]) + pos_embed = get_2d_sincos_pos_embed_from_grid(embed_dim, grid) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + + +def get_2d_sincos_pos_embed_from_grid(embed_dim, grid): + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + # use 1/3 of dimensions to encode grid_t/h/w + emb_h = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[0]) # (H*W, D/2) + emb_w = get_1d_sincos_pos_embed_from_grid(embed_dim // 2, grid[1]) # (H*W, D/2) + + emb = np.concatenate([emb_h, emb_w], axis=1) # (H*W, D) + return emb + +def get_1d_sincos_pos_embed( + embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, +): + """ + grid_size: int of the grid return: pos_embed: [grid_size, embed_dim] or + [1+grid_size, embed_dim] (w/ or w/o cls_token) + """ + # if isinstance(grid_size, int): + # grid_size = (grid_size, grid_size) + + grid = np.arange(grid_size, dtype=np.float32) / (grid_size / base_size) / interpolation_scale + pos_embed = get_1d_sincos_pos_embed_from_grid(embed_dim, grid) # (H*W, D/2) + if cls_token and extra_tokens > 0: + pos_embed = np.concatenate([np.zeros([extra_tokens, embed_dim]), pos_embed], axis=0) + return pos_embed + +def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): + """ + embed_dim: output dimension for each position pos: a list of positions to be encoded: size (M,) out: (M, D) + """ + if embed_dim % 2 != 0: + raise ValueError("embed_dim must be divisible by 2") + + omega = np.arange(embed_dim // 2, dtype=np.float64) + omega /= embed_dim / 2.0 + omega = 1.0 / 10000**omega # (D/2,) + + pos = pos.reshape(-1) # (M,) + out = np.einsum("m,d->md", pos, omega) # (M, D/2), outer product + + emb_sin = np.sin(out) # (M, D/2) + emb_cos = np.cos(out) # (M, D/2) + + emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) + return emb + + + + +class OverlapPatchEmbed3D(nn.Module): + """2D Image to Patch Embedding but with 3D position embedding""" + + def __init__( + self, + num_frames=1, + height=224, + width=224, + patch_size_t=1, + patch_size=16, + in_channels=3, + embed_dim=768, + layer_norm=False, + flatten=True, + bias=True, + interpolation_scale=(1, 1), + interpolation_scale_t=1, + ): + super().__init__() + # assert patch_size_t == 1 and patch_size == 1 + self.flatten = flatten + self.layer_norm = layer_norm + self.proj = nn.Conv3d( + in_channels, embed_dim, kernel_size=3, padding=1, stride=(patch_size_t, patch_size, patch_size), bias=bias + ) + if layer_norm: + self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) + else: + self.norm = None + + self.patch_size_t = patch_size_t + self.patch_size = patch_size + # See: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 + self.height, self.width = height // patch_size, width // patch_size + self.base_size = (height // patch_size, width // patch_size) + self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + pos_embed = get_2d_sincos_pos_embed( + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) + + self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.interpolation_scale_t = interpolation_scale_t + temp_pos_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) + # self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) + + def forward(self, latent, num_frames): + b, _, _, _, _ = latent.shape + video_latent, image_latent = None, None + # b c 1 h w + # assert latent.shape[-3] == 1 and num_frames == 1 + num_frames = latent.shape[-3] // self.patch_size_t + height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size + # latent = rearrange(latent, 'b c t h w -> (b t) c h w') + latent = self.proj(latent) + + if self.flatten: + # latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C + latent = rearrange(latent, 'b c t h w -> (b t) (h w) c ') + if self.layer_norm: + latent = self.norm(latent) + + # import ipdb;ipdb.set_trace() + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed + + + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed + + latent = (latent + pos_embed).to(latent.dtype) + + latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + video_latent = latent + + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) + + video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') + return video_latent + + + +class OverlapPatchEmbed2D(nn.Module): + """2D Image to Patch Embedding but with 3D position embedding""" + + def __init__( + self, + num_frames=1, + height=224, + width=224, + patch_size_t=1, + patch_size=16, + in_channels=3, + embed_dim=768, + layer_norm=False, + flatten=True, + bias=True, + interpolation_scale=(1, 1), + interpolation_scale_t=1, + ): + super().__init__() + assert patch_size_t == 1 + self.flatten = flatten + self.layer_norm = layer_norm + + self.proj = nn.Conv2d( + in_channels, embed_dim, kernel_size=3, padding=1, stride=(patch_size, patch_size), bias=bias + ) + if layer_norm: + self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) + else: + self.norm = None + + self.patch_size_t = patch_size_t + self.patch_size = patch_size + # See: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L161 + + self.height, self.width = height // patch_size, width // patch_size + self.base_size = (height // patch_size, width // patch_size) + self.interpolation_scale = (interpolation_scale[0], interpolation_scale[1]) + pos_embed = get_2d_sincos_pos_embed( + embed_dim, (self.height, self.width), base_size=self.base_size, interpolation_scale=self.interpolation_scale + ) + self.register_buffer("pos_embed", torch.from_numpy(pos_embed).float().unsqueeze(0), persistent=False) + + self.num_frames = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.base_size_t = (num_frames - 1) // patch_size_t + 1 if num_frames % 2 == 1 else num_frames // patch_size_t + self.interpolation_scale_t = interpolation_scale_t + temp_pos_embed = get_1d_sincos_pos_embed(embed_dim, self.num_frames, base_size=self.base_size_t, interpolation_scale=self.interpolation_scale_t) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) + # self.temp_embed_gate = nn.Parameter(torch.tensor([0.0])) + + def forward(self, latent, num_frames): + b, _, _, _, _ = latent.shape + video_latent, image_latent = None, None + # b c 1 h w + # assert latent.shape[-3] == 1 and num_frames == 1 + num_frames = latent.shape[-3] // self.patch_size_t + height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size + latent = rearrange(latent, 'b c t h w -> (b t) c h w') + latent = self.proj(latent) + + if self.flatten: + latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C + if self.layer_norm: + latent = self.norm(latent) + + # import ipdb;ipdb.set_trace() + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed + + + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed + + latent = (latent + pos_embed).to(latent.dtype) + + latent = rearrange(latent, '(b t) n c -> b t n c', b=b) + image_latent = latent + + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + # temp_pos_embed = temp_pos_embed.unsqueeze(2) + # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + + image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None + + return image_latent + +class Attention(Attention_): + def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attention_mode='xformers', **kwags): + processor = AttnProcessor2_0(attention_mode='xformers') + super().__init__(processor=processor, **kwags) + self.downsampler = None + if downsampler: # downsampler k155_s122 + downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 + down_factor = list(re.search(r's(\d{2,3})', downsampler).group(1)) + downsampler_ker_size = [int(i) for i in downsampler_ker_size] + downsampler_padding = [(i - 1) // 2 for i in downsampler_ker_size] + down_factor = [int(i) for i in down_factor] + + if len(downsampler_ker_size) == 2: + self.downsampler = DownSampler2d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + down_shortcut=True, num_frames=num_frames, height=height, width=width) + elif len(downsampler_ker_size) == 3: + self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + down_shortcut=True, num_frames=num_frames, height=height, width=width) + + self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + + +class DownSampler3d(nn.Module): + def __init__(self, *args, **kwargs): + ''' Required kwargs: down_factor, downsampler''' + super().__init__() + self.down_factor = kwargs.pop('down_factor') + self.down_shortcut = kwargs.pop('down_shortcut') + self.t = kwargs.pop('num_frames') + self.h = kwargs.pop('height') + self.w = kwargs.pop('width') + self.layer = nn.Conv3d(*args, **kwargs) + + def forward(self, x, attention_mask): + b = x.shape[0] + x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) + if npu_config is None: + x = self.layer(x) + (x if self.down_shortcut else 0) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) (t h w) d', + t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=self.t, h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (t dt) (h dh) (w dw) -> (b dt dh dw) 1 (t h w)', + t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + return x, attention_mask + def reverse(self, x): + # import ipdb;ipdb.set_trace() + x = rearrange(x, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', + t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) + return x + + +class DownSampler2d(nn.Module): + def __init__(self, *args, **kwargs): + ''' Required kwargs: down_factor, downsampler''' + super().__init__() + self.down_factor = kwargs.pop('down_factor') + self.down_shortcut = kwargs.pop('down_shortcut') + self.t = kwargs.pop('num_frames') + self.h = kwargs.pop('height') + self.w = kwargs.pop('width') + self.layer = nn.Conv2d(*args, **kwargs) + + def forward(self, x, attention_mask): + # import ipdb;ipdb.set_trace() + b = x.shape[0] + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) + if npu_config is None: + x = self.layer(x) + (x if self.down_shortcut else 0) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', + h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (h dh) (w dw) -> (b dh dw) 1 (h w)', + h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + return x, attention_mask + def reverse(self, x): + # import ipdb;ipdb.set_trace() + x = rearrange(x, '(b t dh dw) (h w) d -> b (t h dh w dw) d', + t=self.t, h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + dh=self.down_factor[0], dw=self.down_factor[1]) + return x + + +class AttnProcessor2_0: + r""" + Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). + """ + + def __init__(self, attention_mode='xformers'): + self.attention_mode = attention_mode + if not hasattr(F, "scaled_dot_product_attention"): + raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + def __call__( + self, + attn: Attention, + hidden_states: torch.FloatTensor, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + temb: Optional[torch.FloatTensor] = None, + *args, + **kwargs, + ) -> torch.FloatTensor: + if len(args) > 0 or kwargs.get("scale", None) is not None: + deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." + deprecate("scale", "1.0.0", deprecation_message) + + if attn.downsampler is not None: + hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask) + + residual = hidden_states + if attn.spatial_norm is not None: + hidden_states = attn.spatial_norm(hidden_states, temb) + + input_ndim = hidden_states.ndim + + # if input_ndim == 4: + # batch_size, channel, height, width = hidden_states.shape + # hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) + + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + + if attention_mask is not None: + if npu_config is None: + attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + else: + attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) + + if attn.group_norm is not None: + hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) + + query = attn.to_q(hidden_states) + + if encoder_hidden_states is None: + encoder_hidden_states = hidden_states + elif attn.norm_cross: + encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + + key = attn.to_k(encoder_hidden_states) + value = attn.to_v(encoder_hidden_states) + + inner_dim = key.shape[-1] + head_dim = inner_dim // attn.heads + + if npu_config is not None and npu_config.on_npu: + if npu_config.enable_FA and query.dtype == torch.float32: + dtype = torch.bfloat16 + else: + dtype = None + + with set_run_dtype(query, dtype): + query, key, value = npu_config.set_current_run_dtype([query, key, value]) + hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", + head_dim, attn.heads) + + hidden_states = npu_config.restore_dtype(hidden_states) + else: + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + # qk norm + query = attn.q_norm(query) + key = attn.k_norm(key) + query = query * (head_dim ** -0.5) + + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + attention_mask = None + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + + if self.attention_mode == 'flash': + assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + + # linear proj + hidden_states = attn.to_out[0](hidden_states) + # dropout + hidden_states = attn.to_out[1](hidden_states) + + if input_ndim == 4: + hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) + + if attn.residual_connection: + hidden_states = hidden_states + residual + + hidden_states = hidden_states / attn.rescale_output_factor + + if attn.downsampler is not None: + hidden_states = attn.downsampler.reverse(hidden_states) + + return hidden_states + + +class PixelUnshuffle(nn.Module): + def __init__(self, ratio, ratio_t=None): + super().__init__() + self.r = ratio + self.r_t = ratio_t if ratio_t else ratio + + def forward(self, x): + if self.r_t is not None and self.r_t != 1: + b, c, t, h, w = x.shape + assert t % self.r_t == 0 and h % self.r == 0 and w % self.r == 0 + if self.r > 1: + x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r_t, r2=self.r, r3=self.r) + else: + b, c, h, w = x.shape + assert h % self.r == 0 and w % self.r == 0 + if self.r > 1: + x = rearrange(x, 'b c (h r2) (w r3) -> b (c r2 r3) h w', r2=self.r, r3=self.r) + return x + +class PixelShuffle(nn.Module): + def __init__(self, ratio, ratio_t=None): + super().__init__() + self.r = ratio + self.r_t = ratio_t if ratio_t else ratio + + def forward(self, x): + if self.r_t is not None and self.r_t != 1: + b, c, t, h, w = x.shape + assert c % (self.r_t*self.r*self.r) == 0 + if self.r > 1: + x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r_t, r2=self.r, r3=self.r) + else: + b, c, h, w = x.shape + assert c % (self.r*self.r) == 0 + if self.r > 1: + x = rearrange(x, 'b (c r2 r3) h w -> b c (h r2) (w r3)', r2=self.r, r3=self.r) + return x + +class Downsample3d(nn.Module): + def __init__(self, n_feat): + super(Downsample3d, self).__init__() + + self.body = nn.Sequential(nn.Conv3d(n_feat, n_feat // 4, kernel_size=3, stride=1, padding=1, bias=False), + PixelUnshuffle(2, 2)) + + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) + x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0), mode='reflect') + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.body, x, x_dtype) + x = rearrange(x, 'b d t h w -> b (t h w) d') + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=frames, h=height, w=width) + attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h, 0, 0)) + attention_mask = F.max_pool3d(attention_mask, kernel_size=2, stride=2) + attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + return x, attention_bias, attention_mask + +class Upsample3d(nn.Module): + def __init__(self, n_feat): + super(Upsample3d, self).__init__() + + self.body = nn.Sequential(nn.Conv3d(n_feat, n_feat * 4, kernel_size=3, stride=1, padding=1, bias=False), + PixelShuffle(2, 2)) + + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv3d(self.body, x, x_dtype) + x = x[:, :, :, :height*2-pad_h, :width*2-pad_w] + x = rearrange(x, 'b d t h w -> b (t h w) d') + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=frames, h=height, w=width) + attention_mask = attention_mask.repeat_interleave(2, -1).repeat_interleave(2, -2).repeat_interleave(2, -3) + attention_mask = attention_mask[:, :, :, :height*2-pad_h, :width*2-pad_w] + attention_mask = rearrange(attention_mask, 'b 1 t h w -> b 1 (t h w)') + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + return x, attention_bias, attention_mask + + +class Downsample2d(nn.Module): + def __init__(self, n_feat): + super(Downsample2d, self).__init__() + + self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), + PixelUnshuffle(2, 1)) + + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.body, x, x_dtype) + x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) + attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) + attention_mask = F.max_pool2d(attention_mask, kernel_size=2, stride=2) + attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + return x, attention_bias, attention_mask + +class Upsample2d(nn.Module): + def __init__(self, n_feat): + super(Upsample2d, self).__init__() + + self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), + PixelShuffle(2, 1)) + + def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + if npu_config is None: + x = self.body(x) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.body, x, x_dtype) + x = x[:, :, :height*2-pad_h, :width*2-pad_w] + x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) + attention_mask = attention_mask.repeat_interleave(2, -1).repeat_interleave(2, -2) + attention_mask = attention_mask[:, :, :height*2-pad_h, :width*2-pad_w] + attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) + attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + + return x, attention_bias, attention_mask + + +class FeedForward_Conv2d(nn.Module): + def __init__(self, downsampler, dim, hidden_features, bias=True, num_frames=8, height=16, width=16): + super(FeedForward_Conv2d, self).__init__() + + self.t = num_frames + self.h = height + self.w = width + self.bias = bias + + self.project_in = nn.Linear(dim, hidden_features, bias=bias) + + self.dwconv = nn.ModuleList([ + nn.Conv2d(hidden_features, hidden_features, kernel_size=(5, 5), stride=1, padding=(2, 2), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv2d(hidden_features, hidden_features, kernel_size=(3, 3), stride=1, padding=(1, 1), dilation=1, + groups=hidden_features, bias=bias), + nn.Conv2d(hidden_features, hidden_features, kernel_size=(1, 1), stride=1, padding=(0, 0), dilation=1, + groups=hidden_features, bias=bias) + ]) + + self.project_out = nn.Linear(hidden_features, dim, bias=bias) + + + def forward(self, x): + # import ipdb;ipdb.set_trace() + if npu_config is None: + x = self.project_in(x) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + module(x) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + x = self.project_out(out) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.project_in, x, torch.float16) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + npu_config.run_conv2d(module, x, torch.float16) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + x = npu_config.run_conv2d(self.project_out, out, x_dtype) + return x + +@maybe_allow_in_graph +class BasicTransformerBlock(nn.Module): + r""" + A basic Transformer block. + + Parameters: + dim (`int`): The number of channels in the input and output. + num_attention_heads (`int`): The number of heads to use for multi-head attention. + attention_head_dim (`int`): The number of channels in each head. + dropout (`float`, *optional*, defaults to 0.0): The dropout probability to use. + cross_attention_dim (`int`, *optional*): The size of the encoder_hidden_states vector for cross attention. + activation_fn (`str`, *optional*, defaults to `"geglu"`): Activation function to be used in feed-forward. + num_embeds_ada_norm (: + obj: `int`, *optional*): The number of diffusion steps used during training. See `Transformer2DModel`. + attention_bias (: + obj: `bool`, *optional*, defaults to `False`): Configure if the attentions should contain a bias parameter. + only_cross_attention (`bool`, *optional*): + Whether to use only cross-attention layers. In this case two cross attention layers are used. + double_self_attention (`bool`, *optional*): + Whether to use two self-attention layers. In this case no cross attention layers are used. + upcast_attention (`bool`, *optional*): + Whether to upcast the attention computation to float32. This is useful for mixed precision training. + norm_elementwise_affine (`bool`, *optional*, defaults to `True`): + Whether to use learnable elementwise affine parameters for normalization. + norm_type (`str`, *optional*, defaults to `"layer_norm"`): + The normalization layer to use. Can be `"layer_norm"`, `"ada_norm"` or `"ada_norm_zero"`. + final_dropout (`bool` *optional*, defaults to False): + Whether to apply a final dropout after the last feed-forward layer. + attention_type (`str`, *optional*, defaults to `"default"`): + The type of attention to use. Can be `"default"` or `"gated"` or `"gated-text-image"`. + positional_embeddings (`str`, *optional*, defaults to `None`): + The type of positional embeddings to apply to. + num_positional_embeddings (`int`, *optional*, defaults to `None`): + The maximum number of positional embeddings to apply. + """ + + def __init__( + self, + dim: int, + num_attention_heads: int, + attention_head_dim: int, + dropout=0.0, + cross_attention_dim: Optional[int] = None, + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + attention_bias: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_elementwise_affine: bool = True, + norm_type: str = "layer_norm", # 'layer_norm', 'ada_norm', 'ada_norm_zero', 'ada_norm_single', 'ada_norm_continuous', 'layer_norm_i2vgen' + norm_eps: float = 1e-5, + final_dropout: bool = False, + attention_type: str = "default", + positional_embeddings: Optional[str] = None, + num_positional_embeddings: Optional[int] = None, + ada_norm_continous_conditioning_embedding_dim: Optional[int] = None, + ada_norm_bias: Optional[int] = None, + ff_inner_dim: Optional[int] = None, + ff_bias: bool = True, + attention_out_bias: bool = True, + attention_mode: str = "xformers", + num_frames: int = 16, + height: int = 32, + width: int = 32, + downsampler: str = None, + mlp_ratio: int = 4, + ): + super().__init__() + self.only_cross_attention = only_cross_attention + + self.t = num_frames + self.h = height + self.w = width + # We keep these boolean flags for backward-compatibility. + self.use_ada_layer_norm_zero = (num_embeds_ada_norm is not None) and norm_type == "ada_norm_zero" + self.use_ada_layer_norm = (num_embeds_ada_norm is not None) and norm_type == "ada_norm" + self.use_ada_layer_norm_single = norm_type == "ada_norm_single" + self.use_layer_norm = norm_type == "layer_norm" + self.use_ada_layer_norm_continuous = norm_type == "ada_norm_continuous" + + if norm_type in ("ada_norm", "ada_norm_zero") and num_embeds_ada_norm is None: + raise ValueError( + f"`norm_type` is set to {norm_type}, but `num_embeds_ada_norm` is not defined. Please make sure to" + f" define `num_embeds_ada_norm` if setting `norm_type` to {norm_type}." + ) + + self.norm_type = norm_type + self.num_embeds_ada_norm = num_embeds_ada_norm + + if positional_embeddings and (num_positional_embeddings is None): + raise ValueError( + "If `positional_embedding` type is defined, `num_positition_embeddings` must also be defined." + ) + + if positional_embeddings == "sinusoidal": + self.pos_embed = SinusoidalPositionalEmbedding(dim, max_seq_length=num_positional_embeddings) + else: + self.pos_embed = None + + # Define 3 blocks. Each block has its own normalization layer. + # 1. Self-Attn + if norm_type == "ada_norm": + self.norm1 = AdaLayerNorm(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_zero": + self.norm1 = AdaLayerNormZero(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_continuous": + self.norm1 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "rms_norm", + ) + else: + self.norm1 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + + self.attn1 = Attention( + query_dim=dim, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + cross_attention_dim=cross_attention_dim if only_cross_attention else None, + upcast_attention=upcast_attention, + out_bias=attention_out_bias, + downsampler=downsampler, + num_frames=num_frames, + height=height, + width=width, + attention_mode=attention_mode, + ) + + # 2. Cross-Attn + if cross_attention_dim is not None or double_self_attention: + # We currently only use AdaLayerNormZero for self attention where there will only be one attention block. + # I.e. the number of returned modulation chunks from AdaLayerZero would not make sense if returned during + # the second cross attention block. + if norm_type == "ada_norm": + self.norm2 = AdaLayerNorm(dim, num_embeds_ada_norm) + elif norm_type == "ada_norm_continuous": + self.norm2 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "rms_norm", + ) + else: + self.norm2 = nn.LayerNorm(dim, norm_eps, norm_elementwise_affine) + + self.attn2 = Attention( + query_dim=dim, + cross_attention_dim=cross_attention_dim if not double_self_attention else None, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + upcast_attention=upcast_attention, + out_bias=attention_out_bias, + downsampler=None, + attention_mode=attention_mode, + ) # is self-attn if encoder_hidden_states is none + else: + self.norm2 = None + self.attn2 = None + + # 3. Feed-forward + if norm_type == "ada_norm_continuous": + self.norm3 = AdaLayerNormContinuous( + dim, + ada_norm_continous_conditioning_embedding_dim, + norm_elementwise_affine, + norm_eps, + ada_norm_bias, + "layer_norm", + ) + + elif norm_type in ["ada_norm_zero", "ada_norm", "layer_norm", "ada_norm_continuous"]: + self.norm3 = nn.LayerNorm(dim, norm_eps, norm_elementwise_affine) + elif norm_type == "layer_norm_i2vgen": + self.norm3 = None + + self.ff = FeedForward_Conv2d( + downsampler, + dim, + hidden_features=mlp_ratio * dim, + num_frames=num_frames, + height=height, + width=width, + ) + + # 4. Fuser + if attention_type == "gated" or attention_type == "gated-text-image": + self.fuser = GatedSelfAttentionDense(dim, cross_attention_dim, num_attention_heads, attention_head_dim) + + # 5. Scale-shift for PixArt-Alpha. + if norm_type == "ada_norm_single": + self.scale_shift_table = nn.Parameter(torch.randn(6, dim) / dim**0.5) + + # let chunk size default to None + self._chunk_size = None + self._chunk_dim = 0 + + def set_chunk_feed_forward(self, chunk_size: Optional[int], dim: int = 0): + # Sets chunk feed-forward + self._chunk_size = chunk_size + self._chunk_dim = dim + + def forward( + self, + hidden_states: torch.FloatTensor, + attention_mask: Optional[torch.FloatTensor] = None, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + encoder_attention_mask: Optional[torch.FloatTensor] = None, + timestep: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + class_labels: Optional[torch.LongTensor] = None, + added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None, + ) -> torch.FloatTensor: + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + logger.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + + # Notice that normalization is always applied before the real computation in the following blocks. + # 0. Self-Attention + batch_size = hidden_states.shape[0] + + if self.norm_type == "ada_norm": + norm_hidden_states = self.norm1(hidden_states, timestep) + elif self.norm_type == "ada_norm_zero": + norm_hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.norm1( + hidden_states, timestep, class_labels, hidden_dtype=hidden_states.dtype + ) + elif self.norm_type in ["layer_norm", "layer_norm_i2vgen"]: + norm_hidden_states = self.norm1(hidden_states) + elif self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm1(hidden_states, added_cond_kwargs["pooled_text_emb"]) + elif self.norm_type == "ada_norm_single": + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( + self.scale_shift_table[None] + timestep.reshape(batch_size, 6, -1) + ).chunk(6, dim=1) + norm_hidden_states = self.norm1(hidden_states) + norm_hidden_states = norm_hidden_states * (1 + scale_msa) + shift_msa + norm_hidden_states = norm_hidden_states.squeeze(1) + else: + raise ValueError("Incorrect norm used") + + if self.pos_embed is not None: + norm_hidden_states = self.pos_embed(norm_hidden_states) + + # 1. Prepare GLIGEN inputs + cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} + gligen_kwargs = cross_attention_kwargs.pop("gligen", None) + + attn_output = self.attn1( + norm_hidden_states, + encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, + attention_mask=attention_mask, + **cross_attention_kwargs, + ) + if self.norm_type == "ada_norm_zero": + attn_output = gate_msa.unsqueeze(1) * attn_output + elif self.norm_type == "ada_norm_single": + attn_output = gate_msa * attn_output + + hidden_states = attn_output + hidden_states + if hidden_states.ndim == 4: + hidden_states = hidden_states.squeeze(1) + + # 1.2 GLIGEN Control + if gligen_kwargs is not None: + hidden_states = self.fuser(hidden_states, gligen_kwargs["objs"]) + + # 3. Cross-Attention + if self.attn2 is not None: + if self.norm_type == "ada_norm": + norm_hidden_states = self.norm2(hidden_states, timestep) + elif self.norm_type in ["ada_norm_zero", "layer_norm", "layer_norm_i2vgen"]: + norm_hidden_states = self.norm2(hidden_states) + elif self.norm_type == "ada_norm_single": + # For PixArt norm2 isn't applied here: + # https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L70C1-L76C103 + norm_hidden_states = hidden_states + elif self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm2(hidden_states, added_cond_kwargs["pooled_text_emb"]) + else: + raise ValueError("Incorrect norm") + + if self.pos_embed is not None and self.norm_type != "ada_norm_single": + norm_hidden_states = self.pos_embed(norm_hidden_states) + + attn_output = self.attn2( + norm_hidden_states, + encoder_hidden_states=encoder_hidden_states, + attention_mask=encoder_attention_mask, + **cross_attention_kwargs, + ) + hidden_states = attn_output + hidden_states + # 4. Feed-forward + # i2vgen doesn't have this norm 🤷‍♂️ + if self.norm_type == "ada_norm_continuous": + norm_hidden_states = self.norm3(hidden_states, added_cond_kwargs["pooled_text_emb"]) + elif not self.norm_type == "ada_norm_single": + norm_hidden_states = self.norm3(hidden_states) + + if self.norm_type == "ada_norm_zero": + norm_hidden_states = norm_hidden_states * (1 + scale_mlp[:, None]) + shift_mlp[:, None] + + if self.norm_type == "ada_norm_single": + norm_hidden_states = self.norm2(hidden_states) + norm_hidden_states = norm_hidden_states * (1 + scale_mlp) + shift_mlp + + if self._chunk_size is not None: + # "feed_forward_chunk_size" can be used to save memory + ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) + else: + ff_output = self.ff(norm_hidden_states) + if self.norm_type == "ada_norm_zero": + ff_output = gate_mlp.unsqueeze(1) * ff_output + elif self.norm_type == "ada_norm_single": + ff_output = gate_mlp * ff_output + + hidden_states = ff_output + hidden_states + if hidden_states.ndim == 4: + hidden_states = hidden_states.squeeze(1) + + return hidden_states diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 1a8232624..14f1cdb1d 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -12,9 +12,12 @@ def __init__(self, args, **kwargs): if 'mt5' in self.model_name: from transformers import MT5EncoderModel self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 53b96ea4d..084e9657c 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -57,8 +57,10 @@ def main(args): transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) - tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + # tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + text_encoder = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = T5Tokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) # set eval mode diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 532c1d651..23fc7dc2d 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -58,6 +58,7 @@ from opensora.models.diffusion import Diffusion_models, Diffusion_models_class from opensora.sample.pipeline_opensora import OpenSoraPipeline + # Will error if the minimal version of diffusers is not installed. Remove at your own risks. check_min_version("0.24.0") logger = get_logger(__name__) @@ -66,14 +67,13 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): validation_prompt = [ - '在游泳馆里充当救生员的戴眼镜的猫', - '在珊瑚礁旁边游过一只海龟', + "a word \"你好\" in the blackboard", "a cat wearing sunglasses and working as a lifeguard at pool.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." ] logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) - scheduler = DDPMScheduler() + scheduler = DPMSolverMultistepScheduler() opensora_pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, @@ -268,10 +268,10 @@ def main(args): from safetensors.torch import load_file as safe_load # import ipdb;ipdb.set_trace() checkpoint = safe_load(args.pretrained, device="cpu") - if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: - logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") - repeat = model.pos_embed.proj.weight.shape[2] - checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] else: # latest stage training weight checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] @@ -289,8 +289,8 @@ def main(args): noise_scheduler = DDPMScheduler() # Move unet, vae and text_encoder to device and cast to weight_dtype # The VAE is in float32 to avoid NaN losses. - # ae.to(accelerator.device, dtype=torch.float32) - ae.vae.to(accelerator.device, dtype=weight_dtype) + ae.vae.to(accelerator.device, dtype=torch.float32) + # ae.vae.to(accelerator.device, dtype=weight_dtype) text_enc.to(accelerator.device, dtype=weight_dtype) # Create EMA for the unet. @@ -410,10 +410,11 @@ def load_model_hook(models, input_dir): train_dataloader = torch.utils.data.DataLoader( train_dataset, shuffle=True, - pin_memory=True, + # pin_memory=True, collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, + prefetch_factor=8 ) # Scheduler and math around the number of training steps. @@ -565,6 +566,7 @@ def run(model_input, model_kwargs, prof): # Add noise to the model input according to the noise magnitude at each timestep # (this is the forward diffusion process) + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) model_pred = model( @@ -665,7 +667,9 @@ def train_one_step(step_, data_item_, prof_=None): if not args.multi_scale: assert torch.all(attn_mask) - x = x.to(accelerator.device, dtype=weight_dtype) # B C T+num_images H W, 16 + 4 + assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+num_images H W, 16 + 4 + attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W input_ids = input_ids.to(accelerator.device) # B 1+num_images L cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L @@ -695,6 +699,8 @@ def train_one_step(step_, data_item_, prof_=None): x = torch.cat([videos, images], dim=2) # b c 17+4, h, w with accelerator.accumulate(model): + assert not torch.any(torch.isnan(x)), 'after vae' + x = x.to(weight_dtype) model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) run(x, model_kwargs, prof_) @@ -711,7 +717,6 @@ def train_all_epoch(prof_=None): return True for step, data_item in enumerate(train_dataloader): - if train_one_step(step, data_item, prof_): break diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index ffc34c997..07a0a16f6 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -108,6 +108,8 @@ def __call__(self, batch): pad_batch_tubes = rearrange(pad_batch_tubes_img, '(b i) c 1 h w -> b c i h w', i=1) attention_mask = rearrange(attention_mask_img, '(b i) 1 h w -> b i h w', i=1) input_ids, cond_mask = input_ids_img, cond_mask_img # b 1 l + + assert not torch.any(torch.isnan(pad_batch_tubes)), 'after pad_batch_tubes' return pad_batch_tubes, attention_mask, input_ids, cond_mask def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, patch_size_thw, extra_1): diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index 832256920..2645efa58 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,4 +1,4 @@ node030 slots=8 -node012 slots=8 node033 slots=8 -node034 slots=8 \ No newline at end of file +node034 slots=8 +node012 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/zero2.json b/scripts/accelerate_configs/zero2.json index bc26a0a5f..8dcdd154d 100644 --- a/scripts/accelerate_configs/zero2.json +++ b/scripts/accelerate_configs/zero2.json @@ -1,6 +1,6 @@ { "fp16": { - "enabled": "auto", + "enabled": false, "loss_scale": 0, "loss_scale_window": 1000, "initial_scale_power": 16, @@ -10,6 +10,8 @@ "bf16": { "enabled": "auto" }, + "communication_data_type": "fp32", + "gradient_clipping": 1.0, "train_micro_batch_size_per_gpu": "auto", "train_batch_size": "auto", "gradient_accumulation_steps": "auto", diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index ba03fd8bd..fa2111df9 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,14 +1,14 @@ CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22_umt5/checkpoint-19000/model \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs8_4node_480p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_qknorm_mt5xxl/checkpoint-8000/model \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ --width 640 \ --cache_dir "cache_dir" \ - --text_encoder_name google/umt5-xxl \ + --text_encoder_name google/mt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8" \ + --ae_path "/storage/dataset/test140k" \ --save_img_path "./sample_image480p_ps22_ds11_udit_ckpt19k" \ --fps 24 \ --guidance_scale 5.0 \ diff --git a/scripts/text_condition/gpu/train_ds_image_256x256.sh b/scripts/text_condition/gpu/train_ds_image_256x256.sh index d44b0114f..2a5b0b3ec 100644 --- a/scripts/text_condition/gpu/train_ds_image_256x256.sh +++ b/scripts/text_condition/gpu/train_ds_image_256x256.sh @@ -12,27 +12,27 @@ export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example2.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-B/122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --ae CausalVAEModel_D8_4x8x8 \ - --ae_path "/storage/dataset/6_10_latent8" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ --num_frames 1 \ - --max_height 256 \ - --max_width 256 \ + --max_height 512 \ + --max_width 512 \ --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ + --train_batch_size=16 \ + --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=1e-4 \ diff --git a/scripts/text_condition/train_imageudit_240p.sh b/scripts/text_condition/train_imageudit_240p.sh new file mode 100644 index 000000000..4f5131316 --- /dev/null +++ b/scripts/text_condition/train_imageudit_240p.sh @@ -0,0 +1,56 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs64_4node_240p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=32 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs64_4node_240p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_imageudit_240p_debug.sh b/scripts/text_condition/train_imageudit_240p_debug.sh new file mode 100644 index 000000000..c126f3269 --- /dev/null +++ b/scripts/text_condition/train_imageudit_240p_debug.sh @@ -0,0 +1,55 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="debug" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=64 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="debug" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" diff --git a/scripts/text_condition/train_imageudit_240p_new.sh b/scripts/text_condition/train_imageudit_240p_new.sh new file mode 100644 index 000000000..b840fe73e --- /dev/null +++ b/scripts/text_condition/train_imageudit_240p_new.sh @@ -0,0 +1,57 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32_4node_240p_lr2e-5_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=32 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=2e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs32_4node_240p_lr2e-5_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" \ + --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs64_4node_240p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_mt5xxl/checkpoint-19500/model/diffusion_pytorch_model.safetensors" \ No newline at end of file diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/train_imageudit_480p.sh new file mode 100644 index 000000000..e69de29bb From aa01e9321a8429d8676f50010771ca2c2c26c6ac Mon Sep 17 00:00:00 2001 From: SHYuanBest Date: Sun, 16 Jun 2024 19:30:30 +0800 Subject: [PATCH 043/134] fix folder --- .../{ => gpu}/train_imageudit_240p.sh | 0 .../{ => gpu}/train_imageudit_240p_debug.sh | 0 .../{ => gpu}/train_imageudit_240p_new.sh | 0 .../gpu/train_imageudit_480p.sh | 56 ------------------- .../text_condition/train_imageudit_480p.sh | 0 5 files changed, 56 deletions(-) rename scripts/text_condition/{ => gpu}/train_imageudit_240p.sh (100%) rename scripts/text_condition/{ => gpu}/train_imageudit_240p_debug.sh (100%) rename scripts/text_condition/{ => gpu}/train_imageudit_240p_new.sh (100%) delete mode 100644 scripts/text_condition/train_imageudit_480p.sh diff --git a/scripts/text_condition/train_imageudit_240p.sh b/scripts/text_condition/gpu/train_imageudit_240p.sh similarity index 100% rename from scripts/text_condition/train_imageudit_240p.sh rename to scripts/text_condition/gpu/train_imageudit_240p.sh diff --git a/scripts/text_condition/train_imageudit_240p_debug.sh b/scripts/text_condition/gpu/train_imageudit_240p_debug.sh similarity index 100% rename from scripts/text_condition/train_imageudit_240p_debug.sh rename to scripts/text_condition/gpu/train_imageudit_240p_debug.sh diff --git a/scripts/text_condition/train_imageudit_240p_new.sh b/scripts/text_condition/gpu/train_imageudit_240p_new.sh similarity index 100% rename from scripts/text_condition/train_imageudit_240p_new.sh rename to scripts/text_condition/gpu/train_imageudit_240p_new.sh diff --git a/scripts/text_condition/gpu/train_imageudit_480p.sh b/scripts/text_condition/gpu/train_imageudit_480p.sh index 0a80193e7..e69de29bb 100644 --- a/scripts/text_condition/gpu/train_imageudit_480p.sh +++ b/scripts/text_condition/gpu/train_imageudit_480p.sh @@ -1,56 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_mt5xl" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/umt5-xl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=16 \ - --dataloader_num_workers 20 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="bs16_4node_480p_lr1e-4_snr5_noise0.02_ema_ps22_ds22_udit22_mt5xl" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/train_imageudit_480p.sh b/scripts/text_condition/train_imageudit_480p.sh deleted file mode 100644 index e69de29bb..000000000 From e8157e50d22a01ee43bd5fb00fd188b3d231ad94 Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Mon, 17 Jun 2024 14:21:49 +0800 Subject: [PATCH 044/134] support zp group when using deepspeed --- opensora/adaptor/engine.py | 31 ++- opensora/adaptor/stage_1_and_2.py | 229 ++++++++++-------- opensora/adaptor/utils.py | 78 +++--- opensora/adaptor/zp_manager.py | 31 +++ opensora/models/diffusion/opensora/modules.py | 32 +-- opensora/npu_config.py | 24 +- opensora/sample/sample_t2v_on_npu.py | 27 ++- opensora/train/train_t2v_diffusers.py | 11 +- scripts/text_condition/npu/sample_image.sh | 9 +- .../text_condition/npu/train_image3d_240p.sh | 50 ++++ .../text_condition/npu/train_image3d_480p.sh | 51 ++++ .../text_condition/npu/train_image_256x256.sh | 2 + .../npu/train_imageudit_480p.sh | 2 +- .../npu/train_videoudit_nx480p.sh | 1 + scripts/train_data/image_data_on_npu.txt | 8 +- 15 files changed, 387 insertions(+), 199 deletions(-) create mode 100644 opensora/adaptor/zp_manager.py create mode 100644 scripts/text_condition/npu/train_image3d_240p.sh create mode 100644 scripts/text_condition/npu/train_image3d_480p.sh diff --git a/opensora/adaptor/engine.py b/opensora/adaptor/engine.py index 484ebe19f..5208a9e41 100644 --- a/opensora/adaptor/engine.py +++ b/opensora/adaptor/engine.py @@ -103,6 +103,8 @@ from deepspeed.runtime.config import DtypeEnum +from opensora.adaptor.zp_manager import zp_manager + MEMORY_OPT_ALLREDUCE_SIZE = 500000000 DeepSpeedOptimizerCallable = \ @@ -209,6 +211,7 @@ def __init__( self._config = config_class self.loaded_checkpoint_mp_world_size = None self.loaded_checkpoint_dp_world_size = None + self.loaded_checkpoint_zp_world_size = None self.enable_backward_allreduce = True self.progressive_layer_drop = None self.eigenvalue = None @@ -931,7 +934,7 @@ def _configure_checkpointing(self, dist_init_required): and self.is_first_weights_partition_group()) if self.zero_optimization() or self.bfloat16_enabled(): - param_rank = dist.get_rank(group=self.optimizer.dp_process_group) + param_rank = dist.get_rank(group=self.optimizer.zp_process_group) # Only the first parameter parallel process needs to store the # optimizer state checkpoints for zero @@ -1122,6 +1125,7 @@ def _configure_distributed_model(self, model): self.local_all_to_all_group = groups._get_local_all_to_all_group() self.data_parallel_group = groups._get_data_parallel_group() self.dp_world_size = groups._get_data_parallel_world_size() + self.zp_world_size = zp_manager.zp_size self.seq_data_parallel_group = groups._get_sequence_data_parallel_group() self.seq_dp_world_size = groups._get_sequence_data_parallel_world_size() self.mp_world_size = groups._get_model_parallel_world_size() @@ -2618,7 +2622,7 @@ def _get_rank_zero_ckpt_name(self, checkpoints_path, tag, mp_rank, dp_rank, bf16 def _get_zero_ckpt_name(self, checkpoints_path, tag): mp_rank = 0 if self.mpu is None else self.mpu.get_model_parallel_rank() - pp_rank = dist.get_rank(group=self.optimizer.dp_process_group) + pp_rank = dist.get_rank(group=self.optimizer.zp_process_group) bf16_mode = self.bfloat16_enabled() return self._get_rank_zero_ckpt_name(checkpoints_path, tag, mp_rank, pp_rank, bf16_mode) @@ -2630,7 +2634,7 @@ def _get_ckpt_name(self, checkpoints_path, tag, mp_placeholder=None): mp_rank_str = f"{mp_rank:02d}" if self.zero_optimization_partition_weights(): - filename = "zero_pp_rank_{}".format(dist.get_rank(group=self.optimizer.dp_process_group)) + filename = "zero_pp_rank_{}".format(dist.get_rank(group=self.optimizer.zp_process_group)) ckpt_name = os.path.join( checkpoints_path, str(tag), @@ -2800,6 +2804,9 @@ def _load_checkpoint(self, fetch_z3_params=fetch_z3_params) self.loaded_checkpoint_dp_world_size = checkpoint['dp_world_size'] + if 'zp_world_size' not in checkpoint: + checkpoint['zp_world_size'] = self.zp_world_size + self.loaded_checkpoint_zp_world_size = checkpoint['zp_world_size'] optim_checkpoint = None if load_module_only: @@ -2868,8 +2875,8 @@ def get_sparse_tensor_module_names(original_set, loaded_set, original_parameters self.skipped_steps = checkpoint['skipped_steps'] self.loaded_checkpoint_mp_world_size = checkpoint['mp_world_size'] deepspeed_states = [ - 'module', 'sparse_tensor_module_names', 'skipped_steps', 'global_steps', 'dp_world_size', - 'mp_world_size', 'data_sampler', 'random_ltd' + 'module', 'sparse_tensor_module_names', 'skipped_steps', 'global_steps', 'zp_world_size', + 'mp_world_size', 'data_sampler', 'random_ltd', 'dp_world_size', ] client_state = {} @@ -2900,10 +2907,10 @@ def _load_zero_checkpoint(self, load_dir, tag, load_optimizer_states=True): zero_sd_list = None checkpoint_folder = f'{os.path.join(load_dir, tag)}' else: - if load_optimizer_states and self.seq_dp_world_size != self.loaded_checkpoint_dp_world_size: + if load_optimizer_states and self.zp_world_size != self.loaded_checkpoint_zp_world_size: raise ZeRORuntimeException("The checkpoint being loaded used a DP " \ - f"world size of {self.loaded_checkpoint_dp_world_size} but the " \ - f"current world size is {self.seq_dp_world_size}. Automatic adjustment " \ + f"world size of {self.loaded_checkpoint_zp_world_size} but the " \ + f"current world size is {self.zp_world_size}. Automatic adjustment " \ "of ZeRO's optimizer state partitioning with a new world size is not " \ "currently supported.") checkpoint_folder = None @@ -2978,7 +2985,7 @@ def _get_all_zero_checkpoint_state_dicts(self, zero_ckpt_names): if ckpt_name is None: _state = {OPTIMIZER_STATE_DICT: None} # Fully load state for current rank - elif self.zero_elastic_checkpoint() or dist.get_rank(group=self.optimizer.dp_process_group) == i: + elif self.zero_elastic_checkpoint() or dist.get_rank(group=self.optimizer.zp_process_group) == i: _state = self.checkpoint_engine.load( ckpt_name, map_location='cpu', @@ -3205,6 +3212,8 @@ def _save_moe_checkpoint(self, save_dir, tag, client_state={}, exclude_frozen_pa self.global_steps, 'global_samples': self.global_samples, + 'zp_world_size': + self.zp_world_size, 'dp_world_size': self.dp_world_size, 'mp_world_size': @@ -3231,11 +3240,11 @@ def _create_checkpoint_file(self, save_dir, tag, zero_checkpoint): def _create_zero_checkpoint_files(self, save_dir, tag): success = True # zero checkpoint files are created sequentially - for rank in range(dist.get_world_size(self.optimizer.dp_process_group)): + for rank in range(dist.get_world_size(self.optimizer.zp_process_group)): if rank == self.global_rank: success = self._create_checkpoint_file(save_dir, tag, True) - dist.barrier(group=self.optimizer.dp_process_group) + dist.barrier(group=self.optimizer.zp_process_group) return success diff --git a/opensora/adaptor/stage_1_and_2.py b/opensora/adaptor/stage_1_and_2.py index 80689c616..2ef913f24 100644 --- a/opensora/adaptor/stage_1_and_2.py +++ b/opensora/adaptor/stage_1_and_2.py @@ -33,6 +33,9 @@ from deepspeed.checkpoint import enable_universal_checkpoint from deepspeed.utils import groups + +from opensora.adaptor.zp_manager import zp_manager + # Toggle this to true to enable correctness test # with gradient partitioning and without pg_correctness_test = False @@ -97,6 +100,24 @@ def _get_padded_tensor(src_tensor, size): def contigous_flatten(tensors): return _flatten_dense_tensors([tensor.contiguous() for tensor in tensors]) + +def all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, zp_process_group): + for group_id, (group_flat, partitioned_params) in enumerate(zip(groups_flat, partitioned_param_groups)): + partition_id = dist.get_rank(group=zp_process_group[group_id]) + dp_world_size = dist.get_world_size(group=zp_process_group[group_id]) + if dp_world_size == 1: + # no groups share optimizer states + # pipeline parallel with bf16 will default call this even if dp size = 1. + continue + input_tensor = partitioned_params[partition_id].contiguous() + # print(f"call all_gather_into_tensor_dp_groups, input size is {input_tensor.size()}, " + # f"output size is {group_flat.size()}") + # + # print(f"groups_flat.size = {groups_flat.numel()}") + # print(f"partitioned_param_groups = {sum([v.numel() for v in partitioned_param_groups])}") + dist.all_gather_into_tensor(group_flat, input_tensor, zp_process_group[group_id]) + + class DeepSpeedZeroOptimizer(ZeROOptimizer): """ DeepSpeedZeroOptimizer designed to reduce the memory footprint @@ -187,21 +208,29 @@ def __init__(self, self.device = get_accelerator().current_device_name() if not self.cpu_offload else 'cpu' + zp_manager.init_group() + self.zp_process_group = zp_manager.zp_group + zp_rank = dist.get_rank(group=self.zp_process_group) + zp_size = dist.get_world_size(group=self.zp_process_group) + print(f"zp rank is {zp_rank}, zp_size={zp_size}") + self.dp_process_group = dp_process_group + self.sequence_parallel_size = groups._get_sequence_parallel_world_size() - #expert parallel group + # expert parallel group self.ep_process_group = expert_parallel_group - #data parallel group for experts + # data parallel group for experts self.expert_dp_process_group = expert_data_parallel_group - #data parallel size for non-experts + # data parallel size for non-experts dp_size = dist.get_world_size(group=self.dp_process_group) - #For MoE models this maybe different for different param group - #It will be modified during MoE setup later in the init - self.real_dp_process_group = [dp_process_group for i in range(len(self.optimizer.param_groups))] - self.partition_count = [dp_size for i in range(len(self.optimizer.param_groups))] + # For MoE models this maybe different for different param group + # It will be modified during MoE setup later in the init + self.real_zp_process_group = [self.zp_process_group for i in range(len(self.optimizer.param_groups))] + self.real_dp_process_group = [self.dp_process_group for i in range(len(self.optimizer.param_groups))] + self.partition_count = [zp_manager.zp_size for i in range(len(self.optimizer.param_groups))] self.is_gradient_accumulation_boundary = True @@ -237,9 +266,9 @@ def __init__(self, if self.fp16_master_weights_and_gradients: assert self.cpu_offload and type(self.optimizer) in [DeepSpeedCPUAdam], \ - f"fp16_master_and_gradients requires optimizer to support keeping fp16 master and gradients while keeping the optimizer states in fp32."\ - f"Currently only supported using ZeRO-Offload with DeepSpeedCPUAdam. But current setting is ZeRO-Offload:{self.cpu_offload} and optimizer type {type(self.optimizer)}." \ - f"Either disable fp16_master_weights_and_gradients or enable {self.zero_stage_string} Offload with DeepSpeedCPUAdam." + f"fp16_master_and_gradients requires optimizer to support keeping fp16 master and gradients while keeping the optimizer states in fp32." \ + f"Currently only supported using ZeRO-Offload with DeepSpeedCPUAdam. But current setting is ZeRO-Offload:{self.cpu_offload} and optimizer type {type(self.optimizer)}." \ + f"Either disable fp16_master_weights_and_gradients or enable {self.zero_stage_string} Offload with DeepSpeedCPUAdam." if self.reduce_scatter: valid_reduce_scatter_dtypes = (torch.float16, torch.bfloat16, torch.float32) @@ -280,7 +309,7 @@ def __init__(self, self.nccl_start_alignment_factor = 512 # 4-byte alignment/sizeof(fp16) = 2 assert ( - allgather_bucket_size % self.nccl_start_alignment_factor == 0 + allgather_bucket_size % self.nccl_start_alignment_factor == 0 ), f"allgather_bucket_size must be a multiple of nccl_start_alignment_factor, {self.nccl_start_alignment_factor} " self.all_reduce_print = False @@ -304,7 +333,7 @@ def __init__(self, self.groups_padding = [] # loop to deal with groups for i, param_group in enumerate(self.optimizer.param_groups): - partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_id = dist.get_rank(group=self.real_zp_process_group[i]) # push this group to list before modify # TODO: Explore simplification that avoids the extra book-keeping by pushing the reordered group @@ -330,7 +359,7 @@ def __init__(self, # to the same rank, instead they will belong to 3 ranks (r_m+2, r_m+1, r_m). if self.round_robin_gradients: round_robin_tensors, round_robin_indices = self._round_robin_reorder( - self.bit16_groups[i], dist.get_world_size(group=self.real_dp_process_group[i])) + self.bit16_groups[i], dist.get_world_size(group=self.real_zp_process_group[i])) else: round_robin_tensors = self.bit16_groups[i] round_robin_indices = list(range(len(self.bit16_groups[i]))) @@ -342,19 +371,19 @@ def __init__(self, self.bit16_groups_flat.append( self.flatten_dense_tensors_aligned( self.round_robin_bit16_groups[i], - self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i])).to( - get_accelerator().current_device_name())) + self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_zp_process_group[i])).to( + get_accelerator().current_device_name())) see_memory_usage(f"After flattening and moving param group {i} to GPU", force=False) # Record padding required for alignment - if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1: + if partition_id == dist.get_world_size(group=self.real_zp_process_group[i]) - 1: padding = self.bit16_groups_flat[i].numel() - sum( [t.numel() for t in self.round_robin_bit16_groups[i]]) else: padding = 0 self.groups_padding.append(padding) - if dist.get_rank(group=self.real_dp_process_group[i]) == 0: + if dist.get_rank(group=self.real_zp_process_group[i]) == 0: see_memory_usage(f"After Flattening and after emptying param group {i} cache", force=False) # set model bit16 weight to slices of flattened buffer @@ -365,6 +394,9 @@ def __init__(self, data_parallel_partitions = self.get_data_parallel_partitions(self.bit16_groups_flat[i], i) self.parallel_partitioned_bit16_groups.append(data_parallel_partitions) + # print(f"self.bit16_groups_flat[i].size = {self.bit16_groups_flat[i].numel()}") + # print(f"data_parallel_partitions = {sum([v.numel() for v in data_parallel_partitions])}") + # verify that data partition start locations are 4-byte aligned for partitioned_data in data_parallel_partitions: assert (partitioned_data.data_ptr() % (2 * self.nccl_start_alignment_factor) == 0) @@ -386,7 +418,7 @@ def __init__(self, i].requires_grad = True # keep this in case internal optimizer uses it param_group['params'] = [self.single_partition_of_fp32_groups[i]] - partition_size = len(self.bit16_groups_flat[i]) / dist.get_world_size(group=self.real_dp_process_group[i]) + partition_size = len(self.bit16_groups_flat[i]) / dist.get_world_size(group=self.real_zp_process_group[i]) params_in_partition, params_not_in_partition, first_offset = self.get_partition_info( self.round_robin_bit16_groups[i], partition_size, partition_id) @@ -400,7 +432,7 @@ def __init__(self, self.allgather_bucket_size = int(allgather_bucket_size) self.reduction_stream = None if get_accelerator().is_synchronized_device() else get_accelerator().Stream() - #self.copy_grad_stream = get_accelerator().Stream() + # self.copy_grad_stream = get_accelerator().Stream() self.callback_queued = False self.param_dict = {} @@ -419,7 +451,7 @@ def __init__(self, # simplified param id self.param_id = {} - #interesting code: unique ids being assigned to individual parameters + # interesting code: unique ids being assigned to individual parameters largest_param_numel = 0 count = 0 for i, params_group in enumerate(self.bit16_groups): @@ -519,7 +551,7 @@ def __init__(self, if dist.get_rank() == 0: logger.info(f"optimizer state initialized") - if dist.get_rank(group=self.dp_process_group) == 0: + if dist.get_rank(group=self.zp_process_group) == 0: see_memory_usage(f"After initializing ZeRO optimizer", force=True) self._link_all_hp_params() @@ -543,13 +575,13 @@ def _create_param_mapping(self): return param_mapping def _link_all_hp_params(self): - dp_world_size = dist.get_world_size(group=self.dp_process_group) + dp_world_size = dist.get_world_size(group=self.zp_process_group) if self.cpu_offload: self._get_offload_gradient_dict() for i, _ in enumerate(self.optimizer.param_groups): # Link bit16 and fp32 params in partition - partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_id = dist.get_rank(group=self.real_zp_process_group[i]) partition_size = self.bit16_groups_flat[i].numel() // dp_world_size flat_hp_partition = self.single_partition_of_fp32_groups[i] link_hp_params(lp_param_list=self.bit16_groups[i], @@ -561,7 +593,7 @@ def _link_all_hp_params(self): partition_start=partition_id * partition_size, partition_size=partition_size, partition_optimizer_state=self.optimizer.state[flat_hp_partition], - dp_group=self.real_dp_process_group[i]) + dp_group=self.real_zp_process_group[i]) def is_moe_group(self, group): return 'moe' in group and group['moe'] @@ -584,7 +616,7 @@ def _configure_moe_settings(self): if self.is_moe_group(group): assert all([is_moe_param(param) for param in group['params']]), "All params in MoE group must be MoE params" - self.real_dp_process_group[i] = self.expert_dp_process_group[group['name']] + self.real_zp_process_group[i] = self.expert_dp_process_group[group['name']] self.partition_count[i] = dist.get_world_size(group=self.expert_dp_process_group[group['name']]) self.is_moe_param_group.append(True) else: @@ -652,7 +684,7 @@ def initialize_optimizer_states(self): if not self.cpu_offload: for group in self.single_partition_of_fp32_groups: - group.grad = None #class init + group.grad = None # class init return @@ -660,8 +692,8 @@ def initialize_optimizer_states(self): #################### ZeRO Stage 1 - reduce gradients #################### ######################################################################### def reduce_gradients(self, pipeline_parallel=False): - world_size = dist.get_world_size(self.dp_process_group) - my_rank = dist.get_rank(self.dp_process_group) + world_size = dist.get_world_size(self.zp_process_group) + my_rank = dist.get_rank(self.zp_process_group) # with PP we must create ipg buffer, since backward is handled outside zero if pipeline_parallel and self.contiguous_gradients: @@ -695,7 +727,7 @@ def get_first_param_index(self, group_id, param_group, partition_id): def initialize_gradient_partitioning_data_structures(self): for i, param_group in enumerate(self.round_robin_bit16_groups): - total_partitions = dist.get_world_size(group=self.real_dp_process_group[i]) + total_partitions = dist.get_world_size(group=self.real_zp_process_group[i]) self.param_to_partition_ids[i] = {} self.is_partition_reduced[i] = {} @@ -766,7 +798,7 @@ def independent_gradient_partition_epilogue(self): # set is grad computed to false for all grads in partition def reset_partition_gradient_structures(self): for i, _ in enumerate(self.bit16_groups): - total_partitions = dist.get_world_size(group=self.real_dp_process_group[i]) + total_partitions = dist.get_world_size(group=self.real_zp_process_group[i]) for partition_id in range(total_partitions): self.is_partition_reduced[i][partition_id] = False self.remaining_grads_in_partition[i][partition_id] = self.total_grads_in_partition[i][partition_id] @@ -860,7 +892,6 @@ def create_reduce_and_remove_grad_hooks(self): for i, param_group in enumerate(self.bit16_groups): for param in param_group: if param.requires_grad: - def wrapper(param, i): param_tmp = param.expand_as(param) grad_acc = param_tmp.grad_fn.next_functions[0][0] @@ -922,7 +953,7 @@ def reduce_independent_p_g_buckets_and_remove_grads(self, param, i): self.grads_in_ipg_bucket.append(grad_reduc) self.params_in_ipg_bucket.append((i, param, param_id)) - #make sure the average tensor function knows how to average the gradients + # make sure the average tensor function knows how to average the gradients if is_moe_param(param): self.ipg_bucket_has_moe_params = True @@ -965,7 +996,7 @@ def allreduce_and_copy_with_multiple_ranks(self, divide=True, process_group=None, bucket_ranks=None): - process_group = self.dp_process_group if process_group is None else process_group + process_group = self.zp_process_group if process_group is None else process_group allreduced = self.allreduce_bucket(small_bucket, log=log, divide=divide, process_group=process_group) for buf, synced, bucket_rank in zip(small_bucket, self.unflatten(allreduced, small_bucket), bucket_ranks): if dist.get_rank(group=process_group) == bucket_rank: @@ -1021,18 +1052,18 @@ def average_tensor(self, tensor): curr_size = 0 prev_id, prev_process_group = -1, None - process_group = self.dp_process_group + process_group = self.zp_process_group # count = 0 for i, param, param_id in self.params_in_ipg_bucket: - process_group = self.dp_process_group + process_group = self.zp_process_group grad_reduc = self.get_gradient_for_reduction(param) - #Averages gradients at parameter level if ipg has a moe param - #Otherwise averaging is done at the entire buffer level at the end of the loop + # Averages gradients at parameter level if ipg has a moe param + # Otherwise averaging is done at the entire buffer level at the end of the loop # MoE param have different groups if self.ipg_bucket_has_moe_params: process_group = self.expert_dp_process_group[param.group_name] if is_moe_param( - param) else self.dp_process_group + param) else self.zp_process_group grad_reduc.data.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size)) partition_ids = self.param_to_partition_ids[i][param_id] @@ -1152,7 +1183,7 @@ def async_accumulate_grad_in_cpu_via_gpu(self, param): # copy to a preexisiting buffer to avoid memory allocation penalty dest_buffer = self.temp_grad_buffer_for_gpu_offload.view(-1).narrow(0, 0, param.numel()) - #buffer for storing gradients for this parameter in CPU + # buffer for storing gradients for this parameter in CPU def buffer_to_accumulate_to_in_cpu(): if not self.fp16_master_weights_and_gradients: buffer = torch.zeros(param.numel(), dtype=param.dtype, device=self.device) @@ -1160,7 +1191,7 @@ def buffer_to_accumulate_to_in_cpu(): else: return self.single_partition_of_fp32_groups[i].grad.view(-1).narrow(0, dest_offset, num_elements) - #accumulate gradients into param.grad_accum or parts of it that belongs to this partition + # accumulate gradients into param.grad_accum or parts of it that belongs to this partition def accumulate_gradients(): grad_accum = self.get_param_gradient_attribute(param) if not self.fp16_master_weights_and_gradients: @@ -1173,7 +1204,7 @@ def accumulate_gradients(): grad_accum.data.view(-1).narrow(0, source_offset, num_elements).add_(dest_buffer.narrow(0, source_offset, num_elements)) - #move accumulated gradients back to CPU + # move accumulated gradients back to CPU def copy_gradients_to_cpu(): grad_accum = self.get_param_gradient_attribute(param) if not self.fp16_master_weights_and_gradients: @@ -1181,7 +1212,7 @@ def copy_gradients_to_cpu(): else: self.accumulated_grads_in_cpu[param_id].data.copy_(grad_accum.data.view(-1).narrow( 0, source_offset, num_elements), - non_blocking=True) + non_blocking=True) if param_id not in self.accumulated_grads_in_cpu: self.accumulated_grads_in_cpu[param_id] = buffer_to_accumulate_to_in_cpu() @@ -1237,7 +1268,7 @@ def async_inplace_copy_grad_to_fp32_buffer_from_gpu(self, param): src_tensor = src_tensor.float() dest_tensor.copy_(src_tensor, non_blocking=True) - param.grad = None #offload only + param.grad = None # offload only def complete_grad_norm_calculation_for_cpu_offload(self, params): total_norm = 0.0 @@ -1254,7 +1285,7 @@ def complete_grad_norm_calculation_for_cpu_offload(self, params): # so they have no norm_for_param_grads if param_id in self.norm_for_param_grads: param_norm = self.norm_for_param_grads[param_id] - total_norm += param_norm.item()**2 + total_norm += param_norm.item() ** 2 else: # As unused parameters in modules may not be expected sometimes, # add an explicit error msg when it occurred and an option to @@ -1274,7 +1305,7 @@ def complete_grad_norm_calculation_for_cpu_offload(self, params): self._model_parallel_all_reduce(tensor=total_norm_cuda, op=dist.ReduceOp.SUM) - total_norm = total_norm_cuda[0].item()**(1. / norm_type) + total_norm = total_norm_cuda[0].item() ** (1. / norm_type) if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm: total_norm = -1 @@ -1296,7 +1327,7 @@ def copy_grads_in_partition(self, param): self.async_inplace_copy_grad_to_fp32_buffer_from_gpu(param) return - #print(f"ID {self.get_param_id(param)} grad norm {param.grad.norm()}") + # print(f"ID {self.get_param_id(param)} grad norm {param.grad.norm()}") if self.grads_in_partition is None: self.grads_in_partition_offset = 0 total_size = 0 @@ -1315,7 +1346,7 @@ def copy_grads_in_partition(self, param): new_grad_tensor = self.grads_in_partition.view(-1).narrow(0, self.grads_in_partition_offset, param.numel()) new_grad_tensor.copy_(grad_reduc.view(-1)) grad_reduc.data = new_grad_tensor.data.view_as(grad_reduc) - #print(f"Grad norm after copy to contiguous_buffer {param.grad.data.norm()}") + # print(f"Grad norm after copy to contiguous_buffer {param.grad.data.norm()}") self.grads_in_partition_offset += param.numel() def reduce_ipg_grads(self): @@ -1427,7 +1458,7 @@ def get_reducible_portion(key): def sequential_execution(self, function, message, group=None): if group is None: - group = self.dp_process_group + group = self.zp_process_group if dist.get_rank(group=group) == 0: logger.info(message) for id in range(dist.get_world_size(group=group)): @@ -1446,7 +1477,7 @@ def allreduce_bucket(self, bucket, rank=None, log=None, divide=True, process_gro rank = None tensor = self.flatten(bucket) - process_group = self.dp_process_group if process_group is None else process_group + process_group = self.zp_process_group if process_group is None else process_group tensor_to_allreduce = tensor @@ -1459,15 +1490,16 @@ def allreduce_bucket(self, bucket, rank=None, log=None, divide=True, process_gro tensor_to_allreduce = tensor.to(communication_data_type) if divide: - tensor_to_allreduce.div_(dist.get_world_size(group=process_group) / float(self.sequence_parallel_size)) + tensor_to_allreduce.div_( + dist.get_world_size(group=self.dp_process_group) / float(self.sequence_parallel_size)) tensor_to_allreduce = tensor_to_allreduce.contiguous() if rank is None: # "All Reducing" - dist.all_reduce(tensor_to_allreduce, group=process_group) + dist.all_reduce(tensor_to_allreduce, group=self.dp_process_group) else: - global_rank = dist.get_global_rank(process_group, rank) - dist.reduce(tensor_to_allreduce, global_rank, group=process_group) + global_rank = dist.get_global_rank(self.dp_process_group, rank) + dist.reduce(tensor_to_allreduce, global_rank, group=self.dp_process_group) if communication_data_type != tensor.dtype and tensor is not tensor_to_allreduce: if rank is None or rank == dist.get_rank(group=process_group): @@ -1483,7 +1515,7 @@ def _clear_previous_reduced_grads(self): # if rank is specified do a reduction instead of an allreduce def allreduce_and_copy(self, small_bucket, rank=None, log=None, divide=True, process_group=None): - process_group = self.dp_process_group if process_group is None else process_group + process_group = self.zp_process_group if process_group is None else process_group if self.overlap_comm: get_accelerator().synchronize() # It is safe to clear the previously reduced grads of other partitions @@ -1500,18 +1532,18 @@ def allreduce_and_copy(self, small_bucket, rank=None, log=None, divide=True, pro divide=divide, process_group=process_group, ) - if rank is None or rank == dist.get_rank(group=self.dp_process_group): + if rank is None or rank == dist.get_rank(group=self.zp_process_group): for buf, synced in zip(small_bucket, self.unflatten(allreduced, small_bucket)): buf.copy_(synced) def allreduce_no_retain( - self, - bucket, - numel_per_bucket=500000000, - rank=None, - log=None, - divide=True, - process_group=None, + self, + bucket, + numel_per_bucket=500000000, + rank=None, + log=None, + divide=True, + process_group=None, ): small_bucket = [] numel = 0 @@ -1543,7 +1575,7 @@ def buffered_reduce_fallback(self, rank, grads, elements_per_buffer=500000000, l def get_data_parallel_partitions(self, tensor, group_id): partitions = [] - dp = dist.get_world_size(group=self.real_dp_process_group[group_id]) + dp = dist.get_world_size(group=self.real_zp_process_group[group_id]) # dp_id = dist.get_rank(group=self.real_dp_process_group[group_id]) total_num_elements = tensor.numel() @@ -1652,14 +1684,14 @@ def get_grad_norm_direct(self, gradients, params, norm_type=2): continue if is_model_parallel_parameter(p) or (self.model_parallel_rank == 0): param_norm = g.data.double().norm(2) - total_norm += param_norm.item()**2 + total_norm += param_norm.item() ** 2 # Sum across all model parallel GPUs. total_norm_cuda = get_accelerator().FloatTensor([float(total_norm)]) dist.all_reduce(total_norm_cuda, op=dist.ReduceOp.SUM, group=self.dp_process_group) self._model_parallel_all_reduce(tensor=total_norm_cuda, op=dist.ReduceOp.SUM) - total_norm = total_norm_cuda[0].item()**(1. / norm_type) + total_norm = total_norm_cuda[0].item() ** (1. / norm_type) if total_norm == float('inf') or total_norm == -float('inf') or total_norm != total_norm: total_norm = -1 @@ -1737,7 +1769,7 @@ def scaled_global_norm(self, norm_type=2): assert norm_type == 2, "only L2 norm supported" norm_groups = [] for i, group in enumerate(self.bit16_groups): - partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_id = dist.get_rank(group=self.real_zp_process_group[i]) if self.cpu_offload: norm_groups.append(self.complete_grad_norm_calculation_for_cpu_offload(self.params_in_partition[i])) single_grad_partition = self.single_partition_of_fp32_groups[i].grad @@ -1752,17 +1784,17 @@ def scaled_global_norm(self, norm_type=2): def get_bit16_param_group(self, group_no): bit16_partitions = self.parallel_partitioned_bit16_groups[group_no] - partition_id = dist.get_rank(group=self.real_dp_process_group[group_no]) - return [bit16_partitions[dist.get_rank(group=self.real_dp_process_group[group_no])]] + partition_id = dist.get_rank(group=self.real_zp_process_group[group_no]) + return [bit16_partitions[dist.get_rank(group=self.real_zp_process_group[group_no])]] def _optimizer_step(self, group_no): original_param_groups = self.optimizer.param_groups self.optimizer.param_groups = [original_param_groups[group_no]] # Disabling this as the C++ side copy & synchronize is not working correctly - #from deepspeed.ops.adam import DeepSpeedCPUAdam - #if type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half: + # from deepspeed.ops.adam import DeepSpeedCPUAdam + # if type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half: # self.optimizer.step(fp16_param_groups=[self.get_bit16_param_group(group_no)]) - #else: + # else: # self.optimizer.step() self.optimizer.step() self.optimizer.param_groups = original_param_groups @@ -1805,7 +1837,7 @@ def step(self, closure=None): # Step 2:- run optimizer and upscaling simultaneously for i, group in enumerate(self.bit16_groups): self.timers(OPTIMIZER_GRADIENTS_TIMER).start() - partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_id = dist.get_rank(group=self.real_zp_process_group[i]) if self.cpu_offload: single_grad_partition = self.single_partition_of_fp32_groups[i].grad self.unscale_and_clip_grads([single_grad_partition], scaled_global_grad_norm) @@ -1815,8 +1847,8 @@ def step(self, closure=None): self._optimizer_step(i) # Disabled, this is not currently working - #from deepspeed.ops.adam import DeepSpeedCPUAdam - #if not (type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half): + # from deepspeed.ops.adam import DeepSpeedCPUAdam + # if not (type(self.optimizer) == DeepSpeedCPUAdam and self.dtype == torch.half): # bit16_partitions = self.parallel_partitioned_bit16_groups[i] # fp32_partition = self.single_partition_of_fp32_groups[i] # bit16_partitions[partition_id].data.copy_(fp32_partition.data) @@ -1831,7 +1863,7 @@ def step(self, closure=None): # create a flat gradients for parameters updated by this process # If we are last partition, ensure we have same size grads and partition size, if not pad with zero tensors - if partition_id == dist.get_world_size(group=self.real_dp_process_group[i]) - 1: + if partition_id == dist.get_world_size(group=self.real_zp_process_group[i]) - 1: single_grad_partition = self.flatten_dense_tensors_aligned( self.averaged_gradients[i], int(self.partition_size[i])).to(self.single_partition_of_fp32_groups[i].dtype) @@ -1874,11 +1906,9 @@ def step(self, closure=None): # Gather the updated weights from everyone. # Then all partitions of the model parameters are updated and ready for next round forward. - all_gather_dp_groups(groups_flat=self.bit16_groups_flat, - partitioned_param_groups=self.parallel_partitioned_bit16_groups, - dp_process_group=self.real_dp_process_group, - start_alignment_factor=self.nccl_start_alignment_factor, - allgather_bucket_size=self.allgather_bucket_size) + all_gather_into_tensor_dp_groups(groups_flat=self.bit16_groups_flat, + partitioned_param_groups=self.parallel_partitioned_bit16_groups, + zp_process_group=self.real_zp_process_group) self.timers(OPTIMIZER_ALLGATHER_TIMER).stop() # TODO: we probably don't need this? just to be safe @@ -1894,25 +1924,23 @@ def step(self, closure=None): def update_lp_params(self): for i, (bit16_partitions, fp32_partition) in enumerate( zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)): - partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_id = dist.get_rank(group=self.real_zp_process_group[i]) bit16_partitions[partition_id].data.copy_(fp32_partition.data) # print_rank_0(f'update_lp_params {i=} {partition_id=}', force=True) # if i == 0: # print_rank_0(f'{fp32_partition[:10]=}', force=True) - all_gather_dp_groups(groups_flat=self.bit16_groups_flat, - partitioned_param_groups=self.parallel_partitioned_bit16_groups, - dp_process_group=self.real_dp_process_group, - start_alignment_factor=self.nccl_start_alignment_factor, - allgather_bucket_size=self.allgather_bucket_size) + all_gather_into_tensor_dp_groups(groups_flat=self.bit16_groups_flat, + partitioned_param_groups=self.parallel_partitioned_bit16_groups, + zp_process_group=self.real_zp_process_group) def _average_expert_grad_norms(self, norm_groups): for i, norm in enumerate(norm_groups): if self.is_moe_param_group[i]: - scaled_norm = norm * 1.0 / float(dist.get_world_size(group=self.real_dp_process_group[i])) + scaled_norm = norm * 1.0 / float(dist.get_world_size(group=self.dp_process_group)) scaled_norm_tensor = torch.tensor(scaled_norm, device=get_accelerator().device_name(), dtype=torch.float) - dist.all_reduce(scaled_norm_tensor, group=self.real_dp_process_group[i]) + dist.all_reduce(scaled_norm_tensor, group=self.dp_process_group) norm_groups[i] = scaled_norm_tensor.item() def unscale_and_clip_grads(self, grad_groups_flat, total_norm): @@ -2152,14 +2180,14 @@ def _restore_from_elastic_fp32_weights(self, all_state_dict): merged_single_partition_of_fp32_groups = [] for i in range(len(self.single_partition_of_fp32_groups)): - partition_id = dist.get_rank(group=self.real_dp_process_group[i]) + partition_id = dist.get_rank(group=self.real_zp_process_group[i]) merged_partitions = [sd[SINGLE_PARTITION_OF_FP32_GROUPS][i] for sd in all_state_dict] if self.is_moe_group(self.optimizer.param_groups[i]): ranks = self.get_ep_ranks(group_name=self.optimizer.param_groups[i]['name']) merged_partitions = [merged_partitions[i] for i in ranks] flat_merged_partitions = self.flatten_dense_tensors_aligned( merged_partitions, - self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_dp_process_group[i])) + self.nccl_start_alignment_factor * dist.get_world_size(group=self.real_zp_process_group[i])) dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, i) merged_single_partition_of_fp32_groups.append(dp_partitions[partition_id]) @@ -2170,7 +2198,7 @@ def _restore_from_elastic_fp32_weights(self, all_state_dict): def _restore_from_bit16_weights(self): for group_id, (bit16_partitions, fp32_partition) in enumerate( zip(self.parallel_partitioned_bit16_groups, self.single_partition_of_fp32_groups)): - partition_id = dist.get_rank(group=self.real_dp_process_group[group_id]) + partition_id = dist.get_rank(group=self.real_zp_process_group[group_id]) fp32_partition.data.copy_(bit16_partitions[partition_id].data) # Refresh the fp32 master params from the fp16 or bfloat16 copies. @@ -2179,8 +2207,8 @@ def refresh_fp32_params(self): # Extract optimizer state for current partition from merged states of all partitions def _partition_base_optimizer_state(self, state_key, all_partition_states, group_id): - partition_id = dist.get_rank(group=self.real_dp_process_group[group_id]) - alignment = dist.get_world_size(group=self.real_dp_process_group[group_id]) + partition_id = dist.get_rank(group=self.real_zp_process_group[group_id]) + alignment = dist.get_world_size(group=self.real_zp_process_group[group_id]) if torch.is_tensor(all_partition_states[0]): flat_merged_partitions = self.flatten_dense_tensors_aligned(all_partition_states, alignment) dp_partitions = self.get_data_parallel_partitions(flat_merged_partitions, group_id) @@ -2268,12 +2296,12 @@ def _load_hp_checkpoint_state(self, checkpoint_dir): tp_rank = bwc_tensor_model_parallel_rank(mpu=self.mpu) tp_world_size = self.mpu.get_slice_parallel_world_size() if hasattr(self.mpu, "get_slice_parallel_world_size") \ - else self.mpu.get_tensor_model_parallel_world_size() + else self.mpu.get_tensor_model_parallel_world_size() for i, _ in enumerate(self.optimizer.param_groups): for lp in self.bit16_groups[i]: if lp._hp_mapping is not None: - #print(f"Loading {self.param_names[lp]} {tp_rank=} {tp_world_size=}") + # print(f"Loading {self.param_names[lp]} {tp_rank=} {tp_world_size=}") lp.load_hp_checkpoint_state(os.path.join(checkpoint_dir, self.param_names[lp]), tp_rank, tp_world_size) @@ -2291,8 +2319,8 @@ def _load_global_state(self, sd): if not self.partition_gradients: required_version = pkg_version.parse("0.3.17") error_str = f"ZeRO stage 1 changed in {required_version} and is not backwards compatible " \ - "with older stage 1 checkpoints. If you'd like to load an old ZeRO-1 checkpoint " \ - "please use an older version of DeepSpeed (<= 0.5.8) and set 'legacy_stage1': true in your zero config json." + "with older stage 1 checkpoints. If you'd like to load an old ZeRO-1 checkpoint " \ + "please use an older version of DeepSpeed (<= 0.5.8) and set 'legacy_stage1': true in your zero config json." assert required_version <= ckpt_version, f"Old version: {ckpt_version} {error_str}" def _load_legacy_checkpoint(self, state_dict_list, load_optimizer_states=True, load_from_fp32_weights=False): @@ -2323,7 +2351,7 @@ def _load_legacy_checkpoint(self, state_dict_list, load_optimizer_states=True, l """ # I think it should actually be ok to reload the optimizer before the model. - dp_rank = dist.get_rank(group=self.dp_process_group) + dp_rank = dist.get_rank(group=self.zp_process_group) current_rank_sd = state_dict_list[dp_rank] self._load_global_state(current_rank_sd) @@ -2401,7 +2429,6 @@ def estimate_zero2_model_states_mem_needs(total_params, num_nodes=1, cpu_offload=True, additional_buffer_factor=1.5): - total_gpus = num_nodes * num_gpus_per_node if cpu_offload: @@ -2482,7 +2509,7 @@ def format_options(cpu_offload): gpus_str = "GPUs" if num_gpus_per_node > 1 else "GPU" print("Estimated memory needed for params, optim states and gradients for a:\n" f"HW: Setup with {num_nodes} {nodes_str}, {num_gpus_per_node} {gpus_str} per node.\n" - f"SW: Model with {int(total_params/1e6)}M total params.") + f"SW: Model with {int(total_params / 1e6)}M total params.") print(" per CPU | per GPU | Options") for cpu_offload in [True, False]: cpu_mem, gpu_mem = estimate_zero2_model_states_mem_needs(total_params=total_params, @@ -2492,4 +2519,4 @@ def format_options(cpu_offload): additional_buffer_factor=additional_buffer_factor) options_str = format_options(cpu_offload=cpu_offload) - print(f" {cpu_mem/2**30:7.2f}GB | {gpu_mem/2**30:6.2f}GB | {options_str}") + print(f" {cpu_mem / 2 ** 30:7.2f}GB | {gpu_mem / 2 ** 30:6.2f}GB | {options_str}") diff --git a/opensora/adaptor/utils.py b/opensora/adaptor/utils.py index fbdd87e67..c32ff6efc 100644 --- a/opensora/adaptor/utils.py +++ b/opensora/adaptor/utils.py @@ -953,54 +953,54 @@ def align_dense_tensors(tensor_list, alignment): return padded_tensor_list -def all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, dp_process_group): +def all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, zp_process_group, dp_process_group=None): + for group_id, (group_flat, partitioned_params) in enumerate(zip(groups_flat, partitioned_param_groups)): - partition_id = dist.get_rank(group=dp_process_group[group_id]) - dp_world_size = dist.get_world_size(group=dp_process_group[group_id]) + partition_id = dist.get_rank(group=zp_process_group[group_id]) + dp_world_size = dist.get_world_size(group=dp_process_group) if dp_world_size == 1: # no groups share optimizer states # pipeline parallel with bf16 will default call this even if dp size = 1. continue # print("call contiguous for all_gather_into_tensor_dp_groups") - dist.all_gather_into_tensor(group_flat, partitioned_params[partition_id].contiguous(), dp_process_group[group_id]) + dist.all_gather_into_tensor(group_flat, partitioned_params[partition_id].contiguous(), dp_process_group) -def all_gather_dp_groups(groups_flat, partitioned_param_groups, dp_process_group, start_alignment_factor, - allgather_bucket_size): +def all_gather_dp_groups(groups_flat, partitioned_param_groups, zp_process_group, start_alignment_factor, + allgather_bucket_size, dp_process_group=None): # if dist.has_all_gather_into_tensor(): - # return all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, dp_process_group) - - for group_id, partitioned_params in enumerate(partitioned_param_groups): - # Sequential AllGather Best of both worlds - partition_id = dist.get_rank(group=dp_process_group[group_id]) - dp_world_size = dist.get_world_size(group=dp_process_group[group_id]) - - if dp_world_size == 1: - # no groups share optimizer states - # pipeline parallel with bf16 will default call this even if dp size = 1. - continue - num_shards = max(1, partitioned_params[partition_id].numel() * dp_world_size // allgather_bucket_size) - - shard_size = partitioned_params[partition_id].numel() // num_shards - - # Enforce nccl/rccl alignment of start location of each shard - shard_size = shard_size - (shard_size % start_alignment_factor) - - num_elements = shard_size - - assert shard_size * num_shards <= partitioned_params[partition_id].numel() - - for shard_id in range(num_shards): - - if shard_id == (num_shards - 1): - num_elements = partitioned_params[partition_id].numel() - shard_id * shard_size - - shard_list = [] - for dp_id in range(dp_world_size): - curr_shard = partitioned_params[dp_id].narrow(0, shard_id * shard_size, num_elements).detach() - shard_list.append(curr_shard) - - dist.all_gather(shard_list, shard_list[partition_id].contiguous(), dp_process_group[group_id]) + return all_gather_into_tensor_dp_groups(groups_flat, partitioned_param_groups, zp_process_group, dp_process_group) + + # for group_id, partitioned_params in enumerate(partitioned_param_groups): + # # Sequential AllGather Best of both worlds + # partition_id = dist.get_rank(group=dp_process_group[group_id]) + # dp_world_size = dist.get_world_size(group=dp_process_group[group_id]) + # + # if dp_world_size == 1: + # # no groups share optimizer states + # # pipeline parallel with bf16 will default call this even if dp size = 1. + # continue + # num_shards = max(1, partitioned_params[partition_id].numel() * dp_world_size // allgather_bucket_size) + # + # shard_size = partitioned_params[partition_id].numel() // num_shards + # + # # Enforce nccl/rccl alignment of start location of each shard + # shard_size = shard_size - (shard_size % start_alignment_factor) + # + # num_elements = shard_size + # + # assert shard_size * num_shards <= partitioned_params[partition_id].numel() + # + # for shard_id in range(num_shards): + # + # if shard_id == (num_shards - 1): + # num_elements = partitioned_params[partition_id].numel() - shard_id * shard_size + # + # shard_list = [] + # for dp_id in range(dp_world_size): + # curr_shard = partitioned_params[dp_id].narrow(0, shard_id * shard_size, num_elements).detach() + # shard_list.append(curr_shard) + # dist.all_gather(shard_list, shard_list[partition_id].contiguous(), dp_process_group[group_id]) class TLinear(torch.nn.Linear): diff --git a/opensora/adaptor/zp_manager.py b/opensora/adaptor/zp_manager.py new file mode 100644 index 000000000..e05bfdbcd --- /dev/null +++ b/opensora/adaptor/zp_manager.py @@ -0,0 +1,31 @@ +import torch +import os +import torch.distributed as dist + + +class ZPManager(object): + def __init__(self, zp_size=8): + self.rank = int(os.getenv('RANK', '0')) + self.world_size = int(os.getenv("WORLD_SIZE", '1')) + self.zp_size = zp_size + self.zp_group = None + self.zp_rank = None + self.is_initialized = False + + def init_group(self): + if self.is_initialized: + return + + self.is_initialized = True + + """Initialize the sequence parallel group.""" + num_zp_groups: int = self.world_size // self.zp_size + for i in range(num_zp_groups): + ranks = range(i * self.zp_size, (i + 1) * self.zp_size) + group = dist.new_group(ranks) + if self.rank in ranks: + self.zp_group = group + self.zp_rank = self.rank % self.zp_size + + +zp_manager = ZPManager() diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 159305db8..d081855dc 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -554,11 +554,7 @@ def forward(self, x, attention_mask, t, h, w): # import ipdb;ipdb.set_trace() b = x.shape[0] x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) - if npu_config is None: - x = self.layer(x) + (x if self.down_shortcut else 0) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = self.layer(x) + (x if self.down_shortcut else 0) x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', h=h//self.down_factor[0], w=w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) @@ -777,24 +773,14 @@ def __init__(self, downsampler, dim, hidden_features, bias=True): def forward(self, x, t, h, w): # import ipdb;ipdb.set_trace() - if npu_config is None: - x = self.project_in(x) - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) - x = F.gelu(x) - out = x - for module in self.dwconv: - out = out + module(x) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) - x = self.project_out(out) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.project_in, x, torch.float16) - x = F.gelu(x) - out = x - for module in self.dwconv: - out = out + npu_config.run_conv2d(module, x, torch.float16) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) - x = npu_config.run_conv2d(self.project_out, out, x_dtype) + x = self.project_in(x) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + module(x) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) + x = self.project_out(out) return x @maybe_allow_in_graph diff --git a/opensora/npu_config.py b/opensora/npu_config.py index cd794e219..70ae640b4 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -7,6 +7,9 @@ import torch import subprocess import torch.distributed as dist + +from opensora.adaptor.zp_manager import zp_manager + try: import torch_npu @@ -64,7 +67,7 @@ def __init__(self): self.use_small_dataset = False self.current_run_dtype = None self.original_run_dtype = None - + self.zp_manager = zp_manager if self.enable_FA and self.enable_FP32: self.inf_float = -10000.0 else: @@ -95,6 +98,10 @@ def __init__(self): import deepspeed.runtime.zero.stage_1_and_2 as stage_1_and_2 self.replace_methods(stage_1_and_2.DeepSpeedZeroOptimizer, DeepSpeedZeroOptimizer, ['_has_inf_or_nan']) + import deepspeed.runtime.engine as engine + from opensora.adaptor.engine import DeepSpeedEngine + self.replace_methods(engine.DeepSpeedEngine, DeepSpeedEngine, skip_fcns=['__init__', '_copy_recovery_script', '_change_recovery_script_permissions']) + if "RANK" in os.environ: self.rank = int(os.environ["RANK"]) self.world_size = int(os.environ["WORLD_SIZE"]) @@ -104,12 +111,21 @@ def __init__(self): self.world_size = self.N_NPU_PER_NODE self.print_with_rank(f"The npu_config.on_npu is {self.on_npu}") - def replace_methods(self, target_class, source_class, skip_fcns=[]): + def replace_methods(self, target_class, source_class, skip_fcns=[], only_include_fcns=None): for attr_name in dir(source_class): attr_value = getattr(source_class, attr_name) - if isinstance(attr_value, (staticmethod, classmethod)) or attr_name in skip_fcns: + if attr_name in source_class.__dict__: + attr_class_value = source_class.__dict__[attr_name] + else: + attr_class_value = attr_value + if (isinstance(attr_class_value, staticmethod) or isinstance(attr_class_value, classmethod) + or attr_name in skip_fcns): print(f"skip replace {attr_name}") continue + + if only_include_fcns is not None and attr_name not in only_include_fcns: + continue + elif isinstance(attr_value, types.FunctionType): setattr(target_class, attr_name, attr_value) @@ -148,7 +164,7 @@ def get_local_rank(self): return self.rank % self.N_NPU_PER_NODE def get_pickle_path(self, file_name): - return f"/home/opensora/yancen/Open-Sora-Plan-dev/pickles/{file_name}_local_n63" + return f"{self.pickle_save_path}/{file_name}_local_n63" def free_mm(self): for key, value in self.mm.items(): diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py index 76b45bb35..d9a2a714c 100644 --- a/opensora/sample/sample_t2v_on_npu.py +++ b/opensora/sample/sample_t2v_on_npu.py @@ -29,6 +29,7 @@ from opensora.sample.pipeline_opensora import OpenSoraPipeline import imageio + try: import torch_npu except: @@ -36,9 +37,13 @@ import time from opensora.npu_config import npu_config + def load_t2v_checkpoint(model_path): if args.model_3d: - transformer_model = UDiTT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + # transformer_model = UDiTT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + # low_cpu_mem_usage=False, device_map=None, + # torch_dtype=weight_dtype) + transformer_model = OpenSoraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: @@ -56,6 +61,7 @@ def load_t2v_checkpoint(model_path): return pipeline + def get_latest_path(): # Get the most recent checkpoint dirs = os.listdir(args.model_path) @@ -64,6 +70,8 @@ def get_latest_path(): path = dirs[-1] if len(dirs) > 0 else None return path + + def run_model_and_save_images(pipeline, model_path): video_grids = [] if not isinstance(args.text_prompt, list): @@ -100,9 +108,11 @@ def run_model_and_save_images(pipeline, model_path): else: imageio.mimwrite( os.path.join( - args.save_img_path, f'{args.sample_method}_{index}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' + args.save_img_path, + f'{args.sample_method}_{index}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' ), videos[0], - fps=args.fps, quality=9, codec='libx264', output_params=['-threads', '20']) # highest quality is 10, lowest is 0 + fps=args.fps, quality=9, codec='libx264', + output_params=['-threads', '20']) # highest quality is 10, lowest is 0 except: print('Error when saving {}'.format(prompt)) video_grids.append(videos) @@ -113,12 +123,14 @@ def run_model_and_save_images(pipeline, model_path): gathered_tensor = torch.zeros(shape, dtype=video_grids.dtype, device=device) dist.all_gather_into_tensor(gathered_tensor, video_grids.contiguous()) video_grids = gathered_tensor.cpu() + # video_grids = video_grids.repeat(world_size, 1, 1, 1) # output = torch.zeros(video_grids.shape, dtype=video_grids.dtype, device=device) # dist.all_to_all_single(output, video_grids) # video_grids = output.cpu() def get_file_name(): - return os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') + return os.path.join(args.save_img_path, + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') if args.num_frames == 1: save_image(video_grids / 255.0, get_file_name(), @@ -204,7 +216,6 @@ def get_file_name(): elif args.sample_method == 'KDPM2AncestralDiscrete': ######### scheduler = KDPM2AncestralDiscreteScheduler() - if not os.path.exists(args.save_img_path): os.makedirs(args.save_img_path, exist_ok=True) @@ -219,10 +230,10 @@ def get_file_name(): while True: cur_path = get_latest_path() if cur_path == latest_path: - time.sleep(80) + time.sleep(5) continue - time.sleep(180) + time.sleep(1) latest_path = cur_path npu_config.print_msg(f"The latest_path is {latest_path}") full_path = f"{args.model_path}/{latest_path}/model_ema" @@ -249,4 +260,4 @@ def get_file_name(): run_model_and_save_images(pipeline, latest_path) prof.step() else: - run_model_and_save_images(pipeline, latest_path) \ No newline at end of file + run_model_and_save_images(pipeline, latest_path) diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 532c1d651..298d92b12 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -19,6 +19,9 @@ from einops import rearrange from tqdm import tqdm +import torch_npu +from opensora.npu_config import npu_config + try: import torch_npu from opensora.npu_config import npu_config @@ -620,8 +623,6 @@ def run(model_input, model_kwargs, prof): optimizer.step() lr_scheduler.step() optimizer.zero_grad() - if prof is not None: - prof.step() avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps @@ -656,6 +657,10 @@ def run(model_input, model_kwargs, prof): # Switch back to the original UNet parameters. ema_model.restore(model.parameters()) + if prof is not None: + prof.step() + + return loss def train_one_step(step_, data_item_, prof_=None): @@ -781,7 +786,7 @@ def train_all_epoch(prof_=None): parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--pretrained", type=str, default=None) parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") - + # diffusion setting parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") diff --git a/scripts/text_condition/npu/sample_image.sh b/scripts/text_condition/npu/sample_image.sh index 927693213..eb09643d9 100644 --- a/scripts/text_condition/npu/sample_image.sh +++ b/scripts/text_condition/npu/sample_image.sh @@ -10,17 +10,16 @@ fi torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ --num_frames ${NUM_FRAME} \ - --height 480 \ - --width 640 \ + --height 256 \ + --width 256 \ --cache_dir "../cache_dir" \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --ae_path "${WEIGHT_PATH}/test140k/" \ --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ --fps 24 \ --guidance_scale 5.0 \ --num_sampling_steps 50 \ - --enable_tiling \ - --sample_method DDPM \ + --sample_method PNDM \ --model_3d \ No newline at end of file diff --git a/scripts/text_condition/npu/train_image3d_240p.sh b/scripts/text_condition/npu/train_image3d_240p.sh new file mode 100644 index 000000000..60b174f71 --- /dev/null +++ b/scripts/text_condition/npu/train_image3d_240p.sh @@ -0,0 +1,50 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE + + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=4e-5 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=2000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image3d_480p.sh b/scripts/text_condition/npu/train_image3d_480p.sh new file mode 100644 index 000000000..e0b905d02 --- /dev/null +++ b/scripts/text_condition/npu/train_image3d_480p.sh @@ -0,0 +1,51 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=8 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="cosine" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=100 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image_256x256.sh b/scripts/text_condition/npu/train_image_256x256.sh index bceec313b..deb6d6c77 100644 --- a/scripts/text_condition/npu/train_image_256x256.sh +++ b/scripts/text_condition/npu/train_image_256x256.sh @@ -46,4 +46,6 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_imageudit_480p.sh b/scripts/text_condition/npu/train_imageudit_480p.sh index 45911e9de..398c49b0f 100644 --- a/scripts/text_condition/npu/train_imageudit_480p.sh +++ b/scripts/text_condition/npu/train_imageudit_480p.sh @@ -15,7 +15,7 @@ accelerate launch \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --ae_path "${WEIGHT_PATH}/test140k/" \ --video_data "./scripts/train_data/video_data_on_npu.txt" \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ diff --git a/scripts/text_condition/npu/train_videoudit_nx480p.sh b/scripts/text_condition/npu/train_videoudit_nx480p.sh index 39d9849e7..680301276 100644 --- a/scripts/text_condition/npu/train_videoudit_nx480p.sh +++ b/scripts/text_condition/npu/train_videoudit_nx480p.sh @@ -47,5 +47,6 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ + --noise_offset 0.02 \ --downsampler "k333_s222" \ --resume_from_checkpoint="latest" diff --git a/scripts/train_data/image_data_on_npu.txt b/scripts/train_data/image_data_on_npu.txt index 7c5f1e470..82c9ec3e3 100644 --- a/scripts/train_data/image_data_on_npu.txt +++ b/scripts/train_data/image_data_on_npu.txt @@ -1,4 +1,4 @@ -/home/local_dataset/image_data_obs/AnyWord-3M/,/home/opensora/captions/linbin_captions/anytext_en_1886137.json -/home/local_dataset_n63/image_data_obs/mj_unzip_files,/home/opensora/captions/linbin_captions/tuzhan_mj_4615265.json -/home/local_dataset_n63/image_data_obs/sa_unzip_files,/home/opensora/captions/linbin_captions/sam_image_11185255.json -/home/local_dataset_n63/image_data_obs/images/,/home/opensora/captions/linbin_captions/human_images_162094.json \ No newline at end of file +/home/local_dataset_6t/image_data_obs/AnyWord_3M_unzip_files/,/home/opensora/captions/linbin_captions/anytext_en_1886137.json +/home/local_dataset_6t/image_data_obs/mj_unzip_files,/home/opensora/captions/linbin_captions/tuzhan_mj_4615265.json +/home/local_dataset_6t/image_data_obs/sa_unzip_files,/home/opensora/captions/linbin_captions/sam_image_11185255.json +/home/local_dataset_6t/image_data_obs/images/,/home/opensora/captions/linbin_captions/human_images_162094.json \ No newline at end of file From 1f45901855ccbd9b186db8894dff86129776728d Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Mon, 17 Jun 2024 14:35:39 +0800 Subject: [PATCH 045/134] fix import of npu --- opensora/train/train_t2v_diffusers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index e08fefd52..f1d2887e1 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -19,9 +19,6 @@ from einops import rearrange from tqdm import tqdm -import torch_npu -from opensora.npu_config import npu_config - try: import torch_npu from opensora.npu_config import npu_config From e397f10ffcc5242f853a5b5b36ed637871ad6691 Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Tue, 18 Jun 2024 10:19:26 +0800 Subject: [PATCH 046/134] bind cores to stable the host cost --- examples/prompt_list_0.txt | 6 ++-- .../diffusion/opensora/modeling_opensora.py | 8 +++--- opensora/npu_config.py | 28 +++++++++++++++++++ scripts/text_condition/npu/sample_image.sh | 2 +- .../text_condition/npu/train_image3d_240p.sh | 2 +- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index 53b4d0975..4a46e7a49 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -9,10 +9,10 @@ A gorgeously rendered papercraft world of a coral reef, rife with colorful fish This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. The majestic beauty of a waterfall cascading down a cliff into a serene lake. -Sunset over the sea. +非常好看的夜景 a cat wearing sunglasses and working as a lifeguard at pool. -Slow pan upward of blazing oak fire in an indoor fireplace. -Yellow and black tropical fish dart through the sea. +一个橘色的猫 +a word "你好" in the blackboard". a serene winter scene in a forest. The forest is blanketed in a thick layer of snow, which has settled on the branches of the trees, creating a canopy of white. The trees, a mix of evergreens and deciduous, stand tall and silent, their forms partially obscured by the snow. The ground is a uniform white, with no visible tracks or signs of human activity. The sun is low in the sky, casting a warm glow that contrasts with the cool tones of the snow. The light filters through the trees, creating a soft, diffused illumination that highlights the texture of the snow and the contours of the trees. The overall style of the scene is naturalistic, with a focus on the tranquility and beauty of the winter landscape. a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water. A serene waterfall cascading down moss-covered rocks, its soothing sound creating a harmonious symphony with nature. diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index a4279e30f..35f0f0376 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -591,8 +591,8 @@ def OpenSoraT2V_B_111(**kwargs): norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_L_111(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_S_122(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, @@ -603,8 +603,8 @@ def OpenSoraT2V_B_122(**kwargs): norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) def OpenSoraT2V_L_122(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=20, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) # def OpenSoraT2V_S_222(**kwargs): # return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, diff --git a/opensora/npu_config.py b/opensora/npu_config.py index 70ae640b4..93410c170 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -6,6 +6,8 @@ import numpy as np import torch import subprocess +import sys +import threading import torch.distributed as dist from opensora.adaptor.zp_manager import zp_manager @@ -110,6 +112,32 @@ def __init__(self): self.rank = torch.cuda.current_device() self.world_size = self.N_NPU_PER_NODE self.print_with_rank(f"The npu_config.on_npu is {self.on_npu}") + self.bind_thread_to_cpu() + + def get_total_cores(self): + try: + total_cores = os.sysconf('SC_NPROCESSORS_ONLN') + except (AttributeError, ValueError): + total_cores = os.cpu_count() + return total_cores + + + def bind_thread_to_cpu(self): + total_cores = self.get_total_cores() + # 每个卡的核心数量 + cores_per_rank = total_cores // 8 + # 计算本地rank + local_rank = self.rank % 8 + # 计算当前 rank 的 CPU 核范围 + start_core = local_rank * cores_per_rank + end_core = start_core + cores_per_rank - 1 + # 构建 CPU 核范围字符串 + cpu_cores_range = f"{start_core}-{end_core}" + pid = os.getpid() + command = f"taskset -cp {cpu_cores_range} {pid}" + + subprocess.run(command, shell=True, check=True) + return f"Binding Cores:{self.rank}:{pid}:{cpu_cores_range}" def replace_methods(self, target_class, source_class, skip_fcns=[], only_include_fcns=None): for attr_name in dir(source_class): diff --git a/scripts/text_condition/npu/sample_image.sh b/scripts/text_condition/npu/sample_image.sh index eb09643d9..1465b5e18 100644 --- a/scripts/text_condition/npu/sample_image.sh +++ b/scripts/text_condition/npu/sample_image.sh @@ -13,7 +13,7 @@ torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --height 256 \ --width 256 \ --cache_dir "../cache_dir" \ - --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "${WEIGHT_PATH}/test140k/" \ diff --git a/scripts/text_condition/npu/train_image3d_240p.sh b/scripts/text_condition/npu/train_image3d_240p.sh index 60b174f71..8edad845f 100644 --- a/scripts/text_condition/npu/train_image3d_240p.sh +++ b/scripts/text_condition/npu/train_image3d_240p.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ From de2ef8691114c4574274df93208099b371590129 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Tue, 18 Jun 2024 09:22:49 +0000 Subject: [PATCH 047/134] rope --- .gitignore | 3 +- examples/convert_udit_to_videoudit.py | 8 +- examples/rec_video.py | 4 + opensora/dataset/t2v_datasets.py | 2 +- .../causal_vae/modeling_causalvae.py | 7 +- .../diffusion/opensora/modeling_opensora.py | 18 +- opensora/models/diffusion/opensora/modules.py | 258 +++-- opensora/models/diffusion/opensora/rope.py | 70 ++ opensora/models/diffusion/udit/modules.py | 1 - .../udit_ultra/modeling_udit_ultra.py | 284 ++++-- .../models/diffusion/udit_ultra/modules.py | 19 +- opensora/train/train_t2v_diffusers.py | 16 +- opensora/train/train_t2v_diffusers_lora.py | 915 ++++++++++++++++++ .../deepspeed_zero2_config.yaml | 2 +- scripts/accelerate_configs/zero2.json | 1 - scripts/causalvae/rec_video.sh | 6 +- .../gpu/train_imageudit_480p.sh | 0 .../gpu/train_imageuditultra_480p_new.sh | 59 ++ .../gpu/train_videouditultra_125x480p.sh | 59 ++ scripts/train_data/image_data_notext.txt | 3 + 20 files changed, 1495 insertions(+), 240 deletions(-) create mode 100644 opensora/models/diffusion/opensora/rope.py create mode 100644 opensora/train/train_t2v_diffusers_lora.py delete mode 100644 scripts/text_condition/gpu/train_imageudit_480p.sh create mode 100644 scripts/text_condition/gpu/train_imageuditultra_480p_new.sh create mode 100644 scripts/text_condition/gpu/train_videouditultra_125x480p.sh create mode 100644 scripts/train_data/image_data_notext.txt diff --git a/.gitignore b/.gitignore index 14285d661..b584b40ee 100644 --- a/.gitignore +++ b/.gitignore @@ -38,5 +38,4 @@ sft* flash* 65x256* alpha_vae -*bs32* -*bs64* \ No newline at end of file +*node* \ No newline at end of file diff --git a/examples/convert_udit_to_videoudit.py b/examples/convert_udit_to_videoudit.py index 4f2b1893a..0db28fb93 100644 --- a/examples/convert_udit_to_videoudit.py +++ b/examples/convert_udit_to_videoudit.py @@ -2,10 +2,11 @@ from safetensors.torch import load_file as safe_load -path = "diffusion_pytorch_model.safetensors" +path = "/storage/ongoing/new/Open-Sora-Plan/bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" ckpt = safe_load(path, device="cpu") new_ckpt = {} k_size = 3 +t_stride = 1 for k, v in ckpt.items(): if 'pos_embed.proj.weight' in k: new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 @@ -16,6 +17,11 @@ new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 elif 'body.0.weight' in k and 'up' in k: new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 + elif 'proj_out' in k: + if 'weight' in k: + new_v = v.repeat(t_stride, 1) # 16, 768 -> 32, 768 + elif 'bias' in k: + new_v = v.repeat(t_stride) # 16 -> 32 else: new_v = v new_ckpt[k] = new_v diff --git a/examples/rec_video.py b/examples/rec_video.py index 82b113634..20df4b378 100644 --- a/examples/rec_video.py +++ b/examples/rec_video.py @@ -96,10 +96,13 @@ def main(args: argparse.Namespace): with torch.no_grad(): x_vae = preprocess(read_video(args.video_path, args.num_frames, args.sample_rate), args.height, args.width) + print(x_vae.shape) x_vae = x_vae.to(device, dtype=torch.float16) # b c t h w latents = vae.encode(x_vae) + print(latents.shape) latents = latents.to(torch.float16) video_recon = vae.decode(latents) # b t c h w + print(video_recon.shape) custom_to_video(video_recon[0], fps=args.fps, output_file=args.rec_path) @@ -118,6 +121,7 @@ def main(args: argparse.Namespace): parser.add_argument('--sample_rate', type=int, default=1) parser.add_argument('--device', type=str, default="cuda") parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--tile_sample_min_size', type=int, default=256) parser.add_argument('--enable_tiling', action='store_true') args = parser.parse_args() diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 76391eec1..e1ae92a96 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -146,7 +146,7 @@ def get_video(self, idx): video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W - # video = torch.rand(253, 3, 720, 1280) + # video = torch.rand(125, 3, 480, 640) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] diff --git a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py index f2101fcd0..b673b0197 100644 --- a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py +++ b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py @@ -331,8 +331,10 @@ def __init__( self.tile_sample_min_size = 256 self.tile_sample_min_size_t = 65 self.tile_latent_min_size = int(self.tile_sample_min_size / (2 ** (len(hidden_size_mult) - 1))) - t_down_ratio = [i for i in encoder_temporal_downsample if len(i) > 0] - self.tile_latent_min_size_t = int((self.tile_sample_min_size_t-1) / (2 ** len(t_down_ratio))) + 1 + + # t_down_ratio = [i for i in encoder_temporal_downsample if len(i) > 0] + # self.tile_latent_min_size_t = int((self.tile_sample_min_size_t-1) / (2 ** len(t_down_ratio))) + 1 + self.tile_latent_min_size_t = 17 self.tile_overlap_factor = 0.25 self.use_tiling = False @@ -662,7 +664,6 @@ def tiled_decode2d(self, z): overlap_size = int(self.tile_latent_min_size * (1 - self.tile_overlap_factor)) blend_extent = int(self.tile_sample_min_size * self.tile_overlap_factor) row_limit = self.tile_sample_min_size - blend_extent - # Split z into overlapping 64x64 tiles and decode them separately. # The tiles have an overlap to avoid seams between tiles. rows = [] diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 35f0f0376..4f758e1c5 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -83,6 +83,7 @@ def __init__( use_additional_conditions: Optional[bool] = None, attention_mode: str = 'xformers', downsampler: str = None, + use_rope: bool = False, ): super().__init__() @@ -98,6 +99,7 @@ def __init__( ) # Set some common variables used across the board. + self.use_rope = use_rope self.use_linear_projection = use_linear_projection self.interpolation_scale_t = interpolation_scale_t self.interpolation_scale_h = interpolation_scale_h @@ -181,6 +183,7 @@ def _init_patched_inputs(self, norm_type): embed_dim=self.inner_dim, interpolation_scale=interpolation_scale, interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, ) elif self.config.downsampler is not None and len(self.config.downsampler) == 7: self.pos_embed = OverlapPatchEmbed2D( @@ -193,6 +196,7 @@ def _init_patched_inputs(self, norm_type): embed_dim=self.inner_dim, interpolation_scale=interpolation_scale, interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, ) else: @@ -206,8 +210,9 @@ def _init_patched_inputs(self, norm_type): embed_dim=self.inner_dim, interpolation_scale=interpolation_scale, interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, ) - + interpolation_scale_thw = (interpolation_scale_t, *interpolation_scale) self.transformer_blocks = nn.ModuleList( [ BasicTransformerBlock( @@ -228,6 +233,8 @@ def _init_patched_inputs(self, norm_type): attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, downsampler=self.config.downsampler, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.num_layers) ] @@ -663,13 +670,16 @@ def OpenSoraT2V_B_222(**kwargs): { 'ae': 'CausalVAEModel_4x8x8', 'attention_mode': 'xformers', - 'use_rope': False, + 'use_rope': True, 'model_max_length': 300, 'max_height': 512, 'max_width': 512, 'num_frames': 1, 'use_image_num': 0, - 'compress_kv_factor': 1 + 'compress_kv_factor': 1, + 'interpolation_scale_t': 1, + 'interpolation_scale_h': 1, + 'interpolation_scale_w': 1, } ) b = 2 @@ -685,7 +695,7 @@ def OpenSoraT2V_B_222(**kwargs): device = torch.device('cuda:0') model = OpenSoraT2V_B_122(in_channels=c, - out_channels=8, + out_channels=c, sample_size=latent_size, sample_size_t=num_frames, activation_fn="gelu-approximate", diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index e38d4c024..e4ac72fdd 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -18,6 +18,7 @@ from diffusers.models.attention_processor import Attention as Attention_ from diffusers.models.embeddings import SinusoidalPositionalEmbedding from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm +from .rope import PositionGetter3D, RoPE3D try: import torch_npu from opensora.npu_config import npu_config, set_run_dtype @@ -151,9 +152,11 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, + use_abs_pos=True, ): super().__init__() # assert num_frames == 1 + self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm @@ -199,46 +202,47 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) - # import ipdb;ipdb.set_trace() - # Interpolate positional embeddings if needed. - # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.height != height or self.width != width: - # raise NotImplementedError - pos_embed = get_2d_sincos_pos_embed( - embed_dim=self.pos_embed.shape[-1], - grid_size=(height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed = torch.from_numpy(pos_embed) - pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) - else: - pos_embed = self.pos_embed + if self.use_abs_pos: + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed - if self.num_frames != num_frames: - # import ipdb;ipdb.set_trace() - # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - temp_pos_embed = torch.from_numpy(temp_pos_embed) - temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) - else: - temp_pos_embed = self.temp_pos_embed + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed - latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] - # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - temp_pos_embed = temp_pos_embed.unsqueeze(2) - video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + if self.use_abs_pos: + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None @@ -267,9 +271,11 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, + use_abs_pos=True, ): super().__init__() # assert patch_size_t == 1 and patch_size == 1 + self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm @@ -316,46 +322,47 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) - # import ipdb;ipdb.set_trace() - # Interpolate positional embeddings if needed. - # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.height != height or self.width != width: - # raise NotImplementedError - pos_embed = get_2d_sincos_pos_embed( - embed_dim=self.pos_embed.shape[-1], - grid_size=(height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed = torch.from_numpy(pos_embed) - pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) - else: - pos_embed = self.pos_embed + if self.use_abs_pos: + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed - if self.num_frames != num_frames: - # import ipdb;ipdb.set_trace() - # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - temp_pos_embed = torch.from_numpy(temp_pos_embed) - temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) - else: - temp_pos_embed = self.temp_pos_embed + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed - latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] - # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - # temp_pos_embed = temp_pos_embed.unsqueeze(2) - # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + if self.use_abs_pos: + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None @@ -385,9 +392,11 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, + use_abs_pos=True, ): super().__init__() assert patch_size_t == 1 + self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm @@ -433,46 +442,47 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) - # import ipdb;ipdb.set_trace() - # Interpolate positional embeddings if needed. - # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.height != height or self.width != width: - # raise NotImplementedError - pos_embed = get_2d_sincos_pos_embed( - embed_dim=self.pos_embed.shape[-1], - grid_size=(height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed = torch.from_numpy(pos_embed) - pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) - else: - pos_embed = self.pos_embed + if self.use_abs_pos: + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed - if self.num_frames != num_frames: - # import ipdb;ipdb.set_trace() - # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - temp_pos_embed = torch.from_numpy(temp_pos_embed) - temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) - else: - temp_pos_embed = self.temp_pos_embed + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed - latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) video_latent, image_latent = latent[:, :num_frames], latent[:, num_frames:] - # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - # temp_pos_embed = temp_pos_embed.unsqueeze(2) - # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + if self.use_abs_pos: + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None @@ -484,8 +494,8 @@ def forward(self, latent, num_frames): return video_latent, image_latent class Attention(Attention_): - def __init__(self, downsampler, attention_mode, **kwags): - processor = AttnProcessor2_0(attention_mode=attention_mode) + def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_thw, **kwags): + processor = AttnProcessor2_0(attention_mode=attention_mode, use_rope=use_rope, interpolation_scale_thw=interpolation_scale_thw) super().__init__(processor=processor, **kwags) self.downsampler = None if downsampler: # downsampler k155_s122 @@ -523,6 +533,10 @@ def forward(self, x, attention_mask, t, h, w): else: x_dtype = x.dtype x = npu_config.run_conv3d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + + self.t = t//self.down_factor[0] + self.h = h//self.down_factor[1] + self.w = w//self.down_factor[2] x = rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) (t h w) d', t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) @@ -553,12 +567,20 @@ def forward(self, x, attention_mask, t, h, w): # import ipdb;ipdb.set_trace() b = x.shape[0] x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) - x = self.layer(x) + (x if self.down_shortcut else 0) + if npu_config is None: + x = self.layer(x) + (x if self.down_shortcut else 0) + else: + x_dtype = x.dtype + x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + + self.t = 1 + self.h = h//self.down_factor[0] + self.w = w//self.down_factor[1] + x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', h=h//self.down_factor[0], w=w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) - attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=h, w=w) attention_mask = rearrange(attention_mask, 'b 1 (h dh) (w dw) -> (b dh dw) 1 (h w)', h=h//self.down_factor[0], w=w//self.down_factor[1], @@ -576,12 +598,20 @@ class AttnProcessor2_0: Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). """ - def __init__(self, attention_mode='xformers'): - + def __init__(self, attention_mode='xformers', use_rope=False, interpolation_scale_thw=(1, 1, 1)): + self.use_rope = use_rope + self.interpolation_scale_thw = interpolation_scale_thw + if self.use_rope: + self._init_rope(interpolation_scale_thw) self.attention_mode = attention_mode if not hasattr(F, "scaled_dot_product_attention"): raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + def _init_rope(self, interpolation_scale_thw): + self.rope = RoPE3D() + self.position_getter = PositionGetter3D(interpolation_scale_thw) + def __call__( self, attn: Attention, @@ -602,6 +632,7 @@ def __call__( if attn.downsampler is not None: hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask, t=frame, h=height, w=width) + frame, height, width = attn.downsampler.t, attn.downsampler.h, attn.downsampler.w residual = hidden_states @@ -657,7 +688,14 @@ def __call__( else: query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + if query.shape == key.shape: + key = self.rope(key, pos_thw) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible @@ -844,6 +882,8 @@ def __init__( attention_out_bias: bool = True, attention_mode: str = "xformers", downsampler: str = None, + use_rope: bool = False, + interpolation_scale_thw: Tuple[int] = (1, 1, 1), ): super().__init__() self.only_cross_attention = only_cross_attention @@ -904,6 +944,8 @@ def __init__( out_bias=attention_out_bias, attention_mode=attention_mode, downsampler=downsampler, + use_rope=use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) # 2. Cross-Attn @@ -935,7 +977,9 @@ def __init__( upcast_attention=upcast_attention, out_bias=attention_out_bias, attention_mode=attention_mode, - downsampler=False + downsampler=False, + use_rope=use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) # is self-attn if encoder_hidden_states is none else: self.norm2 = None diff --git a/opensora/models/diffusion/opensora/rope.py b/opensora/models/diffusion/opensora/rope.py new file mode 100644 index 000000000..bc13b20cd --- /dev/null +++ b/opensora/models/diffusion/opensora/rope.py @@ -0,0 +1,70 @@ +import torch + +class PositionGetter3D(object): + """ return positions of patches """ + + def __init__(self, interpolation_scale_thw): + self.interpolation_scale_thw = interpolation_scale_thw + self.cache_positions = {} + + def __call__(self, b, t, h, w, device): + if not (h,w) in self.cache_positions: + x = torch.arange(w, device=device) / self.interpolation_scale_thw[2] + y = torch.arange(h, device=device) / self.interpolation_scale_thw[1] + t = torch.arange(t, device=device) / self.interpolation_scale_thw[0] + self.cache_positions[t,h,w] = torch.cartesian_prod(t, y, x) # (t, h, w, 3) + pos = self.cache_positions[t,h,w].view(1, t*h*w, 3).expand(b, -1, 3).clone() + return pos + + +class RoPE3D(torch.nn.Module): + + def __init__(self, freq=10000.0, F0=1.0, scaling_factor=1.0): + super().__init__() + self.base = freq + self.F0 = F0 + self.scaling_factor = scaling_factor + self.cache = {} + + def get_cos_sin(self, D, seq_len, device, dtype): + if (D, seq_len, device, dtype) not in self.cache: + inv_freq = 1.0 / (self.base ** (torch.arange(0, D, 2).float().to(device) / D)) + t = torch.arange(seq_len, device=device, dtype=inv_freq.dtype) + freqs = torch.einsum("i,j->ij", t, inv_freq).to(dtype) + freqs = torch.cat((freqs, freqs), dim=-1) + cos = freqs.cos() # (Seq, Dim) + sin = freqs.sin() + self.cache[D, seq_len, device, dtype] = (cos, sin) + return self.cache[D, seq_len, device, dtype] + + @staticmethod + def rotate_half(x): + x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2:] + return torch.cat((-x2, x1), dim=-1) + + def apply_rope1d(self, tokens, pos1d, cos, sin): + assert pos1d.ndim == 2 + + cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] + sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] + return (tokens * cos) + (self.rotate_half(tokens) * sin) + + def forward(self, tokens, positions): + """ + input: + * tokens: batch_size x nheads x ntokens x dim + * positions: batch_size x ntokens x 3 (t, y and x position of each token) + output: + * tokens after appplying RoPE3D (batch_size x nheads x ntokens x dim) + """ + assert tokens.size(3) % 3 == 0, "number of dimensions should be a multiple of three" + D = tokens.size(3) // 3 + assert positions.ndim == 3 and positions.shape[-1] == 3 # Batch, Seq, 3 + cos, sin = self.get_cos_sin(D, int(positions.max()) + 1, tokens.device, tokens.dtype) + # split features into three along the feature dimension, and apply rope1d on each half + t, y, x = tokens.chunk(3, dim=-1) + t = self.apply_rope1d(t, positions[:, :, 0], cos, sin) + y = self.apply_rope1d(y, positions[:, :, 1], cos, sin) + x = self.apply_rope1d(x, positions[:, :, 2], cos, sin) + tokens = torch.cat((t, y, x), dim=-1) + return tokens diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py index df8813fb0..0857bbd7d 100644 --- a/opensora/models/diffusion/udit/modules.py +++ b/opensora/models/diffusion/udit/modules.py @@ -880,7 +880,6 @@ def __init__( ) else: self.norm1 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) - self.attn1 = Attention( query_dim=dim, heads=num_attention_heads, diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py index a0abdfd62..1b8d17617 100644 --- a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -5,13 +5,14 @@ from einops import rearrange, repeat from typing import Any, Dict, Optional, Tuple from torch.nn import functional as F +from diffusers.loaders import PeftAdapterMixin from diffusers.models.transformer_2d import Transformer2DModelOutput from diffusers.utils import is_torch_version, deprecate from diffusers.configuration_utils import ConfigMixin, register_to_config from diffusers.models.modeling_utils import ModelMixin from diffusers.models.normalization import AdaLayerNormSingle from diffusers.models.embeddings import PixArtAlphaTextProjection -from opensora.models.diffusion.udit.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock +from opensora.models.diffusion.udit_ultra.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock from opensora.utils.utils import to_2tuple import math import re @@ -22,8 +23,7 @@ torch_npu = None npu_config = None - -class UDiTUltraT2V(ModelMixin, ConfigMixin): +class UDiTUltraT2V(ModelMixin, ConfigMixin, PeftAdapterMixin): """ A 2D Transformer model for image-like data. @@ -189,7 +189,6 @@ def _init_patched_inputs(self, norm_type): h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 layer_thw.append([t, h, w]) - self.encoder_level_1 = nn.ModuleList( [ BasicTransformerBlock( @@ -280,13 +279,8 @@ def _init_patched_inputs(self, norm_type): ) self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 - # self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) - self.reduce_chan_level2_norm = nn.ModuleList( - [nn.LayerNorm(int(self.inner_dim * 4)) for i in range(self.config.depth[3])] - ) - self.reduce_chan_level2 = nn.ModuleList( - [nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) for i in range(self.config.depth[3])] - ) + self.reduce_chan_level2_norm = nn.LayerNorm(int(self.inner_dim * 4), elementwise_affine=True, eps=1e-6) + self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) self.decoder_level_2 = nn.ModuleList( [ BasicTransformerBlock( @@ -317,26 +311,21 @@ def _init_patched_inputs(self, norm_type): ) self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 - # self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 2), bias=True) - self.reduce_chan_level1_norm = nn.ModuleList( - [nn.LayerNorm(int(self.inner_dim * 2))] + [nn.LayerNorm(int(self.inner_dim * 3)) for i in range(self.config.depth[4]-1)] - ) - self.reduce_chan_level1 = nn.ModuleList( - [nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 2), bias=True)] + [nn.Linear(int(self.inner_dim * 3), int(self.inner_dim * 2), bias=True) for i in range(self.config.depth[4]-1)] - ) + self.reduce_chan_level1_norm = nn.LayerNorm(int(self.inner_dim * 2), elementwise_affine=True, eps=1e-6) + self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 1), bias=True) self.decoder_level_1 = nn.ModuleList( [ BasicTransformerBlock( - self.inner_dim * 2, + self.inner_dim, self.config.num_attention_heads, - self.config.attention_head_dim * 2, + self.config.attention_head_dim, num_frames=layer_thw[0][0], height=layer_thw[0][1], width=layer_thw[0][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, - cross_attention_dim=self.inner_dim * 2, + cross_attention_dim=self.inner_dim, activation_fn=self.config.activation_fn, num_embeds_ada_norm=self.config.num_embeds_ada_norm, attention_bias=self.config.attention_bias, @@ -360,10 +349,10 @@ def _init_patched_inputs(self, norm_type): 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels ) elif self.config.norm_type == "ada_norm_single": - self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) - self.scale_shift_table = nn.Parameter(torch.randn(2, 2 * self.inner_dim) / (2 * self.inner_dim)**0.5) + self.norm_out = nn.LayerNorm(self.inner_dim, elementwise_affine=False, eps=1e-6) + self.scale_shift_table = nn.Parameter(torch.randn(2, self.inner_dim) / (self.inner_dim)**0.5) self.proj_out = nn.Linear( - 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels + self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels ) # PixArt-Alpha blocks. @@ -489,10 +478,6 @@ def forward( encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-1]) - # 1. Input added_cond_kwargs = {"resolution": None, "aspect_ratio": None} hidden_states, encoder_hidden_states_1, encoder_hidden_states_2, encoder_hidden_states_3, \ @@ -503,6 +488,8 @@ def forward( frame, height, width = frame // self.config.patch_size_t, \ (height + pad_h_0) // (self.config.patch_size), (width + pad_w_0) // (self.config.patch_size) + + assert not torch.any(torch.isnan(hidden_states)), 'after _operate_on_patched_inputs' def create_custom_forward(module, return_dict=None): def custom_forward(*inputs): if return_dict is not None: @@ -515,7 +502,6 @@ def custom_forward(*inputs): # encoder_1 out_enc_level1 = hidden_states # import ipdb;ipdb.set_trace() - out_enc_level1_skips = [] if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -531,7 +517,6 @@ def custom_forward(*inputs): class_labels, **ckpt_kwargs, ) - out_enc_level1_skips.append(out_enc_level1) else: for block in self.encoder_level_1: out_enc_level1 = block( @@ -543,7 +528,6 @@ def custom_forward(*inputs): cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, ) - out_enc_level1_skips.append(out_enc_level1) pad_h_1, pad_w_1 = height % 4, width % 4 inp_enc_level2, attention_bias, attention_mask = self.down1_2(out_enc_level1, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) @@ -552,10 +536,6 @@ def custom_forward(*inputs): # encoder_2 out_enc_level2 = inp_enc_level2 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - out_enc_level2_skips = [] if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} @@ -572,8 +552,6 @@ def custom_forward(*inputs): class_labels, **ckpt_kwargs, ) - out_enc_level2_skips.append(out_enc_level2) - else: for block in self.encoder_level_2: out_enc_level2 = block( @@ -585,17 +563,12 @@ def custom_forward(*inputs): cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, ) - out_enc_level2_skips.append(out_enc_level2) pad_h_2, pad_w_2 = height % 4, width % 4 # import ipdb;ipdb.set_trace() inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - # latent latent = inp_enc_level3 if self.training and self.gradient_checkpointing: @@ -625,29 +598,22 @@ def custom_forward(*inputs): cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, ) + # decoder_2 # import ipdb;ipdb.set_trace() inp_dec_level2, attention_bias, attention_mask = self.up3_2(latent, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_2, width * 2 - pad_w_2 - # inp_dec_level2 = torch.cat([inp_dec_level2, out_enc_level2], 2) - # inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) + inp_dec_level2 = torch.cat([inp_dec_level2, out_enc_level2], 2) + inp_dec_level2 = self.reduce_chan_level2_norm(inp_dec_level2) + inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) out_dec_level2 = inp_dec_level2 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} - for idx, block in enumerate(self.decoder_level_2): - inp_dec_level2 = out_enc_level2_skips.pop() - inp_dec_level2 = torch.cat([inp_dec_level2, out_dec_level2], 2) - inp_dec_level2 = self.reduce_chan_level2_norm[idx](inp_dec_level2) - inp_dec_level2 = self.reduce_chan_level2[idx](inp_dec_level2) - out_dec_level2 = inp_dec_level2 + for block in self.decoder_level_2: out_dec_level2 = torch.utils.checkpoint.checkpoint( create_custom_forward(block), out_dec_level2, @@ -660,12 +626,7 @@ def custom_forward(*inputs): **ckpt_kwargs, ) else: - for idx, block in enumerate(self.decoder_level_2): - inp_dec_level2 = out_enc_level2_skips.pop() - inp_dec_level2 = torch.cat([inp_dec_level2, out_dec_level2], 2) - inp_dec_level2 = self.reduce_chan_level2_norm[idx](inp_dec_level2) - inp_dec_level2 = self.reduce_chan_level2[idx](inp_dec_level2) - out_dec_level2 = inp_dec_level2 + for block in self.decoder_level_2: out_dec_level2 = block( out_dec_level2, attention_mask=attention_bias, @@ -681,64 +642,52 @@ def custom_forward(*inputs): # import ipdb;ipdb.set_trace() inp_dec_level1, attention_bias, attention_mask = self.up2_1(out_dec_level2, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_1, width * 2 - pad_w_1 - # inp_dec_level1 = torch.cat([inp_dec_level1, out_enc_level1], 2) - # inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) + inp_dec_level1 = torch.cat([inp_dec_level1, out_enc_level1], 2) + inp_dec_level1 = self.reduce_chan_level1_norm(inp_dec_level1) + inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) out_dec_level1 = inp_dec_level1 - if npu_config is not None and attention_mask is not None: - attention_bias = npu_config.get_attention_mask(attention_bias, attention_mask.shape[-1]) - encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask[:, 0:1, :], attention_mask.shape[-1]) - if self.training and self.gradient_checkpointing: ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} - for idx, block in enumerate(self.decoder_level_1): - inp_dec_level1 = out_enc_level1_skips.pop() - inp_dec_level1 = torch.cat([inp_dec_level1, out_dec_level1], 2) - inp_dec_level1 = self.reduce_chan_level1_norm[idx](inp_dec_level1) - inp_dec_level1 = self.reduce_chan_level1[idx](inp_dec_level1) - out_dec_level1 = inp_dec_level1 + for block in self.decoder_level_1: out_dec_level1 = torch.utils.checkpoint.checkpoint( create_custom_forward(block), out_dec_level1, attention_bias, - encoder_hidden_states_2, + encoder_hidden_states_1, encoder_attention_mask, - timestep_2, + timestep_1, cross_attention_kwargs, class_labels, **ckpt_kwargs, ) else: - for idx, block in enumerate(self.decoder_level_1): - inp_dec_level1 = out_enc_level1_skips.pop() - inp_dec_level1 = torch.cat([inp_dec_level1, out_dec_level1], 2) - inp_dec_level1 = self.reduce_chan_level1_norm[idx](inp_dec_level1) - inp_dec_level1 = self.reduce_chan_level1[idx](inp_dec_level1) - out_dec_level1 = inp_dec_level1 + for block in self.decoder_level_1: out_dec_level1 = block( out_dec_level1, attention_mask=attention_bias, - encoder_hidden_states=encoder_hidden_states_2, + encoder_hidden_states=encoder_hidden_states_1, encoder_attention_mask=encoder_attention_mask, - timestep=timestep_2, + timestep=timestep_1, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, ) - # import ipdb;ipdb.set_trace() + assert not torch.any(torch.isnan(out_dec_level1)), 'after out_dec_level1' # 3. Output output = self._get_output_for_patched_inputs( hidden_states=out_dec_level1, - timestep=timestep_2, + timestep=timestep_1, class_labels=class_labels, - embedded_timestep=embedded_timestep_2, + embedded_timestep=embedded_timestep_1, num_frames=frame, height=height, width=width, ) # b c t h w + assert not torch.any(torch.isnan(output)), 'after output' frame, height, width = frame * self.config.patch_size_t, height * self.config.patch_size - pad_h_0, width * self.config.patch_size - pad_w_0 output = output[:, :, :frame, :height, :width] if not return_dict: @@ -815,51 +764,58 @@ def _get_output_for_patched_inputs( # output = output[:, :, 1:] return output + def UDiTUltraT2V_S_111(**kwargs): return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTUltraT2V_S_122(**kwargs): + return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=2, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_B_111(**kwargs): return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_111(**kwargs): return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_211(**kwargs): return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_122(**kwargs): return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_222(**kwargs): return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_XL_111(**kwargs): return UDiTUltraT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_XXL_111(**kwargs): return UDiTUltraT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=2048, **kwargs) + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) UDiT_Ultra_models = { - "UDiTUltraT2V-S/111": UDiTUltraT2V_S_111, # 0.19B 0.3B if video - "UDiTUltraT2V-B/111": UDiTUltraT2V_B_111, # 0.73B 1.2B if video - "UDiTUltraT2V-L/111": UDiTUltraT2V_L_111, # 2.3B 3.4B if video - "UDiTUltraT2V-L/211": UDiTUltraT2V_L_211, # 2.3B 3.4B if video - "UDiTUltraT2V-L/122": UDiTUltraT2V_L_122, # 2.3B 3.4B if video - "UDiTUltraT2V-L/222": UDiTUltraT2V_L_222, # 2.3B 3.4B if video + "UDiTUltraT2V-S/111": UDiTUltraT2V_S_111, # 0.2B 0.3B if video + "UDiTUltraT2V-S/122": UDiTUltraT2V_S_122, # 0.2B 0.3B if video + "UDiTUltraT2V-B/111": UDiTUltraT2V_B_111, # 0.7B 1.1B if video + "UDiTUltraT2V-L/111": UDiTUltraT2V_L_111, # 2.2B 3.3B if video + "UDiTUltraT2V-L/211": UDiTUltraT2V_L_211, # 2.2B 3.3B if video + "UDiTUltraT2V-L/122": UDiTUltraT2V_L_122, # 2.2B 3.3B if video + "UDiTUltraT2V-L/222": UDiTUltraT2V_L_222, # 2.2B 3.3B if video "UDiTUltraT2V-XL/111": UDiTUltraT2V_XL_111, # 5.1B 7B if video - "UDiTUltraT2V-XXL/111": UDiTUltraT2V_XXL_111, # 9.4B 11.3B if video + "UDiTUltraT2V-XXL/111": UDiTUltraT2V_XXL_111, # 9.3B 11.2B if video } UDiT_Ultra_models_class = { "UDiTUltraT2V-S/111": UDiTUltraT2V, + "UDiTUltraT2V-S/122": UDiTUltraT2V, "UDiTUltraT2V-B/111": UDiTUltraT2V, "UDiTUltraT2V-L/111": UDiTUltraT2V, "UDiTUltraT2V-L/211": UDiTUltraT2V, @@ -869,6 +825,46 @@ def UDiTUltraT2V_XXL_111(**kwargs): "UDiTUltraT2V-XXL/111": UDiTUltraT2V, } + + +def maybe_zero_3(param, ignore_status=False, name=None): + from deepspeed import zero + from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus + if hasattr(param, "ds_id"): + if param.ds_status == ZeroParamStatus.NOT_AVAILABLE: + if not ignore_status: + logging.warning(f"{name}: param.ds_status != ZeroParamStatus.NOT_AVAILABLE: {param.ds_status}") + with zero.GatheredParameters([param]): + param = param.data.detach().cpu().clone() + else: + param = param.detach().cpu().clone() + return param + +# Borrowed from peft.utils.get_peft_model_state_dict +def get_peft_state_maybe_zero_3(named_params, bias): + if bias == "none": + to_return = {k: t for k, t in named_params if "lora_" in k} + elif bias == "all": + to_return = {k: t for k, t in named_params if "lora_" in k or "bias" in k} + elif bias == "lora_only": + to_return = {} + maybe_lora_bias = {} + lora_bias_names = set() + for k, t in named_params: + if "lora_" in k: + to_return[k] = t + bias_name = k.split("lora_")[0] + "bias" + lora_bias_names.add(bias_name) + elif "bias" in k: + maybe_lora_bias[k] = t + for k, t in maybe_lora_bias: + if bias_name in lora_bias_names: + to_return[bias_name] = t + else: + raise NotImplementedError + to_return = {k: maybe_zero_3(v, ignore_status=True) for k, v in to_return.items()} + return to_return + if __name__ == '__main__': import sys from opensora.models.ae import ae_channel_config, ae_stride_config @@ -900,7 +896,7 @@ def UDiTUltraT2V_XXL_111(**kwargs): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:1') - model = UDiTUltraT2V_L_122(in_channels=c, + model = UDiTUltraT2V_S_122(in_channels=c, out_channels=c, sample_size=latent_size, sample_size_t=num_frames, @@ -921,11 +917,12 @@ def UDiTUltraT2V_XXL_111(**kwargs): print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') try: - path = "/storage/ongoing/new/Open-Sora-Plan/bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22/checkpoint-50/model_ema/diffusion_pytorch_model.safetensors" + path = "bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" from safetensors.torch import load_file as safe_load ckpt = safe_load(path, device="cpu") new_ckpt = {} k_size = 3 + t_stride = 1 for k, v in ckpt.items(): if 'pos_embed.proj.weight' in k: new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 @@ -936,6 +933,11 @@ def UDiTUltraT2V_XXL_111(**kwargs): new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 elif 'body.0.weight' in k and 'up' in k: new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 + elif 'proj_out' in k: + if 'weight' in k: + new_v = v.repeat(t_stride, 1) # 16, 768 -> 32, 768 + elif 'bias' in k: + new_v = v.repeat(t_stride) # 16 -> 32 else: new_v = v new_ckpt[k] = new_v @@ -949,10 +951,86 @@ def UDiTUltraT2V_XXL_111(**kwargs): attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L timestep = torch.randint(0, 1000, (b,), device=device) + + from peft import LoraConfig, PeftModel + from peft import get_peft_model + lora_save_path = 'lora' + lora_config = LoraConfig( + r=16, + lora_alpha=16, + init_lora_weights="gaussian", + target_modules=["to_k", "to_q", "to_v", "to_out.0"], + ) + model_lora = get_peft_model(model, lora_config) + + from copy import deepcopy + from diffusers.training_utils import EMAModel + + class EMAModel_LoRA(EMAModel): + def __init__(self, lora_config, **kwargs): + super().__init__(**kwargs) + self.lora_config = lora_config + + @classmethod + def from_pretrained(cls, path, model_cls, lora_config) -> "EMAModel": + load_config, ema_kwargs = model_cls.load_config(path, return_unused_kwargs=True) + model = model_cls.from_config(path) + model = get_peft_model(model, lora_config) + from safetensors.torch import load_file as safe_load + main_and_lora = safe_load(os.path.join(path, 'diffusion_pytorch_model.safetensors'), device="cpu") + model.base_model.model.load_state_dict(main_and_lora) + ema_model = cls(lora_config, parameters=model.parameters(), model_cls=model_cls, model_config=model.config) + + ema_model.load_state_dict(ema_kwargs) + return ema_model + + def save_pretrained(self, path): + if self.model_cls is None: + raise ValueError("`save_pretrained` can only be used if `model_cls` was defined at __init__.") + + if self.model_config is None: + raise ValueError("`save_pretrained` can only be used if `model_config` was defined at __init__.") + + model = self.model_cls.from_config(self.model_config) + model = get_peft_model(model, self.lora_config) + state_dict = self.state_dict() + state_dict.pop("shadow_params", None) + model.base_model.model.register_to_config(**state_dict) + self.copy_to(model.parameters()) + model.base_model.model.save_pretrained(path) + model.save_pretrained(path) + + + ema_model = deepcopy(model_lora) + ema_model_lora = EMAModel_LoRA(lora_config, parameters=ema_model.parameters(), update_after_step=0, model_cls=UDiTUltraT2V, model_config=ema_model.config) + + for k, v in zip(ema_model_lora.shadow_params, model_lora.parameters()): + assert torch.allclose(v, k) + + ema_model_lora.save_pretrained("model_ema_lora") + + ema_model_load_lora = EMAModel_LoRA.from_pretrained("model_ema_lora", UDiTUltraT2V, lora_config) + ema_model_lora.load_state_dict(ema_model_load_lora.state_dict()) + ema_model_lora.to(device) + + + + + state_dict = get_peft_state_maybe_zero_3(model_lora.named_parameters(), lora_config.bias) + model_lora.save_pretrained(lora_save_path, state_dict=state_dict) + model_load_lora = PeftModel.from_pretrained(model, lora_save_path) + for k, v in model_load_lora.state_dict().items(): + assert torch.allclose(v, model_lora.state_dict()[k]) + + for k, v in zip(ema_model_lora.shadow_params, model_lora.parameters()): + assert torch.allclose(v, k) + + print('Merging LoRA weights...') + model_load_lora_merge = model_load_lora.merge_and_unload() model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) with torch.no_grad(): - output = model(**model_kwargs) + output = model_lora(**model_kwargs) print(output[0].shape) diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index 9d2e20242..a0bde956f 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -378,8 +378,8 @@ def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attentio padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True, num_frames=num_frames, height=height, width=width) - self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) - self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) class DownSampler3d(nn.Module): @@ -394,6 +394,7 @@ def __init__(self, *args, **kwargs): self.layer = nn.Conv3d(*args, **kwargs) def forward(self, x, attention_mask): + # import ipdb;ipdb.set_trace() b = x.shape[0] x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) if npu_config is None: @@ -480,7 +481,7 @@ def __call__( if len(args) > 0 or kwargs.get("scale", None) is not None: deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." deprecate("scale", "1.0.0", deprecation_message) - + if attn.downsampler is not None: hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask) @@ -542,9 +543,9 @@ def __call__( value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) # qk norm - query = attn.q_norm(query) - key = attn.k_norm(key) - query = query * (head_dim ** -0.5) + # query = attn.q_norm(query) + # key = attn.k_norm(key) + # query = query * (head_dim ** -0.5) if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible attention_mask = None @@ -637,7 +638,8 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) - x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0), mode='reflect') + # x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0), mode='reflect') + x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0)) if npu_config is None: x = self.body(x) else: @@ -688,7 +690,8 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) - x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') + # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') + x = F.pad(x, (0, pad_w, 0, pad_h)) if npu_config is None: x = self.body(x) else: diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index f1d2887e1..9b8840f30 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -264,21 +264,27 @@ def main(args): # # use pretrained model? if args.pretrained: + model_state_dict = model.state_dict() if 'safetensors' in args.pretrained: # pixart series from safetensors.torch import load_file as safe_load # import ipdb;ipdb.set_trace() - checkpoint = safe_load(args.pretrained, device="cpu") + pretrained_checkpoint = safe_load(args.pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") # repeat = model.pos_embed.proj.weight.shape[2] # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] else: # latest stage training weight - checkpoint = torch.load(args.pretrained, map_location='cpu')['model'] - model_state_dict = model.state_dict() + checkpoint = torch.load(args.pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') - logger.info(f'Successfully load {len(model.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + logger.info(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') # Freeze vae and text encoders. ae.vae.requires_grad_(False) @@ -524,7 +530,7 @@ def sync_gradients_info(loss): if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: if progress_info.global_step % args.checkpointing_steps == 0: # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` - if args.checkpoints_total_limit is not None: + if accelerator.is_main_process and args.checkpoints_total_limit is not None: checkpoints = os.listdir(args.output_dir) checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) diff --git a/opensora/train/train_t2v_diffusers_lora.py b/opensora/train/train_t2v_diffusers_lora.py new file mode 100644 index 000000000..497fbab56 --- /dev/null +++ b/opensora/train/train_t2v_diffusers_lora.py @@ -0,0 +1,915 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +import logging +import math +import os +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + pass +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import DistributedType, ProjectConfiguration, set_seed +from huggingface_hub import create_repo +from packaging import version +from tqdm.auto import tqdm +from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.utils.dataset_utils import Collate +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.sample.pipeline_opensora import OpenSoraPipeline + + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + + +@torch.inference_mode() +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): + validation_prompt = [ + "a word \"你好\" in the blackboard", + "a cat wearing sunglasses and working as a lifeguard at pool.", + "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." + ] + logger.info(f"Running validation....\n") + model = accelerator.unwrap_model(model) + scheduler = DPMSolverMultistepScheduler() + opensora_pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model).to(device=accelerator.device) + videos = [] + for prompt in validation_prompt: + logger.info('Processing the ({}) prompt'.format(prompt)) + video = opensora_pipeline(prompt, + num_frames=args.num_frames, + # num_frames=1, + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=150, + ).images + videos.append(video[0]) + # import ipdb;ipdb.set_trace() + gc.collect() + torch.cuda.empty_cache() + videos = torch.stack(videos).numpy() + videos = rearrange(videos, 'b t h w c -> b t c h w') + for tracker in accelerator.trackers: + if tracker.name == "tensorboard": + if videos.shape[1] == 1: + assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + np_images = np.stack([np.asarray(img) for img in images]) + tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") + else: + np_videos = np.stack([np.asarray(vid) for vid in videos]) + tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) + if tracker.name == "wandb": + import wandb + if videos.shape[1] == 1: + # assert args.num_frames == 1 + images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') + # import ipdb;ipdb.set_trace() + logs = { + f"{'ema_' if ema else ''}validation": [ + wandb.Image(image, caption=f"{i}: {prompt}") + for i, (image, prompt) in enumerate(zip(images, validation_prompt)) + ] + } + else: + logs = { + f"{'ema_' if ema else ''}validation": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=30) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ] + } + # import ipdb;ipdb.set_trace() + if hasattr(model.pos_embed, 'temp_embed_gate'): + logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + tracker.log(logs, step=global_step) + + del opensora_pipeline + gc.collect() + torch.cuda.empty_cache() + + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss + + +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + logging_dir = Path(args.output_dir, args.logging_dir) + + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) + npu_config.seed_everything() + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + kwargs = {} + ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae], + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + interpolation_scale_t=args.interpolation_scale_t, + downsampler=args.downsampler, + # compress_kv_factor=args.compress_kv_factor, + # use_rope=args.use_rope, + # model_max_length=args.model_max_length, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # # use pretrained model? + if args.pretrained: + model_state_dict = model.state_dict() + if 'safetensors' in args.pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(args.pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(args.pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + logger.info(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # Freeze vae and text encoders. + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + model.requires_grad_(False) + # # Set model as trainable. + # model.train() + + noise_scheduler = DDPMScheduler() + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + ae.vae.to(accelerator.device, dtype=torch.float32) + # ae.vae.to(accelerator.device, dtype=weight_dtype) + text_enc.to(accelerator.device, dtype=weight_dtype) + + + + + # now we will add new LoRA weights to the attention layers + # Set correct lora layers + if args.enable_lora: + lora_config = LoraConfig( + r=args.rank, + lora_alpha=args.rank, + init_lora_weights="gaussian", + target_modules=["to_k", "to_q", "to_v", "to_out.0"], + ) + model = get_peft_model(model, lora_config) + # Create EMA for the unet. + if args.use_ema: + ema_model = deepcopy(model) + if args.enable_ema: + ema_model = EMAModel_LoRA(lora_config, parameters=ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + else: + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + if args.enable_lora: + ema_model.save_pretrained(os.path.join(output_dir, "model_ema_lora")) + else: + ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + if args.enable_lora: + state_dict = get_peft_state_maybe_zero_3(model.named_parameters(), lora_config.bias) + model.save_pretrained(os.path.join(output_dir, "model_lora"), state_dict=state_dict) + else: + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + if args.enable_lora: + load_model = EMAModel_LoRA.from_pretrained(os.path.join(input_dir, "model_ema_lora"), Diffusion_models_class[args.model], lora_config) + ema_model.load_state_dict(load_model.state_dict()) + ema_model.to(accelerator.device) + del load_model + + else: + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) + ema_model.load_state_dict(load_model.state_dict()) + ema_model.to(accelerator.device) + del load_model + + for i in range(len(models)): + # pop models so that they are not loaded again + model = models.pop() + if args.enable_lora: + model = PeftModel.from_pretrained(model, os.path.join(input_dir, "model_lora")) + else: + # load diffusers style into model + load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + params_to_optimize = model.parameters() + # Optimizer creation + if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): + logger.warning( + f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]." + "Defaulting to adamW" + ) + args.optimizer = "adamw" + + if args.use_8bit_adam and not args.optimizer.lower() == "adamw": + logger.warning( + f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " + f"set to {args.optimizer.lower()}" + ) + + if args.optimizer.lower() == "adamw": + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + if args.optimizer.lower() == "prodigy": + try: + import prodigyopt + except ImportError: + raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`") + + optimizer_class = prodigyopt.Prodigy + + if args.learning_rate <= 0.1: + logger.warning( + "Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0" + ) + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + beta3=args.prodigy_beta3, + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + decouple=args.prodigy_decouple, + use_bias_correction=args.prodigy_use_bias_correction, + safeguard_warmup=args.prodigy_safeguard_warmup, + ) + + # Setup data: + train_dataset = getdataset(args) + train_dataloader = torch.utils.data.DataLoader( + train_dataset, + shuffle=True, + # pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + prefetch_factor=8 + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + model, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_model.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + if accelerator.is_main_process: + accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {model}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_model.step(model.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + one_step_duration = end_time - start_time + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, " + f"train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) + progress_info.train_loss = 0.0 + + # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. + if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if accelerator.is_main_process and args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(model_input, model_kwargs, prof): + global start_time + start_time = time.time() + + noise = torch.randn_like(model_input) + if args.noise_offset: + # https://www.crosslabs.org//blog/diffusion-with-offset-noise + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1), + device=model_input.device) + + bsz = model_input.shape[0] + # Sample a random timestep for each image without bias. + timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + + # Add noise to the model input according to the noise magnitude at each timestep + # (this is the forward diffusion process) + + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) + + model_pred = model( + noisy_model_input, + timesteps, + **model_kwargs + )[0] + # Get the target for loss depending on the prediction type + if args.prediction_type is not None: + # set prediction_type of scheduler if defined + noise_scheduler.register_to_config(prediction_type=args.prediction_type) + + if noise_scheduler.config.prediction_type == "epsilon": + target = noise + elif noise_scheduler.config.prediction_type == "v_prediction": + target = noise_scheduler.get_velocity(model_input, noise, timesteps) + elif noise_scheduler.config.prediction_type == "sample": + # We set the target to latents here, but the model_pred will return the noise sample prediction. + target = model_input + # We will have to subtract the noise residual from the prediction to get the target sample. + model_pred = model_pred - noise + else: + raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + + if args.snr_gamma is None: + loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + else: + # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. + # Since we predict the noise instead of x_0, the original formulation is slightly changed. + # This is discussed in Section 4.2 of the same paper. + snr = compute_snr(noise_scheduler, timesteps) + mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min( + dim=1 + )[0] + if noise_scheduler.config.prediction_type == "epsilon": + mse_loss_weights = mse_loss_weights / snr + elif noise_scheduler.config.prediction_type == "v_prediction": + mse_loss_weights = mse_loss_weights / (snr + 1) + + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights + loss = loss.mean() + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = model.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + if prof is not None: + prof.step() + + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + for tracker in accelerator.trackers: + if tracker.name == "wandb": + if progress_info.global_step % args.checkpointing_steps != 0: + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + + if progress_info.global_step % args.checkpointing_steps == 0: + + if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step) + + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + if npu_config is None: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + + return loss + + def train_one_step(step_, data_item_, prof_=None): + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+num_images H W, 16 + 4 + + attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W + input_ids = input_ids.to(accelerator.device) # B 1+num_images L + cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1+num_images L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D + cond = cond.reshape(B, N, L, -1) + + # Map input images to latent space + normalize latents + if args.use_image_num == 0: + x = ae.encode(x) # B C T H W + else: + videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] + videos = ae.encode(videos) # B C T H W + images = rearrange(images, 'b c t h w -> (b t) c 1 h w') + images = ae.encode(images) + images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + + with accelerator.accumulate(model): + assert not torch.any(torch.isnan(x)), 'after vae' + x = x.to(weight_dtype) + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + if train_one_step(step, data_item, prof_): + break + + if step >= 2 and torch_npu is not None and npu_config is not None: + npu_config.free_mm() + + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # dataset & dataloader + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--model_max_length", type=int, default=512) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + + # text encoder & vae & diffusion model + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument("--downsampler", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + + # diffusion setting + parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + + # validation & logs + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--checkpointing_steps", type=int, default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument("--resume_from_checkpoint", type=str, default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument("--logging_dir", type=str, default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument("--report_to", type=str, default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + # optimizer & scheduler + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") + parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--prodigy_beta3", type=float, default=None, + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", + ) + parser.add_argument("--lr_scheduler", type=str, default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument("--allow_tf32", action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + + args = parser.parse_args() + main(args) diff --git a/scripts/accelerate_configs/deepspeed_zero2_config.yaml b/scripts/accelerate_configs/deepspeed_zero2_config.yaml index 2b8cfd2ad..b15390113 100644 --- a/scripts/accelerate_configs/deepspeed_zero2_config.yaml +++ b/scripts/accelerate_configs/deepspeed_zero2_config.yaml @@ -5,7 +5,7 @@ deepspeed_config: fsdp_config: {} machine_rank: 0 main_process_ip: null -main_process_port: 29501 +main_process_port: 29503 main_training_function: main num_machines: 1 num_processes: 8 diff --git a/scripts/accelerate_configs/zero2.json b/scripts/accelerate_configs/zero2.json index 8dcdd154d..3377cb4b2 100644 --- a/scripts/accelerate_configs/zero2.json +++ b/scripts/accelerate_configs/zero2.json @@ -10,7 +10,6 @@ "bf16": { "enabled": "auto" }, - "communication_data_type": "fp32", "gradient_clipping": 1.0, "train_micro_batch_size_per_gpu": "auto", "train_batch_size": "auto", diff --git a/scripts/causalvae/rec_video.sh b/scripts/causalvae/rec_video.sh index 7f7ffcb10..52c7f161f 100644 --- a/scripts/causalvae/rec_video.sh +++ b/scripts/causalvae/rec_video.sh @@ -1,11 +1,11 @@ CUDA_VISIBLE_DEVICES=0 python examples/rec_video.py \ --ae_path "/storage/dataset/test140k" \ - --video_path /storage/dataset/mixkit/Trains/mixkit-train-passing-the-rails-4462_resize1080p.mp4 \ + --video_path /storage/dataset/test.mp4 \ --rec_path rec.mp4 \ --device cuda \ --sample_rate 1 \ - --num_frames 125 \ + --num_frames 65 \ --height 480 \ --width 640 \ --ae CausalVAEModel_4x8x8 \ - --enable_tiling + --enable_tiling --tile_overlap_factor 0.125 --tile_sample_min_size 256 diff --git a/scripts/text_condition/gpu/train_imageudit_480p.sh b/scripts/text_condition/gpu/train_imageudit_480p.sh deleted file mode 100644 index e69de29bb..000000000 diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new.sh b/scripts/text_condition/gpu/train_imageuditultra_480p_new.sh new file mode 100644 index 000000000..2d4c7fd80 --- /dev/null +++ b/scripts/text_condition/gpu/train_imageuditultra_480p_new.sh @@ -0,0 +1,59 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data_notext.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" \ + --checkpoints_total_limit 3 \ + --pretrained "bs32_4node_240p_lr2e-5_snr5_noioff0.02_ema_udit22_ds22_mt5xxl/checkpoint-32500/model/diffusion_pytorch_model.safetensors" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_125x480p.sh b/scripts/text_condition/gpu/train_videouditultra_125x480p.sh new file mode 100644 index 000000000..ac08e4eb1 --- /dev/null +++ b/scripts/text_condition/gpu/train_videouditultra_125x480p.sh @@ -0,0 +1,59 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_1node_125x480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data_notext.txt" \ + --sample_rate 1 \ + --num_frames 125 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs2_1node_125x480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" \ + --checkpoints_total_limit 3 \ + --pretrained "/storage/ongoing/new/Open-Sora-Plan/convert_weight.pt" \ No newline at end of file diff --git a/scripts/train_data/image_data_notext.txt b/scripts/train_data/image_data_notext.txt new file mode 100644 index 000000000..4e3a08d17 --- /dev/null +++ b/scripts/train_data/image_data_notext.txt @@ -0,0 +1,3 @@ +/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json +/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json +/storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json \ No newline at end of file From c84811b031ee131aa5d507fab70592782a7d9d60 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Tue, 18 Jun 2024 09:29:53 +0000 Subject: [PATCH 048/134] pass use_rope --- opensora/train/train_t2v_diffusers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 9b8840f30..05e0e10e2 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -257,7 +257,7 @@ def main(args): interpolation_scale_t=args.interpolation_scale_t, downsampler=args.downsampler, # compress_kv_factor=args.compress_kv_factor, - # use_rope=args.use_rope, + use_rope=args.use_rope, # model_max_length=args.model_max_length, ) model.gradient_checkpointing = args.gradient_checkpointing From 6f92952c5006ccadc019f14c03e96d48a91015ec Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Tue, 18 Jun 2024 13:06:40 +0000 Subject: [PATCH 049/134] fix rope scaling --- .../diffusion/opensora/modeling_opensora.py | 37 +-- opensora/models/diffusion/opensora/modules.py | 18 +- opensora/models/diffusion/opensora/rope.py | 34 +-- .../udit_ultra/modeling_udit_ultra.py | 112 +++++--- .../models/diffusion/udit_ultra/modules.py | 259 ++++++++++-------- opensora/models/diffusion/udit_ultra/rope.py | 72 +++++ opensora/models/text_encoder/__init__.py | 4 +- opensora/train/train_t2v_diffusers.py | 4 +- opensora/train/train_t2v_diffusers_lora.py | 3 - 9 files changed, 339 insertions(+), 204 deletions(-) create mode 100644 opensora/models/diffusion/udit_ultra/rope.py diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 4f758e1c5..98400adaf 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -590,28 +590,28 @@ def _get_output_for_patched_inputs( return output def OpenSoraT2V_S_111(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + return OpenSoraT2V(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) def OpenSoraT2V_B_111(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) def OpenSoraT2V_L_111(**kwargs): - return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=1, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2304, **kwargs) def OpenSoraT2V_S_122(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + return OpenSoraT2V(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) def OpenSoraT2V_B_122(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) def OpenSoraT2V_L_122(**kwargs): - return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2304, **kwargs) # def OpenSoraT2V_S_222(**kwargs): # return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, @@ -619,8 +619,8 @@ def OpenSoraT2V_L_122(**kwargs): def OpenSoraT2V_B_222(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=16, patch_size_t=2, patch_size=2, - norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) # def OpenSoraT2V_L_222(**kwargs): # return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, @@ -694,7 +694,7 @@ def OpenSoraT2V_B_222(**kwargs): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:0') - model = OpenSoraT2V_B_122(in_channels=c, + model = OpenSoraT2V_L_122(in_channels=c, out_channels=c, sample_size=latent_size, sample_size_t=num_frames, @@ -710,7 +710,12 @@ def OpenSoraT2V_B_222(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k33_s11').to(device) + downsampler='k33_s11', + interpolation_scale_t=args.interpolation_scale_t, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + use_rope=args.use_rope, + ).to(device) try: path = "PixArt-Alpha-XL-2-512.safetensors" diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index e4ac72fdd..abebf55ee 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -525,7 +525,6 @@ def __init__(self, *args, **kwargs): self.layer = nn.Conv3d(*args, **kwargs) def forward(self, x, attention_mask, t, h, w): - # import ipdb;ipdb.set_trace() b = x.shape[0] x = rearrange(x, 'b (t h w) d -> b d t h w', t=t, h=h, w=w) if npu_config is None: @@ -547,10 +546,10 @@ def forward(self, x, attention_mask, t, h, w): t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) return x, attention_mask + def reverse(self, x, t, h, w): - # import ipdb;ipdb.set_trace() x = rearrange(x, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', - t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], + t=t, h=h, w=w, dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) return x @@ -564,7 +563,6 @@ def __init__(self, *args, **kwargs): self.layer = nn.Conv2d(*args, **kwargs) def forward(self, x, attention_mask, t, h, w): - # import ipdb;ipdb.set_trace() b = x.shape[0] x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) if npu_config is None: @@ -586,10 +584,10 @@ def forward(self, x, attention_mask, t, h, w): h=h//self.down_factor[0], w=w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) return x, attention_mask + def reverse(self, x, t, h, w): - # import ipdb;ipdb.set_trace() x = rearrange(x, '(b t dh dw) (h w) d -> b (t h dh w dw) d', - t=t, h=h//self.down_factor[0], w=w//self.down_factor[1], + t=t, h=h, w=w, dh=self.down_factor[0], dw=self.down_factor[1]) return x @@ -609,8 +607,8 @@ def __init__(self, attention_mode='xformers', use_rope=False, interpolation_scal def _init_rope(self, interpolation_scale_thw): - self.rope = RoPE3D() - self.position_getter = PositionGetter3D(interpolation_scale_thw) + self.rope = RoPE3D(interpolation_scale_thw=interpolation_scale_thw) + self.position_getter = PositionGetter3D() def __call__( self, @@ -978,7 +976,7 @@ def __init__( out_bias=attention_out_bias, attention_mode=attention_mode, downsampler=False, - use_rope=use_rope, + use_rope=False, interpolation_scale_thw=interpolation_scale_thw, ) # is self-attn if encoder_hidden_states is none else: @@ -1014,7 +1012,7 @@ def __init__( self.ff = FeedForward_Conv2d( downsampler, dim, - 4 * dim, + 2 * dim, bias=ff_bias, ) else: diff --git a/opensora/models/diffusion/opensora/rope.py b/opensora/models/diffusion/opensora/rope.py index bc13b20cd..39274e096 100644 --- a/opensora/models/diffusion/opensora/rope.py +++ b/opensora/models/diffusion/opensora/rope.py @@ -3,33 +3,34 @@ class PositionGetter3D(object): """ return positions of patches """ - def __init__(self, interpolation_scale_thw): - self.interpolation_scale_thw = interpolation_scale_thw + def __init__(self, ): self.cache_positions = {} def __call__(self, b, t, h, w, device): - if not (h,w) in self.cache_positions: - x = torch.arange(w, device=device) / self.interpolation_scale_thw[2] - y = torch.arange(h, device=device) / self.interpolation_scale_thw[1] - t = torch.arange(t, device=device) / self.interpolation_scale_thw[0] - self.cache_positions[t,h,w] = torch.cartesian_prod(t, y, x) # (t, h, w, 3) + if not (t,h,w) in self.cache_positions: + x = torch.arange(w, device=device) + y = torch.arange(h, device=device) + z = torch.arange(t, device=device) + self.cache_positions[t,h,w] = torch.cartesian_prod(z, y, x) # (t, h, w, 3) pos = self.cache_positions[t,h,w].view(1, t*h*w, 3).expand(b, -1, 3).clone() return pos class RoPE3D(torch.nn.Module): - def __init__(self, freq=10000.0, F0=1.0, scaling_factor=1.0): + def __init__(self, freq=10000.0, F0=1.0, interpolation_scale_thw=(1, 1, 1)): super().__init__() self.base = freq self.F0 = F0 - self.scaling_factor = scaling_factor + self.interpolation_scale_t = interpolation_scale_thw[0] + self.interpolation_scale_h = interpolation_scale_thw[1] + self.interpolation_scale_w = interpolation_scale_thw[2] self.cache = {} - def get_cos_sin(self, D, seq_len, device, dtype): + def get_cos_sin(self, D, seq_len, device, dtype, interpolation_scale=1): if (D, seq_len, device, dtype) not in self.cache: inv_freq = 1.0 / (self.base ** (torch.arange(0, D, 2).float().to(device) / D)) - t = torch.arange(seq_len, device=device, dtype=inv_freq.dtype) + t = torch.arange(seq_len, device=device, dtype=inv_freq.dtype) / interpolation_scale freqs = torch.einsum("i,j->ij", t, inv_freq).to(dtype) freqs = torch.cat((freqs, freqs), dim=-1) cos = freqs.cos() # (Seq, Dim) @@ -44,7 +45,6 @@ def rotate_half(x): def apply_rope1d(self, tokens, pos1d, cos, sin): assert pos1d.ndim == 2 - cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] return (tokens * cos) + (self.rotate_half(tokens) * sin) @@ -60,11 +60,13 @@ def forward(self, tokens, positions): assert tokens.size(3) % 3 == 0, "number of dimensions should be a multiple of three" D = tokens.size(3) // 3 assert positions.ndim == 3 and positions.shape[-1] == 3 # Batch, Seq, 3 - cos, sin = self.get_cos_sin(D, int(positions.max()) + 1, tokens.device, tokens.dtype) + cos_t, sin_t = self.get_cos_sin(D, int(positions[:, :, 0].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_t) + cos_y, sin_y = self.get_cos_sin(D, int(positions[:, :, 1].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_h) + cos_x, sin_x = self.get_cos_sin(D, int(positions[:, :, 2].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_w) # split features into three along the feature dimension, and apply rope1d on each half t, y, x = tokens.chunk(3, dim=-1) - t = self.apply_rope1d(t, positions[:, :, 0], cos, sin) - y = self.apply_rope1d(y, positions[:, :, 1], cos, sin) - x = self.apply_rope1d(x, positions[:, :, 2], cos, sin) + t = self.apply_rope1d(t, positions[:, :, 0], cos_t, sin_t) + y = self.apply_rope1d(y, positions[:, :, 1], cos_y, sin_y) + x = self.apply_rope1d(x, positions[:, :, 2], cos_x, sin_x) tokens = torch.cat((t, y, x), dim=-1) return tokens diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py index 1b8d17617..d0ccd5a56 100644 --- a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -88,11 +88,13 @@ def __init__( interpolation_scale_t: float = None, use_additional_conditions: Optional[bool] = None, attention_mode: str = 'xformers', - downsampler: str = 'k333_s222' + downsampler: str = 'k333_s222', + use_rope: bool = False, ): super().__init__() # Set some common variables used across the board. + self.use_rope = use_rope self.downsampler = downsampler self.use_linear_projection = use_linear_projection self.interpolation_scale_t = interpolation_scale_t @@ -166,6 +168,7 @@ def _init_patched_inputs(self, norm_type): embed_dim=self.inner_dim, interpolation_scale=interpolation_scale, interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, ) elif self.config.downsampler is not None and len(self.config.downsampler) == 7: is_video_model = False @@ -179,25 +182,24 @@ def _init_patched_inputs(self, norm_type): embed_dim=self.inner_dim, interpolation_scale=interpolation_scale, interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, ) layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] - + interpolation_scale_thw = (interpolation_scale_t, *interpolation_scale) for i in range((len(self.config.depth)-1)//2): t = layer_thw[i][0] // 2 if layer_thw[i][0] != 1 else 1 h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 layer_thw.append([t, h, w]) + self.layer_thw = layer_thw self.encoder_level_1 = nn.ModuleList( [ BasicTransformerBlock( self.inner_dim, self.config.num_attention_heads, self.config.attention_head_dim, - num_frames=layer_thw[0][0], - height=layer_thw[0][1], - width=layer_thw[0][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -213,6 +215,8 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[0]) ] @@ -225,9 +229,6 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 2, self.config.num_attention_heads, self.config.attention_head_dim * 2, - num_frames=layer_thw[1][0], - height=layer_thw[1][1], - width=layer_thw[1][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -243,6 +244,8 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[1]) ] @@ -255,9 +258,6 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 4, self.config.num_attention_heads, self.config.attention_head_dim * 4, - num_frames=layer_thw[2][0], - height=layer_thw[2][1], - width=layer_thw[2][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -273,6 +273,8 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[2]) ] @@ -287,9 +289,6 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 2, self.config.num_attention_heads, self.config.attention_head_dim * 2, - num_frames=layer_thw[1][0], - height=layer_thw[1][1], - width=layer_thw[1][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -305,6 +304,8 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[3]) ] @@ -319,9 +320,6 @@ def _init_patched_inputs(self, norm_type): self.inner_dim, self.config.num_attention_heads, self.config.attention_head_dim, - num_frames=layer_thw[0][0], - height=layer_thw[0][1], - width=layer_thw[0][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -337,6 +335,8 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[4]) ] @@ -515,6 +515,9 @@ def custom_forward(*inputs): timestep_1, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -527,6 +530,9 @@ def custom_forward(*inputs): timestep=timestep_1, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) pad_h_1, pad_w_1 = height % 4, width % 4 @@ -550,6 +556,9 @@ def custom_forward(*inputs): timestep_2, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -562,6 +571,9 @@ def custom_forward(*inputs): timestep=timestep_2, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) pad_h_2, pad_w_2 = height % 4, width % 4 @@ -585,6 +597,9 @@ def custom_forward(*inputs): timestep_3, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -597,6 +612,9 @@ def custom_forward(*inputs): timestep=timestep_3, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) # decoder_2 @@ -623,6 +641,9 @@ def custom_forward(*inputs): timestep_2, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -635,6 +656,9 @@ def custom_forward(*inputs): timestep=timestep_2, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) # decoder_1 @@ -661,6 +685,9 @@ def custom_forward(*inputs): timestep_1, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -673,6 +700,9 @@ def custom_forward(*inputs): timestep=timestep_1, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) assert not torch.any(torch.isnan(out_dec_level1)), 'after out_dec_level1' @@ -766,39 +796,39 @@ def _get_output_for_patched_inputs( def UDiTUltraT2V_S_111(**kwargs): - return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=24, num_attention_heads=16, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_S_122(**kwargs): - return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=2, + return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=24, num_attention_heads=16, patch_size_t=1, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_B_111(**kwargs): - return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=24, num_attention_heads=16, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_111(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_211(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=1, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=2, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_122(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_222(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=2, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_XL_111(**kwargs): - return UDiTUltraT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_XXL_111(**kwargs): - return UDiTUltraT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) UDiT_Ultra_models = { @@ -875,13 +905,16 @@ def get_peft_state_maybe_zero_3(named_params, bias): { 'ae': 'CausalVAEModel_4x8x8', 'attention_mode': 'xformers', - 'use_rope': False, + 'use_rope': True, 'model_max_length': 300, - 'max_height': 480, - 'max_width': 640, - 'num_frames': 125, + 'max_height': 240, + 'max_width': 320, + 'num_frames': 1, 'use_image_num': 0, - 'compress_kv_factor': 1 + 'compress_kv_factor': 1, + 'interpolation_scale_t': 1, + 'interpolation_scale_h': 1, + 'interpolation_scale_w': 1, } ) b = 2 @@ -912,7 +945,11 @@ def get_peft_state_maybe_zero_3(named_params, bias): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k333_s222').to(device) + downsampler='k33_s11', + interpolation_scale_t=args.interpolation_scale_t, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + use_rope=args.use_rope).to(device) print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') @@ -942,7 +979,7 @@ def get_peft_state_maybe_zero_3(named_params, bias): new_v = v new_ckpt[k] = new_v msg = model.load_state_dict(new_ckpt, strict=False) - print(msg) + # print(msg) except Exception as e: print(e) # import sys;sys.exit() @@ -951,7 +988,12 @@ def get_peft_state_maybe_zero_3(named_params, bias): attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L timestep = torch.randint(0, 1000, (b,), device=device) - + model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) + with torch.no_grad(): + output = model(**model_kwargs) + print(output[0].shape) + sys.exit() from peft import LoraConfig, PeftModel from peft import get_peft_model lora_save_path = 'lora' @@ -1027,8 +1069,6 @@ def save_pretrained(self, path): print('Merging LoRA weights...') model_load_lora_merge = model_load_lora.merge_and_unload() - model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) with torch.no_grad(): output = model_lora(**model_kwargs) print(output[0].shape) diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index a0bde956f..c143c2fe0 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -18,6 +18,7 @@ from diffusers.models.attention_processor import Attention as Attention_ from diffusers.models.embeddings import SinusoidalPositionalEmbedding from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm +from .rope import PositionGetter3D, RoPE3D import re try: import torch_npu @@ -153,9 +154,11 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, + use_abs_pos=False, ): super().__init__() # assert patch_size_t == 1 and patch_size == 1 + self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm self.proj = nn.Conv3d( @@ -201,45 +204,46 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) - # import ipdb;ipdb.set_trace() - # Interpolate positional embeddings if needed. - # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.height != height or self.width != width: - # raise NotImplementedError - pos_embed = get_2d_sincos_pos_embed( - embed_dim=self.pos_embed.shape[-1], - grid_size=(height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed = torch.from_numpy(pos_embed) - pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) - else: - pos_embed = self.pos_embed + if self.use_abs_pos: + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed - if self.num_frames != num_frames: - # import ipdb;ipdb.set_trace() - # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - temp_pos_embed = torch.from_numpy(temp_pos_embed) - temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) - else: - temp_pos_embed = self.temp_pos_embed + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed - latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) video_latent = latent - # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - temp_pos_embed = temp_pos_embed.unsqueeze(2) - video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) + if self.use_abs_pos: + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') return video_latent @@ -263,9 +267,11 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, + use_abs_pos=False, ): super().__init__() assert patch_size_t == 1 + self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm @@ -312,54 +318,55 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) - # import ipdb;ipdb.set_trace() - # Interpolate positional embeddings if needed. - # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.height != height or self.width != width: - # raise NotImplementedError - pos_embed = get_2d_sincos_pos_embed( - embed_dim=self.pos_embed.shape[-1], - grid_size=(height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed = torch.from_numpy(pos_embed) - pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) - else: - pos_embed = self.pos_embed + if self.use_abs_pos: + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed - if self.num_frames != num_frames: - # import ipdb;ipdb.set_trace() - # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - temp_pos_embed = torch.from_numpy(temp_pos_embed) - temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) - else: - temp_pos_embed = self.temp_pos_embed + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed - latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) image_latent = latent - # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - # temp_pos_embed = temp_pos_embed.unsqueeze(2) - # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + if self.use_abs_pos: + temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None + image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None return image_latent class Attention(Attention_): - def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attention_mode='xformers', **kwags): - processor = AttnProcessor2_0(attention_mode='xformers') + def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_thw, **kwags): + processor = AttnProcessor2_0(attention_mode=attention_mode, use_rope=use_rope, interpolation_scale_thw=interpolation_scale_thw) super().__init__(processor=processor, **kwags) self.downsampler = None if downsampler: # downsampler k155_s122 @@ -372,11 +379,11 @@ def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attentio if len(downsampler_ker_size) == 2: self.downsampler = DownSampler2d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, - down_shortcut=True, num_frames=num_frames, height=height, width=width) + down_shortcut=True) elif len(downsampler_ker_size) == 3: self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, - down_shortcut=True, num_frames=num_frames, height=height, width=width) + down_shortcut=True) # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) @@ -388,72 +395,74 @@ def __init__(self, *args, **kwargs): super().__init__() self.down_factor = kwargs.pop('down_factor') self.down_shortcut = kwargs.pop('down_shortcut') - self.t = kwargs.pop('num_frames') - self.h = kwargs.pop('height') - self.w = kwargs.pop('width') self.layer = nn.Conv3d(*args, **kwargs) - def forward(self, x, attention_mask): - # import ipdb;ipdb.set_trace() + def forward(self, x, attention_mask, t, h, w): b = x.shape[0] - x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) + x = rearrange(x, 'b (t h w) d -> b d t h w', t=t, h=h, w=w) if npu_config is None: x = self.layer(x) + (x if self.down_shortcut else 0) else: x_dtype = x.dtype x = npu_config.run_conv3d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + + self.t = t//self.down_factor[0] + self.h = h//self.down_factor[1] + self.w = w//self.down_factor[2] x = rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) (t h w) d', - t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) - attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=self.t, h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=t, h=h, w=w) attention_mask = rearrange(attention_mask, 'b 1 (t dt) (h dh) (w dw) -> (b dt dh dw) 1 (t h w)', - t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) return x, attention_mask - def reverse(self, x): - # import ipdb;ipdb.set_trace() + + def reverse(self, x, t, h, w): x = rearrange(x, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', - t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + t=t, h=h, w=w, dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) return x + class DownSampler2d(nn.Module): def __init__(self, *args, **kwargs): ''' Required kwargs: down_factor, downsampler''' super().__init__() self.down_factor = kwargs.pop('down_factor') self.down_shortcut = kwargs.pop('down_shortcut') - self.t = kwargs.pop('num_frames') - self.h = kwargs.pop('height') - self.w = kwargs.pop('width') self.layer = nn.Conv2d(*args, **kwargs) - def forward(self, x, attention_mask): + def forward(self, x, attention_mask, t, h, w): # import ipdb;ipdb.set_trace() b = x.shape[0] - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) if npu_config is None: x = self.layer(x) + (x if self.down_shortcut else 0) else: x_dtype = x.dtype x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + + self.t = 1 + self.h = h//self.down_factor[0] + self.w = w//self.down_factor[1] + x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', - h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + h=h//self.down_factor[0], w=w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) - - attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=h, w=w) attention_mask = rearrange(attention_mask, 'b 1 (h dh) (w dw) -> (b dh dw) 1 (h w)', - h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + h=h//self.down_factor[0], w=w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) return x, attention_mask - def reverse(self, x): - # import ipdb;ipdb.set_trace() + + def reverse(self, x, t, h, w): x = rearrange(x, '(b t dh dw) (h w) d -> b (t h dh w dw) d', - t=self.t, h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + t=t, h=h, w=w, dh=self.down_factor[0], dw=self.down_factor[1]) return x @@ -463,11 +472,19 @@ class AttnProcessor2_0: Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). """ - def __init__(self, attention_mode='xformers'): + def __init__(self, attention_mode='xformers', use_rope=False, interpolation_scale_thw=(1, 1, 1)): + self.use_rope = use_rope + self.interpolation_scale_thw = interpolation_scale_thw + if self.use_rope: + self._init_rope(interpolation_scale_thw) self.attention_mode = attention_mode if not hasattr(F, "scaled_dot_product_attention"): raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + def _init_rope(self, interpolation_scale_thw): + self.rope = RoPE3D(interpolation_scale_thw=interpolation_scale_thw) + self.position_getter = PositionGetter3D() + def __call__( self, attn: Attention, @@ -475,6 +492,9 @@ def __call__( encoder_hidden_states: Optional[torch.FloatTensor] = None, attention_mask: Optional[torch.FloatTensor] = None, temb: Optional[torch.FloatTensor] = None, + frame: int = 8, + height: int = 16, + width: int = 16, *args, **kwargs, ) -> torch.FloatTensor: @@ -483,7 +503,8 @@ def __call__( deprecate("scale", "1.0.0", deprecation_message) if attn.downsampler is not None: - hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask) + hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask, t=frame, h=height, w=width) + frame, height, width = attn.downsampler.t, attn.downsampler.h, attn.downsampler.w residual = hidden_states if attn.spatial_norm is not None: @@ -540,6 +561,14 @@ def __call__( query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + if query.shape == key.shape: + key = self.rope(key, pos_thw) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) # qk norm @@ -586,7 +615,7 @@ def __call__( hidden_states = hidden_states / attn.rescale_output_factor if attn.downsampler is not None: - hidden_states = attn.downsampler.reverse(hidden_states) + hidden_states = attn.downsampler.reverse(hidden_states, t=frame, h=height, w=width) return hidden_states @@ -734,12 +763,9 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): class FeedForward_Conv2d(nn.Module): - def __init__(self, downsampler, dim, hidden_features, bias=True, num_frames=8, height=16, width=16): + def __init__(self, downsampler, dim, hidden_features, bias=True): super(FeedForward_Conv2d, self).__init__() - self.t = num_frames - self.h = height - self.w = width self.bias = bias self.project_in = nn.Linear(dim, hidden_features, bias=bias) @@ -756,16 +782,16 @@ def __init__(self, downsampler, dim, hidden_features, bias=True, num_frames=8, h self.project_out = nn.Linear(hidden_features, dim, bias=bias) - def forward(self, x): + def forward(self, x, t, h, w): # import ipdb;ipdb.set_trace() if npu_config is None: x = self.project_in(x) - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) x = F.gelu(x) out = x for module in self.dwconv: out = out + module(x) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) x = self.project_out(out) else: x_dtype = x.dtype @@ -774,7 +800,7 @@ def forward(self, x): out = x for module in self.dwconv: out = out + npu_config.run_conv2d(module, x, torch.float16) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) x = npu_config.run_conv2d(self.project_out, out, x_dtype) return x @@ -840,18 +866,14 @@ def __init__( ff_bias: bool = True, attention_out_bias: bool = True, attention_mode: str = "xformers", - num_frames: int = 16, - height: int = 32, - width: int = 32, downsampler: str = None, mlp_ratio: int = 4, + use_rope: bool = False, + interpolation_scale_thw: Tuple[int] = (1, 1, 1), ): super().__init__() self.only_cross_attention = only_cross_attention - self.t = num_frames - self.h = height - self.w = width # We keep these boolean flags for backward-compatibility. self.use_ada_layer_norm_zero = (num_embeds_ada_norm is not None) and norm_type == "ada_norm_zero" self.use_ada_layer_norm = (num_embeds_ada_norm is not None) and norm_type == "ada_norm" @@ -906,10 +928,9 @@ def __init__( upcast_attention=upcast_attention, out_bias=attention_out_bias, downsampler=downsampler, - num_frames=num_frames, - height=height, - width=width, attention_mode=attention_mode, + use_rope=use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) # 2. Cross-Attn @@ -942,6 +963,8 @@ def __init__( out_bias=attention_out_bias, downsampler=None, attention_mode=attention_mode, + use_rope=False, + interpolation_scale_thw=interpolation_scale_thw, ) # is self-attn if encoder_hidden_states is none else: self.norm2 = None @@ -967,9 +990,6 @@ def __init__( downsampler, dim, hidden_features=mlp_ratio * dim, - num_frames=num_frames, - height=height, - width=width, ) # 4. Fuser @@ -998,6 +1018,9 @@ def forward( timestep: Optional[torch.LongTensor] = None, cross_attention_kwargs: Dict[str, Any] = None, class_labels: Optional[torch.LongTensor] = None, + frame: int = None, + height: int = None, + width: int = None, added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None, ) -> torch.FloatTensor: if cross_attention_kwargs is not None: @@ -1038,7 +1061,7 @@ def forward( attn_output = self.attn1( norm_hidden_states, encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, - attention_mask=attention_mask, + attention_mask=attention_mask, frame=frame, height=height, width=width, **cross_attention_kwargs, ) if self.norm_type == "ada_norm_zero": @@ -1097,7 +1120,7 @@ def forward( # "feed_forward_chunk_size" can be used to save memory ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) else: - ff_output = self.ff(norm_hidden_states) + ff_output = self.ff(norm_hidden_states, t=frame, h=height, w=width) if self.norm_type == "ada_norm_zero": ff_output = gate_mlp.unsqueeze(1) * ff_output elif self.norm_type == "ada_norm_single": diff --git a/opensora/models/diffusion/udit_ultra/rope.py b/opensora/models/diffusion/udit_ultra/rope.py new file mode 100644 index 000000000..39274e096 --- /dev/null +++ b/opensora/models/diffusion/udit_ultra/rope.py @@ -0,0 +1,72 @@ +import torch + +class PositionGetter3D(object): + """ return positions of patches """ + + def __init__(self, ): + self.cache_positions = {} + + def __call__(self, b, t, h, w, device): + if not (t,h,w) in self.cache_positions: + x = torch.arange(w, device=device) + y = torch.arange(h, device=device) + z = torch.arange(t, device=device) + self.cache_positions[t,h,w] = torch.cartesian_prod(z, y, x) # (t, h, w, 3) + pos = self.cache_positions[t,h,w].view(1, t*h*w, 3).expand(b, -1, 3).clone() + return pos + + +class RoPE3D(torch.nn.Module): + + def __init__(self, freq=10000.0, F0=1.0, interpolation_scale_thw=(1, 1, 1)): + super().__init__() + self.base = freq + self.F0 = F0 + self.interpolation_scale_t = interpolation_scale_thw[0] + self.interpolation_scale_h = interpolation_scale_thw[1] + self.interpolation_scale_w = interpolation_scale_thw[2] + self.cache = {} + + def get_cos_sin(self, D, seq_len, device, dtype, interpolation_scale=1): + if (D, seq_len, device, dtype) not in self.cache: + inv_freq = 1.0 / (self.base ** (torch.arange(0, D, 2).float().to(device) / D)) + t = torch.arange(seq_len, device=device, dtype=inv_freq.dtype) / interpolation_scale + freqs = torch.einsum("i,j->ij", t, inv_freq).to(dtype) + freqs = torch.cat((freqs, freqs), dim=-1) + cos = freqs.cos() # (Seq, Dim) + sin = freqs.sin() + self.cache[D, seq_len, device, dtype] = (cos, sin) + return self.cache[D, seq_len, device, dtype] + + @staticmethod + def rotate_half(x): + x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2:] + return torch.cat((-x2, x1), dim=-1) + + def apply_rope1d(self, tokens, pos1d, cos, sin): + assert pos1d.ndim == 2 + cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] + sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] + return (tokens * cos) + (self.rotate_half(tokens) * sin) + + def forward(self, tokens, positions): + """ + input: + * tokens: batch_size x nheads x ntokens x dim + * positions: batch_size x ntokens x 3 (t, y and x position of each token) + output: + * tokens after appplying RoPE3D (batch_size x nheads x ntokens x dim) + """ + assert tokens.size(3) % 3 == 0, "number of dimensions should be a multiple of three" + D = tokens.size(3) // 3 + assert positions.ndim == 3 and positions.shape[-1] == 3 # Batch, Seq, 3 + cos_t, sin_t = self.get_cos_sin(D, int(positions[:, :, 0].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_t) + cos_y, sin_y = self.get_cos_sin(D, int(positions[:, :, 1].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_h) + cos_x, sin_x = self.get_cos_sin(D, int(positions[:, :, 2].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_w) + # split features into three along the feature dimension, and apply rope1d on each half + t, y, x = tokens.chunk(3, dim=-1) + t = self.apply_rope1d(t, positions[:, :, 0], cos_t, sin_t) + y = self.apply_rope1d(y, positions[:, :, 1], cos_y, sin_y) + x = self.apply_rope1d(x, positions[:, :, 2], cos_x, sin_x) + tokens = torch.cat((t, y, x), dim=-1) + return tokens diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 14f1cdb1d..7b2d4d876 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -16,8 +16,8 @@ def __init__(self, args, **kwargs): # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel - self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 05e0e10e2..4fe6780ad 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -616,6 +616,7 @@ def run(model_input, model_kwargs, prof): loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights loss = loss.mean() + # Gather the losses across all processes for logging (if we use distributed training). avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps @@ -629,9 +630,6 @@ def run(model_input, model_kwargs, prof): lr_scheduler.step() optimizer.zero_grad() - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps - if accelerator.sync_gradients: sync_gradients_info(loss) diff --git a/opensora/train/train_t2v_diffusers_lora.py b/opensora/train/train_t2v_diffusers_lora.py index 497fbab56..05f505ca9 100644 --- a/opensora/train/train_t2v_diffusers_lora.py +++ b/opensora/train/train_t2v_diffusers_lora.py @@ -666,9 +666,6 @@ def run(model_input, model_kwargs, prof): if prof is not None: prof.step() - avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() - progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps - if accelerator.sync_gradients: sync_gradients_info(loss) From 44417161bee4f1042b0b68a8a29a4e400109d236 Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Wed, 19 Jun 2024 17:34:53 +0800 Subject: [PATCH 050/134] support uditultra on npu --- .../models/diffusion/udit_ultra/modules.py | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index c143c2fe0..3daef8991 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -355,7 +355,7 @@ def forward(self, latent, num_frames): image_latent = latent if self.use_abs_pos: - temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() temp_pos_embed = temp_pos_embed.unsqueeze(2) video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None @@ -440,11 +440,7 @@ def forward(self, x, attention_mask, t, h, w): # import ipdb;ipdb.set_trace() b = x.shape[0] x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) - if npu_config is None: - x = self.layer(x) + (x if self.down_shortcut else 0) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = self.layer(x) + (x if self.down_shortcut else 0) self.t = 1 self.h = h//self.down_factor[0] @@ -528,6 +524,9 @@ def __call__( attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) else: attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) + attention_mask = attention_mask.repeat(1, 1, hidden_states.shape[1], 1) + if npu_config.enable_FA: + attention_mask = attention_mask.to(torch.bool) if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) @@ -551,6 +550,17 @@ def __call__( else: dtype = None + if self.use_rope: + query = query.view(batch_size, -1, attn.heads, head_dim) + key = key.view(batch_size, -1, attn.heads, head_dim) + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + if query.shape == key.shape: + key = self.rope(key, pos_thw) + query = query.view(batch_size, -1, attn.heads * head_dim) + key = key.view(batch_size, -1, attn.heads * head_dim) + with set_run_dtype(query, dtype): query, key, value = npu_config.set_current_run_dtype([query, key, value]) hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", @@ -722,15 +732,15 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') x = F.pad(x, (0, pad_w, 0, pad_h)) if npu_config is None: - x = self.body(x) + x = F.pad(x, (0, pad_w, 0, pad_h)) else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.body, x, x_dtype) + x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) + x = self.body(x) x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) - attention_mask = F.max_pool2d(attention_mask, kernel_size=2, stride=2) + attention_mask = F.max_pool2d(attention_mask.float(), kernel_size=2, stride=2) attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 @@ -745,11 +755,7 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) - if npu_config is None: - x = self.body(x) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.body, x, x_dtype) + x = self.body(x) x = x[:, :, :height*2-pad_h, :width*2-pad_w] x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) @@ -784,24 +790,14 @@ def __init__(self, downsampler, dim, hidden_features, bias=True): def forward(self, x, t, h, w): # import ipdb;ipdb.set_trace() - if npu_config is None: - x = self.project_in(x) - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) - x = F.gelu(x) - out = x - for module in self.dwconv: - out = out + module(x) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) - x = self.project_out(out) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.project_in, x, torch.float16) - x = F.gelu(x) - out = x - for module in self.dwconv: - out = out + npu_config.run_conv2d(module, x, torch.float16) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) - x = npu_config.run_conv2d(self.project_out, out, x_dtype) + x = self.project_in(x) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) + x = F.gelu(x) + out = x + for module in self.dwconv: + out = out + module(x) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) + x = self.project_out(out) return x @maybe_allow_in_graph From 1f58d6b260b1845377bffbbb8b5b4591c4723df1 Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Wed, 19 Jun 2024 17:35:57 +0800 Subject: [PATCH 051/134] support uditultra on npu --- .../npu/train_imageuditultra_480p_new.sh | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 scripts/text_condition/npu/train_imageuditultra_480p_new.sh diff --git a/scripts/text_condition/npu/train_imageuditultra_480p_new.sh b/scripts/text_condition/npu/train_imageuditultra_480p_new.sh new file mode 100644 index 000000000..0cfbd40eb --- /dev/null +++ b/scripts/text_condition/npu/train_imageuditultra_480p_new.sh @@ -0,0 +1,54 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 720 \ + --max_width 1280 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=250 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" \ + --checkpoints_total_limit 3 \ No newline at end of file From 4e5cd02a4f33049b79c372a2b0886df638c85e12 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Wed, 19 Jun 2024 12:24:54 +0000 Subject: [PATCH 052/134] support process Chinese --- opensora/dataset/t2v_datasets.py | 10 ++- .../diffusion/opensora/modeling_opensora.py | 14 ++-- opensora/models/diffusion/opensora/modules.py | 3 +- .../udit_ultra/modeling_udit_ultra.py | 37 +++++---- .../models/diffusion/udit_ultra/modules.py | 3 +- opensora/models/diffusion/udit_ultra/rope.py | 82 +++++++++++++++++++ opensora/models/text_encoder/__init__.py | 4 +- opensora/train/train_t2v_diffusers.py | 2 +- opensora/utils/utils.py | 18 ++-- .../gpu/train_imageuditultra_480p_new_rope.sh | 61 ++++++++++++++ .../gpu/train_video3d_61x240p.sh | 18 ++-- .../gpu/train_videoudit_125x480p.sh | 55 ------------- .../gpu/train_videouditultra_125x480p.sh | 10 +-- 13 files changed, 207 insertions(+), 110 deletions(-) create mode 100644 scripts/text_condition/gpu/train_imageuditultra_480p_new_rope.sh delete mode 100644 scripts/text_condition/gpu/train_videoudit_125x480p.sh diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index e1ae92a96..3f672490e 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -61,6 +61,10 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.cfg = args.cfg self.v_decoder = DecordInit() + self.support_Chinese = True + if not ('mt5' in args.text_encoder_name): + self.support_Chinese = False + if self.num_frames != 1: self.vid_cap_list = self.get_vid_cap_list() if self.use_image_num != 0 and not self.use_img_from_vid: @@ -146,12 +150,12 @@ def get_video(self, idx): video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W - # video = torch.rand(125, 3, 480, 640) + # video = torch.rand(221, 3, 480, 640) video = video.transpose(0, 1) # T C H W -> C T H W text = self.vid_cap_list[idx]['cap'] - text = text_preprocessing(text) if random.random() > self.cfg else "" + text = text_preprocessing(text, support_Chinese=self.support_Chinese) if random.random() > self.cfg else "" text_tokens_and_mask = self.tokenizer( text, max_length=self.model_max_length, @@ -193,7 +197,7 @@ def get_image(self, idx): image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] caps = [i['cap'] for i in image_data] - text = [text_preprocessing(cap) for cap in caps] + text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] input_ids, cond_mask = [], [] for t in text: t = t if random.random() > self.cfg else "" diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 98400adaf..5d1c5dfd2 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -404,7 +404,6 @@ def forward( hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num ) # 2. Blocks - # import ipdb;ipdb.set_trace() for block in self.transformer_blocks: if self.training and self.gradient_checkpointing: @@ -635,9 +634,9 @@ def OpenSoraT2V_B_222(**kwargs): # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) OpenSora_models = { - "OpenSoraT2V-S/122": OpenSoraT2V_S_122, + "OpenSoraT2V-S/122": OpenSoraT2V_S_122, # 1.1B "OpenSoraT2V-B/122": OpenSoraT2V_B_122, - "OpenSoraT2V-L/122": OpenSoraT2V_L_122, + "OpenSoraT2V-L/122": OpenSoraT2V_L_122, # 2.8B 2.8B "OpenSoraT2V-S/111": OpenSoraT2V_S_111, "OpenSoraT2V-B/111": OpenSoraT2V_B_111, "OpenSoraT2V-L/111": OpenSoraT2V_L_111, @@ -672,9 +671,9 @@ def OpenSoraT2V_B_222(**kwargs): 'attention_mode': 'xformers', 'use_rope': True, 'model_max_length': 300, - 'max_height': 512, - 'max_width': 512, - 'num_frames': 1, + 'max_height': 480, + 'max_width': 640, + 'num_frames': 61, 'use_image_num': 0, 'compress_kv_factor': 1, 'interpolation_scale_t': 1, @@ -710,7 +709,7 @@ def OpenSoraT2V_B_222(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k33_s11', + downsampler=None, interpolation_scale_t=args.interpolation_scale_t, interpolation_scale_h=args.interpolation_scale_h, interpolation_scale_w=args.interpolation_scale_w, @@ -732,6 +731,7 @@ def OpenSoraT2V_B_222(**kwargs): print(e) print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B') + import sys;sys.exit() x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index abebf55ee..701182030 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -691,8 +691,7 @@ def __call__( # require the shape of (batch_size x nheads x ntokens x dim) pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) query = self.rope(query, pos_thw) - if query.shape == key.shape: - key = self.rope(key, pos_thw) + key = self.rope(key, pos_thw) value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py index d0ccd5a56..da2321663 100644 --- a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -804,55 +804,56 @@ def UDiTUltraT2V_S_122(**kwargs): mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_B_111(**kwargs): - return UDiTUltraT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=24, num_attention_heads=16, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[2, 5, 10, 5, 2], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTUltraT2V_B_122(**kwargs): + return UDiTUltraT2V(depth=[2, 5, 10, 5, 2], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_111(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_211(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=2, patch_size=1, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=2, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_122(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=2, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_L_222(**kwargs): - return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=24, patch_size_t=2, patch_size=2, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=2, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTUltraT2V_XL_111(**kwargs): - return UDiTUltraT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=1, + return UDiTUltraT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=48, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) -def UDiTUltraT2V_XXL_111(**kwargs): - return UDiTUltraT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) UDiT_Ultra_models = { - "UDiTUltraT2V-S/111": UDiTUltraT2V_S_111, # 0.2B 0.3B if video - "UDiTUltraT2V-S/122": UDiTUltraT2V_S_122, # 0.2B 0.3B if video - "UDiTUltraT2V-B/111": UDiTUltraT2V_B_111, # 0.7B 1.1B if video + "UDiTUltraT2V-S/111": UDiTUltraT2V_S_111, # 0.4B 0.7B if video + "UDiTUltraT2V-S/122": UDiTUltraT2V_S_122, # 0.4B 0.7B if video + "UDiTUltraT2V-B/111": UDiTUltraT2V_B_111, # 1.0B 1.6B if video + "UDiTUltraT2V-B/122": UDiTUltraT2V_B_122, # 1.0B 1.6B if video "UDiTUltraT2V-L/111": UDiTUltraT2V_L_111, # 2.2B 3.3B if video "UDiTUltraT2V-L/211": UDiTUltraT2V_L_211, # 2.2B 3.3B if video "UDiTUltraT2V-L/122": UDiTUltraT2V_L_122, # 2.2B 3.3B if video "UDiTUltraT2V-L/222": UDiTUltraT2V_L_222, # 2.2B 3.3B if video - "UDiTUltraT2V-XL/111": UDiTUltraT2V_XL_111, # 5.1B 7B if video - "UDiTUltraT2V-XXL/111": UDiTUltraT2V_XXL_111, # 9.3B 11.2B if video + "UDiTUltraT2V-XL/111": UDiTUltraT2V_XL_111, # 5.0B 7.4B if video } UDiT_Ultra_models_class = { "UDiTUltraT2V-S/111": UDiTUltraT2V, "UDiTUltraT2V-S/122": UDiTUltraT2V, "UDiTUltraT2V-B/111": UDiTUltraT2V, + "UDiTUltraT2V-B/122": UDiTUltraT2V, "UDiTUltraT2V-L/111": UDiTUltraT2V, "UDiTUltraT2V-L/211": UDiTUltraT2V, "UDiTUltraT2V-L/122": UDiTUltraT2V, "UDiTUltraT2V-L/222": UDiTUltraT2V, "UDiTUltraT2V-XL/111": UDiTUltraT2V, - "UDiTUltraT2V-XXL/111": UDiTUltraT2V, } @@ -929,7 +930,7 @@ def get_peft_state_maybe_zero_3(named_params, bias): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:1') - model = UDiTUltraT2V_S_122(in_channels=c, + model = UDiTUltraT2V_L_122(in_channels=c, out_channels=c, sample_size=latent_size, sample_size_t=num_frames, @@ -945,7 +946,7 @@ def get_peft_state_maybe_zero_3(named_params, bias): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k33_s11', + downsampler='k33_s22', interpolation_scale_t=args.interpolation_scale_t, interpolation_scale_h=args.interpolation_scale_h, interpolation_scale_w=args.interpolation_scale_w, @@ -953,6 +954,7 @@ def get_peft_state_maybe_zero_3(named_params, bias): print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') + # import sys;sys.exit() try: path = "bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" from safetensors.torch import load_file as safe_load @@ -982,7 +984,6 @@ def get_peft_state_maybe_zero_3(named_params, bias): # print(msg) except Exception as e: print(e) - # import sys;sys.exit() x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index 3daef8991..1a9ffaa11 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -576,8 +576,7 @@ def __call__( # require the shape of (batch_size x nheads x ntokens x dim) pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) query = self.rope(query, pos_thw) - if query.shape == key.shape: - key = self.rope(key, pos_thw) + key = self.rope(key, pos_thw) value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) diff --git a/opensora/models/diffusion/udit_ultra/rope.py b/opensora/models/diffusion/udit_ultra/rope.py index 39274e096..b42096b9d 100644 --- a/opensora/models/diffusion/udit_ultra/rope.py +++ b/opensora/models/diffusion/udit_ultra/rope.py @@ -70,3 +70,85 @@ def forward(self, tokens, positions): x = self.apply_rope1d(x, positions[:, :, 2], cos_x, sin_x) tokens = torch.cat((t, y, x), dim=-1) return tokens + + + +# import torch +# from einops import rearrange, repeat + +# class PositionGetter3D(object): +# """ return positions of patches """ + +# def __init__(self, ): +# self.cache_positions = {} + +# def __call__(self, b, t, h, w, device): +# if not (t,h,w) in self.cache_positions: +# x = torch.arange(w, device=device) +# y = torch.arange(h, device=device) +# z = torch.arange(t, device=device) +# positions = torch.cartesian_prod(z, y, x) # (t, h, w, 3) +# positions = rearrange(positions, 'n d -> d 1 n') +# positions = repeat(positions, 'd 1 n -> d b n', b=b).clone() +# poses = (positions[0], positions[1], positions[2]) +# max_pos = (int(poses[0].max()), int(poses[1].max()), int(poses[2].max())) +# self.cache_positions[t,h,w] = (poses, max_pos) +# pos = self.cache_positions[t,h,w] +# return pos + + +# class RoPE3D(torch.nn.Module): + +# def __init__(self, freq=10000.0, F0=1.0, interpolation_scale_thw=(1, 1, 1)): +# super().__init__() +# self.base = freq +# self.F0 = F0 +# self.interpolation_scale_t = interpolation_scale_thw[0] +# self.interpolation_scale_h = interpolation_scale_thw[1] +# self.interpolation_scale_w = interpolation_scale_thw[2] +# self.cache = {} + +# def get_cos_sin(self, D, seq_len, device, dtype, interpolation_scale=1): +# if (D, seq_len, device, dtype) not in self.cache: +# inv_freq = 1.0 / (self.base ** (torch.arange(0, D, 2).float().to(device) / D)) +# t = torch.arange(seq_len, device=device, dtype=inv_freq.dtype) / interpolation_scale +# freqs = torch.einsum("i,j->ij", t, inv_freq).to(dtype) +# freqs = torch.cat((freqs, freqs), dim=-1) +# cos = freqs.cos() # (Seq, Dim) +# sin = freqs.sin() +# self.cache[D, seq_len, device, dtype] = (cos, sin) +# return self.cache[D, seq_len, device, dtype] + +# @staticmethod +# def rotate_half(x): +# x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2:] +# return torch.cat((-x2, x1), dim=-1) + +# def apply_rope1d(self, tokens, pos1d, cos, sin): +# assert pos1d.ndim == 2 +# cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] +# sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] +# return (tokens * cos) + (self.rotate_half(tokens) * sin) + +# def forward(self, tokens, positions): +# """ +# input: +# * tokens: batch_size x nheads x ntokens x dim +# * positions: batch_size x ntokens x 3 (t, y and x position of each token) +# output: +# * tokens after appplying RoPE3D (batch_size x nheads x ntokens x dim) +# """ +# assert tokens.size(3) % 3 == 0, "number of dimensions should be a multiple of three" +# D = tokens.size(3) // 3 +# poses, max_pos = positions +# assert len(poses) == 3 and poses[0].ndim == 2 # Batch, Seq, 3 +# cos_t, sin_t = self.get_cos_sin(D, max_pos[0] + 1, tokens.device, tokens.dtype, self.interpolation_scale_t) +# cos_y, sin_y = self.get_cos_sin(D, max_pos[1] + 1, tokens.device, tokens.dtype, self.interpolation_scale_h) +# cos_x, sin_x = self.get_cos_sin(D, max_pos[2] + 1, tokens.device, tokens.dtype, self.interpolation_scale_w) +# # split features into three along the feature dimension, and apply rope1d on each half +# t, y, x = tokens.chunk(3, dim=-1) +# t = self.apply_rope1d(t, poses[0], cos_t, sin_t) +# y = self.apply_rope1d(y, poses[1], cos_y, sin_y) +# x = self.apply_rope1d(x, poses[2], cos_x, sin_x) +# tokens = torch.cat((t, y, x), dim=-1) +# return tokens diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 7b2d4d876..14f1cdb1d 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -16,8 +16,8 @@ def __init__(self, args, **kwargs): # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel - # self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = T5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir, **kwargs).eval() def forward(self, input_ids, attention_mask): text_encoder_embs = self.text_enc(input_ids=input_ids, attention_mask=attention_mask)['last_hidden_state'] diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 4fe6780ad..c3db210fe 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -420,7 +420,7 @@ def load_model_hook(models, input_dir): collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, - prefetch_factor=8 + # prefetch_factor=8 ) # Scheduler and math around the number of training steps. diff --git a/opensora/utils/utils.py b/opensora/utils/utils.py index af3daf334..b5df91934 100644 --- a/opensora/utils/utils.py +++ b/opensora/utils/utils.py @@ -342,10 +342,10 @@ def collect_env(): bad_punct_regex = re.compile(r'['+'#®•©™&@·º½¾¿¡§~'+'\)'+'\('+'\]'+'\['+'\}'+'\{'+'\|'+'\\'+'\/'+'\*' + r']{1,}') # noqa -def text_preprocessing(text): +def text_preprocessing(text, support_Chinese=True): # The exact text cleaning as was in the training stage: - text = clean_caption(text) - text = clean_caption(text) + text = clean_caption(text, support_Chinese=support_Chinese) + text = clean_caption(text, support_Chinese=support_Chinese) return text def basic_clean(text): @@ -353,7 +353,7 @@ def basic_clean(text): text = html.unescape(html.unescape(text)) return text.strip() -def clean_caption(caption): +def clean_caption(caption, support_Chinese=True): caption = str(caption) caption = ul.unquote_plus(caption) caption = caption.strip().lower() @@ -384,7 +384,8 @@ def clean_caption(caption): caption = re.sub(r'[\u3300-\u33ff]+', '', caption) caption = re.sub(r'[\u3400-\u4dbf]+', '', caption) caption = re.sub(r'[\u4dc0-\u4dff]+', '', caption) - caption = re.sub(r'[\u4e00-\u9fff]+', '', caption) + if not support_Chinese: + caption = re.sub(r'[\u4e00-\u9fff]+', '', caption) # Chinese ####################################################### # все виды тире / all types of dash --> "-" @@ -461,5 +462,10 @@ def clean_caption(caption): return caption.strip() - +if __name__ == '__main__': + + # caption = re.sub(r'[\u4e00-\u9fff]+', '', caption) + a = "Below that, there is another line of text that says \"\u5c0f\u989d\u6148\u5584\u51a0\u540d\u57fa\u91d1\u7b7e\u5b57\u4eea\u5f0f,\" which translates to \"Small Charity Crown Name Fund Signature Ceremony.\" The names \"\u4eba\u58eb\u738b\u519b\" are also visible, which translates to \"Person King Wu,\" likely indicating the name of one of the individuals or a title. In the bottom right corner, there is a logo with the text \"\u9655\u897f\u5934\u6761@\u897f\u90e8\u7f51,\" which translates to \"Shaanxi Headline@West China Network,\" suggesting that this event is being covered or sponsored by a media outlet or news network. The overall setting suggests a formal agreement or partnership signing between the two individuals, possibly related to the charity fund mentioned on the curtain." + print(a) + print(text_preprocessing(a)) diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope.sh b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope.sh new file mode 100644 index 000000000..d6c3820df --- /dev/null +++ b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope.sh @@ -0,0 +1,61 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 16 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --output_dir="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --enable_tracker \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" \ + --checkpoints_total_limit 3 \ + --use_rope \ + --pretrained "bs16_4node_480p_lr2e-5_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl/checkpoint-19000/model/diffusion_pytorch_model.safetensors" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video3d_61x240p.sh b/scripts/text_condition/gpu/train_video3d_61x240p.sh index b6e29549c..3d03c7528 100644 --- a/scripts/text_condition/gpu/train_video3d_61x240p.sh +++ b/scripts/text_condition/gpu/train_video3d_61x240p.sh @@ -14,21 +14,21 @@ export OMP_NUM_THREADS=1 accelerate launch \ --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-S/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --model OpenSoraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ + --ae_path "/storage/dataset/test140k" \ --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ - --num_frames 253 \ + --num_frames 125 \ --max_height 480 \ --max_width 640 \ --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=1 \ @@ -40,8 +40,8 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=100 \ - --output_dir="testnpu3d_" \ + --checkpointing_steps=500 \ + --output_dir="debug" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ @@ -50,4 +50,4 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ - --resume_from_checkpoint="latest" + --tile_overlap_factor 0.125 diff --git a/scripts/text_condition/gpu/train_videoudit_125x480p.sh b/scripts/text_condition/gpu/train_videoudit_125x480p.sh deleted file mode 100644 index 721179d83..000000000 --- a/scripts/text_condition/gpu/train_videoudit_125x480p.sh +++ /dev/null @@ -1,55 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="testudit_" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --sample_rate 1 \ - --num_frames 125 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 0 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="testudit_" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --downsampler "k333_s222" \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/gpu/train_videouditultra_125x480p.sh b/scripts/text_condition/gpu/train_videouditultra_125x480p.sh index ac08e4eb1..cda869556 100644 --- a/scripts/text_condition/gpu/train_videouditultra_125x480p.sh +++ b/scripts/text_condition/gpu/train_videouditultra_125x480p.sh @@ -22,9 +22,9 @@ accelerate launch \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_notext.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ - --num_frames 125 \ + --num_frames 221 \ --max_height 480 \ --max_width 640 \ --interpolation_scale_t 1.0 \ @@ -32,7 +32,7 @@ accelerate launch \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=2 \ + --train_batch_size=1 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -42,7 +42,7 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="bs2_1node_125x480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" \ + --output_dir="debug" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ @@ -56,4 +56,4 @@ accelerate launch \ --downsampler "k333_s222" \ --resume_from_checkpoint="latest" \ --checkpoints_total_limit 3 \ - --pretrained "/storage/ongoing/new/Open-Sora-Plan/convert_weight.pt" \ No newline at end of file + --tile_overlap_factor 0.125 \ No newline at end of file From ec2813bc714feb6b34ba17c5f015c98e058f3ea0 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Wed, 19 Jun 2024 12:49:11 +0000 Subject: [PATCH 053/134] fix uditultra pad --- opensora/models/diffusion/udit_ultra/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index 1a9ffaa11..f0af24c0f 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -729,7 +729,7 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') - x = F.pad(x, (0, pad_w, 0, pad_h)) + # x = F.pad(x, (0, pad_w, 0, pad_h)) if npu_config is None: x = F.pad(x, (0, pad_w, 0, pad_h)) else: From fed6809d2082cc9736b464b561649fb47ec96859 Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Wed, 19 Jun 2024 20:54:21 +0800 Subject: [PATCH 054/134] fix bug on pad --- opensora/models/diffusion/udit_ultra/modules.py | 8 ++------ .../text_condition/npu/train_imageuditultra_480p_new.sh | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index 3daef8991..d57831514 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -523,10 +523,10 @@ def __call__( # (batch, heads, source_length, target_length) attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) else: - attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) - attention_mask = attention_mask.repeat(1, 1, hidden_states.shape[1], 1) if npu_config.enable_FA: attention_mask = attention_mask.to(torch.bool) + attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) + attention_mask = attention_mask.repeat(1, 1, hidden_states.shape[1], 1) if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) @@ -731,10 +731,6 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') x = F.pad(x, (0, pad_w, 0, pad_h)) - if npu_config is None: - x = F.pad(x, (0, pad_w, 0, pad_h)) - else: - x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) x = self.body(x) x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) diff --git a/scripts/text_condition/npu/train_imageuditultra_480p_new.sh b/scripts/text_condition/npu/train_imageuditultra_480p_new.sh index 0cfbd40eb..ff0c81f4d 100644 --- a/scripts/text_condition/npu/train_imageuditultra_480p_new.sh +++ b/scripts/text_condition/npu/train_imageuditultra_480p_new.sh @@ -20,8 +20,8 @@ accelerate launch \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ --num_frames 1 \ - --max_height 720 \ - --max_width 1280 \ + --max_height 480 \ + --max_width 640 \ --interpolation_scale_t 1.0 \ --interpolation_scale_h 1.0 \ --interpolation_scale_w 1.0 \ From bdb107f26a368248e6d3b11cd1dae2f735c7ed12 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Thu, 20 Jun 2024 22:35:29 +0800 Subject: [PATCH 055/134] inpaint --- .gitignore | 3 +- opensora/dataset/__init__.py | 20 + opensora/dataset/inpaint_datasets.py | 232 +++++ opensora/models/diffusion/__init__.py | 10 + opensora/models/diffusion/latte/__init__.py | 0 .../diffusion/latte/modeling_inpaint.py | 424 ++++++++ .../models/diffusion/latte/modeling_latte.py | 15 +- .../models/diffusion/opensora/__init__.py | 0 opensora/sample/pipeline_i2v.py | 836 ++++++++++++++++ opensora/sample/sample_t2v.py | 12 +- opensora/train/train_inpaint.py | 913 ++++++++++++++++++ scripts/text_condition/gpu/sample_video.sh | 23 +- .../gpu/train_inpaint_video21d_65x512x512.sh | 55 ++ .../train_data/video_data_debug_inpaint.txt | 1 + validation_dir/prompt.txt | 3 + 15 files changed, 2523 insertions(+), 24 deletions(-) create mode 100644 opensora/dataset/inpaint_datasets.py create mode 100644 opensora/models/diffusion/latte/__init__.py create mode 100644 opensora/models/diffusion/latte/modeling_inpaint.py create mode 100644 opensora/models/diffusion/opensora/__init__.py create mode 100644 opensora/sample/pipeline_i2v.py create mode 100644 opensora/train/train_inpaint.py create mode 100644 scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh create mode 100644 scripts/train_data/video_data_debug_inpaint.txt create mode 100644 validation_dir/prompt.txt diff --git a/.gitignore b/.gitignore index b584b40ee..358f95e94 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ sft* flash* 65x256* alpha_vae -*node* \ No newline at end of file +*node* +cache/ \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 30d89308b..6a3a2924f 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -5,6 +5,9 @@ from torchvision.transforms import Lambda from .t2v_datasets import T2V_dataset + +from .inpaint_datasets import Inpaint_dataset + from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo @@ -68,4 +71,21 @@ def getdataset(args): # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) + elif args.dataset == 'i2v' or args.dataset == 'inpaint': + if args.multi_scale: + resize = [ + LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), + SpatialStrideCropVideo(args.stride) + ] + else: + resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) + raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/inpaint_datasets.py b/opensora/dataset/inpaint_datasets.py new file mode 100644 index 000000000..de348a74d --- /dev/null +++ b/opensora/dataset/inpaint_datasets.py @@ -0,0 +1,232 @@ +import json +import os, io, csv, math, random +import numpy as np +import torchvision +from einops import rearrange +from decord import VideoReader +from os.path import join as opj + +import torch +import torchvision.transforms as transforms +from torch.utils.data.dataset import Dataset +from tqdm import tqdm +from PIL import Image + +from opensora.utils.dataset_utils import DecordInit +from opensora.utils.utils import text_preprocessing + + +def random_video_noise(t, c, h, w): + vid = torch.rand(t, c, h, w) * 255.0 + vid = vid.to(torch.uint8) + return vid + +def save_video(video: torch.Tensor): + import cv2 + + frame_count, height, width, channels = video.shape + + fourcc = cv2.VideoWriter_fourcc(*'mp4v') + out = cv2.VideoWriter('output_video.mp4', fourcc, 30.0, (width, height)) + + for i in range(frame_count): + frame = video[i] + frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) + out.write(frame) + + +class Inpaint_dataset(Dataset): + def __init__(self, args, transform, temporal_sample, tokenizer): + self.video_data = args.video_data + self.num_frames = args.num_frames + self.transform = transform + self.temporal_sample = temporal_sample + self.tokenizer = tokenizer + self.model_max_length = args.model_max_length + self.v_decoder = DecordInit() + self.text_drop_rate = args.cfg + + # inpaint + # The proportion of executing the i2v task. + self.i2v_ratio = args.i2v_ratio + self.transition_ratio = args.transition_ratio + assert self.i2v_ratio + self.transition_ratio < 1, 'The sum of i2v_ratio and transition_ratio should be less than 1.' + self.default_text_ratio = args.default_text_ratio + + if self.num_frames != 1: + self.vid_cap_list = self.get_vid_cap_list() + else: + raise NotImplementedError('Inpainting dataset only support video data') + + + + def __len__(self): + assert self.num_frames != 1, 'Inpainting dataset only support video data' + return len(self.vid_cap_list) + + def __getitem__(self, idx): + try: + video_data = {} + if self.num_frames != 1: + video_data = self.get_video(idx) + return dict(video_data=video_data) # video_data: video, input_ids, cond_mask + except Exception as e: + print(f'Error with {e}') + return self.__getitem__(random.randint(0, self.__len__() - 1)) + + def get_video(self, idx): + + video_path = self.vid_cap_list[idx]['path'] + frame_idx = self.vid_cap_list[idx]['frame_idx'] + video = self.decord_read(video_path, frame_idx) + + video = self.transform(video) + + # video = torch.rand(65, 3, 512, 512) + + video = video.transpose(0, 1) # T C H W -> C T H W + + # inpaint + inpaint_cond_data = self.get_mask_masked_video(video) + mask, masked_video = inpaint_cond_data['mask'], inpaint_cond_data['masked_video'] + video = torch.cat([video, masked_video, mask], dim=0) + + text = self.vid_cap_list[idx]['cap'] + + + rand_num = random.random() + if rand_num < self.text_drop_rate: + text = '' + elif rand_num < self.text_drop_rate + self.default_text_ratio: + text = 'The video showcases a scene with coherent and clear visuals.' + + + text = text_preprocessing(text) + text_tokens_and_mask = self.tokenizer( + text, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids = text_tokens_and_mask['input_ids'] + cond_mask = text_tokens_and_mask['attention_mask'] + + return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) + + def get_mask_masked_video(self, video): + mask = torch.zeros_like(video) + + rand_num = random.random() + # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. + if rand_num < self.i2v_ratio: + mask = 1 - mask + mask[:, 0, ...] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio: + mask = 1 - mask + mask[:, 0, ...] = 0 + mask[:, -1, ...] = 0 + else: + idx_to_select = random.randint(1, self.num_frames - 1) + selected_indices = random.sample(range(1, self.num_frames), idx_to_select) + mask[:, selected_indices, ...] = 1 + + masked_video = video * (mask < 0.5) + return dict(mask=mask, masked_video=masked_video) + + def decord_read(self, path, frame_idx=None): + decord_vr = self.v_decoder(path) + total_frames = len(decord_vr) + # Sampling video frames + if frame_idx is None: + start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) + else: + start_frame_ind, end_frame_ind = frame_idx.split(':') + # start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) + start_frame_ind, end_frame_ind = int(start_frame_ind), int(start_frame_ind) + self.num_frames + # assert end_frame_ind - start_frame_ind >= self.num_frames + frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) + # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) + + video_data = decord_vr.get_batch(frame_indice).asnumpy() + video_data = torch.from_numpy(video_data) + video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) + + return video_data + + + def get_vid_cap_list(self): + vid_cap_lists = [] + with open(self.video_data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + for folder, anno in folder_anno: + new_vid_cap_list = [] + with open(anno, 'r') as f: + vid_cap_list = json.load(f) + print(f'Building {anno}...') + for i in tqdm(range(len(vid_cap_list))): + path = opj(folder, vid_cap_list[i]['path']) + # For testing, some data has not been utilized. + if not os.path.exists(path): + continue + elif os.path.exists(path.replace('.mp4', '_resize_1080p.mp4')): + path = path.replace('.mp4', '_resize_1080p.mp4') + new_vid_cap_list.append( + { + 'path': path, + 'frame_idx': vid_cap_list[i]['frame_idx'], + 'cap': vid_cap_list[i]['cap'] + } + ) + + vid_cap_lists += new_vid_cap_list + return vid_cap_lists + + +if __name__ == "__main__": + + from torchvision import transforms + from torchvision.transforms import Lambda + from .transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop + + from transformers import AutoTokenizer + + class Args: + def __init__(self): + self.video_data = '/data01/transition/Open-Sora-Plan/scripts/train_data/video_data.txt' + self.num_frames = 65 + self.model_max_length = 300 + self.cfg = 0.1 + self.i2v_ratio = 0.5 + self.transition_ratio = 0.4 + self.default_text_ratio = 0.1 + self.max_image_size = 512 + self.sample_rate = 1 + self.text_encoder_name = "DeepFloyd/t5-v1_1-xxl" + self.cache_dir = "./cache" + + args = Args() + resize = [CenterCropResizeVideo((args.max_image_size, args.max_image_size))] + + temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + Lambda(lambda x: 2. * x - 1.) + ]) + + dataset = Inpaint_dataset(args, transform, temporal_sample, tokenizer) + + print(len(dataset)) + data = dataset[0]['video_data'] + video, input_ids, cond_mask = data['video'], data['input_ids'], data['cond_mask'] + print(video.shape) # 3 * C, F, H, W + print(input_ids.shape) # 1 D + print(cond_mask.shape) # 1 D + + diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index 2a103b9ff..5182ed639 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -2,19 +2,29 @@ from .opensora.modeling_opensora import OpenSora_models from .udit.modeling_udit import UDiT_models from .udit_ultra.modeling_udit_ultra import UDiT_Ultra_models + +from .latte.modeling_inpaint import inpaint_models + Diffusion_models = {} Diffusion_models.update(Latte_models) Diffusion_models.update(OpenSora_models) Diffusion_models.update(UDiT_models) Diffusion_models.update(UDiT_Ultra_models) +Diffusion_models.update(inpaint_models) + from .latte.modeling_latte import Latte_models_class from .opensora.modeling_opensora import OpenSora_models_class from .udit.modeling_udit import UDiT_models_class from .udit_ultra.modeling_udit_ultra import UDiT_Ultra_models_class + +from .latte.modeling_inpaint import inpaint_models_class + Diffusion_models_class = {} Diffusion_models_class.update(Latte_models_class) Diffusion_models_class.update(OpenSora_models_class) Diffusion_models_class.update(UDiT_models_class) Diffusion_models_class.update(UDiT_Ultra_models_class) + +Diffusion_models_class.update(inpaint_models_class) \ No newline at end of file diff --git a/opensora/models/diffusion/latte/__init__.py b/opensora/models/diffusion/latte/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/opensora/models/diffusion/latte/modeling_inpaint.py b/opensora/models/diffusion/latte/modeling_inpaint.py new file mode 100644 index 000000000..ab09d0b68 --- /dev/null +++ b/opensora/models/diffusion/latte/modeling_inpaint.py @@ -0,0 +1,424 @@ + +import torch +import torch.nn as nn +import torch.nn.functional as F + +import os +import json +from typing import Optional, Dict, Any +from einops import rearrange, repeat + +from .modeling_latte import LatteT2V +from .modeling_latte import Transformer3DModelOutput +from .modules import PatchEmbed + +def zero_module(module): + for p in module.parameters(): + nn.init.zeros_(p) + return module + +class OpenSoraInpaint(LatteT2V): + def __init__( + self, + num_attention_heads: int = 16, + attention_head_dim: int = 88, + in_channels: Optional[int] = None, + out_channels: Optional[int] = None, + num_layers: int = 1, + dropout: float = 0.0, + norm_num_groups: int = 32, + cross_attention_dim: Optional[int] = None, + attention_bias: bool = False, + sample_size: Optional[int] = None, + sample_size_t: Optional[int] = None, + num_vector_embeds: Optional[int] = None, + patch_size: Optional[int] = None, + patch_size_t: Optional[int] = None, + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + use_linear_projection: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_type: str = "layer_norm", + norm_elementwise_affine: bool = True, + norm_eps: float = 1e-5, + attention_type: str = "default", + caption_channels: int = None, + attention_mode: str = 'flash', + use_rope: bool = False, + model_max_length: int = 300, + rope_scaling_type: str = 'linear', + compress_kv_factor: int = 1, + interpolation_scale_h: float = None, + interpolation_scale_w: float = None, + interpolation_scale_t: float = None, + downsampler: str = None, + ): + + super().__init__( + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, + in_channels=in_channels, + out_channels=out_channels, + num_layers=num_layers, + dropout=dropout, + norm_num_groups=norm_num_groups, + cross_attention_dim=cross_attention_dim, + attention_bias=attention_bias, + sample_size=sample_size, + sample_size_t=sample_size_t, + num_vector_embeds=num_vector_embeds, + patch_size=patch_size, + patch_size_t=patch_size_t, + activation_fn=activation_fn, + num_embeds_ada_norm=num_embeds_ada_norm, + use_linear_projection=use_linear_projection, + only_cross_attention=only_cross_attention, + double_self_attention=double_self_attention, + upcast_attention=upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=norm_elementwise_affine, + norm_eps=norm_eps, + attention_type=attention_type, + caption_channels=caption_channels, + attention_mode=attention_mode, + use_rope=use_rope, + model_max_length=model_max_length, + rope_scaling_type=rope_scaling_type, + compress_kv_factor=compress_kv_factor, + interpolation_scale_h=interpolation_scale_h, + interpolation_scale_w=interpolation_scale_w, + interpolation_scale_t=interpolation_scale_t, + downsampler=downsampler, + ) + + inner_dim = num_attention_heads * attention_head_dim + interpolation_scale = self.interpolation_scale + + self.pos_embed_masked_video = nn.ModuleList( + [ + PatchEmbed( + height=sample_size[0], + width=sample_size[1], + patch_size=patch_size, + in_channels=in_channels, + embed_dim=inner_dim, + interpolation_scale=interpolation_scale, + ), + zero_module(nn.Conv1d(inner_dim, inner_dim, kernel_size=1, stride=1, padding=0)) + ] + ) + + self.pos_embed_mask = nn.ModuleList( + [ + PatchEmbed( + height=sample_size[0], + width=sample_size[1], + patch_size=patch_size, + in_channels=in_channels, + embed_dim=inner_dim, + interpolation_scale=interpolation_scale, + ), + zero_module(nn.Conv1d(inner_dim, inner_dim, kernel_size=1, stride=1, padding=0)) + ] + ) + + def forward( + self, + hidden_states: torch.Tensor, + timestep: Optional[torch.LongTensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + added_cond_kwargs: Dict[str, torch.Tensor] = None, + class_labels: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + attention_mask: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + use_image_num: int = 0, + enable_temporal_attentions: bool = True, + return_dict: bool = True, + ): + + input_batch_size, c, frame, h, w = hidden_states.shape + assert c == self.config.in_channels * 3, f"Input hidden_states should have channel {self.config.in_channels * 3}, but got {c}." + assert use_image_num == 0, f"Image joint training is not supported in inpainting mode." + hidden_states = rearrange(hidden_states, 'b c f h w -> (b f) c h w').contiguous() + # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. + # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. + # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. + # expects mask of shape: + # [batch, key_tokens] + # adds singleton query_tokens dimension: + # [batch, 1, key_tokens] + # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: + # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) + # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + if attention_mask is None: + attention_mask = torch.ones((input_batch_size, frame+use_image_num, h, w), device=hidden_states.device, dtype=hidden_states.dtype) + else: + attention_mask = attention_mask.to(hidden_states.dtype) + attention_mask = self.vae_to_diff_mask(attention_mask, use_image_num) + dtype = attention_mask.dtype + attention_mask_compress = F.max_pool2d(attention_mask.float(), kernel_size=self.compress_kv_factor, stride=self.compress_kv_factor) + attention_mask_compress = attention_mask_compress.to(dtype) + + attention_mask = self.make_attn_mask(attention_mask, frame, hidden_states.dtype) + attention_mask_compress = self.make_attn_mask(attention_mask_compress, frame, hidden_states.dtype) + + # convert encoder_attention_mask to a bias the same way we do for attention_mask + if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: + encoder_attention_mask = (1 - encoder_attention_mask.to(hidden_states.dtype)) * -10000.0 + encoder_attention_mask = repeat(encoder_attention_mask, 'b 1 l -> (b f) 1 l', f=frame).contiguous() + encoder_attention_mask = encoder_attention_mask.to(self.dtype) + + # Retrieve lora scale. + lora_scale = cross_attention_kwargs.get("scale", 1.0) if cross_attention_kwargs is not None else 1.0 + + # 1. Input + if self.is_input_patches: # here + height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size + hw = (height, width) + num_patches = height * width + + # inpaint + in_channels = self.config.in_channels + hidden_states, hidden_states_masked_video, hidden_states_mask = hidden_states[:, :in_channels], hidden_states[:, in_channels: 2 * in_channels], hidden_states[:, 2 * in_channels: 3 * in_channels] + + hidden_states = self.pos_embed(hidden_states.to(self.dtype)) # alrady add positional embeddings + + hidden_states_masked_video = self.pos_embed_masked_video[0](hidden_states_masked_video.to(self.dtype)) # alrady add positional embeddings + hidden_states_masked_video = rearrange(hidden_states_masked_video, 'b n c -> b c n').contiguous() + hidden_states_masked_video = self.pos_embed_masked_video[1](hidden_states_masked_video) + hidden_states_masked_video = rearrange(hidden_states_masked_video, 'b c n -> b n c').contiguous() + + hidden_states_mask = self.pos_embed_mask[0](hidden_states_mask.to(self.dtype)) # alrady add positional embeddings + hidden_states_mask = rearrange(hidden_states_mask, 'b n c -> b c n').contiguous() + hidden_states_mask = self.pos_embed_mask[1](hidden_states_mask) + hidden_states_mask = rearrange(hidden_states_mask, 'b c n -> b n c').contiguous() + + hidden_states = hidden_states + hidden_states_masked_video + hidden_states_mask + + + if self.adaln_single is not None: + if self.use_additional_conditions and added_cond_kwargs is None: + raise ValueError( + "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." + ) + # batch_size = hidden_states.shape[0] + batch_size = input_batch_size + timestep, embedded_timestep = self.adaln_single( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=hidden_states.dtype + ) + + # 2. Blocks + if self.caption_projection is not None: + batch_size = hidden_states.shape[0] + encoder_hidden_states = self.caption_projection(encoder_hidden_states.to(self.dtype)) # 3 120 1152 + encoder_hidden_states_spatial = repeat(encoder_hidden_states, 'b 1 t d -> (b f) t d', f=frame).contiguous() + + # prepare timesteps for spatial and temporal block + timestep_spatial = repeat(timestep, 'b d -> (b f) d', f=frame + use_image_num).contiguous() + timestep_temp = repeat(timestep, 'b d -> (b p) d', p=num_patches).contiguous() + + pos_hw, pos_t = None, None + if self.use_rope: + pos_hw, pos_t = self.make_position(input_batch_size, frame, use_image_num, height, width, hidden_states.device) + + for i, (spatial_block, temp_block) in enumerate(zip(self.transformer_blocks, self.temporal_transformer_blocks)): + + if self.training and self.gradient_checkpointing: + hidden_states = torch.utils.checkpoint.checkpoint( + spatial_block, + hidden_states, + attention_mask_compress if i >= self.num_layers // 2 else attention_mask, + encoder_hidden_states_spatial, + encoder_attention_mask, + timestep_spatial, + cross_attention_kwargs, + class_labels, + pos_hw, + pos_hw, + hw, + use_reentrant=False, + ) + + if enable_temporal_attentions: + hidden_states = rearrange(hidden_states, '(b f) t d -> (b t) f d', b=input_batch_size).contiguous() + + if use_image_num != 0: # image-video joitn training + hidden_states_video = hidden_states[:, :frame, ...] + hidden_states_image = hidden_states[:, frame:, ...] + + # if i == 0 and not self.use_rope: + if i == 0: + hidden_states_video = hidden_states_video + self.temp_pos_embed + + hidden_states_video = torch.utils.checkpoint.checkpoint( + temp_block, + hidden_states_video, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + use_reentrant=False, + ) + + hidden_states = torch.cat([hidden_states_video, hidden_states_image], dim=1) + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + + else: + # if i == 0 and not self.use_rope: + if i == 0: + hidden_states = hidden_states + self.temp_pos_embed + + hidden_states = torch.utils.checkpoint.checkpoint( + temp_block, + hidden_states, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + use_reentrant=False, + ) + + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + else: + hidden_states = spatial_block( + hidden_states, + attention_mask_compress if i >= self.num_layers // 2 else attention_mask, + encoder_hidden_states_spatial, + encoder_attention_mask, + timestep_spatial, + cross_attention_kwargs, + class_labels, + pos_hw, + pos_hw, + hw, + ) + + if enable_temporal_attentions: + # b c f h w, f = 16 + 4 + hidden_states = rearrange(hidden_states, '(b f) t d -> (b t) f d', b=input_batch_size).contiguous() + + if use_image_num != 0 and self.training: + hidden_states_video = hidden_states[:, :frame, ...] + hidden_states_image = hidden_states[:, frame:, ...] + + # if i == 0 and not self.use_rope: + # hidden_states_video = hidden_states_video + self.temp_pos_embed + + hidden_states_video = temp_block( + hidden_states_video, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + ) + + hidden_states = torch.cat([hidden_states_video, hidden_states_image], dim=1) + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + + else: + # if i == 0 and not self.use_rope: + if i == 0: + hidden_states = hidden_states + self.temp_pos_embed + + hidden_states = temp_block( + hidden_states, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + ) + + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + + if self.is_input_patches: + if self.config.norm_type != "ada_norm_single": + conditioning = self.transformer_blocks[0].norm1.emb( + timestep, class_labels, hidden_dtype=hidden_states.dtype + ) + shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] + hidden_states = self.proj_out_2(hidden_states) + elif self.config.norm_type == "ada_norm_single": + embedded_timestep = repeat(embedded_timestep, 'b d -> (b f) d', f=frame + use_image_num).contiguous() + shift, scale = (self.scale_shift_table[None] + embedded_timestep[:, None]).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) + # Modulation + hidden_states = hidden_states * (1 + scale) + shift + hidden_states = self.proj_out(hidden_states) + + # unpatchify + if self.adaln_single is None: + height = width = int(hidden_states.shape[1] ** 0.5) + hidden_states = hidden_states.reshape( + shape=(-1, height, width, self.patch_size, self.patch_size, self.out_channels) + ) + hidden_states = torch.einsum("nhwpqc->nchpwq", hidden_states) + output = hidden_states.reshape( + shape=(-1, self.out_channels, height * self.patch_size, width * self.patch_size) + ) + output = rearrange(output, '(b f) c h w -> b c f h w', b=input_batch_size).contiguous() + + if not return_dict: + return (output,) + + return Transformer3DModelOutput(sample=output) + + def custom_load_state_dict(self, pretrained_model_path): + print(f'Loading pretrained model from {pretrained_model_path}...') + if 'safetensors' in pretrained_model_path: + from safetensors.torch import load_file as safe_load + checkpoint = safe_load(pretrained_model_path, device="cpu") + else: + checkpoint = torch.load(pretrained_model_path, map_location='cpu')['model'] + model_state_dict = self.state_dict() + if not 'pos_embed_masked_video.0.weight' in checkpoint: + checkpoint['pos_embed_masked_video.0.proj.weight'] = checkpoint['pos_embed.proj.weight'] + checkpoint['pos_embed_masked_video.0.proj.bias'] = checkpoint['pos_embed.proj.bias'] + if not 'pos_embed_mask.0.proj.weight' in checkpoint: + checkpoint['pos_embed_mask.0.proj.weight'] = checkpoint['pos_embed.proj.weight'] + checkpoint['pos_embed_mask.0.proj.bias'] = checkpoint['pos_embed.proj.bias'] + missing_keys, unexpected_keys = self.load_state_dict(checkpoint, strict=False) + + print(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + print(f'Successfully load {len(self.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {pretrained_model_path}!') + +# depth = num_layers * 2 +def OpenSoraInpaint_XL_122(**kwargs): + return OpenSoraInpaint(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + +inpaint_models = { + "OpenSoraInpaint_XL_122": OpenSoraInpaint_XL_122, +} + + +inpaint_models_class = { + "OpenSoraInpaint_XL_122": OpenSoraInpaint, +} \ No newline at end of file diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index 05436e388..b22d1af0b 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -145,13 +145,14 @@ def __init__( self.width = self.config.sample_size[1] self.patch_size_t = self.config.patch_size_t self.patch_size = self.config.patch_size - interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 - interpolation_scale_t = ( + # interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 + interpolation_scale_t = ((self.config.sample_size_t - 1) // 16) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t // 16 + self.interpolation_scale_t = ( self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t ) - interpolation_scale = ( - self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 30, - self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 40, + self.interpolation_scale = ( + self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 64, # 30 + self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 64, # 40 ) self.pos_embed = PatchEmbed( height=sample_size[0], @@ -159,7 +160,7 @@ def __init__( patch_size=patch_size, in_channels=in_channels, embed_dim=inner_dim, - interpolation_scale=interpolation_scale, + interpolation_scale=self.interpolation_scale, ) # # define temporal positional embedding @@ -176,7 +177,7 @@ def __init__( if self.use_rope: self.position_getter_2d = PositionGetter2D() self.position_getter_1d = PositionGetter1D() - rope_scaling = dict(type=rope_scaling_type, factor_2d=interpolation_scale, factor_1d=interpolation_scale_t) + rope_scaling = dict(type=rope_scaling_type, factor_2d=self.interpolation_scale, factor_1d=interpolation_scale_t) # 3. Define transformers blocks, spatial attention self.transformer_blocks = nn.ModuleList( diff --git a/opensora/models/diffusion/opensora/__init__.py b/opensora/models/diffusion/opensora/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/opensora/sample/pipeline_i2v.py b/opensora/sample/pipeline_i2v.py new file mode 100644 index 000000000..4e40244db --- /dev/null +++ b/opensora/sample/pipeline_i2v.py @@ -0,0 +1,836 @@ +# Copyright 2024 PixArt-Sigma Authors and The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import html +import inspect +import re +import urllib.parse as ul +from typing import Callable, List, Optional, Tuple, Union +import math +import torch +from transformers import T5EncoderModel, T5Tokenizer + +from diffusers.models import AutoencoderKL, Transformer2DModel +from diffusers.schedulers import DPMSolverMultistepScheduler +from diffusers.utils import ( + BACKENDS_MAPPING, + deprecate, + is_bs4_available, + is_ftfy_available, + logging, + replace_example_docstring, +) +from diffusers.utils.torch_utils import randn_tensor +from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput + + +logger = logging.get_logger(__name__) # pylint: disable=invalid-name + +if is_bs4_available(): + from bs4 import BeautifulSoup + +if is_ftfy_available(): + import ftfy + + +EXAMPLE_DOC_STRING = """ + Examples: + ```py + >>> import torch + >>> from diffusers import PixArtSigmaPipeline + + >>> # You can replace the checkpoint id with "PixArt-alpha/PixArt-Sigma-XL-2-512-MS" too. + >>> pipe = PixArtSigmaPipeline.from_pretrained( + ... "PixArt-alpha/PixArt-Sigma-XL-2-1024-MS", torch_dtype=torch.float16 + ... ) + >>> # Enable memory optimizations. + >>> # pipe.enable_model_cpu_offload() + + >>> prompt = "A small cactus with a happy face in the Sahara desert." + >>> image = pipe(prompt).images[0] + ``` +""" + + +# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps +def retrieve_timesteps( + scheduler, + num_inference_steps: Optional[int] = None, + device: Optional[Union[str, torch.device]] = None, + timesteps: Optional[List[int]] = None, + **kwargs, +): + """ + Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles + custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`. + + Args: + scheduler (`SchedulerMixin`): + The scheduler to get timesteps from. + num_inference_steps (`int`): + The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps` + must be `None`. + device (`str` or `torch.device`, *optional*): + The device to which the timesteps should be moved to. If `None`, the timesteps are not moved. + timesteps (`List[int]`, *optional*): + Custom timesteps used to support arbitrary spacing between timesteps. If `None`, then the default + timestep spacing strategy of the scheduler is used. If `timesteps` is passed, `num_inference_steps` + must be `None`. + + Returns: + `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the + second element is the number of inference steps. + """ + if timesteps is not None: + accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys()) + if not accepts_timesteps: + raise ValueError( + f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom" + f" timestep schedules. Please check whether you are using the correct scheduler." + ) + scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs) + timesteps = scheduler.timesteps + num_inference_steps = len(timesteps) + else: + scheduler.set_timesteps(num_inference_steps, device=device, **kwargs) + timesteps = scheduler.timesteps + return timesteps, num_inference_steps + + +class OpenSoraImage2VideoPipeline(DiffusionPipeline): + r""" + Pipeline for text-to-image generation using PixArt-Sigma. + """ + + bad_punct_regex = re.compile( + r"[" + + "#®•©™&@·º½¾¿¡§~" + + r"\)" + + r"\(" + + r"\]" + + r"\[" + + r"\}" + + r"\{" + + r"\|" + + "\\" + + r"\/" + + r"\*" + + r"]{1,}" + ) # noqa + + _optional_components = ["tokenizer", "text_encoder"] + model_cpu_offload_seq = "text_encoder->transformer->vae" + + def __init__( + self, + tokenizer: T5Tokenizer, + text_encoder: T5EncoderModel, + vae: AutoencoderKL, + transformer: Transformer2DModel, + scheduler: DPMSolverMultistepScheduler, + ): + super().__init__() + + self.register_modules( + tokenizer=tokenizer, text_encoder=text_encoder, vae=vae, transformer=transformer, scheduler=scheduler + ) + + # self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) + + # Copied from diffusers.pipelines.pixart_alpha.pipeline_pixart_alpha.PixArtAlphaPipeline.encode_prompt + def encode_prompt( + self, + prompt: Union[str, List[str]], + do_classifier_free_guidance: bool = True, + negative_prompt: str = "", + num_images_per_prompt: int = 1, + device: Optional[torch.device] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + clean_caption: bool = False, + max_sequence_length: int = 120, + **kwargs, + ): + r""" + Encodes the prompt into text encoder hidden states. + + Args: + prompt (`str` or `List[str]`, *optional*): + prompt to be encoded + negative_prompt (`str` or `List[str]`, *optional*): + The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds` + instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`). For + PixArt-Alpha, this should be "". + do_classifier_free_guidance (`bool`, *optional*, defaults to `True`): + whether to use classifier free guidance or not + num_images_per_prompt (`int`, *optional*, defaults to 1): + number of images that should be generated per prompt + device: (`torch.device`, *optional*): + torch device to place the resulting embeddings on + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. For PixArt-Alpha, it's should be the embeddings of the "" + string. + clean_caption (`bool`, defaults to `False`): + If `True`, the function will preprocess and clean the provided caption before encoding. + max_sequence_length (`int`, defaults to 120): Maximum sequence length to use for the prompt. + """ + + if "mask_feature" in kwargs: + deprecation_message = "The use of `mask_feature` is deprecated. It is no longer used in any computation and that doesn't affect the end results. It will be removed in a future version." + deprecate("mask_feature", "1.0.0", deprecation_message, standard_warn=False) + + if device is None: + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') + + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + + # See Section 3.1. of the paper. + max_length = max_sequence_length + + if prompt_embeds is None: + prompt = self._text_preprocessing(prompt, clean_caption=clean_caption) + text_inputs = self.tokenizer( + prompt, + padding="max_length", + max_length=max_length, + truncation=True, + add_special_tokens=True, + return_tensors="pt", + ) + text_input_ids = text_inputs.input_ids + untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids + + if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal( + text_input_ids, untruncated_ids + ): + removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_length - 1 : -1]) + logger.warning( + "The following part of your input was truncated because CLIP can only handle sequences up to" + f" {max_length} tokens: {removed_text}" + ) + + prompt_attention_mask = text_inputs.attention_mask + prompt_attention_mask = prompt_attention_mask.to(device) + + prompt_embeds = self.text_encoder(text_input_ids.to(device), attention_mask=prompt_attention_mask) + prompt_embeds = prompt_embeds[0] + + if self.text_encoder is not None: + dtype = self.text_encoder.dtype + elif self.transformer is not None: + dtype = self.transformer.dtype + else: + dtype = None + + prompt_embeds = prompt_embeds.to(dtype=dtype, device=device) + + bs_embed, seq_len, _ = prompt_embeds.shape + # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method + prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1) + prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1) + prompt_attention_mask = prompt_attention_mask.view(bs_embed, -1) + prompt_attention_mask = prompt_attention_mask.repeat(num_images_per_prompt, 1) + + # get unconditional embeddings for classifier free guidance + if do_classifier_free_guidance and negative_prompt_embeds is None: + uncond_tokens = [negative_prompt] * batch_size + uncond_tokens = self._text_preprocessing(uncond_tokens, clean_caption=clean_caption) + max_length = prompt_embeds.shape[1] + uncond_input = self.tokenizer( + uncond_tokens, + padding="max_length", + max_length=max_length, + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors="pt", + ) + negative_prompt_attention_mask = uncond_input.attention_mask + negative_prompt_attention_mask = negative_prompt_attention_mask.to(device) + + negative_prompt_embeds = self.text_encoder( + uncond_input.input_ids.to(device), attention_mask=negative_prompt_attention_mask + ) + negative_prompt_embeds = negative_prompt_embeds[0] + + if do_classifier_free_guidance: + # duplicate unconditional embeddings for each generation per prompt, using mps friendly method + seq_len = negative_prompt_embeds.shape[1] + + negative_prompt_embeds = negative_prompt_embeds.to(dtype=dtype, device=device) + + negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1) + negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1) + + negative_prompt_attention_mask = negative_prompt_attention_mask.view(bs_embed, -1) + negative_prompt_attention_mask = negative_prompt_attention_mask.repeat(num_images_per_prompt, 1) + else: + negative_prompt_embeds = None + negative_prompt_attention_mask = None + + return prompt_embeds, prompt_attention_mask, negative_prompt_embeds, negative_prompt_attention_mask + + # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs + def prepare_extra_step_kwargs(self, generator, eta): + # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature + # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers. + # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502 + # and should be between [0, 1] + + accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys()) + extra_step_kwargs = {} + if accepts_eta: + extra_step_kwargs["eta"] = eta + + # check if the scheduler accepts generator + accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys()) + if accepts_generator: + extra_step_kwargs["generator"] = generator + return extra_step_kwargs + + # Copied from diffusers.pipelines.pixart_alpha.pipeline_pixart_alpha.PixArtAlphaPipeline.check_inputs + def check_inputs( + self, + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds=None, + negative_prompt_embeds=None, + prompt_attention_mask=None, + negative_prompt_attention_mask=None, + ): + if num_frames <= 0: + raise ValueError(f"`num_frames` have to be positive but is {num_frames}.") + if height % 8 != 0 or width % 8 != 0: + raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.") + + if (callback_steps is None) or ( + callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0) + ): + raise ValueError( + f"`callback_steps` has to be a positive integer but is {callback_steps} of type" + f" {type(callback_steps)}." + ) + + if prompt is not None and prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to" + " only forward one of the two." + ) + elif prompt is None and prompt_embeds is None: + raise ValueError( + "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined." + ) + elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)): + raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}") + + if prompt is not None and negative_prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `prompt`: {prompt} and `negative_prompt_embeds`:" + f" {negative_prompt_embeds}. Please make sure to only forward one of the two." + ) + + if negative_prompt is not None and negative_prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:" + f" {negative_prompt_embeds}. Please make sure to only forward one of the two." + ) + + if prompt_embeds is not None and prompt_attention_mask is None: + raise ValueError("Must provide `prompt_attention_mask` when specifying `prompt_embeds`.") + + if negative_prompt_embeds is not None and negative_prompt_attention_mask is None: + raise ValueError("Must provide `negative_prompt_attention_mask` when specifying `negative_prompt_embeds`.") + + if prompt_embeds is not None and negative_prompt_embeds is not None: + if prompt_embeds.shape != negative_prompt_embeds.shape: + raise ValueError( + "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but" + f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`" + f" {negative_prompt_embeds.shape}." + ) + if prompt_attention_mask.shape != negative_prompt_attention_mask.shape: + raise ValueError( + "`prompt_attention_mask` and `negative_prompt_attention_mask` must have the same shape when passed directly, but" + f" got: `prompt_attention_mask` {prompt_attention_mask.shape} != `negative_prompt_attention_mask`" + f" {negative_prompt_attention_mask.shape}." + ) + + # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._text_preprocessing + def _text_preprocessing(self, text, clean_caption=False): + if clean_caption and not is_bs4_available(): + logger.warning(BACKENDS_MAPPING["bs4"][-1].format("Setting `clean_caption=True`")) + logger.warning("Setting `clean_caption` to False...") + clean_caption = False + + if clean_caption and not is_ftfy_available(): + logger.warning(BACKENDS_MAPPING["ftfy"][-1].format("Setting `clean_caption=True`")) + logger.warning("Setting `clean_caption` to False...") + clean_caption = False + + if not isinstance(text, (tuple, list)): + text = [text] + + def process(text: str): + if clean_caption: + text = self._clean_caption(text) + text = self._clean_caption(text) + else: + text = text.lower().strip() + return text + + return [process(t) for t in text] + + # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._clean_caption + def _clean_caption(self, caption): + caption = str(caption) + caption = ul.unquote_plus(caption) + caption = caption.strip().lower() + caption = re.sub("", "person", caption) + # urls: + caption = re.sub( + r"\b((?:https?:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", # noqa + "", + caption, + ) # regex for urls + caption = re.sub( + r"\b((?:www:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", # noqa + "", + caption, + ) # regex for urls + # html: + caption = BeautifulSoup(caption, features="html.parser").text + + # @ + caption = re.sub(r"@[\w\d]+\b", "", caption) + + # 31C0—31EF CJK Strokes + # 31F0—31FF Katakana Phonetic Extensions + # 3200—32FF Enclosed CJK Letters and Months + # 3300—33FF CJK Compatibility + # 3400—4DBF CJK Unified Ideographs Extension A + # 4DC0—4DFF Yijing Hexagram Symbols + # 4E00—9FFF CJK Unified Ideographs + caption = re.sub(r"[\u31c0-\u31ef]+", "", caption) + caption = re.sub(r"[\u31f0-\u31ff]+", "", caption) + caption = re.sub(r"[\u3200-\u32ff]+", "", caption) + caption = re.sub(r"[\u3300-\u33ff]+", "", caption) + caption = re.sub(r"[\u3400-\u4dbf]+", "", caption) + caption = re.sub(r"[\u4dc0-\u4dff]+", "", caption) + caption = re.sub(r"[\u4e00-\u9fff]+", "", caption) + ####################################################### + + # все виды тире / all types of dash --> "-" + caption = re.sub( + r"[\u002D\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E3B\u2E40\u301C\u3030\u30A0\uFE31\uFE32\uFE58\uFE63\uFF0D]+", # noqa + "-", + caption, + ) + + # кавычки к одному стандарту + caption = re.sub(r"[`´«»“”¨]", '"', caption) + caption = re.sub(r"[‘’]", "'", caption) + + # " + caption = re.sub(r""?", "", caption) + # & + caption = re.sub(r"&", "", caption) + + # ip adresses: + caption = re.sub(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", " ", caption) + + # article ids: + caption = re.sub(r"\d:\d\d\s+$", "", caption) + + # \n + caption = re.sub(r"\\n", " ", caption) + + # "#123" + caption = re.sub(r"#\d{1,3}\b", "", caption) + # "#12345.." + caption = re.sub(r"#\d{5,}\b", "", caption) + # "123456.." + caption = re.sub(r"\b\d{6,}\b", "", caption) + # filenames: + caption = re.sub(r"[\S]+\.(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)", "", caption) + + # + caption = re.sub(r"[\"\']{2,}", r'"', caption) # """AUSVERKAUFT""" + caption = re.sub(r"[\.]{2,}", r" ", caption) # """AUSVERKAUFT""" + + caption = re.sub(self.bad_punct_regex, r" ", caption) # ***AUSVERKAUFT***, #AUSVERKAUFT + caption = re.sub(r"\s+\.\s+", r" ", caption) # " . " + + # this-is-my-cute-cat / this_is_my_cute_cat + regex2 = re.compile(r"(?:\-|\_)") + if len(re.findall(regex2, caption)) > 3: + caption = re.sub(regex2, " ", caption) + + caption = ftfy.fix_text(caption) + caption = html.unescape(html.unescape(caption)) + + caption = re.sub(r"\b[a-zA-Z]{1,3}\d{3,15}\b", "", caption) # jc6640 + caption = re.sub(r"\b[a-zA-Z]+\d+[a-zA-Z]+\b", "", caption) # jc6640vc + caption = re.sub(r"\b\d+[a-zA-Z]+\d+\b", "", caption) # 6640vc231 + + caption = re.sub(r"(worldwide\s+)?(free\s+)?shipping", "", caption) + caption = re.sub(r"(free\s)?download(\sfree)?", "", caption) + caption = re.sub(r"\bclick\b\s(?:for|on)\s\w+", "", caption) + caption = re.sub(r"\b(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)(\simage[s]?)?", "", caption) + caption = re.sub(r"\bpage\s+\d+\b", "", caption) + + caption = re.sub(r"\b\d*[a-zA-Z]+\d+[a-zA-Z]+\d+[a-zA-Z\d]*\b", r" ", caption) # j2d1a2a... + + caption = re.sub(r"\b\d+\.?\d*[xх×]\d+\.?\d*\b", "", caption) + + caption = re.sub(r"\b\s+\:\s+", r": ", caption) + caption = re.sub(r"(\D[,\./])\b", r"\1 ", caption) + caption = re.sub(r"\s+", " ", caption) + + caption.strip() + + caption = re.sub(r"^[\"\']([\w\W]+)[\"\']$", r"\1", caption) + caption = re.sub(r"^[\'\_,\-\:;]", r"", caption) + caption = re.sub(r"[\'\_,\-\:\-\+]$", r"", caption) + caption = re.sub(r"^\.\S+$", "", caption) + + return caption.strip() + + # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents + def prepare_latents(self, batch_size, num_channels_latents, num_frames, height, width, dtype, device, generator, latents=None): + shape = ( + batch_size, + num_channels_latents, + (math.ceil((int(num_frames) - 1) / self.vae.vae_scale_factor[0]) + 1) if int(num_frames) % 2 == 1 else math.ceil(int(num_frames) / self.vae.vae_scale_factor[0]), + math.ceil(int(height) / self.vae.vae_scale_factor[1]), + math.ceil(int(width) / self.vae.vae_scale_factor[2]), + ) + if isinstance(generator, list) and len(generator) != batch_size: + raise ValueError( + f"You have passed a list of generators of length {len(generator)}, but requested an effective batch" + f" size of {batch_size}. Make sure the batch size matches the length of the generators." + ) + + if latents is None: + latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype) + else: + latents = latents.to(device) + + # scale the initial noise by the standard deviation required by the scheduler + latents = latents * self.scheduler.init_noise_sigma + + + return latents + + @torch.no_grad() + @replace_example_docstring(EXAMPLE_DOC_STRING) + def __call__( + self, + init_image: torch.Tensor, + prompt: Union[str, List[str]] = None, + negative_prompt: str = "", + num_inference_steps: int = 20, + timesteps: List[int] = None, + guidance_scale: float = 4.5, + num_images_per_prompt: Optional[int] = 1, + num_frames: Optional[int] = None, + height: Optional[int] = None, + width: Optional[int] = None, + eta: float = 0.0, + generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, + latents: Optional[torch.FloatTensor] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + output_type: Optional[str] = "pil", + return_dict: bool = True, + callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, + callback_steps: int = 1, + clean_caption: bool = True, + use_resolution_binning: bool = True, + max_sequence_length: int = 300, + **kwargs, + ) -> Union[ImagePipelineOutput, Tuple]: + """ + Function invoked when calling the pipeline for generation. + + Args: + prompt (`str` or `List[str]`, *optional*): + The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. + instead. + negative_prompt (`str` or `List[str]`, *optional*): + The prompt or prompts not to guide the image generation. If not defined, one has to pass + `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is + less than `1`). + num_inference_steps (`int`, *optional*, defaults to 100): + The number of denoising steps. More denoising steps usually lead to a higher quality image at the + expense of slower inference. + timesteps (`List[int]`, *optional*): + Custom timesteps to use for the denoising process. If not defined, equal spaced `num_inference_steps` + timesteps are used. Must be in descending order. + guidance_scale (`float`, *optional*, defaults to 4.5): + Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). + `guidance_scale` is defined as `w` of equation 2. of [Imagen + Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > + 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, + usually at the expense of lower image quality. + num_images_per_prompt (`int`, *optional*, defaults to 1): + The number of images to generate per prompt. + height (`int`, *optional*, defaults to self.unet.config.sample_size): + The height in pixels of the generated image. + width (`int`, *optional*, defaults to self.unet.config.sample_size): + The width in pixels of the generated image. + eta (`float`, *optional*, defaults to 0.0): + Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to + [`schedulers.DDIMScheduler`], will be ignored for others. + generator (`torch.Generator` or `List[torch.Generator]`, *optional*): + One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) + to make generation deterministic. + latents (`torch.FloatTensor`, *optional*): + Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image + generation. Can be used to tweak the same generation with different prompts. If not provided, a latents + tensor will ge generated by sampling using the supplied random `generator`. + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + prompt_attention_mask (`torch.FloatTensor`, *optional*): Pre-generated attention mask for text embeddings. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not + provided, negative_prompt_embeds will be generated from `negative_prompt` input argument. + negative_prompt_attention_mask (`torch.FloatTensor`, *optional*): + Pre-generated attention mask for negative text embeddings. + output_type (`str`, *optional*, defaults to `"pil"`): + The output format of the generate image. Choose between + [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~pipelines.stable_diffusion.IFPipelineOutput`] instead of a plain tuple. + callback (`Callable`, *optional*): + A function that will be called every `callback_steps` steps during inference. The function will be + called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. + callback_steps (`int`, *optional*, defaults to 1): + The frequency at which the `callback` function will be called. If not specified, the callback will be + called at every step. + clean_caption (`bool`, *optional*, defaults to `True`): + Whether or not to clean the caption before creating embeddings. Requires `beautifulsoup4` and `ftfy` to + be installed. If the dependencies are not installed, the embeddings will be created from the raw + prompt. + use_resolution_binning (`bool` defaults to `True`): + If set to `True`, the requested height and width are first mapped to the closest resolutions using + `ASPECT_RATIO_1024_BIN`. After the produced latents are decoded into images, they are resized back to + the requested resolution. Useful for generating non-square images. + max_sequence_length (`int` defaults to 120): Maximum sequence length to use with the `prompt`. + + Examples: + + Returns: + [`~pipelines.ImagePipelineOutput`] or `tuple`: + If `return_dict` is `True`, [`~pipelines.ImagePipelineOutput`] is returned, otherwise a `tuple` is + returned where the first element is a list with the generated images + """ + # 1. Check inputs. Raise error if not correct + num_frames = num_frames or self.transformer.config.sample_size_t * self.vae.vae_scale_factor[0] + height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] + width = width or self.transformer.config.sample_size[1] * self.vae.vae_scale_factor[2] + self.check_inputs( + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds, + negative_prompt_embeds, + prompt_attention_mask, + negative_prompt_attention_mask, + ) + + # 2. Default height and width to transformer + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + # import ipdb;ipdb.set_trace() + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') + + # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) + # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` + # corresponds to doing no classifier free guidance. + do_classifier_free_guidance = guidance_scale > 1.0 + + # 3. Encode input prompt + ( + prompt_embeds, + prompt_attention_mask, + negative_prompt_embeds, + negative_prompt_attention_mask, + ) = self.encode_prompt( + prompt, + do_classifier_free_guidance, + negative_prompt=negative_prompt, + num_images_per_prompt=num_images_per_prompt, + device=device, + prompt_embeds=prompt_embeds, + negative_prompt_embeds=negative_prompt_embeds, + prompt_attention_mask=prompt_attention_mask, + negative_prompt_attention_mask=negative_prompt_attention_mask, + clean_caption=clean_caption, + max_sequence_length=max_sequence_length, + ) + if do_classifier_free_guidance: + prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) + prompt_attention_mask = torch.cat([negative_prompt_attention_mask, prompt_attention_mask], dim=0) + + # 4. Prepare timesteps + timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device, timesteps) + + # 5. Prepare latents. + latent_channels = self.transformer.config.in_channels + latents = self.prepare_latents( + batch_size * num_images_per_prompt, + latent_channels, + num_frames, + height, + width, + prompt_embeds.dtype, + device, + generator, + latents, + ) + + # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline + extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) + + # 6.1 Prepare micro-conditions. + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + + # 7. Denoising loop + num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) + + # NOTE I2V + if len(init_image.shape) == 3: + init_image = init_image.unsqueeze(1) # C H W -> C 1 H W + elif len(init_image.shape) == 4: + init_image = init_image.transpose(0, 1) # 1 C H W -> C 1 H W + + init_image = init_image.unsqueeze(0).repeat((batch_size * num_images_per_prompt, 1, 1, 1, 1)) + input_video = torch.zeros([batch_size * num_images_per_prompt, 3, num_frames, height, width], device=device) + input_video = torch.cat([init_image, input_video[:, :, 1:]], dim=2) + + mask = torch.ones_like(input_video, device=device) + mask[:, :, 0] = 0 + + masked_video = input_video * (mask < 0.5) + + masked_video = self.vae.encode(masked_video).to(device) + mask = self.vae.encode(mask).to(device) + + with self.progress_bar(total=num_inference_steps) as progress_bar: + for i, t in enumerate(timesteps): + latent_model_input = torch.cat([latents, masked_video, mask], dim=1) + latent_model_input = torch.cat([latent_model_input] * 2) if do_classifier_free_guidance else latents + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) + + current_timestep = t + if not torch.is_tensor(current_timestep): + # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can + # This would be a good case for the `match` statement (Python 3.10+) + is_mps = latent_model_input.device.type == "mps" + if isinstance(current_timestep, float): + dtype = torch.float32 if is_mps else torch.float64 + else: + dtype = torch.int32 if is_mps else torch.int64 + current_timestep = torch.tensor([current_timestep], dtype=dtype, device=latent_model_input.device) + elif len(current_timestep.shape) == 0: + current_timestep = current_timestep[None].to(latent_model_input.device) + # broadcast to batch dimension in a way that's compatible with ONNX/Core ML + current_timestep = current_timestep.expand(latent_model_input.shape[0]) + + # import ipdb;ipdb.set_trace() + if prompt_embeds.ndim == 3: + prompt_embeds = prompt_embeds.unsqueeze(1) # b l d -> b 1 l d + if prompt_attention_mask.ndim == 2: + prompt_attention_mask = prompt_attention_mask.unsqueeze(1) # b l -> b 1 l + # prepare attention_mask. + # b c t h w -> b t h w + attention_mask = torch.ones_like(latent_model_input)[:, 0] + # predict noise model_output + noise_pred = self.transformer( + latent_model_input, + attention_mask=attention_mask, + encoder_hidden_states=prompt_embeds, + encoder_attention_mask=prompt_attention_mask, + timestep=current_timestep, + added_cond_kwargs=added_cond_kwargs, + return_dict=False, + )[0] + + # perform guidance + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + # learned sigma + if self.transformer.config.out_channels // 2 == latent_channels: + noise_pred = noise_pred.chunk(2, dim=1)[0] + else: + noise_pred = noise_pred + + # compute previous image: x_t -> x_t-1 + latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] + + # call the callback, if provided + if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): + progress_bar.update() + if callback is not None and i % callback_steps == 0: + step_idx = i // getattr(self.scheduler, "order", 1) + callback(step_idx, t, latents) + # import ipdb;ipdb.set_trace() + # latents = latents.squeeze(2) + if not output_type == "latent": + # b t h w c + image = self.decode_latents(latents) + image = image[:, :num_frames, :height, :width] + else: + image = latents + + # Offload all models + self.maybe_free_model_hooks() + + if not return_dict: + return (image,) + + return ImagePipelineOutput(images=image) + + + def decode_latents(self, latents): + video = self.vae.decode(latents.to(self.vae.vae.dtype)) + video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c + # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 + return video diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 084e9657c..6c3ac7057 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -15,6 +15,7 @@ from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer import os, sys +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) from opensora.models.ae import ae_stride_config, getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper @@ -32,6 +33,7 @@ def main(args): # torch.manual_seed(args.seed) + torch.set_grad_enabled(False) weight_dtype = torch.float16 device = torch.device(args.device) @@ -54,13 +56,13 @@ def main(args): transformer_model = UDiTT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: - transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, + transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) # tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - text_encoder = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) - tokenizer = T5Tokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) # set eval mode @@ -92,6 +94,8 @@ def main(args): scheduler = EulerDiscreteScheduler.from_pretrained("stabilityai/stable-video-diffusion-img2vid", subfolder="scheduler", cache_dir=args.cache_dir) + print(args.sample_method, scheduler.__class__.__name__) + pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, @@ -120,7 +124,7 @@ def main(args): num_images_per_prompt=1, mask_feature=True, device=args.device, - max_sequence_length=100, + max_sequence_length=300, ).images try: if args.num_frames == 1: diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py new file mode 100644 index 000000000..ffe25c9b9 --- /dev/null +++ b/opensora/train/train_inpaint.py @@ -0,0 +1,913 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +import logging +import math +import os +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + pass +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import DistributedType, ProjectConfiguration, set_seed +from huggingface_hub import create_repo +from packaging import version +from tqdm.auto import tqdm +from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.utils.dataset_utils import Collate +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.sample.pipeline_opensora import OpenSoraPipeline + + +# for validation +from PIL import Image +from torchvision import transforms +from torchvision.transforms import Lambda +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +from opensora.sample.pipeline_i2v import OpenSoraImage2VideoPipeline + + + +import glob + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + + +def save_video(video, save_path='output_video.mp4', fps=24): + import cv2 + + frame_count, height, width, channels = video.shape + + fourcc = cv2.VideoWriter_fourcc(*'mp4v') + out = cv2.VideoWriter(save_path, fourcc, fps, (width, height)) + + for i in range(frame_count): + frame = video[i].cpu().numpy() + frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) + out.write(frame) + +@torch.inference_mode() +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): + + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + prompt_file = os.path.join(validation_dir, "prompt.txt") + + with open(prompt_file, 'r') as f: + validation_prompt = f.readlines() + + validation_images = glob.glob(os.path.join(validation_dir, "*.png")) + validation_images = sorted(validation_images) + + + resize = [CenterCropResizeVideo((args.max_height, args.max_width)),] + norm_fun = Lambda(lambda x: 2. * x - 1.) + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + + logger.info(f"Running {'normal' if not ema else 'ema'} validation....\n") + model = accelerator.unwrap_model(model) + + scheduler = PNDMScheduler() + pipeline = OpenSoraImage2VideoPipeline( + vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model + ).to(device=accelerator.device) + + videos = [] + for prompt, init_image in zip(validation_prompt, validation_images): + logger.info('Processing the ({}) prompt and the init image ({})'.format(prompt, init_image)) + init_image = Image.open(init_image).convert("RGB") + init_image = torch.from_numpy(np.copy(np.array(init_image))) + init_image = rearrange(init_image, 'h w c -> c h w').unsqueeze(0) + init_image = transform(init_image).to(accelerator.device, dtype=torch.float32) + video = pipeline( + prompt=prompt, + init_image=init_image, + num_frames=args.num_frames, + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + ).images + videos.append(video[0]) + # import ipdb;ipdb.set_trace() + + # Save the generated videos + save_dir = os.path.join(validation_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") + os.makedirs(save_dir, exist_ok=True) + for idx, video in enumerate(videos): + save_video(video, os.path.join(save_dir, f"video_{idx:06d}.mp4")) + + print("deleta validation pipeline...") + del pipeline + gc.collect() + torch.cuda.empty_cache() + + + + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss + + +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + + logging_dir = Path(args.output_dir, args.logging_dir) + + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) + npu_config.seed_everything() + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if accelerator.is_main_process: + print("--------------------------training args--------------------------") + print(args) + print("-----------------------------------------------------------------") + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + kwargs = {} + ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = args.num_frames // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae] * 2, # 因为要加载预训练权重,所以这里out_channels仍然设置为2倍 + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + downsampler=args.downsampler, + compress_kv_factor=args.compress_kv_factor, + use_rope=args.use_rope, + model_max_length=args.model_max_length, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # # use pretrained model? + if args.pretrained: + model.custom_load_state_dict(args.pretrained) + + + # Freeze vae and text encoders. + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + # Set model as trainable. + model.train() + + noise_scheduler = DDPMScheduler(rescale_betas_zero_snr=args.zero_terminal_snr) + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + ae.vae.to(accelerator.device, dtype=torch.float32) + # ae.vae.to(accelerator.device, dtype=weight_dtype) + text_enc.to(accelerator.device, dtype=weight_dtype) + + # Create EMA for the unet. + if args.use_ema: + ema_model = deepcopy(model) + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) + ema_model.load_state_dict(load_model.state_dict()) + ema_model.to(accelerator.device) + del load_model + + for i in range(len(models)): + # pop models so that they are not loaded again + model = models.pop() + + # load diffusers style into model + load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + params_to_optimize = model.parameters() + # Optimizer creation + if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): + logger.warning( + f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]." + "Defaulting to adamW" + ) + args.optimizer = "adamw" + + if args.use_8bit_adam and not args.optimizer.lower() == "adamw": + logger.warning( + f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " + f"set to {args.optimizer.lower()}" + ) + + if args.optimizer.lower() == "adamw": + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + if args.optimizer.lower() == "prodigy": + try: + import prodigyopt + except ImportError: + raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`") + + optimizer_class = prodigyopt.Prodigy + + if args.learning_rate <= 0.1: + logger.warning( + "Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0" + ) + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + beta3=args.prodigy_beta3, + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + decouple=args.prodigy_decouple, + use_bias_correction=args.prodigy_use_bias_correction, + safeguard_warmup=args.prodigy_safeguard_warmup, + ) + + # Setup data: + train_dataset = getdataset(args) + train_dataloader = torch.utils.data.DataLoader( + train_dataset, + shuffle=True, + # pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + # prefetch_factor=8 + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + model, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_model.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + # if accelerator.is_main_process: + # accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {model}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_model.step(model.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + one_step_duration = end_time - start_time + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, " + f"train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) + progress_info.train_loss = 0.0 + + # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. + if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if accelerator.is_main_process and args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(model_input, model_kwargs, prof): + global start_time + start_time = time.time() + + noise = torch.randn_like(model_input) + if args.noise_offset: + # https://www.crosslabs.org//blog/diffusion-with-offset-noise + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1), + device=model_input.device) + + bsz = model_input.shape[0] + # Sample a random timestep for each image without bias. + timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + + # Add noise to the model input according to the noise magnitude at each timestep + # (this is the forward diffusion process) + + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) + assert not torch.any(torch.isnan(noisy_model_input)), 'torch.any(torch.isnan(noisy_model_input))' + + if model_kwargs.get("masked_x", None) is not None and model_kwargs.get("mask", None) is not None: + masked_x = model_kwargs.pop("masked_x") + mask = model_kwargs.pop("mask") + + model_pred = model( + torch.cat([noisy_model_input, masked_x, mask], dim=1), + timesteps, + **model_kwargs + )[0] + + + model_pred = torch.chunk(model_pred, 2, dim=1)[0] + assert not torch.any(torch.isnan(model_pred)), 'torch.any(torch.isnan(model_pred))' + + # Get the target for loss depending on the prediction type + if args.prediction_type is not None: + # set prediction_type of scheduler if defined + noise_scheduler.register_to_config(prediction_type=args.prediction_type) + + if noise_scheduler.config.prediction_type == "epsilon": + target = noise + elif noise_scheduler.config.prediction_type == "v_prediction": + target = noise_scheduler.get_velocity(model_input, noise, timesteps) + elif noise_scheduler.config.prediction_type == "sample": + # We set the target to latents here, but the model_pred will return the noise sample prediction. + target = model_input + # We will have to subtract the noise residual from the prediction to get the target sample. + model_pred = model_pred - noise + else: + raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + + if args.snr_gamma is None: + loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + else: + # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. + # Since we predict the noise instead of x_0, the original formulation is slightly changed. + # This is discussed in Section 4.2 of the same paper. + snr = compute_snr(noise_scheduler, timesteps) + mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min( + dim=1 + )[0] + if noise_scheduler.config.prediction_type == "epsilon": + mse_loss_weights = mse_loss_weights / snr + elif noise_scheduler.config.prediction_type == "v_prediction": + mse_loss_weights = mse_loss_weights / (snr + 1) + + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights + loss = loss.mean() + + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = model.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + # for tracker in accelerator.trackers: + # if tracker.name == "wandb": + # if progress_info.global_step % args.checkpointing_steps != 0: + # if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + # tracker.log( + # {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + # elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + # tracker.log( + # {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + + if progress_info.global_step % args.checkpointing_steps == 0: + + # if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step) + + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + if npu_config is None: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + + if prof is not None: + prof.step() + + + return loss + + def train_one_step(step_, data_item_, prof_=None): + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B 3 * C T H W + + attn_mask = attn_mask.to(accelerator.device) # B T H W + input_ids = input_ids.to(accelerator.device) # B 1 L + cond_mask = cond_mask.to(accelerator.device) # B 1 L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + assert not torch.any(torch.isnan(x)), 'before vae' + assert not torch.any(torch.isnan(input_ids)), 'before text_enc' + assert not torch.any(torch.isnan(cond_mask)), 'before text_enc' + assert not torch.any(torch.isnan(attn_mask)), 'before text_enc' + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1 L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1 L D + + assert not torch.any(torch.isnan(cond)), 'after text_enc' + + cond = cond.reshape(B, N, L, -1) + + # Map input images to latent space + normalize latents + assert args.use_image_num == 0, 'Inpainting mode does not support image joint training' + + x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:9] + x, masked_x, mask = ae.encode(x), ae.encode(masked_x), ae.encode(mask) + + assert not torch.any(torch.isnan(x)), 'after vae' + assert not torch.any(torch.isnan(masked_x)), 'after vae' + assert not torch.any(torch.isnan(mask)), 'after vae' + + with accelerator.accumulate(model): + x = x.to(weight_dtype) + masked_x = masked_x.to(weight_dtype) + mask = mask.to(weight_dtype) + assert not torch.any(torch.isnan(x)), 'after vae' + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, + masked_x=masked_x, mask=mask) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + if train_one_step(step, data_item, prof_): + break + + if step >= 2 and torch_npu is not None and npu_config is not None: + npu_config.free_mm() + + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # dataset & dataloader + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + + # inpaint dataset + parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode + parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode + parser.add_argument("--default_text_ratio", type=float, default=0.1) + + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--model_max_length", type=int, default=512) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + + # text encoder & vae & diffusion model + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument("--downsampler", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + + # diffusion setting + parser.add_argument("--zero_terminal_snr", action="store_true", help="Whether to zero the terminal SNR.") + parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + + # validation & logs + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--checkpointing_steps", type=int, default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument("--resume_from_checkpoint", type=str, default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument("--logging_dir", type=str, default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument("--report_to", type=str, default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + # optimizer & scheduler + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") + parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--prodigy_beta3", type=float, default=None, + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", + ) + parser.add_argument("--lr_scheduler", type=str, default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument("--allow_tf32", action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + + parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") + + + args = parser.parse_args() + main(args) diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index 93625a5a1..7861ee3af 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -1,18 +1,17 @@ -CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_lr1e-4_snr5_ema_ps11_ds22/checkpoint-8000/model \ +CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v.py \ + --model_path "/data01/transition/Open-Sora-Plan_models" \ --version 65x512x512 \ - --num_frames 1 \ - --height 256 \ - --width 256 \ - --cache_dir "cache_dir" \ + --num_frames 65 \ + --height 512 \ + --width 512 \ + --cache_dir "/data01/transition/cache_dir" \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8" \ - --save_img_path "./sample_image256_256_ps22_ds11_ckpt8000" \ + --ae_path "/data01/transition/Open-Sora-Plan_models/vae" \ + --save_img_path "./sample_video_65x512x512" \ --fps 24 \ - --guidance_scale 2.0 \ - --num_sampling_steps 20 \ + --guidance_scale 7.5 \ + --num_sampling_steps 50 \ --enable_tiling \ - --sample_method DDPM \ - --model_3d \ No newline at end of file + --sample_method PNDM \ \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh new file mode 100644 index 000000000..2070bab2a --- /dev/null +++ b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh @@ -0,0 +1,55 @@ +# export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# # export WANDB_MODE="offline" +# export ENTITY="linbin" +# export PROJECT="testnpu21d_" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# # NCCL setting IB网卡时用 +# export NCCL_PXN_DISABLE=0 +# export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_IB_GID_INDEX=3 +# export NCCL_ALGO=Ring +# export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_inpaint.py \ + --model OpenSoraInpaint_XL_122 \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --cache_dir "/data01/transition/cache_dir" \ + --dataset inpaint \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/data01/transition/Open-Sora-Plan_models/vae" \ + --video_data "scripts/train_data/video_data_debug_inpaint.txt" \ + --sample_rate 1 \ + --num_frames 65\ + --max_height 512 \ + --max_width 512 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=100000 \ + --learning_rate=1e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=200 \ + --output_dir="inpaint_65x512x512_1node_bs4_lr2e-5" \ + --allow_tf32 \ + --model_max_length 300 \ + --enable_tiling \ + --pretrained "/data01/transition/Open-Sora-Plan_models/65x512x512/diffusion_pytorch_model.safetensors" \ + --validation_dir "/data01/transition/hw/Open-Sora-Plan/validation_dir" \ + --use_image_num 0 \ + --cfg 0.1 \ + --ema_start_step 0 \ + --use_ema \ + --i2v_ratio 0.4 \ + --transition_ratio 0.4 \ + --default_text_ratio 0.1 \ + # --snr_gamma 5.0 \ + # --zero_terminal_snr \ + # --noise_offset 0.02 \ \ No newline at end of file diff --git a/scripts/train_data/video_data_debug_inpaint.txt b/scripts/train_data/video_data_debug_inpaint.txt new file mode 100644 index 000000000..62ec1394a --- /dev/null +++ b/scripts/train_data/video_data_debug_inpaint.txt @@ -0,0 +1 @@ +/data01/transition/opensora_data/mixkit,/data01/transition/opensora_data/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file diff --git a/validation_dir/prompt.txt b/validation_dir/prompt.txt new file mode 100644 index 000000000..3b3c669e8 --- /dev/null +++ b/validation_dir/prompt.txt @@ -0,0 +1,3 @@ +A rocket ascends slowly into the sky. +Along the coast, variously sized boats float on the lake, with the camera gradually zooming in. +The landscape at sunset is profound and expansive. \ No newline at end of file From 9b4ac0cbd96989cac2442098cf1a7b44d7d49cfe Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Fri, 21 Jun 2024 10:42:04 +0800 Subject: [PATCH 056/134] optimize rope for npu --- examples/prompt_list_0.txt | 6 +-- .../diffusion/opensora/modeling_opensora.py | 7 ++- opensora/models/diffusion/opensora/modules.py | 12 +++++ opensora/models/diffusion/opensora/rope.py | 44 ++++++++++----- scripts/text_condition/npu/sample_video.sh | 4 +- .../text_condition/npu/train_image3d_240p.sh | 6 +-- .../npu/train_image3d_240p_rope.sh | 51 ++++++++++++++++++ .../text_condition/npu/train_image3d_256p.sh | 50 +++++++++++++++++ .../npu/train_image3d_256p_rope.sh | 51 ++++++++++++++++++ .../text_condition/npu/train_image3d_480p.sh | 9 ++-- .../npu/train_image3d_480p_rope.sh | 53 +++++++++++++++++++ 11 files changed, 267 insertions(+), 26 deletions(-) create mode 100644 scripts/text_condition/npu/train_image3d_240p_rope.sh create mode 100644 scripts/text_condition/npu/train_image3d_256p.sh create mode 100644 scripts/text_condition/npu/train_image3d_256p_rope.sh create mode 100644 scripts/text_condition/npu/train_image3d_480p_rope.sh diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index 4a46e7a49..53b4d0975 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -9,10 +9,10 @@ A gorgeously rendered papercraft world of a coral reef, rife with colorful fish This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. The majestic beauty of a waterfall cascading down a cliff into a serene lake. -非常好看的夜景 +Sunset over the sea. a cat wearing sunglasses and working as a lifeguard at pool. -一个橘色的猫 -a word "你好" in the blackboard". +Slow pan upward of blazing oak fire in an indoor fireplace. +Yellow and black tropical fish dart through the sea. a serene winter scene in a forest. The forest is blanketed in a thick layer of snow, which has settled on the branches of the trees, creating a canopy of white. The trees, a mix of evergreens and deciduous, stand tall and silent, their forms partially obscured by the snow. The ground is a uniform white, with no visible tracks or signs of human activity. The sun is low in the sky, casting a warm glow that contrasts with the cool tones of the snow. The light filters through the trees, creating a soft, diffused illumination that highlights the texture of the snow and the contours of the trees. The overall style of the scene is naturalistic, with a focus on the tranquility and beauty of the winter landscape. a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water. A serene waterfall cascading down moss-covered rocks, its soothing sound creating a harmonious symphony with nature. diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 98400adaf..2ff14737c 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -610,9 +610,12 @@ def OpenSoraT2V_B_122(**kwargs): norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) def OpenSoraT2V_L_122(**kwargs): + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + +def OpenSoraT2V_ROPE_L_122(**kwargs): return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2304, **kwargs) - # def OpenSoraT2V_S_222(**kwargs): # return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) @@ -638,6 +641,7 @@ def OpenSoraT2V_B_222(**kwargs): "OpenSoraT2V-S/122": OpenSoraT2V_S_122, "OpenSoraT2V-B/122": OpenSoraT2V_B_122, "OpenSoraT2V-L/122": OpenSoraT2V_L_122, + "OpenSoraT2V-ROPE-L/122": OpenSoraT2V_ROPE_L_122, "OpenSoraT2V-S/111": OpenSoraT2V_S_111, "OpenSoraT2V-B/111": OpenSoraT2V_B_111, "OpenSoraT2V-L/111": OpenSoraT2V_L_111, @@ -654,6 +658,7 @@ def OpenSoraT2V_B_222(**kwargs): "OpenSoraT2V-S/122": OpenSoraT2V, "OpenSoraT2V-B/122": OpenSoraT2V, "OpenSoraT2V-L/122": OpenSoraT2V, + "OpenSoraT2V-ROPE-L/122": OpenSoraT2V, "OpenSoraT2V-S/111": OpenSoraT2V, "OpenSoraT2V-B/111": OpenSoraT2V, "OpenSoraT2V-L/111": OpenSoraT2V, diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index abebf55ee..e07e83a49 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -677,6 +677,18 @@ def __call__( else: dtype = None + if self.use_rope: + query = query.view(batch_size, -1, attn.heads, head_dim) + key = key.view(batch_size, -1, attn.heads, head_dim) + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + if query.shape == key.shape: + key = self.rope(key, pos_thw) + + query = query.view(batch_size, -1, attn.heads * head_dim) + key = key.view(batch_size, -1, attn.heads * head_dim) + with set_run_dtype(query, dtype): query, key, value = npu_config.set_current_run_dtype([query, key, value]) hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", diff --git a/opensora/models/diffusion/opensora/rope.py b/opensora/models/diffusion/opensora/rope.py index 39274e096..5adc3adca 100644 --- a/opensora/models/diffusion/opensora/rope.py +++ b/opensora/models/diffusion/opensora/rope.py @@ -1,4 +1,10 @@ import torch +try: + import torch_npu + from opensora.npu_config import npu_config, set_run_dtype +except: + torch_npu = None + npu_config = None class PositionGetter3D(object): """ return positions of patches """ @@ -11,8 +17,14 @@ def __call__(self, b, t, h, w, device): x = torch.arange(w, device=device) y = torch.arange(h, device=device) z = torch.arange(t, device=device) - self.cache_positions[t,h,w] = torch.cartesian_prod(z, y, x) # (t, h, w, 3) - pos = self.cache_positions[t,h,w].view(1, t*h*w, 3).expand(b, -1, 3).clone() + pos = torch.cartesian_prod(z, y, x) + pos = pos.reshape(t * h * w, 3).transpose(0, 1).reshape(3, 1, -1).contiguous().expand(3, b, -1).clone() + poses = (pos[0].contiguous(), pos[1].contiguous(), pos[2].contiguous()) + max_poses = (int(poses[0].max()), int(poses[1].max()), int(poses[2].max())) + + self.cache_positions[t, h, w] = (poses, max_poses) + pos = self.cache_positions[t, h, w] + return pos @@ -45,8 +57,15 @@ def rotate_half(x): def apply_rope1d(self, tokens, pos1d, cos, sin): assert pos1d.ndim == 2 - cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] - sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] + if npu_config is None: + # for (batch_size x nheads x ntokens x dim) + cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] + sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] + else: + # for (batch_size x ntokens x nheads x dim) + cos = torch.nn.functional.embedding(pos1d, cos)[:, :, None, :] + sin = torch.nn.functional.embedding(pos1d, sin)[:, :, None, :] + return (tokens * cos) + (self.rotate_half(tokens) * sin) def forward(self, tokens, positions): @@ -55,18 +74,19 @@ def forward(self, tokens, positions): * tokens: batch_size x nheads x ntokens x dim * positions: batch_size x ntokens x 3 (t, y and x position of each token) output: - * tokens after appplying RoPE3D (batch_size x nheads x ntokens x dim) + * tokens after appplying RoPE3D (batch_size x nheads x ntokens x x dim) """ assert tokens.size(3) % 3 == 0, "number of dimensions should be a multiple of three" D = tokens.size(3) // 3 - assert positions.ndim == 3 and positions.shape[-1] == 3 # Batch, Seq, 3 - cos_t, sin_t = self.get_cos_sin(D, int(positions[:, :, 0].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_t) - cos_y, sin_y = self.get_cos_sin(D, int(positions[:, :, 1].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_h) - cos_x, sin_x = self.get_cos_sin(D, int(positions[:, :, 2].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_w) + poses, max_poses = positions + assert len(poses) == 3 and poses[0].ndim == 2# Batch, Seq, 3 + cos_t, sin_t = self.get_cos_sin(D, max_poses[0] + 1, tokens.device, tokens.dtype, self.interpolation_scale_t) + cos_y, sin_y = self.get_cos_sin(D, max_poses[1] + 1, tokens.device, tokens.dtype, self.interpolation_scale_h) + cos_x, sin_x = self.get_cos_sin(D, max_poses[2] + 1, tokens.device, tokens.dtype, self.interpolation_scale_w) # split features into three along the feature dimension, and apply rope1d on each half t, y, x = tokens.chunk(3, dim=-1) - t = self.apply_rope1d(t, positions[:, :, 0], cos_t, sin_t) - y = self.apply_rope1d(y, positions[:, :, 1], cos_y, sin_y) - x = self.apply_rope1d(x, positions[:, :, 2], cos_x, sin_x) + t = self.apply_rope1d(t, poses[0], cos_t, sin_t) + y = self.apply_rope1d(y, poses[1], cos_y, sin_y) + x = self.apply_rope1d(x, poses[2], cos_x, sin_x) tokens = torch.cat((t, y, x), dim=-1) return tokens diff --git a/scripts/text_condition/npu/sample_video.sh b/scripts/text_condition/npu/sample_video.sh index ad4d95862..81a1ae823 100644 --- a/scripts/text_condition/npu/sample_video.sh +++ b/scripts/text_condition/npu/sample_video.sh @@ -10,8 +10,8 @@ fi torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ --num_frames ${NUM_FRAME} \ - --height 480 \ - --width 640 \ + --height 240 \ + --width 320 \ --cache_dir "./cache_dir" \ --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ diff --git a/scripts/text_condition/npu/train_image3d_240p.sh b/scripts/text_condition/npu/train_image3d_240p.sh index 8edad845f..280b1977e 100644 --- a/scripts/text_condition/npu/train_image3d_240p.sh +++ b/scripts/text_condition/npu/train_image3d_240p.sh @@ -20,8 +20,8 @@ accelerate launch \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ --num_frames 1 \ - --max_height 256 \ - --max_width 256 \ + --max_height 240 \ + --max_width 320 \ --interpolation_scale_t 1.0 \ --interpolation_scale_h 0.5 \ --interpolation_scale_w 0.5 \ @@ -31,7 +31,7 @@ accelerate launch \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=4e-5 \ + --learning_rate=1e-4 \ --lr_scheduler="constant" \ --seed=10 \ --lr_warmup_steps=0 \ diff --git a/scripts/text_condition/npu/train_image3d_240p_rope.sh b/scripts/text_condition/npu/train_image3d_240p_rope.sh new file mode 100644 index 000000000..1c2b5e4c5 --- /dev/null +++ b/scripts/text_condition/npu/train_image3d_240p_rope.sh @@ -0,0 +1,51 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE + + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=2000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --use_rope \ + --noise_offset 0.02 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image3d_256p.sh b/scripts/text_condition/npu/train_image3d_256p.sh new file mode 100644 index 000000000..61b6d860f --- /dev/null +++ b/scripts/text_condition/npu/train_image3d_256p.sh @@ -0,0 +1,50 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE + + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=2000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image3d_256p_rope.sh b/scripts/text_condition/npu/train_image3d_256p_rope.sh new file mode 100644 index 000000000..942af012b --- /dev/null +++ b/scripts/text_condition/npu/train_image3d_256p_rope.sh @@ -0,0 +1,51 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE + + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 256 \ + --max_width 256 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=2000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --use_rope \ + --noise_offset 0.02 \ + --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image3d_480p.sh b/scripts/text_condition/npu/train_image3d_480p.sh index e0b905d02..2dc3bc3ad 100644 --- a/scripts/text_condition/npu/train_image3d_480p.sh +++ b/scripts/text_condition/npu/train_image3d_480p.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ @@ -27,17 +27,17 @@ accelerate launch \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=8 \ + --train_batch_size=16 \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=1e-4 \ - --lr_scheduler="cosine" \ + --lr_scheduler="constant" \ --seed=10 \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=100 \ + --checkpointing_steps=2000 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ @@ -47,5 +47,4 @@ accelerate launch \ --ema_start_step 0 \ --cfg 0.1 \ --noise_offset 0.02 \ - --downsampler "k33_s22" \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image3d_480p_rope.sh b/scripts/text_condition/npu/train_image3d_480p_rope.sh new file mode 100644 index 000000000..ba443d314 --- /dev/null +++ b/scripts/text_condition/npu/train_image3d_480p_rope.sh @@ -0,0 +1,53 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 20 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=2000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --tile_overlap_factor 0.125 \ + --use_rope \ + --enable_tiling \ + --noise_offset 0.02 \ + --resume_from_checkpoint="latest" From 149d52143dbd0a9cecec094c94e725691a03f928 Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Fri, 21 Jun 2024 10:59:14 +0800 Subject: [PATCH 057/134] use SingletonMeta for saving caption_list --- opensora/dataset/t2v_datasets.py | 117 ++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 3f672490e..ea3f840b6 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -47,6 +47,59 @@ def random_video_noise(t, c, h, w): return vid +class SingletonMeta(type): + """ + 这是一个元类,用于创建单例类。 + """ + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + instance = super().__call__(*args, **kwargs) + cls._instances[cls] = instance + return cls._instances[cls] + + +class DataSetProg(metaclass=SingletonMeta): + def __init__(self): + self.vid_cap_list = [] + self.img_cap_list = [] + self.elements = [] + self.num_workers = 1 + self.n_elements = 0 + self.worker_elements = dict() + self.n_used_elements = dict() + + def set_cap_list(self, num_workers, img_cap_list, vid_cap_list, n_elements): + self.num_workers = num_workers + self.img_cap_list = img_cap_list + self.vid_cap_list = vid_cap_list + self.n_elements = n_elements + self.elements = list(range(n_elements)) + random.shuffle(self.elements) + print(f"n_elements: {len(self.elements)}", flush=True) + + for i in range(self.num_workers): + self.n_used_elements[i] = 0 + per_worker = int(math.ceil(len(self.elements) / float(self.num_workers))) + start = i * per_worker + end = min(start + per_worker, len(self.elements)) + self.worker_elements[i] = self.elements[start: end] + + def get_item(self, work_info): + if work_info is None: + worker_id = 0 + else: + worker_id = work_info.id + + idx = self.worker_elements[worker_id][self.n_used_elements[worker_id] % len(self.worker_elements[worker_id])] + self.n_used_elements[worker_id] += 1 + return idx + + +dataset_prog = DataSetProg() + + class T2V_dataset(Dataset): def __init__(self, args, transform, temporal_sample, tokenizer): self.image_data = args.image_data @@ -66,51 +119,35 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.support_Chinese = False if self.num_frames != 1: - self.vid_cap_list = self.get_vid_cap_list() + vid_cap_list = self.get_vid_cap_list() if self.use_image_num != 0 and not self.use_img_from_vid: - self.img_cap_list = self.get_img_cap_list() + img_cap_list = self.get_img_cap_list() else: - self.img_cap_list = [] + img_cap_list = [] else: - self.img_cap_list = self.get_img_cap_list() - self.vid_cap_list = [] + img_cap_list = self.get_img_cap_list() + vid_cap_list = [] - if npu_config is not None: - self.n_used_elements = 0 - self.elements = list(range(self.__len__())) - self.worker_elements = None - - random.shuffle(self.elements) - print(f"n_elements: {len(self.elements)}") + if self.num_frames != 1: + n_elements = len(vid_cap_list) + else: + n_elements = len(img_cap_list) + dataset_prog.set_cap_list(args.dataloader_num_workers, img_cap_list, vid_cap_list, n_elements) - print(f"video length: {len(self.vid_cap_list)}") - print(f"image length: {len(self.img_cap_list)}") + print(f"video length: {len(dataset_prog.vid_cap_list)}", flush=True) + print(f"image length: {len(dataset_prog.img_cap_list)}", flush=True) def set_checkpoint(self, n_used_elements): - self.n_used_elements = n_used_elements + for i in range(len(dataset_prog.n_used_elements)): + dataset_prog.n_used_elements[i] = n_used_elements def __len__(self): - if self.num_frames != 1: - return len(self.vid_cap_list) - else: - return len(self.img_cap_list) + return dataset_prog.n_elements def __getitem__(self, idx): if npu_config is not None: worker_info = get_worker_info() - if worker_info is None: # single-process data loading, return a regular index - idx = self.elements[self.n_used_elements % len(self.elements)] - self.n_used_elements += 1 - else: # in a worker process - # split workload - if self.worker_elements is None: - per_worker = int(math.ceil(len(self.elements) / float(worker_info.num_workers))) - worker_id = worker_info.id - start = worker_id * per_worker - end = min(start + per_worker, len(self.elements)) - self.worker_elements = self.elements[start:end] - idx = self.worker_elements[self.n_used_elements % len(self.worker_elements)] - self.n_used_elements += 1 + idx = dataset_prog.get_item(worker_info) try: video_data, image_data = {}, {} if self.num_frames != 1: @@ -126,8 +163,8 @@ def __getitem__(self, idx): except Exception as e: # print(f'Error with {e}') # 打印异常堆栈 - if idx in self.vid_cap_list: - print(f"Caught an exception! {self.vid_cap_list[idx]}") + if idx in dataset_prog.vid_cap_list: + print(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") # traceback.print_exc() # traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) @@ -139,9 +176,9 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = self.vid_cap_list[idx]['path'] + video_path = dataset_prog.vid_cap_list[idx]['path'] assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" - frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None + frame_idx = dataset_prog.vid_cap_list[idx]['frame_idx'] if hasattr(dataset_prog.vid_cap_list[idx], 'frame_idx') else None video = self.decord_read(video_path, frame_idx) h, w = video.shape[-2:] @@ -153,7 +190,7 @@ def get_video(self, idx): # video = torch.rand(221, 3, 480, 640) video = video.transpose(0, 1) # T C H W -> C T H W - text = self.vid_cap_list[idx]['cap'] + text = dataset_prog.vid_cap_list[idx]['cap'] text = text_preprocessing(text, support_Chinese=self.support_Chinese) if random.random() > self.cfg else "" text_tokens_and_mask = self.tokenizer( @@ -178,8 +215,8 @@ def get_image_from_video(self, video_data): return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) def get_image(self, idx): - idx = idx % len(self.img_cap_list) # out of range - image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] + idx = idx % len(dataset_prog.img_cap_list) # out of range + image_data = dataset_prog.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] @@ -256,7 +293,7 @@ def get_img_cap_list(self): img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] else: - img_cap_lists = npu_config.try_load_pickle("img_cap_lists", + img_cap_lists = npu_config.try_load_pickle("img_cap_lists5", lambda: self.read_jsons(self.image_data, postfix=".jpg")) img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] From dc3ec4824dfa5d7103951968f9b1bade8ecc4be5 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Fri, 21 Jun 2024 15:37:52 +0800 Subject: [PATCH 058/134] video ip --- opensora/models/diffusion/latte/modules.py | 208 +++++++++++++++++ .../diffusion/latte/transformer_qformer.py | 221 ++++++++++++++++++ .../deepspeed_zero2_config.yaml | 4 +- .../gpu/train_inpaint_video21d_65x512x512.sh | 2 +- 4 files changed, 432 insertions(+), 3 deletions(-) create mode 100644 opensora/models/diffusion/latte/transformer_qformer.py diff --git a/opensora/models/diffusion/latte/modules.py b/opensora/models/diffusion/latte/modules.py index b0de16ed8..94cab822c 100644 --- a/opensora/models/diffusion/latte/modules.py +++ b/opensora/models/diffusion/latte/modules.py @@ -35,6 +35,11 @@ xformers = None +def zero_module(module): + for p in module.parameters(): + nn.init.zeros_(p) + return module + def get_2d_sincos_pos_embed( embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, ): @@ -1099,6 +1104,209 @@ def __call__( return hidden_states +# IP Adapter +class VideoIPAttnProcessor: + r""" + Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). + """ + + def __init__( + self, + dim=1152, + attention_mode='xformers', + use_rope=False, + rope_scaling=None, + compress_kv_factor=None, + + num_latent_tokens=128, + use_image_num=0, + dropout=0.0, + ): + self.dim = dim + self.attention_mode = attention_mode + self.use_rope = use_rope + self.rope_scaling = rope_scaling + self.compress_kv_factor = compress_kv_factor + + self.use_image_num = use_image_num + + if self.use_rope: + self._init_rope() + + if not hasattr(F, "scaled_dot_product_attention"): + raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + self.to_k_vip = nn.Linear(dim, dim, bias=False) + self.to_v_vip = nn.Linear(dim, dim, bias=False) + + self.to_out_vip = nn.ModuleList( + [ + zero_module(nn.Linear(dim, dim, bias=False)), + nn.Dropout(dropout), + ] + ) + + + def _init_rope(self): + if self.rope_scaling is None: + self.rope2d = RoPE2D() + self.rope1d = RoPE1D() + else: + scaling_type = self.rope_scaling["type"] + scaling_factor_2d = self.rope_scaling["factor_2d"] + scaling_factor_1d = self.rope_scaling["factor_1d"] + if scaling_type == "linear": + self.rope2d = LinearScalingRoPE2D(scaling_factor=scaling_factor_2d) + self.rope1d = LinearScalingRoPE1D(scaling_factor=scaling_factor_1d) + else: + raise ValueError(f"Unknown RoPE scaling type {scaling_type}") + + def __call__( + self, + attn: Attention, + hidden_states: torch.FloatTensor, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + temb: Optional[torch.FloatTensor] = None, + scale: float = 1.0, + position_q: Optional[torch.LongTensor] = None, + position_k: Optional[torch.LongTensor] = None, + last_shape: Tuple[int] = None, + ) -> torch.FloatTensor: + + residual = hidden_states + + args = () if USE_PEFT_BACKEND else (scale,) + + if attn.spatial_norm is not None: + hidden_states = attn.spatial_norm(hidden_states, temb) + + input_ndim = hidden_states.ndim + + if input_ndim == 4: + batch_size, channel, height, width = hidden_states.shape + hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) + + + if self.compress_kv_factor is not None: + batch_size = hidden_states.shape[0] + if len(last_shape) == 2: + encoder_hidden_states = hidden_states.permute(0, 2, 1).reshape(batch_size, self.dim, *last_shape) + encoder_hidden_states = attn.sr(encoder_hidden_states).reshape(batch_size, self.dim, -1).permute(0, 2, 1) + elif len(last_shape) == 1: + encoder_hidden_states = hidden_states.permute(0, 2, 1) + if last_shape[0] % 2 == 1: + first_frame_pad = encoder_hidden_states[:, :, :1].repeat((1, 1, attn.kernel_size - 1)) + encoder_hidden_states = torch.concatenate((first_frame_pad, encoder_hidden_states), dim=2) + encoder_hidden_states = attn.sr(encoder_hidden_states).permute(0, 2, 1) + else: + raise NotImplementedError(f'NotImplementedError with last_shape {last_shape}') + + encoder_hidden_states = attn.norm(encoder_hidden_states) + + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + + # import ipdb;ipdb.set_trace() + if attention_mask is not None: + attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + if npu_config is not None and npu_config.on_npu: + attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) + else: + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + + if attn.group_norm is not None: + hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) + + args = () if USE_PEFT_BACKEND else (scale,) + query = attn.to_q(hidden_states, *args) + + if encoder_hidden_states is None: + encoder_hidden_states = hidden_states + elif attn.norm_cross: + encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + + + + key = attn.to_k(encoder_hidden_states, *args) + value = attn.to_v(encoder_hidden_states, *args) + + inner_dim = key.shape[-1] + head_dim = inner_dim // attn.heads + if npu_config is not None and npu_config.on_npu: + if npu_config.enable_FA and query.dtype == torch.float32: + dtype = torch.bfloat16 + else: + dtype = None + + with set_run_dtype(query, dtype): + query, key, value = npu_config.set_current_run_dtype([query, key, value]) + hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", + head_dim, attn.heads) + + hidden_states = npu_config.restore_dtype(hidden_states) + else: + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + if position_q.ndim == 3: + query = self.rope2d(query, position_q) + elif position_q.ndim == 2: + query = self.rope1d(query, position_q) + else: + raise NotImplementedError + if position_k.ndim == 3: + key = self.rope2d(key, position_k) + elif position_k.ndim == 2: + key = self.rope1d(key, position_k) + else: + raise NotImplementedError + + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + if self.attention_mode == 'flash': + assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + + # linear proj + hidden_states = attn.to_out[0](hidden_states, *args) + # dropout + hidden_states = attn.to_out[1](hidden_states) + + if input_ndim == 4: + hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) + + if attn.residual_connection: + hidden_states = hidden_states + residual + + hidden_states = hidden_states / attn.rescale_output_factor + + return hidden_states + + @maybe_allow_in_graph class GatedSelfAttentionDense(nn.Module): r""" diff --git a/opensora/models/diffusion/latte/transformer_qformer.py b/opensora/models/diffusion/latte/transformer_qformer.py new file mode 100644 index 000000000..185f7b3ac --- /dev/null +++ b/opensora/models/diffusion/latte/transformer_qformer.py @@ -0,0 +1,221 @@ + +import torch +import torch.nn as nn + +from diffusers.models.attention import FeedForward +from diffusers.models.attention_processor import Attention + +class BasicQFormerBlock(nn.Module): + def __init__( + self, + dim, + num_attention_heads=16, + attention_head_dim=64, + dropout=0.0, + attention_bias=False, + upcast_attention=False, + attention_out_bias=True, + norm_elementwise_affine=True, + norm_eps=1e-5, + activation_fn="gelu", + final_dropout=False, + ff_inner_dim=None, + ff_bias=False, + only_visual_attention=True, + ): + super().__init__() + + # self attn + self.norm1_latents = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + self.norm1_visual_context = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + + self.attn1 = Attention( + query_dim=dim, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + cross_attention_dim=dim, + upcast_attention=upcast_attention, + out_bias=attention_out_bias + ) + self.only_visual_attention = only_visual_attention + if not only_visual_attention: + # cross attn if cross_attention_dim is not None, otherwise self attn + self.norm2_latents = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + self.norm2_encoder_hidden_states = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + + self.attn2 = Attention( + query_dim=dim, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + cross_attention_dim=dim, + upcast_attention=upcast_attention, + out_bias=attention_out_bias + ) + + self.norm3 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + self.ff = FeedForward( + dim, + dropout=dropout, + activation_fn=activation_fn, + final_dropout=final_dropout, + inner_dim=ff_inner_dim, + bias=ff_bias, + ) + + def forward(self, latents, visual_context, encoder_hidden_states=None, attention_mask=None): + ''' + params: + latents: queries of q former + shape (b, n1, c) + visual_context: visual features + shape (b, n2, c) + encoder_hidden_states: text features or others + shape (b, n3, c) + + return: + latents: updated queries + ''' + # self attn + norm_latents = self.norm1_latents(latents) + norm_visual_context = self.norm1_visual_context(visual_context) + kv_input = torch.cat([norm_visual_context, norm_latents], dim=-2) + + attn_output = self.attn1( + norm_latents, + encoder_hidden_states=kv_input, + attention_mask=attention_mask, + ) + + latents = latents + attn_output + + if not self.only_visual_attention: + # cross attn + norm_latents = self.norm2_latents(latents) + if encoder_hidden_states is not None: + norm_encoder_hidden_states = self.norm2_encoder_hidden_states(encoder_hidden_states) + else: + norm_encoder_hidden_states = self.norm2_encoder_hidden_states(visual_context) + + kv_input = torch.cat([norm_encoder_hidden_states, norm_latents], dim=-2) + attn_output = self.attn2( + norm_latents, + encoder_hidden_states=kv_input, + attention_mask=attention_mask + ) + + latents = latents + attn_output + + # feed forward + norm_latents = self.norm3(latents) + ff_output = self.ff(norm_latents) + + latents = latents + ff_output + + return latents + + + +class QFormer(nn.Module): + def __init__( + self, + dim=1024, + num_queries=1, + visual_context_dim=768, + out_dim=1024, + block_num=6, + num_attention_heads=16, + attention_head_dim=64, + dropout=0.0, + max_seq_length=257, + apply_pos_emb=False, + only_visual_attention=True, + encoder_hidden_states_dim=None, + ): + super().__init__() + + self.apply_pos_emb = apply_pos_emb + self.pos_emb_visual_context = nn.Embedding(max_seq_length, visual_context_dim) if apply_pos_emb else None + if not only_visual_attention: + self.pos_emb_encoder_hidden_states = nn.Embedding(max_seq_length, encoder_hidden_states_dim) if apply_pos_emb else None + + self.only_visual_attention = only_visual_attention + + self.latents = nn.Parameter(torch.randn(1, num_queries, dim) / dim ** 0.5) + + self.proj_in_visual_context = nn.Linear(visual_context_dim, dim) + if not only_visual_attention: + self.proj_in_encoder_hidden_states = nn.Linear(encoder_hidden_states_dim, dim) + + self.proj_out = nn.Linear(dim, out_dim) + + self.layers = nn.ModuleList([]) + + for _ in range(block_num): + self.layers.append( + BasicQFormerBlock( + dim, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, + dropout=dropout, + only_visual_attention=only_visual_attention, + ) + ) + + self.norm_out = nn.LayerNorm(out_dim) + + def forward(self, visual_context, encoder_hidden_states=None): + + def add_pos_emb(x): + n, device = x.shape[1], x.device + pos_emb = self.pos_emb(torch.arange(n, device=device)) + x = x + pos_emb + return x + + if self.apply_pos_emb: + visual_context = add_pos_emb(visual_context) + if not self.only_visual_attention: + encoder_hidden_states = add_pos_emb(encoder_hidden_states) + + b = visual_context.shape[0] + latents = self.latents.repeat(b, 1, 1) + + visual_context = self.proj_in_visual_context(visual_context) + if not self.only_visual_attention: + encoder_hidden_states = self.proj_in_encoder_hidden_states(encoder_hidden_states) + + for layer in self.layers: + latents = layer(latents, visual_context, encoder_hidden_states) + + latents = self.proj_out(latents) + latents = self.norm_out(latents) + + return latents + +if __name__ == "__main__": + qformer = QFormer( + dim=1024, + num_queries=256, + visual_context_dim=768, + encoder_hidden_states_dim=768, + out_dim=1024, + block_num=8, + num_attention_heads=64, + attention_head_dim=16, + dropout=0.0, + max_seq_length=257, + apply_pos_emb=False, + ) + + print(qformer) + + visual_context = torch.randn(2, 4096, 768) + encoder_hidden_states = torch.randn(2, 4096, 768) + + output = qformer(visual_context, encoder_hidden_states) + + num_params = sum(p.numel() for p in qformer.parameters() if p.requires_grad) + print("Number of trainable parameters: ", num_params / 1e6, "M") \ No newline at end of file diff --git a/scripts/accelerate_configs/deepspeed_zero2_config.yaml b/scripts/accelerate_configs/deepspeed_zero2_config.yaml index b15390113..eedb2162b 100644 --- a/scripts/accelerate_configs/deepspeed_zero2_config.yaml +++ b/scripts/accelerate_configs/deepspeed_zero2_config.yaml @@ -8,6 +8,6 @@ main_process_ip: null main_process_port: 29503 main_training_function: main num_machines: 1 -num_processes: 8 -gpu_ids: 0,1,2,3,4,5,6,7 +num_processes: 4 +gpu_ids: 4,5,6,7 use_cpu: false diff --git a/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh index 2070bab2a..348c9db16 100644 --- a/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh +++ b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh @@ -31,7 +31,7 @@ accelerate launch \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=100000 \ - --learning_rate=1e-5 \ + --learning_rate=2e-5 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ From 06412d47dfb3ed4ee6d36e9087678d849dabf4a6 Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Fri, 21 Jun 2024 20:43:04 +0800 Subject: [PATCH 059/134] support opensora3d sp --- opensora/acceleration/__init__.py | 0 opensora/acceleration/communications.py | 133 ++++++++++++++++++ opensora/acceleration/parallel_states.py | 57 ++++++++ .../diffusion/opensora/modeling_opensora.py | 20 ++- opensora/models/diffusion/opensora/modules.py | 123 +++++++++++----- opensora/models/diffusion/opensora/rope.py | 6 +- .../models/diffusion/udit_ultra/modules.py | 3 +- opensora/npu_config.py | 10 +- opensora/train/train_t2v_diffusers.py | 39 ++++- .../npu/train_video3d_sp_nx240p.sh | 53 +++++++ 10 files changed, 386 insertions(+), 58 deletions(-) create mode 100644 opensora/acceleration/__init__.py create mode 100644 opensora/acceleration/communications.py create mode 100644 opensora/acceleration/parallel_states.py create mode 100644 scripts/text_condition/npu/train_video3d_sp_nx240p.sh diff --git a/opensora/acceleration/__init__.py b/opensora/acceleration/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/opensora/acceleration/communications.py b/opensora/acceleration/communications.py new file mode 100644 index 000000000..6f69a73a0 --- /dev/null +++ b/opensora/acceleration/communications.py @@ -0,0 +1,133 @@ +import torch +import torch.distributed as dist +from einops import rearrange +from opensora.acceleration.parallel_states import hccl_info, lccl_info, enable_LCCL +try: + from lcalib.functional import lcal_all2allvc +except: + lcal_all2allvc = None + +def broadcast(input_: torch.Tensor): + sp_size = hccl_info.world_size + src = hccl_info.rank // sp_size * sp_size + dist.broadcast(input_, src=src, group=hccl_info.group) + +_COUNT = 0 +def _all_to_all( + input_: torch.Tensor, + scatter_dim: int, + gather_dim: int, +): + group = hccl_info.group + sp_size = hccl_info.world_size + input_list = [t.contiguous() for t in torch.tensor_split(input_, sp_size, scatter_dim)] + output_list = [torch.empty_like(input_list[0]) for _ in range(sp_size)] + dist.all_to_all(output_list, input_list, group=group) + return torch.cat(output_list, dim=gather_dim).contiguous() + +def _single_all_to_all( + input_: torch.Tensor, + scatter_dim: int, + gather_dim: int, + enable_HCCL=False, +): + if enable_LCCL: + sp_size = lccl_info.world_size + else: + sp_size = hccl_info.world_size + inp_shape = list(input_.shape) + inp_shape[scatter_dim] = inp_shape[scatter_dim] // sp_size + if scatter_dim < 1: + input_t = input_.reshape( + [sp_size, inp_shape[scatter_dim]] + \ + inp_shape[scatter_dim + 1:] + ) + else: + # transpose groups of heads with the seq-len parallel dimension, so that we can scatter them! + input_t = input_.reshape( + [-1, sp_size, inp_shape[scatter_dim]] + \ + inp_shape[scatter_dim + 1:] + ).transpose(0, 1).contiguous() + + output = torch.empty_like(input_t) + if enable_LCCL and not enable_HCCL: + matrix_count = torch.ones([sp_size, sp_size], dtype=torch.int64, device=input_t.device) * ( + input_t.numel() // sp_size) + lcal_all2allvc(input_t, output, matrix_count, lccl_info.group) + else: + dist.all_to_all_single(output, input_t, group=hccl_info.group) + # if scattering the seq-dim, transpose the heads back to the original dimension + if scatter_dim < 1: + output = output.transpose(0, 1).contiguous() + + return output.reshape( + inp_shape[: gather_dim] + [inp_shape[gather_dim] * sp_size, ] + inp_shape[gather_dim + 1:]) + + +class _AllToAll(torch.autograd.Function): + """All-to-all communication. + + Args: + input_: input matrix + process_group: communication group + scatter_dim: scatter dimension + gather_dim: gather dimension + """ + + @staticmethod + def forward(ctx, input_, scatter_dim, gather_dim, all_to_all_func): + ctx.scatter_dim = scatter_dim + ctx.gather_dim = gather_dim + ctx.all_to_all = all_to_all_func + output = ctx.all_to_all(input_, scatter_dim, gather_dim) + return output + + @staticmethod + def backward(ctx, grad_output): + grad_output = ctx.all_to_all( + grad_output, + ctx.gather_dim, + ctx.scatter_dim, + ) + return ( + grad_output, + None, + None, + None, + ) + +def all_to_all_SBH( + input_: torch.Tensor, + scatter_dim: int = 1, + gather_dim: int = 0, +): + return _AllToAll.apply(input_, scatter_dim, gather_dim, _single_all_to_all) + +def all_to_all_BSND( + input_: torch.Tensor, + scatter_dim: int = 2, + gather_dim: int = 1, +): + return _AllToAll.apply(input_, scatter_dim, gather_dim, _all_to_all) + + +def prepare_parallel_data(hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask, use_image_num): + def all_to_all(hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask): + hidden_states = _single_all_to_all(hidden_states, scatter_dim=2, gather_dim=0, enable_HCCL=True) + encoder_hidden_states = _single_all_to_all(encoder_hidden_states, scatter_dim=1, gather_dim=0, enable_HCCL=True) + attention_mask = _single_all_to_all(attention_mask, scatter_dim=1, gather_dim=0, enable_HCCL=True) + encoder_attention_mask = _single_all_to_all(encoder_attention_mask, scatter_dim=1, gather_dim=0, enable_HCCL=True) + return hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask + + sp_size = hccl_info.world_size + frame = hidden_states.shape[2] + assert frame % sp_size == 0, "frame should be a multiple of sp_size" + + encoder_hidden_states = rearrange(encoder_hidden_states, 'b 1 (n x) h -> b n x h', + n=sp_size, x=encoder_hidden_states.shape[2]//sp_size).contiguous() + hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask = all_to_all(hidden_states, + encoder_hidden_states, + attention_mask.repeat(1, sp_size, 1, 1), + encoder_attention_mask.repeat(1, sp_size, 1)) + + return hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask, use_image_num \ No newline at end of file diff --git a/opensora/acceleration/parallel_states.py b/opensora/acceleration/parallel_states.py new file mode 100644 index 000000000..e3f96e121 --- /dev/null +++ b/opensora/acceleration/parallel_states.py @@ -0,0 +1,57 @@ +import torch +import torch_npu +import torch.distributed as dist +import os +try: + from lcalib.functional import lcal_initialize + enable_LCCL = True +except: + lcal_initialize = None + enable_LCCL = False +class COMM_INFO: + def __init__(self): + self.group = None + self.world_size = 0 + self.rank = -1 + +lccl_info = COMM_INFO() +hccl_info = COMM_INFO() +_SEQUENCE_PARALLEL_STATE = False +def initialize_sequence_parallel_state(sequence_parallel_size): + global _SEQUENCE_PARALLEL_STATE + if sequence_parallel_size > 1: + _SEQUENCE_PARALLEL_STATE = True + initialize_sequence_parallel_group(sequence_parallel_size) + +def set_sequence_parallel_state(state): + global _SEQUENCE_PARALLEL_STATE + _SEQUENCE_PARALLEL_STATE = state + +def get_sequence_parallel_state(): + return _SEQUENCE_PARALLEL_STATE + +def initialize_sequence_parallel_group(sequence_parallel_size): + """Initialize the sequence parallel group.""" + rank = int(os.getenv('RANK', '0')) + world_size = int(os.getenv("WORLD_SIZE", '1')) + assert world_size % sequence_parallel_size == 0, "world_size must be divisible by sequence_parallel_size" + # hccl + hccl_info.world_size = sequence_parallel_size + hccl_info.rank = rank + num_sequence_parallel_groups: int = world_size // sequence_parallel_size + for i in range(num_sequence_parallel_groups): + ranks = range(i * sequence_parallel_size, (i + 1) * sequence_parallel_size) + group = dist.new_group(ranks) + if rank in ranks: + hccl_info.group = group + + if enable_LCCL: + assert sequence_parallel_size == 8, "sequence_parallel_size should be 8 when enable_LCCL is True" + rank %= sequence_parallel_size + lccl_info.world_size = sequence_parallel_size + lccl_info.group = lcal_initialize(rank, sequence_parallel_size) + lccl_info.rank = rank + +def destroy_sequence_parallel_group(): + """Destroy the sequence parallel group.""" + dist.destroy_process_group() diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index bb87edd32..ad397e5c9 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -16,6 +16,7 @@ try: import torch_npu from opensora.npu_config import npu_config + from opensora.acceleration.parallel_states import get_sequence_parallel_state, hccl_info except: torch_npu = None npu_config = None @@ -347,8 +348,12 @@ def forward( # b, frame+use_image_num, h, w -> a video with images # b, 1, h, w -> only images attention_mask = attention_mask.to(self.dtype) - attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w - attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w + if npu_config is not None and get_sequence_parallel_state(): + attention_mask_vid = attention_mask[:, :frame * hccl_info.world_size] # b, frame, h, w + attention_mask_img = attention_mask[:, frame * hccl_info.world_size:] # b, use_image_num, h, w + else: + attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w + attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w if attention_mask_vid.numel() > 0: attention_mask_vid_first_frame = attention_mask_vid[:, :1].repeat(1, self.patch_size_t-1, 1, 1) @@ -404,6 +409,13 @@ def forward( hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num ) # 2. Blocks + # import ipdb;ipdb.set_trace() + if npu_config is not None and get_sequence_parallel_state(): + if hidden_states_vid is not None: + hidden_states_vid = rearrange(hidden_states_vid, 'b s h -> s b h', b=batch_size).contiguous() + encoder_hidden_states_vid = rearrange(encoder_hidden_states_vid, 'b s h -> s b h', + b=batch_size).contiguous() + timestep_vid = timestep_vid.view(batch_size, 6, -1).transpose(0, 1).contiguous() for block in self.transformer_blocks: if self.training and self.gradient_checkpointing: @@ -477,6 +489,10 @@ def custom_forward(*inputs): width=width, ) + if npu_config is not None and get_sequence_parallel_state(): + if hidden_states_vid is not None: + hidden_states_vid = rearrange(hidden_states_vid, 's b h -> b s h', b=batch_size).contiguous() + # 3. Output output_vid, output_img = None, None if hidden_states_vid is not None: diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 990cdd716..a16e9ddb4 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -22,6 +22,8 @@ try: import torch_npu from opensora.npu_config import npu_config, set_run_dtype + from opensora.acceleration.parallel_states import get_sequence_parallel_state, hccl_info + from opensora.acceleration.communications import all_to_all_SBH except: torch_npu = None npu_config = None @@ -222,12 +224,25 @@ def forward(self, latent, num_frames): if self.num_frames != num_frames: # import ipdb;ipdb.set_trace() # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) + if npu_config is not None and get_sequence_parallel_state(): + sp_size = hccl_info.world_size + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames * sp_size, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + rank = hccl_info.rank % sp_size + st_frame = rank * num_frames + ed_frame = st_frame + num_frames + temp_pos_embed = temp_pos_embed[st_frame: ed_frame] + else: + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) temp_pos_embed = torch.from_numpy(temp_pos_embed) temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) else: @@ -565,11 +580,7 @@ def __init__(self, *args, **kwargs): def forward(self, x, attention_mask, t, h, w): b = x.shape[0] x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) - if npu_config is None: - x = self.layer(x) + (x if self.down_shortcut else 0) - else: - x_dtype = x.dtype - x = npu_config.run_conv2d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + x = self.layer(x) + (x if self.down_shortcut else 0) self.t = 1 self.h = h//self.down_factor[0] @@ -643,9 +654,14 @@ def __call__( batch_size, channel, height, width = hidden_states.shape hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) - batch_size, sequence_length, _ = ( - hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape - ) + if npu_config is not None and get_sequence_parallel_state(): + sequence_length, batch_size, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + else: + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) if attention_mask is not None: if npu_config is None: @@ -672,29 +688,56 @@ def __call__( head_dim = inner_dim // attn.heads if npu_config is not None and npu_config.on_npu: - if npu_config.enable_FA and query.dtype == torch.float32: - dtype = torch.bfloat16 - else: - dtype = None - - if self.use_rope: - query = query.view(batch_size, -1, attn.heads, head_dim) - key = key.view(batch_size, -1, attn.heads, head_dim) - # require the shape of (batch_size x nheads x ntokens x dim) - pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) - query = self.rope(query, pos_thw) - if query.shape == key.shape: + if get_sequence_parallel_state(): + query = query.view(-1, attn.heads, head_dim) # [s // sp, b, h * d] -> [s // sp * b, h, d] + key = key.view(-1, attn.heads, head_dim) + value = value.view(-1, attn.heads, head_dim) + h_size = attn.heads * head_dim + sp_size = hccl_info.world_size + h_size_sp = h_size // sp_size + # apply all_to_all to gather sequence and split attention heads [s // sp * b, h, d] -> [s * b, h // sp, d] + query = all_to_all_SBH(query, scatter_dim=1, gather_dim=0).view(-1, batch_size, h_size_sp) + key = all_to_all_SBH(key, scatter_dim=1, gather_dim=0).view(-1, batch_size, h_size_sp) + value = all_to_all_SBH(value, scatter_dim=1, gather_dim=0).view(-1, batch_size, h_size_sp) + if self.use_rope: + query = query.view(-1, batch_size, attn.heads // sp_size, head_dim) + key = key.view(-1, batch_size, attn.heads // sp_size, head_dim) + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame * sp_size, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) key = self.rope(key, pos_thw) + query = query.view(-1, batch_size, h_size_sp) + key = key.view(-1, batch_size, h_size_sp) + value = value.view(-1, batch_size, h_size_sp) + hidden_states = npu_config.run_attention(query, key, value, attention_mask, "SBH", + head_dim, attn.heads // sp_size) + + hidden_states = hidden_states.view(-1, attn.heads // sp_size, head_dim) - query = query.view(batch_size, -1, attn.heads * head_dim) - key = key.view(batch_size, -1, attn.heads * head_dim) + # [s * b, h // sp, d] -> [s // sp * b, h, d] -> [s // sp, b, h * d] + hidden_states = all_to_all_SBH(hidden_states, scatter_dim=0, gather_dim=1).view(-1, batch_size, h_size) + else: + if npu_config.enable_FA and query.dtype == torch.float32: + dtype = torch.bfloat16 + else: + dtype = None + + if self.use_rope: + query = query.view(batch_size, -1, attn.heads, head_dim) + key = key.view(batch_size, -1, attn.heads, head_dim) + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + key = self.rope(key, pos_thw) + query = query.view(batch_size, -1, attn.heads * head_dim) + key = key.view(batch_size, -1, attn.heads * head_dim) - with set_run_dtype(query, dtype): - query, key, value = npu_config.set_current_run_dtype([query, key, value]) - hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", - head_dim, attn.heads) + with set_run_dtype(query, dtype): + query, key, value = npu_config.set_current_run_dtype([query, key, value]) + hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", + head_dim, attn.heads) - hidden_states = npu_config.restore_dtype(hidden_states) + hidden_states = npu_config.restore_dtype(hidden_states) else: query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) @@ -1088,12 +1131,18 @@ def forward( norm_hidden_states = self.norm1(hidden_states, added_cond_kwargs["pooled_text_emb"]) elif self.norm_type == "ada_norm_single": # import ipdb;ipdb.set_trace() - shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( - self.scale_shift_table[None] + timestep.reshape(batch_size, 6, -1) - ).chunk(6, dim=1) + if npu_config is not None and get_sequence_parallel_state(): + batch_size = hidden_states.shape[1] + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( + self.scale_shift_table[:, None] + timestep.reshape(6, batch_size, -1) + ).chunk(6, dim=0) + else: + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( + self.scale_shift_table[None] + timestep.reshape(batch_size, 6, -1) + ).chunk(6, dim=1) norm_hidden_states = self.norm1(hidden_states) norm_hidden_states = norm_hidden_states * (1 + scale_msa) + shift_msa - norm_hidden_states = norm_hidden_states.squeeze(1) + # norm_hidden_states = norm_hidden_states.squeeze(1) else: raise ValueError("Incorrect norm used") diff --git a/opensora/models/diffusion/opensora/rope.py b/opensora/models/diffusion/opensora/rope.py index 5adc3adca..6c8d761bc 100644 --- a/opensora/models/diffusion/opensora/rope.py +++ b/opensora/models/diffusion/opensora/rope.py @@ -2,6 +2,7 @@ try: import torch_npu from opensora.npu_config import npu_config, set_run_dtype + from opensora.acceleration.parallel_states import get_sequence_parallel_state except: torch_npu = None npu_config = None @@ -18,7 +19,10 @@ def __call__(self, b, t, h, w, device): y = torch.arange(h, device=device) z = torch.arange(t, device=device) pos = torch.cartesian_prod(z, y, x) - pos = pos.reshape(t * h * w, 3).transpose(0, 1).reshape(3, 1, -1).contiguous().expand(3, b, -1).clone() + if npu_config is not None and get_sequence_parallel_state(): + pos = pos.reshape(t * h * w, 3).transpose(0, 1).reshape(3, -1, 1).contiguous().expand(3, -1, b).clone() + else: + pos = pos.reshape(t * h * w, 3).transpose(0, 1).reshape(3, 1, -1).contiguous().expand(3, b, -1).clone() poses = (pos[0].contiguous(), pos[1].contiguous(), pos[2].contiguous()) max_poses = (int(poses[0].max()), int(poses[1].max()), int(poses[2].max())) diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index 1d303a43f..33e23e51e 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -556,8 +556,7 @@ def __call__( # require the shape of (batch_size x nheads x ntokens x dim) pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) query = self.rope(query, pos_thw) - if query.shape == key.shape: - key = self.rope(key, pos_thw) + key = self.rope(key, pos_thw) query = query.view(batch_size, -1, attn.heads * head_dim) key = key.view(batch_size, -1, attn.heads * head_dim) diff --git a/opensora/npu_config.py b/opensora/npu_config.py index 93410c170..b352c803e 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -22,7 +22,6 @@ from contextlib import contextmanager import types -# from opensora.acceleration.parallel_states import enable_LCCL, hccl_info, lccl_info def compress_video(input_file, output_file, out_size): @@ -159,9 +158,9 @@ def replace_methods(self, target_class, source_class, skip_fcns=[], only_include def get_attention_mask(self, attention_mask, repeat_num): if self.on_npu and attention_mask is not None: - attention_mask = attention_mask.repeat(1, repeat_num, 1) if npu_config.enable_FA: attention_mask = attention_mask.to(torch.bool) + attention_mask = attention_mask.repeat(1, repeat_num, 1) return attention_mask def set_current_run_dtype(self, variables): if variables[0].dtype != self.current_run_dtype and self.current_run_dtype is not None: @@ -174,13 +173,6 @@ def restore_dtype(self, x): x = x.to(self.original_run_dtype) return x - def get_sp_size(self): - if enable_LCCL: - sp_size = lccl_info.world_size - else: - sp_size = hccl_info.world_size - return sp_size - def get_output_video_path(self, name): os.makedirs(f"{self.work_path}/output_videos", exist_ok=True) return f"{self.work_path}/output_videos/{name}" diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index c3db210fe..dc94a8fc2 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -22,6 +22,9 @@ try: import torch_npu from opensora.npu_config import npu_config + from opensora.acceleration.parallel_states import initialize_sequence_parallel_state, \ + destroy_sequence_parallel_group, get_sequence_parallel_state + from opensora.acceleration.communications import prepare_parallel_data, broadcast except: torch_npu = None npu_config = None @@ -154,7 +157,7 @@ def main(args): if torch_npu is not None and npu_config is not None: npu_config.print_msg(args) - npu_config.seed_everything() + npu_config.seed_everything(args.seed) accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) accelerator = Accelerator( @@ -164,6 +167,9 @@ def main(args): project_config=accelerator_project_config, ) + if npu_config is not None and args.num_frames != 1 and args.use_image_num == 0: + initialize_sequence_parallel_state(args.sp_size) + if args.report_to == "wandb": if not is_wandb_available(): raise ImportError("Make sure to install wandb if you want to use it for logging during training.") @@ -569,6 +575,8 @@ def run(model_input, model_kwargs, prof): bsz = model_input.shape[0] # Sample a random timestep for each image without bias. timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + if get_sequence_parallel_state(): + broadcast(timesteps) # Add noise to the model input according to the noise magnitude at each timestep # (this is the forward diffusion process) @@ -704,12 +712,25 @@ def train_one_step(step_, data_item_, prof_=None): images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) x = torch.cat([videos, images], dim=2) # b c 17+4, h, w - with accelerator.accumulate(model): - assert not torch.any(torch.isnan(x)), 'after vae' - x = x.to(weight_dtype) - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) - run(x, model_kwargs, prof_) + if npu_config is not None and get_sequence_parallel_state(): + x, cond, attn_mask, cond_mask, use_image_num = prepare_parallel_data(x, cond, attn_mask, cond_mask, + args.use_image_num) + for iter in range(args.train_batch_size * args.sp_size // args.train_sp_batch_size): + with accelerator.accumulate(model): + st_idx = iter * args.train_sp_batch_size + ed_idx = (iter + 1) * args.train_sp_batch_size + model_kwargs = dict(encoder_hidden_states=cond[st_idx: ed_idx], + attention_mask=attn_mask[st_idx: ed_idx], + encoder_attention_mask=cond_mask[st_idx: ed_idx], use_image_num=use_image_num) + run(x[st_idx: ed_idx], model_kwargs, prof_) + + else: + with accelerator.accumulate(model): + assert not torch.any(torch.isnan(x)), 'after vae' + x = x.to(weight_dtype) + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) + run(x, model_kwargs, prof_) if progress_info.global_step >= args.max_train_steps: return True @@ -752,6 +773,8 @@ def train_all_epoch(prof_=None): train_all_epoch() accelerator.wait_for_everyone() accelerator.end_training() + if npu_config is not None and get_sequence_parallel_state(): + destroy_sequence_parallel_group() if __name__ == "__main__": @@ -875,6 +898,8 @@ def train_all_epoch(prof_=None): ) parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel") + parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training") args = parser.parse_args() main(args) diff --git a/scripts/text_condition/npu/train_video3d_sp_nx240p.sh b/scripts/text_condition/npu/train_video3d_sp_nx240p.sh new file mode 100644 index 000000000..7230edc9c --- /dev/null +++ b/scripts/text_condition/npu/train_video3d_sp_nx240p.sh @@ -0,0 +1,53 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 8 \ + --gradient_accumulation_steps=8 \ + --max_train_steps=1000000 \ + --learning_rate=4e-5 \ + --lr_scheduler="cosine" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --seed=10 \ + --checkpointing_steps=500 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --resume_from_checkpoint="latest" \ + --enable_tiling \ + --sp_size 8 \ + --train_sp_batch_size 1 From 5357c3ba9a4086f384b8cdccc02105a5ada769a0 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Fri, 21 Jun 2024 13:29:38 +0000 Subject: [PATCH 060/134] video ip --- opensora/dataset/inpaint_datasets.py | 12 ++--- opensora/sample/sample_t2v.py | 2 +- opensora/train/train_inpaint.py | 48 +++++++++++++------ .../deepspeed_zero2_config.yaml | 4 +- scripts/text_condition/gpu/sample_video.sh | 7 ++- .../gpu/train_inpaint_video21d_65x512x512.sh | 33 +++++++------ .../train_data/video_data_debug_inpaint.txt | 1 - 7 files changed, 64 insertions(+), 43 deletions(-) delete mode 100644 scripts/train_data/video_data_debug_inpaint.txt diff --git a/opensora/dataset/inpaint_datasets.py b/opensora/dataset/inpaint_datasets.py index de348a74d..967d5976f 100644 --- a/opensora/dataset/inpaint_datasets.py +++ b/opensora/dataset/inpaint_datasets.py @@ -169,10 +169,10 @@ def get_vid_cap_list(self): for i in tqdm(range(len(vid_cap_list))): path = opj(folder, vid_cap_list[i]['path']) # For testing, some data has not been utilized. - if not os.path.exists(path): - continue - elif os.path.exists(path.replace('.mp4', '_resize_1080p.mp4')): - path = path.replace('.mp4', '_resize_1080p.mp4') + if not os.path.exists(path) and not os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): + break + elif os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): + path = path.replace('.mp4', '_resize1080p.mp4') new_vid_cap_list.append( { 'path': path, @@ -195,7 +195,7 @@ def get_vid_cap_list(self): class Args: def __init__(self): - self.video_data = '/data01/transition/Open-Sora-Plan/scripts/train_data/video_data.txt' + self.video_data = '/storage/gyy/hw/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' self.num_frames = 65 self.model_max_length = 300 self.cfg = 0.1 @@ -205,7 +205,7 @@ def __init__(self): self.max_image_size = 512 self.sample_rate = 1 self.text_encoder_name = "DeepFloyd/t5-v1_1-xxl" - self.cache_dir = "./cache" + self.cache_dir = "/storage/cache_dir" args = Args() resize = [CenterCropResizeVideo((args.max_image_size, args.max_image_size))] diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 6c3ac7057..5d5baf1f7 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -56,7 +56,7 @@ def main(args): transformer_model = UDiTT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: - transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, + transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py index ffe25c9b9..30ec30874 100644 --- a/opensora/train/train_inpaint.py +++ b/opensora/train/train_inpaint.py @@ -150,7 +150,23 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh for idx, video in enumerate(videos): save_video(video, os.path.join(save_dir, f"video_{idx:06d}.mp4")) - print("deleta validation pipeline...") + videos = torch.stack(videos).numpy() + videos = rearrange(videos, 'b t h w c -> b t c h w') + + for tracker in accelerator.trackers: + if tracker.name == "wandb": + import wandb + + logs = { + f"{'ema_' if ema else ''}validation_videos": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=24) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ], + } + + tracker.log(logs, step=global_step) + + print("delete validation pipeline...") del pipeline gc.collect() torch.cuda.empty_cache() @@ -456,8 +472,15 @@ def load_model_hook(models, input_dir): # We need to initialize the trackers we use, and also store our configuration. # The trackers initializes automatically on the main process. - # if accelerator.is_main_process: - # accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + if accelerator.is_main_process: + project_name = os.getenv('PROJECT', os.path.basename(args.output_dir)) + entity = os.getenv('ENTITY', None) + run_name = os.getenv('WANDB_NAME', None) + init_kwargs = { + "entity": entity, + "run_name": run_name, + } + accelerator.init_trackers(project_name=project_name, config=vars(args), init_kwargs=init_kwargs) # Train! total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps @@ -647,20 +670,11 @@ def run(model_input, model_kwargs, prof): sync_gradients_info(loss) if accelerator.is_main_process: - # for tracker in accelerator.trackers: - # if tracker.name == "wandb": - # if progress_info.global_step % args.checkpointing_steps != 0: - # if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): - # tracker.log( - # {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) - # elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): - # tracker.log( - # {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) if progress_info.global_step % args.checkpointing_steps == 0: - # if args.enable_tracker: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, progress_info.global_step) if args.use_ema: @@ -746,6 +760,12 @@ def train_all_epoch(prof_=None): return True for step, data_item in enumerate(train_dataloader): + if accelerator.is_main_process: + if step == 0: + print("before training, we need to check the validation mode...") + log_validation( + args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step=0, ema=False + ) if train_one_step(step, data_item, prof_): break diff --git a/scripts/accelerate_configs/deepspeed_zero2_config.yaml b/scripts/accelerate_configs/deepspeed_zero2_config.yaml index eedb2162b..b15390113 100644 --- a/scripts/accelerate_configs/deepspeed_zero2_config.yaml +++ b/scripts/accelerate_configs/deepspeed_zero2_config.yaml @@ -8,6 +8,6 @@ main_process_ip: null main_process_port: 29503 main_training_function: main num_machines: 1 -num_processes: 4 -gpu_ids: 4,5,6,7 +num_processes: 8 +gpu_ids: 0,1,2,3,4,5,6,7 use_cpu: false diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index 7861ee3af..0bf91a15c 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -1,14 +1,13 @@ CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v.py \ - --model_path "/data01/transition/Open-Sora-Plan_models" \ - --version 65x512x512 \ + --model_path "/storage/1.1model/hw_65/model" \ --num_frames 65 \ --height 512 \ --width 512 \ - --cache_dir "/data01/transition/cache_dir" \ + --cache_dir "/storage/cache_dir" \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/data01/transition/Open-Sora-Plan_models/vae" \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ --save_img_path "./sample_video_65x512x512" \ --fps 24 \ --guidance_scale 7.5 \ diff --git a/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh index 348c9db16..1557f51a9 100644 --- a/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh +++ b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh @@ -1,26 +1,27 @@ -# export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" # # export WANDB_MODE="offline" -# export ENTITY="linbin" -# export PROJECT="testnpu21d_" +export ENTITY="yunyangge" +export PROJECT="inpaint_65x512x512_1node_bs2_lr2e-5" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 +export TOKENIZERS_PARALLELISM=false # # NCCL setting IB网卡时用 -# export NCCL_PXN_DISABLE=0 -# export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_IB_GID_INDEX=3 -# export NCCL_ALGO=Ring -# export OMP_NUM_THREADS=1 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 accelerate launch \ --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_inpaint.py \ --model OpenSoraInpaint_XL_122 \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "/data01/transition/cache_dir" \ + --cache_dir "/storage/cache_dir" \ --dataset inpaint \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/data01/transition/Open-Sora-Plan_models/vae" \ - --video_data "scripts/train_data/video_data_debug_inpaint.txt" \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ + --video_data "scripts/train_data/video_data_debug.txt" \ --sample_rate 1 \ --num_frames 65\ --max_height 512 \ @@ -36,13 +37,14 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=200 \ - --output_dir="inpaint_65x512x512_1node_bs4_lr2e-5" \ + --enable_tracker \ + --checkpointing_steps=500 \ + --output_dir="inpaint_65x512x512_1node_bs2_lr2e-5" \ --allow_tf32 \ --model_max_length 300 \ --enable_tiling \ - --pretrained "/data01/transition/Open-Sora-Plan_models/65x512x512/diffusion_pytorch_model.safetensors" \ - --validation_dir "/data01/transition/hw/Open-Sora-Plan/validation_dir" \ + --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ + --validation_dir "validation_dir" \ --use_image_num 0 \ --cfg 0.1 \ --ema_start_step 0 \ @@ -50,6 +52,7 @@ accelerate launch \ --i2v_ratio 0.4 \ --transition_ratio 0.4 \ --default_text_ratio 0.1 \ + --seed 42 \ # --snr_gamma 5.0 \ # --zero_terminal_snr \ # --noise_offset 0.02 \ \ No newline at end of file diff --git a/scripts/train_data/video_data_debug_inpaint.txt b/scripts/train_data/video_data_debug_inpaint.txt deleted file mode 100644 index 62ec1394a..000000000 --- a/scripts/train_data/video_data_debug_inpaint.txt +++ /dev/null @@ -1 +0,0 @@ -/data01/transition/opensora_data/mixkit,/data01/transition/opensora_data/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file From d4a85e25d388e8a53db999cb6017d97871e665b6 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Sat, 22 Jun 2024 02:31:45 +0000 Subject: [PATCH 061/134] inpaint --- .gitignore | 3 +- .../{pipeline_i2v.py => pipeline_inpaint.py} | 28 ++++++----- opensora/train/train_inpaint.py | 46 ++++++++++++++----- .../deepspeed_zero2_config.yaml | 4 +- scripts/text_condition/gpu/sample_video.sh | 6 +-- validation_dir/prompt.txt | 5 +- 6 files changed, 62 insertions(+), 30 deletions(-) rename opensora/sample/{pipeline_i2v.py => pipeline_inpaint.py} (96%) diff --git a/.gitignore b/.gitignore index 358f95e94..14d8358cd 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,5 @@ flash* 65x256* alpha_vae *node* -cache/ \ No newline at end of file +cache/ +Open-Sora-Plan_models/ \ No newline at end of file diff --git a/opensora/sample/pipeline_i2v.py b/opensora/sample/pipeline_inpaint.py similarity index 96% rename from opensora/sample/pipeline_i2v.py rename to opensora/sample/pipeline_inpaint.py index 4e40244db..c4997e300 100644 --- a/opensora/sample/pipeline_i2v.py +++ b/opensora/sample/pipeline_inpaint.py @@ -108,7 +108,7 @@ def retrieve_timesteps( return timesteps, num_inference_steps -class OpenSoraImage2VideoPipeline(DiffusionPipeline): +class OpenSoraInpaintPipeline(DiffusionPipeline): r""" Pipeline for text-to-image generation using PixArt-Sigma. """ @@ -550,7 +550,8 @@ def prepare_latents(self, batch_size, num_channels_latents, num_frames, height, @replace_example_docstring(EXAMPLE_DOC_STRING) def __call__( self, - init_image: torch.Tensor, + condition_images: torch.Tensor, + condition_images_indices: List, prompt: Union[str, List[str]] = None, negative_prompt: str = "", num_inference_steps: int = 20, @@ -733,18 +734,23 @@ def __call__( # 7. Denoising loop num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) - # NOTE I2V - if len(init_image.shape) == 3: - init_image = init_image.unsqueeze(1) # C H W -> C 1 H W - elif len(init_image.shape) == 4: - init_image = init_image.transpose(0, 1) # 1 C H W -> C 1 H W + # NOTE inpaint + assert isinstance(condition_images_indices, list) and len(condition_images_indices) == len(condition_images) and isinstance(condition_images_indices[0], int), "condition_images_indices should be a list of int" + if isinstance(condition_images, list) and isinstance(condition_images[0], torch.Tensor): + if len(condition_images[0].shape) == 3: + condition_images = [condition_image.unsqueeze(1) for condition_image in condition_images] # C H W -> C 1 H W + elif len(condition_images[0].shape) == 4: + condition_images = [condition_image.transpose(0, 1) for condition_image in condition_images] # 1 C H W -> C 1 H W + else: + raise ValueError("condition_images should be a list of torch.Tensor") - init_image = init_image.unsqueeze(0).repeat((batch_size * num_images_per_prompt, 1, 1, 1, 1)) - input_video = torch.zeros([batch_size * num_images_per_prompt, 3, num_frames, height, width], device=device) - input_video = torch.cat([init_image, input_video[:, :, 1:]], dim=2) + + input_video = torch.zeros([3, num_frames, height, width], device=device) + input_video[:, condition_images_indices] = torch.cat(condition_images, dim=1).to(device) # C 1/2 H W -> C F H W + input_video = input_video.unsqueeze(0).repeat(batch_size * num_images_per_prompt, 1, 1, 1, 1) mask = torch.ones_like(input_video, device=device) - mask[:, :, 0] = 0 + mask[:, :, condition_images_indices] = 0 masked_video = input_video * (mask < 0.5) diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py index 30ec30874..9a77cc235 100644 --- a/opensora/train/train_inpaint.py +++ b/opensora/train/train_inpaint.py @@ -64,7 +64,7 @@ from torchvision import transforms from torchvision.transforms import Lambda from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo -from opensora.sample.pipeline_i2v import OpenSoraImage2VideoPipeline +from opensora.sample.pipeline_inpaint import OpenSoraInpaintPipeline @@ -97,9 +97,16 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh with open(prompt_file, 'r') as f: validation_prompt = f.readlines() - validation_images = glob.glob(os.path.join(validation_dir, "*.png")) - validation_images = sorted(validation_images) - + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break resize = [CenterCropResizeVideo((args.max_height, args.max_width)),] norm_fun = Lambda(lambda x: 2. * x - 1.) @@ -114,7 +121,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh model = accelerator.unwrap_model(model) scheduler = PNDMScheduler() - pipeline = OpenSoraImage2VideoPipeline( + pipeline = OpenSoraInpaintPipeline( vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, @@ -122,16 +129,31 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh transformer=model ).to(device=accelerator.device) + def preprocess_images(images): + if len(images) == 1: + condition_images_indices = [0] + elif len(images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in images] + condition_images = [torch.from_numpy(np.copy(np.array(image))) for image in condition_images] + condition_images = [rearrange(image, 'h w c -> c h w').unsqueeze(0) for image in condition_images] + condition_images = [transform(image).to(accelerator.device, dtype=torch.float32) for image in condition_images] + return dict(condition_images=condition_images, condition_images_indices=condition_images_indices) + videos = [] - for prompt, init_image in zip(validation_prompt, validation_images): - logger.info('Processing the ({}) prompt and the init image ({})'.format(prompt, init_image)) - init_image = Image.open(init_image).convert("RGB") - init_image = torch.from_numpy(np.copy(np.array(init_image))) - init_image = rearrange(init_image, 'h w c -> c h w').unsqueeze(0) - init_image = transform(init_image).to(accelerator.device, dtype=torch.float32) + for prompt, images in zip(validation_prompt, validation_images_list): + if not isinstance(images, list): + images = [images] + logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) + + pre_results = preprocess_images(images) + condition_images = pre_results['condition_images'] + condition_images_indices = pre_results['condition_images_indices'] + video = pipeline( prompt=prompt, - init_image=init_image, + condition_images=condition_images, + condition_images_indices=condition_images_indices, num_frames=args.num_frames, height=args.max_height, width=args.max_width, diff --git a/scripts/accelerate_configs/deepspeed_zero2_config.yaml b/scripts/accelerate_configs/deepspeed_zero2_config.yaml index b15390113..0d3769363 100644 --- a/scripts/accelerate_configs/deepspeed_zero2_config.yaml +++ b/scripts/accelerate_configs/deepspeed_zero2_config.yaml @@ -8,6 +8,6 @@ main_process_ip: null main_process_port: 29503 main_training_function: main num_machines: 1 -num_processes: 8 -gpu_ids: 0,1,2,3,4,5,6,7 +num_processes: 2 +gpu_ids: 0,1 use_cpu: false diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index 0bf91a15c..855d865c4 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -1,13 +1,13 @@ CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v.py \ - --model_path "/storage/1.1model/hw_65/model" \ + --model_path "/remote-home/gyy/Open-Sora-Plan/Open-Sora-Plan_models/65x512x512" \ --num_frames 65 \ --height 512 \ --width 512 \ - --cache_dir "/storage/cache_dir" \ + --cache_dir "./cache_dir" \ --text_encoder_name DeepFloyd/t5-v1_1-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8" \ + --ae_path "/remote-home/gyy/Open-Sora-Plan/Open-Sora-Plan_models/vae" \ --save_img_path "./sample_video_65x512x512" \ --fps 24 \ --guidance_scale 7.5 \ diff --git a/validation_dir/prompt.txt b/validation_dir/prompt.txt index 3b3c669e8..8cc53e02c 100644 --- a/validation_dir/prompt.txt +++ b/validation_dir/prompt.txt @@ -1,3 +1,6 @@ A rocket ascends slowly into the sky. Along the coast, variously sized boats float on the lake, with the camera gradually zooming in. -The landscape at sunset is profound and expansive. \ No newline at end of file +The landscape at sunset is profound and expansive. +This video showcases the movement of flower and bird painting, with a smooth transition from flowers to birds in the picture. +This video showcases the scenery of snow capped mountains. +This video showcases the night view of the city. \ No newline at end of file From a70890386341f6272a0bec791cb80181dc6ecaa2 Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Mon, 24 Jun 2024 11:41:50 +0800 Subject: [PATCH 062/134] fix mix-precision with deepspeed zero mode --- opensora/adaptor/modules.py | 23 +++++++++++++++++++ opensora/dataset/t2v_datasets.py | 2 +- opensora/models/ae/videobase/modules/conv.py | 2 +- .../ae/videobase/modules/updownsample.py | 8 +++---- opensora/models/diffusion/opensora/modules.py | 4 ++-- opensora/npu_config.py | 10 ++++++-- opensora/sample/pipeline_opensora.py | 14 +++++++++-- opensora/sample/sample_t2v.py | 2 ++ opensora/sample/sample_t2v_on_npu.py | 13 ++++++++--- opensora/train/train_t2v_diffusers.py | 11 +++++---- scripts/text_condition/npu/sample_image.sh | 11 ++++++--- .../text_condition/npu/train_image3d_480p.sh | 12 ++++++---- .../npu/train_image3d_480p_rope.sh | 10 ++++---- scripts/train_data/image_data_on_npu.txt | 4 +--- 14 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 opensora/adaptor/modules.py diff --git a/opensora/adaptor/modules.py b/opensora/adaptor/modules.py new file mode 100644 index 000000000..e53599d55 --- /dev/null +++ b/opensora/adaptor/modules.py @@ -0,0 +1,23 @@ +import torch +from torch import nn +from torch.nn import functional as F + + +def fp32_layer_norm_forward(self, inputs: torch.Tensor) -> torch.Tensor: + origin_dtype = inputs.dtype + return F.layer_norm(inputs.float(), self.normalized_shape, self.weight.float() if self.weight is not None else None, + self.bias.float() if self.bias is not None else None, self.eps).to(origin_dtype) + + +def fp32_silu_forward(self, inputs: torch.Tensor) -> torch.Tensor: + return torch.nn.functional.silu(inputs.float(), inplace=self.inplace).to(inputs.dtype) + + +def fp32_gelu_forward(self, inputs: torch.Tensor) -> torch.Tensor: + return torch.nn.functional.gelu(inputs.float(), approximate=self.approximate).to(inputs.dtype) + + +def replace_with_fp32_forwards(): + nn.GELU.forward = fp32_gelu_forward + nn.SiLU.forward = fp32_silu_forward + nn.LayerNorm.forward = fp32_layer_norm_forward diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index ea3f840b6..7e19d5d56 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -293,7 +293,7 @@ def get_img_cap_list(self): img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] else: - img_cap_lists = npu_config.try_load_pickle("img_cap_lists5", + img_cap_lists = npu_config.try_load_pickle("img_cap_lists_only_mj_with_cn", lambda: self.read_jsons(self.image_data, postfix=".jpg")) img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] diff --git a/opensora/models/ae/videobase/modules/conv.py b/opensora/models/ae/videobase/modules/conv.py index 1dbc4a9e5..3733bc495 100644 --- a/opensora/models/ae/videobase/modules/conv.py +++ b/opensora/models/ae/videobase/modules/conv.py @@ -101,7 +101,7 @@ def _init_weights(self, init_method): def forward(self, x): if npu_config is not None and npu_config.on_npu: x_dtype = x.dtype - x = x.to(torch.float16) + x = x.to(npu_config.replaced_type) n, c, d, h, w = x.shape x = x.reshape(n * c, d, h * w) x = self.pad(x) diff --git a/opensora/models/ae/videobase/modules/updownsample.py b/opensora/models/ae/videobase/modules/updownsample.py index a0fa3dbfb..5122ddfba 100644 --- a/opensora/models/ae/videobase/modules/updownsample.py +++ b/opensora/models/ae/videobase/modules/updownsample.py @@ -52,7 +52,7 @@ def forward(self, x): pad = (0, 1, 0, 1) if npu_config is not None and npu_config.on_npu: x_dtype = x.dtype - x = x.to(torch.bfloat16) + x = x.to(npu_config.replaced_type) x = torch.nn.functional.pad(x, pad, mode="constant", value=0) x = npu_config.run_conv3d(self.conv, x, x_dtype) else: @@ -188,7 +188,7 @@ def forward(self, x): if npu_config is not None and npu_config.on_npu: n, c, d, h, w = x.shape x_dtype = x.dtype - x = x.to(torch.float16) + x = x.to(npu_config.replaced_type) x = self.pad(x) pad_x = x.view(n, c, -1, h, w) avg_x = self.avg_pool(x.view(n * c, -1, h * w)).view(n, c, -1, h, w).to(x_dtype) @@ -221,7 +221,7 @@ def forward(self, x): x,x_= x[:,:,:1],x[:,:,1:] if npu_config is not None and npu_config.on_npu: x_dtype = x_.dtype - x_ = x_.to(torch.float16) + x_ = x_.to(npu_config.replaced_type) x_ = F.interpolate(x_, scale_factor=(2, 1, 1), mode='trilinear') x_ = x_.to(x_dtype) else: @@ -277,7 +277,7 @@ def forward(self, x): x,x_= x[:,:,:1],x[:,:,1:] if npu_config is not None and npu_config.on_npu: x_dtype = x_.dtype - x_ = x_.to(torch.float16) + x_ = x_.to(npu_config.replaced_type) x_= F.interpolate(x_, scale_factor=(2,1,1), mode='trilinear') x_ = x_.to(x_dtype) else: diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index a16e9ddb4..488195fb5 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -828,12 +828,12 @@ def forward(self, x, t, h, w): x = self.project_out(out) else: x_dtype = x.dtype - x = npu_config.run_conv3d(self.project_in, x, torch.float16) + x = npu_config.run_conv3d(self.project_in, x, npu_config.replaced_type) x = rearrange(x, 'b (t h w) d -> b d t h w', t=t, h=h, w=w) x = F.gelu(x) out = x for module in self.dwconv: - out = out + npu_config.run_conv3d(module, x, torch.float16) + out = out + npu_config.run_conv3d(module, x, npu_config.replaced_type) out = rearrange(out, 'b d t h w -> b (t h w) d', t=t, h=h, w=w) x = npu_config.run_conv3d(self.project_out, out, x_dtype) return x diff --git a/opensora/npu_config.py b/opensora/npu_config.py index b352c803e..fb22876bc 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -8,6 +8,7 @@ import subprocess import sys import threading +import gc import torch.distributed as dist from opensora.adaptor.zp_manager import zp_manager @@ -69,6 +70,7 @@ def __init__(self): self.current_run_dtype = None self.original_run_dtype = None self.zp_manager = zp_manager + self.replaced_type = torch.float32 if self.enable_FA and self.enable_FP32: self.inf_float = -10000.0 else: @@ -112,6 +114,7 @@ def __init__(self): self.world_size = self.N_NPU_PER_NODE self.print_with_rank(f"The npu_config.on_npu is {self.on_npu}") self.bind_thread_to_cpu() + gc.set_threshold(700, 10, 10000) def get_total_cores(self): try: @@ -261,6 +264,9 @@ def _run(self, operator, x, tmp_dtype, out_dtype=None, out_nd_format=False): def run_group_norm(self, operator, x): return self._run(operator, x, torch.float32) + def run_layer_norm(self, operator, x): + return self._run(operator, x, torch.float32) + def print_tensor_stats(self, tensor, name="Tensor", rank=None): if rank and rank != self.rank: return @@ -286,12 +292,12 @@ def run_conv3d(self, operator, x, out_dtype): return self._run(operator, x, torch.float16, out_dtype, out_nd_format=True) def run_pool_2d(self, operator, x): - return self._run(operator, x, torch.float16) + return self._run(operator, x, self.replaced_type) def run_pad_2d(self, operator, x, pad, mode="constant"): if self.on_npu: x_dtype = x.dtype - x = x.to(torch.float16) + x = x.to(self.replaced_type) x = operator(x, pad, mode) x = x.to(x_dtype) else: diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 6a06d88b9..629c07f93 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -34,6 +34,10 @@ from diffusers.utils.torch_utils import randn_tensor from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput +try: + from opensora.npu_config import npu_config +except: + npu_config = None logger = logging.get_logger(__name__) # pylint: disable=invalid-name @@ -784,6 +788,8 @@ def __call__( # compute previous image: x_t -> x_t-1 latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] + # npu_config.print_tensor_stats(latents, f"latents_i{i}_t{t}", rank=0) + assert not torch.isnan(latents).any().item(), "latents contains NaN values" # call the callback, if provided if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): @@ -807,10 +813,14 @@ def __call__( return (image,) return ImagePipelineOutput(images=image) - - + + def decode_latents(self, latents): + if npu_config: + npu_config.print_tensor_stats(latents, f"before vae", rank=0) video = self.vae.decode(latents.to(self.vae.vae.dtype)) + if npu_config: + npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 return video diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 084e9657c..8352228b2 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -16,6 +16,7 @@ import os, sys +from opensora.adaptor.modules import replace_with_fp32_forwards from opensora.models.ae import ae_stride_config, getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V @@ -32,6 +33,7 @@ def main(args): # torch.manual_seed(args.seed) + replace_with_fp32_forwards() weight_dtype = torch.float16 device = torch.device(args.device) diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py index d9a2a714c..567869031 100644 --- a/opensora/sample/sample_t2v_on_npu.py +++ b/opensora/sample/sample_t2v_on_npu.py @@ -17,6 +17,7 @@ import os, sys +from opensora.adaptor.modules import replace_with_fp32_forwards from opensora.models.ae import ae_stride_config, getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper from opensora.models.diffusion.udit.modeling_udit import UDiTT2V @@ -167,6 +168,7 @@ def get_file_name(): args = parser.parse_args() npu_config.print_msg(args) + replace_with_fp32_forwards() # 初始化分布式环境 local_rank = int(os.getenv('RANK', 0)) @@ -176,7 +178,7 @@ def get_file_name(): dist.init_process_group(backend='hccl', init_method='env://', world_size=8, rank=local_rank) # torch.manual_seed(args.seed) - weight_dtype = torch.float16 + weight_dtype = torch.float32 device = torch.cuda.current_device() # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) @@ -227,13 +229,18 @@ def get_file_name(): latest_path = None save_img_path = args.save_img_path + first_in = False while True: cur_path = get_latest_path() if cur_path == latest_path: - time.sleep(5) + time.sleep(60) continue - time.sleep(1) + if not first_in: + first_in = True + else: + time.sleep(1200) + latest_path = cur_path npu_config.print_msg(f"The latest_path is {latest_path}") full_path = f"{args.model_path}/{latest_path}/model_ema" diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index dc94a8fc2..62d1054f0 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -19,6 +19,8 @@ from einops import rearrange from tqdm import tqdm +from opensora.adaptor.modules import replace_with_fp32_forwards + try: import torch_npu from opensora.npu_config import npu_config @@ -155,6 +157,8 @@ def __init__(self, global_step, train_loss=0.0): def main(args): logging_dir = Path(args.output_dir, args.logging_dir) + # use LayerNorm, GeLu, SiLu always as fp32 mode + replace_with_fp32_forwards() if torch_npu is not None and npu_config is not None: npu_config.print_msg(args) npu_config.seed_everything(args.seed) @@ -658,13 +662,12 @@ def run(model_input, model_kwargs, prof): log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, progress_info.global_step) - if args.use_ema: + if args.use_ema and npu_config is None: # Store the UNet parameters temporarily and load the EMA parameters to perform inference. ema_model.store(model.parameters()) ema_model.copy_to(model.parameters()) - if npu_config is None: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step, ema=True) + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) # Switch back to the original UNet parameters. ema_model.restore(model.parameters()) diff --git a/scripts/text_condition/npu/sample_image.sh b/scripts/text_condition/npu/sample_image.sh index 1465b5e18..908ecfa5e 100644 --- a/scripts/text_condition/npu/sample_image.sh +++ b/scripts/text_condition/npu/sample_image.sh @@ -7,16 +7,21 @@ if [ -z "$SAMPLE_SAVE_PATH" ]; then export SAMPLE_SAVE_PATH="/home/image_data/sample_videos" fi +if [ -z "$SAMPLE_HEIGHT" ]; then + echo "You should set both envs of SAMPLE_HEIGHT and SAMPLE_WIDTH" + return +fi + torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ --num_frames ${NUM_FRAME} \ - --height 256 \ - --width 256 \ + --height $SAMPLE_HEIGHT \ + --width $SAMPLE_WIDTH \ --cache_dir "../cache_dir" \ --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/test140k/" \ + --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ --fps 24 \ --guidance_scale 5.0 \ diff --git a/scripts/text_condition/npu/train_image3d_480p.sh b/scripts/text_condition/npu/train_image3d_480p.sh index 2dc3bc3ad..22bbd0429 100644 --- a/scripts/text_condition/npu/train_image3d_480p.sh +++ b/scripts/text_condition/npu/train_image3d_480p.sh @@ -27,14 +27,14 @@ accelerate launch \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=16 \ + --train_batch_size=8 \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ + --learning_rate=2e-5 \ + --lr_scheduler="cosine" \ --seed=10 \ - --lr_warmup_steps=0 \ + --lr_warmup_steps=500 \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=2000 \ @@ -46,5 +46,9 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/image3d_256p_zp_umt5_from_initial_layer_40_head_16/checkpoint-176000/model_ema/diffusion_pytorch_model.safetensors" \ --resume_from_checkpoint="latest" + diff --git a/scripts/text_condition/npu/train_image3d_480p_rope.sh b/scripts/text_condition/npu/train_image3d_480p_rope.sh index ba443d314..cb456b225 100644 --- a/scripts/text_condition/npu/train_image3d_480p_rope.sh +++ b/scripts/text_condition/npu/train_image3d_480p_rope.sh @@ -27,14 +27,14 @@ accelerate launch \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=16 \ + --train_batch_size=8 \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ + --learning_rate=2e-5 \ + --lr_scheduler="cosine" \ --seed=10 \ - --lr_warmup_steps=0 \ + --lr_warmup_steps=500 \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=2000 \ @@ -50,4 +50,6 @@ accelerate launch \ --use_rope \ --enable_tiling \ --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/image3d_rope_256p_zp_umt5/checkpoint-130000/model_ema/diffusion_pytorch_model.safetensors" \ --resume_from_checkpoint="latest" + diff --git a/scripts/train_data/image_data_on_npu.txt b/scripts/train_data/image_data_on_npu.txt index 82c9ec3e3..fcaed27b3 100644 --- a/scripts/train_data/image_data_on_npu.txt +++ b/scripts/train_data/image_data_on_npu.txt @@ -1,4 +1,2 @@ -/home/local_dataset_6t/image_data_obs/AnyWord_3M_unzip_files/,/home/opensora/captions/linbin_captions/anytext_en_1886137.json /home/local_dataset_6t/image_data_obs/mj_unzip_files,/home/opensora/captions/linbin_captions/tuzhan_mj_4615265.json -/home/local_dataset_6t/image_data_obs/sa_unzip_files,/home/opensora/captions/linbin_captions/sam_image_11185255.json -/home/local_dataset_6t/image_data_obs/images/,/home/opensora/captions/linbin_captions/human_images_162094.json \ No newline at end of file +/home/local_dataset_6t/image_data_obs/mj_unzip_files,/home/opensora/captions/tuzhan_mj_4615265_cn.json \ No newline at end of file From df3bcd3c076e9019a300791fdcfb5fd71cc507fa Mon Sep 17 00:00:00 2001 From: stepbystep88 Date: Mon, 24 Jun 2024 19:36:10 +0800 Subject: [PATCH 063/134] fix precision problem for vae decode when using npu --- opensora/dataset/t2v_datasets.py | 2 +- opensora/models/ae/videobase/modules/conv.py | 9 ++-- opensora/npu_config.py | 5 +- opensora/sample/sample_t2v_on_npu.py | 2 + scripts/text_condition/npu/sample_image.sh | 4 +- .../text_condition/npu/train_image3d_240p.sh | 2 +- .../npu/train_image3d_240p_rope.sh | 9 ++-- .../text_condition/npu/train_image3d_256p.sh | 2 +- .../npu/train_image3d_256p_rope.sh | 2 +- .../text_condition/npu/train_image3d_480p.sh | 6 +-- .../npu/train_image3d_480p_rope.sh | 9 ++-- .../text_condition/npu/train_image_256x256.sh | 51 ------------------- .../npu/train_imageuditultra_480p_new.sh | 2 +- ...eo3d_nx240p.sh => train_video3d_nx480p.sh} | 29 ++++++----- ...p_nx240p.sh => train_video3d_sp_nx480p.sh} | 19 ++++--- .../npu/train_videoudit_nx480p.sh | 1 + scripts/train_data/image_data_on_npu.txt | 4 +- 17 files changed, 60 insertions(+), 98 deletions(-) delete mode 100644 scripts/text_condition/npu/train_image_256x256.sh rename scripts/text_condition/npu/{train_video3d_nx240p.sh => train_video3d_nx480p.sh} (67%) rename scripts/text_condition/npu/{train_video3d_sp_nx240p.sh => train_video3d_sp_nx480p.sh} (77%) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 7e19d5d56..4acddf116 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -293,7 +293,7 @@ def get_img_cap_list(self): img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] else: - img_cap_lists = npu_config.try_load_pickle("img_cap_lists_only_mj_with_cn", + img_cap_lists = npu_config.try_load_pickle("img_cap_lists_all", lambda: self.read_jsons(self.image_data, postfix=".jpg")) img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] diff --git a/opensora/models/ae/videobase/modules/conv.py b/opensora/models/ae/videobase/modules/conv.py index 3733bc495..1e6508b95 100644 --- a/opensora/models/ae/videobase/modules/conv.py +++ b/opensora/models/ae/videobase/modules/conv.py @@ -101,11 +101,10 @@ def _init_weights(self, init_method): def forward(self, x): if npu_config is not None and npu_config.on_npu: x_dtype = x.dtype - x = x.to(npu_config.replaced_type) - n, c, d, h, w = x.shape - x = x.reshape(n * c, d, h * w) - x = self.pad(x) - x = x.reshape(n, c, -1, h, w) + first_frame_pad = x[:, :, :1, :, :].repeat( + (1, 1, self.time_kernel_size - 1, 1, 1) + ) # b c t h w + x = torch.concatenate((first_frame_pad, x), dim=2) # 3 + 16 return npu_config.run_conv3d(self.conv, x, x_dtype) else: # 1 + 16 16 as video, 1 as image diff --git a/opensora/npu_config.py b/opensora/npu_config.py index fb22876bc..1e911d93c 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -71,6 +71,7 @@ def __init__(self): self.original_run_dtype = None self.zp_manager = zp_manager self.replaced_type = torch.float32 + self.conv_dtype = torch.float16 if self.enable_FA and self.enable_FP32: self.inf_float = -10000.0 else: @@ -252,7 +253,7 @@ def _run(self, operator, x, tmp_dtype, out_dtype=None, out_nd_format=False): out_dtype = x.dtype with torch.cuda.amp.autocast(enabled=False): - x = operator.to(tmp_dtype)(x.to(tmp_dtype)) + x = operator.to(device=x.device, dtype=tmp_dtype)(x.to(tmp_dtype)) x = x.to(out_dtype) if out_nd_format: return self.npu_format_cast(x) @@ -289,7 +290,7 @@ def print_tensor_stats(self, tensor, name="Tensor", rank=None): f"Median: {median_val}, Std: {std_val}, Shape: {shape}, Type: {x_dtype}") def run_conv3d(self, operator, x, out_dtype): - return self._run(operator, x, torch.float16, out_dtype, out_nd_format=True) + return self._run(operator, x, self.conv_dtype, out_dtype, out_nd_format=True) def run_pool_2d(self, operator, x): return self._run(operator, x, self.replaced_type) diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py index 567869031..16a5d5f3a 100644 --- a/opensora/sample/sample_t2v_on_npu.py +++ b/opensora/sample/sample_t2v_on_npu.py @@ -168,6 +168,7 @@ def get_file_name(): args = parser.parse_args() npu_config.print_msg(args) + npu_config.conv_dtype = torch.bfloat16 replace_with_fp32_forwards() # 初始化分布式环境 @@ -242,6 +243,7 @@ def get_file_name(): time.sleep(1200) latest_path = cur_path + npu_config.print_msg(f"The latest_path is {latest_path}") full_path = f"{args.model_path}/{latest_path}/model_ema" pipeline = load_t2v_checkpoint(full_path) diff --git a/scripts/text_condition/npu/sample_image.sh b/scripts/text_condition/npu/sample_image.sh index 908ecfa5e..1b7cd4deb 100644 --- a/scripts/text_condition/npu/sample_image.sh +++ b/scripts/text_condition/npu/sample_image.sh @@ -18,10 +18,10 @@ torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --height $SAMPLE_HEIGHT \ --width $SAMPLE_WIDTH \ --cache_dir "../cache_dir" \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/CausalVAEModel_4x8x8_0430/" \ + --ae_path "${WEIGHT_PATH}/test140k/" \ --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ --fps 24 \ --guidance_scale 5.0 \ diff --git a/scripts/text_condition/npu/train_image3d_240p.sh b/scripts/text_condition/npu/train_image3d_240p.sh index 280b1977e..a85907a82 100644 --- a/scripts/text_condition/npu/train_image3d_240p.sh +++ b/scripts/text_condition/npu/train_image3d_240p.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ diff --git a/scripts/text_condition/npu/train_image3d_240p_rope.sh b/scripts/text_condition/npu/train_image3d_240p_rope.sh index 1c2b5e4c5..18e938642 100644 --- a/scripts/text_condition/npu/train_image3d_240p_rope.sh +++ b/scripts/text_condition/npu/train_image3d_240p_rope.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-ROPE-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ @@ -31,10 +31,10 @@ accelerate launch \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ + --learning_rate=4e-5 \ + --lr_scheduler="cosine" \ --seed=10 \ - --lr_warmup_steps=0 \ + --lr_warmup_steps=500 \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=2000 \ @@ -48,4 +48,5 @@ accelerate launch \ --cfg 0.1 \ --use_rope \ --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/image3d_rope_256p_zp_umt5/checkpoint-146000/model_ema/diffusion_pytorch_model.safetensors" \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image3d_256p.sh b/scripts/text_condition/npu/train_image3d_256p.sh index 61b6d860f..375bfd201 100644 --- a/scripts/text_condition/npu/train_image3d_256p.sh +++ b/scripts/text_condition/npu/train_image3d_256p.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ diff --git a/scripts/text_condition/npu/train_image3d_256p_rope.sh b/scripts/text_condition/npu/train_image3d_256p_rope.sh index 942af012b..34792dcfc 100644 --- a/scripts/text_condition/npu/train_image3d_256p_rope.sh +++ b/scripts/text_condition/npu/train_image3d_256p_rope.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-ROPE-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ diff --git a/scripts/text_condition/npu/train_image3d_480p.sh b/scripts/text_condition/npu/train_image3d_480p.sh index 22bbd0429..67e51a1dc 100644 --- a/scripts/text_condition/npu/train_image3d_480p.sh +++ b/scripts/text_condition/npu/train_image3d_480p.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ @@ -31,7 +31,7 @@ accelerate launch \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ + --learning_rate=4e-5 \ --lr_scheduler="cosine" \ --seed=10 \ --lr_warmup_steps=500 \ @@ -46,8 +46,8 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ - --tile_overlap_factor 0.125 \ --enable_tiling \ + --tile_overlap_factor 0.125 \ --noise_offset 0.02 \ --pretrained "/home/image_data/checkpoints/image3d_256p_zp_umt5_from_initial_layer_40_head_16/checkpoint-176000/model_ema/diffusion_pytorch_model.safetensors" \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_image3d_480p_rope.sh b/scripts/text_condition/npu/train_image3d_480p_rope.sh index cb456b225..138d6d011 100644 --- a/scripts/text_condition/npu/train_image3d_480p_rope.sh +++ b/scripts/text_condition/npu/train_image3d_480p_rope.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-ROPE-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ @@ -31,7 +31,7 @@ accelerate launch \ --dataloader_num_workers 20 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ + --learning_rate=4e-5 \ --lr_scheduler="cosine" \ --seed=10 \ --lr_warmup_steps=500 \ @@ -46,10 +46,9 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ + --enable_tiling \ --tile_overlap_factor 0.125 \ --use_rope \ - --enable_tiling \ --noise_offset 0.02 \ - --pretrained "/home/image_data/checkpoints/image3d_rope_256p_zp_umt5/checkpoint-130000/model_ema/diffusion_pytorch_model.safetensors" \ + --pretrained "/home/image_data/checkpoints/image3d_rope_256p_zp_umt5/checkpoint-146000/model_ema/diffusion_pytorch_model.safetensors" \ --resume_from_checkpoint="latest" - diff --git a/scripts/text_condition/npu/train_image_256x256.sh b/scripts/text_condition/npu/train_image_256x256.sh deleted file mode 100644 index deb6d6c77..000000000 --- a/scripts/text_condition/npu/train_image_256x256.sh +++ /dev/null @@ -1,51 +0,0 @@ -export PROJECT=$PROJECT_NAME -WEIGHT_PATH="/home/opensora/pre_weights/" -env -export WANDB_MODE='offline' -export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE -export HCCL_ALGO="level0:NA;level1:H-D_R" - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ - --machine_rank=${MACHINE_RANK} \ - --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ - --cache_dir "../cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "${WEIGHT_PATH}/test140k/" \ - --video_data "./scripts/train_data/video_data_on_npu.txt" \ - --image_data "./scripts/train_data/image_data_on_npu.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 256 \ - --max_width 256 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=8 \ - --dataloader_num_workers 20 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="cosine" \ - --seed=10 \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=1000 \ - --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_imageuditultra_480p_new.sh b/scripts/text_condition/npu/train_imageuditultra_480p_new.sh index ff0c81f4d..f74b95172 100644 --- a/scripts/text_condition/npu/train_imageuditultra_480p_new.sh +++ b/scripts/text_condition/npu/train_imageuditultra_480p_new.sh @@ -11,7 +11,7 @@ accelerate launch \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ --model UDiTUltraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ diff --git a/scripts/text_condition/npu/train_video3d_nx240p.sh b/scripts/text_condition/npu/train_video3d_nx480p.sh similarity index 67% rename from scripts/text_condition/npu/train_video3d_nx240p.sh rename to scripts/text_condition/npu/train_video3d_nx480p.sh index 137cb7ca4..a691fff7c 100644 --- a/scripts/text_condition/npu/train_video3d_nx240p.sh +++ b/scripts/text_condition/npu/train_video3d_nx480p.sh @@ -10,8 +10,8 @@ accelerate launch \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ @@ -20,24 +20,24 @@ accelerate launch \ --image_data "./scripts/train_data/image_data_on_npu.txt" \ --sample_rate 1 \ --num_frames ${NUM_FRAME} \ - --max_height 240 \ - --max_width 320 \ + --max_height 480 \ + --max_width 640 \ --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ + --dataloader_num_workers 8 \ + --gradient_accumulation_steps=8 \ --max_train_steps=1000000 \ - --learning_rate=1e-4 \ + --learning_rate=4e-5 \ --lr_scheduler="cosine" \ - --lr_warmup_steps=0 \ + --seed=10 \ + --lr_warmup_steps=500 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --seed=10 \ - --checkpointing_steps=1000 \ + --checkpointing_steps=2000 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ @@ -46,4 +46,9 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --use_rope \ + --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/image3d_rope_480p_from_pretrain/checkpoint-14000/model_ema/diffusion_pytorch_model.safetensors" \ --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/npu/train_video3d_sp_nx240p.sh b/scripts/text_condition/npu/train_video3d_sp_nx480p.sh similarity index 77% rename from scripts/text_condition/npu/train_video3d_sp_nx240p.sh rename to scripts/text_condition/npu/train_video3d_sp_nx480p.sh index 7230edc9c..f5b7c9777 100644 --- a/scripts/text_condition/npu/train_video3d_sp_nx240p.sh +++ b/scripts/text_condition/npu/train_video3d_sp_nx480p.sh @@ -10,8 +10,8 @@ accelerate launch \ --machine_rank=${MACHINE_RANK} \ --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-L/122 \ - --text_encoder_name ${WEIGHT_PATH}/google/umt5-xxl \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --cache_dir "../cache_dir" \ --dataset t2v \ --ae CausalVAEModel_4x8x8 \ @@ -23,8 +23,8 @@ accelerate launch \ --max_height 480 \ --max_width 640 \ --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=1 \ @@ -33,11 +33,11 @@ accelerate launch \ --max_train_steps=1000000 \ --learning_rate=4e-5 \ --lr_scheduler="cosine" \ - --lr_warmup_steps=0 \ + --seed=10 \ + --lr_warmup_steps=500 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --seed=10 \ - --checkpointing_steps=500 \ + --checkpointing_steps=2000 \ --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ --allow_tf32 \ --model_max_length 512 \ @@ -46,8 +46,11 @@ accelerate launch \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --use_rope \ --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/image3d_rope_480p_from_pretrain/checkpoint-14000/model_ema/diffusion_pytorch_model.safetensors" \ --resume_from_checkpoint="latest" \ - --enable_tiling \ --sp_size 8 \ --train_sp_batch_size 1 diff --git a/scripts/text_condition/npu/train_videoudit_nx480p.sh b/scripts/text_condition/npu/train_videoudit_nx480p.sh index 680301276..167f3cb45 100644 --- a/scripts/text_condition/npu/train_videoudit_nx480p.sh +++ b/scripts/text_condition/npu/train_videoudit_nx480p.sh @@ -49,4 +49,5 @@ accelerate launch \ --cfg 0.1 \ --noise_offset 0.02 \ --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" diff --git a/scripts/train_data/image_data_on_npu.txt b/scripts/train_data/image_data_on_npu.txt index fcaed27b3..a65548d77 100644 --- a/scripts/train_data/image_data_on_npu.txt +++ b/scripts/train_data/image_data_on_npu.txt @@ -1,2 +1,4 @@ /home/local_dataset_6t/image_data_obs/mj_unzip_files,/home/opensora/captions/linbin_captions/tuzhan_mj_4615265.json -/home/local_dataset_6t/image_data_obs/mj_unzip_files,/home/opensora/captions/tuzhan_mj_4615265_cn.json \ No newline at end of file +/home/local_dataset_6t/image_data_obs/mj_unzip_files,/home/opensora/captions/tuzhan_mj_4615265_cn.json +/home/local_dataset_6t/image_data_obs/sa_unzip_files,/home/opensora/captions/linbin_captions/sam_image_11185255.json +/home/local_dataset_6t/image_data_obs/images/,/home/opensora/captions/linbin_captions/human_images_162094.json \ No newline at end of file From e8075184d9daa16ff7f75be6ebcdebd1ab25d3f7 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Mon, 24 Jun 2024 13:24:37 +0000 Subject: [PATCH 064/134] video ip --- opensora/dataset/videoip_datasets.py | 314 ++++++ opensora/models/diffusion/latte/modules.py | 206 ---- .../diffusion/latte/transformer_qformer.py | 66 +- opensora/models/diffusion/latte/videoip.py | 236 +++++ opensora/train/train_videoip.py | 955 ++++++++++++++++++ 5 files changed, 1547 insertions(+), 230 deletions(-) create mode 100644 opensora/dataset/videoip_datasets.py create mode 100644 opensora/models/diffusion/latte/videoip.py create mode 100644 opensora/train/train_videoip.py diff --git a/opensora/dataset/videoip_datasets.py b/opensora/dataset/videoip_datasets.py new file mode 100644 index 000000000..27fed8c22 --- /dev/null +++ b/opensora/dataset/videoip_datasets.py @@ -0,0 +1,314 @@ +import time +import traceback + + +import glob +import json +import os, io, csv, math, random +import numpy as np +import torchvision +from einops import rearrange +from decord import VideoReader +from os.path import join as opj + +import torch +import torchvision.transforms as transforms +from torch.utils.data.dataset import Dataset +from torch.utils.data import DataLoader, Dataset, get_worker_info +from tqdm import tqdm +from PIL import Image + +from opensora.utils.dataset_utils import DecordInit +from opensora.utils.utils import text_preprocessing + + +def filter_json_by_existed_files(directory, data, postfix=".mp4"): + # 构建搜索模式,以匹配指定后缀的文件 + pattern = os.path.join(directory, '**', f'*{postfix}') + mp4_files = glob.glob(pattern, recursive=True) # 使用glob查找所有匹配的文件 + + # 使用文件的绝对路径构建集合 + mp4_files_set = set(os.path.abspath(path) for path in mp4_files) + + # 过滤数据条目,只保留路径在mp4文件集合中的条目 + filtered_items = [item for item in data if item['path'] in mp4_files_set] + + return filtered_items + + +def random_video_noise(t, c, h, w): + vid = torch.rand(t, c, h, w) * 255.0 + vid = vid.to(torch.uint8) + return vid + + +class VideoIP_dataset(Dataset): + def __init__(self, args, transform, temporal_sample, tokenizer): + self.image_data = args.image_data + self.video_data = args.video_data + self.num_frames = args.num_frames + self.use_image_num = args.use_image_num + self.use_img_from_vid = args.use_img_from_vid + self.transform = transform + self.temporal_sample = temporal_sample + self.tokenizer = tokenizer + self.model_max_length = args.model_max_length + self.cfg = args.cfg + self.v_decoder = DecordInit() + + self.support_Chinese = True + if not ('mt5' in args.text_encoder_name): + self.support_Chinese = False + + if self.num_frames != 1: + self.vid_cap_list = self.get_vid_cap_list() + if self.use_image_num != 0 and not self.use_img_from_vid: + self.img_cap_list = self.get_img_cap_list() + else: + self.img_cap_list = [] + else: + self.img_cap_list = self.get_img_cap_list() + self.vid_cap_list = [] + + # inpaint + # The proportion of executing the i2v task. + self.i2v_ratio = args.i2v_ratio + self.transition_ratio = args.transition_ratio + self.clear_video_ratio = args.clear_video_ratio + assert self.i2v_ratio + self.transition_ratio + self.clear_video_ratio < 1, 'The sum of i2v_ratio, transition_ratio and clear video ratio should be less than 1.' + + print(f"video length: {len(self.vid_cap_list)}") + print(f"image length: {len(self.img_cap_list)}") + + def set_checkpoint(self, n_used_elements): + self.n_used_elements = n_used_elements + + def __len__(self): + if self.num_frames != 1: + return len(self.vid_cap_list) + else: + return len(self.img_cap_list) + + def __getitem__(self, idx): + + video_data, image_data = {}, {} + try: + if self.num_frames != 1: + video_data = self.get_video(idx) + if self.use_image_num != 0: + if self.use_img_from_vid: + image_data = self.get_image_from_video(video_data) + else: + image_data = self.get_image(idx) + else: + image_data = self.get_image(idx) # 1 frame video as image + return dict(video_data=video_data, image_data=image_data) + except Exception as e: + # print(f'Error with {e}') + # 打印异常堆栈 + if idx in self.vid_cap_list: + print(f"Caught an exception! {self.vid_cap_list[idx]}") + # traceback.print_exc() + # traceback.print_stack() + return self.__getitem__(random.randint(0, self.__len__() - 1)) + + def get_video(self, idx): + + video_path = self.vid_cap_list[idx]['path'] + assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" + frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None + video = self.decord_read(video_path, frame_idx) + + video = self.transform(video) # T C H W -> T C H W + + # video = torch.rand(221, 3, 480, 640) + + video = video.transpose(0, 1) # T C H W -> C T H W + text = self.vid_cap_list[idx]['cap'] + + # inpaint + inpaint_cond_data = self.get_mask_masked_video(video) + masked_video = inpaint_cond_data['masked_video'] + video = torch.cat([video, masked_video], dim=0) + + text = text_preprocessing(text, support_Chinese=self.support_Chinese) if random.random() > self.cfg else "" + text_tokens_and_mask = self.tokenizer( + text, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids = text_tokens_and_mask['input_ids'] + cond_mask = text_tokens_and_mask['attention_mask'] + return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) + + def get_image_from_video(self, video_data): + select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) + assert self.num_frames >= self.use_image_num + image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] + input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l + cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) + + def get_mask_masked_video(self, video): + mask = torch.zeros_like(video) + + rand_num = random.random() + # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. + if rand_num < self.i2v_ratio: + mask = 1 - mask + mask[:, 0, ...] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio: + mask = 1 - mask + mask[:, 0, ...] = 0 + mask[:, -1, ...] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio + self.clear_video_ratio: + pass + else: + idx_to_select = random.randint(1, self.num_frames - 1) + selected_indices = random.sample(range(1, self.num_frames), idx_to_select) + mask[:, selected_indices, ...] = 1 + + masked_video = video * (mask < 0.5) + return dict(mask=mask, masked_video=masked_video) + + def get_image(self, idx): + idx = idx % len(self.img_cap_list) # out of range + image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] + + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + + for i in image: + assert not torch.any(torch.isnan(i)), 'before transform0' + image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + for i in image: + assert not torch.any(torch.isnan(i)), 'before transform1' + image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + + for i in image: + assert not torch.any(torch.isnan(i)), 'after transform' + # image = [torch.rand(1, 3, 480, 640) for i in image_data] + image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] + + caps = [i['cap'] for i in image_data] + text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] + input_ids, cond_mask = [], [] + for t in text: + t = t if random.random() > self.cfg else "" + text_tokens_and_mask = self.tokenizer( + t, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids.append(text_tokens_and_mask['input_ids']) + cond_mask.append(text_tokens_and_mask['attention_mask']) + input_ids = torch.cat(input_ids) # self.use_image_num, l + cond_mask = torch.cat(cond_mask) # self.use_image_num, l + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) + + def decord_read(self, path, frame_idx=None): + decord_vr = self.v_decoder(path) + total_frames = len(decord_vr) + # Sampling video frames + if frame_idx is None: + start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) + else: + start_frame_ind, end_frame_ind = frame_idx.split(':') + # start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) + start_frame_ind, end_frame_ind = int(start_frame_ind), int(start_frame_ind) + self.num_frames + # assert end_frame_ind - start_frame_ind >= self.num_frames + frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) + # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) + + video_data = decord_vr.get_batch(frame_indice).asnumpy() + video_data = torch.from_numpy(video_data) + video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) + return video_data + + def read_jsons(self, data, postfix=".jpg"): + cap_lists = [] + with open(data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + for folder, anno in folder_anno: + with open(anno, 'r') as f: + sub_list = json.load(f) + print(f'Building {anno}...') + for i in tqdm(range(len(sub_list))): + sub_list[i]['path'] = opj(folder, sub_list[i]['path']) + cap_lists += sub_list + return cap_lists + + def get_img_cap_list(self): + use_image_num = self.use_image_num if self.use_image_num != 0 else 1 + img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") + img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + + return img_cap_lists[:-1] # drop last to avoid error length + + def get_vid_cap_list(self): + vid_cap_lists = self.read_jsons(self.video_data, postfix=".mp4") + + return vid_cap_lists + + +if __name__ == "__main__": + + from torchvision import transforms + from torchvision.transforms import Lambda + from .transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop + + from transformers import AutoTokenizer + + class Args: + def __init__(self): + self.video_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' + self.image_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' + self.num_frames = 65 + self.use_image_num = 4 + self.use_img_from_vid = False + self.model_max_length = 300 + self.cfg = 0.1 + self.i2v_ratio = 0.3 + self.transition_ratio = 0.3 + self.clear_video_ratio = 0.3 + self.max_image_size = 512 + self.sample_rate = 1 + self.text_encoder_name = "DeepFloyd/t5-v1_1-xxl" + self.cache_dir = "./cache_dir" + + args = Args() + resize = [CenterCropResizeVideo((args.max_image_size, args.max_image_size))] + + temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + Lambda(lambda x: 2. * x - 1.) + ]) + + dataset = VideoIP_dataset(args, transform, temporal_sample, tokenizer) + + print(len(dataset)) + video_data = dataset[0]['video_data'] + image_data = dataset[0]['image_data'] + video, video_input_ids, video_cond_mask = video_data['video'], video_data['input_ids'], video_data['cond_mask'] + image, image_input_ids, image_cond_mask = image_data['image'], image_data['input_ids'], image_data['cond_mask'] + print(video.shape) # 3 * C, F, H, W + print(video_input_ids.shape) # 1 D + print(video_cond_mask.shape) # 1 D + print(image[0].shape) + print(image_input_ids.shape) + print(image_cond_mask.shape) + print(video_cond_mask) + print(image_cond_mask) diff --git a/opensora/models/diffusion/latte/modules.py b/opensora/models/diffusion/latte/modules.py index 94cab822c..5ce7b9dcc 100644 --- a/opensora/models/diffusion/latte/modules.py +++ b/opensora/models/diffusion/latte/modules.py @@ -35,10 +35,6 @@ xformers = None -def zero_module(module): - for p in module.parameters(): - nn.init.zeros_(p) - return module def get_2d_sincos_pos_embed( embed_dim, grid_size, cls_token=False, extra_tokens=0, interpolation_scale=1.0, base_size=16, @@ -1029,208 +1025,6 @@ def __call__( - key = attn.to_k(encoder_hidden_states, *args) - value = attn.to_v(encoder_hidden_states, *args) - - inner_dim = key.shape[-1] - head_dim = inner_dim // attn.heads - if npu_config is not None and npu_config.on_npu: - if npu_config.enable_FA and query.dtype == torch.float32: - dtype = torch.bfloat16 - else: - dtype = None - - with set_run_dtype(query, dtype): - query, key, value = npu_config.set_current_run_dtype([query, key, value]) - hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", - head_dim, attn.heads) - - hidden_states = npu_config.restore_dtype(hidden_states) - else: - query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - - key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - - if self.use_rope: - # require the shape of (batch_size x nheads x ntokens x dim) - if position_q.ndim == 3: - query = self.rope2d(query, position_q) - elif position_q.ndim == 2: - query = self.rope1d(query, position_q) - else: - raise NotImplementedError - if position_k.ndim == 3: - key = self.rope2d(key, position_k) - elif position_k.ndim == 2: - key = self.rope1d(key, position_k) - else: - raise NotImplementedError - - # the output of sdp = (batch, num_heads, seq_len, head_dim) - # TODO: add support for attn.scale when we move to Torch 2.1 - if self.attention_mode == 'flash': - assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): - hidden_states = F.scaled_dot_product_attention( - query, key, value, dropout_p=0.0, is_causal=False - ) - elif self.attention_mode == 'xformers': - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): - hidden_states = F.scaled_dot_product_attention( - query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False - ) - elif self.attention_mode == 'math': - hidden_states = F.scaled_dot_product_attention( - query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False - ) - else: - raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') - hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) - hidden_states = hidden_states.to(query.dtype) - - # linear proj - hidden_states = attn.to_out[0](hidden_states, *args) - # dropout - hidden_states = attn.to_out[1](hidden_states) - - if input_ndim == 4: - hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) - - if attn.residual_connection: - hidden_states = hidden_states + residual - - hidden_states = hidden_states / attn.rescale_output_factor - - return hidden_states - -# IP Adapter -class VideoIPAttnProcessor: - r""" - Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). - """ - - def __init__( - self, - dim=1152, - attention_mode='xformers', - use_rope=False, - rope_scaling=None, - compress_kv_factor=None, - - num_latent_tokens=128, - use_image_num=0, - dropout=0.0, - ): - self.dim = dim - self.attention_mode = attention_mode - self.use_rope = use_rope - self.rope_scaling = rope_scaling - self.compress_kv_factor = compress_kv_factor - - self.use_image_num = use_image_num - - if self.use_rope: - self._init_rope() - - if not hasattr(F, "scaled_dot_product_attention"): - raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") - - self.to_k_vip = nn.Linear(dim, dim, bias=False) - self.to_v_vip = nn.Linear(dim, dim, bias=False) - - self.to_out_vip = nn.ModuleList( - [ - zero_module(nn.Linear(dim, dim, bias=False)), - nn.Dropout(dropout), - ] - ) - - - def _init_rope(self): - if self.rope_scaling is None: - self.rope2d = RoPE2D() - self.rope1d = RoPE1D() - else: - scaling_type = self.rope_scaling["type"] - scaling_factor_2d = self.rope_scaling["factor_2d"] - scaling_factor_1d = self.rope_scaling["factor_1d"] - if scaling_type == "linear": - self.rope2d = LinearScalingRoPE2D(scaling_factor=scaling_factor_2d) - self.rope1d = LinearScalingRoPE1D(scaling_factor=scaling_factor_1d) - else: - raise ValueError(f"Unknown RoPE scaling type {scaling_type}") - - def __call__( - self, - attn: Attention, - hidden_states: torch.FloatTensor, - encoder_hidden_states: Optional[torch.FloatTensor] = None, - attention_mask: Optional[torch.FloatTensor] = None, - temb: Optional[torch.FloatTensor] = None, - scale: float = 1.0, - position_q: Optional[torch.LongTensor] = None, - position_k: Optional[torch.LongTensor] = None, - last_shape: Tuple[int] = None, - ) -> torch.FloatTensor: - - residual = hidden_states - - args = () if USE_PEFT_BACKEND else (scale,) - - if attn.spatial_norm is not None: - hidden_states = attn.spatial_norm(hidden_states, temb) - - input_ndim = hidden_states.ndim - - if input_ndim == 4: - batch_size, channel, height, width = hidden_states.shape - hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) - - - if self.compress_kv_factor is not None: - batch_size = hidden_states.shape[0] - if len(last_shape) == 2: - encoder_hidden_states = hidden_states.permute(0, 2, 1).reshape(batch_size, self.dim, *last_shape) - encoder_hidden_states = attn.sr(encoder_hidden_states).reshape(batch_size, self.dim, -1).permute(0, 2, 1) - elif len(last_shape) == 1: - encoder_hidden_states = hidden_states.permute(0, 2, 1) - if last_shape[0] % 2 == 1: - first_frame_pad = encoder_hidden_states[:, :, :1].repeat((1, 1, attn.kernel_size - 1)) - encoder_hidden_states = torch.concatenate((first_frame_pad, encoder_hidden_states), dim=2) - encoder_hidden_states = attn.sr(encoder_hidden_states).permute(0, 2, 1) - else: - raise NotImplementedError(f'NotImplementedError with last_shape {last_shape}') - - encoder_hidden_states = attn.norm(encoder_hidden_states) - - batch_size, sequence_length, _ = ( - hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape - ) - - # import ipdb;ipdb.set_trace() - if attention_mask is not None: - attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) - # scaled_dot_product_attention expects attention_mask shape to be - # (batch, heads, source_length, target_length) - if npu_config is not None and npu_config.on_npu: - attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) - else: - attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) - - if attn.group_norm is not None: - hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) - - args = () if USE_PEFT_BACKEND else (scale,) - query = attn.to_q(hidden_states, *args) - - if encoder_hidden_states is None: - encoder_hidden_states = hidden_states - elif attn.norm_cross: - encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) - - - key = attn.to_k(encoder_hidden_states, *args) value = attn.to_v(encoder_hidden_states, *args) diff --git a/opensora/models/diffusion/latte/transformer_qformer.py b/opensora/models/diffusion/latte/transformer_qformer.py index 185f7b3ac..2cfbc559d 100644 --- a/opensora/models/diffusion/latte/transformer_qformer.py +++ b/opensora/models/diffusion/latte/transformer_qformer.py @@ -3,7 +3,7 @@ import torch.nn as nn from diffusers.models.attention import FeedForward -from diffusers.models.attention_processor import Attention +from .modules import Attention class BasicQFormerBlock(nn.Module): def __init__( @@ -18,6 +18,9 @@ def __init__( norm_elementwise_affine=True, norm_eps=1e-5, activation_fn="gelu", + attention_mode="xformers", + use_rope=False, + rope_scaling=None, final_dropout=False, ff_inner_dim=None, ff_bias=False, @@ -37,8 +40,12 @@ def __init__( bias=attention_bias, cross_attention_dim=dim, upcast_attention=upcast_attention, + attention_mode=attention_mode, + use_rope=use_rope, + rope_scaling=rope_scaling, out_bias=attention_out_bias ) + self.only_visual_attention = only_visual_attention if not only_visual_attention: # cross attn if cross_attention_dim is not None, otherwise self attn @@ -47,14 +54,19 @@ def __init__( self.attn2 = Attention( query_dim=dim, + cross_attention_dim=dim, heads=num_attention_heads, dim_head=attention_head_dim, dropout=dropout, bias=attention_bias, - cross_attention_dim=dim, upcast_attention=upcast_attention, - out_bias=attention_out_bias - ) + attention_mode=attention_mode, + use_rope=False, + ) + else: + self.norm2_latents = None + self.norm2_encoder_hidden_states = None + self.attn2 = None self.norm3 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) self.ff = FeedForward( @@ -66,7 +78,17 @@ def __init__( bias=ff_bias, ) - def forward(self, latents, visual_context, encoder_hidden_states=None, attention_mask=None): + def forward( + self, + latents, + visual_context, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + position_q=None, + position_k=None, + last_shape=None, + ): ''' params: latents: queries of q former @@ -88,6 +110,9 @@ def forward(self, latents, visual_context, encoder_hidden_states=None, attention norm_latents, encoder_hidden_states=kv_input, attention_mask=attention_mask, + position_q=position_q, + position_k=position_k, + last_shape=last_shape, ) latents = latents + attn_output @@ -104,7 +129,10 @@ def forward(self, latents, visual_context, encoder_hidden_states=None, attention attn_output = self.attn2( norm_latents, encoder_hidden_states=kv_input, - attention_mask=attention_mask + attention_mask=encoder_attention_mask, + position_q=None, + position_k=None, + last_shape=None, ) latents = latents + attn_output @@ -130,18 +158,15 @@ def __init__( num_attention_heads=16, attention_head_dim=64, dropout=0.0, - max_seq_length=257, - apply_pos_emb=False, only_visual_attention=True, encoder_hidden_states_dim=None, + use_rope=False, + rope_scaling=None, + attention_mode="xformers", + compress_kv_factor=1, ): super().__init__() - self.apply_pos_emb = apply_pos_emb - self.pos_emb_visual_context = nn.Embedding(max_seq_length, visual_context_dim) if apply_pos_emb else None - if not only_visual_attention: - self.pos_emb_encoder_hidden_states = nn.Embedding(max_seq_length, encoder_hidden_states_dim) if apply_pos_emb else None - self.only_visual_attention = only_visual_attention self.latents = nn.Parameter(torch.randn(1, num_queries, dim) / dim ** 0.5) @@ -162,6 +187,10 @@ def __init__( attention_head_dim=attention_head_dim, dropout=dropout, only_visual_attention=only_visual_attention, + attention_mode=attention_mode, + use_rope=use_rope, + rope_scaling=rope_scaling, + compress_kv_factor=(compress_kv_factor, compress_kv_factor) if d >= num_layers // 2 and compress_kv_factor != 1 else None, # follow pixart-sigma, apply in second-half layers ) ) @@ -169,17 +198,6 @@ def __init__( def forward(self, visual_context, encoder_hidden_states=None): - def add_pos_emb(x): - n, device = x.shape[1], x.device - pos_emb = self.pos_emb(torch.arange(n, device=device)) - x = x + pos_emb - return x - - if self.apply_pos_emb: - visual_context = add_pos_emb(visual_context) - if not self.only_visual_attention: - encoder_hidden_states = add_pos_emb(encoder_hidden_states) - b = visual_context.shape[0] latents = self.latents.repeat(b, 1, 1) diff --git a/opensora/models/diffusion/latte/videoip.py b/opensora/models/diffusion/latte/videoip.py new file mode 100644 index 000000000..aad9696d4 --- /dev/null +++ b/opensora/models/diffusion/latte/videoip.py @@ -0,0 +1,236 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +from opensora.models.diffusion.utils.pos_embed import RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D + +from opensora.models.diffusion.latte.modules import Attention + +from typing import Any, Dict, Optional, Tuple, Callable +from diffusers.utils import USE_PEFT_BACKEND, BaseOutput, deprecate, is_xformers_available + +def zero_module(module): + for p in module.parameters(): + nn.init.zeros_(p) + return module + +# IP Adapter +class VideoIPAttnProcessor: + r""" + Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). + """ + + def __init__( + self, + dim=1152, + attention_mode='xformers', + use_rope=False, + rope_scaling=None, + compress_kv_factor=None, + + num_vip_tokens=128, + vip_scale=1.0, + dropout=0.0, + ): + self.dim = dim + self.attention_mode = attention_mode + self.use_rope = use_rope + self.rope_scaling = rope_scaling + self.compress_kv_factor = compress_kv_factor + + if self.use_rope: + self._init_rope() + + if not hasattr(F, "scaled_dot_product_attention"): + raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + self.to_k_vip = nn.Linear(dim, dim, bias=False) + self.to_v_vip = nn.Linear(dim, dim, bias=False) + + self.to_out_vip = nn.ModuleList( + [ + zero_module(nn.Linear(dim, dim, bias=False)), + nn.Dropout(dropout), + ] + ) + + self.num_vip_tokens = num_vip_tokens + self.vip_scale = vip_scale + + def _init_rope(self): + if self.rope_scaling is None: + self.rope2d = RoPE2D() + self.rope1d = RoPE1D() + else: + scaling_type = self.rope_scaling["type"] + scaling_factor_2d = self.rope_scaling["factor_2d"] + scaling_factor_1d = self.rope_scaling["factor_1d"] + if scaling_type == "linear": + self.rope2d = LinearScalingRoPE2D(scaling_factor=scaling_factor_2d) + self.rope1d = LinearScalingRoPE1D(scaling_factor=scaling_factor_1d) + else: + raise ValueError(f"Unknown RoPE scaling type {scaling_type}") + + def __call__( + self, + attn: Attention, + hidden_states: torch.FloatTensor, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + temb: Optional[torch.FloatTensor] = None, + scale: float = 1.0, + position_q: Optional[torch.LongTensor] = None, + position_k: Optional[torch.LongTensor] = None, + last_shape: Tuple[int] = None, + ) -> torch.FloatTensor: + + residual = hidden_states + + args = () if USE_PEFT_BACKEND else (scale,) + + if attn.spatial_norm is not None: + hidden_states = attn.spatial_norm(hidden_states, temb) + + input_ndim = hidden_states.ndim + + if input_ndim == 4: + batch_size, channel, height, width = hidden_states.shape + hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) + + + if self.compress_kv_factor is not None: + batch_size = hidden_states.shape[0] + if len(last_shape) == 2: + encoder_hidden_states = hidden_states.permute(0, 2, 1).reshape(batch_size, self.dim, *last_shape) + encoder_hidden_states = attn.sr(encoder_hidden_states).reshape(batch_size, self.dim, -1).permute(0, 2, 1) + elif len(last_shape) == 1: + encoder_hidden_states = hidden_states.permute(0, 2, 1) + if last_shape[0] % 2 == 1: + first_frame_pad = encoder_hidden_states[:, :, :1].repeat((1, 1, attn.kernel_size - 1)) + encoder_hidden_states = torch.concatenate((first_frame_pad, encoder_hidden_states), dim=2) + encoder_hidden_states = attn.sr(encoder_hidden_states).permute(0, 2, 1) + else: + raise NotImplementedError(f'NotImplementedError with last_shape {last_shape}') + + encoder_hidden_states = attn.norm(encoder_hidden_states) + + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + + # import ipdb;ipdb.set_trace() + if attention_mask is not None: + attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + + if attn.group_norm is not None: + hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) + + args = () if USE_PEFT_BACKEND else (scale,) + query = attn.to_q(hidden_states, *args) + + if encoder_hidden_states is None: + encoder_hidden_states = hidden_states + else: + # get encoder_hidden_states, ip_hidden_states + end_pos = encoder_hidden_states.shape[1] - self.num_vip_tokens + encoder_hidden_states, vip_hidden_states = ( + encoder_hidden_states[:, :end_pos, :], + encoder_hidden_states[:, end_pos:, :], + ) + if attn.norm_cross: + encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + + key = attn.to_k(encoder_hidden_states, *args) + value = attn.to_v(encoder_hidden_states, *args) + + vip_key = self.to_k_vip(vip_hidden_states) + vip_value = self.to_v_vip(vip_hidden_states) + + inner_dim = key.shape[-1] + head_dim = inner_dim // attn.heads + + + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + vip_key = vip_key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + vip_value = vip_value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + if position_q.ndim == 3: + query = self.rope2d(query, position_q) + elif position_q.ndim == 2: + query = self.rope1d(query, position_q) + else: + raise NotImplementedError + if position_k.ndim == 3: + key = self.rope2d(key, position_k) + vip_key = self.rope2d(vip_key, position_k) + elif position_k.ndim == 2: + key = self.rope1d(key, position_k) + vip_key = self.rope1d(vip_key, position_k) + else: + raise NotImplementedError + + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + if self.attention_mode == 'flash': + assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + + # linear proj + hidden_states = attn.to_out[0](hidden_states, *args) + # dropout + hidden_states = attn.to_out[1](hidden_states) + + vip_hidden_states = vip_hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + vip_hidden_states = vip_hidden_states.to(query.dtype) + + # linear proj + vip_hidden_states = self.to_out_vip[0](vip_hidden_states) + # dropout + vip_hidden_states = self.to_out_vip[1](vip_hidden_states) + + hidden_states = hidden_states + self.vip_scale * vip_hidden_states + + if input_ndim == 4: + hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) + + if attn.residual_connection: + hidden_states = hidden_states + residual + + hidden_states = hidden_states / attn.rescale_output_factor + + return hidden_states + diff --git a/opensora/train/train_videoip.py b/opensora/train/train_videoip.py new file mode 100644 index 000000000..9a77cc235 --- /dev/null +++ b/opensora/train/train_videoip.py @@ -0,0 +1,955 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +import logging +import math +import os +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + pass +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import DistributedType, ProjectConfiguration, set_seed +from huggingface_hub import create_repo +from packaging import version +from tqdm.auto import tqdm +from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.utils.dataset_utils import Collate +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.sample.pipeline_opensora import OpenSoraPipeline + + +# for validation +from PIL import Image +from torchvision import transforms +from torchvision.transforms import Lambda +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +from opensora.sample.pipeline_inpaint import OpenSoraInpaintPipeline + + + +import glob + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + + +def save_video(video, save_path='output_video.mp4', fps=24): + import cv2 + + frame_count, height, width, channels = video.shape + + fourcc = cv2.VideoWriter_fourcc(*'mp4v') + out = cv2.VideoWriter(save_path, fourcc, fps, (width, height)) + + for i in range(frame_count): + frame = video[i].cpu().numpy() + frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) + out.write(frame) + +@torch.inference_mode() +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): + + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + prompt_file = os.path.join(validation_dir, "prompt.txt") + + with open(prompt_file, 'r') as f: + validation_prompt = f.readlines() + + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break + + resize = [CenterCropResizeVideo((args.max_height, args.max_width)),] + norm_fun = Lambda(lambda x: 2. * x - 1.) + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + + logger.info(f"Running {'normal' if not ema else 'ema'} validation....\n") + model = accelerator.unwrap_model(model) + + scheduler = PNDMScheduler() + pipeline = OpenSoraInpaintPipeline( + vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model + ).to(device=accelerator.device) + + def preprocess_images(images): + if len(images) == 1: + condition_images_indices = [0] + elif len(images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in images] + condition_images = [torch.from_numpy(np.copy(np.array(image))) for image in condition_images] + condition_images = [rearrange(image, 'h w c -> c h w').unsqueeze(0) for image in condition_images] + condition_images = [transform(image).to(accelerator.device, dtype=torch.float32) for image in condition_images] + return dict(condition_images=condition_images, condition_images_indices=condition_images_indices) + + videos = [] + for prompt, images in zip(validation_prompt, validation_images_list): + if not isinstance(images, list): + images = [images] + logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) + + pre_results = preprocess_images(images) + condition_images = pre_results['condition_images'] + condition_images_indices = pre_results['condition_images_indices'] + + video = pipeline( + prompt=prompt, + condition_images=condition_images, + condition_images_indices=condition_images_indices, + num_frames=args.num_frames, + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + ).images + videos.append(video[0]) + # import ipdb;ipdb.set_trace() + + # Save the generated videos + save_dir = os.path.join(validation_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") + os.makedirs(save_dir, exist_ok=True) + for idx, video in enumerate(videos): + save_video(video, os.path.join(save_dir, f"video_{idx:06d}.mp4")) + + videos = torch.stack(videos).numpy() + videos = rearrange(videos, 'b t h w c -> b t c h w') + + for tracker in accelerator.trackers: + if tracker.name == "wandb": + import wandb + + logs = { + f"{'ema_' if ema else ''}validation_videos": [ + wandb.Video(video, caption=f"{i}: {prompt}", fps=24) + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) + ], + } + + tracker.log(logs, step=global_step) + + print("delete validation pipeline...") + del pipeline + gc.collect() + torch.cuda.empty_cache() + + + + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss + + +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + + logging_dir = Path(args.output_dir, args.logging_dir) + + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) + npu_config.seed_everything() + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if accelerator.is_main_process: + print("--------------------------training args--------------------------") + print(args) + print("-----------------------------------------------------------------") + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + kwargs = {} + ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = args.num_frames // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae] * 2, # 因为要加载预训练权重,所以这里out_channels仍然设置为2倍 + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + downsampler=args.downsampler, + compress_kv_factor=args.compress_kv_factor, + use_rope=args.use_rope, + model_max_length=args.model_max_length, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # # use pretrained model? + if args.pretrained: + model.custom_load_state_dict(args.pretrained) + + + # Freeze vae and text encoders. + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + # Set model as trainable. + model.train() + + noise_scheduler = DDPMScheduler(rescale_betas_zero_snr=args.zero_terminal_snr) + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + ae.vae.to(accelerator.device, dtype=torch.float32) + # ae.vae.to(accelerator.device, dtype=weight_dtype) + text_enc.to(accelerator.device, dtype=weight_dtype) + + # Create EMA for the unet. + if args.use_ema: + ema_model = deepcopy(model) + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) + ema_model.load_state_dict(load_model.state_dict()) + ema_model.to(accelerator.device) + del load_model + + for i in range(len(models)): + # pop models so that they are not loaded again + model = models.pop() + + # load diffusers style into model + load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + params_to_optimize = model.parameters() + # Optimizer creation + if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): + logger.warning( + f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]." + "Defaulting to adamW" + ) + args.optimizer = "adamw" + + if args.use_8bit_adam and not args.optimizer.lower() == "adamw": + logger.warning( + f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " + f"set to {args.optimizer.lower()}" + ) + + if args.optimizer.lower() == "adamw": + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + if args.optimizer.lower() == "prodigy": + try: + import prodigyopt + except ImportError: + raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`") + + optimizer_class = prodigyopt.Prodigy + + if args.learning_rate <= 0.1: + logger.warning( + "Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0" + ) + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + beta3=args.prodigy_beta3, + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + decouple=args.prodigy_decouple, + use_bias_correction=args.prodigy_use_bias_correction, + safeguard_warmup=args.prodigy_safeguard_warmup, + ) + + # Setup data: + train_dataset = getdataset(args) + train_dataloader = torch.utils.data.DataLoader( + train_dataset, + shuffle=True, + # pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + # prefetch_factor=8 + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + model, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_model.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + if accelerator.is_main_process: + project_name = os.getenv('PROJECT', os.path.basename(args.output_dir)) + entity = os.getenv('ENTITY', None) + run_name = os.getenv('WANDB_NAME', None) + init_kwargs = { + "entity": entity, + "run_name": run_name, + } + accelerator.init_trackers(project_name=project_name, config=vars(args), init_kwargs=init_kwargs) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {model}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_model.step(model.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + one_step_duration = end_time - start_time + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, " + f"train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) + progress_info.train_loss = 0.0 + + # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. + if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if accelerator.is_main_process and args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(model_input, model_kwargs, prof): + global start_time + start_time = time.time() + + noise = torch.randn_like(model_input) + if args.noise_offset: + # https://www.crosslabs.org//blog/diffusion-with-offset-noise + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1), + device=model_input.device) + + bsz = model_input.shape[0] + # Sample a random timestep for each image without bias. + timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + + # Add noise to the model input according to the noise magnitude at each timestep + # (this is the forward diffusion process) + + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) + assert not torch.any(torch.isnan(noisy_model_input)), 'torch.any(torch.isnan(noisy_model_input))' + + if model_kwargs.get("masked_x", None) is not None and model_kwargs.get("mask", None) is not None: + masked_x = model_kwargs.pop("masked_x") + mask = model_kwargs.pop("mask") + + model_pred = model( + torch.cat([noisy_model_input, masked_x, mask], dim=1), + timesteps, + **model_kwargs + )[0] + + + model_pred = torch.chunk(model_pred, 2, dim=1)[0] + assert not torch.any(torch.isnan(model_pred)), 'torch.any(torch.isnan(model_pred))' + + # Get the target for loss depending on the prediction type + if args.prediction_type is not None: + # set prediction_type of scheduler if defined + noise_scheduler.register_to_config(prediction_type=args.prediction_type) + + if noise_scheduler.config.prediction_type == "epsilon": + target = noise + elif noise_scheduler.config.prediction_type == "v_prediction": + target = noise_scheduler.get_velocity(model_input, noise, timesteps) + elif noise_scheduler.config.prediction_type == "sample": + # We set the target to latents here, but the model_pred will return the noise sample prediction. + target = model_input + # We will have to subtract the noise residual from the prediction to get the target sample. + model_pred = model_pred - noise + else: + raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + + if args.snr_gamma is None: + loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + else: + # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. + # Since we predict the noise instead of x_0, the original formulation is slightly changed. + # This is discussed in Section 4.2 of the same paper. + snr = compute_snr(noise_scheduler, timesteps) + mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min( + dim=1 + )[0] + if noise_scheduler.config.prediction_type == "epsilon": + mse_loss_weights = mse_loss_weights / snr + elif noise_scheduler.config.prediction_type == "v_prediction": + mse_loss_weights = mse_loss_weights / (snr + 1) + + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights + loss = loss.mean() + + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = model.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + + if progress_info.global_step % args.checkpointing_steps == 0: + + if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step) + + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + if npu_config is None: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + + if prof is not None: + prof.step() + + + return loss + + def train_one_step(step_, data_item_, prof_=None): + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B 3 * C T H W + + attn_mask = attn_mask.to(accelerator.device) # B T H W + input_ids = input_ids.to(accelerator.device) # B 1 L + cond_mask = cond_mask.to(accelerator.device) # B 1 L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + assert not torch.any(torch.isnan(x)), 'before vae' + assert not torch.any(torch.isnan(input_ids)), 'before text_enc' + assert not torch.any(torch.isnan(cond_mask)), 'before text_enc' + assert not torch.any(torch.isnan(attn_mask)), 'before text_enc' + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1 L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1 L D + + assert not torch.any(torch.isnan(cond)), 'after text_enc' + + cond = cond.reshape(B, N, L, -1) + + # Map input images to latent space + normalize latents + assert args.use_image_num == 0, 'Inpainting mode does not support image joint training' + + x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:9] + x, masked_x, mask = ae.encode(x), ae.encode(masked_x), ae.encode(mask) + + assert not torch.any(torch.isnan(x)), 'after vae' + assert not torch.any(torch.isnan(masked_x)), 'after vae' + assert not torch.any(torch.isnan(mask)), 'after vae' + + with accelerator.accumulate(model): + x = x.to(weight_dtype) + masked_x = masked_x.to(weight_dtype) + mask = mask.to(weight_dtype) + assert not torch.any(torch.isnan(x)), 'after vae' + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, + masked_x=masked_x, mask=mask) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + if accelerator.is_main_process: + if step == 0: + print("before training, we need to check the validation mode...") + log_validation( + args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step=0, ema=False + ) + if train_one_step(step, data_item, prof_): + break + + if step >= 2 and torch_npu is not None and npu_config is not None: + npu_config.free_mm() + + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # dataset & dataloader + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + + # inpaint dataset + parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode + parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode + parser.add_argument("--default_text_ratio", type=float, default=0.1) + + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--model_max_length", type=int, default=512) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + + # text encoder & vae & diffusion model + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument("--downsampler", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + + # diffusion setting + parser.add_argument("--zero_terminal_snr", action="store_true", help="Whether to zero the terminal SNR.") + parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + + # validation & logs + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--checkpointing_steps", type=int, default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument("--resume_from_checkpoint", type=str, default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument("--logging_dir", type=str, default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument("--report_to", type=str, default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + # optimizer & scheduler + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") + parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--prodigy_beta3", type=float, default=None, + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", + ) + parser.add_argument("--lr_scheduler", type=str, default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument("--allow_tf32", action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + + parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") + + + args = parser.parse_args() + main(args) From bb147dc3e810c112783ee6b55aace16283e8c236 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 27 Jun 2024 05:37:03 +0000 Subject: [PATCH 065/134] merge --- examples/prompt_list_0.txt | 24 +- examples/prompt_list_0_ar.txt | 25 ++ examples/prompt_list_0_cn.txt | 25 ++ examples/prompt_list_0_ja.txt | 25 ++ examples/prompt_list_0_ko.txt | 25 ++ examples/prompt_list_1.txt | 24 ++ opensora/dataset/__init__.py | 10 +- opensora/dataset/t2v_datasets.py | 28 +- opensora/dataset/transform.py | 9 +- opensora/models/diffusion/opensora/modules.py | 7 + .../udit_ultra/modeling_udit_ultra.py | 254 ++++++++---------- .../models/diffusion/udit_ultra/modules.py | 34 ++- opensora/sample/pipeline_opensora.py | 7 +- opensora/sample/sample_t2v.py | 57 ++-- opensora/train/train_t2v_diffusers.py | 17 +- opensora/train/train_t2v_diffusers_lora.py | 36 ++- opensora/utils/lora_utils.py | 82 ++++++ opensora/utils/utils.py | 2 +- scripts/text_condition/gpu/sample_image.sh | 13 +- scripts/text_condition/gpu/sample_video.sh | 12 +- ...rain_imageuditultra_240p_new_rope_fp32.sh} | 21 +- ...train_imageuditultra_480p_new_rope_fp32.sh | 61 +++++ ...train_imageuditultra_480p_new_rope_lora.sh | 59 ++++ scripts/train_data/image_data.txt | 7 +- 24 files changed, 613 insertions(+), 251 deletions(-) create mode 100644 examples/prompt_list_0_ar.txt create mode 100644 examples/prompt_list_0_cn.txt create mode 100644 examples/prompt_list_0_ja.txt create mode 100644 examples/prompt_list_0_ko.txt create mode 100644 examples/prompt_list_1.txt create mode 100644 opensora/utils/lora_utils.py rename scripts/text_condition/gpu/{train_imageuditultra_480p_new_rope.sh => train_imageuditultra_240p_new_rope_fp32.sh} (82%) create mode 100644 scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh create mode 100644 scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index 53b4d0975..7f3c6dccc 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -1,24 +1,28 @@ -A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues. -A quiet beach at dawn, the waves softly lapping at the shore, pink and orange hues painting the sky, offering a moment of solitude and reflection. -A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about. Several giant wooly mammoths approach treading through a snowy meadow, their long wooly fur lightly blows in the wind as they walk, snow covered trees and dramatic snow capped mountains in the distance, mid afternoon light with wispy clouds and a sun high in the distance creates a warm glow, the low camera view is stunning capturing the large furry mammal with beautiful photography, depth of field. A movie trailer featuring the adventures of the 30 year old space man wearing a red wool knitted motorcycle helmet, blue sky, salt desert, cinematic style, shot on 35mm film, vivid colors. -Drone view of waves crashing against the rugged cliffs along Big Sur’s garay point beach. The crashing blue waters create white-tipped waves, while the golden light of the setting sun illuminates the rocky shore. A small island with a lighthouse sits in the distance, and green shrubbery covers the cliff’s edge. The steep drop from the road down to the beach is a dramatic feat, with the cliff’s edges jutting out over the sea. This is a view that captures the raw beauty of the coast and the rugged landscape of the Pacific Coast Highway. Animated scene features a close-up of a short fluffy monster kneeling beside a melting red candle. The art style is 3D and realistic, with a focus on lighting and texture. The mood of the painting is one of wonder and curiosity, as the monster gazes at the flame with wide eyes and open mouth. Its pose and expression convey a sense of innocence and playfulness, as if it is exploring the world around it for the first time. The use of warm colors and dramatic lighting further enhances the cozy atmosphere of the image. +A time-lapse of a storm forming over the ocean, dark clouds gathering and lightning flashing. The storm's energy creates spirals of light that dance across the sky. +A majestic eagle perches on a high cliff, its keen eyes scanning the valley below. With a powerful flap, it takes off, leaving a trail of sparkling feathers. +A single butterfly with wings that resemble stained glass flutters through a field of flowers. The shot captures the light as it passes through the delicate wings, creating a vibrant, colorful display. HD. +A solitary mermaid swims through an underwater cave filled with glowing crystals. The shot follows her graceful movements, capturing the play of light on her scales and the ethereal beauty of the cave. +Close-up of a dragon's eye as it slowly opens, revealing a fiery iris that reflects the burning landscape around it, while smoke wisps off its scaly eyelid. +A cat with the enigmatic smile of the Mona Lisa, lounging regally on a velvet cushion, her eyes following a fluttering butterfly that mirrors the mysterious allure of her expression. 4K. A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures. This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. The majestic beauty of a waterfall cascading down a cliff into a serene lake. Sunset over the sea. a cat wearing sunglasses and working as a lifeguard at pool. -Slow pan upward of blazing oak fire in an indoor fireplace. -Yellow and black tropical fish dart through the sea. -a serene winter scene in a forest. The forest is blanketed in a thick layer of snow, which has settled on the branches of the trees, creating a canopy of white. The trees, a mix of evergreens and deciduous, stand tall and silent, their forms partially obscured by the snow. The ground is a uniform white, with no visible tracks or signs of human activity. The sun is low in the sky, casting a warm glow that contrasts with the cool tones of the snow. The light filters through the trees, creating a soft, diffused illumination that highlights the texture of the snow and the contours of the trees. The overall style of the scene is naturalistic, with a focus on the tranquility and beauty of the winter landscape. +An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt, he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film. +A lone figure stands on the deck of a spaceship, looking out at a nebula filled with vibrant colors. The shot tracks their gaze, capturing the breathtaking beauty of the cosmic landscape and the sense of infinite possibility. +A large orange octopus is seen resting on the bottom of the ocean floor, blending in with the sandy and rocky terrain. lts tentacles are spread out around its body, and its eyes are closed. The octopus is unaware of a king crab that is crawling towards it from behind a rock,its claws raised and ready to attack. The crab is brown and spiny,with long legs and antennae. The scene is captured from a wide angle,showing the vastness and depth of the ocean. The wateris clear and blue, with rays of sunlight filtering through. The shot is sharp and crisp, with a high dynamic range. The octopus and the crab are in focus, while the background is slightly blurred,creating a depth of field effect. a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water. -A serene waterfall cascading down moss-covered rocks, its soothing sound creating a harmonious symphony with nature. A soaring drone footage captures the majestic beauty of a coastal cliff, its red and yellow stratified rock faces rich in color and against the vibrant turquoise of the sea. Seabirds can be seen taking flight around the cliff's precipices. As the drone slowly moves from different angles, the changing sunlight casts shifting shadows that highlight the rugged textures of the cliff and the surrounding calm sea. The water gently laps at the rock base and the greenery that clings to the top of the cliff, and the scene gives a sense of peaceful isolation at the fringes of the ocean. The video captures the essence of pristine natural beauty untouched by human structures. -The video captures the majestic beauty of a waterfall cascading down a cliff into a serene lake. The waterfall, with its powerful flow, is the central focus of the video. The surrounding landscape is lush and green, with trees and foliage adding to the natural beauty of the scene. The camera angle provides a bird's eye view of the waterfall, allowing viewers to appreciate the full height and grandeur of the waterfall. The video is a stunning representation of nature's power and beauty. A vibrant scene of a snowy mountain landscape. The sky is filled with a multitude of colorful hot air balloons, each floating at different heights, creating a dynamic and lively atmosphere. The balloons are scattered across the sky, some closer to the viewer, others further away, adding depth to the scene. Below, the mountainous terrain is blanketed in a thick layer of snow, with a few patches of bare earth visible here and there. The snow-covered mountains provide a stark contrast to the colorful balloons, enhancing the visual appeal of the scene. A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene. A snowy forest landscape with a dirt road running through it. The road is flanked by trees covered in snow, and the ground is also covered in snow. The sun is shining, creating a bright and serene atmosphere. The road appears to be empty, and there are no people or animals visible in the video. The style of the video is a natural landscape shot, with a focus on the beauty of the snowy forest and the peacefulness of the road. -The dynamic movement of tall, wispy grasses swaying in the wind. The sky above is filled with clouds, creating a dramatic backdrop. The sunlight pierces through the clouds, casting a warm glow on the scene. The grasses are a mix of green and brown, indicating a change in seasons. The overall style of the video is naturalistic, capturing the beauty of the landscape in a realistic manner. The focus is on the grasses and their movement, with the sky serving as a secondary element. The video does not contain any human or animal elements. \ No newline at end of file +The dynamic movement of tall, wispy grasses swaying in the wind. The sky above is filled with clouds, creating a dramatic backdrop. The sunlight pierces through the clouds, casting a warm glow on the scene. The grasses are a mix of green and brown, indicating a change in seasons. The overall style of the video is naturalistic, capturing the beauty of the landscape in a realistic manner. The focus is on the grasses and their movement, with the sky serving as a secondary element. The video does not contain any human or animal elements. +A close-up of a magician’s crystal ball that reveals a futuristic cityscape within. Skyscrapers of light stretch towards the heavens, and flying cars zip through the air, casting neon reflections across the ball’s surface. 8K. +A majestic horse gallops across a bridge made of rainbows, each hoof striking sparks of color that cascade into the sky, the clouds parting to reveal a sunlit path to a distant, magical realm. +A close-up of a robot dog as it interacts with a group of real puppies in a park, its mechanical eyes blinking with curiosity and tail wagging energetically. High Resolution. +An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. \ No newline at end of file diff --git a/examples/prompt_list_0_ar.txt b/examples/prompt_list_0_ar.txt new file mode 100644 index 000000000..69121aa60 --- /dev/null +++ b/examples/prompt_list_0_ar.txt @@ -0,0 +1,25 @@ +عدة ماموث صوفي عملاق تقترب وهي تخطو عبر مرج مغطى بالثلوج، فروها الصوفي الطويل يتمايل بلطف في الرياح أثناء سيرها، أشجار مغطاة بالثلوج وجبال مغطاة بالثلوج في المسافة، ضوء بعد الظهر مع سحب رقيقة وشمس عالية في المسافة تخلق توهجًا دافئًا، منظر الكاميرا المنخفض مذهل يلتقط الحيوان الفروي الكبير بتصوير جميل وعمق ميدان. +عرض فيلم دعائي يتضمن مغامرات رجل فضاء يبلغ من العمر 30 عامًا يرتدي خوذة دراجة نارية مصنوعة من الصوف الأحمر، سماء زرقاء، صحراء ملحية، أسلوب سينمائي، تم تصويره على فيلم 35 ملم، ألوان زاهية. +مشهد متحرك يظهر لقطة قريبة لوحش صغير فروي راكع بجانب شمعة حمراء تذوب. الأسلوب الفني ثلاثي الأبعاد وواقعي، مع التركيز على الإضاءة والملمس. جو اللوحة هو التساؤل والفضول، حيث ينظر الوحش إلى اللهب بعيون واسعة وفم مفتوح. وضعيته وتعبيراته تنقل إحساسًا بالبراءة واللعب، كما لو كان يستكشف العالم من حوله لأول مرة. استخدام الألوان الدافئة والإضاءة الدرامية يعزز الأجواء المريحة للصورة. +فيديو بتقنية الفاصل الزمني لعاصفة تتشكل فوق المحيط، تتجمع السحب الداكنة وتومض البرق. طاقة العاصفة تخلق دوامات من الضوء التي ترقص عبر السماء. +نسر مهيب يجثم على منحدر عالٍ، عيناه الحادتان تفحصان الوادي أدناه. بصفعة قوية، ينطلق، تاركًا وراءه أثرًا من الريش المتلألئ. +فراشة واحدة بأجنحة تشبه الزجاج المعشق ترفرف عبر حقل من الزهور. اللقطة تلتقط الضوء بينما يمر عبر الأجنحة الدقيقة، مما يخلق عرضًا حيويًا وملونًا. بجودة عالية. +حورية البحر الوحيدة تسبح عبر كهف تحت الماء مليء بالكريستالات المتوهجة. اللقطة تتبع حركاتها الرشيقة، وتلتقط لعب الضوء على حراشفها وجمال الكهف الأثيري. +لقطة مقربة لعين تنين تفتح ببطء، تكشف عن قزحية نارية تعكس المناظر المحترقة من حولها، بينما يتصاعد الدخان من جفنه المتقشر. +قطة بابتسامة غامضة مثل الموناليزا، تتكئ بشكل ملكي على وسادة مخملية، عيناها تتابع فراشة ترفرف تعكس السحر الغامض لتعبيرها. بجودة 4K. +عالم مرجاني مصنوع بشكل جميل من الورق، مليء بالأسماك الملونة والمخلوقات البحرية. +هذه اللقطة القريبة لحمامة التاج الفيكتوري تظهر ريشها الأزرق اللافت وصدرها الأحمر. قمتها مصنوعة من ريش دقيق، وعينها لونها أحمر لافت. رأس الطائر مائل قليلاً إلى الجانب، مما يعطي انطباعًا بالوقار والعظمة. الخلفية ضبابية، مما يلفت الانتباه إلى المظهر اللافت للطائر. +قطة ترتدي نظارات شمسية وتعمل كمنقذة في حمام السباحة. +لقطة قريبة جدًا لرجل ذو شعر رمادي ولحية في الستينيات من عمره، يفكر بعمق في تاريخ الكون بينما يجلس في مقهى في باريس، عينيه تركز على الناس خارج الإطار بينما هم يمشون، وهو جالس في الغالب بدون حركة، يرتدي معطفًا صوفيًا وقميصًا بأزرار، ويرتدي قبعة بنية ونظارات ويبدو بمظهر أكاديمي جدًا، وفي النهاية يقدم ابتسامة مغلقة الفم كما لو وجد الجواب على لغز الحياة، الإضاءة سينمائية للغاية مع الضوء الذهبي والشوارع الباريسية والمدينة في الخلفية، عمق الميدان، فيلم سينمائي 35 ملم. +شخص وحيد يقف على سطح مركبة فضائية، ينظر إلى سديم مليء بالألوان النابضة بالحياة. تتبع اللقطة نظرته، تلتقط الجمال الرائع للمشهد الكوني وإحساس الإمكانيات اللامتناهية. +أخطبوط برتقالي كبير يُرى مستريحًا في قاع المحيط، يندمج مع التضاريس الرملية والصخرية. أذرعه منتشرة حول جسمه، وعيناه مغلقتان. الأخطبوط غير مدرك لسرطان البحر الملكي الذي يزحف نحوه من خلف صخرة، مخالبه مرفوعة وجاهزة للهجوم. السرطان بني وشوكي، بأرجل طويلة وقرون استشعار. المشهد ملتقط من زاوية واسعة، يظهر اتساع وعمق المحيط. المياه صافية وزرقاء، وأشعة الشمس تتسلل من خلالها. اللقطة واضحة وحادة، ذات نطاق ديناميكي عالٍ. الأخطبوط والسرطان في التركيز، بينما الخلفية ضبابية قليلاً، مما يخلق تأثير عمق الميدان. +تفاعل ديناميكي بين المحيط وصخرة كبيرة. الصخرة بنسيج خشن وحواف مسننة، مغمورة جزئيًا في الماء، مما يشير إلى أنها جزء طبيعي من الساحل. المياه حول الصخرة في حركة، مع رغوة بيضاء وأمواج تصطدم بالصخرة، مشيرة إلى قوة حركة المحيط. الخلفية هي امتداد واسع للمحيط، مع تموجات وأمواج صغيرة، مما يشير إلى حالة بحر معتدلة. النمط العام للمشهد هو تصوير واقعي للمناظر الطبيعية، مع التركيز على التفاعل بين الصخرة والماء. +لقطة بطائرة بدون طيار تلتقط الجمال المهيب لمنحدر ساحلي، وجوه الصخور الحمراء والصفراء ذات الطبقات الغنية بالألوان مقابل الفيروز النابض بالحياة للبحر. الطيور البحرية يمكن رؤيتها تحلق حول قمم المنحدر. بينما تتحرك الطائرة بدون طيار ببطء من زوايا مختلفة، تتغير أشعة الشمس لتلقي بظلال متحركة تبرز القوام الوعرة للمنحدر والبحر الهادئ المحيط. المياه تلامس بلطف قاعدة الصخور والخضرة التي تلتصق بقمة المنحدر، والمشهد يعطي إحساسًا بالعزلة السلمية في حواف المحيط. الفيديو يلتقط جوهر الجمال الطبيعي النقي غير المتأثر بالهياكل البشرية. +مشهد حيوي لمنظر جبل مغطى بالثلوج. السماء مليئة بالعديد من البالونات الهوائية الملونة، كل منها يطفو على ارتفاعات مختلفة، مما يخلق جوًا ديناميكيًا وحيويًا. البالونات منتشرة عبر السماء، بعضها أقرب إلى المشاهد، وبعضها الآخر أبعد، مما يضيف عمقًا للمشهد. أدناه، تضاريس جبلية مغطاة بطبقة سميكة من الثلج، مع بعض بقع الأرض العارية تظهر هنا وهناك. الجبال المغطاة بالثلوج توفر تباينًا صارخًا مع البالونات الملونة، مما يعزز الجاذبية البصرية للمشهد. +مشهد تحت الماء هادئ يظهر سلحفاة بحرية تسبح عبر شعاب مرجانية. السلحفاة ذات القوقعة الخضراء البنية هي التركيز الرئيسي للفيديو، تسبح برشاقة نحو الجانب الأيمن من الإطار. الشعاب المرجانية، المليئة بالحياة، واضحة في الخلفية، مما يوفر خلفية نابضة بالحياة وملونة لرحلة السلحفاة. عدة أسماك صغيرة تتنقل حول السلحفاة، تضيف إحساسًا بالحركة والديناميكية للمشهد. +منظر غابة مغطاة بالثلوج مع طريق ترابي يمر من خلالها. الطريق محاط بأشجار مغطاة بالثلوج، والأرض مغطاة أيضًا بالثلوج. الشمس مشرقة، مما يخلق جوًا مشرقًا وهادئًا. الطريق يبدو خاليًا، ولا يوجد أشخاص أو حيوانات مرئية في الفيديو. أسلوب الفيديو هو تصوير طبيعي للمناظر الطبيعية، مع التركيز على جمال الغابة المثلجة وهدوء الطريق. +الحركة الديناميكية للأعشاب الطويلة الرقيقة وهي تتمايل في الرياح. السماء أعلاه مليئة بالغيوم، مما يخلق خلفية درامية. أشعة الشمس تخترق الغيوم، تلقي بتوهج دافئ على المشهد. الأعشاب مزيج من اللونين الأخضر والبني، مما يشير إلى تغير الفصول. أسلوب الفيديو العام هو طبيعي، يلتقط جمال المناظر الطبيعية بطريقة واقعية. التركيز على الأعشاب وحركتها، والسماء كعنصر ثانوي. الفيديو لا يحتوي على أي عناصر بشرية أو حيوانية. +لقطة قريبة لكرة سحرية لمشعوذ تكشف عن مشهد مدينة مستقبلية بداخلها. ناطحات سحاب مضيئة تمتد نحو السماء، والسيارات الطائرة تمر عبر الهواء، تلقي بانعكاسات نيون عبر سطح الكرة. بجودة 8K. +حصان مهيب يجري عبر جسر من قوس قزح، كل حافر يضرب شرارات من الألوان التي تتدفق في السماء، السحب تتفرق لتكشف عن مسار مضاء بالشمس إلى عالم سحري بعيد. +لقطة قريبة لكلب آلي يتفاعل مع مجموعة من الجراء الحقيقية في حديقة، عيونه الميكانيكية تومض بفضول وذيله يهتز بحماس. بجودة عالية. +امرأة مسنة بشعر أبيض ووجه مليء بالتجاعيد تجلس داخل سيارة قديمة الطراز، تنظر من خلال النافذة الجانبية بتعبير تأملي أو حزين قليلاً. \ No newline at end of file diff --git a/examples/prompt_list_0_cn.txt b/examples/prompt_list_0_cn.txt new file mode 100644 index 000000000..052fb35d5 --- /dev/null +++ b/examples/prompt_list_0_cn.txt @@ -0,0 +1,25 @@ +几头巨大的长毛猛犸象走过积雪的草地,它们长长的毛在风中轻轻飘动,远处是被雪覆盖的树木和壮丽的雪山,下午的光线和飘逸的云朵以及高挂的太阳营造出温暖的光芒,低角度拍摄令人惊叹,捕捉到大型毛茸茸哺乳动物的美丽摄影,景深效果。 +一部电影预告片,讲述30岁宇航员的冒险故事,他戴着红色毛线编织的摩托车头盔,蓝天,盐沙漠,电影风格,35毫米胶片拍摄,色彩鲜艳。 +动画场景特写一只矮胖的毛茸茸怪物跪在融化的红色蜡烛旁,艺术风格为3D和写实,注重光线和纹理。画面的情感是奇妙和好奇,怪物瞪大眼睛、张开嘴巴注视着火焰。它的姿势和表情传达出纯真和玩耍的感觉,仿佛第一次探索周围的世界。使用暖色和戏剧性照明进一步增强了图像的舒适氛围。 +海上风暴形成的延时摄影,黑云聚集,闪电闪烁。风暴的能量在天空中舞动出光的旋律。 +一只雄鹰栖息在高崖上,锐利的眼睛扫描着下方的山谷。猛然一拍翅膀,它腾空而起,留下闪亮的羽毛痕迹。 +一只翅膀像彩色玻璃的蝴蝶在花丛中飞舞。拍摄捕捉到光线穿过其精致翅膀的瞬间,创造出鲜艳的色彩展示。高清。 +一条孤独的美人鱼在充满发光水晶的水下洞穴中游泳。镜头追随她优雅的动作,捕捉到光在她鳞片上的游戏和洞穴的梦幻美景。 +特写镜头中,一只龙的眼睛慢慢睁开,显示出反映燃烧景观的炽热虹膜,同时烟雾从它的鳞状眼睑上飘起。 +一只带着蒙娜丽莎神秘微笑的猫,悠闲地躺在天鹅绒垫子上,眼睛跟随着一只扑动的蝴蝶,反映出她神秘吸引力的表情。4K。 +精美渲染的纸工世界,充满多彩的鱼和海洋生物。 +这一特写镜头展示了维多利亚冠鸽的惊人蓝色羽毛和红色胸脯。它的冠由精致的羽毛组成,眼睛呈现出鲜红色。鸟头略微倾斜,显得威严而高贵。背景模糊,突出鸟的引人注目外貌。 +一只戴着太阳镜的猫,在游泳池担任救生员 +一位六十多岁的灰发男人的极近镜头,他在巴黎的一家咖啡馆里深思宇宙历史,眼睛注视着屏幕外行走的人,他几乎不动,穿着羊毛外套和衬衫,戴着棕色贝雷帽和眼镜,外表非常学究样子,最后露出微妙的闭口笑容,仿佛找到了生命的谜底。光线非常电影化,金色的光芒和巴黎街景作为背景,景深,35毫米电影胶片风格。 +一个孤独的人站在宇宙飞船的甲板上,望着充满艳丽色彩的星云。镜头跟随着他们的目光,捕捉到宇宙景观的壮丽美景和无限可能的感觉。 +一只巨大的橙色章鱼在海底休息,与沙石地形融为一体。它的触手展开在身周,眼睛闭着。章鱼没有注意到一只从岩石后面爬来的帝王蟹,蟹爪高举,准备攻击。蟹是棕色且带刺的,长腿和触角。场景从广角拍摄,显示出海洋的广阔和深度。水清澈湛蓝,阳光透过水面。画面清晰锐利,具有高动态范围。章鱼和蟹在焦点内,背景略微模糊,形成景深效果。 +海洋与一块大岩石的动态交互。岩石质地粗糙,边缘参差不齐,部分被水淹没,显示出它是海岸线的一部分。岩石周围的水在运动,白色泡沫和波浪撞击岩石,显示出海洋运动的力量。背景是辽阔的海洋,带有小波纹和波浪,显示出中等海况。整体风格是自然景观的写实描绘,强调岩石与水的互动。 +无人机航拍镜头捕捉到沿海悬崖的壮丽美景,红黄相间的分层岩石面颜色丰富,与碧绿的大海形成对比。海鸟在悬崖顶飞翔。随着无人机从不同角度慢慢移动,变化的阳光投射出变化的阴影,突显悬崖的粗糙纹理和周围平静的海面。水轻轻拍打岩石基部和悬崖顶的绿植,画面给人一种远离人类建筑的宁静自然之美。视频捕捉了未经人类破坏的纯净自然美景的本质。 +一个雪山景观的生动场景。天空中充满五颜六色的热气球,每个热气球漂浮在不同高度,创造出动态和充满活力的氛围。气球散布在天空中,一些靠近观众,另一些则远离,增加了画面的深度。下方,山地被厚厚的积雪覆盖,间或可见裸露的土壤。雪山为五彩缤纷的气球提供了鲜明的对比,增强了画面的视觉吸引力。 +一个宁静的水下场景,一只海龟在珊瑚礁中游动。海龟绿褐色的壳是视频的主要焦点,优雅地向画面右侧游去。充满生机的珊瑚礁在背景中可见,为海龟的旅程提供了丰富多彩的背景。几条小鱼在海龟周围穿梭,增添了画面的动态感。 +一片积雪的森林景观,中间有一条泥路穿过。道路两旁是被雪覆盖的树木,地面也被雪覆盖。阳光明媚,营造出明亮和宁静的氛围。道路看起来空无一人,视频中没有看到人或动物。视频风格为自然景观拍摄,重点展示了雪林的美丽和平静的道路。 +高大、轻盈的草随风摆动的动态场景。天空布满云朵,形成戏剧性的背景。阳光穿透云层,投射出温暖的光芒。草是绿褐相间,显示出季节的变化。视频整体风格自然写实,捕捉了风景的美丽。焦点在于草的摆动,天空作为次要元素。视频中没有人或动物元素。 +特写镜头中,一位魔术师的水晶球内显示出未来的城市景观。光的摩天大楼直冲云霄,飞行汽车在空中飞驰,在球面上投射出霓虹反射。8K。 +一匹雄伟的马在彩虹桥上奔驰,每一蹄击出色彩的火花,飞跃到天空中,云层分开露出通向远方魔法王国的阳光小径。 +特写镜头中,一个机器人狗在公园里与一群真狗互动,它的机械眼睛眨动好奇,尾巴兴奋地摇动。高分辨率。 +一位白发苍苍、脸上布满皱纹的老妇人坐在一辆老款汽车里,通过侧窗望着外面,神情沉思或略带忧伤。 \ No newline at end of file diff --git a/examples/prompt_list_0_ja.txt b/examples/prompt_list_0_ja.txt new file mode 100644 index 000000000..c51bbc87d --- /dev/null +++ b/examples/prompt_list_0_ja.txt @@ -0,0 +1,25 @@ +いくつかの巨大なマンモスが雪の原野を踏みしめながら近づいてくる。彼らの長い毛皮が風にそよぎながら歩く姿は壮観で、遠くには雪に覆われた木々と劇的な雪山が見える。午後の光が薄雲を通して高く差し込み、暖かな輝きを放つ。低いカメラ視点から大きな毛むくじゃらの動物を捉え、美しい写真と奥行き感を見せる。 +赤いウールのニットバイクヘルメットをかぶった30歳の宇宙飛行士の冒険を描いた映画の予告編。青い空と塩の砂漠、シネマティックなスタイル、35mmフィルムで撮影され、鮮やかな色彩が特徴。 +短くてふわふわしたモンスターが溶けている赤いろうそくのそばにひざまずいているアニメーションシーン。アートスタイルは3Dでリアルに描かれ、照明と質感に焦点を当てている。モンスターが炎を大きな目と口を開けて見つめる姿は、驚きと好奇心に満ちており、初めて世界を探検しているかのような無邪気さと遊び心を表している。暖色系の色彩と劇的な照明が、さらに居心地の良い雰囲気を高めている。 +海上で形成される嵐のタイムラプス映像。暗い雲が集まり、稲妻が閃く。嵐のエネルギーが空に踊る光の螺旋を作り出す。 +壮大な鷲が高い崖に止まり、その鋭い目で谷を見渡す。力強く羽ばたき、キラキラとした羽を残して飛び立つ。 +ステンドグラスのような羽を持つ蝶が花畑を飛び回る。一瞬一瞬がHDで捉えられ、光が羽を通過する様子が鮮やかに描かれる。 +一人の人魚が光る結晶で満たされた海底洞窟を泳ぐ。彼女の優雅な動きを追い、鱗に光が反射する様子や洞窟の神秘的な美しさを捉えている。 +ゆっくりと開くドラゴンの目のクローズアップ。炎のような虹彩が周囲の燃える風景を映し出し、鱗のまぶたから煙が立ち上る。 +モナリザの神秘的な微笑を浮かべる猫が、ベルベットのクッションの上で堂々とくつろぎ、目は蝶の舞いを追っている。4Kで撮影。 +色鮮やかな魚や海の生き物でいっぱいの、見事にレンダリングされたペーパークラフトのサンゴ礁の世界。 +ビクトリアクラウンピジョンのクローズアップ。鮮やかな青い羽毛と赤い胸、繊細な羽飾りを持つこの鳥が、やや傾いた頭で威厳ある姿を見せる。背景はぼやけていて、鳥の印象的な外見が強調されている。 +サングラスをかけた猫がプールのライフガードとして働いている。 +60代の灰色の髪とひげを持つ男性の極端なクローズアップ。パリのカフェで宇宙の歴史について深く考え込んでいる彼は、視線をオフスクリーンの人々に向け、ほぼ動かずに座っている。彼はウールのコートスーツとボタンダウンシャツを着て、茶色のベレー帽と眼鏡をかけており、非常に教授らしい外見をしている。最後に、人生の謎の答えを見つけたかのように、控えめな口を閉じた微笑みを見せる。照明は非常にシネマティックで、黄金色の光とパリの街並みが背景に広がり、奥行き感があり、35mmフィルムで撮影されている。 +宇宙船のデッキに立ち、鮮やかな色彩の星雲を見つめる孤独な人物。その視線を追い、宇宙の美しさと無限の可能性を捉える。 +大きなオレンジ色のタコが海底に休んでおり、砂と岩の地形に溶け込んでいる。その触手は体の周りに広がり、目は閉じている。タコは後ろの岩陰からやってくる、ハサミを上げた茶色のトゲトゲしたキングクラブに気づいていない。このシーンは広角で捉えられ、海の広大さと深さを示している。水は透き通って青く、太陽光が差し込んでいる。高ダイナミックレンジで鮮明に撮影されており、タコとカニが焦点にあり、背景は少しぼやけて奥行き感を作り出している。 +大きな岩と海の動的な相互作用。荒々しい質感とギザギザのエッジを持つ岩が部分的に水に沈んでおり、海岸線の自然の特徴を示唆している。岩の周りの水は動いており、白い泡と波が岩にぶつかり、海の動きの力を示している。背景には広大な海が広がり、小さな波が見える。シーン全体は自然の風景をリアルに描写し、岩と水の相互作用に焦点を当てている。 +ドローン映像は、沿岸の崖の壮大な美しさを捉えている。赤と黄色の地層が豊かな色彩を見せ、鮮やかなターコイズ色の海に対比している。海鳥が崖の縁を飛び交い、ドローンが異なる角度からゆっくりと移動することで、変化する日差しが崖のざらざらした質感と周囲の穏やかな海を際立たせる。水は崖の基部に優しく打ち寄せ、崖の上に生い茂る緑が見える。この映像は、人間の手が加えられていない自然の美しさを完璧に捉えている。 +雪山の風景が生き生きとしたシーン。空にはカラフルな熱気球がたくさん浮かび、動的で賑やかな雰囲気を作り出している。熱気球は空に散らばり、遠近感を出している。下の山岳地帯は厚い雪に覆われ、所々に裸の地面が見える。雪に覆われた山々はカラフルな熱気球と対照を成し、視覚的な魅力を高めている。 +海亀がサンゴ礁を泳ぐ穏やかな水中シーン。緑がかった茶色の甲羅を持つ海亀が、画面の右側に向かって優雅に泳ぐ。サンゴ礁が背景に見え、鮮やかでカラフルなバックドロップを提供している。小さな魚が海亀の周りを泳ぎ回り、シーンに動きとダイナミズムを加えている。 +雪に覆われた森の風景に、一本の土の道が走っている。道の両側には雪をかぶった木々が並び、地面も雪で覆われている。太陽が輝き、明るく静かな雰囲気を作り出している。道には人も動物も見えず、ビデオのスタイルは自然の風景を強調している。 +背の高い草が風に揺れる動的なシーン。空には雲が広がり、劇的な背景を作り出している。太陽の光が雲間から差し込み、シーンに暖かな輝きを与えている。草は緑と茶色が混じり、季節の変わり目を示唆している。シーン全体は自然の美しさをリアルに捉え、草の動きに焦点を当てている。人間や動物の要素は含まれていない。 +魔法使いのクリスタルボールのクローズアップが未来都市を映し出す。光の摩天楼が天に向かって伸び、空飛ぶ車が空を駆け抜け、ボールの表面にネオンの反射を投げかける。8Kで撮影。 +虹の橋を駆ける壮大な馬。蹄が触れるたびに色の火花が飛び散り、雲が割れて遠くの魔法の国へ続く日差しの道が現れる。 +ロボット犬が公園で本物の子犬たちと遊ぶクローズアップ。機械の目が好奇心に満ちて瞬き、尾が元気に揺れる。高解像度で撮影。 +白髪で皺のある顔の高齢女性が、古いモデルの車に座り、側窓から外を物思いにふけったような、またはやや悲しげな表情で見ている。 \ No newline at end of file diff --git a/examples/prompt_list_0_ko.txt b/examples/prompt_list_0_ko.txt new file mode 100644 index 000000000..476153ce5 --- /dev/null +++ b/examples/prompt_list_0_ko.txt @@ -0,0 +1,25 @@ +거대한 털복숭이 매머드 몇 마리가 눈 덮인 초원을 거닐며 다가옵니다. 그들의 긴 털은 바람에 살짝 흩날리고, 눈 덮인 나무들과 극적인 눈 덮인 산맥이 멀리 보입니다. 오후 중반의 빛과 가느다란 구름, 멀리 높이 떠있는 태양이 따뜻한 빛을 만들어냅니다. 낮은 카메라 앵글로 찍은 이 장면은 큰 털복숭이 매머드를 아름답게 포착하여 깊이감을 더합니다. +30세의 우주인이 빨간색 울 니트 오토바이 헬멧을 쓰고 모험을 떠나는 영화 예고편입니다. 푸른 하늘과 소금 사막이 배경이며, 영화적인 스타일로 35mm 필름에 촬영된 생생한 색감이 특징입니다. +애니메이션 장면에서는 녹아내리는 빨간 촛불 옆에 무릎을 꿇고 있는 짧고 복슬복슬한 몬스터의 클로즈업을 보여줍니다. 이 예술 스타일은 3D이며, 조명과 질감에 중점을 둔 현실적인 표현입니다. 몬스터가 불꽃을 바라보며 넓게 뜬 눈과 열린 입으로 경이로움과 호기심을 표현하고 있어 순수하고 장난기 있는 분위기를 전달합니다. 따뜻한 색감과 극적인 조명은 이미지의 아늑한 분위기를 한층 더 강조합니다. +폭풍이 바다 위에서 형성되는 타임랩스 영상입니다. 어두운 구름이 모여들고 번개가 번쩍입니다. 폭풍의 에너지가 하늘을 가로지르는 빛의 나선을 만들어냅니다. +장엄한 독수리가 높은 절벽에 앉아 계곡을 예리한 눈으로 살핍니다. 강력한 날갯짓으로 날아오르며 반짝이는 깃털을 남깁니다. +날개가 스테인드글라스처럼 보이는 나비 한 마리가 꽃밭을 날아다닙니다. 빛이 섬세한 날개를 통과하면서 생생하고 다채로운 디스플레이를 만들어냅니다. HD 화질입니다. +외로운 인어가 빛나는 수정으로 가득한 해저 동굴을 헤엄쳐 다닙니다. 촬영은 그녀의 우아한 움직임을 따라가며 비늘 위에 빛이 반사되고 신비로운 동굴의 아름다움을 포착합니다. +용의 눈이 천천히 열리며, 그 속에서 불타는 홍채가 주변의 불타는 풍경을 반사하고, 비늘로 된 눈꺼풀에서는 연기가 피어오릅니다. +모나리자의 신비로운 미소를 지닌 고양이가 벨벳 쿠션 위에 우아하게 누워 있습니다. 그녀의 눈은 미소의 신비로운 매력을 반영하며 나비를 쫓고 있습니다. 4K 화질입니다. +화려하게 표현된 종이 공예 세계의 산호초에는 다채로운 물고기와 해양 생물들이 가득합니다. +이 클로즈업 샷은 화려한 파란색 깃털과 빨간 가슴을 가진 빅토리아 크라운 비둘기의 모습을 보여줍니다. 깃털은 섬세하고 레이스 같은 모습이며, 눈은 인상적인 빨간색입니다. 새의 머리는 약간 옆으로 기울어져 있어 우아하고 장엄한 인상을 줍니다. 배경은 흐릿하게 처리되어 새의 화려한 모습에 집중할 수 있습니다. +선글라스를 쓴 고양이가 수영장에서 구조원으로 일하고 있습니다. +60대 회색 머리와 수염을 가진 남성의 극단적인 클로즈업입니다. 그는 파리의 카페에 앉아 우주의 역사를 깊이 생각하며 주로 움직이지 않고 화면 밖 사람들을 바라보고 있습니다. 그는 울 코트와 셔츠를 입고 갈색 베레모와 안경을 착용하여 매우 교수 같은 모습을 하고 있습니다. 마지막에 그는 인생의 신비에 대한 답을 찾은 듯한 미소를 짓습니다. 조명은 매우 영화적이며 파리의 거리와 도시가 배경입니다. 깊이감이 있고 35mm 필름으로 촬영되었습니다. +우주선 갑판에 서서 네뷸라를 바라보는 한 인물입니다. 그들의 시선을 따라가며 우주의 풍경의 아름다움과 무한한 가능성을 포착합니다 +바다 바닥에 휴식 중인 큰 주황색 문어가 보입니다. 문어는 모래와 바위 지형과 어우러져 있으며, 촉수는 몸 주변에 퍼져 있고 눈은 감겨 있습니다. 문어는 뒤에서 바위 뒤에서 다가오는 왕게를 눈치채지 못합니다. 왕게는 갈색에 가시가 있으며, 긴 다리와 더듬이를 가지고 있습니다. 이 장면은 넓은 앵글로 촬영되어 바다의 광활함과 깊이를 보여줍니다. 물은 맑고 푸르며 햇살이 스며들어 있습니다. 문어와 왕게는 초점이 맞춰져 있고 배경은 약간 흐릿하게 처리되어 깊이감을 더합니다. +대양과 큰 바위 사이의 역동적인 상호작용입니다. 거친 질감과 톱니 모양의 가장자리를 가진 바위가 물에 부분적으로 잠겨 있어 해안선의 자연적 특징임을 암시합니다. 바위 주변의 물은 움직이며 흰 거품과 파도가 바위에 부딪혀 대양의 힘을 나타냅니다. 배경은 대양의 광활한 풍경으로, 작은 물결과 파도가 moderate sea state를 나타냅니다. 이 장면의 전체 스타일은 자연 풍경의 사실적인 묘사에 중점을 두고 있으며, 바위와 물 사이의 상호작용을 강조합니다. +드론이 촬영한 영상은 해안 절벽의 장엄한 아름다움을 포착합니다. 붉고 노란 층으로 이루어진 암벽이 선명한 색감을 자랑하며, 바다의 생생한 터쿼이즈와 대조됩니다. 바위 주변을 나는 바닷새들이 보입니다. 드론이 다양한 각도에서 천천히 움직이며 변하는 햇빛이 절벽의 거친 질감을 강조합니다. 물은 바위의 밑부분과 바위 꼭대기에 붙어 있는 초록색 식물에 부드럽게 부딪히며, 인적 없는 평화로운 고립감을 줍니다. 이 영상은 인간의 구조물로부터 손대지 않은 자연미를 포착합니다. +다채로운 열기구들이 하늘을 떠다니는 눈 덮인 산악 풍경입니다. 열기구는 다양한 높이에서 떠다니며, 동적인 분위기를 자아냅니다. 열기구는 하늘을 가득 채우며, 가까운 곳과 먼 곳에 위치하여 장면에 깊이감을 더합니다. 아래는 두꺼운 눈으로 덮인 산악 지형이 있으며, 여기저기 드러난 맨땅도 보입니다. 눈 덮인 산들은 다채로운 열기구와 대조를 이루어 시각적 매력을 더합니다. +바다거북이 산호초를 헤엄치는 평화로운 해저 장면입니다. 거북이는 초록빛 갈색 껍질을 가지고 있으며, 오른쪽으로 유영하는 모습이 초점입니다. 산호초는 생명으로 가득 차 있어 거북이의 여정에 생동감과 다채로움을 더합니다. 여러 작은 물고기들이 거북이 주변을 빠르게 헤엄치며 장면에 움직임과 역동성을 더합니다. +눈 덮인 숲속 풍경과 그 사이를 가로지르는 흙길입니다. 길 양옆으로 눈 덮인 나무들이 늘어서 있고, 바닥 역시 눈으로 덮여 있습니다. 태양이 빛나며 밝고 평화로운 분위기를 자아냅니다. 길은 비어 있으며, 사람이나 동물은 보이지 않습니다. 이 영상은 자연 풍경을 중점으로 하여 눈 덮인 숲과 고요한 길의 아름다움을 담고 있습니다. +바람에 흔들리는 키 큰 풀의 역동적인 움직임입니다. 하늘에는 구름이 가득 차 있어 극적인 배경을 만듭니다. 햇빛이 구름 사이로 비추며 장면에 따뜻한 빛을 더합니다. 풀은 녹색과 갈색이 섞여 있어 계절의 변화를 나타냅니다. 이 영상의 전체 스타일은 자연적인 풍경을 사실적으로 담아내며, 풀의 움직임을 중점으로 하고 하늘은 부수적인 요소로 작용합니다. 영상에는 사람이나 동물 요소가 없습니다. +마법사의 수정구를 클로즈업한 장면으로, 그 안에 미래 도시 풍경이 보입니다. 빛의 마천루가 하늘로 뻗어 있고, 비행 자동차들이 공중을 날아다니며 수정구 표면에 네온 반사를 만듭니다. 8K 화질입니다. +장엄한 말이 무지개로 만들어진 다리를 질주합니다. 각 발굽이 색의 불꽃을 일으켜 하늘로 흩어지며, 구름이 갈라져 햇빛이 비치는 먼 마법의 왕국으로 가는 길이 드러납니다. +로봇 강아지가 공원에서 실제 강아지 무리와 상호작용하는 클로즈업입니다. 로봇의 기계적인 눈이 호기심으로 반짝이고 꼬리가 힘차게 흔들립니다. 고해상도입니다. +백발과 주름진 얼굴을 가진 노부인이 오래된 모델의 자동차 안에 앉아, 약간 슬픈 표정으로 창밖을 내다보고 있습니다. \ No newline at end of file diff --git a/examples/prompt_list_1.txt b/examples/prompt_list_1.txt new file mode 100644 index 000000000..53b4d0975 --- /dev/null +++ b/examples/prompt_list_1.txt @@ -0,0 +1,24 @@ +A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues. +A quiet beach at dawn, the waves softly lapping at the shore, pink and orange hues painting the sky, offering a moment of solitude and reflection. +A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about. +Several giant wooly mammoths approach treading through a snowy meadow, their long wooly fur lightly blows in the wind as they walk, snow covered trees and dramatic snow capped mountains in the distance, mid afternoon light with wispy clouds and a sun high in the distance creates a warm glow, the low camera view is stunning capturing the large furry mammal with beautiful photography, depth of field. +A movie trailer featuring the adventures of the 30 year old space man wearing a red wool knitted motorcycle helmet, blue sky, salt desert, cinematic style, shot on 35mm film, vivid colors. +Drone view of waves crashing against the rugged cliffs along Big Sur’s garay point beach. The crashing blue waters create white-tipped waves, while the golden light of the setting sun illuminates the rocky shore. A small island with a lighthouse sits in the distance, and green shrubbery covers the cliff’s edge. The steep drop from the road down to the beach is a dramatic feat, with the cliff’s edges jutting out over the sea. This is a view that captures the raw beauty of the coast and the rugged landscape of the Pacific Coast Highway. +Animated scene features a close-up of a short fluffy monster kneeling beside a melting red candle. The art style is 3D and realistic, with a focus on lighting and texture. The mood of the painting is one of wonder and curiosity, as the monster gazes at the flame with wide eyes and open mouth. Its pose and expression convey a sense of innocence and playfulness, as if it is exploring the world around it for the first time. The use of warm colors and dramatic lighting further enhances the cozy atmosphere of the image. +A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures. +This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. +Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. +The majestic beauty of a waterfall cascading down a cliff into a serene lake. +Sunset over the sea. +a cat wearing sunglasses and working as a lifeguard at pool. +Slow pan upward of blazing oak fire in an indoor fireplace. +Yellow and black tropical fish dart through the sea. +a serene winter scene in a forest. The forest is blanketed in a thick layer of snow, which has settled on the branches of the trees, creating a canopy of white. The trees, a mix of evergreens and deciduous, stand tall and silent, their forms partially obscured by the snow. The ground is a uniform white, with no visible tracks or signs of human activity. The sun is low in the sky, casting a warm glow that contrasts with the cool tones of the snow. The light filters through the trees, creating a soft, diffused illumination that highlights the texture of the snow and the contours of the trees. The overall style of the scene is naturalistic, with a focus on the tranquility and beauty of the winter landscape. +a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water. +A serene waterfall cascading down moss-covered rocks, its soothing sound creating a harmonious symphony with nature. +A soaring drone footage captures the majestic beauty of a coastal cliff, its red and yellow stratified rock faces rich in color and against the vibrant turquoise of the sea. Seabirds can be seen taking flight around the cliff's precipices. As the drone slowly moves from different angles, the changing sunlight casts shifting shadows that highlight the rugged textures of the cliff and the surrounding calm sea. The water gently laps at the rock base and the greenery that clings to the top of the cliff, and the scene gives a sense of peaceful isolation at the fringes of the ocean. The video captures the essence of pristine natural beauty untouched by human structures. +The video captures the majestic beauty of a waterfall cascading down a cliff into a serene lake. The waterfall, with its powerful flow, is the central focus of the video. The surrounding landscape is lush and green, with trees and foliage adding to the natural beauty of the scene. The camera angle provides a bird's eye view of the waterfall, allowing viewers to appreciate the full height and grandeur of the waterfall. The video is a stunning representation of nature's power and beauty. +A vibrant scene of a snowy mountain landscape. The sky is filled with a multitude of colorful hot air balloons, each floating at different heights, creating a dynamic and lively atmosphere. The balloons are scattered across the sky, some closer to the viewer, others further away, adding depth to the scene. Below, the mountainous terrain is blanketed in a thick layer of snow, with a few patches of bare earth visible here and there. The snow-covered mountains provide a stark contrast to the colorful balloons, enhancing the visual appeal of the scene. +A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene. +A snowy forest landscape with a dirt road running through it. The road is flanked by trees covered in snow, and the ground is also covered in snow. The sun is shining, creating a bright and serene atmosphere. The road appears to be empty, and there are no people or animals visible in the video. The style of the video is a natural landscape shot, with a focus on the beauty of the snowy forest and the peacefulness of the road. +The dynamic movement of tall, wispy grasses swaying in the wind. The sky above is filled with clouds, creating a dramatic backdrop. The sunlight pierces through the clouds, casting a warm glow on the scene. The grasses are a mix of green and brown, indicating a change in seasons. The overall style of the video is naturalistic, capturing the beauty of the landscape in a realistic manner. The focus is on the grasses and their movement, with the sky serving as a secondary element. The video does not contain any human or animal elements. \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 30d89308b..c97ec3b03 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -50,6 +50,7 @@ def getdataset(args): temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x norm_fun = ae_norm[args.ae] if args.dataset == 't2v': + resize_topcrop = [CenterCropResizeVideo((args.max_height, args.max_width), top_crop=True), ] if args.multi_scale: resize = [ LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), @@ -63,9 +64,16 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) + transform_topcrop = transforms.Compose([ + ToTensorVideo(), + *resize_topcrop, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) + return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, + transform_topcrop=transform_topcrop) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 4acddf116..9829640b1 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -101,13 +101,14 @@ def get_item(self, work_info): class T2V_dataset(Dataset): - def __init__(self, args, transform, temporal_sample, tokenizer): + def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): self.image_data = args.image_data self.video_data = args.video_data self.num_frames = args.num_frames self.use_image_num = args.use_image_num self.use_img_from_vid = args.use_img_from_vid self.transform = transform + self.transform_topcrop = transform_topcrop self.temporal_sample = temporal_sample self.tokenizer = tokenizer self.model_max_length = args.model_max_length @@ -161,7 +162,7 @@ def __getitem__(self, idx): image_data = self.get_image(idx) # 1 frame video as image return dict(video_data=video_data, image_data=image_data) except Exception as e: - # print(f'Error with {e}') + print(f'Error with {e}') # 打印异常堆栈 if idx in dataset_prog.vid_cap_list: print(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") @@ -191,6 +192,7 @@ def get_video(self, idx): video = video.transpose(0, 1) # T C H W -> C T H W text = dataset_prog.vid_cap_list[idx]['cap'] + text = [random.choice(text)] text = text_preprocessing(text, support_Chinese=self.support_Chinese) if random.random() > self.cfg else "" text_tokens_and_mask = self.tokenizer( @@ -221,19 +223,23 @@ def get_image(self, idx): image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] - for i in image: - assert not torch.any(torch.isnan(i)), 'before transform0' + # for i in image: + # assert not torch.any(torch.isnan(i)), 'before transform0' image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] - for i in image: - assert not torch.any(torch.isnan(i)), 'before transform1' - image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] - - for i in image: - assert not torch.any(torch.isnan(i)), 'after transform' + # for i in image: + # assert not torch.any(torch.isnan(i)), 'before transform1' + # for i in image: + # h, w = i.shape[-2:] + # assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only image with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {i.shape}' + + image = [self.transform_topcrop(i) if 'human_images' in j['path'] else self.transform(i) for i, j in zip(image, image_data)] # num_img [1 C H W] -> num_img [1 C H W] + + # for i in image: + # assert not torch.any(torch.isnan(i)), 'after transform' # image = [torch.rand(1, 3, 480, 640) for i in image_data] image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] - caps = [i['cap'] for i in image_data] + caps = [[random.choice(i['cap'])] for i in image_data] text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] input_ids, cond_mask = [], [] for t in text: diff --git a/opensora/dataset/transform.py b/opensora/dataset/transform.py index 50c0212b5..96a1b696d 100644 --- a/opensora/dataset/transform.py +++ b/opensora/dataset/transform.py @@ -108,7 +108,7 @@ def center_crop_using_short_edge(clip): -def center_crop_th_tw(clip, th, tw): +def center_crop_th_tw(clip, th, tw, top_crop): if not _is_tensor_video_clip(clip): raise ValueError("clip should be a 4D torch.tensor") @@ -122,7 +122,7 @@ def center_crop_th_tw(clip, th, tw): new_h = h new_w = int(h / tr) - i = int(round((h - new_h) / 2.0)) + i = 0 if top_crop else int(round((h - new_h) / 2.0)) j = int(round((w - new_w) / 2.0)) return crop(clip, i, j, new_h, new_w) @@ -307,12 +307,13 @@ class CenterCropResizeVideo: def __init__( self, size, + top_crop=False, interpolation_mode="bilinear", ): if len(size) != 2: raise ValueError(f"size should be tuple (height, width), instead got {size}") self.size = size - + self.top_crop = top_crop self.interpolation_mode = interpolation_mode def __call__(self, clip): @@ -324,7 +325,7 @@ def __call__(self, clip): size is (T, C, crop_size, crop_size) """ # clip_center_crop = center_crop_using_short_edge(clip) - clip_center_crop = center_crop_th_tw(clip, self.size[0], self.size[1]) + clip_center_crop = center_crop_th_tw(clip, self.size[0], self.size[1], top_crop=self.top_crop) # import ipdb;ipdb.set_trace() clip_center_crop_resize = resize(clip_center_crop, target_size=self.size, interpolation_mode=self.interpolation_mode) diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 488195fb5..a5c05ed2f 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -528,6 +528,9 @@ def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_th self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True) + + self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) @@ -742,6 +745,10 @@ def __call__( query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + # qk norm + query = attn.q_norm(query) + key = attn.k_norm(key) + if self.use_rope: # require the shape of (batch_size x nheads x ntokens x dim) pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py index da2321663..46afa4d37 100644 --- a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -12,7 +12,9 @@ from diffusers.models.modeling_utils import ModelMixin from diffusers.models.normalization import AdaLayerNormSingle from diffusers.models.embeddings import PixArtAlphaTextProjection -from opensora.models.diffusion.udit_ultra.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock +from opensora.models.diffusion.udit_ultra.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, \ + OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock, \ + FP32_GELU, FP32_SiLU, FP32_Layernorm from opensora.utils.utils import to_2tuple import math import re @@ -90,10 +92,12 @@ def __init__( attention_mode: str = 'xformers', downsampler: str = 'k333_s222', use_rope: bool = False, + use_stable_fp32: bool = False, ): super().__init__() # Set some common variables used across the board. + self.use_stable_fp32 = use_stable_fp32 self.use_rope = use_rope self.downsampler = downsampler self.use_linear_projection = use_linear_projection @@ -133,6 +137,8 @@ def __init__( # 2. Initialize the right blocks. # Initialize the output blocks and other projection blocks when necessary. self._init_patched_inputs(norm_type=norm_type) + if self.use_stable_fp32: + self._replace_fp32_layers() def _init_patched_inputs(self, norm_type): assert self.config.sample_size_t is not None, "OpenSoraT2V over patched input must provide sample_size_t" @@ -382,6 +388,27 @@ def _init_patched_inputs(self, norm_type): in_features=self.caption_channels, hidden_size=self.inner_dim * 4 ) + def _replace_fp32_layers(self, module=None): + if module is None: + module = self + for name, submodule in module.named_children(): + if isinstance(submodule, nn.LayerNorm): + print(f"Replacing LayerNorm in {name}") + new_layer = FP32_Layernorm(submodule.normalized_shape, submodule.eps, submodule.elementwise_affine) + if submodule.elementwise_affine: + new_layer.weight.data.copy_(submodule.weight.data.float()) + if submodule.bias is not None: + new_layer.bias.data.copy_(submodule.bias.data.float()) + setattr(module, name, new_layer) + elif isinstance(submodule, nn.SiLU): + print(f"Replacing SiLU in {name}") + setattr(module, name, FP32_SiLU(submodule.inplace)) + elif isinstance(submodule, nn.GELU): + print(f"Replacing GELU in {name}") + setattr(module, name, FP32_GELU(submodule.approximate)) + else: + self._replace_fp32_layers(submodule) + def _set_gradient_checkpointing(self, module, value=False): if hasattr(module, "gradient_checkpointing"): module.gradient_checkpointing = value @@ -857,47 +884,9 @@ def UDiTUltraT2V_XL_111(**kwargs): } - -def maybe_zero_3(param, ignore_status=False, name=None): - from deepspeed import zero - from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus - if hasattr(param, "ds_id"): - if param.ds_status == ZeroParamStatus.NOT_AVAILABLE: - if not ignore_status: - logging.warning(f"{name}: param.ds_status != ZeroParamStatus.NOT_AVAILABLE: {param.ds_status}") - with zero.GatheredParameters([param]): - param = param.data.detach().cpu().clone() - else: - param = param.detach().cpu().clone() - return param - -# Borrowed from peft.utils.get_peft_model_state_dict -def get_peft_state_maybe_zero_3(named_params, bias): - if bias == "none": - to_return = {k: t for k, t in named_params if "lora_" in k} - elif bias == "all": - to_return = {k: t for k, t in named_params if "lora_" in k or "bias" in k} - elif bias == "lora_only": - to_return = {} - maybe_lora_bias = {} - lora_bias_names = set() - for k, t in named_params: - if "lora_" in k: - to_return[k] = t - bias_name = k.split("lora_")[0] + "bias" - lora_bias_names.add(bias_name) - elif "bias" in k: - maybe_lora_bias[k] = t - for k, t in maybe_lora_bias: - if bias_name in lora_bias_names: - to_return[bias_name] = t - else: - raise NotImplementedError - to_return = {k: maybe_zero_3(v, ignore_status=True) for k, v in to_return.items()} - return to_return - if __name__ == '__main__': import sys + from copy import deepcopy from opensora.models.ae import ae_channel_config, ae_stride_config from opensora.models.ae import getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper @@ -929,8 +918,11 @@ def get_peft_state_maybe_zero_3(named_params, bias): else: num_frames = args.num_frames // ae_stride_t - device = torch.device('cuda:1') - model = UDiTUltraT2V_L_122(in_channels=c, + device = torch.device('cuda:0') + + + + model = UDiTUltraT2V_S_122(in_channels=c, out_channels=c, sample_size=latent_size, sample_size_t=num_frames, @@ -955,35 +947,35 @@ def get_peft_state_maybe_zero_3(named_params, bias): print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') # import sys;sys.exit() - try: - path = "bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" - from safetensors.torch import load_file as safe_load - ckpt = safe_load(path, device="cpu") - new_ckpt = {} - k_size = 3 - t_stride = 1 - for k, v in ckpt.items(): - if 'pos_embed.proj.weight' in k: - new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 - elif 'attn1.downsampler.layer.weight' in k: - new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 - elif 'body.0.weight' in k and 'down' in k: - in_c = v.shape[0] - new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 - elif 'body.0.weight' in k and 'up' in k: - new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 - elif 'proj_out' in k: - if 'weight' in k: - new_v = v.repeat(t_stride, 1) # 16, 768 -> 32, 768 - elif 'bias' in k: - new_v = v.repeat(t_stride) # 16 -> 32 - else: - new_v = v - new_ckpt[k] = new_v - msg = model.load_state_dict(new_ckpt, strict=False) - # print(msg) - except Exception as e: - print(e) + # try: + # path = "bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" + # from safetensors.torch import load_file as safe_load + # ckpt = safe_load(path, device="cpu") + # new_ckpt = {} + # k_size = 3 + # t_stride = 1 + # for k, v in ckpt.items(): + # if 'pos_embed.proj.weight' in k: + # new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + # elif 'attn1.downsampler.layer.weight' in k: + # new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + # elif 'body.0.weight' in k and 'down' in k: + # in_c = v.shape[0] + # new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 + # elif 'body.0.weight' in k and 'up' in k: + # new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 + # elif 'proj_out' in k: + # if 'weight' in k: + # new_v = v.repeat(t_stride, 1) # 16, 768 -> 32, 768 + # elif 'bias' in k: + # new_v = v.repeat(t_stride) # 16 -> 32 + # else: + # new_v = v + # new_ckpt[k] = new_v + # msg = model.load_state_dict(new_ckpt, strict=False) + # # print(msg) + # except Exception as e: + # print(e) x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L @@ -992,87 +984,53 @@ def get_peft_state_maybe_zero_3(named_params, bias): model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) with torch.no_grad(): - output = model(**model_kwargs) - print(output[0].shape) - sys.exit() - from peft import LoraConfig, PeftModel - from peft import get_peft_model - lora_save_path = 'lora' - lora_config = LoraConfig( - r=16, - lora_alpha=16, - init_lora_weights="gaussian", - target_modules=["to_k", "to_q", "to_v", "to_out.0"], - ) - model_lora = get_peft_model(model, lora_config) - - from copy import deepcopy - from diffusers.training_utils import EMAModel - - class EMAModel_LoRA(EMAModel): - def __init__(self, lora_config, **kwargs): - super().__init__(**kwargs) - self.lora_config = lora_config - - @classmethod - def from_pretrained(cls, path, model_cls, lora_config) -> "EMAModel": - load_config, ema_kwargs = model_cls.load_config(path, return_unused_kwargs=True) - model = model_cls.from_config(path) - model = get_peft_model(model, lora_config) - from safetensors.torch import load_file as safe_load - main_and_lora = safe_load(os.path.join(path, 'diffusion_pytorch_model.safetensors'), device="cpu") - model.base_model.model.load_state_dict(main_and_lora) - ema_model = cls(lora_config, parameters=model.parameters(), model_cls=model_cls, model_config=model.config) - - ema_model.load_state_dict(ema_kwargs) - return ema_model - - def save_pretrained(self, path): - if self.model_cls is None: - raise ValueError("`save_pretrained` can only be used if `model_cls` was defined at __init__.") - - if self.model_config is None: - raise ValueError("`save_pretrained` can only be used if `model_config` was defined at __init__.") - - model = self.model_cls.from_config(self.model_config) - model = get_peft_model(model, self.lora_config) - state_dict = self.state_dict() - state_dict.pop("shadow_params", None) - model.base_model.model.register_to_config(**state_dict) - self.copy_to(model.parameters()) - model.base_model.model.save_pretrained(path) - model.save_pretrained(path) - - - ema_model = deepcopy(model_lora) - ema_model_lora = EMAModel_LoRA(lora_config, parameters=ema_model.parameters(), update_after_step=0, model_cls=UDiTUltraT2V, model_config=ema_model.config) + output = model(**model_kwargs)[0] + print(output.shape) - for k, v in zip(ema_model_lora.shadow_params, model_lora.parameters()): - assert torch.allclose(v, k) - - ema_model_lora.save_pretrained("model_ema_lora") - - ema_model_load_lora = EMAModel_LoRA.from_pretrained("model_ema_lora", UDiTUltraT2V, lora_config) - ema_model_lora.load_state_dict(ema_model_load_lora.state_dict()) - ema_model_lora.to(device) - - state_dict = get_peft_state_maybe_zero_3(model_lora.named_parameters(), lora_config.bias) - model_lora.save_pretrained(lora_save_path, state_dict=state_dict) - model_load_lora = PeftModel.from_pretrained(model, lora_save_path) - for k, v in model_load_lora.state_dict().items(): - assert torch.allclose(v, model_lora.state_dict()[k]) - - for k, v in zip(ema_model_lora.shadow_params, model_lora.parameters()): - assert torch.allclose(v, k) - - print('Merging LoRA weights...') - model_load_lora_merge = model_load_lora.merge_and_unload() - with torch.no_grad(): - output = model_lora(**model_kwargs) - print(output[0].shape) + # from peft import LoraConfig, PeftModel, get_peft_model + # from opensora.utils.lora_utils import EMAModel_LoRA, maybe_zero_3, get_peft_state_maybe_zero_3 + # lora_save_path = '/storage/ongoing/new/Open-Sora-Plan/debug_lora/model_lora' + # ema_lora_save_path = '/storage/ongoing/new/Open-Sora-Plan/debug_lora/ema_model_lora' + # origin_model_path = '/storage/ongoing/new/Open-Sora-Plan/bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl/checkpoint-500/model_ema' + # model = UDiTUltraT2V.from_pretrained(origin_model_path) + # lora_config = LoraConfig( + # r=64, + # lora_alpha=64, + # init_lora_weights="gaussian", + # target_modules=["to_k", "to_q", "to_v", "to_out.0"], + # ) + # model_lora = get_peft_model(model, lora_config) + # # --------------------ema lora_model---------------------------------- + # # create ema lora_model + # ema_model = deepcopy(model_lora) + # ema_model_lora = EMAModel_LoRA(lora_config, parameters=ema_model.parameters(), update_after_step=0, + # model_cls=UDiTUltraT2V, model_config=ema_model.config) + # ema_model_lora.save_pretrained(ema_lora_save_path) + # ema_model_load_lora = EMAModel_LoRA.from_pretrained(ema_lora_save_path, UDiTUltraT2V, lora_config, origin_model_path) + # ema_model_lora.load_state_dict(ema_model_load_lora.state_dict()) + # ema_model_lora.to(device) + + # # -----------------lora model--------------------------------- + # # get lora weight + # model_lora.save_pretrained(lora_save_path) + # # ----------------load lora model------------------------------ + # # load lora weight + # model = UDiTUltraT2V.from_pretrained(origin_model_path) + # import ipdb;ipdb.set_trace() + # model_load_lora = PeftModel.from_pretrained(model, lora_save_path) + # for k, v in model_load_lora.state_dict().items(): + # assert torch.allclose(v, model_lora.state_dict()[k]) + # # for k, v in zip(ema_model_lora.shadow_params, model_lora.parameters()): + # # assert torch.allclose(v, k) + # print('Merging LoRA weights...') + # import ipdb;ipdb.set_trace() + # model_load_lora_merge = model_load_lora.merge_and_unload() + # with torch.no_grad(): + # output = model_load_lora_merge(**model_kwargs) + # print(output[0].shape) diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index 33e23e51e..a25d1452d 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -134,6 +134,21 @@ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) return emb +class FP32_Layernorm(nn.LayerNorm): + def forward(self, inputs: torch.Tensor) -> torch.Tensor: + origin_dtype = inputs.dtype + return F.layer_norm(inputs.float(), self.normalized_shape, self.weight.float() if self.weight is not None else None, + self.bias.float() if self.bias is not None else None, self.eps).to(origin_dtype) + + +class FP32_SiLU(nn.SiLU): + def forward(self, inputs: torch.Tensor) -> torch.Tensor: + return torch.nn.functional.silu(inputs.float(), inplace=self.inplace).to(inputs.dtype) + + +class FP32_GELU(nn.GELU): + def forward(self, inputs: torch.Tensor) -> torch.Tensor: + return torch.nn.functional.gelu(inputs.float(), approximate=self.approximate).to(inputs.dtype) @@ -385,8 +400,8 @@ def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_th padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True) - # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) - # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) class DownSampler3d(nn.Module): @@ -571,18 +586,18 @@ def __call__( key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + # qk norm + query = attn.q_norm(query) + key = attn.k_norm(key) + if self.use_rope: # require the shape of (batch_size x nheads x ntokens x dim) pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) query = self.rope(query, pos_thw) key = self.rope(key, pos_thw) - + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - # qk norm - # query = attn.q_norm(query) - # key = attn.k_norm(key) - # query = query * (head_dim ** -0.5) if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible attention_mask = None @@ -774,7 +789,7 @@ def __init__(self, downsampler, dim, hidden_features, bias=True): self.bias = bias self.project_in = nn.Linear(dim, hidden_features, bias=bias) - + self.act = nn.GELU() self.dwconv = nn.ModuleList([ nn.Conv2d(hidden_features, hidden_features, kernel_size=(5, 5), stride=1, padding=(2, 2), dilation=1, groups=hidden_features, bias=bias), @@ -791,7 +806,8 @@ def forward(self, x, t, h, w): # import ipdb;ipdb.set_trace() x = self.project_in(x) x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) - x = F.gelu(x) + + x = self.act(x) out = x for module in self.dwconv: out = out + module(x) diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 629c07f93..854a4ac8d 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -445,7 +445,7 @@ def _clean_caption(self, caption): caption = re.sub(r"[\u3300-\u33ff]+", "", caption) caption = re.sub(r"[\u3400-\u4dbf]+", "", caption) caption = re.sub(r"[\u4dc0-\u4dff]+", "", caption) - caption = re.sub(r"[\u4e00-\u9fff]+", "", caption) + # caption = re.sub(r"[\u4e00-\u9fff]+", "", caption) ####################################################### # все виды тире / all types of dash --> "-" @@ -521,7 +521,6 @@ def _clean_caption(self, caption): caption = re.sub(r"^[\'\_,\-\:;]", r"", caption) caption = re.sub(r"[\'\_,\-\:\-\+]$", r"", caption) caption = re.sub(r"^\.\S+$", "", caption) - return caption.strip() # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents @@ -790,7 +789,7 @@ def __call__( latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] # npu_config.print_tensor_stats(latents, f"latents_i{i}_t{t}", rank=0) assert not torch.isnan(latents).any().item(), "latents contains NaN values" - + # print(f'latents_{i}_{t}', torch.max(latents), torch.min(latents), torch.mean(latents), torch.std(latents)) # call the callback, if provided if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): progress_bar.update() @@ -818,9 +817,11 @@ def __call__( def decode_latents(self, latents): if npu_config: npu_config.print_tensor_stats(latents, f"before vae", rank=0) + # print(f'before vae decode', torch.max(latents).item(), torch.min(latents).item(), torch.mean(latents).item(), torch.std(latents).item()) video = self.vae.decode(latents.to(self.vae.vae.dtype)) if npu_config: npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) + # print(f'after vae decode', torch.max(video).item(), torch.min(video).item(), torch.mean(video).item(), torch.std(video).item()) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 return video diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 8352228b2..e7faea473 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -12,7 +12,7 @@ from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel from omegaconf import OmegaConf from torchvision.utils import save_image -from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer +from transformers import T5EncoderModel, MT5EncoderModel, UMT5EncoderModel, AutoTokenizer import os, sys @@ -22,6 +22,7 @@ from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V from opensora.models.diffusion.latte.modeling_latte import LatteT2V from opensora.models.diffusion.udit.modeling_udit import UDiTT2V +from opensora.models.diffusion.udit_ultra.modeling_udit_ultra import UDiTUltraT2V from opensora.models.text_encoder import get_text_enc from opensora.utils.utils import save_video_grid @@ -33,8 +34,7 @@ def main(args): # torch.manual_seed(args.seed) - replace_with_fp32_forwards() - weight_dtype = torch.float16 + weight_dtype = torch.bfloat16 device = torch.device(args.device) # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) @@ -53,16 +53,16 @@ def main(args): if args.model_3d: # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, # low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - transformer_model = UDiTT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, + transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) - # tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - text_encoder = T5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) - tokenizer = T5Tokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + # text_encoder = UMT5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + text_encoder = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) # set eval mode @@ -77,6 +77,20 @@ def main(args): elif args.sample_method == 'DDPM': ############# scheduler = DDPMScheduler(clip_sample=False) elif args.sample_method == 'DPMSolverMultistep': + ''' + DPM++ 2M DPMSolverMultistepScheduler + DPM++ 2M Karras DPMSolverMultistepScheduler init with use_karras_sigmas=True + DPM++ 2M SDE DPMSolverMultistepScheduler init with algorithm_type="sde-dpmsolver++" + DPM++ 2M SDE Karras DPMSolverMultistepScheduler init with use_karras_sigmas=True and algorithm_type="sde-dpmsolver++" + + DPM++ SDE DPMSolverSinglestepScheduler + DPM++ SDE Karras DPMSolverSinglestepScheduler init with use_karras_sigmas=True + DPM2 KDPM2DiscreteScheduler + DPM2 Karras KDPM2DiscreteScheduler init with use_karras_sigmas=True + DPM2 a KDPM2AncestralDiscreteScheduler + DPM2 a Karras KDPM2AncestralDiscreteScheduler init with use_karras_sigmas=True + ''' + # scheduler = DPMSolverMultistepScheduler(use_karras_sigmas=True) scheduler = DPMSolverMultistepScheduler() elif args.sample_method == 'DPMSolverSinglestep': scheduler = DPMSolverSinglestepScheduler() @@ -93,7 +107,6 @@ def main(args): elif args.sample_method == 'EulerDiscreteSVD': scheduler = EulerDiscreteScheduler.from_pretrained("stabilityai/stable-video-diffusion-img2vid", subfolder="scheduler", cache_dir=args.cache_dir) - pipeline = OpenSoraPipeline(vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, @@ -111,19 +124,23 @@ def main(args): text_prompt = open(args.text_prompt[0], 'r').readlines() text_prompt = [i.strip() for i in text_prompt] + positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" + negative_prompt = """nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, + """ video_grids = [] for idx, prompt in enumerate(text_prompt): - videos = pipeline(prompt, - num_frames=args.num_frames, - height=args.height, - width=args.width, - num_inference_steps=args.num_sampling_steps, - guidance_scale=args.guidance_scale, - num_images_per_prompt=1, - mask_feature=True, - device=args.device, - max_sequence_length=100, - ).images + videos = pipeline(positive_prompt.format(prompt), + negative_prompt=negative_prompt, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device, + max_sequence_length=200, + ).images try: if args.num_frames == 1: ext = 'jpg' diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 62d1054f0..aed260fd3 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -72,10 +72,15 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): validation_prompt = [ - "a word \"你好\" in the blackboard", "a cat wearing sunglasses and working as a lifeguard at pool.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." ] + if 'mt5' in args.text_encoder_name: + validation_prompt_cn = [ + "一只戴着墨镜在泳池当救生员的猫咪。", + "这是一个宁静的水下场景,一只海龟游过珊瑚礁。海龟带着绿褐色的龟壳,优雅地游向画面右侧,成为视频的焦点。背景中的珊瑚礁生机盎然,为海龟的旅程提供了生动多彩的背景。几条小鱼在海龟周围穿梭,为画面增添了动感和活力。" + ] + validation_prompt += validation_prompt_cn logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) scheduler = DPMSolverMultistepScheduler() @@ -269,6 +274,7 @@ def main(args): # compress_kv_factor=args.compress_kv_factor, use_rope=args.use_rope, # model_max_length=args.model_max_length, + use_stable_fp32=args.enable_stable_fp32, ) model.gradient_checkpointing = args.gradient_checkpointing @@ -430,7 +436,7 @@ def load_model_hook(models, input_dir): collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, - # prefetch_factor=8 + prefetch_factor=8 ) # Scheduler and math around the number of training steps. @@ -802,7 +808,7 @@ def train_all_epoch(prof_=None): # text encoder & vae & diffusion model parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") parser.add_argument('--enable_8bit_t5', action='store_true') - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--tile_overlap_factor', type=float, default=0.125) parser.add_argument('--enable_tiling', action='store_true') parser.add_argument("--compress_kv", action="store_true") parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") @@ -817,6 +823,7 @@ def train_all_epoch(prof_=None): parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument('--enable_stable_fp32', action='store_true') parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") # diffusion setting @@ -827,8 +834,8 @@ def train_all_epoch(prof_=None): parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") # validation & logs - parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--num_sampling_steps", type=int, default=20) + parser.add_argument('--guidance_scale', type=float, default=2.5) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") diff --git a/opensora/train/train_t2v_diffusers_lora.py b/opensora/train/train_t2v_diffusers_lora.py index 05f505ca9..06ef53734 100644 --- a/opensora/train/train_t2v_diffusers_lora.py +++ b/opensora/train/train_t2v_diffusers_lora.py @@ -42,6 +42,8 @@ from tqdm.auto import tqdm from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer +from peft import LoraConfig, PeftModel, get_peft_model + import diffusers from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler from diffusers.optimization import get_scheduler @@ -54,6 +56,7 @@ from opensora.models.diffusion.latte.modeling_latte import LatteT2V from opensora.models.text_encoder import get_text_enc, get_text_warpper from opensora.utils.dataset_utils import Collate +from opensora.utils.lora_utils import EMAModel_LoRA, maybe_zero_3, get_peft_state_maybe_zero_3 from opensora.models.ae import ae_stride_config, ae_channel_config from opensora.models.diffusion import Diffusion_models, Diffusion_models_class from opensora.sample.pipeline_opensora import OpenSoraPipeline @@ -67,10 +70,15 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): validation_prompt = [ - "a word \"你好\" in the blackboard", "a cat wearing sunglasses and working as a lifeguard at pool.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." ] + if 'mt5' in args.text_encoder_name: + validation_prompt_cn = [ + "一只戴着墨镜在泳池当救生员的猫咪。", + "这是一个宁静的水下场景,一只海龟游过珊瑚礁。海龟带着绿褐色的龟壳,优雅地游向画面右侧,成为视频的焦点。背景中的珊瑚礁生机盎然,为海龟的旅程提供了生动多彩的背景。几条小鱼在海龟周围穿梭,为画面增添了动感和活力。" + ] + validation_prompt += validation_prompt_cn logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) scheduler = DPMSolverMultistepScheduler() @@ -257,8 +265,9 @@ def main(args): interpolation_scale_t=args.interpolation_scale_t, downsampler=args.downsampler, # compress_kv_factor=args.compress_kv_factor, - # use_rope=args.use_rope, + use_rope=args.use_rope, # model_max_length=args.model_max_length, + use_stable_fp32=args.enable_stable_fp32, ) model.gradient_checkpointing = args.gradient_checkpointing @@ -316,7 +325,7 @@ def main(args): # Create EMA for the unet. if args.use_ema: ema_model = deepcopy(model) - if args.enable_ema: + if args.enable_lora: # ema the whole lora_model ema_model = EMAModel_LoRA(lora_config, parameters=ema_model.parameters(), update_after_step=args.ema_start_step, model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) else: @@ -330,15 +339,14 @@ def main(args): def save_model_hook(models, weights, output_dir): if accelerator.is_main_process: if args.use_ema: - if args.enable_lora: + if args.enable_lora: # only save lora weight ema_model.save_pretrained(os.path.join(output_dir, "model_ema_lora")) else: ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) for i, model in enumerate(models): - if args.enable_lora: - state_dict = get_peft_state_maybe_zero_3(model.named_parameters(), lora_config.bias) - model.save_pretrained(os.path.join(output_dir, "model_lora"), state_dict=state_dict) + if args.enable_lora: # only save lora weight + model.save_pretrained(os.path.join(output_dir, "model_lora")) else: model.save_pretrained(os.path.join(output_dir, "model")) if weights: # Don't pop if empty @@ -348,7 +356,8 @@ def save_model_hook(models, weights, output_dir): def load_model_hook(models, input_dir): if args.use_ema: if args.enable_lora: - load_model = EMAModel_LoRA.from_pretrained(os.path.join(input_dir, "model_ema_lora"), Diffusion_models_class[args.model], lora_config) + load_model = EMAModel_LoRA.from_pretrained(os.path.join(input_dir, "model_ema_lora"), Diffusion_models_class[args.model], + lora_config, os.path.splitext(args.pretrained)) ema_model.load_state_dict(load_model.state_dict()) ema_model.to(accelerator.device) del load_model @@ -455,7 +464,7 @@ def load_model_hook(models, input_dir): collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, - prefetch_factor=8 + prefetch_factor=4 ) # Scheduler and math around the number of training steps. @@ -663,8 +672,6 @@ def run(model_input, model_kwargs, prof): optimizer.step() lr_scheduler.step() optimizer.zero_grad() - if prof is not None: - prof.step() if accelerator.sync_gradients: sync_gradients_info(loss) @@ -696,6 +703,10 @@ def run(model_input, model_kwargs, prof): # Switch back to the original UNet parameters. ema_model.restore(model.parameters()) + if prof is not None: + prof.step() + + return loss def train_one_step(step_, data_item_, prof_=None): @@ -823,6 +834,9 @@ def train_all_epoch(prof_=None): parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument('--enable_stable_fp32', action='store_true') + parser.add_argument("--enable_lora", action="store_true") + parser.add_argument('--rank', type=int, default=64) parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") # diffusion setting diff --git a/opensora/utils/lora_utils.py b/opensora/utils/lora_utils.py new file mode 100644 index 000000000..753dd959a --- /dev/null +++ b/opensora/utils/lora_utils.py @@ -0,0 +1,82 @@ + +from peft import get_peft_model, PeftModel +import os +from copy import deepcopy +import torch +from diffusers.training_utils import EMAModel + +class EMAModel_LoRA(EMAModel): + def __init__(self, lora_config, **kwargs): + super().__init__(**kwargs) + self.lora_config = lora_config + + @classmethod + def from_pretrained(cls, path, model_cls, lora_config, origin_model_path) -> "EMAModel": + # 1. load origin model + origin_model = model_cls.from_pretrained(origin_model_path) # origin_model + # 2. convert to lora model automatically and load lora weight + lora_model = PeftModel.from_pretrained(origin_model, path) # lora_origin_model + # 3. ema the whole lora_model + ema_model = cls(lora_config, parameters=lora_model.parameters(), model_cls=model_cls, model_config=origin_model.config) + # 4. load ema_kwargs, e.g decay... + ema_kwargs = torch.load(os.path.join(path, 'ema_kwargs.pt')) + ema_model.load_state_dict(ema_kwargs) + return ema_model + + def save_pretrained(self, path): + if self.model_cls is None: + raise ValueError("`save_pretrained` can only be used if `model_cls` was defined at __init__.") + + if self.model_config is None: + raise ValueError("`save_pretrained` can only be used if `model_config` was defined at __init__.") + # 1. init a base model randomly + model = self.model_cls.from_config(self.model_config) + # 2. convert lora_model + lora_model = get_peft_model(model, self.lora_config) + # 3. ema_lora_model weight to lora_model + self.copy_to(lora_model.parameters()) + # 4. save lora weight + lora_model.save_pretrained(path) # only lora weight + # 5. save ema_kwargs, e.g decay... + state_dict = self.state_dict() # lora_model weight + state_dict.pop("shadow_params", None) + torch.save(state_dict, os.path.join(path, 'ema_kwargs.pt')) + + +def maybe_zero_3(param, ignore_status=False, name=None): + from deepspeed import zero + from deepspeed.runtime.zero.partition_parameters import ZeroParamStatus + if hasattr(param, "ds_id"): + if param.ds_status == ZeroParamStatus.NOT_AVAILABLE: + if not ignore_status: + logging.warning(f"{name}: param.ds_status != ZeroParamStatus.NOT_AVAILABLE: {param.ds_status}") + with zero.GatheredParameters([param]): + param = param.data.detach().cpu().clone() + else: + param = param.detach().cpu().clone() + return param + +# Borrowed from peft.utils.get_peft_model_state_dict +def get_peft_state_maybe_zero_3(named_params, bias): + if bias == "none": + to_return = {k: t for k, t in named_params if "lora_" in k} + elif bias == "all": + to_return = {k: t for k, t in named_params if "lora_" in k or "bias" in k} + elif bias == "lora_only": + to_return = {} + maybe_lora_bias = {} + lora_bias_names = set() + for k, t in named_params: + if "lora_" in k: + to_return[k] = t + bias_name = k.split("lora_")[0] + "bias" + lora_bias_names.add(bias_name) + elif "bias" in k: + maybe_lora_bias[k] = t + for k, t in maybe_lora_bias: + if bias_name in lora_bias_names: + to_return[bias_name] = t + else: + raise NotImplementedError + to_return = {k: maybe_zero_3(v, ignore_status=True) for k, v in to_return.items()} + return to_return diff --git a/opensora/utils/utils.py b/opensora/utils/utils.py index b5df91934..7cd353662 100644 --- a/opensora/utils/utils.py +++ b/opensora/utils/utils.py @@ -465,7 +465,7 @@ def clean_caption(caption, support_Chinese=True): if __name__ == '__main__': # caption = re.sub(r'[\u4e00-\u9fff]+', '', caption) - a = "Below that, there is another line of text that says \"\u5c0f\u989d\u6148\u5584\u51a0\u540d\u57fa\u91d1\u7b7e\u5b57\u4eea\u5f0f,\" which translates to \"Small Charity Crown Name Fund Signature Ceremony.\" The names \"\u4eba\u58eb\u738b\u519b\" are also visible, which translates to \"Person King Wu,\" likely indicating the name of one of the individuals or a title. In the bottom right corner, there is a logo with the text \"\u9655\u897f\u5934\u6761@\u897f\u90e8\u7f51,\" which translates to \"Shaanxi Headline@West China Network,\" suggesting that this event is being covered or sponsored by a media outlet or news network. The overall setting suggests a formal agreement or partnership signing between the two individuals, possibly related to the charity fund mentioned on the curtain." + a = "امرأة مسنة بشعر أبيض ووجه مليء بالتجاعيد تجلس داخل سيارة قديمة الطراز، تنظر من خلال النافذة الجانبية بتعبير تأملي أو حزين قليلاً." print(a) print(text_preprocessing(a)) diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index fa2111df9..f1782a67a 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,5 +1,5 @@ -CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs8_4node_480p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_qknorm_mt5xxl/checkpoint-8000/model \ +CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-23500/model_ema \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ @@ -9,10 +9,9 @@ CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_image480p_ps22_ds11_udit_ckpt19k" \ + --save_img_path "./sample_image_fp32_23500_cfg2.5_step20_480p_pos_neg" \ --fps 24 \ - --guidance_scale 5.0 \ - --num_sampling_steps 50 \ - --enable_tiling \ - --sample_method DDPM \ + --guidance_scale 2.5 \ + --num_sampling_steps 20 \ + --sample_method DPMSolverMultistep \ --model_3d \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index 93625a5a1..172341f1f 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -2,17 +2,17 @@ CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_lr1e-4_snr5_ema_ps11_ds22/checkpoint-8000/model \ --version 65x512x512 \ --num_frames 1 \ - --height 256 \ - --width 256 \ + --height 240 \ + --width 320 \ --cache_dir "cache_dir" \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --text_encoder_name google/mt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8" \ + --ae_path "/storage/dataset/test140k" \ --save_img_path "./sample_image256_256_ps22_ds11_ckpt8000" \ --fps 24 \ --guidance_scale 2.0 \ - --num_sampling_steps 20 \ + --num_sampling_steps 50 \ --enable_tiling \ - --sample_method DDPM \ + --sample_method PNDM \ --model_3d \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope.sh b/scripts/text_condition/gpu/train_imageuditultra_240p_new_rope_fp32.sh similarity index 82% rename from scripts/text_condition/gpu/train_imageuditultra_480p_new_rope.sh rename to scripts/text_condition/gpu/train_imageuditultra_240p_new_rope_fp32.sh index d6c3820df..4c391ccb8 100644 --- a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope.sh +++ b/scripts/text_condition/gpu/train_imageuditultra_240p_new_rope_fp32.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl" +export PROJECT="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjcn_czhan" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 # NCCL setting @@ -19,17 +19,17 @@ accelerate launch \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ --video_data "scripts/train_data/video_data.txt" \ --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ --num_frames 1 \ --max_height 240 \ --max_width 320 \ --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=16 \ @@ -42,20 +42,19 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ - --enable_tracker \ - --enable_tiling \ --tile_overlap_factor 0.125 \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ --noise_offset 0.02 \ + --use_rope \ --downsampler "k33_s22" \ --resume_from_checkpoint="latest" \ - --checkpoints_total_limit 3 \ - --use_rope \ - --pretrained "bs16_4node_480p_lr2e-5_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl/checkpoint-19000/model/diffusion_pytorch_model.safetensors" \ No newline at end of file + --enable_tracker \ + --enable_stable_fp32 \ + --pretrained "bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjcn_czhan/checkpoint-26500/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh new file mode 100644 index 000000000..025d71b67 --- /dev/null +++ b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh @@ -0,0 +1,61 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs16_4node_240p_lr2e-5_snr5_noioff0.01_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mj_mjcn_czhan" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 16 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" \ + --enable_tracker \ + --enable_stable_fp32 \ + --pretrained "bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-30000/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh new file mode 100644 index 000000000..e37a807ab --- /dev/null +++ b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh @@ -0,0 +1,59 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/debug.yaml \ + opensora/train/train_t2v_diffusers_lora.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data_debug.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --enable_lora \ + --sample_rate 1 \ + --num_frames 1 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=16 \ + --dataloader_num_workers 0 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=5 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k33_s22" \ + --resume_from_checkpoint="latest" \ + --enable_tracker \ + --output_dir="debug_lora" \ No newline at end of file diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index b404a2d2a..dab94eee6 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,4 +1,3 @@ -/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json -/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json -/storage/dataset/image/anytext3m,/storage/anno_jsons/anytext_all_3509994.json -/storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json \ No newline at end of file +/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265_en_cn.json +/storage/dataset/civitai/Images_civitai_v1,/storage/anno_jsons/civitai_v1_1940032.json +/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json \ No newline at end of file From 275f04a3a0b758ee12335e8017cb60fbc5cd67b3 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 27 Jun 2024 07:01:53 +0000 Subject: [PATCH 066/134] fix pad bug --- .gitignore | 3 +- .../udit_ultra/modeling_udit_ultra.py | 44 +++++++++---------- .../models/diffusion/udit_ultra/modules.py | 2 +- opensora/sample/pipeline_opensora.py | 8 ++-- opensora/sample/sample_t2v.py | 3 ++ opensora/train/train_t2v_diffusers.py | 3 +- scripts/text_condition/gpu/sample_image.sh | 7 +-- 7 files changed, 38 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index b584b40ee..25a463aea 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ sft* flash* 65x256* alpha_vae -*node* \ No newline at end of file +*node* +sample_image*cfg* \ No newline at end of file diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py index 46afa4d37..8fd772356 100644 --- a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -137,8 +137,8 @@ def __init__( # 2. Initialize the right blocks. # Initialize the output blocks and other projection blocks when necessary. self._init_patched_inputs(norm_type=norm_type) - if self.use_stable_fp32: - self._replace_fp32_layers() + # if self.use_stable_fp32: + # self._replace_fp32_layers() def _init_patched_inputs(self, norm_type): assert self.config.sample_size_t is not None, "OpenSoraT2V over patched input must provide sample_size_t" @@ -388,26 +388,26 @@ def _init_patched_inputs(self, norm_type): in_features=self.caption_channels, hidden_size=self.inner_dim * 4 ) - def _replace_fp32_layers(self, module=None): - if module is None: - module = self - for name, submodule in module.named_children(): - if isinstance(submodule, nn.LayerNorm): - print(f"Replacing LayerNorm in {name}") - new_layer = FP32_Layernorm(submodule.normalized_shape, submodule.eps, submodule.elementwise_affine) - if submodule.elementwise_affine: - new_layer.weight.data.copy_(submodule.weight.data.float()) - if submodule.bias is not None: - new_layer.bias.data.copy_(submodule.bias.data.float()) - setattr(module, name, new_layer) - elif isinstance(submodule, nn.SiLU): - print(f"Replacing SiLU in {name}") - setattr(module, name, FP32_SiLU(submodule.inplace)) - elif isinstance(submodule, nn.GELU): - print(f"Replacing GELU in {name}") - setattr(module, name, FP32_GELU(submodule.approximate)) - else: - self._replace_fp32_layers(submodule) + # def _replace_fp32_layers(self, module=None): + # if module is None: + # module = self + # for name, submodule in module.named_children(): + # if isinstance(submodule, nn.LayerNorm): + # print(f"Replacing LayerNorm in {name}") + # new_layer = FP32_Layernorm(submodule.normalized_shape, submodule.eps, submodule.elementwise_affine) + # if submodule.elementwise_affine: + # new_layer.weight.data.copy_(submodule.weight.data.float()) + # if submodule.bias is not None: + # new_layer.bias.data.copy_(submodule.bias.data.float()) + # setattr(module, name, new_layer) + # elif isinstance(submodule, nn.SiLU): + # print(f"Replacing SiLU in {name}") + # setattr(module, name, FP32_SiLU(submodule.inplace)) + # elif isinstance(submodule, nn.GELU): + # print(f"Replacing GELU in {name}") + # setattr(module, name, FP32_GELU(submodule.approximate)) + # else: + # self._replace_fp32_layers(submodule) def _set_gradient_checkpointing(self, module, value=False): if hasattr(module, "gradient_checkpointing"): diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index a25d1452d..f5b2e3d10 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -748,7 +748,7 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = F.pad(x, (0, pad_w, 0, pad_h)) else: x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) - x = F.pad(x, (0, pad_w, 0, pad_h)) + # x = F.pad(x, (0, pad_w, 0, pad_h)) x = self.body(x) x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 854a4ac8d..6c98105da 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -815,12 +815,12 @@ def __call__( def decode_latents(self, latents): - if npu_config: - npu_config.print_tensor_stats(latents, f"before vae", rank=0) + # if npu_config: + # npu_config.print_tensor_stats(latents, f"before vae", rank=0) # print(f'before vae decode', torch.max(latents).item(), torch.min(latents).item(), torch.mean(latents).item(), torch.std(latents).item()) video = self.vae.decode(latents.to(self.vae.vae.dtype)) - if npu_config: - npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) + # if npu_config: + # npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) # print(f'after vae decode', torch.max(video).item(), torch.min(video).item(), torch.mean(video).item(), torch.std(video).item()) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index e7faea473..07c4c6cf3 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -34,6 +34,8 @@ def main(args): # torch.manual_seed(args.seed) + if args.enable_stable_fp32: + replace_with_fp32_forwards() weight_dtype = torch.bfloat16 device = torch.device(args.device) @@ -190,6 +192,7 @@ def main(args): parser.add_argument('--tile_overlap_factor', type=float, default=0.25) parser.add_argument('--enable_tiling', action='store_true') parser.add_argument('--model_3d', action='store_true') + parser.add_argument('--enable_stable_fp32', action='store_true') args = parser.parse_args() main(args) \ No newline at end of file diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index aed260fd3..dce4c8cb6 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -163,7 +163,8 @@ def main(args): logging_dir = Path(args.output_dir, args.logging_dir) # use LayerNorm, GeLu, SiLu always as fp32 mode - replace_with_fp32_forwards() + if args.enable_stable_fp32: + replace_with_fp32_forwards() if torch_npu is not None and npu_config is not None: npu_config.print_msg(args) npu_config.seed_everything(args.seed) diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index f1782a67a..9610fb56e 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,5 +1,5 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-23500/model_ema \ + --model_path bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-27500/model_ema \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ @@ -9,9 +9,10 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v.py \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_image_fp32_23500_cfg2.5_step20_480p_pos_neg" \ + --save_img_path "./sample_image_27500_cfg2.5_step20_480p_pos_neg_fp32" \ --fps 24 \ --guidance_scale 2.5 \ --num_sampling_steps 20 \ --sample_method DPMSolverMultistep \ - --model_3d \ No newline at end of file + --model_3d \ + --enable_stable_fp32 \ No newline at end of file From 314b266fb1dbeccb292f6a34b0d6424deed4c666 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Mon, 1 Jul 2024 06:51:46 +0000 Subject: [PATCH 067/134] vip ok --- opensora/dataset/__init__.py | 24 +- opensora/dataset/inpaint_datasets.py | 5 +- opensora/dataset/videoip_datasets.py | 172 +++-- .../diffusion/latte/modeling_for_vip.py | 321 +++++++++ .../diffusion/latte/modeling_inpaint.py | 6 +- .../models/diffusion/latte/modeling_latte.py | 60 +- opensora/models/diffusion/latte/modules.py | 5 +- opensora/models/diffusion/latte/videoip.py | 344 ++++++++- opensora/sample/pipeline_for_vip.py | 211 ++++++ opensora/sample/pipeline_inpaint.py | 27 +- opensora/sample/pipeline_opensora.py | 1 - opensora/sample/sample_inpaint.py | 263 +++++++ opensora/train/train_inpaint.py | 20 +- opensora/train/train_videoip.py | 660 +++++++++++++----- opensora/utils/dataset_utils.py | 31 + .../deepspeed_zero2_config.yaml | 4 +- .../gpu/sample_video_inpaint.sh | 16 + .../gpu/train_inpaint_video21d_65x512x512.sh | 22 +- .../gpu/train_video_ip_video21d.sh | 66 ++ scripts/train_data/image_data.txt | 2 - scripts/train_data/image_data_debug.txt | 2 +- scripts/train_data/video_data.txt | 5 +- validation_dir/prompt.txt | 7 +- 23 files changed, 1997 insertions(+), 277 deletions(-) create mode 100644 opensora/models/diffusion/latte/modeling_for_vip.py create mode 100644 opensora/sample/pipeline_for_vip.py create mode 100644 opensora/sample/sample_inpaint.py create mode 100644 scripts/text_condition/gpu/sample_video_inpaint.sh create mode 100644 scripts/text_condition/gpu/train_video_ip_video21d.sh diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 6a3a2924f..2fef3baee 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -1,5 +1,5 @@ from torchvision.transforms import Compose -from transformers import AutoTokenizer +from transformers import AutoTokenizer, AutoImageProcessor from torchvision import transforms from torchvision.transforms import Lambda @@ -8,6 +8,8 @@ from .inpaint_datasets import Inpaint_dataset +from .videoip_datasets import VideoIP_dataset + from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo @@ -87,5 +89,23 @@ def getdataset(args): ]) tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer) - + elif args.dataset == 'vip': + if args.multi_scale: + resize = [ + LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), + SpatialStrideCropVideo(args.stride) + ] + else: + resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] + resize_transform = transforms.Compose(resize) + transform = transforms.Compose([ + ToTensorVideo(), + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + image_processor = AutoImageProcessor.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + return VideoIP_dataset(args, transform=transform, resize_transform=resize_transform, temporal_sample=temporal_sample, tokenizer=tokenizer, image_processor=image_processor) + + raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/inpaint_datasets.py b/opensora/dataset/inpaint_datasets.py index 967d5976f..9d509dea3 100644 --- a/opensora/dataset/inpaint_datasets.py +++ b/opensora/dataset/inpaint_datasets.py @@ -1,5 +1,6 @@ import json import os, io, csv, math, random +from turtle import width import numpy as np import torchvision from einops import rearrange @@ -117,7 +118,8 @@ def get_video(self, idx): return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) def get_mask_masked_video(self, video): - mask = torch.zeros_like(video) + channels, frame, height, width = video.shape + mask = torch.zeros([1, frame, height, width]) rand_num = random.random() # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. @@ -170,6 +172,7 @@ def get_vid_cap_list(self): path = opj(folder, vid_cap_list[i]['path']) # For testing, some data has not been utilized. if not os.path.exists(path) and not os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): + print(path) break elif os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): path = path.replace('.mp4', '_resize1080p.mp4') diff --git a/opensora/dataset/videoip_datasets.py b/opensora/dataset/videoip_datasets.py index 27fed8c22..cf3030780 100644 --- a/opensora/dataset/videoip_datasets.py +++ b/opensora/dataset/videoip_datasets.py @@ -41,9 +41,24 @@ def random_video_noise(t, c, h, w): vid = vid.to(torch.uint8) return vid +def save_video(video, save_path='output_video.mp4', fps=24): + import cv2 + frame_count, height, width, channels = video.shape + + fourcc = cv2.VideoWriter_fourcc(*'mp4v') + out = cv2.VideoWriter(save_path, fourcc, fps, (width, height)) + + for i in range(frame_count): + frame = video[i].cpu().numpy() + frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) + out.write(frame) + +# num_frames == 1: only image +# num_frames > 1 and use_image_num == 0: only video +# num_frames > 1 and use_image_num > 0: video and image class VideoIP_dataset(Dataset): - def __init__(self, args, transform, temporal_sample, tokenizer): + def __init__(self, args, transform, resize_transform, temporal_sample, tokenizer, image_processor): self.image_data = args.image_data self.video_data = args.video_data self.num_frames = args.num_frames @@ -56,6 +71,9 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.cfg = args.cfg self.v_decoder = DecordInit() + self.resize_transform = resize_transform + self.image_processor = image_processor + self.support_Chinese = True if not ('mt5' in args.text_encoder_name): self.support_Chinese = False @@ -70,12 +88,14 @@ def __init__(self, args, transform, temporal_sample, tokenizer): self.img_cap_list = self.get_img_cap_list() self.vid_cap_list = [] - # inpaint - # The proportion of executing the i2v task. - self.i2v_ratio = args.i2v_ratio - self.transition_ratio = args.transition_ratio - self.clear_video_ratio = args.clear_video_ratio - assert self.i2v_ratio + self.transition_ratio + self.clear_video_ratio < 1, 'The sum of i2v_ratio, transition_ratio and clear video ratio should be less than 1.' + if self.num_frames != 1: + # inpaint + # The proportion of executing the i2v task. + self.i2v_ratio = args.i2v_ratio + self.transition_ratio = args.transition_ratio + self.clear_video_ratio = args.clear_video_ratio + self.default_text_ratio = args.default_text_ratio + assert self.i2v_ratio + self.transition_ratio + self.clear_video_ratio < 1, 'The sum of i2v_ratio, transition_ratio and clear video ratio should be less than 1.' print(f"video length: {len(self.vid_cap_list)}") print(f"image length: {len(self.img_cap_list)}") @@ -116,22 +136,26 @@ def get_video(self, idx): video_path = self.vid_cap_list[idx]['path'] assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" - frame_idx = self.vid_cap_list[idx]['frame_idx'] if hasattr(self.vid_cap_list[idx], 'frame_idx') else None + frame_idx = self.vid_cap_list[idx].get('frame_idx', None) video = self.decord_read(video_path, frame_idx) + # resize + video = self.resize_transform(video.float()).to(torch.uint8) - video = self.transform(video) # T C H W -> T C H W + inpaint_cond_data = self.get_mask_masked_video(video) + masked_video = inpaint_cond_data['masked_video'] - # video = torch.rand(221, 3, 480, 640) + clip_video = self.image_processor(images=masked_video, return_tensors="pt").pixel_values # T C H W + video = self.transform(video) # T C H W -> T C H W video = video.transpose(0, 1) # T C H W -> C T H W - text = self.vid_cap_list[idx]['cap'] - # inpaint - inpaint_cond_data = self.get_mask_masked_video(video) - masked_video = inpaint_cond_data['masked_video'] - video = torch.cat([video, masked_video], dim=0) + text = self.vid_cap_list[idx]['cap'] + text = text_preprocessing(text, support_Chinese=self.support_Chinese) - text = text_preprocessing(text, support_Chinese=self.support_Chinese) if random.random() > self.cfg else "" + drop_results = self.drop(text, clip_video) + text = drop_results['text'] + clip_video = drop_results['clip_image'] + text_tokens_and_mask = self.tokenizer( text, max_length=self.model_max_length, @@ -143,34 +167,52 @@ def get_video(self, idx): ) input_ids = text_tokens_and_mask['input_ids'] cond_mask = text_tokens_and_mask['attention_mask'] - return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) + + # video (C T H W) input_ids (1 N) cond_mask (1 N) clip_video (T C H W) + return dict(video=video, input_ids=input_ids, cond_mask=cond_mask, clip_video=clip_video) def get_image_from_video(self, video_data): select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) assert self.num_frames >= self.use_image_num image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] + clip_image = [video_data['clip_video'][i:i + 1] for i in select_image_idx] # num_img [1, c, h, w] input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l - return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) + + def drop(self, text, clip_image): + rand_num = random.random() + rand_num_text = random.random() + + if rand_num < self.cfg: + text = 'The video showcases a scene with coherent and clear visuals.' if rand_num_text < self.default_text_ratio else '' + elif rand_num < self.cfg * 2: + clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) + elif rand_num < self.cfg * 3: + text = 'The video showcases a scene with coherent and clear visuals.' if rand_num_text < self.default_text_ratio else '' + clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) + + return dict(text=text, clip_image=clip_image) def get_mask_masked_video(self, video): + # video shape (T, C, H, W) mask = torch.zeros_like(video) rand_num = random.random() # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. if rand_num < self.i2v_ratio: mask = 1 - mask - mask[:, 0, ...] = 0 + mask[0] = 0 elif rand_num < self.i2v_ratio + self.transition_ratio: mask = 1 - mask - mask[:, 0, ...] = 0 - mask[:, -1, ...] = 0 + mask[0] = 0 + mask[-1] = 0 elif rand_num < self.i2v_ratio + self.transition_ratio + self.clear_video_ratio: pass else: idx_to_select = random.randint(1, self.num_frames - 1) selected_indices = random.sample(range(1, self.num_frames), idx_to_select) - mask[:, selected_indices, ...] = 1 + mask[selected_indices] = 1 masked_video = video * (mask < 0.5) return dict(mask=mask, masked_video=masked_video) @@ -186,19 +228,23 @@ def get_image(self, idx): assert not torch.any(torch.isnan(i)), 'before transform0' image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] for i in image: - assert not torch.any(torch.isnan(i)), 'before transform1' - image = [self.transform(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] - + assert not torch.any(torch.isnan(i)), 'before resize_transform' + image = [self.resize_transform(i.float()).to(torch.uint8) for i in image] # num_img [1 C H W] -> num_img [1 C H W] for i in image: - assert not torch.any(torch.isnan(i)), 'after transform' - # image = [torch.rand(1, 3, 480, 640) for i in image_data] - image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] + assert not torch.any(torch.isnan(i)), 'after resize transform' + + clip_image_list = [self.image_processor(images=i, return_tensors="pt").pixel_values for i in image] # num_img [1 C H W] -> num_img [1 C H W] + + image = [self.transform(i).transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [1 C H W] -> num_img [C 1 H W] caps = [i['cap'] for i in image_data] text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] - input_ids, cond_mask = [], [] - for t in text: - t = t if random.random() > self.cfg else "" + + input_ids, cond_mask, clip_image = [], [], [] + for t, clip_i in zip(text, clip_image_list): + drop_results = self.drop(t, clip_i) + t = drop_results['text'] + clip_i = drop_results['clip_image'] text_tokens_and_mask = self.tokenizer( t, max_length=self.model_max_length, @@ -210,9 +256,11 @@ def get_image(self, idx): ) input_ids.append(text_tokens_and_mask['input_ids']) cond_mask.append(text_tokens_and_mask['attention_mask']) + clip_image.append(clip_i) input_ids = torch.cat(input_ids) # self.use_image_num, l cond_mask = torch.cat(cond_mask) # self.use_image_num, l - return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) + clip_image = torch.cat(clip_image) # self.use_image_num, C, H, W + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) def decord_read(self, path, frame_idx=None): decord_vr = self.v_decoder(path) @@ -230,7 +278,7 @@ def decord_read(self, path, frame_idx=None): video_data = decord_vr.get_batch(frame_indice).asnumpy() video_data = torch.from_numpy(video_data) - video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) + video_data = video_data.permute(0, 3, 1, 2) # (T H W C) -> (T C H W) return video_data def read_jsons(self, data, postfix=".jpg"): @@ -254,8 +302,31 @@ def get_img_cap_list(self): return img_cap_lists[:-1] # drop last to avoid error length def get_vid_cap_list(self): - vid_cap_lists = self.read_jsons(self.video_data, postfix=".mp4") - + vid_cap_lists = [] + with open(self.video_data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + for folder, anno in folder_anno: + new_vid_cap_list = [] + with open(anno, 'r') as f: + vid_cap_list = json.load(f) + print(f'Building {anno}...') + for i in tqdm(range(len(vid_cap_list))): + path = opj(folder, vid_cap_list[i]['path']) + # For testing, some data has not been utilized. + if not os.path.exists(path) and not os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): + print(path) + break + elif os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): + path = path.replace('.mp4', '_resize1080p.mp4') + new_vid_cap_list.append( + { + 'path': path, + 'frame_idx': vid_cap_list[i]['frame_idx'], + 'cap': vid_cap_list[i]['cap'] + } + ) + + vid_cap_lists += new_vid_cap_list return vid_cap_lists @@ -265,50 +336,61 @@ def get_vid_cap_list(self): from torchvision.transforms import Lambda from .transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop - from transformers import AutoTokenizer + from transformers import AutoTokenizer, AutoImageProcessor, CLIPImageProcessor class Args: def __init__(self): - self.video_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' - self.image_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' + # self.video_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' + # self.image_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' + self.video_data = '/storage/gyy/hw/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' + self.image_data = '/storage/gyy/hw/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' self.num_frames = 65 self.use_image_num = 4 self.use_img_from_vid = False self.model_max_length = 300 self.cfg = 0.1 + self.default_text_ratio = 0.5 self.i2v_ratio = 0.3 self.transition_ratio = 0.3 self.clear_video_ratio = 0.3 self.max_image_size = 512 self.sample_rate = 1 self.text_encoder_name = "DeepFloyd/t5-v1_1-xxl" - self.cache_dir = "./cache_dir" + # self.image_encoder_name = "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" + self.image_encoder_name = "facebook/dinov2-giant" + self.cache_dir = "/storage/cache_dir" args = Args() resize = [CenterCropResizeVideo((args.max_image_size, args.max_image_size))] temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + image_processor = AutoImageProcessor.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + + resize_transform = transforms.Compose([ + *resize, + ]) transform = transforms.Compose([ ToTensorVideo(), - *resize, - # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription Lambda(lambda x: 2. * x - 1.) ]) - dataset = VideoIP_dataset(args, transform, temporal_sample, tokenizer) + dataset = VideoIP_dataset(args, resize_transform=resize_transform, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, image_processor=image_processor) print(len(dataset)) video_data = dataset[0]['video_data'] image_data = dataset[0]['image_data'] - video, video_input_ids, video_cond_mask = video_data['video'], video_data['input_ids'], video_data['cond_mask'] - image, image_input_ids, image_cond_mask = image_data['image'], image_data['input_ids'], image_data['cond_mask'] - print(video.shape) # 3 * C, F, H, W + video, video_input_ids, video_cond_mask, clip_video = video_data['video'], video_data['input_ids'], video_data['cond_mask'], video_data['clip_video'] + image, image_input_ids, image_cond_mask, clip_image = image_data['image'], image_data['input_ids'], image_data['cond_mask'], image_data['clip_image'] + print(video.shape) # C, F, H, W print(video_input_ids.shape) # 1 D print(video_cond_mask.shape) # 1 D + print(clip_video.shape) # T, C, H, W + print(clip_image.shape) # num_images, C, H, W print(image[0].shape) print(image_input_ids.shape) print(image_cond_mask.shape) print(video_cond_mask) print(image_cond_mask) + diff --git a/opensora/models/diffusion/latte/modeling_for_vip.py b/opensora/models/diffusion/latte/modeling_for_vip.py new file mode 100644 index 000000000..daab9c7b9 --- /dev/null +++ b/opensora/models/diffusion/latte/modeling_for_vip.py @@ -0,0 +1,321 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +import os +import json +from typing import Optional, Dict, Any +from einops import rearrange, repeat + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + +from .modeling_latte import LatteT2V +from .modeling_latte import Transformer3DModelOutput +from .modules import PatchEmbed +from diffusers.configuration_utils import register_to_config + + +def hook_forward_fn(module, input, output): + print("It's forward: ") + print(f"module: {module}") + print("="*20) + +def hook_backward_fn(module, grad_input, grad_output): + print("It's backward: ") + print(f"module: {module}") + print(f"grad_input is None?: {grad_input is None}") + print(grad_input) + print(f"grad_output is None?: {grad_output is None}") + print(grad_output) + print("="*20) + + +def hacked_forward_for_vip( + self, + hidden_states: torch.Tensor, + timestep: Optional[torch.LongTensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + vip_hidden_states: Optional[torch.Tensor] = None, + added_cond_kwargs: Dict[str, torch.Tensor] = None, + class_labels: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + attention_mask: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + vip_attention_mask : Optional[torch.Tensor] = None, + use_image_num: int = 0, + enable_temporal_attentions: bool = True, + return_dict: bool = True, + ): + + input_batch_size, c, frame, h, w = hidden_states.shape + frame = frame - use_image_num # 20-4=16 + hidden_states = rearrange(hidden_states, 'b c f h w -> (b f) c h w').contiguous() + # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. + # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. + # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. + # expects mask of shape: + # [batch, key_tokens] + # adds singleton query_tokens dimension: + # [batch, 1, key_tokens] + # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: + # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) + # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + if attention_mask is None: + attention_mask = torch.ones((input_batch_size, frame+use_image_num, h, w), device=hidden_states.device, dtype=hidden_states.dtype) + else: + attention_mask = attention_mask.to(hidden_states.dtype) + attention_mask = self.vae_to_diff_mask(attention_mask, use_image_num) + dtype = attention_mask.dtype + attention_mask_compress = F.max_pool2d(attention_mask.float(), kernel_size=self.compress_kv_factor, stride=self.compress_kv_factor) + attention_mask_compress = attention_mask_compress.to(dtype) + + attention_mask = self.make_attn_mask(attention_mask, frame, hidden_states.dtype) + attention_mask_compress = self.make_attn_mask(attention_mask_compress, frame, hidden_states.dtype) + + # 1 + 4, 1 -> video condition, 4 -> image condition + # convert encoder_attention_mask to a bias the same way we do for attention_mask + if encoder_attention_mask is not None and encoder_attention_mask.ndim == 2: # ndim == 2 means no image joint + # NOTE add vip attention mask + encoder_attention_mask = torch.cat([encoder_attention_mask, vip_attention_mask], dim=1) # B N -> B N+num_vip_tokens + + encoder_attention_mask = (1 - encoder_attention_mask.to(hidden_states.dtype)) * -10000.0 + encoder_attention_mask = encoder_attention_mask.unsqueeze(1) + encoder_attention_mask = repeat(encoder_attention_mask, 'b 1 l -> (b f) 1 l', f=frame).contiguous() + encoder_attention_mask = encoder_attention_mask.to(self.dtype) + elif encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: # ndim == 3 means image joint + # NOTE add vip attention mask + encoder_attention_mask = torch.cat([encoder_attention_mask, vip_attention_mask], dim=2) # B 1+image_num N -> B 1+image_num N+num_vip_tokens + + encoder_attention_mask = (1 - encoder_attention_mask.to(hidden_states.dtype)) * -10000.0 + encoder_attention_mask_video = encoder_attention_mask[:, :1, ...] + encoder_attention_mask_video = repeat(encoder_attention_mask_video, 'b 1 l -> b (1 f) l', + f=frame).contiguous() + encoder_attention_mask_image = encoder_attention_mask[:, 1:, ...] + encoder_attention_mask = torch.cat([encoder_attention_mask_video, encoder_attention_mask_image], dim=1) + encoder_attention_mask = rearrange(encoder_attention_mask, 'b n l -> (b n) l').contiguous().unsqueeze(1) + encoder_attention_mask = encoder_attention_mask.to(self.dtype) + + if npu_config is not None: + encoder_attention_mask = npu_config.get_attention_mask(encoder_attention_mask, attention_mask.shape[-2]) + + # Retrieve lora scale. + lora_scale = cross_attention_kwargs.get("scale", 1.0) if cross_attention_kwargs is not None else 1.0 + + # 1. Input + if self.is_input_patches: # here + height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size + hw = (height, width) + num_patches = height * width + + hidden_states = self.pos_embed(hidden_states.to(self.dtype)) # alrady add positional embeddings + + if self.adaln_single is not None: + if self.use_additional_conditions and added_cond_kwargs is None: + raise ValueError( + "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." + ) + # batch_size = hidden_states.shape[0] + batch_size = input_batch_size + timestep, embedded_timestep = self.adaln_single( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=hidden_states.dtype + ) + + + # 2. Blocks + if self.caption_projection is not None: + batch_size = hidden_states.shape[0] + encoder_hidden_states = self.caption_projection(encoder_hidden_states.to(self.dtype)) + # NOTE add vip hidden states + encoder_hidden_states = torch.cat([encoder_hidden_states, vip_hidden_states], dim=2) # # B 1+image_num N D -> B 1+image_num N+num_vip_tokens D + + if use_image_num != 0 and self.training: + encoder_hidden_states_video = encoder_hidden_states[:, :1, ...] + encoder_hidden_states_video = repeat(encoder_hidden_states_video, 'b 1 t d -> b (1 f) t d', f=frame).contiguous() + encoder_hidden_states_image = encoder_hidden_states[:, 1:, ...] + encoder_hidden_states = torch.cat([encoder_hidden_states_video, encoder_hidden_states_image], dim=1) + encoder_hidden_states_spatial = rearrange(encoder_hidden_states, 'b f t d -> (b f) t d').contiguous() + else: + encoder_hidden_states_spatial = repeat(encoder_hidden_states, 'b 1 t d -> (b f) t d', f=frame).contiguous() + else: + # NOTE add vip hidden states + encoder_hidden_states = torch.cat([encoder_hidden_states, vip_hidden_states], dim=2) # # B 1+image_num N D -> B 1+image_num N+num_vip_tokens D + + + # prepare timesteps for spatial and temporal block + timestep_spatial = repeat(timestep, 'b d -> (b f) d', f=frame + use_image_num).contiguous() + timestep_temp = repeat(timestep, 'b d -> (b p) d', p=num_patches).contiguous() + + + pos_hw, pos_t = None, None + if self.use_rope: + pos_hw, pos_t = self.make_position(input_batch_size, frame, use_image_num, height, width, hidden_states.device) + + for i, (spatial_block, temp_block) in enumerate(zip(self.transformer_blocks, self.temporal_transformer_blocks)): + + if self.training and self.gradient_checkpointing: + hidden_states = torch.utils.checkpoint.checkpoint( + spatial_block, + hidden_states, + attention_mask_compress if i >= self.num_layers // 2 else attention_mask, + encoder_hidden_states_spatial, + encoder_attention_mask, + timestep_spatial, + cross_attention_kwargs, + class_labels, + pos_hw, + pos_hw, + hw, + use_reentrant=False, + ) + + if enable_temporal_attentions: + hidden_states = rearrange(hidden_states, '(b f) t d -> (b t) f d', b=input_batch_size).contiguous() + if use_image_num != 0: # image-video joitn training + hidden_states_video = hidden_states[:, :frame, ...] + hidden_states_image = hidden_states[:, frame:, ...] + + # if i == 0 and not self.use_rope: + if i == 0: + hidden_states_video = hidden_states_video + self.temp_pos_embed + + hidden_states_video = torch.utils.checkpoint.checkpoint( + temp_block, + hidden_states_video, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + use_reentrant=False, + ) + + hidden_states = torch.cat([hidden_states_video, hidden_states_image], dim=1) + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + + else: + # if i == 0 and not self.use_rope: + if i == 0: + hidden_states = hidden_states + self.temp_pos_embed + + hidden_states = torch.utils.checkpoint.checkpoint( + temp_block, + hidden_states, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + use_reentrant=False, + ) + + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + else: + hidden_states = spatial_block( + hidden_states, + attention_mask_compress if i >= self.num_layers // 2 else attention_mask, + encoder_hidden_states_spatial, + encoder_attention_mask, + timestep_spatial, + cross_attention_kwargs, + class_labels, + pos_hw, + pos_hw, + hw, + ) + + if enable_temporal_attentions: + # b c f h w, f = 16 + 4 + hidden_states = rearrange(hidden_states, '(b f) t d -> (b t) f d', b=input_batch_size).contiguous() + + if use_image_num != 0 and self.training: + hidden_states_video = hidden_states[:, :frame, ...] + hidden_states_image = hidden_states[:, frame:, ...] + + # if i == 0 and not self.use_rope: + # hidden_states_video = hidden_states_video + self.temp_pos_embed + + hidden_states_video = temp_block( + hidden_states_video, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + ) + + hidden_states = torch.cat([hidden_states_video, hidden_states_image], dim=1) + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + + else: + # if i == 0 and not self.use_rope: + if i == 0: + hidden_states = hidden_states + self.temp_pos_embed + + hidden_states = temp_block( + hidden_states, + None, # attention_mask + None, # encoder_hidden_states + None, # encoder_attention_mask + timestep_temp, + cross_attention_kwargs, + class_labels, + pos_t, + pos_t, + (frame, ), + ) + + hidden_states = rearrange(hidden_states, '(b t) f d -> (b f) t d', + b=input_batch_size).contiguous() + + if self.is_input_patches: + if self.config.norm_type != "ada_norm_single": + conditioning = self.transformer_blocks[0].norm1.emb( + timestep, class_labels, hidden_dtype=hidden_states.dtype + ) + shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] + hidden_states = self.proj_out_2(hidden_states) + elif self.config.norm_type == "ada_norm_single": + embedded_timestep = repeat(embedded_timestep, 'b d -> (b f) d', f=frame + use_image_num).contiguous() + shift, scale = (self.scale_shift_table[None] + embedded_timestep[:, None]).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) + # Modulation + hidden_states = hidden_states * (1 + scale) + shift + hidden_states = self.proj_out(hidden_states) + + # unpatchify + if self.adaln_single is None: + height = width = int(hidden_states.shape[1] ** 0.5) + hidden_states = hidden_states.reshape( + shape=(-1, height, width, self.patch_size, self.patch_size, self.out_channels) + ) + hidden_states = torch.einsum("nhwpqc->nchpwq", hidden_states) + output = hidden_states.reshape( + shape=(-1, self.out_channels, height * self.patch_size, width * self.patch_size) + ) + output = rearrange(output, '(b f) c h w -> b c f h w', b=input_batch_size).contiguous() + + if not return_dict: + return (output,) + + return Transformer3DModelOutput(sample=output) diff --git a/opensora/models/diffusion/latte/modeling_inpaint.py b/opensora/models/diffusion/latte/modeling_inpaint.py index ab09d0b68..e9be3b017 100644 --- a/opensora/models/diffusion/latte/modeling_inpaint.py +++ b/opensora/models/diffusion/latte/modeling_inpaint.py @@ -11,6 +11,7 @@ from .modeling_latte import LatteT2V from .modeling_latte import Transformer3DModelOutput from .modules import PatchEmbed +from diffusers.configuration_utils import register_to_config def zero_module(module): for p in module.parameters(): @@ -18,6 +19,7 @@ def zero_module(module): return module class OpenSoraInpaint(LatteT2V): + @register_to_config def __init__( self, num_attention_heads: int = 16, @@ -390,7 +392,7 @@ def forward( return Transformer3DModelOutput(sample=output) - def custom_load_state_dict(self, pretrained_model_path): + def custom_load_state_dict(self, pretrained_model_path, load_mask=False): print(f'Loading pretrained model from {pretrained_model_path}...') if 'safetensors' in pretrained_model_path: from safetensors.torch import load_file as safe_load @@ -401,7 +403,7 @@ def custom_load_state_dict(self, pretrained_model_path): if not 'pos_embed_masked_video.0.weight' in checkpoint: checkpoint['pos_embed_masked_video.0.proj.weight'] = checkpoint['pos_embed.proj.weight'] checkpoint['pos_embed_masked_video.0.proj.bias'] = checkpoint['pos_embed.proj.bias'] - if not 'pos_embed_mask.0.proj.weight' in checkpoint: + if not 'pos_embed_mask.0.proj.weight' in checkpoint and load_mask: checkpoint['pos_embed_mask.0.proj.weight'] = checkpoint['pos_embed.proj.weight'] checkpoint['pos_embed_mask.0.proj.bias'] = checkpoint['pos_embed.proj.bias'] missing_keys, unexpected_keys = self.load_state_dict(checkpoint, strict=False) diff --git a/opensora/models/diffusion/latte/modeling_latte.py b/opensora/models/diffusion/latte/modeling_latte.py index b22d1af0b..a23d2c307 100644 --- a/opensora/models/diffusion/latte/modeling_latte.py +++ b/opensora/models/diffusion/latte/modeling_latte.py @@ -270,6 +270,64 @@ def __init__( self.gradient_checkpointing = False + @property + def attn_processors(self): + r""" + Returns: + `dict` of attention processors: A dictionary containing all attention processors used in the model with + indexed by its weight name. + """ + # set recursively + processors = {} + + def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors): + if hasattr(module, "get_processor"): + processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True) + + for sub_name, child in module.named_children(): + fn_recursive_add_processors(f"{name}.{sub_name}", child, processors) + + return processors + + for name, module in self.named_children(): + fn_recursive_add_processors(name, module, processors) + + return processors + + def set_attn_processor(self, processor): + r""" + Sets the attention processor to use to compute attention. + + Parameters: + processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`): + The instantiated processor class or a dictionary of processor classes that will be set as the processor + for **all** `Attention` layers. + + If `processor` is a dict, the key needs to define the path to the corresponding cross attention + processor. This is strongly recommended when setting trainable attention processors. + + """ + count = len(self.attn_processors.keys()) + + if isinstance(processor, dict) and len(processor) != count: + raise ValueError( + f"A dict of processors was passed, but the number of processors {len(processor)} does not match the" + f" number of attention layers: {count}. Please make sure to pass {count} processor classes." + ) + + def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor): + if hasattr(module, "set_processor"): + if not isinstance(processor, dict): + module.set_processor(processor) + else: + module.set_processor(processor.pop(f"{name}.processor")) + + for sub_name, child in module.named_children(): + fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor) + + for name, module in self.named_children(): + fn_recursive_attn_processor(name, module, processor) + def _set_gradient_checkpointing(self, module, value=False): self.gradient_checkpointing = value @@ -416,7 +474,7 @@ def forward( timestep, embedded_timestep = self.adaln_single( timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=hidden_states.dtype ) - + # 2. Blocks if self.caption_projection is not None: batch_size = hidden_states.shape[0] diff --git a/opensora/models/diffusion/latte/modules.py b/opensora/models/diffusion/latte/modules.py index 5ce7b9dcc..dd8473dab 100644 --- a/opensora/models/diffusion/latte/modules.py +++ b/opensora/models/diffusion/latte/modules.py @@ -1399,6 +1399,7 @@ def forward( # 2. Prepare GLIGEN inputs cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} gligen_kwargs = cross_attention_kwargs.pop("gligen", None) + # import ipdb;ipdb.set_trace() attn_output = self.attn1( norm_hidden_states, @@ -1665,8 +1666,8 @@ def forward( ) -> torch.FloatTensor: # Notice that normalization is always applied before the real computation in the following blocks. # 0. Self-Attention - batch_size = hidden_states.shape[0] + batch_size = hidden_states.shape[0] if self.use_ada_layer_norm: norm_hidden_states = self.norm1(hidden_states, timestep) elif self.use_ada_layer_norm_zero: @@ -1687,7 +1688,6 @@ def forward( if self.pos_embed is not None: norm_hidden_states = self.pos_embed(norm_hidden_states) - # 1. Retrieve lora scale. lora_scale = cross_attention_kwargs.get("scale", 1.0) if cross_attention_kwargs is not None else 1.0 @@ -1703,6 +1703,7 @@ def forward( last_shape=hw, **cross_attention_kwargs, ) + if self.use_ada_layer_norm_zero: attn_output = gate_msa.unsqueeze(1) * attn_output elif self.use_ada_layer_norm_single: diff --git a/opensora/models/diffusion/latte/videoip.py b/opensora/models/diffusion/latte/videoip.py index aad9696d4..d158ed7b8 100644 --- a/opensora/models/diffusion/latte/videoip.py +++ b/opensora/models/diffusion/latte/videoip.py @@ -1,21 +1,288 @@ + +from dataclasses import dataclass + import torch import torch.nn as nn import torch.nn.functional as F +import torch.utils +from opensora.models.ae.videobase.modules import attention from opensora.models.diffusion.utils.pos_embed import RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D from opensora.models.diffusion.latte.modules import Attention from typing import Any, Dict, Optional, Tuple, Callable from diffusers.utils import USE_PEFT_BACKEND, BaseOutput, deprecate, is_xformers_available +from diffusers.utils.torch_utils import maybe_allow_in_graph +from diffusers.models.lora import LoRACompatibleLinear + + + +from .modules import BasicTransformerBlock +from einops import rearrange def zero_module(module): for p in module.parameters(): nn.init.zeros_(p) return module +@dataclass +class VideoIPOutput(BaseOutput): + hidden_states: torch.FloatTensor + vip_cond_mask: torch.FloatTensor + + +class VideoIPVideoEncoder(nn.Module): + + def __init__( + self, + image_encoder_type="clip", + inner_dim=1024, + cross_attention_dim=1152, + num_attention_layers=2, + use_rope=False, + rope_scaling=None, + attention_mode='xformers', + vae_scale_factor_t=4, + video_length=17, + max_num_tokens=272, + ): + super().__init__() + + self.image_encoder_type = image_encoder_type + + self.vae_scale_factor_t = vae_scale_factor_t + self.video_length = video_length + + self.max_num_tokens = max_num_tokens + + if USE_PEFT_BACKEND: + linear_cls = nn.Linear + else: + linear_cls = LoRACompatibleLinear + + if image_encoder_type == "clip": # F * 16 * 16 + # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") + + self.conv_in = nn.ModuleList( + [ + nn.Conv3d(in_channels=1280, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 + nn.SiLU(), + nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), # F // 2 * 16 * 16 -> F // 4 * 8 * 8 + ] + ) + + self.conv_in_downsample_factor = (4, 2, 2) + + elif image_encoder_type == "dino": # F * 16 * 16 + # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") + + self.conv_in = nn.ModuleList( + [ + nn.Conv3d(in_channels=1536, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 + nn.SiLU(), + nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), # F // 2 * 16 * 16 -> F // 4 * 8 * 8 + ] + ) + + self.conv_in_downsample_factor = (4, 2, 2) + + # elif image_encoder_type == "vae": # F // 4 * 64 * 64 + # assert in_channels is not None, "Please specify latent dim for VAE" + + # self.conv_in = nn.ModuleList( + # [ + # nn.Conv2d(in_channels=in_channels, out_channels=256, kernel_size=3, stride=2, padding=1), + # nn.SiLU(), + # nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=2, padding=1), + # nn.SiLU(), + # nn.Conv2d(in_channels=512, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), + # ] + # ) # F // 4 * 64 * 64 -> F // 4 * 8 * 8 + + # self.conv_in_downsample_factor = (1, 8, 8) + + else: + raise NotImplementedError + + self.trans_block1 = nn.ModuleList( + [ + BasicTransformerBlock( + dim=inner_dim, + num_attention_heads=16, + attention_head_dim=64, + dropout=0.0, + cross_attention_dim=None, + double_self_attention=False, + activation_fn="geglu", + norm_type="layer_norm", + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + ) + for _ in range(num_attention_layers) + ] + ) # only self-attention + + self.conv_mid = nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(1, 2, 2), padding=1) # F // 4 * 8 * 8 -> F // 4 * 4 * 4 + self.conv_mid_downsample_factor = (1, 2, 2) + + self.trans_block2 = nn.ModuleList( + [ + BasicTransformerBlock( + dim=inner_dim, + num_attention_heads=16, + attention_head_dim=64, + dropout=0.0, + cross_attention_dim=None, + double_self_attention=False, + activation_fn="geglu", + norm_type="layer_norm", + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + ) + for _ in range(num_attention_layers) + ] + ) # only self-attention + + self.proj_out = linear_cls(inner_dim, cross_attention_dim) + + self.norm_out = nn.LayerNorm(cross_attention_dim) + + def forward( + self, + hidden_states, + image_mode=False, + ): + # B C F H W + input_batch_size, input_frame = hidden_states.shape[0], hidden_states.shape[2] + + if image_mode: + hidden_states = hidden_states.repeat_interleave(self.vae_scale_factor_t, dim=2) + hidden_states = rearrange(hidden_states, 'b c (f i) h w -> (b f) c i h w', f=input_frame, i=self.vae_scale_factor_t) + else: + image_hidden_states = hidden_states[:, :, 0:1].repeat(1, 1, self.vae_scale_factor_t, 1, 1) + hidden_states = torch.cat([image_hidden_states, hidden_states[:, :, 1:]], dim=2) + + for layer in self.conv_in: + hidden_states = layer(hidden_states) + + # after conv_in + frame, height, width = hidden_states.shape[2], hidden_states.shape[3], hidden_states.shape[4] + # if training image, now batch = input_batch_size * frame; if not, batch remains the same + hidden_states = rearrange(hidden_states, 'b c f h w -> b (f h w) c') + + + for layer in self.trans_block1: + hidden_states = layer( + hidden_states=hidden_states, + attention_mask=None, + ) + + # when using image mode, f=1; when using video mode, f=video_length + hidden_states = rearrange(hidden_states, "b (f h w) c -> b c f h w ", f=frame, h=height, w=width) + + hidden_states = self.conv_mid(hidden_states) + + hidden_states = rearrange(hidden_states, "b c f h w -> b (f h w) c") + + + for layer in self.trans_block2: + hidden_states = layer( + hidden_states=hidden_states, + attention_mask=None, + ) + + # when using image mode, n=1*h*w; when using video mode, n=video_length*h*w + if image_mode: + hidden_states = rearrange(hidden_states, '(b f) n c -> b f n c', f=input_frame) + else: + hidden_states = hidden_states.unsqueeze(1) # B N C -> B 1 N C + + + hidden_states = self.proj_out(hidden_states) + + hidden_states = self.norm_out(hidden_states) + + batch, num_seq, num_tokens, _ = hidden_states.shape + hidden_states = F.pad(hidden_states, (0, 0, 0, self.max_num_tokens - num_tokens), value=0.0) + vip_cond_mask = torch.ones([batch, num_seq, num_tokens], device=hidden_states.device, dtype=hidden_states.dtype) + vip_cond_mask = F.pad(vip_cond_mask, (0, self.max_num_tokens - num_tokens), value=0.0) + + # hidden_states: B 1 N D(video) B image_num N D(image), vip_cond_mask: B 1 N(video) B image_num N(image), N = max_num_tokens + return hidden_states, vip_cond_mask + +class VideoIPAdapter(nn.Module): + def __init__( + self, + image_encoder_type="clip", + inner_dim=1024, + cross_attention_dim=1152, + num_attention_layers=2, + use_rope=False, + rope_scaling=None, + attention_mode='xformers', + gradient_checkpointing=False, + vae_scale_factor_t=4, + video_length=17, + ): + super().__init__() + self.gradient_checkpointing = gradient_checkpointing + self.encoder = VideoIPVideoEncoder( + image_encoder_type=image_encoder_type, + inner_dim=inner_dim, + cross_attention_dim=cross_attention_dim, + num_attention_layers=num_attention_layers, + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + vae_scale_factor_t=vae_scale_factor_t, + video_length=video_length, + ) + + def forward( + self, + hidden_states, + use_image_num=0, + return_dict=True, + ): + + assert hidden_states.ndim == 5, "Input tensor must be 5D" + + batch, channels, frame, height, width = hidden_states.shape + frame = frame - use_image_num + + video_hidden_states = hidden_states[:, :, 0:frame] + + one_image_video = True if frame == 1 else False + if self.training and self.gradient_checkpointing: + video_hidden_states, video_cond_mask = torch.utils.checkpoint.checkpoint(self.encoder, video_hidden_states, one_image_video, use_reentrant=False,) + else: + video_hidden_states, video_cond_mask = self.encoder(video_hidden_states, image_mode=one_image_video) + + if use_image_num: + image_hidden_states = hidden_states[:, :, frame:] + if self.training and self.gradient_checkpointing: + image_hidden_states, image_cond_mask = torch.utils.checkpoint.checkpoint(self.encoder, image_hidden_states, True, use_reentrant=False,) + else: + image_hidden_states, image_cond_mask = self.encoder(image_hidden_states, image_mode=True) + hidden_states = torch.cat([video_hidden_states, image_hidden_states], dim=1) # B 1+image_num N D + vip_cond_mask = torch.cat([video_cond_mask, image_cond_mask], dim=1) # B 1+image_num D + else: + hidden_states = video_hidden_states # B 1 N D + vip_cond_mask = video_cond_mask # B 1 N + + + if not return_dict: + return (hidden_states, vip_cond_mask) + + return VideoIPOutput(hidden_states=hidden_states, vip_cond_mask=vip_cond_mask) + + # IP Adapter -class VideoIPAttnProcessor: +# we need use accelerate, so this processor should extend from nn.Module +class VideoIPAttnProcessor(nn.Module): r""" Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). """ @@ -28,28 +295,34 @@ def __init__( rope_scaling=None, compress_kv_factor=None, - num_vip_tokens=128, + num_vip_tokens=272, vip_scale=1.0, dropout=0.0, ): + super().__init__() self.dim = dim self.attention_mode = attention_mode self.use_rope = use_rope self.rope_scaling = rope_scaling self.compress_kv_factor = compress_kv_factor + if USE_PEFT_BACKEND: + linear_cls = nn.Linear + else: + linear_cls = LoRACompatibleLinear + if self.use_rope: self._init_rope() if not hasattr(F, "scaled_dot_product_attention"): raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") - self.to_k_vip = nn.Linear(dim, dim, bias=False) - self.to_v_vip = nn.Linear(dim, dim, bias=False) + self.to_k_vip = linear_cls(dim, dim, bias=False) + self.to_v_vip = linear_cls(dim, dim, bias=False) self.to_out_vip = nn.ModuleList( [ - zero_module(nn.Linear(dim, dim, bias=False)), + zero_module(linear_cls(dim, dim, bias=False)), nn.Dropout(dropout), ] ) @@ -71,7 +344,7 @@ def _init_rope(self): else: raise ValueError(f"Unknown RoPE scaling type {scaling_type}") - def __call__( + def forward( self, attn: Attention, hidden_states: torch.FloatTensor, @@ -117,10 +390,19 @@ def __call__( batch_size, sequence_length, _ = ( hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape ) - + has_vip_tokens = encoder_hidden_states is not None + if has_vip_tokens: + end_pos = sequence_length - self.num_vip_tokens + + # attention_mask include encoder_hidden_states(text) and clip_feature(image or video) # import ipdb;ipdb.set_trace() if attention_mask is not None: - attention_mask = attn.prepare_attention_mask(attention_mask, sequence_length, batch_size) + if has_vip_tokens: + attention_mask, vip_attention_mask = attention_mask[..., :end_pos], attention_mask[..., end_pos:] + vip_attention_mask = attn.prepare_attention_mask(vip_attention_mask, self.num_vip_tokens, batch_size) + vip_attention_mask = vip_attention_mask.view(batch_size, attn.heads, -1, vip_attention_mask.shape[-1]) + + attention_mask = attn.prepare_attention_mask(attention_mask, end_pos, batch_size) # scaled_dot_product_attention expects attention_mask shape to be # (batch, heads, source_length, target_length) attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) @@ -134,25 +416,26 @@ def __call__( if encoder_hidden_states is None: encoder_hidden_states = hidden_states else: - # get encoder_hidden_states, ip_hidden_states - end_pos = encoder_hidden_states.shape[1] - self.num_vip_tokens - encoder_hidden_states, vip_hidden_states = ( - encoder_hidden_states[:, :end_pos, :], - encoder_hidden_states[:, end_pos:, :], - ) + # get encoder_hidden_states, vip_hidden_states + if has_vip_tokens: + encoder_hidden_states, vip_hidden_states = ( + encoder_hidden_states[:, :end_pos, :], + encoder_hidden_states[:, end_pos:, :], + ) if attn.norm_cross: + # vip tokens is normed encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + key = attn.to_k(encoder_hidden_states, *args) value = attn.to_v(encoder_hidden_states, *args) - vip_key = self.to_k_vip(vip_hidden_states) - vip_value = self.to_v_vip(vip_hidden_states) + vip_key = self.to_k_vip(vip_hidden_states, *args) + vip_value = self.to_v_vip(vip_hidden_states, *args) inner_dim = key.shape[-1] head_dim = inner_dim // attn.heads - query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) @@ -195,14 +478,14 @@ def __call__( query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False ) vip_hidden_states = F.scaled_dot_product_attention( - query, vip_key, vip_value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + query, vip_key, vip_value, attn_mask=vip_attention_mask, dropout_p=0.0, is_causal=False ) elif self.attention_mode == 'math': hidden_states = F.scaled_dot_product_attention( query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False ) vip_hidden_states = F.scaled_dot_product_attention( - query, vip_key, vip_value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + query, vip_key, vip_value, attn_mask=vip_attention_mask, dropout_p=0.0, is_causal=False ) else: raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') @@ -218,7 +501,7 @@ def __call__( vip_hidden_states = vip_hidden_states.to(query.dtype) # linear proj - vip_hidden_states = self.to_out_vip[0](vip_hidden_states) + vip_hidden_states = self.to_out_vip[0](vip_hidden_states, *args) # dropout vip_hidden_states = self.to_out_vip[1](vip_hidden_states) @@ -234,3 +517,24 @@ def __call__( return hidden_states +if __name__ == "__main__": + model = VideoIPVideoEncoder( + image_encoder_type="clip", + inner_dim=1024, + cross_attention_dim=1152, + num_attention_layers=2, + use_rope=False, + rope_scaling=None, + attention_mode='math', + vae_scale_factor_t=4, + video_length=17, + ) + + params = sum(p.numel() for p in model.parameters() if p.requires_grad) + print(f"Trainable parameters: {params / 1e6}M") + + video = torch.randn(2, 1280, 1, 16, 16) + + output = model(video, training_image=True) + print(output.vip_cond_mask) + diff --git a/opensora/sample/pipeline_for_vip.py b/opensora/sample/pipeline_for_vip.py new file mode 100644 index 000000000..803cb04b5 --- /dev/null +++ b/opensora/sample/pipeline_for_vip.py @@ -0,0 +1,211 @@ + + +from typing import Callable, List, Optional, Tuple, Union + +import torch + +from diffusers.utils import replace_example_docstring +from diffusers.pipelines.pipeline_utils import ImagePipelineOutput + +from .pipeline_opensora import retrieve_timesteps + +@torch.no_grad() +def hacked_pipeline_call_for_vip( + self, + prompt: Union[str, List[str]] = None, + negative_prompt: str = "", + num_inference_steps: int = 20, + timesteps: List[int] = None, + guidance_scale: float = 4.5, + num_images_per_prompt: Optional[int] = 1, + num_frames: Optional[int] = None, + height: Optional[int] = None, + width: Optional[int] = None, + eta: float = 0.0, + generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, + latents: Optional[torch.FloatTensor] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + # NOTE add vip tokens + vip_tokens: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + # NOTE add vip attention mask + vip_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + # NOTE add negative vip tokens + negative_vip_tokens: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + # NOTE add negative vip attention mask + negative_vip_attention_mask: Optional[torch.FloatTensor] = None, + output_type: Optional[str] = "pil", + return_dict: bool = True, + callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, + callback_steps: int = 1, + clean_caption: bool = True, + use_resolution_binning: bool = True, + max_sequence_length: int = 300, + **kwargs, +) -> Union[ImagePipelineOutput, Tuple]: + + # 1. Check inputs. Raise error if not correct + num_frames = num_frames or self.transformer.config.sample_size_t * self.vae.vae_scale_factor[0] + height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] + width = width or self.transformer.config.sample_size[1] * self.vae.vae_scale_factor[2] + self.check_inputs( + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds, + negative_prompt_embeds, + prompt_attention_mask, + negative_prompt_attention_mask, + ) + + # 2. Default height and width to transformer + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + # import ipdb;ipdb.set_trace() + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') + + # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) + # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` + # corresponds to doing no classifier free guidance. + do_classifier_free_guidance = guidance_scale > 1.0 + + # 3. Encode input prompt + ( + prompt_embeds, + prompt_attention_mask, + negative_prompt_embeds, + negative_prompt_attention_mask, + ) = self.encode_prompt( + prompt, + do_classifier_free_guidance, + negative_prompt=negative_prompt, + num_images_per_prompt=num_images_per_prompt, + device=device, + prompt_embeds=prompt_embeds, + negative_prompt_embeds=negative_prompt_embeds, + prompt_attention_mask=prompt_attention_mask, + negative_prompt_attention_mask=negative_prompt_attention_mask, + clean_caption=clean_caption, + max_sequence_length=max_sequence_length, + ) + + if do_classifier_free_guidance: + prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) + prompt_attention_mask = torch.cat([negative_prompt_attention_mask, prompt_attention_mask], dim=0) + # NOTE add vip tokens + vip_tokens = torch.cat([negative_vip_tokens, vip_tokens], dim=0) + vip_attention_mask = torch.cat([negative_vip_attention_mask, vip_attention_mask], dim=0) + + # 4. Prepare timesteps + timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device, timesteps) + + # 5. Prepare latents. + latent_channels = self.transformer.config.in_channels + latents = self.prepare_latents( + batch_size * num_images_per_prompt, + latent_channels, + num_frames, + height, + width, + prompt_embeds.dtype, + device, + generator, + latents, + ) + + # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline + extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) + + # 6.1 Prepare micro-conditions. + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + + # 7. Denoising loop + num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) + + with self.progress_bar(total=num_inference_steps) as progress_bar: + for i, t in enumerate(timesteps): + latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) + + current_timestep = t + if not torch.is_tensor(current_timestep): + # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can + # This would be a good case for the `match` statement (Python 3.10+) + is_mps = latent_model_input.device.type == "mps" + if isinstance(current_timestep, float): + dtype = torch.float32 if is_mps else torch.float64 + else: + dtype = torch.int32 if is_mps else torch.int64 + current_timestep = torch.tensor([current_timestep], dtype=dtype, device=latent_model_input.device) + elif len(current_timestep.shape) == 0: + current_timestep = current_timestep[None].to(latent_model_input.device) + # broadcast to batch dimension in a way that's compatible with ONNX/Core ML + current_timestep = current_timestep.expand(latent_model_input.shape[0]) + + # import ipdb;ipdb.set_trace() + if prompt_embeds.ndim == 3: + prompt_embeds = prompt_embeds.unsqueeze(1) # b l d -> b 1 l d + if prompt_attention_mask.ndim == 2: + prompt_attention_mask = prompt_attention_mask.unsqueeze(1) # b l -> b 1 l + # prepare attention_mask. + # b c t h w -> b t h w + attention_mask = torch.ones_like(latent_model_input)[:, 0] + # predict noise model_output + noise_pred = self.transformer( + latent_model_input, + attention_mask=attention_mask, + encoder_hidden_states=prompt_embeds, + encoder_attention_mask=prompt_attention_mask, + vip_hidden_states=vip_tokens, + vip_attention_mask=vip_attention_mask, + timestep=current_timestep, + added_cond_kwargs=added_cond_kwargs, + enable_temporal_attentions=num_frames > 1, + return_dict=False, + )[0] + + # perform guidance + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + # learned sigma + if self.transformer.config.out_channels // 2 == latent_channels: + noise_pred = noise_pred.chunk(2, dim=1)[0] + else: + noise_pred = noise_pred + + # compute previous image: x_t -> x_t-1 + latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] + + # call the callback, if provided + if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): + progress_bar.update() + if callback is not None and i % callback_steps == 0: + step_idx = i // getattr(self.scheduler, "order", 1) + callback(step_idx, t, latents) + # import ipdb;ipdb.set_trace() + # latents = latents.squeeze(2) + if not output_type == "latent": + # b t h w c + image = self.decode_latents(latents) + image = image[:, :num_frames, :height, :width] + else: + image = latents + + # Offload all models + self.maybe_free_model_hooks() + + if not return_dict: + return (image,) + + return ImagePipelineOutput(images=image) \ No newline at end of file diff --git a/opensora/sample/pipeline_inpaint.py b/opensora/sample/pipeline_inpaint.py index c4997e300..7c3ed8059 100644 --- a/opensora/sample/pipeline_inpaint.py +++ b/opensora/sample/pipeline_inpaint.py @@ -34,6 +34,8 @@ from diffusers.utils.torch_utils import randn_tensor from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput +from einops import rearrange +import torch.nn.functional as F logger = logging.get_logger(__name__) # pylint: disable=invalid-name @@ -746,21 +748,34 @@ def __call__( input_video = torch.zeros([3, num_frames, height, width], device=device) - input_video[:, condition_images_indices] = torch.cat(condition_images, dim=1).to(device) # C 1/2 H W -> C F H W + condition_images = torch.cat(condition_images, dim=1).to(device=device) # C 1/2 H W -> C F H W + input_video[:, condition_images_indices] = condition_images + + print(condition_images_indices) + assert torch.equal(input_video[:, condition_images_indices[0]], condition_images[:, 0]), "input_video should be the same as condition_images" + assert torch.equal(input_video[:, condition_images_indices[-1]], condition_images[:, -1]), "input_video should be the same as condition_images" + input_video = input_video.unsqueeze(0).repeat(batch_size * num_images_per_prompt, 1, 1, 1, 1) - mask = torch.ones_like(input_video, device=device) + B, C, T, H, W = input_video.shape + mask = torch.ones([B, 1, T, H, W], device=device) mask[:, :, condition_images_indices] = 0 + masked_video = input_video * (mask < 0.5) + masked_video = self.vae.encode(masked_video).to(device) - masked_video = input_video * (mask < 0.5) + mask = rearrange(mask, 'b c t h w -> (b c t) 1 h w') + latent_size = ((int(num_frames) - 1) // self.vae.vae_scale_factor[0] + 1, height // self.vae.vae_scale_factor[1], width // self.vae.vae_scale_factor[2]) + mask = F.interpolate(mask, size=(latent_size[1], latent_size[2]), mode='bilinear') + mask = rearrange(mask, '(b c t) 1 h w -> b c t h w', t=T, b=B) + mask_first_frame = mask[:, :, 0:1].repeat(1, 1, self.vae.vae_scale_factor[0], 1, 1).contiguous() + mask = torch.cat([mask_first_frame, mask[:, :, 1:]], dim=2) + mask = mask.view(batch_size, self.vae.vae_scale_factor[0], *latent_size).contiguous() - masked_video = self.vae.encode(masked_video).to(device) - mask = self.vae.encode(mask).to(device) with self.progress_bar(total=num_inference_steps) as progress_bar: for i, t in enumerate(timesteps): latent_model_input = torch.cat([latents, masked_video, mask], dim=1) - latent_model_input = torch.cat([latent_model_input] * 2) if do_classifier_free_guidance else latents + latent_model_input = torch.cat([latent_model_input] * 2) if do_classifier_free_guidance else latent_model_input latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) current_timestep = t diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 6a06d88b9..22338440e 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -547,7 +547,6 @@ def prepare_latents(self, batch_size, num_channels_latents, num_frames, height, return latents @torch.no_grad() - @replace_example_docstring(EXAMPLE_DOC_STRING) def __call__( self, prompt: Union[str, List[str]] = None, diff --git a/opensora/sample/sample_inpaint.py b/opensora/sample/sample_inpaint.py new file mode 100644 index 000000000..11227e12a --- /dev/null +++ b/opensora/sample/sample_inpaint.py @@ -0,0 +1,263 @@ +import math +import os +from tkinter.filedialog import Open +import torch +import argparse +import torchvision + +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler +from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel +from omegaconf import OmegaConf +from torchvision.utils import save_image +from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer + +import os, sys +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +from opensora.models.ae import ae_stride_config, getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.diffusion.udit.modeling_udit import UDiTT2V + +from opensora.models.text_encoder import get_text_enc +from opensora.utils.utils import save_video_grid + +from opensora.sample.pipeline_opensora import OpenSoraPipeline +from opensora.sample.pipeline_inpaint import OpenSoraInpaintPipeline + +from opensora.models.diffusion.latte.modeling_inpaint import OpenSoraInpaint +from diffusers.configuration_utils import register_to_config + +import imageio + +from PIL import Image +from einops import rearrange +import glob +from torchvision import transforms +from torchvision.transforms import Lambda +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +import numpy as np + +def save_video(video, save_path='output_video.mp4', fps=24): + import cv2 + + frame_count, height, width, channels = video.shape + + fourcc = cv2.VideoWriter_fourcc(*'mp4v') + out = cv2.VideoWriter(save_path, fourcc, fps, (width, height)) + + for i in range(frame_count): + frame = video[i].cpu().numpy() + frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) + out.write(frame) + +def main(args): + # torch.manual_seed(args.seed) + torch.set_grad_enabled(False) + weight_dtype = torch.float16 + device = torch.device(args.device) + + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] + + # if args.model_3d: + # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # else: + # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + + if args.model_3d: + # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, + # low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + transformer_model = UDiTT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + else: + transformer_model = OpenSoraInpaint( + num_layers=28, + attention_head_dim=72, + num_attention_heads=16, + patch_size_t=1, + patch_size=2, + norm_type="ada_norm_single", + caption_channels=4096, + cross_attention_dim=1152, + in_channels=4, + out_channels=8, # 因为要加载预训练权重,所以这里out_channels仍然设置为2倍 + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=(64, 64), + sample_size_t=17, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode='xformers', + downsampler=None, + compress_kv_factor=1, + use_rope=False, + model_max_length=300, + ) + transformer_model.custom_load_state_dict(args.model_path) + # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + # tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + + # set eval mode + transformer_model.eval() + vae.eval() + text_encoder.eval() + + vae.vae = vae.vae.to(device=device, dtype=torch.float32) + transformer_model = transformer_model.to(device=device, dtype=weight_dtype) + text_encoder = text_encoder.to(device=device, dtype=weight_dtype) + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler(clip_sample=False) + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler(clip_sample=False) + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + elif args.sample_method == 'EulerDiscreteSVD': + scheduler = EulerDiscreteScheduler.from_pretrained("stabilityai/stable-video-diffusion-img2vid", + subfolder="scheduler", cache_dir=args.cache_dir) + + print(args.sample_method, scheduler.__class__.__name__) + + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + prompt_file = os.path.join(validation_dir, "prompt.txt") + + with open(prompt_file, 'r') as f: + validation_prompt = f.readlines() + + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break + + resize = [CenterCropResizeVideo((args.height, args.width)),] + norm_fun = Lambda(lambda x: 2. * x - 1.) + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + + pipeline = OpenSoraInpaintPipeline( + vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model + ) + + pipeline.to(device) + + def preprocess_images(images): + if len(images) == 1: + condition_images_indices = [0] + elif len(images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in images] + condition_images = [torch.from_numpy(np.copy(np.array(image))) for image in condition_images] + condition_images = [rearrange(image, 'h w c -> c h w').unsqueeze(0) for image in condition_images] + condition_images = [transform(image).to(device=device, dtype=torch.float32) for image in condition_images] + return dict(condition_images=condition_images, condition_images_indices=condition_images_indices) + + videos = [] + for prompt, images in zip(validation_prompt, validation_images_list): + if not isinstance(images, list): + images = [images] + print('Processing the ({}) prompt and the images ({})'.format(prompt, images)) + + pre_results = preprocess_images(images) + condition_images = pre_results['condition_images'] + condition_images_indices = pre_results['condition_images_indices'] + + video = pipeline( + prompt=prompt, + condition_images=condition_images, + condition_images_indices=condition_images_indices, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + ).images + videos.append(video[0]) + + # Save the generated videos + save_dir = os.path.join(args.output_dir) + os.makedirs(save_dir, exist_ok=True) + for idx, video in enumerate(videos): + save_video(video, os.path.join(save_dir, f"video_{idx:06d}.mp4")) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') + parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") + parser.add_argument("--guidance_scale", type=float, default=7.5) + parser.add_argument("--sample_method", type=str, default="PNDM") + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument("--fps", type=int, default=24) + parser.add_argument("--run_time", type=int, default=0) + parser.add_argument("--text_prompt", nargs='+') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--model_3d', action='store_true') + + parser.add_argument("--validation_dir", type=str, default=None) + parser.add_argument("--output_dir", type=str, default=None) + args = parser.parse_args() + + main(args) \ No newline at end of file diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py index 9a77cc235..e5cae49e5 100644 --- a/opensora/train/train_inpaint.py +++ b/opensora/train/train_inpaint.py @@ -167,7 +167,7 @@ def preprocess_images(images): # import ipdb;ipdb.set_trace() # Save the generated videos - save_dir = os.path.join(validation_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") + save_dir = os.path.join(args.output_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") os.makedirs(save_dir, exist_ok=True) for idx, video in enumerate(videos): save_video(video, os.path.join(save_dir, f"video_{idx:06d}.mp4")) @@ -752,10 +752,20 @@ def train_one_step(step_, data_item_, prof_=None): # Map input images to latent space + normalize latents assert args.use_image_num == 0, 'Inpainting mode does not support image joint training' - - x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:9] - x, masked_x, mask = ae.encode(x), ae.encode(masked_x), ae.encode(mask) + if args.use_vae_preprocessed_mask: + x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:9] + x, masked_x, mask = ae.encode(x), ae.encode(masked_x), ae.encode(mask) + else: + x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:7] + x, masked_x = ae.encode(x), ae.encode(masked_x) + batch_size, channels, frame, height, width = mask.shape + mask = rearrange(mask, 'b c t h w -> (b c t) 1 h w') + mask = F.interpolate(mask, size=latent_size, mode='bilinear') + mask = rearrange(mask, '(b c t) 1 h w -> b c t h w', t=frame, b=batch_size) + mask_first_frame = mask[:, :, 0:1].repeat(1, 1, ae_stride_t, 1, 1).contiguous() + mask = torch.cat([mask_first_frame, mask[:, :, 1:]], dim=2) + mask = mask.view(batch_size, ae_stride_t, latent_size_t, latent_size[0], latent_size[1]).contiguous() assert not torch.any(torch.isnan(x)), 'after vae' assert not torch.any(torch.isnan(masked_x)), 'after vae' assert not torch.any(torch.isnan(mask)), 'after vae' @@ -949,7 +959,7 @@ def train_all_epoch(prof_=None): parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") - + parser.add_argument("--use_vae_preprocessed_mask", action='store_true', help="Use preprocessed mask for VAE.") args = parser.parse_args() main(args) diff --git a/opensora/train/train_videoip.py b/opensora/train/train_videoip.py index 9a77cc235..668d2deb7 100644 --- a/opensora/train/train_videoip.py +++ b/opensora/train/train_videoip.py @@ -8,9 +8,12 @@ A minimal training script for DiT using PyTorch DDP. """ import argparse +from email.mime import image import logging import math +from mimetypes import init import os +from selectors import EpollSelector import shutil from pathlib import Path from typing import Optional @@ -18,20 +21,17 @@ import numpy as np from einops import rearrange from tqdm import tqdm +import itertools + +from opensora.models.ae.videobase.modules import attention -try: - import torch_npu - from opensora.npu_config import npu_config -except: - torch_npu = None - npu_config = None - pass import time from dataclasses import field, dataclass from torch.utils.data import DataLoader from copy import deepcopy import accelerate import torch +import torch.nn as nn from torch.nn import functional as F import transformers from accelerate import Accelerator @@ -41,19 +41,22 @@ from packaging import version from tqdm.auto import tqdm from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer +from math import sqrt import diffusers from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler from diffusers.optimization import get_scheduler from diffusers.training_utils import EMAModel, compute_snr from diffusers.utils import check_min_version, is_wandb_available +from diffusers.models.modeling_utils import ModelMixin +from diffusers.configuration_utils import ConfigMixin, register_to_config from opensora.dataset import getdataset, ae_denorm from opensora.models.ae import getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper from opensora.models.diffusion.latte.modeling_latte import LatteT2V from opensora.models.text_encoder import get_text_enc, get_text_warpper -from opensora.utils.dataset_utils import Collate +from opensora.utils.dataset_utils import VideoIP_Collate as Collate from opensora.models.ae import ae_stride_config, ae_channel_config from opensora.models.diffusion import Diffusion_models, Diffusion_models_class from opensora.sample.pipeline_opensora import OpenSoraPipeline @@ -63,33 +66,217 @@ from PIL import Image from torchvision import transforms from torchvision.transforms import Lambda +from transformers import CLIPVisionModelWithProjection, AutoModel, AutoImageProcessor, CLIPImageProcessor from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo -from opensora.sample.pipeline_inpaint import OpenSoraInpaintPipeline - +from opensora.sample.pipeline_opensora import OpenSoraPipeline +from opensora.sample.pipeline_for_vip import hacked_pipeline_call_for_vip +from opensora.models.diffusion.latte.videoip import VideoIPAdapter, VideoIPAttnProcessor +from opensora.models.diffusion.latte.modeling_for_vip import hacked_forward_for_vip, hook_forward_fn, hook_backward_fn +from opensora.models.diffusion.latte.modules import BasicTransformerBlock import glob +from torchvision.utils import save_image +import imageio # Will error if the minimal version of diffusers is not installed. Remove at your own risks. check_min_version("0.24.0") logger = get_logger(__name__) -def save_video(video, save_path='output_video.mp4', fps=24): - import cv2 +class VIPNet(ModelMixin, ConfigMixin): + @register_to_config + def __init__( + self, + image_encoder_type='dino', + cross_attention_dim=1152, + num_tokens=272, + vip_inner_dim=1024, + vip_num_attention_layers=2, + attention_mode='xformers', + gradient_checkpointing=False, + vae_scale_factor_t=4, + video_length=17, + use_rope=False, + rope_scaling=None, + attn_proc_type_dict={}, + ): + super().__init__() + + self.image_encoder_type = image_encoder_type + self.cross_attention_dim = cross_attention_dim + self.num_tokens = num_tokens + + self.attention_mode = attention_mode + self.use_rope = use_rope + self.rope_scaling = rope_scaling + + + self.vip_adapter = VideoIPAdapter( + image_encoder_type=image_encoder_type, + cross_attention_dim=cross_attention_dim, + inner_dim=vip_inner_dim, + num_attention_layers=vip_num_attention_layers, + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + gradient_checkpointing=gradient_checkpointing, + vae_scale_factor_t=vae_scale_factor_t, + video_length=video_length, + ) + + + self.attn_procs = {} + # because nn.ModuleDict will raise (KeyError: 'module name can\'t contain ".", got: transformer_blocks.0.attn2.processor'), so we trun to use nn.ModuleList + for name, attn_proc_type in attn_proc_type_dict.items(): + if attn_proc_type == "VideoIPAttnProcessor": + self.attn_procs[name] = VideoIPAttnProcessor( + dim=cross_attention_dim, + attention_mode=attention_mode, + use_rope=use_rope, + rope_scaling=rope_scaling, + num_vip_tokens=num_tokens, + ) + self.adapter_modules = torch.nn.ModuleList(self.attn_procs.values()) + + # if pretrained_vip_adapter_path is not None: + # self.load_vip_adapter(pretrained_vip_adapter_path) + + def set_vip_adapter(self, model, init_from_original_attn_processor): + # init adapter modules + model_sd = model.state_dict() + attn_procs = {} + print("set vip adapter...") + for name, attn_processor in model.attn_processors.items(): + if name.endswith(".attn2.processor"): + new_attn_processor = self.attn_procs[name] + if init_from_original_attn_processor: + print("init from original attn processor...") + layer_name = name.split(".processor")[0] + weights = { + "to_k_vip.weight": model_sd[layer_name + ".to_k.weight"], + "to_v_vip.weight": model_sd[layer_name + ".to_v.weight"], + } + new_attn_processor.load_state_dict(weights, strict=False) + attn_procs[name] = new_attn_processor + else: + attn_procs[name] = attn_processor + + model.set_attn_processor(attn_procs) + + @torch.no_grad() + def get_image_embeds(self, images, image_processor, image_encoder, transform, device, weight_dtype=torch.float32): + if not isinstance(images, list): + images = [images] + images = [Image.open(image).convert("RGB") for image in images] + images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in images] # 1 H W C + images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in images]) # resize, 1 C H W + + images = image_processor(images=images, return_tensors="pt").pixel_values # 1 C H W + images = images.to(device=device, dtype=weight_dtype) + negative_images = torch.zeros_like(images, device=device, dtype=weight_dtype) + + images = image_encoder(images).last_hidden_state # 1 N D + images = images[:, 1:] # drop cls token + negative_images = image_encoder(negative_images).last_hidden_state + negative_images = negative_images[:, 1:] + + height = width = int(sqrt(images.shape[1])) + images = rearrange(images, '1 (h w) c -> c 1 h w', h=height, w=width) + negative_images = rearrange(negative_images, '1 (h w) c -> c 1 h w', h=height, w=width) + images = images.unsqueeze(0) # 1 C 1 H W + negative_images = negative_images.unsqueeze(0) + + vip_out = self.vip_adapter(hidden_states=images, use_image_num=0) + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N + + negative_vip_out = self.vip_adapter(hidden_states=negative_images, use_image_num=0) + negative_vip_tokens, negative_vip_cond_mask = negative_vip_out.hidden_states, negative_vip_out.vip_cond_mask + + return vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask + + @torch.no_grad() + def get_video_embeds(self, condition_images, num_frames, image_processor, image_encoder, transform, device, weight_dtype=torch.float32): + if len(condition_images) == 1: + condition_images_indices = [0] + elif len(condition_images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in condition_images] + condition_images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in condition_images] # F [1 H W C] + condition_images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in condition_images]) # resize, [F C H W] + + condition_images = image_processor(images=condition_images, return_tensors="pt").pixel_values # F C H W + condition_images = condition_images.to(device=device, dtype=weight_dtype) + _, C, H, W = condition_images.shape + video = torch.zeros([num_frames, C, H, W], device=device, dtype=weight_dtype) + video[condition_images_indices] = condition_images + negative_video = torch.zeros_like(video, device=device, dtype=weight_dtype) + + video = image_encoder(video).last_hidden_state # F N D + video = video[:, 1:] # drop cls token + negative_video = image_encoder(negative_video).last_hidden_state + negative_video = negative_video[:, 1:] + + height = width = int(sqrt(video.shape[1])) + video = rearrange(video, 't (h w) c -> c t h w', h=height, w=width) + negative_video = rearrange(negative_video, 't (h w) c -> c t h w', h=height, w=width) + + video = video.unsqueeze(0) # 1 C F H W + negative_video = negative_video.unsqueeze(0) + + vip_out = self.vip_adapter(hidden_states=video, use_image_num=0) + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N + + negative_vip_out = self.vip_adapter(hidden_states=negative_video, use_image_num=0) + negative_vip_tokens, negative_vip_cond_mask = negative_vip_out.hidden_states, negative_vip_out.vip_cond_mask + + return vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask - frame_count, height, width, channels = video.shape - fourcc = cv2.VideoWriter_fourcc(*'mp4v') - out = cv2.VideoWriter(save_path, fourcc, fps, (width, height)) + def forward(self, model, latent_model_input, timestep, **model_kwargs): + enable_temporal_attentions = True if args.num_frames > 1 else False + + encoder_hidden_states = model_kwargs.pop('encoder_hidden_states', None) + encoder_attention_mask = model_kwargs.pop('encoder_attention_mask', None) + clip_feature = model_kwargs.pop('clip_feature', None) + use_image_num = model_kwargs.pop('use_image_num', 0) + assert encoder_hidden_states is not None and encoder_attention_mask is not None and clip_feature is not None, "VIPNet requires encoder_hidden_states, encoder_attention_mask and clip_feature" + hidden_states = rearrange(clip_feature, 'b t h w c -> b c t h w') + + vip_out = self.vip_adapter(hidden_states=hidden_states, use_image_num=use_image_num) # B D T+image_num H W -> B 1+image_num N D + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask + + model_pred = model( + hidden_states=latent_model_input, + timestep=timestep, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + vip_hidden_states=vip_tokens, + vip_attention_mask=vip_cond_mask, + use_image_num=use_image_num, + enable_temporal_attentions=enable_temporal_attentions, + **model_kwargs + )[0] + + return model_pred - for i in range(frame_count): - frame = video[i].cpu().numpy() - frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) - out.write(frame) @torch.inference_mode() -def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): +def log_validation( + args, + model, + vip, + vae, + text_encoder, + tokenizer, + image_processor, + image_encoder, + accelerator, + weight_dtype, + global_step, + ema=False +): + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" prompt_file = os.path.join(validation_dir, "prompt.txt") @@ -108,20 +295,17 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh else: break - resize = [CenterCropResizeVideo((args.max_height, args.max_width)),] - norm_fun = Lambda(lambda x: 2. * x - 1.) - transform = transforms.Compose([ - ToTensorVideo(), - *resize, - # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription - norm_fun - ]) - + resize_transform = transforms.Compose( + [CenterCropResizeVideo((args.max_height, args.max_width))] + ) logger.info(f"Running {'normal' if not ema else 'ema'} validation....\n") - model = accelerator.unwrap_model(model) + + vip = accelerator.unwrap_model(vip) + + vip.eval() scheduler = PNDMScheduler() - pipeline = OpenSoraInpaintPipeline( + pipeline = OpenSoraPipeline( vae=vae, text_encoder=text_encoder, tokenizer=tokenizer, @@ -129,62 +313,102 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh transformer=model ).to(device=accelerator.device) - def preprocess_images(images): - if len(images) == 1: - condition_images_indices = [0] - elif len(images) == 2: - condition_images_indices = [0, -1] - condition_images = [Image.open(image).convert("RGB") for image in images] - condition_images = [torch.from_numpy(np.copy(np.array(image))) for image in condition_images] - condition_images = [rearrange(image, 'h w c -> c h w').unsqueeze(0) for image in condition_images] - condition_images = [transform(image).to(accelerator.device, dtype=torch.float32) for image in condition_images] - return dict(condition_images=condition_images, condition_images_indices=condition_images_indices) - + + pipeline.__call__ = hacked_pipeline_call_for_vip.__get__(pipeline, OpenSoraPipeline) + videos = [] + gen_img = False for prompt, images in zip(validation_prompt, validation_images_list): if not isinstance(images, list): images = [images] logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) - - pre_results = preprocess_images(images) - condition_images = pre_results['condition_images'] - condition_images_indices = pre_results['condition_images_indices'] + if args.num_frames == 1: + if len(images) == 1 and images[0].split('/')[-1].split('_')[0] == 'img': + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( + images=images, + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + gen_img = True + else: + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_video_embeds( + condition_images=images, + num_frames=args.num_frames, + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + else: + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( + images=images[0], # only using first image + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + gen_img = True - video = pipeline( + video = pipeline.__call__( prompt=prompt, - condition_images=condition_images, - condition_images_indices=condition_images_indices, - num_frames=args.num_frames, + negative_prompt="", + vip_tokens=vip_tokens, + vip_attention_mask=vip_cond_mask, + negative_vip_tokens=negative_vip_tokens, + negative_vip_attention_mask=negative_vip_cond_mask, + num_frames=(1 if gen_img else args.num_frames), height=args.max_height, width=args.max_width, num_inference_steps=args.num_sampling_steps, guidance_scale=args.guidance_scale, - enable_temporal_attentions=True, num_images_per_prompt=1, mask_feature=True, + device=accelerator.device, + max_squence_length=args.model_max_length, ).images videos.append(video[0]) + gen_img = False # import ipdb;ipdb.set_trace() # Save the generated videos - save_dir = os.path.join(validation_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") + save_dir = os.path.join(args.output_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") os.makedirs(save_dir, exist_ok=True) + for idx, video in enumerate(videos): - save_video(video, os.path.join(save_dir, f"video_{idx:06d}.mp4")) - videos = torch.stack(videos).numpy() - videos = rearrange(videos, 'b t h w c -> b t c h w') - + if video.shape[0] == 1: # image + ext = 'png' + Image.fromarray(video[0].cpu().numpy()).save(os.path.join(save_dir, f'{idx}.{ext}')) + else: # video + ext = 'mp4' + imageio.mimwrite( + os.path.join(save_dir, f'{idx}.{ext}'), video, fps=24, quality=6) # highest quality is 10, lowest is 0 + + + # for wandb + resize_transform = transforms.Compose( + [CenterCropResizeVideo((args.max_height // 4, args.max_width // 4))] + ) + + videos = [resize_transform(video.permute(0, 3, 1, 2)) for video in videos] + + for tracker in accelerator.trackers: if tracker.name == "wandb": import wandb - - logs = { - f"{'ema_' if ema else ''}validation_videos": [ - wandb.Video(video, caption=f"{i}: {prompt}", fps=24) - for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) - ], - } + logs = {} + logs[f"{'ema_' if ema else ''}validation_videos"] = [] + logs[f"{'ema_' if ema else ''}validation_images"] = [] + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)): + if video.shape[0] == 1: # image + logs[f"{'ema_' if ema else ''}validation_images"].append(wandb.Image(video[0], caption=f"{i}: {prompt}")) + else: # video + logs[f"{'ema_' if ema else ''}validation_videos"].append(wandb.Video(video, caption=f"{i}: {prompt}", fps=24)) tracker.log(logs, step=global_step) @@ -192,9 +416,7 @@ def preprocess_images(images): del pipeline gc.collect() torch.cuda.empty_cache() - - - + vip.train() class ProgressInfo: def __init__(self, global_step, train_loss=0.0): @@ -210,9 +432,6 @@ def main(args): logging_dir = Path(args.output_dir, args.logging_dir) - if torch_npu is not None and npu_config is not None: - npu_config.print_msg(args) - npu_config.seed_everything() accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) accelerator = Accelerator( @@ -295,6 +514,7 @@ def main(args): args.latent_size_t = latent_size_t = args.num_frames // ae_stride_t + 1 else: latent_size_t = args.num_frames // ae_stride_t + # when training video ip adapter, we use t2v model model = Diffusion_models[args.model]( in_channels=ae_channel_config[args.ae], out_channels=ae_channel_config[args.ae] * 2, # 因为要加载预训练权重,所以这里out_channels仍然设置为2倍 @@ -322,16 +542,89 @@ def main(args): ) model.gradient_checkpointing = args.gradient_checkpointing - # # use pretrained model? - if args.pretrained: - model.custom_load_state_dict(args.pretrained) + # NOTE replace forward for VIP + model.forward = hacked_forward_for_vip.__get__(model, Diffusion_models[args.model]) + # # use pretrained model? + # NOTE when using inpaint model + # if args.pretrained: + # model.custom_load_state_dict(args.pretrained) - # Freeze vae and text encoders. + if args.pretrained: + model_state_dict = model.state_dict() + if 'safetensors' in args.pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(args.pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(args.pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + logger.info(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # Freeze main model ae.vae.requires_grad_(False) text_enc.requires_grad_(False) - # Set model as trainable. - model.train() + model.requires_grad_(False) + + # load image encoder + if args.image_encoder_type == 'clip': + # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") + image_encoder = CLIPVisionModelWithProjection.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + elif args.image_encoder_type == 'dino': + # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") + image_encoder = AutoModel.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + else: + raise NotImplementedError + + image_encoder.requires_grad_(False) + + if args.pretrained_vip_adapter_path is None: + attn_proc_type_dict = {} + for name, attn_processor in model.attn_processors.items(): + # replace all attn2.processor with VideoIPAttnProcessor + if name.endswith('.attn2.processor'): + attn_proc_type_dict[name] = 'VideoIPAttnProcessor' + else: + attn_proc_type_dict[name] = attn_processor.__class__.__name__ + + vip = VIPNet( + image_encoder_type=args.image_encoder_type, + cross_attention_dim=1152, + num_tokens=272, # NOTE should be modified + vip_inner_dim=1024, + vip_num_attention_layers=2, + attention_mode=args.attention_mode, + gradient_checkpointing=args.gradient_checkpointing, + vae_scale_factor_t=ae_stride_t, + video_length=latent_size_t, + attn_proc_type_dict=attn_proc_type_dict, + ) + + else: + vip = VIPNet.from_pretrained(args.pretrained_vip_adapter_path) + + vip.train() + + init_from_original_attn_processor = True if args.pretrained_vip_adapter_path is None else False + vip.set_vip_adapter(model, init_from_original_attn_processor=init_from_original_attn_processor) + + # for name, module in vip.named_modules(): + # # # if isinstance(module, VideoIPAttnProcessor): + # # # module.register_full_backward_hook(hook_backward_fn) + # if isinstance(module, nn.Conv3d): + # module.register_backward_hook(hook_backward_fn) noise_scheduler = DDPMScheduler(rescale_betas_zero_snr=args.zero_terminal_snr) # Move unet, vae and text_encoder to device and cast to weight_dtype @@ -339,12 +632,15 @@ def main(args): ae.vae.to(accelerator.device, dtype=torch.float32) # ae.vae.to(accelerator.device, dtype=weight_dtype) text_enc.to(accelerator.device, dtype=weight_dtype) + model.to(accelerator.device, dtype=weight_dtype) + + image_encoder.to(accelerator.device, dtype=weight_dtype) # Create EMA for the unet. if args.use_ema: - ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, - model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + ema_vip = deepcopy(vip) + ema_vip = EMAModel(vip.parameters(), update_after_step=args.ema_start_step, + model_cls=VIPNet, model_config=ema_vip.config) # `accelerate` 0.16.0 will have better support for customized saving if version.parse(accelerate.__version__) >= version.parse("0.16.0"): @@ -352,7 +648,7 @@ def main(args): def save_model_hook(models, weights, output_dir): if accelerator.is_main_process: if args.use_ema: - ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) + ema_vip.save_pretrained(os.path.join(output_dir, "model_ema")) for i, model in enumerate(models): model.save_pretrained(os.path.join(output_dir, "model")) @@ -362,9 +658,9 @@ def save_model_hook(models, weights, output_dir): def load_model_hook(models, input_dir): if args.use_ema: - load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) - ema_model.load_state_dict(load_model.state_dict()) - ema_model.to(accelerator.device) + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), VIPNet) + ema_vip.load_state_dict(load_model.state_dict()) + ema_vip.to(accelerator.device) del load_model for i in range(len(models)): @@ -372,7 +668,7 @@ def load_model_hook(models, input_dir): model = models.pop() # load diffusers style into model - load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") + load_model = VIPNet.from_pretrained(input_dir, subfolder="model") model.register_to_config(**load_model.config) model.load_state_dict(load_model.state_dict()) @@ -391,7 +687,7 @@ def load_model_hook(models, input_dir): args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes ) - params_to_optimize = model.parameters() + params_to_optimize = vip.parameters() # Optimizer creation if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): logger.warning( @@ -479,11 +775,11 @@ def load_model_hook(models, input_dir): ) # Prepare everything with our `accelerator`. - model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( - model, optimizer, train_dataloader, lr_scheduler + vip, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + vip, optimizer, train_dataloader, lr_scheduler ) if args.use_ema: - ema_model.to(accelerator.device) + ema_vip.to(accelerator.device) # We need to recalculate our total training steps as the size of the training dataloader may have changed. num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) @@ -494,6 +790,7 @@ def load_model_hook(models, input_dir): # We need to initialize the trackers we use, and also store our configuration. # The trackers initializes automatically on the main process. + # NOTE wandb if accelerator.is_main_process: project_name = os.getenv('PROJECT', os.path.basename(args.output_dir)) entity = os.getenv('ENTITY', None) @@ -508,17 +805,20 @@ def load_model_hook(models, input_dir): total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps logger.info("***** Running training *****") - logger.info(f" Model = {model}") + logger.info(f" Model = {vip}") logger.info(f" Num examples = {len(train_dataset)}") logger.info(f" Num Epochs = {args.num_train_epochs}") logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") logger.info(f" Total optimization steps = {args.max_train_steps}") - logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + logger.info(f" Total trainable parameters = {sum(p.numel() for p in vip.parameters() if p.requires_grad) / 1e9} B") global_step = 0 first_epoch = 0 + # NOTE checking update of params + # initial_params = {name: param.clone() for name, param in vip.named_parameters()} + # Potentially load in the weights and states from a previous save if args.resume_from_checkpoint: if args.resume_from_checkpoint != "latest": @@ -544,8 +844,6 @@ def load_model_hook(models, input_dir): initial_global_step = global_step first_epoch = global_step // num_update_steps_per_epoch - if npu_config is not None: - train_dataset.n_used_elements = global_step * args.train_batch_size else: initial_global_step = 0 @@ -562,16 +860,11 @@ def load_model_hook(models, input_dir): def sync_gradients_info(loss): # Checks if the accelerator has performed an optimization step behind the scenes if args.use_ema: - ema_model.step(model.parameters()) + ema_vip.step(vip.parameters()) progress_bar.update(1) progress_info.global_step += 1 end_time = time.time() - one_step_duration = end_time - start_time accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) - if torch_npu is not None and npu_config is not None: - npu_config.print_msg(f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, " - f"train_loss={progress_info.train_loss}, time_cost={one_step_duration}", - rank=0) progress_info.train_loss = 0.0 # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. @@ -622,21 +915,16 @@ def run(model_input, model_kwargs, prof): # (this is the forward diffusion process) noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) - assert not torch.any(torch.isnan(noisy_model_input)), 'torch.any(torch.isnan(noisy_model_input))' - - if model_kwargs.get("masked_x", None) is not None and model_kwargs.get("mask", None) is not None: - masked_x = model_kwargs.pop("masked_x") - mask = model_kwargs.pop("mask") - - model_pred = model( - torch.cat([noisy_model_input, masked_x, mask], dim=1), - timesteps, - **model_kwargs - )[0] - + + model_pred = vip( + model=model, + latent_model_input=noisy_model_input, + timestep=timesteps, + **model_kwargs, + ) model_pred = torch.chunk(model_pred, 2, dim=1)[0] - assert not torch.any(torch.isnan(model_pred)), 'torch.any(torch.isnan(model_pred))' + # Get the target for loss depending on the prediction type if args.prediction_type is not None: @@ -682,7 +970,7 @@ def run(model_input, model_kwargs, prof): # Backpropagate accelerator.backward(loss) if accelerator.sync_gradients: - params_to_clip = model.parameters() + params_to_clip = vip.parameters() accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) optimizer.step() lr_scheduler.step() @@ -696,18 +984,40 @@ def run(model_input, model_kwargs, prof): if progress_info.global_step % args.checkpointing_steps == 0: if args.enable_tracker: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step) + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ) if args.use_ema: # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - if npu_config is None: - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step, ema=True) + ema_vip.store(vip.parameters()) + ema_vip.copy_to(vip.parameters()) + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ema=True, + ) # Switch back to the original UNet parameters. - ema_model.restore(model.parameters()) + ema_vip.restore(vip.parameters()) if prof is not None: prof.step() @@ -716,58 +1026,71 @@ def run(model_input, model_kwargs, prof): return loss def train_one_step(step_, data_item_, prof_=None): + # NOTE checking params update + # if step_ > 1: + # print("Comparing model parameters before and after training step:") + # for name, param in vip.named_parameters(): + # if not torch.equal(param, initial_params[name]): + # print(f"Parameter {name} has changed.") + # initial_params[name] = param.clone() + # else: + # print(f"Parameter {name} has not changed!") + train_loss = 0.0 - x, attn_mask, input_ids, cond_mask = data_item_ + x, attn_mask, input_ids, cond_mask, clip_data = data_item_ # Sample noise that we'll add to the latents if not args.multi_scale: assert torch.all(attn_mask) assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' - x = x.to(accelerator.device, dtype=ae.vae.dtype) # B 3 * C T H W + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+image_num H W + clip_data = clip_data.to(accelerator.device) # B T+image_num C H W - attn_mask = attn_mask.to(accelerator.device) # B T H W - input_ids = input_ids.to(accelerator.device) # B 1 L - cond_mask = cond_mask.to(accelerator.device) # B 1 L + attn_mask = attn_mask.to(accelerator.device) # B T+image_num H W + input_ids = input_ids.to(accelerator.device) # B 1+image_num L + cond_mask = cond_mask.to(accelerator.device) # B 1+image_num L # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) - assert not torch.any(torch.isnan(x)), 'before vae' - assert not torch.any(torch.isnan(input_ids)), 'before text_enc' - assert not torch.any(torch.isnan(cond_mask)), 'before text_enc' - assert not torch.any(torch.isnan(attn_mask)), 'before text_enc' - with torch.no_grad(): # import ipdb;ipdb.set_trace() # use for loop to avoid OOM, because T5 is too huge... - B, N, L = input_ids.shape # B 1 L + B, N, L = input_ids.shape # B 1+image_num L # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D # use batch inference input_ids_ = input_ids.reshape(-1, L) cond_mask_ = cond_mask.reshape(-1, L) - cond = text_enc(input_ids_, cond_mask_) # B 1 L D + cond = text_enc(input_ids_, cond_mask_) assert not torch.any(torch.isnan(cond)), 'after text_enc' - cond = cond.reshape(B, N, L, -1) + cond = cond.reshape(B, N, L, -1) # B 1+image_num L D + + clip_data = rearrange(clip_data, 'b t c h w -> (b t) c h w') # B T+image_num C H W -> B * (T+image_num) C H W + clip_feature = image_encoder(clip_data).last_hidden_state # B * (T+image_num) N D + clip_feature = clip_feature[:, 1:] # drop cls token + clip_feature_height = clip_feature_width = int(sqrt(clip_feature.shape[1])) + clip_feature = rearrange(clip_feature, '(b t) (h w) c -> b t h w c', b=B, h=clip_feature_height, w=clip_feature_width) # B T+image_num H W D # Map input images to latent space + normalize latents - assert args.use_image_num == 0, 'Inpainting mode does not support image joint training' - - x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:9] - x, masked_x, mask = ae.encode(x), ae.encode(masked_x), ae.encode(mask) + if args.use_image_num == 0: + x = ae.encode(x) # B C T H W + else: + videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] + videos = ae.encode(videos) # B C T H W + images = rearrange(images, 'b c t h w -> (b t) c 1 h w') + images = ae.encode(images) + images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w assert not torch.any(torch.isnan(x)), 'after vae' - assert not torch.any(torch.isnan(masked_x)), 'after vae' - assert not torch.any(torch.isnan(mask)), 'after vae' - with accelerator.accumulate(model): + with accelerator.accumulate(vip): x = x.to(weight_dtype) - masked_x = masked_x.to(weight_dtype) - mask = mask.to(weight_dtype) assert not torch.any(torch.isnan(x)), 'after vae' model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, - masked_x=masked_x, mask=mask) + clip_feature=clip_feature) run(x, model_kwargs, prof_) if progress_info.global_step >= args.max_train_steps: @@ -783,38 +1106,25 @@ def train_all_epoch(prof_=None): for step, data_item in enumerate(train_dataloader): if accelerator.is_main_process: - if step == 0: + if progress_info.global_step == 0: print("before training, we need to check the validation mode...") log_validation( - args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, global_step=0, ema=False + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, ) if train_one_step(step, data_item, prof_): break - if step >= 2 and torch_npu is not None and npu_config is not None: - npu_config.free_mm() - - if npu_config is not None and npu_config.on_npu and npu_config.profiling: - experimental_config = torch_npu.profiler._ExperimentalConfig( - profiler_level=torch_npu.profiler.ProfilerLevel.Level1, - aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization - ) - profile_output_path = f"/home/image_data/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" - os.makedirs(profile_output_path, exist_ok=True) - - with torch_npu.profiler.profile( - activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], - with_stack=True, - record_shapes=True, - profile_memory=True, - experimental_config=experimental_config, - schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, - skip_first=0), - on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") - ) as prof: - train_all_epoch(prof) - else: - train_all_epoch() + train_all_epoch() accelerator.wait_for_everyone() accelerator.end_training() @@ -831,13 +1141,8 @@ def train_all_epoch(prof_=None): parser.add_argument("--max_height", type=int, default=320) parser.add_argument("--max_width", type=int, default=240) - # inpaint dataset - parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode - parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode - parser.add_argument("--default_text_ratio", type=float, default=0.1) parser.add_argument("--use_img_from_vid", action="store_true") - parser.add_argument("--use_image_num", type=int, default=0) parser.add_argument("--model_max_length", type=int, default=512) parser.add_argument("--multi_scale", action="store_true") parser.add_argument('--cfg', type=float, default=0.1) @@ -948,7 +1253,16 @@ def train_all_epoch(prof_=None): parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + # inpaint dataset + parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode + parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode + parser.add_argument("--default_text_ratio", type=float, default=0.1) parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") + parser.add_argument("--image_encoder_type", type=str, default='clip', choices=['clip', 'dino']) + parser.add_argument("--image_encoder_name", type=str, default='laion/CLIP-ViT-H-14-laion2B-s32B-b79K') + parser.add_argument("--pretrained_vip_adapter_path", type=str, default=None) + parser.add_argument("--clear_video_ratio", type=float, default=0.0) + parser.add_argument("--use_image_num", type=int, default=0) args = parser.parse_args() diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 07a0a16f6..a2310b75f 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -170,3 +170,34 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p # 0, max_patchify_latent_size[0] - i[0]), value=0) for i in valid_patchify_latent_size] # attention_mask = torch.stack(attention_mask) # b t h w return pad_batch_tubes, attention_mask + +class VideoIP_Collate(Collate): + def __init__(self, args): + super().__init__(args) + + + def package_clip_data(self, batch): + + clip_vid, clip_img = None, None + if self.num_frames > 1: + clip_vid = torch.stack([i['video_data']['clip_video'] for i in batch]) # [b t c h w] + + if self.num_frames == 1 or self.use_image_num != 0: + clip_img = torch.stack([i['image_data']['clip_image'] for i in batch]) # [b num_img c h w] + + return clip_vid, clip_img + + def __call__(self, batch): + clip_vid, clip_img = self.package_clip_data(batch) + + pad_batch_tubes, attention_mask, input_ids, cond_mask = super().__call__(batch) + + if self.num_frames > 1 and self.use_image_num == 0: + clip_data = clip_vid # b t c h w + elif self.num_frames > 1 and self.use_image_num != 0: + clip_data = torch.cat([clip_vid, clip_img], dim=1) # b t+num_img c h w + else: # num_frames == 1 (only image) + clip_data = clip_img # b 1 c h w + + return pad_batch_tubes, attention_mask, input_ids, cond_mask, clip_data + \ No newline at end of file diff --git a/scripts/accelerate_configs/deepspeed_zero2_config.yaml b/scripts/accelerate_configs/deepspeed_zero2_config.yaml index 0d3769363..b15390113 100644 --- a/scripts/accelerate_configs/deepspeed_zero2_config.yaml +++ b/scripts/accelerate_configs/deepspeed_zero2_config.yaml @@ -8,6 +8,6 @@ main_process_ip: null main_process_port: 29503 main_training_function: main num_machines: 1 -num_processes: 2 -gpu_ids: 0,1 +num_processes: 8 +gpu_ids: 0,1,2,3,4,5,6,7 use_cpu: false diff --git a/scripts/text_condition/gpu/sample_video_inpaint.sh b/scripts/text_condition/gpu/sample_video_inpaint.sh new file mode 100644 index 000000000..75a3b7409 --- /dev/null +++ b/scripts/text_condition/gpu/sample_video_inpaint.sh @@ -0,0 +1,16 @@ +CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_inpaint.py \ + --model_path "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ + --num_frames 65 \ + --height 512 \ + --width 512 \ + --cache_dir "/storage/cache_dir" \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --text_prompt examples/prompt_list_0.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ + --validation_dir "/storage/gyy/hw/Open-Sora-Plan/test_dir" \ + --output_dir "./test_sample" \ + --guidance_scale 7.5 \ + --num_sampling_steps 50 \ + --enable_tiling \ + --sample_method PNDM \ \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh index 1557f51a9..5bc6277ce 100644 --- a/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh +++ b/scripts/text_condition/gpu/train_inpaint_video21d_65x512x512.sh @@ -1,7 +1,8 @@ +PROJECT="inpaint_65x512x512_1node_bs2_lr2e-5_snrgamma_noiseoffset_new_mask" export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" # # export WANDB_MODE="offline" export ENTITY="yunyangge" -export PROJECT="inpaint_65x512x512_1node_bs2_lr2e-5" +export PROJECT=$PROJECT export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 export TOKENIZERS_PARALLELISM=false @@ -31,7 +32,7 @@ accelerate launch \ --train_batch_size=2 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ - --max_train_steps=100000 \ + --max_train_steps=200000 \ --learning_rate=2e-5 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ @@ -39,20 +40,23 @@ accelerate launch \ --report_to="wandb" \ --enable_tracker \ --checkpointing_steps=500 \ - --output_dir="inpaint_65x512x512_1node_bs2_lr2e-5" \ + --output_dir="./test_sample" \ --allow_tf32 \ --model_max_length 300 \ --enable_tiling \ - --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ - --validation_dir "validation_dir" \ + --validation_dir "test_dir" \ --use_image_num 0 \ + --guidance_scale 7.5 \ + --num_sampling_steps 50 \ --cfg 0.1 \ --ema_start_step 0 \ --use_ema \ --i2v_ratio 0.4 \ --transition_ratio 0.4 \ --default_text_ratio 0.1 \ - --seed 42 \ - # --snr_gamma 5.0 \ - # --zero_terminal_snr \ - # --noise_offset 0.02 \ \ No newline at end of file + --seed 231 \ + --snr_gamma 5.0 \ + --noise_offset 0.02 \ + --pretrained "/storage/gyy/hw/Open-Sora-Plan/inpaint_65x512x512_1node_bs2_lr2e-5_snrgamma_noiseoffset_new_mask/checkpoint-30000/model_ema/diffusion_pytorch_model.safetensors" + # --resume_from_checkpoint "latest" \ + # --zero_terminal_snr \ \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video_ip_video21d.sh b/scripts/text_condition/gpu/train_video_ip_video21d.sh new file mode 100644 index 000000000..920f921fe --- /dev/null +++ b/scripts/text_condition/gpu/train_video_ip_video21d.sh @@ -0,0 +1,66 @@ +PROJECT="videoip_65x512x512_1node_bs64_lr1e-4_snrgamma_noiseoffset_mjsam" +export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" +# # export WANDB_MODE="offline" +export ENTITY="yunyangge" +export PROJECT=$PROJECT +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export TOKENIZERS_PARALLELISM=false +# # NCCL setting IB网卡时用 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_videoip.py \ + --model "LatteT2V-S/122" \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --image_encoder_type "clip" \ + --image_encoder_name "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" \ + --cache_dir "/storage/cache_dir" \ + --dataset vip \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ + --video_data "scripts/train_data/video_data_debug.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --use_image_num 0 \ + --max_height 512 \ + --max_width 512 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=64 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=500000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --enable_tracker \ + --checkpointing_steps=500 \ + --output_dir=$PROJECT \ + --allow_tf32 \ + --model_max_length 300 \ + --enable_tiling \ + --validation_dir "validation_dir" \ + --guidance_scale 7.5 \ + --num_sampling_steps 50 \ + --ema_start_step 0 \ + --use_ema \ + --cfg 0.05 \ + --i2v_ratio 0.3 \ + --transition_ratio 0.3 \ + --clear_video_ratio 0.3 \ + --default_text_ratio 0.5 \ + --seed 42 \ + --snr_gamma 5.0 \ + --noise_offset 0.02 \ + --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" + # --resume_from_checkpoint "latest" \ + # --zero_terminal_snr \ \ No newline at end of file diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index b404a2d2a..0758b4fd6 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,4 +1,2 @@ /storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json -/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json -/storage/dataset/image/anytext3m,/storage/anno_jsons/anytext_all_3509994.json /storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json \ No newline at end of file diff --git a/scripts/train_data/image_data_debug.txt b/scripts/train_data/image_data_debug.txt index 0c7df18fc..788402176 100644 --- a/scripts/train_data/image_data_debug.txt +++ b/scripts/train_data/image_data_debug.txt @@ -1 +1 @@ -/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json \ No newline at end of file +/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json \ No newline at end of file diff --git a/scripts/train_data/video_data.txt b/scripts/train_data/video_data.txt index 7f9c55548..dce97665d 100644 --- a/scripts/train_data/video_data.txt +++ b/scripts/train_data/video_data.txt @@ -1,3 +1,2 @@ -/dxyl_data02/datasets/pixabay_v2,/dxyl_data02/anno_jsons/video_pixabay_65f_601513.json -/dxyl_data02/datasets/pexels,/dxyl_data02/anno_jsons/video_pexel_65f_3832666.json -/dxyl_data02/datasets/mixkit,/dxyl_data02/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/storage/dataset/mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json +/storage/dataset/pexels,/storage/anno_jsons/video_pexel_65f_3832666.json \ No newline at end of file diff --git a/validation_dir/prompt.txt b/validation_dir/prompt.txt index 8cc53e02c..27ae678b4 100644 --- a/validation_dir/prompt.txt +++ b/validation_dir/prompt.txt @@ -1,6 +1,9 @@ A rocket ascends slowly into the sky. -Along the coast, variously sized boats float on the lake, with the camera gradually zooming in. +A coffee cup with "anytext" foam floating on it. The landscape at sunset is profound and expansive. This video showcases the movement of flower and bird painting, with a smooth transition from flowers to birds in the picture. This video showcases the scenery of snow capped mountains. -This video showcases the night view of the city. \ No newline at end of file +This video showcases the night view of the city. +A rocket ascends slowly into the sky. +Along the coast, variously sized boats float on the lake, with the camera gradually zooming in. +The landscape at sunset is profound and expansive. \ No newline at end of file From 219a3026055b175c93e858736f94b46ee9036523 Mon Sep 17 00:00:00 2001 From: apprivoiser <1471742727@qq.com> Date: Mon, 1 Jul 2024 15:59:16 +0800 Subject: [PATCH 068/134] opensora3d on npu & fix dataset bug --- opensora/dataset/t2v_datasets.py | 7 ++- .../diffusion/opensora/modeling_opensora.py | 3 +- opensora/models/diffusion/opensora/modules.py | 16 ++++-- opensora/npu_config.py | 3 + opensora/sample/pipeline_opensora.py | 14 ++--- opensora/sample/sample_t2v_on_npu.py | 20 +++++-- scripts/text_condition/npu/sample_video.sh | 20 ++++--- .../npu/train_video3d_nx240p.sh | 55 +++++++++++++++++++ .../npu/train_video3d_sp_nx240p.sh | 55 +++++++++++++++++++ 9 files changed, 166 insertions(+), 27 deletions(-) create mode 100644 scripts/text_condition/npu/train_video3d_nx240p.sh create mode 100644 scripts/text_condition/npu/train_video3d_sp_nx240p.sh diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 9829640b1..a21207385 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -192,6 +192,8 @@ def get_video(self, idx): video = video.transpose(0, 1) # T C H W -> C T H W text = dataset_prog.vid_cap_list[idx]['cap'] + if not isinstance(text, list): + text = [text] text = [random.choice(text)] text = text_preprocessing(text, support_Chinese=self.support_Chinese) if random.random() > self.cfg else "" @@ -289,7 +291,10 @@ def read_jsons(self, data, postfix=".jpg"): for i in tqdm(range(len(sub_list))): sub_list[i]['path'] = opj(folder, sub_list[i]['path']) if npu_config is not None: - sub_list = filter_json_by_existed_files(folder, sub_list, postfix=postfix) + if "civitai" in anno or "ideogram" in anno or "human" in anno: + sub_list = sub_list[npu_config.get_node_id()::npu_config.get_node_size()] + else: + sub_list = filter_json_by_existed_files(folder, sub_list, postfix=postfix) cap_lists += sub_list return cap_lists diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index ad397e5c9..2e0b3354d 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -84,7 +84,8 @@ def __init__( use_additional_conditions: Optional[bool] = None, attention_mode: str = 'xformers', downsampler: str = None, - use_rope: bool = False, + use_rope: bool = False, + use_stable_fp32: bool = False, ): super().__init__() diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index a5c05ed2f..8698135ff 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -529,8 +529,8 @@ def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_th padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True) - self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) - self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) @@ -695,6 +695,8 @@ def __call__( query = query.view(-1, attn.heads, head_dim) # [s // sp, b, h * d] -> [s // sp * b, h, d] key = key.view(-1, attn.heads, head_dim) value = value.view(-1, attn.heads, head_dim) + # query = attn.q_norm(query) + # key = attn.k_norm(key) h_size = attn.heads * head_dim sp_size = hccl_info.world_size h_size_sp = h_size // sp_size @@ -725,15 +727,17 @@ def __call__( else: dtype = None + query = query.view(batch_size, -1, attn.heads, head_dim) + key = key.view(batch_size, -1, attn.heads, head_dim) + # query = attn.q_norm(query) + # key = attn.k_norm(key) if self.use_rope: - query = query.view(batch_size, -1, attn.heads, head_dim) - key = key.view(batch_size, -1, attn.heads, head_dim) # require the shape of (batch_size x nheads x ntokens x dim) pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) query = self.rope(query, pos_thw) key = self.rope(key, pos_thw) - query = query.view(batch_size, -1, attn.heads * head_dim) - key = key.view(batch_size, -1, attn.heads * head_dim) + query = query.view(batch_size, -1, attn.heads * head_dim) + key = key.view(batch_size, -1, attn.heads * head_dim) with set_run_dtype(query, dtype): query, key, value = npu_config.set_current_run_dtype([query, key, value]) diff --git a/opensora/npu_config.py b/opensora/npu_config.py index 1e911d93c..7e39ef034 100644 --- a/opensora/npu_config.py +++ b/opensora/npu_config.py @@ -181,6 +181,9 @@ def get_output_video_path(self, name): os.makedirs(f"{self.work_path}/output_videos", exist_ok=True) return f"{self.work_path}/output_videos/{name}" + def get_node_id(self): + return self.rank // self.node_world_size + def get_node_size(self): return self.world_size // self.node_world_size diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 6c98105da..9ec6cc3bc 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -815,13 +815,13 @@ def __call__( def decode_latents(self, latents): - # if npu_config: - # npu_config.print_tensor_stats(latents, f"before vae", rank=0) - # print(f'before vae decode', torch.max(latents).item(), torch.min(latents).item(), torch.mean(latents).item(), torch.std(latents).item()) - video = self.vae.decode(latents.to(self.vae.vae.dtype)) - # if npu_config: - # npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) - # print(f'after vae decode', torch.max(video).item(), torch.min(video).item(), torch.mean(video).item(), torch.std(video).item()) + device = torch.cuda.current_device() + if npu_config is not None: + npu_config.print_tensor_stats(latents, f"before vae", rank=0) + self.vae = self.vae.to(device) + video = self.vae.decode(latents.to(self.vae.vae.dtype).to(device)) + if npu_config is not None: + npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 return video diff --git a/opensora/sample/sample_t2v_on_npu.py b/opensora/sample/sample_t2v_on_npu.py index 16a5d5f3a..795526338 100644 --- a/opensora/sample/sample_t2v_on_npu.py +++ b/opensora/sample/sample_t2v_on_npu.py @@ -13,7 +13,7 @@ from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel from omegaconf import OmegaConf from torchvision.utils import save_image -from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer +from transformers import T5EncoderModel, MT5EncoderModel, T5Tokenizer, AutoTokenizer import os, sys @@ -23,6 +23,7 @@ from opensora.models.diffusion.udit.modeling_udit import UDiTT2V from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.diffusion.udit_ultra.modeling_udit_ultra import UDiTUltraT2V from opensora.models.text_encoder import get_text_enc from opensora.utils.utils import save_video_grid @@ -47,6 +48,10 @@ def load_t2v_checkpoint(model_path): transformer_model = OpenSoraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + elif args.udit: + transformer_model = UDiTUltraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) else: transformer_model = LatteT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) @@ -83,11 +88,14 @@ def run_model_and_save_images(pipeline, model_path): checkpoint_name = f"{os.path.basename(model_path)}" + positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" + negative_prompt = "nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry" for index, prompt in enumerate(args.text_prompt): if index % npu_config.N_NPU_PER_NODE != local_rank: continue print('Processing the ({}) prompt'.format(prompt)) - videos = pipeline(prompt, + videos = pipeline(positive_prompt.format(prompt), + negative_prompt=negative_prompt, num_frames=args.num_frames, height=args.height, width=args.width, @@ -165,6 +173,7 @@ def get_file_name(): parser.add_argument('--tile_overlap_factor', type=float, default=0.25) parser.add_argument('--enable_tiling', action='store_true') parser.add_argument('--model_3d', action='store_true') + parser.add_argument('--udit', action='store_true') args = parser.parse_args() npu_config.print_msg(args) @@ -190,9 +199,9 @@ def get_file_name(): vae.vae.tile_overlap_factor = args.tile_overlap_factor vae.vae_scale_factor = ae_stride_config[args.ae] - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, - low_cpu_mem_usage=True, torch_dtype=weight_dtype).to(device) - tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + text_encoder = MT5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, + low_cpu_mem_usage=True, torch_dtype=torch.float16).to(device) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) # set eval mode vae.eval() @@ -246,6 +255,7 @@ def get_file_name(): npu_config.print_msg(f"The latest_path is {latest_path}") full_path = f"{args.model_path}/{latest_path}/model_ema" + # full_path = "/home/opensora/captions/240p_model_ema" pipeline = load_t2v_checkpoint(full_path) if npu_config.on_npu and npu_config.profiling: diff --git a/scripts/text_condition/npu/sample_video.sh b/scripts/text_condition/npu/sample_video.sh index 81a1ae823..b848e0801 100644 --- a/scripts/text_condition/npu/sample_video.sh +++ b/scripts/text_condition/npu/sample_video.sh @@ -7,20 +7,26 @@ if [ -z "$SAMPLE_SAVE_PATH" ]; then export SAMPLE_SAVE_PATH="/home/image_data/sample_videos" fi +if [ -z "$SAMPLE_HEIGHT" ]; then + echo "You should set both envs of SAMPLE_HEIGHT and SAMPLE_WIDTH" + return +fi + torchrun --nproc_per_node=8 opensora/sample/sample_t2v_on_npu.py \ --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ --num_frames ${NUM_FRAME} \ - --height 240 \ - --width 320 \ - --cache_dir "./cache_dir" \ - --text_encoder_name ${WEIGHT_PATH}/DeepFloyd/t5-v1_1-xxl \ + --height $SAMPLE_HEIGHT \ + --width $SAMPLE_WIDTH \ + --cache_dir "../cache_dir" \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "${WEIGHT_PATH}/test140k/" \ --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ --fps 24 \ - --guidance_scale 2.0 \ + --guidance_scale 5.0 \ --num_sampling_steps 50 \ + --sample_method PNDM \ --enable_tiling \ - --sample_method DDPM \ - --model_3d + --tile_overlap_factor 0.125 \ + --model_3d \ No newline at end of file diff --git a/scripts/text_condition/npu/train_video3d_nx240p.sh b/scripts/text_condition/npu/train_video3d_nx240p.sh new file mode 100644 index 000000000..7a0244e9e --- /dev/null +++ b/scripts/text_condition/npu/train_video3d_nx240p.sh @@ -0,0 +1,55 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 8 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=4e-5 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=500 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=1000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --use_rope \ + --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/yc_video3d_rope_240p/checkpoint-98000/model_ema/diffusion_pytorch_model.safetensors" \ + --resume_from_checkpoint="latest" \ + --enable_stable_fp32 diff --git a/scripts/text_condition/npu/train_video3d_sp_nx240p.sh b/scripts/text_condition/npu/train_video3d_sp_nx240p.sh new file mode 100644 index 000000000..3511f2d60 --- /dev/null +++ b/scripts/text_condition/npu/train_video3d_sp_nx240p.sh @@ -0,0 +1,55 @@ +export PROJECT=$PROJECT_NAME +WEIGHT_PATH="/home/opensora/pre_weights/" +env +export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset t2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 240 \ + --max_width 320 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 0.5 \ + --interpolation_scale_w 0.5 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 8 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=4e-5 \ + --lr_scheduler="constant" \ + --seed=10 \ + --lr_warmup_steps=500 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=2000 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --use_rope \ + --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/yc_image3d_rope_240p_pretrain_seed_data/checkpoint-18000/model_ema/diffusion_pytorch_model.safetensors" \ + --resume_from_checkpoint="latest" \ + --sp_size 8 \ + --train_sp_batch_size 4 \ + --enable_stable_fp32 From 937719978e5c4652e031c9398449e79b430bc42a Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Fri, 5 Jul 2024 12:14:50 +0800 Subject: [PATCH 069/134] check video training --- .gitignore | 6 +- examples/convert_udit_to_videoudit.py | 58 ++++-- examples/prompt_list_0.txt | 13 ++ opensora/dataset/__init__.py | 22 ++- opensora/dataset/t2v_datasets.py | 172 +++++++++++++++--- opensora/models/diffusion/opensora/modules.py | 4 +- .../udit_ultra/modeling_udit_ultra.py | 135 +++++++++----- .../models/diffusion/udit_ultra/modules.py | 69 ++++--- opensora/models/text_encoder/__init__.py | 4 +- opensora/sample/pipeline_opensora.py | 4 +- opensora/sample/sample_t2v.py | 14 +- opensora/train/train_t2v_diffusers.py | 112 +++++++++--- opensora/utils/dataset_utils.py | 134 +++++++++++++- scripts/accelerate_configs/hostfile | 18 +- .../multi_node_example.yaml | 4 +- scripts/text_condition/gpu/sample_image.sh | 11 +- scripts/text_condition/gpu/sample_video.sh | 20 +- ...n_videouditultra_125x480p_new_rope_fp32.sh | 63 +++++++ ...in_videouditultra_61x480p_new_rope_fp32.sh | 62 +++++++ ...ra_61x480p_new_rope_fp32_aesmovie_sucai.sh | 62 +++++++ ..._61x480p_new_rope_fp32_movie_time_sucai.sh | 62 +++++++ scripts/train_data/video_data.txt | 12 +- .../train_data/video_data_aesmovie_sucai.txt | 15 ++ scripts/train_data/video_data_debug.txt | 2 +- scripts/train_data/video_data_movie_time.txt | 8 + .../video_data_movie_time_sucai.txt | 17 ++ 26 files changed, 913 insertions(+), 190 deletions(-) create mode 100644 scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh create mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh create mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh create mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh create mode 100644 scripts/train_data/video_data_aesmovie_sucai.txt create mode 100644 scripts/train_data/video_data_movie_time.txt create mode 100644 scripts/train_data/video_data_movie_time_sucai.txt diff --git a/.gitignore b/.gitignore index 25a463aea..c5ceeb720 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,8 @@ flash* 65x256* alpha_vae *node* -sample_image*cfg* \ No newline at end of file +sample_image*cfg* +*tmp* +*pymp* +check.py +bucket.py \ No newline at end of file diff --git a/examples/convert_udit_to_videoudit.py b/examples/convert_udit_to_videoudit.py index 0db28fb93..6abf576b8 100644 --- a/examples/convert_udit_to_videoudit.py +++ b/examples/convert_udit_to_videoudit.py @@ -1,28 +1,60 @@ +# import torch + +# from safetensors.torch import load_file as safe_load + +# path = "bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema/diffusion_pytorch_model.safetensors" +# ckpt = safe_load(path, device="cpu") +# new_ckpt = {} +# k_size = 3 +# patch_size_t = 1 +# for k, v in ckpt.items(): +# if 'pos_embed.proj.weight' in k: +# new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) / k_size # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 +# elif 'attn1.downsampler.layer.weight' in k: +# new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) / k_size # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 +# elif 'body.0.weight' in k and 'down' in k: +# in_c = v.shape[0] +# new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 +# elif 'body.0.weight' in k and 'up' in k: +# new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 +# elif 'proj_out' in k: +# if 'weight' in k: +# new_v = v.repeat(patch_size_t, 1) # 16, 768 -> 32, 768 +# elif 'bias' in k: +# new_v = v.repeat(patch_size_t) # 16 -> 32 +# else: +# new_v = v +# new_ckpt[k] = new_v +# torch.save(new_ckpt, f'480p_73000_ema_k{k_size}_p{patch_size_t}.pt') + + import torch from safetensors.torch import load_file as safe_load -path = "/storage/ongoing/new/Open-Sora-Plan/bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" + +def conv2d_to_conv3d(name, conv2d_weights, k_size): + conv3d_weights = torch.zeros_like(conv2d_weights).unsqueeze(2).repeat(1, 1, k_size, 1, 1) + # conv3d_weights[:,:,0,:,:] = conv2d_weights / 10 + conv3d_weights[:,:,1,:,:] = conv2d_weights + # conv3d_weights[:,:,2,:,:] = conv2d_weights / 10 + return conv3d_weights + +path = "bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema/diffusion_pytorch_model.safetensors" ckpt = safe_load(path, device="cpu") new_ckpt = {} k_size = 3 -t_stride = 1 +patch_size_t = 1 for k, v in ckpt.items(): if 'pos_embed.proj.weight' in k: - new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + new_v = conv2d_to_conv3d(k, v, k_size) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 elif 'attn1.downsampler.layer.weight' in k: - new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + new_v = conv2d_to_conv3d(k, v, k_size) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 elif 'body.0.weight' in k and 'down' in k: - in_c = v.shape[0] - new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 + new_v = conv2d_to_conv3d(k, v, k_size) # 384, 768, 3, 3 -> 384, 768, 3, 3, 3 elif 'body.0.weight' in k and 'up' in k: - new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 - elif 'proj_out' in k: - if 'weight' in k: - new_v = v.repeat(t_stride, 1) # 16, 768 -> 32, 768 - elif 'bias' in k: - new_v = v.repeat(t_stride) # 16 -> 32 + new_v = conv2d_to_conv3d(k, v, k_size) # 6144, 3072, 3, 3 -> 6144, 3072, 3, 3, 3 else: new_v = v new_ckpt[k] = new_v -torch.save(new_ckpt, 'convert_weight.pt') \ No newline at end of file +torch.save(new_ckpt, f'480p_73000_ema_k{k_size}_p{patch_size_t}_repeat_lowsize10.pt') \ No newline at end of file diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index 7f3c6dccc..fcbd77448 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -7,6 +7,12 @@ A single butterfly with wings that resemble stained glass flutters through a fie A solitary mermaid swims through an underwater cave filled with glowing crystals. The shot follows her graceful movements, capturing the play of light on her scales and the ethereal beauty of the cave. Close-up of a dragon's eye as it slowly opens, revealing a fiery iris that reflects the burning landscape around it, while smoke wisps off its scaly eyelid. A cat with the enigmatic smile of the Mona Lisa, lounging regally on a velvet cushion, her eyes following a fluttering butterfly that mirrors the mysterious allure of her expression. 4K. +A time-lapse of a storm forming over the ocean, dark clouds gathering and lightning flashing. The storm's energy creates spirals of light that dance across the sky. +A majestic eagle perches on a high cliff, its keen eyes scanning the valley below. With a powerful flap, it takes off, leaving a trail of sparkling feathers. +A single butterfly with wings that resemble stained glass flutters through a field of flowers. The shot captures the light as it passes through the delicate wings, creating a vibrant, colorful display. HD. +A solitary mermaid swims through an underwater cave filled with glowing crystals. The shot follows her graceful movements, capturing the play of light on her scales and the ethereal beauty of the cave. +Close-up of a dragon's eye as it slowly opens, revealing a fiery iris that reflects the burning landscape around it, while smoke wisps off its scaly eyelid. +A cat with the enigmatic smile of the Mona Lisa, lounging regally on a velvet cushion, her eyes following a fluttering butterfly that mirrors the mysterious allure of her expression. 4K. A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures. This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. @@ -16,6 +22,9 @@ a cat wearing sunglasses and working as a lifeguard at pool. An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt, he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film. A lone figure stands on the deck of a spaceship, looking out at a nebula filled with vibrant colors. The shot tracks their gaze, capturing the breathtaking beauty of the cosmic landscape and the sense of infinite possibility. A large orange octopus is seen resting on the bottom of the ocean floor, blending in with the sandy and rocky terrain. lts tentacles are spread out around its body, and its eyes are closed. The octopus is unaware of a king crab that is crawling towards it from behind a rock,its claws raised and ready to attack. The crab is brown and spiny,with long legs and antennae. The scene is captured from a wide angle,showing the vastness and depth of the ocean. The wateris clear and blue, with rays of sunlight filtering through. The shot is sharp and crisp, with a high dynamic range. The octopus and the crab are in focus, while the background is slightly blurred,creating a depth of field effect. +An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt, he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film. +A lone figure stands on the deck of a spaceship, looking out at a nebula filled with vibrant colors. The shot tracks their gaze, capturing the breathtaking beauty of the cosmic landscape and the sense of infinite possibility. +A large orange octopus is seen resting on the bottom of the ocean floor, blending in with the sandy and rocky terrain. lts tentacles are spread out around its body, and its eyes are closed. The octopus is unaware of a king crab that is crawling towards it from behind a rock,its claws raised and ready to attack. The crab is brown and spiny,with long legs and antennae. The scene is captured from a wide angle,showing the vastness and depth of the ocean. The wateris clear and blue, with rays of sunlight filtering through. The shot is sharp and crisp, with a high dynamic range. The octopus and the crab are in focus, while the background is slightly blurred,creating a depth of field effect. a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water. A soaring drone footage captures the majestic beauty of a coastal cliff, its red and yellow stratified rock faces rich in color and against the vibrant turquoise of the sea. Seabirds can be seen taking flight around the cliff's precipices. As the drone slowly moves from different angles, the changing sunlight casts shifting shadows that highlight the rugged textures of the cliff and the surrounding calm sea. The water gently laps at the rock base and the greenery that clings to the top of the cliff, and the scene gives a sense of peaceful isolation at the fringes of the ocean. The video captures the essence of pristine natural beauty untouched by human structures. A vibrant scene of a snowy mountain landscape. The sky is filled with a multitude of colorful hot air balloons, each floating at different heights, creating a dynamic and lively atmosphere. The balloons are scattered across the sky, some closer to the viewer, others further away, adding depth to the scene. Below, the mountainous terrain is blanketed in a thick layer of snow, with a few patches of bare earth visible here and there. The snow-covered mountains provide a stark contrast to the colorful balloons, enhancing the visual appeal of the scene. @@ -25,4 +34,8 @@ The dynamic movement of tall, wispy grasses swaying in the wind. The sky above i A close-up of a magician’s crystal ball that reveals a futuristic cityscape within. Skyscrapers of light stretch towards the heavens, and flying cars zip through the air, casting neon reflections across the ball’s surface. 8K. A majestic horse gallops across a bridge made of rainbows, each hoof striking sparks of color that cascade into the sky, the clouds parting to reveal a sunlit path to a distant, magical realm. A close-up of a robot dog as it interacts with a group of real puppies in a park, its mechanical eyes blinking with curiosity and tail wagging energetically. High Resolution. +An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. +A close-up of a magician’s crystal ball that reveals a futuristic cityscape within. Skyscrapers of light stretch towards the heavens, and flying cars zip through the air, casting neon reflections across the ball’s surface. 8K. +A majestic horse gallops across a bridge made of rainbows, each hoof striking sparks of color that cascade into the sky, the clouds parting to reveal a sunlit path to a distant, magical realm. +A close-up of a robot dog as it interacts with a group of real puppies in a park, its mechanical eyes blinking with curiosity and tail wagging energetically. High Resolution. An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index c97ec3b03..69ccbe226 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -47,17 +47,17 @@ } def getdataset(args): - temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x + temporal_sample = TemporalRandomCrop(args.num_frames) # 16 x norm_fun = ae_norm[args.ae] if args.dataset == 't2v': resize_topcrop = [CenterCropResizeVideo((args.max_height, args.max_width), top_crop=True), ] - if args.multi_scale: - resize = [ - LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), - SpatialStrideCropVideo(args.stride) - ] - else: - resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] + # if args.multi_scale: + # resize = [ + # LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), + # SpatialStrideCropVideo(args.stride) + # ] + # else: + resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] transform = transforms.Compose([ ToTensorVideo(), *resize, @@ -70,6 +70,12 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) + transform_topcrop = transforms.Compose([ + ToTensorVideo(), + *resize_topcrop, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index a21207385..4664b5e6e 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -15,6 +15,7 @@ from einops import rearrange from decord import VideoReader from os.path import join as opj +from collections import Counter import torch import torchvision.transforms as transforms @@ -22,10 +23,11 @@ from torch.utils.data import DataLoader, Dataset, get_worker_info from tqdm import tqdm from PIL import Image +from accelerate.logging import get_logger from opensora.utils.dataset_utils import DecordInit from opensora.utils.utils import text_preprocessing - +logger = get_logger(__name__) def filter_json_by_existed_files(directory, data, postfix=".mp4"): # 构建搜索模式,以匹配指定后缀的文件 @@ -99,12 +101,30 @@ def get_item(self, work_info): dataset_prog = DataSetProg() +def find_closest_y(x, vae_stride_t=4, model_ds_t=4): + if x < 13: + return -1 + for y in range(x, 12, -1): + if (y - 1) % vae_stride_t == 0 and ((y - 1) // vae_stride_t + 1) % model_ds_t == 0: + # 4, 8: y in [29, 61, 93, 125, 157, 189, 221, 253, 285, 317, 349, 381, 413, 445, 477, 509, ...] + # 4, 4: y in [13, 29, 45, 61, 77, 93, 109, 125, 141, 157, 173, 189, 205, 221, 237, 253, 269, 285, 301, 317, 333, 349, 365, 381, 397, 413, 429, 445, 461, 477, 493, 509, ...] + return y + return -1 + +def filter_resolution(h, w, max_h_div_w_ratio=17/16, min_h_div_w_ratio=8 / 16): + if h / w <= max_h_div_w_ratio and h / w >= min_h_div_w_ratio: + return True + return False + + class T2V_dataset(Dataset): + def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): self.image_data = args.image_data self.video_data = args.video_data self.num_frames = args.num_frames + self.train_fps = args.train_fps self.use_image_num = args.use_image_num self.use_img_from_vid = args.use_img_from_vid self.transform = transform @@ -113,6 +133,8 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro self.tokenizer = tokenizer self.model_max_length = args.model_max_length self.cfg = args.cfg + self.speed_factor = args.speed_factor + assert self.speed_factor >= 1 self.v_decoder = DecordInit() self.support_Chinese = True @@ -126,8 +148,12 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro else: img_cap_list = [] else: - img_cap_list = self.get_img_cap_list() - vid_cap_list = [] + self.img_cap_list = self.get_img_cap_list() + self.vid_cap_list = [] + + if len(self.vid_cap_list) > 0: + self.vid_cap_list, self.sample_num_frames = self.define_frame_index(self.vid_cap_list) + self.lengths = self.sample_num_frames if self.num_frames != 1: n_elements = len(vid_cap_list) @@ -162,7 +188,7 @@ def __getitem__(self, idx): image_data = self.get_image(idx) # 1 frame video as image return dict(video_data=video_data, image_data=image_data) except Exception as e: - print(f'Error with {e}') + logger.info(f'Error with {e}') # 打印异常堆栈 if idx in dataset_prog.vid_cap_list: print(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") @@ -177,15 +203,14 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = dataset_prog.vid_cap_list[idx]['path'] - assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" - frame_idx = dataset_prog.vid_cap_list[idx]['frame_idx'] if hasattr(dataset_prog.vid_cap_list[idx], 'frame_idx') else None - video = self.decord_read(video_path, frame_idx) + video_path = self.vid_cap_list[idx]['path'] + assert os.path.exists(video_path), f"file {video_path} do not exist!" + # frame_indice = self.vid_cap_list[idx]['sample_frame_index'] + video = self.decord_read(video_path) h, w = video.shape[-2:] - assert h / w <= 16 / 16 and h / w >= 4 / 16, f'Only videos with a ratio (h/w) less than 16/16 and more than 4/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {video.shape}' + assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But video ({video_path}) found ratio is {round(h / w, 2)} with the shape of {video.shape}' t = video.shape[0] - video = video[:(t - 1) // 4 * 4 + 1] video = self.transform(video) # T C H W -> T C H W # video = torch.rand(221, 3, 480, 640) @@ -241,7 +266,8 @@ def get_image(self, idx): # image = [torch.rand(1, 3, 480, 640) for i in image_data] image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] - caps = [[random.choice(i['cap'])] for i in image_data] + caps = [i['cap'] if isinstance(i['cap'], list) else [i['cap']] for i in image_data] + caps = [[random.choice(i)] for i in caps] text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] input_ids, cond_mask = [], [] for t in text: @@ -261,21 +287,115 @@ def get_image(self, idx): cond_mask = torch.cat(cond_mask) # self.use_image_num, l return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) - def decord_read(self, path, frame_idx=None): + def define_frame_index(self, vid_cap_list): + + new_vid_cap_list = [] + sample_num_frames = [] + cnt_too_long = 0 + cnt_too_short = 0 + cnt_no_cap = 0 + cnt_no_resolution = 0 + cnt_resolution_mismatch = 0 + cnt_movie = 0 + for i in vid_cap_list: + duration = None if i.get('duration', None) is None else float(i.get('duration', None)) + fps = None if i.get('fps', None) is None else float(i.get('fps', None)) + resolution = i.get('resolution', None) + cap = i.get('cap', None) + if cap is None: + cnt_no_cap += 1 + continue + if resolution is None: + cnt_no_resolution += 1 + continue + else: + if resolution.get('height', None) is None or resolution.get('width', None) is None: + cnt_no_resolution += 1 + continue + if not filter_resolution(resolution['height'], resolution['width']): + cnt_resolution_mismatch += 1 + continue + if fps is not None and duration is not None: + # import ipdb;ipdb.set_trace() + i['num_frames'] = int(fps * duration) + # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. + if i['num_frames'] > 6.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) + cnt_too_long += 1 + continue + if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage + cnt_too_short += 1 + continue + + # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) + frame_interval = fps / self.train_fps + start_frame_idx = 8 if '/storage/dataset/movie' in i['path'] else 0 # special video + frame_indices = np.arange(start_frame_idx, i['num_frames'], frame_interval).astype(int) + frame_indices = frame_indices[frame_indices < i['num_frames']] + + # comment out it to enable dynamic frames training + if len(frame_indices) < self.num_frames: + cnt_too_short += 1 + continue + + # too long video will be temporal-crop randomly + if len(frame_indices) > self.num_frames: + begin_index, end_index = self.temporal_sample(len(frame_indices)) + frame_indices = frame_indices[begin_index: end_index] + # frame_indices = frame_indices[:self.num_frames] # head crop + # to find a suitable end_frame_idx, to ensure we do not need pad video + end_frame_idx = find_closest_y(len(frame_indices), vae_stride_t=4, model_ds_t=4) + if end_frame_idx == -1: # too short that can not be encoded exactly by videovae + cnt_too_short += 1 + continue + frame_indices = frame_indices[:end_frame_idx] + + if '/storage/dataset/movie' in i['path']: + cnt_movie += 1 + i['sample_frame_index'] = frame_indices.tolist() + new_vid_cap_list.append(i) + i['sample_num_frames'] = len(i['sample_frame_index']) # will use in dataloader(group sampler) + sample_num_frames.append(i['sample_num_frames']) + + logger.info(f'no_cap: {cnt_no_cap}, too_long: {cnt_too_long}, too_short: {cnt_too_short}, ' + f'no_resolution: {cnt_no_resolution}, resolution_mismatch: {cnt_resolution_mismatch}, ' + f'Counter(sample_num_frames): {Counter(sample_num_frames)}, movie: {cnt_movie}, ' + f'before filter: {len(vid_cap_list)}, after filter: {len(new_vid_cap_list)}') + # import ipdb;ipdb.set_trace() + return new_vid_cap_list, sample_num_frames + + def decord_read(self, path): decord_vr = self.v_decoder(path) total_frames = len(decord_vr) - # Sampling video frames - if frame_idx is None: - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - else: - start_frame_ind, end_frame_ind = frame_idx.split(':') - # start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) - start_frame_ind, end_frame_ind = int(start_frame_ind), int(start_frame_ind) + self.num_frames - # assert end_frame_ind - start_frame_ind >= self.num_frames - frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) - - video_data = decord_vr.get_batch(frame_indice).asnumpy() + fps = decord_vr.get_avg_fps() if decord_vr.get_avg_fps() > 0 else 30.0 + # import ipdb;ipdb.set_trace() + # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) + frame_interval = 1.0 if abs(fps - self.train_fps) < 1e-1 else fps / self.train_fps + start_frame_idx = 8 if '/storage/dataset/movie' in path else 0 # special video + frame_indices = np.arange(start_frame_idx, total_frames, frame_interval).astype(int) + frame_indices = frame_indices[frame_indices < total_frames] + #import ipdb;ipdb.set_trace() + # speed up + max_speed_factor = len(frame_indices) / self.num_frames + if self.speed_factor > 1 and max_speed_factor > 1 and not ('/storage/dataset/MagicTime_Data' in path): + speed_factor = random.uniform(1.0, min(self.speed_factor, max_speed_factor)) + target_frame_count = int(len(frame_indices) / speed_factor) + speed_frame_idx = np.linspace(0, len(frame_indices) - 1, target_frame_count, dtype=int) + frame_indices = frame_indices[speed_frame_idx] + + # too long video will be temporal-crop randomly + if len(frame_indices) > self.num_frames: + begin_index, end_index = self.temporal_sample(len(frame_indices)) + frame_indices = frame_indices[begin_index: end_index] + # frame_indices = frame_indices[:self.num_frames] # head crop + + # to find a suitable end_frame_idx, to ensure we do not need pad video + end_frame_idx = find_closest_y(len(frame_indices), vae_stride_t=4, model_ds_t=4) + if end_frame_idx == -1: # too short that can not be encoded exactly by videovae + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') + frame_indices = frame_indices[:end_frame_idx] + if len(frame_indices) < self.num_frames: + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') + video_data = decord_vr.get_batch(frame_indices).asnumpy() video_data = torch.from_numpy(video_data) video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) return video_data @@ -287,8 +407,8 @@ def read_jsons(self, data, postfix=".jpg"): for folder, anno in folder_anno: with open(anno, 'r') as f: sub_list = json.load(f) - print(f'Building {anno}...') - for i in tqdm(range(len(sub_list))): + logger.info(f'Building {anno}...') + for i in range(len(sub_list)): sub_list[i]['path'] = opj(folder, sub_list[i]['path']) if npu_config is not None: if "civitai" in anno or "ideogram" in anno or "human" in anno: diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 8698135ff..0c9862bf8 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -529,8 +529,8 @@ def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_th padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True) - # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) - # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py index 8fd772356..49271cc03 100644 --- a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -137,17 +137,17 @@ def __init__( # 2. Initialize the right blocks. # Initialize the output blocks and other projection blocks when necessary. self._init_patched_inputs(norm_type=norm_type) - # if self.use_stable_fp32: - # self._replace_fp32_layers() + if self.use_stable_fp32: + self._replace_fp32_layers() def _init_patched_inputs(self, norm_type): assert self.config.sample_size_t is not None, "OpenSoraT2V over patched input must provide sample_size_t" assert self.config.sample_size is not None, "OpenSoraT2V over patched input must provide sample_size" - self.num_frames = self.config.sample_size_t self.config.sample_size = to_2tuple(self.config.sample_size) - self.height = self.config.sample_size[0] - self.width = self.config.sample_size[1] + # self.num_frames = self.config.sample_size_t + # self.height = self.config.sample_size[0] + # self.width = self.config.sample_size[1] interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 interpolation_scale_t = ( self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t @@ -161,9 +161,9 @@ def _init_patched_inputs(self, norm_type): # down_factor = [int(i) for i in down_factor] # down_factor = down_factor if isinstance(self.config.down_factor, list) else [self.config.down_factor] * 5 # down_factor = [2] * len(self.config.depth) - + is_video_model = False if self.config.downsampler is not None and len(self.config.downsampler) == 9: - is_video_model = True + is_video_model = True # to init weight from image self.pos_embed = OverlapPatchEmbed3D( num_frames=self.config.sample_size_t, height=self.config.sample_size[0], @@ -190,16 +190,16 @@ def _init_patched_inputs(self, norm_type): interpolation_scale_t=interpolation_scale_t, use_abs_pos=not self.config.use_rope, ) - layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, - (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, - (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] + # layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, + # (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, + # (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] interpolation_scale_thw = (interpolation_scale_t, *interpolation_scale) - for i in range((len(self.config.depth)-1)//2): - t = layer_thw[i][0] // 2 if layer_thw[i][0] != 1 else 1 - h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention - w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 - layer_thw.append([t, h, w]) - self.layer_thw = layer_thw + # for i in range((len(self.config.depth)-1)//2): + # t = layer_thw[i][0] // 2 if layer_thw[i][0] != 1 else 1 + # h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention + # w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 + # layer_thw.append([t, h, w]) + # self.layer_thw = layer_thw self.encoder_level_1 = nn.ModuleList( [ BasicTransformerBlock( @@ -227,7 +227,8 @@ def _init_patched_inputs(self, norm_type): for _ in range(self.config.depth[0]) ] ) - self.down1_2 = Downsample3d(self.inner_dim) if is_video_model else Downsample2d(self.inner_dim) + # self.down1_2 = Downsample3d(self.inner_dim) if is_video_model else Downsample2d(self.inner_dim) + self.down1_2 = Downsample2d(self.inner_dim, is_video_model) self.encoder_level_2 = nn.ModuleList( [ @@ -256,7 +257,8 @@ def _init_patched_inputs(self, norm_type): for _ in range(self.config.depth[1]) ] ) - self.down2_3 = Downsample3d(self.inner_dim * 2) if is_video_model else Downsample2d(self.inner_dim * 2) + # self.down2_3 = Downsample3d(self.inner_dim * 2) if is_video_model else Downsample2d(self.inner_dim * 2) + self.down2_3 = Downsample2d(self.inner_dim * 2, is_video_model) self.latent = nn.ModuleList( [ @@ -286,7 +288,9 @@ def _init_patched_inputs(self, norm_type): ] ) - self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 + # self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 + self.up3_2 = Upsample2d(self.inner_dim * 4, is_video_model) ## From Level 4 to Level 3 + self.reduce_chan_level2_norm = nn.LayerNorm(int(self.inner_dim * 4), elementwise_affine=True, eps=1e-6) self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) self.decoder_level_2 = nn.ModuleList( @@ -317,7 +321,9 @@ def _init_patched_inputs(self, norm_type): ] ) - self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 + # self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 + self.up2_1 = Upsample2d(self.inner_dim * 2, is_video_model) ## From Level 4 to Level 3 + self.reduce_chan_level1_norm = nn.LayerNorm(int(self.inner_dim * 2), elementwise_affine=True, eps=1e-6) self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 1), bias=True) self.decoder_level_1 = nn.ModuleList( @@ -388,26 +394,26 @@ def _init_patched_inputs(self, norm_type): in_features=self.caption_channels, hidden_size=self.inner_dim * 4 ) - # def _replace_fp32_layers(self, module=None): - # if module is None: - # module = self - # for name, submodule in module.named_children(): - # if isinstance(submodule, nn.LayerNorm): - # print(f"Replacing LayerNorm in {name}") - # new_layer = FP32_Layernorm(submodule.normalized_shape, submodule.eps, submodule.elementwise_affine) - # if submodule.elementwise_affine: - # new_layer.weight.data.copy_(submodule.weight.data.float()) - # if submodule.bias is not None: - # new_layer.bias.data.copy_(submodule.bias.data.float()) - # setattr(module, name, new_layer) - # elif isinstance(submodule, nn.SiLU): - # print(f"Replacing SiLU in {name}") - # setattr(module, name, FP32_SiLU(submodule.inplace)) - # elif isinstance(submodule, nn.GELU): - # print(f"Replacing GELU in {name}") - # setattr(module, name, FP32_GELU(submodule.approximate)) - # else: - # self._replace_fp32_layers(submodule) + def _replace_fp32_layers(self, module=None): + if module is None: + module = self + for name, submodule in module.named_children(): + if isinstance(submodule, nn.LayerNorm): + # print(f"Replacing LayerNorm in {name}") + new_layer = FP32_Layernorm(submodule.normalized_shape, submodule.eps, submodule.elementwise_affine) + if submodule.elementwise_affine: + new_layer.weight.data.copy_(submodule.weight.data.float()) + if submodule.bias is not None: + new_layer.bias.data.copy_(submodule.bias.data.float()) + setattr(module, name, new_layer) + elif isinstance(submodule, nn.SiLU): + # print(f"Replacing SiLU in {name}") + setattr(module, name, FP32_SiLU(submodule.inplace)) + elif isinstance(submodule, nn.GELU): + # print(f"Replacing GELU in {name}") + setattr(module, name, FP32_GELU(submodule.approximate)) + else: + self._replace_fp32_layers(submodule) def _set_gradient_checkpointing(self, module, value=False): if hasattr(module, "gradient_checkpointing"): @@ -564,7 +570,8 @@ def custom_forward(*inputs): pad_h_1, pad_w_1 = height % 4, width % 4 inp_enc_level2, attention_bias, attention_mask = self.down1_2(out_enc_level1, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) - frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_1) // 2, (width + pad_w_1) // 2 + # frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_1) // 2, (width + pad_w_1) // 2 + height, width = (height + pad_h_1) // 2, (width + pad_w_1) // 2 # encoder_2 out_enc_level2 = inp_enc_level2 @@ -606,7 +613,8 @@ def custom_forward(*inputs): # import ipdb;ipdb.set_trace() inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) - frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + # frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + height, width = (height + pad_h_2) // 2, (width + pad_w_2) // 2 # latent latent = inp_enc_level3 @@ -648,7 +656,8 @@ def custom_forward(*inputs): # import ipdb;ipdb.set_trace() inp_dec_level2, attention_bias, attention_mask = self.up3_2(latent, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) - frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_2, width * 2 - pad_w_2 + # frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_2, width * 2 - pad_w_2 + height, width = height * 2 - pad_h_2, width * 2 - pad_w_2 inp_dec_level2 = torch.cat([inp_dec_level2, out_enc_level2], 2) inp_dec_level2 = self.reduce_chan_level2_norm(inp_dec_level2) inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) @@ -692,7 +701,8 @@ def custom_forward(*inputs): # import ipdb;ipdb.set_trace() inp_dec_level1, attention_bias, attention_mask = self.up2_1(out_dec_level2, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) - frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_1, width * 2 - pad_w_1 + # frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_1, width * 2 - pad_w_1 + height, width = height * 2 - pad_h_1, width * 2 - pad_w_1 inp_dec_level1 = torch.cat([inp_dec_level1, out_enc_level1], 2) inp_dec_level1 = self.reduce_chan_level1_norm(inp_dec_level1) inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) @@ -897,9 +907,9 @@ def UDiTUltraT2V_XL_111(**kwargs): 'attention_mode': 'xformers', 'use_rope': True, 'model_max_length': 300, - 'max_height': 240, - 'max_width': 320, - 'num_frames': 1, + 'max_height': 480, + 'max_width': 640, + 'num_frames': 61, 'use_image_num': 0, 'compress_kv_factor': 1, 'interpolation_scale_t': 1, @@ -922,7 +932,7 @@ def UDiTUltraT2V_XL_111(**kwargs): - model = UDiTUltraT2V_S_122(in_channels=c, + model = UDiTUltraT2V_L_122(in_channels=c, out_channels=c, sample_size=latent_size, sample_size_t=num_frames, @@ -938,7 +948,7 @@ def UDiTUltraT2V_XL_111(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k33_s22', + downsampler='k333_s222', interpolation_scale_t=args.interpolation_scale_t, interpolation_scale_h=args.interpolation_scale_h, interpolation_scale_w=args.interpolation_scale_w, @@ -946,6 +956,33 @@ def UDiTUltraT2V_XL_111(**kwargs): print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') + + + model_state_dict = model.state_dict() + pretrained = "/storage/ongoing/new/Open-Sora-Plan/bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-11500/model_ema/diffusion_pytorch_model.safetensors" + if 'safetensors' in pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] + import ipdb;ipdb.set_trace() + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + print(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + print(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # import sys;sys.exit() # try: # path = "bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index f5b2e3d10..6c393ea0b 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -208,6 +208,7 @@ def forward(self, latent, num_frames): video_latent, image_latent = None, None # b c 1 h w # assert latent.shape[-3] == 1 and num_frames == 1 + num_frames = latent.shape[-3] // self.patch_size_t height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size # latent = rearrange(latent, 'b c t h w -> (b t) c h w') @@ -424,6 +425,7 @@ def forward(self, x, attention_mask, t, h, w): self.t = t//self.down_factor[0] self.h = h//self.down_factor[1] self.w = w//self.down_factor[2] + # import ipdb;ipdb.set_trace() x = rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) (t h w) d', t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) @@ -436,6 +438,7 @@ def forward(self, x, attention_mask, t, h, w): return x, attention_mask def reverse(self, x, t, h, w): + # import ipdb;ipdb.set_trace() x = rearrange(x, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', t=t, h=h, w=w, dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) @@ -597,15 +600,15 @@ def __call__( key = self.rope(key, pos_thw) value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - - - if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + # 0, -10000 ->(bool) False, True ->(any) True ->(not) False + # 0, 0 ->(bool) False, False ->(any) False ->(not) True + if attention_mask is None or not torch.any(attention_mask.bool()): # 0 mean visible attention_mask = None # the output of sdp = (batch, num_heads, seq_len, head_dim) # TODO: add support for attn.scale when we move to Torch 2.1 if self.attention_mode == 'flash': - assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + assert attention_mask is None, 'flash-attn do not support attention_mask' with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): hidden_states = F.scaled_dot_product_attention( query, key, value, dropout_p=0.0, is_causal=False @@ -647,38 +650,37 @@ class PixelUnshuffle(nn.Module): def __init__(self, ratio, ratio_t=None): super().__init__() self.r = ratio - self.r_t = ratio_t if ratio_t else ratio + self.r_t = ratio_t if ratio_t else 1 def forward(self, x): - if self.r_t is not None and self.r_t != 1: + # if self.r_t is not None and self.r_t != 1: + if x.ndim == 5: b, c, t, h, w = x.shape + # import ipdb;ipdb.set_trace() assert t % self.r_t == 0 and h % self.r == 0 and w % self.r == 0 - if self.r > 1: - x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r_t, r2=self.r, r3=self.r) + x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r_t, r2=self.r, r3=self.r) else: b, c, h, w = x.shape assert h % self.r == 0 and w % self.r == 0 - if self.r > 1: - x = rearrange(x, 'b c (h r2) (w r3) -> b (c r2 r3) h w', r2=self.r, r3=self.r) + x = rearrange(x, 'b c (h r2) (w r3) -> b (c r2 r3) h w', r2=self.r, r3=self.r) return x class PixelShuffle(nn.Module): def __init__(self, ratio, ratio_t=None): super().__init__() self.r = ratio - self.r_t = ratio_t if ratio_t else ratio + self.r_t = ratio_t if ratio_t else 1 def forward(self, x): - if self.r_t is not None and self.r_t != 1: + if x.ndim == 5: b, c, t, h, w = x.shape + # import ipdb;ipdb.set_trace() assert c % (self.r_t*self.r*self.r) == 0 - if self.r > 1: - x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r_t, r2=self.r, r3=self.r) + x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r_t, r2=self.r, r3=self.r) else: b, c, h, w = x.shape assert c % (self.r*self.r) == 0 - if self.r > 1: - x = rearrange(x, 'b (c r2 r3) h w -> b c (h r2) (w r3)', r2=self.r, r3=self.r) + x = rearrange(x, 'b (c r2 r3) h w -> b c (h r2) (w r3)', r2=self.r, r3=self.r) return x class Downsample3d(nn.Module): @@ -734,13 +736,15 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): class Downsample2d(nn.Module): - def __init__(self, n_feat): + def __init__(self, n_feat, is_video_model=False): super(Downsample2d, self).__init__() - - self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), + self.is_video_model = is_video_model + Conv = nn.Conv3d if is_video_model else nn.Conv2d + self.body = nn.Sequential(Conv(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), PixelUnshuffle(2, 1)) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + # import ipdb;ipdb.set_trace() x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') # x = F.pad(x, (0, pad_w, 0, pad_h)) @@ -748,9 +752,13 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = F.pad(x, (0, pad_w, 0, pad_h)) else: x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) - # x = F.pad(x, (0, pad_w, 0, pad_h)) + if self.is_video_model: + x = rearrange(x, '(b t) d h w -> b d t h w', t=frames) x = self.body(x) - x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + if self.is_video_model: + x = rearrange(x, 'b d t h w -> b (t h w) d', t=frames) + else: + x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) @@ -761,15 +769,21 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): return x, attention_bias, attention_mask class Upsample2d(nn.Module): - def __init__(self, n_feat): + def __init__(self, n_feat, is_video_model=False): super(Upsample2d, self).__init__() - - self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), + self.is_video_model = is_video_model + Conv = nn.Conv3d if is_video_model else nn.Conv2d + self.body = nn.Sequential(Conv(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), PixelShuffle(2, 1)) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + if self.is_video_model: + x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) + else: + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) x = self.body(x) + if self.is_video_model: + x = rearrange(x, 'b d t h w -> (b t) d h w') x = x[:, :, :height*2-pad_h, :width*2-pad_w] x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) @@ -1068,7 +1082,10 @@ def forward( # 1. Prepare GLIGEN inputs cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} gligen_kwargs = cross_attention_kwargs.pop("gligen", None) - + + # 0, -10000 ->(bool) False, True ->(any) True ->(not) False + # 0, 0 ->(bool) False, False ->(any) False ->(not) True + # assert attention_mask.bool().float().sum() / attention_mask.bool().float().numel() <= 1/16, 'must ~all visible' attn_output = self.attn1( norm_hidden_states, encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 14f1cdb1d..1474319b8 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -11,8 +11,8 @@ def __init__(self, args, **kwargs): self.model_name = args.text_encoder_name if 'mt5' in self.model_name: from transformers import MT5EncoderModel - self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 9ec6cc3bc..5b88f5f26 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -787,9 +787,7 @@ def __call__( # compute previous image: x_t -> x_t-1 latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] - # npu_config.print_tensor_stats(latents, f"latents_i{i}_t{t}", rank=0) - assert not torch.isnan(latents).any().item(), "latents contains NaN values" - # print(f'latents_{i}_{t}', torch.max(latents), torch.min(latents), torch.mean(latents), torch.std(latents)) + # call the callback, if provided if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): progress_bar.update() diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 07c4c6cf3..4618bd27c 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -34,8 +34,6 @@ def main(args): # torch.manual_seed(args.seed) - if args.enable_stable_fp32: - replace_with_fp32_forwards() weight_dtype = torch.bfloat16 device = torch.device(args.device) @@ -55,12 +53,13 @@ def main(args): if args.model_3d: # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, # low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, + transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, ignore_mismatched_sizes=True, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - + # ckpt = torch.load('480p_73000_ema_k3_p1_repeat_wusun.pt') + # transformer_model.load_state_dict(ckpt) # text_encoder = UMT5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) text_encoder = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) @@ -141,7 +140,7 @@ def main(args): num_images_per_prompt=1, mask_feature=True, device=args.device, - max_sequence_length=200, + max_sequence_length=args.max_sequence_length, ).images try: if args.num_frames == 1: @@ -183,13 +182,14 @@ def main(args): parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") - parser.add_argument("--guidance_scale", type=float, default=7.5) + parser.add_argument("--guidance_scale", type=float, default=2.5) parser.add_argument("--sample_method", type=str, default="PNDM") + parser.add_argument("--max_sequence_length", type=int, default=300) parser.add_argument("--num_sampling_steps", type=int, default=50) parser.add_argument("--fps", type=int, default=24) parser.add_argument("--run_time", type=int, default=0) parser.add_argument("--text_prompt", nargs='+') - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--tile_overlap_factor', type=float, default=0.125) parser.add_argument('--enable_tiling', action='store_true') parser.add_argument('--model_3d', action='store_true') parser.add_argument('--enable_stable_fp32', action='store_true') diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index dce4c8cb6..1e9adf61e 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -42,10 +42,8 @@ from accelerate import Accelerator from accelerate.logging import get_logger from accelerate.utils import DistributedType, ProjectConfiguration, set_seed -from huggingface_hub import create_repo from packaging import version from tqdm.auto import tqdm -from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer import diffusers from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler @@ -58,9 +56,9 @@ from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper from opensora.models.diffusion.latte.modeling_latte import LatteT2V from opensora.models.text_encoder import get_text_enc, get_text_warpper -from opensora.utils.dataset_utils import Collate from opensora.models.ae import ae_stride_config, ae_channel_config from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.utils.dataset_utils import Collate, LengthGroupedSampler from opensora.sample.pipeline_opensora import OpenSoraPipeline @@ -71,16 +69,19 @@ @torch.inference_mode() def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False): + positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" + negative_prompt = """nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, + """ validation_prompt = [ - "a cat wearing sunglasses and working as a lifeguard at pool.", + "A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." ] - if 'mt5' in args.text_encoder_name: - validation_prompt_cn = [ - "一只戴着墨镜在泳池当救生员的猫咪。", - "这是一个宁静的水下场景,一只海龟游过珊瑚礁。海龟带着绿褐色的龟壳,优雅地游向画面右侧,成为视频的焦点。背景中的珊瑚礁生机盎然,为海龟的旅程提供了生动多彩的背景。几条小鱼在海龟周围穿梭,为画面增添了动感和活力。" - ] - validation_prompt += validation_prompt_cn + # if 'mt5' in args.text_encoder_name: + # validation_prompt_cn = [ + # # "一只戴着墨镜在泳池当救生员的猫咪。", + # "这是一个宁静的水下场景,一只海龟游过珊瑚礁。海龟带着绿褐色的龟壳,优雅地游向画面右侧,成为视频的焦点。背景中的珊瑚礁生机盎然,为海龟的旅程提供了生动多彩的背景。几条小鱼在海龟周围穿梭,为画面增添了动感和活力。" + # ] + # validation_prompt += validation_prompt_cn logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) scheduler = DPMSolverMultistepScheduler() @@ -92,7 +93,10 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh videos = [] for prompt in validation_prompt: logger.info('Processing the ({}) prompt'.format(prompt)) - video = opensora_pipeline(prompt, + video = opensora_pipeline( + # positive_prompt.format(prompt), + prompt, + negative_prompt=negative_prompt, num_frames=args.num_frames, # num_frames=1, height=args.max_height, @@ -102,7 +106,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh enable_temporal_attentions=True, num_images_per_prompt=1, mask_feature=True, - max_sequence_length=150, + max_sequence_length=args.model_max_length, ).images videos.append(video[0]) # import ipdb;ipdb.set_trace() @@ -119,7 +123,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh tracker.writer.add_images(f"{'ema_' if ema else ''}validation", np_images, global_step, dataformats="NHWC") else: np_videos = np.stack([np.asarray(vid) for vid in videos]) - tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=30) + tracker.writer.add_video(f"{'ema_' if ema else ''}validation", np_videos, global_step, fps=24) if tracker.name == "wandb": import wandb if videos.shape[1] == 1: @@ -135,7 +139,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh else: logs = { f"{'ema_' if ema else ''}validation": [ - wandb.Video(video, caption=f"{i}: {prompt}", fps=30) + wandb.Video(video, caption=f"{i}: {prompt}", fps=24) for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) ] } @@ -427,17 +431,26 @@ def load_model_hook(models, input_dir): use_bias_correction=args.prodigy_use_bias_correction, safeguard_warmup=args.prodigy_safeguard_warmup, ) - + logger.info(f"optimizer: {optimizer}") + # Setup data: train_dataset = getdataset(args) - train_dataloader = torch.utils.data.DataLoader( + sampler = LengthGroupedSampler( + args.train_batch_size, + world_size=accelerator.num_processes, + lengths=train_dataset.lengths, + group_frame=args.group_frame, + group_resolution=args.group_resolution, + ) if args.group_frame or args.group_resolution else None + train_dataloader = DataLoader( train_dataset, - shuffle=True, + shuffle=sampler is None, # pin_memory=True, collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, - prefetch_factor=8 + sampler=sampler if args.group_frame or args.group_resolution else None, + # prefetch_factor=4 ) # Scheduler and math around the number of training steps. @@ -455,6 +468,8 @@ def load_model_hook(models, input_dir): ) # Prepare everything with our `accelerator`. + # model.requires_grad_(False) + # model.pos_embed.requires_grad_(True) model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( model, optimizer, train_dataloader, lr_scheduler ) @@ -616,8 +631,19 @@ def run(model_input, model_kwargs, prof): else: raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + mask = model_kwargs.get('attention_mask', None) + b, c, _, _, _ = model_pred.shape + if mask is not None: + mask = mask.unsqueeze(1).repeat(1, c, 1, 1, 1).float() # b t h w -> b c t h w + mask = mask.reshape(b, -1) if args.snr_gamma is None: - loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + # model_pred: b c t h w, attention_mask: b t h w + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.reshape(b, -1) + if mask is not None: + loss = (loss * mask).sum() / mask.sum() # mean loss on unpad patches + else: + loss = loss.mean() else: # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. # Since we predict the noise instead of x_0, the original formulation is slightly changed. @@ -630,11 +656,13 @@ def run(model_input, model_kwargs, prof): mse_loss_weights = mse_loss_weights / snr elif noise_scheduler.config.prediction_type == "v_prediction": mse_loss_weights = mse_loss_weights / (snr + 1) - loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") - loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights - loss = loss.mean() - + loss = loss.reshape(b, -1) + mse_loss_weights = mse_loss_weights.reshape(b, 1) + if mask is not None: + loss = (loss * mask * mse_loss_weights).sum() / mask.sum() # mean loss on unpad patches + else: + loss = (loss * mse_loss_weights).mean() # Gather the losses across all processes for logging (if we use distributed training). avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() @@ -687,17 +715,24 @@ def run(model_input, model_kwargs, prof): def train_one_step(step_, data_item_, prof_=None): train_loss = 0.0 x, attn_mask, input_ids, cond_mask = data_item_ + # assert torch.all(attn_mask.bool()), 'must all visible' # Sample noise that we'll add to the latents - - if not args.multi_scale: - assert torch.all(attn_mask) + # import ipdb;ipdb.set_trace() + if args.group_frame or args.group_resolution: + if not torch.all(torch.any(attn_mask.flatten(-2), dim=-1)): + each_latent_frame = torch.any(attn_mask.flatten(-2), dim=-1).int().sum(-1).tolist() + # logger.info(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask ' + # f'each_latent_frame: {each_latent_frame}') + print(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask ' + f'each_latent_frame: {each_latent_frame}') assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+num_images H W, 16 + 4 attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W input_ids = input_ids.to(accelerator.device) # B 1+num_images L cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L - # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + # if accelerator.process_index == 0: + # logger.info(f'rank: {accelerator.process_index}, x: {x.shape}, attn_mask: {attn_mask.shape}') with torch.no_grad(): # import ipdb;ipdb.set_trace() @@ -722,6 +757,20 @@ def train_one_step(step_, data_item_, prof_=None): images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + # def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'output_video.mp4') -> None: + # from examples.rec_video import array_to_video + # x = x.detach().cpu() + # x = torch.clamp(x, -1, 1) + # x = (x + 1) / 2 + # x = x.permute(1, 2, 3, 0).numpy() + # x = (255*x).astype(np.uint8) + # array_to_video(x, fps=fps, output_file=output_file) + # return + # videos = ae.decode(x)[0] + # videos = videos.transpose(0, 1) + # custom_to_video(videos.to(torch.float32), fps=24, output_file='tmp.mp4') + # sys.exit() + if npu_config is not None and get_sequence_parallel_state(): x, cond, attn_mask, cond_mask, use_image_num = prepare_parallel_data(x, cond, attn_mask, cond_mask, args.use_image_num) @@ -795,16 +844,19 @@ def train_all_epoch(prof_=None): parser.add_argument("--video_data", type=str, required='') parser.add_argument("--image_data", type=str, default='') parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--train_fps", type=int, default=24) + parser.add_argument("--speed_factor", type=float, default=1.5) parser.add_argument("--num_frames", type=int, default=65) parser.add_argument("--max_height", type=int, default=320) parser.add_argument("--max_width", type=int, default=240) parser.add_argument("--use_img_from_vid", action="store_true") parser.add_argument("--use_image_num", type=int, default=0) parser.add_argument("--model_max_length", type=int, default=512) - parser.add_argument("--multi_scale", action="store_true") parser.add_argument('--cfg', type=float, default=0.1) parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + parser.add_argument("--group_frame", action="store_true") + parser.add_argument("--group_resolution", action="store_true") # text encoder & vae & diffusion model parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") @@ -831,11 +883,11 @@ def train_all_epoch(prof_=None): parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") parser.add_argument("--ema_start_step", type=int, default=0) - parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--noise_offset", type=float, default=0.02, help="The scale of noise offset.") parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") # validation & logs - parser.add_argument("--num_sampling_steps", type=int, default=20) + parser.add_argument("--num_sampling_steps", type=int, default=50) parser.add_argument('--guidance_scale', type=float, default=2.5) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 07a0a16f6..8a056f1bd 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -3,6 +3,12 @@ import decord from torch.nn import functional as F import torch +from typing import Optional +import torch.utils +import torch.utils.data +import torch +from torch.utils.data import Sampler +from typing import List IMG_EXTENSIONS = ['.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG'] @@ -44,6 +50,9 @@ def pad_to_multiple(number, ds_stride): class Collate: def __init__(self, args): + self.group_frame = args.group_frame + self.group_resolution = args.group_resolution + self.max_height = args.max_height self.max_width = args.max_width self.ae_stride = args.ae_stride @@ -88,6 +97,7 @@ def __call__(self, batch): # input_ids, cond_mask = input_ids_vid.squeeze(1), cond_mask_vid.squeeze(1) # b 1 l -> b l input_ids, cond_mask = input_ids_vid, cond_mask_vid # b 1 l elif self.num_frames > 1 and self.use_image_num != 0: + raise NotImplementedError pad_batch_tubes_vid, attention_mask_vid = self.process(batch_tubes_vid, t_ds_stride, ds_stride, self.max_thw, self.ae_stride_thw, self.patch_size_thw, extra_1=True) # attention_mask_vid: b t h w @@ -115,7 +125,12 @@ def __call__(self, batch): def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, patch_size_thw, extra_1): # pad to max multiple of ds_stride batch_input_size = [i.shape for i in batch_tubes] # [(c t h w), (c t h w)] - max_t, max_h, max_w = max_thw + if self.group_frame: + max_t = max([i[1] for i in batch_input_size]) + max_h = max([i[2] for i in batch_input_size]) + max_w = max([i[3] for i in batch_input_size]) + else: + max_t, max_h, max_w = max_thw pad_max_t, pad_max_h, pad_max_w = pad_to_multiple(max_t-1+self.ae_stride_t if extra_1 else max_t, t_ds_stride), \ pad_to_multiple(max_h, ds_stride), \ pad_to_multiple(max_w, ds_stride) @@ -152,7 +167,17 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p 0, max_latent_size[0] - i[0]), value=0) for i in valid_latent_size] attention_mask = torch.stack(attention_mask) # b t h w - + # if self.group_frame: + # if not torch.all(torch.any(attention_mask.flatten(-2), dim=-1)): + # print('batch_input_size', batch_input_size) + # print('max_t, max_h, max_w', max_t, max_h, max_w) + # print('pad_max_t, pad_max_h, pad_max_w', pad_max_t, pad_max_h, pad_max_w) + # print('each_pad_t_h_w', each_pad_t_h_w) + # print('max_tube_size', max_tube_size) + # print('max_latent_size', max_latent_size) + # print('valid_latent_size', valid_latent_size) + # import ipdb;ipdb.set_trace() + # assert torch.all(torch.any(attention_mask.flatten(-2), dim=-1)), "skip special batch" # max_tube_size = [pad_max_t, pad_max_h, pad_max_w] # max_latent_size = [((max_tube_size[0]-1) // ae_stride_thw[0] + 1) if extra_1 else (max_tube_size[0] // ae_stride_thw[0]), @@ -170,3 +195,108 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p # 0, max_patchify_latent_size[0] - i[0]), value=0) for i in valid_patchify_latent_size] # attention_mask = torch.stack(attention_mask) # b t h w return pad_batch_tubes, attention_mask + +def split_to_even_chunks(indices, lengths, num_chunks): + """ + Split a list of indices into `chunks` chunks of roughly equal lengths. + """ + + if len(indices) % num_chunks != 0: + return [indices[i::num_chunks] for i in range(num_chunks)] + + num_indices_per_chunk = len(indices) // num_chunks + + chunks = [[] for _ in range(num_chunks)] + chunks_lengths = [0 for _ in range(num_chunks)] + for index in indices: + shortest_chunk = chunks_lengths.index(min(chunks_lengths)) + chunks[shortest_chunk].append(index) + chunks_lengths[shortest_chunk] += lengths[index] + if len(chunks[shortest_chunk]) == num_indices_per_chunk: + chunks_lengths[shortest_chunk] = float("inf") + + return chunks + +def group_frame_fun(indices, lengths): + # sort by num_frames + indices.sort(key=lambda i: lengths[i], reverse=True) + return indices + +def group_resolution_fun(indices): + raise NotImplementedError + return indices + +def group_frame_and_resolution_fun(indices): + raise NotImplementedError + return indices + +def get_length_grouped_indices(lengths, batch_size, world_size, generator=None, group_frame=False, group_resolution=False, seed=42): + # We need to use torch for the random part as a distributed sampler will set the random seed for torch. + if generator is None: + generator = torch.Generator().manual_seed(seed) # every rank will generate a fixed order but random index + # print('lengths', lengths) + + indices = torch.randperm(len(lengths), generator=generator).tolist() + # print('indices', indices) + + if group_frame and not group_resolution: + indices = group_frame_fun(indices, lengths) + elif not group_frame and group_resolution: + indices = group_resolution_fun(indices) + elif group_frame and group_resolution: + indices = group_frame_and_resolution_fun(indices) + # print('sort indices', indices) + # print('sort lengths', [lengths[i] for i in indices]) + + megabatch_size = world_size * batch_size + megabatches = [indices[i: i + megabatch_size] for i in range(0, len(lengths), megabatch_size)] + # print('\nmegabatches', megabatches) + megabatches = [sorted(megabatch, key=lambda i: lengths[i], reverse=True) for megabatch in megabatches] + megabatches_len = [[lengths[i] for i in megabatch] for megabatch in megabatches] + # print('\nsorted megabatches', megabatches) + # print('\nsorted megabatches_len', megabatches_len) + megabatches = [split_to_even_chunks(megabatch, lengths, world_size) for megabatch in megabatches] + # print('\nsplit_to_even_chunks megabatches', megabatches) + # print('\nsplit_to_even_chunks len', [lengths[i] for megabatch in megabatches for batch in megabatch for i in batch]) + # return [i for megabatch in megabatches for batch in megabatch for i in batch] + + indices = torch.randperm(len(megabatches), generator=generator).tolist() + shuffled_megabatches = [megabatches[i] for i in indices] + # print('\nshuffled_megabatches', shuffled_megabatches) + # print('\nshuffled_megabatches len', [lengths[i] for megabatch in shuffled_megabatches for batch in megabatch for i in batch]) + + return [i for megabatch in shuffled_megabatches for batch in megabatch for i in batch] + + +class LengthGroupedSampler(Sampler): + r""" + Sampler that samples indices in a way that groups together features of the dataset of roughly the same length while + keeping a bit of randomness. + """ + + def __init__( + self, + batch_size: int, + world_size: int, + lengths: Optional[List[int]] = None, + group_frame=False, + group_resolution=False, + generator=None, + ): + if lengths is None: + raise ValueError("Lengths must be provided.") + + self.batch_size = batch_size + self.world_size = world_size + self.lengths = lengths + self.group_frame = group_frame + self.group_resolution = group_resolution + self.generator = generator + + def __len__(self): + return len(self.lengths) + + def __iter__(self): + indices = get_length_grouped_indices(self.lengths, self.batch_size, self.world_size, group_frame=self.group_frame, + group_resolution=self.group_resolution, generator=self.generator) + return iter(indices) \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index 2645efa58..9dd1bf5af 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,4 +1,20 @@ node030 slots=8 node033 slots=8 node034 slots=8 -node012 slots=8 \ No newline at end of file +node012 slots=8 +node004 slots=8 +node005 slots=8 +node006 slots=8 +node008 slots=8 +node009 slots=8 +node010 slots=8 +node011 slots=8 +node027 slots=8 +node028 slots=8 +node031 slots=8 +node032 slots=8 +node035 slots=8 +node056 slots=8 +node061 slots=8 +node063 slots=8 +node064 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index cea7297b1..9dc9dcb70 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -8,8 +8,8 @@ machine_rank: 0 main_process_ip: 100.64.24.30 main_process_port: 29502 main_training_function: main -num_machines: 4 -num_processes: 32 +num_machines: 20 +num_processes: 160 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index 9610fb56e..a2b25989b 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,5 +1,5 @@ -CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v.py \ - --model_path bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-27500/model_ema \ +CUDA_VISIBLE_DEVICES=1 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan/custom_image_weight \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ @@ -9,10 +9,11 @@ CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_t2v.py \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_image_27500_cfg2.5_step20_480p_pos_neg_fp32" \ + --save_img_path "sample_image_fp32_73000_cfg2.5_step20_480p_pos_neg_convert" \ --fps 24 \ --guidance_scale 2.5 \ --num_sampling_steps 20 \ + --enable_tiling \ + --max_sequence_length 512 \ --sample_method DPMSolverMultistep \ - --model_3d \ - --enable_stable_fp32 \ No newline at end of file + --model_3d \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index 172341f1f..5beb9a489 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -1,18 +1,20 @@ -CUDA_VISIBLE_DEVICES=2 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs16_4node_lr1e-4_snr5_ema_ps11_ds22/checkpoint-8000/model \ +CUDA_VISIBLE_DEVICES=1 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-7000/model_ema \ --version 65x512x512 \ - --num_frames 1 \ - --height 240 \ - --width 320 \ + --num_frames 61 \ + --height 480 \ + --width 640 \ --cache_dir "cache_dir" \ --text_encoder_name google/mt5-xxl \ + --text_encoder_name google/mt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_image256_256_ps22_ds11_ckpt8000" \ + --save_img_path "./sample_video_ema_61x480p_k333_s222_cfg4.5_step100" \ --fps 24 \ - --guidance_scale 2.0 \ - --num_sampling_steps 50 \ + --guidance_scale 4.5 \ + --num_sampling_steps 100 \ --enable_tiling \ - --sample_method PNDM \ + --max_sequence_length 512 \ + --sample_method DPMSolverMultistep \ --model_3d \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh b/scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh new file mode 100644 index 000000000..5e044789b --- /dev/null +++ b/scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh @@ -0,0 +1,63 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_1node_72500k_480p_125x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 125 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --adam_weight_decay 0.1 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k333_s122" \ + --resume_from_checkpoint="latest" \ + --enable_tracker \ + --enable_stable_fp32 \ + --group_frame \ + --pretrained 480p_73000_ema_k3_p1_wusun.pt \ + --output_dir="bs1_20node_73000k_480p_125x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh new file mode 100644 index 000000000..8cac39cd0 --- /dev/null +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh @@ -0,0 +1,62 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Tree +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 61 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=5e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" \ + --enable_tracker \ + --enable_stable_fp32 \ + --group_frame \ + --pretrained 480p_73000_ema_k3_p1_repeat_wusun.pt \ + --output_dir="bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh new file mode 100644 index 000000000..281b4032e --- /dev/null +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh @@ -0,0 +1,62 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Tree +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 61 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" \ + --enable_stable_fp32 \ + --group_frame \ + --speed_factor 1.2 \ + --pretrained "bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_aesmovie_sucai_speed1.4/checkpoint-7000/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh new file mode 100644 index 000000000..1959b64ca --- /dev/null +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh @@ -0,0 +1,62 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Tree +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_movie_time_sucai.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 61 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" \ + --enable_tracker \ + --enable_stable_fp32 \ + --group_frame \ + --pretrained "bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-11500/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_movie_time_sucai" \ No newline at end of file diff --git a/scripts/train_data/video_data.txt b/scripts/train_data/video_data.txt index 7f9c55548..b47ed9c60 100644 --- a/scripts/train_data/video_data.txt +++ b/scripts/train_data/video_data.txt @@ -1,3 +1,9 @@ -/dxyl_data02/datasets/pixabay_v2,/dxyl_data02/anno_jsons/video_pixabay_65f_601513.json -/dxyl_data02/datasets/pexels,/dxyl_data02/anno_jsons/video_pexel_65f_3832666.json -/dxyl_data02/datasets/mixkit,/dxyl_data02/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95350.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_clipchamp_final_452252.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_coverr_final_2997.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_istock_final_700532.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_kapwing_final_68429.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_mixkit_final_4490.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pexels_final_266826.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pixabay_v2_final_21463.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_storyblocks_final_1270905.json \ No newline at end of file diff --git a/scripts/train_data/video_data_aesmovie_sucai.txt b/scripts/train_data/video_data_aesmovie_sucai.txt new file mode 100644 index 000000000..ca9aadcd5 --- /dev/null +++ b/scripts/train_data/video_data_aesmovie_sucai.txt @@ -0,0 +1,15 @@ +/storage/dataset/movie,/storage/dataset/movie/bbc01_clips_aes_motion_final_250508.json +/storage/dataset/movie,/storage/dataset/movie/bbc02_clips_aes_motion_final_289778.json +/storage/dataset/movie,/storage/dataset/movie/bbc03_clips_aes_motion_final_519184.json +/storage/dataset/movie,/storage/dataset/movie/bbc04_clips_aes_motion_final_249497.json +/storage/dataset/movie,/storage/dataset/movie/bbc05_clips_aes_motion_final_416548.json +/storage/dataset/movie,/storage/dataset/movie/TV01_clips_aes_motion_final_217419.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95441.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_clipchamp_final_452264.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_coverr_final_3002.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_istock_final_815070.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_kapwing_final_68473.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_mixkit_final_4490.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pexels_final_267395.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pixabay_v2_final_21608.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_storyblocks_final_1270947.json \ No newline at end of file diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt index 796c7d1f3..0cc36a78c 100644 --- a/scripts/train_data/video_data_debug.txt +++ b/scripts/train_data/video_data_debug.txt @@ -1 +1 @@ -/storage/dataset/mixkit,/storage/anno_jsons/video_mixkit_65f_54735.json \ No newline at end of file +/storage/dataset/panda70m,/storage/anno_jsons/panda_100000.json \ No newline at end of file diff --git a/scripts/train_data/video_data_movie_time.txt b/scripts/train_data/video_data_movie_time.txt new file mode 100644 index 000000000..cef3aeb02 --- /dev/null +++ b/scripts/train_data/video_data_movie_time.txt @@ -0,0 +1,8 @@ +/storage/dataset/movie,/storage/dataset/movie/bbc01_clips_final_1610998.json +/storage/dataset/movie,/storage/dataset/movie/bbc02_clips_final_1441415.json +/storage/dataset/movie,/storage/dataset/movie/bbc03_clips_final_1905075.json +/storage/dataset/movie,/storage/dataset/movie/bbc04_clips_final_1718543.json +/storage/dataset/movie,/storage/dataset/movie/bbc05_clips_final_2684681.json +/storage/dataset/movie,/storage/dataset/movie/cartoon01_clips_final_658207.json +/storage/dataset/movie,/storage/dataset/movie/TV01_clips_final_478625.json +/storage/dataset/MagicTime_Data,/storage/dataset/MagicTime_Data/ChronoMagic-ProH-mini/Captions/ChronoMagic-ProH_clips_final_87437.json \ No newline at end of file diff --git a/scripts/train_data/video_data_movie_time_sucai.txt b/scripts/train_data/video_data_movie_time_sucai.txt new file mode 100644 index 000000000..071360aa0 --- /dev/null +++ b/scripts/train_data/video_data_movie_time_sucai.txt @@ -0,0 +1,17 @@ +/storage/dataset/movie,/storage/dataset/movie/bbc01_clips_final_1610998.json +/storage/dataset/movie,/storage/dataset/movie/bbc02_clips_final_1441415.json +/storage/dataset/movie,/storage/dataset/movie/bbc03_clips_final_1905075.json +/storage/dataset/movie,/storage/dataset/movie/bbc04_clips_final_1718543.json +/storage/dataset/movie,/storage/dataset/movie/bbc05_clips_final_2684681.json +/storage/dataset/movie,/storage/dataset/movie/cartoon01_clips_final_658207.json +/storage/dataset/movie,/storage/dataset/movie/TV01_clips_final_478625.json +/storage/dataset/MagicTime_Data,/storage/dataset/MagicTime_Data/ChronoMagic-ProH-mini/Captions/ChronoMagic-ProH_clips_final_87437.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95350.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_clipchamp_final_452252.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_coverr_final_2997.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_istock_final_700532.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_kapwing_final_68429.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_mixkit_final_4490.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pexels_final_266826.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pixabay_v2_final_21463.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_storyblocks_final_1270905.json \ No newline at end of file From bd2de8b25f057134f5a7ba34152387bf32a90623 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Fri, 5 Jul 2024 14:02:05 +0800 Subject: [PATCH 070/134] fix video training --- .gitignore | 3 +- examples/prompt_list_0.txt | 13 - opensora/dataset/__init__.py | 8 + opensora/dataset/t2v_datasets.py | 30 +- opensora/dataset/t2v_datasets_bak.py | 409 ++++++++++++++++++ opensora/models/text_encoder/__init__.py | 4 +- opensora/sample/pipeline_opensora.py | 8 +- opensora/sample/sample_t2v.py | 4 +- opensora/train/train_t2v_diffusers.py | 6 +- scripts/text_condition/gpu/sample_image.sh | 4 +- scripts/text_condition/gpu/sample_video.sh | 15 +- ...ra_61x480p_new_rope_fp32_aesmovie_sucai.sh | 8 +- ..._61x480p_new_rope_fp32_movie_time_sucai.sh | 62 --- 13 files changed, 459 insertions(+), 115 deletions(-) create mode 100644 opensora/dataset/t2v_datasets_bak.py delete mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh diff --git a/.gitignore b/.gitignore index c5ceeb720..d7d345d31 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ sample_image*cfg* *tmp* *pymp* check.py -bucket.py \ No newline at end of file +bucket.py +whileinf.py \ No newline at end of file diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index fcbd77448..292f23602 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -7,12 +7,6 @@ A single butterfly with wings that resemble stained glass flutters through a fie A solitary mermaid swims through an underwater cave filled with glowing crystals. The shot follows her graceful movements, capturing the play of light on her scales and the ethereal beauty of the cave. Close-up of a dragon's eye as it slowly opens, revealing a fiery iris that reflects the burning landscape around it, while smoke wisps off its scaly eyelid. A cat with the enigmatic smile of the Mona Lisa, lounging regally on a velvet cushion, her eyes following a fluttering butterfly that mirrors the mysterious allure of her expression. 4K. -A time-lapse of a storm forming over the ocean, dark clouds gathering and lightning flashing. The storm's energy creates spirals of light that dance across the sky. -A majestic eagle perches on a high cliff, its keen eyes scanning the valley below. With a powerful flap, it takes off, leaving a trail of sparkling feathers. -A single butterfly with wings that resemble stained glass flutters through a field of flowers. The shot captures the light as it passes through the delicate wings, creating a vibrant, colorful display. HD. -A solitary mermaid swims through an underwater cave filled with glowing crystals. The shot follows her graceful movements, capturing the play of light on her scales and the ethereal beauty of the cave. -Close-up of a dragon's eye as it slowly opens, revealing a fiery iris that reflects the burning landscape around it, while smoke wisps off its scaly eyelid. -A cat with the enigmatic smile of the Mona Lisa, lounging regally on a velvet cushion, her eyes following a fluttering butterfly that mirrors the mysterious allure of her expression. 4K. A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures. This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. @@ -22,9 +16,6 @@ a cat wearing sunglasses and working as a lifeguard at pool. An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt, he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film. A lone figure stands on the deck of a spaceship, looking out at a nebula filled with vibrant colors. The shot tracks their gaze, capturing the breathtaking beauty of the cosmic landscape and the sense of infinite possibility. A large orange octopus is seen resting on the bottom of the ocean floor, blending in with the sandy and rocky terrain. lts tentacles are spread out around its body, and its eyes are closed. The octopus is unaware of a king crab that is crawling towards it from behind a rock,its claws raised and ready to attack. The crab is brown and spiny,with long legs and antennae. The scene is captured from a wide angle,showing the vastness and depth of the ocean. The wateris clear and blue, with rays of sunlight filtering through. The shot is sharp and crisp, with a high dynamic range. The octopus and the crab are in focus, while the background is slightly blurred,creating a depth of field effect. -An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt, he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film. -A lone figure stands on the deck of a spaceship, looking out at a nebula filled with vibrant colors. The shot tracks their gaze, capturing the breathtaking beauty of the cosmic landscape and the sense of infinite possibility. -A large orange octopus is seen resting on the bottom of the ocean floor, blending in with the sandy and rocky terrain. lts tentacles are spread out around its body, and its eyes are closed. The octopus is unaware of a king crab that is crawling towards it from behind a rock,its claws raised and ready to attack. The crab is brown and spiny,with long legs and antennae. The scene is captured from a wide angle,showing the vastness and depth of the ocean. The wateris clear and blue, with rays of sunlight filtering through. The shot is sharp and crisp, with a high dynamic range. The octopus and the crab are in focus, while the background is slightly blurred,creating a depth of field effect. a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water. A soaring drone footage captures the majestic beauty of a coastal cliff, its red and yellow stratified rock faces rich in color and against the vibrant turquoise of the sea. Seabirds can be seen taking flight around the cliff's precipices. As the drone slowly moves from different angles, the changing sunlight casts shifting shadows that highlight the rugged textures of the cliff and the surrounding calm sea. The water gently laps at the rock base and the greenery that clings to the top of the cliff, and the scene gives a sense of peaceful isolation at the fringes of the ocean. The video captures the essence of pristine natural beauty untouched by human structures. A vibrant scene of a snowy mountain landscape. The sky is filled with a multitude of colorful hot air balloons, each floating at different heights, creating a dynamic and lively atmosphere. The balloons are scattered across the sky, some closer to the viewer, others further away, adding depth to the scene. Below, the mountainous terrain is blanketed in a thick layer of snow, with a few patches of bare earth visible here and there. The snow-covered mountains provide a stark contrast to the colorful balloons, enhancing the visual appeal of the scene. @@ -35,7 +26,3 @@ A close-up of a magician’s crystal ball that reveals a futuristic cityscape wi A majestic horse gallops across a bridge made of rainbows, each hoof striking sparks of color that cascade into the sky, the clouds parting to reveal a sunlit path to a distant, magical realm. A close-up of a robot dog as it interacts with a group of real puppies in a park, its mechanical eyes blinking with curiosity and tail wagging energetically. High Resolution. An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. -A close-up of a magician’s crystal ball that reveals a futuristic cityscape within. Skyscrapers of light stretch towards the heavens, and flying cars zip through the air, casting neon reflections across the ball’s surface. 8K. -A majestic horse gallops across a bridge made of rainbows, each hoof striking sparks of color that cascade into the sky, the clouds parting to reveal a sunlit path to a distant, magical realm. -A close-up of a robot dog as it interacts with a group of real puppies in a park, its mechanical eyes blinking with curiosity and tail wagging energetically. High Resolution. -An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 69ccbe226..d586c02ce 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -4,6 +4,14 @@ from torchvision import transforms from torchvision.transforms import Lambda +# try: +# import torch_npu +# from opensora.npu_config import npu_config +# from .t2v_datasets_npu import T2V_dataset +# except: +# torch_npu = None +# npu_config = None +# from .t2v_datasets import T2V_dataset from .t2v_datasets import T2V_dataset from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 4664b5e6e..99303fc34 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -119,7 +119,6 @@ def filter_resolution(h, w, max_h_div_w_ratio=17/16, min_h_div_w_ratio=8 / 16): class T2V_dataset(Dataset): - def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): self.image_data = args.image_data self.video_data = args.video_data @@ -148,11 +147,11 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro else: img_cap_list = [] else: - self.img_cap_list = self.get_img_cap_list() - self.vid_cap_list = [] + img_cap_list = self.get_img_cap_list() + vid_cap_list = [] - if len(self.vid_cap_list) > 0: - self.vid_cap_list, self.sample_num_frames = self.define_frame_index(self.vid_cap_list) + if len(vid_cap_list) > 0: + vid_cap_list, self.sample_num_frames = self.define_frame_index(vid_cap_list) self.lengths = self.sample_num_frames if self.num_frames != 1: @@ -191,7 +190,7 @@ def __getitem__(self, idx): logger.info(f'Error with {e}') # 打印异常堆栈 if idx in dataset_prog.vid_cap_list: - print(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") + logger.info(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") # traceback.print_exc() # traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) @@ -203,7 +202,7 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = self.vid_cap_list[idx]['path'] + video_path = dataset_prog.vid_cap_list[idx]['path'] assert os.path.exists(video_path), f"file {video_path} do not exist!" # frame_indice = self.vid_cap_list[idx]['sample_frame_index'] video = self.decord_read(video_path) @@ -322,18 +321,19 @@ def define_frame_index(self, vid_cap_list): if i['num_frames'] > 6.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) cnt_too_long += 1 continue - if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage - cnt_too_short += 1 - continue + # if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage + # cnt_too_short += 1 + # continue # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) frame_interval = fps / self.train_fps - start_frame_idx = 8 if '/storage/dataset/movie' in i['path'] else 0 # special video + start_frame_idx = 8 if 'movie/' in i['path'] else 0 # special video frame_indices = np.arange(start_frame_idx, i['num_frames'], frame_interval).astype(int) frame_indices = frame_indices[frame_indices < i['num_frames']] # comment out it to enable dynamic frames training - if len(frame_indices) < self.num_frames: + drop_short_video_prob = 1.0 + if len(frame_indices) < self.num_frames and random.uniform(0, 1) <= drop_short_video_prob: # to drop short videos cnt_too_short += 1 continue @@ -349,7 +349,7 @@ def define_frame_index(self, vid_cap_list): continue frame_indices = frame_indices[:end_frame_idx] - if '/storage/dataset/movie' in i['path']: + if 'movie/' in i['path']: cnt_movie += 1 i['sample_frame_index'] = frame_indices.tolist() new_vid_cap_list.append(i) @@ -370,13 +370,13 @@ def decord_read(self, path): # import ipdb;ipdb.set_trace() # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) frame_interval = 1.0 if abs(fps - self.train_fps) < 1e-1 else fps / self.train_fps - start_frame_idx = 8 if '/storage/dataset/movie' in path else 0 # special video + start_frame_idx = 8 if 'movie/' in path else 0 # special video frame_indices = np.arange(start_frame_idx, total_frames, frame_interval).astype(int) frame_indices = frame_indices[frame_indices < total_frames] #import ipdb;ipdb.set_trace() # speed up max_speed_factor = len(frame_indices) / self.num_frames - if self.speed_factor > 1 and max_speed_factor > 1 and not ('/storage/dataset/MagicTime_Data' in path): + if self.speed_factor > 1 and max_speed_factor > 1 and not ('MagicTime_Data/' in path): speed_factor = random.uniform(1.0, min(self.speed_factor, max_speed_factor)) target_frame_count = int(len(frame_indices) / speed_factor) speed_frame_idx = np.linspace(0, len(frame_indices) - 1, target_frame_count, dtype=int) diff --git a/opensora/dataset/t2v_datasets_bak.py b/opensora/dataset/t2v_datasets_bak.py new file mode 100644 index 000000000..530009dd7 --- /dev/null +++ b/opensora/dataset/t2v_datasets_bak.py @@ -0,0 +1,409 @@ +import time +import traceback + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None +import glob +import json +import os, io, csv, math, random +import numpy as np +import torchvision +from einops import rearrange +from decord import VideoReader +from os.path import join as opj +from collections import Counter + +import torch +import torchvision.transforms as transforms +from torch.utils.data.dataset import Dataset +from torch.utils.data import DataLoader, Dataset, get_worker_info +from tqdm import tqdm +from PIL import Image +from accelerate.logging import get_logger + +from opensora.utils.dataset_utils import DecordInit +from opensora.utils.utils import text_preprocessing +logger = get_logger(__name__) + +def filter_json_by_existed_files(directory, data, postfix=".mp4"): + # 构建搜索模式,以匹配指定后缀的文件 + pattern = os.path.join(directory, '**', f'*{postfix}') + mp4_files = glob.glob(pattern, recursive=True) # 使用glob查找所有匹配的文件 + + # 使用文件的绝对路径构建集合 + mp4_files_set = set(os.path.abspath(path) for path in mp4_files) + + # 过滤数据条目,只保留路径在mp4文件集合中的条目 + filtered_items = [item for item in data if item['path'] in mp4_files_set] + + return filtered_items + + +def random_video_noise(t, c, h, w): + vid = torch.rand(t, c, h, w) * 255.0 + vid = vid.to(torch.uint8) + return vid + +def find_closest_y(x, vae_stride_t=4, model_ds_t=4): + if x < 13: + return -1 + for y in range(x, 12, -1): + if (y - 1) % vae_stride_t == 0 and ((y - 1) // vae_stride_t + 1) % model_ds_t == 0: + # 4, 8: y in [29, 61, 93, 125, 157, 189, 221, 253, 285, 317, 349, 381, 413, 445, 477, 509, ...] + # 4, 4: y in [13, 29, 45, 61, 77, 93, 109, 125, 141, 157, 173, 189, 205, 221, 237, 253, 269, 285, 301, 317, 333, 349, 365, 381, 397, 413, 429, 445, 461, 477, 493, 509, ...] + return y + return -1 + +def filter_resolution(h, w, max_h_div_w_ratio=17/16, min_h_div_w_ratio=8 / 16): + if h / w <= max_h_div_w_ratio and h / w >= min_h_div_w_ratio: + return True + return False + + + +class T2V_dataset(Dataset): + def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): + self.image_data = args.image_data + self.video_data = args.video_data + self.num_frames = args.num_frames + self.train_fps = args.train_fps + self.use_image_num = args.use_image_num + self.use_img_from_vid = args.use_img_from_vid + self.transform = transform + self.transform_topcrop = transform_topcrop + self.temporal_sample = temporal_sample + self.tokenizer = tokenizer + self.model_max_length = args.model_max_length + self.cfg = args.cfg + self.speed_factor = args.speed_factor + assert self.speed_factor >= 1 + self.v_decoder = DecordInit() + + self.support_Chinese = True + if not ('mt5' in args.text_encoder_name): + self.support_Chinese = False + + if self.num_frames != 1: + self.vid_cap_list = self.get_vid_cap_list() + if self.use_image_num != 0 and not self.use_img_from_vid: + self.img_cap_list = self.get_img_cap_list() + else: + self.img_cap_list = [] + else: + self.img_cap_list = self.get_img_cap_list() + self.vid_cap_list = [] + + if len(self.vid_cap_list) > 0: + self.vid_cap_list, self.sample_num_frames = self.define_frame_index(self.vid_cap_list) + self.lengths = self.sample_num_frames + + if npu_config is not None: + self.n_used_elements = 0 + self.elements = list(range(self.__len__())) + self.worker_elements = None + + random.shuffle(self.elements) + logger.info(f"n_elements: {len(self.elements)}") + + logger.info(f"video length: {len(self.vid_cap_list)}") + logger.info(f"image length: {len(self.img_cap_list)}") + + def set_checkpoint(self, n_used_elements): + self.n_used_elements = n_used_elements + + def __len__(self): + if self.num_frames != 1: + return len(self.vid_cap_list) + else: + return len(self.img_cap_list) + + def __getitem__(self, idx): + if npu_config is not None: + worker_info = get_worker_info() + if worker_info is None: # single-process data loading, return a regular index + idx = self.elements[self.n_used_elements % len(self.elements)] + self.n_used_elements += 1 + else: # in a worker process + # split workload + if self.worker_elements is None: + per_worker = int(math.ceil(len(self.elements) / float(worker_info.num_workers))) + worker_id = worker_info.id + start = worker_id * per_worker + end = min(start + per_worker, len(self.elements)) + self.worker_elements = self.elements[start:end] + idx = self.worker_elements[self.n_used_elements % len(self.worker_elements)] + self.n_used_elements += 1 + try: + video_data, image_data = {}, {} + if self.num_frames != 1: + video_data = self.get_video(idx) + if self.use_image_num != 0: + if self.use_img_from_vid: + image_data = self.get_image_from_video(video_data) + else: + image_data = self.get_image(idx) + else: + image_data = self.get_image(idx) # 1 frame video as image + return dict(video_data=video_data, image_data=image_data) + except Exception as e: + logger.info(f'Error with {e}') + # 打印异常堆栈 + if idx in self.vid_cap_list: + logger.info(f"Caught an exception! {self.vid_cap_list[idx]}") + if idx in self.img_cap_list: + logger.info(f"Caught an exception! {self.img_cap_list[idx]}") + # traceback.print_exc() + # traceback.print_stack() + return self.__getitem__(random.randint(0, self.__len__() - 1)) + + def get_video(self, idx): + # npu_config.print_msg(f"current idx is {idx}") + # video = random.choice([random_video_noise(65, 3, 336, 448), random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 480)]) + # # print('random shape', video.shape) + # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) + # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) + + video_path = self.vid_cap_list[idx]['path'] + assert os.path.exists(video_path), f"file {video_path} do not exist!" + # frame_indice = self.vid_cap_list[idx]['sample_frame_index'] + video = self.decord_read(video_path) + + h, w = video.shape[-2:] + assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But video ({video_path}) found ratio is {round(h / w, 2)} with the shape of {video.shape}' + t = video.shape[0] + video = self.transform(video) # T C H W -> T C H W + + # video = torch.rand(221, 3, 480, 640) + + video = video.transpose(0, 1) # T C H W -> C T H W + text = self.vid_cap_list[idx]['cap'] + if not isinstance(text, list): + text = [text] + text = [random.choice(text)] + + text = text_preprocessing(text, support_Chinese=self.support_Chinese) if random.random() > self.cfg else "" + text_tokens_and_mask = self.tokenizer( + text, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids = text_tokens_and_mask['input_ids'] + cond_mask = text_tokens_and_mask['attention_mask'] + return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) + + def get_image_from_video(self, video_data): + select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) + assert self.num_frames >= self.use_image_num + image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] + input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l + cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) + + def get_image(self, idx): + idx = idx % len(self.img_cap_list) # out of range + image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] + + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + + # for i in image: + # assert not torch.any(torch.isnan(i)), 'before transform0' + image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + # for i in image: + # assert not torch.any(torch.isnan(i)), 'before transform1' + # for i in image: + # h, w = i.shape[-2:] + # assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only image with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {i.shape}' + + image = [self.transform_topcrop(i) if 'human_images' in j['path'] else self.transform(i) for i, j in zip(image, image_data)] # num_img [1 C H W] -> num_img [1 C H W] + + # for i in image: + # assert not torch.any(torch.isnan(i)), 'after transform' + # image = [torch.rand(1, 3, 480, 640) for i in image_data] + image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] + + caps = [i['cap'] if isinstance(i['cap'], list) else [i['cap']] for i in image_data] + caps = [[random.choice(i)] for i in caps] + text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] + input_ids, cond_mask = [], [] + for t in text: + t = t if random.random() > self.cfg else "" + text_tokens_and_mask = self.tokenizer( + t, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids.append(text_tokens_and_mask['input_ids']) + cond_mask.append(text_tokens_and_mask['attention_mask']) + input_ids = torch.cat(input_ids) # self.use_image_num, l + cond_mask = torch.cat(cond_mask) # self.use_image_num, l + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) + + def define_frame_index(self, vid_cap_list): + + new_vid_cap_list = [] + sample_num_frames = [] + cnt_too_long = 0 + cnt_too_short = 0 + cnt_no_cap = 0 + cnt_no_resolution = 0 + cnt_resolution_mismatch = 0 + cnt_movie = 0 + for i in vid_cap_list: + duration = None if i.get('duration', None) is None else float(i.get('duration', None)) + fps = None if i.get('fps', None) is None else float(i.get('fps', None)) + resolution = i.get('resolution', None) + cap = i.get('cap', None) + if cap is None: + cnt_no_cap += 1 + continue + if resolution is None: + cnt_no_resolution += 1 + continue + else: + if resolution.get('height', None) is None or resolution.get('width', None) is None: + cnt_no_resolution += 1 + continue + if not filter_resolution(resolution['height'], resolution['width']): + cnt_resolution_mismatch += 1 + continue + if fps is not None and duration is not None: + # import ipdb;ipdb.set_trace() + i['num_frames'] = int(fps * duration) + # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. + if i['num_frames'] > 6.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) + cnt_too_long += 1 + continue + if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage + cnt_too_short += 1 + continue + + # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) + frame_interval = fps / self.train_fps + start_frame_idx = 8 if '/storage/dataset/movie' in i['path'] else 0 # special video + frame_indices = np.arange(start_frame_idx, i['num_frames'], frame_interval).astype(int) + frame_indices = frame_indices[frame_indices < i['num_frames']] + + # comment out it to enable dynamic frames training + if len(frame_indices) < self.num_frames: + cnt_too_short += 1 + continue + + # too long video will be temporal-crop randomly + if len(frame_indices) > self.num_frames: + begin_index, end_index = self.temporal_sample(len(frame_indices)) + frame_indices = frame_indices[begin_index: end_index] + # frame_indices = frame_indices[:self.num_frames] # head crop + # to find a suitable end_frame_idx, to ensure we do not need pad video + end_frame_idx = find_closest_y(len(frame_indices), vae_stride_t=4, model_ds_t=4) + if end_frame_idx == -1: # too short that can not be encoded exactly by videovae + cnt_too_short += 1 + continue + frame_indices = frame_indices[:end_frame_idx] + + if '/storage/dataset/movie' in i['path']: + cnt_movie += 1 + i['sample_frame_index'] = frame_indices.tolist() + new_vid_cap_list.append(i) + i['sample_num_frames'] = len(i['sample_frame_index']) # will use in dataloader(group sampler) + sample_num_frames.append(i['sample_num_frames']) + + logger.info(f'no_cap: {cnt_no_cap}, too_long: {cnt_too_long}, too_short: {cnt_too_short}, ' + f'no_resolution: {cnt_no_resolution}, resolution_mismatch: {cnt_resolution_mismatch}, ' + f'Counter(sample_num_frames): {Counter(sample_num_frames)}, movie: {cnt_movie}, ' + f'before filter: {len(vid_cap_list)}, after filter: {len(new_vid_cap_list)}') + # import ipdb;ipdb.set_trace() + return new_vid_cap_list, sample_num_frames + + def decord_read(self, path): + decord_vr = self.v_decoder(path) + total_frames = len(decord_vr) + fps = decord_vr.get_avg_fps() if decord_vr.get_avg_fps() > 0 else 30.0 + # import ipdb;ipdb.set_trace() + # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) + frame_interval = 1.0 if abs(fps - self.train_fps) < 1e-1 else fps / self.train_fps + start_frame_idx = 8 if '/storage/dataset/movie' in path else 0 # special video + frame_indices = np.arange(start_frame_idx, total_frames, frame_interval).astype(int) + frame_indices = frame_indices[frame_indices < total_frames] + #import ipdb;ipdb.set_trace() + # speed up + max_speed_factor = len(frame_indices) / self.num_frames + if self.speed_factor > 1 and max_speed_factor > 1 and not ('/storage/dataset/MagicTime_Data' in path): + speed_factor = random.uniform(1.0, min(self.speed_factor, max_speed_factor)) + target_frame_count = int(len(frame_indices) / speed_factor) + speed_frame_idx = np.linspace(0, len(frame_indices) - 1, target_frame_count, dtype=int) + frame_indices = frame_indices[speed_frame_idx] + + # too long video will be temporal-crop randomly + if len(frame_indices) > self.num_frames: + begin_index, end_index = self.temporal_sample(len(frame_indices)) + frame_indices = frame_indices[begin_index: end_index] + # frame_indices = frame_indices[:self.num_frames] # head crop + + # to find a suitable end_frame_idx, to ensure we do not need pad video + end_frame_idx = find_closest_y(len(frame_indices), vae_stride_t=4, model_ds_t=4) + if end_frame_idx == -1: # too short that can not be encoded exactly by videovae + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') + frame_indices = frame_indices[:end_frame_idx] + if len(frame_indices) < self.num_frames: + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') + video_data = decord_vr.get_batch(frame_indices).asnumpy() + video_data = torch.from_numpy(video_data) + video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) + return video_data + + def read_jsons(self, data, postfix=".jpg"): + cap_lists = [] + with open(data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + for folder, anno in folder_anno: + with open(anno, 'r') as f: + sub_list = json.load(f) + logger.info(f'Building {anno}...') + for i in range(len(sub_list)): + sub_list[i]['path'] = opj(folder, sub_list[i]['path']) + if npu_config is not None: + sub_list = filter_json_by_existed_files(folder, sub_list, postfix=postfix) + cap_lists += sub_list + return cap_lists + + def get_img_cap_list(self): + use_image_num = self.use_image_num if self.use_image_num != 0 else 1 + if npu_config is None: + img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") + img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + else: + img_cap_lists = npu_config.try_load_pickle("img_cap_lists", + lambda: self.read_jsons(self.image_data, postfix=".jpg")) + img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] + return img_cap_lists[:-1] # drop last to avoid error length + + def get_vid_cap_list(self): + if npu_config is None: + vid_cap_lists = self.read_jsons(self.video_data, postfix=".mp4") + else: + vid_cap_lists = npu_config.try_load_pickle("vid_cap_lists5", + lambda: self.read_jsons(self.video_data, postfix=".mp4")) + # npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") + vid_cap_lists = vid_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] + vid_cap_lists_final = [] + for item in vid_cap_lists: + if os.path.exists(item['path']) and os.path.getsize(item['path']) > 10240: + vid_cap_lists_final.append(item) + vid_cap_lists = vid_cap_lists_final + npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") + + return vid_cap_lists diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 1474319b8..14f1cdb1d 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -11,8 +11,8 @@ def __init__(self, args, **kwargs): self.model_name = args.text_encoder_name if 'mt5' in self.model_name: from transformers import MT5EncoderModel - # self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 5b88f5f26..f7c67125c 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -814,12 +814,12 @@ def __call__( def decode_latents(self, latents): device = torch.cuda.current_device() - if npu_config is not None: - npu_config.print_tensor_stats(latents, f"before vae", rank=0) + # if npu_config is not None: + # npu_config.print_tensor_stats(latents, f"before vae", rank=0) self.vae = self.vae.to(device) video = self.vae.decode(latents.to(self.vae.vae.dtype).to(device)) - if npu_config is not None: - npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) + # if npu_config is not None: + # npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 return video diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 4618bd27c..03f50cdca 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -130,7 +130,9 @@ def main(args): """ video_grids = [] for idx, prompt in enumerate(text_prompt): - videos = pipeline(positive_prompt.format(prompt), + videos = pipeline( + # positive_prompt.format(prompt), + prompt, negative_prompt=negative_prompt, num_frames=args.num_frames, height=args.height, diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 1e9adf61e..aa949902b 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -468,8 +468,8 @@ def load_model_hook(models, input_dir): ) # Prepare everything with our `accelerator`. - # model.requires_grad_(False) - # model.pos_embed.requires_grad_(True) + model.requires_grad_(False) + model.pos_embed.requires_grad_(True) model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( model, optimizer, train_dataloader, lr_scheduler ) @@ -601,7 +601,7 @@ def run(model_input, model_kwargs, prof): bsz = model_input.shape[0] # Sample a random timestep for each image without bias. timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) - if get_sequence_parallel_state(): + if npu_config is not None and get_sequence_parallel_state(): broadcast(timesteps) # Add noise to the model input according to the noise magnitude at each timestep diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index a2b25989b..d52d72210 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,10 +1,10 @@ CUDA_VISIBLE_DEVICES=1 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/custom_image_weight \ + --model_path /storage/ongoing/new/checkpoints/bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ --width 640 \ - --cache_dir "cache_dir" \ + --cache_dir "../cache_dir" \ --text_encoder_name google/mt5-xxl \ --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index 5beb9a489..c303461ed 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -1,19 +1,18 @@ -CUDA_VISIBLE_DEVICES=1 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-7000/model_ema \ +CUDA_VISIBLE_DEVICES=3 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2/checkpoint-12500/model_ema \ --version 65x512x512 \ --num_frames 61 \ --height 480 \ --width 640 \ - --cache_dir "cache_dir" \ + --cache_dir "../cache_dir" \ --text_encoder_name google/mt5-xxl \ - --text_encoder_name google/mt5-xxl \ - --text_prompt examples/prompt_list_0.txt \ + --text_prompt examples/prompt_list_1.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_video_ema_61x480p_k333_s222_cfg4.5_step100" \ + --save_img_path "./sample_video_ema_61x480p_k333_s222_cfg2.5_step50" \ --fps 24 \ - --guidance_scale 4.5 \ - --num_sampling_steps 100 \ + --guidance_scale 2.5 \ + --num_sampling_steps 50 \ --enable_tiling \ --max_sequence_length 512 \ --sample_method DPMSolverMultistep \ diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh index 281b4032e..82d43e8b4 100644 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh @@ -13,11 +13,11 @@ export NCCL_ALGO=Tree export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/debug.yaml \ opensora/train/train_t2v_diffusers.py \ --model UDiTUltraT2V-L/122 \ --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ + --cache_dir "../cache_dir" \ --dataset t2v \ --video_data "scripts/train_data/video_data_aesmovie_sucai.txt" \ --image_data "scripts/train_data/image_data.txt" \ @@ -58,5 +58,5 @@ accelerate launch \ --enable_stable_fp32 \ --group_frame \ --speed_factor 1.2 \ - --pretrained "bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_aesmovie_sucai_speed1.4/checkpoint-7000/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2" \ No newline at end of file + --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2/checkpoint-12500/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="debug" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh deleted file mode 100644 index 1959b64ca..000000000 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh +++ /dev/null @@ -1,62 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Tree -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_movie_time_sucai.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 61 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k333_s222" \ - --resume_from_checkpoint="latest" \ - --enable_tracker \ - --enable_stable_fp32 \ - --group_frame \ - --pretrained "bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-11500/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_movie_time_sucai" \ No newline at end of file From 226c4217b386dfaaca85c8cbb01d8b767a056087 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Fri, 5 Jul 2024 14:15:03 +0800 Subject: [PATCH 071/134] fix grad --- opensora/dataset/t2v_datasets.py | 5 +++-- opensora/train/train_t2v_diffusers.py | 4 ++-- ...in_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 99303fc34..0f09ef46a 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -376,6 +376,7 @@ def decord_read(self, path): #import ipdb;ipdb.set_trace() # speed up max_speed_factor = len(frame_indices) / self.num_frames + speed_factor = 1.0 if self.speed_factor > 1 and max_speed_factor > 1 and not ('MagicTime_Data/' in path): speed_factor = random.uniform(1.0, min(self.speed_factor, max_speed_factor)) target_frame_count = int(len(frame_indices) / speed_factor) @@ -391,10 +392,10 @@ def decord_read(self, path): # to find a suitable end_frame_idx, to ensure we do not need pad video end_frame_idx = find_closest_y(len(frame_indices), vae_stride_t=4, model_ds_t=4) if end_frame_idx == -1: # too short that can not be encoded exactly by videovae - raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') + raise IndexError(f'video ({path}) has {total_frames} frames, speed_factor: {speed_factor}, but need to sample {len(frame_indices)} frames ({frame_indices})') frame_indices = frame_indices[:end_frame_idx] if len(frame_indices) < self.num_frames: - raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') + raise IndexError(f'video ({path}) has {total_frames} frames, speed_factor: {speed_factor}, but need to sample {len(frame_indices)} frames ({frame_indices})') video_data = decord_vr.get_batch(frame_indices).asnumpy() video_data = torch.from_numpy(video_data) video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index aa949902b..d2742f994 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -468,8 +468,8 @@ def load_model_hook(models, input_dir): ) # Prepare everything with our `accelerator`. - model.requires_grad_(False) - model.pos_embed.requires_grad_(True) + # model.requires_grad_(False) + # model.pos_embed.requires_grad_(True) model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( model, optimizer, train_dataloader, lr_scheduler ) diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh index 82d43e8b4..8730e6f22 100644 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh @@ -13,7 +13,7 @@ export NCCL_ALGO=Tree export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v_diffusers.py \ --model UDiTUltraT2V-L/122 \ --text_encoder_name google/mt5-xxl \ From b73ac090f6dea28a1d3f19e5cf70512c5aee75c4 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Fri, 5 Jul 2024 16:00:41 +0000 Subject: [PATCH 072/134] video ip for image-video joint training --- opensora/dataset/videoip_datasets.py | 2 +- opensora/models/diffusion/latte/videoip.py | 19 +++-- opensora/train/train_videoip.py | 83 +++++++++++-------- .../gpu/train_video_ip_video21d.sh | 14 ++-- scripts/train_data/image_data_debug.txt | 3 +- validation_dir/prompt.txt | 14 ++-- 6 files changed, 80 insertions(+), 55 deletions(-) diff --git a/opensora/dataset/videoip_datasets.py b/opensora/dataset/videoip_datasets.py index cf3030780..aabf14060 100644 --- a/opensora/dataset/videoip_datasets.py +++ b/opensora/dataset/videoip_datasets.py @@ -315,7 +315,7 @@ def get_vid_cap_list(self): # For testing, some data has not been utilized. if not os.path.exists(path) and not os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): print(path) - break + continue elif os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): path = path.replace('.mp4', '_resize1080p.mp4') new_vid_cap_list.append( diff --git a/opensora/models/diffusion/latte/videoip.py b/opensora/models/diffusion/latte/videoip.py index d158ed7b8..6e501637e 100644 --- a/opensora/models/diffusion/latte/videoip.py +++ b/opensora/models/diffusion/latte/videoip.py @@ -37,7 +37,8 @@ class VideoIPVideoEncoder(nn.Module): def __init__( self, image_encoder_type="clip", - inner_dim=1024, + num_attention_heads=16, + attention_head_dim=64, cross_attention_dim=1152, num_attention_layers=2, use_rope=False, @@ -49,6 +50,8 @@ def __init__( ): super().__init__() + inner_dim = num_attention_heads * attention_head_dim + self.image_encoder_type = image_encoder_type self.vae_scale_factor_t = vae_scale_factor_t @@ -109,8 +112,8 @@ def __init__( [ BasicTransformerBlock( dim=inner_dim, - num_attention_heads=16, - attention_head_dim=64, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, dropout=0.0, cross_attention_dim=None, double_self_attention=False, @@ -131,8 +134,8 @@ def __init__( [ BasicTransformerBlock( dim=inner_dim, - num_attention_heads=16, - attention_head_dim=64, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, dropout=0.0, cross_attention_dim=None, double_self_attention=False, @@ -217,7 +220,8 @@ class VideoIPAdapter(nn.Module): def __init__( self, image_encoder_type="clip", - inner_dim=1024, + num_attention_heads=16, + attention_head_dim=64, cross_attention_dim=1152, num_attention_layers=2, use_rope=False, @@ -231,7 +235,8 @@ def __init__( self.gradient_checkpointing = gradient_checkpointing self.encoder = VideoIPVideoEncoder( image_encoder_type=image_encoder_type, - inner_dim=inner_dim, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, cross_attention_dim=cross_attention_dim, num_attention_layers=num_attention_layers, use_rope=use_rope, diff --git a/opensora/train/train_videoip.py b/opensora/train/train_videoip.py index a71dee70b..0f230df88 100644 --- a/opensora/train/train_videoip.py +++ b/opensora/train/train_videoip.py @@ -83,6 +83,14 @@ check_min_version("0.24.0") logger = get_logger(__name__) +def get_clip_feature(clip_data, image_encoder, image_encoder_type='clip'): + if image_encoder_type == 'clip': + clip_feature = image_encoder(clip_data, output_hidden_states=True).hidden_states[-2] # B * (T+image_num) N D + elif image_encoder_type == 'dino': + clip_feature = image_encoder(clip_data).last_hidden_state # B * (T+image_num) N D + # clip_feature = image_encoder(clip_data, output_hidden_states=True).hidden_states[-2] # B * (T+image_num) N D + return clip_feature + class VIPNet(ModelMixin, ConfigMixin): @register_to_config @@ -91,7 +99,8 @@ def __init__( image_encoder_type='dino', cross_attention_dim=1152, num_tokens=272, - vip_inner_dim=1024, + vip_num_attention_heads=16, + vip_attention_head_dim=64, vip_num_attention_layers=2, attention_mode='xformers', gradient_checkpointing=False, @@ -115,7 +124,8 @@ def __init__( self.vip_adapter = VideoIPAdapter( image_encoder_type=image_encoder_type, cross_attention_dim=cross_attention_dim, - inner_dim=vip_inner_dim, + num_attention_heads=vip_num_attention_heads, + attention_head_dim=vip_attention_head_dim, num_attention_layers=vip_num_attention_layers, use_rope=use_rope, rope_scaling=rope_scaling, @@ -175,10 +185,10 @@ def get_image_embeds(self, images, image_processor, image_encoder, transform, de images = image_processor(images=images, return_tensors="pt").pixel_values # 1 C H W images = images.to(device=device, dtype=weight_dtype) negative_images = torch.zeros_like(images, device=device, dtype=weight_dtype) - - images = image_encoder(images).last_hidden_state # 1 N D + # NOTE using the second last layer of image encoder + images = get_clip_feature(images, image_encoder, image_encoder_type=self.image_encoder_type) # 1 N D images = images[:, 1:] # drop cls token - negative_images = image_encoder(negative_images).last_hidden_state + negative_images = get_clip_feature(negative_images, image_encoder, image_encoder_type=self.image_encoder_type) negative_images = negative_images[:, 1:] height = width = int(sqrt(images.shape[1])) @@ -211,10 +221,10 @@ def get_video_embeds(self, condition_images, num_frames, image_processor, image_ video = torch.zeros([num_frames, C, H, W], device=device, dtype=weight_dtype) video[condition_images_indices] = condition_images negative_video = torch.zeros_like(video, device=device, dtype=weight_dtype) - - video = image_encoder(video).last_hidden_state # F N D + # using the second last layer of image encoder + video = get_clip_feature(video, image_encoder, image_encoder_type=self.image_encoder_type) video = video[:, 1:] # drop cls token - negative_video = image_encoder(negative_video).last_hidden_state + negative_video = get_clip_feature(negative_video, image_encoder, image_encoder_type=self.image_encoder_type) negative_video = negative_video[:, 1:] height = width = int(sqrt(video.shape[1])) @@ -323,7 +333,7 @@ def log_validation( if not isinstance(images, list): images = [images] logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) - if args.num_frames == 1: + if args.num_frames != 1: if len(images) == 1 and images[0].split('/')[-1].split('_')[0] == 'img': vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( images=images, @@ -593,32 +603,35 @@ def main(args): image_encoder.requires_grad_(False) - if args.pretrained_vip_adapter_path is None: - attn_proc_type_dict = {} - for name, attn_processor in model.attn_processors.items(): - # replace all attn2.processor with VideoIPAttnProcessor - if name.endswith('.attn2.processor'): - attn_proc_type_dict[name] = 'VideoIPAttnProcessor' - else: - attn_proc_type_dict[name] = attn_processor.__class__.__name__ - - vip = VIPNet( - image_encoder_type=args.image_encoder_type, - cross_attention_dim=1152, - num_tokens=272, # NOTE should be modified - vip_inner_dim=1024, - vip_num_attention_layers=2, - attention_mode=args.attention_mode, - gradient_checkpointing=args.gradient_checkpointing, - vae_scale_factor_t=ae_stride_t, - video_length=latent_size_t, - attn_proc_type_dict=attn_proc_type_dict, - ) + attn_proc_type_dict = {} + for name, attn_processor in model.attn_processors.items(): + # replace all attn2.processor with VideoIPAttnProcessor + if name.endswith('.attn2.processor'): + attn_proc_type_dict[name] = 'VideoIPAttnProcessor' + else: + attn_proc_type_dict[name] = attn_processor.__class__.__name__ + + vip = VIPNet( + image_encoder_type=args.image_encoder_type, + cross_attention_dim=1152, + num_tokens=272, # NOTE should be modified + vip_num_attention_heads=args.vip_num_attention_heads, # for dinov2 + vip_attention_head_dim=64, + vip_num_attention_layers=2, + attention_mode=args.attention_mode, + gradient_checkpointing=args.gradient_checkpointing, + vae_scale_factor_t=ae_stride_t, + video_length=latent_size_t, + attn_proc_type_dict=attn_proc_type_dict, + ) + + if args.pretrained_vip_adapter_path is not None: + from safetensors.torch import load_file as safe_load + checkpoint = safe_load(os.path.join(args.pretrained_vip_adapter_path, "diffusion_pytorch_model.safetensors"), device="cpu") + vip.load_state_dict(checkpoint) - else: - vip = VIPNet.from_pretrained(args.pretrained_vip_adapter_path) - init_from_original_attn_processor = True if args.pretrained_vip_adapter_path is None else False + init_from_original_attn_processor = False if (args.pretrained_vip_adapter_path is not None or args.resume_from_checkpoint is not None) else True vip.set_vip_adapter(model, init_from_original_attn_processor=init_from_original_attn_processor) # for name, module in vip.named_modules(): @@ -1077,7 +1090,8 @@ def train_one_step(step_, data_item_, prof_=None): cond = cond.reshape(B, N, L, -1) # B 1+image_num L D clip_data = rearrange(clip_data, 'b t c h w -> (b t) c h w') # B T+image_num C H W -> B * (T+image_num) C H W - clip_feature = image_encoder(clip_data).last_hidden_state # B * (T+image_num) N D + # NOTE using the second last layer of CLIP or last layer of DINO as clip feature + clip_feature = get_clip_feature(clip_data, image_encoder, args.image_encoder_type) # B * (T+image_num) D clip_feature = clip_feature[:, 1:] # drop cls token clip_feature_height = clip_feature_width = int(sqrt(clip_feature.shape[1])) clip_feature = rearrange(clip_feature, '(b t) (h w) c -> b t h w c', b=B, h=clip_feature_height, w=clip_feature_width) # B T+image_num H W D @@ -1273,6 +1287,7 @@ def train_all_epoch(prof_=None): parser.add_argument("--pretrained_vip_adapter_path", type=str, default=None) parser.add_argument("--clear_video_ratio", type=float, default=0.0) parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--vip_num_attention_heads", type=int, default=8) args = parser.parse_args() diff --git a/scripts/text_condition/gpu/train_video_ip_video21d.sh b/scripts/text_condition/gpu/train_video_ip_video21d.sh index 14c3602e4..ca27d64ce 100644 --- a/scripts/text_condition/gpu/train_video_ip_video21d.sh +++ b/scripts/text_condition/gpu/train_video_ip_video21d.sh @@ -1,4 +1,4 @@ -PROJECT="videoip_65x512x512_1node_bs32_lr1e-4_snrgamma_noiseoffset_mj_dino_ema20000" +PROJECT="videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_dino_dim512" export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" # # export WANDB_MODE="offline" export ENTITY="yunyangge" @@ -36,7 +36,7 @@ accelerate launch \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=500000 \ - --learning_rate=1e-4 \ + --learning_rate=1e-5 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ @@ -50,7 +50,7 @@ accelerate launch \ --validation_dir "validation_dir" \ --guidance_scale 7.5 \ --num_sampling_steps 50 \ - --ema_start_step 20000 \ + --ema_start_step 0 \ --use_ema \ --cfg 0.05 \ --i2v_ratio 0.3 \ @@ -60,7 +60,9 @@ accelerate launch \ --seed 42 \ --snr_gamma 5.0 \ --noise_offset 0.02 \ - --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" # 基模型权重没有参与训练所以一定要加载 - # --pretrained_vip_adapter_path "" \ + --vip_num_attention_heads 8 \ + --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ + # --pretrained_vip_adapter_path "videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_last2layer/checkpoint-50000/model" \ # --resume_from_checkpoint "latest" \ - # --zero_terminal_snr \ \ No newline at end of file + # --zero_terminal_snr \ + # 基模型权重没有参与训练所以一定要加载 \ No newline at end of file diff --git a/scripts/train_data/image_data_debug.txt b/scripts/train_data/image_data_debug.txt index 788402176..0758b4fd6 100644 --- a/scripts/train_data/image_data_debug.txt +++ b/scripts/train_data/image_data_debug.txt @@ -1 +1,2 @@ -/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json \ No newline at end of file +/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json +/storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json \ No newline at end of file diff --git a/validation_dir/prompt.txt b/validation_dir/prompt.txt index 27ae678b4..bb0e595a4 100644 --- a/validation_dir/prompt.txt +++ b/validation_dir/prompt.txt @@ -1,9 +1,11 @@ A rocket ascends slowly into the sky. A coffee cup with "anytext" foam floating on it. -The landscape at sunset is profound and expansive. -This video showcases the movement of flower and bird painting, with a smooth transition from flowers to birds in the picture. -This video showcases the scenery of snow capped mountains. -This video showcases the night view of the city. +A beautiful girl is blinking her eyes. +A flower and bird painting. +The scenery of snow capped mountains. +The night view of the city. A rocket ascends slowly into the sky. -Along the coast, variously sized boats float on the lake, with the camera gradually zooming in. -The landscape at sunset is profound and expansive. \ No newline at end of file +Along the coast, variously sized boats float on the lake. +The landscape at sunset is profound and expansive. +A beautiful girl is blinking her eyes. +A beautiful girl is blinking her eyes. \ No newline at end of file From 964d8ba2fd5f5e289336874ecb87d3452eb112f9 Mon Sep 17 00:00:00 2001 From: liuzy233 Date: Sat, 6 Jul 2024 22:34:47 +0800 Subject: [PATCH 073/134] vip_lzy --- .../latte/transformer_qformer_lzy.py | 243 ++++ .../models/diffusion/latte/videoip_lzy.py | 488 +++++++ opensora/train/train_videoip_lzy.py | 1283 +++++++++++++++++ 3 files changed, 2014 insertions(+) create mode 100644 opensora/models/diffusion/latte/transformer_qformer_lzy.py create mode 100644 opensora/models/diffusion/latte/videoip_lzy.py create mode 100644 opensora/train/train_videoip_lzy.py diff --git a/opensora/models/diffusion/latte/transformer_qformer_lzy.py b/opensora/models/diffusion/latte/transformer_qformer_lzy.py new file mode 100644 index 000000000..150bec7bb --- /dev/null +++ b/opensora/models/diffusion/latte/transformer_qformer_lzy.py @@ -0,0 +1,243 @@ + +import torch +import torch.nn as nn + +# from diffusers.models.attention import FeedForward +from .modules import Attention, FeedForward + +class BasicQFormerBlock(nn.Module): + def __init__( + self, + dim, + num_attention_heads=16, + attention_head_dim=64, + dropout=0.0, + attention_bias=False, + upcast_attention=False, + attention_out_bias=True, + norm_elementwise_affine=True, + norm_eps=1e-5, + activation_fn="gelu", + attention_mode="xformers", + use_rope=False, + rope_scaling=None, + final_dropout=False, + ff_inner_dim=None, + ff_bias=False, + compress_kv_factor=None, + only_visual_attention=True, + ): + super().__init__() + + # self attn + self.norm1_latents = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + self.norm1_visual_context = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + + self.attn1 = Attention( + query_dim=dim, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + cross_attention_dim=dim, + upcast_attention=upcast_attention, + attention_mode=attention_mode, + use_rope=use_rope, + rope_scaling=rope_scaling, + out_bias=attention_out_bias, + compress_kv_factor=compress_kv_factor, + ) + + self.only_visual_attention = only_visual_attention + if not only_visual_attention: + # cross attn if cross_attention_dim is not None, otherwise self attn + self.norm2_latents = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + self.norm2_encoder_hidden_states = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + + self.attn2 = Attention( + query_dim=dim, + cross_attention_dim=dim, + heads=num_attention_heads, + dim_head=attention_head_dim, + dropout=dropout, + bias=attention_bias, + upcast_attention=upcast_attention, + attention_mode=attention_mode, + use_rope=False, + compress_kv_factor=None, + ) + else: + self.norm2_latents = None + self.norm2_encoder_hidden_states = None + self.attn2 = None + + self.norm3 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + self.ff = FeedForward( + dim, + dropout=dropout, + activation_fn=activation_fn, + final_dropout=final_dropout, + # inner_dim=ff_inner_dim, + # bias=ff_bias, + ) + + + def forward( + self, + latents, + visual_context, + attention_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + position_q=None, + position_k=None, + last_shape=None, + ): + ''' + params: + latents: queries of q former + shape (b, n1, c) + visual_context: visual features + shape (b, n2, c) + encoder_hidden_states: text features or others + shape (b, n3, c) + + return: + latents: updated queries + ''' + # self attn + norm_latents = self.norm1_latents(latents) + norm_visual_context = self.norm1_visual_context(visual_context) + kv_input = torch.cat([norm_visual_context, norm_latents], dim=-2) + + attn_output = self.attn1( + norm_latents, + encoder_hidden_states=kv_input, + attention_mask=attention_mask, + position_q=position_q, + position_k=position_k, + last_shape=last_shape, + ) + + latents = latents + attn_output + + if not self.only_visual_attention: + # cross attn + norm_latents = self.norm2_latents(latents) + if encoder_hidden_states is not None: + norm_encoder_hidden_states = self.norm2_encoder_hidden_states(encoder_hidden_states) + else: + norm_encoder_hidden_states = self.norm2_encoder_hidden_states(visual_context) + + kv_input = torch.cat([norm_encoder_hidden_states, norm_latents], dim=-2) + attn_output = self.attn2( + norm_latents, + encoder_hidden_states=kv_input, + attention_mask=encoder_attention_mask, + position_q=None, + position_k=None, + last_shape=None, + ) + + latents = latents + attn_output + + # feed forward + norm_latents = self.norm3(latents) + ff_output = self.ff(norm_latents) + + latents = latents + ff_output + + return latents + + + +class QFormer(nn.Module): + def __init__( + self, + dim=1024, + num_queries=1, + visual_context_dim=768, + out_dim=1024, + block_num=6, + num_attention_heads=16, + attention_head_dim=64, + dropout=0.0, + only_visual_attention=True, + encoder_hidden_states_dim=None, + use_rope=False, + rope_scaling=None, + attention_mode="xformers", + compress_kv_factor=1, + ): + super().__init__() + + self.only_visual_attention = only_visual_attention + + self.latents = nn.Parameter(torch.randn(1, num_queries, dim) / dim ** 0.5) + + self.proj_in_visual_context = nn.Linear(visual_context_dim, dim) + if not only_visual_attention: + self.proj_in_encoder_hidden_states = nn.Linear(encoder_hidden_states_dim, dim) + + self.proj_out = nn.Linear(dim, out_dim) + + self.layers = nn.ModuleList([]) + + for d in range(block_num): + self.layers.append( + BasicQFormerBlock( + dim, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, + dropout=dropout, + only_visual_attention=only_visual_attention, + attention_mode=attention_mode, + use_rope=use_rope, + rope_scaling=rope_scaling, + compress_kv_factor=(compress_kv_factor, compress_kv_factor) if d >= block_num // 2 and compress_kv_factor != 1 else None, # follow pixart-sigma, apply in second-half layers + ) + ) + + self.norm_out = nn.LayerNorm(out_dim) + + def forward(self, visual_context, encoder_hidden_states=None): + + b = visual_context.shape[0] + latents = self.latents.repeat(b, 1, 1) + + visual_context = self.proj_in_visual_context(visual_context) + if not self.only_visual_attention: + encoder_hidden_states = self.proj_in_encoder_hidden_states(encoder_hidden_states) + + for layer in self.layers: + latents = layer(latents, visual_context, encoder_hidden_states) + + latents = self.proj_out(latents) + latents = self.norm_out(latents) + + return latents + +if __name__ == "__main__": + qformer = QFormer( + dim=1024, + num_queries=256, + visual_context_dim=768, + encoder_hidden_states_dim=768, + out_dim=1024, + block_num=8, + num_attention_heads=64, + attention_head_dim=16, + dropout=0.0, + max_seq_length=257, + apply_pos_emb=False, + ) + + print(qformer) + + visual_context = torch.randn(2, 4096, 768) + encoder_hidden_states = torch.randn(2, 4096, 768) + + output = qformer(visual_context, encoder_hidden_states) + + num_params = sum(p.numel() for p in qformer.parameters() if p.requires_grad) + print("Number of trainable parameters: ", num_params / 1e6, "M") \ No newline at end of file diff --git a/opensora/models/diffusion/latte/videoip_lzy.py b/opensora/models/diffusion/latte/videoip_lzy.py new file mode 100644 index 000000000..a2bdf1fda --- /dev/null +++ b/opensora/models/diffusion/latte/videoip_lzy.py @@ -0,0 +1,488 @@ + +from dataclasses import dataclass + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils + +from opensora.models.ae.videobase.modules import attention +from opensora.models.diffusion.utils.pos_embed import RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D + +from opensora.models.diffusion.latte.modules import Attention + +from typing import Any, Dict, Optional, Tuple, Callable +from diffusers.utils import USE_PEFT_BACKEND, BaseOutput, deprecate, is_xformers_available +from diffusers.utils.torch_utils import maybe_allow_in_graph +from diffusers.models.lora import LoRACompatibleLinear + + + +from opensora.models.diffusion.latte.modules import BasicTransformerBlock +from opensora.models.diffusion.latte.transformer_qformer_lzy import QFormer +from einops import rearrange + +def zero_module(module): + for p in module.parameters(): + nn.init.zeros_(p) + return module + +@dataclass +class VideoIPOutput(BaseOutput): + hidden_states: torch.FloatTensor + vip_cond_mask: torch.FloatTensor + + +class VideoIPVideoQFormer(nn.Module): + + def __init__( + self, + image_encoder_type="clip", + inner_dim=1280, + cross_attention_dim=1152, + num_queries=16, + depth=4, + use_rope=False, + rope_scaling=None, + attention_mode='xformers', + vae_scale_factor_t=4, + video_length=17, + max_num_tokens=272, + compress_kv_factor=1, + ): + super().__init__() + + self.image_encoder_type = image_encoder_type + + self.vae_scale_factor_t = vae_scale_factor_t + self.video_length = video_length + + self.max_num_tokens = max_num_tokens + self.num_queries = num_queries + + if USE_PEFT_BACKEND: + linear_cls = nn.Linear + else: + linear_cls = LoRACompatibleLinear + + if image_encoder_type == "clip": # F * 16 * 16 + # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") + + self.conv_in = nn.ModuleList( + [ + nn.Conv3d(in_channels=1280, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 + nn.SiLU(), + nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)), # F // 2 * 16 * 16 -> F // 4 * 16 * 16 + ] + ) + elif image_encoder_type == "dino": # F * 16 * 16 + # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") + + self.conv_in = nn.ModuleList( + [ + nn.Conv3d(in_channels=1536, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 + nn.SiLU(), + nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)), # F // 2 * 16 * 16 -> F // 4 * 16 * 16 + ] + ) + # elif image_encoder_type == "vae": # F // 4 * 64 * 64 + # assert in_channels is not None, "Please specify latent dim for VAE" + + # self.conv_in = nn.ModuleList( + # [ + # nn.Conv2d(in_channels=in_channels, out_channels=256, kernel_size=3, stride=2, padding=1), + # nn.SiLU(), + # nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=2, padding=1), + # nn.SiLU(), + # nn.Conv2d(in_channels=512, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), + # ] + # ) # F // 4 * 64 * 64 -> F // 4 * 8 * 8 + + # self.conv_in_downsample_factor = (1, 8, 8) + else: + raise NotImplementedError + + self.qformer = QFormer( + dim=cross_attention_dim, + num_queries=self.num_queries, + visual_context_dim=inner_dim, + encoder_hidden_states_dim=inner_dim, + out_dim=cross_attention_dim, + block_num=depth, + num_attention_heads=64, + attention_head_dim=16, + dropout=0.0, + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + compress_kv_factor=compress_kv_factor, + ) + + def forward( + self, + hidden_states, + image_mode=False, + ): + # B C F H W + input_batch_size, input_frame = hidden_states.shape[0], hidden_states.shape[2] + + if image_mode: + hidden_states = hidden_states.repeat_interleave(self.vae_scale_factor_t, dim=2) + hidden_states = rearrange(hidden_states, 'b c (f i) h w -> (b f) c i h w', f=input_frame, i=self.vae_scale_factor_t) + else: + image_hidden_states = hidden_states[:, :, 0:1].repeat(1, 1, self.vae_scale_factor_t, 1, 1) + hidden_states = torch.cat([image_hidden_states, hidden_states[:, :, 1:]], dim=2) + + for layer in self.conv_in: + hidden_states = layer(hidden_states) + + # after conv_in + frame, height, width = hidden_states.shape[2], hidden_states.shape[3], hidden_states.shape[4] + # if training image, now batch = input_batch_size * frame; if not, batch remains the same + hidden_states = rearrange(hidden_states, "b c f h w -> b (f h w) c") + + hidden_states = self.qformer(hidden_states) + + # when using image mode, n=1*h*w; when using video mode, n=video_length*h*w + if image_mode: + hidden_states = rearrange(hidden_states, '(b f) n c -> b f n c', f=input_frame) + else: + hidden_states = hidden_states.unsqueeze(1) # B N C -> B 1 N C + + batch, num_seq, num_tokens, _ = hidden_states.shape + hidden_states = F.pad(hidden_states, (0, 0, 0, self.max_num_tokens - num_tokens), value=0.0) + vip_cond_mask = torch.ones([batch, num_seq, num_tokens], device=hidden_states.device, dtype=hidden_states.dtype) + vip_cond_mask = F.pad(vip_cond_mask, (0, self.max_num_tokens - num_tokens), value=0.0) + + # hidden_states: B 1 N D(video) B image_num N D(image), vip_cond_mask: B 1 N(video) B image_num N(image), N = max_num_tokens + return hidden_states, vip_cond_mask + +class VideoIPAdapter(nn.Module): + def __init__( + self, + image_encoder_type="clip", + inner_dim=1280, + cross_attention_dim=1152, + num_queries=16, + depth=4, + use_rope=False, + rope_scaling=None, + attention_mode='xformers', + gradient_checkpointing=False, + vae_scale_factor_t=4, + video_length=17, + ): + super().__init__() + self.gradient_checkpointing = gradient_checkpointing + self.encoder = VideoIPVideoQFormer( + image_encoder_type=image_encoder_type, + inner_dim=inner_dim, + cross_attention_dim=cross_attention_dim, + # num_attention_layers=num_attention_layers, + num_queries=num_queries, + depth=depth, + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + vae_scale_factor_t=vae_scale_factor_t, + video_length=video_length, + ) + + def forward( + self, + hidden_states, + use_image_num=0, + return_dict=True, + ): + + assert hidden_states.ndim == 5, "Input tensor must be 5D" + + batch, channels, frame, height, width = hidden_states.shape + frame = frame - use_image_num + + video_hidden_states = hidden_states[:, :, 0:frame] + + one_image_video = True if frame == 1 else False + if self.training and self.gradient_checkpointing: + video_hidden_states, video_cond_mask = torch.utils.checkpoint.checkpoint(self.encoder, video_hidden_states, one_image_video, use_reentrant=False,) + else: + video_hidden_states, video_cond_mask = self.encoder(video_hidden_states, image_mode=one_image_video) + + if use_image_num: + image_hidden_states = hidden_states[:, :, frame:] + if self.training and self.gradient_checkpointing: + image_hidden_states, image_cond_mask = torch.utils.checkpoint.checkpoint(self.encoder, image_hidden_states, True, use_reentrant=False,) + else: + image_hidden_states, image_cond_mask = self.encoder(image_hidden_states, image_mode=True) + hidden_states = torch.cat([video_hidden_states, image_hidden_states], dim=1) # B 1+image_num N D + vip_cond_mask = torch.cat([video_cond_mask, image_cond_mask], dim=1) # B 1+image_num D + else: + hidden_states = video_hidden_states # B 1 N D + vip_cond_mask = video_cond_mask # B 1 N + + + if not return_dict: + return (hidden_states, vip_cond_mask) + + return VideoIPOutput(hidden_states=hidden_states, vip_cond_mask=vip_cond_mask) + + +# IP Adapter +# we need use accelerate, so this processor should extend from nn.Module +class VideoIPAttnProcessor(nn.Module): + r""" + Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). + """ + + def __init__( + self, + dim=1152, + attention_mode='xformers', + use_rope=False, + rope_scaling=None, + compress_kv_factor=None, + + num_vip_tokens=272, + vip_scale=1.0, + dropout=0.0, + ): + super().__init__() + self.dim = dim + self.attention_mode = attention_mode + self.use_rope = use_rope + self.rope_scaling = rope_scaling + self.compress_kv_factor = compress_kv_factor + + if USE_PEFT_BACKEND: + linear_cls = nn.Linear + else: + linear_cls = LoRACompatibleLinear + + if self.use_rope: + self._init_rope() + + if not hasattr(F, "scaled_dot_product_attention"): + raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + self.to_k_vip = linear_cls(dim, dim, bias=False) + self.to_v_vip = linear_cls(dim, dim, bias=False) + + self.to_out_vip = nn.ModuleList( + [ + zero_module(linear_cls(dim, dim, bias=False)), + nn.Dropout(dropout), + ] + ) + + self.num_vip_tokens = num_vip_tokens + self.vip_scale = vip_scale + + def _init_rope(self): + if self.rope_scaling is None: + self.rope2d = RoPE2D() + self.rope1d = RoPE1D() + else: + scaling_type = self.rope_scaling["type"] + scaling_factor_2d = self.rope_scaling["factor_2d"] + scaling_factor_1d = self.rope_scaling["factor_1d"] + if scaling_type == "linear": + self.rope2d = LinearScalingRoPE2D(scaling_factor=scaling_factor_2d) + self.rope1d = LinearScalingRoPE1D(scaling_factor=scaling_factor_1d) + else: + raise ValueError(f"Unknown RoPE scaling type {scaling_type}") + + def forward( + self, + attn: Attention, + hidden_states: torch.FloatTensor, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + temb: Optional[torch.FloatTensor] = None, + scale: float = 1.0, + position_q: Optional[torch.LongTensor] = None, + position_k: Optional[torch.LongTensor] = None, + last_shape: Tuple[int] = None, + ) -> torch.FloatTensor: + + residual = hidden_states + + args = () if USE_PEFT_BACKEND else (scale,) + + if attn.spatial_norm is not None: + hidden_states = attn.spatial_norm(hidden_states, temb) + + input_ndim = hidden_states.ndim + + if input_ndim == 4: + batch_size, channel, height, width = hidden_states.shape + hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) + + + if self.compress_kv_factor is not None: + batch_size = hidden_states.shape[0] + if len(last_shape) == 2: + encoder_hidden_states = hidden_states.permute(0, 2, 1).reshape(batch_size, self.dim, *last_shape) + encoder_hidden_states = attn.sr(encoder_hidden_states).reshape(batch_size, self.dim, -1).permute(0, 2, 1) + elif len(last_shape) == 1: + encoder_hidden_states = hidden_states.permute(0, 2, 1) + if last_shape[0] % 2 == 1: + first_frame_pad = encoder_hidden_states[:, :, :1].repeat((1, 1, attn.kernel_size - 1)) + encoder_hidden_states = torch.concatenate((first_frame_pad, encoder_hidden_states), dim=2) + encoder_hidden_states = attn.sr(encoder_hidden_states).permute(0, 2, 1) + else: + raise NotImplementedError(f'NotImplementedError with last_shape {last_shape}') + + encoder_hidden_states = attn.norm(encoder_hidden_states) + + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + has_vip_tokens = encoder_hidden_states is not None + if has_vip_tokens: + end_pos = sequence_length - self.num_vip_tokens + + # attention_mask include encoder_hidden_states(text) and clip_feature(image or video) + # import ipdb;ipdb.set_trace() + if attention_mask is not None: + if has_vip_tokens: + attention_mask, vip_attention_mask = attention_mask[..., :end_pos], attention_mask[..., end_pos:] + vip_attention_mask = attn.prepare_attention_mask(vip_attention_mask, self.num_vip_tokens, batch_size) + vip_attention_mask = vip_attention_mask.view(batch_size, attn.heads, -1, vip_attention_mask.shape[-1]) + + attention_mask = attn.prepare_attention_mask(attention_mask, end_pos, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + + if attn.group_norm is not None: + hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) + + args = () if USE_PEFT_BACKEND else (scale,) + query = attn.to_q(hidden_states, *args) + + if encoder_hidden_states is None: + encoder_hidden_states = hidden_states + else: + # get encoder_hidden_states, vip_hidden_states + if has_vip_tokens: + encoder_hidden_states, vip_hidden_states = ( + encoder_hidden_states[:, :end_pos, :], + encoder_hidden_states[:, end_pos:, :], + ) + if attn.norm_cross: + # vip tokens is normed + encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + + + key = attn.to_k(encoder_hidden_states, *args) + value = attn.to_v(encoder_hidden_states, *args) + + vip_key = self.to_k_vip(vip_hidden_states, *args) + vip_value = self.to_v_vip(vip_hidden_states, *args) + + inner_dim = key.shape[-1] + head_dim = inner_dim // attn.heads + + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + vip_key = vip_key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + vip_value = vip_value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + if position_q.ndim == 3: + query = self.rope2d(query, position_q) + elif position_q.ndim == 2: + query = self.rope1d(query, position_q) + else: + raise NotImplementedError + if position_k.ndim == 3: + key = self.rope2d(key, position_k) + vip_key = self.rope2d(vip_key, position_k) + elif position_k.ndim == 2: + key = self.rope1d(key, position_k) + vip_key = self.rope1d(vip_key, position_k) + else: + raise NotImplementedError + + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + if self.attention_mode == 'flash': + assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, attn_mask=vip_attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, attn_mask=vip_attention_mask, dropout_p=0.0, is_causal=False + ) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + + # linear proj + hidden_states = attn.to_out[0](hidden_states, *args) + # dropout + hidden_states = attn.to_out[1](hidden_states) + + vip_hidden_states = vip_hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + vip_hidden_states = vip_hidden_states.to(query.dtype) + + # linear proj + vip_hidden_states = self.to_out_vip[0](vip_hidden_states, *args) + # dropout + vip_hidden_states = self.to_out_vip[1](vip_hidden_states) + + hidden_states = hidden_states + self.vip_scale * vip_hidden_states + + if input_ndim == 4: + hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) + + if attn.residual_connection: + hidden_states = hidden_states + residual + + hidden_states = hidden_states / attn.rescale_output_factor + + return hidden_states + +if __name__ == "__main__": + model = VideoIPVideoQFormer( + image_encoder_type="clip", + inner_dim=1024, + cross_attention_dim=1152, + num_attention_layers=2, + use_rope=False, + rope_scaling=None, + attention_mode='math', + vae_scale_factor_t=4, + video_length=17, + ) + + params = sum(p.numel() for p in model.parameters() if p.requires_grad) + print(f"Trainable parameters: {params / 1e6}M") + + video = torch.randn(2, 1280, 1, 16, 16) + + output = model(video) + # output = model(video, training_image=True) + print(output.vip_cond_mask) + diff --git a/opensora/train/train_videoip_lzy.py b/opensora/train/train_videoip_lzy.py new file mode 100644 index 000000000..746b72d1f --- /dev/null +++ b/opensora/train/train_videoip_lzy.py @@ -0,0 +1,1283 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +from email.mime import image +import logging +import math +from mimetypes import init +import os +from selectors import EpollSelector +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm +import itertools + +from opensora.models.ae.videobase.modules import attention + +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +import torch.nn as nn +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import DistributedType, ProjectConfiguration, set_seed +from huggingface_hub import create_repo +from packaging import version +from tqdm.auto import tqdm +from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer +from math import sqrt + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available +from diffusers.models.modeling_utils import ModelMixin +from diffusers.configuration_utils import ConfigMixin, register_to_config + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.utils.dataset_utils import VideoIP_Collate as Collate +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.sample.pipeline_opensora import OpenSoraPipeline + + +# for validation +from PIL import Image +from torchvision import transforms +from torchvision.transforms import Lambda +from transformers import CLIPVisionModelWithProjection, AutoModel, AutoImageProcessor, CLIPImageProcessor +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +from opensora.sample.pipeline_opensora import OpenSoraPipeline +from opensora.sample.pipeline_for_vip import hacked_pipeline_call_for_vip + +from opensora.models.diffusion.latte.videoip_lzy import VideoIPAdapter, VideoIPAttnProcessor +from opensora.models.diffusion.latte.modeling_for_vip import hacked_forward_for_vip, hook_forward_fn, hook_backward_fn +from opensora.models.diffusion.latte.modules import BasicTransformerBlock + +import glob +from torchvision.utils import save_image +import imageio + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + + +class VIPNet(ModelMixin, ConfigMixin): + @register_to_config + def __init__( + self, + image_encoder_type='dino', + cross_attention_dim=1152, + num_tokens=272, + vip_inner_dim=1024, + num_queries=16, + depth=4, + attention_mode='xformers', + gradient_checkpointing=False, + vae_scale_factor_t=4, + video_length=17, + use_rope=False, + rope_scaling=None, + attn_proc_type_dict={}, + ): + super().__init__() + + self.image_encoder_type = image_encoder_type + self.cross_attention_dim = cross_attention_dim + self.num_tokens = num_tokens + + self.attention_mode = attention_mode + self.use_rope = use_rope + self.rope_scaling = rope_scaling + + + self.vip_adapter = VideoIPAdapter( + image_encoder_type=image_encoder_type, + cross_attention_dim=cross_attention_dim, + inner_dim=vip_inner_dim, + num_queries=num_queries, + depth=depth, + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + gradient_checkpointing=gradient_checkpointing, + vae_scale_factor_t=vae_scale_factor_t, + video_length=video_length, + ) + + + self.attn_procs = {} + # because nn.ModuleDict will raise (KeyError: 'module name can\'t contain ".", got: transformer_blocks.0.attn2.processor'), so we trun to use nn.ModuleList + for name, attn_proc_type in attn_proc_type_dict.items(): + if attn_proc_type == "VideoIPAttnProcessor": + self.attn_procs[name] = VideoIPAttnProcessor( + dim=cross_attention_dim, + attention_mode=attention_mode, + use_rope=use_rope, + rope_scaling=rope_scaling, + num_vip_tokens=num_tokens, + ) + self.adapter_modules = torch.nn.ModuleList(self.attn_procs.values()) + + # if pretrained_vip_adapter_path is not None: + # self.load_vip_adapter(pretrained_vip_adapter_path) + + def set_vip_adapter(self, model, init_from_original_attn_processor): + # init adapter modules + model_sd = model.state_dict() + attn_procs = {} + print("set vip adapter...") + for name, attn_processor in model.attn_processors.items(): + if name.endswith(".attn2.processor"): + new_attn_processor = self.attn_procs[name] + if init_from_original_attn_processor: + print("init from original attn processor...") + layer_name = name.split(".processor")[0] + weights = { + "to_k_vip.weight": model_sd[layer_name + ".to_k.weight"], + "to_v_vip.weight": model_sd[layer_name + ".to_v.weight"], + } + new_attn_processor.load_state_dict(weights, strict=False) + attn_procs[name] = new_attn_processor + else: + attn_procs[name] = attn_processor + + model.set_attn_processor(attn_procs) + + @torch.no_grad() + def get_image_embeds(self, images, image_processor, image_encoder, transform, device, weight_dtype=torch.float32): + if not isinstance(images, list): + images = [images] + images = [Image.open(image).convert("RGB") for image in images] + images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in images] # 1 H W C + images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in images]) # resize, 1 C H W + + images = image_processor(images=images, return_tensors="pt").pixel_values # 1 C H W + images = images.to(device=device, dtype=weight_dtype) + negative_images = torch.zeros_like(images, device=device, dtype=weight_dtype) + + images = image_encoder(images).last_hidden_state # 1 N D + images = images[:, 1:] # drop cls token + negative_images = image_encoder(negative_images).last_hidden_state + negative_images = negative_images[:, 1:] + + height = width = int(sqrt(images.shape[1])) + images = rearrange(images, '1 (h w) c -> c 1 h w', h=height, w=width) + negative_images = rearrange(negative_images, '1 (h w) c -> c 1 h w', h=height, w=width) + images = images.unsqueeze(0) # 1 C 1 H W + negative_images = negative_images.unsqueeze(0) + + vip_out = self.vip_adapter(hidden_states=images, use_image_num=0) + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N + + negative_vip_out = self.vip_adapter(hidden_states=negative_images, use_image_num=0) + negative_vip_tokens, negative_vip_cond_mask = negative_vip_out.hidden_states, negative_vip_out.vip_cond_mask + + return vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask + + @torch.no_grad() + def get_video_embeds(self, condition_images, num_frames, image_processor, image_encoder, transform, device, weight_dtype=torch.float32): + if len(condition_images) == 1: + condition_images_indices = [0] + elif len(condition_images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in condition_images] + condition_images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in condition_images] # F [1 H W C] + condition_images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in condition_images]) # resize, [F C H W] + + condition_images = image_processor(images=condition_images, return_tensors="pt").pixel_values # F C H W + condition_images = condition_images.to(device=device, dtype=weight_dtype) + _, C, H, W = condition_images.shape + video = torch.zeros([num_frames, C, H, W], device=device, dtype=weight_dtype) + video[condition_images_indices] = condition_images + negative_video = torch.zeros_like(video, device=device, dtype=weight_dtype) + + video = image_encoder(video).last_hidden_state # F N D + video = video[:, 1:] # drop cls token + negative_video = image_encoder(negative_video).last_hidden_state + negative_video = negative_video[:, 1:] + + height = width = int(sqrt(video.shape[1])) + video = rearrange(video, 't (h w) c -> c t h w', h=height, w=width) + negative_video = rearrange(negative_video, 't (h w) c -> c t h w', h=height, w=width) + + video = video.unsqueeze(0) # 1 C F H W + negative_video = negative_video.unsqueeze(0) + + vip_out = self.vip_adapter(hidden_states=video, use_image_num=0) + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N + + negative_vip_out = self.vip_adapter(hidden_states=negative_video, use_image_num=0) + negative_vip_tokens, negative_vip_cond_mask = negative_vip_out.hidden_states, negative_vip_out.vip_cond_mask + + return vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask + + + def forward(self, model, latent_model_input, timestep, **model_kwargs): + enable_temporal_attentions = True if args.num_frames > 1 else False + + encoder_hidden_states = model_kwargs.pop('encoder_hidden_states', None) + encoder_attention_mask = model_kwargs.pop('encoder_attention_mask', None) + clip_feature = model_kwargs.pop('clip_feature', None) + use_image_num = model_kwargs.pop('use_image_num', 0) + assert encoder_hidden_states is not None and encoder_attention_mask is not None and clip_feature is not None, "VIPNet requires encoder_hidden_states, encoder_attention_mask and clip_feature" + hidden_states = rearrange(clip_feature, 'b t h w c -> b c t h w') + + vip_out = self.vip_adapter(hidden_states=hidden_states, use_image_num=use_image_num) # B D T+image_num H W -> B 1+image_num N D + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask + + model_pred = model( + hidden_states=latent_model_input, + timestep=timestep, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + vip_hidden_states=vip_tokens, + vip_attention_mask=vip_cond_mask, + use_image_num=use_image_num, + enable_temporal_attentions=enable_temporal_attentions, + **model_kwargs + )[0] + + return model_pred + + +@torch.inference_mode() +def log_validation( + args, + model, + vip, + vae, + text_encoder, + tokenizer, + image_processor, + image_encoder, + accelerator, + weight_dtype, + global_step, + ema=False +): + + + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + prompt_file = os.path.join(validation_dir, "prompt.txt") + # prompt_file = os.path.join(validation_dir, "test_prompt.txt") + + with open(prompt_file, 'r') as f: + validation_prompt = f.readlines() + + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, "ref_image", f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break + resize_transform = transforms.Compose( + [CenterCropResizeVideo((args.max_height, args.max_width))] + ) + logger.info(f"Running {'normal' if not ema else 'ema'} validation....\n") + + vip = accelerator.unwrap_model(vip) + + model.eval() + vip.eval() + + scheduler = PNDMScheduler() + pipeline = OpenSoraPipeline( + vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model + ).to(device=accelerator.device) + + + pipeline.__call__ = hacked_pipeline_call_for_vip.__get__(pipeline, OpenSoraPipeline) + + # Save the generated videos + save_dir = os.path.join(args.output_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") + os.makedirs(save_dir, exist_ok=True) + + videos = [] + gen_img = False + for idx, (prompt, images) in enumerate(zip(validation_prompt, validation_images_list)): + torch.cuda.empty_cache() + if not isinstance(images, list): + images = [images] + logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) + if args.num_frames != 1: + if len(images) == 1: + # if len(images) == 1 and images[0].split('/')[-1].split('_')[0] == 'img': + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( + images=images, + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + gen_img = True + else: + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_video_embeds( + condition_images=images, + num_frames=args.num_frames, + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + else: + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( + images=images[0], # only using first image + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + gen_img = True + + video = pipeline.__call__( + prompt=prompt, + negative_prompt="", + vip_tokens=vip_tokens, + vip_attention_mask=vip_cond_mask, + negative_vip_tokens=negative_vip_tokens, + negative_vip_attention_mask=negative_vip_cond_mask, + num_frames=(1 if gen_img else args.num_frames), + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=accelerator.device, + max_squence_length=args.model_max_length, + ).images + videos.append(video[0]) + gen_img = False + + if video.shape[0] == 1: # image + ext = 'png' + Image.fromarray(video[0][0].cpu().numpy()).save(os.path.join(save_dir, f'{idx}.{ext}')) + else: # video + ext = 'mp4' + imageio.mimwrite( + os.path.join(save_dir, f'{idx}.{ext}'), video, fps=24, quality=6) # highest quality is 10, lowest is 0 + + # import ipdb;ipdb.set_trace() + + # for wandb + resize_transform = transforms.Compose( + [CenterCropResizeVideo((args.max_height // 4, args.max_width // 4))] + ) + + videos = [resize_transform(video.permute(0, 3, 1, 2)) for video in videos] + + + for tracker in accelerator.trackers: + if tracker.name == "wandb": + import wandb + logs = {} + logs[f"{'ema_' if ema else ''}validation_videos"] = [] + logs[f"{'ema_' if ema else ''}validation_images"] = [] + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)): + if video.shape[0] == 1: # image + logs[f"{'ema_' if ema else ''}validation_images"].append(wandb.Image(video[0], caption=f"{i}: {prompt}")) + else: # video + logs[f"{'ema_' if ema else ''}validation_videos"].append(wandb.Video(video, caption=f"{i}: {prompt}", fps=24)) + + tracker.log(logs, step=global_step) + + print("delete validation pipeline...") + del pipeline + gc.collect() + torch.cuda.empty_cache() + vip.train() + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss + + +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + + logging_dir = Path(args.output_dir, args.logging_dir) + + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if accelerator.is_main_process: + print("--------------------------training args--------------------------") + print(args) + print("-----------------------------------------------------------------") + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + kwargs = {} + ae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir, **kwargs).eval() + # ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = args.num_frames // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + # when training video ip adapter, we use t2v model + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae] * 2, # 因为要加载预训练权重,所以这里out_channels仍然设置为2倍 + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + downsampler=args.downsampler, + compress_kv_factor=args.compress_kv_factor, + use_rope=args.use_rope, + model_max_length=args.model_max_length, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # NOTE replace forward for VIP + model.forward = hacked_forward_for_vip.__get__(model, Diffusion_models[args.model]) + + # # use pretrained model? + # NOTE when using inpaint model + # if args.pretrained: + # model.custom_load_state_dict(args.pretrained) + + if args.pretrained: + model_state_dict = model.state_dict() + if 'safetensors' in args.pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(args.pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(args.pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + logger.info(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # Freeze main model + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + model.requires_grad_(False) + + # load image encoder + if args.image_encoder_type == 'clip': + # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") + image_encoder = CLIPVisionModelWithProjection.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + elif args.image_encoder_type == 'dino': + # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") + image_encoder = AutoModel.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + else: + raise NotImplementedError + + image_encoder.requires_grad_(False) + + if args.pretrained_vip_adapter_path is None: + attn_proc_type_dict = {} + for name, attn_processor in model.attn_processors.items(): + # replace all attn2.processor with VideoIPAttnProcessor + if name.endswith('.attn2.processor'): + attn_proc_type_dict[name] = 'VideoIPAttnProcessor' + else: + attn_proc_type_dict[name] = attn_processor.__class__.__name__ + + vip = VIPNet( + image_encoder_type=args.image_encoder_type, + cross_attention_dim=1152, + num_tokens=272, # NOTE should be modified + vip_inner_dim=1280, + num_queries=16, + depth=4, + attention_mode=args.attention_mode, + gradient_checkpointing=args.gradient_checkpointing, + vae_scale_factor_t=ae_stride_t, + video_length=latent_size_t, + attn_proc_type_dict=attn_proc_type_dict, + ) + + else: + vip = VIPNet.from_pretrained(args.pretrained_vip_adapter_path) + + vip.train() + + init_from_original_attn_processor = True if args.pretrained_vip_adapter_path is None else False + vip.set_vip_adapter(model, init_from_original_attn_processor=init_from_original_attn_processor) + + # for name, module in vip.named_modules(): + # # # if isinstance(module, VideoIPAttnProcessor): + # # # module.register_full_backward_hook(hook_backward_fn) + # if isinstance(module, nn.Conv3d): + # module.register_backward_hook(hook_backward_fn) + + noise_scheduler = DDPMScheduler(rescale_betas_zero_snr=args.zero_terminal_snr) + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + ae.vae.to(accelerator.device, dtype=torch.float32) + # ae.vae.to(accelerator.device, dtype=weight_dtype) + text_enc.to(accelerator.device, dtype=weight_dtype) + model.to(accelerator.device, dtype=weight_dtype) + + image_encoder.to(accelerator.device, dtype=weight_dtype) + + # Create EMA for the unet. + if args.use_ema: + ema_vip = deepcopy(vip) + ema_vip = EMAModel(vip.parameters(), update_after_step=args.ema_start_step, + model_cls=VIPNet, model_config=ema_vip.config) + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + ema_vip.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), VIPNet) + ema_vip.load_state_dict(load_model.state_dict()) + ema_vip.to(accelerator.device) + del load_model + # if os.path.exists(os.path.join(input_dir, "model_ema")): + # print("loading ema model...") + # load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), VIPNet) + # ema_vip.load_state_dict(load_model.state_dict()) + # ema_vip.to(accelerator.device) + # del load_model + + for i in range(len(models)): + # pop models so that they are not loaded again + model = models.pop() + + # load diffusers style into model + load_model = VIPNet.from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + params_to_optimize = vip.parameters() + # Optimizer creation + if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): + logger.warning( + f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]." + "Defaulting to adamW" + ) + args.optimizer = "adamw" + + if args.use_8bit_adam and not args.optimizer.lower() == "adamw": + logger.warning( + f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " + f"set to {args.optimizer.lower()}" + ) + + if args.optimizer.lower() == "adamw": + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + if args.optimizer.lower() == "prodigy": + try: + import prodigyopt + except ImportError: + raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`") + + optimizer_class = prodigyopt.Prodigy + + if args.learning_rate <= 0.1: + logger.warning( + "Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0" + ) + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + beta3=args.prodigy_beta3, + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + decouple=args.prodigy_decouple, + use_bias_correction=args.prodigy_use_bias_correction, + safeguard_warmup=args.prodigy_safeguard_warmup, + ) + + # Setup data: + train_dataset = getdataset(args) + train_dataloader = torch.utils.data.DataLoader( + train_dataset, + shuffle=True, + # pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + # prefetch_factor=8 + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + vip, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + vip, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_vip.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + # NOTE wandb + if accelerator.is_main_process: + project_name = os.getenv('PROJECT', os.path.basename(args.output_dir)) + entity = os.getenv('ENTITY', None) + run_name = os.getenv('WANDB_NAME', None) + init_kwargs = { + "entity": entity, + "run_name": run_name, + } + accelerator.init_trackers(project_name=project_name, config=vars(args), init_kwargs=init_kwargs) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {vip}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total trainable parameters = {sum(p.numel() for p in vip.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # NOTE checking update of params + # initial_params = {name: param.clone() for name, param in vip.named_parameters()} + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_vip.step(vip.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + progress_info.train_loss = 0.0 + + # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. + if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if accelerator.is_main_process and args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(model_input, model_kwargs, prof): + # vip.train() + # model.train() + global start_time + start_time = time.time() + + noise = torch.randn_like(model_input) + if args.noise_offset: + # https://www.crosslabs.org//blog/diffusion-with-offset-noise + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1), + device=model_input.device) + + bsz = model_input.shape[0] + # Sample a random timestep for each image without bias. + timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + + # Add noise to the model input according to the noise magnitude at each timestep + # (this is the forward diffusion process) + + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) + + model_pred = vip( + model=model, + latent_model_input=noisy_model_input, + timestep=timesteps, + **model_kwargs, + ) + + model_pred = torch.chunk(model_pred, 2, dim=1)[0] + + + # Get the target for loss depending on the prediction type + if args.prediction_type is not None: + # set prediction_type of scheduler if defined + noise_scheduler.register_to_config(prediction_type=args.prediction_type) + + if noise_scheduler.config.prediction_type == "epsilon": + target = noise + elif noise_scheduler.config.prediction_type == "v_prediction": + target = noise_scheduler.get_velocity(model_input, noise, timesteps) + elif noise_scheduler.config.prediction_type == "sample": + # We set the target to latents here, but the model_pred will return the noise sample prediction. + target = model_input + # We will have to subtract the noise residual from the prediction to get the target sample. + model_pred = model_pred - noise + else: + raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + + if args.snr_gamma is None: + loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + else: + # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. + # Since we predict the noise instead of x_0, the original formulation is slightly changed. + # This is discussed in Section 4.2 of the same paper. + snr = compute_snr(noise_scheduler, timesteps) + mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min( + dim=1 + )[0] + if noise_scheduler.config.prediction_type == "epsilon": + mse_loss_weights = mse_loss_weights / snr + elif noise_scheduler.config.prediction_type == "v_prediction": + mse_loss_weights = mse_loss_weights / (snr + 1) + + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights + loss = loss.mean() + + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = vip.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + + if progress_info.global_step % args.checkpointing_steps == 0: + + if args.enable_tracker: + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ) + + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_vip.store(vip.parameters()) + ema_vip.copy_to(vip.parameters()) + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ema=True, + ) + # Switch back to the original UNet parameters. + ema_vip.restore(vip.parameters()) + + if prof is not None: + prof.step() + + + return loss + + def train_one_step(step_, data_item_, prof_=None): + # NOTE checking params update + # if step_ > 1: + # print("Comparing model parameters before and after training step:") + # for name, param in vip.named_parameters(): + # if not torch.equal(param, initial_params[name]): + # print(f"Parameter {name} has changed.") + # initial_params[name] = param.clone() + # else: + # print(f"Parameter {name} has not changed!") + + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask, clip_data = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+image_num H W + clip_data = clip_data.to(accelerator.device) # B T+image_num C H W + + attn_mask = attn_mask.to(accelerator.device) # B T+image_num H W + input_ids = input_ids.to(accelerator.device) # B 1+image_num L + cond_mask = cond_mask.to(accelerator.device) # B 1+image_num L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1+image_num L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) + + assert not torch.any(torch.isnan(cond)), 'after text_enc' + + cond = cond.reshape(B, N, L, -1) # B 1+image_num L D + + clip_data = rearrange(clip_data, 'b t c h w -> (b t) c h w') # B T+image_num C H W -> B * (T+image_num) C H W + clip_feature = image_encoder(clip_data).last_hidden_state # B * (T+image_num) N D + clip_feature = clip_feature[:, 1:] # drop cls token + clip_feature_height = clip_feature_width = int(sqrt(clip_feature.shape[1])) + clip_feature = rearrange(clip_feature, '(b t) (h w) c -> b t h w c', b=B, h=clip_feature_height, w=clip_feature_width) # B T+image_num H W D + + # Map input images to latent space + normalize latents + if args.use_image_num == 0: + x = ae.encode(x) # B C T H W + else: + videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] + videos = ae.encode(videos) # B C T H W + images = rearrange(images, 'b c t h w -> (b t) c 1 h w') + images = ae.encode(images) + images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + + assert not torch.any(torch.isnan(x)), 'after vae' + + with accelerator.accumulate(vip): + x = x.to(weight_dtype) + assert not torch.any(torch.isnan(x)), 'after vae' + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, + clip_feature=clip_feature) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + if accelerator.is_main_process: + if progress_info.global_step == 0: + print("before training, we need to check the validation mode...") + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ) + if train_one_step(step, data_item, prof_): + break + + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # dataset & dataloader + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + + + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--model_max_length", type=int, default=512) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + + # text encoder & vae & diffusion model + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument("--downsampler", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--model_path", type=str, default="LanguageBind/Open-Sora-Plan-v1.1.0") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + + # diffusion setting + parser.add_argument("--zero_terminal_snr", action="store_true", help="Whether to zero the terminal SNR.") + parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + + # validation & logs + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--checkpointing_steps", type=int, default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument("--resume_from_checkpoint", type=str, default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument("--logging_dir", type=str, default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument("--report_to", type=str, default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + # optimizer & scheduler + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") + parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--prodigy_beta3", type=float, default=None, + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", + ) + parser.add_argument("--lr_scheduler", type=str, default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument("--allow_tf32", action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + + # inpaint dataset + parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode + parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode + parser.add_argument("--default_text_ratio", type=float, default=0.1) + parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") + parser.add_argument("--image_encoder_type", type=str, default='clip', choices=['clip', 'dino']) + parser.add_argument("--image_encoder_name", type=str, default='laion/CLIP-ViT-H-14-laion2B-s32B-b79K') + parser.add_argument("--pretrained_vip_adapter_path", type=str, default=None) + parser.add_argument("--clear_video_ratio", type=float, default=0.0) + parser.add_argument("--use_image_num", type=int, default=0) + + + args = parser.parse_args() + main(args) From 527d52962a028c3f8a44754666b49d5b6ea2c3bc Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Mon, 8 Jul 2024 16:31:33 +0800 Subject: [PATCH 074/134] support opensora (3d dit) --- .gitignore | 4 +- opensora/models/diffusion/opensora/modules.py | 8 ++-- opensora/sample/sample_t2v.py | 12 +++--- scripts/text_condition/gpu/sample_image.sh | 8 ++-- scripts/text_condition/gpu/sample_video.sh | 10 ++--- scripts/text_condition/gpu/sample_video_hw.sh | 19 +++++++++ ...3d_61x240p.sh => train_video3d_61x480p.sh} | 40 +++++++++++++------ ...ra_61x480p_new_rope_fp32_aesmovie_sucai.sh | 5 +-- 8 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 scripts/text_condition/gpu/sample_video_hw.sh rename scripts/text_condition/gpu/{train_video3d_61x240p.sh => train_video3d_61x480p.sh} (51%) diff --git a/.gitignore b/.gitignore index d7d345d31..eaf940631 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,7 @@ wandb/ cache_dir/ wandb/ test* -sample_videos/ +sample_video*/ 512* 720* 1024* @@ -31,7 +31,7 @@ private* caption* .deepspeed_env 256* -sample_images/ +sample_image*/ taming* *test* sft* diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 0c9862bf8..8cc02c21c 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -529,8 +529,8 @@ def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_th padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, down_shortcut=True) - self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) - self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) @@ -750,8 +750,8 @@ def __call__( key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) # qk norm - query = attn.q_norm(query) - key = attn.k_norm(key) + # query = attn.q_norm(query) + # key = attn.k_norm(key) if self.use_rope: # require the shape of (batch_size x nheads x ntokens x dim) diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 03f50cdca..d653b28fa 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -51,10 +51,10 @@ def main(args): # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) if args.model_3d: - # transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, - # low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, ignore_mismatched_sizes=True, + transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + # transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, ignore_mismatched_sizes=True, + # low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) @@ -131,8 +131,8 @@ def main(args): video_grids = [] for idx, prompt in enumerate(text_prompt): videos = pipeline( - # positive_prompt.format(prompt), - prompt, + positive_prompt.format(prompt), + # prompt, negative_prompt=negative_prompt, num_frames=args.num_frames, height=args.height, @@ -166,7 +166,7 @@ def main(args): nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) else: video_grids = save_video_grid(video_grids) - imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=9) + imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=6) print('save path {}'.format(args.save_img_path)) diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index d52d72210..abe95f1c9 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,15 +1,15 @@ -CUDA_VISIBLE_DEVICES=1 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/checkpoints/bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema \ +CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ + --model_path /storage/dataset/hw29/image/model_ema \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ --width 640 \ --cache_dir "../cache_dir" \ --text_encoder_name google/mt5-xxl \ - --text_prompt examples/prompt_list_0.txt \ + --text_prompt examples/prompt_list_1.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "sample_image_fp32_73000_cfg2.5_step20_480p_pos_neg_convert" \ + --save_img_path "sample_image" \ --fps 24 \ --guidance_scale 2.5 \ --num_sampling_steps 20 \ diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index c303461ed..a21038a7a 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -1,19 +1,19 @@ -CUDA_VISIBLE_DEVICES=3 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2/checkpoint-12500/model_ema \ +CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_uditultra122_ds222_rope_qknorm_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.25/checkpoint-1500/model_ema \ --version 65x512x512 \ --num_frames 61 \ --height 480 \ --width 640 \ --cache_dir "../cache_dir" \ --text_encoder_name google/mt5-xxl \ - --text_prompt examples/prompt_list_1.txt \ + --text_prompt examples/prompt_list_0.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_video_ema_61x480p_k333_s222_cfg2.5_step50" \ + --save_img_path "./sample_video_1500ema_61x480p_k333_s222_cfg2.5_step50_PNDM_bf16" \ --fps 24 \ --guidance_scale 2.5 \ --num_sampling_steps 50 \ --enable_tiling \ --max_sequence_length 512 \ - --sample_method DPMSolverMultistep \ + --sample_method PNDM \ --model_3d \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video_hw.sh b/scripts/text_condition/gpu/sample_video_hw.sh new file mode 100644 index 000000000..e307301fd --- /dev/null +++ b/scripts/text_condition/gpu/sample_video_hw.sh @@ -0,0 +1,19 @@ +CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ + --model_path /storage/dataset/hw29/image/model_ema \ + --version 65x512x512 \ + --num_frames 29 \ + --height 480 \ + --width 640 \ + --cache_dir "../cache_dir" \ + --text_encoder_name google/mt5-xxl \ + --text_prompt examples/prompt_list_0.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --save_img_path "sample_video_hw" \ + --fps 24 \ + --guidance_scale 2.5 \ + --num_sampling_steps 20 \ + --enable_tiling \ + --max_sequence_length 512 \ + --sample_method DPMSolverMultistep \ + --model_3d \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video3d_61x240p.sh b/scripts/text_condition/gpu/train_video3d_61x480p.sh similarity index 51% rename from scripts/text_condition/gpu/train_video3d_61x240p.sh rename to scripts/text_condition/gpu/train_video3d_61x480p.sh index 3d03c7528..0ed4d2bdb 100644 --- a/scripts/text_condition/gpu/train_video3d_61x240p.sh +++ b/scripts/text_condition/gpu/train_video3d_61x480p.sh @@ -1,53 +1,67 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="testnpu3d_" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh # NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 export NCCL_PXN_DISABLE=0 export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring +# export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ --sample_rate 1 \ - --num_frames 125 \ + --num_frames 61 \ --max_height 480 \ --max_width 640 \ - --interpolation_scale_t 1.0 \ + --interpolation_scale_t 2.0 \ --interpolation_scale_h 1.0 \ --interpolation_scale_w 1.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=1 \ + --train_batch_size=2 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ - --learning_rate=2e-5 \ + --learning_rate=1e-4 \ --lr_scheduler="constant" \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ --checkpointing_steps=500 \ - --output_dir="debug" \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ - --enable_tracker \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ - --tile_overlap_factor 0.125 + --noise_offset 0.02 \ + --use_rope \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.2 \ + --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh index 8730e6f22..2c5b949cd 100644 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh @@ -24,7 +24,7 @@ accelerate launch \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 61 \ + --num_frames 125 \ --max_height 480 \ --max_width 640 \ --interpolation_scale_t 1.0 \ @@ -53,9 +53,8 @@ accelerate launch \ --cfg 0.1 \ --noise_offset 0.02 \ --use_rope \ - --downsampler "k333_s222" \ + --downsampler "k333_s122" \ --resume_from_checkpoint="latest" \ - --enable_stable_fp32 \ --group_frame \ --speed_factor 1.2 \ --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2/checkpoint-12500/model_ema/diffusion_pytorch_model.safetensors" \ From 48bd8fded1ca7c0f0c0fac36ad6018f3ede14005 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Mon, 8 Jul 2024 14:56:25 +0000 Subject: [PATCH 075/134] video ip training from scratch --- .../gpu/train_video_ip_video21d.sh | 27 ++++---- .../gpu/train_video_ip_video21d_clip.sh | 68 +++++++++++++++++++ 2 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 scripts/text_condition/gpu/train_video_ip_video21d_clip.sh diff --git a/scripts/text_condition/gpu/train_video_ip_video21d.sh b/scripts/text_condition/gpu/train_video_ip_video21d.sh index ca27d64ce..59190c858 100644 --- a/scripts/text_condition/gpu/train_video_ip_video21d.sh +++ b/scripts/text_condition/gpu/train_video_ip_video21d.sh @@ -1,4 +1,4 @@ -PROJECT="videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_dino_dim512" +PROJECT="videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_dino_video_from_scratch" export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" # # export WANDB_MODE="offline" export ENTITY="yunyangge" @@ -24,15 +24,15 @@ accelerate launch \ --dataset vip \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/CausalVAEModel_4x8x8" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ --sample_rate 1 \ - --num_frames 1 \ - --use_image_num 0 \ + --num_frames 65 \ + --use_image_num 4 \ --max_height 512 \ --max_width 512 \ --attention_mode xformers \ - --train_batch_size=32 \ + --train_batch_size=2 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=500000 \ @@ -42,27 +42,28 @@ accelerate launch \ --mixed_precision="bf16" \ --report_to="wandb" \ --enable_tracker \ - --checkpointing_steps=500 \ + --checkpointing_steps=1000 \ + --gradient_checkpointing \ --output_dir=$PROJECT \ --allow_tf32 \ --model_max_length 300 \ --enable_tiling \ --validation_dir "validation_dir" \ - --guidance_scale 7.5 \ + --guidance_scale 3.0 \ --num_sampling_steps 50 \ --ema_start_step 0 \ --use_ema \ --cfg 0.05 \ - --i2v_ratio 0.3 \ - --transition_ratio 0.3 \ - --clear_video_ratio 0.3 \ + --i2v_ratio 0.4 \ + --transition_ratio 0.4 \ + --clear_video_ratio 0.1 \ --default_text_ratio 0.5 \ --seed 42 \ --snr_gamma 5.0 \ --noise_offset 0.02 \ - --vip_num_attention_heads 8 \ + --vip_num_attention_heads 16 \ --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ + --resume_from_checkpoint "latest" \ # --pretrained_vip_adapter_path "videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_last2layer/checkpoint-50000/model" \ - # --resume_from_checkpoint "latest" \ # --zero_terminal_snr \ # 基模型权重没有参与训练所以一定要加载 \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video_ip_video21d_clip.sh b/scripts/text_condition/gpu/train_video_ip_video21d_clip.sh new file mode 100644 index 000000000..1a7d63a31 --- /dev/null +++ b/scripts/text_condition/gpu/train_video_ip_video21d_clip.sh @@ -0,0 +1,68 @@ +PROJECT="videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_video_from_scratch" +export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" +# # export WANDB_MODE="offline" +export ENTITY="yunyangge" +export PROJECT=$PROJECT +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export TOKENIZERS_PARALLELISM=false +# # NCCL setting IB网卡时用 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_videoip.py \ + --model "LatteT2V-S/122" \ + --text_encoder_name DeepFloyd/t5-v1_1-xxl \ + --image_encoder_type "clip" \ + --image_encoder_name "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" \ + --cache_dir "/storage/cache_dir" \ + --dataset vip \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 65 \ + --use_image_num 4 \ + --max_height 512 \ + --max_width 512 \ + --attention_mode xformers \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=500000 \ + --learning_rate=1e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --enable_tracker \ + --checkpointing_steps=1000 \ + --gradient_checkpointing \ + --output_dir=$PROJECT \ + --allow_tf32 \ + --model_max_length 300 \ + --enable_tiling \ + --validation_dir "validation_dir" \ + --guidance_scale 5.0 \ + --num_sampling_steps 50 \ + --ema_start_step 0 \ + --use_ema \ + --cfg 0.05 \ + --i2v_ratio 0.4 \ + --transition_ratio 0.4 \ + --clear_video_ratio 0.1 \ + --default_text_ratio 0.5 \ + --seed 42 \ + --snr_gamma 5.0 \ + --noise_offset 0.02 \ + --vip_num_attention_heads 16 \ + --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ + # --pretrained_vip_adapter_path "/storage/gyy/hw/Open-Sora-Plan/videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_dim512/checkpoint-20000/model" \ + # --resume_from_checkpoint "latest" \ + # --zero_terminal_snr \ + # 基模型权重没有参与训练所以一定要加载 \ No newline at end of file From a95fb1c281e8bec83f8a910e6b36859cb29e1eee Mon Sep 17 00:00:00 2001 From: yunyangge Date: Tue, 9 Jul 2024 05:30:00 +0000 Subject: [PATCH 076/134] inpaint dataset --- opensora/dataset/inpaint_datasets.py | 126 ++++ .../diffusion/opensora/modeling_inpaint.py | 598 ++++++++++++++++++ .../diffusion/opensora/modeling_opensora.py | 2 +- 3 files changed, 725 insertions(+), 1 deletion(-) create mode 100644 opensora/dataset/inpaint_datasets.py create mode 100644 opensora/models/diffusion/opensora/modeling_inpaint.py diff --git a/opensora/dataset/inpaint_datasets.py b/opensora/dataset/inpaint_datasets.py new file mode 100644 index 000000000..cbf52d060 --- /dev/null +++ b/opensora/dataset/inpaint_datasets.py @@ -0,0 +1,126 @@ + +from torch.utils.data import Dataset + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None +import glob +import json +import os, io, csv, math, random +import numpy as np +import torchvision +from einops import rearrange +from decord import VideoReader +from os.path import join as opj +from collections import Counter + +import torch +import torchvision.transforms as transforms +from torch.utils.data.dataset import Dataset +from torch.utils.data import DataLoader, Dataset, get_worker_info +from tqdm import tqdm +from PIL import Image +from accelerate.logging import get_logger + +from opensora.utils.dataset_utils import DecordInit +from opensora.utils.utils import text_preprocessing + +from .t2v_datasets import filter_json_by_existed_files, random_video_noise, find_closest_y, filter_resolution +from .t2v_datasets import SingletonMeta, DataSetProg +from .t2v_datasets import T2V_dataset + +logger = get_logger(__name__) + + +dataset_prog = DataSetProg() + +class Inpaint_dataset(T2V_dataset): + def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): + super().__init__(args, transform, temporal_sample, tokenizer, transform_topcrop) + + # inpaint + # The proportion of executing the i2v task. + self.i2v_ratio = args.i2v_ratio + self.transition_ratio = args.transition_ratio + assert self.i2v_ratio + self.transition_ratio < 1, 'The sum of i2v_ratio and transition_ratio should be less than 1.' + self.default_text_ratio = args.default_text_ratio + + def get_video(self, idx): + # npu_config.print_msg(f"current idx is {idx}") + # video = random.choice([random_video_noise(65, 3, 336, 448), random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 480)]) + # # print('random shape', video.shape) + # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) + # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) + + video_path = dataset_prog.vid_cap_list[idx]['path'] + assert os.path.exists(video_path), f"file {video_path} do not exist!" + # frame_indice = self.vid_cap_list[idx]['sample_frame_index'] + video = self.decord_read(video_path) + + h, w = video.shape[-2:] + assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But video ({video_path}) found ratio is {round(h / w, 2)} with the shape of {video.shape}' + t = video.shape[0] + video = self.transform(video) # T C H W -> T C H W + + # inpaint + inpaint_cond_data = self.get_mask_masked_video(video) + mask, masked_video = inpaint_cond_data['mask'], inpaint_cond_data['masked_video'] + video = torch.cat([video, masked_video, mask], dim=1) # T 3*C H W + + # video = torch.rand(221, 3, 480, 640) + + video = video.transpose(0, 1) # T C H W -> C T H W + text = dataset_prog.vid_cap_list[idx]['cap'] + if not isinstance(text, list): + text = [text] + text = [random.choice(text)] + + text = text_preprocessing(text, support_Chinese=self.support_Chinese) + + text = self.drop(text) + + text_tokens_and_mask = self.tokenizer( + text, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids = text_tokens_and_mask['input_ids'] + cond_mask = text_tokens_and_mask['attention_mask'] + return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) + + def drop(self, text): + rand_num = random.random() + rand_num_text = random.random() + + if rand_num < self.cfg: + text = 'The video showcases a scene with coherent and clear visuals.' if rand_num_text < self.default_text_ratio else '' + + return dict(text=text) + + def get_mask_masked_video(self, video): + # video shape (T, C, H, W) + mask = torch.zeros_like(video) + + rand_num = random.random() + # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. + if rand_num < self.i2v_ratio: + mask = 1 - mask + mask[0] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio: + mask = 1 - mask + mask[0] = 0 + mask[-1] = 0 + else: + idx_to_select = random.randint(1, self.num_frames - 1) + selected_indices = random.sample(range(1, self.num_frames), idx_to_select) + mask[selected_indices] = 1 + + masked_video = video * (mask < 0.5) + return dict(mask=mask, masked_video=masked_video) \ No newline at end of file diff --git a/opensora/models/diffusion/opensora/modeling_inpaint.py b/opensora/models/diffusion/opensora/modeling_inpaint.py new file mode 100644 index 000000000..8692ec573 --- /dev/null +++ b/opensora/models/diffusion/opensora/modeling_inpaint.py @@ -0,0 +1,598 @@ +import os +import numpy as np +from torch import nn +import torch +from einops import rearrange, repeat +from typing import Any, Dict, Optional, Tuple +from torch.nn import functional as F +from diffusers.models.transformer_2d import Transformer2DModelOutput +from diffusers.utils import is_torch_version, deprecate +from diffusers.configuration_utils import ConfigMixin, register_to_config +from diffusers.models.modeling_utils import ModelMixin +from diffusers.models.normalization import AdaLayerNormSingle +from diffusers.models.embeddings import PixArtAlphaTextProjection +from opensora.models.diffusion.opensora.modules import OverlapPatchEmbed3D, OverlapPatchEmbed2D, PatchEmbed2D, BasicTransformerBlock +from opensora.utils.utils import to_2tuple +try: + import torch_npu + from opensora.npu_config import npu_config + from opensora.acceleration.parallel_states import get_sequence_parallel_state, hccl_info +except: + torch_npu = None + npu_config = None + +from .modeling_opensora import OpenSoraT2V + +class OpenSoraInpaint(OpenSoraT2V): + _supports_gradient_checkpointing = True + + @register_to_config + def __init__( + self, + num_attention_heads: int = 16, + attention_head_dim: int = 88, + in_channels: Optional[int] = None, + out_channels: Optional[int] = None, + num_layers: int = 1, + dropout: float = 0.0, + norm_num_groups: int = 32, + cross_attention_dim: Optional[int] = None, + attention_bias: bool = False, + sample_size: Optional[int] = None, + sample_size_t: Optional[int] = None, + num_vector_embeds: Optional[int] = None, + patch_size: Optional[int] = None, + patch_size_t: Optional[int] = None, + activation_fn: str = "geglu", + num_embeds_ada_norm: Optional[int] = None, + use_linear_projection: bool = False, + only_cross_attention: bool = False, + double_self_attention: bool = False, + upcast_attention: bool = False, + norm_type: str = "layer_norm", # 'layer_norm', 'ada_norm', 'ada_norm_zero', 'ada_norm_single', 'ada_norm_continuous', 'layer_norm_i2vgen' + norm_elementwise_affine: bool = True, + norm_eps: float = 1e-5, + attention_type: str = "default", + caption_channels: int = None, + interpolation_scale_h: float = None, + interpolation_scale_w: float = None, + interpolation_scale_t: float = None, + use_additional_conditions: Optional[bool] = None, + attention_mode: str = 'xformers', + downsampler: str = None, + use_rope: bool = False, + use_stable_fp32: bool = False, + ): + + super().__init__( + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, + in_channels=in_channels, + out_channels=out_channels, + num_layers=num_layers, + dropout=dropout, + norm_num_groups=norm_num_groups, + cross_attention_dim=cross_attention_dim, + attention_bias=attention_bias, + sample_size=sample_size, + sample_size_t=sample_size_t, + num_vector_embeds=num_vector_embeds, + patch_size=patch_size, + patch_size_t=patch_size_t, + activation_fn=activation_fn, + num_embeds_ada_norm=num_embeds_ada_norm, + use_linear_projection=use_linear_projection, + only_cross_attention=only_cross_attention, + double_self_attention=double_self_attention, + upcast_attention=upcast_attention, + norm_type=norm_type, + norm_elementwise_affine=norm_elementwise_affine, + norm_eps=norm_eps, + attention_type=attention_type, + caption_channels=caption_channels, + interpolation_scale_h=interpolation_scale_h, + interpolation_scale_w=interpolation_scale_w, + interpolation_scale_t=interpolation_scale_t, + use_additional_conditions=use_additional_conditions, + attention_mode=attention_mode, + downsampler=downsampler, + use_rope=use_rope, + use_stable_fp32=use_stable_fp32, + ) + + + + def forward( + self, + hidden_states: torch.Tensor, + timestep: Optional[torch.LongTensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + added_cond_kwargs: Dict[str, torch.Tensor] = None, + class_labels: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + attention_mask: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + use_image_num: Optional[int] = 0, + return_dict: bool = True, + ): + """ + The [`Transformer2DModel`] forward method. + + Args: + hidden_states (`torch.LongTensor` of shape `(batch size, num latent pixels)` if discrete, `torch.FloatTensor` of shape `(batch size, channel, height, width)` if continuous): + Input `hidden_states`. + encoder_hidden_states ( `torch.FloatTensor` of shape `(batch size, sequence len, embed dims)`, *optional*): + Conditional embeddings for cross attention layer. If not given, cross-attention defaults to + self-attention. + timestep ( `torch.LongTensor`, *optional*): + Used to indicate denoising step. Optional timestep to be applied as an embedding in `AdaLayerNorm`. + class_labels ( `torch.LongTensor` of shape `(batch size, num classes)`, *optional*): + Used to indicate class labels conditioning. Optional class labels to be applied as an embedding in + `AdaLayerZeroNorm`. + cross_attention_kwargs ( `Dict[str, Any]`, *optional*): + A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under + `self.processor` in + [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py). + attention_mask ( `torch.Tensor`, *optional*): + An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask + is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large + negative values to the attention scores corresponding to "discard" tokens. + encoder_attention_mask ( `torch.Tensor`, *optional*): + Cross-attention mask applied to `encoder_hidden_states`. Two formats supported: + + * Mask `(batch, sequence_length)` True = keep, False = discard. + * Bias `(batch, 1, sequence_length)` 0 = keep, -10000 = discard. + + If `ndim == 2`: will be interpreted as a mask, then converted into a bias consistent with the format + above. This bias will be added to the cross-attention scores. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] instead of a plain + tuple. + + Returns: + If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a + `tuple` where the first element is the sample tensor. + """ + batch_size, c, frame, h, w = hidden_states.shape + frame = frame - use_image_num # 21-4=17 + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + print.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. + # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. + # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. + # expects mask of shape: + # [batch, key_tokens] + # adds singleton query_tokens dimension: + # [batch, 1, key_tokens] + # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: + # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) + # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + attention_mask_vid, attention_mask_img = None, None + if attention_mask is not None and attention_mask.ndim == 4: + # assume that mask is expressed as: + # (1 = keep, 0 = discard) + # convert mask into a bias that can be added to attention scores: + # (keep = +0, discard = -10000.0) + # b, frame+use_image_num, h, w -> a video with images + # b, 1, h, w -> only images + attention_mask = attention_mask.to(self.dtype) + if npu_config is not None and get_sequence_parallel_state(): + attention_mask_vid = attention_mask[:, :frame * hccl_info.world_size] # b, frame, h, w + attention_mask_img = attention_mask[:, frame * hccl_info.world_size:] # b, use_image_num, h, w + else: + attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w + attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w + + if attention_mask_vid.numel() > 0: + attention_mask_vid_first_frame = attention_mask_vid[:, :1].repeat(1, self.patch_size_t-1, 1, 1) + attention_mask_vid = torch.cat([attention_mask_vid_first_frame, attention_mask_vid], dim=1) + attention_mask_vid = attention_mask_vid.unsqueeze(1) # b 1 t h w + attention_mask_vid = F.max_pool3d(attention_mask_vid, kernel_size=(self.patch_size_t, self.patch_size, self.patch_size), + stride=(self.patch_size_t, self.patch_size, self.patch_size)) + attention_mask_vid = rearrange(attention_mask_vid, 'b 1 t h w -> (b 1) 1 (t h w)') + if attention_mask_img.numel() > 0: + attention_mask_img = F.max_pool2d(attention_mask_img, kernel_size=(self.patch_size, self.patch_size), stride=(self.patch_size, self.patch_size)) + attention_mask_img = rearrange(attention_mask_img, 'b i h w -> (b i) 1 (h w)') + + attention_mask_vid = (1 - attention_mask_vid.bool().to(self.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None + attention_mask_img = (1 - attention_mask_img.bool().to(self.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None + + if frame == 1 and use_image_num == 0: + attention_mask_img = attention_mask_vid + attention_mask_vid = None + # convert encoder_attention_mask to a bias the same way we do for attention_mask + # import ipdb;ipdb.set_trace() + if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: + # b, 1+use_image_num, l -> a video with images + # b, 1, l -> only images + encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 + in_t = encoder_attention_mask.shape[1] + encoder_attention_mask_vid = encoder_attention_mask[:, :in_t-use_image_num] # b, 1, l + encoder_attention_mask_vid = rearrange(encoder_attention_mask_vid, 'b 1 l -> (b 1) 1 l') if encoder_attention_mask_vid.numel() > 0 else None + + encoder_attention_mask_img = encoder_attention_mask[:, in_t-use_image_num:] # b, use_image_num, l + encoder_attention_mask_img = rearrange(encoder_attention_mask_img, 'b i l -> (b i) 1 l') if encoder_attention_mask_img.numel() > 0 else None + + if frame == 1 and use_image_num == 0: + encoder_attention_mask_img = encoder_attention_mask_vid + encoder_attention_mask_vid = None + + if npu_config is not None and attention_mask_vid is not None: + attention_mask_vid = npu_config.get_attention_mask(attention_mask_vid, attention_mask_vid.shape[-1]) + encoder_attention_mask_vid = npu_config.get_attention_mask(encoder_attention_mask_vid, + attention_mask_vid.shape[-2]) + if npu_config is not None and attention_mask_img is not None: + attention_mask_img = npu_config.get_attention_mask(attention_mask_img, attention_mask_img.shape[-1]) + encoder_attention_mask_img = npu_config.get_attention_mask(encoder_attention_mask_img, + attention_mask_img.shape[-2]) + + + # 1. Input + frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy + height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size + + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, \ + timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img = self._operate_on_patched_inputs( + hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num + ) + # 2. Blocks + # import ipdb;ipdb.set_trace() + if npu_config is not None and get_sequence_parallel_state(): + if hidden_states_vid is not None: + hidden_states_vid = rearrange(hidden_states_vid, 'b s h -> s b h', b=batch_size).contiguous() + encoder_hidden_states_vid = rearrange(encoder_hidden_states_vid, 'b s h -> s b h', + b=batch_size).contiguous() + timestep_vid = timestep_vid.view(batch_size, 6, -1).transpose(0, 1).contiguous() + for block in self.transformer_blocks: + if self.training and self.gradient_checkpointing: + + def create_custom_forward(module, return_dict=None): + def custom_forward(*inputs): + if return_dict is not None: + return module(*inputs, return_dict=return_dict) + else: + return module(*inputs) + + return custom_forward + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + # import ipdb;ipdb.set_trace() + if hidden_states_vid is not None: + hidden_states_vid = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + hidden_states_vid, + attention_mask_vid, + encoder_hidden_states_vid, + encoder_attention_mask_vid, + timestep_vid, + cross_attention_kwargs, + class_labels, + frame, + height, + width, + **ckpt_kwargs, + ) + # import ipdb;ipdb.set_trace() + if hidden_states_img is not None: + hidden_states_img = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + hidden_states_img, + attention_mask_img, + encoder_hidden_states_img, + encoder_attention_mask_img, + timestep_img, + cross_attention_kwargs, + class_labels, + 1, + height, + width, + **ckpt_kwargs, + ) + else: + if hidden_states_vid is not None: + hidden_states_vid = block( + hidden_states_vid, + attention_mask=attention_mask_vid, + encoder_hidden_states=encoder_hidden_states_vid, + encoder_attention_mask=encoder_attention_mask_vid, + timestep=timestep_vid, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + frame=frame, + height=height, + width=width, + ) + if hidden_states_img is not None: + hidden_states_img = block( + hidden_states_img, + attention_mask=attention_mask_img, + encoder_hidden_states=encoder_hidden_states_img, + encoder_attention_mask=encoder_attention_mask_img, + timestep=timestep_img, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + frame=1, + height=height, + width=width, + ) + + if npu_config is not None and get_sequence_parallel_state(): + if hidden_states_vid is not None: + hidden_states_vid = rearrange(hidden_states_vid, 's b h -> b s h', b=batch_size).contiguous() + + # 3. Output + output_vid, output_img = None, None + if hidden_states_vid is not None: + output_vid = self._get_output_for_patched_inputs( + hidden_states=hidden_states_vid, + timestep=timestep_vid, + class_labels=class_labels, + embedded_timestep=embedded_timestep_vid, + num_frames=frame, + height=height, + width=width, + ) # b c t h w + if hidden_states_img is not None: + output_img = self._get_output_for_patched_inputs( + hidden_states=hidden_states_img, + timestep=timestep_img, + class_labels=class_labels, + embedded_timestep=embedded_timestep_img, + num_frames=1, + height=height, + width=width, + ) # b c 1 h w + if use_image_num != 0: + output_img = rearrange(output_img, '(b i) c 1 h w -> b c i h w', i=use_image_num) + + if output_vid is not None and output_img is not None: + output = torch.cat([output_vid, output_img], dim=2) + elif output_vid is not None: + output = output_vid + elif output_img is not None: + output = output_img + + if not return_dict: + return (output,) + + return Transformer2DModelOutput(sample=output) + + + def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): + # batch_size = hidden_states.shape[0] + hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states.to(self.dtype), frame) + timestep_vid, timestep_img = None, None + embedded_timestep_vid, embedded_timestep_img = None, None + encoder_hidden_states_vid, encoder_hidden_states_img = None, None + + if self.adaln_single is not None: + if self.use_additional_conditions and added_cond_kwargs is None: + raise ValueError( + "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." + ) + timestep, embedded_timestep = self.adaln_single( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + if hidden_states_vid is None: + timestep_img = timestep + embedded_timestep_img = embedded_timestep + else: + timestep_vid = timestep + embedded_timestep_vid = embedded_timestep + if hidden_states_img is not None: + timestep_img = repeat(timestep, 'b d -> (b i) d', i=use_image_num).contiguous() + embedded_timestep_img = repeat(embedded_timestep, 'b d -> (b i) d', i=use_image_num).contiguous() + + if self.caption_projection is not None: + encoder_hidden_states = self.caption_projection(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + if hidden_states_vid is None: + encoder_hidden_states_img = rearrange(encoder_hidden_states, 'b 1 l d -> (b 1) l d') + else: + encoder_hidden_states_vid = rearrange(encoder_hidden_states[:, :1], 'b 1 l d -> (b 1) l d') + if hidden_states_img is not None: + encoder_hidden_states_img = rearrange(encoder_hidden_states[:, 1:], 'b i l d -> (b i) l d') + + + return hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img + + + + def _get_output_for_patched_inputs( + self, hidden_states, timestep, class_labels, embedded_timestep, num_frames, height=None, width=None + ): + # import ipdb;ipdb.set_trace() + if self.config.norm_type != "ada_norm_single": + conditioning = self.transformer_blocks[0].norm1.emb( + timestep, class_labels, hidden_dtype=self.dtype + ) + shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] + hidden_states = self.proj_out_2(hidden_states) + elif self.config.norm_type == "ada_norm_single": + shift, scale = (self.scale_shift_table[None] + embedded_timestep[:, None]).chunk(2, dim=1) + hidden_states = self.norm_out(hidden_states) + # Modulation + hidden_states = hidden_states * (1 + scale) + shift + hidden_states = self.proj_out(hidden_states) + hidden_states = hidden_states.squeeze(1) + + # unpatchify + if self.adaln_single is None: + height = width = int(hidden_states.shape[1] ** 0.5) + hidden_states = hidden_states.reshape( + shape=(-1, num_frames, height, width, self.patch_size_t, self.patch_size, self.patch_size, self.out_channels) + ) + hidden_states = torch.einsum("nthwopqc->nctohpwq", hidden_states) + output = hidden_states.reshape( + shape=(-1, self.out_channels, num_frames * self.patch_size_t, height * self.patch_size, width * self.patch_size) + ) + # import ipdb;ipdb.set_trace() + # if output.shape[2] % 2 == 0: + # output = output[:, :, 1:] + return output + +def OpenSoraT2V_S_111(**kwargs): + return OpenSoraT2V(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) + +def OpenSoraT2V_B_111(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) + +def OpenSoraT2V_L_111(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=1, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2304, **kwargs) + +def OpenSoraT2V_S_122(**kwargs): + return OpenSoraT2V(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) + +def OpenSoraT2V_B_122(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) + +def OpenSoraT2V_L_122(**kwargs): + return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) + +def OpenSoraT2V_ROPE_L_122(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2304, **kwargs) +# def OpenSoraT2V_S_222(**kwargs): +# return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) + + +def OpenSoraT2V_B_222(**kwargs): + return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=2, patch_size=2, + norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) + +# def OpenSoraT2V_L_222(**kwargs): +# return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) + +# def OpenSoraT2V_XL_222(**kwargs): +# return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) + +# def OpenSoraT2V_XXL_222(**kwargs): +# return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, +# norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) + +OpenSora_models = { + "OpenSoraT2V-S/122": OpenSoraT2V_S_122, # 1.1B + "OpenSoraT2V-B/122": OpenSoraT2V_B_122, + "OpenSoraT2V-L/122": OpenSoraT2V_L_122, + "OpenSoraT2V-ROPE-L/122": OpenSoraT2V_ROPE_L_122, + "OpenSoraT2V-S/111": OpenSoraT2V_S_111, + "OpenSoraT2V-B/111": OpenSoraT2V_B_111, + "OpenSoraT2V-L/111": OpenSoraT2V_L_111, + # "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, + # "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, + # "OpenSoraT2V-S/222": OpenSoraT2V_S_222, + "OpenSoraT2V-B/222": OpenSoraT2V_B_222, + # "OpenSoraT2V-L/222": OpenSoraT2V_L_222, + # "OpenSoraT2V-XL/222": OpenSoraT2V_XL_222, + # "OpenSoraT2V-XXL/222": OpenSoraT2V_XXL_222, +} + +OpenSora_models_class = { + "OpenSoraT2V-S/122": OpenSoraT2V, + "OpenSoraT2V-B/122": OpenSoraT2V, + "OpenSoraT2V-L/122": OpenSoraT2V, + "OpenSoraT2V-ROPE-L/122": OpenSoraT2V, + "OpenSoraT2V-S/111": OpenSoraT2V, + "OpenSoraT2V-B/111": OpenSoraT2V, + "OpenSoraT2V-L/111": OpenSoraT2V, + + "OpenSoraT2V-B/222": OpenSoraT2V, +} + +if __name__ == '__main__': + from opensora.models.ae import ae_channel_config, ae_stride_config + from opensora.models.ae import getae, getae_wrapper + from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper + + args = type('args', (), + { + 'ae': 'CausalVAEModel_4x8x8', + 'attention_mode': 'xformers', + 'use_rope': True, + 'model_max_length': 300, + 'max_height': 480, + 'max_width': 640, + 'num_frames': 61, + 'use_image_num': 0, + 'compress_kv_factor': 1, + 'interpolation_scale_t': 1, + 'interpolation_scale_h': 1, + 'interpolation_scale_w': 1, + } + ) + b = 2 + c = 8 + cond_c = 4096 + num_timesteps = 1000 + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: + num_frames = (args.num_frames - 1) // ae_stride_t + 1 + else: + num_frames = args.num_frames // ae_stride_t + + device = torch.device('cuda:0') + model = OpenSoraT2V_L_122(in_channels=c, + out_channels=c, + sample_size=latent_size, + sample_size_t=num_frames, + activation_fn="gelu-approximate", + attention_bias=True, + attention_type="default", + double_self_attention=False, + norm_elementwise_affine=False, + norm_eps=1e-06, + norm_num_groups=32, + num_vector_embeds=None, + only_cross_attention=False, + upcast_attention=False, + use_linear_projection=False, + use_additional_conditions=False, + downsampler=None, + interpolation_scale_t=args.interpolation_scale_t, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + use_rope=args.use_rope, + ).to(device) + + try: + path = "PixArt-Alpha-XL-2-512.safetensors" + from safetensors.torch import load_file as safe_load + ckpt = safe_load(path, device="cpu") + # import ipdb;ipdb.set_trace() + if ckpt['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and ckpt['pos_embed.proj.weight'].ndim == 4: + repeat = model.pos_embed.proj.weight.shape[2] + ckpt['pos_embed.proj.weight'] = ckpt['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + del ckpt['proj_out.weight'], ckpt['proj_out.bias'] + msg = model.load_state_dict(ckpt, strict=False) + print(msg) + except Exception as e: + print(e) + print(model) + print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B') + import sys;sys.exit() + x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) + cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) + attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L + cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L + timestep = torch.randint(0, 1000, (b,), device=device) + model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) + with torch.no_grad(): + output = model(**model_kwargs) + print(output[0].shape) + + + + diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 2e0b3354d..3caf27994 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -604,7 +604,7 @@ def _get_output_for_patched_inputs( # if output.shape[2] % 2 == 0: # output = output[:, :, 1:] return output - + def OpenSoraT2V_S_111(**kwargs): return OpenSoraT2V(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) From 6db6ea145291e9bcbe192163d026af533971b73a Mon Sep 17 00:00:00 2001 From: yunyangge Date: Tue, 9 Jul 2024 05:31:27 +0000 Subject: [PATCH 077/134] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 89b67d7f7..c7c383912 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ alpha_vae cache/ Open-Sora-Plan_models/ sample_image*cfg* +validation_dir/ \ No newline at end of file From 97d1bc05362397240e14bb3a762a203f98232d62 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Tue, 9 Jul 2024 05:32:37 +0000 Subject: [PATCH 078/134] gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index eaf940631..df5d2bf1a 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,5 @@ sample_image*cfg* *pymp* check.py bucket.py -whileinf.py \ No newline at end of file +whileinf.py +validation_dir/ \ No newline at end of file From dd7acfb4ef2f144edc29a0ac4124ef1c7bcd6327 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Wed, 10 Jul 2024 08:57:45 +0000 Subject: [PATCH 079/134] inpaint for 3ddit --- .gitignore | 3 +- opensora/dataset/__init__.py | 32 + opensora/dataset/inpaint_datasets.py | 2 +- opensora/models/diffusion/__init__.py | 8 + .../models/diffusion/opensora/__init__.py | 0 .../diffusion/opensora/modeling_inpaint.py | 647 ++++------ opensora/models/diffusion/opensora/videoip.py | 543 +++++++++ opensora/models/text_encoder/__init__.py | 6 +- opensora/sample/pipeline_inpaint.py | 331 ++++++ opensora/sample/sample_inpaint.py | 267 +++++ opensora/train/train_inpaint.py | 1044 +++++++++++++++++ .../deepspeed_zero2_config.yaml | 2 +- scripts/text_condition/gpu/sample_inpaint.sh | 18 + .../gpu/train_inpaint_video3d_125x480p.sh | 73 ++ .../video_data_aesmovie_sucai_panda.txt | 16 + scripts/train_data/video_data_debug.txt | 2 +- 16 files changed, 2565 insertions(+), 429 deletions(-) create mode 100644 opensora/models/diffusion/opensora/__init__.py create mode 100644 opensora/models/diffusion/opensora/videoip.py create mode 100644 opensora/sample/pipeline_inpaint.py create mode 100644 opensora/sample/sample_inpaint.py create mode 100644 opensora/train/train_inpaint.py create mode 100644 scripts/text_condition/gpu/sample_inpaint.sh create mode 100644 scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh create mode 100644 scripts/train_data/video_data_aesmovie_sucai_panda.txt diff --git a/.gitignore b/.gitignore index df5d2bf1a..07f9ccbf3 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,5 @@ sample_image*cfg* check.py bucket.py whileinf.py -validation_dir/ \ No newline at end of file +validation_dir/ +inpaint*/ \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index d586c02ce..be5fddd9b 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -13,6 +13,7 @@ # npu_config = None # from .t2v_datasets import T2V_dataset from .t2v_datasets import T2V_dataset +from .inpaint_datasets import Inpaint_dataset from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo @@ -90,4 +91,35 @@ def getdataset(args): tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, transform_topcrop=transform_topcrop) + elif args.dataset == 'i2v' or args.dataset == 'inpaint': + resize_topcrop = [CenterCropResizeVideo((args.max_height, args.max_width), top_crop=True), ] + # if args.multi_scale: + # resize = [ + # LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), + # SpatialStrideCropVideo(args.stride) + # ] + # else: + resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + transform_topcrop = transforms.Compose([ + ToTensorVideo(), + *resize_topcrop, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + transform_topcrop = transforms.Compose([ + ToTensorVideo(), + *resize_topcrop, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, + transform_topcrop=transform_topcrop) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/inpaint_datasets.py b/opensora/dataset/inpaint_datasets.py index cbf52d060..0e4f5da09 100644 --- a/opensora/dataset/inpaint_datasets.py +++ b/opensora/dataset/inpaint_datasets.py @@ -80,7 +80,7 @@ def get_video(self, idx): text = text_preprocessing(text, support_Chinese=self.support_Chinese) - text = self.drop(text) + text = self.drop(text)['text'] text_tokens_and_mask = self.tokenizer( text, diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index 2a103b9ff..eb1314904 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -2,19 +2,27 @@ from .opensora.modeling_opensora import OpenSora_models from .udit.modeling_udit import UDiT_models from .udit_ultra.modeling_udit_ultra import UDiT_Ultra_models + +from .opensora.modeling_inpaint import Inpaint_models + Diffusion_models = {} Diffusion_models.update(Latte_models) Diffusion_models.update(OpenSora_models) Diffusion_models.update(UDiT_models) Diffusion_models.update(UDiT_Ultra_models) +Diffusion_models.update(Inpaint_models) from .latte.modeling_latte import Latte_models_class from .opensora.modeling_opensora import OpenSora_models_class from .udit.modeling_udit import UDiT_models_class from .udit_ultra.modeling_udit_ultra import UDiT_Ultra_models_class + +from .opensora.modeling_inpaint import Inpaint_models_class + Diffusion_models_class = {} Diffusion_models_class.update(Latte_models_class) Diffusion_models_class.update(OpenSora_models_class) Diffusion_models_class.update(UDiT_models_class) Diffusion_models_class.update(UDiT_Ultra_models_class) +Diffusion_models_class.update(Inpaint_models_class) \ No newline at end of file diff --git a/opensora/models/diffusion/opensora/__init__.py b/opensora/models/diffusion/opensora/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/opensora/models/diffusion/opensora/modeling_inpaint.py b/opensora/models/diffusion/opensora/modeling_inpaint.py index 8692ec573..9185d2b87 100644 --- a/opensora/models/diffusion/opensora/modeling_inpaint.py +++ b/opensora/models/diffusion/opensora/modeling_inpaint.py @@ -23,6 +23,12 @@ from .modeling_opensora import OpenSoraT2V + +def zero_module(module): + for p in module.parameters(): + nn.init.zeros_(p) + return module + class OpenSoraInpaint(OpenSoraT2V): _supports_gradient_checkpointing = True @@ -100,269 +106,154 @@ def __init__( use_stable_fp32=use_stable_fp32, ) - + self._init_patched_inputs_for_inpainting() - def forward( - self, - hidden_states: torch.Tensor, - timestep: Optional[torch.LongTensor] = None, - encoder_hidden_states: Optional[torch.Tensor] = None, - added_cond_kwargs: Dict[str, torch.Tensor] = None, - class_labels: Optional[torch.LongTensor] = None, - cross_attention_kwargs: Dict[str, Any] = None, - attention_mask: Optional[torch.Tensor] = None, - encoder_attention_mask: Optional[torch.Tensor] = None, - use_image_num: Optional[int] = 0, - return_dict: bool = True, - ): - """ - The [`Transformer2DModel`] forward method. - - Args: - hidden_states (`torch.LongTensor` of shape `(batch size, num latent pixels)` if discrete, `torch.FloatTensor` of shape `(batch size, channel, height, width)` if continuous): - Input `hidden_states`. - encoder_hidden_states ( `torch.FloatTensor` of shape `(batch size, sequence len, embed dims)`, *optional*): - Conditional embeddings for cross attention layer. If not given, cross-attention defaults to - self-attention. - timestep ( `torch.LongTensor`, *optional*): - Used to indicate denoising step. Optional timestep to be applied as an embedding in `AdaLayerNorm`. - class_labels ( `torch.LongTensor` of shape `(batch size, num classes)`, *optional*): - Used to indicate class labels conditioning. Optional class labels to be applied as an embedding in - `AdaLayerZeroNorm`. - cross_attention_kwargs ( `Dict[str, Any]`, *optional*): - A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under - `self.processor` in - [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py). - attention_mask ( `torch.Tensor`, *optional*): - An attention mask of shape `(batch, key_tokens)` is applied to `encoder_hidden_states`. If `1` the mask - is kept, otherwise if `0` it is discarded. Mask will be converted into a bias, which adds large - negative values to the attention scores corresponding to "discard" tokens. - encoder_attention_mask ( `torch.Tensor`, *optional*): - Cross-attention mask applied to `encoder_hidden_states`. Two formats supported: - - * Mask `(batch, sequence_length)` True = keep, False = discard. - * Bias `(batch, 1, sequence_length)` 0 = keep, -10000 = discard. - - If `ndim == 2`: will be interpreted as a mask, then converted into a bias consistent with the format - above. This bias will be added to the cross-attention scores. - return_dict (`bool`, *optional*, defaults to `True`): - Whether or not to return a [`~models.unets.unet_2d_condition.UNet2DConditionOutput`] instead of a plain - tuple. - - Returns: - If `return_dict` is True, an [`~models.transformer_2d.Transformer2DModelOutput`] is returned, otherwise a - `tuple` where the first element is the sample tensor. - """ - batch_size, c, frame, h, w = hidden_states.shape - frame = frame - use_image_num # 21-4=17 - if cross_attention_kwargs is not None: - if cross_attention_kwargs.get("scale", None) is not None: - print.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") - # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. - # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. - # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. - # expects mask of shape: - # [batch, key_tokens] - # adds singleton query_tokens dimension: - # [batch, 1, key_tokens] - # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: - # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) - # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) - attention_mask_vid, attention_mask_img = None, None - if attention_mask is not None and attention_mask.ndim == 4: - # assume that mask is expressed as: - # (1 = keep, 0 = discard) - # convert mask into a bias that can be added to attention scores: - # (keep = +0, discard = -10000.0) - # b, frame+use_image_num, h, w -> a video with images - # b, 1, h, w -> only images - attention_mask = attention_mask.to(self.dtype) - if npu_config is not None and get_sequence_parallel_state(): - attention_mask_vid = attention_mask[:, :frame * hccl_info.world_size] # b, frame, h, w - attention_mask_img = attention_mask[:, frame * hccl_info.world_size:] # b, use_image_num, h, w - else: - attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w - attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w - - if attention_mask_vid.numel() > 0: - attention_mask_vid_first_frame = attention_mask_vid[:, :1].repeat(1, self.patch_size_t-1, 1, 1) - attention_mask_vid = torch.cat([attention_mask_vid_first_frame, attention_mask_vid], dim=1) - attention_mask_vid = attention_mask_vid.unsqueeze(1) # b 1 t h w - attention_mask_vid = F.max_pool3d(attention_mask_vid, kernel_size=(self.patch_size_t, self.patch_size, self.patch_size), - stride=(self.patch_size_t, self.patch_size, self.patch_size)) - attention_mask_vid = rearrange(attention_mask_vid, 'b 1 t h w -> (b 1) 1 (t h w)') - if attention_mask_img.numel() > 0: - attention_mask_img = F.max_pool2d(attention_mask_img, kernel_size=(self.patch_size, self.patch_size), stride=(self.patch_size, self.patch_size)) - attention_mask_img = rearrange(attention_mask_img, 'b i h w -> (b i) 1 (h w)') - - attention_mask_vid = (1 - attention_mask_vid.bool().to(self.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None - attention_mask_img = (1 - attention_mask_img.bool().to(self.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None - - if frame == 1 and use_image_num == 0: - attention_mask_img = attention_mask_vid - attention_mask_vid = None - # convert encoder_attention_mask to a bias the same way we do for attention_mask - # import ipdb;ipdb.set_trace() - if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: - # b, 1+use_image_num, l -> a video with images - # b, 1, l -> only images - encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 - in_t = encoder_attention_mask.shape[1] - encoder_attention_mask_vid = encoder_attention_mask[:, :in_t-use_image_num] # b, 1, l - encoder_attention_mask_vid = rearrange(encoder_attention_mask_vid, 'b 1 l -> (b 1) 1 l') if encoder_attention_mask_vid.numel() > 0 else None - - encoder_attention_mask_img = encoder_attention_mask[:, in_t-use_image_num:] # b, use_image_num, l - encoder_attention_mask_img = rearrange(encoder_attention_mask_img, 'b i l -> (b i) 1 l') if encoder_attention_mask_img.numel() > 0 else None - - if frame == 1 and use_image_num == 0: - encoder_attention_mask_img = encoder_attention_mask_vid - encoder_attention_mask_vid = None - - if npu_config is not None and attention_mask_vid is not None: - attention_mask_vid = npu_config.get_attention_mask(attention_mask_vid, attention_mask_vid.shape[-1]) - encoder_attention_mask_vid = npu_config.get_attention_mask(encoder_attention_mask_vid, - attention_mask_vid.shape[-2]) - if npu_config is not None and attention_mask_img is not None: - attention_mask_img = npu_config.get_attention_mask(attention_mask_img, attention_mask_img.shape[-1]) - encoder_attention_mask_img = npu_config.get_attention_mask(encoder_attention_mask_img, - attention_mask_img.shape[-2]) - - - # 1. Input - frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy - height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size - - added_cond_kwargs = {"resolution": None, "aspect_ratio": None} - hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, \ - timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img = self._operate_on_patched_inputs( - hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num - ) - # 2. Blocks - # import ipdb;ipdb.set_trace() - if npu_config is not None and get_sequence_parallel_state(): - if hidden_states_vid is not None: - hidden_states_vid = rearrange(hidden_states_vid, 'b s h -> s b h', b=batch_size).contiguous() - encoder_hidden_states_vid = rearrange(encoder_hidden_states_vid, 'b s h -> s b h', - b=batch_size).contiguous() - timestep_vid = timestep_vid.view(batch_size, 6, -1).transpose(0, 1).contiguous() - for block in self.transformer_blocks: - if self.training and self.gradient_checkpointing: - - def create_custom_forward(module, return_dict=None): - def custom_forward(*inputs): - if return_dict is not None: - return module(*inputs, return_dict=return_dict) - else: - return module(*inputs) - - return custom_forward - - ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} - # import ipdb;ipdb.set_trace() - if hidden_states_vid is not None: - hidden_states_vid = torch.utils.checkpoint.checkpoint( - create_custom_forward(block), - hidden_states_vid, - attention_mask_vid, - encoder_hidden_states_vid, - encoder_attention_mask_vid, - timestep_vid, - cross_attention_kwargs, - class_labels, - frame, - height, - width, - **ckpt_kwargs, - ) - # import ipdb;ipdb.set_trace() - if hidden_states_img is not None: - hidden_states_img = torch.utils.checkpoint.checkpoint( - create_custom_forward(block), - hidden_states_img, - attention_mask_img, - encoder_hidden_states_img, - encoder_attention_mask_img, - timestep_img, - cross_attention_kwargs, - class_labels, - 1, - height, - width, - **ckpt_kwargs, - ) - else: - if hidden_states_vid is not None: - hidden_states_vid = block( - hidden_states_vid, - attention_mask=attention_mask_vid, - encoder_hidden_states=encoder_hidden_states_vid, - encoder_attention_mask=encoder_attention_mask_vid, - timestep=timestep_vid, - cross_attention_kwargs=cross_attention_kwargs, - class_labels=class_labels, - frame=frame, - height=height, - width=width, - ) - if hidden_states_img is not None: - hidden_states_img = block( - hidden_states_img, - attention_mask=attention_mask_img, - encoder_hidden_states=encoder_hidden_states_img, - encoder_attention_mask=encoder_attention_mask_img, - timestep=timestep_img, - cross_attention_kwargs=cross_attention_kwargs, - class_labels=class_labels, - frame=1, - height=height, - width=width, - ) - - if npu_config is not None and get_sequence_parallel_state(): - if hidden_states_vid is not None: - hidden_states_vid = rearrange(hidden_states_vid, 's b h -> b s h', b=batch_size).contiguous() - - # 3. Output - output_vid, output_img = None, None - if hidden_states_vid is not None: - output_vid = self._get_output_for_patched_inputs( - hidden_states=hidden_states_vid, - timestep=timestep_vid, - class_labels=class_labels, - embedded_timestep=embedded_timestep_vid, - num_frames=frame, - height=height, - width=width, - ) # b c t h w - if hidden_states_img is not None: - output_img = self._get_output_for_patched_inputs( - hidden_states=hidden_states_img, - timestep=timestep_img, - class_labels=class_labels, - embedded_timestep=embedded_timestep_img, - num_frames=1, - height=height, - width=width, - ) # b c 1 h w - if use_image_num != 0: - output_img = rearrange(output_img, '(b i) c 1 h w -> b c i h w', i=use_image_num) - - if output_vid is not None and output_img is not None: - output = torch.cat([output_vid, output_img], dim=2) - elif output_vid is not None: - output = output_vid - elif output_img is not None: - output = output_img - - if not return_dict: - return (output,) - - return Transformer2DModelOutput(sample=output) + def _init_patched_inputs_for_inpainting(self): + assert self.config.sample_size_t is not None, "OpenSoraInpaint over patched input must provide sample_size_t" + assert self.config.sample_size is not None, "OpenSoraInpaint over patched input must provide sample_size" + #assert not (self.config.sample_size_t == 1 and self.config.patch_size_t == 2), "Image do not need patchfy in t-dim" + + self.num_frames = self.config.sample_size_t + self.config.sample_size = to_2tuple(self.config.sample_size) + self.height = self.config.sample_size[0] + self.width = self.config.sample_size[1] + self.patch_size_t = self.config.patch_size_t + self.patch_size = self.config.patch_size + interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 + interpolation_scale_t = ( + self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t + ) + interpolation_scale = ( + self.config.interpolation_scale_h if self.config.interpolation_scale_h is not None else self.config.sample_size[0] / 30, + self.config.interpolation_scale_w if self.config.interpolation_scale_w is not None else self.config.sample_size[1] / 40, + ) + + if self.config.downsampler is not None and len(self.config.downsampler) == 9: + self.pos_embed_mask = nn.ModuleList( + [ + OverlapPatchEmbed3D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ), + zero_module(nn.Linear(self.inner_dim, self.inner_dim)), + ] + ) + self.pos_embed_masked_video = nn.ModuleList( + [ + OverlapPatchEmbed3D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ), + zero_module(nn.Linear(self.inner_dim, self.inner_dim)), + ] + ) + elif self.config.downsampler is not None and len(self.config.downsampler) == 7: + self.pos_embed_mask = nn.ModuleList( + [ + OverlapPatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ), + zero_module(nn.Linear(self.inner_dim, self.inner_dim)), + ] + ) + + self.pos_embed_masked_video = nn.ModuleList( + [ + OverlapPatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ), + zero_module(nn.Linear(self.inner_dim, self.inner_dim)), + ] + ) + + else: + self.pos_embed_mask = nn.ModuleList( + [ + PatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ), + zero_module(nn.Linear(self.inner_dim, self.inner_dim, bias=False)), + ] + ) + self.pos_embed_masked_video = nn.ModuleList( + [ + PatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ), + zero_module(nn.Linear(self.inner_dim, self.inner_dim, bias=False)), + ] + ) def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): # batch_size = hidden_states.shape[0] + + # inpaint + in_channels = self.config.in_channels + hidden_states, hidden_states_masked_vid, hidden_states_mask = hidden_states[:, :in_channels], hidden_states[:, in_channels: 2 * in_channels], hidden_states[:, 2 * in_channels: 3 * in_channels] + hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states.to(self.dtype), frame) + + hidden_states_masked_vid, _ = self.pos_embed_masked_video[0](hidden_states_masked_vid.to(self.dtype), frame) + hidden_states_masked_vid = self.pos_embed_masked_video[1](hidden_states_masked_vid) + + hidden_states_mask, _ = self.pos_embed_mask[0](hidden_states_mask.to(self.dtype), frame) + hidden_states_mask = self.pos_embed_mask[1](hidden_states_mask) + + hidden_states_vid = hidden_states_vid + hidden_states_masked_vid + hidden_states_mask + timestep_vid, timestep_img = None, None embedded_timestep_vid, embedded_timestep_img = None, None encoder_hidden_states_vid, encoder_hidden_states_img = None, None @@ -397,201 +288,111 @@ def _operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, times return hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img - - - def _get_output_for_patched_inputs( - self, hidden_states, timestep, class_labels, embedded_timestep, num_frames, height=None, width=None - ): - # import ipdb;ipdb.set_trace() - if self.config.norm_type != "ada_norm_single": - conditioning = self.transformer_blocks[0].norm1.emb( - timestep, class_labels, hidden_dtype=self.dtype - ) - shift, scale = self.proj_out_1(F.silu(conditioning)).chunk(2, dim=1) - hidden_states = self.norm_out(hidden_states) * (1 + scale[:, None]) + shift[:, None] - hidden_states = self.proj_out_2(hidden_states) - elif self.config.norm_type == "ada_norm_single": - shift, scale = (self.scale_shift_table[None] + embedded_timestep[:, None]).chunk(2, dim=1) - hidden_states = self.norm_out(hidden_states) - # Modulation - hidden_states = hidden_states * (1 + scale) + shift - hidden_states = self.proj_out(hidden_states) - hidden_states = hidden_states.squeeze(1) - - # unpatchify - if self.adaln_single is None: - height = width = int(hidden_states.shape[1] ** 0.5) - hidden_states = hidden_states.reshape( - shape=(-1, num_frames, height, width, self.patch_size_t, self.patch_size, self.patch_size, self.out_channels) - ) - hidden_states = torch.einsum("nthwopqc->nctohpwq", hidden_states) - output = hidden_states.reshape( - shape=(-1, self.out_channels, num_frames * self.patch_size_t, height * self.patch_size, width * self.patch_size) - ) - # import ipdb;ipdb.set_trace() - # if output.shape[2] % 2 == 0: - # output = output[:, :, 1:] - return output - -def OpenSoraT2V_S_111(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, + def custom_load_state_dict(self, pretrained_model_path, load_mask=False): + print(f'Loading pretrained model from {pretrained_model_path}...') + model_state_dict = self.state_dict() + if 'safetensors' in pretrained_model_path: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(pretrained_model_path, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + else: # latest stage training weight + checkpoint = torch.load(pretrained_model_path, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] + + if not 'pos_embed_masked_video.0.weight' in checkpoint: + checkpoint['pos_embed_masked_video.0.proj.weight'] = checkpoint['pos_embed.proj.weight'] + checkpoint['pos_embed_masked_video.0.proj.bias'] = checkpoint['pos_embed.proj.bias'] + if not 'pos_embed_mask.0.proj.weight' in checkpoint and load_mask: + checkpoint['pos_embed_mask.0.proj.weight'] = checkpoint['pos_embed.proj.weight'] + checkpoint['pos_embed_mask.0.proj.bias'] = checkpoint['pos_embed.proj.bias'] + missing_keys, unexpected_keys = self.load_state_dict(checkpoint, strict=False) + + print(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + print(f'Successfully load {len(self.state_dict()) - len(missing_keys)}/{len(model_state_dict)} keys from {pretrained_model_path}!') + + +def OpenSoraInpaint_S_111(**kwargs): + return OpenSoraInpaint(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) -def OpenSoraT2V_B_111(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, +def OpenSoraInpaint_B_111(**kwargs): + return OpenSoraInpaint(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=1, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) -def OpenSoraT2V_L_111(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=1, +def OpenSoraInpaint_L_111(**kwargs): + return OpenSoraInpaint(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=1, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2304, **kwargs) -def OpenSoraT2V_S_122(**kwargs): - return OpenSoraT2V(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, +def OpenSoraInpaint_S_122(**kwargs): + return OpenSoraInpaint(num_layers=28, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1536, **kwargs) -def OpenSoraT2V_B_122(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, +def OpenSoraInpaint_B_122(**kwargs): + return OpenSoraInpaint(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) -def OpenSoraT2V_L_122(**kwargs): - return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, +def OpenSoraInpaint_L_122(**kwargs): + return OpenSoraInpaint(num_layers=40, attention_head_dim=128, num_attention_heads=16, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2048, **kwargs) -def OpenSoraT2V_ROPE_L_122(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=2, +def OpenSoraInpaint_ROPE_L_122(**kwargs): + return OpenSoraInpaint(num_layers=32, attention_head_dim=96, num_attention_heads=24, patch_size_t=1, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2304, **kwargs) -# def OpenSoraT2V_S_222(**kwargs): -# return OpenSoraT2V(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, +# def OpenSoraInpaint_S_222(**kwargs): +# return OpenSoraInpaint(num_layers=28, attention_head_dim=72, num_attention_heads=16, patch_size_t=2, patch_size=2, # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1152, **kwargs) -def OpenSoraT2V_B_222(**kwargs): - return OpenSoraT2V(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=2, patch_size=2, +def OpenSoraInpaint_B_222(**kwargs): + return OpenSoraInpaint(num_layers=32, attention_head_dim=96, num_attention_heads=16, patch_size_t=2, patch_size=2, norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=1920, **kwargs) -# def OpenSoraT2V_L_222(**kwargs): -# return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, +# def OpenSoraInpaint_L_222(**kwargs): +# return OpenSoraInpaint(num_layers=40, attention_head_dim=128, num_attention_heads=20, patch_size_t=2, patch_size=2, # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=2560, **kwargs) -# def OpenSoraT2V_XL_222(**kwargs): -# return OpenSoraT2V(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, +# def OpenSoraInpaint_XL_222(**kwargs): +# return OpenSoraInpaint(num_layers=32, attention_head_dim=128, num_attention_heads=32, patch_size_t=2, patch_size=2, # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=4096, **kwargs) -# def OpenSoraT2V_XXL_222(**kwargs): -# return OpenSoraT2V(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, +# def OpenSoraInpaint_XXL_222(**kwargs): +# return OpenSoraInpaint(num_layers=40, attention_head_dim=128, num_attention_heads=40, patch_size_t=2, patch_size=2, # norm_type="ada_norm_single", caption_channels=4096, cross_attention_dim=5120, **kwargs) -OpenSora_models = { - "OpenSoraT2V-S/122": OpenSoraT2V_S_122, # 1.1B - "OpenSoraT2V-B/122": OpenSoraT2V_B_122, - "OpenSoraT2V-L/122": OpenSoraT2V_L_122, - "OpenSoraT2V-ROPE-L/122": OpenSoraT2V_ROPE_L_122, - "OpenSoraT2V-S/111": OpenSoraT2V_S_111, - "OpenSoraT2V-B/111": OpenSoraT2V_B_111, - "OpenSoraT2V-L/111": OpenSoraT2V_L_111, - # "OpenSoraT2V-XL/122": OpenSoraT2V_XL_122, - # "OpenSoraT2V-XXL/122": OpenSoraT2V_XXL_122, - # "OpenSoraT2V-S/222": OpenSoraT2V_S_222, - "OpenSoraT2V-B/222": OpenSoraT2V_B_222, - # "OpenSoraT2V-L/222": OpenSoraT2V_L_222, - # "OpenSoraT2V-XL/222": OpenSoraT2V_XL_222, - # "OpenSoraT2V-XXL/222": OpenSoraT2V_XXL_222, +Inpaint_models = { + "OpenSoraInpaint-S/122": OpenSoraInpaint_S_122, # 1.1B + "OpenSoraInpaint-B/122": OpenSoraInpaint_B_122, + "OpenSoraInpaint-L/122": OpenSoraInpaint_L_122, + "OpenSoraInpaint-ROPE-L/122": OpenSoraInpaint_ROPE_L_122, + "OpenSoraInpaint-S/111": OpenSoraInpaint_S_111, + "OpenSoraInpaint-B/111": OpenSoraInpaint_B_111, + "OpenSoraInpaint-L/111": OpenSoraInpaint_L_111, + # "OpenSoraInpaint-XL/122": OpenSoraInpaint_XL_122, + # "OpenSoraInpaint-XXL/122": OpenSoraInpaint_XXL_122, + # "OpenSoraInpaint-S/222": OpenSoraInpaint_S_222, + "OpenSoraInpaint-B/222": OpenSoraInpaint_B_222, + # "OpenSoraInpaint-L/222": OpenSoraInpaint_L_222, + # "OpenSoraInpaint-XL/222": OpenSoraInpaint_XL_222, + # "OpenSoraInpaint-XXL/222": OpenSoraInpaint_XXL_222, } -OpenSora_models_class = { - "OpenSoraT2V-S/122": OpenSoraT2V, - "OpenSoraT2V-B/122": OpenSoraT2V, - "OpenSoraT2V-L/122": OpenSoraT2V, - "OpenSoraT2V-ROPE-L/122": OpenSoraT2V, - "OpenSoraT2V-S/111": OpenSoraT2V, - "OpenSoraT2V-B/111": OpenSoraT2V, - "OpenSoraT2V-L/111": OpenSoraT2V, +Inpaint_models_class = { + "OpenSoraInpaint-S/122": OpenSoraInpaint, + "OpenSoraInpaint-B/122": OpenSoraInpaint, + "OpenSoraInpaint-L/122": OpenSoraInpaint, + "OpenSoraInpaint-ROPE-L/122": OpenSoraInpaint, + "OpenSoraInpaint-S/111": OpenSoraInpaint, + "OpenSoraInpaint-B/111": OpenSoraInpaint, + "OpenSoraInpaint-L/111": OpenSoraInpaint, - "OpenSoraT2V-B/222": OpenSoraT2V, + "OpenSoraInpaint-B/222": OpenSoraInpaint, } -if __name__ == '__main__': - from opensora.models.ae import ae_channel_config, ae_stride_config - from opensora.models.ae import getae, getae_wrapper - from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper - - args = type('args', (), - { - 'ae': 'CausalVAEModel_4x8x8', - 'attention_mode': 'xformers', - 'use_rope': True, - 'model_max_length': 300, - 'max_height': 480, - 'max_width': 640, - 'num_frames': 61, - 'use_image_num': 0, - 'compress_kv_factor': 1, - 'interpolation_scale_t': 1, - 'interpolation_scale_h': 1, - 'interpolation_scale_w': 1, - } - ) - b = 2 - c = 8 - cond_c = 4096 - num_timesteps = 1000 - ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] - latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) - if getae_wrapper(args.ae) == CausalVQVAEModelWrapper or getae_wrapper(args.ae) == CausalVAEModelWrapper: - num_frames = (args.num_frames - 1) // ae_stride_t + 1 - else: - num_frames = args.num_frames // ae_stride_t - - device = torch.device('cuda:0') - model = OpenSoraT2V_L_122(in_channels=c, - out_channels=c, - sample_size=latent_size, - sample_size_t=num_frames, - activation_fn="gelu-approximate", - attention_bias=True, - attention_type="default", - double_self_attention=False, - norm_elementwise_affine=False, - norm_eps=1e-06, - norm_num_groups=32, - num_vector_embeds=None, - only_cross_attention=False, - upcast_attention=False, - use_linear_projection=False, - use_additional_conditions=False, - downsampler=None, - interpolation_scale_t=args.interpolation_scale_t, - interpolation_scale_h=args.interpolation_scale_h, - interpolation_scale_w=args.interpolation_scale_w, - use_rope=args.use_rope, - ).to(device) - - try: - path = "PixArt-Alpha-XL-2-512.safetensors" - from safetensors.torch import load_file as safe_load - ckpt = safe_load(path, device="cpu") - # import ipdb;ipdb.set_trace() - if ckpt['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and ckpt['pos_embed.proj.weight'].ndim == 4: - repeat = model.pos_embed.proj.weight.shape[2] - ckpt['pos_embed.proj.weight'] = ckpt['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) - del ckpt['proj_out.weight'], ckpt['proj_out.bias'] - msg = model.load_state_dict(ckpt, strict=False) - print(msg) - except Exception as e: - print(e) - print(model) - print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B') - import sys;sys.exit() - x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) - cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) - attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L - cond_mask = torch.randint(0, 2, (b, 1+args.use_image_num, args.model_max_length)).to(device) # B L or B 1+num_images L - timestep = torch.randint(0, 1000, (b,), device=device) - model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) - with torch.no_grad(): - output = model(**model_kwargs) - print(output[0].shape) diff --git a/opensora/models/diffusion/opensora/videoip.py b/opensora/models/diffusion/opensora/videoip.py new file mode 100644 index 000000000..efb5f3485 --- /dev/null +++ b/opensora/models/diffusion/opensora/videoip.py @@ -0,0 +1,543 @@ + +from dataclasses import dataclass + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.utils + +from opensora.models.ae.videobase.modules import attention +from opensora.models.diffusion.utils.pos_embed import RoPE1D, RoPE2D, LinearScalingRoPE2D, LinearScalingRoPE1D + +from opensora.models.diffusion.latte.modules import Attention + +from typing import Any, Dict, Optional, Tuple, Callable +from diffusers.utils import USE_PEFT_BACKEND, BaseOutput, deprecate, is_xformers_available +from diffusers.utils.torch_utils import maybe_allow_in_graph +from diffusers.models.lora import LoRACompatibleLinear + +from .modules import BasicTransformerBlock +from einops import rearrange + +def zero_module(module): + for p in module.parameters(): + nn.init.zeros_(p) + return module + +@dataclass +class VideoIPOutput(BaseOutput): + hidden_states: torch.FloatTensor + vip_cond_mask: torch.FloatTensor + + +class VideoIPVideoEncoder(nn.Module): + + def __init__( + self, + image_encoder_type="clip", + num_attention_heads=16, + attention_head_dim=64, + cross_attention_dim=1152, + num_attention_layers=2, + use_rope=False, + attention_mode='xformers', + vae_scale_factor_t=4, + video_length=16, + max_num_tokens=256, + interpolation_scale_thw=(1, 1, 1), + ): + super().__init__() + + inner_dim = num_attention_heads * attention_head_dim + + self.image_encoder_type = image_encoder_type + + self.vae_scale_factor_t = vae_scale_factor_t + self.video_length = video_length + + self.max_num_tokens = max_num_tokens + + if USE_PEFT_BACKEND: + linear_cls = nn.Linear + else: + linear_cls = LoRACompatibleLinear + + if image_encoder_type == "clip": # F * 16 * 16 + # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") + + self.conv_in = nn.ModuleList( + [ + nn.Conv3d(in_channels=1280, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 + nn.SiLU(), + nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), # F // 2 * 16 * 16 -> F // 4 * 8 * 8 + ] + ) + + self.conv_in_downsample_factor = (4, 2, 2) + + elif image_encoder_type == "dino": # F * 16 * 16 + # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") + + self.conv_in = nn.ModuleList( + [ + nn.Conv3d(in_channels=1536, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 + nn.SiLU(), + nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), # F // 2 * 16 * 16 -> F // 4 * 8 * 8 + ] + ) + + self.conv_in_downsample_factor = (4, 2, 2) + + # elif image_encoder_type == "vae": # F // 4 * 64 * 64 + # assert in_channels is not None, "Please specify latent dim for VAE" + + # self.conv_in = nn.ModuleList( + # [ + # nn.Conv2d(in_channels=in_channels, out_channels=256, kernel_size=3, stride=2, padding=1), + # nn.SiLU(), + # nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=2, padding=1), + # nn.SiLU(), + # nn.Conv2d(in_channels=512, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), + # ] + # ) # F // 4 * 64 * 64 -> F // 4 * 8 * 8 + + # self.conv_in_downsample_factor = (1, 8, 8) + + else: + raise NotImplementedError + + self.trans_block1 = nn.ModuleList( + [ + BasicTransformerBlock( + dim=inner_dim, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, + dropout=0.0, + cross_attention_dim=None, + double_self_attention=False, + activation_fn="geglu", + norm_type="layer_norm", + use_rope=use_rope, + attention_mode=attention_mode, + interpolation_scale_thw=interpolation_scale_thw, + ) + for _ in range(num_attention_layers) + ] + ) # only self-attention + + self.conv_mid = nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(1, 2, 2), padding=1) # F // 4 * 8 * 8 -> F // 4 * 4 * 4 + self.conv_mid_downsample_factor = (1, 2, 2) + + self.trans_block2 = nn.ModuleList( + [ + BasicTransformerBlock( + dim=inner_dim, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, + dropout=0.0, + cross_attention_dim=None, + double_self_attention=False, + activation_fn="geglu", + norm_type="layer_norm", + use_rope=use_rope, + attention_mode=attention_mode, + interpolation_scale_thw=interpolation_scale_thw, + ) + for _ in range(num_attention_layers) + ] + ) # only self-attention + + self.proj_out = linear_cls(inner_dim, cross_attention_dim) + + self.norm_out = nn.LayerNorm(cross_attention_dim) + + def forward( + self, + hidden_states, + image_mode=False, + ): + # B C F H W + input_batch_size, input_frame = hidden_states.shape[0], hidden_states.shape[2] + + if image_mode: + hidden_states = hidden_states.repeat_interleave(self.vae_scale_factor_t, dim=2) + hidden_states = rearrange(hidden_states, 'b c (f i) h w -> (b f) c i h w', f=input_frame, i=self.vae_scale_factor_t) + else: + image_hidden_states = hidden_states[:, :, 0:1].repeat(1, 1, self.vae_scale_factor_t, 1, 1) + hidden_states = torch.cat([image_hidden_states, hidden_states[:, :, 1:]], dim=2) + + for layer in self.conv_in: + hidden_states = layer(hidden_states) + + # after conv_in + frame, height, width = hidden_states.shape[2], hidden_states.shape[3], hidden_states.shape[4] + # if training image, now batch = input_batch_size * frame; if not, batch remains the same + hidden_states = rearrange(hidden_states, 'b c f h w -> b (f h w) c') + + + for layer in self.trans_block1: + hidden_states = layer( + hidden_states=hidden_states, + attention_mask=None, + ) + + # when using image mode, f=1; when using video mode, f=video_length + hidden_states = rearrange(hidden_states, "b (f h w) c -> b c f h w ", f=frame, h=height, w=width) + + hidden_states = self.conv_mid(hidden_states) + + hidden_states = rearrange(hidden_states, "b c f h w -> b (f h w) c") + + + for layer in self.trans_block2: + hidden_states = layer( + hidden_states=hidden_states, + attention_mask=None, + ) + + # when using image mode, n=1*h*w; when using video mode, n=video_length*h*w + if image_mode: + hidden_states = rearrange(hidden_states, '(b f) n c -> b f n c', f=input_frame) + else: + hidden_states = hidden_states.unsqueeze(1) # B N C -> B 1 N C + + + hidden_states = self.proj_out(hidden_states) + + hidden_states = self.norm_out(hidden_states) + + batch, num_seq, num_tokens, _ = hidden_states.shape + hidden_states = F.pad(hidden_states, (0, 0, 0, self.max_num_tokens - num_tokens), value=0.0) + vip_cond_mask = torch.ones([batch, num_seq, num_tokens], device=hidden_states.device, dtype=hidden_states.dtype) + vip_cond_mask = F.pad(vip_cond_mask, (0, self.max_num_tokens - num_tokens), value=0.0) + + # hidden_states: B 1 N D(video) B image_num N D(image), vip_cond_mask: B 1 N(video) B image_num N(image), N = max_num_tokens + return hidden_states, vip_cond_mask + +class VideoIPAdapter(nn.Module): + def __init__( + self, + image_encoder_type="clip", + num_attention_heads=16, + attention_head_dim=64, + cross_attention_dim=1152, + num_attention_layers=2, + use_rope=False, + rope_scaling=None, + attention_mode='xformers', + gradient_checkpointing=False, + vae_scale_factor_t=4, + video_length=17, + ): + super().__init__() + self.gradient_checkpointing = gradient_checkpointing + self.encoder = VideoIPVideoEncoder( + image_encoder_type=image_encoder_type, + num_attention_heads=num_attention_heads, + attention_head_dim=attention_head_dim, + cross_attention_dim=cross_attention_dim, + num_attention_layers=num_attention_layers, + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + vae_scale_factor_t=vae_scale_factor_t, + video_length=video_length, + ) + + def forward( + self, + hidden_states, + use_image_num=0, + return_dict=True, + ): + + assert hidden_states.ndim == 5, "Input tensor must be 5D" + + batch, channels, frame, height, width = hidden_states.shape + frame = frame - use_image_num + + video_hidden_states = hidden_states[:, :, 0:frame] + + one_image_video = True if frame == 1 else False + if self.training and self.gradient_checkpointing: + video_hidden_states, video_cond_mask = torch.utils.checkpoint.checkpoint(self.encoder, video_hidden_states, one_image_video, use_reentrant=False,) + else: + video_hidden_states, video_cond_mask = self.encoder(video_hidden_states, image_mode=one_image_video) + + if use_image_num: + image_hidden_states = hidden_states[:, :, frame:] + if self.training and self.gradient_checkpointing: + image_hidden_states, image_cond_mask = torch.utils.checkpoint.checkpoint(self.encoder, image_hidden_states, True, use_reentrant=False,) + else: + image_hidden_states, image_cond_mask = self.encoder(image_hidden_states, image_mode=True) + hidden_states = torch.cat([video_hidden_states, image_hidden_states], dim=1) # B 1+image_num N D + vip_cond_mask = torch.cat([video_cond_mask, image_cond_mask], dim=1) # B 1+image_num D + else: + hidden_states = video_hidden_states # B 1 N D + vip_cond_mask = video_cond_mask # B 1 N + + + if not return_dict: + return (hidden_states, vip_cond_mask) + + return VideoIPOutput(hidden_states=hidden_states, vip_cond_mask=vip_cond_mask) + + +# IP Adapter +# we need use accelerate, so this processor should extend from nn.Module +class VideoIPAttnProcessor(nn.Module): + r""" + Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). + """ + + def __init__( + self, + dim=1152, + attention_mode='xformers', + use_rope=False, + rope_scaling=None, + compress_kv_factor=None, + + num_vip_tokens=272, + vip_scale=1.0, + dropout=0.0, + ): + super().__init__() + self.dim = dim + self.attention_mode = attention_mode + self.use_rope = use_rope + self.rope_scaling = rope_scaling + self.compress_kv_factor = compress_kv_factor + + if USE_PEFT_BACKEND: + linear_cls = nn.Linear + else: + linear_cls = LoRACompatibleLinear + + if self.use_rope: + self._init_rope() + + if not hasattr(F, "scaled_dot_product_attention"): + raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + + self.to_k_vip = linear_cls(dim, dim, bias=False) + self.to_v_vip = linear_cls(dim, dim, bias=False) + + self.to_out_vip = nn.ModuleList( + [ + zero_module(linear_cls(dim, dim, bias=False)), + nn.Dropout(dropout), + ] + ) + + self.num_vip_tokens = num_vip_tokens + self.vip_scale = vip_scale + + def _init_rope(self): + if self.rope_scaling is None: + self.rope2d = RoPE2D() + self.rope1d = RoPE1D() + else: + scaling_type = self.rope_scaling["type"] + scaling_factor_2d = self.rope_scaling["factor_2d"] + scaling_factor_1d = self.rope_scaling["factor_1d"] + if scaling_type == "linear": + self.rope2d = LinearScalingRoPE2D(scaling_factor=scaling_factor_2d) + self.rope1d = LinearScalingRoPE1D(scaling_factor=scaling_factor_1d) + else: + raise ValueError(f"Unknown RoPE scaling type {scaling_type}") + + def forward( + self, + attn: Attention, + hidden_states: torch.FloatTensor, + encoder_hidden_states: Optional[torch.FloatTensor] = None, + attention_mask: Optional[torch.FloatTensor] = None, + temb: Optional[torch.FloatTensor] = None, + scale: float = 1.0, + position_q: Optional[torch.LongTensor] = None, + position_k: Optional[torch.LongTensor] = None, + last_shape: Tuple[int] = None, + ) -> torch.FloatTensor: + + residual = hidden_states + + args = () if USE_PEFT_BACKEND else (scale,) + + if attn.spatial_norm is not None: + hidden_states = attn.spatial_norm(hidden_states, temb) + + input_ndim = hidden_states.ndim + + if input_ndim == 4: + batch_size, channel, height, width = hidden_states.shape + hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) + + + if self.compress_kv_factor is not None: + batch_size = hidden_states.shape[0] + if len(last_shape) == 2: + encoder_hidden_states = hidden_states.permute(0, 2, 1).reshape(batch_size, self.dim, *last_shape) + encoder_hidden_states = attn.sr(encoder_hidden_states).reshape(batch_size, self.dim, -1).permute(0, 2, 1) + elif len(last_shape) == 1: + encoder_hidden_states = hidden_states.permute(0, 2, 1) + if last_shape[0] % 2 == 1: + first_frame_pad = encoder_hidden_states[:, :, :1].repeat((1, 1, attn.kernel_size - 1)) + encoder_hidden_states = torch.concatenate((first_frame_pad, encoder_hidden_states), dim=2) + encoder_hidden_states = attn.sr(encoder_hidden_states).permute(0, 2, 1) + else: + raise NotImplementedError(f'NotImplementedError with last_shape {last_shape}') + + encoder_hidden_states = attn.norm(encoder_hidden_states) + + batch_size, sequence_length, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + has_vip_tokens = encoder_hidden_states is not None + if has_vip_tokens: + end_pos = sequence_length - self.num_vip_tokens + + # attention_mask include encoder_hidden_states(text) and clip_feature(image or video) + # import ipdb;ipdb.set_trace() + if attention_mask is not None: + if has_vip_tokens: + attention_mask, vip_attention_mask = attention_mask[..., :end_pos], attention_mask[..., end_pos:] + vip_attention_mask = attn.prepare_attention_mask(vip_attention_mask, self.num_vip_tokens, batch_size) + vip_attention_mask = vip_attention_mask.view(batch_size, attn.heads, -1, vip_attention_mask.shape[-1]) + + attention_mask = attn.prepare_attention_mask(attention_mask, end_pos, batch_size) + # scaled_dot_product_attention expects attention_mask shape to be + # (batch, heads, source_length, target_length) + attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) + + if attn.group_norm is not None: + hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) + + args = () if USE_PEFT_BACKEND else (scale,) + query = attn.to_q(hidden_states, *args) + + if encoder_hidden_states is None: + encoder_hidden_states = hidden_states + else: + # get encoder_hidden_states, vip_hidden_states + if has_vip_tokens: + encoder_hidden_states, vip_hidden_states = ( + encoder_hidden_states[:, :end_pos, :], + encoder_hidden_states[:, end_pos:, :], + ) + if attn.norm_cross: + # vip tokens is normed + encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + + + key = attn.to_k(encoder_hidden_states, *args) + value = attn.to_v(encoder_hidden_states, *args) + + vip_key = self.to_k_vip(vip_hidden_states, *args) + vip_value = self.to_v_vip(vip_hidden_states, *args) + + inner_dim = key.shape[-1] + head_dim = inner_dim // attn.heads + + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + vip_key = vip_key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + vip_value = vip_value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + if position_q.ndim == 3: + query = self.rope2d(query, position_q) + elif position_q.ndim == 2: + query = self.rope1d(query, position_q) + else: + raise NotImplementedError + if position_k.ndim == 3: + key = self.rope2d(key, position_k) + vip_key = self.rope2d(vip_key, position_k) + elif position_k.ndim == 2: + key = self.rope1d(key, position_k) + vip_key = self.rope1d(vip_key, position_k) + else: + raise NotImplementedError + + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + if self.attention_mode == 'flash': + assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, attn_mask=vip_attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + vip_hidden_states = F.scaled_dot_product_attention( + query, vip_key, vip_value, attn_mask=vip_attention_mask, dropout_p=0.0, is_causal=False + ) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + + # linear proj + hidden_states = attn.to_out[0](hidden_states, *args) + # dropout + hidden_states = attn.to_out[1](hidden_states) + + vip_hidden_states = vip_hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + vip_hidden_states = vip_hidden_states.to(query.dtype) + + # linear proj + vip_hidden_states = self.to_out_vip[0](vip_hidden_states, *args) + # dropout + vip_hidden_states = self.to_out_vip[1](vip_hidden_states) + + hidden_states = hidden_states + self.vip_scale * vip_hidden_states + + if input_ndim == 4: + hidden_states = hidden_states.transpose(-1, -2).reshape(batch_size, channel, height, width) + + if attn.residual_connection: + hidden_states = hidden_states + residual + + hidden_states = hidden_states / attn.rescale_output_factor + + return hidden_states + +if __name__ == "__main__": + model = VideoIPVideoEncoder( + image_encoder_type="clip", + inner_dim=1024, + cross_attention_dim=1152, + num_attention_layers=2, + use_rope=False, + rope_scaling=None, + attention_mode='math', + vae_scale_factor_t=4, + video_length=17, + ) + + params = sum(p.numel() for p in model.parameters() if p.requires_grad) + print(f"Trainable parameters: {params / 1e6}M") + + video = torch.randn(2, 1280, 1, 16, 16) + + output = model(video, training_image=True) + print(output.vip_cond_mask) + diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 14f1cdb1d..c1669068f 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -11,8 +11,10 @@ def __init__(self, args, **kwargs): self.model_name = args.text_encoder_name if 'mt5' in self.model_name: from transformers import MT5EncoderModel - self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() + print(f"Loading MT5 model: {self.model_name}") + print(f"cache_dir: {args.cache_dir}") + # self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel diff --git a/opensora/sample/pipeline_inpaint.py b/opensora/sample/pipeline_inpaint.py new file mode 100644 index 000000000..a0146a71d --- /dev/null +++ b/opensora/sample/pipeline_inpaint.py @@ -0,0 +1,331 @@ + +from typing import Callable, List, Optional, Tuple, Union +import math +import torch +import torch.nn.functional as F +from einops import rearrange + +from diffusers.utils import ( + BACKENDS_MAPPING, + deprecate, + is_bs4_available, + is_ftfy_available, + logging, + replace_example_docstring, +) +from diffusers.utils.torch_utils import randn_tensor +from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput + +try: + from opensora.npu_config import npu_config +except: + npu_config = None + +logger = logging.get_logger(__name__) # pylint: disable=invalid-name + +from .pipeline_opensora import EXAMPLE_DOC_STRING, retrieve_timesteps + +@torch.no_grad() +@replace_example_docstring(EXAMPLE_DOC_STRING) +def hacked_pipeline_call_for_inpaint( + self, + condition_images: torch.FloatTensor, + condition_images_indices: List[int], + prompt: Union[str, List[str]] = None, + negative_prompt: str = "", + num_inference_steps: int = 20, + timesteps: List[int] = None, + guidance_scale: float = 4.5, + num_images_per_prompt: Optional[int] = 1, + num_frames: Optional[int] = None, + height: Optional[int] = None, + width: Optional[int] = None, + eta: float = 0.0, + generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, + latents: Optional[torch.FloatTensor] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + output_type: Optional[str] = "pil", + return_dict: bool = True, + callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, + callback_steps: int = 1, + clean_caption: bool = True, + use_resolution_binning: bool = True, + max_sequence_length: int = 300, + **kwargs, +) -> Union[ImagePipelineOutput, Tuple]: + """ + Function invoked when calling the pipeline for generation. + + Args: + prompt (`str` or `List[str]`, *optional*): + The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. + instead. + negative_prompt (`str` or `List[str]`, *optional*): + The prompt or prompts not to guide the image generation. If not defined, one has to pass + `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is + less than `1`). + num_inference_steps (`int`, *optional*, defaults to 100): + The number of denoising steps. More denoising steps usually lead to a higher quality image at the + expense of slower inference. + timesteps (`List[int]`, *optional*): + Custom timesteps to use for the denoising process. If not defined, equal spaced `num_inference_steps` + timesteps are used. Must be in descending order. + guidance_scale (`float`, *optional*, defaults to 4.5): + Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). + `guidance_scale` is defined as `w` of equation 2. of [Imagen + Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > + 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, + usually at the expense of lower image quality. + num_images_per_prompt (`int`, *optional*, defaults to 1): + The number of images to generate per prompt. + height (`int`, *optional*, defaults to self.unet.config.sample_size): + The height in pixels of the generated image. + width (`int`, *optional*, defaults to self.unet.config.sample_size): + The width in pixels of the generated image. + eta (`float`, *optional*, defaults to 0.0): + Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to + [`schedulers.DDIMScheduler`], will be ignored for others. + generator (`torch.Generator` or `List[torch.Generator]`, *optional*): + One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) + to make generation deterministic. + latents (`torch.FloatTensor`, *optional*): + Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image + generation. Can be used to tweak the same generation with different prompts. If not provided, a latents + tensor will ge generated by sampling using the supplied random `generator`. + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + prompt_attention_mask (`torch.FloatTensor`, *optional*): Pre-generated attention mask for text embeddings. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not + provided, negative_prompt_embeds will be generated from `negative_prompt` input argument. + negative_prompt_attention_mask (`torch.FloatTensor`, *optional*): + Pre-generated attention mask for negative text embeddings. + output_type (`str`, *optional*, defaults to `"pil"`): + The output format of the generate image. Choose between + [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~pipelines.stable_diffusion.IFPipelineOutput`] instead of a plain tuple. + callback (`Callable`, *optional*): + A function that will be called every `callback_steps` steps during inference. The function will be + called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. + callback_steps (`int`, *optional*, defaults to 1): + The frequency at which the `callback` function will be called. If not specified, the callback will be + called at every step. + clean_caption (`bool`, *optional*, defaults to `True`): + Whether or not to clean the caption before creating embeddings. Requires `beautifulsoup4` and `ftfy` to + be installed. If the dependencies are not installed, the embeddings will be created from the raw + prompt. + use_resolution_binning (`bool` defaults to `True`): + If set to `True`, the requested height and width are first mapped to the closest resolutions using + `ASPECT_RATIO_1024_BIN`. After the produced latents are decoded into images, they are resized back to + the requested resolution. Useful for generating non-square images. + max_sequence_length (`int` defaults to 120): Maximum sequence length to use with the `prompt`. + + Examples: + + Returns: + [`~pipelines.ImagePipelineOutput`] or `tuple`: + If `return_dict` is `True`, [`~pipelines.ImagePipelineOutput`] is returned, otherwise a `tuple` is + returned where the first element is a list with the generated images + """ + # 1. Check inputs. Raise error if not correct + num_frames = num_frames or self.transformer.config.sample_size_t * self.vae.vae_scale_factor[0] + height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] + width = width or self.transformer.config.sample_size[1] * self.vae.vae_scale_factor[2] + self.check_inputs( + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds, + negative_prompt_embeds, + prompt_attention_mask, + negative_prompt_attention_mask, + ) + + # 2. Default height and width to transformer + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + # import ipdb;ipdb.set_trace() + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') + + # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) + # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` + # corresponds to doing no classifier free guidance. + do_classifier_free_guidance = guidance_scale > 1.0 + + # 3. Encode input prompt + ( + prompt_embeds, + prompt_attention_mask, + negative_prompt_embeds, + negative_prompt_attention_mask, + ) = self.encode_prompt( + prompt, + do_classifier_free_guidance, + negative_prompt=negative_prompt, + num_images_per_prompt=num_images_per_prompt, + device=device, + prompt_embeds=prompt_embeds, + negative_prompt_embeds=negative_prompt_embeds, + prompt_attention_mask=prompt_attention_mask, + negative_prompt_attention_mask=negative_prompt_attention_mask, + clean_caption=clean_caption, + max_sequence_length=max_sequence_length, + ) + if do_classifier_free_guidance: + prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) + prompt_attention_mask = torch.cat([negative_prompt_attention_mask, prompt_attention_mask], dim=0) + + # 4. Prepare timesteps + timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device, timesteps) + + # 5. Prepare latents. + latent_channels = self.transformer.config.in_channels + latents = self.prepare_latents( + batch_size * num_images_per_prompt, + latent_channels, + num_frames, + height, + width, + prompt_embeds.dtype, + device, + generator, + latents, + ) + + # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline + extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) + + # 6.1 Prepare micro-conditions. + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + + # 7. Denoising loop + num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) + + # NOTE inpaint + assert isinstance(condition_images_indices, list) and len(condition_images_indices) == len(condition_images) and isinstance(condition_images_indices[0], int), "condition_images_indices should be a list of int" + if isinstance(condition_images, list) and isinstance(condition_images[0], torch.Tensor): + if len(condition_images[0].shape) == 3: + condition_images = [condition_image.unsqueeze(1) for condition_image in condition_images] # C H W -> C 1 H W + elif len(condition_images[0].shape) == 4: + condition_images = [condition_image.transpose(0, 1) for condition_image in condition_images] # 1 C H W -> C 1 H W + else: + raise ValueError("condition_images should be a list of torch.Tensor") + + + input_video = torch.zeros([3, num_frames, height, width], dtype=self.vae.vae.dtype, device=device) + condition_images = torch.cat(condition_images, dim=1).to(device=device) # C 1/2 H W -> C F H W + input_video[:, condition_images_indices] = condition_images + + print(condition_images_indices) + assert torch.equal(input_video[:, condition_images_indices[0]], condition_images[:, 0]), "input_video should be the same as condition_images" + assert torch.equal(input_video[:, condition_images_indices[-1]], condition_images[:, -1]), "input_video should be the same as condition_images" + + input_video = input_video.unsqueeze(0).repeat(batch_size * num_images_per_prompt, 1, 1, 1, 1) + + # not vae style + B, C, T, H, W = input_video.shape + mask = torch.ones([B, 1, T, H, W], device=device) + mask[:, :, condition_images_indices] = 0 + masked_video = input_video * (mask < 0.5) + masked_video = self.vae.encode(masked_video).to(device) + + mask = rearrange(mask, 'b c t h w -> (b c t) 1 h w') + latent_size = (height // self.vae.vae_scale_factor[1], width // self.vae.vae_scale_factor[2]) + if num_frames % 2 == 1: + latent_size_t = (num_frames - 1) // self.vae.vae_scale_factor[0] + 1 + else: + latent_size_t = num_frames // self.vae.vae_scale_factor[0] + mask = F.interpolate(mask, size=latent_size, mode='bilinear') + mask = rearrange(mask, '(b c t) 1 h w -> b c t h w', t=T, b=B) + mask_first_frame = mask[:, :, 0:1].repeat(1, 1, self.vae.vae_scale_factor[0], 1, 1).contiguous() + mask = torch.cat([mask_first_frame, mask[:, :, 1:]], dim=2) + mask = mask.view(batch_size, self.vae.vae_scale_factor[0], latent_size_t, *latent_size).contiguous() + + with self.progress_bar(total=num_inference_steps) as progress_bar: + for i, t in enumerate(timesteps): + latent_model_input = torch.cat([latents, masked_video, mask], dim=1) + latent_model_input = torch.cat([latent_model_input] * 2) if do_classifier_free_guidance else latent_model_input + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) + + current_timestep = t + if not torch.is_tensor(current_timestep): + # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can + # This would be a good case for the `match` statement (Python 3.10+) + is_mps = latent_model_input.device.type == "mps" + if isinstance(current_timestep, float): + dtype = torch.float32 if is_mps else torch.float64 + else: + dtype = torch.int32 if is_mps else torch.int64 + current_timestep = torch.tensor([current_timestep], dtype=dtype, device=latent_model_input.device) + elif len(current_timestep.shape) == 0: + current_timestep = current_timestep[None].to(latent_model_input.device) + # broadcast to batch dimension in a way that's compatible with ONNX/Core ML + current_timestep = current_timestep.expand(latent_model_input.shape[0]) + + # import ipdb;ipdb.set_trace() + if prompt_embeds.ndim == 3: + prompt_embeds = prompt_embeds.unsqueeze(1) # b l d -> b 1 l d + if prompt_attention_mask.ndim == 2: + prompt_attention_mask = prompt_attention_mask.unsqueeze(1) # b l -> b 1 l + # prepare attention_mask. + # b c t h w -> b t h w + attention_mask = torch.ones_like(latent_model_input)[:, 0] + # predict noise model_output + noise_pred = self.transformer( + latent_model_input, + attention_mask=attention_mask, + encoder_hidden_states=prompt_embeds, + encoder_attention_mask=prompt_attention_mask, + timestep=current_timestep, + added_cond_kwargs=added_cond_kwargs, + return_dict=False, + )[0] + + # perform guidance + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + # learned sigma + if self.transformer.config.out_channels // 2 == latent_channels: + noise_pred = noise_pred.chunk(2, dim=1)[0] + else: + noise_pred = noise_pred + + # compute previous image: x_t -> x_t-1 + latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] + + # call the callback, if provided + if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): + progress_bar.update() + if callback is not None and i % callback_steps == 0: + step_idx = i // getattr(self.scheduler, "order", 1) + callback(step_idx, t, latents) + # import ipdb;ipdb.set_trace() + # latents = latents.squeeze(2) + if not output_type == "latent": + # b t h w c + image = self.decode_latents(latents) + image = image[:, :num_frames, :height, :width] + else: + image = latents + + # Offload all models + self.maybe_free_model_hooks() + + if not return_dict: + return (image,) + + return ImagePipelineOutput(images=image) \ No newline at end of file diff --git a/opensora/sample/sample_inpaint.py b/opensora/sample/sample_inpaint.py new file mode 100644 index 000000000..2e4dec427 --- /dev/null +++ b/opensora/sample/sample_inpaint.py @@ -0,0 +1,267 @@ +import math +import os +import pip +import torch +import argparse +import torchvision + +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler +from omegaconf import OmegaConf +from torchvision.utils import save_image +from transformers import T5EncoderModel, MT5EncoderModel, UMT5EncoderModel, AutoTokenizer + +import os, sys + +current_dir = os.path.dirname(os.path.abspath(__file__)) +project_root = os.path.abspath(os.path.join(current_dir, '../..')) +if project_root not in sys.path: + sys.path.append(project_root) + +from opensora.models.ae import ae_stride_config, getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.opensora.modeling_inpaint import OpenSoraInpaint + +from opensora.models.text_encoder import get_text_enc +from opensora.utils.utils import save_video_grid + +from opensora.sample.pipeline_opensora import OpenSoraPipeline +from opensora.sample.pipeline_inpaint import hacked_pipeline_call_for_inpaint +# for validation +import glob +from PIL import Image +from torchvision import transforms +from torchvision.transforms import Lambda +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +import numpy as np +from einops import rearrange + +import imageio +import glob +import gc +import time + +def validation(args): + # torch.manual_seed(args.seed) + weight_dtype = torch.bfloat16 + device = torch.device(args.device) + + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] + + + if args.model_3d: + transformer_model = OpenSoraInpaint.from_pretrained(args.model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + + text_encoder = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + + # set eval mode + transformer_model.eval() + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler(clip_sample=False) + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler(clip_sample=False) + elif args.sample_method == 'DPMSolverMultistep': + ''' + DPM++ 2M DPMSolverMultistepScheduler + DPM++ 2M Karras DPMSolverMultistepScheduler init with use_karras_sigmas=True + DPM++ 2M SDE DPMSolverMultistepScheduler init with algorithm_type="sde-dpmsolver++" + DPM++ 2M SDE Karras DPMSolverMultistepScheduler init with use_karras_sigmas=True and algorithm_type="sde-dpmsolver++" + + DPM++ SDE DPMSolverSinglestepScheduler + DPM++ SDE Karras DPMSolverSinglestepScheduler init with use_karras_sigmas=True + DPM2 KDPM2DiscreteScheduler + DPM2 Karras KDPM2DiscreteScheduler init with use_karras_sigmas=True + DPM2 a KDPM2AncestralDiscreteScheduler + DPM2 a Karras KDPM2AncestralDiscreteScheduler init with use_karras_sigmas=True + ''' + # scheduler = DPMSolverMultistepScheduler(use_karras_sigmas=True) + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + elif args.sample_method == 'EulerDiscreteSVD': + scheduler = EulerDiscreteScheduler.from_pretrained("stabilityai/stable-video-diffusion-img2vid", + subfolder="scheduler", cache_dir=args.cache_dir) + # Save the generated videos + save_dir = args.save_img_path + os.makedirs(save_dir, exist_ok=True) + + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model) + pipeline.to(device) + + pipeline.__call__ = hacked_pipeline_call_for_inpaint.__get__(pipeline, OpenSoraPipeline) + + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + prompt_file = os.path.join(validation_dir, "prompt.txt") + + with open(prompt_file, 'r') as f: + validation_prompt = f.readlines() + + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break + + positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" + negative_prompt = """nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, + """ + + resize = [CenterCropResizeVideo((args.height, args.width)),] + norm_fun = Lambda(lambda x: 2. * x - 1.) + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + + def preprocess_images(images): + if len(images) == 1: + condition_images_indices = [0] + elif len(images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in images] + condition_images = [torch.from_numpy(np.copy(np.array(image))) for image in condition_images] + condition_images = [rearrange(image, 'h w c -> c h w').unsqueeze(0) for image in condition_images] + condition_images = [transform(image).to(device, dtype=weight_dtype) for image in condition_images] + return dict(condition_images=condition_images, condition_images_indices=condition_images_indices) + + videos = [] + for idx, (prompt, images) in enumerate(zip(validation_prompt, validation_images_list)): + + if not isinstance(images, list): + images = [images] + if 'img' in images[0]: + continue + + pre_results = preprocess_images(images) + condition_images = pre_results['condition_images'] + condition_images_indices = pre_results['condition_images_indices'] + + video = pipeline.__call__( + prompt=prompt, + negative_prompt=negative_prompt, + condition_images=condition_images, + condition_images_indices=condition_images_indices, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=args.max_sequence_length, + ).images + videos.append(video[0]) + + ext = 'mp4' + imageio.mimwrite( + os.path.join(save_dir, f'{idx}.{ext}'), video[0], fps=24, quality=6) # highest quality is 10, lowest is 0 + + video_grids = torch.stack(videos, dim=0) + + # torchvision.io.write_video(args.save_img_path + '_%04d' % args.run_time + '-.mp4', video_grids, fps=6) + if args.num_frames == 1: + save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) + else: + video_grids = save_video_grid(video_grids) + imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=6) + + print('save path {}'.format(args.save_img_path)) + + del pipeline + del text_encoder + del vae + del transformer_model + gc.collect() + torch.cuda.empty_cache() + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') + parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") + parser.add_argument("--guidance_scale", type=float, default=2.5) + parser.add_argument("--sample_method", type=str, default="PNDM") + parser.add_argument("--max_sequence_length", type=int, default=300) + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument("--fps", type=int, default=24) + parser.add_argument("--run_time", type=int, default=0) + parser.add_argument("--text_prompt", nargs='+') + parser.add_argument('--tile_overlap_factor', type=float, default=0.125) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--model_3d', action='store_true') + parser.add_argument('--enable_stable_fp32', action='store_true') + + parser.add_argument("--validation_dir", type=str, default=None) + args = parser.parse_args() + + lask_ckpt = None + root_model_path = args.model_path + root_save_path = args.save_img_path + while True: + # Get the most recent checkpoint + dirs = os.listdir(root_model_path) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + if path != lask_ckpt: + print("====================================================") + print(f"sample {path}...") + args.model_path = os.path.join(root_model_path, path, "model") + args.save_img_path = os.path.join(root_save_path, f"{path}_normal") + validation(args) + print("====================================================") + print(f"sample ema {path}...") + args.model_path = os.path.join(root_model_path, path, "model_ema") + args.save_img_path = os.path.join(root_save_path, f"{path}_ema") + validation(args) + lask_ckpt = path + else: + print("no new ckpt, sleeping...") + time.sleep(300) \ No newline at end of file diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py new file mode 100644 index 000000000..0df8c259d --- /dev/null +++ b/opensora/train/train_inpaint.py @@ -0,0 +1,1044 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +import logging +import math +import os +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm + +from opensora.adaptor.modules import replace_with_fp32_forwards + +try: + import torch_npu + from opensora.npu_config import npu_config + from opensora.acceleration.parallel_states import initialize_sequence_parallel_state, \ + destroy_sequence_parallel_group, get_sequence_parallel_state + from opensora.acceleration.communications import prepare_parallel_data, broadcast +except: + torch_npu = None + npu_config = None + pass +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import DistributedType, ProjectConfiguration, set_seed +from packaging import version +from tqdm.auto import tqdm + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.utils.dataset_utils import Collate, LengthGroupedSampler +from opensora.sample.pipeline_opensora import OpenSoraPipeline + + +# for validation +import glob +from PIL import Image +from torchvision import transforms +from torchvision.transforms import Lambda +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +from opensora.sample.pipeline_inpaint import hacked_pipeline_call_for_inpaint +import imageio + + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + +@torch.inference_mode() +def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weight_dtype, global_step, ema=False, max_test_num=2): + + positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" + negative_prompt = """nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, + """ + + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + prompt_file = os.path.join(validation_dir, "prompt.txt") + + with open(prompt_file, 'r') as f: + validation_prompt = f.readlines() + + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break + + resize = [CenterCropResizeVideo((args.max_height, args.max_width)),] + norm_fun = Lambda(lambda x: 2. * x - 1.) + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + + logger.info(f"Running {'normal' if not ema else 'ema'} validation....\n") + model = accelerator.unwrap_model(model) + + scheduler = PNDMScheduler() + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model).to(device=accelerator.device) + + pipeline.__call__ = hacked_pipeline_call_for_inpaint.__get__(pipeline, OpenSoraPipeline) + + def preprocess_images(images): + if len(images) == 1: + condition_images_indices = [0] + elif len(images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in images] + condition_images = [torch.from_numpy(np.copy(np.array(image))) for image in condition_images] + condition_images = [rearrange(image, 'h w c -> c h w').unsqueeze(0) for image in condition_images] + condition_images = [transform(image).to(accelerator.device, dtype=torch.float32) for image in condition_images] + return dict(condition_images=condition_images, condition_images_indices=condition_images_indices) + + videos = [] + val_prompts = [] + val_i2v_num = 0 + val_transition_num = 0 + for prompt, images in zip(validation_prompt, validation_images_list): + + if val_i2v_num > max_test_num and val_transition_num > max_test_num: + break + + if not isinstance(images, list): + images = [images] + + if 'img' in images[0]: + continue + elif 'i2v' in images[0]: + val_i2v_num += 1 + elif 'transition' in images[0]: + val_transition_num += 1 + + logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) + + pre_results = preprocess_images(images) + condition_images = pre_results['condition_images'] + condition_images_indices = pre_results['condition_images_indices'] + + video = pipeline.__call__( + prompt=prompt, + negative_prompt=negative_prompt, + condition_images=condition_images, + condition_images_indices=condition_images_indices, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=args.model_max_length, + ).images + videos.append(video[0]) + val_prompts.append(prompt) + # import ipdb;ipdb.set_trace() + + # Save the generated videos + save_dir = os.path.join(args.output_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") + os.makedirs(save_dir, exist_ok=True) + for idx, video in enumerate(videos): + ext = 'mp4' + imageio.mimwrite( + os.path.join(save_dir, f'{idx}.{ext}'), video, fps=24, quality=6) # highest quality is 10, lowest is 0 + # for wandb + resize_transform = transforms.Compose( + [CenterCropResizeVideo((args.max_height // 4, args.max_width // 4))] + ) + + videos = [resize_transform(video.permute(0, 3, 1, 2)) for video in videos] + + for tracker in accelerator.trackers: + if tracker.name == "wandb": + import wandb + logs = {} + logs[f"{'ema_' if ema else ''}validation_videos"] = [] + for i, (video, prompt) in enumerate(zip(videos, val_prompts)): + logs[f"{'ema_' if ema else ''}validation_videos"].append(wandb.Video(video, caption=f"{i}: {prompt}", fps=24)) + + tracker.log(logs, step=global_step) + + + print("delete validation pipeline...") + del pipeline + gc.collect() + torch.cuda.empty_cache() + + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss + + +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + logging_dir = Path(args.output_dir, args.logging_dir) + + # use LayerNorm, GeLu, SiLu always as fp32 mode + if args.enable_stable_fp32: + replace_with_fp32_forwards() + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) + npu_config.seed_everything(args.seed) + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if npu_config is not None and args.num_frames != 1 and args.use_image_num == 0: + initialize_sequence_parallel_state(args.sp_size) + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + kwargs = {} + ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae], + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + interpolation_scale_t=args.interpolation_scale_t, + downsampler=args.downsampler, + # compress_kv_factor=args.compress_kv_factor, + use_rope=args.use_rope, + # model_max_length=args.model_max_length, + use_stable_fp32=args.enable_stable_fp32, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # # use pretrained model? + if args.pretrained: + model.custom_load_state_dict(args.pretrained) + + # Freeze vae and text encoders. + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + # Set model as trainable. + model.train() + + noise_scheduler = DDPMScheduler() + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + ae.vae.to(accelerator.device, dtype=torch.float32) + # ae.vae.to(accelerator.device, dtype=weight_dtype) + text_enc.to(accelerator.device, dtype=weight_dtype) + + # Create EMA for the unet. + if args.use_ema: + ema_model = deepcopy(model) + ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + ema_model.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), Diffusion_models_class[args.model]) + ema_model.load_state_dict(load_model.state_dict()) + ema_model.to(accelerator.device) + del load_model + + for i in range(len(models)): + # pop models so that they are not loaded again + model = models.pop() + + # load diffusers style into model + load_model = Diffusion_models_class[args.model].from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + params_to_optimize = model.parameters() + # Optimizer creation + if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): + logger.warning( + f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]." + "Defaulting to adamW" + ) + args.optimizer = "adamw" + + if args.use_8bit_adam and not args.optimizer.lower() == "adamw": + logger.warning( + f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " + f"set to {args.optimizer.lower()}" + ) + + if args.optimizer.lower() == "adamw": + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + if args.optimizer.lower() == "prodigy": + try: + import prodigyopt + except ImportError: + raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`") + + optimizer_class = prodigyopt.Prodigy + + if args.learning_rate <= 0.1: + logger.warning( + "Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0" + ) + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + beta3=args.prodigy_beta3, + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + decouple=args.prodigy_decouple, + use_bias_correction=args.prodigy_use_bias_correction, + safeguard_warmup=args.prodigy_safeguard_warmup, + ) + logger.info(f"optimizer: {optimizer}") + + # Setup data: + train_dataset = getdataset(args) + sampler = LengthGroupedSampler( + args.train_batch_size, + world_size=accelerator.num_processes, + lengths=train_dataset.lengths, + group_frame=args.group_frame, + group_resolution=args.group_resolution, + ) if args.group_frame or args.group_resolution else None + train_dataloader = DataLoader( + train_dataset, + shuffle=sampler is None, + # pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + sampler=sampler if args.group_frame or args.group_resolution else None, + # prefetch_factor=4 + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + # model.requires_grad_(False) + # model.pos_embed.requires_grad_(True) + model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + model, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_model.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + if accelerator.is_main_process: + accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {model}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_model.step(model.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + one_step_duration = end_time - start_time + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, " + f"train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) + progress_info.train_loss = 0.0 + + # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. + if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if accelerator.is_main_process and args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(model_input, model_kwargs, prof): + global start_time + start_time = time.time() + + noise = torch.randn_like(model_input) + if args.noise_offset: + # https://www.crosslabs.org//blog/diffusion-with-offset-noise + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1), + device=model_input.device) + + bsz = model_input.shape[0] + # Sample a random timestep for each image without bias. + timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + if npu_config is not None and get_sequence_parallel_state(): + broadcast(timesteps) + + # Add noise to the model input according to the noise magnitude at each timestep + # (this is the forward diffusion process) + + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) + + if model_kwargs.get("masked_x", None) is not None and model_kwargs.get("mask", None) is not None: + masked_x = model_kwargs.pop("masked_x") + video_mask = model_kwargs.pop("mask") + else: + raise ValueError("masked_x and mask must be provided in model_kwargs") + + model_pred = model( + torch.cat([noisy_model_input, masked_x, video_mask], dim=1), + timesteps, + **model_kwargs + )[0] + # Get the target for loss depending on the prediction type + if args.prediction_type is not None: + # set prediction_type of scheduler if defined + noise_scheduler.register_to_config(prediction_type=args.prediction_type) + + if noise_scheduler.config.prediction_type == "epsilon": + target = noise + elif noise_scheduler.config.prediction_type == "v_prediction": + target = noise_scheduler.get_velocity(model_input, noise, timesteps) + elif noise_scheduler.config.prediction_type == "sample": + # We set the target to latents here, but the model_pred will return the noise sample prediction. + target = model_input + # We will have to subtract the noise residual from the prediction to get the target sample. + model_pred = model_pred - noise + else: + raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + + mask = model_kwargs.get('attention_mask', None) + b, c, _, _, _ = model_pred.shape + if mask is not None: + mask = mask.unsqueeze(1).repeat(1, c, 1, 1, 1).float() # b t h w -> b c t h w + mask = mask.reshape(b, -1) + if args.snr_gamma is None: + # model_pred: b c t h w, attention_mask: b t h w + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.reshape(b, -1) + if mask is not None: + loss = (loss * mask).sum() / mask.sum() # mean loss on unpad patches + else: + loss = loss.mean() + else: + # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. + # Since we predict the noise instead of x_0, the original formulation is slightly changed. + # This is discussed in Section 4.2 of the same paper. + snr = compute_snr(noise_scheduler, timesteps) + mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min( + dim=1 + )[0] + if noise_scheduler.config.prediction_type == "epsilon": + mse_loss_weights = mse_loss_weights / snr + elif noise_scheduler.config.prediction_type == "v_prediction": + mse_loss_weights = mse_loss_weights / (snr + 1) + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.reshape(b, -1) + mse_loss_weights = mse_loss_weights.reshape(b, 1) + if mask is not None: + loss = (loss * mask * mse_loss_weights).sum() / mask.sum() # mean loss on unpad patches + else: + loss = (loss * mse_loss_weights).mean() + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = model.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + for tracker in accelerator.trackers: + if tracker.name == "wandb": + if progress_info.global_step % args.checkpointing_steps != 0: + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) + + if args.need_validation: + if progress_info.global_step % args.checkpointing_steps == 0: + if args.enable_tracker: + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step) + + if args.use_ema and npu_config is None: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) + + if prof is not None: + prof.step() + + + return loss + + def train_one_step(step_, data_item_, prof_=None): + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask = data_item_ + # assert torch.all(attn_mask.bool()), 'must all visible' + # Sample noise that we'll add to the latents + # import ipdb;ipdb.set_trace() + if args.group_frame or args.group_resolution: + if not torch.all(torch.any(attn_mask.flatten(-2), dim=-1)): + each_latent_frame = torch.any(attn_mask.flatten(-2), dim=-1).int().sum(-1).tolist() + # logger.info(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask ' + # f'each_latent_frame: {each_latent_frame}') + print(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask ' + f'each_latent_frame: {each_latent_frame}') + assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B 3*C T+num_images H W, 16 + 4 + + attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W + input_ids = input_ids.to(accelerator.device) # B 1+num_images L + cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L + # if accelerator.process_index == 0: + # logger.info(f'rank: {accelerator.process_index}, x: {x.shape}, attn_mask: {attn_mask.shape}') + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1+num_images L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D + cond = cond.reshape(B, N, L, -1) + + def preprocess_x_for_inpaint(x): + # vae style mask + if args.use_vae_preprocessed_mask: + x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:9] + x, masked_x, mask = ae.encode(x), ae.encode(masked_x), ae.encode(mask) + else: + x, masked_x, mask = x[:, :3], x[:, 3:6], x[:, 6:7] + x, masked_x = ae.encode(x), ae.encode(masked_x) + batch_size, channels, frame, height, width = mask.shape + mask = rearrange(mask, 'b c t h w -> (b c t) 1 h w') + mask = F.interpolate(mask, size=latent_size, mode='bilinear') + mask = rearrange(mask, '(b c t) 1 h w -> b c t h w', t=frame, b=batch_size) + mask_first_frame = mask[:, :, 0:1].repeat(1, 1, ae_stride_t, 1, 1).contiguous() + mask = torch.cat([mask_first_frame, mask[:, :, 1:]], dim=2) + mask = mask.view(batch_size, ae_stride_t, latent_size_t, latent_size[0], latent_size[1]).contiguous() + + return x, masked_x, mask + + # Map input images to latent space + normalize latents + if args.use_image_num == 0: + x, masked_x, mask = preprocess_x_for_inpaint(x) # B #*C T H W -> (B C T H W) * 3 + else: + raise NotImplementedError('inpaint mode only support video input now.') + + # def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'output_video.mp4') -> None: + # from examples.rec_video import array_to_video + # x = x.detach().cpu() + # x = torch.clamp(x, -1, 1) + # x = (x + 1) / 2 + # x = x.permute(1, 2, 3, 0).numpy() + # x = (255*x).astype(np.uint8) + # array_to_video(x, fps=fps, output_file=output_file) + # return + # videos = ae.decode(x)[0] + # videos = videos.transpose(0, 1) + # custom_to_video(videos.to(torch.float32), fps=24, output_file='tmp.mp4') + # sys.exit() + + if npu_config is not None and get_sequence_parallel_state(): + x, cond, attn_mask, cond_mask, use_image_num = prepare_parallel_data(x, cond, attn_mask, cond_mask, + args.use_image_num) + for iter in range(args.train_batch_size * args.sp_size // args.train_sp_batch_size): + with accelerator.accumulate(model): + st_idx = iter * args.train_sp_batch_size + ed_idx = (iter + 1) * args.train_sp_batch_size + model_kwargs = dict(encoder_hidden_states=cond[st_idx: ed_idx], + attention_mask=attn_mask[st_idx: ed_idx], + encoder_attention_mask=cond_mask[st_idx: ed_idx], use_image_num=use_image_num) + run(x[st_idx: ed_idx], model_kwargs, prof_) + + else: + with accelerator.accumulate(model): + assert not torch.any(torch.isnan(x)), 'after vae' + x = x.to(weight_dtype) + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, + mask=mask, masked_x=masked_x) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + if accelerator.is_main_process: + if progress_info.global_step == 0: + if args.need_validation: + print("before training, we need to check the validation mode...") + log_validation( + args=args, + model=model, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ) + if train_one_step(step, data_item, prof_): + break + + if step >= 2 and torch_npu is not None and npu_config is not None: + npu_config.free_mm() + + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + if npu_config is not None and get_sequence_parallel_state(): + destroy_sequence_parallel_group() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # dataset & dataloader + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--train_fps", type=int, default=24) + parser.add_argument("--speed_factor", type=float, default=1.5) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--model_max_length", type=int, default=512) + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + parser.add_argument("--group_frame", action="store_true") + parser.add_argument("--group_resolution", action="store_true") + + # text encoder & vae & diffusion model + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.125) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument("--downsampler", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument('--enable_stable_fp32', action='store_true') + parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + + # diffusion setting + parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--noise_offset", type=float, default=0.02, help="The scale of noise offset.") + parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + + # validation & logs + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=2.5) + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--checkpointing_steps", type=int, default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument("--resume_from_checkpoint", type=str, default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument("--logging_dir", type=str, default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument("--report_to", type=str, default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + # optimizer & scheduler + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") + parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--prodigy_beta3", type=float, default=None, + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", + ) + parser.add_argument("--lr_scheduler", type=str, default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument("--allow_tf32", action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel") + parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training") + + # inpaint dataset + parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode + parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode + parser.add_argument("--default_text_ratio", type=float, default=0.1) + parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") + parser.add_argument("--need_validation", action="store_true") + parser.add_argument("--use_vae_preprocessed_mask", action="store_true") + + args = parser.parse_args() + main(args) diff --git a/scripts/accelerate_configs/deepspeed_zero2_config.yaml b/scripts/accelerate_configs/deepspeed_zero2_config.yaml index b15390113..03ce04191 100644 --- a/scripts/accelerate_configs/deepspeed_zero2_config.yaml +++ b/scripts/accelerate_configs/deepspeed_zero2_config.yaml @@ -9,5 +9,5 @@ main_process_port: 29503 main_training_function: main num_machines: 1 num_processes: 8 -gpu_ids: 0,1,2,3,4,5,6,7 +# gpu_ids: 0 use_cpu: false diff --git a/scripts/text_condition/gpu/sample_inpaint.sh b/scripts/text_condition/gpu/sample_inpaint.sh new file mode 100644 index 000000000..898acbff1 --- /dev/null +++ b/scripts/text_condition/gpu/sample_inpaint.sh @@ -0,0 +1,18 @@ +CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_inpaint.py \ + --model_path /storage/gyy/hw/Open-Sora-Plan/inpaint_125x480p_bs1_lr1e-5_snr5_noioff0.02_new_mask \ + --num_frames 125 \ + --height 480 \ + --width 640 \ + --cache_dir "../cache_dir" \ + --text_encoder_name google/mt5-xxl \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --save_img_path "./sample_inpaint_125x480p_bs1_lr1e-5_snr5_noioff0.02_new_mask" \ + --fps 24 \ + --guidance_scale 3.0 \ + --num_sampling_steps 50 \ + --enable_tiling \ + --max_sequence_length 512 \ + --sample_method PNDM \ + --model_3d \ + --validation_dir "./validation_dir" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh b/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh new file mode 100644 index 000000000..311d349a6 --- /dev/null +++ b/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh @@ -0,0 +1,73 @@ +export WANDB_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" +# export WANDB_MODE="offline" +export ENTITY="yunyangge" +export PROJECT="inpaint_125x480p_bs1_lr1e-5_snr5_noioff0.02_new_mask" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_inpaint.py \ + --model OpenSoraInpaint-ROPE-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "/storage/ongoing/new/cache_dir" \ + --dataset i2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 125 \ + --use_image_num 0 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=200 \ + --allow_tf32 \ + --model_max_length 512 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.2 \ + --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs36x8x1_125x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2/checkpoint-3500/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir=$PROJECT \ + --i2v_ratio 0.5 \ + --transition_ratio 0.4 \ + --default_text_ratio 0.5 \ + --validation_dir "validation_dir" \ + --num_sampling_steps 50 \ + --guidance_scale 3.0 \ + --seed 42 diff --git a/scripts/train_data/video_data_aesmovie_sucai_panda.txt b/scripts/train_data/video_data_aesmovie_sucai_panda.txt new file mode 100644 index 000000000..67682fdc8 --- /dev/null +++ b/scripts/train_data/video_data_aesmovie_sucai_panda.txt @@ -0,0 +1,16 @@ +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc01_250508.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc02_289778.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc03_519184.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc04_249497.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc05_416548.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/TV01_217419.json +/storage/dataset/panda70m,/storage/dataset/panda70m/panda70m_last_6268414_flowValue.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95441.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_clipchamp_final_452264.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_coverr_final_3002.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_istock_final_815070.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_kapwing_final_68473.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_mixkit_final_4490.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pexels_final_267395.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pixabay_v2_final_21608.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_storyblocks_final_1270947.json \ No newline at end of file diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt index 0cc36a78c..2796cd79d 100644 --- a/scripts/train_data/video_data_debug.txt +++ b/scripts/train_data/video_data_debug.txt @@ -1 +1 @@ -/storage/dataset/panda70m,/storage/anno_jsons/panda_100000.json \ No newline at end of file +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc01_250508.json \ No newline at end of file From e8ed550a55d102c70fc880558b0da5a7fe948265 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Wed, 10 Jul 2024 09:10:16 +0000 Subject: [PATCH 080/134] merge --- opensora/dataset/__init__.py | 8 -------- opensora/dataset/inpaint_datasets.py | 9 --------- 2 files changed, 17 deletions(-) diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 98bc8e53d..cecacbf6a 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -13,15 +13,7 @@ # npu_config = None # from .t2v_datasets import T2V_dataset from .t2v_datasets import T2V_dataset -<<<<<<< HEAD - -from .inpaint_datasets import Inpaint_dataset - -from .videoip_datasets import VideoIP_dataset - -======= from .inpaint_datasets import Inpaint_dataset ->>>>>>> new_hw_onging from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo diff --git a/opensora/dataset/inpaint_datasets.py b/opensora/dataset/inpaint_datasets.py index 365b11354..395cd5296 100644 --- a/opensora/dataset/inpaint_datasets.py +++ b/opensora/dataset/inpaint_datasets.py @@ -1,8 +1,3 @@ -<<<<<<< HEAD -import json -import os, io, csv, math, random -from turtle import width -======= from torch.utils.data import Dataset @@ -15,16 +10,12 @@ import glob import json import os, io, csv, math, random ->>>>>>> new_hw_onging import numpy as np import torchvision from einops import rearrange from decord import VideoReader from os.path import join as opj -<<<<<<< HEAD -======= from collections import Counter ->>>>>>> new_hw_onging import torch import torchvision.transforms as transforms From 4736d8fa91f20bd01c6ff1138123ae49ee62ed0b Mon Sep 17 00:00:00 2001 From: wwzhuo Date: Thu, 11 Jul 2024 16:08:52 +0800 Subject: [PATCH 081/134] inpaint on npu --- opensora/dataset/__init__.py | 4 +- opensora/models/diffusion/__init__.py | 2 - opensora/models/text_encoder/__init__.py | 4 +- opensora/sample/sample_inpaint_on_npu.py | 344 ++++++++++++++++++ opensora/train/train_inpaint.py | 20 +- .../gpu/sample_video_inpaint.sh | 16 - scripts/text_condition/npu/sample_inpaint.sh | 34 ++ .../npu/train_inpaint_video3d_nx480p.sh | 61 ++++ scripts/train_data/video_data_on_npu.txt | 17 +- 9 files changed, 470 insertions(+), 32 deletions(-) create mode 100644 opensora/sample/sample_inpaint_on_npu.py delete mode 100644 scripts/text_condition/gpu/sample_video_inpaint.sh create mode 100644 scripts/text_condition/npu/sample_inpaint.sh create mode 100644 scripts/text_condition/npu/train_inpaint_video3d_nx480p.sh diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index cecacbf6a..406a76a7b 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -118,8 +118,8 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, transform_topcrop=transform_topcrop) raise NotImplementedError(args.dataset) diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index 8416bfc47..eb1314904 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -12,8 +12,6 @@ Diffusion_models.update(UDiT_Ultra_models) Diffusion_models.update(Inpaint_models) -Diffusion_models.update(inpaint_models) - from .latte.modeling_latte import Latte_models_class from .opensora.modeling_opensora import OpenSora_models_class from .udit.modeling_udit import UDiT_models_class diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index c1669068f..80d3c30d6 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -13,8 +13,8 @@ def __init__(self, args, **kwargs): from transformers import MT5EncoderModel print(f"Loading MT5 model: {self.model_name}") print(f"cache_dir: {args.cache_dir}") - # self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel diff --git a/opensora/sample/sample_inpaint_on_npu.py b/opensora/sample/sample_inpaint_on_npu.py new file mode 100644 index 000000000..2ddb6d31b --- /dev/null +++ b/opensora/sample/sample_inpaint_on_npu.py @@ -0,0 +1,344 @@ +import math +import os +import torch +import argparse +import torchvision +import torch.distributed as dist + +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler +from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel +from omegaconf import OmegaConf +from torchvision.utils import save_image +from transformers import T5EncoderModel, MT5EncoderModel, T5Tokenizer, AutoTokenizer + +import os, sys + +from opensora.adaptor.modules import replace_with_fp32_forwards +from opensora.models.ae import ae_stride_config, getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.udit.modeling_udit import UDiTT2V +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.diffusion.udit_ultra.modeling_udit_ultra import UDiTUltraT2V + +from opensora.models.diffusion.opensora.modeling_inpaint import OpenSoraInpaint + +from opensora.sample.pipeline_opensora import OpenSoraPipeline +from opensora.sample.pipeline_inpaint import hacked_pipeline_call_for_inpaint + +from opensora.models.text_encoder import get_text_enc +from opensora.utils.utils import save_video_grid + +from opensora.sample.pipeline_opensora import OpenSoraPipeline + +import imageio + +try: + import torch_npu +except: + pass +import time +from opensora.npu_config import npu_config +# for validation +import glob +from PIL import Image +from torchvision import transforms +from torchvision.transforms import Lambda +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +import numpy as np +from einops import rearrange + +import imageio +import glob +import gc +import time + +def load_t2v_checkpoint(model_path): + if args.model_3d: + transformer_model = OpenSoraInpaint.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) + elif args.udit: + transformer_model = UDiTUltraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) + else: + transformer_model = LatteT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, + device_map=None, torch_dtype=weight_dtype) + print(transformer_model.config) + + # set eval mode + transformer_model.eval() + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model).to(device) + + pipeline.__call__ = hacked_pipeline_call_for_inpaint.__get__(pipeline, OpenSoraPipeline) + + return pipeline + + +def get_latest_path(): + # Get the most recent checkpoint + dirs = os.listdir(args.model_path) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + return path + +def preprocess_images(images, transform): + if len(images) == 1: + condition_images_indices = [0] + elif len(images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in images] + condition_images = [torch.from_numpy(np.copy(np.array(image))) for image in condition_images] + condition_images = [rearrange(image, 'h w c -> c h w').unsqueeze(0) for image in condition_images] + condition_images = [transform(image).to(device, dtype=weight_dtype) for image in condition_images] + return dict(condition_images=condition_images, condition_images_indices=condition_images_indices) + + +def run_model_and_save_images(pipeline, model_path): + + positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" + negative_prompt = """nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, + """ + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break + + video_grids = [] + if not isinstance(args.text_prompt, list): + args.text_prompt = [args.text_prompt] + if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): + text_prompt = open(args.text_prompt[0], 'r').readlines() + args.text_prompt = [i.strip() for i in text_prompt] + + checkpoint_name = f"{os.path.basename(model_path)}" + + positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" + negative_prompt = "nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry" + + for idx, (prompt, images) in enumerate(zip(args.text_prompt, validation_images_list)): + if not isinstance(images, list): + images = [images] + if 'img' in images[0]: + continue + if idx % npu_config.N_NPU_PER_NODE != local_rank: + continue + + pre_results = preprocess_images(images, transform) + condition_images = pre_results['condition_images'] + condition_images_indices = pre_results['condition_images_indices'] + + print('Processing the ({}) prompt and ({}) images'.format(prompt, images)) + videos = pipeline.__call__( + prompt=prompt, + negative_prompt=negative_prompt, + condition_images=condition_images, + condition_images_indices=condition_images_indices, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + enable_temporal_attentions=True, + num_images_per_prompt=1, + mask_feature=True, + max_sequence_length=args.max_sequence_length, + ).images + print(videos.shape) + try: + imageio.mimwrite( + os.path.join( + args.save_img_path, + f'{args.sample_method}_{idx}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' + ), videos[0], + fps=args.fps, quality=9, codec='libx264', + output_params=['-threads', '20']) # highest quality is 10, lowest is 0 + except: + print('Error when saving {}'.format(prompt)) + video_grids.append(videos) + + video_grids = torch.cat(video_grids, dim=0).cuda() + shape = list(video_grids.shape) + shape[0] *= world_size + gathered_tensor = torch.zeros(shape, dtype=video_grids.dtype, device=device) + dist.all_gather_into_tensor(gathered_tensor, video_grids.contiguous()) + video_grids = gathered_tensor.cpu() + + # video_grids = video_grids.repeat(world_size, 1, 1, 1) + # output = torch.zeros(video_grids.shape, dtype=video_grids.dtype, device=device) + # dist.all_to_all_single(output, video_grids) + # video_grids = output.cpu() + def get_file_name(): + return os.path.join(args.save_img_path, + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') + + if args.num_frames == 1: + save_image(video_grids / 255.0, get_file_name(), + nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) + else: + video_grids = save_video_grid(video_grids) + imageio.mimwrite(get_file_name(), video_grids, fps=args.fps, quality=9) + + print('save path {}'.format(args.save_img_path)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') + parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") + parser.add_argument("--guidance_scale", type=float, default=7.5) + parser.add_argument("--sample_method", type=str, default="PNDM") + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument("--fps", type=int, default=24) + parser.add_argument("--run_time", type=int, default=0) + parser.add_argument("--text_prompt", nargs='+') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--model_3d', action='store_true') + parser.add_argument('--udit', action='store_true') + + parser.add_argument("--max_sequence_length", type=int, default=512) + parser.add_argument("--validation_dir", type=str, default=None) + args = parser.parse_args() + + npu_config.print_msg(args) + npu_config.conv_dtype = torch.bfloat16 + replace_with_fp32_forwards() + + # 初始化分布式环境 + local_rank = int(os.getenv('RANK', 0)) + world_size = int(os.getenv('WORLD_SIZE', 1)) + if npu_config.on_npu: + torch_npu.npu.set_device(local_rank) + dist.init_process_group(backend='hccl', init_method='env://', world_size=8, rank=local_rank) + + # torch.manual_seed(args.seed) + weight_dtype = torch.float32 + device = torch.cuda.current_device() + + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae_scale_factor = ae_stride_config[args.ae] + + text_encoder = MT5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, + low_cpu_mem_usage=True, torch_dtype=torch.float16).to(device) + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # set eval mode + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler() + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler() + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + + resize = [CenterCropResizeVideo((args.height, args.width)),] + norm_fun = Lambda(lambda x: 2. * x - 1.) + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + + if not os.path.exists(args.save_img_path): + os.makedirs(args.save_img_path, exist_ok=True) + + if args.num_frames == 1: + video_length = 1 + ext = 'jpg' + else: + ext = 'mp4' + + latest_path = None + save_img_path = args.save_img_path + first_in = False + while True: + cur_path = get_latest_path() + if cur_path == latest_path: + time.sleep(60) + continue + + if not first_in: + first_in = True + else: + time.sleep(60) + + latest_path = cur_path + + npu_config.print_msg(f"The latest_path is {latest_path}") + full_path = f"{args.model_path}/{latest_path}/model_ema" + # full_path = "/home/opensora/captions/240p_model_ema" + pipeline = load_t2v_checkpoint(full_path) + + if npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = "/home/image_data/npu_profiling_t2v" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=10000, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + run_model_and_save_images(pipeline, latest_path) + prof.step() + else: + run_model_and_save_images(pipeline, latest_path) diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py index 0df8c259d..533904398 100644 --- a/opensora/train/train_inpaint.py +++ b/opensora/train/train_inpaint.py @@ -520,7 +520,8 @@ def load_model_hook(models, input_dir): # We need to initialize the trackers we use, and also store our configuration. # The trackers initializes automatically on the main process. if accelerator.is_main_process: - accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + # accelerator.init_trackers(os.path.basename(args.output_dir), config=vars(args)) + accelerator.init_trackers(project_name=os.getenv("PROJECT"), config=vars(args)) # Train! total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps @@ -626,6 +627,12 @@ def run(model_input, model_kwargs, prof): global start_time start_time = time.time() + try: + in_channels = ae_channel_config[args.ae] + model_input, masked_x, video_mask = model_input[:, 0:in_channels], model_input[:, in_channels:2 * in_channels], model_input[:, 2 * in_channels:] + except: + raise ValueError("masked_x and video_mask is None!") + noise = torch.randn_like(model_input) if args.noise_offset: # https://www.crosslabs.org//blog/diffusion-with-offset-noise @@ -643,11 +650,6 @@ def run(model_input, model_kwargs, prof): noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) - if model_kwargs.get("masked_x", None) is not None and model_kwargs.get("mask", None) is not None: - masked_x = model_kwargs.pop("masked_x") - video_mask = model_kwargs.pop("mask") - else: - raise ValueError("masked_x and mask must be provided in model_kwargs") model_pred = model( torch.cat([noisy_model_input, masked_x, video_mask], dim=1), @@ -806,7 +808,8 @@ def preprocess_x_for_inpaint(x): # Map input images to latent space + normalize latents if args.use_image_num == 0: - x, masked_x, mask = preprocess_x_for_inpaint(x) # B #*C T H W -> (B C T H W) * 3 + x, masked_x, mask = preprocess_x_for_inpaint(x) # B 3*C T H W -> (B C T H W) * 3 + x = torch.cat([x, masked_x, mask], dim=1) # (B C T H W) * 3 -> B 3*C T H W else: raise NotImplementedError('inpaint mode only support video input now.') @@ -841,8 +844,7 @@ def preprocess_x_for_inpaint(x): assert not torch.any(torch.isnan(x)), 'after vae' x = x.to(weight_dtype) model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, - mask=mask, masked_x=masked_x) + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) run(x, model_kwargs, prof_) if progress_info.global_step >= args.max_train_steps: diff --git a/scripts/text_condition/gpu/sample_video_inpaint.sh b/scripts/text_condition/gpu/sample_video_inpaint.sh deleted file mode 100644 index 75a3b7409..000000000 --- a/scripts/text_condition/gpu/sample_video_inpaint.sh +++ /dev/null @@ -1,16 +0,0 @@ -CUDA_VISIBLE_DEVICES=0 python opensora/sample/sample_inpaint.py \ - --model_path "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ - --num_frames 65 \ - --height 512 \ - --width 512 \ - --cache_dir "/storage/cache_dir" \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --text_prompt examples/prompt_list_0.txt \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8" \ - --validation_dir "/storage/gyy/hw/Open-Sora-Plan/test_dir" \ - --output_dir "./test_sample" \ - --guidance_scale 7.5 \ - --num_sampling_steps 50 \ - --enable_tiling \ - --sample_method PNDM \ \ No newline at end of file diff --git a/scripts/text_condition/npu/sample_inpaint.sh b/scripts/text_condition/npu/sample_inpaint.sh new file mode 100644 index 000000000..005be4739 --- /dev/null +++ b/scripts/text_condition/npu/sample_inpaint.sh @@ -0,0 +1,34 @@ +WEIGHT_PATH="/home/opensora/pre_weights/" + +export PYTHONPATH="${PYTHONPATH:+$PYTHONPATH:}$(pwd)" +export MASTER_PORT=12359 + +if [ -z "$SAMPLE_SAVE_PATH" ]; then + export SAMPLE_SAVE_PATH="/home/image_data/sample_videos" +fi + +if [ -z "$SAMPLE_HEIGHT" ]; then + echo "You should set both envs of SAMPLE_HEIGHT and SAMPLE_WIDTH" + return +fi + +torchrun --nproc_per_node=8 opensora/sample/sample_inpaint_on_npu.py \ + --model_path /home/image_data/checkpoints/${PROJECT_NAME} \ + --num_frames ${NUM_FRAME} \ + --height $SAMPLE_HEIGHT \ + --width $SAMPLE_WIDTH \ + --cache_dir "../cache_dir" \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ + --text_prompt "/home/image_data/checkpoints/i2v_validation_dir/prompt.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --save_img_path "${SAMPLE_SAVE_PATH}/${PROJECT_NAME}" \ + --fps 24 \ + --max_sequence_length 512 \ + --guidance_scale 5.0 \ + --num_sampling_steps 50 \ + --sample_method PNDM \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --model_3d \ + --validation_dir "/home/image_data/checkpoints/i2v_validation_dir" \ No newline at end of file diff --git a/scripts/text_condition/npu/train_inpaint_video3d_nx480p.sh b/scripts/text_condition/npu/train_inpaint_video3d_nx480p.sh new file mode 100644 index 000000000..b7f751312 --- /dev/null +++ b/scripts/text_condition/npu/train_inpaint_video3d_nx480p.sh @@ -0,0 +1,61 @@ +export WANDB_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" +# # export WANDB_MODE="offline" +export ENTITY="yunyangge" +WEIGHT_PATH="/home/opensora/pre_weights/" +env +# export WANDB_MODE='offline' +export HCCL_OP_BASE_FFTS_MODE_ENABLE=TRUE +export HCCL_ALGO="level0:NA;level1:H-D_R" + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example_by_deepspeed.yaml \ + --machine_rank=${MACHINE_RANK} \ + --main_process_ip=${MAIN_PROCESS_IP_VALUE} \ + opensora/train/train_inpaint.py \ + --model OpenSoraInpaint-ROPE-L/122 \ + --text_encoder_name ${WEIGHT_PATH}/google/mt5-xxl \ + --cache_dir "../cache_dir" \ + --dataset i2v \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "${WEIGHT_PATH}/test140k/" \ + --video_data "./scripts/train_data/video_data_on_npu.txt" \ + --image_data "./scripts/train_data/image_data_on_npu.txt" \ + --sample_rate 1 \ + --num_frames ${NUM_FRAME} \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 8 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-5 \ + --lr_scheduler="constant" \ + --seed=42 \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=200 \ + --output_dir="/home/image_data/checkpoints/${PROJECT}/" \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --use_rope \ + --noise_offset 0.02 \ + --pretrained "/home/image_data/checkpoints/gyy_pretrained_f125/model_ema/diffusion_pytorch_model.safetensors" \ + --resume_from_checkpoint="latest" \ + --i2v_ratio 0.5 \ + --transition_ratio 0.4 \ + --default_text_ratio 0.5 \ + --group_frame \ + --speed_factor 1.1 \ diff --git a/scripts/train_data/video_data_on_npu.txt b/scripts/train_data/video_data_on_npu.txt index b29eada06..27729dc0e 100644 --- a/scripts/train_data/video_data_on_npu.txt +++ b/scripts/train_data/video_data_on_npu.txt @@ -1 +1,16 @@ -/home/local_dataset_n20/video_obs/panda70m/,/home/opensora/captions/linbin_captions/step2_merge_downloaded_videos_v2.json \ No newline at end of file +/home/local_dataset_6t/video_data_obs/panda_70m,/home/opensora/captions/panda70m_last_6268414_flowValue.json +/home/local_dataset_6t/video_data_obs/movie_aes_motion,/home/opensora/captions/movie_aes_motion/bbc01_clips_aes_motion_final_250508.json +/home/local_dataset_6t/video_data_obs/movie_aes_motion,/home/opensora/captions/movie_aes_motion/bbc02_clips_aes_motion_final_289778.json +/home/local_dataset_6t/video_data_obs/movie_aes_motion,/home/opensora/captions/movie_aes_motion/bbc03_clips_aes_motion_final_519184.json +/home/local_dataset_6t/video_data_obs/movie_aes_motion,/home/opensora/captions/movie_aes_motion/bbc04_clips_aes_motion_final_249497.json +/home/local_dataset_6t/video_data_obs/movie_aes_motion,/home/opensora/captions/movie_aes_motion/bbc05_clips_aes_motion_final_416548.json +/home/local_dataset_6t/video_data_obs/movie_aes_motion,/home/opensora/captions/movie_aes_motion/TV01_clips_aes_motion_final_217419.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_kapwing_final_68473.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_coverr_final_3002.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_canva_final_95441.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_istock_final_612322_huawei.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_storyblocks_final_1270947.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_clipchamp_final_452264.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_mixkit_final_4490.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_pexels_final_267395.json +/home/local_dataset_6t/video_data_obs/Final_format_dataset_data_v2,/home/opensora/captions/Final_format_dataset_data_v2/step1.5_pixabay_v2_final_21608.json \ No newline at end of file From 75ab2a81619db9615b432f1bfe25afeedb2108c4 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Thu, 11 Jul 2024 12:56:48 +0000 Subject: [PATCH 082/134] pull npu code --- opensora/dataset/__init__.py | 32 ++ opensora/dataset/videoip_datasets.py | 379 +---------------- opensora/dataset/videoip_datasets_bak.py | 396 ++++++++++++++++++ opensora/models/diffusion/__init__.py | 1 - opensora/sample/sample_inpaint.py | 2 +- opensora/train/train_inpaint.py | 3 +- scripts/accelerate_configs/hostfile | 22 +- .../multi_node_example.yaml | 6 +- .../gpu/train_inpaint_video3d_125x480p.sh | 5 +- .../gpu/train_video_ip_video21d.sh | 2 + 10 files changed, 458 insertions(+), 390 deletions(-) create mode 100644 opensora/dataset/videoip_datasets_bak.py diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index cecacbf6a..a48c46340 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -14,6 +14,7 @@ # from .t2v_datasets import T2V_dataset from .t2v_datasets import T2V_dataset from .inpaint_datasets import Inpaint_dataset +from .videoip_datasets_bak import VideoIP_dataset from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo @@ -122,4 +123,35 @@ def getdataset(args): tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, transform_topcrop=transform_topcrop) + elif args.dataset == 'vip': + resize_topcrop = [CenterCropResizeVideo((args.max_height, args.max_width), top_crop=True), ] + # if args.multi_scale: + # resize = [ + # LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), + # SpatialStrideCropVideo(args.stride) + # ] + # else: + resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] + transform = transforms.Compose([ + ToTensorVideo(), + *resize, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + transform_topcrop = transforms.Compose([ + ToTensorVideo(), + *resize_topcrop, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + transform_topcrop = transforms.Compose([ + ToTensorVideo(), + *resize_topcrop, + # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription + norm_fun + ]) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, + transform_topcrop=transform_topcrop) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/videoip_datasets.py b/opensora/dataset/videoip_datasets.py index aabf14060..ab53d6611 100644 --- a/opensora/dataset/videoip_datasets.py +++ b/opensora/dataset/videoip_datasets.py @@ -1,7 +1,12 @@ -import time -import traceback +from torch.utils.data import Dataset +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None import glob import json import os, io, csv, math, random @@ -10,6 +15,7 @@ from einops import rearrange from decord import VideoReader from os.path import join as opj +from collections import Counter import torch import torchvision.transforms as transforms @@ -17,76 +23,23 @@ from torch.utils.data import DataLoader, Dataset, get_worker_info from tqdm import tqdm from PIL import Image +from accelerate.logging import get_logger from opensora.utils.dataset_utils import DecordInit from opensora.utils.utils import text_preprocessing +from .t2v_datasets import filter_json_by_existed_files, random_video_noise, find_closest_y, filter_resolution +from .t2v_datasets import SingletonMeta, DataSetProg +from .t2v_datasets import T2V_dataset -def filter_json_by_existed_files(directory, data, postfix=".mp4"): - # 构建搜索模式,以匹配指定后缀的文件 - pattern = os.path.join(directory, '**', f'*{postfix}') - mp4_files = glob.glob(pattern, recursive=True) # 使用glob查找所有匹配的文件 +logger = get_logger(__name__) - # 使用文件的绝对路径构建集合 - mp4_files_set = set(os.path.abspath(path) for path in mp4_files) - # 过滤数据条目,只保留路径在mp4文件集合中的条目 - filtered_items = [item for item in data if item['path'] in mp4_files_set] +dataset_prog = DataSetProg() - return filtered_items - - -def random_video_noise(t, c, h, w): - vid = torch.rand(t, c, h, w) * 255.0 - vid = vid.to(torch.uint8) - return vid - -def save_video(video, save_path='output_video.mp4', fps=24): - import cv2 - - frame_count, height, width, channels = video.shape - - fourcc = cv2.VideoWriter_fourcc(*'mp4v') - out = cv2.VideoWriter(save_path, fourcc, fps, (width, height)) - - for i in range(frame_count): - frame = video[i].cpu().numpy() - frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) - out.write(frame) - -# num_frames == 1: only image -# num_frames > 1 and use_image_num == 0: only video -# num_frames > 1 and use_image_num > 0: video and image -class VideoIP_dataset(Dataset): - def __init__(self, args, transform, resize_transform, temporal_sample, tokenizer, image_processor): - self.image_data = args.image_data - self.video_data = args.video_data - self.num_frames = args.num_frames - self.use_image_num = args.use_image_num - self.use_img_from_vid = args.use_img_from_vid - self.transform = transform - self.temporal_sample = temporal_sample - self.tokenizer = tokenizer - self.model_max_length = args.model_max_length - self.cfg = args.cfg - self.v_decoder = DecordInit() - - self.resize_transform = resize_transform - self.image_processor = image_processor - - self.support_Chinese = True - if not ('mt5' in args.text_encoder_name): - self.support_Chinese = False - - if self.num_frames != 1: - self.vid_cap_list = self.get_vid_cap_list() - if self.use_image_num != 0 and not self.use_img_from_vid: - self.img_cap_list = self.get_img_cap_list() - else: - self.img_cap_list = [] - else: - self.img_cap_list = self.get_img_cap_list() - self.vid_cap_list = [] +class VideoIP_dataset(T2V_dataset): + def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): + super().__init__(args, transform, temporal_sample, tokenizer, transform_topcrop) if self.num_frames != 1: # inpaint @@ -96,301 +49,3 @@ def __init__(self, args, transform, resize_transform, temporal_sample, tokenizer self.clear_video_ratio = args.clear_video_ratio self.default_text_ratio = args.default_text_ratio assert self.i2v_ratio + self.transition_ratio + self.clear_video_ratio < 1, 'The sum of i2v_ratio, transition_ratio and clear video ratio should be less than 1.' - - print(f"video length: {len(self.vid_cap_list)}") - print(f"image length: {len(self.img_cap_list)}") - - def set_checkpoint(self, n_used_elements): - self.n_used_elements = n_used_elements - - def __len__(self): - if self.num_frames != 1: - return len(self.vid_cap_list) - else: - return len(self.img_cap_list) - - def __getitem__(self, idx): - - video_data, image_data = {}, {} - try: - if self.num_frames != 1: - video_data = self.get_video(idx) - if self.use_image_num != 0: - if self.use_img_from_vid: - image_data = self.get_image_from_video(video_data) - else: - image_data = self.get_image(idx) - else: - image_data = self.get_image(idx) # 1 frame video as image - return dict(video_data=video_data, image_data=image_data) - except Exception as e: - # print(f'Error with {e}') - # 打印异常堆栈 - if idx in self.vid_cap_list: - print(f"Caught an exception! {self.vid_cap_list[idx]}") - # traceback.print_exc() - # traceback.print_stack() - return self.__getitem__(random.randint(0, self.__len__() - 1)) - - def get_video(self, idx): - - video_path = self.vid_cap_list[idx]['path'] - assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" - frame_idx = self.vid_cap_list[idx].get('frame_idx', None) - video = self.decord_read(video_path, frame_idx) - # resize - video = self.resize_transform(video.float()).to(torch.uint8) - - inpaint_cond_data = self.get_mask_masked_video(video) - masked_video = inpaint_cond_data['masked_video'] - - clip_video = self.image_processor(images=masked_video, return_tensors="pt").pixel_values # T C H W - - video = self.transform(video) # T C H W -> T C H W - video = video.transpose(0, 1) # T C H W -> C T H W - - text = self.vid_cap_list[idx]['cap'] - text = text_preprocessing(text, support_Chinese=self.support_Chinese) - - drop_results = self.drop(text, clip_video) - text = drop_results['text'] - clip_video = drop_results['clip_image'] - - text_tokens_and_mask = self.tokenizer( - text, - max_length=self.model_max_length, - padding='max_length', - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors='pt' - ) - input_ids = text_tokens_and_mask['input_ids'] - cond_mask = text_tokens_and_mask['attention_mask'] - - # video (C T H W) input_ids (1 N) cond_mask (1 N) clip_video (T C H W) - return dict(video=video, input_ids=input_ids, cond_mask=cond_mask, clip_video=clip_video) - - def get_image_from_video(self, video_data): - select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) - assert self.num_frames >= self.use_image_num - image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] - clip_image = [video_data['clip_video'][i:i + 1] for i in select_image_idx] # num_img [1, c, h, w] - input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l - cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l - return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) - - def drop(self, text, clip_image): - rand_num = random.random() - rand_num_text = random.random() - - if rand_num < self.cfg: - text = 'The video showcases a scene with coherent and clear visuals.' if rand_num_text < self.default_text_ratio else '' - elif rand_num < self.cfg * 2: - clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) - elif rand_num < self.cfg * 3: - text = 'The video showcases a scene with coherent and clear visuals.' if rand_num_text < self.default_text_ratio else '' - clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) - - return dict(text=text, clip_image=clip_image) - - def get_mask_masked_video(self, video): - # video shape (T, C, H, W) - mask = torch.zeros_like(video) - - rand_num = random.random() - # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. - if rand_num < self.i2v_ratio: - mask = 1 - mask - mask[0] = 0 - elif rand_num < self.i2v_ratio + self.transition_ratio: - mask = 1 - mask - mask[0] = 0 - mask[-1] = 0 - elif rand_num < self.i2v_ratio + self.transition_ratio + self.clear_video_ratio: - pass - else: - idx_to_select = random.randint(1, self.num_frames - 1) - selected_indices = random.sample(range(1, self.num_frames), idx_to_select) - mask[selected_indices] = 1 - - masked_video = video * (mask < 0.5) - return dict(mask=mask, masked_video=masked_video) - - def get_image(self, idx): - idx = idx % len(self.img_cap_list) # out of range - image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - - image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] - image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] - - for i in image: - assert not torch.any(torch.isnan(i)), 'before transform0' - image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] - for i in image: - assert not torch.any(torch.isnan(i)), 'before resize_transform' - image = [self.resize_transform(i.float()).to(torch.uint8) for i in image] # num_img [1 C H W] -> num_img [1 C H W] - for i in image: - assert not torch.any(torch.isnan(i)), 'after resize transform' - - clip_image_list = [self.image_processor(images=i, return_tensors="pt").pixel_values for i in image] # num_img [1 C H W] -> num_img [1 C H W] - - image = [self.transform(i).transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [1 C H W] -> num_img [C 1 H W] - - caps = [i['cap'] for i in image_data] - text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] - - input_ids, cond_mask, clip_image = [], [], [] - for t, clip_i in zip(text, clip_image_list): - drop_results = self.drop(t, clip_i) - t = drop_results['text'] - clip_i = drop_results['clip_image'] - text_tokens_and_mask = self.tokenizer( - t, - max_length=self.model_max_length, - padding='max_length', - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors='pt' - ) - input_ids.append(text_tokens_and_mask['input_ids']) - cond_mask.append(text_tokens_and_mask['attention_mask']) - clip_image.append(clip_i) - input_ids = torch.cat(input_ids) # self.use_image_num, l - cond_mask = torch.cat(cond_mask) # self.use_image_num, l - clip_image = torch.cat(clip_image) # self.use_image_num, C, H, W - return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) - - def decord_read(self, path, frame_idx=None): - decord_vr = self.v_decoder(path) - total_frames = len(decord_vr) - # Sampling video frames - if frame_idx is None: - start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) - else: - start_frame_ind, end_frame_ind = frame_idx.split(':') - # start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) - start_frame_ind, end_frame_ind = int(start_frame_ind), int(start_frame_ind) + self.num_frames - # assert end_frame_ind - start_frame_ind >= self.num_frames - frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) - # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) - - video_data = decord_vr.get_batch(frame_indice).asnumpy() - video_data = torch.from_numpy(video_data) - video_data = video_data.permute(0, 3, 1, 2) # (T H W C) -> (T C H W) - return video_data - - def read_jsons(self, data, postfix=".jpg"): - cap_lists = [] - with open(data, 'r') as f: - folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] - for folder, anno in folder_anno: - with open(anno, 'r') as f: - sub_list = json.load(f) - print(f'Building {anno}...') - for i in tqdm(range(len(sub_list))): - sub_list[i]['path'] = opj(folder, sub_list[i]['path']) - cap_lists += sub_list - return cap_lists - - def get_img_cap_list(self): - use_image_num = self.use_image_num if self.use_image_num != 0 else 1 - img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") - img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] - - return img_cap_lists[:-1] # drop last to avoid error length - - def get_vid_cap_list(self): - vid_cap_lists = [] - with open(self.video_data, 'r') as f: - folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] - for folder, anno in folder_anno: - new_vid_cap_list = [] - with open(anno, 'r') as f: - vid_cap_list = json.load(f) - print(f'Building {anno}...') - for i in tqdm(range(len(vid_cap_list))): - path = opj(folder, vid_cap_list[i]['path']) - # For testing, some data has not been utilized. - if not os.path.exists(path) and not os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): - print(path) - continue - elif os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): - path = path.replace('.mp4', '_resize1080p.mp4') - new_vid_cap_list.append( - { - 'path': path, - 'frame_idx': vid_cap_list[i]['frame_idx'], - 'cap': vid_cap_list[i]['cap'] - } - ) - - vid_cap_lists += new_vid_cap_list - return vid_cap_lists - - -if __name__ == "__main__": - - from torchvision import transforms - from torchvision.transforms import Lambda - from .transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop - - from transformers import AutoTokenizer, AutoImageProcessor, CLIPImageProcessor - - class Args: - def __init__(self): - # self.video_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' - # self.image_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' - self.video_data = '/storage/gyy/hw/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' - self.image_data = '/storage/gyy/hw/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' - self.num_frames = 65 - self.use_image_num = 4 - self.use_img_from_vid = False - self.model_max_length = 300 - self.cfg = 0.1 - self.default_text_ratio = 0.5 - self.i2v_ratio = 0.3 - self.transition_ratio = 0.3 - self.clear_video_ratio = 0.3 - self.max_image_size = 512 - self.sample_rate = 1 - self.text_encoder_name = "DeepFloyd/t5-v1_1-xxl" - # self.image_encoder_name = "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" - self.image_encoder_name = "facebook/dinov2-giant" - self.cache_dir = "/storage/cache_dir" - - args = Args() - resize = [CenterCropResizeVideo((args.max_image_size, args.max_image_size))] - - temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x - tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - image_processor = AutoImageProcessor.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) - - resize_transform = transforms.Compose([ - *resize, - ]) - - transform = transforms.Compose([ - ToTensorVideo(), - Lambda(lambda x: 2. * x - 1.) - ]) - - dataset = VideoIP_dataset(args, resize_transform=resize_transform, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, image_processor=image_processor) - - print(len(dataset)) - video_data = dataset[0]['video_data'] - image_data = dataset[0]['image_data'] - video, video_input_ids, video_cond_mask, clip_video = video_data['video'], video_data['input_ids'], video_data['cond_mask'], video_data['clip_video'] - image, image_input_ids, image_cond_mask, clip_image = image_data['image'], image_data['input_ids'], image_data['cond_mask'], image_data['clip_image'] - print(video.shape) # C, F, H, W - print(video_input_ids.shape) # 1 D - print(video_cond_mask.shape) # 1 D - print(clip_video.shape) # T, C, H, W - print(clip_image.shape) # num_images, C, H, W - print(image[0].shape) - print(image_input_ids.shape) - print(image_cond_mask.shape) - print(video_cond_mask) - print(image_cond_mask) - diff --git a/opensora/dataset/videoip_datasets_bak.py b/opensora/dataset/videoip_datasets_bak.py new file mode 100644 index 000000000..aabf14060 --- /dev/null +++ b/opensora/dataset/videoip_datasets_bak.py @@ -0,0 +1,396 @@ +import time +import traceback + + +import glob +import json +import os, io, csv, math, random +import numpy as np +import torchvision +from einops import rearrange +from decord import VideoReader +from os.path import join as opj + +import torch +import torchvision.transforms as transforms +from torch.utils.data.dataset import Dataset +from torch.utils.data import DataLoader, Dataset, get_worker_info +from tqdm import tqdm +from PIL import Image + +from opensora.utils.dataset_utils import DecordInit +from opensora.utils.utils import text_preprocessing + + +def filter_json_by_existed_files(directory, data, postfix=".mp4"): + # 构建搜索模式,以匹配指定后缀的文件 + pattern = os.path.join(directory, '**', f'*{postfix}') + mp4_files = glob.glob(pattern, recursive=True) # 使用glob查找所有匹配的文件 + + # 使用文件的绝对路径构建集合 + mp4_files_set = set(os.path.abspath(path) for path in mp4_files) + + # 过滤数据条目,只保留路径在mp4文件集合中的条目 + filtered_items = [item for item in data if item['path'] in mp4_files_set] + + return filtered_items + + +def random_video_noise(t, c, h, w): + vid = torch.rand(t, c, h, w) * 255.0 + vid = vid.to(torch.uint8) + return vid + +def save_video(video, save_path='output_video.mp4', fps=24): + import cv2 + + frame_count, height, width, channels = video.shape + + fourcc = cv2.VideoWriter_fourcc(*'mp4v') + out = cv2.VideoWriter(save_path, fourcc, fps, (width, height)) + + for i in range(frame_count): + frame = video[i].cpu().numpy() + frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR) + out.write(frame) + +# num_frames == 1: only image +# num_frames > 1 and use_image_num == 0: only video +# num_frames > 1 and use_image_num > 0: video and image +class VideoIP_dataset(Dataset): + def __init__(self, args, transform, resize_transform, temporal_sample, tokenizer, image_processor): + self.image_data = args.image_data + self.video_data = args.video_data + self.num_frames = args.num_frames + self.use_image_num = args.use_image_num + self.use_img_from_vid = args.use_img_from_vid + self.transform = transform + self.temporal_sample = temporal_sample + self.tokenizer = tokenizer + self.model_max_length = args.model_max_length + self.cfg = args.cfg + self.v_decoder = DecordInit() + + self.resize_transform = resize_transform + self.image_processor = image_processor + + self.support_Chinese = True + if not ('mt5' in args.text_encoder_name): + self.support_Chinese = False + + if self.num_frames != 1: + self.vid_cap_list = self.get_vid_cap_list() + if self.use_image_num != 0 and not self.use_img_from_vid: + self.img_cap_list = self.get_img_cap_list() + else: + self.img_cap_list = [] + else: + self.img_cap_list = self.get_img_cap_list() + self.vid_cap_list = [] + + if self.num_frames != 1: + # inpaint + # The proportion of executing the i2v task. + self.i2v_ratio = args.i2v_ratio + self.transition_ratio = args.transition_ratio + self.clear_video_ratio = args.clear_video_ratio + self.default_text_ratio = args.default_text_ratio + assert self.i2v_ratio + self.transition_ratio + self.clear_video_ratio < 1, 'The sum of i2v_ratio, transition_ratio and clear video ratio should be less than 1.' + + print(f"video length: {len(self.vid_cap_list)}") + print(f"image length: {len(self.img_cap_list)}") + + def set_checkpoint(self, n_used_elements): + self.n_used_elements = n_used_elements + + def __len__(self): + if self.num_frames != 1: + return len(self.vid_cap_list) + else: + return len(self.img_cap_list) + + def __getitem__(self, idx): + + video_data, image_data = {}, {} + try: + if self.num_frames != 1: + video_data = self.get_video(idx) + if self.use_image_num != 0: + if self.use_img_from_vid: + image_data = self.get_image_from_video(video_data) + else: + image_data = self.get_image(idx) + else: + image_data = self.get_image(idx) # 1 frame video as image + return dict(video_data=video_data, image_data=image_data) + except Exception as e: + # print(f'Error with {e}') + # 打印异常堆栈 + if idx in self.vid_cap_list: + print(f"Caught an exception! {self.vid_cap_list[idx]}") + # traceback.print_exc() + # traceback.print_stack() + return self.__getitem__(random.randint(0, self.__len__() - 1)) + + def get_video(self, idx): + + video_path = self.vid_cap_list[idx]['path'] + assert os.path.exists(video_path) and os.path.getsize(video_path) > 10240, f"file {video_path} has wrong size!" + frame_idx = self.vid_cap_list[idx].get('frame_idx', None) + video = self.decord_read(video_path, frame_idx) + # resize + video = self.resize_transform(video.float()).to(torch.uint8) + + inpaint_cond_data = self.get_mask_masked_video(video) + masked_video = inpaint_cond_data['masked_video'] + + clip_video = self.image_processor(images=masked_video, return_tensors="pt").pixel_values # T C H W + + video = self.transform(video) # T C H W -> T C H W + video = video.transpose(0, 1) # T C H W -> C T H W + + text = self.vid_cap_list[idx]['cap'] + text = text_preprocessing(text, support_Chinese=self.support_Chinese) + + drop_results = self.drop(text, clip_video) + text = drop_results['text'] + clip_video = drop_results['clip_image'] + + text_tokens_and_mask = self.tokenizer( + text, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids = text_tokens_and_mask['input_ids'] + cond_mask = text_tokens_and_mask['attention_mask'] + + # video (C T H W) input_ids (1 N) cond_mask (1 N) clip_video (T C H W) + return dict(video=video, input_ids=input_ids, cond_mask=cond_mask, clip_video=clip_video) + + def get_image_from_video(self, video_data): + select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) + assert self.num_frames >= self.use_image_num + image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] + clip_image = [video_data['clip_video'][i:i + 1] for i in select_image_idx] # num_img [1, c, h, w] + input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l + cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) + + def drop(self, text, clip_image): + rand_num = random.random() + rand_num_text = random.random() + + if rand_num < self.cfg: + text = 'The video showcases a scene with coherent and clear visuals.' if rand_num_text < self.default_text_ratio else '' + elif rand_num < self.cfg * 2: + clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) + elif rand_num < self.cfg * 3: + text = 'The video showcases a scene with coherent and clear visuals.' if rand_num_text < self.default_text_ratio else '' + clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) + + return dict(text=text, clip_image=clip_image) + + def get_mask_masked_video(self, video): + # video shape (T, C, H, W) + mask = torch.zeros_like(video) + + rand_num = random.random() + # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. + if rand_num < self.i2v_ratio: + mask = 1 - mask + mask[0] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio: + mask = 1 - mask + mask[0] = 0 + mask[-1] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio + self.clear_video_ratio: + pass + else: + idx_to_select = random.randint(1, self.num_frames - 1) + selected_indices = random.sample(range(1, self.num_frames), idx_to_select) + mask[selected_indices] = 1 + + masked_video = video * (mask < 0.5) + return dict(mask=mask, masked_video=masked_video) + + def get_image(self, idx): + idx = idx % len(self.img_cap_list) # out of range + image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] + + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + + for i in image: + assert not torch.any(torch.isnan(i)), 'before transform0' + image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + for i in image: + assert not torch.any(torch.isnan(i)), 'before resize_transform' + image = [self.resize_transform(i.float()).to(torch.uint8) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + for i in image: + assert not torch.any(torch.isnan(i)), 'after resize transform' + + clip_image_list = [self.image_processor(images=i, return_tensors="pt").pixel_values for i in image] # num_img [1 C H W] -> num_img [1 C H W] + + image = [self.transform(i).transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [1 C H W] -> num_img [C 1 H W] + + caps = [i['cap'] for i in image_data] + text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] + + input_ids, cond_mask, clip_image = [], [], [] + for t, clip_i in zip(text, clip_image_list): + drop_results = self.drop(t, clip_i) + t = drop_results['text'] + clip_i = drop_results['clip_image'] + text_tokens_and_mask = self.tokenizer( + t, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids.append(text_tokens_and_mask['input_ids']) + cond_mask.append(text_tokens_and_mask['attention_mask']) + clip_image.append(clip_i) + input_ids = torch.cat(input_ids) # self.use_image_num, l + cond_mask = torch.cat(cond_mask) # self.use_image_num, l + clip_image = torch.cat(clip_image) # self.use_image_num, C, H, W + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) + + def decord_read(self, path, frame_idx=None): + decord_vr = self.v_decoder(path) + total_frames = len(decord_vr) + # Sampling video frames + if frame_idx is None: + start_frame_ind, end_frame_ind = self.temporal_sample(total_frames) + else: + start_frame_ind, end_frame_ind = frame_idx.split(':') + # start_frame_ind, end_frame_ind = int(start_frame_ind), int(end_frame_ind) + start_frame_ind, end_frame_ind = int(start_frame_ind), int(start_frame_ind) + self.num_frames + # assert end_frame_ind - start_frame_ind >= self.num_frames + frame_indice = np.linspace(start_frame_ind, end_frame_ind - 1, self.num_frames, dtype=int) + # frame_indice = np.linspace(0, 63, self.num_frames, dtype=int) + + video_data = decord_vr.get_batch(frame_indice).asnumpy() + video_data = torch.from_numpy(video_data) + video_data = video_data.permute(0, 3, 1, 2) # (T H W C) -> (T C H W) + return video_data + + def read_jsons(self, data, postfix=".jpg"): + cap_lists = [] + with open(data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + for folder, anno in folder_anno: + with open(anno, 'r') as f: + sub_list = json.load(f) + print(f'Building {anno}...') + for i in tqdm(range(len(sub_list))): + sub_list[i]['path'] = opj(folder, sub_list[i]['path']) + cap_lists += sub_list + return cap_lists + + def get_img_cap_list(self): + use_image_num = self.use_image_num if self.use_image_num != 0 else 1 + img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") + img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + + return img_cap_lists[:-1] # drop last to avoid error length + + def get_vid_cap_list(self): + vid_cap_lists = [] + with open(self.video_data, 'r') as f: + folder_anno = [i.strip().split(',') for i in f.readlines() if len(i.strip()) > 0] + for folder, anno in folder_anno: + new_vid_cap_list = [] + with open(anno, 'r') as f: + vid_cap_list = json.load(f) + print(f'Building {anno}...') + for i in tqdm(range(len(vid_cap_list))): + path = opj(folder, vid_cap_list[i]['path']) + # For testing, some data has not been utilized. + if not os.path.exists(path) and not os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): + print(path) + continue + elif os.path.exists(path.replace('.mp4', '_resize1080p.mp4')): + path = path.replace('.mp4', '_resize1080p.mp4') + new_vid_cap_list.append( + { + 'path': path, + 'frame_idx': vid_cap_list[i]['frame_idx'], + 'cap': vid_cap_list[i]['cap'] + } + ) + + vid_cap_lists += new_vid_cap_list + return vid_cap_lists + + +if __name__ == "__main__": + + from torchvision import transforms + from torchvision.transforms import Lambda + from .transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop + + from transformers import AutoTokenizer, AutoImageProcessor, CLIPImageProcessor + + class Args: + def __init__(self): + # self.video_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' + # self.image_data = '/remote-home/gyy/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' + self.video_data = '/storage/gyy/hw/Open-Sora-Plan/scripts/train_data/video_data_debug.txt' + self.image_data = '/storage/gyy/hw/Open-Sora-Plan/scripts/train_data/image_data_debug.txt' + self.num_frames = 65 + self.use_image_num = 4 + self.use_img_from_vid = False + self.model_max_length = 300 + self.cfg = 0.1 + self.default_text_ratio = 0.5 + self.i2v_ratio = 0.3 + self.transition_ratio = 0.3 + self.clear_video_ratio = 0.3 + self.max_image_size = 512 + self.sample_rate = 1 + self.text_encoder_name = "DeepFloyd/t5-v1_1-xxl" + # self.image_encoder_name = "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" + self.image_encoder_name = "facebook/dinov2-giant" + self.cache_dir = "/storage/cache_dir" + + args = Args() + resize = [CenterCropResizeVideo((args.max_image_size, args.max_image_size))] + + temporal_sample = TemporalRandomCrop(args.num_frames * args.sample_rate) # 16 x + tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + image_processor = AutoImageProcessor.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + + resize_transform = transforms.Compose([ + *resize, + ]) + + transform = transforms.Compose([ + ToTensorVideo(), + Lambda(lambda x: 2. * x - 1.) + ]) + + dataset = VideoIP_dataset(args, resize_transform=resize_transform, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, image_processor=image_processor) + + print(len(dataset)) + video_data = dataset[0]['video_data'] + image_data = dataset[0]['image_data'] + video, video_input_ids, video_cond_mask, clip_video = video_data['video'], video_data['input_ids'], video_data['cond_mask'], video_data['clip_video'] + image, image_input_ids, image_cond_mask, clip_image = image_data['image'], image_data['input_ids'], image_data['cond_mask'], image_data['clip_image'] + print(video.shape) # C, F, H, W + print(video_input_ids.shape) # 1 D + print(video_cond_mask.shape) # 1 D + print(clip_video.shape) # T, C, H, W + print(clip_image.shape) # num_images, C, H, W + print(image[0].shape) + print(image_input_ids.shape) + print(image_cond_mask.shape) + print(video_cond_mask) + print(image_cond_mask) + diff --git a/opensora/models/diffusion/__init__.py b/opensora/models/diffusion/__init__.py index 8416bfc47..2d997ff34 100644 --- a/opensora/models/diffusion/__init__.py +++ b/opensora/models/diffusion/__init__.py @@ -12,7 +12,6 @@ Diffusion_models.update(UDiT_Ultra_models) Diffusion_models.update(Inpaint_models) -Diffusion_models.update(inpaint_models) from .latte.modeling_latte import Latte_models_class from .opensora.modeling_opensora import OpenSora_models_class diff --git a/opensora/sample/sample_inpaint.py b/opensora/sample/sample_inpaint.py index 2fada8de5..a63fa237d 100644 --- a/opensora/sample/sample_inpaint.py +++ b/opensora/sample/sample_inpaint.py @@ -264,4 +264,4 @@ def preprocess_images(images): lask_ckpt = path else: print("no new ckpt, sleeping...") - time.sleep(300) + time.sleep(5) diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py index 0df8c259d..06db51cd4 100644 --- a/opensora/train/train_inpaint.py +++ b/opensora/train/train_inpaint.py @@ -357,7 +357,7 @@ def main(args): # Create EMA for the unet. if args.use_ema: ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + ema_model = EMAModel(ema_model.parameters(), decay=args.ema_decay, update_after_step=args.ema_start_step, model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) # `accelerate` 0.16.0 will have better support for customized saving @@ -1031,6 +1031,7 @@ def train_all_epoch(prof_=None): parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel") parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training") + parser.add_argument("--ema_decay", type=float, default=0.99) # inpaint dataset parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index 9dd1bf5af..f5125fc79 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,20 +1,2 @@ -node030 slots=8 -node033 slots=8 -node034 slots=8 -node012 slots=8 -node004 slots=8 -node005 slots=8 -node006 slots=8 -node008 slots=8 -node009 slots=8 -node010 slots=8 -node011 slots=8 -node027 slots=8 -node028 slots=8 -node031 slots=8 -node032 slots=8 -node035 slots=8 -node056 slots=8 -node061 slots=8 -node063 slots=8 -node064 slots=8 \ No newline at end of file +node065 slots=8 +node068 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index 9dc9dcb70..dafbaa602 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -5,11 +5,11 @@ deepspeed_config: deepspeed_hostfile: scripts/accelerate_configs/hostfile fsdp_config: {} machine_rank: 0 -main_process_ip: 100.64.24.30 +main_process_ip: 100.64.24.65 main_process_port: 29502 main_training_function: main -num_machines: 20 -num_processes: 160 +num_machines: 2 +num_processes: 16 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh b/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh index 311d349a6..99eeb1c16 100644 --- a/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh +++ b/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh @@ -54,14 +54,15 @@ accelerate launch \ --tile_overlap_factor 0.125 \ --enable_tiling \ --snr_gamma 5.0 \ - --use_ema \ + --use_ema 0.999 \ --ema_start_step 0 \ --cfg 0.1 \ --noise_offset 0.02 \ --use_rope \ --resume_from_checkpoint="latest" \ + --ema_decay \ --group_frame \ - --speed_factor 1.2 \ + --speed_factor 1.1 \ --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs36x8x1_125x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2/checkpoint-3500/model_ema/diffusion_pytorch_model.safetensors" \ --output_dir=$PROJECT \ --i2v_ratio 0.5 \ diff --git a/scripts/text_condition/gpu/train_video_ip_video21d.sh b/scripts/text_condition/gpu/train_video_ip_video21d.sh index 59190c858..c06885280 100644 --- a/scripts/text_condition/gpu/train_video_ip_video21d.sh +++ b/scripts/text_condition/gpu/train_video_ip_video21d.sh @@ -13,6 +13,8 @@ export NCCL_IB_GID_INDEX=3 export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 +export PDSH_RCMD_TYPE=ssh + accelerate launch \ --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_videoip.py \ From 9ba71b54257b1e1e97ecd77cbf630b190f8a7bc3 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Sun, 14 Jul 2024 07:16:33 +0000 Subject: [PATCH 083/134] merge latest train script --- .gitignore | 6 + docs/Contribution_Guidelines.md | 87 -- docs/Data.md | 39 - docs/EVAL.md | 110 --- docs/Report-v1.0.0-cn.md | 135 --- docs/Report-v1.0.0.md | 136 --- docs/Train_And_Eval_CausalVideoVAE.md | 158 ---- docs/VQVAE.md | 57 -- examples/convert_udit_to_videoudit.py | 24 +- examples/prompt_list_0.txt | 2 +- examples/prompt_list_1.txt | 28 +- examples/prompt_list_2.txt | 1 + opensora/dataset/__init__.py | 16 +- opensora/dataset/t2v_datasets.py | 30 +- .../causal_vae/modeling_causalvae.py | 8 +- .../diffusion/opensora/modeling_opensora.py | 28 +- opensora/models/diffusion/opensora/modules.py | 188 ++-- opensora/models/diffusion/opensora/rope.py | 8 +- .../models/diffusion/udit/modeling_udit.py | 423 ++++++--- opensora/models/diffusion/udit/modules.py | 467 ++++++---- opensora/models/diffusion/udit/rope.py | 154 ++++ .../udit_ultra/modeling_udit_ultra.py | 56 +- .../models/diffusion/udit_ultra/modules.py | 11 +- opensora/models/text_encoder/__init__.py | 6 +- opensora/sample/pipeline_opensora.py | 12 +- opensora/sample/pipeline_opensora_sp.py | 832 ++++++++++++++++++ opensora/sample/sample_t2v.py | 69 +- opensora/sample/sample_t2v_ddp.py | 328 +++++++ opensora/sample/sample_t2v_sp.py | 318 +++++++ opensora/serve/gradio_utils.py | 130 +-- opensora/serve/gradio_web_server.py | 388 +++++--- opensora/serve/style.css | 1 + opensora/train/train_t2v_diffusers.py | 46 +- opensora/utils/communications.py | 122 +++ opensora/utils/dataset_utils.py | 5 +- opensora/utils/parallel_states.py | 44 + scripts/accelerate_configs/hostfile | 28 +- scripts/accelerate_configs/hostfile1 | 38 +- scripts/accelerate_configs/hostfile2 | 25 +- scripts/accelerate_configs/hostfile40 | 40 + scripts/accelerate_configs/hostfile_debug | 31 + .../multi_node_example.yaml | 6 +- .../multi_node_example1.yaml | 4 +- scripts/accelerate_configs/zero2.json | 1 + scripts/text_condition/gpu/sample_image.sh | 4 +- scripts/text_condition/gpu/sample_video.sh | 19 +- .../text_condition/gpu/sample_video_ddp.sh | 23 + .../text_condition/gpu/sample_video_ddp1.sh | 23 + scripts/text_condition/gpu/sample_video_hw.sh | 14 +- scripts/text_condition/gpu/sample_video_sp.sh | 24 + ...480p_new_rope_fp32_aesmovie_sucai_panda.sh | 68 ++ .../gpu/train_video3d_125x480p.sh | 67 ++ .../gpu/train_video3d_125x480p_sp.sh | 69 ++ .../gpu/train_video3d_29x720p.sh | 69 ++ .../gpu/train_video3d_29x720p_sp.sh | 71 ++ .../gpu/train_video3d_93x720p.sh | 69 ++ ...ra_61x480p_new_rope_fp32_aesmovie_sucai.sh | 13 +- ...480p_new_rope_fp32_aesmovie_sucai_panda.sh | 68 ++ ..._61x480p_new_rope_fp32_movie_time_sucai.sh | 62 ++ scripts/train_data/video_data.txt | 2 +- .../train_data/video_data_aesmovie_panda.txt | 7 + .../video_data_aesmovie_sucai_panda.txt | 12 +- scripts/train_data/video_data_debug.txt | 2 +- 63 files changed, 3915 insertions(+), 1417 deletions(-) delete mode 100644 docs/Contribution_Guidelines.md delete mode 100644 docs/Data.md delete mode 100644 docs/EVAL.md delete mode 100644 docs/Report-v1.0.0-cn.md delete mode 100644 docs/Report-v1.0.0.md delete mode 100644 docs/Train_And_Eval_CausalVideoVAE.md delete mode 100644 docs/VQVAE.md create mode 100644 examples/prompt_list_2.txt create mode 100644 opensora/models/diffusion/udit/rope.py create mode 100644 opensora/sample/pipeline_opensora_sp.py create mode 100644 opensora/sample/sample_t2v_ddp.py create mode 100644 opensora/sample/sample_t2v_sp.py create mode 100644 opensora/serve/style.css create mode 100644 opensora/utils/communications.py create mode 100644 opensora/utils/parallel_states.py create mode 100644 scripts/accelerate_configs/hostfile40 create mode 100644 scripts/accelerate_configs/hostfile_debug create mode 100644 scripts/text_condition/gpu/sample_video_ddp.sh create mode 100644 scripts/text_condition/gpu/sample_video_ddp1.sh create mode 100644 scripts/text_condition/gpu/sample_video_sp.sh create mode 100644 scripts/text_condition/gpu/train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh create mode 100644 scripts/text_condition/gpu/train_video3d_125x480p.sh create mode 100644 scripts/text_condition/gpu/train_video3d_125x480p_sp.sh create mode 100644 scripts/text_condition/gpu/train_video3d_29x720p.sh create mode 100644 scripts/text_condition/gpu/train_video3d_29x720p_sp.sh create mode 100644 scripts/text_condition/gpu/train_video3d_93x720p.sh create mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh create mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh create mode 100644 scripts/train_data/video_data_aesmovie_panda.txt diff --git a/.gitignore b/.gitignore index 52611dcc9..b30f6e59d 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,9 @@ bucket.py whileinf.py validation_dir/ inpaint*/ + +*tmp* +*pymp* +check.py +bucket.py +whileinf.py \ No newline at end of file diff --git a/docs/Contribution_Guidelines.md b/docs/Contribution_Guidelines.md deleted file mode 100644 index d7f163a3d..000000000 --- a/docs/Contribution_Guidelines.md +++ /dev/null @@ -1,87 +0,0 @@ -# Contributing to the Open-Sora Plan Community - -The Open-Sora Plan open-source community is a collaborative initiative driven by the community, emphasizing a commitment to being free and void of exploitation. Organized spontaneously by community members, we invite you to contribute to the Open-Sora Plan open-source community and help elevate it to new heights! - -## Submitting a Pull Request (PR) - -As a contributor, before submitting your request, kindly follow these guidelines: - -1. Start by checking the [Open-Sora Plan GitHub](https://github.com/PKU-YuanGroup/Open-Sora-Plan/pulls) to see if there are any open or closed pull requests related to your intended submission. Avoid duplicating existing work. - -2. [Fork](https://github.com/PKU-YuanGroup/Open-Sora-Plan/fork) the [open-sora plan](https://github.com/PKU-YuanGroup/Open-Sora-Plan) repository and download your forked repository to your local machine. - - ```bash - git clone [your-forked-repository-url] - ``` - -3. Add the original Open-Sora Plan repository as a remote to sync with the latest updates: - - ```bash - git remote add upstream https://github.com/PKU-YuanGroup/Open-Sora-Plan - ``` - -4. Sync the code from the main repository to your local machine, and then push it back to your forked remote repository. - - ``` - # Pull the latest code from the upstream branch - git fetch upstream - - # Switch to the main branch - git checkout main - - # Merge the updates from the upstream branch into main, synchronizing the local main branch with the upstream - git merge upstream/main - - # Additionally, sync the local main branch to the remote branch of your forked repository - git push origin main - ``` - - - > Note: Sync the code from the main repository before each submission. - -5. Create a branch in your forked repository for your changes, ensuring the branch name is meaningful. - - ```bash - git checkout -b my-docs-branch main - ``` - -6. While making modifications and committing changes, adhere to our [Commit Message Format](#Commit-Message-Format). - - ```bash - git commit -m "[docs]: xxxx" - ``` - -7. Push your changes to your GitHub repository. - - ```bash - git push origin my-docs-branch - ``` - -8. Submit a pull request to `Open-Sora-Plan:main` on the GitHub repository page. - -## Commit Message Format - -Commit messages must include both `` and `` sections. - -```bash -[]: - │ │ - │ └─⫸ Briefly describe your changes, without ending with a period. - │ - └─⫸ Commit Type: |docs|feat|fix|refactor| -``` - -### Type - -* **docs**: Modify or add documents. -* **feat**: Introduce a new feature. -* **fix**: Fix a bug. -* **refactor**: Restructure code, excluding new features or bug fixes. - -### Summary - -Describe modifications in English, without ending with a period. - -> e.g., git commit -m "[docs]: add a contributing.md file" - -This guideline is borrowed by [minisora](https://github.com/mini-sora/minisora). We sincerely appreciate MiniSora authors for their awesome templates. diff --git a/docs/Data.md b/docs/Data.md deleted file mode 100644 index a8f493701..000000000 --- a/docs/Data.md +++ /dev/null @@ -1,39 +0,0 @@ - -**We need more dataset**, please refer to the [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset) for details. - -## v1.0.0 - -### Text-to-Video - -We open source v1.0.0 all the training data, the annotations and the original video can be found [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0). - -These data consist of segmented video clips, with each clip obtained through center cropping. The resolution of each clip is 512×512. There are 64 frames in each clip, and their corresponding captions can be found in the annotation files. - -We present additional details in [report](https://github.com/PKU-YuanGroup/Open-Sora-Plan/blob/main/docs/Report-v1.0.0.md#data-construction) and [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset). - -### Class-condition - -In order to download UCF-101 dataset, you can download the necessary files in [here](https://www.crcv.ucf.edu/data/UCF101.php). The code assumes a `ucf101` directory with the following structure -``` -UCF-101/ - ApplyEyeMakeup/ - v1.avi - ... - ... - YoYo/ - v1.avi - ... -``` - -### Un-condition - -We use [sky_timelapse](https://drive.google.com/open?id=1xWLiU-MBGN7MrsFHQm4_yXmfHBsMbJQo), which is an un-condition datasets. - -``` -sky_timelapse -├── readme -├── sky_test -├── sky_train -├── test_videofolder.py -└── video_folder.py -``` diff --git a/docs/EVAL.md b/docs/EVAL.md deleted file mode 100644 index 3796c7909..000000000 --- a/docs/EVAL.md +++ /dev/null @@ -1,110 +0,0 @@ -# Evaluate the generated videos quality - -You can easily calculate the following video quality metrics, which supports the batch-wise process. -- **CLIP-SCORE**: It uses the pretrained CLIP model to measure the cosine similarity between two modalities. -- **FVD**: Frechét Video Distance -- **SSIM**: structural similarity index measure -- **LPIPS**: learned perceptual image patch similarity -- **PSNR**: peak-signal-to-noise ratio - -# Requirement -## Environment -- install Pytorch (torch>=1.7.1) -- install CLIP - ``` - pip install git+https://github.com/openai/CLIP.git - ``` -- install clip-cose from PyPi - ``` - pip install clip-score - ``` -- Other package - ``` - pip install lpips - pip install scipy (scipy==1.7.3/1.9.3, if you use 1.11.3, **you will calculate a WRONG FVD VALUE!!!**) - pip install numpy - pip install pillow - pip install torchvision>=0.8.2 - pip install ftfy - pip install regex - pip install tqdm - ``` -## Pretrain model -- FVD - Before you cacluate FVD, you should first download the FVD pre-trained model. You can manually download any of the following and put it into FVD folder. - - `i3d_torchscript.pt` from [here](https://www.dropbox.com/s/ge9e5ujwgetktms/i3d_torchscript.pt) - - `i3d_pretrained_400.pt` from [here](https://onedrive.live.com/download?cid=78EEF3EB6AE7DBCB&resid=78EEF3EB6AE7DBCB%21199&authkey=AApKdFHPXzWLNyI) - - -## Other Notices -1. Make sure the pixel value of videos should be in [0, 1]. -2. We average SSIM when images have 3 channels, ssim is the only metric extremely sensitive to gray being compared to b/w. -3. Because the i3d model downsamples in the time dimension, `frames_num` should > 10 when calculating FVD, so FVD calculation begins from 10-th frame, like upper example. -4. For grayscale videos, we multiply to 3 channels -5. data input specifications for clip_score -> - Image Files:All images should be stored in a single directory. The image files can be in either .png or .jpg format. -> -> - Text Files: All text data should be contained in plain text files in a separate directory. These text files should have the extension .txt. -> -> Note: The number of files in the image directory should be exactly equal to the number of files in the text directory. Additionally, the files in the image directory and text directory should be paired by file name. For instance, if there is a cat.png in the image directory, there should be a corresponding cat.txt in the text directory. -> -> Directory Structure Example: -> ``` -> ├── path/to/image -> │ ├── cat.png -> │ ├── dog.png -> │ └── bird.jpg -> └── path/to/text -> ├── cat.txt -> ├── dog.txt -> └── bird.txt -> ``` - -6. data input specifications for fvd, psnr, ssim, lpips - -> Directory Structure Example: -> ``` -> ├── path/to/generated_image -> │ ├── cat.mp4 -> │ ├── dog.mp4 -> │ └── bird.mp4 -> └── path/to/real_image -> ├── cat.mp4 -> ├── dog.mp4 -> └── bird.mp4 -> ``` - - - -# Usage - -``` -# you change the file path and need to set the frame_num, resolution etc... - -# clip_score cross modality -cd opensora/eval -bash script/cal_clip_score.sh - - - -# fvd -cd opensora/eval -bash script/cal_fvd.sh - -# psnr -cd opensora/eval -bash eval/script/cal_psnr.sh - - -# ssim -cd opensora/eval -bash eval/script/cal_ssim.sh - - -# lpips -cd opensora/eval -bash eval/script/cal_lpips.sh -``` - -# Acknowledgement -The evaluation codebase refers to [clip-score](https://github.com/Taited/clip-score) and [common_metrics](https://github.com/JunyaoHu/common_metrics_on_video_quality). \ No newline at end of file diff --git a/docs/Report-v1.0.0-cn.md b/docs/Report-v1.0.0-cn.md deleted file mode 100644 index 3ddc23e0b..000000000 --- a/docs/Report-v1.0.0-cn.md +++ /dev/null @@ -1,135 +0,0 @@ -# 技术报告 v1.0.0 - -在2024年3月,我们推出了Open-Sora-Plan,一个旨在复现OpenAI [Sora](https://openai.com/sora)的开源计划。它作为一个基础的开源框架,能够训练视频生成模型包括无条件视频生成,类别引导视频生成,文生视频。 - -**今天,我们兴奋地展示Open-Sora-Plan v1.0.0,极大地改进视频生成质量、文本控制能力。** - -相比于之前的视频生成模型,Open-Sora-Plan v1.0.0 有以下的改进: - -1. **CausalVideoVAE高效的训练与推理**。 我们用4×8×8的对视频进行时间和空间的压缩。 -2. **图片视频联合训练提升视觉质量**。 CasualVideoVAE 将首帧看作图片,天然支持同时编码图片和视频。这允许扩散模型提取更多时空细节来改善质量。 - - -### Open-Source Release -我们开源了Open-Sora-Plan去促进视频生成社区的进一步发展。公开代码、数据、模型。 -- 在线演示:Hugging Face [![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0), [![Replicate demo and cloud API](https://replicate.com/camenduru/open-sora-plan-512x512/badge)](https://replicate.com/camenduru/open-sora-plan-512x512) 和 [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/camenduru/Open-Sora-Plan-jupyter/blob/main/Open_Sora_Plan_jupyter.ipynb), 感谢[@camenduru](https://github.com/camenduru)大力支持我们的工作!🤝 -- 代码:所有训练脚本和采样代码。 -- 模型:包括扩散模型和CausalVideoVAE [这里](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0)。 -- 数据:所有原视频和对应描述 [这里](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0)。 - -## 效果 - -Open-Sora-Plan v1.0.0支持图片视频联合训练。我们在此展示视频和图片的重建以及生成: - -720×1280**视频重建**。 因为github的限制,原视频放在: [1](https://streamable.com/gqojal), [2](https://streamable.com/6nu3j8). - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/c100bb02-2420-48a3-9d7b-4608a41f14aa - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/8aa8f587-d9f1-4e8b-8a82-d3bf9ba91d68 - -1536×1024**图片重建** - - - -65×1024×1024**文生视频** - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/2641a8aa-66ac-4cda-8279-86b2e6a6e011 - -65×512×512**文生视频** - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/37e3107e-56b3-4b09-8920-fa1d8d144b9e - - -512×512**文生视频** - -![download](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/491d72bc-e762-48ff-bdcc-cc69350f56d6) - -## 详细技术报告 - -### CausalVideoVAE - -#### 模型结构 - -![image](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/e3c8b35d-a217-4d96-b2e9-5c248a2859c8) - -因果VAE架构继承了[Stable-Diffusion Image VAE](https://github.com/CompVis/stable-diffusion/tree/main)。 为了保证图片VAE的预训练权重可以无缝运用到视频VAE中,模型结构采取如下设计: - -1. **CausalConv3D**: 将Conv2D 转变成CausalConv3D可以实现图片和视频的联合训练. CausalConv3D 对第一帧进行特殊处理,因为它无法访问后续帧。对于更多细节,请参考https://github.com/PKU-YuanGroup/Open-Sora-Plan/pull/145 - -2. **初始化**:将Conv2D扩展到Conv3D常用的[方法](https://github.com/hassony2/inflated_convnets_pytorch/blob/master/src/inflate.py#L5)有两种:平均初始化和中心初始化。 但我们采用了特定的初始化方法(尾部初始化)。 这种初始化方法确保模型无需任何训练就能够直接重建图像,甚至视频。 - -#### 训练细节 - -image - -我们展示了 17×256×256 下两种不同初始化方法的损失曲线。黄色曲线代表使用尾部初始化的损失,而蓝色曲线对应中心初始化的损失。 如图所示,尾部初始化在损失曲线上表现出更好的性能。 此外,我们发现中心初始化会导致错误累积,导致长时间内崩溃。 - -#### 推理技巧 -尽管训练Diffusion中VAE始终是冻住的,我们仍然无法负担CasualVideoVAE的花销。在我们的实验中, 80G的显存只能够在半精度下推理一个256×512×512或32×1024×1024的视频 ,这限制了我们扩展到更长更高清的视频。因此我们采用tile convolution,能够以几乎恒定的内存推理任意时长或任意分辨率的视频。 - -### 数据构建 -我们定义高质量的视频数据集包括两个核心法则:(1) 没有与内容无关的水印。(2) 高质量的文本注释。 - -**对于法则1**,我们从开源网站(CC0协议)爬取了大约40k videos:1234个来自[mixkit](https://mixkit.co/),7408个来自[pexels](https://www.pexels.com/),31616个来自[pixabay](https://pixabay.com/)。我们根据[Panda70M](https://github.com/snap-research/Panda-70M/blob/main/splitting/README.md)提供的场景变换剪切script将这些视频切成大约434k video clips。事实上,根据我们的剪切结果,从这些网上上爬取的99%的视频都是单一的场景。另外,我们发现爬取的数据中超过60%为风景相关视频。更多细节可以在[这](https://github.com/PKU-YuanGroup/Open-Sora-Dataset)找到。 - -**对于法则2**,很难有大量的高质量的文本注释能够从网上直接爬取。因此我们用成熟的图片标注模型来获取高质量的稠密描述。我们对2个多模态大模型进行消融实验:[ShareGPT4V-Captioner-7B](https://github.com/InternLM/InternLM-XComposer/blob/main/projects/ShareGPT4V/README.md) 和 [LLaVA-1.6-34B](https://github.com/haotian-liu/LLaVA)。前者是专门用来制作文本注释的模型,而后者是一个通用的多模态大模型。经过我们的消融实验,他们在caption的表现差不多。然而他们的推理速度在A800上差距很大:40s/it of batch size of 12 for ShareGPT4V-Captioner-7B,15s/it of batch size of 1 for ShareGPT4V-Captioner-7B。我们开源所有的[文本注释和原视频](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0)。 - -| 模型名字 | 平均长度 | 最大值 | 标准差 | -|---|---|---|---| -| ShareGPT4V-Captioner-7B | 170.0827524529121 | 467 | 53.689967539537776 | -| LLaVA-1.6-34B | 141.75851073472666 | 472 | 48.52492072346965 | - -### 训练扩散模型 -与之前的工作类似,我们采用多阶段的级联的训练方法,总共消耗了2048个A800 GPU 小时。我们发现联合图片训练能够显著加速模型的收敛并且增强视觉观感,这与[Latte](https://github.com/Vchitect/Latte)一致。以下是我们的训练花销。 - -| 名字 | Stage 1 | Stage 2 | Stage 3 | Stage 4 | -|---|---|---|---|---| -| 训练视频尺寸 | 17×256×256 | 65×256×256 | 65×512×512 | 65×1024×1024 | -| 计算资源 (#A800 GPU x #小时) | 32 × 40 | 32 × 18 | 32 × 6 | 训练中 | -| 权重 | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/17x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x512x512) | 训练中 | -| 日志 | [wandb](https://api.wandb.ai/links/linbin/p6n3evym) | [wandb](https://api.wandb.ai/links/linbin/t2g53sew) | [wandb](https://api.wandb.ai/links/linbin/uomr0xzb) | 训练中 | -| 训练数据 | ~40k videos | ~40k videos | ~40k videos | ~40k videos | - -## 下版本预览 -### CausalVideoVAE -目前我们发布的CausalVideoVAE v1.0.0版本存在2个主要的缺陷:**运动模糊**以及**网格效应**。我们对CasualVideoVAE做了一系列的改进使它推理成本更低且性能更强大,我们暂时叫它为预览版本,将在下个版本发布。 - -**1分钟720×1280视频重建**。 受限于GitHub,我们将原视频放在这:[原视频](https://streamable.com/u4onbb),[重建视频](https://streamable.com/qt8ncc)。 - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/cdcfa9a3-4de0-42d4-94c0-0669710e407b - -我们从kinetic 400的验证集中随机选取100个样本进行评估,结果表如下所示: - -| | SSIM↑ | LPIPS↓ | PSNR↑ | FLOLPIPS↓ | -|---|---|---|---|---| -| v1.0.0 | 0.829 | 0.106 | 27.171 | 0.119 | -| Preview | 0.877 | 0.064 | 29.695 | 0.070 | - -#### 运动模糊 - -| **v1.0.0** | **预览版本** | -| --- | --- | -| ![6862cae0-b1b6-48d1-bd11-84348cf42b42](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/f815636f-fb38-4891-918b-50b1f9aa086d) | ![9189da06-ef2c-42e6-ad34-bd702a6f538e](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/1e413f50-a785-485a-9851-a1449f952f1c) | - -#### 网格效应 - -| **v1.0.0** | **预览版本** | -| --- | --- | -| ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/7fec5bed-3c83-4ee9-baef-4a3dacafc658) | ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/4f41b432-a3ef-484e-a492-8afd8a691bf7) | - -### 数据构建 - -**数据源**:正如上文提到,我们的数据集中超过60%为风景视频。这意味着我们的开域视频生成能力有限。然而当前的大规模开源数据集大多从YouTube爬取,尽管视频的数量多,但我们担忧视频本身的质量是否达标。因此,我们将继续收集高质量的数据集,同时也欢迎开源社区的推荐。 - -**Caption生成流程**:当我们训练时长增加时,我们不得不考虑更有效的视频caption生成方法,而不是多模态图片大模型。我们正在开发一个新的视频注释生成管线,它能够很好的支持长视频,敬请期待。 - -### 训练扩散模型 -尽管目前v1.0.0展现了可喜的结果,但我们仍然离Sora有一段距离。在接下来的工作中,我们主要围绕这三个方面: - -1. **动态分辨率与时长的训练**: 我们的目标是开发出能够以不同分辨率和持续时间训练模型的技术,使训练过程更加灵活、适应性更强。 - -2. **更长的视频生成**: 我们将探索扩展模型生成能力的方法,使其能够制作更长的视频,超越目前的限制。 - -3. **更多条件控制**: 我们力求增强模型的条件控制能力,为用户提供更多的选项和对生成视频的控制能力。 - -另外,通过仔细观察生成的视频,我们发现存在一些不符合常理的斑点或异常的流动,这是由于CasualVideoVAE的性能不足导致的 如上面提到。在未来的实验中,我们将使用更强的VAE,重新训练一个扩散模型。 diff --git a/docs/Report-v1.0.0.md b/docs/Report-v1.0.0.md deleted file mode 100644 index 8b8419976..000000000 --- a/docs/Report-v1.0.0.md +++ /dev/null @@ -1,136 +0,0 @@ -# Report v1.0.0 - -In March 2024, we launched a plan called Open-Sora-Plan, which aims to reproduce the OpenAI [Sora](https://openai.com/sora) through an open-source framework. As a foundational open-source framework, it enables training of video generation models, including Unconditioned Video Generation, Class Video Generation, and Text-to-Video Generation. - -**Today, we are thrilled to present Open-Sora-Plan v1.0.0, which significantly enhances video generation quality and text control capabilities.** - -Compared with previous video generation model, Open-Sora-Plan v1.0.0 has several improvements: - -1. **Efficient training and inference with CausalVideoVAE**. We apply a spatial-temporal compression to the videos by 4×8×8. -2. **Joint image-video training for better quality**. Our CausalVideoVAE considers the first frame as an image, allowing for the simultaneous encoding of both images and videos in a natural manner. This allows the diffusion model to grasp more spatial-visual details to improve visual quality. - -### Open-Source Release -We open-source the Open-Sora-Plan to facilitate future development of Video Generation in the community. Code, data, model are made publicly available. -- Demo: Hugging Face demo [![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0). 🤝 Enjoying the [![Replicate demo and cloud API](https://replicate.com/camenduru/open-sora-plan-512x512/badge)](https://replicate.com/camenduru/open-sora-plan-512x512) and [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/camenduru/Open-Sora-Plan-jupyter/blob/main/Open_Sora_Plan_jupyter.ipynb), created by [@camenduru](https://github.com/camenduru), who generously supports our research! -- Code: All training scripts and sample scripts. -- Model: Both Diffusion Model and CausalVideoVAE [here](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0). -- Data: Both raw videos and captions [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0). - -## Gallery - -Open-Sora-Plan v1.0.0 supports joint training of images and videos. Here, we present the capabilities of Video/Image Reconstruction and Generation: - -### CausalVideoVAE Reconstruction - -**Video Reconstruction** with 720×1280. Since github can't upload large video, we put it here: [1](https://streamable.com/gqojal), [2](https://streamable.com/6nu3j8). - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/c100bb02-2420-48a3-9d7b-4608a41f14aa - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/8aa8f587-d9f1-4e8b-8a82-d3bf9ba91d68 - -**Image Reconstruction** in 1536×1024. - - - -**Text-to-Video Generation** with 65×1024×1024 - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/2641a8aa-66ac-4cda-8279-86b2e6a6e011 - -**Text-to-Video Generation** with 65×512×512 - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/37e3107e-56b3-4b09-8920-fa1d8d144b9e - - -**Text-to-Image Generation** with 512×512 - -![download](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/491d72bc-e762-48ff-bdcc-cc69350f56d6) - -## Detailed Technical Report - -### CausalVideoVAE - -#### Model Structure - -![image](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/e3c8b35d-a217-4d96-b2e9-5c248a2859c8) - -The CausalVideoVAE architecture inherits from the [Stable-Diffusion Image VAE](https://github.com/CompVis/stable-diffusion/tree/main). To ensure that the pretrained weights of the Image VAE can be seamlessly applied to the Video VAE, the model structure has been designed as follows: - -1. **CausalConv3D**: Converting Conv2D to CausalConv3D enables joint training of image and video data. CausalConv3D applies a special treatment to the first frame, as it does not have access to subsequent frames. For more specific details, please refer to https://github.com/PKU-YuanGroup/Open-Sora-Plan/pull/145 - -2. **Initialization**: There are two common [methods](https://github.com/hassony2/inflated_convnets_pytorch/blob/master/src/inflate.py#L5) to expand Conv2D to Conv3D: average initialization and center initialization. But we employ a specific initialization method (tail initialization). This initialization method ensures that without any training, the model is capable of directly reconstructing images, and even videos. - -#### Training Details - -image - -We present the loss curves for two distinct initialization methods under 17×256×256. The yellow curve represents the loss using tail init, while the blue curve corresponds to the loss from center initialization. As shown in the graph, tail initialization demonstrates better performance on the loss curve. Additionally, we found that center initialization leads to error accumulation, causing the collapse over extended durations. - -#### Inference Tricks -Despite the VAE in Diffusion training being frozen, we still find it challenging to afford the cost of the CausalVideoVAE. In our case, with 80GB of GPU memory, we can only infer a video of either 256×512×512 or 32×1024×1024 resolution using half-precision, which limits our ability to scale up to longer and higher-resolution videos. Therefore, we adopt tile convolution, which allows us to infer videos of arbitrary duration or resolution with nearly constant memory usage. - -### Data Construction -We define a high-quality video dataset based on two core principles: (1) No content-unrelated watermarks. (2) High-quality and dense captions. - -**For principles 1**, we crawled approximately 40,000 videos from open-source websites under the CC0 license. Specifically, we obtained 1,234 videos from [mixkit](https://mixkit.co/), 7,408 videos from [pexels](https://www.pexels.com/), and 31,616 videos from [pixabay](https://pixabay.com/). These videos adhere to the principle of having no content-unrelated watermarks. According to the scene transformation and clipping script provided by [Panda70M](https://github.com/snap-research/Panda-70M/blob/main/splitting/README.md), we have divided these videos into approximately 434,000 video clips. In fact, based on our clipping results, 99% of the videos obtained from these online sources are found to contain single scenes. Additionally, we have observed that over 60% of the crawled data comprises landscape videos. More details can be found [here](https://github.com/PKU-YuanGroup/Open-Sora-Dataset). - -**For principles 2**, it is challenging to directly crawl a large quantity of high-quality dense captions from the internet. Therefore, we utilize a mature Image-captioner model to obtain high-quality dense captions. We conducted ablation experiments on two multimodal large models: [ShareGPT4V-Captioner-7B](https://github.com/InternLM/InternLM-XComposer/blob/main/projects/ShareGPT4V/README.md) and [LLaVA-1.6-34B](https://github.com/haotian-liu/LLaVA). The former is specifically designed for caption generation, while the latter is a general-purpose multimodal large model. After conducting our ablation experiments, we found that they are comparable in performance. However, there is a significant difference in their inference speed on the A800 GPU: 40s/it of batch size of 12 for ShareGPT4V-Captioner-7B, 15s/it of batch size of 1 for LLaVA-1.6-34B. We open-source all annotations [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0). We show some statistics here, and we set the maximum length of the model to 300, which covers almost 99% of the samples. - -| Name | Avg length | Max | Std | -|---|---|---|---| -| ShareGPT4V-Captioner-7B | 170.0827524529121 | 467 | 53.689967539537776 | -| LLaVA-1.6-34B | 141.75851073472666 | 472 | 48.52492072346965 | - -### Training Diffusion Model -Similar to previous work, we employ a multi-stage cascaded training approach, which consumes a total of 2,528 A800 GPU hours. We found that joint training with images significantly accelerates model convergence and enhances visual perception, aligning with the findings of [Latte](https://github.com/Vchitect/Latte). Below is our training card: - -| Name | Stage 1 | Stage 2 | Stage 3 | Stage 4 | -|---|---|---|---|---| -| Training Video Size | 17×256×256 | 65×256×256 | 65×512×512 | 65×1024×1024 | -| Compute (#A800 GPU x #Hours) | 32 × 40 | 32 × 22 | 32 × 17 | Under training | -| Checkpoint | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/17x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x512x512) | Under training | -| Log | [wandb](https://api.wandb.ai/links/linbin/p6n3evym) | [wandb](https://api.wandb.ai/links/linbin/t2g53sew) | [wandb](https://api.wandb.ai/links/linbin/uomr0xzb) | Under training | -| Training Data | ~40k videos | ~40k videos | ~40k videos | ~40k videos | - -## Next Release Preview -### CausalVideoVAE -Currently, the released version of CausalVideoVAE (v1.0.0) has two main drawbacks: **motion blurring** and **gridding effect**. We have made a series of improvements to CausalVideoVAE to reduce its inference cost and enhance its performance. We are currently referring to this enhanced version as the "preview version," which will be released in the next update. Preview reconstruction is as follows: - -**1 min Video Reconstruction with 720×1280**. Since github can't put too big video, we put it here: [origin video](https://streamable.com/u4onbb), [reconstruction video](https://streamable.com/qt8ncc). - -https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/cdcfa9a3-4de0-42d4-94c0-0669710e407b - -We randomly selected 100 samples from the validation set of Kinetics-400 for evaluation, and the results are presented in the following table: - -| | SSIM↑ | LPIPS↓ | PSNR↑ | FLOLPIPS↓ | -|---|---|---|---|---| -| v1.0.0 | 0.829 | 0.106 | 27.171 | 0.119 | -| Preview | 0.877 | 0.064 | 29.695 | 0.070 | - -#### Motion Blurring - -| **v1.0.0** | **Preview** | -| --- | --- | -| ![6862cae0-b1b6-48d1-bd11-84348cf42b42](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/f815636f-fb38-4891-918b-50b1f9aa086d) | ![9189da06-ef2c-42e6-ad34-bd702a6f538e](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/1e413f50-a785-485a-9851-a1449f952f1c) | - -#### Gridding effect - -| **v1.0.0** | **Preview** | -| --- | --- | -| ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/7fec5bed-3c83-4ee9-baef-4a3dacafc658) | ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/4f41b432-a3ef-484e-a492-8afd8a691bf7) | - -### Data Construction - -**Data source**. As mentioned earlier, over 60% of our dataset consists of landscape videos. This implies that our ability to generate videos in other domains is limited. However, most of the current large-scale open-source datasets are primarily obtained through web scraping from platforms like YouTube. While these datasets provide a vast quantity of videos, we have concerns about the quality of the videos themselves. Therefore, we will continue to collect high-quality datasets and also welcome recommendations from the open-source community. We are launching an Open-Sora-Dataset project, check out the details at [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset) - -**Caption Generation Pipeline**. As the video duration increases, we need to consider more efficient methods for video caption generation instead of relying solely on large multimodal image models. We are currently developing a new video caption generation pipeline that provides robust support for long videos. We are excited to share more details with you in the near future. Stay tuned! - -### Training Diffusion Model -Although v1.0.0 has shown promising results, we acknowledge that we still have a ways to go to reach the level of Sora. In our upcoming work, we will primarily focus on three aspects: - -1. **Training support for dynamic resolution and duration**: We aim to develop techniques that enable training models with varying resolutions and durations, allowing for more flexible and adaptable training processes. - -2. **Support for longer video generation**: We will explore methods to extend the generation capabilities of our models, enabling them to produce longer videos beyond the current limitations. - -3. **Enhanced conditional control**: We seek to enhance the conditional control capabilities of our models, providing users with more options and control over the generated videos. - -Furthermore, through careful observation of the generated videos, we have noticed the presence of some non-physiological speckles or abnormal flow. This can be attributed to the limited performance of CausalVideoVAE, as mentioned earlier. In future experiments, we plan to retrain a diffusion model using a more powerful version of CausalVideoVAE to address these issues. diff --git a/docs/Train_And_Eval_CausalVideoVAE.md b/docs/Train_And_Eval_CausalVideoVAE.md deleted file mode 100644 index 691f159f8..000000000 --- a/docs/Train_And_Eval_CausalVideoVAE.md +++ /dev/null @@ -1,158 +0,0 @@ -# Training - -To execute in the terminal: `bash scripts/causalvae/train.sh` - -> When using GAN loss for training, two backward propagations are required. However, when [custom optimizers](https://lightning.ai/docs/pytorch/stable/model/manual_optimization.html#use-multiple-optimizers-like-gans) are implemented in PyTorch Lightning, it can lead to the training step count being doubled, meaning each training loop effectively results in two steps. This issue can make it counterintuitive when setting the training step count and the starting step count for the GAN loss. - -## Code Structure - -CausalVideoVAE is located in the directory `opensora/models/ae/videobase`. The directory structure is as follows: - -``` -. -├── causal_vae -├── causal_vqvae -├── configuration_videobase.py -├── dataset_videobase.py -├── __init__.py -├── losses -├── modeling_videobase.py -├── modules -├── __pycache__ -├── trainer_videobase.py -├── utils -└── vqvae -``` - -The `casual_vae` directory defines the overall structure of the CausalVideoVAE model, and the `modules` directory contains some of the required modules for the model, including **CausalConv3D**, **ResnetBlock3D**, **Attention**, etc. The `losses` directory includes **GAN loss**, **Perception loss**, and other content. - -## Configuration - -Model training requires two key files: one is the `config.json` file, which configures the model structure, loss function, learning rate, etc. The other is the `train.sh` file, which configures the dataset, training steps, precision, etc. - -### Model Configuration File - -Taking the release version model configuration file `release.json` as an example: - -```json -{ - "_class_name": "CausalVAEModel", - "_diffusers_version": "0.27.2", - "attn_resolutions": [], - "decoder_attention": "AttnBlock3D", - "decoder_conv_in": "CausalConv3d", - "decoder_conv_out": "CausalConv3d", - "decoder_mid_resnet": "ResnetBlock3D", - "decoder_resnet_blocks": [ - "ResnetBlock3D", - "ResnetBlock3D", - "ResnetBlock3D", - "ResnetBlock3D" - ], - "decoder_spatial_upsample": [ - "", - "SpatialUpsample2x", - "SpatialUpsample2x", - "SpatialUpsample2x" - ], - "decoder_temporal_upsample": [ - "", - "", - "TimeUpsample2x", - "TimeUpsample2x" - ], - "double_z": true, - "dropout": 0.0, - "embed_dim": 4, - "encoder_attention": "AttnBlock3D", - "encoder_conv_in": "CausalConv3d", - "encoder_conv_out": "CausalConv3d", - "encoder_mid_resnet": "ResnetBlock3D", - "encoder_resnet_blocks": [ - "ResnetBlock3D", - "ResnetBlock3D", - "ResnetBlock3D", - "ResnetBlock3D" - ], - "encoder_spatial_downsample": [ - "SpatialDownsample2x", - "SpatialDownsample2x", - "SpatialDownsample2x", - "" - ], - "encoder_temporal_downsample": [ - "TimeDownsample2x", - "TimeDownsample2x", - "", - "" - ], - "hidden_size": 128, - "hidden_size_mult": [ - 1, - 2, - 4, - 4 - ], - "loss_params": { - "disc_start": 2001, - "disc_weight": 0.5, - "kl_weight": 1e-06, - "logvar_init": 0.0 - }, - "loss_type": "opensora.models.ae.videobase.losses.LPIPSWithDiscriminator", - "lr": 1e-05, - "num_res_blocks": 2, - "q_conv": "CausalConv3d", - "resolution": 256, - "z_channels": 4 -} -``` - -It configures the modules used in different layers of the encoder and decoder, as well as the loss. By changing the model configuration file, it is easy to train different model structures. - -### Training Script - -The following is a description of the parameters for the `train_causalvae.py`: - -| Parameter | Default Value | Description | -|-----------------------------|-----------------|--------------------------------------------------------| -| `--exp_name` | "causalvae" | The name of the experiment, used for the folder where results are saved. | -| `--batch_size` | 1 | The number of samples per training iteration. | -| `--precision` | "bf16" | The numerical precision type used for training. | -| `--max_steps` | 100000 | The maximum number of steps for the training process. | -| `--save_steps` | 2000 | The interval at which to save the model during training. | -| `--output_dir` | "results/causalvae" | The directory where training results are saved. | -| `--video_path` | "/remote-home1/dataset/data_split_tt" | The path where the video data is stored. | -| `--video_num_frames` | 17 | The number of frames per video. | -| `--sample_rate` | 1 | The sampling rate, indicating the number of video frames per second. | -| `--dynamic_sample` | False | Whether to use dynamic sampling. | -| `--model_config` | "scripts/causalvae/288.yaml" | The path to the model configuration file. | -| `--n_nodes` | 1 | The number of nodes used for training. | -| `--devices` | 8 | The number of devices used for training. | -| `--resolution` | 256 | The resolution of the videos. | -| `--num_workers` | 8 | The number of subprocesses used for data handling. | -| `--resume_from_checkpoint` | None | Resume training from a specified checkpoint. | -| `--load_from_checkpoint` | None | Load the model from a specified checkpoint. | - -Please ensure that the values provided for these parameters are appropriate for your training setup. - -# Evaluation - - -1. Video Generation: -The script `scripts/causalvae/gen_video.sh` in the repository is utilized for generating videos. For the parameters, please refer to the script itself. - -2. Video Evaluation: -After video generation, You can evaluate the generated videos using the `scripts/causalvae/eval.sh` script. This evaluation script supports common metrics, including lpips, flolpips, ssim, psnr, and more. - -> Please note that you must generate the videos before executing the eval script. Additionally, it is essential to ensure that the video parameters used when generating the videos are consistent with those used during the evaluation. - -# How to Import a Trained Model - -Our model class inherits from the configuration and model management classes of huggingface, supporting the download and loading of models from huggingface. It can also import models trained with pytorch lightning. - -``` -model = CausalVAEModel.from_pretrained(args.ckpt) -model = model.to(device) -``` - diff --git a/docs/VQVAE.md b/docs/VQVAE.md deleted file mode 100644 index a1af36364..000000000 --- a/docs/VQVAE.md +++ /dev/null @@ -1,57 +0,0 @@ -# VQVAE Documentation - -# Introduction - -Vector Quantized Variational AutoEncoders (VQ-VAE) is a type of autoencoder that uses a discrete latent representation. It is particularly useful for tasks that require discrete latent variables, such as text-to-speech and video generation. - -# Usage - -## Initialization - -To initialize a VQVAE model, you can use the `VideoGPTVQVAE` class. This class is a part of the `opensora.models.ae` module. - -```python -from opensora.models.ae import VideoGPTVQVAE - -vqvae = VideoGPTVQVAE() -``` - -### Training - -To train the VQVAE model, you can use the `train_videogpt.sh` script. This script will train the model using the parameters specified in the script. - -```bash -bash scripts/videogpt/train_videogpt.sh -``` - -### Loading Pretrained Models - -You can load a pretrained model using the `download_and_load_model` method. This method will download the checkpoint file and load the model. - -```python -vqvae = VideoGPTVQVAE.download_and_load_model("bair_stride4x2x2") -``` - -Alternatively, you can load a model from a checkpoint using the `load_from_checkpoint` method. - -```python -vqvae = VQVAEModel.load_from_checkpoint("results/VQVAE/checkpoint-1000") -``` - -### Encoding and Decoding - -You can encode a video using the `encode` method. This method will return the encodings and embeddings of the video. - -```python -encodings, embeddings = vqvae.encode(x_vae, include_embeddings=True) -``` - -You can reconstruct a video from its encodings using the decode method. - -```python -video_recon = vqvae.decode(encodings) -``` - -## Testing - -You can test the VQVAE model by reconstructing a video. The `examples/rec_video.py` script provides an example of how to do this. \ No newline at end of file diff --git a/examples/convert_udit_to_videoudit.py b/examples/convert_udit_to_videoudit.py index 6abf576b8..66c2b13f2 100644 --- a/examples/convert_udit_to_videoudit.py +++ b/examples/convert_udit_to_videoudit.py @@ -35,26 +35,26 @@ def conv2d_to_conv3d(name, conv2d_weights, k_size): conv3d_weights = torch.zeros_like(conv2d_weights).unsqueeze(2).repeat(1, 1, k_size, 1, 1) - # conv3d_weights[:,:,0,:,:] = conv2d_weights / 10 - conv3d_weights[:,:,1,:,:] = conv2d_weights - # conv3d_weights[:,:,2,:,:] = conv2d_weights / 10 + conv3d_weights[:,:,0,:,:] = conv2d_weights / 4 + conv3d_weights[:,:,1,:,:] = conv2d_weights / 1 + conv3d_weights[:,:,2,:,:] = conv2d_weights / 4 return conv3d_weights -path = "bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema/diffusion_pytorch_model.safetensors" +path = "/storage/ongoing/new/checkpoints/bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema/diffusion_pytorch_model.safetensors" ckpt = safe_load(path, device="cpu") new_ckpt = {} k_size = 3 patch_size_t = 1 for k, v in ckpt.items(): - if 'pos_embed.proj.weight' in k: - new_v = conv2d_to_conv3d(k, v, k_size) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 - elif 'attn1.downsampler.layer.weight' in k: + # if 'pos_embed.proj.weight' in k: + # new_v = conv2d_to_conv3d(k, v, k_size) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + if 'attn1.downsampler.layer.weight' in k: new_v = conv2d_to_conv3d(k, v, k_size) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 - elif 'body.0.weight' in k and 'down' in k: - new_v = conv2d_to_conv3d(k, v, k_size) # 384, 768, 3, 3 -> 384, 768, 3, 3, 3 - elif 'body.0.weight' in k and 'up' in k: - new_v = conv2d_to_conv3d(k, v, k_size) # 6144, 3072, 3, 3 -> 6144, 3072, 3, 3, 3 + # elif 'body.0.weight' in k and 'down' in k: + # new_v = conv2d_to_conv3d(k, v, k_size) # 384, 768, 3, 3 -> 384, 768, 3, 3, 3 + # elif 'body.0.weight' in k and 'up' in k: + # new_v = conv2d_to_conv3d(k, v, k_size) # 6144, 3072, 3, 3 -> 6144, 3072, 3, 3, 3 else: new_v = v new_ckpt[k] = new_v -torch.save(new_ckpt, f'480p_73000_ema_k{k_size}_p{patch_size_t}_repeat_lowsize10.pt') \ No newline at end of file +torch.save(new_ckpt, f'/storage/ongoing/new/image2video_weight/480p_73000_ema_ds_k{k_size}_p{patch_size_t}_repeat_lowsize4.pt') \ No newline at end of file diff --git a/examples/prompt_list_0.txt b/examples/prompt_list_0.txt index 292f23602..7f3c6dccc 100644 --- a/examples/prompt_list_0.txt +++ b/examples/prompt_list_0.txt @@ -25,4 +25,4 @@ The dynamic movement of tall, wispy grasses swaying in the wind. The sky above i A close-up of a magician’s crystal ball that reveals a futuristic cityscape within. Skyscrapers of light stretch towards the heavens, and flying cars zip through the air, casting neon reflections across the ball’s surface. 8K. A majestic horse gallops across a bridge made of rainbows, each hoof striking sparks of color that cascade into the sky, the clouds parting to reveal a sunlit path to a distant, magical realm. A close-up of a robot dog as it interacts with a group of real puppies in a park, its mechanical eyes blinking with curiosity and tail wagging energetically. High Resolution. -An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. +An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. \ No newline at end of file diff --git a/examples/prompt_list_1.txt b/examples/prompt_list_1.txt index 53b4d0975..ab0fb276d 100644 --- a/examples/prompt_list_1.txt +++ b/examples/prompt_list_1.txt @@ -1,24 +1,32 @@ -A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues. -A quiet beach at dawn, the waves softly lapping at the shore, pink and orange hues painting the sky, offering a moment of solitude and reflection. -A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about. Several giant wooly mammoths approach treading through a snowy meadow, their long wooly fur lightly blows in the wind as they walk, snow covered trees and dramatic snow capped mountains in the distance, mid afternoon light with wispy clouds and a sun high in the distance creates a warm glow, the low camera view is stunning capturing the large furry mammal with beautiful photography, depth of field. A movie trailer featuring the adventures of the 30 year old space man wearing a red wool knitted motorcycle helmet, blue sky, salt desert, cinematic style, shot on 35mm film, vivid colors. -Drone view of waves crashing against the rugged cliffs along Big Sur’s garay point beach. The crashing blue waters create white-tipped waves, while the golden light of the setting sun illuminates the rocky shore. A small island with a lighthouse sits in the distance, and green shrubbery covers the cliff’s edge. The steep drop from the road down to the beach is a dramatic feat, with the cliff’s edges jutting out over the sea. This is a view that captures the raw beauty of the coast and the rugged landscape of the Pacific Coast Highway. Animated scene features a close-up of a short fluffy monster kneeling beside a melting red candle. The art style is 3D and realistic, with a focus on lighting and texture. The mood of the painting is one of wonder and curiosity, as the monster gazes at the flame with wide eyes and open mouth. Its pose and expression convey a sense of innocence and playfulness, as if it is exploring the world around it for the first time. The use of warm colors and dramatic lighting further enhances the cozy atmosphere of the image. +A time-lapse of a storm forming over the ocean, dark clouds gathering and lightning flashing. The storm's energy creates spirals of light that dance across the sky. +A majestic eagle perches on a high cliff, its keen eyes scanning the valley below. With a powerful flap, it takes off, leaving a trail of sparkling feathers. +A single butterfly with wings that resemble stained glass flutters through a field of flowers. The shot captures the light as it passes through the delicate wings, creating a vibrant, colorful display. HD. +A solitary mermaid swims through an underwater cave filled with glowing crystals. The shot follows her graceful movements, capturing the play of light on her scales and the ethereal beauty of the cave. +Close-up of a dragon's eye as it slowly opens, revealing a fiery iris that reflects the burning landscape around it, while smoke wisps off its scaly eyelid. +A cat with the enigmatic smile of the Mona Lisa, lounging regally on a velvet cushion, her eyes following a fluttering butterfly that mirrors the mysterious allure of her expression. 4K. A gorgeously rendered papercraft world of a coral reef, rife with colorful fish and sea creatures. This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird’s head is tilted slightly to the side, giving the impression of it looking regal and majestic. The background is blurred, drawing attention to the bird’s striking appearance. Photorealistic closeup video of two pirate ships battling each other as they sail inside a cup of coffee. The majestic beauty of a waterfall cascading down a cliff into a serene lake. Sunset over the sea. a cat wearing sunglasses and working as a lifeguard at pool. -Slow pan upward of blazing oak fire in an indoor fireplace. -Yellow and black tropical fish dart through the sea. -a serene winter scene in a forest. The forest is blanketed in a thick layer of snow, which has settled on the branches of the trees, creating a canopy of white. The trees, a mix of evergreens and deciduous, stand tall and silent, their forms partially obscured by the snow. The ground is a uniform white, with no visible tracks or signs of human activity. The sun is low in the sky, casting a warm glow that contrasts with the cool tones of the snow. The light filters through the trees, creating a soft, diffused illumination that highlights the texture of the snow and the contours of the trees. The overall style of the scene is naturalistic, with a focus on the tranquility and beauty of the winter landscape. +An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt, he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film. +A lone figure stands on the deck of a spaceship, looking out at a nebula filled with vibrant colors. The shot tracks their gaze, capturing the breathtaking beauty of the cosmic landscape and the sense of infinite possibility. +A large orange octopus is seen resting on the bottom of the ocean floor, blending in with the sandy and rocky terrain. lts tentacles are spread out around its body, and its eyes are closed. The octopus is unaware of a king crab that is crawling towards it from behind a rock,its claws raised and ready to attack. The crab is brown and spiny,with long legs and antennae. The scene is captured from a wide angle,showing the vastness and depth of the ocean. The wateris clear and blue, with rays of sunlight filtering through. The shot is sharp and crisp, with a high dynamic range. The octopus and the crab are in focus, while the background is slightly blurred,creating a depth of field effect. a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water. -A serene waterfall cascading down moss-covered rocks, its soothing sound creating a harmonious symphony with nature. A soaring drone footage captures the majestic beauty of a coastal cliff, its red and yellow stratified rock faces rich in color and against the vibrant turquoise of the sea. Seabirds can be seen taking flight around the cliff's precipices. As the drone slowly moves from different angles, the changing sunlight casts shifting shadows that highlight the rugged textures of the cliff and the surrounding calm sea. The water gently laps at the rock base and the greenery that clings to the top of the cliff, and the scene gives a sense of peaceful isolation at the fringes of the ocean. The video captures the essence of pristine natural beauty untouched by human structures. -The video captures the majestic beauty of a waterfall cascading down a cliff into a serene lake. The waterfall, with its powerful flow, is the central focus of the video. The surrounding landscape is lush and green, with trees and foliage adding to the natural beauty of the scene. The camera angle provides a bird's eye view of the waterfall, allowing viewers to appreciate the full height and grandeur of the waterfall. The video is a stunning representation of nature's power and beauty. A vibrant scene of a snowy mountain landscape. The sky is filled with a multitude of colorful hot air balloons, each floating at different heights, creating a dynamic and lively atmosphere. The balloons are scattered across the sky, some closer to the viewer, others further away, adding depth to the scene. Below, the mountainous terrain is blanketed in a thick layer of snow, with a few patches of bare earth visible here and there. The snow-covered mountains provide a stark contrast to the colorful balloons, enhancing the visual appeal of the scene. A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene. A snowy forest landscape with a dirt road running through it. The road is flanked by trees covered in snow, and the ground is also covered in snow. The sun is shining, creating a bright and serene atmosphere. The road appears to be empty, and there are no people or animals visible in the video. The style of the video is a natural landscape shot, with a focus on the beauty of the snowy forest and the peacefulness of the road. -The dynamic movement of tall, wispy grasses swaying in the wind. The sky above is filled with clouds, creating a dramatic backdrop. The sunlight pierces through the clouds, casting a warm glow on the scene. The grasses are a mix of green and brown, indicating a change in seasons. The overall style of the video is naturalistic, capturing the beauty of the landscape in a realistic manner. The focus is on the grasses and their movement, with the sky serving as a secondary element. The video does not contain any human or animal elements. \ No newline at end of file +The dynamic movement of tall, wispy grasses swaying in the wind. The sky above is filled with clouds, creating a dramatic backdrop. The sunlight pierces through the clouds, casting a warm glow on the scene. The grasses are a mix of green and brown, indicating a change in seasons. The overall style of the video is naturalistic, capturing the beauty of the landscape in a realistic manner. The focus is on the grasses and their movement, with the sky serving as a secondary element. The video does not contain any human or animal elements. +A close-up of a magician’s crystal ball that reveals a futuristic cityscape within. Skyscrapers of light stretch towards the heavens, and flying cars zip through the air, casting neon reflections across the ball’s surface. 8K. +A majestic horse gallops across a bridge made of rainbows, each hoof striking sparks of color that cascade into the sky, the clouds parting to reveal a sunlit path to a distant, magical realm. +A close-up of a robot dog as it interacts with a group of real puppies in a park, its mechanical eyes blinking with curiosity and tail wagging energetically. High Resolution. +An elderly woman with white hair and a lined face is seated inside an older model car, looking out through the side window with a contemplative or mildly sad expression. +A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues. +A quiet beach at dawn, the waves softly lapping at the shore, pink and orange hues painting the sky, offering a moment of solitude and reflection. +A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about. +Drone view of waves crashing against the rugged cliffs along Big Sur’s garay point beach. The crashing blue waters create white-tipped waves, while the golden light of the setting sun illuminates the rocky shore. A small island with a lighthouse sits in the distance, and green shrubbery covers the cliff’s edge. The steep drop from the road down to the beach is a dramatic feat, with the cliff’s edges jutting out over the sea. This is a view that captures the raw beauty of the coast and the rugged landscape of the Pacific Coast Highway. \ No newline at end of file diff --git a/examples/prompt_list_2.txt b/examples/prompt_list_2.txt new file mode 100644 index 000000000..3abd6127b --- /dev/null +++ b/examples/prompt_list_2.txt @@ -0,0 +1 @@ +An extreme close-up of an gray-haired man with a beard in his 60s, he is deep in thought pondering the history of the universe as he sits at a cafe in Paris, his eyes focus on people offscreen as they walk as he sits mostly motionless, he is dressed in a wool coat suit coat with a button-down shirt, he wears a brown beret and glasses and has a very professorial appearance, and the end he offers a subtle closed-mouth smile as if he found the answer to the mystery of life, the lighting is very cinematic with the golden light and the Parisian streets and city in the background, depth of field, cinematic 35mm film. \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 406a76a7b..b2408baa2 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -79,16 +79,10 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - transform_topcrop = transforms.Compose([ - ToTensorVideo(), - *resize_topcrop, - # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription - norm_fun - ]) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--DeepFloyd--t5-v1_1-xxl/snapshots/c9c625d2ec93667ec579ede125fd3811d1f81d37", cache_dir=args.cache_dir) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir) - # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) - tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) return T2V_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, transform_topcrop=transform_topcrop) elif args.dataset == 'i2v' or args.dataset == 'inpaint': @@ -112,12 +106,6 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - transform_topcrop = transforms.Compose([ - ToTensorVideo(), - *resize_topcrop, - # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription - norm_fun - ]) tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 0f09ef46a..f2ad708f8 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -133,6 +133,9 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro self.model_max_length = args.model_max_length self.cfg = args.cfg self.speed_factor = args.speed_factor + self.max_height = args.max_height + self.max_width = args.max_width + self.drop_short_ratio = args.drop_short_ratio assert self.speed_factor >= 1 self.v_decoder = DecordInit() @@ -314,26 +317,28 @@ def define_frame_index(self, vid_cap_list): if not filter_resolution(resolution['height'], resolution['width']): cnt_resolution_mismatch += 1 continue + if self.max_height > resolution['height'] or self.max_width > resolution['width']: + cnt_resolution_mismatch += 1 + continue if fps is not None and duration is not None: # import ipdb;ipdb.set_trace() i['num_frames'] = int(fps * duration) # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. - if i['num_frames'] > 6.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) + if i['num_frames'] > 5.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) cnt_too_long += 1 continue # if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage # cnt_too_short += 1 - # continue + # continue # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) frame_interval = fps / self.train_fps - start_frame_idx = 8 if 'movie/' in i['path'] else 0 # special video + start_frame_idx = 8 if '/storage/dataset/movie' in i['path'] else 0 # special video frame_indices = np.arange(start_frame_idx, i['num_frames'], frame_interval).astype(int) frame_indices = frame_indices[frame_indices < i['num_frames']] # comment out it to enable dynamic frames training - drop_short_video_prob = 1.0 - if len(frame_indices) < self.num_frames and random.uniform(0, 1) <= drop_short_video_prob: # to drop short videos + if len(frame_indices) < self.num_frames and random.random() < self.drop_short_ratio: cnt_too_short += 1 continue @@ -349,7 +354,7 @@ def define_frame_index(self, vid_cap_list): continue frame_indices = frame_indices[:end_frame_idx] - if 'movie/' in i['path']: + if '/storage/dataset/movie' in i['path']: cnt_movie += 1 i['sample_frame_index'] = frame_indices.tolist() new_vid_cap_list.append(i) @@ -369,15 +374,14 @@ def decord_read(self, path): fps = decord_vr.get_avg_fps() if decord_vr.get_avg_fps() > 0 else 30.0 # import ipdb;ipdb.set_trace() # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) - frame_interval = 1.0 if abs(fps - self.train_fps) < 1e-1 else fps / self.train_fps - start_frame_idx = 8 if 'movie/' in path else 0 # special video + frame_interval = 1.0 if abs(fps - self.train_fps) < 0.1 else fps / self.train_fps + start_frame_idx = 8 if '/storage/dataset/movie' in path else 0 # special video frame_indices = np.arange(start_frame_idx, total_frames, frame_interval).astype(int) frame_indices = frame_indices[frame_indices < total_frames] #import ipdb;ipdb.set_trace() # speed up max_speed_factor = len(frame_indices) / self.num_frames - speed_factor = 1.0 - if self.speed_factor > 1 and max_speed_factor > 1 and not ('MagicTime_Data/' in path): + if self.speed_factor > 1 and max_speed_factor > 1 and not ('/storage/dataset/MagicTime_Data' in path): speed_factor = random.uniform(1.0, min(self.speed_factor, max_speed_factor)) target_frame_count = int(len(frame_indices) / speed_factor) speed_frame_idx = np.linspace(0, len(frame_indices) - 1, target_frame_count, dtype=int) @@ -392,10 +396,10 @@ def decord_read(self, path): # to find a suitable end_frame_idx, to ensure we do not need pad video end_frame_idx = find_closest_y(len(frame_indices), vae_stride_t=4, model_ds_t=4) if end_frame_idx == -1: # too short that can not be encoded exactly by videovae - raise IndexError(f'video ({path}) has {total_frames} frames, speed_factor: {speed_factor}, but need to sample {len(frame_indices)} frames ({frame_indices})') + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') frame_indices = frame_indices[:end_frame_idx] - if len(frame_indices) < self.num_frames: - raise IndexError(f'video ({path}) has {total_frames} frames, speed_factor: {speed_factor}, but need to sample {len(frame_indices)} frames ({frame_indices})') + if len(frame_indices) < self.num_frames and self.drop_short_ratio >= 1: + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') video_data = decord_vr.get_batch(frame_indices).asnumpy() video_data = torch.from_numpy(video_data) video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) diff --git a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py index b673b0197..03efadf23 100644 --- a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py +++ b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py @@ -328,14 +328,14 @@ def __init__( decoder_mid_resnet: Module = "ResnetBlock3D", ) -> None: super().__init__() - self.tile_sample_min_size = 256 - self.tile_sample_min_size_t = 65 + self.tile_sample_min_size = 512 + self.tile_sample_min_size_t = 33 self.tile_latent_min_size = int(self.tile_sample_min_size / (2 ** (len(hidden_size_mult) - 1))) # t_down_ratio = [i for i in encoder_temporal_downsample if len(i) > 0] # self.tile_latent_min_size_t = int((self.tile_sample_min_size_t-1) / (2 ** len(t_down_ratio))) + 1 - self.tile_latent_min_size_t = 17 - self.tile_overlap_factor = 0.25 + self.tile_latent_min_size_t = 16 + self.tile_overlap_factor = 0.125 self.use_tiling = False self.learning_rate = lr diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 3caf27994..76b1c59ed 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -20,6 +20,8 @@ except: torch_npu = None npu_config = None + from opensora.utils.parallel_states import get_sequence_parallel_state, nccl_info + class OpenSoraT2V(ModelMixin, ConfigMixin): """ A 2D Transformer model for image-like data. @@ -326,6 +328,7 @@ def forward( `tuple` where the first element is the sample tensor. """ batch_size, c, frame, h, w = hidden_states.shape + # print('hidden_states.shape', hidden_states.shape) frame = frame - use_image_num # 21-4=17 if cross_attention_kwargs is not None: if cross_attention_kwargs.get("scale", None) is not None: @@ -349,9 +352,15 @@ def forward( # b, frame+use_image_num, h, w -> a video with images # b, 1, h, w -> only images attention_mask = attention_mask.to(self.dtype) - if npu_config is not None and get_sequence_parallel_state(): - attention_mask_vid = attention_mask[:, :frame * hccl_info.world_size] # b, frame, h, w - attention_mask_img = attention_mask[:, frame * hccl_info.world_size:] # b, use_image_num, h, w + if get_sequence_parallel_state(): + if npu_config is not None: + attention_mask_vid = attention_mask[:, :frame * hccl_info.world_size] # b, frame, h, w + attention_mask_img = attention_mask[:, frame * hccl_info.world_size:] # b, use_image_num, h, w + else: + # print('before attention_mask.shape', attention_mask.shape) + attention_mask_vid = attention_mask[:, :frame * nccl_info.world_size] # b, frame, h, w + attention_mask_img = attention_mask[:, frame * nccl_info.world_size:] # b, use_image_num, h, w + # print('after attention_mask.shape', attention_mask_vid.shape) else: attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w @@ -370,7 +379,7 @@ def forward( attention_mask_vid = (1 - attention_mask_vid.bool().to(self.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None attention_mask_img = (1 - attention_mask_img.bool().to(self.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None - if frame == 1 and use_image_num == 0: + if frame == 1 and use_image_num == 0 and not get_sequence_parallel_state(): attention_mask_img = attention_mask_vid attention_mask_vid = None # convert encoder_attention_mask to a bias the same way we do for attention_mask @@ -386,7 +395,7 @@ def forward( encoder_attention_mask_img = encoder_attention_mask[:, in_t-use_image_num:] # b, use_image_num, l encoder_attention_mask_img = rearrange(encoder_attention_mask_img, 'b i l -> (b i) 1 l') if encoder_attention_mask_img.numel() > 0 else None - if frame == 1 and use_image_num == 0: + if frame == 1 and use_image_num == 0 and not get_sequence_parallel_state(): encoder_attention_mask_img = encoder_attention_mask_vid encoder_attention_mask_vid = None @@ -402,6 +411,7 @@ def forward( # 1. Input frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy + # print('frame', frame) height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size added_cond_kwargs = {"resolution": None, "aspect_ratio": None} @@ -411,12 +421,16 @@ def forward( ) # 2. Blocks # import ipdb;ipdb.set_trace() - if npu_config is not None and get_sequence_parallel_state(): + if get_sequence_parallel_state(): if hidden_states_vid is not None: + # print(333333333333333) hidden_states_vid = rearrange(hidden_states_vid, 'b s h -> s b h', b=batch_size).contiguous() encoder_hidden_states_vid = rearrange(encoder_hidden_states_vid, 'b s h -> s b h', b=batch_size).contiguous() timestep_vid = timestep_vid.view(batch_size, 6, -1).transpose(0, 1).contiguous() + # print('timestep_vid', timestep_vid.shape) + + for block in self.transformer_blocks: if self.training and self.gradient_checkpointing: @@ -490,7 +504,7 @@ def custom_forward(*inputs): width=width, ) - if npu_config is not None and get_sequence_parallel_state(): + if get_sequence_parallel_state(): if hidden_states_vid is not None: hidden_states_vid = rearrange(hidden_states_vid, 's b h -> b s h', b=batch_size).contiguous() diff --git a/opensora/models/diffusion/opensora/modules.py b/opensora/models/diffusion/opensora/modules.py index 8cc02c21c..969921619 100644 --- a/opensora/models/diffusion/opensora/modules.py +++ b/opensora/models/diffusion/opensora/modules.py @@ -28,6 +28,8 @@ torch_npu = None npu_config = None set_run_dtype = None + from opensora.utils.parallel_states import get_sequence_parallel_state, nccl_info + from opensora.utils.communications import all_to_all_SBH logger = logging.get_logger(__name__) def get_3d_sincos_pos_embed( @@ -224,18 +226,32 @@ def forward(self, latent, num_frames): if self.num_frames != num_frames: # import ipdb;ipdb.set_trace() # raise NotImplementedError - if npu_config is not None and get_sequence_parallel_state(): - sp_size = hccl_info.world_size - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames * sp_size, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - rank = hccl_info.rank % sp_size - st_frame = rank * num_frames - ed_frame = st_frame + num_frames - temp_pos_embed = temp_pos_embed[st_frame: ed_frame] + if get_sequence_parallel_state(): + if npu_config is not None: + sp_size = hccl_info.world_size + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames * sp_size, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + rank = hccl_info.rank % sp_size + st_frame = rank * num_frames + ed_frame = st_frame + num_frames + temp_pos_embed = temp_pos_embed[st_frame: ed_frame] + else: + sp_size = nccl_info.world_size + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames * sp_size, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + rank = nccl_info.rank % sp_size + st_frame = rank * num_frames + ed_frame = st_frame + num_frames + temp_pos_embed = temp_pos_embed[st_frame: ed_frame] + else: temp_pos_embed = get_1d_sincos_pos_embed( embed_dim=self.temp_pos_embed.shape[-1], @@ -262,9 +278,10 @@ def forward(self, latent, num_frames): video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') if video_latent is not None and video_latent.numel() > 0 else None image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None - if num_frames == 1 and image_latent is None: + if num_frames == 1 and image_latent is None and not get_sequence_parallel_state(): image_latent = video_latent video_latent = None + # print('video_latent is None, image_latent is None', video_latent is None, image_latent is None) return video_latent, image_latent @@ -656,11 +673,16 @@ def __call__( if input_ndim == 4: batch_size, channel, height, width = hidden_states.shape hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) - - if npu_config is not None and get_sequence_parallel_state(): - sequence_length, batch_size, _ = ( - hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape - ) + + if get_sequence_parallel_state(): + if npu_config is not None: + sequence_length, batch_size, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) + else: + sequence_length, batch_size, _ = ( + hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape + ) else: batch_size, sequence_length, _ = ( hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape @@ -746,45 +768,105 @@ def __call__( hidden_states = npu_config.restore_dtype(hidden_states) else: - query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - - # qk norm - # query = attn.q_norm(query) - # key = attn.k_norm(key) - - if self.use_rope: - # require the shape of (batch_size x nheads x ntokens x dim) - pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) - query = self.rope(query, pos_thw) - key = self.rope(key, pos_thw) - - value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + if get_sequence_parallel_state(): + query = query.reshape(-1, attn.heads, head_dim) # [s // sp, b, h * d] -> [s // sp * b, h, d] + key = key.reshape(-1, attn.heads, head_dim) + value = value.reshape(-1, attn.heads, head_dim) + # query = attn.q_norm(query) + # key = attn.k_norm(key) + h_size = attn.heads * head_dim + sp_size = nccl_info.world_size + h_size_sp = h_size // sp_size + # apply all_to_all to gather sequence and split attention heads [s // sp * b, h, d] -> [s * b, h // sp, d] + query = all_to_all_SBH(query, scatter_dim=1, gather_dim=0).reshape(-1, batch_size, h_size_sp) + key = all_to_all_SBH(key, scatter_dim=1, gather_dim=0).reshape(-1, batch_size, h_size_sp) + value = all_to_all_SBH(value, scatter_dim=1, gather_dim=0).reshape(-1, batch_size, h_size_sp) + query = query.reshape(-1, batch_size, attn.heads // sp_size, head_dim) + key = key.reshape(-1, batch_size, attn.heads // sp_size, head_dim) + value = value.reshape(-1, batch_size, attn.heads // sp_size, head_dim) + # print('query', query.shape, 'key', key.shape, 'value', value.shape) + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame * sp_size, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + key = self.rope(key, pos_thw) - if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible - attention_mask = None - # the output of sdp = (batch, num_heads, seq_len, head_dim) - # TODO: add support for attn.scale when we move to Torch 2.1 - # import ipdb;ipdb.set_trace() - # print(attention_mask) - if self.attention_mode == 'flash': - assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + # print('after rope query', query.shape, 'key', key.shape, 'value', value.shape) + query = rearrange(query, 's b h d -> b h s d') + key = rearrange(key, 's b h d -> b h s d') + value = rearrange(value, 's b h d -> b h s d') + # print('rearrange query', query.shape, 'key', key.shape, 'value', value.shape) + + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + attention_mask = None + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + # import ipdb;ipdb.set_trace() + # print(attention_mask) + if self.attention_mode == 'flash': + assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': hidden_states = F.scaled_dot_product_attention( - query, key, value, dropout_p=0.0, is_causal=False + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False ) - elif self.attention_mode == 'xformers': - with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + + hidden_states = rearrange(hidden_states, 'b h s d -> s b h d') + + hidden_states = hidden_states.reshape(-1, attn.heads // sp_size, head_dim) + + # [s * b, h // sp, d] -> [s // sp * b, h, d] -> [s // sp, b, h * d] + hidden_states = all_to_all_SBH(hidden_states, scatter_dim=0, gather_dim=1).reshape(-1, batch_size, h_size) + else: + query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + # qk norm + # query = attn.q_norm(query) + # key = attn.k_norm(key) + + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + key = self.rope(key, pos_thw) + + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + attention_mask = None + # the output of sdp = (batch, num_heads, seq_len, head_dim) + # TODO: add support for attn.scale when we move to Torch 2.1 + # import ipdb;ipdb.set_trace() + # print(attention_mask) + if self.attention_mode == 'flash': + assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): + hidden_states = F.scaled_dot_product_attention( + query, key, value, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'xformers': + with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=False, enable_mem_efficient=True): + hidden_states = F.scaled_dot_product_attention( + query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False + ) + elif self.attention_mode == 'math': hidden_states = F.scaled_dot_product_attention( query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False ) - elif self.attention_mode == 'math': - hidden_states = F.scaled_dot_product_attention( - query, key, value, attn_mask=attention_mask, dropout_p=0.0, is_causal=False - ) - else: - raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') - hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + else: + raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) hidden_states = hidden_states.to(query.dtype) # linear proj @@ -1142,8 +1224,10 @@ def forward( norm_hidden_states = self.norm1(hidden_states, added_cond_kwargs["pooled_text_emb"]) elif self.norm_type == "ada_norm_single": # import ipdb;ipdb.set_trace() - if npu_config is not None and get_sequence_parallel_state(): + if get_sequence_parallel_state(): batch_size = hidden_states.shape[1] + # print('hidden_states', hidden_states.shape) + # print('timestep', timestep.shape) shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( self.scale_shift_table[:, None] + timestep.reshape(6, batch_size, -1) ).chunk(6, dim=0) diff --git a/opensora/models/diffusion/opensora/rope.py b/opensora/models/diffusion/opensora/rope.py index 6c8d761bc..233cedaf0 100644 --- a/opensora/models/diffusion/opensora/rope.py +++ b/opensora/models/diffusion/opensora/rope.py @@ -6,6 +6,7 @@ except: torch_npu = None npu_config = None + from opensora.utils.parallel_states import get_sequence_parallel_state class PositionGetter3D(object): """ return positions of patches """ @@ -19,7 +20,8 @@ def __call__(self, b, t, h, w, device): y = torch.arange(h, device=device) z = torch.arange(t, device=device) pos = torch.cartesian_prod(z, y, x) - if npu_config is not None and get_sequence_parallel_state(): + if get_sequence_parallel_state(): + # print('PositionGetter3D', PositionGetter3D) pos = pos.reshape(t * h * w, 3).transpose(0, 1).reshape(3, -1, 1).contiguous().expand(3, -1, b).clone() else: pos = pos.reshape(t * h * w, 3).transpose(0, 1).reshape(3, 1, -1).contiguous().expand(3, b, -1).clone() @@ -61,7 +63,7 @@ def rotate_half(x): def apply_rope1d(self, tokens, pos1d, cos, sin): assert pos1d.ndim == 2 - if npu_config is None: + if not get_sequence_parallel_state(): # for (batch_size x nheads x ntokens x dim) cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] @@ -93,4 +95,4 @@ def forward(self, tokens, positions): y = self.apply_rope1d(y, poses[1], cos_y, sin_y) x = self.apply_rope1d(x, poses[2], cos_x, sin_x) tokens = torch.cat((t, y, x), dim=-1) - return tokens + return tokens \ No newline at end of file diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py index 68e134d6c..94d6651cc 100644 --- a/opensora/models/diffusion/udit/modeling_udit.py +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -5,13 +5,16 @@ from einops import rearrange, repeat from typing import Any, Dict, Optional, Tuple from torch.nn import functional as F +from diffusers.loaders import PeftAdapterMixin from diffusers.models.transformer_2d import Transformer2DModelOutput from diffusers.utils import is_torch_version, deprecate from diffusers.configuration_utils import ConfigMixin, register_to_config from diffusers.models.modeling_utils import ModelMixin from diffusers.models.normalization import AdaLayerNormSingle from diffusers.models.embeddings import PixArtAlphaTextProjection -from opensora.models.diffusion.udit.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock +from opensora.models.diffusion.udit_ultra.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, \ + OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock, \ + FP32_GELU, FP32_SiLU, FP32_Layernorm from opensora.utils.utils import to_2tuple import math import re @@ -22,7 +25,7 @@ torch_npu = None npu_config = None -class UDiTT2V(ModelMixin, ConfigMixin): +class UDiTT2V(ModelMixin, ConfigMixin, PeftAdapterMixin): """ A 2D Transformer model for image-like data. @@ -87,11 +90,15 @@ def __init__( interpolation_scale_t: float = None, use_additional_conditions: Optional[bool] = None, attention_mode: str = 'xformers', - downsampler: str = 'k333_s222' + downsampler: str = 'k333_s222', + use_rope: bool = False, + use_stable_fp32: bool = False, ): super().__init__() # Set some common variables used across the board. + self.use_stable_fp32 = use_stable_fp32 + self.use_rope = use_rope self.downsampler = downsampler self.use_linear_projection = use_linear_projection self.interpolation_scale_t = interpolation_scale_t @@ -130,15 +137,17 @@ def __init__( # 2. Initialize the right blocks. # Initialize the output blocks and other projection blocks when necessary. self._init_patched_inputs(norm_type=norm_type) + if self.use_stable_fp32: + self._replace_fp32_layers() def _init_patched_inputs(self, norm_type): assert self.config.sample_size_t is not None, "OpenSoraT2V over patched input must provide sample_size_t" assert self.config.sample_size is not None, "OpenSoraT2V over patched input must provide sample_size" - self.num_frames = self.config.sample_size_t self.config.sample_size = to_2tuple(self.config.sample_size) - self.height = self.config.sample_size[0] - self.width = self.config.sample_size[1] + # self.num_frames = self.config.sample_size_t + # self.height = self.config.sample_size[0] + # self.width = self.config.sample_size[1] interpolation_scale_t = ((self.config.sample_size_t - 1) // 16 + 1) if self.config.sample_size_t % 2 == 1 else self.config.sample_size_t / 16 interpolation_scale_t = ( self.config.interpolation_scale_t if self.config.interpolation_scale_t is not None else interpolation_scale_t @@ -152,52 +161,51 @@ def _init_patched_inputs(self, norm_type): # down_factor = [int(i) for i in down_factor] # down_factor = down_factor if isinstance(self.config.down_factor, list) else [self.config.down_factor] * 5 # down_factor = [2] * len(self.config.depth) - - if self.config.downsampler is not None and len(self.config.downsampler) == 9: - is_video_model = True - self.pos_embed = OverlapPatchEmbed3D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - patch_size_t=self.config.patch_size_t, - patch_size=self.config.patch_size, - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - ) - elif self.config.downsampler is not None and len(self.config.downsampler) == 7: - is_video_model = False - self.pos_embed = OverlapPatchEmbed2D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - patch_size_t=self.config.patch_size_t, - patch_size=self.config.patch_size, - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - ) - layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, - (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, - (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] - - for i in range((len(self.config.depth)-1)//2): - t = layer_thw[i][0] // 2 if layer_thw[i][0] != 1 else 1 - h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention - w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 - layer_thw.append([t, h, w]) - + is_video_model = False + # if self.config.downsampler is not None and len(self.config.downsampler) == 9: + # is_video_model = True # to init weight from image + # self.pos_embed = OverlapPatchEmbed3D( + # num_frames=self.config.sample_size_t, + # height=self.config.sample_size[0], + # width=self.config.sample_size[1], + # patch_size_t=self.config.patch_size_t, + # patch_size=self.config.patch_size, + # in_channels=self.in_channels, + # embed_dim=self.inner_dim, + # interpolation_scale=interpolation_scale, + # interpolation_scale_t=interpolation_scale_t, + # use_abs_pos=not self.config.use_rope, + # ) + # elif self.config.downsampler is not None and len(self.config.downsampler) == 7: + # is_video_model = False + self.pos_embed = OverlapPatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ) + # layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, + # (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, + # (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] + interpolation_scale_thw = (interpolation_scale_t, *interpolation_scale) + # for i in range((len(self.config.depth)-1)//2): + # t = layer_thw[i][0] // 2 if layer_thw[i][0] != 1 else 1 + # h = (layer_thw[i][1] + layer_thw[i][1] % 4) // 2 # why mod 4, because downsample and downsampler in attention + # w = (layer_thw[i][2] + layer_thw[i][2] % 4) // 2 + # layer_thw.append([t, h, w]) + # self.layer_thw = layer_thw self.encoder_level_1 = nn.ModuleList( [ BasicTransformerBlock( self.inner_dim, self.config.num_attention_heads, self.config.attention_head_dim, - num_frames=layer_thw[0][0], - height=layer_thw[0][1], - width=layer_thw[0][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -213,11 +221,14 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[0]) ] ) - self.down1_2 = Downsample3d(self.inner_dim) if is_video_model else Downsample2d(self.inner_dim) + # self.down1_2 = Downsample3d(self.inner_dim) if is_video_model else Downsample2d(self.inner_dim) + self.down1_2 = Downsample2d(self.inner_dim, is_video_model) self.encoder_level_2 = nn.ModuleList( [ @@ -225,9 +236,6 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 2, self.config.num_attention_heads, self.config.attention_head_dim * 2, - num_frames=layer_thw[1][0], - height=layer_thw[1][1], - width=layer_thw[1][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -243,11 +251,14 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[1]) ] ) - self.down2_3 = Downsample3d(self.inner_dim * 2) if is_video_model else Downsample2d(self.inner_dim * 2) + # self.down2_3 = Downsample3d(self.inner_dim * 2) if is_video_model else Downsample2d(self.inner_dim * 2) + self.down2_3 = Downsample2d(self.inner_dim * 2, is_video_model) self.latent = nn.ModuleList( [ @@ -255,9 +266,6 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 4, self.config.num_attention_heads, self.config.attention_head_dim * 4, - num_frames=layer_thw[2][0], - height=layer_thw[2][1], - width=layer_thw[2][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -273,12 +281,17 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[2]) ] ) - self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 + # self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 + self.up3_2 = Upsample2d(self.inner_dim * 4, is_video_model) ## From Level 4 to Level 3 + + self.reduce_chan_level2_norm = nn.LayerNorm(int(self.inner_dim * 4), elementwise_affine=True, eps=1e-6) self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) self.decoder_level_2 = nn.ModuleList( [ @@ -286,9 +299,6 @@ def _init_patched_inputs(self, norm_type): self.inner_dim * 2, self.config.num_attention_heads, self.config.attention_head_dim * 2, - num_frames=layer_thw[1][0], - height=layer_thw[1][1], - width=layer_thw[1][2], downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, @@ -304,26 +314,28 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[3]) ] ) - self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 - self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 2), bias=True) + # self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 + self.up2_1 = Upsample2d(self.inner_dim * 2, is_video_model) ## From Level 4 to Level 3 + + self.reduce_chan_level1_norm = nn.LayerNorm(int(self.inner_dim * 2), elementwise_affine=True, eps=1e-6) + self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 1), bias=True) self.decoder_level_1 = nn.ModuleList( [ BasicTransformerBlock( - self.inner_dim * 2, + self.inner_dim, self.config.num_attention_heads, - self.config.attention_head_dim * 2, - num_frames=layer_thw[0][0], - height=layer_thw[0][1], - width=layer_thw[0][2], + self.config.attention_head_dim, downsampler=self.config.downsampler, mlp_ratio=self.config.mlp_ratio, dropout=self.config.dropout, - cross_attention_dim=self.inner_dim * 2, + cross_attention_dim=self.inner_dim, activation_fn=self.config.activation_fn, num_embeds_ada_norm=self.config.num_embeds_ada_norm, attention_bias=self.config.attention_bias, @@ -335,6 +347,8 @@ def _init_patched_inputs(self, norm_type): norm_eps=self.config.norm_eps, attention_type=self.config.attention_type, attention_mode=self.config.attention_mode, + use_rope=self.config.use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) for _ in range(self.config.depth[4]) ] @@ -347,10 +361,10 @@ def _init_patched_inputs(self, norm_type): 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels ) elif self.config.norm_type == "ada_norm_single": - self.norm_out = nn.LayerNorm(2 * self.inner_dim, elementwise_affine=False, eps=1e-6) - self.scale_shift_table = nn.Parameter(torch.randn(2, 2 * self.inner_dim) / (2 * self.inner_dim)**0.5) + self.norm_out = nn.LayerNorm(self.inner_dim, elementwise_affine=False, eps=1e-6) + self.scale_shift_table = nn.Parameter(torch.randn(2, self.inner_dim) / (self.inner_dim)**0.5) self.proj_out = nn.Linear( - 2 * self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels + self.inner_dim, self.config.patch_size_t * self.config.patch_size * self.config.patch_size * self.out_channels ) # PixArt-Alpha blocks. @@ -380,6 +394,27 @@ def _init_patched_inputs(self, norm_type): in_features=self.caption_channels, hidden_size=self.inner_dim * 4 ) + def _replace_fp32_layers(self, module=None): + if module is None: + module = self + for name, submodule in module.named_children(): + if isinstance(submodule, nn.LayerNorm): + # print(f"Replacing LayerNorm in {name}") + new_layer = FP32_Layernorm(submodule.normalized_shape, submodule.eps, submodule.elementwise_affine) + if submodule.elementwise_affine: + new_layer.weight.data.copy_(submodule.weight.data.float()) + if submodule.bias is not None: + new_layer.bias.data.copy_(submodule.bias.data.float()) + setattr(module, name, new_layer) + elif isinstance(submodule, nn.SiLU): + # print(f"Replacing SiLU in {name}") + setattr(module, name, FP32_SiLU(submodule.inplace)) + elif isinstance(submodule, nn.GELU): + # print(f"Replacing GELU in {name}") + setattr(module, name, FP32_GELU(submodule.approximate)) + else: + self._replace_fp32_layers(submodule) + def _set_gradient_checkpointing(self, module, value=False): if hasattr(module, "gradient_checkpointing"): module.gradient_checkpointing = value @@ -513,6 +548,9 @@ def custom_forward(*inputs): timestep_1, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -525,11 +563,15 @@ def custom_forward(*inputs): timestep=timestep_1, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) pad_h_1, pad_w_1 = height % 4, width % 4 inp_enc_level2, attention_bias, attention_mask = self.down1_2(out_enc_level1, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) - frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_1) // 2, (width + pad_w_1) // 2 + # frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_1) // 2, (width + pad_w_1) // 2 + height, width = (height + pad_h_1) // 2, (width + pad_w_1) // 2 # encoder_2 out_enc_level2 = inp_enc_level2 @@ -548,6 +590,9 @@ def custom_forward(*inputs): timestep_2, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -560,12 +605,16 @@ def custom_forward(*inputs): timestep=timestep_2, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) pad_h_2, pad_w_2 = height % 4, width % 4 # import ipdb;ipdb.set_trace() inp_enc_level3, attention_bias, attention_mask = self.down2_3(out_enc_level2, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) - frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + # frame, height, width = frame // 2 if frame != 1 else frame, (height + pad_h_2) // 2, (width + pad_w_2) // 2 + height, width = (height + pad_h_2) // 2, (width + pad_w_2) // 2 # latent latent = inp_enc_level3 @@ -583,6 +632,9 @@ def custom_forward(*inputs): timestep_3, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -595,14 +647,19 @@ def custom_forward(*inputs): timestep=timestep_3, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) # decoder_2 # import ipdb;ipdb.set_trace() inp_dec_level2, attention_bias, attention_mask = self.up3_2(latent, attention_mask, frame, height, width, pad_h=pad_h_2, pad_w=pad_w_2) - frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_2, width * 2 - pad_w_2 + # frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_2, width * 2 - pad_w_2 + height, width = height * 2 - pad_h_2, width * 2 - pad_w_2 inp_dec_level2 = torch.cat([inp_dec_level2, out_enc_level2], 2) + inp_dec_level2 = self.reduce_chan_level2_norm(inp_dec_level2) inp_dec_level2 = self.reduce_chan_level2(inp_dec_level2) out_dec_level2 = inp_dec_level2 @@ -620,6 +677,9 @@ def custom_forward(*inputs): timestep_2, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -632,14 +692,19 @@ def custom_forward(*inputs): timestep=timestep_2, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) # decoder_1 # import ipdb;ipdb.set_trace() inp_dec_level1, attention_bias, attention_mask = self.up2_1(out_dec_level2, attention_mask, frame, height, width, pad_h=pad_h_1, pad_w=pad_w_1) - frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_1, width * 2 - pad_w_1 + # frame, height, width = frame * 2 if frame != 1 else frame, height * 2 - pad_h_1, width * 2 - pad_w_1 + height, width = height * 2 - pad_h_1, width * 2 - pad_w_1 inp_dec_level1 = torch.cat([inp_dec_level1, out_enc_level1], 2) + inp_dec_level1 = self.reduce_chan_level1_norm(inp_dec_level1) inp_dec_level1 = self.reduce_chan_level1(inp_dec_level1) out_dec_level1 = inp_dec_level1 @@ -652,11 +717,14 @@ def custom_forward(*inputs): create_custom_forward(block), out_dec_level1, attention_bias, - encoder_hidden_states_2, + encoder_hidden_states_1, encoder_attention_mask, - timestep_2, + timestep_1, cross_attention_kwargs, class_labels, + frame, + height, + width, **ckpt_kwargs, ) else: @@ -664,20 +732,23 @@ def custom_forward(*inputs): out_dec_level1 = block( out_dec_level1, attention_mask=attention_bias, - encoder_hidden_states=encoder_hidden_states_2, + encoder_hidden_states=encoder_hidden_states_1, encoder_attention_mask=encoder_attention_mask, - timestep=timestep_2, + timestep=timestep_1, cross_attention_kwargs=cross_attention_kwargs, class_labels=class_labels, + frame=frame, + height=height, + width=width, ) assert not torch.any(torch.isnan(out_dec_level1)), 'after out_dec_level1' # 3. Output output = self._get_output_for_patched_inputs( hidden_states=out_dec_level1, - timestep=timestep_2, + timestep=timestep_1, class_labels=class_labels, - embedded_timestep=embedded_timestep_2, + embedded_timestep=embedded_timestep_1, num_frames=frame, height=height, width=width, @@ -760,62 +831,72 @@ def _get_output_for_patched_inputs( # output = output[:, :, 1:] return output + def UDiTT2V_S_111(**kwargs): - return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=16, num_attention_heads=16, patch_size_t=1, patch_size=1, + return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=24, num_attention_heads=16, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_S_122(**kwargs): + return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=24, num_attention_heads=16, patch_size_t=1, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_B_111(**kwargs): - return UDiTT2V(depth=[2, 5, 8, 5, 2], attention_head_dim=32, num_attention_heads=16, patch_size_t=1, patch_size=1, + return UDiTT2V(depth=[2, 5, 10, 5, 2], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=1, + mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) + +def UDiTT2V_B_122(**kwargs): + return UDiTT2V(depth=[2, 5, 10, 5, 2], attention_head_dim=24, num_attention_heads=24, patch_size_t=1, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_111(**kwargs): - return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=1, + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_211(**kwargs): - return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=1, + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=2, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_122(**kwargs): - return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=1, patch_size=2, + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=1, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_L_222(**kwargs): - return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=32, num_attention_heads=24, patch_size_t=2, patch_size=2, + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=32, patch_size_t=2, patch_size=2, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) def UDiTT2V_XL_111(**kwargs): - return UDiTT2V(depth=[4, 10, 16, 10, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, + return UDiTT2V(depth=[4, 8, 12, 8, 4], attention_head_dim=24, num_attention_heads=48, patch_size_t=1, patch_size=1, mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) -def UDiTT2V_XXL_111(**kwargs): - return UDiTT2V(depth=[4, 20, 32, 20, 4], attention_head_dim=32, num_attention_heads=32, patch_size_t=1, patch_size=1, - mlp_ratio=2, norm_type="ada_norm_single", caption_channels=4096, **kwargs) UDiT_models = { - "UDiTT2V-S/111": UDiTT2V_S_111, # 0.19B 0.3B if video - "UDiTT2V-B/111": UDiTT2V_B_111, # 0.73B 1.2B if video - "UDiTT2V-L/111": UDiTT2V_L_111, # 2.3B 3.4B if video - "UDiTT2V-L/211": UDiTT2V_L_211, # 2.3B 3.4B if video - "UDiTT2V-L/122": UDiTT2V_L_122, # 2.3B 3.4B if video - "UDiTT2V-L/222": UDiTT2V_L_222, # 2.3B 3.4B if video - "UDiTT2V-XL/111": UDiTT2V_XL_111, # 5.1B 7B if video - "UDiTT2V-XXL/111": UDiTT2V_XXL_111, # 9.4B 11.3B if video + "UDiTT2V-S/111": UDiTT2V_S_111, # 0.4B 0.7B if video + "UDiTT2V-S/122": UDiTT2V_S_122, # 0.4B 0.7B if video + "UDiTT2V-B/111": UDiTT2V_B_111, # 1.0B 1.6B if video + "UDiTT2V-B/122": UDiTT2V_B_122, # 1.0B 1.6B if video + "UDiTT2V-L/111": UDiTT2V_L_111, # 2.2B 3.3B if video + "UDiTT2V-L/211": UDiTT2V_L_211, # 2.2B 3.3B if video + "UDiTT2V-L/122": UDiTT2V_L_122, # 2.2B 3.3B if video + "UDiTT2V-L/222": UDiTT2V_L_222, # 2.2B 3.3B if video + "UDiTT2V-XL/111": UDiTT2V_XL_111, # 5.0B 7.4B if video } UDiT_models_class = { "UDiTT2V-S/111": UDiTT2V, + "UDiTT2V-S/122": UDiTT2V, "UDiTT2V-B/111": UDiTT2V, + "UDiTT2V-B/122": UDiTT2V, "UDiTT2V-L/111": UDiTT2V, "UDiTT2V-L/211": UDiTT2V, "UDiTT2V-L/122": UDiTT2V, "UDiTT2V-L/222": UDiTT2V, "UDiTT2V-XL/111": UDiTT2V, - "UDiTT2V-XXL/111": UDiTT2V, } + if __name__ == '__main__': import sys + from copy import deepcopy from opensora.models.ae import ae_channel_config, ae_stride_config from opensora.models.ae import getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper @@ -824,13 +905,16 @@ def UDiTT2V_XXL_111(**kwargs): { 'ae': 'CausalVAEModel_4x8x8', 'attention_mode': 'xformers', - 'use_rope': False, + 'use_rope': True, 'model_max_length': 300, 'max_height': 480, 'max_width': 640, - 'num_frames': 125, + 'num_frames': 61, 'use_image_num': 0, - 'compress_kv_factor': 1 + 'compress_kv_factor': 1, + 'interpolation_scale_t': 1, + 'interpolation_scale_h': 1, + 'interpolation_scale_w': 1, } ) b = 2 @@ -844,7 +928,10 @@ def UDiTT2V_XXL_111(**kwargs): else: num_frames = args.num_frames // ae_stride_t - device = torch.device('cuda:1') + device = torch.device('cuda:0') + + + model = UDiTT2V_L_122(in_channels=c, out_channels=c, sample_size=latent_size, @@ -861,34 +948,71 @@ def UDiTT2V_XXL_111(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k333_s222').to(device) + downsampler='k333_s222', + interpolation_scale_t=args.interpolation_scale_t, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + use_rope=args.use_rope).to(device) print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad)/1e9} B') - try: - path = "/storage/ongoing/new/Open-Sora-Plan/bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22/checkpoint-50/model_ema/diffusion_pytorch_model.safetensors" + + + model_state_dict = model.state_dict() + pretrained = "/storage/ongoing/new/Open-Sora-Plan/bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-11500/model_ema/diffusion_pytorch_model.safetensors" + if 'safetensors' in pretrained: # pixart series from safetensors.torch import load_file as safe_load - ckpt = safe_load(path, device="cpu") - new_ckpt = {} - k_size = 3 - for k, v in ckpt.items(): - if 'pos_embed.proj.weight' in k: - new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 - elif 'attn1.downsampler.layer.weight' in k: - new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 - elif 'body.0.weight' in k and 'down' in k: - in_c = v.shape[0] - new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 - elif 'body.0.weight' in k and 'up' in k: - new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 - else: - new_v = v - new_ckpt[k] = new_v - msg = model.load_state_dict(new_ckpt, strict=False) - print(msg) - except Exception as e: - print(e) + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] + import ipdb;ipdb.set_trace() + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + print(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + print(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # import sys;sys.exit() + # try: + # path = "bs32_1node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl/checkpoint-500/model/diffusion_pytorch_model.safetensors" + # from safetensors.torch import load_file as safe_load + # ckpt = safe_load(path, device="cpu") + # new_ckpt = {} + # k_size = 3 + # t_stride = 1 + # for k, v in ckpt.items(): + # if 'pos_embed.proj.weight' in k: + # new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + # elif 'attn1.downsampler.layer.weight' in k: + # new_v = v.unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 768, 4, 3, 3 -> 768, 4, 3, 3, 3 + # elif 'body.0.weight' in k and 'down' in k: + # in_c = v.shape[0] + # new_v = v[:in_c//2].unsqueeze(-3).repeat(1, 1, k_size, 1, 1) # 384, 768, 3, 3 -> 192, 768, 3, 3, 3 + # elif 'body.0.weight' in k and 'up' in k: + # new_v = v.unsqueeze(-3).repeat(2, 1, k_size, 1, 1) # 6144, 3072, 3, 3 -> 12288, 3072, 3, 3, 3 + # elif 'proj_out' in k: + # if 'weight' in k: + # new_v = v.repeat(t_stride, 1) # 16, 768 -> 32, 768 + # elif 'bias' in k: + # new_v = v.repeat(t_stride) # 16 -> 32 + # else: + # new_v = v + # new_ckpt[k] = new_v + # msg = model.load_state_dict(new_ckpt, strict=False) + # # print(msg) + # except Exception as e: + # print(e) x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L @@ -897,8 +1021,53 @@ def UDiTT2V_XXL_111(**kwargs): model_kwargs = dict(hidden_states=x, encoder_hidden_states=cond, attention_mask=attn_mask, encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) with torch.no_grad(): - output = model(**model_kwargs) - print(output[0].shape) + output = model(**model_kwargs)[0] + print(output.shape) + + + + + # from peft import LoraConfig, PeftModel, get_peft_model + # from opensora.utils.lora_utils import EMAModel_LoRA, maybe_zero_3, get_peft_state_maybe_zero_3 + # lora_save_path = '/storage/ongoing/new/Open-Sora-Plan/debug_lora/model_lora' + # ema_lora_save_path = '/storage/ongoing/new/Open-Sora-Plan/debug_lora/ema_model_lora' + # origin_model_path = '/storage/ongoing/new/Open-Sora-Plan/bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl/checkpoint-500/model_ema' + # model = UDiTUltraT2V.from_pretrained(origin_model_path) + # lora_config = LoraConfig( + # r=64, + # lora_alpha=64, + # init_lora_weights="gaussian", + # target_modules=["to_k", "to_q", "to_v", "to_out.0"], + # ) + # model_lora = get_peft_model(model, lora_config) + # # --------------------ema lora_model---------------------------------- + # # create ema lora_model + # ema_model = deepcopy(model_lora) + # ema_model_lora = EMAModel_LoRA(lora_config, parameters=ema_model.parameters(), update_after_step=0, + # model_cls=UDiTUltraT2V, model_config=ema_model.config) + # ema_model_lora.save_pretrained(ema_lora_save_path) + # ema_model_load_lora = EMAModel_LoRA.from_pretrained(ema_lora_save_path, UDiTUltraT2V, lora_config, origin_model_path) + # ema_model_lora.load_state_dict(ema_model_load_lora.state_dict()) + # ema_model_lora.to(device) + + # # -----------------lora model--------------------------------- + # # get lora weight + # model_lora.save_pretrained(lora_save_path) + # # ----------------load lora model------------------------------ + # # load lora weight + # model = UDiTUltraT2V.from_pretrained(origin_model_path) + # import ipdb;ipdb.set_trace() + # model_load_lora = PeftModel.from_pretrained(model, lora_save_path) + # for k, v in model_load_lora.state_dict().items(): + # assert torch.allclose(v, model_lora.state_dict()[k]) + # # for k, v in zip(ema_model_lora.shadow_params, model_lora.parameters()): + # # assert torch.allclose(v, k) + # print('Merging LoRA weights...') + # import ipdb;ipdb.set_trace() + # model_load_lora_merge = model_load_lora.merge_and_unload() + # with torch.no_grad(): + # output = model_load_lora_merge(**model_kwargs) + # print(output[0].shape) diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py index 0857bbd7d..522de946f 100644 --- a/opensora/models/diffusion/udit/modules.py +++ b/opensora/models/diffusion/udit/modules.py @@ -14,10 +14,11 @@ import diffusers from diffusers.utils import deprecate, logging from diffusers.utils.torch_utils import maybe_allow_in_graph -from diffusers.models.attention import GatedSelfAttentionDense +from diffusers.models.attention import FeedForward, GatedSelfAttentionDense from diffusers.models.attention_processor import Attention as Attention_ from diffusers.models.embeddings import SinusoidalPositionalEmbedding from diffusers.models.normalization import AdaLayerNorm, AdaLayerNormContinuous, AdaLayerNormZero, RMSNorm +from .rope import PositionGetter3D, RoPE3D import re try: import torch_npu @@ -133,6 +134,21 @@ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos): emb = np.concatenate([emb_sin, emb_cos], axis=1) # (M, D) return emb +class FP32_Layernorm(nn.LayerNorm): + def forward(self, inputs: torch.Tensor) -> torch.Tensor: + origin_dtype = inputs.dtype + return F.layer_norm(inputs.float(), self.normalized_shape, self.weight.float() if self.weight is not None else None, + self.bias.float() if self.bias is not None else None, self.eps).to(origin_dtype) + + +class FP32_SiLU(nn.SiLU): + def forward(self, inputs: torch.Tensor) -> torch.Tensor: + return torch.nn.functional.silu(inputs.float(), inplace=self.inplace).to(inputs.dtype) + + +class FP32_GELU(nn.GELU): + def forward(self, inputs: torch.Tensor) -> torch.Tensor: + return torch.nn.functional.gelu(inputs.float(), approximate=self.approximate).to(inputs.dtype) @@ -153,9 +169,11 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, + use_abs_pos=False, ): super().__init__() # assert patch_size_t == 1 and patch_size == 1 + self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm self.proj = nn.Conv3d( @@ -190,14 +208,11 @@ def forward(self, latent, num_frames): video_latent, image_latent = None, None # b c 1 h w # assert latent.shape[-3] == 1 and num_frames == 1 + num_frames = latent.shape[-3] // self.patch_size_t height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size # latent = rearrange(latent, 'b c t h w -> (b t) c h w') - if npu_config is None: - latent = self.proj(latent) - else: - latent_dtype = latent.dtype - latent = npu_config.run_conv3d(self.proj, latent, latent_dtype) + latent = self.proj(latent) if self.flatten: # latent = latent.flatten(2).transpose(1, 2) # BT C H W -> BT N C @@ -205,45 +220,46 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) - # import ipdb;ipdb.set_trace() - # Interpolate positional embeddings if needed. - # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.height != height or self.width != width: - # raise NotImplementedError - pos_embed = get_2d_sincos_pos_embed( - embed_dim=self.pos_embed.shape[-1], - grid_size=(height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed = torch.from_numpy(pos_embed) - pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) - else: - pos_embed = self.pos_embed + if self.use_abs_pos: + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed - if self.num_frames != num_frames: - # import ipdb;ipdb.set_trace() - # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - temp_pos_embed = torch.from_numpy(temp_pos_embed) - temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) - else: - temp_pos_embed = self.temp_pos_embed + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed - latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) video_latent = latent - # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - temp_pos_embed = temp_pos_embed.unsqueeze(2) - video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) + if self.use_abs_pos: + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') return video_latent @@ -267,9 +283,11 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, + use_abs_pos=False, ): super().__init__() assert patch_size_t == 1 + self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm @@ -316,54 +334,54 @@ def forward(self, latent, num_frames): if self.layer_norm: latent = self.norm(latent) - # import ipdb;ipdb.set_trace() - # Interpolate positional embeddings if needed. - # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) - if self.height != height or self.width != width: - # raise NotImplementedError - pos_embed = get_2d_sincos_pos_embed( - embed_dim=self.pos_embed.shape[-1], - grid_size=(height, width), - base_size=self.base_size, - interpolation_scale=self.interpolation_scale, - ) - pos_embed = torch.from_numpy(pos_embed) - pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) - else: - pos_embed = self.pos_embed + if self.use_abs_pos: + # Interpolate positional embeddings if needed. + # (For PixArt-Alpha: https://github.com/PixArt-alpha/PixArt-alpha/blob/0f55e922376d8b797edd44d25d0e7464b260dcab/diffusion/model/nets/PixArtMS.py#L162C151-L162C160) + if self.height != height or self.width != width: + # raise NotImplementedError + pos_embed = get_2d_sincos_pos_embed( + embed_dim=self.pos_embed.shape[-1], + grid_size=(height, width), + base_size=self.base_size, + interpolation_scale=self.interpolation_scale, + ) + pos_embed = torch.from_numpy(pos_embed) + pos_embed = pos_embed.float().unsqueeze(0).to(latent.device) + else: + pos_embed = self.pos_embed - if self.num_frames != num_frames: - # import ipdb;ipdb.set_trace() - # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) - temp_pos_embed = torch.from_numpy(temp_pos_embed) - temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) - else: - temp_pos_embed = self.temp_pos_embed + if self.num_frames != num_frames: + # import ipdb;ipdb.set_trace() + # raise NotImplementedError + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + temp_pos_embed = torch.from_numpy(temp_pos_embed) + temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) + else: + temp_pos_embed = self.temp_pos_embed - latent = (latent + pos_embed).to(latent.dtype) + latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) - image_latent = latent + assert num_frames == latent.shape[1] - # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() - # temp_pos_embed = temp_pos_embed.unsqueeze(2) - # video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - # image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + if self.use_abs_pos: + # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() + temp_pos_embed = temp_pos_embed.unsqueeze(2) + latent = (latent + temp_pos_embed).to(latent.dtype) - image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None - - return image_latent + latent = rearrange(latent, 'b t n c -> b (t n) c') + return latent + class Attention(Attention_): - def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attention_mode='xformers', **kwags): - processor = AttnProcessor2_0(attention_mode='xformers') + def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_thw, **kwags): + processor = AttnProcessor2_0(attention_mode=attention_mode, use_rope=use_rope, interpolation_scale_thw=interpolation_scale_thw) super().__init__(processor=processor, **kwags) self.downsampler = None if downsampler: # downsampler k155_s122 @@ -376,14 +394,14 @@ def __init__(self, downsampler=None, num_frames=8, height=16, width=16, attentio if len(downsampler_ker_size) == 2: self.downsampler = DownSampler2d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, - down_shortcut=True, num_frames=num_frames, height=height, width=width) + down_shortcut=True) elif len(downsampler_ker_size) == 3: self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, - down_shortcut=True, num_frames=num_frames, height=height, width=width) + down_shortcut=True) - self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) - self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) + # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) class DownSampler3d(nn.Module): @@ -392,67 +410,72 @@ def __init__(self, *args, **kwargs): super().__init__() self.down_factor = kwargs.pop('down_factor') self.down_shortcut = kwargs.pop('down_shortcut') - self.t = kwargs.pop('num_frames') - self.h = kwargs.pop('height') - self.w = kwargs.pop('width') self.layer = nn.Conv3d(*args, **kwargs) - def forward(self, x, attention_mask): + def forward(self, x, attention_mask, t, h, w): b = x.shape[0] - x = rearrange(x, 'b (t h w) d -> b d t h w', t=self.t, h=self.h, w=self.w) + x = rearrange(x, 'b (t h w) d -> b d t h w', t=t, h=h, w=w) if npu_config is None: x = self.layer(x) + (x if self.down_shortcut else 0) else: x_dtype = x.dtype x = npu_config.run_conv3d(self.layer, x, x_dtype) + (x if self.down_shortcut else 0) + + self.t = t//self.down_factor[0] + self.h = h//self.down_factor[1] + self.w = w//self.down_factor[2] + # import ipdb;ipdb.set_trace() x = rearrange(x, 'b d (t dt) (h dh) (w dw) -> (b dt dh dw) (t h w) d', - t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) - attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=self.t, h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> b 1 t h w', t=t, h=h, w=w) attention_mask = rearrange(attention_mask, 'b 1 (t dt) (h dh) (w dw) -> (b dt dh dw) 1 (t h w)', - t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + t=t//self.down_factor[0], h=h//self.down_factor[1], w=w//self.down_factor[2], dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) return x, attention_mask - def reverse(self, x): + + def reverse(self, x, t, h, w): # import ipdb;ipdb.set_trace() x = rearrange(x, '(b dt dh dw) (t h w) d -> b (t dt h dh w dw) d', - t=self.t//self.down_factor[0], h=self.h//self.down_factor[1], w=self.w//self.down_factor[2], + t=t, h=h, w=w, dt=self.down_factor[0], dh=self.down_factor[1], dw=self.down_factor[2]) return x + class DownSampler2d(nn.Module): def __init__(self, *args, **kwargs): ''' Required kwargs: down_factor, downsampler''' super().__init__() self.down_factor = kwargs.pop('down_factor') self.down_shortcut = kwargs.pop('down_shortcut') - self.t = kwargs.pop('num_frames') - self.h = kwargs.pop('height') - self.w = kwargs.pop('width') self.layer = nn.Conv2d(*args, **kwargs) - def forward(self, x, attention_mask): + def forward(self, x, attention_mask, t, h, w): # import ipdb;ipdb.set_trace() b = x.shape[0] - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) x = self.layer(x) + (x if self.down_shortcut else 0) + + self.t = 1 + self.h = h//self.down_factor[0] + self.w = w//self.down_factor[1] + x = rearrange(x, 'b d (h dh) (w dw) -> (b dh dw) (h w) d', - h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + h=h//self.down_factor[0], w=w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) - - attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=self.h, w=self.w) + attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', h=h, w=w) attention_mask = rearrange(attention_mask, 'b 1 (h dh) (w dw) -> (b dh dw) 1 (h w)', - h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + h=h//self.down_factor[0], w=w//self.down_factor[1], dh=self.down_factor[0], dw=self.down_factor[1]) return x, attention_mask - def reverse(self, x): - # import ipdb;ipdb.set_trace() + + def reverse(self, x, t, h, w): x = rearrange(x, '(b t dh dw) (h w) d -> b (t h dh w dw) d', - t=self.t, h=self.h//self.down_factor[0], w=self.w//self.down_factor[1], + t=t, h=h, w=w, dh=self.down_factor[0], dw=self.down_factor[1]) return x @@ -462,11 +485,19 @@ class AttnProcessor2_0: Processor for implementing scaled dot-product attention (enabled by default if you're using PyTorch 2.0). """ - def __init__(self, attention_mode='xformers'): + def __init__(self, attention_mode='xformers', use_rope=False, interpolation_scale_thw=(1, 1, 1)): + self.use_rope = use_rope + self.interpolation_scale_thw = interpolation_scale_thw + if self.use_rope: + self._init_rope(interpolation_scale_thw) self.attention_mode = attention_mode if not hasattr(F, "scaled_dot_product_attention"): raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") + def _init_rope(self, interpolation_scale_thw): + self.rope = RoPE3D(interpolation_scale_thw=interpolation_scale_thw) + self.position_getter = PositionGetter3D() + def __call__( self, attn: Attention, @@ -474,15 +505,19 @@ def __call__( encoder_hidden_states: Optional[torch.FloatTensor] = None, attention_mask: Optional[torch.FloatTensor] = None, temb: Optional[torch.FloatTensor] = None, + frame: int = 8, + height: int = 16, + width: int = 16, *args, **kwargs, ) -> torch.FloatTensor: if len(args) > 0 or kwargs.get("scale", None) is not None: deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." deprecate("scale", "1.0.0", deprecation_message) - + if attn.downsampler is not None: - hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask) + hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask, t=frame, h=height, w=width) + frame, height, width = attn.downsampler.t, attn.downsampler.h, attn.downsampler.w residual = hidden_states if attn.spatial_norm is not None: @@ -532,6 +567,17 @@ def __call__( else: dtype = None + if self.use_rope: + query = query.view(batch_size, -1, attn.heads, head_dim) + key = key.view(batch_size, -1, attn.heads, head_dim) + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + if query.shape == key.shape: + key = self.rope(key, pos_thw) + query = query.view(batch_size, -1, attn.heads * head_dim) + key = key.view(batch_size, -1, attn.heads * head_dim) + with set_run_dtype(query, dtype): query, key, value = npu_config.set_current_run_dtype([query, key, value]) hidden_states = npu_config.run_attention(query, key, value, attention_mask, "BSH", @@ -542,20 +588,27 @@ def __call__( query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) # qk norm - query = attn.q_norm(query) - key = attn.k_norm(key) - query = query * (head_dim ** -0.5) + # query = attn.q_norm(query) + # key = attn.k_norm(key) - if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + if self.use_rope: + # require the shape of (batch_size x nheads x ntokens x dim) + pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) + query = self.rope(query, pos_thw) + key = self.rope(key, pos_thw) + + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + # 0, -10000 ->(bool) False, True ->(any) True ->(not) False + # 0, 0 ->(bool) False, False ->(any) False ->(not) True + if attention_mask is None or not torch.any(attention_mask.bool()): # 0 mean visible attention_mask = None # the output of sdp = (batch, num_heads, seq_len, head_dim) # TODO: add support for attn.scale when we move to Torch 2.1 if self.attention_mode == 'flash': - assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + assert attention_mask is None, 'flash-attn do not support attention_mask' with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): hidden_states = F.scaled_dot_product_attention( query, key, value, dropout_p=0.0, is_causal=False @@ -588,7 +641,7 @@ def __call__( hidden_states = hidden_states / attn.rescale_output_factor if attn.downsampler is not None: - hidden_states = attn.downsampler.reverse(hidden_states) + hidden_states = attn.downsampler.reverse(hidden_states, t=frame, h=height, w=width) return hidden_states @@ -597,38 +650,37 @@ class PixelUnshuffle(nn.Module): def __init__(self, ratio, ratio_t=None): super().__init__() self.r = ratio - self.r_t = ratio_t if ratio_t else ratio + self.r_t = ratio_t if ratio_t else 1 def forward(self, x): - if self.r_t is not None and self.r_t != 1: + # if self.r_t is not None and self.r_t != 1: + if x.ndim == 5: b, c, t, h, w = x.shape + # import ipdb;ipdb.set_trace() assert t % self.r_t == 0 and h % self.r == 0 and w % self.r == 0 - if self.r > 1: - x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r_t, r2=self.r, r3=self.r) + x = rearrange(x, 'b c (t r1) (h r2) (w r3) -> b (c r1 r2 r3) t h w', r1=self.r_t, r2=self.r, r3=self.r) else: b, c, h, w = x.shape assert h % self.r == 0 and w % self.r == 0 - if self.r > 1: - x = rearrange(x, 'b c (h r2) (w r3) -> b (c r2 r3) h w', r2=self.r, r3=self.r) + x = rearrange(x, 'b c (h r2) (w r3) -> b (c r2 r3) h w', r2=self.r, r3=self.r) return x class PixelShuffle(nn.Module): def __init__(self, ratio, ratio_t=None): super().__init__() self.r = ratio - self.r_t = ratio_t if ratio_t else ratio + self.r_t = ratio_t if ratio_t else 1 def forward(self, x): - if self.r_t is not None and self.r_t != 1: + if x.ndim == 5: b, c, t, h, w = x.shape + # import ipdb;ipdb.set_trace() assert c % (self.r_t*self.r*self.r) == 0 - if self.r > 1: - x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r_t, r2=self.r, r3=self.r) + x = rearrange(x, 'b (c r1 r2 r3) t h w -> b c (t r1) (h r2) (w r3)', r1=self.r_t, r2=self.r, r3=self.r) else: b, c, h, w = x.shape assert c % (self.r*self.r) == 0 - if self.r > 1: - x = rearrange(x, 'b (c r2 r3) h w -> b c (h r2) (w r3)', r2=self.r, r3=self.r) + x = rearrange(x, 'b (c r2 r3) h w -> b c (h r2) (w r3)', r2=self.r, r3=self.r) return x class Downsample3d(nn.Module): @@ -640,7 +692,8 @@ def __init__(self, n_feat): def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) - x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0), mode='reflect') + # x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0), mode='reflect') + x = F.pad(x, (0, pad_w, 0, pad_h, 0, 0)) if npu_config is None: x = self.body(x) else: @@ -682,21 +735,91 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): return x, attention_bias, attention_mask +# class Downsample2d(nn.Module): +# def __init__(self, n_feat, is_video_model=False): +# super(Downsample2d, self).__init__() +# self.is_video_model = is_video_model +# Conv = nn.Conv3d if is_video_model else nn.Conv2d +# self.body = nn.Sequential(Conv(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), +# PixelUnshuffle(2, 1)) + +# def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): +# # import ipdb;ipdb.set_trace() +# x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) +# # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') +# # x = F.pad(x, (0, pad_w, 0, pad_h)) +# if npu_config is None: +# x = F.pad(x, (0, pad_w, 0, pad_h)) +# else: +# x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) +# if self.is_video_model: +# x = rearrange(x, '(b t) d h w -> b d t h w', t=frames) +# x = self.body(x) +# if self.is_video_model: +# x = rearrange(x, 'b d t h w -> b (t h w) d', t=frames) +# else: +# x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + +# attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) +# attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) +# attention_mask = F.max_pool2d(attention_mask.float(), kernel_size=2, stride=2) +# attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) +# attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + +# return x, attention_bias, attention_mask + +# class Upsample2d(nn.Module): +# def __init__(self, n_feat, is_video_model=False): +# super(Upsample2d, self).__init__() +# self.is_video_model = is_video_model +# Conv = nn.Conv3d if is_video_model else nn.Conv2d +# self.body = nn.Sequential(Conv(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), +# PixelShuffle(2, 1)) + +# def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): +# if self.is_video_model: +# x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) +# else: +# x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) +# x = self.body(x) +# if self.is_video_model: +# x = rearrange(x, 'b d t h w -> (b t) d h w') +# x = x[:, :, :height*2-pad_h, :width*2-pad_w] +# x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + +# attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) +# attention_mask = attention_mask.repeat_interleave(2, -1).repeat_interleave(2, -2) +# attention_mask = attention_mask[:, :, :height*2-pad_h, :width*2-pad_w] +# attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) +# attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 + +# return x, attention_bias, attention_mask + + class Downsample2d(nn.Module): - def __init__(self, n_feat): + def __init__(self, n_feat, is_video_model=False): super(Downsample2d, self).__init__() - - self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), + self.is_video_model = is_video_model + Conv = nn.Conv3d if is_video_model else nn.Conv2d + self.body = nn.Sequential(Conv(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), PixelUnshuffle(2, 1)) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): + # import ipdb;ipdb.set_trace() x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') + # x = F.pad(x, (0, pad_w, 0, pad_h)) if npu_config is None: - x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') + x = F.pad(x, (0, pad_w, 0, pad_h)) else: - x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h), mode='reflect') + x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) + if self.is_video_model: + x = rearrange(x, '(b t) d h w -> b d t h w', t=frames) x = self.body(x) - x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + if self.is_video_model: + x = rearrange(x, 'b d t h w -> b (t h w) d', t=frames) + else: + x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) @@ -707,15 +830,21 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): return x, attention_bias, attention_mask class Upsample2d(nn.Module): - def __init__(self, n_feat): + def __init__(self, n_feat, is_video_model=False): super(Upsample2d, self).__init__() - - self.body = nn.Sequential(nn.Conv2d(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), + self.is_video_model = is_video_model + Conv = nn.Conv3d if is_video_model else nn.Conv2d + self.body = nn.Sequential(Conv(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), PixelShuffle(2, 1)) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + if self.is_video_model: + x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) + else: + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) x = self.body(x) + if self.is_video_model: + x = rearrange(x, 'b d t h w -> (b t) d h w') x = x[:, :, :height*2-pad_h, :width*2-pad_w] x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) @@ -729,16 +858,13 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): class FeedForward_Conv2d(nn.Module): - def __init__(self, downsampler, dim, hidden_features, bias=True, num_frames=8, height=16, width=16): + def __init__(self, downsampler, dim, hidden_features, bias=True): super(FeedForward_Conv2d, self).__init__() - self.t = num_frames - self.h = height - self.w = width self.bias = bias self.project_in = nn.Linear(dim, hidden_features, bias=bias) - + self.act = nn.GELU() self.dwconv = nn.ModuleList([ nn.Conv2d(hidden_features, hidden_features, kernel_size=(5, 5), stride=1, padding=(2, 2), dilation=1, groups=hidden_features, bias=bias), @@ -751,15 +877,16 @@ def __init__(self, downsampler, dim, hidden_features, bias=True, num_frames=8, h self.project_out = nn.Linear(hidden_features, dim, bias=bias) - def forward(self, x): + def forward(self, x, t, h, w): # import ipdb;ipdb.set_trace() x = self.project_in(x) - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=self.t, h=self.h, w=self.w) - x = F.gelu(x) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=t, h=h, w=w) + + x = self.act(x) out = x for module in self.dwconv: out = out + module(x) - out = rearrange(out, '(b t) d h w -> b (t h w) d', t=self.t, h=self.h, w=self.w) + out = rearrange(out, '(b t) d h w -> b (t h w) d', t=t, h=h, w=w) x = self.project_out(out) return x @@ -825,18 +952,14 @@ def __init__( ff_bias: bool = True, attention_out_bias: bool = True, attention_mode: str = "xformers", - num_frames: int = 16, - height: int = 32, - width: int = 32, downsampler: str = None, mlp_ratio: int = 4, + use_rope: bool = False, + interpolation_scale_thw: Tuple[int] = (1, 1, 1), ): super().__init__() self.only_cross_attention = only_cross_attention - self.t = num_frames - self.h = height - self.w = width # We keep these boolean flags for backward-compatibility. self.use_ada_layer_norm_zero = (num_embeds_ada_norm is not None) and norm_type == "ada_norm_zero" self.use_ada_layer_norm = (num_embeds_ada_norm is not None) and norm_type == "ada_norm" @@ -880,6 +1003,7 @@ def __init__( ) else: self.norm1 = nn.LayerNorm(dim, elementwise_affine=norm_elementwise_affine, eps=norm_eps) + self.attn1 = Attention( query_dim=dim, heads=num_attention_heads, @@ -890,10 +1014,9 @@ def __init__( upcast_attention=upcast_attention, out_bias=attention_out_bias, downsampler=downsampler, - num_frames=num_frames, - height=height, - width=width, attention_mode=attention_mode, + use_rope=use_rope, + interpolation_scale_thw=interpolation_scale_thw, ) # 2. Cross-Attn @@ -926,6 +1049,8 @@ def __init__( out_bias=attention_out_bias, downsampler=None, attention_mode=attention_mode, + use_rope=False, + interpolation_scale_thw=interpolation_scale_thw, ) # is self-attn if encoder_hidden_states is none else: self.norm2 = None @@ -947,15 +1072,21 @@ def __init__( elif norm_type == "layer_norm_i2vgen": self.norm3 = None - self.ff = FeedForward_Conv2d( - downsampler, - dim, - hidden_features=mlp_ratio * dim, - num_frames=num_frames, - height=height, - width=width, - ) + # self.ff = FeedForward_Conv2d( + # downsampler, + # dim, + # hidden_features=mlp_ratio * dim, + # ) + self.ff = FeedForward( + dim, + dropout=dropout, + activation_fn=activation_fn, + final_dropout=final_dropout, + inner_dim=ff_inner_dim, + bias=ff_bias, + ) + # 4. Fuser if attention_type == "gated" or attention_type == "gated-text-image": self.fuser = GatedSelfAttentionDense(dim, cross_attention_dim, num_attention_heads, attention_head_dim) @@ -982,6 +1113,9 @@ def forward( timestep: Optional[torch.LongTensor] = None, cross_attention_kwargs: Dict[str, Any] = None, class_labels: Optional[torch.LongTensor] = None, + frame: int = None, + height: int = None, + width: int = None, added_cond_kwargs: Optional[Dict[str, torch.Tensor]] = None, ) -> torch.FloatTensor: if cross_attention_kwargs is not None: @@ -1018,11 +1152,14 @@ def forward( # 1. Prepare GLIGEN inputs cross_attention_kwargs = cross_attention_kwargs.copy() if cross_attention_kwargs is not None else {} gligen_kwargs = cross_attention_kwargs.pop("gligen", None) - + + # 0, -10000 ->(bool) False, True ->(any) True ->(not) False + # 0, 0 ->(bool) False, False ->(any) False ->(not) True + # assert attention_mask.bool().float().sum() / attention_mask.bool().float().numel() <= 1/16, 'must ~all visible' attn_output = self.attn1( norm_hidden_states, encoder_hidden_states=encoder_hidden_states if self.only_cross_attention else None, - attention_mask=attention_mask, + attention_mask=attention_mask, frame=frame, height=height, width=width, **cross_attention_kwargs, ) if self.norm_type == "ada_norm_zero": @@ -1081,7 +1218,7 @@ def forward( # "feed_forward_chunk_size" can be used to save memory ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) else: - ff_output = self.ff(norm_hidden_states) + ff_output = self.ff(norm_hidden_states, t=frame, h=height, w=width) if self.norm_type == "ada_norm_zero": ff_output = gate_mlp.unsqueeze(1) * ff_output elif self.norm_type == "ada_norm_single": diff --git a/opensora/models/diffusion/udit/rope.py b/opensora/models/diffusion/udit/rope.py new file mode 100644 index 000000000..b42096b9d --- /dev/null +++ b/opensora/models/diffusion/udit/rope.py @@ -0,0 +1,154 @@ +import torch + +class PositionGetter3D(object): + """ return positions of patches """ + + def __init__(self, ): + self.cache_positions = {} + + def __call__(self, b, t, h, w, device): + if not (t,h,w) in self.cache_positions: + x = torch.arange(w, device=device) + y = torch.arange(h, device=device) + z = torch.arange(t, device=device) + self.cache_positions[t,h,w] = torch.cartesian_prod(z, y, x) # (t, h, w, 3) + pos = self.cache_positions[t,h,w].view(1, t*h*w, 3).expand(b, -1, 3).clone() + return pos + + +class RoPE3D(torch.nn.Module): + + def __init__(self, freq=10000.0, F0=1.0, interpolation_scale_thw=(1, 1, 1)): + super().__init__() + self.base = freq + self.F0 = F0 + self.interpolation_scale_t = interpolation_scale_thw[0] + self.interpolation_scale_h = interpolation_scale_thw[1] + self.interpolation_scale_w = interpolation_scale_thw[2] + self.cache = {} + + def get_cos_sin(self, D, seq_len, device, dtype, interpolation_scale=1): + if (D, seq_len, device, dtype) not in self.cache: + inv_freq = 1.0 / (self.base ** (torch.arange(0, D, 2).float().to(device) / D)) + t = torch.arange(seq_len, device=device, dtype=inv_freq.dtype) / interpolation_scale + freqs = torch.einsum("i,j->ij", t, inv_freq).to(dtype) + freqs = torch.cat((freqs, freqs), dim=-1) + cos = freqs.cos() # (Seq, Dim) + sin = freqs.sin() + self.cache[D, seq_len, device, dtype] = (cos, sin) + return self.cache[D, seq_len, device, dtype] + + @staticmethod + def rotate_half(x): + x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2:] + return torch.cat((-x2, x1), dim=-1) + + def apply_rope1d(self, tokens, pos1d, cos, sin): + assert pos1d.ndim == 2 + cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] + sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] + return (tokens * cos) + (self.rotate_half(tokens) * sin) + + def forward(self, tokens, positions): + """ + input: + * tokens: batch_size x nheads x ntokens x dim + * positions: batch_size x ntokens x 3 (t, y and x position of each token) + output: + * tokens after appplying RoPE3D (batch_size x nheads x ntokens x dim) + """ + assert tokens.size(3) % 3 == 0, "number of dimensions should be a multiple of three" + D = tokens.size(3) // 3 + assert positions.ndim == 3 and positions.shape[-1] == 3 # Batch, Seq, 3 + cos_t, sin_t = self.get_cos_sin(D, int(positions[:, :, 0].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_t) + cos_y, sin_y = self.get_cos_sin(D, int(positions[:, :, 1].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_h) + cos_x, sin_x = self.get_cos_sin(D, int(positions[:, :, 2].max()) + 1, tokens.device, tokens.dtype, self.interpolation_scale_w) + # split features into three along the feature dimension, and apply rope1d on each half + t, y, x = tokens.chunk(3, dim=-1) + t = self.apply_rope1d(t, positions[:, :, 0], cos_t, sin_t) + y = self.apply_rope1d(y, positions[:, :, 1], cos_y, sin_y) + x = self.apply_rope1d(x, positions[:, :, 2], cos_x, sin_x) + tokens = torch.cat((t, y, x), dim=-1) + return tokens + + + +# import torch +# from einops import rearrange, repeat + +# class PositionGetter3D(object): +# """ return positions of patches """ + +# def __init__(self, ): +# self.cache_positions = {} + +# def __call__(self, b, t, h, w, device): +# if not (t,h,w) in self.cache_positions: +# x = torch.arange(w, device=device) +# y = torch.arange(h, device=device) +# z = torch.arange(t, device=device) +# positions = torch.cartesian_prod(z, y, x) # (t, h, w, 3) +# positions = rearrange(positions, 'n d -> d 1 n') +# positions = repeat(positions, 'd 1 n -> d b n', b=b).clone() +# poses = (positions[0], positions[1], positions[2]) +# max_pos = (int(poses[0].max()), int(poses[1].max()), int(poses[2].max())) +# self.cache_positions[t,h,w] = (poses, max_pos) +# pos = self.cache_positions[t,h,w] +# return pos + + +# class RoPE3D(torch.nn.Module): + +# def __init__(self, freq=10000.0, F0=1.0, interpolation_scale_thw=(1, 1, 1)): +# super().__init__() +# self.base = freq +# self.F0 = F0 +# self.interpolation_scale_t = interpolation_scale_thw[0] +# self.interpolation_scale_h = interpolation_scale_thw[1] +# self.interpolation_scale_w = interpolation_scale_thw[2] +# self.cache = {} + +# def get_cos_sin(self, D, seq_len, device, dtype, interpolation_scale=1): +# if (D, seq_len, device, dtype) not in self.cache: +# inv_freq = 1.0 / (self.base ** (torch.arange(0, D, 2).float().to(device) / D)) +# t = torch.arange(seq_len, device=device, dtype=inv_freq.dtype) / interpolation_scale +# freqs = torch.einsum("i,j->ij", t, inv_freq).to(dtype) +# freqs = torch.cat((freqs, freqs), dim=-1) +# cos = freqs.cos() # (Seq, Dim) +# sin = freqs.sin() +# self.cache[D, seq_len, device, dtype] = (cos, sin) +# return self.cache[D, seq_len, device, dtype] + +# @staticmethod +# def rotate_half(x): +# x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2:] +# return torch.cat((-x2, x1), dim=-1) + +# def apply_rope1d(self, tokens, pos1d, cos, sin): +# assert pos1d.ndim == 2 +# cos = torch.nn.functional.embedding(pos1d, cos)[:, None, :, :] +# sin = torch.nn.functional.embedding(pos1d, sin)[:, None, :, :] +# return (tokens * cos) + (self.rotate_half(tokens) * sin) + +# def forward(self, tokens, positions): +# """ +# input: +# * tokens: batch_size x nheads x ntokens x dim +# * positions: batch_size x ntokens x 3 (t, y and x position of each token) +# output: +# * tokens after appplying RoPE3D (batch_size x nheads x ntokens x dim) +# """ +# assert tokens.size(3) % 3 == 0, "number of dimensions should be a multiple of three" +# D = tokens.size(3) // 3 +# poses, max_pos = positions +# assert len(poses) == 3 and poses[0].ndim == 2 # Batch, Seq, 3 +# cos_t, sin_t = self.get_cos_sin(D, max_pos[0] + 1, tokens.device, tokens.dtype, self.interpolation_scale_t) +# cos_y, sin_y = self.get_cos_sin(D, max_pos[1] + 1, tokens.device, tokens.dtype, self.interpolation_scale_h) +# cos_x, sin_x = self.get_cos_sin(D, max_pos[2] + 1, tokens.device, tokens.dtype, self.interpolation_scale_w) +# # split features into three along the feature dimension, and apply rope1d on each half +# t, y, x = tokens.chunk(3, dim=-1) +# t = self.apply_rope1d(t, poses[0], cos_t, sin_t) +# y = self.apply_rope1d(y, poses[1], cos_y, sin_y) +# x = self.apply_rope1d(x, poses[2], cos_x, sin_x) +# tokens = torch.cat((t, y, x), dim=-1) +# return tokens diff --git a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py index 49271cc03..9ec61fd10 100644 --- a/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py +++ b/opensora/models/diffusion/udit_ultra/modeling_udit_ultra.py @@ -162,34 +162,34 @@ def _init_patched_inputs(self, norm_type): # down_factor = down_factor if isinstance(self.config.down_factor, list) else [self.config.down_factor] * 5 # down_factor = [2] * len(self.config.depth) is_video_model = False - if self.config.downsampler is not None and len(self.config.downsampler) == 9: - is_video_model = True # to init weight from image - self.pos_embed = OverlapPatchEmbed3D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - patch_size_t=self.config.patch_size_t, - patch_size=self.config.patch_size, - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - use_abs_pos=not self.config.use_rope, - ) - elif self.config.downsampler is not None and len(self.config.downsampler) == 7: - is_video_model = False - self.pos_embed = OverlapPatchEmbed2D( - num_frames=self.config.sample_size_t, - height=self.config.sample_size[0], - width=self.config.sample_size[1], - patch_size_t=self.config.patch_size_t, - patch_size=self.config.patch_size, - in_channels=self.in_channels, - embed_dim=self.inner_dim, - interpolation_scale=interpolation_scale, - interpolation_scale_t=interpolation_scale_t, - use_abs_pos=not self.config.use_rope, - ) + # if self.config.downsampler is not None and len(self.config.downsampler) == 9: + # is_video_model = True # to init weight from image + # self.pos_embed = OverlapPatchEmbed3D( + # num_frames=self.config.sample_size_t, + # height=self.config.sample_size[0], + # width=self.config.sample_size[1], + # patch_size_t=self.config.patch_size_t, + # patch_size=self.config.patch_size, + # in_channels=self.in_channels, + # embed_dim=self.inner_dim, + # interpolation_scale=interpolation_scale, + # interpolation_scale_t=interpolation_scale_t, + # use_abs_pos=not self.config.use_rope, + # ) + # elif self.config.downsampler is not None and len(self.config.downsampler) == 7: + # is_video_model = False + self.pos_embed = OverlapPatchEmbed2D( + num_frames=self.config.sample_size_t, + height=self.config.sample_size[0], + width=self.config.sample_size[1], + patch_size_t=self.config.patch_size_t, + patch_size=self.config.patch_size, + in_channels=self.in_channels, + embed_dim=self.inner_dim, + interpolation_scale=interpolation_scale, + interpolation_scale_t=interpolation_scale_t, + use_abs_pos=not self.config.use_rope, + ) # layer_thw = [[self.config.sample_size_t//self.config.patch_size_t, # (self.config.sample_size[0] + self.config.sample_size[0] % (self.config.patch_size*2))//self.config.patch_size, # (self.config.sample_size[1] + self.config.sample_size[1] % (self.config.patch_size*2))//self.config.patch_size]] diff --git a/opensora/models/diffusion/udit_ultra/modules.py b/opensora/models/diffusion/udit_ultra/modules.py index 6c393ea0b..22deec9de 100644 --- a/opensora/models/diffusion/udit_ultra/modules.py +++ b/opensora/models/diffusion/udit_ultra/modules.py @@ -368,17 +368,16 @@ def forward(self, latent, num_frames): latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) - image_latent = latent + assert num_frames == latent.shape[1] if self.use_abs_pos: # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() temp_pos_embed = temp_pos_embed.unsqueeze(2) - video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) if video_latent is not None and video_latent.numel() > 0 else None - image_latent = (image_latent + temp_pos_embed[:, :1]).to(image_latent.dtype) if image_latent is not None and image_latent.numel() > 0 else None + latent = (latent + temp_pos_embed).to(latent.dtype) - image_latent = rearrange(image_latent, 'b t n c -> (b t) n c') if image_latent is not None and image_latent.numel() > 0 else None - - return image_latent + latent = rearrange(latent, 'b t n c -> b (t n) c') + return latent + class Attention(Attention_): def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_thw, **kwags): diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 80d3c30d6..1474319b8 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -11,10 +11,8 @@ def __init__(self, args, **kwargs): self.model_name = args.text_encoder_name if 'mt5' in self.model_name: from transformers import MT5EncoderModel - print(f"Loading MT5 model: {self.model_name}") - print(f"cache_dir: {args.cache_dir}") - self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel diff --git a/opensora/sample/pipeline_opensora.py b/opensora/sample/pipeline_opensora.py index 84cc4ae96..86ba919d8 100644 --- a/opensora/sample/pipeline_opensora.py +++ b/opensora/sample/pipeline_opensora.py @@ -786,7 +786,7 @@ def __call__( # compute previous image: x_t -> x_t-1 latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] - + # print(f'latents_{i}_{t}', torch.max(latents), torch.min(latents), torch.mean(latents), torch.std(latents)) # call the callback, if provided if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): progress_bar.update() @@ -812,13 +812,9 @@ def __call__( def decode_latents(self, latents): - device = torch.cuda.current_device() - # if npu_config is not None: - # npu_config.print_tensor_stats(latents, f"before vae", rank=0) - self.vae = self.vae.to(device) - video = self.vae.decode(latents.to(self.vae.vae.dtype).to(device)) - # if npu_config is not None: - # npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) + # print(f'before vae decode', torch.max(latents).item(), torch.min(latents).item(), torch.mean(latents).item(), torch.std(latents).item()) + video = self.vae.decode(latents.to(self.vae.vae.dtype)) + # print(f'after vae decode', torch.max(video).item(), torch.min(video).item(), torch.mean(video).item(), torch.std(video).item()) video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 return video diff --git a/opensora/sample/pipeline_opensora_sp.py b/opensora/sample/pipeline_opensora_sp.py new file mode 100644 index 000000000..c89b2edc8 --- /dev/null +++ b/opensora/sample/pipeline_opensora_sp.py @@ -0,0 +1,832 @@ +# Copyright 2024 PixArt-Sigma Authors and The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import html +import inspect +import re +import urllib.parse as ul +from typing import Callable, List, Optional, Tuple, Union +import math +import torch +from transformers import T5EncoderModel, T5Tokenizer + +from diffusers.models import AutoencoderKL, Transformer2DModel +from diffusers.schedulers import DPMSolverMultistepScheduler +from diffusers.utils import ( + BACKENDS_MAPPING, + deprecate, + is_bs4_available, + is_ftfy_available, + logging, + replace_example_docstring, +) +from diffusers.utils.torch_utils import randn_tensor +from diffusers.pipelines.pipeline_utils import DiffusionPipeline, ImagePipelineOutput + +try: + import torch_npu + from opensora.acceleration.parallel_states import get_sequence_parallel_state, hccl_info +except: + torch_npu = None + from opensora.utils.parallel_states import get_sequence_parallel_state, nccl_info + +logger = logging.get_logger(__name__) # pylint: disable=invalid-name + +if is_bs4_available(): + from bs4 import BeautifulSoup + +if is_ftfy_available(): + import ftfy + + +EXAMPLE_DOC_STRING = """ + Examples: + ```py + >>> import torch + >>> from diffusers import PixArtSigmaPipeline + + >>> # You can replace the checkpoint id with "PixArt-alpha/PixArt-Sigma-XL-2-512-MS" too. + >>> pipe = PixArtSigmaPipeline.from_pretrained( + ... "PixArt-alpha/PixArt-Sigma-XL-2-1024-MS", torch_dtype=torch.float16 + ... ) + >>> # Enable memory optimizations. + >>> # pipe.enable_model_cpu_offload() + + >>> prompt = "A small cactus with a happy face in the Sahara desert." + >>> image = pipe(prompt).images[0] + ``` +""" + + +# Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps +def retrieve_timesteps( + scheduler, + num_inference_steps: Optional[int] = None, + device: Optional[Union[str, torch.device]] = None, + timesteps: Optional[List[int]] = None, + **kwargs, +): + """ + Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles + custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`. + + Args: + scheduler (`SchedulerMixin`): + The scheduler to get timesteps from. + num_inference_steps (`int`): + The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps` + must be `None`. + device (`str` or `torch.device`, *optional*): + The device to which the timesteps should be moved to. If `None`, the timesteps are not moved. + timesteps (`List[int]`, *optional*): + Custom timesteps used to support arbitrary spacing between timesteps. If `None`, then the default + timestep spacing strategy of the scheduler is used. If `timesteps` is passed, `num_inference_steps` + must be `None`. + + Returns: + `Tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the + second element is the number of inference steps. + """ + if timesteps is not None: + accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys()) + if not accepts_timesteps: + raise ValueError( + f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom" + f" timestep schedules. Please check whether you are using the correct scheduler." + ) + scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs) + timesteps = scheduler.timesteps + num_inference_steps = len(timesteps) + else: + scheduler.set_timesteps(num_inference_steps, device=device, **kwargs) + timesteps = scheduler.timesteps + return timesteps, num_inference_steps + + +class OpenSoraPipeline(DiffusionPipeline): + r""" + Pipeline for text-to-image generation using PixArt-Sigma. + """ + + bad_punct_regex = re.compile( + r"[" + + "#®•©™&@·º½¾¿¡§~" + + r"\)" + + r"\(" + + r"\]" + + r"\[" + + r"\}" + + r"\{" + + r"\|" + + "\\" + + r"\/" + + r"\*" + + r"]{1,}" + ) # noqa + + _optional_components = ["tokenizer", "text_encoder"] + model_cpu_offload_seq = "text_encoder->transformer->vae" + + def __init__( + self, + tokenizer: T5Tokenizer, + text_encoder: T5EncoderModel, + vae: AutoencoderKL, + transformer: Transformer2DModel, + scheduler: DPMSolverMultistepScheduler, + ): + super().__init__() + + self.register_modules( + tokenizer=tokenizer, text_encoder=text_encoder, vae=vae, transformer=transformer, scheduler=scheduler + ) + + # self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) + + # Copied from diffusers.pipelines.pixart_alpha.pipeline_pixart_alpha.PixArtAlphaPipeline.encode_prompt + def encode_prompt( + self, + prompt: Union[str, List[str]], + do_classifier_free_guidance: bool = True, + negative_prompt: str = "", + num_images_per_prompt: int = 1, + device: Optional[torch.device] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + clean_caption: bool = False, + max_sequence_length: int = 120, + **kwargs, + ): + r""" + Encodes the prompt into text encoder hidden states. + + Args: + prompt (`str` or `List[str]`, *optional*): + prompt to be encoded + negative_prompt (`str` or `List[str]`, *optional*): + The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds` + instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`). For + PixArt-Alpha, this should be "". + do_classifier_free_guidance (`bool`, *optional*, defaults to `True`): + whether to use classifier free guidance or not + num_images_per_prompt (`int`, *optional*, defaults to 1): + number of images that should be generated per prompt + device: (`torch.device`, *optional*): + torch device to place the resulting embeddings on + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. For PixArt-Alpha, it's should be the embeddings of the "" + string. + clean_caption (`bool`, defaults to `False`): + If `True`, the function will preprocess and clean the provided caption before encoding. + max_sequence_length (`int`, defaults to 120): Maximum sequence length to use for the prompt. + """ + + if "mask_feature" in kwargs: + deprecation_message = "The use of `mask_feature` is deprecated. It is no longer used in any computation and that doesn't affect the end results. It will be removed in a future version." + deprecate("mask_feature", "1.0.0", deprecation_message, standard_warn=False) + + if device is None: + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') + + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + + # See Section 3.1. of the paper. + max_length = max_sequence_length + + if prompt_embeds is None: + prompt = self._text_preprocessing(prompt, clean_caption=clean_caption) + text_inputs = self.tokenizer( + prompt, + padding="max_length", + max_length=max_length, + truncation=True, + add_special_tokens=True, + return_tensors="pt", + ) + text_input_ids = text_inputs.input_ids + untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids + + if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal( + text_input_ids, untruncated_ids + ): + removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_length - 1 : -1]) + logger.warning( + "The following part of your input was truncated because CLIP can only handle sequences up to" + f" {max_length} tokens: {removed_text}" + ) + + prompt_attention_mask = text_inputs.attention_mask + prompt_attention_mask = prompt_attention_mask.to(device) + + prompt_embeds = self.text_encoder(text_input_ids.to(device), attention_mask=prompt_attention_mask) + prompt_embeds = prompt_embeds[0] + + if self.text_encoder is not None: + dtype = self.text_encoder.dtype + elif self.transformer is not None: + dtype = self.transformer.dtype + else: + dtype = None + + prompt_embeds = prompt_embeds.to(dtype=dtype, device=device) + + bs_embed, seq_len, _ = prompt_embeds.shape + # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method + prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1) + prompt_embeds = prompt_embeds.view(bs_embed * num_images_per_prompt, seq_len, -1) + prompt_attention_mask = prompt_attention_mask.view(bs_embed, -1) + prompt_attention_mask = prompt_attention_mask.repeat(num_images_per_prompt, 1) + + # get unconditional embeddings for classifier free guidance + if do_classifier_free_guidance and negative_prompt_embeds is None: + uncond_tokens = [negative_prompt] * batch_size + uncond_tokens = self._text_preprocessing(uncond_tokens, clean_caption=clean_caption) + max_length = prompt_embeds.shape[1] + uncond_input = self.tokenizer( + uncond_tokens, + padding="max_length", + max_length=max_length, + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors="pt", + ) + negative_prompt_attention_mask = uncond_input.attention_mask + negative_prompt_attention_mask = negative_prompt_attention_mask.to(device) + + negative_prompt_embeds = self.text_encoder( + uncond_input.input_ids.to(device), attention_mask=negative_prompt_attention_mask + ) + negative_prompt_embeds = negative_prompt_embeds[0] + + if do_classifier_free_guidance: + # duplicate unconditional embeddings for each generation per prompt, using mps friendly method + seq_len = negative_prompt_embeds.shape[1] + + negative_prompt_embeds = negative_prompt_embeds.to(dtype=dtype, device=device) + + negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1) + negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1) + + negative_prompt_attention_mask = negative_prompt_attention_mask.view(bs_embed, -1) + negative_prompt_attention_mask = negative_prompt_attention_mask.repeat(num_images_per_prompt, 1) + else: + negative_prompt_embeds = None + negative_prompt_attention_mask = None + + return prompt_embeds, prompt_attention_mask, negative_prompt_embeds, negative_prompt_attention_mask + + # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_extra_step_kwargs + def prepare_extra_step_kwargs(self, generator, eta): + # prepare extra kwargs for the scheduler step, since not all schedulers have the same signature + # eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers. + # eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502 + # and should be between [0, 1] + + accepts_eta = "eta" in set(inspect.signature(self.scheduler.step).parameters.keys()) + extra_step_kwargs = {} + if accepts_eta: + extra_step_kwargs["eta"] = eta + + # check if the scheduler accepts generator + accepts_generator = "generator" in set(inspect.signature(self.scheduler.step).parameters.keys()) + if accepts_generator: + extra_step_kwargs["generator"] = generator + return extra_step_kwargs + + # Copied from diffusers.pipelines.pixart_alpha.pipeline_pixart_alpha.PixArtAlphaPipeline.check_inputs + def check_inputs( + self, + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds=None, + negative_prompt_embeds=None, + prompt_attention_mask=None, + negative_prompt_attention_mask=None, + ): + if num_frames <= 0: + raise ValueError(f"`num_frames` have to be positive but is {num_frames}.") + if height % 8 != 0 or width % 8 != 0: + raise ValueError(f"`height` and `width` have to be divisible by 8 but are {height} and {width}.") + + if (callback_steps is None) or ( + callback_steps is not None and (not isinstance(callback_steps, int) or callback_steps <= 0) + ): + raise ValueError( + f"`callback_steps` has to be a positive integer but is {callback_steps} of type" + f" {type(callback_steps)}." + ) + + if prompt is not None and prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to" + " only forward one of the two." + ) + elif prompt is None and prompt_embeds is None: + raise ValueError( + "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined." + ) + elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)): + raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}") + + if prompt is not None and negative_prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `prompt`: {prompt} and `negative_prompt_embeds`:" + f" {negative_prompt_embeds}. Please make sure to only forward one of the two." + ) + + if negative_prompt is not None and negative_prompt_embeds is not None: + raise ValueError( + f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:" + f" {negative_prompt_embeds}. Please make sure to only forward one of the two." + ) + + if prompt_embeds is not None and prompt_attention_mask is None: + raise ValueError("Must provide `prompt_attention_mask` when specifying `prompt_embeds`.") + + if negative_prompt_embeds is not None and negative_prompt_attention_mask is None: + raise ValueError("Must provide `negative_prompt_attention_mask` when specifying `negative_prompt_embeds`.") + + if prompt_embeds is not None and negative_prompt_embeds is not None: + if prompt_embeds.shape != negative_prompt_embeds.shape: + raise ValueError( + "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but" + f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`" + f" {negative_prompt_embeds.shape}." + ) + if prompt_attention_mask.shape != negative_prompt_attention_mask.shape: + raise ValueError( + "`prompt_attention_mask` and `negative_prompt_attention_mask` must have the same shape when passed directly, but" + f" got: `prompt_attention_mask` {prompt_attention_mask.shape} != `negative_prompt_attention_mask`" + f" {negative_prompt_attention_mask.shape}." + ) + + # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._text_preprocessing + def _text_preprocessing(self, text, clean_caption=False): + if clean_caption and not is_bs4_available(): + logger.warning(BACKENDS_MAPPING["bs4"][-1].format("Setting `clean_caption=True`")) + logger.warning("Setting `clean_caption` to False...") + clean_caption = False + + if clean_caption and not is_ftfy_available(): + logger.warning(BACKENDS_MAPPING["ftfy"][-1].format("Setting `clean_caption=True`")) + logger.warning("Setting `clean_caption` to False...") + clean_caption = False + + if not isinstance(text, (tuple, list)): + text = [text] + + def process(text: str): + if clean_caption: + text = self._clean_caption(text) + text = self._clean_caption(text) + else: + text = text.lower().strip() + return text + + return [process(t) for t in text] + + # Copied from diffusers.pipelines.deepfloyd_if.pipeline_if.IFPipeline._clean_caption + def _clean_caption(self, caption): + caption = str(caption) + caption = ul.unquote_plus(caption) + caption = caption.strip().lower() + caption = re.sub("", "person", caption) + # urls: + caption = re.sub( + r"\b((?:https?:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", # noqa + "", + caption, + ) # regex for urls + caption = re.sub( + r"\b((?:www:(?:\/{1,3}|[a-zA-Z0-9%])|[a-zA-Z0-9.\-]+[.](?:com|co|ru|net|org|edu|gov|it)[\w/-]*\b\/?(?!@)))", # noqa + "", + caption, + ) # regex for urls + # html: + caption = BeautifulSoup(caption, features="html.parser").text + + # @ + caption = re.sub(r"@[\w\d]+\b", "", caption) + + # 31C0—31EF CJK Strokes + # 31F0—31FF Katakana Phonetic Extensions + # 3200—32FF Enclosed CJK Letters and Months + # 3300—33FF CJK Compatibility + # 3400—4DBF CJK Unified Ideographs Extension A + # 4DC0—4DFF Yijing Hexagram Symbols + # 4E00—9FFF CJK Unified Ideographs + caption = re.sub(r"[\u31c0-\u31ef]+", "", caption) + caption = re.sub(r"[\u31f0-\u31ff]+", "", caption) + caption = re.sub(r"[\u3200-\u32ff]+", "", caption) + caption = re.sub(r"[\u3300-\u33ff]+", "", caption) + caption = re.sub(r"[\u3400-\u4dbf]+", "", caption) + caption = re.sub(r"[\u4dc0-\u4dff]+", "", caption) + # caption = re.sub(r"[\u4e00-\u9fff]+", "", caption) + ####################################################### + + # все виды тире / all types of dash --> "-" + caption = re.sub( + r"[\u002D\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E3B\u2E40\u301C\u3030\u30A0\uFE31\uFE32\uFE58\uFE63\uFF0D]+", # noqa + "-", + caption, + ) + + # кавычки к одному стандарту + caption = re.sub(r"[`´«»“”¨]", '"', caption) + caption = re.sub(r"[‘’]", "'", caption) + + # " + caption = re.sub(r""?", "", caption) + # & + caption = re.sub(r"&", "", caption) + + # ip adresses: + caption = re.sub(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", " ", caption) + + # article ids: + caption = re.sub(r"\d:\d\d\s+$", "", caption) + + # \n + caption = re.sub(r"\\n", " ", caption) + + # "#123" + caption = re.sub(r"#\d{1,3}\b", "", caption) + # "#12345.." + caption = re.sub(r"#\d{5,}\b", "", caption) + # "123456.." + caption = re.sub(r"\b\d{6,}\b", "", caption) + # filenames: + caption = re.sub(r"[\S]+\.(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)", "", caption) + + # + caption = re.sub(r"[\"\']{2,}", r'"', caption) # """AUSVERKAUFT""" + caption = re.sub(r"[\.]{2,}", r" ", caption) # """AUSVERKAUFT""" + + caption = re.sub(self.bad_punct_regex, r" ", caption) # ***AUSVERKAUFT***, #AUSVERKAUFT + caption = re.sub(r"\s+\.\s+", r" ", caption) # " . " + + # this-is-my-cute-cat / this_is_my_cute_cat + regex2 = re.compile(r"(?:\-|\_)") + if len(re.findall(regex2, caption)) > 3: + caption = re.sub(regex2, " ", caption) + + caption = ftfy.fix_text(caption) + caption = html.unescape(html.unescape(caption)) + + caption = re.sub(r"\b[a-zA-Z]{1,3}\d{3,15}\b", "", caption) # jc6640 + caption = re.sub(r"\b[a-zA-Z]+\d+[a-zA-Z]+\b", "", caption) # jc6640vc + caption = re.sub(r"\b\d+[a-zA-Z]+\d+\b", "", caption) # 6640vc231 + + caption = re.sub(r"(worldwide\s+)?(free\s+)?shipping", "", caption) + caption = re.sub(r"(free\s)?download(\sfree)?", "", caption) + caption = re.sub(r"\bclick\b\s(?:for|on)\s\w+", "", caption) + caption = re.sub(r"\b(?:png|jpg|jpeg|bmp|webp|eps|pdf|apk|mp4)(\simage[s]?)?", "", caption) + caption = re.sub(r"\bpage\s+\d+\b", "", caption) + + caption = re.sub(r"\b\d*[a-zA-Z]+\d+[a-zA-Z]+\d+[a-zA-Z\d]*\b", r" ", caption) # j2d1a2a... + + caption = re.sub(r"\b\d+\.?\d*[xх×]\d+\.?\d*\b", "", caption) + + caption = re.sub(r"\b\s+\:\s+", r": ", caption) + caption = re.sub(r"(\D[,\./])\b", r"\1 ", caption) + caption = re.sub(r"\s+", " ", caption) + + caption.strip() + + caption = re.sub(r"^[\"\']([\w\W]+)[\"\']$", r"\1", caption) + caption = re.sub(r"^[\'\_,\-\:;]", r"", caption) + caption = re.sub(r"[\'\_,\-\:\-\+]$", r"", caption) + caption = re.sub(r"^\.\S+$", "", caption) + return caption.strip() + + # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.StableDiffusionPipeline.prepare_latents + def prepare_latents(self, batch_size, num_channels_latents, num_frames, height, width, dtype, device, generator, latents=None): + shape = ( + batch_size, + num_channels_latents, + (math.ceil((int(num_frames) - 1) / self.vae.vae_scale_factor[0]) + 1) if int(num_frames) % 2 == 1 else math.ceil(int(num_frames) / self.vae.vae_scale_factor[0]), + math.ceil(int(height) / self.vae.vae_scale_factor[1]), + math.ceil(int(width) / self.vae.vae_scale_factor[2]), + ) + if isinstance(generator, list) and len(generator) != batch_size: + raise ValueError( + f"You have passed a list of generators of length {len(generator)}, but requested an effective batch" + f" size of {batch_size}. Make sure the batch size matches the length of the generators." + ) + + if latents is None: + latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype) + else: + latents = latents.to(device) + + # scale the initial noise by the standard deviation required by the scheduler + latents = latents * self.scheduler.init_noise_sigma + + + return latents + + @torch.no_grad() + @replace_example_docstring(EXAMPLE_DOC_STRING) + def __call__( + self, + prompt: Union[str, List[str]] = None, + negative_prompt: str = "", + num_inference_steps: int = 20, + timesteps: List[int] = None, + guidance_scale: float = 4.5, + num_images_per_prompt: Optional[int] = 1, + num_frames: Optional[int] = None, + height: Optional[int] = None, + width: Optional[int] = None, + eta: float = 0.0, + generator: Optional[Union[torch.Generator, List[torch.Generator]]] = None, + latents: Optional[torch.FloatTensor] = None, + prompt_embeds: Optional[torch.FloatTensor] = None, + prompt_attention_mask: Optional[torch.FloatTensor] = None, + negative_prompt_embeds: Optional[torch.FloatTensor] = None, + negative_prompt_attention_mask: Optional[torch.FloatTensor] = None, + output_type: Optional[str] = "pil", + return_dict: bool = True, + callback: Optional[Callable[[int, int, torch.FloatTensor], None]] = None, + callback_steps: int = 1, + clean_caption: bool = True, + use_resolution_binning: bool = True, + max_sequence_length: int = 300, + **kwargs, + ) -> Union[ImagePipelineOutput, Tuple]: + """ + Function invoked when calling the pipeline for generation. + + Args: + prompt (`str` or `List[str]`, *optional*): + The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`. + instead. + negative_prompt (`str` or `List[str]`, *optional*): + The prompt or prompts not to guide the image generation. If not defined, one has to pass + `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is + less than `1`). + num_inference_steps (`int`, *optional*, defaults to 100): + The number of denoising steps. More denoising steps usually lead to a higher quality image at the + expense of slower inference. + timesteps (`List[int]`, *optional*): + Custom timesteps to use for the denoising process. If not defined, equal spaced `num_inference_steps` + timesteps are used. Must be in descending order. + guidance_scale (`float`, *optional*, defaults to 4.5): + Guidance scale as defined in [Classifier-Free Diffusion Guidance](https://arxiv.org/abs/2207.12598). + `guidance_scale` is defined as `w` of equation 2. of [Imagen + Paper](https://arxiv.org/pdf/2205.11487.pdf). Guidance scale is enabled by setting `guidance_scale > + 1`. Higher guidance scale encourages to generate images that are closely linked to the text `prompt`, + usually at the expense of lower image quality. + num_images_per_prompt (`int`, *optional*, defaults to 1): + The number of images to generate per prompt. + height (`int`, *optional*, defaults to self.unet.config.sample_size): + The height in pixels of the generated image. + width (`int`, *optional*, defaults to self.unet.config.sample_size): + The width in pixels of the generated image. + eta (`float`, *optional*, defaults to 0.0): + Corresponds to parameter eta (η) in the DDIM paper: https://arxiv.org/abs/2010.02502. Only applies to + [`schedulers.DDIMScheduler`], will be ignored for others. + generator (`torch.Generator` or `List[torch.Generator]`, *optional*): + One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html) + to make generation deterministic. + latents (`torch.FloatTensor`, *optional*): + Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image + generation. Can be used to tweak the same generation with different prompts. If not provided, a latents + tensor will ge generated by sampling using the supplied random `generator`. + prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not + provided, text embeddings will be generated from `prompt` input argument. + prompt_attention_mask (`torch.FloatTensor`, *optional*): Pre-generated attention mask for text embeddings. + negative_prompt_embeds (`torch.FloatTensor`, *optional*): + Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not + provided, negative_prompt_embeds will be generated from `negative_prompt` input argument. + negative_prompt_attention_mask (`torch.FloatTensor`, *optional*): + Pre-generated attention mask for negative text embeddings. + output_type (`str`, *optional*, defaults to `"pil"`): + The output format of the generate image. Choose between + [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`. + return_dict (`bool`, *optional*, defaults to `True`): + Whether or not to return a [`~pipelines.stable_diffusion.IFPipelineOutput`] instead of a plain tuple. + callback (`Callable`, *optional*): + A function that will be called every `callback_steps` steps during inference. The function will be + called with the following arguments: `callback(step: int, timestep: int, latents: torch.FloatTensor)`. + callback_steps (`int`, *optional*, defaults to 1): + The frequency at which the `callback` function will be called. If not specified, the callback will be + called at every step. + clean_caption (`bool`, *optional*, defaults to `True`): + Whether or not to clean the caption before creating embeddings. Requires `beautifulsoup4` and `ftfy` to + be installed. If the dependencies are not installed, the embeddings will be created from the raw + prompt. + use_resolution_binning (`bool` defaults to `True`): + If set to `True`, the requested height and width are first mapped to the closest resolutions using + `ASPECT_RATIO_1024_BIN`. After the produced latents are decoded into images, they are resized back to + the requested resolution. Useful for generating non-square images. + max_sequence_length (`int` defaults to 120): Maximum sequence length to use with the `prompt`. + + Examples: + + Returns: + [`~pipelines.ImagePipelineOutput`] or `tuple`: + If `return_dict` is `True`, [`~pipelines.ImagePipelineOutput`] is returned, otherwise a `tuple` is + returned where the first element is a list with the generated images + """ + # 1. Check inputs. Raise error if not correct + num_frames = num_frames or self.transformer.config.sample_size_t * self.vae.vae_scale_factor[0] + height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] + width = width or self.transformer.config.sample_size[1] * self.vae.vae_scale_factor[2] + self.check_inputs( + prompt, + num_frames, + height, + width, + negative_prompt, + callback_steps, + prompt_embeds, + negative_prompt_embeds, + prompt_attention_mask, + negative_prompt_attention_mask, + ) + + # 2. Default height and width to transformer + if prompt is not None and isinstance(prompt, str): + batch_size = 1 + elif prompt is not None and isinstance(prompt, list): + batch_size = len(prompt) + else: + batch_size = prompt_embeds.shape[0] + # import ipdb;ipdb.set_trace() + device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') + + # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) + # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` + # corresponds to doing no classifier free guidance. + do_classifier_free_guidance = guidance_scale > 1.0 + + # 3. Encode input prompt + ( + prompt_embeds, + prompt_attention_mask, + negative_prompt_embeds, + negative_prompt_attention_mask, + ) = self.encode_prompt( + prompt, + do_classifier_free_guidance, + negative_prompt=negative_prompt, + num_images_per_prompt=num_images_per_prompt, + device=device, + prompt_embeds=prompt_embeds, + negative_prompt_embeds=negative_prompt_embeds, + prompt_attention_mask=prompt_attention_mask, + negative_prompt_attention_mask=negative_prompt_attention_mask, + clean_caption=clean_caption, + max_sequence_length=max_sequence_length, + ) + if do_classifier_free_guidance: + prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) + prompt_attention_mask = torch.cat([negative_prompt_attention_mask, prompt_attention_mask], dim=0) + + # 4. Prepare timesteps + timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device, timesteps) + + # 5. Prepare latents. + latent_channels = self.transformer.config.in_channels + latents = self.prepare_latents( + batch_size * num_images_per_prompt, + latent_channels, + (num_frames + nccl_info.world_size - 1) // nccl_info.world_size if get_sequence_parallel_state() else num_frames, + height, + width, + prompt_embeds.dtype, + device, + generator, + latents, + ) + + # 6. Prepare extra step kwargs. TODO: Logic should ideally just be moved out of the pipeline + extra_step_kwargs = self.prepare_extra_step_kwargs(generator, eta) + + # 6.1 Prepare micro-conditions. + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + + # 7. Denoising loop + num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0) + + with self.progress_bar(total=num_inference_steps) as progress_bar: + for i, t in enumerate(timesteps): + latent_model_input = torch.cat([latents] * 2) if do_classifier_free_guidance else latents + latent_model_input = self.scheduler.scale_model_input(latent_model_input, t) + + current_timestep = t + if not torch.is_tensor(current_timestep): + # TODO: this requires sync between CPU and GPU. So try to pass timesteps as tensors if you can + # This would be a good case for the `match` statement (Python 3.10+) + is_mps = latent_model_input.device.type == "mps" + if isinstance(current_timestep, float): + dtype = torch.float32 if is_mps else torch.float64 + else: + dtype = torch.int32 if is_mps else torch.int64 + current_timestep = torch.tensor([current_timestep], dtype=dtype, device=latent_model_input.device) + elif len(current_timestep.shape) == 0: + current_timestep = current_timestep[None].to(latent_model_input.device) + # broadcast to batch dimension in a way that's compatible with ONNX/Core ML + current_timestep = current_timestep.expand(latent_model_input.shape[0]) + + # import ipdb;ipdb.set_trace() + if prompt_embeds.ndim == 3: + prompt_embeds = prompt_embeds.unsqueeze(1) # b l d -> b 1 l d + if prompt_attention_mask.ndim == 2: + prompt_attention_mask = prompt_attention_mask.unsqueeze(1) # b l -> b 1 l + # prepare attention_mask. + # b c t h w -> b t h w + attention_mask = torch.ones_like(latent_model_input)[:, 0] + # predict noise model_output + noise_pred = self.transformer( + latent_model_input, + attention_mask=attention_mask, + encoder_hidden_states=prompt_embeds, + encoder_attention_mask=prompt_attention_mask, + timestep=current_timestep, + added_cond_kwargs=added_cond_kwargs, + return_dict=False, + )[0] + + # perform guidance + if do_classifier_free_guidance: + noise_pred_uncond, noise_pred_text = noise_pred.chunk(2) + noise_pred = noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond) + + # learned sigma + if self.transformer.config.out_channels // 2 == latent_channels: + noise_pred = noise_pred.chunk(2, dim=1)[0] + else: + noise_pred = noise_pred + + # compute previous image: x_t -> x_t-1 + latents = self.scheduler.step(noise_pred, t, latents, **extra_step_kwargs, return_dict=False)[0] + # print(f'latents_{i}_{t}', torch.max(latents), torch.min(latents), torch.mean(latents), torch.std(latents)) + + # call the callback, if provided + if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0): + progress_bar.update() + if callback is not None and i % callback_steps == 0: + step_idx = i // getattr(self.scheduler, "order", 1) + callback(step_idx, t, latents) + # import ipdb;ipdb.set_trace() + # latents = latents.squeeze(2) + if get_sequence_parallel_state(): + latents_shape = list(latents.shape) + full_shape = [latents_shape[0] * nccl_info.world_size] + latents_shape[1:] + all_latents = torch.zeros(full_shape, dtype=latents.dtype, device=latents.device) + torch.distributed.all_gather_into_tensor(all_latents, latents) + latents_list = list(all_latents.chunk(nccl_info.world_size, dim=0)) + latents = torch.cat(latents_list, dim=2) + + if not output_type == "latent": + # b t h w c + image = self.decode_latents(latents) + image = image[:, :num_frames, :height, :width] + else: + image = latents + + # Offload all models + self.maybe_free_model_hooks() + + if not return_dict: + return (image,) + + return ImagePipelineOutput(images=image) + + + def decode_latents(self, latents): + # print(f'before vae decode', torch.max(latents).item(), torch.min(latents).item(), torch.mean(latents).item(), torch.std(latents).item()) + video = self.vae.decode(latents.to(self.vae.vae.dtype)) + # print(f'after vae decode', torch.max(video).item(), torch.min(video).item(), torch.mean(video).item(), torch.std(video).item()) + video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c + # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 + return video diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index 52981b2a7..ec0687e5b 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -35,6 +35,7 @@ def main(args): # torch.manual_seed(args.seed) + # torch.backends.cuda.matmul.allow_tf32 = False weight_dtype = torch.bfloat16 device = torch.device(args.device) @@ -44,6 +45,15 @@ def main(args): if args.enable_tiling: vae.vae.enable_tiling() vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae.tile_sample_min_size = 512 + vae.vae.tile_latent_min_size = 64 + vae.vae.tile_sample_min_size_t = 29 + vae.vae.tile_latent_min_size_t = 8 + if args.save_memory: + vae.vae.tile_sample_min_size = 256 + vae.vae.tile_latent_min_size = 32 + vae.vae.tile_sample_min_size_t = 29 + vae.vae.tile_latent_min_size_t = 8 vae.vae_scale_factor = ae_stride_config[args.ae] # if args.model_3d: @@ -51,15 +61,16 @@ def main(args): # else: # transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - if args.model_3d: + if args.model_type == 'dit': transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - # transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, ignore_mismatched_sizes=True, - # low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) + elif args.model_type == 'udit': + transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, ignore_mismatched_sizes=True, + low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) - # ckpt = torch.load('480p_73000_ema_k3_p1_repeat_wusun.pt') + # ckpt = torch.load('/storage/ongoing/new/image2video_weight/480p_73000_ema_ds_k3_p1_repeat_lowsize2.pt') # transformer_model.load_state_dict(ckpt) # text_encoder = UMT5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) @@ -115,7 +126,20 @@ def main(args): scheduler=scheduler, transformer=transformer_model) pipeline.to(device) - + if args.compile: + # 5% https://github.com/siliconflow/onediff/tree/main/src/onediff/infer_compiler/backends/nexfort + options = '{"mode": "max-optimize:max-autotune:freezing:benchmark:low-precision", \ + "memory_format": "channels_last", "options": {"inductor.optimize_linear_epilogue": false, \ + "triton.fuse_attention_allow_fp16_reduction": false}}' + # options = '{"mode": "max-autotune", "memory_format": "channels_last", \ + # "options": {"inductor.optimize_linear_epilogue": false, "triton.fuse_attention_allow_fp16_reduction": false}}' + from onediffx import compile_pipe + pipeline = compile_pipe( + pipeline, backend="nexfort", options=options, fuse_qkv_projections=True + ) + + # 4% + # pipeline.transformer = torch.compile(pipeline.transformer) if not os.path.exists(args.save_img_path): os.makedirs(args.save_img_path) @@ -126,14 +150,32 @@ def main(args): text_prompt = open(args.text_prompt[0], 'r').readlines() text_prompt = [i.strip() for i in text_prompt] - positive_prompt = "(masterpiece), (best quality), (ultra-detailed), {}. emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, sharp focus, high budget, cinemascope, moody, epic, gorgeous" - negative_prompt = """nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry, - """ + positive_prompt = """ + (masterpiece), (best quality), (ultra-detailed), (unwatermarked), + {}. + emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, + sharp focus, high budget, cinemascope, moody, epic, gorgeous + """ + + negative_prompt = """ + nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, + low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry. + """ + + # positive_prompt = """ + # (masterpiece), (best quality), (ultra-detailed), + # {}. + # emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, + # sharp focus, high budget, cinemascope, moody, epic, gorgeous + # """ + + # negative_prompt = """ + # disfigured, poorly drawn face, longbody, lowres, bad anatomy, bad hands, missing fingers, cropped, worst quality, low quality + # """ + video_grids = [] for idx, prompt in enumerate(text_prompt): - videos = pipeline( - positive_prompt.format(prompt), - # prompt, + videos = pipeline(positive_prompt.format(prompt), negative_prompt=negative_prompt, num_frames=args.num_frames, height=args.height, @@ -194,8 +236,9 @@ def main(args): parser.add_argument("--text_prompt", nargs='+') parser.add_argument('--tile_overlap_factor', type=float, default=0.125) parser.add_argument('--enable_tiling', action='store_true') - parser.add_argument('--model_3d', action='store_true') - parser.add_argument('--enable_stable_fp32', action='store_true') + parser.add_argument('--compile', action='store_true') + parser.add_argument('--model_type', type=str, default="udit", choices=['dit', 'udit', 'latte']) + parser.add_argument('--save_memory', action='store_true') args = parser.parse_args() main(args) \ No newline at end of file diff --git a/opensora/sample/sample_t2v_ddp.py b/opensora/sample/sample_t2v_ddp.py new file mode 100644 index 000000000..0cc720838 --- /dev/null +++ b/opensora/sample/sample_t2v_ddp.py @@ -0,0 +1,328 @@ +import math +import os +import torch +import argparse +import torchvision +import torch.distributed as dist + +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler +from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel +from omegaconf import OmegaConf +from torchvision.utils import save_image +from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer, MT5EncoderModel + +import os, sys + +from opensora.models.ae import ae_stride_config, getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.udit.modeling_udit import UDiTT2V +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V +from opensora.models.diffusion.latte.modeling_latte import LatteT2V + +from opensora.models.text_encoder import get_text_enc +from opensora.utils.utils import save_video_grid + +from opensora.sample.pipeline_opensora import OpenSoraPipeline + +import imageio + +try: + import torch_npu + from opensora.npu_config import npu_config +except: + torch_npu = None + npu_config = None + pass +import time + + + +def load_t2v_checkpoint(model_path): + if args.model_type == 'udit': + transformer_model = UDiTT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) + elif args.model_type == 'dit': + transformer_model = OpenSoraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) + else: + transformer_model = LatteT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, + device_map=None, torch_dtype=weight_dtype) + # print(transformer_model.config) + + # set eval mode + transformer_model.eval() + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model).to(device) + + if args.compile: + # 5% https://github.com/siliconflow/onediff/tree/main/src/onediff/infer_compiler/backends/nexfort + # options = '{"mode": "max-optimize:max-autotune:freezing:benchmark:low-precision", \ + # "memory_format": "channels_last", "options": {"inductor.optimize_linear_epilogue": false, \ + # "triton.fuse_attention_allow_fp16_reduction": false}}' + # # options = '{"mode": "max-autotune", "memory_format": "channels_last", \ + # # "options": {"inductor.optimize_linear_epilogue": false, "triton.fuse_attention_allow_fp16_reduction": false}}' + # from onediffx import compile_pipe + # pipeline = compile_pipe( + # pipeline, backend="nexfort", options=options, fuse_qkv_projections=True + # ) + + # 4% + pipeline.transformer = torch.compile(pipeline.transformer) + return pipeline + + +def get_latest_path(): + # Get the most recent checkpoint + dirs = os.listdir(args.model_path) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + return path + + +def run_model_and_save_images(pipeline, model_path): + video_grids = [] + if not isinstance(args.text_prompt, list): + args.text_prompt = [args.text_prompt] + if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): + text_prompt = open(args.text_prompt[0], 'r').readlines() + args.text_prompt = [i.strip() for i in text_prompt] + + checkpoint_name = f"{os.path.basename(model_path)}" + + positive_prompt = """ + (masterpiece), (best quality), (ultra-detailed), (unwatermarked), + {}. + emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, + sharp focus, high budget, cinemascope, moody, epic, gorgeous + """ + + negative_prompt = """ + nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, + low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry. + """ + + for index, prompt in enumerate(args.text_prompt): + if index % world_size != local_rank: + continue + # print('Processing the ({}) prompt, device ({})'.format(prompt, device)) + videos = pipeline(positive_prompt.format(prompt), + negative_prompt=negative_prompt, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device, + max_sequence_length=args.max_sequence_length, + ).images + print(videos.shape) + try: + if args.num_frames == 1: + videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, + f'{model_path}/{args.sample_method}_{index}_{checkpoint_name}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=1, normalize=True, value_range=(0, 1)) # t c h w + + else: + imageio.mimwrite( + os.path.join( + args.save_img_path, + f'{model_path}/{args.sample_method}_{index}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' + ), videos[0], + fps=args.fps, quality=6, codec='libx264', + output_params=['-threads', '20']) # highest quality is 10, lowest is 0 + except: + print('Error when saving {}'.format(prompt)) + video_grids.append(videos) + dist.barrier() + video_grids = torch.cat(video_grids, dim=0).cuda() + shape = list(video_grids.shape) + shape[0] *= world_size + gathered_tensor = torch.zeros(shape, dtype=video_grids.dtype, device=device) + dist.all_gather_into_tensor(gathered_tensor, video_grids.contiguous()) + video_grids = gathered_tensor.cpu() + + # video_grids = video_grids.repeat(world_size, 1, 1, 1) + # output = torch.zeros(video_grids.shape, dtype=video_grids.dtype, device=device) + # dist.all_to_all_single(output, video_grids) + # video_grids = output.cpu() + def get_file_name(): + return os.path.join(args.save_img_path, + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') + + if local_rank == 0: + if args.num_frames == 1: + save_image(video_grids / 255.0, get_file_name(), + nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) + else: + video_grids = save_video_grid(video_grids) + imageio.mimwrite(get_file_name(), video_grids, fps=args.fps, quality=6, codec='libx264', + output_params=['-threads', '20']) + + print('save path {}'.format(args.save_img_path)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') + parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") + parser.add_argument("--guidance_scale", type=float, default=7.5) + parser.add_argument("--sample_method", type=str, default="PNDM") + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument("--fps", type=int, default=24) + parser.add_argument("--max_sequence_length", type=int, default=512) + parser.add_argument("--text_prompt", nargs='+') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--compile', action='store_true') + parser.add_argument('--model_type', type=str, default="dit", choices=['dit', 'udit', 'latte']) + parser.add_argument('--save_memory', action='store_true') + args = parser.parse_args() + + if torch_npu is not None: + npu_config.print_msg(args) + + # 初始化分布式环境 + local_rank = int(os.getenv('RANK', 0)) + world_size = int(os.getenv('WORLD_SIZE', 1)) + print('world_size', world_size) + if torch_npu is not None and npu_config.on_npu: + torch_npu.npu.set_device(local_rank) + else: + torch.cuda.set_device(local_rank) + dist.init_process_group(backend='nccl', init_method='env://', world_size=world_size, rank=local_rank) + + # torch.manual_seed(args.seed) + weight_dtype = torch.bfloat16 + device = torch.cuda.current_device() + # print(11111111111111111111, local_rank, device) + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae.tile_sample_min_size = 512 + vae.vae.tile_latent_min_size = 64 + vae.vae.tile_sample_min_size_t = 29 + vae.vae.tile_latent_min_size_t = 8 + if args.save_memory: + vae.vae.tile_sample_min_size = 256 + vae.vae.tile_latent_min_size = 32 + vae.vae.tile_sample_min_size_t = 29 + vae.vae.tile_latent_min_size_t = 8 + vae.vae_scale_factor = ae_stride_config[args.ae] + + text_encoder = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", + cache_dir=args.cache_dir, low_cpu_mem_usage=True, + torch_dtype=weight_dtype).to(device) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", + cache_dir=args.cache_dir) + + # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, + # low_cpu_mem_usage=True, torch_dtype=weight_dtype).to(device) + # tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # set eval mode + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler(clip_sample=False) + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler(clip_sample=False) + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + + if not os.path.exists(args.save_img_path): + os.makedirs(args.save_img_path, exist_ok=True) + + if args.num_frames == 1: + video_length = 1 + ext = 'jpg' + else: + ext = 'mp4' + + latest_path = None + save_img_path = args.save_img_path + while True: + cur_path = get_latest_path() + # print(cur_path, latest_path) + if cur_path == latest_path: + time.sleep(5) + continue + + time.sleep(1) + latest_path = cur_path + os.makedirs(os.path.join(args.save_img_path, latest_path), exist_ok=True) + if npu_config is not None: + npu_config.print_msg(f"The latest_path is {latest_path}") + else: + print(f"The latest_path is {latest_path}") + full_path = f"{args.model_path}/{latest_path}/model_ema" + # full_path = f"{args.model_path}/{latest_path}/model" + try: + pipeline = load_t2v_checkpoint(full_path) + except: + time.sleep(100) + pipeline = load_t2v_checkpoint(full_path) + # print('load model') + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = "/home/image_data/npu_profiling_t2v" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=10000, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + run_model_and_save_images(pipeline, latest_path) + prof.step() + else: + # print('gpu') + run_model_and_save_images(pipeline, latest_path) diff --git a/opensora/sample/sample_t2v_sp.py b/opensora/sample/sample_t2v_sp.py new file mode 100644 index 000000000..61a25d0e4 --- /dev/null +++ b/opensora/sample/sample_t2v_sp.py @@ -0,0 +1,318 @@ +import math +import os +import torch +import argparse +import torchvision +import torch.distributed as dist + +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from diffusers.schedulers.scheduling_dpmsolver_singlestep import DPMSolverSinglestepScheduler +from diffusers.models import AutoencoderKL, AutoencoderKLTemporalDecoder, Transformer2DModel +from omegaconf import OmegaConf +from torchvision.utils import save_image +from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer, MT5EncoderModel + +import os, sys + +from opensora.models.ae import ae_stride_config, getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.udit.modeling_udit import UDiTT2V +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V +from opensora.models.diffusion.latte.modeling_latte import LatteT2V + +from opensora.models.text_encoder import get_text_enc +from opensora.utils.utils import save_video_grid + +from opensora.sample.pipeline_opensora_sp import OpenSoraPipeline + +import imageio + +try: + import torch_npu + from opensora.npu_config import npu_config + from opensora.acceleration.parallel_states import initialize_sequence_parallel_state, hccl_info +except: + torch_npu = None + npu_config = None + from opensora.utils.parallel_states import initialize_sequence_parallel_state, nccl_info + pass +import time + + + +def load_t2v_checkpoint(model_path): + if args.model_type == 'udit': + transformer_model = UDiTT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) + elif args.model_type == 'dit': + transformer_model = OpenSoraT2V.from_pretrained(model_path, cache_dir=args.cache_dir, + low_cpu_mem_usage=False, device_map=None, + torch_dtype=weight_dtype) + else: + transformer_model = LatteT2V.from_pretrained(model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, + device_map=None, torch_dtype=weight_dtype) + # print(transformer_model.config) + + # set eval mode + transformer_model.eval() + pipeline = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer_model).to(device) + + + if args.compile: + # 5% https://github.com/siliconflow/onediff/tree/main/src/onediff/infer_compiler/backends/nexfort + # options = '{"mode": "max-optimize:max-autotune:freezing:benchmark:low-precision", \ + # "memory_format": "channels_last", "options": {"inductor.optimize_linear_epilogue": false, \ + # "triton.fuse_attention_allow_fp16_reduction": false}}' + # # options = '{"mode": "max-autotune", "memory_format": "channels_last", \ + # # "options": {"inductor.optimize_linear_epilogue": false, "triton.fuse_attention_allow_fp16_reduction": false}}' + # from onediffx import compile_pipe + # pipeline = compile_pipe( + # pipeline, backend="nexfort", options=options, fuse_qkv_projections=True + # ) + + # 4% + pipeline.transformer = torch.compile(pipeline.transformer) + return pipeline + + +def get_latest_path(): + # Get the most recent checkpoint + dirs = os.listdir(args.model_path) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + return path + + +def run_model_and_save_images(pipeline, model_path): + video_grids = [] + if not isinstance(args.text_prompt, list): + args.text_prompt = [args.text_prompt] + if len(args.text_prompt) == 1 and args.text_prompt[0].endswith('txt'): + text_prompt = open(args.text_prompt[0], 'r').readlines() + args.text_prompt = [i.strip() for i in text_prompt] + + checkpoint_name = f"{os.path.basename(model_path)}" + + positive_prompt = """ + (masterpiece), (best quality), (ultra-detailed), (unwatermarked), + {}. + emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, + sharp focus, high budget, cinemascope, moody, epic, gorgeous + """ + + negative_prompt = """ + nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, + low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry. + """ + + for index, prompt in enumerate(args.text_prompt): + # if index % world_size != local_rank: + # continue + # print('Processing the ({}) prompt, device ({})'.format(prompt, device)) + videos = pipeline(positive_prompt.format(prompt), + negative_prompt=negative_prompt, + num_frames=args.num_frames, + height=args.height, + width=args.width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=args.device, + max_sequence_length=args.max_sequence_length, + ).images + print(videos.shape) + + if nccl_info.rank <= 0: + try: + if args.num_frames == 1: + videos = videos[:, 0].permute(0, 3, 1, 2) # b t h w c -> b c h w + save_image(videos / 255.0, os.path.join(args.save_img_path, + f'{args.sample_method}_{index}_{checkpoint_name}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=1, normalize=True, value_range=(0, 1)) # t c h w + + else: + imageio.mimwrite( + os.path.join( + args.save_img_path, + f'{args.sample_method}_{index}_{checkpoint_name}__gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}' + ), videos[0], + fps=args.fps, quality=6, codec='libx264', + output_params=['-threads', '20']) # highest quality is 10, lowest is 0 + except: + print('Error when saving {}'.format(prompt)) + video_grids.append(videos) + if nccl_info.rank <= 0: + video_grids = torch.cat(video_grids, dim=0) + + def get_file_name(): + return os.path.join(args.save_img_path, + f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}_{checkpoint_name}.{ext}') + + if args.num_frames == 1: + save_image(video_grids / 255.0, get_file_name(), + nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) + else: + video_grids = save_video_grid(video_grids) + imageio.mimwrite(get_file_name(), video_grids, fps=args.fps, quality=6) + + print('save path {}'.format(args.save_img_path)) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') + parser.add_argument("--version", type=str, default=None, choices=[None, '65x512x512', '65x256x256', '17x256x256']) + parser.add_argument("--num_frames", type=int, default=1) + parser.add_argument("--height", type=int, default=512) + parser.add_argument("--width", type=int, default=512) + parser.add_argument("--device", type=str, default='cuda:0') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--ae", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--ae_path", type=str, default='CausalVAEModel_4x8x8') + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--save_img_path", type=str, default="./sample_videos/t2v") + parser.add_argument("--guidance_scale", type=float, default=7.5) + parser.add_argument("--sample_method", type=str, default="PNDM") + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument("--fps", type=int, default=24) + parser.add_argument("--max_sequence_length", type=int, default=512) + parser.add_argument("--text_prompt", nargs='+') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--model_type', type=str, default="dit", choices=['dit', 'udit', 'latte']) + parser.add_argument('--compile', action='store_true') + parser.add_argument('--save_memory', action='store_true') + args = parser.parse_args() + + if torch_npu is not None: + npu_config.print_msg(args) + + # 初始化分布式环境 + local_rank = int(os.getenv('RANK', 0)) + world_size = int(os.getenv('WORLD_SIZE', 1)) + print('world_size', world_size) + if torch_npu is not None and npu_config.on_npu: + torch_npu.npu.set_device(local_rank) + else: + torch.cuda.set_device(local_rank) + dist.init_process_group(backend='nccl', init_method='env://', world_size=world_size, rank=local_rank) + initialize_sequence_parallel_state(world_size) + # torch.manual_seed(args.seed) + weight_dtype = torch.bfloat16 + device = torch.cuda.current_device() + # print(11111111111111111111, local_rank, device) + # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) + vae = getae_wrapper(args.ae)(args.ae_path) + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) + if args.enable_tiling: + vae.vae.enable_tiling() + vae.vae.tile_overlap_factor = args.tile_overlap_factor + vae.vae.tile_sample_min_size = 512 + vae.vae.tile_latent_min_size = 64 + vae.vae.tile_sample_min_size_t = 29 + vae.vae.tile_latent_min_size_t = 8 + if args.save_memory: + vae.vae.tile_sample_min_size = 256 + vae.vae.tile_latent_min_size = 32 + vae.vae.tile_sample_min_size_t = 29 + vae.vae.tile_latent_min_size_t = 8 + + vae.vae_scale_factor = ae_stride_config[args.ae] + + text_encoder = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", + cache_dir=args.cache_dir, low_cpu_mem_usage=True, + torch_dtype=weight_dtype).to(device) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", + cache_dir=args.cache_dir) + + # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, + # low_cpu_mem_usage=True, torch_dtype=weight_dtype).to(device) + # tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + + # set eval mode + vae.eval() + text_encoder.eval() + + if args.sample_method == 'DDIM': ######### + scheduler = DDIMScheduler(clip_sample=False) + elif args.sample_method == 'EulerDiscrete': + scheduler = EulerDiscreteScheduler() + elif args.sample_method == 'DDPM': ############# + scheduler = DDPMScheduler(clip_sample=False) + elif args.sample_method == 'DPMSolverMultistep': + scheduler = DPMSolverMultistepScheduler() + elif args.sample_method == 'DPMSolverSinglestep': + scheduler = DPMSolverSinglestepScheduler() + elif args.sample_method == 'PNDM': + scheduler = PNDMScheduler() + elif args.sample_method == 'HeunDiscrete': ######## + scheduler = HeunDiscreteScheduler() + elif args.sample_method == 'EulerAncestralDiscrete': + scheduler = EulerAncestralDiscreteScheduler() + elif args.sample_method == 'DEISMultistep': + scheduler = DEISMultistepScheduler() + elif args.sample_method == 'KDPM2AncestralDiscrete': ######### + scheduler = KDPM2AncestralDiscreteScheduler() + + if not os.path.exists(args.save_img_path): + os.makedirs(args.save_img_path, exist_ok=True) + + if args.num_frames == 1: + video_length = 1 + ext = 'jpg' + else: + ext = 'mp4' + + latest_path = None + save_img_path = args.save_img_path + while True: + cur_path = get_latest_path() + # print(cur_path, latest_path) + if cur_path == latest_path: + time.sleep(5) + continue + + time.sleep(1) + latest_path = cur_path + if npu_config is not None: + npu_config.print_msg(f"The latest_path is {latest_path}") + else: + print(f"The latest_path is {latest_path}") + full_path = f"{args.model_path}/{latest_path}/model_ema" + # full_path = f"{args.model_path}/{latest_path}/model" + pipeline = load_t2v_checkpoint(full_path) + # print('load model') + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = "/home/image_data/npu_profiling_t2v" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=10000, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + run_model_and_save_images(pipeline, latest_path) + prof.step() + else: + # print('gpu') + run_model_and_save_images(pipeline, latest_path) diff --git a/opensora/serve/gradio_utils.py b/opensora/serve/gradio_utils.py index 1935fe9e9..7367dec36 100644 --- a/opensora/serve/gradio_utils.py +++ b/opensora/serve/gradio_utils.py @@ -1,65 +1,89 @@ import random +import imageio +import uuid import torch +import numpy as np -def set_env(seed=0): - torch.manual_seed(seed) - torch.set_grad_enabled(False) +MAX_SEED = np.iinfo(np.int32).max +DESCRIPTION = """ + Description + + # Open-Sora Plan 93x720p + #### [Open-Sora Plan 93x720p](https://github.com/PKU-YuanGroup/Open-Sora-Plan) is a transformer-based text-to-video diffusion system trained on text embeddings from mT5. This demo uses the []() checkpoint. + #### Multilingual prompts SUPPORT; 支持多语言 + #### Welcome to Star🌟 our [GitHub](https://github.com/PKU-YuanGroup/Open-Sora-Plan) + ### You may change frame to 29 if you're not satisfied with the speed, as generating short video is significantly faster than long one. + """ +NEG_PROMPT = """ + nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, + low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry. + """ -def randomize_seed_fn(seed: int, randomize_seed: bool) -> int: - if randomize_seed: - seed = random.randint(0, 203279) - return seed -title_markdown = (""" -
- -
-""") -DESCRIPTION = """ -# Open-Sora-Plan v1.0.0 -## If Open-Sora-Plan is helpful, please help to ✨ the [Github Repo](https://github.com/PKU-YuanGroup/Open-Sora-Plan) and recommend it to your friends 😊' -#### [Open-Sora-Plan v1.0.0](https://github.com/PKU-YuanGroup/Open-Sora-Plan) is a transformer-based text-to-video diffusion system trained on text embeddings from T5. -#### This demo is only trained on 40k videos, when creating videos, please be aware that it has the potential to generate harmful videos. For more details read our [report](). -#### Image generation is typically 50 steps, video generation maybe 150 steps will yield good results, but this may take 3-4 minutes. -#### Feel free to enjoy the examples. -#### English prompts ONLY; 提示词仅限英文 -#### -""" +style_list = [ + { + "name": "(Default)", + "prompt": "(masterpiece), (best quality), (ultra-detailed), (unwatermarked), {prompt}", + "negative_prompt": NEG_PROMPT, + }, + { + "name": "Cinematic", + "prompt": "cinematic still {prompt} . emotional, harmonious, vignette, highly detailed, high budget, bokeh, cinemascope, moody, epic, gorgeous, film grain, grainy", + "negative_prompt": "anime, cartoon, graphic, text, painting, crayon, graphite, abstract, glitch, deformed, mutated, ugly, disfigured. ", + }, + { + "name": "Photographic", + "prompt": "cinematic photo, a close-up of {prompt} . 35mm photograph, film, bokeh, professional, 4k, highly detailed", + "negative_prompt": "drawing, painting, crayon, sketch, graphite, impressionist, noisy, blurry, soft, deformed, ugly. ", + }, + { + "name": "Anime", + "prompt": "anime artwork {prompt} . anime style, key visual, vibrant, studio anime, highly detailed", + "negative_prompt": "photo, deformed, black and white, realism, disfigured, low contrast. ", + }, + { + "name": "Manga", + "prompt": "manga style {prompt} . vibrant, high-energy, detailed, iconic, Japanese comic style", + "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic, Western comic style. ", + }, + { + "name": "Digital Art", + "prompt": "concept art {prompt} . digital artwork, illustrative, painterly, matte painting, highly detailed", + "negative_prompt": "photo, photorealistic, realism, ugly. ", + }, + { + "name": "Pixel art", + "prompt": "pixel-art {prompt} . low-res, blocky, pixel art style, 8-bit graphics", + "negative_prompt": "sloppy, messy, blurry, noisy, highly detailed, ultra textured, photo, realistic. ", + }, + { + "name": "Fantasy art", + "prompt": "ethereal fantasy concept art of {prompt} . magnificent, celestial, ethereal, painterly, epic, majestic, magical, fantasy art, cover art, dreamy", + "negative_prompt": "photographic, realistic, realism, 35mm film, dslr, cropped, frame, text, deformed, glitch, noise, noisy, off-center, deformed, cross-eyed, closed eyes, bad anatomy, ugly, disfigured, sloppy, duplicate, mutated, black and white. ", + }, + { + "name": "Neonpunk", + "prompt": "neonpunk style {prompt} . cyberpunk, vaporwave, neon, vibes, vibrant, stunningly beautiful, crisp, detailed, sleek, ultramodern, magenta highlights, dark purple shadows, high contrast, cinematic, ultra detailed, intricate, professional", + "negative_prompt": "painting, drawing, illustration, glitch, deformed, mutated, cross-eyed, ugly, disfigured. ", + }, + { + "name": "3D Model", + "prompt": "professional 3d model {prompt} . octane render, highly detailed, volumetric, dramatic lighting", + "negative_prompt": "ugly, deformed, noisy, low poly, blurry, painting. ", + }, +] -#
-#
-#
-# -# -#
-#
-# """) -block_css = """ -#buttons button { - min-width: min(120px,100%); -} -""" +def save_video(video): + unique_name = str(uuid.uuid4()) + ".mp4" + imageio.mimwrite(unique_name, video, fps=23, quality=6) + return unique_name -examples = [ - ["A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues.", 50, 10.0], - ["A quiet beach at dawn, the waves softly lapping at the shore, pink and orange hues painting the sky, offering a moment of solitude and reflection.", 50, 10.0], - ["The majestic beauty of a waterfall cascading down a cliff into a serene lake.", 50, 10.0], - ["Sunset over the sea.", 50, 10.0], - ["a cat wearing sunglasses and working as a lifeguard at pool.", 50, 10.0], - ["Slow pan upward of blazing oak fire in an indoor fireplace.", 50, 10.0], - ["Yellow and black tropical fish dart through the sea.", 50, 10.0], - ["a serene winter scene in a forest. The forest is blanketed in a thick layer of snow, which has settled on the branches of the trees, creating a canopy of white. The trees, a mix of evergreens and deciduous, stand tall and silent, their forms partially obscured by the snow. The ground is a uniform white, with no visible tracks or signs of human activity. The sun is low in the sky, casting a warm glow that contrasts with the cool tones of the snow. The light filters through the trees, creating a soft, diffused illumination that highlights the texture of the snow and the contours of the trees. The overall style of the scene is naturalistic, with a focus on the tranquility and beauty of the winter landscape.", 50, 10.0], - ["a dynamic interaction between the ocean and a large rock. The rock, with its rough texture and jagged edges, is partially submerged in the water, suggesting it is a natural feature of the coastline. The water around the rock is in motion, with white foam and waves crashing against the rock, indicating the force of the ocean's movement. The background is a vast expanse of the ocean, with small ripples and waves, suggesting a moderate sea state. The overall style of the scene is a realistic depiction of a natural landscape, with a focus on the interplay between the rock and the water.", 50, 10.0], - ["A serene waterfall cascading down moss-covered rocks, its soothing sound creating a harmonious symphony with nature.", 50, 10.0], - ["A soaring drone footage captures the majestic beauty of a coastal cliff, its red and yellow stratified rock faces rich in color and against the vibrant turquoise of the sea. Seabirds can be seen taking flight around the cliff's precipices. As the drone slowly moves from different angles, the changing sunlight casts shifting shadows that highlight the rugged textures of the cliff and the surrounding calm sea. The water gently laps at the rock base and the greenery that clings to the top of the cliff, and the scene gives a sense of peaceful isolation at the fringes of the ocean. The video captures the essence of pristine natural beauty untouched by human structures.", 50, 10.0], - ["The video captures the majestic beauty of a waterfall cascading down a cliff into a serene lake. The waterfall, with its powerful flow, is the central focus of the video. The surrounding landscape is lush and green, with trees and foliage adding to the natural beauty of the scene. The camera angle provides a bird's eye view of the waterfall, allowing viewers to appreciate the full height and grandeur of the waterfall. The video is a stunning representation of nature's power and beauty.", 50, 10.0], - ["A vibrant scene of a snowy mountain landscape. The sky is filled with a multitude of colorful hot air balloons, each floating at different heights, creating a dynamic and lively atmosphere. The balloons are scattered across the sky, some closer to the viewer, others further away, adding depth to the scene. Below, the mountainous terrain is blanketed in a thick layer of snow, with a few patches of bare earth visible here and there. The snow-covered mountains provide a stark contrast to the colorful balloons, enhancing the visual appeal of the scene.", 50, 10.0], - ["A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene.", 50, 10.0], - ["A snowy forest landscape with a dirt road running through it. The road is flanked by trees covered in snow, and the ground is also covered in snow. The sun is shining, creating a bright and serene atmosphere. The road appears to be empty, and there are no people or animals visible in the video. The style of the video is a natural landscape shot, with a focus on the beauty of the snowy forest and the peacefulness of the road.", 50, 10.0], - ["The dynamic movement of tall, wispy grasses swaying in the wind. The sky above is filled with clouds, creating a dramatic backdrop. The sunlight pierces through the clouds, casting a warm glow on the scene. The grasses are a mix of green and brown, indicating a change in seasons. The overall style of the video is naturalistic, capturing the beauty of the landscape in a realistic manner. The focus is on the grasses and their movement, with the sky serving as a secondary element. The video does not contain any human or animal elements.", 50, 10.0], -] \ No newline at end of file +def randomize_seed_fn(seed: int, randomize_seed: bool) -> int: + if randomize_seed: + seed = random.randint(0, MAX_SEED) + return seed diff --git a/opensora/serve/gradio_web_server.py b/opensora/serve/gradio_web_server.py index 99dbeff0b..1c932c8fa 100644 --- a/opensora/serve/gradio_web_server.py +++ b/opensora/serve/gradio_web_server.py @@ -1,124 +1,292 @@ - - +#!/usr/bin/env python +from __future__ import annotations import argparse -import sys import os -import random +import sys +import gradio as gr +from diffusers import ConsistencyDecoderVAE, DPMSolverMultistepScheduler, Transformer2DModel, AutoencoderKL, SASolverScheduler -import imageio import torch -from diffusers import PNDMScheduler -from huggingface_hub import hf_hub_download -from torchvision.utils import save_image -from diffusers.models import AutoencoderKL +from typing import Tuple from datetime import datetime -from typing import List, Union -import gradio as gr -import numpy as np -from gradio.components import Textbox, Video, Image -from transformers import T5Tokenizer, T5EncoderModel - +from peft import PeftModel +# import spaces from opensora.models.ae import ae_stride_config, getae, getae_wrapper -from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.models.diffusion.latte.modeling_latte import LatteT2V -from opensora.sample.pipeline_videogen import VideoGenPipeline -from opensora.serve.gradio_utils import block_css, title_markdown, randomize_seed_fn, set_env, examples, DESCRIPTION +from transformers import T5EncoderModel, T5Tokenizer, AutoTokenizer, MT5EncoderModel +from diffusers.schedulers import (DDIMScheduler, DDPMScheduler, PNDMScheduler, + EulerDiscreteScheduler, DPMSolverMultistepScheduler, + HeunDiscreteScheduler, EulerAncestralDiscreteScheduler, + DEISMultistepScheduler, KDPM2AncestralDiscreteScheduler) +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V +from opensora.sample.pipeline_opensora import OpenSoraPipeline +from opensora.serve.gradio_utils import DESCRIPTION, MAX_SEED, style_list, randomize_seed_fn, save_video +if not torch.cuda.is_available(): + DESCRIPTION += "\n

Running on CPU 🥶 This demo does not work on CPU.

" -@torch.inference_mode() -def generate_img(prompt, sample_steps, scale, seed=0, randomize_seed=False, force_images=False): - seed = int(randomize_seed_fn(seed, randomize_seed)) - set_env(seed) - video_length = transformer_model.config.video_length if not force_images else 1 - height, width = int(args.version.split('x')[1]), int(args.version.split('x')[2]) - num_frames = 1 if video_length == 1 else int(args.version.split('x')[0]) - videos = videogen_pipeline(prompt, - video_length=video_length, - height=height, - width=width, - num_inference_steps=sample_steps, - guidance_scale=scale, - enable_temporal_attentions=not force_images, - num_images_per_prompt=1, - mask_feature=True, - ).video - - torch.cuda.empty_cache() - videos = videos[0] - tmp_save_path = 'tmp.mp4' - imageio.mimwrite(tmp_save_path, videos, fps=24, quality=9) # highest quality is 10, lowest is 0 - display_model_info = f"Video size: {num_frames}×{height}×{width}, \nSampling Step: {sample_steps}, \nGuidance Scale: {scale}" - return tmp_save_path, prompt, display_model_info, seed - -if __name__ == '__main__': - args = type('args', (), { - 'ae': 'CausalVAEModel_4x8x8', - 'force_images': False, - 'model_path': 'LanguageBind/Open-Sora-Plan-v1.0.0', - 'text_encoder_name': 'DeepFloyd/t5-v1_1-xxl', - 'version': '65x512x512' - }) - device = torch.device('cuda:0') - - # Load model: - transformer_model = LatteT2V.from_pretrained(args.model_path, subfolder=args.version, torch_dtype=torch.float16, cache_dir='cache_dir').to(device) - - vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir='cache_dir').to(device) - vae = vae.half() +CACHE_EXAMPLES = torch.cuda.is_available() and os.getenv("CACHE_EXAMPLES", "1") == "1" +CACHE_EXAMPLES = False +MAX_IMAGE_SIZE = int(os.getenv("MAX_IMAGE_SIZE", "6000")) +MAX_VIDEO_FRAME = int(os.getenv("MAX_IMAGE_SIZE", "93")) +SPEED_UP_T5 = os.getenv("USE_TORCH_COMPILE", "0") == "1" +USE_TORCH_COMPILE = os.getenv("USE_TORCH_COMPILE", "0") == "1" +ENABLE_CPU_OFFLOAD = os.getenv("ENABLE_CPU_OFFLOAD", "0") == "1" +PORT = int(os.getenv("DEMO_PORT", "15432")) + + +device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") + +styles = {k["name"]: (k["prompt"], k["negative_prompt"]) for k in style_list} +STYLE_NAMES = list(styles.keys()) +DEFAULT_STYLE_NAME = "(Default)" +SCHEDULE_NAME = [ + "PNDM-Solver", "EulerA-Solver", "DPM-Solver", "SA-Solver", + "DDIM-Solver", "Euler-Solver", "DDPM-Solver", "DEISM-Solver"] +DEFAULT_SCHEDULE_NAME = "PNDM-Solver" +NUM_IMAGES_PER_PROMPT = 1 + +def apply_style(style_name: str, positive: str, negative: str = "") -> Tuple[str, str]: + p, n = styles.get(style_name, styles[DEFAULT_STYLE_NAME]) + if not negative: + negative = "" + return p.replace("{prompt}", positive), n + negative + +if torch.cuda.is_available(): + weight_dtype = torch.bfloat16 + T5_token_max_length = 512 + + vae = getae_wrapper('CausalVAEModel_4x8x8')("/storage/dataset/test140k") + vae.vae = vae.vae.to(device=device, dtype=weight_dtype) vae.vae.enable_tiling() - image_size = int(args.version.split('x')[1]) - latent_size = (image_size // ae_stride_config[args.ae][1], image_size // ae_stride_config[args.ae][2]) - vae.latent_size = latent_size - transformer_model.force_images = args.force_images - tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir="cache_dir") - text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir="cache_dir", - torch_dtype=torch.float16).to(device) - - # set eval mode - transformer_model.eval() - vae.eval() - text_encoder.eval() + vae.vae.tile_overlap_factor = 0.125 + vae.vae.tile_sample_min_size = 256 + vae.vae.tile_latent_min_size = 32 + vae.vae.tile_sample_min_size_t = 29 + vae.vae.tile_latent_min_size_t = 8 + vae.vae_scale_factor = ae_stride_config['CausalVAEModel_4x8x8'] + + text_encoder = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", + low_cpu_mem_usage=True, torch_dtype=weight_dtype) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl") + transformer = OpenSoraT2V.from_pretrained("/storage/dataset/hw29/model_ema", low_cpu_mem_usage=False, + device_map=None, torch_dtype=weight_dtype) scheduler = PNDMScheduler() - videogen_pipeline = VideoGenPipeline(vae=vae, - text_encoder=text_encoder, - tokenizer=tokenizer, - scheduler=scheduler, - transformer=transformer_model).to(device) - - - demo = gr.Interface( - fn=generate_img, - inputs=[Textbox(label="", - placeholder="Please enter your prompt. \n"), - gr.Slider( - label='Sample Steps', + pipe = OpenSoraPipeline(vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=transformer) + pipe.to(device) + print("Loaded on Device!") + + # speed-up T5 + if SPEED_UP_T5: + pipe.text_encoder.to_bettertransformer() + + if USE_TORCH_COMPILE: + pipe.transformer = torch.compile(pipe.transformer, mode="reduce-overhead", fullgraph=True) + print("Model Compiled!") + +# @spaces.GPU(duration=120) +@torch.no_grad() +@torch.inference_mode() +def generate( + prompt: str, + negative_prompt: str = "", + style: str = DEFAULT_STYLE_NAME, + use_negative_prompt: bool = False, + seed: int = 0, + frame: int = 29, + schedule: str = 'DPM-Solver', + guidance_scale: float = 4.5, + num_inference_steps: int = 25, + randomize_seed: bool = False, + progress=gr.Progress(track_tqdm=True), +): + seed = int(randomize_seed_fn(seed, randomize_seed)) + generator = torch.Generator().manual_seed(seed) + + if schedule == 'DPM-Solver': + if not isinstance(pipe.scheduler, DPMSolverMultistepScheduler): + pipe.scheduler = DPMSolverMultistepScheduler() + elif schedule == "PNDM-Solver": + if not isinstance(pipe.scheduler, PNDMScheduler): + pipe.scheduler = PNDMScheduler() + elif schedule == "DDIM-Solver": + if not isinstance(pipe.scheduler, DDIMScheduler): + pipe.scheduler = DDIMScheduler() + elif schedule == "Euler-Solver": + if not isinstance(pipe.scheduler, EulerDiscreteScheduler): + pipe.scheduler = EulerDiscreteScheduler() + elif schedule == "DDPM-Solver": + if not isinstance(pipe.scheduler, DDPMScheduler): + pipe.scheduler = DDPMScheduler() + elif schedule == "EulerA-Solver": + if not isinstance(pipe.scheduler, EulerAncestralDiscreteScheduler): + pipe.scheduler = EulerAncestralDiscreteScheduler() + elif schedule == "DEISM-Solver": + if not isinstance(pipe.scheduler, DEISMultistepScheduler): + pipe.scheduler = DEISMultistepScheduler() + elif schedule == "SA-Solver": + if not isinstance(pipe.scheduler, SASolverScheduler): + pipe.scheduler = SASolverScheduler.from_config(pipe.scheduler.config, algorithm_type='data_prediction', tau_func=lambda t: 1 if 200 <= t <= 800 else 0, predictor_order=2, corrector_order=2) + else: + raise ValueError(f"Unknown schedule: {schedule}") + + if not use_negative_prompt: + negative_prompt = None # type: ignore + prompt, negative_prompt = apply_style(style, prompt, negative_prompt) + print(prompt, negative_prompt) + videos = pipe( + prompt=prompt, + negative_prompt=negative_prompt, + num_frames=frame, + # width=1280, + # height=720, + width=640, + height=480, + guidance_scale=guidance_scale, + num_inference_steps=num_inference_steps, + generator=generator, + num_images_per_prompt=1, # num_imgs + max_sequence_length=T5_token_max_length, + ).images + + video_paths = [save_video(vid) for vid in videos] + print(video_paths) + return video_paths[0], seed + + +examples = [ + "A small cactus with a happy face in the Sahara desert.", + "Eiffel Tower was Made up of more than 2 million translucent straws to look like a cloud, with the bell tower at the top of the building, Michel installed huge foam-making machines in the forest to blow huge amounts of unpredictable wet clouds in the building's classic architecture.", + "3D animation of a small, round, fluffy creature with big, expressive eyes explores a vibrant, enchanted forest. The creature, a whimsical blend of a rabbit and a squirrel, has soft blue fur and a bushy, striped tail. It hops along a sparkling stream, its eyes wide with wonder. The forest is alive with magical elements: flowers that glow and change colors, trees with leaves in shades of purple and silver, and small floating lights that resemble fireflies. The creature stops to interact playfully with a group of tiny, fairy-like beings dancing around a mushroom ring. The creature looks up in awe at a large, glowing tree that seems to be the heart of the forest.", + "Color photo of a corgi made of transparent glass, standing on the riverside in Yosemite National Park.", + "A close-up photo of a person. The subject is a woman. She wore a blue coat with a gray dress underneath. She has blue eyes and blond hair, and wears a pair of earrings. Behind are blurred city buildings and streets.", + "A litter of golden retriever puppies playing in the snow. Their heads pop out of the snow, covered in.", + "a handsome young boy in the middle with sky color background wearing eye glasses, it's super detailed with anime style, it's a portrait with delicated eyes and nice looking face", + "an astronaut sitting in a diner, eating fries, cinematic, analog film", + "Pirate ship trapped in a cosmic maelstrom nebula, rendered in cosmic beach whirlpool engine, volumetric lighting, spectacular, ambient lights, light pollution, cinematic atmosphere, art nouveau style, illustration art artwork by SenseiJaye, intricate detail.", + "professional portrait photo of an anthropomorphic cat wearing fancy gentleman hat and jacket walking in autumn forest.", + "The parametric hotel lobby is a sleek and modern space with plenty of natural light. The lobby is spacious and open with a variety of seating options. The front desk is a sleek white counter with a parametric design. The walls are a light blue color with parametric patterns. The floor is a light wood color with a parametric design. There are plenty of plants and flowers throughout the space. The overall effect is a calm and relaxing space. occlusion, moody, sunset, concept art, octane rendering, 8k, highly detailed, concept art, highly detailed, beautiful scenery, cinematic, beautiful light, hyperreal, octane render, hdr, long exposure, 8K, realistic, fog, moody, fire and explosions, smoke, 50mm f2.8", +] + +with gr.Blocks(css="style.css") as demo: + gr.Markdown(DESCRIPTION) + gr.DuplicateButton( + value="Duplicate Space for private use", + elem_id="duplicate-button", + visible=os.getenv("SHOW_DUPLICATE_BUTTON") == "1", + ) + with gr.Row(equal_height=False): + with gr.Group(): + with gr.Row(): + use_negative_prompt = gr.Checkbox(label="Use additional negative prompt", value=False, visible=True) + negative_prompt = gr.Text( + label="Negative prompt", + max_lines=1, + placeholder="Enter a additional negative prompt", + visible=True, + ) + with gr.Row(visible=True): + schedule = gr.Radio( + show_label=True, + container=True, + interactive=True, + choices=SCHEDULE_NAME, + value=DEFAULT_SCHEDULE_NAME, + label="Sampler Schedule", + visible=True, + ) + style_selection = gr.Radio( + show_label=True, + container=True, + interactive=True, + choices=STYLE_NAMES, + value=DEFAULT_STYLE_NAME, + label="Video Style", + ) + seed = gr.Slider( + label="Seed", + minimum=0, + maximum=MAX_SEED, + step=1, + value=0, + ) + randomize_seed = gr.Checkbox(label="Randomize seed", value=True) + with gr.Row(visible=True): + frame = gr.Slider( + label="Frame", + minimum=29, + maximum=MAX_VIDEO_FRAME, + step=16, + value=29, + ) + with gr.Row(): + guidance_scale = gr.Slider( + label="Guidance scale", minimum=1, - maximum=500, - value=50, - step=10 - ), - gr.Slider( - label='Guidance Scale', - minimum=0.1, - maximum=30.0, - value=10.0, - step=0.1 - ), - gr.Slider( - label="Seed", - minimum=0, - maximum=203279, + maximum=10, + step=0.1, + value=5.0, + ) + inference_steps = gr.Slider( + label="inference steps", + minimum=10, + maximum=200, step=1, - value=0, - ), - gr.Checkbox(label="Randomize seed", value=True), - gr.Checkbox(label="Generate image (1 frame video)", value=False), - ], - outputs=[Video(label="Vid", width=512, height=512), - Textbox(label="input prompt"), - Textbox(label="model info"), - gr.Slider(label='seed')], - title=title_markdown, description=DESCRIPTION, theme=gr.themes.Default(), css=block_css, + value=50, + ) + with gr.Group(): + with gr.Row(): + prompt = gr.Text( + label="Prompt", + show_label=False, + max_lines=1, + placeholder="Enter your prompt", + container=False, + ) + run_button = gr.Button("Run", scale=0) + result = gr.Video(label="Result") + + gr.Examples( examples=examples, + inputs=prompt, + outputs=[result, seed], + fn=generate, + cache_examples=CACHE_EXAMPLES, ) - demo.launch() \ No newline at end of file + + use_negative_prompt.change( + fn=lambda x: gr.update(visible=x), + inputs=use_negative_prompt, + outputs=negative_prompt, + api_name=False, + ) + + gr.on( + triggers=[ + prompt.submit, + negative_prompt.submit, + run_button.click, + ], + fn=generate, + inputs=[ + prompt, + negative_prompt, + style_selection, + use_negative_prompt, + seed, + frame, + schedule, + guidance_scale, + inference_steps, + randomize_seed, + ], + outputs=[result, seed], + api_name="run", + ) + +if __name__ == "__main__": + # demo.queue(max_size=20).launch(server_name='0.0.0.0', share=True) + demo.queue(max_size=20).launch(server_name="0.0.0.0", server_port=11900, debug=True) \ No newline at end of file diff --git a/opensora/serve/style.css b/opensora/serve/style.css new file mode 100644 index 000000000..73054b14b --- /dev/null +++ b/opensora/serve/style.css @@ -0,0 +1 @@ +.gradio-container{width:1280px!important} \ No newline at end of file diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index d2742f994..5560e9645 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -30,6 +30,9 @@ except: torch_npu = None npu_config = None + from opensora.utils.parallel_states import initialize_sequence_parallel_state, \ + destroy_sequence_parallel_group, get_sequence_parallel_state + from opensora.utils.communications import prepare_parallel_data, broadcast pass import time from dataclasses import field, dataclass @@ -181,7 +184,7 @@ def main(args): project_config=accelerator_project_config, ) - if npu_config is not None and args.num_frames != 1 and args.use_image_num == 0: + if args.num_frames != 1 and args.use_image_num == 0: initialize_sequence_parallel_state(args.sp_size) if args.report_to == "wandb": @@ -323,7 +326,7 @@ def main(args): # Create EMA for the unet. if args.use_ema: ema_model = deepcopy(model) - ema_model = EMAModel(ema_model.parameters(), update_after_step=args.ema_start_step, + ema_model = EMAModel(ema_model.parameters(), decay=args.ema_decay, update_after_step=args.ema_start_step, model_cls=Diffusion_models_class[args.model], model_config=ema_model.config) # `accelerate` 0.16.0 will have better support for customized saving @@ -434,7 +437,9 @@ def load_model_hook(models, input_dir): logger.info(f"optimizer: {optimizer}") # Setup data: + logger.info(f'before dataset') train_dataset = getdataset(args) + logger.info(f'after dataset') sampler = LengthGroupedSampler( args.train_batch_size, world_size=accelerator.num_processes, @@ -442,6 +447,7 @@ def load_model_hook(models, input_dir): group_frame=args.group_frame, group_resolution=args.group_resolution, ) if args.group_frame or args.group_resolution else None + logger.info(f'after LengthGroupedSampler') train_dataloader = DataLoader( train_dataset, shuffle=sampler is None, @@ -452,6 +458,7 @@ def load_model_hook(models, input_dir): sampler=sampler if args.group_frame or args.group_resolution else None, # prefetch_factor=4 ) + logger.info(f'after train_dataloader') # Scheduler and math around the number of training steps. overrode_max_train_steps = False @@ -470,9 +477,11 @@ def load_model_hook(models, input_dir): # Prepare everything with our `accelerator`. # model.requires_grad_(False) # model.pos_embed.requires_grad_(True) + logger.info(f'before accelerator.prepare') model, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( model, optimizer, train_dataloader, lr_scheduler ) + logger.info(f'after accelerator.prepare') if args.use_ema: ema_model.to(accelerator.device) @@ -601,7 +610,7 @@ def run(model_input, model_kwargs, prof): bsz = model_input.shape[0] # Sample a random timestep for each image without bias. timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) - if npu_config is not None and get_sequence_parallel_state(): + if get_sequence_parallel_state(): broadcast(timesteps) # Add noise to the model input according to the noise magnitude at each timestep @@ -632,6 +641,10 @@ def run(model_input, model_kwargs, prof): raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") mask = model_kwargs.get('attention_mask', None) + if torch.all(mask.bool()): + mask = None + if get_sequence_parallel_state(): + assert mask is None b, c, _, _, _ = model_pred.shape if mask is not None: mask = mask.unsqueeze(1).repeat(1, c, 1, 1, 1).float() # b t h w -> b c t h w @@ -697,14 +710,14 @@ def run(model_input, model_kwargs, prof): log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, progress_info.global_step) - if args.use_ema and npu_config is None: - # Store the UNet parameters temporarily and load the EMA parameters to perform inference. - ema_model.store(model.parameters()) - ema_model.copy_to(model.parameters()) - log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, - weight_dtype, progress_info.global_step, ema=True) - # Switch back to the original UNet parameters. - ema_model.restore(model.parameters()) + if args.use_ema and npu_config is not None: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_model.store(model.parameters()) + ema_model.copy_to(model.parameters()) + log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, + weight_dtype, progress_info.global_step, ema=True) + # Switch back to the original UNet parameters. + ema_model.restore(model.parameters()) if prof is not None: prof.step() @@ -769,9 +782,8 @@ def train_one_step(step_, data_item_, prof_=None): # videos = ae.decode(x)[0] # videos = videos.transpose(0, 1) # custom_to_video(videos.to(torch.float32), fps=24, output_file='tmp.mp4') - # sys.exit() - - if npu_config is not None and get_sequence_parallel_state(): + # import sys;sys.exit() + if get_sequence_parallel_state(): x, cond, attn_mask, cond_mask, use_image_num = prepare_parallel_data(x, cond, attn_mask, cond_mask, args.use_image_num) for iter in range(args.train_batch_size * args.sp_size // args.train_sp_batch_size): @@ -832,7 +844,7 @@ def train_all_epoch(prof_=None): train_all_epoch() accelerator.wait_for_everyone() accelerator.end_training() - if npu_config is not None and get_sequence_parallel_state(): + if get_sequence_parallel_state(): destroy_sequence_parallel_group() @@ -845,7 +857,8 @@ def train_all_epoch(prof_=None): parser.add_argument("--image_data", type=str, default='') parser.add_argument("--sample_rate", type=int, default=1) parser.add_argument("--train_fps", type=int, default=24) - parser.add_argument("--speed_factor", type=float, default=1.5) + parser.add_argument("--drop_short_ratio", type=float, default=1.0) + parser.add_argument("--speed_factor", type=float, default=1.0) parser.add_argument("--num_frames", type=int, default=65) parser.add_argument("--max_height", type=int, default=320) parser.add_argument("--max_width", type=int, default=240) @@ -882,6 +895,7 @@ def train_all_epoch(prof_=None): # diffusion setting parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_decay", type=float, default=0.999) parser.add_argument("--ema_start_step", type=int, default=0) parser.add_argument("--noise_offset", type=float, default=0.02, help="The scale of noise offset.") parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") diff --git a/opensora/utils/communications.py b/opensora/utils/communications.py new file mode 100644 index 000000000..c7a79e3a6 --- /dev/null +++ b/opensora/utils/communications.py @@ -0,0 +1,122 @@ +import torch +import torch.distributed as dist +from einops import rearrange +from opensora.utils.parallel_states import nccl_info + +def broadcast(input_: torch.Tensor): + sp_size = nccl_info.world_size + src = nccl_info.rank // sp_size * sp_size + dist.broadcast(input_, src=src, group=nccl_info.group) + +_COUNT = 0 +def _all_to_all( + input_: torch.Tensor, + scatter_dim: int, + gather_dim: int, +): + group = nccl_info.group + sp_size = nccl_info.world_size + input_list = [t.contiguous() for t in torch.tensor_split(input_, sp_size, scatter_dim)] + output_list = [torch.empty_like(input_list[0]) for _ in range(sp_size)] + dist.all_to_all(output_list, input_list, group=group) + return torch.cat(output_list, dim=gather_dim).contiguous() + +def _single_all_to_all( + input_: torch.Tensor, + scatter_dim: int, + gather_dim: int, + enable_HCCL=False, +): + + sp_size = nccl_info.world_size + inp_shape = list(input_.shape) + inp_shape[scatter_dim] = inp_shape[scatter_dim] // sp_size + if scatter_dim < 1: + input_t = input_.reshape( + [sp_size, inp_shape[scatter_dim]] + \ + inp_shape[scatter_dim + 1:] + ) + else: + # transpose groups of heads with the seq-len parallel dimension, so that we can scatter them! + input_t = input_.reshape( + [-1, sp_size, inp_shape[scatter_dim]] + \ + inp_shape[scatter_dim + 1:] + ).transpose(0, 1).contiguous() + + output = torch.empty_like(input_t) + dist.all_to_all_single(output, input_t, group=nccl_info.group) + # if scattering the seq-dim, transpose the heads back to the original dimension + if scatter_dim < 1: + output = output.transpose(0, 1).contiguous() + + return output.reshape( + inp_shape[: gather_dim] + [inp_shape[gather_dim] * sp_size, ] + inp_shape[gather_dim + 1:]) + + +class _AllToAll(torch.autograd.Function): + """All-to-all communication. + + Args: + input_: input matrix + process_group: communication group + scatter_dim: scatter dimension + gather_dim: gather dimension + """ + + @staticmethod + def forward(ctx, input_, scatter_dim, gather_dim, all_to_all_func): + ctx.scatter_dim = scatter_dim + ctx.gather_dim = gather_dim + ctx.all_to_all = all_to_all_func + output = ctx.all_to_all(input_, scatter_dim, gather_dim) + return output + + @staticmethod + def backward(ctx, grad_output): + grad_output = ctx.all_to_all( + grad_output, + ctx.gather_dim, + ctx.scatter_dim, + ) + return ( + grad_output, + None, + None, + None, + ) + +def all_to_all_SBH( + input_: torch.Tensor, + scatter_dim: int = 1, + gather_dim: int = 0, +): + return _AllToAll.apply(input_, scatter_dim, gather_dim, _single_all_to_all) + +def all_to_all_BSND( + input_: torch.Tensor, + scatter_dim: int = 2, + gather_dim: int = 1, +): + return _AllToAll.apply(input_, scatter_dim, gather_dim, _all_to_all) + + +def prepare_parallel_data(hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask, use_image_num): + def all_to_all(hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask): + hidden_states = _single_all_to_all(hidden_states, scatter_dim=2, gather_dim=0, enable_HCCL=True) + encoder_hidden_states = _single_all_to_all(encoder_hidden_states, scatter_dim=1, gather_dim=0, enable_HCCL=True) + attention_mask = _single_all_to_all(attention_mask, scatter_dim=1, gather_dim=0, enable_HCCL=True) + encoder_attention_mask = _single_all_to_all(encoder_attention_mask, scatter_dim=1, gather_dim=0, enable_HCCL=True) + return hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask + + sp_size = nccl_info.world_size + frame = hidden_states.shape[2] + assert frame % sp_size == 0, "frame should be a multiple of sp_size" + + encoder_hidden_states = rearrange(encoder_hidden_states, 'b 1 (n x) h -> b n x h', + n=sp_size, x=encoder_hidden_states.shape[2]//sp_size).contiguous() + hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask = all_to_all(hidden_states, + encoder_hidden_states, + attention_mask.repeat(1, sp_size, 1, 1), + encoder_attention_mask.repeat(1, sp_size, 1)) + + return hidden_states, encoder_hidden_states, attention_mask, encoder_attention_mask, use_image_num \ No newline at end of file diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 40227021c..46b02c43c 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -50,6 +50,7 @@ def pad_to_multiple(number, ds_stride): class Collate: def __init__(self, args): + self.batch_size = args.train_batch_size self.group_frame = args.group_frame self.group_resolution = args.group_resolution @@ -125,6 +126,7 @@ def __call__(self, batch): def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, patch_size_thw, extra_1): # pad to max multiple of ds_stride batch_input_size = [i.shape for i in batch_tubes] # [(c t h w), (c t h w)] + assert len(batch_input_size) == self.batch_size if self.group_frame: max_t = max([i[1] for i in batch_input_size]) max_h = max([i[2] for i in batch_input_size]) @@ -166,7 +168,8 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p 0, max_latent_size[1] - i[1], 0, max_latent_size[0] - i[0]), value=0) for i in valid_latent_size] attention_mask = torch.stack(attention_mask) # b t h w - + if self.batch_size == 1: + assert torch.all(attention_mask.bool()) # if self.group_frame: # if not torch.all(torch.any(attention_mask.flatten(-2), dim=-1)): # print('batch_input_size', batch_input_size) diff --git a/opensora/utils/parallel_states.py b/opensora/utils/parallel_states.py new file mode 100644 index 000000000..def564f71 --- /dev/null +++ b/opensora/utils/parallel_states.py @@ -0,0 +1,44 @@ +import torch +import torch.distributed as dist +import os + +class COMM_INFO: + def __init__(self): + self.group = None + self.world_size = 0 + self.rank = -1 + +nccl_info = COMM_INFO() +_SEQUENCE_PARALLEL_STATE = False +def initialize_sequence_parallel_state(sequence_parallel_size): + global _SEQUENCE_PARALLEL_STATE + if sequence_parallel_size > 1: + _SEQUENCE_PARALLEL_STATE = True + initialize_sequence_parallel_group(sequence_parallel_size) + +def set_sequence_parallel_state(state): + global _SEQUENCE_PARALLEL_STATE + _SEQUENCE_PARALLEL_STATE = state + +def get_sequence_parallel_state(): + return _SEQUENCE_PARALLEL_STATE + +def initialize_sequence_parallel_group(sequence_parallel_size): + """Initialize the sequence parallel group.""" + rank = int(os.getenv('RANK', '0')) + world_size = int(os.getenv("WORLD_SIZE", '1')) + assert world_size % sequence_parallel_size == 0, "world_size must be divisible by sequence_parallel_size" + # hccl + nccl_info.world_size = sequence_parallel_size + nccl_info.rank = rank + num_sequence_parallel_groups: int = world_size // sequence_parallel_size + for i in range(num_sequence_parallel_groups): + ranks = range(i * sequence_parallel_size, (i + 1) * sequence_parallel_size) + group = dist.new_group(ranks) + if rank in ranks: + nccl_info.group = group + + +def destroy_sequence_parallel_group(): + """Destroy the sequence parallel group.""" + dist.destroy_process_group() diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index 9dd1bf5af..dcbd92493 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,20 +1,32 @@ -node030 slots=8 -node033 slots=8 -node034 slots=8 -node012 slots=8 +node30 slots=8 node004 slots=8 node005 slots=8 node006 slots=8 +node007 slots=8 node008 slots=8 -node009 slots=8 node010 slots=8 -node011 slots=8 +node012 slots=8 +node013 slots=8 +node019 slots=8 +node026 slots=8 node027 slots=8 node028 slots=8 +node029 slots=8 node031 slots=8 -node032 slots=8 +node059 slots=8 +node033 slots=8 +node034 slots=8 node035 slots=8 +node037 slots=8 node056 slots=8 +node060 slots=8 node061 slots=8 +node062 slots=8 +node064 slots=8 node063 slots=8 -node064 slots=8 \ No newline at end of file +node003 slots=8 +node058 slots=8 +node109 slots=8 +node114 slots=8 +node011 slots=8 +node066 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile1 b/scripts/accelerate_configs/hostfile1 index 2beea768e..01617084b 100644 --- a/scripts/accelerate_configs/hostfile1 +++ b/scripts/accelerate_configs/hostfile1 @@ -1,2 +1,36 @@ -node030 slots=8 -node012 slots=8 \ No newline at end of file +node30 slots=8 +node004 slots=8 +node005 slots=8 +node006 slots=8 +node007 slots=8 +node008 slots=8 +node010 slots=8 +node012 slots=8 +node013 slots=8 +node019 slots=8 +node027 slots=8 +node028 slots=8 +node029 slots=8 +node031 slots=8 +node032 slots=8 +node033 slots=8 +node034 slots=8 +node035 slots=8 +node037 slots=8 +node056 slots=8 +node059 slots=8 +node060 slots=8 +node061 slots=8 +node062 slots=8 +node064 slots=8 +node065 slots=8 +node072 slots=8 +node074 slots=8 +node076 slots=8 +node077 slots=8 +node068 slots=8 +node063 slots=8 +node075 slots=8 +node058 slots=8 +node003 slots=8 +node026 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile2 b/scripts/accelerate_configs/hostfile2 index b46a32f09..2bd11f0d8 100644 --- a/scripts/accelerate_configs/hostfile2 +++ b/scripts/accelerate_configs/hostfile2 @@ -1,2 +1,25 @@ +node30 slots=8 +node004 slots=8 +node005 slots=8 +node006 slots=8 +node007 slots=8 +node008 slots=8 +node010 slots=8 +node012 slots=8 +node013 slots=8 +node019 slots=8 +node027 slots=8 +node028 slots=8 +node029 slots=8 +node031 slots=8 +node032 slots=8 node033 slots=8 -node034 slots=8 \ No newline at end of file +node034 slots=8 +node035 slots=8 +node037 slots=8 +node056 slots=8 +node059 slots=8 +node060 slots=8 +node061 slots=8 +node062 slots=8 +node064 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile40 b/scripts/accelerate_configs/hostfile40 new file mode 100644 index 000000000..d098b9db3 --- /dev/null +++ b/scripts/accelerate_configs/hostfile40 @@ -0,0 +1,40 @@ +node30 slots=8 +node004 slots=8 +node005 slots=8 +node006 slots=8 +node007 slots=8 +node008 slots=8 +node010 slots=8 +node011 slots=8 +node012 slots=8 +node013 slots=8 +node019 slots=8 +node026 slots=8 +node027 slots=8 +node028 slots=8 +node029 slots=8 +node031 slots=8 +node032 slots=8 +node033 slots=8 +node034 slots=8 +node035 slots=8 +node037 slots=8 +node056 slots=8 +node059 slots=8 +node060 slots=8 +node061 slots=8 +node062 slots=8 +node064 slots=8 +node065 slots=8 +node072 slots=8 +node074 slots=8 +node076 slots=8 +node077 slots=8 +node068 slots=8 +node063 slots=8 +node075 slots=8 +node003 slots=8 +node058 slots=8 +node109 slots=8 +node066 slots=8 +node114 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/hostfile_debug b/scripts/accelerate_configs/hostfile_debug new file mode 100644 index 000000000..949e14f68 --- /dev/null +++ b/scripts/accelerate_configs/hostfile_debug @@ -0,0 +1,31 @@ +node30 slots=8 +node004 slots=8 +node005 slots=8 +node006 slots=8 +node007 slots=8 +node008 slots=8 +node010 slots=8 +node012 slots=8 +node013 slots=8 +node019 slots=8 +node026 slots=8 +node027 slots=8 +node028 slots=8 +node029 slots=8 +node031 slots=8 +node032 slots=8 +node033 slots=8 +node034 slots=8 +node035 slots=8 +node037 slots=8 +node056 slots=8 +node060 slots=8 +node061 slots=8 +node062 slots=8 +node064 slots=8 +node063 slots=8 +node003 slots=8 +node058 slots=8 +node109 slots=8 +node114 slots=8 +node011 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index 9dc9dcb70..e7ef55a9f 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -6,10 +6,10 @@ deepspeed_config: fsdp_config: {} machine_rank: 0 main_process_ip: 100.64.24.30 -main_process_port: 29502 +main_process_port: 29522 main_training_function: main -num_machines: 20 -num_processes: 160 +num_machines: 32 +num_processes: 256 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/accelerate_configs/multi_node_example1.yaml b/scripts/accelerate_configs/multi_node_example1.yaml index ad03b2964..4f3697d95 100644 --- a/scripts/accelerate_configs/multi_node_example1.yaml +++ b/scripts/accelerate_configs/multi_node_example1.yaml @@ -2,11 +2,11 @@ compute_environment: LOCAL_MACHINE distributed_type: DEEPSPEED deepspeed_config: deepspeed_config_file: scripts/accelerate_configs/zero2.json - deepspeed_hostfile: scripts/accelerate_configs/hostfile1 + deepspeed_hostfile: scripts/accelerate_configs/hostfile_debug fsdp_config: {} machine_rank: 0 main_process_ip: 100.64.24.30 -main_process_port: 29503 +main_process_port: 29522 main_training_function: main num_machines: 2 num_processes: 16 diff --git a/scripts/accelerate_configs/zero2.json b/scripts/accelerate_configs/zero2.json index 3377cb4b2..8dcdd154d 100644 --- a/scripts/accelerate_configs/zero2.json +++ b/scripts/accelerate_configs/zero2.json @@ -10,6 +10,7 @@ "bf16": { "enabled": "auto" }, + "communication_data_type": "fp32", "gradient_clipping": 1.0, "train_micro_batch_size_per_gpu": "auto", "train_batch_size": "auto", diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index abe95f1c9..ddf433c02 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,5 +1,5 @@ CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ - --model_path /storage/dataset/hw29/image/model_ema \ + --model_path /storage/ongoing/new/checkpoints/bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ @@ -9,7 +9,7 @@ CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ --text_prompt examples/prompt_list_1.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "sample_image" \ + --save_img_path "sample_image_fp32_73000_cfg2.5_step20_480p_pos_neg" \ --fps 24 \ --guidance_scale 2.5 \ --num_sampling_steps 20 \ diff --git a/scripts/text_condition/gpu/sample_video.sh b/scripts/text_condition/gpu/sample_video.sh index aaa23d3d4..d78151596 100644 --- a/scripts/text_condition/gpu/sample_video.sh +++ b/scripts/text_condition/gpu/sample_video.sh @@ -1,19 +1,20 @@ CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_uditultra122_ds222_rope_qknorm_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.25/checkpoint-1500/model_ema \ + --model_path /storage/dataset/hw29/model_ema/checkpoint-500/model_ema \ --version 65x512x512 \ - --num_frames 61 \ - --height 480 \ - --width 640 \ - --cache_dir "../cache_dir" \ + --num_frames 93 \ + --height 720 \ + --width 1280 \ + --cache_dir "cache_dir" \ --text_encoder_name google/mt5-xxl \ - --text_prompt examples/prompt_list_0.txt \ + --text_prompt examples/prompt_list_1.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_video_1500ema_61x480p_k333_s222_cfg2.5_step50_PNDM_bf16" \ + --save_img_path "./sample_video_hw_93x720p_cfg5.0_step50_compile" \ --fps 24 \ - --guidance_scale 2.5 \ + --guidance_scale 5.0 \ --num_sampling_steps 50 \ --enable_tiling \ --max_sequence_length 512 \ --sample_method PNDM \ - --model_3d + --model_type "dit" \ + --compile \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video_ddp.sh b/scripts/text_condition/gpu/sample_video_ddp.sh new file mode 100644 index 000000000..bbaa5375b --- /dev/null +++ b/scripts/text_condition/gpu/sample_video_ddp.sh @@ -0,0 +1,23 @@ +# --save_img_path "./sample_video_26500ema_61x480p_k333_s122_cfg5.0_step50" \ +# CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ +torchrun --nnodes=1 --nproc_per_node 8 --master_port 29503 \ + -m opensora.sample.sample_t2v_ddp \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs32x8x1_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo \ + --version 65x512x512 \ + --num_frames 29 \ + --height 720 \ + --width 1280 \ + --cache_dir "../cache_dir" \ + --text_encoder_name google/mt5-xxl \ + --text_prompt examples/prompt_list_1.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --save_img_path "./sample_video_bs256_ddp_29_29x720p_cfg7.5_step100_neg_pos" \ + --fps 24 \ + --guidance_scale 7.5 \ + --num_sampling_steps 100 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --max_sequence_length 512 \ + --sample_method PNDM \ + --model_type "dit" \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video_ddp1.sh b/scripts/text_condition/gpu/sample_video_ddp1.sh new file mode 100644 index 000000000..08d6cb800 --- /dev/null +++ b/scripts/text_condition/gpu/sample_video_ddp1.sh @@ -0,0 +1,23 @@ +# --save_img_path "./sample_video_26500ema_61x480p_k333_s122_cfg5.0_step50" \ +# CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ +torchrun --nnodes=1 --nproc_per_node 8 --master_port 29503 \ + -m opensora.sample.sample_t2v_ddp \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs36xsp2_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.1 \ + --version 65x512x512 \ + --num_frames 29 \ + --height 720 \ + --width 1280 \ + --cache_dir "../cache_dir" \ + --text_encoder_name google/mt5-xxl \ + --text_prompt examples/prompt_list_1.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --save_img_path "./sample_video_ddp_29_29x720p_cfg5.0_step50_neg_pos_dmp" \ + --fps 24 \ + --guidance_scale 5.0 \ + --num_sampling_steps 50 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --max_sequence_length 512 \ + --sample_method DPMSolverMultistep \ + --model_type "dit" \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video_hw.sh b/scripts/text_condition/gpu/sample_video_hw.sh index e307301fd..673c0443f 100644 --- a/scripts/text_condition/gpu/sample_video_hw.sh +++ b/scripts/text_condition/gpu/sample_video_hw.sh @@ -1,19 +1,19 @@ CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ - --model_path /storage/dataset/hw29/image/model_ema \ + --model_path /storage/dataset/hw29/model_ema \ --version 65x512x512 \ --num_frames 29 \ --height 480 \ --width 640 \ --cache_dir "../cache_dir" \ --text_encoder_name google/mt5-xxl \ - --text_prompt examples/prompt_list_0.txt \ + --text_prompt examples/prompt_list_1.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "sample_video_hw" \ + --save_img_path "sample_video_29x480p_hw_pndm_ema_fp32" \ --fps 24 \ - --guidance_scale 2.5 \ - --num_sampling_steps 20 \ + --guidance_scale 5.0 \ + --num_sampling_steps 50 \ --enable_tiling \ --max_sequence_length 512 \ - --sample_method DPMSolverMultistep \ - --model_3d \ No newline at end of file + --sample_method PNDM \ + --model_type dit \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video_sp.sh b/scripts/text_condition/gpu/sample_video_sp.sh new file mode 100644 index 000000000..d66abb75a --- /dev/null +++ b/scripts/text_condition/gpu/sample_video_sp.sh @@ -0,0 +1,24 @@ +# --save_img_path "./sample_video_26500ema_61x480p_k333_s122_cfg5.0_step50" \ +# CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ + +torchrun --nnodes=1 --nproc_per_node 8 --master_port 29503 \ + -m opensora.sample.sample_t2v_sp \ + --model_path /storage/dataset/hw29/model_ema \ + --version 65x512x512 \ + --num_frames 29 \ + --height 480 \ + --width 640 \ + --cache_dir "../cache_dir" \ + --text_encoder_name google/mt5-xxl \ + --text_prompt examples/prompt_list_2.txt \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --save_img_path "./sample_video_sp_29x720p_cfg10.0_step50_neg_pos_beng" \ + --fps 24 \ + --guidance_scale 2.5 \ + --num_sampling_steps 100 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --max_sequence_length 512 \ + --sample_method DDIM \ + --model_type "dit" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh b/scripts/text_condition/gpu/train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh new file mode 100644 index 000000000..4bdb5459c --- /dev/null +++ b/scripts/text_condition/gpu/train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh @@ -0,0 +1,68 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 61 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k333_s122" \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.2 \ + --pretrained "/storage/ongoing/new/image2video_weight/480p_73000_ema_ds_k3_p1_repeat.pt" \ + --output_dir="bs20x8x2_61x480p_lr1e-4_snr5_noioff0.02_new_uditultra122_ds122_rope_qknorm_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_video3d_125x480p.sh b/scripts/text_condition/gpu/train_video3d_125x480p.sh new file mode 100644 index 000000000..1f5e8fa1b --- /dev/null +++ b/scripts/text_condition/gpu/train_video3d_125x480p.sh @@ -0,0 +1,67 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 125 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.2 \ + --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs36x8x1_125x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_video3d_125x480p_sp.sh b/scripts/text_condition/gpu/train_video3d_125x480p_sp.sh new file mode 100644 index 000000000..f8dbc477a --- /dev/null +++ b/scripts/text_condition/gpu/train_video3d_125x480p_sp.sh @@ -0,0 +1,69 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/debug.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 125 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 0 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.0 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.1 \ + --enable_stable_fp32 \ + --ema_decay 0.99 \ + --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs36x8x1_125x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2/checkpoint-4500/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs36xsp4_29x720p_lr1e-4_snr5_noioff0.02_ema99_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.1" diff --git a/scripts/text_condition/gpu/train_video3d_29x720p.sh b/scripts/text_condition/gpu/train_video3d_29x720p.sh new file mode 100644 index 000000000..4f13b30b5 --- /dev/null +++ b/scripts/text_condition/gpu/train_video3d_29x720p.sh @@ -0,0 +1,69 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +# export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 29 \ + --max_height 720 \ + --max_width 1280 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.5 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=250 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.0 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.0 \ + --enable_stable_fp32 \ + --ema_decay 0.999 \ + --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs32xsp2_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo/checkpoint-6500/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs32x8x1_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo" diff --git a/scripts/text_condition/gpu/train_video3d_29x720p_sp.sh b/scripts/text_condition/gpu/train_video3d_29x720p_sp.sh new file mode 100644 index 000000000..8886c6975 --- /dev/null +++ b/scripts/text_condition/gpu/train_video3d_29x720p_sp.sh @@ -0,0 +1,71 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 29 \ + --max_height 720 \ + --max_width 1280 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.5 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.0 \ + --enable_stable_fp32 \ + --ema_decay 0.999 \ + --sp_size 8 \ + --train_sp_batch_size 2 \ + --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs32xsp2_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo" diff --git a/scripts/text_condition/gpu/train_video3d_93x720p.sh b/scripts/text_condition/gpu/train_video3d_93x720p.sh new file mode 100644 index 000000000..267358c1a --- /dev/null +++ b/scripts/text_condition/gpu/train_video3d_93x720p.sh @@ -0,0 +1,69 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 93 \ + --max_height 720 \ + --max_width 1280 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.5 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=1 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.2 \ + --enable_stable_fp32 \ + --ema_decay 0.99 \ + --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs29x8x1_93x720p_lr1e-4_snr5_noioff0.02_ema99_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh index 2c5b949cd..281b4032e 100644 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh @@ -13,18 +13,18 @@ export NCCL_ALGO=Tree export OMP_NUM_THREADS=1 accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v_diffusers.py \ --model UDiTUltraT2V-L/122 \ --text_encoder_name google/mt5-xxl \ - --cache_dir "../cache_dir" \ + --cache_dir "./cache_dir" \ --dataset t2v \ --video_data "scripts/train_data/video_data_aesmovie_sucai.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 125 \ + --num_frames 61 \ --max_height 480 \ --max_width 640 \ --interpolation_scale_t 1.0 \ @@ -53,9 +53,10 @@ accelerate launch \ --cfg 0.1 \ --noise_offset 0.02 \ --use_rope \ - --downsampler "k333_s122" \ + --downsampler "k333_s222" \ --resume_from_checkpoint="latest" \ + --enable_stable_fp32 \ --group_frame \ --speed_factor 1.2 \ - --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2/checkpoint-12500/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="debug" \ No newline at end of file + --pretrained "bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_aesmovie_sucai_speed1.4/checkpoint-7000/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh new file mode 100644 index 000000000..c7f2b9342 --- /dev/null +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh @@ -0,0 +1,68 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +# export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +export NCCL_ALGO=Tree + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 61 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k333_s122" \ + --resume_from_checkpoint="latest" \ + --group_frame \ + --speed_factor 1.2 \ + --pretrained "/storage/ongoing/new/image2video_weight/480p_73000_ema_k3_p1_repeat_wusun.pt" \ + --output_dir="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_uditultra122_ds122_rope_qknorm_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh new file mode 100644 index 000000000..1959b64ca --- /dev/null +++ b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh @@ -0,0 +1,62 @@ +export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" +# export WANDB_MODE="offline" +export ENTITY="linbin" +export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 +# NCCL setting +export PDSH_RCMD_TYPE=ssh +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Tree +export OMP_NUM_THREADS=1 + +accelerate launch \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ + opensora/train/train_t2v_diffusers.py \ + --model UDiTUltraT2V-L/122 \ + --text_encoder_name google/mt5-xxl \ + --cache_dir "./cache_dir" \ + --dataset t2v \ + --video_data "scripts/train_data/video_data_movie_time_sucai.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/dataset/test140k" \ + --sample_rate 1 \ + --num_frames 61 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 2.0 \ + --interpolation_scale_w 2.0 \ + --attention_mode xformers \ + --gradient_checkpointing \ + --train_batch_size=2 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=1000000 \ + --learning_rate=1e-4 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --checkpointing_steps=500 \ + --allow_tf32 \ + --model_max_length 512 \ + --use_image_num 0 \ + --tile_overlap_factor 0.125 \ + --enable_tiling \ + --snr_gamma 5.0 \ + --use_ema \ + --ema_start_step 0 \ + --cfg 0.1 \ + --noise_offset 0.02 \ + --use_rope \ + --downsampler "k333_s222" \ + --resume_from_checkpoint="latest" \ + --enable_tracker \ + --enable_stable_fp32 \ + --group_frame \ + --pretrained "bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-11500/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_movie_time_sucai" \ No newline at end of file diff --git a/scripts/train_data/video_data.txt b/scripts/train_data/video_data.txt index c536c725c..b47ed9c60 100644 --- a/scripts/train_data/video_data.txt +++ b/scripts/train_data/video_data.txt @@ -6,4 +6,4 @@ /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_mixkit_final_4490.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pexels_final_266826.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_pixabay_v2_final_21463.json -/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_storyblocks_final_1270905.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_storyblocks_final_1270905.json \ No newline at end of file diff --git a/scripts/train_data/video_data_aesmovie_panda.txt b/scripts/train_data/video_data_aesmovie_panda.txt new file mode 100644 index 000000000..b26ad56a8 --- /dev/null +++ b/scripts/train_data/video_data_aesmovie_panda.txt @@ -0,0 +1,7 @@ +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc01_250508.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc02_289778.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc03_519184.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc04_249497.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc05_416548.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/TV01_217419.json +/storage/dataset/panda70m,/storage/dataset/panda70m/panda70m_last_6268414_flowValue.json \ No newline at end of file diff --git a/scripts/train_data/video_data_aesmovie_sucai_panda.txt b/scripts/train_data/video_data_aesmovie_sucai_panda.txt index 67682fdc8..f583df7f6 100644 --- a/scripts/train_data/video_data_aesmovie_sucai_panda.txt +++ b/scripts/train_data/video_data_aesmovie_sucai_panda.txt @@ -1,9 +1,9 @@ -/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc01_250508.json -/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc02_289778.json -/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc03_519184.json -/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc04_249497.json -/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc05_416548.json -/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/TV01_217419.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc01_250508.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc02_289778.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc03_519184.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc04_249497.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/bbc05_416548.json +/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb_7_11/TV01_217419.json /storage/dataset/panda70m,/storage/dataset/panda70m/panda70m_last_6268414_flowValue.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95441.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_clipchamp_final_452264.json diff --git a/scripts/train_data/video_data_debug.txt b/scripts/train_data/video_data_debug.txt index 2796cd79d..b41b850ce 100644 --- a/scripts/train_data/video_data_debug.txt +++ b/scripts/train_data/video_data_debug.txt @@ -1 +1 @@ -/storage/dataset/movie,/storage/dataset/movie/merge_cap_lb/bbc01_250508.json \ No newline at end of file +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95441.json \ No newline at end of file From 8b7c285a508cdae122d855c9454412401f77c67a Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Sun, 14 Jul 2024 07:17:05 +0000 Subject: [PATCH 084/134] merge latest train script --- docs/Contribution_Guidelines.md | 87 ++++++++++++++ docs/Data.md | 39 +++++++ docs/EVAL.md | 110 ++++++++++++++++++ docs/Report-v1.0.0-cn.md | 135 ++++++++++++++++++++++ docs/Report-v1.0.0.md | 136 ++++++++++++++++++++++ docs/Train_And_Eval_CausalVideoVAE.md | 158 ++++++++++++++++++++++++++ docs/VQVAE.md | 57 ++++++++++ 7 files changed, 722 insertions(+) create mode 100644 docs/Contribution_Guidelines.md create mode 100644 docs/Data.md create mode 100644 docs/EVAL.md create mode 100644 docs/Report-v1.0.0-cn.md create mode 100644 docs/Report-v1.0.0.md create mode 100644 docs/Train_And_Eval_CausalVideoVAE.md create mode 100644 docs/VQVAE.md diff --git a/docs/Contribution_Guidelines.md b/docs/Contribution_Guidelines.md new file mode 100644 index 000000000..d7f163a3d --- /dev/null +++ b/docs/Contribution_Guidelines.md @@ -0,0 +1,87 @@ +# Contributing to the Open-Sora Plan Community + +The Open-Sora Plan open-source community is a collaborative initiative driven by the community, emphasizing a commitment to being free and void of exploitation. Organized spontaneously by community members, we invite you to contribute to the Open-Sora Plan open-source community and help elevate it to new heights! + +## Submitting a Pull Request (PR) + +As a contributor, before submitting your request, kindly follow these guidelines: + +1. Start by checking the [Open-Sora Plan GitHub](https://github.com/PKU-YuanGroup/Open-Sora-Plan/pulls) to see if there are any open or closed pull requests related to your intended submission. Avoid duplicating existing work. + +2. [Fork](https://github.com/PKU-YuanGroup/Open-Sora-Plan/fork) the [open-sora plan](https://github.com/PKU-YuanGroup/Open-Sora-Plan) repository and download your forked repository to your local machine. + + ```bash + git clone [your-forked-repository-url] + ``` + +3. Add the original Open-Sora Plan repository as a remote to sync with the latest updates: + + ```bash + git remote add upstream https://github.com/PKU-YuanGroup/Open-Sora-Plan + ``` + +4. Sync the code from the main repository to your local machine, and then push it back to your forked remote repository. + + ``` + # Pull the latest code from the upstream branch + git fetch upstream + + # Switch to the main branch + git checkout main + + # Merge the updates from the upstream branch into main, synchronizing the local main branch with the upstream + git merge upstream/main + + # Additionally, sync the local main branch to the remote branch of your forked repository + git push origin main + ``` + + + > Note: Sync the code from the main repository before each submission. + +5. Create a branch in your forked repository for your changes, ensuring the branch name is meaningful. + + ```bash + git checkout -b my-docs-branch main + ``` + +6. While making modifications and committing changes, adhere to our [Commit Message Format](#Commit-Message-Format). + + ```bash + git commit -m "[docs]: xxxx" + ``` + +7. Push your changes to your GitHub repository. + + ```bash + git push origin my-docs-branch + ``` + +8. Submit a pull request to `Open-Sora-Plan:main` on the GitHub repository page. + +## Commit Message Format + +Commit messages must include both `` and `` sections. + +```bash +[]: + │ │ + │ └─⫸ Briefly describe your changes, without ending with a period. + │ + └─⫸ Commit Type: |docs|feat|fix|refactor| +``` + +### Type + +* **docs**: Modify or add documents. +* **feat**: Introduce a new feature. +* **fix**: Fix a bug. +* **refactor**: Restructure code, excluding new features or bug fixes. + +### Summary + +Describe modifications in English, without ending with a period. + +> e.g., git commit -m "[docs]: add a contributing.md file" + +This guideline is borrowed by [minisora](https://github.com/mini-sora/minisora). We sincerely appreciate MiniSora authors for their awesome templates. diff --git a/docs/Data.md b/docs/Data.md new file mode 100644 index 000000000..a8f493701 --- /dev/null +++ b/docs/Data.md @@ -0,0 +1,39 @@ + +**We need more dataset**, please refer to the [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset) for details. + +## v1.0.0 + +### Text-to-Video + +We open source v1.0.0 all the training data, the annotations and the original video can be found [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0). + +These data consist of segmented video clips, with each clip obtained through center cropping. The resolution of each clip is 512×512. There are 64 frames in each clip, and their corresponding captions can be found in the annotation files. + +We present additional details in [report](https://github.com/PKU-YuanGroup/Open-Sora-Plan/blob/main/docs/Report-v1.0.0.md#data-construction) and [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset). + +### Class-condition + +In order to download UCF-101 dataset, you can download the necessary files in [here](https://www.crcv.ucf.edu/data/UCF101.php). The code assumes a `ucf101` directory with the following structure +``` +UCF-101/ + ApplyEyeMakeup/ + v1.avi + ... + ... + YoYo/ + v1.avi + ... +``` + +### Un-condition + +We use [sky_timelapse](https://drive.google.com/open?id=1xWLiU-MBGN7MrsFHQm4_yXmfHBsMbJQo), which is an un-condition datasets. + +``` +sky_timelapse +├── readme +├── sky_test +├── sky_train +├── test_videofolder.py +└── video_folder.py +``` diff --git a/docs/EVAL.md b/docs/EVAL.md new file mode 100644 index 000000000..3796c7909 --- /dev/null +++ b/docs/EVAL.md @@ -0,0 +1,110 @@ +# Evaluate the generated videos quality + +You can easily calculate the following video quality metrics, which supports the batch-wise process. +- **CLIP-SCORE**: It uses the pretrained CLIP model to measure the cosine similarity between two modalities. +- **FVD**: Frechét Video Distance +- **SSIM**: structural similarity index measure +- **LPIPS**: learned perceptual image patch similarity +- **PSNR**: peak-signal-to-noise ratio + +# Requirement +## Environment +- install Pytorch (torch>=1.7.1) +- install CLIP + ``` + pip install git+https://github.com/openai/CLIP.git + ``` +- install clip-cose from PyPi + ``` + pip install clip-score + ``` +- Other package + ``` + pip install lpips + pip install scipy (scipy==1.7.3/1.9.3, if you use 1.11.3, **you will calculate a WRONG FVD VALUE!!!**) + pip install numpy + pip install pillow + pip install torchvision>=0.8.2 + pip install ftfy + pip install regex + pip install tqdm + ``` +## Pretrain model +- FVD + Before you cacluate FVD, you should first download the FVD pre-trained model. You can manually download any of the following and put it into FVD folder. + - `i3d_torchscript.pt` from [here](https://www.dropbox.com/s/ge9e5ujwgetktms/i3d_torchscript.pt) + - `i3d_pretrained_400.pt` from [here](https://onedrive.live.com/download?cid=78EEF3EB6AE7DBCB&resid=78EEF3EB6AE7DBCB%21199&authkey=AApKdFHPXzWLNyI) + + +## Other Notices +1. Make sure the pixel value of videos should be in [0, 1]. +2. We average SSIM when images have 3 channels, ssim is the only metric extremely sensitive to gray being compared to b/w. +3. Because the i3d model downsamples in the time dimension, `frames_num` should > 10 when calculating FVD, so FVD calculation begins from 10-th frame, like upper example. +4. For grayscale videos, we multiply to 3 channels +5. data input specifications for clip_score +> - Image Files:All images should be stored in a single directory. The image files can be in either .png or .jpg format. +> +> - Text Files: All text data should be contained in plain text files in a separate directory. These text files should have the extension .txt. +> +> Note: The number of files in the image directory should be exactly equal to the number of files in the text directory. Additionally, the files in the image directory and text directory should be paired by file name. For instance, if there is a cat.png in the image directory, there should be a corresponding cat.txt in the text directory. +> +> Directory Structure Example: +> ``` +> ├── path/to/image +> │ ├── cat.png +> │ ├── dog.png +> │ └── bird.jpg +> └── path/to/text +> ├── cat.txt +> ├── dog.txt +> └── bird.txt +> ``` + +6. data input specifications for fvd, psnr, ssim, lpips + +> Directory Structure Example: +> ``` +> ├── path/to/generated_image +> │ ├── cat.mp4 +> │ ├── dog.mp4 +> │ └── bird.mp4 +> └── path/to/real_image +> ├── cat.mp4 +> ├── dog.mp4 +> └── bird.mp4 +> ``` + + + +# Usage + +``` +# you change the file path and need to set the frame_num, resolution etc... + +# clip_score cross modality +cd opensora/eval +bash script/cal_clip_score.sh + + + +# fvd +cd opensora/eval +bash script/cal_fvd.sh + +# psnr +cd opensora/eval +bash eval/script/cal_psnr.sh + + +# ssim +cd opensora/eval +bash eval/script/cal_ssim.sh + + +# lpips +cd opensora/eval +bash eval/script/cal_lpips.sh +``` + +# Acknowledgement +The evaluation codebase refers to [clip-score](https://github.com/Taited/clip-score) and [common_metrics](https://github.com/JunyaoHu/common_metrics_on_video_quality). \ No newline at end of file diff --git a/docs/Report-v1.0.0-cn.md b/docs/Report-v1.0.0-cn.md new file mode 100644 index 000000000..3ddc23e0b --- /dev/null +++ b/docs/Report-v1.0.0-cn.md @@ -0,0 +1,135 @@ +# 技术报告 v1.0.0 + +在2024年3月,我们推出了Open-Sora-Plan,一个旨在复现OpenAI [Sora](https://openai.com/sora)的开源计划。它作为一个基础的开源框架,能够训练视频生成模型包括无条件视频生成,类别引导视频生成,文生视频。 + +**今天,我们兴奋地展示Open-Sora-Plan v1.0.0,极大地改进视频生成质量、文本控制能力。** + +相比于之前的视频生成模型,Open-Sora-Plan v1.0.0 有以下的改进: + +1. **CausalVideoVAE高效的训练与推理**。 我们用4×8×8的对视频进行时间和空间的压缩。 +2. **图片视频联合训练提升视觉质量**。 CasualVideoVAE 将首帧看作图片,天然支持同时编码图片和视频。这允许扩散模型提取更多时空细节来改善质量。 + + +### Open-Source Release +我们开源了Open-Sora-Plan去促进视频生成社区的进一步发展。公开代码、数据、模型。 +- 在线演示:Hugging Face [![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0), [![Replicate demo and cloud API](https://replicate.com/camenduru/open-sora-plan-512x512/badge)](https://replicate.com/camenduru/open-sora-plan-512x512) 和 [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/camenduru/Open-Sora-Plan-jupyter/blob/main/Open_Sora_Plan_jupyter.ipynb), 感谢[@camenduru](https://github.com/camenduru)大力支持我们的工作!🤝 +- 代码:所有训练脚本和采样代码。 +- 模型:包括扩散模型和CausalVideoVAE [这里](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0)。 +- 数据:所有原视频和对应描述 [这里](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0)。 + +## 效果 + +Open-Sora-Plan v1.0.0支持图片视频联合训练。我们在此展示视频和图片的重建以及生成: + +720×1280**视频重建**。 因为github的限制,原视频放在: [1](https://streamable.com/gqojal), [2](https://streamable.com/6nu3j8). + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/c100bb02-2420-48a3-9d7b-4608a41f14aa + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/8aa8f587-d9f1-4e8b-8a82-d3bf9ba91d68 + +1536×1024**图片重建** + + + +65×1024×1024**文生视频** + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/2641a8aa-66ac-4cda-8279-86b2e6a6e011 + +65×512×512**文生视频** + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/37e3107e-56b3-4b09-8920-fa1d8d144b9e + + +512×512**文生视频** + +![download](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/491d72bc-e762-48ff-bdcc-cc69350f56d6) + +## 详细技术报告 + +### CausalVideoVAE + +#### 模型结构 + +![image](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/e3c8b35d-a217-4d96-b2e9-5c248a2859c8) + +因果VAE架构继承了[Stable-Diffusion Image VAE](https://github.com/CompVis/stable-diffusion/tree/main)。 为了保证图片VAE的预训练权重可以无缝运用到视频VAE中,模型结构采取如下设计: + +1. **CausalConv3D**: 将Conv2D 转变成CausalConv3D可以实现图片和视频的联合训练. CausalConv3D 对第一帧进行特殊处理,因为它无法访问后续帧。对于更多细节,请参考https://github.com/PKU-YuanGroup/Open-Sora-Plan/pull/145 + +2. **初始化**:将Conv2D扩展到Conv3D常用的[方法](https://github.com/hassony2/inflated_convnets_pytorch/blob/master/src/inflate.py#L5)有两种:平均初始化和中心初始化。 但我们采用了特定的初始化方法(尾部初始化)。 这种初始化方法确保模型无需任何训练就能够直接重建图像,甚至视频。 + +#### 训练细节 + +image + +我们展示了 17×256×256 下两种不同初始化方法的损失曲线。黄色曲线代表使用尾部初始化的损失,而蓝色曲线对应中心初始化的损失。 如图所示,尾部初始化在损失曲线上表现出更好的性能。 此外,我们发现中心初始化会导致错误累积,导致长时间内崩溃。 + +#### 推理技巧 +尽管训练Diffusion中VAE始终是冻住的,我们仍然无法负担CasualVideoVAE的花销。在我们的实验中, 80G的显存只能够在半精度下推理一个256×512×512或32×1024×1024的视频 ,这限制了我们扩展到更长更高清的视频。因此我们采用tile convolution,能够以几乎恒定的内存推理任意时长或任意分辨率的视频。 + +### 数据构建 +我们定义高质量的视频数据集包括两个核心法则:(1) 没有与内容无关的水印。(2) 高质量的文本注释。 + +**对于法则1**,我们从开源网站(CC0协议)爬取了大约40k videos:1234个来自[mixkit](https://mixkit.co/),7408个来自[pexels](https://www.pexels.com/),31616个来自[pixabay](https://pixabay.com/)。我们根据[Panda70M](https://github.com/snap-research/Panda-70M/blob/main/splitting/README.md)提供的场景变换剪切script将这些视频切成大约434k video clips。事实上,根据我们的剪切结果,从这些网上上爬取的99%的视频都是单一的场景。另外,我们发现爬取的数据中超过60%为风景相关视频。更多细节可以在[这](https://github.com/PKU-YuanGroup/Open-Sora-Dataset)找到。 + +**对于法则2**,很难有大量的高质量的文本注释能够从网上直接爬取。因此我们用成熟的图片标注模型来获取高质量的稠密描述。我们对2个多模态大模型进行消融实验:[ShareGPT4V-Captioner-7B](https://github.com/InternLM/InternLM-XComposer/blob/main/projects/ShareGPT4V/README.md) 和 [LLaVA-1.6-34B](https://github.com/haotian-liu/LLaVA)。前者是专门用来制作文本注释的模型,而后者是一个通用的多模态大模型。经过我们的消融实验,他们在caption的表现差不多。然而他们的推理速度在A800上差距很大:40s/it of batch size of 12 for ShareGPT4V-Captioner-7B,15s/it of batch size of 1 for ShareGPT4V-Captioner-7B。我们开源所有的[文本注释和原视频](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0)。 + +| 模型名字 | 平均长度 | 最大值 | 标准差 | +|---|---|---|---| +| ShareGPT4V-Captioner-7B | 170.0827524529121 | 467 | 53.689967539537776 | +| LLaVA-1.6-34B | 141.75851073472666 | 472 | 48.52492072346965 | + +### 训练扩散模型 +与之前的工作类似,我们采用多阶段的级联的训练方法,总共消耗了2048个A800 GPU 小时。我们发现联合图片训练能够显著加速模型的收敛并且增强视觉观感,这与[Latte](https://github.com/Vchitect/Latte)一致。以下是我们的训练花销。 + +| 名字 | Stage 1 | Stage 2 | Stage 3 | Stage 4 | +|---|---|---|---|---| +| 训练视频尺寸 | 17×256×256 | 65×256×256 | 65×512×512 | 65×1024×1024 | +| 计算资源 (#A800 GPU x #小时) | 32 × 40 | 32 × 18 | 32 × 6 | 训练中 | +| 权重 | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/17x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x512x512) | 训练中 | +| 日志 | [wandb](https://api.wandb.ai/links/linbin/p6n3evym) | [wandb](https://api.wandb.ai/links/linbin/t2g53sew) | [wandb](https://api.wandb.ai/links/linbin/uomr0xzb) | 训练中 | +| 训练数据 | ~40k videos | ~40k videos | ~40k videos | ~40k videos | + +## 下版本预览 +### CausalVideoVAE +目前我们发布的CausalVideoVAE v1.0.0版本存在2个主要的缺陷:**运动模糊**以及**网格效应**。我们对CasualVideoVAE做了一系列的改进使它推理成本更低且性能更强大,我们暂时叫它为预览版本,将在下个版本发布。 + +**1分钟720×1280视频重建**。 受限于GitHub,我们将原视频放在这:[原视频](https://streamable.com/u4onbb),[重建视频](https://streamable.com/qt8ncc)。 + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/cdcfa9a3-4de0-42d4-94c0-0669710e407b + +我们从kinetic 400的验证集中随机选取100个样本进行评估,结果表如下所示: + +| | SSIM↑ | LPIPS↓ | PSNR↑ | FLOLPIPS↓ | +|---|---|---|---|---| +| v1.0.0 | 0.829 | 0.106 | 27.171 | 0.119 | +| Preview | 0.877 | 0.064 | 29.695 | 0.070 | + +#### 运动模糊 + +| **v1.0.0** | **预览版本** | +| --- | --- | +| ![6862cae0-b1b6-48d1-bd11-84348cf42b42](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/f815636f-fb38-4891-918b-50b1f9aa086d) | ![9189da06-ef2c-42e6-ad34-bd702a6f538e](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/1e413f50-a785-485a-9851-a1449f952f1c) | + +#### 网格效应 + +| **v1.0.0** | **预览版本** | +| --- | --- | +| ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/7fec5bed-3c83-4ee9-baef-4a3dacafc658) | ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/4f41b432-a3ef-484e-a492-8afd8a691bf7) | + +### 数据构建 + +**数据源**:正如上文提到,我们的数据集中超过60%为风景视频。这意味着我们的开域视频生成能力有限。然而当前的大规模开源数据集大多从YouTube爬取,尽管视频的数量多,但我们担忧视频本身的质量是否达标。因此,我们将继续收集高质量的数据集,同时也欢迎开源社区的推荐。 + +**Caption生成流程**:当我们训练时长增加时,我们不得不考虑更有效的视频caption生成方法,而不是多模态图片大模型。我们正在开发一个新的视频注释生成管线,它能够很好的支持长视频,敬请期待。 + +### 训练扩散模型 +尽管目前v1.0.0展现了可喜的结果,但我们仍然离Sora有一段距离。在接下来的工作中,我们主要围绕这三个方面: + +1. **动态分辨率与时长的训练**: 我们的目标是开发出能够以不同分辨率和持续时间训练模型的技术,使训练过程更加灵活、适应性更强。 + +2. **更长的视频生成**: 我们将探索扩展模型生成能力的方法,使其能够制作更长的视频,超越目前的限制。 + +3. **更多条件控制**: 我们力求增强模型的条件控制能力,为用户提供更多的选项和对生成视频的控制能力。 + +另外,通过仔细观察生成的视频,我们发现存在一些不符合常理的斑点或异常的流动,这是由于CasualVideoVAE的性能不足导致的 如上面提到。在未来的实验中,我们将使用更强的VAE,重新训练一个扩散模型。 diff --git a/docs/Report-v1.0.0.md b/docs/Report-v1.0.0.md new file mode 100644 index 000000000..8b8419976 --- /dev/null +++ b/docs/Report-v1.0.0.md @@ -0,0 +1,136 @@ +# Report v1.0.0 + +In March 2024, we launched a plan called Open-Sora-Plan, which aims to reproduce the OpenAI [Sora](https://openai.com/sora) through an open-source framework. As a foundational open-source framework, it enables training of video generation models, including Unconditioned Video Generation, Class Video Generation, and Text-to-Video Generation. + +**Today, we are thrilled to present Open-Sora-Plan v1.0.0, which significantly enhances video generation quality and text control capabilities.** + +Compared with previous video generation model, Open-Sora-Plan v1.0.0 has several improvements: + +1. **Efficient training and inference with CausalVideoVAE**. We apply a spatial-temporal compression to the videos by 4×8×8. +2. **Joint image-video training for better quality**. Our CausalVideoVAE considers the first frame as an image, allowing for the simultaneous encoding of both images and videos in a natural manner. This allows the diffusion model to grasp more spatial-visual details to improve visual quality. + +### Open-Source Release +We open-source the Open-Sora-Plan to facilitate future development of Video Generation in the community. Code, data, model are made publicly available. +- Demo: Hugging Face demo [![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0). 🤝 Enjoying the [![Replicate demo and cloud API](https://replicate.com/camenduru/open-sora-plan-512x512/badge)](https://replicate.com/camenduru/open-sora-plan-512x512) and [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/camenduru/Open-Sora-Plan-jupyter/blob/main/Open_Sora_Plan_jupyter.ipynb), created by [@camenduru](https://github.com/camenduru), who generously supports our research! +- Code: All training scripts and sample scripts. +- Model: Both Diffusion Model and CausalVideoVAE [here](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0). +- Data: Both raw videos and captions [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0). + +## Gallery + +Open-Sora-Plan v1.0.0 supports joint training of images and videos. Here, we present the capabilities of Video/Image Reconstruction and Generation: + +### CausalVideoVAE Reconstruction + +**Video Reconstruction** with 720×1280. Since github can't upload large video, we put it here: [1](https://streamable.com/gqojal), [2](https://streamable.com/6nu3j8). + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/c100bb02-2420-48a3-9d7b-4608a41f14aa + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/8aa8f587-d9f1-4e8b-8a82-d3bf9ba91d68 + +**Image Reconstruction** in 1536×1024. + + + +**Text-to-Video Generation** with 65×1024×1024 + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/2641a8aa-66ac-4cda-8279-86b2e6a6e011 + +**Text-to-Video Generation** with 65×512×512 + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/37e3107e-56b3-4b09-8920-fa1d8d144b9e + + +**Text-to-Image Generation** with 512×512 + +![download](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/491d72bc-e762-48ff-bdcc-cc69350f56d6) + +## Detailed Technical Report + +### CausalVideoVAE + +#### Model Structure + +![image](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/e3c8b35d-a217-4d96-b2e9-5c248a2859c8) + +The CausalVideoVAE architecture inherits from the [Stable-Diffusion Image VAE](https://github.com/CompVis/stable-diffusion/tree/main). To ensure that the pretrained weights of the Image VAE can be seamlessly applied to the Video VAE, the model structure has been designed as follows: + +1. **CausalConv3D**: Converting Conv2D to CausalConv3D enables joint training of image and video data. CausalConv3D applies a special treatment to the first frame, as it does not have access to subsequent frames. For more specific details, please refer to https://github.com/PKU-YuanGroup/Open-Sora-Plan/pull/145 + +2. **Initialization**: There are two common [methods](https://github.com/hassony2/inflated_convnets_pytorch/blob/master/src/inflate.py#L5) to expand Conv2D to Conv3D: average initialization and center initialization. But we employ a specific initialization method (tail initialization). This initialization method ensures that without any training, the model is capable of directly reconstructing images, and even videos. + +#### Training Details + +image + +We present the loss curves for two distinct initialization methods under 17×256×256. The yellow curve represents the loss using tail init, while the blue curve corresponds to the loss from center initialization. As shown in the graph, tail initialization demonstrates better performance on the loss curve. Additionally, we found that center initialization leads to error accumulation, causing the collapse over extended durations. + +#### Inference Tricks +Despite the VAE in Diffusion training being frozen, we still find it challenging to afford the cost of the CausalVideoVAE. In our case, with 80GB of GPU memory, we can only infer a video of either 256×512×512 or 32×1024×1024 resolution using half-precision, which limits our ability to scale up to longer and higher-resolution videos. Therefore, we adopt tile convolution, which allows us to infer videos of arbitrary duration or resolution with nearly constant memory usage. + +### Data Construction +We define a high-quality video dataset based on two core principles: (1) No content-unrelated watermarks. (2) High-quality and dense captions. + +**For principles 1**, we crawled approximately 40,000 videos from open-source websites under the CC0 license. Specifically, we obtained 1,234 videos from [mixkit](https://mixkit.co/), 7,408 videos from [pexels](https://www.pexels.com/), and 31,616 videos from [pixabay](https://pixabay.com/). These videos adhere to the principle of having no content-unrelated watermarks. According to the scene transformation and clipping script provided by [Panda70M](https://github.com/snap-research/Panda-70M/blob/main/splitting/README.md), we have divided these videos into approximately 434,000 video clips. In fact, based on our clipping results, 99% of the videos obtained from these online sources are found to contain single scenes. Additionally, we have observed that over 60% of the crawled data comprises landscape videos. More details can be found [here](https://github.com/PKU-YuanGroup/Open-Sora-Dataset). + +**For principles 2**, it is challenging to directly crawl a large quantity of high-quality dense captions from the internet. Therefore, we utilize a mature Image-captioner model to obtain high-quality dense captions. We conducted ablation experiments on two multimodal large models: [ShareGPT4V-Captioner-7B](https://github.com/InternLM/InternLM-XComposer/blob/main/projects/ShareGPT4V/README.md) and [LLaVA-1.6-34B](https://github.com/haotian-liu/LLaVA). The former is specifically designed for caption generation, while the latter is a general-purpose multimodal large model. After conducting our ablation experiments, we found that they are comparable in performance. However, there is a significant difference in their inference speed on the A800 GPU: 40s/it of batch size of 12 for ShareGPT4V-Captioner-7B, 15s/it of batch size of 1 for LLaVA-1.6-34B. We open-source all annotations [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.0.0). We show some statistics here, and we set the maximum length of the model to 300, which covers almost 99% of the samples. + +| Name | Avg length | Max | Std | +|---|---|---|---| +| ShareGPT4V-Captioner-7B | 170.0827524529121 | 467 | 53.689967539537776 | +| LLaVA-1.6-34B | 141.75851073472666 | 472 | 48.52492072346965 | + +### Training Diffusion Model +Similar to previous work, we employ a multi-stage cascaded training approach, which consumes a total of 2,528 A800 GPU hours. We found that joint training with images significantly accelerates model convergence and enhances visual perception, aligning with the findings of [Latte](https://github.com/Vchitect/Latte). Below is our training card: + +| Name | Stage 1 | Stage 2 | Stage 3 | Stage 4 | +|---|---|---|---|---| +| Training Video Size | 17×256×256 | 65×256×256 | 65×512×512 | 65×1024×1024 | +| Compute (#A800 GPU x #Hours) | 32 × 40 | 32 × 22 | 32 × 17 | Under training | +| Checkpoint | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/17x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x256x256) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.0.0/tree/main/65x512x512) | Under training | +| Log | [wandb](https://api.wandb.ai/links/linbin/p6n3evym) | [wandb](https://api.wandb.ai/links/linbin/t2g53sew) | [wandb](https://api.wandb.ai/links/linbin/uomr0xzb) | Under training | +| Training Data | ~40k videos | ~40k videos | ~40k videos | ~40k videos | + +## Next Release Preview +### CausalVideoVAE +Currently, the released version of CausalVideoVAE (v1.0.0) has two main drawbacks: **motion blurring** and **gridding effect**. We have made a series of improvements to CausalVideoVAE to reduce its inference cost and enhance its performance. We are currently referring to this enhanced version as the "preview version," which will be released in the next update. Preview reconstruction is as follows: + +**1 min Video Reconstruction with 720×1280**. Since github can't put too big video, we put it here: [origin video](https://streamable.com/u4onbb), [reconstruction video](https://streamable.com/qt8ncc). + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/cdcfa9a3-4de0-42d4-94c0-0669710e407b + +We randomly selected 100 samples from the validation set of Kinetics-400 for evaluation, and the results are presented in the following table: + +| | SSIM↑ | LPIPS↓ | PSNR↑ | FLOLPIPS↓ | +|---|---|---|---|---| +| v1.0.0 | 0.829 | 0.106 | 27.171 | 0.119 | +| Preview | 0.877 | 0.064 | 29.695 | 0.070 | + +#### Motion Blurring + +| **v1.0.0** | **Preview** | +| --- | --- | +| ![6862cae0-b1b6-48d1-bd11-84348cf42b42](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/f815636f-fb38-4891-918b-50b1f9aa086d) | ![9189da06-ef2c-42e6-ad34-bd702a6f538e](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/1e413f50-a785-485a-9851-a1449f952f1c) | + +#### Gridding effect + +| **v1.0.0** | **Preview** | +| --- | --- | +| ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/7fec5bed-3c83-4ee9-baef-4a3dacafc658) | ![img](https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/88202804/4f41b432-a3ef-484e-a492-8afd8a691bf7) | + +### Data Construction + +**Data source**. As mentioned earlier, over 60% of our dataset consists of landscape videos. This implies that our ability to generate videos in other domains is limited. However, most of the current large-scale open-source datasets are primarily obtained through web scraping from platforms like YouTube. While these datasets provide a vast quantity of videos, we have concerns about the quality of the videos themselves. Therefore, we will continue to collect high-quality datasets and also welcome recommendations from the open-source community. We are launching an Open-Sora-Dataset project, check out the details at [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset) + +**Caption Generation Pipeline**. As the video duration increases, we need to consider more efficient methods for video caption generation instead of relying solely on large multimodal image models. We are currently developing a new video caption generation pipeline that provides robust support for long videos. We are excited to share more details with you in the near future. Stay tuned! + +### Training Diffusion Model +Although v1.0.0 has shown promising results, we acknowledge that we still have a ways to go to reach the level of Sora. In our upcoming work, we will primarily focus on three aspects: + +1. **Training support for dynamic resolution and duration**: We aim to develop techniques that enable training models with varying resolutions and durations, allowing for more flexible and adaptable training processes. + +2. **Support for longer video generation**: We will explore methods to extend the generation capabilities of our models, enabling them to produce longer videos beyond the current limitations. + +3. **Enhanced conditional control**: We seek to enhance the conditional control capabilities of our models, providing users with more options and control over the generated videos. + +Furthermore, through careful observation of the generated videos, we have noticed the presence of some non-physiological speckles or abnormal flow. This can be attributed to the limited performance of CausalVideoVAE, as mentioned earlier. In future experiments, we plan to retrain a diffusion model using a more powerful version of CausalVideoVAE to address these issues. diff --git a/docs/Train_And_Eval_CausalVideoVAE.md b/docs/Train_And_Eval_CausalVideoVAE.md new file mode 100644 index 000000000..691f159f8 --- /dev/null +++ b/docs/Train_And_Eval_CausalVideoVAE.md @@ -0,0 +1,158 @@ +# Training + +To execute in the terminal: `bash scripts/causalvae/train.sh` + +> When using GAN loss for training, two backward propagations are required. However, when [custom optimizers](https://lightning.ai/docs/pytorch/stable/model/manual_optimization.html#use-multiple-optimizers-like-gans) are implemented in PyTorch Lightning, it can lead to the training step count being doubled, meaning each training loop effectively results in two steps. This issue can make it counterintuitive when setting the training step count and the starting step count for the GAN loss. + +## Code Structure + +CausalVideoVAE is located in the directory `opensora/models/ae/videobase`. The directory structure is as follows: + +``` +. +├── causal_vae +├── causal_vqvae +├── configuration_videobase.py +├── dataset_videobase.py +├── __init__.py +├── losses +├── modeling_videobase.py +├── modules +├── __pycache__ +├── trainer_videobase.py +├── utils +└── vqvae +``` + +The `casual_vae` directory defines the overall structure of the CausalVideoVAE model, and the `modules` directory contains some of the required modules for the model, including **CausalConv3D**, **ResnetBlock3D**, **Attention**, etc. The `losses` directory includes **GAN loss**, **Perception loss**, and other content. + +## Configuration + +Model training requires two key files: one is the `config.json` file, which configures the model structure, loss function, learning rate, etc. The other is the `train.sh` file, which configures the dataset, training steps, precision, etc. + +### Model Configuration File + +Taking the release version model configuration file `release.json` as an example: + +```json +{ + "_class_name": "CausalVAEModel", + "_diffusers_version": "0.27.2", + "attn_resolutions": [], + "decoder_attention": "AttnBlock3D", + "decoder_conv_in": "CausalConv3d", + "decoder_conv_out": "CausalConv3d", + "decoder_mid_resnet": "ResnetBlock3D", + "decoder_resnet_blocks": [ + "ResnetBlock3D", + "ResnetBlock3D", + "ResnetBlock3D", + "ResnetBlock3D" + ], + "decoder_spatial_upsample": [ + "", + "SpatialUpsample2x", + "SpatialUpsample2x", + "SpatialUpsample2x" + ], + "decoder_temporal_upsample": [ + "", + "", + "TimeUpsample2x", + "TimeUpsample2x" + ], + "double_z": true, + "dropout": 0.0, + "embed_dim": 4, + "encoder_attention": "AttnBlock3D", + "encoder_conv_in": "CausalConv3d", + "encoder_conv_out": "CausalConv3d", + "encoder_mid_resnet": "ResnetBlock3D", + "encoder_resnet_blocks": [ + "ResnetBlock3D", + "ResnetBlock3D", + "ResnetBlock3D", + "ResnetBlock3D" + ], + "encoder_spatial_downsample": [ + "SpatialDownsample2x", + "SpatialDownsample2x", + "SpatialDownsample2x", + "" + ], + "encoder_temporal_downsample": [ + "TimeDownsample2x", + "TimeDownsample2x", + "", + "" + ], + "hidden_size": 128, + "hidden_size_mult": [ + 1, + 2, + 4, + 4 + ], + "loss_params": { + "disc_start": 2001, + "disc_weight": 0.5, + "kl_weight": 1e-06, + "logvar_init": 0.0 + }, + "loss_type": "opensora.models.ae.videobase.losses.LPIPSWithDiscriminator", + "lr": 1e-05, + "num_res_blocks": 2, + "q_conv": "CausalConv3d", + "resolution": 256, + "z_channels": 4 +} +``` + +It configures the modules used in different layers of the encoder and decoder, as well as the loss. By changing the model configuration file, it is easy to train different model structures. + +### Training Script + +The following is a description of the parameters for the `train_causalvae.py`: + +| Parameter | Default Value | Description | +|-----------------------------|-----------------|--------------------------------------------------------| +| `--exp_name` | "causalvae" | The name of the experiment, used for the folder where results are saved. | +| `--batch_size` | 1 | The number of samples per training iteration. | +| `--precision` | "bf16" | The numerical precision type used for training. | +| `--max_steps` | 100000 | The maximum number of steps for the training process. | +| `--save_steps` | 2000 | The interval at which to save the model during training. | +| `--output_dir` | "results/causalvae" | The directory where training results are saved. | +| `--video_path` | "/remote-home1/dataset/data_split_tt" | The path where the video data is stored. | +| `--video_num_frames` | 17 | The number of frames per video. | +| `--sample_rate` | 1 | The sampling rate, indicating the number of video frames per second. | +| `--dynamic_sample` | False | Whether to use dynamic sampling. | +| `--model_config` | "scripts/causalvae/288.yaml" | The path to the model configuration file. | +| `--n_nodes` | 1 | The number of nodes used for training. | +| `--devices` | 8 | The number of devices used for training. | +| `--resolution` | 256 | The resolution of the videos. | +| `--num_workers` | 8 | The number of subprocesses used for data handling. | +| `--resume_from_checkpoint` | None | Resume training from a specified checkpoint. | +| `--load_from_checkpoint` | None | Load the model from a specified checkpoint. | + +Please ensure that the values provided for these parameters are appropriate for your training setup. + +# Evaluation + + +1. Video Generation: +The script `scripts/causalvae/gen_video.sh` in the repository is utilized for generating videos. For the parameters, please refer to the script itself. + +2. Video Evaluation: +After video generation, You can evaluate the generated videos using the `scripts/causalvae/eval.sh` script. This evaluation script supports common metrics, including lpips, flolpips, ssim, psnr, and more. + +> Please note that you must generate the videos before executing the eval script. Additionally, it is essential to ensure that the video parameters used when generating the videos are consistent with those used during the evaluation. + +# How to Import a Trained Model + +Our model class inherits from the configuration and model management classes of huggingface, supporting the download and loading of models from huggingface. It can also import models trained with pytorch lightning. + +``` +model = CausalVAEModel.from_pretrained(args.ckpt) +model = model.to(device) +``` + diff --git a/docs/VQVAE.md b/docs/VQVAE.md new file mode 100644 index 000000000..a1af36364 --- /dev/null +++ b/docs/VQVAE.md @@ -0,0 +1,57 @@ +# VQVAE Documentation + +# Introduction + +Vector Quantized Variational AutoEncoders (VQ-VAE) is a type of autoencoder that uses a discrete latent representation. It is particularly useful for tasks that require discrete latent variables, such as text-to-speech and video generation. + +# Usage + +## Initialization + +To initialize a VQVAE model, you can use the `VideoGPTVQVAE` class. This class is a part of the `opensora.models.ae` module. + +```python +from opensora.models.ae import VideoGPTVQVAE + +vqvae = VideoGPTVQVAE() +``` + +### Training + +To train the VQVAE model, you can use the `train_videogpt.sh` script. This script will train the model using the parameters specified in the script. + +```bash +bash scripts/videogpt/train_videogpt.sh +``` + +### Loading Pretrained Models + +You can load a pretrained model using the `download_and_load_model` method. This method will download the checkpoint file and load the model. + +```python +vqvae = VideoGPTVQVAE.download_and_load_model("bair_stride4x2x2") +``` + +Alternatively, you can load a model from a checkpoint using the `load_from_checkpoint` method. + +```python +vqvae = VQVAEModel.load_from_checkpoint("results/VQVAE/checkpoint-1000") +``` + +### Encoding and Decoding + +You can encode a video using the `encode` method. This method will return the encodings and embeddings of the video. + +```python +encodings, embeddings = vqvae.encode(x_vae, include_embeddings=True) +``` + +You can reconstruct a video from its encodings using the decode method. + +```python +video_recon = vqvae.decode(encodings) +``` + +## Testing + +You can test the VQVAE model by reconstructing a video. The `examples/rec_video.py` script provides an example of how to do this. \ No newline at end of file From 304538c47592652d0d457428f4253600b7166ac4 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Mon, 15 Jul 2024 11:22:09 +0800 Subject: [PATCH 085/134] video ip --- .gitignore | 3 +- opensora/dataset/__init__.py | 44 +- opensora/dataset/t2v_datasets.py | 32 +- opensora/dataset/transform.py | 35 + opensora/dataset/videoip_datasets.py | 155 +- .../diffusion/opensora/modeling_for_vip.py | 315 ++++ .../diffusion/opensora/modeling_opensora.py | 58 + opensora/models/diffusion/opensora/rope.py | 2 +- opensora/models/diffusion/opensora/videoip.py | 316 ++-- opensora/models/text_encoder/__init__.py | 4 +- opensora/sample/pipeline_for_vip.py | 11 +- opensora/sample/pipeline_inpaint.py | 15 +- opensora/sample/sample_inpaint.py | 114 +- opensora/train/train_inpaint.py | 2 +- opensora/train/train_videoip.py | 419 +++--- opensora/train/train_videoip_bak.py | 1294 +++++++++++++++++ opensora/utils/dataset_utils.py | 1 + scripts/accelerate_configs/hostfile | 4 +- .../multi_node_example.yaml | 4 +- scripts/text_condition/gpu/sample_inpaint.sh | 16 +- ...80p.sh => train_inpaint_video3d_nx480p.sh} | 20 +- .../gpu/train_video_ip_video3d.sh | 75 + scripts/train_data/image_data.txt | 4 +- .../train_data/video_data_aesmovie_sucai.txt | 6 - validation_dir/prompt.txt | 24 +- 25 files changed, 2497 insertions(+), 476 deletions(-) create mode 100644 opensora/models/diffusion/opensora/modeling_for_vip.py create mode 100644 opensora/train/train_videoip_bak.py rename scripts/text_condition/gpu/{train_inpaint_video3d_125x480p.sh => train_inpaint_video3d_nx480p.sh} (76%) create mode 100644 scripts/text_condition/gpu/train_video_ip_video3d.sh diff --git a/.gitignore b/.gitignore index 52611dcc9..8fff9a3b8 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,5 @@ check.py bucket.py whileinf.py validation_dir/ -inpaint*/ +runs/ +samples/ \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index 1a40005e0..20f07c9b0 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -14,8 +14,8 @@ # from .t2v_datasets import T2V_dataset from .t2v_datasets import T2V_dataset from .inpaint_datasets import Inpaint_dataset -from .videoip_datasets_bak import VideoIP_dataset -from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo +from .videoip_datasets import VideoIP_dataset +from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo, NormalizeVideo, ToTensorDino ae_norm = { @@ -119,39 +119,41 @@ def getdataset(args): # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) - # tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, transform_topcrop=transform_topcrop) elif args.dataset == 'vip': - resize_topcrop = [CenterCropResizeVideo((args.max_height, args.max_width), top_crop=True), ] - # if args.multi_scale: - # resize = [ - # LongSideResizeVideo(args.max_image_size, skip_low_resolution=True), - # SpatialStrideCropVideo(args.stride) - # ] - # else: - resize = [CenterCropResizeVideo((args.max_height, args.max_width)), ] + resize_topcrop = CenterCropResizeVideo((args.max_height, args.max_width), top_crop=True) + resize = CenterCropResizeVideo((args.max_height, args.max_width)) transform = transforms.Compose([ ToTensorVideo(), - *resize, # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) transform_topcrop = transforms.Compose([ ToTensorVideo(), - *resize_topcrop, # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription norm_fun ]) - transform_topcrop = transforms.Compose([ - ToTensorVideo(), - *resize_topcrop, - # RandomHorizontalFlipVideo(p=0.5), # in case their caption have position decription - norm_fun + + if args.max_width / args.max_height == 4 / 3: + image_processor_center_crop = transforms.CenterCrop((518, 686)) + elif args.max_width / args.max_height == 16 / 9: + image_processor_center_crop = transforms.CenterCrop((518, 910)) + else: + image_processor_center_crop = transforms.CenterCrop((518, 518)) + + # dino image processor + image_processor = transforms.Compose([ + transforms.Resize(518, interpolation=transforms.InterpolationMode.BICUBIC, antialias=True, max_size=None), + image_processor_center_crop, # + ToTensorDino(), + transforms.Normalize((0.4850, 0.4560, 0.4060), (0.2290, 0.2240, 0.2250)), ]) # tokenizer = AutoTokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) - return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, - transform_topcrop=transform_topcrop) + + return VideoIP_dataset(args, transform=transform, resize_transform=resize, resize_transform_topcrop=resize_topcrop, temporal_sample=temporal_sample, tokenizer=tokenizer, + transform_topcrop=transform_topcrop, image_processor=image_processor) raise NotImplementedError(args.dataset) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 0f09ef46a..b68f30857 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -133,6 +133,9 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro self.model_max_length = args.model_max_length self.cfg = args.cfg self.speed_factor = args.speed_factor + self.max_height = args.max_height + self.max_width = args.max_width + self.drop_short_ratio = args.drop_short_ratio assert self.speed_factor >= 1 self.v_decoder = DecordInit() @@ -314,26 +317,28 @@ def define_frame_index(self, vid_cap_list): if not filter_resolution(resolution['height'], resolution['width']): cnt_resolution_mismatch += 1 continue + if self.max_height > resolution['height'] or self.max_width > resolution['width']: + cnt_resolution_mismatch += 1 + continue if fps is not None and duration is not None: # import ipdb;ipdb.set_trace() i['num_frames'] = int(fps * duration) # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. - if i['num_frames'] > 6.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) + if i['num_frames'] > 5.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) cnt_too_long += 1 continue # if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage # cnt_too_short += 1 - # continue + # continue # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) frame_interval = fps / self.train_fps - start_frame_idx = 8 if 'movie/' in i['path'] else 0 # special video + start_frame_idx = 8 if '/storage/dataset/movie' in i['path'] else 0 # special video frame_indices = np.arange(start_frame_idx, i['num_frames'], frame_interval).astype(int) frame_indices = frame_indices[frame_indices < i['num_frames']] # comment out it to enable dynamic frames training - drop_short_video_prob = 1.0 - if len(frame_indices) < self.num_frames and random.uniform(0, 1) <= drop_short_video_prob: # to drop short videos + if len(frame_indices) < self.num_frames and random.random() < self.drop_short_ratio: cnt_too_short += 1 continue @@ -349,7 +354,7 @@ def define_frame_index(self, vid_cap_list): continue frame_indices = frame_indices[:end_frame_idx] - if 'movie/' in i['path']: + if '/storage/dataset/movie' in i['path']: cnt_movie += 1 i['sample_frame_index'] = frame_indices.tolist() new_vid_cap_list.append(i) @@ -369,15 +374,14 @@ def decord_read(self, path): fps = decord_vr.get_avg_fps() if decord_vr.get_avg_fps() > 0 else 30.0 # import ipdb;ipdb.set_trace() # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) - frame_interval = 1.0 if abs(fps - self.train_fps) < 1e-1 else fps / self.train_fps - start_frame_idx = 8 if 'movie/' in path else 0 # special video + frame_interval = 1.0 if abs(fps - self.train_fps) < 0.1 else fps / self.train_fps + start_frame_idx = 8 if '/storage/dataset/movie' in path else 0 # special video frame_indices = np.arange(start_frame_idx, total_frames, frame_interval).astype(int) frame_indices = frame_indices[frame_indices < total_frames] #import ipdb;ipdb.set_trace() # speed up max_speed_factor = len(frame_indices) / self.num_frames - speed_factor = 1.0 - if self.speed_factor > 1 and max_speed_factor > 1 and not ('MagicTime_Data/' in path): + if self.speed_factor > 1 and max_speed_factor > 1 and not ('/storage/dataset/MagicTime_Data' in path): speed_factor = random.uniform(1.0, min(self.speed_factor, max_speed_factor)) target_frame_count = int(len(frame_indices) / speed_factor) speed_frame_idx = np.linspace(0, len(frame_indices) - 1, target_frame_count, dtype=int) @@ -392,10 +396,10 @@ def decord_read(self, path): # to find a suitable end_frame_idx, to ensure we do not need pad video end_frame_idx = find_closest_y(len(frame_indices), vae_stride_t=4, model_ds_t=4) if end_frame_idx == -1: # too short that can not be encoded exactly by videovae - raise IndexError(f'video ({path}) has {total_frames} frames, speed_factor: {speed_factor}, but need to sample {len(frame_indices)} frames ({frame_indices})') + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') frame_indices = frame_indices[:end_frame_idx] - if len(frame_indices) < self.num_frames: - raise IndexError(f'video ({path}) has {total_frames} frames, speed_factor: {speed_factor}, but need to sample {len(frame_indices)} frames ({frame_indices})') + if len(frame_indices) < self.num_frames and self.drop_short_ratio >= 1: + raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') video_data = decord_vr.get_batch(frame_indices).asnumpy() video_data = torch.from_numpy(video_data) video_data = video_data.permute(0, 3, 1, 2) # (T, H, W, C) -> (T C H W) @@ -446,4 +450,4 @@ def get_vid_cap_list(self): vid_cap_lists = vid_cap_lists_final npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") - return vid_cap_lists + return vid_cap_lists \ No newline at end of file diff --git a/opensora/dataset/transform.py b/opensora/dataset/transform.py index 96a1b696d..3295de367 100644 --- a/opensora/dataset/transform.py +++ b/opensora/dataset/transform.py @@ -164,6 +164,18 @@ def to_tensor(clip): return clip.float() / 255.0 +def to_tensor_after_resize(clip): + """ + Convert resized tensor to [0, 1] + Args: + clip (torch.tensor, dtype=torch.float): Size is (T, C, H, W) + Return: + clip (torch.tensor, dtype=torch.float): Size is (T, C, H, W), but in [0, 1] + """ + _is_tensor_video_clip(clip) + # return clip.float().permute(3, 0, 1, 2) / 255.0 + return clip.float() / 255.0 + def normalize(clip, mean, std, inplace=False): """ Args: @@ -471,6 +483,29 @@ def __call__(self, clip): def __repr__(self) -> str: return self.__class__.__name__ + + +class ToTensorDino: + """ + Convert tensor data type from uint8 to float, divide value by 255.0 and + permute the dimensions of clip tensor + """ + + def __init__(self): + pass + + def __call__(self, clip): + """ + Args: + clip (torch.tensor, dtype=torch.float): Size is (T, C, H, W) + Return: + clip (torch.tensor, dtype=torch.float): Size is (T, C, H, W), but in [0, 1] + """ + return to_tensor_after_resize(clip) + + def __repr__(self) -> str: + return self.__class__.__name__ + class RandomHorizontalFlipVideo: diff --git a/opensora/dataset/videoip_datasets.py b/opensora/dataset/videoip_datasets.py index ab53d6611..aebf89f90 100644 --- a/opensora/dataset/videoip_datasets.py +++ b/opensora/dataset/videoip_datasets.py @@ -38,14 +38,165 @@ dataset_prog = DataSetProg() class VideoIP_dataset(T2V_dataset): - def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): + def __init__(self, args, transform, resize_transform, temporal_sample, tokenizer, transform_topcrop, resize_transform_topcrop, image_processor): super().__init__(args, transform, temporal_sample, tokenizer, transform_topcrop) + self.resize_transform = resize_transform + self.resize_transform_topcrop = resize_transform_topcrop + self.image_processor = image_processor + if self.num_frames != 1: # inpaint # The proportion of executing the i2v task. self.i2v_ratio = args.i2v_ratio self.transition_ratio = args.transition_ratio self.clear_video_ratio = args.clear_video_ratio - self.default_text_ratio = args.default_text_ratio assert self.i2v_ratio + self.transition_ratio + self.clear_video_ratio < 1, 'The sum of i2v_ratio, transition_ratio and clear video ratio should be less than 1.' + + self.default_text_ratio = args.default_text_ratio + self.default_text = f"The {'video' if self.num_frames != 1 else 'image'} showcases a scene with coherent and clear visuals." + + def get_video(self, idx): + + video_path = dataset_prog.vid_cap_list[idx]['path'] + assert os.path.exists(video_path), f"file {video_path} do not exist!" + # frame_indice = self.vid_cap_list[idx]['sample_frame_index'] + video = self.decord_read(video_path) + + h, w = video.shape[-2:] + assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only videos with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But video ({video_path}) found ratio is {round(h / w, 2)} with the shape of {video.shape}' + t = video.shape[0] + + # resize + video = self.resize_transform(video) + + inpaint_cond_data = self.get_mask_masked_video(video) + masked_video = inpaint_cond_data['masked_video'] + + clip_video = self.image_processor(masked_video) # T C H W + + video = self.transform(video) # T C H W -> T C H W + video = video.transpose(0, 1) # T C H W -> C T H W + + text = dataset_prog.vid_cap_list[idx]['cap'] + if text is None: + text = self.default_text + if not isinstance(text, list): + text = [text] + text = [random.choice(text)] + + drop_results = self.drop(text, clip_video) + text = drop_results['text'] + clip_video = drop_results['clip_image'] + + text = text_preprocessing(text, support_Chinese=self.support_Chinese) + text_tokens_and_mask = self.tokenizer( + text, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids = text_tokens_and_mask['input_ids'] + cond_mask = text_tokens_and_mask['attention_mask'] + return dict(video=video, input_ids=input_ids, cond_mask=cond_mask, clip_video=clip_video) + + def get_image_from_video(self, video_data): + select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) + assert self.num_frames >= self.use_image_num + image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] + clip_image = [video_data['clip_video'][i:i + 1] for i in select_image_idx] # num_img [1, c, h, w] + input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l + cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) + + def get_image(self, idx): + idx = idx % len(dataset_prog.img_cap_list) # out of range + image_data = dataset_prog.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] + + image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] + image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + + # for i in image: + # assert not torch.any(torch.isnan(i)), 'before transform0' + image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] + + image = [self.resize_transform_topcrop(i) if 'human_images' in j['path'] else self.resize_transform(i) for i, j in zip(image, image_data)] # num_img [1 C H W] -> num_img [1 C H W] + + clip_image_list = [self.image_processor(i) for i in image] # num_img [1 C H W] -> num_img [1 C H W] + # for i in image: + # assert not torch.any(torch.isnan(i)), 'before transform1' + # for i in image: + # h, w = i.shape[-2:] + # assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only image with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {i.shape}' + image = [self.transform_topcrop(i) if 'human_images' in j['path'] else self.transform(i) for i, j in zip(image, image_data)] # num_img [1 C H W] -> num_img [1 C H W] + + # for i in image: + # assert not torch.any(torch.isnan(i)), 'after transform' + # image = [torch.rand(1, 3, 480, 640) for i in image_data] + image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] + + caps = [i['cap'] if isinstance(i['cap'], list) else [i['cap']] for i in image_data] + caps = [[random.choice(i)] if i is not None or len(i) > 0 else [self.default_text] for i in caps] + text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] + + input_ids, cond_mask, clip_image = [], [], [] + for t, clip_i in zip(text, clip_image_list): + drop_results = self.drop(t, clip_i) + t = drop_results['text'] + clip_i = drop_results['clip_image'] + text_tokens_and_mask = self.tokenizer( + t, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids.append(text_tokens_and_mask['input_ids']) + cond_mask.append(text_tokens_and_mask['attention_mask']) + clip_image.append(clip_i) + input_ids = torch.cat(input_ids) # self.use_image_num, l + cond_mask = torch.cat(cond_mask) # self.use_image_num, l + clip_image = torch.cat(clip_image) # self.use_image_num, C, H, W + return dict(image=image, input_ids=input_ids, cond_mask=cond_mask, clip_image=clip_image) + + def get_mask_masked_video(self, video): + # video shape (T, C, H, W) + mask = torch.zeros_like(video) + + rand_num = random.random() + # To ensure the effectiveness of the i2v task, it is necessary to guarantee that a certain proportion of samples undergo i2v. + if rand_num < self.i2v_ratio: + mask = 1 - mask + mask[0] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio: + mask = 1 - mask + mask[0] = 0 + mask[-1] = 0 + elif rand_num < self.i2v_ratio + self.transition_ratio + self.clear_video_ratio: + pass + else: + idx_to_select = random.randint(1, self.num_frames - 1) + selected_indices = random.sample(range(1, self.num_frames), idx_to_select) + mask[selected_indices] = 1 + + masked_video = video * (mask < 0.5) + return dict(mask=mask, masked_video=masked_video) + + def drop(self, text, clip_image): + rand_num = random.random() + rand_num_text = random.random() + + if rand_num < self.cfg: + text = self.default_text if rand_num_text < self.default_text_ratio else '' + elif rand_num < self.cfg * 2: + clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) + elif rand_num < self.cfg * 3: + text = self.default_text if rand_num_text < self.default_text_ratio else '' + clip_image = torch.zeros_like(clip_image, device=clip_image.device, dtype=clip_image.dtype) + + return dict(text=text, clip_image=clip_image) diff --git a/opensora/models/diffusion/opensora/modeling_for_vip.py b/opensora/models/diffusion/opensora/modeling_for_vip.py new file mode 100644 index 000000000..9ff58766d --- /dev/null +++ b/opensora/models/diffusion/opensora/modeling_for_vip.py @@ -0,0 +1,315 @@ +import torch +import torch.nn as nn +import torch.nn.functional as F + +import os +import json +from typing import Optional, Dict, Any +from einops import rearrange, repeat + +from diffusers.utils import is_torch_version, deprecate + +try: + import torch_npu + from opensora.npu_config import npu_config + from opensora.acceleration.parallel_states import get_sequence_parallel_state, hccl_info +except: + torch_npu = None + npu_config = None + +from .modeling_opensora import OpenSoraT2V +from .modeling_opensora import Transformer2DModelOutput + +from diffusers.configuration_utils import register_to_config + + +def hook_forward_fn(module, input, output): + print("It's forward: ") + print(f"module: {module}") + print("="*20) + +def hook_backward_fn(module, grad_input, grad_output): + print("It's backward: ") + print(f"module: {module}") + print(f"grad_input is None?: {grad_input is None}") + print(grad_input) + print(f"grad_output is None?: {grad_output is None}") + print(grad_output) + print("="*20) + + +def hacked_model(model): + model._operate_on_patched_inputs = operate_on_patched_inputs.__get__(model, OpenSoraT2V) + model.forward = hacked_forward_for_vip.__get__(model, OpenSoraT2V) + + +def operate_on_patched_inputs(self, hidden_states, encoder_hidden_states, vip_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num): + # batch_size = hidden_states.shape[0] + hidden_states_vid, hidden_states_img = self.pos_embed(hidden_states.to(self.dtype), frame) + timestep_vid, timestep_img = None, None + embedded_timestep_vid, embedded_timestep_img = None, None + encoder_hidden_states_vid, encoder_hidden_states_img = None, None + + if self.adaln_single is not None: + if self.use_additional_conditions and added_cond_kwargs is None: + raise ValueError( + "`added_cond_kwargs` cannot be None when using additional conditions for `adaln_single`." + ) + timestep, embedded_timestep = self.adaln_single( + timestep, added_cond_kwargs, batch_size=batch_size, hidden_dtype=self.dtype + ) # b 6d, b d + if hidden_states_vid is None: + timestep_img = timestep + embedded_timestep_img = embedded_timestep + else: + timestep_vid = timestep + embedded_timestep_vid = embedded_timestep + if hidden_states_img is not None: + timestep_img = repeat(timestep, 'b d -> (b i) d', i=use_image_num).contiguous() + embedded_timestep_img = repeat(embedded_timestep, 'b d -> (b i) d', i=use_image_num).contiguous() + + if self.caption_projection is not None: + encoder_hidden_states = self.caption_projection(encoder_hidden_states) # b, 1+use_image_num, l, d or b, 1, l, d + + # NOTE add vip hidden states + encoder_hidden_states = torch.cat([encoder_hidden_states, vip_hidden_states], dim=2) # # B 1+image_num N D -> B 1+image_num N+num_vip_tokens D + + if hidden_states_vid is None: + encoder_hidden_states_img = rearrange(encoder_hidden_states, 'b 1 l d -> (b 1) l d') + else: + encoder_hidden_states_vid = rearrange(encoder_hidden_states[:, :1], 'b 1 l d -> (b 1) l d') + if hidden_states_img is not None: + encoder_hidden_states_img = rearrange(encoder_hidden_states[:, 1:], 'b i l d -> (b i) l d') + + + return hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img + + + +def hacked_forward_for_vip( + self, + hidden_states: torch.Tensor, + timestep: Optional[torch.LongTensor] = None, + encoder_hidden_states: Optional[torch.Tensor] = None, + vip_hidden_states: Optional[torch.Tensor] = None, + added_cond_kwargs: Dict[str, torch.Tensor] = None, + class_labels: Optional[torch.LongTensor] = None, + cross_attention_kwargs: Dict[str, Any] = None, + attention_mask: Optional[torch.Tensor] = None, + encoder_attention_mask: Optional[torch.Tensor] = None, + vip_attention_mask : Optional[torch.Tensor] = None, + use_image_num: Optional[int] = 0, + return_dict: bool = True, +): + + + batch_size, c, frame, h, w = hidden_states.shape + frame = frame - use_image_num # 21-4=17 + if cross_attention_kwargs is not None: + if cross_attention_kwargs.get("scale", None) is not None: + print.warning("Passing `scale` to `cross_attention_kwargs` is deprecated. `scale` will be ignored.") + # ensure attention_mask is a bias, and give it a singleton query_tokens dimension. + # we may have done this conversion already, e.g. if we came here via UNet2DConditionModel#forward. + # we can tell by counting dims; if ndim == 2: it's a mask rather than a bias. + # expects mask of shape: + # [batch, key_tokens] + # adds singleton query_tokens dimension: + # [batch, 1, key_tokens] + # this helps to broadcast it as a bias over attention scores, which will be in one of the following shapes: + # [batch, heads, query_tokens, key_tokens] (e.g. torch sdp attn) + # [batch * heads, query_tokens, key_tokens] (e.g. xformers or classic attn) + attention_mask_vid, attention_mask_img = None, None + if attention_mask is not None and attention_mask.ndim == 4: + # assume that mask is expressed as: + # (1 = keep, 0 = discard) + # convert mask into a bias that can be added to attention scores: + # (keep = +0, discard = -10000.0) + # b, frame+use_image_num, h, w -> a video with images + # b, 1, h, w -> only images + attention_mask = attention_mask.to(self.dtype) + if npu_config is not None and get_sequence_parallel_state(): + attention_mask_vid = attention_mask[:, :frame * hccl_info.world_size] # b, frame, h, w + attention_mask_img = attention_mask[:, frame * hccl_info.world_size:] # b, use_image_num, h, w + else: + attention_mask_vid = attention_mask[:, :frame] # b, frame, h, w + attention_mask_img = attention_mask[:, frame:] # b, use_image_num, h, w + + if attention_mask_vid.numel() > 0: + attention_mask_vid_first_frame = attention_mask_vid[:, :1].repeat(1, self.patch_size_t-1, 1, 1) + attention_mask_vid = torch.cat([attention_mask_vid_first_frame, attention_mask_vid], dim=1) + attention_mask_vid = attention_mask_vid.unsqueeze(1) # b 1 t h w + attention_mask_vid = F.max_pool3d(attention_mask_vid, kernel_size=(self.patch_size_t, self.patch_size, self.patch_size), + stride=(self.patch_size_t, self.patch_size, self.patch_size)) + attention_mask_vid = rearrange(attention_mask_vid, 'b 1 t h w -> (b 1) 1 (t h w)') + if attention_mask_img.numel() > 0: + attention_mask_img = F.max_pool2d(attention_mask_img, kernel_size=(self.patch_size, self.patch_size), stride=(self.patch_size, self.patch_size)) + attention_mask_img = rearrange(attention_mask_img, 'b i h w -> (b i) 1 (h w)') + + attention_mask_vid = (1 - attention_mask_vid.bool().to(self.dtype)) * -10000.0 if attention_mask_vid.numel() > 0 else None + attention_mask_img = (1 - attention_mask_img.bool().to(self.dtype)) * -10000.0 if attention_mask_img.numel() > 0 else None + + if frame == 1 and use_image_num == 0: + attention_mask_img = attention_mask_vid + attention_mask_vid = None + # convert encoder_attention_mask to a bias the same way we do for attention_mask + # import ipdb;ipdb.set_trace() + if encoder_attention_mask is not None and encoder_attention_mask.ndim == 3: + + # b, 1+use_image_num, l -> a video with images + # b, 1, l -> only images + # NOTE add vip attention mask + encoder_attention_mask = torch.cat([encoder_attention_mask, vip_attention_mask], dim=-1) # B 1+image_num N -> B 1+image_num N+num_vip_tokens + + encoder_attention_mask = (1 - encoder_attention_mask.to(self.dtype)) * -10000.0 + in_t = encoder_attention_mask.shape[1] + encoder_attention_mask_vid = encoder_attention_mask[:, :in_t-use_image_num] # b, 1, l + encoder_attention_mask_vid = rearrange(encoder_attention_mask_vid, 'b 1 l -> (b 1) 1 l') if encoder_attention_mask_vid.numel() > 0 else None + + encoder_attention_mask_img = encoder_attention_mask[:, in_t-use_image_num:] # b, use_image_num, l + encoder_attention_mask_img = rearrange(encoder_attention_mask_img, 'b i l -> (b i) 1 l') if encoder_attention_mask_img.numel() > 0 else None + + if frame == 1 and use_image_num == 0: + encoder_attention_mask_img = encoder_attention_mask_vid + encoder_attention_mask_vid = None + + if npu_config is not None and attention_mask_vid is not None: + attention_mask_vid = npu_config.get_attention_mask(attention_mask_vid, attention_mask_vid.shape[-1]) + encoder_attention_mask_vid = npu_config.get_attention_mask(encoder_attention_mask_vid, + attention_mask_vid.shape[-2]) + if npu_config is not None and attention_mask_img is not None: + attention_mask_img = npu_config.get_attention_mask(attention_mask_img, attention_mask_img.shape[-1]) + encoder_attention_mask_img = npu_config.get_attention_mask(encoder_attention_mask_img, + attention_mask_img.shape[-2]) + + + # 1. Input + frame = ((frame - 1) // self.patch_size_t + 1) if frame % 2 == 1 else frame // self.patch_size_t # patchfy + height, width = hidden_states.shape[-2] // self.patch_size, hidden_states.shape[-1] // self.patch_size + + added_cond_kwargs = {"resolution": None, "aspect_ratio": None} + hidden_states_vid, hidden_states_img, encoder_hidden_states_vid, encoder_hidden_states_img, \ + timestep_vid, timestep_img, embedded_timestep_vid, embedded_timestep_img = self._operate_on_patched_inputs( + hidden_states, encoder_hidden_states, vip_hidden_states, timestep, added_cond_kwargs, batch_size, frame, use_image_num + ) + # 2. Blocks + # import ipdb;ipdb.set_trace() + if npu_config is not None and get_sequence_parallel_state(): + if hidden_states_vid is not None: + hidden_states_vid = rearrange(hidden_states_vid, 'b s h -> s b h', b=batch_size).contiguous() + encoder_hidden_states_vid = rearrange(encoder_hidden_states_vid, 'b s h -> s b h', + b=batch_size).contiguous() + timestep_vid = timestep_vid.view(batch_size, 6, -1).transpose(0, 1).contiguous() + for block in self.transformer_blocks: + if self.training and self.gradient_checkpointing: + + def create_custom_forward(module, return_dict=None): + def custom_forward(*inputs): + if return_dict is not None: + return module(*inputs, return_dict=return_dict) + else: + return module(*inputs) + + return custom_forward + + ckpt_kwargs: Dict[str, Any] = {"use_reentrant": False} if is_torch_version(">=", "1.11.0") else {} + # import ipdb;ipdb.set_trace() + if hidden_states_vid is not None: + hidden_states_vid = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + hidden_states_vid, + attention_mask_vid, + encoder_hidden_states_vid, + encoder_attention_mask_vid, + timestep_vid, + cross_attention_kwargs, + class_labels, + frame, + height, + width, + **ckpt_kwargs, + ) + # import ipdb;ipdb.set_trace() + if hidden_states_img is not None: + hidden_states_img = torch.utils.checkpoint.checkpoint( + create_custom_forward(block), + hidden_states_img, + attention_mask_img, + encoder_hidden_states_img, + encoder_attention_mask_img, + timestep_img, + cross_attention_kwargs, + class_labels, + 1, + height, + width, + **ckpt_kwargs, + ) + else: + if hidden_states_vid is not None: + hidden_states_vid = block( + hidden_states_vid, + attention_mask=attention_mask_vid, + encoder_hidden_states=encoder_hidden_states_vid, + encoder_attention_mask=encoder_attention_mask_vid, + timestep=timestep_vid, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + frame=frame, + height=height, + width=width, + ) + if hidden_states_img is not None: + hidden_states_img = block( + hidden_states_img, + attention_mask=attention_mask_img, + encoder_hidden_states=encoder_hidden_states_img, + encoder_attention_mask=encoder_attention_mask_img, + timestep=timestep_img, + cross_attention_kwargs=cross_attention_kwargs, + class_labels=class_labels, + frame=1, + height=height, + width=width, + ) + + if npu_config is not None and get_sequence_parallel_state(): + if hidden_states_vid is not None: + hidden_states_vid = rearrange(hidden_states_vid, 's b h -> b s h', b=batch_size).contiguous() + + # 3. Output + output_vid, output_img = None, None + if hidden_states_vid is not None: + output_vid = self._get_output_for_patched_inputs( + hidden_states=hidden_states_vid, + timestep=timestep_vid, + class_labels=class_labels, + embedded_timestep=embedded_timestep_vid, + num_frames=frame, + height=height, + width=width, + ) # b c t h w + if hidden_states_img is not None: + output_img = self._get_output_for_patched_inputs( + hidden_states=hidden_states_img, + timestep=timestep_img, + class_labels=class_labels, + embedded_timestep=embedded_timestep_img, + num_frames=1, + height=height, + width=width, + ) # b c 1 h w + if use_image_num != 0: + output_img = rearrange(output_img, '(b i) c 1 h w -> b c i h w', i=use_image_num) + + if output_vid is not None and output_img is not None: + output = torch.cat([output_vid, output_img], dim=2) + elif output_vid is not None: + output = output_vid + elif output_img is not None: + output = output_img + + if not return_dict: + return (output,) + + return Transformer2DModelOutput(sample=output) + diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 3caf27994..da5cad27f 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -270,6 +270,64 @@ def _init_patched_inputs(self, norm_type): in_features=self.caption_channels, hidden_size=self.inner_dim ) + @property + def attn_processors(self): + r""" + Returns: + `dict` of attention processors: A dictionary containing all attention processors used in the model with + indexed by its weight name. + """ + # set recursively + processors = {} + + def fn_recursive_add_processors(name: str, module: torch.nn.Module, processors): + if hasattr(module, "get_processor"): + processors[f"{name}.processor"] = module.get_processor(return_deprecated_lora=True) + + for sub_name, child in module.named_children(): + fn_recursive_add_processors(f"{name}.{sub_name}", child, processors) + + return processors + + for name, module in self.named_children(): + fn_recursive_add_processors(name, module, processors) + + return processors + + def set_attn_processor(self, processor): + r""" + Sets the attention processor to use to compute attention. + + Parameters: + processor (`dict` of `AttentionProcessor` or only `AttentionProcessor`): + The instantiated processor class or a dictionary of processor classes that will be set as the processor + for **all** `Attention` layers. + + If `processor` is a dict, the key needs to define the path to the corresponding cross attention + processor. This is strongly recommended when setting trainable attention processors. + + """ + count = len(self.attn_processors.keys()) + + if isinstance(processor, dict) and len(processor) != count: + raise ValueError( + f"A dict of processors was passed, but the number of processors {len(processor)} does not match the" + f" number of attention layers: {count}. Please make sure to pass {count} processor classes." + ) + + def fn_recursive_attn_processor(name: str, module: torch.nn.Module, processor): + if hasattr(module, "set_processor"): + if not isinstance(processor, dict): + module.set_processor(processor) + else: + module.set_processor(processor.pop(f"{name}.processor")) + + for sub_name, child in module.named_children(): + fn_recursive_attn_processor(f"{name}.{sub_name}", child, processor) + + for name, module in self.named_children(): + fn_recursive_attn_processor(name, module, processor) + def _set_gradient_checkpointing(self, module, value=False): if hasattr(module, "gradient_checkpointing"): module.gradient_checkpointing = value diff --git a/opensora/models/diffusion/opensora/rope.py b/opensora/models/diffusion/opensora/rope.py index 6c8d761bc..18ff24f1a 100644 --- a/opensora/models/diffusion/opensora/rope.py +++ b/opensora/models/diffusion/opensora/rope.py @@ -14,7 +14,7 @@ def __init__(self, ): self.cache_positions = {} def __call__(self, b, t, h, w, device): - if not (t,h,w) in self.cache_positions: + if not (b,t,h,w) in self.cache_positions: x = torch.arange(w, device=device) y = torch.arange(h, device=device) z = torch.arange(t, device=device) diff --git a/opensora/models/diffusion/opensora/videoip.py b/opensora/models/diffusion/opensora/videoip.py index efb5f3485..cdb40a1e3 100644 --- a/opensora/models/diffusion/opensora/videoip.py +++ b/opensora/models/diffusion/opensora/videoip.py @@ -16,9 +16,20 @@ from diffusers.utils.torch_utils import maybe_allow_in_graph from diffusers.models.lora import LoRACompatibleLinear -from .modules import BasicTransformerBlock +from .modules import BasicTransformerBlock, get_1d_sincos_pos_embed from einops import rearrange +from .rope import PositionGetter3D, RoPE3D +try: + import torch_npu + from opensora.npu_config import npu_config, set_run_dtype + from opensora.acceleration.parallel_states import get_sequence_parallel_state, hccl_info + from opensora.acceleration.communications import all_to_all_SBH +except: + torch_npu = None + npu_config = None + set_run_dtype = None + def zero_module(module): for p in module.parameters(): nn.init.zeros_(p) @@ -29,84 +40,56 @@ class VideoIPOutput(BaseOutput): hidden_states: torch.FloatTensor vip_cond_mask: torch.FloatTensor - class VideoIPVideoEncoder(nn.Module): def __init__( self, - image_encoder_type="clip", + in_channels=1024, num_attention_heads=16, - attention_head_dim=64, - cross_attention_dim=1152, - num_attention_layers=2, + attention_head_dim=72, + cross_attention_dim=2304, + num_attention_layers=[1, 3], use_rope=False, attention_mode='xformers', vae_scale_factor_t=4, - video_length=16, - max_num_tokens=256, + num_frames=93, # when image mode, num_frames = 1; when video mode, num_frames = 93 + max_num_tokens=288, # when 480p, max_num_tokens = 24 * 4 * 3 = 288; when 720p or 1080p, max_num_tokens = 24 * 7 * 4 = 672 + pooled_token_output_size=(16, 12), # when 480p, size=(16, 12); when 720p or 1080p, size=(28, 16) interpolation_scale_thw=(1, 1, 1), ): super().__init__() - inner_dim = num_attention_heads * attention_head_dim - - self.image_encoder_type = image_encoder_type - - self.vae_scale_factor_t = vae_scale_factor_t - self.video_length = video_length - - self.max_num_tokens = max_num_tokens - if USE_PEFT_BACKEND: linear_cls = nn.Linear else: linear_cls = LoRACompatibleLinear - if image_encoder_type == "clip": # F * 16 * 16 - # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") + inner_dim = num_attention_heads * attention_head_dim # 3d rope need inner_dim to be multiple of 3 + assert inner_dim % 3 == 0, "inner_dim must be multiple of 3" - self.conv_in = nn.ModuleList( - [ - nn.Conv3d(in_channels=1280, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 - nn.SiLU(), - nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), # F // 2 * 16 * 16 -> F // 4 * 8 * 8 - ] - ) + self.vae_scale_factor_t = vae_scale_factor_t + self.num_frames = num_frames - self.conv_in_downsample_factor = (4, 2, 2) - - elif image_encoder_type == "dino": # F * 16 * 16 - # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") - - self.conv_in = nn.ModuleList( - [ - nn.Conv3d(in_channels=1536, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(2, 1, 1), padding=(1, 1, 1)),# F * 16 * 16 -> F // 2 * 16 * 16 - nn.SiLU(), - nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), # F // 2 * 16 * 16 -> F // 4 * 8 * 8 - ] - ) + self.max_num_tokens = max_num_tokens - self.conv_in_downsample_factor = (4, 2, 2) + self.use_rope = use_rope - # elif image_encoder_type == "vae": # F // 4 * 64 * 64 - # assert in_channels is not None, "Please specify latent dim for VAE" + self.avg_pool = nn.AdaptiveAvgPool2d(output_size=pooled_token_output_size) + self.proj_in = nn.Sequential( + linear_cls(in_channels, inner_dim), + nn.GELU(), + linear_cls(inner_dim, inner_dim), + ) - # self.conv_in = nn.ModuleList( - # [ - # nn.Conv2d(in_channels=in_channels, out_channels=256, kernel_size=3, stride=2, padding=1), - # nn.SiLU(), - # nn.Conv2d(in_channels=256, out_channels=512, kernel_size=3, stride=2, padding=1), - # nn.SiLU(), - # nn.Conv2d(in_channels=512, out_channels=inner_dim, kernel_size=3, stride=2, padding=1), - # ] - # ) # F // 4 * 64 * 64 -> F // 4 * 8 * 8 + if not use_rope: + temp_pos_embed = get_1d_sincos_pos_embed(inner_dim, self.num_frames, base_size=self.num_frames, interpolation_scale=1.0) + self.register_buffer("temp_pos_embed", torch.from_numpy(temp_pos_embed).float().unsqueeze(0), persistent=False) - # self.conv_in_downsample_factor = (1, 8, 8) + self.conv1 = nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1) # F * H * W -> (F // 2) * (H // 2) * (W // 2) - else: - raise NotImplementedError + self.conv2 = nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=3, stride=2, padding=1) # (F // 2) * (H // 2) * (W // 2) -> (F // 4) * (H // 4) * (W // 4) - self.trans_block1 = nn.ModuleList( + self.trans1= nn.ModuleList( [ BasicTransformerBlock( dim=inner_dim, @@ -121,14 +104,11 @@ def __init__( attention_mode=attention_mode, interpolation_scale_thw=interpolation_scale_thw, ) - for _ in range(num_attention_layers) + for _ in range(num_attention_layers[0]) ] ) # only self-attention - self.conv_mid = nn.Conv3d(in_channels=inner_dim, out_channels=inner_dim, kernel_size=(3, 3, 3), stride=(1, 2, 2), padding=1) # F // 4 * 8 * 8 -> F // 4 * 4 * 4 - self.conv_mid_downsample_factor = (1, 2, 2) - - self.trans_block2 = nn.ModuleList( + self.trans2 = nn.ModuleList( [ BasicTransformerBlock( dim=inner_dim, @@ -143,8 +123,8 @@ def __init__( attention_mode=attention_mode, interpolation_scale_thw=interpolation_scale_thw, ) - for _ in range(num_attention_layers) - ] + for _ in range(num_attention_layers[1]) + ] ) # only self-attention self.proj_out = linear_cls(inner_dim, cross_attention_dim) @@ -158,6 +138,19 @@ def forward( ): # B C F H W input_batch_size, input_frame = hidden_states.shape[0], hidden_states.shape[2] + + hidden_states = rearrange(hidden_states, 'b c f h w -> (b f) c h w') + hidden_states = self.avg_pool(hidden_states) # (B F) C H W -> (B F) C h w + hidden_states = rearrange(hidden_states, '(b f) c h w -> b f h w c', f=input_frame) + hidden_states = self.proj_in(hidden_states) + + if not self.use_rope: + temp_pos_embed = self.temp_pos_embed + temp_pos_embed = rearrange(temp_pos_embed, 'b f c -> b f 1 1 c') + + hidden_states = hidden_states + temp_pos_embed[:, :input_frame] + + hidden_states = rearrange(hidden_states, 'b f h w c -> b c f h w') if image_mode: hidden_states = hidden_states.repeat_interleave(self.vae_scale_factor_t, dim=2) @@ -165,43 +158,48 @@ def forward( else: image_hidden_states = hidden_states[:, :, 0:1].repeat(1, 1, self.vae_scale_factor_t, 1, 1) hidden_states = torch.cat([image_hidden_states, hidden_states[:, :, 1:]], dim=2) - - for layer in self.conv_in: - hidden_states = layer(hidden_states) + + hidden_states = self.conv1(hidden_states) # B C F h w -> B C (F // 2) (h // 2) (w // 2) - # after conv_in - frame, height, width = hidden_states.shape[2], hidden_states.shape[3], hidden_states.shape[4] + # after conv1 + frame, height, width = hidden_states.shape[2:] # if training image, now batch = input_batch_size * frame; if not, batch remains the same hidden_states = rearrange(hidden_states, 'b c f h w -> b (f h w) c') - for layer in self.trans_block1: + for layer in self.trans1: hidden_states = layer( hidden_states=hidden_states, attention_mask=None, + frame=frame, + height=height, + width=width, ) # when using image mode, f=1; when using video mode, f=video_length hidden_states = rearrange(hidden_states, "b (f h w) c -> b c f h w ", f=frame, h=height, w=width) - hidden_states = self.conv_mid(hidden_states) + hidden_states = self.conv2(hidden_states) # B C (F // 2) (h // 2) (w // 2) -> B C (F // 4) (h // 4) (w // 4) + # after conv2 + frame, height, width = hidden_states.shape[2:] hidden_states = rearrange(hidden_states, "b c f h w -> b (f h w) c") - - for layer in self.trans_block2: + for layer in self.trans2: hidden_states = layer( hidden_states=hidden_states, attention_mask=None, + frame=frame, + height=height, + width=width, ) - # when using image mode, n=1*h*w; when using video mode, n=video_length*h*w + # when using image mode, n = 1 * h * w; when using video mode, n = video_length * h * w if image_mode: hidden_states = rearrange(hidden_states, '(b f) n c -> b f n c', f=input_frame) else: hidden_states = hidden_states.unsqueeze(1) # B N C -> B 1 N C - hidden_states = self.proj_out(hidden_states) hidden_states = self.norm_out(hidden_states) @@ -217,31 +215,34 @@ def forward( class VideoIPAdapter(nn.Module): def __init__( self, - image_encoder_type="clip", + in_channels=1024, num_attention_heads=16, - attention_head_dim=64, - cross_attention_dim=1152, - num_attention_layers=2, - use_rope=False, - rope_scaling=None, - attention_mode='xformers', + attention_head_dim=72, + cross_attention_dim=2304, + max_num_tokens=288, + pooled_token_output_size=(16, 12), + num_attention_layers=[1, 3], + use_rope=True, + attention_mode='math', gradient_checkpointing=False, vae_scale_factor_t=4, - video_length=17, + num_frames=93, + ): super().__init__() self.gradient_checkpointing = gradient_checkpointing self.encoder = VideoIPVideoEncoder( - image_encoder_type=image_encoder_type, + in_channels=in_channels, num_attention_heads=num_attention_heads, attention_head_dim=attention_head_dim, cross_attention_dim=cross_attention_dim, num_attention_layers=num_attention_layers, use_rope=use_rope, - rope_scaling=rope_scaling, attention_mode=attention_mode, vae_scale_factor_t=vae_scale_factor_t, - video_length=video_length, + num_frames=num_frames, + max_num_tokens=max_num_tokens, + pooled_token_output_size=pooled_token_output_size, ) def forward( @@ -291,32 +292,23 @@ class VideoIPAttnProcessor(nn.Module): """ def __init__( - self, - dim=1152, + self, + dim=2304, attention_mode='xformers', - use_rope=False, - rope_scaling=None, - compress_kv_factor=None, - - num_vip_tokens=272, + + num_vip_tokens=288, vip_scale=1.0, dropout=0.0, ): super().__init__() - self.dim = dim + self.attention_mode = attention_mode - self.use_rope = use_rope - self.rope_scaling = rope_scaling - self.compress_kv_factor = compress_kv_factor if USE_PEFT_BACKEND: linear_cls = nn.Linear else: linear_cls = LoRACompatibleLinear - if self.use_rope: - self._init_rope() - if not hasattr(F, "scaled_dot_product_attention"): raise ImportError("AttnProcessor2_0 requires PyTorch 2.0, to use it, please upgrade PyTorch to 2.0.") @@ -333,36 +325,28 @@ def __init__( self.num_vip_tokens = num_vip_tokens self.vip_scale = vip_scale - def _init_rope(self): - if self.rope_scaling is None: - self.rope2d = RoPE2D() - self.rope1d = RoPE1D() - else: - scaling_type = self.rope_scaling["type"] - scaling_factor_2d = self.rope_scaling["factor_2d"] - scaling_factor_1d = self.rope_scaling["factor_1d"] - if scaling_type == "linear": - self.rope2d = LinearScalingRoPE2D(scaling_factor=scaling_factor_2d) - self.rope1d = LinearScalingRoPE1D(scaling_factor=scaling_factor_1d) - else: - raise ValueError(f"Unknown RoPE scaling type {scaling_type}") - - def forward( + def __call__( self, attn: Attention, hidden_states: torch.FloatTensor, encoder_hidden_states: Optional[torch.FloatTensor] = None, attention_mask: Optional[torch.FloatTensor] = None, temb: Optional[torch.FloatTensor] = None, - scale: float = 1.0, - position_q: Optional[torch.LongTensor] = None, - position_k: Optional[torch.LongTensor] = None, - last_shape: Tuple[int] = None, + frame: int = 8, + height: int = 16, + width: int = 16, + *args, + **kwargs, ) -> torch.FloatTensor: - - residual = hidden_states + if len(args) > 0 or kwargs.get("scale", None) is not None: + deprecation_message = "The `scale` argument is deprecated and will be ignored. Please remove it, as passing it will raise an error in the future. `scale` should directly be passed while calling the underlying pipeline component i.e., via `cross_attention_kwargs`." + deprecate("scale", "1.0.0", deprecation_message) - args = () if USE_PEFT_BACKEND else (scale,) + if attn.downsampler is not None: + hidden_states, attention_mask = attn.downsampler(hidden_states, attention_mask, t=frame, h=height, w=width) + frame, height, width = attn.downsampler.t, attn.downsampler.h, attn.downsampler.w + + residual = hidden_states if attn.spatial_norm is not None: hidden_states = attn.spatial_norm(hidden_states, temb) @@ -374,37 +358,20 @@ def forward( hidden_states = hidden_states.view(batch_size, channel, height * width).transpose(1, 2) - if self.compress_kv_factor is not None: - batch_size = hidden_states.shape[0] - if len(last_shape) == 2: - encoder_hidden_states = hidden_states.permute(0, 2, 1).reshape(batch_size, self.dim, *last_shape) - encoder_hidden_states = attn.sr(encoder_hidden_states).reshape(batch_size, self.dim, -1).permute(0, 2, 1) - elif len(last_shape) == 1: - encoder_hidden_states = hidden_states.permute(0, 2, 1) - if last_shape[0] % 2 == 1: - first_frame_pad = encoder_hidden_states[:, :, :1].repeat((1, 1, attn.kernel_size - 1)) - encoder_hidden_states = torch.concatenate((first_frame_pad, encoder_hidden_states), dim=2) - encoder_hidden_states = attn.sr(encoder_hidden_states).permute(0, 2, 1) - else: - raise NotImplementedError(f'NotImplementedError with last_shape {last_shape}') - - encoder_hidden_states = attn.norm(encoder_hidden_states) - batch_size, sequence_length, _ = ( hidden_states.shape if encoder_hidden_states is None else encoder_hidden_states.shape ) + has_vip_tokens = encoder_hidden_states is not None if has_vip_tokens: end_pos = sequence_length - self.num_vip_tokens - - # attention_mask include encoder_hidden_states(text) and clip_feature(image or video) - # import ipdb;ipdb.set_trace() + if attention_mask is not None: if has_vip_tokens: attention_mask, vip_attention_mask = attention_mask[..., :end_pos], attention_mask[..., end_pos:] vip_attention_mask = attn.prepare_attention_mask(vip_attention_mask, self.num_vip_tokens, batch_size) vip_attention_mask = vip_attention_mask.view(batch_size, attn.heads, -1, vip_attention_mask.shape[-1]) - + attention_mask = attn.prepare_attention_mask(attention_mask, end_pos, batch_size) # scaled_dot_product_attention expects attention_mask shape to be # (batch, heads, source_length, target_length) @@ -413,8 +380,7 @@ def forward( if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) - args = () if USE_PEFT_BACKEND else (scale,) - query = attn.to_q(hidden_states, *args) + query = attn.to_q(hidden_states) if encoder_hidden_states is None: encoder_hidden_states = hidden_states @@ -429,12 +395,11 @@ def forward( # vip tokens is normed encoder_hidden_states = attn.norm_encoder_hidden_states(encoder_hidden_states) + key = attn.to_k(encoder_hidden_states) + value = attn.to_v(encoder_hidden_states) - key = attn.to_k(encoder_hidden_states, *args) - value = attn.to_v(encoder_hidden_states, *args) - - vip_key = self.to_k_vip(vip_hidden_states, *args) - vip_value = self.to_v_vip(vip_hidden_states, *args) + vip_key = self.to_k_vip(vip_hidden_states) + vip_value = self.to_v_vip(vip_hidden_states) inner_dim = key.shape[-1] head_dim = inner_dim // attn.heads @@ -442,32 +407,21 @@ def forward( query = query.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) key = key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - vip_key = vip_key.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) + # qk norm + # query = attn.q_norm(query) + # key = attn.k_norm(key) + value = value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) vip_value = vip_value.view(batch_size, -1, attn.heads, head_dim).transpose(1, 2) - if self.use_rope: - # require the shape of (batch_size x nheads x ntokens x dim) - if position_q.ndim == 3: - query = self.rope2d(query, position_q) - elif position_q.ndim == 2: - query = self.rope1d(query, position_q) - else: - raise NotImplementedError - if position_k.ndim == 3: - key = self.rope2d(key, position_k) - vip_key = self.rope2d(vip_key, position_k) - elif position_k.ndim == 2: - key = self.rope1d(key, position_k) - vip_key = self.rope1d(vip_key, position_k) - else: - raise NotImplementedError - + if attention_mask is None or not torch.all(attention_mask.bool()): # 0 mean visible + attention_mask = None # the output of sdp = (batch, num_heads, seq_len, head_dim) # TODO: add support for attn.scale when we move to Torch 2.1 + # import ipdb;ipdb.set_trace() + # print(attention_mask) if self.attention_mode == 'flash': - assert attention_mask is None or torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' + assert attention_mask is None or not torch.all(attention_mask.bool()), 'flash-attn do not support attention_mask' with torch.backends.cuda.sdp_kernel(enable_math=False, enable_flash=True, enable_mem_efficient=False): hidden_states = F.scaled_dot_product_attention( query, key, value, dropout_p=0.0, is_causal=False @@ -492,19 +446,20 @@ def forward( ) else: raise NotImplementedError(f'Found attention_mode: {self.attention_mode}') + hidden_states = hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + vip_hidden_states = vip_hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) + hidden_states = hidden_states.to(query.dtype) + vip_hidden_states = vip_hidden_states.to(query.dtype) # linear proj - hidden_states = attn.to_out[0](hidden_states, *args) + hidden_states = attn.to_out[0](hidden_states) # dropout hidden_states = attn.to_out[1](hidden_states) - vip_hidden_states = vip_hidden_states.transpose(1, 2).reshape(batch_size, -1, attn.heads * head_dim) - vip_hidden_states = vip_hidden_states.to(query.dtype) - # linear proj - vip_hidden_states = self.to_out_vip[0](vip_hidden_states, *args) + vip_hidden_states = self.to_out_vip[0](vip_hidden_states) # dropout vip_hidden_states = self.to_out_vip[1](vip_hidden_states) @@ -522,22 +477,21 @@ def forward( if __name__ == "__main__": model = VideoIPVideoEncoder( - image_encoder_type="clip", - inner_dim=1024, - cross_attention_dim=1152, - num_attention_layers=2, - use_rope=False, - rope_scaling=None, + in_channels=1536, + num_attention_heads=16, + attention_head_dim=72, + cross_attention_dim=2304, + num_attention_layers=[1, 3], + use_rope=True, attention_mode='math', vae_scale_factor_t=4, - video_length=17, + num_frames=1, ) params = sum(p.numel() for p in model.parameters() if p.requires_grad) print(f"Trainable parameters: {params / 1e6}M") - video = torch.randn(2, 1280, 1, 16, 16) + video = torch.randn(2, 1536, 1, 45, 37) - output = model(video, training_image=True) - print(output.vip_cond_mask) + output = model(video, image_mode=True) diff --git a/opensora/models/text_encoder/__init__.py b/opensora/models/text_encoder/__init__.py index 80d3c30d6..c1669068f 100644 --- a/opensora/models/text_encoder/__init__.py +++ b/opensora/models/text_encoder/__init__.py @@ -13,8 +13,8 @@ def __init__(self, args, **kwargs): from transformers import MT5EncoderModel print(f"Loading MT5 model: {self.model_name}") print(f"cache_dir: {args.cache_dir}") - self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() - # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() + # self.text_enc = MT5EncoderModel.from_pretrained(self.model_name, cache_dir=args.cache_dir, **kwargs).eval() + self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, **kwargs).eval() # self.text_enc = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/models--google--mt5-xl/snapshots/63fc6450d80515b48e026b69ef2fbbd426433e84", cache_dir=args.cache_dir, **kwargs).eval() elif 't5' in self.model_name: from transformers import T5EncoderModel diff --git a/opensora/sample/pipeline_for_vip.py b/opensora/sample/pipeline_for_vip.py index 803cb04b5..5467773e4 100644 --- a/opensora/sample/pipeline_for_vip.py +++ b/opensora/sample/pipeline_for_vip.py @@ -43,9 +43,10 @@ def hacked_pipeline_call_for_vip( clean_caption: bool = True, use_resolution_binning: bool = True, max_sequence_length: int = 300, + device="cuda", **kwargs, ) -> Union[ImagePipelineOutput, Tuple]: - + # 1. Check inputs. Raise error if not correct num_frames = num_frames or self.transformer.config.sample_size_t * self.vae.vae_scale_factor[0] height = height or self.transformer.config.sample_size[0] * self.vae.vae_scale_factor[1] @@ -70,9 +71,7 @@ def hacked_pipeline_call_for_vip( batch_size = len(prompt) else: batch_size = prompt_embeds.shape[0] - # import ipdb;ipdb.set_trace() - device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') - + # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` # corresponds to doing no classifier free guidance. @@ -97,10 +96,9 @@ def hacked_pipeline_call_for_vip( clean_caption=clean_caption, max_sequence_length=max_sequence_length, ) - if do_classifier_free_guidance: prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds], dim=0) - prompt_attention_mask = torch.cat([negative_prompt_attention_mask, prompt_attention_mask], dim=0) + prompt_attention_mask = torch.cat([negative_prompt_attention_mask, prompt_attention_mask], dim=0) # NOTE add vip tokens vip_tokens = torch.cat([negative_vip_tokens, vip_tokens], dim=0) vip_attention_mask = torch.cat([negative_vip_attention_mask, vip_attention_mask], dim=0) @@ -169,7 +167,6 @@ def hacked_pipeline_call_for_vip( vip_attention_mask=vip_attention_mask, timestep=current_timestep, added_cond_kwargs=added_cond_kwargs, - enable_temporal_attentions=num_frames > 1, return_dict=False, )[0] diff --git a/opensora/sample/pipeline_inpaint.py b/opensora/sample/pipeline_inpaint.py index af9f85c4c..076324440 100644 --- a/opensora/sample/pipeline_inpaint.py +++ b/opensora/sample/pipeline_inpaint.py @@ -54,6 +54,7 @@ def hacked_pipeline_call_for_inpaint( clean_caption: bool = True, use_resolution_binning: bool = True, max_sequence_length: int = 300, + device="cuda", **kwargs, ) -> Union[ImagePipelineOutput, Tuple]: """ @@ -157,7 +158,6 @@ def hacked_pipeline_call_for_inpaint( else: batch_size = prompt_embeds.shape[0] # import ipdb;ipdb.set_trace() - device = getattr(self, '_execution_device', None) or getattr(self, 'device', None) or torch.device('cuda') # here `guidance_scale` is defined analog to the guidance weight `w` of equation (2) # of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1` @@ -317,7 +317,7 @@ def hacked_pipeline_call_for_inpaint( # latents = latents.squeeze(2) if not output_type == "latent": # b t h w c - image = self.decode_latents(latents) + image = self.decode_latents(latents, device) image = image[:, :num_frames, :height, :width] else: image = latents @@ -329,3 +329,14 @@ def hacked_pipeline_call_for_inpaint( return (image,) return ImagePipelineOutput(images=image) + +def decode_latents(self, latents, device): + # if npu_config is not None: + # npu_config.print_tensor_stats(latents, f"before vae", rank=0) + self.vae = self.vae.to(device) + video = self.vae.decode(latents.to(self.vae.vae.dtype).to(device)) + # if npu_config is not None: + # npu_config.print_tensor_stats(video, f"after vae, vae.dtype={self.vae.vae.dtype}", rank=0) + video = ((video / 2.0 + 0.5).clamp(0, 1) * 255).to(dtype=torch.uint8).cpu().permute(0, 1, 3, 4, 2).contiguous() # b t h w c + # we always cast to float32 as this does not cause significant overhead and is compatible with bfloa16 + return video diff --git a/opensora/sample/sample_inpaint.py b/opensora/sample/sample_inpaint.py index a63fa237d..6be43da55 100644 --- a/opensora/sample/sample_inpaint.py +++ b/opensora/sample/sample_inpaint.py @@ -1,5 +1,6 @@ import math import os +from accelerate.utils import set_seed import pip import torch import argparse @@ -21,6 +22,9 @@ if project_root not in sys.path: sys.path.append(project_root) +from torch.nn.parallel import DistributedDataParallel as DDP +import torch.distributed as dist + from opensora.models.ae import ae_stride_config, getae, getae_wrapper from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper from opensora.models.diffusion.opensora.modeling_inpaint import OpenSoraInpaint @@ -29,7 +33,7 @@ from opensora.utils.utils import save_video_grid from opensora.sample.pipeline_opensora import OpenSoraPipeline -from opensora.sample.pipeline_inpaint import hacked_pipeline_call_for_inpaint +from opensora.sample.pipeline_inpaint import hacked_pipeline_call_for_inpaint, decode_latents # for validation import glob from PIL import Image @@ -44,10 +48,12 @@ import gc import time +@torch.inference_mode() def validation(args): + # torch.manual_seed(args.seed) weight_dtype = torch.bfloat16 - device = torch.device(args.device) + device = torch.device(f'cuda:{args.rank}') # vae = getae_wrapper(args.ae)(args.model_path, subfolder="vae", cache_dir=args.cache_dir) vae = getae_wrapper(args.ae)(args.ae_path) @@ -65,6 +71,11 @@ def validation(args): text_encoder = MT5EncoderModel.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir, low_cpu_mem_usage=True, torch_dtype=weight_dtype) tokenizer = AutoTokenizer.from_pretrained("/storage/ongoing/new/Open-Sora-Plan/cache_dir/mt5-xxl", cache_dir=args.cache_dir) + + transformer_model = transformer_model.to(device) + vae.vae = vae.vae.to(device) + text_encoder = text_encoder.to(device) + # set eval mode transformer_model.eval() vae.eval() @@ -116,9 +127,9 @@ def validation(args): tokenizer=tokenizer, scheduler=scheduler, transformer=transformer_model) - pipeline.to(device) pipeline.__call__ = hacked_pipeline_call_for_inpaint.__get__(pipeline, OpenSoraPipeline) + pipeline.decode_latents = decode_latents.__get__(pipeline, OpenSoraPipeline) validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" prompt_file = os.path.join(validation_dir, "prompt.txt") @@ -168,6 +179,9 @@ def preprocess_images(images): images = [images] if 'img' in images[0]: continue + + if idx % args.world_size != args.rank: + continue pre_results = preprocess_images(images) condition_images = pre_results['condition_images'] @@ -187,24 +201,31 @@ def preprocess_images(images): num_images_per_prompt=1, mask_feature=True, max_sequence_length=args.max_sequence_length, + device=device, ).images videos.append(video[0]) ext = 'mp4' imageio.mimwrite( - os.path.join(save_dir, f'{idx}.{ext}'), video[0], fps=24, quality=6) # highest quality is 10, lowest is 0 + os.path.join(save_dir, f'{idx}.{ext}'), video[0], fps=24, quality=8) # highest quality is 10, lowest is 0 - video_grids = torch.stack(videos, dim=0) + video_grids = torch.stack(videos, dim=0).to(device=device) + shape = list(video_grids.shape) + shape[0] *= world_size + gathered_tensor = torch.zeros(shape, dtype=video_grids.dtype, device=device) + dist.all_gather_into_tensor(gathered_tensor, video_grids.contiguous()) + video_grids = gathered_tensor.cpu() + if args.rank == 0: # torchvision.io.write_video(args.save_img_path + '_%04d' % args.run_time + '-.mp4', video_grids, fps=6) - if args.num_frames == 1: - save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), - nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) - else: - video_grids = save_video_grid(video_grids) - imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=6) + if args.num_frames == 1: + save_image(video_grids / 255.0, os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), + nrow=math.ceil(math.sqrt(len(video_grids))), normalize=True, value_range=(0, 1)) + else: + video_grids = save_video_grid(video_grids) + imageio.mimwrite(os.path.join(args.save_img_path, f'{args.sample_method}_gs{args.guidance_scale}_s{args.num_sampling_steps}.{ext}'), video_grids, fps=args.fps, quality=8) - print('save path {}'.format(args.save_img_path)) + print('save path {}'.format(args.save_img_path)) del pipeline del text_encoder @@ -213,6 +234,38 @@ def preprocess_images(images): gc.collect() torch.cuda.empty_cache() +def main(args): + + lask_ckpt = None + root_model_path = args.model_path + root_save_path = args.save_img_path + + + while True: + # Get the most recent checkpoint + dirs = os.listdir(root_model_path) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + dist.barrier() + if path != lask_ckpt: + print("====================================================") + print(f"sample {path}...") + args.model_path = os.path.join(root_model_path, path, "model") + if os.path.exists(os.path.join(args.model_path, 'config.json')) and os.path.exists(os.path.join(args.model_path, 'diffusion_pytorch_model.safetensors')): + args.save_img_path = os.path.join(root_save_path, f"{path}_normal") + validation(args) + print("====================================================") + print(f"sample ema {path}...") + args.model_path = os.path.join(root_model_path, path, "model_ema") + if os.path.exists(os.path.join(args.model_path, 'config.json')) and os.path.exists(os.path.join(args.model_path, 'diffusion_pytorch_model.safetensors')): + args.save_img_path = os.path.join(root_save_path, f"{path}_ema") + validation(args) + lask_ckpt = path + else: + print("no new ckpt, sleeping...") + time.sleep(60) + if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--model_path", type=str, default='LanguageBind/Open-Sora-Plan-v1.0.0') @@ -239,29 +292,18 @@ def preprocess_images(images): parser.add_argument('--enable_stable_fp32', action='store_true') parser.add_argument("--validation_dir", type=str, default=None) + parser.add_argument("--seed", type=int, default=42) args = parser.parse_args() - lask_ckpt = None - root_model_path = args.model_path - root_save_path = args.save_img_path - while True: - # Get the most recent checkpoint - dirs = os.listdir(root_model_path) - dirs = [d for d in dirs if d.startswith("checkpoint")] - dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) - path = dirs[-1] if len(dirs) > 0 else None - if path != lask_ckpt: - print("====================================================") - print(f"sample {path}...") - args.model_path = os.path.join(root_model_path, path, "model") - args.save_img_path = os.path.join(root_save_path, f"{path}_normal") - validation(args) - print("====================================================") - print(f"sample ema {path}...") - args.model_path = os.path.join(root_model_path, path, "model_ema") - args.save_img_path = os.path.join(root_save_path, f"{path}_ema") - validation(args) - lask_ckpt = path - else: - print("no new ckpt, sleeping...") - time.sleep(5) + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + args.world_size = world_size = torch.cuda.device_count() + args.rank = int(os.environ["LOCAL_RANK"]) + + # Initialize the process group + dist.init_process_group("nccl", rank=args.rank, world_size=args.world_size) + + main(args) + diff --git a/opensora/train/train_inpaint.py b/opensora/train/train_inpaint.py index 0c5230407..1c6c9fe29 100644 --- a/opensora/train/train_inpaint.py +++ b/opensora/train/train_inpaint.py @@ -1033,7 +1033,7 @@ def train_all_epoch(prof_=None): parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel") parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training") - parser.add_argument("--ema_decay", type=float, default=0.99) + parser.add_argument("--ema_decay", type=float, default=0.999) # inpaint dataset parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode diff --git a/opensora/train/train_videoip.py b/opensora/train/train_videoip.py index 0f230df88..24437b611 100644 --- a/opensora/train/train_videoip.py +++ b/opensora/train/train_videoip.py @@ -40,9 +40,22 @@ from huggingface_hub import create_repo from packaging import version from tqdm.auto import tqdm -from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer + from math import sqrt +from opensora.adaptor.modules import replace_with_fp32_forwards + +try: + import torch_npu + from opensora.npu_config import npu_config + from opensora.acceleration.parallel_states import initialize_sequence_parallel_state, \ + destroy_sequence_parallel_group, get_sequence_parallel_state + from opensora.acceleration.communications import prepare_parallel_data, broadcast +except: + torch_npu = None + npu_config = None + pass + import diffusers from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler from diffusers.optimization import get_scheduler @@ -53,10 +66,9 @@ from opensora.dataset import getdataset, ae_denorm from opensora.models.ae import getae, getae_wrapper -from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper -from opensora.models.diffusion.latte.modeling_latte import LatteT2V + from opensora.models.text_encoder import get_text_enc, get_text_warpper -from opensora.utils.dataset_utils import VideoIP_Collate as Collate +from opensora.utils.dataset_utils import VideoIP_Collate as Collate, LengthGroupedSampler from opensora.models.ae import ae_stride_config, ae_channel_config from opensora.models.diffusion import Diffusion_models, Diffusion_models_class from opensora.sample.pipeline_opensora import OpenSoraPipeline @@ -71,9 +83,10 @@ from opensora.sample.pipeline_opensora import OpenSoraPipeline from opensora.sample.pipeline_for_vip import hacked_pipeline_call_for_vip -from opensora.models.diffusion.latte.videoip import VideoIPAdapter, VideoIPAttnProcessor -from opensora.models.diffusion.latte.modeling_for_vip import hacked_forward_for_vip, hook_forward_fn, hook_backward_fn -from opensora.models.diffusion.latte.modules import BasicTransformerBlock +from opensora.models.diffusion.opensora.videoip import VideoIPAttnProcessor, VideoIPAdapter +from opensora.models.diffusion.opensora.modeling_for_vip import hacked_model + +import timm import glob from torchvision.utils import save_image @@ -83,56 +96,57 @@ check_min_version("0.24.0") logger = get_logger(__name__) -def get_clip_feature(clip_data, image_encoder, image_encoder_type='clip'): - if image_encoder_type == 'clip': - clip_feature = image_encoder(clip_data, output_hidden_states=True).hidden_states[-2] # B * (T+image_num) N D - elif image_encoder_type == 'dino': - clip_feature = image_encoder(clip_data).last_hidden_state # B * (T+image_num) N D - # clip_feature = image_encoder(clip_data, output_hidden_states=True).hidden_states[-2] # B * (T+image_num) N D +# we use timm styled model +def get_clip_feature(clip_data, image_encoder): + batch_size = clip_data.shape[0] + clip_data = rearrange(clip_data, 'b t c h w -> (b t) c h w') # B T+image_num C H W -> B * (T+image_num) C H W + # NOTE using last layer of DINO as clip feature + clip_feature = image_encoder.forward_features(clip_data) + clip_feature = clip_feature[:, 5:] # drop cls token and reg tokens + clip_feature_height = 518 // 14 # 37, dino height + clip_feature = rearrange(clip_feature, '(b t) (h w) c -> b c t h w', b=batch_size, h=clip_feature_height) # B T+image_num H W D + return clip_feature - class VIPNet(ModelMixin, ConfigMixin): @register_to_config def __init__( self, - image_encoder_type='dino', - cross_attention_dim=1152, - num_tokens=272, + image_encoder_out_channels=1536, + cross_attention_dim=2304, + num_tokens=16, # when 480p, max_num_tokens = 24 * 4 * 3 = 288; when 720p or 1080p, max_num_tokens = 24 * 7 * 4 = 672 + pooled_token_output_size=(16, 12), vip_num_attention_heads=16, - vip_attention_head_dim=64, - vip_num_attention_layers=2, + vip_attention_head_dim=72, + vip_num_attention_layers=[1, 3], attention_mode='xformers', gradient_checkpointing=False, vae_scale_factor_t=4, - video_length=17, - use_rope=False, - rope_scaling=None, + num_frames=93, + use_rope=True, attn_proc_type_dict={}, ): super().__init__() - self.image_encoder_type = image_encoder_type self.cross_attention_dim = cross_attention_dim self.num_tokens = num_tokens self.attention_mode = attention_mode self.use_rope = use_rope - self.rope_scaling = rope_scaling - self.vip_adapter = VideoIPAdapter( - image_encoder_type=image_encoder_type, - cross_attention_dim=cross_attention_dim, + in_channels=image_encoder_out_channels, num_attention_heads=vip_num_attention_heads, attention_head_dim=vip_attention_head_dim, + cross_attention_dim=cross_attention_dim, + max_num_tokens=num_tokens, + pooled_token_output_size=pooled_token_output_size, num_attention_layers=vip_num_attention_layers, - use_rope=use_rope, - rope_scaling=rope_scaling, attention_mode=attention_mode, gradient_checkpointing=gradient_checkpointing, vae_scale_factor_t=vae_scale_factor_t, - video_length=video_length, + num_frames=num_frames, + use_rope=use_rope, ) @@ -143,14 +157,11 @@ def __init__( self.attn_procs[name] = VideoIPAttnProcessor( dim=cross_attention_dim, attention_mode=attention_mode, - use_rope=use_rope, - rope_scaling=rope_scaling, num_vip_tokens=num_tokens, ) + self.adapter_modules = torch.nn.ModuleList(self.attn_procs.values()) - # if pretrained_vip_adapter_path is not None: - # self.load_vip_adapter(pretrained_vip_adapter_path) def set_vip_adapter(self, model, init_from_original_attn_processor): # init adapter modules @@ -180,23 +191,19 @@ def get_image_embeds(self, images, image_processor, image_encoder, transform, de images = [images] images = [Image.open(image).convert("RGB") for image in images] images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in images] # 1 H W C - images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in images]) # resize, 1 C H W + images = torch.cat([transform(image.permute(0, 3, 1, 2)) for image in images]) # resize, 1 C H W - images = image_processor(images=images, return_tensors="pt").pixel_values # 1 C H W + images = image_processor(images) # 1 C H W images = images.to(device=device, dtype=weight_dtype) negative_images = torch.zeros_like(images, device=device, dtype=weight_dtype) + # NOTE using the second last layer of image encoder - images = get_clip_feature(images, image_encoder, image_encoder_type=self.image_encoder_type) # 1 N D - images = images[:, 1:] # drop cls token - negative_images = get_clip_feature(negative_images, image_encoder, image_encoder_type=self.image_encoder_type) - negative_images = negative_images[:, 1:] - - height = width = int(sqrt(images.shape[1])) - images = rearrange(images, '1 (h w) c -> c 1 h w', h=height, w=width) - negative_images = rearrange(negative_images, '1 (h w) c -> c 1 h w', h=height, w=width) - images = images.unsqueeze(0) # 1 C 1 H W + images = images.unsqueeze(0) # 1 1 C H W negative_images = negative_images.unsqueeze(0) + images = get_clip_feature(images, image_encoder) # 1 1 C H W -> 1 D 1 h w + negative_images = get_clip_feature(negative_images, image_encoder) + vip_out = self.vip_adapter(hidden_states=images, use_image_num=0) vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N @@ -213,27 +220,22 @@ def get_video_embeds(self, condition_images, num_frames, image_processor, image_ condition_images_indices = [0, -1] condition_images = [Image.open(image).convert("RGB") for image in condition_images] condition_images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in condition_images] # F [1 H W C] - condition_images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in condition_images]) # resize, [F C H W] + condition_images = torch.cat([transform(image.permute(0, 3, 1, 2)) for image in condition_images]) # resize, [F C H W] - condition_images = image_processor(images=condition_images, return_tensors="pt").pixel_values # F C H W + condition_images = image_processor(condition_images) # F C H W condition_images = condition_images.to(device=device, dtype=weight_dtype) _, C, H, W = condition_images.shape video = torch.zeros([num_frames, C, H, W], device=device, dtype=weight_dtype) video[condition_images_indices] = condition_images negative_video = torch.zeros_like(video, device=device, dtype=weight_dtype) - # using the second last layer of image encoder - video = get_clip_feature(video, image_encoder, image_encoder_type=self.image_encoder_type) - video = video[:, 1:] # drop cls token - negative_video = get_clip_feature(negative_video, image_encoder, image_encoder_type=self.image_encoder_type) - negative_video = negative_video[:, 1:] - - height = width = int(sqrt(video.shape[1])) - video = rearrange(video, 't (h w) c -> c t h w', h=height, w=width) - negative_video = rearrange(negative_video, 't (h w) c -> c t h w', h=height, w=width) - video = video.unsqueeze(0) # 1 C F H W + video = video.unsqueeze(0) # 1 F C H W negative_video = negative_video.unsqueeze(0) + # using the second last layer of image encoder + video = get_clip_feature(video, image_encoder) # 1 F C H W -> 1 D F h w + negative_video = get_clip_feature(negative_video, image_encoder) + vip_out = self.vip_adapter(hidden_states=video, use_image_num=0) vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N @@ -244,16 +246,14 @@ def get_video_embeds(self, condition_images, num_frames, image_processor, image_ def forward(self, model, latent_model_input, timestep, **model_kwargs): - enable_temporal_attentions = True if args.num_frames > 1 else False encoder_hidden_states = model_kwargs.pop('encoder_hidden_states', None) encoder_attention_mask = model_kwargs.pop('encoder_attention_mask', None) clip_feature = model_kwargs.pop('clip_feature', None) use_image_num = model_kwargs.pop('use_image_num', 0) assert encoder_hidden_states is not None and encoder_attention_mask is not None and clip_feature is not None, "VIPNet requires encoder_hidden_states, encoder_attention_mask and clip_feature" - hidden_states = rearrange(clip_feature, 'b t h w c -> b c t h w') - vip_out = self.vip_adapter(hidden_states=hidden_states, use_image_num=use_image_num) # B D T+image_num H W -> B 1+image_num N D + vip_out = self.vip_adapter(hidden_states=clip_feature, use_image_num=use_image_num) # B D T+image_num H W -> B 1+image_num N D vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask model_pred = model( @@ -264,7 +264,6 @@ def forward(self, model, latent_model_input, timestep, **model_kwargs): vip_hidden_states=vip_tokens, vip_attention_mask=vip_cond_mask, use_image_num=use_image_num, - enable_temporal_attentions=enable_temporal_attentions, **model_kwargs )[0] @@ -287,7 +286,6 @@ def log_validation( ema=False ): - validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" prompt_file = os.path.join(validation_dir, "prompt.txt") @@ -328,10 +326,17 @@ def log_validation( pipeline.__call__ = hacked_pipeline_call_for_vip.__get__(pipeline, OpenSoraPipeline) videos = [] + prompts = [] gen_img = False - for prompt, images in zip(validation_prompt, validation_images_list): + max_val_img_num = 10 + + for idx, (prompt, images) in enumerate(zip(validation_prompt, validation_images_list)): if not isinstance(images, list): images = [images] + + if idx > max_val_img_num: + break + logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) if args.num_frames != 1: if len(images) == 1 and images[0].split('/')[-1].split('_')[0] == 'img': @@ -341,7 +346,7 @@ def log_validation( image_encoder=image_encoder, transform=resize_transform, device=accelerator.device, - weight_dtype=torch.float32 + weight_dtype=weight_dtype ) gen_img = True else: @@ -352,18 +357,21 @@ def log_validation( image_encoder=image_encoder, transform=resize_transform, device=accelerator.device, - weight_dtype=torch.float32 + weight_dtype=weight_dtype ) else: + # if len(images) == 1 and images[0].split('/')[-1].split('_')[0] == 'img': vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( images=images[0], # only using first image image_processor=image_processor, image_encoder=image_encoder, transform=resize_transform, device=accelerator.device, - weight_dtype=torch.float32 + weight_dtype=weight_dtype ) gen_img = True + # else: + # break video = pipeline.__call__( prompt=prompt, @@ -380,9 +388,10 @@ def log_validation( num_images_per_prompt=1, mask_feature=True, device=accelerator.device, - max_squence_length=args.model_max_length, + max_sequence_length=args.model_max_length, ).images videos.append(video[0]) + prompts.append(prompt) gen_img = False # import ipdb;ipdb.set_trace() @@ -415,7 +424,7 @@ def log_validation( logs = {} logs[f"{'ema_' if ema else ''}validation_videos"] = [] logs[f"{'ema_' if ema else ''}validation_images"] = [] - for i, (video, prompt) in enumerate(zip(videos, validation_prompt)): + for i, (video, prompt) in enumerate(zip(videos, prompts)): if video.shape[0] == 1: # image logs[f"{'ema_' if ema else ''}validation_images"].append(wandb.Image(video[0], caption=f"{i}: {prompt}")) else: # video @@ -440,9 +449,14 @@ def __init__(self, global_step, train_loss=0.0): ################################################################################# def main(args): - logging_dir = Path(args.output_dir, args.logging_dir) + # use LayerNorm, GeLu, SiLu always as fp32 mode + if args.enable_stable_fp32: + replace_with_fp32_forwards() + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(args) + npu_config.seed_everything(args.seed) accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) accelerator = Accelerator( @@ -452,10 +466,8 @@ def main(args): project_config=accelerator_project_config, ) - if accelerator.is_main_process: - print("--------------------------training args--------------------------") - print(args) - print("-----------------------------------------------------------------") + if npu_config is not None and args.num_frames != 1 and args.use_image_num == 0: + initialize_sequence_parallel_state(args.sp_size) if args.report_to == "wandb": if not is_wandb_available(): @@ -522,13 +534,12 @@ def main(args): ae.latent_size = latent_size if args.num_frames % 2 == 1: - args.latent_size_t = latent_size_t = args.num_frames // ae_stride_t + 1 + args.latent_size_t = latent_size_t = (args.num_frames - 1) // ae_stride_t + 1 else: latent_size_t = args.num_frames // ae_stride_t - # when training video ip adapter, we use t2v model model = Diffusion_models[args.model]( in_channels=ae_channel_config[args.ae], - out_channels=ae_channel_config[args.ae] * 2, # 因为要加载预训练权重,所以这里out_channels仍然设置为2倍 + out_channels=ae_channel_config[args.ae], # caption_channels=4096, # cross_attention_dim=1152, attention_bias=True, @@ -546,21 +557,21 @@ def main(args): norm_eps=1e-6, attention_type='default', attention_mode=args.attention_mode, + interpolation_scale_h=args.interpolation_scale_h, + interpolation_scale_w=args.interpolation_scale_w, + interpolation_scale_t=args.interpolation_scale_t, downsampler=args.downsampler, - compress_kv_factor=args.compress_kv_factor, + # compress_kv_factor=args.compress_kv_factor, use_rope=args.use_rope, - model_max_length=args.model_max_length, + # model_max_length=args.model_max_length, + use_stable_fp32=args.enable_stable_fp32, ) model.gradient_checkpointing = args.gradient_checkpointing # NOTE replace forward for VIP - model.forward = hacked_forward_for_vip.__get__(model, Diffusion_models[args.model]) + hacked_model(model) # # use pretrained model? - # NOTE when using inpaint model - # if args.pretrained: - # model.custom_load_state_dict(args.pretrained) - if args.pretrained: model_state_dict = model.state_dict() if 'safetensors' in args.pretrained: # pixart series @@ -584,25 +595,19 @@ def main(args): logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') logger.info(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') - # Freeze main model + # Freeze main models ae.vae.requires_grad_(False) text_enc.requires_grad_(False) model.requires_grad_(False) # load image encoder - if args.image_encoder_type == 'clip': - print("using (clip) as image encoder!") - # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") - image_encoder = CLIPVisionModelWithProjection.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) - elif args.image_encoder_type == 'dino': - print("using (dinov2) as image encoder!") - # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") - image_encoder = AutoModel.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + if 'dino' in args.image_encoder_name: + print(f"load {args.image_encoder_name} as image encoder...") + image_encoder = timm.create_model(args.image_encoder_name, pretrained=True, dynamic_img_size=True) + image_encoder.requires_grad_(False) else: raise NotImplementedError - image_encoder.requires_grad_(False) - attn_proc_type_dict = {} for name, attn_processor in model.attn_processors.items(): # replace all attn2.processor with VideoIPAttnProcessor @@ -612,16 +617,18 @@ def main(args): attn_proc_type_dict[name] = attn_processor.__class__.__name__ vip = VIPNet( - image_encoder_type=args.image_encoder_type, - cross_attention_dim=1152, - num_tokens=272, # NOTE should be modified + image_encoder_out_channels=1536, + cross_attention_dim=2304, + num_tokens=12, # NOTE should be modified + pooled_token_output_size=(16, 12), # NOTE should be modified vip_num_attention_heads=args.vip_num_attention_heads, # for dinov2 - vip_attention_head_dim=64, - vip_num_attention_layers=2, + vip_attention_head_dim=72, + vip_num_attention_layers=[1, 3], attention_mode=args.attention_mode, - gradient_checkpointing=args.gradient_checkpointing, + gradient_checkpointing=False, vae_scale_factor_t=ae_stride_t, - video_length=latent_size_t, + num_frames=args.num_frames, + use_rope=args.use_rope, attn_proc_type_dict=attn_proc_type_dict, ) @@ -630,17 +637,11 @@ def main(args): checkpoint = safe_load(os.path.join(args.pretrained_vip_adapter_path, "diffusion_pytorch_model.safetensors"), device="cpu") vip.load_state_dict(checkpoint) - init_from_original_attn_processor = False if (args.pretrained_vip_adapter_path is not None or args.resume_from_checkpoint is not None) else True vip.set_vip_adapter(model, init_from_original_attn_processor=init_from_original_attn_processor) - # for name, module in vip.named_modules(): - # # # if isinstance(module, VideoIPAttnProcessor): - # # # module.register_full_backward_hook(hook_backward_fn) - # if isinstance(module, nn.Conv3d): - # module.register_backward_hook(hook_backward_fn) - noise_scheduler = DDPMScheduler(rescale_betas_zero_snr=args.zero_terminal_snr) + noise_scheduler = DDPMScheduler() # Move unet, vae and text_encoder to device and cast to weight_dtype # The VAE is in float32 to avoid NaN losses. ae.vae.to(accelerator.device, dtype=torch.float32) @@ -679,7 +680,6 @@ def load_model_hook(models, input_dir): ema_vip.to(accelerator.device) del load_model - print("loading model...") for i in range(len(models)): @@ -766,17 +766,28 @@ def load_model_hook(models, input_dir): use_bias_correction=args.prodigy_use_bias_correction, safeguard_warmup=args.prodigy_safeguard_warmup, ) - + logger.info(f"optimizer: {optimizer}") + # Setup data: train_dataset = getdataset(args) - train_dataloader = torch.utils.data.DataLoader( + # NOTE when training video, we need sampler + # sampler = LengthGroupedSampler( + # args.train_batch_size, + # world_size=accelerator.num_processes, + # lengths=train_dataset.lengths, + # group_frame=args.group_frame, + # group_resolution=args.group_resolution, + # ) if args.group_frame or args.group_resolution else None + sampler = None + train_dataloader = DataLoader( train_dataset, - shuffle=True, + shuffle=sampler is None, # pin_memory=True, collate_fn=Collate(args), batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, - # prefetch_factor=8 + # sampler=sampler if args.group_frame or args.group_resolution else None, + # prefetch_factor=4 ) # Scheduler and math around the number of training steps. @@ -831,13 +842,10 @@ def load_model_hook(models, input_dir): logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") logger.info(f" Total optimization steps = {args.max_train_steps}") - logger.info(f" Total trainable parameters = {sum(p.numel() for p in vip.parameters() if p.requires_grad) / 1e9} B") + logger.info(f" Total trainable parameters = {sum(p.numel() for p in vip.parameters() if p.requires_grad) / 1e6} M") global_step = 0 first_epoch = 0 - # NOTE checking update of params - # initial_params = {name: param.clone() for name, param in vip.named_parameters()} - # Potentially load in the weights and states from a previous save if args.resume_from_checkpoint: if args.resume_from_checkpoint != "latest": @@ -863,6 +871,8 @@ def load_model_hook(models, input_dir): initial_global_step = global_step first_epoch = global_step // num_update_steps_per_epoch + if npu_config is not None: + train_dataset.n_used_elements = global_step * args.train_batch_size else: initial_global_step = 0 @@ -883,7 +893,12 @@ def sync_gradients_info(loss): progress_bar.update(1) progress_info.global_step += 1 end_time = time.time() + one_step_duration = end_time - start_time accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + if torch_npu is not None and npu_config is not None: + npu_config.print_msg(f"Step: [{progress_info.global_step}], local_loss={loss.detach().item()}, " + f"train_loss={progress_info.train_loss}, time_cost={one_step_duration}", + rank=0) progress_info.train_loss = 0.0 # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. @@ -933,12 +948,14 @@ def run(model_input, model_kwargs, prof): bsz = model_input.shape[0] # Sample a random timestep for each image without bias. timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + if npu_config is not None and get_sequence_parallel_state(): + broadcast(timesteps) # Add noise to the model input according to the noise magnitude at each timestep # (this is the forward diffusion process) noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) - + model_pred = vip( model=model, latent_model_input=noisy_model_input, @@ -946,9 +963,6 @@ def run(model_input, model_kwargs, prof): **model_kwargs, ) - model_pred = torch.chunk(model_pred, 2, dim=1)[0] - - # Get the target for loss depending on the prediction type if args.prediction_type is not None: # set prediction_type of scheduler if defined @@ -966,8 +980,19 @@ def run(model_input, model_kwargs, prof): else: raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + mask = model_kwargs.get('attention_mask', None) + b, c, _, _, _ = model_pred.shape + if mask is not None: + mask = mask.unsqueeze(1).repeat(1, c, 1, 1, 1).float() # b t h w -> b c t h w + mask = mask.reshape(b, -1) if args.snr_gamma is None: - loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + # model_pred: b c t h w, attention_mask: b t h w + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.reshape(b, -1) + if mask is not None: + loss = (loss * mask).sum() / mask.sum() # mean loss on unpad patches + else: + loss = loss.mean() else: # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. # Since we predict the noise instead of x_0, the original formulation is slightly changed. @@ -980,11 +1005,13 @@ def run(model_input, model_kwargs, prof): mse_loss_weights = mse_loss_weights / snr elif noise_scheduler.config.prediction_type == "v_prediction": mse_loss_weights = mse_loss_weights / (snr + 1) - loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") - loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights - loss = loss.mean() - + loss = loss.reshape(b, -1) + mse_loss_weights = mse_loss_weights.reshape(b, 1) + if mask is not None: + loss = (loss * mask * mse_loss_weights).sum() / mask.sum() # mean loss on unpad patches + else: + loss = (loss * mse_loss_weights).mean() # Gather the losses across all processes for logging (if we use distributed training). avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() @@ -1003,6 +1030,15 @@ def run(model_input, model_kwargs, prof): sync_gradients_info(loss) if accelerator.is_main_process: + for tracker in accelerator.trackers: + if tracker.name == "wandb": + if progress_info.global_step % args.checkpointing_steps != 0: + if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) + elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): + tracker.log( + {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) if progress_info.global_step % args.checkpointing_steps == 0: @@ -1021,7 +1057,7 @@ def run(model_input, model_kwargs, prof): global_step=progress_info.global_step, ) - if args.use_ema: + if args.use_ema and npu_config is None: # Store the UNet parameters temporarily and load the EMA parameters to perform inference. ema_vip.store(vip.parameters()) ema_vip.copy_to(vip.parameters()) @@ -1049,52 +1085,42 @@ def run(model_input, model_kwargs, prof): return loss def train_one_step(step_, data_item_, prof_=None): - # NOTE checking params update - # if step_ > 1: - # print("Comparing model parameters before and after training step:") - # for name, param in vip.named_parameters(): - # if not torch.equal(param, initial_params[name]): - # print(f"Parameter {name} has changed.") - # initial_params[name] = param.clone() - # else: - # print(f"Parameter {name} has not changed!") - train_loss = 0.0 x, attn_mask, input_ids, cond_mask, clip_data = data_item_ + # assert torch.all(attn_mask.bool()), 'must all visible' # Sample noise that we'll add to the latents - - if not args.multi_scale: - assert torch.all(attn_mask) + # import ipdb;ipdb.set_trace() + if args.group_frame or args.group_resolution: + if not torch.all(torch.any(attn_mask.flatten(-2), dim=-1)): + each_latent_frame = torch.any(attn_mask.flatten(-2), dim=-1).int().sum(-1).tolist() + # logger.info(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask ' + # f'each_latent_frame: {each_latent_frame}') + print(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask ' + f'each_latent_frame: {each_latent_frame}') assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' - x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+image_num H W - clip_data = clip_data.to(accelerator.device) # B T+image_num C H W + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+num_images H W, 16 + 4 + clip_data = clip_data.to(accelerator.device, dtype=weight_dtype) # B T+image_num C H W + + attn_mask = attn_mask.to(accelerator.device) # B T+num_images H W + input_ids = input_ids.to(accelerator.device) # B 1+num_images L + cond_mask = cond_mask.to(accelerator.device) # B 1+num_images L - attn_mask = attn_mask.to(accelerator.device) # B T+image_num H W - input_ids = input_ids.to(accelerator.device) # B 1+image_num L - cond_mask = cond_mask.to(accelerator.device) # B 1+image_num L - # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + # if accelerator.process_index == 0: + # logger.info(f'rank: {accelerator.process_index}, x: {x.shape}, attn_mask: {attn_mask.shape}') with torch.no_grad(): # import ipdb;ipdb.set_trace() # use for loop to avoid OOM, because T5 is too huge... - B, N, L = input_ids.shape # B 1+image_num L + B, N, L = input_ids.shape # B 1+num_images L # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D # use batch inference input_ids_ = input_ids.reshape(-1, L) cond_mask_ = cond_mask.reshape(-1, L) - cond = text_enc(input_ids_, cond_mask_) + cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D + cond = cond.reshape(B, N, L, -1) - assert not torch.any(torch.isnan(cond)), 'after text_enc' - - cond = cond.reshape(B, N, L, -1) # B 1+image_num L D - - clip_data = rearrange(clip_data, 'b t c h w -> (b t) c h w') # B T+image_num C H W -> B * (T+image_num) C H W - # NOTE using the second last layer of CLIP or last layer of DINO as clip feature - clip_feature = get_clip_feature(clip_data, image_encoder, args.image_encoder_type) # B * (T+image_num) D - clip_feature = clip_feature[:, 1:] # drop cls token - clip_feature_height = clip_feature_width = int(sqrt(clip_feature.shape[1])) - clip_feature = rearrange(clip_feature, '(b t) (h w) c -> b t h w c', b=B, h=clip_feature_height, w=clip_feature_width) # B T+image_num H W D + clip_feature = get_clip_feature(clip_data, image_encoder) # Map input images to latent space + normalize latents if args.use_image_num == 0: @@ -1107,15 +1133,26 @@ def train_one_step(step_, data_item_, prof_=None): images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) x = torch.cat([videos, images], dim=2) # b c 17+4, h, w - assert not torch.any(torch.isnan(x)), 'after vae' - - with accelerator.accumulate(vip): - x = x.to(weight_dtype) - assert not torch.any(torch.isnan(x)), 'after vae' - model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, - encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, - clip_feature=clip_feature) - run(x, model_kwargs, prof_) + if npu_config is not None and get_sequence_parallel_state(): + x, cond, attn_mask, cond_mask, use_image_num = prepare_parallel_data(x, cond, attn_mask, cond_mask, + args.use_image_num) + for iter in range(args.train_batch_size * args.sp_size // args.train_sp_batch_size): + with accelerator.accumulate(model): + st_idx = iter * args.train_sp_batch_size + ed_idx = (iter + 1) * args.train_sp_batch_size + model_kwargs = dict(encoder_hidden_states=cond[st_idx: ed_idx], + attention_mask=attn_mask[st_idx: ed_idx], + encoder_attention_mask=cond_mask[st_idx: ed_idx], use_image_num=use_image_num) + run(x[st_idx: ed_idx], model_kwargs, prof_) + + else: + with accelerator.accumulate(model): + assert not torch.any(torch.isnan(x)), 'after vae' + x = x.to(weight_dtype) + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, + clip_feature=clip_feature) + run(x, model_kwargs, prof_) if progress_info.global_step >= args.max_train_steps: return True @@ -1148,9 +1185,34 @@ def train_all_epoch(prof_=None): if train_one_step(step, data_item, prof_): break - train_all_epoch() + if step >= 2 and torch_npu is not None and npu_config is not None: + npu_config.free_mm() + + if npu_config is not None and npu_config.on_npu and npu_config.profiling: + experimental_config = torch_npu.profiler._ExperimentalConfig( + profiler_level=torch_npu.profiler.ProfilerLevel.Level1, + aic_metrics=torch_npu.profiler.AiCMetrics.PipeUtilization + ) + profile_output_path = f"/home/image_data/npu_profiling_t2v/{os.getenv('PROJECT_NAME', 'local')}" + os.makedirs(profile_output_path, exist_ok=True) + + with torch_npu.profiler.profile( + activities=[torch_npu.profiler.ProfilerActivity.NPU, torch_npu.profiler.ProfilerActivity.CPU], + with_stack=True, + record_shapes=True, + profile_memory=True, + experimental_config=experimental_config, + schedule=torch_npu.profiler.schedule(wait=npu_config.profiling_step, warmup=0, active=1, repeat=1, + skip_first=0), + on_trace_ready=torch_npu.profiler.tensorboard_trace_handler(f"{profile_output_path}/") + ) as prof: + train_all_epoch(prof) + else: + train_all_epoch() accelerator.wait_for_everyone() accelerator.end_training() + if npu_config is not None and get_sequence_parallel_state(): + destroy_sequence_parallel_group() if __name__ == "__main__": @@ -1161,22 +1223,24 @@ def train_all_epoch(prof_=None): parser.add_argument("--video_data", type=str, required='') parser.add_argument("--image_data", type=str, default='') parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--train_fps", type=int, default=24) + parser.add_argument("--speed_factor", type=float, default=1.5) parser.add_argument("--num_frames", type=int, default=65) parser.add_argument("--max_height", type=int, default=320) parser.add_argument("--max_width", type=int, default=240) - - parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--use_image_num", type=int, default=0) parser.add_argument("--model_max_length", type=int, default=512) - parser.add_argument("--multi_scale", action="store_true") parser.add_argument('--cfg', type=float, default=0.1) parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + parser.add_argument("--group_frame", action="store_true") + parser.add_argument("--group_resolution", action="store_true") # text encoder & vae & diffusion model parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") parser.add_argument('--enable_8bit_t5', action='store_true') - parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--tile_overlap_factor', type=float, default=0.125) parser.add_argument('--enable_tiling', action='store_true') parser.add_argument("--compress_kv", action="store_true") parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") @@ -1191,19 +1255,19 @@ def train_all_epoch(prof_=None): parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') parser.add_argument("--cache_dir", type=str, default='./cache_dir') parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument('--enable_stable_fp32', action='store_true') parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") # diffusion setting - parser.add_argument("--zero_terminal_snr", action="store_true", help="Whether to zero the terminal SNR.") parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") parser.add_argument("--ema_start_step", type=int, default=0) - parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--noise_offset", type=float, default=0.02, help="The scale of noise offset.") parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") # validation & logs parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument('--guidance_scale', type=float, default=2.5) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") @@ -1276,17 +1340,18 @@ def train_all_epoch(prof_=None): ) parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel") + parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training") + parser.add_argument("--ema_decay", type=float, default=0.999) # inpaint dataset parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode parser.add_argument("--default_text_ratio", type=float, default=0.1) parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") - parser.add_argument("--image_encoder_type", type=str, default='clip', choices=['clip', 'dino']) parser.add_argument("--image_encoder_name", type=str, default='laion/CLIP-ViT-H-14-laion2B-s32B-b79K') parser.add_argument("--pretrained_vip_adapter_path", type=str, default=None) parser.add_argument("--clear_video_ratio", type=float, default=0.0) - parser.add_argument("--use_image_num", type=int, default=0) parser.add_argument("--vip_num_attention_heads", type=int, default=8) diff --git a/opensora/train/train_videoip_bak.py b/opensora/train/train_videoip_bak.py new file mode 100644 index 000000000..0f230df88 --- /dev/null +++ b/opensora/train/train_videoip_bak.py @@ -0,0 +1,1294 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. + +# This source code is licensed under the license found in the +# LICENSE file in the root directory of this source tree. + +""" +A minimal training script for DiT using PyTorch DDP. +""" +import argparse +from email.mime import image +import logging +import math +from mimetypes import init +import os +from selectors import EpollSelector +import shutil +from pathlib import Path +from typing import Optional +import gc +import numpy as np +from einops import rearrange +from tqdm import tqdm +import itertools + +from opensora.models.ae.videobase.modules import attention + +import time +from dataclasses import field, dataclass +from torch.utils.data import DataLoader +from copy import deepcopy +import accelerate +import torch +import torch.nn as nn +from torch.nn import functional as F +import transformers +from accelerate import Accelerator +from accelerate.logging import get_logger +from accelerate.utils import DistributedType, ProjectConfiguration, set_seed +from huggingface_hub import create_repo +from packaging import version +from tqdm.auto import tqdm +from transformers import HfArgumentParser, TrainingArguments, AutoTokenizer +from math import sqrt + +import diffusers +from diffusers import DDPMScheduler, PNDMScheduler, DPMSolverMultistepScheduler +from diffusers.optimization import get_scheduler +from diffusers.training_utils import EMAModel, compute_snr +from diffusers.utils import check_min_version, is_wandb_available +from diffusers.models.modeling_utils import ModelMixin +from diffusers.configuration_utils import ConfigMixin, register_to_config + +from opensora.dataset import getdataset, ae_denorm +from opensora.models.ae import getae, getae_wrapper +from opensora.models.ae.videobase import CausalVQVAEModelWrapper, CausalVAEModelWrapper +from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.text_encoder import get_text_enc, get_text_warpper +from opensora.utils.dataset_utils import VideoIP_Collate as Collate +from opensora.models.ae import ae_stride_config, ae_channel_config +from opensora.models.diffusion import Diffusion_models, Diffusion_models_class +from opensora.sample.pipeline_opensora import OpenSoraPipeline + + +# for validation +from PIL import Image +from torchvision import transforms +from torchvision.transforms import Lambda +from transformers import CLIPVisionModelWithProjection, AutoModel, AutoImageProcessor, CLIPImageProcessor +from opensora.dataset.transform import ToTensorVideo, CenterCropResizeVideo, TemporalRandomCrop, LongSideResizeVideo, SpatialStrideCropVideo +from opensora.sample.pipeline_opensora import OpenSoraPipeline +from opensora.sample.pipeline_for_vip import hacked_pipeline_call_for_vip + +from opensora.models.diffusion.latte.videoip import VideoIPAdapter, VideoIPAttnProcessor +from opensora.models.diffusion.latte.modeling_for_vip import hacked_forward_for_vip, hook_forward_fn, hook_backward_fn +from opensora.models.diffusion.latte.modules import BasicTransformerBlock + +import glob +from torchvision.utils import save_image +import imageio + +# Will error if the minimal version of diffusers is not installed. Remove at your own risks. +check_min_version("0.24.0") +logger = get_logger(__name__) + +def get_clip_feature(clip_data, image_encoder, image_encoder_type='clip'): + if image_encoder_type == 'clip': + clip_feature = image_encoder(clip_data, output_hidden_states=True).hidden_states[-2] # B * (T+image_num) N D + elif image_encoder_type == 'dino': + clip_feature = image_encoder(clip_data).last_hidden_state # B * (T+image_num) N D + # clip_feature = image_encoder(clip_data, output_hidden_states=True).hidden_states[-2] # B * (T+image_num) N D + return clip_feature + + +class VIPNet(ModelMixin, ConfigMixin): + @register_to_config + def __init__( + self, + image_encoder_type='dino', + cross_attention_dim=1152, + num_tokens=272, + vip_num_attention_heads=16, + vip_attention_head_dim=64, + vip_num_attention_layers=2, + attention_mode='xformers', + gradient_checkpointing=False, + vae_scale_factor_t=4, + video_length=17, + use_rope=False, + rope_scaling=None, + attn_proc_type_dict={}, + ): + super().__init__() + + self.image_encoder_type = image_encoder_type + self.cross_attention_dim = cross_attention_dim + self.num_tokens = num_tokens + + self.attention_mode = attention_mode + self.use_rope = use_rope + self.rope_scaling = rope_scaling + + + self.vip_adapter = VideoIPAdapter( + image_encoder_type=image_encoder_type, + cross_attention_dim=cross_attention_dim, + num_attention_heads=vip_num_attention_heads, + attention_head_dim=vip_attention_head_dim, + num_attention_layers=vip_num_attention_layers, + use_rope=use_rope, + rope_scaling=rope_scaling, + attention_mode=attention_mode, + gradient_checkpointing=gradient_checkpointing, + vae_scale_factor_t=vae_scale_factor_t, + video_length=video_length, + ) + + + self.attn_procs = {} + # because nn.ModuleDict will raise (KeyError: 'module name can\'t contain ".", got: transformer_blocks.0.attn2.processor'), so we trun to use nn.ModuleList + for name, attn_proc_type in attn_proc_type_dict.items(): + if attn_proc_type == "VideoIPAttnProcessor": + self.attn_procs[name] = VideoIPAttnProcessor( + dim=cross_attention_dim, + attention_mode=attention_mode, + use_rope=use_rope, + rope_scaling=rope_scaling, + num_vip_tokens=num_tokens, + ) + self.adapter_modules = torch.nn.ModuleList(self.attn_procs.values()) + + # if pretrained_vip_adapter_path is not None: + # self.load_vip_adapter(pretrained_vip_adapter_path) + + def set_vip_adapter(self, model, init_from_original_attn_processor): + # init adapter modules + model_sd = model.state_dict() + attn_procs = {} + print("set vip adapter...") + for name, attn_processor in model.attn_processors.items(): + if name.endswith(".attn2.processor"): + new_attn_processor = self.attn_procs[name] + if init_from_original_attn_processor: + print(f"init from original attn processor {name}...") + layer_name = name.split(".processor")[0] + weights = { + "to_k_vip.weight": model_sd[layer_name + ".to_k.weight"], + "to_v_vip.weight": model_sd[layer_name + ".to_v.weight"], + } + new_attn_processor.load_state_dict(weights, strict=False) + attn_procs[name] = new_attn_processor + else: + attn_procs[name] = attn_processor + + model.set_attn_processor(attn_procs) + + @torch.no_grad() + def get_image_embeds(self, images, image_processor, image_encoder, transform, device, weight_dtype=torch.float32): + if not isinstance(images, list): + images = [images] + images = [Image.open(image).convert("RGB") for image in images] + images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in images] # 1 H W C + images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in images]) # resize, 1 C H W + + images = image_processor(images=images, return_tensors="pt").pixel_values # 1 C H W + images = images.to(device=device, dtype=weight_dtype) + negative_images = torch.zeros_like(images, device=device, dtype=weight_dtype) + # NOTE using the second last layer of image encoder + images = get_clip_feature(images, image_encoder, image_encoder_type=self.image_encoder_type) # 1 N D + images = images[:, 1:] # drop cls token + negative_images = get_clip_feature(negative_images, image_encoder, image_encoder_type=self.image_encoder_type) + negative_images = negative_images[:, 1:] + + height = width = int(sqrt(images.shape[1])) + images = rearrange(images, '1 (h w) c -> c 1 h w', h=height, w=width) + negative_images = rearrange(negative_images, '1 (h w) c -> c 1 h w', h=height, w=width) + images = images.unsqueeze(0) # 1 C 1 H W + negative_images = negative_images.unsqueeze(0) + + vip_out = self.vip_adapter(hidden_states=images, use_image_num=0) + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N + + negative_vip_out = self.vip_adapter(hidden_states=negative_images, use_image_num=0) + negative_vip_tokens, negative_vip_cond_mask = negative_vip_out.hidden_states, negative_vip_out.vip_cond_mask + + return vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask + + @torch.no_grad() + def get_video_embeds(self, condition_images, num_frames, image_processor, image_encoder, transform, device, weight_dtype=torch.float32): + if len(condition_images) == 1: + condition_images_indices = [0] + elif len(condition_images) == 2: + condition_images_indices = [0, -1] + condition_images = [Image.open(image).convert("RGB") for image in condition_images] + condition_images = [torch.from_numpy(np.copy(np.array(image))).unsqueeze(0) for image in condition_images] # F [1 H W C] + condition_images = torch.cat([transform(image.permute(0, 3, 1, 2).float()).to(torch.uint8) for image in condition_images]) # resize, [F C H W] + + condition_images = image_processor(images=condition_images, return_tensors="pt").pixel_values # F C H W + condition_images = condition_images.to(device=device, dtype=weight_dtype) + _, C, H, W = condition_images.shape + video = torch.zeros([num_frames, C, H, W], device=device, dtype=weight_dtype) + video[condition_images_indices] = condition_images + negative_video = torch.zeros_like(video, device=device, dtype=weight_dtype) + # using the second last layer of image encoder + video = get_clip_feature(video, image_encoder, image_encoder_type=self.image_encoder_type) + video = video[:, 1:] # drop cls token + negative_video = get_clip_feature(negative_video, image_encoder, image_encoder_type=self.image_encoder_type) + negative_video = negative_video[:, 1:] + + height = width = int(sqrt(video.shape[1])) + video = rearrange(video, 't (h w) c -> c t h w', h=height, w=width) + negative_video = rearrange(negative_video, 't (h w) c -> c t h w', h=height, w=width) + + video = video.unsqueeze(0) # 1 C F H W + negative_video = negative_video.unsqueeze(0) + + vip_out = self.vip_adapter(hidden_states=video, use_image_num=0) + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask # 1 1 N D, 1 1 N + + negative_vip_out = self.vip_adapter(hidden_states=negative_video, use_image_num=0) + negative_vip_tokens, negative_vip_cond_mask = negative_vip_out.hidden_states, negative_vip_out.vip_cond_mask + + return vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask + + + def forward(self, model, latent_model_input, timestep, **model_kwargs): + enable_temporal_attentions = True if args.num_frames > 1 else False + + encoder_hidden_states = model_kwargs.pop('encoder_hidden_states', None) + encoder_attention_mask = model_kwargs.pop('encoder_attention_mask', None) + clip_feature = model_kwargs.pop('clip_feature', None) + use_image_num = model_kwargs.pop('use_image_num', 0) + assert encoder_hidden_states is not None and encoder_attention_mask is not None and clip_feature is not None, "VIPNet requires encoder_hidden_states, encoder_attention_mask and clip_feature" + hidden_states = rearrange(clip_feature, 'b t h w c -> b c t h w') + + vip_out = self.vip_adapter(hidden_states=hidden_states, use_image_num=use_image_num) # B D T+image_num H W -> B 1+image_num N D + vip_tokens, vip_cond_mask = vip_out.hidden_states, vip_out.vip_cond_mask + + model_pred = model( + hidden_states=latent_model_input, + timestep=timestep, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + vip_hidden_states=vip_tokens, + vip_attention_mask=vip_cond_mask, + use_image_num=use_image_num, + enable_temporal_attentions=enable_temporal_attentions, + **model_kwargs + )[0] + + return model_pred + + +@torch.inference_mode() +def log_validation( + args, + model, + vip, + vae, + text_encoder, + tokenizer, + image_processor, + image_encoder, + accelerator, + weight_dtype, + global_step, + ema=False +): + + + validation_dir = args.validation_dir if args.validation_dir is not None else "./validation" + prompt_file = os.path.join(validation_dir, "prompt.txt") + + with open(prompt_file, 'r') as f: + validation_prompt = f.readlines() + + index = 0 + validation_images_list = [] + while True: + temp = glob.glob(os.path.join(validation_dir, f"*_{index:04d}*.png")) + print(temp) + if len(temp) > 0: + validation_images_list.append(sorted(temp)) + index += 1 + else: + break + + resize_transform = transforms.Compose( + [CenterCropResizeVideo((args.max_height, args.max_width))] + ) + logger.info(f"Running {'normal' if not ema else 'ema'} validation....\n") + + vip = accelerator.unwrap_model(vip) + + model.eval() + vip.eval() + + scheduler = PNDMScheduler() + pipeline = OpenSoraPipeline( + vae=vae, + text_encoder=text_encoder, + tokenizer=tokenizer, + scheduler=scheduler, + transformer=model + ).to(device=accelerator.device) + + + pipeline.__call__ = hacked_pipeline_call_for_vip.__get__(pipeline, OpenSoraPipeline) + + videos = [] + gen_img = False + for prompt, images in zip(validation_prompt, validation_images_list): + if not isinstance(images, list): + images = [images] + logger.info('Processing the ({}) prompt and the images ({})'.format(prompt, images)) + if args.num_frames != 1: + if len(images) == 1 and images[0].split('/')[-1].split('_')[0] == 'img': + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( + images=images, + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + gen_img = True + else: + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_video_embeds( + condition_images=images, + num_frames=args.num_frames, + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + else: + vip_tokens, vip_cond_mask, negative_vip_tokens, negative_vip_cond_mask = vip.get_image_embeds( + images=images[0], # only using first image + image_processor=image_processor, + image_encoder=image_encoder, + transform=resize_transform, + device=accelerator.device, + weight_dtype=torch.float32 + ) + gen_img = True + + video = pipeline.__call__( + prompt=prompt, + negative_prompt="", + vip_tokens=vip_tokens, + vip_attention_mask=vip_cond_mask, + negative_vip_tokens=negative_vip_tokens, + negative_vip_attention_mask=negative_vip_cond_mask, + num_frames=(1 if gen_img else args.num_frames), + height=args.max_height, + width=args.max_width, + num_inference_steps=args.num_sampling_steps, + guidance_scale=args.guidance_scale, + num_images_per_prompt=1, + mask_feature=True, + device=accelerator.device, + max_squence_length=args.model_max_length, + ).images + videos.append(video[0]) + gen_img = False + # import ipdb;ipdb.set_trace() + + # Save the generated videos + save_dir = os.path.join(args.output_dir, f"val_{global_step:09d}" if not ema else f"val_ema_{global_step:09d}") + os.makedirs(save_dir, exist_ok=True) + + for idx, video in enumerate(videos): + + if video.shape[0] == 1: # image + ext = 'png' + Image.fromarray(video[0].cpu().numpy()).save(os.path.join(save_dir, f'{idx}.{ext}')) + else: # video + ext = 'mp4' + imageio.mimwrite( + os.path.join(save_dir, f'{idx}.{ext}'), video, fps=24, quality=6) # highest quality is 10, lowest is 0 + + + # for wandb + resize_transform = transforms.Compose( + [CenterCropResizeVideo((args.max_height // 4, args.max_width // 4))] + ) + + videos = [resize_transform(video.permute(0, 3, 1, 2)) for video in videos] + + + for tracker in accelerator.trackers: + if tracker.name == "wandb": + import wandb + logs = {} + logs[f"{'ema_' if ema else ''}validation_videos"] = [] + logs[f"{'ema_' if ema else ''}validation_images"] = [] + for i, (video, prompt) in enumerate(zip(videos, validation_prompt)): + if video.shape[0] == 1: # image + logs[f"{'ema_' if ema else ''}validation_images"].append(wandb.Image(video[0], caption=f"{i}: {prompt}")) + else: # video + logs[f"{'ema_' if ema else ''}validation_videos"].append(wandb.Video(video, caption=f"{i}: {prompt}", fps=24)) + + tracker.log(logs, step=global_step) + + print("delete validation pipeline...") + del pipeline + gc.collect() + torch.cuda.empty_cache() + + +class ProgressInfo: + def __init__(self, global_step, train_loss=0.0): + self.global_step = global_step + self.train_loss = train_loss + + +################################################################################# +# Training Loop # +################################################################################# + +def main(args): + + logging_dir = Path(args.output_dir, args.logging_dir) + + accelerator_project_config = ProjectConfiguration(project_dir=args.output_dir, logging_dir=logging_dir) + + accelerator = Accelerator( + gradient_accumulation_steps=args.gradient_accumulation_steps, + mixed_precision=args.mixed_precision, + log_with=args.report_to, + project_config=accelerator_project_config, + ) + + if accelerator.is_main_process: + print("--------------------------training args--------------------------") + print(args) + print("-----------------------------------------------------------------") + + if args.report_to == "wandb": + if not is_wandb_available(): + raise ImportError("Make sure to install wandb if you want to use it for logging during training.") + + # Make one log on every process with the configuration for debugging. + logging.basicConfig( + format="%(asctime)s - %(levelname)s - %(name)s - %(message)s", + datefmt="%m/%d/%Y %H:%M:%S", + level=logging.INFO, + ) + logger.info(accelerator.state, main_process_only=False) + if accelerator.is_local_main_process: + transformers.utils.logging.set_verbosity_warning() + diffusers.utils.logging.set_verbosity_info() + else: + transformers.utils.logging.set_verbosity_error() + diffusers.utils.logging.set_verbosity_error() + + # If passed along, set the training seed now. + if args.seed is not None: + set_seed(args.seed) + + # Handle the repository creation + if accelerator.is_main_process: + if args.output_dir is not None: + os.makedirs(args.output_dir, exist_ok=True) + + # For mixed precision training we cast all non-trainable weigths to half-precision + # as these weights are only used for inference, keeping weights in full precision is not required. + weight_dtype = torch.float32 + if accelerator.mixed_precision == "fp16": + weight_dtype = torch.float16 + elif accelerator.mixed_precision == "bf16": + weight_dtype = torch.bfloat16 + + # Create model: + kwargs = {} + ae = getae_wrapper(args.ae)(args.ae_path, cache_dir=args.cache_dir, **kwargs).eval() + if args.enable_tiling: + ae.vae.enable_tiling() + ae.vae.tile_overlap_factor = args.tile_overlap_factor + + kwargs = {'load_in_8bit': args.enable_8bit_t5, 'torch_dtype': weight_dtype, 'low_cpu_mem_usage': True} + text_enc = get_text_warpper(args.text_encoder_name)(args, **kwargs).eval() + + ae_stride_t, ae_stride_h, ae_stride_w = ae_stride_config[args.ae] + ae.vae_scale_factor = (ae_stride_t, ae_stride_h, ae_stride_w) + assert ae_stride_h == ae_stride_w, f"Support only ae_stride_h == ae_stride_w now, but found ae_stride_h ({ae_stride_h}), ae_stride_w ({ae_stride_w})" + args.ae_stride_t, args.ae_stride_h, args.ae_stride_w = ae_stride_t, ae_stride_h, ae_stride_w + args.ae_stride = args.ae_stride_h + patch_size = args.model[-3:] + patch_size_t, patch_size_h, patch_size_w = int(patch_size[0]), int(patch_size[1]), int(patch_size[2]) + args.patch_size = patch_size_h + args.patch_size_t, args.patch_size_h, args.patch_size_w = patch_size_t, patch_size_h, patch_size_w + assert patch_size_h == patch_size_w, f"Support only patch_size_h == patch_size_w now, but found patch_size_h ({patch_size_h}), patch_size_w ({patch_size_w})" + # assert args.num_frames % ae_stride_t == 0, f"Num_frames must be divisible by ae_stride_t, but found num_frames ({args.num_frames}), ae_stride_t ({ae_stride_t})." + assert args.max_height % ae_stride_h == 0, f"Height must be divisible by ae_stride_h, but found Height ({args.max_height}), ae_stride_h ({ae_stride_h})." + assert args.max_width % ae_stride_h == 0, f"Width size must be divisible by ae_stride_h, but found Width ({args.max_width}), ae_stride_h ({ae_stride_h})." + + args.stride_t = ae_stride_t * patch_size_t + args.stride = ae_stride_h * patch_size_h + latent_size = (args.max_height // ae_stride_h, args.max_width // ae_stride_w) + ae.latent_size = latent_size + + if args.num_frames % 2 == 1: + args.latent_size_t = latent_size_t = args.num_frames // ae_stride_t + 1 + else: + latent_size_t = args.num_frames // ae_stride_t + # when training video ip adapter, we use t2v model + model = Diffusion_models[args.model]( + in_channels=ae_channel_config[args.ae], + out_channels=ae_channel_config[args.ae] * 2, # 因为要加载预训练权重,所以这里out_channels仍然设置为2倍 + # caption_channels=4096, + # cross_attention_dim=1152, + attention_bias=True, + sample_size=latent_size, + sample_size_t=latent_size_t, + num_vector_embeds=None, + activation_fn="gelu-approximate", + num_embeds_ada_norm=1000, + use_linear_projection=False, + only_cross_attention=False, + double_self_attention=False, + upcast_attention=False, + # norm_type="ada_norm_single", + norm_elementwise_affine=False, + norm_eps=1e-6, + attention_type='default', + attention_mode=args.attention_mode, + downsampler=args.downsampler, + compress_kv_factor=args.compress_kv_factor, + use_rope=args.use_rope, + model_max_length=args.model_max_length, + ) + model.gradient_checkpointing = args.gradient_checkpointing + + # NOTE replace forward for VIP + model.forward = hacked_forward_for_vip.__get__(model, Diffusion_models[args.model]) + + # # use pretrained model? + # NOTE when using inpaint model + # if args.pretrained: + # model.custom_load_state_dict(args.pretrained) + + if args.pretrained: + model_state_dict = model.state_dict() + if 'safetensors' in args.pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(args.pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(args.pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + logger.info(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + logger.info(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + + # Freeze main model + ae.vae.requires_grad_(False) + text_enc.requires_grad_(False) + model.requires_grad_(False) + + # load image encoder + if args.image_encoder_type == 'clip': + print("using (clip) as image encoder!") + # self.image_encoder = CLIPVisionModelWithProjection.from_pretrained("laion/CLIP-ViT-H-14-laion2B-s32B-b79K", cache_dir="/storage/cache_dir") + image_encoder = CLIPVisionModelWithProjection.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + elif args.image_encoder_type == 'dino': + print("using (dinov2) as image encoder!") + # self.image_encoder = AutoModel.from_pretrained("facebook/dinov2-giant", cache_dir="/storage/cache_dir") + image_encoder = AutoModel.from_pretrained(args.image_encoder_name, cache_dir=args.cache_dir) + else: + raise NotImplementedError + + image_encoder.requires_grad_(False) + + attn_proc_type_dict = {} + for name, attn_processor in model.attn_processors.items(): + # replace all attn2.processor with VideoIPAttnProcessor + if name.endswith('.attn2.processor'): + attn_proc_type_dict[name] = 'VideoIPAttnProcessor' + else: + attn_proc_type_dict[name] = attn_processor.__class__.__name__ + + vip = VIPNet( + image_encoder_type=args.image_encoder_type, + cross_attention_dim=1152, + num_tokens=272, # NOTE should be modified + vip_num_attention_heads=args.vip_num_attention_heads, # for dinov2 + vip_attention_head_dim=64, + vip_num_attention_layers=2, + attention_mode=args.attention_mode, + gradient_checkpointing=args.gradient_checkpointing, + vae_scale_factor_t=ae_stride_t, + video_length=latent_size_t, + attn_proc_type_dict=attn_proc_type_dict, + ) + + if args.pretrained_vip_adapter_path is not None: + from safetensors.torch import load_file as safe_load + checkpoint = safe_load(os.path.join(args.pretrained_vip_adapter_path, "diffusion_pytorch_model.safetensors"), device="cpu") + vip.load_state_dict(checkpoint) + + + init_from_original_attn_processor = False if (args.pretrained_vip_adapter_path is not None or args.resume_from_checkpoint is not None) else True + vip.set_vip_adapter(model, init_from_original_attn_processor=init_from_original_attn_processor) + + # for name, module in vip.named_modules(): + # # # if isinstance(module, VideoIPAttnProcessor): + # # # module.register_full_backward_hook(hook_backward_fn) + # if isinstance(module, nn.Conv3d): + # module.register_backward_hook(hook_backward_fn) + + noise_scheduler = DDPMScheduler(rescale_betas_zero_snr=args.zero_terminal_snr) + # Move unet, vae and text_encoder to device and cast to weight_dtype + # The VAE is in float32 to avoid NaN losses. + ae.vae.to(accelerator.device, dtype=torch.float32) + # ae.vae.to(accelerator.device, dtype=weight_dtype) + text_enc.to(accelerator.device, dtype=weight_dtype) + model.to(accelerator.device, dtype=weight_dtype) + + image_encoder.to(accelerator.device, dtype=weight_dtype) + + # Create EMA for the unet. + if args.use_ema: + ema_vip = deepcopy(vip) + ema_vip = EMAModel(vip.parameters(), update_after_step=args.ema_start_step, + model_cls=VIPNet, model_config=ema_vip.config) + + # `accelerate` 0.16.0 will have better support for customized saving + if version.parse(accelerate.__version__) >= version.parse("0.16.0"): + # create custom saving & loading hooks so that `accelerator.save_state(...)` serializes in a nice format + def save_model_hook(models, weights, output_dir): + if accelerator.is_main_process: + if args.use_ema: + ema_vip.save_pretrained(os.path.join(output_dir, "model_ema")) + + for i, model in enumerate(models): + model.save_pretrained(os.path.join(output_dir, "model")) + if weights: # Don't pop if empty + # make sure to pop weight so that corresponding model is not saved again + weights.pop() + + def load_model_hook(models, input_dir): + if args.use_ema: + if os.path.exists(os.path.join(input_dir, "model_ema")): + print("loading ema model...") + load_model = EMAModel.from_pretrained(os.path.join(input_dir, "model_ema"), VIPNet) + ema_vip.load_state_dict(load_model.state_dict()) + ema_vip.to(accelerator.device) + del load_model + + + print("loading model...") + for i in range(len(models)): + + # pop models so that they are not loaded again + model = models.pop() + + # load diffusers style into model + load_model = VIPNet.from_pretrained(input_dir, subfolder="model") + model.register_to_config(**load_model.config) + + model.load_state_dict(load_model.state_dict()) + del load_model + + accelerator.register_save_state_pre_hook(save_model_hook) + accelerator.register_load_state_pre_hook(load_model_hook) + + # Enable TF32 for faster training on Ampere GPUs, + # cf https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices + if args.allow_tf32: + torch.backends.cuda.matmul.allow_tf32 = True + + if args.scale_lr: + args.learning_rate = ( + args.learning_rate * args.gradient_accumulation_steps * args.train_batch_size * accelerator.num_processes + ) + + params_to_optimize = vip.parameters() + # Optimizer creation + if not (args.optimizer.lower() == "prodigy" or args.optimizer.lower() == "adamw"): + logger.warning( + f"Unsupported choice of optimizer: {args.optimizer}.Supported optimizers include [adamW, prodigy]." + "Defaulting to adamW" + ) + args.optimizer = "adamw" + + if args.use_8bit_adam and not args.optimizer.lower() == "adamw": + logger.warning( + f"use_8bit_adam is ignored when optimizer is not set to 'AdamW'. Optimizer was " + f"set to {args.optimizer.lower()}" + ) + + if args.optimizer.lower() == "adamw": + if args.use_8bit_adam: + try: + import bitsandbytes as bnb + except ImportError: + raise ImportError( + "To use 8-bit Adam, please install the bitsandbytes library: `pip install bitsandbytes`." + ) + + optimizer_class = bnb.optim.AdamW8bit + else: + optimizer_class = torch.optim.AdamW + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + ) + + if args.optimizer.lower() == "prodigy": + try: + import prodigyopt + except ImportError: + raise ImportError("To use Prodigy, please install the prodigyopt library: `pip install prodigyopt`") + + optimizer_class = prodigyopt.Prodigy + + if args.learning_rate <= 0.1: + logger.warning( + "Learning rate is too low. When using prodigy, it's generally better to set learning rate around 1.0" + ) + + optimizer = optimizer_class( + params_to_optimize, + lr=args.learning_rate, + betas=(args.adam_beta1, args.adam_beta2), + beta3=args.prodigy_beta3, + weight_decay=args.adam_weight_decay, + eps=args.adam_epsilon, + decouple=args.prodigy_decouple, + use_bias_correction=args.prodigy_use_bias_correction, + safeguard_warmup=args.prodigy_safeguard_warmup, + ) + + # Setup data: + train_dataset = getdataset(args) + train_dataloader = torch.utils.data.DataLoader( + train_dataset, + shuffle=True, + # pin_memory=True, + collate_fn=Collate(args), + batch_size=args.train_batch_size, + num_workers=args.dataloader_num_workers, + # prefetch_factor=8 + ) + + # Scheduler and math around the number of training steps. + overrode_max_train_steps = False + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if args.max_train_steps is None: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + overrode_max_train_steps = True + + lr_scheduler = get_scheduler( + args.lr_scheduler, + optimizer=optimizer, + num_warmup_steps=args.lr_warmup_steps * args.gradient_accumulation_steps, + num_training_steps=args.max_train_steps * args.gradient_accumulation_steps, + ) + + # Prepare everything with our `accelerator`. + vip, optimizer, train_dataloader, lr_scheduler = accelerator.prepare( + vip, optimizer, train_dataloader, lr_scheduler + ) + if args.use_ema: + ema_vip.to(accelerator.device) + + # We need to recalculate our total training steps as the size of the training dataloader may have changed. + num_update_steps_per_epoch = math.ceil(len(train_dataloader) / args.gradient_accumulation_steps) + if overrode_max_train_steps: + args.max_train_steps = args.num_train_epochs * num_update_steps_per_epoch + # Afterwards we recalculate our number of training epochs + args.num_train_epochs = math.ceil(args.max_train_steps / num_update_steps_per_epoch) + + # We need to initialize the trackers we use, and also store our configuration. + # The trackers initializes automatically on the main process. + # NOTE wandb + if accelerator.is_main_process: + project_name = os.getenv('PROJECT', os.path.basename(args.output_dir)) + entity = os.getenv('ENTITY', None) + run_name = os.getenv('WANDB_NAME', None) + init_kwargs = { + "entity": entity, + "run_name": run_name, + } + accelerator.init_trackers(project_name=project_name, config=vars(args), init_kwargs=init_kwargs) + + # Train! + total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps + + logger.info("***** Running training *****") + logger.info(f" Model = {vip}") + logger.info(f" Num examples = {len(train_dataset)}") + logger.info(f" Num Epochs = {args.num_train_epochs}") + logger.info(f" Instantaneous batch size per device = {args.train_batch_size}") + logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") + logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") + logger.info(f" Total optimization steps = {args.max_train_steps}") + logger.info(f" Total trainable parameters = {sum(p.numel() for p in vip.parameters() if p.requires_grad) / 1e9} B") + global_step = 0 + first_epoch = 0 + + # NOTE checking update of params + # initial_params = {name: param.clone() for name, param in vip.named_parameters()} + + # Potentially load in the weights and states from a previous save + if args.resume_from_checkpoint: + if args.resume_from_checkpoint != "latest": + path = os.path.basename(args.resume_from_checkpoint) + else: + # Get the most recent checkpoint + dirs = os.listdir(args.output_dir) + dirs = [d for d in dirs if d.startswith("checkpoint")] + dirs = sorted(dirs, key=lambda x: int(x.split("-")[1])) + path = dirs[-1] if len(dirs) > 0 else None + + if path is None: + accelerator.print( + f"Checkpoint '{args.resume_from_checkpoint}' does not exist. Starting a new training run." + ) + args.resume_from_checkpoint = None + initial_global_step = 0 + else: + accelerator.print(f"Resuming from checkpoint {path}") + accelerator.load_state(os.path.join(args.output_dir, path)) + global_step = int(path.split("-")[1]) + + initial_global_step = global_step + first_epoch = global_step // num_update_steps_per_epoch + + + else: + initial_global_step = 0 + + progress_bar = tqdm( + range(0, args.max_train_steps), + initial=initial_global_step, + desc="Steps", + # Only show the progress bar once on each machine. + disable=not accelerator.is_local_main_process, + ) + progress_info = ProgressInfo(global_step, train_loss=0.0) + + def sync_gradients_info(loss): + # Checks if the accelerator has performed an optimization step behind the scenes + if args.use_ema: + ema_vip.step(vip.parameters()) + progress_bar.update(1) + progress_info.global_step += 1 + end_time = time.time() + accelerator.log({"train_loss": progress_info.train_loss}, step=progress_info.global_step) + progress_info.train_loss = 0.0 + + # DeepSpeed requires saving weights on every device; saving weights only on the main process would cause issues. + if accelerator.distributed_type == DistributedType.DEEPSPEED or accelerator.is_main_process: + if progress_info.global_step % args.checkpointing_steps == 0: + # _before_ saving state, check if this save would set us over the `checkpoints_total_limit` + if accelerator.is_main_process and args.checkpoints_total_limit is not None: + checkpoints = os.listdir(args.output_dir) + checkpoints = [d for d in checkpoints if d.startswith("checkpoint")] + checkpoints = sorted(checkpoints, key=lambda x: int(x.split("-")[1])) + + # before we save the new checkpoint, we need to have at _most_ `checkpoints_total_limit - 1` checkpoints + if len(checkpoints) >= args.checkpoints_total_limit: + num_to_remove = len(checkpoints) - args.checkpoints_total_limit + 1 + removing_checkpoints = checkpoints[0:num_to_remove] + + logger.info( + f"{len(checkpoints)} checkpoints already exist, removing {len(removing_checkpoints)} checkpoints" + ) + logger.info(f"removing checkpoints: {', '.join(removing_checkpoints)}") + + for removing_checkpoint in removing_checkpoints: + removing_checkpoint = os.path.join(args.output_dir, removing_checkpoint) + shutil.rmtree(removing_checkpoint) + + save_path = os.path.join(args.output_dir, f"checkpoint-{progress_info.global_step}") + accelerator.save_state(save_path) + logger.info(f"Saved state to {save_path}") + + logs = {"step_loss": loss.detach().item(), "lr": lr_scheduler.get_last_lr()[0]} + progress_bar.set_postfix(**logs) + + def run(model_input, model_kwargs, prof): + + vip.train() + model.train() + + global start_time + start_time = time.time() + + noise = torch.randn_like(model_input) + if args.noise_offset: + # https://www.crosslabs.org//blog/diffusion-with-offset-noise + noise += args.noise_offset * torch.randn((model_input.shape[0], model_input.shape[1], 1, 1, 1), + device=model_input.device) + + bsz = model_input.shape[0] + # Sample a random timestep for each image without bias. + timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) + + # Add noise to the model input according to the noise magnitude at each timestep + # (this is the forward diffusion process) + + noisy_model_input = noise_scheduler.add_noise(model_input, noise, timesteps) + + model_pred = vip( + model=model, + latent_model_input=noisy_model_input, + timestep=timesteps, + **model_kwargs, + ) + + model_pred = torch.chunk(model_pred, 2, dim=1)[0] + + + # Get the target for loss depending on the prediction type + if args.prediction_type is not None: + # set prediction_type of scheduler if defined + noise_scheduler.register_to_config(prediction_type=args.prediction_type) + + if noise_scheduler.config.prediction_type == "epsilon": + target = noise + elif noise_scheduler.config.prediction_type == "v_prediction": + target = noise_scheduler.get_velocity(model_input, noise, timesteps) + elif noise_scheduler.config.prediction_type == "sample": + # We set the target to latents here, but the model_pred will return the noise sample prediction. + target = model_input + # We will have to subtract the noise residual from the prediction to get the target sample. + model_pred = model_pred - noise + else: + raise ValueError(f"Unknown prediction type {noise_scheduler.config.prediction_type}") + + if args.snr_gamma is None: + loss = F.mse_loss(model_pred.float(), target.float(), reduction="mean") + else: + # Compute loss-weights as per Section 3.4 of https://arxiv.org/abs/2303.09556. + # Since we predict the noise instead of x_0, the original formulation is slightly changed. + # This is discussed in Section 4.2 of the same paper. + snr = compute_snr(noise_scheduler, timesteps) + mse_loss_weights = torch.stack([snr, args.snr_gamma * torch.ones_like(timesteps)], dim=1).min( + dim=1 + )[0] + if noise_scheduler.config.prediction_type == "epsilon": + mse_loss_weights = mse_loss_weights / snr + elif noise_scheduler.config.prediction_type == "v_prediction": + mse_loss_weights = mse_loss_weights / (snr + 1) + + loss = F.mse_loss(model_pred.float(), target.float(), reduction="none") + loss = loss.mean(dim=list(range(1, len(loss.shape)))) * mse_loss_weights + loss = loss.mean() + + + # Gather the losses across all processes for logging (if we use distributed training). + avg_loss = accelerator.gather(loss.repeat(args.train_batch_size)).mean() + progress_info.train_loss += avg_loss.detach().item() / args.gradient_accumulation_steps + + # Backpropagate + accelerator.backward(loss) + if accelerator.sync_gradients: + params_to_clip = vip.parameters() + accelerator.clip_grad_norm_(params_to_clip, args.max_grad_norm) + optimizer.step() + lr_scheduler.step() + optimizer.zero_grad() + + if accelerator.sync_gradients: + sync_gradients_info(loss) + + if accelerator.is_main_process: + + if progress_info.global_step % args.checkpointing_steps == 0: + + if args.enable_tracker: + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ) + + if args.use_ema: + # Store the UNet parameters temporarily and load the EMA parameters to perform inference. + ema_vip.store(vip.parameters()) + ema_vip.copy_to(vip.parameters()) + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ema=True, + ) + # Switch back to the original UNet parameters. + ema_vip.restore(vip.parameters()) + + if prof is not None: + prof.step() + + + return loss + + def train_one_step(step_, data_item_, prof_=None): + # NOTE checking params update + # if step_ > 1: + # print("Comparing model parameters before and after training step:") + # for name, param in vip.named_parameters(): + # if not torch.equal(param, initial_params[name]): + # print(f"Parameter {name} has changed.") + # initial_params[name] = param.clone() + # else: + # print(f"Parameter {name} has not changed!") + + train_loss = 0.0 + x, attn_mask, input_ids, cond_mask, clip_data = data_item_ + # Sample noise that we'll add to the latents + + if not args.multi_scale: + assert torch.all(attn_mask) + assert not torch.any(torch.isnan(x)), 'torch.any(torch.isnan(x))' + x = x.to(accelerator.device, dtype=ae.vae.dtype) # B C T+image_num H W + clip_data = clip_data.to(accelerator.device) # B T+image_num C H W + + attn_mask = attn_mask.to(accelerator.device) # B T+image_num H W + input_ids = input_ids.to(accelerator.device) # B 1+image_num L + cond_mask = cond_mask.to(accelerator.device) # B 1+image_num L + # print('x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape', x.shape, attn_mask.shape, input_ids.shape, cond_mask.shape) + + with torch.no_grad(): + # import ipdb;ipdb.set_trace() + # use for loop to avoid OOM, because T5 is too huge... + B, N, L = input_ids.shape # B 1+image_num L + # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D + + # use batch inference + input_ids_ = input_ids.reshape(-1, L) + cond_mask_ = cond_mask.reshape(-1, L) + cond = text_enc(input_ids_, cond_mask_) + + assert not torch.any(torch.isnan(cond)), 'after text_enc' + + cond = cond.reshape(B, N, L, -1) # B 1+image_num L D + + clip_data = rearrange(clip_data, 'b t c h w -> (b t) c h w') # B T+image_num C H W -> B * (T+image_num) C H W + # NOTE using the second last layer of CLIP or last layer of DINO as clip feature + clip_feature = get_clip_feature(clip_data, image_encoder, args.image_encoder_type) # B * (T+image_num) D + clip_feature = clip_feature[:, 1:] # drop cls token + clip_feature_height = clip_feature_width = int(sqrt(clip_feature.shape[1])) + clip_feature = rearrange(clip_feature, '(b t) (h w) c -> b t h w c', b=B, h=clip_feature_height, w=clip_feature_width) # B T+image_num H W D + + # Map input images to latent space + normalize latents + if args.use_image_num == 0: + x = ae.encode(x) # B C T H W + else: + videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] + videos = ae.encode(videos) # B C T H W + images = rearrange(images, 'b c t h w -> (b t) c 1 h w') + images = ae.encode(images) + images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) + x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + + assert not torch.any(torch.isnan(x)), 'after vae' + + with accelerator.accumulate(vip): + x = x.to(weight_dtype) + assert not torch.any(torch.isnan(x)), 'after vae' + model_kwargs = dict(encoder_hidden_states=cond, attention_mask=attn_mask, + encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, + clip_feature=clip_feature) + run(x, model_kwargs, prof_) + + if progress_info.global_step >= args.max_train_steps: + return True + + return False + + def train_all_epoch(prof_=None): + for epoch in range(first_epoch, args.num_train_epochs): + progress_info.train_loss = 0.0 + if progress_info.global_step >= args.max_train_steps: + return True + + for step, data_item in enumerate(train_dataloader): + if accelerator.is_main_process: + if progress_info.global_step == 0: + print("before training, we need to check the validation mode...") + log_validation( + args=args, + model=model, + vip=vip, + vae=ae, + text_encoder=text_enc.text_enc, + tokenizer=train_dataset.tokenizer, + image_processor=train_dataset.image_processor, + image_encoder=image_encoder, + accelerator=accelerator, + weight_dtype=weight_dtype, + global_step=progress_info.global_step, + ) + if train_one_step(step, data_item, prof_): + break + + train_all_epoch() + accelerator.wait_for_everyone() + accelerator.end_training() + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + # dataset & dataloader + parser.add_argument("--dataset", type=str, required=True) + parser.add_argument("--video_data", type=str, required='') + parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--sample_rate", type=int, default=1) + parser.add_argument("--num_frames", type=int, default=65) + parser.add_argument("--max_height", type=int, default=320) + parser.add_argument("--max_width", type=int, default=240) + + + parser.add_argument("--use_img_from_vid", action="store_true") + parser.add_argument("--model_max_length", type=int, default=512) + parser.add_argument("--multi_scale", action="store_true") + parser.add_argument('--cfg', type=float, default=0.1) + parser.add_argument("--dataloader_num_workers", type=int, default=10, help="Number of subprocesses to use for data loading. 0 means that the data will be loaded in the main process.") + parser.add_argument("--train_batch_size", type=int, default=16, help="Batch size (per device) for the training dataloader.") + + # text encoder & vae & diffusion model + parser.add_argument("--model", type=str, choices=list(Diffusion_models.keys()), default="Latte-XL/122") + parser.add_argument('--enable_8bit_t5', action='store_true') + parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument("--compress_kv", action="store_true") + parser.add_argument("--attention_mode", type=str, choices=['xformers', 'math', 'flash'], default="xformers") + parser.add_argument('--use_rope', action='store_true') + parser.add_argument('--compress_kv_factor', type=int, default=1) + parser.add_argument('--interpolation_scale_h', type=float, default=1.0) + parser.add_argument('--interpolation_scale_w', type=float, default=1.0) + parser.add_argument('--interpolation_scale_t', type=float, default=1.0) + parser.add_argument("--downsampler", type=str, default=None) + parser.add_argument("--ae", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--ae_path", type=str, default="stabilityai/sd-vae-ft-mse") + parser.add_argument("--text_encoder_name", type=str, default='DeepFloyd/t5-v1_1-xxl') + parser.add_argument("--cache_dir", type=str, default='./cache_dir') + parser.add_argument("--pretrained", type=str, default=None) + parser.add_argument("--gradient_checkpointing", action="store_true", help="Whether or not to use gradient checkpointing to save memory at the expense of slower backward pass.") + + # diffusion setting + parser.add_argument("--zero_terminal_snr", action="store_true", help="Whether to zero the terminal SNR.") + parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") + parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_start_step", type=int, default=0) + parser.add_argument("--noise_offset", type=float, default=0, help="The scale of noise offset.") + parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") + + # validation & logs + parser.add_argument("--num_sampling_steps", type=int, default=50) + parser.add_argument('--guidance_scale', type=float, default=5.0) + parser.add_argument("--enable_tracker", action="store_true") + parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") + parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") + parser.add_argument("--checkpoints_total_limit", type=int, default=None, help=("Max number of checkpoints to store.")) + parser.add_argument("--checkpointing_steps", type=int, default=500, + help=( + "Save a checkpoint of the training state every X updates. These checkpoints can be used both as final" + " checkpoints in case they are better than the last checkpoint, and are also suitable for resuming" + " training using `--resume_from_checkpoint`." + ), + ) + parser.add_argument("--resume_from_checkpoint", type=str, default=None, + help=( + "Whether training should be resumed from a previous checkpoint. Use a path saved by" + ' `--checkpointing_steps`, or `"latest"` to automatically select the last available checkpoint.' + ), + ) + parser.add_argument("--logging_dir", type=str, default="logs", + help=( + "[TensorBoard](https://www.tensorflow.org/tensorboard) log directory. Will default to" + " *output_dir/runs/**CURRENT_DATETIME_HOSTNAME***." + ), + ) + parser.add_argument("--report_to", type=str, default="tensorboard", + help=( + 'The integration to report the results and logs to. Supported platforms are `"tensorboard"`' + ' (default), `"wandb"` and `"comet_ml"`. Use `"all"` to report to all integrations.' + ), + ) + # optimizer & scheduler + parser.add_argument("--num_train_epochs", type=int, default=100) + parser.add_argument("--max_train_steps", type=int, default=None, help="Total number of training steps to perform. If provided, overrides num_train_epochs.") + parser.add_argument("--gradient_accumulation_steps", type=int, default=1, help="Number of updates steps to accumulate before performing a backward/update pass.") + parser.add_argument("--optimizer", type=str, default="adamW", help='The optimizer type to use. Choose between ["AdamW", "prodigy"]') + parser.add_argument("--learning_rate", type=float, default=1e-4, help="Initial learning rate (after the potential warmup period) to use.") + parser.add_argument("--scale_lr", action="store_true", default=False, help="Scale the learning rate by the number of GPUs, gradient accumulation steps, and batch size.") + parser.add_argument("--lr_warmup_steps", type=int, default=500, help="Number of steps for the warmup in the lr scheduler.") + parser.add_argument("--use_8bit_adam", action="store_true", help="Whether or not to use 8-bit Adam from bitsandbytes. Ignored if optimizer is not set to AdamW") + parser.add_argument("--adam_beta1", type=float, default=0.9, help="The beta1 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--adam_beta2", type=float, default=0.999, help="The beta2 parameter for the Adam and Prodigy optimizers.") + parser.add_argument("--prodigy_decouple", type=bool, default=True, help="Use AdamW style decoupled weight decay") + parser.add_argument("--adam_weight_decay", type=float, default=1e-02, help="Weight decay to use for unet params") + parser.add_argument("--adam_weight_decay_text_encoder", type=float, default=None, help="Weight decay to use for text_encoder") + parser.add_argument("--adam_epsilon", type=float, default=1e-08, help="Epsilon value for the Adam optimizer and Prodigy optimizers.") + parser.add_argument("--prodigy_use_bias_correction", type=bool, default=True, help="Turn on Adam's bias correction. True by default. Ignored if optimizer is adamW") + parser.add_argument("--prodigy_safeguard_warmup", type=bool, default=True, help="Remove lr from the denominator of D estimate to avoid issues during warm-up stage. True by default. Ignored if optimizer is adamW") + parser.add_argument("--max_grad_norm", default=1.0, type=float, help="Max gradient norm.") + parser.add_argument("--prodigy_beta3", type=float, default=None, + help="coefficients for computing the Prodidy stepsize using running averages. If set to None, " + "uses the value of square root of beta2. Ignored if optimizer is adamW", + ) + parser.add_argument("--lr_scheduler", type=str, default="constant", + help=( + 'The scheduler type to use. Choose between ["linear", "cosine", "cosine_with_restarts", "polynomial",' + ' "constant", "constant_with_warmup"]' + ), + ) + parser.add_argument("--allow_tf32", action="store_true", + help=( + "Whether or not to allow TF32 on Ampere GPUs. Can be used to speed up training. For more information, see" + " https://pytorch.org/docs/stable/notes/cuda.html#tensorfloat-32-tf32-on-ampere-devices" + ), + ) + parser.add_argument("--mixed_precision", type=str, default=None, choices=["no", "fp16", "bf16"], + help=( + "Whether to use mixed precision. Choose between fp16 and bf16 (bfloat16). Bf16 requires PyTorch >=" + " 1.10.and an Nvidia Ampere GPU. Default to the value of accelerate config of the current system or the" + " flag passed with the `accelerate.launch` command. Use this argument to override the accelerate config." + ), + ) + + parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") + + # inpaint dataset + parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode + parser.add_argument("--transition_ratio", type=float, default=0.4) # for inpainting mode + parser.add_argument("--default_text_ratio", type=float, default=0.1) + parser.add_argument("--validation_dir", type=str, default=None, help="Path to the validation dataset.") + parser.add_argument("--image_encoder_type", type=str, default='clip', choices=['clip', 'dino']) + parser.add_argument("--image_encoder_name", type=str, default='laion/CLIP-ViT-H-14-laion2B-s32B-b79K') + parser.add_argument("--pretrained_vip_adapter_path", type=str, default=None) + parser.add_argument("--clear_video_ratio", type=float, default=0.0) + parser.add_argument("--use_image_num", type=int, default=0) + parser.add_argument("--vip_num_attention_heads", type=int, default=8) + + + args = parser.parse_args() + main(args) diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 40227021c..3a8d05f68 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -220,6 +220,7 @@ def __call__(self, batch): if self.num_frames > 1 and self.use_image_num == 0: clip_data = clip_vid # b t c h w elif self.num_frames > 1 and self.use_image_num != 0: + raise NotImplementedError clip_data = torch.cat([clip_vid, clip_img], dim=1) # b t+num_img c h w else: # num_frames == 1 (only image) clip_data = clip_img # b 1 c h w diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index f5125fc79..1b7e0843a 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,2 +1,4 @@ node065 slots=8 -node068 slots=8 \ No newline at end of file +node068 slots=8 +node040 slots=8 +node103 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index dafbaa602..1643e5c44 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -8,8 +8,8 @@ machine_rank: 0 main_process_ip: 100.64.24.65 main_process_port: 29502 main_training_function: main -num_machines: 2 -num_processes: 16 +num_machines: 4 +num_processes: 32 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/text_condition/gpu/sample_inpaint.sh b/scripts/text_condition/gpu/sample_inpaint.sh index 898acbff1..8866893f9 100644 --- a/scripts/text_condition/gpu/sample_inpaint.sh +++ b/scripts/text_condition/gpu/sample_inpaint.sh @@ -1,18 +1,22 @@ -CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_inpaint.py \ - --model_path /storage/gyy/hw/Open-Sora-Plan/inpaint_125x480p_bs1_lr1e-5_snr5_noioff0.02_new_mask \ - --num_frames 125 \ +export MASTER_PORT=12359 +export MASTER_ADDR='localhost' + +torchrun --nproc_per_node=8 opensora/sample/sample_inpaint.py \ + --model_path /storage/gyy/hw/Open-Sora-Plan/inpaint_93x480p_bs4x8x1_lr1e-5_snr5_noioff0.02_new_mask_onlysucai \ + --num_frames 93 \ --height 480 \ --width 640 \ --cache_dir "../cache_dir" \ --text_encoder_name google/mt5-xxl \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_inpaint_125x480p_bs1_lr1e-5_snr5_noioff0.02_new_mask" \ + --save_img_path "/storage/gyy/hw/Open-Sora-Plan/sample_inpaint_93x480p_bs4x8x1_lr1e-5_snr5_noioff0.02_new_mask_onlysucai" \ --fps 24 \ - --guidance_scale 3.0 \ + --guidance_scale 5.0 \ --num_sampling_steps 50 \ --enable_tiling \ --max_sequence_length 512 \ --sample_method PNDM \ --model_3d \ - --validation_dir "./validation_dir" \ No newline at end of file + --validation_dir "./validation_dir" \ + --seed 42 \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh b/scripts/text_condition/gpu/train_inpaint_video3d_nx480p.sh similarity index 76% rename from scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh rename to scripts/text_condition/gpu/train_inpaint_video3d_nx480p.sh index 99eeb1c16..cfe7af671 100644 --- a/scripts/text_condition/gpu/train_inpaint_video3d_125x480p.sh +++ b/scripts/text_condition/gpu/train_inpaint_video3d_nx480p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" # export WANDB_MODE="offline" export ENTITY="yunyangge" -export PROJECT="inpaint_125x480p_bs1_lr1e-5_snr5_noioff0.02_new_mask" +export PROJECT="inpaint_93x480p_bs4x8x1_lr1e-5_snr5_noioff0.02_new_mask_onlysucai" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 export PDSH_RCMD_TYPE=ssh @@ -20,20 +20,20 @@ export MKL_NUM_THREADS=1 export NCCL_ALGO=Tree accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_inpaint.py \ --model OpenSoraInpaint-ROPE-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "/storage/ongoing/new/cache_dir" \ --dataset i2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --video_data "scripts/train_data/video_data_aesmovie_sucai.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 125 \ + --num_frames 93 \ --use_image_num 0 \ - --max_height 480 \ - --max_width 640 \ + --max_height 720 \ + --max_width 1280 \ --interpolation_scale_t 1.0 \ --interpolation_scale_h 1.0 \ --interpolation_scale_w 1.0 \ @@ -54,21 +54,21 @@ accelerate launch \ --tile_overlap_factor 0.125 \ --enable_tiling \ --snr_gamma 5.0 \ - --use_ema 0.999 \ + --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ --noise_offset 0.02 \ --use_rope \ --resume_from_checkpoint="latest" \ - --ema_decay \ + --ema_decay 0.999 \ --group_frame \ --speed_factor 1.1 \ - --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs36x8x1_125x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2/checkpoint-3500/model_ema/diffusion_pytorch_model.safetensors" \ + --pretrained "/storage/gyy/hw/Open-Sora-Plan/inpaint_93x480p_bs4x8x1_lr1e-5_snr5_noioff0.02_new_mask/checkpoint-3400/model_ema/diffusion_pytorch_model.safetensors" \ --output_dir=$PROJECT \ --i2v_ratio 0.5 \ --transition_ratio 0.4 \ --default_text_ratio 0.5 \ --validation_dir "validation_dir" \ --num_sampling_steps 50 \ - --guidance_scale 3.0 \ + --guidance_scale 5.0 \ --seed 42 diff --git a/scripts/text_condition/gpu/train_video_ip_video3d.sh b/scripts/text_condition/gpu/train_video_ip_video3d.sh new file mode 100644 index 000000000..5de59e479 --- /dev/null +++ b/scripts/text_condition/gpu/train_video_ip_video3d.sh @@ -0,0 +1,75 @@ +PROJECT="videoip_3d_480p_bs8x16_lr1e-5_snrgamma5_0_noiseoffset0_02_dino518_ema0_999" +export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" +export WANDB_MODE="offline" +export ENTITY="yunyangge" +export PROJECT=$PROJECT +# export HF_DATASETS_OFFLINE=1 +# export TRANSFORMERS_OFFLINE=1 +export TOKENIZERS_PARALLELISM=false +# # NCCL setting IB网卡时用 +export NCCL_PXN_DISABLE=0 +export NCCL_IB_QPS_PER_CONNECTION=4 +export NCCL_IB_GID_INDEX=3 +export NCCL_ALGO=Ring +export OMP_NUM_THREADS=1 + +export PDSH_RCMD_TYPE=ssh + +accelerate launch \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ + opensora/train/train_videoip.py \ + --model OpenSoraT2V-ROPE-L/122 \ + --text_encoder_name google/mt5-xxl \ + --image_encoder_name vit_giant_patch14_reg4_dinov2.lvd142m \ + --cache_dir "/storage/cache_dir" \ + --dataset vip \ + --ae CausalVAEModel_4x8x8 \ + --ae_path "/storage/CausalVAEModel_4x8x8" \ + --video_data "scripts/train_data/video_data.txt" \ + --image_data "scripts/train_data/image_data.txt" \ + --sample_rate 1 \ + --num_frames 1 \ + --use_image_num 0 \ + --max_height 480 \ + --max_width 640 \ + --interpolation_scale_t 1.0 \ + --interpolation_scale_h 1.0 \ + --interpolation_scale_w 1.0 \ + --attention_mode xformers \ + --train_batch_size=16 \ + --dataloader_num_workers 10 \ + --gradient_accumulation_steps=1 \ + --max_train_steps=500000 \ + --learning_rate=1e-5 \ + --lr_scheduler="constant" \ + --lr_warmup_steps=0 \ + --mixed_precision="bf16" \ + --report_to="wandb" \ + --enable_tracker \ + --checkpointing_steps=1000 \ + --output_dir=$PROJECT \ + --allow_tf32 \ + --model_max_length 512 \ + --enable_tiling \ + --tile_overlap_factor 0.125 \ + --validation_dir "validation_dir" \ + --guidance_scale 5.0 \ + --num_sampling_steps 50 \ + --ema_start_step 0 \ + --use_ema \ + --cfg 0.05 \ + --i2v_ratio 0.4 \ + --transition_ratio 0.4 \ + --clear_video_ratio 0.1 \ + --default_text_ratio 0.5 \ + --seed 42 \ + --snr_gamma 5.0 \ + --noise_offset 0.02 \ + --vip_num_attention_heads 16 \ + --ema_decay 0.999 \ + --use_rope \ + --pretrained "/storage/dataset/hw29/image/model_ema/diffusion_pytorch_model.safetensors" \ + --resume_from_checkpoint "latest" \ + # --pretrained_vip_adapter_path "videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_last2layer/checkpoint-50000/model" \ + # --zero_terminal_snr \ + # 基模型权重没有参与训练所以一定要加载 \ No newline at end of file diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index 0758b4fd6..dbbb2a964 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,2 +1,2 @@ -/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json -/storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json \ No newline at end of file +/storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json +/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json \ No newline at end of file diff --git a/scripts/train_data/video_data_aesmovie_sucai.txt b/scripts/train_data/video_data_aesmovie_sucai.txt index ca9aadcd5..31537b50c 100644 --- a/scripts/train_data/video_data_aesmovie_sucai.txt +++ b/scripts/train_data/video_data_aesmovie_sucai.txt @@ -1,9 +1,3 @@ -/storage/dataset/movie,/storage/dataset/movie/bbc01_clips_aes_motion_final_250508.json -/storage/dataset/movie,/storage/dataset/movie/bbc02_clips_aes_motion_final_289778.json -/storage/dataset/movie,/storage/dataset/movie/bbc03_clips_aes_motion_final_519184.json -/storage/dataset/movie,/storage/dataset/movie/bbc04_clips_aes_motion_final_249497.json -/storage/dataset/movie,/storage/dataset/movie/bbc05_clips_aes_motion_final_416548.json -/storage/dataset/movie,/storage/dataset/movie/TV01_clips_aes_motion_final_217419.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95441.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_clipchamp_final_452264.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_coverr_final_3002.json diff --git a/validation_dir/prompt.txt b/validation_dir/prompt.txt index bb0e595a4..592bd5a4d 100644 --- a/validation_dir/prompt.txt +++ b/validation_dir/prompt.txt @@ -1,11 +1,27 @@ A rocket ascends slowly into the sky. A coffee cup with "anytext" foam floating on it. A beautiful girl is blinking her eyes. -A flower and bird painting. -The scenery of snow capped mountains. -The night view of the city. +A flower and bird painting.This video has a silky transition effect. +The scenery of snow capped mountains.This video has a silky transition effect. +The night view of the city.This video has a silky transition effect. A rocket ascends slowly into the sky. Along the coast, variously sized boats float on the lake. The landscape at sunset is profound and expansive. A beautiful girl is blinking her eyes. -A beautiful girl is blinking her eyes. \ No newline at end of file +A beautiful girl is blinking her eyes. +A coffee cup with "anytext" foam floating on it. +A person is eating pizza on a plate. +A group of people are cheering and celebrating. +A green dinosaur is playing tennis. +A female elf is smiling. +The girl with red hair is turning her head and smiling. +A racing car is speeding on the road. Environmental perspective. +A family photo of four people smiling and playing around with each other. +The old lady hugged her granddaughter and they laughed happily. +Drones fly from snow capped mountains to cliffs, with waterfalls flowing. This video has a silky transition effect. +There is no transition video from the cliff to the bottom, and the waterfall is flowing. +The scene from the seabed to the cliff.This video has a silky transition effect. +The boy in the video abandoned his girlfriend and instead embraced the girl in red clothes. +A beautiful girl is stroking her hair and blinking her eyes. +A fashionable female model strolls on the streets of Tokyo. +A panda playing guitar on the grass. \ No newline at end of file From c3b74ec4ccf072193042b7967eb500dae70dfc15 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Mon, 15 Jul 2024 07:41:50 +0000 Subject: [PATCH 086/134] refine group sampler --- .gitignore | 3 +- opensora/dataset/__init__.py | 60 +++++- opensora/dataset/t2v_datasets.py | 1 - .../causal_vae/modeling_causalvae.py | 2 +- .../diffusion/opensora/modeling_opensora.py | 12 +- .../models/diffusion/udit/modeling_udit.py | 68 +++--- opensora/models/diffusion/udit/modules.py | 198 +++++++----------- opensora/train/train_t2v_diffusers.py | 34 +-- opensora/utils/dataset_utils.py | 46 ++++ scripts/accelerate_configs/hostfile | 36 +--- .../multi_node_example.yaml | 6 +- .../text_condition/gpu/sample_video_ddp1.sh | 23 -- scripts/text_condition/gpu/sample_video_hw.sh | 19 -- scripts/text_condition/gpu/sample_video_sp.sh | 14 +- .../gpu/train_ds_image_256x256.sh | 54 ----- .../gpu/train_ds_video3d_61x240p.sh | 54 ----- ...new_rope_fp32.sh => train_image_1x240p.sh} | 30 +-- ...e_sucai_panda.sh => train_image_1x480p.sh} | 29 ++- .../text_condition/gpu/train_image_256x256.sh | 53 ----- .../gpu/train_imageudit_240p.sh | 56 ----- .../gpu/train_imageudit_240p_debug.sh | 55 ----- .../gpu/train_imageudit_240p_new.sh | 57 ----- .../gpu/train_imageudit_480p_prodigy.sh | 61 ------ .../gpu/train_imageuditultra_480p_new.sh | 59 ------ ...train_imageuditultra_480p_new_rope_fp32.sh | 61 ------ ...train_imageuditultra_480p_new_rope_lora.sh | 59 ------ .../gpu/train_video21d_61x240p.sh | 53 ----- .../gpu/train_video3d_29x720p.sh | 11 +- .../gpu/train_video3d_93x720p.sh | 7 +- ...480p_sp.sh => train_video3d_93x720p_sp.sh} | 28 +-- .../gpu/train_video_ip_video21d.sh | 69 ------ .../gpu/train_video_ip_video21d_clip.sh | 68 ------ ...125x480p.sh => train_videoudit_29x720p.sh} | 19 +- .../gpu/train_videouditultra_125x480p.sh | 59 ------ ...n_videouditultra_125x480p_new_rope_fp32.sh | 63 ------ ...in_videouditultra_61x480p_new_rope_fp32.sh | 62 ------ ...ra_61x480p_new_rope_fp32_aesmovie_sucai.sh | 62 ------ ...480p_new_rope_fp32_aesmovie_sucai_panda.sh | 68 ------ ..._61x480p_new_rope_fp32_movie_time_sucai.sh | 62 ------ scripts/train_data/image_data.txt | 1 - ...esmovie_sucai.txt => video_data_sucai.txt} | 6 - scripts/train_data/video_data_sucai_aes5.txt | 9 + 42 files changed, 316 insertions(+), 1481 deletions(-) delete mode 100644 scripts/text_condition/gpu/sample_video_ddp1.sh delete mode 100644 scripts/text_condition/gpu/sample_video_hw.sh delete mode 100644 scripts/text_condition/gpu/train_ds_image_256x256.sh delete mode 100644 scripts/text_condition/gpu/train_ds_video3d_61x240p.sh rename scripts/text_condition/gpu/{train_imageuditultra_240p_new_rope_fp32.sh => train_image_1x240p.sh} (69%) rename scripts/text_condition/gpu/{train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh => train_image_1x480p.sh} (68%) delete mode 100644 scripts/text_condition/gpu/train_image_256x256.sh delete mode 100644 scripts/text_condition/gpu/train_imageudit_240p.sh delete mode 100644 scripts/text_condition/gpu/train_imageudit_240p_debug.sh delete mode 100644 scripts/text_condition/gpu/train_imageudit_240p_new.sh delete mode 100644 scripts/text_condition/gpu/train_imageudit_480p_prodigy.sh delete mode 100644 scripts/text_condition/gpu/train_imageuditultra_480p_new.sh delete mode 100644 scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh delete mode 100644 scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh delete mode 100644 scripts/text_condition/gpu/train_video21d_61x240p.sh rename scripts/text_condition/gpu/{train_video3d_125x480p_sp.sh => train_video3d_93x720p_sp.sh} (71%) delete mode 100644 scripts/text_condition/gpu/train_video_ip_video21d.sh delete mode 100644 scripts/text_condition/gpu/train_video_ip_video21d_clip.sh rename scripts/text_condition/gpu/{train_video3d_125x480p.sh => train_videoudit_29x720p.sh} (77%) delete mode 100644 scripts/text_condition/gpu/train_videouditultra_125x480p.sh delete mode 100644 scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh delete mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh delete mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh delete mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh delete mode 100644 scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh rename scripts/train_data/{video_data_aesmovie_sucai.txt => video_data_sucai.txt} (68%) create mode 100644 scripts/train_data/video_data_sucai_aes5.txt diff --git a/.gitignore b/.gitignore index b30f6e59d..48e638889 100644 --- a/.gitignore +++ b/.gitignore @@ -54,4 +54,5 @@ inpaint*/ *pymp* check.py bucket.py -whileinf.py \ No newline at end of file +whileinf.py +bs4x8x16_* \ No newline at end of file diff --git a/opensora/dataset/__init__.py b/opensora/dataset/__init__.py index b2408baa2..c33b3cc43 100644 --- a/opensora/dataset/__init__.py +++ b/opensora/dataset/__init__.py @@ -12,9 +12,9 @@ # torch_npu = None # npu_config = None # from .t2v_datasets import T2V_dataset -from .t2v_datasets import T2V_dataset -from .inpaint_datasets import Inpaint_dataset -from .transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo +from opensora.dataset.t2v_datasets import T2V_dataset +from opensora.dataset.inpaint_datasets import Inpaint_dataset +from opensora.dataset.transform import ToTensorVideo, TemporalRandomCrop, RandomHorizontalFlipVideo, CenterCropResizeVideo, LongSideResizeVideo, SpatialStrideCropVideo ae_norm = { @@ -111,3 +111,57 @@ def getdataset(args): return Inpaint_dataset(args, transform=transform, temporal_sample=temporal_sample, tokenizer=tokenizer, transform_topcrop=transform_topcrop) raise NotImplementedError(args.dataset) + + +if __name__ == "__main__": + from accelerate import Accelerator + from opensora.dataset.t2v_datasets import dataset_prog + import random + from tqdm import tqdm + args = type('args', (), + { + 'ae': 'CausalVAEModel_4x8x8', + 'dataset': 't2v', + 'attention_mode': 'xformers', + 'use_rope': True, + 'model_max_length': 300, + 'max_height': 320, + 'max_width': 240, + 'num_frames': 1, + 'use_image_num': 0, + 'compress_kv_factor': 1, + 'interpolation_scale_t': 1, + 'interpolation_scale_h': 1, + 'interpolation_scale_w': 1, + 'cache_dir': '../cache_dir', + 'image_data': '/storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/scripts/train_data/image_data.txt', + 'video_data': '1', + 'train_fps': 24, + 'drop_short_ratio': 1.0, + 'use_img_from_vid': False, + 'speed_factor': 1.0, + 'cfg': 0.1, + 'text_encoder_name': 'google/mt5-xxl', + 'dataloader_num_workers': 10, + + } + ) + accelerator = Accelerator() + dataset = getdataset(args) + num = len(dataset_prog.img_cap_list) + zero = 0 + for idx in tqdm(range(num)): + image_data = dataset_prog.img_cap_list[idx] + caps = [i['cap'] if isinstance(i['cap'], list) else [i['cap']] for i in image_data] + try: + caps = [[random.choice(i)] for i in caps] + except Exception as e: + print(e) + # import ipdb;ipdb.set_trace() + print(image_data) + zero += 1 + continue + assert caps[0] is not None and len(caps[0]) > 0 + print(num, zero) + import ipdb;ipdb.set_trace() + print('end') \ No newline at end of file diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index f2ad708f8..463c97446 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -365,7 +365,6 @@ def define_frame_index(self, vid_cap_list): f'no_resolution: {cnt_no_resolution}, resolution_mismatch: {cnt_resolution_mismatch}, ' f'Counter(sample_num_frames): {Counter(sample_num_frames)}, movie: {cnt_movie}, ' f'before filter: {len(vid_cap_list)}, after filter: {len(new_vid_cap_list)}') - # import ipdb;ipdb.set_trace() return new_vid_cap_list, sample_num_frames def decord_read(self, path): diff --git a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py index 03efadf23..177fc39a5 100644 --- a/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py +++ b/opensora/models/ae/videobase/causal_vae/modeling_causalvae.py @@ -328,7 +328,7 @@ def __init__( decoder_mid_resnet: Module = "ResnetBlock3D", ) -> None: super().__init__() - self.tile_sample_min_size = 512 + self.tile_sample_min_size = 256 self.tile_sample_min_size_t = 33 self.tile_latent_min_size = int(self.tile_sample_min_size / (2 ** (len(hidden_size_mult) - 1))) diff --git a/opensora/models/diffusion/opensora/modeling_opensora.py b/opensora/models/diffusion/opensora/modeling_opensora.py index 76b1c59ed..b1ed41142 100644 --- a/opensora/models/diffusion/opensora/modeling_opensora.py +++ b/opensora/models/diffusion/opensora/modeling_opensora.py @@ -707,9 +707,9 @@ def OpenSoraT2V_B_222(**kwargs): 'attention_mode': 'xformers', 'use_rope': True, 'model_max_length': 300, - 'max_height': 480, - 'max_width': 640, - 'num_frames': 61, + 'max_height': 320, + 'max_width': 240, + 'num_frames': 1, 'use_image_num': 0, 'compress_kv_factor': 1, 'interpolation_scale_t': 1, @@ -717,7 +717,7 @@ def OpenSoraT2V_B_222(**kwargs): 'interpolation_scale_w': 1, } ) - b = 2 + b = 16 c = 8 cond_c = 4096 num_timesteps = 1000 @@ -729,7 +729,7 @@ def OpenSoraT2V_B_222(**kwargs): num_frames = args.num_frames // ae_stride_t device = torch.device('cuda:0') - model = OpenSoraT2V_L_122(in_channels=c, + model = OpenSoraT2V_ROPE_L_122(in_channels=c, out_channels=c, sample_size=latent_size, sample_size_t=num_frames, @@ -767,7 +767,7 @@ def OpenSoraT2V_B_222(**kwargs): print(e) print(model) print(f'{sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B') - import sys;sys.exit() + # import sys;sys.exit() x = torch.randn(b, c, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w).to(device) cond = torch.randn(b, 1+args.use_image_num, args.model_max_length, cond_c).to(device) attn_mask = torch.randint(0, 2, (b, 1+(args.num_frames-1)//ae_stride_t+args.use_image_num, args.max_height//ae_stride_h, args.max_width//ae_stride_w)).to(device) # B L or B 1+num_images L diff --git a/opensora/models/diffusion/udit/modeling_udit.py b/opensora/models/diffusion/udit/modeling_udit.py index 94d6651cc..02d3804fe 100644 --- a/opensora/models/diffusion/udit/modeling_udit.py +++ b/opensora/models/diffusion/udit/modeling_udit.py @@ -12,8 +12,7 @@ from diffusers.models.modeling_utils import ModelMixin from diffusers.models.normalization import AdaLayerNormSingle from diffusers.models.embeddings import PixArtAlphaTextProjection -from opensora.models.diffusion.udit_ultra.modules import Upsample3d, Downsample3d, Upsample2d, Downsample2d, \ - OverlapPatchEmbed3D, OverlapPatchEmbed2D, BasicTransformerBlock, \ +from opensora.models.diffusion.udit.modules import Upsample2d, Downsample2d, PatchEmbed2D, BasicTransformerBlock, \ FP32_GELU, FP32_SiLU, FP32_Layernorm from opensora.utils.utils import to_2tuple import math @@ -178,7 +177,7 @@ def _init_patched_inputs(self, norm_type): # ) # elif self.config.downsampler is not None and len(self.config.downsampler) == 7: # is_video_model = False - self.pos_embed = OverlapPatchEmbed2D( + self.pos_embed = PatchEmbed2D( num_frames=self.config.sample_size_t, height=self.config.sample_size[0], width=self.config.sample_size[1], @@ -228,7 +227,7 @@ def _init_patched_inputs(self, norm_type): ] ) # self.down1_2 = Downsample3d(self.inner_dim) if is_video_model else Downsample2d(self.inner_dim) - self.down1_2 = Downsample2d(self.inner_dim, is_video_model) + self.down1_2 = Downsample2d(self.inner_dim) self.encoder_level_2 = nn.ModuleList( [ @@ -258,7 +257,7 @@ def _init_patched_inputs(self, norm_type): ] ) # self.down2_3 = Downsample3d(self.inner_dim * 2) if is_video_model else Downsample2d(self.inner_dim * 2) - self.down2_3 = Downsample2d(self.inner_dim * 2, is_video_model) + self.down2_3 = Downsample2d(self.inner_dim * 2) self.latent = nn.ModuleList( [ @@ -289,7 +288,7 @@ def _init_patched_inputs(self, norm_type): ) # self.up3_2 = Upsample3d(int(self.inner_dim * 4)) if is_video_model else Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 - self.up3_2 = Upsample2d(self.inner_dim * 4, is_video_model) ## From Level 4 to Level 3 + self.up3_2 = Upsample2d(self.inner_dim * 4) ## From Level 4 to Level 3 self.reduce_chan_level2_norm = nn.LayerNorm(int(self.inner_dim * 4), elementwise_affine=True, eps=1e-6) self.reduce_chan_level2 = nn.Linear(int(self.inner_dim * 4), int(self.inner_dim * 2), bias=True) @@ -322,7 +321,7 @@ def _init_patched_inputs(self, norm_type): ) # self.up2_1 = Upsample3d(int(self.inner_dim * 2)) if is_video_model else Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 - self.up2_1 = Upsample2d(self.inner_dim * 2, is_video_model) ## From Level 4 to Level 3 + self.up2_1 = Upsample2d(self.inner_dim * 2) ## From Level 4 to Level 3 self.reduce_chan_level1_norm = nn.LayerNorm(int(self.inner_dim * 2), elementwise_affine=True, eps=1e-6) self.reduce_chan_level1 = nn.Linear(int(self.inner_dim * 2), int(self.inner_dim * 1), bias=True) @@ -907,9 +906,9 @@ def UDiTT2V_XL_111(**kwargs): 'attention_mode': 'xformers', 'use_rope': True, 'model_max_length': 300, - 'max_height': 480, - 'max_width': 640, - 'num_frames': 61, + 'max_height': 240, + 'max_width': 320, + 'num_frames': 1, 'use_image_num': 0, 'compress_kv_factor': 1, 'interpolation_scale_t': 1, @@ -917,7 +916,7 @@ def UDiTT2V_XL_111(**kwargs): 'interpolation_scale_w': 1, } ) - b = 2 + b = 16 c = 4 cond_c = 4096 num_timesteps = 1000 @@ -948,7 +947,7 @@ def UDiTT2V_XL_111(**kwargs): upcast_attention=False, use_linear_projection=False, use_additional_conditions=False, - downsampler='k333_s222', + downsampler=None, interpolation_scale_t=args.interpolation_scale_t, interpolation_scale_h=args.interpolation_scale_h, interpolation_scale_w=args.interpolation_scale_w, @@ -960,28 +959,30 @@ def UDiTT2V_XL_111(**kwargs): model_state_dict = model.state_dict() pretrained = "/storage/ongoing/new/Open-Sora-Plan/bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-11500/model_ema/diffusion_pytorch_model.safetensors" - if 'safetensors' in pretrained: # pixart series - from safetensors.torch import load_file as safe_load + try: + if 'safetensors' in pretrained: # pixart series + from safetensors.torch import load_file as safe_load + # import ipdb;ipdb.set_trace() + pretrained_checkpoint = safe_load(pretrained, device="cpu") + pretrained_keys = set(list(pretrained_checkpoint.keys())) + model_keys = set(list(model_state_dict.keys())) + common_keys = list(pretrained_keys & model_keys) + checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} + # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: + # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") + # repeat = model.pos_embed.proj.weight.shape[2] + # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) + # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] + else: # latest stage training weight + checkpoint = torch.load(pretrained, map_location='cpu') + if 'model' in checkpoint: + checkpoint = checkpoint['model'] # import ipdb;ipdb.set_trace() - pretrained_checkpoint = safe_load(pretrained, device="cpu") - pretrained_keys = set(list(pretrained_checkpoint.keys())) - model_keys = set(list(model_state_dict.keys())) - common_keys = list(pretrained_keys & model_keys) - checkpoint = {k: pretrained_checkpoint[k] for k in common_keys if model_state_dict[k].numel() == pretrained_checkpoint[k].numel()} - # if checkpoint['pos_embed.proj.weight'].shape != model.pos_embed.proj.weight.shape and checkpoint['pos_embed.proj.weight'].ndim == 4: - # logger.info(f"Resize pos_embed, {checkpoint['pos_embed.proj.weight'].shape} -> {model.pos_embed.proj.weight.shape}") - # repeat = model.pos_embed.proj.weight.shape[2] - # checkpoint['pos_embed.proj.weight'] = checkpoint['pos_embed.proj.weight'].unsqueeze(2).repeat(1, 1, repeat, 1, 1) / float(repeat) - # del checkpoint['proj_out.weight'], checkpoint['proj_out.bias'] - else: # latest stage training weight - checkpoint = torch.load(pretrained, map_location='cpu') - if 'model' in checkpoint: - checkpoint = checkpoint['model'] - import ipdb;ipdb.set_trace() - missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) - print(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') - print(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') - + missing_keys, unexpected_keys = model.load_state_dict(checkpoint, strict=False) + print(f'missing_keys {len(missing_keys)} {missing_keys}, unexpected_keys {len(unexpected_keys)}') + print(f'Successfully load {len(model_state_dict) - len(missing_keys)}/{len(model_state_dict)} keys from {args.pretrained}!') + except Exception as e: + print(e) # import sys;sys.exit() # try: @@ -1022,6 +1023,7 @@ def UDiTT2V_XL_111(**kwargs): encoder_attention_mask=cond_mask, use_image_num=args.use_image_num, timestep=timestep) with torch.no_grad(): output = model(**model_kwargs)[0] + import ipdb;ipdb.set_trace() print(output.shape) diff --git a/opensora/models/diffusion/udit/modules.py b/opensora/models/diffusion/udit/modules.py index 522de946f..631848900 100644 --- a/opensora/models/diffusion/udit/modules.py +++ b/opensora/models/diffusion/udit/modules.py @@ -266,7 +266,7 @@ def forward(self, latent, num_frames): -class OverlapPatchEmbed2D(nn.Module): +class PatchEmbed2D(nn.Module): """2D Image to Patch Embedding but with 3D position embedding""" def __init__( @@ -283,16 +283,16 @@ def __init__( bias=True, interpolation_scale=(1, 1), interpolation_scale_t=1, - use_abs_pos=False, + use_abs_pos=True, ): super().__init__() - assert patch_size_t == 1 + # assert num_frames == 1 self.use_abs_pos = use_abs_pos self.flatten = flatten self.layer_norm = layer_norm self.proj = nn.Conv2d( - in_channels, embed_dim, kernel_size=3, padding=1, stride=(patch_size, patch_size), bias=bias + in_channels, embed_dim, kernel_size=(patch_size, patch_size), stride=(patch_size, patch_size), bias=bias ) if layer_norm: self.norm = nn.LayerNorm(embed_dim, elementwise_affine=False, eps=1e-6) @@ -324,7 +324,6 @@ def forward(self, latent, num_frames): video_latent, image_latent = None, None # b c 1 h w # assert latent.shape[-3] == 1 and num_frames == 1 - num_frames = latent.shape[-3] // self.patch_size_t height, width = latent.shape[-2] // self.patch_size, latent.shape[-1] // self.patch_size latent = rearrange(latent, 'b c t h w -> (b t) c h w') latent = self.proj(latent) @@ -354,12 +353,39 @@ def forward(self, latent, num_frames): if self.num_frames != num_frames: # import ipdb;ipdb.set_trace() # raise NotImplementedError - temp_pos_embed = get_1d_sincos_pos_embed( - embed_dim=self.temp_pos_embed.shape[-1], - grid_size=num_frames, - base_size=self.base_size_t, - interpolation_scale=self.interpolation_scale_t, - ) + if get_sequence_parallel_state(): + if npu_config is not None: + sp_size = hccl_info.world_size + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames * sp_size, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + rank = hccl_info.rank % sp_size + st_frame = rank * num_frames + ed_frame = st_frame + num_frames + temp_pos_embed = temp_pos_embed[st_frame: ed_frame] + else: + sp_size = nccl_info.world_size + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames * sp_size, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) + rank = nccl_info.rank % sp_size + st_frame = rank * num_frames + ed_frame = st_frame + num_frames + temp_pos_embed = temp_pos_embed[st_frame: ed_frame] + + else: + temp_pos_embed = get_1d_sincos_pos_embed( + embed_dim=self.temp_pos_embed.shape[-1], + grid_size=num_frames, + base_size=self.base_size_t, + interpolation_scale=self.interpolation_scale_t, + ) temp_pos_embed = torch.from_numpy(temp_pos_embed) temp_pos_embed = temp_pos_embed.float().unsqueeze(0).to(latent.device) else: @@ -368,15 +394,16 @@ def forward(self, latent, num_frames): latent = (latent + pos_embed).to(latent.dtype) latent = rearrange(latent, '(b t) n c -> b t n c', b=b) - assert num_frames == latent.shape[1] + video_latent = latent if self.use_abs_pos: # temp_pos_embed = temp_pos_embed.unsqueeze(2) * self.temp_embed_gate.tanh() temp_pos_embed = temp_pos_embed.unsqueeze(2) - latent = (latent + temp_pos_embed).to(latent.dtype) + video_latent = (video_latent + temp_pos_embed).to(video_latent.dtype) + video_latent = rearrange(video_latent, 'b t n c -> b (t n) c') - latent = rearrange(latent, 'b t n c -> b (t n) c') - return latent + return video_latent + class Attention(Attention_): @@ -384,21 +411,21 @@ def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_th processor = AttnProcessor2_0(attention_mode=attention_mode, use_rope=use_rope, interpolation_scale_thw=interpolation_scale_thw) super().__init__(processor=processor, **kwags) self.downsampler = None - if downsampler: # downsampler k155_s122 - downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 - down_factor = list(re.search(r's(\d{2,3})', downsampler).group(1)) - downsampler_ker_size = [int(i) for i in downsampler_ker_size] - downsampler_padding = [(i - 1) // 2 for i in downsampler_ker_size] - down_factor = [int(i) for i in down_factor] + # if downsampler: # downsampler k155_s122 + # downsampler_ker_size = list(re.search(r'k(\d{2,3})', downsampler).group(1)) # 122 + # down_factor = list(re.search(r's(\d{2,3})', downsampler).group(1)) + # downsampler_ker_size = [int(i) for i in downsampler_ker_size] + # downsampler_padding = [(i - 1) // 2 for i in downsampler_ker_size] + # down_factor = [int(i) for i in down_factor] - if len(downsampler_ker_size) == 2: - self.downsampler = DownSampler2d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, - padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, - down_shortcut=True) - elif len(downsampler_ker_size) == 3: - self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, - padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, - down_shortcut=True) + # if len(downsampler_ker_size) == 2: + # self.downsampler = DownSampler2d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + # padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + # down_shortcut=True) + # elif len(downsampler_ker_size) == 3: + # self.downsampler = DownSampler3d(kwags['query_dim'], kwags['query_dim'], kernel_size=downsampler_ker_size, stride=1, + # padding=downsampler_padding, groups=kwags['query_dim'], down_factor=down_factor, + # down_shortcut=True) # self.q_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) # self.k_norm = nn.LayerNorm(kwags['dim_head'], elementwise_affine=True, eps=1e-6) @@ -540,10 +567,10 @@ def __call__( # (batch, heads, source_length, target_length) attention_mask = attention_mask.view(batch_size, attn.heads, -1, attention_mask.shape[-1]) else: - attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) - attention_mask = attention_mask.repeat(1, 1, hidden_states.shape[1], 1) if npu_config.enable_FA: attention_mask = attention_mask.to(torch.bool) + attention_mask = attention_mask.view(batch_size, 1, -1, attention_mask.shape[-1]) + attention_mask = attention_mask.repeat(1, 1, hidden_states.shape[1], 1) if attn.group_norm is not None: hidden_states = attn.group_norm(hidden_states.transpose(1, 2)).transpose(1, 2) @@ -573,8 +600,7 @@ def __call__( # require the shape of (batch_size x nheads x ntokens x dim) pos_thw = self.position_getter(batch_size, t=frame, h=height, w=width, device=query.device) query = self.rope(query, pos_thw) - if query.shape == key.shape: - key = self.rope(key, pos_thw) + key = self.rope(key, pos_thw) query = query.view(batch_size, -1, attn.heads * head_dim) key = key.view(batch_size, -1, attn.heads * head_dim) @@ -735,91 +761,20 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): return x, attention_bias, attention_mask -# class Downsample2d(nn.Module): -# def __init__(self, n_feat, is_video_model=False): -# super(Downsample2d, self).__init__() -# self.is_video_model = is_video_model -# Conv = nn.Conv3d if is_video_model else nn.Conv2d -# self.body = nn.Sequential(Conv(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), -# PixelUnshuffle(2, 1)) - -# def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): -# # import ipdb;ipdb.set_trace() -# x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) -# # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') -# # x = F.pad(x, (0, pad_w, 0, pad_h)) -# if npu_config is None: -# x = F.pad(x, (0, pad_w, 0, pad_h)) -# else: -# x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) -# if self.is_video_model: -# x = rearrange(x, '(b t) d h w -> b d t h w', t=frames) -# x = self.body(x) -# if self.is_video_model: -# x = rearrange(x, 'b d t h w -> b (t h w) d', t=frames) -# else: -# x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) - -# attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) -# attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) -# attention_mask = F.max_pool2d(attention_mask.float(), kernel_size=2, stride=2) -# attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) -# attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 - -# return x, attention_bias, attention_mask - -# class Upsample2d(nn.Module): -# def __init__(self, n_feat, is_video_model=False): -# super(Upsample2d, self).__init__() -# self.is_video_model = is_video_model -# Conv = nn.Conv3d if is_video_model else nn.Conv2d -# self.body = nn.Sequential(Conv(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), -# PixelShuffle(2, 1)) - -# def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): -# if self.is_video_model: -# x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) -# else: -# x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) -# x = self.body(x) -# if self.is_video_model: -# x = rearrange(x, 'b d t h w -> (b t) d h w') -# x = x[:, :, :height*2-pad_h, :width*2-pad_w] -# x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) - -# attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) -# attention_mask = attention_mask.repeat_interleave(2, -1).repeat_interleave(2, -2) -# attention_mask = attention_mask[:, :, :height*2-pad_h, :width*2-pad_w] -# attention_mask = rearrange(attention_mask, '(b t) 1 h w -> b 1 (t h w)', t=frames) -# attention_bias = (1 - attention_mask.bool().to(x.dtype)) * -10000.0 - -# return x, attention_bias, attention_mask - - class Downsample2d(nn.Module): def __init__(self, n_feat, is_video_model=False): super(Downsample2d, self).__init__() - self.is_video_model = is_video_model - Conv = nn.Conv3d if is_video_model else nn.Conv2d - self.body = nn.Sequential(Conv(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False), - PixelUnshuffle(2, 1)) + self.body = nn.Conv2d(n_feat, 2*n_feat, kernel_size=3, stride=2, padding=1, bias=False) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): # import ipdb;ipdb.set_trace() x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) - # x = F.pad(x, (0, pad_w, 0, pad_h), mode='reflect') - # x = F.pad(x, (0, pad_w, 0, pad_h)) if npu_config is None: x = F.pad(x, (0, pad_w, 0, pad_h)) else: x = npu_config.run_pad_2d(F.pad, x, pad=(0, pad_w, 0, pad_h)) - if self.is_video_model: - x = rearrange(x, '(b t) d h w -> b d t h w', t=frames) x = self.body(x) - if self.is_video_model: - x = rearrange(x, 'b d t h w -> b (t h w) d', t=frames) - else: - x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) + x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) attention_mask = rearrange(attention_mask, 'b 1 (t h w) -> (b t) 1 h w', t=frames, h=height, w=width) attention_mask = F.pad(attention_mask, (0, pad_w, 0, pad_h)) @@ -832,19 +787,12 @@ def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): class Upsample2d(nn.Module): def __init__(self, n_feat, is_video_model=False): super(Upsample2d, self).__init__() - self.is_video_model = is_video_model - Conv = nn.Conv3d if is_video_model else nn.Conv2d - self.body = nn.Sequential(Conv(n_feat, n_feat * 2, kernel_size=3, stride=1, padding=1, bias=False), - PixelShuffle(2, 1)) + self.body = nn.Conv2d(n_feat, n_feat // 2, kernel_size=3, stride=1, padding=1, bias=False) def forward(self, x, attention_mask, frames, height, width, pad_h=0, pad_w=0): - if self.is_video_model: - x = rearrange(x, 'b (t h w) d -> b d t h w', t=frames, h=height, w=width) - else: - x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + x = rearrange(x, 'b (t h w) d -> (b t) d h w', t=frames, h=height, w=width) + x = torch.nn.functional.interpolate(x, scale_factor=2.0, mode="nearest") x = self.body(x) - if self.is_video_model: - x = rearrange(x, 'b d t h w -> (b t) d h w') x = x[:, :, :height*2-pad_h, :width*2-pad_w] x = rearrange(x, '(b t) d h w -> b (t h w) d', t=frames) @@ -1072,12 +1020,6 @@ def __init__( elif norm_type == "layer_norm_i2vgen": self.norm3 = None - # self.ff = FeedForward_Conv2d( - # downsampler, - # dim, - # hidden_features=mlp_ratio * dim, - # ) - self.ff = FeedForward( dim, dropout=dropout, @@ -1086,7 +1028,7 @@ def __init__( inner_dim=ff_inner_dim, bias=ff_bias, ) - + # 4. Fuser if attention_type == "gated" or attention_type == "gated-text-image": self.fuser = GatedSelfAttentionDense(dim, cross_attention_dim, num_attention_heads, attention_head_dim) @@ -1214,11 +1156,11 @@ def forward( norm_hidden_states = self.norm2(hidden_states) norm_hidden_states = norm_hidden_states * (1 + scale_mlp) + shift_mlp - if self._chunk_size is not None: - # "feed_forward_chunk_size" can be used to save memory - ff_output = _chunked_feed_forward(self.ff, norm_hidden_states, self._chunk_dim, self._chunk_size) - else: - ff_output = self.ff(norm_hidden_states, t=frame, h=height, w=width) + # if self.downsampler: + # ff_output = self.ff(norm_hidden_states, t=frame, h=height, w=width) + # else: + ff_output = self.ff(norm_hidden_states) + if self.norm_type == "ada_norm_zero": ff_output = gate_mlp.unsqueeze(1) * ff_output elif self.norm_type == "ada_norm_single": diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index 5560e9645..db63ba11a 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -79,12 +79,12 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh "A stylish woman walks down a Tokyo street filled with warm glowing neon and animated city signage. She wears a black leather jacket, a long red dress, and black boots, and carries a black purse. She wears sunglasses and red lipstick. She walks confidently and casually. The street is damp and reflective, creating a mirror effect of the colorful lights. Many pedestrians walk about.", "A serene underwater scene featuring a sea turtle swimming through a coral reef. The turtle, with its greenish-brown shell, is the main focus of the video, swimming gracefully towards the right side of the frame. The coral reef, teeming with life, is visible in the background, providing a vibrant and colorful backdrop to the turtle's journey. Several small fish, darting around the turtle, add a sense of movement and dynamism to the scene." ] - # if 'mt5' in args.text_encoder_name: - # validation_prompt_cn = [ - # # "一只戴着墨镜在泳池当救生员的猫咪。", - # "这是一个宁静的水下场景,一只海龟游过珊瑚礁。海龟带着绿褐色的龟壳,优雅地游向画面右侧,成为视频的焦点。背景中的珊瑚礁生机盎然,为海龟的旅程提供了生动多彩的背景。几条小鱼在海龟周围穿梭,为画面增添了动感和活力。" - # ] - # validation_prompt += validation_prompt_cn + if 'mt5' in args.text_encoder_name: + validation_prompt_cn = [ + "一只戴着墨镜在泳池当救生员的猫咪。", + "这是一个宁静的水下场景,一只海龟游过珊瑚礁。海龟带着绿褐色的龟壳,优雅地游向画面右侧,成为视频的焦点。背景中的珊瑚礁生机盎然,为海龟的旅程提供了生动多彩的背景。几条小鱼在海龟周围穿梭,为画面增添了动感和活力。" + ] + validation_prompt += validation_prompt_cn logger.info(f"Running validation....\n") model = accelerator.unwrap_model(model) scheduler = DPMSolverMultistepScheduler() @@ -97,8 +97,8 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh for prompt in validation_prompt: logger.info('Processing the ({}) prompt'.format(prompt)) video = opensora_pipeline( - # positive_prompt.format(prompt), - prompt, + positive_prompt.format(prompt), + # prompt, negative_prompt=negative_prompt, num_frames=args.num_frames, # num_frames=1, @@ -146,9 +146,6 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh for i, (video, prompt) in enumerate(zip(videos, validation_prompt)) ] } - # import ipdb;ipdb.set_trace() - if hasattr(model.pos_embed, 'temp_embed_gate'): - logs.update({'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) tracker.log(logs, step=global_step) del opensora_pipeline @@ -694,15 +691,6 @@ def run(model_input, model_kwargs, prof): sync_gradients_info(loss) if accelerator.is_main_process: - for tracker in accelerator.trackers: - if tracker.name == "wandb": - if progress_info.global_step % args.checkpointing_steps != 0: - if hasattr(model, 'module') and hasattr(model.module.pos_embed, 'temp_embed_gate'): - tracker.log( - {'temp_embed_gate (tanh)': float(model.module.pos_embed.temp_embed_gate.tanh().item())}) - elif hasattr(model, 'pos_embed') and hasattr(model.pos_embed, 'temp_embed_gate'): - tracker.log( - {'temp_embed_gate (tanh)': float(model.pos_embed.temp_embed_gate.tanh().item())}) if progress_info.global_step % args.checkpointing_steps == 0: @@ -710,7 +698,7 @@ def run(model_input, model_kwargs, prof): log_validation(args, model, ae, text_enc.text_enc, train_dataset.tokenizer, accelerator, weight_dtype, progress_info.global_step) - if args.use_ema and npu_config is not None: + if args.use_ema and npu_config is None: # Store the UNet parameters temporarily and load the EMA parameters to perform inference. ema_model.store(model.parameters()) ema_model.copy_to(model.parameters()) @@ -901,8 +889,8 @@ def train_all_epoch(prof_=None): parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") # validation & logs - parser.add_argument("--num_sampling_steps", type=int, default=50) - parser.add_argument('--guidance_scale', type=float, default=2.5) + parser.add_argument("--num_sampling_steps", type=int, default=20) + parser.add_argument('--guidance_scale', type=float, default=4.5) parser.add_argument("--enable_tracker", action="store_true") parser.add_argument("--seed", type=int, default=None, help="A seed for reproducible training.") parser.add_argument("--output_dir", type=str, default=None, help="The output directory where the model predictions and checkpoints will be written.") diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 46b02c43c..9488c86ee 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -9,6 +9,8 @@ import torch from torch.utils.data import Sampler from typing import List +from collections import Counter +import random IMG_EXTENSIONS = ['.jpg', '.JPG', '.jpeg', '.JPEG', '.png', '.PNG'] @@ -263,6 +265,43 @@ def group_frame_and_resolution_fun(indices): raise NotImplementedError return indices +def last_group_frame_fun(shuffled_megabatches, lengths): + re_shuffled_megabatches = [] + for megabatch in shuffled_megabatches: + re_megabatch = [] + for batch in megabatch: + len_each_batch = [lengths[i] for i in batch] + idx_length_dict = dict([*zip(batch, len_each_batch)]) + count_dict = Counter(len_each_batch) + if len(count_dict) != 1: + sorted_by_value = sorted(count_dict.items(), key=lambda item: item[1]) + pick_length = sorted_by_value[-1][0] # the highest frequency + candidate_batch = [idx for idx, length in idx_length_dict.items() if length == pick_length] + random_select_batch = [random.choice(candidate_batch) for i in range(len(len_each_batch) - len(candidate_batch))] + # print(batch, idx_length_dict, count_dict, sorted_by_value, pick_length, candidate_batch, random_select_batch) + batch = candidate_batch + random_select_batch + # print(batch) + re_megabatch.append(batch) + re_shuffled_megabatches.append(re_megabatch) + + + # for megabatch, re_megabatch in zip(shuffled_megabatches, re_shuffled_megabatches): + # for batch, re_batch in zip(megabatch, re_megabatch): + # for i, re_i in zip(batch, re_batch): + # if i != re_i: + # print(i, re_i) + # import ipdb;ipdb.set_trace() + return re_shuffled_megabatches + + +def last_group_resolution_fun(indices): + raise NotImplementedError + return indices + +def last_group_frame_and_resolution_fun(indices): + raise NotImplementedError + return indices + def get_length_grouped_indices(lengths, batch_size, world_size, generator=None, group_frame=False, group_resolution=False, seed=42): # We need to use torch for the random part as a distributed sampler will set the random seed for torch. if generator is None: @@ -295,7 +334,14 @@ def get_length_grouped_indices(lengths, batch_size, world_size, generator=None, indices = torch.randperm(len(megabatches), generator=generator).tolist() shuffled_megabatches = [megabatches[i] for i in indices] + if group_frame and not group_resolution: + shuffled_megabatches = last_group_frame_fun(shuffled_megabatches, lengths) + elif not group_frame and group_resolution: + shuffled_megabatches = last_group_resolution_fun(shuffled_megabatches, indices) + elif group_frame and group_resolution: + shuffled_megabatches = last_group_frame_and_resolution_fun(shuffled_megabatches, indices) # print('\nshuffled_megabatches', shuffled_megabatches) + # import ipdb;ipdb.set_trace() # print('\nshuffled_megabatches len', [lengths[i] for megabatch in shuffled_megabatches for batch in megabatch for i in batch]) return [i for megabatch in shuffled_megabatches for batch in megabatch for i in batch] diff --git a/scripts/accelerate_configs/hostfile b/scripts/accelerate_configs/hostfile index dcbd92493..1b7e0843a 100644 --- a/scripts/accelerate_configs/hostfile +++ b/scripts/accelerate_configs/hostfile @@ -1,32 +1,4 @@ -node30 slots=8 -node004 slots=8 -node005 slots=8 -node006 slots=8 -node007 slots=8 -node008 slots=8 -node010 slots=8 -node012 slots=8 -node013 slots=8 -node019 slots=8 -node026 slots=8 -node027 slots=8 -node028 slots=8 -node029 slots=8 -node031 slots=8 -node059 slots=8 -node033 slots=8 -node034 slots=8 -node035 slots=8 -node037 slots=8 -node056 slots=8 -node060 slots=8 -node061 slots=8 -node062 slots=8 -node064 slots=8 -node063 slots=8 -node003 slots=8 -node058 slots=8 -node109 slots=8 -node114 slots=8 -node011 slots=8 -node066 slots=8 \ No newline at end of file +node065 slots=8 +node068 slots=8 +node040 slots=8 +node103 slots=8 \ No newline at end of file diff --git a/scripts/accelerate_configs/multi_node_example.yaml b/scripts/accelerate_configs/multi_node_example.yaml index e7ef55a9f..faf757137 100644 --- a/scripts/accelerate_configs/multi_node_example.yaml +++ b/scripts/accelerate_configs/multi_node_example.yaml @@ -5,11 +5,11 @@ deepspeed_config: deepspeed_hostfile: scripts/accelerate_configs/hostfile fsdp_config: {} machine_rank: 0 -main_process_ip: 100.64.24.30 +main_process_ip: 100.64.24.65 main_process_port: 29522 main_training_function: main -num_machines: 32 -num_processes: 256 +num_machines: 4 +num_processes: 32 rdzv_backend: static same_network: true tpu_env: [] diff --git a/scripts/text_condition/gpu/sample_video_ddp1.sh b/scripts/text_condition/gpu/sample_video_ddp1.sh deleted file mode 100644 index 08d6cb800..000000000 --- a/scripts/text_condition/gpu/sample_video_ddp1.sh +++ /dev/null @@ -1,23 +0,0 @@ -# --save_img_path "./sample_video_26500ema_61x480p_k333_s122_cfg5.0_step50" \ -# CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ -torchrun --nnodes=1 --nproc_per_node 8 --master_port 29503 \ - -m opensora.sample.sample_t2v_ddp \ - --model_path /storage/ongoing/new/Open-Sora-Plan/bs36xsp2_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.1 \ - --version 65x512x512 \ - --num_frames 29 \ - --height 720 \ - --width 1280 \ - --cache_dir "../cache_dir" \ - --text_encoder_name google/mt5-xxl \ - --text_prompt examples/prompt_list_1.txt \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_video_ddp_29_29x720p_cfg5.0_step50_neg_pos_dmp" \ - --fps 24 \ - --guidance_scale 5.0 \ - --num_sampling_steps 50 \ - --enable_tiling \ - --tile_overlap_factor 0.125 \ - --max_sequence_length 512 \ - --sample_method DPMSolverMultistep \ - --model_type "dit" \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video_hw.sh b/scripts/text_condition/gpu/sample_video_hw.sh deleted file mode 100644 index 673c0443f..000000000 --- a/scripts/text_condition/gpu/sample_video_hw.sh +++ /dev/null @@ -1,19 +0,0 @@ -CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ - --model_path /storage/dataset/hw29/model_ema \ - --version 65x512x512 \ - --num_frames 29 \ - --height 480 \ - --width 640 \ - --cache_dir "../cache_dir" \ - --text_encoder_name google/mt5-xxl \ - --text_prompt examples/prompt_list_1.txt \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --save_img_path "sample_video_29x480p_hw_pndm_ema_fp32" \ - --fps 24 \ - --guidance_scale 5.0 \ - --num_sampling_steps 50 \ - --enable_tiling \ - --max_sequence_length 512 \ - --sample_method PNDM \ - --model_type dit \ No newline at end of file diff --git a/scripts/text_condition/gpu/sample_video_sp.sh b/scripts/text_condition/gpu/sample_video_sp.sh index d66abb75a..1d2af837d 100644 --- a/scripts/text_condition/gpu/sample_video_sp.sh +++ b/scripts/text_condition/gpu/sample_video_sp.sh @@ -1,24 +1,22 @@ -# --save_img_path "./sample_video_26500ema_61x480p_k333_s122_cfg5.0_step50" \ -# CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ torchrun --nnodes=1 --nproc_per_node 8 --master_port 29503 \ -m opensora.sample.sample_t2v_sp \ - --model_path /storage/dataset/hw29/model_ema \ + --model_path /storage/ongoing/new/Open-Sora-Plan/bs32x8x1_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo \ --version 65x512x512 \ --num_frames 29 \ - --height 480 \ - --width 640 \ + --height 720 \ + --width 1280 \ --cache_dir "../cache_dir" \ --text_encoder_name google/mt5-xxl \ --text_prompt examples/prompt_list_2.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "./sample_video_sp_29x720p_cfg10.0_step50_neg_pos_beng" \ + --save_img_path "./sample_video_test" \ --fps 24 \ - --guidance_scale 2.5 \ + --guidance_scale 7.5 \ --num_sampling_steps 100 \ --enable_tiling \ --tile_overlap_factor 0.125 \ --max_sequence_length 512 \ - --sample_method DDIM \ + --sample_method PNDM \ --model_type "dit" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_ds_image_256x256.sh b/scripts/text_condition/gpu/train_ds_image_256x256.sh deleted file mode 100644 index 2a5b0b3ec..000000000 --- a/scripts/text_condition/gpu/train_ds_image_256x256.sh +++ /dev/null @@ -1,54 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32_2node_lr1e-4_snr5_ema_ps22_k33_s11_4convffn_8dvae" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-B/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 512 \ - --max_width 512 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=16 \ - --dataloader_num_workers 20 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="bs32_2node_lr1e-4_snr5_ema_ps22_k33_s11_4convffn_8dvae" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --downsampler "k33_s11" \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/gpu/train_ds_video3d_61x240p.sh b/scripts/text_condition/gpu/train_ds_video3d_61x240p.sh deleted file mode 100644 index 42b387f01..000000000 --- a/scripts/text_condition/gpu/train_ds_video3d_61x240p.sh +++ /dev/null @@ -1,54 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="testnpu3d_" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-B/222 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --sample_rate 1 \ - --num_frames 253 \ - --max_height 720 \ - --max_width 1280 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="testnpu3d_" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --downsampler "k333_s434" \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/gpu/train_imageuditultra_240p_new_rope_fp32.sh b/scripts/text_condition/gpu/train_image_1x240p.sh similarity index 69% rename from scripts/text_condition/gpu/train_imageuditultra_240p_new_rope_fp32.sh rename to scripts/text_condition/gpu/train_image_1x240p.sh index 4c391ccb8..f721b0895 100644 --- a/scripts/text_condition/gpu/train_imageuditultra_240p_new_rope_fp32.sh +++ b/scripts/text_condition/gpu/train_image_1x240p.sh @@ -1,25 +1,32 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjcn_czhan" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 -# NCCL setting export PDSH_RCMD_TYPE=ssh +# NCCL setting +export GLOO_SOCKET_IFNAME=bond0 +export NCCL_SOCKET_IFNAME=bond0 +export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 +export NCCL_IB_GID_INDEX=3 +export NCCL_IB_TC=162 +export NCCL_IB_TIMEOUT=22 export NCCL_PXN_DISABLE=0 export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 +export MKL_NUM_THREADS=1 +# export NCCL_ALGO=Tree accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ + --model UDiTT2V-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data.txt" \ + --video_data "scripts/train_data/video_data_sucai_aes5.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ @@ -33,7 +40,7 @@ accelerate launch \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=16 \ - --dataloader_num_workers 16 \ + --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=1e-4 \ @@ -41,20 +48,19 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=500 \ + --checkpointing_steps=1000 \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ - --tile_overlap_factor 0.125 \ + --tile_overlap_factor 0.0 \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ --noise_offset 0.02 \ --use_rope \ - --downsampler "k33_s22" \ --resume_from_checkpoint="latest" \ - --enable_tracker \ --enable_stable_fp32 \ - --pretrained "bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjcn_czhan/checkpoint-26500/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg" \ No newline at end of file + --ema_decay 0.9999 \ + --enable_tracker \ + --output_dir="bs4x8x16_240p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam" diff --git a/scripts/text_condition/gpu/train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh b/scripts/text_condition/gpu/train_image_1x480p.sh similarity index 68% rename from scripts/text_condition/gpu/train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh rename to scripts/text_condition/gpu/train_image_1x480p.sh index 4bdb5459c..63721f4dd 100644 --- a/scripts/text_condition/gpu/train_new_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh +++ b/scripts/text_condition/gpu/train_image_1x480p.sh @@ -1,7 +1,7 @@ export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" # export WANDB_MODE="offline" export ENTITY="linbin" -export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" +export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" export HF_DATASETS_OFFLINE=1 export TRANSFORMERS_OFFLINE=1 export PDSH_RCMD_TYPE=ssh @@ -14,24 +14,24 @@ export NCCL_IB_TC=162 export NCCL_IB_TIMEOUT=22 export NCCL_PXN_DISABLE=0 export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_ALGO=Ring +export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 export MKL_NUM_THREADS=1 -export NCCL_ALGO=Tree +# export NCCL_ALGO=Tree accelerate launch \ --config_file scripts/accelerate_configs/multi_node_example.yaml \ opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ + --model UDiTT2V-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --video_data "scripts/train_data/video_data_sucai_aes5.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 61 \ + --num_frames 1 \ --max_height 480 \ --max_width 640 \ --interpolation_scale_t 1.0 \ @@ -39,7 +39,7 @@ accelerate launch \ --interpolation_scale_w 2.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=2 \ + --train_batch_size=16 \ --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -48,21 +48,20 @@ accelerate launch \ --lr_warmup_steps=0 \ --mixed_precision="bf16" \ --report_to="wandb" \ - --checkpointing_steps=500 \ + --checkpointing_steps=1000 \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ + --tile_overlap_factor 0.0 \ --snr_gamma 5.0 \ --use_ema \ --ema_start_step 0 \ --cfg 0.1 \ --noise_offset 0.02 \ --use_rope \ - --downsampler "k333_s122" \ --resume_from_checkpoint="latest" \ - --group_frame \ - --speed_factor 1.2 \ - --pretrained "/storage/ongoing/new/image2video_weight/480p_73000_ema_ds_k3_p1_repeat.pt" \ - --output_dir="bs20x8x2_61x480p_lr1e-4_snr5_noioff0.02_new_uditultra122_ds122_rope_qknorm_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" + --enable_stable_fp32 \ + --ema_decay 0.9999 \ + --enable_tracker \ + --pretrained "/storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/bs4x8x16_240p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam/checkpoint-50000/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs4x8x16_480p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam" diff --git a/scripts/text_condition/gpu/train_image_256x256.sh b/scripts/text_condition/gpu/train_image_256x256.sh deleted file mode 100644 index 92c856f6b..000000000 --- a/scripts/text_condition/gpu/train_image_256x256.sh +++ /dev/null @@ -1,53 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example2.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-B/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 256 \ - --max_width 256 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="new_bs32_2node_lr1e-4_snr5_ema_ps22_ds11" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/gpu/train_imageudit_240p.sh b/scripts/text_condition/gpu/train_imageudit_240p.sh deleted file mode 100644 index 4f5131316..000000000 --- a/scripts/text_condition/gpu/train_imageudit_240p.sh +++ /dev/null @@ -1,56 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs64_4node_240p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 240 \ - --max_width 320 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="bs64_4node_240p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/gpu/train_imageudit_240p_debug.sh b/scripts/text_condition/gpu/train_imageudit_240p_debug.sh deleted file mode 100644 index c126f3269..000000000 --- a/scripts/text_condition/gpu/train_imageudit_240p_debug.sh +++ /dev/null @@ -1,55 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="debug" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 240 \ - --max_width 320 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=64 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="debug" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --downsampler "k33_s22" diff --git a/scripts/text_condition/gpu/train_imageudit_240p_new.sh b/scripts/text_condition/gpu/train_imageudit_240p_new.sh deleted file mode 100644 index b840fe73e..000000000 --- a/scripts/text_condition/gpu/train_imageudit_240p_new.sh +++ /dev/null @@ -1,57 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32_4node_240p_lr2e-5_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 240 \ - --max_width 320 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="bs32_4node_240p_lr2e-5_snr5_noioff0.02_ema_udit22_ds22_mt5xxl" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" \ - --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs64_4node_240p_lr1e-4_snr5_noioff0.02_ema_udit22_ds22_mt5xxl/checkpoint-19500/model/diffusion_pytorch_model.safetensors" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_imageudit_480p_prodigy.sh b/scripts/text_condition/gpu/train_imageudit_480p_prodigy.sh deleted file mode 100644 index 4edac02bf..000000000 --- a/scripts/text_condition/gpu/train_imageudit_480p_prodigy.sh +++ /dev/null @@ -1,61 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32_2node_lr1e-4_snr5_ema_ps11_ds11_udit22_prodigy" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example1.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/umt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=32 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --optimizer="prodigy" \ - --learning_rate=1.0 \ - --prodigy_safeguard_warmup=True \ - --prodigy_use_bias_correction=True \ - --adam_beta1=0.9 \ - --adam_beta2=0.99 \ - --adam_weight_decay=0.01 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="bs32_2node_480p_lr1e-4_snr5_ema_ps11_ds11_udit22_prodigy_umt5" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new.sh b/scripts/text_condition/gpu/train_imageuditultra_480p_new.sh deleted file mode 100644 index 2d4c7fd80..000000000 --- a/scripts/text_condition/gpu/train_imageuditultra_480p_new.sh +++ /dev/null @@ -1,59 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data_notext.txt" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=16 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" \ - --checkpoints_total_limit 3 \ - --pretrained "bs32_4node_240p_lr2e-5_snr5_noioff0.02_ema_udit22_ds22_mt5xxl/checkpoint-32500/model/diffusion_pytorch_model.safetensors" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh deleted file mode 100644 index 025d71b67..000000000 --- a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_fp32.sh +++ /dev/null @@ -1,61 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs16_4node_240p_lr2e-5_snr5_noioff0.01_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mj_mjcn_czhan" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=16 \ - --dataloader_num_workers 16 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" \ - --enable_tracker \ - --enable_stable_fp32 \ - --pretrained "bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-30000/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh b/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh deleted file mode 100644 index e37a807ab..000000000 --- a/scripts/text_condition/gpu/train_imageuditultra_480p_new_rope_lora.sh +++ /dev/null @@ -1,59 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs16_4node_240p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_ds22_mt5xxl" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ - opensora/train/train_t2v_diffusers_lora.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --enable_lora \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=16 \ - --dataloader_num_workers 0 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=5 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k33_s22" \ - --resume_from_checkpoint="latest" \ - --enable_tracker \ - --output_dir="debug_lora" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video21d_61x240p.sh b/scripts/text_condition/gpu/train_video21d_61x240p.sh deleted file mode 100644 index a387d4213..000000000 --- a/scripts/text_condition/gpu/train_video21d_61x240p.sh +++ /dev/null @@ -1,53 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="testnpu21d_" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model LatteT2V/122 \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8/" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --sample_rate 1 \ - --num_frames 509 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 0.5 \ - --interpolation_scale_w 0.5 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=2e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=100 \ - --output_dir="testnpu21d_" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --resume_from_checkpoint="latest" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video3d_29x720p.sh b/scripts/text_condition/gpu/train_video3d_29x720p.sh index 4f13b30b5..41e5f0ff9 100644 --- a/scripts/text_condition/gpu/train_video3d_29x720p.sh +++ b/scripts/text_condition/gpu/train_video3d_29x720p.sh @@ -20,18 +20,18 @@ export MKL_NUM_THREADS=1 # export NCCL_ALGO=Tree accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/debug.yaml \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-ROPE-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_panda.txt" \ + --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 29 \ + --num_frames 93 \ --max_height 720 \ --max_width 1280 \ --interpolation_scale_t 1.0 \ @@ -39,8 +39,8 @@ accelerate launch \ --interpolation_scale_w 2.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ + --train_batch_size=4 \ + --dataloader_num_workers 0 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=1e-4 \ @@ -65,5 +65,6 @@ accelerate launch \ --speed_factor 1.0 \ --enable_stable_fp32 \ --ema_decay 0.999 \ + --drop_short_ratio 0.0 \ --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs32xsp2_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo/checkpoint-6500/model_ema/diffusion_pytorch_model.safetensors" \ --output_dir="bs32x8x1_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo" diff --git a/scripts/text_condition/gpu/train_video3d_93x720p.sh b/scripts/text_condition/gpu/train_video3d_93x720p.sh index 267358c1a..216533eff 100644 --- a/scripts/text_condition/gpu/train_video3d_93x720p.sh +++ b/scripts/text_condition/gpu/train_video3d_93x720p.sh @@ -20,13 +20,13 @@ export MKL_NUM_THREADS=1 export NCCL_ALGO=Tree accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-ROPE-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --video_data "scripts/train_data/video_data_sucai.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ @@ -62,8 +62,7 @@ accelerate launch \ --use_rope \ --resume_from_checkpoint="latest" \ --group_frame \ - --speed_factor 1.2 \ --enable_stable_fp32 \ - --ema_decay 0.99 \ + --ema_decay 0.999 \ --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ --output_dir="bs29x8x1_93x720p_lr1e-4_snr5_noioff0.02_ema99_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_video3d_125x480p_sp.sh b/scripts/text_condition/gpu/train_video3d_93x720p_sp.sh similarity index 71% rename from scripts/text_condition/gpu/train_video3d_125x480p_sp.sh rename to scripts/text_condition/gpu/train_video3d_93x720p_sp.sh index f8dbc477a..f706a2e53 100644 --- a/scripts/text_condition/gpu/train_video3d_125x480p_sp.sh +++ b/scripts/text_condition/gpu/train_video3d_93x720p_sp.sh @@ -14,10 +14,10 @@ export NCCL_IB_TC=162 export NCCL_IB_TIMEOUT=22 export NCCL_PXN_DISABLE=0 export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_ALGO=Ring +export NCCL_ALGO=Ring export OMP_NUM_THREADS=1 export MKL_NUM_THREADS=1 -export NCCL_ALGO=Tree +# export NCCL_ALGO=Tree accelerate launch \ --config_file scripts/accelerate_configs/debug.yaml \ @@ -26,17 +26,17 @@ accelerate launch \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --video_data "scripts/train_data/video_data_sucai_aes5.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 125 \ - --max_height 480 \ - --max_width 640 \ + --num_frames 93 \ + --max_height 720 \ + --max_width 1280 \ --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ + --interpolation_scale_h 1.5 \ + --interpolation_scale_w 2.0 \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=1 \ @@ -52,7 +52,7 @@ accelerate launch \ --allow_tf32 \ --model_max_length 512 \ --use_image_num 0 \ - --tile_overlap_factor 0.0 \ + --tile_overlap_factor 0.125 \ --enable_tiling \ --snr_gamma 5.0 \ --use_ema \ @@ -62,8 +62,10 @@ accelerate launch \ --use_rope \ --resume_from_checkpoint="latest" \ --group_frame \ - --speed_factor 1.1 \ + --speed_factor 1.0 \ --enable_stable_fp32 \ - --ema_decay 0.99 \ - --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs36x8x1_125x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2/checkpoint-4500/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs36xsp4_29x720p_lr1e-4_snr5_noioff0.02_ema99_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.1" + --ema_decay 0.999 \ + --sp_size 1 \ + --train_sp_batch_size 8 \ + --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="bs32xsp4_93x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo" diff --git a/scripts/text_condition/gpu/train_video_ip_video21d.sh b/scripts/text_condition/gpu/train_video_ip_video21d.sh deleted file mode 100644 index 59190c858..000000000 --- a/scripts/text_condition/gpu/train_video_ip_video21d.sh +++ /dev/null @@ -1,69 +0,0 @@ -PROJECT="videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_dino_video_from_scratch" -export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" -# # export WANDB_MODE="offline" -export ENTITY="yunyangge" -export PROJECT=$PROJECT -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export TOKENIZERS_PARALLELISM=false -# # NCCL setting IB网卡时用 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_videoip.py \ - --model "LatteT2V-S/122" \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --image_encoder_type "dino" \ - --image_encoder_name "facebook/dinov2-giant" \ - --cache_dir "/storage/cache_dir" \ - --dataset vip \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 65 \ - --use_image_num 4 \ - --max_height 512 \ - --max_width 512 \ - --attention_mode xformers \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=500000 \ - --learning_rate=1e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --enable_tracker \ - --checkpointing_steps=1000 \ - --gradient_checkpointing \ - --output_dir=$PROJECT \ - --allow_tf32 \ - --model_max_length 300 \ - --enable_tiling \ - --validation_dir "validation_dir" \ - --guidance_scale 3.0 \ - --num_sampling_steps 50 \ - --ema_start_step 0 \ - --use_ema \ - --cfg 0.05 \ - --i2v_ratio 0.4 \ - --transition_ratio 0.4 \ - --clear_video_ratio 0.1 \ - --default_text_ratio 0.5 \ - --seed 42 \ - --snr_gamma 5.0 \ - --noise_offset 0.02 \ - --vip_num_attention_heads 16 \ - --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ - --resume_from_checkpoint "latest" \ - # --pretrained_vip_adapter_path "videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_last2layer/checkpoint-50000/model" \ - # --zero_terminal_snr \ - # 基模型权重没有参与训练所以一定要加载 \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video_ip_video21d_clip.sh b/scripts/text_condition/gpu/train_video_ip_video21d_clip.sh deleted file mode 100644 index 1a7d63a31..000000000 --- a/scripts/text_condition/gpu/train_video_ip_video21d_clip.sh +++ /dev/null @@ -1,68 +0,0 @@ -PROJECT="videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_video_from_scratch" -export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" -# # export WANDB_MODE="offline" -export ENTITY="yunyangge" -export PROJECT=$PROJECT -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export TOKENIZERS_PARALLELISM=false -# # NCCL setting IB网卡时用 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_videoip.py \ - --model "LatteT2V-S/122" \ - --text_encoder_name DeepFloyd/t5-v1_1-xxl \ - --image_encoder_type "clip" \ - --image_encoder_name "laion/CLIP-ViT-H-14-laion2B-s32B-b79K" \ - --cache_dir "/storage/cache_dir" \ - --dataset vip \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/CausalVAEModel_4x8x8" \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --sample_rate 1 \ - --num_frames 65 \ - --use_image_num 4 \ - --max_height 512 \ - --max_width 512 \ - --attention_mode xformers \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=500000 \ - --learning_rate=1e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --enable_tracker \ - --checkpointing_steps=1000 \ - --gradient_checkpointing \ - --output_dir=$PROJECT \ - --allow_tf32 \ - --model_max_length 300 \ - --enable_tiling \ - --validation_dir "validation_dir" \ - --guidance_scale 5.0 \ - --num_sampling_steps 50 \ - --ema_start_step 0 \ - --use_ema \ - --cfg 0.05 \ - --i2v_ratio 0.4 \ - --transition_ratio 0.4 \ - --clear_video_ratio 0.1 \ - --default_text_ratio 0.5 \ - --seed 42 \ - --snr_gamma 5.0 \ - --noise_offset 0.02 \ - --vip_num_attention_heads 16 \ - --pretrained "/storage/1.1model/hw_65/model/diffusion_pytorch_model.safetensors" \ - # --pretrained_vip_adapter_path "/storage/gyy/hw/Open-Sora-Plan/videoip_65x512x512_1node_bs32_lr1e-5_snrgamma_noiseoffset_mjsam_clip_dim512/checkpoint-20000/model" \ - # --resume_from_checkpoint "latest" \ - # --zero_terminal_snr \ - # 基模型权重没有参与训练所以一定要加载 \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video3d_125x480p.sh b/scripts/text_condition/gpu/train_videoudit_29x720p.sh similarity index 77% rename from scripts/text_condition/gpu/train_video3d_125x480p.sh rename to scripts/text_condition/gpu/train_videoudit_29x720p.sh index 1f5e8fa1b..eeae7873a 100644 --- a/scripts/text_condition/gpu/train_video3d_125x480p.sh +++ b/scripts/text_condition/gpu/train_videoudit_29x720p.sh @@ -20,20 +20,20 @@ export MKL_NUM_THREADS=1 export NCCL_ALGO=Tree accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-ROPE-L/122 \ + --model UDiTT2V-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ + --video_data "scripts/train_data/video_data_debug.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 125 \ - --max_height 480 \ - --max_width 640 \ + --num_frames 93 \ + --max_height 720 \ + --max_width 1280 \ --interpolation_scale_t 1.0 \ --interpolation_scale_h 1.0 \ --interpolation_scale_w 1.0 \ @@ -62,6 +62,7 @@ accelerate launch \ --use_rope \ --resume_from_checkpoint="latest" \ --group_frame \ - --speed_factor 1.2 \ - --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs36x8x1_125x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" + --enable_stable_fp32 \ + --ema_decay 0.999 \ + --pretrained "/storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/bs4x8x16_240p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam/checkpoint-100/model_ema/diffusion_pytorch_model.safetensors" \ + --output_dir="debug" diff --git a/scripts/text_condition/gpu/train_videouditultra_125x480p.sh b/scripts/text_condition/gpu/train_videouditultra_125x480p.sh deleted file mode 100644 index cda869556..000000000 --- a/scripts/text_condition/gpu/train_videouditultra_125x480p.sh +++ /dev/null @@ -1,59 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs2_1node_125x480p_lr1e-4_snr5_noioff0.02_ema_uditultra22_ds22_mt5xxl" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data_debug.txt" \ - --sample_rate 1 \ - --num_frames 221 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --output_dir="debug" \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --enable_tracker \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --downsampler "k333_s222" \ - --resume_from_checkpoint="latest" \ - --checkpoints_total_limit 3 \ - --tile_overlap_factor 0.125 \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh b/scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh deleted file mode 100644 index 5e044789b..000000000 --- a/scripts/text_condition/gpu/train_videouditultra_125x480p_new_rope_fp32.sh +++ /dev/null @@ -1,63 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs2_1node_72500k_480p_125x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 125 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --adam_weight_decay 0.1 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k333_s122" \ - --resume_from_checkpoint="latest" \ - --enable_tracker \ - --enable_stable_fp32 \ - --group_frame \ - --pretrained 480p_73000_ema_k3_p1_wusun.pt \ - --output_dir="bs1_20node_73000k_480p_125x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh deleted file mode 100644 index 8cac39cd0..000000000 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32.sh +++ /dev/null @@ -1,62 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Tree -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 61 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=5e-5 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k333_s222" \ - --resume_from_checkpoint="latest" \ - --enable_tracker \ - --enable_stable_fp32 \ - --group_frame \ - --pretrained 480p_73000_ema_k3_p1_repeat_wusun.pt \ - --output_dir="bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh deleted file mode 100644 index 281b4032e..000000000 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai.sh +++ /dev/null @@ -1,62 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Tree -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 61 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k333_s222" \ - --resume_from_checkpoint="latest" \ - --enable_stable_fp32 \ - --group_frame \ - --speed_factor 1.2 \ - --pretrained "bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_aesmovie_sucai_speed1.4/checkpoint-7000/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs20x8x2_73k_480p_61x480p_lr1e-4_snr5_noioff0.02_rope_uditultra122_qknorm_ds222_mt5xxl_movie_aes_mo_sucai_mo_speed1.2" \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh deleted file mode 100644 index c7f2b9342..000000000 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_aesmovie_sucai_panda.sh +++ /dev/null @@ -1,68 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 61 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k333_s122" \ - --resume_from_checkpoint="latest" \ - --group_frame \ - --speed_factor 1.2 \ - --pretrained "/storage/ongoing/new/image2video_weight/480p_73000_ema_k3_p1_repeat_wusun.pt" \ - --output_dir="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_uditultra122_ds122_rope_qknorm_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh b/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh deleted file mode 100644 index 1959b64ca..000000000 --- a/scripts/text_condition/gpu/train_videouditultra_61x480p_new_rope_fp32_movie_time_sucai.sh +++ /dev/null @@ -1,62 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs2_4node_72500k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds122_mt5xxl_sucai288w" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -# NCCL setting -export PDSH_RCMD_TYPE=ssh -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_IB_GID_INDEX=3 -export NCCL_ALGO=Tree -export OMP_NUM_THREADS=1 - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTUltraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_movie_time_sucai.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 61 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --downsampler "k333_s222" \ - --resume_from_checkpoint="latest" \ - --enable_tracker \ - --enable_stable_fp32 \ - --group_frame \ - --pretrained "bs2_20node_73000k_480p_61x480p_lr5e-5_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_sucai288w/checkpoint-11500/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs2_20node_73000k_480p_61x480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra122_qknorm_ds222_mt5xxl_movie_time_sucai" \ No newline at end of file diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index 0758b4fd6..03fc24c79 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,2 +1 @@ -/storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json /storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json \ No newline at end of file diff --git a/scripts/train_data/video_data_aesmovie_sucai.txt b/scripts/train_data/video_data_sucai.txt similarity index 68% rename from scripts/train_data/video_data_aesmovie_sucai.txt rename to scripts/train_data/video_data_sucai.txt index ca9aadcd5..31537b50c 100644 --- a/scripts/train_data/video_data_aesmovie_sucai.txt +++ b/scripts/train_data/video_data_sucai.txt @@ -1,9 +1,3 @@ -/storage/dataset/movie,/storage/dataset/movie/bbc01_clips_aes_motion_final_250508.json -/storage/dataset/movie,/storage/dataset/movie/bbc02_clips_aes_motion_final_289778.json -/storage/dataset/movie,/storage/dataset/movie/bbc03_clips_aes_motion_final_519184.json -/storage/dataset/movie,/storage/dataset/movie/bbc04_clips_aes_motion_final_249497.json -/storage/dataset/movie,/storage/dataset/movie/bbc05_clips_aes_motion_final_416548.json -/storage/dataset/movie,/storage/dataset/movie/TV01_clips_aes_motion_final_217419.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95441.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_clipchamp_final_452264.json /storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_coverr_final_3002.json diff --git a/scripts/train_data/video_data_sucai_aes5.txt b/scripts/train_data/video_data_sucai_aes5.txt new file mode 100644 index 000000000..fc4e028c0 --- /dev/null +++ b/scripts/train_data/video_data_sucai_aes5.txt @@ -0,0 +1,9 @@ +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_canva_final_95441_65422_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_clipchamp_final_452264_231178_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_coverr_final_3002_1274_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_istock_final_815070_405120_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_kapwing_final_68473_27757_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_mixkit_final_4490_2634_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_pexels_final_267395_148323_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_pixabay_v2_final_21608_11400_5.0plus.json +/storage/dataset,/storage/dataset/filter_aes/filter_sucai_json/step1.5_storyblocks_final_1270947_825948_5.0plus.json \ No newline at end of file From 4bb88ee1fe6675657581bf933abda59d0913e04b Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Mon, 15 Jul 2024 16:07:17 +0000 Subject: [PATCH 087/134] dynamic training (joint image) --- opensora/dataset/t2v_datasets.py | 208 ++++++++---------- opensora/dataset/t2v_datasets_bak.py | 147 ++++++++----- opensora/utils/dataset_utils.py | 144 ++++-------- .../gpu/train_video3d_29x720p.sh | 2 +- scripts/train_data/merge_data.txt | 2 + tools/get_img_info.py | 76 +++++++ tools/merge_imginfo_to_anno.py | 31 +++ 7 files changed, 335 insertions(+), 275 deletions(-) create mode 100644 scripts/train_data/merge_data.txt create mode 100644 tools/get_img_info.py create mode 100644 tools/merge_imginfo_to_anno.py diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 463c97446..b3c5b08dc 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -64,18 +64,16 @@ def __call__(cls, *args, **kwargs): class DataSetProg(metaclass=SingletonMeta): def __init__(self): - self.vid_cap_list = [] - self.img_cap_list = [] + self.cap_list = [] self.elements = [] self.num_workers = 1 self.n_elements = 0 self.worker_elements = dict() self.n_used_elements = dict() - def set_cap_list(self, num_workers, img_cap_list, vid_cap_list, n_elements): + def set_cap_list(self, num_workers, cap_list, n_elements): self.num_workers = num_workers - self.img_cap_list = img_cap_list - self.vid_cap_list = vid_cap_list + self.cap_list = cap_list self.n_elements = n_elements self.elements = list(range(n_elements)) random.shuffle(self.elements) @@ -143,28 +141,16 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro if not ('mt5' in args.text_encoder_name): self.support_Chinese = False - if self.num_frames != 1: - vid_cap_list = self.get_vid_cap_list() - if self.use_image_num != 0 and not self.use_img_from_vid: - img_cap_list = self.get_img_cap_list() - else: - img_cap_list = [] - else: - img_cap_list = self.get_img_cap_list() - vid_cap_list = [] + cap_list = self.get_cap_list() - if len(vid_cap_list) > 0: - vid_cap_list, self.sample_num_frames = self.define_frame_index(vid_cap_list) - self.lengths = self.sample_num_frames + assert len(cap_list) > 0 + cap_list, self.sample_num_frames = self.define_frame_index(cap_list) + self.lengths = self.sample_num_frames - if self.num_frames != 1: - n_elements = len(vid_cap_list) - else: - n_elements = len(img_cap_list) - dataset_prog.set_cap_list(args.dataloader_num_workers, img_cap_list, vid_cap_list, n_elements) + n_elements = len(cap_list) + dataset_prog.set_cap_list(args.dataloader_num_workers, cap_list, n_elements) - print(f"video length: {len(dataset_prog.vid_cap_list)}", flush=True) - print(f"image length: {len(dataset_prog.img_cap_list)}", flush=True) + print(f"video length: {len(dataset_prog.cap_list)}", flush=True) def set_checkpoint(self, n_used_elements): for i in range(len(dataset_prog.n_used_elements)): @@ -178,26 +164,24 @@ def __getitem__(self, idx): worker_info = get_worker_info() idx = dataset_prog.get_item(worker_info) try: - video_data, image_data = {}, {} - if self.num_frames != 1: - video_data = self.get_video(idx) - if self.use_image_num != 0: - if self.use_img_from_vid: - image_data = self.get_image_from_video(video_data) - else: - image_data = self.get_image(idx) - else: - image_data = self.get_image(idx) # 1 frame video as image - return dict(video_data=video_data, image_data=image_data) + data = self.get_data(idx) + return data except Exception as e: logger.info(f'Error with {e}') # 打印异常堆栈 - if idx in dataset_prog.vid_cap_list: - logger.info(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") + if idx in dataset_prog.cap_list: + logger.info(f"Caught an exception! {dataset_prog.cap_list[idx]}") # traceback.print_exc() # traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) + def get_data(self, idx): + path = dataset_prog.cap_list[idx]['path'] + if path.endswith('.mp4'): + return self.get_video(idx) + else: + return self.get_image(idx) + def get_video(self, idx): # npu_config.print_msg(f"current idx is {idx}") # video = random.choice([random_video_noise(65, 3, 336, 448), random_video_noise(65, 3, 1024, 1024), random_video_noise(65, 3, 360, 480)]) @@ -205,9 +189,9 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = dataset_prog.vid_cap_list[idx]['path'] + video_path = dataset_prog.cap_list[idx]['path'] assert os.path.exists(video_path), f"file {video_path} do not exist!" - # frame_indice = self.vid_cap_list[idx]['sample_frame_index'] + # frame_indice = self.cap_list[idx]['sample_frame_index'] video = self.decord_read(video_path) h, w = video.shape[-2:] @@ -218,7 +202,7 @@ def get_video(self, idx): # video = torch.rand(221, 3, 480, 640) video = video.transpose(0, 1) # T C H W -> C T H W - text = dataset_prog.vid_cap_list[idx]['cap'] + text = dataset_prog.cap_list[idx]['cap'] if not isinstance(text, list): text = [text] text = [random.choice(text)] @@ -235,63 +219,45 @@ def get_video(self, idx): ) input_ids = text_tokens_and_mask['input_ids'] cond_mask = text_tokens_and_mask['attention_mask'] - return dict(video=video, input_ids=input_ids, cond_mask=cond_mask) - - def get_image_from_video(self, video_data): - select_image_idx = np.linspace(0, self.num_frames - 1, self.use_image_num, dtype=int) - assert self.num_frames >= self.use_image_num - image = [video_data['video'][:, i:i + 1] for i in select_image_idx] # num_img [c, 1, h, w] - input_ids = video_data['input_ids'].repeat(self.use_image_num, 1) # self.use_image_num, l - cond_mask = video_data['cond_mask'].repeat(self.use_image_num, 1) # self.use_image_num, l - return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) + return dict(pixel_values=video, input_ids=input_ids, cond_mask=cond_mask) def get_image(self, idx): - idx = idx % len(dataset_prog.img_cap_list) # out of range - image_data = dataset_prog.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] - - image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] - image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] + image_data = dataset_prog.cap_list[idx] # [{'path': path, 'cap': cap}, ...] - # for i in image: - # assert not torch.any(torch.isnan(i)), 'before transform0' - image = [rearrange(i, 'h w c -> c h w').unsqueeze(0) for i in image] # num_img [1 c h w] - # for i in image: - # assert not torch.any(torch.isnan(i)), 'before transform1' + # import ipdb;ipdb.set_trace() + image = Image.open(image_data['path']).convert('RGB') # [h, w, c] + image = torch.from_numpy(np.array(image)) # [h, w, c] + image = rearrange(image, 'h w c -> c h w').unsqueeze(0) # [1 c h w] # for i in image: # h, w = i.shape[-2:] # assert h / w <= 17 / 16 and h / w >= 8 / 16, f'Only image with a ratio (h/w) less than 17/16 and more than 8/16 are supported. But found ratio is {round(h / w, 2)} with the shape of {i.shape}' - image = [self.transform_topcrop(i) if 'human_images' in j['path'] else self.transform(i) for i, j in zip(image, image_data)] # num_img [1 C H W] -> num_img [1 C H W] + image = self.transform_topcrop(image) if 'human_images' in image_data['path'] else self.transform(image) # [1 C H W] -> num_img [1 C H W] - # for i in image: - # assert not torch.any(torch.isnan(i)), 'after transform' # image = [torch.rand(1, 3, 480, 640) for i in image_data] - image = [i.transpose(0, 1) for i in image] # num_img [1 C H W] -> num_img [C 1 H W] + image = image.transpose(0, 1) # [1 C H W] -> [C 1 H W] - caps = [i['cap'] if isinstance(i['cap'], list) else [i['cap']] for i in image_data] - caps = [[random.choice(i)] for i in caps] - text = [text_preprocessing(cap, support_Chinese=self.support_Chinese) for cap in caps] + caps = image_data['cap'] if isinstance(image_data['cap'], list) else [image_data['cap']] + caps = [random.choice(caps)] + text = text_preprocessing(caps, support_Chinese=self.support_Chinese) input_ids, cond_mask = [], [] - for t in text: - t = t if random.random() > self.cfg else "" - text_tokens_and_mask = self.tokenizer( - t, - max_length=self.model_max_length, - padding='max_length', - truncation=True, - return_attention_mask=True, - add_special_tokens=True, - return_tensors='pt' - ) - input_ids.append(text_tokens_and_mask['input_ids']) - cond_mask.append(text_tokens_and_mask['attention_mask']) - input_ids = torch.cat(input_ids) # self.use_image_num, l - cond_mask = torch.cat(cond_mask) # self.use_image_num, l - return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) - - def define_frame_index(self, vid_cap_list): + text = text if random.random() > self.cfg else "" + text_tokens_and_mask = self.tokenizer( + text, + max_length=self.model_max_length, + padding='max_length', + truncation=True, + return_attention_mask=True, + add_special_tokens=True, + return_tensors='pt' + ) + input_ids = text_tokens_and_mask['input_ids'] # 1, l + cond_mask = text_tokens_and_mask['attention_mask'] # 1, l + return dict(pixel_values=image, input_ids=input_ids, cond_mask=cond_mask) + + def define_frame_index(self, cap_list): - new_vid_cap_list = [] + new_cap_list = [] sample_num_frames = [] cnt_too_long = 0 cnt_too_short = 0 @@ -299,7 +265,9 @@ def define_frame_index(self, vid_cap_list): cnt_no_resolution = 0 cnt_resolution_mismatch = 0 cnt_movie = 0 - for i in vid_cap_list: + cnt_img = 0 + for i in cap_list: + path = i['path'] duration = None if i.get('duration', None) is None else float(i.get('duration', None)) fps = None if i.get('fps', None) is None else float(i.get('fps', None)) resolution = i.get('resolution', None) @@ -317,10 +285,11 @@ def define_frame_index(self, vid_cap_list): if not filter_resolution(resolution['height'], resolution['width']): cnt_resolution_mismatch += 1 continue - if self.max_height > resolution['height'] or self.max_width > resolution['width']: + # ignore image resolution mismatch + if path.endswith('.mp4') and (self.max_height > resolution['height'] or self.max_width > resolution['width']): cnt_resolution_mismatch += 1 continue - if fps is not None and duration is not None: + if path.endswith('.mp4') and fps is not None and duration is not None: # import ipdb;ipdb.set_trace() i['num_frames'] = int(fps * duration) # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. @@ -357,15 +326,20 @@ def define_frame_index(self, vid_cap_list): if '/storage/dataset/movie' in i['path']: cnt_movie += 1 i['sample_frame_index'] = frame_indices.tolist() - new_vid_cap_list.append(i) + new_cap_list.append(i) i['sample_num_frames'] = len(i['sample_frame_index']) # will use in dataloader(group sampler) sample_num_frames.append(i['sample_num_frames']) - + elif not path.endswith('.mp4'): # image + cnt_img += 1 + new_cap_list.append(i) + i['sample_num_frames'] = 1 + sample_num_frames.append(i['sample_num_frames']) + # import ipdb;ipdb.set_trace() logger.info(f'no_cap: {cnt_no_cap}, too_long: {cnt_too_long}, too_short: {cnt_too_short}, ' f'no_resolution: {cnt_no_resolution}, resolution_mismatch: {cnt_resolution_mismatch}, ' - f'Counter(sample_num_frames): {Counter(sample_num_frames)}, movie: {cnt_movie}, ' - f'before filter: {len(vid_cap_list)}, after filter: {len(new_vid_cap_list)}') - return new_vid_cap_list, sample_num_frames + f'Counter(sample_num_frames): {Counter(sample_num_frames)}, movie: {cnt_movie}, cnt_img: {cnt_img}, ' + f'before filter: {len(cap_list)}, after filter: {len(new_cap_list)}') + return new_cap_list, sample_num_frames def decord_read(self, path): decord_vr = self.v_decoder(path) @@ -380,7 +354,7 @@ def decord_read(self, path): #import ipdb;ipdb.set_trace() # speed up max_speed_factor = len(frame_indices) / self.num_frames - if self.speed_factor > 1 and max_speed_factor > 1 and not ('/storage/dataset/MagicTime_Data' in path): + if self.speed_factor > 1 and max_speed_factor > 1: speed_factor = random.uniform(1.0, min(self.speed_factor, max_speed_factor)) target_frame_count = int(len(frame_indices) / speed_factor) speed_frame_idx = np.linspace(0, len(frame_indices) - 1, target_frame_count, dtype=int) @@ -422,31 +396,31 @@ def read_jsons(self, data, postfix=".jpg"): cap_lists += sub_list return cap_lists - def get_img_cap_list(self): - use_image_num = self.use_image_num if self.use_image_num != 0 else 1 + # def get_img_cap_list(self): + # use_image_num = self.use_image_num if self.use_image_num != 0 else 1 + # if npu_config is None: + # img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") + # img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + # else: + # img_cap_lists = npu_config.try_load_pickle("img_cap_lists_all", + # lambda: self.read_jsons(self.image_data, postfix=".jpg")) + # img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + # img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] + # return img_cap_lists[:-1] # drop last to avoid error length + + def get_cap_list(self): if npu_config is None: - img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") - img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] + cap_lists = self.read_jsons(self.video_data, postfix=".mp4") else: - img_cap_lists = npu_config.try_load_pickle("img_cap_lists_all", - lambda: self.read_jsons(self.image_data, postfix=".jpg")) - img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] - img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] - return img_cap_lists[:-1] # drop last to avoid error length - - def get_vid_cap_list(self): - if npu_config is None: - vid_cap_lists = self.read_jsons(self.video_data, postfix=".mp4") - else: - vid_cap_lists = npu_config.try_load_pickle("vid_cap_lists5", + cap_lists = npu_config.try_load_pickle("cap_lists5", lambda: self.read_jsons(self.video_data, postfix=".mp4")) - # npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") - vid_cap_lists = vid_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] - vid_cap_lists_final = [] - for item in vid_cap_lists: + # npu_config.print_msg(f"length of cap_lists is {len(cap_lists)}") + cap_lists = cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] + cap_lists_final = [] + for item in cap_lists: if os.path.exists(item['path']) and os.path.getsize(item['path']) > 10240: - vid_cap_lists_final.append(item) - vid_cap_lists = vid_cap_lists_final - npu_config.print_msg(f"length of vid_cap_lists is {len(vid_cap_lists)}") + cap_lists_final.append(item) + cap_lists = cap_lists_final + npu_config.print_msg(f"length of cap_lists is {len(cap_lists)}") - return vid_cap_lists + return cap_lists diff --git a/opensora/dataset/t2v_datasets_bak.py b/opensora/dataset/t2v_datasets_bak.py index 530009dd7..463c97446 100644 --- a/opensora/dataset/t2v_datasets_bak.py +++ b/opensora/dataset/t2v_datasets_bak.py @@ -48,6 +48,59 @@ def random_video_noise(t, c, h, w): vid = vid.to(torch.uint8) return vid + +class SingletonMeta(type): + """ + 这是一个元类,用于创建单例类。 + """ + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + instance = super().__call__(*args, **kwargs) + cls._instances[cls] = instance + return cls._instances[cls] + + +class DataSetProg(metaclass=SingletonMeta): + def __init__(self): + self.vid_cap_list = [] + self.img_cap_list = [] + self.elements = [] + self.num_workers = 1 + self.n_elements = 0 + self.worker_elements = dict() + self.n_used_elements = dict() + + def set_cap_list(self, num_workers, img_cap_list, vid_cap_list, n_elements): + self.num_workers = num_workers + self.img_cap_list = img_cap_list + self.vid_cap_list = vid_cap_list + self.n_elements = n_elements + self.elements = list(range(n_elements)) + random.shuffle(self.elements) + print(f"n_elements: {len(self.elements)}", flush=True) + + for i in range(self.num_workers): + self.n_used_elements[i] = 0 + per_worker = int(math.ceil(len(self.elements) / float(self.num_workers))) + start = i * per_worker + end = min(start + per_worker, len(self.elements)) + self.worker_elements[i] = self.elements[start: end] + + def get_item(self, work_info): + if work_info is None: + worker_id = 0 + else: + worker_id = work_info.id + + idx = self.worker_elements[worker_id][self.n_used_elements[worker_id] % len(self.worker_elements[worker_id])] + self.n_used_elements[worker_id] += 1 + return idx + + +dataset_prog = DataSetProg() + def find_closest_y(x, vae_stride_t=4, model_ds_t=4): if x < 13: return -1 @@ -80,6 +133,9 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro self.model_max_length = args.model_max_length self.cfg = args.cfg self.speed_factor = args.speed_factor + self.max_height = args.max_height + self.max_width = args.max_width + self.drop_short_ratio = args.drop_short_ratio assert self.speed_factor >= 1 self.v_decoder = DecordInit() @@ -88,55 +144,39 @@ def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcro self.support_Chinese = False if self.num_frames != 1: - self.vid_cap_list = self.get_vid_cap_list() + vid_cap_list = self.get_vid_cap_list() if self.use_image_num != 0 and not self.use_img_from_vid: - self.img_cap_list = self.get_img_cap_list() + img_cap_list = self.get_img_cap_list() else: - self.img_cap_list = [] + img_cap_list = [] else: - self.img_cap_list = self.get_img_cap_list() - self.vid_cap_list = [] + img_cap_list = self.get_img_cap_list() + vid_cap_list = [] - if len(self.vid_cap_list) > 0: - self.vid_cap_list, self.sample_num_frames = self.define_frame_index(self.vid_cap_list) + if len(vid_cap_list) > 0: + vid_cap_list, self.sample_num_frames = self.define_frame_index(vid_cap_list) self.lengths = self.sample_num_frames - if npu_config is not None: - self.n_used_elements = 0 - self.elements = list(range(self.__len__())) - self.worker_elements = None - - random.shuffle(self.elements) - logger.info(f"n_elements: {len(self.elements)}") + if self.num_frames != 1: + n_elements = len(vid_cap_list) + else: + n_elements = len(img_cap_list) + dataset_prog.set_cap_list(args.dataloader_num_workers, img_cap_list, vid_cap_list, n_elements) - logger.info(f"video length: {len(self.vid_cap_list)}") - logger.info(f"image length: {len(self.img_cap_list)}") + print(f"video length: {len(dataset_prog.vid_cap_list)}", flush=True) + print(f"image length: {len(dataset_prog.img_cap_list)}", flush=True) def set_checkpoint(self, n_used_elements): - self.n_used_elements = n_used_elements + for i in range(len(dataset_prog.n_used_elements)): + dataset_prog.n_used_elements[i] = n_used_elements def __len__(self): - if self.num_frames != 1: - return len(self.vid_cap_list) - else: - return len(self.img_cap_list) + return dataset_prog.n_elements def __getitem__(self, idx): if npu_config is not None: worker_info = get_worker_info() - if worker_info is None: # single-process data loading, return a regular index - idx = self.elements[self.n_used_elements % len(self.elements)] - self.n_used_elements += 1 - else: # in a worker process - # split workload - if self.worker_elements is None: - per_worker = int(math.ceil(len(self.elements) / float(worker_info.num_workers))) - worker_id = worker_info.id - start = worker_id * per_worker - end = min(start + per_worker, len(self.elements)) - self.worker_elements = self.elements[start:end] - idx = self.worker_elements[self.n_used_elements % len(self.worker_elements)] - self.n_used_elements += 1 + idx = dataset_prog.get_item(worker_info) try: video_data, image_data = {}, {} if self.num_frames != 1: @@ -152,10 +192,8 @@ def __getitem__(self, idx): except Exception as e: logger.info(f'Error with {e}') # 打印异常堆栈 - if idx in self.vid_cap_list: - logger.info(f"Caught an exception! {self.vid_cap_list[idx]}") - if idx in self.img_cap_list: - logger.info(f"Caught an exception! {self.img_cap_list[idx]}") + if idx in dataset_prog.vid_cap_list: + logger.info(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") # traceback.print_exc() # traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) @@ -167,7 +205,7 @@ def get_video(self, idx): # input_ids = torch.ones(1, 120).to(torch.long).squeeze(0) # cond_mask = torch.cat([torch.ones(1, 60).to(torch.long), torch.ones(1, 60).to(torch.long)], dim=1).squeeze(0) - video_path = self.vid_cap_list[idx]['path'] + video_path = dataset_prog.vid_cap_list[idx]['path'] assert os.path.exists(video_path), f"file {video_path} do not exist!" # frame_indice = self.vid_cap_list[idx]['sample_frame_index'] video = self.decord_read(video_path) @@ -180,7 +218,7 @@ def get_video(self, idx): # video = torch.rand(221, 3, 480, 640) video = video.transpose(0, 1) # T C H W -> C T H W - text = self.vid_cap_list[idx]['cap'] + text = dataset_prog.vid_cap_list[idx]['cap'] if not isinstance(text, list): text = [text] text = [random.choice(text)] @@ -208,8 +246,8 @@ def get_image_from_video(self, video_data): return dict(image=image, input_ids=input_ids, cond_mask=cond_mask) def get_image(self, idx): - idx = idx % len(self.img_cap_list) # out of range - image_data = self.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] + idx = idx % len(dataset_prog.img_cap_list) # out of range + image_data = dataset_prog.img_cap_list[idx] # [{'path': path, 'cap': cap}, ...] image = [Image.open(i['path']).convert('RGB') for i in image_data] # num_img [h, w, c] image = [torch.from_numpy(np.array(i)) for i in image] # num_img [h, w, c] @@ -279,16 +317,19 @@ def define_frame_index(self, vid_cap_list): if not filter_resolution(resolution['height'], resolution['width']): cnt_resolution_mismatch += 1 continue + if self.max_height > resolution['height'] or self.max_width > resolution['width']: + cnt_resolution_mismatch += 1 + continue if fps is not None and duration is not None: # import ipdb;ipdb.set_trace() i['num_frames'] = int(fps * duration) # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. - if i['num_frames'] > 6.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) + if i['num_frames'] > 5.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) cnt_too_long += 1 continue - if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage - cnt_too_short += 1 - continue + # if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage + # cnt_too_short += 1 + # continue # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) frame_interval = fps / self.train_fps @@ -297,7 +338,7 @@ def define_frame_index(self, vid_cap_list): frame_indices = frame_indices[frame_indices < i['num_frames']] # comment out it to enable dynamic frames training - if len(frame_indices) < self.num_frames: + if len(frame_indices) < self.num_frames and random.random() < self.drop_short_ratio: cnt_too_short += 1 continue @@ -324,7 +365,6 @@ def define_frame_index(self, vid_cap_list): f'no_resolution: {cnt_no_resolution}, resolution_mismatch: {cnt_resolution_mismatch}, ' f'Counter(sample_num_frames): {Counter(sample_num_frames)}, movie: {cnt_movie}, ' f'before filter: {len(vid_cap_list)}, after filter: {len(new_vid_cap_list)}') - # import ipdb;ipdb.set_trace() return new_vid_cap_list, sample_num_frames def decord_read(self, path): @@ -333,7 +373,7 @@ def decord_read(self, path): fps = decord_vr.get_avg_fps() if decord_vr.get_avg_fps() > 0 else 30.0 # import ipdb;ipdb.set_trace() # resample in case high fps, such as 50/60/90/144 -> train_fps(e.g, 24) - frame_interval = 1.0 if abs(fps - self.train_fps) < 1e-1 else fps / self.train_fps + frame_interval = 1.0 if abs(fps - self.train_fps) < 0.1 else fps / self.train_fps start_frame_idx = 8 if '/storage/dataset/movie' in path else 0 # special video frame_indices = np.arange(start_frame_idx, total_frames, frame_interval).astype(int) frame_indices = frame_indices[frame_indices < total_frames] @@ -357,7 +397,7 @@ def decord_read(self, path): if end_frame_idx == -1: # too short that can not be encoded exactly by videovae raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') frame_indices = frame_indices[:end_frame_idx] - if len(frame_indices) < self.num_frames: + if len(frame_indices) < self.num_frames and self.drop_short_ratio >= 1: raise IndexError(f'video ({path}) has {total_frames} frames, but need to sample {len(frame_indices)} frames ({frame_indices})') video_data = decord_vr.get_batch(frame_indices).asnumpy() video_data = torch.from_numpy(video_data) @@ -375,7 +415,10 @@ def read_jsons(self, data, postfix=".jpg"): for i in range(len(sub_list)): sub_list[i]['path'] = opj(folder, sub_list[i]['path']) if npu_config is not None: - sub_list = filter_json_by_existed_files(folder, sub_list, postfix=postfix) + if "civitai" in anno or "ideogram" in anno or "human" in anno: + sub_list = sub_list[npu_config.get_node_id()::npu_config.get_node_size()] + else: + sub_list = filter_json_by_existed_files(folder, sub_list, postfix=postfix) cap_lists += sub_list return cap_lists @@ -385,7 +428,7 @@ def get_img_cap_list(self): img_cap_lists = self.read_jsons(self.image_data, postfix=".jpg") img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] else: - img_cap_lists = npu_config.try_load_pickle("img_cap_lists", + img_cap_lists = npu_config.try_load_pickle("img_cap_lists_all", lambda: self.read_jsons(self.image_data, postfix=".jpg")) img_cap_lists = [img_cap_lists[i: i + use_image_num] for i in range(0, len(img_cap_lists), use_image_num)] img_cap_lists = img_cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 9488c86ee..a89ca707f 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -59,73 +59,34 @@ def __init__(self, args): self.max_height = args.max_height self.max_width = args.max_width self.ae_stride = args.ae_stride + self.ae_stride_t = args.ae_stride_t self.ae_stride_thw = (self.ae_stride_t, self.ae_stride, self.ae_stride) - self.ae_stride_1hw = (1, self.ae_stride, self.ae_stride) self.patch_size = args.patch_size self.patch_size_t = args.patch_size_t - self.patch_size_thw = (self.patch_size_t, self.patch_size, self.patch_size) - self.patch_size_1hw = (1, self.patch_size, self.patch_size) self.num_frames = args.num_frames self.use_image_num = args.use_image_num self.max_thw = (self.num_frames, self.max_height, self.max_width) - self.max_1hw = (1, self.max_height, self.max_width) def package(self, batch): - - batch_tubes_vid, input_ids_vid, cond_mask_vid = None, None, None - batch_tubes_img, input_ids_img, cond_mask_img = None, None, None - # import ipdb;ipdb.set_trace() - if self.num_frames > 1: - batch_tubes_vid = [i['video_data']['video'] for i in batch] # b [c t h w] - input_ids_vid = torch.stack([i['video_data']['input_ids'] for i in batch]) # b 1 l - cond_mask_vid = torch.stack([i['video_data']['cond_mask'] for i in batch]) # b 1 l - if self.num_frames == 1 or self.use_image_num != 0: - batch_tubes_img = [j for i in batch for j in i['image_data']['image']] # b*num_img [c 1 h w] - input_ids_img = torch.stack([i['image_data']['input_ids'] for i in batch]) # b image_num l - cond_mask_img = torch.stack([i['image_data']['cond_mask'] for i in batch]) # b image_num l - return batch_tubes_vid, input_ids_vid, cond_mask_vid, batch_tubes_img, input_ids_img, cond_mask_img + batch_tubes = [i['pixel_values'] for i in batch] # b [c t h w] + input_ids = torch.stack([i['input_ids'] for i in batch]) # b 1 l + cond_mask = torch.stack([i['cond_mask'] for i in batch]) # b 1 l + return batch_tubes, input_ids, cond_mask def __call__(self, batch): - batch_tubes_vid, input_ids_vid, cond_mask_vid, batch_tubes_img, input_ids_img, cond_mask_img = self.package(batch) + batch_tubes, input_ids, cond_mask = self.package(batch) ds_stride = self.ae_stride * self.patch_size t_ds_stride = self.ae_stride_t * self.patch_size_t - if self.num_frames > 1 and self.use_image_num == 0: - pad_batch_tubes, attention_mask = self.process(batch_tubes_vid, t_ds_stride, ds_stride, - self.max_thw, self.ae_stride_thw, self.patch_size_thw, extra_1=True) - # attention_mask: b t h w - # input_ids, cond_mask = input_ids_vid.squeeze(1), cond_mask_vid.squeeze(1) # b 1 l -> b l - input_ids, cond_mask = input_ids_vid, cond_mask_vid # b 1 l - elif self.num_frames > 1 and self.use_image_num != 0: - raise NotImplementedError - pad_batch_tubes_vid, attention_mask_vid = self.process(batch_tubes_vid, t_ds_stride, ds_stride, - self.max_thw, self.ae_stride_thw, self.patch_size_thw, extra_1=True) - # attention_mask_vid: b t h w - - pad_batch_tubes_img, attention_mask_img = self.process(batch_tubes_img, 1, ds_stride, - self.max_1hw, self.ae_stride_1hw, self.patch_size_1hw, extra_1=False) - pad_batch_tubes_img = rearrange(pad_batch_tubes_img, '(b i) c 1 h w -> b c i h w', i=self.use_image_num) - attention_mask_img = rearrange(attention_mask_img, '(b i) 1 h w -> b i h w', i=self.use_image_num) - pad_batch_tubes = torch.cat([pad_batch_tubes_vid, pad_batch_tubes_img], dim=2) # concat at temporal, video first - # attention_mask_img: b num_img h w - attention_mask = torch.cat([attention_mask_vid, attention_mask_img], dim=1) # b t+num_img h w - input_ids = torch.cat([input_ids_vid, input_ids_img], dim=1) # b 1+num_img hw - cond_mask = torch.cat([cond_mask_vid, cond_mask_img], dim=1) # b 1+num_img hw - else: - # import ipdb;ipdb.set_trace() - pad_batch_tubes_img, attention_mask_img = self.process(batch_tubes_img, 1, ds_stride, - self.max_1hw, self.ae_stride_1hw, self.patch_size_1hw, extra_1=False) - pad_batch_tubes = rearrange(pad_batch_tubes_img, '(b i) c 1 h w -> b c i h w', i=1) - attention_mask = rearrange(attention_mask_img, '(b i) 1 h w -> b i h w', i=1) - input_ids, cond_mask = input_ids_img, cond_mask_img # b 1 l + pad_batch_tubes, attention_mask = self.process(batch_tubes, t_ds_stride, ds_stride, self.max_thw, self.ae_stride_thw) assert not torch.any(torch.isnan(pad_batch_tubes)), 'after pad_batch_tubes' return pad_batch_tubes, attention_mask, input_ids, cond_mask - def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, patch_size_thw, extra_1): + def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw): # pad to max multiple of ds_stride batch_input_size = [i.shape for i in batch_tubes] # [(c t h w), (c t h w)] assert len(batch_input_size) == self.batch_size @@ -135,70 +96,44 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw, p max_w = max([i[3] for i in batch_input_size]) else: max_t, max_h, max_w = max_thw - pad_max_t, pad_max_h, pad_max_w = pad_to_multiple(max_t-1+self.ae_stride_t if extra_1 else max_t, t_ds_stride), \ + pad_max_t, pad_max_h, pad_max_w = pad_to_multiple(max_t-1+self.ae_stride_t, t_ds_stride), \ pad_to_multiple(max_h, ds_stride), \ pad_to_multiple(max_w, ds_stride) - pad_max_t = pad_max_t + 1 - self.ae_stride_t if extra_1 else pad_max_t - each_pad_t_h_w = [[pad_max_t - i.shape[1], - pad_max_h - i.shape[2], - pad_max_w - i.shape[3]] for i in batch_tubes] - pad_batch_tubes = [F.pad(im, - (0, pad_w, - 0, pad_h, - 0, pad_t), value=0) for (pad_t, pad_h, pad_w), im in zip(each_pad_t_h_w, batch_tubes)] + pad_max_t = pad_max_t + 1 - self.ae_stride_t + each_pad_t_h_w = [ + [ + pad_max_t - i.shape[1], + pad_max_h - i.shape[2], + pad_max_w - i.shape[3] + ] for i in batch_tubes + ] + pad_batch_tubes = [ + F.pad(im, (0, pad_w, 0, pad_h, 0, pad_t), value=0) + for (pad_t, pad_h, pad_w), im in zip(each_pad_t_h_w, batch_tubes) + ] pad_batch_tubes = torch.stack(pad_batch_tubes, dim=0) - # make attention_mask - # first_channel_first_frame, first_channel_other_frame = pad_batch_tubes[:, :1, :1], pad_batch_tubes[:, :1, 1:] # first channel to make attention_mask - # attention_mask_first_frame = F.max_pool3d(first_channel_first_frame, kernel_size=(1, *ae_stride_thw[1:]), stride=(1, *ae_stride_thw[1:])) - # if first_channel_other_frame.numel() != 0: - # attention_mask_other_frame = F.max_pool3d(first_channel_other_frame, kernel_size=ae_stride_thw, stride=ae_stride_thw) - # attention_mask = torch.cat([attention_mask_first_frame, attention_mask_other_frame], dim=2) - # else: - # attention_mask = attention_mask_first_frame - # attention_mask = attention_mask[:, 0].bool().float() # b t h w, do not channel max_tube_size = [pad_max_t, pad_max_h, pad_max_w] - max_latent_size = [((max_tube_size[0]-1) // ae_stride_thw[0] + 1) if extra_1 else (max_tube_size[0] // ae_stride_thw[0]), - max_tube_size[1] // ae_stride_thw[1], - max_tube_size[2] // ae_stride_thw[2]] - valid_latent_size = [[int(math.ceil((i[1]-1) / ae_stride_thw[0])) + 1 if extra_1 else int(math.ceil(i[1] / ae_stride_thw[0])), - int(math.ceil(i[2] / ae_stride_thw[1])), - int(math.ceil(i[3] / ae_stride_thw[2]))] for i in batch_input_size] - attention_mask = [F.pad(torch.ones(i, dtype=pad_batch_tubes.dtype), - (0, max_latent_size[2] - i[2], - 0, max_latent_size[1] - i[1], - 0, max_latent_size[0] - i[0]), value=0) for i in valid_latent_size] + max_latent_size = [ + ((max_tube_size[0]-1) // ae_stride_thw[0] + 1), + max_tube_size[1] // ae_stride_thw[1], + max_tube_size[2] // ae_stride_thw[2] + ] + valid_latent_size = [ + [ + int(math.ceil((i[1]-1) / ae_stride_thw[0])) + 1, + int(math.ceil(i[2] / ae_stride_thw[1])), + int(math.ceil(i[3] / ae_stride_thw[2])) + ] for i in batch_input_size] + attention_mask = [ + F.pad(torch.ones(i, dtype=pad_batch_tubes.dtype), (0, max_latent_size[2] - i[2], + 0, max_latent_size[1] - i[1], + 0, max_latent_size[0] - i[0]), value=0) for i in valid_latent_size] attention_mask = torch.stack(attention_mask) # b t h w - if self.batch_size == 1: + if self.batch_size == 1 or self.group_frame: assert torch.all(attention_mask.bool()) - # if self.group_frame: - # if not torch.all(torch.any(attention_mask.flatten(-2), dim=-1)): - # print('batch_input_size', batch_input_size) - # print('max_t, max_h, max_w', max_t, max_h, max_w) - # print('pad_max_t, pad_max_h, pad_max_w', pad_max_t, pad_max_h, pad_max_w) - # print('each_pad_t_h_w', each_pad_t_h_w) - # print('max_tube_size', max_tube_size) - # print('max_latent_size', max_latent_size) - # print('valid_latent_size', valid_latent_size) - # import ipdb;ipdb.set_trace() - # assert torch.all(torch.any(attention_mask.flatten(-2), dim=-1)), "skip special batch" - - # max_tube_size = [pad_max_t, pad_max_h, pad_max_w] - # max_latent_size = [((max_tube_size[0]-1) // ae_stride_thw[0] + 1) if extra_1 else (max_tube_size[0] // ae_stride_thw[0]), - # max_tube_size[1] // ae_stride_thw[1], - # max_tube_size[2] // ae_stride_thw[2]] - # max_patchify_latent_size = [((max_latent_size[0]-1) // patch_size_thw[0] + 1) if extra_1 else (max_latent_size[0] // patch_size_thw[0]), - # max_latent_size[1] // patch_size_thw[1], - # max_latent_size[2] // patch_size_thw[2]] - # valid_patchify_latent_size = [[int(math.ceil((i[1]-1) / t_ds_stride)) + 1 if extra_1 else int(math.ceil(i[1] / t_ds_stride)), - # int(math.ceil(i[2] / ds_stride)), - # int(math.ceil(i[3] / ds_stride))] for i in batch_input_size] - # attention_mask = [F.pad(torch.ones(i), - # (0, max_patchify_latent_size[2] - i[2], - # 0, max_patchify_latent_size[1] - i[1], - # 0, max_patchify_latent_size[0] - i[0]), value=0) for i in valid_patchify_latent_size] - # attention_mask = torch.stack(attention_mask) # b t h w + return pad_batch_tubes, attention_mask class VideoIP_Collate(Collate): @@ -289,8 +224,7 @@ def last_group_frame_fun(shuffled_megabatches, lengths): # for batch, re_batch in zip(megabatch, re_megabatch): # for i, re_i in zip(batch, re_batch): # if i != re_i: - # print(i, re_i) - # import ipdb;ipdb.set_trace() + # print(i, re_i) return re_shuffled_megabatches diff --git a/scripts/text_condition/gpu/train_video3d_29x720p.sh b/scripts/text_condition/gpu/train_video3d_29x720p.sh index 41e5f0ff9..6be37e701 100644 --- a/scripts/text_condition/gpu/train_video3d_29x720p.sh +++ b/scripts/text_condition/gpu/train_video3d_29x720p.sh @@ -26,7 +26,7 @@ accelerate launch \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data_debug.txt" \ + --video_data "scripts/train_data/merge_data.txt" \ --image_data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ diff --git a/scripts/train_data/merge_data.txt b/scripts/train_data/merge_data.txt new file mode 100644 index 000000000..b0b0b8c69 --- /dev/null +++ b/scripts/train_data/merge_data.txt @@ -0,0 +1,2 @@ +/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094_resolution.json +/storage/dataset,/storage/zhubin/video_statistics_data/task1.5/Final_format_dataset_data_v2/step1.5_canva_final_95441.json \ No newline at end of file diff --git a/tools/get_img_info.py b/tools/get_img_info.py new file mode 100644 index 000000000..e68409df4 --- /dev/null +++ b/tools/get_img_info.py @@ -0,0 +1,76 @@ +# import cv2 +# from tqdm import tqdm +# from glob import glob +# import json +# import os + +# def get_image_size(image_path): +# """ +# Given an image path, return its width and height. +# """ +# try: +# image = cv2.imread(image_path) +# height, width = image.shape[:2] +# return height, width +# except Exception as e: +# return None, None + +# image_root = '/storage/dataset/image/human_images/' +# save_root = '/storage/dataset/image' +# os.makedirs(save_root, exist_ok=True) +# save_name = 'human_images_{}_resolution.json' +# all_paths = glob(os.path.join(image_root, '**', f'*.jpg'), recursive=True) +# items = [] +# for i in tqdm(all_paths): +# height, width = get_image_size(i) +# path = i.replace(image_root if image_root.endswith('/') else image_root + '/', '') +# item = dict(path=path, resolution=dict(height=height, width=width)) +# items.append(item) +# with open(os.path.join(save_root, save_name.format(len(items))), 'w') as f: +# json.dump(items, f, indent=2) + + + + +import cv2 +from tqdm import tqdm +from glob import glob +import json +import os +from multiprocessing import Pool + +def get_image_size(image_path): + """ + Given an image path, return its width and height. + """ + try: + image = cv2.imread(image_path) + height, width = image.shape[:2] + return image_path, height, width + except Exception as e: + return image_path, None, None + +def process_image_paths(image_paths): + items = [] + for image_path, height, width in image_paths: + path = image_path.replace(image_root if image_root.endswith('/') else image_root + '/', '') + item = dict(path=path, resolution=dict(height=height, width=width)) + items.append(item) + return items + +if __name__ == '__main__': + image_root = '/storage/dataset/image/sam' + save_root = '/storage/dataset/image' + os.makedirs(save_root, exist_ok=True) + save_name = 'tuzhan_mj_{}_resolution.json' + all_paths = glob(os.path.join(image_root, '**', '*.jpg'), recursive=True) + + num_processes = os.cpu_count() # Use the number of CPU cores + num_processes = 128 # Use the number of CPU cores + with Pool(num_processes) as pool: + results = list(tqdm(pool.imap(get_image_size, all_paths), total=len(all_paths))) + + items = process_image_paths(results) + + with open(os.path.join(save_root, save_name.format(len(items))), 'w') as f: + json.dump(items, f, indent=2) diff --git a/tools/merge_imginfo_to_anno.py b/tools/merge_imginfo_to_anno.py new file mode 100644 index 000000000..982b4ec1d --- /dev/null +++ b/tools/merge_imginfo_to_anno.py @@ -0,0 +1,31 @@ + +from tqdm import tqdm +from glob import glob +import json +import os + +anno_path = '/storage/anno_jsons/human_images_162094.json' +img_info_path = '/storage/dataset/image/human_images_162094_resolution.json' +save_root = '/storage/anno_jsons' +save_name = 'human_images_{}_resolution.json' + + +# anno_path = '/storage/anno_jsons/tuzhan_mj_1712571.json' +# img_info_path = '/storage/dataset/image/tuzhan_mj_4615530_resolution.json' +# save_root = '/storage/anno_jsons' +# save_name = 'tuzhan_mj_{}_resolution.json' + +with open(anno_path, 'r') as f: + anno = json.load(f) +with open(img_info_path, 'r') as f: + img_info = json.load(f) +img_info = {i['path']: i['resolution'] for i in img_info} + +items = [] +cnt = 0 +for i in tqdm(anno): + resolution = img_info[i['path']] + i['resolution'] = resolution + items.append(i) +with open(os.path.join(save_root, save_name.format(len(items))), 'w') as f: + json.dump(items, f, indent=2) \ No newline at end of file From a74bd1a2103dbca5348118f3f22de356ea17eed0 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Tue, 16 Jul 2024 11:52:56 +0000 Subject: [PATCH 088/134] joint sp training --- .gitignore | 2 +- opensora/dataset/t2v_datasets.py | 13 +++-- opensora/sample/sample_t2v.py | 2 +- opensora/train/train_t2v_diffusers.py | 51 +++++++------------ opensora/utils/dataset_utils.py | 20 ++++++-- scripts/text_condition/gpu/sample_image.sh | 10 ++-- ...d_29x720p.sh => train_video3d_anyx720p.sh} | 13 ++--- 7 files changed, 55 insertions(+), 56 deletions(-) rename scripts/text_condition/gpu/{train_video3d_29x720p.sh => train_video3d_anyx720p.sh} (86%) diff --git a/.gitignore b/.gitignore index 48e638889..4e56543b5 100644 --- a/.gitignore +++ b/.gitignore @@ -49,7 +49,7 @@ bucket.py whileinf.py validation_dir/ inpaint*/ - +bs32x8x1* *tmp* *pymp* check.py diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index b3c5b08dc..764297c47 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -100,12 +100,12 @@ def get_item(self, work_info): dataset_prog = DataSetProg() def find_closest_y(x, vae_stride_t=4, model_ds_t=4): - if x < 13: + if x < 29: return -1 for y in range(x, 12, -1): if (y - 1) % vae_stride_t == 0 and ((y - 1) // vae_stride_t + 1) % model_ds_t == 0: # 4, 8: y in [29, 61, 93, 125, 157, 189, 221, 253, 285, 317, 349, 381, 413, 445, 477, 509, ...] - # 4, 4: y in [13, 29, 45, 61, 77, 93, 109, 125, 141, 157, 173, 189, 205, 221, 237, 253, 269, 285, 301, 317, 333, 349, 365, 381, 397, 413, 429, 445, 461, 477, 493, 509, ...] + # 4, 4: y in [29, 45, 61, 77, 93, 109, 125, 141, 157, 173, 189, 205, 221, 237, 253, 269, 285, 301, 317, 333, 349, 365, 381, 397, 413, 429, 445, 461, 477, 493, 509, ...] return y return -1 @@ -118,8 +118,7 @@ def filter_resolution(h, w, max_h_div_w_ratio=17/16, min_h_div_w_ratio=8 / 16): class T2V_dataset(Dataset): def __init__(self, args, transform, temporal_sample, tokenizer, transform_topcrop): - self.image_data = args.image_data - self.video_data = args.video_data + self.data = args.data self.num_frames = args.num_frames self.train_fps = args.train_fps self.use_image_num = args.use_image_num @@ -337,7 +336,7 @@ def define_frame_index(self, cap_list): # import ipdb;ipdb.set_trace() logger.info(f'no_cap: {cnt_no_cap}, too_long: {cnt_too_long}, too_short: {cnt_too_short}, ' f'no_resolution: {cnt_no_resolution}, resolution_mismatch: {cnt_resolution_mismatch}, ' - f'Counter(sample_num_frames): {Counter(sample_num_frames)}, movie: {cnt_movie}, cnt_img: {cnt_img}, ' + f'Counter(sample_num_frames): {Counter(sample_num_frames)}, cnt_movie: {cnt_movie}, cnt_img: {cnt_img}, ' f'before filter: {len(cap_list)}, after filter: {len(new_cap_list)}') return new_cap_list, sample_num_frames @@ -410,10 +409,10 @@ def read_jsons(self, data, postfix=".jpg"): def get_cap_list(self): if npu_config is None: - cap_lists = self.read_jsons(self.video_data, postfix=".mp4") + cap_lists = self.read_jsons(self.data, postfix=".mp4") else: cap_lists = npu_config.try_load_pickle("cap_lists5", - lambda: self.read_jsons(self.video_data, postfix=".mp4")) + lambda: self.read_jsons(self.data, postfix=".mp4")) # npu_config.print_msg(f"length of cap_lists is {len(cap_lists)}") cap_lists = cap_lists[npu_config.get_local_rank()::npu_config.N_NPU_PER_NODE] cap_lists_final = [] diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index ec0687e5b..ab9ed334c 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -65,7 +65,7 @@ def main(args): transformer_model = OpenSoraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) elif args.model_type == 'udit': - transformer_model = UDiTUltraT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, ignore_mismatched_sizes=True, + transformer_model = UDiTT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, ignore_mismatched_sizes=True, low_cpu_mem_usage=False, device_map=None, torch_dtype=weight_dtype) else: transformer_model = LatteT2V.from_pretrained(args.model_path, cache_dir=args.cache_dir, low_cpu_mem_usage=False, diff --git a/opensora/train/train_t2v_diffusers.py b/opensora/train/train_t2v_diffusers.py index db63ba11a..d85a25a1c 100644 --- a/opensora/train/train_t2v_diffusers.py +++ b/opensora/train/train_t2v_diffusers.py @@ -25,13 +25,13 @@ import torch_npu from opensora.npu_config import npu_config from opensora.acceleration.parallel_states import initialize_sequence_parallel_state, \ - destroy_sequence_parallel_group, get_sequence_parallel_state + destroy_sequence_parallel_group, get_sequence_parallel_state, set_sequence_parallel_state from opensora.acceleration.communications import prepare_parallel_data, broadcast except: torch_npu = None npu_config = None from opensora.utils.parallel_states import initialize_sequence_parallel_state, \ - destroy_sequence_parallel_group, get_sequence_parallel_state + destroy_sequence_parallel_group, get_sequence_parallel_state, set_sequence_parallel_state from opensora.utils.communications import prepare_parallel_data, broadcast pass import time @@ -98,10 +98,8 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh logger.info('Processing the ({}) prompt'.format(prompt)) video = opensora_pipeline( positive_prompt.format(prompt), - # prompt, negative_prompt=negative_prompt, num_frames=args.num_frames, - # num_frames=1, height=args.max_height, width=args.max_width, num_inference_steps=args.num_sampling_steps, @@ -130,9 +128,7 @@ def log_validation(args, model, vae, text_encoder, tokenizer, accelerator, weigh if tracker.name == "wandb": import wandb if videos.shape[1] == 1: - # assert args.num_frames == 1 images = rearrange(videos, 'b 1 c h w -> (b 1) h w c') - # import ipdb;ipdb.set_trace() logs = { f"{'ema_' if ema else ''}validation": [ wandb.Image(image, caption=f"{i}: {prompt}") @@ -434,9 +430,7 @@ def load_model_hook(models, input_dir): logger.info(f"optimizer: {optimizer}") # Setup data: - logger.info(f'before dataset') train_dataset = getdataset(args) - logger.info(f'after dataset') sampler = LengthGroupedSampler( args.train_batch_size, world_size=accelerator.num_processes, @@ -444,7 +438,6 @@ def load_model_hook(models, input_dir): group_frame=args.group_frame, group_resolution=args.group_resolution, ) if args.group_frame or args.group_resolution else None - logger.info(f'after LengthGroupedSampler') train_dataloader = DataLoader( train_dataset, shuffle=sampler is None, @@ -453,6 +446,7 @@ def load_model_hook(models, input_dir): batch_size=args.train_batch_size, num_workers=args.dataloader_num_workers, sampler=sampler if args.group_frame or args.group_resolution else None, + drop_last=True, # prefetch_factor=4 ) logger.info(f'after train_dataloader') @@ -496,7 +490,7 @@ def load_model_hook(models, input_dir): # Train! total_batch_size = args.train_batch_size * accelerator.num_processes * args.gradient_accumulation_steps - + total_batch_size = total_batch_size // args.sp_size * args.train_sp_batch_size logger.info("***** Running training *****") logger.info(f" Model = {model}") logger.info(f" Num examples = {len(train_dataset)}") @@ -505,7 +499,7 @@ def load_model_hook(models, input_dir): logger.info(f" Total train batch size (w. parallel, distributed & accumulation) = {total_batch_size}") logger.info(f" Gradient Accumulation steps = {args.gradient_accumulation_steps}") logger.info(f" Total optimization steps = {args.max_train_steps}") - logger.info(f" Total parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") + logger.info(f" Total training parameters = {sum(p.numel() for p in model.parameters() if p.requires_grad) / 1e9} B") global_step = 0 first_epoch = 0 @@ -605,9 +599,10 @@ def run(model_input, model_kwargs, prof): device=model_input.device) bsz = model_input.shape[0] + current_step_frame = model_input.shape[2] # Sample a random timestep for each image without bias. timesteps = torch.randint(0, noise_scheduler.config.num_train_timesteps, (bsz,), device=model_input.device) - if get_sequence_parallel_state(): + if current_step_frame != 1 and get_sequence_parallel_state(): # image do not need sp broadcast(timesteps) # Add noise to the model input according to the noise magnitude at each timestep @@ -716,11 +711,8 @@ def run(model_input, model_kwargs, prof): def train_one_step(step_, data_item_, prof_=None): train_loss = 0.0 x, attn_mask, input_ids, cond_mask = data_item_ - # assert torch.all(attn_mask.bool()), 'must all visible' - # Sample noise that we'll add to the latents - # import ipdb;ipdb.set_trace() if args.group_frame or args.group_resolution: - if not torch.all(torch.any(attn_mask.flatten(-2), dim=-1)): + if not args.group_frame: each_latent_frame = torch.any(attn_mask.flatten(-2), dim=-1).int().sum(-1).tolist() # logger.info(f'rank: {accelerator.process_index}, step {step_}, special batch has attention_mask ' # f'each_latent_frame: {each_latent_frame}') @@ -736,27 +728,14 @@ def train_one_step(step_, data_item_, prof_=None): # logger.info(f'rank: {accelerator.process_index}, x: {x.shape}, attn_mask: {attn_mask.shape}') with torch.no_grad(): - # import ipdb;ipdb.set_trace() - # use for loop to avoid OOM, because T5 is too huge... B, N, L = input_ids.shape # B 1+num_images L - # cond_ = torch.stack([text_enc(input_ids[i], cond_mask[i]) for i in range(B)]) # B 1+num_images L D - # use batch inference input_ids_ = input_ids.reshape(-1, L) cond_mask_ = cond_mask.reshape(-1, L) cond = text_enc(input_ids_, cond_mask_) # B 1+num_images L D cond = cond.reshape(B, N, L, -1) - # Map input images to latent space + normalize latents - if args.use_image_num == 0: - x = ae.encode(x) # B C T H W - else: - videos, images = x[:, :, :-args.use_image_num], x[:, :, -args.use_image_num:] - videos = ae.encode(videos) # B C T H W - images = rearrange(images, 'b c t h w -> (b t) c 1 h w') - images = ae.encode(images) - images = rearrange(images, '(b t) c 1 h w -> b c t h w', t=args.use_image_num) - x = torch.cat([videos, images], dim=2) # b c 17+4, h, w + x = ae.encode(x) # B C T H W # def custom_to_video(x: torch.Tensor, fps: float = 2.0, output_file: str = 'output_video.mp4') -> None: # from examples.rec_video import array_to_video @@ -771,6 +750,13 @@ def train_one_step(step_, data_item_, prof_=None): # videos = videos.transpose(0, 1) # custom_to_video(videos.to(torch.float32), fps=24, output_file='tmp.mp4') # import sys;sys.exit() + current_step_frame = x.shape[2] + current_step_sp_state = get_sequence_parallel_state() + if args.sp_size != 1: # enable sp + if current_step_frame == 1: # but image do not need sp + set_sequence_parallel_state(False) + else: + set_sequence_parallel_state(True) if get_sequence_parallel_state(): x, cond, attn_mask, cond_mask, use_image_num = prepare_parallel_data(x, cond, attn_mask, cond_mask, args.use_image_num) @@ -791,6 +777,8 @@ def train_one_step(step_, data_item_, prof_=None): encoder_attention_mask=cond_mask, use_image_num=args.use_image_num) run(x, model_kwargs, prof_) + set_sequence_parallel_state(current_step_sp_state) # in case the next step use sp, which need broadcast(timesteps) + if progress_info.global_step >= args.max_train_steps: return True @@ -841,8 +829,7 @@ def train_all_epoch(prof_=None): # dataset & dataloader parser.add_argument("--dataset", type=str, required=True) - parser.add_argument("--video_data", type=str, required='') - parser.add_argument("--image_data", type=str, default='') + parser.add_argument("--data", type=str, required='') parser.add_argument("--sample_rate", type=int, default=1) parser.add_argument("--train_fps", type=int, default=24) parser.add_argument("--drop_short_ratio", type=float, default=1.0) diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index a89ca707f..3c4408272 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -202,14 +202,21 @@ def group_frame_and_resolution_fun(indices): def last_group_frame_fun(shuffled_megabatches, lengths): re_shuffled_megabatches = [] - for megabatch in shuffled_megabatches: + # print('shuffled_megabatches', len(shuffled_megabatches)) + for i_megabatch, megabatch in enumerate(shuffled_megabatches): re_megabatch = [] - for batch in megabatch: + for i_batch, batch in enumerate(megabatch): + if len(batch) == 0: + # print(i_megabatch, i_batch, megabatch, batch) + re_megabatch.append(batch) + continue + len_each_batch = [lengths[i] for i in batch] idx_length_dict = dict([*zip(batch, len_each_batch)]) count_dict = Counter(len_each_batch) if len(count_dict) != 1: sorted_by_value = sorted(count_dict.items(), key=lambda item: item[1]) + # print(batch, idx_length_dict, count_dict, sorted_by_value) pick_length = sorted_by_value[-1][0] # the highest frequency candidate_batch = [idx for idx, length in idx_length_dict.items() if length == pick_length] random_select_batch = [random.choice(candidate_batch) for i in range(len(len_each_batch) - len(candidate_batch))] @@ -243,7 +250,7 @@ def get_length_grouped_indices(lengths, batch_size, world_size, generator=None, # print('lengths', lengths) indices = torch.randperm(len(lengths), generator=generator).tolist() - # print('indices', indices) + # print('indices', len(indices)) if group_frame and not group_resolution: indices = group_frame_fun(indices, lengths) @@ -251,23 +258,28 @@ def get_length_grouped_indices(lengths, batch_size, world_size, generator=None, indices = group_resolution_fun(indices) elif group_frame and group_resolution: indices = group_frame_and_resolution_fun(indices) + # print('sort indices', len(indices)) # print('sort indices', indices) # print('sort lengths', [lengths[i] for i in indices]) megabatch_size = world_size * batch_size megabatches = [indices[i: i + megabatch_size] for i in range(0, len(lengths), megabatch_size)] + # print('megabatches', len(megabatches)) # print('\nmegabatches', megabatches) megabatches = [sorted(megabatch, key=lambda i: lengths[i], reverse=True) for megabatch in megabatches] - megabatches_len = [[lengths[i] for i in megabatch] for megabatch in megabatches] + # print('sort megabatches', len(megabatches)) + # megabatches_len = [[lengths[i] for i in megabatch] for megabatch in megabatches] # print('\nsorted megabatches', megabatches) # print('\nsorted megabatches_len', megabatches_len) megabatches = [split_to_even_chunks(megabatch, lengths, world_size) for megabatch in megabatches] + # print('nsplit_to_even_chunks megabatches', len(megabatches)) # print('\nsplit_to_even_chunks megabatches', megabatches) # print('\nsplit_to_even_chunks len', [lengths[i] for megabatch in megabatches for batch in megabatch for i in batch]) # return [i for megabatch in megabatches for batch in megabatch for i in batch] indices = torch.randperm(len(megabatches), generator=generator).tolist() shuffled_megabatches = [megabatches[i] for i in indices] + # print('shuffled_megabatches', len(shuffled_megabatches)) if group_frame and not group_resolution: shuffled_megabatches = last_group_frame_fun(shuffled_megabatches, lengths) elif not group_frame and group_resolution: diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index ddf433c02..15ff6695f 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,5 +1,5 @@ CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/checkpoints/bs16_4node_480p_lr1e-4_snr5_noioff0.02_ema_rope_uditultra22_qknorm_ds22_mt5xxl_mjencn_czhan_humanimg/checkpoint-73000/model_ema \ + --model_path /storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/bs4x8x16_480p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam/checkpoint-85000/model_ema \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ @@ -9,11 +9,11 @@ CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ --text_prompt examples/prompt_list_1.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "sample_image_fp32_73000_cfg2.5_step20_480p_pos_neg" \ + --save_img_path "sample_image_fp32_73000_cfg5.0_step25_480p_pos_neg" \ --fps 24 \ - --guidance_scale 2.5 \ - --num_sampling_steps 20 \ + --guidance_scale 5.0 \ + --num_sampling_steps 25 \ --enable_tiling \ --max_sequence_length 512 \ --sample_method DPMSolverMultistep \ - --model_3d \ No newline at end of file + --model_type udit \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video3d_29x720p.sh b/scripts/text_condition/gpu/train_video3d_anyx720p.sh similarity index 86% rename from scripts/text_condition/gpu/train_video3d_29x720p.sh rename to scripts/text_condition/gpu/train_video3d_anyx720p.sh index 6be37e701..e232600d6 100644 --- a/scripts/text_condition/gpu/train_video3d_29x720p.sh +++ b/scripts/text_condition/gpu/train_video3d_anyx720p.sh @@ -20,18 +20,17 @@ export MKL_NUM_THREADS=1 # export NCCL_ALGO=Tree accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ + --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ opensora/train/train_t2v_diffusers.py \ --model OpenSoraT2V-ROPE-L/122 \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/merge_data.txt" \ - --image_data "scripts/train_data/image_data.txt" \ + --data "scripts/train_data/merge_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ - --num_frames 93 \ + --num_frames 29 \ --max_height 720 \ --max_width 1280 \ --interpolation_scale_t 1.0 \ @@ -39,7 +38,7 @@ accelerate launch \ --interpolation_scale_w 2.0 \ --attention_mode xformers \ --gradient_checkpointing \ - --train_batch_size=4 \ + --train_batch_size=1 \ --dataloader_num_workers 0 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ @@ -67,4 +66,6 @@ accelerate launch \ --ema_decay 0.999 \ --drop_short_ratio 0.0 \ --pretrained "/storage/ongoing/new/Open-Sora-Plan/bs32xsp2_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo/checkpoint-6500/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs32x8x1_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo" + --output_dir="debug" \ + --sp_size 8 \ + --train_sp_batch_size 2 From 80b05a633f513808a7f4fe59623fc2e47bcd732f Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Tue, 16 Jul 2024 12:34:35 +0000 Subject: [PATCH 089/134] fix define dataset --- opensora/dataset/t2v_datasets.py | 45 +++++++++++-------- .../text_condition/gpu/train_image_1x480p.sh | 3 +- .../gpu/train_video3d_anyx720p.sh | 2 +- tools/get_img_info.py | 2 +- tools/merge_imginfo_to_anno.py | 16 ++++--- 5 files changed, 41 insertions(+), 27 deletions(-) diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 764297c47..3461db5eb 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -267,32 +267,39 @@ def define_frame_index(self, cap_list): cnt_img = 0 for i in cap_list: path = i['path'] - duration = None if i.get('duration', None) is None else float(i.get('duration', None)) - fps = None if i.get('fps', None) is None else float(i.get('fps', None)) - resolution = i.get('resolution', None) cap = i.get('cap', None) + # ======no caption===== if cap is None: cnt_no_cap += 1 continue - if resolution is None: - cnt_no_resolution += 1 - continue - else: - if resolution.get('height', None) is None or resolution.get('width', None) is None: - cnt_no_resolution += 1 + if path.endswith('.mp4'): + # ======no fps and duration===== + duration = i.get('duration', None) + fps = i.get('fps', None) + if fps is None or duration is None: continue - if not filter_resolution(resolution['height'], resolution['width']): - cnt_resolution_mismatch += 1 - continue - # ignore image resolution mismatch - if path.endswith('.mp4') and (self.max_height > resolution['height'] or self.max_width > resolution['width']): - cnt_resolution_mismatch += 1 + + # ======resolution mismatch===== + resolution = i.get('resolution', None) + if resolution is None: + cnt_no_resolution += 1 continue - if path.endswith('.mp4') and fps is not None and duration is not None: + else: + if resolution.get('height', None) is None or resolution.get('width', None) is None: + cnt_no_resolution += 1 + continue + if not filter_resolution(resolution['height'], resolution['width']): + cnt_resolution_mismatch += 1 + continue + # ignore image resolution mismatch + if self.max_height > resolution['height'] or self.max_width > resolution['width']: + cnt_resolution_mismatch += 1 + continue + # import ipdb;ipdb.set_trace() i['num_frames'] = int(fps * duration) # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. - if i['num_frames'] > 5.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) + if i['num_frames'] > 50000000.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) cnt_too_long += 1 continue # if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage @@ -328,11 +335,13 @@ def define_frame_index(self, cap_list): new_cap_list.append(i) i['sample_num_frames'] = len(i['sample_frame_index']) # will use in dataloader(group sampler) sample_num_frames.append(i['sample_num_frames']) - elif not path.endswith('.mp4'): # image + elif path.endswith('.jpg'): # image cnt_img += 1 new_cap_list.append(i) i['sample_num_frames'] = 1 sample_num_frames.append(i['sample_num_frames']) + else: + raise NameError(f"Unknown file extention {path.split('.')[-1]}, only support .mp4 for video and .jpg for image") # import ipdb;ipdb.set_trace() logger.info(f'no_cap: {cnt_no_cap}, too_long: {cnt_too_long}, too_short: {cnt_too_short}, ' f'no_resolution: {cnt_no_resolution}, resolution_mismatch: {cnt_resolution_mismatch}, ' diff --git a/scripts/text_condition/gpu/train_image_1x480p.sh b/scripts/text_condition/gpu/train_image_1x480p.sh index 63721f4dd..0f852de5d 100644 --- a/scripts/text_condition/gpu/train_image_1x480p.sh +++ b/scripts/text_condition/gpu/train_image_1x480p.sh @@ -26,8 +26,7 @@ accelerate launch \ --text_encoder_name google/mt5-xxl \ --cache_dir "./cache_dir" \ --dataset t2v \ - --video_data "scripts/train_data/video_data_sucai_aes5.txt" \ - --image_data "scripts/train_data/image_data.txt" \ + --data "scripts/train_data/image_data.txt" \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ --sample_rate 1 \ diff --git a/scripts/text_condition/gpu/train_video3d_anyx720p.sh b/scripts/text_condition/gpu/train_video3d_anyx720p.sh index e232600d6..80371c408 100644 --- a/scripts/text_condition/gpu/train_video3d_anyx720p.sh +++ b/scripts/text_condition/gpu/train_video3d_anyx720p.sh @@ -39,7 +39,7 @@ accelerate launch \ --attention_mode xformers \ --gradient_checkpointing \ --train_batch_size=1 \ - --dataloader_num_workers 0 \ + --dataloader_num_workers 10 \ --gradient_accumulation_steps=1 \ --max_train_steps=1000000 \ --learning_rate=1e-4 \ diff --git a/tools/get_img_info.py b/tools/get_img_info.py index e68409df4..f09375f8e 100644 --- a/tools/get_img_info.py +++ b/tools/get_img_info.py @@ -59,7 +59,7 @@ def process_image_paths(image_paths): return items if __name__ == '__main__': - image_root = '/storage/dataset/image/sam' + image_root = '/storage/dataset/image/tuzhan_mj' save_root = '/storage/dataset/image' os.makedirs(save_root, exist_ok=True) save_name = 'tuzhan_mj_{}_resolution.json' diff --git a/tools/merge_imginfo_to_anno.py b/tools/merge_imginfo_to_anno.py index 982b4ec1d..067b7b9ee 100644 --- a/tools/merge_imginfo_to_anno.py +++ b/tools/merge_imginfo_to_anno.py @@ -4,17 +4,23 @@ import json import os -anno_path = '/storage/anno_jsons/human_images_162094.json' -img_info_path = '/storage/dataset/image/human_images_162094_resolution.json' -save_root = '/storage/anno_jsons' -save_name = 'human_images_{}_resolution.json' +# anno_path = '/storage/anno_jsons/human_images_162094.json' +# img_info_path = '/storage/dataset/image/human_images_162094_resolution.json' +# save_root = '/storage/anno_jsons' +# save_name = 'human_images_{}_resolution.json' -# anno_path = '/storage/anno_jsons/tuzhan_mj_1712571.json' +# anno_path = '/storage/anno_jsons/tuzhan_mj_4615265.json' # img_info_path = '/storage/dataset/image/tuzhan_mj_4615530_resolution.json' # save_root = '/storage/anno_jsons' # save_name = 'tuzhan_mj_{}_resolution.json' + +anno_path = '/storage/anno_jsons/sam_image_11185255.json' +img_info_path = '/storage/dataset/image/sam_image_11185362_resolution.json' +save_root = '/storage/anno_jsons' +save_name = 'sam_image_{}_resolution.json' + with open(anno_path, 'r') as f: anno = json.load(f) with open(img_info_path, 'r') as f: From 5b73bea7b04d44814a720fd9edf3cc7d29331ec1 Mon Sep 17 00:00:00 2001 From: yunyangge Date: Wed, 17 Jul 2024 16:20:41 +0800 Subject: [PATCH 090/134] videoip --- error.log | 3 +++ opensora/dataset/t2v_datasets.py | 2 ++ opensora/models/diffusion/opensora/videoip.py | 11 ++++++----- opensora/train/train_videoip.py | 16 +++++++++------- .../text_condition/gpu/train_video_ip_video3d.sh | 11 ++++++----- scripts/train_data/image_data.txt | 1 + 6 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 error.log diff --git a/error.log b/error.log new file mode 100644 index 000000000..1d90d6a11 --- /dev/null +++ b/error.log @@ -0,0 +1,3 @@ +2024-07-16 02:56:56 7267855 Cannot choose from an empty sequence +2024-07-17 01:46:30 3654791 Cannot choose from an empty sequence +2024-07-17 13:20:46 169238 Cannot choose from an empty sequence diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index b68f30857..3bc4fa2ae 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -194,6 +194,8 @@ def __getitem__(self, idx): # 打印异常堆栈 if idx in dataset_prog.vid_cap_list: logger.info(f"Caught an exception! {dataset_prog.vid_cap_list[idx]}") + with open('error.log', 'a') as f: + f.write(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())} {idx} {e}\n") # traceback.print_exc() # traceback.print_stack() return self.__getitem__(random.randint(0, self.__len__() - 1)) diff --git a/opensora/models/diffusion/opensora/videoip.py b/opensora/models/diffusion/opensora/videoip.py index cdb40a1e3..4971c8462 100644 --- a/opensora/models/diffusion/opensora/videoip.py +++ b/opensora/models/diffusion/opensora/videoip.py @@ -53,8 +53,8 @@ def __init__( attention_mode='xformers', vae_scale_factor_t=4, num_frames=93, # when image mode, num_frames = 1; when video mode, num_frames = 93 - max_num_tokens=288, # when 480p, max_num_tokens = 24 * 4 * 3 = 288; when 720p or 1080p, max_num_tokens = 24 * 7 * 4 = 672 - pooled_token_output_size=(16, 12), # when 480p, size=(16, 12); when 720p or 1080p, size=(28, 16) + max_num_tokens=288, # when 480p, max_num_tokens = 24 * 3 * 4 = 288; when 720p or 1080p, max_num_tokens = 24 * 4 * 7 = 672 + pooled_token_output_size=(12, 16), # when 480p, size=(12, 16); when 720p or 1080p, size=(16, 28) interpolation_scale_thw=(1, 1, 1), ): super().__init__() @@ -139,11 +139,12 @@ def forward( # B C F H W input_batch_size, input_frame = hidden_states.shape[0], hidden_states.shape[2] - hidden_states = rearrange(hidden_states, 'b c f h w -> (b f) c h w') + # when 480p, B C F 37 49 -> B C F 12 16; when 720p or 1080p, B C F 37 65 -> B C F 16 28 + hidden_states = rearrange(hidden_states, 'b c f h w -> (b f) c h w') hidden_states = self.avg_pool(hidden_states) # (B F) C H W -> (B F) C h w hidden_states = rearrange(hidden_states, '(b f) c h w -> b f h w c', f=input_frame) hidden_states = self.proj_in(hidden_states) - + if not self.use_rope: temp_pos_embed = self.temp_pos_embed temp_pos_embed = rearrange(temp_pos_embed, 'b f c -> b f 1 1 c') @@ -220,7 +221,7 @@ def __init__( attention_head_dim=72, cross_attention_dim=2304, max_num_tokens=288, - pooled_token_output_size=(16, 12), + pooled_token_output_size=(12, 16), num_attention_layers=[1, 3], use_rope=True, attention_mode='math', diff --git a/opensora/train/train_videoip.py b/opensora/train/train_videoip.py index 24437b611..8badbc699 100644 --- a/opensora/train/train_videoip.py +++ b/opensora/train/train_videoip.py @@ -114,8 +114,8 @@ def __init__( self, image_encoder_out_channels=1536, cross_attention_dim=2304, - num_tokens=16, # when 480p, max_num_tokens = 24 * 4 * 3 = 288; when 720p or 1080p, max_num_tokens = 24 * 7 * 4 = 672 - pooled_token_output_size=(16, 12), + num_tokens=16, # when 480p, max_num_tokens = 24 * 3 * 4 = 288; when 720p or 1080p, max_num_tokens = 24 * 4 * 7 = 672 + pooled_token_output_size=(12, 16), vip_num_attention_heads=16, vip_attention_head_dim=72, vip_num_attention_layers=[1, 3], @@ -313,7 +313,8 @@ def log_validation( model.eval() vip.eval() - scheduler = PNDMScheduler() + # scheduler = PNDMScheduler() + scheduler = DPMSolverMultistepScheduler() pipeline = OpenSoraPipeline( vae=vae, text_encoder=text_encoder, @@ -620,7 +621,7 @@ def main(args): image_encoder_out_channels=1536, cross_attention_dim=2304, num_tokens=12, # NOTE should be modified - pooled_token_output_size=(16, 12), # NOTE should be modified + pooled_token_output_size=(12, 16), # NOTE should be modified, (h, w). when 480p, (12, 16); when 720p or 1080p, (16, 28) vip_num_attention_heads=args.vip_num_attention_heads, # for dinov2 vip_attention_head_dim=72, vip_num_attention_layers=[1, 3], @@ -1218,13 +1219,14 @@ def train_all_epoch(prof_=None): if __name__ == "__main__": parser = argparse.ArgumentParser() - # dataset & dataloader + # dataset & dataloader parser.add_argument("--dataset", type=str, required=True) parser.add_argument("--video_data", type=str, required='') parser.add_argument("--image_data", type=str, default='') parser.add_argument("--sample_rate", type=int, default=1) parser.add_argument("--train_fps", type=int, default=24) - parser.add_argument("--speed_factor", type=float, default=1.5) + parser.add_argument("--drop_short_ratio", type=float, default=1.0) + parser.add_argument("--speed_factor", type=float, default=1.0) parser.add_argument("--num_frames", type=int, default=65) parser.add_argument("--max_height", type=int, default=320) parser.add_argument("--max_width", type=int, default=240) @@ -1261,6 +1263,7 @@ def train_all_epoch(prof_=None): # diffusion setting parser.add_argument("--snr_gamma", type=float, default=None, help="SNR weighting gamma to be used if rebalancing the loss. Recommended value is 5.0. More details here: https://arxiv.org/abs/2303.09556.") parser.add_argument("--use_ema", action="store_true", help="Whether to use EMA model.") + parser.add_argument("--ema_decay", type=float, default=0.999) parser.add_argument("--ema_start_step", type=int, default=0) parser.add_argument("--noise_offset", type=float, default=0.02, help="The scale of noise offset.") parser.add_argument("--prediction_type", type=str, default=None, help="The prediction_type that shall be used for training. Choose between 'epsilon' or 'v_prediction' or leave `None`. If left to `None` the default prediction type of the scheduler: `noise_scheduler.config.prediciton_type` is chosen.") @@ -1342,7 +1345,6 @@ def train_all_epoch(prof_=None): parser.add_argument("--local_rank", type=int, default=-1, help="For distributed training: local_rank") parser.add_argument("--sp_size", type=int, default=1, help="For sequence parallel") parser.add_argument("--train_sp_batch_size", type=int, default=1, help="Batch size for sequence parallel training") - parser.add_argument("--ema_decay", type=float, default=0.999) # inpaint dataset parser.add_argument("--i2v_ratio", type=float, default=0.5) # for inpainting mode diff --git a/scripts/text_condition/gpu/train_video_ip_video3d.sh b/scripts/text_condition/gpu/train_video_ip_video3d.sh index 5de59e479..08acaf074 100644 --- a/scripts/text_condition/gpu/train_video_ip_video3d.sh +++ b/scripts/text_condition/gpu/train_video_ip_video3d.sh @@ -1,10 +1,11 @@ PROJECT="videoip_3d_480p_bs8x16_lr1e-5_snrgamma5_0_noiseoffset0_02_dino518_ema0_999" +# PROJECT="videoip_3d_480p_test" export WANDB_API_KEY="720d886d8c437c2142c88056a1eab8ef78d64a1f" export WANDB_MODE="offline" export ENTITY="yunyangge" export PROJECT=$PROJECT -# export HF_DATASETS_OFFLINE=1 -# export TRANSFORMERS_OFFLINE=1 +export HF_DATASETS_OFFLINE=1 +export TRANSFORMERS_OFFLINE=1 export TOKENIZERS_PARALLELISM=false # # NCCL setting IB网卡时用 export NCCL_PXN_DISABLE=0 @@ -47,14 +48,14 @@ accelerate launch \ --report_to="wandb" \ --enable_tracker \ --checkpointing_steps=1000 \ - --output_dir=$PROJECT \ + --output_dir runs/$PROJECT \ --allow_tf32 \ --model_max_length 512 \ --enable_tiling \ --tile_overlap_factor 0.125 \ --validation_dir "validation_dir" \ - --guidance_scale 5.0 \ - --num_sampling_steps 50 \ + --guidance_scale 2.5 \ + --num_sampling_steps 25 \ --ema_start_step 0 \ --use_ema \ --cfg 0.05 \ diff --git a/scripts/train_data/image_data.txt b/scripts/train_data/image_data.txt index dbbb2a964..818b243c9 100644 --- a/scripts/train_data/image_data.txt +++ b/scripts/train_data/image_data.txt @@ -1,2 +1,3 @@ +/storage/dataset/image/human_images,/storage/anno_jsons/human_images_162094.json /storage/dataset/image/sam,/storage/anno_jsons/sam_image_11185255.json /storage/dataset/image/tuzhan_mj,/storage/anno_jsons/tuzhan_mj_4615265.json \ No newline at end of file From e4d7741e42957e780259e43e65eac8a56c404e7e Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 18 Jul 2024 07:01:16 +0000 Subject: [PATCH 091/134] clean script and fix collate --- opensora/dataset/t2v_datasets.py | 2 +- .../captioner/caption_refiner/README.md | 38 ------ .../caption_refiner/caption_refiner.py | 122 ------------------ .../dataset/test_videos/captions.json | 1 - .../caption_refiner/demo_for_refiner.py | 28 ---- .../caption_refiner/gpt_combinator.py | 93 ------------- opensora/sample/sample_t2v.py | 4 +- opensora/sample/sample_t2v_ddp.py | 31 ++++- opensora/utils/dataset_utils.py | 2 +- scripts/text_condition/gpu/sample_image.sh | 12 +- ..._video3d_anyx720p.sh => train_anyx720p.sh} | 0 .../text_condition/gpu/train_image_1x240p.sh | 66 ---------- .../text_condition/gpu/train_image_1x480p.sh | 66 ---------- .../gpu/train_video3d_29x720p_sp.sh | 71 ---------- .../gpu/train_video3d_61x480p.sh | 67 ---------- .../gpu/train_video3d_93x720p.sh | 68 ---------- .../gpu/train_video3d_93x720p_sp.sh | 71 ---------- .../gpu/train_videoudit_29x720p.sh | 68 ---------- 18 files changed, 37 insertions(+), 773 deletions(-) delete mode 100644 opensora/models/captioner/caption_refiner/README.md delete mode 100644 opensora/models/captioner/caption_refiner/caption_refiner.py delete mode 100644 opensora/models/captioner/caption_refiner/dataset/test_videos/captions.json delete mode 100644 opensora/models/captioner/caption_refiner/demo_for_refiner.py delete mode 100644 opensora/models/captioner/caption_refiner/gpt_combinator.py rename scripts/text_condition/gpu/{train_video3d_anyx720p.sh => train_anyx720p.sh} (100%) delete mode 100644 scripts/text_condition/gpu/train_image_1x240p.sh delete mode 100644 scripts/text_condition/gpu/train_image_1x480p.sh delete mode 100644 scripts/text_condition/gpu/train_video3d_29x720p_sp.sh delete mode 100644 scripts/text_condition/gpu/train_video3d_61x480p.sh delete mode 100644 scripts/text_condition/gpu/train_video3d_93x720p.sh delete mode 100644 scripts/text_condition/gpu/train_video3d_93x720p_sp.sh delete mode 100644 scripts/text_condition/gpu/train_videoudit_29x720p.sh diff --git a/opensora/dataset/t2v_datasets.py b/opensora/dataset/t2v_datasets.py index 3461db5eb..6706b492b 100644 --- a/opensora/dataset/t2v_datasets.py +++ b/opensora/dataset/t2v_datasets.py @@ -299,7 +299,7 @@ def define_frame_index(self, cap_list): # import ipdb;ipdb.set_trace() i['num_frames'] = int(fps * duration) # max 5.0 and min 1.0 are just thresholds to filter some videos which have suitable duration. - if i['num_frames'] > 50000000.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) + if i['num_frames'] > 2.0 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too long video is not suitable for this training stage (self.num_frames) cnt_too_long += 1 continue # if i['num_frames'] < 1.0/1 * (self.num_frames * fps / self.train_fps * self.speed_factor): # too short video is not suitable for this training stage diff --git a/opensora/models/captioner/caption_refiner/README.md b/opensora/models/captioner/caption_refiner/README.md deleted file mode 100644 index cf1ae266a..000000000 --- a/opensora/models/captioner/caption_refiner/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Refiner for Video Caption - -Transform the short caption annotations from video datasets into the long and detailed caption annotations. - -* Add detailed description for background scene. -* Add detailed description for object attributes, including color, material, pose. -* Add detailed description for object-level spatial relationship. - -## 🛠️ Extra Requirements and Installation - -* openai == 0.28.0 -* jsonlines == 4.0.0 -* nltk == 3.8.1 -* Install the LLaMA-Accessory: - -you also need to download the weight of SPHINX to ./ckpt/ folder - -## 🗝️ Refining - -The refining instruction is in [demo_for_refiner.py](demo_for_refiner.py). - -```bash -python demo_for_refiner.py --root_path $path_to_repo$ --api_key $openai_api_key$ -``` - -### Refining Demos - -```bash -[original caption]: A red mustang parked in a showroom with american flags hanging from the ceiling. -``` - -```bash -[refine caption]: This scene depicts a red Mustang parked in a showroom with American flags hanging from the ceiling. The showroom likely serves as a space for showcasing and purchasing cars, and the Mustang is displayed prominently near the flags and ceiling. The scene also features a large window and other objects. Overall, it seems to take place in a car show or dealership. -``` - -- [ ] Add GPT-3.5-Turbo for caption summarization. ⌛ [WIP] -- [ ] Add LLAVA-1.6. ⌛ [WIP] -- [ ] More descriptions. ⌛ [WIP] \ No newline at end of file diff --git a/opensora/models/captioner/caption_refiner/caption_refiner.py b/opensora/models/captioner/caption_refiner/caption_refiner.py deleted file mode 100644 index 23952f6d9..000000000 --- a/opensora/models/captioner/caption_refiner/caption_refiner.py +++ /dev/null @@ -1,122 +0,0 @@ -import itertools -import numpy as np -from PIL import Image -from PIL import ImageSequence -from nltk import pos_tag, word_tokenize - -from LLaMA2_Accessory.SPHINX import SPHINXModel -from gpt_combinator import caption_summary - -class CaptionRefiner(): - def __init__(self, sample_num, add_detect=True, add_pos=True, add_attr=True, - openai_api_key=None, openai_api_base=None, - ): - self.sample_num = sample_num - self.ADD_DETECTION_OBJ = add_detect - self.ADD_POS = add_pos - self.ADD_ATTR = add_attr - self.openai_api_key = openai_api_key - self.openai_api_base =openai_api_base - - def video_load_split(self, video_path=None): - frame_img_list, sampled_img_list = [], [] - - if ".gif" in video_path: - img = Image.open(video_path) - # process every frame in GIF from to - for frame in ImageSequence.Iterator(img): - frame_np = np.array(frame.copy().convert('RGB').getdata(),dtype=np.uint8).reshape(frame.size[1],frame.size[0],3) - frame_img = Image.fromarray(np.uint8(frame_np)) - frame_img_list.append(frame_img) - elif ".mp4" in video_path: - pass - - # sample frames from the mp4/gif - for i in range(0, len(frame_img_list), int(len(frame_img_list)/self.sample_num)): - sampled_img_list.append(frame_img_list[i]) - - return sampled_img_list # [, ...] - - def caption_refine(self, video_path, org_caption, model_path): - sampled_imgs = self.video_load_split(video_path) - - model = SPHINXModel.from_pretrained( - pretrained_path=model_path, - with_visual=True - ) - - existing_objects, scene_description = [], [] - text = word_tokenize(org_caption) - existing_objects = [word for word,tag in pos_tag(text) if tag in ["NN", "NNS", "NNP"]] - if self.ADD_DETECTION_OBJ: - # Detect the objects and scene in the sampled images - - qas = [["Where is this scene in the picture most likely to take place?", None]] - sc_response = model.generate_response(qas, sampled_imgs[0], max_gen_len=1024, temperature=0.9, top_p=0.5, seed=0) - scene_description.append(sc_response) - - # # Lacking accuracy - # for img in sampled_imgs: - # qas = [["Please detect the objects in the image.", None]] - # response = model.generate_response(qas, img, max_gen_len=1024, temperature=0.9, top_p=0.5, seed=0) - # print(response) - - object_attrs = [] - if self.ADD_ATTR: - # Detailed Description for all the objects in the sampled images - for obj in existing_objects: - obj_attr = [] - for img in sampled_imgs: - qas = [["Please describe the attribute of the {}, including color, position, etc".format(obj), None]] - response = model.generate_response(qas, img, max_gen_len=1024, temperature=0.9, top_p=0.5, seed=0) - obj_attr.append(response) - object_attrs.append({obj : obj_attr}) - - space_relations = [] - if self.ADD_POS: - obj_pairs = list(itertools.combinations(existing_objects, 2)) - # Description for the relationship between each object in the sample images - for obj_pair in obj_pairs: - qas = [["What is the spatial relationship between the {} and the {}? Please describe in lease than twenty words".format(obj_pair[0], obj_pair[1]), None]] - response = model.generate_response(qas, img, max_gen_len=1024, temperature=0.9, top_p=0.5, seed=0) - space_relations.append(response) - - return dict( - org_caption = org_caption, - scene_description = scene_description, - existing_objects = existing_objects, - object_attrs = object_attrs, - space_relations = space_relations, - ) - - def gpt_summary(self, total_captions): - # combine all captions into a detailed long caption - detailed_caption = "" - - if "org_caption" in total_captions.keys(): - detailed_caption += "In summary, "+ total_captions['org_caption'] - - if "scene_description" in total_captions.keys(): - detailed_caption += "We first describe the whole scene. "+total_captions['scene_description'][-1] - - if "existing_objects" in total_captions.keys(): - tmp_sentence = "There are multiple objects in the video, including " - for obj in total_captions['existing_objects']: - tmp_sentence += obj+", " - detailed_caption += tmp_sentence - - # if "object_attrs" in total_captions.keys(): - # caption_summary( - # caption_list="", - # api_key=self.openai_api_key, - # api_base=self.openai_api_base, - # ) - - if "space_relations" in total_captions.keys(): - tmp_sentence = "As for the spatial relationship. " - for sentence in total_captions['space_relations']: tmp_sentence += sentence - detailed_caption += tmp_sentence - - detailed_caption = caption_summary(detailed_caption, self.open_api_key, self.open_api_base) - - return detailed_caption \ No newline at end of file diff --git a/opensora/models/captioner/caption_refiner/dataset/test_videos/captions.json b/opensora/models/captioner/caption_refiner/dataset/test_videos/captions.json deleted file mode 100644 index 098a352f2..000000000 --- a/opensora/models/captioner/caption_refiner/dataset/test_videos/captions.json +++ /dev/null @@ -1 +0,0 @@ -{"video1.gif": "A red mustang parked in a showroom with american flags hanging from the ceiling.", "video2.gif": "An aerial view of a city with a river running through it."} \ No newline at end of file diff --git a/opensora/models/captioner/caption_refiner/demo_for_refiner.py b/opensora/models/captioner/caption_refiner/demo_for_refiner.py deleted file mode 100644 index c7c0bfc5e..000000000 --- a/opensora/models/captioner/caption_refiner/demo_for_refiner.py +++ /dev/null @@ -1,28 +0,0 @@ -import argparse -from caption_refiner import CaptionRefiner -from gpt_combinator import caption_summary, caption_qa - -def parse_args(): - parser = argparse.ArgumentParser(description="question-answer-generation-using-gpt-3") - parser.add_argument("--root_path", required=True, help="The path to repo.") - parser.add_argument("--api_key", required=True, help="OpenAI API key.") - args = parser.parse_args() - return args - -if __name__ == "__main__": - args = parse_args() - myrefiner = CaptionRefiner( - sample_num=6, add_detect=True, add_pos=True, add_attr=True, - openai_api_key = args.api_key, - openai_api_base = "https://one-api.bltcy.top/v1", - ) - - results = myrefiner.caption_refine( - video_path="./dataset/test_videos/video1.gif", - org_caption="A red mustang parked in a showroom with american flags hanging from the ceiling.", - model_path = args.root_path + "/ckpts/SPHINX-Tiny", - ) - - final_caption = myrefiner.gpt_summary(results) - - print(final_caption) diff --git a/opensora/models/captioner/caption_refiner/gpt_combinator.py b/opensora/models/captioner/caption_refiner/gpt_combinator.py deleted file mode 100644 index c0a6f0dff..000000000 --- a/opensora/models/captioner/caption_refiner/gpt_combinator.py +++ /dev/null @@ -1,93 +0,0 @@ -import openai -import ast - -def caption_qa(caption_list, api_key, api_base): - openai.api_key = api_key - openai.api_base = api_base - - question = "What is the color of a red apple" - answer = "red" - pred = "green" - try: - # Compute the correctness score - completion = openai.ChatCompletion.create( - model="gpt-3.5-turbo", - # model="gpt-4", - # model="gpt-4-vision-compatible", - messages=[ - { - "role": "system", - "content": - "You are an intelligent chatbot designed for evaluating the correctness of generative outputs for question-answer pairs. " - "Your task is to compare the predicted answer with the correct answer and determine if they match meaningfully. Here's how you can accomplish the task:" - "------" - "##INSTRUCTIONS: " - "- Focus on the meaningful match between the predicted answer and the correct answer.\n" - "- Consider synonyms or paraphrases as valid matches.\n" - "- Evaluate the correctness of the prediction compared to the answer." - }, - { - "role": "user", - "content": - "Please evaluate the following video-based question-answer pair:\n\n" - f"Question: {question}\n" - f"Correct Answer: {answer}\n" - f"Predicted Answer: {pred}\n\n" - "Provide your evaluation only as a yes/no and score where the score is an integer value between 0 and 5, with 5 indicating the highest meaningful match. " - "Please generate the response in the form of a Python dictionary string with keys 'pred' and 'score', where value of 'pred' is a string of 'yes' or 'no' and value of 'score' is in INTEGER, not STRING." - "DO NOT PROVIDE ANY OTHER OUTPUT TEXT OR EXPLANATION. Only provide the Python dictionary string. " - "For example, your response should look like this: {'pred': 'yes', 'score': 4.8}." - } - ] - ) - # Convert response to a Python dictionary. - response_message = completion["choices"][0]["message"]["content"] - response_dict = ast.literal_eval(response_message) - print(response_dict) - - except Exception as e: - print(f"Error processing file : {e}") - - -def caption_summary(long_caption, api_key, api_base): - """ - apply GPT3-Turbo as the combination for original caption and the prompted captions for a video - """ - openai.api_key = api_key - openai.api_base = api_base - - try: - # Compute the correctness score - completion = openai.ChatCompletion.create( - model="gpt-3.5-turbo", - messages=[ - { - "role": "system", - "content": - "You are an intelligent chatbot designed for summarizing from a long sentence. " - }, - { - "role": "user", - "content": - "Please summarize the following sentences. Make it shorter than 70 words." - f"the long sentence: {long_caption}\n" - "Provide your summarization with less than 70 words. " - "DO NOT PROVIDE ANY OTHER TEXT OR EXPLANATION. Only provide the summary sentence. " - } - ] - ) - # "Please generate the response in the form of a Python dictionary string with keys 'pred' and 'score', where value of 'pred' is a string of 'yes' or 'no' and value of 'score' is in INTEGER, not STRING." - # "DO NOT PROVIDE ANY OTHER OUTPUT TEXT OR EXPLANATION. Only provide the Python dictionary string. " - # "For example, your response should look like this: {'summary': 'your summary sentence'}." - - # Convert response to a Python dictionary. - response_message = completion["choices"][0]["message"]["content"] - response_dict = ast.literal_eval(response_message) - - except Exception as e: - print(f"Error processing file : {e}") - - return response_dict - -if __name__ == "__main__": - caption_summary() \ No newline at end of file diff --git a/opensora/sample/sample_t2v.py b/opensora/sample/sample_t2v.py index ab9ed334c..978b9b66c 100644 --- a/opensora/sample/sample_t2v.py +++ b/opensora/sample/sample_t2v.py @@ -151,10 +151,8 @@ def main(args): text_prompt = [i.strip() for i in text_prompt] positive_prompt = """ - (masterpiece), (best quality), (ultra-detailed), (unwatermarked), + (masterpiece), (best quality), (ultra-detailed), {}. - emotional, harmonious, vignette, 4k epic detailed, shot on kodak, 35mm photo, - sharp focus, high budget, cinemascope, moody, epic, gorgeous """ negative_prompt = """ diff --git a/opensora/sample/sample_t2v_ddp.py b/opensora/sample/sample_t2v_ddp.py index 0cc720838..c26631da0 100644 --- a/opensora/sample/sample_t2v_ddp.py +++ b/opensora/sample/sample_t2v_ddp.py @@ -22,6 +22,7 @@ from opensora.models.diffusion.udit.modeling_udit import UDiTT2V from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V from opensora.models.diffusion.latte.modeling_latte import LatteT2V +from opensora.models.captioner.refiner import model_gen from opensora.models.text_encoder import get_text_enc from opensora.utils.utils import save_video_grid @@ -111,12 +112,27 @@ def run_model_and_save_images(pipeline, model_path): nsfw, lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry. """ + for index, prompt in enumerate(args.text_prompt): if index % world_size != local_rank: continue - # print('Processing the ({}) prompt, device ({})'.format(prompt, device)) - videos = pipeline(positive_prompt.format(prompt), + if args.refine_caption: + q = f'Translate this brief generation prompt into a detailed caption: {prompt}' + query = f'[UNUSED_TOKEN_146]user\n{q}[UNUSED_TOKEN_145]\n[UNUSED_TOKEN_146]assistant\n' + # print(query) + with torch.cuda.amp.autocast(): + refine_prompt = model_gen(refiner, query, None) + refine_prompt = refine_prompt.replace('<|im_end|>', '').replace('', '') + input_prompt = positive_prompt.format(refine_prompt) + print(f'Processing the origin prompt({prompt})\n ' + f'refine_prompt ({refine_prompt})\n input_prompt ({input_prompt})\n device ({device})') + else: + input_prompt = positive_prompt.format(prompt) + print(f'Processing the origin prompt({prompt})\n ' + f'input_prompt ({input_prompt})\n device ({device})') + videos = pipeline( + input_prompt, negative_prompt=negative_prompt, num_frames=args.num_frames, height=args.height, @@ -195,7 +211,9 @@ def get_file_name(): parser.add_argument("--max_sequence_length", type=int, default=512) parser.add_argument("--text_prompt", nargs='+') parser.add_argument('--tile_overlap_factor', type=float, default=0.25) + parser.add_argument("--seed", type=int, default=42) parser.add_argument('--enable_tiling', action='store_true') + parser.add_argument('--refine_caption', action='store_true') parser.add_argument('--compile', action='store_true') parser.add_argument('--model_type', type=str, default="dit", choices=['dit', 'udit', 'latte']) parser.add_argument('--save_memory', action='store_true') @@ -214,7 +232,7 @@ def get_file_name(): torch.cuda.set_device(local_rank) dist.init_process_group(backend='nccl', init_method='env://', world_size=world_size, rank=local_rank) - # torch.manual_seed(args.seed) + torch.manual_seed(args.seed) weight_dtype = torch.bfloat16 device = torch.cuda.current_device() # print(11111111111111111111, local_rank, device) @@ -244,6 +262,13 @@ def get_file_name(): # text_encoder = T5EncoderModel.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir, # low_cpu_mem_usage=True, torch_dtype=weight_dtype).to(device) # tokenizer = T5Tokenizer.from_pretrained(args.text_encoder_name, cache_dir=args.cache_dir) + if args.refine_caption: + from transformers import AutoModel, AutoTokenizer + new_path = '/storage/zhubin/ShareGPT4Video/sharegpt4video/sharecaptioner_v1' + refiner_tokenizer = AutoTokenizer.from_pretrained(new_path, trust_remote_code=True) + refiner = AutoModel.from_pretrained(new_path, torch_dtype=weight_dtype, trust_remote_code=True).eval() + refiner.to(device) + refiner.tokenizer = refiner_tokenizer # set eval mode vae.eval() diff --git a/opensora/utils/dataset_utils.py b/opensora/utils/dataset_utils.py index 3c4408272..e713d88e5 100644 --- a/opensora/utils/dataset_utils.py +++ b/opensora/utils/dataset_utils.py @@ -90,7 +90,7 @@ def process(self, batch_tubes, t_ds_stride, ds_stride, max_thw, ae_stride_thw): # pad to max multiple of ds_stride batch_input_size = [i.shape for i in batch_tubes] # [(c t h w), (c t h w)] assert len(batch_input_size) == self.batch_size - if self.group_frame: + if self.group_frame or self.batch_size: max_t = max([i[1] for i in batch_input_size]) max_h = max([i[2] for i in batch_input_size]) max_w = max([i[3] for i in batch_input_size]) diff --git a/scripts/text_condition/gpu/sample_image.sh b/scripts/text_condition/gpu/sample_image.sh index 15ff6695f..a28841756 100644 --- a/scripts/text_condition/gpu/sample_image.sh +++ b/scripts/text_condition/gpu/sample_image.sh @@ -1,5 +1,5 @@ -CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ - --model_path /storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/bs4x8x16_480p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam/checkpoint-85000/model_ema \ +CUDA_VISIBLE_DEVICES=6 python opensora/sample/sample_t2v.py \ + --model_path /storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/bs4x8x16_480p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam/checkpoint-194000/model_ema \ --version 65x512x512 \ --num_frames 1 \ --height 480 \ @@ -9,11 +9,11 @@ CUDA_VISIBLE_DEVICES=7 python opensora/sample/sample_t2v.py \ --text_prompt examples/prompt_list_1.txt \ --ae CausalVAEModel_4x8x8 \ --ae_path "/storage/dataset/test140k" \ - --save_img_path "sample_image_fp32_73000_cfg5.0_step25_480p_pos_neg" \ + --save_img_path "sample_image_fp32_194000_cfg2.5_step20_480p_pos_neg" \ --fps 24 \ - --guidance_scale 5.0 \ - --num_sampling_steps 25 \ + --guidance_scale 4.5 \ + --num_sampling_steps 20 \ --enable_tiling \ --max_sequence_length 512 \ - --sample_method DPMSolverMultistep \ + --sample_method PNDM \ --model_type udit \ No newline at end of file diff --git a/scripts/text_condition/gpu/train_video3d_anyx720p.sh b/scripts/text_condition/gpu/train_anyx720p.sh similarity index 100% rename from scripts/text_condition/gpu/train_video3d_anyx720p.sh rename to scripts/text_condition/gpu/train_anyx720p.sh diff --git a/scripts/text_condition/gpu/train_image_1x240p.sh b/scripts/text_condition/gpu/train_image_1x240p.sh deleted file mode 100644 index f721b0895..000000000 --- a/scripts/text_condition/gpu/train_image_1x240p.sh +++ /dev/null @@ -1,66 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -# export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_sucai_aes5.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 240 \ - --max_width 320 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=16 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=1000 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.0 \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --resume_from_checkpoint="latest" \ - --enable_stable_fp32 \ - --ema_decay 0.9999 \ - --enable_tracker \ - --output_dir="bs4x8x16_240p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam" diff --git a/scripts/text_condition/gpu/train_image_1x480p.sh b/scripts/text_condition/gpu/train_image_1x480p.sh deleted file mode 100644 index 0f852de5d..000000000 --- a/scripts/text_condition/gpu/train_image_1x480p.sh +++ /dev/null @@ -1,66 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -# export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 1 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 2.0 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=16 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=1000 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.0 \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --resume_from_checkpoint="latest" \ - --enable_stable_fp32 \ - --ema_decay 0.9999 \ - --enable_tracker \ - --pretrained "/storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/bs4x8x16_240p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam/checkpoint-50000/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs4x8x16_480p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam" diff --git a/scripts/text_condition/gpu/train_video3d_29x720p_sp.sh b/scripts/text_condition/gpu/train_video3d_29x720p_sp.sh deleted file mode 100644 index 8886c6975..000000000 --- a/scripts/text_condition/gpu/train_video3d_29x720p_sp.sh +++ /dev/null @@ -1,71 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-ROPE-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 29 \ - --max_height 720 \ - --max_width 1280 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.5 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --resume_from_checkpoint="latest" \ - --group_frame \ - --speed_factor 1.0 \ - --enable_stable_fp32 \ - --ema_decay 0.999 \ - --sp_size 8 \ - --train_sp_batch_size 2 \ - --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs32xsp2_29x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo" diff --git a/scripts/text_condition/gpu/train_video3d_61x480p.sh b/scripts/text_condition/gpu/train_video3d_61x480p.sh deleted file mode 100644 index 0ed4d2bdb..000000000 --- a/scripts/text_condition/gpu/train_video3d_61x480p.sh +++ /dev/null @@ -1,67 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/multi_node_example.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_aesmovie_sucai_panda.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 61 \ - --max_height 480 \ - --max_width 640 \ - --interpolation_scale_t 2.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=2 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --resume_from_checkpoint="latest" \ - --group_frame \ - --speed_factor 1.2 \ - --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_video3d_93x720p.sh b/scripts/text_condition/gpu/train_video3d_93x720p.sh deleted file mode 100644 index 216533eff..000000000 --- a/scripts/text_condition/gpu/train_video3d_93x720p.sh +++ /dev/null @@ -1,68 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-ROPE-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_sucai.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 93 \ - --max_height 720 \ - --max_width 1280 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.5 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --resume_from_checkpoint="latest" \ - --group_frame \ - --enable_stable_fp32 \ - --ema_decay 0.999 \ - --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs29x8x1_93x720p_lr1e-4_snr5_noioff0.02_ema99_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" diff --git a/scripts/text_condition/gpu/train_video3d_93x720p_sp.sh b/scripts/text_condition/gpu/train_video3d_93x720p_sp.sh deleted file mode 100644 index f706a2e53..000000000 --- a/scripts/text_condition/gpu/train_video3d_93x720p_sp.sh +++ /dev/null @@ -1,71 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -# export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/debug.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model OpenSoraT2V-ROPE-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_sucai_aes5.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 93 \ - --max_height 720 \ - --max_width 1280 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.5 \ - --interpolation_scale_w 2.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 0 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --resume_from_checkpoint="latest" \ - --group_frame \ - --speed_factor 1.0 \ - --enable_stable_fp32 \ - --ema_decay 0.999 \ - --sp_size 1 \ - --train_sp_batch_size 8 \ - --pretrained "/storage/dataset/hw29/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="bs32xsp4_93x720p_lr1e-4_snr5_noioff0.02_ema999_opensora122_rope_fp32_mt5xxl_pandamovie_aes_mo_sucai_mo" diff --git a/scripts/text_condition/gpu/train_videoudit_29x720p.sh b/scripts/text_condition/gpu/train_videoudit_29x720p.sh deleted file mode 100644 index eeae7873a..000000000 --- a/scripts/text_condition/gpu/train_videoudit_29x720p.sh +++ /dev/null @@ -1,68 +0,0 @@ -export WANDB_KEY="953e958793b218efb850fa194e85843e2c3bd88b" -# export WANDB_MODE="offline" -export ENTITY="linbin" -export PROJECT="bs32x8x2_61x480p_lr1e-4_snr5_noioff0.02_opensora122_rope_mt5xxl_pandamovie_aes_mo_sucai_mo_speed1.2" -export HF_DATASETS_OFFLINE=1 -export TRANSFORMERS_OFFLINE=1 -export PDSH_RCMD_TYPE=ssh -# NCCL setting -export GLOO_SOCKET_IFNAME=bond0 -export NCCL_SOCKET_IFNAME=bond0 -export NCCL_IB_HCA=mlx5_10:1,mlx5_11:1,mlx5_12:1,mlx5_13:1 -export NCCL_IB_GID_INDEX=3 -export NCCL_IB_TC=162 -export NCCL_IB_TIMEOUT=22 -export NCCL_PXN_DISABLE=0 -export NCCL_IB_QPS_PER_CONNECTION=4 -# export NCCL_ALGO=Ring -export OMP_NUM_THREADS=1 -export MKL_NUM_THREADS=1 -export NCCL_ALGO=Tree - -accelerate launch \ - --config_file scripts/accelerate_configs/deepspeed_zero2_config.yaml \ - opensora/train/train_t2v_diffusers.py \ - --model UDiTT2V-L/122 \ - --text_encoder_name google/mt5-xxl \ - --cache_dir "./cache_dir" \ - --dataset t2v \ - --video_data "scripts/train_data/video_data_debug.txt" \ - --image_data "scripts/train_data/image_data.txt" \ - --ae CausalVAEModel_4x8x8 \ - --ae_path "/storage/dataset/test140k" \ - --sample_rate 1 \ - --num_frames 93 \ - --max_height 720 \ - --max_width 1280 \ - --interpolation_scale_t 1.0 \ - --interpolation_scale_h 1.0 \ - --interpolation_scale_w 1.0 \ - --attention_mode xformers \ - --gradient_checkpointing \ - --train_batch_size=1 \ - --dataloader_num_workers 10 \ - --gradient_accumulation_steps=1 \ - --max_train_steps=1000000 \ - --learning_rate=1e-4 \ - --lr_scheduler="constant" \ - --lr_warmup_steps=0 \ - --mixed_precision="bf16" \ - --report_to="wandb" \ - --checkpointing_steps=500 \ - --allow_tf32 \ - --model_max_length 512 \ - --use_image_num 0 \ - --tile_overlap_factor 0.125 \ - --enable_tiling \ - --snr_gamma 5.0 \ - --use_ema \ - --ema_start_step 0 \ - --cfg 0.1 \ - --noise_offset 0.02 \ - --use_rope \ - --resume_from_checkpoint="latest" \ - --group_frame \ - --enable_stable_fp32 \ - --ema_decay 0.999 \ - --pretrained "/storage/ongoing/new/Open-Sora-Plan-bak/7.14bak/bs4x8x16_240p_lr1e-4_snr5_noioff0.02_ema9999_udit122_rope_fp32_mt5xxl_sam/checkpoint-100/model_ema/diffusion_pytorch_model.safetensors" \ - --output_dir="debug" From fd646932fc18f73cded4ce6bace341ad4626f1f5 Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 18 Jul 2024 07:06:23 +0000 Subject: [PATCH 092/134] foward 1.1 docs --- README.md | 227 +++++++++++++++++------ docs/Report-v1.1.0.md | 256 ++++++++++++++++++++++++++ docs/Train_And_Eval_CausalVideoVAE.md | 31 ++-- pyproject.toml | 2 +- validation_dir/prompt.txt | 11 -- 5 files changed, 448 insertions(+), 79 deletions(-) create mode 100644 docs/Report-v1.1.0.md delete mode 100644 validation_dir/prompt.txt diff --git a/README.md b/README.md index 2b5635b8d..32d8e80bc 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,12 @@ [[Project Page]](https://pku-yuangroup.github.io/Open-Sora-Plan/) [[中文主页]](https://pku-yuangroup.github.io/Open-Sora-Plan/blog_cn.html) --> -[![slack badge](https://img.shields.io/badge/Discord-join-blueviolet?logo=discord&)](https://discord.gg/vqGmpjkSaz) + +[![slack badge](https://img.shields.io/badge/Discord-join-blueviolet?logo=discord&)](https://discord.gg/YtsBNg7n) [![WeChat badge](https://img.shields.io/badge/微信-加入-green?logo=wechat&)](https://github.com/PKU-YuanGroup/Open-Sora-Plan/issues/53#issuecomment-1987226516) -[![Twitter](https://img.shields.io/badge/-Twitter@LinBin46984-black?logo=twitter&logoColor=1D9BF0)](https://x.com/LinBin46984/status/1763476690385424554?s=20)
-[![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0) -[![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/fffiloni/Open-Sora-Plan-v1-0-0) -[![Replicate demo and cloud API](https://replicate.com/camenduru/open-sora-plan-512x512/badge)](https://replicate.com/camenduru/open-sora-plan-512x512) -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/camenduru/Open-Sora-Plan-jupyter/blob/main/Open_Sora_Plan_jupyter.ipynb)
+[![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.1.0) +[![Twitter](https://img.shields.io/badge/-Twitter@LinBin46984-black?logo=twitter&logoColor=1D9BF0)](https://x.com/LinBin46984/status/1795018003345510687)
+[![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.1.0) [![License](https://img.shields.io/badge/License-MIT-yellow)](https://github.com/PKU-YuanGroup/Open-Sora-Plan/blob/main/LICENSE) [![GitHub repo contributors](https://img.shields.io/github/contributors-anon/PKU-YuanGroup/Open-Sora-Plan?style=flat&label=Contributors)](https://github.com/PKU-YuanGroup/Open-Sora-Plan/graphs/contributors) [![GitHub Commit](https://img.shields.io/github/commit-activity/m/PKU-YuanGroup/Open-Sora-Plan?label=Commit)](https://github.com/PKU-YuanGroup/Open-Sora-Plan/commits/main/) @@ -21,28 +20,154 @@ [![GitHub repo watchers](https://img.shields.io/github/watchers/PKU-YuanGroup/Open-Sora-Plan?style=flat&logo=github&logoColor=whitesmoke&label=Watchers)](https://github.com/PKU-YuanGroup/Open-Sora-Plan/watchers)  [![GitHub repo size](https://img.shields.io/github/repo-size/PKU-YuanGroup/Open-Sora-Plan?style=flat&logo=github&logoColor=whitesmoke&label=Repo%20Size)](https://github.com/PKU-YuanGroup/Open-Sora-Plan/archive/refs/heads/main.zip) -We are thrilled to present **Open-Sora-Plan v1.0.0**, which significantly enhances video generation quality and text control capabilities. See our [report](docs/Report-v1.0.0.md). We are training for higher resolution (>1024) as well as longer duration (>10s) videos, here is a preview of the next release. We show compressed .gif on GitHub, which loses some quality. +
+v1.0.0 badge +[![Twitter](https://img.shields.io/badge/-Twitter@LinBin46984-black?logo=twitter&logoColor=1D9BF0)](https://x.com/LinBin46984/status/1763476690385424554?s=20)
+[![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0) +[![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/fffiloni/Open-Sora-Plan-v1-0-0) +[![Replicate demo and cloud API](https://replicate.com/camenduru/open-sora-plan-512x512/badge)](https://replicate.com/camenduru/open-sora-plan-512x512) +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/camenduru/Open-Sora-Plan-jupyter/blob/main/Open_Sora_Plan_jupyter.ipynb)
+
+ +We are thrilled to present **Open-Sora-Plan v1.1.0**, which significantly enhances video generation quality and text control capabilities. See our [report](docs/Report-v1.1.0.md). We show compressed .gif on GitHub, which loses some quality. + +Thanks to **HUAWEI Ascend Team** for supporting us. In the second stage, we used Huawei Ascend computing power for training. This stage's training and inference were fully supported by Huawei. Models trained on Huawei Ascend can also be loaded into GPUs and generate videos of the same quality. + +目前已经支持使用国产AI计算系统(华为昇腾,期待更多国产算力芯片)进行完整的训练和推理。在项目第二阶段,所有训练和推理任务完全由华为昇腾计算系统支持。此外,基于华为昇腾的512卡集群训练出的模型,也可以无缝地在GPU上运行,并保持相同的视频质量。详细信息请参考我们的[hw branch](https://github.com/PKU-YuanGroup/Open-Sora-Plan/tree/hw). + + +### 221×512×512 Text-to-Video Generation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
3D animation of a small, round, fluffy creature with big, expressive eyes explores ...A single drop of liquid metal falls from a floating orb, landing on a mirror-like ...The video presents an abstract composition centered around a hexagonal shape adorned ...
A drone camera circles around a beautiful historic church built on a rocky outcropping ...Aerial view of Santorini during the blue hour, showcasing the stunning architecture ...An aerial shot of a lighthouse standing tall on a rocky cliff, its beacon cutting ...
A snowy forest landscape with a dirt road running through it. The road is flanked by ...Drone shot along the Hawaii jungle coastline, sunny day. Kayaks in the water.The camera rotates around a large stack of vintage televisions all showing different ...
+ + + + +### 65×512×512 Text-to-Video Generation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
In an ornate, historical hall, a massive tidal wave peaks and begins to crash. Two ...A Shiba Inu dog wearing a beret and black turtleneck.A painting of a boat on water comes to life, with waves crashing and the boat becoming ...
A person clad in a space suit with a helmet and equipped with a chest light and arm ...3D animation of a small, round, fluffy creature with big, expressive eyes explores a ...In a studio, there is a painting depicting a ship sailing through the rough sea.
A robot dog trots down a deserted alley at night, its metallic paws clinking softly ...A lone surfer rides a massive wave, skillfully maneuvering through the surf. The water ...A solitary cheetah sprints across the savannah, its powerful muscles propelling it ...
+ + + +### 65×512×512 Video Editing + + + + + + + + + + + + + + + +
Generated
Edited
+ + +### 512×512 Text-to-Image Generation + + -Thanks to **HUAWEI Ascend NPU Team** for supporting us. -目前已支持国产AI芯片(华为昇腾,期待更多国产算力芯片)进行推理,下一步将支持国产算力训练,具体可参考昇腾分支[hw branch](https://github.com/PKU-YuanGroup/Open-Sora-Plan/tree/hw). -| 257×512×512 (10s) | 65×1024×1024 (2.7s) | 65×1024×1024 (2.7s) | -| --- | --- | --- | -| | | | -| Time-lapse of a coastal landscape transitioning from sunrise to nightfall... | A quiet beach at dawn, the waves gently lapping at the shore and the sky painted in pastel hues....|Sunset over the sea. | +## 📰 News -| 65×512×512 (2.7s) | 65×512×512 (2.7s) | 65×512×512 (2.7s) | -| --- | --- | --- | -| | | | -| A serene underwater scene featuring a sea turtle swimming... | Yellow and black tropical fish dart through the sea. | a dynamic interaction between the ocean and a large rock... | -| | | | -| The dynamic movement of tall, wispy grasses swaying in the wind... | Slow pan upward of blazing oak fire in an indoor fireplace. | A serene waterfall cascading down moss-covered rocks... | +**[2024.05.27]** 🚀🚀🚀 We are launching Open-Sora Plan v1.1.0, which significantly improves video quality and length, and is fully open source! Please check out our latest [report](docs/Report-v1.1.0.md). Thanks to [ShareGPT4Video's](https://sharegpt4video.github.io/) capability to annotate long videos. +**[2024.04.09]** 🚀 Excited to share our latest exploration on metamorphic time-lapse video generation: [MagicTime](https://github.com/PKU-YuanGroup/MagicTime), which learns real-world physics knowledge from time-lapse videos. Here is the dataset for train (updating): [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset). +**[2024.04.07]** 🔥🔥🔥 Today, we are thrilled to present Open-Sora-Plan v1.0.0, which significantly enhances video generation quality and text control capabilities. See our [report](docs/Report-v1.0.0.md). Thanks to HUAWEI NPU for supporting us. +**[2024.03.27]** 🚀🚀🚀 We release the report of [VideoCausalVAE](docs/CausalVideoVAE.md), which supports both images and videos. We present our reconstructed video in this demonstration as follows. The text-to-video model is on the way. +
+View more + +**[2024.03.10]** 🚀🚀🚀 This repo supports training a latent size of 225×90×90 (t×h×w), which means we are able to **train 1 minute of 1080P video with 30FPS** (2× interpolated frames and 2× super resolution) under class-condition. + +**[2024.03.08]** We support the training code of text condition with 16 frames of 512x512. The code is mainly borrowed from [Latte](https://github.com/Vchitect/Latte). + +**[2024.03.07]** We support training with 128 frames (when sample rate = 3, which is about 13 seconds) of 256x256, or 64 frames (which is about 6 seconds) of 512x512. + +**[2024.03.05]** See our latest [todo](https://github.com/PKU-YuanGroup/Open-Sora-Plan?tab=readme-ov-file#todo), pull requests are welcome. + +**[2024.03.04]** We re-organize and modulize our code to make it easy to [contribute](https://github.com/PKU-YuanGroup/Open-Sora-Plan?tab=readme-ov-file#how-to-contribute-to-the-open-sora-plan-community) to the project, to contribute please see the [Repo structure](https://github.com/PKU-YuanGroup/Open-Sora-Plan?tab=readme-ov-file#repo-structure). + +**[2024.03.03]** We open some [discussions](https://github.com/PKU-YuanGroup/Open-Sora-Plan/discussions) to clarify several issues. + +**[2024.03.01]** Training code is available now! Learn more on our [project page](https://pku-yuangroup.github.io/Open-Sora-Plan/). Please feel free to watch 👀 this repository for the latest updates. + +
## 💪 Goal This project aims to create a simple and scalable repo, to reproduce [Sora](https://openai.com/sora) (OpenAI, but we prefer to call it "ClosedAI" ). We wish the open-source community can contribute to this project. Pull requests are welcome!!! @@ -66,30 +191,8 @@ Project stages: -## 📰 News - -**[2024.04.09]** 🚀 Excited to share our latest exploration on metamorphic time-lapse video generation: [MagicTime](https://github.com/PKU-YuanGroup/MagicTime), which learns real-world physics knowledge from time-lapse videos. Here is the dataset for train (updating): [Open-Sora-Dataset](https://github.com/PKU-YuanGroup/Open-Sora-Dataset). - -**[2024.04.07]** 🔥🔥🔥 Today, we are thrilled to present Open-Sora-Plan v1.0.0, which significantly enhances video generation quality and text control capabilities. See our [report](docs/Report-v1.0.0.md). Thanks to HUAWEI NPU for supporting us. - -**[2024.03.27]** 🚀🚀🚀 We release the report of [VideoCausalVAE](docs/CausalVideoVAE.md), which supports both images and videos. We present our reconstructed video in this demonstration as follows. The text-to-video model is on the way. - -**[2024.03.10]** 🚀🚀🚀 This repo supports training a latent size of 225×90×90 (t×h×w), which means we are able to **train 1 minute of 1080P video with 30FPS** (2× interpolated frames and 2× super resolution) under class-condition. - -**[2024.03.08]** We support the training code of text condition with 16 frames of 512x512. The code is mainly borrowed from [Latte](https://github.com/Vchitect/Latte). - -**[2024.03.07]** We support training with 128 frames (when sample rate = 3, which is about 13 seconds) of 256x256, or 64 frames (which is about 6 seconds) of 512x512. - -**[2024.03.05]** See our latest [todo](https://github.com/PKU-YuanGroup/Open-Sora-Plan?tab=readme-ov-file#todo), pull requests are welcome. - -**[2024.03.04]** We re-organize and modulize our code to make it easy to [contribute](https://github.com/PKU-YuanGroup/Open-Sora-Plan?tab=readme-ov-file#how-to-contribute-to-the-open-sora-plan-community) to the project, to contribute please see the [Repo structure](https://github.com/PKU-YuanGroup/Open-Sora-Plan?tab=readme-ov-file#repo-structure). - -**[2024.03.03]** We open some [discussions](https://github.com/PKU-YuanGroup/Open-Sora-Plan/discussions) to clarify several issues. - -**[2024.03.01]** Training code is available now! Learn more on our [project page](https://pku-yuangroup.github.io/Open-Sora-Plan/). Please feel free to watch 👀 this repository for the latest updates. - - -## ✊ Todo +
+✊ Todo #### Setup the codebase and train an unconditional model on landscape dataset - [x] Fix typos & Update readme. 🤝 Thanks to [@mio2333](https://github.com/mio2333), [@CreamyLong](https://github.com/CreamyLong), [@chg0901](https://github.com/chg0901), [@Nyx-177](https://github.com/Nyx-177), [@HowardLi1984](https://github.com/HowardLi1984), [@sennnnn](https://github.com/sennnnn), [@Jason-fan20](https://github.com/Jason-fan20) @@ -159,10 +262,14 @@ Project stages: - [x] Train with T5 conditioning. - [ ] Train with CLIP conditioning. - [ ] Train with CLIP + T5 conditioning (probably costly during training and experiments). +- [ ] Support Chinese. ⌛ [WIP] #### Control model with more condition - [ ] Incorporating [ControlNet](https://github.com/lllyasviel/ControlNet). ⌛ [WIP] 🙏 **[Need your contribution]** +- [ ] Incorporating [ReVideo](https://github.com/MC-E/ReVideo). ⌛ [WIP] +
+ ## 📂 Repo structure (WIP) ``` ├── README.md @@ -224,12 +331,26 @@ pip install -e '.[dev]' #### Gradio Web UI +Highly recommend trying out our web demo by the following command. We also provide [online demo](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.1.0) [![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.1.0). + +
+v1.0.0 + Highly recommend trying out our web demo by the following command. We also provide [online demo](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0) [![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.0.0) and [![hf_space](https://img.shields.io/badge/🤗-Open%20In%20Spaces-blue.svg)](https://huggingface.co/spaces/fffiloni/Open-Sora-Plan-v1-0-0) in Huggingface Spaces. 🤝 Enjoying the [![Replicate demo and cloud API](https://replicate.com/camenduru/open-sora-plan-512x512/badge)](https://replicate.com/camenduru/open-sora-plan-512x512) and [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/camenduru/Open-Sora-Plan-jupyter/blob/main/Open_Sora_Plan_jupyter.ipynb), created by [@camenduru](https://github.com/camenduru), who generously supports our research! +
+ +For the 65 frames. + +```bash +python -m opensora.serve.gradio_web_server --version 65x512x512 +``` + +For the 221 frames. ```bash -python -m opensora.serve.gradio_web_server +python -m opensora.serve.gradio_web_server --version 221x512x512 ``` #### CLI Inference @@ -244,7 +365,7 @@ Refer to [Data.md](docs/Data.md) ### Evaluation Refer to the document [EVAL.md](docs/EVAL.md). -### Causal Video VAE +### CausalVideoVAE #### Reconstructing @@ -258,8 +379,6 @@ Parameter explanation: - `--enable_tiling`: This parameter is a flag to enable a tiling conv. -- `--enable_time_chunk`: This parameter is a flag to enable a time chunking. This will block the video in the temporal dimension and reconstruct the long video. This is only an operation performed in the video space, not the latent space, and cannot be used for training. - #### Training and Eval Please refer to the document [CausalVideoVAE](docs/Train_And_Eval_CausalVideoVAE.md). @@ -272,16 +391,16 @@ Please refer to the document [VQVAE](docs/VQVAE.md). #### Training ``` -sh scripts/text_condition/train_videoae_17x256x256.sh +sh scripts/text_condition/train_videoae_65x512x512.sh ``` ``` -sh scripts/text_condition/train_videoae_65x256x256.sh +sh scripts/text_condition/train_videoae_221x512x512.sh ``` ``` -sh scripts/text_condition/train_videoae_65x512x512.sh +sh scripts/text_condition/train_videoae_513x512x512.sh ``` - + + ## 💡 How to Contribute to the Open-Sora Plan Community We greatly appreciate your contributions to the Open-Sora Plan open-source community and helping us make it even better than it is now! @@ -317,6 +438,7 @@ For more details, please refer to the [Contribution Guidelines](docs/Contributio ## 👍 Acknowledgement * [Latte](https://github.com/Vchitect/Latte): The **main codebase** we built upon and it is an wonderful video generated model. * [PixArt-alpha](https://github.com/PixArt-alpha/PixArt-alpha): Fast Training of Diffusion Transformer for Photorealistic Text-to-Image Synthesis. +* [ShareGPT4Video](https://github.com/InternLM/InternLM-XComposer/tree/main/projects/ShareGPT4Video): Improving Video Understanding and Generation with Better Captions. * [VideoGPT](https://github.com/wilson1yan/VideoGPT): Video Generation using VQ-VAE and Transformers. * [DiT](https://github.com/facebookresearch/DiT): Scalable Diffusion Models with Transformers. * [FiT](https://github.com/whlzy/FiT): Flexible Vision Transformer for Diffusion Model. @@ -356,5 +478,4 @@ For more details, please refer to the [Contribution Guidelines](docs/Contributio - - + \ No newline at end of file diff --git a/docs/Report-v1.1.0.md b/docs/Report-v1.1.0.md new file mode 100644 index 000000000..ca3ec2ba5 --- /dev/null +++ b/docs/Report-v1.1.0.md @@ -0,0 +1,256 @@ +# Report v1.1.0 + +In April 2024, we launched Open-Sora-Plan v1.0.0, featuring a simple and efficient design along with remarkable performance in text-to-video generation. It has already been adopted as a foundational model in numerous research projects, including its data and model. + +**Today, we are excited to present Open-Sora-Plan v1.1.0, which significantly improves video generation quality and duration.** + +Compared to the previous version, Open-Sora-Plan v1.1.0, the improvements include: + +1. **Better compressed visual representations**. We optimized the CausalVideoVAE architecture, which now has stronger performance and higher inference efficiency. +2. **Generate higher quality, longer videos**. We used higher quality visual data and captions by [ShareGPT4Video](https://sharegpt4video.github.io/), enabling the model to better understand the workings of the world. + +Along with performance improvements, Open-Sora-Plan v1.1.0 maintains the minimalist design and data efficiency of v1.0.0. Remarkably, we found that v1.1.0 exhibits similar performance to the Sora base model, indicating that our version's evolution aligns with the scaling law demonstrated by Sora. + +### Open-Source Release +We open-source the Open-Sora-Plan to facilitate future development of Video Generation in the community. Code, data, model will be made publicly available. +- Demo: Hugging Face demo [here](https://huggingface.co/spaces/LanguageBind/Open-Sora-Plan-v1.1.0). +- Code: All training scripts and sample scripts. +- Model: Both Diffusion Model and CasualVideoVAE [here](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.1.0). +- Data: Both raw videos and captions [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.1.0). + +## Gallery + + +### 221×512×512 Text-to-Video Generation + +| 221×512×512 (9.2s) | 221×512×512 (9.2s) | 221×512×512 (9.2s) | 221×512×512 (9.2s) | +| --- | --- | --- | --- | +| | | | | +| This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage ... | a cat wearing sunglasses and working as a lifeguard at pool. | Photorealistic closeup video of two pirate ships battling each other as they sail ... | A movie trailer featuring the adventures ofthe 30 year old spacemanwearing a redwool ... | +| | | | | +| A snowy forest landscape with a dirt road running through it. The road is flanked by ... | Drone shot along the Hawaii jungle coastline, sunny day. Kayaks in the water. | Alpacas wearing knit wool sweaters, graffiti background, sunglasses. | The camera rotates around a large stack of vintage televisions all showing different ... | +| | | | | +| A drone camera circles around a beautiful historic church built on a rocky outcropping ... | Aerial view of Santorini during the blue hour, showcasing the stunning architecture ... | A robot dog explores the surface of Mars, kicking up red dust as it investigates ... | An aerial shot of a lighthouse standing tall on a rocky cliff, its beacon cutting ... | +| | | | | +| 3D animation of a small, round, fluffy creature with big, expressive eyes explores ... | A corgi vlogging itself in tropical Maui. | A single drop of liquid metal falls from a floating orb, landing on a mirror-like ... | The video presents an abstract composition centered around a hexagonal shape adorned ... | + +### 65×512×512 Text-to-Video Generation + +| 65×512×512 (2.7s) | 65×512×512 (2.7s) | 65×512×512 (2.7s) | 65×512×512 (2.7s) | +| --- | --- | --- | --- | +| | | | | +| Extreme close-up of chicken and green pepper kebabs grilling on a barbeque with flames. | 3D animation of a small, round, fluffy creature with big, expressive eyes explores a ... | A corgi vlogging itself in tropical Maui. | In a studio, there is a painting depicting a ship sailing through the rough sea. | +| | | | | +| A robot dog trots down a deserted alley at night, its metallic paws clinking softly ... | A solitary spider weaves its web in a quiet corner. The web shimmers and glows with ... | A lone surfer rides a massive wave, skillfully maneuvering through the surf. The water ... | A solitary cheetah sprints across the savannah, its powerful muscles propelling it ... | +| | | | | +| A solitary astronaut plants a flag on an alien planet covered in crystal formations ... | At dawn's first light, a spaceship slowly exits the edge of the galaxy against a ...| A dapper puppy in a miniature suit, basking in the afternoon sun, adjusting his tie ... | A wise old elephant painting abstract art with its trunk, each stroke a burst of color ... | +| | | | | +| In an ornate, historical hall, a massive tidal wave peaks and begins to crash. Two ... | A Shiba Inu dog wearing a beret and black turtleneck. | A painting of a boat on water comes to life, with waves crashing and the boat becoming ... | Many spotted jellyfish pulsating under water. Their bodies are transparent and glowing ... | +| | | | | +| An animated hedgehog with distinctive spiky hair and large eyes is seen exploring a ... | An animated rabbit in a playful pink snowboarding outfit is carving its way down a ... | A person clad in a space suit with a helmet and equipped with a chest light and arm ... | | + +### 65×512×512 Video Editing + +| generated 65×512×512 (2.7s) | edited 65×512×512 (2.7s) | +| --- | --- | +| | | +| | | +| | | + +### 512×512 Text-to-Image Generation + + + +## Detailed Technical Report + +### CasualVideoVAE + +#### Model Structure + +As the number of frames increases, the encoder overhead of CausalVideoVAE gradually rises. When training with 257 frames, 80GB of VRAM is insufficient for the VAE to encode the video. Therefore, we reduced the number of CausalConv3D layers, retaining only the last two stages of CausalConv3D in the encoder. This change significantly lowers the overhead while maintaining nearly the same performance. Note that we only modified the encoder; the decoder still retains all CausalConv3D layers, as training the Diffusion Model does not require the decoder. + +vaemodel + +We compare the computational overhead of the two versions by testing the forward inference of the encoder on the H100. + +| Version | 129×256×256 | | 257×256×256 | | 513×256×256 | | +|---|---|---|---|---|---|---| +| | Peak Mem. | Speed | Peak Mem. | Speed |Peak Mem. | Speed | +| v1.0.0 | 22G | 2.9 it/s | OOM | - | OOM | - | +| v1.1.0 | 18G | 4.9 it/s | 34G | 2.5 it/s | 61G | 1.2 it/s | + + +#### Temporal Module + +vaemodel + +In v1.0.0, our temporal module had only TemporalAvgPool. TemporalAvgPool leads to the loss of high-frequency information in the video, such as details and edges. To address this issue, we improved this module in v1.1.0. As shown in the figure below, we introduced convolution and added learnable weights, allowing different branches to decouple different features. When we omit CausalConv3D, the video is reconstructed very blurry. Similarly, when we omit TemporalAvgPool, the video becomes very sharp. + +| | SSIM↑ | LPIPS↓ | PSNR↑ | +|---|---|---|---| +| Base | 0.850 | 0.091 | 28.047 | +| + Frames | 0.868 | 0.070 | 28.829 | +| + Reset mixed factor | 0.873 | 0.070 | 29.140 | + + + + + +#### Training Details + +Similar to v1.0.0, we initialized from the Latent Diffusion's VAE and used tail initialization. For CausalVideoVAE, we trained for 100k steps in the first stage with a video shape of 9×256×256. Subsequently, we increased the frame count from 9 to 25 and found that this significantly improved the model's performance. It is important to clarify that we enabled the mixed factor during both the first and second stages, with a value of a (sigmoid(mixed factor)) reaching 0.88 at the end of training, indicating the model's tendency to retain low-frequency information. In the third stage, we reinitialized the mixed factor to 0.5 (sigmoid(0.5) = 0.6225), which further enhanced the model's capabilities. + +#### Loss Function + +We found that using GAN loss helps retain high-frequency information and alleviates grid artifacts. Additionally, we observed that switching from 2D GAN to 3D GAN provides further improvements. + +| GAN Loss/Step | SSIM↑ | LPIPS↓ | PSNR↑ | +|---|---|---|---| +| 2D/80k | 0.879 | 0.068 | 29.480 | +| 3D/80k | 0.882 | 0.067 | 29.890 | + +#### Inference Tricks +Therefore, we introduced a method called **temporal rollback tiled convolution**, a tiling approach specifically designed for CausalVideoVAE. Specifically, all windows except the first one discard the first frame because the first frame in a window is treated as an image, while the remaining frames should be treated as video frames. + +tiled_temp + +We tested the speed on the H100 with a window size of 65×256×256. + +| Version | 129×256×256 | | 257×256×256 | | 513×256×256 | | +|---|---|---|---|---|---|---| +| | Peak Mem. | Speed | Peak Mem. | Speed |Peak Mem. | Speed | +| 4×8×8 | 10G | 1.3 s/it | 10G | 2.6 s/it | 10G | 5.3 s/it | + +### Data Construction +Since Open-Sora-Plan supports joint training of images and videos, our data collection is divided into two parts: images and videos. Images do not need to originate from videos; they are independent datasets. We spent approximately 32×240 H100 hours generating image and video captions, and all of this is **open source**! + +#### Image-Text Collection Pipeline +We obtained 11 million image-text pairs from [Pixart-Alpha](https://huggingface.co/datasets/PixArt-alpha/SAM-LLaVA-Captions10M), with captions generated by [LLaVA](https://github.com/haotian-liu/LLaVA). Additionally, we utilized the high-quality OCR dataset [Anytext-3M](https://github.com/tyxsspa/AnyText), which pairs each image with corresponding OCR characters. However, these captions were insufficient to describe the entire image, so we used [InternVL-1.5](https://github.com/OpenGVLab/InternVL) for supplementary descriptions. Since T5 only supports English, we filtered for English data, which constitutes about half of the complete dataset. Furthermore, we selected high-quality images from [Laion-5B](https://laion.ai/blog/laion-5b/) to enhance human-like generation quality. The selection criteria included high resolution, high aesthetic scores, and watermark-free images containing people. + +Here, we are open-sourcing the prompt used for InternVL-1.5: +``` +# for anytext-3m +Combine this rough caption: "{}", analyze the image in a comprehensive and detailed manner. "{}" can be recognized in the image. +# for human-160k +Analyze the image in a comprehensive and detailed manner. +``` + +| Name | Image Source | Text Captioner | Num pair | +|---|---|---|---| +| SAM-11M | [SAM](https://ai.meta.com/datasets/segment-anything/) | [LLaVA](https://github.com/haotian-liu/LLaVA) | 11,185,255 | +| Anytext-3M-en | [Anytext](https://github.com/tyxsspa/AnyText) | [InternVL-1.5](https://github.com/OpenGVLab/InternVL) | 1,886,137 | +| Human-160k | [Laion](https://laion.ai/blog/laion-5b/) | [InternVL-1.5](https://github.com/OpenGVLab/InternVL) | 162,094 | + + +#### Video-Text Collection Pipeline +In v1.0.0, we sampled one frame from each video to generate captions. However, as video length increased, a single frame could not adequately describe the entire video's content or temporal movements. Therefore, we used a video captioner to generate captions for the entire video clip. Specifically, we used [ShareGPT4Video](https://sharegpt4video.github.io/), which effectively covers temporal information and describes the entire video content. The v1.1.0 video dataset comprises approximately 3k hours, compared to only 300 hours in v1.0.0. As before, we have open-sourced all text annotations and videos (both under the CC0 license), which can be found [here](https://huggingface.co/datasets/LanguageBind/Open-Sora-Plan-v1.1.0/tree/main). + + + +| Name | Hours | Num frames | Num pair | +|---|---|---|---| +| [Mixkit](https://mixkit.co/) | 42.0h | 65 | 54,735 | +| | | 513 | 1,997 | +| [Pixabay](https://pixabay.com/) | 353.3h | 65 | 601,513 | +| | | 513 | 51,483 | +| [Pexel](https://www.pexels.com/) | 2561.9h | 65 | 3,832,666 | +| | | 513 | 271,782 | + +### Training Diffusion Model +Similar to our previous work, we employed a multi-stage cascaded training method. Below is our training card: + +#### Stage 1 + +Surprisingly, we initially believed that the performance of the diffusion model would improve with longer training. However, by observing the [logs](https://api.wandb.ai/links/linbin/o76j03j4), we found that videos generated at 50k steps were of higher quality than those at 70-100k steps. In fact, extensive sampling revealed that checkpoints at 40-60k steps outperformed those at 80-100k steps. Quantitatively, 50k steps correspond to approximately 2 epochs of training. It is currently unclear whether this is due to overfitting from a small dataset or the limited capacity of the 2+1D model. + +#### Stage 2 + +In the second stage, we used Huawei Ascend computing power for training. This stage's training and inference were fully supported by Huawei. We conducted sequence parallel training and inference on a large-scale cluster, distributing one sample across eight ranks. Models trained on Huawei Ascend can also be loaded into GPUs and generate videos of the same quality. + + +#### Stage 3 + +In the third stage, we further increased the frame count to 513 frames, approximately 21 seconds at 24 FPS. However, this stage presents several challenges, such as ensuring temporal consistency in the 2+1D model over long durations and whether the current amount of data is sufficient. We are still training the model for this stage and continuously monitoring its progress. + +| Name | Stage 1 | Stage 2 | Stage 3 | +|---|---|---|---| +| Training Video Size | 65×512×512 | 221×512×512 | 513×512×512 | +| Compute (#Num x #Hours) | 80 H100 × 72 | 512 Ascend × 72 | Under Training | +| Checkpoint | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.1.0/tree/main/65x512x512) | [HF](https://huggingface.co/LanguageBind/Open-Sora-Plan-v1.1.0/tree/main/221x512x512) | Under Training | +| Log | [wandb](https://api.wandb.ai/links/linbin/o76j03j4) | - | - | +| Training Data | ~3k hours videos + 13M images | | | + +### Video Editing + +The recently proposed [ReVideo](https://mc-e.github.io/project/ReVideo/) achieves accurate video editing by modifying the first frame and applying motion control within the edited area. Although it achieves excellent video editing performance, the editing length is limited by the base model [SVD](https://github.com/Stability-AI/generative-models). Open-Sora, as a fundamental model for long-video generation, can compensate for this issue. Currently, we are collaborating with the ReVideo team to use Open-Sora as the base model for long video editing. Some preliminary results are shown [here](). + +The initial version still needs improvement in several aspects. In the future, we will continue to explore integration with ReVideo to develop improved long-video editing models. + +## Failed Case and Discussion + +Despite the promising results of v1.1.0, there remains a gap between our model and Sora. Here, we present some failure cases and discuss them. + +### CasualVideoVAE + +Despite the significant performance improvement of VAE in v1.1.0 over v1.0.0, we still encounter failures in challenging cases, such as sand dunes and leaves. The video on the left shows the reconstructed video downsampled by a factor of 4 in time, while the video on the right is downsampled by a factor of 2. Both exhibit jitter when reconstructing fine-grained features. This indicates that reducing temporal downsampling alone cannot fully resolve the jitter issue. + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/1a87d6d8-4bf1-4b4e-83bb-84870c5c3a11 + +https://github.com/PKU-YuanGroup/Open-Sora-Plan/assets/62638829/1a87d6d8-4bf1-4b4e-83bb-84870c5c3a11 + +### Diffusion Model + +#### Semantic distortion + +On the left is a video generated by v1.1.0 showing a puppy in the snow. In this video, the puppy's head exhibits semantic distortion, indicating that the model struggles to correctly identify which head belongs to which dog. On the right is a video generated by Sora's [base model](https://openai.com/index/video-generation-models-as-world-simulators/). We observe that Sora's early base model also experienced semantic distortion issues. This suggests that we may achieve better results by scaling up the model and increasing the amount of training data. + +Prompt:A litter of golden retriever puppies playing in the snow.Their heads pop out of the snow, covered in. + +| Our | Sora Base×1 | Sora Base×4 | Sora Base×32 | +|---|---|---|---| +| | | | | + +#### Limited dynamics + +The primary difference between videos and images lies in their dynamic nature, where objects undergo a series of changes across consecutive frames. However, the videos generated by v1.1.0 still contain many instances of limited dynamics. Upon reviewing a large number of training videos, we found that while web-crawled videos have high visual quality, they are often filled with meaningless close-up shots. These close-ups typically show minimal movement or are even static. On the left, we present a generated video of a bird, while on the right is a training video we found, which is almost static. There are many similar videos in the dataset from stock footage sites. + +Prompt:This close-up shot of a Victoria crowned pigeon showcases its striking blue plumage and red chest. Its crest is made of delicate, lacy feathers, while its eye is a striking red color. The bird's head is tilted slightly to the side,giving the impression of it looking regal and majestic. The background is blurred,drawing attention to the bird's striking appearance. + + +| Our | Raw video | +|---|---| +| | | + +#### Negative prompt + +We found that using negative prompts can significantly improve video quality, even though we did not explicitly tag the training data with different labels. On the left is a video sampled using a negative prompt, while on the right is a video generated without a negative prompt. This suggests that we may need to incorporate more prior knowledge into the training data. For example, when a video has a watermark, we should note "watermark" in the corresponding caption. When a video's bitrate is too low, we should add more tags to distinguish it from high-quality videos, such as "low quality" or "blurry." We believe that explicitly injecting these priors can help the model differentiate between the vast amounts of pretraining data (low quality) and the smaller amounts of fine-tuning data (high quality), thereby generating higher quality videos. + +Prompt:A litter of golden retriever puppies playing in the snow.Their heads pop out of the snow, covered in. +Negative Prompt:distorted, discontinuous, ugly, blurry, low resolution, motionless, static, low quality + + +| With Negative Prompt | Without Negative Prompt | +|---|---| +| | | + +## Future Work + +In our future work, we will focus on two main areas: (1) data scaling and (2) model design. Once we have a robust baseline model, we will extend it to handle variable durations and conditional control models. + +### Data Scaling + +#### Data source + +As mentioned earlier, our dataset is entirely sourced from stock footage websites. Although these videos are of high quality, many consist of close-up shots of specific areas, resulting in slow motion in the videos. We believe this is one of the main reasons for the limited dynamics observed. Therefore, we will continue to collect datasets from diverse sources to address this issue. + +#### Data volume + +In v1.1.0, our dataset comprises only ~3k hours of video. We are actively collecting more data and anticipate that the video dataset for the next version will reach ~100k hours. We welcome recommendations from the open-source community for additional datasets. + +### Model Design + +#### CasualVideoVAE +In our internal testing, even without downsampling in time, we found that it is not possible to completely resolve the jitter issue in reconstructing fine-grained features. Therefore, we need to reconsider how to mitigate video jitter to the greatest extent possible while simultaneously supporting both images and videos. We will introduce a more powerful CasualVideoVAE in the next version. + +#### Diffusion Model +In v1.1.0, we found that 2+1D models can generate higher-quality videos in short durations. However, for long videos, they tend to exhibit discontinuities and inconsistencies. Therefore, we will explore more possibilities in model architecture to address this issue. diff --git a/docs/Train_And_Eval_CausalVideoVAE.md b/docs/Train_And_Eval_CausalVideoVAE.md index 691f159f8..772cff903 100644 --- a/docs/Train_And_Eval_CausalVideoVAE.md +++ b/docs/Train_And_Eval_CausalVideoVAE.md @@ -32,14 +32,15 @@ Model training requires two key files: one is the `config.json` file, which conf ### Model Configuration File -Taking the release version model configuration file `release.json` as an example: +Taking the v1.1.0 version model configuration file as an example: ```json { "_class_name": "CausalVAEModel", "_diffusers_version": "0.27.2", + "_name_or_path": "../results/pretrained_488_tail", "attn_resolutions": [], - "decoder_attention": "AttnBlock3D", + "decoder_attention": "AttnBlock3DFix", "decoder_conv_in": "CausalConv3d", "decoder_conv_out": "CausalConv3d", "decoder_mid_resnet": "ResnetBlock3D", @@ -58,32 +59,32 @@ Taking the release version model configuration file `release.json` as an example "decoder_temporal_upsample": [ "", "", - "TimeUpsample2x", - "TimeUpsample2x" + "TimeUpsampleRes2x", + "TimeUpsampleRes2x" ], "double_z": true, "dropout": 0.0, "embed_dim": 4, - "encoder_attention": "AttnBlock3D", - "encoder_conv_in": "CausalConv3d", + "encoder_attention": "AttnBlock3DFix", + "encoder_conv_in": "Conv2d", "encoder_conv_out": "CausalConv3d", "encoder_mid_resnet": "ResnetBlock3D", "encoder_resnet_blocks": [ - "ResnetBlock3D", - "ResnetBlock3D", + "ResnetBlock2D", + "ResnetBlock2D", "ResnetBlock3D", "ResnetBlock3D" ], "encoder_spatial_downsample": [ - "SpatialDownsample2x", - "SpatialDownsample2x", - "SpatialDownsample2x", + "Downsample", + "Downsample", + "Downsample", "" ], "encoder_temporal_downsample": [ - "TimeDownsample2x", - "TimeDownsample2x", "", + "TimeDownsampleRes2x", + "TimeDownsampleRes2x", "" ], "hidden_size": 128, @@ -93,15 +94,17 @@ Taking the release version model configuration file `release.json` as an example 4, 4 ], + "in_channels": 3, "loss_params": { "disc_start": 2001, "disc_weight": 0.5, "kl_weight": 1e-06, "logvar_init": 0.0 }, - "loss_type": "opensora.models.ae.videobase.losses.LPIPSWithDiscriminator", + "loss_type": "opensora.models.ae.videobase.losses.LPIPSWithDiscriminator3D", "lr": 1e-05, "num_res_blocks": 2, + "out_channels": 3, "q_conv": "CausalConv3d", "resolution": 256, "z_channels": 4 diff --git a/pyproject.toml b/pyproject.toml index 68ce7807a..42ef57118 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opensora" -version = "1.0.0" +version = "1.5.0" description = "Reproduce OpenAI's Sora." readme = "README.md" requires-python = ">=3.8" diff --git a/validation_dir/prompt.txt b/validation_dir/prompt.txt deleted file mode 100644 index bb0e595a4..000000000 --- a/validation_dir/prompt.txt +++ /dev/null @@ -1,11 +0,0 @@ -A rocket ascends slowly into the sky. -A coffee cup with "anytext" foam floating on it. -A beautiful girl is blinking her eyes. -A flower and bird painting. -The scenery of snow capped mountains. -The night view of the city. -A rocket ascends slowly into the sky. -Along the coast, variously sized boats float on the lake. -The landscape at sunset is profound and expansive. -A beautiful girl is blinking her eyes. -A beautiful girl is blinking her eyes. \ No newline at end of file From a1bc585ad7ded758588642732df5b2146bfdb52c Mon Sep 17 00:00:00 2001 From: LinB203 <2267330597@qq.com> Date: Thu, 18 Jul 2024 08:14:55 +0000 Subject: [PATCH 093/134] t2v train readme --- README.md | 40 ++++++++++++++----- .../gpu/{train_anyx720p.sh => train_t2v.sh} | 0 2 files changed, 31 insertions(+), 9 deletions(-) rename scripts/text_condition/gpu/{train_anyx720p.sh => train_t2v.sh} (100%) diff --git a/README.md b/README.md index 32d8e80bc..af46eceb0 100644 --- a/README.md +++ b/README.md @@ -387,19 +387,41 @@ Please refer to the document [CausalVideoVAE](docs/Train_And_Eval_CausalVideoVAE Please refer to the document [VQVAE](docs/VQVAE.md). -### Video Diffusion Transformer -#### Training -``` -sh scripts/text_condition/train_videoae_65x512x512.sh -``` -``` -sh scripts/text_condition/train_videoae_221x512x512.sh -``` +### Text-to-Video training + ``` -sh scripts/text_condition/train_videoae_513x512x512.sh +bash scripts/text_condition/gpu/train_t2v.sh ``` +We introduce some key parameters in order to customize your training process. + +#### Training size +To train videos of different resolutions and durations, adjust `--num_frames xx`, `--max_height xxx` and `--max_width xxx`. + +#### Data processing +You specify your training data using `--data /path/to/data.txt`. For more information, please refer to the [documentation](). + +If the data movement is slow, we can specify `--speed_factor 1.25` to accelerate 1.25x videos. + +If you do not want to train on videos of dynamic durations, set `--drop_short_ratio 1.0` to discard all video data with frame counts not equal to `--num_frames`. + +If you want to train with videos of dynamic durations, we highly recommend specifying `--group_frame` as well. It improves computational efficiency during training. + +#### Multi-stage transfer learning +When training a base model, such as 240p (`--max_height 240` and `--max_width 320`, `--interpolation_scale_h 1.0` and `--interpolation_scale_w 1.0`) , and you want to initialize higher resolution models like 480p (width 640, height 480) from 240p's weights, you need to adjust `--max_height 480` and `--max_width 640`, `--interpolation_scale_h 2.0` and `--interpolation_scale_w 2.0`, and set `--pretrained` to your 240p weights path (path/to/240p/xxx.safetensors). + +#### Load weights +We have two ways to load weights: `--pretrained path/to/240p/xxx.safetensors` and `--resume_from_checkpoint /path/to/output_dir`. If both are specified, the latter will override the former. + +**For `--pretrained`**, this is typically used for loading pretrained weights across stages, such as using 240p weights to initialize 480p training. Or when switching datasets and you do not want the previous optimizer state. + +**For `--resume_from_checkpoint`**, it will resume the training process from the latest checkpoint in `--output_dir`. Typically, we set `--resume_from_checkpoint="latest"`, which is useful in cases of unexpected interruptions during training. + +#### Sequence Parallelism +`--sp_size 8 --train_sp_batch_size 2` means running a batch size of 2 across 8 GPUs (on the same node). + + gFa055r@ z{-5cN=>-0&_xW^x!hh2X(+N7uq~i%!guc(|G2Jk@&KG~nX-v`*OuCs5>4fQopHLU? z<>dbWRQ`|Yj_I!Yh4^FgGYLO*FGwbRO6=JJeiG3>dPj7Eak{GXW8nVp+bH80qJLF* zs{lx!Ut~c<(JfG4lLU$E)5bfb6Z+r;T~=MhpH6?GBIzf#YM_moGSM>oVa{b!nJ6f$ zwy+2rWiGlzG@_hMU6B>g5Ur$0d6;bz`lyaU9x7ENkXT?ZX~FHeB%q7 zvUHLH7Viyy*yA-;4)<+(-^2`3yf!VmQ9pP@;?79vZ%W7Zg177^7y9JgU$f4>yd}d?M zGUMW2iH>lhLg71AHTiXHIM`OeWKWzw3kZ23MVY!R0Hc|&9#W>{@>1NSdiX^5HZA5V zI5JcLGe;ZJxS5eDbsEHD8rm*)Ow~4Pu!=@118Gl>T_6|OhLmi17g0+XuMpVksYOj= zNm7<3p{H26x5TivyQ+z^FXlw6xc11!#Z0uyQiSLdCHmpc#AoK~+lma;XC|&>bR;*@snIyAml>m5}mX&gn|3R_az3p`r7D?y_!h(JU4X zX{!CkxBK0)^XsiHyoWy;&bk=vd)x||@;J*=&yh0vs+Us3icsCu>JFw*UF77HEv2(j zn>CLkrN>;lQIeFcA#&HmX#}#;Lmx6^w=CzPA%Nm{oc{nJ6t#e<4J-!|OyxPpSXsJf zQ6I zj2(`~wb9jAUSGMcP&I9B4;k`%?9_={*1fBk(IoiwiiyKul8VucFOI9eW4YNfdI48V zrqrc``e72&r<4P98uV4GoC<+xtJiC{IMP;9pWTs>il@4PSjX`d8`nOn%BNhJQpqwi z_Q20|QKLlh#_w*hq%*jgek7Qck5UHcrKYYS))KUx(*xTFk)jz3OHh49?j-4_z8bkY zXbRRq+c2+up-U@(YgMw`ku69HuWj{yh|0VDhRIyN{@No&>304^*+&=k9u-RO%jtz} z?YAGZ^AB$s&2KL7RoEpJ_galq8q;;1cnJL3j`fMv9kn$G^=JaL9;yV$9{BZ~r5QMtyDu`qvz`|~ z?b99lN9o^nhuid)%8FDyqe$D?$Q;U!D*=CMW017)N>ojPW)rMV=G8>j)mom``8KPF z`CkysX`E681zSS2CSMgZiwSTLdf1Ula`kp1s8-TsH$&TRtKO)y1mYe-XI3mMX zoM*YNHl_>5xdAP;mQu2iP{qWcC_Hv(JyiAf$IkvmcT~{U&U5iMMelxWcJEoj<}ss| zLDbT{xoIt_sS-AwWgk~Xeh6PVNsM`NAe%iypp=4CdH|AQX5`uwf1pg^veS>I-)a~? zKXQR%kuK(f%VhM{yIKlTN>e4y+`TVmwCb2;4J z3PJjx_E(K|)0EjSe0uqR2g?0HN{1D_)@oWxRJ4_JAcagum&ji*E+@Ektj^{Xs4LGr z;O#gs@q+p`ay%VE$SJKWDN<&r`uaJwUn84tQ$_o5kv$Ad1x*x6&~{7QTm7teDaO3S zidEbwvC6fg^FW1dQ}k6$DxK5`>Cr&zq{MlLnq9N?)&#faZlQW#ml=5Dx#-FAKSHdJ z)@st`XUR5B?C=|R<#=x{(NeX1W}UP)6LzOTKC`8%g)QdD1xNak;QmhK`jfuif0z~Q zerez)vb2s^97`y$mb&?KsFgDtf-QIOnQzs+TIb$%29Yua_aKo{PR|)#T&Osd&Y#`6!_pl~HSl zmSgaRD%o0p1xpn-O2iRX2dhK6Y7+C5E^J3QBRd&?~wgG zmg5bXEd=>W1%gJoh6{nGtr-?S)_( z6fNI$OLXpAl1UyYAx&&>FVfiHoRY9akKYCtOx_4;Tamg>pu>NdF4WelG$7Q~C?x=$ zqhB)%d75>!CGOyt8G(-S;6@JWb-4DIeW9_g_KLO~EhBr4g{1g&kTHg_^Hq}1(;$WD z**_-CHvLvOXqI?yxM#Sc*=s0)s$LauukQ{G%1-Ka&yXCl?Gl?!8;dzxG~E z+_yG!3bbgd*C^bPmt~h+qLPfV-PuLYOms=<);*WY%M*^ZZL^N8(R}0P?oGLDo9`oS z8sk+EE{)U^9-<^(BIX*^4$}@24CnX3fj|OOW{FEF+BZ+a88pp|RH+$m=?wMp((H(gY1&U}!J z()Uekv69v=kD#$@Yr$k_Tq0U~BSqN#uk=xKzx$5eWIW04zpuIEkNa)eK|xZPB6E*N zUSPQZo`N}Y>@XLpGXDUFvJjN^vZ#7$brYfqK>gqefyMM~I+bZ}g_=rGE8p9fU_9~N zC5rKR<6C{bt8JYDRS(zKH%gVL>JpGbiBa&diM2nufD zXLT*K1lL35kC6 zzibowh(nx1k;HN(AqfRrPl=o->LEgLE=JEF(kUdivUbWGG9e(vxF8(lMi6FJejcWB ziN90|Khm6=_1uio=9D;h4+-^wWypYvZWYefiZ{Jqm0o>B>A&?w1=Y_w(AimXBs)_d zH&}$P;u8}v9kfTBBX1<+Vz*c}S!uzkS*Uu~^F<{x! zWBtM7`7JZlXvabll-gTTdE z6e{!zr3xn-4Cg4Tvv5ZM(eRdAfL2{^SiK3P zvDeU{l`&G%5K_={dXiG1ITNToddIk8bN0Cym?R$)0{G{@{Hw@W-bp+U202_wsoJWT z$nyN%f$o3{#TrKP*r^XsSzf8@>_>rUZ;!;AnLdH~P>QgVp zHA1>p(K3UVYSbn%J# znuU>*FIjs4c2h+adR-5q9+kJe}M)c*h%?`?1PK^16Gb!MiCQJW?J z=>$`-JakF`{V>Mz@knka@lHxCa|Un#ibe{24%9-1ROp)P*#J+wCJlo;w!$b`R^!)d zVtAsW4&v?GsaoH~*xk$`ijLNrmAs^(rcl{ai&Dr)N>Y$!NiqUZbEIT#=5~Ql^<gsgwl&=EwW(obDKr-JnY~ivv_okcl4Hv-_(`th z&}hAvxqG4;7`a+?RihTQWxUIFr^|T(3$){*LEAPxGN0icM&YMl5@UIF(y@z@FPp`A zMW3yc&CYlx9RoBu7DMO-e+@?ey$h-p_K>6=Rfcl-sSO?Oo2OO+g9&0MVrW zBlbr|0)}(1C{3HDUsaP`Ub@#m>60MK14J^~Pr`%&pa4Yfm;_uFEgPU|vome1W72S` zVL?SBpdnt!g`lFPvjMB?yXiH}tYv(5);ULeuiAe~d}fjS`+=u5RK*>f8)iB}rbu*B9j)XPgSZ6F0Wq4W z+I3ILE0ZWr#jFi_=?su77CTBOJ%b*$W>?!~q2~(@Tpw)>)ge++PVpnuPF-acc^UdK z7t*C&VOVCEGPFVFwpMWtNWEfet~Up097RVvCmi+>wwOvO@$9G+~QZZApTfWdeIUGDf*A6J{ z&x!MlCdrmL>Q`0V8ucpLQ`->R+m7KRfMf{qk1))4we%Bo_zjn^cBhp1o5&2#)RhEx z0mpnzgCd{#V(uBp(&}5Rv4_26Y*gl;4xoufzqE3;a+X#H)~^TTSyMR3Zzyp zdt`i1+a8um@KTZ=1qY_5tW6q3K|}&PW#JE8oj5%mvg4FlZP!}SZMRjvl-Ac1{nKZv zg!I$f6}fjku#(YIw7s_rB)I|F1wULdP~ApIylXR6@}9_+n73BC;uf`~WPBi`_)bDm ziHYR4u5AkS8LZkJM!KLT?Q=K#fAbX`RSK@ZG>T?wt6LundIO<8BthcgqeMT`zjV#`%qwF4u?aDm{8( z=oYA1b$=JZ;-H@>I8IOnilK7f0AhB*Xt!Y;l!fHE1rlo+#O_J-7SlBfDQtS+1cI+6 zM68|F3!2(0FupO)u>8q18CEd$4LcP(9jZVM)3~(iHay(YM~H)`uZy#sRdoLV>_54ST_rKgF_vl< zK}nV4?iDhsiYF=55aN;-x=A~dp(CLNGZyyFeD!z^kJhyJ(sbbggtAfKvTu`lZ;?4g zoI1^#-Pe*)aoaD_SNeMJ?l(D$9MuRV0eGR@%^M9wo+tRn(*&gdIeo z!IecvDFHrNpcTtYjD8|PC>oPEX%tVOC2MD5JaFq-O`jlfl>tuDl!ppTf&Q4u_a)cC zK(}3`WO@0P%5nRF%%>)Pdzx(tK_+^U9$}IdpkAwtA*7X7yT9c{V|E4G89;crsZ5cg zXVgZYwQLVHO3L?EOmgz9pQ_7(-bG4ZEh)pjD+>H1>#U>E#2PO)=56rxs0*mj|$c+qXdEN z`AW0Z7W+1BXl3;!6*TW6X;?oRcMW7lpvF8&C9I?^!L^R|I)#Wm`}X^vaq?52+v}?G zzCW2mwTn{TcG$NUAeY9nZA5?<)4<9;g7Vk9EoOH~;5JX%zE1ORvTq=FQKY$woOXfC zHjX&%BaiRd8rx#ZYF9^deP`fhy2jMUsAv6EC=z_5(tN+wV~ywM#Us~V7>$+9Y`{{S6Y`%Fh0om59gFi{0A^HMiGP_4xEnbs77 zo?6ZekIAbX2h?z^A2MO8er5&v82Z!(@&NLPjA>gVYK%9Ir$tNMtV2aN)dX-@8#>GM z!qG5YBWSUC%2M+La?_subGLNB{zT$HEsx&Y~0Y^zD%YcbuVlLhhm!7Q}){ zNcKZO1uG{;QEU!T^P%xHiW;|oO5a92sR8U7Gw3q&!xAr1LB_8(*| z6Mn4@i#R<}Nb?fX5{chZ5`#8I!lV*O_LxpY$6X_+w6vWzd(1B=N-l!vS=%IhA{o;Q zjnXo!LL_%eUOGbZ1iw@fb7e}5(;}Y@A+&0ftbjH^H7S%O8k4NKHn!6_fF;*K9mo=W zL0tY`$NT_=TA)Q8`HI?u6(CAh-yN)NLtX4F`$)A(Tql ziYjQ^Ajr?z2}JQJMO>jKUk=!7nN;@3Q5S8iKe%Llkr8DQx>P}SDwT~JB7Vq=lp@Tu zMNTttQIc7h^Y%h=1t-;nBJm3EOs#0zBz(koB5=LcL>V0+qNy#p@Tn#Sdnm49x&$U$ zfiC<$bJxlnMBv~Tbd-o-sb*SrSC3fWnIpo8`8${CFqFynHlaQM>L4rY_QSfcwXt;L zP6@Tf{lr@{3g3{Xs0(zE97N?Ubcn5UES1ylxEy->Ql*xi+ z~@Rr|>reef00_*I5EWv4Qfky4;$LF?~~ zHdJc2nDbDP`*f@+y4E*6H6HkHZShg(hr$%Ompp}CDpI9P1sNYbV&3Cdl~V}#Q*AXY zvf6+W@gDe5@~EAfHVcN=TU^KuCE+@#kqB5=(-slq->D3(4=TRs|BJxRWw`XYGo4?@Y2Q2rNWw zD$HCs>i+lL#`BbtR6$MeKWygYx6;`MZmR1`E7w@C!e zQW5C5+i0B1g^jzCKuWEA>WT^*&_xNJ)6je4C7%J3P%RD>#mv=NE<3tW+_=#yEbeip zLR=v~i0$hdOnZU1RM)KJn{^a%ryx*axs`QAQbTO1!c#FWhEY$CzWRBpRfWx;RhzHf z!*c6+?L`GW3yE83(1GnR%*I$wXcO_49Ws~rqmr(8<~r>Yw2E4GpGm+xHPA(dI_>R_ zhzx{ndz1mlChM49YOb|x77L0cAB&)j=0Z0;^(yMlKpRAfWwF#LvTCY|nnd;`Msk<3 z1cnDAC2&bMYF+gyrb_+=86LiUVY9_jluD+&6Nhbg#uD?cd`_fk;FAdq0Yv+!B#fhs z=&|dQZPB^_-6;)cGZLF7)e#lbh*CyB#5XI2wKd@iN+r;PD4DkH3MX8m3dB`ST8cFZ ztw(Oj>P+Fygasg!lC}%6F2=B)X3KG!4Cd-BbazUI5++25Qql-1Bu`Ilc%Pg7*>{eB z2u6o;zLN4|vMjI5rI>!7MTtypT%zBe8p;+2 zysVQVexbX7zK`;w%sW;^^mC0IR`K%JbA8C?y|q-qgthYatphm)=d}ZbHp(*Bg#)k=I`@4mht}%BP%C z!3sJ_M^AL4?rzlWEM#`T#?h!>8uy=fWLt7r;zbJ3Pp2<+7K?q-ySBb<^e$X8%7Iq( ziqc$ahPJQxC_fM(C~&J^0I3-A?=-x=={R0niaEOIy$8$Q{cX%YPdTl0RFA#x)NlQt z^Zm}o*803sx!=q%On_?LRlU`3N@fX~k*ZWZlj$4(055x~nM)3v&-EI-&%V26Om+8F zfbuz39Lmp2bEKlnEomW&@BCwb)O2;2_5dJ2-@nJBcJ=F+^Bb3+_brPhV3mW~nJl0Q z2})X+*S%v?zQDgh=G_EtN|Hy1LpaaoS_~u=qrgg8I3axtc91JMyNE8l3!6UO4U0RX zH296>_Y<|7q8r^!p+CH|$Vb5)z{X>&*KoySKP-U>QFX-YS(UQf8{O4u z2?wXLBl>%Y-Knb5Lw{*2J{F{`Ynrm|3coj%;Fks3ZU~qwNtjgh)GA^bn)3G3KNlq5 zFxc@$*!in*foTi?uDB=3uPUIx zT=TKT$JU4Lk2FSZ$D-B-H0z?PRg`Gnb{USTKEoeDTVz+x7yZ5+vPW`^f=Eyk=qDF3 z~r z)YL6XQXXk4TR|JAEvihT{^|ybTr{Ww@=&9=_bH zRrIQAzSUIqEm8_d_}Or_bUx7^QFh00Lvw7|8fpk%BX?J9%V~J%Tqr?K>|cSo12Lja z&dYB}qEz-cwSghdHzp_Q&ba!u7y&_g|CJ_W-teYYJ3Nnfxm8GZ6+u4; z@m#sIBJ_{R?uSpz8z*T#YD{IM#a-HbhncHi_4aqo{-?s^6~hS1sGBvGI?;EbSNCo- zxtVpYoP(o*RaL$h)G+`)hqXgm8RTgvB}B>G#+l?1s` z>8M-U`h@C9)-G9P)6gnMU%AGob$feA!!Ukdq0Knf^-|jzWyvXf5e_6MxTf1Xk|(Z* zT_fh7I`X%;o0#t5KL8@D>OUmxY;zWpT1-ANG*CYxa91E;nQeA4b;fEbYS?h4rFu$I zR-Xk04J1T*Z)y3PVS8lRT<{GSmiK>@*$Z3OF^n{-8qdHPl}ZEm2AZ_d4PANVfB-u5 zF)i<#7O@^!fq6t?e3`tEbInFI4RJhG#zjt4RQ~`rUs)3r=^!)|J<|{+z1CkY?MEXK zx9!2;#)qjOP;PU}xejPeBWLofGCmfqY@MjvIs8)V9+lf2oo`}!kHr4~73AI3-&YwA zgnTUU3MT3d`o`sX+TemoABV|MS`>;Qc7Igv{Sd-bv9PBVf!t-4y1a$Aol13OroVg6 zW;1!{?<8j|CX032*OnINnsHK{FXuL2m-%Z>E-BkYwm!LTmrY~|jFpFMTrvF8KC5wk z-!_Guu|@*4eZKd<&7HgA`Cc!^mnJQ=d0{H6b=q`PILn)M1u~~+u&2YQ_C_PQJcGq$ zZ-vaCGW4$4{{Ut3c-udwZmk=7m3rcCe4xM(#jfo@l~cAPh4lnTN8LEi7BH= z*;1t8+I0J(7`s~Jnh=e+^A5qRBs!{iqU_H#t+p=a(xtI8kqXyHyB&ckWq7PX^NQR_ z%S{v<#|`Av*>x^Gacr@@|vVwrH@-4cP0!3}U6wKP*Ab(Rb4uHFx$#kNL#`!< z2B65uN1NtxVi_vkm(zWl+^)K|{-?~M#x2BC09*Eh01IlhXq|IcC9G=S{wXQ~L@0QB zVrxh_N)sn@Mk%`GZJn6;B{QCy#-2uhO=7KXKdirS^(O0;ayCv!O?|Nr3+5pc+_DZp zyyj4vB+7eKqxJy~&c8(mYsMV)S=Cm#2`*&&w2WKjC3--BE~#sl+R&ZGUK+z-oy5^3 zw!nn)MYa2dIC7mE)S(w9-Op`8qIJt+cBvJoUZJQ{RY+1wblWLFZ6$|Pd#Lu*CnHsf zajiN_Lj<5XAb&DXC39(xJH@Wa8G`kHy&ht zoPM_d0C8^JK+$n-r~x54OvTN%HTMtYYOB4?MQIUx&dU1n7YC;qw>48($EjHON|s30 ze{6eaZ1&?VW2?7cDfjo7kG+(&hK&pBzv!p*=Cj=HHs0lVix+0L(Q8q22(s5QhgtO! zCG!Qh0R(_f*^g{yJO2PZ$j+2Il|PrgyVr1@f0Qe0^*`hOF7Ee|IPB;gA|>G($73L#B6iVKp6t%!@;wnDbYa@G{HKLzzF;dY_15a@wv9k^Qmsk#03&|Y zNDwA^$Cg~Tl0pLdy3+1Bq7J1whq(|=Pfr8+@@= zTFUBQN?s%4Y?(~>MyAI3uV$rTa+Wf(M9a$`Zjt8~K<@UGdQU<3KzGw}oet{L7Y8u- zmYDVt*;U`Tyk75-UK)M7YmO2n|bj zRtvWur1nL~e3Qu5tiJyMTV=*EYW34ul}aU1R>@S-y~+|yhp_7fA>fw#a|U#KB4`W=UtoE(B3gT#;Ym9D)zSj0L`TdtfY6Olu;n0p8^1ie(~+8 ztfT8jYQ?njZ_PVA^~0?vZzRLRWB3)5&hi#gk-0VQO!Yw~U#>i%*{Q(YeJ;-4wmcM< zgx&Itl%zRWun;FXpFezQu=C9@SpMX!^euLl=&6-5KY0v?a@MHMH5=(>rP~4 zI_Pq|hiU<=Skp~Tuw+D_XSIzyEqP`1C>2#WH%fmI)(mM?65BCKmJFWy3E8C~8$D(G6Cojw^^3c#+ zOcuxUPc~y5H;uV)xU1(bbpzA1S(+o(sdic=Bfxo|2=eaV?b$D-uFc}Tf4jTV9ArH< zeB2chsHv#|{&45I=EY;9*2C~+f*Pc6ss_cKRWC6=6CSf;U;*erJ*9XJ{$@!X)|l_X zF}UBmH*&1`>WbQg&|~y%Kl}#NLum>8BqaNy^7FHRihydX@%`fF$y(^BKXkpxH<4vm zciF0yGcGL+0A@f2Uu5H5hQbSYfbdzo)=k$knk?YW`GxxWRZ>&FrWrFTAwykbO&eMj zB#bW6Q{1~VQ|oXCN!m}3T_9VljZya2pbDfMBOh-ldgG8}6;4}ltaeUh7f~(CUsmav znUQtFr?6`RwnCOk2+$^0d&u<}mD!f5&@J{`S_2jBQi`$*VS77o+(nF{5^o=ysef{*(|Ir*Q!WWRapd~hC;q2D9`Q? zb-PK`XY%)riKJIm^x@mv_O8+J8at95*;=+zn^P;0dZpr!6yp4Lz&Q*{Z>q;^vG8|1 zmIs}OQk<5IlJn2!CFTxX%s-JhM+@BcWt>!>O{YCGKarr__}y1ehI?iJnurmd$X-Q_ zXtlI$WjYEe@m7;$-fr(5NP^AY@P4K&=M>|Jc#jPJOzr;3xx0O(hr<>3Qj;&5t0)Ik z_<{*0dZ?4rL)x92^rYNe9(kSjdl8x3$JVq%0OWREY`mZW;^FOvBHy|shuxukhjh;hYPR-RS zDp2XWr`-`c_L>CohNAFgOLfhqWT&EuI>&ugQac+&rp4{h;x?Hh#Lj2*L*t&RzH4p@ z_x9CMh1c3^sc%_t<@tTmt-5?~7xWze01}@}D}SxGU^|HcB*P}!t1)#OgS1wK>;mwG ztJRt{KM6A;MqUs*T}57WMBKpc>M2M`-S)%!iYFl4_2uO7Y-ID1LZrF?D;)F2U+ zIZHJKfI;XFfcHbo0Tobkl%n2#+5lO6B%Ml5y&!&oM3)j1uEWX7q^hQ?r>pedV1mnl z$b;9Y@rF-s*EWSKE?pCW33DmrH*Q&77C}){^L6(Y8(Yqvq_#F|_5lpEwhU(|PrA8u zj^;b5X4%P9k5B=k7q(8LNdkTFG_ZU~n63jtaZu#cS6cufQ+}%GMADg0&)*MD3AXL^>4v2O zr84tdJw8opDy?-)vqIutng|9-(21Wh0=`ZGTSEbEs#EPpDB~+1DHiP3ik06fZI>jM z9H|a7v*nKU6w9awNXuur-oQO;RojvQBdRzp5#T=HEZ^+9lFLt4DfWEIYDx)9hzfOQ zO+1Wba<~@0VD;#|yPc9NV;Im?RK8DqUh|q->rG16ZhEWYTk8SE$P+oM%*M|Oc^S&Q z+qmrv(ic@FqTtO~swpT?qjhY!q^vld$m^7i>62S^fjV(z!8G>YBrokJ6HxwR?1iE$08 z-BE6|ah1Zh#eR7zug!SxDy%hhO*$Lg;ih$I8xuVOV1{mlxr~(?+?cPwUtY1bX*4%x zl&|nwrKeIBM&?{H3mSMqEu1F9YU-%tBd@$-)fF`MTY{RcN+nlLu7LQCy2OF?3fj`v zr7}~SuM>*HBw^VM=W6I)3TvC~q>m7Gi>dE?@Z@HP2edY}tLl-)&e`Kg!r@ z>c5Fv%{kPzDi84#I%r9hByq8z&?u&b&Uh}>GwPUU)FPd*hINujB#*T9ghrO|Md_{8 zH7}tQ>?y^oO{qyF&%z{QsG@WwNEVJd%PZYNX{^t@mYf%^;c={vFIsi{^H{Y7$!l~9*GiOKm>bk5IBZNZIX-#1O(9~Bc3ild z-csd}{oz#TDs{sxrDa7UY`naq(l9o>j9y)sx-XKdm^F~QO0iD*{$h>Jj{!OO#)VUm zoz_d}rl>(OcA0y9Vo91%4CFnq$rxPJ);5J$D=tKeX6c@zsa4pUSGZQ2RxBh$#mes2=(Cy7|#vf$Pkcq>7L z-)`J%d}|xNI%;e;ABxR(oZ4{&`S=myBO!fg%cdF+h%LrG{r0kklkk;cZxj|huFG9& zx==NBvR+zSH!E}KORPT2so12cDwrUH8CXQN@>Qme1#9nZA-sgO!k~rn$=~N`7tYr6 zR`NVmF4yq(V)}+LcAD0!U0R7*x6~}m>D85=!~~9`U#yw&)*5sMKWL#wdT`~qXlhCdFAJhR_!!tLG(ok zk=?tb=N0WIn5%1I2u!AN!Re1x3MBP2gmV$UR?w8%$JzlTGz`6ygym3+gm74yOpLn! z0B6b*lqX4ajSFJ{0kfxsq0ZGe>VgnkkSF{OzSvz;eyt1g2>$@NA8~=G7kygvP^4?= zez@ud-&BT04~(?(h|5%_;S4B;O|YFHoNf@!Q6wfsGxm8yL}5GXk+f5RB!R3Wm=le~ zbO_p5=n6mK_d;plld6r1RFEV#kN9~+4t)xer5H^hNS5EX{st4BFFiP;sH8yZM7$*p zEM3-Kia>q7aH2G#bU3F(k~U69UkHwgrgDc(GgH@3Y;Z{=g$&g@V3}$mQi)NIO@x37 zjN&O50t)@HkLwtTDl`<(5(H@+xeO{;AG{OyK?|3ut6&rH1NMlGfj5d1=9`rN0BmLfx_yV(oUQJFbWbySq1NkNys7p4>1bU`kuYZ46v`$RXUo*q0FtMf!b2cmlFRBc*k#}}h?89UqY1XuIz*E%Y;t8s6fm(aLvm;35_7KW zp^=XTgo5AV>#TP&SuWAm@x&;}O^;!mGN}ySv}j{A-AV_#W*YON<2_NP>{Q5?zzm>z zr0Sm(Wfpo+N%X+~0EfCdtGdqdTwEw=+of>q2@)kE+Ye#YIg-Arl=5;zRqrVcjThnV zFckZ(t&Xe(M9nBEV9G*z!0O6GxLG2ZWGHG3Od~?dXck>}UiC}$`&BhGPTeQ_BM;rA z)p{>!MhtaO_idK?vZd+da$4qTL)tF12ioy00Kghw^Hchx(k;GKgSq+CW6SRZu=M%_pVcS^`#`L*qN_2vdTM5+M@?rhbYL>B z-EVAuRMhR+(wqH40%jFI(+usc%8x#3z^0s(CCkIeI&D#l$%n>OJ*)l|WBXq0yO(>v z%$~#eqJ=eeHs;Xf)}?C*OGt4<4|y`uHTW*rY_OSSJgitgDc?8AiZ z+);42ETOBTR8BkVslb&5B{QG6n9$^VCf+y;f}zFtoz&ML8n2$qg+)E5GP_dKIl7jf zrPMO8K?Nvuq-*v^fIdMSNx4xfGhJ3&km}f6g_PhK)#R^~-dn zq|UKXkB!}0(OF!|9THCEo+-KCtC+f3o15y@TXd24#97P%l=C-qPAfDR+Bqj-7*8Zx z>zHP;sG^OT6{m3{Yz2$O+`{2fXr#$x;gD>eSyv6zIha(=aCd|~7kdhTXikOj$>pKw~e5$_k@y~d2g}DcXQtNNm+rbqil@tRm zz7e0v;XxG}t2->Q28U%`ar_GD{K&;6Ybuf!CpnSUQJA&GxJjzf$&zVhLeN}ikFCPk z%VwMaQVNW{k-vt>G%Spl#FC?~P{nPQTTMdRU?o8%K}jAlS(bouY>3BPlJ!h1&D`(z zgKM@hr<7GC%6@*x#9Oer^jle80~G0B!Izt5q+V+pQdB@xi6VaK#p5xjmZeg@>{f}} z#nyY>dLE^sLgv6wT!9@u&I82vrAlmN_N%S8B5kVjR#dT})>2lG=hmpW90*s7 zjO>?d_A-X3n@Si%e-o#qfY3@&D2)X3LaXjuW4Kwejqf+GH1EQx84@M>A+L{>OB>b7 z<1TE>@tRs2osQtEdzRFKk4C!1Pm^*9udUH0RZaY`IE^+VXh|zxLuH$)s zo_{Q+%X5nR-6gC9vu}2##~T0unE%_W ziKeWlQj+TmM}VJ0?Tg8xHCX8DT~&_lQ?~r?y1rkqaV7-F7fYJGONGVF7^I|Zk5Shp ztbjTJsWId}Y4?M1R-Ck}-=JREE~v z%HL2AR#Jw5VaMenv?zZG&m{n;`CGVcB<=jKQJ*7<`U2Z1+@+9f_oBjE{{S9lyfv5Q zTs_Lz`}qCx(DRh{J4@+1+gMpj9c+*{AyS%DG$K9o0o)e%@V02r#l$Zl$YEu)CN~PS z?4X*;CoOj>nioDVMMNb-#A_(@`&oJ0RpnV5a~zc5l_?1YGBN~+b=+L+BgI9Je=L<+ zkHEQg%IDjcB2iFRJw=k@6zI8GE81B@>@Jb;@>GQlK}k%(@Q;bR?*ayEmESA0UvKtS zc)tx%yHu&oHrqda90f_;E*nVQKR0pZGd9lPv6ctNTq&9eZtBt#;G~JG*kpA~L)&-q z;4gljWBoVmQ5l>>Ih!7&f81)!J43F~ujQB1_kHp2vxdmf=9+@MZpK+5abesdxt$Bu zme=G9lM@06!_Cu?N12DM2?av7ERi<)#n8HTo$|#D z&Vc^_R5wLE{e62m^gOScIh*TGr_NVtI@L^@s+-9 z85Idts`H6+K3IjfcD@^ZJJXvR%2J(rNJ>3F9h$<~e2nHHdByK&=dbjVPDYg<+`De5 zTWfylmLV=AZX`-)&#I$OY36}*!&wyq$lrMv6Ix#^+NeIO1QzQ0ibYlW%`l<^;E5l) zJ(|lc)rdHXRd@zlwpnj6V0Az%-{op80^ifmNGX?ua6p_)ek;Y;ZH?%q9Xw$^rbOS< zU>s0?mRf)^B!T#e(j6P*a^Y(LK&hK!9|~FD@75gAB8o!@E0bEwNt1NI4+E!!PTNkKv>yf4c_7YwK07yzo_;_j^rE)lDT+2+~9Rk$ElP=iOwjtVoWi#Vz)# zj3rFErpiBbK~ZEJ0uEejU${@)f0r^#Q;emXgMldh-O86=DaEyqaY)jsFZ)Uf8&Va; z?}HC|Pn?5M$@Qq{w_YkTdh=IPmR{k{=mbB$SOJ4#*& zh`xBo(^cvkCQ@fKszacJZf%uB9?qPKYM!)x+*&R6_vsNr(wiyKI?iNENkqfB*aa0- zzFpiGRZc1F&pgiY1alF*(%RKsOY`>oa2u4C%6&wIC>c&O^WKNLp;}tlxr3(dmT_Nu zq~)wiuH}l0y3tQ>acsJfNS@MD=NWtM;E|?_)3H5?Ya~F9D!AqRfl-;fUb#E!)F{lX zt6jv#cIzS~-EOg+zx0Ddi%mwVrh{zjFiKCHb0aR<82Pz1DQ3lHtY)Yy(<+>dytVeh zR`%}ZL1Zq2n5GMbEmEwDl6M_|f%X``n0yN-X&@4#{3&Ru71gSBy*iQQ0^(vk)l+u` z*dS3yy};FNy9!@YW_p3_(klCz+N-Y1i%P21w{TXwe3D}xy$Y+Q{sMIxjdyUC-&Z>hckLry#Q{|@Bhv$fCr&Nk+=OhY&0Q8N7c6Rq|@GH-5 zFYfGJQ&JMG!?uj26zZt3FXx*emYSZb)51_3ou?tyida0shnkXTpiGO1c_P~Usxhjd zQcRo;5<2;SV=Z@px?{dlwl+QGj&pu+mhp}}&gz^)4B1~o$&!g_avmO0Z)1tMBo0!k zbA8{AjlA6B-n4sycpVmcN_(C5kQ)C0#GeKZlsISdz-t3m9I)2$XJfw za+VGstiSS3U6H5r%I{fC*EsqEq?FI3$&XOGwuP~BxY40_wi-LBCNE$m&R;cs{r9DK z?w6CP8clQ0Sg2koZE9M|7AGl`lPUd?7GY5 zJ`u!~_lwz26IggFhV3}vq;8LG6K%Wjf&4&`y=2Q-pE&wX$}tQrjl37-=5p#=>!~gz zI=GUn$8;UxQudJEL6_NWl=li`0&4B3Qc&aDx=CJfXP3T;+TWf05Pge+yvyVTv?rIC z?!I9A%=(Vp+ z2AJx+U(74liKwGaMtM}(Rck~5gsg<4Q7M?B&AXOzwf(OEb)YGq^V3e$s!E)MRWjhK z<80BY-oK)^WVcRV<%?f7W!9w}FA=Cm<*mHg2&#M?+mcn(Bdq-6+WQ+2V7E6<8q?ul z5r4D(Ww7$%AT@Kfc^n6V*k7%mJ774c7h~PC;;5#p$g-ztZW#u}B>auOiOHcV15meI z8Ux@7gQU5Y(GvbE$vmL!j$7e7MF1PGmuD>0s!7|mCC;Er&)F1CU@PnR<#Ti?PL9BZ zNFd0WNm0yYQ)Rp53PH4CiFgH{i)hk*KXh2bZ=$5QzNWQO?k9seRanI`2k{g}fOt+U z;evykg3ev!3-lYWtly)b(;d6YES<&kRfMA#p>Z#9_9s-v)ZAZarAV)Jw-YkTPxnCR zqBnGuc&C}Y`XdGiPsPNqV)DPsr(HZxIM8gqllgCzvCbvMU%1z}gslk;+pXvzE!PN= zQk0JmKPd35E*e|etGMF5@47pdEPcODJqpa8K)X3hk5=~?%QYWARMLO;kH<-y3>5;V z^ywbghP7iK(2RRm&Uu@c%sTQPV#sb&?!zMF>y25vUKH|z2yTr|NwyFV!jyp?sfoqI z4viF8JIUpjbD}53In%VZ49clE5%D_nhruXFnTQR`JT7doe4tFxIF+ec0%eKU6V+g1zO{{RzF@*aFp)75Q-+UX>e z)!nMXK`2OGp``ejGF*^!y(qQuUR`n?;9m9K8DgvM9{ZN^PbFWKg>Yvdkx`rFM5zcv zDb*Ir3H~~l00{vzDF@7mhdGxvpg|Cln<0I=_ICWjQKe<7XytuuS zqX0cN><8~vUy#0@ec5AHn!DTlFM7LBQMO#TE`he|o?##f?rNE9S(lPhV&+cjuz3TV z0#T6IUa)AxK(mtjL2<{cU1|2*rFo?-s)~0KP_!uu+N3Qi0D=gS2T>C_!j&5<3u!sM zsfBT-qpoMxr$DBvGxZw;NnYp%O+vf0x-B}UvtF|fNk?jjL99Zq44{PRqN>#@+)SiY zlj33R8lWczs!g#r!rA$U#KK6{dO^%to4pNHjKHk-i{%Y%{{RrBseQanOfKbgVTYV% zfyYdmTdp>emgb?MQ4uqxXf!8PpSfjQUj?#!6JG9r$MnzTmllU2(IrCjZ9+l=)aa61 zc_LEygttibitERg6#43B9O6Oo zMET9yR<^M>q|gXoJ{Rih+8W1MTZJB)h}b9QeB~bgAENn=W&n`Ye40X5qrcBn6CkRN z+pO~Dn4>+wtQg%qsuevMb&07sQg1ENr+qp)JDr-*_^A)a}=I>bZ__Z>VKbX2b zPi<#tJ9loYaT5fcBqEsiEvYA$S}iOcfpekdmKJurE8{b+J&pP3YKefSKQ@Fs%nMSZ>eadva(W;+ETBILRw3#;*Q(6@66

~CYj;)mw|n0K;+Y-CD8n;*zGZQw#&2UGq^@S2kxuiJ@+xUffieAAog+?i zmow=~1?KmiqiJet4yse)F5!3=7~*?1uC3M9^++G*y zveM&we;zIVEz-?@E%2@WhKiDtE_$A!D&4)sHa4gNBoAJOCxb0?&BCv_?HdVpN|@eL z#|>MPW42~gG=MnchN0&U=M>@GN{+E+&g7IGLd@hmwuWKwnkNkTwtRY;C z!x6Sv`(46PyIK)%VDWd4mnPy)fqGU~3d>wm8^us--X5Bb zQ6`trqu(B!*^L*NTt+CO8>b!I-eT+Wr+TTH-RY4sW@YgEV3BeaXVKMFyu#0s+M9Xp zj(h@OOKAOYV7O`(_5oJA#~|G8&Zs)X-D+VA1wlZc8O|51)!(OlHCAJ`>_@qr&YA9M zEjxTt$}5?v zeId<+qeTKvg!F=2a#F&|6%JgxHoaV_={Bq4{7*@xI7s)Xh?Yynk)lIn;kucuy{%$&E+kbv<<&_1YH)y( zq5YJI9M7icme?uN9^ouvkJ9;jH9AXwg+Z~qq`Jy*8}H2(rM-nz{{V?`EwZ8Xr4)!C zhG#)B(kQNT6k01RCpN0r*zwF$j_s(p+!Wdt5(-cf6s1qYSfY~VNluGP4U-~mQghs| zP^y9H6;N4_qqWq?E=tm7P#f%r+NWjluYgd`HS&`*wra|zE~4RYLJ#yc$%0oW<{_WeS?ac}f52Xq4XZ>bPssSd zuYgrFx{FSGP2_58KOy25TU+14dEnFR%&3&V@Dz|EBi$IWG#sl(+xaepwFv(JEoZiB z&Qi_ZvM=Rmjl!TX1kwW1;R^y^+6E@XSQgs`KV1-cE8*8JYO?$d&n;y%)LdhpTuogS za+2ORd3AGgk^YDUXHq91C+>`{cenZu>c$q|x3l74BYD??yqF$;UyNVgz;fzlc21-Jcs3KxAc6mAM<`PDETng1@aMsx?V;q9K!F-5!d)gLX+HWURV%MPU z=L)>3+KxNTi)135Ul{vgn`jp|Mr0rkgGM`^$Y~@)iSwJc=smIcXN*qtZPO6t!4pxe**zRf(hpFil z%{?=8NMq5_PD|ofz9A!BlcJEGD7%b&IsiPRJS7}Lm`W0Jw8&8kT6Yxw9U>V*A(sSk zMO99KLFwc?KSTuP6!u)vbG0ecY>Z7x1tTV2j)c#oK}7DMT*_v1lyM0ol^H?@?@E3G z5lm>m7N@S=J&_!NM7jt`JvNVQLq#t>h+U{hQ`gElO1zpRdY>reAeTkzRkz%F>k zxxz4oB4Cs5h*N?xmkDCF}R2;?lMK9tPMH!U{DHGymKSU=vUDPO4 zQnDnS50_YF0(Vd$0}Q18q0$;ubw*Hz5(LM-5R`TSy<6kt?A%YH2pBcAW`%m;Fbg`CJ(rXf+ zspu8f2kHcCZDg*>1;6BtWHk;{G_B%#kZ0|jK{2UX%N7vS*GiDngL(Fm4e6uO(7I8f zjjMEOCH5GTV4C>Kfivzp#Im=`)$X>lEmJYC-xU|u(aEV+mN!yGphPg9KEN>gw(6;r z(`#hU)R>w3Vhd{+qtq$xFQ$-ctQh3Xeuj|^Cbb7_zAZmw6z8-dL=RQBow+90aI}D?d1@mZIsK7ei%1PXd$doMQ2CInu4RMqndb zErn3L#hr$fBtjFX*&dYG=+M07^87_Y{L)Kw?*r7#d$fwYr^>EzWR;q{sFf;`p+i~p zKHy`{@_#8_fsYgknZOQucj}G3LH&9|dvA(vWDZp{WY0?Q@dV7Bbc^Xl9JA4l7NY$n zzM**UVjaLQBIK-gry}DVt%m8ZYocgBSwQnkD{i3;Ocw^SQbxWaG-EGZbD=+Cpo_P9 znKsH6-u+QNuRXr^mD{z?h&WD)?9#F&Qn^{0-KZC9`CpfHFYIBLPpVdBw{ZYS_{YyP zclE|P>!%fKRrc3v{{XB}ukvdKP~H>ibwTG zo}xWE9`e7rxA@#w&wb0~-a8k5=0*XbRI%q?H4Z*hRN~Cmsp?5HF`9X)uN6BQ185VH z)s){dT_JHxOwrRM zq_%{mTFp|{AJYk(#Vob3f@RQj>kdt-6$~_!xFk&e*x(YQX(<|P8u-B}sB|a+p!j?Q za^!u=cIw$Jx|yVolALu171dGO*o_lccIq2w2V7Q~d71ddd#zeuP>gNS>^sgG8zf1R_h7mzCOw94$@v}(9O*$ z_#9GzQ6y?&y5f~wv0N~V@5xU*go|Bx3*s6__pQ7{CSH#j8I+LmD2T8 zw#`D42ls7;$=V4Y5k4Iw#cAH7(&0OLC(R6;^0*o=oQrNu%eO~e z4os=V?cRFLUVhm64fwuDLk9)(`@4g!a%6V3DtTi0W4YQJrmHDZ_U&*V4`gNCXEbtX zwy@t@%OAv~ZqvJ=S7yDy;asm{Ta`9iI+Ro9RzxTtp0m8-Q&7~Ep->+ZC;PxfCwz9J z@^*BkTom}6#l5*Pmryxg{&iKxKpwM&V@L7}%|<8mIA2K8or! z!$soaGRJDK&u29vvhkYYquDl9 zLWes;?t1?K+UGS?RNIe#CB97#_j# zflyFgk>39Rr(}$BAAp)I74EyUiVLNh&D_^(R8D7{G06orb#|*_T&$_GR@9y9Z_;HY zUXJoar8droNk@)$Pku>e_gdam7Oz+CuH1&?iQwX?zaemMA@OC0kFp+4u&)=j@+#Ke zIZakQDwo4)n$%iyN=O|hM_AqC`@NT?;xEb-bRDwTT*DzB5~&@+U=(;g=HGF)5QZz+ zLf0=9k*Sn#JhAR)-Z{}faIyY(c0^d#GI;6nD8|c^Aynmxl}$(kV!Y$mCbFqs3vHVm zv?)OsG`#6ZAdd(l8*Wr3#rSckLW=K6*^wfj8N}uxL_Bu}sli2WN?$-CqRg?}P z+DDRPq&V3MN`deojSOY`gSVl+w=u{n3flIEc1w)95;06X_1tGEcOl2fdysMmvNy5+ z07CC>F?|$rdV**QB1vjG3%9H}NNX}XlA5O&W z7s7W8_{d20GXM|M1&fxb9b)%zKqZ=`fw8459)u*z@02!6hIpvWl*58QPe%Ds3qeZ1 za7(NP0)!lTCrkz^Kex}erOkz)d zW96Hp9H76#tJ0t4UQ*T`w9y`)u{tgG{x@4|QqZ-y0Qf*XNcO@y^7orfkq~0>+@BWe zGRZ7WF1=>SOruJF2$2!C#$G#<FkM9;}_QmN@@Ku;fEile~ft#aCSuRd3kXGeGl55^={bt8qi2iDxBvqxvl0c76r0@ zaVcp;jVItEEfxaOEhKu@$=vtvKht84a+UH}VHH;<-M7;cX0n}wU%E6HyDW8R-Ac~p za(RAUXM7K};0oYMNI&wP4?0+!+9 ziZaT|F}iE6zO_;cCBS-XtPU>URczaM!aBjNsYyc0k|rc4(g+i{pspfdS~Fn!gmTS~ ziDw;mhx7=Q_)IxfKo?LJ+M?)ZW=6{owpO8%(M3<`HRg>ZPK|x%BsaJ z%7tx|)T2KL0`fj>R+hoSIyFI+rBwB52{Hib_r_l1M=j(4J({QHJAO?XP+m^$vXStM zTPu*!QmAqk9LGf?wVvClz*nRK#HN4A7XJXNF=r;h$l*kKqm-zfU^J#?k~JM6@652E zqV1UhOBNnqwb`0jWuk?s5i#cvW@&*oe%O`gRR`wz6S+m%+T|bNESV#$wVY?Nmpyk? zY;bt-Zyh+MS!56l^$}37IhSZ`X|B4?6*rw3N>o52NaY<;g&o4Sh0ygY<(FF3D@h_p zuY?r^+v7C|ON%Jw6mY0kpNu<}%JDBvn#KjN*-g6&os84XvK_m`S;#PTiM$`;P>jia~D+N|LgW@v2{ z4vqD-t5bfnr31xkGcoDb7EoKZjQ}s2`=>dQ%1K!{o>Bf|`k&!gow;_bYUm&3rlAFP8C68JhRK>Tmx8I-x9G`ojaGR?h4^kO_5y4w^S_3m?}UI4I>?z z?g($1SWOzfgY0iCCc#o>ipk=uIyherxe%&5s@e)a5ZsfgjB|TR(odDk)t%-xLLZ%# zMeDnLpkcD(H?rVJm>p&JL#OOGz^HPX*UfpH>J}+mhU}=aCbEdAKk3i-zoISse#@c@ zR%g6&Cgg|gqnxvi@(kZMdX~XV9(JL7Qc03T)fD&LwQ+ZGNW#%#yXw;$kLR4w1*$!m z{UUBT&GOTgb4}@#<1DSWSB_IG?|cKhYTN`!A0Q)n+g?*XCx_e`j|KBrnm_HRbY=`= zNO9Ac{@}iXd6&9=2KsyOyEx_`-3oH5T2Ge6aOz@|t8hlpwFFFrrh1bbeC5nAGmua; za9=cbCu3x@xBYo)ON~h@t@E$tpIF5lfM%-mhM)mmCX1OVh*7y)@wSc8y~Gpt3=fzq9s0& zX#uN(&E%n@RgJ}xnuIct0*c5QXk$xlZ^dEnZh2Pz{Um;y_YUVX%1%ko?Jl#y_)E!M z&Z$7QoAtq!hZ924>RU%w$nf}jlP@y!BfcA94@rmPE6}{L>`5|rB1aGDkG*}KWqBSg z#Taw93dOT%ve_67(o-ZQv?VR0Y8ygR?vMgLT(Y&A{(llqtLv<#)xIj%lxRR$8_oRx z0Cwc8zP}~AI1_53QB9uNGqJYPWIF!w@iFvQZ(vz#2xuCwl>3XgWV)ES$Uar%Rq|(G zoUvB5e~ja*>PJ}BA;C;mg%hXl>O?l$)WMYpgMn3YUG?pGYp7o|&*#<1yE@|zovCn= z3G46DJrGUGQ4Pm5#YJUs?pmd}VYkZ*HDZ?eT-2IjO55s=vPtPUv-+_`lDxz{lJjs> z-z%)SHE^j#b4zW-D1wy};jC6kB~|6S!DtFPa zzfe)r@_?lL-sm`M7IP1#cz?7^p_HpLt8H^O`X>?=-3nBmi%>hAk@iFnlBGoiCP!64 z7KB^q=qj)Gx{#FZ+eJ#EX5#vQ1P3(rSMWhoN+uODE}512kQq~@YLgV{1caqWO$U@E zwVS1OihKLG?P4VEzLWb2?IRT7E-<)v3C+P4d~(#usi<+mvZA07HmO+)l+@uG%gz?^ z{D*x!BjP+)nBDVE<1CwSMJl-~D(`Qb>Zskgrx4@3WrfwFo1CdLJK%1^g4N=(0@oB913{4Q z;;gzkFEr!sK*Vg-_XSqjYw1}zOHQyie8aLI!%+atbu#xr84Oju%noxjaM>S%%3Rw{ z8!n;S)GwcZuTK$Sco#8o&m-bi)-7u03te-$GYlX$g;!f#X(_7Oi2$b?a!WGQfMZ?d z-TabTIgd3gIw~&hw`5r{)A?LK599YQoaJ1~R6)DS|%O3K^$&&Y+sw-Mbx8 z`Yv~}=Jlf6YQd^;n!9VSF>`*Y$)c!9S*|E!b3%Fh%J5rFIp&?S-eS$Oj8@E%HyRAkd*@8}I=oOhyc1f{YE!e*edJI`@ zAb&Au_drD(C>?eq;hexapRzEn>|r~MlWX7?r$gp^mf%Hgpc|ELJscOiZW4J`ySTm5 z)b47ZBTH&+BH>IVTx|-J(YX7Fk2s3zOiliqe&y`1ftl=4CszQzX}JfBvK)sx$}zhW zNT#O6E9$5(SE|$U?Rxa?sih=t1pXxG0FR`tJB|;hY=?+nAzt>Z{{T|x#{!$w{0DNd zu&kdgy;~Z`?A7%2*V=W}{JPT1ssOs5SR!cvlheW(FLkxU`eLO@&c=9mG=_mBa;9_H zcM)CPuCQ{GoS{&asZHV*R$O$n2A{;NsR?dJr@~C4t(hC?&&x%9{yAX@-m0_a9sFVs zP*+;;ZVtCwnsMgdwqGw73X00=V$$~nr40l(BQ14<*<~^L+@dy+wEQjEG&Aq24|RNr zK}%}Ca|LZJrrxBvLu|D*Pisp~1QY@sq-`$f2_KFEkG1WZ;_7O5R}ITt(t|)1WxDqX zmSj#T%!ct@kvgx$^~$=gxS4DJ0B+)<#tdvRnt%}U&v7P>aZQIgj_8#mYZU5jwIupJ zo{9s*UkOC_EMZ|3R(QK-vE0>C&o}pPedVgF9aSZ?<13(}r>3k+bt-VX^6duFXCWW} z(lfDRSkk=?EX0MOS2uH;y0^Hx_gqob66&Wj;%5^+#y7C0Dyb`3($fh~)g++gKUh%4f zu9=XhuX(WOU!+QR1t@gWNk;~^!CP9%@UIx;0+pj>VL>B9y;?&hU{ZbR(S>r)byf~F<5x2(i;MVnWk%)i3$<0Dr^EDU0cFsZ!Eq_t z=v0*>A`GHi3p-IMb7{@7UT>JqT)*LV+!G4l-WyN2gxqnIRi#9N>n^D(l1zz`<`3T& z=E(T0(95>Gq$yVx?q7=TRcdbIa8;I~q)0VX%8F7z=oZ|-`%FqLN_f6+g8iSb4$7=G zRo0uOEm9joiK%Jiu6w>McA4u1K^}-ZnTw4;Ss9vo)p=U&_L_BFU8;SyXf5odxZ|(7 zpxPTkQVCL&q5uO@Vo4m>+f%Y^H98H-+w2F7IPVn%h%&@_b%Z!9&cusDw+jI!-bj|vCv58T$z=aeL=+85Ho z!+}kOegY-bzz=}6Xfcm$g$k6V9nJ@p7dZ8k@OT(Zv+4kAjA=!%%A!u8Kp1 z0k4pT#7{1%(?(kD28gxx_j9&UvsI7ZE!TxOzfD$fze=bR`|@6eKH0%`?F8zXa_2F% zX+@7WWEk6Y^wka0HC$6PCS$5Z%*Z``(C6XdE~!gz+*b^3D4A9L%ib@S23)$+B}~%* z4KfKzOpmqx`>-9pdGgplI_L6byp)+ zjH;sI`qt?M;r{?>KZFkn&&NoAP&ahi*F~Tmy4>#EUt@QhGH269#R*${d0~Aj^={V% zB%nMIEh*R}l_>Q(2&u@$)*Pc{JPdO_B#gd5a_L!FaIJK%wz1b9ebc7*9Z_*duZb{R z5OTJNc_snXDcy|cu29^l*2|?TLyeY#PsO|_ZI`xj1^u@)Bqv#1@=6F=way%!aJ*+& zwKbrB<`~VA-fs_}&0*IY4v=Ny2U+>X4+Vh{!0v7&twv9{Uo7L7g1X(ibCo7xM9qTp zQkL%Y=?Q5{GT)?xdCzwC+oC+|8ZTJv?(7l{SDjR$+)r<~H*;K5ed8?JloZw^y^h|{ z2h7^;O{DrYOsmZV{6nIXG@T4&yMH@AHt0XqYgKbs2C&iyFxI#!~vYlpZRnw^cCSOOyli{jPpvT&Mx!e%stwgb%{_j%w6Fq>N z9d_tt6d?V|7rI!~opus+(q72DqOTH=nAMa;B}9WU;p~K>A}LZ@+D?Gz0P75MDvS~e zA_{B~AT{wah$x&NRGy~7d)&!_s|q`>Zt7B)c}p$1?XpDDvj3RGF!yX9IS%i9f7brCnOHG`s8r?AQrifHBm z75vh@dhJp3GBC!riFw12KQVK7OVU^Q+QX=)5&(>*lIZ9JHlVLE3^|Odd+r($v|4U! zky2GJ9ew{m8B zoez&l+IB?vQt_LlhkfKe%Pyh^8&etw;tf4ySI3(jlq{xvEd~WH2Y!~W`eYDYLcT{ zw|G}4NfhC7GmxH#^uSVQT8WRd^r~RoJEh7h=9Zl;gV8&Qh#j0X(OTQ~s~fs#)$_)4 zT29)vka`5D#7@h+qQ`GS5)DTtXEmSp&XTUD;xPfgk;1BRihytk#kVBQD{7FJUUZ<5 zxO`f|$9E-E#u!=^>EqPZIcR6*LW`S5;z*odG314voB(t~WtDGGwBnctcBDvuL_TL0 zwRNMxbqcJv?7uE9x{4-T01^g=QPL>pe4N@XG$J52QT7s{>OZ0O1p}x8#qR$y-wa57Nj|q5gP==c+4;8~F8aj9u`*a}{ zZn(4`#7c>db_~B#Qy-h)5RPKF9W}VFloaTHZJ(wCTUK&A4EYReYpj+KC(CI(2Jntp) zFG9za9`IY+aw?<&@XW}<_TS7`X1-N0Wv|Zkq@<9o(V@I&#;D_uL`Ua^B2!WkGL1kW z5jc0{s^RcHp)+!RQ@?s2Ce3=9U{BKmW7(vmC3}Olbe!e7uSdoCnV5?FEM-Cl+bj#{qC72E8Z4TLf>eZhfDx$iij03amWtCO z%INAAkXe}~-%RAmDo7-s9V68s9|H4<&%~7^4~Em?Izl0gs3~nCK&nGezBzYI3n=!rAN@@)3q~6Pw9k6jHPl&B~XKP%h2$as+Y2HFHv<6&Aul&&QaY} z)Js&=I})O1WG6ZQ08BVOsS*+^y?d$>nU9EW>87z1P6Ve>NHukCQCowpjr#(KPKJ6x z1n8m-A7tg=k?aKN`9i$6rk&-9WU~rHxI>I~G}NW9BLyEGA@>=m$uy zWxLuHk(I7MCfj$u)DM__UYcYGoZ#D7p6wKJI1SXVXv337LS0pQ+XWwmDk5jwtQ+*D zsuxb8O8jDiuJ}T#3!BynSu@ssHGn6w11qmn_69`(RY~&R-+wY$)`wdj; z6hLrDYvDKtA!WGAe7#y27a<|pV^v-c(q z^4f{wH3~jOZ9@*Eg%uCFH6KbBkjZx7pZ06jW3-C_;ye@q)M)@**~h&V#g;}&QmLnY zs;F^mLe@$XsMZ~CYT9Upn(jw&T;nODuDfDg>k2uSqB+gr#Aj{bm zdCt$Vzhxv~D&G^{SGJD$U7%k>-HClyDLE>{_iNqh)~71Nvi1$W$C}sdQd;#=8cS7+ zaZ^2JhdP7SK2P%p$(iwwH&$fOYen>jV)=t>ksT?3YmT1PO}p#tyCCgu(y|U5 z%N+G^+`WVAu#n``DV}jN32*~6&qWTNF}?D`%u#y}Mq^0^nyg=$y@2~kI?)Qod=zWi z1?3cc$Bomx>R3f%ieY71e|pl}mY#EqPc`j2mR&*lP+nGD5qZhtsdp_~)nVlP?${>L zx7}--26P(`cn@H{-k6-FCt& zopCKT_T8cCb)+b^zv~GQq1Vnl_rLs^x5~e(8Re73Z@WLuPo!Xqam>J&ec*Swo%eUh zRGHpbmgpI>Axz?y3UHxKZec_zNk;QBr4cnBI4`jLtBV&AYqfFQrgx8-hUMTxqe7?o zu6KOQo1_7BgCmGn2?VV^9t@eS=lp<04wWnC+y#lvXS1}T=!-CnNEnWEmzdm-dT;S_l7Usx@0N2J8EwhddA_m zi8HcaEjIlyk=AmM5&2Wtk!F8iV@PmtcDwkm*M9n+5_^6}0>aKsHt=65l(gZ6Eymhe z1!^s%CUO(gr?Nj@Y+fUQyxJNHuf=?eWR~syAV{gD5#OD;qjH*}bwm2a1PMi@o!zXj zC8Kpq^L~~)|lybNt?7N=g)!fV~VzT!u$hi6tig#J62-ACwq?6&+FnJ#+tnOW1 zSEJ+n^vGMbnk4MQxNbzv=#9SZP|GyOsv2%n*UbB%{4bR@R!-&`l~de4cI0oF$aD%E zcHvg)l%>?T60gLNq6g@YLx%%wD0xEjIs203+VhZ#?j>7_jQsk=t1Z|z6jYA7c{L?+ zS3OTym_(cP#RTCb=qK%jZM9QP+60p$`XNQRUG+$#iXakBu<*bXCR(CPQ3NO+klu=3 zvMT8&TKUQ+=mecmsO7C2YXP56>5OHV_?Euy-vWI&kWhj;%dgoMk}4?9sn>we3cIpE z6HN3n_Qj?iFkY&&lX9_6c^w;c%(le87#mE?{G(%X(9p7$kEuztICti#+?327p!NMR zota(2<5ZD!!j)_%b6*JPseZVIp&m*~YLN(rHcc;GdS}G`i03kiInX7opovO;F$j%2nyamno|5y4IYR28BuGSu zPZ(ZQ)F4dv1ob^(c|v7V?-#SVP}M_{m`Y;r9c)NcxhuLFD5EDqX7u7`w`o4_ILzO; zwrVYIECT{pps%Q6ohoVNHmxTjL|MUj=8Y9)-J=}sLSCZFue54HShzUYo}U?mqs_|G zp;a?>{j>Eg3Bb=JWh}@Gl9lW#oE$rjU^hTNw5<>2N(kj#sV09uM9lPuf0Il&p?|~e zJQDr0mNRGFZHjw>%*Yb)*GM8h=AG^V0d?9ofx3lbaGqGmv6lr!L&5jdH7b$z_{O^5 zDI-R!&T^fl430djoZGq_;+f>TO0nn>GMP{Gz_||Ldv!~O^5zKz9B5Q(uI@AW5F8ro z(8W0J=lCr!@}AuBP3z}$In~yYY`;-0-@G(21;=(jl^bd0U8`SOq$@vge33}Yj8NYk zr9#i{sE)csHvQjmuqtccD{+O~&TUd%&y#bD`jl?jB~?&91=WGCuo#)y`>8%>8X|3b zGaU!X7_YH4-MUn|skrK#SBjKoe)tL3Pj`yDj>0yL07YJ`cW_c%Adb4rU$P@~&hbRH z-M$F@7EyVjP%KwCjlN1c#PQ{3kBSQC1#Kw!niG_A9z;+OYnHUD@oN_B?kVW0PiWcJ zyrEhy52|v$*>I|;QB#WDGb#8+a-g-B&ADyd@`CzkO%LL zJN(kSmOfpVj>Z12Vd1$lI;YgC23N`%k0M?wZ<&?;ry#o3t9307NOO)MRI>wcG57A$cWP$ruUCKmcxPJ zw)SzH8v>zAG_A}DHqr`~DpWiHE{YUdUAE;}77$@c{=Kk~UXWh(o(PlPr zpCxN{iHeQQG)PbfsS)WGu?DX-nnG8+Z_2ef1(T6@TB^KL**PM~T}rmhwXA8P>jzzD zG{l!y&2xjPs8w+nvD)lb7+-GAS}vCgCy5TS;*jDD#?plp%kL1!NCNbG4~j6FRk_tw zD)&>pwP-U+!iZ4!5duEsf~*CbTs2OcM?F$H+HKj@sW~MGl2KA5nx=%hrCu_J3&K@w z_(58=SqydFAH;Cid50XhR9V#2lsWR+CKFSA{iE_d8YnpP258r0SErPgsoHLw7lM|x=m)}d z`+5j|e=^G`IT@^u*w5 z%07?HIU%4_inoy@HVT!XF#J8Bu(mSHYa_KPqagnPtGJ|Mj2n8@dG0@#@xI>p(wDUU zCCS*x9=25?zqwvZqd&vSa;ByeZlrV_-e_yBqc)3WrGe7IH!^j|`sPhRNk>Eeq`omC2= zy5d)1RD-o{heFgH9|})T0|I67aa=}grB)4>bJ^U`3ax6+dxYWa>FYhyF1){EzBZJ8 zS!t%Gd24oR1W5@(mo*-mN1faD?3S!8jli_})}rgUgz^VHq#saN74^;J^<})7#CYb@ zN&K^pzNW42EBMW#x#SRCZ0dz5H_*n*wQ=Iw-P}ux&rNjuyY6G41Mpuwt6AQy1YbFC zErkUUa`KWv>nX=4nkBkLf&e7%ocv>gh0UsR(TS{uXeH7d5YspXCSe&B;~%)+UYKtl{u)ZZ3$J69!iO;e1kE*TBLc-Ddc;kG;Ye; z-Znzool0r)UvRanqNf?nZ^%*8B>w;}@vRJs*DjlqB_S@VR7VwyYg#BLzLDpB6&uex zpLOcQF?>>MZImvd+73UYaJNOd2jLY?G=4t`j~F%9p~8vQ@|J3}Yq75WInw)?+v!+t zhTX>$@=jY#RMQsw1-6N~%}A+Qjkee(1`PX@9f)oqb>vDUa8qV{ds36Eq(*K zTl$yxNs4imn#IgLwPV=lAY;6hUvRc%oV8BB%p1046fGs+K(v(r+p2681rZ`goTo+E zSkhWzoE;XIx-)S;K==qe+((M}m*jroxi^$9RhMkvEw8=RRx5Muy;8L`zoh*%$v>h! zMhgg`6lhrB>1IC}Q1b6JE-QMLe!^iK(orph1X*>fa00XEBi1{Zrv_1UnzV#xLuR+OHE+Ql}(_)B*F?Uvwo>wasqne%*`Zc@ZsIG5Xt@jKTR@N8TX&Jv5kw!-6Pe zVqi}C9t7o^s#f}Oin>-Q{{Xyls3MkHOwBJ8ojuV5qNy*i@sw{^W)8Q&QdyFimh}dK@3Y7`p`+KxeUvD<2 zd2-C%-h!#t-M79f8Xy6VXna@lE>5@J9at_SAW-sY2Oiq`ydnwrDPNRV8HV0zzB_$0-HJsWb09Da?5C9H_eB zmzr8;C+gH9k>{C2jENB@KEM$7&Rb1SAC#PJn6zp(C@5 z5S1Cu$S4xip=m_=pzAxvpZ2{X;dc(mGzEd#D2EC!WD zW3F3oAdt4SS9`mz@ooLi-zctj9D1tTU5L>r+AYaQsN225;?`6K?}tguN+QJJzMYh< zQ}Q`uaVIv*FzfjPBB*`#FXg+`ssu!!6LH3rp;j-_C4v)mLu1%a=o^M46XkRTYDjC^~k-7Iq&H9dcx7{GB#%iV4ZYRRkbY#MAM zGkIRSY@qHAl(a;Yxg8AjfLU&4mZ91NYp$CFe5JaTPO7g8d2Xnz1uV?09};F`SjNy% zW*&Kti5csf&{m@7`KpCe2M`oTpWPQxY<;g=Yi=Zk98oEZP`a(P zD3`bKC&-9wg+(G=x!&0+O8}L$wC<#<5|fGW))=DTmoST7U7WuDId8I5Aj+fNc|Dl=KNn=`Ouuc%TdlIj1j8 z#&SiMHU*TSGTlD0G8}+Z>%&bmkmojstJFo7#N&4?fyv1!qoYuHV4v;n+xEm#j$uTc z{G*ugS9gXvdY#^JIj%;K?TY(YwOuKcHzgD+_%?+2?L?$KkzJg#akb@-=AxSckYKp1 zpD9*HAk1+bca?4z9xc6m?lQL!{k8T=fNg6Vmfo^5NsRrKgtFNIIQW(H`6!~g=7i|A zYk@g}gOc&~nsMOW^4yS`x_=cWS}b{kEn~s^v$x%EDIk0$eGS>2%G=1pNz^Lk^$+ab zJ5>z7qCdoX+VZsga@@*~;a{;vEPTH2Ct|L^dH$2P^?KHO z&2KN;m9;WPDJwSo7t2L^u2Z6}lCp`)D}^JDF;__P3LavirIws+#3)TFLXuLTbp%J) z@J`8C9>Y*YSHt%l%#g9#7MduM?#M2C$?{s%AU@na?(~s(nX@j#IW!QAHua z(KG!K8>6a|tD-ezvM&DscBQs1wVP8_0m7@LLbfp0#1;HU%*sRC5L|o#6CB*hw&Ds) z$WO)_B|H2SK_JQ!nj)JjCAHt`PuvNH(5LlKsBWa{8zgzih#HD+svR^niXaMgYu7^& z^uq42L#D-XaYTkpj+2q{i1bU=2y|k%Wr-G4N%li|RHEb~2*NI|$SEpFJ#;XXOZ5R9 zM<3RbCWZS>*c`Peya@AraZ+cwvoG^uD4uThLL-jW5)x2%Yd?lUI*A3^KmN`K{nz}M zN+N!(7;lYfz!PL2IEqH4`m|*6)hGn|DFfVMB>w=YL|?(FnMUNa2#z671^2w zNwShUPF;S%0|1L_=qQsa?ZpTx02y_cj4E*zO-k0}42NH5lrPLw(_Ct{LUYqOf6;EE8!c|BkT&pNkdMQIs80Fm{ zkZu`+z)cD|PvRhmKvHyqiLy+(X&EBGxnrMxcPGi;pQcc!Xs71avV$x|eVtYzzg)fpxYj}>aLHk`iIm{JOQl_Aei zNPy33h|Fn$LvPE6LIi7HB;DKhKf6E_wVCp_ftqu{K1 zl!>7wW5-V~Aq;LXZ$uH!Je1*Ui!pjx1*H=jfe6rs6^Yau_? z3G~fQ7g7+Vn++8rS@h}B19AZ;!Y2VUoS^QuI%>J4XbSL=^YMp0i-YK!?O@i&RWnYN z9+Mys*%3ZEl>Y#za#C*wv^SxqKd|f%pAvQZ<3n_QHB7^zOR9N>sDhNK0y@qnA0mg8xym2By>D^i2@2{=R><~~sMpkP#|s|GL#)?BLN9=*MgeWRo2^nTSrjbkjrjON5VkG9Fw)PRatU4#%Qee zNy_-$Mi#0BynyPP+5nN)Y@+J!`ZF4*x3ZV0sPMX?p6~gEEYGC9!9(Bdj9hY3PGzq! z)s!mMVEp4~sHdl)x&uz&At7qeXeApfOv^x_S$oVm1y1yrTV>I-yG2%)60tENBF;Ou zIa0dW%rr+^X-Wj9YCcdxViVAJ`mn2)??ujpM^$q5A!+Dg5+Mt-5>t|q=^T_bc&f@~ z$Hd?g{91t!nLtxS!ipR9v4gm}JdU9Bi2kLjZjqu+tWz-gIi2QON{VpVs0#_xY=HJe->U#MNbG4lBNpCC zu{@HWNi)=wFCTmcGEtQY11xKva8y*X2gP$4Q4QTv1(GLLZ3C=II`n66&#tyS-N zw-BWeNK#0coCN#1IHKWW4$8n{lw7ZxQKpL*qN%T^3HXrS;s7233uEPbY7kP%106(w z*Bx>NW<^b77H8LadC-JY5O;4Xmb3Q2@<8y57fcOS=I_|Ya1>Z~^Cjxss!B%N^;%>l zMM?oP_KhLJ$?tB6C+3*Tzh?y8Dh+g>V7TRX&+Nuj$(YjOcmy|~)x+H>kG1Upl z$5r%~Hfr%pqW7iLuh1k+g#pq$qrWkZf#P`hZoZ`Qt2rIy2G(P^QMO5ERhH*fMBqQd zQ4N#gp9s0ZUyGY?;Jm}Uu?EKZ%sW+aWP8$CZ+61J#RtsQOKmMzciJ)%T}bnYV=8yH ziKR@~cTYLb?1DsrLgv+Yv=)Z;mQbXb%tIz?Bx!4fK}ihnjls<{D`fjI`k>h{+%3-9 zyTiwuf807pk(w)=Q5pCacpXjM;8Y=b2QdB~639*^~n@mK+ik=zj{5WUkw zYIM)=07?B33-VC6h*JKAaP68=gxYTPfnO_ZsCddlYfC?}19x=Uvv(^PI|m~#$r(Rk zN)%WoYg(X|amuytmfzthe#jc*zwzuJIQOj{KgoRL=7J;cPkUzsa#ecJ<5oqm2D_FB zlx6Qc-Nwdg%~j}lul1`tF6^S8Fn-RtW0oIsiK^BtjN1^OQP+}G75-?_Jh!}m*y~Jx z$T_OLue1LE(>wdtR#FvgCIcgG4-^4r)VYW@{VQj z_0O0$?t`Puc~L7CCtH%ZQ$x4aKZuhcA^Ci)b`AJ~rNrRnzj-a)7rWxsRx0B~T1K5n z=?CMln|Bp>6&6ayTNyw)I45g4E#X$GdnHaoRQ;NkL7Q5iaZDt61QQ@4vOd7DWc^mg z1J4>O)8>9i+w>hPi#@6Pi)GmTrPMoJM4FABX-NbR4Pm*?$GE#?7x8_ZzJ^a!) znMpPL!-&?}swh#8p|!Y@l(>|n2@x+S#^HRLzP7p&7m^m^Kj;27>dnyzz$8&t%Br@M z^48GWl@stwq^?}Cm$X4**+OoKTrELE;v!Knfid&fiVaDa(J)^@2yPHNrPVTo z=#K>-)Gyl{@c@{Skf_}?SD~eoASQkA-jZIb?6#>S55%sqFlu3ya#&C>sv#mSw!A;$ zgbfMpofcC-dp}e!NFCF9sEWg5aojYPp7@L~1n!|Ii;gnc)oGFGsE4E~l3QC>&TFQ_ zKYp=&{^wYzmnsn2Do$ql-Yq$folkxeTqt=j4o?52H;vF50$ku|+AB_cS3(Yq#0tk17O_CmQ!4(bQGG*wvU)6kipwlUX(`ipaD>M;*a+P0_0UNJmJ zMFhuGDM&iYSeA+qSD^%|N9~BZOR9!Uq%@fD5Z+XUc&STMAq^=|CR32qctrpQm0hiPNL{zHvJ?LrU+BdX(!S(I>Cj3Xqg=Bmf6rIFgDW z_4%+A-~l~MggK3=NcU*0Hx^*;+*WJcW8iE|5ip8 zLnzBVrETGu7StlIs0P(NH1dW>bVNcmMy$>5L0t6*K@P`_R2dg)ob9wfcxrk@^g~rj z=g|%F=#e9+_CrewXH_D;t&>PIBd4|@D9H8Ic}{{sC#mp>B9Fy%j!KNQW;E#p1T)=5 zemiZpH&cz>3X!-Z87s)^9;@3C{{TUFZ+grB0Cp8O<(3xCb#Xz}D0OmIp0Ve7)}N^$ zuUzdX526S<7Ehwl8q8=R&<}++<^KQ@bI~+F1z&IKi)flvY_e54sM{lH1L8du216AU zB=gXz0?SOIP<7T)`=e83ZCWh8O6q}K9@}_BF6KT&!|*&p#_jVW%j=k+u-h6;*8NNC z2}*hy047wg1cFY2G&d7RXKrM9paFTk(UR`ru(MY<(SJ-Yr?+HyS7ST}W8?c$uHkZ0CQ_1SLky4Mw3+Ax`ja0U_qTP+mAfXt2+#%gZ zS3COdd*>=%;><$gK0=U><_f0QFICRIm?OY=#_zDZTaltyjY{(0GW*x8^Au-5u+xw^ z0XC+dVGhozz;vJC6PH-_SS{CxK06)ls{FOLQEGetEI6o09%zq>m8z-hG`+RosxZct4Vg zuHCNA6_8Xw@gVpJsB`MHaK}_*01y7{FI?&Ym#0pTi2eN`fD|r9P{B?p z{{SHJEoJ8-En1!VOHG_6n$Jqy!dqdVgb+GR%4P^NfPu5dgQ?u2wS13JdAHNp>S+>C zvXHmh8yr#HEoIhm3%)X~jjv2CH|&43oP(Wn>{|)NsTA#H<>sA!p??pi+7vxStH~)$ z$v(&``szD7b1V2Pwb`2X;6oo3u>Sy3ZsoA<+;Ho6)7Q7Eu>02PYF9b`03xGFcCMnO z{WxGY38~$o*_IrI_h)S4m zXvyrCwad3J@+TzNcz3$Kbh8_NDUxJG_0sXpj^@njoCu{$rFR83pp~oul&VMuNl{SK zcMsRzPy%6L_p1l?!L+smK`Y0(e7f?b*8s+HyUsOD&lvY0wQqSzXD74#{Y4#6oQtVj zjRWy@GuAC zH}2OH?bDVlxw<)e(@Sk(D{kj+sBFJZ;#4KO(poiAPeL_|8-D))u$8?zb}JX$o>gQd z{-h(~3+S57^=j>U_{GmXq|K~%?D;g_g(X`ScONYl(KKyIZ*pXG25~jEKO1sGV|nEE zuRXoRXEETJW7T}qcU8=nUTfw2!;x=SCvMjaj;U_0smYJwfwc%qPvJsRBoZTT*2fD2 z8L(BDYY7Q5N%Iv{YARV$j@5g1$cY$G>aAo=q9}#$Q4<6RXH_}v(L8Ll3#=+k!c^GY z0r3JRkK$F+&rOrU-)MTSwTe1+R`c&DB`82DQ6$SinV7PQ5k;HHWZKbEXtg`H4$JD_ ze&2u0?WU^ZTo9`+Bchh(;WVk?)g#77?TM}~85?z8d3Tg`%eh;!)fCySoPB9}meF5t zyk+*b(3_S}^8WzxTPPx4nhJmpI+3QG2#QW;Nb2xTX>4Mfl|eZpxf*Ll!tktOy~^yR z!nEA;>cps{dOjIaCBd`;28sd>w}3P#Ly0PtWZaFBFaaAkYO0#faJiWh9#vHW5Xl&NHXLeK4W$YY9N(U*3i@{nbdngQVfqmBu0h}1P}<~t{{+8 zUD8z@w&!tvnwE~0Zq!w*H%U&ElH;pZfhb6sQ9eLJ9Ppx&_BNeLwitJ1Jn4M4(`30# zl8=h8*Hn$!K4EaD#~LlGQnuNyBB62v!L4GxTcAxEhB+$j>FMjDce}^^nsMuovy$?? z`}Jw1q7GMaB+PfjUX$IYB=Wpe9j;?CKz zn|%${1u47aZB*q~#O#?*r1eUWmdyHuB1R+n7x5?;p_KcQcXt((mhNTbTYa=8*BiSp)wr}| zmy)VhRFkKWGJ#7$R&i{E?Z+PA%)Y%;Jm<>wJXJ=TTZK(6`KmNlH0?z?TNKo43QClf zx=M`5k(7p}1iE~poPiN-ZwtP2MHnX{dWqFLNmiy*C$ZL+w!(zUXCQRC`@02e5OG9_8nTJ0i_Dw%|(2jKUzIt- zBIT>q1;X!7PiMDT=~|+&eDQ^yc3bbR@v>Dy-T#-QfD=2YlGM}23iCFcI;Ypmx5XRLz zpq_M``ITZ%Qe619u~<#EoT&X+GZwNzaDpW`b?Hk#5G%vK6!$M7A|3A-UEAB9>br%tk8^`qOo zrSEz;SFqsYCpLvTE1IB@YA&TC)1Q#?hS5$g2uclOeye4*qv281f5i+g3TE7tti~SOfFZxC0Rv=# z;r$UEN`KUlhD`+s1bS(i=wu-j&7P=dDCtlD+CJe3r4B`0rl*+!0MEi2PKr*U2oXZk z0VS{}5{KpsM@0Kq>6=2_jo(A^PD;fsY`hN{V?*OWolO zM*y33tV()m;~YsUX1z!E7Rzc_2|GvXghtRLB{Y@p+$TPf$`|g4xk{5WwnX$0idMR* z8Rh_-lhPGE0Gd-SgI@@0ArN|v3Yi)c;|hkVMScK&K!LxB2SPpm!w8A0N_yCpC*qj% z`(u)%SFBVE3T@B{J|ssZ3OLEQ#)9h8s$l_ZSTYjUe#jBghbh^3V=AI&+(P{is_RS? z6q0v|Hp?huC^@`PzxxkbtXlE@Vj7IqMS`#BCYO=VWGa*f|-9q&m zDFpTQ>lX0b0v2}j5#XQe=1LU&2s)02UvF5Os?-?9IU&8w%+E#4j*{yPH$gF&mlTyz z%&#o`D^&b7F(1_{Sg~OxJg=yrc;hc|zG=;<^ z`bQE_HJ5^fa^R{bas&z|jQY+(DDxtLYdd)8vh$DuU9r+N8qEEMJo5MCE6}XmMHFen zZkF;_f$#(FoI~56GLtv*DSIzicI!0$@%^!Vc0M&zWOH3ioqGCi!W+b=9O7e!hzKjG{ zCdICn_mHO=SHyam!1tKR8g^3H%4oA= zH77Esd3eaUitJmZsZyv^sUZbZ9b+ew=CyX4Ep{=Dx*G16Egizvp`@kO6}cdE^N8T? z7jZ>NgaIlqxH{z|gs^n~0Ed)3=Sf3;h!SJG`+IkALnR3v2|wh9H#X%d!L+33dAA~c zK?zCRLEEe_=Hy6lYyznr{#VJ}Q6Dg*p9IW^hxK=qLvc~B-_99@dt2KNI*$?pj7l!1 zexWv7ctN5{H|(0R^;1dTSNf!?a5H6@#dks$ilvuqS$dciPqyk=R=Uqm9U@m*%}SKk zTtrO*kZ_(F$@A8iQAD!JK(Wsyw+vCmZuNY*kLqv3 z*(VbWe#XibbX7m1rqqO|VF&_Xfg|G)_2UCYFVINmELE`gF6K3nT`xAOLhsEjMZ~t9 z2vO_ODXyOzj*D4h^Al)PZ^}Ky-!n?IJ5faCDo{a0!5unvfhKEOixP9DQ#`A8uB8;~ zTG#1;@yL@eFBlU;vTHX>Hawa1)iGMa_P)Zm5i$wZDsuKhcuNlzw(YFMmXo#Z;HRw4 zf~P9RsPhYZ1@zK3l-&^8O4Nb2b0I!bj~KyTiwo0jA)qHpub}(e6#TY1w{Z?)2DDM0 zD`v=YdzCG(7qe7fE>$X%6twLv#i;b~8b+tMdydl|8IorJ7n$~_W?y9G426W1W^a9{ z_Zw};+q~U)!oHr|QM|0CYuoKeoW0THR(^b8Ww`0J>)T}i09|uJK5j~=vd`PkEZ&?~ z$m4G{FWqzoP15s8N|frqiew1R-TBiCfeaO@+v5KK)%UW7M}b9cI2(`o zGq+c&>Uh(*YVJRB;0U@?&{UYAt3>N@L+Q6)-IGbReN?wrJNhD5>u zQe1g$+PwVIy{WfRi1xo^JC$*zb!i%;I25UfJY+kPZ5jwn{Ts2ZbtS)YZB%7_yIIw` zfu^2XCn;)A-XeHAbGaw^iBbX*~`u?R{!8uBmdh zCC4$V{lqcSyKxqe`T8!-7?5N!u>JrYs_fS4{e&KNiui!a4{ngbMK}-Qy^W zq@(L20Xs&A^~8WA>aRfEuHZc4ev%icyjV_?SdQr?x%EPXvYn@S_`R@LjSl9ni}YzE zEy)qqW*pW~sFwz|geZXI!1kDZQXm&~qDb2*k<(enQAgB86?mouNd`KrxoS{foS`b* zmgVKt$1drh+`PDDv9e{?!aA#rX(9Xj%_%_cN{>${>Zb_25hm{y)k{)ZnIo(;8man7 z(M8Q;aGaIbsfj~sqhSFSc7o!dlkrHOi86uuMIa-BN_y2^sZA;yd^LpVxl7blX5C2g zs-~3cu91>~ra0%*WxMwg-_K)T3p&RoH9TxKjKpWY28$ zGLDHyDQOD&aff%d+f^G@yZs7X8$wIO0wy%_f||mp=C>8mC-TmH$JzBQQ}hisDqIAm zAS4~#d?Ecc5M!EE1IVb{Q?&w_wG@*i={+JBvqgIhpz5K@ymH{DipwTyCTWrVaYH6N zmsM#Z$*?wvKgezkfD1%Vr&x{lMXSEOg%)z#R^L=~_rtm>MqD(Y(@RnbI|-j&ztIda z6z;26e=AcdhDjf6GwIxytke>zs#X60Vt(jjt=tp3fdr~boye0!^$`anyc2|tT?!|G zC(}O7AplZ!!u%QIS!3``Of*p;G*z-(L9g)MvRz^~acGomtne;>94MJkq~_gi1nM2r-96 zUnk7JZ3){ba7kLBN{ocGoGB_W681A~UDoGL!)V_%3H|Y7j<@POD%`!-N+MQdO>1gXoCjXDf9{Ed*`bw|Iew zMHGouid*a44!D4o5=y`ap@H3M!i$e9BLa;xiv=B_ru`L6xzHG(%Ugzx7NZ4z`CY2( z4aMBJQhM@`*RP3*u{I>OtFsvp$5B2LhlKtiH4dNfF?sZBHzrV`we|!soj&+i+0Z42 zB1uf@n^FstPsB&R#sHGgg07*wpj@|Y2uL7Eh!MMSN~9CG%y|frv8c_!D@6NU zrwhey@=TB5yReINHJO0v% zTN}5xT}0m0f}oHNlj#OyEqU6jveyPTrz=)PAyNpC@CT$_Hy+}tB*+RURjR=Ai%Hw? zrbPQ9($5xZsPA&11v%iDbxqE_hFD;@0*2*Fp!UYT&c&=1E6?*8TAB_k?*9PjAN1#; z;|v;yyPik0Az4OgNwbLUl`P5IeXZTdcHt#{71a$J(qXYb-jAES-0x1TpGy(=M*jeU z_b(=U27~J58l9`)wwt@Ycc1#W7#yLjIelUDt$^bc4{ZtI7Oh1oS{t|FNHmr z=9CF>jn?{gASY~}YPdTzePxP@4!(#x<+kGm||&dT}ck?Do6 zh%qX^;kc^1fFg~Mi4}y9J$ix!!)Y<+S$mh|vI5n=+iBGqXvX4f4a@bF!$JE!IBuvf z(sO`Y)C9-f-24$`!5Gq0{S0F%d8AKW+%K>q;k3cmeCd(Fss^ES$|d-KIMw-l;t zO@S_w(-eS^%Rw_Um+ukO!SUhZv$GMmekjN;<-Wb@oq^GO&g3-ea`$$14HUMgm*}EE zNhHspk21ZpCPG)xILqMJz$pyTx!{EZ_kOsp`f{&6s3NPQe3_&a{e~LrQc&Mi6_V|q z{PmYRrlP5n)R&-^Dj8IPQcA#*Ng5q=jb~`Tl4>kZm@VeBC0)+vxTW4I%q@SH)zw__ zT(W_imN9p%r!=m(g`@{tQfDGsS(fDJK@siIJW+hrjh~*Xsys2F)U#VrY(-OMt*UnxKtro!DJW4QM2$o}jj9UvPFAZmi*eOg zFmm2OW8iEq=SPTK?hn0#<8|(uirNcK;pvLXjn4v5omR54JSJkwDB9iD2P>WtH`cdj zv+l$(>vj326e)5{)|~-&+@7q4?UulSxTUi0lOFk*K)F1_E!psj`!!f5!*i3Cjy$ba zznFPfiu-J~-7!3NrTeZ=b4r@Y`2o~~lHT1Z?b}kXg!EBQ-xU_3YnSuCLEx=j1XhEl z-F<5wxoerSTlcEX`3~CgRTV1JjBhQiz?qb~;uGiAYK;?HO^|Ggrlm$$<{Omq>oqlB zHA{{0)sCIV@(kwe;iR?OX_`*uP12v@x_TwFO+@*1iTS9ew=afn9WMQvlMTVEnQ*&<~5wE9bZ&Xl*PU?MkaRY}M*2r`oHkTlMa#LBZ8Q zXR4G>iGUwBHtSVqk846>SJvDvI~0N*Qp}iP8ta(kc-pPsXa33vVDK6O2S0SbNv`K?I`|GhzwOh8s;AZWy;Xt=yGLsrKVjgjYlVArTBaJwvnK%(O4W3_A!%(u zcpFL2&66(+j4|(+V)l{s@I?J=`qq!#~=QX)i?Zp**{o3JGUid0A zA+b=mHV^EkKzTuNz{U*|+mCaO-v0ni5)&D2Ic`!^%@em*n*@Zc`b|uEkupSZouL>o zQn;tpgP!s`Wpy9qG?bP!m8B|MH0e_0NC)C{D^uqS^#Gvm`(?4PxT|ZA+j6{*xQr75 z%PmRCo?YhF#m^0K1JbxqxauBtl2f#Vy4>clM25Z)>y24Zs%+y7)GOEAA7|aUa*qr1 zKRR~7i(L4>G41lR)8uX~da-v4MRfuPsO3cgac(KHhPNzjJ5*9qMC4;39pv%c21sfS z%Uf@C6p?erC`RMc_bf`eqhbh*n9M_@@#vi&d<+6ua4 zg{o@9Zz7+AIg;K~q&IT7;%a#yfDg-8zfzrg=%64e4H5AUaKC5ykmL-C<(556nrAL< zH|kTQoeNa$v$*^AP8vlKbxi~*At+E11w@hU5gM7SLj^XYw5nteIF0R6wn`%{Ew<)T zxlhNShQ2y%ksf7fT!B=&$onF8x`84RNr#PuL#nAw zajt2$+9AiDP$Sk3^duSdjMNxrzU%55@<)dSb8*}<$8YrRjC)i8#jSDYZmi-QlV)uh z$0ubJm6TcEA7mCqrrT(@KHyUhGULE{jy!}<)Il3{66+dAGni1SMqL&3q^r3TTBI2u7N(CI+lP2@SUmKyR3kTt)k@8s#}Yu$*5JX_g3uHxmt80U3_BL z#+v}kr`L0b zQw)2mkA^=|iS5@%sb5p6eO{@UQPyM=Gu99|gik~r)UAtTftLy@N6b^aB{-M?28#mdsZlhk=kYF0Hn8@i5cz zXjD{n6aI?dbh*;Go7|o?l-CCoubx?%dFjnV%Sb56L82B$lc@+V%w2s;a1fTs(LPd! z^~v2z#!w{-h2cmx`t}xEsbXoWJ_-rcbcE_DISmz*;=JiB78aK`QdE#CTIv#IIIp^3 za74o>X^<@26p6;8!`P`?)>c-h6n+sc4xL1L0bXkpYEgEmX_;2i03Uqf z9-=BRl-gCf8&and6ps&lLQx@_B>`PZ(_n!i;nE`yQ_&S(MMTQBmjX!ZGCyP_g*Y&j z0<#-!DHCxfXG1wkAuR<;#t?&7BiYan_mH1##6)2VQ0gQ)zE`ra;X$fwo}|n}ivpdE zap0QO+}mWQEB^o#k^QNUD1gQSHMUPY6Gh(OtZ7+Du1=jy;gkvg0H#WI2*tmjEj13v zK9@D{)Q<>+NGq~Qr+(2g)b#a zoTLb!mz+k|w4*LDk6@~|ma4>Dt?bqXdUwQ%d`mQ`$Er*AgewUkF68 zl2a7&g|SX0A25)q{t$d&;fijH{1k&?zEj@XNohf4U%N5VBTOwJGC3O5v@oYoi6jZC zP(0yY;^x+hH^T0Ps-T4`Kp7uzvLIbVE z5lQJe=wePvB#@#mE@aQcsfg1WXi-wF$JyZsiA3B^;AB0*IUy1nisF$C% z7n+JlS_n~V>QLgG8t65cj%&eH4`ZTo zu3VeKApz>T20cb~Qa~Dk*TMq0wg**rkIR;oPj2|N#?bln(m(|KI)Wt>GQwI`-VBQ* z8m8yZRwR((l#)HL0VP;8Fzkd)bA?HgiThz2AWtzjf;cMOB4o~f5dNX6iR{sJ>a2}) zlr*jd2I7{qj|1%g04#ATc#1P-ZC^aW^He2KB-N`zIHh40#*#*o6F$u%`pw!bh3M5v zwUrGeb1De~+XZyhdg7A3q%9}Z8uS`OM?$3OrN!JB2v5Wb@RU8~sw7!=HUTp~3F#51 z7L-+YWF#mF0y>D~i85t{{TM;z%$$im1P49f(p&Z&RSKyG#;T>goL!#2ahONc2Y}>4&sd7`5L19 zT0@rfw&eaFgn9WGg6A&jI4GdTa%)uHzS}D5ZK_=I-C>VyM+tS=@azlt@@-Y(!exkG7M80Xjg7F0-Qz%I=PGi7bZ=DWlAx!s< z+1s|`ADpE?txbjRo|2a6xPNcawN=n}BcCTNQ)hy>p23>r@7r&VCAO)57QRaM#5lY= zB$AHVO_t0^##O27&D=?aVV4S4ZI2+KQkVoxNchCrY(!Bw0kRtvn3`7Vr#?HzIN0Iz z^)*!#{{S#s)hBHA9zN*0vay4ws;a-Zo2x*p%kE(GoQ{^#ro|dpBC#oTpX!R3#3FTa zdai{_oRg5@3}c%p3nl%(45g_$PAxLB!q~nP9zH3InxxJV<{Wz!p{u93DJ4ZyZ99C7 zv_EVOg~-PnXi*tVbKGj0{KL%jnI-C)s)-5oDRpd*#qjjfDKptNxizot1q(U&^|(F*CntP@hmnq6KB4>vEluqGp)+hU;u_{{W?@ z8`N=SE=_#Ns%kD3w5WWm*9mDVT4yjP=NkuhylslLS!0}#Nkv+?FSgtshmx<@?e3wv zn|-n6B9+%2d1VD6Yd$_D7idqY|Z4Biu5TJcGqMQt-yo$fghqg zf4=;>W?GMHxnABOd)Jo!amj=Qk8e8<0<6y!chP|H22oR$V;oS+HPuVrNLo@6sWJe9 z2EI|`{n6xo=28I^lNtcMv$j9j?0tmJk~jb*Sls^r>pz?2cBictoF$8L9!}M6QXB(c z^JCAk{{ZS#5WkqoQnwR-x5NsvHA*g>{Bq@b>Kvj)H42_lJCfQ#6Z<36Y(LlQ2!3Z0 zvzagU(<3j)&=hxc?;D!#b4^B4<0&)IuoL=(Uq|G=ExWd>8*~26=H&d;Ov;|&`8u^F zH5pB3#w1i8BlQThweqtW8PS1f?tJ;pNxX$8y~=rwQUIvQYTflZ?sY~j+1NPd?7*}4 zUT)?dDb*<%d5zKXq|{y?a4V=&iZNvmYFW2tk8;jkd4rLE%^^Q(BJK5(B)WpHH5GXM z@N`%hTd)eAZtb{u)x`a%y04Vz1Ngw7C^PiNw_vLz%{+}sT>k(vGyBeK@BWA=S_CP{ zdeAOp=_n}#s(;fH{R$oT-n315C=!xRzQ`B!R7H%uX)b3`Dy zsao`xj5(2jQzV?Ag~w7%$%0H_1J2|sF3UvBr#`bVPe~3+Jd4x2XSP%J{{WH^tST35 zhZowP01v-dAT+47T?@@}z95+o6v)J#G$||8S6mja86O!!^w5raP-#~s4}=g;SqPo2 zQVA##gwtG|S5iRgdO;lmg6g4>X}CKwRDIoILc*KX1sQI`R&*&VC#WR;$l}7gs1f91 z)hbG6+^6F+3CL4=iAk(MQbTtWuf9Rx!xbVtJ~Gl*K)Tbq{N$3o{?OU}*_y zRnfc^eM&1EN%d9gN@iLeMurKB;xoZjK6{T9k^RQ@Ghf5S`-UfW zRmz33eM$vPx&nI2U1CWs!PFw{v%IE{RHof(Wo{~xrH31-K>Qt$x^m+64aqtSiM~%#*899w+_=7wE!Ytt-`H6ySat&)FUHfpNSmkL3pR z0l9f-Kc+kB0zrCHj?sp++o^E1GohS^SeiRC%~Tb|>2RVKGI3P4#nX@61O(4XN+rLm zV^QM=^%X6JRw`DqDKk}t3ATFgLM_mRKAI&1Qipfwl`QiP&Hm_-B}WJ3V!4qjWqP4+f1SS zA+|zVMuzHfMUltbml~A;t-|Vw24s?c_@|L+z@S4g+k_}}_C!gpqEc<5f$)PavoO36 za%-Z2Gs?Bx?~Ks0xug&RM~1SDt^Hpu7olLI>A+l6_lej=zRgUPwgSpQGxmttSV73e zhRa>^yp(piDQGHve7{sq;YC##B{1$)AwZ;$Il!V&uC3uAHC!8&G2_+*R)DX;0O)lm zq)zH2<}2`Q6%F5LhEu^KoU~K5`f>cLbxITQDJt9dNFAr`F7F zvmb%gl6wB}icH;!e3f3C+Lzq00Fkh|p+@Q{oh2zcPp*fgGY)xlD6H!Qji^)IzK))y zC{~Nz;~Sf3!?s>$b2FHAQdOfGOS?i_)c(IHzPGXFSxlBtwX1i2j$cp5d$Z@8LT|RU zKgB$J<2N2vttlau(C!Bf+qud?O3yUv{$>WLm@hM`#t_mp!E>X7m>d zv8P|d)Az6F5qY`eoM(i+TDIAqeE$$U9PdPu{P#zH{Xj#5zy7ph0+j|h*!FJH**~9WT ze`@*1!ELpj!1oVh!jVv1wE9pXWsP-c~(wCf_RCoZ5*#vyhgPySq5 zlvTs5xRq>jYn0`*{=pOegb@D#<}a!_jd1<;mg)Jmjkg}Fu!UAV>7cB(!y|ImK=2+W z3$ek7oE$e$W}7nPCKM_zmp4#C zi(aJ32_67+5ZT^bC>NRl=Rck5LcVgv4c9iT~F@tNEAn2b&d=HneQdAU|M3uA-vNDw7u1Au!cGmo@*(YK= zcbf8EK*lqQ*sAgjzOPp7bZxm%^T?c(;ZV^D6BAO1Nbw>PINVoUUPm5^>|>lQz}>pm zzf;^6VvbYfMAOdOz*xmb>Ix^wnnmvJf>AM{Q!H!XcS1IUbR%SZrj7LU^s1ckZs>Bp zMd1HQliK0Alm#yv!n&zb|_lcSP zFv3k#&0}~3Wu+*B3F+Yiqk>UsMJXv*+?N3g9y;ma3JnnVkTyumPZjlj+1L9Z?stp$ zbGV){!RT(i+wi@Kyl0m2-&HAn#kEeVCT_lplmg3*rfs{{N+~*~GuL;QHtr$%u$?~z zkCMhPDYLyj2dOA?ol1K!?K_*O?>{My^51c-eobM+(&KN|dKE!TR$LuGBqg?4aZ>Qz zFB1`2xz|&*#Apz2h(I|RyO+&LEt2iA%fo8g=WRm8< zuiXTR#Kth39zuWE6e}@nrFvE!xLfL*3ub*O)`B-{{iqgZ6*O`hq~FdGx$ke{G6hrD zPjo`2b7+)NQN0LQZ2Bic=A>@xDC#8-DNUfGCnis_dPh1sCpG>lN|nWpsZc5a>U_i{ z6ZwHku7*{%S}VvMAwf~dP!7|`Bqm!_Jp7KZNUDh)rD(Q6P|$(5+YSghjYaTpe>s{H;R079Lg^=%rx`QEz?y(io`M-E8Yqf z@g)8sGZNawzym;`JQi;5;l%1|L+SgJlSSV>zUjG13lz^;-&i9Gul!(l&6?C+ec{<&w zYad`Gw9pyt^fnCyrKG$d*0tfvu)V6MWtOFU{YRty#^O>3PQ4=CuBsvz&?MIqs!C)K z3J{rZ6Y-ScN;QO_Xco6u>gorNhyrjaM5WrQz2G*<{ulr(=w$=)nxdLCKnm)5bPrI8 zk8fWtqC1e4&6EUAw=jK{eo==fPNDopbnEA=bD|*<$?Bu4o?A|Wu!Ft!=QU5q#uk-R zu!5rdrP9@uHB)T4lrdegM&9Y3)hLg{G0?*~RH8FgRW5Xkx7{4KJFdklY5n z3y@cuVeS=G2Bf50vg_$?HB@&hlKpivic9XPTem3&QwPZSdm)5zQUO4wPBG-xt#t2U z`4Yb|d;b6kAP;Ov+Ktx%qFBN5L1(*sy+VprNj`uOc*&i8SHc?$)N6g|m9>Aw9msV3YA3^IW z{3{+QBt}QI^YUXaTQfErj}WyT{{Y$%W%164kBXWV@q5Zu87~!Pd0!pow)I+b3?{Yf zc1dYhR-a^o6q$j!4xveLwSMwc4~%`gkIBP(ZrP&wTq?gk@37NZ+&URFb4?TvapgA| zD<#Qo2685|bP{{W*oMYEk%<_~2J7I60;%oluq=Q0lhB2N`P9Ufnq)Ua6@|&+l8?b)S@? z(%#^>EFNCSM)h-Ysu6LtE#&BxBjWqI#IiV*MQrlNs+`{IUFTp|Maq5=8rK2L#boi< zzHW({sV-Url9S8{nfPl7RooIw%%@Te#Bw32p>(GBR<8r$_QU#UnyB93 z!D(mhoG`S2mAay&O%nD)RE*s{>`{{UnnlB@Ei*TnTqa#YjEX%hW2=>lpaNQq&X?l7k{i)^Q9|mXyo1y660a}_qqFds;|Qk{zh zMGEtZTZ<*Ou*=sq8n+-uE zgq2T2))$m6WT-)oI-*mwne>G%33|uD8DkeENl{Nv4zbis>y+%G$IBoqq~$#!142hn zqNu&f(khmT`Rgg{`?ZYrX|-D(-*rOGY^5Hhs-*aUbo(REuG#Pv>2_|}K{y-!hD4O6 zTBz&L^@_Yx=`N_7_SLF%eO|4}l_*Qh`T543)O3oKpCVqkp`Vfo+ost|!$B6r0)Wv# zw~8iJQnIoV*j9=2ikX@mRg4ZGXrDM9Cuh33I-~`E(U!4shsVVs_?2!-_C}2o#!ZJ~ z6nBa=u&@#olA%2ejG{b^j7*pTvRfO;Oh&3p%@K>!;Ao{~O^3=vqz8c0q*`SmhoPdb zu$ErAs%~*zzZ|K@-c3T#UQkx1Q<46N0}~uw5(+CQ`8t7C9!|7S)8wuTc?)f8{Au_3 zN1x^4XqeG$aZ@{|s*!NMPHPabuGaHjpmV03BqGksC(~yttG&jVWT;cW7k6QA#Txk< z`T)9A5=N8M^wM6~xW{)4Kx(M3dv)$LP2ObYn@=QGP*qvzg*8no!jj@Z8qdlS>nJ2Y z3Vo&}@eOueRPtVXcg2-$RMg3DU?D#i&?Ocxo@c~>5W^gmP>v~4msxMGRZ%$YJyV86 zq?wtRrnQc7BD}nk2SyRDMH#i;rIp+tb>QImlxO4T6XdNXeF|ilOl=O*7Z0z5$aO;QEzhE?8oqPuuP^ z$|xKDG1z1ZCqD_tUs?d&&=&9bs-!MEY-)Ff&nWsB$;Y; zi@R8@ggWl4?jJj((I;UStU8mC>FO)c%T$3xr78npQP6ewz&M+WW?cMRBQBwx(NRxb z!*WI!Q;gHwttOnKZr00tWDkz15{us8$Xb}b3M$-PgrZN0N4>T49xH(UVe`pK-+pD1 zWM!nJV1Cr@2yL1}k=d+Eor$FW=TS5E`9)WB=NWM@E!ZOP_}4`p z4;43YuY4IN9N{hFRCMjB2u8-8pTwOdq_v4`yMvZ0QA0iwud1y1Mssk^GRHF2l^cWZ zhi54r^O!$mqKfAE=1Ps#%vQ||p;m(L-hVw)VN}@Dml}pG6s(5OP=XH4yFeyTf9m%< zv{3$^Z_QO*Fr3epvfP-QezL7sYPAwWZYe66GuBaA4cziNh?|HaV|590mT`V$pi>X$ zm30ehrC-8Jt1bd$I!+ehvyYiAqar^lTH{@k^^2Ud$r$peBX z>Z8k3DW7yS!9-k7p&s4M9Cr-=@36s5Xei)=$+m} zq(AA(Ucapz6z?4Z58nifRNt#co1}e2f|8$UF+bALK>MU=G!CdAKR9>Oq&H%Vv1@<& zvj?Gxg`$Msilb<;RH{WN)6P*awOnq3I*fj@TXs*`4mR~ey6G84FzJ$1Bz$_r#@47q zcoF9DMMzFxasHSkwv`A|aC}nDE+~=#B4f@85GdrhDSeDipH0c1wjM3Q4c8?aYaF3L zkyMmUgT@fX5axF$9VRDEq>ZW2_4}Z|rxZfEQWBzZs&vXp=_w6iL>i>)6lJ>$dL*`x zJ;46BC9wjBl0uE5(8FpQP!b9Dm|jMvHbRfLZDbShiFg@9x&bJhp@j=z&uBlQJD?W{ z$});3!6iu(@P+9V-l{a3R7pEz2ab{5CEZjwq(Jcic*k_Y;VEB~(gSKiSSjj6N2*?; zuNlKTYg{{i5Oh=u_R(2FY7&MCOkNU5nNFXkC5kjX%jl)g%2u&7q^nIN3=&wY!^uUT zuv9wI$YnsMp`1OfqUB$6Y68VmhzUEBFmxtB$|~;lD&pHvn2l91os#hOK>YMkKD-k1 z7iQY&s(c4cV!}Cjf*IDvh~c`W)RVST>8R2>s-x*7s)s#YQlJU&m@wCxshY~Ivid~u z_9uDFm6~L<$f>Al(3(Qzq}mZ8NSr;fklhwDEjRUP!F>kzW8cRV_Nj%jL2mRaFKcF0 zcGgJVcBevQbQ)^{zA{Q4Hy3CZ&#rg%LGCA-E)|SomNP+Xc|^p zc=QVjc$^zL*xCX-OM`=hI%X>MdXz73I#zxyN-wd&uBBh6Qprx+*`H zwXI+(Lruq6&t`2nt-o*NL%tHIGE=rh4c&j?tY=oy8V>Cw5j6v;^Mz2j)qCrw%WV2< z3so8!Do*Lnam%WPOTo=#Q$D>6qF!~&twGe1JhX`tRT^oOtn1)LkoLB!CrFi=SZOi> zQi;=C`lzQjtW)hekSC##^NK8#=8EGN55=mfl@#iB?zEK5l}Pyb$~^thhfKXCvQiZJ zIL$}Op~NO&fj_n{hCK4D*~zz^6a0dyJsDJovK#acPyI=SEZS^EHBI`BNR34LLAI8J zJ5&|sNOI_`#vgZX*GuRyl9vdVNq9!G2}2u2fWL>G)j(a4?R86)D?5zlJY^BdDRS#^ z(+MrAFo_MbF&mjgOQ)(*Wq<~QX&yc?!4By)N!Plj?Uw3?)^?DSu=`9!bl?InA8uat z3E`IILVchTJt;mx38#E$o;;1It+0G*AWRGr05W zqr2r=K$=ywOvo6DXCtj5sZ0mfrwCC!I>l+-Xd`MVvPOMj&1#gP+H6dpD1S^LCV5KK z)DAqKRV=Ag;DPIel8O0FQit2f!uly6X>cRw3Bi? zHR65F?rG|(Un0-$-LXaJ3u3J{hsKo7QHtD zv|mxq$|>6qTXhI+bLpuae4}TNcQ^~qb8*m>aX60%yXIGGWs=KidiQ#`zQYzvJu-LH zR#ntG8EKZ%Wi>5o2_DlLtDB}w>+ zmwl}kD;_vFil1t$KT*!*5*%-bF>3N!WF_3K=GE1IiMBNA2USO-d28CmqpcubKjyD^ z86@bis<+x#3L_`{+n2M(4KE*IXm+jor)GxgUZ+Q>5DL14^;g{*&g0DnxI)jEm4w1# zTa?I%)-ix}RhJgH{MJcI z-1RG(eWVG>0D?|eE35b`8hc+~RYh}-s#5An_6RxTD(gu3TSX;sMiA#A@*R%Cq5c-J z*40|^6H9dMo$DvrAt*A|4rf<_q0e3z;-K8iT`k_6Povi13zQeLQ}__t0@n zAEbv8<9(<0%gO%$P~J?lxA~2>hN~a#;@0p1Z`8bzO6^Ntr|&-Y))1jNAuA(AMXz$m z`|GCKN408JCmnVy?VWJ%3J*_C3mUtq=6l`J?Qpo>?uxl(nJr88x9Y_#smCfR)TdUW zb*BUgG^ZowNi0Snre&Y8wX>Pl^WIVzKE?h`x9DjE5Kjd6z~RG}eTcD(hJQJ~C& zsP&?kcKUkOuPdPq^xP`u(*YFpt%0}GX|$BfO%MblhN-r#q+oiLh~VyiPmlfG&i%z+)J6cy!}FQ_W9ROFXoUg0%MZ{tzh zG60#>Y2gMn^azGpGK)cb2C$s_nrSg>i;8-aC$itDYDZc6w)LnVeA5@W=);Dq&pXC* z1A%Ize2I%zVtEyQKBkFwiRT#HQd79L;cQBMXDEB(HK1CXTlmGOdyfTU_%pNolPZ4M znDT=U;oN-b4lKIUh^(z`KM_kPP|{<>!PeGJ8kOf=(cSDVak$+d^Y>{vtsmtVnJ}2yW}o8*Jwuca+1sU$ah8 z%rBfLQ-N4d2=Uv?xZ-@-ZE4C^8Mdh;gitzE7%ILE@5 z=&si~D%*AH(^%!c>u0HAuFr93l(|h;Oz3%+kU{)NT0s#0YAG%u=4!XOig4S5xF2&A z_rqBBAg9@&W>x(Mm0s*Y2l zb0p||Av#IH#3eR@RC-8xLqj!OWgscri1YmSjBoV$M;+z&EF&OoCA8ixm5))pTRl*s z(E(^b)leb~(ZYmOEaprwn(tnZ-0|@l3Y(Hd^tz>h<*rYMIqcPpFc0h<6lLOumc7EH}XXw{vc6 z;8zI}08-M^e(Cy)Ob%luN&5A>IHgqfr_DEdbcS&wNU8OHu9>-e{xc0RdXWt7+iE2W zUd%b2O_aXfvY2WBUBNRX{{WhU5eSchaPFI9#Yq@PW<1MBLzkbGp{_&L^(7S4_2^o? zRJxU>VM$D<(s2y2hVG@0xuAE-Q#Rw-_dZz~U1d)nzf#gh^YXP6)h0(-Nei6)l8C;V zsObBe-P}_u;r`L^{6e>nZ&>wZqZqhc+Pc-Is@&^NBDID-ToCfx1Se*NAjrcFsYl0_ zf#XLey){}Sl%$6eoeY#Th`MDG%Z9t`De@29m*06Id z=1%-KWpuF4HLSGTY0&*csCBSp+DQo$9{45pz^WGHHC6+Rc+-yZtf3ZeSIu3(ab?of zsTnIwht`^~-|!NolrBjs>Sgy%DRQz;6KfdeTHGAaNNDp#Tk>lXtqxSvy83IGr**`l zsk87p+FVi);PC-KPDiBT$_F{j1>2(@=7-U{CQFr}hBl=oI)G)MP!sTmKXglE*$`FT z2uUR}^8~{HQyFNR)mN#lWh&FhroQMyQL;_bHsNqu)}RdBJtg4-x%EV}BGecrUy|Fd zS1S#z>w`1{eQJ=IlqAk(UO!}Ulmu*1I+akqM`XBDq0d#Nv$Iu+5TPTip~q0Cj%{^7 ze=~8-PaEWRE6zIdxmk5oi^g)W;S35E)i>I88@~}945Fk!;U@Ykc1Y626RN>cbfzd; zcQyyWuW5s5S2mmyMZS;n%9S+Zb8)3@STfVvA%VK72+>S7JDaq@3#VC2pa4}qn)pgB zp$d}rw9#48-5ZvxJ=T(;4mWgcCDxKo=H6>X0uHl1Vn+(9I#8v{gI}22sq2DDs;E+y z>y~vYM?^2o>7j;?g-V*st4lH{{obY@p$MwztT!5F?A+;F#jfswJuNjWKN1z-gFge| z5SYpt^lFi-Ess&UceZw!$ujbYpR(f8LTTJ7)WhhjV3hAa!~>?0BR4_FDiyBci}Ekn z!<0?cY|QbJDlycROXjIltBn5uoID^)xX38D*b6k^uICtMPTUx^GX#{Sqarp;C@Zq>ycf-7mPso^+zTyZsDpaMrvs0YbkuBhP82R70 zu*TYB;C5e5_MUyWkPi^CZ|jTMYx%|tN8Ps?+SB~m#ryzO!=wAe+~WOl`G`tuGv{s z3foa3awPTNK8Hy9ZZWF)&I!d5_ZZqOZE_|WoUNVs(r$ErpCNh$QT)PvP*0ZG&M3Rc zOmVVwzG@mR*Oyl|!!H<`oRtguDR7dww-Mu9ev1X8ptR#?qo+;0Ql+G@?J8MHTUOzs zflrB#&Zpi=Bt1&=)qa-TV>y~SROF`x@Zt4D;_eYgef<3Y01i`8vxnB#QPfqjZCVYr z7pjT|UAg?XrSJqAg)FEeK(eVv+dfdia_mz~)jk#QUk&-uBn-i+;v2Ls#db_9%Vt0V^lWkihvZ_Bva?`ffi5_;i~gYoxe@$9qzl0ZFd`0UBZ@_ zDy=C$5Kg530L2jGExDUie{AH;a&oM7xY4_9uX&XXi)lmvC+8T+zz0R_&^)=S==Qyp zE|Tj{LZ<>Gq{tqV5o3*kJLL*fry4O%l$((UPv9w8VwkDuN zrPM=FNwZwW@TAK{P7u{f9HX1fGEfN#*Uld21XE*8fT@s~N(YfRv?L3KWSUEhLHS(jkgM(t*9UWQyZqT#ZY0VKkMGg(Y&De1}gM zyoySNjK0S>YPig>iW{>pRomMKc-*$rEq$UJJ8Gb{aSl~OCc?@besudK_=lN_iLI8x z!uWx@g|2o7X;!a*$j?n;vRi5espKgSrUCoFjWwiWm1XXqN2wp=_K>2Fd>*SD!wS4G)#>8bp3JEDb08(VGJaR1cTB-LL6G5M-xucM7F{%LZ!5%fDY%&h_a_}%P|A}sWS~8J~eyDC>&FC%`b84 zed18-8@bx;0%g^#E*hd&`<_QzUX?sl9G}8Gr2}6{a`|;nw1yDGxm9eLA(eE?*URAfuWF$wcGhydNsQ(mwMQDy3xrrAZ#oI9z5rSs0&4p>(R+ulm3o@T&$C~3=Mp9S8}CwE!+^mQzV^tZRt4H zWII|Oja#8vn@s$|dKH}%d}DmBtgCLF!V=1gQ7vWyh>t#lx8!?@-^rehtr}{(UO#ig z?oIsZDnTQ3^y%+~`g04lTib`#CZ z=#3;b3qQNF>*8+cY?8y18;v6uHw%>&Bm|!w zU|gN7hCmMLJbl{>2@1$#{k-SbJb&hsb5m@F%ZY3qCAM<+McZ%^^s7`0vW*YfR)(-k z&C@=m#oew|GtRslTjE5Cd%fLbGi!vBTb^30iEo2p=AyEEtJ?$Eb|p!A{$_sY;0Yy# z?MRY`keA;Y+|OxrA?B*3u+Ir3r4W6w@I9wIzt#+fwQbYW0H)YUAesAq$gb_4-V+m4 zc6TOW0*O<(eig-d^Ch)XSnAaCRm2r;Ur&o!${XBxR$Isdn+2Fqz|sC*#Cf_qp*RTM$6$XuwWYnh%2 zigK&9_vag8)=3PFCE{hQ`o#_%(Kb>_vB`7vg%s1?-!@=5lQz44BpZ1Yjt2{<+bKx( zi93d9{{SkN49A_-Ex~!5Vh^^uY6q&ntXfKl1IWm9irWl@rpRldVyDGjKEIFZx?67^OE1^!=6OC; z_QM=#m8?-A%Afhxfh2rV1bI(bu59F#Tdl4^I@j;daoQAQnFMEx?E_-%+xsozdgqc|;6h$}|WKe9|Pxhr6*S7!iyW_uMs4LW{^ ze^o!}N!cH7jGS<8ak8R86CxPr)kxbj#XyJL?eqOGAk|1(M-;WioM^#u z-ABP7ohCp&_$~>OvIu- zlq%<);u!5c!PV6^hSq#cgv<+Y@J&j%wSW+)J9fszz9OPJ!-QZeE}=(JK-vI1r~RE_ z+^f2U3G(Td(lqj%brLq5P>5lmqynEHAxKT;A!~1Cxz+Op$Ep~zHsb1E6Q@c6Q7TaI zma%bd9a^v%$2EqkMdTjR`PZGYI?UrJw$;5uQg^V+g>y@0Qjk3aj7)9Xsx9&ynw4U^ zeeE6WTAiv&%QBs)Yx}@koTN^qkUB%fgGR+j{GoP~S}(-i+-5z5WGP)0rFWcW_z>M? zp+!xl3sR(&rb9#KAgTLSJJ_{U@%OyA%a4Y-tKFIRA;>-CU~dCz{J$T4nf$wH3Yk#T zQZvm}r2HJr5bk4};Fj5&E# zyd=z8x$b)+#yLNWUR49At8>sRK}3{7f@i7s!zOEGh^-bbM?*NO!5Wr6TCw_96(<@} zg8lQ11dXGpy>gtu72!%jnvCTQPoPB{+v*);48P&5B@$2sDolv@q&&nDBM5Y12`JDX zg!n>{RMcx(0Y1H9LXGJAL?U|h=wZx#6iAK2UL%cZQO|G3=*5ZcwMt>mk zO#;{9Ekj2!;Gg_;j-u0S_1{j%s&$$2lw0F1!y8pYo5&d?Av*50VJIVaK^8B1qGWu< zMOQkNJhG5yDtZk>K#QvPVt}87OBza}6paVpWu>cYYEpes(rMf$ z69$sf2I*|fVrG~8wH5X)Jjio?2XBdJYa>@)&I{=R;;ymR6o#pZRbX}YNo~2OuSuV>Be=&@D@%Lv zRJnw2V&EuU1qDh8O!Vqy1>%{?>MD-mEiC3=WgqVDb}#LeslJ2m95Lp+tFiogM4aw-%8fiQD@>kKnYvP4-XK-5aR~!ri^R zecOA%Us(Oib2Ybwc)t0RzUqH5^vgDCO~5J2hXj)$}@wfS+>D|_`E=B!_ZIM&JA2N(wJMRd)am6j9$Vu%T)r7CmY=^K?ZGClE#?28F5 zr#CY3*=@Vt?mKA3w0t*TEj+2n4AMJM>%Nirw#>(^PzC2OM0u4Y$@AAMUQwn_{j=I8 zWOepK4yy5yv;`Z~U2E!8r9S@vq?0gP22k@wCXP|QQ;{vTe3c^iyD2GKkBnAazm({$ zZSCq4X2+D?v3rv@dJ3OLv7n%5B_(KBkwSscf;1ur5)!47*`S2PrArNF-E#A2VE3rRb4TvW8}L_~+pR_clxD?8$G=?FZVk_7+0+{0DrE z)cuNA=lq8v`nml^EjA81TH529inj4?E<<3Yi)IgHaH^)_kb*)3;5hgrs8ea`A{=eA zaQMD=e{tHMY2o&MDziJ4Ia~f#{Eg6m^o`Hk!0{Za*8NZU;}xLHv3}Edxz??mFWQBn ziWe>P>Ih-$%T!28f}~`k!(;+X{xvJcEW0ZjBZ1d4kB&`##f|Pu$6Z{A8S*p1e8O@r zU0IprHw)cMlT*b(lo9|+Mv0Ibbbzm}OP*Jz;cSFafUO5)HwSDB?pG+$#ZYmT`iV2s zXXceO^xLLA(Hwgrvxnjb!_)UDVfCOVLpexJUI1wdQ{r_3unV;CU9+`HoZJ1XDSgJ= za+KQXIAOLx^l`K>wYhS2(ut6z08*f!0r(}mScsPwYBXGB(wuxg-TghAs$Qp^Tj^0* z+DVd*y<$!(cI>rN>2Rs{4~wUNrV_<@^~upYsg|8d=se>~i_v*En2$W7i+jgoU9@rO zUm%GY$i6Q#7^J za+HeCc~?=otu>{CB95m|Pfz%!#!+&;^QR$M_`8c~8pW_0dX=$+T5eUXQ&C$emf>3K zZdHpC^$LDB-7}fdAn7s1Nl%ta=n{=>;>pMBz3{&kRsR4ry?v}@n!aOgg(Bm0tx7!; zMQ%%bmYNkgKT2N{lbM(z3~1$TN~e{@?$dAE?DY0{t5CjA#(PxYYu^BKJ64{3sux~bJUu>t&x!?*bI~;J_sLZ~l?g#`WIuC53zDHE8ExaM23elNKp0kL;mEk(*UQion@T)`Y zJ_aU_WINqOYCCnzu{JAmo*?ClwNvnqa8zyM+5KF`+dXQXou>TE!h#;R)fB9(OZa75 zrT~*-%;tV$rqG(=PwQD&TxA z$hq}fRQT>QS6Rz%c;wZKl<&+Uj^ z8U^_&U&TLY@mh6y$e>DDDI1x5ASO+KXDLK3CxVfZJH>fji&|6pn}+12vr$Of^sS>s z1xr(*=`jP6;FYWdr3GVH`}YTET77I5Z7Hiq!X&2>LQm@u*S#McR87_6_KK=&LxD?6 zo7O5&03@ga01^|_`9lne1R||$`&UgdLI~_mkt3sYj%f}P24EELt}Zx zqOzLpR_Uf`*3jZfl*l`$rqUte;mW=Cy69-3yvMfAFUwU}o8KDSE0*Nan)+r%6+keTn4!dyAR%y0`_AIL58 zMK)`PwRF$!=&q%!EMC;(YAuvgXut_@k4)+|`OYaYGs`bAkWz<5WttGiM@ZBk;Gz$*FjqrcJ!{{XX>ND@i~ zcEeDkTa9Ya;?T3R(kE{U(qqK4^1ge!EDq~OO%%!N}PCCE1{K7yj( zZ9{8rr9oSkl4N$vPrf01Rn-|38;6g+A(v!qM39l}HA`^-PC#_?ls*X!6>QNfL&dIQY%_9mC%r#SQ&Uc_ z<;#0a`l))j{3#A8Lhgbj= zy2`W**Q~l23RVzwQ%55}QLSytq37PGaZ;xW{tzH|`=FRbbx_Ql_-02_jNWb6DmNtc z8-Z<-WDJg=`wv4B%rrxd3edb_^K7HN+cJ!G#GG?Erc1YLYE0asxuETI5Htx_Q4~*Q zMyhAH1ESEL-1$x1H!h%M$I5M&FcDbUKLU`W*UDlrec^>{_TSr3Pq;)BAm+9YZp=elC!jhz@f)7z2J`*G_@Ng^YBMeuT^g6Y{ zd`oxT*}I*l_Y0F?cFS_9_}?N^x1J}e+C3v^ecy_WDgX`CPAk$QuFZ${qwL=<`=z;C z0Oud-H%ERyYWOq1`%(B@gQKM*cCGNIhuQlmsO`eQmev;2dln`~*&E;*?GB-1W0}o$ zT6I#@r@9{E@SI89CmQ7Im9H6fvdZlVs8X9tUx#8QNkEb4BN2DyULxXttC%Vmu;aYv z&f42_TOp>(rCu*}>!@|=hg9W5$|`xq8I=-dNhUY;mNCT`V?_X18{Djya+xGGaZLxZ z3V>9S>0h)7K_U$lePWCM05Ch^v(wugVX9|1UZVWuiQO~ed||&%-Y9ZP=kv#m&#g51 zLUh`n)F_Qb-SrW*WbPe)*nrz9Bb!z24b|kl+LZb0@PnqX;>rY3XdzlYJi7~>3aQ;* z;6#ci)?zC!8ng3~$ttR^a5#u+x$4HyB%|<<1p3Bm?$iRk6Ss1l%%YGxUv<$`!zJZsk4x@#J+$yM*7{( z!${ax{m2BLvNdfr6<#}YqmpMV*p=zH<7t$H&Yos*$7N-Vnudi%Ja=4KtSvdI3Xv1= zoJ7;Avb<$mRc2EsY1>c-p!=cqRRHRdD$+L)I*%VXLKNZEP3n|rKod~>F~v&5s$gGP zPSqcHght1*j}+#|NS}_8CrxvkP?K(nn=_xBMp8uT6xE@tELI>DNhIncf?56`rWTq- zeYGJnGfa=!4^*a3QlUJGfpm3CDnSBd9}gIf;1aK3m=KT<{@)+A2Q5V-NN^|(e04C0 zYN*pYwhF38R)M(4%z4LZijHb3Uz1u?il;#mpY+9K(FJ{Naa7CsFwu}sy+QMVI!e{9 zqFJ_Gs%?*<)|?=MI%yJuLS&S9R!<1zmfT7ytCzUy3W+iTEE{qP&Rt^&H3|8US*=&* z4OKEy93o(iMx)secPoOfYs_v{vns^ZO1D~Zzd0&VQ#>nCWqCyfUaUJ8xhXOy!YOVc zp<3HZu;7_h6sxCPieyJa;9v?XM>rjlrZ@|?MiDLF5Yz36pcN`PbZXLU0*}(NQj&rO z(J?O>dt*&(C>B2Hz_~tL^oUZWBG{CNSUaXwe2H4YbG6d^&&{{$x}ZV{6V&^nM#g}V z=CljScLUG5<6=#PO(};d*|Ko4Fo~Z%AY~v%LW*VxoN_`JThItHl)Ph-YP(HBb(*K; z3ZS7%ZG^eB%zg1ILRu9{yQytW+Dg_(#W{^8b&U3WaZZln0XWX4-%S}*Fk=U=* z7)do@UXKgDAgX;M%5NibMpAM~_XU5JGVn8&Wu}zYHE6N_0ONXYJgw45+E24c-C48Y z)no3QK~9U6b*`G6rD#s+PhBF?X&Bb3<~Bzi6eW1ctc|{}g)#*6ma_N7EZGQJRo%6u zr4`3An^3G;*R&!>;gFZJSf3x1fH;)Qg_PW>GWV5HIdt5FB?$-NRDhHVdzO!@R85{b zK^lF%$TR}Vs$iWKfB;UWa{W;}{lLzbEfBF`-OpO1ST`)QWT2{|YMF1B6iP@M>V42Q zJ2qI#1ttC{5(z1VeB=wY*7PeFWpw_U=7#0dUkC!{BKJxN-4_*$M0=@adABS*h&p~j z{{YHRpN^oU$Rp2RWN77{{{W$=G+DMX+pkKBrdA1AkX-6rG=e~?ZY!vtSggc4XH{6` zB8{LUnKmhR$geHiUu*nBhDcPknfJtZ)-I9sU2k?GWkOl0?Z+v#T9sblK8h2)dPqf_ zU9}@yQ?d>H#Me94qB^W8&zLl&wR*}jLIEU`Idp|Mg5a#(n`~_6c7QFh`eJ=Ms&6?} zvzD{lYK<*AkoBJ2>tyM(cDae%?%jOZ=C_(IyKmFPXwmntq5fj#I$kTq zX|Nmxl)aJM5$G`6GAlt>!!x(aJq8OsbzeA+XBALxt>YcH;};%j<*UMLb6bcjprdhR zN7Y()1f(pVzENXwG;ONRSNsTBab{H0$^i_|asU$qdCoJK&?9LiXtp^U zj$#FMQLiIrcEseg6>Y0?RL%nnThh2+)qKcaQ$J2y1JO&NLqdEfC<8xraV(W^L}8+Z zjUW{5!QbV|>~#K3MN;*-3HY4aWK6$gSlNA1k3=2huotN9n{H2Ee=RL8BII2&kUt8i zuUN0>$oXl-Kb|(mr~IdjIa1q;H&=0W$7l;-TSAV|2=Wnmca5A!TB^%?_>7bZufp6F zkn%mv^|xGTX=#a9px-#KwZ<|tMM0OAN%k~VkDEJK$8Yzm zzC(`$+i7YPT}2WQ5|ol=WIP}n>}Ybg$6mRB0#ReZWMmY*#x zNd)wgeWEX6Xe_B!v&T)ASY9k`-t#Mq;zbB~T_xbCsrpd&RTA0DTSYb5ER^gr*eQ?z zd>{z5g4>jAt7f>aiQ0+^WD%#^FF2X3rvxWCN-Z%=nF-I%CglilN*ygR@RA1D?ohi_ zB2>Vb68_Im=!GFn2~9ixNl#={-aiS|BzgUi70t+LQbPf9QpGYh@2)8*)BrY(e{6IY ze92|Bpi)BHN&ym)@DX6rt2#!jfTaN-bdFM%g1inS9mQJtL^3#pf-o1Vd`m$%b6-`? zWp_aQN{%yfscaU~;*gaI6!;icnkKn{+oNrVpV!N$wi_IUHO%lwo3~Ur zppcEz>l4OmnC4L(O_Hr&_A>J8`yyH}-C%Zc$Os;$kb z!MP{B@ zqIiL&3Md#FdTHC@A{V@b@d!o*GdlkO3`j$H%@g8i0UB+V(vu{C*YrYjl`smd@-f?T zdVU1{s3olhRa}ZWEAc9auTwFifc<(zQl+-yr`40PT_xcWQ5al{D3Xs2eUa2nRI8qy zahibRN-6l2G=nMoVi{48NL4hYx7XFE3R-nORnP$hP@R@~Sz5tTQL4Kg@`NRIR=1mM ziS8|^54=M1kfLSF9kEH_wGgDgv9KGl7_vw z5wff4E9vRm-ICXL#%gkQlk$>5c}{H!9XwUg#o>g8)GsZ#y%R}CZB_MSQ9;{oC4o{q zadv@{)jrUbDYYp|2%k@~GOv>0%|N$c_uobswi0Ax88*2P_n+@5jWp-F74lklrh zHS&P4(h|;6ukz3`C-VhVIeOCmC7E1lYFaJ&irx>HQSg*yGSRv@bX&YN!(@i3_rlyA znez=$T_qc~?0%S})m@g_f=YV)GnC-mel7EBvZBp*3~&Q&cYYdlA7G*)|#LJif> z5EJSEqN0DMFaH2dgbEdt8T*GC@Kv*%`*N@1eYct?jeUxvP_TCv=g=8x6}PzlK~S{V zoUd0yx~&0V4HFe=_=VU~XZXF)v|UB{+N89ZD1xy$iS>rKfD}w&B{8md4^>69%#%H` zF;O(6=&hiNi3+ZP#ZbQDgpSf@Bc_oFjFys)w|Fj415-EE2~XpczSzqD0M1)EfB5Qh zqqf$nnY92`g$apu);7>YOI8ZvR`Vg#ZLJ1-NSA>G&KO`QUb|4Am)4U#svl%d$jM4& z0S?W+v?y&tfFFxZ58n(Cx~ZmSrP?}LrpYQol9fwcV3^}Vx^Uo~Y@qE0Q<&&{;o#`2 zh}9=vYn!*wy!y`4kaqOz5Y0GfMA=&xIYgMvsoawMYMdUUC{)Ub)2!etSh|j?(qGTf zhmRSkxU;s=HB|6XoQWr? zh$9@G_^OqAnG8iXHm#$9ir5pAMDK>&Br$D z_Ai-IqU&0Ot-DH$f)-|#A;J&BM0*w=%kgyxm4p&k%{|@c?3jzgZ!1V>;-%|XY`mGl z6pv8l_I;xXE0$+AZ8^?DhSF zT5;KbP(Ix_9|7%WgE;$yc=m`;VtjRNYL%ysuGKu0`jr(=sOqO*bxLt+CS;iSPcxI7 zA$VkVrNw>KgT=v$unca2p<-u#yxUjGe3YD8!1C)XwpUX}Yoc#*jYVSI^vOz7)Rkx> z=w8UfHp0`W{pnTm$A4$vk-h1&QB`>Gi%uo<{OwBNnPiQ#QOFmjQ&V$EAU#`lsZa1J z5Kg-q1OqOTCOr1(^Jj8=gX9Ho?O7LZ<2ldw92d)TNB{xkBi?N>?6-(U(Lfi9NsxWa#zz{GvR^E@RbQ%JcA}`3lu1fS&+@I zH)pb{*TvZSl+DWEjj9Sy&bs=WX=AUbg?{X39dG)ENCT{J@9psXLHiY4x2?0WacB1> zwf7ueMa8pZhjP zoKKjI*&K^)hsfPZ%Ec>eZ49U8et>#O(#cC>Y zp;Znk_uNq@s@9@RZL*U#IKvyD2p-q4E`K|{5~yir*Mzz)!q`=`}V>1qeY&~yZV57aZtrdl7%enTB>Xj6LI(0Ci+No%@i|IFawcGoZ#vHydDs8Qzv2~3RKT)JotdK{MoTEpK zYo5Xei^}}Nm_l0OG+Tqneg6RV?T>u^F^plzw%M(936ba_ z_Lz6D)yAb|pO-SWfKV!e*Er*R#myLEaqO<;X|rUv8g@S;sjqRG79Uci+4R=nZ7JQ? z!^$rlzyi*{sZ|#Dcgt5UD!*TNV*{tEzj1T5sJbz7^?h4a#`L7k=YVNM6gI6AlPzXI z(s0{$FdkwxQcEn-H;|u&Q@jI&Xmc!AD(*A5Jf-eD?duV$ELYx;f}74=dNW!qINKx- zQRVHE)dS$CPNeP!Xc;`O#v_kcAG_9lKaQslY&v|-(W8%#sDAVI4QfO?%KWpMcQsdz zW4P`VCr$%|4gw1a8K+*5xYXHi#M zAW~+A{L@R+eD4J4-vwgm*#?aje~*W#f5Y40D4p)(OJ{jk3%xM9KX5B>+v-a9R2NiK zT~fak_Z`H}XYGPzdw3&kZ4VU{Dfge;>!g69cBjAsXD_lVogk{%T5P{IKqhl3Jz=sd z7C9baDOof@4UhuQjHd=Ml|eo9Qk}CBxL%b_Wuvyaa9za#rBE`` z-*Lv=8-D@;I?g3+xm4G=NSHn)t~UPM`A4`6BZf1sO|_>1=1w%UJ;%JqcCC50Kc4vg zk?Yv%>KY%!71RKPg)ES-h@D8<`)eY{dXEL4$oHd69U)g!P<#&`9r*Fn>{4DO**6dI z$H-jj*g3sTHBu?-s_RsrO%6Cl#VMrdI)N6hj4wUA%G^jdQ~5=rn$MTw%~(yb%SuaD z%H|T33RNrxBXCF`2}u$$&v69;+PG?qWY-(z^4Eo2&`UQvGihNJEURO|O^p;*2v)m% z`pMHu{`jE3V_p z_CckteNNj-q)1ck^ase1_rqT9W~jO$q$EB0eK4{r`WOuBNUp)D;cl;)xjQ3&BxF^KRAK4+FJcAA8#{ECH# z)k>;Y3$+ZHp=#7jt4##NM(BY?M+5v49alMF^wl)Gb1D`?F9$cFX(SP(#DqX?LXojO zNjU!ib-ZeYU;2s{a5@ZZOK$&@wwj!jFfxE2psPeTwG7>R4Q85?w!e z{4s2xzO?<(>U)j$oG^;@Qq)aJ6Y-bAHHUo>O?1)|S}6}4?ze-lFItk%BGFUPS!e?k z)Ni}!O0`XdO!|`Y)WgM*;*sxqnk^a5J$pixV!u&dNvGP%De9VZDaU-Is1^3)8Yc5N z?w_=jiE&G2x}8P38dR4Ql*|RF{u%kh+S({N!88baor2R!$*QJjZCmVbLn@kf8GtA6 zCrFL*(H0^HM2U34ZMPO&$F4SZ@)ufQRAi}DGGAhyE7d5QgYgm89rbM?OmXtJP*o!2 z{>S*2B2~)$k6SA+O8V_-YH2_4&njA}Y&eo6JY^^{+0Cx$@0wVKiiGAoJ>Aa=<5{*l zh}~=QP7=JW4_#~)*PM2mqOb&nKJC1j`lDzYk`ILX33cUzPP|s12iz_oHOEDNyL99p z3QyD9o3TdmDtoUdJ!omZOyO$fGhxgxcZF1cgI%rHfOv$c0@~1JF|4(ZE|(j4FToxw z-wszA>{EdpHB8Pd(RSTv!A*$P5I{-%vbtu_P>puD0g#-;=K#v!R+|>e)mNHq;`3v;vwE{vs@-;_aW2zV zwh~-Q(yi0@fd||~1Y`?HB2L>ayDQ|UZ&w>Nso@|a_G_i7zW36c;A z)W`^g&erav5IF3aPr3raP^p39B0$Qe0*U#GHH+2KT*s*Z}L*pTPtRTTj*IF(OEf``U*)J`E1xI5aV4B{0VxY;W(ennGnAU8*R zZ$aQ3Nz+m6gX4Cp%Gtz?Q?nztCii8kV_+^V2hyIhP}D@GI)yw}Jf@1VTU}ixQ`D{k zg4&8!4w{267!?sQ<_cuSE;Q8DG!+#TqUgmttvLO;PA#;)+7=Y1%G>yo6a=O<6N1Be zl@!2*Xln2Ftg@!zkXUinaLO9j=ShdtzWrh-TL}o*mudi7#*>=7;FzK<6jig$s&oGU zC){!jb^FV7<>XnEgk0WARVEs7TU#qo=`*056B{U_YrZsMN?Y{d$GmKKzf2j1a_ii+;T|(3YX=0(k z!pSF~0dp}|lyjK_oZZ5(#`3;!*KAgf=Xj;lhq*fAhg?*nO-kc+80~b3O;rQG8%s&n za@8vGA}BC9IPw^0v0nY@zH7aKyHNSRW8A_uZ?;uUNX^1LcrNVVCrHMV{P@6h`7?HcCy;zx0oTt?JLL!>sRLi|>Q%jzp4GeJ_ zDdUwyb>uftq7TBNA+#!R;EPncmD8w9NC6~|eBo4V-A0wQ$)v0G2S9X&g<;d?YLu}& zm6rlpD<2f5l63nFNLsVkHpCQnF1EENh~;0Ne9cyA#+vC ze-NtWs<1ZvbM{O`e1SiZBKLF(x4t9Y8$A0h60Ym zF*2FVP76(ywF%y;{27ZI%S*{OAXHM2Oy&lV`XD(dN73g-~GON_6^>Q;KF~48+qdjcj>n%0O4T)SUHLMGLGSm2iME%A+ z3elXQVkek6$)%Ut7f{pA`h^N9Is$bPfv>IH;F8kh2WqP|(!}Z?aF>*4InFWn0DM4$ zzc|Cpwzio8%d%UHh#VZIr4(WNW9s z4!XidWuh7kq3NkL%qG&!i7qKXx)VA5k$rXBurl98oVUx$Wi>~*zR_c8>crMIg(#8| z6)h#K^@sNMG7(Va7IyMd2P)$)ps?HTl#bIO32W6#suGY4f=;jwMkr&r6xymw2_<|4 z6?kfSTZ$>TlBUY?**6#IeKepX6>cc^N1|Wm?2VTvsbp<27av#6T@_8?+pU(7HLWSM zE7f&MYI;c}N6*eNSQ#VKde)~l=jfdkxlOjun$TWr84IOlQz-yyKG-hiNZBn_ToArS z(yDpP`~C!&a!q(!uunBK|+6j*1m{LWHuVO^nJIpZ>>%<^k` zo5g8fOO&iF25%IrbhZ81vBTpz&oK+h{KoHA#KR13*NXa0%JSSxx9l05YRZI*)9j(E zeHst}D%-a}I!5u4@`^8>7!1y-P%XLp4;6zM8n2s<^Lx&|cMF|7)wZ?7<2L6M)eR)bLXo(Dt)R;o^ltkcruF4KJM`X!g02~ws(E?X42vDQ7t2l zap7(uK`00xfS(weJTyV6$13vm9Ah|5rOe`*kHbeU%m}?sGP!M7^~&jM?vw)!dNF$!tq% z_V|0^qqgCRj@4ggA0&;SPA=s0JUab&wwrw;Oo}2B>e3K507sPkp?i`@%f%Bgu}R3R zml$&X17+jClS_>UeqV0Shw*HePO)KUlYx-YSKe4k>1sAr0*61xT;w{skjh%5Tjfow zKswH1GlKRnsH(R%k{GD0erwzXsmy2c(xkqS*h)Gb0FC~YyjdVFNsN>v8R!|kt!EF7pe!0xSyMT zrAhuJO3IX@tmbFq74SVWcU4M^+#5&%v=iqoV&ATLRcYNCHSUw56bGEA3~ZF$Ka@Eg zYO&P~5}`j~)*c)RX=M_E6;k3*pG8M*r|pjBNkls|)yq*(Y?Sx{3@t)M337?4Z_z`j zDUmZBKSX7+ui&liN_ud$zN)AvU@Dxa24clR9{8&`?lD-Y6{!7!FT`$CrQ)?gClzs1 zQBgAeFz!N=s1c-Mjw`9__)JG6N#LW6;#*I|G(AS95v`)+rrsw=(vnt*6VSugBT2Ga zP)_gyf$ByjUoe zCmljmIt?O9g&$0zY4STQN&W~)+xVn3j!$(+O3ma{&5z+zDSI?lktvNvk#alIEA7s* zibVyu42c~*(9S}Tg;v#o-D)nzte}(y1qlP^?TYhhs*pQHY;bdQv|F2PEhs8LD@l7I z)(6la-5#ksDCAq!k?p0Krhf=M44{WfO7Eu%RN{QvuO{4T#@T)ubezXnjJTA&FcpMK zk<+KRLJ(I17otXUS4eeJSM9)PW%kD|T<=JcA7+q?m%ETUs%OXuibcpzRVxxsl8B0x zuM}MRvP!-wf(-uX7`r5pgFQJS~v%{dFB>RlQHrlAlpQ0PWOu zF?|iWVXE_syU{BsH&+J^{~eE$GxcX|uI78GNkQLSSMVErt%;8tYTl zKN9(;??TZ>jq?skbK*;ztz$Ix&VEj&R;0O4z3*n^j%j z#e1>LvPxQPa`{l>m9%6dbwS@Wi6DL>;~Bf|*lBk&R`xFOEN3g#_U@k@Zz$t`UfTMO zTW~^+D{T^x0>sYkzoIU%4CuR6=Y7?5+1sMP%$u-#-b+zid%RMvrmnF?Z_MvA>(i`j zZe?SF#97ZI4yaH&-J@T4=HpRUM{04Flnly{Wg)LnB>}?5H0X-C$;lx2l~B)kI?II<^;T> z2QB>0mwqeI#wsBW+!mo)-Mw<>2fssF+NqzT?pxcY%J2SL|J+&B7=tQ#S-6@qxS4Op^O{hv>XbAI+q^&G8UX2XRm9+^4>XfvJofKgn`YD?oQ(Cs- zrxc{5l6n&YO!}fAj)=Vt65Mn;;M@;QI(Wc>EmZDx63##yrnFRTmhFU3&SD*K-c}VQ zlj|a?bP6ut$?82pFZH7k$NvCbZ?1>^y0{j4R2m9$IuwsTDD=xg%*EvQQjqeL7fO*X zkU@zG>4{th6Fqu;u^x%M$sVF@Hlq&kTaYK6Ai6t$Tluut6( zOlX|lbWnyzbb9@9MY@t@u&Het&sjwbj&zP%yI30&If|I^TN8FGb7+tcx}D21=z7MA z&fBKdmdaik9Fy@FGb%Ir?f5HR>gCuZi3xgUZ)b$kmACS3W5yKf_^P}$~3**-kEsp1d#kr z_EmW13(D*RGKM)m7SloL_9}bz1>znr;~ww1ua5YNwYvNV6u7dCeQiF9rW#B1ZB#i< zhmcei=&fZlocs>4dxFSMd1D-t3P?u06tUVe(&k8`HDEe>6=BEl{{WeNmJ&Y_wE|xV zma~l|yEIjqDUP&XV1Gbv%jq%uW<%YsS!tDJN-A;gB@UxhQwIRK*Q0P6y5a)}SCSPi z6(1}4>D~QFMWYYGH9xt1HU7W$aTU9eYq0il_x|8k-gk4$zwFPBYgwUuHG98>Q&12< zDGE=kuX$llC&wccUwF}$kk$IN2EM za2}+cV}3VaL3EWFp^NoYkWUO#d&r{;LCB!G*){%$m%=f@4FwvxRJ%4E~2Zm@waYvR%U6f_>GBQ zCYr|5ttRM8)vHkaNtFKp3`J=hY%HAje8ducz8;>nUzEG+y{#Gw(0e$M?j(4C6}0-j zb8bG~XM*;v#w!%uU)vR~p2uvXrZR2Y(WXmP3s6pkve-%|{bIOhX;_%;-EjW^o4{;A^4=iaI$<^y7EUDLZv8dW9h}=w~=^kfzNNGlI#c;wwOSP|!RojSkOz)yFO_RrTGOh&z;?UmA?_4!rv4t! zeKt04A#00%q%>Ilk1f47!Q97yQ7(&z@alHb-yMEb9ScpphPMS$)vRb;>fwC>u9g++!IStgf!SiUBuPf?b!$_b?bn%5SBhg}D%LN{sRNP{Jh!Ap-|uBm~7U{{Vp zy;arZSC7i^yCRY@+|s1)r=V>r+q6&dI%E=thP^bD1o1+zOn~fB%-WK}kyX84gV>wK z?AA6Ird_Sa*1DR%NNhE#4wEIj)o4kOJt8w0M_S$T@3M?dAqpG8pNE9vH%C=x*9TCK zFu)pXyZ1(ry&~e4c|%22li|+ZvrbRCTb3!8WyUHe^BF7bElp|DVst9kNr<$zv#D5| zzjQ|%Xf-QncteS?J|o9}m+UmGyO^?mX^f@D!rrV2_=9_tCIWmCBF(V?yw>98ONk4d ztRcIu;;K94V%%LF(U&MMo5>5Ncxu|U{n^ZYq7m(&N%El)O;K~Ey;Grr^VeEdnYsjz z?yBhYLx6dyGJ%--8l-b*m8;llmYCl%3c7n`EmaT7Tq`OThFV%!Op>MVAWRe19QvJ7 zauo^PyNKd@TW#4a01CaMe_h3DC$Dbv|%Rgatjk zG}HvrXD_3qc}%&8A;(0A@q*H%&rnKCk4RxE!Wht1xs5AexN*u9?IUO?CSQ4kL~=L5 zI=K5kd3RMiQDNoynabfNzg*D}_T}lhoB% zTu1?hjhrP~*JI$F1@OHA2}^Zg#Te1C7(PK_9_TC_0IdPgrB@!-kqJ z{WgN3Xs-!(L&Y5JOLNBXZb+iXGK%7duUJ*>KbKtUz7U^HlTcEZ0O>Rn6cX7u6*D7{ zdew`(IdQw!+HLvWk(zpJdYyE(`cx#eJq3X!%`*x*b%ATB z?;`PTKvHWhW8i%$p6MiXQIuQTWIvf#KNt_oqM=>Da6OMZ$rZ>p{C!u>Oyl}u#ylCO zTXzOhL7E6o*~lFU5umn}oYhs=4w0>;YNM_*zTJ~%rf()f+PSB2T3#eRl_c#@SUQ3u zMAx*e!=pUPPF_G4{3hH=Wj`Hbi>g#SnxtyVfVRqt_YpI&c!)rqV-{j#m9iWavJK}twa z*QkqVfFTbnwN>Ykt#nqK-_Fw9T65 z&)WrBYO1eVLb4b>MU&ep?%3_3MEOCv-IT4q=yL;1avc; zbJ`x-a8>Se!31u%e~GK{JFU9$3ZU5fg`UUf`WQn~3>(ynPz6oqVBNIoEm7}WP> zF_$*{!-!c<%*#H`>y+>-XKa@`d$k=keJaA*t4L831Zp)A^Ihe+jpTS0_0~FeTt!{M zUn<{I4%ge@?tZ%7Y!o7sk9mmIUh&PTA(~s6a4O?SBsQi_EIfhavIpUhsl2N0kF2rj zJp6I~&I{rnIQvWV%!!qw;FI?He)W&po<8C|MI}Dbr%3iVK5w*-mR2l2um6TQ;19y2hs7)p#9$6rzUWE>~!`OqZ(~)P4)TrsrU4nM?H)rqVj=(gt%@ zc45e9s_%FBI%+7Z-wc96Nh+6zSPt!+%qsV_uQ?{7#`^YtR;l*NcI;6dJdC3oaN0VH z+^rlO+W4!}+GcU`w+d=CJGD)y04LxKUgD>#Xs~_LY#Qo?w|3W2eZ|tC0?(%}KBL(d z&uvx_b?NDE5lPun;Yg{!ld;ZnyQ-BA4_ER!_=BqtmYw;QMJ#Q-5n z9W^2pAdBcz4zW_-Gd;dOaH>?xyF4~H2^0O_baIuX1rgp1m+B2ciCo&EnChfQUkXgO z0uNINV+r+8{#o-4Eo(wa0&@Zy_A4uCg(y-+H<8t|QrZ-iB`F{RLGbrPBB_@}jtb0L z?;n`k(1%CiNe4qSF$M)d@&mL{)=go1a-C(R5~KKJb%=~e%WJR#kN!gDm4y7Gg!zOk z$6p9eiYN65{8F_guJrPKBwL)Wp+zH)IYWq z!&E5G#?ijWy*F&xO(zeRKuteM#K0vqYVu zePGC}7#4E>0KFd}_;yhbDe*OL9Non#(zdC|J$h-RbkH;{E*A7m-&$x}Y*f{)g^HA? zPmX6@J?1%-D9eYsiR7$LAEB_C$Li3k)&3udcn>MTKTg$F+lfq+qGUHmZkch%0dJ(d zMEp*%1DeoPpkw$2%g@_bPKgUh@t5?(XO$QuaYbqG^hr{r#^5?g58Dw*APQF5lD(+p zdJ9-m)k+Fc_=%9v2o_7g0-8K|B{P@Y<|9J)r0(6o%R?bQx&%3PRU9yK5TrXFaoDxN zK(G-YH9r3Ux+aGz!h(|tAy7YX+@EjDEy$pxk^Z!#G}b9{_YII}uRDIx(Qc?7P_(gY zs31up2mG;&xdE(O%SkHgD$R*geC|@Bbx!bQ*TyMv!&$6+QCZ)$fld9jcINLW=C;aO^dHLDbqcIe@lNiV&MdJVyxM|wSf2dv2iw~a)2Uxu z+&_flJUzy2H;&RYE9sa(q@_Se)F1$pBjYV&(yeVeY|(sck-d`Ya_4xcTFu9I?58!a z;wH%CZH?e@*{Knc`nXAXuJ7u2mJBsG9EYO!PLXU)8?%$ZF$5^Wc8Qsfg+5*;Jo~<`F6SL6N5Z~~^0OCVY~H-auHY794cgX+pIQTp zSd@t%cz(F^3vSZ64+YEIFFVHDkb&T~AGlsNZpYg06F~F8+;Pe(bXHRd87(Q9>Ftj} z$-o%^9;N13dED!hU5d!}`L{5jqh37DQCf>@azLXME(XFuIsrJv{1OR;SC&th>0)OIO5aKQpXVOOkVqRm(B$YiKkHd-=Mt+C>LlQg^4oe?&(6`5Y9C zvSMV@-0SJ?E3aW{OW(Mpj|hXWBTYgm+OUcxyQWK&UD_0{sppV-C*mX7)(Dp3X$1{b zFk3{#gF!=*QWB~3VBeu+C>=wkj8sL)i%9ucu_11B5VF{1n zRd-W$4ON<{d)4@*EygU+q@>bL>COeJDZS408Je2w+&uhoKY3F6-`D$oS5iFy<%(~zccEKzn77!#P@rV zqKUg+8%VvY0Xx9gNdXqwd8m0pqO?5U60g{PBfW+yAL>9LeUTJV&{ah|+NoOKiPJiy zD35gDp&Ag#oC?P%DaPkO2_L;FLKxJ{lSDN&EsY{&Ng#C8L%l}kYx?2JXlURsK$ZwNV4Wo#y{ zoS>h=B|SicF;Nzv@0p^om%P!teJOoN^iYBL)zFTyc`Lvgs&+zLbWwdDG+C%68F!r{o&23XRGrb#4 zwdc@CC`lj>IELi%wxLM`RXVCikaJb-y4r+OtN@ird#4$hb59PUxQ7bEGgg8wg0GQ)tlG`93$r;B^r@^+ zZ+?U*%dDd}b323R3OY77m0S)crEa#=!rxJGZXqR8)Xqa#(ApZcSCSOmn{6X%5^Q@2 zgHWkWyLB$qg4oyeP8UVURNAQ!simX|bTSbaAY7FiWh^(bAPokB6|JI5s{a7W!6jg% z^y{Q^-9+k?`rsoql%Em$j5ajVDy{$_C`q2AXd+suoY1`s26K=n!WBljE~auOa)eyG z4kCSJKBhTx4Ac!|p&=2y)J_F>s+PdijXLWM<s(P#9@)f!;Z(@dO5=;~EYsZg>2 zyT4sWc6ElTNmIcqDGS_Z@d2TN+QtqD2Es=aO8oP8>uTMrc)sfOB~m75&+mtZRTUG1 zH7M5sIbCDVXdAJzwK%7f>#49#&iO?a)SD;ID8P zwp`o>l|J_`*{>VhFeIQcxmL%!doCMDxt1H`{kCy! zL-o!ET3d?Tkl>e(xW}nSF>`fZJn$Ojl^w?RS<IRn%FQ0VlKS-4C1$+$#c-e(sd%Rb)G3SdUV~%NyDy1%>H%5~1}r_g16hJxD&-qYbEAHok}k?F1-)2y5?%-ELI{lwx z+l|sR6bRa#3HL{Q znU%$SAv#`b|S>mmDKR7-bU40e@s9)yVLq;(SYXoqJS zrjn-4fa|0b0!K?!WxQsOsb*dh);frOscJfmVwGD!Y;F5tKuj|dlu?M%6$!T>M~o8; zK%r%v0+lFm`Y_@qz#@Jka+Dtk0HEf$(P_T~V@)fHE{SQd5$PzcQk><`YwwRv?fJi^ zyeGRN54x(4KPmqJ60|GhlD-ixf-Z}RG%5Ct3}qFv?PIsRlb<nV7}H*|LcG1e}X+yW;Bgp(zZY%Jn_<^2=*;{wGvSBx

#_mSSPUAvm$Tl* za(+;+LR-tS+E;5R(&5_Ts%wE}k-3>20)nI?0eoGR%!vO0D@xkH#^Nxg-PlR9C1$nO;%aa8E-3sb ze)HyM5dyan$IBQ<)z5{gL>VXD*Vz%uXc2LZR6Uej4NXFA>??9of5c19eJ2%HE~%oo zw`U;LAcVJ0)}v?<0-evQZP(NZfSpu*u%&go2~CQp$yU+vD1I+w4EI%7$MCN#)L%b& zp+XW&-9Re$kRcd8B^~^{v6Z`Ae|Ar9J(lwqGPhCke1p$B_@jfAo$0A!nyIGh@@tbt zKZG|-kiu8}b1xV%@pWZuI1Nwk9-b@9q;VzA@zePJ{{XR1pGzBUr-JZ);5*dGZOAm- zHEVAh;)Ygwx(H zVIUpX*N+?VpKg`iuD62n_FcsMg@o4F{RCE8nYGeWB7@~ZNp;s>SyPBQmlLqhz!>uD z+?~{!3bC#L8gcu$FG7P8VQ+^d$;n1cS)dXO0f3acZ2&ZLTYmgnhsm;$tvAz z$0p5gE5|Qw3rm&31xc>DrgKAc^w}~bPgP>F*~g!Sx&`R{wc2KPhfDte598=OOifdi zRB|3ca?5gTjqHyi$lfpBZ*@+W>S|sx6L6E#S^zWv0006qQe2mAy|&9298926rZbG( z5bG{m8dHspqy(sm>o7H4(C>FzTP7RoMQ^uTu!c5+r<4)kS%L?rnk8|{t zJL?l=rgaawW4P2Hq2w`A5?d}bpt(<7(lK2oSLxi>9a=XEhWoV*!(7<@7al&J!`e4k zm&~1^T)PX*E>@o6@TKH=LsT^@y5yK^!`Dten^)MVvbe`0x!7v?XOu2a+N^hajkdSkNTsX2KO$Ui zfwQ!fHafVGu{{BTBb+(PS)=7>QYLZAckX?rVxr|uZ^vpddwneprw_GT+P72OZMNop zBv#b=GE3FTPxr2oH4R}+up1slXo3ufvO*+T z$w?-sRQ;xM(1%)~rJ{XTN>zucE-E2Rq|RRij`)-7zhWI2{f-3t!3 zrBu@>1cf+0A+f%tT{=SZa|BCURb4Kf&Sh8dyJr{U^!W7%AU&7eRp_Ni=vI^-q;(VU zgmfUO85*it=21;_xH{fLkXG{b!IQDIt>;>^wDDizLrstrCJcvh)TJv?NKo}#4lHH#;tR5)V+nTFO#C@XX{C{%0+^#t-lGbik%9;|w zYZUY zUF@?b2vX^@D-~jgMull@N5l7kiAGfTCLBW>`I0-`nDWyNsZ=78;)G2>6xRk6xk`Ek ztuY|TH8kVLiYT7mdEU&xs&gB=H%!+@QiAe6A8RXIK(^3wYT#wQ6`<@-7yDh0@jbh zrAmE45gX@WR&hrsE~>)tr+pl?h9aLDtFB)+aqW#wGWDLdZYt?ROFfcevC+Sv_(Oz) zDNY2E)KLj>Ap>QtoN3%Y5$1OG>#+QRRCmgNJ$wS>v@><2a3*t7W_Jm6DI^#N=-h#1 zrJ|{~l3gilF}&jOX03A3lU^S~w_A-uy#Ti9$dGTuC(MHuHsX!}S5658tbWS#OC}-7 z7v|m0C^EV`uG*AcXkAjQtk> zK=e=8Vn<)b6p{AKl~LL5R;f2v6v|ZP3RDANlc=1DoC55Xc2@rY zT2WmxKy^ngRsgGoE7vM>Rc)tn3#+BxqqKanzeTY{KAm62GXPOB2t=9NkJh)DM>Y}LfbAflD9~jfSKu}az;9e zHqJ5J=9e0>lZ5VY8>zqDT}eP$UKUt{j#;Ssmk~1 zg;G+DiKZ&qxk?fQ5#`n!Q&cPIRoJI-)4Vk)>Qku#C2MNAgcM$`i+U7GdrZDD;9>4W zM?w-6hNtqo>kCxU&{zvkm^uRj%*q6N9aVLB=RCS#x4Jv!xFV{V&=YdDIyci+Q>c7R zxStSbq3R3)JQZ$=R>{g$;Hbp$zD2@SpYiDmqHWE&1~RQ`t*$Fsl=!xM6e6BE-y8W{ zq`0(=?dD>C_HoJ&aBn)}Z(QFjV>v^D(~-_$DQ2x;H0kjE8=Fl0rxd;Ciu$V}T0SAD zNA^xv=b9dskzADoz67+Ai3|HoQFc~S>a2_} z`I?OHn;~g_C1Choum1pVxymk<)~$~^0a{XN=6pcZ2=iX@!t~x;7qj_qp6bRJQa61a zy|vtbZuw?2ZK6#^ag3o?o<4Ywh+OEKQiC(p?WjRhO8)>m*%W<|+%Lyu8vg*wbUwv- z=5J~(x#qg;TjhZ_2 zdWa+9l*gHa*j9ex7c{S2qh^`S&1gXD)1*#R?5S#UesfawC}^*8Q7sHa04f(mzddIA0;AY}7YSqL3$` zGtvfZ>uOr5YFb-F5;mwv>Fn;5Bew!LL7r4)GUD5{aD zyE*!D0wl+n72bFDJ9 zB`R0pmgP&vCC6L6rB(MHQr+?BSJ1Z#TduX12I;DisYgN%nhhgO$wlMyE31c9PS-1)BG@xR4`&?7|nXSO48Pxy*rxpmOQ21cm=09*;t zZ0#1DEd{G++&Z1TVWMUzhG3O2vXJe)k60E$-l0JAB_mB>bDL<{F}UfZJE<_oL_T(sp(xiW)B3@_srgXwdx!LXY&8e4Z(i=Sv(K#5N zD~B~g=!Fz!cw-l>xP0oZtts8p-?C&6x)s+nLk1N4x><2dR>4plr)>%J$SL?ny&++d z&QeVvXwiK6_tBPP)mP=)?RyPES2-k&06xfd!O?n0V#fX}JJ_nJY$wumfTOBreEpCI zP_IiY1s+{}W8nyTpr{FaI`~7y!;`+1tJPGfNjs)K=oQxvDp=zI6LHn3kV%(_oN#qi zsX6+VX+bV5f#98CTy#@K;H$4GXOy&*uTs;v3+n`YCsKU0jOEqHc7b2n*`3u!Wi>Zi z#lE!}6%(}1LS`ysSB(G)6)M_R{S$5aZWNH64E>Qf*$X2_1SQ7c?Lpk)=6qCRlr0>k zL%ff7O%0{=?)cUSmw^T-GV=h{p~S^kD9eiI%a<>hQY5tycYUx15-*CX!!IV3@K&O# zsV&kaD*RHE{G)I_P_gHA6=b4@rGgZY;gnWi260xFjVcDZ(b1*H0ui+->93iJ8KR9} zTFUAth6w!~BlB?y0FWkiF>8i&F}Cw7XZ_FQ9b=Q^1W2++zSbG{W%2y=H& z8(r3!t0HNPma{!2;V9KvHj&X}uiE1+QR@obY1I~*)ay9Y<6$RYsWLLRiBGzFCC=kh zOy{Cc#GPg4V?!1>@;GR+7g?C2X%$meLx$WJj#CF)P$dI#J^+twTjX&>$4V@|FF4UH zYN85ULB<~1Hzb+MuYvYPG~&5d-Iz} z;Rhd-)(+J&Zog2j;%+qSrFQtzTW}PRl#l>dkbx%eJBnzoEc~;_##9}2?l+Pvsufc0 z!M|1ATv$NVoqS@de&x7w)7y5nv`wH19COztQof3&uR9ctbO3bL0++ZZJENO|hrvil z?gux`Z3?sG*QHcZxHhdSIswulIVVUc;-?svMrzTjgzh|Zk5<(`RNZo04jtP`oaf~g z&$4TcRj|L2qJb#euuM9bp-&<7nEQWfckWSK|KHyhjk(fBLv5a5B;WM^veGL3u)Yc zbK!`M^kZPA7IJv5$1KxK`W8vcQ6&EWK!zLk@CsJh5ycLUSGlS}mdqL_qKZV}rtFm8 zryL3aS>9anDr$fNf3g6E{Y$(O2m%G{&p&O2RA|y06WRyc;SkGpAPMZZ6;F-`<(!u# z%<1iwt{+X60O*csN8jZcJpVPT&+EGsarCsG`8N6+GqD9jS)mFd{mQZ;b9II}d(rmphiU^qgR zIcuc<0HQVhyALc}%Cj?d@ljW_dJ;OGu%*pQ&?1Wwc;JRW-41 zVuKu|akiwgfC-QjjT@R80-z(}DN`MAr64cyPgv-}Q=(#W?dAJziTCLpExRpcroB8M z9HgAAPqJlQMOScq-t}?3G{g04DJM-y&>2Q@#^ug_s^;R)@SzKpjfe`s2=~ z`If3JmDcT@L^V#K{{Rg{Y;e03Q<2?U+~wq(Ba-X1{-YWvI*Ni#mRjcS%OuiATm{zI%2lyiD z%ff`(VM|wt+Nkbeq!yA9vIyiiYKCR(VG`rt!$D2R^)@{Ruhvi@AI-jp=eDtvb@r2fc= zNPGx@ zPJL*Th=eX3?X!Z9$8)No;+`?~;4HGdTrPKYPQJ0qsSTkkQ6e;s zeOHz;7Pn-U%`V+HN?ssB7ST!yQf+G}3EXth0!%zep^yYSK||u*uZR$xMZuscBLT`P z@teghb554SY6W`r6P%!{OQO=N@2x}FwF3v_cJhlwE3LQ`wT(>4)J6axK;OTbDFcO8 z4tq{lD=o((^V`+7*;uW#^)8_=Ip7iEV3?mpNs?jYG~Ine_OsiMZP?!xFCeI)pnl-0 zaP7wCwIHb^BXOBjWinYCuDA@@6@$cDKG42X@hr~nE0^(Vo0b02h`L;-x4PqWj-x~r zB=`ZRx-{%3lvi~_Z4fRXY>D<;-7O~=WmhV@tz%T~uW3t;u3rc{k5~&MWZQ`OO1ZJS zlFm+c?w?;!kE;cqst# zHDs*6Q2V{BhpMa!a3=B_akfsnf`X8G!=HT8!h&aOI7$>L+i!7|E3{;cS^4I=b>D=Op8QH_8rbjdL1K8ZR(dH^I5sU0E|$1;fHQ%;0+=>(($M0v6L zV}R9gM_Ek5LbRDWM5Tl%-rA*x8cdUjtz@2_UYEReNNxo0ksX-)+6gN>nxv4Gel74^}S~?&HaIMHExmJ$sw11ncS)`)4@Q zwUkxn?2$$FU-TpGPMXhd%e$V)C<&=_JC5F==0vg!FBFA z&cSc|(C)JYT_)~UCSv%_UoGS)MnEqw$|(48?F+iFWVKZjaa?JBr4jX0+Kv@ ze4;n9idJr#wF+@$kS3yJA}1OITsx%ou>grccE9Mi>NoQ2$Q!L(?;|wCaz0DpUf(!>Dn}u4S8aHGy4z#R zDN+0z-c6CEyY23^2}JiT$K7qMqwth%5Pl{n>i1|#{{VvXY`l%P9yA^vo~^jmT;Az# zO>kcYaHnXt%8ixSJ))l}<@C4kq1?9q*Ubi8*Wy(>Ymgoj5o2T;9O($xV6ZU8>8Akg zy2y*t%~^Qt)nu9ZldL45xUKcOLq!6dJ4D8K*<8tCzVfy&Ntt0*Us2np9a<+#b1Ok@ zExGJTJO0Rlw%-Xm6dq#n)Br2T+;?c)WogH6oW;fVlz+2S(JJJc_T4C=qjBew4FhFD zPCAm}LQn}Hb(9GUsPqG_r4iJqFN~>NZ?*32USXvwi7CPurgVXzdid#36ZRxUgsmDN zL(8PsQKF#LHfj^bDB$(EGvNF=uM6 zH;bAYQj*J$)2XIXrAQ`fQRqn1SiQ9kge*68wp+2vH7G7{?jsKE#QtghBFMoMk0Hh@F$(0-?DTFlDJgyQDE=`pRFXUhi`o}zs?Er0Lg4&Gm2$Ps zRG5vmUx+H+aM_WnC~`x2yQ0yNQK{U0#Rkz-kWjYPsU&*Jp3^NgF%*O=2tj#GOw*pR>}8sa z4~~|cpCPq4TcyfmlWwCkJJO|}?@E1|LjV<11`)cc_k8Ka<~Cd1XRFOwr4z!YuO6vo zUy`;^M4D4*AS+TO;W&3mDKtu)%Z26lY_gjv=6YuEJAK&t%wqIq6!fezP&TyN6zbF> zM|Aa^S;7X0C*2K{ah3Bmvf+?bIgaBN^e`w9Y+ZZQ$xALJr0o?*LP9n@?bpP3!`{X< z5(Av94mF2!KX6tnc3+fYw6?xC!zeyMj$6AaoM#)Lqih1F7gJNTRG%`?S)P)Li|nLP z=r>k&n3*0bHuUta$Xnkjc1Of!-(*fxTisq|OcU^11GWr0nbMhU6)umMssp6bBzox- zGGEOpr6{bvhB${X_^Iyn_p8@yx!ic?kL|pboAK@kg5a4Yy|rOh3@(Kg6=Jra2U-^k zrOhiPWhYdmX|ARsem(Tm)g(FC!Jud!pJ(h#Tr1qKAaZ6wkz;v(6DN`IYyFO@)Y2`q zv{s4>BA!tH02f(SmeSH%LQ(N4XK?BPIz@HGhbPKtYOUbNnZ~0%cPQrg;UsjsJ};Ir=Q-A-{< z9HO*jRh)NZL6s$o9c`<7Xl*kIN}Nb8xO~*L5_-h0Wp`RQU~5K-ID3^BaHvz)T-PkD zea#f=?r$vX=jtrP!Un0tUI=!w+gr*&Ird#jOsDD91ra|WRw3U=DwyWJyQihP@offd z^DbjUA_ARprm7MF^8w&b*&X!iBNBhxZxt%r^4-Q&OiR*D#>rXnYbh19@}>7g<}DR( zC#_0!6`X+L0k(2omm@oV5Y5fXCk(Wf+K`P;l;A-CDX*R5ijc9~pSUYZ)$)S%M$!b+ zRZCWDm?=IZ(ptnr^nr73pR%`j7jay<$J|@WoUNMW#MAM0zMZ^38@aN=Dl6|-SN>L< zG>01tSEs7fb(V%A=+WRQ8#9Y)vJ&f7=6`Uwht5tQrP**m|sjow<`c&^FDc|hkhRrt(NFg-&hWlf%Cc@?#d#{x!Lf-%5$3 zO)}a~fgk}mu7@c0o>D5Uq{YVOmxDzL*Y_=y@#T8B%a$lm%U^^rY+y zT2mmf2D+K)8V0x#Rh@>^l2u9O9^6>G+aKgE2)v_`@kiWoRj<(@Up+$V)K%%#QvNiZ zddgALzeRbCxapo(4HW~s+HwnJbwvfjnd*9~b(ZQ`QVLeu(sc?+)Dh(Z(CoBK$wf-1 zekqDvE>BX2P^Q~S21O@dvJJL`pl`A*4Xai8;+{T34UMht4kl|Sq|cCx7LHb|HPpRF zD=}Zg73szkda1W5R-$IAxs9Vkq4JakCC;dm8Eq9xxnS=q>ie{m<;phPA-5HvC*K3S z^A&SzCWt!PJ6)9xg@jx#h?!DTBFkO?6XIeS28!AinyCJ|>rml&4pq2{M^!aZg{U;5 zIue=6W6mOIsystbo7GpKr0sDcQrab_NK)Dqj@bo5`_)K3f=Nu|l@AjSFQVtPsZP5T zRaOa0oz>o{AFr_2vu>@z(o~A(=u1IqtS6$7Nh$LZ)+2Ssv`{&rHgc+JDwh>khI*ph zT1c#0=}U5Z_mY4nNFH!(ao2TT`P$N>I*YzpOKVk!^NOU}6H+T_?PcYfHmgEHMM4@; z*q0@y;3pFFf^2ud6v3{!awZ|k3!>#JO8PtAOopq}{lqN2SxHivGgO7BpHa00{3-{+ zUJ(0fIUPlK8YPaQSQ?CM-7Q3=pP56+9-5`1rahkc(*Pt3H0!RD=v8ID;xFQtAC1#3R##*Iw<&AJtl&K%$SwWEkQ_z^TVJ!-Z;gvN{%y#2b zk6!H;+ifEiPT6WvQ%J>ANqvQumANT#rvXaRd{RcS9E_r%5cC@9FEFklIV7Ov9dJ-R994z3jz#t{6M>7;0MJv<;{Q3v8v*BfUS+y?y3;>lD} zqcm00D5$eg!Te3Qk<=g21KeA-M(VkbEON8gpUX14V;N{WeZ)b7dEbz`bzeQ zE!8%aOWrk)obO=izKiS3ZOdnf9~JV?^&8*@_)EDQil)s+^4MoD2eLeMDM$@9TI+$k zjXGu1Ys5P*Cq0q%FO{9e^?30aHG4GsI4_1ggtq{6-4)kw(3MYm+LfgA+*^*;+8c9XLW^fY2bZ!kQr);cMID=5 z)l4~Kgc|joevXfFbqH7!AkWzv8%a5as3(q^rx`yNT-Xkk(k(5sp<0kcjQR<{x6;DF zLg2O-N8CJU(vh&NR{LhVvr|t%@|!A1SxZYgeS%R}^$`*C1!sG2F%>O7<$LYQcCN(R zR~8i4!?1?|68FqNQZc0HReKA(D56`(4Op))s-)4j=Jgd1fEjpok%<14r*sw8c1ZZ4 zEWAOF*(%cF=@5maf)}znOSj`TWmU(WE1e|DM7zZ(zAm8 zTBdg^XSO@Vt>DJjYFtSPNS2~fiJM{o(OTp3vN6>R(1l8=h{Nw#YC zTOg&BfOpq<8?h+>0Y3=|X%zC>tsTAYiFuo|r87>0$^$mKt2SMDC5mf} zQnd$D&_K?FRTkT)1fq(XlCO`%YZ1BttZnWb<3;6tEpi$!t%Zhwq11h`ptp5G$Xv-nQ%3G%9TOFIKN`OK4t#T1qWL0~)QcI5{p}2^uUz%Q73DWqPZ;-0BqLuc<`|QQfpC z&QVQwHCMReF)g=c0zpUcwvM}C4sx$(gh0k(4}9(pt-}jN-=;7bsO( zNyCj4lW~1zgD-}DQF&ERqC9}sDGLowMsoH=ealOQRi&$dDlDjKxx%19CMnEMD%r1K zUeYmeWPp>kN5{ulnkXa%B$6*_fu=d zDeiX;4!X9`PVYqtr*&aFdZ`FmA~J`%Fq1g7lQf@d&)a4*P>}g8-W%emc4^xVcZt=h zyY?MhW2qflev!ts2VGPp_dqvT=16N94j!Xb=$J14Yse3)PpnZw*(NUalAX~|&!pl( z1fOP+kjzEWZ>@Uv7W7VP6aNpXjYyB zqHZi`RpkMYh)EAtB?Nd6IlyzxK&&BYs!vTrmnwwP(@oW=%3=bA#qXjvSi?nBnQOx< z-K9$Aq%01TB5`vStwOJL<4RXXA#elYb!2~5W+eJTo7ZaLSY>G+6S4!LnFbu&XC)Ja zBY0BRB|5!FiBT~h&=mgwscJfmC7lUKKG00h-XWhtRKHphvp78(z7oh2{pjIfp*K`Z zOkq~^1f;Nox5W6wpG4mTpH`OH#VyGKbm_$2zYtxsd1LV@&Ar;NP4@S@$J zb7mzep_)Xf1hSo@ss8{ZOVe+HW1dlKojWSG)g=vpOKOqptfC8uMAB&kpx z60g+_mHz+=k{_HbHsix7F3us>Z0&g(Q-M>pwB< zjf{`@0-!p%B-txam4a6-e+Yps$ssJNE9WY;Ib(34U&E2>?~3f#_SE?Hd}^=paovzI zr7LWY>oJtY{{V4lvIF5-A#A8iRBR*}nF-6?8;}-mNfE(u8|Ans`z9vmQFRattO$30 z;6dspKV(LuN)%T_CE4#D_&`&tLSu4u*%ej_ge5`6?N)=w!Untf3ZC7i&QzNjW}z89 zGX7mfu;=5E@iUB-v}LnZXy2@BM=`vGgj?yGKM%qu)Si-!TvHWGEOpU2KRXU;)X4BV zNr{B+Q5ZsGW-eA-?awn$Mz@wklklG%AWO-KZ4~yfJDpT&-XqR#HSW7bV{z0iM2!gQ zV<`Mfl4bu!#sVM;;o?-mZuvcW?g#5lOGw4x}fH_d&;sGYYta=XrmSFpNi$aCihdDo5;T*=PDOFY7rjnn-uD^5?E(uZoi$s2(x9hsqz5JCZ2dtC_ zvrS=+xpJMDU=zlNw>lyb^SN?|h){6?7p~6xh0`bDV(iM3v6VvG^h{bB1c*DW}C!nf?`!N&7&DGRBH0cLPdDsO4T%szjKzp_z?7 z8AP8*4yrNSDFN)qm+FB8p^$ub$d9%hED+A_7Y->A_qkTCey!z9B|>@=IC!(6K~3Ba z;E-yu?n17guaY{Fe3Y-)Vs=}Af_`n@DIMN9%4&bV$X_GGdW!!5tr9lOQ3~wA98nc; z?R(peMeXGkcyCwq#LTq?4D6%N6`zEX2s-&6C>;X1PT{s@er7pwE4In_yrEQJxS@la zJdEKNIFOA~JPN)eGBpMqG%1eNP8?~-l^CUa4XtZk8;TyQK$#C{i%VO&6-{@K=WnfR z2MF>eJ9AYt%B?~^6z)=%2jVex9O8=&kjFd2=mk6SI33l|2|>Sk6-{Cb)7h@RlB>@16PS$WDmz^R+9 zRV}KqXaP$Cp|y~9sCk1qL~ptPEg{A=;Z`e-eC1-@ey*u?4>)%h6rxG@lLNl}9S$|D z8YUHHN9w6c-!dmJVe0`tQ@S=dh)FBHl0S&BK{95N20#g+rxs6t!)=S6p!>rGI}B;Xelx3lKYBv#qL^+%8BY@xxycj8V-Z|!22f; z=Fv#FQ;ANb^@MBaxUM~R!Iw~ce4&Bia!b496`18Ru6CNGQdO`5nRNz45v;^AYm*yl z7e{0zcPUAh?(g|?Aqd0n*$Jq#JNzZGL77kC0GO|}?l;K(QmwB$J}BbmYI(Ne3PPu; z8lTe_?W)VZK2t@)-E4nmP^HvP=_25!N;99ZK&ffUn^*jen?*B-W!!i0*RAgCA z&Ys<6WCEgUtEv*h2_8VIXV?rCp2;@W)zTgTM}o(?^6il~iC=zPCB^vf0r1WV#nu!i zn$v<>>FE~irRuudd!KnnNvX#QmQR_Ff-={#U*0+AQsTcvaCULyG2xy8WcPI(mi|SQ z-)kFmsj)gsb5r~bwn!)0Gap2DJ|IJG-8Ek;_uggG-MH{v{XMZ(YBO#|<@?DAwP2R@ zQ&Q9_TT=Z>ORf-4idt{4wkiC}ye{OoZtg1I%bPI3`$>x!iN4Nwvx4iKdY>aA8QptqR34}rJqcGHOKRg zaOb6faS(SgjE#f5Y$B>|XP) zCR}-wle7Lqxj02mZFa12zj(FL~crmNgfl1k{BuC zhMnBTs?l@*03l(ViH+m@L5$&bJa>R`9kKQ}_aCRaJ5(z!E>$J^s_T((e--!j)czkm zV0&DDD~qdIs=M5Y6m2vLD#ejeY3$RTt3FixzCn{>*44c+p`=^Yqv7%-%5XgY0NkQB z-}_|xa9Q72wI$~eQg@XSq{>=Cy@_L$Iq>WQI^-wZr^DQOt4eOn8MVHX_r*h-kbmc4vI?+m3bJ9 znran|y|d*`&;7V;va5=+9J$MVzmKsJ;_?0z;@cAmq_(21CO_`isfmH-tVW~@gol!#w$@UhbTfqtDK=O4v;OO|ZV<_6)ZM|? ztqv`*aB(DKDy0aneM{GnpnQ69%f8 zZOo>mryf^ls-S#XQC&e`)kx-4d?WxSIQ^-)mkPbh@^-4f6NB7H~#W2`r*0MS0v9{EqHMb}L8GUP_7 zu{%OursE1n6q`+JZLJpy*3?O9RW1*R?eHF5V0f*|%}TSC@muHys>N`}Y_xeD(vutI zyR~K?h27BVZ8^=o)U=vf^Z=&OSR4c;3j~Rs4v<8V5wffkRGP1OvnAn1x86yf_#9EbdM^Dn3#d);G!zKpL89_(zfkmZehGtbgNA&t_rOf&KE{!DY%t5 z({3RWk=mCPIE9U>J+M>R>FLEht-Zf?LuRGAvCQ4v+_J2~)Tc$W5x^`DJ8rF8GyW6T zYqJFjD^QUs36L`c#8L~MU>LzpH9LC;6k(Do{Kmear`mP>p)#&^e$lJ={jcQyA-!9( z`(0wi!Rx+A{c~0HYEdaIw!lhKq<}YxBm>YH#P5id^L!eAkMSs$1&Q|YmD64ke&Z_q zdhtb0Rcuua*~e+0d~^#-=mjY{^f4%b#Ut}Ps%YWf1?^`v$Zr;YLbqQr3LAZ%<#VFE zTP^|hIa6G88ltmyAPvWQY!3Ri%zel2@5Umd-2V9J1Ke*1=D6$0_Pi zg||rcOEoT{V0xI2$8QmQ<-tj5-nc?GL#>Y%IX@F_Oj~;QhLm4iA2TJQS}p5 z?j_5ulX8}-g76Xe8*sS7zvU!|b6uj2NP;g(Ta{t2dX;vIm6ad8Q%0v&(D#~hM7{8j z)i>#s%a8K?b?T9HH*38vU26D!TRqhVp(mo3GUF0bzhL|$pJLfP=JR!_ z;Ox@Ymg+Fd3OawE#%}(Z1xR(kJxXOvbvk&=^oZo<;-w9tRzi=OIoE>Rpt*7-ck{iD z`%O(vXu8xoZkX+D8k$!f=H6#Yjq(5Y(=y;*adHz2w3J=Gv$2S)@3JS!iR5w#gC2c9)(EiwIjiZ8RA>dSpeaLV=OyLZi)Sz+Sa~UPln%72( zDs)ymbVV$HFDU5KPXeJo2pn~J$AXwOjb?*YLhTmJo3=hkUQtPVa?8n9H`;mJG0))A zTpLIZ%A!Oi##gk0kbc;>c&JQfp+0f$Q{ukqG7PUQ?lX>2!`WVAUhCNIZ)m1<6jba} z(;>0zFhZ2-6r_?|ipKmXfdL^na1PQkN~+ulS#OrSrM^;fsaUOj9a;-%rlW9zr^5D@ z)})Qw54)^d3--0XwNoZOStqCou*@T0*e($~= zBi|s=4QmrxO?W9s8OSZ%kh-c|jjxlidcXNjDA|)M9tGv6LIedYPG+dMgJgE980G#S zdFq#{t&Adtfg~|b(ss0l!Ipr2xSUK1l(UrRsYYjqy<|;GZiJ?#qc;)LceF!@R@Rei zLaTM`l(^>Yc1}r%gJ`bU`lf=MQ>j^O7d5rs?J?qkyP=e;oDu-o%d;7O?p{I=a`T`0n?|Lhdbd4q4#fgiT%l4o837# zgXlR$o8y(&PB)_zwx(WEoKh|5c>e&0nx;ZgGDNyS0(L%LKo!-dMr&4c6Hrp;Y#e3A z^|aUgA1BXKSDPw@mEpE!(#@x;+)xFjI7!;DBn0I@vJIZ%Rsl_v4kqSOc1#ZE`(wyB z^5X5ECbBzfeb&(Z8?K~nbqds!6zD|6y2XY&AsxeN-BFyZntf23jeosN8x76(R?O zMAjPAsGA_Fc;FA3D&s`@Nf%Ul!FWUw*L3+A6e|Oo-T`cysDuO`w^fL*eVva5LitYh%0SQ(XZw zNh;9zm>j7qRbsfmC>DB^+v>r3+jIf9&{d*IG728ps-81dJ0DQC+v)pPMO|y+XvN=9}W2_dSWe&tlaT4ouS!C#=N(09c29s)zd76XM_4)h#+(4$%$A z`!k>44jG2{s4gyL~b}Wj%`^*w~J|VO2k{} za@&O=yg5wciuB914X~hwQl6)Jgr(;mU!C(c;1 zU{Fn)y}o_Mt^WX*<2=EW+uC7d(;-(6S+oN%LQ?V;N8b^S;kjYa#aT(&wvYLk)hyzl zQ(o?^r7YYzrssO1OvAL*)b{t#pnec%=mksySBMDPBp3~n&p{OW-r(RmMec^M7V zWZRxsjt7t1_^raaU$KnG6td@Bcal=(IBro+imMJwZ*>pSRWRnMQWkQDvTQj?z(I zDg`M18Wk<0PN$}kUnC%v>lmA&==Dli#bKvgEuhIM9cR;6geTo3&SY}7@DJ)Z3N6kL0eO4XTParE4ksAyC8+xfDtyX{1ckRU=Ag zW%*sd6}Qwr^Ql2;Dl;SxuzO-?ZMl&5S9}KR6??DlE zs+;wI25BWbp`Zlh_UPVxrf)-@K)4GS@Fd5Sjr0?=oyx&s6# z(>m~m+G)Z@hI&dOb7<8&9a5r7n#8T;1U4oPzR?woeM+_wAa}ZzZ}hf^J|a_G(3S10 zQrEddX+1vpyT=k-sxL(vrV9cH4H<%F59yCv!dp_5UT2w;b!M=4P|!k%_>~|C_eZ<- z^FU~-@V@B<+ApYYpid<69~AN*Al|t-rYLJKtWmdNzg0Ch*bObuQ3-H^vv%-(vi3$} z&d$!sN&A=9t;f86(`k0KbL9&g36Ppy zsbwQy^P}Que3$Oy@iqw;*MI*2HJ(~b13J;4R*IGPna>@E@&^v(6ty{SH<4UXO6sj> zq;{Fw)Kn!qkX!WTpk`yz{I=|F?yi_r<1S5h+pD})B9@&dYat|# zf^kmZ>0GR87%Hs)03UI4bPTs!ST{Vd37DLL*GSRhv49GdOOU@V&?$LVie}qN8c0YT z0gLaaVA)Xzxg^xnw%8!;1oYNk$bgU$T~wG^NKr@=m$n>;U_<$ZYA<&N(}fBPc*f z_Kups?rjtK0vHmJB$-2`PhEV>fSDxu&LX0JQFN-JL6I{ZAqY~`AdO1M(4V#&(+MiL zQ_jsGOi#pV4cQPVLi80nLrxM>ts*Ioi;n7Y<6BJ%Y8$9S0Sia(OU5LHfCMy>8mktU z4$&i^0wtp6zNj)mGb-1@7+992G;>8t$oW#{0#&*Pc+p80&K4(gYErDOtzZBWwIBIW z=6P?NuS&+Bfmn{&xfac9{{YFWDnbWfyi_lOVlYY1u^@%PB~cn|k=c_SX)PnxN*5q1q`b z$4P%mB${;qCAB1Zh%yptpq@H}m7L*uHdRIPZ87PUekBq5q6^D@prU21T56csPXk}< z*A{Ckm9!#dLSv-jrqdJCa=4!0ylSk)J~L9=dlyx)NdR>xq+EbPuQAmQoN+0h>Yd8{ zN)pbBIfn z`35}9gLL~!dL5%rz9ss1C=87orFQcBD_ea#cdAdCM2tAulARsf0#Iu_X`&TRXGH%1 z%^%SS%Oh1Y@{9o=9FKJYMVn#)Jv51anaT&{+eB;qqvfj!4ALPol!n-ymL!E6vrO?q zBRb><$PADVjFMr$r>I?=uM}`(T#rx!J#`{-_yCX)Q*9J(&6K3m9EnL*T`C?J_xX!Sw zdbhVFEk^Zg4i@NVWgT78t2|joq$O|Ht;uzo%+KCo-%g@8u6Go&Yd__eG1o|GaJ3vE{PZWt+%T?tm zEVKae(mA22<>gXT;>@nY`NSbpn4k#p9F6C1BHR;q!JM{Sz9hZUJQKszIkg@&^UfmTr0aB-?K2AetqEEewx`*$!=lqk>P=;qgoM?1MJ2o%Y(7=~RxAC&R)gWHMC?CBzQg zrz4!V%g!_DuKxg1nagP;=_nvWB80lyiM@2rc`oAFYPS1O^Glr2Ts}W+af(#d{Wwf# zSA1Hxyq6wvM9pgPfw~eYn|Bxps@c5|cQU@3X$WAFsE?_S#3D;#?4GpB_sgs|>7U?` z+_3RQ*4@mVY1QlodO{m#sYS$RiYT#LIVppugrYMGl%G=?^*=D5`xEy^bY7S0qF%}J zMB64iqUGOL8ua?%QYet}lsA;7bH=@uNA-U}j-g%7#o$drDH{%_Na{HySeyB!E1!~7 z6{24Wh^~P#lwGT-AXR@Ercuez0hF8{t})oKexYu%F9z9M_QKH0Hm7vJ);$U$yslRO z;(~NIbs)>i8h2cgF1J6+Y20~aBqVtCfbT9j8jZBI>J<`G(%0peHBWJ;-J*OVp^S8= z)UO^}18K!g_FHRqx@OsBkd(Ft<#d^i1Tb>8i#H_v&#GNZ&J#45dBpsPSq7-^Qo5CB zI>PkVaw84AMAhIv*jBQOxe5o{u_BEfLz{NYYv3VLC0cc*Kog)o1}2YWDsWKC zYJn3bL_u-1gd6rwYDSQ+PGT$?)C8|jDk@|Yqyesy`{HhxC>ftnoV2t^Do99_O2I;h zC2a>|6v}sua*9+9z$J4YWT*VGxW#O|gS*8hTFLZ`?cu!BxjU~W-KayhjvJ;`O){2% zQmf6weLX&;b#1WX{f1HHUUg^sR-Fc&UcF1mUf(x2hH^A-?O#Y7seTkV{d+CD>5$sj zP`StiB{Mq5&pokW)SMOd2YclDcJvG9pOx~Xa=Kiq8(h%)t_DQ^0CcFx$JDrMew;yk zdzZNCOnqfr8?Eei?ex^h@*h{^a;{YsB8hX_;a$Y2~5mR90jZfgco z@dgrDk&xVglTw8Gn}SJDGD$Krhnp6_g@8#p_y^zV>FTjJ{_lHcvEk40 zb_ewT0K=Z*H9CiD?HMiV?EUuOu`%nrai{eZ)knls7B|TF#VkFQdSA}@+#TxKT}|Ki z1)V0q(%)bF3a6ak$NUR{;n>%691X>E+5Z3&aus9NmvP+2uCnV@$N5z&Uoz#YwWbz? z)g{*d0E>AJq>r$bKL<+P=kKJ-iNYJbx+i)`!=>zP`h#R0EKCqmXmD zMO$(lh40CdN286>hUl%W0CWkarAuwdbQ^&p3z+hq6YT7`om321t-ai+EcVt-Mb7B^ zRJO`pKwIyuG=ZSd5}`BTBC1E!)UA9ZF_a2K=4mkDvGtb zYOYmE*d@LCrKplT5}2yKl#;g?3tm=LM7H;6)l|!M?)o(9RGoXNNbxf9lxFg!?TD6* zR@)lroiGp3Dcd>2IIi7^Q{3^6LZ!am(1eQYyTnW^%b(9ue(o=gQbUcPC{0$%Jr!9nw}kY^KPv ztE;G}th%tksWi;c&@}7QDCo6GK2wav-*LVLOlhrE8^ip77EwnFV@ryp>2&ER~@Ew^vSqG872fuWXF`pnjh+mMK8oDnra1#O60| ztiY}nsmZO3IO~}Gxopm&jpM4eIk+ewsA;S}rig4@)G80cusiN@E%%Jvm)zU7+igiv zlM?YhLMMVcCR|?f3N>F|F~Tf1HfgG_mM1DW?6#yiP{P7hMN+?kE12pgJ}$ERV7I=D zHE{Pi)g?qc^USKZrmu~0ddkcF9mV9cWUw~LcHwa|0RRT(q=0pvfJr zxg-FQtc1_93J|L6yo^lH8kJ=6+$)8+(!QRFrVn$O?&%v%y+WTFpm{2ko>IwOM7(s{ z?uF!`REFwFH%{!|ExTkdH0LbxD|oergl)BPg_R6Cie|~tq%lU6p%U|PN81a?ih?@T z8R0%}!!b@$#qF548rZ4m?+^T)!;&)QTfJ4=lTd=ts-~w>QA)#zQh^0QL(UVYRQI&A zS}5Nrca<)AEymB0<<~6bn>}moP^nKlQ%W6e(vMM!c7ScY(x33qI>hlY4K$6WrClpG zZgqU8#5qqi-m)5gJh}1BRU;Wr6I#KgsJYO(+7Hf9DZ+`U0!G4;N`!%jNjEFQX;cGE zTVDjlwW}B747S>8II!~lrsYhg?Y7#hg>A8KIO?RAlI0L2DJlKZ4^uvnMFNS@^Xjad z7{3tb)n=E5tj}Q86s(|Lu=~}(J6iAti{n0(p8I-0nFtI(>f0rWy$BDR*gyD2u3%SM2LTncSd4XA3F4!Xj0(MaRit+umJ-qkC_8IREsE=~H5 zH@JeQEicVdnwym8_9|e*1eH!8g%{s23u4tP#gpXf=kV9EgtDM#)a@F?{Uq+H4>7~h z$2@Gj)~$h;Vb9jJM@?Hqq^6VR)~^y>^WfTy>z>KfHGv5rj2>M^C` zxV=(qv0$R*e;M3A#ak2=`^>Vk154EEf*^K~O0qTtsRJBl&x2uAyoV9CsJ1mn-0@ zrKLirxkVjT1pV;VOi_{8k-MhZ5Qi(H8L0+U%Qr*G*xdV_19R%JSac(VZ z-KlYFCTH6k7^M|itL$+Cp=tLQR6fle6Kzy&SV~EC3JMCJh$3*eL=dQB1+G?s7xw=E zFH;rt!mKH>pGkVkdQN63oAWCPWcXDB?m-lQDzb%?wv_0jAPE!mi!u~e*8~+sYU~#q zrM~ZXVwkSoYXH?g6q(Ik08Hk3ctj7`X)Wa?GpTX~+E-g<$2}{>qa|U6@7voS!cr7U z6~08aP^D@?fZYE8i5Bi^C5LG3v^a(8>2I&8$#P77p`gW9!SWQy1Qe8&>a9nV4bDJA zXw@Rt@@8w9X1JFy<#mH1&J@c`H*b203AK>hYGJfWK?(sTeRMj(HuGym71NwTlW1$M z=w-%ltkYB0zNOSXPQgJ!ORC&u>8OcM!Za3?SnRWCmF?M0O_B8~nxxa0M4Txy+$X3o zT!0mo_?xIh=Un}fQsK|z_G`1WDXBuyaZ5`nLebVlbnu2pV%DByV_hV~H0-G*gFdda z-kiA#rTp^Oxm8J7OPi-Xz=PC5QZPAbpofrYy0@6$E#;h;#XY|($t$vFT9~&vaHw*r zY#@njL2#KSUJym4vsg3=rz0!3CaFfZi@j=q>ntf@LTf-8PHR2+WdYMXVoN))L~X(8>73urCuPCEU&!csTopaP=$CI z&!ib@$8>;p>I0W!B`$8t4sQX<@%vQ{MO%*5v5i)`rm1i0r*$-rIFl`@cW&9w!g1Wp z=jEuX*kg0N?bIm-*1{d90bdHnsZ_imc^N{s5Y8Ohr_G6KLX){)5;O|;Ky$XK{Z*+Q zjdEbdIa3U%tyHP*j;7OE;5%lTnIQ#UU?`?M^S*JtYbSV(R==<_2HPaic6N%_cP)<$ zTevFQi*5Q$L1}-b5nb4~W^8XS^5~!DJ*c$~qcoO+vg}U)%kHy3&fv^$1go1Jg#?5D50ipJzMXSl>n+m#y}9l~CNp z%_v=RCvTRUWeTpi?L5%{i$HY0Pcr>1repy#Oi6+7$s~AWhjbr$`AW-pOpuM0KDg6U z(zv$EVGC@KQ!PZJ)oo{U*Up!gp_&yQUh0$SkU#|f6V`EXz(%TqW(|}JmaGJ=NdRpV z64EX0WhDbED5%Fea=VyKitTV+f8}&^4LIdxVpSrPAtaf5bb+&(=Gfbpoz?#UYWB>T z8=)B6iuo$2PT4csV7+pjSLxWZK580kl=x-w@^0HeLGlVWWt`_Nmtlna|(r*jwf`A ziVA(iq-*_Q(gtHfCHDmyZka77YGz44kD=Bj0*WU|QndT&t=^qyZhVB_5b_P&BY?SP zv9>PnR4A75tL;LAG}lI_qFQl8HCCMl(j3sCTee-7+=m@<0k2KRQQ%J>w~xQXuP*ar z%h+?c_c^2b>=g&Z0jNICA87Ck=x;8y)l=LmSW*I%+KNijcO;4EphkoqpvTm4kt0Kf z#eRAYXdYvJgc>i{m9n;)wJ$aZAk2~DtPvYIO0UaYHO!)Q=U1DnRf|@bAkwI)JM45<_l8UlYtQI?U1uYO{Qbfcdj;I-=3U9L3?`;7JpWFAuQZj)K zB~sOvw5QXWw}UA_Wr)abNGfH=ZYc^%iid{b6?aYe4He!l<-FsYW%L`{)w>g;;{0Xo zb*4|wQ#9pkt;nfQsXrd$)?oNY$Nldm%dZ?CS`C-!x0N>d@6#CQsZupgj$5U%*I<|C zuPSW}e7l9W7cYgtl2AnaXVL)eXUyBCmiVJQ%#P6G-Oo;L3pi~YOPO3PsjWpxr>1&p zU?^%q=zL?{WX?jza-G-7)}5zwBz)sQn)P_a_Q7+e74-^QQn9@Bl#CkN_RATf0l4XH z?gugAt(|4b@8pJ%lQ0VjzLb4$+P;RC<;NTLDgk>^mvF06)wy1xh(Bz5Rg{0z z3+4R5{<%nGO$h_eECQi&5t?H?#WF{alq1fGoZcyqoaR>CR)P24s11~)%=nEwj8AI; zC0E?tlb~2@-Dh=`cM6*84T_}iSyD^MR*4grgj;oaMHT)7x1B4wowCcCqgiyTe&KLZ zo~U@0DQhz(W^q$A)n3hxvTQ7%NL37kl>wx>Kx*lwB&{2D9U?g&K&Ff?5S`?S%25iH z0Q%c$wJpVM3D$6g(JNcbRcYluR;RSG?XKFtQlV2xQcU$e=mL4FbXQikqMEABZnfMp zTF;!WQk+W6kqYQ@dy;bB0awsiFClWk+vo^A}rC3Yv_}wAA}xD>l&TkDhWGoVB*fZ$sf| zYxs;`M;lc+x^OAAOvppPBmou}_<$)c9!pfKkC~Roi0dA?+uJWX&9`-Duoh7IsnVx5 z?;uOx9_`!wt^s&QcgR`^TkqPn1+N~$sGUJTar6SYNKBFAA3|DUgKXLx#ePb8^L0Ju zO~BTGsgICu?XKMVn_5yft62lkMdk+^M`{$r9_VRygR3jxb7Xp{MS(pD#`drhX-Ob&8jscPlox zm8x#WEY{q8q*T-ds-j@4q=uexb!`)43a;W(?GxqRN<}gQOebnl(IH+?X~{Hbgc#zI zRmx|5|N%GPa z6Vyi=p>yFSv!K>FD3C6lpmV@!;vueWQ`@5OP^NzrGo+s9)pTf*I-haUBBGE!tM|#I ze(vwO5j}oQJ5$jhOWPck9ZfSEv;a#`VF(EVmpJk8+lox3Bk+m+@S(m6GU^pLCE1CU zelD@aHLK51Yp1$7N*)s{I(K!7r3unN%>9uB$3-lEI9SKbO_y>y)c9^Q`$>;9%(Abd z^o(1m#UJA~Y=)Y>s@r33NreK1r=FXZc+{Rk_bvoTuY6 z+o05W#qHcLxD-4UO}xyFPZ({n`*pgNqd*X(Bn6TI)DLW2+SvxQ0)(3BwcSW9y;gvf zw@d*a+5U(>=}@0o-xMpZUKV~9QYH9((9>w;&d4+_lDpYDollUL>k#8;N@wa}2DUFN zI;}&l(}IKbP`WlNf~D#>xu3Ed^ohqPD2GNh zRI&kDl`m{}(S*^LM_R?7B<`P01mQlICVr`1(u;Vsl{K0EpVbm{hk`?s0V`AEX-YvU z67kX$LOc^4n<;jmAEruXQTE70jI=|`mvISIi$yw8l=?zn)|mc?m|LI@Ow}zlZJ|PD zQUM(&3-sYV_l|JgDO0g)YiLje`9qr?iaxnY&DUDmN=C#F134bB=ct)ED8qRrwwgsL zLVSji9TcK+y&1K=y}L+B9wd!psGesbepe;1%!7MWK-a_iAtne zC}S}xIo@2UnJ8R=>96@=0(aCDLEcxmIFac(Qap7((;Z5c46`{CUn$-fG>XF^*His5 z)J|-qB2MyU+^28OD209@GyO5irgU8LE!@ggYnP6yg`)1d1zFA6;AL2#N3_RLMEMFR z=3{hM!m70>drAF~)J!fYzw<47q<@Gcc})J;DKP@&_P9!K6aN4Yd~)Ogy*^BXhG(Lt zVXxV8^Yd5bBDv4PXZFCvRdrBmxj0j)B3hoX<;XE{b8g{3Xq<6My!o~GlQ`wTuN6@0 zh?i9eD>H9wjkuToaF{e%f@X4uW1H7%iGqjmx|yg;xsW==g;?KpjNC zrWfg2rTtz?Ci}ciV@Wb)C&XwpA9OU^H5Ud^+jMwK_}k4cYl zZJ}TGX%qg+VFi;y@6{fOxx}_2k^^Z!`Z-4%W`sxTk3^i}i!Vh}pdSfCdSNf>jzxzN zSoQw^4hi<_9n%YjRm=DLBC@Q=vW-2Eh8FlK8<%qYt{$?G2cV5Qf51aT4e(MN%aUD3 z3|dwAm`ulr)AmOTW}XSdEacy=;fqodcCZh(NQO%W(zHZHoO(e20ArTvOtb|2OrkHO z9tgH(TvAm>5nIrIy8!vfL}MPN{Ya!rjxeJNQA1!K4Iv$36QaqKlII&!x44i1Pfn2; z#(;@#T$B=U3M2%Q6T9_7i$;~lC_-U!h9geKF;?Ips3B6)T?FE*DQwXhR=9kNjG!tR zfY7&9oYK=PRJi&^zvo616$m#!YV}OS+_rV~Em&`e=|3l?t4K=Lv?X0Ue#rJ1ajfO4 z@Cox084jv2$Z;kut5%?$Or`9Kd&@xDtp%0qq@`7D$!%`lE=aeqNoc5VNa!OkC8?Cr zYOSt>O{$i#{y#-ED``?n2-F=X7xtKd#^|Uob2W05Z9HrFrAVi2O>T7m0Dyx`(UKiLSUQr}!-Vz1bKv zB|j~EQ($zZDRjjs?`k7BGm10urg@2YNj}-1IIeXUJ?6wE7JQnq6!*dE-lL~D~C{jTZ8mFk~9<}B9hbjl%e3OYu)dv~J59u0yV2e%S^0avBh)>8x~z#PHs}r0r2CpcKWu!9=4NN> zGGq;#qWfpb{@-nmAUx<@sxjR@VC9R7^4sf9mYl6gr=wK$1?6lK`{LWMECcRZJQjD% z{E1`+hjoKhYOcud_Ny&9olVxNo|c^>sdk~%v_DFt=scS`7LIp-UpQi`J=Rhyr2Wa%@vQgsnsIptf-Y(&v9Ec9xtTa}$u z2^)`;Rg$#4fha$3T}mn)dCx(nQ>@OU^@-aRR1VQM-lYB#TSU+djQyV2hDgd}A?{ie!;E`b=1zL0eqVQ6-^!RjxptsVB zizAa9)hS;GrYvO-QmjoM7zsLS;S9Ej2chy;ztL8@%}-Hlb&Bc=w{7*b(p5EeI@HVT zs#2O(*$N+Fi^C3-D+*szRUl`%ZIeB~YAACod6zi~%!GbJ$rMRNElp!dSH#85qz#Xm z6Sd$1$3rnV21{xw;kqF?yEfpvmoVlISSl-jm+@L^dW(*Cn7J=X(P6hVYHI40Gpd49 zv9$QDNgXE?`8&U-j=z}b0TMqtgDKVoF}L5KFWO=h-N zcDc$|osg1&-bt+B0(PRb&f#p@ zz#uiI{+@wBd<~d>ZD;2W-RoNwG@S2@RN{PLVn$1qIqaa*V*!x zf;gvtAH1Am+mLcw$R4$XDuWVIx+?Qp%7>@{>S@{~dUPa3Y^UK@mJ##IP6`jHsr4OO zi&-mKCBjU}CsI$gDqd<=q;du@0E;Qs`cGJe?W&qR3I#7D2}}SwiRlo1GdvZ9H%=QB z(y9cK3a799I>UrKp*CBzJu|e)P}qJm_}Aw>A-_)A)`(|aJnU2I*;<5b1jzIU-35W7 z9nK``rHp3iSy};E6HwjPLnt6LA!x4G4p3HK?{+|>MB2k?cfu zmt2^))4C4ovAaMwY7;Tn#t&;KmRpS|75KsM7TTu%WxX`3WvYotHz3o06H}21Z70HD z0tbXKvYOB!n{TB`hdF^G08DO6jtWWJ;DQbXilBma@HCVt)#r9}@fs z5{et#nI?^1n}zL$F}%)H>l?@O9!tpREVc_ZMc(mxcqZRwq-jpn)VksQ)T5~+%%8NF z1(7(btE<>;o4UHT9tY#D9llW6IhB?2$1Ycteow#{?Pj#k86CHZ#(@oNpT>lA)^mem ziDWLab5HX8zMh|oU(GYR=BQuCc`qxbu;Wx}*^l=9h!&~s^uQVHxIWNQTq8-9DMX!Q zIz*iGQ9*|?QiIdef`u+zzkRBsHcV=jyel0-LL0VFovTZ6KxPX~79ouv z5@F1|t?oFbxS~qtW=TFUjgqQ&TZRo2hSf$Mb+T4i26A+D1bB{}B6fvWkQHju?rE#V zIMWQ^yPp|ytsZ}wa=tv9mm^ZzsOi}LSg)pOmXk*7jVWi=a4ltIkV#200EmXx@WnEU*yP#=BOgpwp>xRKH(s=H;AIcOBKXXp1U zlz6o&IOfH5sl$-8S#Gtcs&(&-6iv{VQWio<0O(~4%%V0;0V`p=!)<2qQE>`Ih}XI75^y7H7D-C(NsA zDJppWuCzZYE+bimJs>(~54Y(_G7qsfD6pNKzivB_~OE zMy-=I!$PyX&l(V>jyt{3Tys2Xi#M;X#_=lZrkltwG!2T1hAGVVINNFp1dk#jJH{5q zd3RIVhM5OG%0}e=N8`*lAg;FY_ElY$SjbqmWEV8QQ|hr$3YvHo7R-*aJq%bX(^O8ubf~seR{X1N*U;SCEnRF? zBh;piTTML{8Blv$l|dTpMjS;fWGP$2KG#mbbb$^{KnQhK=1Jr7ai z6-E53UuQcXK%?pmzK15eQhz%YR@5=@uIQB(9%v&}r7Le~2ip+|$tud~$a2&x33bLf zE1GF2GTeIcY+>v=oTiF`omAGsEzN>dnzimpbxH_7igSqMbxHy#yj6J0sW9$Pz;a8^ z4cf*Ro??`~u5ZJ42Nb)7(wMLKw<{y+{`I!{xlTSbjX;#91nzh!=rz$soOhk&57_Kj z=1*Nvy9*nWjXP5DQ%wb(>um~IBqvP-oIE(8FIj3I;|TRJFqV^5zqh+${G>pUvoPi&Iawm7b|GR?XccVAw9hl^$9E z$85Cr$DiZ5o-)<`!Biflc4$(LzLLNbO1AVpB1W1yQ%MmZDlN~vg=t}%g)7SS-)R2; z6)7_TDLUvOB$XQlkt&d77uuX7C1FigQRJ-EQwLc^9YB7aY}K`@36S{E42UB`0$j_? z7KYB$gqN4lRB^@8s~cC^JDsxh#WiXkw$!GjhkbFqEv-%d`e!1O(7=q6r5Po?8kaDu zwiAeCRCy05*44RTX=^q7TR`zh)AVDB$sXC$?X7}g<9=ANn_&1Vh z@Xl<*?|4nZovJ#U);Dmca+c!N6?-1FHArRf2}6nal6nz85JXtmW*FgCnuHElw_H!g zC{pcDJy|YShtY0v@o#ZwWnW{j`PY72} zMQa6{oGPr(ZkZ(>Q<2eA+Gd|kNlVR<(DmyQ!*fQg=W#{TZ5PuQANCDhlHyCM)WL#?_*tc_tE?D%Yv@ zf%5876sZXUm2EvevFcIIUeH&CUCl=kU6STAhA~j%Tck-k5EBw}6;(aSB85D&J6$D~ z#^F)`+J$aRhk{|O6)0v)Q$?E@E>Tj|&Y>X_VU-!Tp$Ai@uxnyzZ55x>mb066yq%zT z3)>Gp@YOd3XEv%E72=+ax;us^bo3EVagrtLQM|Gyuzo3Mw4R=E;eFAE$y?p^qp5GH zeFNDY!-crA=uSCgA9|qO(sr)@003kttj6lsk#W7@N)}tmAx={$%|e;=Cqh8{EB)X+ z<9*xNlX(>+9IpzWArP~hm8Y49WmTvFZTU`UKe_JKE+mGCxaDaTR!54jRW^&AJ#8Au zsJ1-CZ8@1A9Xp9w{ZU1ffJqaYc!gnOa9KNF;8xxDFv}T^C&b*XMnkOJX3D*Fs4?*+Ou;G`G<}v?>P=Dny}RB?W*}( zfB<}KIH05x{%XHmX}eo4=-B9q(62P}3$&R%!!7{SDSFV9u|mUW_&`XP-4xg0zCudZ zVp_@f%e8?7-+rLd!kyLINbnltLELeeB)H9^_1FGcAyj`EV-s&q6O|3$R47M~#o{3Dnq3ne;iUgB!wAZ1M>*)>8@i0%upDB+B z=^{^tQC$;H<$?@~WT7~Rq!oyktMEqMJUj#^AWkktDp*BBYH7i_D+MkmU&5g%8SLg1 zMMW_Nqk{VF`e$c#eb080hhA^BE?o0oDX4c1&MTbWw<;HGX%A3TElMRtZ&A3>T8~u& zA5HmHmv!Dhk>}%Kq5a;5@+k+LG>clA=!o2}NGMcZP<+?5jznWq+_X;HhB1-_s{g7Z~R2Fw!u2gR|!D}dMR#{~2AB@P60ZPqR zpxR}cS*cC?W&?e%DMdXhicjG?OttpI#0skWxMGiToUUN^t0$&v*eOXmnG@4kWQ?L3 z+dCBIfTty?4pZ$?feP~Si0F@V>ZYt}v-`RD^wv0y6?~?ubFfwbQjyg?Q}2jSG96UU zWvVKXoPd;~85jUd6vnDrvDA0Fep)}aWKNFL)?cVVf_LIRWST&2jF%}p@MvPY|ElC+UT2t#0l*J@-U zrasrRwPESB0=4pY_Ddlk>%5S!E+xoTBxAHqF156JOrZs}Ejn#Zqf;JRlfg`8Bn9+l z2fFOVs172uP>&^LcAEOOeo?@+7XTYdXYbN9n2QM*%~mr#nSxt^$>OB+xTo-tLHKb9U`b6v_1hSk6KE1mS|pVb&<#Xa z6Qa3mifUA9Op`A-f#*bAX(iUm$w<&dPBlfwiafUH+HuEKwJ8%)j3LlfNFA!HT(Qe- zQ3q|7rG-4Hr0qJ-qycetT>`eWw5Fw3OnV!+*@^~S zdDk}WPfn2x*F2+CUA4J!(O0~V`BQ(c`D$Y}NtB03&E4D?9YD3VcCCrEDeZ`2DWwU0 z^hw2P2at;kOAy^k2J-BaK%~VqmK#D4Bs^l$lvRYHYcnK{iv5wvq@vCenMn1VC@X>` zQzFumdTld?J+)Isx{O#+ZsNVY3Z*Y`fgvP0886D5wXc7N>(+muMf)~X?jI6oH&N-h zLVAx6Y)u5brmAPhqIX*^#g!nXTThVGM(Z1vXJZIfDUyRT*Z=PisdC zrJQDnT&mk~)9ud<@af}kW zsOCe=)m(#EE*T9+t5CkAkV0keFxe!jQo(3r8YEQStg9aakmSEaN(e|;;Ek6kUS(e| zwN4OZkS27FQHqqwuDqdzDW$ejQQ$$=UQrv>gaD9{Rgt7Dr_&?I?J*xvq64k{K%_Qz3m6Gt`)78<3IIRzL= zO+q5v{A(L|UEqwGr6aEOd%DM&<()^bK(KXMrfwIfqpnI7QrJ>J0L*AiXfX_FYOKkZ z4OO;d7O<2Brj;9{4@C8pXL(RW$J4yNb;(4$WsA<2SA6cW{M2?_^-3 zN&5sb;;4qUh@$T{)RL7f33zD()j#R|Xp@-*y{$s$3soOXqKe8WfYAP!>ZXXw30Yk- zWC4`?gyX80ro8@eUXn>8!eDC>^%n@mSHI1b3Ybb|FT^DY)hbR3sF=T&o?R07_uQIb{o zdYQvh(PC=IBVBxng+s1OZ}m`8r_;tC(o%IwELf=~VI)hWqq;>xy-hpLZUFoIVR}kt zx~OL8-r5G~5$uSms%JT0lS27cfDn3!|h*+3Ig$KD|mNhl(Ua6v8!`068w1vla- zHM>IElQ+@_yh8&5n~POb00A>JV2O-I^m%_TgZ~!<$DSNDN>g&-2xf)O`zfUN#xgm*> z>In{}aouLF7`Igvdy=V6hM*aA*Zi^4U4V#g5yd1w$aL!@7m`wXPpBV!NA4vpr1U zx@s+$@hAZ?uVcMOH%foi%NU#yzryNN`I{%9}o-d31^Z)D2~z8~o!NjsF1T)p{;s zK%Z%8m5|k+?(*VoDLOqf@mX-j%f0EJ0LR1ldsCw;GXz3d-)jCLNgd~*` zsHML(+ltWRK!{b_1sPT07c6G}!?)XOlulKAZ3hH_N!EJBRi(J+R(Cl@J%^P__SLi- z2}l}BN2F?6-4LS<2NUT_|Xrv{WS1CsfRIj*ZSG2|3YceZH{DA z6f3yaF6mqHROzQ_#I>PAZmmixSQ1JI9Z3Q&roOeYgv&R|7nSCwyt}#RTZ+9{ES@xdL@0s@5*;GOOC}9fESVWzbt?U0ymg6G(K7p% z*3&eJjibm#8>rP`4CI94e~qywLECI;NuHx6{4@+Sd84T6iTBF_iBt%WUl2^WeQt%e*V{7wn8`D!z$wb=&YxJ!02D*rlVl)6?7S zj3@U%31JBy;3MMeQtO+d^SL!&qc>r;RxONh6j`D50>29{7Gah&x#_C&T-k90s=YS% zpVKJz{{SrQxvpI90es))z2zflS-aZ>t&F3&4m@Jw;v2MDD^+v3yx{?~w{2}zASkQ* zND=A%?<>WNn!!>+@~+X5pH?2@Ko)rStIc&hG>B`%SmZtJbhD3ZH$?QY}U zZy{vZCBHPM!|U<8E0@rh>6cdJV-3|cNTfD|INsf*&{U#yn9>BfhNo3$f81`fi4B}=ZB|^GvVvx@N9pxFWxT_OZwG1$&;J1A_Z{0s7TmjAT4ce#l_GOc^t#LK zi!abZkN*H9Jn%{D?TU>_+U3c^DNOB1l)l)H^hTZ1x4)!9({|lyr&4#-I_}xX$N*wb z(AWwde)yHbiplXs-;ww?9?x;!G0W;PJ9jACg{M1Wowg6=Q~)}V($wMUqT)(>T~Zc?ccoee{J|c`kXQl~Q|I3Ex`)Rc+%5UF$gxJJTclE)4kyUPOqLw4 z(P`V65Cn}0L|&@{gqkY+eanwz7fk;8wWB|)t)8gD@|@A z$kz%7)k|=pwO!QEa;1lp@gSj1r+G;PB4ine*7rq>&$cElTsB#L&dFQ0UP(N$+!>y`q)Lv@Hda=PSy9}BKz$Q^sGqhUV1+4{2 zhFqu`gv=F7ci8}~noV}LMD6@KlN_7{Q6*I<%#}<;JoHmFlqeHfwsYy{IF70PK4dMX zt=AhN#{#6Iw0V0c6S4J4EP7Qv=<3KQl8BJjaP%savAcmwjqX1Pm&HG17flomT7lXv zHC@K4w!W1CN~T~o+SCb7_k1+*kc|crq&mD8ka?AH%+SEDw=CxvcNFn0^C`B)Ob;+UutX8@C<(3wG+KX&VQkT)Ro<8u@j#Z-ML-ia+f25$+j5lF zMx=ePmlEkfqi%w#F6Ta1zKi9rif}q+tGPDj+n@P*m+4#V7SsZ@+%7I?5R@eD_`QRv z0KNjq8P4>LWS3G1*s^<<`KHH;+HV|P#TQD8!Y?XPc~6vy*;ka-HmzG|r{ZpbwM-!s z`1+kX(O+d9GrddHFkQVJyEWAD_4;~x>)#r1#!bsvD%DY6OO4{T(3i7rL%Mp*SaI4z z_+723N=i^wEv=v(%2$M@Lm`X}NmIX!UU`;-q!`^2r4rOmzYc?O>!d7EsH7>&NXMwK851g&<#qcd2gJ=) zeci|crC1zyhVK^IF_H2`&;6C+?QB_o<4aKG-mxnHfkkXUOU_Im#J!2}oE9P>XKrtr z;ispkpju1Ybqzl|@it}JUkT&x;W*7gj$dfJWh$k2BFnN$6|$95Q$0#j#Ufm3GfOB6 z+>mBLnIqo7#=Je;Jv}@YK6|G%`d5eJ>H8HD&9Lj{FO%N!+l8^}O4~g{j9e(4PRA+T zX$C^%tCX&{g_^pkeO_Il2>Jwd?HPWHL-8~xIp0bhFUQ>ab$|IR5cFBI#V!lyT zCaKGE(1!IJ$wEny15gxx*+dSGqKarp4yw{#>pQvPPFb{e=Ynu(xkZJ5>ib4!aXkE z9QL-0%%;k;x}h;oaGW;!!Y-Jj70F7QEmhJ~yH2T~g&6{k0TC&*kcukbu8hA>rYpx4 z@i%fgC4NE1ciKKZvs{>Ac55ZtTG8;-XBU9I?>!doh3K<*As#Cb)_m1Xk@;*bSMFuC4@dcCTxeErR zZ8R+2EVj0Ydgp3nD5Z5OnE8fLuSE}N4hA<+?22r`?NH`5np0QWKQyjkR{Ld1O7Y_0 zGN_}G`1g3|G(Iq6WTuE`Fgsrq?!vo+%J|DLv}QbIkUfsxEsC2JNZL}csiDT!m4cF$ zqG=$HA2?&pR3hJ(RcR`DOO0u`x~|v6_CPMV#ZqrvoswSZDXK0FT$J*iRV5>>nTEHS zp)6R$Vs|uWX^XJ+jVsT$%W^R@m@ltX0>Wk8bfzl0zq@pk##9pN~J9b zBu_|tZi*HxcOlzF8(cC zgsOv)=au!3x~Hoa^(gN5M6y3cBsl*76&*i(sde4Uc|*v8LWi^wA`AmNIrLUB zvyjv&dy_ejn!A+EDf7N&a59*^)^l zm@6G)6fC1f2I`9=dn4ahnw`GSv1o^HeTDPaAaR|)nl0G<5Bn`ePs!BgmA57sW}c@_ z!N%mLb~Z>0Qc`4n#w?kYfIp^wWsbiuEiM$Ix7Z!Kgs(g!&3rw`9G`Q*c*734(_Iyu zwL))L+gW3&G`^Dwr3J*SiIcWLLsJsHYN*D5?_UK`Hn_(8^MY3&O8^Nh^*|ps9AjPEIJ(!fDrnup2_BJ^{uHTJ652ipE<;(-xAhaYZ^?mdW<}UH!!Bm(4nCQdJudf zaYMSSxrh?9pMW_guQ0@Nz9P?V?p*9yB_fe!T|xB~MOuXa00qP?!btdljOFGzjgURM z5VROO_b~EEYB(yG}Q!wo&%%*B)7J4qaKdQ;ORy zW6KUJ)d%LJl>zun$R5KVFT>o0xo)Y`S|57)=KHc5>=z?VXet#~<65nh#W$qfar9FaP1-_4y^-Mk(`CsoHwEZr9&4 zeN8fi{Y1$=!{B2(Yawgu7SkgLk|Gms-c%TE3KCOeYKOHkado=O+8$jz60#hT+H~{Q z1XhvAsfTs;PnovaF9=4}HtL#jek3U~AF4fH%bT*-mM@q5^~Lk@zt)(1FWZf`Gocsk zrAk!;(-icGQ9_sDGDfj)-JR!d%Qc3@V*Z2U13_;x2exf z)VQLrNU0==6XB+jKWulTmoPj(a1wWyl`OHnXHQ~R^UwN)@@>xj$hRjR3w?oD_ybiS$Sli*9uG>!MV@ zzMDOfs#<4~`}ZWRRq07Pno%+Uh@Fk7s%x79LlL$<@7oJ?>8hlNuT#a!d0~@c9STwS@Mew5lqmU z%SBU`>b0lZrfL}n{Mhu*DX!lg>)H8S0sD48a0}+2_Nx%du`tj1nCCa_(2u^W=zk?h zZlTZy#Fc0z(mt0Fug~(D{+eD&ff`Hjoo5oDd8$)3`HN6gl(_pKiOkI-Uwkhr9(e!#j)yknoEtNPKl6^o}Li8g^tNxx#x7=<_vDqi(jcu+Cd2=Dg&W9Lu6@iQ+D0s zgtV%q=9nSXC;Ep$3>03t%Z^~479dij5UG1SwTPrlbG1`tiln7zCs>8dsuk{2J(92C zYz1|cK;V!mX2n!O(33sKtOqP%Iyq?NFIcNW3rf~=)n9B^O$AlurR5aGMRl8FVrFF( zbFhV}v$_t@Cw;QrmE01SC~eQa*MUM5WjLgo_6W@l7OKL^_CfH2M55i2{Kd|U-6-Ct zZ7T$q0Ukcs7UJeNu=1rR;dry9;a`1IYRlfCu{v}b%x#2_d8jT)z;&9#afGdl$UBysen32MdMl{Zi)D8JEng#wpqgc8tNT(qNPR3 zhlx_wqp3Y$Hx=s^aW#yhK&+<-267(HYzpJSU0amZs%Vnrgn{#cU$U}lYSmK}C;_5@ z_>b8Fy@uXf<)JZ0NAQQSGJ(4TCh*s{h(h#ZGHApu*gA_U(+TF2=lcay3bDw32j`%>1Is-egGT@~S`Zc-p4ScH2{yQa15?pr`_)KC_Iq-Twex z+}MdFbwx7p4O2PpAi1UD>rWJ_eMgiGacHmA2ZDTA@jIOjI_c`1Q|VBapqQFFY6>dp z=c}r#-y`v5;^TMuni^u1tE%Pa6qk77=V-L{Ic71Us2pF8uNJn|>NHffe(?rymewVs zRJS=+J7nUk#ILTa;B7=5B_bkc!J;4QKt-CvvP38ZGNK1b1FSWW5>kuMmZ|Zqxe_R( zT7c>V;&U2sz5zNc!y&QMqN=*yWPuz3Dc?{>Kf#^cFJ*Dn|c@j(4Rh%N}I%4}&Q1`LO=e?&#~0WDC6BDJUm zGfsT8Px&FjKvEMxx)-)c3PTc?hML5c(y6U=Q0-ny%)>;VDMTcs>ZzXvs`yD(i;$Xn zd!UHgP_ht8meA_7@lS+HBWP0@x-^$0L7tHqQ-p(b+FC#JC< zIZ<=9cv{F-t-hkBeWXHjke)e8F7fI+U+a4IC#sXy7=bcs5N zI*nd`FFfX|sycNO^M^JXrTTz`RyKq2DfRp340Lo$^#K@e!%3i9q>=4}jdxrlskB>L zAo`_VUlgKhCF)mXDy{b9kbYnUtM_*g2vj5YM;NEHr&YX>=QRja%~HclvZi0eM1c5- zA{07`H-tClwgybgs3-S-3`k}PIz>Oa0=II*$ViZX{9kq8w2b=l8Yixj+%GU4HoE zJiZH>AqQ}i-^{NLNC(ts)C1Ba26MqXnjnc(-SQRsVJmS(dZ8ebyWKd1#dG|naJ;tM zotG)3%j*dZ(9|k(Gup2hdob5h?9zslKVse76XMaq{PmJjwOM21OZD1{paV}@#lEF& zI1Zl*!a`2IMl56OjGM(;!|qw@&oujX+0>GF+I$O#>`tb;PVRo7Z#*}BX&`P{b%eDc z=6y#5NZ&5=FWNUMiG2I*1{xcv{{H~MIW7H+cWHZJy2>6QuX2^reK(8rax&1&dZdrG zEZOBYa!sQ?^;vcP%64o&HOqa@{1&uv{SU6NlAo~o#pSc=R(Crk0-0|1AetS)6EfCA zK{4VY)WoupTu`WCiyN<9RFx=@nVqC*)(4?hw>JbaN{nHteuxs-QBp?lgbA5Mgawj* zQ1hrmEyW8JL^h4GJ|oD(8xq!8UDcINoF4#P$I}aMM+C-^FHVZLA(0@bHf6@b+izQ` zXXBCfN<&i!TcWD9$vd0{P4Wj_qIGTm08o%mfs_d-(Rvl6qeUE7UmtB``jQ96XTmFG zB&*1PCuG#B$(~H)=o0XmFaxD1*lh(wqMHl6|RD$5>?QT zIE(6vAlWc(iwl+AUlLNOEBhuC$n_;63$#^T)R81FY5vNhK4+%$LWlW_fYgTvw1Nlf zgsYcxMuut2y-SHKmdah#gvhzsb<2&;X*Z8~hKw`7*5ccQQ< z1+}*=yJsLwXsmmZ3H?QR+})9PXil{X5w`-^+q-#B;Q}?$Tn7yneoJE0LafgRQ>vo* zP7ppR>VDWD8jTPaWYUXSF@_Y1Mhve42k69l4TZkj0EJ_^zR)_V1hX`w{EI;n?=^K8oo z%q&?}3zS@N+@8w%9<=0Ey0@t7Dcnkez84rE$jjnMnp89(jU!WO3^xqqkOwsIE6zKL z+DIhpM0s8~g|_$x3Eq#?a`K$E?Ov`?z2S5$+tHI;m2b7Bs%^#Kg({Vnf|R8aqIMEb zlo37VAGhG;1CTiE7nck^Yq%~;^pWFAoj+htd(UUx!Ekz2cF(YrZM9OkmY=y()H7DV zE9g|FjKH3gA_>L!Rh90MyHmkbWxGoLz*c6_PnaQb%W4u%dyKkuXT)1d9SIf9;kquq23{|Ixt8%y@>Xx;irq+UgX^$W6YZ797i{E?a zECtIV3pKu^_hlTHjWOb{;W157Qhb1t5DAZM@}ArlJ>IqRhnu%RPS){QYn5_dS&ig3 zYlbs+cFj*phnc9TXZ<{KP7&#tMc2EQYD(Y zTe>|enpe;W-i4=bhoXX1H4#_U4wN*b@|IxOxy$GWBFU}1heFj&Q(}(n_$sPTRPK?` zdnX!*A>|cbE2GM)SslF5)l@X16r>aJ0oDv=rAO1EVbnB`XROckL!@0qMM(sZdPj8^ zRSt@D%xOKph{TNvuXzb_)s9`Sl~lItRk8b>ZBo+R6%9K}dFS0(+LSih0aBDtKm>Kf z@k1GV%>Y8h9Fxh&!BSMeaqXLst=9_dV<4%r;k<`T(@xOwz2@mpZ@X3{_Wf3aL1eeN zY5XBe8tI{mdu;8zvGp18H1P^coK*2i$ni&bDow}~S>G7tui7&_R<{hlvNttvxYL$Y z@&{EivI-tY_M1|Z?Ta|)jzLzMqwSblDD4U+#$Su%U?S&jLst|Jjr4@1ET0a8tP9db zi5DOIYfByg`wtQkrFVv%P1NU2(??w~6 z?%nX)M1DC9J%ftacAP=2blzjBbvPW5%mHi|-kmcK1J4$++Q9Pk$d& zH{Bm?$-rnV{F%o0ujg*xxeTdXf1RiJu2U^quG4l%)r#74HppM&l*Y3VJ})Nes}zHl zM(#X)VM}+2>AR^JtY>dePhP2?ig)?n1_6h)#`u45S2_%0+eoW@xrZq&bypfXB@LyD zdzDo(>d=5c2=z|i-IRn@&)GQnAI#?K?a^IX_uftj`55gzfS-TkQmVQ*_1eotURjo9 zv|)?mwE)}0=DO=p^Ny11kEUfwD^iMz)@GwKtX<0+V`nRL0R@gPd-n^%<#dIHI*Ge$rxz;;Yl_8UsRYxcaB?=pY&^ZmLLm z&`PFy!WPh}nB)rH+yFuNya(9=(G^qz%C?=|L?KeK9JL{7NGdYal%BC9VI-;ROEoC~ z>PZneKoug`)S$F%>R_Zh#amUp{{T;nUYWc`@sQj~S))P}{Y6872H9nX%9OHCkk&PJ zP?pE172&ci~mMAIgRtf|qikg&xO0t8rlzdBh!zlnHO#Gr}kQHzR#V_*rF|&c$ z?cdARhOYS){;5_zE48|)sFyu+(`ZwVWH|$FC^>6o)BVK-IRpfWdW3bCT>a#Zk=n0I!NCke zL8)k*@cdW(6ltYfDVNB5jwL3(_#KnlV zrlny{Qd^lzW8nHc)+4f6{jdl)XGwll_+hQI(uSBjY@^?6i-Zg z&2YY4?e>dXX>zW)#?aE-SEkB6R}$X$7IZ^t-MMP;0GK(Xf#Ruh7p;xD?pBO<8{5Up zux#%W!|Sh99D~R=>lGeDL&$Wr$~HzBu5oB<^2ENShaF_JceuIrNdQhk_-mm%Ie@05 zdzMEfaQ8U0ojpGP0L4KU>W)Uut(BR^Q031lRa@b!rf*o=?=X`LKZO$- zbP;zWXmnKZNY-d~TAlRQ;J#pG-^ zq=1p}iVXHrJ9v*UC)gK5WAzh;jRjZK4psE-?9({8R%dzd9#Q03MPfs@n$Y!QXsB=` z5~a4oiAnrEBVQQs**w6>^?4bfu8Zine5S_7WM*;H3a^o_{OI_n4Cd^@kGI>yh~qRo z8@Jm~97-sv8yhMaZEl_FYj~PBY9>cOBiu2}nA^0=^ANl{5pcsJ+TWBFL>G`&)g$=T zj=Z_)xy6~va_I&1!o2IWqK#J8Dc$Zba+A_yq~XDKh-#-o!;U>Pgz8oA^NHhX4o84d zB-J$G*qy6dLE4j{Gnbc0dC^4cfEQ|E_tUj6r*VMAn(dCoCwp-P*zxZ`YA(~z|p zdZ6x7lQSYtu%sqIa9ZV8`74c=Y`j+7c+1qgW6iXhhivXicM4>6T2zFOjVb=Hh8C;1 zuLILDjzXrb!Rt8w)pv5oRb5!cYxaj5U?kjW(x#hnFKr711rQTCO!S-z;Zn2^MjR>= z9=i>yB7oNsZgC3`(ZyA9Yj}7-d$C#w8Ustez4tt zmTR7Nt#kLJ{JJ}&oECwV0h-yA1gs!2sIcfeL*D?kh zC`9Ngolp*KAHiOPZTO;|Hx@gTt3dvC*TGaUpO8^WQE|bbpW!~y7PUb*DRYMTasDsN zt+%SmQ*_2&S{lmifvRVmsG(Bi;ivhusR*b-NmENY1pz%L4U(>0)&+E}IAzP1?^cv^ zmvpt5KLY0zmwT54bB`lG^HlXL+iFutZEc}0p$4oH5Vn$}kf2BsAVq1pJk$cJN5%lD zDV>XQKQMD{8IE=nWKFf}PB&L{xNyC7H7XgpSJtwMiq~mTRJBaCQjP8*3Y376Bmf|y zKs?+~&5g%JN-23A{94O+#AsMeCFxafGgpwf3TtV0D6su9OvcoyNg-pb#7)+yPw6F$ zHpt#VjNENDOhs81M{-)Js&dM=oodr&RYc1RU!o)wq9rvRy&?xnK~hG-(5g;TV!qff zRkq9RDW|y6IJT-#1k<@H+Dd%VdV}Q=xy~h9K_fXUR6T%CW4kg6&!NA>EoN!+Aa0+KPIML(Yj6xlJb!_QRkP zPMRLEV`Z2(!|p8GzGz{{W&Q zt^WWuR&q1&DAM&s^A7+unbs}glSQ7Ll_}%KSbbIh03x#76D_poYy{MeG=#IIp0w`d4eEDxds$aCw?48m9>fuXSa`coivAKc)@J!9W@~A^Uc7 zx#F&W6J+VO-r1ym(3*O=qFxg{36C%Cne{6kqq{7vVG9|h;P5*Y4Fd{UO>BqYvY&q!}5M~A2fxjJuO z0a}Qfso!Nt9`)@%t~Wh^NW7Iuv`&aQ9c5^B)CZ0$ZO&WoG%IQQZ>qCe?8fFxq=c1t z>lwTa;B>AYE7LQW=QcKwvlUf#{CrcqRw}Acm3-0^CXm0~mz-=11D0Ys^Ckt_{A%hE zQMzj+m(`?{VnIL=f+FqG6~`fXw6t8VTDJ^ybCGnbOWcdI285;w_on}QP*Aj`pGEi z(pOFX&2oR6keC~;Hn4zx*~g%Ha_V1tsJuh{kBz_MRQ$$9)7fdx?Y`(3{1OZY8^jP~_j8-vJ;!A1{1rSV* zhJNVu?odTW#d-EOAOmRa!s&_?u`)!$Oq8vB{ji*>!|NL2p4N(@f{;$2{)4OzLRULQ zGT18ugL6GP7=h8zF|(y-@1^nxliYdb8I1^+#fp#3i)6+>@MGq(y!(jX?qeJNtT2U81mfCI_=V$R*UG4+0SEQ zv=yaPP0BfzJ$tU8w%ST6Dqejl1v*Gi#xAi|GD8(!vbl`Z7k5n&c&i~vM5b_d{X}Yp z28Z1qcKHFkH1S_TwWXNBUj;$=Ta+wZdug~X|25{n1x-bPj=N zG4@ZV9{Taz?wt<|gmjZ>_=AbBTi z1xhXmT;rz73%}oLZq+4W1}H!Z1+;nxbW}kFdjIxGClap@FOuPp3I}>k{?Ki;<{S zkz3Fp>*K65s^q1iD6ory9!Vle8j}hpa)iPax8j_jTd136q1~zjsqoe`HkWi7td;GF zF40fT=hpCq1w55?={UKL^ifv2&B1;-^|Y&6fiEa*m&0V{S{k62om-VDJO}(e@Hf~r4XemN-ZP9gqTf)3%&S8tUzo8~LzHCU_6sTEASQ&T~IH^otLXJ~%zJyIjHocLhuOAp9_` z6-I7K6@RPgX{{EvY8`DUM$t7NbZ!h{l~~y)Jk(L6`OS%fp}8&;%ozdU13K-sQ!+Qx zWS9QX?BUHM1dJ!uw51Te5r2}cs8CWs1EBu^fQ4ntQ_ZWAJzpo$w347w2(gCmtT&8xs}s#K4) zG3Z#oEltg^m!6ehJojJw5Zq<+t-3|Urj@Nfa+x_>=x^L_9pKk&-}%PfdTz^3?Nw#5 zlsuOUOr)(_W}Qrif+Lg5CpdgA)eDRN0A(SXP}w0Z2iN=-T_d&bRIgfvJ*m?pAf+S; z>)>Lrd0}`?i_|~!9x1H^l(5fdJjn3cD19!4`AK$8q#&mA{{R(A0808Q?NjM9**o30VSJ&_%z7C}x0kcq<#q0^ zlG{&EhYDOWOQ>lm&`8IiS#~w0q(iFCVmPmpd&m8P?hCxTt*l?m89xZ1ufKHpUv3{m z{84IZ=gVAwYI2@uYuP(b&PT*0^AWrBR-G8qr-pwp?*9PlpJi86Onn9{P6ijvd}O5{ zl=_0^hs4L9;}DL^Vk6y+A7VUqPnh%k%%9xlXLwJykENGzRJWUt0kMkm#k(AEHGH~S zXxyuyrhRA*23GNDEqa?sI{_pD45UO`S!8Uc6^=HLuzlwTa^R$EFQk$;JX8WmH1Sra z59|}PA5x>_xN98s*-X^Zt(3)EXZ=+saBbYN#sTSpUsB0M-S=`D8Qy-U+@EtV*%oGk7oe+U6O&qy8D&4{0Yq%TSy zT*;odhEJ+65DiP6KJ&UqIk77PeMwh0-=3d$2Rba zJ5Dl>M%cxDtxeT5*)vL1T?tXt_=&`vMc?oIU) z?$(Nws_|Dy(=P%ERRBPjmc0xMdFEbTA@Kq3M&iHL{?534BjzN3>ItKO{Y-nMwB$6j zR_pBrO$}m~{{WZN(jiRD{8I%bBy{tNynmZ{K`jG?75;nuQtda|66QD&;D3VEPBEfF z+({yHmfyZ2w{ED6^Qf#f^4x=NQ3#RPejR+Yf>=p%$gm!D4cVv=tI~p#Afwd#;&ywxltu)5Y{?&fulZlt5as9H#hr8-1%4>INwo?#+*K+>$Hg>_Wfx-< z)#j6TWWn2{YmaMIihap6=_Y>(mqYeK{+86EyXI-#GirNmqC(a^0A)EDPxeC&AR>Rv zWIL4~<`@Pe#a6d+EQ#jn5Xe6++kq)A6QGo5b0#}>>Q>LSWS;%h8!X7?-s0cCyHRGv z)T(+G_aoN|mf<-aBg(S5gCQU6(;G%;PlTNvhv~ z8rlSYhNY*9nppsWFbq4Ym!`i}AT_sYfl2+?jWB`|JB|(K;{O1SIUqr{`@vg~=G}Tn z6uiYdZ23)nvDU(0D{UlhPqJ}4T~yUe8lC^n*r6ta@*1XL^m~ab7{*ow`}^Fe`!E%ZM2hZILwsB_pP$Jt`(> zyrSkR1+7*<1rsU>>YYC5nc+}Q>J;FwZ7R08F+YIo3>(o2F3Uf;4Jt*(q@M*42cD)a z?Zpsde+>}=KY@)9|p2bh0@e$NT zK0p-H#B5tmLvMEK%BrpDT1{IhP)f=ttclPWMxO-chw8ldyhG%Ki1%Bc$6dzbJVlMt zxxkk!hYHACY$mz&)u6OOlkozPFuD6gYcH(qFB{7<1Bb1A^$dB78O!33w!R2^?{=Kk z$QI}9S(hNz-z?SYA2~+ik`khmxCL!UGSV4ogRr)BVqj@g-evL^4W!R53c&EyU*^>5 z089`D@6iF|5V-bhMLS(BffTMddbSMxz)Zy>WuU6jLu<_N2Nk@%lyUIm&fc*vn5X%B zrc{;QZlH-PeQcK4KjyR$BjWEcvc~f$?P>;rewz71ff(!~YA8slRcn^{DVv{g0p&Zs zgB^9MX0@2yg-KtgJtMbpCAiO76<$-^ea+R%*MKRT%ba;_?l-xte#dZi&*gk_$jkW{ zXzf^GOIZjC>XPE5CN%&-xVy*VF>WK~anWM)HX}Ef1M;+Lvs3Dc+jk7`HW`UzoOe}O zk6yCMw`rG!KKiQ~s9~mx*HVWbn$l2Y49twjjBC8U?pw>(IkIX86`t=--nPfg8pEkr zc|zc7o%fZ|BlQUN0>H9>D1mAG-zsZxSeHvsH7phSc-a?za0Y zL&)_Iq`1D^0h zdMip4CNu#n1N%@o=Y=#BM#I zY%%SI`Jd1{vUnZ8A5D~}&vvVyZXKQW@xYfFRap54mrX`@&Gzz9>ZSIa6t>z2l+?nJ zN}malxjVdLJ==7?%S&=aTFo2ytsiyVI?o*yw$69N;qKGyKe1->&xN+7AP2C zr!XtdIn&FIx$`;VKhA9}TIjR>G|R`M$h=8*D*USVN@S8klQmP-BoE#2Yi*X69SEy@4&I)WL^+FttpgK#oH#bFvnJsh=hBMXeNf+!Vb!mELW1sw zil*90+SOV~Ld2$KW@%x2+Ib)IeZ^@aF_!bRY4?v$O75wxMbX3svgg3yncY1|PreM6 zFhaDjlvP-4Zjb$>_KD3HD{56u-;8ed%;LjEr^9b~KayQa4_P59ZN)}r_5#yYxWe6v z)7M{5y=7hI-sLT0>;~uQsDz&}KCzX!jI^&^u$&gA!>l=}QVIs3OnJbMZ9=qbl3hhQ zfI1ZsEn$W=G;5DRQt2b*13jdv+alr0a#JnR_R1ecGhI}#Z)0jw27q-vX9@yrucC6( zAeHJRu8><89D;oGQg$6cs@t6%APQ2QSQ|i@Ga@YQErBYhBY0U6D{3+eweO2Na>V$v z7I_WkJeJ#^%!opNi9u*71xZ@G6z@UoCN>a3%D!~XWos!rp*w!zrg7R@oTkZ2r>rjj z0EfEcRVm$6>xdypd6gL`OJ6yTu<1`K&B4s)RbBg&R;&*Rxzkl#*|XT*qBmYv%T#u` zVX(Ffs-sCih1YaQk(|$rS2jZPRcaySZ8unl&)llb8&2~6Swg3BJjR*2E)u|;d((dx z!d@zDSGZaH6?IY}AQ^yX(k6VQtru@&8(NX+>Hh!~MR@-JGR>{8alA3Q1ajHL7hvZ;(l8BqT_YIjkkFpnN<%YtJ)xGEX35PQSC$`0YyN z=5c&>{{WHP?k~Eh>Z+b~bu|iFmbIyBnNpOgl@${*PmD)(CoU~M^^;p`vR5|Raa%%nR$l66gt)2g4O67* z>xB3xLE>Pzperqr&M*#EomHP>jVNziYe}B)?hRM`r)Tu-+4a`8=*3;x7ZS;N$CpBt z1-C+^nI+VuqH_jBMbLx+MTuy`Q8m9MQVeBs9SjpOo+cgXCxS+NOK8;GqCtt*!rY0=wFc)e2e$-sC zx#X6bQ^#M~PfxmD?Q18zy_exG;}-XBx!u0Kr!k}>Z>#eYkO)eMNYMWPOci--$HHsG zEZou?ZQtdsXSy1`=IC2`bboelhX}X3AKvh<1Xj z?komg3!EiJr_GW!9WveBd&vLK`Ure;ahCA6DUP zTVDX4KNH8!k$mW?XQhv5i)0o7|G?eT~$ zSuD+~MFH_iH*~NX9aM>hkEgx9%XQ5-@3)G)?w=e&QFnUlS6N=#-h8AaG?!Ag!sV{g zo!&wk>p9xu9_si|BQ0|gN;!a60};TxYs+}*-<{(eUxDJez0JV&78`N-7SM`6RFapH zt+H4E^-qULtGW|&^6nM97qOPqq>CZ4-dU|huZOt#%Di!>9<8=9M??NE)loxhVL>Yr z1W2S4;nHyb0HK@;l-A|otrK@KwbWTT0?lv7ID+4CuwQ6x`|~ug%^P<6o5#~z05k~+ zY^gfRB7Co74ONA!ICmn zP}vAf`~+iRyPVz)eiOlOZY-h}-YX_J_W{8>o2tIs@EeO*e1(KKvAfk8-KJ@8?n(#;Ylse+ zp~Oq51i>&)*X*A&+{Xdns+TC*?0JqwSCU~i<}s_yxr%1)ctrkO(zjKnBuF$*ZcxW@Jl_<> z@AzS0nDqJXO(w!zkh%zPxG~8~U*4IO_GtK#%97l1(c=4=y z>w|Yyz;}ChG6&+ud@vR^489qP@T*7W1nvEo2WBD3VW{BcIK(D6ltzpLj&ls3Zh{(j=51Na~|Kb%WT@R&F_D0+mWuY*L3FLsXoVno?ly{AAc@k@=hdJP@8_HAoP5%HhZ3>yFx!o6OVCkU=w}mVce9TI~ zN)hp}C-y(h-Lq8H8f>qJa!huGO%~Btj^!evv9e6PK#SWu6i0$d0Kp87LUby+=Hf6l zZ>QX)CHCvae9vxe-q-o|n=i^A3R8SboKvF~$sHhWQ*^JSu8yV8>VTxr&JOzNVl;Ts z2HQyH{@bG^6=rfvD3;#HrrLQr@g0`i{nlbYZK^fbc`Cc{Lf4vnBrUJ{J> z1ic6aBTV{=bSc6TnJ5o9w2AZzo#Lv-cB4lRs#v6_Gp25GE)n8=k>*{=o#ZAq?pL#RUJ24tS8Nr{ zXK7`sD_e3Dr zx}FP1*fN%78BZ1SH<;7$WvZi*vA%P%ik>^mMH5 zTs&M7-8EmLmv-$oqP`2|FOu4(v*p=cg57jCdtHj*S50QT?b>VU7^qupRBe#k>b4TH zp9#u6PTuUgF;#<()HUinO>*{>@0v`8@A% zCCN;xs}jbsdu6hbWwkn_UG6p253D5oK?CIwCNw8V*7k!WY>tp@Km*&UKeR6w^WpIu zYj~frbBXZaJ^Fsn74-iA+%2}pPhR@y!e-$+f=K8+(exK@-8i#V@K1SK#Mw_S%8{F$ zJq5wHkf4yt=Or0<#=JNYMdf#F)mW+wnu6huge^d+Ns$v80w4%raC#cw_1ET$E@+%RH7*;wUIf&QAO_`mV$#}=^ z6gQ?Dc8TWH97@3HF0(!(&NDbWyNKs$Woowl{{W1zem(tA28$bRcvm1?Z}+Qy8)vGy z+UcrZO+8a!`f6<}DM<=48i@MJ+dFF58fI_~qWs%;Z)9#RoZEI1%I*@L8^-y4k1V!1 znA;w<$ze^U)XgPc!_sk6mdo4RM0zHxb=p0%cWHRjaMC@iMlvNaZ{w?#QCd=}dR4D~ zUxg@~c+NU9asYbQ)jid8*s%nU1@ZyipE_PMOuqS#+UwSfeKYPf`=LaI6s(D9A5X<@ z*y9W0oMnryWiNM%qp7#-C2IJTe-gFUaD{pV4H341dhIUKM%k0pLkLAjC3i`$TMGKf z?(u<2HYX%{iy2wzw0Xo%ZmPwv3BPo(xlc;-$=6^c$aIJ$X-2_utrl)~70Ok1S1K&j z&MCF(01E0!mq8hO`_eR9yhVWIs*89pw@jy(u0sC+cwu7Jf>bx|lBFXNSy`95rA?8_ zvL?`OtZye-X!zRN&4${*V1&uVfh_=+gj-7(W{Rf9V>7CDRaUB~zP-y?S6LI}VzNhQ zubCJLCBOhGDwMhi0I6HLXkKp3QK8f7gzo4k(gdF5HCkI*m+qk3)%vROQ}Xo6B><2} z@r?BII!90~1+)Str`{!qp`@a!p&OkeP0?p(XH8Idm!#;WR#`$Fy>l_y^!i^Kry?yYyrA9tY zvC$IyVN#(Q5beIIF+#jr!icS5aa67^8sB%N)psZCnId0T&UcL-8~V7si=pgr@Bp;7e0{1V|HZ&$t-RosCXM&dT_CSGQ6+7z6D4$_SF z_#TnP4npnLi|8%SuR#bG1q|feLr;)Ynt)`r>fwsZykjLbD%RU1=tha@$Gm#RBHEJK zlGUj_1my%-S_npp8|%|C)k;n%q=0pc84w`^l@e8~i5fuCB??N!%$2W@(hHB}NK~}R z_z+H!&KfR8&`#my5R{=OWLnv)50275L`MCQP<+cAxv&2Kno6&0ia&gLj%(*$kB#{g z^MOD3B~3pu`yl)t;ZDv* zUaj&dn=45F0F(q-4$!);%C>JfaRdHfPbxmgyNa(V4PaF%-{MoZ^_ZQVmv)AKocN~f z>ZjeK>BHR3)nF$RG@4BMgbhB)#PRk8(N%N#?~ZPQs!p_hDSMK+dVZxfB^W(IoR}WX z69>WN&!x%IG(S!yImW%&SI`bEv}PMK+8eeiiIaq5(k_X%=R(}$#@l28J^qDUCql2hy@bl z?*6!{(8XRe+n)qc;`GgrOy$>vHBnhm0sLXK6#x%}$dqETUT2UCVPFAkdw0mE^9S8Z zTznRP9G?9V5;%9m#cYNpW8BiR}AJCI-gcl6F&s z@*X`*)xx%kXtu2ZZPbF5B%q1wFMLrt?nR(&yhKun_Y!g5Q_VR=1|y5)Hk(E6;z*|a z+P%6K3WtXEgQ@TUL`}SS+@UUGYXv~6oO!3Sj&tqym{syp=v<<#PxvbPWnya>Q6P~e z1R(y1u?-|vsZt|>L)UVr07BNf^qpdV30bfOx6|~xJyXgUe0muPTkyQD~Q(BP>LLi z$dc97l>QQ>Bm$(*!Chm~uk#JPYiaW@C&hNr#ks<|EduR5kE$jWfm!HyUyrVjVGp*K z@%q)!HlX7#k5MaaZz&|CgQtg-d&g?B^_Ib}*?hs@SuV_$_0Ez10M)Lvui&nd@ZYR| zCsP|nMQ^(|MOvM%P&nh`zcWGJVRO{rih+s39lqPis%nue(a{rC&M9-#|&x6yl}bJjdXaMsn7z z{{Waz*raECS~$^`{_?G7B=&8c_Q}{sC*d52pAJX1-kZ5N!|pV+E}aijm8qbIR_xQc zB#nGvcsqg%xa5JgBs7|=W!Nq5D{HU=1Dpi{_zf1idRnK@!zpPYL?ERgXb+5|Pszq8ysi}*hlrJ=o2(2F($YuYx{ zTmm=L-0M|6?x?Lwb;T%>wH~_1(b-6$!(+pT%}p2O=WANzJjd>7B)W0w0!xpm9f#}# z_bku)J9op}XEOap@yjP8@@-yQYvc(%NMbN;WC71cc;z$DiPP zqT>2;B{gdLC(W-Wu-LxbGTOzUrE(EOKr&a>`k9 zrncKbMQlSVeY%wzWR(!}jU*>)WNLh4QG1h#m20%Fi_PTn*CU$3K-dTzrlzKgOBnwE zc(b*l96;bauC-1d;VDg85EbtRr>s&s)sH_6g;hi3{#qgNmV3&pe9QIwz_V-hm3(j7 zyKPWe+IVCp`sKUkU-cA_-==9=r~xUFCVKRXY!QwZkn&$tneQR5uGkB%dUe~DzrFwy zKr< z6|M4T%FC|VzBdMUOb8-}ZGP2|4q^{lTyCYM0^&*Z)#4+_r@=Zsm)A`0CeArd%jn;X zpJ}%eOp zBb#+qc1?9l{EJ-6wz`J*1u01~KHy=3v`|??%^+Dv$<8G;uc&`nTEJ2-SjT*{Rler1 zrpYE{BTu?3ZWym(ZFEt^)nP=5MNGsi%R|bV=t%k2RDTFm{{T!>%xdLwK2o&nw(P!w z*4-g>U?@s`Bqio!QHy;Sk#|k2oZ_@Ou3fh~P7=}r(g(9oY+trYuQQgxz@$pPN}_N% zxvj&(U-3k|^-+r#6x6HbD&`jla_v=XrNgZ1RXvW4CvT{fou$=FsaC&acn@@EU2!b|(S4=ZU6#um zVNvlFP89l-qGvLEV;xoLT_DxzdSGks5Z6A5-6h%`N>#Wja`1&x0ES&_i%3ZzZRlq> za8S6_6(IG6qH66WK#6_Ojv6FKQpqycQ`AG6`Yuv08|1EHxm}{Zp=EaDKG>~nYc|TH zWFT2vrBTqjp=nQWR;xWVhQpL&>K=t^8FPN=5S0Ky0C~;`!d$tN0u^^~Tee83RF~Sd z^jFz93b#v0Z`~Z3q~_~rG^JY{X)JNeZGs4 zvV2PzwmgcZWfuxsFc6?fL#}|cr;;X2VY`m(+E~6vX+XU%u#py1!!u&KKGoP#<|W6CRC z(zGqdJb#Gn&+Ct>+aF5QG;j;zlW^zV7kHGJ%H7Lccr%^ykziq`>D+4HO?62pbB)uq z9oP)?KM{u6_WLpxU_A%^NiIJ0xq)Q&eBH!koycai`DN{-&|0h%-yw?FTR#OYF<+?S ziPv)6R;oV0JR@J*UAGQ464q{aRT-Sz*&Cjui##sXty0>aDO8l?hM(t3E+lXwia2yp zjWUx(YRGP?J678cCAAZz)I1^wnlz$qo6jf;wf4>aC%G(Qq^ogh%IAuu(p2l|_r)d@ z^Ia`Bdk$Q67TY&dydhylDp34hUvzpbu{4Sm^Q=yD4MCwsAG%f4f8uHvQlJ1UaYbf8 zNNS>bK+r@QC^Sx%M?BTWrA!!~ZaJSLaW-Gec(b(kJ9ZaVDR1@`_ZsvCG?)Ekpb04< zDxA9Ymq?|&?pR>DY%bl~E88yKntM|_bpwKdMl)206qgaSr}s?fq-dJpLe06<(I|^r zSV9TaL)qgU)F7bbYFo=PYlTIp8n-)Bx{}bxqe6+FfxX3heZZrORId5a+I5 z`1x2ls*Azn8woNz^75$@zKd5M@^%%#9l>7d>*{T_^fkDqK}SfY%G$29yr-K##`pr3 zjplwahwT}pw7!|r_^42T1YAHgpiy-745XOa2v2gunL2_gPK>h0KP{L+9 zsfrL1ebbLqbP^G|^LFGK)1dVG)NB6$w?B|ro(@>}j{=}A{sR6o&2o~de_El_KE5I= zB(g6WlCGo8_y&)&_=KyEciqZ(_De~6s>QK1QMlTu@A&l=JjJ$#?{0j6{Uu^4R6~ue zGanL8(suzd0#Ac3ZiQLecLRgIFe&!_B}kls+J_zYDTUlQ&#~H?tIPIOC48CM{ZL8B zG1pWM!e%x`ZJAjhkjHQ+I6KGN>Frw?F)_^mn#}VaEzl9{{5J4A z15*2XFN6wcB>6_AOZeZ0nwGEif&|sQW{WJfDb5*(2|N0 zq_r(GkQ0;)LXZ>*JM>C+m_$H2Cj~QVN&xDi)(MIw9L@=TikZh0ov11*=rx1mgsQpZ z4cx5^?I*YUhFtC2kg_zc#e(9JU489n(N8uvrKa~wa*39LG&uWxbzWuOJnvCYe?zv%6($;amp8nG3G~Vv>z77@$5?2GV84< zsTfJNppMrHJJ`6#;x>? zO)S#4F)-UG0V$uq#vzk+zp(-qde(w=*NRy?cjG_(x!lIiip<{P?Nz(BtU9et`VQS1 z2~9>o>J$o#R3zsI$)P;H5*tjGX2DN5-e05kSNHUgubw&giycWSU?2!ok~eMpAsU`!17RTvQfeu?qF(;;Ft}o zf~&D^Z{(}pT6n7<`RY4Y7OAw*WjVe)=rc={`;+ptFZ==5R-k1tfv6>AbGc{@6rqfj z#5h#`-%m;?r=yM4&hs}7U@PjI>G_uP?Q&_I$8lQ9&NRsomDJ?-g{_8L zSx)a-oIJ2xB$*PFo!V<2Bjzo>J9j(d90K3@W0K3c{E-Sq?j=innZ>=RcF~w#`O}eX zw3%-zW);h=P^l@YQ*RV}LW+^)2Aa@nt+OY_D=?Qi9IFH|63Ch;6`pp_5rc7-T0VCj zR#*3h-Vo$U+xr%tS+VhN4&+pnt}_8r`x4Dn_;m|=EK1ElBLe4eFMbh%D-R`bHt`9Me% zbkxlN5EM5^kZ0o~7daa_BzP@(U>+P$_WXblSR6fn@G8IM{lIf&?;Nba=yNJuV~%Rx zRb6HwVYgA*Y1aFS{{XgIAh@lv08M=aAc>^Z?yaq6?{{UFrj;*V$pOrAmI{FK{jMP*=LfLI^ywOPo(BigBNqYoSKMNDjH7G(Z z9^|==yr)pKBelNhyG^chxIF&pjau-Gw;`n$J8h-UKpL-3jJN4isH6{3OOaAgJZI+= z8O&YCb2@r}bz3YqaaxES&OS4;aPh|#P~*Lj+c_7R^GEqjdC9D};yh1$Qz=lmUaHEe zsVJ&}_0dqWv8LiRkxr3L{FB{!SjNyda*c_NZMb}fu6GY^%R0M&?JeFZdWCe<4&r%M zMB3|ZoI`ebrCr4YhWBWyTRUpd*;dsJsaj7_8mkFP0x%RM?S7aHrObs`?KdIFc!+|@?(rAbP3=tz-(Ou#mw7S_6~G^ju{?A*X2mDsoLsV+>*~(X{ioWdUVQ0=1{N{6cj|$BjYGX zJwW&yU3NBK9uBG3))4a7O6}?H@%@UHy>aAj0pyF$5tC7>mivp=yF-)Xl@13@EnOp} zw^|N1ND4xdM8WGCnB)&`yA_a@zyt8;wxfr+TaA0Rh$x3(C^bL4=$j~aB1 zs8^`DsrCVoTWcf~`#^!5(-G!w$~N49dT&#&YWy*O;od^7<4gCYQtcI5UAEpBB+}ey zpHfRTER!>}XdsYvNtp19;da-+TsSW??h99o*0k$YyxC?8RgdGAx8Ff6xY|A-H29OG zV=?(QK3WeIqs?I73;>ZqMF@O?UwF$xqaqKoHN8dWFK-1a@cqTSPmr;@l}dwjsH$i@ z=^6wnK_u&@lbmLIvf0-*E@4yIRHCXinMS?jK4_dEPAqgZY&xfqK$EugndF`76+Lb9UDqBej zC)PywrhNz72~{+6ZkZ$%ZX?7-e(N73^Ota1 zt6a1ewt_LbkuaN!mG-pqTY6{K+7EpSjb!5YN~Vzu8?}j_qXwW z$PUTmIbCGh40Z5PK3&ZijxTNX4M?uIDHCpqvI>HT=zODNiR}BUsWzimly^s&7IrX} zwAJf7`4f=tS>5@|y?doCpp? z^6B=*fuf5v=T$3NaQZCWs@!t5X${?2eU)yJ6LGSUq*QktpZ1O(rG43Lra`Xth4Ojz z9K@?_d~cCbvcg%brgpA|>hFRLGsZh*RGN25zENIRe>!W)CwL_{xTJ$i5q9ogY1IE#Z<+pR68wBvhauX zjS`rbD|b>xkdl8f40)KW1<-AWQdSG8$bM&M*Y zB0WQ}?b>DK@EWh2eEG>RVawb;C%kf>0ML1;e$iF+U&=W(Wk;&vetXH;EoDBsnD;9f zTf;^0h5W_iRY`I?St&*MC6XiJoIDcyg{ zDipSDw)BxSrN=d2c zziK@F+NHjSk*^iZR@mEllvCxFm!IE5R<%h9cXCjs_Yv@{XiSEN2kj3sahbOmDX)Rz zpz|BZ9QSG#-8lR;`J1H_Ea!f_;gMNUi)DO`S$4GS4Z6`GrM!vlld5PWtL^I=JATT< z!6#NYnjP#|``-NSE4Vrm&gV12sjpSdG*uMeh%Bu?#7SA4=hs-hE(;+ZE<$!to==|9 zTo$00e;mowX|IGZm2+oc=*?7Qo1$K+_(+uc&Tu(xO4UZ%4OFjoLy)-PN zRb+7r#INPIbUyC^C>B;AUj1}e`-OSKlKAz5NsMGmYTV%kR1SdiAySs$m7NcqqtA2Q zonw8ht=GkRUuXH5nZxOCAp^lr*B)Zztnjj%-c0*+M5I4P!Ss-%m1+n?y2U-eXJhQy zRxnj;Z@kRSWn^fcL&;r2)m}8DvQpu;A#X!TX>{!pKupSt0W+76oP59C5?ng*!PU^O z(Z4M%aqY%1ww0hw9(P5ptR}LpZJ^dtl&XPx1qU|)AbqiA*+y4g0mF*V`MWiwR)Ni@ zHwBjc+V?||ac>OW8~nF&>uJjjJWh!&B42KXJ@dBvP6;kLpz0UPozvfUM%+L~j$bP1 zywzW?7b}fpo8GUeb;YaN;)p1?rr3_^)vAqbmTCPf4F!T^bD(l$kE2(?wLBm$J3K=worD41j%%LhA=rVjE$ zOes5%DibvS0HqjQn`=7uJTU(Nwy&veZ)rHSrkjiCszowF*1#lt(>T>wM@BC+%ea}9 z81C?d<(m=!NKpU}d_jG>(_ubUNEQ;6l@l#Yhn!Nm)mkLczFym;ZO@1#r^YMpXKPV* z&=(z5d%oW4Znb5W6iR2|)AUA8c?^!ATHAPoTAp~8BvMgTR-Nk~5Koj^S=rN5x~;mq zu7ONUHrpU2DH?cvk#j@?-&7i=Qe%pdt)NNHABfAs2PUcHQ51B`asH@>#+=sGSd2@x zPI1lfCwU^lhPhdbvX!j77N1#dwkD+^zN84|#tEfVZn~h^Hn>xR^Im!6dTunsT5YSl ziFL~As)ZD_Ow%SyQv6Jbl!uISYWWU1WX!5*u9VqDJCE7!w7b1G zJt6|oLb@q4h-wbXNF7tEsrLr=HlaC86F+<+Esd&-767>!9XE&^HjYNgvUgLB$UyUn z)qOQbq3O%C13CBz<;ZZ7dPF@{Crt`~K#_?Y>ID)M4Ghmu4>*gTRFz(W0i3mn($#bc zQ9X725UCK#>!fnzVxPu(M<)WD6rFw2pX!2BQR6GuBZoNhVJO zqCf&a$rcDMqB0XX5Ix2kp1%ToQ9rg&v{CgJa@SKoWGxqU7ij@X8c6xaF6tuzAv&Z; z`NAk_his}~ct-`n8x9Z*v@(<`av2Y)1R_(Maz)~mxXu+S^&NV3=wTNr9~sh0>!dzC zG*LP9Qk^=qM~t+I%2qn1A424*9-a{a8mI9|nF`3W)X!Oi`UFP(l-BQ~mOXP(CnBg+ zx~);=`LCUNRxH(uL+$xH?v&|03X}UF>>lA(XBL2R%K;c0iyHp`5AOJ5d3Y%j#oGS> zbz@D`6Md*mrjqF={b=%AhgL=E5Ig#7>Z9p%oAK3;g%aB|2!62*sa1#wOw#BQB$9gd zkFUI@d@r@FI4{r7_7Zu8*C{+!8TV(D)8>87auw#5n3`o*sVyK62IQG(?vE|*xC`>G z(@L+Pyt8SCx0%d!A#Lx{`FL%8x%*w(-qW>(1cuy7LHs&OaqhTlHk%;T@ZXy#lsFv;ZwarrE(M89!OixJ_p0S#Lnvk&5m27SQ0M;9ain?#P zHiy=Gnktq}zZAC9C!pLGqciRzByXB5c+rTNf7XUK=9v4xibSsZwD&bur8!4SZ)s{0 zN%R(F{{Su}f0$BJ;b5v{U+c~=eDb+&4`4j0&b;fum)y55$OAV^vTBmsO{Zd7Yhge# z1MQ908v0wPIi4s=`J>Ei6|N^G(twi2mGjHq2Nm9UhbGBhxZj(&RXbGsuc$02R4YPs8bI0Qsb406vai=e|0YdKV%6YHAmaDQAxGge<8Vu zF=x779dd~%DQ%UKSFrd`tfC&7Q8}O$3Vf|stb@IO!&c;(kU^Zm2hZCOmJa`LSuj zAH7?}{+Yv1itXR~fBoei*8c#!P2Ocf)TVK~a+N&-4J}H+*UQup!REsT(FJE8`fZ9I zHN(%?2k%WvulIpF?XF7OqN}mm{X?ZocK1|KuZHKP9DLF*6cIPvkB9w1z{GisvYK$_ z1NSYA;9l=JtGi#Owr|KB>6JH;%KyAMelc^_I^=`~9n;j(d z0nP+3mi*Z5OKeAaTiV-~bcZ3-*HQsI2Nm(H`4wG5t~$jk-b%?kk|)&1&9mdBbIAjW z{WTd_n9v+(o>+@n)(w;6S-vpJsIU07Z8gHhTv8S2)lxc_+FXu?N=PyyMhwT>+`{1Y zgHWmRc`SX@E`mpVE)+h(tS@k!>)cm%w=8n+%QZ|_*5g#J(b|(sPx7oG4?F6uMX*#i zsEthG`!#)$$k04&u>GNg?e5&?7P<$Tn$VwqistkR@-aW8rqlC+QD+bWbn9H z1(ZfY`qxU(5BQaO?LSD5rJc5$)~N0(w)FdH==Nu4X+1nXS+>u&tV& z&yMIi2!Q_p>{Ug+(mtNNQS}Jpye*u<)tvU@SH~CopC&ze$dg?`OOmGK!#4JnQiqZd zwKP_wpekNsWJ6nyEuMgUH1S@eKYiuydqL8X$$iyeXbqv|qq(-z`F(2lf3ts~WtO4U zTATFGf^bf=+U3u&u03R-r zNu&!V`u(-c<|RcabH;r8?h|np5A^ZfeF@Iqa72pUt(me zxrT~Pt>sEbX@Ud5kUDM}K>_qsO~#ORO=?a-r)lls?WTID5%2JV*&qqiSQiQ>aOIXl z3O;C^1l2$C#QjF9@6hUYO{(5Nq^o%@3W=(If1)}$f;05O28sP)^85cI(&WJ})_lDTed zEyxNOxKx#TiW-41k?(;nRmHf~F>X!*qaiteL}%q!R^cHulB$+KJxQPIitu$-ax>vq zn}aA#N-4q#QBMdp3Ya2QI^%xeLxucd6EDm%5>0V7z_wZW^(t}ERrbh;*3{VnoEO+K0k?3^j5#m2Fa1HQTJbd3=JYgYpz8Uxrkd< zlG4(XA=Hluy0&V`WasA5P8RUAB?M1dPwb0!0z;C=!^ajn>yzxPsZIRlQju?_I_L=X z93$NxE9ag}MUD}JtB?FwxBmdHy}H?R9YQo{Uqjv5U(KhDDku`>#ok>*J4cEE+^^Li zJb8I)B)sN47u23@WI9%yzAGwlOIOe4)GH z&}8er^G6|8CF!&-RSmTxKZSxCAM;~ng^04x1FQ& zh3APg+l4g+t=yj3no+8Pw-Rk?A2m=8<`$Id3eZUdO+g|v9n*u3<}mVT=9<-Mdv`xA zvx^(6UBNW@EpA=1yTyCV5f zRJT@VB&8#%K2XVr!n!bdsJ4Hz4kEVYyqj#Xa&P#H8;n%BNsd1IiA(iNCvp&^0yOHW zc*;CW%zTX2mVHQcHD2@N{sP|i{Ks>ynped%JCVljIHxOTxXZLAfj=@Gp{7bbG#W*X z%&7nX3UZyS>Ele=+v%-O8HNb*`wk zWLQnct8$S=SxEX@s8LFF2~%JtQa&a#-Q}7X{ET;CR1FC6S`NYOgOtZfX6+8(_$mX; zA4!g7vDH+$XyhC(A$5SCR*^T<($oTV)fEUGd^L?uJIy$*bm=%()Tr{_PDyMunWX!F z;;B{t0O{Y|6(M)!aB5r2Oci>sQD1D&pi8E|FrKk?JDL}rh*=kQbn*URe$@@$J1Fix zm(^+BDz8(`PjgJWM*jdo=F`d&_Q8zHMN}W?)7F&x!e8Oe+55@ku1UpnEGq=cFw32S zx|*d|$}*N6O)GD{7*8b^xa=~)!qTOGOcgcdGba|sSR0f%xvm0r}Cf=HdR6fosE1Mw-Augts*IQ8@ zOF$_mtHCs7>U<+VE$m^jBeI{yGG zSD)&T!u9rxqsLqwh|_add-?l( ze~VI}J#6DfQWJW*sXr212@WY^e71xo8@-||ESuDI7#0IN-IGe)4FZj>-NwD#=O$yk zJ;ZqPd|AVGSCpJ{7oqeRsA)YdHlMQ$k~W^(}eldNf*5MW+;a`|3r^{4!A$M$yW z@!Dz|qi#~-C<7m;d1VN4uqSC!*QnZ{bey${i^-}Hmax}UbU;^D9iF{l-FN5uOz9+ zZFdDUm8zLE$U{hJPOT+GBWOwypc3f?NVGOEXbIAYmucJwlymPYs;1#}U6T#A(pF#j zk0we(ZCi0$Qd3iKsCh?XQdWe9n(-@GmqQJ@ov}CDqBYkg?&Sw<{hptX1uF8#BU(Af zk`Z}L60jaEu?lJ&Clh5L3kESrLK6U7kCYh%S}gn9)_1Fiv(wW>RkONACoYi%Wzvq;zQ+65?>3Ls5K#FGO(rOQ`k2#LC;buFg4;a5*b zNl3b-Ryc;3Wu&Q3I_rxFN)VLHl_;42I+$~!B_!8Djj2~d1G4eQ1LssYpCMfuu<~8k zZ5hvBrZ-zA1no^ZBBr91l_hmGC@ug<5@gJfWvR`8Qma3EE2b4`&>%7!&STm)DWfLD zc~Xk+$u`u~xQzaZY+Wg;7_X-+xbsfcA!%Q(+lFCDnrA7_C7Gekq1jQw3#vG;X$~ib z+G#m=lj&H>IZpIkUJJwTw(DuNmOd2S@2#vPoyJCO%SQv6CXN)jjWDMSD@ zBt@J}WNa^aBc#pb^(1%_F?p^<31CyH^?2`*4r-`g$e~qF-KoOv(PpK z$ErzB%q$;2fF6MLk2lWuwZ#7b)3XGF#3-?Su>dA)gMg;AQr9j0PqAw)w(ZB9F>Ger zQ*JKMT5d~IY!-t6p(TbSG?IP~JtAyZmZ8oQT)UW_3At}Io+Id4x0SpK#{OF8K3nD+ z#!+wl@w-nZW2}nAQsgRKB}GLWhzcrYbtu?UbkGfEB_5M$VI8|USO6ZS=dUweznW4@ zy=uPDadnF)sN{t64>Du?WldL%F?Hi>s#$jA)7c6(`u6u*r7CirD%5rOlmXLG0^a1M zj-591SASB_SmPoM)znJ3PyYZ5--~mg)^Byl`;5RUalufFf+*{8m9dnq-5U5GreDj1 zbSeq}^_)R<9qjC&qYZt&o|~cIF)*6c{h#ngtzGe87%vv%9COFLu;SFY4l8nIvYgqcn9+ln z`nB};E7OSByOr(>l`;C9{{Vq5xaV(~mCN%x_CIgKz%0|yQg339<{qU=i!Q#}LWq$w zI>_ll`^z}nK_g8(O1!tlWCzT`KE(rPoNaE+Pcb>3b|-M-e4Q0eF~!st{A%SxPQ@ae z`FeV*i_}O{QVE-@Uta#o2t1l8pA?WU*-Lid*~9h|YbszTsi z@Ocx0IST;1WiP9daUMFWeyWFyZdFTifx5SlP%0_aCG2quGf9yE>nNhX!nS4*J`#9^ zcV~Y1WDee&FQcv`<}MZQ{{R!SawQ$MnR)&*bomGW06J!g`<^=Mge)rDKN(Dv6qBG_ zwVz1y`D~`f6UsgX={C2KWatmivY~y^c4KWmBjTEM5;361XPD@l{HN|z`5asA(WATZIlIkL63IC}n>! zOLL}3Lu;q>LVYy2fv7T`r^+v`?SO4UD+!nCXmm@KoTCcLcxxQ4Xndc9_dd4Sh><1hw|2&okI~=KX=86zsqSssTTpdX7@^dm z+G3?PwV&@2N>q|H1So0<>I_{-M2(^b5Li{SkT=Q>rTZ{;d$9Ojy3C8Q>g zHnmfPFb3y=Nfb?H0S9F^?s?bp`3f8?C{VAXYq-kA(&K zZwQ{dQa~|K=8%n!gzAW6_UBu2nb#@Rc)7gdQ*W{a`i18dblf#2Wb}zxYMXP)28w%S zH25ytrbSZDL7py3qQ*Cet@c!;N^Yh=Ez#hSpo<<_1yjsQjVi9yIYwID+bqZ)N-G^7 zJ#q*yDTe<5^lgPFL7)i)X%UHeQGx)T)fdY(W;&a7wvC&9OGKryQFBJ^mntd$01ZJW z@V`IB<`1$CW~!#!(Hf&W!p&Qr7EdEcAc4K=(uG$)wca<%=6lR%q~2g##XB4 zEAO^yYC;uLaj9AzQXd6M+~QkOmM1wXE}*=tLavlGr14arx7_CG#yQ=WAfyU%oVt|1 zNxn#`rzheQ8Xsr2E#M*IiyfR}Y0MYZFBQ2@kYYGjA=4~A-5QeJ1z1+%npadEEi3%R zOo$pC9$DhE*=K^mB|pEl zmDHJyOls}O8V!~neq!!pxl}7R#C~jvWi;9Or+T;OLplKcf zM!?r_R*Q_NSQ!NcB~+D4^N0tb{6tDoMVEQxD{q8g=L>Lnmph@falKYMUho$zd@ZUr z6n-VLu{PvAlazeboWQ)^bsMN9eRbQN;}_}u2mHZDc6T0n_=oD0SG|7;GxO;hS#*m< zi?nl{aZ}>0J|R$=YSc5jNT^JLd?(g08JM}4y_2=4Y;UKElb&ck!>p)HFC|GM-X$40 zpUU7@qfZEYWGu7iD_8R7TT@O3#vf0p!SfR-KW>rq&t_f;t%2gc67xeI>s$9%VylH^ zXxyW|O6p^D&;VXTa`c$HkZVLaGJ1&!C9#wcxT1PscZH()MiQgFP9z0;$nuK()tGg; z9;I#Dm!-FDKDB~hQfyLzJdMo!eN@)^8vC6>*-1#+gx0i*z@hf6$HV!?bZ?KfexUZ0 z-&2ce^)HZ&Q;sqRyQ0SN%0Rk$yEaEfQpSJ+iW>InB(SN1g$CrHbRd!A8e5Cf>s3z` zmnG7E{Y?97M-%f^-<7cIV|4sm%ymy(X=rI$C}Pcdclecbgbfmh7rEA@rm7wSWOmj; zvao}=Q|0Y>TnJgN(Fz)!BT5w;v|pF_PqH~V12~VUmW!mg5=iQhd?GrfEmEHj;@2#% zD|PxCQ}fvGwTV?lz(zUEy9fo1!*Eftlp=WuCp zpsM>n`bYLVf9+3&vQ9_j`(kZ;zs_zoTY0sp0{yDYz*6X_Tq1H<-lrTBGE#_{lzaaG zYG93|WS^L?g}mtR=sP z+7s*%@+Y0pOfq#fp4f0fN~}&NaQKjty-a`*Io(z( zZ^!qbqRVIZxXI7i}AibPl4n7uoCUhIQKF+NhiA&26?d+bdPnOeMOXQH_fU0YNHA zpM(YFz0|L`kP}d~erQ@pA4YgOG&|?Xc{{H~ly}t?n0L1$i+;*}`IG%(9{{Xz{{{Wg}O<;Zn=2?&Ltm^Ld;@(uH z6Z_g}173|5j|lx02fE)GV{i@vMCUHE5u2XmG%aa@VOM8Ce_oCK-{v)oM#%f2o z?pmv8gIbg^d~B)2RJw_%nWhZ3vaYH-o2qCO%Sg&;+e&3GIJRp5RL*r!>QG2J%pRs5 zngvm1E@o5sM@VTzjnO5aXI}_~`>8~Ttx`&clb4LNiGot#R=)7p)F%hyjuN+EwOH*{ zUPo-9G_BJ#*h>zbg&{?KJavW^4r;1LM)Fk>;V$4W+!M^`<}7-Fs+Zh$ujlNmBsEb@ zR_67#c8R816rTu^KpnCrk+=l-?*qFl_jVWTTIF)Gqa3wjOuF5!)`j}23w(mLtz-$E zBmjZAIZ=@}RY|T*gf_WrF(LO(8bUR$qKLsJ;(YfA5x+7rO_R-y5{((@?u8Bc`z8YNF|`(t+dmLMch2_tO!}i-27vJ^*VR zqm>L1BQfF8G0G$2jW*es^F9&Fnkbx9C^`e75JLivO_H@!Kf@#+h>4FVBu0m9qNtU% zqN$ot|nx-Nob2%O`RF62)^IOWTriTfj$BM3p58+7u8 zT%;UuNJw}}9L>=0-AT-9kU!zl2wc}uD0qVry|pTEQYJ>D`8`sn)EN&g3=aL1q4}0V zb2$G1+JW!$)gEV=kKt|fqri!t7G%z)xMjNsBKgAck9#`^na(ow&0m5&sF1B zALlQzZ$!JLO)226&j)7RsB!)6yS)Zd)wwcKCfZlCX&xI>8g^Xc!zmaG$nqG@MP|}&}^QaM0uueD`O$#UuSk5_DAVJx%riIyjhy`9`xEKK+bx` zCm~?hFt1m}*^!v*UYR_c9W7*+nZAr?bTHHdv*rszZ_iN0eoBC{kYcmAY{6csMH*LZ~+TYoRXi!Wff9|sesjUM_9Ub zrEMxl&LnG;GsDa%pU6>R)VwW?MPBcnbbz7x3Q7WaMnL! zR&Tqzf=qTUd$tZ^3wf!)>IF2RA6M3P!AxWfzw9WG z1bf7OU0JoYdQq}JU~&DVsly!l9ep+IOnR01Mb{$N_=>B@P}ZL6 zP}*aMXxfAQuf0*r{=xSojn(x30PP=wznov$$7g&)#Lw{_5md@}+UB~au6Fv1m&{YC zRWkLry6Vh=yhP(kVKvRIxs8kgi_3Fa*X8me;&+!%r`@86>b{>}O#5<+O2si7T{1+< ze>S9KBz2$n;J{+)DtwKq^b>oZkn=i8PrY!i#D12GVF*=LB}+dMkW!%i{{S-)9E(Bz z+kWIzZ8L8b=YQUbp3VM})->*@$#E)h-&HFlrUHNaoWom|dwy0ldz2DlvkyNL-hJv+ z?ca(1msr;};MCkf$yl4b%D3MbJ&ua?PgQh!p)`&-;%V9oB}(*y$(QcW!YlI~#W;(U z#@ZezORmkxIx{1l^3k}d`_%-`zMGsU+`oFA+23;Pv|}m8E|#Y))aCYDT}zdee!Hu2 z4ixANwvbPhX{@A%I2k0ZtDF^Fo%@=#w)$&BMuK&vcm*uC^oQ-5iGbZ-D>_>O z8&y-(;>Gd~Fi?WZwxw#RT=z_wY2z77yNI7K4Q~7wt62Goo*3uc$|@o);X$oO4^nzP z^e^BNr_oc+clOw0M0ze+The_9a-CYrF2060kCd#}{{YM`Mmv)mjNzJ|ERGU~B8q`2(IIl)=h{h;j}PyQ z()syovir-)TsPBWgNW73IEslg>Ftaf_IDr(iS|OBk}kr>j*-ikamf+DdJzpwMp6R0 znL>b&lsFUj`9sqJQfz{R>(i_^ibQ>>NCVfy?}E}Vpw86IUP;;oyi~kYi+weFC~TR4 zge^fS9>5~X-IX;|7tWwmU$-TiUR$lCNC??b>UNHw-2t}+zC#&2awTA z*v=}pY0`jzKda*!*lqL$dDY%fyG10@5+q=AA0*Q{cOi_TE1d6C+=5=ybASJ)Z_MP%`G9z&-CR9-Z;qJsBdb#Xgdv{6-5J1`_RHc0Cpsg#2aRL8_qGvov?L`twCfkxbsUH<@^SX0m+}&ZZ-L1FFJ@ioBEf1oif~_V>ZPv(A zR1A)yI%mf#3r@-iEGC9P@xfSrVD8%y8!^yPPV9!g!? zPPW6YFzVV+2hvz18Ijk{K4k3<>p1z0ZTOl_i|b!8I}i038#xqk8myw?9$@2+BJR4u zkK3|@WVmJ2zhk}SyGs;m7dFz;RFI`ip$~+Dqok%rk?kGNj+*xo)h(mOwDvEW`#&KS zj%3AOh1ykTA9p-yeBusm=j=j4m`Lqu5nTsHmxFwFZ{9cL_KWr6dvUk&S!o;UAPNFu9xg ziez0KxVp6|s_KS`>ZCsMXW=arA_*aka0+NHrazeb(VWiz0Dr)EUCeD)i-k5#%udnW zRQf9_5bY*CYN>POejvT+Rb%NZ5+D}PC)7ntVu|t{1p?8{cQkNNxYYX8Nx_+~H{bbl zjqogUlJB;+Gh6kl*7L&YQd9EP6^KniSFE)pgtNU`j|d+n9Bhb?D5t_cStbps-}s-n z6?ks_>EsvAMdc>3>qW7wTl4FM7AUIQdv3m)RkdoRZEJgRB&;*Oe%Pgo!m$eORprg` z5)d{9p6}v|@Q-%2+@H%>hZ z45lf}@;{?WWTq}p|_Kbgt*tgTvxmR(|awk-#(Cf7e+FGTjExJdiuI)~eifJ*$^9I*W zDtx7dy`{T%GCIB#ro&zJIvq6i@WXy!=N?Jr?4G|RY6_c!ODjzy ze+ra`0!)I8$<`y2WlfQAF41dUj@5C-aOP2LXB*8>(JHN{MSrB_Nqb$%GtokN2&kNM zDIkb}RhM1<=eJc;;@!4cE=$>%y-9V->~&5w5be#?r4*FK@<+lJCTFKZ2H8bMRY3`K zlGD@FsMm4z*yanA<6KS0d5ib#0?TZmPEm?nsVP#;4gEyDTk_7eJ;@}2x&XgGK)-s* zAdWW@YOk&*4FC=b(d5?acW-^DSn=xx;Z|(lyAw@zD+Nx|e(6uhxK(e_MrmCQ+ z*Ap*@Xl4XM1~szHS09&eYGkq&L;nEPzq7;B#H{tsvRYcZV5Fm@MH}^N3%ERx0-j1{ z5P{@8OkT{vO%rj>rlmyonkTF3nPN^XI-rz00Te(07Nxck;E7%iZ<)SfY_Y60jwl{5lbWaMM}FJsP==&eaT); z!rx0#V&g1&p`)K?rkFh4zK?hD3uq*7SV$xR)2a5_$mT-Ixco<8ywgA2MepguuZbKN z({?{)?X&6WjN*0tZ;hP3)t%e^VZ7;w*s8g#hQyuTc8^U&fhi>~{{RbG zX+@ckL5y&gyzeVB`Tqd9EYp!PMlN|Cdb|B5$@iwc3CVF;4Wx=N88D60dN9 ziz==;IduYx4|(g3LipMBeH02gf*Kh}rmzv3QipFUwDp?8jO6l#WbuOQWSvscAvNAV);RJ5v zg{D_}zZG`|8d)1z2#k97_g8PWzNKba#6Ft+uyJLZm@YhFl=r)6yIpH>+h-)*65cR(7nAa#>MA0K1dHl}B~ zM|#ihS5kL<L{*xTR*e*HbvP+jlp$=~hSPdWbpw-0%uW#w*5RIJ(B2?T2)@@7NW zB2n9T#%&NwCR+MoLm?kv{otjn3+pN0)&%mLcb1#Y+HoZ{6nQ0{-*REZljHKJq;hi8 zMS%dz+SU^8-d_qDA|lG&!K+*xePi&Vxm;`Y6zrq8s+wGXy1w7(xF=SPWybRv#6#oXsDV-k-%S^AE>E`@BlI z*=Gsz_ZZ+e9H)jZ77prrILZQEz2nw~ny+ctP$d@D9pP;mK?G?zi5+6P^2xHWvExFtP)g`rCXCU+eQIzod$>n)d`(}= zb@_fh!S7=h-;(9)#lcLyb+?44*DX>h@PJxW%965`b&wmnc_vf0&7;Fu9O|7Yy=N8N zv0lf|Aq^mEvva<_+PIq2WyQHujUK4Wc+Zwn^VJv2eN`<@B{fq20E!k@NU{e108pKZ z)DS`P8b0048G(uVIjXZgi27$XXvf0kj$B#D+_T#+IaUrkz1nelJczZA=QY^_4prF& zj^OeTs>hjWXi+riaZU|%*F(}QF5#NWz&|sbL0zuF<2~2TJqi{y;oRobT^A_JFQ2Ai;b~P;y6z*=Z*{F_Hq7G#xE*k zp16+Oq?~^jvRn}3v{^N@R4h8x8?PlyR5r4tFs($9si!RHNy_7=55Z5!UOAh;5vH5# z_U-=w0!$H>TX4Q87Rz1Ov8P$>uVetmC7%Viy7q=iyjRJp0oObIRXAW8Ui&{)Rp z5TKVD6$rUmTU1#RUmbwXaX zP*zmbQM*J*TTUU$N|X>r^e8DMBu`L8;%MXww&FnjmFTDCEB^p2QaOxw0iIG-V-*Rg z7aD6`HEm8v_05(gO6#{2Gr!X-NK1oHg!o06*HD6pQ$nL$!aBHIEw_@c zBOkk2sq0ZkR`JM*woaYum8G=4RQTGb4%wYzJcx5yQNc%S(aoxK_YJ_dzB9lqxfQ>J z^1c(P!m_)TA9b7j8);skNnrD?)rGAiRW)isg^-ScqXWS-wBz zTXV(vv-h3}v|F85sZncTbg#Cr#Ggyy1MZAwOAgqweEbfpQH$;;&xh$B%c8Dz4hnmT zQ>_vLz9#y}PCXJP^GBlb%bSqdbsSQrS9q}3Qd`=!TE0r9hW6Gyq}*4D(oq&R+F0C6 z%~LX&2xVvt6j9MZaI5!plv#6XshGk_kMjFLdYyY)E&>Trmy}Htg;!lcrB!3T<6p)k zAcRj*`eMpC6>DOls%X_=h+9ImGztO!CW3JxkR@!+2^yx%%PHK1%cO)rLbkTJ<_bvT z%0{jh+@{RxLWNB+TR-~7z93?z{{Tk?u*DyMsV@t4#v8WQ4IBVXK(s%KY^t3m zsoRR&jUC#Gqg|~nDxbK{BfE$`G72un^QRzpg38~k4Y5|YO)nBEaMrXz5Bsalfm9vVmgUc4~Nv5In@7vNJbu$3TU1ic@ zePa}ETwSmw3==ek)gY$TQx%kF(|wZ5lai95BPfzH&9W)1 z1%03Y0K3Q7#fup2!z1qVlX0f584onvmrupC)zs=(rpN40sex98-$(&f6(Pl;U>Tt( zBhEd)%d0+1b1T0w)7ks$zuLYr{{UpWozvbn$}>R__^JHIQa;TEJVMC$XxdwKBUFW?%qmBrmXT4Iz}jE8mS%%QRvSH( z!@r)@<*LAPM?K~|!YLokuj;<8jVI+P)KD64cBV*302AVR#8^Jvv#_s0peml~?!2a9 z^}Bg}JXK)ITDMgR{JIoQgh)tfDAJ3PR!xw&C^GAc(bc-9!)Xdx^4Ii44v>Uh>*|9hBuD|GqYStHbCN#&cp$!ma~ak`m1bp;d#|GtXBfTN>sV) z)5by`Rs6Q~YM*h7#JwdY!=$D6L{(Kr*wtAKHzi#oj4Z1}kw3m5nl50P%^b9=fpS|m zeU{>Q&E5Y1Mv}c@52PwV$q9u0vXptJc}Xq3o1|vpdta57@LuH^6u&7eYizg%9b=|t zi)J}VOqS9BP-O)~N?MW$G4ciHa9zssT=KtD@ZFh>w}b{p)ZqM@(geRPr5||e-l`~P_b77YAYgACPF>7;PVyda0;*~7v_?nk8#8#%%XE&LqrAT{{Z}<@DEg~UOo{TDXyVdGu(g0%CzbpH3Ls_ zqXm_Z`u_lG_RJ^$02Az6SK9vc@nsTrnHB_$zq1;fKaqKUXn$R2-*}eim34Xf0kucs zCSDL8MTE0epzXITlFBZwv0A!)WW(_&(0`IPuHa2n4xKQsU(zup2Ce& zkP{G3AgTySQo1y19aAa)0K?7|N*qGocLZf_|sV%9p>ze9Ittmt2MKV$bW3ILkV(`&XjkPr#&3-vsXL4>hCp!Pr$@o!w}6D>Q$Q zT+`wVnHhEXV`qFT3dcZyskV#b(khTtnz}ax1#t5{fuxowe_sB=L%2F`#(LzQ8hD(3pv zqW0y>VC}X&$4V8beZFvJAChCN)n8dk9cd|>YWPZor>EVleG7rWN6-}-uZ29r^9J@S z;>g|bqq(nZ_{Ck?tK=N?X$k%tN$5Z0gXZlvNx7l}5$6@`5zy*W0q)drwq9@6s`!*f z>Cf%!1CGVEuIoTN^vM?J6`jO8J>VztN}B7-)c*iBlq>ja>SPY0QDgoUkLi%$yt@0& z&gqi$1~|2~SO?DhFOoCLxm6w!h}YaP+Ip3u*!xt^EGe{^Oc0q!$H?8~*;D;%x-2}k zU#b39`HN(5u{!EziD|7!ObR{<<%N)G`10toe5l_)sWFysXJ3su7T!-WaRmPWb*Jr5 zD{pID`F7g6UNO8YdDHw0+Wv@pJ2Lp4TC1k>I}p$Lfcq-Gg+87fLCM&!0p!@mXMkKS z6^^E*nnk$5LyjTj6QG$sK2h!+r)4BK=S1GYFyIS(vS2@i*r0%}!^7qUfR!-H8 zf3NE~ag{%QFJq$i|WoqQx zOX^O5X%*_nkv*~6{{UHSvUDVUohKHS@8;2GahzXEby9q!umZ&~g%I7VXCOD~}O3u(q^5~Qdeg(Mihw8Y&^{Dt`n66b1B$BF|ZcRwyw`yi?C674WTi@gyK5xBw zW82!$%gTBq{{XZR?*OR_8v1R0Tdz(s;O)banqg$=TVG2~3RTp~qUMvYx+8|-*(h>HFG8t=i=6B(&sony-sHmLV5&{ZtXsAVC^X8hVyDG5+uGcMmC|?;P7ID%u-d zsCwf<=ILp;DPWn#ek)5BH(53Iuc3#ZcSM%7I5*W%`xB)f?)rC@(o^#IPBw(gK&!PN zGCq45RrTPJPjM5TSVa8c=_nQ|)bnDeL&_r`%sjJmx z&zka{49&5^M))%oZHoMf;WEBit3hJ0*rpsGx+{7N z2;4NExYl7WTFn!~nq+M}LDU^~@b&9ON`A7PtM_Nk9sF`t+kyCh zDbMlyT^+4A%Z-GDQnOIDCX~9RR8xg6NSub^Bt4N&lCrv<7>s1om1{hm?#|t_$H>dc zVq~1#JXDcR6l=e7%+~&&{{T|;{{R)f+xY^ef&Tz%iF`=AzKa)XKYuS1u= zdmM_oXaWBK=s&rNR_(j#!}TD!^QJRyx$!1T#K_3luWpBuZxzOtj-SwAzO*g+2kIS3 zT2myXqylt?Vy!YQ$c~^Pl=--OJZtt-)~I~K^B1%mfODXhIY=YmYe=uXpnlb=6`x9O z_{+~yS947p&y5tc-tTJ))B4UdC-+CQTd}TXdW+-9Jln!C+FJe1{1Zw40HQ~B8M{{m zU3-MI*d9Y`T?$uGdaW+H1i}ga5l-SIF*vEq+CQi8zP|OX?Vmp|ES;@3pKTgH{gswg zj2WFIO!bdCY#M65z+s|6c?No(z7gFz6sBZJ#=0LEW0*>xs8L0*2$H(^7;f)oi%Di8@ zA4e6md~`}J)a4`hM!#fOJ9Xppzfy~Hc3g1oJjt(NTOOpNeMoXs0EUU_4xizS(MyDY z&34B6*TP>aWY6WCgP7l6d)IpBajbPqoXnK^rTQc0-r}FBa7z6!?OP!QiyP_+J)D6A z&VCU=RiRx&lUHd-K0P{!M2Oo#D&095M^zz1paHaaP7yU5M_!QTrnwqO5(mf59GZ-^ z(EH)>Y?LESIHaf<%nt~h1kx7$_DxpOyHl)cvZK%+)_aUu+c4E;@&i>$cp1ba znO{;2!mAyUy#D})Fabz3MnFKP%MEC@zWcc_*GP#W#siJ$% zlu~v##F=&V*1s*VRaciaK|>26Aaye$U#0_oMkciDvOe1vJfvv8wEmwQTT{fntnuxR z;Yw)lH(^98|<~?M(Aw>530HEUzTE0{tNi>#fq&EKmiWat%H2dQA z>mWtr0P$HNjHQc-@LxIE<+|NtHI+_2YKj2?B|BzEQy$3c_i}~g&|_MuUPoPHtE+WS z+g^FsiT)yA+6UPU{YmQ4xc2Z(bkf->YH6vdt<^Jd{Afd=YtT=j5fRF~A{&osQ2ziW z+U##5-fLm0nO;baBrfk%`MNrc}yyBU3BzXcbe` zzKe=kLk_EQ9GboYr1cXc!n=>k{{W?Hrh|r{FLLd#I3Ih<1;F`*3E-)3eO;&A`*-AF zam4ALe~WU3sr*8@JDqNhXL{(C=Yei>Y?G=b7gt2Z$NLAaG_&uc9xnXH3`HK8y;#Y0DI3x;|IUh1VE$J53aY)#Y?I=YqPUtXGiC1{bE}^N|D6^CDy5395 zZdp6Fq|z#>NN&|=N+i`4wv_>jYGeWXt+bSnX_ zGZeHpToS`~ztU0?TXiU>qf=m^Qqjq}Hjg;0$lX?r@mj9k#=EBJ2LPkIe~NO~_}e4P z6G*11>1ll$L=uqYMNMG|0U)b!R^7@bP~WX#sz7@ynxcdy}9#y!~DYap^ zt!)*Z*AhZgbiV%pQSK+Woz+OW<}|5SG?OLWyDaWEjKxt?9zJp z`(Q`ORq2?UK^$UtW(<&pUGAYDN#v@}lcGQ;=xdXonQLqE&6PA=Dydl+x+})?j%- zkd9}PeLX*Vz~4R6PAlo^`~Kxaahw^hW0HBY_f9;et`T&AGvK>$uH z8Oyqg_TaUY8ZW87JmX8I6@lVdjgKY9?zA>`>Co*BVF`6Xuo9$@G?e^cNp6P)MB8UO zRUu?n8dO$Mn%+9K)2(4u6<51uZ93aXX(Z2P)R}@ODMfn;E=9`DSjxpbar_sYdH(>C zc_%o_ZCLG-4Qjfo*w#kdj?>&;Kvf+rD_b{BWTs7)#0PXx|a%QZZBe&ufa zsj*;Ofy&o}RoHnGk8k$3sq7g2lHrc2>pCcHVN@+CHA~u}gRrP984!G7mh$rWYNfh_ z^KsMEY44}+S=FB5c$KQ{dB>>o+N*|LkJYr@v8l4%!_+#69y*-oN5) zt(m6a799r^Ojkx2!-vdv{Czlkbi3V;^Hv<5$3CT(&fxA>Z2JbUx?-1G{h_*+zcstw zuTQR-VYR&CN{gytqT!I3pSm&`OE*W7hm>vi1*fu|l4E4%I+Oc?$xIiDtoc@BopNO= z7i-}>Bz|?5-YOTerr$$RP)$7p8zt*iJuuU0MyFK{k-Cy^1(mj|$0^WydU^!Z=e}#b z_W{W_E=I1$X|Y;2siHhtZVqKwNbc>>L!|YbjPp%nvXk<&Rg&_^b?OtfzjJS8g~QUu~1pMX*v8)xiI+# z+s3`V+&M>xd6v{UcRsJ9x7~Pik#YOcRf)Pwiyo)(+Y0!U6bgzYNa?s}U?^`LISy!` z2T(@8V$(s7EO1oL?VDt``g3P?xzEYiL1vnS5QHVLbSeYH2}PQ>bWC7YlY;vs&$(K| zN6OrTf1=^O-l!fx$vM9uQfcTzJBkZ*4NWVnc#*XsKMc>}o{^lt&qo`&agKR^lqgvT zQjR|iem{rfx3^_=@t)_qRP6%&WyCvQ;$+^cFZEQ^Ss!X9`VqRDj0`iR9`Xe-8xPlvoL0hKxrX!ZTN*X-c3HsNHx*)A3I_gg(p z1x*`^Zl$#_^KC6ntxYLxmlB^*r4$l}6Evu53{<`4#;eqDJt6KGSa1oJT!Mg3G1v+9Ef?WC=#k{6faYv zdiJ8MV|36-EH_)m|CKOb5g7js-<=K5>T ziZh^hf==k|dwjjQy0G|UZmw?CKA&OVVhY0^cjH`M({okt2&${RZbdYZGr)leKqMRN2pxej$Wg6Y1w(+2P$T)PWgs~676@GQ^1_lhw=^Y8>7xKYfH|z zlwkKJko_Hf6+`C zUEuL!b{;w49Al4o4e`g&$UB%pO5>#TVIiX3Fsd&z$gz3rEi zW+4Hs4QB=O3Ed|Y-LPz@EXDF#VvL&)tg4{M-5Uq0T3*yB1j=bM1RoG*9-$P%XhYkv z3jqY(IZQ`(s1+7`gOK=}ZYN;7<#~S?vf<=v`b&Kwp|;}wr_0;68h_)g?IG$C=7OR~tD_C{HhTD-_K4iCdr+T<7u=*GY+^TuBQnym6 zB*-4K4DWj?$?rw$XBK zD^44yt$Cp!g{#TF68le#w=Q*TdBD`A}iSlcT`;)m1If}?$|W({%A zFI8D`tCN;&1C{L=7aMaUsVkZ+`5hvuRW^o;D5`0b@dF?UiyP>rAh@nu9 zU4hnN&N+rv+m&p)XHEhLVEeZ(X+Ojh0U-=0E!A#LCCS~p;q}J<0L-by`MW-_H~FSSYod$wD(|UTT~)|T*g2E^u;REKEr!EYd#Sq7ajklVO(VAV zo?5;^^&&@MLbj5fM~6WdkVZpjqHBYVK%$PX4>girZuhD?RXu^L#Z^U1Ryty%PezyC zQ=z7c{w;_K+L`MhBF&A_3puDud~b|iFMOrOwi*ko*zHCB{{U#EVokdL36K?HtNM?Mp0#LurT+kAxfQDQm|o zIkpMeit)wMC9Fdh`c*yncPIj76j^-icFn0PRoi{5aepXl1wx#!eX&+9)xEU;03Ar1 zsg%evnGuS!^9my+3iiB*lM)Mh(^Z}vr^l6e-O2iQ>5Cu|5|Sl3N2y~o47gw|E9Z>n z0$VvtTorw}A;xJOrbob;&u^I?__t`P%DMs+aa@(`ksUgH(J59Ip*r}!q%oAzGc7B3 zG~#^1(nsxpuVT`xRf>|XqLi&{s*M%Yfh8)ozTlVkN}x8nx-Anv1*V)8b~)bz-K-6* zCh3Gcr|ITqLJ#p0HPlXIM~ZhWV$&uZHhTBI*}eMwDQVq_!o|d}x+|?Bf=i(&sz;dv zQewhrK^m%yOV=|{r)vI%(Bju?>8C+KTJYT0645d_d6jJXh+s7wliQsJX>MfH-FTL*H8p5TcP&=Bn`-X%Mw3KDt5HV{Y##`IG6( zMHk!?O;J#nJFUtR-Knca@9t0v#Pug=GU)|dLIJ7*Ne?QqvoC0W|+l``)4k_Tm1EOCTKogd+X6f8Qhm_K z;Gm#LIZcwAFiRHuWjI~(>b7WX!8&iOu5~o5Cr|4*;za$iWoa9n?op+9rrg}JCKsJU zbw0tV`2#tH+G?zi9%PpcYVB;i(7alzw>Xz6jN}56W;#U{O5KyhSnZ0@U}l!m;7ZNfR2z;uH(Ruk*KgOYXGCn5<#OlvJIh^d?06;%Dx? z0@VBo@`m@F--L*X&Y~eGGfg?3o^U#qp%`t2X)`ZpSn8=-Y7wTXg1>mc&fQ2DgTA-2 z(N+DFAJq+04Z^|B;N<5l(xGNZ6(AGk@y07CX+PS%n+yK{#QPT5a5e7Hj&V&JwNIcW zx^Pwsr7z*n-5N`F^2EI7BGUP^Sry(NJXvmctEIZw^KUp7r0z+Wn9^Dk8LYNY06<$_ z+`~zsD?IF$s*Cks&s3-QN><>L?J<*%4x#2N&}DdFpi_0W+T9~bN;C-)5pNt}s;%!O zDJV#gB2>l9a^zV={6nXhhN)V_Nopzhfa}%}om1M>3S21?lsbvjUT-^!yi7IBs#-5e zC#(*dyq`^abdIV}y(?Crktlmj7bM!1sZFdC&>z(ia{`EfYNB;Z^G9W)oncoa%>gOPPZykb=(zc1Yw-S&4|6J^FDuWJK11&j4HJXuwyXEbD0oPCY9pwo<_n=n zuMr57oG!&LnEPR9p)M#>MxO!igi|h6N|Dez3P36QA{hcGCiZ5@7H7<*G?V%uNr%FU zh>mcv556h>alZGOW&Sv~!f{m;8kAAN%e| z_WUuox*yV68*y2dHq}z8%z~x0iGuIw-5zdgBwmKsQY}0e)2Guq`;Ag@h`0RUL&cSf zjiaKJ-;8}<aU%;VYN zz+;&&Q9TzB@GaD zy+doJROB@3krelFWj`rfJX5g}ZYq`C?w9G&W20IsZFcmwf_J%@H{U}{S z>M9qz{awDERb&#lWez4&ZpMW27T!j3fi)=&3O&D@{fg_|YBv7X-Q zuK2reyG>PdW{Fja4zx;t9?1m{XGpQjeLS}27mBOS%*0QPxNXxU)6Qg>WpH#t8Z)gyIHvwpk2^r0R2}TEsqxkblxU;2lT{ly+Co(>up+T*icqmWw(1)x>PUjE z<@k^}C_!TSg!)N+UO0cTuGaEi9OP>yZObYCYPnu<-<72fw)G9sww!S;J0KEUbt;fO zL63gy%%(l|<+Hf7j|K5RpB`K1^4TWOVj5T&)|!wt0-inrXy^X`Y}3M2MM9U3?uu9+ z2?%Wd*wjmR+sEdS%f}+}BfY7oOn>(z3g7plFr=iW+r{;66!cS}3DAF={V`bUzT2tQ z>c&4Jd*D;zh<(4oBrpBy9C4KGJRwz7>VE^ts6rDzIVmP9&O^63sqw4We24E+DrJa$ zr|?&+i~gsb)OSO|@AFp~@m;G9#Z^gFQDU#TrP|kPRZiNK@UoKDlT4=`y@>2QJeGZC zmsE`bUpRNZTPumpiwl>v?rEc`s2{On9mnhM^)R?r)->K1gjvH*Ee)v_8;hhAD5TGQ zdI9O=BhLl9+DH-TzQAW>cH3iXjLvZ&9l-oib$xMurWFA~s^N;!6^^ZAFQP-)y18*- z1(S~K`;~Ovt=r#`&;J14Kl~_WzO|oG^|Y%0q_9)6>eTYXNfTR+c#+USws>es9Lxn* zW-u6zgNu2^0Y5O{J&MR>-%)<{dv@kq=NDAg&UR@wDG@6zzwX{5sO z*Bl^!5v)q$@=lZL9)jTQ3s-LA?<*^FYm08RsP=_OJD&Q={X=m)uG@xCiw+-AMM?9X zk}(I_b;PXtN_S;j>sHY@1rLUCgwICn8=hFTua2wee=oeQ?fkYhO>hq_Xei)4n$W73 zS@oRykK~99&~p_%0@4CYw%lUa98nr;>ah4H>=9nnOSZ06Jyml2C2-S~k#Te{?r&K3)*X^0&@!D?tk55#{$}TN=9F0(@Na z{{Z6&dZO0iz*nH7K9UDWdj^VOe$FK|tEgOTfV3Hj%*s&LMO8a4n*EAId5{XW`zH-C zQ)Fu&R9#8(h=gHGUVz!Ah7*w|F+jM{P|{Nn$2N)WdH}Mc;185Gg+8m#mpyjJP^ASE zsc7scZln5Q2@wr7O{&xorEchaVya`JwzqmAZc0sSB#FrD72|a@=iyq3+YL9UXn_Dv zY=`KJ`%&<(4esasMQx`VS(|>}O*Bj468DafWoW#ratTdN=x}9SxQ{KPX&c;nhNePB zS~9OFWc4t(D?Z(ulKF?$z5()jCoS1VOF-&&xkqrUX=(8aZl!2Hsy<`M$IEcwd{^oA z9!`5m&x=Rw2`De1m%PP#DGrW?9Ms1lkR}!F(iEnlfgsFvgl{KD?u{i#9Xw!xvYi{INK(_{K@t%5jZ+K4)XqMwq;1)a^GjY2a7eX z0*RqYB$V%z#=02LTE{>N&SWHQQU?dx?Xi&6R+nRId*{vFJUl@FZSe z-m#NxNMA~vFZ@hajGa^viJf}O+a8N@Xjh0>xHu_(>B8@&~aaHdEX({w@Zj(&)%vEcP$Mz z0uX>ERO5;v4Wt38ltA{u7g-B?X4U43k_&6A$MVDpDbI4{&eu4a{Z)b9xh(SUC{Q>U zuj1}BpsUWVbah2r12xat(7HMzPmM$9PKIVc#y<1Rn|B^aL2<1#71ov4!%rVqh+c_? z@=qY<^==4_gjTL0wWjo~E9@01ewUPaX5^Ymj9a!_KNZ+t3ol)HD>7lr;|WRl7A&}J zJ<+A^>QK7G5$}Kwb0=}bma9(Q#=u#F%_)0_@{JFUuA%#qn;DL7{W93eJBFaJ)Xr?v z;5>arY;~mQWGgb7yRlLB`a|6Ww;y!dvsS}z9gpZ;M{*ErTA3hh5Ft#My1QI>Wjq)BYD*HK5;sGS*nbpd;b+CBei^QV2b2d?R>kHvL;T1JQ8-E76yE2z(cB+|bCS<9l^qKO97(fYP z$jsMxs4DxywA4>1*>26;*!|7PEvS3uWejY73c;C@cTCst>y@%)>U|SGh!94e=uU}8 zHz3l0ZkcFMD$u9*VR;IFP!Xaslpvi`q_ol#kgn$m@q;qk71tV-ej!TuPuhr%_MEyS zEQd6pD&b-!&O~ge%&e0*rEdkXy%dRCT6Cy}mXo*zPKKIgIS==jR7vtHsZ9;Mc*h%?2xo9hOp!%m5}PPZK=bxRToWk*${6q%7T;r+>i zn-g>|Ew-+qdyj4BV9Z&%&gbT)g;nQmyph=_J=NRp*5p>EhkZT%*z0HV{FfW7njA>o zFi`3>Oy{*?N2Gc*x6o&7Iob1%cp-S>v3D38fr>%!JMRi5?yrI=IJWa>%J8DQpAK@3 zFuQSW`enqXk>m$lp?P}$02-8?q#a6Rdd^pUpWPzjGZdzhvqS6kte<-JqpfEWL_en{ z$|SmD?qJ+qYqdDhZz$zLi-q87m9dYoNTX!wKk*Jt)ejH^8TDdmi5mnwwobN$H0WHL zn136$PLJj*vq+?(qy^_zq|EJapg=kqlM)-DS|ozck-Wt!`sljxmvp&JxnZ`gIQ6!H zx|O8vQN3PTrP>JgAjW?wH!RE^D|LZj7Bail_^Q{)wO1TA--cM3X~ZS4mzPYZ2}0E4 zW8wyKBjSlJ8SyG>9b)#c+|iJ|k9J#GL0z?~{#4A^^wf}2rO;TY%jFUWpm{=O1J%;# zuC1)V15MAT>{&|PC{n#q@e``P*nFFn|3`x!smK%*fm}WTM6>n?R ztjz1^trW-ls<_mmlXtlx$)9vc*76GYAuq@*`hd1t0?@z}?)T;_K7^W3e zz(qJk7BfVeVzkVp2|AYuFioK5Z>eJP1U5o=`XAm?AF+&X`<{0$yS`_Vhej(+Rd6>u zyS}X}wM9XsHnr3l0(TkA=_n%M#4%94$w=h18?bPS%?ssC->S6$1mF zDxJ_K9tYmo`Cqw#ZF3gfGVtDwjt!TS*lT{1o;v;53t6ii+hO5IoG_G%PcDjq|LZ0e;iq1+@4 z$rQ#uX?dN&Salp?_&EOn$M8`{cCF_e4~^k{s4mN7rL^MC{{S=RH|srHRh4yFEz+2w zR5v9wwN1(f3R?{k@JvfdME33vtN3iDx6!ma)~D0cf}t+GT7;p+C)KAZ_@yx;!meN5Z&{4=qfZn3VHC(+;+^AC6@t zcPz#2kXe@1meTs|Y+Z zB}FhDy1z{&c2FWLN_HUSyGd=XW*XcnMK5%%N(xNq-j)5oHIji9tbt` z@lekxs5 zGz_6Hp)FS+2Wx&En+M0ZjSNq3l8i_3Ftl*n-iEft-WL{tH`Mx%58J&yAO^I_Wt>LWTRM-2z*>DWJ5Phm#;unIdQ2Am*B&pQu|8 z$NXR1e_$4#+k3;sFRj($9JxbFR9K-+O6WsXO?=1X_d>a)YIUiprv!jlAv%wXI4-Pm zZ+)Y_Vd+qW$=F}x3ooN2F#ElS_a7ZN(AqxcaNI+mY`9N!*abSUPSrED)g!nz=>rZ| z+jEBG8Z6PI5JKLYZZ3MQK!8H07QLr__@;I?b5}G~mGD{K<$^N3bkCEgep_EM!aN@RG>EqAYo=<~6+DG%v z;}FN|>&@}Iosso5dR4BK2CBN*MB{2g+f_!wQcz|lsKR$v1{XW7MNKL8t)FW56wP3^ zE+Mu1l%d{-ZjSPI9o~DR!+6DfNyg4w5w#bayR|tb{mNx6lKo91zNu~J64(eHqLifS zsTj}p?ohSH2_07ta@S_8d0Ho6qaE*9rHs+#Sgn@v%RE<}T`-!uCADhLak?j7ZlqhP zoY;q4255GjP5_b=CY2E{6B=u{+En<-i-l^M*9%7~TCX_HLyy|49HqQIZI(;Yt%jzT zA*)it)kLrXO+wUHB7vx%gb3N=XsR+uq|}L78mU*coZi!sWBj+;&RZCwjPZcPG?y z{o2~?rrpb|+v{r7%_GTc=cytf!%*SIe!tEekDE)1}7R4DL{62;8C*GETFFYXIFtO;l-;B}Sr>oeC}w zOa%CkEg+WDf^?7N3QOZoAa=ILgqvesHF z?hY+Z+f>yx9T0HSq$NAcbJj{FQ!rK%DU4^RC{}YdBMiR*t9?B@d{*KA0K4^M+Q%(i z9cYG;^2wS1069*6*v5H=@-3P@Yf0s-eAmxDBlazO$EXoO%DlfN?5DU?C22^P;sQtS zoJdQZV-fW%t+PZ~Df|gZw>)R(ZVtb`Xxw(DF0F5Q8UoAT<2sU)!6{oXlvO&0n`UcS z0C>(DeVgQEN;4nZ9eFugH7e(EMg+z$M(fTppI)Q_tt0>fJUhE{#^v+}MfJ~?9g^D^ zxtujBT6qmPg|?&ulQ9_3<5lZ(#K$m&n%&*v_QojJRNFx{N?iRw05Xx%J=e?o=R5$u zH1nGY6mBF9R#0R#jiIMXPsh*?bbF&6Rr2id@|6|VG&VWAthD!yV4|B6EsWJ0ZT{O+ zFciz}s2^tG5M6`IMWV(?1C^0d$eHRgrYPNLgV z>wce6AhH4bqQ2??Lr{dVWUOcNNU4OL{Y)vIo!5`)+d(-+1eOcSLy=zvx z)W15r;~ZefJ)X4--053sYVnf4Tx(%ERe7}O7M)vODBJ)dT_NQ`8h_*NTPx`AIOXz* z7b^xqO7#`uh0RdyJws)sf)B98E@s)X!diH(zi8#3XLKZI1@ni@U8`ig72IoBa%opx zkKC$-Mhkh7Lv@z79h-j&D-v_nCYVZAMDQYhW^E_=J>d(HQaVP z$h?)$bQ}-FI6cplIb$Gj)7g01&cbSL)sD5aICd#WLro)iEBVI)@{%>Y+XX>+kGCGiFpQeuZ)s|x zmE^wC#E(=h0Ylm#$h%g?H&>NME`IV>7MC4#TSLWPS6kg-pvSwD##mjsWwsvV+apUs zr<|J)R@>B8N)iXgwp343un5&faMpl0F9Pl_EIO5vBd+U4ZjGlPwa{c3{bN^~9p#4V zthahs67@Y*JLnD})|QEdB|w4*i@4kzDi@91*hvHV9w>zHIeo{0_s^hG^Xxv#)0$&cgcto7R2h#(BES@>a5hZ#{-Pcj=j^=7-NE4t! zvyS9cv-J=_nU;sn9nJz{v4Q*&qjo#<=}--;$O2A~?qE|+CFda*^8P@jkyc)~lhV6trF^&vB6AYd$_|n0_XmS> z4N9+=H8QIZl@T&EpS~+4HCsafU{cC$q-&_3LlIRoq)A2^rIf%U`$>o$BA=5=A$=5q zp#+Y)!wE_%g$26iYWz zKmPz!*YEwX#%_r}Dtgk1wvR6uR4Ga=i38;vxgJ7FQzO zD4tOJfCL{HkfcDBOo9hUAC#1}N)*hv@!Hxqnd=2MGesNZ19Y&>y0J$}Qb8XHiA}qZ z=INd(W|q-cFiS*he^8?4+yUg969pu6(h#<) zK{XS9H)IP9N$~*7Phl|x&7)CPj;oe0^7??uDrR~uL-d&PtiR5^M-}oZ6SpzC{>UG^ z;$`}x(*&z?yR9|lCN^MhZP)l8^kZ{)Kcunt@BXsI+o!4g$xw#A6?)nqJ!KwoBYrjL zOn3C^SJQ9P7l$q5jh@IVsvHkoTUl@piBgd%N`4*@^&ghDT_`!dOW{90N7P6{rX083 zGrVQanZ6r!wKrLAv0NNwg&wO&AwZCPb=EXf`G@q^CN~WNz~lb_*Vg2*O`a!^@)NB0 zjfCB8%#n;YPUs(vIt@>{G~>)Gce1otWB$HmA(EW+3E{@w*>SUZ1qw1&k}bhgs6>S2 z*PxEF@B3#vl<28*{zPQkA2R_e-13KWJ)Pe0^;6^4)U|#~WP0dnNm7(k)@D+XjT1YT z*o{b7%y*SJxaAzJa9H=1@=H~!xuFY{GM2wU1JOFlGC6$oeKSS%hi`USi;$i4s-ZlA zSc=QFN2cMF%81rNW^wW#bKMzkO%9b`V|JyRW40P7@x%gvkO#EJS-`DuEkGp+0YIa1 z1Kk49f{AS?zOVj`lts?VGwO|#6xS;GiRr2nrm^*Jm4p8P)$8-q{fTum;QK#dPyPz} zgi`juPndDL{CdVlgt~6ODeL;kB(uGI;!FN?f z+}%|Z;}HH;dV+qFfk{ztC`gH<4=C~IfmhT{{Z^;Jh5FCyza9jn|P+OAmsSJWj-joqCG|?5v+G@#lqM)Jw0K_suQ4^RmhPF*o zbjfL|om-~9+${7pb&F9AreP<R<(qDRb2-w);qSn4QZv9+be zq@?vaz?4=IuB||#wV+e@lppK=KaedZZ>+mRmS0IxKk>c<7<8DYWij9wx40b(}+;>N|Hg3Gq|@g zxU}vUzrl9dBM)xG0zHKj6rdm}Xd00t5!yHUgW8I8)MktCOX<7H9C^k608f4>smzt> zT5;TG=B~RLL#4WAnp2A)0w*y(vG$Fo9)HuLm8OjiUyj~w6MNow5j0iqkkj6&&41Ms z={EJ^wyxjA)%VvQbxT!ZF*cbFl^O2;01>yWW+d*LUAX%ft;hbocRVrtP6Ph{ynZRg zZu*z~E^C5RzTWOG5A9=ite%6#-cGyeem!9H60i~Tj-OJbX~I;xh; zgG&5{%$?K!06EB-IHh)=s>yp}DH$Qp+y4MiQnw6ukM!l`4qM}HHn;YmyKVcBZ!}h$ z9nz-3WL-TiT~m^2$5w|5l8~S^0GIz{Yd(MKVQRq^abYD^K(mMj~ z*O##FU*ns-{{T11sIm9#cUvu%lC&E|Ei=uw!ry&pE$wl~2osjEU47gPWGr*Z4k1n1 zUQFe)wlY55!=TrY1DpoBYza}jAMYA=Fj;Z4!kO4o1egf7*i+ZoE5ILYQyk-KR>*$! zqnG_&$#T$^d(~LS{20S97?MT+{ym`3K^LulnZp3fk0d*w$B88`YIJk22X- znh+N1W%k44?l^$?zU4T;@h=4Xz51%%8-%J0TR3~GngkQG~MY20e)JY zNh+myZPHq_q5EOyn6Rc;KmO^I@n6qdnyH0NK*0s$#A5x)8pWb8U{QM+c zJKDcayFOlgWM{xA(kNDi8%K;isjfyF@et(HAS4<1h~$Xs45Ve(tTiyB45*Sp>ERA) ziTWc+J3;d>s;bNC%T!i!X|ehWWcn;L(_=sqG?#&kNF^nzw&K)tYO3|Q`uuMu zrBhF-2A%XMs3^!Cy11OR(Mj2ew;;cpCAvA98)NLp9M?{Oc`Brsupxb#@fSHi{V*1Fet?LSW zcGZa6a_sJnyX9+E%yQI+t1?@)LE6P7x{;Ms)IQ>lfi1RzqY~tAgb^1W3qJ-S*LMOT z#HRJrk@K&pO(ojnjbHCP2lVc>JpHqe`0B#*)+~pVnx$D`%fG3XX{R`9eoHioo# zFE72yU0giASC1FvZWVRTZ++!Br5#kFOj*Rxs+?U^t3-gcwn&uvn2?m>+N)>U zwj=!K&)hGH`enplGFw7lXb!BiR*Huwr%jES#zVWV`Z)O#4KSLmEf;XzwV>vpQ46Yyn_f&Tz0a93c*)x45U14T>bJH$5e#23p)H{6zL z>KI9%S^4Uz$FkC`UQKNptJNhwnt?MXQa!VX6}L?{+(1_HqvNTGSke=mDw!ENQr$I3 z24iB3{ppDlA^DS2f>O!kOmkhYB`+*FE}sGZn6SpSj#NG9LAF|A&Goj@-t|n#1rDc8 zHH}KMFj#!sb!~3hzkezW10^Y zGh_g!rCpYlUZR6(N{8WGwELroRuJ4KSVO}NvIs4Xi(&_PO)Kp=xKVY5QXm|EdbRQB_}7l*Xag}OXF z%~)^LM~bMpW0mhT_73Lo8g(m-v&Tzqry^+${7gU6ZYyXGplVE#N5R@`Iv;ITtN$`&-mBZ5C}5 zEtXg$g!;uJtcQf+)&^9ntEp(-P?{WbPgQX3ySlGaa$B#f1Tfecly>=x5SS=`m{*APdxBD$1v(N|2;Ts{8)yZ5Eqd;^!VyQsL~4%;iP)K*npuJ=oo>YmUWdcRVk z>Jhz4Qp~^+V%KuAfn+&(-&Yl&Urt^O6<}F|Q2Mx|;f0wj6GiTO0{- z1!hVrCvZBz7xK6>R5VtNGM-aVwEHB+8lkF7ba*v6G}Ng`rDCPF+upB;=t%RF`o&ar zaMd3pc~)Zg^<7(xah^Ac8&dJdvde&evfJyW@TMsw;Zj@$T5eF%E%E2#RZ>PtsaVC! zx5sJnHQTl++BJvAydUeL7e_(r`uAtJB9hrkqt6VL4Fi? zw;x{~Z67UbZQ!blg+knLfSt)GN3vc~TO?vx0N|2WJ&iQu>FMlAICnR5hZo`(o>I(h z6#2&x-EQ^R-Vu&u6wO_%H~QK{dX+EJ(^L%`U=p8M1P%IlMTbf^5(eQ{K1mruc>e$& z!AcoT^S5lTvn)d%%P~sM7|A)~HmJK?Mhj0&x)W%zI=X6ARkd?iP^a}BDoPvZM8s3X z*-W}>0=~JHIPF77rF|>=6@TJB%IdRjQMpxcwf^7SHy`3PG$Sl--B(Fyy*AZL()m$I zb9Dj7h)TZ#jKCxT(k|bWXwkxjQ5!e%g55#%^w}&~c_zijJ^I^uKe`SXtF|u*TdTma ztb&TN+evGxw9^x3Z{Tb;@>*H}0VJfaSOVd~c5*b)Sz5y+1DWGP;;4@!-E!VU&p8W| z>fLhS{&9G^Uaz+hmHKIR;au%nk~eA+*>lrG{1PJ88)C?SI{=vLq0UVyRKJAqcQR&6 zs{TmBDl+t2(M;{pHqh02>6@%kZk)K&YAYpbn2-mgLM_4)_aYe5zMFVc58#eC?fHy% zCF2@Q3j4)2+k3qAlLlO+sK}RBP5e(ts%|aURFVLnz&ZgCK+C?d5j#h0JQtjOMZB)8 z!%(VP(ak;eay3<}4Qab}E5~-nQ);NIeH4~lKN6%uiVaOdQhp+a^&k}~Q;-P;R(BV6 z*meSRbI!ElN|wVJbCZy`nzg%EfK_{v`;uNq!kFJ~n7g5z3jwUGsw3DEhfC43s*P3%Syw1~K!CSO)#25hO3IXBXD$Tj8ka6xm#WDJvlPSna zX3J=026I5(FLEM{{R+482FrbJ|3U(J1Qv8GF!hX?i;t>H{r&4tDiX zgkjfvLyNJqW6mxQ3(Z}1aQR=8q@!uaN*po@QucwQwVst-%-*yg1$=%T(O7mi2lEl9 zuhO4-GxZSV%hwg(`6qy|zA>_4wp_;K)%!cTSn=i^MO*J75AI^EEtNDkBjenZp1R1v z{k>&w$eSr7xC&~2XjONUOFV`;d?$JZoxEY(S8&hel~p$WcDdoSPOu-!FX_2AM9FH_ z!j!^Pij(O+?1*i&6q0AsEoBir>H>f1PGKCisr!F%R#y@Fi`=ukdE7I(t`)_5oW{L< z?9|`s*|KDmtSV?OQ&(E)_WGzvCAAQelP`#M6!(}$%I;{MSbgK~*W9ygkh~9^Nf=7aVuC4D#$Qc?%Y`Jd;sM2&raE^ykt>{KlNXXF zVCWOPY^ zMZ>px%6iJG+$NUEc6cQP%SHawlG|!p6RI7jeWa_T^q-7;_miG{etFYSfz46t6<jllJQ%#d%bxzwaWhhb*`_#%iDp4|i2iw}>eTFwLxr+ALmoe<{vzYj$vYGaB z{Lz|weSd0Uac>}Bc`VD5Na5uZgv7AnjU7LZn89D{>aik|Nb@ zZ1Tx@hXTHec}Fg;?qiLpvEC@Ozn52JEL*M^?TX%-+J#yW^*^UuNS0hB00$MS^pGZT z=U+!}VACV?9xK$uw^=tfD76a_vYypBYL}aw?eihD(_s9Ic1y3?SiD@@p`>Lk+uXUK zFTBxxwEB&=Bu-~>14wk)c;thgJzB1D*oh*aqb80}o+SSOE?#kWs_-{2wNxB^TykWh zjn%x>j;h`dqrYWBH1&`;QLdt*nw9q4X|ptyq)K{E#sRspjJAr}!tzMn%}S~R&X-JryOEvc zLdCwzb<68he^a&_daYJETcTb|`)FB8mX`J{A!fM0uAkQGQ93dF zddtq8&s=IM@(X_*QggKpRa)-VM7e3R-(H~km3s3DA-7*;Y9Ts>EFS8h`>vdeP=K;W~k=}`S`#mApfRaz2+zNK{q zwC$1dB2tYjX;o1Z2np4SS?cl|y$zC(fl+aFmKfYfM)#H*PUGMvtxQLKbRmSi>;gLz zW%Uz?{7To{m+4t&#qnIFc~@{&D^DkJPBgr?l<^TuaB-&Wif?-gqN)-~)>Km3!jt%n zz?6AZ9%Zo1SOWJ+{F45NbBzkWU8QlS26oHX_FLUwFY)EF>&h1Pt8Q2ZLv*7r)-c+o zOKEPLlR9NMxh+J1b&B8CroFrx(^X|Q0^1c};01JFI{eMZ9IwcCdmYm_<(oC)g6JKt zcMiwtmqx`6W9}(y`K1Qi`G+R6wMr&KqV#3 z6C*!3zhxTLpO`tz>W}Ck)ymVyoTFQLqH>Jn38A@T-m7Y5bvERO8bwQCU==dzlC6s$ z3ZfAA_LuGbka+s`E`?VEA6+)blk`xZoe^g4ZOl&(0b8tQrRuvqBh?7>-BNxec!_BqGn9-rV@371Y)I!?itd*bgzY4a zM~qi1_b?E%d%TLjNewbY(&5)%FK62x;pNj!D!xbas!F_tm3a+4BHCKg=u*u0Bc!9= zZH*VomyxJ?=Gu@+Is-Wfu&S+NXqo%HDwMCt{6Ara$lA248$m*>TeYEg%Ux+R6|^i9 zA^X8H@%Kd#Wmc(=AcSh&adzEvVyTi)>kbDPUZBk;eh}#x9NH9cack-~S0|5d=(yxs zFL=DPR%SY^rf2P(WwPuMw0f4y4ChMo;#KSIj-lFH&Q)trB_S=SgQlR79y{hNLCtHI z(H=_3=;V6}wjV3D{+f`PPMoDm(_f27^3HGR(R#bmSNM(t`g0 zWD$)tdJ{Jv^y;%`xcftN`ISA|pZlx*BQ+}j05}5Lzf^q%*jJp^<)?vP4SB`jw`CTpd#zmq$#)v6d8DoCTDklxX=D{9W>W)09)I09=w9Au4Hvz6 zd6c%}_R2leEYee;?QWf* zH!P`Y1#3}09Q$)Dy*S2|(S3Ua?~dF)Q>_*w{Xu)8%zK2Xt9rV=qmH;T*06BCK3NH_ zx-GImuCD-s3go%y;w~Z~0{wh(tbTS?vWJ~oW zMbV-l-VtnRWBfV}(>d)C9Fr2Jjg>>Wj@rQ6^2wi^gfpm74Tg!oaIo9bk`qjjBxj)O z9nMz`6g8At=~|g>p|nqMkOyCMOvj?(HB3qp({2z@2C_&p{jlz#Pw7<+l2Ekuz2cPo zL`?X8sC#8KQJBbC?aEDD=xZM|>L4jZp(1hyI!f-YG0kZjsM5nzk*=kZl{vXe18%;3 zVg|T`Z0wAUmCeJ2bv?bOYDq4UHEsk*#IBf>1eVRJ&|cRr=mMnFDr!QG-yhc$_wp4j zoC2i>-7WmPWVi}d$w4aoJ$gh|&e0I~$1n>Kuhn-N%J&1QacC|RwqDu64t*Ay*`P5^ zymxm+9Xn}tBuEJZp_D}T1r)62QnUFh0V87Ok>DYVoZ2EP$pVkV@e}Op4CD*B3I$j9 zH3?5&FeAA_PC|n>>c<9%mfnCKu$+MqiCwO>X4z7wm9YV}c<3bz>8>uJ3kkcp$mrO~ zD$vVvKUd3 zniUg|isa_qe7?g2Gjw73Q|_4rBnfyzp;C|l?FkzA$1X z?Gu_x{kYC?+{ss|{6aV`RvU#db!Dina3+3|z&cOg)-N$uoaB{(?<~QAzy)O+;wly< zl>~+PNJfk38WtJ7!7Gz;^~dzoZ7fr%GEcf-z~;@I6{W6i?ldYctfIM5xv{nFB6^Qz zkOZvdD@PT;a7oH~@Kv+sltCH|(K3)ds=)T^VRre*cA7f(LYr}E3IvTwGJ>pNJnJ*v zlb@&2TyEvK-isN&*;{Fp?|z)AN<_Xconqp608~AdbtB_E#Z`)nK=?vYEi~x?7#8%r zoslDJQ0gTO=qP%wMb$u*^+)J{`aw<9D5B^|(CIHNBf3E+P@r75nP@)9P|7C4fk=T$_4~Ubyf6cM*6IuQuw(I+@7&ypbYhOS0YM7Io#q@Nh%gwZ%` z^jjRzYNM~V5pXq98#Gf01Wb<@mEciuI;D1K=hSHoC&6$jt)9{r>=i59`>K)mi9;Sf z6iu=CmLBse5^{>Lo`_n3_k~B4XGJ7lv4|9nNnN#5t$7VU^-9uGuWslw0;_W_)oK3# zEi|NY`q$D-9e>e{)#vt>GTZyh6YCO2O!E9g#QvD`sR1&5tI-Gcm(+LDB-|?a(9#=_ zZLdaXnVBC6^N*?gypx_9dY8nWcE%d=eQOdsw8!sPUgr4{m9`gB;JP>i;Apck7;fUn z%_c;EIIl+X?=Np}f@_@Nu~LRB+J`OOag%tr;*}Ltp&R{R4@qv1L9_Dy{K3o`SC8hr z_rSw@(tMyAs6YLcXDnoI+o~-NwNl#3eG(Pcages{o565eMF=a>-QT#k*@+tQ%0jTz zJ+E^`){>p-97>_q+yJ3+ASe(#{qes402#au8ZRz)o47W-j(@p2u&&Fwn~-vE>nHruY#cop$Sru?2gl?x&oFu1!fBG^5*8Tp`T4UMR{!$ z;1%_2^lGMgj>~x);vSOj_XGnZneAyNKB)4bf7liI+5X47{{YuD^#v60o-4$8Unb@4 z@*Og2R{d>2ZEh_{4WCqr^#kvXtn50F7oWQA!HsD&6k3gF?8~>zpAETG!zLN7)$E`@G8+{>3 zDVu63BpI(S7$saEETH7|Ch zTrTcY&^79*)c_<3(3mZ+q8!Ciq^kN`UJr|buqxoqOmiyxn7$IUr?v*x-#$I0Fauu|^Y4Cn zabucz7a)++r41CW{{Yp;y_zvn+uV~)SmMfoarRx$k z{uvVXb+;g3T<7_g|9VuQ@fze!bTqs;I5Gz5qyarlr#Js*uobQi+cyzqvC? zU*f)iv$YUJ*v8EOeSsmAEl4I>36MRp9iR6{wHD)|`;zRBw7d&G>?4UOuesH8Z0C5j zIWLJ(M5v*mBzspj)5xTHTBI{LqYm2H1Z?#Nn8DR+=6 zl!|jQe>k8?)2z?4k|tWz|$X6vuXF`DXH>j$Zj zQ|(P^;jfp)sLPQdED2T)aIk51J5|xMykODXV_S2`KZv9WYv2UBN-*aF399x8ppr>BM-@%^FO*YvX0#=Gyfi`ci2jAF*?LE7 z_I#1c&gD*PP+ob*zphC>U12(F+#`1@KnX2OEB8m)2x3p_E=4ko_X&N`!ZN$4z4>4~ z3^%4y^%s7Ihrs#H7+6AoFh%}b-TGi^rvyr^p(7zJH87G#qO*!b#;!)ix(N1&sJMtL z2qZ{Uz@n`yP?4CJio(#2dkJd2+#8oOZ&fH;!8-s1dqiF10FVF^16?A>HKln)^^s{P4u0mkI!-po)VhF~Q#b)iwLbwGdtw`>wUL!SY~r6i z<3QlPMLU$ht1XY)bA^f04DVY8kOLbf27YO(@VZAdd({)L|!~js+zbrAOTh%#w)9E9z%2 zC?SBRlpR2Q)PR|h5*C%fw~Ep}--#SFS@gDqI`WpkH4;4H(%Q7566MSd-BTl`iWxm>bXXDyziH9lf4lt#(!@)^$EGgBN z!3x*#$F^e}MYDOgUoK_&U3TK#s;;g1u$o6ix}?kfSh0(BTXfnrX16ao1?501#b2&3 zEsm=@bTv$T{^?zhb~WD)Lu+w#*Fl0-P&ZWCiX1~1Cj}$LB|3?rL=)|duYXGR;^ZLE zhMj)Jsq(_sIWw^b3XWl+_NzN$a@CFSCl6zIvn|nBEqJwwiW*h{LtzHq(u{!cnK7!y zM&GAzv1NOzcRM2^iK#9rfE@&v6urtM%=yj@r4=wFr7BlbGpyzNVtDpZRXXTgY&h6g zQTt&ebp=reb*kS=tVyi1ygUl2ZF;o$ZL;%tc<6R5x@6F_f7Ytg-5y8gzC<$|TGR7r zy>rQ&Z&NVZjfGd%LjM2)he-KOKbd{IaNroKov#}iKQZ6#l@0HpwYZrm^eaL_K#xnB z`=O3&M&)?zjxXvmZh6=$@!l_Qx1Mm!sF2QSPmS5^R48G(RZ=-p;+=JBaZ8$3kOx61 zv+T^^w6`XeB&r_z?RQ??Z$KB%uXcTkSScCgdOV%%nQjI)P(4hhB%z}BxV_We z)Okm(Vl$%RGU7H~L6pEc)c;pXENJ{{ZpdT0y-1BQdYbYXwg<+*P#- zJCkjtoPeM2DkP7%xsJ+aV7X06RZovQCE?66g z&LOHQLuacBamAS>Ao{YIK5*0Bw|~eI{{Z64ANq+)Ano6^JO?GZ;asEKb>_jASr&%w zw#qDNH379P33UW5RSHQ8N`ai>x!u!|KPv(`o>*fWyoDBD2lh1V)ZcSCTd6<1F-l+v z{{Y;^C2w=(PVt2q^8Wx9-K0LBVy%3yig3OudS;V`xgPDvHrMIg{LZTHX=>E#56(di zhL*76NY{3fi+en!%*-ZchM!WVpSP{8rX3!A z6d}!rYMmo-ndmi+id|~2veu-+09OL1h;mzCN-h_hMH;{dX|HW+680b%Ik^Uo3mW`G z_#*9K0S9enXKiHzqLQZ^I{N})jmWpbHP{!|bS)k_&O3CXy;?avt3}e*>va~|`b1PW zP{ZvV$Z@w2Qc$FkFhPjso;jLW;ZRdy!pRH7k(xLy!Q%e_sSXLsd`ENQjzhSyYUlf< z8#eUvFr-ZtCQD0B;&!u9Qji00sWVARj?zriDUdv&{gotFq4A&T_;@cyxbHVV9$4o8 z08_mxaz5bs~;z0NfAcxB0!&L{<#>W87N@V>_?#eyMTd*LIntmEh4q0?kJu?`R>BaK4gR%d zdV*$p4^0M9Ac&wPygn-b0QRwo@%}5uJ>=&&zAIBlc*c0)%S9b^eR7|uKh2kldwRmQ z1fWR`9Z-;cu~mM;*;od|q_w#Yh3-4Fem{b&{EKMeYOAg5B+4u|J8P!PPi(@d=$Bj? zU1CqBr4oRkMxseqQZ$VWBrG(K=B|rodqLoiF|guzb~TUY_o`t{MAPEfZcdWRuP4-4 z0mPJnxXwUIDRL5kQ5e=497*D>4rj-2Tkk2h(dSoXJ5ZlhwfgNi;X-uOsi}U53MW++ za}Oz4x%C#kUai??A!oQ}Mxh7-%2lX9^3r<77(@t!lb+idvu3Q_WyYwFP3!}czK}LG zP|AXWDG*ZH492rPV&2v;Th8t%TcAvWF~jz$445Ta(1 zl9M?Rq`?y3B`~XIZ9R)O+;+^m_@Oid(~Um#5Jk>*UUJ~+B@EqX&nt4$@GI3%9PmziTTzKy zT1~q$%INAUscCaMrrj<+x7lH74$y^74Iy&{8kx==J0p~HJPP{wEtWpTz)(1W`+Yq$ zNH_))bmUy87^B3o}1Z-Oke$Kc@vilGI+r5W5YSGF$vkIr@-r=oHuA$t1prHyjOKX(L^%92m$<-l1 z&S$Cka#;I$GIMpO1TcK9Jbh>I^bNZWx2#Yaxb9YFWydt+`mB=J)#K(k^ARq)c+MYZ z(V-MhJdn%psDn4(+z4)a%=PGE_UiC7^0P`STvjXKtMx_mE>z9?Fn(IV zDC%3}zDBt`-AS#xJGGLkYFdQ4(iu_$kVq$W+*AsH^ML^XC)6915IS@bJL$>F-2#pF z1U9r=Gu@?cBIK0(cc1XRpCsX|H|{NJE@9^(sq?QrEP!P%I_IFm6fCUB1~dJPP@gF^V+hUQJ7Ewboa)K*cdFsrrRd zwzQ!sk&!1*2@?Rwo1381y%x?mTgo&|be~95zRClwzMa(I5JHf3516<3wPw=AaO_Iulv`t$zO zxR&usoTCS>s%3YqPW+mcCXoy}mjWv-&V(tyRU1+XC{Ws?7EO45j@bw~*y84&f2QAW z@Q1sd;+$^PXSVY`17{XHUR7T0tC{c=pHX-ZAhM-7UvF7#s0upe1wBThWDfSh{0WNb z<1KwSaP@_EVQ?PWpSO(l&!)=rdQEpv`#wy2I$y7aRaRO3`1#QlH%O z{0B)$Z2k%?gd704Ku5o?6eCbj9krRC5q2FDxtLC(t7?0);%+D&Ua^L$rb+q)=c2g@HFCSTGL zpA^~e7NLc49m_g>b4sq+{mHs2R6MYxg{IevsP9T<2urndoNk0t2GPZ4T*#Vq1p0bz z0{TPxWzl6EsBd)HHd|4SvBuQxi*=(Fqqk$0^fz;PL2W40G`gn&^kz!lK7C`y{K2^q zo*6GS)-+RweyKF|=`yc6`Hcwko-H)ZRr+BlVT_HT90Bu9dep8j6*z)oVSo zQIL{)2*1KHuE)1d3YRq_#!2F})mBa-y4Xf*V#jay+rB4?X9`U ziUizdVI^&uf*~&LfT0MxE3;AxnR{#FD?cvhS+^Ki`MIt)H(NdAQ)PEL@YSZGj)`ed zrg4C#f}E%%6zwBGPlTWw>w_CXrB$9if=3Wd6<)RuNXxNYM}n%ZOK1*Rz<3v zKO=gst^B!F34Et=8d9DmMMRCHm?&Px#+MPnMGdTy$;b~BugyFicJ0rTFI*{_+$bwE z91-efE_HTl<5@zbRj}P#C7Ohxl@%l`PH6-kJdAaP`z~YV0)y73xwCz=yu4MG&v^@) z`C}BkT6vk;bly0=vfqkj_T}5{wyUdl^wOI8H=k2ww_iJvB0y4QBQZD*HjZ^;a_I{` zAf!&-GQ7s?!x{aD73Vw;w~G~1m9}g^yI^g3_0IU^DhOQ_-L2IWt+ zytN$R+SNAR>;@1`2dAdVqk;B`#Cd;gxE=2qscD{cyHwO!xrZCPT*9tYc@e7gX_ZEW zmcl7k2I^&}bgFDNkvcGIs;_n~9&3&}&$I9iWomAXS#m37uIZ9KQAbMZ zTw5jA_is=`NGm#(9TV0f&rKn)a05~m$7M$s>W$_)0=6r-u1w>cfOo~mxD8mXKiYusY$!myTc0&A){3nw+VMEtKrj+L}_S(1y}vnRFvg+ncf5UpWXKmw}a@1 zndkhAn0H5>@wd?}!^4 zV*|w%+1F2e(vo;B2E#Gr+$yt25`bPe8jXj?M-?dCT-^pj&tmeMOm9|M4<2HyM-^hV z)wdQ{TV#fo3e2<;)-fH|m1JZ9wSNk;kTb@S^4G3`P0 zol2&4Nl9GNMx7JmGZHzqR#3DBGt-L7YHU$;i1b`?m0JlQg(Gy*sgF6uGlr#K!%;+e z$7bkKUNWDIHLUSg0ktaAa1F!M&2ZM8RMKi2X>;;%TV?uV7u{l(&T!zj{fyA-*0Awf z=YwMd7v%fPZKMXSbclHX2}__qq({&BoR8F6)bL+bcD@|iWJntoVM<2*{wWeC$aRcE zNuu?+K4guNuxji#T7A2N)b6HV=OBn8$kI@%bAqs@N2P5-C~{ESJZm^ z9~6(eQSJSZ{#l&g74iq0HpWYKzlx?;dU3f@697+MonzRo9ZeU^cQF+m+{zS^l07g7 z#p4#>o(iCl6M2JB^X#)s6!M-@LoKBxN>L}E15*%Q+%hJYIMH8Wag$xj85kt1-^Q5D zvxlwJT!G0n=BE8nCxu|h338>^lq8oNM}Z#RoqETEcV2DRl5$39)%LfS9gH#1^=2O# zRj<4ZkC!F)HlCBd$|*iN$TKII0>Tlex6KwGE0;>UST}3)}CrwbjO@Yx|Bn zi;4yf7C`bJWt^*%Q+E+&GOj>|@+CqW|TsN}xbxUrhsA%TSdX4mz~j z+b2Mpa=8wjRXCWyIPRfq+@ag&AE2$}enCKlnf@AnhwjoJ(PERQASxfe@q?zO-Ey@T ztQVUlRW-)JdbU389c{kjLh@>9s;Ql5g*5Xnp({etW)uMslH0Y-qLMi!WBHnVN7zLV zyf3J4rtj5WkDn-PJ+t?pUq_7h5W-VSgYv-$rLyO(pah^gt*B~_?h`Ljl0qLdTQc~Y zZ!tbHOK*vqxAuB^aIFS+w&S>uor0btN`vb0AG2e?ojK3=_NvtT%1b+%oWh4fJ@Ipn z$%lJajpT7ph14TOT5tBE+LRKWlbm0M)m0GH8rCP#CrL}Gh5}Rpv`7ybOp~mk;e|ZB zq$-iTE87iCQGQcM7Tddll851k#*BneC>p2+>eiX<0QKo8dOnHFfQrgJo2CenFttn! zPuP(Wx-sdJScH>!Gu-8+E>Vtr;HOJ*ZU z;;1%HO=Ph?;vYb5Vslb}mZm0_isH<@`UJ#Yb&gw;FHs_00GCg)A?E}uF=p_RALVNj z6n!);z&wN^XGow^%QOp2+oO4YUj%YsWfw{1H79juVblQ9Dj4YwHrA!z6&MM^hCUY>Q!G*HwLi!oK1`ri&WUlw31R z`jY#RD7wL@3oWa0OoFC=M0unaj*{Z8*?VNR4T8+Eo6&s%@DFl$otFS&*4mm%n_xED z1d}NbYUirc#Bp_FCc0e5g_84TYDb!z^xHynyud+nLV_dj5CzPQfA^m7=YL#vGY5WPr zd6xtJXOr6KD^1Rc_pVophZ{=USPCLaS(-`+ly0#&xFoeMcI(Q!i@0v0ziey=ma3d~ zV6G#xcWaDW`8~=PFgG2zQqb327Li7Z$^QU@b!v0rl+ZvbGMN#Yho;#%crAWp%x!AT z=&jGyO7Xm2C7wRV8a_LcRT|0?IGSUNSQmM%rkug6gFDKIitW}+|$@zJn zv99E~rNt4UNd*0oN>=JU)JoZ%)n})~UETY zC~#*CAxPI%dmNmHO1BdupoA$!S8$2QB`HZ!*GN^(-Dpo`wpHG(nE`B)x_3wi$nBWx z=Q!D5N$HZjhrh`Brh>DdkbAG?yDubXwp85EDeQM@`bt##U`eWarLv#4NsLY3bIi${ z-vYf`ALad&mdtqFz!c4QyFN^*J726rg#IE@AV2b%kuCE2*cznDp5*g-f(q7xeB?xN z?njk7doCXoME4Pzks`DSbRZa4cO;ZVe~3fkmvEl!xic2Z?N@tUZ=(GpsVM%bk(`V+ z&$?yj6+-2^3JD-JUqIY?u5Rwj39x4N%s#IoEYqYux;9qoTT#+SXfo*@xNe&0!;cl< zxcd`tE12E|I4V0;=lO*%i!TiUr^QC_{{W*AeNtX(2y0dr`)~euMU8bL}$5&?c)^t@T^(G*6(Rim;Zvv1ixQmcz*ZwC8xw9^+ zG>?S+vk<0e{8VOX2I+dmz#O5JR53?m#A@rRo**HQO<>IXgfpF-N`_p6o546aKeZlr z;kDIc4-fTk0>pi+`dr|pShxQ0VUkWnwcEfpe)wVJ9IR9j1c6!ki5 z9Aj!{>J>tUn^-$e+4xFu$HG+b+=II5HuX3@AB+1y7*3f}3xJF_Zc+^8uay4)rXq7{ zrEBV!tyE<$1GnM^8`BC<@}E@n-49U=b7-4dC8}yIaErWr)$NmS6}W(slok`v%( z?uafvMLnm^78Ub4LCJ0#NAIqG>qnSn#a_L875%WJ&ew+go|Oms`gVA0D` zIgN>NauQ`J>@m@!X@9b1~uA8O+fNB=Za0VgFt#JiyI~W4%S}pELngWNx zEde0Mwc6z&f&kZ=JAhvWca4KDGR8}H7c#n)O5ET+WV_~-cQMBgfU1>kNkRujPpXgV zj9l;&IdwNH-eS*MvmPQ3HKNc;Ojn%k*!8-No;BL0+e2b|BqS#}2r79PPU{t67zb2G zjcgp_gK{%D$MQl&T2JxWSx6gYFA?pGpK{?%<*|$%*ROUzdBumhY}?U6J@W83GIwip zWYk(IvGy8Fq!dY3<1NG`G2rld6(>-?nw4KzOa9D&A*75NautbmB61T|X`JSt;f7}uW!5Y|( z&AUo_weAMV_}MvVy`te&QC{Ur(3Z(t=m}CsT?W|oUfk^Qj*LdrP`*|3E6%C2m)%}a z)1X-m$lQg;d|k+;=7>Sc^kNN+%cP4{^nCBr*^ISlQeUa-8dI%kI_G z*9w|^I^yz;xxySRjpy+1I!-LUz zvX-l>)cUFjNtqzcW?m8J5tdPUtD|`0uOA+9WkxrTH-En|zTt6cE+DI_60x{x?trCb zU81tKBbKEya@!M^O1JB)2u&3^K9gGNB`NC?+Qx>xx~Zb1T!Qzdb$x~r=^?cpMEXOn zG*Q|L>QWv#R{sFZHc2u`@dVHBflHLv4HQxxeSY*djq_JXlbBIg#!XV%n^Cv22%en| zv+;`eRcW3+9TQo`P?tiMbuypd1jQ<ql{vG;&uLhD(d^0bhtn|TW~E&%&chA~{Gu`{E`E;T7v z;q>Tg*Z%-Dvl0wl&+@z6P_EBeG|^4 zRY^+F&eYY0RM}L-rbK;}fqQ>J0mhEyUzNT2HI=uRPjlSYL?o?iQNOC7k7ZUj()~8R zsFf*T)bR#&>nsmcRb&*eJ-Cf`EAut|^N$gB2O(PXntNB;S3Ya@S23gEKFH`l{Q#ZT z-=x>o0_vdW`y9xJLKgk@-klHm_2k5E$6f=hpL(p9&2G>m;S4^;e*mmD5A>P(k5YzH zcH$Rzt3;QtmgAuFBB5WVHkiAk3n=*k`&XIw2Y>ebuKtrqAN?coRtJZEkN*Hr%XdC; z25orbt8-X>AClRw*YrJ+(#do*Rn1gBk4d1ivQU#Gp1yDm{$e2hV*R~ohr4joVM|?)K#x78O6wM%rw{s}QU3tuzT0Tl%AftMKe-~- z{Um$Ry`^Zma>XTE?WCmz`OLsf<=CK=YPi*3$?jc*kDxdhS zYW=0a5Pu00Gn6EE1&X^TjBKqwD$L^?==};1J#!?=b3V@r#l^lr1?Ja%rd*iZ<<51N z+81o)0)`?08LE9Uxb(I`+CA)8c;rPsy~ zrjU$*G6zUR)G~Sq;1s6S!^}}fKZgc6Y-B$!ZIni1@pgmS=?HY@ts%DE*VbUzS%&-O@`3ZO{KIA5&}~o z0QN=vj1AB%{kV4JE?-|NrFY9}zvjfS8kDyKLbEfOoR#SKNlMr4i--!(&dQYF;_PaR zm^jCrI6k@IPTwtJ*J~?JbRp`RbtRMU4iuP=GiD~Xb7@+ICvN3UhDtd{0u*!E)_GOP zU9DpFJN)Uh{v}h`mm%5Kw5q0#>0^0RZY9tsB7HfJXGjyZFZ$M?ADOxzX!g@ZW#(2F z=`wC}NA%=8%|H51qwZ3Ah(d}EsvSgWYO!I6^)$x6grCC%r0K0@`dQ!|xMrD$E#148 zvD>m6E;nSPa}u@PH$6eX7pw!zSeh?UWX}XIn*6bnMdsy>%-tElN-d zkQ+(zj}7juPDThruAfqPFLCTX;l*8EIy*?{8^vlpN!r7!>9r~|`zIcK6Ke3ip8A3! zqh3F^rIxPuAd$Ylp~V+Hn~Z6*6I3<60cEhZ?Tk6zDhAE6`$Ks~MvA(XhuL&Cl7yC-ZbExym;A2jKgu&l-O5@>^)Rh|9B)CPIl=n>cQYe_wPQs?wV#e?X9 zk{S(EV2bILZds7hZRX_rU6c^EjC?Q8_Gn2Qb{DlGvs1}=QLG~#z`soRB4tibXO|c z7n@x*T^oyHvX#WC4mRsbEv*Vk)F~()B7NY(<7ox6?e+^y+YlG8Fb4|sxuYM$#>5vW(!h&hVM%viM_4f37oyFOh=+F9a0 zR;gJ_Thb(pP2s~m%`fn?P<26&I(3cGmQbvAZve?PXjS``aonp7xn42Ms@CP6^=Ws~ zxmz6)TWbeG2$)h7HA=>c8iOdhwzzDKs9t~3d~cx?PU=c}w!WQQyH`$t?M>U5u^Ln$ zs!395vBzs9XCb7?9@xfZwZCBXek;+j6&4Qm{{Z+^LgMS4_NSBh_Z~GHX|A~qnwp^@ zB3oX$ve6p&Nm}{A7lM(M)-CwK^{4lGoU?VS?w_7&C@5$W{XR!qO5OC)vgJyJyds4q zD{vjM49G}CRxy7}D>p~X;;Jo8=Jk*{^A~%Iw+Bayiz-TII96IpH)$j6gQ9Y!$TERxpKt2?yR;tjuJ0A5s^wS4_I9@sxQ8##R48lGVqIdM z_0_p&sSjrAS-kkrdVAD!lr@yy(}>$Km+7k5y4O>*^*X9sLXwTZs$sx2KFwmnq!pbO z;KoYY{sqM8sqvPs58?NU%H|5tnu}_Z>Ow#O38CamPh?Tvx{pNRnWAyz7QB5|vgJh% zYinCgZrMUbif9n2Sg>B<=i;-|Vgm4W^AUZ}2|z~dIXty0nIhgWBQ%`uMy4X0w1Gr?i*OEu1FA73gVFgE` z&`HzIQ62O20Tqv6sXPL){hD`cZMfih2NC7>Yn{gVf3Bmi#qPAl3pAGnEemp;_V*;M z3R-s!4~RNNWt?!5$||<)LDbW- zw5RZ(tOF#Dv7)udhg;BSR(9tk=1t8`D(m*~gz#N|7v&xV%2oDH`G-%Q$QC-4%H0NjAvtfWDlMQ|IncUD%lV6QDo zdvOb$xOX$pJfV|kcs*7lc;zgiio7D!&Gg%?_LkmSo~uD~(o$B16v)jp)<6ReXmoA( zN~_6oxE^6jt-iGJ<`vq92=gZ*V^oR$U8;Pe7-dB+EFd(Ny=I(9NmADF+Dg176`0qX zi_p`uuFSEvxYF(`74qE1w-d!LS&OeOpOLw=)Y4dLnslL7yAuT>vBarA5M4^oraF8e zpBVIO;u<+d!D2gd$C&e-&!?x@t3EwIE*0!jQ??LnKfx{P*?rL-mPKgHjk$JEoQ+={BL!0RJ955n1=E*NCY z-fu`^Kl_iTr;Bc`qOppb%^?2(+v)fF2ARs%VU5(%Rp9*laeE5GE=<%_HGibFxm|cS zCe~KnLfdey#U;WBQsu1Y?TyYt>Y6u)f~>;gnFD!^b@r3{(|uVVFC!FhjqCC%>VDm> z)hoMDJ#j+l?N=t#qjiA`K-~C1AHqt^kA&kq2rlM3g|2f2C^k@-#%*ZiC-Zqc&&gQq$Q%~X~(asyW6ZuyBbv|pEM&k%G0EC5mrX?AMTRdHYC@Z=AQp(7iZ%S3cVTHRZ#W8EO9jAsa9FdA$b2poz zy;-dJHAPh|-8F71m8*J@Y^83Y-Q~c96E#ebI>v_j&^5ZK>?IPkI-Z{O8PstPB3HSZ zQDfX^dtE;oy2UnQYel-S)Z^bU`l(rJ;?syq>B(A32dD<3D}=&;*L9+`Yetl%6V}@jyVG!r?hZ4HCm}Kyw-)3I?HU_oov!tZspdOxvIoelu{{WQM@2E?jo6OXt6sYJz%*lpaKbgG4ai~iaC64y*?H(2N*X~u*I^?|9 zk233)J4eL)eTe@6DO6VDw<;P;K8^L&J!h(2wc478uC7(MbVan%dRtx;BE4g(O$b|s_1Um9b>lAEGU|pBnu;8XSZM?Ouc_CvgNA!FMhu83#M~FHG!w<;}af zBjTw~)dq_;Z=P>2=2i5r-mmuiMMurssuc4{ZA`7Okml4K$tl)iV@+UuVT{{Rn+0u` zFE^#n+^B-jd!@Q_j}qQ7%#xLtHrVL_yVIfiBXMnn(mX1_Qh{Mo85dz51iyzu=WDAC_C8tQx27hkSP-Jf#o!A zsz_pCkcz59cDcP~y<9EyR`=`*x7;h(HuXA7^y;Upagf`KM^(45B#?TEh~W?|)mT{$ zM;aAuIK!1V=aV`6hwr`ATwQ;R?aQ~@v)jL?QtQaH+vweCl_=08zM_{lO-Th_CUH@e z>c<@-3s;Jo-SUGm`&NN+-)UQWh|^6Zd}*v;Ilst#`x;;_Hjt}aW?!J82fb9 zxV;U^-CX#+Jf{z;igzd%Bs$i>M&YdV=wo#y)ihppZDPq9O=(5lmXD1p$0M%k-6I&K z!xpHe{#T}Q%X+CfV0G0c$ElueNi@Ze#zVqt2TwSFT@k{nXfbSX)znh{CBz(6%e}~B z99@TZC35B*i!`-6S8U6yn7zuTtkhA`uBxV;_O5TVq=p%7N?V0wbOL0=cX!WYHqlRu z!L&=G{dy%T>_fCmO-Xi(J>;%HTMJuYHDtBf7*f$RosLz$;?fBnKqeh;WqRRWRNKzr z$461lNeSqz{{V!o*v(1Adp@+}*%c6-zbND!lIM|AA#%~fYNbshE*T9|ya=^}w�+ zbC!Dhz3qc2#)Iuu%a-{EnYkkjRj(_|kzvKyDHMELird-6ac0__q^dh{-0X*g&MtAP zwZOv=s1-i7=U4sNJla-+)u-|MD^F6d_H3T(W3YcB*&7sgXPiw%%}8kpOR7peG^cPA zl0eXO5ukyz3qI)qUqK&28oH0?KHw>o;S7rfwZM_ndTQPMhCKVfp)F)*{{T|cc4Whz zQU3s^@miN4d3MNcmBaXFmmMR^oO549A~X@*v7DremF?E@`V6-RW-ubLlY- zk{45Cw%3u?l}e#WROF$jU0_ve=diTuvAewsqE2Y-yst)7Nl}+TH2rb(_m+?!rfFXV z`Nwn)LCvnqJFaR3Ek#pKvi8TUQFxyOopj8vcU%UGjj5<p4m*#xbn8GSuoQkA35v2#%Ts2f0+GR7|R4&+LnoGaEprwr_0L<5)!)NlUh* z)HhP$)R4C~BusflL&y#)`XkH$@JfL}#iMJ1L~EogomFTXMLqD%$kSyQH~g=F@Nq6XzZLQ>+rgoTgSA1O@wM&|n0-4?dP ztjCW8UpvZL$0->``BYp~8mCW;&sllt5@E6ZV^gqC&*j?z4I7l|r1dH+^!}iejx;xW zei*Ni2G8s&&Q57;{h?A$QL}90YWEgqy9L?yoJUCoI8p%r09Z$>_O;b70Pzdv4>7E1 zV&lc7Q98?I>h)GAEOw{vHLhsYGYu`(sFR?Q0gqvRTHX;?xCQe>J-2;)?pRn!_9;&k z?AwxZeYM%8YZx_<$xfkaOZBB4bZsYUPrYAcWOBY}!q0{g%vY=SxBBIfxjgqrk*acP zo3(D*t*UbGl-sGV4X1ON1%6{KBK?gQ(>=fC_9qa*%Sw(a zu4f-ne%G-I%C+M7)mI~4(YU7dRD_|Tuk5YRrAu7^0uHe3tI3GwG*?&o+en9v7I^cI z)I&Vvibri$7u&$B3Q;Mx+rPuCgD`ixnJHgXvsB0k3?Js-{y||EOHP&7cXayB( zwNV|%D5F!y(Fs)gfCj&OBU!3wU`B0s%4f=w;0Z|Rkt46!I71qGVr2KqVEWSAK_^mE zFX@Sz+NT)_6e=dGyHmRI(6)jPK&cI3!Hb&uCKf|QMOuaKXW?*v5P6ZPKEpp4UO)n2 z6tau&doxJvKtjeGp{9Wlx-)kQ6n0LPS@^PhnG zU{ai!85#knK6=g&qoMg#alW)$tY5>V&&3m?Xi~+U)a*)kb?}BfXq@pvgt*foMM_W# z^D>8~q!8K=p|na~5zPQDs%vE^QmMqXS6D<_U!qat+NDvtTLPvTnU`D3e%qg_lAF3uZ@CB$>xi zC)7DD(_)r_Qk2mnX#U97IV&by=C5OzMm1k~uS0Aig_SQ*kv*xLHc5j4B_*p&^74g( z*J^y1yFP5LptpjHEfUr0tsv}elqmbPF@tPI{^b7vC|<M*_gA(+`DC6lCo_{R;_r= z9m2}}THD^QN_y+@5#tt@4CV@&JF>@6DxZ-xM@LfYMUWI!W@Y6R4`mG&rVFB4k!Z~e zs|V9cyd@}Q6y0)WoqLKamLW}*o+tfYP!&J&~(Hd?P0M^3GFNS_#9m`)OHPl{=EkOyqjNF`ZSz#TumwM4wGE z=^^@{_6-$esQ77Q%6{m56k4v*fS-SyCm>uZydZ-)OnC@zOeqV>fg|M+xrsT= z7kkdYd`w0{k*=sk-d^B#olb%|O3l@c;6CN5_$q@SZM-VGDWQ+l}tFK!rLXW0upHtg(^4%!G z-P=tx&Twk=TMUi77VTkh_(c;z>pj|$UX@k3)*K(Wq$wx#z@JAWcL>(sLhVC>^jhvG z{{VR0{{U3N{UiJm8_2vu^;V0CN&f%_Zulzl>#P` zC#*#sw7la)M3<2{gj6rMAEGySnAdIG64fqah*V!1x+xA*7D58VhO-XKNfRE<>|B>t zpfuA(^DOz8?5~{X4sBNRfAHKpcX%BC9NZEQUDPz{m)xj9<=xlwrT+lQ7JtZ#z4bZl=Bi?+1grr0+S)Mh@LSL3UH<@* zEdKzJF>=2C`zyWM-woi8hmw^My428~kVGAuayN1Y>%Q$CG-8*&ot%N$&puvrJH9;K zB_;m=5~&G(!&S1K7ZRkT~d2>E;4vIO}8RRu@g z_uy&@81^lLjYIVI{{R(~e^O7TR|NK<#zsL3T>MMdu_NFLPVIR=`&UsP>n$Q)^$4)Q?yGYLRjWMK!hhMtM~C=xKeaKt zy#CU~+y4N0V3tCm`Tb%40MaUde0g=p_e%9EVf`iapY*A!uKxgKORb_|C6+g&m1<^0 z#-7;vYs)H#LG>?+{PVREOj=by?nk~}X2bcfEyr24(7R7%ty6TQv_gsjnBA;g<-E(t z+FW#_)CG&}kM*Os@^&J59N_0xv{UyPeOs0W|`t-2ZWXev2xDq zxVd0_jH`mkU;hBDI6FysVwiwa7KiHn++8k2nnYn#F1+ji02BKiPvSC^rhi;fk1_80 z+=e|B`~LvdTrjoji-IUCxA9imqw3GxwJoBarCfDFT2o`vQEBAN`OK3W6Ptf0H!AaM zPbjfwQyw&efq-pBDWkuxbB6W=1FiR{=Z=zVwPU>orgv(<+u9pij}oDHTyq` zX57B5UDy1gs%Y>k7MX5G)-=^CGVz(;0ngtTz*Vv6jI;j$T|0ZI8ta{Y&&5SmUs=xR z!j9H$lU@qekfQ~v-l$+=hcg6^iZTf_7%EO@1vcuU1b zAUAUfb0!DOH4$wQe&6f@iE`dyBL0 zr?@YS)unr1VEMIzw8t_TYS*-CD4=%xNbUiLg4K#uuVK{zvH|bUmVNqe=?)Yz1V6#DIzAn){orUEwrei-@3zI?Na&~q@;RrR-) zUUb;dTTVFFm(40!R~1-SYwg33+692yt$7f zCt4W`U$r<Ec5rj%r90TU_q z$B*83oJ^Fl!%$b+wqNQX5Vh^OZ^Y1ccM)F%y=ngddG_&HaHFLE0NY7y2bPp3c|a=M zq7pPs51cKxaN18GWe6wMmhb&qk_d~%%XAgk{tI?HpYL~(cqg@LetorLHf!ziib@LF z+&xtfHqb>_LYCWg0U=5#-_)Q^T_bJ&H3M4bkZ93W@+WHM{{U0WHo?vUt}Un)<3)?S z75?#0bNrp$Clv8YI8zX_zy(fAtgl5iC2nkE%Wk%siuZqO{~G<;?2M zU_6IIRfsoJSnZ z1dSF;W|sF+kXIKb6c(W=N>Vz&jgUxc7rWSpLl9}*HCIHCCNu(55jP-Gi4bX{O1f+3 z9M+~ba92j$4^Osm(;A#i0^r+-(nDB|g*)n6xK#lMq=ca;o+bqqc8hHEARh>66$*Aa zH|uG?hOy@Y$judlHc_SB1QmN{`l5#Ev{#mTIY;#X1(D$tQmq}i6hzVrHA&PN#C(-c zk=07nrlCqm4o-iF1MG%6Iv_{|H8N)$MnP*!sVYS|3Q~2}N5&~G%^b9N=8jbP zp9NkkYf$y7n)HD1Oy#LDQIv6eIYPh3Igv?Hs6)H~$SxHsoiz0dCTT!&M31ya5=Xd7^vQp{O*polH6yfI_5fSF?UBxh~DQdv?dC7$tnm-`ApRnhp!-3m$fK zbi|1SfCsW*MUBJtRB?}m9=@oW7j@TZ*X)VyPO7%^VzVEb-B;%h5Ur+XU-YFHFsgT& zq^=#yYB79@g5=V+Ew>U?BQAz|$}Hh!RYl_sDmO*@g|)rgA2U>UwS!PQaqX|2K1$u) zhBHQ$I_5Qh(wdhdZItdFi{A=g2?9!)Gb<9rUfP^>(UgB}=U-9r{C{?(de#c!9R4fl zzP~5lM#lL9qQ&q{j2kC8B%RPG+e19OOYPQ3ArFjq2)>1wp#Z5Pr{A4(~?%PLzyyHQ(iplSIDgf`o? zB_NM$fSB-4H*N_ru=rJbuaH>q8!P>5Ua~r$l(JlECoPRCUh*X#M_}5@7r$m?q(vuU zbl)$IQTQp;70i!4V62q9)B2Eh5Qf_oK{MGd_mRG#qm>VCPALmB4ga)k3qKn!Cqr`tifn?of5O#q&hT@)FUM~&Q{(A(lCivv`JL_ zMB(jqS4ebGwf&-+3ZOLs3)k#|?Z?D|kkw=sSdF=L@Pv?)P~^|Iaf^r$@1o2e00a$R$I>MGj{Q+o}R+tF1EbwsD}XR42*VeODl7xqfX`Oo|uss`u+&qHpjt zx7WCUPN!a{#LUHwwZBP$z6!EgjFR(ss*UDpX z_xZ%iwXLE~cC{x$vFA8M($JINtw;oKpSn3n)}*Ja6Sx`vF9<1)qL4-tj})wxVoJy@ zEbVazP(ji;;J8s&UJg>Ue#w5ZbN(Y>;GR&pvt-ShLo}Qn%=If)=4uo|S+d?-)VW6S zA+{egQz{@CrT}r0+1%~aaSJDRWfji)A5Tx>pwwD3OgpeXZN#(OdZ!r4F+Sq$_Pgzx ztpzSq)zD#SWTYKZq!Og53rX6fkfAU{>#lB_E%P2Wr{apn+8C^!)4wW@Nr9@P4B<>0GI*sBvVgV{EdODN2;igQOPghBH!V0?lSazEiRyriQwnOFbr{R+Fc;GFP&EX#8X}Wl%XKc1Z3(RME_=>q{$fR21Hm z-w2%5@TpTC=sFFM7o@M94)E&DXEkK+E~>h{$5eo7t}L2*v!sY|wE-y+<*aJ#w2wt$ zj-icnhg}`DcEQ|z)t3ubd!v|jf{j3g&8qvl)arHdDM#pvyMj%?3e-c(Y~npb@2Brf z-o+>>Y@O0^Z9YY{n#%m0Etb~fZY3#I8{r{n3Lm^d8o?JrjX~SM3x}rynOUWS7_H?i z!ttz{s9)-9t7+;jRg|)Brm=PIFcQPaJ3uBtZt;tISAh|W^!VFtq`XaS(%PyvSxHpGDN%6zV0K6&oSGL2)^6>9Fpvdd3|VYUjo4@+_fkI znS<;C_R`QCx{d(r;I#|tC48y3_RGV(Y>H&iR_2%IlTM0d zC(>07rkY@lz!fDts-X6MVmDC8_iY!f< zYfTC?2t#y?g1v%fTbxpi<7;&U+ZMLnd9nqyE2iDK%Udaub|y#RlBQHiyT^!a;ps!L z^w-v?Ev%)UV=RMH`@eUn?Z;IK*l!r0HQjQ4XYSJBd&jLeH8$H^onW-3_O3Sst+txB zG{V^|scp2a%1df0LO>)-SO#fNGSfk58Z|WZ`+pS0y3skvc|#VJ@_QDXa>CQJTy09H zYAP)3TWOHB)3)n${0DHZHYAlx$KY52vUKSHcY8(i$cDtu#_rT}h5)U!B=FKE}5AcMZ5yzhbyt z>M0kLUK;e;N~FJ>QEc$R%U{ByF#qx?) z@>>g(bTpKfWl^TNs!EEr7adV)u%$K&T`>WjfMZTPtl0IFyhO5}nf^`CInv=6ew0 zYwbd1+F`zP0NH03D_<^E8J&JdOU!p3Sbvb|X)gIEAh^wKp0ejzR+8h*S5vH@t;Sph zGc!rhr1?HpwX|py18-8av8fe5g?*`&ZK*}N+gom>x6>gv3WQZvR{AOVl{(u+q5NLE zWOYxRIntUbB*#Wy%}rE?VdV?ej>%`osqEH+O=zidv8%|p2JX((spQl&U93ZZtt>4e zOr)pbGu9i_b#y{pS+t5!Ef40-;;bkwx!ZIav0wAor+NtjQJ%sMz+8NP~&+_E_c1JEj&Q+nYnpA+S)Cn#sPt~z&? zazD&$NVQq1t~UA$s_GX~ZfZ+!F#9`Qd1)wY8K8nBL>SlUP1G_xYwP=U-}a}+c4@^q zo0A+)H=NnoeeY+qWs>N)C8fYodG4*-B2e*4{Qlqm=aN%)EI_f74bY=m{Q!Km3> zZqvB-r-8GqzVFL<4T{NT!mQMHTiK(#(B9OCkyy&`C{E;pcDNh>%*;wCJ9h&W zjma$h!CrSiamkRhZ$ITLDJZD+x{#r%=`o;`Zf6x(hPJBe@l8e6#gb#0UAo#Xu23Nj ziboq-+I>K3f~0hxPdLft;$!0=m8Zc?G;)H$QmcP|6<^I+{f_tfaxAV@T3Th>JASDs zaTNbSD$lj2ADVxtYSTPUp5xmlxi_t#NT zN`yCAR)iI9O5XslYD}ln0e!rIgtcD5a{+h`&?jaqiSU(oaP}KbcQjpi&yE#eRa0f; zMNqu`ZMCLbb#$JqNjvu_?Lw730VWx99IcFLQlDyIe6AqqsV&sDF14W-FU-`6ijXVz zs0YA(U^)W+A-oOJI_s=+N#zyY8w)@>DSx-yE)$b;KNsVilUGk~z^~W33Ov5!RbTmC zJv{|O!KYkuu4B>(24Pbg$V^Tu@)JF*CA+JOJ61kG9Imf<++V>qULvvIxWk=nd0o#G z&hmU`7~?8WK-Zj3nxTH3)UC3o6}{HRj?-HmJSjhma`Bl$m%xeDacwcM9eZDN%w27Uqa6`jj1EgMbCnm=kg zx8QTc&`BMJT!TG_eCrDb|dYozggeXUnQZu)ZzGFF<7hDZcRVMb7k5p zhgVYZLR$!D)!t>PBd3&R?EJxDXEU0%Sx+fuj&7SIuY#pr>-{--x3!)$w{i|;f3e@O z3flH9)e5)NJ}OwMs6vZCwNTmu(xQSUlQH5vwB5Dd*7+&AO$0iN7v9~GdxeNeF&w0U z?^!zConD%VW8g@Vl$|Fy^ae-EzKaNQqs4FU(6-FGepB2N?kiUDx&W}PB&F6=5I(?| z^KUYZ85KCWYdpBR>dA0xvwsz&xG$)?73t>GL2{~~sX+e#=?O8SzZGKv@CzS`mZ9g? zRELam1;-AmL$t?xWJsjx6qnt+%a+m0NrLYQvGP@_SG0E-lB(bvdCy&?leLfriHxn^ zm$txYC|9N9JlVZ3-k~F2IllEtT~SosdXT4A{od}L=r#_@w=4~!8uQG{hR8^D6otom z=6A@?)==3msiUl>Xt<=T=u#v+C+8X7<-u8FnjH4j61F{~-1!Wtb6ENi7I$|MQ-2w? z;+G_I*1NFjO-qE%U=0ttT_e={L6Dnfx-A;KBfR98Upcaxx~i+(+)|lw6Pmm}=-5aq zR#+(QtKGuOa}~DN5}Kfgo7|uZN*zf;fu5ZrH?%oeTe^itrR1f_6>d;EsOb)Np-;#f zYNqQA!y%=>Xy_|!n=zZkbF~&cg-HJLqL?MO+Cuz7W+pr*6$hqdBDE^Tkw26gXqG77 zL3z1;@G=p~$-ETB#}!4Jn`#GYgN>*Y>`*bE%WE6Gi&KVi6F1tvkFkP^oPQpzASuTv zsft!Vw8}}3k1~2r;p;!Oet_XXHq3V#1zEXqRaY)x!R;QZmrS65-8Y8yDrEh_Hh2aG zSh@UHnRh6A^387rZRy$!u**P5G?b=Z;V8h&>bRBca5t7P6@SUCHgR~%thsq39lI`` z%}(LF`l{ikE|yLw;#xdq zHub5tuXW7_9CT_uM4^(A?f8j}o-4E_yKr>RCaM1bUuR&>SR(?982NAN1Ke*YBC3-4 z$dxww2T2Boi2R#-N$LColP`IXQ^xi!oFE!htkvgvT+5|s6&$$cmul+L^=;Mq`h}89 z&b+r3{{SeM*2jv8f`CzEZZfhw>QQy&P7R^QN|XuSsmgFrHOZWCY<_(3XWXYtR%CQWQqwC+88y zqhm-!6xe;GC-FR)j^GyxA8|4a`OglDx0TekHS{k<87EFLv zfVS2Svj%^zIoqPGm3hi3b~<#H)g59%vbK(t zNmi$Jl&7IV5{AjLbHZ`eq^+e9;x#^Sk&RR|O*dRiOPb8KiGGn1t_i1nxKk-1JI3QV zB7Jp)B^Mh>2-YCt4Ny`DP?6KZaL4Spwu(`^Ah}5AfFKwSG(}c?_aXvqHRf_xhus5S z{HtZ!q!+5i+iS{Q?ey=K(oDU(pc@baR);;h&}zpO=*cEyZin3mRbj(GB{mWL;3GrV zU14gZ;GDD|5Q0y<{{Y1bqkD*1i}eQPbz5wz!)+BL)>Vy$rL?E_%77oH z5XO+pM1IX<$+A?FjAM5!pIIo|E!DRW5012LJJZG>Gse^L5D{>(?$}j7Xt+b1s}hyB z#CZxEQPJv)Wknd%{{WK`)G%H_{K{s-DH|nE{kPs}+coj~?E}$H)>_%u%1+fL9B(bh z<{x@al)uV;@5LNrx6WmpmxV}l+xexEdV8xOhV~8wBr(cADf_<^q5lB3_032@TYPg{ zR1?({?om{$?x%fc^g!Zx&lKjxA>4k&6J^|E$`&@?{M(GODikFqZ>q_1Q(}HQ(8`ng zVo>2t3L*_1MO*05w(hH^czmr=S0&Rs{5L`JC|r~e*#pNLx1zh1?JQEYDcIUUP%6?7 z2SrUI!c|BL>SRwr4oOrl(I7g39JmmJQX~nSGnFk+N7yGF%DSkw;slPKa6J+SQ3j=^ zdJlPoqAn-sw12a^U6#L&a=*;zkx&-u!>CXM?b{)#5vamPRwo_FzAnPkA+z+;KTH$2FLrSYi4g1AKQ)c0pcArRt zy6I{%=^}&rq8Hs`+u)34u2IK|3e7>l+4Bpl#^`A7Y*DUGI@$q|9#H68YapvBL@m^} z(7ToO1#F)lV)h#s5#lacsF6uX;|Te7(vY30k@AfVq=BY^Wv*f7JkH&Arkhcm=Ragx z2|QJMn%@P-pwZC&sDCi=Oz5}4a%o#ax=v8ef$>NoYVx6~bW&99PT~`w={V;$iOsfY zPM@U<>K)tiifP$nBA#DdhD;NPfZ8mSb@H-7j6=p zIk*y$vYaoQu>5l8aHZtzf{EG|R^lFR=~|TnfJh*R1W#@vJAv*)Iyu|<&%CIr*U9a#Jr)=qLp-Z!c; z{{XN*^J9N`{iTez{_6Sv0KX(0g0z)MKCYwfk1n74r`o+n5A83fpQSBQ?CzAdxffi?91plBBd$ z)^5uG0FKtyywo(J`U36WkG`K*jw(%7Lv*k+cS}G1(PHP^*PY*4k=R)v3mvl;+Y1AOJN9(kw1}depYG zZ!)a0@2=ygB#wV4+OLJ@m42O6n9VLe?Pa0o>5_ylRC83o)?_#KMl9kkn({QF+u~=L z!lkCAQ{GHZV7V&oRJ7NdZ51gxTq)KXEl$`<)^(mQ_=M%{d2Uu{O3gxqT{h1&@y<_qH+C}AI;-iu%% zubOKujB*|>} z_F&zT-Sg^dtuFdRfz`QmKIr;O%V@dr<*wSR;7|53gY~wN{Hn_LA>LOo**Rs*R_n*K z)O3$kC7LCvhY4^gVH21i5i{_QMSnC*0lP04$6#Te7sy-#+reM2Qsk&g+G6{>{%6p@_#2|H8&Qqo2#rjbO2W< zjo_sunG&rd0cDc7B8%H^@fK7BihC2flbkZ9F6gAq7Mh9$ke4bFjiod3)2sn{_j7zm zi%TXJRx@8xv8=OeyV={h(Y%VbY7i8|pv)){F%^>Tts+&6Hlcm0r-NxHdHfQVr28O1 z>L)dY2Z-1dk{)@vDRYvH#Nc^JR`$U@YAw%HxP}=)an;E!wlyjzK?BEb=emJUH0+F2 ztD)QPV|gDT8hnQ{q*Vo-L@A4T1TEC~6?q8sY#(iNd{16&pYFa|wTn?Pp4f1_PKKU_ zhQ`APX);oSX$PmeH+H!A*}-|W#!6`pr7qX;jwwKys+}N_ufhO}Xy9lnE3q9sty~aPI8))JvO8oQk+x=L)!*?66=6^GJi39Ca z^RR4j%%8o!*X;C;e6>Z-GPL>?M^Ke8wN?}1`>dGrj0?FgL%nP2Uo+a{+qWQakN1GS zjeEQAJ?_jq)V$pe0Pb@A+QQwv!pq)*nrr40QV3Snbv;Lq*&g$8eAn7A7L&{u=f{`6 zT*;ZUl=Vb>j@bRGOvHY?7{Zxg-S|ed7Qo)yb(HH^64&8c2ES}&W$nmi)DkMR5AxRX z*w8ty1BzxRQPA{b(=zY3v%js+W03 zJKgY>CdF6#zWXksN>kIMG0mzrIa5OF$qA58N%+7rg=K8|g%@5a z%*mGCzwpv1F5{_M8(C9ntZK60@C-6kRC-I>6%T@?9P9AGs(4UwHTtbd^ypTgdVmkV z!W_#-1yOU{k+!<2Ud7wKPVhxq?JfrVPSfS7GlNByTT-)A_p5op3aw@~wh@-y?WMre zwzmr_1c}W$7{p%z)GOL>uI9FDyiy}JD&669rMIL`sy}oI$Q7bfOINxX7pOf{Csi%8 ztf6^8)psp)N>mwjtq!3zPOgz+Lt=p%{juFfRV0>f+$XcR{x6x*ihGq6E#j5oB9|LN z$Op^D8#SzN8iX`=b3*M3t{kb^om&>loTrRfamScSmsg9aMP*1D&4r&Ce(JjTMn^B( zpblu(>kZw!Njj`N) zs+Pk~S2o*-kRZxlGxkTjTQZT+e5ElmOkPgqXudgDD|-ii~qx%HTKdbrzuI^ znM?vnj9=(?80Qy7@kVwYAH(*0DNj|m?hJl<0L!8yx*BtEfByhc;O}08^Qo=TrtOX>^G9ZY|d6{&_GA>hJ ziE29r-ay%dffQ=of2B>U4X0OO80*tfHqPQ z{L4c#*S6TzOps?-;v10mtJ@e1Lw-81T+PW*%9q(~GbZ4n~FZ{$a$tm5-T~QLF9(+SvWX34iWG8~XnM+tGQu zzrFUYFS8`3*F)qRM!#fZr}@_Y-}_+`ujp0f;R@Z?J(N+U{EPUzhzN4 z!?qjGa97SkV;j6u+pl>40I5??M^Y3MRGsotK}X?~`jHj+PUsdd4REbiixJvxy^Oi0 zge)!Y+lBdPcV)=jd1B)`?qhwx@oPQXE^5nauDd4PZE@PRlTiAo8*^d`n@|Mxk@7LO zvC7++gWN#oRF{~En&QAK%luU@rnn_xXLQnnM&Kp^8cH;5aB!$p9Kxwdio>qe`AkFV znIckVyJ~?ZXQbg&N&ARc)tcRDY$(XBxWlyp&r0ofs9h}zTnU<6Tc|^uO3r|hDfJ>F zl1@;enWf5Uz_G(>zFo2GqNNm$xj}F=^=$!C-_BCBR)nQZ85FFiMGYkmj%uv*_UVxQ zS$%IP`~@;mAEpcsDh4-I4Oq;~Q6|Mr zK*R156y(vp8=YCv2qc{hVlNySl51&7g0u~FZ5(}L_bM~nJ!5TmBirplQ|YYc)(0uU z68J^UvO4?YMR!9HRaSaP6;$p?PEvOh&eZBw^|zR0zM_nQ-TBQyBTsw{2gnt)KMT*S zl?yJR;*t_&r$~_>MW+7%NIbry81CAcs%CvlrbA`9TkO^82vtQDL*APGb_1Y~P$eZk z;}rS$%dN_)4rse+E9>jK`g#|8AFF+(EmgM`tiPF7)}<9y#ZFf;hnC@Ys=}u;;o%-h z+NMF0rvSa1zKkx!j#^fWo3=laOAb;H)~#BkLu!^w9}pRbxBH)kiStX z8imVrz>yL3EMF5B%a=o!)7rN0h3>djRsnCOQORf&u=paFTp9yvy6s|V)-u=8{{T@R zh<#X@cdF(Z$rh_WEZ-~+2G%I)tCFXYH2x&1Pc=Ds$e1G4zyuynuGL}Ze3onNFD0)a zyWc6YVOvhep>tuRG&Y4FfkC1|i1X{`8oN{QC*xo65TJMOTYF~aTT^Yekc^)PsH_Q@ zT3c=U!LP8AAQ!a#NIshtchCO-Zb!5BgwE|*tA)F`jxM~@ujT&$_N%p3YS;k?ohY5l9N;6B02U3Eq8(!iG+$AFb%E%o%OTK38hmo=yQ$g^bL;F?6?2_5Txr2E% zZEEVV9I=-y>!QgL^R*Pt1-Rf_PE&;u@h(SqQ7DHd?p7@QqZ;>tv8vE7sT)pWdFN{w zZQ8r%Xn5lYti0UcMCVYt+T^^2sV)){R2T!VoJ-~Vfr@E&si(TCpUkLv@({z|5B~tN zzI-X`oyc$XS4!2%71dQ0m5!*$CAB!GjQ+zHZ6(E9-8exI3sx?SVK_$tcK-m|{{SI8 zynh_!1i8%J$JR4Uw`!QPs+Dwj?Iyx!ztd$d1S(-FRLF`OdvPVnlgb^Dwg^}ie!{CPSH}=GvIaU z78GLv<@;6rqg>JZJv}u>`J*YItF_zspM&`(mnz_%A-Y^z!e}d#PQ~Kmd8}UA`^uUK z+O$5T$(0oyt$+Xkm>JEl#B?QD!xLyLtvx>#S6i`W>#nAzwNX`7*4EWEPfUtU%UkuY zxEn)AEp=L!ppXZNfPo~dNuJiSrCr?1Aj)|E0FTdXuI1~mrDmR_yj<{qv1o;ry1D zopC${@ygDvG*%YS_3oLbZB8x1o3g*dC7Fa2jjIw-mg6YQ=Uvx@%`7KRf3aO)VRgLW z&2Sp3x%IxMuFE;Ij`M7fkul7*i`A^!FzZGv%e6G=X%DT}9j&Q)p=k~+Pa!F76Ohy% zu`gpatg$Q&b9Lf|?qdG{)cMPSsNw1CSZP?>)U^*#)v~5vpjV;jE2=#+w``9|7|=}R zYXkH~*jU&^rVOBdnkT9X5UC)K;lmrc8dYa-`UmoSd$*fFQcCVQjYYDN!W6KNS!jHP z1nP85{n2KBwy|wAxHa_MR!%^nsjJH_wv$v{MWW?W)xCrP@rG*@*a;Hqup~U7MipY{ zfoW4GGuR*GF6wg|?WUfa6SY=kmZTPHs;Po#l}S%UO1Ab-c_mGq0(zK)l6F!+2x0p_ za!ktc)+@PlD7aWHW!sG^?v+ichDoUTYBjn@k`XziX)ieEvD8gzY1W#l%Y&^K2dv!v ze7obd-z!aq(A6@-l;wxt^m03RDGk08GC`L^6T`Zyzo2P8oK@xI8#YmC!25!?WVF`n zRW3`I;1|9-$1U|t)~bUadmpW34Zh=%8*R;iP$S}9pU^{e5@Z&LFux9X)L8i zoEFreq9j6M!hJ#tCe$qo?T;VIt5s&V+iD9mbvG({cTh@axo#ypq`xiG5$D~+Ch6 z7e9uleib+YAWKk8z!!HDh`|D-9c2xhp5tCnvwp2^H*~5=a8!mLc^@*AsR?u?#ULyt zT7aM?0oTqR=W`0H3wDN`6Tgq`MVJB{8gly0`rs7uy{)3_9^ zGE;=9V9do%E(S7)WKsH>w&N2Xr?0L##|wvoO?dI3F7uq>~k z)fCa9)eodw`3^arcYU4RXsc~Cd~w1#y;`elDPCZt*0#zK{qhoK3nxiHnOZzCr%+rz z8XU;$-yneT#*+2tL3a~{+8fNGhM?_3i`3T<{Up)#Uq-jDr+l0kiREk%9^TOwaQYR zr)>bG;*HKMC~h$9z5BhkKas5?I*AFuPtOhpoJ3loZlY*3|s-&jx`f zYE%-HZ5je}jV<0r;^!G`>5X1qb^8MGy9`aOrfT`N!`DmR1jRz=45 zH!k@7N?WUSveSuiJCdZ542b(89M5js-?NrcP>Jo{@4Lo7niHu|mnG!)Ovd|j$Zrn% z_Y0Go-f#ev^x6VPkOD1nb`e^_BW8dg@>ywbCNjzM6$)H5DkH{Uq(zEig)h%7m)%Qd zLVp*sddEGqM_aiul$0sONmDb{7>`puw$4vf|_T{f4RPyG5w&^qHutNK304 zPHLB&qOjS<*$cqk9|0ho4gsqBw;%;k#S9wgQIN0(Sf7tW)Im zVIHNT#_KRXwe)Ak6=J)OFv_5JdY0-?Pqi%spVc25=7lbu?O&ihto~~zA5xENtcbkx zWfgG%ixnbQA}8?@WJli@u-)=l7Wk@nW5l^GZw33F`-{N12GwHzW2rYh&pE6`vyJc? zQkO25%bUhXX@+M#qpOQQ3Pz?Xu^35i-f5!-r0@#sHJfFmU=3b&3nn}6`k?W5VC%*$ z-05q&a%VE~FfZhoMltfwH;dXAqhPAF#*w7-PVMdxr2#1QF4pa*-GXBHVs6C#V!XpK zpL{PJOVb?rdG33vghz8V!i7fuZVKvf zuYK8%Z}pbUTb8S8bIuF7s55HH&gzSeflK@;a(cMP@|?$n;);7`M($>yGI+@EqUCV- zmc`OnSu)EVvnR%@+Pm1VOd_J&ai>~V)6BY^$x=#Blp7V#FkT;(!oJ>4QZ`JhtvQpQ zbV?mXTq7YuS#1gaAp%>vpC~3O6=?~oj2I=Sk_xtfb%&xH+9!}$T_mThqA7GKjWr4i zNNX{n_r-$55vojOto_DhADvCAUY;d=}~~l=)89$Cp?Si!@1gm_bOhTB4uu@ z4?o64c}{Cvg1yJI{{Z-51I9vb(*vhZ&?0~XKt$GEKT9bR>mTJ78)#w|7-sN{U& zv{-08FBLY`K=qd|Y7#t&fG%@3&~9pdJvgZ?>@KH22&b(6AB__eGrs*RyMATnGVX56 zd&uMl5EAw!$MnotaTLBp!By(9Mb{D~B3W>sXGp`|_s28Hh*#6xwzeIYF$14ZZl8Al z0JT9@UjD0Y_d#jw>*?!<*lx`$Yc;i&tM4pi8#*L!uS&Kk$REIU`{O%5b6q{Arh>lT zcnebpY1>Ci@AeP8>-Q@Z-y3~-`L2Phl5E*$6=C;eg|LLVit**DnUbG|Z3k&8P@Ozd zqxMB)xj7u|Ttz*UKebsi6>QO?LHF19CJo2wliuCQCbO11=;It2_Z2&ZHc!oW3W~HP zMrtZes;a6)OJev3Iy2lkeZB z-4G8lr*6@eG3+~Yp?iJE^S?)Ka*`Fro%wLzAUdLb7AUz2w%p?7sM{u<)c6fO@gscI zBj<_9r_0#L_#Ddn0wHz!Z+&36xhcC~{{YO{^#vH*i;fYWo7+1;m_IE2WjdBTfN5Un zyvBK-X5*=I53Z{}eb_SX%EymT1MeEDrJHlVdmL9)RbuC=8ppme%}tAuu!?l0ZY4wz zm#&l)t|;haq&#QT9~j!>d##e{*+Y*C)A$vRzwJgd=7hJ{Pw`1MUf_6&^0X(B`+s{L zs&s!FEWxi&(kv=nQ)#{Y#Tf+oI+8piRS$8<(^5HysO$IsD>XN6!dpn+%k3Y$ehO;E zaGo{gYVVZco_n!eFK(uEJb9R*+%o-8vM2KNj@EkA6>sK!<1r>EYj zFELjUH<*fn#B1!WHb}Xz8uAYr-cgToHW_ikFHWb_W`f~Q>4%#j5VjpbTTdoO@iw&& zY+LfwRBEb3?X4@SY8qt(l}ba-aMd+Za*#&7eBq95Q@M>1M?};?(iEpKB95nOeg6Q{ z10$KT(9Y1@SNlE8q@W9L4!U&_tiX@ylDx~hBOg>3<}bChb=B>({+pB}2m@6gD6(vh zB|+0|H3?FR<55)d)U|qwkqRp5udvPvh1;R!Uc)yse9e=TXG2-qDZVAC1a7{_2MuXiRjc97++D7{DfSp(-}x_ zW;G}~lSs4dl;e{TW#`p^kg5j{kqL`BwA7Ths3J~|zkFsgLsfNZM>6_D;C>*jLay#u$g25?9Ua(A_B)2}{(YeQ-P%44j(!YJ9B2V$?K!MJc)i{0iN zaO__WNOI!x&u(@}E-<)vupA@;+% zVMo$^z>jPcvinKM5FQ1-kKl@cydt7t%$CKudGuO0YPxREA+Nw|UwFvMI zB7bRvFey4H?%Y@>~;CN(T5f5?s1=k}I5)PGy;Up_a|RerBNPO4vFk1V+U>GrQ$ zv_H~cPG3l|aH!)I(X{WTSxS~n^-@VzhEeptmQ^tiV))z6w^nB?o$v01QFkw#pd>R< z^I*QDneI_YGm38YY5K~I7Kh3CNySj?307ON+}n+86_j;6qWTeMq*UH6r`7cmCN_kS zb&p%@oAAqeIIo_3`0d-qIG1e{;;acIeq2g=X(=GcMva+% zXVIm6#V=vyTk|o&L_C$u-I7!^y6a_*o$?7G4bT*sC(q##aLeX_S1B>wsha2IXE6Iz zM`-5$&FYAKSV$!aAwkMHrgHG})(8DYA~ae<+8O9v1Bv&nl;=)Er>o9#1z?OS$&Xg8 z#L}{m8~zz5pdJzDIn1P-zuV22_XmnwRcZ?gs-#$|LPUWJOpm@j9~$~v zfbG|X7+ucd4JfqovX=Q@fZ3R&eJwDi5L-t=r0g9;V>xLvmf$pfJ7}7?b}xEqx|+(m zl%=+cjDYZ&>l4X1*elM=Pn$wRo!@RAg5^`n6mvj zO-uA?T1X{|gtsN2>Cp88Jo~=zEWEZSloti=-c0wy@!2*+=?I1H0mFqhtvHcgKIQQ_ zi%>O9K-;OOO82L5R^3d*hbP*^(LNiU`5W}^UXFBP|+&TeX@$&jlVFW zxWQi*Kh&ALeaY7>F~euCTA#Eyq3|e^= z>D&|eOyxg67-@)v*l9%QXBGHbVv3zhTF%rVAd|T02b=>Ge9xd(E}5{hrD|7b{f-)? z&}Vu52`gAjmdkR{G^7nelojJ1kAc7jfafnM?r!7ik{GoxQCGTiDs@u_?|`UEv4zx8E4+K0S===BcNc0> z47E~^>w#{VU7$qu+Q3{w`i=TacB_%)y{OV(znxfYWHtAp=!6$lNK5LZ^wtzamI_LW zeWE^r?T+V<9gFEWRJa|N<~N?uNs#Wovj-@SoAIKn_h0vBYR%U3acjE7)H`yjsHsyc z#pSTnJg3TLn*RU-sZKnp{i$m-h2_Up^a%d|YwjY2k^M0E8XKdIv;cnOnveHsFUTcL zcRfgVb@ef~f$QP6x+MPq3$h$ZLOcDro|@Xz{{XxAB|1;{YVyLbkGhHkw=uD_V|5>- zuD%>QV$r-x7Ekuo&J-59{{Z!W1$Dbp`U=r-KP~a^E!=tWSv}j0?hM^6bc|Ztqe`A- zDn`|nzVRTXWN9d~h4xmWpg5K1{nh7=M=@kmW0i`sIeALWBa>K@Ej&>$;M9OLl;hHF^9+i{Hta~M@NUcU!?<&$@3S>y z4!;l^`$(@9EnweKpQK}(D(%@#6@}+O^$WPYeJKi;jOgVrwlL>2eCmccQF;WPPUfOK zva#g)ol*BFmn`nH>1oJ)+fn4%wr^+3xR%|=HP)q8s?|{a3a=GcrfLH#2&XG6OAVc* z024ANGbk1L9Dw-RM=;TCta~dVZ)eebay7-kkFX`Wtk38#=&5L{#3&Tt+!Xz;;zzPx zt0oAKCz1TXQ|(hp+a1Fukh)6yQyRN4`VLenC3!9mlAR=*WoRFvh%jdE2Z!E;9g*I{ z<$R%JH)$@BYpG$eA_)MA&OAEOTCdSeqbOCKe{bgDA_;+ctg zLbg_wZ)x1`8-*X$aNY5MwFU^=sT&BCPaDFyw(cHcqYKU{S!;D@s7~FdT{@U^nzhzf z&5$N_MFL1AL-6(1P$d#uCqE0ky$-KR@T)jJGf7AIz?2{ z1*L(64HVPGloq!WKJsciBag6Q`-sWj@7U#A*n6uz{ zs8yqZ_#xYs+KE>x1qT~&1vaUTstS>q(nn5_B}YetJWJFCnZ#IEV2apSegfS(`5(mfxxt;B8SE6uyIS2^6F zSA0j4@%ya}MFYB2zLf-|j}y{1GhG>nC|D~f4ZEuCledJiT9k5ig1WAqX4g+v9eL7& zExW>pSk!j5CPv5!3n}IndKcUBH0oCF)N);(_APYON;_!-NkQo?sR<*bdV3?~UKtbU z6`aH}Mk^^PnM#2&0BbJ@5?0hJ7CNCnuI{Vy$5a(PsXG0T=#40Zx?;>8ZKx}|I6Oqp z=`m={kPU90#U101E7e5I`fC=}+p4S4#aK%sceH~&0(!-~V?~*{eyUi*a;w%IbELZ2 zE0;%GN0p1D%G;a8l4 zNd1wM^LI>;Z-YYJc?SS)Eyu;j_N%d&5ams(oazgqQudCKip6|{uSv`w>VTYA$3-=U z;^ygWr*cRA0{~jEy5M_Njz#>Ir`WSsI9V3T<Gl}*+c@@LJmnrpD;@4Ge8+I; zQjhm(683yiwjbG8CJ*Z^P_}|B^dyd~ppUg8F_Jp3D|2ps0FbU;pajZVb@p|MBi%A$ zqU-ygz;zqG5R*8vfuNONG5A#z!#jk|IA<&6imWodHtLZ^`ishK4iyBQ`+hG2En%51 z*M{_hjf!)HRilun--W zrc$Su)2KSZmmRBZD+5hv-IJ}mp5)cyl{>1T$lcMWn6etYpOX0R9J^kwC_-)(^p@mX z7UGa36*?lHi_G}N)uuSY$~DG{vf|*%YTzoP(4G~(Ot3CEl|!G^c@?zjGad^GSs!c~ z9WNDLVGc)G;|8uuQN(h~c0>|43Qt&4)$NyqPs?}MVs_mTxI>l-B;$qNZOe7K^coi# zSWylFk|pD;E9DLek8X;=Stb+dllu7FJIEtzknouk)wQWf! z+tvVe)3^n6b!vGrl5kBMW$KE$tBTNM74e}m`v8YBSCvsLNCfhC3egpoEDqci`+i9fodEn`2C zw+_2yd=Km@!LR7smv5;1%R-N+t+aIg#c<~{;Z$_!{$E$2ne*45wR+v_p50(4($7Zw~fCZS$wD~NZOP0b*ifbOUyY8 zfcy&bHr2&efckcwK$nkAJmO?pwmpmTZI6h%r{Qe#C45rjwrl+#=k@C{X{s86X`WjD z0J4<1xcdYLl#ia-z;3ILD`#t+wO7**bsf_1?p2O*PCbg}wU1L?>`Hdh<+_#H`z1nX z+!aYuDo9FOQi4>K6qD_Yeis(z9%l*_VV%v#CXF=&m!)j8@ z$U;kv6R7j)5nD8(uYg#K-(D?vxMLXZYjNWWTmj~pEL|L;t7f6Mo=e06pxRP*N?v2z z8oNVLL>-&CT}4xSb>phubEDD9fLW2=d;#w+f}EXak3e9&5z$5xH9zv zG;y__R))0<_Dv<($pif`=uCd=k)~P9Bw2Ga$)b!#jw}Zxl~DnwxG* zeF64#btQKShSj%G({yRA-Tf+hl|M+=?LV}uf!yC1+Ut9Mv0rgcA?K~qQ|6({=BombMs^<4h|o}Y3KX(V;8Dfxeo ziz57Du2Zo{-Fy{2t z7BUwLfm-k2Rh9a^K#x*B!Y>?^xZ&JY{pcSKY03)>@2N_irn#Asrj}sY*!DxTOFrjqJobfwNnz(DeGGexZ2iE z@D!Z{qMHWK`%3uK{pu^%*7~&CpO5cV3fF`2UlMSRd5+q7s)o(ZeDg<1L1AwH0BU^} z`!*zs(7N@#T}H)FrmCwggnU2tY4*dT=H~S+95b>(sHYE4PY(rEM*!qTvXaqYBjUfpM+)KZhGfA?206NYgxP2AlZor@vKAx(n*NbSfUfl6N_N!d$2;>Z~ zR$KT$wN#p>DJk0~n&W=jK|=jK-i0EB$EZ}XFad!;s%&p`vT5n*>7h`YR~OzODQa<3 zR^<{@qNNf_lvGJ1ok{9UB(GY_Xn7l=Q>JtU4Nl=H5I!k`)IcTGM7-5WxW6M`Y*(8F zZC2a$20W=#SZfJ1>tSk}jU??+Y^Y2HfOeC(3Bg1MXjOEOf~+MwoL$_fBHOKZEUO&t z0~EE;7ilVT8;yO=!re_8y;7xB0Hr`$zrE1e((~XnK7nRU zxAiJq8|~D7?bSJ@upGrz_v?M)l@!lAP^N0D9W-XFHt}!NY*YlOh0Ahh)EI;simfzP zeId$tBkJS4#gwpEvpZbXQ3<7TkhfNs6hy|+Or@Tl!4nviqawhWmG=9kE8i_yZm6bp zu;S`HR$M@BrnOquQ~*j-`^q{G7$2iTqLU!d(pAyoJ1P$uaSm?Nt3{^eAnIaLy4kfQNIV;Vxg!Pce<1wMrs%wLj-IU*6;3%a;K*(J5`y7Ys#cNF zAB-U~4LqmRKXF%1%Wtt;us-7KA#YPuU+?^9awNgHE1wuD_vzZO)n`^y<}lvvNNVy^m$<`Dd(^DXgb*rRuth2NEL9bB`D=A~o*j@}t@U|jopo4BA&gd$?Nbi(ynqeRWL9 z+HgX*$Zpjla+!*0&C^;2g<8hz2e^_59-h^oO}4L_co&sYpHaYKmFuMx){& z5&@i70Sz9D^*Ac3wv<*P)U+?BmiQU4>W|5T3b|aaM{D7TJZXKf5fVFj&go;jZ*SWu8#Qg z_O4gAQChOfMXgeLmfcn>X}|!;Y*^A#oXq0w?W(C`xl(pD$lRO8_h&D>YoFeD{`XZ( z?Hx_l&2Xxqsdkb1dW<-_b)c!W?Gs6uGKkC#JE?k(XwhlMWZc`!SkG&JQ@&%(Ga9_Y zwu1x5v21?fcBpo(X{o>5>lsoUQ_515Htdj}Nx-)jZFAY`HT3(?SC`WWBm;<`_Urr= zuKH{JK=^60n|*dgMNv;rX>lrYROM_Gler0)QjE##?28|uyq(_z zsH@`4W9?z3!zx17-bC)#1HJH%aa1^O7S`HroI{Y>trrWEi)~i=%ffvM;pv%fP;Dws z?}k9>4z=w0qrPa7z`4MxY}c6ga$kCRj0g9s_zu@XKfaYIou+tysIFop>b6q zY`;N7?Y^P4x}>gQWkdp$%_BpsPuu=pKOcHz4){%e^^Nlb&KUF9##{0b_EYX!Q}pKU z--5Xtg)rVb$E*JUl`~rf3Q=*#ZX0RduBmXT$7(5xjGao!o|=j3BhCBMwXY+(GSaOj zd%u-FXkn$q{Wm`>tZudTC{MN<^*%w*-&I`9SA6{~ivIv%v9S177K6ZTb*^OkaRYpt?Y)Mx-{K$-z`c4%Ziv2_(>gov7%H=brczvmYcX%Hp`x( zQz8}TKXgVNRda2sV4+_Uj)0z~6%^WnqzheG{JBU$`~@Va`03IEmPBj;Jt!Ym+A<9H zC_s70dL=aWP0UUO$KxV?46heqtM+q;|n-@V>%9&1?oQiHTTrux2T zT{DvRAk`*=jndUPkbDj94FCj8K@qc6S1gnYEaL`J^%IpVGaAiM;@TC>H3}8wzEe$} zxywzus#QdpfM!66BWA0fbWrubIJ~%x>#9*y=2fP!a3KKZQ7``hKR+~zl{-}8>G7?MmExHIwfKsMMz9Q!-&23SuE>6`De#w8QP1hN@{{RlUk-@km zD^0?FA_o#wP^B?Y)NZZ6XMMrmJ6z<8FAiq6wO;#p<%+GGbCS5FEfyQKLZpgUR%f-R zN!kZMq6q3`Quh|QR`(7NT|>?X#1`J(=s;boIgdF zk=(|rb$!XwFK>|jP_1=P5~>Z{!oamv_>~he`XD>sk!|~5=T#rK1OEWE4OjWf{c%-e zC&IN^vGFYjsaZ-82U!TT3l{OJCrY&`%|}j<>L}i56?^XAEjYIsRWgT5PFDjmB{>9% z>lJy6j+`Z2_LYg2=T(^9kYldoyq|H&T|rrG;u7%_>lseki<(m6y)V5c>Dz`61-#d( z(&CjUDUl2v29l7EKy;cf7Mfp(FPlE%9xLRlx&HvuIY0Hxd2Vt)`-SiQr~d%M31f{3 zOSI-%ALxVetx@Gk3j5iVlZ~haN^L7o^kY)bm10~d^+ov+0AJhyfTvzdY^0D1+;Kq& zLS}VRiIPCnodjlYVEERi(&+MO=|!bHEA+n3DfsS>xL)%)63y86ZfXJKWx2DWj{g94 zZCiabTkOvko+^5KTP~ESs#0x0#(yQBwzX|sYJELFdbzX4U0yqtf}el8)9gvPKh)o~ zPS{rFEA-RttCjWUN&X)5q|{{@_gjkAQ&aaXbPf#^iM0KyEw%F(Xxv4n1Hq1SZb43+GK}qQepa_X ztlhGdNThfL$ixoF#1F#Ih;5C-ja=$_f^ydVPy5li(W@5Y{!M?2F@-i7rNXdlMXw*D zX|XvaCWe_+UNV_@z=mlilR!_g6VQ$pd{hJ0R0WV;d!FUGlKyd&WVv6=>ZdXr_0qKj z9e)kZrQbfAWfs=GyLR(lS_9&$uDgQ4f`#>`ho|pI^xO}XJV3ZVZ=g=I*o3U>{{X8% zi}-s!KqzPxG~M%V55gRMzJ)BKw^Rgz{zMAAQkq!_`^kda*wNEcf|JY#1EW6jg#Q5B zd$*@WUlgx7GKi3NEx(F|-eArAp??1W+kLl856)Mw{*4-a!U=C++Jyo07Jual`Hk86 zn~xl9@!vOO_;;iv(yce!wXmjLCGu4b$^g0YzRCK~dy!Fjp*Y+hu&ZB+eL+5=oO^4h zvfTN(&S=A_n)IH-v&(H3SxWK*fb}1^Hfqj-Q}y-njJ5ZZ@lcj%R*xU%txX;hg-3I} znsTCDYq;O1t6Y11eW%q~%6u!4)?<}_IN?gYS1cEO*5~FyCv%e?*_fk`wQ_b2{+o-3 zg1D2o@z;am03RBE#U#~sO~Ty6P}MhP{oi98xqZ8(<=gltKcat*b9E)Q6;tRiyP{3H z;_wJp_eyOhQyn!nJl(?pY|P))Pj9#GLcbRSk2EeKo(7u)nES6K0G5cWC1r_64U!2Mz^>B0Jc}3cVMN+ODVvwE3d6^ z?I>E-TGzsJi!N1O+mp(bsPUTXYWGznY*gf-uCo0QOIt)dlB~Pu{@uA>Amm%#MP+eUaon(}tQ$g5j}V+1j1WZ560v_fR7HbLIw&mx(35$!>{IcTaAIsnyRbabrlF$PSG+YBi20AJ=@N_H3fQS zZg)#;nnQ(EiMe<2j+%m5#oPpfl_g6^I`rsbmA#KEc&kagz+;cfvRAz>MjVA>&adK~ zH2(mYQoc~idL#y=`yFX$2wq2@sP&B0f@^_BI|bWI|@Dvi;m3W{Lr{>U zxYZk~N`dj3K@sX^Ej3>`{{WO*vB%@oCa!74d5xCH{lT09<3&wx1yz-5rF9LqiFHv> z)2ECBac)C&)m>q`p4w@{T=7z=efo-~^H{^lr;<0ND{@0pOpIagtlcBWg7@EBi|C^pGY-9 z-CMdxlHLnWMS{6FeV65{6cCn>8wB+m1Y{WcPUX9MNw#el7MdHYcB=N$;+6nCKgk6b zw2+62DH_6~_Smd&R!~)sR+79p%}WaCrrZSzNggu>B$77B!VeW$1>BbN2ly58Kiucj zpPo1ikh7}0CIwrX@h#5dTUm!>)}|Fz^7J)NDy0opQWcZ9oC@>G!GhM@@r_?Zo0j7|pT?mDz+gYFLD zv#=n_>Jz{UpV~C^@mqn}pJ&+}%B@*Df#@tehfF(b@0+Yo~--s(U+K+xbliC}3MV}sX^ZXwzS*xtZ zTyUf1ZfImOik255Q@JTfaUB&PM2zRP?XKdA6U!*(3&8x^@^!hQ*>Ahm_c)3LjyLf!$ZZ-!I&>7k*3j zsfdFET6}72_mw5vzN)`a7hG+IIZGg{c|%rMp-bvlj)AIje(1Z4yDq7#EA}h|{zZ1l zuB{%|0sGaV?HlSp-!C9{O~Bl5mGV{omln$M7Adl-8rl&`7e(nf>e79BbSNc7Au|WY zFLAkfZf9#=!9si%2h0y7`*Ontb8x~sjAdI#gRMZ|PYSJ$<`1Xt5#GNt=GjLhWtR+M zj|-=yrMIGp(i2c$4m|Rl`Gl;1HY5oRV|U#aFc!yAT{tg{JiGHtC4#q-J9dN2>YCSv z-?cy#-%4KIaqF^KqP|}4jM2KV38LH7Hoy`-Iz_HKv@BT|ai}Y^AMB@b$nwy^ zuc=CSSJG>>9D|=KFj}e$(>D5yC-du_u@`q+Q&o;$O&e}av?WR2*fLMym?lw(%(@#_ zlF?CB>G*Fo^Y>Wr_i~3c(^l}MRHJ?RE%t{_+w!T(97|t;4c=5&DRev7SLlhYZW}NF ziYoh$IWxA#zXQMfRATSZceM(YAt>jqXK@vc^*7<&xzs&XiRnGF~XBT`i@c2_(paIJAn@{7{d%kAkoH zJGk$n=xqoGUWZu-Ik34Ta{@&b3*}YYl~U*eyrEC{h!b29|#m$LoPy z>Z_32BCDurL=^S#hJ>LAm%@M(JBLoNfGJ%1gngBisHk|#ViiUb{XIz=W+m;PSdhk? z=hZlAs!|gr4c%mX;du&)?W%EEavOz}Z?78EUmW$Ru@*Rkem=&2_>UNiuqMGNK-x8!!+mKYMa*!0637JYg zLl@hjmeniBa((*Si_TW;sILNX9iqOyb=9m2hUnL#6ebLe<44<=W)sXp%6FG=$kx%N zXa%;{MMQ)mnS`>IzzNy_k41(M!=my@Cv>~1OA2vcnl-scL#NpiIm#%?wcK%#I&Uea zQ_=xn3w}ZaduptsD&Aa`J0t6q=GgJ@DKRy?TA{98rB++>Q{>;O5`!X=boNdxV?c`0 zF>&X?TO)R-#w{&|s=D=+YP1-dQBh8SqtFQtIYx#NqQqRkl+j2GQ#J>}Hy;r$k&3ufvprn1GP&O74>x!d$j!IimXE!6hgj*`4bs+1~ zJ+jM!`Qt8kSm(J%xP^(%VsifgN;W_879pSBTET5pthVwzSV#QW!%Oq6+#<1r+q%6V zu{ln2Fi2@ASq`cszA0XlpmmX%f~`iWw;L>i%WIGGpDxoAAe(Jcke_fdthf0Bpm@}* zd+tHiJ;|8T!I@quYBKja{61L#D9c6~C`385{{Rs)9wrDE6dNhSHAgAgcByGaAMqUh z)<@$f$PXA+T~!YxC8sTQM&alWwplTFdHdt zw@^osBu6!ds-|e@qFbB3o0X{`sHCYgp1oxSe5iUP3l+ZIaj{(Lt_+u+VyGdI41hrs zGlAUNqPm=_M5p1*rR-p3mhLrr;g7XWYO5|yC)e~TXS6=@P@<;WC-;;9i6eV!L<4R~ zQ2oxff|cblL1?B|r68(}v)p`TBNE1?4yr?}yE?Yekw(KQPvQw|12ON36umUFWXvxu zIL$3JR=|}HNBB+18T&+YK_y8uKw&-#)Z*F4DO;^SmeSpS^16Y!$`#VE<7y;>(P$@M zWLrgMNY!9(bMbR%qs~OftFPAzi{qB`QdL$-RTQ+$7Mt7<0U;wnIZXRtmROKfF=nDX zRDr@2my4Hd-TCpV9Q?Yv>y2T#Ryel?(wDI8wKWf}XTT<{0O`_kNtn0fm5%k1@6+)` z^NFH(u9O6OMNjSuHNm;|+sA_{#yLXPZq%2vMrtjzZY6sz)D=FxEm{ByN>S|GWzD5QD{1zG z(hi%VuFw{O4~o~rBS{qEJwI}wN<)g?Taa2o*riO3$IMO&dv!#RC{>iq7sMaQyiZ-k zm<9J7xzXE`OUv1H?(S|$yX1g25}YGl{Y??B zfE#*v4^P~*Q?gDxp~*N~9LXs0I_&uiVb&1+K0>-ilDc=)f>O$!KtU4Q_C&Yt)D@J+ zv~!v(Fg#}8z*E?dHqfN~(T327u0)vHtCtVI+wL|jT%NOxI=-UcyqxSk)( zan!c!$ZF?tyc>m~l&xSW2EAthTD-vP;~FawMO_@cu2hq@oYMaQ!<@s2=UC|p%5eIW z+-$)}Qish^pHtFWQbCh}B?=xOz*%cSJ2?);ZE@F*iNV8gr{?>swex3k*&bKU_6w^x zuPEo%Y}M;dIZ)EBG}o&uOj6NPG@9_VsfDJU4Xqtz_;rfwTth9P5wn1&r@c*gcI$_i zJBbI~Pr+xFWpy=Nx0YS%Xw^ol`^Cnf(7FBV3VyN0lqktR8cEkduasI_=qRE#)fM)w z(aY@p&-0IG?OG^%qU5eZ#xh(zcN_N>D;TAF9aX-8jlzY?qjbr2X$2c}b5f93F&QYW z?OA`!7#iSr^{oTL-)KH6`n3Ej_phD{HG1f(nyj@~F5K%b1<}{G;+t`xu`S74WDrF3 zAja>FRToLGjE@y&@Q!8V`i=|DI7a8mwMoOMb31kUR-kS6kc}h@8EUChz zkH!f<7O_!t3_|2dPlyB!!E<2gjjQ~Biq`(gGwby?51ST4msGuv&~g=?v-y6wRXBxH zE{UW%QR^>m#cWYKd}lZZC1hRkH+m`?dxIOtHo*Q1RQ{lxuW7+;n61YHwc9RvpCdH& zva33Jg_`qhwY1Nq(imHl^xT`1O272(_gH>(jCSuvKFtxgoy7}_q@6Bp+ zOv<93zNvT1oi$?Dd0UP?>Yb%|FYj8`qNOO7wNW#VQ=ZVdi^{F8Kp?Am^#h{DYWaG* zG{0DD@-=c!Ka6A;`}8%5XW^^0B9gB~vhXY4Pwe*fiVPS(PFuGrAz0+44Z9BU3n@8{ z!;|HFjZ!h}8!52iG*mQHJb!M;EzUJw&9SCO<9U`@Wzx44r6q055JsjZa?slfwzQ|W z)AytNAKG3;${Fi6tm4vf3>L#r`PVGvR~4d_7EErD1y4Lns}gbP2Ab>fs2+g8*AAdl zm-9NFo}ckc{l{Xw>tC1E=X{IBHw+^TrK6;vz{M>)Raotx7NJMWTIrgsw5j?=(3XJR zHvujcc$q+FZ5ERgGg|FM3HyiHx7){UD65SF)YUR_mGo4!qaf>R3QWd_RFWV$THsct zlQF`rH3`*Bm2OUi%k@N!WfW34C8j9q-ca8QvHl$4d-f~IxV^8qY?Rw6x#NyMo0+GzW9X~3EVopOXWLtp>1pIPm8DYL6i;1a zGmE>rlJW7#-|3H(({*u|hSE6LSVuYefjbI#dwq){7_}|8ZbILaS?TT;$CS&gw|b*# z=RhO}8U@7fY|HT^`A#*Vl-*JnfS=r3%Y4nmxucmUAMyj3-SwJhsOs*`8-2}%(N=9P z)4JVTbu2iC0z}fJo`c3Ani%91aHv(6mu`zuq4e=ujofcKXM9E4*BN0~&*kdwVaQrd z@13!$osNc2&}1I6sVUP#N{VzgwFL)UNJt4vOy*TM9vH_Bkh0vh#20;WX-!^Je@gMoH3M(7`rAv6uA$Y-VIU3A1;h0W)~-Ky z6>A=m7ip*2uMflR3M1jWPi&l%%6PtUg7S9o}SDA86p1S>MyxCr-w6>SMI#=Ih zRTaHbrqYD00VAoHf~5{pTk2KYH1ptxjj zwQ@UrlYPhOQBO=9OVntx+upY&w58p_L-8npM&E2);(LjgQ+(&0=J;v1<`&X$t2OfPbgEdq4$fO{9K9howU8mqqj$Xh=nNKY`L&F;`Mu28ab zw;Iu6He}xG+Pk?{{uf_Bx9P5il7;n_T>+J;DJcYlq?7@RY_sEgbUUTP%`*Z+yK8})QqlNXChmYSPwV%?*B6eIZUG@=xsdVm$AYWr3H0Myrl z%;)VJG*lsM#h1{c;LhRh6`3tnQLm`8*IzA3sV&p4zP72>9H?=<0@RdfPll1LaA(wq z6`H@q#~vlZFnN}A*mBEHA7;(bW*B8YUxejURo3d;WkYIPY^t%Xj8-!(eMXi7lPGRb zhf;-ko*Kl$NplIP@JFe0Y=ar6#qL&1jb(=6Xr^hp3ie%bhtQ@Lu+d7IAf$wuK^FXv zXaj-AbU9d^;+NB_b$-k-Mn#~qvRC*b2gR~;^!o( zZjVrz{jo>h-H#>SRqy7bN&U-9`ByHl&aOY0Ry3A_Lt`x>a!Ns; z-y2@n?da?xJq&C@!zOid6S$qe5KT0qd{3%Rt)?h% z)kq)_Im1dWCb>sfM+#J_jbXG_(7J&}_okF)O)Sr;)qH>8`N|H zUUP_s%K8zikJ9oM0-0EGD%2<)585GXRXGXcYI}7v?VwC5M2{^50XFEUHc|zx(dkqC zU%V#}5*36yt0RUYb@uR2;gr8YiYwx;EgM%=ZzpRn`rJ=I*g8|xKA9iw5U<-GFL&7g z0KF^iUn`zYG$@n5OxZkp+HY|8G(Ymy*Sl3!uG;kEO-0$H&^AB#p)mf-uA;+YQN^9HcfyJ{3pqD9zl&kEC)+9xCeco6YAecB`>7~(Nh8F^(%BmZ zT-~w9UPD#n&n}Jv29;396S9wdz(_nAaj@fSo$ONf`2RQ~|&$CYb2H-vC&)#2}3BFB)cHbuDZTHdxv zmfc^q*1}cxz_@8S< zCvpA8=(X%|DL#v5#O;s*Iqu?_7lO{Z>&0#U{abacaIMOf6(CMvOwJ~vA>`3N?W!qc zlj1sgM>VuVNK##OVQp$iRODlv;+8OIjM7}zhUN^3_W47Uu5A*F7qyCn4^!a>P)yJ3 zgeHlhszq+4HIzsjw`upp5=m5J2C6^2)~bcTv7rRWeXu*}uwzY@0t(^52u+}bBGlwCJix0&5NJfpdacPT^>Shk#!)OM>tXE{ncn644g z9H?o2*bOwdEAyx`k~?K$)5Hlo=cJQm~}!4RA_>Qvj3J7278{ ziGJy7X1pXgkhzUSzSx1oMG#H)ajG_++?GKju8@vxLU@v;D>Y1pG)h1tq@oT|m#UPK zt<=uXz(kR#s;C!pYTIn9J|7gs4Sn*hpKE_{RF2$;7wV@x)O7clq_O!{yDmN@pvzb( zLF#3oi)fZIU_+6L27y>T2Y`u!5>1wFeMGV+DJ<6N2HYtt5>i3X&O$Ri+WJSzy&ucF zerXGnd+GCxC5jg=L9iD4rA0^Yh&xW+k<~lCuD`2Atr>E5Vs{cwzKXT`BU`v$T=xR- zzG%7e3ST8%Os17QxohU59%q|x^@{ozwNL%1PHsC_rmA4Es3JghgR$)*h#bAZTCLiz zYy9Wkz1rc9aUMF;IIc2;tCG2f@lcy?ABt0L3tP7BCyhT!`-8vr8V=tOupLVe1DBT zkfxpfP_!#r#P0kLh3#0>)GP#~_~v7~CY+GL%0Fl0`!`;{g6%;QYccyc_!{_Zefumu z?gyUvi@6NqqnLZ9<@>)RWzPZ{$}i8U<4%%nx?6fjGe&0 zndYT$ZDTE9jxj;%{r;ZCS?;{ht9pMc{bAWkcksfaSl)kKfim>ig-tOaurNkMTw@<5w+;)hz$9A3V(X0Lu>--gV zL%VEkJ{J5()%gDa#ZJ~9-T2nQ>A7-UMp;_kt}3LKDayKN(?m>voJ?-4yCTvpUGX1U z!ryn*)VeZ_9dND!W-%B0)M78R`yWDD9`{{>)ZL}bzI^~ z4c8L2ZJz%CGXw$@A*hrr67}}JjPRb!cQtQl)AwY!fJ$;#~y4@*k_O~4@{%cnAY*Pn--cjl}K;126 z)=&-g+&1E`a0ezLv`WMqQHVbbyuMsIv$D~xwC+!b4^FA|x ztA_ImhWVR>s$4?qxVJ7-w>BB{zYpb?2ZNQi<&6qUE^J8vgibNHeZB#!G+P@E;&o{5 zDg(dpjxoprOX9HJ&nwFOy*+1 z-s9%Ae=LCqQA$8@BdSKJKYS4AO%o!KUT)Fd4PzNoBjQ+j1gGAZ)8Sdm8CZVkVRV42 z7xM}ZxOVyYMWdQX(NFp-7RfN=Le^l1a` zoN8@W9^s`#EGU$cbS5m?>Y&R?Fx#ILge!t*OLWfknfoC_s-bgepH{MdTBpbOYCrN! zEA~Ui4>D=}AN20WkUZ5-ahJAFDk$<_BEPn8#1_9-)D|)ffe6rwr>1|>iddOOqhBBh zqV5t`#^D6?iBN7e*+M+$UXS*7l#{DXZrJb&Wj;^mx)%C4}}RkzUYE+@T)sXhd5t4T+-T`%q{_6yyCxHc}`)! z7uvnQ5&g9)_ND-V&J%Du3$6?tkp8EusFnUpe^- zwqGto`p>Rk>qnB^{{VFRSF+l+u@7SUi~2!T8jd>oYPIfR3eX*N0G}B8zsmOWKs}4$ zKR@1Ea_S~8bN%9T4j<0>Um0NLmHKN{OKn1UiG62R${C?nFbwFOXFPr!oF@HzY z(v%u4mgDYr?h^^eXf5w{_QgUe(1g9c{i0xNB$SzZqgriT`WG*sXS=ZvJd#us;E}n< zmivR`ydLX|Tl;*bwG4@cGW}{FOQj)WY{F!nN46xA2DP*rlwKM3qTopP&;#JIjsC(r zpm7O$p9t886}<^BH}Hw>*IgqmCO%GWD87J;&0Ir%Zf(2=;;)Amx!-3UW0sswj@3y) zLvBJEVZ{{gDM*mqM_EPOZI+ZE732A9{4|#aNSyIsXX2tB?E8%Fr;2E8PG+^VYF%hp zr7Q&!6cV4sxO;SgGT9HNkC-$qZW+mkn*qno|ASK=^@60~Rx5VDHkrP7DRE6Jt3L5SUj4MitQ~9MR`wPgNTEuF> z>RDSR=>Gs`?+a-y6mHxxn^R5Jlr)4S13wM2jFiNQO4D1s7#f}Q>|1P>XFYe4vmz8p zl*+V-2I%nSHX~IFUTUn(txH97NL=Um#)d8Jnm}2JpBX7Bs@iLY)|FVxt&J3dQbC@Q zBcv})-U&HaHY+VYqm~{Kqrj``^1eQ_+^+RaxGnc|)3Vh<9v}pixX?-KsfJ{67Lr>N zBSN$7JibYp2DS+Z^;LYBa~BxeY8W>wLq9H+DE|Nqd_%9dSozPpyEfg!nl<@JG&TZ{1G?n|!BYH1ejBe$x=gn>!xHzKBh19<7-;$oi% z7e+2YvbOG5{bkvE{a9t*^M@x>MF!**LBy=ieP({hnmv>~MPBmueP-TLKqKOr zpRyjLRUb5IK)l(}a`;b?5XoJlnTD#jukHn2N69zpO zvpc#AXL3xy;J#_|o5`EZ;O4cN(7TNZS1yCS9pca|CdJwht5WIOlHNUk zWn=737&=94-P*zOL_Nyx*T|eJf4z`%Y5xGNzy2gYZkfk*BI~tDAtQd!3Z<+E27S+vRe)F2r|*0hZP?%@WEWiW2Ivc`T}I#W zQx|ufQOg|R-vfMYgYx~)iYaRCw8rC8ZLPRd)2$9EMIq*>USzD2nGME3n4de3ebkK& zBv3Cb>>m2D?fe6-CxMd}x`Lo^9wZ+XWpH1kPdJ8Hu6w(y=%yqWU#B%Gc|eUwF4m|| z-V9Jpm5$u>-6N{I{{SVj`JGtoq7C~$brdmq`gQjuOWiWK4~hG~zuU7b#~)W^b)126 zs<@`56t?TtJ+TxHF5gmx`fFW5Qe>h8zX>KPau@Nie8kts+PxpO`=0kZVURD$4l*|L}(gnAUN#qS<=&de%)9rh9 zR$D{&ehRvA=VV_}o@K4c>2QwpuM})zmj|u)3#G>Ck+Dd(ngK*K;$B+Rq6s7v4xLP0 zVxZ#$t};G1pM4hfeb%uK^ip@0}rA3wvg@x+NNIyY>!} zfb~Q?gkq&~OuCsvf{i3CMID7^$LcFMygECy*k2)67xszKMKGxeT26RjZ=3O z;F4oQqMAxeV1cB%MX^*dr*{?t`!2STpDrZnz#6;uokVvekp5^=M-TXpLe_Pe02 z;G@5&1I_DPeqW| zB>*b1&_P?9hvJ;Nonj1hGY3`rqwl1Ij#g57X{{{U`Qk;newB=U_)g^f&sxUMDb=)bE~2XsySDlG5h z^(>8++|Ov$(XV?d`;q3pSN?5n2KH1ouQxwL+gBK zrF=>U_CYjLK7^o~Qp1UPB%y7Yw&F$of}d<0 z$LfHPmZj`XxNnd|SvCwXl#973x}%UYYsHRD+~vJDNUNfgB3fc75BV zoVCY72=~!p)zY0b*Gly4kZsAx&S!869d(SfBB6Sd-UwGG9;9QFdt1wR?KNqw%eWG) z?C%V0q;G!{x}~~2f|aw>{H;M|wL)KnsuEB`{knR3c%b2MV5f$^XCGdhpRg)=ZDpke zED0-AkDSyv{{R=Y@=qD@mv4CO=JS-<>Z<7T8`7O} zwTG=#%Gi}9x@Q4F-!PJ(o!&aedm&_Hk*?+rVm_E_Io}9n_InkkzW)Gw z!|Fn+7e#pdsMFKdwYeaI>tKHoD)^!Ksm&|ij3ymOPX-cCSvs*lZw>h7TkgMD(I~A*IR4%8tMS* z8mSB_nxWv^ssx03glc*bI7uDM!%hoH1KP&`QU^A1EvC=S_xdak9m{cwTN9KSCKrR{ zl<1<#nzyJmREJcSRHo`$D+g<>sP0Us@lGl&rgPh=s=Jaf*HhS4WaLXlWfozThr^85 z(_CG0s5zspxlpA%Qs5tjK#~Z887&GRYACcTv~Jw+E^*>|jEg_-<=);LP0q5_QGr%n zOEigP2u{b8!m6pqr2hFx5)QKwO@EP*lU0$2eVU?U#oN9F-ikBD6 zGQ3%5$z*+bAh@ zB}+pfk_^;9oZ`wTfq3>WJM=ExS+g~kUdG$6X?cUCs1!Zs1 zrD@pvA-c5|KvCQrZ0HBLgBhOHd*jMV!{V%a!U1uJhC$#KCULGX{vFDXSf9DEd1)6H z=v~QJX-w1Bw^V~CQimj@h40iWp^sT%Brh;zlxn=(#fjZBj@IxTvV{tA^~F`ycs5X= z$S(>Cs)bijJeA660VE=QCS@4dX8LRQMDNe2SMo5;XeA$)MV`5jZ#;>~T!V+>ls9|6 zVz=$|HmhBVo~g?9r{z*p>9iYJ3EGkZw@418!8fKjK`tN#V`FtBtrV$FzGrI><31`! zZrpNQy?pcLr<{2qLABA|?ku)gZpPZ1DpQnJ`5Eq1mh9Cis)Yc*KtR8abBm7L71)pf z8aLzV>HAe3x8b>!qKzW}x!f^pB@IJLtfseJ-)@m_p=IXEsFd+($tcVPG(`L$g3#cs z;ka{npHEiGY37_yoN=1IJ)a9(k1Zbxcjw&&_2YEG;X*4lDn?<2&Xvw!Jsn8#FML|K;f|DU31QD>mpNP-9=qdQEs;2R)@%8N@Af! zVRs6UTtY$q?H5`*zh7AJO$*hP&0Fc0QczZu zI_*q@Qesan({1ju^6~WdT##~)}wdSr3 zu|iUUii$VgM$c8!CP?>2AGt(x-^UMz>dUt;u5B@L%e8bKwL?3ft;sAd+AwUcf%|3q z(>I&NE6*v-u3UE|BuPp}p(pUirPv4yYaIv+$8Db|apxqGYN-Phm321_tR$$ZQ_xRI z`(Uy(S|(gnQECSO&^1v0IY^p$El*Ok^41DMN}Nh&N%zKAF8W4Rmk`>@6*2m)(LHJB z>-{~#p%j!BdK*LNx>7#ps-&ir6$!GSQczS0k(^Rkmb%+zpt+IlyC$f|ALgC$Vtj|& zwJuX~VH~-c)7>k3T+TCPT59q7TRqCq5m5D8w)T@y<4XZE4>(kX6z!DahVmGm)5s2+ z@9DDioPFG_Jg+*tYSV!AQ~oL~!1y~kx^ahc8Afx9VYxMjg>g&u*OKqmO1L2?uBd(0 zrTa4N!~XyfA7w?d?2)j@=&d^9_%OA;IxQuhDb9(~8c=GoJ&@sbDMCuf{91mUB55WZ zQ!OpsPZYRn{!-g;LXbbjr7f$(gzlf~5HzGKXkt9IOy#IB@|`IvDoNUWCDI@+3%N=S z_gyHK&B9uUT7*H=Jh{a(8U}bPHQZknaqWiL#XM(-;}%aAXzz)~aUwSaw!h5bY>Tn%ll;_43F z@Jf6#$#l2f9LllvhYeC|Q*>Q3E>taP5^+tly+}{OH%N4kX!7prX<{ZrRq=286Na_7 zyK7=#YSCT`vcJXOM+{}EnK5zZE9KU-5g=V|>3>^j)jda`z?ZTn?YA4A>n=Z#e!#qc z%;vVsvEz-u^GQGZjemO7&L-nWU*bMDW|ffJty!(%f9HI%C)NPYcPNhe_5Nb5J7=)W z*x1WPyrFfKaphH}k#1B3mk1;g;o}8jyPdMU0Gplcx7xCwsw_X zUv&TnMH$uq0C)@>P!Vppy49fD+Ygc>z!m*TQ}MO449$t=jv1 zl|4FpfQozNZ)L%5w`xvLjaJ-<4OOeJwX~S|gTHjUs{2FAlm7tf^_)Lj{Ox$hZaBsx zM7dUHnKde&Ovsfrs*!bTCSDtY2iqE7D{c9%%lrbHy=9;Qe67*Cw)n}Wls2ZSvZ|mkxPMw&MYkX}#+;+~{L5^kq&%8XOIeLYU5 z9TIGT5?xNpgo3#pA~`8kOd5qT;`L$rP=Y@ULd`aj!k>xlNYSkXrf|(YY z!iNG+#1CHyM@Jz{Euu4dd;~VX0%Sx-E2^ImMlDYIj1W)Z>E{h&=#lQKCvb43+5Qyi-)EH&Tv(mBhWjgJ3 zO|3*D=Ng;zi#`DE3P<`;36queS$^-b62nk63wg3SHMHZPjin^ad$fy~-ORjBb^xoc zX-Dw7cJ7?yV$uQ#L$~2IP_->#rmsp$wgP1%tW1k(t5m%}@@i?&q4>H^8Ph?pA3+KP zCsWk>q9PYn5?To}NSX13NKvGq`U^_Bp9oZ{f_FyWr)#cR{-US)L;-#Bt)FXaAXMkK z1^)o&H9B-y{c%fU@~xh4b)d>oD@3%?En`@R9aDzb+Kh=4P@n0E!mYVFEN}XWT@_Q3 zT5ZWx_ACV?d*>KF@qJv-y^qUQ4uLAs_Cae!4kt#Wq$sOtG9F+>{sacZvEAn_XDaz; z^&W;VUP+YNREOCY{GSO}Nd9oXYI()vtB|gQ{{Y`AUuvVz^0Cr#zJcw@3AI|!*mq%A z&OyV+zHaw33XTZudlslbv?;VWGOkgpO18Ic$0^(XFQWR#r}HQqmM7^*NzqAv+!5Qi zm72Ky-kzLjMEEzpP#wP!;p#rWR}Ht>hu@dfdpquT7_;{K#+ZEf_O>0%?c)VzYQG>%?I(T?% z>9|;tn{wR^3MwtuG^XouAH(_g0W{R%Klfwz)H(ywDeiAMbQ+bkv&U3b+a}*0@TVtp zeQ3R_)T75Y5nGwpl?r4fCQ1w-kX<9m0#oWl6^iZ4tJBT1d{6F7%l93ngJ^YkfZy%# zSF;b|zBS@XR4W>{i_S&1RT*V^5bZ?IUqv$5(DK?R=1h9VE4D3dMQK-fmE@UT?76*l zD@OPPcH?$-t;H?J6tb?W+)kZ*V&&$WDptedo6fwll^xpo#13^+gAz)rXE}8~Fo~$D zl%X>%JO_-v$U>!zC47F#e9G^XSmrZjs=wc^22?g$CS9qibj%5aGzpZ>syYu6BeWZ? zl1eMNf%b35^tE>McVXTS9QJ`pRHUivCGzB({!Eq%q*wV}k<+$7e)H3xP$8Fjn|o;a zp5f}LqK3y6b9VC9G>|xePp7GU2X=?_lKL8F7@aP8a{7tfyN|Y1kcS?nsiej&sHj1c zjiEGDe=#6Lh*QcV&T)a4yYeO914Tb~+f}$7on@ePLx9w3KHVs^ryKfp{Qxl3QnZ^L zgCA3HR=vz+K z&0S@&FQElW2u}4>w4{lWKNpNZ?EX>0l|J<>eYb`OnT!?o1@H&;w&px{z2B)eVa0u$ z;1-4Oe+RUjJBD%WN|K6-)nEB)J$t>{ROn%p`t@pBOR84gjiXUHMq1^!mvU2A6}q%G zmT+_E(0KmEn^irEcO6Kz)2|w}J#a!&rCS=wvBzGS{ojJLr*QEbpyu{n`4s?r&_})T z;Ly1Hif&zdf9{)!!LNUhavL)YYUN$MCgDz|wUL&O*-s`#ZX!^uJkhq!P~S?RkoDvrTfKs6h%tjj6H}6om<;XgVE1i%8~*cGMMC>q%fV zA!&zZeW7BV{x2b+AvZOkbN2zTI=s(eaW#*%84vBcE_Ar z%6iF4psGNhxJ0);TRe}2+Np8>0PaPjKfe*DsQ&;0og72-+T{)(H~L!4^1+%@+{zV7 z^%<+8P}(IWBCRH0VT6aVuAvkrsJXw|+!fQoqnqtcJJ)C&(@3Q*-;K3$WXyuaZ7Phs zUAqS0Ayw?B)xcJs^m zerfU*X{4vOR=k%KMx>P4+!!RgIvmfqdk1kRvjF4Pf2m3wS(rQp2>c`doRsR5E>|Y3fuwVR_DkI#+ zV#W*K4rzC-W9A#RTxp$34~Pw<{>aom{jlX~cqp*lw)~f9m{@3B2wJ^h`lM20+;vN3 z$MH7Yx>TVpCZ*Q^196n(kY_2!soh^&OhlY~7sasu0IxT3%;r79gNmH6-caZKs?%=1 z+bmR$(_9E~g@Oe43C~h|;D<@eLPm?rG9ABd6s9oqr>RJn+V16UR0=`0QPIo~h%D~V z&q$5;Q5}MY40LQdxTMQ1^SNt+t=5`rT{o}lrL$PQpb|(L%tSKYTMfd~TjO5w@VPws zvm@>!hH#3kqW5sOE94MMl#PwcSHnFINMmKUm4(e4DD5%QePdyJzGF(PmAHLUxg9m7 z3$;>jqgfiGRZC4{5jS&0#}P&Jq#x`1p1|&={-sEJrtZTi?h2xq-RWDSr=be;T5nmY zKu%}ujGuD%2$_^_%i6rM^1?hjxy!knR*3x%o^d%=YL2^9(NDb0LH(rq*`I37dFMfY z8UFy&`_;Ga`PUidUN&2UPtBX=S+}_2cRad(ERf^i0TbH2P5RfLR z4uUEi(bP3k!siy~kh2_~rHoFVvZD9ue8~YUrE@CQUJ#MMIqDPc9^)MqeWT0m_1P;{ zONyQ))CC6u=Q+iUwGGiz!)yz+PqpCeJf+k%Sy<8un`I8H06a8-zO~5bVudOS%1^0v zs-o4$)K9l^sjOhsAUcQER+KBKk@JBw_l4G_Y&@~FfY|VN|Q%I*TnOtUORM1^D*MNJG!@&udeXXbf@MUuc$sD<4YGF;`SU50kk55 z!(yjumW7ldDJ~QM3#-}(#Ix}e=NaCQBUay>xRQ5L_1erW46pNB%QPwpm}h!Izg$ef zxW=oFO_Np8I_uWE+G^WD)FscvC*&eg7~-p(;?kq>%xyU0t@#668&TStkTnO)&LA;! z6+3PVcTB9(zNMB;eYT`kQIJ#$2s25Jjv^F+)d~?-RMo`}GP5UBFr*nye%Z$oh+)xJ zEcNvx#1L`@1I8{7s!&q!S2De39yU!VNr^0}D^Ma{cuI5a*5zqpUT)JC?aI{yH8 zgA}E`RiKcjKRcIS8qV5{K5YKSMiv>+_lFMPrFX%#bfoRk%b7?gdubN)|LA)Kw5V%zoI?THfB&I6_B(r|@2HcjY%=mll`v5yP(1ecY9EUod2xQZHMdMb1&XT0AtCRUT|sRkK_HX@W!J_TpYEKb02!KE z4%G_tw7iS#EQviP^<3TgB?g|_1N)Vw?K~Fk3v+(Z;*~SjJj%)11@u-_)R3eeVPfTQ zxwMKWl-VLmmyB-ll5&OR8Q9wAYN1tPcl9Q2R<38;(lj4Z7E|e2;F6ugF50M7xT*L| z!+xXIjfy5yWQ$XSUKU9&tw8=Bx!V+ z;wsZpdIR^v4c3~e;(1WscKyE?aaM{)!`Bi0=>~fblw~qmVL}00tQINKYUK<%r8u=b z>bJN|$@83K=9swEZsD{H2!U`WWSs{=0TPcasxr-C1W8lMw% z9n?|s(Iq(Uho|&Jsx|}%t^koU`+uf6NEo_JTWJK9Xgni=WQfu$ng9Uif*f5{5;m%| zsUv9{M~o#>JX9c;UP7h2RQShLa0zPNT(?M(J5%_ziJKxvHmSo+aS3!Juj0}|4nrqn zH#X^$>K6b$DJD@oTOj7u%Y&LWg#O^$qJYAUCw2 zH6C8rfwxbEWjcq1p_jHPuuknq+gD5e@II}yjJTDvn|QMKeOEeg~Wk*vf)BZX+9xdiO4 z)yI_byXHAfY`Q}gYz z^=I&$1O;vcNXT>%G&x8fcR^kD)a)BA9MdRmMn)lvZC^7hmQHugEDw z_VA*h5&@Q=#kX!Wi70^XTe#lJf);%8iwN{9nr&&B-Q%p}W7jNUaUpodPUX7MWDTC3 zZub*-0R*^$+B6f?5>M!jBmV%lvG(S&)o8lb=%8sWs%qFIud*l%6)RQNpxs7yo26Yo z=L_lqAB$1$gJ8B4L7B-&wpx>XT}f$vvRgs*6p{|pk4nuvR0MobSo-QpS(rv1absBfvH&K+|!Dr<}7-V--cL?kdgj+pFS}hyQ>&9Vu=#+ z4^t^ch}0-rMo(|BxqZdz6sDNTXcXO58;+p?#UpwZG^iwlwLln?n^jo&lN%DZSJB$C zN;;Y+X`U}Mq$S4GlOPP`F%LpZ@}xVXRYl_ETXFyZ**bh6K@N2g&2>{wsiN9*2W-k*Z9srJ z#yTLt2q$Ifma~}~;Z-?s&NJKx8FFU|at1TPId)rXygb{iTd8ro-Ayh*ja^$^1$!;H z)j4f#xWmg#$=VEw&_HwEL|T3zi-z7_U}$vpAKSOD+@;=O>@S`;*EY$m=L>5R;tEHf zcMRoRn)7C{+A6JSLTVdC>uQZjd4w<0bpevC={iV*14EmM-wkzEO~*(YCDpGo?km1yn*;Uvpgd+sZ+OD(Y@Riqufa~=y} zzw#bej$_wGEl<g{DYeR4P6nDTWmz1ByM=FX zmhAZd0D7E%Y_xD!B4ZqX%9K@1R6mtR?0Y3byUeWP+-d za~1`{*N!p5`5%Nh&ylI_)<5BMHgS;B)xV9r@1^OX$~xB+rq#9dlt}<4Mn_Qt!5hSc zJm|FYIzE_l2K(>({{V87GJa3H_a)msrzgK(Dz9C|_WfNuj%88lHBtPUrFwSfSbeY- zw`nR0P@t)uq?yV9j?u9=^8BS}nQ~@~c!i(*gMPbbIrc+-x+uSV&gdAqSJX^6YN}Xa z2yyM;CTd@%O1-m$MMm8ewc4%?+g^I!X2)@y-;{Au>|ZYCF^}RHin4pH>6Z$F>(Qr@ z*ktVzDJ1TnNP_DA?V^w{IQq>KqOrDdtga*3{{RA^KI3rSKf(Db_h{nE%$mQBRLwO; zD{{;2t=cjgmYrxRYOADWC`)o)?Xm)Cl)y1-Z0TJXbbvMIa5V@4$k_{8YB(QCDId4n zraw`}Je6&$?OA=%Ww@i1IR-x6HU8^ldmpsYlFh;QTD8p8Q7+ZF zzhW>%#~xR0y?iwL_5G^HViulA%eybit6RmZaeg(#`8t~W$n++KEL$zr6_#JgsTy5V zsozios|=+(m9&6Nr>kwRS=SAB%tnhplgEycbuHSy4SZW!l$@tGM zucWh=o%GnIpt&;DcVCDk0P2X--PS5HHw>`J_1&|Z`w;lM887i>1`3uF`2NZ$CoJs%yLhIR)q)Gs5u#>;Nv$uuEe zJ_EH{E_ZH0;J!$@u~CcjJ*k>zzFpD$JBvW6hnjO}+Tzf;r0XRC8SeCrnV+#; z_KnnJItdPiRSr<)d$5*UFJh(9CU70t3xU{>4mZsslftzD3(e7pT z!%daC?)85YKy=cJ0h4Xij&let8U=IRSm)WXjd1vg*VEs}hZ?G_gK))vA#v|6=B?bV zmg_$f($GJBMc%0tX0;a=oM8;mrD{*%h~DO~dU}0{XOD24iswmxEi`Nd=j{xMO~E$~`Ei<~mbf9t)0N%CU}7R9t6$-#vqUEzaivPH3@0J7R<`R>U1c-W?=>YX zZwNqgL3^bs#nqW^=Yq%g235`-Httu`h3~faPiY(@+ci#MXuz7u)NkC6Yp<(QZ#-Wj zwYqenD){!PNlMc&4%qUHW=Ov)CD@F-H$b*s!H((SqqKB^r0RbKoE)Q!oY zB_4a7&yem$ik0kLyV}h#nPfBpVHb9myz<+2Dp zv^5p^wH2MZbg2$GMB3!EA#3WApM=B!5Ys_YuV!mYt5N{#8g+_U*l#im3Z!9Rq6(9w6&=8Qici~2}_06oIesIL6?+y2Xnr7 za%^pW2QRPuw!qk3;!C7xFA2Ak_MOKAx8J71n8aD5J!b*k=VoQg*vDO@n)aI7>fK?)pf>RmAax=tk8n?2)uercWkK+$>btZ~ z-))>9+^->1;FkQU%VpD$a-Jlodh2qgxzOQwE$*Jmss_Cxls>gQ>KkylcMZhM=@Sh6 z=VFX+WLVc^_Te58?c;(u?;^YK&mcK@zGQqeRcCV@sIH`|x!Llp;j48jNJ|M%@ZJ6@ zkdkH+2%JrAESIK^J1nnwTiaqS`PwPXqImbw-9H24+_P8PMqQF#aBfB691{-0DRX*S z7OQNv^fhkNT(5LbwJ;lap|%T75HbpxO$ta>+QV19xk!fL)v^M}kKmb~i$KmyzD1pYEP`H7S_GMc{@^1z6J8JqT?lXbM09__8wzvj9Umm$iQZBvrlsJ2NJz^x4Wj^&dxTT046^o@*Sq&$)==qAM5GU?~Mm- zcHGe#xGa}=_pDc1HB)Sg;LESNbSWg6d*>dnYRU!T_frQI6I@&iS(iXy;Gl`gKI>G< zC=%E+Q5u+y&8o78WeQ#2B)S2Vlw=_QD#ixTCaaeQDZxvJYW0*$(+KDQP`-Iqg4?d` zE;tb;*VD!k=S85g(K0PHEYvd4aVk;U@al9joC{QWV|XX!n4t)%-JvPgB9S~NeT(>x z6wZ1B0!WXIS^haBE{Wqk!a`nJ#ZC6g&m46Ncso~0f7!WORXx=waPlFv$ILzW2)osN zr{z&44RuI3C!jp-_P)A8m$D(g^{0+iQY-XsD6(LtsY*TGTfzEs+BTRHwG z_9*+-P-mxt#R`_iJ_&7Tkd%f~B>P<(kL!ztfGj+-4>)xFjQ`G+eR&v%J@VErR zePzmt_=P}nZjETDN{pxkLm5RX;MMD1)CFz?fv=J4 z40EcU(NdPPT5ZfJV?YPOe#i>oo(RZAR3Ak8o2($L%XFm)8_g5UX=3c*ODLA9CsFN& zyy&_isxx48l&FzDkhb&e)Va{ZBHti=VPpDrh=s=MYou7E};F2NP&wg6K|ppVFi6^;dzA* zEAf66$d<=~*~+cd@4S@j>C_Yiyo2pZ!3Hkv4O+f;%~}|zBi5tbrpWWVkbq45ebH+R zfK>#jBEJx;`QKAyq)FC)q7P{RDH!D-<5t((N)Y;_gn$*NQxMFClqX$9zMTA0l6;~C z3QTxXH=;li>LndS_HL3Ms%V8JC#O#sm5ok+%(MHtl1-wvMqxZsulBile66gcjGah%{!}`q&*!;LOwk2LZ(AFt2`bLQe+}cR+69Qf_sKRL&SbpsO z`Bu}BYLezTR)n3)QV0khKMt{dBSjWA-27owzR~*_;;fBXztjW2VD+c6UL?CzP+}Z# z-j{x1%qbmEZpCkCl&N%0xQSIP5S5UmD5bPIPl)%e_dLB2bmBaHzONC-ZK}SJ?O6m? zq0QDqvHo9Qt==9R@3ma|fPFwYFS}gi=6rsq zLaGa4DSoGJ<(Y)-Zb=?@cPA)1t)4d?;z1){PrYLnYslA}vG2<05b7x_97}Yz=7QAy zQj|ha2yaqJ=#d$x$mg03eT&g8ESnhSI#4T7#66|kZB0>S{K~OWSg@Dv+Vzyo)%FcxUy78It_l& z0iqFAyh;>CgosKhm~=>0Rcz9y)LX#;AgOC4NDnteWiN4e z8r_noF}qzmOx0#tQczMp>C&DXJw0)eOs{Kzuaat}5>@y+t@u`qqd#|c(3U@amssl!Rh{9>yzQwg)J(7@bwk8TY4Hw1f{jbwh{-rGR5~i zDyg1K@4tViHyBeIy#D~Zt|!Ckl0Yw3<@n191o%x=9>|bIdi|=S2F|n^)m2VO z`se)|xVsR@vTBa`IM*cnnwk2K$g#Vl84b#@Y_?XGsp)K}sBzV-i6kW^W*~*q6RW=T z=`5pgpkW_k{!#g#EUzo%u2|(Z?dq`Hd4D0uueT~zls*w4nekyKQZyW zpO94I!90aeO4Xwl<5v@ibS*kH2-G#8>q+uMOf~e0$rWE8u4r za_p38swt|FJqotn36#hnkI@{`8@i#n$Il`7T1xh;igbeOpap@oK+J0dvBBFc<>j2v zpN*s>rmpKsh)eqPmrrCh(F7z?n(mZX$m;3HP<=;lfF>9%ns*AqJK3AFh(X;hcI(>? zF?F`n+}U(V+Ip56c?uHo4kXSb{VE9u#L$kl?u?`ktWvmp2~{tV`%>-SkbP%ra0YR! zqmw_ISypZCZzHOZ&%nOEDT=G^#IU#&r5CRDulpI?_{R^3ybl#wY5M@8r>LrT6XZu9 zS_w3k%cVBbMDzvrBnQ82V{iPW+>w|u2t z1D>1eX~6q3nF&yJ8f{SNIYuJu0dr{YjA*`t?SC_|nTG!WNy4Xst_;>EU_y@4I-d_@ z2`cm`my4@H2>^pGrg0EVnmPn#mbDwwl}$6~XVw%oSIVWVX0??wR*C#dZ9r?*NrKb{ zwMidH#T55vm#CTSZs-TUdW2>&qODpBJqq1UDCVd5gE7pK)hcS3t*%<#K{6ssJtHZS zzzev?sasQA{)+QkU+;zUdzqut$}3Vl9#`llJbM2C`^Vb7#~J?s z#~#J>(e#jb90@2?!Q4iJ%`x?Vl}GvjdY8f<><|9{CCfBEq7HgJ+VUEk(75voQTF<2 zPwa;Gl*T*l7oqt&!0Z^WiT#0Ax-6dIOp1M#N-1E41QVpehFSLntm3ckaB#eujn<6u z=hR~`!m-O;g}M|in_3cvRlEu9^U^lfzGHyE94fw5?l1LrE4!yk$q42(*-MG~t>h;Qr`ETkw%lxZhtHvQRsc6{{X{2)$@;@PX7SCkN*IrTYsp-l<4Ff8TVWbt(&C@LZ{^N zXB!JrIW=BMglQq*cpuoGox^gi&l2OBdWve4A}WENT$1ZBbV;CUPobR^T~!TC*zK zRbaK&zNOmgCWR$xDgd5@L=qIVO^ma3s^FWIB8jG`Gf|wih=?j!iQ=O!M5=D8%DT4I z@gx$mJT!q`QINK!Rw_J!x`!0VT+GM-p0FQP5+Wd`hFI8fm6Uk+pAk5UVZ{|}?NlTl zQ#DIM7x0B^`r@>=TBDP`g~0PF(sL3%N3M`${uJ> zk?qToHK_(E4ge-V*}tHGKt7*>(2h0jSF&Co?*_q#_Lq-e?LE$M`l~9ji@oP2y4Tt& zDXFQPX5iDz)Kogi3TeeQNp5=0I*7Nl#aqD`l5@X;@E-f~FS+t~sUVe&7S5n2k)Z3g zpjU^Axeros=PXv`c^7aUve9BWH8s%|9Ja3L-386}lTJcX@2Zrj{JBc^Am|7)jT12} zmM)`c?KG$h!7T2tF^&;i&U$J&PDVvb-VN-T_O4V4Q2hw0viMr@6UVRhcnG zOGulA56Kh;Ql^gOq^ZQOT{MiJa^T^f$&aI2N#MSk{{UY5<1Q~|Y|=VL28se}O?UM1 zSWC!Vv~n*R+pM{5>ll3)?c^ca8^d5Y&&HkUO3Or)5;YT)dG=GV?(y8RxK*cB^=E5& zy^PDf_VLI%jL@{`e#-8yvmaB=MQ;qZVfTz~is4glW|fC;Bow;pX%d7a?E`33ks@Q# z?7ZkO+O{IW@UNV*Kk3Fg=P4!gW_aond3Xx_s80O*vEp9mxL1v>_Nz60rz*xP#d)y-K=w$#D`ZQQh#F4!XpCXm_=aUmC*J(c@I_^>@hp=VfQEx#bVHlu&D_)Dfo* zm7ncZQ^#3adKsUvnMco<{g>MrNBl)gd2P*wVs>nLOHz)~|F;*#9Rt)OjEl9Q~xkr=-POZsz~h>VKd91?IS zo{=HD3GvvvsuJc-Z=uF0oU!BUL-*T)6qg)Kh+!mcnU{=FWU@3A3txoo{%3PLWku>L z->H3dRc@E+UnNeuqDqubv6Pv_nw990MAixf0jZEAnR`5<6S9>hJyS2jbuiZgimL60 z*FE!`{n5*j(NiSr)b)i>k~ku7)Fyf>PZ0_nRE9c~n7WkA%<17A=@U$7leJ_50->*$ zN~94~r2hb)S{4Cw;ypdEA6%)vnK&peh0UnnBm2YK1-0QtBnb6W>Oz5*?eo*hQ6NyK zg@pyimg0ugfKS>W=-I^!B#EV6kUL9tE$)y;lO8a~Pw`P2$ZE=|9?|RHm|?coekqB2 z^ouBdAgJdYr7BjlLye^@x&9`xeG}q7_;_##VeulgmvZ~5M$p2E6A(Alg)h@9b->*5 zl<t7}!tN-L0+l%>RyC%gl!5k1pO5PwI_CW~M>KZ|bs*~B!QyKkxd%OSMV zdiA90U8npo2P~;YQo1Kg`HLQb0o2VYGEB~-h?%lbA^x1I-<(za{ZiJKCRZrw2e&RWHpS@G&`117Gvj`J^LTz+@8*B+-bTx$55fYKxnNx znDFeKQ0u3T(YUYA{ioKS$tkJF@drESny056sDrAmoO5XpEo8YFHm%$-eA6(4dP*3(oP^yhg)S5q?W)e3zt6)UXN5i>s+j9*<7 zLWp(}&4WrMq22d-8lmoK%mP680;*j^hd?wypW6g=0R6fC?`rB)x4Dlh zs7tdBwyLG58T$%hP1MVxvbQB#UNY~uy}WZ1M<3$6f}1t9YEquRIccSpKwJ0$s{y>xsl&|vE6R}ahCw&tuINJxv^E^kmEPFM$ zV>EBvSBLknoLna?iF5SO*pqRpF~+OpZ( zO5CwkxkBx~IHX(8G~(M?MO~%V3RSd;q?ArZJqeNrJ$Xg!6k=my`F-)amR$MIRcmm1 zigh8tbn;M(5XGFHIt1#tbG6ddQdoZEN3naS8N>X|WtI9Oc8Z~%+RfDWTT3ZbbeKhSI03LqWHLjwnc&c~tRYfI_miWIRe1uZk zulD8FDN@>+XI~Vrl@Ap9PO5QNn)t0;70wCVmklbM=De}aJPV6v77iiGn!*|4T%Rw& za++~vu-C2YO>OF`s)5q&(vGEQQkz%8q^Nr|(lBEd$s+{nSD;#FrHbLrX`uT)&mI&| z&;Hy;aNMWcZNm_(=bXx>w}vX2siC#f-s-3+f0nC{$vn|+-lj^@%dP{d_)ujjM+`&^ zcQ9zFR_x2n=F!CI_I}DJPXx5o@~0VI_->Y*R%E<*<>NcWUVUZiii&n9(wn_2Za5U8 zwKui&CVG^r1De&b>NHmNx18+&>PEhv^;y}KJyKfrcQmskEE$56s361*aaK&Dn^kl> zT+QflzE{9gk_O$zYiiB5^p}SAlEuljQz2?W(1bXos(u|{xSLQ_75L@VXkT*pr*Zj4 zCBl4_dgKm9n z^q~k*_N4h3CK)Iwoh%@Y_tW;DPh!@756{)e*RQ~|&oA*ncan653Nq@27*UPpO$t+g2>NQo4S3mi4)q4>#7Ac)> zsXIoB07in^=v>q0Aakqpez4X}vu?J!!YZ)s*Wv9F#AVNT6&-jbfK<@JDFG~1%iO^ z*G~bj?@_O07X~fdw~0~Qt5a&Ta=+$$Z{O#ci?OwWK2{0^=lM&!iJi>zQdD(! zv_EUdElWacELYnz$wPD%OevbGP|~+Fq`4rK5JbTSFB=;qkC$*(YH42+xxv*+9K*&q z_i!e_#Zt!7l`56T5$X@Zpbn6XH5FFT z3T~6AIP;M&nGMGiwqRF#mO*G?+RM$Ax<~$ImXe(bamUDI{#LXx%?7KNmVf(RhPc^G~Zv0Vg(j^=69 zCzgHOPb_m4%GFoPnHl7_tTz7u%dGbswLLSn)Rf@v32vdL+EJO+MSF=llQ^Eo4R3DJ zB4}tj=%UGHX1Q=Onvj{erMrr6)E7*5ka8TC7Pvi7hp~&bqvh&tNO+*Pwz`pWd0>?Q zl{Be|oeV>D3!A7;x4GvWzADz|mlv%O6$a)2m9D zFQloeriltjB0y_CkyT-ICt+|D)Q@tubWe%&Bp*+;3!G;>*TsB6mUE67TYSG(=C`V= z4S#Z%ReooXQQ7T{wtiW#@O@O&)s7`dH48gdfDqh?nTw**?oc-L9^~gv&@1Wx01sdA zRg;DITezP0d1C(n%r#l>@-5dCrEa$-<@L5Lm6gmgl_i=xZ9OtsO0jlOS zGTzD{ri7Adr>2OAU(dIvr=?Q=04iQ`u3%LY9H)-lthk07OqyJO9a0#!Ic{wTPp<0Q zlVRG2)Sd1vK&YP*B?o;lqOJP!_k2o3fPEXV=`yhAE2NT$3Rn0F6#AM%mi7AkJsg zT6nKD%)|PHHFL6$JlH9U7o7Ovx+pvs52?ROgT)R7cdb`iC- z=BW#9fyhet^8v~g>b&zMyAt7x-P2AY@!Ft9^ zxtRtywcJ26z`68~wVH$nJ$th~9ZIrE{&rkzSz@a9JTG;LPjb|HVmY`u&9O)6_|ZBpDCvg0eIxB7?`wP>bw z_te`Bk`(0BK$3h+c{g?2%KVgc8V|*NW93Fp2yu_*a0kQe3q>#oXfn(L=*wcG$Xb5A z{%=#&^t9_?PSaV~!*2J#Qnj_ID`^hC%VtZb2Xyp+^-SS-|K8o*oSbu&1bPcQ%b>24F!({d4#-`Jfd1x%q7Hn(-WT24Q3wT zw!EbeCv;f{P`qlhV>9lTxB4C^x$)h1B~n`vlJSQ)V`A2ucdY%g{{VmFhOd^^-1KBy zO3dHG#pexEd)6Gm~ZVr?Nu4biT zcXWNO_b=SHKJt$#qmKi&-bTnSwbm-7->j^=RX|<&dwZb{)F($N+HYW-_2@N^T8>t- zCW>qq%8#G=?<0x*nts3&4X)aGHwUT4xWg~A+i|`{%5U@-rV~V)tvy?HS4yO%=Gb}a ze^4OoaV1AkkPnnd+?zJJTeTgEI_m!(B=Z3M85ZHTL)0%|*|jdvmp9?c+K7`ISXA+b!e!jenri zR7p@;=x#2pB`I;#naw-Ar4nA;kpZPa^=_lbvNv3S@uFDd+y^M+Zu0p*8l%B&m_@a` z%9|Z?6R}iMIc2rCP^xQXI~0j$U?XbMwUCfXiirYpi(4s7IR1LpjXIsq`>!v~*~4?4 z=mDdg1vm?hNHqS_`w93cOFYqhU8`!hoT!>okT^2T7{{Y?{wADV)vv#$1s%o`rZh^ON z^g^M<>UQj9mqAp7WbK4d@n72JR8J-Pn?nwpm{t}iV#+T0z1>-XlNA>4uHfD^GZhQ zS^)N=TmH6NodlYw@vrxX>~$q&-q8?M3iMO__Zu*V(#^Lx1w5-}KXe{#|W{{M3te`r+aCHps5+Zt~WeDV&nj z;Gh1)LJ#PPo91(l>_xZ82>40|zwk)(Ut4}Zqgp@y*zf8}Qz;Aeqffp$-e`vI#Do2O z{{Z{L@B9;ykFLib(Ke!cVaW;z+JE!AGPFK=$1}|!?V}|;`2<`406cq-;G9;UucsZ` z)?PiRhVMIl&ioXGVgCSm3yv|u(p~KXB&d01 zC5Hb1n5_vVC#0m=I?5~E->*9Ko%enynEaSy5BJ;dci^q>4u8H6kKo3a45REm>wi_+ zt+uIX>GMl^E8ME6QdnuUvS^eFl%fQu_C_bUY(G$cP&D&+Y5Vm1*R6Jr9(-gayx_Nb z(bWBkN9qg6*N$86Yn1XU#R0Zma%-Kn+N^v$DowQrw9>GOQtChpQ(H(;013}Q9ZzwDR!YM928z`6 z`y)XS(NjuvRC>6r*IY?fo+cN+zHVri_WZ`Gzy>KtZHB#&`SP-f%i zioOch7dIq>z^g@#Wg_Qe$M3DL{{SCHPU-U8TNC{vEG;PLK|)+JmO>DjJ?;DPZ7-;} zmcZDObQ*MtK+=d}{ZlQvP=%QYYgwx2K8ve@{{Vy#H0v`9Frrx9RMYgya%A*YpL_u! z_>r~I9Nbb2p(>FeTwDuO$G7xAQvAxoA)Rng3p4c=dT8qJhFM3ecmbn{NhLiF@XC9a7g zryyE^2+&Sv#v(1EM#7u#++KB4B!I5E$|p`>Ar(rAQikag2_IxKL&{M-FsX-h4HemH zW_1my&)NYoOOp9kyS0D16&~%p?#Sv>Eqy`<-xT=$0@-Ht)o8&##AQAs_K47k))r$@ zq?;H@nlbT&^&$ILwj{s04Fav>_zzk1;Ig0U zDc=oGaQVd@b~|W1UE6;Z(3=-(TpEO$W-&}fhLFgqs%B^Z05iFIezii^@gXHGbxO+q zcb4VOaeIwicPr^j3~X_Rv4VhU{{RoOZ$PjcIn8c)t>rY-Z8=oSu7>TF*20vx7NfN? zN@hxwOoV4;5={P93X;7R&egFqnc6Kx?E|qMU#~?zOONqo+mU$^sUdCB+h`oEMTxbe za6;VxwA;ldYAR45Xgtra_NQyXWgk{*84ujNd%Qc&OQ)D^>(d*bA5R5)>wcVF>s}CQ zUg7$iavnR9ip!O)hjjBve3;oukc8}31(40eed&jD`fB%z+SFa+WGCDCy|kau>;!Hj z;Et=tsx{SMnAOR%u`Mv#j{C)XZW#b1(iSBx z>5;_bbAm^h;MDl;0rmbK{lfHoFKfIoOEA4i_qcZUM8$}}!qG~qS9ffq zK*9I^DXom+_^uI+=A3`@@*L-pHJd$3-y&tUtg@>Zu-*Pdt+st?ng^}TJ?ZXH8kquo z;oqV}4%I%rP|;_uBye+_8V24T%4_$EC?>b*P2ATZ+$u60o`Vg=t1e0lsHnT&ZaEF$ zOcDMeUV_r4`N`w&h5rB^WV*eKCxt*dD+s%dkpasi&xjug`#1f8IjuiM%>9$=*K>uE z)TL)|AcIhZB)TuJm{{Z3zN7)TMuGen!?g_2#`h2lB@BCGez`mCKtzuTHdv)^?tIzI9 zB~>{M;^N}pcTE6lnJqMgk0hjfV5o4AGuEr;?i-i7pz@CaqLI<`=j_?tq0SJt5_+sI zplkLlK?4bFb>&ezx-VI$IrpHy_*y-eIImI88KqB}QB_=@x=zEtL$>ft{{Snz{&#!O zF8wU~7_D^%r;6?r!CLL7$Zs^`?DU@l^hAdZ2ZC%}*Lu^TueOKwrHg0MJL&ZMz}H}z zD{U<5lx3G~piA21{{T!dcJ-}mL+>g>-nZT({{U-$1#Ec3u{A^#Z@s^`!iBdz7$QL>lJSt_%#Xs)J2E`m12&K^*RQ z9>Msk-^sj@^)#rXdf#T^hAd1iBr39rx_XMC<1nQ@un7MEHYoBL%g@R{Uens1NA{Fd zGjq6)5kdD=QyS}+)V8JKr;(SE+0_WA01x#M8RuSPiLW~K*u1~&z}j>Nxyr@xk2(EA z@cV@YzW2qKPD`^kwEX=AZ6ayweIRuRs7OqR9VQH0;`@sF2Yha;RtvhkfbDFXUC?QX z^wNOS@7M2JN5UNIYs^}&wdDBKb|Z3a0XJ+9C#FMadAD>aX=>ivzCVQh(dd7zWAEdo zMOVi8Unx7Awr?aaZm=eyd|mJed>n?kZ)u z&+sVi@*+I*xjRrmI)aod-#oPQuJz9r_boLXO3ECj`<>V>w@XWyT~e#9RS(kEFiHtS z)b1&l6Sx@=6p|(kjCt$elx)7Sx68vb1H4piS;;)6%bS_wnx_(<5VpKN*dO}d3UhG@ z;+{CyH}Xv>S|8(T^@5TMi7iPYM^o_#=RQX662|Bp>IW-r^o6foM|8LPhZ`-sab~}J zUMW(h;U8pEU%IVU?QbKNoac9+lgzS)f~B^x;7q{%E|-wERW~{w zz9w){fE8O+1apnrSOxfcx(atBJoGTHWdfejztmN$w8s?x00?ACBmV$#PBTeV6~*?}o*4hD)ECmJMHfD^Y( zN9cr67Ev)dO|96?Vh*1H{V+WHB4Bs*0AD#g`TRWExxd9H6|cO|aV>Ncw>8Alqj`K-zt>+aFf>KI%jEFND9?u}Ri={Y31w zDDL++rFcnis+gd3m~k2yi@qW*zo1*MC+@>Le!c|$z@KX6|>t`yH{23n6~ z45U5kX37c*yfi|#zL;ypKRAmc?&{n zg57Le?sI1@>ET<%cVf{nXq40594dQd78{z>%RI&o~Ct zvCH==zrFnT80P^SL9dFM^IxY;UMXX(x{LF*Nu+H~7S7a?l!^I4QuY`EwOE{2`y(v# zh8V!FwOb#fg@ty0MdTU;D|e!n%8b00q-saH4DH)qln!sL5$4`h+{+XETBq-+O|6w> zSmk}rsL;{dtEuWLst@kA+o@W=K#O~-GA?7X$aZ^VmPZB(k_h*TD#grQ)^_8Q+3D<8 zEU^_d#Yqk}HeVB}wv4u=y>D|OR#~M-RwhFhaXz$c% zs-~z84U0pq2LT5|;|D(vZ!!i)LLVbJ-8DVmf~lRz_B-1i4^50(aNPM>HcMG^r%hd~RW$m(Q40%j ztu2icAt|3Ysn2HK__b{otFW+XP3l9qdH%nsnD&3cnY^M5W~txYYy|ZF>1xW3Et3g$bD^e#m7~v?tvy-isln zWoueRQ*zwt=zJmv3L+KBrtSUwkBx1a3Lg~-u4zg`yJiA{cKPWj;DCFLR(fZ26*j6H z^`Y`hls=<-Jhn_gw}y>YxZ_tW__yRsrN2K&;`Bp=B|zyEOqAA&?szEGCfr^XR6Edj zt7ssIJfRwBR4G`HYsl%ikfJn{S2?s+BvdCQty>!jCv>zv64o6P=u}0D!Eiq^=N-NB zo+Zq@bzGVZuMBzD8f|;j#bqPxDT<0`(%DHk>&f#v4k$NzM445Zz*XG1Q01*8g;I<_pR%;TfU&#MNQ)} zGC*{?aPl$Do@5V%|1OAfHgSxQ0~eQ5zQ@FqQb2P7g8$^ZiV&hJdt0@CT?+G6H{ zb>%emQB*GZ`$z9jyNa9KfbBD*JIu5-7w2u9el#*u9UX6TV)GP%9%+iX*(0R70Pmn zO|8SNgfhLP0pg=RWA?;(7hT&q;JDZ2RZ{QtT<&J~TUPUU-MZY}QqT- zCIAydI(@KHe7a{q$IdEt)dqjuY)96zMz#E){9g|bdYyPk;29hSfKOV8+ zFv40_+;_Pk@}mAtT}yc@&9yVPp!;J*0|;u)O)xI1jUH~jL1ZY8A>*V@f)a|PN|vr8 zqLt=#ghdCmB*oKQo>(6dQ2ZmTI1hzLXK76Q-uk>E#`W60$yB?!x(ZT$C9IDxWCf6y zm8k4oU3oo0QdjOT+^edtmCh;ZnuQfOwHcj#@sg3{#dvqK5#t}a})yI+yE!6brgZrNEMHU)jp@$)(3v*$kD5PX%2#PB2(9_ zbZ((dnUx9dDt%_sW;Ey`$*Q(pl%s9}mc;YNMJXnJeInt8jVgifCQuc0xKydzn~N(T zf`4DOGS5HMTDCL>(OCXFtV0&(sul4#(q!22D(f0cM-moSB53~rQaf;E^`@rnaMi`n z(+bpnn4WV)J)^^&MHEwB+!s=mxF@*JSrP1tj=#pQ#gHwjPYb2)rMh+qb83W%Q9dy( z!|D_k@X)WNU#7=2)90Kz&Np1}s9Oy#k@l4%B!rg8m$Y<@;dzcqtNEc4Mp}FoI_K;K zTzknG1*wDW{)uxOm%)Qb9Q-%^W{Q77=q9YxW+D+|C35r6q=hW;9aCvBUxlcy@9sT^02wYEERZYS9h~?njw0ehs7p}O<6pILJ3F>YY=L)TL=L42pQ=wNy0p1df!S6^RHY84 zW9?CdZ*r{ z2qZe_>+ea4ncUAj6OU&z#W$Vr>iF?09Aj!u?SC7jeyD-H$nn7_?I!r5{{Z`acSVV> zj4cF%(C$$EA{={}{s@+KlRQ^Q@7C>1QnvV>#Q5<-(y^zMz=+3jJHbkOU4cuOk9<~p zvuvvG#+J)k-zg3*okb<2&%=1uDX(+V0ixO9@HW7J<#e+Cf8s7S!!QiqpE#w->>0nR zhm8YH7cF&6OM5jbbPcK#5|>`FQE!)$M-W98zdhQqTgD}f)~f0Dv)r3Jjc=9`^6TlE zQX68igrItASMtxcfAxl*c*nyTTEy++&M6bBy-OdWL4MtSddH3G8^ z301rf{ zcag%*XB80rM!74qr!Z@M$G6+{4;=DE{@lWH{u3=!+b088a*t!Mke3DF z-OX+UMv5uNCX&!Ui4G6d8o1Y$O!V8xn^NoCKiX0KFl*gJ3QEia=D~^}pMi$n2pV{~ ztScQ-xkZkA4uP+>TFc)B0$@Q>nymI z?MY$Fg&kYYHEqb5skL7cg(U^bTINecd_a%KwEd8hHC!@qQVpKJlDLzX7_G#tH??LM z#cRybS}5)HOSx33Ni4Zf^8TLX2b*xDp-GWSyo}T)#z%RkD}<6re3iAb5cl<{?m8vEkIJub2K~ z%5A)T$sMcmwZVDEALdQc*JJEd)(gwnzChYrt)aPEYnG*`G`-;uvKO&PCP6y_%706qN~FU%6EWbNj@Nd~Za1VmcH!(9i>l}?YQ0i6;%h}galqOV zw*55{6En2+iP*~N;;8cWq>gj2Tm5ih8Eyk|8RsIiQIru~jnaq7TbX8(LcIvZ}EaS}Awf=tN4&$4~DXtdG9g}=t9;WDq?sYcD zQo4GISC-0(Qun5!3QClMtqP?$F80`8z+>31z-0dbO}i4EKvcKC?o;6o=sE7s&73EK zTsZQVku8+8*?uo}c97g_FO-F7RY_`z`jnuluT8Rd&1DmpoH$He*0%jQoE$;Yo(lf} zCzXcw=dBPe4jPYNrk?Kw4&pdn<-HW1cZ=89LHB$)FNbcSU4X~PZh5<{NLR!2StE)QQJj^j|kwt z=vlT=b1rdXrJ!Bt@(q=h+ba=pH7Rw(mkT9HQUOYi?Zg>OD=2NBFHTX+MzlU2Keao# zGSafztamS`+C57^xcbA%CA(iDbLTMR9LbU7Tb{tUSLRgJGM1gc$E&BJ*cY`&_tUfq zc*cv827D}S0PtTv+}_;GOQdmkr>G|;XUSFzCBf0n9G>nyR>1~~jd8}*n4 z8&|NI!Lb+eQYQVVRs&kf?K~5^4CkKHv$|F+7wWu@(&akBX>S*YmsiwCDQavuQU|n4 z&MV}PDH-MzBf($C`dq`5=v0kQg}_0w+$tA zrt?m%$w75nfn-+q}%QbyI16q7Ae5H|kR z?&-Me>Bq$q$S)YqU=wqdIdhY;yNzv!k~nLQ>hCmZL$usuM`ulr=IuI{>8Wa~w-mmB zfS-tN(4A+}3$Vmn#48(#uZgM_Y|YC}ByQfGzu2nAPH=kzim!8sTulv8DW$Y0AgfC> z?v;|DYIc=4lxUeBD7TI+)YM*X`94S+ z#qqO7!BtXeD{0=67Sck}+FA5gQ#pVnR4QHEc1?_!{yts;q^k(9w>?kl9zb}KJv}^A zmy^4E!D-m!9GjebxunZ^Lo=%mVm53B)b(2!g(F}#8n@Lc%66N0HcC?O!X|fZGcg^c zmv@f%Vk{mfiW?!G$5|hWK=M3254}7&2b#Dh{gO~-TwmK|1rBjfOGiq)!!fL>$)8c7 zWU|}sP&)GIQ&4m&c-*D~BIX#YdGEj$q{1 z1!hf<)qIPp+{#|4G?b0cO{~*0P=tUq0Vo1CHrSY>6aipwJJNHjoUXrVUA|Z{9QMbH z=L@O0;_Ke!b;vA$g`ov#Lf5LjHIXAenrj&@{K&~=WL*fW)}zaK;mYKLZp|U_D_D8Q znR{}=@QO|(%BU$bdtEwMW{VQFDH~X3n1afdq%E|xQbvF(e#r1X+U)Gb^~t$lEIuWD zKjsgay|rOvECnTC6rfq4c-78k%Bte{kegysdx zsj$Y)L3gx{^upCMCJYvIzMbs=LRoS7HmGw z;A+&(LQzuVV9DjQuqFrj-6CvuKcvD=WZg*=R!?BD~-FIsb zvu6C?oMmaRRSm6%?Vf`H4l%i^cO3N4l?hDs=nV4Km-0vrcoIJK=#WDUENBbotDUkc ztXk`NFI%qXFXbgWUwF-Kt(s=du}-NeqOz8kxpW0PQnKKdi3kBCfa$6g_BK9YUMi;6 zJXYYU3*O}SZ}BDqA!;D`QTu*rdAC%cr7hM|ZQD^u;!ee+Ya=OZ?TE_(H7dd@hlczn ziq-M|03lei8j4EGjmoR*87V6B2Bu!G8B*e14fPA8! z#aSFuzF|k7c~R1PPVV~oeEh%v0BwIdMOj~@)+gVmYL!ZP zR*Rj^^35F!)hxJ>)jJNp=mFKKz?1?6^^{rP$I6!xa@ln{shct2{!z8(V$SX2+ewpQ zubP_b%4~s$(M55Q8H-g_>ifOIAkpfSIz-TcOKsJ#&K)2uk&6pOHFvX2lA9B zBUNQf_hmMhCstbyEyoB_PUREUbAp2^c1?BJ8>!p0P8{HR3r{Cxz02jbbhwT&OnoLT zPE`J7TX0_G2I;C9JKG7`2~Z&F48R5mkUg#V04LU~?p@B)tvx+jrGKTL8ew@)bDSq{ zU{&2N7YwXl9|ob?xBV+~&R#dwx_58%{Uik_^(JKMA)UoIixB?+8Z4J>TT5}=QOI}_ zeSmrowP?*YcVX@q>P4DliO8FsQ>u+ssLtlL5X+DHX#sHhX;7I>(BmjKp! ztM6F*xlQkRx##)6XYs0mf8LkDTwfG+mB_a8=Gq(m+R0GhCSkh!BI|)s9tBi#`(wzl zvll@lSK2shR!5X;j~0r0dlnM*1}*yi$@RcPNC(EUvLe~)`YxkoTzovD!E1<8xTzDf2dmf-| zZ(Q?XPK!OQC_#}XuBmkFOc5zgbMT3z!Q#NxBRiuo;yD3KcY6f-ebiU6XTEb68`v^~ z__4|?wuMSs#-#yF)NP-KSO{V9$8D7F&t}I1RQW$je%kHr(cLO6v{wrya!2J?fm&X* zK2$fQDX2rm3t3NqT>X%2U8|39uIi3%{>#ShS5y?KL-fh*uCUi#Xz;sJi&{|9F~mNH zY1#=2o9QjQRk=sc$jUqXR>r>w3FE%9l=z8H{3Ea))-j}TlNX*1$8Obj>f5*bTB^BH zGgjTwmFCy-Wy0dXC6YlvSK%NLIGw$z6Mj&mlgw-Jp(jNr;+?v7u|sF(`!yd6;kDS( zZ#698bdK9CD>Tf0UrgX_haLq zafw=SadOI>VO3T(sjcd(rlq#i8?AjyvOy^*Zka_y^e5UNT$Qv@vVaPesO`xvCzh@! zMHXaL^%>VRT(T;MS5^6y%C_ZL>C~&#eQu^?_zl1hWEW!<8AZ3a8m_r|umsVvg=|c> zDKnJk7dFF1Q8TJlBjooAE$=n>w}L^Rj9f#q3gcx=>y%q{Y9erJ_CXMuw{)hamDZgz z3qpja)_O&K+e4!v5oT)eoE%Q?%y9lV`j@@SbJetKS5iJ&scnJx0TAN<0I)qstfP5m zKD6_vKB_f7;}&8%g-rd>i$>8o&Kx34AtY7G; z42kiG!j+AxZC1Qyb=Gh!+oPk|FXNAFeWFPUfvUTvr+TG|t2%am>Lf?D8zhLB_ErAq zk4mvXb%-45sh8w9r8J}yF)bm4sUt`1iV1KMW${F@rZ|$Ew<1d<7#Q3qr*>=Ks;9i?O8)>YWK`F-k+p58Y(SjVop1zsXbX*@+q1MPy`_xlnRz~NCcjdrH9+G*vbn zX1%Xlk2v&##n$0Y;Fzg-E&#f8I5bB^l!{R$HG4HL**yavpG&91>{r)r#*(j*k?h)(4!^(2{?)V{m?$TP)&6rms=Yd&UBUkVN+e`0 z929Sr)kd1ZE(lUXw^!Zso30<$V3xuuR6E{M;}}yEtvy|Fx+a2#g|)wMxYWJr)3C!y zE;!?gaD=4@NF_w{5ft*r5UaFA+geF8fNrY`JE`yImHV>Isyi|IdRwY1ahsf}Gu~#T z`HDAgNDC=^u~dz}DQrLmD}7%Z=3%idyZoyqoSP#jM%-z{`oE8lyTp4|8@6rn(RJ=l zz8;)?UjG0Ebh}0DL${v6@fJBJlqx9l?qc*(?ZY;&Lmww;keO!I!soMIu4=V;%0OVr7fMX~y9|W#z4a*zxAFI?@W*HznIyX0 z(erDWl-wJ1(LOLtR(!^#NN-C88}H(@R+-8RJ-(97XrW@;ZlFMJhTBZi8Kz~li3Fjx zz(@nwAi}^EZAsLiy-JtP^)zMYDCDSC>FQ7Z^DSGvqm!EIBM6{yekx_F`pPlXQ?AoP z)hVZ1NazYl{g8{0>7w-XRVvBwJ4l5JDG{VJ*!WF5iAeF-Omc}0{v99%FyZq48o#0y zax&M;f)Ace_Zl_6vhm3y|sfn1@k>$zpV6yQADG-2S~^}1zKV~w3Z##DmZ z2qXHxh{0CGhL?CMeB|t-Tl~d*jAUKLcQJLjJT3g;mj-vMYFfxHjUWmB>RWjr4?0;83?xp}gx9&*lCBJ z9q~lZ{h=@y&z;z?;@Wqgvxd}yLx@&SdIS)BXHgWRA(RG%2fntIRoqp#d*)##EIb`%mU8xKz)yR09#nFJ39kRMywlA=M2Y;Dfn;?Z8{Di_Gnr z?psvVga@6x8^y|?u#~7B#ZNl3wqx2Nk@*|TNAm|A7N#2u3+7SGX$RC6!uB=w39RM2 zU0x}1x&Htn^R7E@LsvVc3&x$ivVo!+RbJ2BeIYx%e^QjE!&^E$?j?yAeh8Dl`;o8&e` zgK3Z_z!+r{i-<((+)X_#OS@7wAx2UI_e>9XKc-Ze=W|RG>*)mhbv@KoxK*^Rn}drU zK!p$682OD@SE$Jzqu8(CEL`at&$8BwqG+zD3N!xz(ipL{HDFmw;yQ&v^>^A}@d|r) zP!s3j1(?u)s5_poji&^?9s5w4cQ-5j69dRJM%cEpDATia`uqoatV+HIYU)CKdMYfT z{@RZ1-`ApBsR2(qK$#@#1C?UklUCvf%%|NDKXmltwu2;jf07ykid5=ogCXqwP^u#( zQxBA;ajLm%@E_F>PBcqu@9Kr~gWMd3Z(L5F#9aRQN0H~gb@T^s{{U$|ds`r8G-Mg5 zIFq;SlpouBgg{^SLfv?pFE9m}Q2Bw;Q-3ku=J7y{28<$daaodwsEg+&DX%i@_sD!n599dB=yb z!y9RfgX(r3AbQrv_MOKWw;u3~4k4M?Sz`RfI+@zjtiCrC%cz%}Z^hf23tD#pe0ki~ zklbY-G-u&+s;XVzvJO?K$kP?X@yI?h13yg%0wdjqN zxb5?fc@vFpcUr5iKk2tzWaDJ+2`EXC1_aF9vSFglfQ47wT25<|0`eP&8RXpLUJvrDP_fa7dKo53(=jzhk(q zKX16(MW`w9!(1QEJXrm3q{f(g=a!Ye>fQt)36PYBSZsI9Xris|i*b-}J5fsvG}|&vNs@qETNuHiDgBp}7`wqU+sp?AknDXB`Bvdxn}uI1+txR3akMt-r9E_- zhgkB1<%jio-6Hxl{{XWrWL-Af{r>>LY+en+Jh{dk8)f3g@&R}_)e)qjs|pRxJ9dF7 zodDBQ9;sm)VT3cme8HN@o4e;uMLCUVv-663M(5mnl`c75P0QpdE8kk3Ex-Iig_&v$ zSJ>ORvlkPh)Ms<>+~e5`Sw{{k@n!r?%PF)eD$8mg3uQ?S7nG8(hfP8KMl=TMK2xAr z2r%z0Q?CrwfTT04;gp|4Y81t95i{5zvPWy$;yho3(cVgz(DwZd7yP{slpO`CPtvbsT7mQy~*;1ErDBce0MHgzQ)?5<0wcfT2ysOQPx`cKvU=w zL^Vu`+>80638=lg_pegpC+1w}RIr&7_P}!rwGnFGSFFgnR;`?o=Mt@|4wLqS8P^&) zYe43VDaO@u`O8~jN}kl2>n{$FbZ)3rlQxA%_fO&0i5Y-@=>rfe2*<*gXi4Y*-%EZ~&1Ata8Zg0zRU{`R@Ai||Tr3dA2x#?@x1s<`6i#5nC$ zBNZzPd4v?sZ6(4~ku5hL2>K_=?&E1^N$n1=iufD-l=530dwMUWCNZESP*m`#SGvLV z9qv&N{#$af)78CBLPM*ds0&p6nn$!pjI(*wl=rWcvR+PNdO^&b)7sR#miv~sb9q%JC8MEnP)#*R886inG`CzFWMNhAI)xOdpfrSN zdX4EM&LNi|6;0Z|s^))1Zh6$hLGNqSAA2xYsM%jTlH$6 z2}>-bsbus@Q>@}S7|`0MLp+XzwGCc+`VWYRR+4oo5^Jw@)+3Qi8Yk;@9aG>T zu60zDM;KPA140Npy^z%uiBP@#H5vZ^Oidb!i2|kV$^ag|5ZMx>^}8YgGBSvXQpUMM zyZS4_I;C`}8sI%Ol~1pP1v^b@jJ7mHsjA8p4D}r$kt0ngo_4zBI<}h%ktgt-V#TLb zPc&+FxjLF`SPe(0+{d)aFI}W-CvB|iu`S*8go7ZZ5DaIFbz1N~8mj@v%5l$5olZ$0 zfTZ;kp0Ryo^_9x0TaX@MiwF&XnIMFTYoLSjPoounCAE=N3sTgpnw?U&d}C+7$}&+J zxou=LYJ23b?WIPtDuQlCiW_~_rhOzcYJrCJ&*aW<~#^kcA%$qd^ z=rm)D6E!oZjO7e$dMBHTFHSK{IqVZXdP6!zbeD#TNaNZT?Kj(XLulKz#Laqm=wf2e zg#j(aO3Z($RyM0QIMUtPY$dDZ9V7YlgO*ZP)4p9?^I8V4r%$1uZog0b z=jrEo=8o$xOec`+*G#cjTPp9hR8=%M{g$OlrE-priNNJTpQk``L@1RVplK-bEWN|| z=-rpJ;_ZbMY`k^{+Yfvl(BxKLHs*|jhj{Y87rNy5MVkACRo7*eG!=Cf6Xg%{}`Gw04qq(Yu+vX!jL2vx( z*G9UBE!Qd+>8NX|)D)er0HC(X)^Z@vZIpO-#D=`KZz%(H@=HNj^;I1WRZStJcB!V_ za+O6YEvaYKiLvp65?nNZ^^5s?llB4FaSm=0pP8bFHk#wsc>( zSCF&*KD^Suhh_D+<7--eb&7hY7-`TIF}*1=G9ynYzKOLIUUz)=HKv=by62qVxt==h z*M&Ke-r(&Sos%o6T`ff&4otes<1W0@l_(WdeNvXgMGG_dNdS#vX{}@4iz|%3ozM7{ zG;_QTXs>BMF`7L2@L6$lAL@}6Z-@o41vYBCmgX9O>uAgBTSK$nQuDLd$nMe@7y+v4 zo(pQej`q%A&2HJ=V{GS2PAIV3ZTBinlOeBalDFnvevj2{gtb{vzzQWHTlZ)>bunVx z!(wddKLyq%IE4yi+L;H04eAOkoy$ zM#-7i}ZN!B55Ksn{0Y?%BC)JK4r#TwPTp_SI7A_P0GBWtBdq`9iG0{ zEEY>OBPnuK3{*0;xl*?bk`PGR4El{pFx&|Vpyl9r{iA|9E30S6$|_y4+*8z|E={|7TQ>ICp{#Q1 zr8comO;i%`3p$kSS~o0!DQ;o7eaLGY@KaOxD|qi%7+n4j3I?r+-W zfB^e-{mRhsPBW9+u~qV&ua2(o+PM2Ss<`BM1;)u;Xr#FDrS_rP7aKmKOG;5r)k>67 z3gnantigxO*j(BI3HP9&oAZ-)N>VyqqI;n+_Ma^6kFd~ad4v4!JT7ppOA zwH;MuO+!>yN?LlhXx?31$x8u1AP)|Z_*?L)$FbXk`T<8^l%<|)DWmm*M7-7x++ z!&dG|m|c@qPLkvG-z=H3?8r$43X$>ficF=%f?tFa;&?2B0j&?&>Hh#9YK*e1FBHZr zc@F7o;Y;3IQ+82B>z;G#&-j(H;3!QUy58f3L8KG#qho2AnIy%oG1bNxZ04~nsGv{iJ+6BPr{+>i&+g-z9vp+s5c74S7fw2 zZ^=Ao&U{IAez%M{B9@8FyK_K0K-FuBqsgpKB|xODkBv=}qJom4(r`P5lv+4V2qSMw zk4`?t9`pYIA7T7G%$au@aZTczCgQuJbho}atZJpI_rlgswXw<@o><;9BI z<;kmx!wozeOR7yPDs?GKTY%V^ji~}cdly7mNa;^fWRkcM&Fu7|efAB*T)C2S1Dq|$ zHd^dgLu`HLH} zp%fHXJessr)>XMWrA(5dw_Q;?ik77kO!b*Mn9;@XiVXs6mVkRozCE{arX5*HcIA#& zuX7jRtQACLSMQJJFBOxLE%X|0TGp?HMI8^qHJq|N#hsKlp>8WHw)J||wkHb3;?EquMzV|aRPXDfD3te214!f5W7Yh9V!?-1rJsjSBlS`_1!)Gtp_B^$mJ6Ev)T z8F_0KwwX_;A~{bi%-Hy<%#Uw-_-=Dqly}{K#YRRbz z%e0$Znp&+01t6s87IZS1k&VrkN(-97a06uY*?HCOKIX;3Yf?;U?vD4fTa~8eX5?%K zALOb_vthf6>ezOX+LR;&q&T`_Qhx}M2=I#Oe6fbLjpeH5A2)88IbOh1lF8nO7}MMB zjO87%Su#5M3UyKbVN+UObkY$oOM8^1MngbJ=@oZAR$MsNIg3^Gm!41v`DKJTM>$7v z{k!CpwD#2YPl-~|(xpO;wN})$l&I*Lrk0%nQ}LWhY2~&1UGZEWa<9Mhe!<$Z06jg4 zzV~yM+S@(P7qWKs$CzC;#5O7)rL(4?#}dd2Z7vFw?No@EQ4^R3QFU$Q!()Fmk~!X` znw!nZ+-VyFeLlpQf9{*O%&O^0eXQgM?REC>gwa1|qNKKly%Qt}u-H<79YTpuvQcYo z*tqy(EDftp^)*zto@8X^lbfHEj{u@f#_`D6aH?>?SMasV5?PWW9JxIb!X-2XM zD_2AIMVN=gQD;s>5xq)wSn;-LY7t9~<0>^SNHn(ERb5ocmWOJU_Xr+q`$z?4cpqw# zD;$;40^TPY_yVq$3EGx%UrgR%%6BF>uL-A5satTmtthxHp{$ywS(-`kE0?&(oMrsU zguA6?(wY_hVp4Xf zrB4rqDxRG^fJB&n&bXQX09AhdwGH4FW4OG+i4eO#MSL*W}o!l9pG|D_8*d1$2Qk{$W7?Hui&_Q{lmD`xDA5+kh^w&rU7% z-22m)Gw zI;VqpFLsx`Be09<@vP);7jY>O%m$~PxL1n#lM=P1+~xc2W;M+VK|^6xWzSlY;POe) zZnjVsuM)6MuuX<(*0=ApevGAC_$K6gVYe16yJoU}Z^b$Dd)j!v7<-Q?nfV&+i5FD~ zhZcf20l7N-U47A{1V}0sEG_9**0UoiuB*!Onu=t}Nu^qg>ycA*Z6m7BQSO<8BzM6? z1Xi_@s?K+x&Q0VT1&w6*Z6z)pk7a@C9OjHN$}bsBb)lB%>1t|{*)9qsp^}!;p{W3# zk&MS<%rdwgLDYJeqFhR43@y=o=3|_ro4U^?U>Svlo>bvXMx`}XxNYjK)SC@eU0SJ- zs}0QoPh098JAgtrHnfFqY|NN#?(1)kcftn$0Jm-)9-CD)&l6a=1?AQD@sPitzNXgY zUkzOwrnhG{>wLnfd>rPfM zTU8xx1CB9YZ+W$~Q%w6xC~>EnbR|u=k|d<40uNCfMG=g6a&*}*!M7Nue{SUv5K%O$ zeh{!er8eoBRcK!h@Q)JSXqw6SLy_-zHNAv~Yp+zeUC$%5QKjaAZB*5`^8$bhO%?-A2UT7G#;O7+UD-q z>jpso0GNuOfAIU2VZt%~&8jHtvmf!bZrjsv{Hu?VBHcSlN`tRf6zxm04w9J@AdxXR z)m&RiC$(d&;ED$NllL3RKAX6&9%3A7-x=TXA&1?b$lt#>mZx^Fs%g-+URs_6vYp~h zk+f|9Xdpv~xh}6VIt+!I8_Pu3!udC_Oat?}T!V@)7aKLhBzr8jCXSlimzb`oqxk^I zC1Px}*Wg6R2U$)EILnt0gwXobzpPpDYKgm&?klq!{{U;;qs9Cm#90P2nAfm|aO#Vl zT{_q=Ra7BW4?eK_iY^cggrp~@j4hTv>8~8_s;|o5+PVYFqP713Ou9;LFGn|X-M0X> zSSWjT<5!-htZlLX0GMAdjC$#1N|IEcf`L1>kub0#zrNuP>01p;$kW)fURp}{S#fyn zm?#gnwI5{x{{VvXv0UEs%3POQpXE694Tk-2b=!qnXxJ3&in`~E?@2Q<_>^b3ljjO&yZihkAct=RR+b$9M?#gt6)J~Ydz^d@RkNT;YkKJu71 zw4Otpw*Aizj3HXy1Nzow9j|dNKifs8wG3mDEm-p|DegzE$}UtDm43RuX&$XJHs{+( zrzdI(T<8JQq%G~^`otZ-f3x@3>_Xe(U@>!&dA)+3`u^nfU;DwFfo{yW{{Snxc87^v zZCERJlI9e0`?IuH=Wgqzak`47y=h*@D9tR`Aq$rE0k^CS=KNKuA7}AL!-}_cC`~x% zKJ9h;ly}F!?+bFj7xC(G6?Xe?%bpjEru}=*uNj@r<8)f`OVw57H0V`jx?-MzH9s#- z3iN3S047q2+jzh<9r8!45r*$>8pqht41>a2}TWiK*M>|T;>~-3lYf5-i zem=UQ zQYo^+X_*xA0Tkh+1a0y7Wg!xar&hIGBWN{J_Y8M4&7Hiay4}UutCTo(@8HU~rsmRb zIA!jYN(*wHy2)BiGLYv*si51QOAzRDH!(Yzj-mICG^qP{Q0O(Hiy?=$ydTwXH?NNl zKX$bqr76@}F;3zxwHNN$s;+z?%+*4oqUqX~^Bu(4F0HcBd2V617u;a)5>o#FDyfpv z2@I&A8sFPqK6e+j#2RhFzOFv4d^Gif`&LBWSsEPQ=H(S1{X&P2<{moK=qiK!k;!xq z)!QrcJc62{ndexz(A8Y-$bOokm6qIT=AU#e3qpYkLVzIXK#7@{xCWIW4?YIj#=Ncf z58R4B$oboiFLrBp^uiPf7fXPcJ_q>FVa&`&%$Uq2uy%|d;rK0j`YYk}Ci~q_a`%B% zRQ?^0P~iA2#l;o0yRKVOYHkuMDka8fs8W2S=e8;E^GSJ-Bo-W*ESum?g<>4_9%)ikP=Hr6~s zXY3MzE;cBuUzsWJRX}%*^BZEOrP?p`MF}1a)PU#fF%NJ50BHMC=Uhy{`htE^sGufB zWOeq%;;B2M>T_}=M9J}vT#@Q^N%K8o2?Cle(jw3#LDpR$5{iu`RI6^|<>3LnOq;cG zQi7)=0zKlW=(b9hl}TWQNXtX+Fglv5akEvIt=^gVkB8kHxkcF}GE(xFEvggyK`<&Y zuA*-=r2Yxa_(CS78-9-Y%>LL^)`}-}dUr6AHPf%z1f?6M%_x;mUm++KnyH_%e_WBX z9(Bo2XfykwOR#!q8#hu#7hwWst!JTG0RI4`754uC2v&Dmg(~5n^@$6(t|Ru7?sjhIJSW{o^n%E`uId?Vno#ZmDCA% zLMgOG6t%5WL7Dwf=8C0rN~&GX5OK%Hl1$>eA@VIZYoF=`LVIoaGMe$$Zj<+_De+$_ z)n~%6-NYohi#k)G^wksPkMz}lY-=jZD_u=XH)55#K;Q7I$aRlAJi)idN*$1Cb&GrXuM0i`~J?<^ueo^?{<@w zQ}&C%J~7pmlwlfipX(@C!%Ye}}J+g*B@0{^l0U z=tuLodz<*5+3jCec@4??#Xxc362d+fDbNC2Z2)abP!pg42?60Atnvt=@krw!)TY15 zGoaW22VZP5s&(k3Y<}p4T)kB?WiXMagrkCEn^Yp2q@bm)xZNrmx7#hvIZ<=DKH?gu zr>9{B*By0hASDP$B$LnxON*AlA2X5G77|=ZMz5Pr>ASV%->F|Mz1gQ`X?BBzQtbH$ zkpOvwUTD}@L)CSTd@5?#+5YXH#QLQ@45P<#ISY)v&rUCs?x%_Me&1zRwqtQt*n57o zzdsM*>;0b9b!=4~xBW1LPY$J47QEr%PN$|zZ0 ziy&xRIs@mZf&@>L)SxQX1+IKk+wUImjywFj$CehX%v8yDo0^gxudZ|rzNJM%{xzr$ zlQJFxE!k?pO%P9zlB9uZwlcK(>s>>DNiBn*@#r9w#BsVKw3B8_)UdKm4SA>M3 zKC17h^`X;8ekmZVpRj(|C#sX_y=Y|H@5W!nDL!IR!a$?xBZ#+TNgoT-pNR>F7d0nH zp;2kcRDZn^_b2)x#zvvU4k-H;Y=Dpysh{35AJGx@Q4F@CqFw9p7DLbK#rQ^!^wo$y z@61(Q7LQT1y0jD{asrV#1JZsmJ>}b3YE=vIa$IB@-vysnFQvv&!#3t``GtKY{?F>A zq}Edc6c(ifr*aC30FxN|E7liLZ8IIcJQ4G_)F>C2xVqbdS(fF#oHkdz3@8CGppE@{ z#+jD<%+Yy8)$@ePzGQp~?rCGf^V>xjUB4@!QrA}6PRD9#UNp}EA=N8M2ipN!VtTG3 znuW8IHv3q`c%%gt=GnGR>XmepbH z&j)7HdAix(Z}|4Ar|I69DVmviO%wL*jH$%8n`QNcJ<4ATU0uQ;{xHf`*bU2FK>{9e z4N{)sJ}U6hTdfap)m?jZiK(u`m~Ff5vD zudOFZ+pKS^db74};_=+t$X0E*?Zu0QxPpI=XMFzv82~4!EUjtxff#PsymS{~!05f# zKZ|YMo1r@M4L!U*{XL86zDc{K4e?Fgm-h zi7u_)$sF2zr8m@EYy+`C8Ouq2xST2}4HJ7Ht0}YGoKM0-OX@t3XVw6}_+u#CMzaKo z^tR-6;{DcROg7Qp8*S4v|*pc>19IMkCl8^q=RfPB@bN>KV9X&Y_ z79;wheBXBxspi$v{{Xym{&aZnaeV9OF5UjpdG@NU`Lz%B9BdQk@j)H9g6^Y0wwsy3 z9B_620Nzvo05Uxi`}<4GY`?y~YqRzxo7E*|d*45Vw7-XYoaLU} zIOQqg`=TwsI+oQ~7T)=*!&!qbWP1)9KQ2Do`fKLS@bdo0H+la6akB0I0ILsmjyk1O zZbb7_g_xXOINC7Z66Z-0(BlgiX{I4$@gyjsuuqMs~DE)%;jf$WcrbP zO1qQGsIdMG;ya%>-Rjm5mMs=nYAIV#I{MX)t(rza@h-fWvbOUEVXVWHAabAU9x^SZ zezbR?5S}c`8=PguDvmxrd*YN&jD>FL^ zk|ima^KSI>D&^M=b+f3^y3qDNk@hUMd~*(Bb|g`Jr~OQGFRHK8(~~N(JodJOE8+fK2{Y;D+VuHmN1g#MvTgM%M}75?;g1)A4vAZoHGg+mH=( z8ix^D@gUFw4*OJX!lt5eCv?A6E^Mr$qT>uI>6yD!KxyhsVZ%-*{d$F^4J3b@p&B37 z?uO=zGjrv)ZZlv_eRciIZhLd=lj|AEZPVQOH#fyNPSW5;=4$DYbGoW>(9_Vb(@)y$ zR9$z#f!`CW>{WmG#?oO(7~x+UElR=um0Y0pC#lQ5+ug4*>wP zR9KI2Sv6bM>(O`lKRTgx%B*i7VWKyDyUsR+Hu6$ITP^^k_!R(E4;}vicpa{xYtNJ& zxqlJpG_;Mu`G@Wzx>w!>MRc384t~h{kmI+nm#oq2Wy;p#o2{f0QC?t^B`+eUS8 z3Zmb&@(*Y&VI$a}E5~*gOq%y=!?Dcq7u$tuiW#G(sPz{qYEIHjr4@Ib#cbvtR{~bk zw>uj9ovxLHH9Rg%Ce?3R0p9b+)OB;2+^=kTMndDBLgn@kZ&)_o0MLly^kLUT{w38Y z1cBG|#X)rzl_U2*erm~#T5+m%ZFTYdR>qzHm=T^S(rHCjv8GbZqtsT5I)V014`XVe zkd%{Eaa#{85(K3rog!Y67DAJ5bq!V1E?SiXxStq^h0$)ItGy^`)i|9tnfvvK0}7c% zJhJZmJ;w^>rraA-Woct#c#^PGJ>jYE7Y?84MsDT*02r?>;b0XG9_0E78;?mjXo7BdDBA!$k{v(CRl${$cME zGkG@amCuds?rcuRkhVNQ@G1~J{{VD8*}vNbS@sY5`Hn7haos$73hRP9q02K&klLE# ziaK;Hqx?j)NCsm~5;TJQW4k7~pO>Zrhs{qTvG~g_sk(93#U*nGb@>J}msR7>@r@B( zDUe$J5V`asNia8Xcl7qN^txc9yu0!W&i5YMfp-0TKjNgD)=ht?;;LM!+(LmWyi=k7 z07NHnLKZi8fMz32aeE`II=q6e)nf73D@gO^)K32Zfz+>xsfgx#d#M1`X{YZ4AD^Qn z#rnDaEiZy}So3--(vpgIeOFiQpI>xYMRdpPt1j#$hj63@E!S$07TXI@SePpO#t_dI=q4x?IIG>Vk?nvi(_w@x|t;@~Ud8VYC^gmDE+5+oP#ne`vh5UG-tBbv%y@4g%ghF#I8p9xBbPd~B=QMXjw^}^mM z-92DZRE3Bt-&L@s0l6S(eBwDAS41^PwD&TFYGzV=pm?r_6k)RzN|j*NFjjyRQVC4F z;FG9O4GL&2!rHc|$x@u6x%rg3MOJA)j_s1JA~#=N-v)V7)Ex^6@Iv1?fEhtbCI#E1>P`f*p}-l%2x1hx!+j7X zE`w!9k+u}7iP~uVD)1f=cG)Vkh(d%DGY_d;zbQotk_5+yj$F7x+@9by_eW7E3(-=a zRZaScE~Emq0-eHsVsnIhlCm;X@!M|)b1yn_)rQl;IQ><}Cc@pP$Sbd=*;id>vomdc zRidKiwwQU8GNmv=TX*~EDIgFPAnu4(HteRWO}nk(tF~*}pCNKpgSuV;wc2OhEIx=p;>Fj6@sqO zm0MP#ubHCW8#CHVO56;*^?}&^R>gywDkRK)sk&N#hn2Hlgm{A&(3Oe4kCZEMSH%@~ zI}SB`#rV%7xYlLO8iz7^407pFM*TA>^v2g|>DHBisequVPGT&Lyv}dKs?NodwT;v| z6k?l$`9bg#N5}ax_K%2vFWp;|y!Wyt3Nbe)f-(8KLHUu^`+MfPwHB%P-yl#wiE6jh zH!rbaFozFvcp?7)$SpsaUCzyWT<$j><-9`uk#xH`r;?CPBGN$WK z7-DzsTB69a3O7qhaddW?D^JajP&!H*XsIbmWG4gKf=1ee*<3k?1zr0EycE{wAz&%KUJ<1IH(WtYD+}=WmZ^C} zjbWc#1rwbP??Ke)Rri98#Bx|HO z(GAoFSCqL<{Vc}i1rj=YVT-ucT+Y-lwoArOe&I}+)SNe-?Nyk~;gu1vRaG`uLh6Ad zR6YP2{`6z}zQBl^J9lBK*>-*NOPvF@a9PV_cZE0yk-2jm?h}tMT%}!fwcV^1OlI3s zi&`jhdIyRHThEtUT2Us+3P4dELQLT6!*OwL$3WoTCcYjCEaHaVXEIPp*nRq_+mCrS zEax8IbH35;7~QUd&BhJgIX8$YX_|G~^w--`r!N!;b@vop6%_9&#EAtJ1s;R}Sv8|$ zxa)pmC{L?`v4&#kxjZ!=zu#I9X;dobTDvXAo~f`NE`+~KWh5yI=p<+Eiwck`6x`g+ zUrGFX#JGO|cE8*AH+Hd0PnU8JKO-D_Hox4hP2ZfRr+z}bQ8eNdnopNn)wsx?hgr%n zlgTvFxIEH|_SumJx0F;GdV7|G_c?)EuCy>srj%uVSUD zM!hwpq1K&1+BzPB0>n=DRI_@~T4n8+)@!vmFP;wR==gg&?dKh;;jwtnGk)Bv=&=rd z;{ z+`FQ?;m(S?$}6ra#j)y(!*t*_T%~X!x0-2slPH|Oe^B#^AL*52GwF?VAp_pb_ zom-DD?Db7meqO@VP}_580Hh&eL0(a-Uy0 z+vAq}^9RX0B;=dUH`{ZZe_da3uyVcrj+&nBU7SB$wm&Vp+-lr!<*4?kwY8vk0F%2( zC|j0*?rr_(hCi9SqeJ$3ej{R{Y&ddX2l5@xjhueKIWr8ba+KUIHp&c{HhR__3KiD1 zzrBLwfLk>IDgh>P1n5LCHb(qNJFBNe?Kd8tpLkl?m+>vby#Cp^f4Ck%T~*VW@cWZ^ zr!(?3QrGC;Y)CIHs-!aCrqNq&IE0j>B_Jt%WfjpmmiHWupl<*;D*K6?_Il~>QimGl z*X|STbC~;zz2V0eEfA<`pPZzmEEK)J-We>Qy@2 zZIHHckn1D2Ke-(%4aow(2-qkR)5sYGrRE-Lm9?RzrGBd`P;y|-TFx$o_~M6~x7bFD zO}kn8mSy~7abnMz7cv|rf5WP=R-Nk&oe!ev+)`vthG{;|v6}84+P26tt@vc%LO%tV zyv0jLe^Mj3D`B|$@5FrIirj;>ZcL%XatqoL?oGnCrkho&wMy05Qm$)guTWg4Doph$ zG38fsUt^uidcyXNoHrk8^x=iaWj-r6hB)rix4A*QrP|jSElR1+aZ879H}GXybEQLf zOj4--09mSpZ9{V=a$lxp;Tp^recThv3&Y#sbSnwmURm^oXK88i8ifM6*)Y5zZ{a+- zh~+d1GZiYhB9(GICO14a_r~d-V@knEB6rvc{fo`z^4`u*mS8=)BpRN{a>`)kMP6Ns zFv~(=E51_9?a;^wBzcX@Xd{3A(yE{3yiFh8Z9S{nF4t&jZ1)?D!;|pZR8zF2wNLqz zFI2jutMDNxJ$#~RJK|FC(G!h*Mv4ggD$SsmPSAn>0Al=GZL6ZAxO0WA$EzC!_0Y2Q zMLjgrzxa+!4Lo&+w>OY@C9x0!jkt^x90R8W`&6})_UpvlbwiEb^L}2Spv7}K7OOE! z`f1jIUXs{xUXW6iH65#Y4>-qgh7qhyoFI&T^u6jvAI zKEwE;p5|{ii>E4!lWA@1Z9+a$hk8!!=m-pwtt2%%M`ZEc%YR0P2!gM&?>@`Jx16xd z?gNM5pH_bGGb^I1G5*o}S5>rBB`9L?M`m`RVG`ODw^G|}5A@t4&M0T>d~LYr$lddw z0Dp43ht1snK=iRT1~#50v>#zKA9{m%V-4k$)&BrDcbD8|HNsYkuvXQgw(u_3_mU=5 z)g_wUB=3>XAxB=KEuillZL>sJ%XdBAG@$m^il~#x-r&ef^%;wnvNCoK*V#<~J|8l# z7f$v2L*V*mo$a%UYk8lMSx@l!Z;)DDJtZT+e|t-6Qc|tJ>L!-I&M{kYW0}c5my&WY z{#O1WdS(X!fWxpl$|Cs(=6FA_ijQCb6-qf*x~_NbF8=`e-zDZ%H}$q>LvI99T4({& z{3Rnt(?*}Ys*j3{X>m9^O9s*w)7QlPsGN=E)#3C^+`g6J8`W|W8NF| zKqF5WwECi}8;^GdtNo30l5@54H*HN`o3T60Sp9gyZcH!Ia+@o+Q+&l6s8VD>QX3$U zXYdH~9`~^@o5_;KuF2HC4ZqbG{|UITG>t zZKA2JE2nu)_GerR5M?ZmrZ60yIJh2~6hzAFuJxa_e)jT(DPNxnyTq6mXAwgiy^gqXjmIT+%+&fpoM2^APSXp z4g-+U@#h;9w@+rpIZ~NfE0`rK^v_gL-k5!K4*c?H%8C|*f$1gFJ*qx4im75IzNMtK zx@|_PraQ+eQ>Y9rOjgmYFErua)67(~t>{}zPSTa7KoqGY5OoFyZLL+{ycMl>Qq8NA zn#(RNS-YFT7tU6CbP8rO+G?R;2m(i(BQgJ>FtE=@WVdb%OAS-PJNipCtf ze=x77+3WjM*@t~USI;VL_1YgaSh;SloxrO87R#Jx0pv?^PQsSUHOpPL=c=}s=;{*L zP>@f6td`~0$NG#k@fwfdp6m9`Ibv|;rpJFzUez0(8flBvcd^)48)Te+S5-~K)@;k= z6_exF`*^)J5F1kkbZaV0A=Lq~MJrlJ0FmPpGCMoxhqTnUVip?q>gwIxj}!#!r|i^z z^>O=Z#XD#2`;l-9rW0h#cpIB77Zl%ebkV%0v74p3)TZC4TS`h&+d`s23U5QH(L-3N z&fHwvF!PY+9RT94aFAMDOaw#X8lO#+!OWbk&76guUz5tePAWU)fps->R6LDd!Y++f zSSa17G}R&NXYpBVu#ZtFTEPWU(8M^q8&<>vB_RE^M&GC=ZDKDugqg_MMhz zcg{Iyu%*zwZL-`l8tjgPE68nh!ydP|O5F;3Xex0s{v7q3y|Hl|5`I}~s^r%~-Tg@h zs`uCp+Z;vq-`c)yL3p`Rnyezq&Pj4jHQwIBnNsT;A;gCp*y!A|AP$q9E8liDcu5td zvDCFOGyLD_vx-({`5nW6xeoco!q;qfAE2hW+-WYh%Y|0@Vd?``l9nHQkQPZZL6DJ% zn{|z^zVUrkLp2vZU&Asyp1Sni(NarP z0ku`Q;+g=VYgB?trU6cod4w18Jegbw;Xtx|?G8TL$aDb?Ct8w!X)9?}n&7-o^vuRA zcM4SFJcHhTQ&n)Gu%mc&R;zt$UMm4!gH*JQq!Xbl)A2@n(%Ngj;peSe5c|}*%j;Zk zXSOzqOF8|z{{Z?m_bH#UzB|L2TQv+Q)3%nqM_oqH92zJYs7&f(QQhU-vgO9MHgZ zUjP_D=fBSHu*=;opSmm((S8Dsk2)?>uk6`QuktY6649UetVlX`b&o6XIFrN|CP*Z=gOQPhjdm4Jv4N8g1kE`i(bIFF#*` zutqIZ6&OWz+LoQ#_8OzSwCgn#lq`zHZLI_;3rc_~ zQb-_)gE)#=-5m=`m^c+|Etz&J-1P+(9n3e1JVTxsZK_T^?gpU(o{s#qfXg<#u(Xm2 zmmrFJkWSnyLLy&SGQuL`P5M_SakSob5t*579l#0vVwP0R6|qXfvWIBsWW zsKxSY4MM4Jc1pAayH4_CWi*nNr9g!t#1fUHg(Q@aVms?;U^H@;-SNLsud#hWb|=Q@ z;cmcqM%PhJntS&=*IoHK{6dPP_uE;fMF6q=O}+!RWH@KM$pq8p?dwG^SlAtId*oCS98hKT!p z3M1RyS$4mb?F#6pY3h=nY24ZI_CTGuYs?h+)_g5@sY_uc1wwJ7S@dZsBzrthgmUCU zq-o^?%b`R>)PV6R8h^vA22l}&k5H+hMD>85vYdp2Rn{xy-5D0knm7G7f{#gvf-|nF zVx~M^yVOYmauU!)iPT8>QzWiq`e0(E>1d(wujiLCLly{xC^(v>Fg5^w=Eqo@##P!oycOsNA1e(fs2phzYlsk|m>7#+LKqw|>%5c3fg?zlGimCT9 z+P{{n+&am%0q;nhSL9>(7MHc>Z&WG|+hmuL*Oh2XreArATt~{bUBR-myz5F|#vcMw z+GIY+Hz>-+s=Iz}SyoP0de-XKZUF*x%iXqGd?s<{(h$eJZJ+p0R~C^}!UX+WOj4VMltz%!xBHk5R;T>}~=kw^}cqbDl{` z*-gx|00|lz@jg2%-}O(rFSpJk>}RtY%GN3}9u>f9`D>k;Y!<`W_anJf!fRG0bwWQe zNBP z&@xEtHB85#V(g;F!P920JD(Ni+5FtMvvWIPuuI*l_G6RtW%Zt9=ck`tqOD1)r?)@5 z*SYJwOdVTe^q&niHg>j-aprQTblPaQ27=#c$E~z>n^g-|>wOXuiV7xBlr-8BBodH1 zbc?1C)d_JZ1T$H4ac!Z0Ek@k^SBccmSr2SKsFkd$9kkmm;Gw4dcMs!rQawGfB1ble zH*P?gr8_6kb%=~f{ZtsZl0te;Iz=kz)v9SbD+CC~_oC`OyeDpSyt zG2(TONmQjtl2wNrPVw=m12L>Qj-gLz=2erDuH}w)pnv}9>v9UeclTyLOfw=s?c9UK z3SR=PuPIs@$#E;4nQeNFJlQRdvT-AFPuN6x{hXYxUagXhOCKp%rQG)&;dPc8Y(9?- z%V;?^-%a_2RzmAV{9>`N*4I#%QV2td2jK*dwmlcNFAR_6abG+4_ApwrosHwFn*Ng5 zXA<*20&;H_@(oQhSqBMZwG|dNTv`K;S@Hx5xuhLRMM_;xktO7lTF!ZPD?@~i42`Qy zpEW;+-$kPAcGuhXGggY)3q?EiZ7eAdpv^;BgQ+nj8R^5Sr@!ngDWNQI(5YJaYQx*I zE~Pg#u$EGNCdSY-64%=s3tr~9n?e_aW_*>#z)OaUH<#GAYx&jM>ycGu73-_HRJ`rY zH1%m7l}oM{7B-ZbYI;Oh`3r`ARb_?kYgaRrynsDu>OPx&t_MrPjQB{V#85cvt_)ug0dT)HIcTUeMc>BRyZghQ0w?YK=v8O z%#*OiF=*Ts_N}zSOLn+xP`-gan4R}xcHYu3?lH}Hirt=M99pOB%f3|91ia+}TvF(X z1qw4uN(esG$I$(=Zu{Fdo^6-Peq`T^44aQGkJs%x{p!}`y~6RXNUhB9y)CFJa}B(m zC(=?#(l&5nd zU#hLtw+mx*7S(!8HW?>!Q=mVK#!;J+2mw}Bc9%|QH7Sc-_1NwvjY>+~wmty>D6+JH zi+6G~NM?8%0l2}7u!;!eI^5p#pKV;%6SY{ME%h;Wv)6Js@tnqMm5n9wB;Up4%W+EblXl=Su0FZG0Y)=*OAd-f^+ z0QQI2uEnCx-P4XKn?Rb@(n4k?bWgTDLf*@(Y8Q&%UUaU?g)olZ`7@Vuifki{aLa~o zlw4LtHICm-x`(#TMQYkMp##NhAtS`a8!&Ejmabmzx3`L~G+X=HfBUq2-N~Zgf9@|m zr{bF<#Ve@9ZKzFk+hl^axcdJ9g4&Gr2@02-SqwF_I>>A4S^0bZdGxnu+J6uDg}i)2 z^gH@-cJ?S;yzxChC0;22Tb215-MW>jBd@0Nl!;HnY)Z@cMv*p%OS{|2MP`uD@EQ+r z(P&k#ldx_Mvy5Mda5@ZHj)Ab7eWKq+#Ul-+B0%h?N^~CBid#Orem#{G*@nPq%}PCv z_W{e7>tgaci|cmA=-g3D4txo7W~CBH6A&xAe$q{}rrH?(qm^0R5(X#p3 z?*p*T;w~Kh$oW1qYefxDSt}Tf_N|W1ez_u#2@d2&Bf?T9WE_JgKZ8pSAMOt>b0_3iDVlfHrlVOxQV&DB{E;QQDyA!%AX&Ax5+t3oSHGkh8%19u zlx=x^DOnv<0hiwoL6VydT*9|DRUo6G*Y?Cx$IO%(CiUzqX;2bEynjpr;Y3&}59I}w z;dRH!N!P+FZxxM`Dxw}ZxG9@G$R&EImG2%M3=IR1OsQv&`k_|qokr`OLezI3QCj`6 zoR!sb)IlG@qzqD_L_B~_iPC)~2EcVeT_J8SFL?z18Oc6Sj(JHN2?^7VJv4-<{w{~a znL*K-ganPERH;YHRD55*&_l|Jrl`eoQvDJDGXqdZv_pf^xuAj%6{sDhcTr1;@$H`x zyyH~26Jk>7g<^w#qm87^Gu#Z!jHB)U04ICBkH)gVt(8=mU9jz$hpvpz?59mY0U1kx7`l0Rs9 z#eK6m`C6J#@OGW!up|Nq0O(}`c16$q%?MUs>?dqRHZPf`lp#VL&*pF3xo+v`(j~t| zW7Y&2%pE88N1pfX;r&E_7pM6l+iZuo*;iJhMRja>y~gjt7HV3i5V~Y(D$^O^VMWiyiD z#fv|ovE1qw^KZC~g+_iSNWQ_#CsYDfH@)pxZ3~(;UqxJ1#5T*uDQ&NLr238^6`vW9 zJmb`h=+Ra3bcB}`DYwElKC5L0U1l}?Q6absx@BOe)blK}QSwELit)Elwy}myP5%JX zl#^2ah(VWqQb;(oCl*L`8Bm%BQ+S zf+aXmxfgw9CR3S&DrmMzp0tPx(tRNl!AlfVQ5u~!g$b{1P_7XHH8@rPS-;_(_Z}dqvF{E7<&>y`!AMm>)}S-?%Thd>5K& zd28vX%RTp8@Ef(g)Mk8T$W3AmV;1__1o~7pv~S+634PZcZP!xh-gc-Ygvb?mCTeFi z@{dp0zNo~cB>{EXUqAH5y!^ zp}1XFPwG@Wl%+-84yS5$iny*E0=9H~N_6vQ)Bgav%V!$$1x>5yACB;5`deK+-;gOe z*{hB7_=;#EtqoMxQlZ_2zzc8fl+1yiz0^G0)F~P$7BS_RJ@1XVGnViS&Vr)bo8l-o zywcfGR^5A)_ZFLN(%GqH>W1Fhm85P%iXIcvLdwh_?+?PFlS;@(@q9X-gvHDuW#wV2 zL%LmTc81%ix?JgPRPQXIr>g22VJaazhy~Ro6Cu!R2epoIqeWLm2_;^B8SD$V9$CHO zRQEhl{wu`u8bw^}{DG8H)LN}h+Z)fgf!;0`5HUo>lf<0@@ddLHo}gA32bf#q^j3mG@4OqFVWY(E^gw!=JDPX$9Yk%G{Sk} z@Y?Ee`b>WvT{Y_{W`t2&sUCK=k!xwU(tw4@#3o44rAnC&=00GQ7P)P|)dPiosP1rg zac=V;J@&(acK-m%)>_wj2GLK6*IV+-tvhrGY1%4#l|t4Xs9P*0Ev-P3OrfI*AvMzM)p-ikKEF`zPYNatT<0AxvLa%K;CZuYjwkxU`{3>lq znHp*p77)m@SvFD1X2rXg?jHoO(Bk-4Hsv_hHET+fZO5qZb5;ssY_B5;oz7$D_U+#@ zF_Awq=!Y;B^A@>f*BalcJc9W{{xUNOncbyL3-C-Z6m z6lNAQ`(U`YL1%xmt$lsbR5oy1T7!$|zP){sZ!2iLvNrrlfQ+*9zi+6u%5s+DFS;QP zs+GcpDN^5ZN|hxjQ^X#h3gd|ZF-bVCRmd3C#)9)^YM~b^Q_WRds&=?bPq5-$Ld1r$ zPzF%@h}kSfj@YMLOz3424Z@#$`NnCv0`tfH(b(v}Ajdh*fi7gMmejS{^7}g?n&QYn zl*{Qys@NZhOvXnD_d*N~{HIT61*^-nOyY`*O)kPqcdv@u>S2 zll>!mtirBbb;x~<@^*TjFumpVwO1YiR@1|fbdA@IdBDcTOw78WX*5+AH=4i&~AZ zHc*!vP$2G1x)Tc5hVGe_v+Fb0Mf9!5ytTu)C65xV!^n6qbN<~aZ?_Eh8OC^L72*yRwo^4r zOIea+qLTG;Rb>!(tTg+z9;2yJ^a>zOe-z-$Ti!;7x}={0=|xL4E)sJzY`$oDLyhme zVVd4Cd&N7gJ!p8LijWrCA8LD>eWepJ2$2d0L(u6Ka$W4i214(0kD zFlU=N*NW3YYv-O4nwe)fu~DJMs4vbo*+_lVr3q%aDIl2zbRuEM7K+|-!W<98KF?wH z`fv+-c(b&AJ*(hOHtr9RGMsZDy79%us%xeRh|^NPjH28w$fiy{l{S?wp;X?WY?C!j zB{Gan=6X-4=>eyTw~G|P2RWjb`LmMnUJC9O&&bz%*~lHYSgI_XNx)RjqLRl*YMO1= zE;mNfl_H;Fk`UaRDj=whPJ~5_lHwjfc~0cb`<~I~ z*weg?cY5iQKKhW|zTwmZIG&C01WcRg!B`cPN# zQEg`caegbrmp)9gUXhb>20L2a4bKZGLMiGEUX9f-wq~U?&_NP{nwK=bJr!C~Gpu2K4Yl&Lvmt~^>P}|iqCKli>I0-Z9 z5?n;rN*;J9EUX6B!A6x)#Z-U@iDaoaKtkf`Kt zDXZAsW&r)DFYzvd>h6>BOW{NBgz7Z$NcFm9%Q#M)S08FzagbO6imePqYyQo48&%WE{a$ z%Z9pP=3y;x@LDOvpHyyi;|gYNm09(#l`&>g8~mFg-1^IXHm0N{4+)R!DSqh2W;+KF zc6pgt-Qv}$#rNKF!Mv{mR`@&dRzZ&UyBp>PUU!%H0eB`v_yzp05~`&nsY%+RODdvt znfNIRiYU8pJC8NAo1MN6R+pJx+snp9>;;eUZ}8Pe^>**4Mg8xXws&2VVU3byMRl>d z_-n>R#M1$%p5q!wySuq#(aZQ#;-2sQfCd6`#6~rpG1m?W4EqNAhlFs^Xh{&i?>}TCOZr z+wQey%)^y$Q_Rd;Txa1AG`(V;tte(n=M@#k+BUb z4-GUVdi;Lstnj&gQFvDbuQk22_G5_Q@71Bnti2uf0}&TKb=NeakI9Z@KdIP9u#vl{V%rjIhQ% zKu|=aS4ci+>EK_NvJJ599Bs3?xq02<<5g`oXC*Jg%oyJWfIk!NAyF&OAkn>7Q**s! zDzCa$Ox0B_*D9)AC;ILzsYm@I8Yf*w%^Cm*HeqCc#NtWy>Y6TcoeNu$Z=qAGPJ!o$ z3HuTvwp-nIR<^h>H%T=-n|-Mp8>kE)>Y!}{%cLQ^aoJdYgusTQ$?mmE+~s47T8^Np zL_--Pj_I=5#L=>|91F=AM;m9@wsT~-3QN8(T}@v=@t)S+rdU*tqv2X+m5=I^63cGc zSX%8tS-auWe=~F;ZWk)^E(4ABugkgK7iE6Ol5);o%|9VoUwdP$T5e%iq_IMvL!T{N z=H)my5(ehZfJQpY9P@b4{aC@LwH-C&ZMLW%6Sq{4i1;SAx;w(MypI0vBZ63IS6#}y zo3WKr<96l(LrgHsvkI!H9|bchK^heq%1XA&WjX_qg0DLTRgJcD>i1~Txl#`-3ua@F z+TO%*RN|af$H7)^8o^bw=jf>p<<(v8g(7ZO*Q!NoSK%KBJxQ~}W2B9>kk!uA{s9ej zl55Ywu-Q}k{8fsl3(H2w93y+7uv7>V@zyP5wgoZ^;?q=<{?0M#rAp;WYLJ2OAPtf| zXYlI*i&lb)W?|%|S{@z4F12kpU5~e3(n@_cR#ubjnMnl1!X}N<$(E38wClEh$oSg# zjaO%UyN(=pf~{L0xT&P6rQPcpb8}Sm>hs>2DZpkr0uG}ep3eUO{5`!4g=w)^r-AiX zIz}}f-U;c<{m$h%?rTGCq_I-pxK@&#ng#Ja>836eo}DARnSBa*D|MQHof7Lx64GFS z9c$Xr!xeUZEhPh#ebl1CSmx%h#(#h9 z)mFy-q22Ft1@fYfyEVow9CuM^U50|pta5}@zif4Yo$GOJ>+tEjp`T4*qX86uEp_#+ zGy93urkm@b&8pdrmT6%nN+!bF(j6SE?dB+}F z$v|3`r5zF!di8=EW@$>*F}Dyk)uyU9!*Be*-gi9L<=9>~Y{P50BKK#pV0cBnvGTOc zC^bn@S5B#>VYGVHPDwHf-60_;x#~XJ+>m$`=UvS`wD(P6^!_xdQ}5K{y{s!a?!k;7 z2CUnJ_NheP3x{oFqMiDhHMX0psOXZC`zT-${{VG9h6U_~Hr6Ld{{Wfhr6+RXrpof{ zBVf>adT;7hhqWI0=qV^^sgnCNMMw;Wih|TY08Kj1QK7wLcO_NfVYtS+joE1)58ReD zkzCrOthlyXuRfhaN?1P&q$NX70txE?ONeV?KET$51UzZ=FB`;MV$7t4#GrF9Es zr%*(d5MDXLKZJZG!zT{U#v&}Zol(%qKAXM*FO>G0!0he+5_X?)_Z6(t664f=fQlw95lDF15R%C5V zGMYSWc>DZ(KYFj4C)q!$(N+_v_3$IZ*Te3o)(0!5!$FnO;8>7tykC!KZFs$bwm=M` zj>AILy*`)#NhG$SH1UsH#9H*NfL>jZx#y8boQrc!Euoqgm$gcl!{fGIF>!3sRZ}a- zRBJrA2GX|FcBkTz{;}&8VG8$ERU7q4apxpXq|OX3G(pL_kLw9527U%m#~K8z-6d5C zGPM&kBl@DgG2&QDu`yt zTq3k5UC5Gt-60Rkhi>VGVWk$EOMamVaEZwHp9qOXG1Yq74OK#1a7>fa!WFxxgQC0K zRm_g?);o2{R`Er&(KkzVSM?h&g{5cMlLWRv-PUh6j5iF^bfZIhnT z5u{i}l!y)1A^op4c0>numg<2&m;ryfox{i73HpqD3X}X{{6aR*ac1DOsR}AjSp^7| zE3H#x!!@Bl@%~{o?>5>=_{aoxnI;Tl)_)~#A_j}-Yw6wE_8ZG~VVCEPCgDzo%GskT zkTM_%&%@mwdEFhsaSQ0gZ)jj5y{qLrDs%OI<6b7gtm~%Z>IUuAtR>Xk7;Zr(XwgzL z*G~x0V&|4?YQJ;?x_0>vNcLYnJ-IMjy<8q5Qh!x>gEJH3W99d)es)UHTTW7>Rege? z9a0i{dqf>_+J#SPI1dXeGn-(dYUa{}jMfr8q!_~Gq74MEPQ_j&&!(3Uwa-6LZq;~- zrIo)!nr4>o&4N-CK1RxyG76Brz>SM;flaIY=%U8RA!FqJP_Hx5nV>WxtrgdeNEG(# zU$IR809vl+Q_20q_iM@2S0y_4D`#)vSRERN$3u&8vkhYP2|-YpZFRIysXcUhVmz-e zdKlu-$I`3P`%4shO%D|1Xyd(gAGB%e@zGenpr2}s!T8%T?>j5BDVygauCp28%BO&m zY_zVW7fRMd&aYG><-&-Zn^d$>p0CTSK8~H+oxPvO_EBd%&+aO{wyfc+y}!fpTd`Yk z+~U?1H5rL{>mH#Z@C+~yo z?cx6bPYxrf;M@x1Nq+9TKBYt5#BwDPQ1ck>yO^?3xMp{*E?rD+2F{+pNs43vB5cr?*nIOH72a)jC2NZ7JH2kR$*CKnS(11r!UU&1;c6 zf3?;cxfQtG!on!&2}LVkh>zY->#R=JQE(|F)N3tDeemioT^G`NDnr0RQF84Ll^Kqn zKFI0>LpEJ{%kY@$Lr5XJjTTB2sq^e%NIcu?jgWJJqYk1$DW&m zS}&nunm5KAIH;pBO03vz`TMfex5773qGzg88xGm#bLv;0cg4gp!@i|w_W|-1bKHZ- z6?+sdbwwq*UGqUzI({`KdICU=%&g~kD)5|z$3`x8ot2|!yB-%|N^Y^cttf9t4$$g1 z4G9|{v)VHeP_ov3XxgyyxD``-_NcO#sChni`JQdcq56zds$ z#yaBKI=pDTC$@Wn%Oy7~XW~Bfhh5=RF^%^xZ_CO(980;JR?Am^L2_s}HmMtY#) zho4tlh}O~7kMPu)1xsHuW~)-d&{CB=%Bkj)JnKs!8B~0AziI;|e)DaXd^FJyMrmDJME3m|AMGIwL zMBc=%s90Vta%7p$(Z1aqkn+*^vV+WQgSQHvZ!WR1>cTzi5Wc2=O#TGzhm0>-M+8&d znaiAQ*C|V2?uHl_^L` z(DWuzr^Cy6DEJD==B;e4wR*8^@xOV>P<<9U)*I>+OoXNEoN2~4C&6RgoyKtq7W;yC zYfzPbVAV}J8xk(e%kGee99~IG%=RzAQ_e?UGh)?FHGR6(vPnH%T%-)X$iwHGeQtFt ze}#TJ<+=qob~%~nZu3R0r|(r&cIQY0KyzG{1jNk(N}R44pZ>GpvhAKcP^QGFHK z&a5g&?I8n(ns?O|n0@}V>SXUdV1w}-#NIiTb(BL%ziAjn6&4qZ(_8M2 zG?iX&&IG%3uX;e0)IP9Ob?BMx9RQ3zPrVxF^x_(Ltqupv=Tob6?R*uuxz+LBB*XRD zJ6w~sXoV&i290(&Mt~p+*4P2?}^%i zgq3YdRAwhHvNTwzoW~BPrDF3{nWv|s#(dMh+@FN+lwj!YXZy3M>=ksz=1xTc{M}#hEC`6EXKhR=B2v zsygy{oIVkvu>G?4QE|K0*4x!zPiwrpw!W5@nb5YKVEjZ4Pe5dSknE-*rM$y+S%=EZ z$x9!gQm1hFMn7%MYqES^;&srPD9=hc$vKwZ*cGjc-8_AUVctW!VT>9`&mn!Bqgeqf-_d$8cauyX+ zdVbc`Yl%bZX~Xb^owDj8gv*#r8<$HLGGO^Qg)*VEPhtAj4I#h+apyEj>m?cUw zoKod2f2KfJxSPsM30pnOxdl5s4V`7Grv{%IvQ}4Ct)Pjzib{1$1b@}D8sGVEe|S@Pdb8vw}9S2 znD7U}DzaI2PRg}zaDBe}NeFh~AAGg>wRKyAavS^utmb7I7rKje7*c1?w}SCK>itqZ zD(jLQ^i)ia*%@_#!<4*MRM1wxf63}@Z!DEM>eN(v>P(=kcA|Q`%6Q(9L=c{vdVPWl zRqjxo`R$K7l^Gp>Tu;*oG{&i^Qwt_?N$I3QxFa7p&0RO7;^gF}k7<+#ak_g3subk! zgAKf}bt%;#`y!(Fd@A^ue>V0;-~QLPKfdvi#uL>Y#;32rmYS*iYvXh)vA5Hh+0AQ*qn(P7X7j7-dA1&a3U_Hc(09r+?X=< zow#rDuPl-yS5u2m;D2aa>0jHIZB=duTj8rtfCk!e6%HW-`v_B{53)X}g2VwwI;-)8 z-|RPaitMQ!_I7f+nZOq-YcD+e zy4DuM6~o%b>;X>7P8H$$I!5h{@zuQ+#$*_wOAAXV1Hmat8brNF!#M!}eTz9incc@E z=4VXu^SRZ0+*s1}d)}L919=lYOudo$mn#0K;}l8G+dnDfs|{h3ew&G!i4&1AHaX-< z6(@7Ip3sH1Tw%@B7EES}nRTfC8)*`rvy^&UM>9hCYU0ST<$1{c(_688ZtZ&f%3mVO zjwK|4GL(}pu#?e@B^!*!x>>_$zF;{YJiKyVd6j2(x`R=1$!i^`d1~-UJ5Zl;QUDR> zmy?%0eM^JKi+BT52~TcO)9s4aYE_e4R1%a_sd+~wVAUUCKu=vHAp&9D5w1~6%7;%F zf=$%W=#m17C*qv?M0G^;lY)ejozwVb_rYv62q*56Zx4`29S4j-Gm%h))(**HMpA&< z`XhQt?`f$h1t9z~6CN7HtP*Ie=Vpj}j?^kBlU+`Y^H0YqY44OJ^jys53OhOI>L|^W zEu|?SN=HFBq>Wqvmza4!C0kJ_%8GHFRo! zxV(ogH1(?>rIzQfiIk&NF0s{N<20I{wFlVhRdL7_P)yrGr#`aQQJRJP&s!NF3}Ty+ zSF|#Yf?9~u!Zguku45#wt}pt`q)v0{Vmq_U7h~H;6xXDZDpEj{j8`jZ5sTxKU=E;< z*$DlVmftdMVecutlG0MGibX?83i9~GSF$}-gy-R3Q+Q7$yI{fi(zWV>8FHPZRBl)H zW%^>~-MZN((j zBiSkb$TUSjs$FyvdFI;+fhMGTygytxM3mg2DS48>wT-nY0G)LLP((3>1oIUFcd2-y zTBjeXrl(WXB^0lOvfHKPpryi8 zq<>y0mWa8&3Z7*@MUL&VD`rJ=%sZl^$ZokFFG-T!EE!KUSJ&H@kf})m*;LJA&b1~b zGU8FOG8=pZJUFO%Ldy>MyC-L{%%6{JcYb@rdBZE^%zC!-mZJ{13!OeYaHgzblSxwl z0IcbA)l20~I-r8)#hri%xw4p`?#IHYUms$$JdMcwd&hOzovV%MxeE1evmEyL;+wPC zg?&m`HI<5Ng?cQ4R=E+N>X9`IhujF;Uj;!Ozft-x=%@7R`b%)7_}N|%BCpk7)pp?mX#t&TFRECDCIA5Qb<6qo(ofNZpJg# ztxtcQw)V^1CL@Sjxqp}w!uL)uT~$S%@oTD}rzl-jFQ6N$r+q38u#+XFqEqnzXD}kM z_w8e<^P%_i*VMnCkJKZves%4D-T7|sK27b@Bfq;vg!dtAEmYa-+^b)y zPTg;$^UhJe;Uxe(0u-^eD=H~WY_ORqW^eS(R!_Qndm)dUw&pU|EW&27#Yj2|Hx7hF zq=u_F8>lHJwbHRIc7+860z#6ckU>crf)1dLh9i<@;%oITWiHj~Mn?Dwae zaZTTdE)U~ei${^??LALTep_4UD{b`o0JtqpRiV^;s|nmhy)*GnDkgWuP$^$$?nE%q z_^+F6PNs&RKdENc^R+h0E2YMtAGo}qP3o(2Rj*UhRJwo$#lFH&>X9j=t7+*B_N^{9 zq#-hvl}UcDY^Q3H;h(tc7B=FFi!<}2hE!L)`+CHsQ-ea3SOBRZUl3N3hqd%mHPuQU znA!l5l>MKoCaQ9JaOlC>=AIvClc<+*;@Ygc7-N(uS#UJ-nB7J0nVX))lAWYdt-~-u zkkEr80zLW^Pz&ojE@w30Tc2^Z4%t4Cww~QH>>-EfJ9zFH*754+m&X@mKS^DGt*oqi zq^ZKoJD8+O6ri2EqV;-QjLQ=<98>{6=RgmbnDM{lZCMLnwNPfty7Dp}sj z)AGBfvoaKu)b*TdY-BvZoz+ya0}&pjb$P;?s}#=jTU{dnDwgR~0#!MBPPywR{&al- zgOr#8`18AM!*5(YYP{svTaC>&wyN80fk=YeJ0vCwiJ6(}tXs9zD=1k+Ya_z%_;Z&3 z0L~Lm<5w0NsibzAs-CF|ZQheK>s*NO2T>C>^-&T=>9XNbl+mY9VauUUemb$;G7O&A zZM9Q4^;6CyBp?$ne}L)Z6Wb4o0iBkyQEE-6gz)McZ9P{aWz<#%Y0wnnDry;Q%Sq^? z1l&)m!^abjscwT0;j5jd`CrF3nmoBR{{Tt+KVUv9DXx@3$EvB` zLRQ}5HYf!GCpk#P!=x%m+BliFyG!aTNSDIH zk{u#uxHv4$+-BZ-{$Ec|cZ#T07N)MZ7wpu38We>)Ls}G&+sP>lke^P!q6chsD#7vs zDm7Y5^ziO^{x!W(aAzXl>n!+{#<FWR+NCRVoL-9c& zx-5=X)K;TU-nRRe_&$q~aeP-T=e$b^;jTYv@lirAH+qYXX;tbC6t}5DP={A31P2zW zbt9xy+g_H;S^}uJw`m&cvv0YsAjf-{uw7{Tbi^;&Rz<*c9<@$OT};(87h54p*19S( z$HM#6#BOXT@7f5FCUK$@HkzvYc?ddsdU|fMON%)(GvWNk)0yE}6-GCFwkR4Ks|2K$ z$s5~PBjQ5G!nBd7_QRYOnkl)O2rKEEv`*}J$CdbB9^sxD#i%fj;N7pNt+8>YLgO2+fupoWl893Aj6QQA%H0l0e#p5LA?bAP9~2Hb`9vk++kt@hbts90SYsJ&;sf z8@bbE{C#rIsP7h=H4BEWzK!;6qK2TIzr2VowMGUQT3iZ1w;J9EcDVUqflHIte9stOZ_e62rf>h74qWr7r zMxgB{>=8HAgwLa144u~Iq*M@n<{R}YH)F+D@8>Gel95n#@R^6jaB8ADYF0Nm$9r}r;>R5YrCY_Sj0m%1mGvq8o zldTmlCgFx$DeE4Rik9sfTnMXTMCu-=Q;m`2s9)4vt*ZJEy90t za!mcuC)Aat7IBUUq7=1GXK6a_&_pkFRP1O08g0acf>qK5DnxyXge7y7_h~46Y8g|; z!sSI(Wfd(gE46i1j-i?=CL3}0>K}Dm8*QfA3R2dhWD)@+^otk(6+r602mxnumyLvK zSJJOwA53bRYAb&<_eX1L-rFcmRxQLeYC=+_rb=nsy5>`u{NET#VdvV1wu zTB{`f=BqKkgof%a^=ax7r3q8EbV2}>v;uqe{{Blbv|%d-_NJCIwNfF(viO!>g{Qi%ex{k-wE3CMZvuN=uaB99NGZB4H2aCA*5 zXLxNbCB+n>55BS+WhygLkHQrxMt3`TEHQiysVeseZpox2+YMB)%KrdREZ6cpn}F_^ z^#!wwELDoPSuD-cE{6L3Vzw`=}p}j59LVSzaR~&L(?!}qen_`C3Xmx>-;mJ>?#YMFhNT_|9 zRklHGZXqSPlb}6x5nXiJ0svQ*4kh51X~khKONM1Q_bYSzb}kgI#lM-Z^%jmj!(Bx! zV>SzIYoeN&^7TZ)bf;?DQ6vIndQLMxrcCG5uVJ?CTUJHL52vQ8NV)RYFLvGBPdiev z$B3!M?i@hR4e@0ST_w4{M&nySMJtg@saEEhA26k)d`p^qgg#s}lF(N`K~9&rEMjLh zJ|ph&AAjBB!rgWq#x-53_WjFU+~m#9zYw=C3GK-uYA$6LWR_~H8cF;~r9yx!>oDh( zGbr&S%(Z5<)!xq!P1l<_fzEZ*gH4<7^>~^MwAX=H=aX>NuCl78y;sK{y41S2ow-*P zDe8xvaZWh%t1~KE)S{&y5@S^SXNG}V*mnSlfFF8iSK(ZZ$LOcOTZ5mM)XY-c>6TYa zvIr6?MuA%NGBC+)jhKfz&{^)bOQtZlZLC~YI1=T4)q@ej9IEo9Wih``$ zX=gKmX-a=vs|_YwQQJpyHmRwe<2aVa_1dWeV%2@b>K?JzxT%lt2ziivei0u#cPs`A z^5MX*t-A*4ve*Xty+3NoO#6^B+eUWod5K>tu$TF2(PF*co3}pg8x@rGcD9+5%(Shp zAw`vvm4YTtk!NvJM)7lk2 z92v-$J74o&8)Z)<<82hJ>e~a&Txw|D18{Bn6qMFB=<*Dabxy2UD~l$FfwaEbX)<|Q2}c&p3^ zB3kfI&P3%7d+#4L%zZkMkz!e0a_en)Mart(Z^toe1Sq24OGvuX%T#Sr(;+RTwv-a0 zlCqU8MMZ5fATS~CGVT%`cTora+50xymszdO@M_A6N|u?sT!u$dZPcOJ&t*e&g$vvL zkbUxl9gASy1T&AV6H}7vGR{2V%r^nHWg@c#$8!vb+E}AgE>bSxRYc2`4yL6DOOFL1 zasg&QbO6Ofu(mTmui>53usEqlhwN7DYghwn5+(5s)c~D%{pOVP_4|tv9#+t6srv_weV_5Fk#k-G zTAKGal(|c z&<-pmS{h5a&xXDppJw}2TUtUjq0oLG@m8y{?#OtHj=rw+U&%R;%s79yt~t7MU0pKN z)xpa(%8t(K3}e-Gh*Fz%+B=OxLLQXdN)lwFHIn;$yxujVT5J2idQ6jt(>R?gqP=(( z&a)xEJ8-X3op=SkTeo47{)vh*`Neu%d`eJ)+ zbwk{mDpSap61P+V_yp(nMS~iFd7`b?S5-B*KY3cj^+VA&UO-Vy&zwagv_vvJQd{kp z5K^AH{+%EjkChV#%%ct{bkTFoMJX~B*6Ng=qGbw7qO%I_TiSSqb(R&e2f(XEQg&24 zC9dNyWNs(jVcD_(D3C$x;9zwi2(3n`_dt^(V&IXg1k$1JfSzVeNGkYAw07 zwbkhsulkL2o#-UKU8v!9RqubJtTFGN=YgCTpRvZ{vaCy zhVKC(=MlcBMH1HBkx@{FS~F=H5H!?l5{;9&Q?k;?SV2&c9U>={&TR^o6Hdj$3Crv? zZHkIZ?Y63wNF$=0Xzf?L7_G=Ox;hQi_WuA2s&)%6Eq$T)sG4-$Ec_{wSK8lB`a0Tl z(!7V7ASfvtA0&bZBS@gO?Hsqx8fdNN?|#}Rk<&0gh@-Us0P?S>pP*mS7ma)2;tqA? z-uV%nrLL_?Sy3SBqGQnD%SqA>JVjTLy4jLQS_Z{1INzFkSGZ;t`wP~* zhSODWKAo<+)TD8O@%$lkJEUdcClj~4x?@nPue$~JCXF;DeJyZ3r@ZbY;0jJi!ML8* z?;hSP>bm3Cd$wIsnt@4Jq5qKZ$y-ksTp;Q<=21MNcuUXl_igpIXE>B03TYT z+`-zHetqI&l$mR|)(L9| zC=?K+87V#>0w#T*cE=-mnlwdTc5JP8D~PYBs3z6#xvX~KLiIb86+V{P1$+WT`-y^X zJFHV=3>}W@?xLG!XWGSOpD(E9+)tJ+S9*}y!7H#uVTrf2=Dx7F{{S;Q+Rp6)Js@N! zJ!KiIjM$TKwRry5a5}s;o;BCp3P0L+aeQ0ZmvOjIp4TAc>Vtav?H4bKV`r$T~3} zi2dH22Nmi4huX$Rb>$xj`~5ihEQjOVS)Rf%+RM%At*nE;PEMgKbSX^S8{(Ty#%{P? zbS6YTID2M@N2 z(sn`GC0>7cW0^Thh4*pvKB~JF9deepuQkhAaaDB%(xt5%i6E=-t6q`5v$6#9IbkH5 z!ESvJr=h22nT@P8(sre>pa5tz5o~3q$pAwdlC=OF_Xv&8s*k3W6sK0&8E$3OPfsB& zVbN}%O%f`QHwkEiD3~Z5DY_6ag#)$8k>R8*N{^T?LN*cnQ3+Mt;d&O2>jC!1a;Zhb zLg=VUX>d!##4ZFU3x<+}LfI$#&_M8rsHbhiN*zjXXi^ow%)(JSt`a>}UePH`j+*p@ zs*t$`i;AR@bskVT3Cr@Uf{&2$5y@96uN?~X5Qewalq2L)C+anSOfyTew}3H!u(_3WfdTD=n- zIoJvxllVyTyx6MFDzIEx+k5oc4Q&gI)41wXg%;-Xz@-5y>U^d=V&@Zn_B;S9s?B#6 z9yu7|<4qUMH*|_ODjqw^X>um=I;s)dp8o(#P>0#OSXxjH z(p0>XCZv!^nvBHcB?L>5bTR_%smH;EuKxYFlohQ$9mW+nV)357t8dt{zPBySJ`5<;zDaWfZq8 zj@HP&(y@|V(5ic_GCp3dwFJK{$#n{xlW#Qs`P6gE!EN5musq%3;+k@mPahmmS?+**l4 z2+vfylq4kjbhU*%mOzbiK{4`_!LMaN<6Hu zXlYz4U8H#QKWs?DMDeoRKc%h#L0aIw*HWd|UV3v-{nVNG$GP?en?zj2eCOtE`7;)S zvibtc)r{k~?Okis$z`@0+~(AUE8$Oo^N&#^2Gj-OwR4|Eh}_-Y9UC`0jk6fVYu!yU zme_ezPVG?|Oien-#&02>d}e{aa`X&mZL#1NID6J$@{UiMa)nyMIF)OTICN1`RS#-6oodnVzmn;FO%zGRrKUAaqA6o(yQY4}N>QyNas!buUU zn?;xJocuF3jdfPQ@=e~Fj|i&|tTr2IK{M;BdPkv85p!r4m03QsFPrXtSI^~@%V9pe zoKccKE~=k^j~?zkEy;C5Nek#++`D5Vp)^na)hem&nZ+AbO7xs{X(>XbB6I)|3xI{u zx`Ys``rg(7=L#C!q3PlYqh&g}qOS$qE98EQpAdC(8v z_Aj*xuH`yJNOLrfz~$B(Uf$W(z{oMeP3erSi2fYg>I#zLHxDU zdq~jzgnsX%EDMos`9BG9m3~OKE;+Tr$8U2qtbTjHGIp5@s50sJ$&|G6jbxD>2-vS9 zje(@Ja3xgk?D>Zd;|vCb(VnqsM#`!3afxO4Mv8Ln=e7fWygQL(i*agY>9uTbVYCFOp0Q7m z%pM8W-LcK8)I4*xdMbygvO7AOr>>xq(Dv{Y->2^&#%@f%F`~DNwc zw=r;Y>n`LOO^WSl$L@Vl9Ic{qnI%Mc1uAolt^J5~+a;|9byv2zZ>P2`BBU>#&!q3u z+qkaMaIWSt4p_n4w_S@g&04P%wG35L+-d0PoNv}-v_gV^vZRBrNcK!VX4X57V(N1* zI`bna?5Q(G4J%q697ta(-NNuUF;;UIBV-i3dv|u(alBVP)99h8Ax){}d|!jdta;~k z_O0Z%&XIzxC3^Q^_s%q9V~x)N;8Rtr{{T;WC4McyHP)MjBev^{mrAFfq;(2u)Tuxu zZc4x-K?TKb=%EdE>f7rXaUpw6LbcS^r!IGTYfC_>+P6omv`^vOCj-cfi81@S&RVwF zE%5mCNsBp?bSd!NE4FBhuamCol#N5NUCV^ zePcCvjl!v?2E`-dO!bPJ_k0T99>dJ5?i%N%VZ1%nBAsM?up+O+-2BU}epSXjQWH|w#i~x9 zd_LXOdRwLf)iUKN-C?1AeFUQA!k`sF385*QNaU&9P#T_sIy0gnJh)esYxqlo+%; z9$&QR2e}vj0BgOzwrA?oC=RIGyS4% z?g96yF(3F>aq+MI98VL`^=}8hose8mp{NY(X39H&8pSr6~lRe0`=f^($;b0W04vW)F;VLMpmh z(pri2oOMzL0F-Iek_7r6*$NY6L@oh9`#gT=jzFCl(IX&|K>MI_jAqHfMo3Q4qI{s1 zD)~1}`@6?^)2~Aj%!`0%n!c=@kusl;wg95BPRl#xQb{wI>E|dW-34+}(%n{+tkKmU z@YNa_^b_%m7@bi?P)P?w*1y@mEv`z?nbW*xg%9a~?qLAvtnDuOf>@%qHCIQuXpubXAWm^xi~y2KyU7zk6pGDJQ^`!`k>X5THbqCsmEKvyrXleu%~m%+|ytr?j~|fqj8Ix zv4!J3$BJ)RRGf#E@2yt68mm|{(^D3GLWPG96)IC`a|T2S2C-`UOfOia7-&(deqG5o zf-X)V4uC0A{RRqbh&SS53d->Ab8+Mwc3XwtD@{{3+g(~I(6XcaL^r)M_URA0V3ys@ z^(JFOZEo1%;2dFy7%{gmEiHIxg ztWH4mTITT!-{$+$T5_4Y&3iPcq68;7UV)AGAelvGJ>hWuH`qQo0L@2)Wwn zt_gK}-4zWs{^bcB$JGj%5*nE1+cCQ*oP>btk6T@}+L{!$$~t><4y{XFH9Ji(+MHRL zQACRtFf9~L5{{YFo+1&epKIEemefi%f#RVNWg;$)gNLW5wMk_+E4AXj?(qI6;rvS_zj99;VYbT^ z`!i*+l3nc0CYh_ES~EkAGe}a)jt+s%n~2h6Ml_99fIDS{earS0-1d2ul&dp5 z9owyaeRZ!RwO896Vv??))TySIC86XuDJ4JyAT)q;b}XDwFRMJK^LrPJU=LtxhZPd> zcW)U#Y5lSH+m!R3BHuf3?mtID_7&STdg))cWtj!h0k#@P&NvXuk3UewJ7HfA9({6r%!Uvuc{Z()9LfU4`3O6cPQMxpA2$!0)9fi;cl+Z zxbBxA6yf@cI`x|$@kHQXFSL`;oDZ*A^gPm1|s`WA=jvcgP@>7~n66pmecdF8Gw z?_(Nscjb3ZG0v>@7Z>ERpW<4tZ&A9!jK2q}xg94LJzAO_J-(m0Rk@^k)kMm>edq2U ztax%q1?60SRD=-gmn$t>Z8kce@hqjrKG5L@!Xk7gx*}TO(4!o3u?=JtwJ51Wi*3MU zl_Z(YT>*jK!WG4Z%R${qIq(X-E~yC{YSM*?c+DblJQ|^Lk7+5jYqs3(ZyH-&3zhW> z8;dO@t$Rk3kk(OQMV<{LtEtAHPOkR(Uk6tw;r+Z{t2og+!t>X@N|M|BFUKX-KH7*+ zi3yjs1~PzsYEDO0OO&5V&gE@Zrt%(5$~gZ3g>m<44Kp?Qc5iL8I^*C1P}?Og6)H*x zsD`>`siSF6arBwJQdHBm$-UF@Mg?NLdM(peT{0SmS-3M+*;-dg>QkUpw4|9$21a0b z!;GNprEGLlpK%?s_qoZrP6dVGJ={=tpKPzQ)KTKM%O-Z>sO|QKjS| z{soDd2|;$Ulq1`?T1VZ@6RYKV+WHid&Q(WmuA&1_zgm?kJAD5D1bqS8b8}k&zEJZT z*QI)0z^XNr(cNsfBF)W*X&;c>!>J(1^dK155DN3gx~LjT$3RNT%B7%BguT(tZ4@#? zRWWDz4jF&5@xa6|rm+g^WvEf+Z!-I@rt5KO$^7By^%;z1111Z@lBD?^!T(CaSLL z-L27S=hH6AjH36<{Ce0bs?9OPRMD~ys&d--S&;LSksd_!na$v;S!d2@6Yny{?pUqX zve|NEu~5R4mm5$aK!Ytoi6M|ZpsdBiT*(W36n^zdZbDNBsPc(aJl-f8JJns9zJ}#* zs&b0sMB*N|*0!RD7&zJW9MZPt>-OxAruOrCaucpl zXN@sElo@S19;@MKPvJ#tf5%6+cZrHyG05&hwz9ZLz~RMIF5S73vxNBq-2r@qtTpwm90fC20O5bLip_PJ$xLV+8SX?06pnrpDhGA6>_Xh&zu@ z3UN$+?l=iN6Q0m9k6atcILDAJblFb{W-YzzqVb7c6s6LsOVoVDH}@4OnIUPJmf;k3 z851)|KQnYb)uoo)Mr*h_k7w_v;CQO2%ZeZ>TS@95vQbQVqCAJw)SZ89AyTp6Czl=a zW>v&_-Z9CTEmz8A7@Otl6gX#3R>IJxYG~?H(M(jdwKU)$=w>wn5Hyw75VmGhim+Vu z$2etz)sOP`6{s(Vd0n;K_|5e=mA_`VrbgwDR!Uo6qIfUU21QLuQ;{%HG#<4}WJ6uj zJ8<;$@Zh7`jxD|9+#tt3+vt+UFH3WJ(UrKmnr4+pUfP=o^x9heRV8J_?I{`_og!F` zeX64_!S;Fuv)zI7_YU)ZEZ#5Nok*?a4ku*P+3uCdHNVVOI(7)2*OjeBDIXFO_{5K0 z09TO~nzStaO=x%Y^#1^gx_M3|%pC8DSu!37;@XTS3qz`ZjcpY*Z)0ewb*=mBYg0~w zw$QmrC`t;1{iKewy^Webbx(C~ESj`YV^?~euURq5`=)co)wH*|3p)KSVYXY`ex{z! zd~wg>xlZGaq^_NAD22?;wPWgJ4T2_T;y{&@Z<&h7B^kHgTp(M5FLS7`nR z$J5inIG@0IM}s&+{hH+OU9cRg(VN{|t)pp6VXKWAt<5%vlHxuWSycWNB#=jumn20KzfJ?rj$;bq8tce|jTQ@&pilNLHq2 zP)CnQ;G9?sN}G8hzS{7>u z{X2&+`g(4mG}NUD0U(vnqyl6sR_M^h=N#Md1p7%4y;Pkrgboy$l>(INRC-D|vDFPU z>|m)X+V=O_M>qEocBQ7mF*hje&ni#m7ZkZoK+*)Jr0!1lWDLME~b_eixPD+-GjX zuRYb__R5ZH<$6Uc^IEEVU_+F42Axm}`DTS)~AmT+pn^o>JP%#TNW>yH+RhK=J{c5inOZM5)9uh6fi&~AZ=WgKV*8Q zQrx+eZi^3!os2&aU1yc|Y_}j4ZxvV9-K45Ic9Kle6CjizbOK!lkR7YJ8?NZfmY2Uy zDzIjpr)|S)m29;zi>=O;YE90mN=G;z0nD(ktC~KvvPQRBq3lWSlg`rFlgIj~s!N z*5eq-s0u?C_YxA9%RrT~S0YfE35`gPONQtjL8x9|b&Gu{;X;r;wcmOhqOh5Zh5v@>1}Oqu$v{{UNY{gDxsS;YYj zQf0Tct5-ecSk3L5n>dBz41S*Hb-6mjb#(U%J9EjX^s1@cDGN-*f~F-uEh8!1IpJ)< zrAahfF5SXD;1?Yl(N*)~cd}*O;di4)X2UJ@EK^*s&sn(ZAE)@5{V^mGkA{` ztUc{^sr1~&KAu&5{$Nx$J}}6$Cg~mGyd8_GmqSs0y48jmwJMQGaBOZ)73?EoQdD$I zd4xHPi!vWd@CY1#di9%L+r`OlE-kB4D@vMp?OJ`y7di9P=WnDx9%pEARnKlQ6_hvKktH$$8GM;oSANxBK0mk%qDUI=W)@8H=2dFLbpOZd>iP%1VaaRZ9w| zZAx(nDooouf@UB&p6xw8#CsaRDs}zJ;qs3ea-K=XoxS0lr`T3`+*b}`*9#{WcA?K% zws%{H)mt6DRp8YM}Gg?};mB?L47kXaO=~%&C@bR$No^JfBAL2Ee|N>V z?SLP;At2QuQ#0yLkSxl0wMZ@rIIk}_2%=*NQq=mIs6vy}ND&;QQso^&o}=2MAPkay zoncVnF)rzolb4Uh2#E;=w>G*`2S`ey6UNx~fgtF5p9tj~iO9(yf*}&KWaPh&8e9IB3F3}3tIU!4{I03(@MYH5c;ih8_KjR_Jy8<~}eOuTedq6>X(8ht@nTFfK6b$nJ|W4srgw|BPOg%gz(^lzb5Y%roriVDysTFlN- zUG$~(3u7K)FyO7ln$_PGy{B>O)t4ox!Yb%$?CG*sq2o_UM_7+cs+_u{Y*J?=kaX}7 zM>Y+F?5>~O!keJ@y8i&(FKV2D$rN~wDP3KM@;()K#V#2AeG`>7))RFtxT^L7012o> zmH{S83NxOPitD%esX&Fd?--j=RbD8t9Oowabi0bwQ=)|)g)JLYi);qlk^u=yKph8! zX{_vA)@l`8xfma)Av`Ud5s}xn6;p4irW%DI%6AY%q)*}zZw4!vDtnB}B0JSeZ*aan zZNdenvaaIY*~+h7*Got&DX@Z_fgvEw6FJT#b_Y!q4(?45++IE5$w9JDsKe{Hj?Sj_>R3*oB z2QZ2&!Q0O#-8f5?uuRJhxVcW|4RfNnGg;!MnsaODAoL4qOF{M$BTbXA5?k_~3muET ze0N;n?pCdHb!{CVEN9J7NxBNVolRxABT?vAE9V=Oyf(Te71laOMQxKm)FnrBRqwj; zM)GIUETamGtmMP)m$__9v@Qd`b0$$q`Q`EV4X2B6u%ef*`87jDRP?FZ3cV)(0K9!k z1cV6yNdV;gw>;Z8ejQwN1H||(uV!}qm-iyNFltYLUpcJiH}2mu+>@VqW!ip9`ib)@ zh04)IT+%A2xXzNTAqlRkZA#hskzfYtS=Z&z2eh09HymPz3{d=5AhcqT!6)YTc|_>r}N?WS0dw>bmoVT9y>D zN|t{am>Oz*ClhYx4rebmy_`7E3J*_DU+vyIc%!z_)6r1Vw?{`zl%kfApb*<=LPA1P zl;{8gF3F~W0n;TYCallj;}Owy5rkBYJpAD(x{29rUfXrj;JFnr+FD^=N_5J9bV~!O z@@D^8`aTYu>zCyKCS{k%iO(XT*vo)7CX=+vI(-5_6X=+r*NlfS= za;}k$1&MLiu}v>5j^z|O`v>n>^n366gjF5C>}hRjBzjoI-6|v7J<*>R_XPg{5UgE} zw*%(;kK(vTc%M=FmHz;T;i}|?AA}5I=qLWL5QgS|6$j|{zs`NdDxmX5&4mpxp(T{ zPy>wdeZ+7462=iZ{{W=uhM8_WQ_Z!Fc>DhVf_`FLr}c#w%LA9LDBp~H^$Kz~YH`X2 zJu7LdY*e^i`QfysqN6>e`yssdM2a3hAMi(ud|eG{4Gy*AO*G~4^{t7hx+UZX^pc6% zlh;_#N);FZ6jjuj4f0RwffJBM=meyx&)Xci0=t}*J;ZC*!Z{)~&DB9V{9C2*6XhIs zP7Y93a)#^jJEY9gzeoj7z@i~z(=HkaRG*Oh{_bCiFBRNRVS2}Jv|*cREO#|pOO?)1 zgoheai*@B8OWLV!+pMEsY1$TYfgu&D#82KAu{g%lKs`KFFy&6@IrEuVsH4yMKIeJ0 zzDZS$4vIP%C`f|MAZ;oV@KobL77o@Qk*bp8HFrLbaUmzsAvJxuWK=0|Dcmz7p}14C z{YrbCD(KxQ_HROG7ffJvne`QlmPZWBYx&qmyHKrnZXZ<6b?tk%OP2s~&5F}f%lAt9 z`&MIIs<$d=8o7|;%%YvOp|yV#a+0EN4(iuxzZoSWe>mmi1$ zU$g!T9lJ`cb*tuM$G1e5s?--!)hfE8rjdS&sn?;`^hb>LRK_`78Q!bkd)0x^wn4QQ z*Ei5#oen*ga!+no>fo9yqFc!DYE`Wu`hkBfacK!Y0&Tcu;-X{NBw;aE!_(8)yglR@ z2=Vmv_OGID<2c^emE!epGSG8-Mrj}rCW-k)UN-s7l8L!YS2T5B9sT=u`(1_P<2uDq z?FB^(!PPv-C{kQWKM*5W+D!*hp}}RXCgrSWign}5$Gg?-qVvd zR@x@p$eyx2L|H!Bm)52yZUUsXo=jcnk-sS`<=>NVTfZJ=SKLbRPO5tAB}1%KB|wSn zSxQLu5GNln=JBt#xGX#h`uhWtZPmM8IE2!pqFrT8Eh`AzR0P zU0ev`B=BELeTCppVc30{is}PrGl_7M_-g z&zi&*`*cWBkQ!TIO4lSS8Xo%SXb=i`*fmQbp*^knvu`J8C-YDh{;qSDZ z{g864irIbi+`PX@i_=rxXq&4|Ej1+*?FWNldsO4)N>K+ykA!^vpYszfEbi!kO`!e0 z_4IdQ`3xns59zv6cz{UvY7%dCKSZykr~4`7K1AT0V;tbD^9;1yZq(NrTJ_Z9^&cZn zy0tT3L03?={Wa{{>NmA%o`m!^9hK(BV%qJZan_B*0uHC_JQb&X`6&0n?CFa>%G>ul%l8~YCx3xg)?2oVKb{_69{!r3* zFM;w{jLtUg@iFD?X{AP>`xMUJPuuQhrZL+^q3ZKdFG_-)@}wqel!?@V6cXa>i1>9w zTxYUO9}=3md+O^?*p3^FaE}adeWMyQErzV{JSjfR`=W{NtIk#S9csaesI5+VRd!P5{rUq z>KkIS^1)Eb+L=R*r*cbb4FU&-_ijn|Qyzw*pb#}8yC2>6@GI>O$FO~st01^*TgW~V zCD$DyPf@m*r(JDj^7~5A zN&`%{xePp_q^CqHSjXdR9BKneHRDmQw9~?xRcrE>@YCWWYH6waD1U1EO6-4^ZMlzR zoE>m>{Y{@&7=!?V`ErdWS#1ZBH<=m%1!`Lqi_-6|XccKBe;R za;cG^x^MK&iF8j9H)b^{F2gzBJ+>j>1!TBjL=$K(4c$Hh!(78l+|X^CU>_5@vBsk76o)J389mbMDVRRo_C6%c3Vi{&c%ZrHV#=4!rt zGxaIMtEuSmy!RYMrht@u#m<>qwVtXiC=Gtt*kiluR_OJT%6V@Kg=5gI%-}9%;VkbP zVa_|tD63q3DO0Q^X%z`lhwr42sr`|lZgv^=I;?HSZBK2X8U?+f6 z0utW@>&Mcx0Z?c2g3=l=RnE~=4$OY5JQLiuQOOltduY47+^^LC0I<5sn#*O9(~#LO$f$hW z>E5tB-8o;XK`3=I5}frBs>fyEMle#`6rACp6grCHDe zF0qwsbz9Y!{I8JIX8EmsQruIl`ic$Alb~0&4h;zr>~@G#Ff^9I_ridf=b*1wAC+-( zB<$)3nGrN%s3(=>O|WEkb<{FS+ykiBpobcS(Aua%p|`Eoi(%kE3LvD90q=acU`YV4%;e{jrw0aR!R^+|@N~Yy)0qintAauD%Ool0Hi0YXiv5>G&*(lbxpY1^`%IKi)0xulfL%UwJr7nr63sj@Dk zQ+qCbLi1RFKLXSM9y-7mO{9fQXllfpYj#yAAna7c{`g2fWi-vJRQr5BrYA_bO)8h` zi{HqzLst-(v^JG_lCjA> zmGD+T`jb&~vC&mymvx2BMeKgH%X(D4pAy?j54t|Q^6xO`*oN0-_%;6Kb}KA<%2_#R z*?uAR>-*KoGzd@NXaYas);$HcMSOz$77hrqsgg(cVt#P$wRcq0Td}+lDro63Idl;y zdI9mVVecQ|6u9;^>$uoKZL&s^)?V2B($RliOEbHesQJvCH`TD=m>&5=kv63Zbow@` zFs#IAESN*tC2L7Qr=%RJMXRd6%ABUph;!L9(osZu;)2*=?NmqsT^O@~=v? zijAsEalxP})6dQYd#Hx)V(duM6#|n_6lV#P=6eoP- zM~B%RCdusCHiEXeW#Kuh21K)D;kV6Aq|aXy1)wC|G_OtuohY2l$$>6TsLOwqHs%+s z42@=iI4u<=4z>u8Bu-H^yMi8Y2vP{9FEQ%3uH8hU%r=2lG}NVfWg_=)a@|s8RKO;k zXRe+RJp-HT3O-&}X|f??b#3~zua8Y_6$yK^j)3Vz$;DJPoG2pXkP#^pIM3vt{Y9k3 ze{C0X3Pcp1tpcs$M|Lr7*XP>r1LU=c-T2=w%_Ud^-JK+`>&U-1>@|pn~AgTBDc7VlM0|qf8!~r>lJNb z+ZAd>&&Us}7Giu!Ri7cE$`es;cccR14KJOfPq6(!e976eh z`uBZHF#dh2yY|V)SrrCt=uvTJv%o#34FTMPTOu%k|i|EO()t6;DB7!SW8NL<9*Wew;s3{!})h1 zw^KAz=};=^RaZ!mo`6%eW+JNUE;21w7ElVnLqVA&}+;u+J?Uh$X?A%Z; z4_ZaRg8Ic9)eB(-#=!wDDX>A-M^O$A0P3T0=S$ZPPqX)Hb-=kppZgcHawY?8rG5NI z0H(~Us!?`wu^}s{s17(BAwX?da7>LQEg-IlaQ30wp|66{x-NC(9`7^U7YO1XrY)lH ze&al0c(sP%lH6&NV#e(Cbm?TcmADe1q$Q+nQUO^YXavkdBtUMDWVH$=$|(7_woWzw z0NZBbpQru_%NI-{<56?J=C){{V59PEzMy7_)F^A!VY8>ij;$n{73|pfK8Lky46lWnE)!m4s{Y%#o;rqhM*O zsxsBoS~J{{RnLj|2a=M~m~!c5MGZw#K}rLZ$r?}00DNQFI~wA#(R}{^=DnEe2Q2f< zZ)&_6XCALzQ|*n5al%TrAbdKQ^j5m7#+u3`8cVBbZ7ES8fg*eiz0mrk5YxS{1`5s^%#B&aH636brI8rq@N`|PLb$H*P$ zoQm;p%c<~uKNwmYxn3NmRa?)z;x`7_LRNqzHBmG7MLt8hW3$kTl$Mw&@1A)L09vWr zuhY9d?EAP5KhCWAdNYh=vi-j-r(G$gd*o``C{ocS5M-d05$u6)dx|^DVjH1Ou)R&T&BDXT6sc8+$Pi;w``PxJ9H%_G-synL2F#cw&hq<=2 zFK<$ULWFDr^Qlu}p=0BkcBwr~{@7I*(MxWv2DIcdh5&aKUDHCX-0 zw1(*N3vS?cRMIFUx{>|kV`-d#GrLt)duY&0{r;YwDoMn#6WjN3mX15+4hpvSNodXo z<@|GOdg|r2!FqD=rrNjOshwpG1)&KuQ6u;&Cs}|@PSj|rq~_+|S`oMKKW~6O2~(8t z$GdjtQ%}d)?YgG zI9$VDx6{*QynHF#)?3}@7i1S|T%MxQ$@pswzsY%<6x%MRdZ{-Vj-><5JuhRp98;{Wi8MPZ35s27tQRU(F!^cMv{J;1RF!-5gW-6P zD|^F1R;}WDkn_x^wSL$f&9cg@(famsD!MnTTuntqB^y^WLrlPQq&TD{2$FY`Apl}5 zqGazZ6jb@E$G#?qny5)CmBp961qBoFBWc!WsEty~85G-dSmJyV@yz)yZs#oBUfSN^-|SS z;P_S5OgBe&yse_uA`x2J{WJpVlkm7wWe~}7T-puQm9J`aZkr^Uvx+g=KNaP@zwu6G z%Y3C$nqKC4KBA?Zjk?#G6_jnQ@?2%L?lUTOZmnrDB0wFf5npd=Sn%RDR-NPAaa}W` z5ugfEukC9Ve0HxI$VYO>V6QlUcq09 z;25>#t_ioeAm7+j2Q|JbgVr#hTs2eM$JQkFvYnX984H_a)B{Q$F1fJzUDugkjCT6h2=a zpd^RgNFr}0TZoe0RJK93UAu8|L4Uy?hZF7e_oL>?v5oiubH4B6>>lw&m>gkeW)EGA(Rk=eGCM{gfWAxCia zDp>8LxE#l0_mA8?pJw$Npq$|y#&$Kvyt8m&oMO|)xh3AoL1Sg*Ryj#zO3_O|+s++a zd@|cxlm?{u^^BGY;@>dlpU%f7&Zc||@JrtH_;YbdwVESn=Y4D z;(}6!!X{LrNB|80i*6!9visZT* zg&SK+ny5&dVJTW1n2_Bmu)GaV1aX1Y8fsSkW7mXzJo|mIcT34wBP;JKlITI>jy9}q zzn0^>Wyz(sZ#PyONc5{7K`BFPASQm8B5oMKPXw8Jq8fjq)2Z?G4^P2pq`PJ!*cSk0 zcirvu^?KDs!FgJ`&6Qxi+u*#xQ(Kcs&j3_Y$d(HdYx1v!g+%u=O%zEF&TW~HVTri$8?!R4;OH{c(0A}c3tG`&FW*y z*D7@=xlL_oDZ>DxKwQ5<+LBJ+j!94&kPQ092NP>8_355rNZIoG{cBy_Sq52Hw$j|z zNA8 zqsnI_%jDZ;&+Rnp>FQfNS7|c^gmvWNvpi@&ciZ@QFOn`_tHLc6_e`@WtGo=ZsCCd7N`H~XpStxII-Q{LsDFZ@g zWF-Sdl_kil-rFpWw5qy&{`V(x%J2@@do8k6u~&)qqmI+Lm0S|wQx*B`=9JW2UYeu| zXxgY#2t8(}0g4ATthjux)z4ad1*DC)0lGK9cnVYYRakz?@#{Az{WpHAJOgO1V<)ZS zJ7xjNR5vPMlTTMyY^$ghDr{>ljnM?1V-4Pfd=Bxb{p;C#FP)_LR=%F)gnV~&sK>6q zo?5vE<;FLTJgTg!#j_hhS}u$`a;IG~{#oXfg)_ak7KNxE!lFE5GmVhBv|fjkabYBJ zNi{aRoxZw()^Alz)K!H$Zn(S@o#kh8lvcFJtnQk%Cs?znRBgBvUSxSyc1crjZ!qN> zesf!AY+LCq-#tv_YZTOqD^)9LZ)%Vm+;rQd^@o<4DGV}(^AJGZwDH^Cm$F)XQLY)b zWVnDfD$JWLuA;bMPSh^4p{h%DsfQV>Q|^FBb;pz>GzUQgkylYn*+6cpMD357@cuS? zCBnJKiqA4|wnqCEb@n=2imf>&D|%rCv3SR8(6kmSeI;yLYEn?J5@wPP{88~@s3M`_epTD-)gfO<#U%E^1d`!wpZYFC|MM&-YBV5!ks2Xf5j?O zglYulBdy*}h{1H$v=RoRO?(cd`VSDHPC(*|MP0v~Fnmh3-+!RG(&ahd6#iFLNm9aT zELSt~)T>%mAWb8H1kPj)AZcvcbt-n<9R+{jsGn<`C&*pQb4_L~#98Go3(dHtnx`n? zrYdSFD4MeWbPY)l#URy>f@cY)1akeLt@t4zMu{;Adw^CZUE1Mb3Ilq#q>1mfm z{v(ny7b~r(afOY^TU2syY5XBF@Q-Bje!%>4mFNCoUV%u{I^={|EJ>#%G`+fzJX9uU zDEC)CMdbuhRBYbn+f?;zwy+B4GvgAlsLqO;+q#1B6Eo3XEK?GMGM}*wer>hc~JfmD^Svv(m~myuli9{6MA<* zt5(a+Z04(7T%q$$e`!Si*xNc{Rz8TbNIpG!2pz;%RU~L9kHUZ9;R8r&6yPLkgo*h; zF98yf9Z8bO5(lIQE{M1VQY}CQQzl(>_eFUqrZ$B)p8QZVw?&1;BgHO*&>2D}IkZxx zil=j7vS%@=hcyh~iC(G3W1Cd* zr&&^4^E#6YR6AQl_Nzg661g%2^O; zz|S-aCW@4W${GE$0K{rueZZ@o#iciIq@0zxNtWUdfQlEB(_Be7$nbM}64-M{q?& z-pH&5xH1qBqRmY;){PEHB2p%tr_N$9{%~jI$xR&3-jx08Q{|Uu+hOfr6hAACe%b;1 z091SGwTdSg?<=@QkjRk8oU3FjQMSI3n!7F>c+!m>(Um>Q{97RnNHSP7?(GP%R zdd1CzyzM55m)JPsEiNbGRd$=cUf^vHtv39%4HT(c%Bh-G;t#en*ICEL>QF6Rr({6p z)~@%bm80rO+-IuW6?rWx+A09;pr;01I;bA86~A#g#)&Vx9gSsLPui{~Yu*=eJVS_a zwZktI3#aDG7xOL+Ur|Fu;$OBU-%Uj#Qwk+Xm_7s;mg_S!HY%$T+s(NyCr(2{?Npnd zyRYs83BF-fb=LYiYj-H))||r$cBa_D`MWjLyxR|?4WdPK?j}<3jJ?lwTyuOTzq8)G z62q`y4Ltf#)9nf@p!$Y$wH?yYQ(1Ghs=-NHNafv{YgIhlqiyt>gbApPTiiYH1G~!b z5ke_lefs;ApZFD%o6AGZL+_@XIGQcA%0fm$zrxj%MREq{4sPhKomS8A52l_|u~p@TB6zum34vpd}>EewtJ~bY1;j&t2dBqriCkPwe|w(Gc0kV{6cF{{VUMJ^?89LE25vny)oH zv&z3WRfV}$YMrX9Syg6Jb6cC8D{;xF)1>INe)0H5f;L!OV3hMcVe|L(qkJ?2zvp`-*lF2PwyLe-;pzBNB?Lg-R8s1y_jEbkMkU5B zJ!j?|Uxacym0Jn2=2>b(mbE2`yHLPvt+KZk*gHT_mtP2^?s;Zxbc}#~V!w8Z$G7d+ z`F=25JO=*&y=ARWH)r8H)%8c>3IqQDaZ6==&7^q`npAnA%g!Ck){eduKmH}U{SE{F z03uI+{{Xs?&p+~m+nJ;Kso<(4=$0`Dm-iL8s5DD|zdL{SpZ@?7WVov^J~&=Ir}!dH z^B*)l@Q z9Q1(qlr~T4iQ99Qrjj@R0BQVE7UCPBhQIyEO^VLqTqL>mm&UE-UJIjfqxTdfK(BsK z{{YMb{{Xc9DOhmC{{UnE0L-l~ZS`Eh-g9AIL-lU3bB(Q+zsB%gC8nF^vNqPz)95Ct zY70ifeM7VpB>5OW18E$?YAzeU>HFP9Q zoxmM^kcEXUQwH}`+t>Q?6);lJ`$8fU7y%$~D=+)q#ay;#*ydu&XGxOK`V}i5gj7)~ zTKiOmkGeevw=Sy{4&iyme%F?NYNVU@N`5^ITuk**7*h8c*Ee5Hm|`kV!IatGWsy0~ zrk%u9G=B6&jtX9(bVrC;F7IzZ#dG|Q9^<-?@hk@Sm_2x6(%5l!g@jh1kO)^nr2Uaw zXO5D0I5&Y-eZk#!*ygd)?D0^Y>){N#orOB%O|}p;K~#zNMTqUmnkrUtzGvgy>HX-Y`hRAO+j+YAgVnklq~%KuRlTGi`%1bpD%wuIX!Ro*-3;bsR)Yrf81@yL z+|!M@z#abpIx;C{uW z+%L%YUfwzHk9l^?wWYj1=+l>GP<5!az6%v?9lb)DT8JOk0zK*ogPTtk;MY#&Jl-Ck z$LwEuIMz>Va+ zj{R)Ar{zP*c@BI#dJ+lpjn)q1sM;?$%t6wUtLE>Cb1L51JDa!LE_DWv6Ul24L3;!p zfvSggB}9K@k*|ahCN|U8f^^_q+tBWjzOB8nH%n7P&KSL}bd{k9zqrT}Qe4#!9lrjt zm-7R+#IcE(HR87ZT6Y_wlj+`liz@qO;!4gy&nO*R1y<{8h+E$YJB;)Zi|m{Wq`2mA z&@Vyne&K93<)G?U(_Ry7rsMuUarGcFrkEa(kRQ9CkAIke$_@+Wl1!3R@l<<}b0vyf z?n3*%n_xJge6%v_8N7wZC|b;AmovaC9x_|qRzr|n?$u709H@0$Ni#bqr|66g#mRHm zu31DFzyfcTdklv`IsS-`oFY-S)w8Yx{~ALRJzzM^`xks*Gndc$eLpN1-d}{{SR^*lV8DgvREFMgdwy^#tVn z`-b~c!traiN^e||$yK2_Md2>GkCdkQUzu{1D3}TWor)(s#QU_J2FYpL{4TfLpN|=^A%D0HM5ycP`4xLtz=!w z)pE?PpT$y6)}37RN(9eeX^(I18>f9gGndVM{cRuC7}_>e&z-WYXP0~I!!bPawN2nx zoWjSB!kwO`;(P!xfbQ&@Oxo>GJ?4woe7?YPSU8OhDn89m z-Bq;w9i4EmZGTfsKVo=K2Uz=V!!V1*(;vI@u&8psGR$(cW6oCC9k^E9kWy_`6%RK$ zVwIt5QZ+~$PJTOQf=Ky&o1Y&+`!&-^C@~Ia679gQjT+bOtu!1y?6li04@Xo84Y)- z*bPN@`>U-6r&^o%WMg!XO{qW&kFoHzMEt6i;uwOm*j8`A5I60(qP>T8w z;j+`}P6Nxi65o;FwfMugjC!h>>U*Nlpp`Zjl9{1hHwdqOVhBLhSH*yuT`0bFd4q>? ze$F$z*9gn0S5=YZ4<%TQORWW(%WJABLy1yJ0HkQIBO4rD(`ApDs|S;_bTV=fl{0IO zPip#lcL7mlP-c@MzFBPiirXY5&?^#`=}@pIFn#e$-B~EaWwl!V!@)eA41MiCB2}UrK)T_QGl%0;CDLr+W z{Sb}>DJq;8uW%fJ$5$pUc!pO|Wvr(lwEGUJ3z|&8GCe)`QFtHg zug$z>*&vRO@J{QmHA{@k&&siKlXmhm38VAcW}NU{{Ju zd2@d!#ia;XtH?MmE1MyU`t1z*uLZ3NC{FN7jOPn13@#;mb`n=MFbb9XX|H}OvoK!Dv?9^O~?}AD(DR2MiK&z?L~O^k|}H#M(FOV z{{R70NO5PPXX1U3*GsC6tp!Y(b}3-WX-&q(RNX>~cYFj-Q7|Pq;`)@;T&@=Cn|i7( z^@u4F7r~F91)g8(5mM(6YB!c z@Wn+Xq+HY0M6}A)18GvfL{d9-q8lqA#6c>kdBP+bZ2jRTFRUrL%jG3Jz$nirsr4^) z>I$6QR?#nPR$ZU!E1LoRFq7@is6K@#?F8t2qnWxf;-Ogki=o^anPkYI>-RooKnE;ag$!((mhlyJz7}LE|3av)3?} zZaE@vm<>|st7@rSr>M5ENm9o8R@@D!I>!G1ic{UEjC(g>_sxzul(=^&Uj%=)-|JR$ zy(WfiTzt=9qqW~sN7+q2#i0xOymktJRC|M>myn{ll0Nn$(B0=hZMS(qUlKe20O|AQ zLH_{wa+06=zjg}PpZj{Id&nl=bN>L@5U0;B+5Z5n6hHp}&d)NB{{X}O&&4CZ^@i*{ zAbNb<)1H6%eZ&#>u?znIojYJ}Fi@ZW0H-gRC;tH9{_*&zH#5sAs3;d~rgLFZWv5wm zkBgWB{rh>U%pPB+Hw>DLAv5)yNS|5mF=J&FToo=_PMjpD!?^3npr^(#>NDxJPRd=a zXdm7(0x$D76liM6c8!p9hC{_wl_-S&0GrT8L3&_Gt{r9J2=Egluh@*RvoY=cksM)2 zLJ`v-2b@e21bT%;l1EWh&mQ#eqtdV69>&Z(8r zOStQzrko&|%`tHcswk-DJgz|0rP>P6^JJk(Ap7C2t5mVHJR|j~4y}uu5=nFqStHNP z$|eO4@kI#d$(>kqOi!uy%5j>?)I2RVA6BOIG8Vd8WDcrO1EfaH(NlcNPHXs7?V%z; zAOd~DF5_<~qM&=ZpHyOz!Rn+=9up)>@3;yOk>n&|N0k^0s(-=Oeq+iQmW2?yyh8~a zXD)($AX}Z0k$lNJ4x71MxiXM1qDTE90kh^uJbWc=&m&$DZR0Efm99wNP85B^S;n^M zys?6?)?=D;?N3K+rK@{2j7&3B(pR`hf$+h2o0CM$<}^ECm3ZTjQG;Gm?QJrgX$k}? zQv?paddI5aucHm4m(BhA*ptjl;|pRqOS=pPrh<`*hmhKeLVYzP1LjGeNWGF4PBbgR zt}QRD^m7lnT+2^zoM}>l{yDAv5PltDj>AdfspQVNx|S6Ev-4LSx%0u98$4L{yn5LmG!h#$SH9f-)fa1 z3Q!?aG7=Ix!0dl6=+MFWbqh$iV}`Nb9pqjf?c=#xT7Chn%o$25t`_%gb;8oy?I=}P+Fx)aZdBw>zH!$VT6`*4<986KPu4rSA;8!LN7JK-IcASHWy_^a%bK$5Diui0 zO`@r?XzbM}LyjamM#Z`ksT2SR8=}6{7n+oLC9CGY9mp~qM)gaRW0(5eYWYBCLwL8< z)KOJ4F|_PGUs3)D_TRXtcP{?`yi4^=<~$1H#x)$rj&Xi1 zb)`)uRYnzv)7fsOm?R->BCf8gxh*#0NkR0QN`g(s;S-{s+17BJG0Xf%%tXKJ} zw`-AeO=F)rg>MxFO5eO0%&3`@JxMymoYdI?EeO?Hjxz5G-GW)}n7=so(S-MbgjX`L zM`y~7ZI>Xk)ND{iMW-RMB-K-^;`orXEGtPVAP{1fQU;r*#{hBFYEBdMbh`6Zx3{~l z2b@@V{{R~9k(S=#UQNH&rRxP{U2U~@T031js8n8c=UHXJy+f)`Qa~d~DQNa0TM!Em zdxGNp)3@$qxDP@%agGf12| zs%V%gqBnC%`Fc8m&>=r;(-O0EJe#CycN}4Ab%)9oEhRDfL{3uzFH>ZxxSvrCD7xgn z5U*?m_O5AKquEb%d_mhT5l@a^`$ekmPn~XehaJUhK4QMAtkAfWhVAqg>O?wLq=YER zDJ4YD!Ym^?Q4U^fs9P7vJ(%3N#}uaSPdMlPRqZa|DHV|3xc35DXm1#WwvCj&&Azn> ze5>Y_x<#_tAJ&{haB9?KM^y^<#oP~SJ^Ix4zmajzZ@jm4smyP-jxyvt{U)Z%Pinb8 zSj7W(OVgAob8t!=3tNhi+pOmm;(nP@@^b}6JMrMY&ii@KELhiQ9Mf~==kPm?#~bb= zA+K@gspvA>ab;1fnZ>56x>(0Dy;z1bZReiEEKSv17e>A! zzoE6tLMNccSNPSf;w3TiX25MByOUgK^8S?>7STAE#H+!2@l~`Dz z4y++HHFq7ytjEHFNrv-*x`G>{CV_nqc1ge#y}ohPb&i3GT14I5OVtfiB|#->frF zW@R2r-8X_+v|hj3n3r3`Tu!B03-v12+m0|1>qwOD0Q4T%t;afsipMJm<*J}u=Zw-; zEy|+OX)4wn4>-`;m5B+=10(jwpkdpp@y_AmvRjd`jCH%6O4_#iBrQd1ZDk=vTJ-2X z$lrojmt3ETSIgy+%S@H-W%d!SiC9_x0F@{qt)NVd=%Z!SW0u#@^LJXBDwY-&!BZ(- zpwIVJIEqk6qOr6guGLHY#dG5xN5k>1B)r}ptFvV!+&D9t>gmx+s}5JVs61$F zP$&wPijdJFDqhMJr4xb$c=~#Mt1pv6ydNsOVflRxU4o&C#?61GqRmy7`9!q8Pgzd1 z$!RH2O3<_sq?8Xx4d6MnD7#r+$8$GDX}gffyJzMuF2ninm^lk1rS4_Al~{So!-(Uw zPtsMlOqGjfLi(!x7SbzFl%drJCS8~UY1bsiTt_)*Z^6K?NOYFdTZflX{6PD+uKxhT zg3Jqhc&+;FcBXGQrNeBrEL1;5d%m+)hUyDKZL+V++NmwA&VZ4$9;PiG^Gy(tMoFSt z&25zBbxleg|ru3eW3Zn1ytub7sn7&wL5{{Vy1 z`J|AcO6gRVgqbFD(@9!FMKud2d2iEo8g%sZ`;_IL;+D(@6-Q|}F9OiqJBh(+p15)z zdgG5)WSI+8?W&}`VlI?6D4k(QSEhopuZZfhNG=%*9j?lf_Zrz+bl1m^{6CKs5Aa_) zWqfa%;{1<--(JJ>+*+ir$#-wyT1g(8)YT21t*%85wyh6Cs1lOmDCc2o^#x^LZzZeh z-6`qm_^+XkFXz5I=N>)B^2~fX)ixsfYBR3UY|gfd+tz7YP*mM}YY9T!w>Y&GkWR#( z#$+WK3!5iI<$oKZb97SC<^-;~v6Pd)ZcjdHa$ZzlHGs%2!f#r5V~TGEor9;#_lb^CXz zwJ9o4B(*)N@F3`JW4SHaG8dDeP73>N&l}=y-Z6`n6ZX4>lMSag8(l+dVxpF!s=fEt zhZ}GLmGmkEPH}&RAl1jkROU$>qt*L@DUH`tqP2%=(yEjt)`Cz>h}@tIx@i|TKMN`q zV<=n`f4+R-j9UA5<1219*Cg@vv6>6D(;rR9(cCDjT&3Fc^aWqTTbm5R7bzV)Vvq+? zr&X-STDPbJO%^X@`1PTm{ja$9CUY>u-dC=xkzsVz)_1pV3|tu>6+5Us6+Na?O0^I_ zFegpc{n1iBSLy<_+)wok%<=kqTvMKKYF>ZA*+@kdU1dV(vP)VNg5LUC_JIz`CP6NV z{{U_cY{rW%knI>H9K+^X)xbT|ah`pVVi`vb^EIyLnd6p+>{!1H=L!6t>20K{^g`RE zeZm{5m!yDH**mt#lNGmdH2lJgB_=BF1Mun-Fi3t z_1>nb+O~=olz_A$Y6T#CDG5j#0TuGX8}g^aIX^f`mrg=O%Gt`9ec=q>u6&||v0}B< zl)Fi1wo|s_OrZ^Gl}L^1AOfOeqJiv7BjR|W(L?#a)91FXM9tHUn>!fIC5I9hteYma zzKUwM?v*d>VGbk$-l%ECs!8ZTh~IYR?h!UNax+#vQw#UeikBp+!RfgF0Gd~6xbi!7 zl~&7T(%y=e*rueX`K7DWrg4Q4w4`)8krBH7Hj0&hZ;-xV+P6$mT{{VJ$++uc56)RO@JBs9*a-#V6(WAR7v-YJ{ z=GYacn(mj}rdQ^-T8Ba@%#?N_+?oCGf=G&A)#a+2G0&q1Cc8;tuDf>~j@p^Kyz5nr z+w6BLrPCpjuvNaMqMRs_nQD>O#vnIr@J*8S?iz3D>sA9C`aW@8g>76i-f78mnkss< zF?FD(YL3%QRZjNS97??q>!5sE$$cTh?W(Hf)1R02{s1eR#2uF4`2FLM_^!jnwA@2# zv(nWJf05`{>QvlKD~V+x)x4!RwYV2K9V85+t};z)1DY#T3|PyVoJ59zE0c}jm>(M2 zY*|&d)kBZlYiW}1QF*>T-4iy&Q6&jMA2y^TbJJGhrD!#gikqvsU!l+|2(i$@bQLQ) z`>^_|_P2q*mg1PrOa08;>uPCg(TdVO+Zc7aG+>g!;W6>8@ulM^NJoiUC!r`v6s}gaqa@eDOXQxapcUUN}{A2 z>NZPqovB*FWw?~fOWS;W5PW_dN{Q@yg&nV_r>R&Bd$*nz?QK=}Ztic?+>yTJ?qRj43syS+bS&0y8v@sXf!^a;*_&TlU;JD7RMc`q;X1S-s`E_;@aFo zR8#w=M^WJ(q6sVu%b4bu5EIvgt8lDt@qECu&}wncPqsa2ywo4=DC%57oS>?jK`B!e z8&2WS6Fnn0+&0d1T+K8s)m~t(#B*|yp;VJR$1dHQ*(r05Ub9kEcPp7N>-Y~EKI&c= zwmcN7W#20jg)JzpuPIt68zh#|HxxHbB8D8RZi(sqqe}YxKF>}IcMY4PAV20KZo z!~QEVIZl-|Rts-7;TKo3o-@8wCA_NKE@ypZ6N!GoWq750u_>Tejt7)#;Zo?qe{0Hwo&%4`7SEhTIz1#T;^W0A% zWS1M;xus1-7b~}SOE($}#hQc^nkQR%3r5!TEtb*)QjpM*8*v0sLKz-_a9F4Wv&v+W zq@F&Wo2sjQPk57&1gYIB{dmvhG}S!$?)i*Ig>J|36qkpB)1fy zSN{NsZv1O&y1R4aJFS}@=c`RHbBuA@p&!Zr0H5y)YNGQ|NT(7injjXDKS$L3W?)0Z zoK;lztRB(hwRn1d`X#ryUFGG^-?@^Ox|5I`eQV@*%|UXK>XNO(qM7#6l9Hq(NkejF z_;lz@QM3&e730pz{8~8+4DHgzL6TD4RYly7Cfb!<$My>PiMfSh)yeeHRP%C1%avPX z`e|6)gzF)!>x?hEla&7eY;WsA$v4wRT;J|H?p8zT>A-m>K4s8>SRs?5M72dQ}xlRKWCD)d^Qlq;(K?m)Is_t-hjiI*;*C`Ip^J z%ijGwAK8!hELr-T_R(9+jW?I!HOyRZJP(gON19|$7MXV^eGUi1RG|~~9HtwRl_<=E zDaPscrf7*++$VB2((FcdUo+QBWkm&2kyMqorj$a%i76poL>`1jKISI}Yohm?JGMyP zX6scncIiim*fXl0T;wco`JawBRe^rUZHiXb?l(jzwP?5QQBVFNuVlxHP@r_2M*~0w z$7Ci^?wXM_(3M>)uISIQx~Hb(@7}$R@s!{0CByhe~EA zsfr%#i=NJ-aDU>oy{kLL-n$X|5AiD?xOUxN#k}?@?|fH|b4|w|q`FeQ#hHTY6OP=j zG;WGQscol%{gs)EQ}v!PV}j%hJ1mj+n0BexG-y;al!Z=k8N$wf;%%R@185KV#!$#31@uH*kLZ%Vv-`06XLkL|oblWD09rW8vw--Iinxax%lnq^ z?vA3G-Da|5=x(fv2EJyVqMq?|s`L_NQYB1>eNa^}m16Fvlc-wS`+a_%rK`rq&WLk} zJSnfM)-+!%T*+X^@jL0L;MZ@L&LzqP#=nfR{G!&yzV&&<+qpkQZ@IZnhL}n|X|_t{ zhSHPZks<~34|C!ir<+Z}igq$NNO<32qKV7KD^F&>GJwrbqSD$!>1@a;Y^;1M1W3SljSg)Hs}5T5g$N#L1yOIIo>W_J?1^rP2gbQ7Ey9N?t+<$ zg?jsvzruO};U2Q#MS&!e6${L--n!P(d{0rV#7Z0jkk*R0Wi=brvCt#%{h~XnPU(8s!|{GoL%A&a?GMan4@f0JEKilD8gb9cGaz!#c4 zA6h!Wm1!QLtoy3<3LHNXaUhdFFe6&smR#<+K>(Ea2#!O+OAue4M&yywP!yF%@JRJ3 z+9XW+YXeH9i6lG@)hD1Pa0yeS3Y2Y0Jx;J8M#h^pJGK6_;3w>eX6TbfZcF;Ilt$-O zP6B_}D)z5xwIWjYK^({jwK#6*+=8HVAWV5j6p+_dAJy&-DtZufh$b~E!5X){ClF`& zWj!OJQ9Vc$N>b1iIwz;PCQ+#}-?hsfTYW;1t$ng40-to|nkdzZcN}#nXH_1F^Ntb* zg&U(J=%jDU7mZn*zg<`_ETo%dxPCX>9R!DmnTn|{obyJhIzlm5DrcQwyn)D9dfVOI zr7ce%@bsWbETD8sK=KeY*rz>122?Ppy<9PoQ#zC`ok=G^Bt!_@JBI{A`+#v@TR-lO zQ`TVJ)pvb{+>_|5JLCLfx{MNLwd45>b53b51l*PZkv3X$qej_Gg= z?!TX|7OOWL<(Sq>b+BVqbvRyB&^c=F zxHo2|T+}F=OY#h*GM>Z$I!+y%#&k}Gv`N`H;dsOF7KajjzO^aw&Uwe&jluWC^9Kpv zdxPYj8>PqbjvcvC+^((L@r+)wwxiKvwuag9RJ1A`TU6*0C2I-ORYrA>fxC~FhwvVr z3wt=6lgjR1ZYN5S;O+-n@B4y``}E6sBa=CQ8_D@Ux2#{bT#oOC!ak1J=)kbSZAFKFHg@!*X?AGv(V-gZU| zo10CK-&6b+gwxKm3*p5By=KPCRZevZH1!U&pU{{I#0eCV{{Uz#wEDjHMNh`uagTPR ziPw>m@@*|O_nInc+;z7pF=pQ@qO}z_x8k@u*QoH_{mM3|Npa^L%)45@cN>?z$XV)KqQRMUDz8|ZKQz-|2I%RC8YVym z5z#)=r*`fU&pk;vt8VGahW`LcJ{zA}`b5XA_Ui?z-D9;;;nSoldOXcYS5xi~lwDMy%|Iex$oRt5a!{vy zna@!ixhr1lizeRER5q~4-aLwo;LB2nmqkwdskG%eor4OZ#&9evI2P)vnu^DBOFc#H zi)y_jw$t>CQ@Y3wsHmkyM^sFB##6i`Ahaz{YeEPl>{Uaii8rTh{I351+&=`Kt}5@H%^v1_GMs-~`5}~l%@AZPoDEz(-^FodxAMFH z0C4;eD&bD>szQG;?mr8lk&@?#NQwCTBu*dHpTv8=iXW2O`Cb12xPA+T_SM|2)cQ~N zg@xO?Zk8lcf8{t~d1mn*xuphsWMm9k;ae}%4q^!P1NfC)kYpVbMwbqnrErh2s(*6l*ZA|Y2vb`6UT}>hvh9}xhgMOy(!4iYtth99N>_dwN(j0I2?|@oo!0;+si50Z@vWFB(?540P1kq{ZK8(v=UK zNj_kRh1nMPs|Mm^CuHX1E?#epIXegB1{5ls#H-q>QvqQmZGc;GAdeE28GB-*A$r*x z%5_&3xQPJJB0o^BYvT^!c|!r^emWI4tL+`7yMARwRubQ+^(997WwErG$qH8TPn5i4 z#Jj4*iMwJS*=mV$nLrXun$^Fu1kPF)0N|O4 zls2sJR_nMczBcO>wDqK{kgdr8Xiktu6Pc=`Jka8>E1iCw{OR4b*xh?y;rjklOErd? z@qM>cQB%^}YSXdAqM`cAhEjti1sM`R)=^=X%-LJ8b)Fi1YLgj_yOq?1rNH|%_x|dw zPoer!_bGYpCaTTdRkF!r$gEachVHa9_cUCRwkcUqLyL8`(1f|QGayU@r2JyC*S@T- zozY>fbXIqLt9NKRuF-t&a8C^5I7UTPYq2zi(6vzDVysS1p)1;zc=Zvn$79M1d*NQxcZ3d)_t=zKjN)Li5iraxr7%ad`QbQPB3U z%vTGwJ-s-yw8<;l>+Tfn8pxS~bdNHJw5JjZ?7f;^_OuKn8kM@;yy2{H{{Ryk1BwQ! znw3hebD=Km+@d8Ot!E!X4pred?-zHR=ZRlBJgBwa^Q_M1w~zj5ajkI=kHnqT3ff1s zX(;k6jLvlKUrP4##CGfeY!}jPo4&jY55l`>@3WS;8pF;wcX4>joMqfy#=3;mUh!@~ z!4SGyOWg|Osk>88@eoQ$NI?V?$du=E#2h_1DyXqxv&h=~LWBcK5xA zf~)Em+m~zo*N$#>+ibxt7pzmb{KGQ7qL&w?yJB@=G?@a_@!HKrZ6ej{p_bILw26_a zIcuvpo$QkOl)TEk+O^Rb*GQ=dR$53q@wukYQhkNC`n~TUI0SnEhB4cMO7|6 z&wj>>JwBzFWrLHkTg`pHIJ0KC7Y{M(+j6EJrEP8RQ2CY$!Q8HdfIMSE+cz!|d_t93 zyz~uphnH3JAD43m9b?9M2NdLPV7W1dTx#g9w=O=gqvjmE-6$I^)1}u{@F6XwUOsUq zR@&B6%=c)nyK?5{Al>B|hD%;yqxY4_#{{UZ!f~6E*8=TguZrkbJ zx+n>jnM&PRALgcJ2)OKAgImlSRRn$KV_G=V+M1PASw%G2tg%l(awdS^ogJ zzl!45T~%$bYPaPoC?2O!S{?LWXrW0u5K2Udhe2<18~M3Nr>P~=?dOQInWP_BE9jT8 zUgqezg8>y@Yt&nz~U==j$*s9KV$B%zYpi6D=R5C~U{8EXI-JvE%6|HXs_Ptlkg~H>D-`08ceuZkI(CuPF(ABT9iFN%q8oRnCQ0&R9~NxVGbJ znjRy|sfrx7q$|vOxmuyz`mHHNqSo5xns&mJneQpX$S#ZVv#6`lI5NnCz#AS`$Yd*@XP9w7>OYL2BOsnES^LD{0kn>+YWxoknWk~TUS34OoRv@ z!5jvO#b;Pcsh)&@B?drhP#0kk%Gd=#&6$soLD)^3?#l54dsc)mzIr1uWC7|g9ohK+N?lDBmH-n-@vexe=nR>LoqsYLv zu2WpHnyBlNtu-|Mo9S&zr&P}Qbk+gNL|v+~!rYCMcGia7r=vM%Ye(l&tF$w3&OJBLNT=zjTWuRs+E=l~4Rt*u7u<3_R@KI<(R)h~ZDKiM z6bYf(Z(?@d@H4Sw%kxC=rxDVkr}>kZE*AQiElt$2&~Y~A-$`vBH1bcvozny2mr41Z z#h|`TaUcrgts`S zCZc3In7L%^J1W^NoSU_3Q%y~3+zx;U>0T5}VH>8xj_u?i>EomhBq{5SN>emTZ$lBT zMw@+2Xna2ua^wQQs9KWh%OSv^7u1QQiHZ3~J>fZchp%_V4s#$2)c5YZoVT0#&zJerE~9RW{mS`8(;&G}G^H)NL0?i_X@seABr?!LbC8^;Avr-M zyr_tQYiN@z73$b%9|{C5mWY@6u^&*V-rA7s(`xhJ-M()qK9d6R^*`p!4SvkS%6#s1K5?b75Kza3d>6sk6|I%{n|sp>oF zDD4jeB>Y4ZG@fK+l^0n(+wP6t0?pM|#C_3J_a3|D<=3h^fw|PU>$mpqP9Y1pRne3N zlz@jw^vZNfbd-cV+d7m|o7S(a9?tm#>Dy(+#tV=4TaM(p-zQOj?MBNLbd-(Vuj!}t zMN2fxN5K0+K{|<%(qbqsENAA>B8Bq^Unt*G?AwDg>V6(y0=bD=enQ)F9miuh{jgD0 zuaX$OzSV8grD)ofxm?)woHJj>vo#VXZtQL41L9Tl%F=2TtNKOMvzps8ej&Ll)}Z9< zX5mhTmm-l@Nw~ITdYyFAFMEFTrF_%tRDI`SZZWPNrJVVtH}ZC0kYjga=v*vyl{9t1 zVnjH@s&Dp3(@@Fqg1#|zp=*nL3UAZg?dzW4! zwS@t+{1ok^to6`!h;ACZ{jNOZ6=(3O#U}YcUm+U=-pdE39Gi& zB!7pvQY?v~=1!tgpGd5@jGaQbg5VKCn!KH7ymEz>ry9Ng0O!2n#T3Xm&Qy|?)tX_< z1$rw~fZtb6fig_bRKOClnuT+3DR8GB+C4u10BWxMW6QKSJ>s7Ac(AjV;x|T(vypH* zz!yt=uv9poS%pe+brSU&Ur?dsOb2w(;YD+doUz`dhm_)2MpeqU-ZZsaaL(QA%u_#f z!!w+U`EjYB$ZgdQqvXHEJQkD{wxV|gZ3QYCkrnZ@@me`wMvh-kPqkOu?V_`X@|G;w zRhrkI;|Y8kt4*%rr>&P}U63lGt)fiQR22ZLKvci*IDs&)-6u$IB~41}N7kV_^PVE( z{2ub{>h(jA<5ZWFw_AsBwZI`)`Z{-lTuN@$i3kie*V7w4WuioYXA(ypQL@}_nl~J- z_i|GyU#CTAq(Nrejj-BUp{8KZ;6q7}nFr!g)m|nG1dWwz?fRRm&&1sMXyl7N5rcCL z;-eGGxY@=P!X3}slA)1e57U`4#dC?=x~d5hiIFq|s8F1pKu}UbV?LjMi1a=v?>ce5 zMRUy+UC3-(aBfh)q&fWWNT%Dvxd?$I)=<>8161{EKuVlJB5K#HNejSLmv)^~>-6;e zRAGsnpE99Y{@+e+{ye+#WRp0b#OC5^H*WG1tGZm3^D{kU^~ z5_9$!Pm5Y8zuB+kO+S6->`t1sAI$mH#jqAd3#6q>ZRL-Q&u~fF0(!dnjsoOV;W z)e2&*ykSbp>o1p!Wlt7X8hdc#&Ni@9rP`}Y?lM#V0GQMR>8LohB&{S!bQ6F}Bx8I~ z_HwYl;u@;?+^xc@`N{WGR{-tC-%CSFa&V2HzTG%egbBz^O6VidFn`!oS=WO-I4D~j zplIFgs&%oj)UX{)rX&_T3|h|OT7Zea7%G-`_`G`Co#kAsi(l$)nMPT6ZjR%CEu{K3 zwrTCir!F=}R4BDSd5XCldTVQeV;?f1{9A6e;26u?gW0t|?+3>2!{%JEt?E#G*XCamKcK4rc&VZ~j`Zg11#d~r=y zTDxAUDb!o4x!bMFMA1V|@9>mL`u+)l1AToT%86ySGc?e8dU^`hryZwv)c!8vPT@k2 z!mmx#84ympRFn3>e^XTiwjAEIad>CHzU%lq(6{5fp;MRQbie_HrcOb+oTXl&~;#s4(`YKG0 zp($FUq{O+>m4cw?uMQ`j;#XX^k1|XHmNBfR*^61OR?B*6uU6+4MO!FBln~RT zl1fDM9U`7wd)z2OE;BnFQc9LAwmZ$Tmjb0rpE#Q>4b2bLB!^HH6ZUJtPcA} z?L}fP%e6|Vu~#(Jv?`c$dROXK=>yMkeJu}-->X%QWchRU56f~ zq*98NS9!OyB23T1U7zTN3wOU@xX(eLRhK;alXgeO_Sfz1^A#ptbQ{Zb7AkjczZThT zQ;h)F_s&ru!A$iVat{MW@9Du*E@1lU)VpM*sNp^pO{VvHqkS~FjrB{Z1y<)v4m#Bp zDnbmjCv(yh6CM#guWiQCdEA$+cHvMo^z`7dqd)FnyPozl3nnLayk5CCAjsZZn5G*_ z*isyEC@vqBq-SF-r6~mN+cb%isYS4Y2%A=f_E?f>A*uVlJ-YhTvu^C~k2sGH?d!N+ z@A=Z%Y2mJQs;{@)F|0lJFIk0_=}gOtrlqN-VVZ%tZnmasNKg26$tp4>NvLLCvbl{L zap1KP;htIW?@IV+1%GGSO#szc>jrbim@UUE?Z+ptsq8NRz^;`GxN?QDLaUf_t1ZEY zUZ$i?OH4kR7S&GsYFTKiV`37a@j*^=ovLM}t0N;yljF5~M!J#f_EBcvAahn%$oS74 z@%{RtyY4%|s>m%IZ<*9G>sRZZJ6Clt(Hp0z>#6mS%T3KK$sm)VI+!X$Xilp{>qtbr z(TX=sJ#-cH_o%zpa0wudr>XLWgH&q(+KX5Ik2!6!)!lWw62E^M)5(@A{ejvN5wXYK zp*78J+z(HdRK27^T*O@!MuZSn54)K`>Lb(ltG}D5S+Wb3IZwFU8mg$JDgE@8-BVo$ ziIjRByMv<#)Vx>%(yi|ZaYiGFS}i@na(xo2F&tsmhBc2oQkU=7hMfs)?N|QtvavNQ zqKML9Yl6?@WVwIS!gt*G1-`*qHU^7{e#MzzR13|QxSS&~?!evY?RhT|w@Hil320?% z2u-lpyo){hjzr0%`rd_FR0h&F4}@z`jI(w-uaz2;BY)K8YMkU(HpZpHU%6nQgTIPg5wh zVH8v{4-l<45c75Wj=0|4$(_MlXfoVR%;j5Msqx#*wewd?>Q<2DTggAf7Fq-GDJu9} zbOa-$h}x4+Pft%3S#kvd_kKRlPYT5tl5=k2`7+~|Wfe6U75@MuuKBy=?PvJ)Rcla6 zP?9-8bEp2Gs zLuz3NETOtLQ@3?cLgck15Il7PDQ<8!`8$yJH%4r)+1xjHnam7`02xj5Qmio0db*^no8{1ooKu|6Zgpj#Qz(E%su_JDJ&yuVkmx8Fa?p7Oa zj7+1?=Hi|CXK7qzh~kZ1b1UNQYApKpEJ>Qy>QqX^(p*f`6s1pHV$&Pky;(UUZi7Nr zUnSdSD!uObmJj!l*iZXQX6Ko4MYW1d-2f?ec0+W`+`u6}5m5+jU>TK}2@i;YtXXEU zvChtYinr|k@wl{*MEaJjp6&wGVdLC-i?!=cTwSZg2)^gJ6}go^YsoU|mVyNuYBXAy zpk(?iC?{&xRH;u`^ZUk*N)8L&FmH3;)in6DrxEM>RWGueJFHyk>uXIVRXHKlDGJ=t zPJ}Hgk)MR3ho)BIM-WG?Uq7VwabV&PsIO=l!xuHIQt}0s-&ahA)|M2`MyD~w6{x5H zCteS{K$&Pu?4)So6{afb%(-E3;;hDZ*?Rf2>mQkRh%a>bV9hy8FSS$T51^FNFqKzi ziS(NqxZ;>Zkcogv26B%%v74nAKY*|9UXOeX(@|X(ikcQHoMqKhrL?TJr^-W#LPCVJ zm?Zi>KJC{wq2y4nF~5bjw}v2VMVZ*;qN@3mD%ESEdgXh%y45Y7*G$uubyaVa%hPS9 zf|j*>B*+e$^oXGj=T{O+*2h|{CgEBh=sWa&-Pw;Ha|M0-A+X#30GCg1Pu;62?X=dq zHov~O(NjE>Dvq3h9xtI%Op`ytAnWE@M-hAI3a+w}`>NhRc}dXf2A;kvVm_gri1*io z@aA*fw|Y5`2XYd7dgEJtzORii3`*;0xY{u*HB+Iwdu?59TA5mA@|5B>CUO!f3D*;W z&09cUU5O{yJ(~J(UW1y%zT_|g%HlxYyZwPv9vJ@scQe0b^B)9qJvD9*fLOUUoa9($ zR^`UJ?B*n-+tF}JE|$-eU@D;qd=`+?>PnQ73XveA6Z6sa2>|;L@!_Q80H63QeEO31 zuVL+9x&BSHI%`sbKQGpLuHST1fF4{{SC&r>$if&TZFJxZ^bl zs5*kAI1~qmQ5y)aV|mc3Ij!8r>JuLMTq;vEEGu7#{{Y7ZNgzQ{GMzIM_QC{(a+b=v zf&heFL}e)n1OYUd;*<^wu=AE%N|Z5=(YPvWUv&z7DUtNTR0#V?5t+ONPr{H228mVI%)h0VBDt%?r29*~YMXG5_1fGMbfh09k^3fnvvH>&bdLOa{ zl@!g=BY+7X6V@T+Q-e^Ktu-4I+P7^ZNkdp_nIe!JA=-UR#@!Fu5+S}+#;ru0QWQL4 zr5X;1y8wkG4JFn%Cb_jwt3$_VnV#OB=yHImZo@ZdpKENvNz<(U7zX6f)e_B7imL20 zjx9;OUmZg096#Y`)THk!B7F~soM$es%#No7X@ZTYQDsj%T5mPNuCr-Ve+@b$l9VUF z0n?-xXFP0K`|C+wQ=W&GvA4kWn@6@u0xnr}!W9&jG6X$OA>x%PPR5W&^+*%_aQN<6 zReaX6khWj_-ab;b+kUCIo;^(PZ8^6d=JxQ~RUqTk?^>>i1PuuZrdbh=^Bv8X7eTmi z{p(}p^bUsis7~5@{{Y~$6YKqtHBH^_5pzQ`Z!g2>6TAU6IYn@)Wa@gzI+&pH{p91^ z)4-(j2(~<+b9j$W+P+rqR}1e7^TmDJ9!m(hTPhUFE22|gCs-;XR zmll;c6dT>LQR=mmHfo&pN#Cg8+f`+XTqW?i_O*Ax|@Ub z&beJuwL4W-R{zSTxaC?#&yzpj=J&{t*mfulI z^Nu?G3Rq;J#FVYTtz{#exs29>bESU$XhHre@vdi2ncFbdpLY;_NyXi5FA!uHp9tk# zF>T~qon2KW(L9mMJezi*z^!5Js~{YS*$-+tDy|5)-T0obEaP06 zl!6sZi*3a@1*r=JlQTtJ6c0%Ce=KeRwlSZ|Y4#1i;uni~r)ov$kZ2#T+D5+O3e@_1 zpBLla+Mec|>1C`Ng)MhF+^yuDt~`b+6zeq2`MCO)me2;It4M!xLZhaQEs4#Ov-&k zA#epMQEIAE*Fob9GA>H=)gKn*Xb(+whqTplA$_|xMmz`+R`bMRfcKT^b=d_9L)_xfd?Ig(pKK}s4BM#TQmbfR? zt?zd6@k&Li3Pk?^%ylr~4MycOT{rm$-}s{SeV_BKT0$%P+`g-D{0e1+$bX{vg%Enz zeM>)(eg6Q8Y`@r_C^|}>?@I7WzwaE|KmPiOI!V7!&*UES_@I~APa>_NKlbse-%uY4 zRZtGU{V@yEOfxt6N8k9V`+&O{%JAId5#&3+b2mJ#>=L<)iZO8eCWgEKq04N9Xh=2G@>KK(e!s3auhn_O z_fd=2l9foVQs`nQRHZdZPhQC?JM|}Bf1(FWB?0;Jll$aHDFlo1FY78F9Kh=dMJ+;4 zth%XL#ysj&N-OA}YyxyDZRD%=$0&@7NY6R@iaUPCvCM;&E32revYLl!Y=0wesJN-e zsoC|Gm5OkpfwU3{>k-~xvH@3UnFcC*vO_7NhbsQ5J)mt<6?W^*(V@^ijy}2n0Pa|> zZ+5}rrf+0j?m+j1+phYV_B&JxYPhjiCd|UoQHeC@P&#T9l45h+u-2fd2W8!O4|-%> z{-M84`+|i<)3?^I1+gZn*NHK;KH%aEqBGrb9eIkA{0`)Np`+~yu>SzmC+Wt510APQ z)DIFdY8L+hTVfmjR}b?P@9;a{{XX{oD4PDFe@}+NG`*Z#)UV#(kI;YYvk30*gmF$C zg?H-vk_}hXgRvk;XR@nJM(-13w<2HsP6U5?Ta_$*hkQIf@5Lam>N(jmi7fU(WnYq2 zY@ieW0OBzp`K7Oq?NWb&Ui!V^C)9mI`$~KtlLHq=OctV3Jf*6LB%*Oc8b zEj(_W>7_8*8O#Rp=Oo^xR7gS?p>~~nR1%iLQUbIhKy{5q7VbNUxtJ)idD|&1rsa9&K0YeyeLp#F zr-Zwr!z)^=G?r}R)v;tqoRGHaQcDUy=DGyE@ogh{nn6_0ac-e+4==doaJV|^i;7TI zv+KA*Si~5CyO6zQw-I;K%|B00>#cVlG`l9hi+O_MMNP{5ZE!1Hva!`HQZ-!G&xwk* zl0rj?(MTIs2ni{FaQ4-${8W&9Lu25C!RKx)CgzW0xb^t{<#GO^{{T|n={dUFd*gd! z)VyoOHR$xK?RJ9V>n_YGKM<&^cWRP@;)c?h_>9 zM;7Q|u$2wRQ{xW-MtoM&_Q{8Tu|5!P>sr91%Jb9;y5OXM<87#a1+zUy)S1i7#`_Hb zjz4Pf-sZ@<=IDX)gy*sc9WK;3qT=caQq+8BtXdLI>Zz;^6i>Pq5>}Fw>PN<9(@5md zNJ}^O4K5v%Je@r{3X%u3GGJ?|J_JTPS;w_})Z{Gs^|g+jORhMj`wEmycQyLfPs_Ry;w&fQDK=LkVS0;P3Zo!%cUsPXuk*V40GQ90dud-|eBNr~yS^yO{0|l)IV9 zsA{rH9lHSH%rz)a7;q*vMw@mQh+9}_w99mKjRc2Z2+`WNL2+b~HYDs3+F)mt^FLJk zjLcEgFzS8h)5G6g7LR>2E)+jc?(BHqk@+JcrMGf7&M{)ixIMabyMJS` zveRG(+NcPbAt~3adPWZgYaC^~)F;7tM|S3}XQwMqC3ilr1@*z1@`DzNGfgONa%G07}7Hh5T#d`-eo5ufqc~cSS{3c9Lp2PvFslEYQ<~p^(cQLw$xTr zSt;)csNBo7?jmi>f@GD;ci94G;cH>|6~|-ASsY$`G&}u^3bE@3XUaEvEPl0&s zuMl8=?(X6pyj0t=d_I>d(x#Un&z$l05w#bMNg!# z_@njI81*)SH~Mnj?VKi0xeP{(Ag@BR!a;KzdZStj*T$Ot>vMjTT-|xfaJ=^)=6g*g z#NyQqFA=S-qF`0KO;p)Z(xojESVGcde2k-E+1F8DFo{4UF9Y+Z1ltRk-Dctghr}vr zTJZZ8rrkLMkAWzq#%;gmiaJm~=0@N2OEbGd@&;euG-~tjOxuSd@yh6Ge>1}^4%6&V zbUkmRP*P-P`;a}~Z`e7etq6-O{z;M!M;iN9Gr9bWWaUhztB*lhO-mz`B32?%Wt zIIYUq1i(!E1bG$JwX_qF5TIXA;HSE{wGN+Br9pgC^zEb6CvZED-vqJs2*VW$usmAw zsWGa`dJNjaProY`@+d7R?b|=CMtYNykmi<@WB+QPaonR)uig?ogOT*@p zg=lSn3MO`K>O@Lq3NkfR(>}o}X-oeA!YC*+*)3=LAebJIs=0o$Ev2b0H#N>;W57dX zHA>e}MYjfn4iAPz9c9y47F11AQ?(4K3PQD??1D2GB;m3*jlreJr{IHoV~FS zYJ1L#&~c+)m4f?Kwn|MpfYuKt2$M zrz9*(s$^lO=1U`VR)WHXRdLl^rb9%jUVUd80&5D&NGWiK;#+vQ+?!!3DhH|X>jz?@ z=5ik{@EDkUmp~{o`;{Qm(@~$l#v64X#zN~^D-!w|`XzBIbUZc8Jj-&TA^gt&08fuu zYSbndM`NN*Q<9MX0KctRKIoy!R-k%%Y_%9U0BPX1?w-v~QWOGHuYij26&2~6osyy~ zEUsYyxTDY{PJeVMXi+j}AzHK3p<7g>v#kANAA#m)#0BOymjIi_|H)w#iWeM?)4E=Md#r ze9wleV!So$+k)jXMCYcuMbzV%DjPSD^&C;J)2UX!gz93YuK1Pgpcbj_!;0JUJ3{UC zjfY%XPo}AAlP_r3zG3&^8~&S`wCsPY9)uHcFVv;9JqrM>NZ#K zX4yM6-6Pd*Q6XIf^oF=2BN`wn43+Nvu@eTV;W*n-(LNp#BZ5Ftq-c(vViNM4k?T@Q zrgct|_C#-BDa>^f--+nZyvo+%kHPCCeaa9;qX2qs>xp8z-Q2i^VYtH$>FFURUQ$e1 zcPIY<94fxu{{ZA7U72!-wd53d-EB!~mgcmwjmP1d{vAQ^k36->7#=(d^%-%kxNSJC zoc{pZW4~Rg#pafny-RZV+gd@F#wXxCvFVn1Hocp#HOu4Vh4i8?PujLf*&!6BN3X(9 z>w~{i9xAKpo}4s&u3x}OT|fMv@zdlyP3v3b&7rP!d?PIxoa}(lxFTA^k3&=Q4xvOO_6WgwJZrkqmT3}M+OR?2@ZM>ia!_qRB zlt~k)?^0kZt*0@n7r;v{wuqv$>?LD@(r6CSn z2r5woZJ0`@Ej(c)jY|~TsHY;yk&{!t>$M@r-BD7+{9*!1C#maCD^+WinB_Z5mg;1B zdU%OMT`-(deXZov)v%=*d|gjXB6#BpDHX2c%zfO-p_b^ai@9wmQmNWb^(+IZl69A! zkoN!rYa*)9nw}%zE=b30SoS^I9X}LurZYq9<(#hV`tN^s^oDkYN{+Auv{tpGhSHD( zhy=hdx_DBH0724<0L}Yq&av979pc@IRYv>!hI>k{+Qtd;ujlO_LOv)6JOy?FBUCPqh9nh0uD0EeK==aH1`*%BV{bjcD zFE+Ps%D1~z*)5^Zn{8u6uTdi-?SrDY24LWXytO|}=AQzVeyx07t}yPWBE3+z^0?nA zlXJ4aDKx64$!@8n830O1N{IHy)3`g4Eu%?17sc6&Zk6-fu&T}bD64L=o#3@iwWUYQ zfM??tFPw!F>{}~nwkwFcpPv=Y`)Up`@@a6 zxS)KwPfp{iZ2SraT|Mv_&PI+NTVC)f?`-;+U+79$sIPul+GyLGrB&^v<1 z9ZyvQR-~PE(rFO(=A#=RU*-Nu{{S#dn!aG|vgniQ^S(LpOqo`R{{YIA8U9QH2>E4u z#3ohCE$w;oTyveQN(%gGZmlX*zRAZ9=mUZ!o?A=AB28PnI={HhC0}wDTYHsX52mL# zNS`wxA+PSPQB`qm<&{-^&$zp@hHneE**oFm+8gUnAUkfaxE55}cTeNls!Y7ZP)X)I zO?NAT+*tnruK5d?dE**;6U#B}8zs)Md_y4Nu3N)041*4NRah@1+cG*AC>eFI99=b2 zsaD|AfXJmu{cv5cd)vvNDwiwd_DaB2?b~Hmv%bW6Cos)gxZ=ph?3XM1%&R-a(|n^A z`Zc=f6;)IZ$R)%Xsn1S8&!iZ8f(~8QejeJo4*-og`cX>vpQoLU;l~#qHU9u=7|QB6 z*Ds>1;+{gixxHV@_jI%Q#kv0gkPC5aq2)OHCsENTzI{^yeaTh$_ZT1VDmf=R$YsPy(%s9U5POAHJXmt91t4koNl}cCO zaRdqIK``!6NOL@53GhR7TYA;-Eb7fYH?;3lmU3!ty zJ$5Dz*O=agje1TW6s?fZs$8}O@wc72wDJBvI_$NtweIh@%bV_&JP*68a7JHks;5<> zlxRqyudaD-AqirrQl%qR0l5AZc_e{4ysjcp31X_o-Nx7Q$P}ZR*yytkQqSLRyfaA6 z#<52RtDmbW#Ob-IWD_zHN&latt zf8;!RmC9u{)5TyFTdS6umlA~}q`1l)LZ!G!5fVjmLaKW?omx~=)T^r%^+ituNSnLV ziYct2Y=$bS){`Qa8*l=Zj})i^BGMe09>omUeqrEJ+f_Kj=tr*oMUtgb^_#3@gds=6 zdt~YDi`X(jXQ#u;(rK!4;~ZU)=Q&M_598F*1!XhQdZH^xl;04Thb0xy2=uE zwxcyHbn}8AD+^U~ppvv9Pk6rscePo3$gs>YclNJ%Z4xW;3q>|(OGaquom!bnRA;6PAOG zEH~P#emP{I6n83>C*|5uFH}5KG}@NIQjf3*Y=$VAR}cbZSJTNTXsL1G$K17(4edW8 ztL?M5D{gbcIThmR!(6M83q~?+;~%*44j+``iMv%(Q&e9UN`_c!sghRn#UuE7bp+t1 zyKChxW3*nK41AGt$k0oLKl)8~r?X0Udh}iAeD!SY$?L8QYve2=wNUF-qL+_2ZyuuL zK3uY;=Lt(Syn~RqZFG;m+EMB--7yUVwJJ~>jd8b1!G>UD?HQ)1d3XTA*{C~NsTneuyZ? q2P;FX5+PaUPDqTa(u4ubgD|* ztGK^#x-hg;)KrC?>85M}GE%ORyAd=ERb9cjxNtH&kOc*Gpc?_MfOu*ZZ@yly`90#V zAj&UQc@6&J5G(I>^;H$MZwADW7WGQjwFHd-Cqban8#9VN+SoOzB`zTV$x=Qd1}&%74T04}4rLpmqCa0 z%%Z6juhZLUl!ZM|r(1bRNlG^BImEX!HL;Oi3j?2#_YMJ~roZv~m(*7#_LYy?c`q;S zHiL@*Kx_{+IZdhpQZEw$`T?qa*vVmBn{d(w5stsp3>pc8oUg zn~9IGD!Iuy265aj3&~cj7RUL{TgA>(KV-vg$SuEDi=ooNO}&7AHyQX=qz{R6(^*EH z(#4J-$)`6Jp0$E(m0K31a74JnjWHg;F&v+`e(5m!o&DLX=(GEO@!d2yPXqig-KySROxpTpAd_^WI^w_57GQ?o}8yn1cx z#;*OZbvZtNn|G1M+1*bq@qaVaBCeu}MSaqptgQm^c264|epOR{5R3buhfr^&YAfo{ zkND6Oe(!nYZs)m4SxwTN4b-iwdOKyZiLl+a$7iG-l_k1~q=1w($}|Z8OwC79V@qL# zBns#j3o)Ezb>iZ_mVTYxq2U;A4SVKpW^Fc}GUNJHJ8`RYDGRKs3EmlHLPVWL;ta_k z4SHsK$GPMV7FL?b?G~%DyIv^Zc0&6`w@?oiUiqJp?)>G-cM)Q<8(uT(7I17jpZl5{ zdI$&vbP7;T=M;Q>(ZB87VYRhuTx$L$c=mTLZ|W#v6oQpiGP_NAAgYnVnNrji&;6?hZkkMM6thMG^sH6Gnkhp4Uk?ONjktYz~BppE}Qmb?|cB^Y0_rd@= z8hUzorY;Jcn-9onGTR-hrke4C)KOn8yVzSqR#|G$d*@s+SJ;yT+D!JB!(!kB|9l6sM|E#TCN7@nUw-Uc=6`Hw3ua z3KX=0Q@(OCk@BlMc1Xh+&}u%#ySm9u41w$wrlZz%2ESz%j`FA1@3<`Yk881OKM7-e zTXwv%l&dYGzg6T4q-|;V{S?$uQ??f=fZ~Fs*KFV++K@2Un4hpCPk-J_IZH!++Wz3M zC%m-d*$de3d0f5SRhu|xm}{z2iFVP8+AcM@Q@3g*B9^tw&3o%jCAG9D2M?*ZtrAN> zldhi1@1oHiH1zi>8KP~)U-`kQB#KjN0l78x?Zb-9{y(a@;{2CSlUT2HIGyU#Q1kfq zOG0+m+TVREZ51@+jaJxjfuxAg=>|Z<%~VX35qPh&KcPP|@jcGL!yUKgn6@$g-1t(q z@pqx_3f#jqx7_Z_d_{3^Ts{F%hHtTPxuA0 z?Tq!Tl63h)K-*gTclG;yrF{$cnZ?z7k;5F3WyP$v?5{Vhu+|)J3c0+Rdwtxp-tU!t z#c11ZY72xAna)O1ihM(Weyc;DE#%Pbs0Kmb-HU^`Z?@Z?cie-=+!f1sh52H$sLR%zU~K6e+!e2bHFh4Y{Cip%w$jY8r9@Wry8y@YRupP!n`KQJV>d?90s-?W5{%5&lyw%q z3-&y2zL9_3eeJ^6$lUG9{p;RYRYo`4O{n8Me^`~Jbk#YIQ%slYp4wCR=q;&0Lcy2# zr4FJzySpy=WaSIg`(nzh=3DFEcYaw;IcCVP>{jaCy)}q(h*mZd;*_<24SR~F2q2jW zL;HP?=H>}=F)#{IeBIu-=ZmvovI?aNkj$HE7h5iAj?A@k&o|tc}AsUFS{;g9TF013r;Dj)*PmF zvO}p;YEHv0il0IwRcn)iQBYQL?+-LageZrT`96;sx4lVkbuV0;NlFnh@afdbDsq{I zwS+fx`wU~;@l^xyo&kH#uhiF@subNL&mn4cVr9>dpr3?M>#YSFu; zAfa?okAza82ugf_ND=8xmzAI|IGKZ-r1q3g!`I3hW~z;>qE__O7&F#lQu`tmI}>K9 zG7A7gkwqg#5>um1dWZ?zqgs=m0Pmk)2xOZs<`hlp)QJNwhEsu_+J!7>wEqC&fzd1| zN;MFm2cDt^Qz<6i>2{eYCs8T;V1_!O4>M|!FAZ%w$3-5WY&kR}7Toy$GbDozM5n!% zYk>fne?&T}KAN2H#0O*!p+iIZBDVRp2#EJtgUi{S`u&~ch^nk{bhi4p>FC>;*pxEi zK#=<*12m*O(OxtHHBqZKDm{#9>NAW3L6G~rWB)GIT`pWhgpQ_=-Koy^3F)tyL`@*DtNG+>{9jb&K{fbhjb3 z`wQ+gec(V{!08=m=7CYi)$8}^_Nnm}(Y8k(AXIy52iu`5>x=#1+Rx5U>`O`Y(BKGCLZ>j50iOM8+0xRrCqci3 ze`4L^u(x6pN&p3Qr;Rq(T9sWpt;RVro3<{@vRuaus<7eshx->qq^_arj7uwZ z6*ScV7eMI0X=MSno@Av!Bq>{fQSg)rWs{CTd34l|aVt-8X>iFM&rN9KPhb32cLlld zg#`iPBt#f>O`t*4A>y`>gcX;JkeOKtcy1f)FLRM|dC zF-d!B_PAF~N9|fWN5^p)p{O8@KJUJV>{l-=&-*aA;hoah>{#AKS$xlK6sI(_JG#pba2aq&_{g<1Gb$7_H$^j@+b^;2TXjGVqIUE$@qGk-j7! z-TReCn-(k(ME?K^kKg;$*Y!u`%#PXE9wo#$v+7L`C-QAC^Hn=24n12>X|p+1O-52u zr3C!m$3RSpQU`>@LFSCkBsIU7qCBJIadVI533s$E524^rC$M9PZ+=5|Lw5|$xXN$b zYFkNCMu6;ONwkCDpEDYM)x(QRP(Lx$l~vrCgL!o4eo>(IE00R@l|961_)KVvYE=qc zr+IP!>pA|2i94L7L_M$cLcRJXB^KTX*&OFpPRCRujnsm50)7WrYLFDINl_HR zX6cUEHY;l8?N;xJTa~Zd77(D_siMrT_c4qwEX;9F=Pu)FI8ED4sEb}ZPk8*W+?%PZ zQC&eEsy$7mI|PQGbYMHm7RfLKod*I(>|U4JFMA<00Q%`wNR!6Bud>>ee(xJ1sC)^{ zyoY*EpW@tqOlP9DXdHd#@mh9V%I$#n55aVd7qVk7o9?qEsN2;)pMU$a77>QiZ`zvX z+}Gv*0P>H)ba0--_21mSOta!m{Qm&#s2qk5A9+d5bNrwFQTQNE7uXAgCabu7mZYPk z?=C27Hg(qkv zs%*iW+d8eFEjQ(>vQuxPAyWSU9ZfJbk@Y1T;@U4WYKCqXf$9ma21=WcUcwjs z;g^g?S^`$oA}hWq&S&8rHHQ|3!FtW)5}%K+Q#f)&r*OHgtF{YW>rE@DeT7TFxPko; z(7+Osqra$C8e@THmf)Z^$k-% z^qmW*^+E`T6~;<0Rcf(m{wc)&0G&kWj_9>F>I(InZIF{HrOiKt{G#Xjp`T6{eL+W@ zp|zn1*--O^Q`|)&ygl-Sm4Q985|hgm4XQ|Pp=0+~^3or!A}kb6>z1qg*^R-c@!Zo_Wd;hKDwekYp^Efh=t_dU_(hmm8TUIRtxRA>r8f4T51@nr%>t^ z!-3F5y`nu6m9<`VC3#oU`tnAzPCc+>^!m4Zw^Bd5%(%izy8CAhj1FWB&eKJM{q|z*JTk7CC)2FBtf92dM{h&Y8IzE;7iAcFqP)+V3~v{%4X^xNa#Y#` z^(AAhVR9Eb6~4sUd_iWLt~lbB^QU^-M@2CR`$?GT z;i-utQB_Z!vGI5NS>^0{%~;uT!)8&rWJ&H{M$veD5_34ai9*|L!H|%o>>eZA5&)!h zwNDC0QrIK{tv%7nhD28|nsAJ#!2E?*{^~F`L*+!{jb|R!^M6>MR;H*gLA_tB$B|4w zsO9uUxuj#*h*|Cy>jqDZ-mca(loyl4J~h~i*0@4mCJp5 z3P?2XQX%vzdy02%)Q>pz*WAW!Ib$CY;=F>#5p>I^Ni3t_cErMGcG({JN^bB?=l z?n9e#TbQcYc3^6Xs3RT&3mB4snbHM<=t(zv^Q3tQM44yDD6nwp_X#mCR)Fp zpptz(KK=;^y2wOh=M97!>q_grHYU1Onr*dJjQV|jMRE>n${6EL;VLNTvLv+AcqU5c z@KsvVDI?|T6sZF85}g!fxOhxPlG6LH>RC+Q6Y0R|nuLGqpUV`Sk;M5|3S!kv*4Nk9 z(bC!Qi)w{U(z>lq1#bvsl28*pbOXe}Sqp#rX99N!Ufhk%zGRznkbT;$rQqBr3+!H^ z)tOxtZR8H-GD}rjC=j%!s@28fu?C#Fl}%06URKhS0Urwh%5x`wi)OR2V_V4%uU}4L zZ6?baIC90r@ptg`c<)^(dm77l&k;+t7Q0Os=5;Dtm6uoAL)BF;I-=vQy5o~8TGXPY zDC$WjJf?ib~YtWme{7-KZzlx(ltInLX5TZw|V!V{{Tv| z;KA-nZRhxvQdE~HsgxY*cBC24*dj(`?%T79W3je}@`W++4keIs9sQ^9#hVQ!F+r@k zqG6<@8OZJHKPdKZE${Dx`N=e1ALgbfA2fWNqnTgJcM0V62nqgZ-a!0&Bxm=>yAI`; zFP(qFZOKf2Mc{5ge%R|)oRBHyB!HD9j=FUQ1i8-5D@X+$W!iS}Kbnf0QTEfz*2>4K zjRI>{rKwAG3Y6x3v6A|EIqFxVTT0f)K$R{yrcaFqQ+v0nnbZwZdVYwsWkMOi60I)) zHr36>mipwHVhYlHfzkk+RUHW-TDzw=TQF+sdTQjMswJ5RPltr!7TX6YoiPe)vFCWE zac*hKTIOAG(Vzq#uso0!?42H}HO~0TlJ>Dvl$4}gIw_`_k4g*PBs<;t9EU1xFk z4KWBwGct=FW|aXHDwk!nE2k^k+?A}(veVxY+%rn2MKyF#EoH%Myr4|vuODPr7bA-T z2j<05B6`cO{BawdRILCees0-f)b(SgLYWIvM@?lY8t3Fw+d8jQc}p)dN6RdRT@1)27;WfIi5+jmM@|PV@V~D#WEIt>q17 zuhwHccUuU@!WU>+1W!rzh9{M9xQ&%wFdN-5{vz56**_B~*4Zi*iz}k9rmJrCOyjJm zrLR`oNTibI!>DbrPNzvk6Jug)YEckmC3~nhQjRm4+#bDA;SM6hO?PNiKX$m>ZA~RX zWk#VvUZPVXLbM0MD@|OqajBpXmEw88=2nvOPrZ*Aus|Q4WiA z+1pm9#baMJYbJV;uZ$feHDaZPPFJW}xrH?PlPO4v#&%&swDIhVrNbla(jo;&mF^DBY! zsrQQRYGSsWEpkTTx_RmAlE^Up!O;b*n73mVo@|jAd7gsQcHDmyd zKytrru8eP@%G}P*{ymXgtuGCqEaKAGl%|k$j1}Zuu3o2&xaW?kw@#py6Yhc7tU_F6 zMpK@m7DS0m5$WiyM_;}oPH6=vKk+Oh%<5x`Kn+y2#5Tv5lTtdA6(yyll>kh&oEK)o zU7{BHKpLjL=CZ0M>1~#mDiXT3kTx_(1OPRdFrCeE(HL(Oy{~HSuRjG+G0eIAYUx98 zqESQCZl!5bNCs(C{Nv1|U^QO1ZvIySzUBUp{NI9iWy4e$UGkxoXV`)oW~QA*CDpc} zAh~t$fh_X!u=164n8U+y5PcTfGuf>pfey7yg|_GK6e3dlV(r<<09bQ}i-1n6Z)J8j z$V=*J01ae;IsMR|uSlT%5+w1q_D6=5B`+-sCVBv$=!m552|_WQy$iHaKX@NboLi84 zY~))WG=la|ML_Lsj+}&qKT$@HSU1x`pppl+3d46xy5X+as!rzZhBDZ>n}zZt+0T0% zne@W$rzy{|TxqP|2hM1jv2ynuy!z<=a+->vZ&FrMxKiM5u8qfC+wjR~T9B{ZDbaUs z6|xV#0oqwI!Kq(z+%?@l6!S+6+PRxEqGuV$tCHPC1y$>Pl#}#jPc(W*iI*nWzICZ zYmQplE0tQig-a_+6o!zpQA(BkdkTuBF&1z_S*=q`#TG)UxoAQW>P=}s2$bcgQ3Uf< zG5RH{eCIf)Z4A<9SIQc8WGU%cyxiJlzw=u|;Unx2yv0b_2~LK2pO%?p<&P|6&r_%H z(>s5m}53WR(2kbs0#LWeVV03!A=O&4T@hy!^3OQa>IqU5LXpx9k7Wd_Qada9uUGadfY##} z4{F?d$l3jET}#K8vZg94E)COEOJyiw2-v3DBjFo_!Pi}4y5bV!x-Q;!hSG{m%75Mk z@1rER)n08Del)@n>+D-))c9-*V3BEST%pmRw3{+QqNb?S4~U6taLGQs`;e z5=6mJoaHbNw`ER>TZ>T^r0TFA>9w6%?YzN`A%;yl_-QbscRxr?DCDqKfGY1st+g_?$g*#EW1E)bhC`sn{6n1uB z)Kmv0LeY?4E{;@%IdZ9S$53|_%b+JsW6rN&CPGU3KLeJE7z1lcEeiT;~jK5S&W}=AN1?@_y9|WzC2lY?IICz}b zbkEbH-iV5Aw#t$Lw*`aHY!dxZIKFt|qjo+c!B_t0X}E@;j}>RjpJNutFH0<8mlwyN z+m{rl)D5Y{iEU6C%*rpY7fv!!+blPCgtLc#RHTTOnx@jG$~Nr zl_c~jAj}b=joTy+*?za=H&IT+L zc>A1eZ~TuSVs5yQ*Qmu=GOcV5ti9DU2Xy5RmL7dZ>NxJDgb`na=zX5C$M|t7_Tas+ z@=j}g;f_?{_pIH^aeYovWfh#4WLp-Ts?|c(!LM02>oe+TdaVI9t&=n2*c!l-WK3YO z`>g%uEs~KVNb&Uj{{XRB?B?<)vrM^!K3RM78qnimQlIgZk_^0vjV?8SMivVrUa?Gi zD!?l#RMO9=m4!^1%zL8ES1T)U>@x$;duYA)EtT>uJ~wf%xGvvIZ@E6zHMW+Sn*RWk zTK#fRQ2UcA_{m8C32Pp8ecaQ2rSze{YV^E6Y{G0lq@8>gAa^&+R!`+8dr9M~rNZsS zoO^JmyW6t*)&Q)?FI!$U4z~8vrBI{P31z8(2;2wZi}@{esoh%QUL48;R;mMG$MP(~ z?@NwjcGqsV%FwdSD`G_rwkEe7QIKu44OFEN0Ej?Z2e=KlR#P10`#uYURq+Nm+jjgr zF>$nWesg9*7|E~ntT{7oVy*uG!$gz+0A_-a=A~sokXS8ss;_m!i+O9Or>CK*94`jQ zd7qH2+*QCdmkSN5@TV#Z6*m^F&oN0%iMdl-TGP$8hMG#gB^7F903?9|O3H7tBF;(* zgQ}aduH!CZ=W)gP{{XjqKe`{~n`y;yJihR^Y}e{aI!6@B>L~Bl29o3J(rrHAQio8j zViS+%)rXX$AkA_TCDp#3oD(X?#vPaPJNC;(ySO~#w_D{${w{-&vHBd}6=`eN(`M<2 znrh~o1pfegRLLM}VS%@>0!Zc_qpzpfi8;F&sk3}|Z}!tqZr=3!$G(}ogP7$@pyF;_ z;eKtmRlb@lRo5EjOG;g;a-qP}NHiBV>0fQtk|flWW@HI5&F;3uD)u?2+5A^r;d-qB zjt)O4aXNwCDW$!nh^M#Otiy@%xu5Q9E;@Q*IWXL)3QqTDJnwQzbV! z&q#Yasgnw_H#(lL;;HWNrUOZ2V=L?a0WY`PCuDeS7`Wv9`BUb8f&fkt$G8>t<6Nf+ z=!>32 z&;Ft*?9h?#Z`z&AeM`BM2Q|%h@q*yJty(IZA=+Fc541I0NfMj48^blvxUdJ}(taW$ ze`-SG%`=?y>pq{gJ|5?|4J?VRji$z!YB|2(4LCwa)4Fd%ccC$$K65>gxrrT}6Dj=#= zQLE)tlpr;CD8PaOxo5s#KFDdSSyR)~y8kN^moc zbWWP}jT)LQJM81J3nwF8aLk*z%jHKNaPKTrUo8A-eW9ymRqM^Em=Dm>R#G7@R5pdB zw@$#>D?3t3cL~y8Ub-Px>S}_EsWbi^y*)o_yH<|RI2Vlg2a7QN=lJ&D!JMmvA^d^- z`lZ@h+@B}CeDkldY<|x`wbAR%dK2{L}8Y=ntXAHA@ABu-20+ry<|@3-vyd+LD}~=Zp{u0GGPbJoxf0I&am@LqR`_OY>vJ{Ok8Q2 zQXEAxRtXAjNtr5APLV?szHbB#Kpl4Xn(Oa`Q`Kv%T^z|UPf3xd7kBn@b+1(YQql`Ox zzOB_^7;7@6%Xtp1vI%C*fb3m^bANGn?yNpE1U4$0>8)MONTL3s;9diO6DFx zv0#>q<&TQ+XVkRgn8o2Mdk;&9N~GQ4Lu-x~hr(Ve8&x7D6n7Wg!hvh?_%?zL${FQ~ zj?!!w4p_0@dvwh>%a-nT;MJP{0Dl4`#yLpSN>YZNy(oWbiuj+l44Tu!{{Z`Yj^&uo_=?ydguy$Cl#`{n?XhH*_UWPOa4h=V!hj52b5rU`?^U| zm0g46Xro~So};q7B?}u@ zFBu=QEFriq)l6HZ6otk;#%FwKdc=7bjv5->NmFH1DQ#gUK&!%W zX>WzIiauZfB~ZIDcPzYx#Dz|AZW+lrpDCny%##T1!w#%*WagPuk5(n3lcK5ZG+SzF z++T;riS-Kerpso!P54H<7UvUy=OdftJNi*utTVNK)p6cLu+_V3CahZD#BK zDQQi{=XSQ?xbGa;65(>qhhz4ZT4I?15CYtMDNY@AG9KunZTSR}YP5~QvY-XDKThiv zqqk3{b{~1Rw#_Buoi4eDA>S$xhS^)c%@?|giWHE5of)A^4WJRCQX|Onvzac5J}ciW zG2$o%JQgJPF`m7aWLGf5{;#C*1;C<8mZkW6qth^!?QAQ~^D^^FLX1}OF)V0S)R|O1 zfTW<16*UbfIIwx5t87H36|Sw9ZM8LPOx~$>XJs`ij_I&FaH-Y%j4}N4DY$1;R?}#+-D(LIFjTfF>Qa=KQr_)D zWm=>jlazX<1&*FDU#jzbs|0U6x&=Sr)(T5iO$r;QN*Q3LQcR`*)1+)7XDJj}=bj1g zov__DM^q6omk`p@4!?;2#+{BOp=G8tku~YtD_Ty{6XIc1oiLoCuozJtWF_}N)lO>w zO!|~fN}oeHh?R*V8l-iARA&&9oyZbe^iiIZKe8DjQW6z@3LuFq_KI|?xYsKJk zl|oZ;sCuD-R0vXP94-Q#qEeCZj5N2_JjSlo4#;vK=-m`qb^1hiU1|sBIL54?c+3yi zC`j&XowJmVn*KkTsw&DppPa>m1urecYHaN^WT%t@kSFFuT3T4cH_B>*pEWdpFrsBs z_QhUY38-J91g5WN+ZHXrx0woik@H9ip^PMT@!(5?pN?YnmA9|(N z-7l3$6_qH0qc{fG+uKSU;a{;GlYQ;fWsN1S*z4iI`v}=ltH;tq9kkh%k-vmvG?q0; zQ&wEFTXS<4}8=&AYO{V#c1`%D4V)KGe~|I~U`8Lc_|9-P^(Gt=WYS zur(_yVi=d1d%`9ipE;WH9B;s9yRuAKYG%)I|O9)d8_TG?P``g zUhKM`1m=4uD|VgUCOzCnH;>huA1zwwaQvGYm5%Jv7go!PyN6m+i3x4QwjABLDLM|U z!dg!i=zTy9twjjX@ZZ>e#V>PYZQo#Gdr!szX(S!E=m)GT?c7HY;u$_K$5#95TP2{SfHlHAv&4YCicYabWq9J@H`}q7myT``hN8 zFXLWZuv&QofNh+)cDYz+D>2-j;?*;zje%8G)0D{-0GR~0HV|j%sdgoC)YxhoA1Ag=>SVT ztl}eQ;hhZ+>}bJ3&G1NZ_YOj*>J5RmNvG|H3a9W3Rl12v`@=C0Rh|jQ8976L@WE^Z ze+##483V`v04t+2{{Z4J?xTDZCmk?d4*vjbtC9Zz+jhY<{RGHcLR+XVl;EpLfbYOe zN6r&ypWWOwaDTOI#;T7csl_U7FWKrJ#N3l9e)DSo z0Hq2fP)dub=gDqH5CBMk zQ)-l*t4RK$mf;$oI2Pk%s^@lIrrXK$N&1Opgr}9+~Cyd28!l zf8YNAAXj50qA8U?PvVyN^w)*uP1G&T?N=iHY3;)mNp!sRowd<8XEXdFW!K0UkVITX z6=bpjqR*cCY-Q|f=J9WTq)7)^kux~4c%W4L)hfvLw{xJ#`zFEat|c!1S3^@svPhau zElSxRp)gIOh^h`_S_PUMm3nIJk<~X&XGuh&mPqJK&q%7uT~L*%#9LG>x9%4z{Ldv- zqMh$jo`XjZfmzaDWMJ;+<}1}0dJ#V{tRD3{O;XAcAZBGf2={3XP3nck?$uRv$L|)# z0aKN<&oqfjb6aI18FIo~L{2{8jFdR+zH)g370I~wDY{s4n@hIZ`e#=pEhGmJR(7c- zT7o>I#_}*eIS)3PucGl6aNI%*4O0rKNLWLOM)h@1x+`4qTC2DkMvGXud%7;`xp(RR z0FidH!1<-p&&oZj-P4uh7F!Y!FPSGC+;>t}P_eYB=T&H{c`s~fKisV;2`UjJ*LU{~ z5y0Xc2+(nE$8k+J_Ip$>vd6)4Cb$5yN`hQX1NQ{_o$EBMb^&PT5mfVyUu>9%4tI;r zxEB}Zmg`g1xGk4D+jq)QYpZ!~scELQlIort%FAU_msuIdr@|d=01^tmi^%dqDXyIO zKef%F8+v$s>oPOUYlv+5g$4uK_aoJFo_6asRoOOIky;KdYD?6~ONeUB+CrRKRISqW zW~~OXoVLE4&C1=@ic1@3!W|}!rwxAEANY7C$~nI~;e39!qbJ0hvU0a6(Y)K)gX<30 z+pTDLu(qiQTD=z?+aI+IH<_prZN1y58w-i6vhRIaOZkBj$*|nn;==W-NcgO9Wn&;(MszSmo}tqtZn55Sq!4tjb`?xJldP7L&=&{ zk`9D>AZBw7(GE2WGkMdBKPy#5RU2c{R_P=WIIxaNwL<8qvr8+tE|#(6fKWj*_81Om z(XqOz3eH?L2tgZ_JqNM^MWilnt!~$btwmd2tSN1;GD+3}%FD>j6R?pPCRCn;6(~=s z>SH3)%xbrC8U*;H9-App>Y^a{^$M<6YOFRmu+JZ44-==Pu|IhB#h9l^tlh)Et`%iW zl%fRSwJJNoS->v-PNj|-g?JEXaf6F z;jDI_k2qV1c>ax|)ixcBSm+Smf?kceq^O^Ii7@8ENJKUM3Q#pAMFqk@07_BaB=<;C z^@}8|#_o38^|&Ed3pGPER7rIK#{Fg-4kL0;Q8B29?robZIbE?uL2zPsFh}yvK)&X= zP4_XsIMQ!93yE^zY0>DjG`1Q62n5Ikqz=CLg3}VnV+?M=EZ47<)hpvp#>0xLM3QAO z7}%Ohz9xyg+^wZ`ynjmLjZJ896ex~^p@Nvy1Lh3`s!Msai*1_NN;PpBk)%HU-;@G{ z!(;@`KBUZw#iT^mo!fozz@FXL3KAp;R{v>ZM8~E7cMmG*{aXY1WCZGzm)L zB~GvSctqD9DjMI4Vn5Wt1s@XjmBZ=}4Ghn+Q6x!aKMIqrr7v5S+O<9(bX-Jg)tHbL zxkT623snvhBq#f0Gj=HDs;wDT%FxfEJI#*1Q( z#Pj!;Ln#f+lk6VY)k^Wns=zuI88lP}xSKs4(<6TSW@xQDieHfUOtqMlW3pjyuu_oP z6<5L(l>&SYqAEICEiXD*rgL&f>H8uTAxi9x`hhd$9F0<62tHa#1uQg(UCyjk15t?YXlt!Fdjm87Ne(e){))<;(ql1AbE zSc@f`x3zC8HF_r#g}J?VG@3PMmD?}KGV85f>iKbUvc9QmTGpauCTAczNJpC9Up{Bm z#d>|MjB!WG(3GeuSJlE+qtL=9tugv!_j{3cr`*OJmr~r-PhhU8r=`d#3W!4K+;T*n ze{`9Mua+7*pJR6gzvy zN~yR10B<)XcMiPO*4mo%l}zAEs+@-OZRss#I7=?mh};iSOuu~=A^ji&{eT+7Zp zz0X-iMqA8$jd$d0b9P;#V)pb8(mLjtRlUE}U@a2AfG|pFwP}bnMBe~{j%Q*vf#_MhwjlShv`!u+Mi^wRX zc~owC<-G!3Xe9)bByG_o3M%JD;&k0$rWtj4mr(MXTTU&sl_5h=PNW}nB+j8$(Ct)< ze63rTYPXVh`idq;p*=c8jFUvy?vUMe!y*q;`k}6MQ=<#E)$J}MSHRExz#Nd zRxsu67~W9g0zS<`-62mnTyXw1l1U`^#O{Th0vGC}tw~n@0JFS-%RehoG}GHs%aPTDvt++pUZUw2)-a_vS7$lUK--9nlC?dvVX6&deamgKahOU7Y3 z$FXCq+jVHr?hECv^1F40MaO|E!Ct%R$s1y>kXdQ#5?fel!bnv1$QepeV~_1v!|PRy z%lGV^9$Id`5<9fL-Y$L1^NsrHUaFgo?;)(Hs#=VdHzaoZ01^OsL7581?oq*BcF*-- z=R8u6YRFB;KYcYx52xha0SBg}MQrEcTAM;S)%UEzv8P=Ne$P(q{7XqctVKY13dqDN zlHS2_J+c-$f3dWW*$?WZF@Tds35Y1Hn-d4#fe;zWk3?;p6A}p^SZSr;eKf~?K~e~U zJgh?&r>1%4?3C@Tq$r_5w9a5ptS~**T-el+!uERw%Mzrjx>%_04mzd!i+Y*22~P3= zJHdo|%~XIH(=OFpylL_p}l)hE?RIICa7c8d-}acTDa1|LEmLuh6xjj1 zQiK7}3U>bhq8?^)afl8HVwa9F6%e20Y7_u8BAJ>00L_TqaNw1&H0~3^(Z{&U53;7Q z+E8ckpO$DHeaVNm;lqk!ZQ8zGf8IQ+skZWGY&gR-;)2slj${?>(YA!P+s`|5rC_+E zqzRNI8$mq*jn8j9n8`k`-my98D=5)qZ)|+cV!l>exZ|A}xaJ&Wz*A|s(71zfTaOeV*T2M^CZXb+2;pVRP!GiS{Co-7aM&d zh5FFiek2RMr6ANWvVxPiAfV1-LM~#uVKgR%WN&Tck5TatPfy$xWVKM&H~t>zcP41G$`XIOC?FKdXGukbZBEGuF z*l98f>rI8{?zNdEo|>)ZDDF+$EH#fbnx}Ax32e65OvU3ItP!Cja7p=VQ;Z{wd*S@sp6IJTk5ixXsYPs zlThPLgYf}1tRiB55LA!lZB=|w^FE5<@|JSvY?K#^b&%5XM;c`=vNK*O$pN@nZgkEe zD5(o+&B2AZl2RN>OpK>sF12H4=7LF~s9MSM_{r;Y{rm=mT z%?`JBA>z~Nr`b`!EYIeiKIC2&zT7xk{{Y7o{4;U7CV917cEaIGbjQUE<`6lo~R$DbQ zqy&{cp>_2G@Gyz3W?ccCE99fOc%nwJ^PK=ro;ruwvjI>u#~AKI~IaGcBn%IxhamA9SKO8r56lxI-9##c<}IT-ISD9)7q4!xvcxlRIb@&@`Ntd=J={}S?paWY|O+7yEu_^ZZ#N1O)+@Av1RPx0wg6VC|aaV0w z9c`+znU?Ae?vy19S{ALotjl_mJ|R6IEYpiEoOx@ZS3R~QHzBQY)QbA(Yx|Q&yDWL! zZ@V7lt(C2}OLnEsvY(qvX!O$Hdh+|}b5y2bX;@VKu{IIx`bN9NATsc{UcIMqPv5Bf zih|KqMwK>#@Nuw6+q4N0?(&NVN+_#bz=~H~Dd|$$(AX;=fFxBgBs_el0Z3Jqyh4d7 zH%ICjT2S1=das})3Cx&)lv@oH@!I1lyK)w1huj!VMHeFA*|m|kP?GPZUsGgqvV_S2 zL?2$2GSjY|U^|J36&@eKXp@g2JH*Fj#8qdx?|i6le0-&LKAwr+gz~F zZj{WbFP|udBzdHmM_t5rDtFH4jR@D;6#I?#6>O$Wg1?b@($eDVvQ{x`wi$!l>$4lJ zE#>w;VY)}F2g+ON7V2aJuQ*#tI+Nl7yKdf|pK5>95-Y>_)6+$M`1^`D$A&o1Cb?pH zZ6{^b*88=_lDC(0J1sVK%hh$(MKle%*J^DGw$L+Dmjc>(A=#vZuJEB&=;wQ=&`9+3 z@m$-eqoa<#-af50KWDTB66SsUS?~{gO>$=;U)*;f%S0v(P!Aqa@@}& zW{#c8yQS+Qak_eTLhCL!Yn!J;HVcY+qM>A;g(Gp-K^E?G5RGX-s#zSBpd^#+Cr`Ys z4&si;F#KAs-;no($cKc~@>;91aGfIt=g1ef%YsSyCQ6e++T;OUNd`xvWjL>HR_WZakDtXcr$xg#$f=Xj z+S{7;DGn}kDFrD9P2@!JcBDd1t;@OJ;C2dzY~97fdNz&ZkBQ=aJqjO=yS1~uXI%xC z4Wi4qgD6{zqqpCX`&I6?R9kd9?IO>mP=$%AMN%?8F>^JWXJr_9Y2dO^Nb3pz0ICat zRvOXU1~tu=pU*fGDx|KwsXOa<(&>TWN$l#_D`hEJiaT? z?7MO-gnahS1>Opp@^=_#SQXBz9Ln)({66Z(7miM z6;FAAzOty5geZ>NVScssv0H9gCSMxDQjN0fAFtqYed z+P0PYTYa(DOSTfH(JxxMkz-{+NA3j)4Pv7I025O&ha_S#N4p+Zo4hmkfEf>hi1WOJ!BF z_^md=t*cnmN8gbE{^~uZJ-SB^U<>EaT+jn`aZ**QgR?9zx4gfe7{O@yJCv@JG|J$v z9jUm|&@C<2RT@K*SE8-fLg`p3QmNV}_^K1}67&1c;x{*92y|%qo%@%h*!IUp*RL@6 zh|qd^e(cA}dt2nrL%UY;HzafYrk9s44TkPjx5~?lwQVUHhC!sHahBRl$V+XFgTO`5 zZks0CUN9OGo)=!+Ypb{>%V)fC#wnBXjk>Ahb>{p#A!!d(Py_rVRog1jO*b~3RFDZe z%UM7ViPiNhcU>@Q2(PCV0^z-;^F_9zsdZyV{!_1<3Cv&YzQ}8`vIto!ZObMwP z_^CegW9B|WDX=F-eBpG$- zC=Ip(s?_D&28zaWMXueJ{VXmt7TRByd$i79rK+wcjd>bbRZ=p#7=;Kt+|44FD$>> zYZ!9cU!ttHx*VmfX-jGLT}jbxxPp}>N$?=R1i0`;HLMEh;5_NV8I_*dV(x#8>oO9B zAr(BEi`6w}$*Gx~y;stp7_2Cq{xB3}BREMeOikG&D5+W zj<*)0ZnZSPd<)-4RuHwH9S6Dqc4^~+vlw#b@4D8{rABM?&EyX8`11?MExAr=yh{eo zGR%gfFBbN(f1Ac>=t?C=Na<6hEL6I*2SG?uW_1E7GhNR!ni|tZpzUtVh9?)h;#3yd ze&P$JRa2Vc6w4Hu4Ru|0u-7SWKTkzdSnW+KKvZgkw#v|irdmo-ou4Hmn?kpC`1rZB z6jz4N%XS(&a}750Av*-gAu`fOq*O@aRm5y-cqng_Io`oZZ>m!_CeXJ51+=ITCOYXI z9At!ZDAEeLJR{x>Hz4+zf#Fu$!w%MQoxQwzrrk{D(9$jGC}foRt)i6}^30XAkls}4 z30~2RzKY_?po)N~o4MGtZqk01>{%p5h*1!^LwMWy5vx) zXXP3xl1`cdq=YM^stt|RFXQ%Q)unB!RJ0cMi`NSBCe<}M2gKsY)7U)Dkl0%1x`7ic zt&O!Rp>yXeqW=I^?k|Q1Etpjrgpi}P`-6!Zy--u;e+qg58#^pu02@H$zhiY+tK6ZL z^cyN|b5HnnIVqJALGqMYHZ4$ut2Kb&${a0Wy}ZWc-D>LJK`EEQ7wLoU_+!<3YTSsI zo#$>xR)`yIw{1%)NfbD4#EU6U|i+YFSC$PrIbYPqHB5iA@U0ZdmE8a(i1)k(~p7 z*$q>KlTau2L~?AAAyyfMd%tW#l{YGc$GQfYwJAYKsQ6R|NuP!X;E7I4eNM%JIeEiU zkex4|n4}Wwkq&F9xF&T_PpK(rB5;r;yE?)h+DyqJMt*QPN)wyAXU zk3IhY1!J6CUAiE56_#1TPx6+paCS&?QtA?A8N64O@zw>v^qS z(cph#D$(Pr!gQmWeZ&>^-}T(ED*pi4J`t6)3!jqY*!6DUCK8(V7ffb+2k}IBXP47| z-Z{QCUbp5Ym;I|<;YIOh-aAiUIC}$4ysa?R%`wq4nCikz{rjoa~(^)rn$;}w*ubLJN_ zH2f(YJ+oOOZxE>4IR}0!V)}0O$;}iv?+erOPaG!UMS#>goPyPp?5{wlRa>fCiD0;( zwJa#D1k8ifctka~4Ag3-zYtx~%)@)Z7ZaDWKQPbKU{{R;L63v#H zkxHmTU?sGv#)3kUJ{bhbUdYV}{yNcWS!&T8D^;GE{{R*Db(Hg0D%9c@1<`P0cUn82 z4&QDUOMS7%m}Qm}^*WfQDw3o>l2stE+gUqp4Mg-&t1p=bEyn zxi^X3H(O>)Xr{Mbo@SA3E}puD*FfBr07{mH4~kw9yX=D_UC8z49scNE1LIk>*&a1h z#aug{*sBogYHyhf*Cl$5mejJk25G{C<P%05LyD zP~|;+opuY24Qp#!9c9WJ{WJT}q!3b|Cn64!obLMl1F6GbcAm{88WN;nraRCgM<8R;m zeK%g4oa~Ev>iE7FcyK?5rptS94n)B@Qw_G~{Bx31WmwJ763ulvC{s%xiKp6fOJ&sl z^J@V@JzCc#6~lR>H4Dn`vGZFzvOz);>aPqTN(yxe+t*(RI2xenxJB-tQkO)$=lw9H zavc}ONRVGo9U)XQ6nFEM(v<%I5E=MPM3IEOL~LFe4J?v1Nzf72B@4QVqg#1t4Ja43 z?wDMsG>;gbH|mCX(No@aW16n{O$oqw>lMoJHBc~zuNTL^$2U>xIu_Cp0VS}Km311E z8Eo~ZQhKeX7Uhh9gIfLG`u^n<(((TQKD^R>d`FM$^VKZ;x2iywS?&^wu?H$9Tkuk_ zw*Pbcg>|!UGoiQU%gFZrErP=0GzMc3(#sC>w@Vx zKQ&XO#Va(kva|!~mT~)OlBMLK(h%7MK)z{F)qQ_?cB+>fwB>d;=n{SfuO7CgXY7)p zhQ(OLeH}Ra(~}hBKJ!_sqoTjuEj3tuW5{bKbru_AFCvX!-V{9BVNL1cHG(nL{IZ2# zdve!CXq_C1k_ws%SyTmjRdmHCbo35^;A3#|u|c$QYt>#0h*r}tGd`jik~H6_yZl-& zPkeHf5mn$%@sakz61fRWc-rbDy$4eb<_cES1u9V8zHc75vXyRA1((}N{FTk;wftX{kJtEE4?wComG~SBi zwB1XvJbV@nzh7y2)j3UQ#_2-IDPgPicGL;<8`IE7i5OfLvKYY#jZJO1i92h@+#{h~ z=D43es1Yp+i>4AfuC^A{BVw?*FBDy_GyP&5BXT3A5DU9l2%9)F6%+hUF_K+cA7F=c zAK;?qXh}6!+fodN9X=AB0ZvVDP%{4j))O7je}YFoI%-ixW%LMCo4{&PWzb(9b5FF# zbY$>Re@pq1MP_Wz)BZ+tl)E;aEd<{waRBNAQGH)xSTu z2ry$VTr_D>oQ(KVD=I6oE9zuav^MDq@{&mrHzzjAy}_YZ zUfeUcY@Lu^=o07ni+$qjNQV4KcBHIJ-MR{em2Fjd{_PblzupwpJwnJTt*n|_GU!#* z#xE;y(CQWGI7kU@gJl#B0K#voHw&XKJ6TkBy!WV}N|mhRqQ^#CRV>*_SpHCrv)cC@ zFICgHul_=jB#DDAk*H+Na>m@m==4&hC9KpGZISlMaUlx3oToE3 zm6DZue-G~oYGvgdm}sJ|WbMlX=DP!y?3U7jUX+nVc0dTJWm3?kU{CCd?3OAkhS9@S zXNT???4An_ix2tNvmR37C+jQi^iE{>yJ{+KzLg4b)|(wYL?ldok>;6R)|wDY1w!`@ z-SYz0=!U=Ha)S6DpScgUo@T+h*NtkwEzJ1#v({pr@V}L>r*S2ksv5)s3YdMh6fHeL zl83~R82fwKET-M_XI$f7O>W0| zlK%jIsJU8d(kp1owU(NNvub5kC@D%vNkcn;kP;segI(Mc-Q9DVY7I5xr3E$)ca)lSR5n7J{940pG94?SM|@HMsRJ1MY}=jU4<_jLt}mRcMd|t|Qe2pkO!SwwaTA|Z;}8lV$+-&J(hIC@LZ>wyJT!=? zLYa{YDA63PX{&$as}b-l>{9$SGKo6nM&@`Xwnt29mM#{zR?t5aE}=wwbd*MA@m$f! zowz@AoS}d%6yoN)rMi+;i+-ArQib|bz9y(|z8I1JW(P;Zt3f0Q8+tLE|_-Pao9afIyr@9gRB#HEq9>}tf zWm5C1pJ+da#dnuw@VOvF{7#U0Z}bYHcmDt=D$tIq_x{+=hE;1EeiS+r(NNkU)#U9T zYp!Gz>mT-e$F^EV&keH3=e}iN=-%{mnnOLhkd(_vzK7;nyO(?@o?C3&EwL=^zh&O(v z`B|kP6LBvIbp8^d)NhJWcj{j)nEn3%7HXll4b8G9KX?wYfsLZ8U2)74U${)R$8g3d zZM0R0dD@3k64FRML!gOTDIlVy$dcFzRqK_pC#|?nH*+kImrw$gh`_ zXZqLDMo$u|`=7&?jN=qa`T+s7$g9J^Luj1c(RF?!2#Ey>o`O-yLRO(a5Si;EI3gX? zG8Cb3+^Th-XJ2#^C=^gz?o$Qv@hvm<2&E+&#-gn&YrgJ+B#B8Pld;FcpTkhlqzU_C z*3wFL*W_7l2^A;dOuhnrutk)? zj@1d0w=We6bi8sU%HqN(?ar1MDfr}MRqWC%rMNF@&&6axsYI#i8n{q6&0ojrq|S3X zk=6lpb=@5w!gN)8FUOs-TpkKf@K&N*KFP&Zz@b_@Nd*&H+?VR=S)-(BPgFjjmRd+8 zsYJ*L4zUn`zd)sy<*~M-x$27b4@K;%>vqC zyCxWD>3%0FxlGd3;@x?|#Vnw{3s;4?<(oOIh#Qe~m=iK(KE zt~E|NXCy?S-9$xGn%7Y4H4Id;t&TLHw4R1(Gls6icrN0BnZi;sA zU_fP(;^w8akODfNvNc_~f6DGcvVGHx>NlJ%QF`KCR^-fstZr(&obHt^HKm%yoGO?H z;s?qJ;G&IM#a6qQagEZ?Xp(L;>?{HViQF{%r4`oqB0GRjinzG5>)Q~%JAR|K`>JxQ zt9O*C71T>oM9PUWQS<2*UDV*_R4BU*rZ*N9i`N&NLY1_c52xC-06@gbU15K9#om67w$!I@-cig%2VXFhnR(NWE^B zh5D*Ts%aU0$`ysuy0*}zG^vBR1widHNYr@2;522sU|q77#E@Ti4s)DjDd+niF#;K^*96tAGpMo zOUQvW4FZ$kI+7q)RXdl|jx$i!g(?&96O=0>aZ5m^t^&NbYvmhlO5QCY3^-;7h*}7V zq=b?YHq-Jixa@uO)LwF1q6n3CDUa5Le^~kwC#jEC?SJf1eADK*=Deyv#V(Crt~JOm zdchO>BV!olUK5dhLiuy=b&+iDdz7is@CPeHib&H{wl)0`p3D=@y^pe;{{Uza-px&5Jm$65T4gj4N|}GH9E|COV|AvdtF5!Z4=*vRXi$? zj)86@4|0q(`>77f!_kQp0i@;nVG2T~EDJAt3cIm(W}oJ^0lA6o-S&WEx?I{QrMMA_ zt3yNG^_}ES&ONJxqAG~sx}_=>D#FV zDfFCBkaAl1h&IazXKyhyRM}4J7Ov%`RbROMX*mv*pXGkutn}rRHVww{SoWi&#XUQJ zt}UbOHl5t5zt0KzSg|YWRowpoF8x3`D~m6=e;-=$o7NLPJlm|Wx6)o$!~p>f%|x9%;>tUK#+o3nn(5v>N>}Er%RB9tp0e7@I&XYyo#R)U z+ABAt>$c0Q6s)?WvFgZeb*B?01UQv8phUrfEvIXSs2hs|w-UE&v(DEpzf(Tf7tS5a zH)+ChOO^Jzi;gpEEKa-bNK#R_Xk#Uk??YujD|HJ= zXXR9P(DT3HaoV0Af2V;!U%rC2H=lKOZs;DOWT=)|dl+rqgUfSOt zhjisJ17!Tg7ZLK(n$=iXc_?5eujT|(WJfIeLgTq!NqWCj_bGR4^J==9mgzs1Xi8f7 znuXL9g^J)#-SPyEf+ugAbG3oIqJaVb0P_88TwGr5O?Z9^N8^8{CO^ksm!Z!$3)i5a zwh;A^hQLrDiS4LRFvI5TmZOs=7&dYu&YlvMWS z3Kmc`AiUv}{B(tb&Pij?HAw#e_0HZ?tc6z;{UJF1mZ1Jk&lxS51yg6`s4a}R%Fb7SOWj_UXQQIBj@x_!}MajI%?>X40 zDgIvFe9UqCeaiVn6QUH&!jal%Hy~)0FLJchn9sA!pvgUzr25T2 za0!u~b_0p>4;JIxME5OKt4{{Yu3 zasL1{-*@1Vp87H4B`XQ;V&bO{;VQ)Fr8E9*d`QnZq#uxaeW@S*xxq~(cis3Vl}FJ* zg7I~Z=Tt+J{tEFS<>FCtf}7_z{)8w00Lz;{>YuqsT!Hk29A}GOxfdetR+f_y#Sq<9 z-s~}#%k-NbKxx*3ZVEfn=F*936)8o%znSLr^qR_&s;+#IW;k`7E{)gRtKM1L{{Uzl zi9>pGF7MYRzH*iRy>%BHis{A|+N#=z98y6+G@&8c$jF6tKV)fP%3cQ0MgUlQ{?^C3 zQt>e^_0#YQuvPY#+nzODQ%8~a2b|P4$RVqp4-#LSqjf|~DGpVZ?tw9{kBKQ?quXw>+kusG(uxGE|j-;7;Wfl1u}X>%hS0xavm9*o%v4-dGw9 zE9tMNtxEhc$((VJ=6r9$XSy0}BO=YO^c1xiCk1iEn;W&=E}_ZNur61+dy>5sIFy3m zc>s?fOhU89R= zYN#qImf^Ybo%Xr=)>m?I#cNQyilr$mtzAygqy4Cs<1RqukKt%mc72k<{-np5$8`m} zUghecRrhP!?sab+&$#Q0TZ>l zXQ%P;Ry(;|cbGdlr&v+iK#*}EQ$oFkB-`S$ z5M(0w#=Jq`yxQjnK0bQH;@<@?ad*~@%6ly~56--KLEHus$EM%O--Az9({z|-vBbHk z15NYyh|T1C3OB!=-NrNO*=}rn!?}r^mn{+xTDsg_$RAH`?5HpIOd}i2yLPCuFt@MS zHEq4x+%|#0f4W_JPCZN)Ga%AkO24xdA61yJe@>!b+v)CCUrC6`!_=os$J1|dQ07;Z z_OpXkR5NSl8tmXwElsrLHG%p%XZC1R+tp8*rCtYYZESAuCTK2aDOkLr3dB?vl$L-udlXlI+eat<;S6Vk3q`2};w;H+KMM4m{)cQ3P$w+Y2 z08*!rxug@cjU&!6eVvy2!FN1-;F^F>?p~{#^AiZ`1CO(LjDbSnKVc$=?ER}MDb?8U zEE}JGh^&1EZdHG?{FNY(XE~{Aoczq=*(2^8s1$4z9-_P|PT9q7>yBgMRH2RX2h%2# zE}*Qxcfz}+sOg4}A3)~%P8)P@Q2 z`Gk^>w8}Lww~%qeY6TRq7D$VbmWGwhD%^Gh|^K zD%o+@4LoX@yR8o0tO{kbO;qcDg>ENissLzU zbBwhIn~ItYoJ`mn(eV75fOs5yx1U zE^#Ly+3GSL7U$|KRkM~XX>FlWx@#?B>nR_jc}Sa2wx<*~>!J1t$z{B}w7if=9Y+PP zzQfED;eEJ*S;V6QDf*@U$XKGUWk#TMRhvW@QDBxme7Ap2G1?&u?1W^&skmh zNcX5SEvouVV-2b2IDb6IOF)v#*Y}y+Qa`a!p1#2ap2W3atBRiA&8x{>NZLt0-`=vP zydLL!N_Pay@eTy-4ug%ZccxjVxANuA?_Y4fTfHiTIIQi9rwr=pIL;ee#=x`2!xoUj;uhtdA}>+eHvzD+h5(eN`*iBA)ItNGksT7%0LOV9R;i zlB^51y+wabxsZmE%MPuywss^501;i;o(-`5IZU=`qg-`JYl=L18b^^iYmX=Gg2$_aJ)4AQEv7^r%CX=~|fs2n={ zTYho2&%RMSF;S(|)7Pi%Px^v?hQF~r`)}@|{lWSBm#Tb*p034y2xlm@J6_mgr3oaZ z!@0vJ5jh1910xjK3{+DOm{-~rjw2;4ySUMPcyl5<9HqEX^O9FCW6O z7V6749HHBzR|1wBKN8udq1>gYj)_u{?UZvNou4w5xqD}7)av@s_T$6dk@kbYJR`(5 z^|z989!FDI$XU%nO0M_HYc;Jz`pW`HXXYPlrKCwI0#fT5{?xW}+~RmFPJZmY-lg(e zeL~%++P@NJN!B(pXjK$JGw<{tEn`hi+4`zbKt97M{+N;&fpT4=f-rF3L&GF)FV%z-GKibXQ#>>*YeV)l2=xxZO_}@!is% zMzYxbRV`cT{9co(ONj^Ep`=R&$P3d~s(Ws&TB;C|pDC(j!IeX`v{EfKLY!oYgDp~@ zY#=ILm32s!t06#>)Iz6f5ZkAZ|WB zP&>Cuk(@hJlh#l)o2i&;lqpt0PO{gmHAtRRPkN1%D9n-ePEv$ikn3A1d2=4|?2cT! zwsFGD7DMMr+H3cXoNN;AiqYZ>E{{Tu<6R7o4eWzLY zMPo8hwCYt8@pC-vRB?6BF#_!Ba!O`KzY%dFECyyD5QKILYV4C0$-H?)pHbYhnFvo; z0`~w6O5WnPZxoL0tCPV>I6g(inX-!({!F*KT-#2(Xi~D`{T4>|7qtVT2UFn>cXQj) zp(8s|Org$iXCbn1#jd+E$EdwZE9P>BK2p<2qCGbh+x2Q`FDVI7R@RAEPLmN& zcidunh6<}0`(4(drd>T>!|~mHlXtWAnEGIIUuwBWZWo-_Hg>w_XSdT+W)!#`?M&OQ z_QW#vJNxR}WGic}3GJ95nDSgE10iX8BqD;oBahm*-O1cG7xp43iRSf!KWJYloQceR zzvVX8ESBC<{MLe<;NYox({Z6#+b{`kp&A(-Kqftb?CLkxyn-uPBiS=C+^2x8wMhyM$8eY(#R;XX%Gi3D3Y%yzpdcSPDP3V=u!c8P zx+>{zuHU1EiP1yK7zIl|CvVAV9W`P|Np-zcq2VJD-a^x=w6J`DY@iPLES8GS&UyZ} zj0~!Se-h zpCi`e_T!Mbsgoi>a)js8}I8_kR(EquLIUG<}JWxwLEIcO;bIvG$L`ht{lJ z<~B~!{n5(Kq*eA2>_3e{tjpsb@jjlHgmS(*mNH&1x|Z6yW)?c6-tjA|1!N(ciWxiU zoFZFR;!vcMB&_NV65}0vjg+1qBT#+3NAA;7y(-rwkITo(K5qk|9?l2Z$G~p2@3O1q z{{ZSikx^iMx7B0z{${AB4_tU_B*>Vu+|Q<&yzK+kO{o>M&7P}o_<#|)0O=Y_9`A#a zvJwd4$J(+vZqc}n)Q*I3JU?Ng)|2!|`kXX?p~v{ctB~G?^C}Y~<@`ny1b>}bdi~$U z20LEwzY*!Yk>Dg?!RC!s2|H3xvJQv4Y~(a9p+L#omn;>feak-|$geI_Dy5nV zn#y9G$#u3|P&#-JCF2*d+=8o}7b?qM?Jj(YbL6TTymKeG%vOTY5aI8@ z{WY(G9lYb1lQ20Ry(7riIsyCD9rbr94V|grjNfc!?{bEoW9yFwPjJ^m1mbreOkNlsqQn(85jRMcGEw<}cu z4^ni6>Mlw&)k`WL5%{`DM|BdAist;9?;qlI*Lr0o%Zr;Bprl@@b;T-jmyCw7TYY=b z72=Du$9+S&b_()#zs!7D+=eG{;@s=fN-~NEC^9Tcxz9mWOgNB-11n0BNIgMG1bYly zTHZl<31ovpH3et#47#$MIy0!^6#oEG^wI4)nITksWk#ky?2x1-(=U6IC+&cbYuQqq zqKZ@A`6mzVIDMreyY%YqQH7NrFQ5fF$g-Jf?CA>L+p})X3C-^;k^cag?|Di0Uda1d z#%=YNCwPL>QBOkJoOk>JA9HF!CP_+3>E#o}g|h?Y2_4;;6aN4)-twUu3^%feTnV>v z&N}Lek_oi8{{Tq>Q82{WjtF<>GH!3qPuisFUJ&j5#*pnV4r6LDtSAG?~96e+4z5mDZ`Gp#W>wF6v--fp?0bk(%MIfTG9XlU1tw|GR`rm2xeuk z9%vj&6kNM_wV;b-(-cdz`Ywu$+m_-~4%r6Tnb5#-$7D``6k(eFpA}b&o3LWfMD6~t!>%N>HwQ*iNDB7B7_$_@Ccda4kmI+87&nnN*~`7u_N9# z#a+LBVhNy;t4n|6uXaMtXIs1vVL{XKXAWFkLOe^ZzX-V5TrAQ0Y2+uTjDcW95-Hs316?brJ zx8z)-X1d?4xVf*kxqWtm*izf5Wg#Fao$~~zpgL(6w%%Y}w#drp^$R(F*butsm9F>) z6&C$NKAe4#b_3a_Lz-K1`>mdqj^jkl6IV;6I>mi`)~PJWjg8FA&O!<7zWltsxarIQ zX0<*FpS-&|3q}KMdF1fje2L_lml)UAR@T|Fnp(SiN>M|ex+D|3!bSn^+~h(}fYys^ z+1VXietMm?g0uy6Bz2LLYkO-s9ozzkP_n(#gp$r` zxM)WCBl!AEp*P(o3?8`?2GvGoB7r*4#aN zmMrP1OF(K{0!#f9fwLB-NHpM+2YJ%Eq1dV;!3qkq)KV1^q$*-vCH6&L8KgFcb)wFS zx}kB*8HQVlC91r3^-*elwE~5-vYE@I#-ckrdn8S(cTwRnaNHU^XqC7-zOGxwZglin zZMiwslW;{zYzAxFM?zA63e3C+9{3*(+_RHMYNpS2J*O`-4=G!F!(6F>b1j({JWA#s zsAwni3q!y)S4^TOCmUYBY^5HD4B_nsc-8%cki3oxYF&XVOu}SOgtUpq>ZVkMcf=E1 z(%E*MFE-$kf`Ux`@dm}pP>+$#)M%)$I(84o9H&oLP`fa)-6|%p%;^WF;Bbzz8WH9U zQ)W9p*7dX^g=sLJVc+832z5t+Sn_R+XV?3QF5l!2pRE@!ZdrcUJ!Z*{KWY9@+Ds8+gzz2xI+wE49YoSdXdS729nV z{NE0+a=qUjr$r^NEY0}+RhQJ$(~=oV>!|)wP826i$^}GzB23IdXK{NQd1Q=|IMZLZ z&?}4ETph!?qm%%A9x3kM?^OGhyQl6$p1GyY;JF&xQ~uJLa-G{tma9Eo9k$k*MFq0m z6);P_no_B&RVb2XPe2E}edNdFG)3E6Vxnyr_JQjmJ#8J-cSA>Am|pGp<<{Yn@&?0^ z-_v8Oa;l}oHW>?=SS-eokr>FPYWkvozrB_2Wj}h%PlLT zaw=|hl(cD){?7HQ^iC~pJhsTo`DyJ`*Pd_TVf^*>YxpY#iv2G65}{30=bVK@U|@h< zptiQEx6|zP9omoiF|n}nhDMsm<%!7o{{U_UA!SdA^!@7Y@#k%}E+NOon!$9bxHQ=+ zex{_PxV4_9NdN)t5$QOd-esdXLh>vB05K)Lo0;N?Ub3azV~Sv`pr67Ii0R`NZ-gx+ zrO=g?34@d5HLf8^Wz`_1Bx$JW15CwtYARJ-$}$VBD~^RcvI+4fQk>u^9IfMsCSzIc z*KejDR?v0S>nI8GlBel}pscm`+eq~!wGX}%sGB1wk}9gzR1_AY@qcTt-wdcJtt2L8 zbqkHCs2R-9bo7K$vFd2TYL|Spy=#42pJaayXZK|e2ShCTIY+nJby3y1Ou0?9onkT+ zu5ym-KKry)C7g;IS~+|)tKWOO0Eu}Zp@?+Gwv}ps49;mFD{Ct z_eoG!%5Tx3Q)=5z%V6>QTPD= z0FEV5E3et}%T`5qr_d$V2p-vM8f-OdMUl?2SQ_u(@-4Bjtrp-0Uhx_jzGat`d@97+ zGF+>Q`InCQcL%}8BjnfxQm!-@y1i5Ds8hJYrddOZ042g_w8}1`x9SRRvH3gE2?n$a zLutOSobuX_=DnB?Urp_rit#V?s35lB3%AiwN!?tB;dno9@Ja{kCCP~&A7>1p{{UC! zs{a7|LLdGxJbgXtKl4evN5A+WfB12Hn!QJ7s3dtiR;hpYghT!^JX6PcPvSlQ0KpVL zSZ+@r-gcr{Kh;_*59_575BS(mJ>7CYF!!t=!+UtnvU*19nOfLsU`Pb3_Q##sW8C3Y zvhKe#ZEYmws#>M(uPUR-?9}&EuBEiR*+@IQ6g1W?Bg7E!uz8<1pq|yuZuD0dx$YEI zw}KN?DAcFz&7nw8Ot#1kV*dayZXa*#UO(k8a+q^Qr$-fvPbX+vR3yI?^^pr|f~{pgI0j)-r-|-q(P(Qu zhG0BJ#g|-*1))|&$PQ90P~0lEJf@ynBox>JLPX9SE(>G$cTv6q_k!c1`Kad(DdtQ| zB)Hx=I+m`cs;T!k+?-OJLHLY-C-8ybBg-Bmxj$Wo>uD>SLgPsP#^IW1AXq|3%7>Ll2!_t`fvRdL58 zTB^}fS*CHsA>{6mDJqwoNeuk21$6BXjD_^xd#u6jmYO=c`oy8f8%Y4lM9W_o^?NBq ztOa;3SahWzDBkDa?;hJ0Qq%sa9SHV&rw4%1#ZsH6@Ck1k`mtkJW6E}l*5$>eq=#ve zKq~O^gfT>SQZpxV-Ga?N@%xCyd!oGy%`PUKeNB~aKMwZ^*W#C5A@^mHHi}GD#PQ3_ zc&yL9aovH{xDeF6k(Qer-v#I}cLa(&s^d&?JA;W)C-;GX9gAW-X5M(F``lxX5LA^o zkVlD}q6smLJQU2$zMG`CxW3E+y(+bWK$#wJB-qw~iiqHL~w6iwr4!?wW{)iLk zLYQZbHC?sDFDx0pQaXY}KlI?Z=fL2U7~z$;4C>|6;*?7sC~lYhlz1hoP7Q6<5AkYB z$nca9F=C20cQq30eZEsIAxpZsFY17g+c@viR8Q_eIHf9n-mSoeF6Q!xKiU3>m-JM_ z-09nG;ahE;#Y@s>K-@N8E#qK9!{?Rchv9C?PS5n}?bh{c zX7fdL%NK2^N2t|O*lj3MWiV!1)^RcN?l3D=5cAsg#(|09fBJ9swZ@oDcO>$arz)qy zEscjOt{$tcO$Jg>6zB@pei6>>SlZ5I7C+A1mBq+~qJ*fE1;+l6H(TYt!p;MlU23Y9 z5Z!C9-MuoQ)T`8Z06xggj0-EG^po6KAS)u&%xAD~q$(&V@>-f~lGa{pac-Wvu?iXm z5vqxtEVv>?08?9cxNE*5ro6AaZrkw8g6P&IeyBz!&N7PywKeDbTAimGT3XMKNfRi& z!&^EA(aRm)nHJwXIlHLix1Q$sft%gUypJqufr_xBOJII_{^@hug{rAF*4p2ec9yjOgt!*SP$GW#rOA?9y*IMkOKK#Z%4!2#fqH+_ z-ddtpU+p*|qT))0uVZRzr@lz-D+lC^=KG)CGrX>z@q8%ts2jMlsybvL0SRuMr->Z+BX{N7h_>=4w#2sbp$qlq3(ZM4|LU5m!}7 zc3TC%7@$oBwF*T(4Bd_AKZUQoVDKB^gB3uW$yB4dd$s(vhEj-0mBV~knnO4rTPAug_@1W1P#rO1$a z2=ps=69DJkd8NEBkxiqChh%XtHma(aAz&vdFLq1KekA(!hPS?bj*EV%mSWhBd~ zfbVB)QIfQnn>SoYJbgW5+!xV?j0pV}Zwqq&L|CZR-pQUV=DrRkp(CfDR9xt}0m zT+jxI13hR&sX&Q>2=Rd(C~BRN)f{cbgvh9gK+YbdOCu;?M_Wqw zfSLPYf$CXWn%tSixSus~{r>=qc?#C;=MKtH+Aj9isvf0rev7H7ZAf*6C1_F>gsCJD zK@n$<%gJjXdp8P~D~5{l6C9r@P?f8(95#-DyFNF3$T90vL;NcrnK4qP^pyQdx@R0x zy{cJ_c=Hg-`MYi@2;OGpxlpqcpiLhatvPos~G%!kJD} zf~C`y?xcOjiAhi;6B_LzUb)%5)+U>yFWA0u^FzyTr?)pSeW7EG4gUZoX6)$2Zr5w# zSL-2F7TQ|+`iIh>P-fcth?X zKCi)Udw;fXA-WPD+AH^vjq|5vd3Hs~Ima}2DbM^Z*+=_JxYpk8_8abI-eGq|%Hr{= zeX46NG*;sFlA;sqj{)NtSUaigXG+>PNz3Y|;c}Y4!l?2qy-IkSi`469_@q!<+E>GNh|k>W6JfQfuB%LL97IoK()FfC+`V&uhxv0&Tx&Q1eMwJJzGxoUW$o;;SxK zMby<*5~_#XaJscCtkk8FqJ8El<-9I+YgMAO#s(S&qUpU;xA6i_M-&{m=^WZo zeGmOU`KOBe{{Z27{>1y0zWO=NEk^sDwDOCKNV-uimI@6Jbzu5|v`S65NOho6K_~_) zZ(l9Eg5nSU;o?0#Hd*_OP?@GFD_#fFi1qKrx~gNyoxFDI^!>TgQ+IoEws$4Z4mPWX zKiWmYW%wNq%`NC=}6f{PTuAGK>COULi*b7^C$~o1~zeV{eXIK*;5=H zzB~yhA;zMEZAPb#ygi&jRjZSJp?v=U&9!tkD`~f4SY?MY~7lJV{j77p;4>_(uWh{{RmF7KQdX*w-fa9a6_B_X(KR z@qYwji2TDWzSR^^J$0vKmt3WzH6RwzwIkK2AOxhz8&pzg>@lE zdy|^_$j#e*hGHL8bb01?+eHRagRIi?Dd92_(NY3c1%wyU@tUY@$8R_qnZYFAdHOGdk(bhg^K}AFWb|J_Kz5X%mZ?PX##bhHxs$*}kJxH25XA zjWbTwc#D;>OF~!j3(G8K<#lCCT9-7}Gf=~#MMr7sJmQ+~8Ps<%acT7xd1GsF2gzD+ zr>E{!zVpdk6O>%vx;NYXiyUqDHyb^!^>;-hQ|gw`^+Saz@&!gvd={*w2~bM=wz%)F zkNgz*!oAR1IGW&v@tz#^2B$S~yLINZ^gOKwx0c9r!DM zhIgIG_vV>t-puZ~0_p_N^#&!7s$lr*LO>BuG!_lluF?Mh+&>ifa#?E7TF8I<9sdB~ zS3{2TEYm8ZVO3p)W(jf8HK=RRxp~G!3i{O$=C7e|bo_+XTbNzX z7qC=5N|1`{o8V8ZiKV7hv zaqO=2gr9gtk!C0|eskGR@`n$z_^s|2{e8*X3cr)=C-7eXDm9F z`hN9CF~XN`6h5M)*jSj1J-hW@t9U*I_Cx;chtvDDF8cX1yc#a+xNd`+F#agVus3k4 z71dQ=Crfm_T(5$u^omzn+~=c0(taXjl4aDu*8nU))4%1%*q0hb4~qMT;$Njd(_ez? zsjnPU*v}W?_qreh83QeEcH{?>NgMGw#;{3-YP+D7}zN0 z^V?T%IRj5IdEm@O=F{`_T6Y|?EG&~fXHTh|5gqJr~$QZ2dd9 z>w-C|tDZRLJLD=%ZqZ<_ve02y`<>kud&2K-QPe82+6$wrQlF(1dlKT32rE~d8{8R) z56{3U9BZEpzR-an^8c80B#sM{`*YF-iR7}n9DdA;_sDkXGh(RyViGCh5>oLQ78 zl8L#=_h76Ai*-W1Lgh+U+D}kYI{uh&*#&X)s>5wFT;<^}(GxK6NT0M${{R33_QcO^ z6I%MB_23yV!}=kCIU*H+By^vII0~W~C=q3y$JyZ!$dxA4w4f){2!(M4HtJAjW7_t2Z3-@_%zV~KJ|OreX$hguiMhUa{DZ^ z{{Xtj+jsKTH&IvICT&H1dV-O812t^e9%R@BCG{ z?3;3H&V8DG2`=US>lbe83KZSR-q645yF+Q5{{YZ!FyrPG$MSyF-{A0nIs3l^#khaE z!^N$I{#o3WVwW%BrRo|2ng0Nw29Y@P3T`YzN!W1kkp16+jV^!gyS%}1R-WN0Tx}nL zN}@tkf7MhV^9kR|fOlXw{-^9sCx6|T%4yE#dEJFLo}=Z#)7S@$P5%IpNZqJ2*bM%E z+4v~GmVe#!$asH?c}JJCeq^tr!nmIwyJUCzt97U6?hnz~=+|89RID)U)auaQ)O;sX zs4z?W_nE&cNIMiXjelq0zGZ8pl>PCRm%g6j z4G14+;=K+o%7vnwtTg`s>;1^3KRfo4(#fg2^KD~5f(OrFEvjB$3kR%A{%Y^#KX?BC zi8fBliQ5Jb{olP$JaOD-)2iWivRLkxzIsLS@mWbpZ>|@dF?6iGQu-K6i%LsJYE-G2 z1EecE9u`TEI4il*U%Wlo_@ zKUrC z=XEY|mRWd9$gjsPc4bx2Q@c-4wsxWFcUwD3QV1npF%Aa}cQvzK$B|wef%_Goab~Sy zymWFz4!hQb{?Yd(I_}-`TT>SbY!kZ-dnc#4q_uRj+v@A7Y3o~3WJC1+yN$SHPKrSz z%tge#vwHUYM!#V|$K3enejb@^b=-axROcz;PFmrL#xsj`HDv`BHCatpZ_TYvRWtc* zdk+UG-e#(tp~f6S(iGc?P$@EzoO#xBw{9&7aUTlb;k%yJ^oF(HABT$F-rN5G-b4DP zai92X za}mDY^p|+chfPMfG#$66vwHsD!o^zu0N(5G9Uvyt=2j^~sEtx|3e z-rgPE4T(ch!$_N>ls_<~ zC(*i4yrj5Nf}K>sQivnqk7n|(&KBfx;rB6+c%R|;ReW{+&i??eu=b{X&{)hL9^#7I z+Az)>%Is7*J`Qo*=M=e5^30mp)ip&cr9X)T6a_ZodM&l2DJMcB=$u)zyk|UgA$(_% zj_TFivQ!kHo;SjkKmDYwIR5~l7Xro~W~+NTSC(^6q-g- ze^f@xc)`)CDQ{#ulO*)iqT2z&VX)cg`M}id0d{Sxcql%qv zvT>%+qCO&$zlwe*;_C?G>P}>iC>3_U#C=tcr9}S#Y0vu5+Io_8itzTk#EMVwvG5Ov z#J}334ixZBGL#KhZG25>qp!N$nEa~MYh`s6^{ahB4KmtF)TI@90TM{|uvIKl7aA<2 z&TwiT;qyw0Ol4;h#;m2ru}XCzOABnONp_(kM|*%z*fjHu;l+JxtFk<2#fYspQ1(y5 zyp_YXXkFgew9!*68hhm%zN1qbrC~@RXg)fD3>c}c3V6#~-VbWI+!gf%?CuPspe@v>5~+Vx$@}2g@>~k4xWtO-vezy&=k71&yIHQoauhpqZ2?u- z`{XrT&z|PgW=HKaUX=xfQ15IWp8B1ID z2Z9#D=EbFiJBqYbw@ygEW7eDdoO69=7bTj?yTUCsZquQyo94APg>4cunbI!%!p`pR z7~s%$Rvt@t7B>&vkv?Y%SB}U^teVsLa%)?tZTdx^DYsiGQdF6Bn2(IHqHBBZSJt&( zwq42GINXj8o<3S~@1lm6$2o^^7uIcF9S}y&rqTB52?M+yk)ng>b%|cg&}? z&is33?thejG01VQB)`;G<+aPIs=3__zN*DvXjo{}DQj6ttOO@_fs46&W=SCF3IM3` zz3CBxLf@Iqpr8X#LE-8i6X6XMv|}7S;oZXskS; zHOWAc;C;}!d;?;~^xz*F^Up2vmo3bh?+N6g1MLJapH2(mUO}M0&{Jl3tBvE;4-<58 zPOz=0C?qRrR2Z-;ri)amcC4XWHf|H!;wRxA*G1uE zrS==tC~K~wQOU`6lkkuPq^%)npz5h}6Q`uT+U}U;!L+?eUdo#-r~^I8Bgd>66kNiJ zvgyuY<_jb5nC(Xo^A1af)VJv{r~O-b9|~1Y_r{5PrbpQs-a{0`a9bGezM|X}`CPnekZMU;0X%Qtsy$9zpH^U%{%S0N8m zIEUGE0)?c71p+1xkZT+QrunpAt`;;F)BHK<_(+58uEE(>-ofId+m(rz7e7EBZqYcg z4kc$Nf{C|LjZ*aa6u3$1QiV(1F$n8+4aHO)c_Y}Al!OdQf%_t6#StGU zPi{(I6YoH-$xm#gsjPnADtc5YZmBKIgrsgV2@v|jxoApmCUKx9s-0w=>hShH>!`Pf zu1jS_C@rBg!bQyPDP?hefKe>|BM}2Bw8Wx6i0%a?=;eJR~8n3h@?D)5GSeDQ0P0-DV_jE-1%Q5F_%|h zwA%>ntGNDF!ST*T!+295zhfC?I%=%-^pvIG>rQHwNhV+)Xx1$+GnSDbjo=mKkm2v8 zl(s0`KuuiZvCi)jg0}l)%&OcI(;Mnl3P)in&N-3d_pW2&7TdL{gCCry^UHBt&hh;akN7?et* zmKvoD&gpBpJl%Z)!qoBxw(Z3zQkk~4L3PE-KmpRaW*A z+{vcPICD&nvp34`IW1atyK{L?B^`F6e+c^od41&OSCUt$Slb993|?B>kpf?A6K>rQ z_byL@(vZqjfz$~IvH@4RMKxaJ#|o6~)}hqT{b2@OX$q*fHM>=TgpCL8p9sxFDz%T< z8rUWX(q0hon%2}Sf!X`JISo{&QBuQ_@-p^Dj|zSzmCrJ*R!b)I>3I%?^{Z+n1TSto1SJqw40#K#Yl(TGY$BSVj;~^vu z7_09)iQuy5g~g{$K&juthxQ};8ph7%{{WKZq0OPu+im#KXM&#kl%dr$OO9K}Yw138jyLTSjUpt zDxhoS0+^|F8*~$pl(dY!>wYD-u$SP~Hc&RGg8&6$p7{M&qVrNziI?k(YbSuIFQX(W zX0gOnQ8J`TW1yek7cw_RNfW41J6emb23B-E4~#(?DovD~kJ{5uP#pX%lc@8Y8pZ&t zWs^lJ(=xiHvddd zY89R;X+)!ye63!n>8LL??9jX&p$;qMoB}|jag?LW?ivG^wA)qe72SA*>Ne>vkqRS{ zi2F&9)BXk(+o@XGr6$LT5YV|Op;FqUo}CPGYJB0B$86WCD#R?OV$x;|r}0A{vL+o$ z`fu#tgE0>3FeV(iSFIKFO`svftm;1xpP#Ze-LsVskCg7b&%5wkuP+-FW3K-IO59{A zevRGXE{fbI{{S{ME6;>9O|*R}vDK?`vZ{nme+-i$_R1%YRP$_7l|Ry#6jszfnA9YB zZ9zY2ltk&kN-c_4KS=&NeIZZg6$Getng{HNUvd!{(*e8x05u@f(j>g$3LY zV`WtCRZuddrbb;qR3G|*92F6^g~N45zN7qKj^UW!7+q*xG^?w+dJiy@@RrMEWX%5n zn-zDLk`uDf;BoM_yj8W#dx5gzJQs>%+;>-I%4+MaRII!OTN|m~6zMY|sme#J{m{ld zxKOJGV_3(PTKlN(^83iWsb6rc!j9QPb2Q9RH*TX?TIvcGgojMYl=jX-CcJr$0trK6 zv6RX-Kr{;sIP#QH<(7KYfXgj93Y{PTkvSOi`(_?`mG3z_kmzW%nH-l$x>VDq>OD3< z2}oF%;g_6fn%0Hpb6cKe0c2K8e<7}NvbOM(xRdyS2`5Nv+S9s!Glt2-gJW1lw&gcTS$k8%x<E_NCr=Xr z0H#g)r-HLg}roTZ2y~ zMWm6_!Y5>n@l{esCcr6!2YZrvTb8(Xp4*4#l$NUv#fP%H{qcp` z?R50C>=i6H3!pyY$O!=`I+>XWhJIc{MH~=F=Zb}|s>c%GP*#eblkkk^RMl%6Yyb&1 zy8)%Hqj|R;2rEjq6Vq`TPs>;)+UAbc4|`->$}f^G<35%h%=a7IPdDb)dV77E(`vi~ zQPb6mhZk7N*h<=YuRy2-kTojT*&6Hq>WPd3i#gl~ulOhMS?zrEfMP$1&UDla>HM_3-vBMX1Vo z73!rbhXz%05`G02(3#839b-DkBQB!Q8;M$|&pLOF-A5SZ>&2|ywwt3(w14A}up0Ta7?aOH!US*2QcU|qf=o?i8x!?Z)dP*e!0PVdlohR^*%4^Jr`ci{` z#ch9?QGc9dKa~PI`tLyh0Q!n3_NvibKVgEu#jSstJN)Az{D@Eezs~Z^zxl|Z`Tqdr zRpMWIVZY**o-4o3a$H)Br@y}PrKbik;r`VP!9VmNzu|UAcOu*0d0q`h8SneN)R`xN zsaHP_leiE15Z~~N59UgH^DaLk82v&!&TC)&x0*%I&5Mwi+=yTBs~_e`{(YDJMd$wj z3`@N!{{a60I#HkfgL3}>sR{lQWBkj%&$9mj$i4pn;f{Bx55wC{5~bo=x*ybtU+|kB z=1Km2m;O(^aDJs7?1`jy=W9V83a7gd)|ij{4#R_!hnV?MyFUK_;Jt6^XWr`-6s->2 zET}auCAQS_7pKn)L8%=dW92VvXZ50(ttX|};)dduxCq#fwrbEtimJEVkScfq2UbWk|vs^*V zH5%i=TNgO*jj0r;>hF|`PlOO*WKldKtwH3-c6|HoI;YXJMA6egq?dy`c9-g9;F7Dqx$=iG>F45jAlV3!%sB~KmdMP4M`25VscmyMRV#Y(R zimjigy=A00-J6!}mSn7`txRI6O35Z=N-3-XN;A~>!yo2WOzTy1E&|S110##6;%lec zk}7){%&DCubL9F}i&B&SZgh#CFyl78h`+IIFPEt-k1vh19sDzBaG zZ!AnGBNS6--yv`!1b=p9I4j|a#yDyhG~J}sQmr33&HKVWA4QCDmd4*16;&!m2XAxa zLXFrgNpf3HY*@2PO6oZ-^;VSrFHVZs%>CQaxM}Wup8}_g0fuS$RKJ687XZ@N(IYvx z-*a1WaX&W7FD}{~DDn%ow%H$HfnVI2O4pfLM~lM0PEWj#*tC0(dob)DEwwjv#<-5M z;98f=Q`Tl#eM^n6$!J}AN9=*5?wo=7o%QsOZ3*-m5l|R8&&9B^L;?bg={0`dDuN+etK>{dpK zNS8v@B2R=w-v0o~H)p52%N$F-W>r0r{{WcVYV6l)N^F9~aZbwX6~>mHwu#h|J{2)i zg|{s~s|xQia@DU;s0WmIP7wr<-x2XOa(2JMSTdXNPx59qC*jmlgAG1nl%L|cu?^-60(pf_)83J9zzzge%f;m{{TA4{{T5d4=?f6 z`4h(7#{6d9(2gR+(=LHH)tNH?09KqrH@z}Pc@(YeEO`8iN3-DA69pT1!y9j;ifk5? z^zxGtDDL~=v&v8KYEOV)cfF-nB`I6b1A2}{zm(6)5;p^+SWqVVqWqm#32oB0Bl zEXJCcwBo|FRd2V05(f4<@TkJ5SPh4KZ2qAVIKui zF-k$%97aiQQ2Z|I(rqjDL_HFS5>Yg=?r7p10heFS`8vafA(VwYo2Ax1p9zfbIDu+iQ7wZfTsrEPGc@Wbdy9cH$7q)W$GF_>GE zvr3|yyzClC=Nd2OiS?-BJl~Z&ljeM{gn0hz$=D7}$r)}#jdBg0q}(m_)~maswWTzw zXBN2^A6NlMN%&{bf^DzhmA36=H6O)N_Qm`!x{^*{1Gf8?E!sO_?nTOml*b3L1D3H!Aa0vnb53ha}4@F$^&oUD%*A>fFVNr)o%SNFWy-D%t=5nDv}3sAafV z%iE&)3eN@G(5i$Ls%BzB4r2lfsFpKZFs!Y@sAaTxZvsUBBj6oJ>?kO(A%$VkQ6WmRL`6+PsO*{OLn zh;wsnle7;BX$nmg5-9G2;ZaoeC-uZ(mDNg~RIVf?6Y?=23zv10Q(%KR&Nwbj%7+H; zgiHuf7gYtk9~ne4B0>q#*WD1&Dn+YkiG?a;^b&~V3xYv?uu0ZJWA#H?DwPykBmo9y z2bB~8IKs9rbCfZ`DNaV-^%~bh(oqD77c4g?QlNSn?S=VNEp-*i?L&{O+_}wsXU4e= zZ{>Du(&XJ0%Hk3hSZQclisY>i;gnWqZcKKi!9LBL@`miU=^sk&6{4yLYb-SZ3nE3N zB$WG-M0p!~ir^gr)w=-?5}KF)0Hsd~D&i`s>RhT?gttu~121gh?e2S!c1)ADpgdHG z#$QK_Cpf8Uj?P(TrAmM2=mKotWv*=Q@Qrh3LcC|z+s z0xPfXUafaLc2+uQ;@S^s-}n2%`ktiki;A&oMrmoe7dE?7QKTV>ND&rb)wO5Pty8i%p?QOzj{{W=viHOjW~JQSa)pXOfE{a!etl%IznRJs9ls{N~m{)pcN->QG= zM}PQpoN-35DQWUI2>tPLsVDw?;D6BD;Jfud{YVSn*4Ei|$FDfW{+}brFkF_FqJtyF zms0(66_j-imX_0vys{LM6)8lc007xKEU9z1Un5!dnVYs-kw0kd@~~PBZC~VOl-*kb zgw(9l1tGMlGfPDv%fv=X_QF@cSl6jz@7dE7m=A3eIarO!ofpD2%mesRq}`USKhTIy z731=$4nQCI!|p`evSb#Et+w%M;wz;cWu^9NX&y}-)j&MMiFF6!Yr#nnJMEnlxw#1= z)JiB$9^O+Z*VaE3X!Bn0`>*6ivZ{ODAy;^+&MoHGkyO-JYpUILkxcxsLt#h(8&RTU zKFB`nxGrRjx0T=fPvEL7yJpHMIr+s$tSOVTj{TeMPmEdN>!%Hz>msSW*Hp0c=wHrJ zq2wZTzbdMAtf(w0M0MBM7u~Df{{W`IBP}j9JSuh9+Nis~vrbav%GYv$2O19z)#6vw z*Oan1X`98nKe=6&=qN#J6}swGqDWg5$}$n?5P69##5J&*{{Zx#!ixm&FJXuh`P=^h zy}yc4&v{Gg3(Git*B_?rKCz59A*ObU>GM=<1}bT45KvO2R=CMe;?$9(Ly^edT@Fr` z{Ai`cV)2%*5;ozX!`olDEI{QRH{>qVxqYh+QEO&~>WVhrtF*OJnz{LOqmQSpBGz!N1=g<9am1>E zvAn^RRN`;C;ZZ}0C)J^OLVI3mK%`Ho0QH2Utl zZP`a1b1xNGbNdxdSI=WLcUwI@bM#eiBsWmYi*OQKAt_P}rVRWg8VoLC8%7x3G*wx9 zC*HU|Jr5t;w1bJhuKlc8v#JVglY%fiRubbif05duC4q^n%K=P0*3NZP=vIzyKi5H26uSE z>VN_J5kKKl15)xmX{CzyecHZJkYDhs= zhFX}VynY`l-dlzbwQaCjPT|2gKE`4XsH>HI>?^i@;<)Ktg~eQ){lUBSLX&Fb_8xk< zG|zxPgL0s8#1KUOBoK!Lz8@&R$N6K7mec;ufR7Iqk?s#TGVe67HIfe#d%y{h?|@ ztg`m?6hE{r&*3g#!nyO0?Yx14)q0e#kj3)M;*s-qy7Yitrl>+riq~>fG*U<+J>Rzb zf-HnQ?ca#-E5rMP1ucxcuG@j}Q!7jqC>0sc_hUq;YLB#(>86LMkp75N(3U6!+)U(> ze|Q~qh~pY3xz$elp5tbyZ>-xSs(war^wrZ!3PNWgpqKiw$cDPBhZYb%6T8U5p}<2o zr^Zgyq-*xVB366%$IMB$+vs?b$5;G&f>BwREwvej0R@_4FY6zTL&yQ+6uAqRb8@vG zWe{koQNL_hlPxNl`-(zdtO}~BQu-l;4a4n}Yw*^hHdSGJhU`ft6GM_S>KeSJ`FZsc z>s79=bsZ>BHdbF zZmrc=9LFHBv2e2G;`qasDxeolb!}-~JMB6Qq@gheo0hkVr50~?iNQ&h*)A0A)my9D ztzkn#+JXD!(8b-g(2!`TuI(KgwN=-icsk3?1)Pq{a&I5cZB)XVmf+}>Ep<8+R)#7{ zM8?viv0`O2)Dan8RB zNzts%NPHbk33P&WPgb&pGe-YBCDV5Jv_-d;;}DUw1-B{TADzZSx=%a%!; zU(sR5t?1obUZAHog%Fg4p|D9LnE)a=vT<8RUm>7USz{);lt&P$DwtnTz~^-dBrN60=W<*5>aPnb3% zidOk!l6Rm@>W=pKik*I<8?LWq8UQI41ihQ0x_>h4fY_q#{I0*|+J8b5whc#I*w`$yJ>~*V6Lu^rpr#I53!peujCn?G{7(COjW9Ka` z_N)hUVB3$FFgQM=hXreCB&iJvI!Z4eUK=aZhEC5uf zYgKeOhqSJ3%h*N!%Ya;J?Fa=zy$t*%Bg7<`pRxrx2$*##WwMp*&@A!qiQb-+jhTO$4j;-BQey3|A=_PV@?FV7MPm5|nw(%w}M9};}u-Huf#YP7` z$(i-3pV`;|G@^2+wl2l-_O?^u*xkJ-2Wx3lD3I}x3Wqm4n|F$g_U;9vPE|%bqT=q@ zn6;L;#)gTz`cK31PAH;Nk=y|w{gLT?p^&~G-q0^4?|V#e(jC@nuZHU?)}V$;l4rC? z^fBqfC@H$}m*ZaJN)+wP6d7wm$GqfpAweN`i9D?v$^nA2Do z_dN1w8c6mdChd%TE#@y39<}h%&0kM0>+HOr$avu6#?2L`_jh8=b*8ycy-8I>i;KdU z=F}YBwQX%wlqM%5tW7^;U)=!kHw#T?=AH`CyrJaN)yIz?cd0O~ze~>Y?`d1s{8p5u zUx@tCc2o28kYcU=U0t{4a35;FxAT`1CX5buuqlTL`b2jW%ZPt9yc-)YiB!^1(!BGk zM?wOe5(Y=Cpxkd`^xsn;L*;zfYnjT(97nBvrSPBC8q2{vTY&MI?XLd-NlAlRt@gH` zWet|xO&u~=D-%M07TNu=QIztp>SQkJs{B8j!q@&s+@`v3t(P3QC0D_DT_N#ODO+Fa zA}F8atehnUX}|NFL~9K`#a%nV`n_>?D0aurTxUbZIW4Z^$e7L2$wysqqC-~pS97LS z7+bDx1O)n%=?=2;u1i@&rQ_i>QbXp8GA`t7bYB+N>}jUyjU#TFkCq!7eCSTmwtsA+ zc*E58 zvb*T2lb*|HQSl|URIkM*aE9y^AjdeQYW_&Rw%BINPx)9^SB9(8+F&Vi5)YLdZHJ5V z8mPX#>z&S_RTcGXs(okUTGXW-eWo+>x=9UOYP4`i9Ab?WT(wT56EUyqfuo5?`GRv= z+EG$cp1u%dZB>^qsZZ4-Yi%HzohSDDAm*J=T)-(;kbl7ORfdRw6!OA|@#{D*Bcv*i z6#i?Xt3nb!8Tj}{Y9}p`*%;{(J+w^$S^m^&RL3pj{{R-$K33a<%q=JSBL4sn^Q`u4 zRfSm|Wm2f}CAi7hTXd3j@ad?F`K9G@$=k#WD5d_}`7<-(eon@DR}jme#PVA`9d$Un zumarqYQ_ssGbMruaWWkz(1R8I&dFrQJE!w-R~?dVVavwp{{XjLeTsXz_o2-9Jd)mN zc`GSZp^BCsz8CB2+EeWO!d!W#)llIets8(NKuqK3+#Q#32?22+SBm>vx_hUvO_^&L z3&`9n56pj2dd%+2hS+$270I}U+lF4Buf9}XGH=c5a(YK;)XHe;epzl1!c?HJJxPL) z2g5NQ&yBLX4y>dV@)vVuv5s#oyGTBsva03hx;{a;zVphw)iPaCDob^hNlF1a1dZcJ z)RwexSO?1P3b}U;+y^GT#W@n~nSJ8fPFG5tj^{>ZuoBX{&8j3TbV)7N3NCN=cX= zF^=yjUGy$7kBMwNzLl=Xv`MvB(6&F^zDtHVWm#Zk*1aLCR)xECQ6)3Aks{0 zB#1Ugd?-*pB_39*_^L?i37l3*x;dL7egt{MP8AA9k_zgTev{}|ltU2s!j@f>+lyRg z%3E5uB#i<6F{!eWpsc1+-00G##qGeJE+`*I~Ba-DPW{d*Jz^4#1#gDaV&{ zb26*h2g33Z##V)+b84KAQ4s8a`($_&FZIYob!y=~#~P$JbFT9)3oYC1FDYILAj)5C zbtB@R^*0VQLYp^!cUD}hLL9X;5LG^(piU8ml}vPU(xqnSKXboy*j7bRVazhhx=`7W zDBRjWl)Zv0@;5w#MXSMMh0cyyhs`<8L7hJ9`3=$Qqp49P*H^g4Li%#`==j`IVA)gZ z)=LhnYKWGTic$?$pPr#3xm8vF0C-681oaU3(otv^XR5W+)VEo6T1MrK6}Rw-dB)?l z-5GsdqVw+XiRF}P+8lSBxX%-6mF4^G1!YFC8b_Wj(ZfE6}dexevg zJxZ1-(1dIf1Qg85CI0{*wr=H9Ke2CKbO^n7)iS|K2ct>qqfnAIYxlz>?yEt?J-%Gt zo(ed~zNz$YvY^GiZ4)`FZNT5=Hl8YW`B!?Q)%{M=;Sy;`BcisZfAgci%&k0AzwuS- z^&eAu(Bf6dWOdD+%BW3L<;n;1y-9Wu5-3mxP{Xe2B{{YIMJ1rwS3hkTKCfxf^ z?gukm-lbrrTBWoo34OAG-P};grBM)e?c64yC5uPj$-rmA^wBV$kH~M~Z*AOn|_CIx`tf;%C39EV3ED1Jd0%hSd4H-5$sEF+) z=Bj;FcJtgQQji_DnRK732k8(Dxd5!il*KiwyLavuYFR}Bl|4XPgZ4yUs081kn~F+v zw@%{9jHzgqKi%~l{{X874446PY-FnCzTJC^DnI#EG4hk={;UY>h$+UxP6+c|xpyGq zN>PtexIpM?ND}Gd1VD03*%b~pZPb^?{4dOviF*c+n>w1-kubefv(IyN`o(fE|<-OiEfa!A< zp>|{Nw_Nw@Qesno5%w-PiJF~lUv(Ca8nZXYnAZv3a(rfw*Q=+=Xeuqjm2X$n zxmi$>)kBS~B`a|2BRJ4n*g4};vsnzVi$D}2pQw$7)3x_FGbT@_NhAw$%;n*+>BXdY zeRou!b$?WvR6}FRhk7-fB1Bq$feD!^;F6K0Mc5i_kYPmTJ$Z|2QB&>{b#wH_2rc|5AKU= zLSK7vCndTklKpxcO?94 z24l~{EMS?A8VwK<+mWpSKERh+M%^ITVF?Vx3A@& zr3LEhaE7V_CM76Ly)qJjo!EB~`p~~kAt{@1D2M`H+mV}vA;(X_ zM;AU#!CKuc#)Rr*6I9v#Mk4(%cqA4V4dA0Y&oJN&C1H7;80)+E?aY0~9W9GdP(eF) zQLSG*@ntDKwsS@Roc{odiJ#L0ixwioCH16NJDlMvLPbV-N_@gp6m<6(Lt`2uIH&L^ zq3ztk8yhP9P&AgYS9D8ZxI7A(_#^7E+h-VNmcQl;?P{^Bi|te2DiK2R z!%R56rP2@yk|((P1Oa=Ff$aH!SNMsqV0}YL3+$tgJ95Q2CkMoH%uhbHF3FuVAH>+G zsbZeG;M$ND9AcSD^%Rl}#DwLklw+@P?}+-A`yG^J!+mM=_i3iuvWM`&(7xeYVhv2Od%qI;aGgVVz0S2+BAb$w*Z?*A@qafCuzKnF{K)de0NtY1jNE zEndyjoW0^1RJ_3&{y?LrAt`BEbo?e5Uqqq7eoivWN-d!(N0{25=#FHh1k&)0HH|3- zd!#%D(fu&)xljg#sZU`kP(qa@co|A0`u_k@kET3Rw%d=Apgliy;RZsy7j(ypkS&%V z*21QyM^1)cWO2H8twwoA6(if(O)3*KBusuYC_#4Ta4H_OJ|o_qR5#0g2?=@j831Tj zv?uk%?V3TT1w$n8c!cVu47#MW)@Lpw+ z?I^A;T`6$*kG7ftQ%yYtfYPh?rCu|9varB4Q_sCYtufTVvau3 zg{N1Kckw{mI|<`^B7=7Csjm+qlplq*Z|Pw^UxZbW0Zo~hpY4=<7#JfuJSUj#500r? zQGepQT&>060(;=dzv{wj#UbKB)XxV_ka=F>-zYI6%k0mg4(G>Sw+cw6NWB zcOgJXgQ0~vd$K6ligPS18t`23De2;>tzXl7vV_$s%ZJprjKORW5l8$@vOn9YAbTVu zIW!qJJ4oCEG-)m>7FZWNYq>GsN0E5ou7R; zE$CUL?n^MeDY=vKwi2SRaQSYrALyB~epBvF{{Sk${{S%er`K-Xj(s=za#Rb5pgQijSJR^zy6AeeBOfbbqP@$mP4^$G5LOtFy@ zg?uZi`w#9{E4!Mo2|+s+fT)9y&xNPxB!P<~E+Ep^mDNCsce>ma`f}ass#{3WTO-!&&{23RIrI#E%g^ zkpuTq#=C@5rFtibQhwz`GA5@DGIzLz0Wz7-?ue?nOdNa4m+ln5*||3tW}2rU#U2$F z)+-RD(<#9;NyMQ^BuMxe7WNSMNIM}-Yf+(AnxpG2-B=4OF^f>TxsmDHs+I)&x6~6E z7U3W%Wx2cHQ%b+;{{Y?{NAVlU>{Lqe2(&3cd$&Im{*Q0qn%7;C^Ll&IO)u6vzhG!B_ znt5X=LdXz~p76#IcE<39p^S|emM%|nHc&lr1?t<>K zwTm60fhl}8^!a;L8a`ErG3uFByz!FU)OS%)Nb9vW_BQ^J5;e3-EixJ3Ol%AyC8GR3HrTBxV(x3&KORw7bzBlP@>l&B+hnt^LfmHz;NZ6J_m;38%X z7jh@9yO*;Tsu)_an_J08fuc%eefq=HlnPy;=M1mVZ7RmC^vUoDZ^9+#3O5QPjdIoI zrNF0cH0T4W(w(yV#7@{4MyNME_0Xku`D*-teI&HdKl(Hhe?%ujHQds8qcq&PkvjIm z@L^xNph`#Rlq0NTJQA|$_1vtW?rZtKh&zY3^OiYU{hH-?sb=$I$XY-lyR9k`%jpVO zQf8#25|cTCe$Oq~yUOAoe`O!*R>Zhgiz1~NR)e(<$z z{h62#iKTikWZau8u7T078hxIhdak@hnmKIw>^8jbvtH^N2@07MwS`hfCl^O|>)YPnnmuPV4xEiKhhx>8WxNF*poFK*yQ zshkskX2%^y=t~Qiv4phh28+7R%Q4H#RSLuTH2STqh6Pr0OCi9J7hde zQ(boaunO<&S}Q)`wz$({;8ART{{Y81-5PQ3Qf*kiLZVHdm?#-8Z6O;{)z*!mJ$AGM z)^1KGN$wMhpMl1Y(D?p7p5%MPjMbWt<@^+l#ax}uy_oVfw)JJWw!2j5CZSbW z&R1&O2qjJY3lcvApa&W!!eu8zhRj{#Ca5$&aUPs^8g4W+nT%DYP6F2rykBR9W|%27SspJ`@$_rh zZF`5P-qN=GP~ueH=@n1F?H_Z3$kF?8@FU0U{v21kOxtYUKbgFC^&bWFC1=U(*$uYc zda&2Eb+X=AW~$G0WodD$nreo_t+<6JNhvA@K8&%tWYGt-)D_@&7E(naW5oz5PxA&s zkH#cCW+2e&At;Q|UKm4&Dmw^1BS?p;rRgV)P35YaJwn2h_(&<7iPNZuwbe{5p+r}D zY?L+t8&W)a=?+MRV)D^4RJ^*!Bn21D^UF2|hTn4w+rbnTI(5F{3Dw5{lWix=tx^C$ zw{PWYKHffOQDO}hUEh~E3by|Mds?b$uN7;oxu~5I*;K+ul2DKmJ%TDRtTRrJ$+$xg ztz61#;f-P$73GzB4lRXJP^?IQc#zlvUQ>@tl99)%@{;2$se^)IlvQ@CyWgB!YKib$ye!b4Y7J2VzMid$iY$R!DRI037k%HbA?V6gW7ySdq6g@*LhXqFa*yQWI!v&(nm}+zvsL#rar1te5Rly&wRnSkip2&TCZC2uGxmaX<-fq(>akT#=2hIwK0$yHCS7I>An*O7o87^r~F~tJ*eb) z9gg{L`KtCFa9e`Jl3N7rl=?ZR&Zq}f=o!y2E+h=(G}B*dm+X91h+@}gSY9_w zipG>swK~XBf`^8pQKGfQK?|rFDyi;smom3kpL)2s8D@^1ih4R=(h2yG+Cm5Xks3R8 zfwIcnJh;@UuIZEF9B+(PXB>fhXCKFHDGPqFXJfQoU}q`@9>WD}(v6+QoQ&n%R1FAyqcglp)0uCRAmiiQ%=K zqKzd{-CWx^)+Ed`Fpg-pr71Cdy$cuv87C%4Uo>bNz z-xRNlDz^jytm|9<0IdN@CP4m0V;#OMon-sdikFXjU$_DDS9@JS@jpC*WPR#^&F)pd zD-rSH@aK2^>Dz7MF4bB-r8`!qNI!_xwy)Ax6_*(-#ePz(V#0R!^8%!+P8ZwF8);P? zEt6_b!b<6tFSceX<-W|5vxmK1+2Z?sqvmQ}ULnGlib{$KMcbNv=GKp`!CZ}+S!ptC%O}7Xtf+Sv%52Zn zR8c)|an&iE;+JxpaVe5kk_MwgDElX}?{RmQ411Ual+}K7cg?11%Od!Mjxkq42;0NQ zMU%Jwapybh&NY!`c_XBidZ??bu0aGIggB(e;f=Fr${H4O8%w!oAhXn^ z>g(IeS_o48XA1Nke;MlylI?_0Ib2#{HK}P=hk<^iHa-~RPND13`*SvN;+;B$cQG%j=We-KH0s9%mbCu>a<{@ub3fWLjDYN!_&sl3 z=4H1n9L)gqKnuTpi8m_mlLETBEi}R+cUFLU44>{oz%%o0;=fJG$YvqVSA9>Ny;``26n)-Jm+=tuk-17)gZ5Uo}iAgq2@JL zaT=sc^|qPT7NH=diIWpX;GCkTEtGd!dW&nLbHpD{*nN;&fOw*C*OT_+6@g1Pe1aZyUpK?DVLn1*yHxsoR1w4nYJOWWfWleVUc$(IObs8r;( zqG#`jIzd0GD4e$2VL>h_J9f)-63FSQua;V;MI|G(we-y;6PnXG3HXR22zlJ0x_>JP zPwsGW6n6Y(&OQ~Tjm{u{T2TgHL&}4}#i@F&-ig|iFN?+~!!=rmlp>Dhd^*F(NYkR8 zJA11Z-cZA;6);6(VQ1K*D7?decB+duf239O)RLj<9rOxtozR|e$rf23kDUwEf5G-8RM&NtYONdx0aisuKq^qbP^#(Ww zY;{czd}gCUv=4_qqI_GC(=$PQs%GC)Acq-y;qaGL=@gcI!5h_nU$AZ)u&TZ|-y?7b zBvG`o<+k8nOoC*})JY!5&_jOHv0i_B+ml3f8m#v3myED0P9<@^W2tJ5#cG5%w`c?v z8Eqn32}D^dpkh~jt7#-t)P;pfo3A{cn^Zzin4Dsobt~POFEc_8wIK-CL#Tw26cQAK z=i?D4F86yOD5d8{S#X5|J$xZJQ9UmLO*+9k4v<0lP$jh~g>VHm21oHgQf`io6&iw1 zfs{rzPU5^ielgTfYp9+W&B1LtkfS+jk?zt7v!7HJMiA!JOZD`P+^ycCJ|##Q_3(@L z#2(d^ic-lap$m-?n|W=KB9rj@qMCbEr?;A+AgQGxd#5mDK=PDT^op`!3I70!Toj6w zp=2d0@rw%?&1kD~5mYG#=S4J!(4WPsgy$A>Fsi##M7fCEep0EhwUCu44SlfdchIL~ zBB-jrd=FN`7c&0uiAa)X=hiHq%B;b028vJRdxcHHndG5!N`|Q&4v-^Y1fqGUs6Fj( zHFN<^Yyn!%2Azebiqa2=Ra&kmZrPGzIW{|acp3}s6-6Wav>RV+p!N1Zb4S!+YJIK2 zie7QJRbMFJ3@;MmYo-H5d#0&Xxo)oCQsU63inkI0AozI3Qfy32jDS|gEX~BT2N1Is zqR&-mtgE1qbW^N?v;skqlwzc3BbT8-dBsqS6-r)Gh$WtbWjkZ>OHDdP-vW9>T}3cT zjieDdYvCQ$Q*s2Lic+C5sXiLV2d8wZuHR&pw<}LG;%THobJnSCFE$766?+6%Po#s? zts2l`Ag{kp8M0fhI5naA`b3u;X+H@_(Cr#V&eHk(fUhISV;R*dO;#h0r4v;PK=~9W z_Q9X1zr_XmV-huWpA@4}oxPBHl2Dz0R5_QgJX2)H#yBJccg1NJ@X@def3lwll-@Z2 z=%XAF)#bR|WgGOKgGdh`jRHZ56*DJ#Kz`sJJ@%{7lZ>0njrOvq30lqcci8Rtaewr27_6%$o<;m-9A zd^~qrJ*BcIKYTOkoV)cX+Kp0no6fZ@t=gV2ozRH%Nr&51x5&Gn(ieSS= zI3*Fx33L0#l_S?*XTAkF2c9J}^u`VdL~@F16R`~xAo#>)>E0<~w`e;hs=Ka2t3T&| zBI<3b2W#5Mfv1;9ak$osc7QigYrLSesYy$HIx2tUh3b_kY;@qbS147H@F78702O~k zMMtHiex#viQ^bQkSSh?e0hLuW(o=^Vq#y0uLQa;FNuB!Fe1h}|U< z`a!`JRzjr>Q!lW8OiR}aVQSPt%es`Q6eV-;f!}m!j)pd`!nFj?VH4{W#_ZJ_^vy?` zs~?3anDdl();UG=P4P<m<|N{# zxUN8L)5AdnzCu$vTBj8)Yk?^RatESeopOm}0H*~#dXyw&q6e}eGAVi}>ML472XILv z@W%d>5pw2ontJ{<icv0ky0jh4Cjq+=~U(%X7@y*JPv`;`eMX)9k=qJP9AR+7~gw!`o~f}7m-!)p3{s;%9dG1j80r}FR8{{ZWI%T3YS^UmCtlv?eME$e8czTydGhZdcM zNhYT^=GD@-py|Hlj-Q>7-|0i-IruUqrGi> ze`+lDea`?r;UU!h7XGag*sjO2M-N#h`-6YdzNQ)eZEO7Bx9~>6+1C}+0rrHJucF7* zg(q{pjpU%7sW}fG1OEUkA`ut)m9O)C$M8n~0EbKB)It9MqaW2kDo*x8k~Vxv$oT9? zeYqGA)82O8KXLpQ4$6Ove)L%V82+ckpHs8zy9Hmm_EX0#%kId`5B_6s+>vN;d5!yn!-aId(gj?}oO-*{d<^hWxQvIouZ zEw0)R??1@=W`I5WauX3A^N0TcSGhQLrN^t^{1I>d60d&f3RPYOiQ9rY`rM_*$sl{1 zyecIB04|Tg-tXd<{91qgKK}sVh&}Xk?=GCmrK}F$FT|($Uk5-R`-FggVNPt46Ig>QbTFN*5t=ZXI`NJC-1H^@v%Xcm^ z(%sK51djr~%sV>zICd}GM{Zm_%un8PN-O?vWTmaht&Lx)DbY_{w2<;p(Vk8-m zgBtAV?5^*`bQH9RqN~eI%BtXQW7YY;Mxx||Jnc00hRdUL!nGiYT;#h~Jrikk-dQ+- zLHjgP;5_2MWuxj^xH*T4$eW)ZQ}0<6s%;)&4o)#wJ->W61#MyO>moF8P1iW`kwle; z2|Z55MNjBgCxJzd>V#}UD(4%}9X)rFJV9I;XvOWYL7sfOrQCn08hw%RZ z$q|m(li9w$@Npv!DZ@i{^Hu6El>9ry49|{o`?MCo>*3HK00u-*US-Ki4Dc z8*q8m(elNK<{Z=pu*v`vKLPwt2m)^`Evr#gd1vOhBjc~UehN0L`qzD9pE7rot&1a| zZxW+V{Kdfk0QkxUVm#4<#QXmM{nVJRtl!opdY(+80RI5~D^2@cj{gA0kS_lK$`<}7 z-~Ryaqm=&uSnsRy$l1wMV4kwYXvqC8L%;Fd9td}NpBUnO{{X=$FY6Qahhehfv-&!O zCt3dhCQ~>-{{Tg16aN4m+of-r%sfxO{{Y=gn(wZs)l%6|symM^+b#8N{{VSP?j1nN z#QJQ7DNYLl?29{{VKfmFBr`!A!X0z)n^+mavtGg?$zJL4eWX0N-`qtIKaP~!q7XH;x?(4b^uQ5KKVr*1> zQ~sv6GEGlDT`NQEW^e&dJA(m_Gji#WqgyoQ;S70Gs~+#G93WP+zD8 z-6a=p@pJmN*Z7i~(Wd_Z@HUw+OM6nqr{o`h;;XKEuHnN?J(d0Brj>7X9ok-(JHK=M zg;~3*f1EGvb@nk-ffkIwSu|-IeafOt&B-<9KJ*J5t<6`aUTWpdS)FsMN&X|*Zbs=d z_O7&;FZ8P_e{H_u_#uvB=(@bcM@q>vr^|?}%5ce7ihJ2gJKjsl<%NSvP3g z?g*K(S1uniPN-_OxR0K_*4BeL2de6vAM)HVK(@DT%?KG@_vr0Wf5|P%3QEEi?@ZXcD#vxN=xtPw80nt!4Me=0_b9g@`@6@{RG z!m^eB09#^5?12bkD7cOZ#Y~$BN_|G!Bgn@{bGVHiIOx*hiF;3s#N+HjYY+Wf=P@kU~^{ zbbyijAcmC$kd&&%duKoiLkKePG86lvQ78&0TavEV(~mo5>)!VZe6*Wm9dhk;xNMo* zPf2H}Xp<@Fz2u0f%uA&Lx$V1i*4NY0s%qwpD)n~dyuRl3xx}KPt>4Q8&XD_ z8vuT|)><&Nl`AKCavu5xYco0uTyu+P*HCtxz)l5Q4g?E%PtX zA_+$-?kO=q2`Hb#5IKqKuh9%dw?K?j886y3hPWju?QB-%B4e+FMbb!#@(5BfJD_f5 z#UyNzl#by7WF1J;A>CD$=x$Dt{9;nBn#OcGsL~n{&&aElDb*|_^+}XDL)2<@@x75Z zzD&ijHiHC*m) zUGfULm)zX-YwLO8&~9~em)Ij`55myA{rXm_iSzYMZb!>B_yro3RYhbp;;k(qODJ-o zDRE@&2ci@s1C)8Wy%9H1H(rZ_jypVgoyLULo}SfcEp|(NHMWA1kq>$lh)YbimK@!+ zB2$nNzk&f9KweLGeCZxab5dBDC)7o*t@(a5sgwG#Yq%T z7IKqLQ(p=s)5y4G8rF*5RKTbfo~;}6PN^r_7;Q!am$+1ygJRfW*BQTmSvK z)3xnv!FwxEE5Gnj-Ij|c;?6+5VHpJ(qP$yNsHdv8tbr=FP*ji(qDvhQh$0EKbhdMt z+?kMyJIiq+Qa#I=^4g-bvFOmYWKwI}US{g5s1bk5^JqLSCOt=YXwupPf;R}8|# z%2=iG{{WwM^FqUie1y`C&3^2)5)tTqlaM!7@0yTD&G24<6NL7`-)f&S3#~>^#_V3|7FAteOtjq{8cwQ-eva)r z6(j;sgJN$68h=>4%v9n<&~rIykoz$b;Lm$^?K zs%E;&o?Pgv+5Y9^)i|&RwJsGTk6{rzuOuRh0gi&T@ZIq+vRWF4>a6&3Q^R&vfW z$^&iWbVwvMbhiaaID{%;6;$&jAp25uI%@EF$#IhhHPwvo$3>UVVByN!mOOIG-MvTf zKX>iaUhR0+!`&BdbXhhJRE&=Q0EALMka2y{ja;m@sXo2N-yoH!??Qb zzHyOdY`&%;S2R?9A8Pad!^+K!zc$n89jCCW_w?)M%DbK+c;UO%XQ^3*C3X0cmZ`1F zWA4fJM(L6mbzwuvE0;kT-z{^6%r@>ijYqTiehXXX9v*~F-Z~#@(bqC%5j8t0cy1Dp zRx+g%)T=_B-@QK$vXJ{F61DVAb83%N<=&cy z@x-O3zCs_>OzE#_b3_t?s(?i1MB#dhHBS4{<`*hx@%}KOsgo`g-0#Q-@sdhu(64lg zrS6Q@YVwAGYOyxK%&ANc7qvBR!!FnN6aG5t;wY$9Eq4AV2tR0zKWf?r$Iez$x^BsT zCxRQgUQ7P~W!z6?ZG{9}uC+=Q#DEl3q%>5eNmCL`#6jPE!}ONAZd84R+otyH_zo(8 z;GOGog&p@EqRJOXc*Yv3VHZ}BM22eqRIDLo4m3dAl$nW*WgZ!e@0+*J%I#WLx#YVF z%c4s`4gqXcE_037Q#89R$rW_fNJEs#3i$RdAc?2#k5Y<)hVfR_ z>ys}PmricYs_Wjn)@AhXDJi02x!ag^#HNv^(9>ZKeguT*G=Le3V|4*p&7FJnKAxX? z8-X~diE8-z>B?N&a?HmSwU9H7)N)=F#U;Xm(Rr$E4mQQnmA3P(h5D(I(%O=l{6Gz2 zzWvfP(W=hLu}itt3)}}6cKbnjUMa;_JgXqJ*w9mxWfzoEwue;WLV%@3Gi-&V1F0#I zJq$-|`Cs!W=jnr@t^Bdu=WTeMhZ{#2W|!Ve&ac!4w;;JDjN2_%chnQIlhRXR#}}#G zcNt6CAkh)yqJ{TFXbnQ7nH7_e`0)a@yWL$4DVb^HI=@d%WJ3P{R8>uJG)F>J<-NQz z3J@0<$8+3`Y+G!6(r*m54)wg!RwOm}h?bt%I@h^w27^@{=V#keyDMRnb9X0kMWY0* z<{VO&2X~V#-<#p~H{`p6)^fj16>n3dDQqF$t)`hNkBF5t;Fl*ZmiFpgNhon3r6hjo z9uc9nkQRcn)5cihk@7lKW4RqBPQzgdQ;1DcfYx4cGng9nV$xp4Bxl$s^ByjaO0u9j znl{p_%V+?W+apDtzFsCBG$OS`W94zEDYb9QuHol=$mDl+}K)CqMoXFrf6l9 zgEcKjO$WX!GC8;{V&*GDjl)ZIFk?ogV}EfS#pYeXWnVdTaaPVhG(l_`5 z)F&l8q4<^I&}rAfbGi9%WvAs|LU!+N!;gl8S8+a^6kmR)HhUe8(#(-o^y@ATe(@?Y zYEr1%08zHD5=dNj**S6S?;_(%{ezHj`k$ApQWBf3zF)z8DA7n*9tXhn1}A0X z?W7X3?FeZsVY-db$-`sdx8t>*>h1iEV4=8JuTEQU6sbxp=mVt>uXwX7u|0OFLbJOuSIriUvdRvv!SfzD6LZszm1 zB$~ET`nne;p+B%ejL*DyD*3w}E$ULc9mnM)pss`~%i~b}P(bbnx4{m38ZQ!$Be-mh z`6bJGR2AS{Scm?+?^5RWJYeon@m#l2Oa?N0z7Jw>=tTU@5JRR%Ym%8`1WW@pTp8t~iA z@kqc*8p{VYu8~MU@YC>72YS5W%{dhwBO>SYIUY*}jwf_Kv zZ3OL5mgLU3O#w0UK-=G)pUkS4PsGa(bp7g@ zJ4lo!VH-*5;SK2&je2`=y^+g>{*_*w+GSgvKJ9#9%Siq$P&o-c4_az-Ma?%1#oA=A zdsHb1N3f2uNtKqeBk*l4jYNegNePsPoZ`QzuH5RblF}zR8JIx*Q);BHG+L;$TwTf{J)fMM9_b_ z>&~H;o?1^ruz~kT#7^(Ja44pB0pdbRUv+sVp%i9*YF|i%_xbQB!R7p3DKTBn<_nlh zNCfmGsQ!t>-{x%zto#E)MH(u`pTMdq*WNBdMz{{YfwxC~YCalm;_s??@6ZH4can#Y;Nn)P1Jm@wT;xzmu?lkaGMJxF80EzoK-jUb!wpXAgu2w0!u{I& z4_G!Il?5j0iF5w{0RA4xt&??Q9?3bi{6s`)=*SZwMDQ98=Zu?qO|q!Nlx4U3*$oRZ7D4uW0u8Poz>^hn7*h ztBVFh9JP~V#Ba2&_-`xq-{YILNObNcPfu|=_!B70%V~7=vSx1_+9w^ev99b%^)CEV zTFXu!;y6N-N4`Nsz%IgE@d&})c7tUIa%T+Z{9#_2T(29bwJBun3vFQ_4GyFTslBj~ zn}wjq<>9#bPO3L$mp3G-GM9mhdKGoj3FSpJZoWEVpK*eXn6ISZsLh z6(7$qeKZgILC{_;R>ssO(kn0%$Bx* zBX*t`wp9>SQk8oq0f^g&Vv0=r)4i?_vY;o_d`OK%$Lxt&iHs&3>*$tIEKq8KttDaS^kIcZ9CP~o_JI?|=E+Jbrs!7Q_i zhzT+?rR3P~us}4-gn1Gr_eXs!eha?3Ulib@r@)$06j-NL-@_o1H21-5wU5C<>#0hZ z@y=Ao@yc;cM{pEvBmffH%0{2&n3dLX6wz}j(5HnDK4Yv`Q`OYo9B{I&(hFHLA8bVH zcW{=jL1?Qdz)i9HVGa^hSLy*KFY2$vtd#+e)+qbi zh<`wpFY5!r6Ia!Xi<>1##;Ua%B&i>=2X}5v@}zEX4hXBiR{l3{MB-HjeF=3SLr-i@ zrTuZBY>K<}dBBh+-W>Af;p?ONAwR|@@m>1!d=WK$WN{W3{5%2T2Sf7+{elzxUT+mA z`5&nid+R}EW@eX-opN0#P@m|Bv$ZBXRKMh{91>aj*S^%nHx+J3=v7odWK4T9Ulh~a zt?)#uKDN1kN-i*@mxlU<>GueL_I!tmh5g8U5)Dt*-Isb5~5RR@mG72OQ(?3ck1U)FQF0l3lIvHEHBCXM$c z{S1D&C<(E4w6l#a{Jzbo7ymVpylpas*$U{uU+fBkg6_3 zprf4s01cLu?e}gcr?v=hY+Hmg`+|3E`#*|gTz;%w{{Ynz80Xih0iVTBX|toRm#Iv+ zDjwamuA-_J=5C14(Kep?qN9zd8Gqd2{Zkx+}i)S9dAM51~eDLCtbx13xg`YD7Qu8z1Bz;G88Br z-cwQTbLVHtFY~r;=|9%YvSokHt}v~^SdYlxaQqUC_2=^xo~~Tx7k($Xi!G@3FDb}Z z{{T9$#KR9~!HxG1!7KB3dlw&hM4#lIN~%fuo=nJ}3hU}$?+c&$)h0Xidq3oFxF}9u z>-o>Tq^0F~YoFfbS4aH!+^ZM=06o(aewku#po{{X0C{;WyXXM$%mzA3?KV%GBZiWU-7bdybqkFZ6x zjFFepimvQFiR!9aHroCjGickG6W%XqPGQAgK4bLETn^j$N{ zl~P;;IF(g8L7!Rc8f#B3aQ5b`g`%FNfXn&U-FJ;5Te%26wFvS~RQ03Sylnzz~3cOk-}aR*oA5=ytc6 zCY{~)sP^}dSZUjF>Ut)Yk*ao~2lWWaWIJjA(pu3)w8Q1^BzS3Y`_vzJ%KVx?{^$gP>JuI%u(wufyGA-9rSZe=bbdPtCp zW?aVoKvs8n`R#}W+o=IfQL~_?LVwLdN9qv^+TR2NC6mOj#-S)k2%1u_007hMi0I!0 zL3@uB3y*q<>Z7;J`NCUrzR+IW52F&+t;IuMh zEt|M2&zSDn^m^3%BHEe*Rku!;5}o#SSbjMyQj|_jr-KaQLSQ)TuNmC-hVp zj7-J!(gCW7bF5FDc|PdP^Et5A+^)2Z z3U*)l0Zybcl@$b#+7mevbvnSWW69MM9n5Xfsg^i9J_PQ>>qNZUBlVFT%A9Ws;e1V% z(gd!e#PcBZB%}VcKUEvAQM7T+NwfoNb5HYS`r<(HqwDIFo8z1KN|tSoAOSNyx(~i5 zsVX@n0S8fxRok0wRZaZ59ur|sd^_dVP}e$}q>24ak=uG{>D+9*>J$MR{vpy6s-Ead zXFYAMsk&D)Z9=Mw)}@4?ZXqPk!XyBcSa<_f`RfKq(swybT1vGWq(59CVO-f-QhB4h zAQDPIJv9@AXFQ6!Sjg&Dx_{U|SAdxPky-Q#H{Jq_9y`n5L!z0m>xl%9;sKx@ z=o<3Sh{^Co* zgmXt3bun}6&4QuOTA)kee1TWdz?Of9{n zN*~)dms4M|H@J{azMj=DcU#(9Vx*QfHlPY=;z#aFo!?`uVOheoDo-lqZN-UufUSHGTwmF*FY@LKY~(yw@`c!M4%IV8 zimK-ssVk&?RPGXlu;2r7Ouj8*j?)J<%Q?ZQ^{pmnyzJ~|=}1U*I1l0yhah%A&fH^# zUO5jYvCy1bA4^NS)l=1nR?}}fmQt;%Z3KZ;sk>dv~2s;Z+Z{Q@+8R6xeuxOlXvyxNt=7}Mb+ ze_>Q_H=;#0d74q704A30b3ze04XxI?nNPz(9%FlC9guTR^B>w*qvK>I(lh@6rN6K( zGobAEwo8b#J56)WR^?qaJM|3|QlW(vww0_UYo!EAerHaR>(>5TW1SE+H6z7%4d0uY zXyhymZibbnt2}v&AH^{)Sj{Y)e5%@fX2n{TtV*w5>ckeBY^<$FNuDX zLeZ9Y#4zX%mDkf%?)WR&F05H3l&glnf9_V>zAG&jV(%IEKZw(+j@8SSdvo~(GGt8! z<%(n=?kLma+EedLZoA^quZAngzE^;$|I zE7FTsS`QjKlV}Qc1I-Q(jR(k);T~^wZOq4 zx7O3urBafd2}_9kJ`)>Gz_{t%Q^YKPf7`kCXVgMva^)}OoR4+djfq)9$w>a#m+XN% zsvec|h*mwRDU%-Br=79&D~2)$R=rxULMi&{YIbf&`!$P8XBSwKRZYxa{<0p=*r81) zDeD>08LHD2!-h&gBg6sjf?UEEH)P)6~pD_E? z(CvGrfx2qX&9vU3FnDEv?#yo*JwV5DcbF{R6hMe@8KfPwl$C z^#C|P$G?Z}oDbK+CyV>6-_}2ZiSrB9Cj$3Ayz2p|CkwB*D(hlPW8iklba7t2Hq>oN z2n$*gHI~$#v5CBK$7MQ<`AM(Xcl7oxRhStJXCuC9Q2R#Sp4Fv^?h^vIvc**fNl#Zv zR05eH#03CTyW&cW=d62N8CYJx;CEg*E-q-vYlR3zSIs!#N{TM*f<~XjK_~2-SPR(v zPzK6DxL(t9mOj$LKRIZSnQkjU{jk4U&lJOI{{RGG?sc;e2)ef6_AMWP+PfuJuM%}QM{ zDaFpX7Y}ETSgibsgjHnKn5{V$>c+0Mv<#=_!d9@R=@y;G;To9DcRn`W?cB18)7Gzh zQ!{m935bDR^au8+65GT*z2a^$UuxmnC#xw)Qw~I|Q7f_>sM@b>MDCz*@@ z_^mws(~zBnIp&A#Qqw%Nt4nC7v*5Vp3n>F*sJ0JK0pJed7GMFg&zQ;IRC$hS$(-7T zES8o;{{R{-;FJFVDi*3EZkMclx18H=lvxD=sA(>g8@^qCmC`!gmV$#e>KkzfMHx(c zwTBuZ#ksDEQ@HnG&#&2PvF5YKEV%u1h<>{sw$f7D=wGO8Z9}dxOxjSTqyZ%nI33V} zYKZu$N~R@mKJsnNvenNb7Z600>$J3raL9P8(*&Qk6Oo}W#8Mr1J8~^@7-FjXHdy#j z+G(kh00&J{jbbOhia}DMUSZEKSyl&;Sub@+uAr=7kK#1aJvAaq3Q27#kr7vX&|6i1 zi!KrXsIP5UokuA1CJD#xvM4c&rN`FH-)~R+j@Xq~R+SUbEtXV?_-=~s+kn309e~uW zPjHq)YUdMIQ%(M3pxeWLu_AXN%s0O2`-bE`TCJ;Et+$H5Ll&W${VRSuY){DBshMqN zO`$9{l@x$JB`EbKGMRgqU*A4PpNEFq4YuLO?N^xlQH`)_qx1}zM-olbxa}DGf70hmYO$?s#;W{N3ciG-dIgsjD05Z8IrQsD4uW zRUU>rcF>KNlpgTO{{S+Duib`my@AD=djo~W4&*qupCpm5La7*!{BF=}6MxI{1X0e> z8@KVh_6CthPfVs+U;xs5f_extD=@)8fOYGh>YdU~Yl*vCW2vWovdK~o$3Lp6Xms|( z7k1>cMQe_|0zp-swm$KB2Zy;r$A@0+DXhR~E=@DZwnV3M_gf;J4b(Q$osde)B}KFS zX-`P<44-h`K{p^8FMPxHjilCwYU)Ow-(RQTs-JN@qR%OEeoa?mdgDpN)OQ+{@vbvo zQR~p#7O6IeLqw#y6QY$eV+EGFo_R@s%fWB(c<8O50fXZsQ}(Drsry^?miFmsg&h+s zY*ZPNrKBt3CVn!Cms6N4wl(3VrlhX2Ylr6-+S;#FzzVCK^KIk0(qSxW+yVV2~S@VBFVSR@l+qC zOT;HVKXYBbIF!#kpl*E$d9nWhN)^}fc%h}oN#K-S?tizcCd00BB>`{ulpjGO^utC< zFBK>0b30&@$9bKzv`QDrmhiOoo5=o{R^;IEN^qA4f*r2(dtw@^lkM|A`egqAOi0|U z1B#FQe&fLzPkH^YDP<>>?P>nOeLvG2+|{`|gwuezrpSN&ygt|$Exfa7ME?Nv=-2Nt zC+imf0L+;uZru1G8t&h=2UJ42hRlQDO=C$D^3=f*XRU`6B%QT);FW26ob9rqddJ`}3qPCn7`bb{l}TRZb1r0v_UaOAm zxVxK*IxLxV@sti;NungU1^I;3)VK&}s2-*!fsQ3a=@ZV7uD`C|zs>3yf945mUhCPDHrsVbh>iq_rs z(&9{rc?2KxRR>v`I3cdjz(TW}!Hg>zdg1UhM+k*yCvvU_25;(hT;` z#x&UsODAPR1B$@G+=kLp!y#bss?B`!j_wv@JyTPU3sMwJkD{i+K=1&89#7mIrufB6 zC<^u-+3xtRTpA?iKGb8jj4KPw`LhYL@+~Usa5O&S*-aB`TT6PGmR6vErpi<%y`aeS zik{Hz_-=1QBo%1gUH8^{YaCUX!x`$gH3 zW9e&t(!#_(7@9uC@$|j%+|G4O@AsAY{@VKuxHYZ|X<(~RTXDvJ=BPv6+c<-~ZTBu` ze0ME$o&NHgHGPWq9ei4yMGKbam(^6)`nJ%M>`I(cN8K6hua?+bk_{+Ffg|=UU5A@} z#}heAf%brZa?@NZ>k*D7k%524l(GQcwEA*CaWRNyQa3Ff<|<~I3aNPgTKHb+NaG2% ziWgJ}8=D7TvI+XO6-N6Dpmtb^^$X{Gf4CgN$$7?7l+n;NvS0vadd^UnL=jc4?YmdA z=2^n>%SiZo^e_U}xwJ^jQBf*0pM(s$i;q1$#yKh-xp`_lVG3zNHln409YmB%$v&Mr zi4p*Gg4%gP8covz6^r8U-Uo$d*lqg$%eZ01l{;gIe)VTF4 z8xXTHjUz24F^(&F9!>2Uqtw@mR!_j#kpe?c2pQN$5~uxb=~0}LjWSoH3a8RS8r%~X zJGdkY!C@WS@}C^B=wa7`YS}MINdjq?l)6Qwm4r;$P-QM8xZOVA`j7Woeh>DiScFW- zXiwWHy5)#&s3gQZ{$i3J`hs^sS3jEM`o(Le-N}&q;XsUR7e|PH5}TEs>-QCL3iY`< z4O<1P?ACdHZnWXLH__A;-}VXKe8nX2gVUKWH*(oa;w}U$f%fz%u_|ah%{ZR)~R}m zQ9V^N;SxtPlACa)59BUPZw*F#G1{f#Ycl-cAxmfp$dX5ZJxol=PRb)2QxEf6?x}r$ zWaR{h&@OBs?t)d~;79@0p0V9j-jh*P-K)WoaG!CAI>ae{m?FmSUa)_Nq>lv7JL(GW zrfLk_DoLNLv+U^)>J=wRs5RHrX*X0ngv?9z1vT-sa9sXk@l^d&r?me7OfS?F&!%b( zb&o8}x=-H=^#w=LY75tJ0#vQuQ=DoSja1R8eDw=5 z_CtBfWg5LJdr2i{?1btR($=m@>LsIUpN5g$LYvcS;5k@931vWgqq>DJOdwX}SM><{ zAvuac7+n0#LPm)?brOelT$Di6UCOt~0!P``#uKXKq7b_rmp3^a2f4l^z9C z^42)YQiww2R#hDa{{XncbqZdHMyalq%s7R%RyLWQu`D-FVbMn=t6ViticE^V*4u4r zBzS2uW7NBQFz1jKLm`T2)m`q(EX&G!h2r{0nI(!!^ww05BvCf1iJ!G1KBDZ%%#t#? zYQ9|m0AjLDuUtxz~a4^$gD44GSmFJs{QoY?IVl(h|M`0mHUIQ zb>0-<)~V{Q_>&7;YshXdHtj-bpIa(RiKbr%&yG+>y9}mja0f8YiSx`g{^fo%cQ1VGtH^+0z%$xQq+mc zJs-+z<9hPLA$w`D;Jjb`ukuR;bSHw0FWl;H>>rlc%U837(=Yx~x1_5wOTOB;`9!J`(Z-zqAMIh>YyuN=ifM)B9=um|nX)QvEcZ1tDD#XuVL^+H2fl+8d)TF;d%$K}k!ADKiAX7ZOJB zMOC$vCyGG!f+(`fBeACt$*%TBB+HhHg|wGhkeWf7NK!n+jA3qGSo#I-R@V=>ggN@u zabB~P zWwupU-kfwYDx9VDTva=gNCmQ@Jfg+XxHFg?*Pmv4*51bL(7G_<(@mTB6#{b}CCJsH#h}%~d5$dXDZA4gjF7K?Fl8=I&MXt^8Loi5k!Y zxKMrpF&fIU`lGSTvg%3wnF{`VoqXC0Ft69%*%;JUAWHl-=~oQmezT2iD1S5<12 zr*Jy2P)4F5a~)OC&kMK@a87P6sJ`C0JB%zfw`%IDe2XW?ZM96UspW;Wa=p^ftVv2B z%`TA$B`wscA7vDviVu~?Zw8b5m96ix%~i+!;ddX$cth{mFq{)4de@CwY29@#Fx4du zG8OW&cC2wN5j3gyWI)zZoIvKi zPWD^5wDC@^Wyq>B3`dL=XsdC{g|#;%+}dqR3qW<`EgR)8BPqmrGb4cIQFe?m!s#0& z6b74?N{|w#DhczOhAT;!a4TS8wLS@N7{BF~>!Z$7);~}4Os%!KHP6gydKjM8X@j*w z-`d+cEh$i(Puz0n&+%?m4^O1rQJ3k913ZeYvD=OaFHNCvBXRty2y{KLn=N7TnIR-gc;X?mEK#6VDXGe$ND^jwO9^ z-+NgD$o~LLchJAZC)cyV8&3`uiT-frz5zxUEnnih^(Oc(&k2VTciq>fqGxyOiGGFN zD~3#XEv)({ajQ60!?~QR)Kz2k-^*6CRjc<<*UnZH0pb1NC+vz#s5vDIUxvLh!bbpq za9gPLnTy-R1-8TJnQW8S#!+A;DoG9 zGVmCe@taaDcJ!r42wH^Bl9YsO9%!=G&7#Wx0H`i(X=o|!O+J5(zj`Ov!Vk4zg>Ed5-tmUJbUNvvW&!h#2|{sFNNrU8{{VC%k~F&1!UT-9n2DOzGffd?u5K+S z?1#}bp-=G}BX6)mB}ml?Ax2_bd?Gnhn(8F?37onC5z#c15}e!y&{ZKL9F!eVbK|&A z*kP_T2!vTd1xIk{(gr0t6mXA-N>cEMq)Lgl=kXFHJ&K@2{EjV#*l!w0iU1tPe}H>*Kyhi2(QIO^X3DwFI{jQ zN;DmHS?$_e)wSKv@wIi$YpJZb*4$jKsaYyhuRCt3ace3)Nl}?Gm8x{9a4u6NZa^a9X{AkS%^j-oRd?Cb5}EN8D@YSQNWGTQp< zttmV-9--s>6i3Lt)%Od_HtUUpH{`nOra5*g`Rs9|)6!6_2yi-t7U`QoQWB(s0*UDW z&wp_%s`5zi;rRXP;>Wk_ZFP-+A09u4+@G9D{v)-H@Y3)1l&*2R$VcxKry{CC)$Ra^ z#m%I$OKBr1pyfY)zp)Q@$lJ(I6V5*1p{_IJY>$sFS6bX=nYx#6455mYxY2xiXjyP= z1IA_>i<;&KlGdG6eZh)??l9R+08d-~U&wjAJMGkCj?^()3RD=iJ@HRj1tGG3Oj}$@ zd2rftntK*<&lO{BAaHo7{{YlK6+3n*ZMxtc#AA6bX^-5j*mIOuI$N#X%Y9gBpIV=D zTmBA(ZGqFQTXs#g#ii#v+CykZzKAa{FKw>z#x^~r+`#TQRacDThB<2Wo91`BZynv9 zwO%gQI*WZkrCHp?O=GU0%dV+VjGgCe|{go?Cj_(X)cFbd?9Ko#|cK~{n z;O`-KnNe-DTPmn;31Y<44BNI^R)qvpww95(NJP|RnHk2JvgTUADYD93+4k&`7jq4L zY6!@9rAbiwZxmy5%t`-MTFa1}bB0Wtu2Mq2Z>FXoiIwA8I#k96ZL;t{q% zL7-Yy+vgGGxzBBxmm72D4_}G#{k2qA2iXj>DU2#WbKR5s}PB+_0Y5^Vd=b-}X^vB@QAu*O=VmUU8udBbA>yTah;=e4S6LYyL~8LcI9`c)0~@U= z$JVmB3${s*dpXP9KuvfW?4Zu!XwyN;R|;Zg8)XAdfdHtcD|^BG*Phj3*}wb|?LuDn z+IZs<#Uy@y=$ge3XcTSoj}3!tfb-Y% z#dv^)Vr2`JQ7=NEJpSl$jVD|q$VSu+=WO(pNYl!NdDRxKWlNx`S|p;6s=6d~HH&3RaGzxp&MDYb-16m(3d;UO7`=I41=R?(I)XrubfLneBW%EoO@nqagv9#BhZ^<7wP z2nuwh5~5@T;A^jiyvJm067xHhZi!gFFA0nCQSbTCGpL69ao02O8!S_tMm<3z5#|jG2u%SI9Vc%FCu}ENdEl5(WZ)lwV0Ee4BHcw==HGRp~Oq&gsj_B23 zN-CQxL8*NyLronu1x6k9ArC}pJ*IP2>fM#1!%b$oW!Op^cOb(YxwT1BT7Ub$mwE6R zbxI@vox{(h5A~_S5~8a9+WE%P{pO1-u+j1LRg&C`rOm6AeKMxQq=wsM(XG(p)wJji zke^x-@;Go%M<{WpGjX0S?;>QD>T6#eu}X@UGB;}I_bpDS2H8b8bx(SW+%|%N1KnRW zg0)tb81pl^Rc5$yUmWE;X`jDSo1|o-f|(euI#3YhWnE2sY`T-YPxwt0ER^($Ir9vA zXbRBT;I1N?rKW*1xSqPF1L3~oxmu2GT;8Ig!0>9e$xDXek~$o&^vZSIidKTAHP#Z| z_`_!-oAVkpdr$mT&DF4)>KNVf)V+Ys9bk&4PlmhU;glYPzH;s=AHEy{-Nk zBoU;eF=YOYYSI>mC20A`a~ufwt8apLw^znEHa&sj)~5deEXBkB0G2%F^0I0kFQ~Xu zuZ!YQ@{dZzd64^Cev=!r@+|L@!y|ZZC3dbls}uC(=G#S&yS$gVE?RL*r=_smsVpxo zhYtIV(Z-qF*p`8&TT*?>gzecSyU2Z1fQkY=^!yav=Wh*;AX!93NfrAC@2}jq?S1y~ z4e6CmxeX=h6eVFmiTf2Eq}#x}J@~WWnpaNXxPKy2$-C14dd*1?K)+`cuEaR(6LEQ8 z4%~oHx>G0w4zy)m@9ok4|;>N+kqGJ~>+Z*6~h6M0(V0*im9+x4;p~2ZF`$156bcsYjgTjcGGlmZd-4acC{~}8n`KF2hZc(8R`2g z9_LgFEfgMOWtb>Ue!!R)KcqJlxU>0RBv&$jbtUR`kG)O6^L8zt0<0nPKOi>%{fVD) z`bIL+LTvoF(`ZbJQ2Q;pB0tP2fS=hm{0g1EWL&#;1i;8Xm3hH!#_0J%(^3TXdW|h6 za{kJo8?MR+XDb^IFu6{wP_5JHVa~L!ukx&(!*C~5e;w9$A1zX;MPcl>w_>z$d6jN{ zW*>j>Oxmxezjbu8`D<;ats-(Lx+G8UZW?=9<+~MN&icl?aDBt^NRItD`?*w<&ND>m zoPb5iwSJ9}S3hgs9f~SG_QiVr!|_rV8GSbUnWV^R?^&iE%~hJ*hZZe}t!dR%J+P$- zAxQuv6EiaqLE86R^?5?%d*Z;dkpo{?{wcR}`dN2ZS#YYVV-}_K>YQ7RvWkFqC2A@O z)OZ6Z67#cWbGfREgUowIvF=$l!A6X;*j0oPzl<`v!GOaucAn(%q zyGEz~0JJko1OEVyQ~v-`5#5zA;-)@h*$wON{wXImC-_y#Iu$%#Wmu1zqtn`^&JFrna(!*eX2evG+eT^qK^*Ac*$C?)Wl!Th`uAjWC_s0E<=NwIvx0i8de@Tg5zzwdI>tbu@ z-DpZ}skfvy>ER>ojn+T6p98spUQ670_%bjU5YVvg{VxA?8fPmHSM8jUKexP<`?rrBZu0k&vCy^zA^4cI&V0~uNK|_t+*#` z(${LD6&B~;Qp$^}XnG+kRQ22T#yd6Ikj{CUXjiEAmzg*C$huD*QR}Xr#X_K2lQQe+99u%Zf~B%A`IdTeT*g)Nmf2m%Wc(Lp7~ zh-XsE37nDum>$S_52%@!7apuCX(a7Y{2)XQtx++!0E)4U>}5|pCWA92M?JLBI#~eG z9b+GDbkz@rWQMvC;|->X1c@@$Z9`AYjmaiJ4+)rU6U&p^^7g_c1c@Czu{TK%)YTx@ z(S#jD&$ti$+gy zDD@JR&)1`-d=eB0{po#??Y*%hdOI(ee8Ga3p4DbDt{US!zR7jA-te1M)fG%UgX>Z< z=F+qjX}LOei@dd*a=<|YWtqa ztLNc&>Oe1u@?<0dLQ1^kVjhZ0Ho z7xGsp*H=zVg2@_vvGW8M?qQ){V%*{4kX5R}STaTAX!R*wE+_#Z(o>78YllB&m$}C= zyQf7}6-Z$Qr5jXEn#^e$8+Q^lRIgBo;qqt{QHqrs)-0Db zJ_ajYx;{OTM5;s&4-*KaMw?nkPQTWiazt$lnUcErLMD)wEw7ec26ER!1l!3?6cao>S8jH>P~584Da6#1*Vz^K zat9i#yi-d8q}R9pSuQ7b(3$B!ILplXYPGg(4swdF7gfxWq^Bc3r|gP3uP9cY-q|d8HxME5ZBd5(n1T#{=7t7Q;aLm&B4&98GB*8<#$A`P1kV%EdKzl4l)v((ku7o zn-e0V{T~PFfumNmWA% z)hd!;xPVPX7VAk+DPEo$#EDQQrJ+4RK3YJ;1nQJr)Hb1@>EqH9M2mw{PDHfR#2D%+ zI*Wq!JcP*Fe94Yo)GN^aDL;rz^@nn5qe3OxGBxTt!clSzy2hbBJSROND!F=xm>pIp z?l7Tp=u}b6?jZXm9FtYxsGt)H0Dq$#qjSh8qUxvukHJ4|h!M#;g?00kNdZB*`v!su zqE4Y-g>}6D0N60u`5B=m% z`l1nX6Z2{>3UEHp@1*Y=@Rw`nT`rM0{pwxB7f`wpRhwTP3e>nzf+w9 z688ZLC{h*>yBC&E{ggXIoP~M$%W3=!>+b?2Xrk_!U%xz0{m;MR5bCb!mEv!S6VRj| zEn#Y*=&l!kLVO9IvQga0dMLWGBqcwEWS*o)b0>5VKg>uy8**Q0gsOMbdeCaf6EB9H zXWOszLK2thhl&kX$4DQ+Ss&Hfbo~&uP3R940r}79 zM(qCp&Hn%-A(7*PXpMu40apsy{{Zkg;!jV3QOBKkO`aien(MhbP&%RHTQH64ld(;t zTu4Kvs_LnK<&JCr0Ha(J$tQtLCNB`3c;Du)S2l;}9ciKXl%~`?eWE=Fv2EOLM=;T5 z^Y4sCNFi)b({B)RubJ*w)iicvmP)4qBk-=J=BbnV6sY@Kv+Y{h+fAsyIltVj_e-{V zRn|!wJ=G;{DMOl&B{=nNV81E@f&xwgbtW98(Hd;1k>TxxP&@@pJJ7Fjj47s&bt@mF zK=;R%(E8WXpX*(yc1)Aty!vD3R<0V42E z3}c){Qxy}Mi1yA>^#mC7u49kwmz>K9E+nuR#(*1tgkOIoM3B0x;W>&wqA z8HgrRri;dZ+CTQbUiY_y8v|(@UBh2ba`^u40{qqUl=&Lu%Ynu&7rUdaEOaR~t`L6M z`HReZroSU09xLnr04=em98@hkhBx*FUQJzN3*Sp#?I9ocZY{EP{=pID{d3~INI=85 z2kb`w0GKb*0A8y-ld63G0Mdo}hYlr1Y$5(p?hA!o?`k`hcd0)2-9N2S-&F4upGOb! zk8oUzjnw`tqvU_%KLL5kK)fWm<5UG*WY2+bc z8mkAimA4(@{{Zc^aqbDgSquD8Xv;G!w-mKgU4My}z29rydZxCjspGkB zyy}Tk)}z%#&LR5v#=$&x^rLc`PI6^CswFt}6Eia(1Bd={p+8V>f|y|6;<-m8sU=>W zo{1?}hopaO;r{@sx4|#yj`1te$V*;{wo#z}09Z073NkG`7aTF-O5y9`?yc=x3^pb4 zDGenj?~d#9Xqs(0M{qASTWK6o4k&G2eUX3!@ zBTo*R#QcFYyra!iET4cC$8rSTa+R33RJ$OQ?U!F}JP8KlQvXqd@iSROxPD?D|)w@E2 zWFI)?$x)0#xXV*N072F{a#E$kYf1{9BT0#LhDze1Y?{$*-_sKk{DX*jKIbx;oQo+= zv;E3DQw>OailaAvd8PI*Lcoq#YvQy>1)&Z&wWuwblGC z^=6tiEjvN+oLgJ7;i?hxb2(??fj%<*Fgv$SM%s~-_3|;nC`)vz_&}T@c-W=(RWx*z z5LC#s;DzrRdBcKZT~sql1ZMvF$z?hRK26GSRAE9=@zAwl^N@#3Q~ffwns^n z^hof8Do&z8b_=){OIej@$W>?(NH$eo~B9@|GD_Xt~=4M8fLpm~om!C38}obp;6k4SJZ4Zs50- zyn(`{5eIJE#O{7mL8_X0pPw+UF*ueB`E^}QRx@R_QPg@KkZrZMMujOP6$3qB>ARW> z2-?Rgdrs1uB_pH;f~vpdz1ZUS+)p9LQ){j(yU|orGNlv%l{(olE2;`nI0k z%o~-U#QA%11;buQpenBL?nB2|&EoLyC0eTQ7K+M5wM4}l_Be;q>Qoj^( zYiXS6{!+Jn;f}qzfHv5MydEmR{{XZ;(@N0du`9#d4E;A8bc5{CalReJgeOo>y?MjC zXSujNzjAit9l>_hgk7lb_~#ndBKK=iUAo|0QCnq6DKg&YLJ0;eZTqha6Zx3kLI(4& zyMpQ?9I}pAZ>nl?cW@j%%2(F1EQ<`TsJJl5r)z8JLQ>LHQ~&_!)(6OUeja!8J(Vcj zCt>F7V(G;w4fORW&7AXqX-fS*F{`YfK=o?EN7xKix41BaXx4?@z1$Aoa6Nl_}_iGExWJVTa8U28;-ylL5QT zySnHCDZW)>c-17YyMAtRVCBn7N$_m~A|7Mj9|b@DFvfo=_o7Pfr<#hljNYlWb=;DX zBkcH0H2H~l;-qhuxWV~;@{w}R<+;N$#x8hPLCP1Kh8=q?8_p`K#iHL{T6Y5_07jxIa$V(hJ4;KA3ee*_TF%wn(XawI>-eiH$<;3RcfrW#wpum3-@tbJ zeW#x(+HFPq<~oz|mfQGCNg%k!jMb`I02ZkvCURYt<*Nbms82+Ts$c3kb6pusuKvTIwTpI>*1tXIp$ z`y{;AUDk(#wQ87Y8_HX3XoLxPBxxCImtD;%b3t0`cx~Z~G?0=@SE7Cp0cxAAY>xh&m>{gdO~mm|72o&`8b2Z;NSWj7v*OQeqpFZRN8?)(aOG(M#K;@czH zPa)zq8i$mMTFTZ{TvSOPF2VVSXFu$qh_%4=opjc^wA}rQyE7{zwR3>sPkxKOSj-;G z`7fPdodNGTk8F&{uA@ITkQqRB>F{D zbSQidGO@L3I&>*AjXnexNv_3HW@HYCjv)b)(Y?o9f|}bZkLCWND{QNrg6Uo}*nQfd z#a--g$K*Mi@}Jm`vyqq9bawk6Z;JOgWLPqze#RX6YhB?esQ!6pG47XOHmv(4U-g42ey_ z&#?&ph?D9sd{lo=j~o=w+v8crL&yw^t-<<>RiTHUtztGw5x zs0xR>q_d9G;2#lM$wh0(aP9}>oQn|0t8z@@y8^px~w~Z3GHMQMl1x1&=#Vxj6r_7eqUqsh)IDqUSy zv$rFI4W(Q6sea3q_r$6THLB~5T`!i*e(!9)+U<7=3U?Sy*3VegeIw2#vf7rEI?76$ zQQD!Q8o^J)U)(d1;E_@H8jrnHeRd`ZIpcWZH1Hyaap0|gDECG6P3{jp#yLljGK#By zdxM$aH~USh&5ylcK8a`i8nEIzF@1H{+& z1zqyYAHIy^BFXa%%P+pmO`5eK+yxeC+2@)AIo z;8;)+9z&!uUdXv{+#;FZSk`O}N2q=%t6ik$ZEjQd`*kv#_>$NvB%djrq6HpZOsCUUyFGvhTwIo_A1#Hz<4-x~xQv!M8VYKa>6 z0H^4&DYMDB3t!#dpGuF4Tv+z`#5H>En+F7CJgt?cfBEG?r_=bWAGR7^^rG2=f;87I zWeEvNuMZRbV(XvN9nSRcr)PptM=a$E+M#k;mrz!(7?8|YJ_eM1u2;!tF>_r10MA@= z=26FP0$Qi$Y{j&Y-NnE(m&N@t8>On9w(X)+rRUuKkz}!Zb-8#2B*Z1jOaKJsrS5l> z=~4q(0?0lkC|6H-hAs?haZvYr%XFzoMeQ1N+`>uy5Z8*SV~vS&o4oE{YvNtehXQ;J zIR2PuX$!ucsdbL|xmmKGQ4}wM_k~rUemsOwR;ru>?Llr+SG)sfH!S2 z>!b&DH3CaK882C0x7U%>-0yCHm#+0SFR3G-Q?C`Co>PG1cQ9U;Z68UYsH>KhIFCpf z?jR1Dl6{cq+00R9RU)Rw?F|((4O5VIf}#gg2S*(c627G-)LO0%f;|OEU35MXB8?%` zW7W>6fZDd6zX^etBNqHgUZ&?*REO`&q#$5YbLhFdTbwIunwcbpkQJoImXM+}!s?W> zak8e>j}K7E*E;Db&K8;{aZ!UUCAT*L+vbWWONrO~FR!$_Vmgp|;v`^6XV)Md2G zK?^s_qUfDcp$qj%rg3Q#ii?5)R`4(ozj3B7S)Jx|7M5BrvX!NEAPM{+&%@3kuwi@9 z8Jm{H8nwOTw$>lUHf(*7=($$5(h!m0l zPK(j!6-y2ziOhI(hY(atmd3tP=wp{B9i=3^lh0|)Ll73!)|G5SI1Fyp=Bak6E(UzMzZ%iX4r`58+fdSJGZZl%_cWez6<8->D!g| z>xXxN+-?5=xGqJsU>JRMCPv;Ll3Z>K(^6Gczm+M}23%CqvZhA#{{UxE9tn=hTS0c| zByecgQdibp+l!v}9UM?Jy}&l01E<2#`)am@?qT|DIO5ix?y#oPqcJR!grE7*s{a5t z#s2`-XS^>aJ%N}10HFT>aYUTvuctLQNNc-nDe$hN^XfGbFZ7~z_jWw(D?Q;tdkZiB z073r%`${19KK(iAel+(_hdQ+MN^cbF{{WjfQ?h+8ddY zbP)EMt|(MEl%9~}i59#m#FQk>I%)RA5^kK`(_Q)!qq#HP_C#eA*HIBR#g!En$S2Ao z=>;&EbNOr2kU^ig!gNYuxCqF%RRTT|q3(|8l)Yx5#XIRU+Cd)p;R}*=TAvmQ_0u|q zlQE>nlv!HLK~=X_;lWMxRSaBd+d&$UtX;N7xK(Cc!or&OOB%O1v}x3NeUK!ORUHM= zVb)ug(}F@rT})O!+=X<@z~vxewdw>>ysZ^EA1JW1JQY>^N9GC5aHn#bu@9ukD9&Oe zowQH2khm*v!WTB~a-nd|OGz4$`k<&=%ocwrIbBs3^EM}4WV=;3;!4rXvppJTj~CtlQO4pHi2j>Rt-zbXUF@e1?jSN&=IrUx%7PLN$Dy-DgxpS;Hhrn(SNiLFwJ(CP1q z86^~AFaq>dxOCcQ5^A{euD)r0MmZn5u6flI^c-4;z(o4=hw>F2RfO%z_k?1ck7T`a6#%$n^h=#X zDr?pN%t>1hSwrJ(P}dcvip}@OneZ1TH-cPvQrwCwaZ=?>&8m*rRp{70w! zJR`XZMZmBR>rOk6uBz|y2Ko~T$Wcf}vG%4$1S&D+ zYzwq#hKAAVIRnPL(C;iy}0EgQS=q2i|(~Z$}QUv^^2q8lm zm!Hd2rAs?O2f|S!Vxmr}^Z8}~96;m0jP*i(=zl6ssa*_iff6C0qyGR8IPQ>6>aNZ^ zZ$I{9?hsc@Bi!G5y;q^e zLe`#W5bLTTeURI60_dmsOy4np zuYWeDAB8C&bRQhCjtVY|*J!-3g#{3eJ<%|hGLg`{C5k+Xl?zVk37u3WDAQrD+Y^GR zyRmcDjCM`jzY*}dIU4DmzgErLAx#A>l{D1LeFchjy6eSdCqh)T%iSMG_7`x&TFCAT z;y?C}$-LKdvKVZs%NZop(zVbl);^+Lypg3%#FNncnV0JN(Ya#HNq+2fDxXd(aO3Lbxro%>v*~x_T%d^y8i0 zmOM#PGyOsCgTE)eR4(KDy}#2FLyHGea6#cs+%uJrcGw&*VuZneA9thZFU3KIKaNBC}n(}|iE zBxXEoE8Ow1{OoqC>xTAYuH)%S+5^RCMm6;d<0ffT{kOb~8MeY_`EMZb{%D)^F&gY~ z{Bp14YF0XKX>sF&L>}m?WI1@NIu_kgL#kYg@|5p zTjn=S+*RiMYlDoxMaGBj(6(A+IEOEnjbc zt{B@n#GMv{z`6X?(|R;-;imhA^ZR|dF>|e`s%e8wOzPHIr`}vCNl}n0AWX}tlzj1& zx--gNc(10hmPQr^@d>J?#Js?YcQT6YuUz_aNZq0GhEX`XRnY`T6GYmsk^%R^rj(;e zZ@XQbRXv`~TX=bplJitlbrotrQIIMW$XNi9r25B3$2rsq13(pV0D8zDwLK`fpOkJW zLzLWodT}b~Rcx$5`**e=mg24t0;=WBT1IGIKEqVvV}W^=ph>-*1F2G5PwQMx%mu;Vp_jUB$L0kqQ}*S|T}CAOQaY75 z%0Qo=#}f=4$ASU<*Jya9>TcXQMMwNahtQWYnPb&}`$q0Yx1B|_fgBm?7^VV^>!ImZ;5=N!>B@T##pv?HdUrW9C%#V6}e@J0UskZ8gG0LCLuqGB8LgT*`b zSv(NU;dBkm8s^jNB_=!Ro8pI)EidwvIx)+83ew`x_Ygm9PaJIpH|pho1V81r#DTux z*gj=Q6a6qp^v@K$viKFqZot@^w`z9Jh*H6Zb)g#7DT00gu7yq}$eXvyRQugP{jk4X z2a1#Q`;s;&RNrtm^Qvr?&xnuxa-t);Z&dsggZQm{1-83jYAB5xY5P z(YllTa{1qsr6Jts4br54h*F3e6ttp$R1EIpwI_6AJ5KQMT`RlZAf)^u_cZ(|Wna+( zN!>P$JE_~+7w3W`=X)Grz>^l5r%3Vi(m!krJG#-lQ3JAXPYNV@p8D{{#Hq2WmnG00 ziT$w~`_j^yDOuPzhjJ1#-TK;gkxhoXKLVwoK*zl8iZpqDbNsNBKgMFE5@C#NfW4ewLXnZf)7F51v+0i*06KbjWI#LJsg7V(7XC%*X_i zmH~&@U7L@OfmZLweK6#$XtY6YDyElWkQrNqq#t&XcMj^Zavl>0i!VZ$<#^p{LMw6l z=TNS@OK%@lKX1hzgJ*0+V6QyAnz9g|xn)45#V+?nd?;b1s%K3S3>w`uP&iO8mA-$; zjz!3MHB8Er^G>B8>-beK)fbI$3ZEKqTr+HSPrnbMeIRBP^3s1 z9d(>!JI4nt+#!rQtsiXXVY85#HdL)WyYfLJ_!Odl{BS>FW6WLU_2-CNqu_Y@XKzbu_@ z_KMWBjl4&-T7GHmH#_8BL&34K~A)~rL@vpOKC|B={-CnVV~?Pi^;iz zz#h-yv3O50ZET8 zZolcb@BC7mU6pY#)p>2@-+#&gf7NOy9$wlGB|`5tuTIQp_x>qbpR-OBPOsi>?)jv? z(tlC}IC+0(>-Us1{Lsjb1KgG=`#$aQBlxwKH$Swh*$;QZkqUbb!`JU9bLMVRx)JUP zR-d$P+yMc`>)r7sR|kjcA_<4G@$DN|?NQ0*K3ju_xPA#qJ*jro*GpsWOQ!3EN&Egi zV6oa*@8v>2%v`zr-*Eg9ly=wKZ7`6<+o)HHoUDBQjqsTUEiFfTbB>^ zy<<{EZar3>9^=dNP{{9kN~z;6CV4J=j~CIYCqUvT-B};%&L)etEI;YD z+!TN18AJa7HQw@@6&yo`sZ*rm6qJsm^5_2m>O()qtiO;a{{WO^{{ZSc-cdoqmX~H* z;$^%2l-nw`{{U46BR#Qc{DBZVqWpi%ciy^lx4Jr(9I5{RWR(={Q;=RimDi+@e5BZh zj?}Y1kb8o8?;F;i?mO>BxRI=vw|-Nw@@^@`7i;~Vl}_8a2MJB4x}uu@0E*nl%G~Yl zGqnMJqbe$IagcE+QP3E#J-*8)nV7<84M5^JkHVVQO75?%`+?W3=*;i|*po^)Y-{^{ zUb-d^a(r2p_8Z+*p7Xy^8zmM_%j4tlyKVeOw{V(0$mLb{i#`KcjpC13IBP1X zx!*Bc)5JLDNqsM+tG!#!scTtN6%Hw&g-d8U2@wvzyanL2d^_pH*I#k_)3FwAYg`mLk@qexzfX7as$bL@{OE$X50wPR@(EENyqbzn`tjdI z>Ph;8KbaM%;Lb~@9;HVexQUH6xk|ES>`LM{S%J7uZb%cOlp9M&aKy7NL%vtqT(Vs7 z3p=3EtqDrgQCTM2CvKXB=@}b`Ha9*>Q+L!Sh3*1Ychio0ZZMf(+M)mL_>*v;Xv zr{$ka&)Ag+sAv7gJPs{a5>^ZP{#X9hC}VA+zQby(*}Y0-DN*s7 zM2u^34Qt^9tcGdjmjovQs9Q4l0RI4ovJG@BJQZhM%b$>M$tT$j=rMO)%g>VpXaM^m z#x}0%m16sA&3OBcZrQC~eBDxo+LDnLKa^+HD|MIDw-CL45J3_qEOA#%fz=~*TbH$U z%K!&uZd2bk8`@en`*t08Qw?Z@r{!7-OuCY%1Zkb!z-R)?j?pePDItCJ;;s|u@zrSq zqTH;d2krpC?e4?GDP7tP8zPT)-9;^|akHPyDlt4MMxSBs>&O?P!8v=Kby0;|8MS6-+NYzT`ASO=okOWm zjn&>SvKer6prR{{cYSThsRe7Ccpt*%z%)Z-dpamyT6Iq*JDIR) z>v7wUHi?xXRdNT}G`AM4?wJn-SN<^<1#-BJyg!t<_J=ut15Px$+KQHGZLd1F)Z=vP z^!-_=CP73Qdm~YeyL7DPaH7HIK)08Xrm8?z6^O7t9Db21_P0OV7Bp#8X5jdu=Nrev zl*n$=O4=6U5~X@5(Ir3~NbsCKQaNi)lkH`BeFqfz`X||G`HSmk&Dr(O45q(fd_9O? z=WlbTp^za`ztkypGqk zSkiTQA9ZW{m)uIRUOr2!eOP6ww~yi}x6&l**P~)R1+rWLK;!RUFOwFqxIsZ| zV)PpHA16;QyJUAY&+{L8vM@vd$3Qs0I4&x2OdYBks@mLQ-l}>woOQ&SDq}RZg0ckB z;siJ9r>!b|>auG#*G2K;%s*S~7UPvl^YvcCTS4tO6qNniXh9>T9mQN4XW8l?*}QmF5Y?$pfw<53aHF+IJtApFPf zL94q@rR~u}^B&CdOE5o+<*(&w9wtGmfedy@lv4hKDdK%Sg%AGTevy|U{v(7ldlB+Y zWnApFPgK`W8|kJa-Fq0j7PT@r>XTta<@L2&P{2VwgZ&BI7I zlkZ72xd+kR#L0ZaoMT(hz#Ow&9@{bIX$;~yZrV5f$mnr*6!=9~lo)@~pDIvIhrFBh zs5|&kRgXRJ_-bCKObpwGotHY!=zPs1?){-GueQ#@YPSAS-<1m`z%~l&Vp{R>{4#WTgoJdKC#lcEG7h_=Zg*%m#J;T69PpS&CrwFmo^ zDxkHy?e$DVWoRgX$!oGQ&(p^wxS>MB+$ITlSMDXdp_%q+x)-X{;@J?0`ekvOhd z7@gFg!j|t6o|FCN;lbfV81uS8tom?wdqN1K#x0kWr=g|2QYl7$LCuikBU3{i;py&7 z>fXdV*S7ph;Jcm0BmUBsrr!7?+5RCvsv~oNO4m^An^nE2_j7DQ+^_BLD5pv~l+d>S z0RHH8aMJJvQ@Y~1kJ@BpR19bv-)Hb|*$pzeJ zlPL_VIKL#MmsLBD-Vi@cG)mdpHzVE@tu^uYAHfwTDyAy=oJ!`--^3Sgu56EZQ$UED zuG7UUni6~0`j!UTn{$+fJ`ObNfAheH2^4N?NI?GpHqxSD#kLeWr+A;-Vr|gwdj}AV zwUVWO5GnSMpnp^q8X8Cf8}s$9TXgd3CkMqgh<~RGp?h;p$vekSz80!CG*XE(CA_Fv zYlw4Vi)u&{*Wwa^=XFI9HZal2V+ywK$F19dnMBrQ)R-zDLjt*#^(oOqYz|#1S`FwH zCJ(d5B#2ct&QlV@DGmPs3@ShXJf)-*gIxt7WCYWtBgaAg5zCQ;(>A|_G~q;kEd)YX zLh7T9*=e28OoR^+pz92(;(%5!XhPRgrgcR1S3&nmC5X`|ZBzru*M_RD2)EIs`Yh;V z47BrQT^X?1(yQPNG`10+pq;bu*cX7}^9pgNjqVFcU(x%4SmCC|7Kf z7f`CLm%ar{f^sCQPh@3o-|;K^VeqTt*xw(fp*1;GQ&aV~ZJI$LC^4wP+Wa+GPU*a> z`BWYC15!$~q(Nd$qfSk~ik(A-Dy3alrn?l$5ri zDQagp`Qi6KHGMkEM^{vbV|m}^3VO7oKzfZdCM)fq{aT|hA{`nlfV--mwKh_suYn~) z{y6j)TuTD;yOw-(QTZ2r|0R;zz5p7eEfk3d5OpLl|5-@^+EGbD8bZ&eg zr`!Jk0~584DIFo;jk95;s4_~ymc1d(fuRc}%f|${M}{Tk0?_Mi*>qh~=%!(B z6)4I;$v<>tui9O*^&CXOS8J7rAq!HKPNbwy;`c^Q;?3Z#)HiIRZ#!tYf(oV&?+<)e zzTN27-Lipoci^Fj87@wRMqd~@63E(rX>1vRHo{P%XSBx8;bUi4QCB~s$sfE z8O2|9VLq&fo{+(67kyH6P;t#=UeJ{lpAiN&*eD-+ME9cMJCplJQIYA?!ELexZlE{G zeZCH@+qq#!rjjtfPSl3=1m_VsN)N$5HGjq-KA0g**(DNJ9C}3{pb3o(;XaiVE;K}( z!B|OYxSbkaWv_eu%+WS>%xF^kgtAf+-<*Fz76)D%0eND1F< z##sqTLP7JFNV1OCR0Nk!qH#Hnwi2RS(@E(D8(;zmPpT+v4TViI1H(zfdSP*$$VTl8 zXo8gk<~nH#7?Y@urjbb>geUsObA@bl-651O4Ju{r@jW5xE@ThOnQlm!`{CUb?qa$5 zaz}vnfetY)4k!}ZfRO3pB`AzEmDNC&W!7->8b#=7G9EtJh*1)8d0&D;XD=9UB@%NL z;Pwx6Gz2@%N;C+N6V?g=M7$&pz8zu7arfk)cmWBCD#nHB;ziF0+oR>aX85?GekDMyWn? zJ_At-6i#U?&zqls`{0qXii@FDp$1z=Q8-jJS81&P^#BNwgH-1>h-Fs^NGt*8IFp#B zAdSjE1gsx3IPPT`jR3o(w4D6){+NU~r*Z|ryP+DXU?20KfjA4fLjC#EU)E3FW4h`N zk|?{_ROCUI>4yt|oGpqb<>fO6aG8$!guhCOxqnEQ-_xm%`h=fCii_|jUaXIVq+&N; zQA_%2EWCgZ#PrYE5kDbRqa2B=Ap(EIpR-7m-i0$P2}PHY5BtHVx)9_jrEWZUB=zKa zlfPb?nB$TjDSu1V2mH(iictM<(USG0IJ&P!S`asSgHLJFC%4b?ofJ;ORTxfoTS|wl zMEs)a*S_S9Rms|+=BtnA#{l?NvV08S=J}5&ifQcTdXQ_*>de20u`Zt!qn+k7yb9sj z>>L8%X7!2yjpy6CApZc!bNHu!iN+9aWwp*E09CJvR0TJBOD7dOJ7yq~-n(6%CvNbQ z)2s(Rc1LkV$BTp{T9I?8p5_*3)l!Npi>E(zWehR36BerX0U?qHw9EFuPGE6HnVQsH z%Pr1Ba6(b$TtNM?2#rk3rXsFdDtv2c&QH2x4IHz-#Teej#(yC9l#Nl`4>Z*O0DH*SHBNu_$piExL=Nto=WODq zZ)al%{IU1`DG~l(ON)P>4rPFZ%-1YZ*618h9F>n}V?ll1FPletUuwr$|wxqB?3W_v1? z_B#q+dU%!J_G9^e<@iu#FQkD%R^+^PI|<8r;l(dn&AeOpAsfeX$BdC`t0&sp$Lol` zitz1Jj>uWJ^8}*S51P6@8zn*Z?F03gE-Y2+v_ro)WIr%R5ytu3k|M_F2gK|j^r7bo zdP-r>*#YweLzL|rCAxKdI%z3?L`;;U zt52vVJfQ+q8)!R#NR+%}BLQs3vmv4>%G;SWV|wxxk;i`ydx?mZ(lDK>us0{J2)%zmI;7)d2r%_^I zE(wj*t5NrVcNM1A@jT`1{b`+-aPKhUHu|iFvmmrRO=BN5Uqs997mF>e_WdM?xMB zwq>Wo6d4ZVo+--@3Lmil0KlvKcWT?s0F!;e_K)B~r1_mf(=4jL>&CNE>3dZ=4k&|wyuE)7%*7~juA;gCoueVeLs3u?l z6qWd&2xG3>WYF#fSU(nT!DV#{eB)kHvcRl+MIzeRjl({K+rFVbQI z1VKW2r0AGDk(O~4lFakiwW{m+q9j`tPqtZ?Q=&bP4c8-8_9og#x{g&rUBcO$c}O_x zt6Fq3cu4+;&77+$wvFva5n7{7cO7PED1RphXX2;y{{Wi}c2ICh{w#b^b$2636qL1v zFn4&VWnZjA8Qu2bUNw;;*5 zvnFYVK4}+gRbuK}VaF7O6}IY1n<-S&Bho9ZVv18!6D&u`?!{Nj+qgkJ%b-i&kUm3h}D;(4(<3pO(`d(r=!=(|&zcF$KNgsUW?q*AW7 zIk@6dgk0#`n<OHj*5JhR@V{?SSuZd!pHpll zX!x1?7PZz^y2(&eT7xBfB_7@!2&i5fVIXRo#`2`7?q1n`0TC7z9;=jBe8!1#ehCN1 z?}#53iP5@_s?1JsX5gDYb9uX{*lIG1J&S6R3RIeUYIpWb`KPFlG|OG`GD1hn;61y3 zE8MXW2gwNWVLxq8;FU9sX5GphDx0OLw(2VxqH5u24T-5QGTheK@|c=Pi(I{vZn6B$ z+zl3c6L%$+M77%!LaA02acTQq#)0QUYo<@BP84(?^$~)>UDc#`E$(8_hld4YG1^L7 z-KAEymK4S2nRImqdI0leB>jq!k4?0Rv)Z|*!CZ({+~m zrsXl}1gWro#U?!0x82Jl;y$pw2QoS0Y=eagmH4p^KZ_-Yb#{iK_EtPnQm&t-HGQ{J zZ$rS5?pY4&Z~etb5DI**h3S#DW8WGMlFkVbYoc~qq-oMx#7at_!zxK7Nsdto3I+C@ zP*Fm49dt1qjU^KiL`5s^D{*B?*HB@CClw=}0yf4W#-=(LjK7C#g~E2U_D8$^feGa?(oT=WbP(!uXRD zxL2^<$;7F&sU>a5CSrPn8aqoE$<~UB_a`uK!V0}q;?ZffP+P6lbk^%-xwJJ-$Ewm0 z+CoHx<_EqtaG09fa{w2bUE9w0*OZcdN^yZ9>#QPK-lJ~XRzAD3}>7PB7I@l-j%MB})kGzrigKn>52~>~6ykWHYVn|>wQ2Rxj!-w^y*s{e zjmdW7n#PWJ#eUZO!|fXS>Egelbye=ngf{Z4TH4?R3kyj~l0?A-84zREqOqCBV!kYg zJrguK5R*nlVM4$7BFHDetv>$%I4AU(_yUX_&2QqC7n5TS&^u34bY!slREJVrNZgeP z5!cEEb}r;R5Yma$)h_gc&fC%B&@xn8pIbna5P<1P`X5hq_k3XqbFFig;%8B|5*+w{_Uc$jgV1uSuzre022d4f~>qvDGD%Z@7V z9Ksxhd6!6(p6UI z2U2z}LK-7;wp}g6G5pF#x!U`;!b~fSV7fFy4>3XO(**)Xu_VfUHJE{XKg5@?kgu~Bce@H z3+d)vWl;&a(IYYKC;lITaMFEE`{wmN2NanAlp8y zz4Ou(lgx^(+u;2Lwa*W0I>bg`Q=4H$C0jnS{pnI53cLN5`Ug=}VW3p~<+P>=j#Uhn zib1vgZ2Od^KzAVF><24hvHVq4F0qPM|*cY;I%DuBX}jQypzRD$!rJ_$LcLSH9iy6op=Xnp|;Lzws7Qkv}Zg729w0G^Hf@7S_>}Lg;57ocqcljE92&`vQac z&!2vn+1D0kwtgDz6E)*bV7Rc9VHq`EWc|X=MO0P~s`WB!6qNuc@Dvbzkt}V0^H)Q%RP zNbvA5)Q{OXZxc49EcDuDVX~ivM$Oed%8;k-zLE~Etbs1Sicy!Dm3q-zI*eOK+g+ZU z65?7~qBbp5{-eZ+Yqn-LmIJ2<_AesKyvcU4bGba5`LmVsD(_Q!Ot_|56PN)ioOzCN zQu)gD-rkR=54A*QnL>@VjRxWN_{DE8!mOO(%#A8r4xUjVaz>p{NitA9W#I#&N_g1P z3W9q0lj9S|vKjlU@xV70?f6Z_g}M)|TD2C*6V(8IsPtahi<&C0Den_WRM^Gk2@^}3 zB{@uuBS;!nBtD4ySH_Z(ubD_$_ZVJEsble6s~}}+NHrll0RZVE&Qh10 zE|TM^00pQ4))StI#DsmC_tEe3f~!D=mJm|?9oM2%Ovh30hqB^|GjF1I(OkmxoX>)U zj2{%C!B)MLORQHdU<92?Bg3400kc5wlW_m=E$wq3b z;DWN~s-1pVgHZCyHzj2B6O4t`zzfi9;2d_{s%MLZrE5}h2 zMDUQOrKN2vY=}SAf|&F)te{S}8*gBl9@s0Gp#n}?Dbfz3PO#La7Fnmz1Fw%*T@zbg zD5}0+bjcEihYF+-(FUPPCavSytP#!>oP`G4l#(E<9X!N$P%h*s7uQamH-yWsvE4wQ z(I}#-D97S-8TlCQpk2}^tCjSGjk`!4PLUVXH7`ZTlCBhK>a?fXF$?u9Nzo_|%xOGb zNc(3A^(334P)QFBJ+UKZXNrl|??4;vy(Fl;BMmn#P5M_LhDmOi{x6A+`lc@E z73s=t$sZD?PeUE`OkMQl6;@Skn!YK|#&O?L#J^5aW#tqBJByll9W{oVwt}7XZbY5S zsM8{cH1e2+{cuheHAU5!()c)Z@Dly7pIjFULQzHL6y-!E!|ePp(U2+s0H+j9d6QF+ zQ6t_WPt*eD*c1`XS{f+;{h~Yd0GH^U;JN;KU>R+c{3ankQEKMn+`0aBLb`sqll2tS zjVqPrl92!d=Nxi@A;%OC^TE^G3Hq2_`f)(Ho2aDiSf7;Pe^g93l8T(w$r+FV_Rsdh zeyF666j2psQYLQc=P?{|ib07)p3N3hAaw_7^Ssk@4J!!;CMTJ(sV=i?(0za&uu5Y#~BRY@}l zD)tHg07M9Ho>a}mH5WNeX(T0u5%$Dp`RZTNnZ&M6M_PXiDpB_z+X?mWT-36?3d|b81@EgI7rP5Bd zkm9%&oWa6ILY#pHSHVBOv?+86gkV;jhKxLTKszD4tq*cN9~D5I)COWKh|w{Mi&PXyb&ejl}q+- zSRbw+IQf5^r+&K!2-=E0hgVPIyi~dW0I?PQG0(@pi;ulb%=CZAJ;;i%htK1#c1Oia zC-%fH$2-LP(F3>}{G;50{zoZDDy^AU?(6+9Fvp)1_jd=WemGkxKltr@rT$oR@s!r7 zzpuFPU4JQ2CsS}KmtTkcu+P(i@7ACEs4wN4NS^ztA7rDyPilYa(|;=K`DY#y-CpW} z>UO3FIOtb?v2oxR<6^~|_>D{JpMVJvhQbh&^*f`$E|q4*0H64eWXfd;^l))b{YLB5 zycV2PbVI5hAgl;h!EfNZ>+YAomty1p0C`+L>G;BkZN~-QS#{!u7W`~VcH&2g-hX5% zh7-X?Z!S+1VK)3}J7;Y=^wh-EgTY4YtDojZ!x-aoQY}d`>k{(A;D$5YAIy$4{h1_6 z(1{NMfT8+fTIk0G3G06{Qcc#}CR1srN0iz|pL`HY2EhvEN`Em%QF@^=A16)+kU|7Z zIeZjuf=?2R{{S)5p!^LmgpPor5kEV869uc!5{);KCCLOf#J!0}?1kp=YKlu|jwLJ8 zUMd`tT4XL?x`yZagihGW{HRy3e==I9a{B(PTk0JHpiMKX{{WhpF_Am@R4$q>E=%lN za|Z%_EL!C7@KPu@~F(P)8_lqbU*v)onJ{MKNg$9N$e z=zo}d)4J8hyu)!p6g*p!)y`XeI}OofpRtIZI3@5xO?__BPGu)pdtdI0AW{XVfU_FC z@+nv3HH{5@ucZ+)Enas(ey0dJ5T%P>VZH9$k`+bUK3cSYT~sv95B@7qn3b>*x*AK^ z{6duM-%O9FE#)Y3hqb(!!~jSnTNBf-TDTxSF`+0)ZXDzAiu;tajr|~grkNN5&)P;$ zR#vT{OLEanjl=s-)*yYb6xmBi{%HXB{t3;tzIgazarb^IakqU2zN$;MKXTa2CKS~;Dw3Vq#^xk?v0WxB+_f3g+D_zV_hx}E$aJSJno>qpBS^f%;-+M{r zynec>-+Re%8Y+r4skhulMAchckV;IKPRRsJMU(BFRUCKBJ|3T9Yp=JPqcRr`Q^qM2 z2gm8u)YJff&}A^6OyZ?w;{r{0gKbpFDy!#@S=kgBi8+IbCLgKBBI#$2dz)VLB!DVSf0gH9d%oWLtfZ2KH}9Iegj^+dyU zj(p&8DQSC0l*XMR!y9h!@PW!-0K%GFby6tpRP~RUDfbq%gVcg&8p+#5m9~^_R6pPC z+P2@oS1rr*lXXfHv>6#GKz^ugkoeJ8Z}@7yX)4C*l@5hQ4VJa4NZq0eVCo=uI;uor z8iBa`%y3+fGyqToq;XCuW|>5U&IwIQN%%+;m{f95yYlwOCDnB6V{f1QP>BLUeIyzA zM-ZlK>XKX^8_cQC;~)g7K0e6GTF6d|L3;rX(_*eiF4|X^{W`ec zC}0Pxcz-;tv|bmql_HMsMqgrQz;04Tuy=Jbwk>esPr(V>H&nlLXNdkP1nr`|N~Wr( zQ7TIg8IO3q4CzOr;PSD%pkW7WCzUNZQoP>QKiB{wHAs0LrE z8f8rpD7QX+Zcr>rW=C1|(kb#zqgI;(vW4lQ|*jHqN!e{?*uIrvXdM9BBYsP$KwIh9<5lnvnOIzyW3 zsNC8tCGJ1BZsFbME>6lZt0ls}70a%!*5mcG)#NqkxYKhO6M7K)g`+X z=x}DNdJGN&nWJ3-jBK61cR^iL#SpVxDSdT>HsjYVs0vXD6Cu(-g>9YF2QfMXJeg|) z@{LcaQ;uDCEWC07;=j$8kyqI+UzR4F6>6!ccI;jGRfd+~l#;WvcfZ0sSGn?%-raOs zY5}L(y>GVh(OTPeh@m3CV$?2f`ei4Kcw-Ic?$r4buafglB&VT&n(UO}CgpFv(Vw4H z+SbEhx$^YuNZi>9J3v@bkp%nx(|w#_+v%^U_>aT>1PpI?$9E}Xv;^tv9tZJML%AzG zFCyD)ecIp@g4*75Q|WUZih&4etaCJIAx=Di!JBmVFZ@#rGx*A;sg$NOS!-GmD>ggR zf${r&H(FeDwEm0rBX3y#8z11BSif++#bp%++d;_HcMFZ&{98(!8;Ap0 znZ?#4xi4NhIki<;JOzw@5oU?z&nEX)yNx}rmZqY%?@_qZThUEQ>+WnxQlyYIkQ0p= z$>gb3LHcGO@297+8%HwtQA)QIvRi(f?sX2*wAzs);uKRZkiV|vDZw~)?kUHQr`V%j zN#)$*Ey4x?kT%mb*DQnctMEbsUaGh`N-0}Xh|_MuCH6*VHIkCyfrsWKEBrRB%U+R@Gf3C-jViSwCKmGeDR#kn&aGDZEA6h7*IPGD zw-S)zLXdSzf^`HDGw1KSR?$N<=hbCw(WgrGs_u~=<5Uj0AT+B zmJ|Gn>wruCH*4!b{{Xd2&&Bpn{=pI7Pl4 zhb|XlnWX;!Sq?2qA83nPd?s389nM93TvavBa~E#v28xdcgzmQX)l~2X{h!+HL#n+L zJ6fUsuctV-hp?rP)>#6qpUfDW@bck zI>ptmmzE*bd&yL{K4IMv@J0u=s^;>3X~lW3Yq-}3Wf=`ax1JzNh~SPyxv*;Pw3N>+ z$CqYt_VpBL1SOOZ0!Gj^NHKAkwu;sk^t3I|eTuIGeLS~??;kecR)^Q^I+s$qCI_UNmV=8@l#!ruKe~N{MW1YWk2Se8Ly zom6Kj2+tn-mT7@@%V!^#FjSpqEaV%Sr=~+obOePNBWVY2zkF+- z-Eq1)Rby@YB0)DW3ncISuI0k%Us%gQNUCvb3)|+Y#s=>#@o2pw+qZ~d1vi}JJBt4R z4xwJcAXAcZIAFpetmfddM$)PK;3IzWQ-&3bg=QBeXj?wWklo2jDI1H7A{95<`m~f3 zkv|wmJ>+VLip7l8N!)I~;wy2IkLnu5wlI=3S?td0#?n685D-;8BTCoMEUc00x^={= zay@+EqNJuU5E4lssZxlZ`XVAu3`Z5N%rYxpKW{|pE-s}3U)4!7QTG9?PXuMfL3=ok zGFF@L-I+KhWog53J3?+3YS&QI!hJ_xen1(^UbE6PcDBupwQ}7l+tVPwW&NHz) zM#XGihp8Gw#m{loR?lr41wyGjjhtNh3z2gC;-b?}VS6gL)YltL8-A4~D5!L}8fgiT zN)il!fR51b=wynRdPjrJ)ty8ta;T0G|&@KB~l-*JtwrRM8{p1i+Poub(D2( z>r?py>9@%kzZ<=poMGlET4&W*^p-SGpHcwQHaA%K4O-Kx@oZ0+R}GvAnt4jc{Hm49 zMO|IF#>!k_N=m1p_)Lh8I=`zNLi%eH%Pe8TViQ8$U%I-DqMyd%00Y`Y2X8eTj+QVs zT329TH;zywH?#O+XWRsB?fyWh-hLtz=MlDtTdJxu+)R`1fgt7+#H}*g4E!e%$l8S1 zMGC7(qWsS?w$i7owF%sx^udZ~;~hm~b1y7)TK8FzCl}&HQ#7!_MIO|SnuJPmshQM= z>|SRBIqsGaWB@5Z*1xYRm{6;*Y>D`uLcwkRyOVFrbxtOgEGwlj6YHP*aoCj-PBx7RYHr+CQa# z5^#4gTY1`>n)pjL=6CTY?9SsaVcFG-3Kqty*?lgir8noP+o@BlY!nozlz}1y=3OhZ z4gUb*kxn{S_Y@4y!dL$E8t|bb_7VHmpHX&s+11g_t@d5qbs`G3wkf$ki7o|4!&hzx zL;iFy-1i{dk9Y7vM&8grD0lwj{{Z4D1LtgG=_T7PQ;p#r$zlD`@D4?qQ!1|#YPypO zz0z0HHnbtd(^S|^droXFBrFp&%uGm$)*Rk*s2D-1^$L6VeXCb%+n5W8OPDln`k&rX z3pS6T<^G+gX}gi`8yRWjg4uTuvDBn{W}8Eirhwui5 zJDOCa@6vrwlo;7)fP5*?#X?j8N~bQSEFyR)IU-8TzM-kq9|)WjDQO7ORum=s(~d}% zEtS=DZzaYZC?oLdVpzti3D^izCOw)wL8q)CTjV!*&JKb?6*a||C{HI=&07juek3SQ z?TOs0V{HnLs+%;`p-12Z=_rbd@vf?nF~yp{h88h*Rm-rOItNtG{NOW;B6lH9iI1&Nhaj`lfKu{4A0nR!i{S{eTv4RvwzMEg#LLQm(GKPb zysq#-uaM|jK_%4bpq8+mK#)Mnw+B8Xv&8t*F)J$k$KCQ}6qG|!m&g6f1 zk1nwhyON2*$%uloyS#|h&-~*I7jN-P^ejaVNphFOq7UxO8N;1@BJZJM1Y~z_Z~Cf+ zePjGreG?E$$gZY)mUZeOKB?*Lw`CD^-tMcP#Gns=2lmH(Q`5eko(P(**J5Wi`(!5x z^)K;In3{{Oy5^}!to@(S6Lm^Xm_Vwk=%dkMUOK|{N{x|(8dY^tjMk}mM|Db&#?``w z#b1c1%ciCioysm&p!Qb@d@7mpoF`HKD~ns;zbTsa2~9$N^BvTGicUbh7W(2RHAe@( z#dj!Pepgzeqynkw;!H_bt`uII6>-`oqEEcShB&8lE`^Gy8I+R}@)g=3&5o#bZ?C=) zu@tS4FFzouCv?5v@SHcIQ@Y`K{Ji&yqjJS)!0Dc>6p*Y)&9e`iktoD;u^1k`=mL z2ID{16ZG1+PUIKlR>3(Z-8Dya+PU%*vI$muasiq_C!vN%6HwhpqTeTNPg-z%$(%{j zYIjlVg;HC57z5cI(`r#9h!s-Xz$ep49$H6q+MU&U2)95ww324N*pkY%YBzA^PElqk?ISc&|QnBSq~&*Wn~j z^@w?xrRajceq}_N@2P1L@hd?6uv1)I7kw+Z0)$1*jnTQfhTH?hEd&p_Q2tZknEOIE z1#+vU5;a|G4X4ddG@sU}m!|#-Hd#FE)9xrV-YDEw#;$bEDMSRJFSMdbGSDSQ#vd0q zg;zexMv9iW4`EKLZaI}*f<>ysaJj8ygd&!LvZ$z!0q8WjpJYhTM(Vst5!jD-{8Pf; z!yMsu+HKrDm)0R)g>B-w0ZE>k5l1i66S=0GJ>fo669q|c{^tHE)}Gh9opk|i@HS=| zJr=BD@PYTaPMDP2N_Z;Cb2f4O&Is){X}!->EVg)CBTImMNOp+R$a{WeP0ArS>CXii z?hJX^?ezN+yM37VKKexXn&D2ZBp#}@Gz9$j)(GK+xFWPuJ3VCGdBOMo2<=~CJ=CYqZY_ybtGD-QhX%N2=FyN+(x@`P@rp$p!TDMD)e>j64jWnuhc#8zr@_{3DN0ZN_kbVCEis>D;@L2qOpME~- z3Ln}bf5yj(@AB7wC++-Fi0#|AigcxKa%M!e4y}qSXrH=b2p9;5%u^3@%I%sy^i|Fs z?b?w1IZj7hvJTzNTiZ;>nMz_$(jW(PJM;4%6iKgOTo%CDO>ev06x_Ehbx1%_K3gnk0JbKOQ;cu#U}~ zxN@9|?RdiQH!2$rwU5v$q12k{*0%ur&Ll_)kdmaP4a8xLgqa!u^&0EPfb{#Svc1!B z%0nHcO*8ImVJaMZ13}S($S8MAw+7WiqjG zwJ5=CXdk;PRV2Sk3{Kp11M-C#$WDJTPKsK)Q$GV3wWYD(6Kta&^dZsXq5e>!`kB9& z`_qz+=ER1}MH4`HCW?@Lrr@~j;cw*#ci&9!%u~9O_To!>XzA175-HdneaMLotbPFs zFC;uldD^bkD^UC>GXh9L5(nG_PTO`1 zRI_NSakKF5Pe~{!h*56J?5mB-{7SI656wQdB&Duu1MwJSku+=+&ABe)Re5mdYAESg zA=P{U5H}vNSm^4Gf9VD%SDxE9{~OhsX8dfuK9D@ zrNinZ8gTqqhWVM?q4NDaXKmNVo>^J*4!lySiJw0h^!bHK^Lul_M3q2%L&MGo4x%|V zYne=ypc9w}%!rD2RT&ssDa2VT_|=OMC`y)uP_v*rdWhfREyJMIXYc1R%TTX%wq2^@|A(i5a@^4*(!_GBBB_ z2@kR)0Io_cB+h1=LmjaAOC!x{;kuoM_4FZX! zBUC4>DN@FfoIsH$UYbD4QYeus(gf5Dz?|YK5`vn~%|%L39#sqlfG zoQk$W))Eif5vHe&2-~;ENkGR``e>fkN9NiK~Sbc$O{5(zJQmTBSM9OG>;)@d=i#BaiR3}@Aj`-4rR@_k(&`w_0$?GBhSgFW-iti3SB?#K7QoDl7sR|pl9&?HqS@A7o&LOGr z_B}~f*imR9L-3GDFk9_`Rc*Z?HtR7;)T{gja+>i5a+D7w1O#jsZ9D3%S8wiD(00f5 z0Zmitf7HTlY!#W1H(Vb&PtGdx;lj=5z!ZKx?fU3|{$;5@O6mn!`$NJV^vi#m*InvC zC-Da>6hVy#Yr{)%PAfg$?=QV~t50@{1@3ucj((@zwq|UoQc!Zmg%&HyrL;KXZ$7|L zIs!_aWPOp6zW)HV&9MIfI3xG17BQyh2KJxGK_9VcyVTx{+E-S|3O{Umqg_#=^5Q6U zNlyeM9Va6PL{u_>ns-)E`bjN?6n}Ol(k2Kg6=?2Hk?1&2hIs>!9dJ|J?bI$)<0+Aw zw_3#fwJ4Hx(Zz)m-~{4>yYk}7&CcURqU{_}yS`&7rFHf!l=^;fs;;Le=e(kr*DjgI zt(ZFMQR@1&uO@Z1pT$WkQWCFCD+lo@bJRzT_68TJA-I0DI)3uKi@&o?iXW$LgzxO| zT3e6gnQt3qG#OqWbXBtVLQ+rwia>S0=i+7LCUa@#`Yp8rLXvz5k7l0PuVoJv<8jDX z@Ir+-SBR_@8m!y84gS zDcgW9Xyt3|p7U?HuHB#FG>b*{fGsKcs(po}RkWFE2vnSx;ZYt081>H0zh&!7U89=3 zySuDkWAQLnHy?MYJvh&|_wrlaZNoabRbA-cuYb`=t8^vWWIUrK{6P3f#-iT>16*CI z&R+LPbDG$N1qF20yj3%})3KCuhCtQ2oKf1TF15>~U`@m`No`uv*w?^@hu1uuOB-;X8qw7D#J{O!L%6C?v`|^w&lO95x0?HFjk&s!E~%nc z6Y!nL*GRRow~XC+$X+i!;cxmw~=if8?%x!*x&YCr3PDc0n zzU9~Q_ZTSr7;3=w)>I$*P>!>-AC)M`-hVO`ymO8R;lmJr&VA*6wL_z{wEj?>a(7?M zP@^5dr8Hsm=|uci>Hh$w9rRY;$`EHGcl_lG{JVq_NW|#^N3m!2#4=gi{{Sj;G8a5v zPz$o*DI|X>QUE$DYLD%RzK##&3Qw=N{&Vj@8Vo(LB;u5l@SA7)VSbhmOGm>?`h9aebh3BTERa$0%7+01HsKGO!5hJ`~2GxM>_b76xAA zhP$p2+CLz*@jY*)ZA9t?P*>_qN-)wWUEL8SI)2hKmqeVWVU+k3)JlJFiGN9vK>p-n zLgkLqFBZNgwcGec9xo=O+5CptJNQy4&!#TF3?n2S_ZgsA`Xy8pf z`YgMmyFbVI^_M54QT(#x(^W1ul@J}Fu4tj=)h@af0zANv7PYwgYv#y4Q@^mUa-P)O z*}5b1585{T6{xvz&4#?9x{BnpMP5C6LJCStZUD5%k718nmkAuBm>t)Vv$!UJ@)5Fb zJHnP`kH=QE_!Q_MU-7tx=%L-Sa9pE`ajF6r>a9xhnG%k61}5qfzqsxnp-FZ4eWj43 zw&5qiN;4mHFyq?6x>jW+B{p2MQa3b>cIb40dSMzbUW9JZPDJ*E%h@>YK#<+N3adpr zJ~9EEwuuO5NOM&;>YTea`cMSG*Vz(swJ5rBKSOM$abdKeYu8AcHZ+9UZe9gcjbgT< zg+#OfoWJ3uTG~VcrMS2+Dnq7pi4rDES%p&~JXW#{lPP$@sF7s{ftp8OF$v36ENbKS zmuY66>6PX+tpzU6Mp~pNP?sGYN4707(Tc36a!;iK(QePI_cZb+`$(apZOYtku`jfz z08)3AOanRf5ulm?_HwKQPAq+V74rIw{apK-4ih(n>afUhh zp0j?7AF8QR5EC^l{32S+#v(f>IM6RkxydpyRdl#dv3^eF)Hh#6Lb_{HfGIC2rxaK2 zkd1~rwKt6XDN!>tHwrw*bL&-toozQ|YN2id6zb%ESc;Zhv@Il-Qag0txn%6zD%Dac62#O@kR$*w z_gwfX8DiimRdzcr?kaQS5t_yJD!tDDbo*zOLR_Y4-zREy)txow}3uM#{?==FQY4cvbrv=)_#Q)qHpJ zT2J$R>D41?dDjU~m&AVf%V!NGC4C9nHEpX$fKZK{VOP6gI!*Sa{;-OeesHw1xoWd? zt3{@OeiW9G@c#g!BhXW#8aBhTqx~oZK1i{SbC$tS@KFAOiGa9Sq z*C4oFtJd2Msl*eg6O@mvLc$1+S}5JMCR&*j-D~* zR_!j8dlkdEA+M=eteYG(SUojbhS`BL(t1X^=F|&yhPA)uxmCug(hZdL8l}M5ut)1C|B;acjs;p}A(uB<@uJr@0tI1nZbqB!4>MrGt z=9OMaCu~g)pe&>Bs~^kkovmJY(=)De?Q!ito*J{_3aNR@$E}#=Pl-2JXj^^Ly-cQ} zrL`a^nE@f7k3R0!fpEu-HTTx5=IA&=Qp&gnvvh%|ziDMO>^a267(L5gk&7l$gD;5>PP18kjVolOyA&oHR6rEfFNf;p^t1_=rjGs~R)*yC3qlPKI2oz;k zH7Y@B`^kYmz1}Kc)8j&?A-b%{5`B_~{ae#bluNU9y(?3=&!Lfs`qFr)&9p&&Rl@Y* zN5-_1&b*RQWrHGYaG0+gS_j|>j(h}okpWg;-xzbdnrZ9PqpBIS} zoDyN86wIAAj4chml@^TjnIz6SJ9?E2v(PCqI6ztwy^~M6FPm(t-Ef2cO`<4y7AS1C9Wu8(Qc1q=lc<3`-V1 zbHQ`7HiPp8=(5}$Qd@3`_m-^^62polp9uuPKc!CXnue3OtlPW7IdQ@BJ6%&0my8wUo}qOsM(nK(=_{G?8MJna;WC+8zul;_Uqv0pV)rms^nmK ztEmN?PT=A^56ZZc{{Scmowz%b`AQOA?63#t2sb$EJRihXCtI$F0g|PXdH{q?MEd}V z+uL^>LW$ksV&g#ZRQkp{W$tG*eJ$dhx@Xt&R1zJh#4rAo)zWUcN`wL9GO0vAq`Vq& zMNgMxZL5f(^@IB=o76l>msQ?svFgXI_8Z+3uCZ4`aIR*RO;j?n+S*Yhq%A505k9eP zA8trO&d=V^wCP%V6aKrjE_tN0FDI$PDx1X`8=lJ;r>U&9Hi=Jqs;N-6l9_(-P#)Nm z{8mzTL%Yl?VBLvL`cB;PI+-nb6%rdDe-wIzNcfF)f@AFo59U?Ud6xhc8 z?BbOvBUK7#m;V4rM8~pZ98+25OlMHkIPIE+2|lkOq@N_vFZQ_&J)i#osY(8R9kWn_ zwtRA1sx$ZezqseFy9s5`37I) zAA0om*~g-L^6Rk&QzmG~{TNU2sQ&<&N&b01@&s+Z-FT(2koETWQ9s@vCI{$5A?>)l zR0qB;-LL^3YqzcuK;HAW%9)uIeA5X30Gfdh9k*_pJKFoeB{sMpxAdYy%Xz3+{7O~L zN$c!Jkn!7gp-290kRykHy{{ZpZWf|`~^Td0CSZjv( z04Hl$3#xQ}`^N$KJiZP~j}(3~a*e$V(xzmQ!lCp&(CP64Z~|2-_F{`9km1 z?;ZKdi0r)IS&Z@qcW$=gc)F-; zy@j9$pL*6l$aAabaJ;ES#e&z1SZ;TVRpPS8UfwZh+`5}}3zcYPEwxUOHwre)ojvg{ zbYrco?fRj$6$9S0m`wfU+@><~S`v|KJA~&XTy1n#s=6yPZoV0)p`^4?HW(>h#}Ed| z%qSF@YdAmjJ7aIo&<#e55og{vuDUTj#1K`T?k6SAF?!n-4i}c(n@e{3Q~O zOM9l$r62&%q;!gm7BVTGLx#&^-1%#ZR<-SYK~LV*F+BU6a;ttBlVo+TQPU-Lx2%z+ zl@_Vn18XWX3Ebf_N&8_tp^(|Kt=$iR$z0?bOUVrzl|eqYT&>19W_ib5w($F_r^;{! zpSp6*@|DD;XmYuPwL?Krw&YalM(5U&eh^S)@j#dt$momSG&?Bq5Xmjkb6r-0L9d4A z{wo40ayx?;dTWB~9jaY3sBpI9t}QFrprREs(8?@c>TJCyqG{_>TRg3Ix@Q&))6rnm zSgure8*P#1((M6qiS#~}meS@F+O};{NRb4_dnX%=Wn0)h2a2f^>I=kpoegd^&YVwd zrN?;dohVh2Su9k|B^2}*An&DfrC=c;DGk(;KmZXwk)6cdJT$f2g>iQiNv$_k6A|t% z-ICa?_y=+Pe^1Q#Ju(Y6+@Z9!MVsTuDcT&jT;!iw2p}la)J-dtih$BA9PVI_hAvk6 zzu=*sdf+5rv{YHY9p%h&R~FvGYsRz6N>`~KLf%nGp|7Nu$-%uebm@3y(p|*(9ge>M}Rz1~W9H4!p)M^D!ulH?iGX7EHzRavwoL7YMT0HB! zoRzofd26_eMhUlJbngI)iw+!IkPeMeQE`^-3Qz`t7Jp98T~=kXO7a>uPo>x)y4EyomGTGq9ar7dbDN>Yg=l1U`N5Xjbz5i(mn(L-mu(_R8J#~qc6e-0cnLe#fHrYdarb!!putx+uuT$QeLC8NB~B7PAS5)_JP)|CU0FyA>2BP`h@k~UUGVp9i1uy4Sq~@Q*4P7{J@ZIp zey(<~s{Ns^p|O zT~rTIE`lO$yNl{lnH$SRJMcY1LaJ+?Gg8!`f^^UcMQ&M~0=UDF1yvOKnq-E&a?7ou3b~wXukx6nSf?t8q}3TwvbHHqLMw3r6@E; z8VUwP&KTCwJ?5!lg%h-UL`aw%LR3haW#K(UH6TLADSD`O=d_Ol(h#W%PpWKzGod~a z)lDL)2B1oQ6OjTVkvtUKtnq;)MFie?3M!EvI>ZjI1<5(|l7>? zkU}m!(sjb#E_BrtzOw0--~qap$xDr|s#b*!LXvd^Y77R!tyPF1wu$TY7D4W(hd9f= z$~$vx@}3~|9~bwHLr#V6ISs1b((D)Y1fc%_3g<;6EyXKGLR*<4Jomh8TGxqN>_5Bj z)9tRyb=mh1kNPgz-xGgf8xIaX8g8mrVjR-P4tE>I8C~h*ry00auE&}(unJdYzP4?( zJE!23yS{(fivHZbq`aqMjP6?!=<41YrruA+&-UTn-zRYGNc8Bjo6Thvok~)cT((pZ zTW@fc07+dd%_#f)Vm{wzLn7vIukjyoQ+EmaoNHv`PvCy_WY*+{rf1GQ#iK>y&O{!1 zKuti8D2%+Mrt)8x9wl*j4u6bX!Guf!W{aq*j07+L^ zi9h{k5cP$xy@gp#leC@^rzrG|MWKqNiAqMHA=B?V#+$cdBHg~lN!^bQym=laXE)Yr zZ}kZEp&h4jQ`#mK2PjX& zQ3PqY81XMLvQN0VZL2r*r{cZ8%gkdft(|h=O+Mke(2hUjTUQuy<|D^8bdSsNIu_}a z_SA(xQlY+~wy1&$Wh?O+=P=UJ=gUEI1u#Go#gq$_ar=7Z{zkP{3Cts-tmg_X9wnWIj;z1 zkGkg>@~aK{6|_|i5|`}P+GKy=?kr59)|>$kCTnhEa#82)Y}S$qBRUR0xR2ZTt>e0P zQJCZ#?mu`}f3}Wa$N5hU&7Nrj-<9F^nwy2|9Y_fqP|J#2^xBfP%KDC}p1KfExZr!P z8_Or~k)u$&N4PsqTRYYk3dZ87hXU9WbmM;EGn&>G?dpw-Jtcy?8GnkoT#Bjy_LL+O z?2kX}dq>RqB=!CKmFXSZaU4Q8&fmE-`-`hWa(g9)n0z*CbvP*+&DxKNT6yRqF7}(6 zaKX4z_JlOIFFa4|RudJs{#%V>wxtk*K}6D41c&hyo$5b%jdg@Uum<=Amb{;ycRSXr zCo|zD&2>&iZe$|gbXWMar=wLpNzq>In4X@ zg{tHcGf+EsZSd(Fm0h03_}ZH!xNyDs4EQ;xdqw$x;j zJ-$(X`N&!J+X1p`+%Bqgl98&VKPVn)^i#(Ux~83NL$0M#>L(%N6|$05gBTN6<{eYp zHJJQBz|}`|v`dtzDg;hi{4nGzm#=&+s2xlqK$F&>H6}##h~!f>wMsU}2&!xq?wJ~S zK{n(PoilV&^{pDCah95Xh|oaNR(boW9@|LT(gFB~h%q}w6?B`YBQ}PeRJv~QhXolZ z%B)BSPM%(IAYlo->c{ZS)`rK8Fka3jScX($Wnn>C&knka~#@ogjO$ zSW8o|{IO5Wv9h;~Z}#hk=9pM)lPQBPvFEoqNgVa;Hu>nxTB~nu?bhv4fwz!U+m`B7 z$Ec<4KMv^CM~4C}Rtep~{{V48vVwTe40^wmEmk+_O6k#5k5wBwi~IN!+4sbacw2p|`T?63`oUE|NsOkyGE!1|}8gUPoK&FSTO2DiWloUucgns`>%Z zYK^792U$eCh;AwG#}B-WrrhN`$x72H8GiI%VBblCq3&46(;Ba)j@z-kr#r+gR(n;g z%bi_AX+v!f#GtJ`(d_cwwUAYO!1zP%2p%e=_nFyuFL9GKdA3K5xo@*Rzu~$HR8Hk| zArq8iui_H4ye-ZPFVK)g?kg=Vj<(iMW2bOPeB&WNb^h=O&f7S3XuT#zd>qSRpQQ$d z-jbc)!ft*axD0w!G5jHA<#;Y#+bzgtZI?A^N3Xxx9FaQ~sv1GHKMGHThlC>JK;6AU z5lY!v9TgAfDG_(9_gjh_T7f~3AwJKtBXFv7LTT4w7W6RXMSVg_kR}jJ=dY9@U@3Zq zM%C@mQ6)&ArU@F!K_gj0awhXtMmM@Dn#C!_1?$jH!>o58P7hIS(%kA4vhyq2WhMB2 zh>Mp+Adn{d7goa5rr#h5OJ0H+q+Xl7TA6m4$4bz?vonrdp1d!Js_UFm*Q_aMr&yGx zb(}+QZCNU-b2q_}dez_NoLyCTL8D4KAgKL=+ZLDhyzvkg4;hdQoE4e)FBsh}8FR&S z<#dd^W=eGH?TmIe5aA_AUWu0OMs#^WsZs}Uw<-%|9iJAr5LA#0&7{cATFiUm$2}-( zIYO@On4b6ZxGH~E+R~%D#D18nbqj1bfck3d2?xtUHH}6V&C{@jhVM+Q7Or7 zTXKtaF>NXNM;BCEAwUw6B&mZxD7m)n`B?gbvRA#?WcY|1tibw~{#lc8UwhdNf%IK1 zRaq|-UMTKutT=}%8x-_a2XaZ&HprYWb;nXG!ixLLD~5BET?lyhiY(&5mJT6dB+W{u zY|qc(gKcIUfkTjBGZgV{U?_Z2NgX}0JH=2$Q7E>u=Rq$hSjQ@SiyHBJ?L9J@D?3Vh zpV1#KW9}QpeM5g}N|UPj3X-0fB?JirBQg6T>}A>o6%XkSbGA{fUm+r)XL5j&i3U+v z{J*VTGYI6L{OpCDT-z@Z;}D#`ttrDeFW=4WD_>S~_6Szx{b`2~^cr&3m9MKQctqnW z&lJOkcY^6TTVr2Ve{6T^tnpp?JC|w88YB-^Q~P1RsyD?r;@#kfMpV$8-Iv+^Snt%7 zPR75%8%|Eql$WQ8BmA(xsVM&dPk(~!WUNeo7=7R6h5bb@>8?W~C}TjUKt91JXV(Sa zro9+zBc@RWN(2ohF$Dc+OgQ%?CNfGx`u!7>cpdSTO%HD(prRJRcL~aA_ zi%1?0$Q!2Yr7K_|NLeyhg1wMPl8U-W+N8q3uD8j+6(os|v)LC6c;!{EQR7HIjKGn18l-Q*Tp$$a0)afC1?{*vI=FOLmSkr z))7XM52rAn?8N^7MiA!dDz%DH$unpnDe9t`{>YeHgd?s>UZ%rn78Cq5k1YftfvKNV zaki@iCL!!1EH6**#OCE0Y+P0RH95;Xu%+~2bq*N zqI?m(V2^%LN&f(H2ig8ekLk+qsc77x5o%#1$*G@lKdKO0xp&q_;=esW%Ro(;A1NQ( z3=p|B`3Q#)yJndxSV|cxknlk>`-E_{c-D#;GH{>F5|eZfJMgz`b#l|;WyAV}Oxsy0 zvKiSZN9G9HEEl&I>C@wtk1bs`(!8(wFz=$1!9wiis6Q}H8cZ8304-RxYYnV?4la?@ zgX8^@_W27i)^RP4XDW1jIuW`4f14qUcXq>isn^U3p!7ao{PN*q+ zrq~VYr+i!K!QDmK=8A9xw$i0FDf9_U#jF@7RaDR2kyAycoLlrH?t7B4I!mp-ot7L@ zX8k!Jl&j0E9XN&-(dLSd9Ifrks~c*K-hP1H&tu4S+lnb@z@FfsNs!Wi2rx4ywsyv< zNiT2X9}_5|JIB%Em~ppu+;SQO_1ut`CO=#q9fMq}CGKe%xK74Chh5QKaD&LUdYesN zDhUFiRYea3qeUpR5J&^t0-JUR@^MF;-N1*dPlZIw`bcxz=~^l7Hs^={?MsR?pSNt_ zv4kZEd*IV#T^~tq;Tn@p)LYpjOd-dI6ga> z(DxM5k$Yhd*V}1rsr4w`Q23cyb@D=zbTc1hYi<0uYqc(3Dct`6>?P%tg{)?Z+2Wr< zE-a{Jzs~u#^H$>erk$!bXW{$FTwFKHi+K0dd7an$EtHhdXr>AepzmvRs2@F=hF(Yz2&^Vc~21A<&1LcY?P2`o2|RjdBdPF|mp`x21_GqtREwKM+! zFn0;r=wJR3*J%hPCmy@I=CggR^72fgU-3+T@~BUnoyo=j0C`FjUqrskt=pWfz$8w18gihnJl5_LuTYA5)M`k<>%%o71?owpW)c)l6**IF)!)Re<)7++mGf`Pi|bB#oQ0bujHJQ!L~kR z;@qNj+$MmgnaJnkOFWxJE? zFB4;q+U}#XjFGNo?Y{FjFE;9Af9*qsykw1D0Jdb>iC`{G|5HVI=dEpuo` zxcYi?V+!x~0@fD??%3Q^nKV4|x{0vwYVwxz*bHR_)zYY?S4fHe4*ZcDXjvl%>!i#Scvf z;8~GY;-mo_k1vvq(wnG+lf`aoYt5kce(L_uZ4}v6dhR2N`G*YU4rA_OyNxmuh7pJM z#fj55b*;#$-f7xeJ9Bn6l=UNYo0Jk1<15e;IgP-#UFlB*1)aQro_)`zo`+AmzjFpl z$9Ur|v-KS9Qs3MDeU-NDZZ*vOH2poU@k8n@YYybNdg`hv9YTUe_geVWqIRJ@L~Xiw zIvqVdI4j7kEvBPcq>j>to9VN?W~Wi0(xjbZET^y@<0$dkyxWd!ismdq6twpSC~cHi z3tnW4ZCyXiwNq7!UD|5o(h4R)Z7zZY#*uLa^GK_Xp7lc|fsOPV)`!z?y-{R5>Ro-O zkhUDBTHC2*X(?N7wsxrtN`~aACqf7WM5W-V87&a)uhkMn)IZ_p2||c#fnJ0ZB&A2* zV~a|1D4`^sf6D@tS}qkOZI_&ngf(>+ph(VLW0c)Mj3b~jl;Ko?8cN3WsJEeZH7au%Q(ynfyxKKNAY&4S+NW z69>5zzgD7TcK}TNkc~7)-H(Mz%v}j8AyX$#vW2Rw4XS!tE{knwlE&bZB>O}yQkbfe zvJ110UedCl6dP|}aWL0B1XK`JQx`Ap(r5gH>W`?WFAkADn?nr`*%sJY%Sx{9yB?ql z*LLUJ=0m{TugBcAj8q}KgO9EZ*U(eFyXEQ-kfz>Y-J1GuG_^LavYf`CM#|>e9EG!> zlR)6STI$~7!sWBXDq42&@gAFQzOi`T>Gaq7j+$Odp9!b>AAgFcoplG_}<>`Kk5(YPsiKo$L~_@6+R)VPTh%H^)1*ml=2hiB2nccY6wB``ELdqJ%~};T?xX5q z+2>&#Ieg)M&aam4Y3F;3Pcp2AiT18G+f{3mZB@5Q=0AqxjZ64+ttgUY4%E!UvG}}( zLf}nxR0{{T&Es62k}#e8^Wxqe5TWOw|-GQ8Aec~(#;ot8lwhSy=kF zE!Px!lh6);NC^N0dIiL-*`k1OMPe)jG1H1?D4_~UypEy(Ylv6Hl|>hLQwv*Yb7D!* z&sc2brKJ~Z5iVMBgi|rjUr(He4mBdDD&TCLcN<$8ml0*Xwj6dvM9g~OQvd)x`o$V&u}Q z=$Ue@uSpe%r&@!5GQpCYaEXaaQKU{sHi_+NjnxVga{#zfsnuM+#=Pk&cC%mwg7k;4s02RZSogF$7jFB-yaG{>e~Mqn_^U3oFJ0QpN7rKmY z^xU2R4__0o8u$Y*@?SCH{1Jy`JQ>M>f3%(_#QUfK3pCY)Wwl?u@U}h%=OB^f zASvwRa=N$3Teo%FSQcJ23Q+_1O0@kjDaT5t{azB48pZRPVgN#wr^Bc*8L^jwX~@7* znkaLseWW(D`58`sWZ;&^Due2vO0>24%UepQ+**!;NijTakSHXY-0@G_Mp2p5mi}gh zvdVuO%RvKQY0@m3@~b9nTAHg{g>nA?EZ;GO=XV!F)s@CF8 zT`Ab8$LMQWDq5#kSMsfr7cbo;cupsC3EfsnKC07TSo-fy&{0)Uvg+CiLzQi^mUjR( zBqzkfNDUe%L<5SpHj4{XO8G_*5*Yv_q-w9QK$A`ui2h@(`-RE!@yOuX~bv!wQDf?d&T8Y@(l!#oOxh*1da{Z^!N7>h`Q#zjAZxTF6jH>$`tb?u#j>qMq9t z0*fwOmbI+SQ}#{-`kfK1#|oz1MyNNi;6x+N~&quRMfXzZ2=(4 zd)5*nIIe2&y5kwk9ah26)vA`=ZDnfRvYmv_{MN*J^{YRk7noj-NeEACRsCpbTcog* z9jC?&-xDQ!qbZP%uUb50zXH#%sBT);N0(V`w8;nZ%4Pb$ zYNfB;2V1}!8e^s|k!7loQs^hIj0ojwighvqdczpnB;tpVMzfs!Aydnc%#umd-3nA- zrDi%O-Xnr?S4ZK5E0=yA-#FypS7}fZA9;>QTuLiasidfhb<#Oc%nCrLCvLtSXAE@; zb97D0tw|ySkr2m%klX=EI>>p%E>1|*3YnK#c=^ERWh)9NPDpS|&-Or+JXEROLHM9# zFkKmk?xysB1T;ld|-D9A`y#nN~+mP z#L>AS!|Wy^b4l4eNLTA-e~obGIo4u4qMu*4F6ZpY#Ch7-qqP*&t91N@<+4UjsZfaMnisXF(js#zA9MkJKx(nE`IK=%ChE!E z)?h_6O6EsYDG4eBB#l5CMYKwmQK~IgvX;r~KCrUvk)W$5#?AzGN8fcyguO~EZImm* zYO8W%^u<1R{{Z#UfAp1ghHY|DA6Q$Z+a0jlyLsVy5H}ipcGEQJBRVvn+ZvohX#n;t zwqVwGdsasMZ$Wikx!c>5l{F^#T4;VRjdaa{_n7nVF=OZHx5R<(1TRDK(cz)w><8>2 zY%VoKiVR;FrrEdUt@Lfk`KFx-{gLWc4dsL{EWI_9pITLXPD=4I9_XA%P|3BJ-tE|~ zFMP?bE{3gG{Ui0a7YPXgnr6$Z)XS($pli#K93{tsmfqRVm8}}peDwBVL0Q~icwB|t zE>TRXt6o0+WzLCCre2+8Xk8__xetJr7*9!=5R>hX8tnUTugSXM+cf_G!|wg7+xxoc zSYsP*-5q;bb}R~yL4tE{{?-7a#|R9E_&f)Pu_oXED$)>_D^TR}yx_@N-p!IXfd>Or5T zJkvMZx)(+@JNo@ReL}l0O~ZEy6l{i|ZRz{H-mDeo-ZJEV(m9&-O=+UM@m^DpE#~TR zdyztJw$^IWx@9NTZ*w)YFC>uFOJzuLCPqpIJkH|UTW6BpKacGT(e3UdxL^*}zMk~- z?Q1E*>hVjj8dO*QRg~YZ&DWxl`3h?0soB`uO)W%xX(ap#)b$hATaLxfTEP*G+Bll~ zeTx;|_=}mI*&C}yp2Y>+(&r*`bO1tiWBKi!$MFr_QfT{g7YQt>H+Jeetp&P@M@k+~_K;;b@>$|Azn8$TRf16?Lx&Xq z0K7bx5scgi{{S2Mhr(b0e^hE@bzH|Pc;ldReX3nc(@0R(dXEU##j?b?x}(gyAd)~T zJz&HsNvOx*Q^gDfk-1s&Q&B&PlSH- zL?jEkMH5wfXW>5xI?i$3K%5~oT>SG%1cUZM^#b7uU3~jf9~9*s)eD401iPXeN|Zn- zLP;e4m|k!bzMacydu6d%Fpdnwo2|Rh(cJQ;zd5@oR#PN})_|tcuaBx*2~2thCgup^ zsUoY&`;OjuZcivA1uN9#*bP48S)AIbz|~=Q8bKjBc&;s*mw;|UqK_AJO#=#PT{$m? zDb!I@$~H=Jna+0_bN>J}Q31|d56p^hZ{9od6x&xfa19s3QOlNQ<m)1S#$Rb^hrXTLySJvQK?~S3qSyk#3*}X!>=tvrFjQOFL$(_K@%Ix zLexWtYp8H*o%WvblUrW0-dIeXZm5#gLrR)ewMi0~Q3fRK;_FwQWAK#DQn72*semL-KQ_LS64guC=*!Nbwu<4E>X7`IG@E~XT)5@Dq2z<(#utCecD=zWm3N1 z;%#Xl{+OGYq-cU+JX2n!gn+ed6Y)P}G20?yDUA9;Y6HsE|JZ??D=geY{7jZ6nc zn3+hn+JqpbN*@X91Ox<|r-cn$X;C1^9;4X`-P02Is$`%NzX?iW2uW2BaDb1RwDFR4v&R`89+S)sUH$YrZ zX~n72d~dkQDoyoOlvT}%?FC1qy`mi~H#F3!A(qL;lu)HkXUi?z>1gZdDT>tMQdEzK z393<8C%6vPSxawe(mbSvx)irmQTEeITU9{GxHE4;sw9-^po8qzHU9wes=TS7{aqF; z;au;S;oP<=@k}z`klyO5gLD*iAxTP{F6cKVa+!>e)VR$+HD0T8jDgmi(z=abN8CmA zTKb7`78OrJZ0t7Aeznq5d{xN4y4!2gV5lX9kw0WwJ{eA=EHn+nvY<*pKCj}i2kKef zry%!#%Qo!0mAEqB!g(dFg;PIew^S;j50xubxa!+zty%#xI!0@`FWn#vaHSWab}k;) z*t$z;Ioj`4KNXw8gtR7v1JHFJ(-@|L3iKmAO6%oHzY+>a@RI975_HA{J@WsR3QZ<|0^=pO|1>mF}l zWj8=O#e0?GZ3Us^snKuU`Nk>D0uiE1ZAf@%fFs=7@`dFs6{;C+muSk{P(qLaN%zDK zZ4;L?2&JhLePeTD;z`t>7;1GiM%}wwt!OD>ptnKMN5XN*5*=P?SfnW|6rhnUI_n7L z)o@YW(z)u#992z02uPU|Bijn#xM-ObcWOFW?H-{XGyM?uwJTbg)!Ac6QQE-?*Kyiq z3`km=uH;tg*`daO2dfRtB4jKER($oOej_y8VqPsWD-x#}6Id89Vv>^%GB0?VY7zzKT<* zRhu-;@H{4lt=lnabxPc32%h4Z#ck!}akSB2;&Jmz`a5NrxgJSdnB`Ago>5pX9f_Yr zgC2P`;g&+Zdcx2_6%tkPpGDN5ra?Vb;T2bL(5riC0SUN-pH&CKa3Dq%C*#Z{Qz(!4 zP7L6yUUgS5wC;bd?a@4qa;D?kPa{$1D59jRw_S_qX`hgB$C9>`z%o(wh%*th?fb_@ zG*}+(#7B5Qd8HMkKT?D;LRZ^9D8QJ}HMME{I*m&LG%n7$ygfbZ87E*{zw>?n0Kq^V z$oHD(GjV2rfl~2Bw-9@gTeYIP=S5WWn+2+%Jlvb~k`{V^NfMmmhdGclwt#B3J)PU^ z$G@AJpnB9%$IQIeCuCvtlsQhqQn~n$0E295SSptw(CLW@+*F>WIvoeuq)O=m5Cnat z6v|(_FSZyYQuNjYCb{Ovz_LF0_~DlhXwiL&VQE}DT~P1haQ*PdT{xv|EeNLN9}@%f z6)N+3lj3!b>JYn?b?Ptr#uupN)C>0Y{{UG1<`bN*qUY9FIw-<(l)Qm?>o35O3)CyR zVG1f7l)Xdu!@AD#Oz8w7YTpA#45970c;cJUZ-V#dAO5+A{ae>`n!WkTkL#FUta?t1 zd>2B#pnuvK`{71LUlqpPh{7(_DgE=Wwtuc983(A1n+i0Hg0&xo!|(Bl+mvujHY`h2 zd7X0_DZ?)lIF>xChUiIgO*<(9%gCVx0oCmzLP7J3%S@>8RF@c1Hd9_X$W)4P8!CqF zG@QR&UE5^aT@@YXDc%ag<9S^jVp051R%I5VL7}RUeG+rloR?jz|?}eF5$I#qg*PE*{E1??@3~6N%)X->kqcJ9P}w}B{cX_ z&Dz&VPQX-xW(@S{7P4Blh!`&1N<+f2`&Lk4V4Wm%h@rGVC*lGHY!$!mO|69Y|7>>i`Hkbd791pBZgej$HYda@P@BDbAPa^T4c-9i~H@ ze-`Ive_Tx7&WtKck29`G-5IOwi-0YS{{Y|_X*1Qrpkxo6Oy1Ft!32EAx^djCUG4Mf z%h&J+F#@he8rZ+)32l{N9E}R z$r|cWI0o~yXAY|cLdfB~*|L2^t~Cmh{Y%%|u0O_AR~#{$WgW~9;VPL7rCWglQb6gf zA7_JfQ4=Uo-*>mx(0_m)#XS@oWP381Z*JfRc7w zMfBF;M|lRra^$?8i+Q;%Zd&OS(tZ{YqIY~kdV(O>V`sfkTuU%@D_Se(d*+@T#B1tp z=I=&_?XD@RhQiq@O44?aLFpG(_)z0jOEz`!>8d|y?)Qc$)DSN;$tfTO1-3yG_r;U2 zf}5xt+=qx-z1VMl98ZvC)ckIYij^YvpqeC?Q8d5MPJRcZd6$27q@5mi>O%DoEW7?T z-1Ock3fkM%hS7bYak|Pz-)+>LtJ)9Z5#iR?QpEk&()YGd+(pez)>L;f!?d)fvaW`W zEy$luK|T@dUB8kqZ3$il-I$)6ssd>C!+fQ^;n|-L-&_qYv8Vq4E=uPxr6xyg5twO? zQ6NKLrxM$`7b+nARb~yCU9tOVIjOC}rPM)2& zB(Z$O8jz~y(KLY|tvbN6%0snQW(F42CqD>smAczgdgDyN#3vrf8+MgJ>1|4%XtGjD zv;jcM0-D+6hYkt$cT5c|JXQjC9ox=V#oVo&Q*r)DaKS6-tKT$^P(G%qYAIHIRu#1; zY4}LiQRr3~2l7-ElFD}drNfAj0;)}SasBUcMPiyqDb-x5(K5-bqo;i;mxkaHWPR~) z+_cTqtZmno7`e95KJnkG4|w*65Y5Jl$C0+5#Dy~bI@SJ?R=Iu9L$ieL)j@IOWwF(g zkgf+B{ZIL__J$5k#0aKOMO9N*t;8P>!r~Jl2HWH!c+@XH&G~6{XYxr^W9Yu1w<0&x zVXm}Hq!RK>y!x21Z1(4Z#2hwVr5maGfYZ8k14g~sBk}X?`j<>jv40!3e2&<#cQBBm8mRz+f)42rqC9WrC9f1do}SfOz~=lw z)E`ez!7?*`-S{KDR%&wo0dn8KUB=cRzj*Ebtzn#BC99EBuGwnWAxit7i_kv_Ke{GY z4Sl&_@kZIq9DEPm>FG+9lDWPCn(@X-cf>P%ZnqN3Y%eLBwm)9NsjBImNhy+&dZfsd ztt6#GRFXA@wxJqc94M!r)O*e6&dj+5y!(W+N3qwcl-hDkW%9a<744MBdUpyf$xDO* zB)XE9QTwZu!K=z*V7z4>-F$ef9A0AP$<<-gh~R$D-@mkIwyNoY_6PNew|;wpUG1He zck6R)F5PgNnH)~>#BEDP`Uv-LQdZSAroBVm|ajcDb`u*h~q2-mYoi~ zJv@C>B8x&hb;~ilKQPa^cem<_?&rDVF1Px4wUaV_oxVfIsad8iifT8iwzXQy2pX`U zqaY0DIG~djS}WkM*_<*FP0tTbA99T;@qRqu=JD?4`R6B3ab5W4Y}4MU_=hm0a+;rv z>OL1yQFpdh?Ok=*rxF8AtO|DI6ohCBt1ax^a{7F&9-g1QTHIyZX&I#){{SCX53_y( zzNFsb4lCu}^?Bzf?oXZhD<#4>lLS(3ox%4_OIcNDy4z^!+X<(&+G}m;Wk$LAA(mOD z6KxvE5`e3$tF3J9#B3nHfb_4Y1xebDySTaQ9h!KJJ)2OE5x0)2_xEkc{hefat~q4x z`?V_U9>VX~-nFT{e~62fA|9erNE4WPa_S=T+c9TyD;vkZR3+u5-La$CY2pP( zyi|MWs-;CeYb~Mo>6~hrdWw{Qw$nso+W5mJ0%9NMRZqKC?4ps9}tUQm=NGmI)_ zlBHACx_8tMPu+;fL1dZzH1o)72gD0qY5V4>VEr8iX99jP5jZk0%P z3X4;0p|`H9uVsxIrgil#w1cFAR7f9mN^@Xr0t|MhqP!DsuJ3Ja0WGbxAOaGEkO22e z0K3U`2CGA3hUpCgYFFPGNIO;pynT^W>6P?W$H6u#Z)2%k#760nzeqxFRo+6$4Xdm| zWk<&X)9rqu>k2)xQMJkk)w2q3DJiM!l0u$RF&#UaL{GDTH zy3sB`Hu2S==7ZKHietqIG&N5e?Ee52evBrE6!e^BC3SZNRTXGDl9D8lJ@F@4D)-ts zBU5CSi=so6jG@$>Qd@Bg1a$q_kU61E(Re{r-fhWPWhn@7iIkZEFMLP{QMf!W$i3mz ztp+ObYn|rpSd=QBu{8){jfCs!f}c%AW?c-x5{cU2bPB!lIjtm>fA}}IOwT#vizh61 z1Ii2GUfgh03tkV*w>7*o8?BkNmy?d-v?xaE)F4eMs35f7=_h=MOt9OU&}yqP85WVu zbUJ$uo9|lw+USaNCvxe_usS{f;OtCrcWu^(%}ZQ$NmS)UZbMzW{{VtnC2^AL>eK)s z3MIDB;RZm9`0ZnEynKB?fst+vgR8Kp9;%q|2|(6&cVg(^km{{UgIPr4AWC{4qPkgXRS z1CL$Xh1pxoZ%wEF0Gn&gG8Ee`_^OJ9b4o}*g>KD5$|mT9_D6gsDvapb^1Pua5u^0zos_Sb_L#X~3Gd?phT|D|Lt7}@7dbnqe>hjCgw$n<Pe3~?ku-S zg?dkG;f${fX|SwXj})=dT4>t{MKS_ZW#R_0;F&w~G=+T^h_Gx-8ZVl!_k_Npt?|YX zj>~wi`dQ663Y9fn({ZO*3%28KOt)?-NhL2_6h1L2hC;OkX^&d+yS0O>cPP=nFRb|e zpMvp^Gkc-aOf6zMADH^QKHa^Q)DRcLwf?rIx#g{D+@+FGC(s}0k5;;nxyu)02O@{= z)O9YcVtN_R#&J(2R)w++lO@|bq|0#ze%_G`pDL0$Ktx}#I9D=B>GnWmBH3~R>c@^$ zRAe?bU1$az)$(&7M(&<85yXJ_B+J|0KbhJnl3J5@#up|ift8F3+$8TgVl+`%w zXkE&!GzkPcq$$HGpM?Ts=>~`VC}FC)d~t6ynDsq!lmb?d;R$#lGX0U5oRcEZ+dq|y zBD;;rUCDVW{<7xwDI8lYsCex%Kc+036G<#brm{B^^ZMb-xb}WHoYBXUOyt))X#EY|qYqW;muX7=%w;70Aj{j$DeoW`z=U z5ce9cNRR8g?1AJ&G1W-5DIO=MQxPXrgx;wjDL{0HWkpmZ2gw6LGpq-}P86Hkr>9jC$dtPIz|tj4t(B#0tSB9-1nJ~w5xt-V8^EG|R-hKppg$Cj?|6vgZ;B1t z6xe+-QU2GAMDs-UIden*0EldL3L<0efx=gk6ieRKtqaERo2pO=+_tI|MCOv-;W_)G zAKuKQV9{>79_F$TcTM~Ii|%)}D^DcJR}Nq8B9?<6L-`#ZX#)no3;%7^%l==qf`tCDe~8rxk=fkL-F+kTpWP~4Oh{`3=K#$3SYUAgoK zT$GNtm6Hv54!iuP_g238Ds#qFISwgw%6OAGqISueV3pN&>)n>4XnvBihM{z|3@d3+ zn{FVdLVV*x*||3cm}L2apzl0mNfXDbT%}sMjmn;a z02^+S%WOzU=#-*~umo(e(SJpw)UdfZt1@f}|zT!WM^xng7C4|p9Y5R)V)Sw}j843r`LJ|I|K>d;F*5rl- z<#V>KE5_L>Q9VKb0FDR`q7ba(`kV57ZYRKxan?SSRb~9kT}zMIt<3_ro@w_2-$D|d zPV|pVdsGBqd;a{#Y141a^GbVG`^)dnNF*{v9 zB(3UYyH9w`uGB5vYUK~ktrYvzge4_Q2~@v#Nb)Z6xQ;)Emr_@&_Kb2p+t2{A zX1ljMs_#c>$vC>2vn00Pt?%9O`zx)6?+(&YttqJabT#nRE}cpqpl9OgB4hN%e)Ndm z+B`Xn3vk*zfRxx?pm>Nauc8gj2|zjl23;aXycHXQQZ8+4aXO@Q^ND%1Q3h0|HC@WC z=TzktwObfy%BHKp5#S=R@{(v3lVNLHS8k*`W%GqGYcXpuuwV1Mt@Ae6rns?dwXm6J z6i}5sqEA_A1{q@mT~sk!Pbnp|XdWr0U&(md+i|-m6WXW}ll(f0nu_YjfSmO%IFNqm z9Nmr$M8}MG=6Ovz%%Y~|(})&o3u;LlRZPO0Q1>ZIR*=qeRQ~`<)^Q~xDfiNX0?zZN z;yOe{JIr zNGj_jq<+X&+7O)Vi;5XG#*?5D_W=v^sv~$#=uvJ;1c@SkK0l%%7GZI^s`OBRCA^X0 zsfoUpi@v3Bssf-=NmlQ4LhCT3`k;bcm4zS$G&fFX+X?h-P8aU3g%e&qPNf01Qa=gu z@$rQi)^SsetAd^I4C1|{(&AZVdhQmQg$8X`N`?B5Q8=%{lH&d%(J}-Rr-qP4xO7IG z6%I<>l7i-n6z03qJwp9eB}#TY%8E$qsEfn0%P=U?tPxP4oisB0;4-eDKMGT$SxQ9J zEg?{|G!lb-@>L>rGxtXnv9%>Qp{&lQ#v*vCGBCMqWh+5R)?y=O>CYM|3X>uEb{ZqV zNnZZ|7`cYvQBywORN$k}T4r9P<~nNP9em6;>(YLmRBc?VN|2H2 zDt=Rf212Qa7No3k#&TwwtepK#Qx_J;pLLzQ1%`^m;8sT1sY06tMN$AphSiHHdh(^* z*4&otZm4zGWj_rdyO{nI1+?E9&%2}Lyn&PIZrbRVj=Q5Z`PRde`b%OLTD5lSlo2HS zfCx$d0M*8s^bssQtWNrh(PL*CFqiqZ%GyT7n&Y)V$fWw6L}V;YZWi;n99@kyUu+$@ zU`^imC8z2gapz4Tl%+ZC>Pn+)ER@}N40ff>E9CzG>VwDhcCJ&aS*vwbC~-l=t7<}Z z@Y*`*7{2MY4i#R@ zmeB6dM2CQnVr|R0X~BHh>j>WB5{J0piG&WJ{7Os9&2e0Z-=#g23Y))B^vI@G2O;-heT;+}*6 zA_FycM#R#dJU@6A`uEr>+v^F=ekKgHn?;xvjXQl#?CXvlFJw4(qV`{6Dbz*pj zDyYP5L`=VD5TrQi4-{dnr-ChVqrm^e-yq`)Z+iS5z-H zNckAa*)nBCv(_K~Uj60PjSF%1(q)~NO zl7wqlReLCQi7;VxcAmPfnMqquDGN{GP%$eddv+=p#@7u(lQDiwy4qfBRJvVg z9ZckWydwJ_m2*uAR5#d0JXO1^oyXj_OK@$LjX;{DCAb*dV{#zzSR95a?BJ}0Jg=4Do<^z~(M+{T3g}Nxv0)6uWm|IrrBm-Ev9o`nTkWzP zRKFDE`r&V&5)V=y#(tBNHkN|Pj^pm4y4>5Ztx^sxDN#y+4HY?Z6zSTNw4V_q0h1Ff3HmS&YdZ7XmPNjN|FS?5{-T*BNgli;h_r++!#5rS1og2P)hkQx*Akx z*)=^Bo2O>Acd=_PxDhcs60PcgyGVX7B$56VOmzhe_ zC7+6NI(YPnB%V>aOFVUpoj{Xuen9TQ6atpRl-K@V+DK);ReL}>AG$1}%G`3*X7XK; zi*%JcpIT?sJ=|_to8xwS{#jeS>a+xJd1NI;Q=F$S2&Ktot~YVfX?uIHv26KUs?nZ* z<8D?*7h={1@|uSpT3So0^$JKEHb@!!XB8G%E@!Q#Z?>(SZ$bqXf5td3ofx7*8031I zbGN6cqdyr>ap@a)Zyn1(ypr$Bn-{y7un%IZ9m(SyvtntQT!$vA$SBlE2wR6>KH^Me zJC5c;%2j#~VfJN>tNGg65TPz9;k>&xzgt$S7g20e?Rx7Ysse=Nuaso1Y~1ze`K;to zL_jO=7wOl;R(nqcCZo}Al~mLwo|Ot3qy`S8eUR(-Ig$pf&LB2eobs_-k$X_VT;xpB zwzlh0QvG$#smBkjOJJ?ivJxlLSoFIF{{T!cJ-vHOgdlC5vvG?nOHa5~oi`)ZAfLF1 zB36(pp=(&I?+x*H8u8uslOnPy6t|iM1=3cfWH^I4^#&`iA?mZ2#aLOnk-wa1Li)Ds zySZC8HQ_Deb(P5ebi~Z{26}xES>6gv^q*K81$f~7(S6a34q-G|8F-?&ai1bl z53q>zxmooQQFx0(t){NXzjl0kNs!tteCdca>QP)$hO&erN6~lyl&wKMCUh`wZ{8Ts zNyP`;IPm`f>o;S%r}(ENqaUfIdDneF$sVODi5t>7gYS(@)_liBm={1L%%3B%<<;uD z*y%qkaloHipKvMO2BbHj*R@e)zfh;rR*4D#kbkU`)-D)B!mKj6g;D-M;;P&h ztMf{BK6cQhO^SdtM!&oOiGcwlt{m4^iCH|g#i5a*St76@@uJ`;%;fI^i~BAV(i!!Sz3yl2u<&^F?_DZ`B&q4e9++Ozw)4$5=h5vHfgscY$T z?kv9Ta~I_cIvsY`P=&~)x97c7&xz`X43F-p>I8Mw6()n!scR10tTk;s+SJqZt|7)u zk-jyVcLV*(mY7a;!lv0IPIDqPn!r8vveyq#8V5#lTzn(K^yguIQf>@*%R~m0H z#==vv4%==|DGGT=(4%{;x|wL{&_*96+HNv+tsG;M)v@&Spj#|wck@TkyJ(U#?;idB zhPw{pYtU(%7`}UN}iE76Wx4ZFu08zq-KTN;{==^lRzBTr9FfI&T}C&T;}N}oskL6M&ZLxuDT<)Wt@Jxq1dmjbHIGKJy|=n$SVNk87nkHQ_ZE_J$O{}i zYxjO3SgUV%SNx*OPlWdsg4%nj!R@Llw%D@Qt60eGMnxy`oSxpK-00OUHCw420ziST z@GN~x4=ME`ah=62xdG>yym-`l>%ZIYT9trzA;%oyjdsVwTt!t$$NV4LO(XYvM`O7Y z?VOJ~&S{#ca*m{B74=Nf-BO1f4hE{ymkWc`StKS{X7+=jo2enKc+~x#&)&TX9`uIh zBOGm`stBL}ajt_;0x6?u?4%Xj?Ip7qCdcp{tDWF}29yK0Q4L=?=s*$`vf z&*SS*W&38?_awmnP&U$>HK*TC5w4piFC6#H-^Ux~_^$`{qt6vQm1)ZF^fnwXAIRA` zrYm!(udYQ3Jw|RTa-q6IxT$`VhzV>JXaR5iI?(X_-kYtA8G9S6gA{-;@piQPOMWj` zsQuMzk^3R-Zj$FkNo~#fOSk-{wt` z(`Qx}MD-~CUZ{St2}tT)X&`yU)OR-T(3&4kJ1T(NkUk0#>B6;Dy?i;3RMz~SH{2!% zd8kDFYjsuG+eO8xX(wrGS}IDvvp6l5ZE(=0NqszQ4|5MG<4qC9_zyT~N=+@!$974d zqw=?!M}1H8Hxy2O5fQbf$;`^M?h@mDxpQ-L%~vChFBc|Wkf&9?iOZ*{IsO#ey<&QK zL_HFp`kPXfM;3Bt5>;9*oQHtla4Kwp4>e?Z_hq?S6IQgNdXm@AvexwkiIj~%)WEX4 zmu*wc*qBrICXP#RKA)#BqO_6>^wuk7m0j3DN+va$>?upy{xt)k@`0N$3fkfdg!QAn zsnR|z(}~+Usy4wpDDKjc^&~XXA$;h5l1{qa0_$8m#L83m&LY&S3uyIKQm(^yx!-T~ zbbzHMtzl#m0anrEVtW|NfkfnIV@L%T{#(hLmV|-eO2~sb3Z?7DxK*vm>x0L~JnaC-t?%W%u)FuZLDzFV?Gky`2QZYJ4OYK^qMqMwwk z_(!KuowHIkkcgTbrDe=#ie6&R0H<2+#) zMk{%B#G~b0T2Jq7GKnJzMcXK@Z)dDrxc8WFjFPRg-90^fX{{vx0Pi(N_;&&O7dV3> z0g^RWEyymSmExwn$EEjyAXl_aE%2qQ>u)3~VHl873q{`t-P#h6?!_e|r9aV(OxZ@b(BpWCCVGF;EbODm#{1sJ?4Cx(}H7Nz0qE> z3*q$bmP;i&#;w(VSx0u%Bv0a6)DoZGA`NYo8?Ka8*BQo>M(VVg{vhpNv5PIbznj{& zcNg6SCdVz^xZ3UpqX)uXN_|ARf2$kCbzbQcOo6dIHko>mO|`6&ov6>+vv;{L9YL$T zTs^<=uYlaD0nHr!$vpd+xtns1RPIG`XepZ|5lR@KwNcOErMJ>GCh3?+00f|GA-JgP z8Wz4d#R9L286WXGUr$e8tw#*psasG5e?dbO%bOEpvO6>UP3u3KP}Js;3HCX(<|=$^`fW625w&j2~)}lU0FX2}*8`waQtgFIoy^ zExc}p-zB^7yH>QGpai$?iFrvVQUOitreT!kRW#4!7gg0UNF{Ezpe340L=K2$hQd&Q z2AT+&kpQ{Isd~q1dbwMYjjjIj{hx1)JTwIp%CPulhL)AK4#2RDR;3Tcq)*dOO)WyR z*p;=*jYBRuxk&*jZ&M%!fgOdvxMvuzYp9opm@(+U63qB&XtSvH=O7>yH8MjM0Q5zu%$V*`{qLXD^)cz5}B*hPSWG+0!VEt zQRxD9k8Vklx(pvKnbD;#3-GOHk75dku$XkN_->gQpn(qu*4|FiUsqXyI$v+9%}a*lj-5G z^#xyRR+$~j1<6T7iF*TYB< z#)xC8mGH_&6`5k`l=s%tiGS=-7Fejjlp!yj@GVBUDNMYDBoY%XJ;pt5Bk%?1)BMT( zg48=tQF*+yf$GrEx$%*rLjDstmytkap-TxcCaTTQi zjCUGQW$os=s{MVuT%Jf%wUrL7Qs_4*m?5-MD6;a_$J|I0GyA+C3#RKuX=?O@e-x!6 zd?LMDb1x7M@ux`RvT8blHwbgWXmBY(lOUP+j`+C$_3+!fwW2m#`XU{DQ_>I5W64yGWGqC#^~36#k6>jKD>Zk%XudQhR=~gCp zCPe!{hXq|P!kg6z1duwW1d%LnMXB6NPM~#>2kzDw*!50(swkV?;8P~2i1F&ZpBoho z5_;&g%3r1^`^pgOTCEplNlTSbdrIZ19uel8b1TSE^PG2lO+|y2tg1+Hs_b6&-^o`c z#1MCSNl<+w`pt;*Oa;J(PyHu#>KW@}JO!iX+E$TZRd%v zl$U-v%7Ti+^eJ0+Yc+6ELPLplg*I2WLU$+57FT^G)dvsX`+cdg7F}Bh9eIYn-af-! zM}~_+{-QP;cQ9f6G4)L13qhl|^7?7)oy}OOL2;`tbwb){OU@;v{0-HHi)vbk1!+X4 zR~A`lj1r5e{!>c(czgEuEcOk?Sf67r%-WXv$Ev>ZrqwE@QC+ET)`}~|68%bgqyk5Z zRVftv!O}POcj9Qng1}x`J_$=oXxEAuk z+s}Eu&E4u0TzUJm1Js}W-KLST`V%Oj$-1|)k^JBO$XYxrDPa)xAKF#c`enN6xNkpT zs%$foEV7>>ss;&^dM|7mM6vzkKdwE2XeC%ZPe|9S8@EVR2mlZSw{`t+hg6BRSk|s6 z>O8%WtCK#umDiQBov%^n2tzD^GPSruQYGysP%Mt3B9&R$^+?N=f98HG?VB3@Ee~%t zN-GbbQbB(%$((BIb#xh!K`v9bpqME$pNEBaB)?O6Kz7sZK7o6WYs1j9<~()#2cb$m z-(RV?kGHNS<9i$Cmm|lnG)E4=$$k1~S=!(#T7n9KhhDIMb~r<7$j1Kw zu?gEvGh9AX$A4OZ9HqrOl%ciJ^6OMQNzRvwN`K`3W=lq&9?M=*X4@C!Fg8-vxj727 zq;4ikNs>Xzw#%x}U!gpBA8)6#!DvO#O~;p3jvPm`)S%jW?m_MAF1T^eCs=A|S*1`l zmUq4-vW1_5rjfxkxYFe?Qc6|eL7Wqf$xCMbN9x}X!_=X_$HjKec6{DHxT_IMTS0QA zrMg;bkzH=9PU>o;5JHDS4~QV>LDWY6-u16|Jog>TMq30_< zG_R?wS4WnugTrXhL~y^DtqY>*v;=*&3X}M>J`hxG)duM`N_2F9s3-AH#w8GQ6ddy! zB?`-W6xQ?tk~|_wq0tQm$psH-pQnb*d^|lVaT_LEpy%*R*v>HTiWfa8dmT3}#*$NY% zNFv*06~$73pkaEEtVzy>>JWuhrk|imC&0tp%B5_?APteFC~neTIzu`s{{T`#wLDzf z7`M{1FNF!v%y`DL8S<<~T2GBzydf;5_NA#ZoNBFt$6YBGkz8x}eb5vplqzTJiEkwM zP!{YV;bzs`q+|@B9*-wadgCyDb-RpzNUelQw|4uq>Kma>pZgARdvX1RleYf)$F4Lx zdCJP+R*J4O_ZJC_&4K>9*mn^B0QG5Ka{Z$&rmKTW8FcAPpq~&v0CkN8+rA*YUmo6Q z%K1_Hu28y;Rl3x^qNSWR8Eg$_-vErR))FDSijRbkXE8Jc*O0{4b zMUJpjwo%4@BzBb-*uaC18FdBcP5>bLfU{%UGgxARiMAaHvYjLMvTES44uMsmLFS6*)CrarbAb zPm!E5MwKjU(&FTOMU@W-fk>Oz8i6G!{4?q03WfuUjc*m9C(~%oXH|K`5}JiO8LDMg zx_vb$i6u}QnMGjNQ4p|Q1-j_k=0Z}b1y9=&LoX@{_Sc*#O8bpFn6fJ=+h5-0k+ zAju%=wPxa!MB)^6a^h;*X}GrZI*p*uNe>vTlN1%KS%MSajxYsLy7SIAGLWJaM!IVg zL1Ry1hVv)PQxlafh^MG_K$%Q~=M>q9KpHI=NWj;t58&_WZIe) zEj~tm*s}_*Q$nT7D_<*)(6$7D(ij$FBzQ9lfageeqUxP$0&8TCWWT09eLcSMy80jW=#Iq+ z{FPo)qiPE95`{g!Q$Jmed;F7K{{VJ==$HI(r2Tn|p@mgQ9j9M(M%)5l*BZPSZAkso z`XNpTF8yt*qleUw56~iEgG~Kts6^rQN*lsNz0l!=AtB8*2yn`!B$R+hutYzjdXj#) z#1gRDzv?K<*$y}YoHI;czmsb`5J%k|`Tk(8 z)M=z~!|Af`*4TkoA6u^0kHg^*94QI^0IabWUMpOY{lbazi2ndbDdR60;)y$s);3Jq zRQ=Hturmu{m&Mk%5mWBISuJEE~ile<%cVDMB!QSrVnNu?~r!$&-jlnH*s;MY)d~hW>h^D zC{9Ty*#-8FOhqXYe5F?ThuiMBw+@HrZp-Wjr|bLM^}_+7V&)TFk|vpLR5rHvr7mrj zgvw%ZV=r%G9Vvf3Dzf(7$(j%d%Qh5PeaatH0_BG{v2ndIZCQ)8Zl6>V%O<9CAQ^e- z7876E+DAxrC`(S-yuFw@;nZryOC1GTE|vExxBYkPo>R{{sh!JOiqwAd9&Agbl(_LL z+MLEm5Nl3=B01~jG>S*5sUGhM#FJ~PZMNx^#O*M06unTPv@9KU@rf}l0s5$WfV-qX zTI+-7*iEw6M^9DhscA|;3P?Q@=N^+~a~neN46Z8S?^6Azk?;jBVV2KH5ECh%d>-74 zehWKsfuF>rZcOA{U-CzqrERs62g04BshkOYe$v!_8wn_qr8V&0X2$JSG_qPxs@Ney zNtZ%3ls3;I-dYu9kipDfRUH2SwVGUqm@v~ul%*}yr{dG75kZ{21=I^&+nDJhkK$LG zx&BmDvmC`#-Bgy?Y{+>06VydjqriH-z4+OptF8AjRYgveiis#ESte7`QKfC)!@AF# zb5&J2Q<$o=+qESd$9pPu6G7{t6YCVWbM+lXp|*g>5vGeodvryLlE-d&)*D-?N>0KO z2-_(V1Ycv=#)akC=g8-kt~^EDW?Nve)#Et(;S??~>Fx>v3P9eiqb@3|GtO5v0^rQ+ahr+$fWV|V!)2Fb=jYL#;77jbe3rex=+{E@50mEhW+rqrTgZDASb{;~+D-ly|zcNL2 z8B$d36=Qz`)b0V%ZLd#gk?3RQ9F>%|7X_|gU10JNWUYCD){3Hc3&kr#RZa?G7B?O8 z`-qLVZ{tm_>3u5vO{fA7j-f(O z@77NR8SU1c)`4)x)O#mqAGGmLaTW)8_lIawlv=54mOq*7Nh4rYuIA!{n^XS)bmESh z1p%a4THf;=mVYJNhjyRG)6%O_IEN(2Z5VCqJMC*XXSl8czPGcG_!{KddbWkN{wAvE z9GcR!f+XW<_LQh=7Vl>;S+`2~O&p?ple6v??xL2zBkflDFm6j>vi3*YHbqc|D$?q8 zbMlI(NDibO649T4_hEuNYwO#v;sHq^R2N(y*MlhEhn=Pxk z>|`Pt1hhihsrww3kuRn`o&@0*(vBff}_{XB0{C$>F9>jdgmPV>nO)DI;xwU2Ci zHZw74dfsT>96dhO=9xSt^^55%3k`SvJ9~BICV#D!AIL|Y_)AJV(R=4ukP7384 zI&+Lw%4HEWw5s<7V;fU2*lwU96Q0AMfG_)Y2~RUgY4s~?UwB>GxD!VavDkyd*P%=H zY>Vl+oH)B>Ul#H&WmhYb{{Y8j9A}hPXWV>=B%m6MXCN%pFv^LUYK$3p7@%%5BWmtu z5b!u1{r>>PZQtFtnFa<;@VK2x0;amux|KTVud9N%d{b)ehqW#m=5BA{9^|oJ>N}>t zTdFax+fj?!ZrGj645GeMe7)NFceCWxPc1c6>~0OZl!*dTgv~^ndx>&4w=EJzSsPBe z?@xA~p2J-HWEQlA!>H!ybUOHr4LE3PuKo%#?H<@WPVDoLyT!?E&saGJx{IYB4)%j_ zc`BlMnWr6Hb?YHv^%Sf&Qpsrw0B$TyD1`$D++Vut_(a2`Vy+)`6?KLo*4Bl^zcnan zUG^tkwc}5`E0a@ey6|Q(OLV>WmE4CM;R_54hF;~0%gZ>xsHs9`rv(ygE)2Fxr!_X8 zhf+?a65{qkeh>vd!TVJ9__mf1O>5#nBaWI=-|PvkP5kX>%&re$zftZqq~zwyPg(9X z_viTP^tyMO4yWg>kJDUPQr49PdTz{XB9YP&?_=9Z1wsL^J%sWdcx-;ibgzFKqW=IDbF5~r!)>8Q_{^9p58RmagJGhtE=PQJ-0B5dK*oq zNo9mG(h`t>;#>7f00Aml13?p=#dFB9^q^RB zTb+g0)Ri=7ZdKGJ3Q3qC(mLFT@FEOz{7@#`A0M!glsQ+Cc^@>#tmBdS7W<$6K~g^< z{POW|yP7icN*CxGi_a7lCqhS26}O}}V6E`%<#i6Kxn)-bAq}T|)3!s*b&T!J$SZwk z0iuuSYXEUWcG7ho=&vtULNZgPn!KQ=__`TOSm7!TOLX+1x+OpYWiJ`TvC4*)+rby9 ztS;0zr0KLojh2B+6HQe2!2Eq-zhalGmByIVRIck#l&B~Vg)O7{V&57`YR307h@V9* zFN$#;{{R)eMLq{~Sn{mrJJNM`6ys^ywrL7ddW|6}js&C!_}a+YcC$AjN1i znx-=9nnXJ7_MVyd+&>WuEFmfr@zg`DSw^7mWHkw_zACeq5|q=WC^PUff;wRb*FGzg z#gybnrAz8g2lwKu*8XGAH(-bOA~_B_A+BKV;%zYSkC& zGWa7-c6obJb~!@FqN61?wd=Tf%ovNN)iW<;92YEhg_E|=sZD2P+7`)5oS+8-20s+xS%=aBqT_*%` za{vWlwX)J{OF)f3%~VU&3DqGwZTvx=Q@B2F6s>AqNv3dt_Xw4efVsr*Qe~?Nz0e^o zziEAxH>$^?;%0rIh#{_NL7r+UX2EK8D@qqkQh_3*l&jt25|_0DBiuMENq}0G^66P= zt)gOPMiZPSx}~dM`$Wufjykk*Qy1$G=5=|MomT5in@YVmej+z-1Z^aV`=bNhIO%4Q zu4||Psr9cx?Oc>`LgqMBkes;oE$Nom=}p3QRJz0NZV4X6VP$A4w41e#6#UkujN0=| z6Ku9Di(@Ji55Aw?*TEmIJcGP@hnM+qgToAyulP(ZyJg%v$(M2Xbwi3qf!fD=J%={{R_Uqz4_J;6OuQ0+khgW!rtV z9wN*zQ>gEl`ushE(}MY1Gn|(vbY*|b`A4h7`p?`r16t)B=HP^>Kr8T_2?JP?wUl1OQC_(GU@$Bmju6pmm<68&v1eAk|PMw?RKXu-7)J zbWSWCz%z_dN>qTarU&hHFm@;Vp#gvLEl;?prPQb=e$8Zh9~kv`O&YKgpNOYsB~TS& z&Rl#33-oDily#bkI%yLiRdTLop`=zY2?%i~aOpUTL{u&WrhX}lJ6gr!z1F{lOqHoh zB11{SvM>=6OhT^t0zbyoZP6i0m){jvUoyI|`BP!iG&0sJD6GrZQke?qd|?7gmENL9 z&!j3!r3RFD05$T7A__;kiCu&2>k_DmMo7@dE=5+No}M#~Tn!NyCqHL|MJga7508xb zL?zm(4FXB1NdWlEtTpYbQ6faE0GS^jAF>BiCgn(%*%hzUqJb!rU)}{BCQLyiNhpBu zob(6o9X{BGMitSN?4y#30TY=0ussrW6QkBc6e&Sey)sBoUu-A{S+}ZfR9CQ6xgB6< zol{y8Lzf1(33Z$gBfu(tjZ&|24JPHmluplZL1A<1d?nHX?l$w_GQJ~(N2$Bk-Jl?rQ@DqSHNGa#I9SaF^i*$ zE8bGeo2XPsKN`FMjSgzm&2HHnscwhTpTGi1RQPGz4bz!X%Krd}4+Hs!tBD?<6YuG{ z&iEyY`I&aX&yN>-Z0?~9<9vZev;G>{c&JKPw%!my3TebUu?ukqW8hYSB*VLU^^(Nv zd3GX*9_O)?NZW|MX!Rb-)}9(_ed@gOR~BZypZ5dE-L>)~p0USkPvm#)i$y)o-v0pc zsB73-zy%czC>3p(3kpo;lM9EqeU-t6L;Obr>Bgxu7R<<6w&pRZ-_&@Iv-VLXUJ8ne zN!C-1gwlsMMUH`lJ53S>)ceXq4n7zw!l&)@ zD`wL4)Dx&oz?qL#mYf%q1rv7@%2b>=!xytzFBxKICfDz3wj{s!9p*OHDS? ze?mlI?+^XNpZ#CBFG9w)mXUw-{_?al>5_z=8qZh#Y>8$={{WB}5$OH3{iTENPyRsm zEd{GdB16OaV_+63!&HXqXF2rnh~5cF%#i8?gp5d1CB8eW$FYn3>y$G_itvsSv)r=` zxd8=b#w^SinP>qrRO9;q02%a*_Hy~Q_U>`;S9psTO*2|IC1D3`)LB1pUC`C?K2tlb z)pgbRCQDR8y)$>Xy0?-(GO`ertn37tB>;FwkYM36Ty-78U$n1Z&dk^?x?^*o_5|DR z&o3O)+`UdGVXF19j8*j6Y|f@~A%niT@(BDzz_IQ~fU|c7_WP84qff{1Mq(fqc3j7E zui&POs}`E-A!wGol=C-7 zeaG-Vo9|jOTvs~ne=n!AU;R9nkMg7n%wsB|SF3Bi+m|i1lrDl&90?=TY?-9!bSY7y zx1MVk^0<5_(@()%&lES&b2sK5n*J&2d_@jHVKp=^irZD)l_f8v%SEO_W6U0)#?nU# zqV*864Uy1I>jhLxBGqfZe63PX!lxLiM^3Xx>yJOMagJiry$a|FBIX{|D&dY1ZF-c? zbrbG3eZF@(zkTgFynR$Ss)*%8{!L{eT#^qo}KcELTyaGDj2YON`>AvV4W$GW*68$67 z4o#k=((oI=6ICnZczh0N_lS-5Pfz;Rq-U_INc!?}+nM{m^x8X8i%>*4NG#c$KExHh+%O5A?rQg!sMP9|sB z>M|ol{@S>vizOF)TAjfOk8tc$xw;Uom}55(ndwbh!pZjql)$g;+mMcDcv&7LM^`@8 z`-ruvNvz_lqQXBBX_US?f9RB`1^A1G@J2)2wod%YIyEnr45;Nk3wQWt{KoE$IH$7PJ0#(s(g! zDO&uBcOPe`D7-E`C60}s_bVm%?bJ8S_E&xg>?CCJdDyKNpe%ZQFp)p02HsR z&eYstyjqBr0;19g*ZTv?Jvqj-UpzUKDj)BHq?~r*H;ttVl4m_O!!k$uNbDFU;4hv` zDL?%Eu(YI0{{S{)^sZmFJm($%0L=^CE(I-CymBf-cexEh(wQG{k4}E;9Yq|PLboTp z3H}&&O)xnOmKMa8l@mUr!W!n$IJ~1mtN|qfGt|RVUgAX%4_w^~9+;5un9>zN(jg&S zDxGynFDr6S;7Ep)RB5wNrqD@9kRA~WpHPa4AXUB+BsJHhMx{)vy?Gv@N8$kK;S6)M zN(~fmRJEv;PI~A}4CA6hx=X1?s)ZncB{*b>$+-PLrnIddJw!#t9^+OkVZ(2twvh;R z0c!~ZUV>AL`%PO#=UGBn^(bU5P_%`lwva&fX)_5s05n6}xRi^H3SyeV(mo=SpdBZq zMmADUp;g0`tgpZG4gf$_?Iq-AD9>f1_*T2Or&U4LgH=Te(MVU@7-|q`x4}u4M62@o zB>YEDzAd3?Dx&S-pp`e>*cSS0Wh}(ePSGjQNR-URf_8F%7G}~;rAbZI9mJnF9*DKl zR;12eG1Mu|br+K|Bf#~9OlzvWLE2|IOT*3;D(!0C5_)-vibc*UBEMmDOn_OY36$ob zKU@PngGU8rYmz0(I(Hvw1O%pjaaL^_v;y@Ja@5g~+7d@zkq1etKdLAd+3Cl_>4}l1 zf*a8Z{{U1`VP|W_nV)nC^zBo9V*UABC;q^Hs}AU-`m&9)Vxqe7}HM9>;UpWYOL8-r5P|3&X$Kq;I=p-Tk08Bg+KCt+q z6OT}xOB#7YK8dNu`N(GRn|g{>=mXb7FX(~3iL1W3$Z?3!ET7;6iD@Yx*%SRM6n|Ls zRqQH+^gsml5gV+SQ4GkrsT!vPYiW~P$sJ@O6Jikz$dETpio6A*@P?#BLhA-NqS=^m z8WhF4anS`tGq=dfDz0#?u7O(HcMFE)Fm4znQya<>NboX@?D#6H_%qErrd4IGs)N2se^qIoYg;Yx?;iQt&9f)q2r9@-T?VZ4&- z0SrFxNdOFNY;u&PRzo3&@K)oBc_o{D{)z8fV@uoW>*E^yc1%@y-cK8%vV5tN)p85C zzA0c|%-3WOHBOK^e-U6Fo?bC?aQ?f4Lad4Z08JTr;I>PHzK*mx79(f6UdxqLwX8Pd zz=9;TCvoUNma(I@!$iO;ysIJISCc&QY^Mi)ig;)v)MeCpfT@172L77@1NE$ziUB`G zj0vy`RIpE)$v>hU`eX=0o5^^lc4hRO$FQp{ZNh?UQ%EH#_=wOMX$}1n4hz4@ujBcS zi|22{^|Dle4FqDdrvm@*rhs(JCPuE_o8IjO6o zS{``#kS8(vVUpezEuJxerUO}%wCyPfeOptYKqSl`IIkC~&Dg=RU*l}6*W`R%exXTC zN+x=r#SIrB=TzA-;6zHlUD#y7R$Ot9Q*yvW_3}tC_ecY@Rd%-A!is}eWR9Vbl({+2 zS@eP=x}Z}sz|}_X-d-0r-%84q76}Jm7zwuom3A@hG0|J>2b*sedJ1-%SyN37ME1)_ zy|%p|EM`Xl#~oIRc7@y(!zIY?mdcitpea-mx%AyJq~hYr?77Pwl*6_mqNSYomR3=6 zRziwb3JU5RB_QYy;|<7O5g=OpO^7grKN~%j<&Ny6Ud5yWf~80uWhkEBb4sMTw+u8^ zYS(wU;%WJ7wZRNHlQIH=y^wK~5~-wtSe`Dpqb`0%^pKZN@W~*~S{)#w@EWP)w>2wd z`vC5GzqBqz`8<;Dy6zy(+-a=Jh^b2X7k~(E;K~P!dL_U=5kt^;54Ieki9s4?D1#wl)gy2ftS+s!g6Ef-c4msx$6k-fE{NSG;5Ac7<3+j(TR zla%l)==-Q5y@#2+R#~>Z=b-lDC#79qUKm!!_L)9X>!>|PDNRH54B(9n~h6_ zYObS7im56PnT58mi7MOy@QaLoU_5f2)fP_#=-*MYiyg%nwWJ}2%SuPI%*DW&R)_@> zWl~;U8?-C_H9Mg0XM?Y3>e zZW2EfIqsW-uurRZ(@~%rHJ4XZdZ-CXByKV-l76{#Q^JeswT&S`Z`|IHl+t8>ScyMQ zj3J$sxjU5fp!$AbG$@kZc&R$b83IST9~)ZU3ysu{+^K($coLh3uhdyi2Xn2)vI+?X zF7$OzF!4`{90*WLh(0J-DfU6u^3hjZ!_I0|qPtsgo?CUBhILGwGm|LFRcz&ZIu%Ok zs7L`0RW!nznG*bHAw$9>bjphJ56o)QLZ3NSVVGNNR5)*Uyn5Gg{A7fs7~d`10vo>D znm-Cw)y1SFtN|~9@)h7ppl930?O2I!VrWk+;;r#jb3N=E9^~o^hFRLCV#g`aHji4b z0;yA0{I-Q2DOEGxnrbEH+^6JVL3ZffM=Lhtp}A)?BrL|uc((C}-Rmql6`HEedZAfO zZKk%J+FGVa)8lQ_r9-Eb7ah}HG*naL-x;HVO0%yc#_lv1J59~nTH8wfOJ!0Vb()H0 z{{UHs*;*WFUP?lNiR6uv3OH009C5v_=4l{}h|rH=+=1g+>Sktjx6p%#bECr zgzA&%TyS1Qx3gPRh6)2=3|*ROZh(*`3(Y#O?M6oHu`Q&3JOsr502cWB7ULb?w~@56 z;0c&_4yN7}18-F-c$Fwt;$KLbIpD4Ir; ztrC?y$6vw>S4VxF#~+M!*f@><02A#xD;aTJx0N3OHu7*3_V{SpOQj+0lX_|)K^hcxWt9jX{Qc0ncw;~d{SoC|hD`{ihg{*9H4}^Gr;HO*z zx?arKzE$OC9_FseGW>v~WEXBY&zR(_F-buR+N&0QO;D!WSV#pd(@i5pkriVTbjK2C z;qEI?&giz4t&9L#38yds>s?EMuAC}#&{LO1W9o9=JLfw^>|h<#_RZY=`ubb^j~3?K zV!w;>pWp-hCmyYBO*)_syIV?Ur~p7xT^3iFgj0%rt(9CpBgoyw|VQHi|?ZeX!Tol?z!|)vx34Nms zlyC$N9-g1I2atH`7KaxK?mz~fv8^gNRPonkO`f9&RLV6yNb~z*+0CMjj|F~9Nsw4a z#5^IX{J}M|)Su<#&;I~^nEh}a@zGr3*;lH~m%dY0ph+NuFSarl3(zfCn$&N6da6cV zFeO!=Ni43j(wD}5$-}CkdlQ!1U;HE$FGJM(p+Q3nbajf&^$rrL$Z4mUrq|s{jN1cQ$c=hMkt&6(qA1s?RGpws zelmnRr=T6RJlcvqH1F{6oD7wCEO!S#1u1r25R|Hasu2>5F@avIlu+HdX;S)b0X`G) zFp2zyc-Ix(>fL#u+ED8N7a0m0q-G`uk4TC_xvi=Zd`n8E)T+9bA!+ zN4T2G84c3~xcan_9Z$J24^2+TH9dIK7rRl>FBnx5mnzokT~(%)hFp2|GVG*@HWkqH z(jIOZ7^x~Lpmc_f6;>Xfw@jUv8~${-+wfGnkfyD54yv~7=Iv9qiikL{f6gLaF$1!| z=)qE>{UeuGnk5(Tz6`<6`|Tauk*`V9@`{?atv1vsc&)l5uAyJ%mjwJ_q8o{RZ32Pw z*}<@>O7>faAaX^%<7veG4maa#F#iD07ev^%+33-zldm>6xUwfC6{$*#20;u%#H#1q z%|6-$!o6nJd{*6GhH)nQ6{fu^Zzs+agd**GQ5#e}PsGaySs^46I>8K#IMl9;I+_&; zQ(G+bZKMhY-P`-bPGt2u7(1$#J8cu66=woBwvK9>ojsI=Zv3SbZdL{ONLMK>?ep>S zf#jL!mRf*yT&lK}FTA=cV@$wOlS+OMP_?-2-czROb(BKaIZxAJ-48{2HrtIY&V{fm zZSDtYmO@q%;!M&=SRlyjA|Yz!AcccuW`g}wL3dsGqL(g9TaG;IjUjFBcT=?o;T?34 z(i5DgaxN8lxiMm?#+a^Ave#EC<5Na<$yF-TbmcMEr${H9t}%j(GU`HV=Q6jOEuex# zg1YqfM;E9$5UrY|w9^TxT3KuWgoPOZJhg=mDl-vMWhfMuIvRsmOVvtN`e3Q8DM64b z@e_s;RIh=lMuNVuuhc{C0*}G*5!MK6>Y{W&1oXB3X15BHQlr1b>*MVP7n?*Ym;#zH zdr2~Ox|N!suy&!^c9x&C6u^?a_^BgcsG_*H&V5 z64!E9&~~v!X%$X@FNqJWpVX5p?zcCt?bCr9xm5P!#?^&6E>TFuN6TjU6)NOQYHc!d z-9(`#jO6RHT(+SFUrKpx*(TE8>LhRVUAL%yC-!~cFZqGy6nD2~CcpY^I{MGxIQspD zzc;xBpDU$F`MV8eD~4rZ+E>vp^^zmdrMGjIV%sfk{J}(*MymDo*;{Hv^ioLZeX&_J zvzm5SaO7>t`GRItt5d&EIrv3;;vrl|bjofLGp(&es2O?7T_OPiMKVB9P=%w6M+2_6+uTc+E5E}e$dC!t!{n55(N4xVxlv1+)Z^~0Mr4z$XQm$#)LHzg9zc*! zQPLt)NHtwZ=z7i(O~rJS4GA#Lnp&gGreoTA2$dm9jTs1P;EaiFkUrxNYpA&bwDn9e zDj?w#0(yvETB+LcRG#sgiIk*ZMI+9sDe?G^wj+@sNR??5*QtSI*%w_CYN;oAAZQ{Q zDOl=+Q3xA=mW05M*+>F$(H*2t8s^ngJ4(CfnUK7rggnxHsydz^56cVKDdfil8s6&&QP#l(|M!v=tJ%%^z;YE<1Vh=NZ zcAB@_r`f3WR-h`a$Q)Uda9=Iw{4bg`mvSyXxVu4hT4m{0)9nt@*0r%p{vAsWHv4-1 z6^ZK@mO~^{5KqL@Pj9d78v2&fOP0+DcpnSJN2}HL9TuJBwukiPU2|?FZm(eXjz4aif5m5PE=7gkOwdIs zr?CCJHTCFtkh2>M%LY|r4LY#9t+gONC-FGfK(IQ?&I`b0M9k%`syy}~&`ZodQjVzn zM0kU!iz#Pfr65NVYp4qZVEg`bP0zprt#TWc-cD z6*C$ZgH;}yjQk~1HlMm9zhQK-Q-iDPpag!>5l~3u6wY@BhW+A$+&<-cyPo91#C^ST zMrSaW?KVo&ZzWrI)w;r(WgqD+Kt{W`uf!HFcYs))+Oad=wY&*bv+0+|{{YDSwOKC} zj3KL@c~bS_q2a2XY2|ETNhHg@9b5M_Y|TB(M-ZZ}y36j? zHvp_U#_y`nCsy85#r?J7&ZX<;AXn1W-RWnjNvKUN6Ke^X9;3_LH2u>aS>_e-k%tkbdXaxxkFs>Fi1d!v{>WZ?-e>;f-TM}U)TD$cpFIq00>U{Utp;TH`9db7%Cv6U z2ehgokSB_jQa+$umUx%6`%fI(s+CWb_epm{*L}Ov<9LoJ@KHjFNRn!6oJNO5B`Ojk zJeR%i>O@{A)9_xY*w!^BQ^8d}EUl&OYqPwsl?a`T(V1t30r$oG?cda`{R?E@)E_1A;;Ut zt2m>SZEob8Es#20T`LIRa>cGrO(N}RQh_O2cM|I(;c!nvNeUX~2G1WWk+!!67xOfz zBPZkRtG6uatS=(3XC%P$76rqOv+6QeYQ9^YQ1q&))iM%DJ6&vg+ zS;y*=O?^vASMV}kC8vt6$y?Sc_!=n`OcCed8x@u)%W9bGsOt)t zLT^wJ+*Zwg<9nN<=Gh zu{U}xMUR&`>!DUe?{a+G`yuWFYh{ioXtw0GDMFPxdv($Xg_eyJaSHn1$bRRSQ4vxl zFJQgPgo2(gx1}dhx~`Eue#k7{poQ6=PEtMca_ns4VULeXY*cznA z@%*djdyhPEO#JDma>cR~gYx#fT4m8Fqd}M2L_M9X>inxccmBZR$e?mzUR=*CwC*W? zDN2wu4brrUf$mH;>$l4rs8Qm4(~9<5Fz%auxMnxHj!Dij+nvExmg?lUQpGaBC~fBE z2~U*4lw@BG9KD7~i{S>Tz2DvPy4AwBhpaVgv)0hp+uUNRoT;Z%{w)B>XdsD*%J)m% z6}K~VD`VxfY@YZwLCo4653sDC$2-FAD;q))Zse-vp})iH6utidJQ(wsyTb9hReLqh zVB;QsN_!NqX!?tHLu?^R%u!yDBoOU0M1FuE=6Ai%6JsST2Xos`9ZQZ3Pp_8z07(NqUwJ9gNzRR!4BI(7(foH7g8vSjUoAs+gH3%cx znHFjZMq1^cr~UhCs%!|hy1t;a>NWoWMkI?XWaQI?$hUg^=|vR%gx-o3O?YCAxzH-9 zXaoIYq)gk4c`7$rip3La&l{yG{2*LXNBpA)HP)l$Lb;dTvY%F-2jK@+ zm#mzefJvAiGRnv4F<}^nyMRZr1tQ$(m5#n#U6J6tzvdN*B6l-U-l;D5SN< z%E>_?U*$8l(%G2!(S2<8Yo1c$+*?g-ztnt%Xs1FRrgPybR^58|PBc%oaW$gyn^`(w zg@C_Rdv)I^SH9blJ^MQMg(eH>*Eq`HLF_RMGMT-L+$SGc0 zDy&sh2U>N1x-~ey}fRx*ztdUAZ#bak_=o+T^zxm+;3`Bnb6(?67e^;-arrH_NpP zo{dvVX=yT%2U$c_qOlPJ#c-Uyts`QLfEt6L))?1SM&{8PZz(77ASh4b1FSrhu5A#@ z%xKaI7S7pt&&ma=kVHyHtIJxbR@RcDgy_{amo>CW^^V9W_>Ciz z(-z|4R_&yh71dCV6$ukj=E_O^F$03&bVM9aUn2q)B2IO`ie6o19H&$N01H)y9(9!& zAKiM18u58sgiTr45hDayS51XlJ%%S#m0xbth)kolip^ZIXwQMeSI5 z`jJ&TFgtp#wbuF;#`5c^3rGaW6#+dU>{Cmg6)$nk(}0yZa%~soc_Qt@r8`ZHCO>p} z#og%Ta?N1n=Z2|)Fm(_UokeaMy$U0wG0!DDA(F61#AHM-Q6&Ngar1;PT}AnB8Bfc? z5zeaQa{mBLxsYYxboW6wBB2FqCo7a2b=uuRl9c$E%*~;q)r8+xq|aD|ITu{IbpjQ0 z2QXevI*Lw3>HtxZGWNn1B0SQQDGwhQX@$8^G#enQTG>OAARVIf_mEx2TLm$<}qPy)Mp%|}*}HiV^J47_4P7%Ads1lPD> zY&Z&KE!`w03|Gl$3NkE;j50nu(ZE^wRj5#r5DfT+ma8jF>zM7-36Nncr7Llyk+Z0m zSjt}ExkXm*8=eo)sO9>1DV9>=N@W?iSh0}wF=wiJ2fpZo$P`%koTDNkCoH0@wjxBng$AZR5@iv7w*c6uKeVzBBbmA^6z?09>3L@TmdxdHu9ylIq$qfqe%Pz9TI`0(MpB9#)50zaq(&*< zQpj#c-aH~$&Zt=%R4sbf^28=&`1wvz70BYIvuzxsikIF)$_XTSd3i-UpQ^p-)jW8` z>XXQLshLSNC;-n-#3AuZsuKOQh^w{TrEP0Aykm2Op-D{D?IKWI^U&2&VWOy1jaAOA zxd#cDl)A)5N`-i1qmf&4YBv_kgGqzu62TBq$lw~MOEozyBqb9`NS2Xr3s!36m4CY& zVJ&8P8F@PrX=H=$1pe5*$B&Au9iDYss{a5u)jd(lIU}vEN%@CF`p!x)CE)nhlL~1F zNx0T&kXaZ?E%k<&{{UG41xR~g@w|aV>&Q9HQk8CHeYz=b+$)@@TBWHZnFux)=!6!n z3QfQ>tb)?@GA;GaQh;^AD~Yl?F!rzS0RPb>j$eQG)|;*aoDbV82agiFag z{{XWC+GmJs#4la){{XxUP7q3T;J%qTUytcB`!lt4jU{&)bP~$PMW$1c_xMMaVKR=l zla%beJGijzx{sN=ti;N=-M8x8chU5oVZulPQjyj@e$ONv#o#xXmoj7RQe}@BYdgX! zEG<-p<+c!|QPMm?6;z==6JVbibc!i%-3lZ~K1wnPTw5di(2`QY%iJTr4RHCWBQ(+GR6xfmWK~YGf zNjCa5*i~6UU2kslja?`CrH$#Sm4o}pJy5aKC_w~sYBW%nQ$w<5<$b)_?-u7Tcu*Hx z8JW1-9Ruemi8GRc{AnHv5Ut3NHQhJ#fae0;m4l+~2ee_qEMzp5t~Jcm^s0X1%|oZs zexMRdA)TQiGU%bNoMhjgzBe?dz_x<}jyBuT>Z4WhoN{(qDFfQx3lpvOpp^frb+&YU#)Uy>CH)xTIm}beH4!^KHmsKBaal`lSar7lxeZ> zwvjIxYYytUFH9n904N!B5YXa~ghj-pk_ebRVoq|PqDy%RnA(|<;W%?$Rn$oJ45ehv zAj{d(C2Od;CmLfvgiFd&hjkZGNElvykm1W+Ej!7$Su2Lyo;y^ZT8gCxV5LnjK{@pi z6XT}I?}3gV5UP2jhWM|PD%!nqHb?%%`8|d2q_R|0yv??u;wSzRt+E!viRcc4$^$n5 z8bIa0E@-30LI!LgCz=yNo4k>g@=iXMIR5~=?iZuxYYPA(&y?0A(Bus=be#?M-jKHh zyca)gHIYW`V>BpHlDUBL+%m24^!^I2WPP6SUn;n|zS}vK814S2;v7fFMY~mTbN=Rr z>q?bUR38L~0#tlVN^a(h=N0xNS!Zo=tZt$3KA(!D+_~GYE%4oGwQ^PkUy8o18mfMu zeG};?MKe^#sKbEx6fD8^*(MR9s=36=5!7kotG!M)aHm?Iwmjtvz9lM~Y97%BexT5+ zg{zdi!QIsNQQD>GUid$g?_7A`SXE6GqNi=XrblrVHtK{uM(|PURMh>VDsp&ROUBYh z0#6=3^8Om8% z8w$UUR@qdhceha1y#!*U>^ZI7&uJ@|ePo|ca@@z}1UD@VzJ;$Ipg&>zb*WG9_8;_P z?pI@6OU~KPXc;xoZ*LpSt;YF|GV78ak+@U}#32Bg3003~kqy3EKV<&^>NH5-RV(fv zy>!@4+rJ#Jmqg>Q8ha|>N7&TB&whmb?ESG>CvTjChWAB|xRp4?W+{B2rmArxSqf^m z_7xH2>}a6#D%r-9wb7qg)846XJg=TJSRU7pas8+Eg_~S~*uOjQ{n6HWPlhkJYN^yd zVY{WN@k$R(Ut~PN>C`SY&}XcsZCLQLGWPyPADxc$^@@+ZCN*w$-%QOaXH1crm8IJ6yVqlcIZY>K$bmoTQtDeDa}0<7Yeff7q;N!otcf=G2nH&nsow+lGq zBpDUeG9~tyrN|8x<`?HxXD)S0t@sYRM(-IJMpodjNwV2DDN{05PQG5~50w`oN2&@` z0SYEvXA0)kMuxT2uW?9Jf$9jD(j2)jP`t4Fq?zs?wkC-UENfSLD#tBXz9+A&tD{5p zg7H+?bwX#;4KzUgF{#IzF?p|XIGNSgYX`NKQj}1#&`0ljVtW0MM^RD-)hPb}CtFsK zlu`LCb3C%QEaokr)6xjR7cgcz_-#j0bo4r^!y6lkaOoQ|bRokP-BCSE)fWMfK+2v9o$}2ysj8JIyKBVw*4oZX+%4HuPRrG! zv-4>@^{=tIV zsHl^S{g67>H7#5}bbnQ8}|MH`%VQppPj$sU@X+JuBS%gZ25 z(iWdT2$c@z(F~hyBhIZ&C1`A);kH#W2eii~lzoduAQC7f_?g4p>aGc9md{Aqfer4T zfr(n`r4f%5;YG2RLRI*7^!CDp&=aE1j~bU-NFN93iCXSZg`TQx$MJ^2XK>mR`r;S8 z3g%QPs>zScHUe}nkeEcz?x256d1hxRCFIl_FJi>mLt_U! z=qRXfbzRY5xwAKJ8OQRVi%not8HMJN@Q~|g;axHo*_leZI6>5D=N`-D=a!cZrOd3a zfL|v0z3088vKu&GKsO;$TQ>*rMa72OZl;kekONGRfRyPi00KRl=3BV*Wkeoku4iw?^6<(&J_zb3ha$+ZEn0HeQW%mcD7bE&3 z@r}T(RQL@#7O%qU2|<^zB2bRb#8ZX7Ol;zy43p?tjwjU=81+>;jQudvj%^3qqM|X8 zfbNfF-eJ5+P+j}zd3dHS8*L?fX%mt}jQ;@EL_A-^RWmdBRt-Xn8Tv10=)6#|YqZ?z ztK5CDNlug?l`JR_KLD1oBk!#tA>^WMdzT|IQx^=GEeruY#|AH zh{S?K#3D|r3B^iei2)qDsE9hB-kc-fMCP`Li;3$Yq3Hsmn(Cy=tpOlR_>Z<2@Sc#X z0(PohPlO2~A+D-z)b#_-At;EqK(ddB5!AyZNbS<)I(F@!j6~v|(M0Q^*JRAU$q}(c z=T!@uQYxbvacGhB7L_a9BDX0OTU9lk#T=kvMZBTHHml3VGJPd|)koO2DNB)8UVuk* zlJ%*wk}|;M?~GTAGNhuZ)RduWoRT{D2rB2}x8y-uOVhaLRPNcvmuqcMRZ5UHnJHS# z#y$*F*lMst;cQ{v(x@sye&&`scLCk=$=^xN>qvXQXovtmpKxF>OPd=?f|Pi zPC!-B6Tiegyhq!^>`^BRa^`Qr9G`{r7aQEorzXUn-$L}rYO0;Y`gK$lX%h=#rLs`8 zkbD$AB&2}srZ(~dK2y4$?k}l`u=;o(Z~Qo^8@>)9#VxPm{qT0jL}~b6mue65)*()S zq+vO7jXE2^Wk;mMB1N(id$F{N}@)Kz{&e5Uq zABg>mvs2a3-|cKZTqN+?9Q&Ph@EAZrgCc;HfVxT?D1BL5MM}q8zhC zr%!IIuW_n66nd3)YeQ76j;eL#hR_GJsDT8rx`3i|fvqKd0rM_5Ns0Ht*xv&-3TgXu z;KplSVng^fmxskOsWKf%Y)%~wzdl8k;L~5Uwx6*3lostH6(i5)c>95`?N!t2x0Ti2 zF%Bi*OB>toT)TdyY}2A@P^6=E6In`nr7pUHeZ=@czF^#QhFiCHk?!J)^8VOG&v5OS z3cQ?WiqT-$PA!OA+e>sf{S`GO8(>H!3^WiwRBo~M;wW0*QuFME$(Cn4@hVL&p|;lZ zstIfYR-$?(AgG_w1j_D47kZG9j(3KRD-U~Xw!7VL)Eklcp6c4_@V+Q<^y6w*#@6;6 zRRszXWKvQ5)LvyS=*wD0MIv3xI^q?sSj z7r1$Uhi*MHw1+eSd3Ss`i?6VL#i136ABJ<(%=_bDu|?Dg4bR2zjv+YX7ix8`^6*+3o3@;CWlg^BDT~*N*B>dAwxqHSz={?6Z5dp`hDwBj)+5Wd45NVg%DfbU*#6-!cXxm z)ggyZ{*BSOGy7&BI~nL2{{RAwxr(kW@d{?Ast?n)>UG=!C*CI;m}CzXSXtXgBPm!& z3M&5qI7F%x-&)DNOzJkLPkz6$AaC~(t{>A%Jc3EY9`KWm{{YSc8J|D)+3GZ{{Uw@ zKeh24T&ztjWh!(Zxcv%O2~NpVB(4{%ijL>dob(EiJbUZ8r4&3d%j4_CaPUZLF5Q=dwU0w$x|tAVOPt z2i-HBSx;$RRh0h#`pEWd0QAUru@GN4c5g@|#U19T4xb8u)7dz-XZq#bZrp+PtPSt~ z0M@6rMvY-VVf+?V_qUUAw<+^48Cy7}-BE6|Ra@$sxO~>>y;2=YlO(8}5|DlI?7f@i zUDguG#_EOS=)XU|*njrOd99Kc7L{yK=}ou)0D`R-dv-Tx%WZslV#d*tWmhuN5|yPg zF1nG^-PS!y-wkhXk9EwWMvAYRJ1fiVw{+)Yu(o*|?xj?vdf?0t8m2C1`Mo8gsLd`> z+bGWwYPBqcq!Ij57>=bi1^)HM(&+;E7<(GBGWi!l? zyy$#rN8+*zfosh_GE@~GhOM>C`!^5MA3I$i>X5#!v>*0k*pK)4o!m!|HP9-ej(*S| z1~J{>f9*(HKP_EL@KzkNMv{@F=hH~>VG{Z>OM_IPiZLl|8hata6#=*__vPcjX$#Ry z^=ri2_Jc7fPKx7sRl&Cr9nnekOOr^Q0UD2dM@1;OD}zotZX?+XQk*7U3Su=%iT1-u zE1f#?<&5Vu`eTCbsud!maVdDFLrqLRIo=A0hIl0kTaD(IT3eIM9}TX!KT(O9bu)Hx z_n~F9f&TzALHiT3gOGU+gZ}^tm)t4#=mKir5+CbDj9F`tG;u)7h_gSCCyft2b9J;Q zsMc0Vyf9^SD#u{`KIweA{*WjXajvLKD=ZCyR$3y-=k+rud1utRPxM@`>+5V|L$l1=; zg|YO3C8X1+1NSNmcYVxwDyMm8Xn!>mksno_V|B!?@r7BgODE$c4g#;fa)N))eLN6Q}Xsjw9#BdaAr;?h9qVJ~HR-xVHTZhXh?M`-nr+isd5R2b! zmg=Ps#IW5d`?n%24}99ccKyl|<(At+=$ z-CsN8vz zPU^Os3zCMq33-@zAW2mp)hR|m#9A&$&nHr%CFUS^Hi=44S~N28fQUr0N}`UUej4~6 zI6xHK>Ph2zuad6-t7vrV;T9HpEY@BQV5A(u#Z>N6N8>jz?%ypcT`gTp z$eJ|?QG@Lxs8pT7cdZqRt+q7#ZZd=d6bK?-aA1nNtixrY1T2>BYZ+CaCri6MF!4Se zb&oLaOKwSOz1y>Ida(eN`%$rfFlW>(b@WsY$>@F{HHGcb%_^?*22#s<4k_`^)a_C7 z)?rIdptk6qZLeg~(n$mDFn(13092-S$UjKhsC_0ZqHisOKZHzqL}V%Iixx?l=mc`+JT5&i4PbE6RJ}#`u(1y0 zE4qvEVPF|5Ut}>zyGN*%3G^N@$7K^;sy5q;Opk|za9tu%VyR(a$#nci6-1UAxETR3 zN0~7wlSLmOM%u1HRFp@s2ykiR%S2Ay?SNbs3%cQoH z{t>D1j!Q&+rEMW3=`s2tK|;7CMpB0)7Ky9EaHU3D;Ez9JV!5)7p=1p-`(dtSQyVF2 zi!$y!4zRacV`=`V9eiLri$L(LtZ&OmRa+>-9BFS&i49~W)7==%mMC~HM8#%3Y8bA! zJ4EV%5=`{*(lHknj$@YI-sDKt_Pl>eVsR1^tZuuLN_3COC z4_TF;*9~>d$HgB7t*9CRn`WY|P9IRMMO|mQD_&$LinDA{Q%WU!ZB0!(iBeXt^Sq%1 zvMA+^g*&V76%}-;L(UbHfg(vgVNA|&q7FC!DnR4Kkd+C3AP>4NED$*vG*Wq_arLKQ zGCfRPhE-6Rs1ofJnMxqbtQ8alAtkvKN2{!avQSf^x*dEXE0$0$u(>4anvP8eRZI;$HBSCq`4hOi*-ZhxmmqJ8r5jYUr#V%Mt+ zKDzC$C+CAccozu9o4XEpP27>9dye z{wZ(E?W{XdOH!TlLKP%?6``EtX=jg!MhlmV=6jyw>I`uT(Oe6+(r-M*{v)|TAQUoE zQb_6jQ5F$44ho_D!FTzZQ?CH+PbA?6FBjXbxlFXCSL(h}Kma85GJ|aZu8OXF(UgXj ziX4Yln*8e~c}kq?AKGWH<02~VTSt}aIMYZ47gn5R;O;wFktOzx4I;u8oyp!5dVD0# z7&c2Zg$GQK_QHN88GJBoMF{trDJH>8TXCgVTR#c8d#*EvyK z@>DL=>qtsja4X>nkuwE8FjB8^)jM(JyFqZGsbxedg(S>%(E0d8#evPC*$ieZbwc|n z`d0lx80D;H!@(T4cuKSTYXO`$BB)%yIHq9|6z(ZqYEYEvKgK$040sQJc5|R{nj!G7 zdGhzYn>%_FABkztCUG+r>w5iTU?t)wSxA)rBmH0p>5QHeI#_vJwSB?b?UZxIsb$`V zw;H@RcDEH%R?{weOlp%G@a9g%J|-URrM zI9z4Rppu7VMn=t-Q1iESMRJTOp;F`YY1!gZ2VcHD3OT`|^9gN>TtbWMELCh0uqK}r z#fw~qk~E$2EItb_r(T68NSB-;P&7U(soSrFaNQzLae`$e!Bk5`P|%fvR5p(vbRa3R zMz|CNbTHse6a*kuN1zh;f$C-ZA}*@s(`-1}iAa?6Jv9*`L7}RgF%Egkam!+{Y?6+b zkg!UW`gNF%;jvc8MEbC`&21=n6q$P>wtEp;w?eI>zH5af6$r)0h5Kmc1gUriQm!7# z>cIyN$W`ju-f;L++*3bJXF!)k1nbL)N5(1;vfyo`{?oZiZ(XsT=KFA<82#;sZrpWW zqjo#JI~fiuT!YXf)%95bgQupXn2=mSHpYcS8+C7^hcHm}>yGE$yJ=F7RU2(??@VhsHlqpzj5->mCK)#rDlolvA4gQ7Q-Ef6yxx`IRZmQ%D&JkV z!*ewwL+(+IW=YL7+g6nl+5E7+4Jw-B zx8LB8*tj1NOQ5#Hwwkq{;wh!61W(uq{V^}sG@xCGaP=w25a4=@a;Nj0Gc2sI-O7TI za-gVw-B-9B-``O!rS3psuyQT+Uj}*v(ybt(>N<9HjUfEoGl|UPq(8FK9{^UU}mT@a*S~#CX#> zdD;BU`qOLvT)3I)PUceZob{gJ0XMg9Ezk6&a2^#$%iOlp=`2kr*qHU)Mf9%E%geg) zPa<$dbG=`&*QW1A3F>_}o^*gbwnUQ6nTDK%JJan>jA3qcdrN(v!7-fraQ#0rrxfNR z-j+v;7qw!J{{W3Nl9`ZupE8OZmgd**Gawt#0 z@K#@DzexW38>)5d4;519RK))P3bM(PW0k00n#eA-m41j4Y=azp75p(dt=hiN<3$o& z{+Ye`*p!r2cDa;1uQn5Ff9~{aR|(gT$M*gS9VE5+PyYay@l9Ip!#m%m1+{jcoGpDm z3rbxgU;4LI93^K1`+o%Hx##kqc>GdB9gz2*NU1y8u6)wHnB{W+0QXu{Bf9PJM0C%k zKNPcX?8m%xz5Gtq`2eMI8sir(quhv=IoLR;q>0`OUOOf2Q@Px0j4S9eZUM^iy2{I~ zL-bVD)@rI{t&-XixCc@+n2Spcy}KA{<%ZAUE~c8SZi@WScCFkUQz>!IF|W6_dOKL< zH6lLJ^2DHnpSR#uFzp-8B8g_3wa!Il&ABb#9L5+Q-Wr?bF;YL#Xewp}e{e8-ttA-F z)RXhP1;hLYfZB*wP24?#g$e%gsun74OCG=)D2QXrPVbe9S9ayQb##FuT=6c{tql=# z3E3NF8a1f1yz|N}w5m)gi&q*^5K@{u#mT$a!GBm z)TvrTcNg)*(oY2}c5+DI8WOHkg}a62*%lvHbE3T0;?-`^TrM`}{%VEVh7QL)2ZB-- zkg29}>I^>8ezODi1x0rcNz~mtF`H77w#Y$JmF$DT04nK8D#By9^JNI#FN>__;jWPc zjvKCJO1GR9!uL)@$?ew4Hojhsy{_DDDhJZ1rvwEko|Bex0TRTihlHLMnGtJ<5`nQYek zJBbQH(ya(kIuR5xNLcP*XjX#y*^@Js;-ISgdF@ZP>-PTuo#Klk7UbspG%VA(GvmF0 zllo(0W8c|G2f-_}^{*t#c`JjvaW05d`jk0+`grhF$f;I^U2#RLp`y4tQlBX%qtp?r zv-6`XAI(P(SYCIN{{U8SvY&vKdkTy$Urpk{E5-TZ{cBQZ={Eg66+$QN+8ZQ&v43yp zoQFvvX;|Cu^(&WHE?yHhv$4+REaU$GX@1~oE&(6ivsrJcV4r%Lb;0@~$)0j!WHgcr zEcE{X>vwU&-{{5tv5LaZfj*~9X}0AC=>f&_IZWdvmc42 zwe>1{pC|j`(0mRL36%Zc>LH90itgX*uSjD>KS@z+s0x=^N~t9M(PGT6Y)iC&dsco- zAAi~ky4<*Wsr$%8&vf0(;-}NjJ7vhJLwdNeYpqxV{OWezPuyZ*h{8-O9PRg^4|R8C zuCrRYp345pp{ahM){1f&$oQV+XLTp|;-QC`l(gvkNc}L#`yLq&^yUZc3C;7K_7VMS zSC4B?;H69p>KDODy0rb`*li8W15i@ag%i?eZn{NoXUhA@A^08%SKYh*#A9Kf`q2^g zED`U=HnZ{vaeUpGSZf7-Ka*V;a?MdMg>18Lrltx^{3h9tMeQt9mUz}fC(Jind2;b& zB{va#uGwafA+&*2O4?*UgqfrW*F=SosI=IdxGmO{oybxQ_3A*;>pr{af=wK&`E6ZI%gR9a@jxWIUiu;K4S0Nz0nW;j0K;$AC>Y}MimAXSj_`sA> zG|-_NNd-jsN)<`eN1H_CJO@)8QxU{Sk>M_p#3bb_u(KmEFh8a{REQ#GRU@H^x?w5_ z#QUYLJ5?``LUwlH1jde~n z548|0k#Y=oAl2~z!zPlgIlj?DYEn8}FPF1BVT2h-$*m^>& z6p`pAxJ#3% z6>8=_KPz6^x>KWdl+q(*T`>VoI3#>0(1~LC3~v=_Rre8-BV>%x;a2?f3aIV3J7w!d z$$8HQ=YZ5NKeL#UG>nXvLh>)2R00SY6WU^zY<=!*5r>Sn9Bgw&CrEd>T zAF({^?2IxgZkEb{a!h|Wewd@sq zgWSh&xJnRQ875(mw)Qc{eO%j|H#X!ILwb{b4(}xiY=< z++B_1n9VbyKqP##H7WvQ!Ad7kd6&#{{ZyA6(!tGZXQ4VPI3K%_bN?J;`@>M zfZlR+IOSFk+l3Q$+iQvg&|T%wv^%tpx~V{q0+3>=*{;3Hmz`S}p!T2PMQeQa*x`g@ zM@#zx)yiF|N)Qj>01s)DdZRX8L{6%?cZtg_@c#gAypfkPRGNFslxE+Jg#ve6E(B7y zB&33N?di8Zh>g!JHyj(1%4#{xY{3)V{MQ1 z7s(GfGq<>2ePw?Nh4$h=KGg#r3y}W%y{qWE5B@Vfi%9;M4kU2ybd%KI75@O>&Bv$q zZ~p)sFD~!D{!iKamWWlXPprs|g2TULim!>#pZIi+A$K}ya~CC3@J9#o4=p~RZkF0b z+whxtNm8n>*EE_+M#4s_2r4cp58X+Ow{~R(wXr---qoe;jCr}dXy7$I#fJGVO3!S( zH&?~Bdx&?Rd2W>ta^2~d1CHaCTCd}x(j5fKqM<6^QzJ3DQ~V}8l54kHT8M|v?Md(l z_x}LJdh8Z|S9R7S`fbsp#Ew3m&lPdIEA6VIiultc=6vGU@_d1rS}GgDs45dDZ1$Jh zB-9ii!2LpHDe9C+Xi9`!_U;hG!z=eAPf%Ij=Nx%59UwJoyRYI@+^1jgA5^tS3Ni^1N0zh= zX@8D~+5A6Zy2D-Hmb@Ts>o7HuhRU(3719EEG*00B4aI$X(7xz zt##?4Yt!!(t~FNnm{PxqPC{mUrKD^zQx`X03nP`c<&wM#b?*}(A7tX#1Lf)>DGx=^9NdpW5o9bJ`>R z$-c#s?!W$Hl%AESkY_J$k$20jR%sn+QX|4%Fz}HI^2(4z=0D3Exk`9bo}F!+pTd=( z%zMOG*nHtv-j9T>7sqaYkn;Zk0%YiO(M=`&x+j<8=-nvXr%<1fR@nL<%e#Rc>}SN& z-o9@0`xY;|Lhd?Lh7<19Y?7*i(vv?3`u&#lUxfnSl+k*cpAaYK5_Ez`LX7;;l)}11 z1`wg>D4#aFwPNHBA<3#$ww8kL=vBGlwynQNPJR=NuXC`oFg{(mTAsM#6z{b4#4)Or*TmC-Bx$@Pg(M!blu=yT3*0O1>q0{M@5p}ckGA`o zJGY94KBw4!4bla^dH4`iT7tYct#iSV~X%anW zA2!2fo5x0BY$e0Qf55NkKeqn>+rGrQiaF-E3~&OiE&%Q8{1qtgC!DaZLZ{-a3yyD( z<(MCC_$zqYGwO2YY z!~MJScOBl{AzlZlk18>Mz2)+np~FMZq;(Su z*WlF5r}oMa0mUJa81#}snfoOj(+V*4!DTZhf1(-EOVnJcgqcgPgdvSNvXmL73hJ0| zOs9$HfL%7x_+&iclE4(rl^0V*KG=!zS7ji5S0w<-dKgZL8?Fytgn`pYhy<}DUNy=CPUrQ5k_3P3DQ!O`j}LNuRS3lcpg?1WD0mEorcU1O4okY<%C>oG4FS|@doMx2krN{EJ#Q@W_YLZv;@Frc|G zxGM?Tnq|*?ezT}MoKbFydmnZ)w5}hI`ht{FCW_>h-QaRxpX314jq=hs< z9wZUgArX}kx(QttsvBAqM2MThK+I_kE0S62Wz)g}cw7-<)cC|IZmMuuDh)kLDoNTw6Z;HH z5urj|N6ytSR|Q-t8ZD-7EF=-*Go)Qpm@L6~jmqNj#gF-C7obYU$Er{kvP8cYzA1yOnKe zQMqxA_LL*4zbmX*``bdHj>TLL%842KH-2E7H5R+&06&>nE*AA9$cN}^n)~;*Aa~hF zHwf7BcFrF#P;12QZj8i0<=R6;kZcdVm8vJA`>!t2t^-J8%C$3IP0>B z8)?}cdiWS?T~)<6ZIwF|*jBpyNA*PPRnl&s?OWNnQteO+3mOimQ3QzFL^I}aDh|(h z`6Z`aX_2^0lkeBUD=%yT3qg#^>qShn99*=wlBSAQiO9-Rj1FHOy+aL?P+eUjshv)tnN7w*O1k!{O}DWn*7=stwLOo17QJP%`}f| z?DjHHzJ2e>q=1!io#AX=@`l@^v@W8d<0P`xI-Dveq=G>)=+Eu;JA(39dzNN9=1M>O zFK$Z6EtMCWegRiQZfymBHBnBc2GE`JlaeG$*%fZ=#>t@FT|dm>XwGNsv&WOT*5Ssv zMP5H~r`Yu>idNjEBOY^@!@ky1!o7#Kdxk8Hu$6~Gqla2hWm-ujXk`?xWdhKHs+Rbr zg;ctfGE*%9l;@;uV2{ON?q#7;Ew=G~qKAle0CV^sXC1n9axxmyFXlwc1oRb$tg8a{+@CM)9kW!@p29x(ePXSQ2-<8CL zTb{>ps>(y=nzpXFwWVZjSqW{z z1pTs)K@Mp8-GOP9}`>y3ie{j6t- z{(rYiOScNjioHUrmZhxgIju!R{+QIyA2QlnOd7xtv=(ZX6x&OAVw~!xw;iY61V+na zMI4iYoz&Ldjxf%}rHn>Kt#ed-(~46iH|jzX$}beHfPr65pEIC&1MsU~!bE)4-kqrX8M)X# z0=}f|Zt-R8A&>dOwH%kl6qovXitAznbtDp`0r-rfy9=8vG;*-+9^GWEt9YuBY;L~+ z$rJPR#L-j|)y|NQg~rvP(D;blOFL#@b9Y{09kcCaE*+cs{io z;Ar0e0EnQ=q^G3gL4nG34=c9{kGU}czZ-T{X6ZuOl(eZk6iJP%T(DD(6AIyP460AI z3t;H*(LjBWOI=k}BJQIZyIHN){HC(ps_b5&-HExkCAYX8P`Q2+>@d?aYfdTs7dKuC zJHJ%ZyH!zFLfEElN-ALh{t@BQ4TZG|DGdraj}W|FS{-VOMXWQLur%MSZQ%?AGBg$K%7v4w; zoWTpUM%=;bSPC@Bb@X);Z9swToD~jJ#Ro1ji8Xku$w5ohd$)ca$Uo5&9Ed|d7<>|I zn5`)jRlO(Kwor`8il`loBZ46A8A|5ulw~rMrKjwIop{s5LhDDjV5yIPcx@9Mv1Nv; zlsd!J)|7f{NGU4$)F@9+F{DKD!?x;cSYGE)?7nNW`|d@J*S2x|is58;Qnx6p>gdep zF|9Ju8Jp&U_38Mi=+8jC$GP{O?dWZEe9@QFDZRU8lwWp*$F zgCx4ty;RfHZ`_`5avF1DB$i4BfO$sK8;!Ox)PTIZxw2PrO-8uUPW5i(*EyQZj^SDe z0cd*RRIs7scQ|JjKS6BWfLU{vyngN}9acBGybFiOtxBJ_Qeguj@Cm&0oyjO~<(j8b z0%?~ec?B4g>SAw#chb%4OSbMO$ZrZrqsDF(p5t+zY;v3ler=Ign#zzm6(*LL zr8D+x61u`u^AsVuZ{-R%$T_nfuV|+z3zT}Mpy1{06cVYE(6E&6uQ02~PP$c4)i@VP zY8KR?AuDMPxUi{s&DofFjXlm>GMf_HSFS4QmXYw5%WeXFvyq7!C=!G|I?gQp#oy*i ztA!Pzw%jZ`NlerA3V411q`2c?CVfoFhV+TqD~Jf4J5*IFbxu0%I!E^@^!IMoLUQQ| z(n?&MR;vajQq5&$`!-%YDH~Cvp-rl?;O$$!4TDakC9r?2qahGWqM4MUO{TO5lnX?J zAHlmr_Ja&=rm0F(ZqnM65{1psCSgpJ6-fgsXr$`iEzNON+8fI8x~m06_LZvZD-})p zPE^r6oz1*}dZ9y5V0bSXAvEBuE^VZU3XRguqShO1qww5d7J)@K2>o?NSI@7*d*;(q0phik4$-jn6yDtv2EA zRJX^%qjLQ3OwpytRfl41R@`RgZ9aN3V*}TW*PE3Q5`0E$BHD$>2 zOI?ca^I_I)l~uINKBkE&-Vox#ie@D_L>8D^shQ5k65kpjvmNJ)vVTr#8rOJLc}>Vz z4RHdCGqzNy^a!q|SL$3y{5{)uVfP_m?|ekREUovY>U)){rb?!{F;PF>L#?az7!}r2 zH*kH*&OFtF{#hD+)O>nt)efaY!%-2QVYo#yNL{msy$Y28k|qiAlrhaE6&u+~R;W}c zs%J=v;)sTkxLw3_L=wLc2s8Vmf!9Qij5un6EHb%;wiFMtB@?(H-DLy*W`p)5`pkO8 zaBQ_-XQr)e^=;-_aDB^3iCK`iY4}`6+J}?1aq|y~CZDlJm+szpD_DM;EkVeb#fX4`#QuYsYLEMPpW5 zTVNrodUsoDr46A!x`c;h2b6uK*|)M>;-Y)aTt@5UY~74Dm(oQL+EIAVN)nVj%lJ)AfU~L4FB^5U= zT3aEYP}M>vCQh+j;ZUuFnxt1-Nz@{0pGXzkshIRv(@-CVLK1;W4N!?m37_bOas?%5 z5yS}rKCu&tr4pf#l1MVs&JnHEA{>d2C?o{XPzAC5X+joIr_}`FK|k8__C!*JFmfp5 zh||IbkS79@pHGK}hm1ubM6tl8?Q&&5(*hLa*Qof3(_aXI?y8zGqVAakauiOUP|%|; zB`Y-8pP#lkg(PXgMhuZ7(^DKaODU?AaN8^SMe5Cup_1d*hufuL0B$OA^@h>_9%RFv z7cf-0OW&uA$>6O0->{tah1Hsi1w#vL{Ck^J=l4Y^#_Ls(x9|46?3p(n&T|@9LQ`&X zX`jRCpXi7kj#hU4?8hBbCgJDa>7<5 zpi`uXlteMEQk;ZzqICBghFvn=AHn(liKADVaRC=ghLxF&d255f(OdTJL6Wi1IO;VYhuS^lP$-tuhtqOqHqG9FxmVNw00y-w6Cr3F z6|abtpb=}%6w+Sw4GN8~Rx9o`VY}g(Es1xW2I#WoUA4nCQPmlQ)YBi_U$k}cihPbJ zNX|kPo-;HXAahL$g)nTv994LC63Se5^~JZ>6m9jg{ynwj=$eG5z>oV>4HPvR=yUkn zxpA^`KUk04w)rac8;O? zdK3j8-cVC|MmEbL8p)t;X^`b|}cH_G$+6CMkF{{YELqFf>SJd8)&cu2=&4+( zt6`>^Eubp*DUeRQx?h`T--k+R91RQOsYTu(0KtHd45tCn!@5_soi2xchq@6kHxIK zwGo*=GTFuQp$?EjmaDLdfM*Dz>aK!7)A0iwlcV}pkUOtOwKdxkrT zsKKgg6>_k?US2nL5+dPdxY9VfM~6U3NeNf>l4Hz74ubcP@vpghIk*$q^N&KJTxy|o zHNJy`N{@t3%|3B=VK*bFg~-Rw%7rBb0-uLN7tRz5Fk7lYZ(ExH96;m0lRgrk+YD*= zkxm~1Kb$Q804h8WiqZc7I6#@_*4(M^L;C7v;*|wqHU#-Lpnlbp8~)b+0O2>-v0czuup@;M8n!TxW;hJ?2y;OiHPZUu?sGTNTR^FdzgK^@o z2y4HQUR=(p%6xX$dtj8**lI1eI%fpQ-$PK_`VToI$F*T*F-sfA0vC>VHK)|t%?bSN zaQBKWov>dSy;*HnI(3D!Urx)-EE$3wY%jy#A9iEsY|ypihzsz0xG_$(y^<$x*Ae%n zpO`5kW?oQFMN@QYx;0+d=SG?!Q3p3sF1G@@S0wFv$+otgekoNs-!U&_QFj~w**ny| zTgkh=j&e5t06w1e7puMwBg-+1eUmu6)7$P;Y#|j?^(`$zVIUo<*ixY}?-An`*~nwv z)S~-0CFMp*^7Ap31y#)Ro;zx7Pq}P;i_J=kifO25lxL^)+(J@5_>x~S?P6^R4p-Qp zCo?WOxU_p!G2~w1Y45bPc6@Tj`AxO-DYdxj(q1W0k&s4$Jkz{<&-$25OcV>bIeWF#Jdr7zzSk12FuO?0dvw$GP2ggWyBq%i zZdIq3>O|km>|^KLzeD`IlP=@ar6tnR~a{I9epC{Uwv6cB`~qfO;KFEfZIJ zRpkzAsF4T+x)^s+br)l4@rESTaI`k5O(4no~C>W;%APGuJl`XN!H(!cUpl9&=B{)^UpvxoSC#lfF zHJlT<&}E+;BqN;KDH|nKh3!F~Q=TxigQ{J1N9<`)r|mEPP*k1Bf9;mk->DuhDJelz z%;pESJ*V=o9U`twhjGg4(xR409vVs_Q-#x563E?DLw+*$MA4|{g-bxKiV( zmXjS}^r8@RIHnc%Ev2VQ+F%4M2>=AWnnY$wqhW)^L|06m>cuH2SHdUeIv5r0sLzB@#81K{(Y7QAO&quZDg`Q450R zzKNvu6U`GbAfE`0&8i?2P_Sq0fuxBL5z2KGE@%=S1Ij03XqHFRD+k9ktH0OUXaYS= z&LB7k^+B9j+iI}c*A@z^=X9k@Ez!_6i3BAH2_6Xv0xlYA7oPL$RcbhY5lStFx#WCH z17Rbkn#7H2ugF}~QD*-D)TQfsDJ>L;+U0psAsT3rroQ;d_dy(sf8}>El$BIF1F2OP zD^}G6ZbcQosn;9uK9~!P&Lq)R$*nCRrTEWIl9~LHFRH?-tN^YEmS<% z0qXZ$6565Cae=$ZOeH*|?!AKF8*bd90wjCd(FmzqpcHSzc~U80N|ZW{Z0AtTT^kj*)AxM-j^s2zk1XnbKleKKvlDO zJ!F1YI4tDB->ACO1S~0NOr}ib);!|^_e#B!xERPrio~^uRIZ&1U>(FxQ}d0~HhM2T zen1kEYHSxeGr7bnKzv$x!%dk>9pfC4ma3K%ONdm*Sb|<=qeI$Pc&=Y<8=S%t_QMx=3+#btDpIJ@G3aRRiMHD6w`(RFrf+H=s=NJSWxhhf%v8)Xkd~GNFoUh3Mmq`D=sZk9Y<3Wh|o@TRFRD) ztuLXo@u5UWL)jV;UC9^p1xoto;ZLY9(^rOZ ziX(BQp}gK&Z2^BLY*kz8As}d^H`Eax)0Dlpdw-`O0ercL#N}A}Nf{+G`;YqF_VIed zE?B<_aK+B=c&cf}sIE11tQkFpW2%xvf#cX+t`HIusc3s{=jC*-nzGrzA zcdk^)t8(n1p*8m>R^r$|0SF5ugpCJC^G@N&8;MK2SHE_i5xDzN8#W0tkBn_K6zOfo z!it2*>7mjnxD~8ftt#$R1nb%1*01AA+L_9_n`{kg~eiUsXdZJ+Xcteyw~dBs2&;JWdk31aBm zU{11wd76@Xt*ump=9v@oe|L6N)(z9h!w1Ju%`VQfBu zxM&naX+c>hFcj3PRl?ZQ^bFNFn{{EGRS8myE0LHx36%Y?OmQ&#P{){i6mx#Dy7*hx zkK&<2V)3Qa=VsQ zR@Gg3NT{VqN?bx@NKcvZh<=T&*%S5B9oH@87P|U6dK=XkQ-~67y|4p`5|_p1A}MX0 z(^P$~KsZpOI@@JCfU1(IH7Nbi4x)Z=$|t&JosuG$t+qOfgeTQP2k}@V;c*8-4B|j2 zDxxEqBHb;_Edt?hGopC>O%(#BoRrEH?(m5Ns=0}%<8Db_LJ6fpgrwyMKYvdxKc*n! zxp1xj0JDXCN5-l9UEGDYDrrdM zju*spmlQrK*DZpMr{C(+5N8#w!g3N?bXLsI^EQrXQKi!;;hM!4Z9G!u8u>FK-F&zu z0Q(Z^)FM%4Xqe2C3E2{-&Z#I=MYAftThl*<3#e+O0pa-436wBdkWqhL&64E@HDt6z zC7j`Ir9Xt$QMT;m=d{BPJe1$m8WYNkxGaDzDX2}3?uqZuKqL;3K(v{IdZ}%$ZQf`!SSytApJKU1@k>{Bw+kVb z)=^)KKTZDtJV?$dWezS{*xVZ35R@IU_I*~An@4z5Pn8MH+9a|S@7zSAn#BvJb#_jc zdqUv~w#VfC&)Djf`ogg$#bn@iPNp>+J?inx`3TBl~Cq=`|8fRGkMRf)OE9PNIIebmn>BzY zrN_LkXs9IHt`^*Rq9hSVY^6-JmW63i5OaHS4koBsbMg(SqnqDwe9Mp}X}R)c*~Z-K zPSr~3n*+*J`yyEI_Ut<-9hjW)QEg^ZdwJ#5)LfpceR`?-hn#(6`0DDF21jojPJ)WI z;^FSxl91{$JKa>;cIfIF{p2BK1O7~)hf{(CMAVEXTqSNPz)qz~D*bTEDG`YUd_73( z6E*cnhi$ggqJ+ASD{zq8B7a0B5_6TnLggUQQ9dFj zX2~9EQk@M8%2dhp_kkrZKsENnZ_!B@(JoWk8)4?$thqAFGaH--)H0u04zLWaqM4Cv zsZ&l_iE+zTjCsr(lu?#mD;ZOnH8n*N?XA)}0Fqf#gq5O4QeB!)#O`$1ijbI-)i-nv@kzW30!vC7R%Jq_wkga=1+K6;*TA%8HIp$19fWZ3fi4 zzo==F6Cl~#2}m9QX&E-X}Hez{4un;llNGyo6256IrOCRVzt z{B?__c&^Jy`1=c@$EjMw@ERYUxW6RcFCijpaDWPmyb?SIgn5=vC%dFNFH*$fk1%9m zT#O?Hp~PtvwGml(s{q&BN_8?Hfivil`eV&;`eZ^7qSYjJ(0m_+sLGeT&Qd z&hv5%RAwk>`H!z}YQ|rs)-TAt_q*IbGxCmZ?p2*!GAc21zgDAiQrhjo)KgP3pMi>Y zw@J3qpdMyq{TJT3D=yr%iryxCBzjeRM~v+phj(OUnpd81Y3kS-4-NkS;&obweC$go zrwbP6sVjERWR|AS-b8uzub4J39}uV5t}nmWyu4%JdH1M3`Px4>*;cjNY&QoGj*igW zmYqF;r&416-?}i;=xO$anYr?}xvhRu`g(~>NAV8nu_YB+>2rEBPG*wLUE$=9u`aDY zqAeXZ3TDvqeafNO&hfX_KB%XxlyQ#YJIupd38=edtTx=XuHkES@MY!zw?Ih0YH8GE z_P`G99^aVgTd#o%*5AK7;?chlex4ww9S77;xyug0bb8fvLVpr!?W$o$UVehn`k*&? zONp5L4*S)_U+h>f(IZoV#s!~JhT{+kP0A7^FZtKg8tY`aWdM+>hjP0w9I>0Hi$;p@wN}bj$!rgUB zx_T9CI84O)B~hNAw(ns-6A1ekpji2phPodF&DYi!#t+nno{;yq+)`QikoESF-Qdg; z<(h_MA9ykLhnCE6!Ntv?r|(}RcZ{*WDI=I_3H`xjw=&hgOP0LS)KHYPf&~1;Z{XQc zHWdcBbXwh#jFOdTsr8AfigPqk)^SdUofS!6<4(GD@qn%ds>;_z4^%VzqAu5I@N*3iyhw6Ikqk!ZLhf1eap~t=LF^yY@6mTiaLkV zJyb1(0tp)GWfj+5xj5CX3+LQNnf6aSvPN1*h+J0SJ+NfXZ&P&iZm+^=N!RQ#PU}0C z8g92ScP1)AH3F%laqT04Z>dtP*M?Y2{{VoO5iivdI(gZ+R;{|RBjIpSt>fw0fZcZ+ zUHY18p4q9jPA6^qyc7Qbq<&W@TO;~i3Z2H^^0lUCVVX7j;YHPWB3LEauTU-j0O?DY z1vps88mLe1gls>fNRgAmPP!*K_n>HMqojQ^b89=7i)Hr%a`l8}R_e<(cVKOj6<2Al zY_jSmkVfPZ97mFp2#&pu+DOQ%mmQbCwzzBPbUYW&u06yTPQyFQx_0B-7URCHs6kf z4NBVnUiwI60J}oXy)}+F3aF~uPpZr9DLas~kO(13Ac(l>G1@O3es1nJS5=aIzc%4z1cmTxcF;4pS6@RMOrl3r3QeSA>t9Tf=pYkjCNg9lcBFVRd1h zw-Z7~IMk2Z>HE}S#FWE7!#KCLAzYe_pPRW3vnS;Mm8WVf)~ZkiI+UM?dj5ltAdO}O z<3D2y-y>o_o%KExAGqkW_mZ+W-8*(;C_H@ySJ7RbiHm(5B@H4HiYgYHW{rf6p$#;E z+7tEx9{p_{7#hBHxVY-PxcH^@IL$pRO>Q_{A#DqJT-w%SURN$`*nJLVC_TzDqI z4?Dw060uven!1jE?#%qSg zDpm!Mz2=P-S%cj-)E3KRuwVG+fo_Vg$uBIblJUsY=VLbdcR@?YqI0U=Qhpy*G6@98 z%tw}YUwI8q59U(xfg?gkAbQ*X=oXRd;B{CZL^$^-FN2&h+7uq1U zx{1sc-8A`f;Y*a#V^ur7Y|Di2Y*AJutN4!NmqaG5rOsNVm$xIY!U^z$jtX{)DK8mv zZBfhm$kr?@PsR`zKOtAW_ZzISHA?81Q%Ff2M%}WHTC|h;Ysw}7%t6?zz1qJf(^cWW z$=BPXY+bQ84Lu@WSVEtnKN~7e=}%}o54t|E@}s%M#gL9id`(}LpX}F`QrPC*JPal| zb)1-Kk>IA<`deOOrZs5LR7)!1UcetdAzCgmQ=_{?u=okt7K~+qT z+X73Ovu5ue^`8kal3(x-mX;BX#bKerL9=%x=l(Amz&YjjpJvlEEy(K490tZ;l z<@>W5*j(2{?6Y*W^=t=v|?a7~(}sY;p? zsr*AhAsOq5>aT6B)1!^tbs3F-r0o7WME#ST4HVj_@=?+?ICt3jR~_A&Or$C4i37|m z7_08@hTRwC>Hh$`9@UJL^@L?P1s6ar#Zu+2u$@%X z0AGbt$B*cR>ZRy~;i`2+k@};$rFTWE-%-ZSumO94ZP zNJQPDDKbKcBd9$gh7~Jk@kCs1p(M&>AZs`rf{0_9S5!`|y4qJCYu078rA{;bU=K)E zI)x?60gQp6Q?DgwSf*QNu+ms?ihNe0*4Em(#u7KxD`*p008ga#i+ih+GB(O;L@Qt3lf}h3fo{rnO1mPe9oR{Nf_Q)-9F?ye{Y{uG2QBD{l=jMA8%jJfiA0 z3M?z7qjXnJz=rI2z)6(Sfiz2|p2&DrGyFwhf7AykH+-c@4sTOil7s`SfsZ}z+lpwt zr^`!l1yq07I*a+oeN2s^jUqbGtf|ygOG^w5$C3 zg{$_*o!yXSE7`4{dQJ8Z*b;iWkvJGI0-sbAfI8}YA~&5>fy<%+>kAs-^1LBFaQ6h! zRnNiZA|7<@Rq*Y@593^ek6L25GR51|A?2%X{qmSP#-q1mXG{t#cXUfH)D`QfqsMO# z+Z|Y_q-{hr?@=kpbd5`0MUi_+LtfdStE4VdBrj;0`=K20U)ccM+PDoB%onV7>9R6A zg}K7rTPjjhp;XMGoZd+p)mgV66ocU@#Y2-@>r{ZHdTa8F2y#tv)dM~)>pLIXelHS(hy+kKTCs7KOu4L~r@K&NFXrNHJpp-Ids}Z6fGVq5~q~r-ftF_q$ zAuA*g3C9N%oQTS*(0JmniR38;9sfD)puq)SsjWMMNE?oFA+HO zST9K%uQR#FM%P!BN*|h9(2$Up%=`dJg87M6uVY69LF&pCE@5d%I?JFCGxt-3A}+M; zfwA<34u){Vi_?0TBj$jgwj*=2Q}P5+cazY+GCCQ=NqGX~T|$(9h!sCuHt$b#L*CkbVQ?WWQ}@i30%SOn1UH7P`(EQhxyp4d?6|YNAQn0HU{ddXc8=w%RHTSqiyjuizhrRTj{1sI`Nd?Y7Q4<|mlM`B}7~nk$0j%?0^G;R-{0=1k z*!4_KWwNn&?q3M#U$TN~tqs(rbk!~FeHtgjUXi1UMVyc*p8o)BsI?7u(v5`DnaL>zdi05aJW#T| zyM$7Uh%b3fXUby~#eN_uHSPx3E;LaweQBhFy{*zQ7?t`U((NWG;i2mK%+rm1?3LjG34NIFRS zL`LUPN*oFWR;G;h=m%dIR>(@~gjwr*V@c2K5gXi<7x@N9;!sHc07e&)awg?HDkf0i z+&XIu$WuAOEU!d$08dG219V9#dBP|*DMTee0q%qlIZWAVfLmx~X?>``A z&JV7mOTp7sa<`1gV`hp{jIu4WiW!0;ofLGk`RwN|ePS(xQ5k^9wP( z!u&9=GwvtW<4u&g^JqGS%z4T!Uf@+AK|5U1M2f-oM0H)th%V%c>r?a16sYhd`zY5th*t5kg#_%BtFS{nelUdm_x0HjQD zm8p6TLw`5p&8BO+F)#gMf2I?t61E2wz|S#(G5Nz9cn1<(wv$@ic3W)D$-;|BftBCTy2A;j8uia^5?aglgKtHGz&pKr+i=8?`?ch zhSu>;0`57SR@@g!Lrqz9xIFdlf|b3s5ACUWz;>-kn$iK+pwc`$%Fh1)$mMb_V}axg z-@MN9!x@Rj33Lwd3nMXZMd3`LhAsDtUE7ZCRXb9v&~+DEVit4?aj+YGDW6iA%08TQ zP6d3d=R}t>EMD=3RI(maN{R8(Mgfw!d4{Q;PSSJ*vXZYUKoi-JHbljCTy!gD`zhnAWfE$$>ZX~er?#O%x{?9{ zG@6Qu_C^D`F6ugM+^y$p#3Ker9TWrHM<74W{K}^BLGv~YeW7o(RSE2MDR8ali9fnj zbUs2geX(Wq7M~Z)?N$@HvIik|QLt81g1DmUx>Z>$DEW433Qj|jl#wzMFHHxmwCE!> zp3JqbB&~ODU?h+V1){iq8)(Dr&C*-g33Whx)ma03ikDR%*&bDs$i8>_m$l(>Y_)Al z#?PsT)sGtO+PTi(I5O!^edl|!lTVf8gKP8{bu++jQ-@T!UQ(c2XHbcSIi!-WQ}XM} z884nExD#jISQj~Qh$JV&(NUho#7^6o8@$9opJcaTeXC5nVzumUEQPXh8Jqou{ zhbu4k$5K6l)RYA~NUr6yocD zQW11??uj^nw98OHApP)-nc(PDkH_0cHE2I#LAYR;h21CfTwa3W75KesI%jA8x(pW< zV(LGY0c77w98RD*tUHsgc9ofyny#m%tZaD5mcuHIosXXv4L{Y4 zIe8{u{W7(dy$K6dcgcR2T;*x?7msE(T$Qo?+htWWFDp+F;v7%>sMgte-;9+108xd% zdh%@F`f;4M))@o7@5NT^v$L-1Fmhd2A6;q^!Q7hLRZDd;k(eOTxEJ~(V`Jt90_#HI zuZr?~zma|4VCsguPvt-GS)1Hls{a5>$@j-H+FH6ihixC`%vUAU)fZ7rgpXI(_=<=F zQyW;OwvrmaM<)twy`!_8cN_tiAc+R`-Ddv)aiwkN?y>>eX{nVj4N(ubIxj+1#q}0e zl_-25=zm-hvYsH5E~UEm3IWr@#s_n%5j^A-7hPyeN{~lc?)Sj5GKjb)sZEtKK?IEl zNEywlvumoj^vNP!KFH>c6X?723D?V}k;SzMD1}Wq>UBPHh}_yPX$1_Lbr~5o|TA+rLT)s)3R>nZg2!WNn-jH#_tMy=YD<*0z29ZHj`7Yx&>I+LmJgy|;A?~NP>s86U)E5l@01Rriq9&Ia zITayV73P&BZ9c;R=F6h8dz-&0{fKB{j^gE!zvV6!_tk$-Ti*h3?ncs^ym9TSsRm+S zCdv$_=7fVEEJNMd2>w|Qy?dqCl~^l={BXD2puCT;PTBGjw?-1}o#kulQCur&k+#?Utu12@m3u;5^vu4bfvPJK-m7)GM+ld`oVXyNE*5{1S|4hymixM`w0*HZV$5%AJj(bm*)W$oGukxhEGzp?9uf~~^Re}h<-$EHmEH$qn;HzifKHWtwyXygvB zt!=wMyl>wpCDc!imD~-F2YW}^;Za(rzx%&GQO-5)SAY}RpK`Yr?48Ya+J#vH?~|ji zz2memE{c^-{Ndhf=sho#twP9OsDQEnSd3;~-t+sG=b4_ZX~Wb0E7Y$BHqKx!KyN?o zps(0#P<#m1wO>W`d`H6Bb-(3RZ=tMd)X6JILV*YM&NF8(l7-bZMjS>4=uxB+zIFcq z-UIA6waj<0UTo%CygGztxauuzVAm4TeRgh_E4?)fA&E$ZD7z! z*zki9kZhLk_)g$>FXv}P#~km4y63 zeU#(31i4?6(K*0~I)|QFT#Xb12F3tDALpruPP@ z>5$zrSt@mUlc?wf9bw(9w2X%Cpvv~ay^*eVfC_!-*JQKz%k=`ds*fGDJjMhRky%;s zS+Rx?qM!?}yERmiDHNl5{>X_dl*yi9q<-%Yf2x{#)DD*wi60RK{`2nP_N89Ydpfk_ zJ=113Jd*KConU>x)ga_IYEZw+?$@TA+SMylp7xOP73yoQ!7;T_QGme-Z-_dzo1O&! z01tgs{m0y98xY_q={EHb;6r5(yH}Bo2UiN3F5|(LlUa#J$x!riEH& zbwrM_9Fc0L;F-CrVOUODXD|G^t55xesDfDj5{b0=)*G~)&p3Ka0WbJ_qcCImmfj=q zrAEet#I0>YYoAngJp>TY08y@?Xt{m@bM`_N$n_e~93KqjtaUX==TSq;rd4M+X0!y( zmg$ZRy{Nlz@d0buPoin?a;dbNpMCZS*15GP0pg%He#oVa)!x;9 z@kWnQeB#e~*3=-Mje=6Iv!rNoveq@9%CeGsQiU2PaH^SouFkLhItlw!AuZ(9N0J}XQx|+w zRtIwa)hlX0WTLmbzp#kx6aN5oJ*ygN08?n(HISFx9#snh_i4=vP)aAL(ojG`mmw0# zKIm&$3Sp5B8bI)gXrnTeC(Eb$qu&WcoaKJ`bbnMsIzcZ~uR{qQ$nKCy)GM^ppiIbV z?1_GyCYcHo(f-6Ypp&YF6r=pIgy^Pq(4vtbei{9dAhrQB>I&#lw5@iJVgCS%7wN*` za90A}*dXl$eX!Ttg#Q4kD}`Izn36`m5Z;hTE=uV(^oh$=e(tc|ig(o22r1ke1OHFKA7CMEUBhc?YnA&JHUO9-8{kZ~Mej2Rz zdXOr6M%nmBoy!=i_PFKICpm0U3Lud$IEBzuDcf`clsFSGxAA8yV%f#+oyx0ak;R&( z8r8)GprhfRl;YF3uA6N)Gqylx;`)dS8@kv_Z`3P;D!nw+8oB^n5Nui7XY5pRU@llS2@3U9gXLvU*m6sC zXs8oHTdQS7K#83v2Dc*%hJ{vr9Lzf?s=EzqdYoz0EeProL4zZE95+pnJPnc;^5NYz zB$*#AA@phu^OgA792<~^$WQgBtTctu%o9%KYj8~|Pr?qM_0l1XDfz-rr)5Y`3QpbL z9wH=9k<|%phSZbOQ`6ZI^%NpBuSmVC8of>mq%RfT}Y+^ zd$gW{BkCrDRGCpvP@2ano>t`pB+UDIL2*W>d#Eb3Teoa5WB6ijRV)P6SE#xVj?yFn zi)%P&ypOpu9a)u#!>Q2q*i}A2Z3^6V&};33TEYU)In@+TK3e6hD+0u7>u*^t9i6tD zBqi#W+fsr=-&#Eojgw)t@7}6k8Cs;4fZV_dAYECS*O!aoe6DPkj3Of$5E(SEL>l0$&)_Qi9gAs*P8c1Sge zqpllP2I6%R=>kmFJZh|M-1yTL=VCPKFH)$0HPgZr!l8NW%q(>ZCs9fWB}tOM zt?Y$+D5PY%B$pTITMy_4(tkud zqzV>Wk`d44ihzYZOyQDbQ{g##;z4ceP@{{T%Z(aBdY1%99M!hK;&`f>1Gj!C-( zZVn%IvENv`^!3@~yQu^fnD4A9e@{lK`4;lE0309=re+*3QzWqo>1)kz3ffIlkGK4g zdnTd*_RUY*c3E+#PQvL_`t_NKSo2g9;Z}2kc&j+&T#A<8WVSf}0I1?nmsAxZRDSr@ zV(nbxQ?kNm@~w>Ww^FvN9{Ngi%}tVu=*?Xsky3!9u_dKqVtRubA$M;Wp;&htw;F)F zSJLmFDc>w9bcsmnvQ^>sLVlxfz@`@7cqKYtrPnj8g*|+trz7zKg{#BZL_B06O|8#@ zbJ6`UGxPwu?3$cx{AIQje?cZD7ttht)DfsoYEP#f!joyLy{+}5@f6eT1~^{D;-Y+0 zk+OW+`x>;+>=lBo@R9K;EtLMqotLrrp-gjlDQ>~p{slmk`qt`-g=x7il^@n2=JlQ` zK7_nNlqh>(;|9xUHWacv3X(PZOfcRL6rW0b6QY;4PAy5+*3)y-sz;17>(ZM_Cq2Kn z9x`udqQg60mBwBlRz#IV}H6-{<4SvHBOMK#tiww^c@~7={44YMy zOZj@H%2dm!Sqegd+!T^J5I`Wsbl2{uD~5`}y}uYC%`_otR|0cJX@t?y;+&0I?o*A^ zB@Ns1HIn7Rode+kqCok~FG0$(%V?YjJlf1bpQh2`PO8;!OM$1bW6NJjq)^bzVK2?dL@5 z(?tMeJWqMQ;x|01jpi1cGSrKuq^PE?2uPo*5_Bo|Mg6{4mQd5hWUldK#muQ??$#Ey z`h8E}Ott+HtYyfhRx0Kv2WX^jKJD)1c*OD^T;w{}F)kp&*7iEXKHHmwG9o@g)Lwg(QMy?-73aTftHNG2*zf z_X)^&V%`2*ySZ-lOxD^^7J@*Knp6n!h#b}vs<480v`{GW`^y}gf2^yosk_wFJy@c* zln?~T@&EuQ2*wJobdKVOpwGEh?_)+KadFCv3ooj!-j~pjrXNyvsJ7yyLRI2ALb@=f zg^oHR3ZT!6Yc%p&gyxY@+|0e3q8h1g^&w?H>kT18hXohY zxF#Fi<&o0vOO^-+G|ztbx=RjVGKZ|5EHT;J&3bEmAiX z6E7)4o);MCoff`P&Fj{NY3r8ru5@iJ?mu*(XBP2F!sRyO*w1oVBPnvngx+rLxB+d( zt?#KP+(JY}E!+&;Tf!;YRYz67IHZno5>TK@oT1cIM@Z{*2>EPSs?SiMMO3LaUWuahalK6>~B@KoZm44Rh#K zq!wjGna?fdIrSSh3!Qh(TNLVDQyV~<(W*|KQB#m{qPE9da+<1R$ks;O%k3x=JJInZ zd*dEkSFQcKz6AEC#t_Z9r#&4)2GcLL3k^V*mYNiA7NG*FxDRem@g9;G=t~vNu`C8HN{R$85GcnPT3} zF6P)NNhvE?CPc=X(q}cADjSPP-s+Urf0(%uaw*7OLDxaF56~gsRP?{4#o&`1{q0PB@Do>z`!2mfUFjBgycM$G>m4^KP*ke%eyC!)om&fNbb+r!)(?T4!mBHkn8M@AG*j&tY;_lFqjsxi zLgTd#0#JpzMA8nu3{KlBsyXw|XxT_~-G%2hgL^lW?K0SvIG}#`o9MMw6ZcD+5>89l z)>n*C8vD)Z6zmZ{Q+kv%*V(K;^z;*Z&EV3Co97r~IJH{oTDpEytw-Af&kris z<8obLu8K7Q^-TkCC*S*EBPzU&8K({-R}7Wz@P!i)m9D8r#ldapw>}Z>gz7FY)p(qx zEG#7|-y=B>F&))YwmPT&J(%59u~bzLQB<_Bx>}}|rrbi(K}wLM5JG|IMzICWymt)b zkX5BY9!^_H1L8DrQ|~3;`A?VXF&wXyGaJ5pf25%K8(nqIsrr#kOwz&h+DnIO36Lr& z0IEWBltqf|t1As*XauKcd(R<$0G;K-){5G(=i6nkT|hWGAd&kg7qnS%F$%$9S?x8h z#`Y1Dpw?nn7Xs;>>g}$sdwyYpdL;h<+9#nN*spYS)mqueQnkXxQ)IcnZ+ll6+EVB< zO3<{0rD^sk5JV(iwLGcKWpU>A1ZWldhrSP}RiB%DF=(U{N$_tX9&M-*2v+ ztWz=wQnglvQs|h@T-DcQsa%uFC;${d>%SP~zl8XlEsS}A=-w`^nLA#h zel+e+?i2Wrk4}+?w#TurGf1@;ISFNNNty*U_WRo9O6jTYck~(xT7VT+nGoyA>JpRt zq9`-{)Ca=Fx$S<)=A)bzj9P6LEI!OB>04^mYPU94>x6_Mv>l+_pnOe5(Esk?bu8l&f z%+Q0P0@MjlzIk!w3!X*A;G=g9@%H+?JPP@T&0jod*IQWcv3CLD2Z{TE-lPz|SGhv( zS4WiFs-LWOuCAfgzf)B5*6Z)Sw@Z#V>y8qotz^wgQ6!yCvGj}F$iQnw0AD!Tb4bGH z8j_E4_E}MO<6}xnWK!ALIZARp54t5~H$&PunR$3E^#1^Nb1Jvm`16~scN&n=-LADh z$WnxrDpj_rO!xsFS>~2HN#c#o6{qo9?=SK&+GHi>G^zc;eItJ{Sn9%--Fsvc&?rl< zn2#0xBH@E&?oH3zJp9Yw%l???fG|akIILo2gfr zbdMi=65BSBt5V65drpr+qbJ!T>!`XZqrvZp1YBO1Zc5b*(?t z3Drf+SY2pN>i+;iL~0ZwrTv?B-9W0;Sb3+7kWjjb=RuWY*AY59qfTy?(Y#F0-xTxW zZX?~LYV13*!f%z~EuqHU0Jh>>2cZNJ0d&aUMQkqPlvFqrB2a@epbYvL9nT=AIyl@o z74k>*fUzc6YvmhXY3By(n*MN)&^@wv~NTE6&%?nT3j+mZOq@=7xeX`<_+**7w=L*7lInuXxQnh;FkZq4djP6>U<5H&-$NNNwU% z=`1hQ`y;#`8%zgxY<)M@pL+8y_44}DAsC8XtMH%YJ{~kR^?|@@O5C0R;I7rXli^-7 z?gzGRK**WnUQV^r-SHcx75eM7RMiay1TBj998)f~W``S41u6s)F#%q8UFEc;l7Mya zSuA&ynAxKyK4!RRbvh8fa{mC{P^!u}dl%rY-)VVP{hH(4lT&cW^2+-a_k!x00LvsdCMYeNQ-|$#2B0*v3(5u~{1o+pNse+ba;mXgfj@S`Mm1 z%5dv=^Dznrnth*&>$l-1bzVh$bxOxL`lclzBE^-U9-sLgFYRE&-F(;bpHH<0EigaK zKK}s0Ak=^G(DH>Fdz6{c`Hc&0Q#C-7JwFpkWDmX|b8|%gZ@CiK@I!82y;J^ znB+DdR;8`9=Tr_Mc?Au7p0#0h_QT0jtKPYlB!fAa4ofMC{|z^a;-Z0 zW$;G`awVrW%d?CPjIS@lZp=~J{{S&s>L{CK))M+$6vX;vVM9eI6CQ=-{sPkCM>)-@ zX{X&^-n`$;KH$5y4PzZfxK~ed)_&?auGib2Zn>^gMALK?6qH{fhFYo6mRkJIwLdPv zN?QN~p+|D0Gy**bys}Ya>|9zXX{Wb>^KQ+?OOd|;+gDXv{-0cO_aAoeW4v8brR02! z>zM`ip}<3~IbEw;_iMN|6F+QWds5G(Y8-xNYJIC=-gjbaveELlLF@|ISGb+-Na;OK z>5KQBR(Xv9Ah@4XN$EbY+9|opXlDor!m|)M8*Nel0OCr9OY)U9r_QjojcOgAvroP= zk^IYT7s{2AB#|>eY)E!dBSXR!Mb%%SRIAuU$jv$U6YB^P5~CY~>9g|JCSDwp zQEbDWkx|psd^Xw>?k182wmxkTW{!n3{{ZE?+N5-nM7*Uhw8n(2 zx=>_Dd_7?T;Jm3S^i?tVdO)QW3X$<(#frAAG+#feaeNm-9wW8(Njlx%&Mc0YS$^w-hzC35d!yHYXMRAE2(rn zs~2g3k_5E?eyH+z28-x9icn0z9tJ0(5Thub(Kv8WJ9IK{o(suvAc zdJ#V#C~#s{mC^~AN$C!5i=I&XMCa!VRUa`zgn8=@4N;dNLJpc~4ov1kj+*Nn5}=I9 znCamPkqiOp)WQU{qVh_ZJ5HS;3gB0xNguu&_AaXEoqB`qg(9ngT%C0A@r3Dw?qnaP zm`;#O&QhhSoG!J5kv+;tKYUFCDC8aF#4%bd*BEVFdqHuh8(s~9phkrB@Qoy~)*Tiq z!t@)ODw#YCUtLR zrO;)U20@gE%(|iXD3KQSnOWN12|!$8?cCxELXd6U#Pa2^0sO=B=pXiV4I#mx^BYWA z4&l0Q6;}S(vv&$7%@24PGh3)aKe4S|bTmuUUFlhC z9Rl@%y-p5`RXgpuDR~D1A-3LPH=2$L8T_|!`Cb%>Zth=*ZfR_~^uZ3MOgdocJ6#BhKCNmM)alH=E5h1qxhBs-Jb#p&R{ii ziXqJ7q6%wC3#vJnc?#bEIq|2A;PmZ6(XV)ZsOZmerO+^k4-8UXu)>II>aqHF;l6tz}kTUX2VR9a1 z-Az##pkB9y@@F4z0hE`YaP;>oFO_mWb(_~aa?7%s+=8{@O5Uk`$4o<|&c7 zjR6Jn?Ee5zM1UphIerbcj)?sb*EA(E61xjRdT9vOfS6iJ5iSxYVc|5!xDHFAdPE{r zrHj)1rSSRA941O4SKGG!F09KeE$J z2U2`K_zkfTteEieP=$7lr`tLuLL699wbV@ezAW8BRJPJOY9b4pP(m`^?1v~?^Y<)LcSSza~z6w);=rCVN1{LlPxUnIPxT(%*>CD&52@bwS^RnZO{k@iQ7GC~(0gJxHibSZD3BHkR6!mvG@%~XBW)10%=koMIpVAGlBDY? zk2sJf@qM)l&RcP zWhEz6m{bu@>s3-5lWQ{F;C$euArw*XB-n!5cBKjYLSjNHa@PL<4OW8#cT%bi`j6es*G)>|R0~cmDc$#wW7OG=Z%U0{Jl$p-E-K~pSAzF>+qLq7 zm5Mx?-n{}+*Q(efNXy>#?ZI_c#>2a;jyvZxl8qGNPo*^h9FDbGo1E?MH%axsY*1uoSsjOYr_6wImBQ0vsnb%(AyI`}ls*20?u_ZH`VNI23 z>=1avT~$FgcF+o_S0?X+w;D#AO`(>ql?k7u5R`<^-xiQ|{M6K{IeU%{0aXm|ySFkA zSva+(irm}N33OhMte6F8 zs-JlGaiVUqapZL`ck}gD?+GnK^+WnB@J}v(ig8ioJg-ivv!2xZe~P*On2bWh!7%Uwx`1pG_1L^$Ajm4UfW^0Dryu zDm|~|9G&{Kwkwj7`hpr^rdvT$m+*i#r_LVexF@yMRhJ^n@|%U$wc4xQQr$y}N>E*K zAt5GGkRnj+6RS;=?}u8rW_b-&qT_j~r=g&tthih%km9$csy0iX2{V+;gb6sIMZ+N> z5=T5|Si|pA+3v+uZ78GU>k!>zN=PywrQmwX5VERB>sp|$`F0=6wCkg(v(sVu+oE(; z^fPYXUibamtf>D04Ql8Lbes(ELN+Od6+6DxR$J@p=&DqvDybHx>Z#T6sS80Mjboi~ zLb7W?39_q|bBo&3rA`ob1EBqo#?I;{7=qoerMJ{eE9@_dxgU(Vr?-*H9l!F;%-x?~ zr$)_sDk^9hpna7J@Tz*cXB%WI({QCpGm$AE3}CY zWqm%?M0szK_ zZi3y5*e)n+2^Z;AIVB(uiLxT1b`r^y!*&cx{uyNF?V|KfikN2dg{_Mwu2_*v1+QxXGaXX5xSNeKDprE9z&VETT zO?3LJXn_a}w!&&NJ3NwB5Yd<(K~jwTjrf*=*~B$V3&+$gn^2MfNZY5f9t~6zZ&ZVd zCI>nWo!(~U3u#x9Lt;QS4*u9qU)yd9Gx3Ka& zO==J7RA0q;7~-*cEJeeG14^^ho!nrwEVPAktrAr!C<>@41#gUh}I>-G{X^{wfUS zXXg~RS4|^Ay$Zt)=*0j{#xHhfSFxJbmZSTDX@L2`R=-Oi-2+*1q6qk2MxIJfgm*Ym zM{M-WxZhj&lG}oQ5{S&I!Itz3t0m7GRa-7(Rh4RCGg7GN*xurYPzsbl5cXWOA?{$2 z-zuCfoXc;pw3aYVJZY+ge|2=YxF33Q>#+hu?}naM8s*>^;Hq-E9@t)0%;E z#4iazxll1g${eIaD&8HXQW6xEk13F^7?ey<(&NVkW3m1;yrH~*B2DU@e0!H$+q_|! zir2L_Iw#~nZL>88Qcl&N$fS*6<~$KZ1hDcYDvBm<7d5p)56Dvlp(|2<7#&aB4g*Be zN|o%?FT3cVg+Ag^gr!QFjHM)qh?cZ!j$m`PS~0+#oZm7UN=)CA6mZNHxj4&j6r$m9 zM_Dq`wMtL*ls&>T_jyrHR_?6rac6fe0kykXb)ll--XE}jNsXX{e3d#*_vB^jKH$|v z^)oS=xO-XgJgci)DJ6F+R)%mFY!=K#ntRSn!fqFr09w+1BsO$18>7y%-Nr~QXi3Jd2aj;dqUa0M=b9oDH@QL^j$))VCCLLJ$g+bO4ye*;w3P zOc@(XoL8ab^0s)pb0n?}0erFjM!uu@=W!NW<{5-4uaEcuhgg2lR;qixPTYv~E*_Z$ znpJ7Glo_ahdq2QFtMc>8=rPR`+<@u04g>hFi@eJ7g5xhCx3?v*@$o;y)5IU1&Mmo8 zxsY7uklP49!ec;`fMgOSAs{pYJ$f6E=N04GD?rFVO>SzeNyshEGBjwb;s-%FN(Q=r z#H=u_E{uf0%&+Nt7H*Wd98pR{$Ar9LL$pLeY(vRgAM{_xn$K^yemkk;ul8c?`LCF_ zUUs&$)IUm+Lj^A_u{8xu3F{*$rS9Cr?CkoDkEuhluCeXdZ}J^EB zu&fiCtoOE50+PDQdX_i)KZ4`HPuV!~$h^<9c9TLuTg#s(Z(|j=xaRwRifF&Y9ruUsW@sc914 z z7UlY4(mV}}qO6gp?Nx8??3@})6TQU*JITaKi#1gSozbGuufz!R2UsdT$tPk8uba&F zw9(*#hj+S68mX|q&)RBzQmN1Mi9`PY!zO9j4JVlvl73{4zT+_mmXyi z-j2&%n~k(tuXm)j)zliws#HHyAfH`n3RcvK0k4#LY*xWj9Gsphu-WY z)BO=3vS~uQEb+w=QMJ=0PX-%Hi603GNG27Q&GjXY@=ETj7a?P{83o>=u+2qh;ZIp4 z8A>be?W(;C4SX2QRC2yMQnfVukA{K)Hp<`icA&r6vixb~_CtP|ck1C7Z;oVUSl7Ty zq$ks&e^_WoTfjuY^z`%B`XUEFoZO%&>s)?Jf?JS1AR}$6QI{MNn;c|Us*sR8K_Cy( zVi}sojY^TXu6Ej?+`z5$;&0q3X~KH%d8Eo;=O{&d*ATSo;E#mdIB3$Emns}4-WU0X zVQQiY(3dNzks;UjmLPdAt*NVmo*3@9H2aiWe9d@5)Y`slldOp-GFnky!=!8C8&Qhs zGS-dV!CS?Nzj++2KIIbK`Pa9*c{{WEdbJLsoesMr)Av&q689COc&o^}S0?Yueab7p zcSXU}i&&v{;doMKx6-9kY5U$Ois}2()D02vb|y*t=slrCm%i??&>T&J2wUm!cB$l~ zlm2P~zg$zhy+uA2tsSqI(tdXZ4&M8$&74g`loo0VHXBOTStxO21f;91#Aolj+GYas zC_UgFW2$Huq0taX7FN0r5{%{Uru9=nS(d$?N83wrT0vq(q0B zlERce!xoWUTOZ68Qun>`0BIQta^s%Uc%O;t*|^y-#a3Tvj^=MSHNRU@(#J(4zoJjL z2szV6Tkw^Vzsz3U=_OUAq|dVpP(h@)RIsL{w1uqn(@%5;`zW8d3cB3R-H8d`ZSF^x zEZ-F-Pgcqk(LSStUu?`*$%D3j;45oN0ltuKBgR@Y(%RY4=4}UyM}Az3}l>%%CdjgcV9@oKal)VQ|r+ z)>!`lu=XxL5|tR|4kXX5mH72##**nldtu6Jjjiy98GVX|%T6}p91>Dgl@zERpv4s3 znR{&D;1;$&Ht~1ymREK1$BOg4fBU?h+}H3G+K#g^`d{~Dc&xs5_jF-EtBmp**VR*V zvV3av%`j5S?I0ADH8ms^HtLnDmmJ)S8p&wM27voGdzB2m)p2guT*Bh=-<%z4HVz+f zKDru`<&wW#zH;Qg=egycd2^2LwoVVFw5K28{E6nhTW!Lbv?{Kup*0MZHWu@)Xp%?5 zfhp@7XmL!(W6VI*@hi=-IjJ&PUb}$+>^(n!f~NlfOzV=K;QQ-L5DmK^xgq9%c_nUo zf`4I%`)T!q?OKlci~i($2kcr&O;-DgAv!2@lxPW9=I*jKg7;I*KUQ0f#ndVLX8_~E zf`#qCw%t*rCwc(rqHC6tdi*-ak+wr|nLZ33wR`k&3p@xL7n=QxTGpo2z1>fUnG-Xd z^p8VcWz4@7G_46T@Ef)L5!|77FN(8>a62msCCjQAK2^ zQ4{KI=|}wt^KRmB>zXfW?VERJ6QLttK^M``Dd*F5ZBbP798;Jv`>j|I<3NcC# zs+AAj{ob>OGyoFPs;~0?Me`h!k&(wO*cM<#q zcoe%yt#gt(Oi$e?xrgRd^ACj{%#QqGxiZxu8h@rCxwBhxrNU`2Qb!V!tS;CI{5$pk z08GZev_JSwSuXA$_l{CpWiUaUqe)em>7$IOl9dm%LUB=mjZ`TmvXjt{h=}4Uii<*@ zn884$rcu;_BF58XCBW3HK9{#rQYv}|)A|wwYAw&gu@ijdBent;IE$!M+@TY5_mrPx2=~=V9BDb^l^IS_h@2`BBqBwrb(n!lnk1cDPFzR= zUeoS~4#?dG)*^i`MS%Nhwh_wo}z4&I#!?3hTnX{IRpH;vsq=C)Jhv z^2B?8L_^SB^<{baVdxCK@a~#ls$Tw9uLT3{j_DQORJ|6rWHzQ}&!l%rH|m1#G3keQMDV+idE_K=FKgZ4umvgA!*PF@f>P6!7R z``>(Wk%f5Eb5d0;CkUQgh-GxCEjClsq)b59jreGd#ZB=fODQIlpO@6j=g7 zQuo6c+9_O7uo8SV5b%d7PJzc6eFe3Y5z<76v9*=MWF5m=TdI@XuDGg!w`ndl3^ zQ0l<_1}-F?Ld^=xh@%ovfRPcKW%R3183j^`G)&x4{bnl;R(74$sl{8ke4!T2GFyf7 z3T0Osryi(9SOsMMB<+Uf@1%XDPl{Y7J&|7?QKb|-+D6cN`y(vDUDnmeKm_#~_{tGN zhh?B$Z9lxDpotiDO?#*l*31KUR$EZ~3JIT-#9G=Pj*5cx);gng03xoT3DgChe%LYJ zG8MllK`AoSYYG`upszNQEhi02%!4RNo{~Orlur&T)QzAB3(6bS}LtRuEc(u=Ge zHIxh#tt)lA3pR=@Bsp?I1k<4)YtlV?wLi2M&mLnunaaL*L5A0D3k>a7S;{vA1G@76 z0Esa!7)~2-Vedt@i2#!=o=~RItA*RaS+UR9eVZP*RXarLT2NHN0hGZL@r*`R5gPT5 z*tv9#p$e=E*npJbp)+M-@#jnMx{5{{Z1A&r1y!o+k$; z?NedURrpj4!HV~VX9MVHq<%il7e!FrrsUuaIUvibBuLNQ7dTw6E4f^=-NA(qZ#xx; zP(@Qg#A`iMtZqrJtIvodblI(^JC!tTpNJBHPu-Xi$CCBvdO&6BvvC0GV!V&03c+lqJ`a+162IjR1vTlXoam zC1sUOQ~JqIx>2Dv)D>BnFY5Xxuf&CFBAEQj*%)Qtid{UQwmzyiHmLFyyFflKD3J{D zL}4v2sY#jeluOZah{(IxuMkANki8O{&sE@#&N?I%g z-TMf26rqB4<&)$0My?miHyCk)@UjrTzDG#B$9D0J)jSqwFWw?%P;pmZ9kZ>fL^9$Q z!hs;ns2|fB_!&j#^2)~=kfaN4FQ;1}9UBO4tp{P>rH#GNAT*4`8v%0F5z#urHO33!BNDSMuyCQn; z99OEU=IidY*+p9SkBoZGjef{~NxleSE6(ea#~h7tK>3^1TfWl!$Mu+&DBIwj%zRMv zyaSn6sS90F+}-=?TF>h-Ij4f)#Pgu-hmeM>ItqH~sle`%g-b-@XSYsQZ2>J(hj!?a z6k)u?M+6yvAfZxzeuAo$rc(=L#QXY)b7**|9_MjRDjV~GGFys7x`hK1bSjiepBC(m zNeL-ey;Gb!=~A-xheZ3fA5D1=&|R2Ky7njX^?(HLQ0YH>de3iVfE(z%Q@L<8B#qTYtIYW) zhTW>O)-sFcFIl-2)>@=|#SKd?4Xa5)rX-}97}viOZ+G!GM=v=!JBuP(@yyjmd4sf` z9OdP^kZ_IZt}@K3!Cg-u+?^uNQ1?=rT7_!CB#jN}DydY_^y*CYo`ge)PHdp4?zt+rS5j55Hq=qP+su0w(!+^5dqgD@%#_(G{{Zc| za~Cw{xjn;;IJ)6+wN^f>g~H&Jvqfp4O=i#0HS5#06yk6*IiVw#UuDB zw-4>w2YC)A0>bhq)i z(~7l@)y-LVey?36&aR57vVw&wa|9p6eBg&lI48!Mbk+4$H);lL^EHn?flh?}E#W|d zJ>6&F14|;EgruHXsA}`bnN`Nn@2f*n`+FWp_*Ar0w%dwKyb98z12B`Sw1E67D|w5$ zNmFZ4+H_RU?Sz^1Q!7WocP>rI`0u%kbye!R>xX?qAH+3Q`@sxRgx#Kuk+rJRrd5lutBm2^pIPdvRH&qr0uu z36y}e?ml`#^kgEu5bev`2lDA(Y6`Og<#eBd;^7`eVLz-u^1&h5Sw)hDnmKK%0)fl zxyDT?cZ_Pkq&C z2%N!6PyHBE70-&2A!K+aeRKTY&h*#a+aJ~i3H^csmDBMm#whJL1j??cLO& z7)x5WPNhr2dxQk?2};=L)i*niBwH0W!T59_K`m+(`=S{3INC5uy5C48+N*-n7cEFc zhrSve@hQONYH`@?rGn0q@dLyNDbJz=$EtW_Hqr8J2?A2or7oUv9CfM=SZY>ycL8B~ zr#7RlrmArZHuf7^VQLA{#h!rsV?k*ZTA0}bN`XHsxVKQ-e=xOcB^~S3eelr7Bl@jq z*-Mz#I=2B+?x{*jei^9B31bZvE!5r%+x{wolB6j~QR*i-!pQ|wZSIInoH*V&K~->Rit{$qp}3PWe*Td7HF6g2n^IeO;d$nQHlM>SVUfgYjJdf4C2EUuQ}Cg%loR%td$v`<#;e9`=vKa-iucL} zsYZzf6NZ-APSk*CsY=pm9U&PKW9ClFO}GaV5XVy~bum0+L==xx)sov#;|+zj7Lqoq zVpAO@q*+GF?yKE6Rs$PjGQQaG%sRHPy@=or);)+@N*h3D-Gi6 z2U<64n$jh_rAm_&=_GG`$s}|WRDqMWJ38|*H5fh)CxAiskC>JiICV9D05>b+W4h0% ze=PS6$SBLXPUtQSTHSScmIpxM(_ykSzKCg}X5-S)Pq+%zthS`>A76I2XjtK)<%zX_ z7ah;wzAWyp<;iEJ=bhAV291Z}eSYdyQT|2e$MTzfML1zp%Y71*$(i;={L+#N&s$)vr&ROuT!X{qysfymsgiF0&KE>_ALt-3JdPX$U|szK;Mh;GlskHc9O zN2-kOx1=hFqt)IOA-7!wLM1B0=`~tW*as)^b&k_|wPyL9E-jED^ouI$)*4FNRAEy#6HAi#~BT+xXhX|M16w_hs z=06-`+^B8x`8!{dM|;%aXzvrc{833wMo#7OnFH|EQ#AaZIE*)aRG&k0HE=7q{F>CB1%=%o8v3R76zIC;F9hJ!>!C-d%Rd{=os>rH*+& z3D_xKfAVbRp0ouk(b)P7$A+mf>~Z%S-Sak!!sqO0+wwZ9GN{6`J5IDW#jC_A4k|<4 zBL_1m!=(@scC*{X5vj=e62Oq9Ug}WOf4rhpojvgWc*@~{eRM^sdy~tp&laj~A@J8w zKnF;1bbbiG@o5gVMK64}gy(gsxMvoss@nD&4O2f)RHnAH2_Q?SQwq!1MkvjelO2TV zHiaZNOT+hpKbgAQl79*H8FUZzlL+46xC-0!mX-OAG2)u@wB~n8bpG;^%{rtGz9JNN zJpKvo?baPtWbaDn$9=N!75@O3EZGHaZF{xWTWIZ=i>x(Bht+VaO)*-ZM(v@nt;R_+ zlt*{o62%d9Hd@}y>>-i8Y#iLR1lQOqu(Iv-Fwazl?>5)?U#}EA>5{8%s5l{rs}?>oU*a}3<|{Gy~?Ha`=BxAo!f?u3e5Q@jvL7D zaQspgZ`5;~-z3xHmKq;t%T&An06F0RTzQp#b^s!w@@Ev?+~S&5-%vi{>PnEy_KGmB zr55A3dy1Vf8@$5Elekuqc}0a9o^N>k6IQ3a{^hC++05IaGSv)GQ>1^&3V|QrITvw9 zL*<4UpOl}s_$JMFcRk5gp-FR-t63s{6G&mRueB}3ZO3_J59U@dd4Gm!uTH;dQGQL% zyw8xJqWQ`9s-$YQq*uPmW(Qy36h?CUxvyl=&nrz~+W6>=SrdKL{k2ctVBv0E;tPKn zT{$YYvC+!7?Y}0oA=;|fl}$@>3UQ_?g+z;W8xj^!d5g+bln#{)DW}&@WJnw0qw6c5^y}ac{kabI`*GsBiXWcF zs`6WX-#;b4QKs?Cexdv08y3O z>AS(VP_l7GOXT&8hv6zOzI`e&D%3CXOs1-uV@=6vazAXK z_Ag|CBA(q zdFqra@;k+v;0Hvy>+F?l{%WG?A2Y29)n;z|kh~u%hHt-3?(eL-*H42}UQ(IuRUOHN z6?=oeoLfcaCOOxcY%*MuUmz$$J71x5YyxDWyM8>zjtHg{NZKp z{JG0R{{Sqeg

UMAp8+mEsP{Ew$_A?v>=bu2RAEaCeH;^PuqD2_l@4Qxp{T7;z%IHeoF^t$Xxw23rE`h-IZ=aAks}wrB}-D1btfh zPY~gJo3zg0v7_97XxR-D^cH+hwM|lAvBD4dN~=wHN=kh*@dn)dB+V;G0Vlz)d!j

G4i1S>2t}DmBq5U;C5_sob)#WXA^;&+2O*ewAjs=JrX`KTieuSSaa&YOx@i;CApf<8Vf>v$`vwY0OK1?5qV>vDb%2(t6sha zEtY+Iy+cs42=J70>Ml!)C-E7dd~#YLJ!eOW_MKt8fh7})0!iAN*9bS3UxA_7?c|k=$b@72z!*rbC@K&?y4R8qPTR@@CL=!6WBTv;BKI3l%yY1(d&m32j z($v$)9d}FYiTi$Ef{GsDkKzhJapAvrL*W`XKC3L~flAcww^-P1X`6{1eE!Iu-Qa{1 z*jiIo^U?PKt&C6mkj$-CZMf4S6fZP^GCOkd2czvW3cJoxkXj(7;ndDy^WB zsKpg&Y^5%E$Q8uVDCA{Q=va4D_40{O&N?8R01dPkRW%1@lU6WLP9Mx@`pdc zE<$q=S&;c^tTCV53aR9vdh7AmNb;P%>(}vz;R!b%M*8NH=3s1ND&;mn{{T%*>a3`t zZ626AkOZaf_C~TN7OZbgt~#R0$}3W$PU3tz#NSj`8zvqo&+}EsuGM~+f7BG;p=XK_ zIZef12?_(*4{h6%euj!ku9U7YL*kTM{$qPXO7oMJfGE%I5Wm$? zlM8sI+jWC3tWpDZilqgfzLLs%`=E$mk;arm$#V-v>c?PQQOb8!ux+^ANg4p9Cu()m z_oW&;OmwwbT$XCw1$c0t*ZFGT(+jTj?d=dfKuIP?{MgoG?IxzH50TE>w`FHBPRy>a zxlZNA>g_|58<5%v{+PFjhMiVk=Q3ULime3TzRL1eZZ`VNZwsxu)V8FS>ekZs?gzxE zNC=(zOIX3?a-y2YBXsJqrEP8t+aGc_yA?f>@qDJl?Cvxgb%INddS+S>+`K)ple+IK zI9*nXtBW5m@^>PRbpn_%pQkn!hZ}n<$lbj@ukTROQqoEH>SH~5=0uJ(V4~UMyqb&v zWOYig_}5|_8*6O^%zag<68PS6W@3M2XRiD5?Zb7n$N5KK-NKyg9kuXvj5eA~3W3d% zm#ENlU-Y7aUh2Ab?pmlj0>V9-6tQT)a63o|w&HXYn?LU9niBs2lNGXOZe976)IGUo z&6pI0Z?xOeQW>LeoFkx1X-NM7k{}5(6FYKMv^cAF@B{2i3p7-sPc62A@KH*CTo27F zyj0}C!KD#3)ln@2(s(H+_flpfn+wK-B^R;s=uuNoM%fC1o7^ux_Yh-VN@QF@Dx zH)uh2s;!E2pnOVENRS@*nh2zM&?ZT93}fOdHNAHQ#@OjoO1XUFsR!U;z1xqJ#Ihvwt#}_f71(GKup+l5;IU4pvax>6=g=0D68CoZTE z5Z=BsT%sF{OfiksjlOrfF`%xm6h7%uw} zmmSS-3u^ZOtFBevk8N10*H3cgDogEqWQ9;#lDhTGcut%tKc+9OdoLj_%%JtGmG7Gv z$c-yg>QQyiziue4q^UT4wc5L~0W^%=3h3E!UI3{fQk-5v<+SzwtEfFng6qx*+~4U9 zeQK{{{r&emhadi2fdWrJj)QL^XrzKiwWDQq$LGXmXlX z)i>!YZRl@s2T&JITT)>z{ISMLC@Ko?9rbqC+>IS2Ctm zgv?;+(%!rKK}sAt9y~nYas;is-pal z+9!4xGnEfrE*Gqt(#b#h-Ja!BQiULX@nk4EojeLmyyMX^J?V+Mqff$pYs)izhnlmi zwQ%(+=B_-iedj5!SqHqLEXLKmD~U0pEI{J}Xa{4tL@ z;8WC&*C@;T+Lzqa{{Wt|1l67i&|SFkDpjH4OnT5;Q<&{DDO9#C#uB;dKB@)+ebZ}7 zBZ-%O31M~`8bU=aJ7_8BTR=vh$x0aCi-Lkn_cLOPs@mHB0KF=of!s&xF#iCjVc>@u zNk3+{Yr#35RtN;JiW_Jd5^s+F;jP^8(O^8r8;~+H2(mH`fxGB zTA#|SncO!&%zf!)oSP(IjhD*Fe^=(8Kdn&+?fY*4jc0dV(?=Aj-glEzfh)CAzxF%7 zw8V0D^c(^+{JQUnN72dIOJiud)F~&Z^%eU}L;gP!mA&6T6k)zj$&bbJk2sOzy>SDu zaPUO8y5&3nK}55!7wf(((`JbxB3`K%+YuXBa5$S>c zq-JFKmg2f8vmUp%Ta;L7B_x`*O2=Igps(2upJdiMRMXF^D64PRikIpZggsK}PSoxs z1(EupVWY$agz_(V!xRIHQ)n^9QHliNa#rgAbK zF(V%1#X~t1Ov{697Zyc2MCU(*L2<}cQQbFIhRHK&AP)&ALH5M_#WzkVDc5P63p-Mj zw?B-8%TIhguJJ-kDBz@iHsQ`p=4$4x{BOtC47qMAYOWOYicJGk(r(qd62b?)aY>f$ zOFKsVZWUJ;&fmPeAV$NlxnKNG>9dDmG*tPAcO0u~!>p;2&J}BYE>+Yh9p0Miof9+h zODz-RsK8x!<<;To*~U+;ZlmlgJ0^86Bmj5?JKFj0j5w}>rvdEWjO} zzNP;F4^Sa$4!ods1@+2*yAWIUHPzd?mySNfJ;#`~wj-rt!A})iIX|)fALqJ;IS-oh z9IukO0b7*3ioYPO38yORnrc>*nsyPaf?7gEZt=Y=dG1+~-@mIX$#=BS zdj)6}RLj1e+hQ`e!q6H z%~szMeM?f)MkLB}CU;7rYF0(rEb_-+ydtxi8ew zxu>yvhv|UiwvWL`$3DL?L|NuO^spz8`l>E2w@L=p(^dT2Y92oba%ZQMR@`FZaitaI zzE0vJqso@Cf2xN*;ZCzhm)g-*h9n$KWjfkf9`h)px$P*Ouv)xtd|k!Hpe+zg zK7}r6f~JktkMR#M_)=znsueHS6n9;fBXw%nT6v(-K#(hwhxcvU4s^C1q06gry4Q!n zPN!W))8{m)L-fXW=M{Ap?W)+>cTJq+QPirkxb9PAx;EWLCC$!Mwx9}GanR(auD=xi z_@WCz=j1DNaWL)z^cloy= zBl!6BGszu(KfkDe`YirpiL1{5k}I$FO4O+roGGUP{7Mb9y?*GOt$*cm5$tU~CZ?m9 z=M&r(rJF03XI1;nE#@SD_8F8++2$Nn46uOkqI6?e_CI7)50 zN>Z1}r*r#BK0X>iHyG!Dts=_APN{XOn?+y?r**dh=^_li$P4M^Zs@kp3-(3oXy_hE zLyWexdqPN&`ys;OM(j$F_HhxY1x2}k4{^3!(%Qe`cI&%t0u&#lWqp0SXBHNj+qnK$ z6-#;BR#K0Np;FFD`f7INn>SGXy2$n;adKt4t83+?B}e}NMxdmhx;B{KF|T3yjY7iY z{HU;>P$*V<@^{ihKeZQX_{Tk^zc!hctyeb9GtxZ=s?^gz^kaRD^IjuCAfod;$CDGY zSdCU^^3MkH&j;70*~qw^O^Hq`Z9Q=wr7QA3_HVZ|E8RK!Bhhhv#cgyd!n~s~+ZT3j zq>b4@7YgNc3tTFqGk&E7^`3x}yV`=RZ-kGaFOd;#pKV#||ac6wKE zjG?3@RTPyDvXEONLbhDcIEnj4qH*o;;zjMa99PXJ@eX&(Mf~v|U5Q&v5S&a{bR=NDhkOBoj5&EHy*-F)E zdr0R8cta%3E-m7 zH?@XV(Ty(*4J7JmTGQ{-WqDEcAG9>#NW&^agU!T}eX;3Md6YKgE5_O6%)z=iCSP*B z)AEj0+l_}Mw9~bUVw|aRy4lrC<4Ah0vWDEyL+eR&5QfW&*|-V$7|3^ba>;XRP$^Qi z{$F{06{bzil=C^Q2Sdcu@mUPgf`?fj2=aEco-63Lwuw!FsqynLR45rznp9*0XCVZ% zlo4tX1tviC@i5RzV}V{8mzk5SD{HL~rjL@+W)7Z2M9rDNf|NqnXzhynJhf(YneQH8 zd5s#kH<4N>DinM}ieM#R`$B<_V6>N>0Pj-yJ1b!8d)LNBpp=N~^E*e5vxjWEkrOYO z5EU;tPGFa&S0Zn~8ico6i zhRoF964pe^LD$_8Njq9ARU0kIGyx^0~T`uXmUe1)(3NP^$4k(=jo1Wjc zDQ>fouuy7NuNm4m7d$B5B&c59UEGgyk2bHGUkLGKS@@~}2KiS`Pwpq9a~>z|FaUMM z(W&@awEqA{jmHfKRzd#&xA8xB+OG!;aQ^@o@QnhIiQ9f+^HwKv$|}gIxm76YO5Sp0 z1LT3V4OWK+}i z!2IPmkS84;Rs@Qi_>WJ2*%HR`pmQjje8tLb;Jh!&`J1&Kg7M1*(y5mL*VIiS(ur%- zsEI{>X6L7FPpMsY)r+RRV?f-6kl#w0H>r4!k?yq=}SYVisQ-t(Vo!}%LK+s4ZAss|jYK;8Ez?V-5kE)P1h`@LW6E%5j5t37=y(%ZiNJ^E< zz&ewtJtLPO+Wcew9x=<6c;&B;D5EF;0DTY-*tJGyEI+8U*sUT_OPVC>6@=)nTe?%n zk?LKW@!G@W{{X2ExB1j}MvN)>X$RETsg!DM4y!#n8#Vs`;0;VBYx^ZT6aCPhTtWx( zizmm`a$kIFJ5&DvhSi_$&;D8a6tI9&nI5sBSoCF+s2*RoI4(oHKxtEnpR#jx(L6C? z6kCzhC~5Y@_M0H?wpWpgUDPxr2~C#g{-}+7N8t+(FU-9(;byp{PNyc2!d> z4vQbI8tSPdTM^mqV(oeM1yCI<65vUAD4?V*2T-KQh?X)Ahh>q^MB?9o(5<{rB;_=Z zH~DWMRkWnc04p7z-WLY_9kaJ!USm75EI$m6Pdj%z^DPKUs5wI7(MRD1rAr@jrwV&^ z!NcSV0o+;0kHaIBp=0jPxjSys&mbF$1ilJ}S$B8av_(zNVq+dYN-KLGGCPJ7UZs7i zQ?vKq+$|2&)=*n-2u_5lwKk;z{$iyW>HM&p!^_RIh6Kkn#JWv|lj0t)=B?p|!V@_7oVU zpUPX2^0+NkxBDS$w6Tz+`#*jCzTFa!naLGsaa~hYPWy5`Q;`u%KbE-=bQN0r4?l3v z9}}phTQ_q&6MozB9Fc3>zKC5)8$WW$iffL_$j7@1(pmYtgr8{wlWp176>~^cvnQdh z3mO)xSy}%8lBlDf4|Kz;Pq}JrySE=Ut&ktJIhm}Ze18J14o{}l4QKYiE`mnx3c8Oe z1O8V8IB1p35JWV;l@RdP9oJtjqxeEkmXO}MU!@DBuoN{&C&xfUJE}(%$5=4#0!gmN ztv4W1akSFcAc+*aPRpbIT1*hzsX)=heW(erHm^Su0s9nZe(lSGa-=wnlZkQL&~kf) zMV^IopR~0&E-dxaG!817W)<^Wu?1v@}uD7?l6$9^nDvNtAP&hU4RU*+S zF`egp-8qR_Ti+w_6KY~;KER+Hhv>iAjnTzDX`smQS5-2TLw~EMOpr5`DXMW6nVO5;D{{UR= z8h@nX6nAm@J#(e2aH-wEE(U?ADxRkasImBH&|vqpK6b; zKTf}?4lLnTDa-iwM%!j=qvxwAlt;Pd)*#mRwmrwiNC$qCo+xLPo4dy^L-r6{9+WA|mSuQh4w{sA^#?oaA@#->?+0^|jn zfA1Ydo$AKO9y_T0Sdc#%CV-(1SGuirEGC|u7qp*OKHzZ`rM%mS>ZlgklZdUg+-O9{ zU0*^qj++Z*Cd4N*&7AZfJV)&Z@KRNe>x{!{N9P<~mli+0Emaf|?hYnV8SKcU9}AU? z9(`Ri`;n>V82a4s?OTn0b;nnlB&uGctty%DpEDq!U<;2foPJj;n_u=KAbvQA3*(dh z;;v}HJE-rQ7v{b@q^V~fbB*j)D~HZB7RjgaT_L% zQtchT)LM6SBn`5It>6`@K-|u0K(u?gUMk1=bFuyh?iY%Zs)X=X>>s#%bbM77i<9e3 zO}gN;fxG4F5}*SA01n~@S<3ggY?&>qUd|h@JnYN+i!8LTPADY!ntXjJ9=+^N zM5K<9=T+5vgGh1>OJ<}3xishXybLt4r6}fR+aju_aVsQzN%lh>7*n=zQ1p*EOr)(K zrAadp6A5Y4MbkB|6i&K(yN?v5{{W6+M3eSKtalE6>WXVX-ISHbG!*q&Jq;@W96;m0 zj|P!)+`?3PZ*WOJd60{nNI+CsOOhGvQmxAkuA+U`9bkujk(Rds9lQT`^G`0!HKKqI=XiPkaZmlEKf{$ z4hiW;RZU+?T6Hp6(2XFtr5sgj!5b!h`x`=#wWLi7=z8lATv>;Ts|zdF2PRHEkM6#- zmlL@OAdNk-Q({{ZxKR;AiMl%r_v)hPqZNk^f$QLD}vd7e{tsj&J90z`B^ za4XwTh>i3qwD#s4DNr*W5C*L%vGFAeYOqiN6Z7*BTy6nDBnNPey-bRPbkKOipBPMR zj%sOE&?%=0NSb>1z|I991;9J6De6|qlC>TpBIhb+#tK&svhqq&xg8~>PKTo5ZT|p} zz{DwHDW#LPO$zmtfr_k`%C2#rI-WfjNPsdR>*X95r4=kiU_KMKMxw zP88q@a3F#;oP=l_jD)Q0nVG@jl@AtSBk=5$wCI0SJM_3zXEQ&oB|XJ>uH)$hsYCZ< znBj_v$~R^%2NZULhAj1D2I)hANBXiIB2kE8It2uo=~^`@=WXrE-CD4gC_Uq8GKq!( zhg37Wnou{@NmhQ=XbDfRM3f;tM%{Jyh?YD9LJm&ml`6-_sEN)0Nkel5b7O%QEuTAKJI5Ir%h zkGczHE;!Xxndx3gs<>QHz&RFgRZDNfu;;4^$lFwwPo#l4YN<#=X1jtBsR}4!y0~>4 zRjT|!^w+&sA*!t6wHBsTw3=oUxl`&vCrHL*yT&-aRjkGK6!IFqXvZCPYs1(OVdUe; zx>mC(zfmU%Bgat~%g*bXPAk!Iy@?E4g=lRU&9c;#vuU(5MA8O{Z3#w__6VYzDEDqw z%GVPVV?Yzin#k+xN~#|8Q~TZ;OU^6ZJyoME$AVsAHfwf;UohbE__{(h8U*>RaHrBCQ}6Tfq>Ob$``4~Ep4ApnMS3=d1wjun3yuV zs9h*-${yt_Upi+LYPNCjAyDzn8o>_Mw??X})ld4WaD=Dc1YFopZ4u^p=2_gujpfUE zV|OZ~_fE?@hPqZNuD`eoh2iCysrR3&N_E#nCCPq>(?{JFTo@J#PQu6^`K3AR+#TGm z0E8l+lfN^hZ}Ejb@t977`^muvFKH>&sA&6VA#8Mzl#8{B1(wma9A?zT zYn}y760g%Z=H();`Y3KYer>?jC|mKqXJJWw7dv({YEG^#=20}K#K?HW=H>@%DuMbL z;Wd2#PWo)+x+zdmU23yC;TuS#XLDP+d?Bg$#8O%?4rk~TXD;LL#i>d+N(;pune4-s zIRUON!&pxDer?v;bwWgtH>gPaH9p7&Im6${13BEJo)afHKm78sW}b*BbR*RTO8)>w0yh}$!N^=|FTgwk^IXrGxXVM!Z&$fdo<#bZ zcQMG`r5-nTrQ`cTr~VezNRLz`=mC15EnjdExUll#!Vihx5%sS#$@!;|xio3z9;ICy zxbjaX);m{{Wc9cDvY=ji;acm71x8=?s@grmG)Bta+DwkVJtSLK6R@vyJl*|zgieUYb64|;f(}I%qG=#cjMw)o(5*C8FlK`By>-x|@ zL(2B*MB+EXJQJHTeA;eCq11YdD@L7v%LB^71yPVWr*&)aj6)r{+FGlxw6gyIs0Q%k zXjJ~y2+CjH$kU-)Y;BYeZlbDw!!fKX%%>fqvr|$&-~g#CqH^iw6tmsT?kh`Wjkam8 zC<;iWsiB~CH5MBBjDENu@97*nlgQ0X4UHf8!Ep7*n@W%)i8d1>Dlap?a5h$UObXTto9lHViovk9tnNpqDK9T zA9;-iH@)&!`cBSa>R4>Imw4H=wvvazHY<3yn6O4xDr|I_rJ)@hrzv`9^V+E>AF?!% zcU6t%l`MCyVXnJpDP%SeYKv}bz}=`@H|8qvi;4dIFE&FP`hM`7z0stKDY*UVrMiSAL?2>^hr29e(ngS?`WWODjo_xG&j8A8JB?M6 zrb+6s+LWKYGZ0L9I3KY?yB%=e6-`Ge-fe12jVZF8sUljz%`H199n9U(0Z2lI@*P{uf_{{WP>cW)D(+|*LGdVLTBF(wj}xzP*YkSeKOyi&w-`UBy!So* ztxs5>I-a z*Bh7M2dB60h0zX?%>so&R1Eg%tWGvk766W}nWT@2A!_maqAo(BaRDM#uAP&qOJ-nd zWjXud&5$>8qjQPbJ+RK+`OlUfPx2oa-0qZ)5EXWLpOK;oJrA#3T0tM?lNA{b>an)f z&ea)f+W8xk@HT_itwule)W~hDBJIyS!C~V2I!5}68b?KScFA-LsPtL~+!yx7R&Oxn znDW}jTvoE*%jlsT*OG?&6oYKVy8+;55pl3#_YQu{sE+0?cXj&ILBTykPX?)g9>!E* zzolpL2l}q*wD>4)Gu^w5{?mGo9w|4FJKp7+{ke8bC5kR9uz|6fTT=-6gtyW`<)%sh z06{7D#)lc@rJPRYn3)f)WAff>Ufeg95F>A?Qe1~7y5DQuugI^JSK9S|c~@BbwJz(g z;&nOl^wWk9H^a_C^#;8oWb1gIk>xHhfMDfFcH z%#Yg^3z<8o$w{{VD6u}U3PA75@( zTJ$Xy6b#h`i6}VJZl_=K!G`jL=%jO01CVj14|8$XsjVruKIF)Ll|`8o{gj!0h~m(T zWTft*TdpmbVH@r^{q6dS3Sg%c)X8xv8u&$3&9h3mv~fEcDQ}AVoy+Yl(mvJ6*|Jl; zDJ3)8(qJ88mireQ+p^MF<}NLWHPL+&c7w|nzFWnyr>=gDYVIoZs+w?W+o||uqIH~k z26oBT?lfNc+Sx{2#yOSnt>fN5cCxvblx=36ASL!H9c+UwNc5B=J}+E7Yh5LjP6vXl zxpk`jNm{YS9I-w+XZX?n+sJ(XCjg`A9c^xr~&u=DRF;$%?g zIW{qomP~@_-&!giNI%s`oX?buM|X?2cC8deV>8!Ld@nr8*W!8ACTVMK;ZH)kM5!?FZp)zQ&OE$!{c%Z@DuQY8FLvlcu?G0%m=`ARV$D?#mabR zCq^^Lcmu7W)bFLcFz3SiQ;{g#HmY7SI!4bK*^pl|nkQ@Eyvr}(VohOx_@)Q0tQh?(^@AG_xgq3S|OcILMg z@v2Gx0EFUDMHe~MOs-0`aqdln-|bwDY@@j1^)$~5lNCrpkQ*pJ7%TiGJwk^_xxroB zHsPU!(PulJ#?|(uyan+w%9##5ZOpT5e-Nwr`wbO+eI<(EOv|h``q0wPoYO6QW9tlc z$qiw4>*Gwls0HDhYN)S!eem2@e#ZD?2B}pgkBhC8cKnsqO+Pht45Ye{kt4OCA@84* zdu7j<5m~_4vhuvnLCgCj1w)_WuB8oITqlgXJ1Hc!fb~+fy#71f35<9szTsm(mvR z=50}3CqEd6sHb12*H5+>WlKa=KpBq%5XezYSF7on$@u>O8h2%l+Oq{R-Yv^?6}X+o zs-UDW#ZARcy+pSIfW+prg69KBOxH_c(LQII5 z5`s{KRSbX%wA1@B$~SDeS&~AfpwfQm=Nbg&Hl=J2Wx3N?7XzmrsHopxgIsE;(v$d; zO;Ubsk?sLx4|IATZO$D9E3)w4GI4z?@bENvujBWt4DCP)6I8sF7cYHQDUlL3MBT1J zC;U)6=0qc&0+qJTJQ7>^4Z4*IRYiMEB|S>kg#NP;I^}?FiDi_oGyyX%+}Fq0@;6*^ zYGmx3+ik$&=k7$zR?C%%yixIBu4LFK!n?Yv8-uE%z_DuVrn69&%VnBvbRQ8Dio>3J zv|y^ZXKC{5DFHRA>G|K1$!jObn!&N>h~ICwl4?|g;8&!ecU#XsuQUq48!J)pH<`o7 zWhn8_4RQ7W`fe;xerriwgEe7F6YI19-7K=El~V{IBBkj zLA$TE7-UT`1iwpk_`*`*+BmQh>`$+R! z*V?pxVANgEcn{dL0KXz+qwb9rRwt$x3#nJ8lod{M>#U%1`YGETRaxd<*Lbg&ZfVJ` zcIPqN&9NbwTAp%-y#nn4~SY^Z*g2(vy^Jz1E}{ES)BTP zVb?mMGEP*<@*8Zy7W%64;$#fSB|x2Gul#B%>f(OW_^ZL*QvvX`tv(^4TrMr*UOwUb zGHm!RJjI=dCG#~&8>(L*OLMNOYEk!?y0^53A@Jy^Z*S+gcQl5jNo8s?)==G)C?zk? zQyfa<1FmTxc9=xhGN`MIOvybDNJlsYEJYD-WAb;)i1+EFk4$f(U_zqqwHukDo-IzAPjd{wzywJhQZ z3c9}HzY7$#3Lf&1f%@WmAy=rcKenEh>7m}biEWJ)v-Mf&wqLUaf8ukggr z`BS6aJn*Lhb%81W025IL*>FN%>{a(|zwmTQJv}4={-}*yTQ5GCP@ojp8hH2^bEvtF zsxG8yuA&7}qAxcxc8$NbIGqz~s!DYd{e}ilL|s5S_;|y40&{moR@m^=c!*$UiU6BH zWWSgl5oM=okkvj0BXFXTuA=UQU!v8{+tP(x@kBmlPU3dBMuuet zB1R-=R2S46s0SkDK2NdaSElW`MR{SWx`ebnRVW0d)dBjTS4wS42eO71ERrs0n=`ZU~>DpD|8Z-Np|-W(LW zXZnm`RX&ip<1VBR#-s!&4?eK()MKp`e4W4QQhm3*o&&vY7YZ7hBR`C-nS`G|g!F>h zc^`@fUfp3DiYXS^&Yim7{Xde*S)z4k@VW~parc1*$9Ws{Dz zLPKg)iaiU4#ZTOYh>_9S$@4TI&4tuo+jEE7q^nkG%C@bl$N6&ON)i3iQ(WTZ_AN?* zChgpGyRahR&gJdL!z+}JZ0=*aOnkK6Cn>pJ+J6A4rl)>m$nQq6L3QQkDo@C?7G7uf z1SXwIsWQ*sFLN|RCb{cX=_vd~bbgYR?=2_~(HYtNq{q1FqSo2@?V68AYNShF)I*l& z%0=goXl@A--liXUv!{%dlN2{zQAAI`<*2s*0Aa7&^RlW;w0%jafk4POzP6bD`j~kR zeuV_ZMbDHnepf5oZ}vm^<5F#_>LtR}+Xcpg>10yB7MOjbJ| zaNXK{%R_J6b~AjBY5P)&UZq+1DiXhO2lYfI83y4TFJdP`J?K(|fj!!N@WQ6}O65)b zCw8I}oFwUvE8T@NB|C(7Q8Ymx5RtBU{y%s^nY-9%u5GLFH&IH|^GKi%#ZW>O1j7EQq9i6a-j4>D}H4OTQ%z?GuLd#Nc2w7%}IX~`iJ6a z9_@dE%Vj%0&kEqkY608B*Y@~*tK~BBkG7euDhX7+B*oOw)hrhjOjjwFPDna~(7?rn zD<(Rq1F)UGK2XOtsz6FdtEMXGHlG+4XpzuLk(AofWiPe^qB*6sLN=u&37(!1k-Vom zFjCbblC^D7+D}Najlow>RQj~-NJ7q}9ZXq8t5q!DGQ541^F@b@d7+#ql3Euo0q#kL z@E(7#Eh<}e7HjLml9sF_5htiB-EH>V8l^ks)(VE`uyUQ19%B~kmUG8mqblam(u8Vw z_z!M^iXQJjvuodM9P!G$6M1~p-gvg|{k6-c&=Ffp>9E3P4GzC)dimRMM zUfy=>i0#hol<6fa>foq7Wv-@)#Wta|wn$REJfkr@MWuRVH*I*T;kTV`Tah&Q&QZ*D zQ=J$oRh7{`cBwv^yy9R&4ql^DqgLb@DW%MKL2<;>vJLlVigsEVs$u5UO!BE}8B$c$ zK>SE?CTUQgh&sj84OG`|Ylgb7t&YsTm{z~2_iA`|aoj6-qom9ka;Cw}m;>(ZHms`A zwYq}I0cuNCO{uz;lD$eC1a*Q2UI^XF{TGsDyJGxKO}XZ0+53s1@fFb)&;EkF$>Zr? zrHnIiN=~jYvPlEkw8ux3w{hK4I@>88?Nvu9`a$lRbz8ew3yxW4wji zAWiPYhf#mZ7&7*WwK2~MiepL_m zC-i9Ue~oyfC(Z@Nmia@C(mPjiwza$0C~Qqzd^8aRed}#^@HcfUyAL6aB1uQWzF2N# zw{fj)e+t@KlC++ng%Ct%E@b$KUeSvcgdY%+TwljAtO-q3mr$S_RFu6uzf3jBTH>P~ zLH?y=xjd9pMd=^lI(%Ly-5M!|i!W~CUQ}|IfLRLA4xUgYRg0KFs^YDWTjEcK zpu|Qc%#hbq+;K}`aspE5U}0V=&`1ktl<960DM=(B(GxaAEUcpJ}`tK zqZ+2IT^gBLDc8tA&utZqa)OIfJftZ2bp%68NJI@%svn$z{71qO4&kao$hNA1$(>R) z5H0SlR<<87Dz>?nKV`0~3?MBI%_>3hk1WZ`1IqP_8)E3fY+EY6Zulyajf$vT$_Gi4 z0OOuIl?a>0QW`72i19xs-z{v`V|L}$rNpF$(=v7P5$iTq63HD}ymKj-wu$6DDctz~ z0Gsj6#_^5Uu-G5oY*9>y`LlyvVRX=|q03|6peeG|Mren_53x{%lPM6OXBFX>HKMV9 z(o&#>W4ObQG8^?xJNG3iX;}m%MO`NvJZ-zk_?9a(eHeGbul_5=KP<~2QhKY9GXp^} z8c5;f(Pbu_l`v58`}XDo{{Su!05THL$_1lE`&~lm8(=Hb(7^->CNOhq zn~TdY1QL<39)evVjcruzVG8y3MaGmI#eKh2<$Io^vZVr^h%BWlQuUdZz7XNE&Sd@< zg{s&mZdf&6BV)EiTN^ZC&|hI+5KvDF>qRv! zqQ_5fOG!uxZL}tG9>E?Jlgi5X>Q~b^tPC+QQL0#{Lu7!2$Pf530XDLrf6wjE`i{k2p?oWrkl)3T{4&6AyqXCbSUdG zdP80lUAK*}l_rHIOT1T2B1fSK5pDVuaJH^1Jnrn8;nZVjJXF1F zwPm%)Lzzv-*Q_J>%`^*arhWiS#cSTy?5-i|IlmHnTXD)Mpe~#`zMGF&8S~W{9k@H7 zGOVYLxZ>aWj%AQgW7NSVQmK8kEn9l+K|7DwAgg?J&B>zyK;LJqY{g#St0mLC7IJ!_ zW8gkNt>hbvq@fpw8ci*|5;7_-Izf>4MxGAQ=r@{kMTNcd8P^Ar*|qhjelNisr^xS9 zUwNM_`3h%bvxwUc+IF>`)j6d8=z{k>1+#f!8mHsCR_^)TOP6Wjo!qC&m>+Anm1Q0w ziYBJ^(+X2AmdQzQr$Xz;Q`Tfo1H$3v%Fq2rQL0|)%tL~;A|_STUoCyq_ZQ53x0PIO zyn~ZHbfa;_8*QZ{WfdcPjFQtyJ4A*XW+2s|FBDDT*Pg(*gt^T5$F080&G!d-5u}6|) zgKD(+7sUA}KM3?r%QnXb*toP+1#=&0MOUqu6$y-*UAbzU<0C9C|mH3rAr0yV*FAXCPlVu*g zBM$1vqL!`CIF}HTl^xmyfMT9SqhqR_^z^rv$SvF42%o~U@Q=L8Bdt`$+ITP3)va1k zRd~NpS-|jmt+cD|1elqT$-pX^uHtU)36WjOyj^W7`Ml8F97k5@Dc-2p%5nk?hRWgm zD5$P;mXV*Dsj$A_c+saeQ&?{|B@e*sifWAq+o@55W9`Z0(x575_nZtm0=aLyi=B}` zV|GQ`saxpV)ysSl?M~1lfAARJ^AMmuXv)Lzw@+GfGv56zTSAvML;_I8He>Fww`YkMuzra${ zFZ3aX*FR~dk~h|abG(+n3j4$$%7-`p z<^m6hQA3O5ANJGp4+53zDR~`~PcGsr9I1s5{tMb3V3m2Dj}$5vE8AMqIUK{_Q(YzC zKN|zbsYiDvxS!RYO^RQzcNTs!x;m25Px;6TPw9(z?k?OvruMxJB-Y@6QZm%iz2lrm zai(#2k0sM8tN|)Ig`oud!nTlN!p`RSAL=tiB+^(pzYC~TdH&3=bqONfbfKY!>cNgiW5EZvkH7rUpndjBDUW?h!dtPc4D8ai%8f)2_%H-eUR#sq9%h? zP49TFXH@M%8^!BYGG+c9&YyH!MHp4`x`~=_jO3+FClaqJS^ogvXFUDUKSHUURHbj? zo=$b-e7+BIc4SL!IoI!lTG2#2w=G&#+YT+0V*F-pSv8sKCB@5~Yw4q?0S%IG&)p2>19G93Ujb5=EtsBbl`W=md?x9AQ6p;U=$}#y{{XeLC-lI# z89P}1X9QKfjpUj&g-uxJX8iqrZ53BL4Gs@LtyfuixH8E~eD*ROSKlb7pSqGojKaNu+!lFNS)_2uE@X)d_(c+PMp}S)V|B z6jS%jg|3jYq4zBo*t!47Q zW#lK(hmd-edB|$3?~nZUMRj=LMX<7J&O@}lAd!}97>Ju zwU+93%0i}~HTFTReM+r*jqbPopzJ9KPqWSmF{U-O3OBgqDJpQO&z&Pzb{`@k?f{KfRE2m(O8Z!axx0D@Gg8$*#Aafp@#>B0o>dEF`Hk$M!tz51 zR-2B8NG>>#uaLP_EWb6}rFzwdX`KEcDFf^?4wJMB-z?g+gyXLreM!cd?R(N6~<(54XO>JF4VI1<`zDzCXpP2o2t{H-)&C&+j6bg+8r3&?6g`?(1m98fsSm z02|rYQ<}U`EzICf8RO2^afVZw@`@|(MZ)zq!C1H@>SkutIbykpNV<+!{x0)cv7 zHs0GF&^fA9@&3~D{NS#iiLl466qO{TWfkf))S!P@6^X0t5n3%FhgX`FaQn;4f&Dh< zc%pQDt6wsz(RAhBOslvD4(g14fyXH*ljF3lB`TNQ44X6nq-{o7_QQ$hmCC^4d~w0t zXv!03p|(;y#EGm?L$w5t662*xpW77oR};v0T1#6iSVDid$oOF)nmW@|?~83DtKHHA z`Zt|Qw~HecXwa9Kzpl6=<*lnq4@J8q_3{x6%>f+85}b6jwN+#(U;;X!Blf^S)l)Hv zQm(eej8ZD$klpF66pVsgsHuInQYF_{2#Mmfl1+jZ<0Eeu%xJTFyzX-0&SSxx;_fiB zRr3!gHBg0PuUP1vt3?ajfLfPOl!OEn%n>;l^d8QI_7e3ZbsONkm&|v?mAC0Y&S&_j zpR&%?`?KS$t*q+)D7fa!Sdg2IhOVJCHI*#&l9{L6gdKDxQxR+49r0&!&OjIM5yoVMQg`Hfrl> z+73NX@>pr0O59R_0Ckz1O$%Qkqe8Bp;yA@zRV(LS`+IZX+FGXaT>h4>^1x9>Jw~+D z)h%PgYh=q&;~Ly&XkFYLs1`Rf-nN$P-&OP3-Twg9H!|mYbzHij&`mK#54WvJ?&UDYRJl*p-xvZ{7uPIs4gsMW>y4mTTW$! z=;*QIovzBQ<^D#mDl9{YOq?j|_CQ%VYVB90;3MU?XbKT_wQaxZAw(@Ea`(kt zGLTk;Kpff>&AfnWRIQTrHPHF%6!+s&uPg!N_9PgpqOw4(a|!@M3}Fxg0YoHD87gx^ zMXh=IXH!>7`l+gE$a0#Y3Mg^6TUO*LDoltVfI%G$SMoT#Q*~`>*0^$!P=8|kK*V!A zy4l8rjo}{+DShO9Je7LYJamowmciZu7B?e`)|q!N9e2`w|?cHh19T#Kplu%DfBQWdjNv72W( z5ZtA=1_k1HZMwAsdC6<;6F(N}g^+!bD>37Vs*Ty24~3w8iRowJOe)e+;%(xo&ZTb> z;nV#2!{e}ty758HmzqJkmMzRsP$4Z*(l<_o_#p`$JKnrgr#XE+9Izylhs{6q9J+J78#;R zR;Sn^7rKasu>xI4GVAulDn-g_1oR`_IOWTg)Jve5@jYXgAOpimjXYzLQ3kbu6)uOu zBX|W))1oS+$vp1&qGmOYT&w>8Ehlb6YFHX7 zn2CFw{{Spz^8Wzb3r&aq*--oHDGCQ!#Yrmdnx}3M%+-H-S~Ex!KmB9Ce#kEz!d^-m z{nTo3vO-)5mY~XWi_3BGDk&E^UbYIK9U*eR*eTeb?*#ne^=-tPKl)|K1y1$OCfTFai#R;imdWo3wR(F(z#1Op}Uo4{J4r$#~e3R&@!u1MtB8A4vx-AM) ze^eGbynw1@>_@cF6$@VehrD>TC{bN&DF(Y_QlK>P+zb;Rc@G3D`#Mp?sA~E2JuIn4 z&6U&yfMk`lgZiZwQ1?qJs-*2pjulZ2=jgx5*7BuV@*w<)*p)FO`Uw3{Q~SC#485&z zR9$@fRreEOO3exxxcYj62~7UTnl9ss$O$kPEa0L$$6~$B*gZo29PL9$B$c$Fss5NV z^*G4Tpk0cPZq*cDxI>vSMx1*2idRw7u|4Z8pATv$6Gwg=G$=PhE2vOtJwp6N-vllV`?9XO_tmrtQaON)Gpl!Fj5W*N4Iy+!WHUI&)X{Ps#oGJQ>g3H_d$g> z(34P9Z^Mtw)gjyan(fN;5R;QNNVhtn(5AmifQfqScN8nI@z-SapwIY1?wQ*&YkCU5 z5BRhs{;pfX}NUx-zyCoe;nJtqq^9T`5Hl1R+87x++&Z#2vc@D-Un~j?@#4<4vB@kAe6qc$=Ck?SOjOT`yU}5?OMEl zoS11e=U*+K?_gfBEZ>OxKFV=>)gAEJGE9<=wyK%--t^hHSi{MgY?%VOQP;*b{fCU6 z)V#sz!_g`_@~C^NXXGvn)g^y-8CKL>UzW% z@l>tGGDaA|O=VNKN!$33?23^c6~L4rJvTv2{5)fXbW(FPrJ5%y5iVN{jwKOX;mt zIW?^9qPVu(gaTZrQt952nF(+Du=)FTE92nhhVCx*lMifU<>n3hbU$wcu7inRZkWH- zU$rcjowZ%-Yi%oYY@2?B$P?$Vs!>yM<=x{C^v!EkdhCc;{iU$ zCm!IdTTI7pT%8`@xjEOu9lhnP5-fIGnq41>38Zi+f1?%e$lt_u1zpF1!(4o>Z``BY zgRqX+vP7s=@x`s=xzwhawEn2!-8W-Gr+1gw#}9;rCEL9I-{{+uSzQH5@Y?HSee;Vp z_I2q+zs$^N91}gx`S>XTK^MwRKM4+^B>w;lzEHc(Txjq}=f9p5W@T4;btO6pIFCPM zb9)l-p^uyx;lV(C$$dq5B8!T$_NptX>RYR~)iSzw7TH4mR*C>1oqEOhZ+3L{w*l_i zW4qtXYh0DLMj@$R%&U>==xcckIj^_L6gLaK#-fb^TT;vQjyQlN;1Wb>ucQV-kHE%7 zt82g}D%UeT2ateEv{qc2(FZ-tDe)w043McK3OM9H?hmvRFS0S2#k%e7f?(h&Cbhy0 zgazAw+$zJk$HElXFC!^<#^6Unu!QQrNfuw$mke#B;QfqF7v;q^+W7_>%xD5IK^?6?Whz zej(Nhj!kt#$!|?ktBti(gE1v|7?sRGwZs(LsiZXS9m$aJK5!e`MQIcHkZh_$O*}`k zBOs~MdnBkPiP>~mGW#G~!|bML_Ez9zB&|bftRj zl%b3PHEmBWj^xpCGE*xX)6=1W@$Ti20;j!aG-Yg`ZQayZa65GqH%mFGX|y3FC>y6O zBi%4LKp92zzjgN0=8%xD?WeoW8qAUyywW#D?$8pKT1X$JQMH2oj}$A+Zo5w66Hrq} zGp6F~u|JvEsF*&ctt|*lkDjp{;k(g3-SRY0l!=E?(Aw%yTS-b1M1-^wh@*D8Dcr*C z)v3m^dwV{UOHfEa_}A|CK{cuk%%Xo@a;rJF$(K?{LHK4SNrD&&Lp*A#t@^FH+l@o1 zZ)j1_cuET_pi@O6LkzaEK}@IP3~8d2RI5kZS2$Ggh3>AY6iKe5xrC+kZrY@j&P09w zF)zv$HTAKOtvcea_i#E~{>75sH{0phQ1#KaVI+wepJY^JY)sLR)h-fqYj7UryL?gI zXAg2kEitj>^p7~=Q#So%Z<+Q^JaZx1_W{I^zKz27jkJJjtwp}^ZLMW4&{v`KtER;R z)-y6%J%)rWtQWD!J`+@`rxcSd#Qm@jO%{_WovE~38m~|BOw7lShdgMLs9I{Ima-}f ziz(i}P1B;AJ|-)<4ON?`Ynquif`*o-5Rl_bo}hSpAlMFwIayTNMR-D#H%fjU;|`Iu zC^niDqTR$S{41J#CE*dNIn8NAXjhZCHnI;%%!qRx%4)h@G*nlRdw0wK0Gcm(mM?Nf zNV~hG^;;{{qT_p~@HV|fhlJx}jqV8f16(M)?MgB9D`~&xf9EPvEtSwKSk# zZI*px5IXHF%*D@6*5Ff{RYlKvWPVzOWiW30_^$G%sj}Bx#V&w^t-97+Wjlt4RS2WJ z?WgX;P_FFz-ZP|)Agf!C@<$uzl#2YDEu*b!tQ5+gwWPRGp)EEqaMCl=<8C8%%TaBa zwVAZ$-BxV(>EqkyB;2ZU7pn3LeeSH4D(KEda;hm&JEoTFoq4p0_y8jJAKdpgqh*=O z_C=-X)yg}g>>rhQ>wDZK-7VtAl&gCdTdlH&zL@yQN@zB@X%@%5qn}Ayv##>qcr}64 zPkKx5)GLh&TD)So?={;ROqTt!rFP22C(qHT5}(@*Clhc#l%pe)J!WV}I7BiI>JVxE zN1Z!x$Z<+{2Fj|Mnlx5XiRgYdg25iy!du}>U05Xh(D$9w&N*(OKIK_+4ifB_xi44p zYx{Ve-l!d+ms3XK7JS=wKJnw}ihW0m@3M8OPE804;CTxba^g z(PXqnYNw>F{tKiYfH=`oPu&{JPcvHK{YKENR!hkKFutm--n%jGa6^8D`cJ(Tzs;-ux zEo<>AD@@7v!ABu%@$itU#v1-3@wBM!pP90nBP!}85IX!T9y<7G1P8Q@5dgRyLUPh` z+h`zF(kv6zbD!A`V?haBI(S=o($J!v>suqlsVW-F+Yb70qjkutkS!e1XMM)DS0{It z?+N)Bjn?FZ=H3uih43FXTUU-$H05h+3~ItkjnTV{=ac2bltIQKN9HcA7q2<^vY!uHrnC8nyP5;txx5m-FS_il|Od)ifKvodkLBNoQCb-C3`C{!%llV~TDc>JyQfCnvKlFr5bF^&a z8?}6b%2X)%J*t8Gf%*pL_LkHlK1lxnl^V;Jjhv$HQK#kH;|;IET+8T94aNF5-K%bs zYJ;>@38`;IPJa@7;5+PGGq#&6>?d)_YWmixkz%&K*jM&r{Qia6?0C zK?y3Kz9^sDkp-_kWR+>GyE`<3x`cieOeA!OP6{BEz3n4~ z6heD{T7EsaVjItB|vKqd^ghmY9XDI$3wGflqvg+!10Ft3>Sthc6Fyl z-&HbPa()EqKhqrE){*}JJcNZ^Cw;>nk;W>U_kQTPs`d*udX(5Hj-HhrFlYdLltGkO z!P+)#`L$G6ee-=M%6U{>aDS1rR|@hBf~w)X%!-W_oBP@R zcb|Ihj|Qlw06kcOuBJ;G{qbOKG^G%KNd6@(j8>Th6fp>8c6TwA-4AK~UZEa8X?1!$oSLd$r=NkiE5mW7xf7!YNu+EA5;%QBBhE zTtbVr69#9~-xIaK6r$P0CJn;owmk_$KCE^(eC0X@w<-(K6dO}RB^pLsf5ac~{ zP~@lcw>nmc3iAkCh5(bf;b>NncC?3rmTZ4buHx^7wcj$!HayyoQ&9a>hqNKI#Z||4 z#?zuXotq>0(N1{h(y?w79Lt>CKqL6A+o%$c9)WdgU+Rj;{#%e`r>9)$vV|ci9|rdN+Ye}#oVD6pXv_@{s)qLy=1Gdj!s$(AlB(mV*o7gF?n~}acah4jr7gcj z#JKaa4_8&Vh1Ld-kEYoqkUSRxd~dvxgmOOhvLYYrH3Px?D1Gh|3@mYAp6*8m+r;=gKA59km$o%@t) zY@=?pNf{P;ixoeok770XUBPk^>Io5+N6Me!YumMP>lsjsdN|98rPIAZGEw0HyX6vITrbG$RKYp3&&WdtA(vIU>G$P|nK6qfy_#~R3aim`a+Eso;Usieno`b!%Pi8hK>8fQ;0 z;iN?r*(qe)tzDKb7>B~qqI9uQP_KbPep3hbz-3uN+M~+egaxSWNE#Rs(UR=~lbN7u zQ;yAWLYXKIr3xohk_1OJq@?H(ao@t$>OyJdysd&_4LbfunOm&rYgPQkV-3sSxlt?_ zYl%Kqlr-JTX%N-nQT|N&o%Wx{il}C%ky^i2uoON{+J)6BY4Dq1kJB1UF2TE$)QT*v z{{Wd*QS5576PZ4_*4B|(MZ}zNr``aHd&Sw%luyB4i!&$ONR91>mB3KQRtq8K*?mM| zWEV93M?0V9mnv!RT!~R-v_H5m$7|g~Pqg?F(cG`J#_Jo}wpRNARe;F%h4s$vqNFvK zd%eZ=zg1GF-*}R@)TJ$ICs~rBNjk=s0UJdCSz`CR>WhjJl$UC2lT6Y^t6IW9o|?`f z>a-+nyocopJ&cox?AIzmlJXrLDqD5GnQgWDs}fky1kYH7_i@)ktZlJVkyvP7U*;96}UA6KymnqFkiWEWUN@QZT`dXFh7-)H50Y!bi!YbO@t2P7~8lz(i>W|@2%HThie?0qvZol z>#ZJ(OKt^}pLi01y41~%l$Zqw>t0c3F<3zs*IO2QAQo>=xx-Jd2^I`OC!!q-Zd37(YJ)>ALAlO7A+ z*EPH=>|ZPFI(UY9$v?C3RH3EJ>!f5^CoHRdRSQ36!*l6reA6nHd>J zb0p5Te{!@B zyR~L&(CB^9cFUqxAjd$9nuH`1Jx54Kypt99m) z*Zkrjf2@wWkhzuUSZPQIa4W$hU%ml%%vPc= zl`bVf&PSvmO_3-h%mNJku&Oj<8HvX(Ljp{+mb%9-LnEwm=d}ICkgMwB}WuUC*k#1x9^R2W}o{osJp?x z_bOcxq&uu>T_^D;->f1;a;(aoWC_k1{gauxT*I8&^GTM8Qf7bBiyI9b5ZA}xD@N&n z{JPqZbo3jT_H7ZdX3Nhf-4=05-Lx424k{pvIH_nPXVxK*YLp`DQIwC2Mx?4L@C5`N zJ+Rldsd|Yi!QuJ$%rL7sYHW{ zAyZ8K8|wHr4>-_;x^%x+F5zwKmD+;WR+Pvq({0ALOWw5z&B@s|&O0h-eAmb` zLUvCo53GhH_l={wi`XcgXGM(vfnesElVZB&h9`o!XOWewhO_bFE~HEuxz z!UcbqwAor)`M?h0O%@NTM-koyb}zsj_4PLIcHM(u7X%{q zubj!bBakZT-Cd^~w6df8YJ@PPe|qwg%{w7MOEZ+h6hMb8*yY#Uv_GK8`%_H@!`tkr z+mD8o-F-IK{S*7C$JS}7ay)w|LsJdcwCzPWbOWK-;t5R0gv?&W-0^_1bM}SMI%=4g zAElpnHU+G`gwov5-1fOmR+aAe0+A0r)etJZ-KT%RDE9B!M{zj%Pv*Q&asek*Hpi-& z{{W#lly?oe8>n4^yk_kxJh*TVB+xl3{y(}>kTu;z=o)>IBXZE5DmPJ>lN{6XPv=w=_pmg`aBJHP&0&7_-s6j7` z-Ik0gUVjhth=0~{?1gosyb(7z)~`Y6sDb1^q9hpyJBnx0k+@tpgx)C>O*_>z((rB9 z5&r-*h#|Mn98?_v;FW0K?$!Ua#B-5r06v8Py25%^WBqb zXmh%^5}kIs8+d8pccv$1T3|I@(P>Yr4(+>ywP{mTLv%uVhiNvb{islhhHd@{uCz{i zZuxl{luq%5QB{xLTTVEFK>q+aCMFrG4ML1sHoAng$A1=(P;Y#9P?CQV*mbr40M3*- zxn?_4qKD~Q;GDFb^zeOY+jFtp?WFbJOdS&Qn?S)XzKo3u9^%R)`BUb{-8XHQHK8lY zQ)~*gQieZXhYM{jU;uQP@SI;;$PSAuc@b*}w_C+~*#vN*-#aj@Njuvbxg2m;@mMZ-^isvE-e}TTFp-bmBhULf+zu zynwmDwbie$#-sLy{OG==U6y6Ngm-z%J+kGlyK_blRw}Seo$aM}%ueK`y=u5ANRTcq zq?Vr0e;KvOAtf=rY~tbu$~f=sR538bY57S$@wnJ{*Vt)OMW57(nc)cs8shwSUx$s-RxH@b=tz>QGB&H z-Bp=N(FUrSLFR)00J2n&73`c}+PZ;Yb5@beIb;=S-G5Hr2J92EUhw;D;Qrw;Oilb| zhnO*2J?*-iMRUw0FV|3^$HJLb%VX9E*!#kDbW7_uUJdiM1i!g z*qV7?(5sAVR_nNbIOI6;)JbWomMH5GJ(Jqmm)i#0d9OQuW`eUf{{ZTxp*4;|6a$d` z40(>+R^}7jr5#BIZ6dPUR>HLDHB-Jv=#3m6Y(npyi#c)ruFnyya{7D1imsnaKdA-~ zvb;Arw&d)!{o9n84P^q%ypvLpC+wURADB3qf4JYdQe62T+;-Z+`qS1&;+j>QHT8Mp zgs4;FS#5GD{vT4^cBi={>C^<&DE^2wnd}t2N4WqyL%i+&P~-OgE8>dn+@CvR_Xlmx zawWw}Ye1#Eqc9A-U_+UatJ|B|wvx8AK_j}}a@r@KR;&L2Vk$fJ=@WmzEf??oy1s7z z0Q%yvpFdS_PwVoiUFGeW;E*r9!*bS3ZhqT(%&RMCUlMVJI_AXhIsPThLL zn>|tsVxwM1fA~m19tzXgBQu;Jt(FOzriKCk0E}O-7!S6`^(u(&6YzyJj)FIZVO_fR zP>L>eWhhA1AeqF>Z4;y-iCUSULP-bN)+S?WfqbdwU~LGiL6nDCMV*rXDyH^Ng-F#y za~cia$hZ;n1)LJlrY*|em8nWdl=?tZTB}QYgwu6}Hlj$|EeMMGa;sLnIdt2g02F@6 z=#+155?xI)7rAK~na}NkWeC_#iN8OWaHPz6!0$Yz8phQu(bpx(ps4B6a7!OlGvKDF zMQdTyd_!`)3_T192@<8BO#Bm+M&{KL3UAyQMSE^itzoU&1)hR>MP2vGuCbaGTr2+o zHO22$$n|{$4cn9q(zKr_@{FZKr@?x~tsfBaOh(pUxGmK6aeJrlGm4vG%_fS``Z9{= z@s+VFpk>9ixR50twS|cCFD}f=1B#ujnBwj?+jRp;+N!r(MxNCO*4oBuD6sikkjTwe z&|!VCT&g(G-6&~_-JoN6NCfVZHyIeZ=KxTzG;^HyRBWs36pm9k^G+bLv?)naPf$`w zJ$=zLnN=y6qm0~MrgS9}GtiG1E)+b|3M&(jcJ0qOb}ZgokKq+pTO;UF+p0v2&^4Tl zk4PX~v4(`|s+{K?K&pkV*zNMqRP7Ds)cplxM5Wf8ZAl=GqgXH+h0(YagtdA@;z0m; zz{<=xFB-TYC=Wiq5{V3o9!jOwF9ab7N~D?0B%MSlkw+{(r028ccWXsMRd&n0MZTev zxwlz)Z73*okwFkEpIy?nE=`ri;|~6NlH)}%-K#!&??lGXni}wG5wsqYoZ`Cg4{tMd z3f9Xvb#8ta6{37y^`gtI<%(`d!CS6KDGRT+x@Rt*-MR<(h-`7cVv*j)u8i(-jo`GOcSv{D zh2Ko-q^A>pq9Cq~Z=pS9;|>%&R|XNm4Au4Kc8Z!8P&|Ga$U~^#6&VFAGjy^%ke5nk zzr=-tB1XHHVxoL)cH*eLW(~(#I|Zw@Ic~Tm-^3oLVSRn76Ny`SGd6Oon+ayTL5lb z5R0O!P(RVC3lQJe!-lO*J*f74uA-N5><5u7n-Z=~tG+aYqg9lYrE2ySun`rr-^B0B zM0^<8*-E;qNpJb73E1M9s43A}9d&@*T6R|L=L1wqzOLk3Q#UMe7X}wFwt!00x2CC3 zGl^rAv*jcqr?HM!{A~q7S*LHEzjAO|vK~C6wp~;Te>A6MR8=Wa{{Yfblo|Wt>esku zi%QjIuRAKnZA)lXdb@U`$lQ3dap6v2u4dlE)SCr**8bTUfQ6aq?-8T6?mKxt83M=T zdrt1$)(09dES#0xjfKTftzMW<`b_|0+TSU65cpoQ*H~)@{L%f&x?g)T`g7&Z6gPEa4yrTs(kfr22`9t& z`a$;n&5>c=p;O%UZYn{w6kl-qQuf1tTECL{{S11a-6Q8{o|O7dY7uDO`?+t-+N#%i zTVnlM`X)uk=*MsgaqLf;sB8NAYH^{{+FNx(4TsEU*Wzj#Zz*E~;d*AT>1*9)Acqbs zmD+EO07?*wml{zXT7n?<`GW(#V5%nas&GCR1rOytocG6!IJK{jYA$<5l{8gtI+N}j zft*-F-j;6M(NWy_aemFUP~F!6?-v;h72IQx(*)?H{IkvhFKb~@VIOr_xai=s_dS!4 zXXPa`9?QFxv63orYr9HssCI^>Kl70VH$QCQp$fWA)xB=*D68D}%@**4-0_N#NB!+1 zsQYySC2jmih)}-?INhZkr0#P!WbLcQlBr5*A!$FPK{4GhjSw!yN#K`h`FsLnTLenLtH*AL2gMDb{-r{->;(w> z7X@N)4Azg4vAo+U!|_|SZI6zrK6^B;EFcOxVA|aWYL*nDQrd_K1nCf6Wg&^c6GC=R zhY>V_peV_X>Ue>~CB~}m3Tm z2!Q7f2mrJ|OTW4wOuu_;U1A^8Kv9geg%fw` zGiFGWb{f+zg+&APLxwSl24YSKiTa6AxhdlHPS5(zCJH#n)L*O2ldAjtYOpKO#1s#L zU3~caz0la~BdQWh>D-%j9q`^(73kvhjek^?El2dkqS7@1bj2ety{y2@wL?orJQRk> zR4XHhZn$;45f^*X&m537l>&N6rhmx_;{udfT$G-!@wo-n0F~WeBdAL%AM+C&(QU-3 zK929fAExfpk=&Qd<9C|EuOFAFGau^251Q0*MmHUHcoepd=y{Fw?PrXyb!7hl@uTPU z2#@M*yh5M!YwrSzubF#{yiHc_AG@p6>AIERf8~gPEKW)XT1EZDO4O~ zS!`iuL2j}Hr{k#*!=AGoRIzrA;X4)R?cco|goV_${jDqL(itT~?ttd*yHT-LkoI-a z+$Abr!MljPlq&m{U6~Jmc$6h5>(}uZu9Lkj#}#R9*i)0TciTRgS6a6cay!NL>^dp6 zRqRjvfFi1X_KSg6cKwSl5T#oW(yNafX$G?ZUFpHs=c?*n6VS|punbho-emDtkait+ z3U6PuKYhWjQ|+xoJ`TF;ORXJ;!xz7h^SwYPI&r<-HBE!xXeF zrcw;B+aP=WG^Pc)?kBs7uy1U}1ryGXfzeN*?rGcy#AZN%&TZ~SL4l2@Wa4Lwsp)0D z=G$a=f=uEk@zZ8+Pdo3+NxO#MINL(aD1`kw+%>V}rigi0K2CE`?!$-%%?c;0(|%fS|ID7hk;W)W6zY-U+nbMib)ctZAx=!J|vMTz|&s=vNqco=W69F+s!4N zq@t3Iw4{Co1jxV-ymOWPy{lLflerby)C4rGlJQCe#0DQ-3bHhA$T0n7LP~01Qi?nZ zrS|oQ{fK_1yPzJpSS_jbE3Oo@n?DhC=F|TGIK<6thk}LoaSh68T{+K(@sgu=@Ym27;;@y?t6% z`BPYz6A)D(#YJUivp?BsCVzZfTlNJtd0DI9H=RmG)u%i)lJZA3@s>HB*qf%auOVPM z!w!j|DxT5_8t6o2@2u_ZrR8v{*y8h7chCnkfmNM9Q4T`yU$?t;4n%JZTlm(rC57a6 zeTvyyvOsO{;&->WOKH>-<2dvV(zeH4Gnbd*3(0%KIheGboc{n1sbDrq-Vboss;Zib z&SJf`9a5$fa(RK)T@X_qh5kKY!SaQd{dXlOsc}#SQ_MF@p?0Gy&(&P>--*h*lAlpN za$3oTGAmfVR)rI7FC{fDCaS(<{KJLPIN}}1&3b^LeO6}A$ zc;?y{Hw!i=a=H=G*8&(p^3a*Y668v=gSWWVH6P^;NUtF_yjqsnjdr^7l=6qXCPFHo zcH*vCiC`L}+K0J<`U7v};@Z=?Evi-!+=2FxOd*xj-4g|bK^3au{F=3sQFY4du4!;h zX?5pFSn&i6AW0tdTrsp7Q8umoTV$ZQ(>YN@mlBr}W#V=7F&Kr!f~`cLHj02{&onZe ziIgaWvt3q{4augabJNT4L;aPsX9pF9A4rNhm0jsbdki= z>_{ZaU$zxQj5S*#C)2>kB}NUkV~nN66!wx04U|f z1@l8$oeXkS;j2M9O#4K?Y;xtus0)!AN;yctxm7^$`wv+{b5TqR;Jxh-$z9;NdX;Wg zra~ZGl5*E2h-Cv^C@R1AN`xb}MOlAwVf7P;Dl_PRrYRlst!#fPSp)(KdKh?3ksE1S zR8!!5VN_K|T3+OhCFLBI7z;DkSmnrcej(vF<;oJ})Ro?>Nlb}VJD`88B8MgY#iYa^ z>6HUIkX&#lQVgZ;iU%zfRH

r{ms7vWTqJ4?B4W!{b_?1${P&$7d&3@?B_L2VpvkI5H zzxgNZQqWNwPVK{A7}JebIJSsntOrK31Zf01+WRU=)qP}kn5 zKOWi@21cBvX#)hN-)%iWr)Yo35wmNk)#p?0iYdvc!+EL{R6xW@R}OVa@RT4^@JZDQ z)JV)2Yu!YRZ4o6TlcWxwzahD(4?eyT%ah?(M_ISh(%X;8lz<&udMQfiNj+i*?wa_* zi>v*2E5UtgacNNa)MqkJU0@=NfUAdr-s&wT&*_y-KAhAv19qMdKMqZ{KenI=5mi-pS4hY-|CwE zS*v3Z<0VqllA%jqTU-1oZsYs%@ zIEsW*x}+H?DO|RaBxV7Et^Ch_EnM0U9v<~wKl=HObIV&N#l&e}+N?GY^^VG^zFoYx z6LG-MKBZk9L7J5$?E z@bYEa$70LF4uTrT#)>J;zzq`t*HI7lODExfV`@qy;S zWu_t000H1Dr>EKWtBW4&?I;>yw=NDGNYm~=xqVgrC3sT~`gHJRRkDF>R&$Rls+!}? znSu%yr(bCw;jQc~K_}&?(z2Go04YUFBXWr*%iG73QL4yZ_rvC%jnSfDx5417t$Vf9)TbRkc&JfAYAYsB;wDE}f!(`K3jW`{8h1`g zZtSe}J5uUec7KUV2_^t`0m@O`F*z(X+*S#3(yrA2d&3ExltVTs!x-_({_GyM=ewigW>lwiu*IP4Nz*xzlJ(4kR<+lgOL8bHz_<+^6Y zHL}?_@J;%j+&fmk{{Zy?nk6`Y?9@>&xXL^0uG$es!o~gxwOiOvZuh{YS+4F|+*tTV z^ea>Eh#Q`~Vev?NUcyc)Fu(mV?R5!K@!ZjcB>o27N%xMBe0|+8`GOK}FJO519H5+! z>6yv&o7HM^g6f3Kpa%+6eUd5hizauAc!ftlmhyw43IoV{59a$Tq^|^erLj3FkQ^Fo z;Ri$Akh_Yixbpt(rl2Th``ZU7T0+ft9$cDACpC&klnk`$gqX2sal-A0RnvBL+Xk&F z6ufa=-hsuUvd2|Ow*LSVPE)#qJ^PX(#w@!X#43q!a~F=4a^CXr^|o`QzCz4JYJO(;B9tMw>NQ% zcRmm0mS}B)rtZ~>;Ps`XqvKUW4SLAiEobn;y7!xPYl(z5cK9!u3j3L?aW0e;dVmr< zM8x8z;MA{hw6~^-IZB)ei$&I?q=J6fh{~P<0oT$-&}K|4o&uS%6qxI#WXoSD5ze9} zD9~`_GA`f~AZaLqN-0=sxI&D=yuV~do>QXC{{Y6=B!Z)-vMjA1QmXHus9%1c?%Vgd zig90I88;B$9d^F!hG$pmn{5IB(TnkJ>Zz3(58}A8AZ)K^yCCkPcl;NgqTQXq@)qQ6~rCoWT1c!Y!3m$WrDdT&h}@R!_sY7U$vT7uK#>T%Ek3x4g}&S!VFQnc%nD754ax06wZ_Zj*~Rb#Cuz-*A|9!KAJ^2@$-6;XQGFpL>R?DOp-0Pbj3?7S!ETN8C-Vd-dY{46{o**l{{RIjxrN+E zy$TkE35Nml)I%K$l(Eeh0vBnKoQTug4WowzD_tLmU&W`AQB7Z$RW6)`r7BP|9@zI= zr~FI7ac%yRNTamV6s1lq0pcKjqBOLn1zIiW1p1<2beH*Kx~cJmmi;xkw34?#mrY?h zs&=rHg5&nq)Fkp|H6mgneBl<`Vw2yyT9PI62~XP*8N!veRY=ceHhN_lzLf#(5fqog zidgIkQ8saZrn#xHO60(v;yM8ow^MVg+;yR0K=Jt|u|vcBkWX%ZbfJ5cd?8JZfKn!9 z>RTRbRtVD(X6z39JEHt`krd*TxyoRX0eDQf+fUg&70=RY2DUsEFT0 z+*68a$?LC=NDMnvRVtR~tIp5~*UAfQs0rW*?L)hoQR^>kNL&)f8jCV}f|;jyG`*_x z9dy<+Sv!tUXtg+)D&bXHFBb}`x`$Nz%5co}K2haaNz0zSYR(|m1alUn`OAZ7BS1v{ z_^NxZ)#F+Fu0pSsv$kvA7j(4VDruDB zX%Gsc=H_c>EPJw+*c1y*$=P2=AEei#u*45L7-DHI8xd0GEy6Y90 zyMqz01UGK9Ow!&fE_>AaW%nV#*+ut|aI6luJmVXp+^MOmwl?ic_R^wOuTg|Xh-Cm)g%6a&&jag9wPsB++^nskq^f09RSJC#)V8#gxP!O?2v(r`3~3{Is?5(V z0{egLLj%U@I0F}^uA*k4AD6UJ3KBG1W5{b5%=PIH(QUhW&5wxl1)hECcK63E0*BC<_{>gl=G=f&zKW^CM}KmiNqXOMT`R-zxYNpQL&vH}h~#u`+$nyQ z?D--g#Q4d=}>@W#@jz|5lp(fi)R&OaNnudc9@ZE zE6eHfI&@2ND`~eWKzXU6lNA$orKE#Et}VN+`ZK*%qQyS9JjrPE*~xhB-*;`f1-(sF z^91>j914ml{JwZABP`;Zl@K~Owq9Z6pE*o}6!Up{{smuJ z`J^lHwOD+A>p|Mp=%bpz|`#=TN5$2h@MEoUpWo za%C;4$4-sawB-Fdv5QQ z!`_{Zo8voGNq$jJfg~<+_rqHohT%x*8hE8zd(Lv$9);DH5}v!ze_@Bvcqn?VJW;h~ z9HB~(rmgn-fD@WZmf1h9CJ{%nf?4IJh`XGv$h49*oMgI!o~ov)Y=wJ*qGnsdc&I+I z|n<5@+SWy$r*wFaC zWpyb@GfhK8E8X~F!IotHWnTNnkIIBkJMq%NMb3>6C)$Cx?jk2;yPL64J0TzDYT^D& z;9Hw*i-n4T&A`nLQd7AxKWa~$A&%r73LW>Bhe{^hWglsGbH1krvOHDQW`dj<`@%#i z`l={-thj^&c-LzP_$aLnTMzCgma3+l4{&Y31Sw?nN*!o)N>r8(5Tn{{xjo*7Q`(eX zt4JSW)}aEQsPIlLw%}5zULe|s$Z}=bl#YpAOAn1d^{6Ho=FWI2!ME{2k!dU3-zGfi z9Rt+#%SrzLS8PfhFyTb_;(+n0B~Xh?gokVGH-szk`qaU)q2>F+CT+yyWek>f&fbZm zamRS7w5VSA#?Nw6f!_0JWY66+h?$qS9wi9;eW!pbJiGB9Yxh7!e-$-OwU3CQ2tTA4 zC7D~6@j%Uluz%$WJGl1Y+D&4N@NrKtMvahvL4xDYT@EUqUfQtm3Oudt8?`6NQ+7vb z)K5^iKWT$v?rXutR>|5nCxU0y_Ho+?D)fb0Cti*#L-xe&?t8>6otb2H(Ka9K_qTK` zmhKgD^4v@m-sigULBF#t5M#7{1xOx^&a`=aVqf!OQQY@FC};L%sR?4&!5D5eZoJts ztk&UbvE&F#cl%QhEtad$naMAPihV)x5hVz7yM{+@gZ}^!iJK_?r?s9fQctGqi36Yr zYfp634n4y!iXok^5l!d2O5>MiTCaT@x_m8Z{{SWv{Dxl?{{ZoL=!q+|JR@DVMf(9t zW538H@l5{!6p03|&@m~@-84Nu1b)#A{zET{L)x(f7yj9%BKO7f?VoxQKTwH3$!76M z{w)GZzjj52)!Qn4E4ouugp$U@fM@M7-{jLIJ)a3trLVD`I;o;*+WVzrl}p|0fKrss zeiDJ??%4sNot>2tN{x0O#K|RVtE*b2(7FCdjosTRQx3|5!496rcx|SLM$rs0o7ehxWq%0G7|xQt;|;YKT(5 zLZJqJ>xoSrt!O_gTc_>cf*DZTiPIntCX)_1+mGf_H?}QD1m&aQE(N68Q-{-)4Nve; zb%_4}RGjYRGjKNC0(Dby_X|=>(8FoxJ!MLT6Z#>8Cn@~MvxtL(Q;we=;~I5;hQW}d z;1Nif&+JS$>*W4q@nIM8sl$yitP1T`+jNtTX{{Rb%Q=pH+pvq^bxIkZDH}e&9^boXssV(YjOGJu$BW;}|652=Xh~0T1 z;-1?W#{^00!%jeE*|h5-5IX$t&lJ~PKf!d}Cyh&5HI zqz9-~K-26uIq=mUoNIWRnIP_r~bF`Rr^Se83J|%PxL@;LT*;^-L{Tu zymE~t8dLazscjw~+gvH4Cx?Q>2ikrgt(<8|C&(SV5O+?xM0Nk!lEFQKJ;vq0>cq>Ni5VGX&94pLJVR7cH z_U*mQpOr4RH9Mn*s_j@dLQZo+HqwYrK{F4XCD)IVD zeJxuI0H$iSKMIbMA|#6FxF{WlYfTeHzU8?EklLveM@0HRyKyW9DJt_Dy2Rn?FVNWK zAkqCqyW6qa+p%N4#ow`uH7a(|+im8OnGGk&KfzHSWDh<*-a}V36kWVB#iJZRP%k5Q z6WoU|G`fCJ=WMSzpi+{vZn)m6o~8>)m{*{ZmgWwi?a~$qV~G3%l}#p z#Z@e-qOIzME2(kE75dCbW{|0yX%HG9f0gJINAL#Y=5?C3XRtu2-|j~v=bx%L~(&Dt}2`~Huk_yfnQ`HkCiJ;LR+@k zay+uIWB6u2a?LM`AuZVKv=x_H#T6L<7`Ha_hy7TB=6GWt6M#>G8D4%6O*}RIis$lL#-jTJGnH~H7cvQ;rJY4B21k_d~* zr)-}PIu&M>ODPD?=x~XTjno295aU4xeacopkw{}C$Fm{=mc3gcw zievF07%R5p5?`{s5FOUy%v7Xr%lpFK9!SX4&Le(#hdmb&M^f@kz#xXyM97bS$lY>4 zeT(GmY#j5CYWeWx$}3ZK*v0D4R;sjQR<>$xpw3L(L#{UaV8~8VN&92xO!LE}<{Ven z-Kt21(Vg0U?g}kvsC3iHDI;|jiQpv~H&hqw@{223iVop|iO5y(4chrdilqZoQCm}_ zrwnnTBnqHPfa+j$`YT7-^-)hP+-U(I+%fyA5ij0S6wAO!bFQ9J;MuQc>(lOrNYu1Kq4S_7WcWl*Z4`nuLoINBC*>+9 zr3Fww`-xD;Y>2dNBv^q+cBp~Wf5#NGtCq4olFAQ3=MPPl8%l(NPK0%iQSzFjh;UD@ zPZ;Eg!c{5w&N*^0(}b(_$1YJ1D4>hhKIr|{otNGOQ)FZK)&3;>1wd}<`_j~vtM$bJ zk5#1R(M=1dO*a+s+vy(_!t;7j{{Yx6A+Os8SvsTy70(}HY*h6J&MoBKR5YO~8B^Hx zc}K=l(&j$N#F8?wl-qMj$F)1Sn1$m-6PZKqOnV3secBTL0NVhnJH5a6A7YqL+LoV( zL*W|m;IS^KC|255hI8|jIMFt!!-;W7Qe<`ZLM~~cP7FGkX0?x4LL;=>*HrYCsR{0UF+MMd$Ld#O#RFQ2HCUI1?{5B!0O-@41X8veD%yIQ6ll z6;xem*hh-i^JyQX#4=vS-O4Gr*Z3r-ulS8IP^!!Aw9@{)V+kLnP{Wag4bq%+YFZG&g^Dp26|{t3X(%RqRdqQhLc=+c_iyz#if#TDRj4C zw;Il}E;_?g{VIyNJEk%6imM2_CS&su5n3MU=`tkO{#(b)+YKT;M{bo>igXfm^VRz&p;Ls>H(Dxc zN}JvAlA4Fqgqe5*=@oNeog|fXt|2-e^<)hlD|R)*kMZB{P7G^_#KO25*&2_z*{)s7~a8+88wvmp4!p3}4N#OMJ&y=m;K@_yp;2fP0On1TNQ zrj7lSTC<(~L-6X{eK@af-Ll|ZEr=x>T6&irwJN1es1da-u}+4OFFj*ZVcQmQZL0FC zj_S$E?~Tr{td&sRN^>&S4zcZvJ2U+zhN=wLeq}POrNhgB;Zk5aD#AmPD0=u`Ecq19y@_4DQ9`5d`K|897P<(fh`(nCo-DiTehr3?U zx|S%r#Hgih3r)pnNFz^B{{T!?JGL3%tt0P8jw$VE?}n)>1M`FaxG*#ORlW+vo%;U( z=2DfTzDj}+hig$#m1uWL27hO?@K#ZIlJ<@2pLD(G+G?9E)zMSb&O%3Svx?XDnHwu; zyvA$A2uh0XCyFnv`d6$*hOjO{z_yuIhu#T+-P^LWVpQMd*3x{yB5z##Qpiw6%K*1D zU6%EwhKJoCA~)l%+p~%;ET%q9+7o88g8MYe=22s(w>aqPjji?8Ad$HU^#v%47bb)R z+^g&dwoHk#<(}?s%5cpEFCfeBiB3}aycEZ_v9B6YGiyGS_zl%&p4rIkqK2g|H&n~5 z2MKM|ln^$ocoppk?gqW2+p$ozK*Zv+U2XZuGgcwtTv+7kXT7wKr}*$s24m z&DR*dZmpoGLN_d@)0>pb1kZa)JqV3XI{eR4bUvE<>ORG-b)y>-OE#rG(ZB9LZ8$1a z+P5}VttA$y65UcYQ|jCjhze;9QSn3oKs0yEo{8%a^$MBPTsUSr$U+jF0tE$2T#1*u z8VYgQeo>hsTJ#asNV({J(2J6#3#Fx?5vG&(b%Jg8QCw=(^hfkl%kH%H?tQgckSeNd z=FfFiAY7ED+)LD|knxdhN1=A*!&=i-^RJs2(&x3x-+feRIZwDA%=21XW-6mYfmPe@ z^-XMXPNAD?{;P{X4VjRTW+pU-XPM6#qNnn+AT07DoapM0c&G1QA3rpQN>&v#uj1Se zhI;r%n-^5Ct3)IP)24Ef*TBLQ)&rswr9wev6)Qd=$G9>VFt(5y8tX;7b$?T~ZHNK>R&t%euJu*O zsQZeHN-&lq@~mI#d2{)kn^k{Tzsdska{G^^JrI}o9X{A;!Px%*Dw&yF*g80-9mngR zkXnT8`E7l1Pe2p6>F*H(yMDv@RjheD?S5jL4*vkWCCjuftWZ4ldI0zXwCE@FL=Nq2 zbH~cAV9nOqDsW|);#+ochhqBj8%Y^It3p;%TCHkB~tJj zMO*Sp@L=n@N2ujngTD(vkbKGM5FDvfM}i5vSI8HKil(v>Pf4HK5zCQrMaPL?NKWM! zcaF6R+I(jMy6NQ>EMo|}mrD01x~W?GOd+^>R@=02MT_pJO>Y&G)Dm@ubqaWmqk`=& z?%n4K=hZo^0I3fH+$z+1Ppn_|`TnBLcM$j#*p+}JddA+X&fGOa2`Wdxm{gqUN|cSn z7%hURIX6y9QKEI67gRq{8FBK#^${>r(2ppywcS@;kARi-W%T&^F>tqX{ho2RDO^2g zGThSAiq+f_;V5axi2*Vn)tBEAWqZmi7lt@24lA;+GEH=W+NfKRKi%)=RBtg4rh1cp5AJy+u0d-oyrAPa`%JueC~I(tUc`WD#lTE#Oahp!s$X( z@TLUlrxF2>I!+C?%359;Ms-u0Tk9=mZ3l|QE9I%$cOGqMC|VAwIvqXnRdXE;7S7Io zc7hX0b8Sa(l7CcI6>`^A0OG>ZQ$D``0NW8tQ&`%hD%2B71o1+QV5%oh@k@ed zL*pIPDpy1jtxZT2$_MdFNK-0gcH*iR%j(EWn)%4aOD`hX;qCPb2dP<8iYn?!pW7a5 zbpB_rMu=#nEC!p^sVecFhwY2(G(1%op z^^mIjDwaYNf}oO8dL+svys~8Mqm@0g-vhbWzN)(|`kr?4-0nR^X}aENESWJwZPr<< z9c?R8lmx&FkXP9rZI|sUh)+C4dQKC(t>*rm05d?$zu+q+%U@3J$@`Kf^_1~NlD6pT zX3j}K({#0oQTT#`w8bQu`OY&?_bvUQqpGzR9iNT7Z!;*D@Q&2$czYac7UTQ5tFl|# zE!xOyQS8)@kxeO-l-YXu9X4O zQlBKGl4H=Yoxy3#K;?za_TAp}Sl!9=Io39ci*8SgTrCSm@V2UHA7)bj0M;Z%(mSZ! zG&NXxEaYd*2wAzFW9YB1dZbM=CPS>g=vg7>R#09BiYRgW8A^h7?%GWB5HN~}hFsMI zEfnMNHMsZ^1ScRw2|a7y$*9+&m`acNqEOyKp4kmotHte4f&3to?9=r_B#9Q;$r>W6 z#ORtTD5-U#4v7moL}ntQ5_qp$wN_LA0C9D4f7K+IkC4?9*9h}fH^zEb@0_$F$~mTq z!eCN^cZ`9s5|diD_XqHsK{}AVG;+ zD~M2Zn3d_Xdm73^?5UzWM2snrrW-aCy3SpTEd&1mFbuCrZczP#6gVi~VmK6Wo8CV0 z{uS|&E~C`|pS}?Co(Y20-X$+v@@m_1!mL?y_Trzys#^D$AobJHaWM&kBfCLJTQhcX z0ZKM5W8Rpz z^Yz}LEzEz2YKEi{?cT)92L9NzKa~{^ab#QhikWPmSC05?DM!xc^mlTZOh0L-DL&J) zZ|H%f?CUeZ5g&PEn0Hd0o9lz!HM?qV`2CLZ5I!xmuG5p@KZR-#1G^T{+i0yV{K&p; z;;KuveR+9Jt)ltGmg<$0@u?2HfiJg6g!yr7c&i5arFUIgpVr^6Wzi%HpAXV4I#1KB zGyN%nzwx|W9HsvN$}*kUpVXgTd`-nRn*J2t%D=q-036xw+TAfAG(#&ybs=)#6RGZ4T>lK03kwmYF}&W?bi!T2FQeM{U9&yTi0rnbNkE9z7qyf`GxJ8s*v2nzzC-)t( zJ0$-A{upO#ZXkc;HPq@q*#r-{aWCi>9x49-u3PvcRK5E4q~w&hYiTDblFG;*aWMn1 zR}CsZ)-BTW9q@LSR8j_V{U|+mH0+1mA)ljMDSuP9PAYEkdrNSDIc*f)Cwg5UZlWW0 z9_Z{7{{U6DPD)`AqUC#E|RFU%h=W0k+fGVW} z^~4{e-FPVmQUW(hl%2zH6WTG(7E?Ze)-<2iW4}nbPW?*X#VfPToH15p$1mAmpYMKQ zOac4WBfmXFwaJ!cp9G5PX`j7t(PKE^pZb-zit$;;9=R_|J+wGS zLM!RKf76Mhv!B64Z*9C#V&#S17NFK`ZcmPtl*ifqczbQ;@KK4NI}|!Ccbt>m@{xx^6cFGkQJB;TPu~#;ik>;SLJ9YsWhF&P)6AwF z^wmu>fRk5tT6Hcqk9v~`VD&EgrNS@8v$@-iGig0{Ed!_eQ76-$0Som(3HPMDDXV`yj0s-=PZVpC-=f* zjv`-+VwhR~0FyY7Vr5aB(*kbQeMS3hquj6d$3o`M1}K#AKJADzhW?8;f_LkUO{$No ze{A#$Q2uth+;(+EJ(bWEFCw&vccM;SNQlZ~5EMlajl;^njZx{AAn6AALvHdcQ`Ih~y1 zH&1tVpXE_C*Xy@`bdhH5_JNW6pEGuKL{He;F!uI<;#6PdGM)*GUG>7|ih{3@;!Jjj zNgyriw@Cd7iND07@l$`2&ET2UzgiyTYEq=NhvM{UbMTLss&Zd@q7l8R4~k*#Is6k{ z{{XB9bt+Wpzn3VtABZB@Ndwqa^iBRP4~pk!?mQALSJlV6_t2omQEQ}8p5IE>PpA6I zR6KUX38OJNHAStTR<8EicsPz#W|)cWLM?PcbN>K21{wY@Y)V$=E&^v}A5>2KatB>k z{@h*G)Z3$0XsPY&Bmt(9r%@k#6CZ9_H*r+gXD*KwU$5Qj_kVp-{{S@ZLo2E-ABd*r z>mEP)4Us?T7K8Ey2*0|1?kXv}^QSo930?D7cNzc)caK7i3PL+@`S+2d`fOr+|g1M^S9R>AWCl3IHRxm z$}uPrjsPglv(Ji2a^YbkYqvD2T@krN{g9^No(r2~ zs~`m_B#3n?g)>nas(!Au<-b!fmsND-9TYL4=j6jC$>U1tq0Xx%rfRHH$S)z-Qhrleg(NtBpEcP!8QDVd1h zbrVLVNXollr<^yme&aa}Z6gifyt^R|{{VLNHaT`%N}owOSO}N4a3tBQSjMghh^(%p zGz>oFa4>(O{{Yl8JM;xN0o3DdRDK%E<;K@+Z6mDI6!VS`^&*nvyzCkIs;;g3TOlsZ zr?=3nwa4G?;`dj6OHOhJDB;|8+JZ{ir#B1p!^~}I6lkBhW-6ugHra#V1eJGd<(=dH zatGh|E8)kV?$q!N0;%eGW1jM?$!H(Pw%O^e)*3~5{A)#9^8Wx#Mt1Ye+lOezfV6i0 zTHCOEZYR^;t&O|scl3Jgj)fNvLBa7y*FZx505#9DGjv!|WOoLx^~*Z zXYsm|?2RrX%Xc;4%H^5M`G#K3SlYjw{{TAMowxGWA~#a6Qlh%~b*)pOa?YXL)9%qm4XhvldLD)V>LN&~K~$&|c}^piMJz4VBCT^L z+ZB&>RhtkK>jDEprbV6wNh%!=Ut}^_6tJ2VA$Ivw@_h5gFK(2sNJu2<@wDPUY)RX4 z5s+aXC_e9xn-eJYe(<&@nX4+;!?Jc#N}OG~qiT0_1+zJMX&&F)4kFl*;J#w*cq?=P z@mMWUbF8gtntF$nw#V2; zR)l{f_iasQsHoM7YU-L}sncIkC{z*4K$`oht5T8bzMNJYk{136+`c7}Ho2CPOdb0iv3VHU@B3Vdv|F}E2q;}xHdPc$ytu%EhPLdQ(l^yhp;UGewcqjAkAyHr@2NK zzQ+5TuqDO4#x-{ovG~_S?KM~;N1$nQm)RU4(GoJO8F1r0tQ={wP53oYs=NH0Vl!>Yr>cI85e7(84!N z0WT9MbwX9xVG7e`qs9?Af}sNC7Q{@VX)viA(?u`md3J7a{x!+*8}-8+tD&I$s_5+n zQ|zI|Q7&z%X+hddiRvRk46iZhR$?e(Y@|E`REuEl)40r2VjRm`$!U^C<}P%tyht7% zqd~QO<{WY}w{nYi9?D)6_9@FB`ib7Dwl|sado^e}J6mq7TWj`^)9IJ|sGs%sH*kP| zXU2Y4d;JQZajy8dA0+gdx!h|o`j-dyR|-{z(tN#EH>32y5a(hhhec0u+E*>rx{p?> zeXw#jA7#xU%wILOUsArFgHZ@rcFquqGQ5?BP9D=}p`-vfQ z^ql#Cus_WTiksbD73MsK##MG&WmQrD&@Dg@^o(Y>tu1s4d%L<(D9dQ1y5(u8 zt=tH8$DU=xqyf`vL^~f|uiAyWe*VGxlP0(gf<6as-36 z;-mK;*%#v@BBD6Rj|3`n9D}N^D+Ksy{ShN?q&!q-8yAB0S)N367dIv4IA5)k#VE!_ z;G8&aXMaXVZf#eU)3ZO~x~Uzh>P%TS-Y$Xa}b#ySU3RY~_!(#>nm36i;=}scNU{-R_m^Q23PXbwQuJZV^u=RfS$bh?l?* zuqgJsCZ%w#NvB(+oePdFWBySwTRv13b8m?wbl-CD*(xb(X+OK+0-LpB+<8EopR4$j zEjox8&&;J3e5f%~yqO-5!r-Pb(RJ#pbiw-}9QrP7X+oVrMJM{lK@MwyLP{tUjq00y zmY&~4+TX9a6z-;JC2qRnloEs`2?VK1Bmf4a^o=d7g{+|5tluYATAhcvU5b7-x$t)$ zP~l;pTah^xrmkGaaz@4cjX(F4c62OH`>=PtBdW~0X&Q`nG{F^V@jG?rJ$HMzxg^+R`RO&xCphZe0%l1cc4GN`t}M0t#{^eSmt0G|^%!0#xQ+kllR^H*D3+*tth))`eG zhK8u(^QRX%I6yvU%k6<`pU4-#&rkS0@6sZZbl?|5I(+C}B%YI;MoL)YLw`Ft;Pk%u zW1CW>x3Xt^2mDIh#S9y8V@j8zKg!j`d4MyXOleqg8%z$fBj z7ut=`CPEIW_jTkdwy5{6q@_e^H+gFVwg9GV#WFwIy=5}~8qyD*;}d--D2}ReTe-UF zO(iDcLV?h3^YDmWWGbH=v`<i$?qaziAihvX?{6^= z=}quTGUOEPr0!m%B_UPB%dD1t@e#zaOZwydQ|j5>4M+f+nLxT;(R(;1^USYI{w=`3C76Q9V8r ztW6wlB}D0(IIS%Db$1=g-KcV<-tmEA_UxVBL8hj&-K`g7I#~(%k5RW9^vEG;Qjf5R z)MIK2^B(-OZNvlBRrJ@&9pv{bkMQO4-B-c*WzL%}w?9dDTGLXrw#98KnprHVJ4urC z_@x?0!_3MR;ubJ+%m~tje1cs2Vq)J_RKp8#igsLDX;{#dAu5tmNu1;~5t5q{&Smt4 zuX4=x0^?GC@`%%RmY>lI655oRog{in3~Yb}PjF~a%D&of7kby-qNQ7{I-{{ADmzDC7+1c! zq1$=Nmo2@TWOYkOMH;>-ZW}$maNeLQH(n27l&qbC-m5m6id8ASCo~lGKIn(6-YcJw zs8hZz^yQ>|)*Y&&T7l8PnF-JA5gV?W(1#Me=SA`7&vz7FG8zEjKp(${T95%`bspa+ z@{G%B>aV2xY*DJYsQ{T1DMbYqx~rm{q)%DLQ6M#5f(hvgxP|DSL-$7{E2vb}?$hNR zRnX}C*Cn|6c2Au_*E{Rac_yZU%IjL>61t~Jrco;q~4ywKSB|2oyTfBUr$by0Ip-xI9lkpurp6Df!=zw#oP_-0^ zsM>`7DgM~7wHl!=kMzp=@A@izS1EgA;wnB8<`lfVI@+}>8oZ)z8dNP?*4mPKCVIjB z!PyMS#vDFiusA<3-G;eyPOT+v=r*`x$8vsI zU)yTXNM6^4^N$m9Kyd)8Gn-TKZ!6(1x?Z{s{;HGf6bbpi`7B1HyOV7;ojOi=TLP2%Hf^^UwAY$sHTGU170{Zln zgb6H)c7l^!9YEB0L^7h|dM^8_EG$Y=@raAGPH5Fcd1Thxgq=JGlx4F`(QL4%RSr}i zjaqohJnG}-TbPE5T;sOFS}q=^`eRvz&;^~%Ia<6n_g87EYO0Y+ZRQN-lOrLnk+-y; zl+|J~7UD+gwRg5|TDaa?`74cLo^7;^yN{ld{V}}6+;sx+-r%(?c8ROp&muPM!8Xb# zfh#FWqRVcnq>x6lNfCEq-eE$q zS;&BWM(gO?vkvlmfok<)#HccN?vzqW9WO0T6rgk(&TuAAw{2u?UIHH(-B-5mfgh+z}+@u zNd1BT0Q*N)fa_E7kwKhOUHNG?o2K4)`2o}4#Rr@=N8>|r;jXe)YXL_%q>r*ox`|w-Ab|c zM@OB@TW$#oDLZ2gb@I4XQa(lc0CuyKdet0zo8NG%mCUx;t8U=Ho}y`~NmRYcgP`*g zaDS-O4ab%65m=x1sxhAb0K1{fROR=0=bS9}hSNVrmgPlJN~HnN6|$v?{gH78m{#O` zK&m;sx4dciO;tM0euiEA;)Uf~aT_*d!+NE5>$85M{m7|9U#>1-?@I^qqOF&+GI5`l zs;6dOPX70J_tjQM+lETh43oO9mW$`wAb8G&PZ@oYV%+VU_IOp(VsD}!6AGkRacxiQ70IwE_gA=4l^cObyh?D?`f&c|VDxqJbU~ zI>Uykt+)Yx^pNTaLZqK4W5$Sx=S5dWwGZLFvPZ1UK&NDZ7IxcMQ$Lx|^w<2b=CZ0@ zt5Q>zdcsIk$HG!4$K4YJ!a#|x(D){4nIDME!qGRXiW@?;CB4V(g;CWMU8N}kY1==1 zbr&FiEGZ*pLXbScBlQ?cDfz+`3|F>yH*;|Z%q_wDVRc9gQP!Qlwoj#ceIv$BEPmMP zl)T{_YUh?yoy=qv30L{kAM&RiQjnIE?671UiBnR=BNv98+(1!!ou%nN9RxR2E1KQZ zkyVvtc0~UGk806zNA-<5kfR~&R5(>2EG0JTIeo-UFX9%rWulZ2vHMIfIZyglJ5*2o zuu!NS*o_KD_IC)6u2<A(6>;>W!phl zH3R;fD20cDpXq(5o1DAMNsy=II`p7^@8)$Mwa9lxo(aE5d`c+iGV?LfJfCw(9aNj0 zM!)jox)Jy&`Uk|X6U;owsTK0A)E^;psn`BocS1h}-%fl=0bHrfC19hKF9BW>xmA1r z0Iv-YvYR4$2gIdTxs#hxfDbF)$~tLtsS^JHl@ans;Fq9$O6ksB<;Q{bxmNbQx`1v~ zTK@pbh2)RHCqVd=nC~U>WAh8->&%Z(ZgpSv;$DM16n{v3N=2&6^C!ZT{{SG}94Dw% z#;gATWOFnwlZfpbsM` zi>vBR80G{I=IN(U^j!5l(Z*t>ZJS2~3z;LkmGG%VY1UEQa+W(Lpbk$^CVfA!pXh-) zu2VM91;NT{fdME_yW)=Oxli;#1^mrcopvYRGYQpllj(x|a=VO<#3eypddGEKm+68o z?pbmG$jV+bC-uWz^ql|^HRYG3XqRSF)a@EWYMdek0OnVHDUhlu`vgVQQx*hW8D2n> z@YX34@sON%IbFyXE@bz&NDkZ_C&O8OsB5Ega4iTYFPBwLRn8DUcX8Gu0H-&RC#vyw zQIV-}Uh;ozJBnjVcp{EmE()1dOMrNd#t^4RD^Yd7TE2x!pNz>S9Or1J=Ll9&TI#e4 zOuDK>M0}~QkOX}!R>*E}ne`I>h*~CnKt@qRQoVN(<<=I7KB=g5+g^~F*TzDBsK-%v z)Pz6fiW;d3m#`R;Xh?nw6Neslqn0PwR-m3L|<7b5LOS ztAf&+D?{~)U*StEy6HaP*i_+sTy$Yp&kXu6{X#Ng&#S@fv7vICw+n?0IFa)nqL1i`={vg7yiu|Cweye4t$r*1 z@0WU4cNiH3*Am6)+>v!|+^Q;8jddGXVnmO81uuNs1!EUuU%PQdYViL6_i(cnse_#J zt+JJZL47vcYwsv{ou&5@KJgWEd4&}y(5`K~y0{-PS8I*`0NwZPGa3}{X8Be@oYXZU z>pw8fy@iqNrbhW6^P-x5`?xN&+qbNs`B*Bc?sE6i zkBR5(^snBj6-O@QS#5Z)$*)y;4bk-yyS2uuqT^7l-Dd4--C3Ws#rzU6qKyE16-%IV zzG6?eU)qnlR&t*jwE$$x9i>>AS znUIuLGy``g6XUc=D?evgKRIjEggh%w(#e)Q#O^yPqPJ85GLJtp73r;DKsfZ#DU&Xq z$c^r*dt;&~w*-f_0woZ}pcZwMOTeev(v ze~Qri7t0;Ic`paGV8Nzo1=iB$=johrw-GPuD{TJ&M10{u=N}MP*8Nu+5;Pzc!ao$1 z_8HNnGO()9&SzLJdRpqyM;&#e_@+#FEVKZ--b43XNX zVhEblLF=F}Jf#rUrK=6icDwzGxsp<*YU#-{@GbyiTT70d%|5gh^`MsB?LGxw`;Aj; zbxID8i1nPOIP{sW7`z%gS8G6uC2JCszT!mThUuziLgBbWu~AaNN}G7Lr2ZA8?KAhM zSaU9E#VhPsbuHC9t(op~Y9sQ@%Ij_^kp+v5TT&oMeg%k-%Uu*-O5X)6Si7$7ZyXK( z059`~ML-z-=^s2^3I71;l(`R#XoQx|HyuKpvA?Mwc=j}g>iI3KqL)unj`L`>ToA80 z?qLV?#No&(F;VdJ_fa%Ei0;=e?w5$}Tv5r`CN*kZb!8Q0K6hZXHEX)GXkQP8{td_} zaEUH{22op?v1Q~uhfiX%?fduRr|LG2_U*2onykybaMG-n%Dc;XQWr@|ei~Ql)R$C0 z&O)RQ*&b(*?BNjRu8Y<2-S5^syt=7Nhxy-;zf`Y@Uh-VN^GjjJ@%o&G_g}bC+Ni1Bsi}0O zB}&@cDN>ApnbI8qeZ#p}DpXf}J9cxtfC8~}9liG>UK@Ikgfd5(ckn4z$C{u1=GhnV z_T}LkHBtWnnqvm<3G+?bM|L&IX{LB0_6ve&ONQRp-dw2?iHB_7@oHq<@ryf1KJ_Vf z>u0!GZ<+56@^(G>UH2NQXY&g!Vp~$_T&QU-6dj6MOwvJ^Bx?m)K z<=62xe`E~nAW`hhI4bLs_KnImZ&TF9zImii`1GzN(eD7r{)id&EhQfgUBN+Djws0N zum1oV&s9NCgCsbnooz`_@>m1(7!me>Y^>wUf+%)J?D^L<;W8Es_U~?Oy*&?_Tg}LL zY^n7J=!lDFZp7%NBGOG<(dq0`y`#L|eX)jHy1j47T53UNu8U);1N&?(02BUfNfu7! zr(IMJz}d(&(M%Rks31vfWjtD=xBJ)VYr=^-Y7qNq1N~?ycPRr_m0K@nvB%+ZmnPgvhBafVtFalSyb+@4U< zlvc8bfj$KbAwd0+We!RQ>KqCRd^N;l;sr=8t>=;IvO|rZ-$@Ej*hg47mkoBxsn*Zp z7vfY124nAs85CTIo>@{;h2;&ENUqygKR@n_2|ns;Kb>QJH^QYE?ix(}kyJM;T+e>=Pjet%=-xZhoI4i3UiH<(6-m9uWHB~8;{xuSOrlS-ib)@{Dt2)IU zW!JSTOVV5QX>blk6qPCxn@ry<3V8}XGE=OP?}%-P>v&M8rEkK0WA-25qFR;fGYt@xK{`)PhEdLaLXm+9ZhWy<>Lqe1250PvE#E?t<($l- zKJCE^nNt1W#FESqY_uUEy;RWZ=AB>%$_lV#E+~_=Z4}KXbo?M^ObQ{E9gOctMxk@zDZctRRYu{-TT0mhd3Q;G+NGdB{M|?#Ib1yp&AS&N+E?L3+ zOL0bb$=B82Gg=k@0PQJ?(lEE$p&LK(E~p6?HIfHG*RM@&jpe1Yxui8d3(xXce2x4@ zD|YgSIRE@P*nnjw9HzANZ!>r}~ny zp4SA&x$}yzN?W+^ZudtN=@Lurx<;q_Aj996bGC@0qU?Ah(f2I-4N_Y39%QUa{{SoP z=@f}vy(K+MQ0Y&;Nf0CDy>R$qQnwR?i2PR-_5~Yee5=m*D=pMyJb7xzsUAC8s&d=` z-k?Ytu5@UhA9i2Jv?{mF?wRY&4Wl8jD>m zJ4s3$aY;&CNB|HEt#p)!NZjRHd`i``u zk?cB0uSb{DW%FIfX2{c~p*`vOQyjQU(Z4q%)@qUR)^TALL8S#RZcN?@NBO#q@&!HV zm#5Hrf?{y?&I*jksz$P*cxslANp@aBf6)-tPt&H$ zkDRE~m1(E%LOWE5va22%m6@KJ${f0jwrgKPf`W;ch$bYAroK98#;uqc(n|pw@o!HTB6Sshlq|7}_N!xe4hf_{Z<-9aY~* zl$+#NP(Tm$mqLH0C2b0fTRAMV$#1nK3Xig9`eD4o!EwgCqsNqLPNuNP^w<2c6+&Oq z+?%<^RZXAZnSKX)cjhg5RRu*W&CjCL*)6C~#VP>BZcTyFm80#hrM1&sI2C|bJYw%} zxK`R9rE$v3W2nILlY(vz5%U!a1aoC zx#KNciOSrMZ+nZ?Y^6$GdZLvLr5j4WZ~}hFvl9+0&8_jzY`Uz8xZoEXrAv;A0_No? zl?0t;r~+rUt9NZTD`=H1oH2Z?QUyJsYVsilNAHwG2u2yolI;DtWi?Kurqp!;LPtmy z@`jYozKUbV`(4T^zyjatDN-7w{{TRT3x?4`>586luF&=K%9iArRI_(Xi}Dkj7VT>Q%tw6`_7!T{8RwixK#p$uq9m7LXsS&Bibr%^_L z?n#urksDzrbYxd-XkX0FCaHGkRJVvKDw!Jek1@LViu#7pfx9L-wp5i&Hca)LpgxMU z7Xf;kC1UzEeWFGq=#>}M1Tt~@)et=u4{V`&B|qu{Hk@L&LRwUhvOA(vOz33e6*C)< zuhSC80jg=88%`?m)mxMA2l`@ekxlx7x=t?gPmkRT(kTq-a^GbvfxH>`X$4!x8iAgA zry~^WX+dFo510HGf)=O-9tD?ivHrNYg_8aI=>TEeYDo06fQ;P?-B-#t)fQTsn+bw~d|IkJBK6 z3J^8+N26lT^w)-W6kyC#@iq1K`jK60xIb^HL7EU0y24 zVEy^`Q^xl7+N@l!T)S~VZMtXHUMWnaqK>0Ttd}2d=Tx*+Tw1>3F+lT0oPEmY8OJMR zHTiC3b$0P~s9Kd%RN|DWl?mxGWf=VIRXS`JuJ+3s(Y;Aja=FPZX#7263oDnS!rG>- zOQUw3JYWQLVu>)j@lIWQAu5S%Xk@&S2w&jv5yd-|7N}ZFYXE3`A}L#1+=AmvZxhF)1r=fzP zCGyona0F0MX)hdEO}f)a$EC5=p)xx3`y*>%9Rl(E%>2(Q=!fZ>!SzqmjMnVIKv@Qv zL{3Qqs0YkNPIm0DQ8x!@?z8_ut7T4-H;nu5!LD%!B3l?i!B!zYovs>asen1HD5 z!@KTl<7@ie?mt~Y)7 zElmv)`JLPKFj5d2xzW?s1qsVju_XQRQ2a%t$5w@DCDuaGoXvSOT6JXq0CfjOFK-Y(FEG($~#tNB0<2>%hLdeTe4iWfl2F#}s|=2t~%& z;6i8Sws(ll-1Z&Yep0m7{%2T7+C8-U6}85@>FisUSVL5tuZi9g_|}rS%48~DGfi4V z0DFHY7S&ev{lkN}8-%HK;XcE;oszoj3jxegR5W?IOFPO`{6d)lQ4?;mu%0S$VTPN- zRRHDx07oCEMsVBDtB0AfIQToBv|TH0zyf9@npZ-9L`>fA$w$3ZQuYomO{uD^xkvlF zeU{$ThNOewkj&}3d?kUl1$RLM?7I*c{6C$CYtQF)}RN1Uu7fo z#l#)+WZ|hQl3y>f3_1#`@00x_d-}!Hvs=JbmloOw_|z3Nl}R9ZZEzpa7A)@jC+y{2 zFKA?$yA^)F_BGv3Hl==d+xAaJ6FuZwYMlaJ)>5b?ox^JFoE2o43)XJJq07f_{H14F zg~yHWcICEC^u24XZc_gMJ+TE0y}8-J4E%k|cVYJ?HBJG2qgiUM#Z^qHY8odheSv(iySn5dtHc0mvUZZ?c)#W}hYZHxJ zDORLODL)a`L(YEalc>sWxz8tpGnrY9PpT5KcAlUZ~Bn_v> zNKSI6sPI9WR}^NT3HKQ8a+>Z^ycEhB6E7(b@<(%&hme z%dAe)LSusp85CNDDrG)1nf-8p6wXE?X{TyIAOJixF)uk?%EkNTR6h!|@aqiKHcnOh zoF!f=(xQ;0f;`7qUShgikbza5 zN|uC6*kPz%s^nN*>NgSYj!x>MUy`})`;2lR%mVP)p4570-e)N46vJ`?3-Xop^oTw& z%bh4FtM;cvmFVd+5Iky}W#!cXZH@p=$@@A)s&1+UR^@9Xl1GGhAW@63y8OJ(UYEt? z;~mTtU4L3I&4#j}30i5?CFBW&B`;i~NyRHvq75-o^V(uL08u{Zqp83yuZg0SNKCrz zoG{UFzNYAAc)I%fk{Y60#GO(BhL;5Iu1-2$3d?J@3VSs{o|{8XD9rnWLiySPSneEl z3F&O@E0u0wDvJucR&M+Ot8Htu{{T^?L@vli(YW`b+XTe?r`%MRWBPpeEqx7(M+fB> zB`#YO-HPR}xRyiU^@dz-&RA4vWu{?M~A1mO?E%`YCEC11fu(y zVp!@=;+=h6SqI$BI)7XRCx6=T1z5MsYv&D36uDym0Cnp-s9S2t`;4<&TbL_F9j>;Z z{e{<26|VCJI_T7VURYcatHAX3s{xFEy9vbfh->+)xckLo!2T$=+9+B~{hu;jFbtky zxcse0N9Abz&p&tIr`&V>+m6hwLBG!V2QbKpm#7QY%21>K0I+GMKKKpY{{V7!XjUxp z%G7S+r%Zq78}#Y1`f9DgFBuBas+C)Qt&*kYmK54xXLqgZhMFP=mw4g1`_#>4`b>Q_ zFu&se0PVfwl?!P)yK+;Nr%h&lX~jf;Jo3k!d@3`gHNbX;WNX#@PBP zhOWCoW=T%p{dbfM+44_3ROa6i7lJQe+*i}5irQDJneo;+==Euj$Y8I%jSinN5bE^RBY@0=gu&o zOReEN30ZP=Ki$1uc4D0e>|v)2SwrQ=7dcf+=9GuzVy2n5`_Fv)TxwH=aP~7`N~Rd7 zex|m$NFEbT#(^R&S>^q^N5pEYr}G;9=SG3)>{T-={{VP(eVYc=Rg-saN=`IY=OFsJ_r;Z=(Ouk*OHP`f#ELq>Mv>tm^6jLL zzAagRv~T4ICuHwkyUJ?dUC#4gFlKa?oc{osvaE`lrMF39uOYg+hZF?BQWpNwI`~2M zxR?X9XjPY!+=kS=pS4;VW++=rQVDfw)k|0_R_`)YNtuhUrfAqK+vYAhom3axUELP^ zO}8nBq_iqaP!gQr$fE!#+IgI^vnR;vUR$$#`2O^w2e_>zj3HONlJLJ*a-8eaUm3+5 z_a>`bWd@0wi^Ap)sWkaaK)rHNE`$X0vRz7?LPDlN>jcF5C?}6aGtpKWie!R%fv36% zYv`-q>Lm(vjm2uzC!&1Gi3F+#3aUfO8C$Mp*&P?G3pDSR++_arl04xg%2KT-MWV)G z$0&U@Dgv+CR`ZAwN|ZK%&_-fc>s7Y4sAvTutf&;s=sy$!_t9B5WD!`Nf_~T?I*0`i zA1?@sME0VtxgboU7rKEH6Lm$cgopw|!QSpk_Q}7h%APphSG75fHKU_^8aLUm_)EwQV3;tK9Z5KOQ5>hK{&(ic8gqJTkCLZVw z`GHl__EXQwK~EYUX~8N0OVy52AN?W-*X)Z(dzRepsjfRV@NC+t2HTwBHy{fa>T&@5 zDq2!?_n5I6yK}KrcOAQS-P%%TAY5$K_(koiOREc7T2jzb;wE;P$O*+pa{ed4Kq|wx zF_W_75;UsOucy7XpBwEfAIK}v8dbr0E!wH|f&4mAQ0fq=o`9vf!5yw!9w4}#4Nt#R zH+{#|&xVd1quc;k&&GYf-?{Gy$})U~0z?8e=;HwuaY_faYK#ZOU*@@uzDeJm)B zB3(os+SZJY;E-xYinqRQWS+$nKflwpy zAcbiTcyUmT`-C|P$X2S{UZF^Cb6aitfJ?{WnTpBq<)*6EEX+rWOSEPDpMt)FWXbOM z`q}&pqNbq@iGS$aB_E;{t(}_^rXt=^s0B=xKJj~jvIMtLl(CSqj*Z-?Sa|;ctiWYo zrYHSQ>6%qn%Yd_attsqMvODw2?pAx1n5$BGR~sp zq3sDwIArBbIM_!7FwXMC$o_KTPnOjG07N<2IZ9{g@pvyrca~{8%f9&}e#l?1o5gdn zke&)v;QH4xo>#_(S5$7Tp_=CCn|K8!F0PfrnF&j%f@~ynixd}h6Eg00RUV;qrdP+Uuja6 zpRkJ>wksmduGb;|0BYJ4+bLJOXeKQg*$q3AsipHmPsql%?62*rZ`A!fXkE#LRW4mo zPmKiyB7KfzmzXKy{b@NOK=SIBJJZ8p^W z4PEZOi?wxbBus>*K&?kkfJTJiF}7{2l&ZrH<;&!y<%^itfcyf9Z$0Mrv4rA|%!vYq#8N?>Wc z$(wexD@Z(%vaj_dar?g&Yg@q;-CqH5?c0;9*K56HAG5moDGLhie+%N4YLSn8#mcU)N~?`pf}J5V z_&`EZl#g(LaXpo5k6{s_v)*G~Wn2uj0Bi=yPPdOOPKOfx0Nzjcnxv1qEe(TJ9C_D` z6Ds4y;inYkvG*zDJ`@6FKehvw&zEI;Z<*%MsKV(@ky#yEPHs?1kaA>xqZKj-w5<#+ zf=&n;9GdY+v@I?zqB^N6C-uXAsmWx;LU<`E$;em69SJqAC>a!jNc$ne?dqoM2@AFg zSGIChaY7AI4uong-2xs;C&7?JlxDcgu+xG&7-S(|D?QCgQm4D~>ng=OQCKM5O^h~n-? z$(K}yYAKnj*aqRWkJaN0mn8A*K_1bG>FdcnuU|z(q*Pea?7D;!4F1sqNt>Wgx9mY( z6(enNRMC;mIaTg=b_@Ol|=n3 zQi&hgCVyVzsF(rdm<-Xf|^{P7QWkaYTb!VrC=X4m|n0^ zKH`c~vi5_P)uvBUsjb7oF#iCl0RbfHX2-iNxTG{6Wz{H4K%GBGF(|ug4p~XrPuQOn26hf_!z=O|Ev~ZBqiCp~9pjNFiE+B2TKP6zm^**r z);KZ5r}b)+syU~PY^WqvQ>3PInMj6+UByKEfSDIw?{EuHQKzbGmimyS{@9tZCpJ^T zMOW{rju&T2(yHduv#ge%)2vC;Z4@qsL$FYWQ}s{8?f$Q$cWe}cB!$co{)vRETe(db z+lTBGTFt($l+A}xMRHM8w2(6WCIdHdg=HUYT}iU3nRnHXEV!xG=_=eINBCeVQie&D zj13j6J&8H_m1N4kqkNfoTd8Kf;Dt_03nNc(K9U<*)Q#z=k>)AreL74FW0Dy;d*L4wPTy@aQco>){UO9>g-8JeA`kyG7=vT2H zpsri)r6}t$G3N%_e8?NdXr=24%VoXa4@nJUd1DEp!e7a%lr5?*KTeXJ$|@hOE@3|s z&fJfIUd$Dei#0IVOp*cnqtYxJNW$>U+?AN8bwCmTd?WfG$GV!T*jrT8s?%_vp0EVc zS4D3EkFwiRZZs7M*))`rbl0bZQsf}#YS&^f3}sO4&W(1vi_NJ837G&#ndG7@Gz-~r zQwbOfj@@{!QWBq0`Xa2AbjDE?Q4=C^R3ag!tMue1uf95oK%y^Fq?AZ11)cQ@Sca-5 z%1{VjoMW=9T=s2LkbxcOm8$3~H5v;$CsE`jV2Zs0tJNBW&qJOseteT4k4%FRdOs z#w@%K!l-_sD4=fBMi%Mmq7kHGB(2QUTE>BEY)Lw=oxX78>YhmC9D^Wwr`D%=<))Ea z$xoz(kR#KrErTNgd0f|KNlS{ZR}6(KQl#LKCQ_eJVdb?%3AI&=n{yP?TPgr@M5NFD z0xR!YS{9oM!1t`G$+8D4?ln#Y=v02uIKte!;;Qx>bO13sxK6Fs`AbcqW$8zePI^ir6LkQ1WDy9&hkw{l%$NkS7;q>umya)hDny)M$IV}fZ3 z<3r1L#uSyad3l|`)pcX_7*^bncR{-Z)hlCN(ehS0wpVS;tEl}s5VoryY>9bnR7h_d zN)QT-xL3DgLbSL~)YG~QIm&lz+{JpQ^gjz`;_}3jG#|t&5`mosv-fC3h_6_F7oU2# zSTEP3y!=$Om$@NVmfJcrTFos$AZa32)ckzl88CJa{HUn&S$K|;9Tm32Ki*F6Lf9#h ze#Wdh8U(#U>nmkyk@Ib7m)#Y!_Faqi(OW~_*=7xOqP7@M`^+xYEu>|!(m)6JSay}n3uNr=&x1wZWO-XX)(6jcOzA)NUU8WP80FC zgo%Nnmz<)ae=jMha_dp%!>|{R*?6eAdP6GdOmFIb2MyxwsnAB!u2R! zMV&;S4IPv>=(0>36+3fSd3+JIU|p4El9nle-IV_T?;)!rg+QO|b%903NxEvKb8#{a zD1l#Sota$|{5}}N>ehM$QP2_hA}9Sy{CJ_iOU0iAmhaQ=vfGr(`+vqsbMdLV7Mq#> z0N7?4{^7oEfAS-`fYH|n6$s#^a5n6VFUc;7dPP#(m_o&iPI z&!Sh;*7Ouy;zuZF(9>OR4N87eaKUZwo6*GyHJ-Y(4C+^cR=mQbwfl$(Vs$orgTf2JpInAaRa z9#1Omsi8jf)3|@TwcCS$nhsCMsZ5T*q|iDC{{RyZY<^+MT|y21RJ3g3m{$+?aCnxb zE;(=J+r242ggHY-;2-LN6OS=S)0GbID%>`4QN7Fk+baT!{{S!Nm(&R*9;KHYD4(a~wee@Yyd9t1GKM$#HD4*CIG0gK0Gr=}bEUp0fN^R7CyKBg^ zNT%isa1!XMC4bk4{{Z9&{1<=Yo1YZgs{a6X%a>48S>>xshxdQNIzQ$_QQvJK#=)=b~vW}XZyF^$dCLkZ)0!|!as>K?IIoi zZEwXfy@z??ksSX3cL%z8otaIEz&an8SfAdL3I1(tr~d#3yzxmdf4iC8(mxHx#-Xf= zYiHB_0S`UvYKA?Hf9gn&f4i~XB2-EhX?lN!BC#p`Vjerr(}_$yk&r~y{_g((dXgoZ zeWh~nHzo(`j{g8QvkSk%atIUt?+<&r{{ZC{a0iLHQ~tvZ{{T6&^`@T5$RS7jydCdr zD@B)Hzxk_Q(Hwi`&(@RtCnMm!{{ZcHyVxd1&t{+h0A*PIh+p%YMBng?g5-a@&))({ zK(X2fy6s9w>6AIW@nn2ge}m+F5u|^+&ED`JuE}R0PfyLM2kS8J^Lrl>xOP56!FcEU zy`Ap^BxAOc2SP6Nn!kT0z+PKLzjq0BgbCp_w+zK%egZX-}utx-f2+vE%tWLfj3%z=!buu*!t6d!7^S6)lL5Hzjmc5HFXw7m8`(Hp?{?) zh93N~8x-N#c@*ZP{{VMWx%H_&z4paS2-F(Wr`*g&d-~VKPCE+uIqARM$L<|vCZ|2I zQ=s+#01&l!dm0eW{DW)g68`|>yVSQ${_W>8gp&UNDcM_5{nYx3{{W&QKm3bor5;<} zc%_Pe_iedB$$ZB!+Nu2i0K>T9{RBdP8su&r$0@V>-j$sSKk}S`{{Zr2d>V`%R7?En*F*m8 z76G^}x4BEALH)+;lxjbEoDl=O&7skXR{2QoFFv1Qb<+O;yH~^EW2EzyT@51K+o@s(u<4B ziy^vy+rEx{fZLLkx59MwB|pCN&bUv%SQ<|BzC2Ma>`ZvzpVrT%mtuCQ8h+laPLzpF z+FHctto!s}%{Ka#7Qy31x zI_Wz*?HbV}vA`HxTL6-kWVW3Ky6!=UPG0@oN_XfOp|}ZS{fl;=kNj!lY#Pb9GfXEr(zMP7ldgBg6MVhZbZV;-fNRBR`g@4!gT9 z&@ooXdzi5MYEjh;*HCFAE{%Jk?Zadr{K<~a;-k``M|Y2@mt&avRw}vn%)d*jDU`Rm zd3tGrzr->RJ1Ae@IEcpKM0bCzk74$Zu+J$ubM*R(dMC`!vv^DwA7EcUaH^&AHyY2C zLpcxEld~&8bMn!7z9}6N+Z4@i{{Y;t2(X9Ci;^`oLCxm9qeg*OT#xH9vBrVJ1h!HXMsM{IPi&xl!DC$k1rk6#?YG?G~o^Vb_!o>w%{{Y@g=Eu}iYvT(Iy*kQbq3vg;rNIR^%bRxYRb#x^ zy>ZY{L>JHZihHuJ-Yt|iTPW9QYTlO!{{Y;pR2vsyS@A-<%-oBVEs_0s``Nyd+gy%p z=^*JzK|9vS(vhjtai#zOqjI%D3fzRsQpH0_eDN<^3p8GfP!sn0?G8Fmzq9 zXx*BDle%&e4PI4e$-BkwCoJACeASTFw!iNhUE;KYI?F|fIk=b^vq!mA$&k5zrN`c( zYo97+H$vgKW%c#TQJR$XTkcGsJ<2gZ(#8&e1m2_Hxjyv5tI2LPD=Kp0w^W}JqG$KS zttB_4)P+}je`y8sK-!*gI23bW1ffOw^@#_yKe}{?>ZJN)<+I+M1tCrq0(I6O z)F}j9TLR+fnlYw^!hlx$tU!*d{knpf4aLi&I*>wDV|M7Bv9O5PJ0K z7txTg^00!dFLL)(xrzqRW>mTL=_J9TG0Rof&-mz8Ro*TtQ;sL7CtYJZJjS$JYd8Xh zGOESyl21ZrpDzfYx(i)sX`+u)aBwJffZF;iLmGbL*A;F_D(eMDA_n1<3dG}80p<_^ z8+z`EU5fA;F zPp0eIJVPnDSt{>zRtma{B?v03xdv1jYz;{(APqf`c!}<1K+y*#N(M{FTse~o)rqKh zdBt?cRdmgfVxuiIfgI&OB@ioj@oUq<8P-l~5_`%dOhD6W5plOvCOlMfOD506il!AK z{g*<&WJ8EN_e~zO?1|;IdG!^4ELTQtnMGp-&6eD~w1SV_ZmE$lK>pCuJ+2ZXaU2Tp zJJ?LI1I4HARI`=ne<$S2@)Dh>dDJ}8e8SKV>yHu6Tl8{A*1fm3CFd`i;S@m1> zlh>#oBp95sp%WC8brNbxN|K?Zzhp$zVMl5a>Lo8E>VFZ3%@-8}Zl;UI74RuNPk@P` zKMF}ktAI#c&xWwP+NC2!aQimxFOvJK&T<-j657p8uZ*dw?m1n`qM^EG7*$)VW{s`O zoYwP=uV|MwBYLzP)2y_t_ab&phivk0Q_XnTgIVw$&aruR7G)XIMGUx!jMp01p(+^`AoJ9V54 zY@#Z~x${~I{pz9Rd{~T9?_FfOxkXu9M6!onqJ6rmWf7^^>I9S^Ic(!s%*jYac1*W0 zTNB-By8bVB*#SEj5#WiBFEeN8)l*=UGN7$=oTehC_p%Kt9B8jG{$a81qnH+^4nc2+ewVG?eQ!qnl3CS^ z*wTdwimJ+oDBe;4;y@k0LbRF?RI~1m$iL{YW-9ASe&DI?Y(BTRx~=(1uh=zEpJJA( zjB|i5xj=A-atgbfl=Cu3Vu_EJDjNR)Sq3BQa9fKmKEJs?%s2Dpn$g0aipczbH@{;U zJDE+Ir_i`M>#KOEZc5Vf%GQ++5N94cX_%G^gCh>){pEX2-XbaO#81=!KIM1rJ;LP} zitV&{cOO(yJ6T6dR?~q2k=0O+M8F53Rj?=H6;T9~; zz<8>McGb*hR+Q*w<%(QWI!mQ9p+n)5i9e{pV)7@Q3XAC_Zs4w0X13Kg{1;nr8J~pD z^+E@;#YE=G;E`%@_7y#;6C=VTcP$q(ayTaKZUm(Rw9PZnXdrnb0T3oBJ-cpMV^UXX zyVO!89UDWd0)F6NH$c*ihV8&7ZMT>?UdQ|=Dq6X+lP1#Zb0iNn2|zEf8>6Gi_@}Ke za@Qtg-S-Pwo{OlZUg`UGtbW*I?24W{u;87veD}wfLYC?(oU}T=sHts815fKpP#mz3 z6palj-%EeP?|W52b)~OL#;HrFC+?g?2Ad~Vg(lr_oJ#W2g=LzasS2Em+d}>QVj~#v zPjrspR5^a_=Z&s@qLq%dOrt;IuvF2X`X(7AvmLlZMU$Dk6%Jl|Q^(|`TJA5=D4&HZ zlY?Klz>dboQE}#_;#6^S?Uy2=_^tJ9+yZ7oWTYsMEoTG25)tgpX{v@Vcny7`wdxy# z1)8=DoM?PN5+~seII%=zSwTdEacBC-f$l%i5sYnkDTYm`Pvt7Kf}#i-cp1ljoS6Kh zSK*6XK$g=Z(?~;LQ$JK<4C41GOtUnep9zNyYn1;0sI@yV=5E-=L1@3VO0t_}LvAZ* z)PQ86_JU$aEnLS{2X!lEXDVxC9-8udK6i!1%HPj)}3tZm)>5EYW_ z!;!4KZ`{WfUb2!73z1g4i}DL=Qc~HmWpwJW)UA7g_*pe6eE9qjE#MrWqL|$X^wBXn zBaFE}h`wcpKyX3fV?Ag$9E<@gJM$SJ-Fr%Yh9GFPyKC)QswT{L!#}=gSkV$yscVvVp^r` zo=V$YmptL|4Ym>b$%_cPX~%MdlfDP#U`zDfwc`mAe)CrGGMO$VBlHL({B4{{u9Lo| zI{-e!?X`CA$AQEl)Gn!hCC5}|V5oa;4l1^8^}Hi`Dc>0FvjW(co5vYa03Q;*DgE)F zw(Y1XS}djCc;1t#DqObk)(vb4OE!iXQgxCLn6rlq3pgq%yMpFJxlf8LUml@EkxJSS zksy!+8y&Pmg07b(KZ#1eF=e3Ikd~TA{oSPH9$~m4Nmfi^@ZuZYVR=)R#9Jfv!A!qpYNI&U0I}cd ze?e7l<^DLJyj3|zK=qMMCY^KG%KmCgb8O`@2ov%V=l$_DWNl3X_TMVlMWm&It^|Jd zn|0JShb`CDTCOUuUAJMTcVR$U6n-Nk9(i{g9TSTUNM4-;(>8W>1p|{86|}p3$Y^bo z+A0*UQC>+(N{{M{ZWag2b{ckCe7(?u*t~F7pS9jH#rYqc=`j4NoYdHvX0i0KIVJZg z3JOra?3tOzs&=M2Iq!KNuwH53ncIs@M_MCCl0N#a##3~*=4q&CsavC?plu9M(6WS~ zwwi2hN>Y@701@;AY!S1RXae}UM>=Us-fB*$wNAx_Za_2XtPL!RRvHx3tD$AmPS8|5 zM~t9n8k9&hNh`L1H+pmS=_3#qqgx>gJj}6bTB>7EmZY?eM1-I_xB*&RP3b0z2zJuS zR6CJrC-NY z0-?nzf{;$B2{VD;d}^(t#t=1MnZef4ONpi?T>%i*f8C$;(TR)g*x1YCUFUr`;B8$(2(!J*R?lQsY+_B$TqPe@ER9ZcWufn>Gz7 zofJHIeyiFTrE$+95@3(6C0D*CH-dg!I6Ex3$Tw_xL3;iJ6MaXmKxSarDF)ZthI4aT zeLGshPl-vHoGHAj9!sz|DQ3sm##>dfZ&RrN%Xh=9KyQHE4fQ2V7JkBRNh}(gWhp*C z4_~}OeyCR)w|FUn%h;70AP4QGY185;>-WUQ`&5PHN;baBG3s54Rjsf`Qc{?NUJ!ID zIVC58k*(dVV>a!6RhH8!sU-qK#w7ZRC`+82jPTwIYAG%Tfk^U50u`+#LS#a!)yEd8 z?sbHe_0==e-wO6VsCgU`C1!QTwqT_S%5CYMvrI`F2^yfD@)HL4+@~Me8(BqdI*UWW zNFTNgV*&`cs5>O@n+~@&;%V!cK}_VW>FMm$M+j1vxT@8deMvF84^SrV(c3wMsWTsO zhn`S(Ol_pE*eZ>i{Zc5>r;$r?Dg&WQf`6hD>Y{3%KHj*KQmZ~m^+~$adV-br9UASK z*G)Ra4$DT(6`Wn2GxtzFL-iWX?qx!q#UvhtsbA9&yExFS;_TN`RR>?Y&*Yn`mPOUZ zfjSA0Vk4ba&~|)$LWr*Y$MO}(xQ8#V0O2K4aX^{CPtUv+YZqz41xk;qxhpHWDEjwj z-rRZ>sLp?ELUR(7doVCFf&stgTd%~}dCk(-a*naxX{1eit)kwEVXO}- zq$#$U$a9>vVlbX4$(1EI(vdEPQNqGrDg)hca7lH1e_wA(mg=+}Wz*J0se8;L!71G^i>kK0!=ecG_5E?e2lyxpo5ZexxVTA?{V_XiTvNWOfp}?dZXbmv z9C7bao55Zs@15okUzt1sjNGOxZc(Pq_7nTeExKlr25EnGTbcpgoK)0C?C zB73x#g#6-X>J$;Pl{Q^#)FgF^w~7^cY~+}_a5>#o0V*aB znMS!M?}9u`WU~Qmv4=pSO0g#h@;xI9IQW*nEtH{z$ti4kpYleh7AVlOnT^$+#Th|% zpLU{YnD>zzt07f@$-K3ssaufotG&M0L0-xV?X@UO+)sgx8{u~<#9CfP`3}0PQ`cGV3!MoID@}i0=!q4Hwpz((ksfoKsot;*a4_2A#ztpc?7zhh}c+9Rkeu z{gq_qD)aZZO_^r)=6HXNikqsbZdb~r4aq}N048I^j-m#@O&mv=${yxq^BzdXiyJcD z$a4%niQ0>$dbYNSnKn|C`1RH|Fy&*A&@3F-xuPEsqN!KzHlnn+`!80NDUzh?;UnyW zD^Q_{q?JWE!x3h&Z?KBF0;vg~4Pv73^K7>md3jA$BdOt*+<8lOy;CIZ1Heu*@?%Lg zD|sGtuu_W9a3&{Wz^%E81C5*ZK}4#1U2CKO%A`m$JpnN#y_jnaH(BhyLP=$9N5T~t z&$yG4al5ns0G<4u&2O!1`N~?9Eh;KIrb47dWimL~1FA~&+(uU2@qp1q7`tT2Xvy_0 zi&#%mk`?XnF<$F;g?!tn$|!0pSkd}NTlb}7_C#M3N{~1dVHcgCB&?=h8i;9?!6!u@ zVtj^7#!B!=lhYmw)1*J@T)4oEGmzAE4w2?$K#pz6DMh$P##vB> z0;h_l;383SLW)aaM0u>vpb$fZOQ4((F3_gQs(Im#?74@4uD6@N6z0$3l^HE6XtG9{ z4K2FbR23;LxR=ne@Rbq>krV3)E!ik@Wv{h2D!HQPbERD3+l zD{hJz`1Wl@`CO*8kOgajAnWlEKSU5Nyi{((0-V*?PW0>Td`)7%r_DlJy!pKhVF#9!ie2ZEWM*OO`a zl&@?50C;P>I#$psZgwk@)75LVw32=PnuH^}GRUJ_-x(k`PdY#MjeDVFq}5q5nvtZz zx4M(3`L75S-IHchzt6HfQ=*Ukm zsmv+>RPi=6?%8fBa=8=#0OFcL{{Vqnc$F{yNWZ4Zev|#>{OP$0-V(7t;C5HmC-sFw zJFsm}<|(7h>(=hn=%oJud2;&T2BU(lDED0Izv>tt`~n~5Dc|`j@A*@ipZAo2LRC?~ zlxBTOZ!XL9w;X)Gi}{q_`8MPEQoBFzD&v|LI2E!FSzcB8Cd>;bU^9c1gUq?Vl^h@M zD`Gzv{{T1{%fs`eiT&+D4=$QEa+Ci6C(Hbc#(&;h!NDQ^-rr8WQGI9+ytNDe0FPVz z>Nk0tDg3ETpZAtH)Ho9FZLRS7loOJ&H?{{ZF+LFU7M>7P%v8z1i<@D|cf2jl>c0dln;)VS~b zZvO!034ij#f9ao3wH8nN$ea_nkM>za>7P%v43GDZ_z_78R|j9x5B~rx?Z33gf8%L? zm@fYS%dwq0XVdLQ2m8ky0CoQWVtEd~{Ia!w)`=|?Zni0}MsFY^Sa&BlKrNH6~YypzQ!GHZhCkC;{B zm-aUkA1xj~FhhUk#_(ZCum1qNsevbV%kV3zBd4i(XaoMEMkN0L#=rWL{{Zs|J25w^ zzusH65}`}Ll#P$vE?(ZB+qRw+zFH10Lc7iR#?hwrPy5R^bszAY7imXX6{T8DM;Kg4`=iCg96w-Qut^G_glXbLv2{{VRFxu*h?PWC@UR8G*F zLuw!&YTYqAI|j?vP|x!lE9y=9ulI<1uB!kuf3#8)sBt!$QTOg93uj?j4b*P$?5Wy` zxp4aW_k&iF>NzH!k+q2+F>AMNpItXJVo}((Al8US_ug3kWX7-h>UW!16k6tLroz7n zZjRW1KI&+cc1@4+s2$#UZZAyg@2yvQ)y{I>{{ZGNA?@tz5y1}b?aJ|b zVch<%z3Nw?&Ajn{Z7S=wtSA0r1Qj1_*pJGfcX4FT=PEP2eN8*SyM?N^p7K==x?O5f z?ES?EG+=C-`9h2JjDxp%Q8nwn-sP?(Vy~L=H@E&EtHo&sek6L4BXM@4W`!Ts-P2c} zdW^0e*>hcf^w#n{%CHc37iyN_Gw}Gyf*E4~8Z;({Tp!F+HnX2|>cUl1lGMCO)TzRw z`eIhW7#aYeeYAw`QailGk-TQ@`PHcCM&dtEiMbj)6fU-L;*uZaO3H#u)$S;(@e-q~ zB|=8nYLeHQDMdwfOPT%PVPa~O+KM7e&D)x{y0W9!;yJ2&Qb^`-@Qlm z!x9Tdq~+$XZZlrf;wFElIK4bt4+KZ~(dDRu=}f({5WLzh`dTiPmOiAP<9sskKV%_z zP8P24MAee502NfoAKogIah<9`XnYYja~d`#UAQ)nIj#~**udRR+;oKK6MDCmF^UkQ zQ+{CpbtP#U{qPs+*6iG)p}|}`W~P11xL!M;NmJPqYf$~v5~HWFGl8b=i%7*wg0h3L z@1#03rWMb-3X}SNtySmQA;;|Su|;?ySDPm^~d5AS7Yhx!`%Ea#~sGi&K8>^*#2(yMK&jzZ|C+i5<&~zX6}R1F9*TAz7|c zjQ4$z=DLbR@3mJQ$P?0HaXMqb;;eTVvu?QF?Z2DTeB}#npubDFreG**H2FmEimF@N zg3(3vbBT8U0L0gfS0B7vC@N{`tLd0^qUXwe(lRWbnso73w#*%$}co69F|sTr^Qx97d%|^rmGm z(HGHdve!f@l8wFlb(*|>*gb<)Ugv_W74c0a zDrD;-AV->&WC8JYlG!#YSr(TdW=R{0lI(XL8aK2`i%6nz5 zrteU|cA<21lPgT?8jt;BKAtv3LY`=Tz^)se$1PFXlvwA^lXDLMBKNhLn6KGuK$+XrLmYF4I+% zXb6}SDu*a}5@l`WA!l=>9d!1}JD3g98)8aBRn4_1g}u2dob~X8l5j;7tcs4RshsV} zS>3{h(Z#iSEk#cpW0fx4@!VNKua~8*stQZ4q=W^MprfHt0%kx?JQu%iCF_P0@QrEf zUj5j%(S1Kq4~XesQqzkg^!!`eMYi0entO&jht||xsrsl_T*H+IPhoOV>6TW;(+xwv zD)L*qkF4$;HZIfNu?B*X+RCbGMo?DuQ;2P}4;3tgi118&9P$SG*U?uK1~9nn1y-zz zL^*7<(%edGT%e_4fVUDBWYa+)d_q0$+R>sg3i;N1w+Rgo6wJEd_>IUb=IpfY9|iho z2lN3QE;1L25R10U&mN)2-lOW?6v1l_=IGp`S@CJY2i+CV?aO1qT|?d1P2*J5e}fpO zTGeUEs_4l437o+DQbCHt+hM2SaSF|y+UI=iY5Nj)`#t1~Nu8@Wc|kvfdXA^3u$2fM z*n5`j^gzy8JLZfbo37`ZGLz`uet9VT2vI`kFL)4*x4&uJ6yb4;e*zKa`J0(-YT0qK zF<6BDAX^~)=~4Yqg8uQE5T-cnd-in_n_b7=TQa+0o+7?K>g=woJz;OOOc2jZtAHab4cAP zQFERcrx2T!EnTB_ju)wx0N2a|4Sh!HUVtZcT{&Ya!z=UZ+MI6g-O}Ay#X6N#lO!eL z2vS7l_+%lDnUzQVEWD|;Z&j|R`?rw2zld%VhI6#0mb`A4o2I4-3Q`8~`(i-GsGU-P z{IvyssYp^!;S%wQew-+IiWt(Tuczj9@?J?MDDm@;*eQ9qQ5vw!|&(7g?SmP4tp@Al<8M%|AFk z)hA!NAN4HpUG$Q8E)7LIzwwDa&B7DvS>m1aQg|RfVW4O8fh2U2fc>!ZD?C#D6Z{jt z&6?e91tyy6WF!M>SW*c0P8aG8bSb|=%4!vd!TXNHvC5h;oN%qPlI`rNCbpF~I_q22 z2p_s#rX;3*aI$p>(aJuQ19q$Njr~GCoc5|}Z_U1wR{O;v{GmB06a@me$`jCZh(FU1>3A1b6$qM)F38VV-5 zd*Pqp(*9?XobL7w+A&XW@JSW_0AF5AyCAb-;M@Yj(&r?%W<$vX-6Tbf9iSR&TvcqH z>f`xBt{j*39nCp}CaV7cVzgQ+LW+R3TbiW?Vp{biMxN6Q$VQbahU+&Z$5BNS+c~b~ zb*g5e>XkGCr?g2EGoHI-KSWp@#+_-v zCOw4d7{31iKhkMlujLe@`i$eD{lALIO9H8M**IiP*DJGD|fW=B{S z=H-;#Tw<&y=Nc-*Uty?CL*iyzj?~Nc#y0Z!1ETet8xloIN>!cWWvBzBz!6D4MQAOt z$y$oS=~nKu@sw3fkkwkjjv+hfu&RoZlAuaYO(L@6BsN#JxKi!*Pg*WHb>yV_WbW~l zhv|yh^0e5iR^dV5os_&zrzJK(K5~GLWcZ`T5xxn-NyBS%PnRkO*{|6Hzc6dMmK+!d zP@Z=F2~(v7RAkRXqzLS>@m5jb+IT4v)xb$Yn_HIs+mFC>(q70CY{^Fs-)9i4zZdYJ zwpX_EcJ)nONdEvNRZo?1;;pT)EecP|coOw~U-)Yi)d@2RlSGN1vMMFX9%XA59fNfq z9?G-I^-h}@e3f(xXo<}E$_LlfKr2v!vS}(F)sy6p=Wo zPTSgM9cu|ft@Pt>L9sCNc2q_R?ww6=t_^3{TxEm=iT)G_chorSqkXv9b5m*Kn>aJ{ z5$O+)R0xfxM;($LK%;zvk@&{W94e`%0Xh&=k<-QqWu%2O>f!-c%)`2#Hn1hc{%cRL ze&|>oJu-8%IZ=TQ~n$tv!0s8gjjbS&{Ow3ZP+F6_7zmxcfVMr zB#nBBM57NyTzjbaCbjF0DQ!)i&y~2HQYRs#MJ6yc8*kh%f3Y!Jr)q|zZZk>_qpXbJ z2SyrwO7Kcv)i>f9eWKVK`P3u^^_+pz?2D^wwt%vEEM$*%ic&nmj8d=@Ov*?dWIxFV z{-YYse~!*dYF)XOk-)E%rT~$mz408mFcJ3Mrs~wFe)XGHT$H~}0hZU{*Y(aRX1%Fe z+bk<%RvPESqM<2NjHRfQ^f9}CZU=6>*`4k<6v?RJbfsz~sZZEt5uVs-MK``3wNYg8 z6KHZ?j_4!&U8&#ZZX7cs(+Y2>bCFg0T|xktL!@^gQv&1;^a-7!UK$8PKoj|i zMU>|C9|Agvi=9duLg1(b?&}e!0E0^W&$=KjAt_51s-KptX=hZ=Q5H5+7Cj4f7c*4M z+pN1z($V-zK}>B~AaiQ4?-X?sm7=Ut*0Sl<2_T=o7TNr-suuinMn@HIu}tobbgfgZ zLUIx(!=!Aj@*zX|NZ(l7*U=MioI>Q%a$=O(^Le zRXcedMekOx5;PQ!rR_45Kx9O65`=A2GMt1)N@)nSP?=cs0ta}YMnI#CiCd;Tfz~p) zR%lkY9l=D^f8RzHR=2k7JiRVqR@2+S}=r!B4Qp$Vv#J@^<3OWG@GGJ@(JYSe7$*wXu-aBHu=r z&{q4SYC?Absf&DF%Nl4_Q@X5Yey!Dgu=b1HrwUs*zSnNVGFl3%r8bu2hngiS*)2dm zeIYW~^E18`O^(gB-Mp_9D@piv$&TLXsxoKl7_Yir$#L3}Ql$`~Fh0n|{k(XkSe@=kn=rf2O(tZuej6zehtT2&HE~kin^LNkMZc)btlcVpV<)?4+P^Qkq2!SH|4(C+I*y) zzp5cE2)N`Zlh~d->Q2iS_gfd#RT;`kVohy26S%;;mmR-r?2f9F40mY z*Z6Bcs6#-IrvVxfyS2%qHBr3L@-46Sh=0TrB#D(r*vcweGErIqI)wrvM#}J`b6QgE zK3q~K>yhl7P1SNkY!UWz5GA^zWb`x8M|BE==>k@#%NC^#u8@4E5&c0-eUM5i%bq$1 zG#=mb!5w7}&;|LtvET?UlRpW=dZkKiS7^)XQgnF8_(3hYq95q0jJ27*lQk&M!WwR> zxZ~=LFF1QZ6n*oQ;!`+EG3d)#wM>92pNN?eA6y0BK!*NvsTzSZ_Ct)VQ5!c%piltA+JFHS0AdEN2H7MIR%1IqHoI9+e z`Vfa#AXP3y=8T6?I9{>wUGxp`K{=efeic2_+F{I)o+*BScrV}1%Kr1$`!{`&Cr~B* zJTKqRO8)@8{W0A@ll1V7{{TEx?gUiy_QM@1Dj(DNiWp~g8cwT0;h>Ie#3hd&h5ml2 zl>TQ=Im5o7sJ~8MzuE;){{0`m7wQV{(~#qkDo6+Bj=mFxbnCxQT^#zO#;E=9_fC`a z_0RM3{{Z{+e)wlyFX`w($Ta|IR(|+!nJ4MXwDJq&%jPFPX^8%%HQ3SMg#Q3&6#`SK z_l)({NM2OGVY3R0E^)9vYJkHzY1N zvBiG=c&I=2r%!A#*F>)UEFi{m@SL9_eRYKTrzt;AZ-NEP+Mz%wy(0#v1;S_o~2}f@f*BmVn3)TrsLX1$k6{qH0h@^+h0uqE4t?^i4|i z*Y{N7SY$4t=VTjY2`W;F@&X#k2`U+3V>d?b7TndkbyPWEb(ZJwHm4d6tK5y#g83Xm z59(niiYK~e*4uqU)YluMG*wjOrlyhAggDvjB?W2>11;kibyl(C3&lXFOxmtg#VO`j zkT6e#oS*6j2Xcixvt0sS!?z0(&3VBW_76+r60n5O+t zbM(t3Q`p~k%U|f63hwJX0Yta3UBrZvuH|lys3xBisdOI%?kDsJjQ3oe7)qRW3?q3O zKGX}G>z!4ADDWEBH)@HY-k)M32X{5owv446n_}OZRN7933mf<6RgWp!d%sIsBlaDP$Fa7*+*{l z!`)ame6iITW3|3SuQR&m#FBm>3(gXL)d*497mgnkg)sSpVBeAv>GmfTKW42txAR=i zrS})tV`-QCvSN1j2>j0{?NPhTw~vxGgzdNXZGuz$Rrfl&;o=mnpYtHwe!{YE&8n@r z^D6z{AOq}AR=auNd|8B5UnpqWZLLeSl_fy?!j-IAN832aH7yFX_jY37{4OKdnE7`t zaK05lZ8$CqX}?@4)wznL&@!f3aA+2lDKnVxB065+w6he13ZiU#e<61#H18p&eRl;# z+;5cSSyff~<9WK)Roj?W>S{M@D&3@Nq2o0cnR&$_1g1cgfuvPpZ=|?+d-$tvJ|5ZpH+&~nd{nJkCZ~C0T^Xq{8G?$@P$a77S{rnpAC9X z!26|-qu<+>X6labmqHelnRqLxf-uASso4h(TCbGsfNiUOLvGb7W9^Gu ztxa@JZxYNHCIC8a37;urwxTv}23%u>GLjpDS?Lgx(v z2A40z?CS=i^xD>s`!=^cPrG0-2kQ{D?^H;_Tu3&ek?frNXrPmJoL=XBd`KXrq_*0R z`OB1`xciii8q$8m9NnhSDWmPCfG07=)ZW;2Z? z;m3;Fc9Ym3EES+N)khff$oaCwV9o=!)WF6n+yyMB3eQsrBs=_Z=<+hgI#T`veHEr6q zh3`}%xoB)9O;WA3Dsz&hDH9rg$Hhf)a7*+cY5PyVMdu!B%_q<`jNH1va?xJn{;0VP z^DfD5eY|%0$2?nLy3`@oD(kSjCC=#cX-EXRN&{(8GbAJ*e0xcekv4-`FAT7^)v3rFveCvAAs>Pe+d|O)Q;iapmeyW`Z z$gRY=r`rtr_+;0L=`3B`~7rA#t$9h7TbjAYirw(c0^eF18Ip?a0}w#%vb&@_nvqH8W# z?P{YNvPpSL3hCplpgWU7uI*%L39S84ZAu&V9-Te0S=0!|#H)8Kw53+iT$o64bjtoB zrMP?~C`=xrapm3S-;6wKLcL?M}r-FecpJ|{(^HAq5<*q>~p+E~gj345rU@1vo5!qXs0#asWJOyz4;}| zL8+S;lxG+me~P6oFI4*u_lBX^Ib! zkk{;iABc#fS)aIQ-8ZWEa@8f8TBWeDEu<)e`XUK1QIV#I$oryU^8ivQ<9x2(LaiA! zLt8%zL(7o+Cny27!LxkucfrwMNLh^?jgl&F8x z0v_D|058tc6Cc{4-y?xeYF^h|2Sq5|*m58hVrtlTO)t z^@0Ph4uz@{ZDma!jiTjuGEZA_kt_!h{ZAVm8Wn+I4Yv4O2l^+{$G? z&6TM_d;{zKh9h@h`N}XwYy5!*<>x6{WW2VuUc&nS0Hz~&o}W2NBewqlrV#~KWIB_R z)sT9hS%1?5^3D9^N*izg08Ao|RNoGxBC1!n)pPn`3ugXureL@JLGDGpd;Ijj)So%L z=ly6Cl7HtadRzYhAonjy@=-F2{Y1x4<2n5?8H($J%1|wMuzQipdzN~{y$k(O1MraY zmDAp0XeG!r*PNl{gC6bJ61zE&54OtW0o?^ABN%@mN^J5EDK-K{ z>E+9&DupK}p;Ar+#0&rP^*&<(3LVbpgVjyP8R#z6+1JvIB~*e`?|nkG(r6 zIoFqH%0IxdQ?ztdIR2PAKJUKpLCe}0qCRRT-K)A@RG=e%ElW@nyX2B{OA*v2_;6}4e`x?Fma$5N`RMPmyA z<{er}RV5kB5Oom_N(+0q`6S?{!d~5Dr7}QuIu%bZoDqTAEH&(7mDM#>)d(qUx=>91 z!4y*AEtwH(IITlFw*E=l=>~wR*+s><`i-=e zI7H@TM9g}lub2Y)?kohgNwq$@)>iD3E~yfb(kkc9wWVlm`$go|i4}JT83jJ1hP@*{ zb)2bMyH3;tf@ z$&pb|+T1%+yKyFR=@qiyIa)=FV@l$di{>2%(%hN+LRv*l#Kl_MTepIeESURlEhp2N zGM1ikQ*m%;ukGyGr(ACXs;UU|Ev`CGO|6sqgi=j@ui>#%U5AJ=8r3$MY;vvb{{Vta zEKVukeN|w~K~-XLF4j3B`RNK=Ca{CiMWO`Hz94<*MmE$dGl=~^`GW9UBNkmE6)%Zs z8bH5TNloZ9D@liaFEje3va7OC!$Mn;)awb(YD@MQb+BEIs#HRG(c8P*V^|V+gbs;BLL|?fhYDt zi#l$HlPdCeP7%P^RX95>jG&MG7}6yXgbmHh+=g$7@cI;}3MP`j1GEp>2QlD{0;ISE zAYI2XJ91Q9LkZd9LUoG>!BiI#G;W#KysrkcB~x{T`g*E+N9&Xn*d%dPGh`xeiofOE z%yHH}+vsxRPJ)SoeMIRNHu%X3r=KwxRLeg4fN{NvFNEJ1N2fo+uzE$bIFhO6%xj{e zyovQy!R>2&Q{Nj>k_?DegYJUAN@o(8_0l_FsJ9}0VC~J9+qc}(zOqm%)bxTIZ4*YR z?2hHf@7Fq4ZnpXE1ij#ZUBmqH}QrQ7ph*$aPe6Gy0SAHU9wCPv&m7K8g-2 zp`V1{Nixy5Wn~X)IQ+#{9H+bfM8EIdb$MIKDp^qn^_U3em85;B%Icu|?>A<5V>*kA zpqa>psGWNF#e1@j;ICVNg`jAos@%r$U#s^Q98v2vDhGY@$}&83P2iAC#;w()7ZkFi zBdIZ8^$#3TLKcN2V;psnWfD~o_*P_f{+PV7uzbTsnagJ*gF>ZxZrQSz5*JkF*45K! z^42dP?dKiVVqWxPc1mq_=SfC)R@5oa9jDm@x3`)%K)bvocFB6(#F(WD(p_vQYr1lR znT&~{2_|-VHL874;!G;E`$jBWW*Q6jGf9oyu2xuxou6ePv{R7uuH~|Y%2!m8Gqz))*X)S|LCA4-_LWxi%1@TKI8Y_7f?mkQX07=j zRqB1abYzSuz2%)}rsL8xuPa;tqEB0snLQw5B9e#_=DlV-q9r&Bj-MRo<_Xq0Idi*0 zC832}ze{YC49}DzPGE&g%1>Ut1{x?%i721A{ncwn%DTP44};kn9CgmXtoB0Fh*BN3 zvhdt|Itb9l&1$mOM?~nQtf`_8zOHYJWyRj)rop&p}~hk{I^(U-N|RnLXx**>{)$^jqob`-_{{Vv2DwemPRn+>e*2TWFZn zO(1}N(H7B8@v&65Hg@v-?FDEj3;kAm@!)!T8X7F3pFOouZ3(5gr9sk!i3EolQhYs- zvPd8E3aX}HVF&tdE&J7y%l^C^qsbWhtQ?!hRTh%8?0&A5e!FUv^^&Av*0(H()HaA~ zuG_eiPM~+Eu8NdpedzJ_V48r7vaR(m0u+jbsiKlcnxfWB%Lt+#F3h-Tttu1=QJvT> zjw$ME(+?n$K@TAH*Uka@<`KHIW*Sy$M2_{vw@Ff74kYv-&-Fq+R-KVdw2_<=9c6NY zCX!FLsDaF(9ULV!s$N=&Gx75Ciubyz`iU*)eD(7%E6r25B7UlXK0Zbm?E;cB!33q5 z`OX9?Wyh*!Q>1d5CHT7`;V4QeS4eb%4w8;tKv0ROP?tbFVNy^b{$WR1{+Q*+MAtk( znVNm^%aEz8aU_JRQwv22kfV*)KA8l&1kpE~qlvC?re6yD!4r<6ag?D`U(JRG?8MbPZ^%t)fhFVjqZjYvY z0oY;#8&m$cu1+OyNZOmLr#b%@b8PoLDypB z+ipW&Sswj=raR*CN+oGKF9p8jI@BQhfrR+HRGh(nG*=O?%p`s0I0MBk8s*8PxqsiI z-|+~2N@3;jUVN>@b*M@ENRFvVMy^#!b)Ju)PoSEks!|5EaGD#cNl?qZgW zPRv`6c>_18tD?m1#`Rl7!s=>i+1#ZehNM)p+zdukyul0dk4P0&fj?n{QcxhiU0?Uo`w9K9wOz^=<3^Q8 znxcG+rgs23`s1z!`&GU*A`UCKNVO1HL>53Hj;y2AsAT|wZzO#>4Xd1ifnDDHBJ zph|SjrktNm)eXZ@{{S2o$I%An)hjhp;F5O|JPhRy5~g%QcVYmlKvloiOU6%fX4`FO zxZ=T6t`b9SpmkEn=puV)Opa99E5kV%R>o2rr>k0l z+~V6t*^mgClxHq*;^Eb;7olTw&$Nq{l_?z^vf|XjS3KdS$kj4P6F%ao40fC+cO%bw z+>`LN44w<(N=(5Q`o3bl>QUw zB>u>qrqpl>2aDGYNlTRZrxR0Z)ioR6`b192ARGc3XfDqZj{g9)4me~aHv4Hv zpGL>@M2)$Fg%JM$r(B;D8{E&wwBQ4C^!$*7PxV6mWGU^!+@1v@)N{rN-qfbC*QMeS zA^Kt$h>oqBJBkqdn`;Y^ow72Yo#HLkma(Tkmua@)}2k@vQ znSJH}m$;-mE2uj@9x0!6=DtU}O$*80r$Oq5R2v3NV?0_AJp_m3=1N9^c5k?1yd&4M7+MmVD*vXJmVD=Nn*!o@}9 zgp=XwC$HZFJ1Y?4iEb9*+XORQp@h*Hy?bPo_$2QC0Gc8OcMJ_0l`H#Z?c+w2`LK6G z$5`r{cXegS)Jja#)2z&oB`!h`cXTb=R5YGn-buX&w1sVP53GK9#yI;p=Nxexmx?yl zRvx+4t&j;4G8E`vkAwkv-DTpfjFay4_bF_=v|}`uU_F}t3OnWRt50_v(Tm)0s{BJ6 z$1!`V19kN1W~JJdBgj1C!c+11VjEoT?%{xazrk4!%EhHGXe2^%jp=Oms z_mIL0huT4j;%gWv017D>=j!JZ_M?Wf@l~QK9Xq-uGe|#lptgg-3}c9H0#mlJbq^&~ z0aKWlsCtj|pvYS#Rdwu;Q$;vqyi;+_npGu6KTz8vomE=>G&M|wgSklu za;G2!0DC&)9@8|sG;Qmu(Bk~k$L6GEwXIzvwK{0fH8m=?q1in&XhNnB>5SW5S5Z?-j>*LMXEXfJt+MheD%VWQO{gl3R26Hb*y4=AD+wwR_eZdHe{Q*!qG9>9 zUL)Uq(8(_iuEmM>S{~UiSj{3wU1LW57C>s4p=hM9Vy3K4m9+&_Po~5fnf-z*B$=!g zN5KzM;qg?bmGf%;MdUoIGPypgnv2a+y*CtQMLP&hGYR)4rqTp>j&A79a9!(PMB%K( zy{lL_58|0j84^4oXq{A4p(+JTMWbCr32G5NV1}}(j#JkWWtBMgEy#9i6{(7htuzhQ zK9jVTDP3{GgCjX8t~FR$`WEly+^i3I*%{`Fzbk9^uN|s!*_EiNTFjV*(v!LlJAPHm zRPH_aW@qK$45E;@0G?LcV$zhIl0F_2iwGm2Rg+Cr@}c>b3RK&rD;)?VNX3grin2l} zs8VH%khupQDP5N{xmy$^FG9AZv8S*uQ6pVNT`inWI)yV>`?l^Xzz`_8KIG$QIwAEn zQJ@|rzb!} z{$aFF!(G}$b4_mKL~vfSP1IG9a6S`|f-%lWp}IDygtGYbT@&d)IHtVCK|aM^WAoQ` z$K_Cs>$H503tN`UgEe7CM4*LB?lG6S>_)n!rETpz#w{NkQQkD+PENqGmaCCKm+9XH znz;k`70}8j!gjp&ve$4>`OfjQyn=ubLbx}4X8AzFZo6xEwxsItX=NnK%n00c9V|{IsD`-cp3Tu0R#r$LebEUjdZLR;NCrVe!$@JGdmliMYv^byQlR}CPtUIA z!GPz1gzS#Cox@_7_1K;@P@(Et+iP*vZGwX@vH+JE3%Dy*W-b^sDHZNA#vTD_uX>3( zm6IhUAbHL!&u_v}Z0?EMxCL(42XP)L#eJ)By;UhqVwGpE$}ack5RXysn{9Oo8UZAs zCu&ERSlQdyGTPFd0>xzS-*tV>wN?XQl^>n*mA>^@(pJSy!P|vvB_T-Ic-OhGQjFI1%k$4 zq#(E}$;HUBl2$94eEj)#`1vE|IkE^6LzAMaN|Cs%SzRC0a-d zLV-OrF(a*93TNsJT#GeGP?JQ<*%AFKyb}F;XpV~&a7b`4c7g(=J+Mn`-Qb7)L7pl} zx8Uloq(kf|g_#AnBi1Zou&7!4jK?XWS*PPGle!_b60LPfgBy)ONt-ERn~AR~P?||U z)!Q5_N`cvOpBC;Gyf^_ZnIGNSX9Y)&Y8rf-lkVNYcb7taIz8lpHEi(vV3B0Nfcp0o z{I+)abqPpSDmJU}g(IMeI&2##y6$mOEvJDWN2Yc*+L0n;nHXS@HB9)NLXau=uC|p4 z3b-i=0DLJVXg=sHT{s{e13}dWtOA*>-mAsKIVx(LMC-DqjwV-RSNS&9#-^PPdrZmw zF$Y-qqIyj5Lbr_C28yFku*gJm6*FLHPP+=IY^x4Z(}XD#@ET9Zn4UY=H0Yy!vrg&x z{Qm${P#Ow4Xa<4^W=d1V25jPQ1e*3yTT((ol%BFs6M?3>bx~|<0!;NURqG;|hpGwC zDJW5w+#)C34k@POHOY3}#hitaDI$v>tg7AkWW$>Nh!ygLOD-$1QiX@Jo@2<0JuG47 z0{W@YQXof8)2b<#gewJ!{WW=J@#(6yrs$HQI;BQJ26f8UFmr-9IYk(j)^D@$cD2UVs# zUDziNWAyC>V^rm700b#B6B_u%JnSP?uC>T~G*(90+P@fER?_LL6wMTLQDq(w4@{Iv zya7s9&M3m|8+|G^5Co)4Xb~eIDid~{RQ7ykhH5}%5*lcoB7dd`H{T_epRs%>X>BLn zD1So8Z46L zpQ!CsnANvSQmLdU0F(a!9BEsQiZQat8{4VevWu1eq?yIl%9Pt5erIq8)Kn?^Ss9i(?Y0#<7Qj{r8M-ba+QVNEN)?>yRTA1yXfaCsBqQ;wO zI*?S>n$O}I`9`A|kz`_v37YNK+#0EVv$-Z#&!)hXhKcDHhd$6WSs~d^4%IQPeBV=B zyd)9V$4x{+{Kqn~x9yHWs%5{(7mDfpxl!;03BVFxNa3PAn7Em`Pk!r@)r|v-2T#JN zR?9I?F~AWKXv%A4CSFiijF+#z6|qd^!tULH9H!%3zTn{5NaT-FL5IhWl$9L0s2H5| zTn&}h0DTo9C-DLx)?rJPWS!L;QjOoYYpoR$sw#*Q6EZXX5cl@^3M+o^_^7LfM~pvV zs9F}2BoZSze`(TQfse^`f@}pJ6F;^pRkHNCF9_tIFFjc0)L!pYaz*H0p0UfcTUhfh zDWoUNboa;9%-MWG1ss|eIut#)7Wausaue$cLM935R9kkiDx58Jq@bCRC=tvdXss{W z;X;w(jt+#IcdJLf)Gf7m6CQ6XC|9#v$R5y8 zOsJtm!y26SFKYh)3ca8}mzyG>QLXKJk|ow=VltQRqSNEsMHW^60DTzCkC|&C`BayP zXb^mrKhPstirHs!yQ_c4%=-IMkV#N7_URsjiEfL_yMf`Y)i`UdX{M`ETxg{$I%s0# zw1t<3=pB*d+oeoa%FAvfO(b}Ebce*cSp&C0HJIM)%k)&CQj|+^P}Whx@PeLk%zJcC z3vVQcLVjl0>cr%www=N(V8|M(tBhD@6yq75D^c)P50`Vr4kAZi^PrnqV~wZNi;@(INtp$~SveoEfW@ zZ>aUAd;D>f+scifEURQE@V2K5i9ceDS3b@`^0{2YoQ3}YJYfCFJj%jCMW$ z04wS32z2v1ED(#nGh0Ai}l2_Iw>ksCvj6A6x@J=tOWs z)f{c@fN30&D59|dNkID}mj(G%0e^&l2~@xr9;(0KVQQb!L8_#l zy?h}7PC74rMQCR@j#Tg$e!9A;dwswdg(3-c3M7z0^Pl-6la~s36w7RY>(&uU!h`h_ zB1E+J$0yZzr;w2|ud`U?)m$m`{{UBSzBzGR`rvgPW2loxxvm$f%eV%+(hd zG)exlf{K)9KLgSrM6cejl1w?TqHS02RKl4O(RJ$kdUe(rRI8+~aZb8>#4T4L7VMHp zoa2h*7p|ynS1%Omf(c2}-wL@G^^`n*$(U;8``2ID3suN3SE-oxnB~jgnIG2uG1M!U zzP8|GW0xzN*)7@HUQ@c-*-%UO%Qa(E%25C)wo>6h3L<-i8wnIZUo?=a{{SMR(9W|e zbs#k5J<72K2_U6AUr?5w=n=zAeV5wGe zW7;mc0N5zryDX&+mmRL$L6}bRe{56BlV~d9EK9r-dZ#*}3Lq$`d5sV0fuzbbD=4s! zBV>mDa*fB+QAqICW&wU!0*z(}@ktdKs#a7t6F(88KV#Yfi2R%T3?kk!H^6pF&f# zewjgIt-+~|HqG7PC^u-!Rt%|HV0e`*PxMX{THL>20v-3(V~Htd+eMt(Uiph{;j3jr zl2WJJnO@V>Or|7dmD_e86fBtw2mH+?Dy-+VV{K+>P=Cw;KF?$v+iSu;CzLa;tXuNN z5?bsx3WZ9yBdn>PS-he9VGcd#=6Omo@w9v~w0(<@fdEAs*w&>j>MQ=QcL@PwCWhez*Wiow{9mLIJ zYIF(k+P*_T2`S5r@!u%th-v&o0Keq)uL-qPZnIpgSzfaET%}=YKYTa`9X3+V)F6Z(7im76V;T6h}%(0^%db!;_2ovP?bt8uaiyhEEEc&gd5@i1wsvv}{(r@8uu zQCpwnxQ`Xk6z+A))8I*b+bvW! zns*P7a;=oic^%MmEG^r*tbF+?bq0gO{wNf4@6*E+ANxsmad^yB$o>sRO_HWdS5Q`p zFHlR^AgKPF4U`gCU%T`E)$6_e;`YRRw-w)_Wy&$;UCgK~IZ41mxL$F%ko z-WpHB-Iu+2b>;oG4&B9#9Z5AO)#2>p?(k6c3nA6+P%=-BrYP+hrApjgNy;b@8;hxS zpoH{nN&&o-Iq0i47PDR`h$rvq_CN%ZQA8kIQ6SGxI7gZVD;mC!J0`2PWPeXgO4)E_ z6-%*Q@;X-cYNUf+x|E3zAiX2$zQMT?;X&{udyN;#K5N8c?lMM!!KdyDrDm2EDJv=$ zK-p!vkfx>PPLTw#QN<;sZTep$kySI*DsOuv1iIDC2J3a?g#n{aH!=63DrW7bgP~Ut_fy_J zAd#KJuaqAOX%Six)lIKc_9zIimlF1DtYOSV?es*PZR#>bUA5-49Z;e^@i-9MJ*5-% zEOy0s)nydIeSNCv5}-%xhw2n&0+o>SMWPU}vV1|Dp^g<(vN8fIQKMw2N<>NrQZ}it zTxYsk$Qi(MN~VD7n{Nat`FV-Od!7hZO_r#j_)NlxQ0b`#BScUo8cW^ofkAXvT81V2 zsdZp|(IB1SHTKcFaGkcUUC%7;p~kKPuV?POozPr4Ng9Wf1#bP+_ltK~c%}(%^^Ub% zlcR}kQq$b2h&{QocT|)1yKC^3Fi>|3!*{xWPb9_<&oD1fMw_A)?|Eqv&0=7k7j>lJu1;7v;7Cn#!Hku&_Lb zRG6RX<95YHZ|7*$shx7|w<2sT*>Zbb?HyAm%PN=pK~UG@>#2Z^ZBUMLvpKbPi|EAsYO09W%D^Zm`4ymRIs+(*hGl? zA~|DHLpUKgEcVsbzS{`}OJ0c`OwYOtw6`uQvi9qgRVtibt*2dO)`)Sx+#rHI;tl;d z!3c7#T?}@u!jz?z1@x4mryk`O>Wv&ygBU&C_3ceMkFrF=#xcq#=Hm(DZ{iG&u%pqT zSqtc`gort~o@6As_JsccwD$9p)w-0b-slRDCt>l4TZ92u%iKjx_{VA&il-^XRSMcd zNZtx$4!&XziyAx?T=|EdDspqQCZD57a-Y$9UMZZu=r`yXfNoXcGY@9hA5rPHbRrw6|`-5go%%b+9AxE zF~LV9ZgP_7xN6Hxl=`iOsU20OvW}YRfpLxDEwjg1#W4z1C@0<^19lGt@oqv@ipXrF5a7j{_MG~WEwXk-w!c#7akk%R1cu#a~Ub$MsLSX>U%lweutB&is zAs06Vku)Mlz_GkWPul=Vm9G^maV@HxEZwPb^_$W()>?XCnM@_LX)kPq3noOKD5&wR2Sp=M@a_|A zLS;nA4UW4|CL%wtCuDH20XeC0d^XsTww;|d0PToGuO&zr#-&8$IJLG@Qz%PfK_I7J zWakWfByyZwM$m$LoH08j%Bz`aN%7pD54HzxIps!j@q@Z*Tzk>NtmQ~m`KHv3CF&<% z(js5co_9_i%+{UMdvp4S;Pi-Pr^Vl5=+09aZC@NfIYTeu#{zhJi8iCcQ=-2ds=AYG_b*?i{6MKXulBn4qiB{`T+F9)X7^ zmW#+ciXqOvZu^F_JjG>lzjkWVr;J=&II8Shc}mTEf`vk<^bxpCMJLKpM?R};Q~pS@`=FFCoo*N!--C*I{P8L5}D4#oae0Cz%7$Lx*a2=X-Wt~vX!`?H*G#1 z$X2w~M)wZ|IN+GAs^X?aXUDoznbXc%#+M%hUVbHx%-udZ@~fGK@qQWU2VSRWCPQH| z@Qrj9F@@$=c{_(Xidk|ro#R@jWyhbaRAqlE;s{Tdx7`CfHxpHy zJ*O*ziZZ_9o33HjDZo*1N_xmZH$BA48Y`)%IWv)N zqm*t0A!JHv8cR-*^-CIPz7gvR=#U+vQBhKp_@Pv-kS6t2yVRW06}!)@AqgUvl+-BF z^^T#>T_gk%s6J)}k}v|$+22(`xkWPRTL@l(vSvQm%jE{^dxV;$MDIyTSGUG9JutUS zc8I##1ONm_D*};@DJABN{D6Q+RuMX(6e2`})&^4r>XZ(>4~!yOptp}Gq8nJm6cI~Z@ryX?~!gn|EOvr&2 zuWeN&w1L4lOwwfom17)>_R~LU>k#ylOVEw@%tHd4$c&*ng>>?i70s5yU z++kv2X(L}4guOUFP*qybZ3f?QaX|$jk``s+BNv&3ht+zP4)93ICJH=#@lF#D8A zq2OQ{%}SCc>Z6;=CA`jYn8*dIvEZW0;U~|u#xCRLTBuHnOvMrk8=iyyAsUQs!BAva zNmjd#Z!KHvDZOQX1xf&``lF<7ad$#DYOo!{jxxq*t!>K^YWqNog}Z9~Q8JOQLF|q1 zBV}bm@xFJ!{#PiqRL{}6?IY;)$P$#b5;F0C+`<*a6Nd_>j!8gTPp${Tp{|e>!&Swk zg*te=)2_9)s#;5kQIHDonS$}nu8Na5_c66sZ5&OBL<7%GoIgcYW|U~ z3(nu0m5)`MIN zYUpqp;zQPip-Styum?znNFjFN9?OfC4~4}x>iF-E?gwi@6lzyOm6S{Lz*Ax%kZnX| zw{c#_xg;dN_Bl(tb*m_-r~au$M^Dob92-E<$KI^J%48$rV^7$ER~%=*n2p$(8S=cRv$(C@^Fjg(^4@EpBjs{L)ujMz3qntap5NO8I^OVXoahM)q^GX~sVt{oxTYD;eBT14YjH2)LKz&T%5;PZ%rTmPpU1Q*Cr@!PD>8SD zI<~2Y4UKTpN_~k*j&Qy*fAbzadSAR-QjhCMi4JmMy9yQkuWWuKU@;t;RAM%qaCB*_Mf#Xwa<7W#GY!lm0lrK=k`ts{{SJ2h$)Wk8Te5Bp(-=Y zd~)Ce^LH92uJ&9XvL!kDekBfjR_G6vDO2*lA5#B^ObiDj94ZI4@UF%C$?0SF&*KyF5^))!qmN)eeYKPudJe z*B&X|7mf?^9W$D>Gt@%$l5!seW7b@TpQaE&0>EDtdJB5UaIb zDbwSi_D2m74G0A})F&q(4|FUF%u0VwLOq8T*|SBQms=%#CEfn7#~ zku&8Ku5f+bUSApsaF+s+^L|OG;B~Ng`S)5-`&51mKfuE^a>auRQ5>*OXTC ziZfD_qqve!QcNo5vqVVN&#I*;;++}~=DdvjyrYzhkD5_3p0Uf9W~OomVqJV;6X>~B zSU@TVQS8<^Cn;VU!l>%5W;#mEy7Xl*bnUrHvc6U-n}%H3rCqw{@Hq~?T4AHv$`fWnh`hkK2 zC#H=mH%SwBAbqJ#jDIA5R&4K|X>CLPbfCDbZmF?CKP772+!CuePFC=lq7>uJ#i2j6 z!9x*r;k6J?&)K()QTF)m_OJX!!kvJ6{AF_g08~c|1m|-_0q$#l0Fk_Zm1hWwXcqkv5xp(GDjoav&m!n360p!?%qqmQMK|1PFxhUXmNC#>{W%!dR5781B znAHiGo!6<}K_^4tnduzX5~D9(mX=JnNSKka(KDPbLn|jjqJ72`H&xw0yfm+qFojF{ z#Ay%Z(Lb)hhNiXE);JoZXwr+%T`+SCBp-u~iU{_(HO9HyV!N*j(LgMykk4;5VU zie5m-mb#1e?zc8m7#KGjDDI;iy3?qGNTe!V0#8q}A|c@t z9>hoT%Cz-Dl6;8pg`r#~YW%8ak^cZ@WduZQ0Ik!f#Ivv89%~rpdiS|a8l4L%prPGA;wz#@ zShjc(u~D!YW%VXYcd;7H%tpTV_T?0j0`EE&%puDl19ARgHE9O5s?7+&$DE!U+gpb^v zIm#&pA3)>Wdr?zLq3P08GN^z-W;9?-q@A9gLbQFI z)+&>l+bIM1_KTxr9~q$v{gJ=&;!peXJxk7f)@5+V{{X4Ky9 zo`rZVwY6FkPNL|fxMZ!sA2~%?yCqr0YKVShpj2ltuie%Mbd^T35)p|C2=0Rlo;#@r6oXrWSr!y&eM&W%eg_P`?O-=q zG^5*+cueZ-ChJBNLJ9W1uxU3rvOTX zrPd^;GK`Xk^pFAC0Gud^Mb1|Ny{2g;2iRp3)S-Xf#f4Yx#u}GU0^f6sjVk$nugptpj>= z_NE4Pl!3b|2(ob#TBc=PzOJ2157D~9fz>KPgud|sO$)%;9Tn_O*rxqRNl8rQ)=(Hm z$GVC$YrB@#REC%Fs7Oo7DDrnafoX8|VneEgUafYm!nG)>ag%+)eye930fK=8w{g@@ z&LE5_YP4?6n%3sMpHpJ(sHN<6ipzSDGD6#Fexd=Fnq?k|YcZC?m(;xG<|f-2DwMoHwR!2!iQA=xdJ<-%`I-dzexWX3!Rh?&ZHa8G83U9%?T+A)iRF{V-R@;=N zDh;UyMD_6zWowENs_X9H>WV7>=WN%j^?TQMD1Kd4L=_-6;y%c-eXCVFuiTxMp3SKG>k)K9i|bscd|R}-WQ4g@Kqd!>G7zoh@KP5$#aJ9?wtF49wv^=(NF7rl zx68^nyip|xQcX7y<96_srjf9w`WXp`V_XV>z||?M9J5=JLRmlw{m>KT4f=o+n#!it zea6*B3uL4e&S{YG`(g=hfY_p$0q{}1+cKbKOG8LWBg#=-J=sOaZ9=1~wr&)qgbjQ| zykG-&RAVhjDVVIfx~1KrwQ12nA2=M`RHAx9i0d^M`o$kcmDNwk7-5*56oNJcwMB?# zcV&Rhwb`qr!SV9a8O~Qd?Hegd$J@Up#1_ zK+uo@Iuj52kc8^BE1nQ(u|7;0l)H?J_XeWCCltg;~r`>7kFbmffzSH3c9~ zeC7Hj0zV>)Yz`q>3`gnz0EF7o(fo<2rc+Xonf>HIa%A1XNZTc4agNh)+ek*+TPfQu zb|>}-4or*F;<5^Avv960Z3y(JTS^<3Ks-Lk0r#&Z5%fVxG&p82LWC$V(npDq68Gr{ zZXDpH5wHZlm$7SVH4KfNbsw%du6Zf1m4GDLzDC6D$stv2r*DMQ3fnihrb%}gOsl_e zcy+ZqjdLm4=Cp_*xc8EveqJ`Hdn4}~2)3z1^R^J6H2xw<(g|)I#Q{EXfj~KT)HemQ zHtLP>nYIa@@#O|Zga>pH?p<_M13LP#;MT-GSiUl)CSfV{0sAzZSX*IYZEA*>F>?Ka zsM(L!Qqae$nkwb3NdzRJMqj2S7LYV1hF(1As%~TTkjQFM=(jGR*^{uOk?i+J7r@j^ zSc}Oxs*5O1TVSDRPbv)CAh<0*F?4lf zNu9*b2l|H+tXYqVqh&wfPt{jDN-CaHh*Gr*Q6P!=K<=2=bz>d0ehcU)vHNW;I&A>} z2R9(8QU^l@+q$hQ8 zy1I*GUFGlSC;nJ!l)9<2cacJefFH6Bx7`DJbSpJ`nWsb4HOK-)g>E`V^9EOw_Xss# zIX%eIN&4d;sg()+v2$}uD!gGMb(NV*VHHm304g(-XXHAqeY0v6U*vE@PY{0;QeB-^ zqQz8;&>?D269!>W8oo@*wCTIc_QRO!E-9~8i4q2+^${ywRU6w>l#69;St+KcQ_4}$ zBp99;%l1OtTrq;ArP@9-%PwFRElwGa6pCn4f&cHc;{+ij93Ede7=y2O&>7|@vpe&NmvrRBCN8XA?BS2QG%_Rcc5OaaT( zF_$ipx~S=DOF}?T#(vnXR>;*BQD19c_-9U?HHqWAbZ0~z#yygaQ@WR$a;hK@)oPNF zpiK0G#(G6nFKohBbv)UYR3=8?r1_c72szmu#YW!Ngm6uo-c89WlP4}q0Qo47(Gb5d z@`{VMpc{nJs>m)CDkXI)B=s}v0lxX4wCF$3l$Nze`12B(y* zNu2tUVj86lK&fO6z7Um80du(OMv*f%Nk!;U5;LEehX*dA{8q0GA$gY&gELmA#s*!w z5KRJe-S+f}V=6kYqGz$EooT&|mFc8`(mubA5k>jr`-&aX6;2;;SUXr-lR9Y#EtJ1h zJW*fdON!I+Q8_OIw8UkUQhjJhwfu2#Q!gygJ+ed<`f89q`AP+SIHT<^04k7ji z0zK44LApy?fl(xZ0|JZ!h!v=5uSn$TE((WV7~)dV7gMr7i%%FC&#II$dvCZf#ln%- zm;5xilC=4W>k>yhd4gr!4s>J>1@-gT-_m0Y?(?>b7bIeL(L;T>uuCrP^%?a5+7dwN zsEv@zGTnolV!Sd;CB@s^8g^7qx{szea=olUDQ__NPO64jE-hWlARq;r)%eYQz4fzKO5+Zokff*-kbT5Op=u`H{5xcnZR<*i zl*gQmL}EtuLI>qnufy&T-jbKD5baYakR{>bA$m!Ma+dAPIk_MSNXWtnlylAqQMWE* zXXVjWlQYDOj8j`h>Lk^9Q~v;m)6ZYi0MD{2 z9n@`kSHke>4E6Dhz4ywsRvZ*xSbxMHd}MCV#+~V@rfStDn)cPqsxtFBLlU8q}nj;@hGXrJOq@bZ9eEnFI} zY_ctJ-7qlL9H73XryD5OP;``xM`jCOsxv9cBs;Aj!0rXxLn&ImDG3>M(l-zQLh^fy zdN`o7i(57HrBdGE8fgRFL@0=`kTuGjWVT^XR_aLVPLh|5XYL_d`$?2_Sj4nSWGB`u zinfYkQ+A=b8H1qkhB zY@>KhY$tb#2>`@AsD|88CoaWHKZL20dK8i&IaIOTAVsD2rdzLZC%}~xD2dS!rTV70 zY=%$s0|7yskyhPxNFe?2_JRU$GdvLD;gh39Jlmjj5>M=fQuM;}^E={XrP4OAf3hd~ zqk&!02r+o5RKu3wf2;u1{)pnakb2N7uc34Q01;-}?a+JX9aE zr8DZHfPZ8!O@nlzNqkW!ZMb9KiqhIh@dz6Jh!OBb;D}|^?fk_Q{#wCbKNYp4{{W&V z5Suto;Yf0qE!o8tcLTs3B2Wh^eL!V{ji4`&Ij*Z{zEqq57-yu#?Fa+u328lAB8@< zhxM3Gqo)#_+~7O0zUAt0ERB?c#&1@Etie|8VmH}1?Bbp>@SWob(^#f{){JLP9Y1+9 zG0n4^gOn#DYJcUAc}D*LE#)e4xtzVKiFhrkM4wA>{K>7mvnS*u?Le*GW@Jq_T1ts@ zn1$(G4TxORjytvAasgM~YiyC|(!PAezor}2yY(VPzBkqvLontP2|H(}QoO+b04z7^ zuKWT=*zFkb%v zwj6~g;>V>g?GgJUzg3sPJM;)Ym@fYS+imS1ix?j8=k$na%g_9(LEF&(060Sqc)Ot` zCmLCplEPL5{^*I>nMVYpw<157AQR1XY9y}}c_;kw`$QnjN#MKuY9aZ;G5*=#BV1ka z<@`bY5irZi;G_Ql9gq2f4SdY*gZXLn{{W_+=#Dvu_!RH)xc>l{ApZc{E1o#>^BoTK z#A7P|00N@_02gQT1YtbX&^qI_8ub9CB>hvxJa*t8m@gdW=68SdJ5~3i`XRrndXwAR zmHfeeM>sH&XOC40_{|^D3NswM5<9l4K485(e%Xn!z3E1ZUeV-+*=+?Ypa z9pG0twp;wc7jvB#BkAK69?7XcvKVu(Ka{5q)o1fH6IVIcku+kjEcg_8ejtN#FU{{Yp5{c7X+ zN}K$8&~G4J{{Xc7?CLPt5A^>4q9pxR=lM!!=vH6#2f27TFOZf%Mko?bKvL$A@shg! zREGy*Kjt7+pK@c0CA$(+=lnGoll3c;`BTO>9tN*P+|2n=WKP-{ANrI1FxOw*9tqz; zjvtv5b0y^mJ{2~!&!*O8C}_O9JQIGK8{&#NRmm@p;#9tk-aBRcA_qF~r?>Rs!4&+j zIH>;i<5HP_X;8mOc6g;X)(qL90IX9bfBygxaeRZ|rLH*Q?hfuL<2PzHXh5frYaN&I z<>$J6e%NQ|PCKc8$yhie&2gPXrn`a>KBHK0!V$$W-OS$v`mkbS9;~FfS!?O(FEqN- z)+{YbX@AyC^aOYqA}n7ksuexXY4Qp>v`F_3F-6k8uIF;doUN^^ac)&S0Nl40cPPz7 z_?Tq9yxCpM=W2Xf%r6B#nB^|8?6%?K4alinTo8#OX zl|Y+%>Qp+HA8?PkGKt&Uw$0&G8~dv9+Zug|R-YKeSf8Vh(;0Lt1!w+If@8+pu;|c% zmo;|n-cphowo>g3TIrYr@RYLL{{U1P+jxe+A%3c7ZsJgjtzOwAfRp%Al%#&49As!r zz&lXIR9DKsQ0zk1ri9(LSZG&rs8JnKl`f)F{{Te5 z(}uTDpB>zV)ZmxtIFsnzg>5-Ua(s=+H3%QZrng#Iqo-LNLRQNGLVndl7(<5YQu)cK z8hUz?x#lmTt_FAG-MjJ^D8AoAcD^{4RqsQ!E$QzOD4Ul=JodLTI=g!`MTgXXvRpCb ztBZb_*kjH%2g_Wt63u(SfuSWyd&VL9gT*oUiJmCuRUN7N+Hx(OwKKrLLx`L!``y{n|n|u4lXw5YmXYBcQl1JYT>J;IHgeV^O z%)G0YI$z2;8uxv9UyW5)O2+~F3!8sLN++n*GxWGd!6K&lT~#AjSmWwQ04>F-XK3*F zrwh)fZMfiuRZ>mlE5 z;EoHXA6SB&zb;qcq3wxk6dbMyHCgOcWEyJZ$Q?EQn4U9L6UhNfxZVwDx?|UhDx0fq zrTRD0g{neI^Z>3vQNPIttX|tl*T!0W7Fy`Z9m8HY6`YJwB9Zq)$n1c0(vLg6gfM<=TIvP8(v2G(ppbhp0QjBtr5g7;mNDobcMQ7 zbuyZL@aAe)N@&%pUrq;C`ByB{6Z{0Oh1Gk?9B=jr_a7^oL=PUd;9hgKvc!A`?^a_y zw$nJ>SXlO?4SLQtw{U8_&cb$p0;as3kE-c~WV8rcl!qytS`pXdY;RBL5sU5{V=c3S z-gce$Lh*;zvmF`}`lIKLS~!LD>yeaBW_@6&R4zu+tt3RE(j&4R!5AGn%)yO1D2lNC zt2pC)%Wf_FEBOnMC)wymzp$VG0Dtxs=l*FjxMzR$58kgW+0tKH#=d5KOnPnM)p#~A z{2?kR6q zHHlpnAf$U?yyY84ssU%Dx0S2pEBm&#!3RtTJd)4;_w z+*J!pV=)ROxm$`HQApUY5Iw%=%u5jkYpt%q2`!aXu(9b>EdX@^Mj&gP5soY)P@cA0 z>#L45hbtH9?f?R3IqBmDTZdIr-Uz_iTAUw_n#t*bRnz33+LDD7?gXEMBo{*lCy%dx5`5x-8H^)nF`wRER^V^EhQ)Pz}+%8Mgj^6brjCY4LArLbsx4N zqL7-RH4V5mm?Zd45l)dERJ=Ms)G6$B5puN3Wg0>vP#e+|3QVRYKAJ*RB@usJ!MD?J zf+wgx*++E6=l+dxb zN+xbu;RyUJ4jU&5Im+<1!fqDoYSAx>XaK~&tk-aaJ-pHKlo{kaBIo}A!7OM~EmPd% zQU_7jNkiR{h1-P~#9XuGa&BF5-X_VuAy)Mpsv2*ehuJuaYdcA`D$d^a_6?Ikd?Aax zqPunYEeBntN%85@D&_5~dgxVyd&1}qT9B*wHnyDq0EDYcrCOx*nD)TZcEd)XqayDU zS~;pv;`Xjsov!DJU06RMUhk;HfFLPSO1}8A##%Z*gsP0*QIk8JyQ`bS-K*R3vYU{k z1(hhN2`QKpJ`tve9>~^aT#MhORp`Y#R>i0lPHU*!Dr#GaKt3K&NN7P(#Oka)gN886 zI=_cP+E4)S1NFlFO6J-cr2X1#;J$G+hMcewKIt1 zx=t48;vL7?ARshHlz{zF5V;B60*$8eXKgZVXdYln71AYZ2u727qF&UcU{A(SKUF@6 z7wFSp_+22Ypc)@I8?2OD>r|If$F(;^5Zy|S{<`$nSQcBL6zztE5*@z3b6nK=lvYIi zVy*WiqM6`~x7t!(8)S(sN@HjyM0=oi-VQ0QY>wGo%p*z(LfGtk!PL!j=)lB`} zV5Vbb;B6`mIyf7dnF? zI{uhZk6=2XUw0wdRb1QZ)mGtgt5d2#)CCm*_eyZR2!d&B&Q(h<`k?X+!A}ZXh(bv| zpTvFM5M)L$xMSvb6Ns zd^ZD%PdsAyKMD`B5{SlKv?bd&9abgDr6i|G=`+?V-fD=Bc95pLZvm=EMA}gP$sHvM z*xYqX^-5f{VvjHry#-q9FBwCOW~ye%qMk5&V~o{OD`@~I4`f2{XpU(WTgBO>f1tZz z-!n82BF5K7(NJcHCadWeh_0%3%|@Nuz5q%4BRiA%mDV;&uqmn6rY1(FGqP^4VKoUo z*UAYql)9K|L!xb6YDq%CGSg3Q-2}o=Qwjx~J;^>*M_n31UO zH8t93RH^G1bGCt1b5cceW{RX~AppwQa=_+MF%%5S3JB=2m z(c3Dcdj9pmKdfLia+!4^VP0B7E=cP_H|~_jkGd6c*-Ka@=N_SXrB)#!Ns&8}2ENJ0 zvllc?rB|Ql?5-X@6>BXVp@LFVC)9DY6v%_{=|6OAW4(cSrOp=YISpIR_*;!fkJcRs87rl2T{ZLV46q7wC z(_JE-PT=rbSUV-7QlcyOG+pjZtZyj_@rv7w+nu7(+xEOH)kGI8h5Ev^I<4woQQ@pq zy5-I)_A5rlC=*%Al`nCT1Hu^DNmCK3CRRz@bn=G1wMqgi?25GM;}N;EQZ>l2Boi|z zbZd&|PxuINVniTRr64`<^oyjd&Qgw1y$hg_^XUkfzfL)FSYPiSR!7>Mo)LypVM0I>ULwZy{d#=fSdgQ~X8duMj-do@de9w6IrxHG#y^-5hxpsSUW&4iQAw%p1P|@9) zllc$0u772A%wzk5-iKFb{fgr)m#b}<^&(oDmh1I2sYsWe^~XConUhXa_n?Lcx8vEs zm6;z}i+1nZ?I&|wt8!(=C@lEmZ7xvT6twO2t7__AWweB7cA%+#LNr)| ze10c_7JmBM80onKgGY}KxD&41ecG>x{!8t1yuAMaBD-Euz_&|iuYK21ZVy|67($d( zmeNH|p)-&%HeSx+U4qy4_EQyT-d>(Bu+Dyp{r%9x>VZ(UVt zAKkdG=lhS~jH|OQ>uFn^XAxDf6V#fkap~v&Ai-auT!a(5vm0F<_Y}*!cIC{LVBx>x zXtN<7jc(P`5$^aFsDf-P%oTA@x9&?L;(evAIE| z=w>D&mg4L!4ZVww=@eD^-x5`-Wh3Ji!tA-o@1~;qHTp^YSov$Qs~tWN{StF-E3b6E z_B`UtZ__QuHz~#zcMlUZ$Va7ESw(wp82#6QcO~?=`{zjz_({|+vYy|5r=6MilVzu{ z)n93?IW22pHMqUqq_F5xv>*rcQ11*7_r@J5~Z+j+GjYaxqU?yt+Am|o#4yW5tqMm)Yz!HsLk{2F^ik#TDbmIDPsu%ih20; zjaED53XbsVRf5IPNJg5^uasNHbXoVdibYdpX-~7p3S(%R1xkEi+?kVc{n47cU0Q=c zk6M+q+w&Nl7i>N8E0;3&8!8|Hi_krlUGLE@CV zS8iJN8oE+b{6><$g>uy-iO3L6ieBUawO&?$zfpC;u8VQ{a$H3+ z+baYEyR6~1oy!b|R1M#5T}0gtRVuFS zXQv0_K-x9kAb(U!>u^)Prinb;D9_*lXW8KpbOLy|uM}15O8`tDr(H}#Btb|VR|elt zDFhKQ=3}U(5|F%2u|5k>9&?6r>YddEbSV}F<5IAEHR%!qM73Tfo!+9QNggLd)*=jC z9aJJ|Yf1hcX74g)B@F2L($#t?Z*CHrbuIGPCEbVcq};<{4~w%z^r0ogky?mBV+;RRp(JRYudm=oIq<^c4t|tO%yt+>ubyN3f+@8q5;l z^K3VBp>maosn(d1i2dE>1{J)HUT^`*ChkZP0qW)hL z5wT(EGh4_b#BEpfL|;i7nYWUl9JAp8TDpJ__Er55pGg{@>m^@}mj_soeH!@fKhYf} zyh(npR2i_~!c(e1pR@dt57Wk-^)oKNCF8@XxiXRCG^+mo7AwG%}!#wEEGY{-wkFA-yvENmd38JJOY zJQq71>q+iqcNfVm(Jpr1gfj6VEo0E zC(3Cmm)!~0a$b{o6iLg|O@Do2Xsg7@X+N$b>l33 z?zl}h4&aTqe#f3E^scT^lm7sC*jh*1IDgeQ#YFscz6n~L{{U1GMR&GWdXo4ZQkhrj ziJdq@m^pU@E~=A$VI{@lsjF-L{*lDI54Yhnh=lGN1olN4{M7cQ%JMcDeJHNBy>Cq9 zu<8_(FBxtSg6IRPC}XNV5(=E~3*Tu)4Xc(PD07dc1);R zMT@knz2t%Lo4w?_KYr)j{Sj&PmaQ5pNpYD^=AwB~_siOrIJRBGdltkgoj^M(D_d@< zdP-l7COF<(jtV1+zH#`RoCx}=T48tdJPh0Kig#6quGP*3 zA81pOfS)Od95`Xxqh0uj4oiI(IYJT-|J$^{GVfl5r*|R=+5Tn9dxy(V(V@d$r$d^g zx$uoflbjDGQW3>CIzzBi);aWl?wH8h4RCZRRG7jvBNkPRl;GBLZ4HwM& z_5T1ql+++>H2^6{oa9KE1p7Kggx7sY`Lz|+hkvzt99F#}%6KO8lHRDxPxBRU-Ip7H z5{Ln!cPimZ@-fBbI5i!#Ay3?yWjd*5LJ7x$>1c<)cPu2WbNY5zYbY%IwN&FIHnq_r zWPCFLY8v~Dd;b8J7RD%@Rr7b7xdzK5e7+yOT8x&1<{f-`d&)|I9Yp-2ZFv<}nc7YQ ztocm?cB@_fp~3)7JxZ2?<>{+P&gEhafyDSNP9k1=h5_QSihml9nL2&3^5l-Jg?)=n z5theTE*R7=M#1T%i132}LzUL)YX);B(OJIJC{*ty+%C#V6xN$nUiT%ZsZdbT2kVWe zVn#*Le*hPrd80E%G)8xGhu!}G6>lzeN>i??dJ~|YzEQrocB{m!q97)%@a9vo|?#)#W)Gk0Iyp;LUGy~KD$~-f(VCMiW;h$=X@&Xam8oKQZS-QNR;L8 z_Q#>)B%oelm$#{^oQ6lWAcx9^zR7*@e#@q;yX~rxu31Wwh5rB)tv&;+M=b23p4QNf zlt+50w^x-Ksz#mhJCL13QryPUv{ur*D8lJ%u{pfAP4db=g-xu8qLKi#5Zt?pkE@ue zqEJ;GYNVctQJ#@mJO!%a7l-Z=K)zKl;Q8hSOJMw4bTa7>bDk*Aqz7c>qN>I31uYBD zM76b9ZhDlbLRQ+-`oskLSsS=k5#2=Xh)If8q!B+k#7=DyY!zN^ z-7&YXgf%;naH1qrwo~zr;GP8$MQj7&I{xT}DZ`K;k>e=j^a|>#7&C3vBian2C&=J* zDa^NPhN$f}Wqh|S1*+w^g!FAjV2>?hqP>uDOC~xujg;Sq-k!g^O671$RD}b(O(aBV zEUr1VD<^+p%4im_aK|QDal1V`LDA|Z04q@!j<{7Y+H0zw9OnC4hw$pq71u*371snJ zLEI%A-1Ymr{~X2NGf|kH8U{})R3Aj zihf$VkEds7RYFXU#0r(9;YGz%QrVRyS-Y0DrcOg<#&XD_+~>ykAp>{)`^fV1zqYxQRwEy|X6rIvwG z!1f5bupGf)^To_MEKueC`>Qe2R+fE2>vIJ&R+8z~G}iIQ#bRc@l$Dx1kM$em2c4*D z*-cO+qv>d=et`v!n^aOZSBfgXe(z72@&zq)>!@rj%V^w@?wAlX_bukCCf6S0x@Ld2 zF5&V;mgy+&6+7o7r4FoXuBHV!HUp}>j|k!ue&vk!C6g=Fe;T^1JNS08?x)@&IkR#2 zr?&#&c1SLE-`pkDNT{mDt}L{4Ds>JGJ&*wI2Jmv7n0L4Gr{*cyFDm8rD50uhbgCo= zO80IW%gDqJ%(k1NW5zfgmDa)jiucU2ovNFjNmou(H#~$SNP;J(fXZHwS@`2))^l6n^<$t4H!xV0!rq6h*I{_gJ1zN?GzG<%- zs{5O1<2x!BmG%wDxZBS(&03r+{{Urr!NTQ^;)fT`Zs9`}ow#zPk;QsVt;D3r%_Ma4 zoIj`(qWYl{Mia|+kuT-?htS`OHJFBVTjHidhGE= ziJDTA8N+0a;$3u{D=rh-%BKY`Sw!(nmpytzzMwft^yNs{&HmK4QR=Nf5%C^SH#Jj( zc~zCgYV37LW@-F*^pb;v5$NRp`#kumm0 zV=ofZVQ12c#yeUiF{hD?%$uzoXGDY+*khsN`S(<_>ANaQkojml4 z2@b1eXC8{F=G6e9)PXW*Ir}w=iPomglvRrIq3W#o6&M9uCS_8A)EUoN!YR6SNe%)e zqo9SVPn9H8I_f7OFA%8al^%(`RbNn$Q|pin{7>wI#r_6K`yt{&cqbUZ;*6r2 z;P3Zs5)VxbJVznmqI}V}bf(TTcBqnq3PG2FiQ~sT!3gY}AurQl7dnA8BZb(8>wtbIm02TgUOot#KE)p0 z?>K$5DHT~I!v5&hN_wr;?jy`8QPSN3yNOPN8uuL<2iT%J_a#}W)W6HZU?w_JHxX)RQ0-y)GD z&&ZjW^(+kBpf3>ai!m$DinbVi+PSpR+%5I4QPtC#vsBX5+lW$3!Ag)p2i+c#X+Kae z5X$3u9aX7)xBXH(tL^FfIz(n~XIQ;CPWu(rYH?|8G7>|_ap~qEw|Glw+d>fbnS0Aq z@Hmm+R{8ex^~K;0Rz(Ik%yyMvcbbBfJ5^GIu90gZNBFXk6Z4D}_hQJ?%?VzEjq@FZ za!%EGGF}wy>$^K6)if;GayIS_&AO%yJBtNFs3_D$BWB3j9YLU4HrnE{3_PR)R!{c} z^eM7jMQ!7UZ>qIEy~}3Y@%0!!APE_WyR+{~8az>FG98U8f3#K~^Ow`pxlRyzrRIx?|s+lD+Ab_xi$dm}KaOO4QJt?@~+e*;zPMRKO zkaj+R@!Q$8SM=QCj$h**=eS#qdAkCtMdJtMYb6ch!BP_?C7RX}>1|-hhZ|8SSUQ3T zFeIe0)f*K-4=agdhM`8ew{N&USYHp?no^tuB$T9)`Xa{PEh{!C8=NFB;Hftz{XF>h zB}>FunM=q=?n4H~AOof65kByt^afu`TW?Ly0LQcxoWnv+z4|tFAEjEITS!opyuG zc!Q7p-XiT=Y|nbwNmYm{8?3-Cb%4>(2LMfjlj z8OJU!UY6#PIxF@^F6uAMwc?e^P?b{Y!5&84HuckVB5SIjSPfPeMVw-FJcB^(&kAC` znOQvpB5QhpI6Y*f0wzj)Clk7*nr?-Xq-eD(k@+v_j`j_CZy4jWKXF|v!ozQ;wYy1L zinTUFB~FdI091;M{2*xHlx|gHY7JI11~(+6t8pDqNy;4d0V6X;KfW0w z1rx)_MAlkMf5oRLGu71Vr`Zs41oJIX>S|gFr`TZDB8%2i5SQu*LrL6axl8Z^#ubr{ zWgM3Ut=U}+7lmyYm9?ysBH@O4;in|F>QR5Mr?s@5v(*-zida9|F{rh$VQp%%SDnKn z9YPenZ|(<-@in%JU#Hg*Eo=kn{%LHwT-}({;#FhnpWO|9GMs%q>8$Qa2&WHk@KOb{ z>L>iLl%mhrp8`>#kHD4Ssd8WIJ**CKEjyy3%e-jN66@breq^I;HDT>fx9c(hk`Q>d zmZxmIemb_{j>JYde$T-Z7HVqzT934TDzkGh)Hk@EZpUu;Zc~bJhAoZW(dpOI;=F5P zu+@}#BB&~%)O*rF_QYRC)@(m#;H@UfM&?igJ+80r3WGwo(f|V0m1iXioMM9MpailJLf%Y9CD2iz*IqF0x&VD^1q#|27B)f7$ znyeHNr(Fys5klY%(I;*14a6sS^9CS%M53E;(vh5{RESr@?4OiJ$XbYO!6i7!NFXGn z_!x>!kpe+fN!HdT0!c}M%8{=Kp<-#A%)ZErrl+bc>6z+ef8p$e3{NF;<5TFdr-UG4 z;8%eg1m`e(Vmc*46t~e+oI8}JjaT4?l`Wv3xIzGsnjsXHQYKV{C;LlEWIodm5tSD) zC~6jk${*5d{?)#qTKxtF3VC&TU2_Nj*$;XY@}A8CwNhjQGE zyZzV5;}_brtK7PJMJM#c%(RgKHSBF~F>`NC`mfW zUWIGqY3zxcZQ_BGJqI~xH69>o_t5>yS9|D3-i?Np%Dm$#;@&o5ojaPT%Q4P?)s5p;?r|hTVss8{n_D^u&*4#IqHIru8pc>0EN1%Yp$xBh+nMkVlvzAmwRR?Gg?PrY&l(DK`7K zaccZPC~5j&VHhc{dxqf~cNNQ7Nn2mVNYwl@_H~JbY3;gh$r`5Lk+zTV7S^9_Jrq+j z`eHtbs&KfBozs4ygp`w4U_c4#q@<*uZ08X=DxzB{Y0=3I>I(y^A=(#dy3I&bVGz;=n~*%>4C3^oGW?jMGd1;rEs|e;UJ^*_{KF} zpsP-OPQ$AH?wYZ+w0*@%Bz}mLM$@rTI`U?ZiHTTX`MoL&)D5$IrcR}FPNJX?HPI=O zzeGybNyAhVvNq>>k7^m}Cx%R>1;2@_R6~h*+ z*cD#77f!NtX|K8p`l!mSlG<^>EdKy5I{QWIeN#6k{{XDo(?9i2uug<%R1Tc9>!M0k zqOP84sijg>N!z&uez<|rlDJ+E6kWyzKgOyOqu_de$ZsVR#I@qMH+I-S39~lhgypzT zSZkeBR7|fFQS7efkOMY~WRD3p59^5eikw=;c%m!Ja!0`dq%kk*G?6dS4KFHRq=a!n z3-(CzK>39sJ~GqwLW1I=bWnsEU%I>qT}z4wuf%-fM#kb4Z+u}X)3a2v5~Zz5_HLQ` zp~518b<+ZO+cEpy#Yt|G*;iJj4HAZ&SLzWU4FgpJ?#BfwA+MStxly)99fGk~e_}lEl9sn(&Hx!e=D$(4A}P{CeWjfQZwW|6cg9o0>3#cxT4vIjiY5aSbI$3>lVZd4Jtx> znn;)*WB~i=9l1m{!X~Mj$Hcf*skA1R*2+S3N>YF{5HqB4Qj2B|3RSk^mO6AH2&ZX< zCR+&ur`rKJ&M3ycp*!jGUMavx0%xd z0(H&wOpMg8A~bu886R*c&$rN?V2l$FyxK+XQG6z(#lXr|QANkJMB zDMzE)7sUl1TGijQt0>D}8e6dg_5}vab)Q(Od+KVAqVT9l>m1jm4L~VgMj>;Qp{i2g zjA888m#~agmc4f`6-q&BQ<__0$CdgJV)wO~&wAE)tp5Oc+;nkr$>2YDTj9kRWwU~~ z`zxrt?Y2_iYLQUe&Pf}P(>|D-^&?Fq>8w?>cFZK`zBtKVO^}cQs;fEIcepLRRg{+L z*VHstH#Z`ggXSWWBi{DS1Pw~pVtl^5l8z#Ps+X4Y4pYf?tgG^h6p4zRg1HKTek#&(l%i^-0bh>Zu*-$JhiY@1RZ~seGJBmxI+E%p1w8^nKm>WHlOBuO zmd^S!AmUHlyi3eXfObW&otg*Vs&Lg@ayFDpmgH2OcevWXpST#aGR(7#f*%x~tc3}X ztfz9E`0h%i54=JNoy8C!1$K+26-0_E5@*m%L&V_jxWH12N)t&6noo>zds1{f+mV z5Y?+=lAqfYZmPOqNR=xKNFaV5rXwhZ4O4Y(?plwJmz-C~+N)N(M5$>&6->`vVn_i6 z3v_DItrjHl6ln=NNR*>rV&btEtp=&~YZeo#`gNRHvD&QpfD)S*ONxM0lmxtI5zOIK zR1j51HMdtv)Q?hQS@epNa7xfYWklDhu2O=ev$Vhtda!N13U5?A>WLnHKFAtJRT~RH zO`69PN|JgX4>%3tip~U(;RR<&Kt;7jH)Lvx;azWZIYC(E8=&apYf=uL;v!h(b54ap zEv#=J5UPJta(;J3PqkM8a;6`Nx`xp9p7jaEn=(_4^;Gg;tx3~G!Q|Ujd!$~l+E*4& z@v2xVX@+j>vxSK~VeB3|7mGxNik#4qn~-Q(CPxEU2O87THgMNl)t$ zTP!lSaIK(~jv5e=7Ozo24Mv|2PZ$-hqOog0Qg%6Zc8P?R=8`%OY-p^dqgHbEI=MgT zxjyl3OJTZ&B_qOQeyA|GgbQE=Ej7(IM4yhWWP8rA$4W@u5%lFLCz`4hgzl^}JwYY} z<~}M6hs8hX!r3#-_6jmes(l4Tc` zHqL8mu$jz+Z+iKKyZZf>mP}J30Y1|3` zk?A8@bnuTp$lk~uKv$;W@ywB$tw2v@JV{AGBxAIpNJxT)5xJnLoLo=CO}9B5jmoab*fh0ck1=7`^(}2u4HIb-JrV(& z<>MO5DajkJKE1YUT}moZkl~p7uap{O5T$$rXfvEj>!$?PK+-i*#sR>%ZSAtR=vyJg z%~MDqm=0^#jf%6j#4zfk&S~uP3cgXiq+1yZP$4S_0YgLLAuV#QCsj4ZEHr4cvyeL; za@zarwr@gGqELY&`eQ{E=p$4KiOp4_@!KYCX?XmoQ>jGur)iuzImHv%B51FTv->-{ zRc~;Fb&x$_{c0x)aZXEBPHSshN^*tdDXyxK(g}{}gu>RjOc?Jc-sqNuRW|6`Eyz*~ zwD!S_z12ZAm{!-0Ilhraq|`mMnGK|kKKQnZ>7`Xm30f(`T+3^vxzWCwq{&2n@EY}k zSvaPOrfJ;Gmhf;7JlR~g(eJKEBqS^nHR-QduD-D{t87MTtXG1Ql&n>aklkWdnP{M) zq-P(Ecr+_r26i*W2k-iEQ8CwDxz6Cv{hEuPrqS z{uv+>q>q7$*=?K#%Kkfu&lJjfqyj-f03CENGnnDQ71nKcNsd(~;v`CQlqS4VJ>aBT zsL;v(Gn6OuXoFJvO;aPJKc=Byym(Tx2iqM~lt8Yfy?!At z7)qxG6kSzq$xitQ(^!w_RTn z4XQ`RT14MVBa}|LiK#}L61%>kG$?NI0En5Gxg1c^Wuj_wQ1R7s>K>-zpB*~E@!_0R zWZl5fG*4;{EHVX5lQ~PNi#RY;-BZup4m&4}7a2B*A4nwl4U>x~aP0*HHgV)9wFe$G zwKAtq!}yNbPw$H%V89h=$vEnr^mrvQQiS?oDELTz$SJmw(_{?SZt+e^Y&n$tK9qp+ z1NOw9Oa{n!?n=zM9Cqniq@jvMl`E-{j}eB#SDga)JB*wx+=V3Bc!Mdplq}K^(a;$a z`lCZ@+w&2qS*yi2 zRkb14P&DXAze&Zcc)MuzDxLY;xzEG0GTwEI;>uU$)NLi!{{RET0!O?N9Q*iUIwnPl zxs>WkZDf7NUyBaYQdX8&me7=_r4siDuDZ{)n=6Yh&F0-e7milE+*IeSTD6rQ#8zb) z>#pIi6|%GKXxcVR3hbV`tj$i6PmIZm8M7{OzJn7RcqaR`PrPR1g!^KiONkt6Rih$n zc3h=iaVV8>Y8@&}hO#5VBx~xJLK(LuFV_>rB?^V?QYD0?%5&}NBSVbdt22=la4oDY8mPFz58- z6mSJa9joItVIgdnZ$DgaT*5^qpO-8x<<`dbAy+zYf&HLJl9vvr++#VD?Mq4Z8Wp3) z_l`!;(ccqR-SCIhYqW0W=oqx!Zj`pmwPHYW&2SB};U++dFRY^plI<&~4P)7QMlZdr zuG`Bfp-$db`eSyh+=Z>%rM|~UO;=nLwl7xiO{b8h4HY(YKWtM)+;^6*q?K2fJ(XvC z%EUtX@$c{G70os*KJRzo+Jt9xR`9oJC>8=L&8so)Yi6Cn%K3%Y0k(n5xlKfMCT70uw6dj^fuqeR@XJwR;kEhjruyH&y4aEIx6v+TAljLu-Vl8kW3lb z8dW{dZp7tPb5EuZ4PTPE;xx%o{{XZ@VX`{(DT|osCb_Gt_;CyRi(Q!7B3Pg!(^^ige{r3T|`)8Rj6zp5eCN*oatrDsn} zq<-qWAwpU&$!w!lb?_0yt_z)s0Fs`aHR~Lcm@Wzo`brw*&2>-Xv*AZaO8jZxk#16|`<8=F~j? z9Y@;}2?LP8xu%3D(!S99Woc7IKh`}A3n)akWZHg>Nu0!p=wMO`vulS07-eW0>o`E9 zFDvnE`{xKkdmMu;YGiaBW2l@BQCAxC`yqvaPaDx?u$>h@vLkyGprWp}2{Ilq$|nMg zC^&^pIv*Hno188bwwECFDqbF7Z6oaw%Y>9dwPmr_$+KE*PM_X!9-=iqY1SC%npC3U z%?fD1`!eQkV_KTxYFkcsQ#m5rO?kId)^wShHLkK@-=_0yqLa9p3u@q0#(t4q{kD?r ze+Jap>GUS1uHir%k?AEurch$HW0)FqsNH%|^4nIT!^ZxR1yCM>2RK+ba-BUmvW~ZwX5Bji7 zc$WTRg}CpIf9*zo(_h>Z@|)@B^!mn-UeCMzr&^i*_Kx10Yh_YC8=HK?hCVQ%J=J>c z8yK(K_^%z}-=dZT+J)Dow+9TB=l|tf!&dT~Va`bcJm8!-AQ%dBW=TIcwl__X3a7eNMYO z!W;vO{+QS;8DM{gsJWtuxD+*?@Tp%vrWf^CxCE1Sz5K4`l49fIt$y^judbgkT2NWz zF4Xw5j?$0=4z+D^v1kL{%{ZA5JefhjE_Y-^2C}@LSpNX`lM?my)cTU$%Z=K34<5m( zSGXaoCNWD)q$OPdRZFjlk3F!mWghMd#l5kQV{{I$w0_DcJ0<$2{YdiUx~qqG8GR{q zD|2)&(9tJp>Hw#v68j<cH=9e{l-5q0}YN0ENYcdLj*5ox$i5)q z%aw|8N9{2+T}mQ8q_Nd$X}~li;S%YMI@AwjZ?nSpJuiZvYO_qhDZIA z=?)2fy8f7VQQn$|plBQe|Hp zS*i-0qj?HJ=zye4^~6tWQ=2TIN4H)zc`8?+f(m+&LH_`oD2T=puw>jcDX9J@lTfK& zE$fZHgruvdScJm<2*%_hJB7+_SzQWBDwz4U#)U7m6hBCZj&L;zju!6N(Q>=Zj=T6* zjA6D^uC-LlEv{a`W+5~?g(pev6=RZf_B-lb<~a$lkEy<^gh^xMKQi_KswI)6Y#h^cpp#RmZ^Bcg7l<4#@wrFy}6-HsT5;eX#bvc()Z$HXMD= z%IY6??MAFz(}uY2%+rrPa-Cn^RM1qG924;gP$`KSk!R)!W$n2?<-JeXt9_qmYh}8Q zO{wDQ*W?Q~nm3=MK{^tr*hvW!@g@rETfAthe@@9p>T=Y2wO2QfzL~7@}08y(VeOw}L!G3oMLi({&rZp^l^K-)@RiqWU}K>{KS3BYEprO~lfz zCSD|O64EPOko6*CYtI#6Fpr~m8;6ptmit*qATUk z%2CmF^k8hJ+<&po6}2EDlL?`w1Zq&i2kQ}CHg0EWRCT@rA-a^T{jG833X$^mI(l@s z{n`@JN9=$fe*MW-kl~>m6sv3DoHF0D)3!G>`0N1m_6P`F0INV$zq^VwS}<%A@`DJl zQY9&B-h>qO_)|Dx&S&#LL3V2V3O*u z)Xkw=mX^Wl3WQ5eD~!Mt%dX1k8#6VDBl^3Czrs2ohJ3)L1^bsUoN=csatwl=(??Dw z?Oj9dw1k1F)kHx?2fJh&=M5_`dzkv)_Lsm+SK6`MgZ-#wv{Om4S{!ZUq1i|W9Rwi) zek8$mSlJ|X2ud3({y|klsym(fqIZ*x*K%W*%P-TnwR9+a&o^dn{#^UCrhARLmbop| zRxFO25QJ+qxMOE$+Xd91_x>B5eTu5)>NuT8yP4n0-`?%L!1!pYA0&4%*#7_yQq;4? zSl&hT59f3T5mnqTGgns}GMcCbZK$?lwC^DgrM2pS@J!RMsAFS~z(onIb^AYdgZB8< zQcpOS=WQF{J$veEe{CzESOLlvmkY*SlVq1b$hlDo;_G9ImcRSQcjqv36& zmB|PiB_ILPGrhvzS1&-pTsB4k1ppLLd}IJxR0{t95r}3wsT;E5BLyn6U6LcJyniTrxrvS4#CDDn47B5vuI@`7%+S1kq(X_Sw?mrek;I{e%^|ea+z6 z4{=tPG3+v@@{2vSKqjR|NLoP9jk1rWvKaAq4=6NW8(=d(r`!iQywu>b>fzYs_YS!` zQIlQ~a;9jhUXw*hDkE7L7{bHl^l`tL?c%7~GOZ-?}V8)kQ+NabFC*^MrBHD+a4i_$}sN?N=UK4uwB!zTFt9Y#mZwSbZdq z34!a%t^J7&#jFx{&r>KNRfGzh=oXNmd`_~2AR;5GCTOT0 zP!7|fTK*uJAJG9iHl2|yxNt+YWngPg^Q@=BN-7_5hV+0_HsGDoiYP8Dd@R-n!SGm- z{SbwUPH>JTS4IMTY7$f8w5xaOhp9mYtFND@SHS1+<=~)FL4nZ30(40CX6fosvW0~S z5@1V$jfYKCS9P}^#GkqYAz88Mr2Px23fey3Z`%P7k89F0*jiR*pRxle=1HPEZf!}Z zK?xuNHTz-gBq*gdYOuJnoD`@j@cxL>+Ph_tx?xV|)}=PCvX@V6St`ClqwCdL(g+HY zM@X)`lDaEPDAapXRlP)$GvFef?y9~GMFE_`l^}Zf!Y3z%6Bb%(6~9u)uJ9iU=cnBS zXc~p)cQTA`J67`DN#ONW(iub?+^x6@aTHME(Zwozu@;x5tPAdhP@wQGn()0JZN>pwk> zC55c~OJ!SPCtX^}yHua0+{g01sd}9@2NF%NIEjHK*CVW0!vrLJD!y5!2jf80+g9oo zwK6B)1-{FfswQ5Bl+d`_>M511%)bc&DXwwu2SssfcGsrU&egQAB`B_ZPHyV%E2R2SAxrAF)N(}=#O z>7&CKWAorth5$-EQV1b3)EHOTwfib#bu%AKpSuPGi=8k6sdL;xn0 zXK)_psnJzNubl^KhUu9RFC8Mjcq)mAkhklze)(OnQ_xYnUWZy}q|f0ylBsy8#s@2o zCDj3X79%PnXa#FE&%aD{+bUO2P}&|+ejrpN47x^r*i!PelXnwoMVI}MMeZ5gnjq|QjV{!! zc&6@05V~VQfnM2D?dVFA(ouoDv3p;#^h;~@7H*h)eVAi+Y_(6Y`buUrnCeVdveoT7 zB8==JwN`UKsj$`KuPq8eS#O{sVlA!OYO3y}VHH@9&-O|u?scnS@ChbDdTY`)c5k53 zVXxv2a-)?PyRTeOl)1UEsVl4y@@cBAWFopN#l(D9Uj4n3N~Hp%&Qh0$+Y~cRs?=FP zD3Cdi6u#rlIHfRMX&{}_pF#FS5kFK!k?5#Z?;xc0TyD0XYl>0cpZMbT868DWG)awC z-Zx$ks2kj`bmGb}HTUTwfA+t;j+HG=`7ils*Nyc27sC3LNNAw38Z_jHMx`jk9?uI-qu=N@${ zqOM=?`U0cj1jRF&bU{mQ95q&pj``0w#6Df8s}{DJ(I#_{9`hE_-8iiLnEGt4E-~KK z%9)f3nw144OYsr=V8G_PR1=|@yA>?kyX0xc(dyg+Ouq?HiJKc-hB93QEq{A-OKxA7 zB+sDV{V?dP+f+2SHz!33<{i`8?`*9u*P>2Ne}w+nuz{keW7aC7=DdrJUJ~ksLR}=8 zYLhv^oghjfjgeJ+k=rH3#b}kKa}vw#%BAu{%<-Kt5B3i#G9A zA6WLNChfu2>b0o+xZFA@4NNoi<~dImYF3>}jxRh3Or7d9(m>Z)03tVF*j1A|k?J7G z_+_eQ)T9Hni4_UyC=Of$Xo_caI;y+nm~piQ>{N<$B>4!cxWxvm`+VVYfv&CkYf?d+ zi4%b(wE-;5}gn05d&laOUOz9*0(OY z!@7i~2#PGKw-xwORSfDCH2_?Xnsmu66A}(*a9vh}ZM!|q=@8nC`ehj zq{A+C1WmOMV1pToc|u8-jj7Q?$8NZx>a?OdgRF#Fvkg^SE_=Z`={U_NQpi%gG=pNp zH;So#b;m$c(u0lJLQodS3LX*=ba>YIAg0T>r)>@eP9-J*`yl9S8W2+CoSiiIdk6?g zWF$+=tQIYTf!Cy^%6wgv%WGPAowA1;a1;-nI9SJ98%L+p$Y~P@>VJuF@TxQ6R>I1jgC|-(>t`f60E}L5_^Mx!5BJiugJZp?I zwwjr`m7vRZhA3L=c(e*$h2+lGske0HdmRB#p+Z@0BjJ$)_CONmTE$~)+VZ^AsCxO$ zj=Q}LzEVWwC)dU)C+=3$LbSG>m)s+EJqP!;*7hS?i*jaYd8^K4_c@1Ss z)TKP5yT)-zHev2s7;%he1dioPt{qlMJ`q_Z%bmGLG9c6mQ0vwrE>9tTDiZUAs!mn< zdQMR75>a*b!q6mLq|4tEvE(jBCj>RsQ9LKLf%&!RB<@y{PP3e7@gSX+b11+yD)3@h z?Nj(Y69q8J9xNfQqRaZ1+e+Pif_ zHIx-jQPeVK+l{SBAw$PWK|M}nG?w7(v_H3RsW)Sw+(hh%*{fLI%6FjxPyg3-gKCCXqkv#82HwjW9`U zf#cx=$Wp2Ew7z<-?Ps};H0DZ27Tu19p;o-@gdHBR-3nN#-7_!Xs3HFVKZDyA_-laI za+9H1&g{i~MZH_|1@=-ovYNh*mg7TSm*}e+L$sA4Q%j9Fo#;>_z=8~FWyJ%iULwq+ zm}ujuL(+3wa@+Gj5$%fUF*>UVvosnMxYsncuo5c4C->H1mhJ7hc`rv|9_Qtsf657e})5ak%6)CxC zM4fvYsoa&*wypkY5in_L6!N(xJB{JhbM+{iSXcHD9AdqkBdrLN9%&4?w9dU&D*XUC>tXBKU&^fpO)8E)5>K8kg*1{xj`r|(Pe4T zLpQIaE@Z%1w_b|ENtD|3bOun4Xegm7B(@bl!}i1mH8WpS2g;Qwc#kM=DxavjG{qzUB|pSrD7&l> z?5eJksjA8z_)2H!7J-2lT3Hx1*Z@_i+jTh^Y`eSzr!ke_@_g>%cmB0q= z*I?opLexo-d1dAZ2m4|rapuyhqor{PtrTCC{Uh?5+amSCoR?$dTRlMCPPVE79Vj}3 zHdN|K*Vz@*SXW}LVeW|{(alwtF#2qFMTsS8t>e0y)HYA?DblAxrQ!$>F~@qB4{cVFZEJl2Uw%o-Lak18U7{BX5Wh@f_1nBaqN(xM`mph80&nYVJW|CU5$81|dRUdsTfYzZ3Y~xC zL)mHy9r?B?7RlRBE?m+VO@UoVRHeF+RDxNy z+$icTRcl95rqKEuD-tu@nU8En;TbflNZW84Tmc|&O=(@L;r69dz+5evAueK>qxkhh zf@!Cy*!w|VQ~Xf(Sc0Q&UF|xijo-DdQn0lBPBU|&%P153wC{wiX&V0kcJK7WPPKOg zP|8M5ssqTdN~!|mId&6MXQ>~Gu?;D-&(CrO)Azuy#+Pmp_St7Q`&v&^fx&*%G@hv z&RbCle(^-NvHPM#^-z9=D@v8)B8eC*-WPn$1wn6#V@+X|&Gj8Y_U-!+i?H^`nLK|W zMFJJr z2hxm_UjixnEBls|@UPaFvpz!kc5WG+Zx`GhmiuNyO12wHjHI8KXM0Cq9nc(@_}}uB z9JcqcABm*oJ_-*^p7xBq^GjI>ufLN!D^grxll2Spd;XZ`={o z2WY4I9#+C?f;}99nC~Bl)1t=we(iH@lP^8Asdx3oq&=AW z(Zxq4!|AwHM=$#1_hpc_>MWT3hZiqTV~6Ie6oLMcXc)3*XGO%Jog=s(yZEZbm3>b; z*v%Vtir!x~Hflc_ZIn#YD0`IQaEP#jwe8ui=^XD0s)?QQ*DZ2QLfpukezv-2VX4iw0ugZprPK(`4h|u2;79`(0I*V_a#E<=mOPFB{CVN>g@b$WtO6 zq@i(1XV(f7ORJe4L}>?Fn4lzpDpVKKOL7BT;u+vHueEi{ob$}Rz+alkxu27-9FKLU zAkZ%5R98(zH32lVtprokfC(SMZ8~*{P1BrEjfgN&qMn2B_9*(2qiNf_aPvRqhY1QD z?NHYtzNapwKf59NqbJ-h_?GLoBLXU@=V>!NV=4mG)de^VxRoN|N5VK1;1Kk6brcn} zb@Z#*Q$p)+Q?JR!))tfZh&sx19KpkZOr93W8Dq90p7r!$dhfG?@IE=k>pwRaT!Qm! zqo`eqjiR--I(?EQ&ceVNzez| zC^HZr`CLzkwcJ(V>?vcAIhvN0P}JVl6s22|iGZ&#G>;&aly;jhY!9ekhli9TZq;!^ zO*)>DWYglKp)ZZ)@3oN^!j71$Eujg>2mE7(Nc9dMhK|0FO}Yo6v2Y z5%#Ya^H&xOvD^*zQjJ5U4lYUDnf0GYwR6ZT1kF)JJR}c@>E#U)6i^HBesVJ#hvJCR zQ7JI=lPy3dIZ4z@RPF@;XCgX6s6|rSFDMZ%z8=^hY@>6?M%3CKNDeZU%gh7T80bM< z*_;YzUTxIGIHu9*6bbw~c*SMRAz9j7k`!5S^%+`%RWYdm>lJR$Eev}m@~QzcS_ixy zkzXl9h|wooX?+Cz;v%MDMp&b#1q(@^kcA70Dps+)Y0%PofF~F5I*NvHpUu_NxY-I4 zG8&Qn5PM`4Rqup^%E+y()Fi5zz>_bCb&A`zqiqcoT=li)iBTKUJVA=pY9t7%kd#kd zgn7qRI6A1am2L!jd#0a!MEMTLhQt$gwQ5jES5E;Dz12rV+9XxVU#bDBDR|V1q+wIJ zbW$!gk=5fg71b1$)mE3=C{v80u~$7-~g1VmL_A4SPQ1P-vN&XE>UdV!~uG^qp7 zXl(8hGd}2aEg?O}MJVGfDQW^*nSQ9-;^XF6oVJ}b*{!~`y ziK%H)!*fr%5=?M#Qd{OJ#Ya~>RX`KzNRMPtUE;kUCF#NR9E3o+k%o$Yo*NQh#{PAa?1bRTxc49Tau;!V;MH#ZZE|Xds1otUH+wU{6aPn>X?Z#J)PO1ulhS_JahX?}Cmsp$Yvy|D3W`;;SX1!{%#kf& z9Vnt}EJ-@8JMGiDYKjKwQuTR4lP$ojO+2Pzx1tD_tuao!qRJbP{CY^mOt>f{)`+;XQ?o#>-val}-4$?h>2vf} zB_%0xC&pSu4exE!WuvvvyNOq0Ci;U}oUyF}>Xn5c06keAog#=}Id~!_&bi8f@;7-l z+pS~kO;-KNAwzV=p4ih`VvAd{p}5MI9TW&VuApMHIw}H^6gMQD2nIS_ONh}o^$u0F zU6=P0jONxR98F46;7U`pk)paDv53lJpBuGXtTseS#bY_L=V7_pDi842AW!a}X^N;= zK~}d96j-^+HtSfq*Evi=6Eaei%c(xdyN$$bt7V~8Cm^YIflKr%2vQHjueNb+$5jOA z#S=B2L#?BDAk-C5>QYOyGV_#48|b2%*`!rEnB+j{+Tx6e^MYd} z1yuO8M%S58x`dTFnF;-~i6eCiXHP`Uy=GR1H!sxy%cwm}84PZ!iNpe;+n**;w5621 z2@{va7LhUJAZ0A7VpVdZidhuuQ65PqU#or3vz>bZBqR@tKayZsZj^Oldpsq6`}>#At<7aY7;LI ze5R-;q`Rus;+tu-sR~k54^Dh z@R6Jq9v#H0`EzXv-$li>PROmJBi3OUbEj>LCO*+aRBTVWBr8X>Eh?8*sM919}y2d_!86#f}A^ke2`sFLJ zj~yn1Gt)?SDGCS4>q{(SrbFQdT}(;R39fxYTBXN=1mr!waPZnFL#maVvr?b~DJ#cF zTBx~-98yy@B_Z}lCe>h#$2+)HDO+AT`{U>7v#uvXzLB@VM&1dtQ+uzdB&p-~#ca7L z9IT_Ujk^SzLC-6=G-!zwCG}Z#S@FPMhlB16e?hK@PdQFhEP(iwz=9` zlsK0P6gnt*!z7R)-EMfQP2G{Mez}^*9$Tr|;z{Z!9#h^#h3?*3mU2;Nt zie9^l_uEF$g&Z$hkGJ;5Wyj-MA@ZsB2mmIWj->vf8Vf`jZA!QJ)9!v=hGsSZC8x4+ zam%F;P82CsKuCeCK#>z!36i`((h!tEXeeq@H%TN+brB1nRI)^PA#0cqGEhSM z2}1!)F=<7#y@+=oaLag5_bb@sCo#UiFzy zA$;54Sd(jW%m>0%y;mTC;Kd&S^T1oqi||TymH76XKkdT&!k4#B}hS4sq&O@+CQF zxp}~#K4)yFs4yGx+UkyvBT$w{F*>BBDX7#D;yHujZH`}AAgZQjdI`=U zemtq0lTKQ0Tig@AqC|ApSQy!Ajb->t^xU{i(p~~`)_ov1*(1h7MB=99J-|+*Z`}|+ z&2ulR61yCt%JftI=uf7qf7OKGXOd9iNK2rn!Bdn#ZIK^ZoD|%QleHz}f|31i=`jne z{Ya0jByN`3&e&NKbvpSG5&bfou9RELDBO`N2lW2fX^4%Nb()Q*BeyPFl7;^OTj3Hg zkBz}e)nZX<;f}vAT8X*%{3G2Eh}@Ojg&a*59{QxZ2IQ|ai4VRx#p$hUd=P8G`1-sM zx<->Z%xnY+!`Up23FZi$xJi%j*BVwQX{M*%uCr-Bwk7>aUlka!fayfstnH_Yt0(GP z*l>U9&?AOMUlre_KsY9ycW1mwR6mJeq+I_1^l2suW@11$L&=1P!7}SR3gN2s6)PR0 z{vkbDN&R9RGm`O5{{SC}F)m+D9u%zHRgO^-dXUmb>Jck*Pw_(>0}}$}^tIt?-OCmR z5`}+M0psk6W6nw9pgp|Q#JPPYczNx~O^Gu;AefoC*M!sCgMmzH@1z$Lx?C*Vme&6O z-QKScd`!;aaNS051SVB?(YKGCD=a-xP8a_GW|tSYPho`}%v5K$kBVblevJ6J+?sox zLkFNb9X{xLdxnKE*f&>n$F2GW8^NOWPbfku>0*0!M%7xvBK>nxo;WVsG+M z;E-US;*%WuFRg&7WOXVhq1!*!Vc+CoN^s5`QY-%eM@xv9P5J$!@XRECOgwiGQ2h(S zNqgw)cO;+Bo@oB>!jnI`6n6xCSA8tWE$^dU_0c1eI6>+QX@nX0hXlVzapIeGe?|<^ zAx~PemC{rv@H&)~>+TUVdy+LHG1BaHO#8pmd%AlHOPKacLy#pC^yzEZS_uQg)NXx! zq6~++BI2T(4&%W^IR|51;Nu8czV`O+Nl)WhWvQjAntc5?meT&fAhvfsx)xkap;c_# zkM0;{LzJ10C41w#gq@`qyH#BaTc;ut>*xqpzDkP-@8WpSpbT=ea8zSiWNhtSJ!&c& zSK)OMDNvV~Ftt+0b_zst*61n)Gb5&xg+;aD5?u)z5J&=A12{@4dWf2-k^mce^%Iv! z8j1Zty%f3`wq@5@!^0Efk#q9mUxf9aSYDEos=HGghnes(6&H0%CaNJh`hRS4UbB}8 zq(IyxQ@g11gk9A%Pb@z%+pGO~pPpIQ=JgG{O?F%bO@YsNVR6FY> zQZm*eHcY$U32{}yAL8t^k0wX>OKk^Fx+Z2qZxkc!U_2Fg%lkR){{SRYpO|5m_e%c& zd*$2k9&&<4Uf>e9-`$R?|Pz5;HL;ii7l!ml^EW zg{f4aoMw$7KZp1YgZiR3WaTGxhaS?DX**crYh_YvDQs*s^F$OBG=U@UlsTCRIFv+o zOlVK+(}#F_mT@oSeqrN_Hzi{MDMNP4&AOJbHk1*xwzm_#3QU1XLV*WS5lM9|Q5LW1 z9%hmct3{Z6q4Y`Z^8Ha)cHmx7$hb=4>2b z-qA-UUbT1A2j0hM3%3ZwQ^jn^pmdVZS}sB z$^QT~1<26bkvt28B;-%xro##w*A}H@|h~E#%tTTpiG9);6t9xCl&V?bGu5= zS@&>1453AJbb013@|(F^T`f1Fo2YAgBtNw%uXI-tokc>vWY$xS4GQtBs~)LRTfgIF ztgr&goFdq{?h;j@Bi#7+-1Z>nCkR#cJ;jO)mfp6^$qH`h%}TbP z9jXvu>w&#O36~hzYs}79c#4DjY4;}W!<#S+iRY8dJ*;DQir0+m?)cl694@+l*V#m; zno_)VND$dx!YKBuOK4ovSSBD1fuo1p{{RHicMI(EGFrwzpXJ;$aC7^*Jeq=v`)ol_ z=qhZ5F9eS_6Yq*+X>9%^D}$5DMM|}jjwQ7O`lx?!LR-6M=Nk%_l5hto+4&-tn1>Tv zh~*8jT9BQ8iAhlHJAp&RKM{saiI{5>s-|~w*vs%;#AA27c|>Q=I7@ye>Mdk*?7VURcu2SfOM>qNNM zzfK3{x^qd6@=9B(s7MZ2E>$9qui_tsw(^oy_D&~`2o9_uti9}63v)?YN9^~iQ{A6r zR<1>QzmWKL>}fK0Qm3w1^;KG*qOICDO@|y(%WFOYdry$-7xuF9iYMc8O<|+P>l3fm z`{plpt+3>Fx9s<;YRh%B6$mZ5g$+6$p!gA?@PY;P7S9EDSi z0eUV8Nb9GR4fSh{y6F|kI!;5*2yC=Xb-4&rT+#jT53s}pBm5+k!>mi`Pkx;HzDY$AFL{Egl<%c zKH(3vw=W7incE(v6Iu#XQgkUg#4lwH5e;=kP$g395tmU&0{yuC@xoqJ)KgDy*Ec9R zD%8Oxwl596O6ff&v=t_E_NF#{i0KXOC}`lYzGPdm__sLzLJ#g%^6yCfB})B7{3S|} z@arCpdjs12Lhwrq*q+~nq(>C6dJrHdwu;@9JWy$Li6oD-L|~~pX#G7*)F%p%#1oF1 zqEeXC#vU700ZWWIGohIs1QAsW#;I63lATWB;3uqg61s&quG+G()DQs>4a|BXtv69U z;Uq0e9SM-qC?wfwBat)~*-Yp6z%nCakrmh{(jY{mw2dqfd?(`y5)!5?YJn9&w7WSO zNkgu&sJ4hWEZy9^sz^pA`qCFp^wM7MZAdR#ZVl}lCn?NI0_eOVn|B#RRZCc&tdTEdI8_iQVd9)N ze1h?3OU+Y%;olN{l?jDQ-w$KRC})P0m3MNMt!4Ho{#o@Ixwr6=dg%4ffTuO$i7OI6b-Nm^!nVn~3h zStZd<7*z+TP_msp-f`%-7)tY;-NDo=BVw@!5P`F)l9MR(C@u6}Yjt(0PWLfA&8zr8 ziGRbS5bJs>zo{xdz2j8vp;~NFoae7ds=KyV7I%+Oz4qHi*>Xoz{{X{S%-+Vuw8vaN zfkqYdwCHo;0Q+MvbsIsV*xE|s6M6ptM|6SxA}Zx`Rl6KYN_p1NDeLTtyNMc)iG-xL zs*^LIn2P5~L~L-BoT{nr9YN{rfr+X$u`fMQ=lWt7ol^2IJyGZSU{^k=+>5_d&Sd-H zyvw-|!R@^~Vbx77suWZK;%5m}A1NqQ)u|&xBlkpvQ6PyN7l#srr+-Z$yvYm*2QKcN z!_*)j(+FOkEz)_qsU=?)o&cDE)1m|mkIo&Qi;(l2Gp@VoLOR9y)jYyC%#{gD{{Tcy z*9AoDA-cZYX<&VJwqI)ISaC^Af>k+90Y~)18*xN5mQ6)6u0y}#wZ@71^*q{t0>Zoi z_Q5g8tBt6tW22dB9eQi4SCLViNa<4@W)rU6%ahs-T!Hc32rfKSFR=wkR*q6hDI%cG zU)Jjrbpj9c@TLm~EV|lITdF8j{t^^NPqq`ORLLzlbxzv8TDm&k({piY@J{k$ge?j& z1Cu_lkZPV%)a7E7JVx*G#Da;RPQ0hf6IiCwYNQmPO1Ep_6EKAo(2y3Zc5TiqVl30J zZUqB)klipkewfPTY{ZT#>lr(v0cZ~)_fv0n%=%Mu4iX3|3XqwM?07&5)0~5z7I9{M zz_C+TP_LW|1ep+^C#my-KS}{Y$?8-IeC4L<=e6mT9Z6re*%JK`u8K;gE1Bafc&NLf zI%@=rQl>7Va8Ys_ROpy^OZQC1q82+sN{&ezMf#_f3MaANoqjLX2gPJcshR1wSIc(? z2G(96dkOQ4$T0xwP%>SHroB#ccdt8sWk4&y&Mjicjg@TIXewITnx%gc)2~^bV#+HJ z6=b(uQ?`Y)iEc8KwS!}|3Z~-vp*?7RT?s;qflYEzC+eQB62&E3Dam??sUVwbH%!l6s7Sr_0)dIQuZ}S$X@cHTlaFU zwLeAl%nyb{d7o?xCP}qM$A~3B`B!pvPnCKV24Ef1RQka0%QP!UaJuTCOZPTjZVOzh z)&cS+0GBLyD@d^dsy46AuGPfud1{}ujVu^N1Q;Qpgwz zP-7jgx@`(sl>Yz>6x%}Ks({~eY@HVVCrwB37Luv{@J~oKJW$;YE@j*j(_CD`0I6-A zz6w5HbWYqud=Mur_i;yC!0FMeZ3DJn6vyp}zJN7%>JO&rdf$sM!-*w;N;(-tjDRRz za!%z*R=Wi>hUSwq)JQ==q$tdZ(AGTM(i&(ceGY~v4MIrdqRu@sM$M%3GKFhU!epe3 za87#$-)yz}qEZM^bChuy;8>o?eTES~sFYERsL%LaJ&^7~6eaaW5s0`{NKCzwF(*kU zP$MkjstOyrdCEJcn(07F?Bl3Rw+YLk{{V_54bcwywMs487;i}rx;lx$8u?0gfhhnV zvKF)}oIoq0wps}v2n1?+!%Ki%fL(h)fY_NHQ0XNJt~5d-sny7Y%6<^4HXTrP(oyooSJqTr1qf17QFu?b7`O%daD~-(D*d=ZQFRyX z!a9qny#{c#Nk#j_M*_Kf302FanHj{bNQDM-i4r&=JRq(KsridK<5CT%r9{jSWg+7i zczU*rKb43PsP$oH-^%j~&bsausow3@17PS=~Bpv&1C7;Z=_1(309t)Sg*=M(WC z8+X%=F;|aWTBNmyG&Gk{i#4tITo$!!IdJo@DxX_Y>RAGVe^- zoXzm$Cwguc=3RQFmiqqy5u_zk0Cg#iMjyB$bGcoj$Yv~`%gi-ZHJg~+wyGIZ542?K zE&_$6L#QEAAjZ&2DNwSBRjS0RYLolkF(Tq>ln~&Y`V->_nvSJJdn1=2km;G2m$oH| zEm5YPnue2(k#$vW?p};k*;#lu_(Y1O7%uRJvDJEimy?k#7Gd1TN?jiD9tU{ZFQx4G zDCI}|K=v5Szvo&u{Hnp>U?mjN@t^9A4XS{vXO1gv8VskW{SgP%Gz8!ztdcqyRSzO3 zP|=VBUo#O%RDv`WASqhB2#6lxRV5Ot%19&~VX`O~0a7;wat%ita>g}zOU0{g(5l^h zKr%|{>XeWP_N8DE8h+Tg=}qlep8Cd%i$ZCrTG3aOt8gfGY3dg_Oc5T-6GPchzFV2x zt{|kxGN(b9bKYki^tIhEI2F@*ddS`CX*nHXj)5tCQ-+^4SVDh_W_oL^C(;Csmqf0{ zY_KM3NFFdbvLSJ~M^T@+0#ow@b=O!G_9*t;qY30EM$z*V1E}37h}l&$>IzU}k<^kw zdS+sO5r}_Jp+pOk67yV+>QW_DAa&6H0G0%Gfbd4P?ZGHF%5SIt0DAWiGo(W%IX~;1 zlnzUOyuZR?x4yd=oKeeBL_3KxIQOR5Bf3P5|gOa(93O- zAH_-C(?RCHf8SCAn7>}Bxp)aBJWO6ior(VdHku~Mg#Q4h@O97~58?j+=2WFO9Ch1;Ri>%WlDym1 zvgKxdwbNJ9FZ!?3J`n3_9w}Nui+JA0Y(Z6*7IQ_bnn2fH2Ak0Ae#Cs(=;~5UB~ViQ z0W+LS$^spZJW@qPnyrMHv%lTaBb4Z-Ym#c%aRh?jPN|5fr0OrioU5#pG#1}Oi2Kq=flN{}aESWG zKVq5}TD+Bw&QM(%tw8)KD%xSqBZx>rDlNQnK5J#%s;fiGX$RtYnL3a>Ow+7Nz{edF z50ULm<#!OvYEmAkQ%=l@tE`V$-r5{{Xc=+YbFKDE|Oa z9w@a(Ik4yjHKj@L*H70Rah{~lt*;b|QO;-p{{W6i2f}lR-Ptq-C`f)+Jkd0;oB-^wW{i zHk|H)l9Uni0z^nCEJTAGI0QSgsSdllW|1}iQX%Wd35^fWqeeJ)>>ZT3Timi_*y3zT} z6sGRDJw`hEiQiR9;+|@LVJq;4^%>1qHi~;|8^Nq{hYtdq=)`fml}lBa-dxQsYCbAu zJ{3&zNbn09q>o{ad=1^M)gIiv)MJup$#1-V3QD(f4{|ttGAO8XO1z53fgxuKJRk7@J-hwMGNXq0H!E?vo+;-C z@oRtqOk=sV2j#Ce=7c0%Err$;XW|edoOb+2Se`-k{{RXr7k4$>N0xX)ns~6(Q`Qc? z`YAgfzIUO)bg5mQqOUc>fCaZGYTLit+(%iU(yeZ!d(t9773>DI<{qV6Aj014dT%}= zpTqA-oQvF^Jo2wt)pY{ZEB8*rz;*(x=1%n7NUK^EE|HJpD9Z=QH*ah&nP}Re%dQ9f%XC3 zZz(+6ST}5$wdpR)W9zV1+u1vW!S!ybr9_YU&_sQucI`t+RM%bq0ExFWl4b+#um1p} zRd8_cqF2;H0FFR$zqFxOjv1rCTY6!~@(ha8LK{!^g)5areSpTN9p%;2#dcVH$DFuE zI-KrxKb5twypPzPb)Q8n^A&CDhcI_b#k^llPM!7X{Nc-rnM|g1P^6h3WLbaX7@BJ& zt#8_?$Da|n^1*E^gU4^&Y41&DyD$1;V2p+Gr__7-x}~uV06Ol;o&75wPQ54iD!2Jwzq=eo*p53Kb-va0IS_M?06%SLOrll%<4HYQ>tW3yEPAcc`Tx&%wAmx&@1kiEUYVg3_b3b2Q`!+v&=_x=ks`5&`B?D%G_*7+;9T(cW#l0xfK zYkIzxNzk8O+4Bv7?@);wXb#HEW*~h7Tr2MVvsabA^6WQS=N@HmAIp-Ehn z53qj4EnQxg_jVlQ99jR8bdf z{#6~^m>{@G=w=VF#Z<8?n59iODbq9Ld?LKUtYeWA4E57U6Qrc-qfE9=zR{$jP!|`4 zGUB^X+jAd;4Lz}cX0uo7XSUV`(j+ECiA-A)K&fO*tn%PjN@O~NAr)8W;a51eRH!DJ zrky2aippUqocz2Y-$z98l1a|ut^uQFXU2NR8xch2WKW7pcO9t&{3b)fTENe}hk)tHaC1s(alwY+JRidb>WuiT>II}8IkZ4@BYOccnCes-+vNSZKW7?&nl){{T>na|!_= zAPoH&Y>rNgdxyrCD&K#}f|j)fXl5rKeUZuq=-A9U zj;n8qab{}k&_-Ts>YdiA3~&KAoUbbQ=zK7Xk*RXU|weI`5`l3gs$)5FdOvPMmGmpERzaME;l`suu{5YN|5PQzN7= zUW!o~2-SFaz@ku|VO$!bd_*TY?vPz2r@jhnO0AuPf^Xk)o1MwU)ZFR|&M4|UAb|QI+XpdB$1e}esVXT4pr7=^4CNZz zV7g90R+(&Yq>iR%SZ7gp(S)f>oL%V;!BC}ikeTp@iOOlQ1w8Fs!F8fqO57w5ZjmEy z5RziaPd2&Mp<8z-rhRphg>>Mcor`!~ubBgzfOMrx>Y`z$Fi8teBs~N`Z#H7O5kZ!F10ig#kK`e3U$W(CFf7o2*jhCVq;I zJ$}C5Y)R2-kW6Tv6q#~eZ3ETN`+ueCh%t zH0ow27WVdPrdx&La*8(830%jN5+i6;lRl`>�*f_u(?qQ`RSJjT#}J439y-%k*@q z0W|?D2jV1vVo`F&ri@eEnztT%062uy{vQBL{{Tdxj>|MD8G>=uM^`R*uXA^&Qlx|X z(}ACr@I^BUqr0CjU8+G^suX5U)3}4>74u|@v5yqdx?y`p;5OMij~tJx0=>C{9%?ZnQ>~k5Vv{l2Rq*C|V|)f{Ce{V^irQ z^&(ow6&pz?gZB$_JEivcpV0`U1g6zR_KhI=Vt(02Rdo_Ox5ofG8zy}OH<%?v1;(kx z4bwsgRT4a59OU+q#eQ-15Isl4ye4#rlMz>LbWDP$DpMr>?BNQLwMb89)>5&Lh72q?D1#m0aRIBuI< zQEC=GaYCieALnjH?c+GL=iFz1cFlF5aR#M?sG54F(r4+s+~lBs@dZaQ4yOf6ki^Gy z!U)&JeU|+`eyw~b-1a$b!Ff>(VfpGBLstt`Qj!>`U*Icc#Th9Ftd625eX)_XX6wtZ zZQo@tB)YX(Ugf@(d=K0$9bRRFdRWFyTnnsS?b|{fQpD!s6Y#6w1!Mb?dsE8&P*+$O zCmhkQp5EcUo_)x6^!>)Q>}|}y6QNNlzuVhegf~CLwxm=g;~tfa?x-dJR)u2k@pHg+ z6$+Lyaa(0f)HGOm1cw_~DN;x>GXw%7LmT;pRcBEwBB1C=m^}IJPpoNik$|dJ^1x_mBx)ctEGCj=|VP1pT6LQWLPUI-`ovBB({@D5A{87by zO<-yPQM$js`(r5o0G(+YvatAX;H3WmG=8YiTBsbYRe?j~(4QFzeZnjhbxZB24c=#@ zLg#9lz=3d)t36PUdJ0cEBixG ze#kBc(A5ob5PJwd3+Yp~F3ht|=etVdF5-B`OCzdZE^c zGmlBecY9*^9l?CD+?|;-W+lGW0CIN(^2Zj})Kg`crBw=9Brc&Xsm6rAv?G0GnYWl| zRw7J BBgWvZtdQ9{YwkHnHCD6w-xkljMmKjx%LN(m>S*ZLs@N=~Bh^TiE)O-D$N zMK4p(IPp@ zQeH~YaHXeC!T3x@>tsN?@Lm4^GAw|7LnF|{2F*%mLw_?Yq<<8TwmM5~6J0r?6|AXg zB_aJsnuJ7cf=H!Z47uG@U637hj`~#ZsYBD|uB4EcmFM`P52W=eqq!*ZbIODgK4AMm zFrP>~6yH|w6r{%@J~saVi69QL)^I1%s729Ajq*FhgX<|L&T$*<0Yyi0|C-HTJeTsL~l;Nl3*Jm?SNdBz+e#ijWqhq_EPHH|+a7cQ@A8G3lNn$vun|e~h zc{RlA^Cw?u5gD*j{c`N`ZBRcAO7{5bWf2=PnlcFWFDAUA19fI+QfK4th#h8}+v+2S zliooj*6lvkYZHAEQ+`lGnNvy#cDX2?qDqDf72jNeUpLXFbGsyYd_($Se@-brx7L%- zIaw4+6?SYWoo470@$rNh=O-M9Lk?oLJ2J(@IG-@Ikq$WGHBV*eAshbyY?O!qis=FU zq5Ux5qbc837blzY#v zL_zvlNZaz4={tuhm-x_-d?)@`Lye$BHy~4vlen}<51p5p3J}lJlm7rwP6|%q*iMS7 z>*Ll2IMKy3>(jQ|+vky0JAxvEASLxSQ^vHQqyq>r>1Xveq| zLC6XwKPjuVuHtCL7dr5sDavgPTdKWTsA{U4UfsLfst7~qPzOaNV2GGN_WF1zBFwro zR=S?WnB2YemF&wkH8(C0w9-z23{@lnF)os| zwwiX+;xzaE-F-Y0&So+`HZ_@tQpJ*#Hc~|MJ-aix~s=pAVv3Dzr({3oN#Vb=y zd)NV1=PcKivgnW+hMsiKpo^F?FZ{#9Z@*9Y{wU#RIrv@3Yx%aNJV5dP0MquW339$M zVY@FL&Memq4<&4n?S}bpthH5%Xn%uT+e;#TAWAHwx?{$Q2{AK`Mx#wZud=k;?Mm#? z(ba&eN`yWXCY_`idwRnI!mpYKgR0~5!U0y}{%_k3Olzo=Y3-{79lx*63u`K)bfr2d zt;2FbK-{R4rkd%^Q~u9qX-jA+JAJUdBpkEzDbF0RTZu?pNhd=xClUzI50I&%%VoB- z+5%eulh#8|WJN_^EQLul6x1vPAD2Ri{7c$B(cG#oqE{%lxR2sfpaY?rL~s;K&J!;n z&B3%Ms6q}V^$@45cQrOT&Smyi zueC8-KW@VPLc6ujk+~!9Rv!`izVX^r-Mg4@i~j%<*Aunsn))SGP*2DFO%JMoJ>*3N z&u!CItJrexang>brrZ6>*kawscHPdj2!9^SYDGwpztZup0Ka1sW*_>GZ9V*CqiTeViueiqv9uA=n_Oah7P?h@=<(ogMt9$I< z*+l;U@Xv%hA+O+mQzzJB*@dTc6QYyFK=}tI<+$sGtL+t4wMd?iwBuvjsRVt-3~jS2 ztDiI5x@AAkE)@^)+7Imm`l5fP3Sn}SDml6Jtf*CKEq*^Znml+=y6wdyIn9*`B>d)v zT5g7ncu(u5^-ng{D5wg#&SP%#{SfhuOnkKouU*U)4uwjrli~3Z1Tw?~6XbDCYTi?? zP%^3|GXA&uV5o8J2_GwhY*q3dd%&NWGak|e9?N#LAq`4x)@IetXliFa#CI9W3v2;K z;zDOv-)gF6JwgE%~r zB|Ex9Pj0lS2YR?7j&|pB3qL1wEQJ0Lp}2mHVh8rmf-Sx3%6vg1)bO7n&?QEdmadJX z_fV~>GS{M@S+J8ZX{wo>(Q5OqiK$zO-6#*ClH=|)0sir96Dc1B0TsWdyYmHN%&cct zIes{OEkZUoMAdyOgH~MlT}bvAXX(SiOw5zoYt5=^T)1l-tkH$JXQ^EFwBkz9KVp+8 zte*nc4HfOLcFxnTsj~k76lPW}@7S(Nb)aLR=*h#g(iP&DYQGZsbtHS=`%yUyP+?4 zZbWI7Q|7h`Jj#(@il(xWx1Iz0FCYaEz66jsZrZ7!k`MWAEGj3sS+LuYUdjrLa-fs& zA!XKqI`nf;AXnMNO{lG{@|ThE4+)u7hSes2AzamJPvAzWQ6A$JjndCHhod>+DQOBK#cY(^KzvMpLa~t|yKf-iatEn1P{G7NVodkPl8f$LM zx{dk2raB*;!3@sz?hA%jC|V(1G|)tan21jGQFQyh<~nG=fBHkeaSH49i}eNiMpoT3>^FnHnU-57 zNnpmVcj^p-E=8WLHx=58U*STQy0-$>9o&ie7%v@lXDj+pJC(kwYO;Oh+8N00+db6X zN*V!DNGIAU!-B0GhxPU68#~oj?hx(IjJR5n0HKn_kX&6tQ_^0TqDTX*r3c<**=*`H zE-TB(XJ~Y#E;7o~n6Db=9lP zt-?t^Y-R6npF6{(_=wn@Y42J%AZ%^rV4(0C4{$-vVKNFJc}^AA5>s@v6 zkd*Bbos56yc7ja5#Idi3_ks_wc@s3B6edgm0FM6v{*(I_iMvG2O*l)QjMu8Jz8y95ajAmNCcYWDo44JC{( z2Ssn~-%(Ga2X(9>-OTtsn;W>-gon|V;)$gVsOTkB+beEk`oVfcGD&pJwL!lSzZ)tXKknA$RF1zHtsBlo=;qfF=669tQ#Sw2@Kj{as4ByBu= z7FBY07IIG*SG{lKo;H`P-?EMa`8!^I{I+k zL+-TGgZOF-pNQ0ch_7@ctF~N$#U?t)N!kR6_Czj^O_73KP>c!84Nrs#O;HbWlrgfj zqiL8R4LoAz%VnL&4OFo}>Db}BZ^~a=>rr*&E$eq+&`e+}!3CNSE zhHXo4a@tu!w3!6NtYSjomZ>lIN^!J1>ZEksCj`YY1qC&u9Gnei?*Zw*P7k<20{ICp zFgs+f)?IC0-%%yCA0ea=+rdNu#yKc8d$vAj>m@V$;#SPl#RVmli07$5@EaDDlc^h^ zz~MyKME0-ArlWFmLO}C{Omm1_J8PzJN!QE2a?5%~k06!7bnDdOo=~e64HiJc5Ztp2>I5&P0bewt3 z%j#FD*s_fYuBDUkndv#7II8B=Tn!L&0FgOBu4t-~i*^FsZIO2?J+hoZHPBPC6jfC!J~H+|54xyQ zh>(9UOoXN90*T6ZNqg#RFVO^-)h8ku3j?o13JPTq!Fp%}q191Qd$|YjjZ7ga;;1DJ zdV%beH9d7Cp9tiLL=^&LC+80Ug%L&*TJvAsD5QB#9$Z3<#0m}C*WH(>=IL}0!y~*? zn-Ciy3#9(^N!Cx~U%f>LlB>B_zN(|SmvXPg#VhxEM^$$s*Av5Z3WV8m zsWQ#T#O?ku15St87LdpleAarWwLc>5+x%INd|JhFt7W%Ay~s}zb~z*O=@NZeL5on5 zDl^KpNCynZw7~xWROKhq4+M_(Rdak3>Ys?|;V641G)n7W5@VN3gXU{dD)84xZ#hlr zgiyU$wJUPO&ru9)M3pT<#QP0*UTUiWjT9es<;Fn&Lj;Cfc8#0sr@p#YN6K@ zl(seV^VT>fmW4CmHmcg^!cv)nK%c%Y?BnKC_tNsM4dVVMdcBsNaleFtx?*zDbBkD; zUDh_@{8d?69ulN&u&+py<+MQ!h$^>Ul;&fJZOouflx`hFlMfMv57ZwNBCuOEA|{}O zl=VrJMq5IWmqem2(O9WB{B4bguAizV4XQyi%5PTREfjzTWTW5*z8nElHI7X?hFeNE z1{xsyw1o{R#iS;EW=U4yl2Syy@Z>`_O^U09!PR_k?N75vh0LOQLwcpQ_pSQvfc_yk zVIU-`Z`R+fOr=UyVn`iE;jA(4(M-mYnYFxuV5LgIc@B9U!_FfUX{s_CHkv%*PwSimE=k1btfRy*CiB^eFj41e~j>>H8z7yOnz!nz|C6y&_;zA<`rz z5(ULMtOxk0OuCW!;>Q~56TP`ROze7HOl5nW0}ly~X`4JDT8Hn%c}_*sfH>>4xTR zG)dc95v;=4B+qQRr0X7ujPAR6N5!F8OUx|rpAfT-wyNi0u3ft%wbfXwSeSZ}uASO< z0htHkbt9x+i?%yrt3#=w(Hx|`P&lM)sYxk1CSq~i$&Yv?GuhdmCZMg|%e(RVdvf&?+8y0z4gz6I$OCpki*=HkrKlDTzMjKpz_(JxS-S9kQu+;<;`J zrFs0p{{RWcas@YBuYWmlUxFC+&?#8@mD_h`i34!lPMSniQ=<#Jx`2?g*QAMvdO<4n zH;2_JJMK=V6hfsGrs+zO<8X0cw%d-+pa@s&h$V)rn&3F6H7c*}pN7`*oA5gnB`GFWXxO~fRAlLN~MrpjhqG^JK<)b{lHmA7UYKfDWxpXh?$N$%&nhjEz*CREXwVnbwsDa(a>#uF#;7`UE&x#G)@HC#n0*N`R`p)jB7j zE~x(iL_47$f^RuS8@9Z)61w-6JiSE^+Y074!6V$_l-jdWCqBwTRq3!AO_s5IKY? z+jGmhY+Y`d<~oHrcC@adoN4p4YA>K^sUT_x*$r*D2)5p}AT!9Qs3bR7Jzw8#M5RFa z5UE6xKms9>ha*4EwaA>RH-3})A`X|SM7jQZt{q$>?J?ao$ij2F@B)LXGX34r3!nuT zLWgUR>e5f~RDAU?h7gjd^IVNtp!F5(JBXAw>Dr^}rA9d_t*J_-M2`To3hPi4`l(%x zRjdF5P_Tc_oJ8w3s7=CGrsaB_{{Z-<2@mUt<6;u^ie0AVnuL)mrA&Wn5w<`mpL#-D zW0&ijAxW+vc>G_`Va%`v2yTc+$2M2DA}b0=5|}0kj~vw#?+Qs*a2K{5-kXZku+0k! zGf5iE5#tUR@kIl!4OCOgJh5%$`%$v;1+IrUw*X~LbfaRdC&S@Wz^ET_f?JIQBkb>{ zVW=f$KP7P|9aGdUy9;Kg52~7$8=|Rq<>%B%r&OQ^us~18^6^@WJj36k#eJ#?UDq|o zoGc=_i`_GS`|#OH%)hjs7#ZBnD%G*WAf+grHm+5tXIW>=&#Pj zL2-kwttw{4^86XOrpq&|fS zhfqS2OuWS4_6E68ytr-@k7>75Qvo3&o|;N<{+g~Y3H3)-`w1aQ`$t2hG`5NtJvgbd z6g1dE(n^!3Swp%ZFp#;a$tIJpxK19}LX5l=hUQDK{{VS2`@M9I7K^zMD$ATEN^)9A z7<-}tNQt|bOK#&dfI%~cz7Q1WRNSwcgU+I}PMQ@%U(7Us-xP15o=5z}N$ zwWF7eez((cL(k=_bo&D0sra%uDVZd0+4T00!m7e!KT%%Y^2VCCWtUg*y61%6x8xKK zQ#O<2UQ$%a`xODc$?Vk<*kwC>Ud>dRt2b_3u}AaPXDxRTR5v2$Wvpo`JGGptltKGM zBMZSpN+>l7EXjLIwbr3(!+8G_*)l!l|n5kvf6bLf^ z0JKPhqIf$b4{A;s2~wn2ul1)f60xaR8ssUxIC`jS<2Ws?qK)thFT&&&WL2-Q=?do% zgnJNzt8mgs+XO|r8d0L`CDkTrOy)XDEg=;urs`nPRioTf^zXg$2LoJrj`PVhxUJpR zT&l*gw;4l>y2EMQJ$FjdQdi+FkmzPTK-GhvxnvUZ(NI*;s+H%MQA>`~tw?x+2$~5@ z!SM!NV(+E`w{6!uYK}AlWJsD%Q7Dp{?1yxeM^VusJ$xcVrm8m(QRcx+k6ktPLUX8I z7$Tvb^~}Gy(>zz)hkl#1OY#HNP+m+&jfT`#Va>ab>D-s3t&*P-YIU( zq)jySO#n}`aGU`qHk&O4uXK2*w&{T6bP7?qVQsr1{{V@id>FR{32q)|$8NkOA zUI^BA5gbzz>A?2ZkE*JnXIUp>;ldO8;CEoF>PNS{9MP`6)R9fZxe`>iELn%nk)oI? z4+RfqrUy7;IHHGk#P7;J#A!XZ^3Fo7wD`lo&!e#}3Yq%6kd)hI-qNwQh|ar*xa1{RbwE_@(_H}rkX8_oP;x<9I#%h9 zrCUV0K{{3t0SW2uZjf$P8tLxtMw;DacfWmq-`~%j=gyrubIzHoCiwPP2QDIODB;S* zH0$60JhbtX)z}WfxTlE%vQcv_QC0kU@V6$JSMguoy#8NK)y5(TK57q~>yuAj2fQb+ zxqDN2MY&HG?Ee?5xRtJ$h3X}Q(ZhAj*WDZqATsqYb-8WAo_XkAVT9yk4MTo5E#@TV z)}Nkjw%F_^)YHBMwU)|MS5nY#Trkx$k$$!@(H)h>l{{a`iM*l%UuJx}+5 zI0Wh$!enWr-dA~sQTg8>hw#P&YC!5nn*%R?`Be0ju9Cjj!85S41;mo#EgcP*K9&(* z4_Iy1XyxFDPV@?kQo@JAqc>&Lc+ZXyN#iCZzC8OCJ#YDav;;L{R}-c}igjY(8rw*{ zah27(sRJX3)k)rr!EKhnlgU`Y6{0m;EgD_t%IXr=s@B4xqvXi7Qg2)DSvuZgyq|~- zO4sSXs#ZE#Y)903+fM8N>wi>4E#&N`XC=i8?~&@So18pJ3khDUTe$|#Ae6USe6^o*)gQzG&UyvHj5Gm|HQsd84Shx2ihJ7$Y0n&n4=3&?V%|mfmc?>|$#m<5i3t zyIi#kH(0?bc~H!%i%MM6EIFDrujJ|H`9Q|Iuij19R?yWi9p=M`)v}AWG@>r& zCT%TsiAcO{z|Llyl7q=iK`!i32IyEX7TJU$>^TNKZhTrOSf=e0h zv>XX44}CL;U*8{FMgPoIUQVU{Amo4i;h)-Fm~T&i+$@O2B>?#M{a}p$k(sVVi3^L= zJ%J+2?XxO~x9|f@Os0LRD1Bc93I;MFkor z%Hfl=#jM}Smph5e%-W_i)mbprvECVKJX)$Ggc1!lsj@=s8J70hNzQz{@4y;dtFBFadY!RtEs20_)Cgi162zD&Fd+Dr6r-=9(FjH!}*R&r9Wo(NQr)=K+5sJ`-x4xkByv5?Z}_((_E*_wyx6=U0gi zt=r0UOU*u@=o(+osWT^Yt)(VQorPTLYIR7q!Q8z@vT5FaB5TdXu}N;zdXP|GVs;B= zx994rW!fdvFf+JUCi#OQhnvhU)3_QgUn9{`99?>Y3~p68rHIP z_NWZYbX7`OZeeWc$5DR@g;y#DB zrRw$35h_+^PXH}vdNTs6mCoC(-`hgqw#F{hkNsy8I3{{*HGjRt%EZ~9@Rrg~Cc^_3 z1#OMaR_cyEl~|_ib(&|FDo1fJL^yQ&P08~Foe4|fnp`dHDJ&4ZRAxWczj>{$2h`vt zi{@&}Y*_V8Y-lTCOCOsrP{w{~d68>SyHIe)2~bqbAQ1TQ{%xwQhKz1>EtZFhDhG+o zZ2ScBH$(MMX5&FiiGG+wO`r0cXKxR$l9#b=Rtlb*ICA$NwIv3un5~qTbgb@3EclwV zC4POOT#xwtX=iG7z4Xm93*7dInGOQ70-mZ4(^}E0_*l=Qu>cqLFJ(?|iN$2&4had% zRJG_el0v0$9&P01A&->0DQ9zx2ZPuy5{jqZzAb$yuLjd-WklpapV7bgQQooSYqs*Y zy}Y{vWY5BTrbP?837G{YdOb%2@)(>Sx!~tLo9Ln764xYjD@)+!!Vm3{w*XH*|3S(h zs#O%z#?Cq;rJP_qtUWMV%7*nt()}Po@|kM6_}{;UOePvO7H---K`BIKV`l$6-jIsJDIwsWNr^}ZZjgLaP=HgeH3UAyo1>~?!Xuux z#S{0#QOPr7VqeZMpT|)uiOV&R(Ry1paRE-zJSC&Y3hw-8u3%O>w40$*=n^%V#CjMv zfgeM5MdJ2ax`M-o4ekc4aP_k}~jCI)pUWTVr*dqeCA4p^@c6)&EU(@Q)Ed z$=-g<8m)`GVP64z?z|e^D^FJ0T6#^@qMaC>*7R?TD27(XJ%U-`nxaY*1&dD*sv5qh z1zQv=>cEt459PLGhV6O_Ut9G&5z6atJI#+&hvi;UnF!aeE9)egepCyL2~%0nQU2{2 z^wUY~lAPjD3@>z3J|%%?k-Xv63SVZI483(%45&w z%kY$o5Wtafx6?yiRsX7?F)AXirx#~SW;A3RKM7mv-nqObnA~&VA3p$ppn0xxT`i^H zPffbY%;4s)5Rb<&CW{~5*Y}9imZwkr)X&(h9IVy&;z``}7;Xd^xa>?6<1q_G)t>4I zA0`{7DF;qJ#M+L0z>724_I%8vcTW%uY!Dfoz%7#`3VluGp-mw?kQnXbJuq@@syXTN zELj6DgQAYsxjgys`NjG$EaCCY^q$1M^eIJfF}z#Y#k6U>ESO%!c*Le; zdg+a>>ks%R)IY=~nSgO2_#lVnEEFfqxgHJB^S@w!O{&4AZ~MyOUlWbuhh*(X@Qr2M z@5W_MP`rBwMd|Jij~sC3Yo7-f9h;*PoGP8ncURu2^c55?zu<6i$C5*de?KK`LIVsa znRZOx@F!_vUnWFIxfc{RPq$n}$yeANF?qt{Ow1dd!! zw_@4An!+=+L~6GAlNYdNY^!4p*rEwQ=lL z`a*kS1{kEFA?p~@+ToNtfg~LmSGKQQC`HUFu&GrW+OXg6@|Az=Oo61gYk|BmHr=W<>{kMp+_T$j!Hqp%47oX?W^TD+f9XN>s$l-N`P84l`Z|d0 z(mq} z3@{XQbnK(F_8BGEm|k{7RsQJ9AO}zqTwck|fCi|JpK2*uI)xh$XDOS$3%2tp-(D8n>ps3TekvtcD*BoMUs&V)Bh_(hc6VV4sM36iM;t z5d66P#`fexe5(mh+v)QP4JA2ZtV;-QTYyrt?cB@{8|v|zSpsO5MxF_spepEapuA1k z5+rn2ot3#X{|60lSlh7>UJbN+hBC`1O@fLIAiey&k3svi*OjN`n`i)VZRfsb$Lmxw zUMw#VsH8$!?u+WlvUr(v%d~km#`rx_&?BbQufW$4zBxAGyo~+ z8{5w(W|2Ug@oU91fbrp!8x1JNg|8MPKc0gjzf92pOm+dDY<-!y`hy%L$c{#vW`eIX&rBfvW+gK_I$OD$R|%N>WGyF%GC0uy8Ymi2bnYt9Zx|tz6Yjg) zuuGf24-7>LSsl-1k5Le=3__+oTO=In4_k3iCTVyD*=vIAZa!$hiS9IkhtC+48;trM ziv|?zzFijDW-49U<>>5>6+SB;toSR31_YCl1hm=oZAx=fKEkwVNWRg9)=E90aZTyl zq7|_8Bq*?(a#y4xwej{aVW-z|O@$Mzdl5ar!EFitfHAN+=Az+uRUJb~wP|Ows zlGdd?tH*W@M*|2ur#C!XKqaDvn^WLD&DG>RMcx}=$(aY$dvBYI3RKDylk4a}JhR2S zPe%hj{y)z@E+?Dcj{Y*`3$}>(fMTs2rIxakg#@J)Vw--|7+#eb*u(pq+29P?6+Ks5 zve*4>?`sQm5u=G&I1L@}w`4;ETu zUBOqiv!*tVmencY+Gt z<*GTY{f8Q%#U14@-yo?6fUEzZI=jpe!foW748;tD?D9^eF{2tw4(TC^HyzdPdDOT* zLI(QK@jp1ezSQ9?1qyRC(~(_arfD0h)r9(#HS%%;4M;k7eel-lp1gOB@Qa^+a1ca# zh2Bs@_VFMW$Iu-OH?1^hYp_aJ>ySU1gPNA=4o1`>cwBq4l$2|%FIiN~puXB2)f*!gK*20=5Pc&G*?SgZ<~Y6sWD-k{ z6sG6Jd(W!o#jos+2AD7it=u$ZUJ0WVJ zwGtahEEzlit3{7aqXF|Fk%iDJob?GuXbd8@zfK7|2r^PnMg!^BB!W9bOP4X(t9Xzf zK!zoDY=qro;xI+W<(<%(egxOWg=hX3I#~rh%yy*_c1XozqEzV&0Bzeuq5<~>tV92O zy8Hvv{3V?$1#E8OrxI(@hTLLOq(6WUX5wtXJc=|z7opEufWp1WeXQ*fN~E+w+Ur-u1kJTor|fRG$ozlc?(7=uc2o$lHA`h%k^;joSEe~Br15qtc zcO`ZfT{mqtK(HTzz3viipqo4rU#EvIGPwKrWrD(v9viXZ@u7mQe*1vwO^%i6$A>P^ zfuqf3Lfz+)%hw7!Z4sB)NpabKf_2|G$lGzL>;g#BuB@Kh@A) z)xGoXt&7)KbQ6&tFAF~S>tfq-J9k#U|c0`DUluoNB~y#^9h!%Ttbjhz!lyl*$E z1~B1qQj4dNtS5uki8}#~8EC-tFQmaTtlMd0jzisQmjqzw* zH(2GJ-IH=G{p4FraW8+Op%D*GVL;r)be z0n1!Bf%ia%_HbYa_NLh#(^AA!3!}KH>;X4B{@SV>2(DI>h{qIO!vnq^*0X5AXAhg& z!(knp)Ez_b1`$U6TFcd&8GUAz3bSP*>6!S7cTR+{aQYA{2WX1#OwH%>Unia|x*4J2 zS7L@U+Fir$+M;fW7UEk|2wT9-J)2-a*l)^ou!M63<&Xc!Tk}oZXk%mud)2ja^Y;>1 zt2sQGjE~3RVN*(i@G?!-|F3I0+W`2{11i+jhbX-m)37H}8TEUxfeuY$kJaOW+to9* zt1;NC-;vs{%Z|M!AG7^tK3lSBRQ2_EDx&=Lko;t1(P?bmXB1S&gWj{P5nxS)Qd%xhCXEgL4Oo4OONiGp5_zTOTo1ui1nATNzs+!=XT*|FtvhK zzb4uyaBF7YQTa{f@7QULW7VhW?eC~zT?nmM4+$MLm?PciWR8x`i&-{OH;Jta_Rq=@ zQPV4|M;U4>cX~z5=jr0#gX-%q5pM}AJX=Kdx2t;I5xiNcOEVaXfUI*5*DFXyFYK$l z%|)(~H9G4~-uiFb=!mYwo(qppv(VY>EOXnw%W8>zVCv9TY3P1xJmfs|R(x!Il0 zRA8&M-+!oT7kT1LPP;Wd&`uVtQ9t=M6%p}O5L?*#i?Sr&?Y^9t?`*Gsx$D#Jo-^%g z4tV+iOF*>0F0N%&+HmA2ue)MUvQJQtgN04%KTT;m!kYDprDzP}EVMbomVZw#Xp4+V z*(1(h(x`=zA-z7S5=%%B9*0z z(Y=W}&9a@Nm*MPV|6WdZ+1dLNGDjJxbx7Yl{EX2p4QRMnLkXpOG2z~++H z>rWS95E~{2RBZYF z+9=p6P`ik3m$9pUd+{u0s^x}c+WGdF06tjStTd?GwtU=9vMZ!k9?{FW%MR6m9!Gal z9Bp0Tw*PQ0%o*OM^OHU<8F;ZV1;wCE#mw57Nh&ubBw`M`S@GbAUzWhFj%EHyz$7Ws z4U+nSBYh*#5D_^v(4;}qdXvSb(p-cKuT!z+miGpL+f!soM!6JP9J~joXhsfS9=nG-4@zm=7?}TpbDXLb>@fh z{gBBB|7oKd4@-J_Y(#!CTZ56eASB*@E{jIR0$YI&HcJ9TZfgIhODw`|X0UF`1?n0! z;2k0^N*uoQ4Yyr`^-=YG(Nh&rH6EW%1BR)j-~{JLzB45)m~m_aW7UNWhQ?_4F!4;n zZKnIQH|sh7^S|!|lg#ny9;oxO(Euh*jEf6KIxb_vJr4x4uUEq6OkBc)OHH#hAlIW3 z*_Yc;!$y?LxiOqzA9J^|XzRuNBe2tZMV6tmY*UysSsgiaO#G;SnZO6M+qRODShd)t z{;(EVB)0)eB0{o0->rmV)IFZZ(-fn7iFLd!pBoe^`mQ`L=9g!=CX99EbNsrB-MN9o z7)K`rhGO$IMIsW@{-E-VCoiT_$*UmE^I-7sbbj+(gR^yD4&Kd-mKAaXC>-rBr$9?B8yfw0udVOC_pet#F{a6LWI&kJ!+SydKu)Ush|fF!=jmfaFA8 z--^RkrA9=dqu$g+nt64(N}-mjFR z0e7ONH@Oh?@35eVgJKjoA=MV64_Atxh@z|Jk=AUY2f!;Fv7{!tAzNj>2%O ziyCc_T3W5Xm<9SuP_}_)3*UVC1=sPd%3_y>f<5XN`}n3P)Glbkq>W@NWTJIspW0$c9Sc0#zfe;g`e*Z( z_y8r{ii3C=_A&)#{ffJ~<(kdbbQ}aK z(fGa*xVDRL0WvvTx^UjI=WH@Mra!%&84A3dVeQRl__Ut(@m$SORN|Bpzv1TH_L&oM zL(ErXfKv~}{^=&8!ogapDnWrsVVT=dw-KlL{GC0voK_{d=+Fc5g2wW)X|D52i&Gls z>)2Q$r)VzQ<29FAW^>6xRk7-3(Rx*Q{ekY3c<#bhH~qY=7UOB(!1I}NQ+xBhp=1}s zJ^jG07yNh{51NP^G9T4l;Zo=Tx{j}&oKU?Si!_5Xbke;@U|%fml6(!gFqUk^f#~`F+xQm~)T+Avn7}`e>e+<~ zzbx_C<(Dlhl#kpok5Hsn$?)Jtt1;WR2W!wFMrp>r5x!ygazl?NaA2gi1}dVWn(R|B z_-FFKP}ZL{i7)F_kZT$gj|O;2T+ry;SM9Rr6hMrK>ZS8!HNQ?xwcU^)UG4_l)3?aH z@g8Vop~Q$@`qWPLNQ;0Qb&#L`7SorJ3*$lEtTIp)NE%cz{&JPKgRlu{b{A&SCZ_v! z=y=8nzI{&IVd;fDZ2#dyK9}9A+Bu0oYZZPb=k-SLJ;)z){LH!e6Xt%S_H? zb!9zhN@DW)QC;8rhBf@DW4Bm$5npLf{VZU&pcj3DfvzrTz$B6fe{H%3tp4lS?o041 z7nWIhT9L4{HIModmf&WfNtZ`HuIy2NoFDVi(_^06{+)d6J$#0knz&Z1Y=w69Q-IN{ zTL@(h;`4^ZXOvTidWQ1)kS_C$G%i-lcP={p8iW}L903b5o!UcbkydN{WY!+Ry@!vY z0dpY%xfEA8=To~V@HfFSZADLJ|LlGz_-C$}ZOm?E47Z04`43;7Y1z`fbmPUShai$0 z4X6x-1#OzKJ`|E`JK%+(0V9;yXn;X9e4+bbtFE4VY>|a_7Ox zU_nympv%PZJ&fN)qO8yW*Cr%S{}~-->&})Eyv%mJ(Y3{4QJho2B!!6xxg>#wcCh}= zKxv-IBzxzjq5)JxkXv@hG=cCdKe-M?o?0{j_(9k^p8*Y^{fh=HgrQRGYDq}09y9Pv z&0{ubiv9QNNM}3Di&)^FLDiU)`q zci9#$EROn<)lJ&L+*mP+iQvHlAKQ`{eG7+WjG&v>>ZK>!_na97_6v@@uTd`MT{gD? zQpc75pmIkTk>p13**+Z;Kc*HSJ%ciI4D|+h1}1s33)W-F^=6M8KyU3;;IqY7(LQ9? zqT`s2l(n35l$(v&*w8z(!S^FQb8D<7$UH=NG2$aeo!@yWZT;>M$1y z?p$DTTq;r?=?MSk$d@!TxoQ1Q)Z^elPg@)6wtZgtkwSKM20$l`Sr-(61!u27DeU*Y zI;Oj9+|8G>?urSI7i)>m;D?8(`p3-P!G2x(GRwR=g@t_OZu>`ZR_^xLAjB7lvMyAp z2UB?Rv2|w<@Q(4RYj6c|dA#kVL>Z*$74Hcs#dq>MF&eN7slfVlK#Co1`*g9V0LDV0jR{k z;m#f*z23nD#YK?Mu?ZBcyiz?EOryKb=~uvg=#sqRUMv(kaA6bq>C`gq3+d$r8RW@Q zyTO90(|lvTnyD^5XP&TXJ(m@@r%DprMOt%IH=;h37o=Um@VqBJr9AW_`iQZNZA(Lu zLT~x(9=f!YTzpi7Y0lKjEh&HI+jv}kEGR~?Wuxj}u+(GkJ@f4Gf?zr)UuW)R_lAk0 z_0g_KnJHAe>v=~5W*D^N7T4l0MOO;iv?WMX8teu+O7$45VXgXysDxJ|nbnxqR>f2L zVQ#>{QOPufk&%I+G1r7)A1I9}SeWP0EygsTNM;|fDP$gfdyzRBpt{{v7<0|XQR000O8#dZ2vi;$=_e!dI5GNgp3X+zU zl92|^X+;L1JpC|XP!EhZHb6t*F=BK!1yb`DqB~{OW)@m|H>h0z)uR zWhq&hr;LmYR9RKZL&i(R1EYEtdOCJlX<0dG85NkUs+z2&|mi`k3f8whJe7IbHD|eoBw6_zYG@+C;g{%XqZ8G*nck_ii`@vNFy+zf%p(l zj6paiAWZOY&i;GlzX|I9uSSLYLsUO10ENSPnq$yd)ZfOZkH!5f<2x0@1caW(B=oPr zp*{an48n)_|Bf!&QySxsITZ~HJyn+d?dLRJYVd&2FjRmi2ClCmaM~h;#iG?Po*pPq zSvd^M(@Rkarl6qU2~&|#M8nWZUb3pPDl#%CwCbPw`hlMKUsiwVqm`7sFfxj&Fhvzo8`ir=Dd9_B3Bn{(sm1_ij+*&tuA)(4D1B5IMnm9N1;J#LVRA~yKPyO5Mnz3V z<{!d=XslP%za**`f;koYFGUsA|=OMPmXDLr^#j;tvHysXu1>9|f=&f3#%? z8WW-+U=7tMmMZcpvR)V!Sp}G~tcNm8UP(m-rs}CG4@1kMJpP!x z3g%C}2p>$qe;e^X#6knT!Xi*17#;7^boleJV-y%zvm|X&oq?&&l&okYwTBcLw`RV)gn%_TvhJ(z<%BO zoZiZ){B?^Dz@82-=vPVocJhZIG(H6N`wj9ByC{GBuVPVAR+9g#oc^dUS?SXX`={bx zMdlfZ#{5Gv?DW>~9~CC2_-CiU-+Jx-urDK{CL^bI8c$i3znMh&|5pb83zh%g-Cq&> z+Y9o~rqj3X@1XwqzSo5Q8~?_?@o)Sa|Hi-Z|3CcmivcF!bR!lapr!e92RLix6ZmVV z78nGTmy!Wgbm8VC!oR*s01PucmtW=pZ6uo)DX+c%q8tBnmBsw0APR;AOmCp0Oc7PWNu@K{AChupa%^-6$1VP z4(EP-DFJ|IFk>qy6!<>_qi0Y^7yy8*PPsh#bRTrelTNvRL|D+T_T5v?;^Fs;gGqkz zkkbL4@-x4<_aFT1Z<#;1_Aidc1)xv)uQ3Oqap+%s=#;00<1wckVs^??!m*f$Q$BOb zh5Ye2>?!~9OBaVhg#rKx&9C+_jHk~jmpSD$AxMPYDL)4Q6g1v{;~szGVVKCUUz5@c z42n8ECi{dz#XQBKvZ|^oP-9GlKPD^;W_7xRs1P(%FAx`m3Wx%LKj!=!Ouu3aJq@yw ztg4a{OioG`_$U6`&VLcV*7gs!Z~c4CKx~iy+4f(_1y%uo%E4)Da{t-pQ3L>8$pCO> z{-13^6#zhgx^A6M|27}CU-|O4??qUQr_`^2{)PXoz`uaM`IGu(?yvAb^)X&3ynh(< zSKK@U{R8nK(9qKg!a!mFIf?(T75|36jpHl=<8^wZIz7=r?M|}{8{mDq+yQ9p@8b(L z;D5D<|Ceh234i4EDVS@104(RF0EVY50A%MeK*CH7KwPe$_JICcH#15*!0PnBJ>R=O z^8PnK|Fcza&aX=-*88`f9s&vV#D|3c(f6w?0aAbppa)n04uBUB1jGOsAO|P|>VP(2 z0GI%l01|Kn+yGC&2fzU#Km-sABmgNuCXfph0L4H#Py;jqtw0;l1@r+AfQP_1FbT{8 z3&0An4r~Lvz#;Gr1OkzRXhF;%4iFzm7$gaj1F3*CK?Wc*5CY@~atC>X0zi0BEa(y_ z6O<1s233L@LAO9Xpa-BwpefKx&}+~p=o9D|3<1-CS-?DC5wJ8^1*`)$1tY<(U~h0R zI2xP;&IVrtSAm)gqZ|N0wYl;F($Dk@gxZ*i6^;CQcTi7(n&Hz@{Hs)$u7wc zQd&}OQb|%ZQaGstsW)i^X$ol}X&q??=`iUm={o5l85tQHnJAeunK79onJ-xk*=4da zvKwUg$)?EG$PUQK$vMa+$u-C=$x-BCx3k!qisnp%KbmD-vbOMQ|08ud-;QR>&!Uul?VBxv+#Txjq#*);Vu_i0|z?9~uexYNglcIyuq3JHrmC$w4P1Aj#r=%CA*P(Z#kESoAzfJ#? z{yhUFg9w8j1BxM@p@gA_;W@(rBQv8cqa`Db@iHTk@e$(&6FHLzlK~T&DT%3?X^?4+ z8Nw{ctjFxhe2KY+d6;>fg^Wd%1)s8iiwS={wb%hPWCc*}1 z^JmLpYh#;ZJ7(u$*I~!7r?a=RPqQDL;X0#z26HCk%#Aa%XO20b90nYI9C;jF97~)e zoD!VYoDrPYIfpslbFpx#b9r)QaJ6yGbA!1hxDnhj+|}HVxes`Fd5n01c#3!idEWD~ z@oMw>@)CIO@^11m^J(&7`3QXX__m;|P#vg0^cr*sy35baZ_JP9ujGHi|4l$tz*Znp z;D*4GAeEqspqC&)a6oWZh)>8|C|0ORXkM67SVh=J_^R-T@R5k9h`mUvNSDZl=owMC zXtZdP=%N^{n3h>Lvc0mO zjDqs|@E4)x-Ry0>kRUAkxqUSK@YR;``2xy=*Dm7L# zp_=ZR6`Ctr{8}ijDy>y*L2a~lo%TB&aUEZsW}OeZGP)tU9lA$)YI<>c_w^zAM*120 zPYjq0Yz>MHmJJ0Ay$zcTca0Q{VvOz^lNy^E=NUhT^T0jfjqqI)Ws^9QAyXPt8`EOb zRWk{*5VKx$u(^r(74t<4VT%BZE=#}?Zh6IW$x75J*y@fonYER5iS=8AEFu;$V#926 z-iBy%h}1>qAQx=KZ1J`OcJy{Gc8zvN_WJhu_A3t34sj0Sj+~BI$8INbCp)J)rvqmL z=K|+-7e$v8mpNBa*GSh#Zk%rZZvE%!&!f)Yb|-bWcPF}^pe#{Ur~?nUN2$lIr=I6k z&uz3entS2qqd%niL<-SLLmVWhqKmG0f zZ{R3!?zo-+rU1Wyp+Mfi$iS%}$)J>=m0-2tg5cc{(~!DQP^fEYcNoj*X6i9s44;gD z9j+N(68b1F`(E39+x^bmA)GLGd2(0~Z7@ zBwbj)Xmqh5fhxf-VIol`F+cJ1CHqUgNjymxlh%@rlABWKQbJOmr>duxr;(&#)1IWu zrx&IF$UtX|Wy)k0W}aO3ygZgAn{_SgXSP@NlN_a->$zmP0l9N|T6qomjQP>|Ygf## zbP}M148rGv^97Fz6$;C*QeDMgUAbmbEs^H6yhuwT*S0b(wYF>jUan8W0UbjmnLU zL~deE6SxWAwAt*^JlUe((%lMct-Qf@Bl9M36Mu8-miw)_Hq*9&+bXwP+lAXpJD58% zIzgRLox5E=T`S#=-BUd#Jp;Y!y&ZR??$r13_m%Xs_UGKCx|?+O=e_8A2ls>SzklHU zU~S<1z`~%z;LMQq(34@);n9bN4+lqdM(&MjjP^ZJd(<uKqKYsTu;)ML<+IR8qy+2HUEdIp({Q2_(K==D|GJwASYya?b z0Z2MMUi=C0uOArv`|<}MV8|&>FC@PJA^i>VU!eF4pg*)oArMk>QWDa?_56$Yc^_b; zAWcSPL*ypn?-9^W<16Z|MrMKA1wV?Y{O1 zkr!g$OU&03rm|5uQ`P3FC^8*-Nq^urGFge^3Ob2UV_-Y@EDVD;EEE+JX9PgM!a_m< zCMP33J?_wh05Ig#13wd)Ecq`FC^;62V8MjEQxEd2yQeOM*j{@muv6{{hve%io=I%8 z8SoUTLMlzKhU# zI#0#gxm`=15{;hjgcB}PIhFdSKV5~5j`IBkg6O{=pFQWosEUk#Z8c9nY`H)cA#-0K zn&SD+58F3=jnx^BwN#Xxai}fgR1gv5klq=dg`r_f%*h1wPL zgm1*hAQdtF@9G^YIttu!@;*~c7vWj5h36xv{Vap&4XJ6363N2b45tPvRoKpTcdl}E zZzzCPw0cExa*A6-M+*&Who1l1)!73Szk>1;iQa{obuX2@CafmLS}bdN8|pz8E8qLD-R!(ZqB}>B zQn|uK^U(HIV&OXzB2|r#QxM}93;x9;g{gsz7dU6Z2t&P+T_ZcHo9jyjRnBx*N+qU# z0@tcT=00~p>)&SIn=%ot|6&>0Z6sQ6OIURJApUr;Z7f=CNYSn(&6>+)N9(f5gJ@8t z?b(N{PXgXH3A`>@PgxxnbIN6FG)^~l%Z;h`OUWo9vG7VZo97~}Tes|*)`oLpcfaU| z^3M~!tR&KQ0EaH;*uep1JA!%M+RBjl5>L-*3KRH#P1 z(Iuw*EHr~RKbftdFVgCcobcz=A(YJ9twI7lM_us5!BkL2(rYIP6D1%%_kBP`m36vm zVR2(p0!bSpm4@`}wR+P>I&k#|B;hl$`s~>7FH1djNn+#Q$j0Uwnx*IO-HkU}aqjQT z-VAQM>+x7zJbzKL0mY-%v|mFv;?O};!+z_1soz`T+|$w3+9Rl<&#tRpOz=_l!_sw+ ziuhMC_1|&2+BT>O-TSi=@EN{RD^=0Ja=uoWQ$O=S%=5sgPVw*bUmB|o>&kU`>m7n6 zo=01(=%bHfs>7!hsVBpq_&3vG8^nHGuf!eBL~1i#bgJ>ltIH-37r~suqmQY$HhIYCtfV3HkWUm!TQu)r^AR zv^Z{Y&e!+F@uCz%*amci`h;;`)-$r-v%rY|DQy<%XC zo9rPZ1^Ij z2^*KT)o>5mv57I6`~*M79}liGB=QH`Ty^p1p}Wp-wu%_R6Ypfxl>WAWC7|=F3qKqh zP@eh(`Y!3f-r00=r7nl}Q~WEqhJ=P%69>pzvmCcm zx-V>0W-e@&7(d7xx`}c_Ir%#QR!~pC%n;qGN4kA>^< zrt?{1?s+^>s8_lTEt+rxlMqY=jsaHl8-M8Xqu2!BVAIyD;kS#auYYw&)nRa zyxG1{N&oQEvntHtg9mCaMDXFmtutL}R7YaISQctarK$pm>>?x6A&0%p%A zysKe5`hxnb^L`TZtbFfSun)L#C#Tbwr!T%OK{ zoV}AG@B2jZZA{4ft3-CfEX=PY|JkU{Jlp&X*=w z&URN5T`QVUq*8nr>wVrAKc00f9}q^sGiHCtT(N5X(mZQGC9_ugwrX)z%$J~>TT`op z-jt@yW0ibw93=5Advs?;d?dkJx~+2&`)QSe++9z`t<;{&MDLK0 zc!-?Yq-9Q$_mRqxOw(}7i^#3Zw@JHP{iB$vg+)Sn`UGsq`LkwpFl$T?5$Z4Q2LA+1 zG?qnqnu6p#*0)5(5HE$repHOo$=AaPkBzD#aYx-d^^Z^-3)24iVC}efDh4N>SAuwb ztE)e6%8ATk@Afi6`2xRYiR;ILnA6WS$A}O;9_oJaY2xCcwX^9=G`5BWKJ*Cc9@eEZ zvZ@^xet3{GD{C7NWlWokTzs6yQ?ViSir#=6@B5Oh(n+~9HS+d#F`H~J!sFR<7gM&y ziA?#rSF2L`?_6R(5uS-KKfoi;4PVafy`jeMaN z1WhfIvcR%(Y;TQ0`gO0Z?<^nQxag(b%4R0MjvmJdd&%19?(|kYS%a|=##;(U2^I9% z{Hz+E^86Q0ob6lPJV(kq@DGsnlM>3h8k=jkXQh#-*&&~GNRuRvc`qO)UfU3VbtncA z%hqveB4+oJD>Bxew^2gBSZG@_m;M>4PyiL13}?rM{cbQDzxV~`s&7BaZAWvoP1m`@ z$QnFj+0)m0+qXD44-0Ru`A;Cqa)}I)Z*Ly>;55QglQ^ZQ2iQIydmw8d_(JMizZ8*YZHI+x(z-Pp6;>48W2QDmHhPvr7|yaZ0k}?=2HpZ zg+;N$jGgzk(UZXu>K;JdXmF!Th6D4n&UBy`8}??ZIIoF}-wW1=jwA*uhqoIAL~;nYg~4v}fnola7Rd<545UufexRubik z-)a#}&%M&##wyi|+a0pXg&GC0L zX#4vrZ#=rujy7u%xubY)QGya|GtDoQW2@0yDLqb%x~cS-M{pyoDm$;HE=ImR`FN|a ze#PTKpmeSM4FTw^5wTdCC#qJtQpR7XLO3FeCF~8iaJ}Dz1DdHZP~-TCz>T#z;_S#r zvU!$8#Z;SugpGtdZdcNDXiaYRMv%lg?HyXg$Yrm;202krg%p)rs#I2hR9 z^Z-#$>oI#?XNymUl1Wp|;+$dxD8qy5KqG1Sy?c$U_mdV;&Hl=o@XvlYAF7%tOY7!U zLFlo*QL);inOwJxZ5ic&E0X2MYN&x zHTXyT(M#0wa=Mm}u@Hj0Lyzvzqt8PlSR+e=Ne1=}?3ngE`G77XvTabGa*Q+Xj){J% z>pAz3T(DU9OSrJP;5sEKBm0Q_*bp(xW2Pjer6kGRbh2T)J*;l7BtVDw7Wq}nx} zIQyLa@ygEymn8ZF4gkXZn1rqh1nLN_U*UuB2SJx%z%Hry1`q~7TdUf=i>rlqlLEnfaGlH30B|(TcX5_~paH?~QtLZS z<-u&X!XvOdWmxi+P18`ZE@9eHk~;T@q~!es3LEoe5j2HO8B&&{QrtwFi6I|B;p|nu z`{Tve;6P&&@Ac9jG8K27o$&&0=g-FWQn6fh2K=Ox#55E5CmJ2q?UsuzI8B`<5aEQe z^BlUSV}T14)E#ihyBhvhe$BZVfsw6e0t!iG?2o%xh{EAFDYsPzMa&f}I?`&l235np z3>tb`q<_S)^xIUO!&s7owgi; z&D~ra)SuFvLY5Pm1=py2!?JYEXR5K+Hk%cZHqip)Z`2P`BCrPYRa7?>m^XPmV7)cp zwT;WMhe?UdmFcQ>BHDbLHT<#r4Q|uU6E=y5V}1s2DpC{2p!NH%oIFUl05 z%^en^8ulaZBp9wEuBI%N#g-d0w+F{e;?!ixzOLgFPv=2eF-z$E$*Ox4*TM4U4fI>UBj za&|^osOCLS)i93H@w%l5FTxaB=PD*|6ijC_K$V@ndY7Vwjv=ppR8RcV{jSD1Wxm3^ z`Fb`|T_cAtggS*;KPlrP4htrGP6|n$pTLRy;RyTkz{FJ{G|7XHJXj)ngs5Hv36dw& zkT8-Xh3~5)+VX~K>Y_o7KLI_aseYZY>C5*TNXlsP!%D?QnZZ0(c-ORRCUs=Fi7w9D95Nc5~7DC~UzOpBX>BUA-yELv~koS4h-h ztYQp#2S2B2Zp>DDK>kqeZk8{ia~%Fe)1rmi8XUs->8o$h2U5OH6_X8}LhY;A@ZF@= z1SG}dd<>6?u>$4jnKkE?nnk-$mnu@t!psAlb=mTT!jb?T^Q+*l+5i!!r|;-*y zrlRX56g-ONJEMIKLd4%J_PI-C(#tXO;Qoy?el(M3qWo-7K|5uU?)-e+*kfqk{w95l zfl!Y}4Izu>HEFvmS>xoYUDtUcOALwSTBEGiL(h`|Tdr<7#AJMdrtHj5V65GEr+!Sa zDX<_)OXj1aDcH6{9iJ%Hbdjn;5iZ;jE|)N6#e+QmR`mkBRpT7->YVt?y|0*nxbB}o zgKx^rZD=`NUdeoM!N%rPu}F1YTe?hQc)EetZDRkM3d&#KXc!FPlVY)j`ug+r`)or} z?QSJA7UJSR0rK7Q%1rYz!lkN5xBF5keR;vLu-?9j7r+R+1RrT7|zsh3t z=@md)bC}0DA~a$kb3ihYD%f{3&I7N>OG)Sr+F4_{VeoJq5uo&TR#B$%WQg)aZHT5X zZcjawR`;2IOcw3}dzZ}oPe2PypcVJijfgTnxqp~Q(7y=28F!#izz`i#8aE{&b}0k# zy6=1B+P6&p*~WR2rV;BO4GfsL>aC5dq4yWUCZ;ylea=aK_%M8-wqCDb;{X9T#~d*4 zkqP74WX&5S5~4;vw4ketb+0vtBDL4~BA;7MD{cSC z%C33Cz8iP=5TuJ;$}4?sCRADHp;8xNMOn&9D@mmHu(IhBt=uH*OoMYWE=jiWyACPQ zGUNmi5z}|RT5O*1-)9aqA+mcz&2lcQP3xZ4Wm{FGxqNB_Va1fo?_EeN|8dQkhSDZi zcP_X*L8^j>iHAKS7^b+tATQ2wCSKW=%mFb^;D2Oc0#5-!YkIC@A3u;U9nZeO@{*1< zHhvly4=g zIJF6Q+AL@Eq2y>}$8dEAr?mZ^+v*F90BJVY72>2oe9@=z1}zHWIV)?Qz3~>SF{j$L z3KlOK@ZpSrKw-M>go&%AYb+EYR!dWn+_I(LOIH?s#)Vy<2O0N4;|3>*?sv{>rAY&q zE!y7jW?YR*hiNbx*h=DIg-e0^lzvQy&ui-qoco7TJCj7D)z{Qao9GD7E>(UScj$e+ z|Av$4R=o8C2OTKh$}_jy8iQB&NlJR7l%1&H$9>1G?_1ewi2hnta#0t3Luw+nZ?tr% zj{bf6=m$uFoU`sgRiX3Hn2(CRWsi1lxpcro?iQ0OwA8Lh^}ntnOzHNWZ7F|S?`S$s17}4ZWUZiP z&+hfz`WBOB9D_AWG#Y>F!4%nVvhEjbT~+83GZtVYo`WF90nD6w^Z{ubUW!#^krp_e zsCsSuP(cGoAfDkq7IBt!i}3u8x~+esxsVudv97WBQi9yo6WiH9F&*SjAjNv794Gry zNuZVx9dl9-I@!dv;xI5#hWTGf0nPy0%rfTs2oW)3QUjX23`=IA81RrH8oC zri!df>=HJNN!U2M)tB||H}|7* z{H-qYoHqp`ud#AExPjBx;I*aKY|dtvTw4^aYBa824KtOF`teld(sBT~b)G}9M~*Ct zbirV@tg!(%sUYCm=f1ryc4KvZMHzZlTKHDU;^j%(P@l|tp>M7eylZZbwZRMW(v3~H z?>ah?+UkhL>t%JKXX5RdmDHTnt6sd9mpwn+9XocvdxXI0KWRT@Ti3c|#e`V3mC>1V zIuo*dP)9J1C9O*xhR1eM4u8GM9^rwQeTCj!WLjh)k1Wl09fj{0NU*xSQ=wfT@4w@B zN5Sz$^@h5%#C?rQG_s*fIS`o>|I*xL;6>ccng$bn>^IMBi~)1r6NfPCJDO|W4a>@= zCQ2+E4K?_$W0I;gctxsrsT0iF>-E4BUmQNm%7tLHj~0HEuf|}QXWe&;j>UdkeW|rU zO?)`{4bHZGuo;>4ZXe3`;c|&$VyKHTwZ#y|$*@*~ggm3f^&(^9UJe|Kfp1x%@{=kCr#9Ed!mA?0EQIp!6xo_8u?Hxa^UB+sR ze>;0;-7cuOcxFNT6HVObi)~lAuu{272jzO4Db@*VAy40<=c(59sGB7Xr1mo5%oDH1Bw(I3DTt-LlNXHu>sinj*nQvt;7Z zS0wp+nI`V079Oj#o?)fU65IY5!*9(Ca@+DsP9IC*V6!)ccuVvvjiXs}tLpY?FLmap^xsS~cxr7!3a3e|Wl*DF{ciJs zV56QW?u5%gB#0z1^OA+6$LLa6DZ^$h$(2QU!k(NTlly)Pw$k*>FoGEQrp`)!d^IC> zcyJynMa$c2SN&-KUJ3J47&lkLCoK9}lh%4vB$I5AR7$qy7_`hPDFWDeO`>&cO+#@rjjY%~JLKXuR|W331yLhD(f-@&O^IPr9vRlgz=TP7zbEA$pmaP3nBL&c;l~{;s6^>Q>@q^VT;n zF|>7)OMdTasi^$?oMeOUX2RRD7{)t_&p5G^zUvHgO)T1GLH%+19<%5~%cPJ5mFceI z3}k*-xea*CH`5>Kn^&qH^&RGXw$TllSM$w6EOwCZqf@u|2lZ%FsjZ3nws;ggiZUOa zFS;DO=2A+gYAiY3y_uAk$VkN{Y$FiNaKvmY4MAqF^d;DxO{mxXuFy~)mJWrzYi4?FqjsFBQ+x}sR7}IyZzAEW?1pk zgeH8!@o^}a<>;X=v#XbnNxaUt>VeXy%9bbEPCI zciH5xQfe^>Gb&qu@OAZR{s}C6i2tCG!3piZq64uK!e4b5*pJm(?x%wcySDVujVciv zj_Si~*RE_y38PGd;3jJ04&RNxg`+Ofy*Rm=sood2m=I|V>dhzVW&WPGQTM27s3tqj z51%cf7yP`BvCo?3%VhhUq@}`Ryvl2R@vi6f*(pJZ<~AZSCOqHa=Ft3>I$k;jy+=$U z-7eId!01olHcu##sf33LDwPXY%Q?f{F`5W+VmnL{+WwT{Yi;h8Y3!7$8upzkUSDyNx|Pz9-?9#OeK*e~%gTADgL=z4dn-cwjr`t`l=$5bJR zIXC(*96q4HeOT>fJSuoJahzz0yG!3dG0djjKzp^f1gzjX z+VJ#GdOqBNijUBn4_#SkjxX?Z($jWR?t}Ib2*$isu{GOk%zMgCV$Tnp+k9`Cdo>E{ zKu}lbl%znYyH<7);{o9}8{+q$+_6V&r6Womg+>}Hxj4c1h18c#Cc2RMxzQtfTd9Zb zdU;lF%X#-^=c~yGrzCZqicZm(QBa9(g~wZ}45f>>?r#pFLs69;`C8=dTQqpKSYN_u zCmo~`i^v~Pz#Vxonu%&uYelv_{hMmnws4Ot+!T%JJ+70RIPS8-vaHoTFIAAtWIswM(2gupI)~0 zwKb`oV|u2)k`9D7TrayTlQpc|g|yqz?I^w*G*pi?b0yTP6U4Jo13Ju;*KZc)D7?Um)xI;)S30t);@|;GM8$dGw~Qh^ovJHy^v#anXfh5c z>dRM&HaeNDb4Td!JAvTEv)i%|0W7kt$+6t6Uo&`D@?$-GD_QL5c~3IgEUZ~9{Zr$p z-)q`DDR{Bb^^df8`^W#i^n8>*si%H3cKl=@A&jgNh4L8+p!6^GDz-L403bD8NQ8o z6?nu`z-Av&6xOO%o24n0J9WioVe;hl+YWs-go)Qzwq%7gEAgur3;W$;TE^@i-A{TZ ztC8q!j4ESnZ*=yGMI~lbeNN^Z!>rX%QLX62oKzajQUIm{ae&hxaAa*N6t6k5ji2w&NWm@PDNK zSf}ICeEoK*+;dwAF#`r8Nn05G9HV5GH7p~6UCRnt#H@l{sV^QAsCUb~zA8*x&&Ate zV0qVAZDe9A?Jz?$?%CR>WM<+?*Id9Q;YP z1&NYTpENzVgwIW@2^?_?l`WA3tB{%i9l6zO6v5)C-infU^E`gOr8>yhBu7ct)9)3K zJAD>x)IiaS2WXq5Z}Gd6RIGrX^GThxGiEAG?F&};bg5|j#{2AgyR@&d9gQYT)7As_ z<0f@Fb8aFk+=%(H3SW{e&0C)w6r8+bJsX9gh+2MUyZi!C-7A%3oc>8X$fjb7DO!^* z-eMhRVscOwXD$W9O#`c8FNBbgX3*Oe1SC%pA@n+T8q^a&G$BVq2Sd!J8})Q6HGQmV zY{iVl%pwPOdMajRHb5JfIiu)6f@E@bJ(NOdez;jj>iiPJn`XOQ5)o9DjS2osGSeM9 z#>P+aUrN@hvW2Z;QnnVlJ~zJ0so}J{sCH2?OZP#l@25hSA1ITVu5~76jUgVG$qm)x z3R`1gsvW318c8>6(CTCKqrhHP9;S7sHiUme62DV@Z$?o7SwN=0&k0MC_r(y8t+(Ce zIIcGAt%yv^Pe0H$uBqqn)UB3k6Ui!&&ljA+F%bz4@zfd-or#^ zuLebr;FjDni+CTiXP3OV%|S$aLPfz9wixu12u>T-|QY zW)M(U+hEgNAsVHAWm=Nm+$TO$cW_zbef5=ykZ@xw_?$ppNVlUra{NsIYp;>x<1i!h78sEC|ipD{gNrDG`(6f&=2;hrUm~d-3Dp7Lgq> zc{09;Kh~e<5-jLdwQ1An@4RB?V?+3OhkEz{sYHEHfrxILh`#9sHxe$y?pwkU1bYZ3GjZ5J=I@g}-CO9?R) zeMP;!xN(jN6l^yAEimB?Au@U1af>@ zK!%^YzEXNYFdwng*`h-yj@S~dN;(m!AB~B9RE+N2eADrS`c)%r8TLsKyJ_oNSd!KH z-8f_1)fw-vrYvmUz}VVf&31QW0+Puq*6*77#QD5FA9Dg9iSIl&dO}{7^AKDwn45$( zN7}EuCF#fJG1QP8=neWrY2>8jJ`)W!iJENF&e(!s<*m%tnGT;5!AA_-3i^r)oGfPp zIEPBv35rzJamo9})IY?HLUEyJR>}<{$8>`md?lf5p`$9LAz5TZ*|#U!gPZt=*Nm66 zUT?FPx?Kq3Y!=Pm6}WFq#ruw1pP@btXoZJkM(onHJp^SiRn4{M zfrNu&or1-t*7V0A>5d~zSZfJ5n5KPqjx$DC+%GC^R^R{WWeC6TXNp<<2|i;shXkAO zuK5G&Oz2=~P4G}lzS7gjhAI5QD6E|yHi5FG>GmCqs+x))47oPdL>OObDhWZ&2gN$e zaE6Mjoe!cpS<~=?LcTq8zfI}JEiy=$dn1=~+jNfq)#p1Om6nX<%sQLn88n6)K~C>K z(S#6OVn%bE=f>_KT}{%ay><3w*+9M}{=K#qkIC~|j>0+X@iUFl;G%7LNcT+j8eSJq z+(8yRK5k*qt(Q~G$;@zNHjRJiBp%}b_?-*DKG`g zs>+FCgVu37T?*mpbC+X$)ZBVoyrY3Z=e=NN3#lk?rHzD~*}HFR3HR$sqG50;g`1Tc zOFweo4k((bHeR@lww%-)n{1oU#mf^`(xfOPq~1P@E?<=V!22=lj;UGitkku1hH4qm z*K60hM%j9aR9iy{Q!2}ADlwPiUcOfJi%+j>)4yp-Un}-%i{&w+ctpDTf={5w{A(gT zPbjRkq~zJ7(3he0Z|?c@+3cCq;83z37P)T7?_HkHRJ9A4XZvoBdKe(^ioW?%SPH- z0&--KB0f0DZpS}RX{Q-AV*lY>zlKl$w0H%Z0SwM-&-mgJSq3ZcmW>BN*O^Sr?JP5P zVo|@S!zbq9mB^^{5!x_&J$Rq+R5iFm(DPzJAfHx}?MGY_@$Tx!rcGh+{T`}2e9Z>J z4{?Q~aL4j7N##NhocNS;OTc5b#{SC~7W-a9-!pg#WYW;_iKp85%J(j$IJOW&xNmmN~nG3U3o_vC|ax-hgD zW7RM5yGGZfEzZ9dZU`Ci40z=7Y=(90Cm_0Nxfh}G=_+6E%ephehaJT5b^Z;tkIggn z3%>1Xk@Su5r%g6j&`sZMnaRUf$)~N{<{RoWSRAjfJ-WMwN51Z9!a~RITaaGoOmb~r zFiGuWPORl9#3J^Ib&Xw=Q-F!FUB58xs(Vl{bgNFzKI<$iE3v>X8$A{rBKCn!-P!m_ z!l35G6AyeBrnS=QC-Cgr;jB8_t6&yZ*)G!>$BqD@A!s)mirLvp?2tX&#HcJQX?_je2cAR9;=z8Cs-ww=6*nbnd&)bZDC8p z0T|P)?#hr&!oTsV8=uIe%Tjh6<)t2;lxi!HsWe%A2mU$xMs7q2-d9%_t&Be&8BaEM zqeY7p=gB=Ye|6b-A;Vh7;u)fzM>pfvyL6@>@J#w$;^$Xt^&d?9OXuI$Y_P4if8{W+ z+kR*7M`j1jlR%ji_G?;cSYMx?b{1UY5wFus_H`C^&rXVWU{yFm&O$!3u2Bjz^C5ZW z9{2k4%h{zLlYl;RFiT%cios}gA=SBcY=9aGw3Xf zWKJ^8e*$t_;ds%bfmiyO5wwr0^!#uiZTvIn%IYb2tOsXU64w?Ul!#g9Za!ZFT!zvU zwrm~B4xHd*9+0EnrqCe7xRG!p}qZ@DtRWvV+B0R8TkGfzt9Uw2SXV-`9Uj^An)3-lJd#Q(fj@ZA5kiBo?vqkbl@=U>@n%HX3?*7kb59rltYZ zykfvFgoT?t*`)ZG_=Uvl8QP3ilX0w`ZaTG--r=iKvO=y7kJ(CutpsP%8+ZIxO>Zas zs-^TCaPOi@Ywafh-xkNP7nLQ+ywUleusi(t|vBi(++!M5u=-MnHD0q6QP zj=r%Y2;v=EvqFJ+cQTH7W{79KI|yMlq5thohv8o4jLZ$C!*4Kh9|x>^|q+xnubta$7pEZ6)Dtx{_tUz2XaP!bs0# z_B5xG;EsgJER@s?>;B6I34Cx|FtPn+0nfKwE%zLmBQ2)5=@quM`)#}znXa$Q%}=-V zN1u2;wKsdQ#d=$JSKF}jp|+F4R@fJ9b{3eywyp5Z%wYqK_Ri!TWB9Ya`)P#>^kMuT zYx!$+ZiH+|zallcLQElQ#2EFcONe9IBcjPbgBft~+m}{{+~R&WW)znzLGNnY>etyuznK zI@zTXW4TE)J|!hXsoihz6UYDzrsDeDWakS$QCRhxRVu)%=(fB7ndSS0<=zUZ8v1LK zx~1pxXfW31W^Y?Gtp+o`;0;QmWwAJ-|#E2MHf@nHiH-O0Z0w9h?X?Hw_ zOR$kgHjQrOAIQ+-0M7Qp(pwjop@H=0Z#`%1&hj@sQ8t=On|!)|MVjK~%g$B`@kdn# zpUk!EI@l7fHcne_v@WeRKni6iq(1W-RxBQIa5U8ffY7L6N>sk^o6_EsiW-7@AuvNZ zAc#kdYwMM`KgNnV8CaK`{XDICUQkHBomP=?eyb>yn95(dh$ApPPu|N4(2Iw>^=vFE zv+aQK4B@UZenNQ_sy+SjUigv!JXwS88K#q)?c)zKLpUjOMZ>D!v?c`7@?9+V*`Ord z_VEWq8^7u;Pwt;**zFYN3C}4FQAj;-R%$x2S~YA!$M&{M@1Mae*^fJ2PFhzn`AX{a z6G;0`BFw~AwzZcnq}rjg6b_#(O56H6(9kpTu4bLq#D}T!@+=ViZ1M3I?d0P|djF~; z*QJ{E)mYRd0)p2rbzsg*>KT??FbKHAb3VqHI@`=|o?H%Db#2IMKSRXZG0of>)?k3A z;)xYpktm1hAE?7WZED_pR<3^!$6zb8rS|mR`y}4RhZ!U#O@V_oP#>FhhPzYpdmkw` zdL^7P3m*g#b_m}_#&;TG6w=W`J2QvLbPW+y_0;SSvz!NrmFgw>+0|?GiWl+L%16n) zBzNz*HNKybz}AwyH><6AlDA(AsUn-7m{uc7U{uu$W+e?*JH#GAInpSHy0=;m!@0nM zozBZGaY8kX`Wxr?i-bUCj?Nt1<^JC6=JGzlGjJVwq0ebitrE6#aKtg0w@C~6@A z#);}t7Q3v#>j)({zz_#?m+g;=a)`M`WSG8h=&rc|nwrl}oJQ$CE|Mr^?}q;@KiM)zG083h7ec^41Se;p-O}Eg_tG zx~yW;Ie%nm#}eNos7q zp-LG_$SEL5Pq)%Ei&7}OrO54NB+)&s-e?)6Docq80!0%Z5iRcls?8+EO82G%k#4k= z4Llhj4-XiD$=nJpUx!#o#|o%Bo~h**i%TGadKk{KZ*@rv)I3ZyQmNkI#-DnTg;f%@ zNNp#;{Nh)Rwa#ry&pVFxHbb>biYqO$vYjZYWh$8KpdiXG{{T$KGy=T$bCaGo)uj+# z@Q~d)+_pb5nUu1w{{RWYGvoG+6uvW))%28pl@l|Yg;ln(RMluLEE#?gOUg=E>JYd7 zEomI_?q;Dh_4gv`RA<-r#d2$ktNpE>(wmE#l zU7&`j`C~_v?o0wcrVNHnP!EkLDV5V$Bs85+5;lnbk;5?r08noMDJb0liTCdWF~LVZ zGE$P2-^(KouDP^I+82ol*XbWsGFTG2iSUJ^%{jx-^i6oBodrzgu8{L}3D>h!wzpMk z2T4jfL^w;D8nHBzsY{@TAx+2<;)1x6k2IwY)`=oI8l}Yz#LXUVz2m6~aYI~%Bm*TK zCj^nA9G$4`w5cvLS4R4%>nYR~O;*y2PPU|(f|VjfN)NTak|XIVFC~Y9<&<~@SkJak zxfd>QU3C?!G^u+t#M2~xteMqQr=-+Bj1L~Lb;LF~5w>6hywzU9{{S<9lYOBskHiwX zL-zOY4~lu_$3`MnM|!o2q8)8B`H9mv?#U^lP|R8IRmKtA!)*~Y3Y=i{W?b% z!UN!ixP-gY%BV>dMBC2j%H40gR@t6PkfK^i8-einKo>T2R*O8KeuYRJBazii<4)7n zZBXhr8IPnx7>h|(D{1S|PL4I^CMy!n9l`Fp1rz=7JyEg9REK$r8?890rciMX4Q1Q# zg}Jmv)Fc(-9YT^pNKrG=MjLfXO;b6M;qAQJzsmV(47d#~SJeLZ5m3P`c5hHDQR7J&;Q-RDJe}wyl z#42fSRBbzOaiw}{AsduRWjXbnA9ECUF;T$*YUwSc+|sC*G{U(P+D>Zo^vQbRguL&W zsVPCU&#{b$Cy0{LN{^Llc-*{qQS0+79QNn#GMe(m9nRZdG_0UGN=OPGAc@D;mvu`q zmOEaG#jp5|8PC{qnQ^v-*(e{UsF3v#zYu2@IH#%_;?H=fRbk0|Uy$*hK~rbE(kU#V zO8q5SN!a2ip$EnbmB6T9XtP3^*KLi|%?>AeSK>TkCgjyi*f=z%e~`GD#nS%(Q?|7t zfJ%~{{lr4aYyr_)h1nvBx4|xBiSNtr%6YZOWB3tqHXFWTlQ>-Li50cYkeW8#Nil`ct6&En3wzt>Vd~YF=c9CYTgOK#`}?o)!f-!%mz(n~dNh28Zf3Qh6CkJQ&!n^4{6Z94vFPDP zM^zKB$9K{p1zut`{{TY^W~wqo=CMvHm|H7P<{yMQ&U^tT~WGSG;Txq+cK zWpY$O&A6`Nk2nax^`(1)&W)fpm|ay`CL#)QL&LQzeq*XN`@*Hs}7MJ}WKFtzd))hO3U z`FvKlsYdjjy~OnR!dwU`s;P&=4D4o~Zh~SY?bxDj^eSvT(Ek7~ygL5?DD?S7g1RVK zzGYFojYv*qbQ1pn%v6tVu~xYUc~+|}BMBe+Rrr4B9Tv5kLE#@REgz&`gnXO#5JN;n z<|JThE{PT^p#V?B!d%i(VpLmELP`9+W9lWcPZV#9(5W{Tj?tkVql{@TOPX4>OS?T9 z4Y{o)FeE;aH>Cos%5_X%BhrIV;)-Rdk&gIkl^NADi=^DJN@wweKU8~kN^EwiR25Ba zIubO2-pv!ul8Rknw?sb6V5?O~nZ!fM1xrOJ+LyNw7fiMGJwFIcK-D!nG*R0oLIqbz zYDxTM6VXoO5Ji#MrqVx+LrALZ4wyYuaj*v5&)Kgpnd#$f}_LmSyK0+n&lqS>$ z%G2>fT#{H+3FS*rpvdYJ5@hKoQQVmKR|38hST%WbmfDtG8YaO+jnf?^D7vb;4<8ljGG9GP>_f_|aUF?)mKbPur>ag^r92%dGSn`h`wf3$EE74Xj z4~!Ls27waNQCg9q9$iwN>`B)Y%8-+^N=g3MMXLt!R5je2np9TzA4;bj4@*=}PZ;&* zX0CR{wRz*X5fbXCa*aUkxBFw!%Ns62+aWSg8u&uv8lYtAhO~DAe0xDiXAM^MD{Qon zoa(1QNc+?O0GnNWA@Y{U){8j>6IXGeghy4v$`oe&#E3rvBa#%U2B;@U6Lkt8EFBtt z5%Co%FZq{}{$F48oHtbN#2M0Mx5un??G5OcP>b9oiJfDjp(zwpiIeq)By#x{I)N{j zNF3@azGkUYyEC*uLR^aS}LpzB3w-rg;Ni)Do}{{V*DVady^rX$21>bw#? z%h;WzwjwZSo<3QsQrm5<(-QLydUT8{idr{$D^BH-owO=2ToL9V{vtK&8gyA)&eajJ zf$I_B7CRX0AW=824QWjWD83e^DbHC2W`FKW_$f~pA5ayUZ z5aQw-%QIG^0AhNk>ye z_#rz88V|xaonCBI6}E_d#e3<9OGxltEm@@r)?lg>l2$&R(6c_rNE)l^pz8dl9)Cop z^+4SK8IpY6p@i^39Q@Fc(I^F>!k|?pY2hCt%z7f#FH$?lCKL#0+NqwN305M0E^I9X_$b zQIU{}9i}ts{{UoszFNgOZ14Qj>|@}*qET4vi>-ApIG-f@_!v`0s2jV0!9?c2R>h`= zYDjD#8GaG-pWvVw5tZsU=GjY?Z@7f6VP-%MpEzaGD~3A60jh53ZPgX^E$m5IabG!W z1AD+L5VkI$3P56bnrT`?&n9XTAdvuEO;v5iwLVwbX=>DOl(Q-LGXnLYv(=qQEvQu@h-IORKqP4wvvOBP+LzaU= zp)s`n5G@MnA!=0_6VeL=rd`!*{UfKUeqA96hc>GPQ!PDHFTy^eQuEMobVpcISW0#B zQD|Kvq^%|)B(6E4h`q{XCs}Yitsf{$&J_*P4KwYO)$pfco zQ0S8w6h#j-pnornAufoO_LNj~)JH>VyHr~1PI;s{!%wIyq$2lOP$bHJJz*uJq=XH0 z%8{R!;|X+RghNO~?^7rqlTNxqKq&`Oi&n`5=?N&9&ZxcGa!-_MArCqtnt`o}_5T1| zEF>PNBI(pWhC04lO){0#YuXjyzN*9YtMD+2u2o>VQ`4+Em3&K$MYAk<@( z=8fa1q**I${Etxp&xd2jq|sY3oyDFbw&A;nn9!z2AjI-4X~LVOxoBp7TC|PrAN#|n z)*)`KWQ*n+EjtmPv%M9G!oFT9Gr0=s^ngm6+CtcnH1ILdkm7^{iD~sPwlr5%&gJTu z2e;N9F@nBqrD^m^2vQ7)?jeB6bWVG<(5I7!6s29NcWOr6KCx!wj{L&2j^h9=!k^Xv zJqC#IjbZ`l3&}Zen5B|a%mtWr&6O!Ds2>6cIY7=3SW@>OOE%D~*tfDUaBci-vc-S2RWUKFtTy8Y9VbG@@ zSej-G#NwkR*n8-->r7kWgua>pr#}tg>#n6_u$j#ufg?VXjU$TV3z)pM+-^=Ikkv?P ztXkn|Y$mq>s2exoQJL{m~V6Pnxn?MnFa3 zV_kb|5rCs8yb9|h(mn-!O%Uc(0r~fWhg3X`M|A50`{BqssL7g+>73L`4~MFZnW*xe z*~kbgKB_KQ(bY4WkBlxdqvT>x(>b7-g2R+_tw*%abn*B@(LRVN7>}u3>8#+l1RR7_ zDJg0?c88nM1uIQbnu@1UtaWuM=&Gi$cWDl^6vod|SkYA|N=nnh9UT((rNy$WouHqr zd_3VD@ zOrWCcDj^QY&Q2n7P%$rKn30-0R{1KfbB4n?-yfml#4F}`=##?_fzbQTj0nBjgJc|qA(f&$vig$M{$K(**g7`;@z1%}L#WUMu7fgEW&BC6g z0Z1*aDmsbwj59UH?kwIz+P#a#In{<%F!){#d=jV7TT0%a%idlQU2*WDyTtzhBw)o% zSFSuIhtkl-2vyR0(jIj2PNh&WLeRof6jQd1Ie-c%^NoIt8hy>9%|PM#4c z+oRyCv*UgV346q8ZcRFu83dk!ELgnL!PKnZJ;dUe9TaEG_^qdXUrDV~KjG^db~liM z03~_*knj@HOG1fRhZrdZ)GnV*b?+Aod(i%cWB2$c#O*?hnWqw=)I3c#;wSt)W+mPX zs)=)46e#4b5lusHDY`P1ODC#1LbOEM-8qFoOz(&4=$@!jDO5He1JWbzBB1E4 zc6Z>Y-Fe1erYcK}`Gn>_?SRXutTEg)gn+A}ah3i}n@>)V!^sG!+Jd(ECQ9;;80$?} z(nnRf{jUmmac48RN*JcBd)94xM(NBDesRHaq)kZyOv_Dmvn8cV)Xt?! zBX-G+!WAT)55VArStscUPc#(KR_l|L zgaAlLJ$wj{iK1+@5T2Y%JgT8_8y+EAO!oRgkz6@P$!OA??B5o~IhPlH+j^yK>W5oa z*HtaklrC_|+qJ#U+yDXZ0XYeqe_3a9hz2KU98C1yX7QYo;P6e#QDOHUvj z2tF`S8Wjx*+UgLWUM1m#@~xV+EI1omXdaqm6}n?sq-HY$-QKMhlZV88Lo_PCy~=P6 z^NjD#xcw^APL(N1tOy}Hf#^sOyKA=0+Jrsjtd{N#AtE^UEn74u-&El{M%2%!BTn%h zd2G_G7B{qLQcv1nCM%1*-*E<3Dt!tI&1dv`nB5?zZYlJH{!0Q3<44<{Y4K6qm>DAk zbqaBP?=S|PZ-_ZHhWDBp$EYi62ma%!YDt+LrUatFn1nV}8;+i>p;X0)l(wWMUzs9O z_(NmUsWD&*BjP9$d;?sf~|Bnt5U=Cbxo3$q_(a7N6I7a zruL5IGlzsq(!v$>aF04-p6mEcc*anM+0N|^dTzQ>1D?7QB(KIq#K+Y8@rSqcsJ28Q znbEtaf`IupHqAZi^NL@oy$hWJ*4mP!{&depYvTkpq2pB9-5b6N8FE({RAcpyRnoMj zQmSMCG#bQJ&ADmOPTJfDwKQ#^Nj>H}N)X7Kbi1M{GAaP87qcK}Jo} zf+sJeJYyA1yy}IwNVC(YfDp9M4k&VI4w4fm;SPIgR4YwIBG9J%vyy~1_}2oLd_hen zsfYz6%lE{!pix)4DXfZAyNy1*v;J8hD4C6_y*a5>ZOC@%<(D;V0F|9$0`0&#T5Mld z0Fj2=2s(Cw=n*X_8g07jKNv^06N>|GSNL==rnZTXaIGt~q=GhU@`i*}P6+`(RcuW9 zAB=O`DbV^Nx2@_b)iR$*Y3xh)NEPXq-(V4-V~PCQ4RetNZqzlf#r&! zJsi5JDZ&;0qv>jL*kpdXMJtly#P-f_!AN1R%oiZ^)ky38tu4Q}56>bG(sAN^<)fjw za}R>`W)kE}ZX6LMGk%-9r}<(H!irP`>f^5ku%?MVY3uZy4GwhrWfeMtfS1ZMw~(!g+rArhqN^k8cRr&jA(>&O3e){5hR^QNkdH{sCPy}5?OO($_SUjK8&Xx+N8b4fyc>` zXXEpNgQ^mCk<|jCpnZh`K`95P))@|>a-gGC(!8P$!>?#>-AVy!Qw~o?&c6s`lv`t{ zSe6}@t(gw;F~*%29TL;}XMe(f2>CDC2pf#MKlo(*A;(Ejh4yto7Sobckg5Ek4}YaU z6KGLu)bAbJTT%GRKHX8O;ToVTVZsUY%>Gallgw_2O`^`_UDkns+Ll~#BW%7g6_u1l z0e8V0incGf1I=641!4}SjfcG@dUUM=wnkke^Q*i0ko$trd{@5vWrf*mx}FHLl-8kM zSQ?;AZqu|t@mM_-P4d}^l8QQ4&vK+Xb&Vd?l~g%R5K2m7uvP&wp1(-C;O)z%YO7^j zeX>&xoHG4o*+>XG2j$W|X~(ULEyDBGdf+-@l5JV)7qkYIr@+OXucEF!wMI?Z-y{IR znePs@^-LjcS)W0fWT(O%prYt)Q1)$4dB2u)ge3r5sxzmwJ_OR)nfMs$WICl7GiIp& z00I0Ungqfg)uHJUP)~$(1k{=>;?Cr?L64J*dTkF74#OQue?{tT6&s9_ejQ`v!WN4| zhJIg{SowUt{*LE|?2grZw|_)ehwL%M#qab-2Tf0HAzO;=7LVx5{{RVuua*YR{{UG_ z@r8#JlF(L8S=}IQ6C`U1OQAuPWOJg2$~Y3kMG!}j`t{yR%dYR(g)<>kWtMm$HEaa?J3V37^6ne4wN@ zG72w#%;}udq3JL6$0T?u#>P-5Y>#=oz7Uqasn2N_xnp@IL`r?<91j#vIx|!Ob{9^h zOXCO*YIoTvGqx9!43#)lXu4q07demA5PZ50-Y}nWDX>tY*Q^f^NI^*N9DJ1t=2aCt zkGyB5f)l?)#@ZBE_B`T3iiEsjry2x97!eI+wJfBo!>Nun)G1~$;EE|}Q&Uuw9S{#$ zLWZledRm=2V@YnM&;UTwq!8CpsD{lZ zOq0BxgX^RY=Yi2!g||fd;aG}UWxAS%z%mk_%5f8MaIP4mlanUO9 z%Xz21Q=p{Gs$F_SmNld~LuKv*qE@rS_Hk2ert)r1&~y{+7po@JEK>RYvZS@A3ibB6 z=*z%MuY@{btB?+oiSDrAiAA|iDURRa>>=?Gs%`QbD&F^DkGD>@NKC*cM2SR3P6JA- z9u8r;iU2cgOy{#h(jZ*gD}!BCnYFjw5`k!vWk3~bY#l_U-V`IDNNlJ&?d?ud>C!r5 z9t)ew4y$Whw-k+fLujz=Rh$zYTL>Sm-*gm91%Z52gNrIZj7uA^TV zWzN-fMo}~AZd$5-5%6#ep^l|dQ_Qp{<`XgSG%41Cd=M41*Pl@+hF#-_LY@sYqS8>vJwT2js8CU}f5TlP zkv%VATISqG6OW_YROhtf zf~vNkaQ+W|>oMJWbJ z!>S>!#M!A#ou>-$AZz;|XIjxo+vZDBRoo%g7NbJ)kg=&j=zb7^9@QkwgC)?QF3~>P zRK?DSRJ25u<=hDPf+5DpDMAqPS{9wr8Wh}QJT-~rTi&;y=+&y6*4~IG;*+EaE+Pw# zo|3FsuHoctj@dskzaQ~TPDR6+mOQ5&rl$V2M1!O3esDf0I>Z&m0%%QJ>I+fB_ZJ;_ z0CD!+0q@_UojGzh-XjQ}(yj7;52t$(xNz1G}*CrgwFLr{i zpXn$@wO?06?r{4<`n6MlNX-EJrxW~OgDKHxy~UILI0}AP6DAOo;5ChE0KA!&HF-+; z9kTF`g09z8QCtE+`1KGghN?!_RT_DULfKJ0JgS%B6@32y$X40gkLQI9D4hjHa-W1} zEqL6$C34LYiKfQcfjPs(D0v=ggdr2rNfL*Bx~5#@E|tfq+)LEU^jG3E)*XqiVXIK{ zv9dR7wFz@Y$QN2>{U?t3Y3fYQCg9FW(L$>CJW2Aw`;4R^ z*%AoSAt$qImE#A<ax7OO6TTT+TrewItxo9yuomaFwM|TwBqpFDRKeZ$Q zKJh~Lm!TtzRFAH4Ma3T7U^&fIlGZ;;2A4_iq()GpOiMJlQw`LFk8K#Tpf--Mb54k9 z8jaal013>PdKT!TrEaL=ox|bM4~nWbSi(G~c2d$FE~1T=GzAsko{0*8l(D0wHm|m6eO{wMI*owuAQTR)TX_<6r`o7 z6P)!BNO4U#)TLDgK_pD+3fu8q0;MeN^#*AG>j`I6^TvsDK~S-`SRf{7mGqS$N_}IL z)g-p5Q%6}MHIJ9nqE^vY6m9cP6Hph#C1oAN5{W7Z_fU>yQqGFxN(Y___Yw?APe)o5 z6Ei;%42KeGkX@w_+kTs;U_|HC!BEKNI-^1mQ`#~{mbd~6mfqg|;ByzZtA#Mf?+#w^ zhE|gPKT_h3)DFIq)j4U{#N)EC-M8DgXg?K4cOSjQ%kS8{wDlvwbaCx-?kmN7=|f3v zuCGOxCPGjvEwuzT;#5b1x{rvA_mcLx!KPbtQRKIt-hIT!B!b@kyC2f3#A#}%ZZznt zw^SmjhX5#X1QD;wF{`^NVRqMzSE(`icy414iNJ!4S*UTn2>iY=8zePT@mv&W)lgb? zlL1MhkYmEk(=_&y0(}fS9nl?Ab6N)kR{gtdh{mW%gSRAwp>~oP?p~n&?-+Q8W2ABvcptpgPY;R=}P23Xiw!Rc$!r*bxntTB>4V-ou%@*IAu8;SAw@w3Vm=KAs`<1)snqQ% z+W?fL9bvG_B)n9oUl+>iC|_(BW{HW8x0kx0p@G*@~(sHvfF?N;dkW)%_4f9q0lTNd@`thXV=aW8QAdaXAEA)C`} zlWw@^7Kb+IC*e4ihI$a2xyLo_sOqc!&z15=wLIRc!FZ-p*i(nlT9Bl(P}Jm-l0Fd? z$+-J&n^B@KjB*L=U9{>E4>WSs=b5Q0ZO_?TZrxG}i}D3~{vQa1#bS-CX;pE*%8Qd) zD&om;8@3-_x0pc;tj}zZNzM(GfT3-OIwXVH6~=waPTTg(Fqtw!E~wj07WM_djzm&)Cm^Hs({o)JY&+Q9tPz2P^M{8&w!DJ+tqxT{D$7p&TUI@c^yzmnCrSB z`3U@yLa}kFN4>YrKYV>vbKy+pgDkX#rq5pMV!f1-0YU0yG}Ge{yi+x|(C-y1%DCu1 znBJjIFLT(#8HN#$^Ue&%>S`#NLUJ~m1gXYIp7ImiMdoL0a3i5rOJB@k2da#=S8Uvc zWBoezsrL~g*#MrJ^@D{ZYK)PtiOMPRHx<_7wa&iN1vG0WL;_{0h`BAxNmG`$LE?$B zV%JcT0BoIQI3I0Me$rA>*y|T5H}#f~^94c~?WT!ONsQMlB!+cYPY8K7Mu_pOVzdxF z6F(l&#*mtj=#7zy*62fLcaQoARZ~kt_?>sCf?J%VXZ4Sl%1QBk0;$tZ+k;fgNlGRy z9HDN)nN;n|8hc)?sC{P=YB2Rx+nwU|Rk<~LtqtibJ-lKP)=;{I zr66rHmq=zwe36X=8mDU7(~G2AZb%)nGwT5CsZt*1;G28hpuJTfnd)_yc#yVz6jk(E zf{U56Dw!ajr>>He6xqdeQHB&zd$*#Zjn>ow-T0YDTwFyH3<0jHjdwd1%Zs5X(M0Lt z@`x*j7P()oCEplBXz}o6G=e+BO>%!>rm0y)iMHtpnDOZ|4Z2-Yn-x-xtSl%1q7y!v zh*-GMHep6AVXc!QpprVmJ?Ab{PK!6N;E|vOJ(N$z5$=@xSx*GH;)`ju1T8oUl)1;A2yp<9EdG6`R#*$Yp!1kQ`N)j#-3}rg}TWm$T;-TtK)cmQ4 z%((@qjCpfhrt1`+s5Yo1!dsl9{urQa5zz+CGv^5?j|qEsA;>O~90a+ejKoHAKe3 z2Sg+Ew&1P7Ng({8e&(s&q%96HZro?KUr1=qmKs(HX4)s_K+~p>kuj=x!b%2FY}8E? zA1Lp=Uj*ANT!1Z`I*O*GPfq$nCG|>mtEr*{Q)NLvlTNybT#|sk`ga5gV#Z09!||Ab zx78BZ9FDPeZ7RQ8q?Av1lKgb(6<%3+veDmyvhh13CDTD3D&%-%+v#x{r-)3{*4^F2 zgnn7~2JP?3ka|^qjPY9m4mf@aDDr(;9=%nB%V=5Y*Q{vVW)dO`Ams9HoiUZEB&uEd zMgG_TG*sCM)f?MeWr|{_M7EDK{5r;=#BI08G*r$*b|Z+U>iR7vwM*QUERt2wclgJs zY(JI(UVXn6kd#Pjt%(u^0Q4kE4XOyaW{SI5mzfjr(mU=^Ahw7~YVFgOh@emcFSH14K0y*3c!n1VKN-n+zygE_UR|9kV?(j_*lqWeyh`ayo6-yvO&! z0#)oe?tfiS+3oE|}?}*xkyIN<}79 zj#W4zUjG16ruauHzV;W%xzj81>-fjZ?`3>$l>Yz^#y&{)oV*u0ZF+vl>0N>+c_53s z(uA3~KV)*DE7&zf%e_)7qkv201Jx}9VNMzAE*qjO~do85@0Nj8~^@fAVM=}G{ zBIV_@t%+|2eMI38N^A}*TJtJYkKvHe>kN^ia6;v{g6s&u@YZ?r}&ZsMF#h6;BIy`68F|s!=(p9W)b^a5|wrEL>4jnd2L~NOTiV z_s2caRLg8#R8sbBPVCR92EHO0bk$A^2c)IbC~BcA2ypG}JYfXrmkohQnp})ix=C0h z>k4Gzh>5YA!q;`qi;Wi2y?Z2lBj;RGMBEsF-BK0%i`A}dNc9xq8;-rbUC=4qWtB>ND%fJvND(O%jYvASRVdX-5Mnufi5!FI3ps2r?RjhkYdJRufCDIkUMA63UR8IBRcD=_@ z9~Um02BOv77D1Mu7)gGMpFr9m%DOL2zgT14DZya1e^z{d372U5-6)@;2pvuB)RG79 z(hy!!zo*n9m$C(?znF9y!i~acV>Lw&>Ye%k5j)q{ zgyycjr`ix^Ty2wlQ9VXN_xQ<_GW(5F=Nu@C9mrq*02$JK;U89#G2s_Ikv%`mV}|II z>BqDpm$DaiCBkLUP8R!vMb=ofBDbAALga(*zIt z?ML{J;~XpRVQ6yuQ8VWxcGSW|hs`JH+vKn}$^e2ueZPS(G|%B3_sP+GEt-QH%}nRM zQ;wUHN+kU=e3l09Qms8nLR{hnQk!ZIg4V00PyXa4ePNHNaVgVe(crcJ08y#`02%yY ze&f8-ew~X#tDgo%(=+pjeMx@HPlCtw&Y%3#FRV6Xa4A1b&n2YRzH}=;wjcM3SFqnC z33b&2tAtJ)e9le2)ujP)snS0=`A!;Zis!V^(F01VsvScViCfPs$TpdhN%=x$xol#j zubFLAqJMbzf$|KE`g*InRZC8#X72}C0{cBqLgAA~Skx(Py)Ow)h`CUDcke33C-mYN_h z*w}O^2FI9d2sXf?;>$c5B(;7V6*n42!aN{XTaYwTMF$ny)8i z4yjTH;R`AVluvLPbeIs_ibl|=&CCF<)mP^HsVC?}QM1~xt-H5olZ6PG1u9gIlj|Ai zXa#z9Lr|3TFDOYXJ%1^KL861YjCdoYZ6s?k4m4_`9aX(dfHXQDu=Nv7kpxl-Nd-N6 zDiDYcsx7W#$gQcFFx-x-XpOa(T~fP=R)%swg^i@jUoc`<218|4{@^sa%As603Ed4V zbj~)tqy(O_=^ljRk{@oX^SikATij}<{T5gsqd;+TDF6Z1E^$r|Rp+~mVQch6x0~}$ zwC?2rq3;aKbSjl&jBs^Te0z#rHC;VOkSmsid?PmH+ekF(y<6G#J~;=HuSX`7t6c=a zjE&3J@f}h=wEqAxUp)*)JesQ=vC#ku20jhZ7PqBFK+YQmsRSXdDsq%9j;KRO5zAc> z23x4PMyTnPdB1Uk*(nM-K#kaN_w5U3tE!Kci0YlsAEY=Bf{&Gm<&}XU;}5i;oYZ$t z@dK|&IgbGYENJeX@Jz(Y5$(FFy4P04&eLA)vg1mi zs%pkorq-1MKB?^zc-)+}@2`TZ=6J=u({&p7D?awg_cG)@DX5^Z@>`DN7~?@G7iRYt z5RgFrVnLHs4|my9{4(MfT~uk`OZRDe>* zbvvedf;x#tDakkm)tN46+|}#O-}j>*mSAh0wf;p(M!e__wBRXMa-|xnsIW9-2VWV)J|SlyxU(kM${}{A&PuGR7KAC zk++jjQ%@ph7pITT9uBI6d2rLBgj|)vDR4jO(@3Z)YLNcuQ{bvT&mdY<2jng$a@_q# z^1%t29iS<#=&lwSW}OsLzu1*Vw_8#qP)w(!;yK^~tr0(P(Ki){a-C#@FP@?~;D~H> zMW&%@Bo8vD&~}7XD8!YvjBcjl5UrbnwLUS};4VQ=l4~@ob#ZKRw%1nEtZr#j>IsU* zXC6SPS57IHYsd~NX<*q!MFLoEsl_xzq;=LQ@l0CnR+%-v_S6M0Fg{QyEmK!7^%TpN z)g4F&PVqF^DpVr}fl>$hCBGw~6?95Vp}>5gYtjh!LPMEE(&YSqG`Ti)BTY5%hZ`Dg zM{J)4cAZWSOK=|ub-)yY@)I|7uqB{Q-Fgw>2m@5^>ZCPYnYT*8kXY!bY3-@ea0HXM zV-6JVLMEf!2w}pyE`XEDw$#u#7qEp-K`jsC34CQZ*%OP1U!A3OwECopiNaXw1>L~t zpL>;(%a~ceOk4xL4irX%&1kiqj^?P2=*tS>Pc$Y3Ie#d@o#+Yr%lnvuyOqJu>HY`>}v&BQzWLYaG zK{ZE361ig9Clj{vY&1dKLL_eBpD{~kGr;OGO81n8RH|T1j??Ateh~;`Eh^P*aM?UkPKzM5 zvZRL`NUBgu)TPC@EV3jbZlJ5UkDlNV&WO<}sj~|n)Pm{=9Y&DL4Oc#y z$jLd4kN{0e1iyz!d=<~7f074LnpreReG?{9(m4~?O{3_Yjy$=sR<_(=g{FFVb&Ca< z(y9vjExIXUf7ClTNXFP4!D-m@(S1aQWPgRNPekKvU^8F_F~n4u4T8IM&zk8K01hzij+`O zgH)E}46ctI+Pbl^8j-m7`oPzYphn|?anL4%{;^}>XM2sv^iY&W?^AzACz1^JHe=wB zA3^Zc82j8`e@l8Kf17L@lmn=rLP-(UiiF^?eNd}kGg<2%ZH8V>^-UlJ33x&poPC>` zHA`E4)fY#a+CrvuBf=kTT7-K+NA;9t4HZgiJt5$uCT$W9tpP=Ck<+{u2mwdQ9;!ZP zZM$oIRfugw1Li;90A9nTRf7+5PKl>qB#K3ppW!}l#!+4`6{5Pf&CeA^6r8}*C#=Ej zOrx)qC&n^LBZTvM5Rv@yNzz^)gm$CNC;Ci?YI)TiLgg-oU+Ru-Z<;6iQ;;gYcJiO+ zZYP5E?iCt)4 zrlj_lLk>j13JU0wD4T9@tZ|zo?k7?be8abvHqP8!ZImqck=oUzRL$R!tJ%8NFbN)- zh#fVQdd54SNTSM46jc=tN4#$G%#ZPF{gCI>6wt#r$!&L%?^+3w{{V^z_tzhUWmcy- zGVoMpvOcQ*`1{{qHwen7O9IFryeSS=EcFxX&`Kq&&04A%%Rg<%?~(4}$8K(-msJvG zuZPMNdTG&pgRu8{sQTw7T=q;Xf_v-z@eA&&kianqdy=lx5_L!6{)QB83SaalAa3$a z;!+NgBdjOiDc0cQ2qO6!@SsnsPt?OA=!MaQj;j&KH*yT!BTpD_>P=CN!o-Rk&u-+9 zUQ%cA{{UPh%vS~$I?-u!J-cl)c}9A5F~d8mz8DzLKl8oXWcsIH4L`OU`lSB=^d=`y zin2?!&i2mtx22VU5@$U~idQwb{{T$y!B26#Lt~r~=C1dPZk05cA`+#MG)(+vJ~8>P z$>M9WhsRazTML5Bcpn7C<<%*vxIW=j)T1(gWNMf=$cQX&Fl!+x1mixUg!BSa@QX|W zioEfnKxg?l#;qg`b)C&vI9K;^$k+8#k0(7x|n}`6$91NL>D#8ZvJtxB>xr zN9t$$;E!u0*9xFDcPfa~K;5|1CUSJ79?+k3qD8AwOZBe+NpW5?nS?rOCtGe2QtO`zBsgX9 zf!{LYA!s_S3b?-`{M~#d{;1_rb}t9|CH=d~HRpR#cUgg&lgIoOHN@ zM2}z+ux+KR?ocW|8pj1wzQ|A45b?!+VRmEFr7EXE1nJU75$+Y;&2Lpa-N5Ow8$)Gn zhSrqMWC@ew5Z3^eH;$d8=%RlkWz17MP}6P#p`sI~j4IkKby63XH3~A-)<1KpTaOZ; zPKFL@f_A2;TNFiQB3hzH@q-$;_7W5Af>4w+lBANO-{q`ITG3SU9ZGxgrhvUsf(Gs4 zUyK(Jqs0|dC9Fi1b>7_Vh3kD8B55HYbnhBvgnyJ4C3-0#D#J0WGecyC)oDKQA_9(M zYE*-^+0rZzf`jBqo}MB)SSph6Kvci)p4;hC)s&C%Pv#6kXUO)IuZaHuGet~_qm=cQ z;(k5jIWDT&$?!!l(z--~d}d&f69z<3ZPI;2#%#EQ}{ry8lqY> zMD-1*%#Xqn&a2{dMGvJ!fAmoEfn96pie3k~KNAd)Q{>V##@Lx5EjpdbV*!7Qq04HPWiXK{woB$r`S^swH{SW+(GL z5Rjim?kxUp!?bk_i_}Omm*$QXrgcD-x^w7bsZScLWPA*Kn_frD<*2v*)N~|0#bjGa z?HvJp@m21`_(D^ybrws`cJYU| zTWyuLs;X&2uO>xKI0os<>mGaM9mkg7kkde~Sk64w4OFC}vD<8{p{jP0jgpdNCHo^I zyU0m1s^e90h9*YZ1q+(U=?Z{(&(p6@BJ_{fgMFmz)3^p3Jj_R)vZy>PNhJJkAxt!g!3Ix zw>X za1gN!t;jA%2ANH!MH1T%HR=o9sY&~a2}-5VLM1f{Ud`I5vCHZr-Byr)GE@)Y8QyaOJxv+eS8|C3RZIT#Y zxF_$e+jVYORg-X+AkkOTQlwObIUoopP-pXvyNl-8EMrCf>L)&;5p#IZDv8B$_c`N51^Ig|MQNyH zIlglU*S~~ind{)^73wbDapMpUqKZ!wwZYeP$Uius%*|H)6%s<>T+07t?eVbu!eBc>mo-vQnU3OVtk zig!%LzgSRA7?O^W@jkFa0Rar=P^BF+`lKF^{Cc6>+9eem`j0n5`yh%Wzp7nZpV2;U zolJ2dqeQ)-d=Qg9uuFO*Y}Fq%SNTNb1aLQ1_WC2bTEEMzEpkMor>@&AW8{a5B_%a} zfX`DO5ReK{(cE>O==cD-l{A+PPHMbVf9lB70ZXIGt{YbJXp;fOI7E zhg~k(CTF&5!Xm4+1kCF=>A0kwUBa90Mh31SV%J z9?^lqF80wCyMj7XNF4) zii$a>6SU$UOO(@6rV2=$<@AZ@?nA!?Lzcow%@iEvJ`-lCijXNJpcF#3{>b^w1u6@P zBXX-gV&W&Q?^L>qLf+kU9fU>PTPxNlGg<>2W7A)LW{cCdxX;O{O;xLtMWXO+RS!?h zeKl^-eB#w*S2m?la%>UFK&DR@b6eJDsatld)u&RD3uN~Fq9mBgC}DF)YNwrw>yPKQ z_THjW1M5u7TZiT%eWCU}9M*>NgaxLqReGzA!%03U#C;bwee+!D!kJlJSPh>vTb?t<=>mYSMQoNEvGj z6iX=(<-K$jNmFy%JwMPwNT^(Da%nHFQQBOnE8FR(uY{wcK%9itB=Fpx*U9%wrZrsJ z9Tci5cW?>{1WT!o!1l;?l+DGlVB8c~_XF*NCBSa~0O$M-hZ{=+)~Q1YQa}UDKQ56M zl*1T~=JZb5Wy6gf)j8hRJ>|b@ucEVLHD-e6NTi|m9Z^p811TNntb|tx59`%f?I&|Z zRjB2b?l$)uiF%Dy)3cJ(qkS{zTYjn(6S!$P!B)1f@=v<{B6T4^YV3OJh}>$itci{L z3X;W09!C*513sN0nIJ7XsR$)&x)l^T$BP-LVaoaz0QDwSv~`%$A};O0QkJt*saA39 zc)RU??bXjYH!UGp<%cVJzP}&+MA}=;yGQy|1fgIVxUQ1x7A#vmt7E&Ws^nkwH-l!h zT=(L&NU1@1GFtsxs&Ofh@`Ws_UrEF~dbq2Uk(RJ24Qd5!B3c;&A?i^FA4G=aYI&xG z)c`#=P_NrK68*J=r>qSo1z1-FQ6lS!w1t_aZGcBv%xU;Xn)8zEoUch@8@dr+B!bWJ zTKszS&E|zZi}a2M8+c3*P_4tK$}YTLEzlK8Y$oh8=D9SMBNBg)1pnAmHtK ztf))XQkq2NF|3Sh7Sf3uRiBv49Zv;W+%3s9*sa{yuIn_#rwdgKte+~-sVE*-PMOL> z!ca8!+EgoMJ1WN-IYVadSISq*FFxWHjTF%;1QDlrhu%R~80}c$qA+Unc%1!pY5viN zqbcvCYBWvO@}!y6C{Li=bo@*&+$~);tym(y?fQm8l?qTQNmR#S0T7Dzj0O!|p<#O6QcS*CJ7TAO!DfQ)LRUpB)oq9?Z+y> zsAPXMbKYM%2G^qXHB}(YxJmix9QRWGoxV#YR=yHuvVNvJ?-k!=sIH=-pqcuHe_bJ7 zU?2Mcc><>q8{0xR(-_{p?0cJq17fs_C<~!t1ZYH5)(!4 zqSv;=>at`(2XO1*0_CazQv6W0@1a$*Q~KF;Qspt$T6|&{%cxo#t()YEi*=JxnxuRQ z(m7O*WO*PJ34gZko+df&(4`1k4@7H>sp@;BF;bM7sB1aTSWHhOud+}8%@p$E)v46d zx}o`ind#vY4(O=UXjF)PfiBZ-l*)%+2w<8N9E=_buCJsG)HsqS{{U>Dy~c_-#5X3- zJhQi1ZGTVTJ%)w}BCSqZxUxlFnRNrS70$L3G?l7K6Vy&BH{)ZL(S_S10z>+W8cSLD zz@(C<+5lM zg%G3=LZ)G-rIJ;4_Gw)4EAPJuVEh4+ft%UZ)s=WaEYp~(stSRX_gt$|* zDnt>lU1P{uSbeEWXuS!HoJk0B&W*oKz*Hx$kr0n-XoO|7sISLrTbJ+|{dCqWyn5qg z!1PcVc*~eoHS+b5R=kd>WgqcYPeJjIQDg3d;AvUyyGU`WX#F&mZ!OM!j2rH$Fc-Ge zife^6QbyG_u%RQah5(h?A{`JdSBdSK?kh3+bg9kd*3b{grZY})ZcHhx7}+%xj;L&< z!rs};oo60+m@PDPLg*bWxDs?7;ZTG$sSnFEqPHHY1pp0-0GQHvY}+mZrsmjO)l8Xv zyY#s&$pO6*xsR-S3mWkF!p)_996};)m1Gk+2sozWTg3{e(@`BM5%pL-LbVOjndux( zhbO0u7&A*pNonA9iK{s@R4zrU=#>1gZz{DiC%Axovgr@s1GP8DC=C=-s%CGE$QwdqQZ4K@~#a^Ar#xg}NlPXtb`CQaVa-31T6w(E)Ol zr?DRxa1^4@i`{8Mx6T_aDaMMeXhGAO25Y_z@ZRJD4`z z--5MqMsJi=UDhf;Lxiw}BVbElpOmBX*64egM<0sy46gRvG{I6E6;+=ImeN1aO zc79k`)=GN4Cz6s%rZh~-qRj+ExF|VgJ4%ZD_g(z7Qz3FBfwSo-^xtpn!(!b?FOt_Rex>piMmPhLdO`)h|de5(9!;NN%hm%w}rd6bpbm;_B zMa{Y+CRkMD0R~#bZWT>QqBmlg=z4g=%xwxlWpzf>)QTOt^@V$Bwzj$>>S_fUkc|YP zz?*z@=#0@usR6Xk6+|}hj99mxg%c;?qT>ywMdKi76jjN;R12(gQl8w*;7emwL1kgh$k;^eDY_ zO6U?#gdyaj*%d+z{c~31PdQwyp0o0j7*wRfCbH7Tf)8cP$woWa>kyUc1LMweB zw_Q+9sseHof($@6RSESa@1RhN+{Y|i?XK2at?wkb3W6I14u(+<`Vy`dk!)hED~-~w z>rmsaD6A~cZn6`PgJ`<=$yw3TD4vrew00*|FkqmnRZ&oGC?yh-lQ@1bYLNGeDFKJl zr4*^$iA$_Z!qY`c%G?0zCl?>t{eIuz7C(GQ+I-5dlRuPI<9ldiD%nD^e4!}#>Oe81 zSZf$qz3YE4m1Ee`XxiDAl*%NOdXlsh$o~K>zclJlseHD=8P#>tg4Idh`l!8DY7*HK zQCD3gXVM|FZS*TI5tfpwZ#Ic;NNmXyn&dUsGwrKsGPa1R+bXvoFVIW88x^*SChq2i zc-}rqnQCT!5b21Z-*rzFyx9FD5~NGRPY7|4l^pxZqB`mzjW+#ZGDk%1w4{`WxUfMd zXI_E=lT8%lJyJ%hiF@Wo;rpiph6<@&-~>?p8Px2Cp2{a1JyAVHxn5}}PSL{BxXCp{ zZ_p?Od3wV@8YDJNQ4{oJPs{TVl07JL`zdjy=I=U#>%BOLsUrf3IzE`gCmGECrTuKnR7l^Ve#s|MXs=| zJ`kU6b!l<86}Cy~4KJ33vb`h(HW z+YUB85)7+)E$Uu@B>f}rZOOAZLuu^JJ1HcIk*Cfc5T;#r0z0O|D_#Ep%hR@p=?gZ6 zAm6vm86%Ib4jB{?lXo0J`anrU`}O|l z=dc2Oxds$VONd>r)k&kFqf?4rfR2O4A#d;EfD+=QEy0x0s#WoSwxv4dHErdeT~nW= zr1b9?XD;qu&{-K*97h~3HFBJI-yT`<+Gdj7DiX8-B!B=Cj0-89p5fJT(PtIc~N~jfareVg+(iCI$!O_uvUfIm> zQ*Us7uGZmQ6;hl30O4a%`(iqFmTC&RS;oo59ucL3l%8eu(%jjW(g3JOL-P^aOqm9S z36f|}&oT&8_12f+Ed&!kwb?%Ls^%rZFV!0I?9%Qb|VrBaj{=ha0G z5xX^@Xrn#Q!}_KgQLA++J47TkEeKLjq!k5B_fKeT-4Sdeh^nTYE73{Zlhn(k1o+C5 zj_iboyID}bQN(BmfRr@mRH(nS{4ZS!-k#-#ubUsLvE-_TM+Ww9s&wvv`lJ&ZwNRgh6wS2I|+F55{0k=71+x-!dbR;6cT zfmkyUa>w%*jU2JeziHrcC6q^(E@6JTg$CVJ);enkU2<1EXjDa?ZW|M|37oxMYHoE3 zebto(^atW%0`BS0bykgx$Fc@(0Ve8HHl1}HVm?=AblxinS zoqI>XsR9tT+#ioo3^JZ5XS5@FW-6*A7->!)5GMvO5>OLJ*7Zhdq^@BhZc?o%?y?NJ z!L4Jd(E?_SngWF|;ORJZfF`73IHz;t55zT5^06E+@8bqEP^kGCLXH`7QkeNiSSq7r zVlZX?pT0N>Vo1@wJx-8ICe;Td2#!9P{ais)pKx?&T&rXLrd6WSpb?!e1atnvXY6_3M$44t@;Z-O&E8TZ2@Fm8D+cn|));~@` za>(<`J5n_AD3dRgPqy~n@;`2NgYi{}Y;SQ=`__*m;JPCAx%U?1eqy4ewq33$v*aWt zFI%5h#{#9V=IgJ7W&E49@1eqm0NgbaoLcc5fVE~kn}af7 zO%!p<_}RBZ)2AD?)CqNsM-iP4pe1>?lW;>Dj?kg!AaRn^mx5`I)dRX|oLw!in)5}C zWU#M^-4%QD2M40Anx%iG)<#}fkqizB3_b9gs&M9xAgZy|Ry@@qZAhN7)2ZnZliN_M z-U~P%p;1bT*!4A7^-(1x_(W@dWpkyV5bTNTKY=Lq*aQXO|lNz z%SfPdS93Bw!>US}QQA^| zF|62IQYDFH=LT|8tB<**>QL1M<p>o@fp$8UG@w$atj~epMJ)hfHqpW!etl9?SFE?loge!f$(`9N?Wn6F{X~${6 zaSIdN2an1U*(#iUxlwB_<3$y?e^0eq%*qR6>Z2r%g+{EC2&kiV&*bg^^_N&>>_)WI zCf=gVw0UK)_Z{INrivx3u%+d6*Rhw3e7;4OoL0RyTE9tUZMc#OKs`^&K0Oke+Ni

;G zJ(Ql?8a9uDyzxE{ZFvv8_tnQ6)t8tqL+KXYe`$Ech6OASvrD8=0WUx zM=fpz+Le9Eht3&^>R#bUI~&8%Y;=~8`xH}Qb_=4*7kYJQg*Lx0e+kCFhsg&tQDm3a zC66^xr{8A=4mRYEQ~3I~iE02H z{{YiSj>^`Gdx)P}uIKFGNtr!a?GyiAo$01T}73aZKwnddkAlFT@C>R zwNSd<$%T}ZfUcrn+YZLpRVwag`)Y6T^J;3An{rIVh@XT_v6Wf9*UY3{va8po4XJaK z#G#2BLsig_c`Y}f`j(O9RLrTi(p*JVkNO@1T zkIYQZVKV^a;0|pk-pOP*r;@$`Dn!Slm^0ijmSLP$fSNBg|a;6YbpT;+p|A@q6v|!BV8Q z+adB%c;Z#+Ov)sXG!i4ETEwYhhzI#@;)SM75EMw;ti(3(3iv!LLH4U~t)-)*rgL=A z5P~H+5iu0rO%C{v;D@u23BsNgi(c|b(CZ#@x{8l0L}}4^M(6VqXF2tO8Pz6c)S%hD z8LqIrgeQ7{R^jxE&l-4a;Hu_fr(sn$oZz>W)$|);Z`*OvMn3VO8negOV1c@kM9WFY4Cu<3M^z8g0GD3>N71JI%)mFJG6Nu9E(tQ zIKuR#O$-v0{56E^2@c3Glvchfzprr==j9{)(8Hsmd}JDeD~d1R0s3`$k4bcddj&K7 zMU+z4+m{qvDC=2rsS`WGzY`eebY}K!+_cUF;Vj~c(XO;8w&JO6GHys7Ul|DSwmRS4 zyD#{!L}u>xThrj8wa~3~Pxl*-;W*bUW7^6rmht`d6o*JR=6i1G9^6+NkIYw z4a~<*sJ5$&ZZza?I*z(W zewc;#^@g}nU@)47rj?m|W$=VL6~levw>OA7g#)&}z_LFYNquOjW0FbRmZ6m(QY4^Z{a>g9kNS9;b z{qf&r3hTfH5#=T*gvf;c$nUUIA5^zLiJ?=PXV_?C?_jUKui}z73`~HnsOj*Idj&le z-^C_Xg}VB*0C*j}41MerN7wvT4&Vg|Dp~;}q3It!9a5W-Nh_`{SxSc4ORj^$2bMAs zw)rt#Q(tqiuX})#*Xbw~(Wxe1B%|2KdSg?s_D9ZuCmDL)wMB@+9eK?qBiF1jElpGH z$N&)2$17Bdb^6D~1mszDxBi(Gbq}>H!S86rq&`6x4BSl1Ab1 zGlduuhH9P&o~w$0klqJ#sM3EZ4A~mhEtznN9^$|!CkMk=Bw3WBmPy*aHB%E;sF@E5 zLoTzSO@CPl9bJYgo#40$6VM%DZ?n-p%&0|e;oh=Rpy8gTQtJ%+3Fqco)LU`Ikw%pj zIkfm`0lp#=u;!Xl61l2ol%$1)Ncf0n+E4vpD+()1^@J@A`8w_}Q*GB>KayVJ_$AjT zyi!>$RLXv?scA}5l#?J#wUiFvdIi@}x|MuB)_t$pc(%aB>hQ`{cXdy( zJ*{g+R4lg+d#Dm?-CsJ}ahqjLzKW$OP)S2=rCQEX8VCY=g5yM0q+mD|M!8MSqcOX` zajtl{rxr>QRXw`G;20XAc4s!-A6>;fc#DeE|U z=+s=<+Vo1w8yg@b1cTxrxUCdB^6sj1?6&VhoCNqm{@4LUT;3NsAJ~!U-suz!_N^r8 z{?LZvXRQTyC1O{1?VefO7mV&)%9OGyB$NsYw+0HKIH)~!=~W?ix>wQhme4%DV| zJ!KTuSsJY>Oru-kj_GlAIVCLwlhg>6ipNTo7d~aEX~)vYRM9U8Bk8~jG5G@Si8XVf zLK0AZvxxayq9z-J-P~w{iZT)a!%9VyB(+t&9%5StUOyOWNcR+UcSWHls%>p`00XS$ z0zMs6zgkE|FI(G|*=h2Qdnx8$=}Mw1nkGi!(p}-P5?gQRlh4th5;}NvhF=gVO}=*I zM^{MLocbRKZSd%fZRV*lQ%cd?WiJS2(TXR<=_MyBY1-FFVa=)_T$Dvn(`R&qzHpHk zqH&i+5mYePoW`<&-w^7HWz|X}sA(NK&%Au*r{8dBiQlD6%sc}5*BT zZNaJtsxF&^C)x?`s9OI3NI$_G_LqL4c&mFr1GaO82wIta!pHP2{{V(N?6n4NiW0>d zC;376!|jc5Pt_aKP&Xh{0X_mAY#|E!qLjAEb{g9@!6g1^cZEX05Hs8q;Ly~v&d?Ah zJAgIP3HJgNw3J|OU{7i~_wk1JB$f{}OJ8?!DIrN6!UPWaNMAM);8(cb6@1O=vZB<0P6+ie6ycGi$Q4($z|kp{a0yNtAgzm2-KgacfSC zZo=T>j8u(N+S@c%=DjL*!Zv(0I!0M>aQB+4q#!gDYhC>2TlBqcR%bAeS z^@Xq|iXw4FWS;QzN)V_cSy|;WRLp*}N_1bE_SV{_=Jipc^_)gsJJPN=$WlU`v^!Ll z5xZR=20DhS#WTxeqI~g|Jv1aHXq2@Ye(099uT_?2<*80}CCcMXw6^E0kNw83!{B0h zv#NB^#_qZ$4K`B3PRgc~%E$3b%lN~h>=Q&3n9_A=N}5}>BjglR1KarC^UT-wvE))soYKv>1Ky6@-saC%ULo&9ZOWK1}m&6$Y^#0va zR3tvLhOHQPBC;<&+L&0=C=9ZaC8mZgmd*j9t0&1jQkB)TsPqbfnIoWyHZq}ACX-ae zWzB>o&~@vqx@cf2V;~hRiI9>M73IMuYPIe_jN6otO7zEX53Q;y(!6G*$oYH5H7136 zM6exE&}pYqJhDC_54Xw4!$d6Bts~;Ga*=2gZ0-a2d&ITAS*NOulF)<*l9MH9M^Iu` z3EJ&K6tJvRQfK1^In`ePR;3|Fyol-}geMx{76C$ISo*0{N?UTb1c}VSQ1nq3VJqlR zBqlzPZ$)3aaz0@IiEa5ov6`r zvqWgyg(MmL;FKJVM^vv!jZEQSp{Ht4(?6!Beh|8nP%<$oDBsm1v=TZYsj6ICs)m5| z@P#zdb?zQXb3sr@NS}nE#vM{aXpV#GNe6bl#4WC>9zm)QilINvsD}g4uB}n6HE9YH zRD25IjUUonH5$XseOJzH5{{O*%o+aJT1e`Y)RnT3DvnhU_9aane2DXSN;-$330X&Q zIt|daxKR)VQof4eExv!GaM38h5~_;lwq9-9Ze03l3a5C0FO3OhVIFkakOYW;QsjTPX%L@j)VricaHOn z#~+iad_%!>S;4;RIJcVY^j8djA*QKzp#yViaXWz`aw!L_VLYFJUD%q^tzNv~9p}eo z*cx56{vfGP!^}L8rWL&l6ABVNqcnU@X`x$0b2Z==>s9ZW9|^tD?8$L9YspjOS(YqOr$l962mSZ;=Jvg;?9hn6=!p&w^r^{)rFcc zr7C3kd}kA}$k`Tj=1BvzRO`z82XMzOPCCsgXiY_aQZ!Dm#I}?*S8Q)0E@){;ymHXx z8j=fT3KHwnPO%?wXxb~DExzOGo~vWww6;_5*W@CDnYZ25Zrpz9u>g~q&A~sQsMLZI zb&9>F6uN4wCPOnwX;obLn_{x-R28h$Kvbhch7i_KWViPlq^fOdY_Bq+;t*v%y7WH? z9K%%PcY>5lY(p@;;`I%WP`OHiKayuDPyKPz9U6vsYx;Fl*NQoVdg-5`t~8aY+x)Vr zN?OMxXg&$Ab@7m`zk~h9&|I3OvSnl}tOTa5L=b1?6PyDjv2Lo{VU`b|Jyff`;B*-c zy*C?H+LkI4xhhVe0w>x9jpn+k384}Sg%-0OGumoGsYsmVr+=g#fpZHKNXzgg(zxQA zYiLCLfF9o%L!rcv(4dWtNpiKEddHvgcL7?nnyMAlxmQd-Sw_7~!Zkk-V5`}l04ugk5(80H8$Gw? z{Il)rkuMh>B+YAZ8tSTm?PfL{S+&+XKR2sPwQUML3eo}bh#Tj}D=6Tnag3G;Jq{sQ zZSU@7b>+t|d}Gbl#;iFdD>D9`aE!v_qSZNNv37npB`P@QnB0oioGCJ zFw4r8i%&p%z>kAzQxM#VLT{|D`4Q%yfPwcE;1pTrtp}`CTJIqni1|lcDkzQJ(Mg|b zx5k;a(6hhKB@e9SEn^Gjb_7aF+qkXpFovrNVxg+)RG{m)Z7Dj&ZX$3sSFQSWQR|d2 z^r1_vr)oteWkP*=#oq&dMFOSgEJJE(TBiw|agTC--m)$D%Ms1>4P{j=6%V=gx7u3;n6Zt)#3eCv?QeNHwH5D5f>hzeQfz zSGVhBRoU06sYmk(CrNcNA3c>$isfsZa~c9nJ?-JN1r*ZhDfBVJU{T-hWZd4kwAtQE zfxUqzNr~tW!W#Nw9Gs7kZeo!9I>KpPrxFAWyH82>iHk_h6&ovYx%E>X=#Z>>piV@f zAHYQ6=%JbOD!1f2V@~B&%Xd*Tx4(F%UO-FMn~1w>BB4pftW?U;Df-F*`eQ{a{ZIvx zY%HciL#+BvA8om!aoGmZ8dGDm?tml&yUIRvIw8(TBkC+Im*u%BnbYu&{UJ^N0964i zD0s1|xd6SP3_}} z?bN|v%M%?{3i==d0H;7$ze9NeBdSLX2Hwp>L^L-gLRt>pdc#e}>bWN%bB3wqv|E_B zQ#73<03+d~7wi^=8vDKPO51IES#=>Q1vA&=Vj5xAszMV~KKn{4sf)=Pja)3Mo5_6ndZ(pyBgB_Q`d2ply?oQN7#8>XJjgb(G3c~0CW(6X`&wb z^LuEmON*+}ahH-6PP^ScfzkEV|<=SZKG?(pjB%aZn_YyFeu=UA-?y)dd(HXKru`8)VTo;qJ`;m4&=SCMaR0%h~;GSQ6Ol-5VhluxyvT`HT;(&GDvm5@hCNJf&&K~+JP`1!2APmPqx4YoOqrrh-Ym9)EfXK%n$(Relag?Y7|ZW zK)F0M)pqA-OFo>pCR;!n7;;DfQ4rj?KDAA?O`e%+DhvWXf>9SOkXE~V)}2v3Ri2$5 zV8{cZ(gS_11r&oah2AM8Ud1CSB~7RB5ES;hqhZLq%_p6_S?SgM!j$(`lMvHwYATIw z+qzW-8Fo0?n%qbUmV^Nb_D7O*T)LfCs?M>5ou$OccG3`GYHZHx^+4X_JZU0HT4m#) z{m{p-+M-!=APA{^i;Jmaa*&b^s+cgZX_Q=KuO@{|P86%5xKXoRLeMQN0vrN+wVtu( z9`j5Evx4*&YylJ{I=Svbwb7bDvVpfl9!2B!bbjxO*mGGD;Y7eb;#B6n&WFY{TUgZ= zR}^rPDQSy~8$cb$X-1X8pb}0MCzD*3##3KgmfBHqAw?vl>!9po*ci)T*qS~I%~?C* zWjdvOt1GdDr$mKlRiF_&ZyirX3pLy6)h=!MHH4n68ILxC3ibg(w&mirDD|9@#AFk& z9^aNb?1b36dN@W$WeJw#1Ehq6COdOfx86IV4O^4gP?`Bp#zU-g>563Bx}8!g<(65J z73oP*@1TW!H^Cv3I=xdzkYx0aZ}f;dnfHfGASoDbPgH`czND)_3QRWZa8aWmK-FkP z-3~~BF9>VdUwv^#)Rz>@LYljFMM1bjCMue^U5>F|c13XpB-P3=`@>Gy2}d|?JE z1oLJ^Yso)PIri(?aKQ+nGmuv^wIGm<0O>hS5aXt)ZQV|&E1Z%L2BYOD8PuYAk9|=~ z{Gy}-{6A6skl1rUMIH1>`mRxVK}ibNvGImn`jVYJ5j)(UP;>%v{My4UeR{63nymwo zpITHDtxxvC{nSp%qRNcT=X|1mWBa~Q@sK8+XR0n~%InMFDGCj&>SZ4plAO(+DOjs| zEmUPr9d_zWyAF^$%|%cyaeI_|&2n&;9c{lc69FUlz?CX?#@>Tem&I%q`7LhcB>HMT zQP;1G5azTuRV52coj|08kB_RW9iX#YDHTy&SfrsTNF`f|0s|9QK&HK+8){X}dtUad zYU9gIK24F*rOU-a1CA+a9!~NTi2(>}YO8m*UgPKzliW8s*|1AJQw-H1q_oBZhTXNZ=q6Bo!WGqNj0h>=HQp$*~ml1`UX8B5GDXeMpFw{fV7uTA{MRES#Ys)tJJ?23nJBM!wy4gua ztj{9}E-G=-U;P#vQkNi+(0n3tC+MI(q7nx;<&|3~ok!|AgFiOi2C(3Ff=rF3z5z2B zkF>vce)WxQcLi}RMoW&e>X~yfuYJ2^p-v?El8TCt_=I=ZlPC`%J+xZN;1f98o9l;` z+C*LPIuF-F`;@N2zxH~)?%f$>{FfucF$Ww8F0!kQU3!pDVi;in%jXrhax7eDsXw7v z8Nzubz^T~J(ogpU^*8?jv!m`}l8MTMdxOI&>r#?iwi`{|q?Lb%*7cV!p4cUv&lU2P zJMpOh00kc>YWX#H`_Z3<(m!tF`;{eon&7TV_QA=Qz9i*~AM`x7%e84KPu|>dk_eMXA!-j@#@l%h`F7QbTbM_-l`&8o=cmGjLDcE2#I}sRh}0bq%~p& z;i2iQ5Bx}@DXL6Wok)}l@)6L?leGtI%%$*-$l8q;422TZD?{2DqINQq)dWXc1yKR% zF;se)Kyoux*1D-XRH6r1QErLY$q6-5dcdyHO2X|bll3d>;{&;sD9Gp0C#rQ+kABe1 z=7BAb6+{(1N%_FcD3)|YZyzv+189hJl^P<)Rnx|12O)QUh$E`_$_%L5mK;bx)T#F} z_`(;}TK6Z$K3rcH5=qxz2>E=mE~IvksG5;&VL?jQNp*z;!IG{=wCe+CmZBF>n=jEF z(=9}7PNepnp#-M(wwk4_wt?pw^&L=Yv8t4#Ejo9J8%tNr;1~(2h{)w4fwgf0iq<`t z{hWR9`{MmA!;HAO+!u}?338*BvZiWncD1yjFse4z->p_i>97Qm{BeF~8@0A*H*yA_ zA*or#&o!2JZM2Qjd`C|YAHeBAbrdvC&%EEWhE41vAi(S#pUimw04HBF3U}%ikJ_nS zqN}$u%!jD^RWCfEhY6BSlays0+U3}MZMgb(67%3ziQCL(Pa!Ec5$tIG-vu9SxV4iT zq)ja&DMM%iNeKY?W5YTASv}jyC3=4uhK?4mDk{>Dq&5IgQ~rV}(@I^{SR!Z%KUG## zPntp*)i2yd4=pE9shD5{pyG<%D_I?-)1jO?0^xs}(XdKqPXh}qq8HtAtxCIw=H?bY zI>+ks7ID}NT7RvofWtJD>lh^40jltR} z2230%?NN;uM|Gi+9sdBDY#%nBZ^k4docOLuWfS^nmQ*<{Jz8X>{++@6DjI$;r%}Z)%DD&I=BS->kM2=q9WcSR7Z*3m`(NImp~hnwq#GQ#u>Ai2ct5!kM?S&t|eH9 zDcX?|AQEOZF>|zVaH_;Jamd#cy{st=8SAI!`N82<_n#EQW~oe_)?H--PIOj0QS~Sn z%gXXpg!Pf1&NEI_>WT&FUfmB+Q7MzP!hSl&KOm`IlmpU^T5!k!W1B)8w4)H_piAcr zrBQmOT66QKcAa~OTyK$@gQ95L0*l_aDu2mdoq_8T@a5_?OuE>FUgLEv890M4@kFdQ zt#V<|QF_@yI`uM?9NwsBf)bY8K-;XRsEH_?p)SEoN+o_+kd4()7cEM50!Yw7!9q$6 zQKK#EDSV*TgbdDFs}NF>B{AGXa|#5zchsip>Xdn=ou(kJ8%1)kc_|N4pp!AAMn05Q zI2vf2o+6`aza%AcQkw&6PsGF4EU8gB={80y(OSTd`*s zcMUxhxW}z16sZ1K1t1fqq`~5fttlYYZ@SoNs@qofLV@WqIYY(8y%#-}$x}_v)mvl8U372Sfy-@B)LH?$5@Q0ejh}l(&YP`o@onb*Hhj+CDM4_dv)jI1qC={KwN;+E5Hu<_oDxda}j*j8xPP)g*l&+<(&`ioc zO7_W08cP5oK;6HINgip!n))Z2$|W5&(COya!Wt4(rpqXm6gNcz4(~5vA2w9SwwAOv zIfVJ+yO&)J61I-(EwWafOgp-$S(!tkR8!p0sZ9LAJ~008G%K2nN}8%k1p;4)hvIn$ zL>tj?X~Xg4KhQu!FEaxUE4UNFgTqmP2pI9Gjd z@p1^SwP5jAzl8EeDV|#?u2}uqn&xT}6onzgs$hxfD8VxPcH+(CJQeI-GtR6skq5%? zYM1698rmzUTEE}=f1rgftadBHItuw{tGA+|1uiE}(I*Adm{d*nF_R5KYVwRJs<&Q($Ps~AJLmnGTLbg)&nVGWV0b9Re6JxHZTrhKR;X`f>#TXuw#s7MY=!fn}_7+p;= z9deX~HnKICGwUw#C2m3LqpuiRG*&6?cc1YZtbX0XRaC~B?L$rJE}cmmT>x#GPe?xf zzTpOcWtJV!h&_GL@w@z^fEJU;gEx0lD@F}y(xX&B+ z7lbO6`p@ZEWgBW$mmz5EeV2lKzG1J@A||u<0Rc^0-k7y^p;ESD_bGdPAtxA9ikqZ3ZG14S$ zWd%ma($hk$0b>&8Tt2TG$tc*lQQRBa{VRiUN{IZ)@FTRMM1depM=O}^6-0A?w(OUM z?^PAH_ME%n&p3ZaR+N&ubR9xmBd8uR6D$R%MLTPD*C^wDc{LWQgcBlCMCYk6Iq0TF zsYAedVyv|SolDrZ98r<(CJ&5LGnkg#Ra@}c6Ae6-kYhQ+b%USKaEGx={fj@2T5DdO((#x3S$5msv(VnxrNPlGpK0cR`ZTpsI}v*>R;G z`HVVbuw~asy;%vGL87GI$pfkh38DA9!uI=iR>vga?RvOoe$cL=eWXnc)XHukf%#Re z<3{#fEJ465`YYzgF<~kg@%Bqojon)FmXf7;fznbi3=i5$m6d?%6_MeYZ8k||N(#c3 zv#PG;gkBd17tuPeUI zRDaJh_UcwbCBLxKQ2{(rNn1uy_Y_w;-OrVrF*~pZ@#bM^!j}S zOCE|HpE9csNvf!8FRufkCPP_uoI*}DR=a2V5@CzINE<>vaHhu6oG&PHZf~kf0Eu>l zdnrFs?pE!KTcNUz<;y zM3!xGoD?B9H~VhYx;FPPbbS<5!&KJYtxhear6`(YXA1>32?`~0C9<}K)wSdOh>=m3ePLz> zuY|8(Evu?ixG6yV%reM=eaaZNWFtA}Sm7n1(^WggZj5&W@lrnc$DvWVGYS0NJ~88~$ctnldVJW9PQrgqZ%sVGn)Nea`fd%uNeJjddvD86T!1z^X;8(Qff(W0qo_nP*daN;a?0?#aT%cNql<g*R5eJV(?QKCGtpt$|f)`jbA9?gJX} z^vx;6vQXUJC~;0GfvmOGBx8tCMqEm0oJOs+?jl=BD(S2e)&&ke<*ML(>c(9q}#%+2TK06&zIb1?faE`8P9C? zq~0CPxWhP0&R}_~jUk$Xx_K$;O0+d3huZ`w6zH_-0Fe_e?zwzUeU zFZSw!rTTLMTF?DmpoxH@TtbP9zgeqPwaG;~>DR^ta8i)~Q)OFZu1Q|Ul#(Yu52P~K z(ZB?me$`&1tLq2r2@)2n+grKvfEPp@5AB;P#keBDn%7xsDXq1PA#B&JvX<8|x?q^{7kejpi%yHwycXTC zvWMnOM<4~aTDFNg84i*HbBsF)5ZWlLIdVh46xK4<_tKSh2UxjUFsl6B5ykzrBF9ja zbt&>5{UccI-LW&Q+nLWuh)|~CXs@WczUS&yBC{X~m=Wu129VIa)qdf;gsik!tH}hq zr2upjF)F~~YNH=53Uh6k?RwIU?<+&x{Ghgt5VAbDrHw8(S=rJE>!~nIZlFUJd{L+Q zezR}39sh2q{sI7@YXDO)lg|rHw zolu3Yk8-vI%}O9APNejM2%xGB^X&$UrVqF0+9!!CxU(PSZ*zFcvn;A(|>Dd1Ot{QVR zUou3PtDypNJs=VhYMG(>MwB}P(|CQgYgbykMx6R(%%KCr@Zt|19# z+!mybv+yz9sm91cQBdPcea6S9J$2KxeA7hXxzRavmbz;-AX3no%6pIc;lkQ=LB2xd z#%I}@QkN;&REU@W7zvK4N7~weDpMO|v)5%;REa6Z)~_+~)*WPFqNPsT#$IR?)xxlf zdnF4DF-_{$)Bu*7mZe9ai0LKX>IG-Euxw1+BAUUT8WeSKJplS+GkLO*K72Sv2S(K|wPBbO1nimqh3gcGkkRE1ynf z<-5NpWHl=KXr6-|Ko}Up5fO^s{kL9ah;QNoGZKNQ`6py5$>1O)0Eb8%u zxk~4+%5IPV?1)cYB?pO*MI0@PVq3@V9JR|Fb;XpgEU%DnmfKRuJsPa5LrxG!f(nc( zDd^JgAffY7mj=e(u5cf>Rr(2WR~zFzN5`0#9by)?Eg1eOLd6{g6Ah)b1hUvcGDsSz ziId|P&PMgq%wA7b>Ff?BJ1bVk!Ab<-Kq_T6Jh#job-~ykMaev)l~Li?W-U(TP3rkt z>RM%{+etsgs+46Uq-sur4#(r=xqjA#U(a$&YY6v9bzh&S+?(D-7rvizS3XWqaas#FipS;9|5OIuW*sd7`7;RsJPv_$VY+;xtSo(|C#t;udOn2)yTxX!4l z)q;BI*h3N`zl8-;saL{L*1D;+rAwTpF&y3(%RyQ~jLxvHa3;Xjc$Wd(XD=A*T~)w@ zw7HKGI+#p_Fj@&l?K*b>W@2S)fQsrNGb)o`D(s|{ZR`(mlG^0&FfGHGe? z>Lntl9>QBF0W~*wD9+DOAwaW6l?A!qY9^uQE_vK0Sw`XjlxIm6>D z4Gr4C@LW*xFDr64F6S8+D01FoS)cMQT>9!VJfgKzapwsoC~XOnT4k_g+eiT^9S2f; zm7B~5Y8G~Eg_~L^0&MGR7WU;6Ka}DX#;C2RnfD1yx=s+BlcFqe zDD@f`K!3pw*8!pkwp5{~_d|_lslg_QFPu#wJC=F@4( z7;2UXbvh^?q&-z!bY*Q?e&EenKCEfbup7k)p|G$ z5u+;j^*Tb5Udo}#Q3MI;;RB{9hBm*By@Y*4my}}P=7iC5m0L;@uPTK89ic3p%{nKH zV2w~mZ4|EgfY?uQp@}%MZU>8Ys*Sm|sOXe56=4M} zx;8Js>HFfxYk1dmDy@5H?*z5CJd%3pE|D_}gOz6PGL#6aA*aC}tE2=rFC~z5nbUmR;Yl=x#Q-v(dXnV&<0d=I3g09lr>D2&< zNX&MRjZ&P(o~Z|s@x`j}!;REXrNXdexeX!M<}^{0T7#%j6OcW$)4Cq2wKw{A1j<|; zKB7>!LdTCJyKJ2BA26bA{{WA&d|~%dQ%I+pM@6I)r;GsmSGT6B#c!H|T@HmNS*a;I zRpnQ3bU%c26lAefXpiZi3eLn>k>!>{;ur?@I(qcEAV^YW z(8p>hhnbZ$Lf1;gQVCK)>9?^5<{;>9pB}9$D&rRv;D-vSSMB@WCoy|=xY0Fhyt{nG zT3Gcg&$y*6v`<}%C-xYWnp=D3?46>g#aAP+zr;!b%lQ{U4uxPMxrj|-b0&|&% z8FJuIGFq}t3P#?s>RN;>6ap93Qu_Ca%M7EMLab(Z%x?!p3c13|Lq~6}O7?@W$m!u4 z&l|`(M}_82;9waXZVQ_ab51Zay41DN3p)}zb=EcqLJTX-m}|E-=(obECa$M?$)12| z6=y`X_+~q5gH>Zs-dS`dB!WDul*HjIf~q?wm)q2+&zfhDhpFr&(*(8Jg46 zRL(m*9Qr0@$~T%bGfI=yF&nsouI{6SQKh=!`f>xQntOsL1Tx}?JG(MPLN}_jQC*q~ zXUO61J}^LYT%(#_5!a$bRNJl=hrtO_cIuZqb%7;_)k#Bg9G)&Z?{Kk&K}F|Wo6WYrY$Q8yN(8mNt( zaoR_yqRf^-(opBNhH{G+oN>#0>OpH}y&A&6T02?9Th(1JT=Sy&N7ihe&3aG zl~RGQW{f3Xh-j0(yJ_P z?{WKTK~kF^n3VqjbOCj1K~StV7s1p8S0^5d>WsJgbt5ZXN*1qKAu0tDGV>4biuoS3 zW0vFFk3}_Y)fb$7#+s_$jj}Q`A0CDtb)Y^9Bl??~YgKe#Y;SDXC%lXHLu1Uc=WI*H zP|K^dQj`j+mR3qilfH6DB1cFb?-1CVyoD)=y>;}4J=9$gVjpTRXLz`6%B&UKnxPUa zcHvUDwM`yrHGPh~24L+M0Ug zB&j>c_*QgOzHupz3j2ZJ4CjHlGbrP{^2F_jBgN|6V!n;k zsU(1-vBVj9kgzu;LqZHqWHIge*lG%><8KT904@4BFSyq+de-Z7>tB!`FeXAntVe>6 zmNg~rs;9|qas()>OfGZn9c!pkLj@E^%>Kh7`huxYUH9S0Yjbin>IGJE8{dx~Ib~qo!RNJ#-0n9eYX-d#Iz@-BUvCp3G!c z(m?KHAWwBWw4zF@1&(3QO#vo)=?khQkQ9cyS*&wb*3dxjq%Xm zyE5XN2wb;y)_r0Y@s*)d@S5m{-|14~6tE{acG60O^sx$6HWid$scBMXHSOaCwV_13 zYM4Gsv!t@Mgik_eD06LEh_R5d-vt?J+hq(wCSD(Xa^DpoF?@ zQ9jvQ8-AZaPyjQQf)Vboy|Ra;bj2$24xb3;x|Qvftw^O-p(zsUpoU5=H8p**UX1gtuGxlw3O=UZE4^mfGQq*V_B9+cZ@rys(h8`RH zTY)Iodxz~bRgUoIXR^MJVW6NtxpPtX;`Yr;!#?EImD{L87HsLXyunBTA>Bg8fOt+e z4$$1X$00p~x-`^&1w;26W>XIaKz!dTzYwTj^z_Mb1#$opyZr=u6|M^eb4gDA1$3vQ zxY7EAAQCnCN(H`PF%S_pWx_#KWcZccn}vm~ikS%#KREHnJGI$xe--F%J1*z~2&W1k zAc@H7@rr{ZDneqc%k5)a%Dvrjog>3n*7{O()cQwH*%7t_{{Vls1XONE-N{Tm=f`Tl zZ@ga+Gs=F)`6RPUmo8W`TfJLVv=tB0s%`*&C?;|0?s~?J)k%@<-aR92Em#kjUU|ET!bbz(Qw#y zQ0g?7;|0f~!iA7(rjOgE%WS=S-r&aW4TmYIuC3H1vH>2WK4eaR7;89b2}{O`-Y_on zL}fv5A$((@C@KX10NM<-BDdR@4_9p5{j)JaWqrVU-1BN5Sh4ZvbyVI?JxyGFUYbfb zkDN}E>Q(580WsIrZQUU_CrNai6q}LB z$+cCbDFH`b!WPn+aS33}TYGenf0+PHMB`{ zYL_*zxQX%@JeS6ZJk4RzXMDqrz zFIfKoQ#figOI<{kdE?^@xj7mvTZL;NiFg1H%x(y0i*la7SbRDFp zu$RZiIyFqoEdq90wbR?FmbIiMNdr^T91a0R8JtRHbG)&-X549)0RVpJ8fnSsrR?B6 zN{cmrtG`@IK;9Ou2-b1)KpLxDHpJ)@pyO;ci#;<{kFt{Nrht0(h^%mPz5`m;b#FDulvLYX`x6-9W=kmY8)iwFAmTY`yj=sY$w@iw5 z)RQ6vq@I!x<;~d|D%=q6)UJ5>+ng@kpMI-hx}_%Ag^-lbMF3$I@K;ibl2Fk=qw7tS z8K}goO%!9_RB+PBl#k&HY=}pVQH?!Xk|a;%gqKka=tWVBRcArF>mA%En`}tIHJefn z{{W;bbu>=VfsWLM7!r`fM zkGUd>$~dB>be8%+PnS(1%VtWX-LzDv_O|qYNhGU6pUd!xea5s=4UH~6ntB!9_r0-x zwL!Lj&2)+?Bmp$GvNeT|z&f{EJX=t|+losq6!YX&!U*bg2aFsIDM^Q&dKzk+lazjV5#kHali) zi}aO{GRk|Yee09B*Foj|zqgu$RnDL!^!?BydaD^aCi3d~MD_Z^uOf(c1cj}3bBOUu zqZ-tctaq-kL)mCjS@=3 zv4y7m5w*UOuC?IBD=KwHQvh_FCRu|uDTh13NL&ksF88O|dZL+0X+#xFX)duvBsa|8JU4On5MN@Je5C^=zUbF8D36P4^+lo;=lM8HhD~^_dX$@}t#Giz|FzARO z48z4OvFr-_lepVf`UMumx4fjNBu~6qFnm_;ApNnoMPJNvYim~ej;c)Jyd{t0h(&Hd zu~|?grKHFN5p5@6YEQ>@@${hay#~f2^s8vmKEtXq9^5{}# zNE!eHZ;;zsSjIF0i!g1S<@@WZ<@`~Ul%2gg!-gfH8=9@bqoFB<2JTIlqY6hJVOyIe zB|QY;#+o4R7}tsvyt8ICv}y@?JDWYh=?}$n=Blnsjzu8Ts&j3bbtOj8`i-Z!R*@$S z(@9i&-aR8c5#p|FJUp%id}jr|M$twK7dOFVD-$uSEv^M5+m*dFNtER|_l^k@MgoIX zRF6$kREZs7r#TUC52-fydxMBBe=|Y(L`PjrKv#Tipfq1$HNkgsqGn2l;iMxIAasdb zp4f|ZJh1xow@S>6#DV_+Txhn9053A;Vs_%4Hm)fx%_^RQSUfkyR3(hnHkp-DTx^u6 zOIg4-r$uAILq{lC$x2I&)PIXv{bM%dMy;ashX~QZMm1U9XL8VYqr;?T#?irV(BvB7 zMWwK`DoO+&j5P78GjKf-gK8&F@b`r6O_!mg%e7s4=?wd6q&1g1Yx4f+@g;Mu`aitB7YSGz%IAy?T@&>IoW3J8mlF+u1g{t)1hQ&iiYLNsaB32jWrVGCdsO*oU$8#LQ)qyyCT5IT2M8m`dIN>1cV5%h;y zBZB%!K?%Cac=FeFX~6v=SGr%qC#TX0j$k^d>zgi=Q4f(lw^G%It1WE)o#gHyqya5@ zXd&0c$FHEjp4Z|OQoiy2S&BNe)X=T^R{sF=k|sbNAVW@t?dhmfuw~(xQ0P%L8|15M zgp>%HPLakrIV?bG64Kb6%LMhDI$5PuYt0kAj#7G?M5Q71GlQavp+j8V3Q#Aa*<>qi zK26ga33r176b;1GK`xdxBT|i^2yR#n5f;~a>YJL@O*@-Nl+Rw#99y+dM{!o5EmSMw zWz@nLNGO5<2-%9mr~hSuSq0L zTK9&~((A^P^JoMkC?4Pr=SK}pPY5IOcESuj;%Fy_KM~%mOG2qtAh$k znX6%C3k4>9QhG$Cwn|9!@KCq>ej9*vq4QRw?W68hpYfx#Hf}?8S6ee>C{5f=N=gQv zbf4c0WN{Xm25JCuRu_|H@Gq+ge11!yZVC5K$2`eyqpPxBn0?BWrcDKaprR)tCm6pZ z;WOHrfui*fZu`k;l%pJWzsRY2!-4j~k*GZ*IhWI+UV(~W=F}}tkclV>OT2u;qHGOQ zZ<%IPmMeSjJm^l;nFB7OC2_V7mHH~rCeFue86>J&_g(I$&1f~$tGKq?t}LM`D-)U0 zLUNBlaQ-;v09?E&-LB*U*KAdhknrwp%CygAl$482J#Z_hBonzkK@p}-<}hvXUUI?Y zZe0HWcmhEx6J_}2=Lfw108717sfEm>ZqO1uBE5SV8m-ff@)q2-heD+<2w`pIA)pxF zyd-Xv^@i=bD1K1loo-C_*40g_p>5EDqM6MpoV1h_A`fb$HsV`%R25R(t;1I=Zs%AL zNTj7I@`4EOuR#_2yCT;ue~$7N$ruFcqqFwc=$tJ)(L$%(K#pp=w&*#!E%dHEN>Zk# zl!M#C6w+FBTzeYkrOhI(x3*0sVp1Jgl2k;9Jv503oY#$1G_X1|P>^{JCs}7->M4+2 zr=OL;45b{B5SR__ z{{T+8v{UHRr9~i-;zUH;+iRkow!c1V;rJKaoxb$NHZ_~AFEa2;bk*uq#<~fZhaT9RHYkdGcO1< z5L7{7VNt5EC=-@{U$r;2*Nno`Aw6fTzhp+rDinka z0MRh^yx$_VILmb<+GVx|&Y%!X`uBiC%x6UKdrpN(OkoWy=ADd z)O}REw_BS;N6cIl+mwc`-A;X0N0g;HnFv#x@J+L-XZ^c#k0N_j<;w>oVO2_^yI7vv zaH3Hmn#x5Tt}^HyQnC0QV91`?BP?*Lj*{j}JAo?~b3V|0;BlwBo;sn+aO&hzU#Z(| zIYubr(rTz`3Vgtg2}wVdLqa1y%HrK_{{Vv9@EnRO*uk%Qbdg4`6DefP}b51vZ>L+bCS_zhn&!8 zq(e<5#=c213MW4EM@NxRD1eozVC+_g9b?L5fT7-diPhSxRO@Y(G)(*SpTZvwC4ASV zHyFC4)HIR^RQ3F461L3%A+B}mrfp*VO9PtAdvpDfBO-v0ujy57$`xh2!m>w0)J1me z(aPIuD%6FitXLyoq%j+l<%sc$*8xP1uv=UT4tVI4bhicRGxCnE&?Q~Iy>k+U)eJ&h zTdR~M<}xtjO#*1M(v~!rhY%AbnGHmB(p3%`Mu`5GBXE#6{@6pERHrhkP;l#|`lgnx zCnA0)tW4Q-Q5W54oSs;@uHQh~l6IZ2P0KVxTMflV2Xd#1WLzYk(=ihFk<}R33lUXL zNaVlc_SfMK84&^?QYz(>KBAx()2GL@A<`z94~k1Sd~wQ|DMeF+HejD~02;5at|{w! zj^ZQ?;cs`QsSFfwQAZ%T)Y9D++fEeuyYxEFA}%#5&1%p>b}(FpPNk)&?f~>Xj0tfM zr56l`c8Wr}=&GRv4azAReIQ7JDe_cwNLr={T7eK#T}2wrYMQLt+#jQJ%1Hpv@Pc7= zLSjuED4{+?(WI++qK&dAUmao|>`<$QIPPLc7Zgs1>*Ea%H6s{37UqhSQg`(22y~Q0 zwUkCoxUg5;cZ3Jgb)g9gaeDYiebnO^o=zdYxoF54P;h{vCZ8Q*D(!k~q{+X-qUAuBbouet9LD z-ZkbPG^(irro(k&zJi#YeG0aAsDa_R1})!i*%4`#xwdwjow$A!*Y?#_d*P2V+S}ZX zbRejM{{XbkELFJY6h70fo6Qw#prIu`r=qsxbn7X`myNooHgRFAc2NGcABnUlhqj^G5U_~YF_5agE! zy$gLM(WdKNKb0-LHpx9S1wfdJnqQ^7is!+OMq9=%EoFnn?V{^n-tQ}5bUxVmHJgp$ zhgivMRrJ&-^&278)Y7R5178yx=Wsc7o%NhiMD5RUZ$*@FJ~MX(@s;hPSz6cdEAz3f zrZl%ImPE&<;(#CQg-ShD#^o-wj=n*8+m*GV_Ch?$SYzvywU>-tA|i^5bTm&Du&dKX z-E@N*!BrOErytv;#Zvpy;aAU-bXoqf}jAI7=NQwc^ah)Razv2 zG2G4@)ifFw*Q-*L9Vb5V^5J?+)>h3YSx3vQ=#o`+0JA_$zf&h!d|;pxTaM(JsDD!> zK)M2?P$N=jDaUs;DT7t96&@09zT#WtW{nsTCa7b?q;_nSuNBo6wE0!$o^0>{KZ}oqTp;j zn5uqavVKdE_vYF<1xS}S2ur(_KLHZ+3aAL8)ic+pPn?x$uGj^v(4zusu8JTm&O~br zJc=U#@=d+f^+{j07XFFwoBNBtW8(u75lu+i_fE6d;Q&gHkD^OeJ=AychPITkphRyf zW?<(=nzm;V;w+i6urfJrw8=+@vK-!&Bllx-DG1%G#o5Is0s#iWf<~_yS z`9mVDx7({!n@ejTs3`fitLXv75hkTP_lAlnm>U2CyH#ppS&gEPl%Y)?-&k2q1SJXr zQ_@}GK``;E5Ql;2WMiYEMKwYjsT1q-go-bygnqFS)1ZVrlAjT*S8+&`kIz_d+$+A= zPOly8xKyAZZqx}u5I&_w-!P3-&Bn0jN*Sp1rV}G69`P*;G*#P)fvS|)cl@Ibwxc3| zJiQ`b601@)6uqmvm7phfo-rZrpq&saW;2oAaICKzQfDIkcpfWh}_`z>hnuXA-{{Z_${8x|hq7Cnk?-jhEPfNATWt5>5&sHTH zNli-pp|Y)Jb(9Gw6|xTZH)-UK9NQ-N{eazk*EJsIJ==2s0KBeKtmS@WyqDf=xQs&) zVQUu46$^{p8Ky$Itbjd7NZg~~BqOt5fHp=b(2-?|_Wg2to_tdzyQ)x+%^~3ohSf=# zQexwFN`j@VeY&ABLQ)FjUK}MoJ}?7;L;z@#RdoSDM90J@*eH`#FHV05RYv5=M0G9# zkH=ZZ$K^ys5^Ax~Cj-AgyHuB}d#

!agLNs$WO5>|%(TrF)4d zp%5E^MTVNB{YQB#uV`o$B+)>V)hd{n@7@uI(^G=8ZhH?Y9SYR4;TyUvcO29HEJ5fc|h+5XPB@OOU zjHNjEyae9l<6_ta*8c!d)R<6da_o9UHcvkdyJ|{Ry|j|LjS6Y;zY5aPh99YBM0X%h z_QoH}dywY>*vh`)IIf_$DQji3QCgWpY%-CyL!cca2+3t-xP4w~wM>1RA<>~Ie^Ib) zm`GPq)?MPh@JUCB5o7P~(I-OwytuM7+1I2dUUaK z%ANjiQ${7U04J<^8-w;zNgN+K#Q9fqTH3ZQsacJt!ZB+g|{G6 z`ymt{uZ*CqwFj=)=t=Nf+poA%`IxF!of1sMIwWxDnth}%(ZLxrnA|DKeRO(>ADEBz z$4%RU4o*1>{#_D=k36DsNp)KkbTiT(W#CZv98y8i8NHEGxY-DJDt)H~I_0Dw*G(|l!B=!5KugIkFhfkaVwVN9M~$S4mo^VEH@>{ z$VP>4K8wv6Tzy`2OM#QA4gPtn;9$XYLajRws&6u^xh%G|70i%y`an0=p{>lcvKySe3B`EXYig3VyOk|UPzeWc{H9%gkzZt?zyhsPTfQQW zmu#i11xW!1;Sb4Z*8~iC85mF(ap#$unvoLf1V_Hag(q#9W4fZ}7}TqKPg>Os+;_ zKxM2bAzo3XI>|j@#)KByqco+rG*}8r>LoZLDW1Y8Q1aTeol#X~ohzB9chaBmnUSnm zAkDmJRBX0Fx+e1NM_Wv#exZeJ9UK~@{t+hJgI*KxMYSZ|J+G~urxUE|!riLZ0?%Lr_g=g6wJ#lPmH41ImGb;~?bIPcXi`)R=khRE3LTSELYH*a2_Z62T28(&x~11dJJD(2QhW7*-U(b|=z%|E zW&O%6C25zNKw)vbh1nR_bxrm`$2O~?7KKqEey`?E-8F+_xY0+?Xgm~P&V8{^RtIXW zjQ*0Wd61^&0N1~aIv`{9svaA;;rN9dE*yW8VihVC)C#DjLH_{Vl!9g3P$7GEghO&I zkW-;fMi_>lE$jfOb<#u=<2aDFVNmx*ibdKfW`v!95iZdn=cs9)RkHPNW7Ax~)J|eP zdqVk-@k|y-!W(hP4YDUwD2KRWvsgZ%H!7%-$S0>rk9MkIj3{e0T}eIREv-}S(Ht>U zG!uun6w8^aKc}qItPmWVbb2F-YTx#wwM~X&l))3~^CC#c+F(fg{U&<268E!pyuBD&Dil!R0ZcA_T~3rJ6t@MM84Pl>9}4^VYgd33ZPmqOeHP8 zwHc*K=`egFIOLoKt|ieeuY>oROLmB^ygXFZxqH{*g;Dyn`5iefr^v5WGt_+|oX!a_ zI;}0;-?1XCDh2z4_i00Vx7JhRmoz%#=_zeUT*jg_t}(?M9Ses=gY*95I_Np9E1Ms1 z_k52n=bk6GR$dnUEN;_=}$zJwhGlLe0)Z**S)c);H-D{ zre_Z|Yxiz0r>eGXQ$UvysN3*{E%Kvsipz1+wzN<$nmw~p-kGgZXa?m^U7~R=$2L%@ zQJHO@r$DNQmHn?*TAZmLpUw}02Jm(%@5&Y<&ZnJo^&Q0(@g>JWCfnX5ojRNS?my2=@jLeoN}-5Xvi6hY)p zBXJ6S##kVxWhnRwfd@6>iZVtPofJgBV^3Qumeb7?ZJx60A8k!+1SIV%&QVk&(sZ^0 zgvDLp}m&`2rb<5eO!^O0_N z(v@$xH~mJ#*oY*Q1V=$>;xAh$&sdHdQ>wl{F+4{ixHHbezg zX1r|K-u)1q)a@!$s*ygZ9XmuF<}+NR?<*(L(bx! z`un0AvbvC|pH5Ss!Y3qWrB^BH_|;Y?B~^Z-Ev!`K;C`knR}S1&+Rh@@1de5Tjb{xs z(GxRuN0iG3Q|k`5su9UWPUfkWkm6;>RH&nL4ZbnZo0p_I*Q9k028i{l&Yo=q zeO1RcsM?aTCDz8_GDM^0$0@VHs@z%JphUDiVs6W#g}v&Oy!Tis?M+Qu9;8FpVu3Ma z%~UN_(K|@rKM#~exay@uXs)K>HYjg~WI8W|X(@5J4W>s}Wjxr?WvaNjUXeM49X}XU zXH?vvrPb1A+Ta~!q!T)i0PYGQW$QvKog_(NM5pLtHdmUfWq_+ujv0AFD?>_F*{JJ3 zj5i?#szs2{)k_?1?A=u}X!Wr%Q3 ze=s6Y^lqwU?5czOi)K|dQ>zJ)Nmi2WI2QhdDeN=pqGXh^g&C1Aj6)ek6|u3?R3WN_ zqz!&>*ppPZG|?EdP=bg6wT5#V<6mqNOgghgj;THWYhhto;>xFCkzQC@N#b zK(qBkIv-6HrSKL`)ymkG6uFflJx_SUdzp>j9YWB%e|53+B|Mi#5-QZM;Xm6RW|6yG zw}?jO)lR>+M=s{vQO}fioGSJ#uV?I<3fGhZn@~!Ns0AdAXE{a}-Tb~o6y{x6zuS=L zO4B%oI>P=3L3qvV0-$_U#QUuKRLS{^j&6L5a>(j$d44|1pJAma`YTF7Qh-F{A&$oP zD)SqhYwzVU8Hg1q&P|WC%Hd6|q+&M42>>oQuC6;xQBzL)WtZt(QWnt~q=O?d^pDpo z8`e!2b9xZG34d((W-ND5QGF%z(tVOocVG69O42qBXHq6)vz9ZQa+T3iqek z7lN}tw{N$q-fYhqeF_w`mbX$|eE9Ve1FU${?ti+>#6T_BYWIchKepZi*`j^)gVM!m z-t4%V>&4=do~hFPFQkCd%*LchX&yrDpCo1Mt^`rRYyHgp$V80Qy%lU-(KpnkApAPV zq3zv8=Q&Y5Qj1h>sY7Z?PM=7*@%XDSP}!X$B}2UK?1yg!y}%JUj{Xtst}z&GxL#W2 zPA-`g+UpwJoqXM**1=U!F{cuOxiSNEjJiRMc<7L0(J85J3G$7?I@h3mtx>agFqHm% zK5*htW{bye#YmRZs;km|N3O8zK}z;0h#s#Sl{IMs zxaypAdY~raC)f7EH&Ca8z6&JRA1|9z4ttuU81+XKmD;CJN5i~23hPFxU2d!!ldhA8 z80sco%~PXht4Z71UEs*wM8u#cKHF2cp#Cte`l0qE&913TKTh~Y{{TV6I0s@{GSKHWK^j`2$lLzz)=&m!5V>FDm*z0KrOCC4e64SJN$Izm>3 zZJehu8KzeQEN?Cvg|hMfTWvIAHyy4D!<^)vJ!;x+?&kKEtzkRO!B+}Yv=b@JsH^}Y z39!gbC~B>y{tzp;QBptLAMVqJV;J4gcQ3{ zK_Mzk(vGv7gj%q;GEVpScq(@wxxp=wJk+zQk1eu zB$7!wlNQYfMW0ZUQkzS?cadUp)S1E^(HRiYH8-l=p?dkEG@Vg?*iB91zj0c7j-ncq zq9P!ho0UR|G7$4$MDS&GNDGZl=tKg9c+}$nMid#V~rXl&oBW<8zqqxOb1++?FbOq8mPBS zT$9;OK#B$(LS5QY4zuqZk-+iof(WcPZ@JZeTZ!&%&#P%RV{x{eQly?kuryvhox4dRJEujQ`5ZQpL0Yrm{OX6g)%|ZLtyH;O6a4n zCeyUdO56#NnIQOUA39S-^icrh)zjQc5iQyQ4K?*fx`k?b=C4g#R7#J=7Ypcy+7f=I zxz&TIpM>E>=%z$UPE}PpwGp()ckQGAUTVHElt)mdnd=05iA`-06Xn7Tl69ZL1%8Eg z7FMW=p-&#thS5#vleJ!{o`z$$!W90it!Sbos!N7)PgrcD<4LNPTr=7UJ)lCL6hxI2 z?~|fVz2zM7NksJ&&+DQ5A&EgXN5tV&OHoSs1b}?s2xvt?R8P7JA*iExAIg3@Ll3D5 z_=u=P6;yRIBWBp?X{s>1RS`c(_#$cg82I!}G}RWnO7D5V`%P+|4*?gwOt2;Mb#(|~lAy2Sk(32@c^pmEGSFOf)k%B7_F0zy1IBA0bJcU5GGfv8b<&&ocq2?)VGJQg8pocB&RLgO8(*D>Gg4f|nq zxp-PD0zUT;#G!PwPgFkJ6zvzCbMhr1lLp({zE^E%B$XR+cF2WXXcN_kaE;>i=~rK8 zYoG^kY>6#Ed=H3?gNJr@^8LZmdRnsHOWd1lU_*5&;<;Wp(}*oRvg(Y;lo-K&V(oTa{kqUq+KfTAcOo`myJ$EIf9VIHS1&y*i{H5fZsBT?MmC zS>UB`JQi8b=@oEauKAZC+!u4n?rzsQ^0#_LYTB9nBOcCj=_V$pMX_Qyyf))Osywsd zt)pou+64R~JiX47;;y#1`hE#$-L8s(8`mkMbux$vFJzGDrNj+jS{$%4y;6yT&>2VQ zL4XzclFYM$C9TF9aH#qKOQ+1o3=Hy8R@uil#Sp)UNU2Fg{4~}F`cqvLv|NY56jb7i z3I70x`yp<#G*S@f#d@TLWgKAc&eb4|!{Ir?Eo3ZpLK`J+$)YWCmpG<-O;W`&9)I-j zl=+f;V(Vv;4$`Xx&v9q5#DP$kZ)FH5B@WL`NP}O0$8cLnBq-T_Qw%YPOt9Lt8+KX=*CzNK<5qc8K=5N=pkL z)hsQ!d#ouasYc|aOLnkaHzXZYHQp7D=$y+wMN3u_wZMBwkrNPHHOirXihDXGN2N(x z6TJyPNkZLh$Ebg(nlg%?u_k3y<3p$ufMqL#DdBxvMJ^}eBx%$Wi3h>0xT#XSyE zH>?tpIwb4xFw@|ok!1Bkf@dH!CK?f>5K)^)Xa-Y{h@$z;s77oONivb`9HmMUKvBIr zDpGu=Q|k^nwb2fSg?e*Jh}=5IHkI~^$En$UBq~xPP-XqG&F*hRhc>7UyNl~{TBS8> zpdmUB)PO+v^y?Cq24}i~RHQAr&Gf~nkP?Qd%6U0;WaT+%j@fw4hA z>oKoU6Af@Gxg~3=OXQ#?k<%%Vlhy&aXPTD81MUSDE(o2;$tGjJ;}v_KnQj)2{E&=U zqL>OwcBqc9JY~fPCCYNK4o1yziiRDu-Q6rV){$x|M7%ZW5)GZ>MGtq5f^BI+r-vAO zyP>ZvP~{E(08XkB3VBD!qtmH^;zaKFm-7;|sA?zL z2~Z>!ol~JfSj|EZTUaBhAn0I)&}xqwq-oibnVC#|VYjlW^e6f&N)zG^{4 zqz4=urgfLfAnqJ$teE(ynrEttRHGTx@$q*+-VO95(`q!hO%S;ts$=j%aW#^vp^>wQE?6x(i0|svO0$gNA*94 zPxSGRsJ_}z#;!^wCVxF+p?ySoOLjYim)}u8j5yaCq!O02H*S+$q11vr(}QTdPhc)c z=_!~gjub#i9;wNuyaP}9^hoiC8qm3_C9AxmXKKNhRQSOAz$l30E^et?iC?DbN)}ll zjnXnOTW*O7tBhkEtrOY(UcJg-Q*d_@^E>S0SWySD|vi9BP3I^u0s`>8HXe)?9!&`jvm=eY%m68U)1T zSW|T&#IBS9;V8e^I49+b+OUW-B05f8<4wKH>Q$Nni) z{fg6+GwV{ymgEG=B_ORJjjed7QPq4#hoV8dX|1z9rs|t5%c8dEVmfGcsW|QfwN9P~ z$hT}pskdn=OO)#))udUP2=Aw84qJA-B3p7n(OVC;-*jrVDT7B*>(D5t_11-~%^oE) zh#Tt<5Un_@xCV|^!(_eUys2VE&e-#AtwZ^&XcU!g)*~AsX`xoJ405)iMXp`qG&qWy zss<+NC*=w0_(YhLI4EhYOG>Z2p50evs#+gxwzMl<4`U4zP=zfaEopz}aaP}vUewJ; z!2BWRk3!&mT%C>`%c&Y=c8d8evoMlK@%Y0rynu~kTU1TV{0Cvn(*5roN@?!s1zS=8 zOPzYlxrDic!E$&&8YnGu#Ov%ei?>~AO*_nzovISleEUacWe}e62cl46xjo|yx+1QD z^Xbfb%^gs*cS?Z-5PC;#pVS_S(tJjRG+goRg4LXhk1_BK19tq(Z>h|<<)RyEu0wL) zq)3np?or+&Z}CQj%u{&$rtd@4W8*GQtEQ<<6>?mmt*2OOs*%7@<7-Kg3QFWb1avVG z9EGNZA3PxH6_RmBEcvPF>S{LvK-y33nduThfYc;zAgQ%N-J>hEINPnI)ViLEkvfTX zfhHxYRzFx(%kD3O>v4+b@(V>VT@&YX;ULIq@Da%5p|6Sxdtugzsp0Q(9OLcc7NMc>N4mlBz7{KKK+vlJXHs7>7jLEC8^BKzU1EU^Q&`~?3bVK zU#5Rbnkq+IqC%6?X*B6dq<(NAhS{A0nPu(Vqt@b9I6hNZ$yk+r1=g{5YSa}tw<;V{ z&MZnqsH~Dt#&JO9v$`po;n8UvO4v^UVd|^@0Gs&5`(?tW=S0rdl%wh(4HK`zB=Ohn zmNBY}zsEW$3XF8sZ(C}$B?Tv@ouXHuQB+bl0L+_lNT+pkL6Db+?SzuL6Y#4Q1^S)0 zP>`h&Abbor6x&^@MDXrvKxbHm>W0l!x>i){NlDaBVnd`^VZCeEq9w3!a4M*CXR4Ev zR;9+u9Bj#H*Zf8xXK~)GR?MpcC}yc#0wG#(nkm(B54B!%inx**x z=%0p$K4GS*PKZ$`DJtIuBWcsWSbc`5S4Tvmr?^xB{{Rg_I@f5KECm>gl|kqg_(xJb zv~5RmsP&mL>DZ459F&K~r{@AK;)-Rs4AdrcmXeD-kogFDZ(6TnqzgMPS;(kfIGsEj3wXz zh>R@pn)-^HvnfgxM9jy=8;(O%yQswz-Ea>708LO@c9Nd3;gfQWZiPdP{WU16acN0W z3Q+B7LAIhbe}D0+u{=c6rpc93=D}5`n=r)iqkF2!>Mo zK~kQY2r!Kh^wlMYc5_wY0>#2QhG)dRYAy+IeZm7I@qtrjIqOC*9 zPoph*>+*`=CaUa6+7_nm)wBNqf~{e|!iZ^TMr_-zL#swmkr_@0gf$cwB+JT7`of&r zC5j=)qq!gnGJbHw(KS<_+lBI}g;G<#N=(2}>GF*K0GV5d#A>B@{LXCQPrH%z8EO5K zkOpz&eAFJ?jVS!KLRrpIxmc^hTXE`oaah{ZdziTa#V!+v3? z{R>Cn_d%CbJr19(biK-4g^y?4GUZ~F+Lv>;l`W+aAY5ij27g%b#kOVANfU@Z;~o2! zrGVn|THU;V0SEOJ!gsjbY}$EiFr}iVEYi7D;+gl%Q$YRtAPbM`XBH0-XDUmRivk8>a|mm>8ul^gySON_m7efSAx89+gRh9(wmHFMZ4t#s{kdpTQiq! zz?ijiJ#1V)3bTpQ$E<3590{<$LFK+lCtoM^m5tfdxXFr zmW!4@dD!EYZd$t}Fd0LuDjQ|Af_o37e$)N5XWe&&j1y7<@XvkmLM(xOG%74xX{vo> z%_nN5FBtVbv0iPucuv);)<>tj5_$m=jdqG9>EFiNc&W286)`DzPpl|PdMNoL-PKHv zJ@+y0PlqT&9B0e7oMM3=meSovsY)fTjXoBU^oYycY!dYvqbzSOA~b2BN*tf=N$;}v zMD4D#+zrl#iNusTX&9uYbx9&bdWdgupNN5=z*{ixAxpb8cHu&FRsR5!SKl%!`s>y5 z=>6X7b8&9mZZ!_6*PnG}WlLJgB$$(Png-AlEn`Wk>Y2;cq1V)@sLQZ`UW<*wsiVDP zbtJE;-9Mxu%``j&V~%-xoAn`5vZ*n$)zrO-r-YcmQX#ca&zmlEU13j+#NXsa?yq zhRV^l;DS|I-5(A)@g1Yo_D4!DG`p%zU5d>GN|HB8Cu)8&jSGmyy5uUiC4Sh6P+A1ty|rjgG|M!-TUdm2zBTYAE`E^VPf#ySUmeo)$8z_S>^m^)`2s`$ncofb6Y z#KzI_?GD6rNr^5k*!cw`W}WnhlSMc->l=tEl4O(K9b>3aQZ}i^wcDgqB7~(DutzOP)5EY9%PgWx7yVRaoWPcrl`5pR8k-Qa&h-o*Zu`6 zU$Po4kF67oiQv+vpNc7Y#5R=#CukaM(@0=Gib5CzbxAt+!jeHc>o^_qRHGXh@lDmy zu;5sknFdEu2$lLSwX`X|xm$|D=cNAtdcK5)4w4%9Lt%ujI%+3}8^s!>LMj|+Y5^W1LO!Aq^OCmY_eL$$&NSM#l!K>?3oS`Y zf)YxL8BNNj?E0x#JNKCp2-{U|v9(G}Cy!S1iK%@A1Li7MbNEDU)yQC0{B?nOPW^2y zJ)W30*c*WBpq`OZyt*%S7N3a5GA2~@MflzQHk#kp6@;vrr(T-D*sEF!l>^H10Jmr> z&P3%a-c3wxRJAKbEVj8KQ!qXvEX|IiRhDnnM%s*H)md87RHd1f9;O!9fD}phh*}Ob z*#7{_`~a8khZx-wA2m={vBYj9B40=)&8h^*SBb|EjYD8~!xT;#YO+>7(NLd$x_l$F zUpie@w{i3kbQ*MyQE~Ssoe&XVLXObfrSOIu)hV+qRbp&^C{SQ9C6igoe^V{$R=BUMiR+1OAXZ)ze3*GNEyOqH4Cg| z4N+6{Y>f!k1w9kZeyEk&HoV$>B?xi#CC17cq8f@8f&7~MM0^ne5>#6nB%PuIkth^e z?Y*SwE|BFDtaga1qS)NY(_Ye!%F&`&L@(PK>JjsSqZ*aa^F&WoY-kV5taqqI8Hhv` zcGixeF~ZulJMR;!Pt@9()Rg@t0y&gVHhQ8ei)(1;Cq9tV#)(1ZsHIkiA6RLIiqv}P zFd#zl9;Fc@flgi}!`BPL?XBhsNg&QdN-6oiSq-yJx~W`lCP~Or?+)RL3sWq)QBF6e zMq5Oq2jqP6S*jXzT0Sp|v49sRmXg~^K+;gdK_xxoGM6CvYFw^yz}AWdy-L^j2#O@4 zMvEmZtEuS?+yvl}d?s}OnRS6^om*RJYamO@+B%Te)fhKPxPTH?qo_dCnCzybMaK{| zB|8{@6XY35Le)%YC=80KRtjbb?cQ;H;+?j>$h;CL)kWkS_S(s$R;5UKTMr-cb-h)U z>t)Kibb|Fms!ZlSXGry5743zNJ)n_SFFxn_&E@;3%>sp-`R}g5VMR@=6IidW)hSD4 zZ3Jo7FL$}SYc^4|s8D#GGdwvAvG8kCqWQ=>)^sj&XegQXkGiL$$dTfc)tI{uiR=kKgfjbPl`n0bb5aWJ zhBkK;wl(bn%V@2b{z!b&XLiI3YEeQ+KPZZIl@;F~$af7=3ckTmz*NyWPEi#+MO!U$ zt#U=yQaafnq=Gu0(HlxDCA{FI^NcL0%tJ-GDRxRIhEY?Huzzee#-?YRM7^L3LPCWwKI3Rnk8^$^I2g~K2z z$HFlSj$d|e-9nG#ppVqMEdVFS=@J%J#qQ{-oTHJp#y^Quvk~^ENnb?RdW^p6hn>x{ zGnY{p8+aO+apmeI(LR=!A6L(?tR5|DeL3^0B_A= z>8G`|LFte{aa&|b2gWRTd`n}YRZdUKT|T6#n~ZRJU!+QxL=`mmPmgYqr9jp-p~%`n@f^C&<$F7?d@Y8B16{H`h?FZ}3xepS4Uaj?Fz1l<&0-AredOk=F_3TCXl4gXQxP- zw~?h*riJV@AxMi`th=>BN=ZE@Jt0nbst?5~*rjR1)429YAKsX1k@(IFBu_I zpHZaXH89p`qT18a1e5cRmq-L5%+wG;3ZKS0kk-){u~{ih{bQt|Sjr=oD?pc;aJK-K zjF_wQ0#8U4i{lYW3Tm|(5Ur^lpucpXz@k5=tusu{vB2UN0M#n!D~M8j(ucuZqHryh zN;`fr^IR%~lqG8q%ulQo0Hsp}%O`6jc5a3Xhza(#r2=xWq=FC55(;-=@IqR+8G%2a z!WBe0aYryu>)LU~8lof=3QAK}BvAzu zDI|XAe{}|_kkFV6(%`3R*R^ug>p4IZI*8b#4(f}sYt?NnT|10bEuTqgP?7NQhGBET z(5_rsMDRfxD{kT6csYfa6{oP=F0MSwNZN&xPSGiN#DvaqbwYqVa^93Fy5%lYb1Pj_ zj=pOZQ9Y_8NNyhpVo4;e6z)F}3@tlCwm-HdB|Sy9+WK~=q>xg2_3sq!RV5-?H*FZ# z-5nMCs)dJW($dluxNG=EPH!C7)GtQdS+ViZil*Np!C9?sZZMD+BP5AMvk%DJ(gTXf zId^X&nWfAcsPB;B&(k>SR?Mf?U`Wa}Ja%o4Z&l@PN5dtB#;sBFF5{Y8njYOuo#*o= zLUC!b$buG2lEOF|fS|W6`*)+bvre$?cSk5ekl~fqdY4;%oj@e{RVap%Eu&ScT3`UPnbHS{^k^nmd)g^-uji^wM{zNw1f?Fx&M(6dmI7u+i2Xq`afOgtByZf#Z?tTsoicuVtu<9WvmpTPm=2BtXG0P&fhWG z+B&;^d1j?DHCE-U`fiT;bcpNBNO-7IxXk0=cD)s>;9UOzg!07*rd3mR`|NcubCRk8-?{vT|-SmWB9htZ@9&K z4W@p8SXerQnS3Hql-Z((dnn}JvFlmxP6Q2`7NOzL#C(+n6v-2%ZO&_$OJ#0u2-m5R zhKNslg&W?jc)u)L{{TemX|~^x59w=U1lQ053T)L~bX8F2B*d=xh3cZuexX|qHb`O8 zld;s3p-JutjyI|XW{_5kb#>Pssb=AAs9OqYTY}I?DU`fn_De+d0sK-&n7At(=jPR# z;7MZ;<|+hkuA=}mM}61<0ef^y-V6L-EBg@t08@ISEpN*mwcQjj#4~&LF?guP@_Hw% zm-{n?s_Sh)ExOx@I)gsl!5R^$h&IStXjI|0+}+*^%D6X~68K_@rm!VrqROTeCIq!I z(j?x%N17Tdot;$Rd7Bn>V|v?K+A2j#2v|@_Qf4JRX8;32j}r$C6G(Ws@tGN}{StN|!f5D=XI0BhWj{VOb@pt&kd zK6WW9(?uCYbCl!#+jCB#TR5fv0JCrb@luDG@k%pR`z4^|y6%GIJDo?EPy1s!#@#uT zY4BPOV#%`9j}=zjfrKi_a;R%zLq$Zkxk_M}#qQGQ+-lD*?8_XJc(Zjh)%2(fC_7EY2IEVYb!PTLvY=S1L?;)sa!uezKaS2vdzpV9sASpkMOy6U#_iJ~N26XoG=L zVuOWQ%y9SW7(y2M8X?4xCY1px5v)Yp_Z^I%W3*Eb?bDqO;1vw}m*FFda%Fuh)Ssp< z%}u9lgttm#NY?FcTXMkmm6p$J`)3w7Y7-RR2GS1FnfR(&M4MSu3*JIJr@H)PJEE6EOoD4vJgD7)=7>$$$hgopJ)8i-==jQqyB}Is(`Pd?7)g zr8eNVsvMkVllV|RK2Ztw%5YtjA?$LK(*z()XiT7D1yM2G6!vh%lAXJf2@~|z3FtcX z=@lMLZXFR*c#E1y3b3lXLJgq~qps1fjD&diltuZ)dQ*(deX&%WvbpHhwiGsms1Qz? zgDB22vG0tu^jZ~*sURcGSq2M`-!gnrt20$@IZo3XQjO9|K_3&+Jhzd@+F9O{v~^yJ z#$@G^)|FLH?x%{WtoZ|%7nuuHQq{f150CAS&~LZiQYW(Z$Aa;Xdod-KA^vI|v1FrZ zX$e3&=zcNj133X=Ff&ytI9lv+=lx1$dS+jcAI325=3^Of$`z~dY>T0ip-5}R@pqRH z^Q+zvpDg;uFOS6thKoYTWWCo^pcc17b&NS9B$@n-;oPC!0UrcClj$HUq;F{#6ka$Y z-qM~0##>T-InF?*;~$-w=(gmf@YQVczj2Jys8n_L0K?GFAqw)?DJ5gnB15dZN9t$U z##?=dwogUn@3_onq2MS7OG8S`j?w;tKS8afqVZ+STF|9swG3_@2E8ImHwvi7#%i6M zbyUr*5&(Ei^oQz)dlQXSQ%-3VpHLD~Bp`aG!+lAqxzsdPscSN1JRx4HyW?^ilpu9ZVK=Tvj)5bl2#xBg3z?J8G^}g9kBBpMXNQ4QW%@&3}rB>X+ zkLiC*nMnY6L2i8zlt*ZBq@_DjHHNCbBSauLj`~VCz5_*=eWTC~BdMaowK%p)wG%PQ zrF&rmd@_20uSijSQ%-3G09sk|Ct&%*t#wmwZBSi}8I7Rpu7VVwXrkVR+X>MFUeWSs z6g`&Om>^Da(mImn)TKoqrjnG%Ytjz?F1$4S)b3%e*O) z5;a_WL%}FpuWcHWsg{si94S;dXsXuj;)qFC&=u3a?1o5RZbin4E*+pu_7T7nW#kmC zve`mNaY*Y9mW0EfM16FxX4_1p%qBttkkL5{3yO@{ZSl&-oMn|Ltz`Ln^@j8+)oE;p zVAvbC7t>bzCuty)F29TgcQ|iFYR1}_L;qZ!m_2F?^9xlYRRWg_R ziXB!K4gQ`V794b&tgeSaOEv;I>vm5*KBi5Z@r4_kr^=G2A zmq_grYJoVk$x|~v5XjLBssrU8?S9C5tE*P<2$r$)dasrWeG=(2+6i1ioN;}VsXB-e z-51qBONBEtl*|mcur76%Uh(pNE{FsF0F`iu^h8J1NS*Zm}t1pt72UZ`gHd z{({<0vq%TPh`%Ot4|cIzNM5bDMsJoiE1C2 zkTB1RQ_(}u6QnAsVl-0oiuyxt=$>OJQA@6g8U8TAkbs{>4?1Mnp0OTGSZh1gwGPg1ewggc^jhJhy>F0ExPr*WS_-UNHV>Ya|IDD2oJ+W7JSX=!a| zK`BHoLo*C5tBNk(-bcECDKCR?Z~+h0)5s39{{UQLyqmac!*N9u} zjUf#*kW<_N+A(aNc59a!tp?W`$VCTKVNd}I005ELX%*Ep(1;#Hj=l6EIM&e)@k9WH z11^x+WfQx?*p^hW(g%?_8K6c?(-Kl8*Fp1!M^*8!igocWD#&s#O~$E<6pK2%q?r%< zV&%tpHU3H7hS9!ip_=9vwm{VCq?SL#Smq$gZnwp@`|SDV;Q=U@X%ilc;{BVpvH|mI zRwu}NmveS@MW9R%IQz%IxXPNM*=VapZNvp3x<+KBZt46#7}aFIv$BqjprWW-;O_5V zSA(jk?n&qVQ{~F{+ZArxtn~^zSV@tW#!;STdCfr$(P_9&7})8!RX6r7J8{+e^-Y&v zCVr~ssh+*#Davw(0H{D$zZ&Ya%N#RVN(Am(@fs=wA#NlMqG#YlQ0}u2e??`6z&BEW z-fwnmBWm2VwISjm7FhZ29J@WlQ3V_`9#sodj8QBZ%x(rXY!3>ctJNPR$!KWR9q&0M zb;ZT0*OepCOwKgB3`B7a0<2Gve2%J4`c)MRB`GQ=u8}N2Y7Nth>aT!f?XE{|r^lpv zLZN;}oVf@sPAHY8UYy6kLj9yfIr0xwmaW2@)o4LM`t^oQxKp;=nDi+lSBzCNszl}2 z#!xVf)mUxwk-Mr-)Z3nFVdN36hEVgFqLza3uIQ6Bw1do4+~w97aVm1!WwAB;g=M@4?e+>$z^MP;3( zD3-cURQN=BlT~JhDCNs1x3l#WTz7|yHI*Z z)m#A|rgn4oibCD-9}>6?o-Uy+z75j?Uf?Gval0R!A~2$TeP4-fO88 z6H=0Y+9OEoZeG%rJ%mz?63W|srl<%GnQrkTtVs*7;i|4&;oQ^dOZ!Er-&EUFl*Qt? z0HBj7B0F|SGzzVe$(v54I<#C9U2N5=-)27y;U?pvUB38+%v+cyK; zY`1q=tYu4frBmOwep4@m6JufQj*6end2=sl=J_k%;lFRqKS@mE)Ztrz-b!@TM4R3N zXck2KmprK_Tfmm;Izmk#8$lyP%5wNXyU{HsialJohMkN05l=`JHFT7_^@)pF=rvKd zEp&!~OAG^vD8EAAs@NV~Q`cXFNlTRSLr2E<^$5R{I7*VD)l=!wg%X3dLR~~kV=}tx zR4!+Uxq__g zadM4GAfzQql*e)+V%FS|qM~K)SYB$ZPmVppUn6ax)o`b%RJSy>0I%B|4Iy!o5LAP+Ua0Dlg(2XeMv4bR_($8Soi7CH zoJv(SP|SpgmtP$rxdBJQ;Y5?Y*;;wE?ndd(dPiY-1tLpeHNSGkL8EwXSZN|?Bx@;0T*kYicHZhnI)|c!ml`_z zJ!6zN7K;zGWM&MH$_0=)s&*nCG*~xP{?}Mfy1q8(Yju;Bx`&J>$U$_k^oB7G$eHS}C?DaEu@ zZrL*<;|pu5mgdncS*R%q)*LlV7Hgu=mL`2BsLLLUsG{X)I8cxNEP%_@kgR_c;>NA)g&a(Q8VKX+VowR0HsS$sM}RP@R@!P z%#@d3woR#LWI8I7%|y9fT{<`&M4Us1rn`No_u%IgaO~Ryi~kx2m%C*7dEwEi0my zcdnVQkp0n#NGR&BSU2QfO(`;1nd~8eN@|rwH%lcQ#kB_+0QNmbu{V#(J~P!;bNpfp zsK}i^wrHY7pa2vFFxv5r44B zpFI`{l;fL81ZtvpBXGS9>8gSHFqzE$Ux&&pP+W(qMD}lt`b8CA^FOu${{S%MH5AO$ zLV`7%yaY2fxNugCCV9~sII7joxSH*bRJu(f%2G_oNtfXe^4pf0El$Tg$yyULRY0Z| zQsN30jeCO)k_M`CdtMRUs{K(y3l%^=PDkZb&XLwnPN+xO*HFB=;?y@*&BD&0gM z{5AMN1;Gy)b55xzPgQWURi$CYQ>~*p>N**Aj`UBohp7RGav~p9JBzrk*?FWPRJRtE zP-Z%GfbXCW6e!zyT@k2L0*~Ca*5zY3&U!=cIGI~=7U$+nrM1vHeBus#VQ!&K+UKUDb{z-hS}&zb&(z_rtl3M`sxae zZTCj?Y1DP=;skuh)ka)GT2pT23$#@qt)OvCINXRRn33x-2LnXYNIDde73R7U#w;2&QHFkRM1&eNwK! z-v(_C8XzKjqckdStNgRcSo8L4vklcTR)Ua)NXU`YN5|8mDbj8=RTJ)Cfv+4}czM>C zRZcx{QB}IHX)U%0p3-`04BWRwU6sWhMFiMgMT+*#%AA(e!+@xXiHXEH^#Tf%_X|jH zR(p?MDY-j~>FYwm?sim-%Icz!p7E08Z44I#_!X|=@&e}D;uK=faYSUcwYY|-RZqCg z4Q3(h9D$AcrfzIRji^ya^eMMjs-46Xme(nn=s}Bq`m&)`Dp>8MSXT-2y;ZXgRy%Y4 zdsIpOO1h{gsFdQpcY%)N>(yBC+4*eQL8wc7Pu_Kl3cJ=ZcB)mr-BUEF(hkki?gUe_ zowqM|e0@T;VY{BdHprvLxEsxfn|5W`>I0#@-jzTSDn_SR()h)smhlF;XWWY-%ZZDd z;-HJ{yGc^xPa|rZBx~@CSaPjxsau$->8_E$XqbFu9<@-305y~>6yCswwNfXxxfuS-GIO>%!08vZMBw9gXQv{GA za?4c*dT3DIWl;Sp6r*fmXPC6n+j6h z87rnM1I$LFtO`LcDfGZ(>In6BAIeBG@r4iqUcyreepn6%0SX|?@re1sB}!WjD2r%c zAzfqO`XifaeA14Y@z#5fj0Wi_x*|ucJY=oO>p4QVl2=_A3u2U32m?XXP7=Og@=h-s z-AxtE2~m(nka>N%_loqV7P|bQRF6eT z{aI28{L&-gIL+^%+|_xsyCKz6YlHKOdp$ClsCWf!l4g5&N0<5cAC%lxp>BLvHqT~` zDUkPBaG=Skx6+X5YHB7^k4$~J8EoB0c&ux@W;>Quy+W(QQl_d(7Ii`L{xR-H9n1xJ zGNfpotQzE7e`q8rNAT!lIObN>V!v@|%~Xe@xCauBy)_Ys+{Z&jrbhaTrCqu3ry40& zmSSQdZsYV6d=k;sR~zjUl-#!BXt1}rGSMoYllsSo`;US!Uzt1!TlX8403AT$zn3=P zc!eE3@?*6Z)Vp>=I;LtGPGh%^SoO!+cO@3r0y1l!thAILj3W)uq=58s6uFK790~&pKCP9*0+B7bBw=`=_T6-avPisq@Al5`V>-!_QmK!|E9$=$EWLq6x4A}D~>5IYijcRA5|{jG zVXvyKYPAfhX;~j4CqD6t_X{dLlE7BE!Es024x+ej63=OITv2S*BsQ;1%pUR*(sB8j z+&(!h`7P}g6<*)scZ`nLX+8=qa+X|+=1W@eRFgj>x)6R*t#JHlf&dRym2%6GEc$pR zMxpOI&t-RwKGA0vV^m(N>9nE6mF)x(ImVU6aL=AV0?zYyL4cIQ-|vrIE>F}b%uYy8 zgnFw5W9&j2v8>FS-&6*kr9nEA@QKA;+oCvPtwADvAw}sP*rGK>X+7cc?@Er6GKV!- zcA1!0xa5%9uaQ}&=F`RzxjGoV{bBzA7wr(@X$M_NP?qf>4kC#pRccd*aMYc@+Z}7_ zxaUz|m5@Ztg!K@;#W(GgDKE@`OoTgMQsdjA8pFB`Vbp*qkLl`&kes?sJECRHHAmFd zhGJ((bKFf9r2`it+_lrjQNr+^bag`&dFp*lu)lROVbvD9co)pwcFRc<;T>^Zft!;VnBSd!PPEeZDt=HrVm08Br|m7g z(Iy>+)?^jvdB&8qPIEIbiRTRp-D6~5pi+{a*F%k3rL;C%PP6Jr5m&ytsaNbyCW56g zxmEdh7o0<6dPzM-lY#AQce`>cBbaOq+M9QRiB{pHSs{mN%&#&pmDMP@S)EiqP;P3rJUXlY07PjeQ}U197E5uWq0Zw}S$A{Rc}@YMzbN@buNom6 zR7%j4resHGW~De0`(N;t@r5zeUtB2f%F}HwFLvqA%icbHLT)u05Tm(5dLF-w4%bv8 zeZLZhp<8F9=O0G6;Jz*@87rGY3RN=-*yQMxAPegWDezHaiW4*I;{uMTwzL#eQ_yLz zj4Nn87flf>sOvHQ^l{Ci;6*QY`9o54L?WjanX6y)lrXx9o$K(9ByT7K!}EWqX!)Xv z%A%(bN!01uI{g=0ss!_Aw{N63xUZJFOmgFTf77RAgv@SKk4TNa$TBwdML`iJiTlBL zMcS3NMUo81+en^-7#tnuezHO4UN<%1qq( z{LEW8o-AD6G#-Ge{{SVvbT;I-s+3u0xL!NrEL(MDTPCqBkku+>xkPl3Oykg9NU-)SBC)&Q?}Fsk-Z>O4ffE%JUq#&Z#A7*zP{j(L6T19*%*uxcfi>DQPaz zpWbGi{)JlMv1nWFmpd(qlQ%*GeJ9=kx3!+-+6t$nyN(g~DE|PN^9yJ%Q_!qTX}WrT z$kMP}44q!8c}`N^jZuRj%WhX^R;M3PNhe9^8lE==6d(WoZS61N&1;s4+ zhOud6Km`!_fa;a@Ob+96TVv5#3YTJZfvzr@5UDGyYuE8)cYD$N~Q6|wv z1uNd(tL56-cS4ab2w<=k^KYEgOIu-Sdx2~b+em1}6rHwJ!^I>nIIAwDTUrzEK9J#H zRvSF?ntG<*@rErs*3u={arU>Dnr{a8X^X z$SYkfa!%dE8ANH`5t2ZU+FLFravHL@DoTLc^@wYesOIYof1Nhy^3#|566 zA^!laQ5M!%DiV@PNggI6=e44kvdlHdszlta0W(XDf$@}}StAPDWsxVLKrEQ+_gmc+ zvXMKK`-n*r>E?vCEohgR%X=2Z$H`p}0efs}uGei^Xosj?Y$3hOnv^-h){#*`S%_Lb*p5)u}foxdGn*)2$F z0xYKEwyMrJ_3M2l0}E`YX3-?+r{NG64%%p@tw1eHJc}o{H%9VQ;z0@T)<8h5h1Jm_ zX;=oOLEM9uEcVQ+(p5qM-5?1P8pVy4zJ$yd+g()tVcgkeG;O$Rk-JIEjX!J#Z5(t~ z7G>A~DOHm5J%X;ChUwS(Fsrl zC<7utbVeo$RH-C_55iDQ#2|{LLo?geDsdli8RivO%P+ca zRB1Y-pEQSm2!)m+(yTB$cvc6+p6f9@nH2Q*3Xkcv%ShbQhiKj5tZ)*E4FKq+bq+~r zzETHiD_eVbnA)IcIBZGgo{iNBJ9(yMY1}0vJ<5k(Xf#S=K}?oYXlkJkk4!8m8Fz*5 zC~Js;3bsAK@zNC4P12T}A=+|PE2`BoZY#$_jKO_$apB zusVox9NQwT2!_a^MKpqx7W$O#PQO@6OW2&7MM3^q9U#WKiJ)ketyB^L15w@@-%`G7 zYPCI6qNJ}hxLItWX+Y2OBpYcH>zcM>`od|-szn2#H-1t3meSWKN0aj;csa^OmY%;aDvJxazXo^CLCKd4Dex{3AB z!BG^%%&Ah%r-JRJBuJ2T9{~=Im0vZYsx4;KTP|w;6ziwPAm%kHX3>-gJ|R?9Qi-ZV zNeCtadKLB4Kq^|}Zi7gJIUg7|qKzoprTL_j)8`G8{iRKOmA8GfuT>Nus9F|xH2K<) zdU(UP)=ie!q3}5a219UN5#qenk2Jb#MgFlA6$=SV3R;3vv!ImEc*pX5WV(MY?~-Aj41Pv z;!iN8wCfT&^&c41vALGGR2AkavHG*|0M*!I?`Jxt|gxy{Xzl%ux2AeP8NA=a&U zN(a+yzf#Qk89{l!PgXk$(FZL+@AQRD|`l$IwU@sFO>*d?l2<2lt`xQa^j z3aTYZF1E5skkAl&K<^X@PiS$Xupeq(<+BfO`B2LE@#gRN<;)?xzY2XsEye+!<`jA@ zJ-lKj`yR`P>CmaXPF8Cd?&{TjmP@w|d(Oj^km5CMvwvx!{YKcmsH~-Xx*b8(Na;AI z`%~Gzj@7G7it5)T9_Hxf0Qqm*&G!theYfdJW@poQB*{R0qFM{4N;Fl=tb5`rDO{|# z8#9YL2|zlgU+RaQ7$pV@Cw}1Lh}?x1o=;KJSZKs6M40Us^Gb(X*#%jq* z(7jz?t3amDZA>=iZ3yUP57EdUCUCXe5GlS|rFg_^hWZPN4&s2X;BXFvLL#^hG)TV3 z`(CE%6mz)S5E2&3b~v3(jEv_6)ke~&22+jU+|^-g1myNbEyA547;hjpk^0+HheU-d ztBjxGNro>h+aUOZhBHVqD3G)ngee4b@ZE zv$3a)w>GoUK>Q)kYN37Z(bZIe?n8m^yg8Iq+U^Y+i%nT8vR%>-q~l$IAaxN7c@MT! z^_{?QMZVj45!`ns7`0=vq`G+&7If>U?}~pQvJ$eR!CbiI{{Xc8r07(V$Smoxia-Mx8vpCM>Vf`U!wyyP4z^KE_SRd%BKdsO;n zu*0uQpsYhKxgZRPJ`p_cOR5wh8=FO=lJWHI745a^($q~#OsD4Y5kHT)-ZfBnwjsKN ztKplqHC4W+9v~#OKTQWo33QY@BQ}*D9#TV;@n6u?uC1oz2ZusB_J>+P3E16In8t}D z{6BmTGpdYhQOjx~MEzmdN2Mk0M(7%+;|Z!*Y7~V?fJ}fhkCzI?O5HDnbr0>8zKBk- zj|uA>O*yqv=ZhwUB1w^viOVGmc)__0<^`2gBu`SAsCJHDjT4p*6;WBz6p$w^A{x3W zJ({BWgn*C}r}oD>CtB1=RiHD|U+sh*sm8R`XKMcd-JLtjSXfGP+B_40^9o%bN$CZG zfuSWbc?BRvQpVpgKS+<9dX$~8qf#=Zf(-h6U~;CQ*QxXh@_el=hyn>?~B_)}WrN&bQHQxFINUM~yW=Ug;Al7^!kt zL*gob4KZWFkk*=w$ZuUp)-ardHCnx-l?i=pGA->?)JTfm*gHy+gH>ie&Yn%m{N1hL z{E|>s2I)c)A&Y$(x?;F zD`;y1TL2jtDX&570UR#3`6?5ZnrjU`#HJ#QRVL~yOx5O*5E=QA@y3YXsw7AqPiSzw zB3lcQEA<)~Yd943M@G~|RPj{jr-XdR1gC>DRTV6<2bF4( zKUlBiE=+7CPUG!;$AG8L3*)q6hLyJ)Nm_u~nM!2*qsjfo&iie1iO?@l@O)q)qKZc5 zI5qOgVP@!t*4+!@a!*Z0nuzjsk1e*ox+BCDyWZm@wu4TEM0>*bRjkfRB<~vOtZVqj ziUpEo8h0XMu;~d`ly^Q6Vz+9mT#XP+pB_bBrN^06+)8G3>)th<1(y-d8dhP-EK8y# zM+&CZPWz22K%kI2i1kgQ>dLG><}x2elWr_dUAnK|!BTc{v_PyLbKaWdG@)~cn@ z&&C`DeCYH;5mI}T@Q#-ugc>c0svziR;~hzJXttMU+CHrl5(V$8=AUmK@JJw}1{Je< zykK?3l2s|`Il&1lat!tGhZ@?ikha_@GXv)wX)Ea?Mr}Vn@JbmQF;o+wKUhl`}nA`*&gsImQ0{{W2qjCCb?Tso24ymF|8 z;+!N*8GIv%sstTGZSVcCJrJeA0CsZItaUL|RPt5fGujgFiQV*aCaG;oBulSYOFAcu z(q^iVl6<=BC|hWn1fZ0*zfr(;4zcodxTS~b8>Od8v;+>FA+EZirHqgYZL%Al;ObIL zlMTq~sY@6&NJ~yLQ;=McfYO$QC``(IVV6H>R_iNW?$Jv;a}cA(ZE1072~$a#G95^Y z?bP*JP8%8;DpN0>WG>fLxlKx`vIz!9O*%jjSh`14qp$MKfrJjJpr~bPB+7n2II_oV z8Yr_n%#);XsxDnJwVGy6LkcnaRDZcrfi9SnsMPlV0JawwRKgCbW7tf>7iv%^DON!s zeB6N6=f79h3QGB#2 znZwZrUBq#Gv?`5Y^dx_5A^j5GR?|kJI!nARi;e1?ZYOA_?%u&KFrUB!9klt7Srb;P zI>enRwKX*!gZD+2G3iu`v8vrzvRAMAcaOJGN1Y1A2_;D~n1#K-rGSoUlC6r;K8+Cm ziaheWB|+K#Q8;zNs5q2h^wbq2&=aVJK>m8wA zAg;7jBzt#-2@3fYIN6y!$7mhRsxhsqE2s*RhTS}4=C+80RPqe<5X8w$bsUOcXShr; z7%AtS5i8A1ne~V0nhr(oGc)_3GW}?sX{rL{Inzn}1aZy*EFkEjb>-|sWlC4&16XQF zn$c9_DoSv%o;11M975_T52m6diE2dRpOxe>!VO9~%NZo6K&9>t!%b5)PUTG^TS(NX zje5tQ`A2a#64n)~@%|&URq6Ssw9r}_4N@|cr65gG6VfsJ?4;AWu38nfqatk@imnx8 z?i+ORf#PK~Pe|${JyN$7PF`Yr!N3|O0#81lhOm&p7sVAgiO5U-{Cv0;0+Zzp7J!Xl zs`*H1jjB?#?jemy(KtXIQ60x2rKC#6>5x8j>l+lGZ!ax|Mln7f^#^_ExuvguOoV%4cDQj`)2@G)k)&B*VW)lpXXwnjZ6 zD(u{N)i?`%0SOYA+b)q*y~`8grr@_qTIE_SRLd$;X;2fMlbDFOV7R(>q!JX-xQZs~ zQxlnTixlZw6bjz4F+QR+OfPSxkb?1|rewKW8=xvMWSMR1^6R1JRp~48q)+=JUBmGx zt=a$zn|;V#Av*O`0~$TZ7O-R z5|AXIBq#NZKRwPaE?q_wMSjNd_@Wgd4Y*`h>pM!=QjngSfu~5V!C^hqwW(85UBeQP zLfF|82VRnwNd04Js{95wr3hx;<4OxH+H9U%JNGp?=hr4&7>h6k9g^-+O*QTJdPxT@`X4L1csNlswS28T zl;Td)slt?QNSFY2iuBgR6}8}b16~4;c@?yjS>X_#j+R#ngqQ?va>=#4r^@#V%;B`~>Ab^o?&?0Im>U&2UI-6vLC~;T6rq!p_2V#%r9im?wbk@IB z1)jGu$D)z8E30my9)5ya#p2#(yew-eV&;_RT9(U_AdeYDoMEIYP5WFEgKnlnh?2G4 z(8pNCSh%<#qe_cNT2hrKa%B%b*but~`Dsaut|9t{R8pTQ`ovw$uvaW*bP82@!?=Tt z@e%$lo@HDmsq|>Y6t`` z80509G7zVwzYU{JH?8Rd@sQReVqj1cU6jCbwIwx;4pkt87WD*)booT2Hf8M9M2xX) ziIK*A&Cyv_^{%?Cp=_BUM2QeI>lKWaByXzKvA1$M5J0LZ&Hd4`DPKc%(+dY`!l&E! zK^C?x8mLQ$&m-un{{WLYy6In#Jy}Usugx^L478t!hbC!gSBxGBu8Y;x8RI_UZ`AZ# zO~FAzU&MP4gm%<7cBl-d238ZX8(#g--deY;D!E=^DQ?uCgcB)^fennpTuAB^wBQeM zHLqj{sII9hZPz48>R6Pq>G-zy{jJ9&r^j9Z|dxWy8(85((KOmII?-j#{JJgr^ zfuWQoByz4(p~*{>^wkM*OEQrlnRL@wJ+QhZS(^2?4$&lS95!=GkJPT8dL1B;MCUzp zfiET86xJrzg$~Yru+gH<=`?+r>L6T~%u2ymIqiqNxg!03`{OIg8~?f2GR!>^tMsYMBb_bzQcv zZ9_zCI-cEP(GBEp)D*V^ww@{P!S5`#mbWNKbP;8-kPx%Hy`!pBl(Xb4C6I+LX-JxW z5)m%PHB%*p!$O5u-gCo`G-hd>Q8GyICI-5+JwlPk zUkMEgmmEFr?vs%!(`LBSE|%)vj;3B!c9DO581yE06LzsO-X(nKGhnnqIOgbf(^rcxdI#Ep>N`YP^b zK^rPl?Q5}>wl<=30(x|mT;jB=b?UP@^Chm)7hi3Er(~3;YSY{eW%Y<0g`UGzWx-^9 z-3m+Fs&=>#rAt|U*ouZ#b98zqX3tUr8a-j^5`%kamW{lS*)xuVM9WcY&&yu@p(Li- z#7gx*3Q;ScgnR-dwP1>|;~im1D|0nseR_6>81fLMsP3Mx{iq?Lg(ishmbieCI8$Fl zB#hM`wOC3_)hTNY^+*jhUYecfcSMe`$!e2}s(d)L@%pCZd+K~lb`{y^N|^NS5pN3X zP_%leQT`H{%tkkv;@1|A)Ko7`@h;<7HP9BQ`Qxka1)8jt{N#uM|8>V#CN9>medW}m{Y!x>$XDP}w%uao; z8kOe0Tie+nby_q`z4sStp)R5Jfije(KCy4M$qFn}FM@Z7QG1biVutS3Nlh3t9(^Y> z7C3I2;1w=wX(4q&tFCPJ(x%N*^z@QIDkUL4A4pZjxOYWq!ty7_K;>Kcml@aDA9cS- zgiOj}eIgQQOG?vmnMO)0;+X4&z3NvIqymMub?eq}#JU|7fYHHkrMV)Y3R{Q@NHPu{ zy6FUaf|{u3TJ1UoqQ1qnDx#EcVCyld@$C;0qGYs%<)Y(qv%bw`wxx9MvO!4-GMGKb zcoosrC{93A``h2RMAGCIs)|g7d}GOU>q>2zO*z{^agqN3gIM3?JC2&bvfC0pLZL`o z+!j&MniY6lZ|+|l<(Db=G;HfAy^JIr*2_%E318mF?y$u~Cf*YVnwnwxb}WLa9b zBoUzN0XEbVRUrGfIBoDr+U_p8I5xZh_AN)zWkNw@W)KALu3o|2W)?GYc zDec3@#ZFjX292kpggNIO=bqSdTWPse{{Wac`u_aF*1COAa zb<1cj(>*UC1nbwR@P}rFuWEW3+{b9Bzmz!BBjD~>X3Kr0QxDP9B~&%Z1UCCcenp{O z2qG$X_WJ<^Tx~8I=QNJ0QSy!bP2za&Mpe1$Ldd9a9?JlAF+rZhRMBXW9;c|>)&{aN=>wXBL@ZKzltR5(UbNg@Wo_+3i7n~l913ZM zs32u}$sagUM5uBSMzibM6q^WIcPscv@QyCkXR&?}^;gwK6e*omI%m+%e^~n2Gbqxk zTXAxHt)8T&C#-^myd$c|mRefcNg&J)+V_rSIwq`sWf|S2SSuQt{2~oaqSq;j087dv ztEj2^M>-Hp`$reZO`?rSs`Cl-m%PMxBa)G&gSk_UhF zM@vN$B5IVYq*H)|pD)H3b7+?5RV4EZX{ljlNtx~OhQek;qGPM^( zE3rXJD;yIFs}uI&l3MY4h169%Db$ptrP+ySCoN;oeaPcdUg*J9rg6-?a5vOZzdG~F zIaSG<1+J{tPyRcg^a(vA3F#M(%6577C+Z~%tIzv@$p^I&RoTr9Dyd0&Q&xnl_`EyE z=vQx}EoD3xk$J5C;jfC0?NT_2f_n53u-k4GS+ki`YfRFB0C|b)1hc9FNOht$c&pM> zl_?I8f}djoOS`#hI?G)Q0_M7^MX`>kwbq_dJ9LzFEv>33s6yw~anz8D>X4uTlrt~N zoPSiCsSDhx2?NwXuL1U;pKsT}Rt4h4OdhCGUC^lQNF!O9$F#9m4%ik4lZ0+5v%4WQMa89m zl4f)8i|!sHAqtz4Z~&z0b7oB|)Q+D~QYjriQK(?}*<_*NXBk`I<`$ICUJ4wodDH>5 z!@6HH9*)9PKml0Y`#&(yB8`&#rd~bbVyZRJ(Qa?NO_BV{ec^JxA?@Zqke9#6dy>s&i6gJSPbyBVnq_ zbL}`vBimjS@A8c&-acOl%GiVvJ!XR`2>E>W(QnvyN?v0hQ6UQy#L9CM`o~vRf1@3e zL?To*5Mx4R*m%MRRr6a!4ayL7?qR|zS4&C&?X+tl^@jQt@I;k#C?A-1(8nCmh-+$t zFw;#5%TWocajdE#zT8rV)R3@GL-LP_piaYXYLIkxx~zt@Q?P;U(uuvwdt+17mF+Z1hgulD4iuZ)a?bM`)A>|2ceGMwWNzqd1B0dXu$mMCf$THeB z9^POrN>a?0vpM|Y#|^VUS%vOQ%JNZq=w_Mt&L4AVfT}KfiLoOw4VLPCzYh z0!al2{Te$T*$wYLN%pjrrOG62I!bUvOXo0vfPRq^SHt5SlcMBh$Rg>RoVLz=OmU!5 zIyWsUqie@+!Vuv^rqu`iD@RF@e+3z`ag@U+3^U-g|_6S3pRo$g&QW_nl^D)N_S2mO#z|qoLLrdpIo z_`vkl7FdebfMe${bxB8-OhV^>b+l5jqJb%@QX0f2M=KO8)QY3+Y+oUu3Q^!Z2&cw5R>5(_LzXvQljR*uS9~V{e61K+MpMX&q7GX|0y_cK`yDEy|djF54G#bZF3>1#(tjVM zXuNB+5=wVSv+VajYbaxT#Lt+)F8-PW~ytimF>q z*0t$0tw*H>;J z9Tux;$KZjYs4ed(wAtHmAxLcr8YB_n;35*=2X}Vrnxf~+LjY0FFy-VW4z)<(QB3tG zPuUtK3%6CFa;#)|SvoaFjF*t>D^}qBNl{svwfM%li{enk-in)XkeTMSR~1EA!=My6 z4El{D)7CtAmravg5Wl40Y0EJF*A0-IYYB(#W0u)xx zZ74pf(un{xioQxA)o(moAan{e)izOB@@3rV^@^YIEwTsIfTNmLx@kQiun-p#A>jmc z)9DM^P-Z}}cr5`&aEHZI{8DO>7fOdix@bE^dhwdkTI?D&piR_*brBxuN-@kv^rb3W ze@Vjyjzdnw<5?YXqLntjokZsr8w{auRZYudclalI#d%T!1BxJntXwRzY;akgUkpJ{ zZasBj;E)KPNtm!(OxG;aCy#SVaw$trfvo&Y5JIa;_ch7n;{G+b=XZwKYLQM+B#%+R znSxG$YXe*j6m{NoLOv?OEFR1pZHjBPEwl=Tq@<_`NR44uHetnO*`9kbX$MfIUlBiW z!Chw6c5Jeie6v*g^y?ZOo;PzUu)E$#wo_83%X$3*EiJ9Ll4Okp<@Aq+EbjOw8jbp(xP6>N{F z)p~P=!$`wiI;KjOk+KT?)U|c1S^=%;wh(a9LERQ7Tg^j9r)Nl--gl4U6kARKHSgnVeh1O;+frjdc&;^m3N>KCpo zbN2Iq0SSW2`1+oq=ThY;GoI<_L=9=1k_UBG7h~HeM9x%V6_%%zss(KcGfQOu0DNdz zu4mv?Zz0DE`Yh9tT_`q((5*Bone7+rK0zFVs-@m>-KV1X=C3VfD_K;>N&P$`L|)Yj zF`Il7rH5B4xyF=4=0u0m4uU}KR3=7@6jElCsc=cIPCHV9O?% z;|@5ns2ecG~1rDW<|^uobWSQJzlv%C#E^$&Fv$2ah{dX{OlO zR^%slO-!O<89^U{MAkk@q36tDRMv{smVG&)Mt(YYK{r>0u_)_2HtDFj;^eXE=9hp> zkE&%5)|WfVxZK!m0DKZH4)pNwgTYXeTi2ozm$u0Cj+dG*j3F=ZDza{2E`Cx*=$XLW zQc5aUFSgqjie*__X>_PpLoGD;Mz0%2(7f+?UF9Zo29SzZBm{ue_lP{x0F0Gx;*&VY z0%7T22BgYz-68ZboiBJ?BuSj% zK&94+IZ;JhliZqiVzb5grDs$paA z%89+Ea{^xvgh`6gMC}zqYh6)6Nisd)X*wbx8mnjfYACHW2~M(nl45cb{+z0uKmE;3 zpLrFjr^0T9CQ2+hGxA4BMT>^Qh~ygFH}O;_O?I24EnBrAUmaxwwo=#VrzeCKQ23{> zi@A$8;(P5i`v%A!v{f>or&&VP={i5cq|Q)$mo1sQWEBdu>?5O-d3e)&!c;*O=`U9$T*aikgq5YJxpw!7DHCbr$b-}Hlob)G39=TcKg8bRE*QNP zJ|D=|^OoWJ$LU>K%5E0b4Fj~#eqdA9Q-LSO04foYWXLpE1HwM)9<$h}+P+-b(y{T;JrBYm;+(M2%65BL?pe8t0A^SN^p3or(-z-snIM7)ApD@%M(&Cha$__h zG=Zv$2Mcc_({TjI5Osq9O>U;DN^quL-Lj>0^=S)BlNy;3+wg{*+9#}LAkd@UWa5iX zV?zD@z|dms(l@CHmer6ZMxHSr?iQ6da{zNya$M*t>FR1}YH8HPT_Tj1TO&|lnBTZn zKy55EN<4QYwmoIJ@#iAhQI&DtK#+@_mEkooNzwXs^=;I31Va}vZxvHESEZUBiX!so z3}n9DZgiPuCtU7V!&lcQVaU)5oTXZ|^eX~CBGb}mLja*bUcV5+tu!iLds>adqIme1 zn3R7?>MMVoHU;aa)5avD^FRY#3Yi(U3x)fj!3Co6v7DCJki2g4CF?NyQ!tWyq@JL{ zZmmSDqFuD+H-f8go@_Vl9|^hP`4bCf>1j)7QBp__1S@D-XVej_<3GQ~drl>2miZrc zFrwcX@)c#CwT3De{Gep8PLWl=vJVyUR!m+%uc3Lf)FB7aZTJEpNS#T+^{CQ3(o|)^hI?(h7xLf29~__>yW{H7vEc zV^olSGl9rQ!#_Zo5lGWhOTJZ3MJGcs2`!>E?ImK% zX$oI$S*!HIYAOO<1jKy0*vVSyUSrp7K|l~kmU`-8BSMt;>J+%*y@eZ7aGRAbA!M0e66Dy67Va_$s!S4r#P93^*Q zMekQplA|Hz>(UYKE^RBzb#*VV-L{9-@4`br3(e62vbkO&(Eg52Wr1nevS%4ap#)R95aMqH}37Hk_wQ zsoe8OOHng8&AD6HdaI5O-bR|Cwp_vbTHqg^$?BQTGHd*MB?W0X+xNyrRFblC6&*bq zTn_nw68Xw0P2%P9{1t*+_+t|>oaI%S1xlKyD%hhUCPzaWrXmY#DD^t3HbBpI{{Vty zt`@3>A1Ku<`mzmEHgTxQ!4Pc@$>%Q6;?s}!Sg*B+27LRBez;47?t zD#O=1g+c}SYwN#6K>jJ*!!QfAm20`=q_bJIOIcAf^L*l47s+k!92*BfqVDkXz}?by zDm8OXW-~X-mQxdx%oZ zwIm8HKiAp+07-v*1Z6~vCm=3fm!;Upc3zkhz z+Eh~DC$eBkXP{CL;#^7tWzCvm;@0(n)A0Gl7nI0fZa`PAEns4l5~aQ=extq60F?!* zT5CCJ9x~+6J}JpQE77>!rV&JMUC8%)6Mxg)u5nZi=o%!Hfzb7cjDHt`XYUOgmu|BX zK;5L&A_|UArKwDsSzP)>!VWxI2a>K_WJ9_nMe_Y$cte7nvJiX9T{=at9LS)hRc`b? zPJuTyl`mQweaev~36nqd#;3aztr=wK@^BQ+8 zP3CMU$e*nL0M!!0@kX3WkfE5ee);!*G z4*Td|=|u@^DPZa9d5_8o7XdQtGBIl?U=WetyyNE1lKe>s)vSS~0(WWKSap|LbV552 zT55*1eraHh!%vJ9#|j~&$CAAMyq^!-3f!9*4 z*pxl2skHzm2i7~eFZz3tiZVord8P533%PgtbC$z@Z9vOe^qGQpB#d){2mN(F55^n! z*MFxu0(&7yNd;r}K<<`>FZy{&s_d1qri7>Xr~T0JxCJgeSxFk4o@PN_I!+odMKd0W zslQv1R*G75Db30n%g0W!%6X??gjR}U@qZP!Youznv9IPIF_`bxBW~_!tvI_PO;El@ z$|O<)az_kr!KC@gE@AbDelp6mH?Dg<<-zh?f$6fa4lVRhzCSp{aMv{wfR8% zlADjB)T@iil!=aRPB$L16_o~4NtcW`?ujn80eby=%_QqDgeP(pw!)~2ruMX+^Y0D1 zYA1#`IHHGd&UNn${Z6RJ@ofPXy^*SFC#Ps@-vxAHig`GO=DMqcWG`P1x=%=x#!FW! z#(K|S(OG^fNugj9KQH!0nk7V-YLDS6J>yh9od;+H$blSPN7VlS@En3l;*H$E_oq(s z(g@lWAp%V8sQW0l6%dhf^L3mCX9|D%dJ=AAk*qczku8%#+xG@en=;sMW1x^%8`Vk@TzTcI#{1OEk?Dmkfk~>U>~ly^YkahA9ii{3CV0 zyu-CL${?y?J4qkg6fQ-};iq%i((=-wgH3DD0F`Jv|ZeO{^s_ffX;S_2`+?_L5ggp>U zIohN(YryW3di-KSF`^mnsYyz*r2;%4x@eLCDKS{i;;D|J9F4jr-4#kAEP1{i;UcL; zn@}2u8mCH%hyZsaL+xYTr4w&>0XNr-jUg*-l}z?O>k&C)it%y+d)+im%}?v@D@c`nW9ef({#LJT;n~ebc8wnD>}}10kMJ_yrjnJE+H;ex=K5vq;EJb2W@Hw znygnX%jTH%N-BIZjijYsm{}r2Po!MMgmLs#=`ZAas!BrN@c^|fwA8@pnLxL`+eJ=_ zO43)mKs8Bwe-~9;DiXCb>9S9lk>eScBj*;@V1Q~?gM;H27bFrW6S-&O>n(w_yz?ki zsPidGl)r3w+m-hk;^^&VXtrKG!(q2>=XF4GjONj1Xr(FvNCZd_dw6LS3tT0|%a@%{ak^*rr&+6}zl>=df487exGFA3leyn1QFkTe8miT}3#DpalLz+3 zyJ5!Sg}Mb*lIBTHr8D;%rEOx&+$l`Yj88>k)^rI$v_z(f#}_pMXWZ)rh?0ZouF1M8 zmVC^vM0gn5+SU0w`<6SZFs8ZOX-Anm)7+ndhg(pCn#LES+H92vM|_94j{Ax#Y$@5*!OQ4djy(7EoxF9V>&XlikBz)lvajG&fLQmGwAxR*o zQ_#U1fKv9?Boy7=s1DWboX0{YAXm{`ahH1PnQV!g_QIsSz;qEAb-8I+c_Jz`=CxHR z4+l_E1pLuoym@~Dj=hpVfLZyjO0;9Iki(8$jg0=Xz068=ix#1a(pb z>V&cCws$0uW9_;wI)x~$_gC2=E7W+yUe}V3nhnXuwB(GELeH2=Q`7fCqLxNVs_cANpyC0-Gsw)UT1R9eDbS4KY2xTf1fY_}=KLWtO>R6sM+Q>;zEHwu;J4CTkDg>P0|a>T^~ z8T1fF*(XV91sMx93pvO&jwMHKWW?>%^siS_ww4yiB@Q6Slh?FcusIr<9*ZaBII!Sc z-3qSEhnwoLm+CFGHIwwo37WJr(t4P@n}~K*M&fwI%k>o?sGrE(=(_&^S+CU1wHXb1 zPvsT+>(-8iZ5S>T?KDuoGRqsesbnvCB+SpQu~(LQt>XuZF*P9Qo=RTzx`JkYaK)e@ z)l*kYt7x2Rd&NKL!b;?zIz-baX;jIv&o8P&~?iocW+toWRKjXe_q zkluX-IH}871x_h<_W`ABSbGBIgf|q;n{7gGY$-Yde|#?&mKCB8J;xm5(K+sBja5^I z)aa7Bs(%QVlJvb+?Qq63svCn|owMXrmnL43ZAm+(b=Tnuvzu$Go8>rxb8K7iSI6z^ z-Gb~^Dh8gc{#$8G=B~hL)WxZ=MRq6-tIKy#USi}luGKCd_c+dKDed;>{Yr|Fw5N8n zREJ~niL8GbZMfc5S2NCKxp%ZQMo9M2anIQoI{I}uU-Oknp1SK5{KjFAabmb}d^FJj z;Z(wMm$ZkhR8PK&DWNG=;j|y5qLp`xhH$mKXD7L|cqHhl@0)PpiCn5ene@<-K}@AD z7{qy21i5G-X00dG0YLbTez7-i zeIzGBqUEq`gHVbd!dP_fY;9LiI%^k9S0a5ta;;=>4QQxNP7RFKQ&hJk7ZW+8N;Qln z$dR=RDbLxENm7p8Zm4FJEA&pu34%2dY=x?YHs-lEIjR(fTvtIOqz!&3(xq`@)B;a? zb8~+uH@i?24CUaik(k~e&MRrhxAKDM`-zl=(Obb(_aQSM4FnYdB{*Xhs!DsDx6>mP zx<1$eXko&X$Z4eE%#-PEQ`n}r>O$p9k!sZLXM9;CtJhd6@zx^o752eWdB+cNBT(@Z znzqg?AXQfyg{G%TE9nXkDKBDF;+3U~q7}bYOUn`%XZ~nao>^*L2k27n!F(U3Z_OY; zkeSjoi(85_Rp+bRY1~nMVR+v};!wXdkL-w??BABGyi&_-iH)GDQLEJGPKWCm>t+P# zwYJ7u6TxYHUYZF#B1SL>QI{<>O9WJZEP1;1f?9@abw!mmKb!VIq>86^A0(cA^VWYT z=uSrx2l|D0nEPo?`vnLUX_$vS)PoW=rmRk$ZD6)PL@|vjXsYHDGdO>Kh*z;G7^?Wf zQ)^?DWg3-E2P`S*x~I>Gs}ki?bLvlDtSSI^CEmNzr%gM-HiZ%onH_#cCnOYVgO69J zAutN9wiRH;hcf{{T*@(SP?+rWd_x#kYiB1o~6E z@gI}I7_lCbjL+$C)z57e+?s-!$bx(PU?SsHJu&nIySH47fC`lR!_75A2c=sTC~zio zC;U^92gDeD8>a)JV(S+St)rCj_S5gLn7zAH(a|?^cP+z$)9cx4ol?}>VE+K+Eku#0 zXd5Gmw7z8=DCHH2%jGb$ZiU6s`7VsTw)^7Y?{Jh9%v&#Lw&evW{a!y(q_+ALMzY*| z-$==GoHlz>OLc1Wrw#7aom`iT}ww4x^cLWg>w+b2_t6FT0dulZZBg~~l zjbwVq0!)n#6e%lr$7t?tEemXKk&=k-^og8yY4N#KbDTO0rVvJ@N6e=MtfNzkZ3S%V zKm(?;jW-X-nI$T`!_510CAlS{p)ps?Ws0Oa)69hh%qS70{yIhS3&2K;4!^^=S4t?G z$==`XclvjnMH)lu3L-*sn1j9G@S>wLTe_p7t6paIxmiHt?y*ElbtzI&DSiSGC6FgY zbHwu9v<_ACmvQCJ#QTlZQ>bhvY9d4N5F_F(I;-8TQYp0^Q)0F%?auCHN=ZcKBxB(n zHAdHPN0e7Odc~otm4rNcyR;^MIP5af3q7qmBPxqRRGp*DllfFNI>9ZK2IzlcABSgo zuM^$P*A}?BPfYm&<8EJ`mZh+DLyey)nC}nGXxECQw%~*CRwKZ^<(yS|ZoFkyn+`;| z9G8kJ9sdB3xQz*2Hsd6y{35k`iEEqFRXJ;yk{1HripA`=t~&P-XsUkD>M{K8i6i>n zAySadQWl*Il4URtaoQ^;32luIpk*HRih2o)f<$K6 z9gicpHF%~%S*=oms9I$w6X7oyX0%Rw0m?OUP7wY-d;KF#f6BUm&uC^DOfIca6 z!(5W8TA%257muLtkttEoPF*6Oe{Dlhuef{QM$(l0vxPMz-fifcE{)4@Ya&ehLHOCK zC|7bYP*fyG9p$d$_cT$}61rE2O4526kUPYN!yBp;Sv%Tm)h{^{2T4;|HHJYBNoQoF z2s6}(v{=hls)g(&g>T0$X}?@!}4A&TSTX8?cjodO42l# z5IdMu)-vEc(aJMae=&lX0YlT7rjLNhUlA@VU&EpAm)06DQ zG@$FuYr@2xAa{oTq06HXjjVZPO+|Yxt<4GPsGs8t_sLD%V}n{su&y1hQtjzlwDQcK z5@i;AbF|fFc}S@35)+;>N2n>@T3nI`@ptJ1E0Nu~GMRvpETPqVr z?ss0}lKnyXl3+n{RKN6ART)_`OOorDLf$54UxF&o(d%8+=o+3ae0r_lMIsKqiS*O82kGYCbb|L0DDA5$0K`sBR`-uz$KFy zVM`{Uq$$8AP(bNE(ObLa61dS=EI9mZ14SOWLgv+(mz1NlrebpNF=pcKl4$`>WNqVP zdMN2=%~MrWrc(6PJp|)k`Uf;D&g`G0Wm%QWAg+3? zPnCs|pqZKL9^$c!+F)wH=3sWHrj`2F{{Z2wNC*Asn`4|nKuFevrne&ChtiNvgc7ZF z{9x;M9n>m1W=QP~OHfp^7}pMo4RLYwrgJ-l$?GTrN0fAM zHiD@QaH@aIH<^4V5tlD2a?;tANmr^~LxpKtf|EIG-XSg2@?k*TmxCNx(ZcP-ayW{D*(gn!wQJ6CLcbsF`tM@}C5%=+jgo~%c63UXI zjRem~xE#4~P|dUh!3?>YD@`hj%2Tv?rg~`%+BQjFK#?^V%~V9>?nGtBUbown6tDcn zcn*>14jso!Ky#H7F>umo6na%vV~XGEle^>EH9&k7W7=<$*q7@`PQs*~lh!^CBce~Y z8r~hbPyQkJ^@V$mUG^E|jahoTuIcIHtUMC`{txz&CiAPS&Z{^ku z2u%VrMHN|*Dn4$y!=f@%5LkjvZZtLD5!klWi}px z8&ziQx~t&{ArKxag$q?|l2jxC=?J5|(VLjJ7G0;8$`)Eil}tIUD7EJtNt%@-A25)Z zbZte;t8fFUGXDTh(EIu(0|LQSjv#J9kyl-Qu+v>c`@o{fHT3~PYmoZFo^?S`RT{H! ze5g%&9}Qs@D6NeF9@E#jsedrbu=+xSiXU@O@m8`wIs9QHmGwf@y5)_@->+?Bh>*sj z*XmyhJB&16P`E$=Cu*N@M9q43hRRb~I-r!6>dU25bkwOUQpg$)jH8;Q8EMf^9vb21 zXqsx_aBumV6{$dKWgNS>04WT0iGHO!dG<*|i(6LBF-jJcnI$^Qv_iSAdbYtEVr@#V ze9rT8$SU1;%utmXCtnlR3%9eNQFnJ?$3mIgouasvElDX09mjYv@s${EnvxY%H7(65 z&p9Os+8ktlYpj-?QI$0k=Ph;pk>6=J>wuJW zmIob4GAcccI@pRDOR-dH?lPdy{4ypyAeO{aCh!EGcN=$kPp35YGKOCPO$(;2%wZJ| zs%d19f9>rYZJ}^oenHx|ep{Jo*pS`^E&t5s$wswU3lfgvL?ug(!9Yq4E! z@lCDnolJ!!d53jCGIUa~CjOu)MpE20oCX?Pi}%_(1O-eLAIYJPi0D$=DlBq}Wa$n2 zVwFk~r^|(@)|;bbe-g)Uj7eiYbz0EjK# zv?xgtivZ{)uY_%Yl4wyW`oi`UDU~MfmLiZ88_;z#faWRqovjiwrp`*rLNyvo#t_$fle>}ihmG!e z4MS}+Or#;h*KFqzc?_IUG?yADV7GjZpi_&AJ+Qd%p;K8$CbUwNQ!brj&wS6jaKTnE z>K2#BxFnMh=k!y%9^>mCD@v5JN{>~r09=Ihk1^%>mCfVLNugUlI|&85Xa=Z7#;LT0 z2EYTPqPAXKby6lxG)=X^$?pWucX&PkfqhCtTtO2Zf9-?g2uBJeZBuieojO6IqUW>l zL@%+p9?<6cubWyV#m&|!lCA3kK+9Mqu(`xhHeMPNX>-3NB}u25BcUB)a@QTEsupX4 zPKgmz{dH+Fm`UtLu{#Z;S}KE;kPp2fO zK_LxNtQy(a(+u-kSpI26e zTV0U0wr-SB$1ml2s(|xMJ0{G+NbMRX54T_uxGHu_mKPsNidlwbQ1E#{^5T^}!g?6e z?Kr$}jRLH?{z*-0m}`yNvf%c?IT`oyiMCobz6;?VsJL5RSeZ#52p~Xur+xY@$g{6^ znFm4A9c&as`{-DpZ6ZY=!z3?^L(+r;&#-`q(a zvvTnxq&n>aSj4pSE7O0ZTBNsn{Ur^Td9DmhfM^j7%CeonqL%@mcp6hcrEIOZ%9uVQB5T~jKQaqN=r!>W22$~BRrE*iV6X`yYDF#8VYCdj1?T(3IZn~vy zgX&MX=@RzR0yI_os|p^AWqt8YAw>tx+|C=gxS-J@(-s`9ubMPl;! ztL85R!kPQVMjtCel`v09#TzA(A=C>;#Nk6lQ7t3xg?fy;DUwW#L^Ckx-Bmv>AUJeN zicE^&z(^`mk|nOOIcLdy3aei6#@4h>E;X7ZF9f`gk+d54hkSR=|bQi7|nzAMA5ZQ>NKmJ*}sqi&sKp1Q^#J>&h;Rk~T?;%&I6 zl4E(=)naPbT#rcydKDr-(8>+4d(4WNnR5hYomVR!_o=AqZq>i0Kp?oGH2A^1mkI9M z@1nByi#+yT1{0#IF+)=8>RQrLgrCjYUAjk}@2$%obYAkZxej1PMwB*?37I{FFdex# z<3wplkQR})V@ZSG@q-w8DC?$FDFcAn*{XSor)V;l#x<@rBihkIvpnsyT;iq0vk#=G zp+Z*Q=1zzEV@8Vc!Dm+%A;m|2Tf!4ZPf+DKDFq~s(e=gPa}$;}TU?m`0B*F)XOhr` zCP6Fnc<5s|x_;@3)~y+~5U=lz?$2APY1WWX0z9cQBcx4PL!ytnmV-pwdymViYVhmI z)Hdy9z!lsX7;`MZAV|R6JKxoF4VS*BvRlLI(MqUrlRl)FipL5uN@X3n=`y#7DU!5_ z5h3Vjl$Y~O=KSTuails_l8+1CDC8bBXv?v?y`ApB!2Xl3*`6gxwpGEQSv6D8gStA!^O_1ZrKy^Sgg6Nar$&`jHD^f}GC2@#WfF~wz0#Q?9%n?; zqg&ppI)ZgQ;kOM_(8omm@ZDOKSiqFzM9!Ve6$3&V_ok|TG~oV*@SjxZK9M6Ptx&Lh z>b|*luWj|jCrOjKaaX!%s=VVWT7X~`U{$d*q{i41JqmW|x~bEQK25o!JqXq&<2oqk zw^g0N@OMp85XHiNBk5M(_RKm|8TEL`{7g+l{{T+2sQ&=_sZ$5u+Rynm@^?;D&JsKb zL4?~MN&PBYFhA6V&gBEFRXX9Ocp|9^JDXOUwVsh0*@p70(zDRis-w6&PN3Vswz1ZI zB2AJ4H9{6^qFB(~x()vTwh)Kf(%y0Vi}rh^ zB7*6wDcn;|C18RM(jmLXEE*?tay{Y}ya*Q$SZXwRBF8g9m*4xXQ z)}fT>kX}#m4-z7w$T&s4&@YQeD^C~hef}}xJBqoUYX1QE=au;X0G8}jxegJK)88%i zNd+!4;#N{foRk63{0w8+EH%ZesPMfz$N6QRN-*f03Gz#aSs_ASSc+CR0!jy0M9oWn zo2cFoUXbmrK3V{xC50W+*<7>-r9M=U>;_Q|qU(S*R> z@mJ|e+j)W{PSQtj2+?u*c1kJ=^Nx3j&1^_>Qzw<+6gPHVZmN_lIXRLCShQPR4-%*s zxYsb$D*NW2YxK8vs@|ic8|0%cpcD8ACGfID&7NLscYGB^a-R!caZ2iw`b7qiq9r5o z@POukwP{$6LfHnTM%w#gn^FEw)uZx)By{=2VG7#GgMeFYx2lR2h95qodP;r27*uU7 zI;F`Fa#^;j3$;1fE$ai-LF=gSf*C0&Iju{2B$akIUu>use0ldNE9n*LG`5ydv=QAZ zOdIKN)duyih{=&xA(t8N>$ z-BWSMd~)rn*Q)4R6;$0)5;p>as8UGlF$Kjg;D@@kqqOLmhU#6R3)o5s(6`XeIPp~6 zPt_P(vS*vz!;X`wmZ!e38_JSlqIE1(_AK6vTkgn9k4tMVDpN`Mn2T^DP^954c%e*( zBkD8lkLJ$(Po})>BWK{!< za%!|l=l5e#^DM@yEO%FsmgJO@TU5FOtXiRd_NYm}Ey?8KjG>#2Gnl>UL2bMxOyZ^G z(o)tRfjgsgP4jq7)P+K}qcs@tqZ9jWt8-8l%74lC*%L;zy_t zPmDZcs6#;i0BrS8?~^#a>PnZcR4ow7$u2el(CZMg+v-*44U`n2DT1l9K8?h-P*XXA zHGt(5R`})*=!)I9x{_pqsq`?)C7L6Qe%|AC%`a}!k|o}7rQ>V8#03CASu!#t^@x*h z<$ji607ppSyV8b<)5mFq{WFs$^esR1UhEW4rlNcGkA<_7pH>o!AL84sy1kdwg-9tN zrd^?IwRKc|{p(!XDNSs%y2ZC*$CxCIMzDJrLZ)00WKjo~Y;HMpTZldhj zjmk!c;$X)|qR;KFN4lXGAn@(ZqVZPxN!Ur+Bz4vyu3&Xa*Kzqq2!bZXxVGa@Qj)Z( zU>#3sz}EK81vQY!vF%c7!-qXVOv;o90D1m0iS|c8s8N+xOGD9R@^7M0SC`oF1sQ%%mS3W^T6QRF15fNJyUv zMsdq{;4Rz&v*L2vfTw;LTE%rcOWHRnB69c-j9{5-S25IDMVz7Ss&Jig31Mx1QlTlu zY0RNhP`auna|S58#Wg`?wKxD#BqztlH0}i`5{jy~C3$pucqk5R6}Fqgnov?UtPY1y zgnA9|ZVMig>V(v(bHwjD)AZqLJ4qWOJmt0?f~PQdKA@}@8gVXL+ERMUMMedD2z?f_ zXlgV{2JhD!bIC;$#idj$Y{s7mVjL_NG6Nv0dAAZq4GJja9w)zMnEh>H*>b6EwxuOv zYLJm6_(!BTWR|w^X^bnVdj*BI@ zxY}rXgt4>Ygl(I1oV!Je*{T-eowj3DFt`(nuNQ_DacYremURRq%TDtVhj$oJg{w-z$yU`# z%BnXg0%_e6Q)Wd3Yp1}+nInkrRw<@Q+n`HKsdA8(hrGmC?5*7A0E3d~HL4wQo#No* zuF})Eq$Pbw=^`cCJrTlq%zHzMq-JwRpi!|_2dZ6Dj`^0a&pmtT);2h;14RO)&5^0q z8q(s5SR2#RL7>(iWCWBm?|?^)Vd+vwl2iB?Ew=OsSLA?4NWs#rlSaMtG1#qBb7hf` z!V~5GSfB9e1xIKSB+F=pF@l1ly|4iA5y5JKlPr`JSSuw!mS-{{_FL+KpDcXR&fkEv zt)VSTzfTyDiVuQ|pC{u~tfjzO{{RHY=^+NP0t$?mCOpxKaA28#FHe{Hq4z{V2osYB z#STso5E0Fq>`fl59u;|hCuV!E{h z)ax>kWdTn;6s?t!a*((D*58g>1v3sM4y7_m1io<^Ya^AJjbu!zQ8OvZSAMB+Z$CDt zHJdAb@@%5iB0su-jkzqM)ernNl|=Jv8kaj~@gsFC85Uhs{g5s>pF>7AVm%)DPVjt93-UI;C2hnH}L04ODnW z4LA7?%{!QFbsRE}J=AiANNf3ul4a&Uz9IH% zt}m2r2=$skKm)84*wl2F0UFa=$^64ud688N>yVGA?P6og4|^zIs7qQ485?GvzX*6W zAkI>ioOjf0&AE>mM;Q~GnwIUX6(titVQQ+S%1U~5)>JYHd4^hX)gX9~(oUZkdAwrs zU0U4~JNeE%vh3Uoypp>E;7g8aQTnQM z2=D-D7@@g*(h}mYP{zgz1edu~g#{15L2MLFyHcCGlbe>kzE~{=6giSC%qN@=owNT8!jiDh6ZGw5bw;fvwYOhGEMlH|=`piy@(gH8+hy_iBG7FiwD3Xj)aAsWO+-TO%n-!%O0yz zrr}v5Q9l0w7}D&x{BeOrRJh2w{Yq>uHC1(6hVnPW^K6Q7j&YeeC9jvM;@=zt(Z9%jr$@xb~L*<$v1=oVW(e2zyB|e%*)KAB8 zxd2@snE4=b(w<|Lj8kOpq=4&0c##^oa{SPn2Gh``9X3YGp&+;u@G;l9LrsQrd=cY! zW}EMiw#bcp)(?uEcTj#DGLcv0ZQP)_m-^)<8{xgaUp$VO&SE%xgwVcdCF^6)|E(IEu zbh?C@f25*CtxywnMoAyG9Fx+AaY3F+cB@W;1u58^YMZ;lCh5{q1$Wh5F^=@2R0A=j z1f;JPA728uS{C#S}JZ})F=+q?GIQ( z?mPgie6u{u3u5EdTDJ=OYjW6%D!OV3xYas?y0z+`JBKDBo{>|3RDudln!*`iH)^W)W%3#Y z4EcwaJ$h=U1zf^`)_Q!QGXTw1>xp1Dyb=|2Wf?H8si<1jJ>-;Y)+(6^YgOrvH;oa2 zMILhS#aj<3$PqcqPlQr0W?L2>*r?xE>(JFKwxb}e5RMHVLu$r`^0ta*e=kLJ?-0{lDKgLkU0bgduN1cA zmC|M*q=H78q`nrErA<2}AE;sNr9CAFSmqwbP^i1SJ7j8J<2Rw}A%~uj5=bXstZP^s z?{T`FRz=8g-raVEW!}{}4LCMIZ@D)40V+unREJsiF{ z68qw_uMeZ5skkTB4W%M#*Fo_ySIBXq_tmPqY0uphdmrGF{DaQv%qX93+gJL9z7-?| z6W8Sr+F}e&858-1uAJq zQv_*0jAOUfCK)KbJ&wO+%pu`pSsA`P~a7at9!Yv-ll`!0bX(jK~p+!S20liD?0<7Rl3Wj*5N@=&d+b;%h|yfE7bG*K!{ylyoquc)J_ zX+%8P0VY$Kft<%M>Wqfrj_9>aA1<>I^W9WaQT9HPy7o|KDZ$urSyUGxbs$pP9;8R; znDWeR{L!gevWyD^yPMWfERvN1l|T)3Fdkb*Da;ecxC&|L+gfUrCHzKX>l9W+tyY*N z^njf!PUgZvm=e}~AoyKH8#h#}Xy~7ShmekSQEgLz10xB4MaB(MPOaTcmDUjswT-e#U%&~$ z5C>L(D4VOeq7``oE9t1KRDk#B9!e!L=EX`!=@L!1eJVnT{<~=RZEJ;4O3+&)Qkiw_ zC}ilR?Z)9ypFS0+b#ID@iR2Js` z(t3{=9#l&@D}MW9Ik^=&%1;s#6R`gP)0IGJrcjkIz3@1ZhTg(VzvW!qJ|!f7Y%y)4 zN;@Ro*FkeFOIEa2KIZZig`WKk;uC4!t=nk9RO4E5)|67k*{@xs=!XW2PuIL2$q+1k0GUIrG_%#|r+O?D0?DV#XcLR+;* zP1c2kOwKsRQPn`p3tlKw<-+%_Aw zZIDzb*dV&Ff4OYp)8*j07WSq0MdPkmZjTt`vWzn>J6jGsuqkR6DNPGKCVLpobKC{( z^~0!J9}e#z+#$RhUFNNBg5hnvQMFras&2BG;1tu%sVfQuPGF`p39X+CsNq7r17m*; z%QrBB7mKDEZie+YO6E-=CB03dX&T&XC+IIk$tgDTlr!F~2 zk5!#}bP|mp3(AA^ab9xfU8{T~x-n3g8~!kr7nyabj)f$2jSkNu*9GUyrVY$-YlRd> z<(>etl${#?{a0%BVD zK=(GJbm*@9ca;hTy+I51D_v}P)~XmSy3rtlB`?MYARQKuXLclQLQK-CriIY7D5YL7 z8wS@JBY@r2FKu_g@;xWQP`;bK9~r^%%n3qGXh^6*?|6NyCSheeX>KG9j*rT-DQgaI zst82p6m!O0>zeR$=M&AXzvoUitTL;y)Rj1&2B9#oDe79Hm7uhw5*80x&Ks02M)H*QOo7{b?LOMh z_Q>zkS-@_NrAS3EYM3}aX=KXn=F5C-4+oGFCCG`N4~$0K#A{V%wv~@<3TAnafz6yd zeXz7@ZFK~xXh4}n#Qu=DF0`RS-(%^hQOd`6yUM{;11OT zYj|0q=!kh&fN`F5qpZ%jshWIemgrC&yJJo5W|H(%s+QVBf*wDC?*n^S!>3aJYK0 z+>w^*Y71lZZmLDbmnrT_j=f;XEm<>lMY2%SP@q;p!MQgJU)^oSl~ZMTXY{q{1fZgN z11|BOavnZoe&Nwxc;;tKr6TjJZHwi+R5aKxwkNo1Pk)3(*~nOERhsCD1yp>Yq^PxA z+HtQU>DCF9)2K6)A4=*JYiJ1tPV6@WxpNjhNo};*p1t0xcDF}EPT6($Tu|Bq00fdr ziD)PE>Z|uQh0mjUshf#?k-gyEaMyD5R^#}+&DP7d?mM|vyenul3zRj$Il6@(Zs{U+*9tf&m&yDsRcsDdQL*YXBBdR&m*Elj5ChR#EHZ=|6%}q) z62jDijgAhf=`Vx;=tx%!d%*Fc^3QE3t!YXcOimKUd7&>P^pdBa5F`FqY^5MI@QXF4 z%Ck%V0G$`|&Aat(6ztw~mYqaDaN4C~=Gf$$!?1d~l(rb8m;+rp#~Birn=sL(Q^3a@ z+zJw(>Ew2v%}?VEwkjz1W}D`>CyZ)z{{Yi<={>OPVX8;7~#1@{!ye;wQD{0RYm(~R=3Tc(O?&kCrH!Rm7PhkaG9jTC)!cibbiirvQ z#T|33y4s4@P~(6pB#+w>lYk1)ESTp;n@UX9+xn!Xq5{gal#lL)qj;{1>xkdAI%~-P z0QA<@oeZR}l%VoGDi%7EL|tmV3WbD~es9hA!2af`Q(F)!l=m7Yt0>f!CwAoa_`{@B zA!C@a!A5Mh$Qg0<2IaJnI_nU*3sUV>?+uXLC{ssQN~!2I`a~RzR<>NA0hf2kCs7Rm zovIweFV0&=y3eem>MyGY3Fajc^* z;?u!kJeI`T+Tz9U?!HTJLF1;_i01Dd~n*Z=q&JeqCb#sX$i0 z&AID+lNGq)WZf7?^~+OkHsS!@l>#TI8pI?w9@rk^j|0th1?(5uwj0S@2X4#q znl#kAl2V^cZ4yW+*iN&JBe%G@@;1)64m2GVH=pu0W3F2O74ux!eZW1uGwu7_)@^~) z)9ttKMP0CgDX~*E2@j-tN4J?DskI^BGYtyB`SrMlW=P+a;;JZHElEL1+pl4yUz)>J zKsSlq#cX1ts*SLjt;M7yck>W5{9;34+1Pko?)n4e$zm-oDnp$?r7Fr#wX6y~QMi zR#MhgwCYT>P*7RaL~C>NHQpboTK@pq_JDe758}C=`-*!;ciLYlH;m9#J$1serd+MV zF0RYg^iaL7Fy`F~+awA18pP`)5;J&FFv#eoEO%8=85IuYyb_(W>pzq*=!pttw|i4L zb+#y}M9BEVW(^dCSLwUD+rK}O1fJNV#ra8!TPt-{?H~^_lmH#(R3LdRXu`h!q<4yV zmDiu!_t_MJNcLZvsMDoH*5bB{Y`EE{^~`32|;ONU#=&oUJ^bKQ|R zw=QP5+X(>{YPT5x4JL(1i1XwYA~{?->a3R%R4C<;x~5opPAYmC2(nO~89kJO6S%y~Z?@1AFN=}2XXt*)73byIZQMElOOtk6J9gaHc zfto{9cTCZkhyAdG+)?Wb+7d~Eq6k+##TcQ~4NN@fg%dJPLNIC2r2=*M!=fOXnqpF( z-6By7iZvfJgeBFn0PWIezvB{7G*v^+^HjN_rAmqk>D?j;3MS#?6sxAUH0TLX0FIN6 zwY4fW_YOnFrLf%eGU#L>#x{jIb>oUk z-!2KKm_qzyG!XD{qT?CjvB#E99PhtmXk9A(^D?ztxOsb7JK4V*VswFQF*0T7-&7Gwc zJ13R?5z!=7T>6OfNDi?upd4xzmnz|+PT8F$@{g*UbmcB>G(fnLq1IZ%;iIYsSzT1{ zS!mFN%*)9w-6gCWYRZ}fH1{5glRy=^djb1qa&Np#qd5&y9Kv{(g}^Qui>;EXl_dF_ zr9y<3ua7|zmzR*(vvc)v@KjhIcbcEtx6&ci-{88*_WSne_Mz?0-$tB6i>Pk-3rI+- z%WllKaISe^YOU1?Qc*M6AWA4#{F>%-)`Q@+OgC?AEkG_C-{y5vYbN2YD9kOdS~7k* zvfi!L2`F}&qT5K~c1ltko0$4Ud^vlTI-$i_E;!YUjiF$o;}`EW?Zb(^wsYlz$;3-d zzS*5x+`3&b#5k7`XmvFnkk}?vlsl?|Rnl5%7pO7@TVUsnR%pvc+ugc0A4pU^lF_)~ zUhPSkmtU+zA5m78O|&Lz@oWeNNtx=S(@6R7r+=sv-PO?{3sD2CF+dZ7V^mE2C>nS} zWuYueikC?uG@SlWJcDXfQYoqJhl`bzZ>e;YBX{yw^U^PTSByIB5>{o-xx;LowYsUz zTZU9(if)xZ`Kl5#Tm7uNEUxYf$T{KZFZBdLpfk(xGAj4yhyw%>MX^=v9_UJtQ5phe4O$uIGNKZ`(gaNAgvQa*mP7Qj>?sn@AKWM1F-L6YRbav`imh;XnL;w<$ z0!&Y1Y#%Sc4;5X_XQQ=k4z4{1is>ua57@nV16IC3_g7Nw9hvZl=(xU>ev_^w`GBl- zB$Ui`N>A1r%JMr~0kRKU{QMC7dxc~(2i&?QIFIo7u8TOs+aCdN)smkd<6bAR;+EQU z1P1Bt^!}oN5}MpnO4NKwCMb8Dp89uH&p)8iY&d@3;;fmrIKrxby8i$S@Gc32*m)++ zWw+wor+c`!M@vIO%QWk{Q>iLb%XOwgXX#6KsV{`}j2pf=rau))O<&>Ww2t5L_FLbV z#h^V$f}uC73z`iCcu!JwUVo#gp#eN zQ6*BJP^cxc(I7xlV&IloLY#5X3VUhV3^Er|i9b#ms%r8aji!o_(!xwmS!2tnN{G3* z6-?dQ>04g4But?7)c1>xy^C5jP}dg(q_U_SaYxme@g^jpXljamTv2UL>1e@QiU`}R z{t$djk;Z@^rE?ppO>R-hcI#trAr7QCk)W8Lgj}%PR!I#=RomSASO^L(<$Re}T(?l$ zgf?{0#;=OvkwPk{ce&@h^-VohZN})csZn!6d}84Rt5`Hu$?n|pk1Tw9a=ocCZAvQF zq3!UFPm+oj`i&EI_A*Fx6;J*f?dqbT#@?)HO|%F*h#nsp&GWs^g5II3-HhQgN_|}v zsjy%e<}XeZwvb(D1L_%e5s2n{gL!!EttN|0#&G*sL4e6zc8Fnw%j*B6iehmRcv&~6wZC%(7=Wd zD;3(@+M-kQ5|4`l=#ZwrETd8qW}dy^Y85YR{SsRpWcs})GySpiSeH2|$rP$mbxyu9 z@)Z`o!A^lQ_(#cp;XqYNr4!}+NREb863IXssko#jNB2gU(g9zc;l%)2LL@=y^Ns)! zZ8ciaT~f@-9x^b`xKiC}$8`vMIR#5{3)kZvYluS0hhw@UW-?SuYlrI{Yv@#TxI^HK z6L_b1NH0$aUhq(d9QKqJq2xFe0RzM73{^iAWvL}|LR1e!;v=u>tQIaIG`Tf1N@>9^ zqppw?l-gE-gqgv?L;gadX{QvV`2PTGRc{3sqd1MzoZVAbLV_Jshgb!lGQHg&N>|eZ zAqUl0q$t2A%Q^iaxlrWlfm8#vTF!mpk}8RW>X0{ydb&hi+PY%CR7`~Q)8!Gb>ZD$E zLn-?HFoK#W&7C&5qsw7T&u}B9lgy0))*$B2$!Zma>T20A2~?_VPXCjZ>+Y zaI!7b1x!==QcTQ;!{PFY$Zq#&vTMlY024s$zhn0*^5rJ#Y!6g47E+XxF+ROvcYrls zc*7ex)mP6W-(Gms&F$TMqg^7UdnSv0*Bd7owt_<1np!AaAWL=u-ZO zEK7?|<#3t!P8|NW_V}j%0Hm;@+SeX&3lpDd!R*geP^7mFwA3Ue)`@C6yLJ)NBP3E* zz_Y4MEr(nF6R%S!_(lj)0#LnXrREIMcFUlIn_xnJLXW9$^)3GZEozu2#P)!{q_n!I zd5Iw{taoP}eYK#S-6S-W;?<66jOn7LW^K1ORX2Ew?@+>6P*Er2^XnJO3%7!>K2d~@ zLTaToS1Dhy>U1uWt!0%;X_+6oE_V0i1&L;O3=egD)z13}#;bWI*H3u7sZWZ%r7fy^ zKtDf}L1uFl=&AU=DBpKyg1mblw<`;l#@$T{4&;gXJ`qcsEay9_(ychWakWJsal8#< zr?6ZdMGI+C5~(bSY6pEgL3rDjIe(v|2Ge6w4UYEU-nPf#=yj^c5ko{CQ|mD%3 zRmItcG%QPz=E-)7Pa@isQA`OCpAM!`IBn_`ZMkX{8F^)-H%k15r3PYgUdXgDv|0`d z`+XA`z>6(%GItKE68ubO(tkN=(SI#ob|X+6wo&);kK7DcpqrwFy6TZU<>;>qxs`#V zE7R!*iKxC{(NT6oYNM*STx8fOE+_&e)SmK#AiD%A?6A6}HJ3~dlBuW7Y5V(6Ls<^@qiR^uot?;+s;O^azhl{00YJsgTL>MDv$t4OF|fDeB9^oQ}*?_{Gz z-0l2wTbe0jVNgXIB7Q;yhtf2tqN4)AFOTMw2L7b3ozwxO^q<5)H|^ZTGl!FGfTRto z*q0wAK4U-aip+K=aiYA-m<<}IGLp)$$`n&64M6P+b%TNmJi}h)l8{(FnvSA+Lk?kS zqBKG2JsH&|s|Ko;up^sQBoT2MkRfa2J*0)IU598u2C41AE9@;& zy<0&|RPd%@=bA(Bk@;Ivz)|`LlCfvID_VLIqH@x@I2`fR0=ty^dVQn4u4a5`jb|6D zBRN}@^vu)>*A$mh;c>L8W2ltk&C4^9;#$@*x{$N(5!?BB6P+aT8UxWyzJL2m@CDBU zyS0X4)b@Lg8w)hEF0hg#FgH6QGQc^$ZheG@znBKx}#_jN(|Mb_0;(n6*Q6EM}5sqE3!Sb*9r z4l8ty%|8g3m*h~^XV$9|n;cNcpH}b8uiY8t`)W$j@eL)2p8~k-t!M)xl9~Kx0o%CM zA2?Q1fKsZq(9+N~P#?N1?C78+0;onqX?=w$YDV2Z7-~l8RH&a-Y#(f;sH&kPN>3er zkv{XgwnmQ{n%BzXu_l~b-N3gCM!>_^3J=KNIzJv0!xaz zGP^}lvla7LJE7p2kt^-jnu2eg;+1P54h(r z`+cdSv)yY~nqdv8DR$FRwJGMCp1zxgXQW5vaF(~t)KSSeE>n`i$ySo)c`lKCxO>du zpK=yjdW>5jYL5N1e!Cl~T3f4Wa*-QVDeK%uLz?1u7KWfztJ54uxYl_VhPds2l9o;| z6(uLl-YPDt(!*=2(Csi-*y1LW9c0CZp))P3WNiv-@}54Ux+TXck+JC?DCkUD@Vuqb z5DLmUk8L5kZsv*=8!}8S>Js7~V5I9J0gZPNoNR)iuPbtH86}}1!AH#3jV+hzN9pS5 zlHpYKkerNKBD@4F!v6piSZdW-JoD`phm%urw9-mjjFOp|%6)`5_yhF{LmSB@v>~qO zs)Ib^ORgV>yo zRZiFh`3G}8(eq?*2~U7Md2+i^+^CgHbYN5$CK6?+9kqmuf{-!PD}NqRa#s>v&UeMv zcW4&owU*7wRV`D0;Ca4^64T)g+*`Ims&+Y@8TcvV#=Og7Q>Nb2__=uFPK_JG5xzK$v-4nKdWwpKFg7E+`TL7cjMVSw7HD`_0nGkE)y zsBW|-$$vs#Y5flJ6b!_>{NM*fMyg`k>^xAX!xZ*Mi1IE!ljRIt+)FRC;_VcT83x@5 zD(J`^wcZ+6z8jgk}oX>H! z3ZDediQ7+tQRlp6ihH288`ZT;V_eT^Xq8naO|f>^G)tg=7Md%p3fRa}#x~py!$P6n zF_vb0MU}3{kQkaQH`*$(zDfnk|TnN!b&RF4k+!gwonkLm(V_BLV0FsT<(EKAk%i&!c>d~Um zF_{4)XilmZWO(Z>ylNU~0;4iIAK4Mn&*r5jU?W9(e`4F_6IIL{gN2LO4?PHooQhAN{3$KQK5-j@?d>Nttzu--*{8RD);9e9IM7I zH5HU}#Z-?^YJyJDtUUp2tgBMRi)2DYlVDB5V7JoeOrmmNX`x}S-G zt{G6Iag!auDC=>jX{8VVp0kSVf|UzGfYc&zmeO?{d%(j)>_njrskDyWe{^;32&eQ$ z%vCbl(zKq05+HI?+Se&%!}r$K<1O#jKuh5mZ*XjZ^#HXlEp!C|Qg-W5kw#qI`I9h4 zy>yIbOLA(odr3oFy3+d^;5e<*+Pl~1Sw&UK<#DS;aZpN>Jtge}Q8>^$IfIJeNMF{a zQahuTn%V3O&=9KY{{U!fge`cDZVkv*OJJvJspl!~OKmb9q4Ol4oLYFa{{TapmeidN zp+#n*b@maL0D78eS232#@YC&`6*8WmIMH%YRfT6ZucLyT9A9xYbPR<6KpIDy`L&MO zYPRk;Km5L8)tL{H=lS-T;h zZ22q5D^eX*R_-;A{>y1Eh}=JFrHVQ-9ohu-p6K-30j(AgW&|g@jp*2OE5+KVRH@#t zqeIi^5NZ$%;$B7p8mH%6=QIzB{R%L8DocJFiqTNCmT4)i)M#O}fD#g#bO9hDizy|= zYWTR?%XWtJqEy2~BiA|Ke-mp>yMRlZf2tab#?+6_SPZm34a$qI0x)gOeE$H!XFQ42 zz65zLTCdJCy=0IEvV&c2osQ)+7gZpYk*A35C}75(Y{sd$xsW%IVV4S{VU>A<(5~l^ z+UVG9y!};EEwb1X)lLBrdCRui*}g{_02J>Yv3=d^BVef|ch~Ipwu6p+uG?vDAX2+a zW!&gd(qV$G@#9-Adv5rpqlHO6E7u%{i%KSmfOMr-B3!AGuPfy~N61_u&+1p3f|_!A z6bgkTeIz26Z%>%`sQceUn|zdU&#O~SZ+$^}?J@^W@t|=C1Ey7ctfVnRs=AinsdZ{P z%*@U`L63|!FtY5K+~utfHiP+rJ)qE3VPz46FQn_bULQCnF+?NdB^n{-wxktmA<%U~ zx-x_}QrbvP>7Jm$Q2<(+EHt9tnxyU%Q2mhXg$wLC7_nBB5H}G%r@|VlgIvQ*_M2$E`voyLiOZn!rzjl)i`OI=W*GH z=G8g@l)yTuj}sNHO+V)B9RjuX&cv7 zm|Y)o+f1p=susVKPBoYQT0uV(8jOtC(9z%)IcqnlyUeJ3n@~d3c4|*jAb7^ALTR)^ z7i|GEmrlYT4DnFX$Vo(1R4Cd&M2~Y0xC$<|b3~u0pb}P)Gny04c?qV<=}>^MQ7O)H zh>MpR^j0ivsS#_W^<6*%S(ykJG!-{I^a<9r(;<`ssQ@5|^idADuT`e;c5aci4Zs2< zK={I>00r31N@-GGyw^z0Orct7*I(NVJkTi!V=vJ^IF3BiAuCz{PMY?Ji<^~n#9|Az zqH*i5>{+PPlq+P1m!w0qIptg&#z4fuUvjlvpL8i_VLAzzuvx)Ds;6Y~8?*%`S8{9* z%^@uy;AmA=V^kmYzEdTAL4}DBt_F)*tAwT%9QO)P3tFqR{L_wmtK?j|ks^D?0o60G ztQx9MWAcxZ5yc~@(Lm|p9SII%U0SLWxdM9p~aVc*rZQ?Q=NK{O`6W`pj(meydWAI)3@#h`l994)| z@LUqa#y^bX%p#uKZD9cjLJ%d}sXa-I`ttGfyt(CV_*^6w5QyRw0W6&oWVzxvO=UMAb8SOwMFu$2zNICxWJo()w&%88Lt|4G zYYYpW>XSgIIi6iX?ZK;$isILtIioV>%%*r+Rh zt3Ty@KWuSxR5b_rO7MK`HBg}o9$wovyMR$MflkKy?9?h}37cACR@~%jIq<>z|>)S)sPCP)| z843h6fO-VP7H5=8ap-5Ip9OH@-Mxa<-t_*t51y*Ue5oJdN`8O{sXgEdm8Lbm0aw4f zPq;@H`*gS5Y*_p4XS|>gp-YX^xD7oT$dFxIsF+9{c8yGD9Iv?cmJBUvtJLl<2Q9%G zXt|Q+j&|fuea)+L-b}qJ@p-Erof8QvMJ*LLGYnHRIW~d#lc^>=iORWE&Q4wObRG*; z#bV>P05m3jFOHL+)+l7FTn(bu;3}E@klWq@EwSo}>z`?X=OfBBFxxJ7JEB;Qnv~b% zIUPMBSGB+t2Vx>(-y0;fOkCYDg)z6xo$ALH+eDf7{{XTs*ql2UDph9pAW5P{u8I1S z>O4Uw_(_Xg8(X5UypmB>cj;&nr7dX$37P02_~Ludq$chpYg?*ia=t^hS`z9~@KTZw ze+anZIIPnVP^wujLmYiNsI{1K)AfsS6weTz@;XMhisBK*&rsE8m)wQ#c7-u_8|8h` z(svb?3G8C?4XYRh1r=iA;qK~DjgtzxUYkGu$ZR1T?FTJBP^aNK#(GgV=G#P`8khq;)?i#Itgm*K(XqU4 z7#mk0_uT&5XiJUL?E$`um=csluY zL&{_`icP9igvPx(Lj{_t>jwmp$0_{t*{CD8#v2bSvZqO_a@3M!b?Kxk9cqVrsDjj5 zoYTl<+{2EJy%R5$2344os%0G!fRNb~hDG6*0g}d@qW1TYyfo}{fk555GpwBl>L4=m zh-HJkkOf^EQolb)_)Kbx79<6+HHkmR^63w?^+`76W*!A3g=za?-PBwxA!X|;St>?2 z=hY|M3rn{ew&W7If!pB-0WLcz+=}aR7h43ZOb^X1VK08sRDHG`r=o7}xeYt5IWH+a zj0-%Xis6dHJ+4ugIjT$Eq@*V~cZ#*yTaOfgwu!8_fTiiL!UKi+tKHzyE2+?ACEK(} zgBnpXf!cp#5MZYn+F)*G(bAsa!8Z zT4_d7@r1pFHNJ~-+d6ifGUm}Ok}HDya@}a#N-5T)ml=0dsDQ1c#g~f81e7bzec`fp z(&g3v00P};T(Z&$p-Tt{S#+0J)!5=%7mIRFXmL*7ds%OG*;160?w4PrOW4#b&o#8w zQct=n)+s70cBo7hPqe2N3;8I`RF?;~{Lg}_e4@Fx-E<{$B%OX@DKoyhFG^x<=|sTg z3G~}q&r#p@#3kEJDzxk=tAJ4lIITaaE$-J{esN5_)ysLnL+Gk+66>G|AD*!oZ9{^x z&^2vE&DInqOr_Ey?r~uBPq5V(F_1{=tvn(gTLa{cYba3kUfOVeU}7puXSF=qRS8R! zwIXVgw{;pr47-tq8}zIpd~Ed3q!gJG*TQgo8Mlt8X)(Z>giBYADoR^vDhFLV#MQoG zu0p=q?L=y`R5t2CX!)h2Ov5`|bWxIAP->mLHykdMpGtG=f7KJVqjI8VvIeSH;RtTj zS}r4C(^C`Ht|4Xlxl7K8+p4(ISFnbhDKnRrV@+bgWJv0$nLz!gPK%)Lw-$Y9wb<>H zKB^M7lAWV)>^>xZF%g)F&N>B_*j|Y4NGqUhca<#6+EFT$OrQV>0$O;*W9Z#gemN+; zuBse+o#eZHRgSqd?xd;2uXK)wuSmLKF|VS{xfdXfrhu+h{m)!KS(8=2`B0};M!FL? zjL+B*{X*Y+PnnD8=n9Wm>1?{%)}Wq}A6Od7&;lr~SzFTJRK(+Y=G#R+(~7{l_F{zKquq@(hL&qQIa;LBe~wW zf8?8?6PRs!50pD4=Ef(jq2BO3)gLb-r8g?(o>bHwT4(un2jdWSoSSQ?RMp=bt7?v# za-N_~wFU0$7K?1lU(l+Z#yGP|BD(8kGyYwJO?-O9&9+&j(Lzs+@(CSLhcvo?>JSj2 z(1{qa+1)kH1y{0B$QVL!Z5Brxq)HHy6{M%~i@X;D+gXLwE-yqG6}{#u-BOyAhvq&o z1>=K)lg5`cq~Cyoz{@8gq%5_IfCve&cNtUE_h8Rmq z`bL5k*8R6W^SsgQVKkT)UuAog_;I%fr9j)^7b|OLs9{c!m8M-1Pr@y)y0yr{Be)bJ zp;V}$zr;*tvo&ez)zN-U{{Xag?5ic?I_j$f*T*wQ=~f!U-?ddX_*b#>wQZew*4LkwfITrS~bz7d~y}oY#~tt%fQxn%8O@q)f_K zX-e`SPppS{e>;>D%}|Nr7;mAo5uT{ugIrOqLP3D+&VNSd4F zRm)H2D#zx<6Ocdt@zbnlxdZDfRK>UQ!bV{)pXw})-^3y&-{wY4u6x_=Hs#vT%7S+V zBuIhcC-IA1HKMBCFmLLdE=9ye3oivV`9YGT0U$!&4H8=Yu2XyMv;{lWNeT%by1+ej zoLHj%LZJs&Af-RPG_6=ZL!nmcXQlrDgu{$GVlIie zYn)Z`=skc`A#~MC1t-P`+oe#h)hHOQ6CP+vel{bzTZj@nzLEF2s%P6v3r0(EwJX)4 zsZt4+_UAMxPr@IH;I$2PXdE;er)JlXS#ks+s>LSPaw157^0c$@@Pk_By+)n!_0%Jm z>Fce9CG_uM!Tg$(d7Du21VUcI)J?psdzR)dTcsnUFv(FaVQM(1tSglN0DLWva4Dw7 z*9}5iV;PP~h+XL~xW(oB9omgaRW)@xifw69OvmLq{b2t9eXkU7s8P2VavUESqk_F( zXkT|XC>L+Ry~b``I4EY1J83fla!O4?2BYGk6wL1jieO(3g=oBAx;GZC5nSrg;;-Wq z%yEqB%SVu6x0-wHqTqzNO<7LjTtbqeF(R49OM7E2yY{?MdKVX!w!CKOom!tHT z-T@N)6r0*oZt;%M-J4r)vH?98{gEk;%f1s(Ra-m7;k#)BYM2bKj2@@n?J_-P$RtQ= zJI0TO@5`6Vh^xD#Iezc&|EU`0R~wX%s3C zb3e9A7GFzP?JXKpQm}U?WP#ErOIavYNG6`i^=Z&5Z00|2nGOeCQ0sQ}u8?J@($ zq(@v?7HSr&$2qK1fXC?N7hR=N31!Ci5J*x8?-BkKS|lsD+HZ|0E|Rsmc~;ufF)}rjG-LGDEDDqo z;ca%E&B!G8N$LprK;^Yddf7pGsJFV>ca&{En^DpmajIZAwOKc{FLf!wn_e}OzrN94 zWW`#Ek`}Ebdicau-Jb12xZ$&fo|kkfgS%rC?Pj^Hvh_}vEO|}Fs=Cct{2MA=D-xg1 zCalF`0IJqjFeWY@Dm`Z0Z+gHlU)5|%TWDo|qYFgDq-QWw+8emGrlB)$bG5+>$Sk-| zxeR?4oWZ8-e1~mlw`R#wkdIY;0v+)a@qya_a8h#?$SRzZ$){;$5wX@n&y)c$fO^o0A3^G^uL9|gw`+z*zxyO2HMW&ESVX;XNe=9b>Y>gDMvmM-^d zRF+~veNj*qVPe{x1 zJm_MsHov0LG5iSSA>7WYuQ~VI)=R@VOO)3tLM`=+QKG6yG9Vs?ajxNWcG)=E_{z+4 z7@LfH%OneLAO8T$FeI-iPQS7(kh5r_rA;S_O7iXX%X2Dqwarb5jeoE~k2^vMk?%|; zkDYHerKBaL5!PgpDQf@;(xt|e(Js%rJMci03i9>}J}{$uR4efAL5I6*^J+j_Dw0m5 z9e%L?0M&qnb{0ZbwR@zky)6`vva#x`X;jRm1N~qr_Tiq*N?uzpYPr&ig-%NwfR|$q zom1!2MHl&s^HW7s5ZuTr^L8-}cIp*x#YMfUHo1}4y71Zw1K?mEbxnORN<3EQRZ)AQ zZ9vIt6B3tk2oh?!bsIxe@ulDW2uMtNMr!cVCKx|0Qw-mnt3Q=4bpt7^LR&jRh!WSl z;XrPYYD3Y1fYJsw7 z_Y)^*ztvIR7Si`gOY5w)Iz@G|?RvAm-*Xhb!tSBg8wJVCXf%s8y!C3RUqeS|jdJ== z)LYi1q!S|}4!l$#U?r~#AmlAnGg9hGBt+#E8`HJxQpM5iNOe0@Q~*f?KrV48!5s?P zl~r$02`9(n0}`I17KGHO06_#o45)_mPd^o1>uD~{AyZ3AI!{l8QTbh-_(@hg9Qz&= z@%8@zTWoGhoYEvC&%V~#XthY@^wo4Z_Cfa^=N{WKjxf7aPTNsbYAKLUr|6yf=`vNGk-Y*PP))J%`@U=0=~7y#)=YnYc3hO2JFjR;2!>-XEQ&;Wf#f zNhB?3K2*n!zVZ3|n2aa48TCn`_2=5(5@p@X?p`#ERdp}!lO?RdI1cwoW`FcV(5a?& zX(_Z)00ZG4m;5`BUEE}u7;&LGuTfgX8_csfbOwu+hmy8MO4TCsVd3s%G$>4UWC+D0C0wLt9@LlWh{3CQab8?-^l5TvfS^h$)f=zq3345X$dk43$w z>rBU{XXC6t?v&F;h;t5BpWq^0G$*V&$qCgmT8ycktu=1WRt1zi>5OY%SP9$kBZK7S;=pmL8DbGT@C zQmOv{bP(olC}`WYB%G!GX4!Z8YGDm+6R&+%PSPzYJCi>MbhOb!$tS50RZ=LG9lAs1 zqKUS?h>cVPtw5(i13EoXjF-(dIkmWiA0)cZLlM^u8Yblg3 zGzqwNiS;c?B$M4SBTZFl@|r9hrFvt?LGji&Sca<5Sg4$)Q}Bk{sz7*Je@{{1e;E5z z%E!`l&&omh{{X5K)>S30stC4f60K&S2A^1KaSNe=Z)%pJN61LiSar^-Q2`v$)4b6D zf<7_uB{KHvme$7ID@Rc;gg->V&?_JIJ?`_7y_xb89EXBZ(%utN*kbN^^O}~*Up%u5KwxB`u z)RF14r*Sg|Sy{zzsTC?B{`%tZ(3&^NQHDW~+^?0DcPp)Hw=0cu*6nR`?kU%vb!T#x zwWyM%5@ty-RJU$&8rsFN=o52xQkGPA5(h{Yrl{iD6xUrTpI50PIg&&-yrQX1n2b?Fi|S1)LwRg9&(UTRf=*moLcO0}9szeuo$5;ax97-^tN7)o09 zK3^fBQb7V;wT2w}g~tt4aOOg<&{Xc%O#}$%RXGxaESDEjLOlD4mxovtqpeh!IwuCl zXsxPJ0$VBF(qpVO?}qrMC|#m?EEr{FLfYQamJX5t{xgV+-1JdbRVQtXl2g%7{{U&+ zgO&Sc!)+D9c zTUb&aC{XC9l{IwhDMo3Y?v?e(AQ}Z`$MB{%)a_M$-L7?)3YY6D>J?d4Qmyq*EoxHS zQPinLQzXVmcYOI@xyGekZ7kaupcF(2X>PCJqtYU0ct^($s6v`U2nA{Y$ez&$H2UbJ zW4094Bkvg@{Ut^^*D-m zN(9V|{{XTzK0Vut5J*&PpDzRu69bTC)pdczsnsfTmsr^`9AY?V09mc>Rq~#xt^T7? zTUqIq84^hAtiEw@g58n$C~2l7C9Sg8>b8{;sNUiQ9 zBSAfXY&=4pwsIiFZQsm6BSX-9Aa=A>eK{%TqP1>q2W02k9b{Ed^IVal7P}z-01mz3 ze$rsdZIub?MzeugedYIChT zk!h<+0-yAhdL>7$pBR*F%SxkUIArmi1ya?E{WIwWx6-eGA_)?;R;9zp(?Sr1T$)&( z?K|rYx@sl`lY$|*RyMRKZUhO-SX-qI3R=$Hz^0!dT4b`5BjFLV6@SC9l)z;bVEU*l z)_MuV71>(`2H!xEwzkZ{CTCa<*_G8!-#INA9#JTeW3*Mrij*WH{i#DB)iC2lqMN(`cXKl)&*Y*%~4kbg5Qt>|c&@du>fFQB>-w>8>RTVv0%tO5AtR_(x`sP})2t!PN{Uoc^J)_#v>?Y{1Y~leryhBeG_Fw@J)2aN zM@5@AciJ*hnEAv+mJMxO?pIpmM>w^ zDmep^j-INPt=9LHj{~PzBF^eoxC8~RJ7d2QGGoJ>h@lP9; z^oAzUkx24@V$W>$tm@$U_0S~Jzp6D*8zhhqlo{*zM6+%IASB$f7e=LD2YZajc&eq6 zTyYJ8H3z5f=@Jk)6Y3f)SCnwKO5-FZg{Ek2(q0FY)U6Y*KY0?sZnS9eaW4vGx(zqQPqu~S)5 zLcNRJRCep*K9LiDZw$hoD?{PD!v4Zg9a<{6z1gl@<`2O} zG8afoh+3>s0^NL8RJj$xoS>!SA=FL_g7!vqC>e3>e}YID&|nnAWPhdQM}<+nx?w^fVNy+t&)`IFP3F*IyJ z3Zm?cjFB;!9v+mb!iMzMw@8J%$5mS~Jb}%n3TJM3>(t7dXwzAUtX*S^dMy_hp9IjP z*kx?E60lMSK@mnW6{6nphg4+L)8a>C3Ud?2>Zof@5JbtG{*lHqsi|M0P*PWxlc)fH z2C^eaB((_}sFT+!qfoM8)d?gG$U$*NM_COb(+g_FRhi(uE_u<*aw)2IEG0lm3Gp9Db%WEY zZNODN{{U^}6}wiTcYn+{0z1entV~48M5uDR{bHCN^eTlm9dxR20^3ZVQ3ghSU0`gR z9{V>_trdeCa3jdDyQM;)D}En=a;8B2r7KN>>fs<&G(ag)&?#x0hz37AIZt+Z4;Q>~ zbA8Bb`xogK8|`l&~Zk@^a?I)pwAiwZ%KwRR0)w(6q4q30sOST%H z)7MY=mbX-pNk8B{;PVq+D%3C3NW(`3fqt%y;?+w8vV}^+0hQEHD z4v^$pmB&1UaEB1^Ow2yl(FFJ#Csi!HxBJQFPi!}qs<4~;mDt|!YcTq^7Uc`cn4Ezm zp!`z}-{S4>8MZ;MnqwW4yv57D0%_#BSoW*;CE+h|l$5rL`e?<8`hj9;1@f zva=wxp55ap&Tv_v^+)Pz-}t9>?QZED{+Fv0LfWW8Txns})&WA&K?EH^n2I}?T4+|Q z9F2IZO+xYdbh@2|JA`&Q3wkbGT8D}riMh%6{(|b49BHJq@`2QJ{gG+J@<|~e6Qa&J zPj2n5+Dk26F^Z1C>aQvN;c&{q*uH8>=Po|+o9!cV>MoUXkOLDiR zVL?Esr=^ej#HJi3Y4}bDTw9XJ+9TN$;J7k zkhn)LMPAn%1$Cz4nkw3LQ#{)Csih=>2r};wvN6qY1#VVXaZMiPl_|0I%Qkvm-bBcK zB0;nPN6OM%kvzq7T_z<>V?+YlUYuh$~6}$ zS0dd)bxyuA@P?$9M1yW>k^quGZNGHax{DoBS*TLBEq--&1avUjB8k?twOH+QR*SzE z@pa`@d#Y~KD^IU5sk+03DiiUPJ8b=>NJ{{7DzUS-Ys&5a0Og#AYHC|SMY_E}Z8H3! zN3TxtFZCm#Q7p38PO2?(M-<)h3!{%xvZ{AmR`y;_h+1c%1ZffXaq0_FnjFJIiJQ6N zJduw($NaTGn#v^&mh0NIsHAl06cs7R6Aw3Rts%oy?2*?{p!XtiXCQlO$!YEs&#L8y zTviuk$yl1$eG}XevL+I+J07tEaRcIYThS&dyb6}QKhE}i&fLwDBT61sN(vlp!U!oo zQ#tjA;+>N;hjmg=Oz6lhQSaOb3b}+_zs-2kKU0a^+}wpE6&iJ{%%xus2&CsRMk7}A zS{@@R$;A&)o2(l)$*~KazW)G=-s)^ut6GBSu5_+(#*A)y!6BIhDsG4Id&Z>fUuBM?ykWNC)W-@%&?1QsYv&p3OL#H$agTH#%Ip z-nL5r0Ot@1Nh+F-pBT4BHn>n|suEo98mDjD$2He+j$X}@_PNs)gtqF;hvtwv#&6ut z5B2ut?CKVe+YaHn#^c(Pp-;!X+^-PL`0>k{9A(PJ+x2M{k|vUr&&w#Mc5*ALS&VN6 zis{}dEoXtUu!B`nSXKR^klGh5+ACSgLOo#{>KB#X?}AElwC)Y92@?t$h-%~#6SmT7 zqgt#BNe9y<5fawX80S?9MUaW^Z%F7#K&o-jsS?m6&rl=cQ7k@aJr*J-yz{FIgr*kS&=F|2B%+?Rj;G22xe| zWvorwOW+kxArFdt3vJd?sVYcTog!37dMf0jT@wkG;*BPuO9;_NKzu*Y#eXGiYgKK* z-+(BapYpqYsq)N|*hDaGRkR-zWZo><0Qz$zb&!UhG*7lt3NdFBW<$%=bd)e!Xq0Cu zQ;%3vK~&qRklXxH@Q9480r62t2eE!X}JYFrF7DBkJju6L>vZqMI~-ubSgEH$Pvz7Z=`j^PV~1;^CYq^!F?<;CrJEEecA2v+!D$jQk2hRf z7D`1H@nd!i0au~+)6Nl#;&0n3UkGWZRHP)Rc8?o(4;gc7n=?gSRkJ&2FD%16QBU17 zkZ5wgKBly=>RoZVrj`;HGb#0uk4*ic@q4G|op&ahEGxNNEb!(Z5~yC)L>za_W)gHG zr|pl{8(5c%uBA0j9W@grundw+N;WN2WUs2#S8riR*t7yA)bA548KpL~=s&O~G4VyslKHyKytLY(5tCT0q(r*ohD&9t!NX9_CEIe2vko zf8}-&<18f99mRzb8Xi<-LD ziI-k1$TQkCC!mcWyn?EZkgPw;4M@oo*WnMw{SXpJR!n0k00j>z?F2(1Au+u8p@SS~ zZJwjLVdgy1b9SqHSk)*q*dgG#ESe$6$LUW*0VpayiY?N+!5A@OXLsb5-ugieYKoe3 zRCP&brfx%Ky^IqWAqxA7r9~yNukeBUVfLY9c}fauCeyS?YX>msp)M;_rKVxD>;b(8 zSRthj@|5&RZ6pl`!$>96s*aEziDyq#lp!Dq34Zu4DToL!qz)Rae^7)dtSAzDfCPNo zvI3i7$+4kHv^rj>+DyqEp$&1WA*g*lu(;@$GoF+9Ly<7@|s zDVHS3gpn@VL_OSksmyfRsNk~QATm2hp0OI`VGh+2)FtbK!7UO}bTaD$zd(w%jJ&0_ zgBWSBmkVBHbm3Gk5NHXjv^M^sGNYyXsYf#c9IVARlGys~GDx*Pk+CU1^ zygBZaDS-%8%W;P*Nqa|3M6uswJ7-j@e+XC+9C@oi(I2`Unj~Dw^XVSeh31o(4Nze zoj_4d)i~UC%&sk@i9uFIu2Gpb`c&GqCR9xsb`fG|AS$!PN;Err2}gBIuZD#&@#wnX zwzwqL(WK`n2YFvLsy}k28`acmWxlHKDe82A-tYokY&A?aJI3o(LJHd637n;*R&rUW z;X11=mMTel1tM|IK53xZ>KocbwDFABHO-Dea_17P;=A3DBDyk2d`6uy-nCbnVl$PGhho z?yG`gTcDb%hpVn`6*{6DQJE7u#m?Hrk@zT^i-O{UE}o8=3R2bibyUnqM$N*2DA}Ju zQk%@1G_~3pSX0SKPfbKvaeQ`Zh@e&L{Je1vswZch(y2P#B|wlQ0uSwtZwbPf!V7|+ zWqCm&jZs4X0GQpc&bpL3r56r|#x(0ZY)~^rL7yWeuGJUWFe~l)p#^NML0akzr4CJ! zjv=W{+hgA=v{H`=_W0x8gY|4}9d{Jybd2kf^LXPF1zd5QS?@I?MJ25Kca7t11-Q~T zpaZyp*ng@r9G5G(ozw=kSF0=xQBtU`{{Ws^EX}Q&Wu+k+DqRe|5G|$}=IPqi88$)~ z0**OnEjd!R6HdL|Wh3y7UygRrXy>XvW0+9Uy%Pm?$*b$#TD?UmsGS0|5wK#otWZz@ z7IBcv$p8{S?Y}9hDLmCZT|%`dUZ=S+b%P$(1qU_BE85#C%LPRz$Kg0Nuu%df6;1s$ z8j*9@6Cw_W#ue>)m(n{#_SvmYIZB0u=^--)M#=>ZTJ=gsY*i&)H&5Xl*pTPyj;XQt zNSR63sg7*bH!G2vG1oMcWPD@3(oTHm2~)Pu(H3IXc+Jn!|%a4|D6p)ee`lhZGSIN^ff3a;Zg1ROV+W1j=8kx?4gc6bNmxI)M+{ z)D#WzOBNC|gIs8s##OC+d&6z?KXiMFCDho7}0n$xR)|W_v)<5pNXq& z)C47yyeC2dND?0y(k-0YFD&LQbsUwb zPGntsmDPT!D|2xQQJlKPUn0U=s`Wnu_S;G*fp)5>Vf6x{3W<`uL{epT*HL;`9}6DP zLT~T4C4QaJzGQ+A)&jYfqKe6lv;qATbh?7=YlJA7ok9F!wtN+;aI zjRJ|czg3BE;|XghoMF)xRI*lNk+}DUp43baqAYsY>e5cbqzK><(OqhZoVPqT5Rw9V z=>xhjk8Q|B>TSdx>6d66v{eu!1$^7?wK*v1488zUf{5~p{ZUS|9mPdI7#a2rty6EU z6aN5%XUJ_$HsbcV-ex7NW^)@wLC#_Q%8(SJ$MV`L_L&M;T9u&GNPQyNYdCNVB+27k z$7&MxoYFp`vH@3Pr+)~TiOR8e3#x-$=_!>?;YX8bnCLr1jCUt$g{zwJUW#P>zwp%N zS?g4_ODkokzyO(zB_Re_WE{qolJ_4FE~aaI7igZ{J-k?zj?k_8M)w4#O}Y^-y&?+x zGWS2`OJ)VS4JQ@Dh70xVb=%Vbcwm_wMP_BisDMBIp!hMzrSSht*zvipl+R0A$_ zqTc$5oYF+RG=?vwa0$x`W|E>sePfN>ULeY6U!3A1%PQtPshg~89?pqbW4l!s8}m&h zi5~Kcwl^yO01~t8h7E$HN&E)6MaD_$Oq5T~G+brypjkg8#r0CAj-d}xpcKr6$VI9P zuv@ChZtPFF(Kgu&4R3e459Rs7A0Q~WES%KT+^8COChM5Y;VVQwQk{Jc=nRa5reA*8)6OTF{h{xKHa6xUr(H z@&iY!9ervT($YG5aWQf7C_5jWR-Y~;o9puFHBa%3`u((!tz#A`5J?Ojz4u0keZ!;; zY1KJ?;YDx!C70YWn>8COsO<>^kC>EPEr+F2?q{QttC2H{hCZrFW-;G1R2rk#F^svDAg zy$BM6ag*Da$d^E)d3D|Fau28Z$3@G>xUX#9;r*lM6&2YQH&lv1;*A~U33OAwiOy+R z8%)5Qr#PbXU$=`+Lu}_;7QI@#Z}(|+$L|{r`y9hv4}nu3+DF{B!EF?{Zz?F++dEkm zDgkIe!{dL?dF$ElFZ+J4TCetx>)6Rr{U&8V6zv;{2Tk%5ZC?dP>33;9=rBB)fxEf^Gt{uim+wMi?KfOif-r4z#i0K}lhGw`_8m~0m+DRFsg%OoCVx%Ucr)ZF^6C#vH z4BYw-hH{)b9++@P)VBv1Ax*AI24O(zA;{d|dZy%wt`ttimylbiLT&jaIk{2*kcycA z`(36L-{fLKE3iczpykJwnz1aKw0@Pt5;0!8aOX# zYxbW_$)!mkC?3b<4OD~CCB*n6uwhgDX~T2s{B3b?)|`-gHbx7>!F!S-%s7w(VWLuU!Uv*#b)VMvKS!hl7`$dCzGn`H<=qInFQ;q(0O1vY-B1 zk`AJC>l*GmD%XnDd8a$Z9}A6JLaOuJZ?~I$)rao1l&PnyjG#q$!kWVY4a>_77~=qJ`lAIzqA zx)zXUr(+GXs#~BmQWp_L;h$}Gs%y#>NmqwnYE@*cAb|ZXLS{fG!1jluBYhHXB6qfl zas0)a&rI(KakHVGvk-emtXbWqyn87F)JkPXb3d5}uqG9^YADFr2gOCc zSK>PCZm76h-%hesOIp~P9ZA(X!=-^oNFKvq1lig%AJWiP;Ti3v8BPy%Np8B<{;F&% zP3)7ZR-U3!@rI&>Hu(dTW96OzxbZim$lPOecPZlf_YzTYMzuEA3W4-0QZ*8sg1h&K zE1M$6R3~AaRXp5D%eI`p%Dgu-Z8em2==7F{2=gS3vm3jK!_N$rn@?3MZFK0JP?%0p z;)>ohzoAs2RhYfVmn}CzsM@W4HA}iJ(#lkj%9KxThHx}k*kgX?jZyB(%_BBAsKrBc zt+3smr*lN5S_#MvVshF=2`oURJ$>w4ke$bHmc7j5vdothTB4T_g^6sJ#v?tzqB|7~gVi2pBniS4* z(<%LTK_E<`3n&7yU=O7-iKakMAu~Pnh{bCx@R&>M2^ES|yYz~k`1LEjqpI2??0rEY0$n zr-WkM%E-1#tv`<3Rf=^@JvA>`?vMxJ8NO4A#dk1shN{i|aK{}I7u;r9aLU@-RZ?CJ{#4BN?H-`))*c%@ z8Yr+n=HEJInw1Z2<7tT0^{XVw*LS!k(>)u?JeS+=9THGDom6+N0htM6DEkY!mHICp15_p zgPLfj4>4tL+_Cyf2OTaaYGh~O9$w zMqJ<)5qr?6@0C{JyI@dd#RK)#*)i6(i4J8@ zl)6(rCpc1X+FAPPI?MOL4CtGp^YsVAuUJ?qvl`rro|^02$6DeOQ-F;r>Isze_{5|} zp+ri+ju?39@relBXrmnxJ*Ik6>|>rOpKnBX!}E}qq8w|=4p>4tW#s~o*dVxy3GJXt z8fQ$z^qd^WRYc0&QR{Vb022m62Z{nm`79SyH4~IAn^Od$s+y$qpPCkFRIuQ@voF@I zl(awD327D8I?_l^rIy@XbqU!rPNF642C63T)v3e&f(vQffx2E1H5_GBA2nQvs%)*K znWALMe@Ibcb+;(ByD|Mt!+MQG3nMCxhCo7XxlVyJ_U8`Jf)mzV`t^$LL4XRYJ}3fJ zeBLS+^>fU%`GTc5fiD4EV6-kuoL<_ZQl%%aN&Mgmr&kEs^;G_lo#i#P09&fvq4~!ML{WL_p9tj_R+YFxm>pwi%=IUn-uxPkNQNtGp8<3p5VaC;Qp!7n~aZ;pb);K<7 z+7^n!PKxOqE={&7tp5O3uOHtW+?)20H|i!oz8MawPc^he@3}dV^^O{k6y$NDw^pM) zA;w5+s#~K)%1-X`fr@n&ypM*F#7IR>i6vhSkeTaLh=HPUxb-Lck`>#e6<{h*nJ?w3 zw`Xl8-qMd*Y9h$S4>eLNd@VA(LiZ9j%i{yMYQElTvQ*?{^oGwwwz!g$^?2~Q`P7*- z75TnVn{vKV%Rf(wy>Y%c>w#YdjXjEz+@U1vrn+nUBNfQ!WVkgaMXFfg*%0WAQ2yB@ z4WcTAhKj=1AxNm&M34HQ31dX=sx_u_aZb_xsYFH7v{JUrs$ALJDJW94o`xv-AZk^P z)h}u5+NN*Jsr8)oimb6Y#Z0l32(smsp``3-Q&67ZH0`H&a^Dc=9a5ya0XMamips@I zI6$0$iK|RdVd#L8;GYy&P}(N9S2^_&V}{&QL?fj0NXzbBZL>1rQ>i5&$WOS5TO2I1 zngP{DUu7bNsx4=H;j`3giSV7X+(+)5YPb#sL02r>C(HF9D4~$&cZ>7OX_pkWESZgE zsf{+r3l+FjE6lvJ9Z^-c3ccUoN2t=jnsro(f-Z6}!rIjd6_k>dAaKqCb=9)vQv+$1 z;zy6pGu+QPjyixK6qltbBWp<1C?XwAfMr-7qK9`{O6c@{5ayP*q;6@Q}hQ zO;AjWku8>X7K%in1v+*kqz_|-xVv_>qB7$p6b(^-Gv+6rTdlJEz?ew~Sk*Z1ZYn`= zRNSX4@M%%r@tE8$t}J?mK%HY@#ql^`Xwy}jUuI-`wJ9!^AE{R5sY)leXt&y0hKhMA z{I>Q@|Pu?e8PR0j&AXDEl2;H?`O4%C{anOG(xz@wDR!$hEDd3Dww zQ4$sDO0?2C5|~kKNRm#o(7?q=!0l2l>^DzuU0^v%vSZOTDrAyK@2mu+W;8;G)J#e_ z5dbEdbVnA{%!&4iI2~0HdQ!oJ&O`}#K>=5#D0&g``o}_Jbruh!$Y-d8xKt&@)i;-h z$Eh*!(jl(`ykk$L0c9P!j^-n3i++gFqz>?=jZ-BKX&YrBtT{nd%BL@f5Zbv%6;rYe z3ZzbQmZn<6QMh~K$whJ|*2sa?cDL+@8?{$ht&3j865DziOiWHuxZ+$rp+({zUuaCF zT6IPfY*iNRiBM8{NK8ZG>A_`L9AgTuPjnb{x|)i$Z6qKmDgiPY5Izxa#!g(?6^Q5U z8w|jYHP2r(+GTaFLJ#pMZR$F}IaU%iTekqYC4oUK(t%7ZYC!=h{CY-zdfFB14lO?D z^-MkOYV^o5p1(+ek>zRDFw%uH)n00)6R8JN5iTUTTjSBFKuMQPA;6OQQD$2z@}x|j z(Z?DD*eOG7$CT=1rpP_wt&qxw9T8EmxhYeMrD9&6PP#>L+4>Z7ZHYi@Fyw)wavvyD zGu2NBv`G7g6tD?Pa~~*@W>z=oQzAz-A?_H{wB=woqI>HSamd$nM#p=usXlG4HnIvx z*TM=7MVpq<4W@3YnQ2>UQhre57?^w7;i_{n+|sVzv?0aq2$z_cCOJ!Lt9cwWa;XK5 zaxxxmw3nttq^b8k6ID*Ir6pU0y7klJ69VK@MN69#N9q*s z`+f2gIG-Z4rymW6ok>DWgvm1N1zEh1Ld`jyhRFlhY5WQ92Hc!_hzi8+Eu+gn05ph; zjB~fuL|A7U(Mqj6@y|5X78>NURsmCLDVI>ud zXd7#&o0c|oOBQYqsU*0iD0fVJ`jOFn1Z@R7ybn-ZqI76pzAJ>AKiV%BD4orG&+_S>9sIJ*7%} zL&{YuWT{Ov8u*B^I`P4k3WVH4Oq3x3zS&mu1;n@llnK^-zEPE5_efh!vE3HAW7@HC z5OaFbGWTX{E$S+864^2Y0!P*^ST5jO+=1%VRkJ;}p4z8sN~WQ=o(WI_W>f7K`+K%X z;6l#tu}@=9f^w|%l+H0f;saS~0E;vL7HG4Z*xnqI`7ODjxTFc5gHH$&{@tW0yfo6V zgv;Dn4ZN^*mxNUA;-OlOHgf7!BP!P-Pk!+OAfmV0ij^omyN_0}3O$JVYZJJxinYjq zFIF{LRE&MeYL&D;nnb0*^c4YVqm^*-y1(_RkBK`^(lcDA^9tE9L-ImP@%i0&Wc2fN zfw9L_&GSmNt~E;{%+~o5kO=t@BIR!|qNd!&H&q+tDQD1h$OGf2#vPHw&?X`($iCYt zo7WLJPNc-NU&>Vpzw1=`_b<&EuLaQ~KEcax-ZAMsyL-wZr`QH+NtcM8u!t{h;bHg1 zSfV+eD)v_gS|7|E5f=@8*nO#cqO%?}_YsfM;@6t+SgE#N8EwiskfiM;w^j%M_L&hU zg~OWr*s196@{+PXLEH_jHo4nfKh6bHL+&r^=k3p&?bVs5w``fLda|V|dYaIOlJyIl zhTxPfGg2qe5M>wJeqLQme7qG0Lh34wrZ3rV)ZlKn5vXY&E{1e%|?l z>n!xORhZo)yQ(bpt~mX&v9#$X-9V5hw?i0*cYek=e>Jys_Ecn~cz<|auk5e>L%A;% zowm6;xxFai`Akl+kQ7O8JgCO zdDl*bQtu~m4NT~~m) zziG0VNeGrRPcSr1YAaNiHmO2=K^??v40~OuSp3lzx`iFm+!C#zoqT5nHN=E7h)Q~T zu&tmOp5_%AL!DHqvfk^hH7(T8RW6;z&WlQjBn@PIL@F5^4u~ll)73pTyu!th1T%hU zP+Vo@y0V!iMt}KGAB_YSbizw z;78gSwJO#|#D#b1AwTSiD_*JxCgagN{LgUTUZkaa^a6cH?1^B_6>9yV#)-7!-bu~4 z$0Vc5usoWt8NO01DZ08>RE^Rz+O+;vB=-uKhA!=*nT3xP1v6sl?c@)M#iTBSy`+89 zDDtN5n2$cvw`RJzv8rs|oh3AmsQG{95wE6vNYmvQ{{STI-75G$QuHSY??ZtQ-H+0) zZqs50>c6#=%R{FH1bk&SFmm6ACCJI!7K@pSOTS*hPyil)4SY%;X<42w~s9OEi<698LUt{9{>%0Iz;|6kjrj*=&1aYfm&ZPr5vfN-X-f2 z;ryc$!Lpj<(%&=sp+}8Tq$$PJII|%jk@4>yg5o(xT-zBy^p2~>-NNE=WAZJx2iAHj zx8&?zuNTQ~w(6Q?Q#jhp+8}}_uCP?pdE6A{E$(Y}JE%;}^Nwy@iDc%gPO3>r{jm{uY*=VQk;3PhJ4Ftt z9kA3>sR{(3C$E8&N0^8z=^VR)bFCLfUq_=-1C6gmHmOL=OS}l?sQ}=L?pQTV1z^A8 zRVJ3Hl?NMA6he?jvII!LHx^Y0rrbHDNUkR3i^KP73Yu4#vS-vPNq*5(own=qP3Web z{{RZqIvIRo276-DqO#du)8@)xXpUX>mzEZ2N4;L6=})Y85J>FUAUm8A4KMWn&BDv2bgYaPjh ziEY$8P(+H73DCffg_-H851?*aVC&`rAC16;343DZ>JtTxWfzV;xIIq$3A$J=0JnO_-%jdk z)>JmBD@o89cZVBC3Kba}wz)ODvBHg6=~c^oFsCk9O4sUNapaJyy9F)h zXoj7dgoj-r_?tkOCvi!f6D(D-q*BzmHXQO4pNW@vX|6Rz#V8sSbDiSrr=9NHjr5Pm zyauwKy`o8nMICS`>V+L^@Vfk#=+bRW6~5%ogRjm$n(46$yP_f(T`8k_qFb&ld&Qqc z72=6hg@5prp`j3nu3IZ=(@>u8Sr(s;`2tMY)C=cX$m(P3WQ>DF67Hd)`ek<#G9O5hxoujYY~4vy zA&|CJR)5(#{xMm;ZCtNbN6?1YS|vdv>jYR86OvBVtqwMX{De%j)GG7J8D4_xBp$Lb znM!6bcP5JL!>D~*M5alK?aOH8d9aqO2%q`A#=aUraw&-VA=JTt!{Q;5x>5Bu;FG(o zH7hoXr`YPJTLyn?$<{)DyvA?LEeo4fbK|p6(MlT|yHd*1b!c6~9}$bL|Yd5M-sRlG8y<&(mBUAfeE6M<-El!{*1;c3DTH58X0+YMr-aMP#cajS9 z&u!??o=!lymrHH$@})W^eS~5dW>ZzB=I)8nM(m2XC6$6D;(igQV4x;IsCUF|Dmz|k zy-K$ys2%5|dMggyu?3gppH~u->QY3%h9=c;>Q*V+uHUSDxU!Fm`Dzi?bW9cERk+Gh zf|5{%#)di*MkHv&j@fG?60o=h7b6W()i~6Agc2kOd0FX4l;`q}&|6N33BFW!N|H#@ zL&g-{P!fYgqNQoWx5W4Q!OUnX8d7MKEg?xFVt*3`Feua23odOz(@_O53ZY-s19|xa zKCs(@;b@{KdF|8qb%i*(UXc07!C-aH? zd}68|{p2*Ktu{o4QbT5P8pWzMDpQ;SA*{1Z%p*?F!=b9>^q*~DIp8%$ zTgn1txt82hYN?RwjR_jXTO`T_bHEC?D(N;*tDEamNFioN$5^Jy<5jX)+OU9Ar)8sU zmeO|g0%xQHEfum4OIBB^)YG@LEECvtg+~&P$X@LfP1R*dc}hFFnZaODROXbq$ODU% z^_iS~R1s-Glu7CIkE)t@ARvlX(n%B6J}a=)BK7`bA0pT^S{zUUV@=se4KQ z>6kiohRP+86ay$UDI#(@$~w9uC@iDmodFj(fJ(vX-Vo!WA+b|byW{DL zf(K%tXZ3rPTzpUqo<%`#VNN*k zPTsowVB1_&vYG&dpCKG=)knsN1E@WVUq0U{( zH=FzFO)HAn;nGCtdPcF1c8rrH z%4fKB8cMucU?PNSjII}&8){2bsX`hytDN)Y>X9YN ziq#h$cXE!p_{EOe(Xr?PfSTlzjA`izB4e*ulMM($YLpbTnvEcckP@Yhy4n=Bz#0-a zL&2zL$yD*kK{O zsE7QjUvc#tZd=(3GCM&c4%}#<_fVRrs@-pBk34`KBdJKp)3rT~N`5D&NKl5Wl~0!# zaDsE1woy{y*!fS9GM~yW*bX2j8=a_CT(2ZCyJLE*j^R(Yt_e|XrK+vRnSE{qtJipL zGnC2m%tfMSGc$&MK9UI+75?h+mN4#8$T8|w`n1%Q3JfS~<(ZUX9EQ|Ga9BJRx5e)b#jud| zQL>()NpY}A038AO#0wj$vXMZaoJEl9Aw8t}aRfg4oBrRy%RAxzlWeU}8 zpheWcg=-9>CN&8}$kI^jf{c)?)Fd|@2ZRf^RmL4rHD=SekHQlEib~(ANLQp8{@4k` z(OK;dh^>Tzl4Sy&QF1MXK4UJOp^{4ax+AM%bpQ#HdcoGAL|>(w3HV6FkP5cE zl_zxOLO|ACBSqr*YPV@#LhfcK*_^AtqvsoY)})bA3ER|>AWzP5bq)^IU^#1@ngs>D z$8uX2S_-!5+!9vliO;m+&0#jsvP`9$t{?hU9P*Qis;;P6*VIyvjWWl;Z6zXbLXmbZ#@gX(Cgc{{Yt&Nsg;su#->|(MH$;EeQK1YVqlRW)+2Kl8ydS21gDnQLfTN+0DU7Q%Urs; zDR|foJd(befg1rSpM+Kn&POz8w>fQUN6Xi&a71fi5PFG;H!BIOL~B$&lqQKV;5>moh$oGDg{ItOlcDqcbiAB660JF>JNA_k#1sB*MPQNkhn^fO( zgZ{P~b~BJeOWoh7h2KF~2usH}i-1=@_u8jFuH1H+r`BGl{piN;Y$)Zv+DtvLv2M9& zNHU!J#4Oe|QauKCw`UdaACJ8&bN& zuu2DnwE0ED8tqjVHsS_~MdD3KOp-dwSi4yb6$0(*Mp{%*!aTA8I!+|zdG^RqF*oz7 ze9`?=!F25j`Gk2ZpF&x2P`zcs-Bfk>pb_aD7ja>@ zVAoaWUPId{A#tFpBf8}2bt@B6RHNnuMW#%{WNw92V&cz*+JxQJ)rb8>6QS=Bvro9G z`&&}hq#eb>yUWndaSc1_tu_&W_lf7!Tc}3q+@c#f&BiH2_UP7iqwwySmNjrf(w(iP_{wN~Ds&ez9vwl9g zmTc^hWJnS}NVMBaK&Y4SdXcT}vfo8${{U?w99_X`RFCQvwLc*^ znzH^`R9o@>wMid;m1?8GZ@~)lIY{=!5!^ONh+N&c`ezkCjd%Xw(79`Vy^6K$tfxc4 zML|j;Y7_81r5Np{%<#UkxQZ=*GsSWDaZ+f*tMF2jf_>a`_qY3vBb;-{6&3a>ay@)X z;I|!NNKf!KrIWTdE<4@%+R+X3M6vvOuORmW?Oxf>b;c>~f@80zL0^dIe}BbZCkTDh z_;Z~tG@N(tD;1}(-D^^oss&>xT{GLXB`aLO>((`lC-nQLd)V21x1ZrBMdf}|E_)IA z8JmB45vRBYzodWakpAU9(%dWVO4(bG@D@QyPf>9~pK8GJkh(VtyyMb!8lcaoQ5T8% zD<2QBmWrdM>R0ulxQ24#+T-5COSlbo6GA@&{k{saY?)`=8||Z&(Bt>28;)(nv}-~a zgXAtbQB10Klt=!baU04Ex+Ic50z6CKzx;pkPGt$W#qEkyQ&#k^#CR{#Uc&zXkx>-rCm?fnYexKrN`wl915>)2jbZ>Pp`{fI@^jxr=RhrLyKN~s-m0ODu- zNHO@U-LJOp;&#gO-Hop?1EZT#7wmUxe&IM@Jha%?w|N?|^ao!rKg4PAOdmY`uq-zx zYcKpAeodOC(5FAjmCkOaT8^@1{{Z0!urm>S?Ee7Yt@WIu*Dl(QmFQG|aQ@PF8Wkq2opwuctwCp#*~|o|1&yzNH^6qh7@;_^aN3E_+kGu+@g(a$F{$ zgIk7Ht!k$TCSLk>kA6A%v6A;^_R9^!rKo$Hu^J($ z1r1g^e8hGPj?q+vDZ)@Ae&>?L6#gMIhNKmA4!+ z*OoW^O3<(A_vU1_mW33}X)^>7(EMO&AgXznBy~Vr%Bd^YO^b6a8mVh(h)tU~Xb}4b zavR9%JbOe;v2AjZx0n&4m3W>)t97Q6733J*b4rBL#;gk^2MJx)@6LV`bd`A5J6rk6CQ z(+lKaf%+SWO-&<$PBytICVEOx*@%NxBX4pJg$2)ecF~p5KX}E;T6xbq)Mi9ZQ_@gd zB&r(kAMR?^5b5i(-XzFfs-!_^9mu&`9!};e*jJTJIE73o5Ppzm(D6lGjz>i8QWH1g zyH-s_UCP`P%Gy;C6&aTpB5JI+s&idM8br&C%o0!rgQtW;U0Q853UUd=jrgPfE9E;D zPiq)>tZFK00B;~FTc9PuUsbzMuR3%2=c5F^S ziGZneA^@7lYjjg>mb6t*n`1W&Uh?#iP}^%H85wAP5eFkn3OWx_s(1LuC8Eh~&SSSQ z7PSHz3hOC+V2EX?P0Yhurw5Jf7JMr`N^guMR+{2HI)W##&LH`9oU>Y;omBq-6}^fZ zq3(1}XStp_#j;Fo6_!+?Z&M(ymDI{-N%oB6fMqUWl*cuCtv8?ATFnz>go3J?sfX!Z zev+XjwbLkq2$bVzoGgwaNcf_X@DrIbhFQ@X8iE(nNSeBIkA#6Dh{}ecDSZTq>!2SP zD%|RtyJw|KObDm?Xgj6l1PHOk(NV7fs$)5c_Qihf($}GgT%)LHt{i=}YGIXB-u+C$ z)_yaHCb%nav}l%?tL-l5N|Y*U1|Eaw2ur_}P5WXI{R5}wYfq|T$nJ`>5AzfwF|n;@6NOQCosj@2Y@9&&r(J}l02yu4B+h|VC2GQ>Y3Ql$ z?HrMnO)#W&&V)LpD2dPE5w}s)QCM;J8%jzljSVSN)8`f07(rJECEhBwB@HA2(tp_r zI)wERlVyNJzZr<>6;8Msgt5n}+Geq^mnAcyimxNH5=B*D#LupKlp0GU8$ztK1rqo^}a!YVN{Y?bm;a-Gakfa+J7**$>jT+EuQyOk*6r z)|D$r<5JZ>n2lF~l>1Mnv6i*8eyaHWSXR94#G;W?R^UN0@Qt^(^9|u;9Ns#{RCZlr z8gT1BF^@;u5E{JacM&c|3A!~QH3Jf&T@-sE)dPCD1Rp+z6Qviz+f+#HV}5TifSzcb zw_&cSJy}|S7bQ>Q3Oy8rahfE}O?yEqn)N;45<-!N8fiqCtFLcTSKHwTbio-7i;5%_ zZLQ=D(W%J6Pn^ zmxmihaV)FXtnq}1}?ErN@2-Z0K2Y6K6oSu~&>+V$LHD#>9oVTv;YTWA;MZK8Xg=a;s>V43d{5Vp-kFtsaEZft@AR0uFjXA6(CC_`5^%(WWzf<)Cq{V53Ip60;Nul)o$ z9mNV^_JJI+acMvlIXF5g!74i9$s5yGg=xEA7%i^_LQey#l@t{LTTb971VmJ9%%Mwa z7siAcr%fVe08XkJ>Xk zAw9M07UsjE&Zd@*p-F0+WQzQ~VZUW5lA_lwX$E^bM`nve<+a5Mj-1C%k#4NtppgJY z8#NsQ(D>!WUKMsY+iX_Ul??*P3Y2H^jaKDg@w6eX-w{a6rV}xz;S6jw)f+YGr2<0N zUXb$CrLJOnd?8ICMadhUQ3<)URDAs*!sLcP3JklNW#Jr8I@-MuPwHpJIaKhoD;3<3 z6h!Coj=0e!)?%nTmE9(-W9`YgEf8002ad7MiEXwB8LpJ|*R*t$+hElN{-y_0^n^Lo z3DIt|{;1#hp)Z&KzHOK*V7)d-8;9+NMZ$mR!ZK>?-%_v-&J=D}Bd`T*Mb!sTxkE0WEs1=pWSzkLU}0)&sw_=Z2CDr>LV{gzd1t4D5f&aXy($nCnAjk!ObZ8hp+`ay%sy<_xv4DoCyOnZX=I;BgQz>a}&&wkk zek+MXZp}i=uemJK8Um5Dc%Da;zLlv8buJU33h6kKmg*QAPKmpFNoVS$CJXK5%7V>B zbulRf5~T@zVu73HO=Uw>mlwuMja(F~$GGDh#+gFiX%j}e_dR1N$>(lvT}CYm(+>my z6GSrooLQ{Qsj5JP6eNMK(gwxw%lpUlG)lkZc9!F+V=lai{nDUQ(SCA{qNFBL5Tw#wS*ax8Die`~dxbLCs-BA#DpH2ZbR({v{;}3bn|w=V zOvLEi1yk#~U4%Ty>EePgny7Vor`vx8C@WcCkO4E-!Wu5Cv|KUJYoeN*Plsi<$|f7K z>4YujHqJ#4ADs~i?;R+}m-t9=bf0W+ea zPCRYJDng6?y=l8>H7X?JId#IpajCa1xuG)}pC($a7RD&rvC&l|pv`4V2 z=wiEb+?Zl%Y%HNx3oh7R+&CMlOMviHd&_satd{pzdbzx+yOmRla;l+5WlB+;=NVn~ z+vjJPsF}(s~Y8)-Pl5`UjltnSxsW*c}`C)fNH%x$~NNv>j>l1e5C`;y& zQl{m0X+cFQ-KLQsNTQ4_DF;_^-8mne2xMJ_iXc`f>ju8HDv@jwQncttcxFSQpSB4n zRGE{kMmwso+Z_;EU^n#X+7i%-km!$F5=<>dJnFG@Xmt9=7h^>=O^vED)&+2?kb(N7 zg~2i^(j%lk3e9KH7SoR@(isT(Rxb90Ikig(MQhNYl>KK0+b9f&ids^lV9ZFK@h2En zxlIZll;{8&n0A0_ncb?^{*93we8XyfEnJ)1vOk$F-Z5Lel+|7_?DU{b0U@V&4bG}9 z8ZwE?=wsxPjN5GV=nwHjO$4Nasy3sx)Ld(*s2D*m)i{>Wq1dax(mtoDHF-a?5pn1h z_b5W@44ceO-w4)@SD=PqvYu@w_Rf<>C(K@{e|Mt@7QDWKCO8g!+mJPjY(* z(yejp%{e@+Nm0v|EVtBB)CHkR0V*YOI%%Xx{V8bC4TqRZH5F~C+UufraQk4M}RAh9Db8hi5DT#>VF%tD5ARUo)HXoI_G^@;~$1-3^tCL9Vnwqqc;kLCGKXb$5jK&Of#I7umF ztZtw5=}OxZ9Y4^(vE z5(X~zIYQu~XNzgkGqu#t(NykAb^vq${C*K)iR6l*{lMWuom?sUs@jHILDc9+)50e# z!6j6(&1(TyS?pVip2%;u)x46lIHZsWP?4C8PXczuQWujusQ?{7;=g|9yK7fqKHPo1 z(ld_GsoEeU+EkIWYvmgBh2LcZMFPd~oK^A?MBgGxuh}*i;EjHkQ1L+FEeV=wocAvq!3oB6%H~jk?FPO z>;RNg-Hf4I$NPw&6}V+>C{jaY6wLJcN7Li>wMyA%80Q3U#FpxsO!k>8XrgifM0gqL z6xpo{W>?nV2ivQU!)sJ6>?JcBTTK2jWA=0kklD`ikQ~!+QZU=WN@uyX&&Tq^H*kJg zN8{xtV7c_Z-@>vRw8#n%EKB3MQJBqU3(l)^v5@;?@I&aXth|tw7>s?OD7fy3G)flB zE|Dd5>GFVNwACEhNJ=YJI$5h>NnVOj{`hknYqnGdN_xbl4kihwNk7B~o3V{j#sC>r zljLSQmh8Cz&^xwsAkGKOLFlOJj>672D7RE4R_F#(CVz}kp*xXtr1;(H${wMM6S$O!z_}g}~^Z zk?kQBT`=ZqTUhkO)(qw+q+4+pvqnV;Rh*5KPpv{@GEO&ARJF1_Gt?vx2-UG1qGr;o zH_CCCpIQXT-t3jOwJ0ehpmjg#-ZgAyU`!Tmk-`{Un`+mHe2u;^WBo-55Za~}wh%8GBdI1{NlGT}AGR{9c0#XJ)5;*O>rkL2<}&!mc*aYT zKDD*tKa>(j1l(@);aUydJWO)U=W=rUqOxop0cvPYWF&chk!or5g-X62v^L`LKy!Ch zLsB}2MfIOmZ69oTJ5yYlOd;hT*%qrq`AUg?Kld-vpZ@^3i|R%bct{fe0F-#o{{Weo zjLiQ4(YUFMPPsOP%bHtCR_W4ZNB71<8;0V&0W1Sz@RHj1rNg;8f#DrMqb4s(DfH@^ z73~kompZ59p{yHecRUUbjl>^Bv*q zoyEs$DVC6CQiUkZKM2>b9pAWxzRlBp$j_gG@^3x+LbJMiuCdNAP2smde-YxY$BTaI z`~%NcCiu_Xz9C7CJ9R)}r}~>Brq@uV`AI{{OptzZF{ttUjymPu?mlgjf0X|KZC9Lm zKMds9$9q{gJ;+B%8`OR&0nT4)J~HK7dbtDIW^lt*Zhy^DW523CkaAnWqpe_twtN96 z<|5^FpS8S*+vT8Zm~^cH2lOuS`@D^Wt)Cw=x3{3F4j+O3--?H}pK^ZmJ)2x;upVW` zZh3|=LZaJLw_&!mEhOqGYb0BE0nn9wW5FH${{Y5^59N(Fc!D9A>*?uV-WTYO*gR!g_0&0`BXe$4$L(eBy>1;OZ9QA4qWyy4pLCtN?nRf=O6Ilh9Uq||tyY)1Z*Pe9^>((@ z+HXJBu{?>@3oT-bO2m`DY|>;%2ef{H{kV6gj0!eXGC-=m>7bcWYSGIx1t4kF#T0Js0 z&TxkmGb5su+*ff+O)?9U={b(D!$AnEXH-#YX!v z2ei2Rtg|HCS~mw%;tucb{Y$BTxDOLiUpUA7?M6dYc$rCdpKttDoKQ)T>_LoEg65qZ z<%h+38@e1)IUy1^t5ga5cjJQj$o!7Z&BdEzMnZiRR8tqeQ;-lw(^ZpkEDzPa?WaX& zckVi>ru9~eUC2Q*ke-njjpQtG8mBWHDp>1AsO8A~Ib_SIDkyF?^r>#7gER^Bi>*Hl}b3JOptIv$Z4=zc0*2Ld%t z3e%6~v|`d^K{yVrXRn}UCDM<8* z{LP(8Ncco#*1Haf*sj48@lsOO~TPtt2GS}qCeSK8ifw?hS=(^&u$K=lA7Z6E4!4km88ia zbkYoaD1vKfp8OkeHQ<4=v{y$QWoakze zK{V%~GgSLTK2#E|9C6J8g+=3VxQBvduP=MXrZys!=UzM-NNU}fWRQ5~xiUa4@8;Y;>Y7Ld76Coqm#Xlq*WLkD2 z;y-B>V->Y7y1!APiIKEI?BXM;3nep1PPJ01)ktL2hS^JB2$Y%wu~s0Qd&!H)o2~sy zl%(iBC&npvk^0t`V{+Z7=N(;alO&$H!FF-!RUdzF4}x@CRd8$`sQ_s>n2ai{?4@{TNcx42;P=1iq14$ihOo;-?N2 zO$|;}RKwdOr1_)-`pdjU_$zZ@^wnVAX;hxau8`Pr`Y5$Z9DgpYcD1qQ6Hoy3`ou-} zs|ZpS{+B7a$Jd7)VFAr|q!Fia7{4X7AeEzGWzk4mRoP5{fU*HjpnPL8nhH9q=2}Qh z&nZ;8<8C^EDbHE>#?!zqw$K%q^Gk9y3MLGR+G^p|MJm_ftfO*q+|3q1-=2zJaYgZU z?6#FAK|pGL5tj1i3UpWAIQkVWF{+KOqDE#~2*<7g_ZEc_^sV``uBw66ZQPk1I>xKP z<)gx*a+qquR5iQa->0r|qP>P>q{zx&SoD3go{+rpeHaoIs+#!fj;@ktpz(=D$yFBk z0y=K3_tKEH8Gi{M>Vn!dOolHi1xm+}M1iRi{^RQC&ao9Uh$>><01_I$=T$T1P?m#3tPWpPYam5*cPiIw*iPk0sA(?K zi2Q-{l`Ufp9u({1>vhGeaDBuOl^_tJldhi;9!%~Q=g-l46O2sOLJ&NO*Bq~XB~zIp zLSGo9V`HM4zmmGB>&ULurkU?fa!8j+#;t&a)GE$F&7z5w)psKD${SLFBoahvaWs&s z?%7lbJ9jcqq@qtlBL=c31R0r#!kxRjfG5)@#ZoXxw|kPk=%|IJmhqy!C4Sf}S-Al% z)Lv`nDK8*TYNg}b_CvnI(iuEx2yW_y3ARs&)*Eb31#{kN9Md;4Qj&$Fp0I0SaYMb$ zN0f}*Qz0b*;Uf+PmX#kQtg2Mew^`-#C=c`yTy3dA%QFgE+ijKZgp@Y24^@%l^NE-( zih{hAp*YrDdg8Q$=#?J{MC340Rc@|@Q-w>7+!{vZv_Jq%>!cjWk3|zDD^<112hueK zGLFa)NSW$a!5{)YGBL(Dgf`F$C4Hynf4IRR)kt+w1!^AwW@bVVQiEx!LU89DP1&-k z{%HXnfs`qWlTfTnieB|!r_*se0Ut=WVyLPacM#PsMH*E^g$Cw)2U$hR5$V-u_hzn1 zYP)}#O4;;<=!U9I)-X~g6QJrLn}P~@3e`oPWI~O;ugSOot;TUg&uFwhFZ1fFUi9OL zdZYs-1uI_-Vuv90E!xsg%L%x+RMe8BsYLig5>+pCQ9>x|A375=(3nG5Q-*LB{{ZN< zVr1&CtaQNcsiSljmn~IN(9HK95J$G+6QXc}s@YPX;U}<$J(EOBXR7!5Tg6f!f?=1$ zN>G?DQ)sGGq}q>xlzbp@Oc_>39<5%gNdx46vI+WEAzgUZ^;xxuRy;sTRH^>}2nHP( z=_y(vMpiA~GA_7cb+n(E1twZd5MXE9P^p-%8bV7uYpZQa8ze_vAUU8Y*oZ;@0H)VY zU>?2^9$+;^d!Y($-&&I`vnubb5%k4!M}#>Rn%xNMdi)_@Oj91~e9)$?m6A+}pUOM* z>x+Li>WevImRD&Z>@99?`j1lZFzOATAZB@r% zEuQn7;BR2BzNxE1to7i}r+q;X>9r*(*G;O{Z1vOB_4&ub%?b4_Uw=ic?tl0wL#;qh zd+l34pw{^R0Ps=IryfZ^P|r2%Sp8zu5*wcH?}W=9-BT@`JED`^QG(T^Gf-)iK{G1a zdqEZ#EC!0Zm5J^ZQn{){wMdi{1eA?Lr6{wp^;JR&O;n)GeV~%^hAlvp*0UP?BY>dG zP->Z>CPEQAXcV+m2Sf>nm+34<5!{Pfn$c_A8syT5rnv+x%gjt<&_`26qP|+9f|dUO zhwy^_n|g2G3u2ett7-oL4194TjqWk zO4?=Jia(Y_b&Gy8f>71UwUXtu1T>~kBFt`8C1?(-a`$vHKe8`&9AY>;Dyeroj%fj@ zNLmbf_mVoG+iO^LKoUZxXWwp`&!OK7mOL1KWjNQkto)rXZfAq|3LAWqIyo z)*3Zl@f>B&0J%$h9}n2Ex>P#05~YryfCT>l2+gL-TwT6_T_drE2Gs?~FVr?R9d4gu z18I-|&ruUNju$oA)F`}%DvI1_qgGqU)$c9V*>t5O=_90T_&(U%L^lzt%W~bondXC{ zWUKDA)#_PVmZGz%pLxzT9Bv922c!i?yUj}>sVQ&AmUn6^ko8J;fM>6Hn45;&4+R-< zaOFO+%66&ksY+4;c7J3T*MOt8)Tq19$V-;$l?2F;qbdA85S!2)8g|B_RaCVs5q7CZ zr$0YPq1*r-v>4s#2}?srsi#lE98DCirluq&Vm@CbEh13ZkwE#L(uEnoPqq+K*MRX~ zA!lwdj7t!!yM>ed=G$0keAJ)>G|RolfK>x=G;#GHDcOR1AmxghcB(R(nk-6?$f<9> zg)Aih0J0)l&l7bwK|re4{KCTDtExg=-Tk$2o;6IivyMp>%OCL=r&<#-o}mvq=p*hNt-meK8V7JGId#cpQzrvCup*3=P-TCEcwSQ{*^Si#wl*VEz6Kb#?oF&Wu}N4NJO-9xO|NOA&xEbYWJG2t{ApYl-uh6 z0M5DP#_WoeE7hYz<%WQij))qbNpw~L#ckCeHL=GE3!GgRm4 zk{wdeMCK-y7@%L`-R=_7wER|1HZTVYsQX=O_Zxt${{SkVH@3CVv=rS+l0F2HIY1}s zVm@kprpj-DN((=^J2k1wdK~)0Ux`((%IlR>>q{Vy{uwR3W3uJg1tb(sw+chIMt3Lt z-YD-D)-E=+hp1~C3#F(=sajRuVksY>*Q|&kwO-h`$2`c{Ley|gQo{Ed=A?NYdIMn3FM07Qfg@qhS^=*KY ztPHrvYKv-9xhLTqs*sC9k#|HA48E`pz*c;DMa(0}e0xC68Yu}*tK|(u$Ib;kQv4Ev z){!wGsEIo&(^Ua}N?6nM5i0duo2oLFpD63XV1yo_GiaXT8gn#H+x1QL+x{>5c*S<} zM=QmF^dM~{On40?16HWiWYCn)gryv+NhsNwPGtQf;%FBDPQMSZ&Bu_j>UTid>P!NA zh~_@7m<3+ucMfHPh+S@bF5@;U?G!64?otrkv<7pQy(4tShQ|nA1>_d4o@4l{gtc(J zGnVuyvo^hzB)AzI5VyeH{%_meNT|Rq3FQ0jY^^>Mb#-dp=+7dNTcQUgIOOlFFAfjYo_PHEfl4XM~pD5%02@Q6sS&q@k z&YJi|XF7i^A8P8kI4Y%58#{h+oJ$s}!wU*+@`}o;N_P~M9Xbz@iCk5)UUg4S`X(vt zuCW&Z>MlceiPl|W=IbU1)1JX_s*rd!(aO43+*0=w(EKME=2F$Id7|t9fSwu@rPa1h zp!SOE$)Z$3r7x(G(@6?fmVF|q z64utmH6wy;uZu&0$!oizi*7b22ZaN4Q!jG2t@?$Q(>+h&8bzB<)moNd(wu%ZqHT+- zCGq77k@`v}>;(?0r<#Kprod8NaO@qmRN>sb#m`#@Y4OG@5=cyiwvm@x zK`jZZ_R1On7OS1%Epp#qVx)Z}t(5Db(ioY5g!QGvgMuo-@cMI^Q@Dir_YH%v^LL8% z&5KK_v}+ucf;xCBA;z)AiWYpN6(nu^A;w#pCS5IETBfU=G0LT3_kznrtI=>yUMYqy zEs2Puv6p}90_N*9#xFFP9oc@8_AT|0pbVus#&vUOcXVEYw8=2W5?M8ON*$$5E=kax zdUk>Sm*9+Lz&#9Zb%|<<*k?IAaZk3 zGNuk1;1q4{5=fNi8Wp58Xjx_4)o}<`V6+Mcc8SeBVVsN`nv28S7L^(6THjc`B_d`% zaZYW8Bv$_buH3}pVU|ZLYN^6Ol87LV`pPWWyjz^$0;2McQ5Cdh&MI|r-xJkb8(^h^ zqM$U9(ov@4c=LeOd8?3jRfXg+KQ3^+)|#Ck^6E~|H3AB}!w@u3xPIlXeD8I>w8~r68&=B8(Lg>(HZ8q{#kh6ZMPKXH`Kag-t(h%#Ey-CS(z}u90)cY8HK; zKC-5+NTQocwVs+popK-WH{cP2yW89nj!mAK+!{Irb1Oz%{w>$Oq` z<2X%&_0chX8nWyq0NQEE9a0JWB^Ib1R;rZD^%bu2Ju<25GJm!f{-J#f6|{Y@*DAY? z!KrgksEZ|`{FOt!AN#U?KH^)|hTVj}`PJ^7=7RxbjVv5gmMi}NyEHCdo!z}#d`#mR zkLD}U5)Vj0*1ss+sTt|x1+l6=MpbL|Kid89`WMbda1>>ww+2} zLb|fKl*i46MjEh|pMn5bb-0SVq@YU;w`2wpSZG$&4YL&THX?O!N zlGCa?C*p}k_Yco6AZU(n%xA@89Ivt%tJj%r$Fnb+dVZyQzTST9yanzW)}AZ(hk@I1 z%gxm(rd7_pw(6{PYgUs(uO#|Jp30ZPG<;4)v-<4jF~9t!dH(=E#iz!&w)PP$f9C-Y z_X+lR;|^Q3Imn*VGWuIZ&a{4_lNwW=wFA4sQ2jgTB}~qJH4#4kQYB%BBOOOd(?X*$ z{At^S*x8LnyJ|rnf$BeR;HbNqc+cE74qY1KZgscbY_8bfS+i5z%dXNoN0)so%bFIF zo|_NOGMtBRvDS0C>c%#J?xngd_m1~(BX+sQDvHibvEyApGJ@ zkMyW{VoI7Cr$3gUNfXmfut1a`-5QWMb@$R`)wNG*Gr9&N}KA2k(j(FmT?(@e5JyggQz16;Yg&hSkU! z`-jaty}-|;{{ZGzKe8=T%~k{O3If&&6Zn){#McLGJg#7=yebMo{!B$&Dr_hmN6iYwiQyE#oTVD~1(WOv**kw!_tctJF|P3M0c=b%;w2 zGZa&MjZ-LPO$wI-rgN#%o0YwF{Eo)%Ks##kyN_v`a>DOk(MJt18LWm_G>7?Vkswk8gYfzI_ z*uCQnu4^yj%bS$XHcE;aAf-v}m7}3g&I7o$@d^0k^G?nO%2u3(^1C|P!g&qAf2`w` z3j)n~ksFF8B-^L1kickZMY70ALb1bsR~LDfoYD4a}3JFB)n%-vFDp#1b4@73Y`+dI`=_fJO5Qwv_eG8c`nfsP^;4xj!}JmVPqFtZKXDSEVVG zQ*4=OKb31-^aw!JN3e(8V-r6y`ZYxh+3pXu5Ua|NzVCj>yuZZQEq5qkmx^9PwOXmo zPFau9(>UFA8bWt1ZK7)Rn?|!K)D2|;{Y{MSq;&`5;;m3)ayHQiO{n;IDgeE4-AyCz zG^U6MCTSu>51Eu9_LK;QUDPDaHYi%HL2)N)R5tj2$YL96IU?fbqKZ{u#j0v1+t>-2hsr=9uw4B0aXX@KgT) z)b^$J4~{UKqfZ8aP@dIGpdI7p+v+q-++J0xYEjUoc9gX#jf3|@!y*(_u;NONIZiV` z<#X%wQl%^SrQUxS0&8}Xr!6mJ4W&{-lO3RHqjDP_T_xA{Ls7ugiEmzNlrPgV-5QFK zxN9RgHb#xAIvE@vRUUHs>Si2sK!T8Es3)$mOUXtmwA@^^WlAE)^4d05mcyY+>9^zl zh8eYUKwn(Csm(@HO0=OQ%&2)sc#>m+f|bOSI*zRsMI|krPhE6}87Kt@I|^`VD9{>A z+w3Q&!VYVNDu-Y-Ni3SuiiXxoWuX8_k zgl7Dr+#OZh2_Y0xBGA{a8d;J;WIO8^{q$RER*7vUiCIy`Xv>)`r2%AUM_JA+yiWPi zy;eCbqhxNXH1Zb${{Td(Q|YHuB_x?^uDuLzb{Fn@7BPEd81$jy+cCBj0(u#Bik~JW z!A)Sz9HmA+@MYG?P##{LBQLvz?^+8b8&r+NIPE#zD~WA01P~97lI`Oc&LRs+t!E<- zWfK9++ydS@9<57T3w=~UA3m|@yllJJ0>d+QTO3M@RQT01H6EC7erXx_lPIyn3Z}|P zSAXos?9-d`<}qyKUQWqe<18ZTkma6*G~wHwV=5C4B`y$_(5|T_bB1p-P(!0dN-EO@ zb3Uii6#oFf{et*e+e^)39Ltc~ZplIPDk|8bs$ot`_|Ou91pEvIA1en!)N{Cl*echm z9`|MJyFT`*$PeK8%`z&f>Qbk%jHQ;AD6VvYTYW~#rzIz%kWA8Gx6H{Ky`?7JCr16Q zP?GD4QtPyCoW$kOeIg~*B~3BoXzJA}^xc(vP;3hXrVv7ZFim7=Wk^glub}0VGL2CNQphj%jUHuf=a(O$x6& zja@UhO2rZs->9UImr2HFjd_<)Z(ONWx6(!5bN~?Y^Y;7}A0H4AQg4&2l zW`CMB{{Syp(37>Wr1c6v>xF$f2+H(dS7)}ZIy8?j_evgRzMcsUl3LX#-pB3XXbEXQ zAMJ;JoL8bTlgJjKW1^px2~7M1{{Va|*=py#DmJIE(UOv+kDR6bkiTWW1ly#260+Hh zQY~mHZ6-!VU4LXd9C7%R3*(gQMP`1>y@2vJy9>Cc!|h48V-y5`m@_)06j0M^`2p&; z2qj+w7V#+*<9CaOQVWlHBdBE#Zq?7;P@Tlnuo!y3qixLqJBcA!W#- zd7%^~1$oD1g|!BubGw+(D#LZS+3RW5NmXri#w#fnjjKbgETKU_^dN~55)w?&)kMVu z&w_00Dj3vD%4Z|q856g~H0w8{nVr3Uvxl1_E7d$K zB=HEx7`L`7+i@t6-KWD?dfmdM3wLPhbW@v&+S^f1Hc2FsO#C#99x1H@s;K5IWA-QZb{{R%B@b>tSfqk37=#f_nmdaEF^ar#B zb29*?SJ2wLyFj9+FucIF)b0~hhzXe1DLLzgQ>xMU&5WCYqON#uJ-(30xo4P57SdOu z2-U4_$zFllIvuM1=x=j;3Uqsc4`&~Kq2xA(N|vtMNdCy>LhWQv&k}oj&iQLGvf(&3 zFH?}@m)BC&1-_lKqchb?GLljTrAI*$cAn8bq9~#A7gI#t0VQkE%1 zn|Kw_lm7swyE*E8HA-JOLfMwLAH+XeYw0KQD8arxyXrrtJgvPymSN$Qx2Mbyrp|iw zC|Wr7^(2#Od%yl!hR$N%pD;@}>w-EIEgX4!kU!Gi{{VYxpm{SE)cJz>wigG$p>6T~ z;3WS5=}v-tP`P+`L(I!s5mMlrb~@l5g%~r(H`fm2+8iUGk`#ZkK7Mx2pD;~}3CY!^ z88gP0_doo|@6ihW=wM~7JX{w|wEh60NaDPNpCzf&P*5M>+B&++NQ_5_-To-?)U3dbHsQ3pe zcu6+Z3H%KI0BD9L=C)1o3Fct#oH`WYV_w#>I?9$_tE4CiQ1We@$Ke%>pKvULPpM2} zxYf`#a8qvx;6z$2Ne)w%)|8Tz%UN{NC=quC7fbVWD$xEe{{U}LlbI(IUd=^6D{VJel7|K@ynt7D5>5j%K-CI zkgudXIjxcYAiMt6y7EPDaXw6-Cf29M1fQMRQ>aHl!|ptm%Wn(hF1sg{CYRPy(lcIN z%j_j*%oX2@@w>RBtu#wSdw8yK9Uyg5xRp~gr^N&K1j{AGd>)hP*(vN~qXg%ET8o}38D|l_|&qTDweXLhG zP?zp*9i)#b?IRV;hjgtP(i&8)uWzmkhT)==xUU9dc-sxQwwqdqP)?+X%d=g@HxAI~ zw0vI(5CCYLkoBtE$YPZU3P2!q*YS#kIk-!zO0xd|NO*36HrW%^^_$Rz?MG>8@{JP( zna47fAyKnfi?<(0DS*FW*DBW>DsYhD{IRIx`R+D03J@r=+ujdx;WqS1Oe^Elwe3agU0Nt^J+NPmV&8FQY9?M^uZ&V|d&`ZlT~*2+ z+}=~7JZsyn<&>ss+DCaEe{2Bm_gFOfrGMKyry;1|`-3G7t?-qeMJi&3T4y$pZr$44>PxySMed5S14GWn^UzlB_JUQPNe7{ zmWZNdUEqh>=Q@o|-XqHRpCwtIzhQNst+Tf3nQp8ggsh;*?VPlaqquB>t>}zpyO?O@ zy0YSSx(GUj37tns9z&ubj-%?TGndI@k@5wE=(jgZ$4H}I$w_*v^|W=#gEnqj~!wm{4mPNr8+?RsnjJZRQ*qgjC>PlWittCQ4OR0wDk||r!E;BqYs5&ip z{wra%*qF6j$LT1kHwp#`BsSSmAwNOVJeS;V3$W&*^{#pS6U>-L&x-$ z#btFV^A9kbrS*ttyXe|b>`qVKe0!6E&oBF>Z}&6NtdU);S4{boR6X#NS`n; z!y812&WPy=no02M4|piTby=wd4zq?{&=+DgMbD-|diX;{9S}&np#0GhbJbcg$hwsz zq>Zx?@Cr&+Evsu?!=ht^E;MM4?TMKreIUzXp>kR!r7|=hvL#$=MOu@p6dOJ;hdQaX zqNTCvopk~b?ue^yskyo*`R87-T)p{~(!=Ch8Enjc=olkOMlB=Fui*)BU1+K?YQVO(4QhtI;r#9PpW`U4UObmoZ(M2yHV^`gnH5W}A;pb%eqrGd z^E#nltk}?_-bfr!7q8B9F2dW(j3F=Or=Mxsjp*1roQ!TPi5TV>k z3QZ-wk+C#@HJFOdXnIlBeN%=hiqJfhBx|f^cVur?YjQ|P9G=AGMLV)VB#xhq6N40fPOR%w-@&`C*sK?wc7k0E{hPtV; z1FBIoo|?)cu02|>S8$8S{+uu-qSWw90VQ5wJiR9YvTCWDn?tz|a-}63XCi*E^N6ib z@j`3(A^dYvPF~VF>!I-0A!T>1UF{e|O3!hIRBiOrPhfZa#61}ZX`bq&w<%kY_9=Bs zhy^~9_5T36PLcJ*v|&f#qCW7dq)e*|q^`K!0j5cttK6>LVvQ`7d0OjqW{Ix2@jVq+ zsV0InJ4bKtjs}7k(A+#73TrPsHm1~~#-0Ogj4>sfq*<8S4HYu#M{Ys#d&(bS#C$aQz;{uSHAY&>cj%dL zJ$jwTQcj5;6M$p2+~qGl;f<9lT5#4n-9<$qaT5Yk(nft_OyhW=KuBIt?tdYX#DG=B z@TG>;OG!>$Z8Hf4Ng#gM(v7P_Y8RExMdo8wDqWzky-cQ*hUBp^pqcu_J;Y^C+Db{N zQQMI4du>BiPcYg^lBXd)qFrLImBlhv5LUgy^AkYknw0_bOmVwKYp$e81H7dzW6yk( zj|inYXuaRSxlo<}3DqZT_jVgn8!l~^S(u}JI0lqjVYxk0G=eT$%{{YD~47pC}LbpsEBGrX>4^{lFT-vD5M`u;W ztj#k)R)9`jdqws*!-y+7ypXo4b?}}#nnW^ofK+w-XBPVg?V`_evhFHTWVZd*&8as}AToplIua;)CZ ztEhcDjXvaTkse(#;^ZMOZXI@}C~j>3${5V93L{ zrC^+gj7w!94hbc9dsDP(XE?ZflvST z-puW#D@#kcFs7P$T>_Sp0Y3paj>^5p!N6)lHr36GD%TUD=SMX5$IQ5DDr=m_P4?22 zZWk&ACZPce)EOttKM2Aj$v!uqbZuUTwz;&s6oufs$w%6Yec6Snnwx9Pyn`jSw$zma z)50bp$-?1KLT+m}vqBJaMRU|5h1C(MAqp^e)WhOJ7}-4ZSqdAd9&2-x>~@`Ft-g!R z24@l7F5@-NAhh)fQ)gYOK_kPY7TmN=JDTbhUcSPn51qJO@Ir zw~apMKGXf);*{J;?>mXL1Q@&Gqk`c3mNM)9 zQdVc0&d+SmYbgPzgXyRKsgCy(_HGq6YMx2GM{{V^^zb$jN%+GD!ehO=h10x?}e`S19V)AjlGjfF&f26q$qAn1#5a(L{pr>Fe z9i;(82s)|1a1l_POA2IAR+*3R_JahqL@}BtQq}0n?npB1Slj+^3TQ_6Hu=GB2L`x>U~8)TY%`P{>YGJz@-u-u~$#_ zai#Pe_XQ)kHEBYWemhkZK4NO-Q_X0-x@N}7_!UpwlUUKt^HP&Frt4om+Wx71qT3<- zq4+9&y()hb`_g9)s!7bT)dh{os$`F%!H8GYPQ&J`Z<6OGWDF}Q{{R!iUrD#OgYaIpMg)>ybSGZ`YYCf=ao;NRDVDs?Z>~WP+_aA-0Tyrq;5_KC4SV&23#3 zq`Shaq9}pUVSd6bIfEv;&=W&&Zc9Tz>{kB(fgKFsS3(?z0nOD1$2mtN!Cqp+$oAFb zIN}r%)yyYtWhm$rh5rB)rP3pA;d44En@hIzDpFgXw_mMkyW=-Olv!LzDXB;wsborW zbpu^IVW9AT6({VCwF_DqK~Qmnufi0`RCA#u^h&E8;-=W>*x?c&k|E(JAt|wtq(y@% zqR42|Lv(EqCBe0O3!Fz!*I6{tYlZoZQz>C{AlN~B2X|9C#jc&XSu%dW9*oBpHT2ujDKxz z=a6g~DF)}a&NQuC7plsJja@JbQ_~YFNCr`nWj^FvueLN=EuUstKxFxn_O;9rykXlD z)a|+wl9VoEQPZHDQ{nI1pEz+=i92@2z*qv`_V>7PmkxWO$nNQu-!IdO3gg!%yLqLK2u(GYcvq*O9yZFFp9a5{m_S%0cD4zcSwVMNOZbiXF zj-3oe4)9ob6!-nMpT#XKe&6xxI&`+>a1s&+xQD;*rJ(X%J+735ip#ya*scpwo2{54 zTV>Es=O0Pl+h-1;DlXVa)ZUM5SkQ85d>M7^GXa0zXgms%{{XhhS!UpS6}*I@U=-=N z0i?br7S8s;T_~76vvE!-IRZgr*N{$W5K!h;214sctw`)b<~Q zpcy;V-vuKdY_v&scv{#Tn0`q;2Acg~Hutfsj_s{im#z@Cu8BxZPy`Pt)4$;XyS=~! zQ#)cS)kW-|+eNanrPr!1BqD$C!4e-0OhaAweXIXqV<4zOYL>uTYRXa?*Mr6}Frbp)xgS+^P3T))|+% z!rZ^Rx=0Z;xp_V>NzRPEK< zm*~?vCY7XVDMb$N?jFI8rB$Jwx{!*caz5IbBOtAJQjMJfM_=0!Q+>wSC!+0?wd$6* z*W2qSqZ`fBa*}+BDEh=EOTV@t}8*;;Hx`n8BkN|5vHHWABj<%P@pMC$!v- zL;C6}N|s)JonR28wBT7undoH~41eBTl$t%XRQsQ|xd*5kuDQ5-JH@W}PC0L)t7eV+ zT_rs;v>TG0o|9n!0j8R18z&m>#v?Ax?2vBqB~`OLGV;OOT+%>J>y`9Ppt!oW(%OGA z%F+k#i;MDht#0D+PsLY7q^>j#HC09TCgSW%-3}**VD`(R8@%FoHCDTNYDz6OwhJq0 zE57N8f#Dcea6aYia3seIgU^c5xUXs?$RBcHw0u=Tr|oNQaWj7{ed(E#AP34Y8;`xa zs?(V<@?;ofBCUQYkMb-{{S*^cwg<$YU4ZN?!VSe zQE@<>z)U0==s|&{`_`O|Sek!l?U~T0x0-#WBatkrvC-aFZ)Ir+sCgPFkH$0JP4{be zck;4|(S+^Iq#a6#)PHHrbS%2&SytlJ`HGC^`(mRH+_pid zQlYQ34;nO`%e%U*y#ZHPist5EJYAMb}h{3!FDXo1^YV!_X~FBR%k`H<@+DsaL3rOtRc zKF-n{{YOL{{Xglza%lAwa*AM`I{=++^_r0 zBz@552k|ez?WQudq8Q)XFN53BEF(_)$M%?e{{X_1IsRnd{j$`D}AHuEY+0}m+@JrtC)HZ@^J~^OZr6Ry zW92^G^Y*;>m6>xs+RYi?ZrYC}JE`+rUa20d7pir)DeexV`{UB~-O<6!&CQv#{6fp* z8EI+9>9EHz#ZX-bRbaxhRm#0#pM?!_AKq_5ULC^@!`M=y2BOd|wULOAdg0Wz` zeRp>8y}McQR9}<(L*iV+Bfe!Eg!fsglvSx3M@QVg zc>e$yN&f(}*w6B(mr zJ$?{WKXW#rC^@=iHPg|?lWZk$!+W|C&7r4tB&}c z&A`($H3UZfu7}pkv!72($kH<|XTN!m z^OUdl4Uy{AsqbzaB!qcv)TuiE016?W`}3DQ98pZ|#f5%UpQ-lk!pH;*Jv6>Z5Yzkl znd!Wz+1slMbV+x8yKu2m_gd+-?X@NfZ+~+De8o22-(&v($`E1g*M=E8KcmwD<0L~r z{ty|r^l?R=-(zX3LfqE^aPtcaZrx2WsYpshp_g~lI)D0wcm0KormYHNdpgLtciUGf zv6;U@(rN2y*GW}S+E7-?R%f_()4W`GC)_3dt=(?Pa~-0p@=n~@%?%-F4hr?`e(N{` zAfRf#i;}I4wxQ=`=hDj3PO>Rfh>uz0yR(G305m|$zl=Oe1$-7ob;K`WcHODJE$_XOX& z3Y%jYl@&b(DUUkixc)8*RvUH1>@K>a`P|#5fR8Top8jO+Hhi<^-aid2r@%WVGuWH0{X=Klcv;V zPxnCo0QVC!U*C>LMGj;b)iSpsmcShhlMfC-eDU)X-}H-cdIg{T zN3zk98*Q|a-^wHBWbV(2PMcVVf&)>^v@GreVF#|FILo`1y0s~%*~3nO6gkY6wJfJ< z7Nt+a`(nc$diA1##YO)BO@N+=x&B$UN?Q>l{4!7Pg^o#o{$jcP4i6-rt>t@pm`V~! zIwweZ+?M=*5nnq8ZRC=*Jf%vhkkSC@4Y!crk*O*3i?(lqE>A2n+S9ZkZJ(NBq()rc z-byG*ML};AeJH(inhwAcq{wyY5wY0KTGXRW>pT%XS1~PRL2hV!%uXa?w774HLQ6Yb zh+gExJfNko%%RYbWd=3ZK4matjG&6mDcmbsfFVQ@sfqr`B0Gzr{6#ili^*gDb7)J_ zknjgxA%9hK{$jWyhsgwYJb5$za33%GVQq#&Yf_vSEC)b{Ycuxs09 zp8CP$lCEZmTdV&7m@}4|!BN`LG;RWYD8N-c`RJ|wDLjt-9j=HBAEPYK7#yJhfNF`(rpn^1p zy{Y`lbV&$C-}PZ)L-iX$(Cef&>kEV8S46Q70E*w__Sd|)w3+IkqzwB@iuk9+%xy(V zH$gPwa=|z)4!69eo*1_1Be3R;3kg(3#0HKPZv^ z0B*VDRX*IST2-cdSFc^)j&0Xxl2WpT29|n_+UXyr-G#;oa1CkkUI6b+_>(r`6oY;G zf}oM9afat0 zlHd~E+8oKnJKWudfOSzet%a>{kW`+D4f z@%wXDt7B6v(K3LB(n*};m}WXOPz@80sWm#4lc?Mll^FsIhlC}}De9`a=SmY#$Tvi6 zJ61-%GfYylrjJ*QYpW1UU1hb0f0(pxQha*E1b{nQDOOdch_m!;p!rd-`~dm(gf-OB z5M5A-vP!3|)@LgknG~u4W^w^4{{UQhpY9io37H(=Y8LU?PC#RGp-ly5Z&LCBF3kMn z%nZVSSD@W~Q8M#4lnC_*JR^`Q2a@T$3NY6bB}eHC+fu(YtwKlDOzYzemv}222#wJ-e&GSV zic&s^bt*fZ3@d0%%H(|5k|)-Ep^z_Ii{Uy#8fhNxXB7ahfqeaJe4OzOM_m@9~Eyt$Gy$oZBJ3-)un&( zga+Q3B3gBnNMi3K4y9_m{{Y->ITT*7x(~oPuBz*5VQYO`RD|iN8jp#bXn6Z~$SOkf zCTAeX$=5`ir+BQ*7K>1`xDHIdR?986w*lOB{C<%gENXhzQlZx*I@_M4 z&*hxHuvM@{-1ST^sCS9!Go%Z%;p zDlN2OEuu*P%S|8=lM_2sSGPdEvr;ehTV>NpLa$slF2@n7a6hK0<_v+HrXn8N;5Etf z8KP{mDn+t~2Evj$>ERcQRP`X9iYDlRqKx@{ElZUrXjFkdCTGSpI2;Q>3MLnTO;Khs zNl?XGVZ|Lxfd2rtA89p1ez}@rAJvy zNR;;I6n(phI;5sIYHsbCS!hJhs`@06*F*J!tfSoOD9%@UVzY7hF1-DMa9O$I7KdJD zZ5G=oBo4bk@QKU2-(*pFL;bn+lb4-U#oV|i(u{1js$_3Vaw19a`9wg;lP6U1$`Dy-cYfC&$1=!r8vEwAa$+;kswq|F`+C&Mx*(Cb z&U$IAR-ZhKa`fYEVv(WCPx-Z>Ft@f7IVbz!56HX)*zk;T(Jc6LBcrV3N=kJ*H5n^t z*P-}COu0HaS>eGD#c|d$!fo{B9nwE{NI93vK1@dM1fS%3ieZb~Ql$;Pnx-`W015Gq zPkp;YRh}u#eN{f*(N4RKcmR2vQAyLrQ4>K`b8kdI?ITM}^pNn5dx?ZWTSV5R$y#(V z$wE3h9;oLZvb6lirV?e_tT|~R1q*W8gkf{VHYIKWA|^qUrKiFtStzQkOw(S8fy~-= z-i2@LKD}Y9xlIxH=G3Gek>LGOOy?zPKCs1-8+oRK24X{LKS3Km9u4-w z@$Wv3&XnkjMQ?$$h zB~3qA%5uHNfsZT;(ENX}*Di!Sr#{cgX7@;xxbY!r^B&rbAj^E?E&!*BRmTRB$kLq? zBgvFzp@&{=GXipmE=!CLWmWrvWn{OQH^D@gMQ7_CLrNoQB6|47K^_s(NJ8{g*CcCW z1sB%cF0^53^+?(}_=tngU}l79r0^V)IsHB;mX9vDZ`ij?wbaIb>E;ij(VE5<*F=`d zZtJN^y;W{c&Mr7hhY~7vRZOfv0+N;+l{*!MQyx>HJ-(5%W3C3!vdpv~k^-F^KXj*j zQf*Kq{%IbDQFpVPIjb|uTM*?YuSF`gf;`d#yg8885{Jit*@8x5xm_iqv=`Qt2G(W? zA6ZA|k9+acNk64~@3dF(i(m?v*!M8wl{R*1mg;wu$x|utk1?|sb|mOl-0SOiN7N>( zKE7J+{{Z`@;CFdC2@~+?9>MH{ca1)vXuRp&AEtLg6B%)~yyoqwI`o*2Tt(4&vPZNy zt6}?QZD!$G%bFB9sQC1YRlc^Qtg`F>0J+sjkAGEyp3PjavXILPEVwk(CvfS~AoH_2 z8z(q*Ry;O6mpK--{S_$Sp67DxQs8N-+&3F)SGCm@Oq7L7p_o5W7?qbgw~^l6({}}a z&-QNSC;Rp$sGiXgDFBLv(-yRfa~ECb8veb_HVM6Ymb6~ z$MgKgCSPv;BIXYimdN-W>myEA+L29>*^ikOw4fIPPxz6k5uImvW@^t;3xM$pXyaYS z!e3Aa2D`|nrRz>POtrA}Iv9PE{Km>;Qcq$CjP~yoVtn>9m*RH)R5D2 z{{RpP5{ToQxH?p+Pw6(kK?D>wJ*py|wGWbEzw0xOgr$rnzlaMD>Gl<97;Q=&wA1j0 zf2~~q0GOqJL$&_^C|eV3v9uHXmWR+n{{XMt`IW)Oto&LQhwb>yQ){NGp>Cz34AP*P zb?ONkL747elec(uP9UfZ&jrL?#$%a*!2A=v!`|V!kK0Yfo1YNp^(|T&Qh!y2*FImN zaiBpdY%G$4;6!?Jf_Lg?G((b)+-IMP@(*-=)gxj0oGiY~qrO4Y0^fDkH)?VH*;s&vogmRug(3k=QOv^Lp57RLY;~u zu{~J2#Y)5LX=(k6>BoDCVI*L(P~d#}s@Zj$o-uK$vt(A9tbX-wQAKs0ww>Bqrc^ZZ zwE9w^^$}itWsX{;;tMgM#)bisI!%CVU9!30a*$ci-hjys+fg@1!P>EcinEMbuMlyK-_$ElouO`qOIQ=RXRS;pJM50_a|l^ zo0l7g3sRdi4lOOAb!o3*ZKSDE0&3D`AbCy0tpAvXCpAJE*xl@v++JxQ5TMu}1Qj?ret@;M?jcA;lnlY&J!E@0I~ z%;S&IP}|n)#k@E_g=I7CJcM26)y!Y)6lIEJ5wT757{FHYf zNE12g1X!OFB^3&hkIP(HK91BXpv`#l{{V;AKS^|;Qz~slCD|Df<2Z<#Cp4`$7oLjY zwFgv>qkgJ^XDAoPf6?*Mfjp;w=w5;t>o30mT6XdW$9GFx#5K=wZnVDR8hiXvExbVruE zlIh$EZAAnTG7o`*H)x}zj)z3?Ub1(xUN^{a>dDo9u9d+|oe!K1((B?O+bz4$Spwr7 z3X3>j3!^l}7`fvvewQ$}dV;RhNR#2N!@!P^26GxD9XhA*mzy-`kFngk`)Zyskq0R% zsR>5MCJG2Lma*iH?Dq5BjJ<-3(;Nev29U>^p|{+E^GewbRk&%?bRd~X$Bi#>(nRg0 zdOka*y`Y4br^%`+RvU5RqE4n~zseOHBApYZ6<#kcw6!$kl#}^XtwcX+*R<(W70fJf z)Fj1bT=Ls(l|JLmwf_JOqO}w!f><;`P0@sHTB;A4dK3N7!$hmKVCu9xdttM+H98);o}XA>=7rI)^g*gF z!C`x9r{Xt5!LRtHobVDV?Q^XHZMdlpo@3a-EiNcs^hn2Vwvxo}Xpk8@w@27{)V!@h@`7|eJz?VrJyGz=Q!Q8heZo+h zrS2c?k_U%F3X5nur{4D;6vuK4Iqe%|`Z~w9r*W3$Yu~h7F%dxHs6kIOwd$E09#P5F zHEU<}FQq7$Q1cRHu7{*eTvMUhD!1hpg}nOT2yq+IR8{<4WMY-fapCrk z1FaP1685qHS4W%;?lm^tX5QUU1>LEf`se`EXd}a!Tn*3EIO=sNj-sZb~( z4znc167PfFN_RYxN}P`?Y&s*a{p$rKxq7u)uY_m0Y(+^UrB142}uks1ES zzOTwx-tD)V*gfVlf=}yEKkp_McxCwh6~gO6I<2SPb6_WJ$pU>-xj)|y#lrFfz@}a; zG&M%l-t&S=WpY9IL9p>0knkzFnuTB2|NF)7?k?{oxWw!G~%J-7yEO88g>?6S9LIzj zJ8yn`3TOJgznTE&x{Bv?MZlBb6p<1TcG~cbYLfibxC=v`=5H<{X|GUN(K`1rCm(C) zJ!+#Q&%_dXv)mo!$3oRsrMfgi=hN_u@xp9f5%6JJQ^?b`eO!rwF< ze8+JBC-r|XH%K}@1@J?=krGj3{Jn9SiK{A5)?r;BxO;NabqFbWA_ng-*SLfwb&Dch zCn=f5g4?%EZh$E3j!sr>^1Whqky5a;)kMl;=weiM@uR^A-bhjV9Kl|bxmAWzl**2R zBj)YPYoJBP$_so@m2+jmjT2Z%BTqMY8~)sb;GBq-z#sodNb$TxU0J{g8%^c5A zv*xN%mqV-({{U`X$sCzbW1t9lt)*e)TsylWU6Px z{{Vb-oOF3EyC_IRFXv8_$kh-P)@qzRA0IVLyD3CRSD7hOSL%C9tm0lg2f=*&s!lnU zI9wo9t6qc0SRiroWY_{ z58-_LW8=c##n54ZPy*y)F#=KuSKowh2;QACS7{NEyA!TzP-o* z%3n&x=M)6`PoRf}6MsOy{-Z^h%?ec^P)cL0leq~>F%5D$W7nRDio)i;MhQUb}_Eg_+TZF(acm(x;<6?dpo)g>8plo1~v03-~c z^F{C699mRnQbv$$SdmVMcVvDkHkDRfu2h!x=C>-HN;~u?SeM1|-*!+an7+$~L`_#h zKHP07vvFOLmu!jRKiR;IT(HlR4|wA)cj*DRHB&pC}CVL^q;N<`kvu zFCyBBsit>>{^8vfet(A-xYskO0b4j;Q|WJ7dIb?Fmd#5kS$ahDmroe-9Gph%J4UZe z#mQ~ysX$&01EX}D! zvo*+$fSqCHVDAa}3T)=>oBTl$)mvWk1gd`t#Dsi$^ZbP*@=JR435l;hRY$s{k5%A{ zQL0G4Igu?OXLVL6kqe-gG=XJOtU4n~M46A-2-~U>Vvm~>KsoXKZ~?IC|?2*wW*>?VnW=|C_IUw zpi4@0=sY4okwsdR8lhVUj3p(lqSEpiM#AfQLPj5s$2$r(a6ya6C+9Ng_G8zLoc2j@W^P9~ zfSqjDgRB1lq*S_DRFs~BUY%za$SmS>cC2b#&CC;TRW;1GHFb)162?tM%R5SQO z(@?!v+RoxOXu}+JQ4a4_Q%Sfuf|P=0uDV8lb7~i;F&U=3160K3SLLCX5o#FL!ZG&{0y8AwL*x?g+XHCH^|vl5b2d z>Q#R08sM9%j6us?T3js>fOXTTi>0;OUMivPAT4TnSxql>A{4l;c?oqWPX%25Rk zypDfGJA0IbM_Pr?uiRrVvtH;cXzAWUoJR0eCET9zrQ+?&E%jKO*NvYn(%yisa__eE zs-~epc?mm5h=?iT)~nR``H6+8uC1d`TY&>^!e%w`h+C#;tQK=?LYCYCX<9itiPa`( zkliw8J;X(O*!nvzGWw1KKmc9Me=d-tDtZ_+OAJFbr!FQ$>n{HQ7*sFSH#?{kzV-d8nspTcJ*UDZ zU@8Vts*2pLaA+mR5GC4Op{u!#5!mZvT@x>lUUpR{S!<|H97U!oX`Xr#3S4+SxJ!zTSrEjxSn1ON!vQ5r7~$&%m}HP7(P z3?u~h-e}cww=7C?2T`cjHVeCdb|vSld#_?@rY#tf*Kv6XXL5=B($D~60~?0hA*kxD zc`i^)VucapxN4Yb*L@QrKP;eLMTTjN&?!zW$&b1IkXBM zXj<@8-1g^wRbrY*RMXTB(=ni6&hD!wVW3tx%E#r9Yfm#9Au6l9Hu=+t~t#DZMgLsvS<>i26m2$PZ^lM3~m}6zqM&>Kslj zUQy*gr*M;|gJf1YXa4|ia=7s?{_Mrg)iq}49h#&&hcfmSh_BMuSS5N+4yf~5t)d`W^(qyMY*Su<0 zxh^BfnYK_OkgSsL6U{io_eSG1L%|^Ot{u$nbgo}9N|BaWP@UqUnS0ihb(unSispCi z&D(w3H;EhY>aDzU?rJAIMR?O2{v$;m-^^>M+*=h?>S@-Doo-_dg(KPiFJ>nSgKzahFM=BNSgSp5H+Cm+l!(CD|yRn z3IyZTYcEnP6Q~I$X`jHxtAOq`>?7N_{K)=2SC%`0_Tgc7S*#DUFNHdOrEb{AyMJi@ z^z1ZvU%Ff(p52$Mgj#ZBFZ5RmXGjU1HGMfs<^Iaa2e^$oD-3>7yRDtI{{ZDH&XZ*M z9~6^i`wWqPm{mtVabIgLO~)x5&pohY?DKvK^J;y(U2d~Yl2f1pt%wOBn!kq9g?tQD zulW425**S9{YzNm{oJ>?+U7hEM^6#`zenP!w&9jttap2sJ%6`VSnsxXRL$PcTEo)CN@2LOq-%*DB{$YO7p<8krD&L1Y8oP4yx>4q`M~#prEa*n zjxv0d{LJn(6_!dR{$&(&@X$tsZ7o_BGmwODOD}F^n#_CL#T(8krPT4w{VW;CrNnwm z1RsQ=y~u$w#a^3~{{RJUx4J+=V- zeG4i+R9DlYWEAu(PM#n{h9i(NPfVH=H!0x--x-Rx8sh7`?H32>3+RJ9)?2Uz4Kfz@eHIetF{+bH3xk2#MUzeF5ymIXx`=QRP) zco@>~yrr^eO$zhpF72D+tAMCbkC%P6x48AQs$>+R1p63ndm=Sy(NE)fLz+V+>Y?pjHB}3qo4}#Q z{{V+jM2}5i0Z?^U*sfb6YSUDCXUrbAsVu&)RfWhcqo^|<7$LH0suE}_4LYEC{{S3P z)mtjL8v$CVZx*!r)wC@rq%Zi10W#Jj@;Hd@S?LsVHx-|j#ng7KO1>B8c5+Ha?75?A z^-_?UTDKCUxR4B|HA`@c1LL~1f2?S=dn@ZZ=xWj`q`;zRq*PKkDk&Y+my$seJtruh zj_gP@Ps|9Ds-a)$oVVPa4a>Ij z6$f2Aa1~Lu^kFJilORUIHm=!6$MzV@8kz_ zO(>y*_UpBd;Z=&|bZVlnr7A#5!2pTMV4lW2z1*J82i90Z-S`)Bb@t^tq&?=vQ)sD8 zLr5l+nWaOh);za&$K`><5keOI21SxI5NeAqw5zzK2&WsCutI0!6Brx=t|kou0i4N* zqpy--^QB(#W?g;iMF!FNT-r#FLt*vg!%B3eLSwH}5Vu{M zm4ihiZ`_F9Q)hC{5l-n!%7o5<%74BgAKG~v)l5gum{Mx1k(!c{m6V>tv?-mfaqvlZ zH55drk9d{QyY(Tdj#XieHq`3?bBjCS{;KVc0kX{{Y01FE5XxF;)+Y1QdRaCPG4B zM^6QnH~#BoQe{}xira8y_GyxzNkeHG7=^yzk{HN7Dq|VS*d3~VtUQ-bY@}&`_&`7c zQxh5wjAHjavOeugR;d>*Z?-5=sO6NY(xFf$4xVZ01&-a*_)JlB3dGT6na>wb9^HnsC`oxu&5Mk&PUCwRnK^Wv#no@tAsP0wY@rkDJXH(@-U+zYe z)hlQ-)~aL!#LuDKroUKi+$=Y!P7X%n(JEWZ6sCf`QgqZzbr3M(F?6DI<|IW`8M=^3 zQ=VbkaV2fQJ;6{Ab8?X0sJs57AqpSOl#Gt2NSn9qyN5ucCFc#Q7}p?rsN3sIc9E2| ziFmth{{SkFe>NUy#jN$#z#ph5Z+XLVcE5LmSmyv9t6AiAB@QK3atFN34L@p$9TV~M zhL03X{{SfZ376`KkvjH(AKIFZh_TLFyXHi$@`9A5f6OacNX+X$oC@yOH~gwX9&Fz< zDw{NULOjW}8%;9HhKfs3k^L@>HcI@=`jD-Qp|#i3vKjNDfZzwW=np z%+|S$@;~o?t_*K(p?5|mCFV5oMM=(<96j>(XC1Y<9c;RMYv}Nb0n+niOt7s@#=oJkb9D{jmQ4l`=Wdr_6>k znxo1;RD1U^&-+pEPk+AK{$wa->72b@o#tW2cEI@{9nM91qY7Nv0b+Fn;j9)rUVqA( z{{VguG^wS{SP4JQ{{Y{N75@Ni5BXCkn={RC8MA-=QTt4GKH9tdsiVvp=Cz#Z69F&G zpNyb`+u!+63m_)+h%{2GdD9E zdZgW7A{8I~*fKpelpP;znNEr^{L@~jwanqyR1|9D0qMKLybwEQACgSm&D7AXFJ}!Q zB}%Fvi66MZQFh&x=%XLZ9p=5G&ReQc0;XVe*ZWLZZF^fA>8I>bc^={WEvYEQSzBon z{Y<5==8G+_XF>4&ii>yNC|YT|qAP!pXxG`iyTzz6-`FejEE(k*HyqlUgDobaK!py*xyR*BJMJO~{85X!^&*=E zFP|_nSky#rTe3dODnhd0d-oON_OzspXCt8s*kAY)IHF72Ry5L!PKTPk;jQyn$J!up1?-DU_2KgbKn*+LLb_Z8(b zCT1OX6NqUE)T9?0~N@w(z#Eph~vO$s?%Rl>PDd73A41 zLr%QP+i6;wxd->*r;fS2$@+O40*>T+!{%z zMXp(J@wZfATkWEboE_w#C#aD5KyT-!wDU%uA}Etpe&20ure(rXI-Zd9eAFEh2N&9r zRs1JtU?}>;EKXfNwj`kC^(Z@x1vO25>x=0r%DpGbXQ%CpeUBy*(xF%GafUF7Eqr9v zRY7ek-bt3}OTWf8t|^UW(?KJu%=rcDt^8<}w@wvPHuXHp2-+vNu@a4&9mAm_v{|j+ zDR?>+8nTR1<7}vL4WUISIuD##A>okIpipvhscY3XHPp98$^J^TpX`U3!=P_~Nq;Dt z%_W~rb80(~cO6VA+)@^e07S#dWOs_)M#W`Ai4r>qnt|=CqMe_L3uaV>aKqinGqg)O zD{>keopN0#9(2!MgDn^|TBbVNYbxYrQ2n-+?ugu$yy$rx<%}8=xfnH9zyzcK-ZGi6zvY{ z<2aHWN)II({y`xWsx5mUtOWl6F#gzB;;>SbOpNrqx}^2nE3ajxgt@s=>95bU3wgzD z!04}ko7$ZAVvYI3c(@+#z%kF1CG!SV20ay3x$a+yL^ zQgQo_>Kd5SNq=+{I4ll55bov@4;7Vv9F~(X*XHUa(j9jOw*x_{<9Up}Xal$+jWU2> z5|OFfu8_z30s<4Q=CUMP_)De9`Vs`_CEfu&zKXpOfA3dQs@jYJPHjGc8;^t*&I1RC zKuOJ{sw%r-Yo=jt7DyV(Qv7v-j{<}W(KOy|Bac)~#n_+gv&wMlCWe3- zi9-~?k7aK7I<7bK{cJMGl791J~i>6}4wrDIqhOW_!mR z659GKDyY+4Y`M^>y(=l6LGP(aKiLgV@L5D{v#B5|6sF9U8E|bgNhv47T19YL#1BBN ze^&a9Q#qC*p^C5NJhbXYz+?=*0t0=FULYgebtO8bZW(WSwY|k<~Y=m7ab zjJv*;w5oJ}qD>=kG9#~y5c@bQ@`NG0nD~{gR(56PYE$b3{VV`QJWc3O+#ZC_~9nMQ62M z5ZZ7cRVmPrdc$X_M?iH+JCkKY>k;<^QCjit)ni7~(sh7FibStpYDZY@YPi(YXl{9RLu=xKaxcS6d|n9=)JxU8NCLt{ajfW$b6kkjQ3RdAs$HZ~ave07A( z>Zc-Bwf5jNYOQ&P$O~$tL!s-hc(d@krZQc6)p^6cnD@P;_%6PEzgn#ev`|e5D_Rg| zr-b8mZQ&zD;heS@ZYo+{@q0C8q1MYt0Z1V!kk(=!x4$iS!BTQqc;3~P^yR=oOv^83G7c@l$R87n+>!*3b6C|o~i0Pm#D^SPY2>?y95KGA# z>R=ZQRMwkQuZ&Xe^pXU+MRPFi5w?lsp$T9DPv!0OjQa9Al2w}2fDzl5hUpuU)FdRnd0eG&S1f)iR(JyR6Sa(gS6&Ytn~5 zZOscc%Cdy5*Ncla4k4t-sF`)wQz)DORY5yO>YuJW!6|AjsktpF3rfP0y}G2!;TI?^ z17qB%vph`l)6rZnwEX8Pv(QpeTi@QASBLb0~hsolZuwMtqPwUwle2gVJxjX-4lLXvjt zlGjR~IV24SV-^_L098=xRW-bZ+Nx=s*F!&?;47vn+hZ>Lp8Cy|ph}caJi*`1kD>tJ{Be0s_l%mxe2# zkt0TKokz{#98Eoi&QD&TaN~B|-`I@TJ<-)(~ZzL;d z)-mkdHG1AKMv(-C!;!M}Lg^Di7J{MQLlGA7dip6lsVdql@%H_iTWN7hCY*HuCA5vC z$n^L{;>?Yzk#=ZGQgRM?YyC6HOX*UU2-=l?%_3+>D5JKQsR8!xtyRM;zdgE0Q@&39 zH187lsVfmwTe$Y$Q9Ivpo$cZ^Vt!??;!l7Z35vHe1lT!TILZG2-PpOaZGX;m1N>%j z<#$D?E7nezK#!>EHh`Xz>jO<1>`$s#(AAWsGt~FiA*PKR1j$I#sCRFW5@b_8p!^^m z&u2v-Lu3?is&a8G&nm?$Y3lL&~t1OEHppZJh6rBBX%8bcMyC= z!JkYD3|x?fqOoC9=$_EyTTcX^s7GyGwn-$DKJfT~cit$ntWy;xD1gv z7Mc|el(K^9g_1Nmr_D?L0{7w0JPC57{*RD3d&Zb>IJ|PXYI(6K2{y8KB7PksQ^0ox z?2p;G8`^w)SCqNu+nh2>^*H10NHw77RirDOJ&^sz?G0l*^@_8J@>8uPt7cZgO|th# zQbeauT7frHKjB=`MubJWeluZ^k9DhsPk~s}e3y`M+ntx}I|oM&in+bVeXc#g@Nz11 zek0${+&;n!&faaI2LAwVXlpv#ds8b&5}S-q{lOVtZ;M@7%6APHrg&F$Z8B{DAKU%E zw}OCntLy1n((&517N*}pnoDjrt*T0a0HsMHL>)v|qudP&))|nFM$`~jRv-I6PKus7 z_iI@({{RAkBfOsbFD7aOU(KiOg=1qsP}Loqlkyy=U2>m$AER-=NK#}} zM23kSBWAOAq%Sk&jUH0l+pW@?)UI!R_C;GiFBq~SXWhf7mGNv*-)Q1^x z_GYh5s=87BSIqr4?T&w!?n**`|wBFl2!`>82wABkL!}2Pn37MTgOzPFp>8a>q9t`Epi?+CKYPrdc$7dT+=#cB2bDj%My* zs6w4-4H$8(@>n62FThtXqv32k16Xy>TSslyVSGL6Yw!^#Bz5t(*BDt zeW%%CM|i#tDmB|QLdThNe6HuWEppW9}Adh zRlgmTWbCLZ+2c0q9ipwLs7ZF7sizxpv9DD8V9Oy%0&e-k+bYm_AKNbzd#?78X|O*` z-aW}2Z0&6wn=fIcBB7TO;+;Tvre33K21*B~Xolt&E{fM7fss#0GKH) zWXQ40%`z%!s*tAE0c?^2Pk96564nWUxsF!&3{{SDkYoi0jU^(j-ZzWd;KX6@kBUe#zwo@&1Yf%6YK<~dY6FMDY z$uv&t6%dd*^-N_iZ|sJ+gmHSG*3kI@7^SB zt@YJM&2yzw;YI>&#%d$3gH0t947C(cPrN&m6_mPDTDdHgnMif(q~HlHKs^!6yCny( z)HaHw0+OPi9=;LtZCaHj(E&L%*FuYGgGrDHl5~frw`gxf&8^Yj)kMs~rBBu^N>TG2 z0PXZKRG!Ea(O#|Zy%ef4!rlZW3Q-a|Xa=$KtXe`t?se*wS%drN_9T(z)RC`aOyFMwzcIc3H%mLvH z{U|;O$6h$}NNke5N^NUKglH5yLvrIA;t?_AoYxIhc)R6Q>{P9=^1P(&8u-OZ`;V}? zv{zdUJYUaD}qiSCe*JGZbaqFWLtq4-579f+%y zuJLPv%I!;Mx6>ni|tBYGf`Cf>nO1^0~>r5Yx>lN zx}lBUY7g-$XSB?q*f8;*1Ou|N*QrK7*556OsDao_#Gt~mo(YF#Wb;`609ku!^LTfT zjB9+8?#Vu#R=2MX>UW;g)WJg<-z3xWPeDJdIzNd1CKdfPzDRatWb;B9yS`4~qb|l| zLK%)}@J|cF;!yVQ6+=*?sOm{FhQ>Gp(KjYU)2cP5xKyo3^QcZhonXe|^yq z$+=XwAYDRqC_@wbL`#S;rZPVCuSZ5_zC5->SeHo>uO}e4*dy z>|J(E=&j6(q$H13NhhS^ztR{y6JOMxd{I031z1=G;%D9*j};gl6F*+}4yd7b+Oog+ zr|IJk{UZm0=(=?l)ZZ%BpaZIubLpeu)Jeq#1#%N?JzB>J%1e$w9P~5pq`3#mvr$#8@kp7esu|T*Q5vRwyH&wI*7`e>sXZl zR!`PB>^6z>A)-I0y48Rasqr(MbX%ccA*wlU>h8iyh&~#@Ucz;%483okmPpZH7xY;OIjfF zn?#BEcY_%K@=RD2NDgc@8J>s>W%fVv_d(9N?7RpQMHnmSa9rnBWa?Mr7i+hRy zs1ZFS9wzQbcYW8EM(r1+xPNbbrUKxn{wKy(+XD>Fl_zIu0n_ImQq6KV%<@TFzB3fa zngtV(?NuGQl{C^4kuIM|sXpP)Dp}FfqHFRC@(5IbD*hwWPO&Lz8=g{L@Dm-8WGYwk z(5=6kdi^5VkH`6_%bSlR(A@72DJxo*WS+A>AB0=%F;yx8?&5(nnMOdGaH+gJp!e~O zBL#&fnksfvA^=IeD>|lf@ZA`EDZq6T>L=hM)0`U~V`!*YM>4r=if55aQ)NQXLZ*!j zy6HagyW`tMj@|W9rz*!OYM*!zkW^1@{{T3V1w@*Z+g4!oO9~~RRo-&f-f2T9+oe>diHow-lfOM94@Hl5-3kmk-mT*7(k1I9ab$&tEfz zHN7plF{so1QB}qE?jUOLr7|4N$K_3R!z-?=Dgk5{1NmDr+1i@;Sz_Od0W=0 zp1opimZ7~AJFZ-MuSE4E=&q3KW1{GitJ6r}PT&f@kn?pzx#S>DGT>INnfHg-%@RTN zBz6A)cch^yNE!IfQ5leJ0<+?zsiJ4A{{S&F(tV<+_G+$;c-nzdRjG=WmXjcp<}t{h zM^!O%5PN8!{2MRWw$QgkguJB0&8+oR9IcHCmH3WE+o@?Oa%DhEroJ&@W2yqC-9#Ir zky!OoTu2j@-dlq^OQ zW6K_GZTGsQuwR4+S{ zFNabb&MFLnAtz}cGGU8Iz;Z6-=8EVsH3qSJOLLhGCS$^4FSzmO3XOfVJd|Me_dJcZ zTv_OpiIez5+gYlBxsjy)sx&Z z+IoMyxB^~X2=G#^;d=qDg%-L48T;enS} zH9+4$k&x{Sz51z0Qc~jHnrW`F4>%;NHaxA=4NWuYKG2Q2DN3(uSTj0l2~Nh1?Nt6$ zp5G`o-9qFVmGteMy&#h11p0-&O-RmD0v~Tmq-{YRM1de_`(dd_0TF7zT#>g1fqUX7 zFUBiZPX)^Rk8K!&w%UQ8Qvs0VTSka#8JENN!#k60X(bG81QGk86<-|+91uDr9bw5R zLg=Mbs`jPH2;;2ngH=)JQP&U-r$tZU3&h>~GS|sb`-R7a<*tKO)wi~)S7xlto>d>b)n$8Aydx zdATW1ZwX2B{%s<^Al$Tj8AE(iF_zf_$#AI?Dw#^9+{9hwxGM94%WX7FO zDpGYl%vvy3(brWQlDKn469mq2UfpSIEoo6!qGwHF-8MQZR)`ix_Knq2PYJtJT&a?! zDPI6*A|)A)a>=p2flpXYUK07lBtC=JNP=@{rW)<2s{4LjvXreS%7Hob?KqgjVnn4i zb$hV_!+A!L=l=jqxAu`aO#LJ5ryO)q48tD&$=xf-Ywsx*q z{i43EoH^^EwH?Ws8-&iW*(UZQUQNojY`#Us-72l8BKFN|{{St#*riD!voW)@zZipu zJblGEcfkuGsPJ6O{lK`97*+0~sfu4k0csOUWv@>0Znc)8)jqQVBJST+Ic;u3lCOlHK;&vE56lL?5V(tP&Zi|G zl&#{5sV0%6lOPZbz7aU#=?c5Hr8T^-RM5q4CwN5LQP<@Sq%-CLQ)SBH(kgDN-?#aS zGtQXk^Hn0+VcZ(Tj7C0h)sTUEP5@CGf|JDsusvQBf4FN}~iQyPl36?X8D zx#`w@OrubR`R8pYs{K+65!ZUt*$4roy0KE&^erVfyL~lJ% zuWZy87p~;gw4j!idLZaO7nqY(4VnjoCY^z9CEHwKb z3ayS{Y3p5kk2j)8AdPxPG0AMpUcFPpaxIn+E2@f@i{(f|i1l=tkTDJ$>D62I>YD23 zO-sNl&A5rq0CZI-=v_dkx3$_J(Wl&wy7Y^-A)8vWzDF@KDQsG!xU~;~6$v=UYA(`C%dOmwvfwW8lK$KjqBlN09LSI@^-szn|Ql4lsx%kNs+YKICjU{!FZM8)A7|6pmt5d3403hYPM?B9-~Enp=Pw8DKG`{1 zlJRtKDZ$2h3Wd7xkK!$~=>RxKKuFidHTygU12)fdA26@^Nmwl1$>p48PiHr5?X~&9 z(Ek8(fjpVQ+@r%cXDu9&gWRziq@U%Q<_Z~cGujJpx4ET)dPz}R$Z-q1Hyy&G%q@3} z_fr*0un zJ~G?~LLtnr3W8Sqmb7zEhlo*lK6`(Uli0|D!_#o_{p!xX(%#T{Biar>?ynrp?-Utj zhb&^AuL{ep#l*K)OHV}ON)E8#C6^{a+qpx)#vRS9%no2W5P!vb^RihpcqVm02csRjqQTJzP4RF;Z3l-@-Qrfp(zBCuSo--i*9hx$6psJ<_<6Pa~~$E$E2froedBRO8{#x z2(ix?Sml$*Xih#mnA|PR{{T+7QZ;$BEwv3{qkTBq%R_Dic;Vor0RD|wOTR_UCr0XXUGDl+*82(HN_U9CLpPH{` zxO*h5mZqTic_(MvH@+@U;A*BQEq5heu_l}XY0O^iEx9vIh21df(w3{3J;+HiaTDmIOhzr2 z#JB&*caO9y zu8ZTL@<*I0&Y4FL(&e^GCBfPh*IZvOB@5aYGc97ld5ezdG`MO4rCD+tiy5588Uj%M zO33k^c&BcQl4uiib)v7(-G-HP%2u;&#OtSlh->aQ1hH%?+lu9P5lij@qqVz^GF~F6 ztF3;e%-4#XB)fE`+^XAPR*74!r>axjh{~nJ&yKp>MPpk{j$YR(Yf92Y4~%Yf2! zW`^Y5%8ui0wA9nrAwZ?j3yA3xF#r#q(H9NsgpT(g%l~(IT${3Q)jAa z#$PED5)(4g84s#@I)x{$BOB#w8Kb*4*ti7$03x6PyLQ+zDmzCcToGE1p+MeH;`@#- zRO1zFs;Y%XboG*$mtis5DBOdIMDJThs?mbvlD3R-(J&PB)mA#xRZ=*lq$|#mGd*Vz zwyk}+{1ol%&6ia1G>blWW;Uy{Npa@^0V)8Vl8b$f@R77~s^3_E>V;hV_3iS%60fMc z*_bAmRV67&pN^3uaRYQAMM7WDrl$kGzv~NQ#Daj3LWvSRHJmiogVk}e?J3B}ENHnf z;#p`o;)h{BqziXzRC_OI5?3Vl*UZ+N137(v7hdSGD#Rtqe1TTlp-fv)(R=ntB=#pr z07edo=HzAZ28oSt60D-DY^}8+GnpiGGw%-Dz(V7$1A=iFrC6kqo}^pn}KFEP-su7bA z?G$ys;iz(@$2}>MdY_4k?3TK#9kxKx3C8gE7u3N@PYNOUmQa%!hYse|8w%A@) zfgw|dhSs4YBPov25v^3iWHd%CVQnOXx6Gq5V}XS{3R_t*mRWhVk1Dzo0AIA0(LHCO zQa=}~7WPVf_eMR+@qQ~)<9C~=3Y=VL#cuK~r8OHAw5>Bx1E`dv?hR(wyb`|V z6P7f9qsO^VXpY!zj^600)`g%ItE{xwSoGb5Ep#;UUTMB-n>|m8i+p!xUA6WbaXVDn zrKef_d&WJ=*f$Whjxg^?Q@e_3k#oJ;?MfTPQqqNK+?fyG8H`bHoGa%2v683mU+pHE z=ZHOUMKLS%%7GG`if0@D03O7M;ss+og6zoSRZHB1$Mrb2RaGJyDQcxOKa=1uI3$mMwQ$D*DEdlaayhe2Ik7vh0c~z5Syjd zjl*jXI)Fg#m6X63TVVDFd80WL-rI{pYOZ+ds|Ty0>nPB1aO_#d^T`^d$irMwn2;x@ z!X|?OP$kp|LwK9X08VoF!?6I8`4;tJ?#u)Uf%%L&4HHG{@A9*|zc!vfNF)W%aIH(m zotfNbM@@g#9jZZgt60VyBUMQM0EbBVd=^Q@9-2s+Qa^{rI?#}1OMG9A@7X1NHL}HQ zcJFelS`ke^!WQd}II|#>gaS#K=@O7I0aqw{o6)+B)T;-^KGVMWZ=#xDKib69AiUnB6od&PI!+*6RTYTQF3x0zO2 zD_K)6qv9vj9>i#1wzZCJMxi!$HPm*6GgWjhwzDNnejOo$6%A3r+NAwL97!QZr+8Dv z6K^#rlA#VBouiv*y1}9a`g(#@-eatGnkRzPElY7gfM$Ov?F>%uN_DZ4kLc{-wI^`U zb%q=@Pqr!}-IVn05wuKn(i75qZxtp~j3A(__=zSa_{K(f+v-&= zXMnZ3oAScuuIp7tTH$d>DpQ2cTK@RDJCIRSVc>xxu2!07$WNpqchiIXx2*Tyqx+;?cUhCYo;n=UTxYlulq)I`F2NI`bYC#sCOz6{k`x3=H2zq-$I zSk(?`YsaYYEH2~%?U&2z%BFI$v;P2?#X17qQV(Si0qp|Im9>P?tNj!=074NXdyQBmSBacI}G{{ zgjMBb3b$L_Xp_pbNn%LQo}zFIpd_iL-GC9rC7Hb@-1PdwnQoG9(vF_YohG06pn(uf z8?v#Ql&QAj&81^OEi~}_Vd&VIhU`v$r?>0@z=FF57meu(}heA)% z3cAX{2AxWYw87rr8jnj=pWHsle%@7$J56N0U1_pr+ajLo`>0!LcW?1F1h$h9zwSPw zip0(gSOMHgRl0qnTlkwNx?}fRbiHx3JX&?#papF@5ib#}etdW5x)%<;bPY5Cdc(F{ z9vJP!gle3&Z1h&965A|Q}FE(dpDG9 za%~B;yk1o|KbaDCwgjGux7~o6d*#@xcbrXMnEWEmjKz|nZ!ajBTkYzepVTfaEE&mD z{C{+4_^v$X(t%a3GGVo;QS&e4IyxrZ+VD()=uG>|NY;4w2$AGysrhb9)(TZa+{2S9 zYw8zGM728MBuEb!-0+-DkhE^1%yN8z(j5v~@J=syt-Ye_aVt_BAf*OifjPzZ8zeEE zp;B^!Nd-!@szp)L*;-{w9!AFEPgw}RSo8}$yo`fH$6xC0$#u=If~1m9nsF$}8lucj z3B9Pf(t@Ogg-(4{sD%ld65C{GkvCeZS3>tXk=-Ibl3-e#jv~at=j<@5S5(^Pq4Q25 zrC@p0O6(OPTTaL@q|_moc37IdD!DB~MKoO9}qsc76#0VtgN!jlpu zO%6R!@zo|;X(%E#ml^=RFAZ%%P;uvFW2q|(RbS?|qd>QtaU>MZQ_%hqOUuVXL0WGfoH?}~DS)^) zB`Gr63G3D=)3sfqjVGN29Sn!Y29HJcN*`^zJ3{gmxTFK)2Sp_c0`eWfNsc_t!klS^ zIW-ArI>nOK=7Cdk7~)M-*2Ayi*4bC7GCJ$@luN~U*DB4%J;6IFt3#}#%{|&qVD%9s z+oq945%iC3bVjVnG)q^2Kk1|!8PNrC6-q?hYfzOa6;4o~@PRBfN!8p67NhtobC7~V z(hPc}232>H4i&F%oqS+BiF8jy+9JLJdamuriM3kk9JySpR1r@=!W8SvGyW7H69rg8 z=}v(sDLW%&Tvtd~f494yE5|6$`J(Q}6ya97WUi*7y=hHTfFvtw8=3PS!>nFn_Mnck z6`tH=U2!JZg;id2*T3v?&sTKBy}HW$bwd@Jx$jgs#2++dX+B7 zKGF#dtNrBkKZGV39>k}%Hy?^|oX)OvlnSe*u$%hW6MB)QhMptD1!!ZkTpIL9hS{$1 zqH8L>++a3prBLNK#qSz@v;4xkLiz#uvAP(3Nr2FVbXYkZt4Jwxk9(rvd$mJ5bCxJp zt-eaMIZgEbYDTbYG7!?9Xn8Of19zh+207liV1H zxYfjkD`>(SL)Ar$(VewZ+GLdtVmeJoQBAsNJY}<`$Ew)jt>uDPif+ zXF}SxpjKtRyByC#+U(%?jCZ6V^1P#ar^DPYoiCnrWd|OnC{)O)yesQbbYBRhBnU zCi670nyN>nlA=bRgh}GBLmn!D$V0ihD2Z2&*0Fa0gS4w&zA>Y|<&K1v9R}C`Uqqdsga;o)YZG0axTR64q=GavF@EDP(ZHze zqk@Re-YYEEuThuE3RS2C%i|Zjn`XFBggvmVbsbd4<4mTa;Y`ZhROSE)pLs@Smd3S? zDcC0@Q9nz0MUBLEnLh}QvuJ5U%Iy<7m6`4%=3__4Jo{?9JAFyDwN`$X1Hssn(h$7Y#J}c@~ImI(|;S z;syqX=T1s%9TmP?8ei#Y(h_H}JvHqU!04-QX{u~Fdf!y`Gjc;VQ# zQKqVzGOS+w&-Ev1!BTgufIKF3fo8FIZi;^2DHT9ayD6l(w{dh;Hg^Rb#Q4Q-?T)Ui zM8wR;H$+EAPE_hiOpNs&FdCDU{*@yzN<}ssRJwut=N4NEG*wVCa&B^csa6_blOg-! zcBg8s$lX(MnO&P2C1Q5>a-~Q z?<}6|dfimF$n!?66st$7CXhzbuK^Nr&KN47Fm`U5N>MkMUm+&G^t6ZM*j$2V-f%6A z@Ozb~C5dz_8Yy{7__$D@6T5nUWJ_E|)CI94RTTNN2>op(Q;2RxN+CdY)Ws`5j1FyR zwk{K$5y$~5ud-@diVE6>9ZBAW5i{IR;~7NJmbiuJTRFt61Sb5vqf*@bNAHdTjgf^| z=eCN3r8N7sB2p$USf=_xS-yP!Wfb|zswHA|-K0&vzZpc$%c~hs)U$1eG;Vq0AvjVypdN?kgW@f=9^PfJCaZ(`{MUu@`Xmo zeCbD!t+kb&n|^Rq?W#-!g=9Z%RRv@(B_+K=^C;$}`FB zB#qQ?AgwmjD;%u_q#C1YOMpp83!l~jlH65IMDXESrG>iY(J2#={{RfcLtRaA(|9@MPa^MDrs&ux|eBcXx1F3sA+3$#4RK! z5TuYn1ZYH9anoE|5Limj`5mpL^ESubM^NgS!jE%!~{zJLBQ zqZ#EY7V`w2vOXybj#RA~6{*J5QlRoSOQj5cnQ3;#6W1Q=J4g}tBjkfUCN9;cmTiEy|B+x?1@ADygUSe9e~_q)Vv_ z2dZYBL^aDTfiVH*r#PMA`gw`}04Y(k56$?W+)cKIxfC?qW1rU3wfgH3bQ^U|K}edI zaX~vukMOD{FYP3R`Gub0@>X|@{{SnIE>Xqz%v!Bf)$|Kvz>(!rdW9r`A`igCb7lmo zc`=WORS;9sziuP}xDz!dM?=;*BO2hCiskXSJ2s9~zW$A?HbTNOJ6m7|QBnT@yDTPB z9+T241K?&LGj3xwYMB0@WoFl<{wteQ*zS}!+&1-5XSz@;)TJm{Y4+3)K&1X*@bBD1 zHx{wU)M~!6zLE!3hea!Cv;HgRKSq<4QaI0%ta@4togyVEo~|Yvj^g6c z)f%)bB#*PnHj)jlMb}$rEqi~x+Ah~e7fnnS+qjP)+G>=#3ZPEpDVnA~DvwfyNcJKv zHrabi=IwA5R9v18{{R&yxd9|~(OdVm&$@EtAO!*4 zX!(&8PDR_(LnIwMmaV}%+^#?q`bacs^F^mNyeuZM!w= z(^RKo(4;7CnpBB{8-5Ea>xhAHp*}bg6;D!?(^N7@aSH_|eLKN2FjAp4X>>uM zP7X7j*IHhXsoKNI*y~4BEhnl-IuL#_Uca_%t=dIZqZfYoU%94;hUv$)5A!WYBXP3~ zt!L* zg-mHal80k;bxv(GQax4njMmfo3%f|DONrVRKq>@{I>Dk^=!ASGwee8M=1W)RYjhy6*S5%?C?7EoT3@6bWf*6Dje4~ zqg3r8s?o?ccP;le(5K3J5j|zpM7$Ru^*~Pzbqc$=Q;4dtyot+|0|hjyUgA;@bMWmB z?DZfehf1fz+ARh>lY5Nd+YVfmuETi9ZOmPmbPr8vQ?m$IR+3VHA6RcJ9AT?^udtCd z*G+4>F63UaS)aD5&i$&L`%8^_E?ZX1Yhg_7*|4^tQd2D@D9Qxy-CC{mSw*ywd! zm$$4BfIY}oTCf{(EjZ?5K-K1+%7RODZ@%i=b+*Brn<_@+j?C)QSd2CaV5Int4j*!gGs%zQR5!T$tti3w) z=?uhAiZB2Yn$vecW6Y398W=IAh;whPQd2CrrQ~keGJ8rw2D)LQn8nNLO=F8eQ8LzM zL$p9UsyJB7t>@WR zXF!W%R5fx+k_t}ZJZ2+2;`v`@ZKq9D>ycuFu!jv(_wH)G$61HhHNCa(X`9l+L|6&0{qXCkQdQUU{tkRAl}5kuun^(#jhkOqY(c#^qP8D);UZsAVy z6hzNucZ|YYpJq+U{w;mj=&!Y0ooGiIAgDJr(v*OD?%O!m@jSVYg{>V{X_~+mIGQRI z=bHLUj%Qm~+W>EL>-;~OajD^C+`vl4Gujd;y65{yaef1adqBYR9Ae(b`Hzv%^?qG> zN>X2O;Xd91Gto=YrM^JjjXccjk)KIQF ze)1U335ffzx>Mp*wDgbPEfiNAu8N&0N>OmATF_KNdMU&tDMRbtHXK~elOESQs=+d_ zwk(U9&We>U$Y`Xcw90dlh-r?hWsN3@==Q5#F}(JQ@F~KU(}^i3sMcqsV%^r{F0!<9 zo8Y!y*kBuRa09$oLwwWka*G%C+jz$;$(7dQZuC^-z}i-giU56M$#_2H-exZauy3ZT z)42{24mJZhH8fnvT~EdYgi~_3UGfeM(#5qV+ZF zQ0uQ_1+mpbJ{9q-Gb$iXT8&J6^udg*gi2Fxl$8;~7@;Ja}`g_*=I zwLZ#T**L+TCI0~4gSgN$kZ1RLm+0smc_?ueI*kH&(iOZ~CoryHqtm;zB=GyCYPKT}P-! zzL0dcwtvKQi{BJ`%w!*wEUTP0F(@thmn(IbBsxh*01moHc*-o>v{i`YqD@rQB}B{t z4LQ|BMD0;yucYZI&$Ke+Y-|86KBWHuh7_GfbjbAxAw7FQPjTFw*inMsN)kqZ_7Kyb zRO=XsD(eaginW|PD2sbEQs3G7vPx{ds?2XAugBF;sXh8ShZ#$odIbb^`o+%-*15Xs zv5skMT`#23PtJPc>z+?wcDD6!sJzyM6$-kSR^sM-3W=2BcvGMXua9oSs;>H8B&b8d z3u#jwe>5}ns=#lWl&$db(`yq4^BEcR>8vT%fj9Nv1gy338d^yvn{@m~NcquGH5Ju- z6I080DYZ~I;#v)fYo@&=-Xb!G)6q}i>RqCURLwS;sY__wN7jBODUATVXac(xuKlg} zr;fRI-ESOY9E7Hx$LU;hx!YrA*#!LoF! z?$(x?o`#m1j)8{hsOgZF=~!(kOK5EaYEqP@Kma-c8DoiayOF+)t<9YZ#YV%DEt<67%Q4O6n&)oN0RoVpE6PjTxhCAevCKx(Rd*CiBo zg^tz4FVBxRLh>8!>hxI%Ro12Yg4%y7J!cf=(JsIlMw5I&C1iMq7}x+-DAdjJ_cCKVQDLg8 zx!1P!?x7_%t3p%@t}mb@4v8JfjPIXwyIftVB>JhMp5xdlZl(Z@6(I5kVD?ddyks{8 z@2XHDS+NEhQ$;M61eMTAUpO-Xc{H!B6i#j^<6=PNzUZx1 z#+;=^YIpQUoOpN$-7(TUH&v8gfa9Wz*MD-(y6dZH)jE|Es6Appk`9O%t%r(GVp&FS zo6)&nY-vfPncC`)UzAGPVds!kATDm%8>pN7$B^=zBDKb^Nm5x?@d8K?2143c4GNI3 zo;kWEYa{1|>Z#V+UECMc9Si~_s*(CY0&8h-x zofAaRrw-Xn)Y4R)5A27FXouXNd=1QImi29@$l4EDnsKvQ!2UMErlIu_VZyc}E~>Mi ze4-PyF10`40I=Vof94JGw++k+>^#RM;&m!5nXeCDx-Rl?3U()(^D|pgJ~&kQ(Dy{ zrjmy&a4=&%Zhqo{%<^fn?hR~vLgUYuD@}cET|!U_oGPBv)@F0*q-Xi5eaQ(6PUCQv zMgog9>sV3pnS=Rz#ZpI5tMn&Cov3+Q`95$x6TbT0Y`4~4D;uMsWV+*o4xJ`po4kZp z%-rMAeFeLweFB;scfhwM7^knSQo_uPfRNYXBOcCofI{LJuGT%jGK#rLN(^yU6eOsq z$bfZ}S7PrR#Hp8d${Hob1zH^l^xQsDHzskZa9)1<#|F7)Jj;VeZ5eYs67as3t;Fu~ zk@3{ZJripurFr`Og|FzD>!ryd^&qRM^6SUpD0H(|GS4Vzs$t*=9>- zk(Di`fa<4RGHn2OsX`yM_a=215O-HFTZ+8utP2ABXWJ^1O?LMo%ZbLRl2E#?KgSZ9 zs@EGnVuveTnovkGI}tJC7E4Yn_iG_koR2FNq(NK132`sAuW0;lcjatTj*Fi^d*1cAFWqK%{Q@i8w%|5* zTt+vJwxuPO+L<1!fJiCAJD#1Qos+$E?x%oSMjsf6kBlVG}>K{Uk_LVHCOaAoWNgyREE1DGF zUGeLsF)IrdNFSLBNCW}#5gR;3y%hDk+T*E7cB);YcsSVfU2M%jX(@Jw01Y~+2HS!v zhu!dd4 z+X_0~EblT@9;NxpFVBFlFJrPTcS4(cowyRBT9N+%b|YsRE2WL+#UN{~n^dnXqB2G+ zWv%T(3r$E8p(OMtSQX6(x6VjM?b9o4m}ZJL~@1K2~6 zN>dk%BqeGoOo1SS;S!FJCbx3N;wxr;v?1K+C3`>#=EXu8|>wZ~1@+HH=! z*iuSUIrh>$0c(8LMi=KVDZ-g$C0ZrBg)ED-w({wlB&6#u#6!Q<-C6~nOTyx(s4D&U zC&X4-DmurTrb}1g1Q|tc<|0b2L}(NN04Y${_|h4%|xRA2y# z6wlo-x?@@DhGf0Ol}dek#Dg=h3?P&#SeK zS|^1cXL495REOeAYxH#OqNy7ej~Jt8T-QHCq9y~Xkxx0bjkE33=@52JqnBnWsmOnr z2^y%+src&}7C6zW3gMt?im{AM`_0V~LD?_>k({R%>}9K-bXAPDMUe$9Fkf#MyrS)H zHtJQV)3}4>Ovl$l7yEocpfp}}=H1bb*@imSwOs>yWpg^>dnF1gDV{@g$w({Opk$Cd zKV)2Ot>b)jUL)k0R~w@%eAKkg{pUSFV}6>dt;U>62v+m74JFKmt*CZR|8U;ZX$6503kr$zm${)&@KD4yc$N!@5nV#HMWwZ4a0FGQ56@q ztJTpDO%NKJLr#s!YzZA_tN{4Y(azw9@olg4yoW}lr_0#u)T#k=bb6MF`KP#!RJN;>F(6=hs#DC$3`vc0Fwxq;WIg2S{> z8@16xUPr|!UZ{Q6nMj<>L0?AK6??u8PJ^O@d-oL_IWnTR^#~1Uj? ztN=oLN?jnCCk<7IG0$_Tp(il%f+?G-LLU>>N!&a1iU%XhhJph0FK@VyxZqq9Q^P!( zUA`eNzGmXbQfJm8IL=>jp;qpB#WqD&j*8(p*6xeFwx4Ueg238m@Qp?vTSD{Hu+Wfn zO8n0sMaJ0hQa30iTA!3NcK~}xPuO2NDFF(hx$6@)tE2Csy{X)0q0kw|O~^6kH&uFH zf#w9!07{B3xCKH4=Q2NRR(8>9FBMiH?WNQfX^yioZp4$cEX$C8Fsb#Dy07x`{{X}3 zjX&>=!n0ie5s{Wkv4wKPMkY}faA8dN4j$;AZ(ib5Z<;#*dev|r+ z1)Emc=oY0oY;L-1Ema_sJ=B$+qeLi9WF#MqR9TzI*vwqV`h1S5mte8i(19a8RO&P- z#IW<%7v_ph4=`|zHa3tI`kq6<-2D}Mj&@Vl>awO52UE3E_(A^w*Ri=5ZM%R2{$QGq z7nH<-^iz^Q%I#8$)y8M1Q%(z?##H1e#CiXRYTFV6Ps zCu8%lmieupj&~~;fBdNdK3*uo(U8mjGn)vI3GW%r<$)bmk!N}eb7(HCDgs}+0aGrPh%Vl+g@A~0#duz)** z*GNcTI*VasmeiR759Ugwq@LXjO4>?TcMhs9R)+%8DhhtMzug}^aP2a!IDT&JRy3sS zdkMTG-R!Qme=}u@l2n9|r;$K>BXHr}*T~pWuRGd24~OaSUVQEcVQ@KcTq6<4d`5{w zoZr2g`;NCG{-bfD{@Hm+&Em5@#oHB_-D|6mO6uuS0?;R^J0?HH9*M(qoK|dYm(ca* ze6MRc7aDf^WA8*iw?$gqv%uL08s010q^_sTv4#j$w=}jAN&N^_(~@y2;=ax{L5$@7 z;23OrNv+1g2$1T6zu!2_dzkICO7KSLaf+MRdENVUsF`87Cak~tz#zQ%fPv^k^caxV-*`3xzQ_Z$k<5r5ISy5 z;?GNW65*WOwv766EIJVDbX*XnAgck9xe9;m0(ovSe#*; zqY$`F7mP0CpVZsx5U!*l$@**XQe_brIFRpCG%DMVG2zYX!)&9CmOVNsV+_?PK2m z02h0gvoEMS>+sKm~h@biE*ZPbWQg4j@z4URVZ;wY@j1=l&@K7Avo7@d8XY~Mv4W9 zWw_()V-Yy+Ax%$coMVmTdb_>TCKd}6yBSu+@vYA$joaw2)J>+cpf%AWxUt5Z-kj5RLPxS`ClAd-k`nE ztq5$8-A2_EkPly;k)p>dB^7Uq$3HUN`(7wfeTye7kf7p)#MqjBz3_=scmY9^+ys6|+`-x?Hm zWC^h3{{WLvkY%|FkOqDoVxJt3jm588CfMd^d`VGmsD6#$O16}cRGna`<)=i+w>3|` zC6=k8%2xjXnE)RRNrIZ?$3_BuX|J~1?HaAu1P7XRK5%=?N_xYv+*3k^mfW32iYIcn z7(HZp`<10XsJA*wlm@$J+8eyG5;TyWx4(3LlB2Z^!lKPymm5->*r_QZaav} zm^^5lOXWSkIk7F8grvMucO54=M7t#fCglXTRSa*|TgDf6YQED{?Y8G_eH9kk_Ls57 zUS)oxZXo!jKoJhNYnobAOGDz2J*w(>v+uWhv0#)q{{SlRCCfF>tn|oXd1acmg^C-J z+dx2h=Bk#Jvb9QU2?|W-F9@m2#Te(*I)P~RJmKY=ODaI>smtAOxeUMDuP0x*Q#wm@ z7fY~1Hp>$$+|9a$nff&7o~LLha=VgDPQe(3fsOE0T!f}+Hy))!dfSOnRFr0C9jZfH zMBi69ON$fh)c1zmoQa`tv~Kl~NgGd3#u*FikwmwwP&7&dQaYaUlo;YRpquk1dg_8- zS9p)nzm0Lu!*H zPp9D-Hb%+Btv1Txv{T*O;JJzlHtIJ5*_4k-j_^+(w<1AQA0rHOYP&f$vwhju)UQ!N zUSMbAKJn=c43uc9^7kZ&myi{VdlLJkX5P~6Ck(t=j&H{AI9*0Or-M$?I=L0DoobjH zyQJt$T`sX!_s#Wa?=?-watq9Ko{_npN_z5l-`Q6jdb)>+cvagzK~mb$Ns!*_SaP13 zWJowE>30_h>H$y&BP}>wOnqc^LbF_R>8EbiGihA)@~6`RQ@Dy`Nq znyDGV*3_|aVykH0NHh3HlRMph_dr+|r+Y__Xnx`o-Otzc)LQ7RO*mH9{$M=}faw_j z0AjfPZrv#hOYYWd+q2jYRR-?ONlH;V4R+2wy$bS7+Uk+7RHUzF=>gy22SZg6Ek8=r z>myW!O+BJS5P@`b3rcD}KfWG}sP{(Tz5PN*&+86UC(06vi2)m=w6Nm;0LI$WGTMOd1UYkS4a;cZs%FCS=F&>Ubn;ehlm7ti3g69<<}r_N zboAL8+mltbcVguuZV@T^Z6GZ|eWoH7E-@WyU81o+n@-_C)k6N^eamu}xeWU2$vnT4 zoR25D0NpYILsXPBh$Tp-rDYyg+fL)BQgtMlpMjCY(AKK`%#u#+&+4Os>ei&qMqR>w zeIdg2@P>%JZxw?x@`j13RAQ(pb5k`#PLstr7(yRH(%v&7L$QV&@{VaJ=&#XkN6I=+ zq~Vvkfg1#M>WXS>k~JzIj-R?35)}8ch%0N*Q80HC`Gh@MC+GlE@7W1WMnyuBNCD5> zV)cSPm5p-&`Jr9kAY=437^y%=1;&9YkRwFJp4sY@MLH-f?lVf&LgauzROc?ggniWz z7`7_cLXb+DPMwEXbS8;fi%c|psRR9=q%k*C^k+pBeZe||YkZ|%V4jf;lr)W5xbM!Z zG5X@oL%UU`HIb6k=W)L)WE^#nTJn5u==HAiX>qrxE7@6X#5jX6K}?KE zM}E*K;ZUP4an7#h=zP`2Sw8)~@+|n3Hacxs_2v$D-0NfXzGAbiZ?aGSKTmUMjmY5~LC8_fh6`YTG^71!s zfmv<1HKVeJ_0ga_FC%w+odF{-ZL8&yJ*mm zHw>(}h3<4D9`n#jDv{$!QvT>2Q$LbsY%rGx>H(1I4w8#zCyYKviiHu7%Q1thj@fS} zvrd;19{?tE)c8it#&`ze2@5;S^3dG@QIja;Du)Y+VdX5S%R{IgKC!vtc!V)DIF5@n z$Z{DTO$u!oq{uRdiprPiIAkVI?TO2rtQM^!O%N8?xu-QgDa*2PT_Zp}TG-t`GK|R@ zMst?s&vippy2p>Pv?VNRC~S5X!6g8sX)p=WD$KaJxOd{FpCm8@$LIXdXtFT+8F3*< zNfHEhjVq7vxg{XEQnMVLtpqheoWGWDnM;*PqkOi;r$Tj?Zjry>xYRH+qRO&)XUy)Z zCuJ8qnXFlQrDaMw5>AFEc7f3W8$lYcY>IlJ2@6`BQoky-GoG>W0d{YdTW_};#fSWj zO-kyUW~!MIT1=))kITX|%pJ^4hgFiLM&0RL+`*G&_JIdUuH^ zZ+423qUFSbXaZI>Z@D{9u?bpGp`wiX7=y_20jR2($6$%@NNdIcRdd{jJA)znB9D8? zSkiS@?0yC1qJcE`jvua0<)MWG^8Wa*f4ZY5QmjJR=s5FrHR-3fx^Ni2AMpFUURnLgNE#q>sD;`y9r?oW5QX~eW zN%W5@T|+T(s@tt)i*AW2lI0X-1f@YUp5K?wBr*1KI)x(ccGEX5&z)5+cnmk4J-l$Wy{IA@7^xEkb1E}LmYoJt+bxLaLlCLN~#FT10V*QNbPOW4s zD9rO#)Up*g({aWyJ7ai{7vq$;m5!8cZr?!KSxUlmn$(fGWcMV++7T2YjcTO6NoG5o zYL{GPhEm_%tIMBZ=|y!JL_LNW&%(qME)Wru8qB`D!AH2$!*VJ*II+MvhLN=IKKek&`;wAGbq>S z;X;LeCGqOhP=w@mbR;IFupMpPQ2GcdQb?YJX$ZKAx`ch5$sUrDR28+hTBdGx_f(f@ zloS*+m>og*7y*+xy#|QsU}J~HEiYDzTleU>2O3J4bU1|MR>TWgsVdp?BBhk8r*bv=#0A5s4oJ%*b6VTL-T zN;K)BAIG%vZ)f;G)IpGHg}7pLUP^pt{jf>|qQ!tUpVB^DN!*&$lBLbQfSJlQSt6qF zP7?gns%CEX#DbtvF%eQRX&C$Bn>($s8dcSs2d zogkRrK|{>SrGs=^rKF|SJMR@6?1i~Biy>ZKonV1*6T;PKT`Ht}zbIl^01N9|bGat2 z6lc^DOn~d8Kua}Tt+3k-sHgOYT|QbvUr=hEWxrMT>B@TR^o~AyubXNqs@3SE&Pjw+ zq!xwYZe5JLCLx-X&e{d(YW0urQq)YQCP{%ZR_<_Gw*$yR)&`!c6S+E15dV7Zz$jDOJ~6wD>G&aoT%&fo+fo9c9uA8ArdVNsinF<0mxSX;#F~(*88;BprmqBX(^s0BBzk+4;Yg8`h_B4^yE~CAL`#Q8;EtEGZ`(j*Ey9&gop%C_eIv^VwRtJybZ_8Ylzs+22!`Z$* ziyDyKt*}y=B|8U%{35FlE_`n4trTHGE|jrg7OE+K&~V$I3(xA>*QfJjcUam4TsExfVY2R`MpQq=K|d#P^E6Qw20y zkhBgv%Pp=n8Wk8;0>Dt|IZ7)sk6OLO<`b8UqjN3!WF^z-64pmW8GZ95a;Xrst4}Po z1|-2zKg65-gAhgAbD^OiI)M;3*40)!E8VT4Z0hhuQz^ofXgYkL0c@mMkR`|pG4-&Z zHcm(IkBFyLCGONG6Bf20K7gSmOnJU>J!7_^RdbFzbyA-dcI8J*6-?(*A@b=Liwnm! zx-4@kkBU-i7R;jkel+ehmd@?JntB=hAjz+!E$XY5ylw`*svu_eYt7x)9ja2KdIQ%; zr{rOlT9USYKQuPL0FXG-b=c+Vo}sd@D#YdRGKd=ozTQAphchf!R^%0OzTC3`TW+EY zbg4ihW@Jc@fsM}xawLkY$lSKT+sr@;c2zctyJFQX`;pV>54yezw&OsnKiu`1rm3o( z4aiMInQBI{Qsnl-WmdP_OUHYZRhLkr-AGS?6F&*YUPxY(+GwmB+bKI3)^k6WEts+B z3p>v<^NO&%fPe9{W;G3U{{XuaS3k-s6CeE5QyjCVNm8IUYGz|2%mKM>IDh4$%oLyM zEDsflh>o+Uaz5YK`cNnn=& zPn9Z|o|;D`Y>teE9aH&ilosv|k+ln*-*%qkJ*xYY;to9H9s}cQUNYuf`#Gm~E68rt zh@+y#ELGLT)i!Ed5?ct~0+I}*CpcY&yiOZsrsYe>atNGD;{-OI16ufeI`#M`6Ye|g zf7s`>zi!+~Zs04uUnBdM&+BNLxn%5K+_=%!P$3)c6)L1@)v&^6FGL^%+r|+#F14dT z1kfO(%0^9GO+eJD{^cLGPq+>k_NRaMg`PicrNlADsB2e?C2yko%`J22WvBlDg(x7n z4%yCK=K@}Fw^_&UREG_8M`1ZY$lQn+&_UBw#N!;PWYVm;qr4jI7wve>RM^4RM|Pd z+jk{6@-KG!o)5nrOSH*y=V)=?6{qvGlk}82%N{$KY4@06f1iSGd+uS!{{WQnaKYey zWW7cib2JORVE4K{MzRq!*V&#G4j~}_02>qa6Y`OYCU3CPxuy1x5;w?oS1vjCClg*H z$T85r%!5RhMq^b#@9FChT73per&v>N4%($)5g7J&hTd5^k}!|Zx9&U7vH1WDQipz& zprFj?9#+ESlz)Wid^L)UmL3#Wn6KJ(s$9`t8E_z>hZS5`qVAuwA?cx*?=l&7SdH|NwxO{)7nj$}j;+l2X%ZODLfzh=s<}QM zOGt?5rN$kWClsJ!n(;)J=;bDEbn6bZwDkljKie9HJCoZyP@PsineEN&K)Ir!JCt`z z7DU>fx7IfKBl1jc0QiHfSzwuxY6_s;*v|o@nyjnsid&`5d6l+^AvoH>m&Haip5(+f zHEq3(Bwe_C7d@Qp@>9$_ijMyP)@}#?0LoPvJmGp?KBw_WoITR4s>a{=pG3d$L3sAk zK~yeSR@Ky2p_4MP#*}R(L?vNiUpYno7S&-H7Z&tQ=eO&&M~_{3Zsm3`O$TVsuOo>A53{-P}g`T@j;$O%Oa2?pu@RR!cq! z??aF2J<#HGOrf;hZYf0$Q*BC+5?!RZFq9~$4P>w<;|^c;?UnZ}AG6`$pm3f40J&p5 zjHG_^dRCNh_@58ljjMf?@K?C13%*(G)12up*UChGrO$5JOszR%B>9Rc-E@X)5(tt= zApA*+{{Z`r>dH-*OrZY&GQV4Kd`4;`E;>+M{zKG%dV|-DbCS4X{B76UUB?Q^Z3{c7 z>Kb~It(Ovdgt(NF4`a~9qATcbnN|=&q+DUH@w3uKkmTbz?j&E@6*+aFRbMI6kyBkv zl+(LflmRfXe4)i85EY=GSc1r7o?#+0svbNO?^3zn^I$Cj;Dk%0m-AgM4E+N_ZhM>kZHSO)J#iWoz^`(X*6PB|D zt!tV`>Rgn2t;MOmrF)m;t_!Ehn&f*97gpMB`3qYbsWjCrrm427TW)RsrKGQ8n`8+S z08T?%!(|M%;m^>O<_=TG*rrW3lTh5h*gBD~Q3rdv z=%V3kUMN$Mb+lDm=~%C&c(YK%%ED#}6q#xzA_$JSHQhoiS(v>|I;B;=oog=?6xfF^ z*_Fun%VjOm)H=PFdKC1XHD~7=dVr%S6_&1q?vJXIW>vDcWx(7OzJ05>PY+$6pE-8X zd8xbGDUtm%8Kkd3OicX49BmhTf7nJ8^E{-GAss0_sZR3hdi1ea}ygm0o z_Y=XnQy4|kwLG7T{{U8XhUhL}nx-jNR-{R%(m??urs=@h9x-nuTd6{gJ5LGY;|CL~PnT6qhEtGM&f8PtRo(1)p!Y zy+V>d^#`jTA0<>MPxeN0$>iO3R7uj0 z9~6zFg;XE+1MMdo5g1oMBj zJj;zPHFXw>M_r+1YAU86l8_HX$~J2(oKv!(uQX)wH#RcSz|bhwZOLlw)j^uHDxKzu z4l=Mo64P17Ul<)x_BWxzse4s)wPZH`0Ml19>LhJbYX@tMsDBa^H3UQ!M-v>)>8iG3 zGm=}pQ0R%6$Q`r1i+$&dIuf{017};&z@LFYtW(%R@Q<-V20S&!O z=y3{>Lcy3K8*Rl=&bFd-thp1Y2tw3kI%;%>J?e$Vx6L{fIR!P+nN(G>TT2otGS|0w zCOOI$Xg4U|$((8_-G1k2Z+iI8n_WVhc4(2>w4dBy zM#fr;icsaRKeSR`E$OIfAh-uzCk_)-mkJdXO#`$yHyYSexR5#y@e^|?X;L<@`6Om- zWhrenNJ@zlpT|$^i1uFs>Qx)`r^zsN)gFLxD^$<;dTSCQ;1Qxow@8ZXq7vZpnf(kP z&8Fz(wIER)WA|jJ4WyKg^63soIHGa(gd^*<;Vm?+!f66z!X}$UxNVP$Z>ld% zm7c)BO_(RexuR2B@2shydn9UpkozSRN|Mx|l{cmq=$H~GNL+USp}w6P+it9v2YJi9 zErh0@p-Y-#-AYoK0+2PFD1_Bfb<*0##VED}hMG%JtP3M*q-~^gQ)hBYY1~?ZKnL=l z0TCBMiqUB5giMO2x0xABy!42xk_wlGFBEY>cuGoj=z7F8^gEiQtYs{6eK0(c#;Pgy zD5+qL0E~l^SRK`(*?8_q2GXilR@(1*1*Ev!Yf@Z5+aOHDykcjFMnx0{ExBn0L`KiO6wfSeL@G(!i#20ugg|=daNoxjA zTUSDsM(0YC&;#=A9LHiq2+=1en*=6Tl3RQ!N_B-LXI;b{CL>kE27netnFa2uY3bKc zigK;;Mu3r|zZkbcJv>!5Xtkvl)Kyoc6qFfDMSl;DlMv(Xek-hmj|HJ?TFO?Vl@!crA2R5Js`>ULEvU+nld8J+oIEaq@6r?{ypgo+SEwXThCYtr3T#}qjEM`F zNeAONA+n05B(Wg~ZuTZNu3jP}8zm6#w;>uP_cVZ~CQ~VP07uP~gglm_ix=D6`I2)}Ent0a~aZEc;F?8QZqfir>d< z&h1rrRkR*`YDn9-sDnKYv7bHSt4(x$771a=f%yQJ)?YYFCjk=Zsu9~YUXnmjnq~QX zA?D16mLgKG8> zwv@WG?A}S5RW61gF6FC~^|fh4*HyPU%ddn)N_A5)4GKTq$6UomHh!qDmn_6Boj)teGEm-m4&~=5zNKRe}tz7 z*N!;m-Sp%v_Lp5E2Jbx4pekP92>$>Om)49%sl|YrVnQ~}Kp3UvIV(r;Da$M?iRhji z4j}SE1B|F045hDF&NAGQuJKn~ejJYk{{Z4Px2>y6(3Jz=Qe>G%Q<%PFl~-JJE|0-J z_S=<07zCt~*vq_USC-(qp@}w$fC5BnX`2Wx(LAm;p3d&Py>XssK^u=>9={sV%mL z6ddjW8j~o{S$k~*d=*jFv&>V6gYcE=^6DRcoof@+5KmL_oKy3iz=?xsD$$7H&vv9K zQ%A(#(5XxpGrmyj~N&ZyKiMQnd^=doKC8%H0-Sh9wpWm$Xsml{y0i;-e{hkSi+ z)lfcAR7r1Yry7l=^{~(os#8HL`hvZ(;8(_}RNMB~0`>B#CtZ9@OEW1Knu~pmwMdK4 z7GEwEUR!H{Gth*Y_4q=cdl~UhGS)ykCyxc-n&D4QX}rKbuu%Y5ka+eu6ZeE zMQ2?oLuzQHUG#z7D?*TLXKzHbV+_4kvL2_TSxFO|v@(dRo2NunH)v2ndZ_p!BM~V4Fo!>GuMX;#Y_}Gq0grHTi`(r5ySt^jw&$LqIw`<>3 zVvM-?6cZOBYTg?8M;z3<&OfRcqZeP zx`GhokC_zVU!6m_=p)m;rtOW*;S)bbFFAK#x;FMqF-CVibfJuK#}xZu#6_nNqG30W zMIz;rJP@k-m2DF$aEYXR29fOyRtgJfz+j~pi1Qqp;_gCZJ$w{xx$|xhmgTC&>9cEn zR!X4bhaYOKrz;lPCAnp^sH#GGnL*PzodMNFM#qhInj?BI(>+wfW}P5z9-_xm34uVV zPlu2aU){GS)Z#b=E)ipM^prJC8k@?~{{Whlq)7yh$Et z3MTU4lSKH~TUJGPa_d+nO}GYIGTM|5ktr66F~`AeZ_j_NsoDW^?+^0X*oHg%Et<)X+}VJX9r$lGX3kIYaLmt9S7a-m8sw51Z3&`ya!IwTK-R=ttCr!0uns@+I0|Q8wGqEx6I?xQ!3c!B(I-rKJy%Zpu6- zeyOw@dvu$(8hN07K51VE!_f`JVEW( zIps>WZJ35DReY>+U&^OpYakx;NFpv+eh+zkD;T4E%F8)tc`=wrfjj+2pco?VTRS(ZSM)DrPjy*o`9alAbBqhy09CLqu{hY-S=A?n<251ALK<8BfMhD zWGL;I#zr$4t^)Kmo;=VL-B#KZmp}>JJR%16WpuEDfhcWuH5SM6^&?1wp5s+yw}z`_ zb%|hVgQ!0!XWdW9Z{o1D+0=k$-iP zi24)ci(I2nns&+^7a*jYbI}($Or)sGv^*{e=%d|ifVndy%To$7`Kb3oP(-Bb`%G{> zA@LMGlr1hc>I9rY1q$#W=?}JI4N&v5b7)42$bGo`#=CLL6t;WwjyX=o)VAhxC&JcOR*jh8>1o1=G36-T1rfoH0vE>4OdA>e3GG{(){w2 z;^&2CiXweeK$^lX0d4;P3jYAMH5^NJimLVK9TlB>LA)lfAhs~!OwyzQKBRPwbB{>( zwRx|(_Tn%~?q}GACBB;wv8-$hQ%#bYj>A}?a@RT2YLVNkc(zqq4Q+iJREKheh&m4X zMNF`*Ui$Tnk3~=Z`^~be6uTXPWTkRgmWOE(sBo4)YVl`!)`a9YT5$LaiGQuVKb%(dq?ECQ|u-bQ-Es8OQntf^w$$HGDH<6>y_Q-#xX5~?=I|0&${x)C zT%%{aEq$W6-yXG4sWcRCx|YPHFp^_D&SA@3OID-Cxszm!2lYrCW|FKaRPVB;++^(_ z_lV4Qk?O6tZw?!WM88eF_K?)H#w_i?HQ6-Q&bst!>wcCXiCGu@4k;}2Yo}`nY znUta?dvd2NuhR`#W=g97OJEc$Qab+Vj&-mu>8hQA^ljBkKX3O+%T04M&M5k7P?XM{ zdrmEUdQ#mMWzB7yA>0+J+3#q2(AhmoH6IB@-Lj6WD42LCBkpy?)LWkdT7^Ylk&yPS z7q{bNfR$Et^lC*>I*F*vpT;t&3Ge*0oz2AVUPkTnru#@A}4W>4lT2FMDA zIAx&p#Qy;B#I?vKGN7^VB45&~Uzbv$lvQ>GN*{rYqbvNyyJ5jGAUF~V?kpKdg$~p2 z0b4iJ0y695D%m)mfo|HV{g8`|aUy$O5+}UkS0DVPR-97miqw74;n?nTggcDoY$^K%;C`OEOZ3!~Ipk83 zPLmVTa3(_{`)EtrDpaAwLwhr0kkA!DA8=o_76I)af+;=7;=boSdlpwz%?=UAHycVT zU%XV*pZE-1C{z-UO(8`3h&l*_#Ab#2wmGzNj^;cFVWJa8{mt_J-|k;lAj7!f-`@wQfnOvfOy~>h&YHyS+6E=x;S5-s)aM4k@=v0N#K}2^tQO zEt|ZZ^lYu5Kea~j2<_~iW`x#3(@ux`cqj9iIb(%>;MN~v9?Q7$wvXD+9`RLbD|5@< zQBu&MaL`)+06>~LXIXGezLy->rwBWxq~UpJ*^&&6r=jWa9tqATmUb1lQ_j*|C3d>K z2|avI!@)~my%z&;FB0${vfdr`m%w<)3no5f~D1s z%37tQ%%w4%@<}C*=s*X|Xg^ZXxU81*+Wk>@t3^-6QkC=V*YA^td4g@4qBStTam#XF4Nn+OvB0EtdK((kxMz8+-vu(I|sBYaIYRNJ24 z+dxFv`OGgmfxATO+7XH81V*bbytS*hOyt0JhkSi%aa? zwa37z+<$p9SZ4-bw|fWiH&yFdCHEexO&!6PDas`+^OA@AD;g2;1~I(%ZLjRyNo_}( z^gk8v1}`K4Y=Aq+^#G7SZ@*3rqmdbW0Q0_)=s}7Un3B`#8m(&EDA$UEP@EffP|Lv! zZP6unoC^Cuq(#t5W-nni~0q1&QM%1+M&44BL(&Ns*r8G>Jq!G%4w-q?ZIz zU-K?N!Fc|X>brG5Wq7t)-KtP7Gfta@V{~QJBe+MYxQ_CIVuqlfAW!N-GVF;O%4p&T z>r*SNjp8e^`;`R}OWsF7r%w zuMp9Ye$n8l&!3oo{;_f}^wdhPHv*MN>IF)?1ZJl4I(1uI)~^nw5`T^?H#+Rs8%hvL zU8!v-SxJ#4LO!<2!@p8ANV~>LVIU5w<@rO6Zx#+Iu~$@rl-w4iGLV8q_HG_;j9Rf= zmIu0k>a(6p!sd!F;yR|2+CCLsm1msoX&@zZ++mp1p~3t>yvbiBOA=Eq)}cdRuUz+%VWqLLRJriQD4b( zHnRJXJJaN?Qt+-dl7rd0U=HvhHN2DT7bC@)Mt{MX{U?BiK@|TC`bGs80+bVm4Qj+jMAMoAO_?YeQ6>uKyjFk9SM7$4mC5Ve` zygR_tbu}N``&Bv?zqWOc+=m-x-0SS$mTml_g=5b;?N=PuA=)g)*vk$2t}|Ckw1B4) z50HrkjYP~=+0SlmiH_;CBgf5ZG4{^GD$zZJp}_cfkAjZ8RgLo3x4OFgi;4LQBI2vv zUgLUx#dEH0gRy zZk%IPd#kRw-L4fE8@0jLAGlp#aZf)``^zg^jxEAer6`g~CT0j{A(Qk45pl(s<5y}= znlZ3omuo#+6%>^&F;PrGX1S#n-9a6~lP{==%MMuZH3|anghX{}mU!*{_jIHbnQIEU zwzO>*me3IBGShlSho-S+gOV~q(^U^I8kL`dIB$*WZw0I%azxhZj`Z7-sK@^OpvbfG7+@l`9wD@ z0o_-b`9}n~vL(zK1yDH+lPAD#FI#VPu2Z_gihV=4Cqp3?YE)EoS^QU|9j?%zc8j6B zT&?!Ss$#9i+XU+}wEze0hMxmn)lS*n5aB|f8(tZeP-S^8A!2PPzhjLzRI3B(A-cME z5x#9K^k0DK6pY^=bYpyHMXF%ANOS$EQSGN8VzKqm)H1tO;+XBKwmu3RN`{>eSgbZ$ z=D)mIk@@NSA@(q!oN9h4YejiYQ%a|)Af71~cNG9A(M9sN%=ewJB*cpILTp+G+f?-32)>v4D#Ai~Sy5UQKYZ?>fFN|Jj{68zj`Xi<^iq$wFyXsW(GrYP6F^=OctVlwM1 z5N@SS+2Um;fF+HW+s<)%N>@z6l$Cy649NOLPHs_X)2o8BGYfLzpi1Vwsd7V3B2p$x|C`PV?Dw9WWHY7SM*DHH70u;2qH(P zQQ9un-NDB-lyiciOT@1L89?U043z;#3}e}ED|+mgmXx-lB#@OEOv}Orm$-PggX#)G zE+LR`pbFQxC)pdn(dmWGg)dcv&1*92-YYj9%CxVfsW^B=$MFPD?i<+Zowq!i3Iw8| zS|M$Y=Rr8AUHAIh!c9t@9juz+x&-%qynfLb9qg-=eZjOct3@NFHXIUVG_?y!pQ%-} z%}ED9j)%Nz+*`Pg&O{}Q(Z2;s$?#E(lVqB>tGK;;8}^NUuse*``+mknsniz28n&{- zs03}A9c%@HI~m2nrNdgE)`4}DL{n(}OP;TOpJ!F^#rB6R_Tg0hW*vW9iK)hG9uB{4 zczJ>wSrc1CbSF@Bigz{dJ*OHC`oERlDoY95bCxIBG;r`x8x{L$^F?i7RqxZHM7+(7 z$~vEbmXVd^e(T2IAn0_wcR`xD*IP+BSK1e5IT=d!wWxm_+|5Z9ddj^+@H0t zE)*p}@MqNpwTVJm_kD-zQ*(AQfrh74gV?*YB_C^^W&k9;y(dsMs7fDy-S#4$kyI?5h`94wDt_5q zzfJ<&s*vv_sQ&`in9Dn7tn z)2cV8`(kn`rMjU+p2Z|W&)x-e3IEmyp9*s?o!b;Opgv$12py zJfTh?C}5de-si#wxa3?b*_rmjz>1Z@0qkcIsB{SNGDgpXVdgRHrF^$SQxl z`CsydTW{O+QCD$BZ+*8h@X<=I4sJRX7k(1#Vm@EItDv4|U@ebAk#}FWO|qZVEnU~O zKa~20IN!Xf{6Z!lw}_l{DYm)$3T}d77f(?Np3+pQ!4CV+4QQxENA}%Qp;3#JeSuZo zTUbTZjbuP35pnm6ZszI{cE4`YMtZ2FSN7<;u=&|@{#K?^6P)~K05k5w(f;CizQAh%ANr(~-7?U_KmHaG4y{gq*jM?Z zJ4djql^siK{;riL_)-27-XsUzqzx4fL&^5}A^s%zjiU#b(I0&zY`-d@`|1B-QCoOMymy%U~QSyt?l~*!rgQQw3LEo zQ!%f~BH#B{eosm%XuBV0;*=J@x2GOfJcb>oDj?6P6BGCyA~NUhuP6qTN3*gPE!9fy zIr~^}J!SGF8 z=F`Vj!ELFwR1`IuQcr|fas9-;k5EliO#E)vK|%pdHOGwjs-a3Nq+w-dWbFfAlw52( z#gztu#YA0j8wYi0NK3yMadLu?iWW5_gzX=`QFX`nrz+9&6%{XTEC}cmLwVy~I&8V< zQM8o+dk^l56|a3VekDLl+bbIA6Ma#}cq&v}LkN;ihe`Ov?0xshj->?d+1Z^agbZ^O zq-eI%5}myjFWJ%#{{X+45zwK}Y;3?pYqDd$saQHsUfM^@`+>-gsxofb*`EYCHf&+U zq&TD~j=IaQV+D`6OWL5B+RIVV65KJ&NnoEz1IJM#FPt9VxC_6xks^1q>!KNibce(=%(X{4vDwE zUTIX~loXlIL;E9^v67rG0To+r6mAsmAnzl~q@c~jH6}BvTCCd>Ocpkk0in_m%8{y8 z@m&%E=U;CQtO7jWAs;~*)iU>{*85pVQol?Bspxe6*nP6Pv~N`WjQ~{iPM@~lYix6- zV@+R})SF)#P&+&8!C|LsHW`K+I&Dh2DPA2T)}4;*P3~pF?s2vj`S`Ca_rJdOI9ScB z2XyY!PX%o}*YC^QN3yIjj58m&QC)JoZ7N&G_`bvGR6fgTM!;V!9gPDmO2{$p8;R^O zQG0Bro=e1bINZK!Z*|~+PgTs9ynnh6x(<4Nx`xk@tBP>yfV2xGtbT)0UOvK;4dyM8jTDm^%eNHitv2XsfQtIDz%Bz)9 zop2W&Dp6L(kW9}>Lb!Q+k{7t@h3=Hhirtp%6x5mz)DQ1nLE7?mQ1X`IQBNc zaR9D!y-NE){*iz-mWIbG3vJKCc7`N@piplllOOH~cOBA4x2#`HYb~uO{DLa{gTI;Htqh)%5v=V3H1a?^{5Rfvi zOt@2-9rtjbqQRN}00<+jMl;mqWN-{-iGG?=0$Tw150pA}QBXzfh8i9`EJY(^0~uPBivLRZgiQ}>!QpXRH+vyI;Q zi}F*ANOVzyAZ!O6BzVPoQKH$DMjr60NcJ*?oXL9elx`= zRaIMfeYCjXOu`UUHxu#^apHI(bn^G<8#QtGEdKzxyv6vdAR!y>B|8}3664eSPr-hY zKE%G-yhZI_0ElCO^QPdc$bB^mzKl*DkvIgm=Q}J4FV!qTZ zZGZPX*!S;b71vPO_yOTW*!|r2M+3V1R^;q~`yD%OSgmi=G~!!APUH_Rpmrv3FAdJi zb8jRw65+I+Qv1um?Qt24YpXVf67UEM^QOxzldVYF^4C*c)7OU!Zj$V`_{YZ^St)A*kPzR0{X1{V^hw-x=j zeb4d@GgX+!5#BCT&jIQBbPp_Eq%Q`$Z-|5%eHsKSIlkR(@P#$PM=&n8s-}3g8+w_K=l&6X%xI^TzcFF=|6EV_IB)MJ6 zPCPX{5i|BPy=n;Q)8!C%7xhzC*un}Wt+E+^!|BvY1~WY zDtvhfHB&-TNtC+G#Lv|?!9z)AJd$#%no1i)KDy?7H9tQ40nle^gNO2=*69fbCjZ3!K zowCrcDgDFoqj3g-bx-ZHg6$}>(kh~rFFFvPIY?CcM(uORO=t@6ori2Aty~n2%=k9O z<*~u~B+R8#(tV*3=bv7w?3Zq#EYNDH=iKKD(AyJU<20$ByHpb{lh;VIHs!NLRhK&v zC6(0zr7lcqn{7o+C!`5VluY`Lu~@oz4xnC#;{nc$7btaXHLCDcwi%zk*E@e_i z?6(t6;q??q2fz0~H&#uhRLzXXa&4Y@;!fv}z<9({p=i-rTQvd`OJ7#O68;~opm}j@ zAt2pVEbV!%RU*<9PMv;TA`X5w*#he?6OBR0l0=qdpNhKv5nrE(8t99QAwrIu&YOKE zL;>H;3|ny*?yV9|L?oTxC>2|fh0K}DUXgIdV73vWiMhV0D`q2SsJ&MqDM34xBc#bO zpx9!IhJk09{z%JFR~2KN1Bu|=gFPck;+V%V)OU*W-*P#ny!NMQ z)pgnIlbdMm`1PKi;Z^7;AvzhJx=JItj}Lp@xuIj63%c;ZC$}T0txn_0_PV=RT|L1h zluYX>=^3x|DOzYk-EO(2 zb9bSt<-eKeUH-pts?-9r@EVwND8LHZyKOJ}a1=wmRk+ixd9_UX{Nk%E7VTox)!c6H zwBgvZ-gD^zmnn!E9jaPz-SWNM-k6|n+aO12Lm-m-ae_eDmflG>Cv z33llcRRE|d*;cNaJ%rM<#jcSqkfkeR(seS0-bh1HctFtV6?)iZC9&EBlF}5VC(a~1 zJ~3aKM^d3_wl>l2)`}wgx8odFDa3DWRkGC7%H=esf0UhKYYTSp>Z)fh#K87ai8?(AFW8%L(Mw#aoQM9k=7>~6ir(aRg33GDiqsi%}U){_^?pGyq}loWTikOE2Z zh-qzu9k?qlGa^iTPgL2~p$w%+00NP$Jm=6OnB8i%wN}$~{#_;_yL)^XbyGHWJAy*r zZVhf&CsRG6!Xf0yhZQ{rJhdR7sSZzR-Qx^4=#+o5w4oDmC0=9LctekUNJ2TZPaYaj zl7g>>lGZFZ*Q%y%gsCHJafQ*#XqW>@#q!ox(PlSRu1Jig-%jdR@A=Il0i`#e)yTOk86}JPC((R zm>AA<^Hd3Fm2{8?#6_YPEavvXu6;sixjl-D`)@e%CW#V|b%1Xzca2ou9`D+40YIF_ z%8E*(wK6F>8EPbBJIz`KJ#Lr_;`nt^@7pSZeJO*f6Zp;@#UDxwF5q0J?n>fQkJRm- zmAU@O#Fg#G<((8JIDTkVKg!;Gh4QUtvl!k{$9nVz903q3-aK!0)s%zOraNczFRywl zmf;IhUb54Ii4ugJ()da*wnNaUx2<;cGm`CMl?zZ3C#rl;(i*yuQFhihR;s}M*rWdd zQMexmb%|Vm@|9)Z^@UoOxmT&!z@7a?xy!m7Qcx|drAvAJ$^an=PNhod2aIsK7PR}l z60hBGTm&HQT#Nh9=Nn~ST~Eb~W;rd71I4ZjuDo#F^*0d3vgQuJ>nvA|?G*{jY?4O5 z800cqHc=#0(NeD^%G|*CZb=?M_kLVY*V@m3#M!~ozk$64p{VgV2j#Xbd*OM~JWH*Y~ zAE%>DQjtcAL?)<$z33B4k`kHDeO;I7VV7f_wc0LQTIDY+4AI747$;FdrGH+kxp7B1 zHOx9$PJ}bvpp~$1Z@jxDyf{0^Z+9TkjcLxaH2+`YKJiZQLEFrTsl!GRgC3r0Qgn zArax-H0+KePZCG=_^+^%5#SZmK6_e!w{o+uyMp=ed^iX7c8r zqPJYHe0O!VrlOULbE-6;eFZ6!!x66x5pWv-Jw6?nz|~BgH0OSCDFU(B0)RaB@U?|{{Zea$o>(F z@($Hqz&AzeX7JjNE&`PU)1rVeo!#RkfbDo8 zZdmNCz0)^D;NPc>bls_&+{lmpB->*%TT$~SV&7RaWv+R;f}Vn08+w~ zKr{X!8xJ6N%lIr~5g0p<#V~ZKZDA{v%=$zGMAa8F6ruLjaJ}Pvn~UeRjC!XapyK-a z42LIXotIxrR_kSBZ@9O8Aa*0mEyqA&+k(N#dewhuEt`hed_PRZZhB??xM=W^xt@u$oMC)r51i;iib4idPkh4PC~`zxlHaJbtKd%nG@DL1#137CBvL6va8w|5r&2^*L>-Uz%!gl zi{>odJBamJuW4mT0%beFk@<+W;B2!WQ@TpgS28&qYha^wSIN%V7CCtK;fPezhLRhp z-iDPRwC!(RokVK9Mk|4KcP#suURz6KDNPg*_Q6+Ptizw`G0MbSD;LJuT2)anfTmR2 zX>ln&By@?6=Eqp!2VYXH_S!+ZXE*$XKr7|VlrGxUGs&Tq!jtE+=$7b9C6Zl38*q9r%dfwkn0^Aj-v4enPM?eIFS@>Uzm3?^xtFBk*7 z!bM*FsB^!!x~6%5+UFrvW|mE*bocyfwdxzo3{0=nsikE@)ky0jG5*X?*>_VDkayGH zH`|lqG*q0AX7iYq{Z=MVaz3;NQTjYmzZm<1TCg_VlQEz_ zlXXK$31YKv@g-p-%cSCR?|{K0ecMM=6c#tV_fR|7$g@%K*Yu~ZyV@<2l`XZIx3wNY zw{yNI!aZ7>9XU-7S#D0!t(A7lf|aR2e-m?`gjOGkxr0XqquX*j8{xVV4OBb2V|P4$ z<5g?C+MTx6(JZ2-ndY2jsL*NWZ z!pFvXpiHcGiAPgz{WqywZ(pMFM8mC;l0GA7iCAYRs*94{qoTP_w;jT|quO7(s=J%z z`lj8hQK(nu^4WIGlk$m6hm&^_KNP|JK2=Uuo3%~Su9>E9EWcVL1uB#fvL_%1Kw|57 z{Z@5ju8H**N`yAe6+tO=q$Djfp1(NkW`e0@y3nGF>OE?5u$_tkfv%8bZ>gbC_e>;J zJ)YhD@^X*28-o_l)VY-4d%&y3YF$>=(>+Z)ik`&}4u&Ig{AwG4QPm%S=9g9vqm5d+ z8Q?#4JW1{<#MOQoe{5b5g#Q3k#-EdQw9AypC_P8}LrBUpcqy%&LNs21!1DWB_5#zY zktGxSqvsI46`Ts9i@l25dZlubzLhm8a1cU=XgbpVNS#)w>-=m}u!jz+xINAL8^`$$ z*;{b3(4+N%Dj@+ND14+NQ{(*Kqa;{2Gwo=Q86g^~Js#MyK0M|eYSnh3aTe8FX{4p3 z%WtYX{UbzW^lyl{rD3)>%a|_&at#!VD~a*WMy39-W^pt(*8J3W7G#x!Edr&~d<;^u7@*^(g?MI7oJdFk&s#bx$7<@

B zWBk2*p###=o}yYSaqsbrBf34IZd>ys`If7}`;zQ`rcQ}%n$}t>LaL}jRMNpIPjTKn zrI+mnX3!cikQU!%+zf5m1UOYjyVhB3q;*X^5(+``#R5zP>m5flf)%xyOKNCBY%W=y zslw)oBc`7SeYoqI=~Sm)LQ*;0p48){kOEWFq!|^Z%Y4w1Ttt95liB30;UpiFNVYqw z_@KSmNl9A!g@w>`NIJ-nHHC&;p0!Pxp$R)xS!oFi+JXj~ys<4DbI~y;v{FLlU2Y)F zDUs*^f(5kB^G&jN+L3inThyWw(sl6>@Uxts74VuQoT(HkoPtsZUhsTYGgSp7tV!CM zYF7%N_lA8N^r+13&&f4!tUDc4|a~i9>RCxu>^CCM`#xh%7 z;;P^Hdnv+bXc$FxD``?vBxE(xJjtHUWSJ*YwVZ9Gw%&n07amIEl^;zM?FD$0_(f7| z#zo$0WtO(J=nzV7NyA$o%3-6fgiBA4NQHg&OX7?I&&Vb1qn#{v;MxO8LUaTIVJ*sJ zfzqimLu`DJmB*iPijs}=&8VK6rP>FZEtNe1N{Z6Vc`vDb!eZ2`YzkJqx@bWD$d8u) z050R8DA;4N8WI!Pi+jn(tyJl4q&9@0NTy&*u+|!7_aUts3XpZ4IF6wL$^GKkFGUS* zX-Lv!s(MZ<**sQ$=+(_CO3r+I9w9n>Pxk<)!r4WpnxdUn`IGv0W&kIwi0Kx78Qd;y z$2~AJdpwG+ay}=6%fEBowccQ<5Psy=pGjM3=!>9CFM7WpjA@g7((_%k`fv~6s*LP* zAe#IC0J%3gXTE%YE^R2e)ikuE%@oKf+7HZ_!TW>Hxkn;!H;gn2?SSp>Gad_~lT_Ke z?voPUl{UjlPxy*}{n46?J?Q=dtiMMUK7ZP`fA3dfyFz_7_RG&AOb+a2?S4X;lN$?l z-}_J#?t=|ef!CO6X;<))4xdAUM$P-3<25O;?IR=&6h`0fhh2)k z{{Zu42rVo8Xj=Ws@kEJD6*5nb)PC4c^o!y?MA)%E=UPAasW`o$5SE(QI!P)1=!|9# z^qOp67+gP(_OE~Uta#Nj{!>Z-$QdRN7#rX66gh|ff7-RZ?&ppZ`59?mCP|OKpkIHG z{-lv^KhOJ7B~QDaJ2U=6MqPg_b-Yge{{R6*x6{%80JRiUd${BI?i7rfbUHyNiC;VT z59(hFmj3{q0Z+UP6)W=6GEjSxKeWT6iQoSKkfj^uALIR~(XV%Weo_bN84B+*A@BCR z@E;-lNvGYmzd!9m(eCd9QarS*4~FE!t;63mP_U-U#eRR4#3m;{OzH+ab>taZ3c@_s^|WblEh{i_9g$HJO2O$!IbVJVbI zo;8pIMiRagz)#KlR<(P~!w@%qjfu~u(f#nT#d1Xai=ucb^M2K}9`$h~x8$UzGyI@{ z3?Kf^$)DgrKIRYee$+7g+r`p^O^S4tA28Fu)(HOqX5~M~g*TSNtG{Z{zk3+Elmcn# z!T8Hx+Z_J@VdY)?2lXe}k+lB+H|<#;dKlAa3-qbdd_Wz3h975d^0oPYVs-cSRIdL3 z+JjVk*|GU{w!kO&q#{YZzRI8BQ8H$%oBT)${p`_&0LzUceI=nD9pXkF*ybJl1x889 zYI!soiyCNBHs|hh)I{nk$lN@W@cNj4{?uPLs4M1kdp91by109lqBBye0ZQi} zWDcKL?0vJy58)`k_jR!TB%gQOg|%fsx+H%opZ6FlKGxs6z@a4Ow!qe?8ok9^$&y|K zh&C?K-v0oUq2JFfhl;>^hN4OGx(9U$!Hn$f@jod@9RAzS_N`9)j6xKBC#FyFZXzUO z?2X%pMBDq5Y&=k=J;pi+TB-=}lBWj8*c+lcA&+w{$BJ?>4qVJLI-^I6*zP$kP?qLs zX`ZPd{6j#>B<()i@)L@*DEp4^T103{d7m8njZmunc;d{0*=<1Fs^@LhK9~biOp~Y9 z0?K``P@ve|H=AWTA==XzvvMp_vO)6LsSC_Gu&(+QdxstC7A69^*9;JyB&>IJB@f z)+7_D^F+KIvyzpf3HgZp(IMu*v;P3QgGrO-nf#S?o=9mq40Y;(xyma#{-<>MiOLq<-N<|w9mqiOK;Gp-Kl$QbD%}z=_Tm0WGszp~ zlC`|W>CSb3S;VXzx_6R|xa7YSntPihNc~K{Ax0--?VIWHsL6R)0T!NbLMJMppPC*lvps%o`gef_@}L;Fb6;G6#d zcn>s5QO^%FvY*xxf8G!@ovI_CPRGlKn$kJhZjCCAXzb4ZUv zRFZQsU;gFWtVw^{DZk2@{{VQ$=8K-qp53($6EB1z-MEx>YHV_-$?AzKvv-0PONL^4 zP7vvEAR&+Cb!q-y`2|c&7)xxn_#_#PQGG3K!s}LwYIc`W z^M}gX0a4dpf)Z?toEp)oPnm;TRNr!+F<|^y+HGkF%{c+|l&2qIz-aBNBg(qRP)x|` z8&1A4ymt4tF|dn^v8dDqlyfd*_k#>3fb}XZFWe6?SK6y=)tS|04YuG`r|pyuxkkpt z{{RT|h*qCC^%Hqb%V7}2cCdH_=d;1=A2+=tcfPf$)ir^pSWE6QWbI0nx`&iXoN;RE zrL9D%MEH|9g_iB|>f%XOdwVG26kq@*GdFxKPN1lcy?U8~U0ME z)=@A0PmpfT)YUqFH=|B~!%6AjBUHgyh+wUCE6u#eAp5J@o{iC35Ba4b;E<`yw7UqL zYpPTmV-(dQx#FzdF=`hRH7V8>l%Irl>7=!Nj%6fOH+pVP5VQlD!piLJd-dnX- zST0YiV-&Tlsn5!g@N2pGaE>}j1bxXpy zx$a2Jfnh1516|*n`np1*`wr*he`Ei?(Xt4!DO@rdWPj#pb}F0cOp z_FFiY8p(N!mpKaV5cS^@rlV%g(!x&BP*%05Prr~ww>^-3uNi;0{{Yl>)A&-VKZjy% z@_hQ;lT&OlmwP9_=VPH zCxYVGg|&-yg2zp2repxyY&3?z1O3q-qP?^1)(-<{83Zk_iJ)FN&hmTP`Ag?}oKKlr zv#bLVp%F|tQ2ZLZTo1t_`4xh>`{ z<+8x?*8}JK{KEXzzUJQFY(Cum-}1K|!)n>|Vs~RubO&($^x!?KcsJ5I@b({?%W|q!Rq4UkKb?m#>P)n}GNs z)OP0{R>Zfwof3d1OyGNr^boUrj-y9fqipzm(>{!CRIK7}V!q?KwT9}r`-<+@`*Ync z6!cFmua@ExC-sb5JNGGxwv@$e*Glkd=>-1ZY5G>JjqJosXm#Z5gdZ{!lb-#sJ=Af% zt=qm!#J8F{W)fFW@~!UdZ6i`jk+?{F;;SFs6`N$)c?+Q)WH=AjKp(YPKkSY=<)7#; zB9-1iivINhvTPE*$5&HDUq-fHrX?;mrpgLP>JI5f+l=M5m(ey@9v;_uEY~r??krwo znY1A@my2^ug%Bo3U&bfm$P(pByT`SuvAD94xiXYa!*pp?Rk^boq)l9qRVbH&5|E+}?GJ&1{gOh7bHhrs zpJAMfM`FkuwA8FOHO-<(6DpTTlj66Ig6^IREbfjk-m@;E>x0{uCR-{jw(;wuRG^W# zcnN+Hy1CLg0vC;%)-lv7N@bbVv-*3IeFbD4ssu_#B%+DLD)w6DCK44*`nwjeeU@|_caAh9^ zQ~@ArcnwtSI7J{@fYxc2u`>;zDiPe4K%?g&ahRZC?X3V4p+vRo;~!qx0M#*onWLVYRs)b^Bpw5NU5>KJq&sX7zh2-?2)5rw{ue~MR>WYqLV zpHkuyq|3L$Em&(kwWwH^a=9>(p{+WV((ej;4`%6NwpD(U`HD&wX5V>sluZoWcKR$f z{{V{KUNcIXskvnKp?Je68g}36G+PrAnnzEMXm&1B45Mk)XSoj2-NJprRKfC3C}n(I zQoDV^t;Xr+B$BN()5Jxl-y3Z+L#VU6jHW`~Pimz?epKe6kW;+3E4P)*s%jq?YT_8; zVF6iiHg`8Og7B)7m~wXNY92v&ZBbDKN@VW%MsbpOFJ9E(c!L}Y9A2w~jyQL8oW456 za${1q8#u@srn>6zDps@WGXhA{)k?tK6SwWUjWzkIliea?NK9=GHBY=2wu;>N&mT=K z>YAZIc|as|l#Jp?Mk`eVGwY(eFKxV@-Yl9_`r$30C(|7O0M0n+D_s!w_a-OQt5C;1 z*IDh<4W-+H8r(JW=q0asj($DPD%Bq*4R2Kmz0`Z#v#ye=*F}&vryoOrBrPQN9VZjG z>~7UkW~+Dr56ZNyPol15)f66CW<0vUJFo3)5GHh!!amnp#Y^7DigUTe?P(`L)2^H z^-(Ep*MHEso!SZff3i{7@;=e3Zd`DcJeGXJb4h3^B&ce9Vs6iq3R0@Id&MeiM0W4% zE~!yqk`G-XS{&oa(N?Z_;H?U8u8h^&+)$}tjep`x>l0At0Qrnj_8}p{K|Ud&JkmO zcSW|MJ|0=bwcCkdQC494p;ccgHm~cinfZ7A(-`(t{&L-MKgbAI8xk*c`bch1%$dX1 zZljSGO7p$HQU|O}keyPIF8#hDElj=*VuNNp%Jr|8=~T#ZB@)_Bv0l5$?mbk%!!6A!y3a?lio$P`N3P4G}T?{Z59fga*Z{&=B_jvMfbvy61FEW^w>ZmexW!tj$Xd|bpsHO6BT0z9;4P<}$7}WSKe1;#i;&vc#q7x+ zq>2{-;o=H({^lGF_Q}I|#a!?0vz%zFvKzhfmYuvVn&8Dl6f|$BDa9#*fbt7rQ`G6+ zEwD>-w2O@h=vHxeXJs^i*dK5^=4k!8ejW)Ohx^w1w|%;3FiA;cR~Aee_~@ zhq(`LmHyvwJT~P;L7rjv7U^lPW|{X#>R&Z>)v2Ylq><^k+Q^V38T7PRLj$av3H4A` zr>UyTGmN^2B{l0@1dlqky&I^gBg}aJ0D6+%>3+l9DRJXmXPNzr`+~N;XU#4a%YCbl zvn!XWYI*yeLv+oiitf{HJc0_!iXB6nq(CAl@XYAsbZ%i3=vKb_D_Je$m+Bf^;jchJ z)AoMal~Y%|ci%^`E9WKgr|5DlZxpgrxl72eP+IG+m<4%YB}Ma6s#Q{YWJ~#{(^isK zNkxIu7{gx~qmS$TN}&EUEc>f0V7^g*O{}H1DfZia1ip}!r69=dREUdT1&xi7=HcPZ z>H?v6PUZ4K*4o785uqS>o`?17ker3~ufs3?@+<5#*o`{)eab{04r!3jbc*sH684yV|I$rCb2mCoMsv~+)27Zb^J73vgg^3X@m0yVS-%k z&D`wx?b5c|sw#uI)?B1{Or4K}g=j~9v5jPMEc&-YJYb5iT=Cp%n~rVN5|)xGHBZkQ za;F^It0+zP`o=$0!qBa%r%i1(xeJ_b)YLd0e`R+WxmzEm6d}+DW>$a*aXw?l%TE?p1rychU`*U`k}jx#B6czWcFsUN_u&9I)0?) zeVcB+f;mZ`f{#*(r3LjKR4exB74GughGNRP{lCCpn4umDn!V92_c^zf9^67r;<%(t z={L_|8DAN-VY6wcg4cOtkJdPRQ3n?yS4%6Ub5l>WRHWh6F0Q&AOm6SYEfSF9`q%+ST1@RC0ksKOFwh4=R?JBeBzs=YDe&3eT)2{JGXqc(F zP}8=vbF?985*2iTKB@rh;zE!{vxBmnYFMaSs- zV{1yM^#(>O@>&t|D|;>)dMDqBvKl+4V~yGW0AJ{{cdBBXyc9N|zT#1={wjd4U=3kD zN=n7;e1Wy0Dq-h5qn>+LGMbQPtFrsNLoit+hG4d-tx^DtG-*U!Zj zZ_e)UcQj+_+Vt>C2e~`ikGoW}ZRJa2Tz$wDsr2Zx>J7BcrShE%vQE>lP_RMxMH&t> z$vD&Z+LJ6p_)fYl0^=pcd1F1h#)zgp95mHNtnY;Vz1Xmid#%AUw^+IIr+{zbiXR!+p6tL0)&|-J;20gMHwD|u3Z_uWn z8-2#`j#swZu@8Eja@QeKgrSBxR@BNZxm5^0UsY>lAvF^26gBP)S@Il7t{b5`q4@7| zrDMIjwp4kin<4hUjb#=Jo<#Pq$rRk>h0_A{b%mujO_bW(5>%=vj>6{A)V3$^F-(!N zK<$&TkBZUZk{iZ*wvP_@c&OHj*Vr%6*S4lA>6B4ZM(R}Cs8~9sX;1_on282jZUBTo z*3(C5RmA`5cQFjY*Uq$YC_#8RLChvN;>$Mh0ESL+GYw83bEm=BfYkLNCmw< zDh6KKQXYMm5(?WTE+edz2?zH@nkg%&R9Wq)OHocWn&sSmf1=4SyY)^zd$y=9n&V93 zn_5USk(PuW;OPLlKt{g}+(5~N94dfFgnGC^09?Mv>tY`Y6y7w!?HTJ9^_%Nf%; zQ@E~x)Hl@x{{V<-8Rlh;I^x4*sd8^%~63#+ETICNb5@Z+UTQiIq{|obG3h&GAefH&8ca1 ztB^Bj`+Zhv&MrD?e;+osdl#Zx1UYxJVhF}ae!2vJ6|+mUgtabab5OmM15Sp==A zohEzf6@``}jadvEfi9m%BgM0I)X*`)cF zR#TyrEuFu&C!!Q`sVmhItCFtTAxE$wcuT!5~7L=t*E1VWPd2W0Y zU+%dZ)UArls}wxMng0L`jBxhm0pOTCx#*$nqNQuvH+*{!+YUd2z$iAZHSa z7I}jrsHi}4xzb6PC1#uv?WKR`$e7}-q{b}3cW8cxUP;V#>2A9 z-N*Tgu5h7RgS?)G5if46f%ymOMMKPP0M*4fmDzpbm9XNg9ah;11*!m(w^nY~+i&Vp z5%YVI{Acx~O~aHgbgFd!08@1YgS78I-vLY7%Lo4e^vCK&Gq{&b{xka5wmw_DRJe+D z*1GodBIl^7kBF31^L?tvG*@6#c<$g`IMa1g>5Nz2u5EAqQ_7i|Nm9G~<1V}5whVnX z1#gFx+_}FoMpxIb6fOF~sm96%r*gpS^Mfq7U5nfXz?iw^Hxd5;5{vndE@XK1OO2x9 z;-*f~xj>0dE>``dv6NR}{YXgrhj0f}*}d*BB+v@y0PYIUQ4hbeRzKwkhW9d1I-n}| z6_f7-Tvzcs)%~Ug{{XT${{Zp?#m94H^IAtgxRRgZ zUMHS%W&Z&4>%aKb60PjsJN$tHdxbZdUXF0d)DG&$bpHTopY4JNXYr%>0{Hg=e=?0} zd5a`{Gc}ISO*awO=MFQpIPxj5Act{g*HmuDDP#*N4pz844)Y2B0JM1j0P+Oa`}%+L zBSmuE+L;AkuXSGy@z489VtTYE{{Y`od8Fl6J6>Ayrn$tKo{pKrKlXmduE3sWIk-M4 zBU#Ux3KZtctBFyZfO0WGTr?LM4 zApJ@2_pIJ!WY<3A)e=Iho!WYh=lkH6cCy4DkR+aGDe)-5o@&S*@}{xPKiWUv4-VPc zSL6wqd92i+B@Rz8-{wE(9hdv(NRS<{vZu-vEWFy|&2CpL-&|JZTppbCPUHRXOdYed z1LO)ePH#1JC2aoys<+OnxIU?Wv|ypz3s3TeA2&CXz?HQ;r+Q!Iu6-q42J;~(FaF!w zf0Q79-*+Bm3LKk|ERd@APk{?ozqTac;5L=6z=xNaTzQnM#&di9b#fBrd!Q*v1aDS? ze;A#%;a0%>p+Vkqxw`IB(-nTQwE(U4tzW`PJ)+NR!tLqu1xvf;67KnmaSa8;$w6mP zKKjlL{{UlKe~>6=b7{fV#Q=7Ku&n<8Fpzs|3_F0^Q{@QQ`NXvOiq?xoZq<;b=nUl% z@9q7wzknxV=QB6NrW2BGb(WMY`in?ULMNdgD6C!f@&a{eR*ZKsmYq~sxo1}rrI6|C z<(WO8NcbJUi$btJmQ7Bmp}fxKr4RXq865#9`{D7zEr;-gDdkrlWggMx7ZoV(c|^)! z3793EItu)$xj78({v@QLyVbm*xz!o7BG>2 zlqLWfK<2-Oa$hv+@wKD;*_dLO#a(!yr^v2UG*8@?e5^NBQifLH*Z4xFA)BmJQHp?_ zvC7FSz^d-UfxW$PSGUX_&4=-}IZs1PO3Qz%zItd?WqaEO=iYW(iCNdIgaFM22Sssg zEeY^-mS1gcKGI!jz(b9xAxTg@h&^MeZev_E3z{FhFAVX{1It)-?+&)pSuPpH1w~d# zW~D+KZ`NV-wuikmNF=N%&%}wwE8M(PCE?8)_^n5`H*&F#X+Ds2RYv8Vun0N<`Bnb_ zWMcyu(t0gaWeQ2$EukCFs7g{~wN4C=kH+KCJ~D3bC{_!_e4WboXDqyPV{;;_Xa4|p zR*f5AAMvk1K2euDIc1H#?%%ztgdnxyIg3D6`zCVWyCBFibL0>7LImyF_m z$A|CmQ8%27F)=spLUBcDWkzeg0ra#w{gj<;*d2N^>Rd#=}LdZcXf#&!x##R ze<6A$cRa6AJE{rr(CGv>h_HAe4su6$pi0i_(nv2TpOm3L?M8Vnw=DkvG%>usqM!mC zJG^HFH;J;kfSG<(#Pvygu2Wa~joZ~U=`egCS@?`d>wKev*GHDrk+;_%_2^(R#iJ*p zTk{X5sBy|`=QO^pqr!T@knt$|ke}9F=A2G6xm4b=1}UAH8fiYd=@y(16okK4s>Xki zy|L(3tzn#XPg9S#P`39Hfw=2A$WAt!E-!A@e4%6)T$*P*>Z2zs;)u7_uV6h)hTR9@ z5gA@NZ(o!tj8=ACy;OIopm8fY5hK=73;x@I-@>OxYEj~e-=kE)nV)z=xYQKT1h(fT z(A6d`l*${TCVn*$6rfUY=OnLG-_^G7sHp?ran>sLJXw`A3U=R-5!9lP-9REl_v~Vo zk>Uf%S!{VB^Ft8DK^sXreBzBZC9RWcy|U!KE$Atrb-=GNPk@(5u-fsx-WsSYoW|c3 zm-O0|9+Mxu#G5#oI*_5J-_!sdP>PDFnNLo}P&ngGAv)Ziolq5B zFLc)1^EMjyEw@Xp8}zJPTG7^hWbK+2*!*H^4TARWTIUXh1ACL*5oju^Y3i!1*$u-T zyg0R8sGeGC*6hoxSXQc1lu0FBXWlo4U6ML1zW&mk6O~NiaVb&PO+S1}MC}vw#_2@J(VW0`9n(C?l+q;UKQfIaxXcybyB)aev!wGI|*BDDdnyA zN>}NjDM2MtKPYA_y~oNV6gd?%N=7m_tL$t)Oyt$+0-_ZoI5noBN4(L=q{}Mb zz1Xf+x~hWP?xjj*pqZ371k(*VEeT7Eoxw(2KIU#WD(h{@VHp*>%W= ztv449iaIGrYO^Mqfu_>mKmvXdWVe8YNxHfYD8tZOJC(O+15#j0L}D0fI|il1-I z`M=UohQymQC;tE?wm!71^_=zAHoK^o<=z(cps4=yf!`diRCp?X=EGMM@HCnk=Z6-DxW+3YOv& zo}_e(ZWAwP>o-~5kIN>Y`4v~rV)7#LJM4J^c4_#JCDbMV0RI4JV}~y|!dc^t68`{{ zWiB=;tuSG7RLO@hIw}sq6)Fm*oBS21Yi<-hoZ_ zzZlzDOWy7CIO^p9@ynA+$?05hg(bHVpaiA)r>x`3eaDH;vwKH~EA%h4Ee7^Cp8^+c z-)S#%*fkCjONHI;4BP4)OG&1AYe)p8AZ^#AjDAw^UUUA=Wy}P&^kxR2TbQWh+%w_02lMk_uc4Ndyr-pvUS@7Vh>I=FCVYZ7Jp#fG;rD z5zDpBAdiBL9_BsXaWA(l7W0&4xm^_-v@E!`X%zcmYgj=bN`2!H?>F5wjl2fS7Jcif zGy%HxTCZ(;HImNUf>>D#b#us|1My#*-@b2h^t35TilvG@s+vn}pG_Lb ze+oor{i5QxSe$8+?AGFAx&HtHd>5wq{zlc_)s?UEZJHS0kD2`{GuqJW=6gzj{HX)1 zXcsvqvr<(;*1*50R}=Pq_Jv{hJMLDJgOF(pxQ-@-*VkIsG^z+zh(GL;Df}bNec8@k z<#HDIh##}xbsR@f(bk_6T7KC5GNi9Zyiy4bB3Y8t8= zl@nD|l_)8Fml76|k|aS2oLqUIZt$5~7sYc6Hqh%qqK(I7t}Uexjl?(|C|3f%d_FGY zjx6^Fcuo^XPi0i1qYc4lAc`TNTYSMRopN;kv-<@y)tWY-d8 z_+_o_(ER5DDtek4Rdbn;Rr^~suy{{U=UZ3?5_F*mmg z<1-~|6L!k8J2goPy5|i1-#C_Pi*KfO7lf^qjaHcsU(o136c0hSl@0y;DE_l3z!QWuYcQ@z7{F1)Cj4$fanQJ zgLI{&zp^&YIhJIs0o_-L`-#Knw4AyzP^YV&@n|m=^3pC|s@9n#?i&6kFMh}ks8}?y zTs}%T6p^$)U&+^m4eEI>s$nEgKxNt)ebiR*?~u?1pmEL(b$1|aZK*1Mafh+{wpndz zq3U1Ju;M{ViZwI%nDe(P`7R6V3v>?}| zgegzc%grIFB2j1U&)mK@B{+s!HN|Fq%=l%<9hq;d+xHq41#>gnV)|M&aY!KKB0PJ zjpja*Akw3@S;hA%6s_bl_;^6i#=464jOEO6>Y5j@+V+V^Xqg`fk9Ixk#@yE`7bpJ!s#8V-4m3)MK-au&m^-&J)~BlR2O!2rE_5&r30(8u`Ay$R z!0kZ`Y$yRe2?8Q6^F{hEN#VPD8v|WM6tfJ$`?X?}7Dy!PII7*xxUHKJk8Cl}Cep5< zW?pWlBPmrq6A~WlR8m$a<3GX%xHM{(w}{n0-qKI4o>cBL0Vx1|XE;resiIZ(j8{X9 z)1{>}@=+xZ%xAAin=H{+oD<7W$m+$Zi^cNGZ3&eUM$sw5b=nk6uos;=q6@#dy8DH) zp{fgtRueJm2RcjTKN#r9MiVn*r6E-}yZ(5(*z7;m?Kh+TX7xa~(U&(BPpLiNJB+~} zDwo4?MojO_RpoB?56PG0gGg@jjuU7Jt zxXhLJ#;#W3SUt4Vp(-k6y&; zP&XAeWIm8qLPoYSPJs!Gw=R$dij`YjkA`IR_ov;Xvp)N4T*H9}Y`S%f7c0qZVw8ux zFF@h&7g9tvwCbGR9`@tQ_jlO6F^IkWHM6+9p&_83aX5#!?q(!R7#;~mT=1-sVRpks zaeN7Fe#>l4CDD^g^C{ZvY7#RVYdFv{m;U4d%;-Xf!r7Kdwn0LPdBURJq`X^LnV-+3 z;+q5HCaU3c9D6iV2ko~}Pe#>sOusXRN(199BW>f){{Ty>^Yx3)1#OT3DvUP(K$%)Pa1SkgKI3HVe4R_ZD$2ZB!Ur`>~{cu$1vzS%hs zF}L&Y7f@o=HjX>I*IMXYueC5}RYt`%!DT1)K8V_u-O+L$Vob~dZ)s~Z-Du=aZvOx# zZR@7bCGXpt?Z06Ah;YtloZ7PLbIhD0dQCCH4QlNdI@(goDp{?jtfnb>hj$q^@8#T* z35y16HE(3r30b&5sq$6M55lq;10jM?(;v#yLtO#={(fB*cl)=o`;WlA@GqCz zj4V@IY{w{hHJ2KzQWB@BgOz7-d86h?b8@Gyy+kg1i6xny*T_bSBl;b^j1v38lLP8g z1CVpCX}9lfRw`~h=M3lD{{S*#cSv(Cj7HU!@wQ`Z za)y_jQ2izDm6|K+eHML1Ebdc;x>Gk0ph@c$_~OJ}%kDEtS>gWx4<&1kT=qT@2ER)B zQnaY}SN9*+@vm-mU)x8RJ-YED96`nvKHL4&Tdz39i<2uDVd_g;mY1rUrn=nJkhRiO ze<-QcEQDCS{d9Zz3}@ALdt9{xVmykYH2jTgz> zG4AG{o@{N?Gf?dfc(tOUPwgob?%GwgLP`V$CUD+A5c%=XoPx(R(^d9=lG{U`j?JwO ziNd6c{@SQVcDA*^_t#>7f+K@q&C~-*7vAw1yU(G9b%uK z$Hxu94d}Jr-DWN?G3}C3M-+snr*V~2sVWH}NDESkl0uXLC*UVD5f+fuTXJ`$jMJ*M zzq;?YY)jeg_uDTRag9T_WoFnyubEBOp}_aATI;R0wYCO`O1B-U9iz~llgt~NNd+hx z;y)TJE4#QKp}%X(_ZKvN-Bzz~*8AH2%s*}GrS&hQjQ2N9skh3B{S&6VvQSmk8&Tz! zl8o-|@)vSTt)Jx|uS)*af12LrJ~e0V90oa4sR;1?7{$Bc za0VdP;I|LA##b`Y@KqP?JqdG}c{N1LF7sO<)B)0cCEFdNgvx1Ed;zO&P(}aT=yZOv91ThYt)1}PgbQinFBE<mU(3)(b)Ra-H^px(UVYk|8Ar3aJ%HEYEg#tkltV0Dz2c;LA z{{X6oL%K1k=znTeXCC75er?Y4yVp8!y#<2nTKm7!`7e$G78>vrzazEwx{|sY!a$mp zc^y=;E1QaRsAcqi@%R)xe z5^uADc!YBSRT*s!?X6 zBioRSEjN!Ja}P0G+0?qqE^Ti*jiePHlw6-HcT&2nwi|bWs8WxN6>oOYbTtj7(zF7V zC;|kJY4?N&(ZNg@YZ_HIcFHW4>Z;6I?{T)#wz8X}l#bLi*4IBMc(Oo|{{U(}xw(bJ zHPot%ipJ%Ydn-hjv)?LzR;1n397@Uc9VUNYtUBfvii86s4mzjV{x!rhTte58;5mQw zypi`DYWah@*QZxXq#YF8kDV&)Vm=&;{mrfvQj_A?==U({nES1Q<86KVdfHXe)YB4| z8d?feli)(NiMU=4s;zZzO$wAA+kNJ8_qLn86Lu?^y5b62P16#racibpIv+}Z@YV4e zd?GI`$6&i36=LH2#_t;-)~#JE`)T)g#lGfo-`8UI6x*^|6eQnqT9qXgRK$*;e-a7k zp`>Ivj1;zy^9$14cb8janO6M6s&P4U5dQ$F*p*HvNpk%fDprL%T;HE+Q8T2pFq}>I z(n=ggseFSJ%!GR?t9ymGofyBnONJ?R2))x#TOMq>>QZ;1Xxs@(fgKENoR2E!;ae*W z4MOvu594fSBYY1Vg$jH*>)5s@nzvPYZbR_GUNd7!_tq$CPz_Oz&?S@WcigfR|HvKl)=6ph9u z%@rDjt?C23ZJ5`fF(fFXJ+n$Bb()#xkbh3KH2i^WNj>6L%3{?Od1xLrL|HXU^(a*Z z`J$&3nKt=OgnZ)PZ*(5Y5U6)>x);)s9%I%L=&HIUN6gYKXxp|^8kiStJP~4Lsdm#nQy^iv7y(2!aU4crvCsPTM=_Y87iLO2aj0KGg!}TlCM~M-*8b#_P8Y8sSxs* zDnh3twtc=4n_XR&JzHC9UK1^x@Oo^hb;Pl@Kvw3P-b-w*X01DswG$u92|dia$}G^^5@|&a>##Lam)rLk z=RA>HLycgVjc!|z*8c$RwW@`MKJulhBqyL9$&e6p@vQ={(M8FXaXVS7l(pqAWnSm_ z&mMav&NwFfV#le`zNYjVW~qh*r8mDwRDFOLF+AG<6^kmlUDfsut9JvuIZWl)yw6+rYqIQ!_$7e1{SHE4WD0DlgUwpo(CH)4qTgB^Wp2WH}W zf^-mlzBH8|b(Cacq^VnhdouSw$hh-4W&`d204U3`mvv_QNo;rY&Lj!$ZBFbf9t8k{ z60u@q@o)uK=FdoVV+jee$awc7#Vc!V87+pd9=zL>q*q#M>D{J%m8Ym$9!gXX(i@iw zh_lRxj;XV`GN!_&k_Ud{te_b&lv2#Tsp^}1r90|UibSWR&Yhq)S&*h}uS1GRy={GK zJAwF^5%z|vPCK@Rr8d_TQV8kP!$#PGr3Junsx+juyjBRDhe?lwBwDUIKmt`!+g8$L z`Hyo4!E2&}xVr_iL0QhD3;)dn-bhRq{#dt+laijxo72?sxEgMePsGfDO5}>EhPyu+&e^| zfCT_%HAM9`wo;;Y33&I?8TNuCT)AT1y@1P}UV}s46m4iUPcm{;Gv}&gGfz>oLIEUv zJ`r2VL87|gpQ^7*id;1XGSCw(XV+M+wj)(%FDrE`E-7D@7wr}H@|QMCMO9SRkfffzAc7*ll)Z_KHBz{oe9s`YU%#}!y0z<% zF*{Zb&UUK2+TCYD7`A1!%R;UwVE_c7d;1^7S__QceF5|L_m9>!|SG2841FVEa&Cg zZO*Rw_H%S{?~q-VHmL6O*k~UkETCwH&nx_b*jsU{t-b4G;2pu{ z)PM}MI_P6V<6Ns3k`;nb<4-iEcJlp_0OlGJuTSi?j^npZ4azbH*>0luSAVT);PQmr zA+nHwXRNmLkI$*N-M(|5-5cLfV}NKqDL|$>`wh+}D{EHNH`}l4SBT|?*;!7u9-=~! zu_Yj+5J3{@9;Sb5F;@_5FtlmpysFH!@xO3!s^|Xz?!D}z0=L^JdBYR5GiAKC@+q&0 zVF9#MrPXLul*kni9;t5AXuRcdlx$8QokC)>4nX}X-DR~%_SaHkddrNBG*CCZh>sM> zRo^PFFRe~Eb{`WL8yqWGIx3~^b?zt)eKLI#dXOSUdo&Ceq^9n@Ww%07bPybnP^ktw zp%SUpAA~yAQ8d{oie7!v>QWAd5MoMY!cx$oWgnJb7+=$rJMMK!%g3sRdICIVNAG~8 zw>3sv-GIHeQ#|X5CS)G6ghga(l{NBLB7~*0q1GgYxlu;bW|uA-M~sFM{ngwM?bdz4Q$Dx|%2SE=O(%uIw2E5Rs;$qNcwAL`_yH+*|Q zh7f!chIa*~w!#@&Pp2cK{{YZLPGZmqi6Kg2E}UcwS+=KHrboPBm#qH)SOWAlh%U0K z6Hjev!c(+({Qm$L*ReLN*bNp%nw{a~RU6aUn{epZAP)$!$7<3QXxxyA@3usyjn!<~ zFlK%bsq9+Tq}km($<;-CiIUZ7k_4u$e;BUKWXjQAZEi>jU5i^4R><;`F7iLyA6w#@ z;n4+ilr1S(_AsRaLJEF5>jy{2m^=`NI7f>5ixmOck)iDb4m}V(5He=n=Aj?Fjvr1w zZM?%{rXdV$rOA+q>V+90(mn@5d?SlyUI}z5Y3<|Lw>f)<skL=WMV`0_P z3aXmKu;QL;MQu*fNC!x=W4QeD#vI;?g~)luoutM&D^QEQ!h0e2E5(>S$0fbsZd@$1 zXi}^06fP#IEjFLxDL@LB(ieNjt|Dz*o5b^58>fKNLaJATGo14P=Zr@z<7(=<9NLPp zZCG8RLYGvhs)1lO+7c$ED|CQ}9DI+CQMaklTQkwd`$NB{Rn<=(S~=4W!}02ma(R3C zCn{zo%bis#%1Yd~QMzW$I>G<}NlDk^8pb-!(YMvquQ=v!+a$i9lqWXNE2YJ=BC_Gp zBd=IF%~7p&5{9J}6qVO|En7Z{UTivquR}EaJ~NM+T166^!q->9K#q6E>Kjc%?4<8( z?o5Y_MqWqis0K|T7$TVHbig6*-D*7Wuf=S zsLaA!CsLWqK?hnq5kjJ}FbdQmxf(qn`{|sVJtkwbIP^iZ?&;M-u2aVD6_r$(CNo;Q zPE~9rRJF~MCkIl80Q|{67>K{OEz~Leb?C0w4HISm0KEpSh_Z5Z1DNG&U(5ci+DhqM zS54ksK>5T>nFbvmD6BDJUJmF^?}CzCPk!Q;f+~@g(o`wk5PehycvxtTz=|P>J(AF_s#d5;=Oi! z9@kM`@{FXy6hZ+YO=<&3teI|Zokk56h}&9Nw{(uxZEG8-#=rG^y1g}-JDzHLUW-jU z$L2kzB3t@JdZ%`^FW>Zo5ctmrguBv(%02Dzi!TS2g zDqa&)ivC%jZKn z3nx-1q->M0QGQP2r6DqT1uIMS0d7*CXs=vER-1^O!kVVV>I8bE%dbNKZ7THz*`iTd z)G3@yh>+-?9bu_q3U=2Z;51LhJ%3#zDJgeh16hCD05;f@R7986Z4xg#&FTXVl{`R5 zJEnBkyhG*T0fk+7TZxn(ieoH!>up_9>gqNCugyP7Xlcs^aVSr=-uxxk+Jy$qR=k;zs8^Wl3B*-JjfkYWb==@epBZQm0S6i zabJ6{>VH{TA1v>k&D%YgsT^KS(0;_nSZS#q zbU5gm0;W?wvy0AB-Q;Mfwi)KR+m5P4;>=l6SRn{OJy9X1y<<1Y!8WTz$7LcRTc_KT z+-;lNKQU5ei%2$$q6ID8kfefHS?VV(LDoFS-P|5t+UhK=lXpH1Nb#qN)8H0)+&l5F zaA`WU{8wZgY3~z;v-=$t)`v2pr~N}|QDx`bLcx}^1jf#Qn#pfhehOm zQ`ijcjKnr4J+;uZR23Y(+x^ZsGk|cr_+*u*5dBehRqmx5Xb+slq`d z{xRrlD?+^Se|Ms8t~YS9b?MVy(Q3AjN-AyCn~FtN*A*#R%i%bif+__GJes7fO-U-8 z=MvWK6j3v~R27Pnl@BzG8rN!~rC=#rZMynIO!WQmd~%8?scpa|E2(k?35m*m72H7$ zlDd==qdp1B#kmUWXmtjjt!<}G0n(KS(8 zrw|mK!*P+Ydxe_4E}<%I#@3aj%0v+md2Uzq*A1E%P=cp;ZK=5i2}N4%7aJb+IK{hP zE8VE9?Eyzv>ejFfz}%_IQTqYdPJ1q60>?c1fM1s1a-Fq<1IS(&0BJQ*rrE@ey-Bn5 zo_^}v8+EK(it|nGP&6HSCTo#+aSSl${Gvwbol_&B)+`ZNxQ$hE%aMBO9Yq#Z#MDmI zI6VN8*;K&>S{*tFeVFE-1u=2RFo`aGRaBZ-nLQ)C*A;8KY-s-Kj zfNW8gve&dj<;_~9F+z?>kJJ{IKItI!ilekNS2)-R)j5_-wKU8Lxv@wcC)OwN*+(-% zRd*$d4>d)ZMt{1Pp)LQ6!OkZK=^f(NRXo-d+8CmtLB!`P4UcZjd~ftOr>vn!5{GnAOkU}(ku2l zg>$_J#ZT%=FzZywc^*X+%pU>sh;C-*3l~$#O7Rwdt#9HJ`D?PJq4tbR*!{DtH1-Zvhr^y7sO&r$`l0K19ZoXgh27T zE2DhSYIRciyXYHi<3Z6z%$n&;#dXf6icFnUB-XD{SMBYhBih!uiX(-Z{__lK}d$+xuse6+n&_7?2b7y3%Y_na_bsSF_eHThcq|d6@H%B zD|q*gsyPm~HA5AZ`3o%~m-S5{^sTnSiDV_fpaO~3C_LHnng%UHw$j@|2#^A}+WU(8 zUiTky_czR3*MZ zh;b^7ipaM2DCfk&!TOb9a3&4x`zQ8)&AD!QpVi@>>bQBj`s{Zbe&B>s<#dmtRBX`F zktG(D{{R%vaXNIAMA|kJwnmx|tXVl{HHp;JXstu-wf5QMZ*qJ^fqSpue{r{OZM>_C zp={dnYa^?1inrTzxSCswO*q(bm23gl*DyiUbTMepWMgl(3qz`b;QVf34AM4Dt`!5} z@$>Nf4?|F^uixh{`-!{z*~o5Q>1yp4u5#h}n>DK7?Xi@LW3;x16LDZA36|tN>P>Tz zAw)v5khV54u=-j?pQlw!$4f22bhjS21M2wDsj-;zH?R-5MjevTa^D{@?8n^L3?~NU zt3ysz;g|HKw6v3OZ3$1J;!0%Z;F$t8teJ?Au^Qu}}Aejv)Mm9}e*1x|!E^Ggk)VqJDb%gn|CTKcf4*}v0VhRX;K=}}F8 zQhz<8yY~q8;mdaJT=tueTe)VwBozytWh$$nv@6sZ1 z&^}OFHE)Pg`6+A}Lp5V@=zc04;r!K`(ffzW`KG>xlHbU8e9EIIzS#viZj~2QT)$IO zP3fJ*062o3WQ+5Qi;jAeGSCeoUybs}F&6Sp<5MB%h3DKR z^F1_H%x2cW2dMl%;JLHpqj$zKtcHr@7FR)ESxoa`Q`%|jR?+G4OhV)`f_HnKYVm`? zI^F>p6x*Ufxph_83wj$58-&^n>Po_f-JlIj=L1WUhS{Fvjty_cOaB13X1_k%ZQM?j z>a7zJ;r{@8zU%Ao3jRmOFIk;dA#SCy;5l7=KEY3DW?qv@Qjpl%f&6AQnEe$cvh~LIN_HwS zS}(~$Q(w^Sdf~OUrxe>MJ5QAxXDGB{BLZUOo$^-xNC^k*viRi|Z_Sk~fQpv*+b~H}t)<4a zrAhu`NdTr`Odhch&29I|82G791uwU4GmnExubNIt_Rr09)|K*C7}Wb+ zR^)oSTymMsQl$`oBE*UAA=xa!cSOY5Al6i=QXeykwZ&-;IJPx804o@n6unp9^TS#h^e z&AqubDyi=l%8ulUi(21E2mPr?L&5DoDX309+C~~Xs&qKJlrJ21^=sCvRkpH{?9&Y0 zt+n(FR@1zeR9216z6^-hQLHVju9Z#E%r&B(d>8H)1<7(-#fqbc?Ko}!05rBeZ@FKr zPgGGeQX5HmDte7dTSY{TNR&5mWxchop-$qnHLDynO!h{G*{rW9My;(uDIe_UAX6T? zsV#%8714dMm>0ikp6u;wPzbnGEtO2lLEAV%pW_)1bgo@%`K{2kr2fU|9@E;3i*pg^ z=MU~hZ+jNuD~=f54MmoYk;W;)O<@~fQAz~Yzo;|sAB{Hs^X8Fdq_}O>TGyXRJ{9P{ zNFnXV8kW(P(pryD>N?lWRmMlhO!0F>DODZOQR|ryd`A6%IXSu}L+&p(O zk69Z2EAz{`+$t<4T1X=K0nuH5+4Bm-GCn8fr?1v3Tj9N2=_u~BwTv}ThiOwrr34V@ zP$@gfmyA2fuVii4S}E=kkBRn#kO*>#zjNPa+;{EW%y*OcvC(73^J@2Kt~fbfxTheRcNpKT6(62(>q}AY#ie`|P~q8~ZY`DC?-X?kYNml#GmBX8`^yGPa1bqLTNFo`;e$=5CkNqMTieP1HKvk^+?^?=tFV)(EnR<){m} z8Sa35P(EGZJd2JmPg<`OWVBjC^c7$d%k<8T$y!uKxs0NU2j z{{a1a<+an*INB}vHEY!93NlNj;EY{kG?4Oi9?C3(XXITm(Nl5A1G2QFX z`)i>IndR;f!TFNN{g*Daq4xq*C@zQqRMRYt6otxvL!pW6A0nR7)dx={N#&d(*CQDY z6nC1VPjLRwv#uDce&bHkulS;j>2d~8uOfPcyn#K6{f0KI7IxbCooMeR=6vrJy~Mth zt31?1H7_ut%}Qslmv>-0!8aC382q3_R9Ax;dwT5Yq&X&)Z<|xo4Nqlc$ zzh;`O&g$AoBrQ4=-ZRDT<_%Y-)ktbrPRib>WwaHnHi5XAC$R4kx0vYMjK}1qX{Um; zpSS-2urF^s@9m<$Fy!1BoQC<6+%8nKccm>s>S}sbnsoXqM5IEdesPcT47s(r_QyrK z@q7rowD%8EstY#u<4x_G-fhc*@<&lyWOAVj#;e^Cyp`8XB*gF)LGHI(Ya(V(N+{ zqpCnA0MNlZK?#k-B|W_f3P~qf{{UQIDEm5a zFCOvtw7d_MEVNBlW_-O$tBv~jq@hnT(0NTFkqVU3gcMAULA zZYApHc&lcrq^7T>sG_Z56_j;oQ*~6Xp(}01);6gsN~Q=P$b%J_w=mQlRmND__LiQ7 zahLZB;p`iPecol5mL*Kp7B^7M721~Al}$7vs%ZBu$g~bsR8U)-n$@^0jdY(vL~fj8j)<5ES;k)` zUhNfh+}cG`&N|{kiq$f7i=$&`6i|ZUqK2(Z+1j?|;jMQ?+_k#fZ?sU-TJ2OVTWz#0 zrK&m>8%s>I+6L662tWV;27pBg&o4D6Rj0=b7%0&|sf+JN!5m3*_ScYerN-MyWaPXd z`+GJ^>uEzz+|gNBie*u|wqBtrSyBm^PTvW|&x_tY?lHSo)AcGJEwN;DmAVpX_^vZ= z{(*hMl!Z*_bng_cHz+#xk}jW^^^xBLa5ko+)5An zMz`+TJ|a*f;1yF1zaRuTl%WM9(}x5t0W|yflwy+KVJP{mQSW&os6%T&5|GpO!Gbj# zEVL+VPNs6{@bHHk-B8jxsA^tpCSyWBY!eVeOClW2N+txg?+$xaC@0CTieCFZ<4Y$m z^Pe1X?cw1|ZOSfm)YWww0vl51peN!15ilzpCBP_?54|{HI#qZ70ES`s?gNY1@cdHP z)tJ(ukRq3Wr9$+wC4*B5mi7d0!XZ_*?-5Pyqa zhwB@>tIQD^gq*2+bmh}o{9%y9z1pA(dt)m;B4RtdQ4<&-g$y~-?^TM8Jjw)qFdriFwZid}V(ivr>By9mGKmbZnpUC$()S8v4!4nuZv`{TM72?Zav0d+39cwo0 z6|$YzuGO_FN*HdP2r5}wUXqj`yfAy0^>Q>Ul zGw47{(zy}{Ab7<$k@hO%j>+{lGB{JgXxtOOnL8VnnH{~A6%N&N+r*ryp8c);(B?QT z;Z-?D20s-)^k@zl(c;nvB0CEO9OMSL-9Rn>mgsE*l@*7$vCPIRY!5=NM+}g+sl|kCtrk0f^ zZG#{-lQBN=W$m?=OPca%u6?&hZ3*2{I;}U0aM?IU8shsSMolbbjAeD#sXj`JZ?F26 z!jekV+DIzxDE&Q$9>#zv(Rd>xm@b^t#X(%crNvy`2dPmK_)bwG+i(;-w*|P9M?~Bg2XbkyC;5v|9sd9?)*@XURP=H!4O1;?CS695 zu++(!X%tgDRJxA6BJyC)^6XlIl2Px2G3cNlL zbWHT9n}O;=Zq~#T`81bEkIXi)sa!a$fhB7wlH9c1B_3?d3SR;7UY*0R{Q>+IiaLGX zP?9ERIN9+6k$|y&L;S*(RCI(O`1r-50I8A!i1Hdxnn$K40Q#B17X*-%W+?Rza)x(Y z)MYceN3V=m<|3-=Zcfzjas9lxOqq|$2gW4vplwhYO4T~bwxqUZksve?F&sfwA2~q9 zydiT+1cVS8)X8xv70S5HHeJUVb%v^#IMrBPOwUQ_5q1~|nJ(&Ttyw--aXq+*yG15BJ<7|4D}0g27pfZ?Zf+u; z)`#9hbrnvi?FnTHGa!5fqH^KPX{B{l7^9MvqD5iZ1r-JInbz0pwN9|5D@hPFhMY7{ z?gZ+gW%t}ZXM+3Hy<&WaUgcLGa(y}%fmgUB)g?m!Axv)R=9I^Hr~O2+-EBP;;NxPR z!+Q^?DLu({3TyqjbyVOx>Y#%yK?Fsr=8k1Ts9MP8I)zr9?Nyv-nF^YgmHiszc2%8r zq$taAh*rt5)GEa2TO@xJL3GI7!?G4H6vurPN`r`SA1azrpHT%qK?|yY7Jln!q@ALQKm_Jwk`pC0TOWS}37qwRBEQ#!+l_mAI6R$sp}dQS*T$By~>3 z>P)6Xi(4-(t%~{<-Achqg3^^wy!8+?wz0{f2*|G`iPZ%%b>7_Zs>fFCzT;70b!KW+ zJH;cfghxA$#y(R;ITtm!V55L{;u6yf;l4hwx*y4|w+yi;9!duu^xGvr;@AY>`z%$o zI=ZeZBPX7{xzai?A0+*|$kdh#cl3NV(#A7xNZM0Qgog-}wOpo7y2PBhy^Px!+Mwa% zk9+q_OOgqy`kSNg*H)~(sod(6PgB-rAe}RwcR5+@?b_CW1t_*Mr>>3$+L)wl%YS77 z91Je#ihSXz#IsTc3Rkj7NE!hT%JCRb?TQ661QRD^cU=$d@nCpGOlgg#KE1E=hY=1O4TLehA3p^f0C zHAdjeiFu`!kg0>L;)8Q)9pJa@c0e|or9~EDc%}ih6$GG2ndoAhkmAgc){8^N^7qCl z9FA%7`qy+@sz-*VQ-~QjV*%)_W1PIUl}f8_K>CV9iwc~)+5r)l9CvSm?Y|~-j-@$P zYogaBwykskMQZ;50va_|yni4h^-{}ZAhC-SVUVk&;$ zjOBH-tuhGyL+X}-TAf5-8QeY^?s8oNN&FY7_;(=7TR;Jj(N||Ua!qsY)f(HAmGy2; zPdwpTweCrccei*5VQ3LX8a@G6`PVnQduaEn{mE~I_g12^RWmY7{xRsRW;lSn=a$Te zo~fp|+|}kAqTgv`6)x)|RF0~w)R4ZhH533Sc`hoGb#=2QW^?>vN;pswNuo|aQl%LP zl7=u8Jg!<%P*e1W%Kmcacp+mupjnZltR9|kMexb(y06}D}s}$># zeu|W!CIB8Xj{*J5;Vv#-E+eTSevNh}OG}JHtldCJ(R8BirHM+E5omVpw&27nLiz3bAsb9`4#(P zU^{B3o5!uV?mgFc6a)%^@l=U-qPcS3WmkW$1>e;;OHKZ5pr4F>`1W&(vkQuj5H+T& z@Q>~foXeff$tgMlXryj2?dN?09deRnq)KDA&_?lNGb+57mt?LqOs6#abbYD{m)fKt zZ8MP2Onjq3#B6Y)$?{UoXzjsPWzUN4bazV1cIeEIwE}j;&-9l&r$mPHrZv8 z59DvMrx8G)gbf^*Q}vF2KzRt;KIL1r!l|~$xW4EvXs3RZhZbcrnaK9-5lz6zC91Pt z_Y&CJ`cA5B;eOIC*+m0o$`$_rGy|e$1pbjW>qa%YtfGEFcKJ6lsz7mnu(vFEH4VaAg#zy>mauc<5AHW<_j#w!xY`mGpwxjBJl0X} zc8g%)d{PLSDm7CH%y!Jh5|r8yB$=LqdVHd;FC^TxnJ|zxD$!Mky({xrordFarl|A_ zNpV9`C8YazjYp48(;ZyApWLf=i?G=IO6M0e_hWEV*9v4@7M+S(RpwOth*j=VHjNdd zi}uEQfYR?3UA)E2b-86im^DgqD<^Q=O!fH1BPV)Zy?@*O2^Kyr)YV6~YYMuF4$%iw zxoJ*?6h8TpGwBV;LLwMXicQtjDr(k4xrS(uiK3|uT=7*=Oy}|Xz*APLVW0-7t?a3! z{{Wn>MMhy;0!Pesf~ka{bW}HS~{tDyOY+akZ5JOs{Twu}n_7%>8E;Y;p9eu0(35mByh|wy)G6$g-KY z6$V<(BDb5L~DusBI!=Pk?|j5C;mW9BxR$?G!7v zeZ#mPAz^eEY>B2y$xE%eCO)LZBzTv*Kq?fJoU$h9;sH@F+}2m@SCO#Xg6VUqqO8iC zrg>IpX_y|dsn|+20BJF)FxBT8aHFY39^PLA{U(l7Z-nu6>z1?ICmr$qk=xEyke5?t zwlbupRSIPVYSqG&k0_ZW$_~YGi@V2raa7S(xfdp|#zZEZhJuZ`H-d7`8@^TJyt9DZ zaVzyQJw?-3vZj(+m*zu=niQ3Ig9*uT>#IXYj)@q)TVsCEVQ?v5z#j8C@7iTk^?utq ze(8hJRn!X0t|X^LZHBU-HV_u|6Z1yxqD4vCPMX6pXXS_hKy^ISfx)&%lGknB)}BA{ z`1MialsSKqIe(cmE@kBFn7!p3hhA&4O7@ydO|GNSLn-vz69_^=1d}*w>yS%vAaT$F zrLFkvFvGA94Sp*7mt*!p@3xPJXgIeaws5aA)$!$5{YbQ;nN+l`WwvtD4Jk->VpbhN zDkUar8i?08CQeJ2mN@S6UViR|Hpbba;wEj@jvjqFrN&|G_uWqj{iyQ(TgY-c8lJP`&*}oGtCAai4z`d=nma*PaVV`~ zt=I&RKTzdxiz6)=Gdi@cf&J!l#}9j_<3;CJ-ZA6OQ)af2&-3<4i`2DClu=VU%9T%0 zJz>2|Y0ad7x%K%)n~7V;6}*wUrL;O91yAnOliXfIG-igPq>}mTvj%Rz0Gw#1xxdrh z(QgEeY0*HdH}y5=xm9iQrMjENecI&x=Ap6+PFBBO(iu8^F zw6Zu3wE5I21-usa5_ERg!4jS4*$e2A;@n54In(a;}jqx^S zW?+2s{I&31ier3PjAGWt=^1D)rKA9opDGg}(J2}M84MgZA8!pF;^{o*dd^;%bt#;TS;*aN@p19|n zctK>^+(v2w5B6{1%-YL>@-pvsI6d{t3<(e`)AoNu1w*=6@1tfRGDtDSi$sbH|+ zORkj#0VydnGMIt8CEw;w94TMHXx-=gcVsrd)v=Sd<`3fAuG8^IejLT= z>8;E(wWI|o5TQTJO!~*I;2t4(rt%TA)mw)#+xYR1YkWw>4Q)Bft;?%b z##8e20K~o-(uqiSMNQ?7wRO98eBLTJ`wzEcm!3fP1!lfng`V4d<4n4l%i5Y#sH5Lb zl3r3#H4uqRfF?vo^H;b@6NiCH?ft7Hx1EkO+|m_S^8Wy{-df>2XCCA%UcTSYd{uX4 zCg03lbyB>J(4{tkxfQhSHeaJq9eqTU{{XidXVr9vj6x@*l2 zp+L`%d@rb|cS?;9;HJtw{_;+1WNRMx7iq@*)nhj0w&gv)F1HE{lACA^f6Cvi{)I@; zPHIbpm`Sv@aopcY_$dRFybKJJ^BeL10D%3ps(5(L$L#0ajm;v~|fIN@sfZvv@+2YQzNCL1r6kErzor@9M>wwlJ_=cxUE@gIV2IoI3AEb!$u zmo&a(sjR^=AXAL;#g+DNSjE7klC75t5~MU_odfAXGmzFk7i^K#9a3(w@<70Px6CHz z7UVp`8F{OoJ(gGFXq<}_RgeCSZVhHwO3NxIywe1=t!beMNaK*vz?CU7Pjk4weTn0C zZdv1sD~dn3F^X(h;G&X9BeMJj%ZKC}$y>pvMI7Z}e!%_6=R6gO=h(Idfw!Jt@`|c@x*8|nRXsauWd)^q$3mb)r=&H?u^;izces8^yjSuYtGBafsasauF}F&nt!^|u6^4?p;xhBAsf#BY z#S3O?Dty&1mS&%MCtpvJhIdyKik6BGm>o?20J<#Ynks(so*Ol7Q#Sj1t+=hpQVKPk z$T2B}b!6M%R0kntBiuTLdo=k55ALIl@eVoI%_k$%(J@bCt5T8{>0zP01w*=k@`Dv@ zwT#%g44qh3hk?mk=V5F_fbbG4*asg}U_SM|!|~NiYGSg%f~l9;Skx{0l$eg>*CJbJ zq#h)G@T~kB6m934Bj6A2Di-38{{T+Q?z9S7Dq^y!t-TD;`oaCtp<2jY14&sG<3nzo zJc&}rfccJ_hB>KOgVytMVY)|{evBg*4#4b?QS_X*T|L}9sJHF0Wn6&`ETSRQR~AXj)HDLWZ+~?XI5_w>Lr%SD4tY z7+UZMDy03-`v-MRT(d44q%Q=8Jzs_-&2!YK(x~yTI0Ooj{%c@wdbx=+NmIU zmfQ6to;!YLiG8k6;T6FONzQtKG|*d_b;N-pbWrFpqATxMbUdrS`+cgZEV{U z^m>YShOO7iH>qw^=&h!GJ6vU86s!^PgYGSX2v*OkikI9+A>(T2?^sjnxLJ3F)MM@_ zl{SgY`bWdz5dzwUNWS8&R%Ps;-exUA(s4Wcs-RQK0ZlvQQ)^L_jN&6Phc9O4ss2C7 z#824aqMaV#J+kq~x(rb_N+;25$(d_?mnEkND%?hhbPvv--2mV@J;jsuKTxLmKM}Ub z$L>e-Dx~)J#`%Y~{{VDXJWrP_{*EzQV!E?Sf7queUQ3NlD+?lB5_Bg>)9>%TwkA3g ztwO`bB1<_Q$wrjX9DTHY;+z%3m_BL1JkOG~fUg(G@AY_2M^cABL0xUGxjRsX%ZLYS zh}|h3qEd|SmvP6fcU1)sMjim^WIzT~h@aQ9`$e4oL$BX+ZK<*}-2jFl); zY}eJIu+;ehkyB|;mS?Fy2$01=Bo@Jr>Z5Ykk=$JGXGH>e``qUwbGoe!<(`ct1(`c3 zsMt!W8%63Fa2tQw@>LP4bGw@6m3azP(B_864%tOl9g0<8ErGxePPAKrEcu7 zi5|oE$5I^HqAP{StSK{>!ahuZkhcoOD3x~|AU>rSMG-=4mE6b4^sU75I?Gkco~ebD zEw$*aWiJR8=GC$_5os%!?G*XN`L^KAnTFKqDnbB|QP)VVJC4R(S}P-SPuohSlQ85P zrRu#(Tk0rUzu|-Mlvy#j_D7`^b0c>5gQ~SJxBf3pkk?bURM2zO+E+gF9VZt1$u>j^ zspDh!Cm~voy51kP4kx;LbZL@l9heCke<;8hOV4s@9+gD$YYngxI;vOue|^z1KW7#z z?*;M>4^_!LVQ;zC)ZxzC-esGep*1g;9cX@_ggnVvDhZe=B+6q!G0FRO`?vd%XjNy0 z_i?!R{h9qu2Sqx0JMYPkqaV6a@jnDzRh2%{(P_EgYUrAvVdW%*t!+`bwcHyIIt3+_fJouWYl7w-SH0Cs@$Y zK!$YNif)~)dTIb^tSF>3nxf|`9+_05fJ4Val#iUgd)9CNlBFDr&!1pPUf6Kgp|{%nB3aIl81Pc zip{b#hiVFky1kK(4q>4R{%rpMf1HBXj-`yZ+nBm!?6j1djlS;NR8G6LTh!e{d(a8i zK$w7=h%|J<+OB(xas!7_4Cfy*D(Z)Pi)sV2?jW|a zYmf!iLmMvnq>@izC?&e2_#tdDP=Deef!L3X6C89M6n)LW@kTWC&hM1wN7fz?9<))W zVN~tlS#9G7zFRUZdbM`j-J#arw$`+*ifPuBxhfwJL`gB;ov!Gjt{m~E3yzTKT%>6=4;<~mxpEziBQ1cf@%lHxZWeDALBOmUa)HA zMCF{q>m}$TYX^dX6_+GT`G`m|m&C=b(p6E}8LB0A=#ru|hW8MU5&RH0E+|VY+`Xq+ ze11?I(ts3&wY!UyMdnlbhTTd;&Za^uSJQ=g#M(G0lTmFdc#$cR27}=h=g1Ybz-W~A zYa6R|sZUcbzlM;@vJk?}&olul8wn(K){2s?ckS_wdz8hwPS40(6NB7xTFXj{cT^>% zqJi3V=(b;VQbyGejK^4`B?7Q46gJJPyF%l*vzl)~|!>$jh5h+*V5 zR{Kf4wDJ-{XFug2nMG>%FvtZKMETTwdVM0HcN3kex!Jj` z(J1ZpB}odOUeN&ich{n}+dNS&@q|*pccMeYZ4Wm&nG!b%h|2r3YcRq}S`Bue+_jF| zTu1fo6Dbr1k#oLvNye>WYLk%V|+@ML+-mpLoc-sm?57wJqE#LDHd0 zVR6#SAZtM+ofj+LaZ9C-lsTn-nF?S300kWO(JgfrcKUS^Jil0MASQA+t)u?{mgzkV z6nZ8ilhm5Z>QZWoB_d9v;Sug-RQ~|KqBW$Tn`g@lY-&DfXizt4ke9$C>m2sNb(83v z8$CLIP5~1nYX-vFD7joWRVHs#sQpL^?n(Wz%aDnM)kl^pV33ox6>c;`TsWt zk?0KJnVVGn4xw6jjnn>WY!jeTW+w+?kF7J4K80=e8h|VZG- z8csR96ysshNz6gS8dlXl+Ws*n9yWywGy`!;THuxSGKk!#$WfSa`GF*^jg7hq?Hyyy z*^yh|ek;`2QCxW+1P0uhs(vxIVt}p{HWNVL>Qp1? z6QH(b z)2EC}s?lNwSxKI{cth=VS0jxOWezn-k6oX66H9D5SJS4#A&ZmwW~keCQb9!-KG!^r zMaLfIxi5}CT$)BXVuf{98e(MIwYJr$ts-(8l-k6A@PoKcMWB`{UvzO#a~m)n6}INp zIZf8)`!ue8ji%J0>VqXr{@BnZ6)PNuN)1y1%ef`Lf;igk%G`xqw(Rz)BHvC9>YJ5g zpYW;LN{q^ubooH}3&9k6bw=Z?T|B^eE_yRwb>!}C<;o6L&8ppP%PWLp*syg*hSx|H zQaFrSDne1Y69MdEeaG_DRI2XsOdM~fGO3Qx7>e%#|sZQ!@ zw0W6zCs~CJJHzdvH3~h^k<=#6wyZK;NpvS~KqVs!e2cA#r@HyktFV*=#)$^dJ5Y!K>WQT5^I{3YPGQ1(36*k zic;YQQW`+Bxk%eHRLkV`q%`D}uVdj5w@uYstiD81j9fiqx^gqt4Y6%d*#c;sWw|sb zS;%~%Tt(!orRbsObvna|r6;b{;!BazHHl7(^IpQ$G&k#j^$(lr5OSSXiHfw*AZqSJ zdVrqr1Kdeg%X=<~BU^hc&PqLCBR1Zu%V&<Q>giLJfn z%Tt6j=I`+tn6BKom7!vA`y0^_wVEgp7UB6oVSoOEW{LRY zct`Y^ZN6!dNSN1A-WfLSCs3Yiluwh&|DTaYN7Xt>hW zI)v3Q%F?A%AL0o!F$tRDO}U7*riD)8Ib+~`Tq>+f4)@iIVs@l8ZO0s4TPOemPQfXB z^wK>2ko$R-;%d42RchA#;f1ZBVr+B&0Dkch?q?{*Z6id=WPF2nz7pkl zD=rdA-QVdG7M+`AsQk1Q*Lvi+G?6wH_gaPSQd=b?>Y_A>HgHMU)f{>J4k-~^TKbf- zTRM_N=iUf!8J)ffU+$&=sE#Ktq`75UleD_dA|Y+uEU`%Qm80WU^+S-HdsUJ=)AfvlqHLIQcI|WBa#| z5r(*jejx|D@*QqO`?Ytw$+*?LqCqMpB%cjTY#pD%TiD(;u&@n6zaq3is;jDMHZl zih-2$C&n(b@{q{rva3D->xb0zPb>b>Y*n_kA-WKvQcm5V>n_G2S^rCqEej9ydw7>B#vu(_^ggz8Fg)V7;8$a8sVN7v`|_pkLXYo zgr;%>NXs%=nG4RkFHmuAAkhG;bx&3A0kzwyi_Fp_EdcHzAa?PHrsZDZ0V>CR#iEIu zgLO?+A15`4R=Yy$p$JKmHSjSZEG=_ts}|QTj!HEvD*eJ+Q$*oaY7^u$n!Xb8isPK! z%GBGb3p%Pf`-l6Wwj6rvM4O@(>eAp1h3h#;Xd-JJkTJ1Qq~Z_AUo`kta|M>>&pz#C zLvBdaD4iAlBlFfH?r$9P)q1~<@y1)i(M@QXvvgfU>TzXCQjd)FfK0X3T|K>`h~289 zPz>kbp9o*3l}RlqEk$0PU}-v|Ar(Xwj}3|>_R z(?wuD%Ueov>d;Xzt-s9hG7&Fe0;*+tE5Eb}QnNiq(feX{x=~dC)is>hl#E`vKf+pG zpG_c{K2OkvV~CV0cPRe=nYnK2Tu?uZqMPjc%F)~iU`@^1kqfHOXh|}K#KgniR{LcD zN`WK1PC~1fJyGRbEv2L2433fXjP+=mjnoOgxv$Yvfhv&wkmH34ON~fOW^I3Rm8EPa z%RZfY#0E<2gQ!tAQqV-D*z|=@ijW~~v(Y)!1r^j$Ik@%jp%GuYwKdgR z?5{;@R9q~|c1hbg^oZ8uHBtj80?|>7DPl#vK2gWwT~zaO?Wa_=$1rbI2@7UrLSsqk z78^WJ+N=2-y*eRxj4V^FAhD)|8GR)T-(y-l5qP}0r&T3*dl5#~yq}wF^nz?Gb~Q)k zvSs}f-JN5U?Ae?O8Clv^>~6=NgwD*=Ar`kP8nzKc&o@`thv4jP~=CD`hy^T_;jYc=_$l45z*?-UA>CS@!Ge4%kBRF zFs)m3Gm-qWlvyJLg%`hRQR_E#<6KP1-mfs9c#F6isXJHbRp9-yUvW6iv6o2-U#TNM z5<5lW*q^5hAf3nS+@v4AM!!*yS5c|bko!fs2@MtkPw$T0wx9${XYr8mU26IJN#RlGR;1BV7cV9RCnrUD5@!FZS{$xZ4L&B z{$u4+Ou&?k;+rRFrJiEge`?V$JINJjBa8O$ADVz0;UEf_bT^8iThm6 zy`x{RG@OIOS&bW%H1_+uYW}5VL)%nQO!Gt?i6d}r#F-w1bc7B`G&7l|in5J2ekpMA zp*^^^j#ArjbZ?j8@KJ-?mpt9LLzXdaPN22YWc2^xB)oI;vgYtdQ|uCj>C zMN~3J+L)uW|-sNy-XJpt=Bx#l0h4zBYvL%ekvJZ zPp!+Eo(h;+f|5?UD9m-~87#24yw!SU&O+G5Jd~NFtspi}LEEQj0!Rodeha@f^<8q+ z)lv7fQ6E^WpY8MS)can(yG?du*R)iqNkTv%8HG&8gS-9q`;xfI{{U9S>he8J^IxDH zk?*z=`vBY?T$ToWuk!p?L6%>&DAXxPt-V(Hd;i%`ks;G?1<#nau(cLKBM8H z-tgUw#pI~#OXy2aN}3OW|owT-)5+yUV36tEI{SV{3S09VFY*x$-XdVNq{O``^?=n{|p5^85aq%h$eaSeh zAJ4cZ?^#_=gK)419%W@IDsiOLLgS5%Ulg=1B&TxwKL^MF>=OXbr@(lM;5SXOz=2@C9Q zl>Y#m`Rc!MEzxnlSn8yDq|&7_m)0XWYbhjuijm=SQW;PDrBeqS@;4yy4o>Dp#rcib zjqhq{qHgw=#X6NFfTHVKwEA|5ZFLGC2+<`GNCxpxuuOIA@D0$rYrY@h(EgPrFFxjY zcbDl^MebkP4Uap$HsXDLd*iyF@+NreK_nH`Q?wUuuW+46chgFbU}Xu96|`=NdpXnQcHZb+?zPj_?lDm87e}C$RT$y5g1Ss@#{3%@vJE z)SnGU_WWp=&-&TP91)V$o-O1nmMLSnK5HtmMwFVIdZ|iY=MhtDs|%)ZI7!}i1%I?4 zjP1)Ke?<*}wUx0|q=Dt({{Rn(6h9Q}TKR|C{{SyPS5wJ6Q&H{9knU+fM=NpKDlRyk zOS9akt~j-F&!$S0N{t$dkBES6?twm|RU-BZ>e?TNL;5_=;7`RU^51GaqyAG~TY+9I zKIL#Gkhh!U4mVTP&al!5CCjCaK~=PFqoTnJ1IMV2osQGT$rT-ptviJaejnlZ>7hTN zCcUzXZ~BLYp|_cKuvQk*pmsQil1}6;CPXhnBt+s;D0Z_|6BMM~RCp;zoYZW*-CJ9m zm+Gt@z!{x2h)Dq7MW;jZ)T@yDR=ewaSNDdg?mJcwZnlRbJ?9Lq%zqWG_WGYgC-(AG zjgwR@S6cKBGFx##4->i|qnBZ!L`c9&d9YU2IO8W$1d5!e2v1!})+DhS%2ZBpeoB=7 z(s|2{IFWgUvzT#e`}Qf4u$HOr7NwL|+bU8_o=6HoQe=8hPXh#J@Ulwq<{pV0Pm@P$ z8=D}|sH4c-cTse?-}3xFAY(eS*Va@x-=az`ZML@xk@|{t`S)!)kuXSutfg>e9yL(c zyJRKIH3dc+pXwW{EYvWzA3;0XT2z&!j>Rey5L=|8ieyK{JQ(_ev^Iv+Wj7EH$e5YK z{{S>7ZbKRN3Vg84?1DV(Yp$hGTNP}rO}LdL%n+oIW3RMijH87@ zjCArs?a`7C!C7|zeb6%OYxGt=T%tLJl>sgP09jCSP;2)TJ#GurF(^{CqX*4?fUQ4Q+fJ7KLHA7nrBY?aZ#-T=)wm0F6eyp*m8C zgJ^n!rw6t#dD&+fS5f0xW2?B?!AevdCvpVkr22snnM@-kZAVo%iR9ASxHZ~^ap`-D zJMmA&&wEe0 z@LwQbF>9>?mqOdBcEfm5e6^IzB&A!w#3vMt0B6_eLMD%(L)Q)4uusD zLSRlquz~IwMB^Pq(qC|dqi|C)B$*j@{2)g*slh5nF~=9nxvN!*!Bu%h0)0y1Nlf)Y zX|dHy4JAYmj1b#WSx}lc!Ln^d^tNz>6>Re-^CW@@AoL<5cF|5W)fuwN5iO%%80hM{ zv3aDcuTpx)3i%fnKbay#zCB=KmAIiTAt_f`fhj^Ms(rl?Za+c16cUlWz@ndoJop;3RSM8#}7oX+Js()N{>wy0@XMo)hKOF ztTnvb?gFmiOONFBRW4xIbp=AG-FlPOQh!xY;zr;WNAl7SO-x*QcXH*mki!KkJXH_3 zeV&T;-|BAqJ5W9fYI1McU)wGB9j$MTYcBXsTG|w=UB;%p>y;Xv0%;Gl{{Wk5JxWL- zDE8gQyDp@@pjMT0+4~sTSP5w#8m@Tx4~VPypFYYs^DMUtESC|ga*F7HcOgn_%_~9v zBrPN^b8s93$3hfkj1FlnI#D1iO*JA2NCrUdGl;iNbyF;ZMActv5U{lM-#Psv zIq#}NS>zK(aABt$S=scn)_(voQI3^szfn& zvlNUiO4D5twKbg%=uju5kAxLUq_T}lTV5z@ z9zq*$aZ-RvLVA;+0$wmo0=g6x)Z0SoAK3TVQwz?p8!sjMn*DMHNW*nDJQ9+nbZ#P< z)PCvCWhydp|@51z&rpj z%(uWNpvR=OPF9mJ7o_}PC*%~Ic;pE=GCQ~lF%Lcc28v$S95-r^Ia5fyg@k~kDW9BF zudXP4%ygZx`#0H5HoF?ZT?1K`pS;N?DmQmy{XCdC5DEX<*G<3%r8U zFx|Sjym+7hen5f0qkPrpdm8&r)a2Dx{Og?P9H7S#QspK*f0a>EKQxp-g(tk@HMPFV zXQr#G93QBaq{)uP;Ue^a`M+}Lj@3cNye)}SS}fFb7+x`GY$1v^!)!E=g!h!DPsdob zUF7lDh^pcRKU%0mZF!NFv?i(ody4n3hQDdK;@ENp?xo}{w;t%$po8;_p6j06=4STo zSauGjTchJJ*vGzOLb<-@CTeT$)z@Xy7L`)9s!0G1NheseM~ad#Hw{#C@-Y+XDlb!S zL))D1`E-Ilfe{7eHA>1Cww}IG-W~pq)O1-gjw!DJhgxweC@4@fpF$5<8t09}GYcdY zX%te}?nwK0hp^CvXujV5<=1`}veM)Ct8ewYV+jdmA!kBA8BRRgclP^{#9kYWOrfoD zscGo7E4(Q+iEHyOG5vrC|KcR^Hzis)xO}if}BWdeO zqw%~0hT9$T%=aH40R5^0a?jlNmvgqNt?gH)*+o}0t&|l2LG3h=5%`a^$ji51PU#Qq zS|$&e@qA0etzZswdKYSt{0@rlPq^0I>xrwZs#rA+pD^f5g#KM)+WocQ7P#9%6b+yY z2k*ao?(-8-=8bNE4v7QqV+{U}>+WaMCL+)2q265RUymplI zm&ORSwxWp1{{Lb3IFX{s6qv1i1}26OzB(`2BcP*X6Fz|v$0 z`He!k$u(MuR3}0SJz=>RK~AyBeP==RC(wXDbVx90fSBrtZ;hw*DKeQS_rn(qtrL*A zql+s&0Usd_2-7bFt$7<{QM)lIj}gW5rqqGB`hGI)7H7A~RNJGJC+4uTT(v0@I>|p+tK|M=cE$XvG}N}WEiz2}Vk{|? zSyN1QnWwCMMeaJC@KP8^zc?aGlZo3Ro|=>Lk2c-Dx0F6>)HX2Ez@LKAZCVtd4?(PJ7`vK879*Rr zKIuxfmXH!9p#ef#LstT@IQM^701|)<}YpHps6VL+~=D(a>+$O&R*RpIU?^ugfN`e z!E#L^E(=0JeMhOE2hviHU8-35Mb6W>g^dk(*Ue@5F3iODZ;jL*N-^_Cxy<9;{mRXC z&T#R^_@+6%Se^je=AjHN#O*Z?D1@bN5M?=NG#z5WYmoZJ?pgj%4lHKa(^@FYlG`oT zq^Pq`w5V$75;OWo(#NzVTFtr0w+H0MF#D>KYWG{#p}w4}^33%D8D-gOCjS5$WEE3G z9Oc_(a|lSQL(DtS6%K>}5^T5*3biGHjnxZX?-`@1xl?CYT|#ZS9U&gCrlBXACTdEA z^&Sw>ijJy1*A6V9PyQXrt<^Bi713KNkdf-Lzbb-&*HhF&qLjBp0k#g-sD59G<9W>k z;p$|$xhK^?0Qpo7r(Jb0`{EA?_+)ftKs>vOxgUxyPhPm@thsAiimNOSv|pfk1E^aj zU{B#FiF+oGrlCm3G*TUODU7*hoK=3VDCFKTuE8#@t8Gp9A8@La_A6C8qfZ$CV~o6k z$MYINQXyP6-9ArO6oU|0)XfS*dt-D zp}-G{ciy1<))dp!oTfwird4W0{{Z4uHB^;@KHvcYnC1uOs$xb(;MMgVb)ucr2A_W{ zuN?vsQou9rN!AqCo{5*lDdZCJ*=NeKR~b!g>1YxE01kz%5C=_V-aG9*--2QC#TZfs z%Zt$C*6~?f;G|)c^i;J!F6JcwWDWBw7#B?K5mjyNcQ+GHMv}$3v*b*qS1#&^TZ@NC z>Ub+gF32!yls59OLO^Sz1_cT!-BAVULjV*&>%U`(J{pgXu;i{O`(dhJGSYTBW@Y}U z0JK$Dtn~>yh*kv&08&zX!^c>1+1luuztKBPIQyUmnp2Y+Z3M*I7)q?kGeJXJ$M03j zPp3*kRs2Uu2y|OKrhc`6Cobi8s7hD8lA|q1GK=Y$)p$zF9Kv)dnZM-J?7XBpze#7H zCs{;Y&BYdziNZb2sMR2<@=9j{${!2(P9mQ_lC|66Uf!WMw|teot0{A*K{$?@;(=T- z*jH6H7aOzHYEaxlPsStM@r9oa*6sxhBhD>6#UH5cC+IF=jXsA&7z~%+VIf%iFwdR>$%dJ$0WAmIWTal_o zSVu&spyWIK07@3pY7(hT_v;*UpH#M6#zHlFlbuRW>MiR``pZ%KA(;uPDl0Kgr~u>& zI-|KsQ$!t7LDD$mRQJP@@mMNm}#0D^rX#yY)I zE{*F2Kfn0W;6KBsXiH*iyc3NwB6rzg#d(<1#0Y8D5g!HfZslP`Pg|KM{tUN#1T+06 zA0$A`PR|qx+cOSGrF#mx_Ao4*b~gr=jvoag&R#rMsj<|c z9jZS8)&_qakZPo3<#3g~ib|%`0)~T0#B7*Z8ip7iXfaShM5QCsyi;;~ZL?@XlD6b8 zglJQ`*EwZ2*23wk6}>YlJBS1J#3OrY4Chd$KkhWpwzWhSk9OJBCRuN$ttu2QIs>cR zM3PU0`bE=;_Qnefkt0T{J?H(!mm`jRWcV(J{jB?fwY`m6Y870RzN8?kL=Lg)s|W#v z0KYVO4b!2yb$r#7+5N;(B9*W!&_YrOJNteziFh%^Rc|=}145hJyWCY3_UOf_y7bA_ zgt@5DlPw~4IK*uDs$`i5O)k9@%i+tlqN?9cpi;81JlaT@tK?*U<)`57BG?qCX0F`b z3xp{N2pxzWqB~Qnv&{rxD1FO06}pn_hum=+0)&o}i`E#p#6rX~&WX2R{8N#Ga;>VC zhMm9HAf2P6x_@j{Z?}7ft6;?3#vlYGCQHipuh2ZT)uLNTmhbj@YaF+Tmjbz!%f=!g zl}ob*TVW;EIhEI&(j{Y?GF6PM0d2a+t63l}>Xo!>*jR93KRR+KxJufwWj_ z?|U1Pmohe;NCV{#G#3SDy)BGUInY_++$NLpm1erN}ID#shVkqyoB=}Ww&I0|A^rK@UE zPSAqw&V4{XvT}wtl8d}FRw?Ys1vyQ|LqJm@@ewVLRII50E4;KObj-}p%8&s0MTw79 z!u?Y_&aZ~CTHXHu!rJzqp@-h;#!!tvoE0uPeWw+dGM5DRsFCpsic6*Z#ilkAV+p>v zUeaooXWvbF!kaiv-CCLiVMBWXQ}rZ1aU%rgiW1`3qepKDd~Dr4-Qq$Sw>d>!MX)5@ z(XH({-gEuXY^W$+qaPzAwA+WM9ZyLJs^$e6w7x8AQX>PTa=PlmP&WWZo*K#_u1~bWsgOK0F!b2OEv{M5@k(rF%+pNIU<7p2`yiMh4QgugV&hZ~ zBcnvyQ9veOk*AEJHtsN7q%1CI)beqv+M{6vNivsz109LA3G0L6m0W>(Wmb2Dk`Dv&0Ydvc@HjWHp;exgK!3FBu+p}yi~5==B*ZS66Fc; z=YLt2B|#vm$0j6tO;HNy>irePj}2v2t8tzZ{o5O5)h0;TCEV&F%LT8bDz)AD4$(B+ zr^m~-ytlnFJwB#dP6WTTAcBmuzuwv_SNm)>TC&^GDHH`y{{R$PF^mQpDiz43=_$(l zir3WB;TL7oxLI`5xKN--QclT&55iFm$sAb`Ay4tB+a=8$Di=JKo>t`=dR5SlLcIQ! zv9&bf;Z&3XK7b`)0U6bwB*;>NLIC)!Ztsd?9%^D~KZ5$VFUl_ruA1+Y+%A;PNUolu zvG-dfY9^Jaj=u_-RD@!P1Jrb{ z2`!YLmqCBb}m~=hj zUc)ta1H+p0QSnfBe0IiGkm$t^!9M&^?$6vO4PHgkk0wTcL@0lzrptXts)nupp>=Gs z^8qVBQjEznjVp@xdj9|&Hs_7)DmzFQc&)Y{>eN8AS|&}Dha|s51t)Jq8r~ne*c;oAa&TE81bj8|ST`yAedJaRt((Kx z%}1J*PGf&<4||zzGgDu5#MkiysEX}(^Skn+kT4Flubm1ca(%pU*l{p znfWEMR-PiKrAp5K0Nnh83d&u^=DW^?Q)e%IzVEkRad_ts`*WtLuU=_Sa;wMbZXUao z%~H@*dP;TLv?XavY6NU5Gc%0ql6KTZd}E8!+E%^`X~6e^t;06re^ZI2Iv>=T-c|M= z?(c%~8thj+;j6Qi_Nozdthm{!*Lk<1*S$@OrN2g%1fz0DJAgVEt)3Gu&CDSF1xhox z&`c`e=7aG4{b~ACVrKb!#y_aJK;=!jRkW{DRMfCvr!UjGB(x}_ZsZ>hkrgeS)u|wY z0ajdYO3v}ZggS*LFqJmdN{fmKkpxO#l&e7%%2mXzdfwuKl~NU_(w2bTFs_HZHrQ&U z{;UABzVIN)Qk-$nkiKomB^0$S#36Ds>j@A7kF~to3O}~iT`3$&>25C5?K7HGM@@S` zw>);*a9Z{USKy`b+|vHh!O=+O)8bXA`(OL3`=9pPj9U2j9LdvPh0;7+YS~uGUR!>l zn!+5>KohK|@{G5T_C_m~pQVw|xcCl>O2&8keZb$w+mDI)zgp{W?Pc%QyWW2o=auecNdynh$q_>n_drJ09T?r zb=__W#E;ecrc0f@DdR=Yb*BE!*Ex2lXhEo|J5UoZ%&j!nL_+xTS=Ve@Q~@GUK+{T zJydtZY5l)a$D)2Rzm{DX=mFRC*Hik)L>FIV{Mm9v&n4ij!wvU4hETj|-MHbm+Gj6E z!E3WumMUB8s7~@eF!LB%bkpEcWU&$ojjtYUKiGbXl@>C3T$>86UJ=Te&3+kN`j*%E zXDGDQTa@}i+MtBo+H5$Vie?XJA+C57I;cB~6A`YhJ%8e?7uaVq^41w}y>VVG$oH!s z8UBIy>p1DlgY~%8LK3i2+f;R`brh+Z%>4k3-RBc{Ol(&U=|>KQSF$;amLF5eVesqy zzqi3SyxWKSZ{>WJt-7999@;rhkR^rVSDPWY@%_w%`hnEf(jYrT`nO3%YuGf~<(@sHUEbk=IbDuY8PoC}9bNuwus+%7^4D()b(E2* z3qX`cHyt}gM@2Z?g`38@IU;!LC(H1w`x)OGW$#$;^eP^PVUJxOq^toa{u9QTO{qx= z>f3=yJ;8;<=QR3;s9TX4p=s;l5AX9I1ERJ51ow+%dLN{F3rT^5zo1 zS7XkTya42hGMu}@?lf|nwU=`$u2bzg>2un*yi%hxFaY`5RwF;=!~IlCU+vt3=w?1& z@KqO z#ZZ^LQHB)YU0ok{d?uq?6D=nF(cMfdfw!S;s{bjBs(( z^hh34;wURiINY1FjbxFZQr@w0%HEl>X=&2r8qf97-qOHG~x6aJ%Xm&Qq|Jh z8TDFFZAbNur^vZUUpd9YC!4mo#<;QG2Su4;c&+7@W;V#vO~G8pw}m*5x}P~Pzbx>& z5Vp^b#(%mny!N=}ABy$Q4v{_QIY^2<0|Uk0A2lO;G#QTlJObtffhLD5f5=k_y6g>SaA5dItibF9W<1 zMxovED)TF`f@q=NEm1<$w$-__sOv8etSl59jH#-aJTGbFY(k39$awPV?4k4OP~6xn zNFfTAyL3`~L5SP-+s=KrfUGu*y|n^pYACNy+b_DxK18Bp70;C2pDb+wH1`D%hAYSt zGN0vN!|;q#l<^5I-M%qauS{@G<3)QHw?C!fAoAB*N4raXD0M4EV53_meS#!U?28S% zh{5VLRMR*-pJ>z-Ke9|>&6L_IthdSr?iSkS%XJj)BoIP`zK6iZPi2yl2{$y*g}vmk z(A>qf!B7XiUuRcdH@S?LAEa{WgjKv0ny`(_rBaXlCpP}W-ZtJppNjSxADk~ZaxODI zI(^L*VqDs|!iC--ZnB|-CHAS3>uZ#VAtORz4}Y$R=v!EWiiOvFK7zcs;?$O(K|2;S zGaWiXTT4@qfsPuZelq4BM&X==s{0km$0=GMIMm9TS||BP=>gwk` z6TR*mJGy(Zq{G{%qH3P!VoQNd$R)=dbP0`qU7%|?MXP9fDIAQZ@a`Q{fByjOdV7e; zDSxQhu>`G$QcFeG+i660(NL*!mf+Bo1Xz354I83_T<5_#?-5*uhmdi{@!PJ_wp)1~ zR}M0zx=)4 zsZ&<3Fj`7<1z^m9225DS08XkwhkS&zXsb=yvWE+68&%#SUv%1{#18U7oU59oI}J63 z8@8u~sYS0cofVE;kGL_tLAor^@jcN;Fj1N2Sj4ka>$uFm{+{Sp$SH;*8*ie zo-xrfbX%h8s>h7>q>CvS% zN?TN?);!I#!smTzHM(K z?Y$zY$M%`XxK?w)e0b(DdZw#gmfKB7b)jv<2WhG)+YK!aAQ`00cttysSjfw_6Rk?n zxL!&*G3*0E(LxjTd&@DrcHLKXzF#RVHtH7CX2_FeYgA2o9YipqKdS#j#O5>mR>C|2T)NlDZP7UGIQ{uxE%du`!tb?CB!=UmL`NTpRvW4mrVrp?XIzS>{h)jn@l52#q#oKBPt*ka zZK#Z!r*$9#3d!nZWACVHzYr3 zc(}ic^G;LCY&b3fkG+!RnLR=&Y}RHIxlTIa>Z8#pNSRWOfC-pXx6Fi8u0fBIcTuaM zP8ItX=Qd_6cm7D_yJaouvdi`O-4zqnEwZ$zEVcTDts!!hi3pSi?E3(q9aHY&62GgL zM^;4B+by=sDwf+X+iBTkFp>Z%Ww6*O23}nvwIdu(?=^b2a~QzVdIZgTg7+)LKHPAt zrd`RoRXwSyW|n9y$y!{mu5=`nw8IG{YEq7!Bs;Wi~eX2JusHYR-U1>Dq9Fp(jB#ZAjdEnZ@Gn`Yx3kEN06d^Q{i3 zkA|79PDM_l*&95-cp2#o*x#@osLPydi0G_l{{VKYrm(aY3PEi%SJEucz8iX|xUt5% zg&eE$7hG{b6EQiS(FrBfij{)dy%8g~m5G_mo|^QAjLt?!5NSo?_fV3rG~`CJh6yN! zsR`}t1pff4`PH_;Uh3Jlys`Saz$OxqMD>iTpFwvddm+-RW$oqNq;B0Wg6eWBFNGT8)=z(EfJ=wMw&pJlbp^6wP@N8G@Nf6X6^GDl}miR?&FlXj^etD z)$MFh6%xgTzjlT9>^SHSARa&2yWxRW104>~UOl~fvb*>ecM&%V5 z{`mDbZ+NAoG0-#xg!5ieaU-c(r0+UeEmZU`nW0LNnx+J_iC$4_1Y=4l%oWtKm0DLC zme{L#q>vI6WP10~GFfs_2T^I3_HWuMg0*h{FBdc;MrIHKkRK$d*DMXAClAW4Yd8Fg_ z2i$Zhs$0&Z%mpi)Z zls@}$lv~-XEubWn^^ow3HT{JZGYOG3rByUqPTKV-jj~d->~)IlZK9pIo~g*Y)c*kK zDw3z=Bp%Zu4%lf`7RM53qdj7O^*0FAYy03mp&sL+Qc{mh50;Ri5Kehs*E{@@Wg{GM zpiIW!Qfn%Q{NdBT?lBzZ>ZS)otEFg`0VA{&A~Z~IYD`?2A@oT-{{R?ez>=S0Ti6hy z$oSi>kNk2pu|zg7$)+b;<)*L*yz6={bnwqpw87cQq+0 z^#Cunq@rpBOIQWWHBwBKJ29@tq9X3Uui zTC+n@uqsDh(lI{Uz^f*}r0xEJ;KPU0Jq)ylXSx(pIO`2mV7*betD?%&BRP)J6Zu7Q zOn~05ZL%SW>%%D&+fB+-5(0h^V#d#+r)8~0Q#6F{}ONBPW1`^W=s zRBYwmW3qAUD@8+zH6Y0hOiZ7oLu7I^8YycGJD;ggS2g!Ze!4YK)t;P$&p{*KNUdC6 zy~SGK$FZ7~0l6G z@nwSdf>yCuQv~q7Pa1i{~87`)6x{!MSH0GZOaoQ6DGc$7xkpX}r=Y zm(jfjX(QkWjUq6|ijNhTG)V)fR8q)zx`x1;t@9+iF4tk8Nm#7fR04iJ5CzS=Z54k& zrlQEtF$|1Ul*uESFbra$#_YE$>xvN~mHKC4J;$0_L{sy2%+adB2MxZFwRiz6>^}6Z zmSqN&Vy<U~5e36(8wE;&epY9@(h$1b zvkS(Qs!KRdL2+zI22&|RG8Z-#I9DI_xvUEtwn(Nj`{{ZUlZUi(x;83@t zXiv+=BVJtnRkj#e7||TsC^Y=PC~h{THe|7;pm@nGre$3cAit$?Ixb0Y){R!WtTOX( zAt`HO7tv%LKNxkm*Pf_Rm}3%lr-GfknbuV)w$J83@PZkDX*DSamA$$(9$ENb7qF!( zCw9}RQJ6aX;z~R`O$si4L2)6~E1R}s7Np1_t<8_kA=B#xHYmqM68ou~v^$enD2PfF zfUl=WYu;c>_GEHuZ!7XSDS$^vp1K+@D`Q)zQJat&Qqba)`SsWS$gkb4w~0sr3OK!0 zRNI9c)R|~?B5?uj8m*Q%BXeqjE}_Mc6iFw>5&`i=HV`^38~zZ%qI5NAFxR+=X{yQ_ zO)QeR2}*I=rMxYPSqH{_p;SpNThe+NeItpYv)#Qzw4rWo7eS)dx`{11bd)!^QmN?M zP`5njm@}OFLX8)|xot>kWK9EJnh4;9h;u8O;?}o*TK}s2Sc&*50y$J=n)ACT9GxRU>PK?8R zLe}ZIPt3HqeiIUoeH+q_Mi=#3y=t_dH4#xCnLzc02&GGP&AWz$3q@jojg=;TA{%R6 z(ujE-8R&&ISb@|KA$thw5aol-2}A5EMMfQTO(Rf{^5AqRQWr|1naW3`lO5l+v{0&?wDtumx5e}whv5z}$zb)tB3hmNUii-T{p5LPAJ z&KGaTKs6{E&QH0kLo{?NdwQEnl#?*6I)gdH?Y1SY)m5*u#ln^9q`w&RW!DQ+G;CEL z(==1J&(L(}V%2DUumZz5e;IAl%Yail&F-4Is+puBxG8ecJGIx~3|e8j6j-d>G0lBX zMC4$+qj$L7+PKtJDZr%ykuxr{f*_2@R7Ou8%VwW7XL6tbKDjN^-vd`{nXRct>4mD zdWt|LD@vuT{5!?6#~LAFs>rgOhD)1}S*nUwzkSYF-xQTMB7_4W{K7hZ5do00YuvT0 zJFi^`(4o1f+$ogUsz3GQDN0EJAyb)`gyEZbBaBrlTsBJPO|&#r&(D73@_tagzP2is z-6EDsNLUd&1?zyqYhKkqMdv($Dh>GPNlR+m?Z7g9X=5u6}1&dEqG*Zg;)iX zi)mS*AM&6~Yo}58M7tr{6(-_hO%pZBoNYqY1y0IfG&;!jm&!RTRcQ;BcO*Zmsm$`+ zn@#GirqWZikuII$uOl|OYdCp><4q;XpzSl(Q_=^JD;2vzL2)*{TE`Slgc+Z#4A!pS zIFPK1*?m0nYSncGX#ywl<5ft@m3CH2Vft2%*@BVYEIR-SwjI$i{^2MVtSa!P=9xC5 z{{X(2&%XjAsq0kcG3sPh=t*g*sddnI9M}%qVxd zs4=RjNtjx`2f{h;xe8-%id0(b9bVnXq3V0YrZPiaReLnR0#($wr&v^okr8EeXsa-f zQj9#TsW$I`n3v*Wo0}_8;sF5RR+?SemAIfr?=U_SoHfOZMAa$2;JMz-0=kYQz1!$5 z)Z09u0Wqe7O-yeVS8Wl{FBNhNDIF;#I2o>gLMT>-+?LdkzXBx>-ef}~LgDakmS8|o zZz9NlTYOPM_q3u+ht4vrrOA>)y*q=k_N~A^N%`aC)4tmdDLq8?ctLpR=|vrq^n#qr z;;CDsAv&sB%Se;D00ag+5{PYeVX9>ibv+V3aT3xfr6ge}gCMI8EXe8zm%<|M+f$;I zvS|ZUd5&GGDVf;XNz*`Q^Mza6lGN?Z<3XYem0YWc9Ri#)%peeV)62QqQ-CA~y^C$lF z;kbofh`TgmygnMa(KU)vYK@9*J!L&&*#idEQoMZ~(KP(ya-dRONl+^!lqDockR<}- z9w-1`Su~AQ@B43%ZIr11&Pp@s+?bfK{z`(mZ~Bs!e{qvMI=p}Ih5rEX5{Q0H{H0{# zpY?%rZAxV8hJE{d;~{5KwHX2ww+M2m4~VD{jh3}V)xYeN6Bj~>G6-3 z)mSTUZD-U$3zbE66iqulG*tHk3icYl7fuN|U5neQ)Y8>Pg7ZWcssJIxsCNoXh$FNW zZu^KGx;K`QB*^jm5z^Nt3lA8jaF@x?8ZVn zSzxx55g_XBqwr3OJ=lGx{n2E6Espzz!z%dG58}K+uGZna(!Fh?a`jMYfD5R$6TK)6 zETL*UcPf2i>ipf6)KA#G=63ACSF``;g-*I=h`_60D;iq4hz(SzP^lYC|(6WwZpk z0R#LdC^=r)uDdMZWzu{V|FVHt$*M_{m7Yz2ylf>I?O;G!Ber`H_a)jm7+}g%5ZEfO-*q? zl$6_4w3+MHUJ)T}1zfB5m-`o4U;8}k6xb)Z**Up?rKNliifP;voLBkOdrW0tJ4JqF zj$ldGsVHUPniiB=y_@5y+?YilOg;Yy^&~%RSKZI~U~UovEr#`#Zi><0g(+){xd(J* zt^>xp{{U~&l^3sa{``1KkyuwJRo7*>iASQm6O=J#vddP~=mS+X?f#`Q9TbYLke1uV z;Z_=v;-+7Ba{%>#LG$VQ$M=$?7q;x<+HW)6E)_Wc01?#tedmTL8*b)omv?S>J>?HP zlsKm7>c^=k_G0M3P z-Nx5j_Z$aMif%x*wwsl>NtEtF(?+))Y^FpKroi~XnfVK)uS8BC9_F`c6$8inYx@*K zdHs5=@V(uEdpAlv>V$GH*2)ir#tcMN4$h6l%2$#0h5l!?Z+41TN{WV( z1iKq@ll`$v=1i^Z0cw4|Kl^RMV>wTbZM4*Nm2Fe{Zym)814BJY>F|p^#y&r8v{e{# z?`(v8R93RFD^6&{^=tIozwWGvr@-BlkghlG-)&T+#A6jgK0Q5aL-_YSTg!--LVL8Ua>)%Mv&#pbD$yIZx|#+pd=Rtlyk zp@`gu7A9H9_UWRqV7YYjU&j=H6IxYUpAKxL`@r*MI@SA6WuLr=mf*_IQA zVfd|gio7>HD}D++ea|_kgt;-CUde)7SgYi$8ueX8nuJrSrl~DG+}qz7ZQQ)Y`hI)YU5kaoITiCu7oI zGxo*EctVp~aI+h&jdCqtC?``)q!S=Yo0%U`5)fwKm&S!xuehDvv3i7?E@!m0( z`(&`$E;i&TO|bJ$%|2u(l0g&bMB^poS$QDHR2qfo&Krb^^9VOKj!;jMaC3!UE_P^8)z%Dx-rdI4 zaY1gbmBfW5Nk2a0v4}e?jF!(2X`u?mk;KDv&94e0bAJlZX>l~W~BvFZ9sxG(?c4QtpRO9#zsiB z^eM7U*5ut<)BgY!g-j{AXb)2%47%4u?03~!(()BfDRE}uLRC*t!sdkh2C%`$i2(`t zvZg0gE8q4_?%y8e+Y1ZPh2y4Ko2Wn=+TfuJa#e-Rm9DC6@)?akCJn}u?iE*co> z0-PUx9@*^v!eV*-hTSZVjEqlt}BGKx{uY>6xDG> zBG@Pm6rnv5W9zhUZC_8t*y;F|0A5>hZqi!uk{-W+FiFAkMc5OAnKP5oCL zQa8BzdWp~F_(#XNm#09wI7}W$j zk8Jr$+Wli;)j6~jDst-;jK_9mi%P3ihi~J5f}WV?v#c`pOw*21Bmw~hOR0(-*L`jy zJynJ-)j;nAwqw|59J=6BjFCRNj`0mY+?C4?sOUR00qBkz`+xDW^&(yp_`@gedU_-O z0PKuNL%zef-jxzVMqT{h_P~Gs6BmS~v$9s+HCoyBRmXJBr)eRZWHWW%$+h!m1ji zwzi#2l-h_Of+R=EGhT1^Ka~*w0H>2{l>P&>mTs1lMhku^XYE(p=M7l#T8zrUim=_! zBz41CP?frn0u?C)rC<$y5$evx<}Y%XMqOP>WP83VCGIW~(#s_1VT>`MKH1;5ZW`ok zJN8_^3a7cyy)vY!Zv_L^V1>rTdeQBXytm_3M~*fFU7)C)bo*X=Ot>ZWw}!b$T$+lJ zQs#v9mf~YQn+=d4n68iOQt{l|baX9I<3IaRduL&~hpo+4QLLt8`P;u_gk_wwn{pgA zNSr(?Mk@!6ydXL%ehL@m-)*iVwYuUd9(`yt+6uc4`o=>y-9}~??KII|?YlRQkW^JN z7Z10m9TgOz_meJ)nC}a3-PTxmA{p4L7bJCW+uH>~m+96%gEQgM9cSJKNOfpQm+kt* z_>`Tf`+ee#O-=iW$N|f5dyyd;!7q__93gEr?{=J@T zGQV$-O>#ckHxr98Ml0sJDRH)+ZB4p_y3)j!g#e_ed_j+8HR6@3#EWXG2ksT^i-dEg z?6)WX09R?r*6iwNDXukb(9|-JOx&andV7JCh)9etx}EO+hVc|G1?Q@{@gCVlmJWQa zt3s*Uud>cAeU+hq&HxWhdyLcY*I2`k+}2UvMQE+;o;(c`J9YLq#-&RQDQ$%xDtBl+ zb&emooRdbOA)Su1D+_P2ZaH#cRSMiYlm4gzPr4kS(?tAzjIr}d);)splEM_@sss&n z?>~eGH{Cu|1tA|}>;tN|x$H}eTUaS2MIRH}=>o;~J(qD(5A5bWjjAlI_D{ylnXu6( zz!RiPTYbi59Ca!l=d^YQS|A_$#~2DyN1^kX*Y`!L4*vjTUMp1)?$%?12z{e*?Y$+} z7?~fHd%visxx7scpzY(&<|1)D#lwkO1|RRrv=xW;Qdhf>W_@=j2`q zr~yz&#jV$;lX1!Kw+y=MH2$l0ud28{C#eBdD_KaNVHoxIcXAUN2M|6A;bYp%DD`lu z_@q8n#TdiG)creq)zLtB#5_I6yAJt{QS$H$QKr?09C2NSn(UXGZ7GtZM1j(O0T(s0YY)+za ztO#}2TT`RSq{)V3%r-aZkuEjQZ{nn7k1?jPQj)5W$x3Cr{>am;JDUsH6m zQGHKw-!Z#U+87s-l=*>(1f&)Ov{{R$;jXxh^02(UI?dTdUztXA#k-akn&!oS6SZ(YJ{Yu zwyM~*TGpq(mITh?W#go6oL&#g3jpS~HI$`4zT4ft;Xzm%c`G|fK%ZHB;QWXiN~q!< zcU4jg52RNpr=U`_>)t8iDiw7yf)bYJTmJy3kv$ZX-{B9)G)*!<9in6vwO1E4)EQ6f z01%{{`lT8ckdS90Bj6C^1PZld(opX&-yLwGO9e`ARtl=j&*fM4!w(cFz2z>bTMA&$ zuo8z_Jkuq@sXKCL2T~=h;S(pV-Bh%(H#U><*QbOy-4N0`r4ZknP%~(5u@2R(zCni337zK9jmstWZIHj%fiIk7m&^-0BBlB$;WYIg~bgy1*9>QgMb(MY@xA8B;$??5NU zTEm}AhZiK2>Hh$i>DwqtY#$9jwjgs+{!#oDn~uDD!3MlpDGfQ=fDY3?jCn^lxL+T| zdrOaPHXorR?eMH#G2siqc!zFn38Zmx(d5VL9G5@*j2yL)q{vE6aAnCv88oG-a;% zg*knZdky99eRli4Q;AhIigCrcf2CGcR8)Ohl%{J-NgtbR$HF|b%{$}D^O-)p{W_IsRLZke6+8J-7{EQ=me(_WwR zK-{+0kuVe`Ka@#!ySv$0^9K)^X3MZD2>Dz*Y4P|aa!wP4$l;Dl3QoovJ%_7&kKlaO zRsR5#?X*lODrqVy0B$;w9;YlT&M$c7s*&LmvEz4YG*v76w?+@eT$W6{caV*z@4+E{Wu~$KIp%r%Oq^S-zwC`$st__)+@JI2%V_WUNQXA;ew9!ekrPZ+Wpi8 zDxUb45|hZTLeyK8?g31h~2z~|Q#TOqjjQA&$j9TW- zF|KK=R#IKCqCwk5LfmzLj-Sg)$=meOBy1y^BSLBvCSGexrjxdh^Zx)9H!+@bt8S8^ z+@{Qmnrb%p)Tz150o0@u5|PXS)l+50xyRC;2lBg%wmN*H0J^363g!c}?I?PPGaI() ztouR6cWtXxbg`mMvI-Q-r`;bZRMu2l>1>w&0M%216w=yW^#u0F#W?dxu}#j z=-<*n!+=OvD-&0(VOh1wQd(WwuKI~WzM1K>eG5_b2MT1BTUSz-J|<>6{V~Nfa}fkb zbY)I%08Vi{T1levg@l2rr^xYlUrUPg_XFSO5qB1+a=+oTjbR$7?~`G54Jp@JN9EQ) z7>T&DAT5Up$yne4O%wF)FtU}Tf;xi`lE+1%MKE~L1>6%k2xUJ?tfmf~{2@DnWiOpP9jEH=piz7syY#( z+P8ej)1Z%vL24Y!#Gl+DO*KJE#3$4`K|6|PV%(J(1J-b$;1UU2w;xX~b;Ok}9?CF~p-3G*Ht#zuYTo73sXBtetj}HHw|ov|d1iR+{iTSXN1c&O9N?=;m7SP2U9s$dy_CF2Qrs6e;oRCUZ%XjVZ~Q=*F1 z=IjLGy^(3E)p+3^(vMe4nap_1PqadkRnpy{B@QUZrd*$t$V|`cA4Q-*-ae&S53+sE z-!2LSEBVO@{{X}OsGG;CW@DvQ++*lJpr;!`RuU&UM9a#pSJIn4Zl`HjRp2a07F;C% z0No_PJ4uZG2nV>CN8-7|yzChj=_r#2@TW!0)BqCRnKBMOaJ=I)ssxS2a z0GJaglinCCO_ZXy(;q1UEI9jRRLRTMsZ8SvO*XntEN_cNR%DftMgCSo!!LLOLQm2t zdHCoSy~CZTO^+R9!q0VRsYNLUbDog_hr0IOtz4%O??S9U2IV!HHd}4U0s#ESSk!DY zhidbFTZRqBsb_S|-?+A+>l3_4gFg?PcK0afwu_jqG|080Z(DI+s`XXoGaAKl!md}y zndqQWFA2RSm6nQ}02vdkrKd=;VI-_;mlXc5_3N>Qh%2XzP0L~k>KBe1{=~g+0T(0z1c2Q4Qq_%{Oqrze>wr53CxdU=X zWz|)+@0C_QU0O-d>jPb+bsp*##RkQ`rc{YREXMEl)Q$VN!OrJ zi}pXcwikc(5olHudG@`U#d@)qjs$cBluphK>vr8i^$i{)s^4Gs>f|c+4SyBqSnnuXRtanLm8hkt zP#y_!PAC5Weu!(H(~+z)HNjhP-*u6Ex7P28--d~lzI#je6^T{6)zgVDN<}>VN}5_l z*0KV6kg9F-2jQ$i9tV2~)M&1M_s0i#%CbryCDF&&DxTwJ$34(m?o_T@>shk})KWas z*>$z04h^b9fhq(Zpoq*nlVJEvd%V}9e%~`u-!f+~)KO4hG2U(ze1Vy!om#nbuTc`8 zTDJ_N?vW3{SQ-g?tL9S~YhA-2&GEEd8%n8~t*1~)9&an4{{WzXZJqhSF_0hW9IR_A zc?~Eb1Rv!9AVA;084jO87rAt33oC)XuYz>_pIsccx$ZKqlAfBS#<=?SS4CMRQoexe zC)0Ov9z`7vy&|ieZ>gV(yzzSbQh8A`Jim71DyML_aG!3r&N&wga)xnFYQEL}o!Zva zB=s^@R+h<0Cuc%W!Yq*HE?_kIf%8J#UADTG>CgaG4N6^O&D{EPGlR0S#P$sVQP%fYF-}8VNA~!l zOV=!9`Ep-l+<&#~N0v*|2QjtL9B(69yMH=?I~_g1AVh3d5Ek`P{{YtKaif6ze1Es; zO#M$4`!D1M>qVQ9@;(j9n@}{^wo7a`3K26eO;vQN+?}LI6&*wX>`Ozm)kw#YbD2}% zoUS$YJIS;cdq#c7xlOk)$FaJ)TFShF$zIh$#Vao*PED=B#Dbai1`!@6NaO@`QZLKK z=xBPRe?C9x@*Y5S+G^CLPeOOd&KG)VAPRF7YYM(Uv|7~1wBL1gD+RVt{UVT~r1w!Q zldM#^cDWb=()dB!in(`@LAg2RZaDQ)47VXZQaTxkycp{p7GEUeeSRvxEbU1=fXUD$ z3@dMko>51Z9k&Yq02IacpA>b2N$x|4Ujk`sD6u$I(?N7% zIhOwb?XnMnP2m3k`@ARqaJot3)H=uBPnW41gSm60TcpR$HI`U_e<3+>PLY*Q8Q;1_6>pUemONsPLY9b8m9krS-Mi zE{*XERf>wkPVPNGi){tj38@s#Bq1QYkgv*+0FOG~%^{YKHKO*OD%K|3g26!H(N{ma zEw>JHx3DxY_NtNp?RaZm^WL`ybTJ&ZCVbBN$?^*oZDV7#~=kV z+bir`qHA{+_~@-4+P~a;9ON3ie;@M~ql)Hg32vJDleXVlv8JU`^wxY1hp~)PmiEc< zR!B7-OVRzk?;KY|Lv(h(%~?(1+fC)VYf@_3s&49%QrcFP?`m3+Da>mbwWKn_38e^H z_1w@+BS0a*roWW^wDB)1*%fKXF-m$`meI0?`9{(yt0~nf0+$;Ceeu{FlUaw_BApGIGoz1xlvB(AZPlA{*1{%2z)|n=jE5`QxLj)0e8zKmC^vOcBK=2D zpr*?~Bq)_9S&0~ci?r~9tRtSvLTxTqYKIE8w34CTTEoM&u%?ebku%vNRd(|6RW#CP zDG!fWZJn%RbxN0>l-aj-da9&Tp^2$RL~Ep>`1@>=FE~C)-?88?+^GYrV{)AMNdm^~5oH@N!h zq)fKxkkoEJ-3CYYnU}7vUpww5$o~Ke4gUaNYSixKrC~h;)MXt10Ps!9*H0D!E0))m)rjU8SeKmG3Q`lC~WoT~)D1 z;SHa&?n2!HA-{2$kTq6C?IVYB4|4X}jLyEPc*X%t=cLuaC<~~rbw8Obhcp2{#G^R2 zabL9Qn!@UhUunedZQy?|nI>aMuwcI5Im7M5Pro}n4K<>x&F)tAD|tVb-L%!z zAu4v!YoOtDI0j}8sdqs}dvseqah6e0$>ExnveZbFSA~J`2 zjU9Z~IqT=%UB|LUZ&v!JsV+9xgRGP(OuECUSMaGnl|aK8{{Xe?SBKS!s;;SV$6Qpk0I2pIgcW(%8;WZ3@5M&j_tp~a z7#b5kpK%ooK3ZEP63}^2$aDcnHmp%VGPm)u7BH}p@#$gP~z*;jE^Uv+6_nw7-MGKFWk^sryw=Vab=5NIVnr1K+-( z35}0wtrQz(pohk@GyL8rAd59q^+wgzLCP`|N_6q?f#iL3Ma3tnHw}5K+$8tY;|=Ua znv|(+un-Q0aML5HNJaHy`Df%|W-?b>qC!elz<20>7-`HUD{=8oHMOzm&veNj*$WAo z0jf@1njWa}hTaKA#%L1Gz}i&7_y}}S40@s)@;~N|n`T6zDdxJU=ET_vN$ukc+M9El z5tcwFM7;nq*T!SJhgZ z1g_xS9Yc%&JMT(Le{5w|UHIedcFp9|#)hA*X&Da9-ni;pUn+|Jr2TNN8=Z2tV?l&* z?l)1AWR>#tXj)8y5D%Hh6t5}}p5(!o{rj?&;@chh_YUYA{pfp+&75o>SCT%=1H}0L z3S0Yx`*3@@!f<-*u7?4p&5=gZ-Knx!Yd=+R2}(CAO*odqhy$WYJ^;bjyuXy#v9Gzb zs}EhKq<*?6oD_T`BJ&hi1(JAg1Oxh1GW%J3XS4gy=DUisnis8j<`TEnbH^V-*{Cnl z)D|3R_Ln6gNm@!&lcCx}>@5J6i_1_O&@`9CN0{N_^Khrc?5SN_lkyAC7dzAMN0>kP@MvMb$=xAj#@WyCBYYi(?8kR**4GZuc|@`<_k za3RTBL|bbMON2;n9ZIAjjO%}i;5TVfcYY6C6tUx`wFP=pc zgMPqB8ku|{w>GKbcBZc-xHUmk6qZt^&^*)W`ydCqYLK>%c}R<8=Bn{qaAl=TxZ8Q`^wjDsA?5kmYmfrfow&Oo=`M9g(4l_+l>A3%1t2^}?FI z-$ZL{H6XKdr(cyyO23UD_Z?x|7y7kO*#?3%>J!Ot;>=pUk-Fr(lGDCmgQyeg2>3;w zEPEW8%8@9AaH7(J`hENO!dKK< z%7pbWDN6xnR7|@|w0ycn7l4t{IaR>7Aye1F78lM7U|)=NuAxZUiz*5Aku#}3*$qfk zj1Bayr1{7{2}5PtQ8Ypx!lZg?V8~DtAt^eKhLG}WZi{+)$H6w_F)Hk1ttfI9^nPI- zOuM4aMt_aU#L5~_{h1dJNcqa_BZLrVDvRcK@H1sxz*E#vBt4Ce`Hp-(ls zB%NE!{a1ZXzl=35rAaZrmYxg*)cVY+18|r9V)$owJr?&=kC=Z?nn@lA$Fmeg%2Qi_3|)BgZuI&Vw5 zr|kHH8fuyQoo!vjETyta`V%t(zD>bgvDR_Gn_8@W*XqNL1sM~$GKlkRNmo03sADJ+ zmWJ0#+BO-dXw(to4~VD`lHI-TLcc>q6(Xaj${RE~Cs`xbhwXHenUa!w!V6lTVUYxO z%}%0~wt#l@IsyA3m%tR9cWw$)a5i?FdDdvw>QGXk6Df3`$E1B6ZK7`O;DJ)CN|%#q zswfap@ZGdeS&alZ%VZ4K;EE{6%lv?a(p4o-ZJ09yUbFCnVzo3;Pnw3RtaF+K-)?Wz zl0aIF!JJ23V}r`L;q$MFsko`aL%?jApUxxQgwap4mAbm4J60)EPcjlz0Mt+WqvOX9 zRGE2ORgQZj$EqpoYpMzfL3m7c36_z_>`NY^GFhCmP>NPb&=3x@@PoaQR8YNO zYGr-F)ICdsUQ~Rk46mR%jk1D$v+WZ!f207G2N!12v)D>uYLffH8B`+)~ONmJ$XHCbqgj_LRptH_HA*xl? z(jiSQL$-n?e953K_qRKnwX|$VBEECIeuHJZfzW17UEqqBp(Qa zgBe9@x#TZ!qJ6QXgS*qB^9!IYs653Pkh<9Bt>eo0841)S>R=Z$Gzk=Pus}QM%liWsqs;l-9fB zn^Fq2m-ayon9(L!HAYU|RH^GJK+klPaikyG!#o}3T3(syFDtpxd2(vlV7xg<^j%Gw#28VwV+ zIRR@oxJ78W;HTrPrlhS@32MCrorp_`N{F9=r3OdX9JRzkSX|dvS}NSXb+}t;j8_eF zXlYt#i!1R=#@0{kamuEUnfb#jT7MLS6>N`eGj?wE#Oe7hPA`xE$2&nj1kop* zsBxpU=9JpOmeP_xc#6fwHsTXW7)R(@6{jY!xd6o@UHBESykrl57!rUf5<%!!!QURe zkuhquZ5pb#Qu>yP7u;@OD3HB6_K7IB1srEi1|gf}n1yODU+!Ns$SPA?c)Vj& zO1ai032)VLbnX(Olz;&}^o;kr&n3|l)UCVidI;f)cC$bN=G&O5*+(U2Ejp3^0L3c6 zejoD-h&z+{E&LXh0IaV3N@po)jSgjwILZ`&?PDaWdPpoCVj9WRnHiIaeKAuDDZG`` zD_Mks37=voIMAz57APYi>YT4`&pMnt-Bn{AXOqX5bTno`Np;i->G1+0vakop&&5wz z&RJ(K;Hu*;uIDZ)siwK*cKe<##b2cDuCDh=^6iyLSl$IQ&rPKn8OaNn9inRD63Q&T z*6qX^>HWOXjjP->RckJ{nc_5c{;*n&(uvKEASi-XmX;KPd?0asw9(B*e@t_z<~*fc zmj3|8*&Rz2IHw|f+-mQ%;1m>Rw>VTBo#q zcWmo-Y$mC3WT3xjlnt&uXp@$!Pd8%Lgf9xqTINi8+ya=rTO4ZOrrScMJ!V}Y^y$`V zBthcIuN7;MHy195jViFV@;#d%s;;HvUNpDlk2<2N=bBizT$-4XPvq(+IS?W-){`;X zBI3%z=Fq7L#vDEEdnTzl4h_xO7q;xNw52r*t8Grum69Zfi$x)ADE|P9NFHpp)ID2O zC1s7XyA%(hQIFjQK8$0SF4*19{YREPNSaj)lH5V0tP5l*E+kCKeaDPBaPd1@@+eg9 zHX{XNtw2|a_SC3um)W1YnwlUFq|G|Sj-Fds4hy-V#y*x|v3VC|gyYm`Hx9w6W4RS6RwFl7`RHFyJCm2}`<}{Gy|D&(G+t zmeUWXBqxi4b1V0@{zS6o*Ni9nx&1SaHILUcK&qpuY@j>=3Lp>RRHZnFOoatV+m6QJ(}<7`YY zhSs!E84QbVCN8Bao31>^@m!%7T5I-7?i}?!GM={F(9f=d`ZWn&(iEQ%L>~7&r{IL3 z_WIDRw7D(!CAr*_dcL`m)zdPT+q*bPTa7k0r>?pI-&jy+14YPPxTyZyil^t!G;&f) zH~T4<{{URrgyp~cbf5esCk8;u#qe!nNy;*Y#nA}L4yKOfVNr=-~tpXI;)UX1)! z(Z;3!0QQ<+z<+wHPj*0G5!w2)9zfa?>uOOorVDyKTJEynLq^3h*aX{l}2-I7vuOyfp$AoN~+ zZQFrUuY$AeYNIK~TgX%Vk6oW#&hiQeq*b{r2iYNqS}iArWFc+K41-Fpvv9kGw&#mj zt8GuFqJipGO*4tqYzPwR9#Xz*gw6-8SLlW+MmpK0y(ptcySipw!0I1a+FL^9;$}3B z54SEp=?fq3=D`RhQU&2CMKg~ndIf@{{EXvuq1wE>=Z27eD#5Bh>Hby2l<2KpJJ%2C z>Da#}7 zp(N%wHCSDZgC)A4(^Y5`nf_(%j`@soD|c7&ymFN(V&{!K@luU4ysPwz&WZz3ILz|4 zP~=!z1q$3ScQ+WBTJqxNt9IcZaa>!^*y|U}2P9^_-KqYeXHtSIX&jcQb))7L>eal6SK!90N$w*3p(?g_f?dI0@xK&bQwg;_FGEUvil{Tb}0ZOGJ3|cL+5Y7RN)m3h+E-jA9 zTSU;~6j!@lNUX6{J55teK`KEh21a`Af~Jdb)ds_`)v9}NM?crKa;CIoPL~`CN?ajF-1$kscbXH7uN7Oef3&?R?Yd^+?OJTQvP=y`RN$t?Z74eYoS4E+;vF!z_q&e@~ zrA?Zuw&i7{@=U45fHEVll7&eVd=myoG|;F><(0~JUSaBKIN2ddP?t}f2*yyP?j$4z zSBg^AFzNKf)ebnM3Y8uyu;!^Rdy;{orz7)^n?O=zbBNJQ>?+Ynn_4$Ck+mXo8l5AX z(?RFSBXfIQDN>Y_f@h!-9p?_R5me!s!88GsQ@mw#{9r&0V7z>?`Ct@Knni=j`U0k=%leu z;F6U{c8vHMEiKL6s%!a|A)?24>nW_Obto&OeX4fVFFTI5P>?>REF4e_ z@H?Ue=oiM}&K*;!VZud8NG;7I0Ca+zV9zD(-qjeDyF{B zB0*@VD=XedaIirJF`h+@eZ9e`(5=slWTLpW8&0FDnJQ9SLX@MT1k_>(0!pyR@WzsQ z1vvh9rL{^6^uvB#OXqR(!mx7@3kDN+|xPFAH2$55y5RJ=JsQBfL(DM9}Nt}kG`y-#y!Z+*3nl+-w+;K=7B&BK(Z`%#Jz!T99 z?232rzC3>OSxCby6|M3Tlq3^5kU9Yi7=hxGCS--vpW7>gZQOfpcI{VaLpM66hRULV z6f_4yb&97fZ?_!+zHl?{&Ah9LBc{D0I$J3T3CP*@BgZ(E=PAtZ3|Z_}##ZHJOUp_` zf@ifL>ZKll4NOYmadX|y84%X0ugf#mwsvlGq*9$15&WBv*mDk3g`|(?w2Qx<{ohtid22pf4cfZrs+NB1uor9&wGKHnc6~TLgi-K|YY< zEdU75uc~i(ZvnLW4hC6DWiS`z)&-i`q)|NE!VZeQ@@_q4tD2PBG$e_EAZMtENv#`I zV216~PCpZI5^s40wTBQ3OIl_KGW_2NWLY$7l5U(`O0JQ;xO-j4o5ktec7?SpHVE>9 zWTeg@uJ1sSitS~KX@_!!UiGj$eiu-t>01nfqb;IR66prSYTEg#-N&KiD#KHSM49R% zg`|`yD7vx9f;;z)xYZB}LtM6%>)68*ry%u^ji?<6p$ZyLw0V=dGHO?)yJZpB78cs1 zJ$IvI`M0={x9cwPlHzgV;y~Yu*fRIN%ysxKmA>A*TZ~_9wAEFOA;#pL&37awMCv2b zc>X%feYkT+RffMb>-mT#fGKb8KZmhPT?H$wp|%q70Z8gdpTZ_AadBLlxN4#@SCU)k z3T?1nD8*d15Zya#01ade&&>Sd@o#SwKmgTBnpewL6RNFmeLN{>rnlTHXlas_p(;=b z8c0vLf%5nmE@P<(9BzJVX=zo?o-DI9EL|k3U;{q z+SO7R1v3hi`^B3TgOABo@_DBd)j1by6D=TaOv<`QN+j=bQMJ7g_{_`8x}Y}NDt6pN z(;}1Kq*Er02T^GD(-JjZ27R6R(}}Pa`5TV23;h-^ljXHEb#@DdW38~=TWqwZl~3w9 zAQX~9k_pykQP=HYM9c$*iq*Z*`_)m2uw*$wVX zDqhCYOxC4n1RIZl$sqDj*%?D3HC%38a=VU2dA(jMo+i5WRZ&-3q-dX3q18OO4+*PC z)9jx{tVaA*3Gqo;9RwyN)>2J~f7&0DGcDf4s?E7~ZR z3xRS)G_bn4j-cxU4HT?qsZ^D1Kr-vpM<|fwQ&4=1Gtx&-qWrPns_Yd;k@OU;6BCzNK!v0fl0&*841^U@ zIi$2Q5`x|i)!4eEmlz^h#T>^+6TANa6fY1zH77EEC3GdjG#jyMas;;_I{0ZCEu$#a zoZmlH3w_tn1}he&4ML=uYvU0)h61LrGWaU}bMvYzR8~KA7uCr#Jv4@76zGX*YE5lZ zm282~9-lJ|ghuTYyW_hZL)0xfJr^%8e(+~hoyMt0kXY2nD_GQY{30hN&q-En39G?R zpR$e^$9cEhHVuO1iVLz_Dr8))UYwl;1p5)vp`ZnNObGJN4DId*hV%JNKoNm|rE5(DEN&dhQ=eY88Jb9T{Sa9c?CfWT_G7UxfVoLlUd?uE#C zpB_?ES7JGWX`AGWExj$Jqgwl8^)(6EB!@zhl`F*0xsS~Yyk=qio_w|ABg-_w<*oBTt5>0b%VsSOY^*R)4+Tc zO<0j~#?tj&lIH!b_XL*fD=nz$+@?|MT(=3uWS9lqES8_<0IUYnx>;*S6K^Y+Pmxj2 zm~Iu-=yg{r#a7g>RJ^rrGwyzIV8mf;@o;-~k+wGc1r=+NbBv}{^}ChQABj-j>Ks6I zPN0i&^yVR>aG5 zPZ2PBOr-S&4z#*u!*uGaIZHXQve5J?naVMno+EguwAP@4+KEEsWT8C>g}jhamiG;n zsi4d)=z5*UWw}Co&uw&oXB7zRS8XY-#!_hS1;9X4WPqvsBp`coYNRp_4vh`wI}^6* zJ1-(wX48x<3At=C^xPVyT6ma7QrxhK0_Fh8dW78Bvb())QiV-(uBCbqRAn&{ab3B^N>61IK%h$ZA6nBgbs}WWBsWnq z*uX{WaQXN+$ zAIcVzVU+7zRjLq_wuL5gpIC8;8%pvLo&jWqZ9fQ5G+c9PghxCRD5d5906neWSP{RkY3uS5qONh@#G z5}R29QfJl?ypoc2LK-4^^)O!Gn|MQ;Jr8b?^koWA)Ex|W+)Pn?ljRUUxVuHMunMO> zu)e0NuIQ9hosRF6JSjyPA=MqQzJgGu3ZP01w-2mL`)LTN2UJU2P?OaqN~}o&eW8cR zQlxq@JeVzDX~w}DWhoyGVs{_WXB>(u>ZSfGb#KrDVn7KhE!M`NWViA9(ZzM|%Co+5 z=po?RAfq0n*5B^warF7LB>6#xqLfyO%@voXsCAu9JF;LO5!lWNgleMh7(q&x0n74%eFf=xJVks@^NAZKj~S{YtS!rO5r>NU)x014iHlaaj*6d@ zy@n&AOlLT*TVBhrIE6T!cL6^SjG|UJokapOumh@$_d6ZJmDLKGC8ATA+dr%gEkV>N z%S@CH8WdCd08%s}eMF&6)fQ&*L#Zi}oyjQLk=0%>>nISiNNTK;*&pdkRY)LD*HGJ` zm@yM_Jxl3#0a~k&w9?zvM@)Sn6SPQy`(+I<)4@kga7m?9fA=SDw>yK~fB;RU?$B%H zgZ`Kp%855`j&_!n&x&yCW0ltx7Jwz^eA*fLx=t%&62jx6)L+OYYeuU@RtIi+a!-tm zx9+3+a%5LF~ZmHCxZ3{|vfTP?&K#-%J`l02G($Y}uiIF1lO$kU2kQGTN z)=?GJQ>9%5&tVL?fTx(iy|>#c>s~`Gps(fL8-=HGRI@Y$?Ay5R>Z)H+DN#uimc9`$ z6|1(@SFSRx4x&umC~IwQDVLI|BR$=gm1$n@ zm|f`KE2FMkJ4pc`=ymWhkzsq$p2x{Zj&bq>aTC?n(z-MH(!!setW_y3Wq3nX^_pN#vIhJs`R$`4A*#Vsj8#3|A=c89uWvAm zR5#tb%Jm{Le?p~Ahwl~y`gcgt{{Z77%0F-1b(N$&h4NDmdYZROQlu(G_L@jPvKp7M zTyp)jv$+*N6uWP`&TEHu+?AV)N01>CUVWhQZ8J)G%8EPd0%R$Qt*TdXB)A0s02r%Z zeXr$O-OxBZek(@7{r%u&AYDf2kKmbm{{Rp2rZ5z%)*V?&+CSnt8B>V=0L0iLGkm{j zGxkT-5uoy0&l&rd;#o?2cMWZP%@b(&c9Fu(S`^EJsuT)U^#`O~!K%r9J^GVSOZlDchO!XgF_%$@*zFYc@dO@M6X1Ih^ zbEuMAAPrO`Vb-(-$DI)Rs*syx&rq444v=&)>Z=!0RHmBYP?sHWr@vURTQ@~tx@M(n zrPW4#ClYNUnsrdm1s&2msF;xKV5@LcbiGop&fomgYu(U^53C&z%s&+sd?^dR1=^?B z<;DL1mOaR=MqZvdY}`rz0Pk0#1G}Tw3cCHZ=>@{)Gu&0-s|O@z6%UQ=xpS-_d_PdR zL?rMZX|?iN1+>(W--%5IKgpQo!s&0K>sq%`DZ+r5T*plz7MATVOk{4JA|t^{JLN&z z+peIYP9sfDr)*r~XP_0B%_pp<+uJ&pygS_v#eg2b?hU#RPf1TFckF>~qaN#P=B1!; z780KztEQ))xvzlv4%t(M@+E!J(QBt3mJ@mOavW`GeLi9O3!+w(g~&{{q#Zhlkh~;5 zw5VC$BIx}%rj;o)k8oTqlP}BskXm{w&QBpEu~L+HuAln! z{fVLX!^*gQSGn8vb7#k@?d@cC>vOdfR7w3Rshmq?Kg^}5+buezNW^C~ki{?!=|v~o zdlucL%-HtL*usw!4)&ljuGc?V_!L{vTC4LV@_1MN%A`fYcSCO@jwQ zDiqVP`+;`-M7mBx!!4Y(MjQ%7{m_ej^|3yT+^upe9|fTNV#|-^L}=|*OC`f%k<|lf z3GTz);ylIWQR6(qt|GcuBsTUxnX;2=P~IegbfXJGWO^484~UCkzlLb2I;y+yH+Ijf z1wvze)Z>?YAC~Txd#!!G=TKRrdAp6OtreE{+j5;dR?E$qDI_Lw88bg9D|Rwdx`kff z+Lo{Sud`2fv}sTwbgl0rw5_faKcrIm;P&nzc7fbo`5tw&Jbz%~ufOo>aCZ)2Ulj4Esg}!n>K1xX-%YO6;oiFFEaoCf@+thEv?8 zs=n~9Nu;yf@+T|OYFW9{dTufZNgy~BI?g>f`Ssjal&$g3AhD;avd#tfk-+cXc?T%Z zxZ1MinB>^~HaTXkPoY}f6*Dck3KAYr2ujwlqClRJm~$(bq;p;!w{L>h`+bd;-Zw<% z?k@3A1Ci|Z!o$zd)e?m`QkIHjLN`q$k*1LKzi@4~hLp|UYA(x*o8p=JY8U8=-Dc$5 zt-6&Z#sbJbI?g8Td3~IOYei1K?dDE8`f7|Yx`RnudZ=1UjRp?FIv=Tt=9E4~B!)IaB%W)HaDQCol#ZiK7E5QN0% z9VA(g!9{X*f9?+Wl`lBw+Lg=P-vs403QN;ivlTD4)fM^XRQM0GwbGEGPM<{ig~I))imG>Xc_)v zH2h;y<6Q2_`_K&^bBw{kfX?j$Te3a4wXzplq?-40G$q^V5w?HY*Ctab30Hi|x4 z^DO1o>#a76^VIb1D1g$IppB%;Ls)i3#tmqqqK<0N6GYY6?$_HdB{5rE`KYP&(6Z_3 zNFYuXbnxitr16oK$_ktuvC6gCHX%zw@hS9L2~j3w=_t;Aki`88+N77>j05DMh6~Br zpBd&fnNA6kKaSq07dEO`aG@)a{EjE`ttY`+K~qN!%XYj~3k;ImJA7t^T(zU_z-u_O z{JovwwG3x9p}I$Miam?BDGrc@ytIS8vJ*2QG4YDL9kpPe-VXGQXskJ2?MiJ#1-_y{ zC*ZE9jw@|93>y}$vema;Y^kBAa+0aF1)*tMK!OOE?-=H0CpP85!%$ZL0LG+@*)X)$ zp#YvhVZRdc&l=*~m55zzxh1K(#$k0nz-6!Q1;!w)!Rt0Ipu8p~poeGNA z%z4W`VYARxC2caw6p3wC7AWJorp6RiGy zA|~S)9u(!KV8lQwQsbBGZ7hG2`C+;iHSva_R-Mw=&hDl&A8LWMBZ}tt6Ix1 z-t1IlsDhvppDKXVbQ(llvNhi-G+4cdUW&8w+f`+q+KLN>GO6lVBq<66Pqat8&{cJV zS}5^ekj==hQy~8UYy~C73XpBJLU?Q$BUJQhY))Z5B7Gr4A``#UE8vs4CE2>5q0$JCkVf9611A`f!n96(4-(WYAzqTCC8plP;Rb)XVe>(MwWTMb za;-c-@~Pw+QhTWFKivzDlS5vs+my{bJN!pX(I>&BukjTnaeQkxBC)9;&)EPQ1wswPc0Z$aCVVsCE@ex@D zCcD+qS=|ioZ>OqV;+Y3A3S~1&+c#Tq{{Y=rp9DCB_7fkP0ll=txz(u$#*6D~tlJy3 zY7-sIG@O@^)HE{1ZJ=PEMv=3%pk*n`xiKqeb&t1YAy8QyZc5@x%c_hwJW<`tYtu?q z{uy+G+m?j?01|BBC8x@n+?xtp$#9m4BpIL$A+wo>%?^0{_4!nD$~gJef)K$`GnSf- zeo$xiXnfb-&@McaD|q92)G1{&%JBrk9hichS6j#CZ-PpA5GWAh1L(PZ*c+m7;F!;h+ycEj&4S*sKz;B8(- zrhOEXqKCkUV)pjVaY-mqRyf=1cW#W9qux>XG42y1wA5Lj&E27(d=ykOM%#4DaS9S4 zPBu44{NgspAB|(TGzz1yukx}tW62>vE3KZb#~g9#y5ofJSu-;_p0Q?TMT1J{s}9A> zUWp#=I`hQM0PX#sC{fA82UW&nCLRc7w;EVnh>-CxBl1l1Pd67a@JQ_I3H2FJ194C% zr}sm@)S;(T1o&Bj=%Xce3s{HTKA9w$=${y8`lMcKrx!nxKe1rT4zeC^azPpq;|@0R z0y>~yjGP@poE!&*+9`4iZB1emv9+gD@Q;nuKx$Xdu#}n_(W&@#m1NX`m1DJt;q&dH$4cS2WQ_{HEb@t}da}loO#!ldtW8W#x9qqk<|9 z89PRyGL_iF^))4GZ&eU_ANwLZ{lO7ERGT>Lpma}<4Oxw<{cossxdxV*&%$Hj4~*PL z#Wv18C^e$!i-NNYeI6p&edLvf!00m%P$9+R}?wv*b1IDkM{SM)oGE(@A8Dck3NenTN; zw&?3kPSO;B6i6Qk1^mL&*X3|2=`dFp9YIID6Yl}4`XJ3^N>JevlGXWT@SnmhI9^w8 zcTY7{%klZ(8YDlu4rHOpY&FfH^tcv^TqgBJ(gGuqKmg|m#+Jy$w>iCg~w$1P)s z?&**c=|6;K*T%BdYZd^As8hSeH7ZhD+^>L(9yM38-BZbWB}7Qf0SDw-15kLmX!M1C zp;XHTa~k&jv7BE=M^$T>>y6QbSncwrKp|>uj*Fa+?2n*`IIpgyK4=H^nunb(t!h)N z(7L4+8F);^ip&PmfmLs>OT`LP+8-seyG`A?$}Vu;aYS*x*wcsgNZZ) z^Zj&)*AG%WE!A$yl|picq&g?zZ3;_LJ-VyMtPMpd=g}*N{1ivYmvu$2Pt#Xz48I6z z+i1RRYLI(@tMZ8ub%^41(MZLpPDQ<6@}QLKE|7a(g?IrcZVLYZnB6J-Bd?SWiPm&W zym5cYPVpc|<@!g=DW?HMxWupG!;?`uwQXvlVJ;KU0|i;nwA0NCb8H+cJQSYfd}5aA zVrKh5kfo&oMq18dXQV%S5T!!pl#S@34oKROOyE$K)R~Xj5$$zGsrNTjl;TFq`MW9G z`BtBtw+JH@{Nx^Hq9xI92U8P?Q@Es&BqR@{X;!Dwv%9(I)eSx1(*!@gEseX3)I4Vq z8G2Hv>A)~kIJLDvFK(K9jlLY*N+wg9}$c_Q5E2kG4#z|tcxj^@!$Vq)5Af$;~p zI*ujJt$DS!soFb*wxOzvb$c?K4zy>wM2*0W2<{`tpK*PYzT`}b>cRfq)wvF7{6EEe zyY2V8xT_l}ak&=mx13P?7e!oq_rT&RoNC(MYe2N*R7iPlQx))+=cL z0Gh|HYXxKsV&5~m4S1oxDqaKZM9#q18exqQtY z1eVqY51kX6=~XrQZ}S?14x%`uvII&m$=lLlxp?JrG=$Rl1MW6f$)dTxIqh&qYo!O znt;0F;zaxim1a`O;J`>lro}0)lk5T!E{Ulk36+TICz$|#dT!EM9hnN zsc$PGJ3%u8<)jN6Xi-y|wLDhJI(x;QyC1Em6*RXd%3NghpJS{aAm0%RwY%AzI3}Ch zwff(U@_l`t!0k~(&O1$Zw7P|QO$O9cyesJtcsF};5Um`ZBKR8P;uVbF?3CFZspgcC z>V)oPa+wBGi=5GK6DeLB15R93jq~U z#@$Kkp$|7DrGJ%Qv_t3OfCQ3>Z@E@0jylaH^C6|S)>M$1Y8M?qaYb4QCUb^9rGeBX zNdEv$mCW5u5cX=Cb++7PVdN}KDJEcp(hA?IkCCCP!0y#6aq9>{dyCMNttvvsy_l9m8qM5Otp))oWGAi^?03L%)U`WLcRN;`8 zry#hLr&BpW?sZVkasd5MAH^dGYiODdsZR}}v;-uDjndLCvB+1AuWb!con2a1k|(+p zWkSOZ5<0y}nxUBvrBB-p6EnyU1Y0VSND>EZ;=O&KD?#E=lQ=3HzOtv?C&a~J%_Ofz zMA|ehMjvx%oL&RFw51HWwN9^LmDQASFt?*c3Bs2gYPRkjqvxFxLs2WzFC-miMxR(~ z+Z9q#{HRXvIG5Mw3S+37aM2EA{^&8K1t3Ei=%`Ub6}%Zo5=dK{(tolPhI-tU&U#8v zfNGL-Stm*NoH!i1O57Ak`NzfDrl_~Jpmgpdp`kYLhcck8Izx{vs-HZ1`RTQ@)sjzLdubAuvn=N)V^WUl zuX94lb4h79H+ewW*=t1G-7bhV2vArvI?e_%QOe~Fm!T}|H$s#ogEW|#hFXPL$XaTb zEnPRH9r`3|4u{o8MQ=n~S#oXHR8;T#qvU4-x!{#80MRaS3Xno3p(TF93b*T2CG2!G zPSWZd4Iu$rM1;g1JxV%OQ8amTsS9#uU>#NPhOQq#ip9rQCYpkwE4{O<z7<*Dt&^bAzfQ}2=Tk1W5pv{|wGthYHK6;gp%oxZk&H1lCemqG_|27AG(g)}a4 zG%175IKs?^Y7|;ZN%?n!npojNf@jo5{{Tr*GbF+-w64495R|5KIzuMfCk1H7<*tCC zO=U%%mYBMKQUV0zWuS=n#=+vQwzsm#Kp{(?XIFZ9N~-i4fC4HHc}{&I7GgJ1SZ=MH z8C7faIlD=1hMTDx4=$to;E3TA)mA1h1u>lcnl!vd=7(Rz<0@Kw-ei;YgWzaP;Pp+tUQ*oHt# zwG$)9@r?J9omT0>33#bU90}T|Uuhvq3ET#TAu+M20OTOE!c@#ArOr;SofQ=Qc zlCX^saMtB*>O5daFqC5?0Xi5pEjp@NQqq!Br_z5IlCy+vP7QS8;f$tW>^ffN^p%{F3P$V`!&b`lS8M zD|NRD0o)*Ww3YNlp!`!gdT6&&kg!ssuiPRwcG1ek0R$@s_M)Biw5sY-pbyS*V#SRW zH!y!TihAaalw*!{{HA~SVrt-3rB!Xu=Bj<<<)B~dLVx!rzc|eD-#XiIzsg6PU}s*@ z22xUG^^RJ6!2^5HsrzG57WQY*BoEmZCWQ+C5 zEbaVb$uo~xUdtwpZ{Us4*fQ2aA8RTSp7ec3YyE4DGKB6c{D~4IpM*~0Y~3t@t?E^G zKYJal23L>(2&<5AyT&C+Yq(sf)k@9WDOAo8Aa)*+q~7s~?AQzRs>1lMa z(?r@+)l{KjY*j6X0h4NMl#%sG20pR8xrbIbkHD2-$zZYfEu_)4!1;hvosM$nDPnFR z%QkCqr%Dctsr5SEohFwGqd>-YPmtJ=?au0aby*J}`*`9RKz-X*0(^x(a*{ZwzRqK^ zCAKei>tw%lxX-bxtz>;Tmc~XtOJ#OfbpB6U1=lWcfmXq-txF7 zE@=tnZVhQGbjqftowh1<@f%Xw<4dypiNIZrw|2s(si0VnTkM;gVw_)#za$QTxTAKd z{@i?yrrKjDtmAu~M7EW?QAB{Hc9mKb-k&&=V|XTH)ucw_`V~&&Bi`OFe=&x5+v7lg z#VlI;ZQ&WpMOPL3jiRolaBrq&uAZK)B&dJxN+(c%WCN4zhAL+6wVU>X{{R&&gZtU! zHZR$fv2$tE(58nl`(yi?$Gul92(f2$je=QK1Nit5=1p;83CB|;^wN2n-FoLg<3_%7AH$mvTsce)85_*nh7 z@Dy(tt@mf=hw)Z#w&VFXE1Yj|_;y9eJfV=-@oPn1L2`1UsbxM0q(NtH_)KXk6^+**Im2JAi?I|mglC(S$q><2q zAbWc^M*5+jn-j;Pn|TX>w@R(WLt!VUQU{D$BOZlWkp5zSJ+iz8AMSv-qRK`Cb1f(d zg($j>ppSupF9jYCl9RQewR=xO6@IIg_)jOuDKTDO_RCjexYGu|RmhkYL@6s>XP{fw z%i#SZvLb3Ja2~P~j!Azj-$T(;tgv?Bm1#e(`+n4+xchYCK0!Bya1XkyLg#qg5kbTp zi*BjFGFpOBy#9{9rEbvGx}L$gd`xm#$^IYh{k|$@&ek$LFVr6$KjYWNluTbE`$+c% z#J1CSHVJ}Y0g zK40cOBIH{QtA}}N_s7(CchKF^p2KlkZST{#;M^5L>2j2i4CY&(dBnyxGw%x5^r}{E z6AQKvjFUm*LEJm_Pde0AQm{r-%KlXD?gnF&}Ym@RjbZw;XQ{C>@3 zR?~g6Z>HK$LbS~O>TK8;oZnAUwVN(P8tNW{&-`@$@|6hC5sV{|aY}Tl z2Qt8}%w;xmri}M)p(%P&Gu5pkDcA|)`E^ok&ci;YgZqAjOJmRTTxHj-xrGXMw$hoc zwqC6%51pwYNmuIIX~0LsYXRMTR1`YUDT`Sllu&skzahCwikl+o8buvFO}>TJX`4)& zYkLV%2?Ia@=p#vm#qcr5#b;49texO4ihj?pDbHzs?po4ffomjb`7N%>Dc#H-xW5na zTMuf_+D%8Wje^F+&rlXCbCNdHcg0yRv+NIt?KUoJyz>5gX2zSb z*s6vXZg#Gj#oew8ZOy-@4r-+(NJs%X%~9w6ae%w`4%U|dy&1rHEsL57a1K6dCf)9J zHP-N_lj)a|m8r6H1d0C0p~qt*8|rDV;I(6tfydI|nyR{of`~$gc^{aLQRB^X-i)H( z*VM9=C8dyDGFI9D0Axv9T*}=+Au)Gl1IDcdB^ah=uQ$T$7P20ryIYsLxs_u_`y%s< z&rEIH*BaC;+d0K;1kI76gf5!6gr=kJ*OycyP~RC^%2n6%2}IU)#Ni|Sf5B0BpZ@@V zm+=1p6%M)Psipgf<<+vFRZDdstHkV2_r;HoMt+Pc_a&HpbG`*!C)<-Uw_f;y`;DR0 zrt6N~opP$0V{vA&q%F0}tcgJr_(pfi?HwJ)MbGg@1!-bcBLKJ%Tv+e%? z8Q>pe*qs%!iJ_NWaT;peyCMVrV!E=TfK(|eSN(#mHCW9+Tb^k&f*KoYushWA#U2zVfWK1Neff>Ir&C6{l4d|}i7mvkr8=iWoq0IPQ zuN3EM`)#=hO$9j~nkPTZqdj%-i%vG}(%S-1H@N8Hy)AD<6E&&XX%aiPr=a)wvw6)hv{M^Bde_ zz^N?qR_TEKz?E)8a( zr2a84eT{64Q8qbu!tq7W7YAinr!8X4A?3AH^4kH%W|egv4Ex44o`OqBH0ZaEKYbPS zL8q#VKJ5L8v6x#c@(dQ4E#2wTrxQ$s{{X}wPi~MczCt;L9id9T!M+!6Z$;03Va53d zGmzI=ue1(ZZd7Y2a+SmobTXKjh%A&+L~GzwmM%s}+8q)`s>5-jZ9%lFY_uBx0CYh* zQaY;>ZJSzzl&Gh;k>+ge>yzBDt2`J8i0GtEy5((4XiugT z2UKWh5U%DI74c7wGY3@U(O+!>o?8-?0icIj&1UebRJ?8)_$KcwtfHv(3IbKTz)A^= zmfVx#m;T+i9cY`XOVPAdexa?YC^`vB4`(&IZ%UAv{{U-SxatCa@E&-2-B(P-YlG1$ zOu-=N2=RgLx&7H5Do)RYTAfwJ@oy$$7d&Et#wyzcpcSHHNr*ES%Npj@{Zw98jtJjI z^hiEzy|LbQMBN z#;yJdqPt%yTvqgeC;7DcM3h|4(<5id{{Y(tyP90>>QJrYR^i?Y zQN1z(q?r*Z9Uy0O8>;xHPaC&IH=EOMrfL(~Isy2Y7EWPwJky`;%Wr}}zU>m6DhM0C zAZs`f^2@J^SNmSu;Du7u(K4W7Cmh0SUDoOy7f@iu~^)X44CvzFpI;%&D%GJ#P7ST7J;dxgSzoB&2 zr8cslMw9sZ<&5EeY0% zE$K5Y$^vJg(hP1=_MRvgV(oqjMQ6CRMDuHGZBu4*(o^X;A}qD|Jqq~aYE|jH2GdT@@mW3a7TuS%cCXIxm_ik~agnK%I4n>xcSCMU4nf zW*uQw)`dQkCDj>%fG4bZN4ju7>og10y|S7yW-3vgehYJ_THIB=#5X-ApYIWyK?T4a z3eYYe2|ZAHYeiiOP`6rA0V7>TzesWNO4Nw#;_4`czRS-^A!;fzkUIS%kw)@-QXdNk zUWv!UXs@&%^Hhv2v=St-GdV`H!>z6uL7@veUtJKjQ>jHm zNf8sakx^CUxv5KgQq{DD86Lk#v0JMB$Ien0Gf7!my7iPhDjp!3BBT0~BOyQSfXix8 ztQA}9447ZUbr82O5Q>lHNeU-7Rmk}N0BlDAy#5G+@(NzG6k0@;DUS&3!$sPn`li+C zt4WC;-3_^#YLyu^NHr(^dVRs^00!!f_Y;w9VPB_Cy}z;#L&%73Et2^?4B?lscqc(i zdjm@hq=7m0kD2gHz4TsQnQ@?pkubK@6qDX!IstB|h@DYFrk@hzNy_mdZmD0YrjfBo zGMLg)EoklRp-^|VjlhK-`A-~v%~M|5leL#OdRlht0Nun`QC9PBO;->&%?@k&s*|y~ zfxAH|D0sluSr$P0xk=pPWVnuiE3f1Jd&e=nb-HJ&+maHU+JVqTqTiFkO%-D2hsqjB z3Nd?x2gW5YE(ENoPNQxStD==j;X*= zp@xe$BK_2fxxS{#mYpR%AZbMum5J38f0*vxR8y$QJ_P>&d;sT_V~ho@HyhaRR<;xy(D>M*(D<7R7MLD1WEDK!H(8 z;@SlX4SrQ6M^6ax9rtkC4WYG}huj)ci+_W*`75lbx`owYHLX`XlwRX~zSVn*yi?Rv zQ_$HTu3zXJchf^>`G!Chc)>XARzbD`LesRXcObs9`m+wk%%ndu6B^DTuOgzSC*y6z{WDpVCqql^|5Dq>6x( zF}hmHEU`D!MM&C1>JwAR*uScu>y!xEw&q?5>!))lioIsjP^WCA4s8l9q~%NFxUD^+ z*0D=mlzLCs%lUu-FE3zzF&z2a;Y55kjcKVsxD(n&74CzT*yCL0{ZnYlGlt${3(Z`3 zpVQug3zXq+Pw;pt8uJ&otQIh5p_ zAkXc{kEtZk{}GLbbn(A7YA%d9hS zJt|KhC=URt>9OUlA>UC-oXpJoOW_e>;)aU;G5(sR!}-%?q7a}Z&`k9BL(aI`d_w!tT^ z7lzQ&i&~{2p@w(VDLY(wbF@*fk@8eHmnqs-@~sJp}4Z;?X;+RvZ)*^P5 ztAZ9VDJj+HkE)w_SjFf0nhc>CAwm9dUkTjQHj4UoR zB1Jhmqz{kE5H%`A!i-D$0z(N@!eljx1~#gha(!splK%kuRTP!%knx4GU8MmwJASBB z`Dxq#02Y2GaHD#Y77t#D4{*w)xtBZXtTOB4RbsWnXq7e`&PYJ@2I2f9_JeJ&)E-L& zYtb#3&JiM?SO7%2#IN-cMFRW_ypUJ&=DkEK)dqTfAaAM_swNyU0XtY`TS!|-E1aZG zzaNZ9*<4jXZzSlY zEk7tT`cx$~KzT%l?IZ7grBvUHa2*p%oANU1T5x`+aU=5uPG5vN`>J$A+hRRcDkWr@ z9b3-0;HW-*WMU?3lCL%xW&oLM)%vs2aHVP;N!LLEPj>J$R@{CrEyG0Pds(?=S8S@` zOJqvFOsA3_aDqnjq@op<(tC=3J!5YiBh6g46~%mqbPc|`g)e9y!h9lKnK>GM3oN-~ z9jFPD_hEo$`2GWXs#}UHr>AjpksT6yLT>LI=#3Nj2g1vyf;?9~b`0w$ZFP;*FkMm| zSWz9wRE$#Xa#I3VqcS{p)#0Qc1hb~(+lx{Uu%J(Pk%Ktl)k4d};_yba{GO@JYEpVn ztUNQyJn-g};A2_(YN`@a@G3p0uSlQ8W;c~q&hf8pIgVcuP~Pp*+&UPs$#qeprGHK# z)KRyWpG8AJcb=!i@QN=Ylx-_%;hF@K<8?NQs(s#JG5CWK7&A&EIb#Wo(OF*}zeH#l zOMn$9b<VyuDX@Q7f7FcGezsnQ%#p=V(pUZJME#1gL6Hl#OSs zN1JyI6p*`a+N1hhU)81yE$-Yrz=)f*$J8q2-ciC$6AQ@pL|dN=LM0=h_|M}Ixobeh zbi&^m8>*ci( zsaKRL61<+^6F(~EL}eLQRqJTPI*|l_#x(^s%uZ?Z4*vic?WC1(PF^(8Pn$hTQ*Ccq zl%`|nwkCt^n!_uE8n#}eS`6re}KbBi^P$Q3H*`N+ra zO6eHQQi0L7$(PA70n7*bLY(7sf9~xoo7LZ&{Yt=aC(LE|$D1a_lJdQ5awB6=!&ipf zQe<@QA&28#5c5W|>)cY)+kYQb=J|b@rLv_{yz)R?iIPgX#_QQG5bK+fG=o6DKfdz& z+RS!5*;~8T;6bN?mLGB+GNQz|%L;aZl&IQjTW!};1c-45!{Zv?d2#;OYuYFmlzzxL zREH-dGCHN+YMdC;UB59H6Mj|t#UeXE3X$n9u!PB?+$2xOv~V<4eA|VgYzGLIvg--< z!tcI=R8XO5(Y?g~01Uh%tOdOjHwBOC7BqJ&?TO^n*{@ZWSCD3hsOqXIl#|y&!qQ@2 zAD6thbz^&*Pl;8mzYoRVG`4tIAU`% zU$!s!-tK4Z^lYSo=c>bbFWQF($W+N{)|38S6o|&Y=KkTR-=|I9iqn)+p#K0lFEs5s z+#eM*R6k^CxSzNTvq$EJxPA)>qf@R5sEzk2bP8eH2AdiboG>+G+Q;~%>jk$5AZsR@d>)qT794dtQB zb=Qk(oS6!zFCnJNkAd}Dhv&%f+==;2Wd4OKWryzh(#g_5uGVJ>&)Ti0|QvgZZtYyndORRgTl{W0Yf`?d7rV1I5h+H#gs7zT`3X z*I94Fn$2yk%}K7Y*Hc$bBfq9phY!dmD>u9cB?zaMAo&6 zt}#m2Y33Ja zzh%cz#trVRy@m5c^x&1dEg6BrLe5rBwlY`^jY00OD zZ`oD(bg1OTIELA?uvX)ay5gK61QeraNgx#vA?fB3uoL7H*=tAKyoPnDMc(1Bc_ZHA zxf;`y=ak%;cd_1$6=ruzS}a{1Wi9^zJtcxtNe5XItVc;Ok>IzY4$8_{nnT8+EOG@p zTckY(%}{!L;&Lic3nOa!sR!+Obz<4h>{GYu7jT=d%2Fn$6qzX?C%KsE2)kukh_IXx zT6z$w9`(sy`ED(D=~^lW2IZA0VC1*%9d4!K)jcLhY5xE++geJ9Ag->-C8Y`I7UDW}gJRD? zU9PAqB3>tSJz(zx^{EHO*~F6L zkt9qq>Im2L6$v~y7UUWb2Zd@s!AmSZoAKU5%I!{j#`c9p$hobt%3*Bfj4%HHjLede zNLor($s)_%GDtB+anIT#msK#&rj|Bv_lN=ko$5sa6Udir}p^%gz5Xi_Jzrw z6`@dy)7$?M-av-aO4X!s$%Poge0GR^aWqie#C3in?17QAiq)nNe_Q z)=?iE@e%*iqDEUF8P7Z<;8g=!ou6m{-JlwR_jqr+Rzo| zQB4}smj}Ru@`qu{7MfK@j6}_MN`G$^Wmp&7$J`a45_;F2{n{ED< zhOwIk3Oza+GW9YRrr1aDwhuwtCu0?XsxI8RJBL?Vu7G~eWFKiB@RSVJ-KwaQ{_U~j z6wd4lIv>OQR_odUy|R8k;Hj;<`dnetJ;T!*R1ZVkMK6y5y;wO!m8eDdeOe#L>NjZ9 zi6P_}nT=-~CmuhTRyWE=<+t%duQ!HljnvvLl*|ehI9)+oVM}epsgXSgcnap=ME43Q zYfNyJt#C9n@JSVNW>KSyj<7;VYQ%qdV{(+>&AlD0=hf)f0^6!zqw9TlG^K$ zTWwz%*_%Z`P*&w>Igt{auO8?zQLyy~{1s!jw~d=KGr`iI;-RM{Ufr$t8_y_IoDE-J zLDyd-giGK@`d0o5JCry5W6|##dzj2UyXM0p#A;F(R??Zc+nt)2sB7~CxR6BTdLE)A zuDJ9!jibVZ$MUjq!_Id&{F9qM*5VHoeS4Nl&CJs;2vb;~59E{AUxfSZbThT*s*GwFS+ghRbwl zrz=SYWJpX1s^WMA(d517e6^~$@-Fe)O@|ij9=cIeM+SSO&N%PfQ=D`zE2E;w9xp#4 zuSsfI)S2`;#+Q-9$0oGsu)KdKp4q{>s2r@f-h1m-Ifh(ZT+MAZt_^n+tl(S>Vx_(T zUh|}eH2J6rW4an!Em#iAZsK+6;npW)9-yl&o`9z6)$YGN;ioQCc-7gasp{0PRi&gV zes0kXm%+hsL3%#{$S*8`5p*dpW9B;UOUy;?`1q^2)hHKU0D;zW>U?3?tyvUdt>}-n zxO}s2e^Q0Q>aqZK>EaUVxdMC3MoUwm8~K6 z%GIdt2SVzF3vzW&hF^20^mi&wB=wm1L9l30&J80}*W<1;{$rx3W@vVqlk z^;_>4?wdqnImbn19BIn9H!WBZk>J77{ob9k@2*eM*m*e0b@)iT*q+eGF9f`4g^>j}!?xr|iMuQz9SUB#2h0-+!{KbUf! z8ptTp=c8vAt?7O!Om7JIB)TrZ>TS-a_YitxV#SpqZ^#v>z8h#9wp zue9Q=G^DIHH0UMc5wr0a-ZctB-;v1F>RPGXr`VCAgrt6v3qJvg-U?bSUnLWPZ+fLW zQV)PBl%)WF+j!2DM;>D-=u+<5nzx8dyvLx_&N$xK#t%f-`^HM4w_6=*7%#ZlNRc8W z$7~@D=$wz4KYEY3E!`I9Ru-WolR2wjghO5MDB~2Op0wnTkuujlK|?hlrl(;^&;<$3 zaU9+w5HusIoBp#Y6=_pngE@l!x;824?#n}K2?=j{w3pT`7_Qq`$|_o*a@?ljx>ZYM zTQAvFH|pN0Q^8})yIl^wNr{&_#vIlW*#XQbTx}Yz*eK7 z?F^3DTAgUR?sISxUvO%rZYU`2l>Re?9@*PfGyeLU%?h;8zDf@1g>{1A?e(vc3T|s~ zJW`qZ7m|=YLS{Uqc88B|t-JyZ^J|0RlGU_s%%m)nKBgQT3g5{goW6SXNm|Mmf~HqF zc9c8*#kL1flP@v2J}9i)9dL=S=?fep*U2Dqn~GGG@+{A+=)c(s{{Uj!c_n|l=0&&r zTeOT7OqCI+_(#v+mdBt-Jknq~rlrv7e`=O>CP(27f3mH?=!QJXVZ>=d36EjpL+#c9!LS?+tnb8@14$2zTOi^#WB5XW zxaD_eO~pb#aJ050)$X(ew4`p3ZbP@i1HIzbz6}euoQCLs5`_=k@6_&;v2uB)$^QU` zm8b}YcNelJRs|$E71{nWmw3vms~eq7THR4UnMx`r@thG47Ph913$NtYU*;)EU1lyM z5a(DSoKo0y2@d6#2gMM7L!OIS_ zR&O#7k@~9PGfRpZnG@Os4b>^SAhnJib?Md@ISEWDJ5agI%3=czs(z}*{hsl=If^M$ z=z6GHl2YZ25f0oL&*N#C~X#HRVINF4b5Ibu9Ji_xAt-x zQ5QcsxR1qDH#yxc*qiQBxRf@giiXetK2tA*L1z0~X4NT(?lr@@raO-^s%)nFO+wjD zi}33Z`0m}?2|fy6CC{dGbXMDgalN{u6h%s!Nmr+-knoLC%Z@ zUdqLxRVZiy)PN=00n6I8f`KN&_`TFBr!sXU+-D*AjQ%>sPEl7;Ug183_iy8jpR&$taD`-OpA5xG(LjTw-Z0Y7DC`UBTgyJsPSvjr}a`8e91$6hg~3x z3Fv5torHxYv$ic6t(>W?jxS`=0TTvOi76oxHmW)a%uY}bEU;vFtyM)$qLphiB<-B^ z`3R)tu#-5|t3twZIAd)b6DM=zE>@|nr=Ve)rkhfLQ2|;B>kwABsoN+fs&4-PKWijZ z3Rc-}my}}crs!N#%R)jm{MR=Cf~nN1 zTJ(krA&sg})wUmTNhUy^p9qhdH792JIT_ll6wZLP01u4+0J6iO3hqiiF0 z<)4{Qk|-A%)i_%WnEIJ?VGBeKamKUv?5x{Bjveq=%rw=V}4}J-LLue^(%G8 z9HeEYIeZ=j5)V}fn9tzMb^U{ah?)4z1VGch@jwV~BiGx-+0Xsx!6-orLJ za#OFQCPzW#iMPi^Np;0YRXOiAtT$TQ3166y0Qf~~C)6zi2Jy8r{@;=f>WryHGFoIt zhe9FCL^ez-xa0Nz0O_=v`F6#Q5~!CyGE!ty{R`t=M@>8-)|!+#>Z-?{uA=zO77VFEvQwnRS;@wZ#s{Y1T#&6!Ff@ z&DC=E9W9K(uhBNQdt2D9LhCy{Ix3~^WXnX%^oaieYL8N}&A3!0rGookV7giID-Bto zyV@#g>g|>3p5`j4T}#cc=%k6nMr#ip-K*hvsBkLR#k0~`U9x5xIiwYA{{H|m+AnfG z-uRF1FUNM)hD%6_d~e)Mwv{7wJ!LozH&W)^NK2YbkIJBD9!TvrT24F2WW~)<8_6y5 z{>6JkySSKnHyUP2Dj8VOyU+UtUs%QE7i(QLSRgp0k5qUWN4Oz)cm;lPT`|$fT0&EF zAkXIyJ)jiWLYYcuwZI%grWV=MDI`dP^AH=EREIxnfmKJ&JZDqJ+>LOjqhU$MucdX? z7bHjtaZ_=vKft5-L>=9=9<>}z_?8jo6J#mhDNmJOwmKov86=HD(M0x&Qck@DBWX;T znv}TqX|=O$>q}}VGcwSP3{BbC7nPr8`6sndo_Y>7rCUI$fDE)C zMUFf;+tFs%T+Tr43NEBg|we=Uf62i%23u6+|=Kg`h|Y>k4aXnkQ9*#Fb0J6GTH&dFPax z)QQV)_rN9OIZ13D0FOX$zuIzb4uw-CB?OZoKM0?;R7{I#Ggr7Sq9Rbkk_`y*{ zF*>1Txw1V9G--?JBYP?mmYT{7_GGU`A0*bk1uSh>YpntRQWPcdAc&BPN3}rR+Cw4G zqiVdqxFpTR2>~vq7bVM@goh5olhv6k)kaHvs$M7i;7M+al%2jHGoVG{@3BZ&c3So@ zH@uJf8u%q0yOgdrb*Is((0)-l57`CBkSqv!xX_ezPBjO(OQS(4T~vVUtl-$P-xUEj z2tEj7{MzkuJk=xvriLApBnl957c;>pD>)ccg%=i8E}Cfr8{}L(ea%EQc}rCk&mHmv z9}bd^I_MCjV8gkN3f_L$oKo&dW;eGe(y?=^TF~^2$`WKXfY<@`D$gUjbLT7`3#pDB z_WaAXUim`9r8GSVo&Uv{P;1JSE}AZ_q;_{1bkXShP%uOBdpj{g9Ze&lqj8+(M! zq{wNvNG;aUxF8vSGf`ztqJcq04TQ*bfc}+;>wKKG)f-~Ab`5;d;xrw^AB(mPasxkl zOEpqHhaYLmQ8kcA8%JG!5q5?ewPtU<)i9i~V=Ysud#AQ@5mnDYMy)%BUR5%<)mzk6 zC@C`9XD^Y8os{LO`;)d3vV}|BBR+nSw$QCYKO z9vVbOZf3kyU3k=QVAV}eXySINLJa4(uT21j;%l^Ona!VBQ-hEX{{S0RUl!FN^ofhd zovIGgSpuY9OC)5K zR^R2cNOe?W}0=^SD9&5F>Yr{kUsz)(uzZo)dvf&hMW! z(K`}G@6Xu>x{eZh+kQ|tngkV9p(8@`Ga*2IA>Kom{;!Oo9;T`X5aFkSb9Rv8Vtc4R zO0T9{2wKpz`feo2L%@S5^Y~_@c`w&%dyzCjvQrl=lh;Tcs;A)iW~#T_Sn04HO`{Apj~s)xX_SDOBQxQ;AS1Gcz5b z*2hf~g7&D^((k08wvn}Sp7Ah8)(o?A<23~Vd?A^;UeH1F3Gl-CPmwVShF!*Ud!3y~ zme9>>;U-=wQk0*hqTh|=m$17>m6h^t)bab)pK=D0d}&2AaPND5;AxvhCEDw0&8b$9 zhuOYa>1%?4(4>+Ah$1ewe9GbE2`E-Ck?ck?+OM~Bm`8<5>A&qGe}60EzjK)@mg;LO zw;W@8^b}2&sas#5>VRzonJ27-XZ_TheDIohuS5N|+8cX$#cM%D%r$*bz4N;c{XJpRfZTUAbA*O6|lYs&3A+W?;Q?pLxCUuEg?yEl@G&%nO7G(_G zE$yEBbf#^)g2aa`;@hHZPGZlYuhQMXtd!CEh=tX zK@yRku`kZAeVCi=ooV2rdqQTGNd(~SuS23~IBV{0@8^c=Y2CDYi@#p%^yoC>3(64I zQEFW<@|IN9;kRAbM}vU;Vu53smHzKU0?0`{{U)zv3!f| zamh^~Eb~7(a3X5j4IFDD#w{7$skNzFCTnffwKX9ifhDcrM5E~-bDQa*AF7GlSz6~B zf+_odT~F;zzqngXqQ#xuEg0@W{%glAZBjE~$;C40)n7=ObOzp24#`cGCR0i27N}YI zObV(dCU>rbe$G=q(LLuZs%aK#y{R35v%aGg-txsyXZv}r-?9#z9rla)*FL+&p>iXzB9>L93?5>*KoLZ@~ z8zNRcR%)36@SdJWlO+QdyFOOt`eguxoaQ(jWEa~wvC92E&-Ttcy!(T{=XeFy;G=NI zQ%!TltERNjC)Nv3rnL!_jX)iurQN1P>o!Qw9|fZJGwK<8BPP_-uj^62HNP_7xbA%} zu0>fbdP=9E*uB5m5SR!_vNduT3Qt{`P+)G zK2^I4%e@Obw|cLpWc1Zw7?_Nb@kJLT0hZ>f>3GuBaIMENDhqwN%BNnYWh}IjxTu!E z(kCa{#X+}dk;hFGBgkE*#(mmTf0!-nx7;Y6LRQ-Ri$aN)Lk~j}T^X;4RUDkM3&!o} z3cGK4H3)Z)IKgd#5d8ydZC*Q@N&eWXaoU-m@GH<9ilW{!d_Yhe5uv5III2qbi!8pa zu6crVBV9~br1a{mmhkThnde?7#O-xWKW%j@{aG^wQy|OcVc0E1!&EiQrrp{k4-nzh z^mq0>70hm$MD;&8!8bM`jL|z`d%8_jy3etP9I+LnPvwvg{ojmKu59-jtM#?Uz@f`n z<#QD-?MPY@On~bVPqHCNeS1hw{s3+k!W5>LEha?tAA~%}LsWFPK&Kw_c6y;CJGhbz z&w(<6Ski}*P*$ukDOZNvI&Cu`_;rGV{WL_F&7q9XG+2(M$KhN@c)%4f#P3r#}k zKZQNwaaS=hO+<^URvei?2W*utB`Z>-KjEm$c=<**nc>$Kq?YQ{=u8JVwY*@dql%Q( zR8)5+6xBlrs$tZ~p+G1QesNKXc_Mb26>63UNuvgM9bH()zkA zioO=Vxt!sY4nM|s$EHer!qrgWpQ66uwI9f%@{OyF=VQKR*J$vu4oQcW#!b!Dr9j3I zf>NM$F+TWc6l`}Qi2j9XLXtc>h-tu6RBInnmGloxr9c8Z7+}T}lN^@zz@@BdF5eH^ z3id%xwnJ3(V$_YT#=n#dy48m37kCf48*mkfl=;Z%2$6q1G6jGY>Mgghcc@6{-ua%nRXBcbty z`)h(kmaaPV?;Q*QGAAi8`u8iZ$`s~RGRf#r1%iopoFm!^@2DtKEA;i0;c`Tg?xg-! z@cl8;K!SDGv@#;1W%E@spo3dr!&@~>xy3H%Tm__=N@5l*mrUu};G}ux+9i8LJ|>M=Pk?!JBC*0Qy2=Kxk7af{HREQ}dCrSH@*rS*omC4;IGt z0VC8-C-)E)YHq3>XO|-Sol4YJsr4tk4|3|Ft%6{y7L_<;W@ppF0oW1#;Y*BH_&$Hx z58Eyj;5zJ*0Y4~x2u7&5tvlp(_(#ZXQJoXN%t-M5AFO>v^;FT}O5{^hyh+#yK5xQ4 zr;>U%X{v~R@a_$`r}mIe;y<&r5bu=>TD?{q?|(3mWi#=J+G>jT5oKGN=yV!FXEmZr zTU1Wtfni;zJw8y(t`tv!qPNetb=itn8}*0*xoM`7QSpfo-^xH*OSWPPwtjNUs27Vq z+Y3sZTV6raU9yhi<1{3rvNsKI(Q}>dCj_g??{C#O;*bM`gpUGX45PQ55m?5CfSK+y z!5qc1dU}xAC<>IwJ^pY7lPGmkH$I@Q6AjAQrNl!eK_s`TMuVhTA{14LhN`*!%w2z| zJ6564gY=w0EjdWmUWj&#g@goC_Ibx zPs8u-meNeRh?ac-QI^#u6I>?Z3G5=5dfipm9%$5(ck>yKlo4(;K-{}kjXH+lOi8o= zpk!gHR#G~U(LJEhf_pVho?lduR0r`-NF^zTfi>QsQ@$g|s zrL8C9q%vaxJtT&K2!ATI(lH>Ur(p3Avd|MUN4X-d8TA@T+EQhxG1EH&COdZ@1XS-S zC6<(w9lCYt``}|JDe%oxVUhC7t_1+`mY{%0g&F<{!3t|!?%g8vyyJrC+w#myVW~MT7)WRLa7-O6EWKZ z;H#H6Zeh@=0gv$x1yxSnNTO$W9#RsS>LHk{<8HJ~KI%izrW2d7>qU~0Y|yrpY$PNJ z0prpgxnO9~Jz(cJ)RhQdU-K2T_c0izm#AFQHx0+MAXrl?hM*0IGfv z%+dN&_Hz2Dm=$x{*}V%h1Q)k$l>AT5E3SU$QCeYUl~N{#_g`0OO>3m5azRn~jbp7~ zDX}31MxI3FWm}$lmAC%@nLV-u=@D}}u2WX==~Thi-7D`Qb+rXaN~a{5oh1Xh3hIjb zlTZ_FUz58{SeiO#75aw`)TAhDu8|EqTb)3xmiK!js&?_*GPdBtD{D}tt-uMGI!sPc zA!}r-w|QO1s`n{r7CJY=M#(2Z(nJWFE!x!!9NXTYpCDwFcWVCt>9j5%QN;xHCsU+C zOCfXUthTM&9aGQ4+#^l`E>{p%`=Ah|iH^S!6PWy9CZ$y~x#B$`Nh`k{*eI>531u77 zG?*I1%$96uRaRtP=cbnsqhj5bu-hpKPpKi`e8a{CY&0m@r7s$*9d4)$Z3SXdXaxTN z;hY+JC`O-O;Hih+J$LFdRHLvWQCi85QqXWx4T%ly{YUz>8QZiaNk1}7zp^`KD!$6N zE3xChHMM~~0!04+2%c|6MVxxlw)&E~Rf0E6NW;tsX+?S{(RsC7T&ya5C3@-z0O|9F zh)Ry0D5|QG&M6;43u*G1reb0~cA+h&Y0+1|CEF?4$g2?20Yi!^Ngefw8KWW9S1f$B zWfSl1XOA)yTiQ3IUKOTWmF^9d@g;aTV9JqUp8ihUbjAEV2lwN6C z6L^B*NblBhEqMout7EQ76!A$6lNcEtu)N9vxY46U0jiRbIY8EuZFLdSvZAUt+%50Z zzF2jll^~gk9S_zdg8|h>*HqVX4;ob;niCaLBs%nJwZDJ#G81H$icMXInHn-OO55XSnOK9kixC(Nv@95eB2ldhhN30GkGv zW9K!FK}r=4IUkAh4{F@+$lL>-p=RTVTo+Gnty)SGTR`FUsVb09RJziJqe+50M(f({ zSr%Uy$pQ5Nuj0J@-i{RfMp43^l4?H$dJ+_Xp{YF#X!1Q`Zd+S2DO{vP22x0>Yx%!P6F*Bs*!KKjZ)^aq(p;Ac zp~A}Cx=4J4HSB_d`iCY!NF;5KlzFXDWkkX8oDT4gg%qq3qG5`o+H_h~(8m*jY1{FR z)jPR;x8omGeYtuR0Q|jUtA(psQoLX{H^n#t$t#f8tT?_~W99PtK3^i^{{XuwkB?Ma zPo-zL7fzb#FRUr92?S6fsQjWFYpRAr5TFjd3?--3g^(dl5vlDce5PIyrq7r6$3kiS zR%z6M;~h;7h{cp?Fk|7>3nS9UAErx{P3|69h?KRCu2sCm<*ZV}2)8mBX-Y@{0j#3= zj)s9?oR;au#XMAvwgOWv2U8LtEdECtud3O>M;a=d-fDw5 zf10RC2#DRHP1YuLfOZ6SfF0L9aNDBV| zX4P&LmZ_Ajy#NGH@re0&oSl+jpV?`4aRs3c`y z0`d(6_ERl;XXV=g{{XkH8=zCx{nW)v)PR!JJa?VYk{-7@W!NP25G%2>v#2U=E@O+6 zqk<}`v*dJ*DG%G;Y%XGDCR6Akm)poXf}~)|S;&1Z3$5R@b`MV97~*JhoClRo+SWW2 zp))vc?n4_w%%f{AE}ZVVuBW)h%SB6PVI=`7Er1Vs#WM2Y#H}9{Xx6Z4MPHwB{BDln z#QAapP^Vm^cx}_KrefX3Byo(@WZlVut+-(xVSa8m8XL_XYnd}h>Vs7GI@FX2l=)v& zLeCt6Q$ymt4QnO4tfSmD+COp`{X+W5)v}@W>jpM?K%xoucCPVQJ$$;ff~Oc#yOY*J z8g-CWUGoCKpsF?Pe)k$s<{v57sF5@2Oga%xUkj zr*6*rM66CwQv|xDc}bMjF98J!&@URHW~pIB&p{tma)o>SL4GjfWJ`NIm#V5JM0M{U zQ*cQHgeIP;L1S-D@yNvrM4I(V3Ol>)5w&EFl7kr`1xLvPT!z1UaJS}H%UPJ?=Ws|c zwwfeG=P0P7Y0`p8_#GuE>RlGNO82};yd6tOL08cT;bt6i2ktYt#B zUubJWZ1hB@Spa_t#m5t?D6{@#!nZ`+x2i#k3EPhZ+&M0A(TxjMeyn!NN==xPE;i9xx3C37$Ep34-^wOcU6=~B* zs^l^fG3w2X#K!g}1DSE=ZM9AY+Xz@PBxnGfPGav~@G7d@ipi0aMQ=ZCM1nICNnDAU9iuk0!#?VIG-$N0N!)9ERhce6s%WoQ+t0T=OM@_9>Q1(oDrlN7 z*4HXqoL1e+ROQf4H|!oND;O(HXumgmjm>Vk21bPFPJvLTyWV7bn)%Biy&=+U)-@{M zV`NAmG{Ux(06)Y;<2mFP-%%+6dt0>JL}PJn2S*U3ciW!XO_SwW1~XeHY2Pgn(da^q zmH5G!D}ftJUC@Q&54<CnU&C3^wQsx_x|1Zbf( z9V4X%G$=rxlRsaCbQSZK#4QQjR(cNb>_pWD>eQ|Ht{a*1h35BEx_1@Y@m`>rK)b@!#cmS(etku1dZ@Ou`&?d1pO+K`ce>j+tpIv1IM&61< zO;*%&OUzZYI{HeJq^A=$fE8KFOGc&r@q{;RQ;GluCTRe6lu0q9D!;i>qP5Y`ZgmAZ zf}<#QCW28R#i1KpNLfJXKCoy_NJ?GXLS{D)tQ!cTi@bR$(y<28S_)Alk*2XVWZJkY zUS;%*{tK`_vulcLgFVWupZIv%K>q;l4*vjr3*6E9(0)JMtsidn5!{8(2Q6Iv9H#o_ z!nW5^=pi7J`N{19-DyC4l;dR^kK&qKO4oDB^1FQ%l9kHJw7%ZTbP24LC;%jSl1xWM zBUoxmIy-00=xD7}=j8k|A;XxUv*fmCZ1<+A(rW0P8(c)v6|dtHw$iojs8x&m7u(CR z`V?{Pw&c~K$?i72)dGqxKEAF>1yV9hjDROXAp4`}3O%jR{8d>}<@}Rp$SSh@3n*2V z;_GEr_gf6tTgZMzNp_mGFE+5Cd?q_e0p9JzhbXwrW;z!Bkef@tDChT#owjpq6Bo=| zE~wPhQBpZ!tf@gHsR?$B?Mjq-`Cw?TCJxjGCnmz$kye*9{wM?|Np)Pt$Z_Bv< z0K_Rtl!tg5F^8?NHK6#bKNHPG9EGv~@596Uejz}*&u{+5Grf;kJ<(>oCw-_O9Cs(e z>F?IYPeksn$JCE0?=$g={B}W<&=LK=w?%P&R?0&_;t#}h_;jc3S{?giat+>foF&g3 zUC&gGCBi5z`8q5Yk0QAEKkcgo;@x5B7yHS9Ouh6F1rZa&tz z4XR*-72NH%`fJ??r%JblCF?y+PUNepDe2Zb9MMJy-LL6P8#}+5ji~%apN^?b&qAe) zxz=k~dXB!9o{6g3DugMjhTdg_B}s7(0Hml9s5-^!$W}HLLid~}a5@#xzuC20y_$R0 zSMe^-StI`d)@Q^~d(t(7`+2WLb`9Q%>;1Ya381d@?M?48RJ5PZNUP&xEegY%HISuO z9lGcBddHb5_0zdXJ;u>7D8Ar{6{5`f3tK>eUN6;cRa{L-ao0-Lmlop6ltDip(AE4q zbw^@eUH}@TCDDotn`{*V(wDk&Ki$$In8uQfpfmwE_Pv@;q&`93{wa^Fzb%@vBy-`Pv~WYC>O6|M8n zxeQy~mNqZG>V3Lf>~(gVN~$qD*AciO7s^_CfD~PEr_uwc8Xr$_h`di8eXNy@+IcH3 zYbO=-J7$m7=S57p=NelqYox8RT}}HLy&%76t#e~lt+l^mQ&Pf7ExGWElVGhXvKxc# zIJd!GSJ>8pwmDmo^eFuvJc1@q@VsItxVqWKJ}PguasL3TmEJ17d7~z(Yo57z=2z(< z?2_tSM@f{_6wZH0mB2`6bMay%B?=jp1;{>}#buyH!LU^>1pX_eWKA+@#ljy@a9gbTV>MF>}?dX!8p= z#IQ>&j(1wCwD(m|s-7OoQqMF<0ZllgK_RYvflO?V1++A4TBu|? zTTz%Q21*C9iRs-%T(t(NoXTwvo11~#n(8+8Qpo`RFn%~_g~~7#ZO09wk07T3exj0R z*W(fwW{g5(+et!|{DF6-V;@La3Gz(q(2*5MTT-;P3bZJRO_0z%Td^xiQkNx(jUafV zYmE^1Z*x@D!ttAZMkv*AZ6vsj!CH3^@V50K-*rJ$)?}!sWpA}00uMi=L3!~7jPm6adYhojpR;%ITu2aGJ zQprty!*8rzsb5M|v^KC-f=5LssEk`9iJsZ?a9)DNX6^4I4q>WLWcZdpm)l;o-|cMN zsa{D*ZPW!w5iamOt?S`61r>XbigwY$stMjbnbTpJO7ku^e4(a^bl6s@1gd+#mQQbi zgLw{SYpY$O<_cT380!pPNWM|%ckEIZj(I&b6zy2DU~SiWMHbW?1v-r-tWxne&Zfz9 zD^lhBBh+p!y-Ygjo$P}gux7R!jyaLr+Pv6rEUBvMCsVz*8(5UTxb}-xrQ6_;J%)v2 zxg2{X1&<1XJ=ps%$T&8-r!K;neDolW^C!58nOJIqwvZ8`Sz4bSst;mB$AomLXf+Amvq@+{CPztp zql!{EG*31vLSJn41c?Yreb3Qu<@)8dj8udIWYV?zf2tdBqe)7(imw!0^K#RbH92k3 z=hmQ-wMisOLS{28+@!p%&yU$V+5`m0KEhvly`ozcsg(Bt;}F4tS$>vJB`SClrmB|f zLJ)OYk|ong#iImjO5j3L>QrP7y@V(wxuhi4ROmF?p3*S4Fo1;dOtCv^MEk&WDgD7NX&(WG z$kt*A3y$cW9xJMCtghCjnWeV@QTaoX4?+o!qt&TgvUWeps6t(on*eGx8)xGYaKl2S z=bE8GeA4KGs?rysDk)dSaC7=7dQ_-?m{$^3sp@C@AV~#OgkF%D#nc5oxXyF!31d9d zo0?ODj6SatM>hE;Q2H8zrOV@_EQC6hoM(3Z zB^r+Hx(R%AfdJJU4FZQA>aK>OrO)PW1x7@E5bUm?L56yjeLCm!3Ee$(5F3C*5>N`q z3X(|DelW|L6Y=y(<7_?h&Rymw6ZVY;HCMSV zY|6y5enCyf+*nh=K%zt)bd)~UZE7+{mr+5S#dOyim#6&Adc}I+0_8bq$~89$pr>K> z)Eg5?pZZQ1Yba4}aitTxL6a$~dRapDiAV|jB4P&;6ddA(M2=~wRYEFMK~N!6A4n4M zx)f!H&Z}o8Nj1vzck=!*NPo_+7S7Q-9CE#djb-^~_z0D=Iw-rIsqQY4M7}*@MbFU- zZPf;<(JGjb0xLJbmj!LHcnwJ^x|Uih+^q*l`T;d<_phm`0`w_8bIeo^S&5Ky7Z#PHwhN&r6OP;O_S zBuoN)3|2U|bny9ymRiUhPjZxzsnqo` z<6cSK%%Ht7#?wGosqkI3$2QnLLMlRyyxnZZ>@T?0lBFsqbdr@1?2YTUdCS|l1E&;N z?={2O+e}18sv$pA`|hQ&xy3BW4S-ZhpI|0qS!6DG(Pod5IK^C3lVRarS;<;WZJP_N zsG>TD%T6IAd;uE4;|p=tiV|*BWL>tpsing9E9N6Y<+lF-K;^ovp(V1kDFkW$)S`aY z+0AVVgT1|Mx{irgf6L#r+*G=Ec7&OqAJ}3=vTX`E_QC2_@V0w#y;~D1lA@uYnbS_1 z!_74cI#~9Eisw3-mG4uFB_y8n=pbv5h_Zf)BPn5WAVgZ0_g3~?*|$l~amphhn`&+I z=L1v~W^yV?+X3A4QWi$9Rhw_TGf;T2x1mhD*PVV#CjqNWnznn5!B(gGpN3!POzBeuq!%)*+B9IzKR zr8^O+)2y|JuAJ7ggQ^P|Ss(CGpNps;&2#lAr`4vXb5cPfQ=e#$w6-pFRK8x|M#?Kh zO`4L?VrgzP8}!@MfuN9!1TlcK<8o?L3g=9vmTBuy%D@RK*RSq@?qn1pOJVBgRYIIK z2KADDy~O&BFrri8)*@`<1@%_Ul;yDi6vAhbQ(>cN2~a>t0Vo=F>l3ZH4HQ}2_GU*^ zqS}s7tGuO6De_x$L#Z(9W+f&_LrT?))+t#n3zOaFNh9J+77YSr%EprTIMPE_B4{C5cPMa7B|-83936 za{O-Y`(q@T2?(1CpKwVjeSIL&I;4U?27iPU1yJKat25jWBh=y5POhAhGpPsT5m#3} z*+yDe2^2+p+>LNPeg16-oag%>Kdv5X%@zbL%w$zLQc-fKLZs(FddfKCxG$K)8>of0 zRLiYC(u-~~pr+Ogid2&@M5B!qH3{QKwuM^%0JnZeXOXZoZmoNL=EM@s#1rN{kiCDz zBGYMc?#DybWZ4XHvYgYQPCX(Gw!KZQK1CC+?13hLgHVjOlW_b~W63zHOjLhWr$_Q? zJkytc{UF;}0nni4i)pH3>M6h@O(3}wEHqJ*+%68~!o-}m+|KK+Hhom>2XdPRYU$w+ zIcP-#hk~^6o6zE8anR_b_qKfUn>fPQp{Q^;@@dkQqF~6%Q6p_Ad_h(+_D0&|gp|+m z<>Pq^n*mD?9nGpVxht@nriYBG6G7Hy~_R-Dm=m4`}ugsC4G#Bkjivkx9$!e9#6*wmex<^N(l=Lz_^%1$SiVElq+(2g=nGLM@Ugk zxvCX%W)^X*nx)!ycG|9rF49P7l_O>09by}-Z*kINUz|HQ|_B@f)>T5i)m175k8QFmXc8K=(L*u04#huYY#o-bWay7+ptnBd72aP(@)t5 zGW-*DC}J@xCy7t#(4c-=!$HA1?yExZJ95dLDgoAd32Pr!a)gvkSH@O)fTX!f*q3dy z{m_v8QC~L0Y}ZPpy@+wF)Do zOhnaNn4QUR%`)6b(^H}N!47p34sB4B+FyMuQW`Q$wfe(iGE)X!sJ^O(vXE|46jKWf zElm8!`Au?5;LDTRs z)V`3Uto2d);P%SqWM)HyUY%kTpUhTxN1Lu81-pLld_y8%nQRpUw)9c~8rgR;Zbfq9xIVC+U`nK%Lj8zAch0O~=Pw{uwC4P~z zgByo}^TqGcV{GHSvYTwIAn1Co9=V=|r-%8&lsLkqmg%tT&0Q_Ds$+dF?^T08_Y?$V zSJ0SeEItK()^>Y3a@=;_<=U69SMfwvtm>U;9;%WhImtpAY{09`*3>BdIh7bdCZGfn zG^qaot_p0xp-U1?Ii^RNUg_}uKCsnf`3+LGF-JzvF`yeevT^oa|@D;D?)AcB6??V8g;XikL)yKBPCYr*V%hz0Gf%y|} zyc1m7QU1adwImLJ2}I^8!O%9nR;R>rUs+<;jZ$k2agyU6a7a3V;$m{#CvqXw=$uTk z=22(bKne*mk)Kf#Z7RPDX!Q;&-$}T>q5d-{>_JP(LXK_kfKw7y(2`V4(tw}re~z=- z{06s_X>i+}PaNAS(Lr<0kw)sRELSc;EICjH)VN6+7@dyMz*SanGEEMpH1qI6>{SU} zLv87zk`>qqkp$Sc))WSE56UL1ukW*qqyB;Wp;+kFHrP_Tf5LWA8{Gc@=Rx?Zf7|G! zHy;H_v^?K|@%=|8(d1NA^%olFs#&HF)3*AQW}!5d^pWt0eC$Q?0uO?-+B0iSM~P6^ ziF3-UKP+0W*7LcfSF={tI3y)rlj(FM%b+rs$|Gfmdp3EgY*oOvf4B!lXa488uCttS z=X1KtOEwoOXqcz|olHq=dM)kUHR?*D!;7~z>DIa9SLNx0Tybyb$9B$3rVni84y>?0A6lRzC!SSv-&d2o?m*ohvE(x&5@t`PtOr>s-oSEXDqVC z&f3Uu60|zi3y&<3JrolV)_G_f0c(M;;I8lD_h$97H2QpXALISWjI-l-%~ulFf7BdV z%O@aMoI)vSFBY1seNx#^m=>2((l;peI?fA<_CI2^)g{4c3i^dic}T24&Twd{dB!vH8mbqv{{X6WS3qB8 z)Svz0zj|+LBWJHnk5B$2RBIXcc{P^Ye{U7&Ud2>e&gIWlD~oR1O}SI@B+I$-#5JT8 zYC9ou>Z(-Wb*rUv?^cmF6459F#Oc~D*cjG-$mHmy_p#0nu=|1KJkgxlhb}g21ri^n zq6*rDyz9FT`D>)79v%Dh@4iEY;-Yn-+V`HT*n&S1l*&Qf96>YD30^ypV z-pnqaYDAJli%y?GACO=F0MH&}#}n?=euX=>d^VIe!5C2c4dVJGBlOrcG1x+r{P#Na z@Py6(0Fcgi{4yq4+ddG(UtoTujrSGp(-XwFviozU$6L1C?d+{|6^*{(4V0%ur~@LM zI!ZSmx6i(APn6_vT3#BmM^LEVQQ7V_mBvmj;|zNkTn#*zI`#QubvH+O8LKLHL6kG-2BrLEx}_9msK)$P%vq0Nkno_WQt&lbwBpGc{#cT; z6i}H5kmdIuE%z@iI%Y~pnFF?(nM2bLO%idE2S}ezQ>b-V(#GR3+|SZ0*MNYuYX=hG ztM-QHR^8W1nmtKKr*>6srLClND~RWAmb`5}By zz}Z*0-0tg(;@3J$Jxv;1dFr~tzfWjZ*$tB<$DcdP-@6VSEOFd4xXIFsZ|$FI?s*Nn z=3}s$;$b5T+T(WCzQP1jEO-*s9^@=IV5LQaU(hotTqt7`n{DJZ7JBC}j zCkZ6!@$p;cAMFnM_QqSb%-xYvx^$|4uitI#y2+4Ot*g!{t1T6O{8qX)%G*<41f^4) zQF&+nM(ivnY(m`C$*d3B%y$tNzF|?}MH@ZLds4q}RV#TV9rRzZqT*YApttF|%9YTa zx)5V#`+fVm$8%ffS?%`b9tXux`=j>vHWth3;&%3U{1bV?KH@nGhwn^YaSP+LNf5cQd;-($s4XNP zh>YTRpA)FyLe=28ie}Y@sV$J;82&`RUaFKeSf9yO*x0WfN@X|>;?a6%~Pj_eO z4kf-t6;h_c%yonp@ysN z^@uIhQ@E6^wV^^pC@2xA>I_T8E12Nsl}Ws?4h4@2jlJpnBB|r&?tH7n*7elk&ZxVQ zV+^FNcG9CYI?xj(ne`o0@{Mzja&}JNZcQEg)<>P<@3wyU{#9MJ7n0+s^XN6!EOKap zvpb4-?X}2dwCXwuYuX!hs>2n%6S0Zhz;(5i%cR6N9ILLkD0e7UHF2^%M9}qJaRn$y z{KTk8bb+$@NUbehEQXC}saGBw#w5SJWNq;&j>W##^}MxfZ!yX;b=hCJ6*|oWP0WVR zNs>U1$iDG?-(=Z+BP<#=H2AM`*jwOmtPo1WBgaym;d4nT>b_icbc$Nt(CF-Q)F-lx(9!l&^fq>#6uh zLjr)1YLK@zXrsGWPqvWe(KnUuNXV;M^oG(Ajg`_@)*gtV9@?Y2$G}Md!}UP9#Uv`~ z4)sj@qvVp}Qf{H`O1}XJ4N5v1pkeR!jB%oYBgJlNqs}MzM|ODxmsFIT${i|aZ}>;= zgcDGioD|U=Y5<2E+N3enO4O{iMB0e+OGB&(!9t9C4G^4PCN`VZJx=YL6eq8KkmF}) z>V<42%2nJskgCdtzg6Zb015Y$BkCB`Nw#?jtggt-(Wg|E&v$X&IA#tB4Y%5uoUP>m z=sJNp7-mWWoM=qy>h?T!(4W9S?s|2NqH(d8GUkI^pS1-`r)%$KNM( z#^`G2P=sUkP_@^HaUiQwe9#3Ep$4dU5h3-CKD0_=A#{?j&9`2#3yl<%YE^Jm@yY;{ zw&@c;hN31co#{|_%>~s`miw91ieQDTi4A6ad&Bnd55Vo9HCZMO5%yvYP9Rt5qa{b zaiWjaSLRZrBD|;x$m%B)xnet&+7}I}(c_dQev+p%p#mptVcepxpF*DUP&J=jK2T)P zXn=;}#bEIs0J%U$znLgAkceq)s-|u4s1pI5IUJuUE_o#BKqfODe975T2g)Jm$KIpq$$+LH@eW5O3Jr}zBA> zL(vrk?_sNYq0I8OZ14;w@akJSA+S$`r4PQ6Q5v0#4DS$dpA%Yby`ISgmq#$loHC{@N7!XdI`lr&T>m3Mo<+3RDQwyd43n zRAt=0q^9Q-f`u$h8ADf1^rNw^s8Iz=k=mpAOrd- z$b8Vn!w%GLG5-LXsh;J0eut9kA4`XDaL9P;;}lnq&00iH{E$?nsN^czoPW>`t7$<= z6P&b`l7d9UgfJVO(KgsM)ePQhD&5t4$=y9>37mg+7>Q8GErkRb zxQ{9M#Hc!jUy=P)jj7$3n<`MyG&DW5hn>+!LVq<;uel0@)MxLcjRUHX>)t9EN_&B7 zIA;+WT5yXBO?@d(MG+(NgRKP+cd~U>{l;r5wz7XJWS=i+m~N__c2lW#Z*{%kQ`8>+ z00<@lstNrSJbSlnnzvKdMnL@3A|oUytT=Mogd~< zO=zS>+08%M5+G=+bIn&H-{PD&#?^I?s(ScOHLAcfOMTTVDYjN9Gk9bj6w;4VKk)U1 zWA44Vb?BivHGq~Hj;hrzDQ4qGNpqu6TU7~4+jTmEk|qqtj9M<+T}O(cW91+go}mY+ zq;Bl=}>(d|)c^KD%nOUx}lkwh!ENRgUKg&DYxsXDqQ>zaO& zp`hb0q@lzeW|aaA;ArCy1Qc9X4vpJ_i9B6hQ^LI4bHl2fQvfYM(!X|lgQlX+NYJqA zrU3r{bj(CenD%BZJr$2X7`wF-H>l974COp^yqgxJzT2Dimf8Y}lc^$6Co=(6Om)!* zYKZi27KlRAAPGvx$FvEyrCA|)X{xoqwWqloHxTo6XBcu>MP~_nilqMlD)L39r0%3j zt7gR=Qqrk`v2JWC>mocO&At2K&z{g;LHyXY)5&_Hu-wV9Tzw?k$0nNjrn}s?x9i`u zKXW(BMj~ed`OzgE;V_?4_0a_{MvMmL1I>8bIn=~iwgcO zB_tq}k<`nq68i+JPIa!SxV~T)IwH3j1;nFSNWgPgkgj;XTLiit6KeZzu=1OAB`Qzm z1WWhCOPWEVy2SRmwFoKbW0X&Yfk;^=hBBa-U>pkt^Q#h zOuM2SkN^k976KW>t;zTanBr)yz{r9J(iEGb-r7{TN^|gq29(pt2=<{rX*hlJIF3EGQu0L~U(_XY=yyRL@F6wyLlB7yZTN`$n>gr4Sn5lJ2QmMBIZYE|?O?9(e>Vrk; zXJ#j3LtNU?Vw`fiNA$OEQhEiG{m_S6KftC=S%mtUgFk7=m{gHe92)o^?T@Cmlg&Kb zmAUE?y9J}ZCcafJfSHLXbp4SxiT5|rRrxf~80rF&H1xI2ws%x&2baU7T!S977f&Ab z9yyY(zM7b~zk4oz`Nd0+7Y?ga;L)+}(J~m)cP#|!Pu5z+LNQXb>w`l?`q|gCBq&J8 zd7@4y0M!pL1vyq$%}eaGNIHQo@V1>)dx26X9>dd9NxRb2r$H{UDGfHLyMsc3WLPUr zQ>+GNH4^yz`$Ml_q8>Lm6&f;2OP0EosRQ|DbNRrAI&@NS%eW-dRRpR-@rT+fn|)D} zPif(-EPSYE)~LhOI|vFLAtC9;F(I;5pFz!fkCY8^3FxJ4n?j@2!Dsc%gVd}O5UwG>k}iftMeHJq=!RO-%@q~rBuAK)cFfr&}RiXIQ=1xOsG=^1To zh$C_R=Sa!&&ZMh+!n5)ci;b4&>eyO30+T6xL^W%vUC2-cXLvqt?UKS9nI~Y|ufilH zw+>M3xN{Q|$T=-mMR`v3A5VAY=^@q(irh3PyW67ks=i;g(>|UhPBkZB)1>0dZrU|x zO&^+5f$!I9F${{X=_Mnm}pa?lT}1#;byRiEk=reb^7{{X=l$(!?)w8SYG zO#SiA?@a0mnLikuvvm|r-NbS$|}oWhAn1Tf7>2}|fB_(G1O!&s8$UA}%c&6h1CgVh7rpSj`5^ONK5 z0Ari-{C)}+;`BMLeQ~45ae8MiSDS*iQ_(gQR8R2};0KIQad6Fa@YwY$)x4XN+2Sot z#lwSG)qa0#`~w`}8-`V96d>>gvg~1D^cRq<*#IE#TZt!g zf?iaJiO+j-x2~%H0NS3^a{{YKj6#Z(SKI54?nWk~TeBn&^bsUL>&^2|cPe&@~ zLS_l^lZGnxniEC2_C_}{(f058nBo*n#53x~9;ch9X&@;i^aH-JHE(vc9F{N;qz%I+ z{dPW9xA!e#NSM@V6SnfyDmB%RY=Vk9JyaE1@tpkR6%*DUgQ9iu01}3hqhLuLW+(W> z<+JEjcJd{BoD=V3+lD3fXZIoPBPzR^Zh6dabv1Z?G5Tj$`sCGBQ@+(w9trf&mVGn3 za)PwbY9dY+mtHn%jiICw`oF;d>Ebx^0dKE$YvvV1;x+#OsH{Q?$tP~(;%5@}F09`Q zs>W_QTabFuc1!kiQor|_z0}kV2(Z()FPa?@3Ec1ch!wA}O+Qa^<;o{ETwOeqnosq%-)B)ouz_y~VL*G+j3V-Fbibc5^ zgzQI_MqLC>(Nv_WHul+)Q`#NndzIFWH(Nb5kf!Zwao^L5idP+zi1k1_C)0Osu-;l*Tty^-YHC=Tej@ ztCA7{0z*YgBksF#n6=h~g>}bw*Nfza(I5`;s!e5n-1!d0a9vgJk8jxhGLpYZ`(-1S zY9>@fgq@%cprrTK0fOHtYE4?|D@WI1FB&#T!cW8|{{V*Yi@l4JsvW!obD_r>O-&;e zO{JGls^Msd5VV4$^BPJwUfXBp5ubZc#d#O*8;VCXtZV=g@KZnBMHd}C$UJR(RvzzB=S3AxJlD8k0 z;y-Wis(Bvwz3!g%il-OJ?-;)(;#CL~_nQu9icr*kp%VgMsirNc84t}UlG}`&Rkg3; zqF*L**GhsuBm4;c$lo5$u~!+Ue&zP_#O_V3p#K0>#B%G6kwshgZ$mAtpb~Z3OrXKX zVS7OjKZo`zym_6|9$NUuz99bqz<%`%{m-xUT%VG?`W0J0So1FndfhIroI2W6mXx&t zyr6k^cZ;q$p|P|oqv~gIT@HPkSxer>{pmKoztgJG^!|xZf3`DT@9TMQ_%BHIOw%85 zf4739i)Xd3aMz9>rsnHc6kV=7Mv7Nne>i2f)kxcP-bWNh{rgfxrV}o()NYBd_V*3m zUMfED%o)72?ep+LxI>9$R(zRo0rBoLuG8|liOsC7Wvlv zw$b1IuzvM$?eFH?zBBwxaQ%ylYmGvYU2$!+?kK5v5PlCTKdmfGrPlGEfM1Cm@(*V# zJbq7i?<=Ekuu_!gAoB5aO~&!TKE2#m_rrb1sI-sq{{RIO+SC5FU&H)V7|$Gio=D_p z86_pl?bShG$oZ<)dc@`k*4QeaCjS7carmYKkSo>t8iKj*69oKa;}Z<63a>Gsl@{^r zO<7)2DiW2cyJ7VlT1k}k08V;N3c-SARgPut{WnclQ-5n8YIxryaCLVn)jp;(`+aRy z6`HAPQkrEH;Grc*o~2#PHQf$${C(6=?W+i>Q9Y9Elo?wY?(0cIjTKXU&;6`(9xI;y zy60r5%3ZiRs-tqYFzc)#vbhC0v>lR{UXf|RW-TwEYuI%j3Z0$8-eIm}h3zJ~gG~yx ze{|F;w|iUm>hs7bb=xf%boZKR5R>>yDSSq4_j3FS+BpTej>ZCfLaSOVqxy2HJlj^S zzy$P>(Ec%Gd>*2pW15L??Q@PTzUIBkVq8altzEjydlfF%R5n6gEz-1HC_&amUN9#h z$Uhxm>AJv+iK6gvVQYF|*EZGrS zudyT&3V?XET|1ZRQtZ6Xch-JrtshCMvAhQg6>WIsHI#9>5|pI5=}c-g^1*Q4$_PMQ zVxldogIT>>bdS7-+|)0pc~9XJ-tj7~_ybe@!|!g#svR?%jV?#VLI)3(F%1<+DIZkqVV<*)ulV{|vRQVxZCd+h>Wdk=Kq5ant-_8b2IuHM{E(J^qV znV0_ndahCVJ=~A~0GTWY#Qy;KC4PeT&cE$!kKo_lq{e(2YSzQs2dGql766zNlpo%9 zw%w7-;s{vx6?2Tlcu=bS`+!ogcn2$WNC2q}C}=vRK*r(rVR~ck6c6Yg5AIO?&0&{} zTr~JDb1m1990V+Z;W++=`hdSMpQFf5WvcX2T6LGpYfOk(G9dMw;UT_jk{!yzeYJb& zx*N5%#Wz;chvg_T z>)am3EO_ow70g@OYL}GalUhx&!imGgvZA1s6-a@hnf=iqGNlaMMM_z_eO}{5)acf00XbB9{G$cHB10RGL zyqCWdq&-}wGY>LfJfHIvnQ^3lQ*mZaK5$qB=?O)R%FF)oqmZXD3a zMO8-s09O?b`O_Y=v6G9tffVn&k{mLY?uKLWkJzuWoSbI{_{V`&-r+*~iwKFaFcMupI|!GFFl8uc`|JxW|;=+-?+rlj;pmS@xW8T&QU*O%h7sbEA@qRz4cR4V|hT z_H^(_D*Kf&-~7NzdrU`I?Nbi4DYd!Wmi zuX%hRb0(YX%Wj5aUClj#X8yrl2+) z+)49Gv<~5_BsiyE5pch?DVv8vCDtBW)q5N~)1F6~NCvo%Ub zS&<1yG$_W7=FGq@PsdRoJF0Q51SgLdxl_47ujY=2zh3Y^ifG0tqGvk2>N@n7yUaA} z(j1w`Y7LA9${;-Vnn2H8d|)CG4RNZ{>$6Pf+d&9$s(qtWO#MWZ2gU%9DP}kYDtI21 z4<#-+Adqx2i7Q5s)faT&N_%tSDB2|lO}%uSJ$8L7mV?xq+ZEADaE--tI)Y*yniVFL z74DZ2x^kE05a>FtRcfTEC~Sple{@L3-xLI+RM7G*M(S#V(rF+VMcYLZtaoovCoEF` z09?O`JAW9f5$aZm96(FXKDmZe*Q%|%Wg+s3YbI)=?+SGu$xsZEzNSaz2e^d}&`Cy= zcL7k&?b9SruSg7yRG0>eKk~9SQq&Hz3M1zz0`!V0Jbg(}cvHTsqU8LMzF?X5Jt7^d z!sh6vHxkhWFqxeI@{Ye4n6)%emrkKanfnQ+3sO#+NO(>NbAbp!)ikt~B)ow$sg|(N zM7WX@!Dd+$!1OzHj-=H_4r$A(EUyDj=}gaA$iUYuL|YmK7}cHE++7J>LX-znyQIuA z8pGg>Yo0n3A$6`epDMQw$Fvg|(Q(p}RtafK{Q4;MPgq+cu857GYEy@W@ort5)-gq5 zw7U3wgsPw7w@i?&Wd(3Ri&j6bngPAS+z9$s-=HHs~~~oS^bck ziiRcFNl8^E%+*;*pfr3_5C9CRcGFz5{XiT=NsMF^QjuPU7mO#PPNuD;m zJC0GIEg3CR1P;9-V%?nrtL0?fs&emH8@2SRTaFXCB1gAKpNiUos+lC)T&wGt?kje^ zZFX58{{Z6u08~NW({&Zw0DT0b?lfs@wJ2_w0$&)Bu(d7<13i-VomJ)U6)P^*AQK`; zTt7ZLV5*0<x(*PktWq&OvNQR;6hAYX>u`gt{uo z>z~_(k~$@xzxN_h{{SkAwsmDxUC$1tqme3h>H^if;CD(W*UlzouHzyi6zy=b0d|=7 z+ywpx3uS5)c0ryh+PK0?pk_v4AtGN$n2Zn=N&Qo^epbtKzXY8=kX^Tm212Rd-AFH7 zR+(s2e|$&eik`(CR2iwJ{HL%`hycZIYmF1%V@}&`An-^ZvJPm`K+E%^*2|74Oh_l? z94LggwbLm~rCW48RpAJwQ1UvUg<-zmb8~veSBzIZzN*NO8B&g`Xy_&Ki&i#oahrTX z&GR71$Yuq zn1YSaxzB0&=?X(3%&hH^ma*||S6ccSsI|!W>9yY7eTMeZ))M1n33-%!!|{muiEr2v$9J=IAuT&F|PGX`*J|lRCXqb$i1b8`4-L znd>NvmepJ_oRImbtI2$P8)fRccBJc5BuYeqtV7>f(Y5hg&kE($aAS^zMwPU-)=;r6 zx)NdnX3=eJcyOp%m-$Q%oNrVYZ^jo?{ZK$75!xx4%T8pr#WAL$7!(jH-vbnJcNN zl0s&g=@LwYQCFt9D{AQyk)mWzc;tWrklTVv%9@oUchXVwU&;J;96>rbEwpNYRBBJxF>U4kO0~x^@VJyQ5h`2iY~gRVOVMX5I?3V z1Z$^AnQSL#MV~j4es5JCb87qfH@zMaE)KP34V=N_)z^3(@SGVm=Ly054 z;{Y-pdaHDkww(e{QQPWVkQI)J_uOEnTUA8dU9rEy5XWgQObNAeeI39`)%5Ql7@j&5 z!)JlVRs;GFYI~kc!oXtnP&W21D8~b&2dPfWWn|&3ZT(4GZpd>c_7q;~6C*L^J)r2P zoBYK<-eR%Gs{&k5=UL?_N|TU3G1P7{huJ;A*Kt*=jyH^d{{T!SJwGn;JwDQ#;@l!q zn1Y5vGWDZd%<;yi`nw;SDpC z8tM|{?DL#7AuFw)aF(g0m)UuVerZhi?J*BCl*i%_5_V$q_()3@zT$RuFhc7d3)Cb_ zQ8CgVeA-O1YPwNjjAN+k?#G86HNh2`U=a`YAl0R@a& zs`|!+kx;M)`$S3^_XthC)-&L}H)`-n6-(VMJjOUAa^SF!$vB*Cd#hEz`tOwu!ed^h zCoogF&s7d(jcAUbDQitzI&rBXT92QMUygt*T6QSy%!>=7l%{9-`bsIhiM3ka0B8j= z_^<$h)TU8Ywr-0|yQwMex2XDQ2e=x+LOspup=Bni&BH&NaA*-dq(b5Ol4lxdtP26N zDf(tXADDTxiD;WeUYl1V+`AP|>70`?AZn>hbcdxnubMPao04(=07VZeEozDOfSwIe zkURpTGMDK%P@y7|AYs;&RD;D2r*=X^sD%U(aOjOnz)0yc9dl@tM1;v*{!08LVVO^* zN!bdO9_|iG;>tS7V_&RSE@kzFb%DQ>t~1&Do2+*ADE?VUe4_bA_Hv@_ zh45x(lRn*36HoaHuG)V%DnsRfp_x}}KRSo)jE6a2b#Hio4drr=5>C)PV) zHA%gt6@pl;EeNqHAxo4pUqbqL~fyBkMSHjZo7_HY@P^a@Fj` z=}-?r)=^_U0?(WL!j+!?0IHHP3#w9eEjUlrFwR8&L8|Sm3B3Vw(SCVLt*bhTEnt2o zGDk=Y*4-Vs5hk|evZrP7(kvE4Q9#~AKtrfJ$sNDd7Hef%n4&&JSQaHCY_|@-7$)_@ zVlNWf$e!P}1;u#(0J}amx=u}@+JrizpxWX#e{4IzOTVd#YOwzRyeWftbdEj*6?`9j z8x}8k;Z7gVTw$;3xOMuORM#Y-1;iqyl{Vscb&=~Rz4u!b*4wgo4^v9Pw>_^FKdllgg{Lj7W_xY_NL@6EGL-Lj?1b-Y5q_aSd^ z*Q8YYS36e@Dut1t9b$Vk`Wv?XurF%;>#e*k0PV9e{A7j4Wvixrqx#bkNRl>fG z^IbthQ(O90Lux{OPtze~2n(nDiX%F;zACw}02Z!2MTpKkUFJa->wA7xoPR`yF2uGcu=D4Kh%Cb3j0sFjUW0EF!_JxGaW9V)Bxol%n&C(APg z3yL8f5KiwLtJb=-3W8)(H+34GkzvFN$Q3U?K++dzA7=f1toyHUs#`5o;H$z-x?&r- zfAz2heC&_?4CQh`%(Vqvx5zD&`G2X_4J33AE5af&f{zN(WhGis=LjJRza`CoW- zDPh4WN_xVj8(M?Bj;t!JYOdJ@uD^Y%>ir_hfl69=)Brn^BQKOdiG6pxe5Aw0MNh{qWIBo`Ck6K%$^3s@+t^-F zcjNFCHj`<%RW{3ejP#~)YG=1}!Li)7K}WP7(4yUsp2+UEmCgM+ey*RfPp&@qi^{nb zU8y{ojadE3Qe4mJ>3_@?)~)vz3G)yuEl6u$iAm|KN!sxnTfsqAl?Qj-OtpBXj6N zx%U46y0J{Q6S;fd-3v{B! zucK^pxHnL`Bl|Oywd_~kokElp6-`up36B^ zhsbI6>1b5$qo{RB*W)uEhrjuFh2^%ybk%;ReU~^K1cAMG1$9=}Y1EG^qC?{!h&BqKYbopBV;~(lox)&$E!)ymb#B zy;}QuCd`npLJ0kfhx_xc)8rS_Hl0&jQ?wByBJ!j9OzK~A2yo!M9}$Fd4{Dmd{cD2+8?-5+p(uF z%D*kNrN)6 zDyzG(OaQ5&eSTF11xNudvx`3??M<&DXmoD^MunE}&$yhY43*51BKRbCO#%HCrB(Iu z=eoad{^$5ll=FKPDsC;}w(68+6mPJP)iBc7QizhgQ>c$Ri);~BNliV#HzDh&JUt+PGtkK3J1nP>WCWdcnC`Vz;bG=dJjBGtg+dv7NW z>KveX#nU9m+PS?8RSj0&R3EHL9?+Fc#*!IQvuDjfj{UT9&Hn&}eZt`Uy=MOanRgMY zTI+4=QA%oQolmCgQymJDbUln8FB=(4H`~+Z3WtBr{{W!h$9x?^!}`Lxe{r7w0N?jB zWuU@sc(?g>!l&7OrB6PW-*Lu9;6IiC5;0QaeV%>}vOcBXg1cmX<5|k4YZ^cU;;WOM z(%{A2^Ao%fOf!&ft5U;DP!(_57UNaTxS61~Yqb-(|3gdF7x6 zriDUxK2L9p-9Nwu%=M+Z%KeU0AgnL)gv`hEXkgj^UzI}nr&3dgM9tcXgsJLfp(^M| zIzVy)dZQw`YxJs7(B6*OXcX6T)*0ABA+#y!;=Wu03*Y;Y#{KfkJ^G5j{YRj{6-oySBVhdQ%sfx4U3;P6nw+ zw!sHzk%;HAp-eUODFK-aqU}|T(bIaXbp!%B%25NAww3Ufg>2(u+*~!!G@_v|c~}GV zIRjZ0RUl%bsYNv|N=j6ZmT?ontgfQCa~us8wcK}FPh%XBkX!{nQj3_&MS_wgFA^qW zW^KEc5Xo9i6O&{@9msL#{{S&&wS_N983zH3FYanLyNBGhy>_j)v^R`x;q#$at8b`D zv{4k0l&OSmA2bO3AHB>{4YlZ(c?tT5ekb>&+S^y^3W?B|${lpyezj9gc-cg(w^RlY z3YXSrF}Ihh=ESX{Pj4d{_oyxJB@ROZ3_ zB5^AoS=3Nh77R%{hczI!qF*2$nvd*<5tg{tM`x=9r}4P`#xSdnGfr&R?l zm`LjWS4mQbt`s{Rd%*(isPCbvDP@UrQXVN!ujl^&dJ2Z?a)G|I*i(+YygHo~G@kQ4 zVgCSh(MLWoQES;KI)%&Y32dgQFJ_3Aqa~Ei`OBuT_WctwIQb;?$1+VQ-ldri^ALja zn&^=goUcntwIx*pa%J3fhFu{AC9SEVLS1>HjY=sM0Fn8%`1~ObyCp544G{%5Fwuu* zY6O$odP)cP7tJ*ZM_0@=PEjSXQUI2t;Rthy=!jz|qt;o=zomU{Q@998J7#Co!c%~< zEpZB89^q8kBc5yPZWQb(N@*xi1ydS`tZX$_=;+|4j~a65T{8i1>mf2v!{HXJWRw(C zoSbxWP-onj!s!&C@ZB#jPLVtYil@G=QN67Br>j6K1WrVJXFjkHCbUGC5Gtqt0Jj@; zCR6%l0vt<(1dg#`vbF`aDh~Gjz|l+4dSHnXXD;&67c)T#___rXtY~<TFa5{llX+EAAnNI~-st$g(Q2(_y4D`50YJ!tNtT@=*M`DA27>(+ z6Or=h?jN=>Rn3>U-*Go9^+j8jG_6#7l_d_Fb%{&6x@Z&*I>qlhii&oO;rq2POpra|NY8 zwfL)+lG1_+8hw8Vm35ZWRbClQ?dpS7y($%eWwdMbi9jgT80(GBqJg}$@2z=23jQHK zsfxZp(d$XWzL2LUj8>MYJsp!P)c4jVaPHQMhs*jJDI5M;Zlb`q%X^N}WJF!Atr}ri zwVpxZ=c zjjot}ZNyDIHHlW}#QuyKCW(J+xO%0F~S#N||!`je@9JE)#W8tpJ}GzuzCs-iv8 zpr+)nEcGOwoe7Am+A9t$=%CBvX+o5p3WPxgYGJO4=Cq_Lf}2VCNA8>)Iv^z!ayzC4 zP6YPwju!Z&RF0|EvNBY_^7ZWv>QGLTRN~;-6S$5|L33?HFjNs()W`~LMn-=wu_KMR zF}*})Ej{?YAy}O~LpK^mE9q70GU`&KEOa44Vl8q;9LlNVm4eCa}in5+Lyl_OcoTCGSxlqEt1}Kq@D0qo`EuEzx7Pl?Q3wRV%7( z;k8I5DnXp)4_JzffK{vmRck+Mk8@bR5%X)jb;=h8+2O8zwbtT1jGCtsiaKWN*l{$~ zZu*{2h4nE0J0jx23|8Vav7a!I3RMpiI- zL)-`9Q%~5G?X1gPVq66jBtn~9iZtsuOPxfQ8l=8uWN!OuaR5rCk2?#OLOPGE4>UxC zWxd|l0pOnuX9Brg8bXmoWI)_Xf3`1Cvugb0xVkPnC4EO0+)89<$O3wl!^cxZ5S~za zCbuNxx~g=zmD24Uyt;ld^p@2McF!jEJ5>{T9}!bjwFxxfk`C=YaRGM#En5!}&VFFk zR|Z{aUB2;D*l%h|j?omGs5ZTOBadVR*1**x)X5W?dcgYvu?Y|d&_R@;mkJbIPC>|# z=^bHC)nf@tLKzy$IGL3UN_tDQd<9U&$TCs?05R9RHvXus{*}G9fpyJ1BeBL@Q&P!B zqpN+YuD#T^PQhA?qyQ(Z84OM(MBU%V6Kdj}>o*YIu1$v=r&4DvB!9Xlt#Oh#bXn$0 zxOULGfT34T7szPZQ;jrNv=bl;}o zZjK851?*F^bY`IlWq?4JcvZOSnvztgp=BVXs7MZxAjs4e)P*WoNJ<3EqCt%SC@D=! z*HWMW_`uSS6u4Tkrdb5c{UOE*uFTYbL{p*f1VTYkHz1?n6SQa~VA=%~NvbBSYz2)w z=wMg0DLZL2Tbf6dOGsyJ6Hj)lQ>}mu$5=%Y*!8;~vpI9SHp6Z1pI8*L){~jZ2q4@_ zn0PM{)Koi8u}nx(L0YBb6ZUwb=K{Rx%6a=*2&$1^^kMJOD1MlqD3_GQrIa4~uoS?HCv+kfqe zC?nB8UR|R~n@|kSTFY2bi$ok(zNA4zYh6upSwnLwAWj)E`joBJ=ww@pcd0OkkxZ8s z#Fe&SM3ix=Eyj2kbwyP+XDP~%q)JDmp~DqQCM=6xst(<}`A*$OSXZ{3ec%aEZ?+W_ z`DH$^cxHh@-Cm~_*^ie|B%`l*GB_$IsrT7!r>Zch#MG@ra??op)<`&c0P{%8hBEs_ zXJ=2!WMV37XEduFo^7u2NV@BDjgl6P)9yx^L|oBTTP&}E=#teGx}D9Az8#`D(JLG> z=!$A6+vphvedQe507u7gQFE5o_5N7v)=(h?lu)}R1y#K%6G~+$Lz_T~ZP7tpr#(_7 zK7*t*TdI6qlm%!yjG1Y0efju_$C^U^fgliA9dQ=0Z)wjv&G@Kk(D@j-@0%PT@;R9w6)VhAtzZQua=%QR?8K{Y9k? z%OAcgbGKD>gZXOXeV;7?$4TbaLPOyfoLBOK$Fq$d=~I(6SQ!0Eil-!H(kZ zF=T_~3W*g2r6~U3)k;PqbqO1anj&34hEj;!^xJLCQMh=$%iy`q{{ShwSSsIBTZ@WO z23;m&&X@V&6x6ErE+2-Ot+!LbG1*m(>vd^LwQLit{xN^!SrIr3FXfm=TggNYffL)> zU8764=(8DuO7dF3v8X3OC=&idwMb#39_3O`+wUybaMpQl&8Qd^b#{}vmK7isJr7u8 zj^!O?bM7~&RZjHw{ytBLb(75+?xjwjbH4lXe{KH@nymuQInntRga-)@&R;W$znyIC^YhLG*5q_`7K zqGYEV37)d+4e*|4kjBa_*f%lW3VZGYuvlI}ZgMvSvRHxCc=;)-_X+1b^B?1!fBdxb zbaZ*=mmnx53q!9Xa>ywItmUj$2Sr9s zB)9{P{{Y2Y%8sOxW6!MDs{Lt(fX&G?LXbH-LyGuXp=sHEI6YO zJ;MJ0uYqWtl^^?T{{W>^FSiWm?Gwkh&T&`VQxM5=o=&}FFH~`(Hi|3#T@oydKnn_0 zoTawj^|x*=9aAYrL)=_vA$90=q4XB{fvVKIeU-rrXZ>Y22O4NjpZe7%eZ_KT+iNPy z@b!BD!?FxUkNLwkr@S><%L`d4^bImD3tHwi+imQh@#vBbPM|_$a%Fw`l}al^wH~7BPTf9G z)xD*Rj`c}Gt2cLd@Q*}$f<349zhu8W^)?^OxNcDLRkbvWxf^SWGMLf?cLeSaI-NL~8(>KxDs4 zEblRSZ#_XODSL>i$1+@{sw}Txin(6O&+!c?D#3G7} zG)zl;abT;~*;F2Ej`C2~PSFtzjIO0zi*5kWRVi%vesPSi*2gdPHrHDKq53!6WcZjhulUs+dywQjr;)i9yDrZyv{szT?@w-XC8W30iaH}wDH}?b z)jA52*i@$>6R}1BZ8|9oaI0%tmC;w(uAvI{1@CwVHu(G`Rdz)iZ;yNX*$KUri;<-zhPEGCN z`%9Cy%Bl^Gn|*anN|;Kvmr{ykx=V8s-O?!+zxZ*qF?*5T&?_CM{y;d?-~AF#=~1(l zzV9{8SaJ+jvx;i!1ye86RDdc{^*WS=AeS0a3V}~?C^L$G;uc#Qxb9nYJp>gh@4uck zXZehXhY)G_(NXF$gkNfE?#n;|;NsnxCUS8_UH)K<@L!p{>z<6B zLR+EIM-o3u=|}7k`W?;r-R6_A3CA1Qk?~pY5FOICrMzGKL;IBPXRpidU;-Ft8 z-xEQX=YS-*w(l1rNRialEPrGZj{g9sYVLFS?)+37DtmT6Y#^1qe@yFTK$(pS^?`X07P-n^ z!8cE6)YU-Pb6b1_Gg*VI>coBDD684mwf9*+%3|KOkV%=68!43a=_trJ%sVpIK>q-l zdgm`D>MM=&AGFQMD>j`!L{(-?*+^rQ@4A+is%b zmAz3=@smt4$O>B3$l8?rYI-QWkr^j=Hu2OG@uUQHGY8sOI*Nj(74 zr{hYE6}Wql`(*zB(Ox@WB;a|{?zMR6Lzj9d8$trYNZwm5fgm-QGt=V~02x5$zb@YW z#ar^L@1dR`!2KFW_o+Vi{iNo5S&n9E8?Sw_xNm6`G(J@D{A#LWl`=;zSmW(Hpj<#w zXE}o*FDWQ;PX^{+TR@1t?D+I zs9M(HRFgFof!KDBEN1@z@t26nOBAAa2gT5@L-GFrzUi@+^2A^UIl8Ggmi@`^ITf{} z(_)RXqcY+PsygUKvK=8=ul!ip<_H%TE}-&I7N309)*hxn2~dmO_r46{mUzv05^Qwz z6g8?sYwMDwE!Ckq05#Hc?H-5hcm6v#_Xo)gqa-p+!9iaIjrT+Ec6{xd>>-3k$vV)W z{@nQH#ZijeE3QEaaJG`Y!>QY>dOIUP@&H&}h4GPenT9Z$(INCe_)>6_V z#8n!r4m#!gs1yKYbysV^wAx!QRn<5U%V`tPs#7xgz;@Q|mHR+b{Hu}M;w=67O#uC= z_P+6UMa5QHtNrYs)VWQ)gs2dd?ivBQG;TMBY*0PzH6Sm~uITq;`rd9Ea?;jr{b-Mf zt@q5b+AivCNl{XZPE5~5b^VbSXKd}HyIPgCW_g?zg^;tr1vPk^-RC0vaLDMXt7_e@ z$DL9F*(*^<6Ooitxjzwt<)))mwRYdRw{ehTbwFJM@GrZ*Ip%9E6-6!CnroBEPpL|! zfUU%8HIF{>9tSt@Rd4qQ#qAY1oi*8=tya>ykfaW$aqx``jAI!i3#hBNe2zgcw_O!YS{W!vRHYz3 z5n`Jf&=av`TvCRf=!;YA)roqv$_s3rWTcshrSk3}21d#7N-Eb1E7ud{ud%vpfb>fo zqu+i}f?JzmupL7KZ{{RO4%!nRl^w_X7x%8-_+LhoqV`V~<(yj}1MZkZk6I>XtM0pW zrdOn^PpahgSSP5{!ZEo%!tR{^092~)!QvJXrg|pB`AE8 zG|x%tD8BJ8v8e>@63OYRgUIp@A7bxujC0874@DiSxfc1FSG{?^)S9~I&gHshbeBzI z^tZP?ytU()=Gwu~FEIBfxzEj9)PUAEKnF@Bt?IC=a+Hu2)iNe8A*3v+x7wzv;JS*H zC?O?fGnv=FNU&PM2vHYzqjE%DMOZ?Tpr*+D#&IzXwN-xN^dv1)wS`0#k_M9yM0SO$ zDtqvVE>G07(wr_GdwgdPdnFI`lnG^kRKB&erKqNTJamG?Xrb?9E>eD#^{JGrT>#c_ z<4qNP)g@_NLGu}zN6CU{it1`awK|TG@=XeA^9!4eF#gvF+6<)}FVQgd4YIs~;-}q0 z9a%{y#&evzK=Mq!4NcSc8?)*G33oxLB*S7>uF({~ce*h!uTcbOOwabhgMkUgvsLvb zQ(mQn7ndRN)c!D+HmTLJvboxXt9r_`=yim>`?O1jO{oC#i%*9VWKw#KA~E)&s$Sn9 zXrFFA$g4?dq_}4(BnW=c6gBjXBB}nkNSv&gyY?4|{c)D_N;KKo?JE3z7@#XPdpYCAwW08jei z%e;5E>Y?m1Mj=IBoR{tT@ouSe(sfEH1Q9YM1Vr6pND9YqnFSZ{b`f~6WKKC!rG%)7 zAd}e28^+nWj@2Ue{jX}CH_)Wol9Q<^0Q_QJR0XL_wHvmQvQL20&d2&b zcXhO@EsdCPs!vnrN^$fgEUIdhzB7~r%i~%pGmINl)#BQv)LW9ZD^QWNeIjDsN`+jy zga=S9JcyFIcS+QMsS)ys9K+}ptB%F%3L#WB_w@n-4Ai6L`Sgs&Tcf#aaaq->S8&Cp z*TX3YB!uWskAI|DaaZD_%~f-F!A%p3$yO)Xr@;Q&xCo2LpP2nTwM zk~K4fJtL)IR(wbsSs6V$=`6S^EZB`ix%}mE|QksB|r)O5fv^`k(w|mD{L(D5a~*f6#oF{ z{z0!5*)7HDrk^wPO34C$0%AHzWtCX%Y~h2T)hIHaHDNgO=u;sj2qY^{2t<)1t?o#2 zg0&PcSnn>=B>w;{DwdtKh}Sh(ZKn=KFS?}(nauZq%ZVuFI0dPx?(3j|-cfOCbxkKT zOk)*_p3$*FW90(g*J_Zk{$iz6rBxi9;w9xTNnebjgeYe#Tyy^b)Bc5Q_;r?eGD?7w zn(+SsW>I2i%BVBGk|-m}mVZX?S1~PBJrBxoBcZ89w)TJ7v~X7zK*Y2#ttGP)jQmF@(|4{YC9Iav-*gX!-_dlIZ3NZp7z^&PGJRQU%*WM zk)<*t?Nxc@laY6#iPxH6{YCIoB=6V4C|6PbV%;%UJE2E-Qz=eILq94Kq4Dn=g}$Uq zQH}P^1BZH5es{wuwzYHpsUAuY!v7S`pzSo6`C-fn<_TjDWrNPA7If7 zGpOWeOea!2y$-Opol`T76U}36ig}SaOF<5nP$pk`J5zH;?$DGU%rrlYECeInK%%$0emJYC zqP23qF4-5nOoZN|}t&8pF&?bYDa4O0irm~Kiq3hO6&=WKv#dc2afWXGp z&N?aEY?}i?cBv(6;eJKAzK3#3hU`{{Hu}^pD6Q8JJXO$6P(`LG<=(AAX6KYJ3PvbY zl(t(FYNe;Q)66wWke14%PJ%;NnfAODMbf#>E_YInbZNCX=?x{hmlmaMNFF4eA&FWe z6)3xQI#WiCR#X*qHwpfRt-7$>x2d%S4SrGBh<2!%;&ggAP@(nyy(PY?qMDG`)H2$Y zPe(AHRX|K4eWxEhow_FCj5*qxEBxc1Yc~2?Mkhv2zz(U>Us#cwEWDqN;GpV4c5WWr ztjtm@euzwol}Jh&87Fm9a`2eLRBO+@zuOpXOA6(FMO#V>B znD+pWi}cwhT7yce=MlPfVq$^(0IK4_Vqd z&!UgZ@=JSo3*34uKHz_8^;a}f)YMQFtjOw}BT2#WM?8W?i^N^d@2=ODMpchvp3xND z8-MzAhsrd|Za*6ZD7>?j?v?D1s3_^*9`&^&${<9 zJJeNidy)3sx=KpA%4#o3+IgrbCPzrmc{dv^Wk6oB?8kY-z=3N&P_6wRZjT_NB|aM1%G9%y;0RKO@Y_lH{jIw1rB z2Xdo)wCUD9bLyRBG+UUd16fGNBYgsHSP{(?ducvm0r|lVxos3}&7+DtZsggQn0z6| zOrsxURuwf&tc|K4j49-JB|`&(+SAEWe>~wK+N3@+Rhy0=iO=R?$B|7ESi+3O)vWNLplG}U z$vK;)km{d${vKtzC4oqiQVEa|HwB1U#rAFEP<2&#?+=O6#fs%n-7dwpg^}ebt6#^5u%8;jPFK#ZD~!$@PeexJGCH8=zcNu0I1 zClw(*=pHFyY_mF*psAC$O-Md4d{KF*8@!Wg)jas!l`9A6QcCxSHUU1DUeYiH)uNxqXAOF$QvHezt`s&1 z2g*P(Te`Ndg3~bhz)cBFRegu_>QQx~QaUT9u?=-6Q;kPi9YcDif7im`G*lr;L2{Gz zG)hW{lr`;Yjf&tDPU{67Mx(qpT@;LOsYS20W?&aIlw@)x&mXektt1YT*XtHM0QIW3 zEtGpoQd=8YVLhZG?ip4rTJz>imoRxK(?8-r-xU1+0Lm7ZzztrRtX%$1!%||M4^^#R z9TV3=pc-tG*l7~tlvT$#RXLah{{X5p{_=l(CBHC8o1(NnEPW}bVRQVTb)R4&ej!kx zr8IJO@;c_+r6lwK{@o!mjZ@J8O;j`GG$-`+eqd5kqxeHHjZu<(s=V)r-EpYeCAC_9 zFo)>m%PLy5(;-QglbC`%y%4T_3Nm*_dHO{Pg!<;Z&PCv41 z%US$HNVazc8$FC~>Xq0=e^rQN_MDNr57xVDa5x!Mz*LV(!C0)1v2Y|KqN;M|TxlWSQ@2CT2NeTjpMPWeOk zIqEni^^gHc)1>r@LqQ99Brj3DOVu+6_)IqxW~hkX0=ZvcIsK;y_NVV!+gW#J-EzNU z@3lil#!?ZgaM@!R>BF=>3&K79#@)}{EMu7-=&R~9&`|jPwOG7i zZFZfCa8y#Zkc5$^@PH3Uq+<6DtJ~RmVQ}a*Rv+zC-7V|dhbB3jGQ+p5R{qO$S=Kt) zJE|yyo6_q;r>fB;r^itiNN$8u2QM{u*Rk;O{7a#P2Fx{S_@}GxB~6F>t>n%n=FSAH z{{T1B}xUCveV%=;H|=W^$a9`Wx#IlIkXn&&dipLcqMa2EHR^`{07CKFw#2|X zZurhF_8dvFTUTAw>Z&=FEyXB>5KOU=}Jo|}qzBS3~@vMr4Z9PP_{+hHQ zVVbrQvXJv7L(GB)Sf@J4@xF(_dfSci+gzoqWQ>ZD)5Ikw8`HRplW7NO0&_Dm69-7J z;(lPExjvw-&A!YIrC!uM-6VcwSwf7@bkjQ!9Ps}Dt%LFY;3LVnWJ~ak<49ME*+?Co-_9sQY0Ir#OhS+!4n>!B$-Dqzm$ZV=L)RO zMtynGRBuAhmFCm{0PCroQKx+hwc6W9i<0ZvvPfE6J* z_uu!pz8mBx@ao%s6U#V#RV6h=C;6`qw_Iyhw-Q3qs)|ER;3Xhpcb8vBVR5%;a8TR} z95Pt&;&!^HRKzRae+%UcPyJ5z3xn0E$$q-&j9e~Q-C~M~n$nadNSw}-R6sL3kC#Ws zMQ}J9m`9=YKilE`=+52DKW+S=s8(ToufkOem=!k5mA4?K0G@%mwJm2~_F?_FsWqu; z5^6vZCNC3A`+E7QxkDPrTGTKj) zLZal7@x5c$@c#gUWc|ia9Cws2*#7|evAp@aFr#UV-Gm0%xv(i4Exd#RWC}>qF42O< zf!2z*k2`^NDN?#Q`!Kz??d9*q`TqdJVzejW{{Y70hx=nB?+%yN{{Z|e)4h{5#@v2C z+!r)eb;WdJ&Q-CXl@9$PEWC!UmZ#f^tZKSp6jckRMMF!dP&QZ}iigDDb0>pdVTBieLNz~biBH5m2c`^ULmt1#n>y;cpET9mcg zvO7!4qO4(SK}w2TYF$Cp8H3&~Tz2)<_WTb}sGQo)8KN$cpwppJUkdl{??VR9ThFMu zuH|{oRX}ZRUoOm1Tneql-&C@st(1bD{%Q4}c)U8s-bj6?v~90Xipp7R)*bQaf4AVA z{{U|u@p;P}=Zuq*O+AMt%=4xc&P)AP*5Bx#n!@)X6x&N{C#fBw=i9bu_7Z45H2uhY zpXEot#X|mZ&2Kr!F=x45=G#kTx!Jc;osK%*ZYCq zdQcjE2yd{C1j8$Mv)smCa>SUeMk{Fz*d1{i)77bkx}_lYBuiNDbn}u~XRR%vJ44RD z5u%ms{tRYtm)G<9-4Fq!_!<-a;XS_Pt}WF29`?)>zxjky)CW>dpn{>SWSF~h-0k20 z0220*y!SYji9L1&MU@L}S8Cu$6H(qrNdwj|c-tq*B^nhMJClyu+`Z<2DP!#`o9|xT zbH#Ekl7&@u6scQDR>c$PyaH7)BxGe9to^&U&0$TQ&W$uH!JLb@89Y}c87x$-Z0#n5 z^dm}-*ryLa{nqbU{{S1^@QTZOR`xHcQ*{b&d3erK9snNkQ{i837;Zg~ZQyw>ZlGwU zyM^C=;PcFP^xNXb+te2kT97rSwCbwP@0GEC#~W~kHkBboM5RgdEcDcNjO)2~jh^|= z1F2r<><%&379u;wQyb__bD1?TjdUp|WwSs%4Q{vglg-smB|~DIQJC*7CkNOMT@aZz0~JiohaIM(X>&?YHllLskr<9bHt#Fa*sCM$gn`ewGU0S-#4Y7)R+0+SUfMts z3I%S)*anpAdmg67Zk(f2U6we3O-fnx_ew`{=6@MMk-;a>$?X3CFpU-m-yF*sl(%Kq z{Wvu~O2Gc+8HXRe$Jp(!6I(7;)S)d&pr0xvBsBQLouaV6wZ_ity(`kI5$^TwCpF7# z+l&1P-TAtX(a9jG)oBq<0#9BpDobt< zscAXJpO~4)O)DgexBp^YPoU%pTSl|w$wgGCLh=+M)P0O>QY$}fJ_ikR!iBwhI^ zLs}USXB~J(MR`z^7#rWsXdft^u%Hmvv#B>oulaOH$jE64b3y2$x9Wn_J`pfXk8g}{ zg~Xn!s!Yh9z2lCePW02|uX>C4(p z?E;W_$Gov!M!I^M_GPf{n#P}jh9j_viODlF%b`#^kp04A)y`C{b&UT2Hf3jcm)3FL zsEw=WqmkYU5Aq)-<97#_={nlfsq|3RdiX$hQ?RKUOKB>bY*!<)IdBVLr_@rq>HV+- zcPy%smKW2ab+1>p-=XrW%0#E9gg-0l7h?n_jpxC2TFYLs7UM#VU`6!{>*nbRMK#q# zPrtiBWg;egU^3zgc)qPqrxW0m*?Q20HXT&PrX*$ zO#MdIwA+S{_Ab4X=N(fr6);gp`7&* z2lTHF)mV-}j%QUoIF{V)J((p#%m4%tsF;gAvna}{jl+iYH#(tPjJ%z*1Q17rQuzhR z0IT;Fv)pP_Zbmq_>Zj?cN($;VjJA2*y=?Z@-i0uDT8X=AnH3F`kOaiKP9|)xOGqjM zBZ}VL6|Hec+h3P`-s3C0VRh6v<)yt5d#*~$YjdQMhQKH18a^{V19ciKmpR2Hbcbox zML%=jY=3h;(Jl2kHf=>qlwov+by-cUtxYnJXE%`Sl%JnSve@P!mq4m_*jtNs)!h^- zv2sTr+g3lUT$*2|WE(2(4!sOotv$Nbl~lhnme2(!F;8`zBZ{Ggw%szFM7EuYQm5Zj z7D)0?uxZs&B*jYl@6am}_QUQ=3gwHMEw&m$rxCZJJfeRg7MwKBou$K|R8B!^El3_E zT&^R5WWvnV)}G4m=)l|^2r>h#=L}rtplkM*3Zz(Zi^&=82AUozysCYJzCo*LKIgaQQzUF~akT(ue#4#I{J!QtVzthvLDs-tp zNCT|nmpF+fVjeC@Z*L&o;F>>oe{n7=#W7ks96sjF-tx49DOgtGohSS=)_tM_3&!1Q z^63pqvhsd)^{f%V6#??^y1rTE%9oKs>$ch#6hd|qOu9-e(q7O}s+AT2&ZDI@QL2lM zFZnfMU%Ec+V%}8Oy-OogKJgUyIo(RJ+D8m6pa3V=6XFUhV`*JWK?F)umuPjc0J|*Y zr#JOB8v`nmke^6&)3iD?Oa~VxgE{1?H>n?_rjmgK84;`q`+yOQVXB1p_ZfP*+JH8# zN}T%4P6U%sRNae9P&U?=5+r9hk1WwmG*w~K$~Exq0m>0^`cqNNWQ<<06Q`!Bz}F*1 zCy#HfQw6c3QOd3%8t!}dDsi95{Pn6${{T<=70_W8m4A?0T9K!wC8xkemJv{>mxhm3 z`OLK7UMi9Vl$R8LZ+KZJz#tzuD8G%LH18uqJkyZyFm0_`tNA0iC&K>#UAl&Qw+$pa zog!2>(yOeKYm{=!stQ=BBT@nH0Z;O&+fz~$9KX}KbIQFUG*Z8|T17(WR#mN8%d!O` zY!(+Ne?a1f$!$MfB3BmbIZ$~V+t^ggpsgsw6x^h(gJh&bCqv;+el~)krBy@jwYC1G#e~XdzkbmZlZa7R zaZWT)3C$#-sRR=c%?jBWPi>j9EtK14WStMr4~%G|?kiMjMuKWay$F%|!uFFcEoSzf z4}upfXeNaDg=SR=_?ngn#ip$(KwJ^ zN>8WbFf@VsCuM%BFWxAfvRt2G^pq4Rf(&({sCgO!HKSb}%8}YCwvkgySd!cH>k|t~ zgpP-^KDm&G^LCU11F0$YvX;impL?0Oh)F|T_Y1&cW9M?CO;}tZoq?@Ib zhN@dv=O7Ig@ft+569;rq6U62@XsWdas#FM;v(!uQjz6xc0D`cnv@u^wx<}a#u~3yE z=F~cZBw+!Kc%enI<PV9I@c8B#i}*u zHzr@Fd(%jqj)O=h3YA*PpFs+>xzUE7rmF#wq{>Io#O<@q6$bHxdZsR2va|hV)$9PN zb55TaS5T+1GP#39$-QbxGnC=u9*Sv~WFoiiD3|5w0vObUSFT0Nv^Bf_5Y`sP0ue2} zcbW|4m81UvmLy?DAfIkQ!3R}|vo9b&7#>IkdL@jLywGEhwrBjY@{~F3H_1FQj`KiX z$61)0W91E-X89sw%e&1=FKs+Z#zA~$rOHs^R_a1D)2vx|#t=&-O2a$T&!1~-SPcyq zNL&ZOw(C` zeIhybV!ajBxu+i#9XQ)2f6APp0?>IUj%nU&SNUq4Nz8+Eo%w>n$)Lq89dg*ic`u@uybuMvI7bH94;*=f)&kTBjhr=YUaGMZsFkez-psWvbLD7%f0cNn~+H9zGnk_7bHU_AY(b-7ob(B;j( zS)Yhpmu0k0&}MhKCV@ajLgt_Q(dGNbZJ5!1=5hO$&5e!c{HW62nv@|V0#A4{DxhxM z!cmj%Rw<$hAp@|7omwGbYXMJ(2W>Yr6%RJyq1UN{u_CWo$8e-9WkR$Y6^)v^YbZ-% zPKl_Fr%!}f-DYE$GTVRh zYDn$^VCKZes?Fyhx_+e{*854wsF_lb!SeJ@)8X=m9zj7xkeF&yU66Za%gAvI+mxjN z*K7?V0a7sXCr8yX7f)=NidN!YwXb16;38jufMv$jSnc`Xi&Vnr+$~dNVc*o!sV+8r z-dRgY_lRr!ISsFZzHu(%HhIHAQMlubDb=u4khGFDAF?aP5Hwzfw3*ImCH8zDgjGKc;i`@t@rqQ_(5c|w&^iyCRJlALH$|uR z+aXysQP&r@)J-!%+McE;F-EqGjKZ2Ho`#)UP?97x*W(gKiUm1%Qc}giwH}iIelmqZ z&Fl!{I8QVhN@5; z-hA72>pfsLxPpihk`ynfch{_Odp|OX1LqxQRrBZ37&6h>I6V&IqV?9!!!t1rZ9P{Z zB{q3b3Tb-i0SEDcuC)rokLshnYQN`DCn8oqd{$*|sb1k8kI=3E0NL}TSt&r!6oRkD zFW6mKMT+trCMtI33QFTB&O$CT5P6qMbk` zUE(#41fyq|r2`hKRkle;-3FMd6jQHU3fh}pC^OmnVWzMr1^EFPF>R+zv;chJ)~TC?!8ASqTE+=qg!ZB{M)EYdHC>2~D|uQihq|Nu~O3EfW%2QsR=81K6Z! zez0^EXdo0F!Hw?(v=Tg0>jfzTKmh6oNlFa1ZqkIv&g~Mu_^(+3GnnXpP+Qx%H)(oU ztn2LYobj(3`;N-1c`qclV%9sZJn^Hgx;oQTi>ju0ETk##C~%;eX)`Fc@fc%{R~MfJ z<}bPH8{C98=GCOQI)F*fm1ggp$y!KY=Dps z^`C)|kb&{qK8s$>)U*}X&)H$pzihtolA$!Wk10q#Brxp1Xo{CVll@#jir4!h+T(5q z!E)Kn+-q>;PE5*wM{=W~RhpeoK5E_!`jT1lw?3+e^X(3iy7(sWH-g$XG>Ym{U)#P8 zrE)#C$y0DqX=*Op#KBPhM!Hp`fjLg4bLt>8a2Y>+K2L zK$*}J7E>KnTHU6!N50mo^C~+ZJX|>P($)U}GqvKibr}7MqKT`M6_p8fAuPE_;33qg z^-OWonCL*>H&IUNlET6<$knTmu z7u+)r6$_X<_5fBqHe-pC*QzTkh&{lRk*)md`hR;5goYCdU*=Qgbf zxjK-fuN!8n#&9cbGcBR_%g#Esm8wd60~0ngIkD)fSC-+^RWf{ulGR_Q%fiK5=uRjBV9#%|cg zZADp8YgET-{%LU?jFa+0ux43ViiY>vmuoJoNKC*g`pe-5!tYXn zxMd?XQeWHNUx{H<*A?AtOSxq(ycdsG`lT#WO|pgWK>WncddH!AQ7hc-qx7!<{l?h7 zNvJgn>Nx)ZkUi1gy_{xt3qKgMdEvI&ta`sZN}6c0fZRhYbX z`oYiB6o8G#S)nlh0Cb;?maMXjrKf8$T^qB-{l_pj`a zg)Sv>3<`rx4?x&brJ?vnNZ9G0x%^kQ@b^CV53+xoLZ&`9YKHB}_F4<0i8iXIhwF(x zUrd>q#;d_0{{T==RpuYLOstUMsKQ5`72IT&KW#X#A1LAem2O2+(tSr3C$NM7W>ug9 zEm$deEV&=g8zLTFOTc{Q`;NOF##lN@E{W8d>E@~S#h&raC%Qa*-_a<>)UEfiMV0-BbdrAtbj zRU#0CdVUf>8b&G1VQ6U9uJc~h?76yYNRSW|A*X#totH{VN!DF;iUlgY4(UBATVT}z zSS@H&wLzKh(l2(p+x~@;Wk?rar9f=Ty$xl~fTXO4rUd%QQZb(03QbjhwUhlaoxE9+ zt&CWDPe}`JO=sFsX}5;4RZ`5POrw2O*sqmbe{ec0p`N`U3rY#dRx82{s4Hc{{-!dg z9TS;PLUh#6yjbj?vPDDWU|Cvv8_ z9eW5@Am(u5qYsmzDzk#%6Xs?yL^T03JgIKXFnpUJz;c=;Tk@=OsZ_UXX*;&Mg-S_R zLJv?!Sh7QvZPk!_2h6IF@$aL*bZ_dzLY?ey9>^*!6l!{zUS`syiJAUij8ty0*G}~Y zm3WJu;$VNa=~^5~^DYCoLH-q>jt)}&V#H)W< zXQ*<8)fp8akVpo4z!2Td3#!z2B;mO1ynAM7ay7qlPEnKWPDgNEXX%XC^$Awlh$8Ee z+{qrp(NuW<0A?{5>w_#z8;^=tVm#M+#_3T*LbQfbCA67z(sD5!7a@C1D5mn=rnb2| ziVv1@joUB{3g=P-M1+Kyh#vDX?gV!m?bLP$(t}v5cA>RTK>@z(0Il)iOU)F9!?QY? z2ir93^xQ~HrJ$JkL{-*rW{OhNfwD(bg!(5ut-5*wsqOOACvp5D6yF&iRjTp4BW+ng z61j_rTL@58r_=~6iiKt;RFB9mGHvOdvK=9Z!ZxFAr`14iZ^%7ARBxYMvpwT2&f9`b zR;kCX2x@~j3*PsfR_PFbf7U5xGEYTl_tN8!E}N@RH*Vao?u#ZW4OL$^atP|DRxxo3 zpk`{Ez?Z^WMXoz=2CFlk>=&ggS2Dw)z@sohPO!Ob_X;6R?%x#S*?CF3Z91E&8mIEA zWX%5nWK{0>c#+is5g#Nh6e+&s4oVlAcNl7ENjfC=pVln6JPEfd11XjFf{qoHJKb46 zCPrQ2)FY~`J>+#r3;V}n`pdcaL(;^T1jKbl_!H^%TJ!3aOX)w1HS84K%!H{!Nl>5D zB+gz@taI7`AlW?;EqzppGy1{=DQ03)YNo7h{Mwk{W~h*UDYwZVn+_5>{?Q!v=!Ew? z(*;v}U8=dqTU?~C4-H}iBVgH^LZ7$1EOK8_bCPZJsbOkSDH7}42+uA%e%HRL!rn$& z+9rEB0MHpZ`2nYQRr3F{9?++XqaoYTHbJg z1SKV=r6(&DxF6XKy|qO`4~lY{E0HA@al>!s`7NJLs3@5gdMf__&Fq;~HA~7} z)$XQEsGXy?j6q$(@#3an5$Qmfde*WvRqS@+vH{SNuEryMm!73fM-xYabhz@4^u zAy-zd+b1s4`9x@q)l(u+lQ|;y+g+BD2Y=xXZP9n$s)bkA3wQN^KQTYP1#^8##ydm~ z_I-96Xl)qx6xtbcXGF2!ObQWk6H5(r!ttv8I+5gvd{+jGvQS zQmbU)v6DcPxZWh~%B_ZIX$@^vLed$fLT06@QcQ==Fiu_0Ebz9jxVUv17o~W23AxMM zHV8()+^)yk*V;pwc}~eqmR(kiTG?2Tx;7tFj@1c?ff!CJn{zHs^6Ye8+&&I>i1S+B zQyas$%eG!T%P)u?3g>vojeW@bCgbXh9kDbS-akfn+o)O!MN3oEiJvNfk4N_Jk>;`V zw97CW^l)B%=lFb1VeYxaxy*1C+DGnH`R^ayH#7T9Tx_)Rv!c6I8cq|L&!KbLqloN0|AD(dpBSAF*0_DP9bvnqAlt-_YkL{jZ* zyLDJ^P$%?+@{Hqgwk3?}RDT!Y&B|nV$?~;y!$W4aTd0}0Qc*V5BPa+%AqkeyB_3*? z_E{giomZ%>@mBFiuryIXzjk&TM-^Bqc{+>=8&VtzxwS7NYjEhH;177%yM4&a=h>9& z;Jn@5E*$G&^)L-QRUdN~za|4N+p#B2#;Agn`b7R>4{;jFmMG7f^T=h);0;27xep-o zk1W3?kyA}UQc3b}1cGG|cR4~u0++Mm^*f$}Q}ET@4W-M|3tdw6D|8Z~ejB0|XBQr- z*#(m#8U&=aag7DB1=_NNZS!gbDUi`KEUrBlw>*;*#hFH2_)`Sw?}@BCegaSnMJXT625<0QD<-RO)lo13)^? ze*j_{;Fl|Xg9K_kJ8W@^)NM?YGcO%XG7gGqkEv20j~G*LQx$YdlP{e95dFP$QCDu& zS?3ij{*9E&w_n018>m&+Nve8lOsO3I0CiCGWkyO>Q~Rr0)V;)lsh*u8A0!oz866dW zDcz+ybUrZ}^=V^jpDO}Vt4%Q@w@5joMHw`nsw1i)I)s2fi~C`n-V3k!Q|Dk}blS{~ zKM0X*KANiC$IP>>Xaz2Mf!-46suEy2q^)oSE2g8YH#h)M7YRKmtY6z!R=mTH4YsM| zyK%=>X`dREdR<#l`bC!*{{V72c!i&4Aogdkfldo`tF>H86n3_fclD7Ig*~Jc$IR4` zK=>UJ?Ke7D^+4utShI^plx9Faw2&{Pu zroGu7Nli@+T2V2%D%5MJF?C~`b8k`AWLLKD#@`T6roDU=a^m}?%MQx#d}n`fxYZ3q z)T}6-2d1JEq45%kn>YH43bI~&fi9(qdThgooRlKk3yB2IRL(Lus=qDRxybXXrq9yI%+LfoX> zodmv+_QJSH=&`1@Rn}BDNl8qqnwJckWdNyaC%_G1QQOpYLQ6PjRndLA?&=yyZOH?m zikXguP@uY~TWc<^tPr<10S?90IVAcfuPCevr*w^E4F3Qac#06AFQ(9_8_dt3)6^26 zouNlqows(1lXCj1f8|xB>*A7??%Og%^w+#QD4+^s9^*vLzLd!R*!i7Qq)#Zh#3Eo% zw~R7Ma6FU~(nN7Tyste3c)<(c^0cLd>orTmNcnUC96;m0$X3w82#^wvwSgnuHAPP{ zH*F)S)A57{3eg_N&{bORvaBJemuphl1S@Kksdp#k8pjK>8*02e-%R0>=(fHqq1+DG z-4(KoAscp}0A&|RjYSq2ZfmM_7(FIB$^au&@j~1Q*r|;n!PPNxJqjdyfu?2Le&7m0 z@+^-1j7YausrQNf{klo0LyIt zFxNAdTCOCma;HbE^V_>SNYYKy=F+%g>Ia4a6`80QMhzW zjN?Ku)B?PDkh*gmTAzD+EjUplxobL)2vxKSt$%!`w+LWN-dv(nj0MP~%2J-9K9z(D zsGN0Ym08{q!&D)bois?)4cZUxh-q)jR;6Rc1II-8QgKzarji8^0$TY;hr~iYwf<^( zyivP)EmfRPZ*&xjM3MYcNcng|;xk5&19X|*$m!y?a zyyDbJ3(o@CYN_6B7WUFgbn<@qE*sNY6r==i2c8&xtBq@sS} zAtVZm$L^%H$+fldTsWxILVNb@3>Ot894t;J9Aplq)PmlDU1Dz%^Ho#j z7EMyl(U4V!Qm;-*BS1&OEileX%jU5glQGRT^_MyYQ?8VaK?YMFNUm}sOBRd5nAqe@ zo;hcAu;2WufHIjWKM1D53%U@r`^%=+s$98mbONU7RzgO)N5&K@Zdyv;k5!smqd{#H z3CvGVgfhZGM?Y(uqM4Y6M2dPgkEziMr0 zF5ARTiWX+MQ}K8x#Ek;Nc^N+OQ>!v6PCrzh{+gse?!@)^{IaTe{{ZLyl~b90sJ)d| zK4t)LnSEm*zG>dKyl?V?lH4X(wpG%)rECF0TFjzj#PO2*=%Id8*7RxDrqqLN- zqD1G^d1A#J`YLSE?sAHJ>3L?LI;1QuWXnl+fv#WTq^zT$)GI~%c%%OSjEY61&07wV zi92)YP!~hiQ(5;hQrN~S&rKvx)++p(Ps&zq-VgP)T=&o&?`{HfQT!(u%%jx3M$eQZ z3-Q}b2X4`66_f9bbr3t5l^li(-ufbX!>tJ@OL-$qC1cbELsd=?FNIS_7heQAmn30x3Q(VIb%L98 z3LZ>Lk)wC2bxKRn&q>4IQYeKOmma9Ps`ix0DR)lV>jN90jEfLys?|w)Q_4u+sO=nO z%~Llx&=S&>eEcF>+p3Jb=$19Es4aST5hB-7Lx(LJ z8-0+Mf>Z$qX#mKLVfrD-SfrpRgo#h8qoH?ll9=ziUT4PryXHRCXfHI+aOQ7Z-Nxr^ zd5zUn^(cK63KZ||8xZ1<3er+%+Ea-rvd?5&6*BvVTFZVe#YBVkBsUzor*6vJqpDTq z?Ky}d(bbR?jMHcUU4H$OsuaI&e)C&aia6r5?J-X5SNZV&096m-wLZ7~?lidj4Q{;`r*lur65{WTf2Pwp}nu;sC<%;WD z{*6jP%A}#x0|Mf2L(Yrrt*B#YMygrfTy)!ytgljO0c4qzAs0xTPgPM7`m|1ux2ktv zv2!C-IW2Y{acdlX%T`BiaIslR@?Jm-Lx~1oQ3`j#d-Laq^E-MN?g0&Ah8z>(1UTc3gpK zQk3hJHX0PDl4L~L4gTnLoU-U2Py~JmS8xkAA4zciD2}t2E{m7zaC~m82?>6M#`hU( z{{RhU4re3|>cRSu?%=G9`eb+OURpVt&tbbzUw`P{J6UqIJk3qH@A-u6s9|Bo*WaR& z450g7RLGxSXdp6t9rH79yx%(k1tUug^Y0;Ccn zkY}Wf)H&7sQwyje?8AQ}Qz`SAk zs;U_t+S*J10MiNMviqscm|~&Y4|0}dTmoAOpuA(11vMc@%!0L}U{LA~kz%#(O!5%T zDyw&0p|XyzaCJTgh!-_is`;K=FAi5TcdNNEMD1Q8(4YSRwu*nUF^=+|_3#h*SEjoa9V{m&#oyc) zFSHJYSx~;^AOr9c5eCZ8DO<8Pr%KA|(~i*qsSh-heq9ihnRl49s6UYn6)AEgZ6ZpsahpSR0J58J^Yw zSsXN1!^9r)y}PwQtA0<4(p_quZMIckP^7lv5S52C4v7(@jCymD+1urA8*y!SvYkOI z^Ov{yyss9;&1HFy_lxUCcXKr`I<}|vXew9+(Y3n+*Mrj z3(7|(au_B<`rXf88P%-BWtZ|op38qUsIOGwP2t3o(qw-4I?U>zFWXij>$yK|DX64# z(^?2xQ`nK?8jlms$!jRv;=K9XZW|s#GUL=M&*1+6x;>K*_GI6-)L?6>s&?ISx2GcY z&>L6v1-R;*S~l(~Npf0vb&I{nXsoX}3o|d9X{%1(!k-`RSXNWK8H-LOCmd`H_RitH zz78bnDe?16*S9}?_m6KFk0aq$yhT|BCnnMUj;gS@q<)oUwXhtJN!{gC<)mG?-wm?M zEZo?nEcOlVIQ^cM3bQ*Kc`r&Xo+;*_Za1XaNh@d^d+$++ak`0Fnx#yeg&Z zbsfUk%C`$^ueKGhn`5d#C-9F);~M8$wE2bP%=?)Y;b`+rtsP6Q@=Jwkk@IQwnStzK zt;teX?*9NtJds{kaa&mv#1kPsqxM9!j6Uj2EFVw!r!#~-%JMh29QK2d@E2Nc@kE8R zQ1U%SoMq7^A!(SHFwRpA1>-|z6#*j?dvjp&`*TMinKwJ#xnq`VGyK&^e)E#tTd2FZ zgzQI9B$3nr1YPiu!ob+tJt1b?vdZDMv(H+{K_ywuovJFUT|G?-LTTPYOH%2!T6B*_ zWAU&Eo9PSji<9ze=VW9qd#kwkl$O6+-Sn20(dr=2`DSvN?*QibeYMkTK(y}Mcb_GQ zkPzbG_@=Jmi#GEsb!?WF+0=9&IL7n6xG@dQ8ZUZw*Y0Wzi`x{2k4z3;-95dhwvd#z z4&`o_c}8)2#Wq)Vi`7`(UM1+8E_FmdU!a$NVkSd&8du`iW8~Ge2 z6{5p_;d{ZI!dm;5W`GTJPsb_Y*8B$LX}4PL4ppwPl{-+kHl0#sAs_~z#b3MpD$^Sy z3<813{{XZ8^JO_^I#=G%zBf@3+N+k$GD;RnT)cY-#$>msUf8sdHPuho6It&$WpzlS zEvDZ}q@nlR3n~PrdQ102?;h;FM=cqf+}Pg=uP^uG@3#}dFNKJ>DyUY{Ey71LIe(R!`=9a1AHxq(jdw@YY{1HX8A)G5YCPKvd;?*0(!f>$sKzI|gh z&5WjMC|I4dxg;O`|JRaPC73hCA!iVE~XO>?44*4`H)H)->OnFtCpS;|ViUSCqC zq5$a*HZ&`wsxE$}oq|1QgJDOhAVj1#?tegnyvRihYWD&7q()C!OuBUz)|DzkMJfmO z2x{)p%<2ke*D-G(^td0=w&3-n^(-`!5>~AY$7o_G$>-8~_q5laBAveI&a-pVDl)LuC3 zfu51VD7XbtElYs+(8Ep@M#3l)-NV-;TWgzfO9xRT9*}jLkhu*;igK$urZ#}lb44a{ zo});c7Xpg7>tj_IGYh>fU3hw1g4A@7?b0Hrxb>-+q5QQvJ-1&yDtdGxl};vFd3qT{ z{yo)dr{p~@PA4y3pA_p_t;=mPBXMl)B3}UnOllKNoC+$@&nw%mseCEDo5=*r=@54@ zsq0ArOq14cUa2cfWk4RAf4U>Ydh}98={a^fR8b|hY}>ST+YUz-)2a~9xhjRiV55HS z^6v$;`Xo8ED<4{OAW!QIIn+wk0IGOvr|p5HAOuStR9wTQ3neO%QS0-DqfSf;bg>7~ zZ>*)R-TcOgP*#I!>_jRjQAbYzcvE`K?g=f~nxL$!nQa>h-L8UKYZ4M&v4c{EhZif( zfisx~XqIvW*msHcp4CNxxVUq-)xLbiY&bI#nI(#OD-M8K+KM(tT{GK$3$Ef3}{b}9Awu!)dO`7 zrAgil)Mx#%6L}#mG*Xsr0*LneUbKLR51aVDbBvdgMB+3HY2p`6X$p3IxN*A%aqdq7 zwPX#U`&*SPeyt0arj(U;2Tr<2gg)W%Y@YJY-g8J_DBa{}z1P|NfrAryaSpP!Jv{mz zi@gkf$!oCMsO`3jX6fiDn@TC#Z6G8iBS}l)9^K=3yGuJJM{WRM0+e1T%j74zxO3$s zxStgk{m%1F5rkOkXs>rRS#p^RT|!jiLgg-i6V$~=l3k0dw!;%koDWgpwB8GcFhuLQ zoEsP(J}a6W``mvV-?7T)5SQQnXx3PUS0L8vNti+&TemV9?{J3&K6Zk=Hpn z3k6zAo_4=!c^*N7)8*Fs_Lhg*53xuD=8e0xQ20P?=3q~P#eXB*Iu#u;`zr6b zLNOaq6_t(D{+^^aB><$EEAse8fceC5hn*^_?6?z4v4*PKsq@@Z+J*;gE&6}f-EAet z^xUOwnx`4X{w>)@IK0uJT&(#EF>YubP}F_aeX-kYEmdW?7{{-vMCvVWk>*eadr0au zk3Z(wb#HL!;)U<0kY1VN!ov35vdM7B!Ru11o83p;$B%u*VYkjO!YW0=)qP27q14N2 zQiV(^elzJD;F!Gh`5`Qkn4@n>sSfAmIE9V@@kcP8clmS*t1Xz8IbuyS^(YHUBvhqp zOuEiJLnN*%^WG?0Iw@&}b9WPC7J*G!Oxq5$pD5T2$P$-mkCn=H-d63RkG!ewXBw}n zc~*2KETUmMM5iy5GaR%+&eHd3(LsFs+@;;`S&BuPbOLsngD^x5b$V%{CeIc@)kKVw zk+M92#{sEz;N<+He20F~9VPS6im=6H8&h3Td%IIQLa9^?^jVzrGw%Z#UsWJ2+eE3c zWiHjB#I1z_fi-pn&*cV1EmRHUR8?{BL!5ap8pzX+S>FDiZmk6&^o4FZ^wugIhbc5} z+SR6T-aTtGH$TBD{gH8YLz&&tlw>u}To&mHNpO+`%;qNt@%W^3H#@2ikUA#KwSs^v ze0s_(=|cc*4FRWs5oK_oRi7H7?@)ChgA4{xr)21uK62cwR<>G_l>;vcKv%$R)k zcvVxzmi(mhGxZjmTa`_Eq$yw-OMh9#BGS?3YJjvHj#?2A3h8l6o=&VQ^p&)WVrimrPjFEu!XxE6sR)`J+JwpD`8`WjP5Npe4uBH` z#Vsg2K_Ku{>EYaUP1-U_r2>RF zppM(VF(TQG5H7H2HAnV(haPuw)ieGW5!H67FK7t2GpS7tFn@fA)Fyh)A+qnFR^B$v z$55NgJ>|>Qbv-f;@Ce*B(j#rERkrRhz?FFwv-GahEC7S7G0 zQ~ThE6n*7Nvd$@(qhe~FgIJY}nkwX-Q_pc&6i$MBEB1(TH=3bjcbckybtMI5eZq+& z!gGksfYDg7Z%_aL4_^p*$WY)CR;KJ+^7(=A zlm#>X2-su_Q2R8>)ocRXQfJG;uWfvUu_%?w(EAz`EKb}HeZ&C7OhA?ZKaxSu8=ybSxziG zZy)F`DzD5hn&&lx!8AVWG4JwsxlFXvB(qOmx_a7DG#|`J22%K@E0=8cZqLP9G3VW} z1IZob^o`^=jjti51NAJS9;}9`Jvxt!87a^?3v0B3(xrYYR(S)4ZP%5bQtgfZ0LoXO znX79;Nl~AOi8!U*7*LbPwpt`5DmIOBm^Pzh6TeLozp~<_c87RjCgfUac8#sdo{P36sn&w<#*7BYHA-IRicQI zBqcpJei0FIl5>ul1utiUkE^v-^J}}xowX%lL1RM>7Sya6vVb&MRNe>*8@2hzeWH=? zCfg^yAq_gBxe00~A2K?q%!Sx+@}jQJe9PKunx!dt1$lwj`yyaO164j5x4-nNqRQ`r zzUff*{nI8#;T>^TYgm0L5ba_LPnvV;A+qHl*iaKtl6pc#3RsO|UP*-o@M6q7f;G^d zu;F;BZ`&(g;n>J{Lp5EZ+^15RcbpQq8c;PXJ6BAO%325FWemTndD=uz!CtSji)&4{ zY>*lT!5U0xBXr^rfeQTO?$ybDK-bM(u0BAeWdP1VN?$2NwaC>{Y-RMN)iM-OuAUQy z#Sty(s`9bVO|>aQSs4gJMO=iwn`;y-_YZjVY?Yxz8KGae#I%pw8md^Z%Wyvx%?G$D z#8qV#N=XFnJ^ujOjGHknURUV9X>E%vhv1{Sw*LSZ(jbc0!Va=0sGp2CM4$*-bOv&T zZBG`h;bL7ijs(WjRqJFPoqNXts^jW}2^}T!f@7#mm9+^*v+3Ftn{g<60W(L3j5`_@ zQc==bN}Cht2%IqXDAOSYUVpMm(@kQso$?-=a@Vertns(f+%L~g?X>>@R((9x&{*%` zR21n^=#eAb#H6j&RPE#?y05Pnq<)?CZUaO6p%J{*1$adjUVFF58*bNQXEO&nia+2*2Y_*WCV3edkqYtlX5*v(^$5RqKU<7-h~-#A1Dk} zSj7;}=`1ZKZI6JE{>X8K4RF9v7pzPcA z=G;)Kl>30P+FOCj_Y&ZHP{f z7_`%_rE>lx#Qq;pshqY#6xPS-sKqO4bs&)-M~u70!GW|{Jky=}AemLN(O!f!q@#H~ zq)4B_Au@~vD^DM|dtA3zu&Yr^XblM_UnBTL6B^L1ue)*aP71I)Iw@9DOqDJ~g9(xl zx-N=fGi+v#+@ny-L9&8%p0O7^19erCA8_XiBvl?(?HUU~()4;(C~NIOvlk;TSP&J5G{<{iFez2o!q+)u9mn(h$Oq zT3>H-YV!2>!>?^q>@-bIUrtur#3w}!KM0JwjMJjAVEk)d48~`HWon|YILQ+9P1oHUj@SN3xH^XQX$mJus`Bni z!5*brICm3&)q>T{wZ9xwNgi1MbnO|7GqIKEH{j$R2$w0MEK{hGNdU}_(Q@NA&2z$_ za=dwLW~mQtpt}OhWEN$g}1ozszc)Z4sLfrM4E*m1I$GAm(nBc z`*{7-sU`9kPiP(R5DN|Fc@`b5lqw}^@tYF0wuxQp$? zv4FHF{{YW_Z(N^-F}v18?qJ1hHbg0P4Z2c-u%5%f#7=X!V$Ln$^;cd2-{)oTMYH1C zeARebojNFqD49+u7Ve{$qpsR0ohlberI?qLNb9UjLhThQ;goW8a20Msya6NU6BbUW zP;C}ko49*>_nK4CW9!}j0OIb%A3Q&w3e@a=<>qtFN4i{S6Y8~e zc@BERLpk+7Fkmh_jxoDbHhOC6LVxbX=dkZh7T1*l8KF`93w;d#(LpX;AQtsOb~`08vrfAh5e_B$`L5%7NUKqiH{gg+-FJ zl+llA{V2Jx_mB#{HxI-lG8s$eUa$wEfil`h$`1DjRSfb$p+XZvl0Po~x|k>31rI0` zKo@U9fl3h~4xq!82sXXTLZ}@cVt^&lN4PQY2$#r$s+GlcM301{>Lj*GB~CX(I{HS` z(o~n-X$nCT1QmfJ2jf^w!|_1oC#Ac;1vYsiiRw(Y<3?CjJ9OfizfDQnPz5>&5;~0x zdJft;0s0mWC;Y$Cx?1}}g3sFz?gWr#5XH;_d{vH^!u!g9_XvLiy*b#^{{Xg4`2PTc z<<^vXR%1P6fv3_Uqiq@y*W?$cQqb@J02>z46Vxb!;}fy`ik)!Z%}th4q58Baj---) zu+G$61z~Tg^ebtXKg+*|@k#IT{n`GS5 z%|I!4f!!RSzY2h|!a@lP+(d+dpW-6HX?GxX30eMg9Lbm{(3);gz9!{A0*B=T00Gs?4o!Wy- z>A(1Y#Z?zNRH=?r{ve5~AbvX?)Lsx64RQ!18{$ZK0+bqz8t z>!@?lF3gFmxbGb2*Ez{^HeX3o2>Geg^sLJk&M#$wzy6`LT$cJP)cy$T+6DgrhOq7e z=gJJ#iaN}``%_TPDyo*6TdJo^DM~#xC=#7PI!4dPxb9kfoxHHPh?(;WuNQId;9)H> z^IP3Aw-N!c)1^TD#;Q(pHL{TXy>Tj<22n>!mWz%h0BulnahUvLAG>{Xvk!{>5aE!4 z#BKEc5V@|oR5e9&Lft!((t#ta=BbH@x!KLOR+Wx-4ihb-fL8(S*PD3H9OCNBefrj? zE1#!PYN%5eqD z8ZFlbX+<9HnR-^X>Ci&dwE#@#w9ZjTeBihRdl8nma+6lesHea^MNI8z%@ghs8b&%0 zZNV!Z%-s`tK%ag9`^|Fy0JlGG7oTf**A`nZG?KfwjpuIGzKWV7c6G-Wx|lXny@kS4 z=J|-oa=odxy~zDQaR$9S7Tur^I1tf33x59qK<%;Gq&yHE z5>ocdsrW?~?l$5XE?8svQohC6ZftG@4hPj;6Jt$VwJoP~&;XQA54M#Qxoq#(Gu)ykxtJ>(bYM8b7ilju4uf10*r@;UyMy- zttg>#S!O&PROzr4GfLP}w2&jfX%;A>s-jOmsK&Oe2FME3P6~=DDA^>cL{`-m6>L|| z3u^+8l46UHR`bkKGa~g%h(lBV020&y{qZr5#~J5rW2q{$%XwT_d}Nn$3br zkFX3r_lZ$t8BH21YO7mHsvdm>z)2JGq?rNqjhWe8ej1T7Fz5KM%1%R*<=oN zZb{nF<=~nga`q#MSYLMGXR{<*(0`omC{iRmRFwYs(fI7RY`6`Tfv+phx!!5axP+yx zxO?LB@=@;D?emZA&8N_&T51v}>X#Hw?;T?G{{U>T%@h%;T)%VN#L2i^zxAW~j16=Y zOG*>!QXoky+GYSAF-qhepU2A?_Ps7!A8)?)ITfAT1ldQ6BvImGJ* z$n;QA#3hqeWPF$HuaE9{+sIY9koMI}%AXIE0TtV~(4%f~afVbXQsr-IRup9mr=q7q zQnj5D4xVX5+%{q2rBi3fJ~Kdc59ezNV2KOAo2?(nj*%)Z$SNRQ=vmd$CQofQlD1k9A-4&Ox zHCp47aH=k4@@G9E8ww}fL24ZZQVQz;Ie|RFP-GddUIc3&HMC9>Pl`5r_x(1MtACnW z>(V;oK$i=5IVbGdpsc$$PV4)mv)@ThQAW#Lyc$(%JbubD+R;hNT3UIQ3Qo{K?*(A4 zTk<#vs3p#``;8K?fhT79;0ze%Ea(gi5qAhOAN<-(H8d}ua zarLPvl86It>Ca!3WW3k07JR1JvC1e| zY7dgv_^*8KIP2V5>TAUaU8L~OxSP&JgHy1SpIwHMl+`HjGAZlyi|-QcdB)qe-R~}F z^HiQo+{mwGGepgmRiW>0%edbC#d-Ic zbNsc$n;j#z4+TYhjqPch&B42~4?9g#!-3YG3n@}mg#{$(@{7sFM*jc`Dx7)ghf`HB zIJ@1Sy{uk}n~klv8VjB_-8i?=yt;L+u0xEnI$t9xf+yH!alvVi_ao4v(2tH7)ykm^J6=Uzt zYWM1(XEpLQ$C&cV<#juX{+mozC@HrPfRxW{<)q?%D^^Cpz^d1mGDzo0RZ8(cvVKte zhx?f1o;Sp^Dn327afKsQIW7LNw`x)iQ>$&3+F_-(N|akOB*;Op0T}+}7zePW> zMcj`5^J-e&s39kIs!;^~F&Q(%MyTspypu1HawW2jmlEw_M&HbLfe_a>VNTmvmv|`W zU&;0xh2&Byy&IlflAY-@GnSDa`NevvGivtuDMf`@EfsXs@1tUpwCKGx_`&gzP!a+X zlKF|_^4zqc4iC&G3@y#kaycA}ahzuzL(U~TH>l_aqGPx1gurw{&l#$y&$)e`$4Ogb zRU1G))7z9nWk0EkuO^RBwB8dT3{U?6+rsNgta+xAfVCk5w{g}ioNd*Lg;x0xr0T*l z$Gu900GSgh^zef&8fv28Iwtk+%?zSqHBBEXvFli>60i$P1SlVrp^4ZDp;8db?W{e& z;<#lAqT_y=OD(BVqye1F$mRY@Kk(?ui>P973sHQawsnQrO;70aA1`pLm(}oC>eI zBHE=rrioPVq^wLP11upRNE~!cw<%pcIWmQAoPtmyvzMFIMWb=3D9WK6L5)3dsTFE$ zl#OTb5z7W6@>e5u67!LpYMQ#8kaih#A1Hp{08^G7DvdA)3ZJDJ8g*)P(8_;|OEuM1xeuv5Hxz|XsXilEC@>)8B&w+QPzZ6~fmiIDMPKw&_+hP7 z4OG1#MwFw5?W`IUJJ#_*W8u3Okc2(ZMO?G&dpnr zm_N83i}c*BkKKJgHOA7N%aH2JyL)`%k;$T@;DvqSQtr1Prn`+2tHv!|Wpo!3>oaU0 z8GK`w?kiwSTbXtT(! zxDOoNOX&oY;xIVT0u%yM3fzReGfqc z9x9YI{F7*DwUcmc85t<7LtjrKT-kVl>Z5-rbDgf;Li>%@p<7U?fT7kVE$s-@s+I0Y z%4pqHMsqJZBKuiV#WiA79a4jbaMor6yqE((RmY5Rshl@F6&T;h8CfeXq!ZW80=i&U zfZS8S=!=}bv#9k^ujTVYW>AR!l>Y!FfWEMzrF!TCzOYMW3%}A{2SvRXYI8sNk@B1+ zt_1Z>J0AZ4C7<n2|~=rr+M{+{n8fW0)I{M4EJ zL^W~QQ80F?%7nS;T!MK!=*tbsv+^a3O z2>|I=$tD9i0Imm*%2o-=?bDH2A4qgvHE`f&SsbF>+2j8Jgu{a%7k{Bmb1yKo-3 z1LR>NBGi+Dnw=1q@@rO;dtVtC<8mzlH2fU?OLB5+UCb?GCdUpSswj%62(JLSS=g7C;kRdaZ^BeJ>*b$*~Ms0omv zA*5a>Eh3?L)uHXsta6@Fr=_HHnvtmJ6gtXq!(;#@8#g^wx48a8Mb?7ipIcQWS_3(0 z75u&BT2`ssOas2HuBVEjJjG~xD!Dv*slzpPF_GL*$Vp$Pym_u|i$}o*qDu$m3y3y} zcP@G`a+aB2!U6Dw7|?8;;U%sKVTyz;Kba&*L|xlMTqvil3!cIXlpH@<>KbIuQji+x zbrEdBNl+FW+)HXzSmq8;hL~RM%TCabo{@aEhf1#|=EkL5FSWbFG=HK?&IEp0AE5r| zV*Qoqqd4{d0JSv#PMG zk)p6$@-J6Zw56*J@Bf1+@ha#xAyT?Tc0CT&2&wkQDGMk?vx`KUIFXRV4;a zxVcCUyQkDff0VKhl&@@XY}}Dqv?$wCg)e)EAd)*uQ5x)W(qZH)Ll`2@Mz>GAeGVmk zCn~3k4_Se@&^v(Bw6%|7z2C(CBfc0E= zE82~j;$rv;DB6Rl)4$Rl9CD~BIq?RGMQO$y$0|ZXcBB#FBzi$HSo=<*4t&VEZAy5k z?++oAr4Aj$0TRX9XsfS;7bI3if3KuJ1p|Ot!(VxNl=;7q~YUoe3WSRRXJ9CPiJ3QCT+(Os6)lBtNKHAkaF=!ekg@T^@ zlh-wA#%y&L%J+}*>8aXVi$8<`o8_4NTs!Vw7(OKtg5mf1dA3_Hxcn2zU+vf2y{g!& z%ltWa7PO7U<4n2jl==ZU%VIGmG+gyOYYWzX9ODvl#4h>)1!e+YT%D%xeyRa#XjWG%UrUnp;o!=2B7}{N~27{ zl1EPe065SvPpd-3IXh`NQ=d6r%LQh#nh)-aCHL~mr&@p11yGrl!Q|wZIWJFC#u=1( z4_=bRGO&dB@QZBM-RaFtjEX>+>_~(MQaYjEALdb8#uJa*4nCEyX!3$g$6*=PVmZ?`{bH1o2nv++@ro`^{%0Pl>t~OJt*W}OU>Eu;b|XmGP$fI`+$KzD zoME#*3aSQr=G~VR=|qP6lVz6L{08uakfkZ3^Oy!H{lc@Iu~)%c`*nS6`>HNu+`3Z$ zw1ouip3~#6&NG~A-?eU;D5Kn)yvE>%DV^A%Ga9c9E-n>W2R4X%WhSwTiH)<+=_4(4 zk49qvpk80hvXxS=j&7{7>r9fA>`5JZY2g~@5^Wq8i+js#_dnpJu03&OG)ma|LEEZ< z66rYGZADnTnUlA7xzRvwdbub3%A@MdGggz+NYAs z-P$0ax%Dg8B*d56(1mi`Q<&c9ctZC5lKSPMr}U2$RWjj8M&!zKPk6RE-KGNX8ZR|s zr#AU-Y>g{L&Sx_74=d#vE%zVEbK8~b)Xht&6pvIWw4uD}M|krOBgx#lV#hertMs>r zaa?pZ%$bq2JdUZTx6LX~no>HC7=X7L>a7<7p*hqJ%8>a${{XTkqkf97I|*`hc>U6* zRU@D?-SC`CV}Yu!o%h*LjG1=gzGdJy?M7GRv38R=F-pNi+Bv z$^*sx>$%qY6^Y&I8&#>jy6;L#N5I9&tQ1-FS`w$n+XUDf$T_mNCT5lUBNjS8*P^v8 zxlWdaeAZMQnCTRb?^{VP8l92XaUKgr?5y!e${~_UZ3o3$XSl9X_Gw|Ow=rwGVy)tA zvpbbr3{pu701({LooB4$@!KKU&Q*VX{+Hbi@zk%`emCAOCyTHyy?kZf15fZ(Del9V zJ*V>yA$Q|`9H7VS_0EJEjN+!5rd*O_qxyFwk_kF%9^349+eTu};kP$;4*vk*Cac3= zaKCqW8=FU4SlhM4{{RpHRh_%rUK32?uT}DWinYZiewr)N49{b!);+r)3xH4;h550O z?semwT-c2gko5I6Mg01E5s~Kri zJCg2KtcQ>?E4}WpR$Z&Af~y=TOJHni2uRw2Ata=9jpD?UO8G3d@WjS|7f1g9X;NGJ zVtvF4jLKQL8FZBK)kmNE#6R~ae+BAZ#A}S3@&4esb<qIv`g{{Z3mz-}IZoV|5i zQ>Sc67=@vGzd8vg$6aDd4O**H5Fb?5WV?+^Q(mehkuQ#r)TE~^hNv~RmCJ?F!)tBu z%C42vbcOj$HY%!7PrQ!%cg|5^<90%Omav@)t8;6&TH~ace+a6%{Ojza&e)B?tIjFO zHayQ0t)ph5-&W#Wqpw@5NLrXvDO-t{pOtfw@DX{$OKT<3gl=sODtc(JOr~<`9ADNy zYlo2Pk~TlJS20)d9q*bdGwV%~*J`D%eApambDc(o7m&D$ z2+N*m64twfd6uapwa3>fOUyq^uHtixWtFs}!Dm@4-I)~*2`6mg-*fmvE^EcH`~9|# z;(zrjMiTQ)z6X>C5)=Y}pK%x5ZQRiSvJOd%!`~Xr(C@_=Vz__0s_VrtV;j9Z+|%n( zhTKfb^yp>K_(h7|{^-aJO%*RI#(4MBkQm+G8fv?KlAN3z|E-A={NOP}OdYy$CW_O=CpG2EJy6h4(Td z%Lh<&R-x^)kT~la!Kd?&oIYn& z8#anGAJmdCv{0`?aj@q|6@Ln)Ci>HUzy55(LHxpVjJ7w{a2B(D5f=Xd zp-!J%{{Yix!aA0Gy*2X2-4nLhuR7AXDjyXZEL>H7x=}A74}>s;>0uZ4Ze6l>}`hh}3f2U}PtbNB;l<{Ko$PcS(qK&AeiwYKr-IB*QK! zvPtgtHIOG?_$iIIq$ql|F5!%_l}_9u7Z|RkX~x?NpP3~kdH^9n-oI#L%3Ob#UfSPCWo1LE7wS)Ne&6u<-Z=~w z_ZtWUQ}`ucxgT;@4s@-=lTCwEVl86+s+KApON(_+Bqypgl!QR{4?HhpYZ}r3HP_;_ z{{Xc<(6fleC^HEQ=rs5yhKBzDSOO%SH1i!N;~9MywQ9rBOMh(qn{LK#7h8Tp_vq-Z zZl&5f(tL?nNI^=z23=#&-L2-fk=uPd&J82YeoFo9_75|EkvR(}jID6g4MbE{S(FTbFO7s0BzW zNdZYBVk6{~xKUPL${g1c28y!1!|}D}k7}0X`H{6#o3b+N;$jx!0^L;hGn9$O)e))d zEe`sDOH7H>OlTz$u{Tv>W!a%6DXfv~}uEgnNKK@;f`?1G2^kYkN2s&(mZEz=@yn4Vn0$_<2CK+;4lh1J{nobPuBo6=>XY499o($-lr3EYth>0=E>lW~ z66@9=`8Of4v~RfARMC15g?+7NZ|?&n8wEmQGF-NoF0O9Am0+e@M23YULHnb@yz4YC zFP!nwe%y9zggXa_WwAA&2!RSR271LIYycqlB!{Wg2?AXN;Bs_IkQ$`6Ilz3&%`V+P z=>Gs0-uW!qf&Q?&MFZ7s($AoAXd6L2gywuAyLZX0ZRFVVLZ&P@&E>-jqZ%lsoMKrn z8GWwDa?1r?Gk>=@q}N&NSazDFl>?|-ZzUu|)rLw7rq(l1qA#)67OvYUp-3l8l|Pg_ zAkhrbR2{{EF0+M}id8t+(M?4`1FQx@l8>THR8%aL5Owi}H;|Ut>JsaZD;+mRkc1T} zL=B_HAGX!iOkMh0sNdEwB)l%P(z3ARev2^s` z%~29aB!M+}L!Us_p`^>X+9q?3bK8z*Syho_uX=5vXV7S#fPCUo*iob?r^MGvDy;|G z7rAd|*34@mW0Uf$RlfUYB{huKKGRA<0)K!kkJ%YtCYtrP9)f7SAHuPC=ULU%s;drm z_f>^*9(S2i@xJPe8Jefsu~#S=KFUmi6Yb!9BJ+p2kaBq)T8UAC?wSEn7G%*!_tqL*(nu%2$^jizoPKD%RV7OrNS=loa}%Jg?Ycs|@X%`c zrlleCo3g1W)=;?;Bux4U9)FcdSvs#wHH#ytr@IGI;aEzhtxV-P#TNDg=Pe%_Kerm3 z_*Qf4Q8rYfR+I^#nG!~kWx-nQZBAy^^R8u3j#7`ME$9x<;&gg3Uze1 z$~DkDdL=-DPhRqd?=0km`qaiRH67Q=gK_42QE|7D+P5T)PgzPs`(d`yiebcpgXVWd zI)lISuT!kd%thZoU7!k5*6ECGTxg`vv|LvGS(jTbf`PNv|Vxgcf8Tsu$Eh_SVO63b?jvWjLkfrQmWUv&JNXnODZR5})! zkU%rkePVkO{ee}v15A&oP@?0IP*os;q?D*=PO&{5)g7Tw0zDMA_DO_eH|+VWvnqL} zto9MEl_KN2i#r-o2o%0oH<#~r)$+f193MON@1%`(bdH(>^q05_zR`8=W0#iS%PDwYe zs_Gj)FFy(J<*XA+%-el5gZ|zr8ezMj@oa=Ky?x|(MS$xp)TG_hupeZja=ma0CNUk#Mn)N+S`^xPQsf>AsdiZt-k>gWzB0; zvs&uu(NYJvpLGq?xceAoD%DCi57rdPBzTCd-`!HG;fAwo$W`Tb8T zDpQKX<|WHQwz6m)5=ooQYZn@N#wwOxLx~`gPO!`6^#uI(o|R@CP0Ck<*{KlaPn%*x zfS-Vi4XdPZl+Y^Ba2F`2dzjS)}W&jY7blFeMpGkI$zP6IPPk?jETTW~-}@l1hX z_$)r(nCsT-5q)z>AnW2f!B|$29aA}*fh5&d1CdYlPLO3ee(0Ecs-xpbnW;+YAUYTV zP>ihtYI$wnOB$?KbcGp@lrb}iYUC~4`YPbW`9~?Y<%Zo$a8fCKLXgjGyQQR}3fl5d zc7Unt%LaROqOyB#O?0lp*5yq~*EYQ4anS9HV_xmGRCmEi6+!a#O71zGZvB6lsdvy5 z;}yA$WLG;-84_@K*BWZIEqQbx1x$Y9682F7s#&Y21omFBX6Y!3(1GO|%m_9{P%71( z!7ovg{t8~GPLrCuOT2_dNY!(-bx2V!E?St#x6VYXSfgJWO0~RD-EN(JJK0-Q+iDxu zy`U@b5op31^;!P_En#4&QqG$eZMp#OrU6)=!=zf95TeSYyCKO5dS$8vyuO~8DhH(E zHsRr|D$#~%k-qNA%X7_7NP-d8kHe`}V#t4S}%`{PS;n?=n zsSRc2T$s|KDt1T%Llco?Tmr4!;bJ8i*YcJ9w!*zlvj%;8Lr==Q6o~LKlA9~PHRM$8 zmeS;=dg@?T=AH_WXBmy&3HSEV$hjVM$@sn^%@dXN*J_s1#XCtVSWe(nqI&`3790*j zOPA1%6)%(FqPAfmu7UZ7kEtzOV&y$*lFi1Egps&vRFw1@c*U0HAR20@xY;WG6;Reu zBh+vo9XdsQ!CO>h_X|#zF=f7m4e2rfeqauoL`}ta0r68dBnqE`yN_p_Wn9M!ak?6# zPy*RZr<6e|+~NX^Y1WPmgaWc_>dQHzi9d2CZ7*_eCu6o&HCxtRvoP(?tF=JlL5>wyDGRpZ5E)V)@kvHT#3h zp5*#&HVeE`St@-+P_Jtm-S#Rsx~zQyJg)c6lhjkGrs)>9C8X3qjz5x*2BBf>C#%&aiausu;kg$u z^%`=&^mNXXxwI7x4`_$G%FOSA(l9-)##rl3$KsjA+-{W)DcXwn7UKGy#v+8TCZwy| zY~Wy{S}3y>w;6x#;>Z2t9CM#etA^em@`d59uF`c&pUlU>$HcF`mMMxfy zA3b8o9W{qrGmnbnv5<~|YjXKN>ANi%KU5PNn;ly=P zwsw%T^j0D4ubJwss%q0$Q?cqb00~r%$>|rK6Pqxdq^}=+%68$C{&NO}bQ8v0{EEF8*(`PqBNp9{-KQW?H~VY+oKb$(fC<{iGUp^>9hTdbZu6F;8& zlVKE91**=WTJF{m{J0J3oU`My&nHT%VfRh$5$l_Ebj;UpTf37f_?XZ0`J(Yay@}aQ z){!8Q?Nx*N$7^aHs;DJvbwtekVvfm4UfgZ7Vqj}}1-VKFQqaSjsj6T$izz4P3TUcL zO3X=Jr{NrHl6o&fXWBZsRlFfR#+~7(nx>12+}myI=KYXVNiK0h8*FttLI^{>=+2!e zNF_;}`X2~W2c-ooi9+fW3sN3a3Z4F%CawNt@9-xezl2_kVf7`Ys(ntTecC7Ybg(^yPJC8ymShLQDF?csO+eflvri)Hf&aOV&s6|ZF zw2&3Mr_vKHToIyPC%5k1s8I8gxe-;gRi3Rz;Xp+|Nn1!LGx6L=@PU_$>~@ zm@t7l$vRTr9Nv+r>D-3=v3{-%w<9lGlhR``lKrBpMV zs6C_wO&;iZrk^c#wW?Zxw#tmaSf5Xa$|Y{xBaVSp_|6^oH(}&yRZe_{{Ut?JNfIG* zke^Lrm3Q;6SKuw(93Xip^I!TzEkZ-Ky}H0sG}T(5-$k7hw2Rs#2|t-oYXek#&3Gt1 z)KUiARDn<(duas71rzq0_kf!kn%Z=_MxAv6AtpMjgm%YjTI0y0s8ecCOr|x^#Er8- z6;+I(dvs3=6gKAB-V%@{G?|YGl`RUFEb?#BGugGqj-S<7SeTxIUr4RwBxp5SA0CPF zXi_rAeEoZ&lq;^97#hw{{S?+l+u0Q<-lW4Cyt0Dwcaw=;G11RH0wo3Bl@>PH?R{NAl3WDCz{G@Alj&QFjL5o~bc% z;c7>gqU&U&A0iL!?E|}w#7{+FgP+_1)e{aCw@pX%zzsBzhQHge`69pH6M3yqaILte zVw7p{AK3)|0Jov?T>k)kPv)&l?PX%(95<4xsWXPDX>03tiW-`bi4qQg6=*ug=cnH{ zYBKo?7RzKDTHc|n^f&Bpyf~{%r@?sUwdbL7>lT+3I*Z z{{TX?-)HpdYSBvJREkoQBRxs&8dd?6j1aL-R^V<5oVm+`{JUBI0Lst*00Lrm`PGR2 z3YB2m+>gamj&%CJ%@-Yt{{YNZzatoKMy6Hi+)(_Goem_(9H6XjbDET$q-itWAGeQi zqK~?D4ON$8T#-=Cm8N$QG>*O>NQ}7R?3C_L+wylu=|~)hK3D1%ON5!7Cohh%T)E-E zylAXZa>64tD&YNsWcNF!F;3y~Hh`kqMCB<)qk_P;I65pJlJdD_yrohj?h|pjoL1=K ziBcPFMM*R01Xj6z1(Ms4RkMKdc`fdNs^;l=oZWpor6^HP#zVYg7Mxj(UYUP9uTqkC zjH$QKw5w2_`s){LErVe-3Zt6kB49!$pv@`hjFX{9O%GqfG`mai)~u@{8!0IIi<=&^ zK9>-(R-Flv{^+;kIdOs23oPWFyMrJ#DQ`pW+OFJ}8YwCS83`zl4+ykF-U%8t3pk(c z-Wgq5raL+2m-5Skt9>;iNlRU470j1CGe2-zUl{G1EF#%jqzj?dXT&fUKy2lo`74cHHPBSaafSN+%;=LbX6D{BogU}&#bn01eYIex=j(hDT zGbEt+gukFdzfQEsfIG@0Be`meliROR`qQs~0@EQ&k|a)&I>_l0c9V*#_Er`hYCl?8 zT-Ba%$YE>by|?- zqDS_@44f1>fvM->eZSQnCsU`_Sg~PP3)X+jqo510-?FljeYJbkPjuKP__FL*x!L^q ze-(P|82e_|)8;oe`ik@~(h$rw727i77lX{Y|;gufMfKZK)V67e!qaThKg?3xgNY9*YX7(7}(NdEv7IlCrI+BZ94u4O$p zu8yT7{wRkd!d-uq{-ql~EV1Yw-_}3HI{Y3XxH%7N}fo`@tgN3=k<7|DOH7I{{Yn< zMXwUBFaEOJgaoJ2d-WkM1*C#VPy|j_IAN!;qhxe~reZ0SRB6H;L?hBcve?_FXA*_3IuSta~Z>X&S zC$#<%^Ps6jM|vq#p|Rq4rN*9;x!zZ^SZdG`<4m~Y!PeYK+$K;oAc(f&EhAwM-9i;3 zk!K{DGZUlS!_`0E`_R1EK#FP>ecgxIrAk|-Q?<{z>7^n-Y6gG~v7y>#$kdkAV|P4O z7Bsc7yo2G?BCFrHUSzGZ)?@4UT9+~FnoCy;Dk>aJJE&fZ-jvE=MC-1wO~z6QbtG={ zLt5b73xQ-#pzu*nvk1m2Y83LhO4bUF=ZI~>H8VX7S6#=grFBe#s?zRPe-PRsG+iG3 zfE}`9+1E2#tvMwwP^WAP^`2oXL1+~yBg(1u@rW+sVwl`~U>eYm?pDs)XXcXq;p{Ct ze1A%*k8yP+JiE!EVnknVl1cvn_oc;7KaedxWYFNb`Z)gpD5J@npiOhs1eRJRQc>u2 z@QF-)@rF>~t2xYIy@1AaQ*zPw8LG9kg6<-RUQUZ%^qo8P5qpQ00o9>nm))*=&!y!m z&)mswv%MHoYTMBt%M<)WO-0OOJzA>wU8A}tqC|tN`;puztJiY2VyLF9Ql{EKNdZKD z=q?U%9Yaf0L%St(UB7*3t$&d@pDg8nw>7IL$-Oq!ejck#AuNttH9D zWfZ~CcY2;)(-V|tHWL$Y7PXdu3u3HZnPvVtD4dU^kbr05tZt7#=vSL;;u2*)N@8vq zenX97)iiXMFXbz82pSNbOo56;)@oKmvAq_(VZ<%pbM81SxR&EnaK`Vo54feg~rU9Xo}CmLJ^wV@_r|CbN%gY8A>AZEB?@N+Vq~GVc_QGjdJ)hsA7Ms@=cR+I|W{ zHI2EGX3Sg4y+70~AxqalrACK>#r;M74@HA_N1HnjfigGdqO%M`+wGQu%SIY%YDxz8 z37+D5#`O)f5PDbSpCInWO6uXyE;J=O^cccybRwBTL9hZmB`(o19q4rcSzZ2J^UYJ# zqDHANxB7~jJZ{wO+NF?7s9_~#J$mgLbcNpKp|u{e2jZ?6E;(^_>PFJz@D)&lp1CiP zca zjMS&~EH>a$*)k(Z5!P@u)Pb?pcIcU&BOWWKyMGnq>|zX3{)G*e>A)!o8hL&L;TgTg zc29bh@mfIQR_(Istrs<4b{uwx9K|mSX(?$%-K;7=5#Dp{8fBHKnhKSMGGlL$*xrkt z9udIu9$n7qv1%H(Dd`s_T%lqjH-4T)Mit7o26{qGnXl+cVZOX5kEPEq{Su zvfM+sV=sz^>6!@|9s0stU2uvv=Gh=?)O;ZlSTedPqnMD5N^P##dlMlFCvnjAl%R&S zpmbkFl$96Su=LbjXsA}R{{S@N6Wp07>nM`K;{K}bv5ys8?k{m;!C>aSfByhYr2ds; z{B`Xo1ijEw*VW@qTolNfYu=f4C#jNzj*;oeAh5)+F`}+tg?y*HoWqviNk6%^Dv$?- zJiaOa0BeZtJb7+S^3QmvpiLB%?X%WenObHB*({%=Nx{h7c_*@12tF%3&s)iD0GdbD z_4o?@q|x*1_;!C{cNj}4sHn&()lTz7f>KtUyS|f)Cm+I2*q+%>p*zO8Z_;Gjkb#7I zeQKKyDf@Hcw+wMFQR7bCP}&Hot5b>xLHrl# zdXIu-d0X3sKM_pOH2V~ZQAnqGWo12j6Rdg5xjbUp+zH_M9KTBb)c)WvGxf5*Z6{`h zM@5~4g!Kne^7+OiRqpf(tfka#2UTc36M;xX7S(i{_FeZ|z?e4$*Kj^j zHx_E{3uc9@B}tr7LB|%FwnMTkVNDv>n=lWjRhc+h@ro^s+}^ zW`p(kpnI6uS!Ej?Pj;vEE^?}^uQIZLnUOs_V`=vm62^I;H5AUJGf>b+Un(Rpbwrg+ zNj`BYz$BU@8y<)~1}}5JRHByKNVitG4Zs;|@`Xoc=k!UrzGOj9J_Po}`wr9IF0Ged z8&xGI$_#;&S?qDaqg6u5=F9YIvew$O;`r#NaVTkQBdCz*nMB}`tsN8w*SzYhOP{kE zO1hVuV1)#gPDi(473`0gR(xD0nx^ZFxoO)nnx$>hgo7jB*_!Gq{I-RvSW(@WwBw3RI>Sozrm1x%UF8z7$S9&EjP9saYtDGB zw<60^e5z?qJi_Mk(xN7JPf!k{=Kx%6ap_cLz5MKM&{De-%ju~t%~1M*C4>Z|10VqH z05}2B8u0g;omLq&#kZUF3%*56k>X*AH7JPLM9x-LB}-P)2nVd-H!_IkKSWHI$#4nC z0|T@aKp>+=+y;GB;SIP^anGV?xfN&8Qg=+5%orZ!gf$(EKD5$e7Mk0=)v8Jcit1fd zlk+N+K*5iF#FPPA?(ByFBaP!MYXlkw@i>csp=8bX{_SY zpSpZEEn_+%ICd7&TN5mrlDWwH$T5!K*`7a1LFutoaCP0R0urewL!j+Gkbg9Qn`RaY zxmNf+%rRfe9B9CqpJn_9$Xu~)rd6*TsuWKU8>?GON>?*2CN+r64;z#-eBO|X&vS@3 z?6&p0;IB`N{?^-Qn7w7TLgS63DZuJU9Xd%uQ8wGV2z@OTDh0MVE2i&|?QfENKj~%# z!|bKBGSw>SLZ={<^pMbeA*t`0N0K_*78-RmC><}kiYrSG+O2PehJgtoQa!{$O(SZo zHqjQfKwfd>$8HzT)l^i-IOmZYf$yv|Z#E)+ki9n+eodr*Y8`NpIvJA*hbOM7Psdaz zj>pcoEOyybL1wR048D``M+s%0`BMl}yVaH*&+I#jPw#Vc}& zoZ$y13R`i&1k#RduWH~MMMBWXGE;ku&a!~zzVc7EA5_>n7h zTS!1vd$_?mq4}m}DrlTaSE51ad!0MSXO@VI8*NtaW>dxGU+HM87(78yoi}P zsYj5xI;Tfj9+0;hl;aH&LB~_1Jw)%g6EDnffs0q|RESFF_(0cRDk9VQilb~dG_{^Q z+LRF|OpMOHZqZ1!l9x1B8-`XDYFO>fr|@;9wGG9c74h4wUcjttI&cDYS3Hh_tje`Y z)^4s}jQd4t_4gIHyco1k?-06%Lt%Q%2%ob$Dh6Put1GExR`+CN0N3Xa%t;C+&4Y4; znSFCCRX%1loTu^lL_Cs)R26)&w%&Teg?Y8*btdxcay#Of7x%ogDF9`y70L1CD=gw{?M#4g z{{W$jE6CcYF;{wpoY2`C%YuGi#Rc=K9{RP@@dPu6h!UosBuS~nCV ze@$XWepH|BiMWo64nb0$nyDs{eUu00?I}SGX1b`?=v7(!r&3hvRIL zpns;HME?M;A(f^wpDb;VY?Q`Q@(m(QtQ!XO?G9gbpAU4lHXW==V zW+={gyX6Wp{{Sc;X2$B`ueaPbXNLPI;aVy>R@!qp$Z5Yrf#Shw^->yWH~N;6P@dN=I7LKTrONkP0KAgN$@l!qv6C0VnW>7@KaKvi-XrR+?O7G;IrWc{LsnbW;~iYu78`*IjufhDf=nuV zhtfW(B9uy8KO(8buXMLcRg@$UA4s0525`GcJ*hVZ4al&}$Xb}4`W2S(r?{GPZl@@^ zB_>013iVIYG%Rj+(bagTns$CcG4vGnE8JsYl96)q%733onPzRSSSEun9 z7_6e&z=E|`2O*%5S`p6ms3#x0|D=_HNN347jOAxl+NRTFSgzgE4#8HD)C?lx& zL&f5VIUy@uO+eVG>+vw#+$f#5i&Rr?yQY%4Zlz=t5K?CWMKeWIrRF%v6)2=;C&{HO zA*I!kumA+&#bcBkX;qAJ9urs8H7qMR4Gt!1s+j{x9?>dtO`@&A!`u%<#^;=hkkg1o zUZfdo5ZC!oMJlyUCBw~XTBCcJTPk@@E!|0+%vDZ7q17d9t-!c8fFlY_vYT}MU_3R0 zW63oMHUaeLj+)7sbs(3)J%r)t@*{#2s1d%U8gbq%KQ2H)W>X(0?ZkXzSR zFRnW~c6AL)6no#LqbVZtji7biC}{FaYW&NQ_Hy`-kfZuPxQkQ?Bwd7n5+TiW`9ojs zn^1Kr@!5=zO{5^V54ltRnuB+Z2$s^}5}f=Yxp{@4@kB$|jEm2TU~->+Hw)6%k#$i@ z&Z$)5KDy{3hAWnV}6(#j`SLitVm^Of!``&unOHeN8C$vwdz2FDreAMla8gqw4 zNlD4=HngNvBn0)=8#!POg-FZ8oJDA|uH_14p=tHR)Fsvwa{`@LAmOI9qHZ705$ma8 zXI*AYM$4AfTB748Z77vio=$F?(Iw!JKiVJP1Y74e(LrDEvhs9Gq;o4yGwGV3039b1 z?&JW4TmJyJ&JL{;8OU6m>&>gud?*rh2Uw|QGZh*Ysc}vk8`LEa4`oQNd52$mWkeMN z-|>fd{qT2H1Kb=Dh!D`M6BxVErk%2v1f&Gz;3H7AouRc}ZphimT2VZfOro7ANZvdQ zO15T_puUCvNcnkN2uYI0-wBWGju*4dIUSSyl5Vdhr`<5H4!#iBnTYspr0_%zf62HR|?95H|kz+xTPvx_Y)UrA7^hK z$be}0Ec+nLo#ci|i=KWpDVw@w)~DQYLDZSferSo3@)Yg97UrE3DVg0VYgu$Ptoln` zV!x5PW148RUNMiBCyJ<5kz!T13(8$?oR;^3Ne9L=3+poJG+vLzXB!>83D%0ty{}rI zqqefksM=Zynq)dkQFFrEmd8bxXD>a;Nm8#rC9Hm|D@iu4-w2erhV&tX3RL`7M zC;ZEDk^cb9AdjqKSw?2+2bk`9a4j_Ihz^3-s?bUKM zd1@91=0B}ye#yn64lTtRHIOP*&v3hp?vNkUj~VvS?MrC&^45VGYg*QxIz;8a7UDzy z0IT~|e&@J-%xTix{{Zr=D?PINMf8-U=TzuijK}L+nEDe7e;nc=mG?FLQQd=aUpBup!`*vKip2_Tib(g zY+mE=DdeN}ulDdtr&Y=n??Y{}@?+@0pYtHlcjA+|JKm0Xel z+iVb(@+BI`>VK)Ee(0EQZ}>p}0O}9^WdSF4yVU-c&L93FGCUjh#$?Ity5CR})c*ib zo}n8PKdU7DK?bd-`(jZEroM&H zKYF(WSnlzSDnDXET&jJ9Au0?vQltDKCZ%7# z26^FwpeoC2itga~i+^H4S6w}quv956RaFG)nWgjnu^T=26KBf8Rif7m+~@qyX#Gh% z_E+sOts2DY+?PY?x=BCypdag8XmpIJc=)Gr9oANl)Ph=-gWYbXs{yt>YoR1>W!+Ui zgRJEh>zk`NWUaeM3ejz6nYD*y`-9)`U08jU8ub0R`_w{uoOwD^lA}f;*So{_5i+?1aiqq}>PF_}kKjSD!4;jcn z7+AMC3&~^ws>p8`?cUjHrFOc7bnVqN6vEb>yVEJnGwLHihS=7tJeK9X6twoYO;+y$ z``c9=Pv~oDc>UGV+N%tz!_5>cnmziv zM@?L=WLlf7x4ChqZ~BEBuBw583ZB^GQ`ii=(v%W%SAU65oOd47?KB398je}}f}yLG zbw+OWV>X%^Yn}e9^p@9|p`oc!OHV%GZ^#rxhm3sN{D4vIwc~F-mXd>9J;QtNpbm_K zEpm;jcA2OTy<(sr$#?!6iC5`>+Msk-csuLz095|u9QHh5ngD8kDN)D1`F-)=nU*V; zLcA0T=4jmG-p97>uUdJ`uMUwPC3VzjKz}#YS zw+)g8IGX+sD4p8Fp0eKHfjb_WJ6qlNfI!3sO=WZN-l@DN+UB)6RrbZ?>~lS~akWlELsO7mEe~F()`h6Dnl?n$GBw@sF=Mr4a*Y&i z)?(ro)xxST*85+)TdWt{f!m@Tr?FNV6|{pjI^Z&x_(8)G0IrA;%1Dqlol;{B;x?|; zcV^yv`Zl(GRlO|<@QJK03A0J5r4&~v=1#R>AXclRU$E{L)+x#y!iGv*cC#EwQ?k}c zT90pmgm+^QEv%!V>VLs~+FbjuvN99apW>>I-0eD%%>0^=H8;DGrSqy>XCfm&w!wAP zFnF&WUikv$bhlaDX}2x3Z&p61R#FNYl)#Ycdqj>u8e}B-D&A8*+iC;inp(!(Vxj6+ zQ|D-quDTFmMj{Op@!bas`K(!VpcNCO6xLM+`6y8pduw{Bou83QM&(cap$oMiniKDw zdu4RQ?M)+JYn&RKC@Q6*kb|Hykb^IxExaUr6L>j+vNZUrFU)D>N|$G@?NceltKLgh zHslX=H&pqKMQmkMlodXkW_y4_W<>P*!1nn`n9|mo1SFVSN5~xUQjKs+wnu!;E$DKk zWlC7wcBXQkf^vs3PL@lmxTkWhwu0M0pMG$!tzwFmhnywUqxoi2)>`z5K3iHT9^XjH z?oe8ux%A|eN?wHyi3w_Cb&STyAE_UT*7AkFO5^ZWVPvRn{xz+98H#qde6%sW-JO`= zN0Rdf7Ui$Y5OkqYCnKl6R$E=DRXW8PB>IX;01pVuZY-zv_mF6}OlL2!mTzUR8~l>L zhH69BEP)Lxc|y7#y$oqwG285j3l8qgt&1$zJHm-q!`wNXUU?3qC8M$}+d_)M>I!zJ zS?VVR@U9Jb>(eWR8mS%B`;x#|SPOmIo;;L-Rf4w?&e^W6WYej&CwU-=NXC(j#J<|> zk~*(Bb8dNU$#CF8YJ8Nov-^OcrgnzfAK$rYLPY*Di|mW_(Q(7ix*cZGC8y6#HmOHR+ zxZG4dB`p(CNF_4)3HFSalChb}$GVPb(u?#<2M_x+HsHn!BxnYsp;E)#=NLQge*XZ* zST`5r3Cc3u&f?+}me3N4WCWEL9$6AX5H%#|Bg|dunby|rf&Tz7U#9;6WU*V_K1-&2 zuGFV0!S+Drd>+kJe6?qm7P2PVDi(wyq@bBkXR1WLvXo<-1Di{UyKIdksq!lC7X+^aW(?cnU{UXD1tBb24BXryW z{cHKE&he~ehGFo$7Y00!)Kna+z2`Kys;8f>n$+R-R1=}|Y1Syt7Xo7E9~J8CE&YqE z*wEboNqolrbFbQKoZh)ef#VcTUvlSYy%X9vVh-HUn#!8s4&B<5uZ(3AbzY;klAtAy zCv#V>nM-Io%!rRt;Lz1b+bwA+Y^72}&q*j!y)3f(vlp6@uX#CGGR2BSlodZ{A6=*ts3|Z*C!$ zFRUt=y0s7zl}yTh9b(-O8<2|q&cN8g78zP!hYA_VLpu8X4gf^bDRBHI_EshphZ$yUwAz+1T)iJ#1xa^b5?nY`C;%^-0&BMm)Tb2>2)}IvH`;O##D`pRPuD85Wx^~p2 z&}KI-Mp7N(YmC{OI?XRegw8f!_bz%HNCDXA58A1Vh`HwTa^&iIyq2NGQ8M8{D+&oj z*QSPWHW#_$BXnYUpstJb2fklyqZ7wLvt>;^R30>40&vH83v_SKY*)t_Dm^*17sd)Fao%+l17#)u=nHot0O+{28_kGQ@8Rb%#qf2N(Qj}DU zLW;kzMjzZvgcjDVvW)H{sb8m`Xx+5PKIPe~#u&__(%N_xT#wcHr_}gH5WhylmTagv zkvW->@$Wd{2~jer(RQ>}Q+jT;7TWdLM8Q89!*anC!jBb1us%InXpmVABXrc}l?|mR zMu|x10E2CD(Xq5(04S@hliYn~v`L;b;4VSuY{`r>jx7HG)h{)Ko!uL0Gm%oskOCL< z;kvs(qHl8U18hTcyp^0I>{0GbjAY;Mm#b>_x9Yn=PE@6Y%XFqkUzA6S>nA&CI;D(m zak2Y_fHgrZ!`W5G3cu9a@(|)(qghJPFDka_r{NQ9#SW@3{lzCobxYnj;yiyJsi$ca z=`{eSVtVTmme#Fk=%OdTe1%N<3mUQOA*oFnZ2=xt0WD=PW#8uxwcsI}87dia_IqU@zF?qY*A@c zqQw2V=amk(P~{rgN|X%ts7OoW48@OsS|;Ys#svbAR(t~OjJZy##?>~A=OPT{6L1ZR zA=jcr;8$eVSCGxVktNq(A+$CLnKGA1b~#Ip5SJTcZG2L5CiedT$ye(`w`Ohrk{w3r zs1jsE`$4c<2Lhoa$-R!PG*$DFS$PW$$lSM7(=TdVDQpSKK*eV9lC|_#Y(_#Cwy4J- z$*;ECO5S;uHk1!H!%3Lzk`NK?b045l_SepIbvH*X^-qL4g=z^J>n`yc+n`EQtcXvg zp8HF5NO`A03g`ix64O*L8&AP4s7I)c>D4;U6yd5{skW(v<;$42A!}NQ(8zqgFcsk- zBQZ@S$-CjcR`wv|&>w?b(PfTdFl%apk5)`!tXP@U*dmYu}n0`LP>KVtG66(aJs zC>h9~tzjR_LW+`Q{n1-|ZY5pem=Qo!UB6XUTJJPhIu{g*%X^7>oxnONCSn_k>G4Vk zUtU{BG(8!N9|r#bBJqD1F;r04XZ+W2c}-F@NC_)SQj(<}@{L;??A)|X zKcoePW-szK?l#smaX+XjZ^gf6CGxi-G0h(8vZr}FGJVfbP&kD*ZKDN2LAF?#8%M|I z0cAP0oG$#P)x*t7@qX0g?qmM|RQ;_wnk&rXegxpY1+cQ+4YG#T&Ak!@No@f;MzR`8 zC>NQWjq~Z?R<(@dm~1?H09>M9x!-&Ie~DWz7JOMu)zu*@Np%Dif(KtLO4#E4mhO3} zQMnBHxI1EQi=2*j_g{3r-0Iz}yKHZAzm^xQm8^Ynm1xi+Y%uR4u1w|w%5Pt4UMy58 z^^+uyq{M6&CF(+JZG|-4)so#P?uNe=fMmZ0&h`O z>s62DNpsg(&TuOnK}CiNXqVKqhQ6INrP8E2B<%V~=_r_m${y+wNx%{L3-Vg@y1mB zBdYXNWXpVi(H1yUA9K(r3c&% z-%`_NY$kmGmq}?U!CC&H1&8@cAMDk&w~7_d{{Syh3&#HdEq!(T#U$^pr}a;3)Wh^m zgpgXu=s}lJ3wtTzlpdL`Ue_16c$BVR_+h)Rl`hlyil!|Xo$BSBI{iH=mVo&H>I90S zk8f^%qN?wEI1tm|tpgLrZ%kp%wrSP5Eseup33x^5JQgh0LU+|yha_4arOK+5`gWb_ zGx0NsmoRQqEV!d6oNhc(VJ!CPA>$Gj`YQHOz^6ASu2QHJk1-{uPjNcw5H?Ns3bo|iA!rH5xB3>W znTE;q%*2O@iEAfpl8UL7w=%$dRNbVxexn^^p;J8%&|1}I((rp4^r*m(5pJ)#e`bv zXXV_1uk4E>TcWGI!l~rAteQ7G+7zCrrjSu68QQAX-1*$=gTY7ln6KY;Q#hfbgiitJ z33$X&ZA!G1_hRWk&}o(a!JJFAv|@{xA8{5mrHhiJdKds`<=2|EJQk%&ojz{Ev`GS@ zAJr69+b_8-*4>NaHn;D&7C`zbY?gXd7aysqc*zJtM~`#R4Nj1T#QEgd-BXXgj@Bb1 z%>Z3O{l5K%V&2Gj`lpz=nH05L(U8+6%guVWsWzH*4^cEU%2)9T`P4d}pc;NF6%~YA zkIb+RL&+t|+-#1%*eQeeZSO|6F1Xd%EROnCW)@P-A{0SdbrU;uhTXI%Zr*WO9bt{<$sUKUeHM3R3`K}eM!N|}W8N)Qj!>SB#Ei_K3} zboWDBv;@r65&#|@N74a*RlrbdeQBkwW#OjP0WT3aawpNr*`wbb3YYjELWi2CKBtt+ zS;9Xl*V}(ztF~d}PrbKCv6f4(22H(?2$t&EBc@Eu z*S#T9(n=GG^^Sy9S@I&F(I7e7h8pFzt&3vln=S6gy)-p$Ah^qoIzdtvl#qIXB058P z+)va{w?d8LT%hBLYlh~P4}+S1XlklNv2OK7*-z&mM$((vs>nO0hhEU(6bU9Zu`eg8 zj=nL%^pg=-7mn=&cYditJr)7wPg5K(DPGuEm-YB~!dh2EOG4i}jkq+#9XT=Fk z>ad^7)2uo5vUw&BHZ&|=>c50;@zNLd(s?2O0H#L07rOD$sRQFU@6^06jxrxLm-Wkm z)6p~RW9aT4x&1C5HQ~P8H)_|)%4#QX_myf4J!?7?e?PNDU@JJ4a!f?;kN|=)o0x@qRxrF}!!zNu$l7;W zE5+nD6^&Cl^#(ijFX&R^#6$`LYN}M@s+mgEG#)THd9JEOmaGDZn&p*U?9Cd630Bkj zb%E!+bbS%_+nI3~(AZO$x zkm{x_@K(?dN=w%w?)tosDL(-*@qt{|pA|iA^z9meg119`QPQPiTFPcVF~|p0`|d4A zfJ<8?1?4MAcBM+`1i&QoRlJP$?ivL1S?QlUJw+3znkHcmrBxp9U^*q$wWob#1TI?X z@q(K7H0YtOVPpIgt!;9$oun@KsR?d6 z4PcohaUD`EG4QnL(V{>yYFbfO>Q~o6xc>lb0YcvuHerm9lq{X7sv<@8k>AXGVFsmq z*jPLeRXXy&^VBETQy&KEoe{Umcoyu$+)*6~++q@G43yO`o zk*pOd$F*Zs&xDYZGIUg@+AI)SIE78W&ql4YeFc_ek!HJAQ4 zxjeF^sZyWDBQ9|cD&33a@i(GKqm1rqT!PdD^y~f52*N!0spoSzKu4`{1;OnUvo9H+ z?Stdv?r`d&_`{56F?j~_J2?kiSfh9B`dbvna;?epfiJBSM0IC`lB z`L@rpXC3~6?=;cw14=2laDwV|d=(zrAMSzV;=%n+sz%efKHo&J#5kGz8q&IQN2e~) zC1=OIr3ann;^=z3Q}a(xbGFl@3d1CUAYydD_6-W7GDu~1p#?de%7I%{`8!UEh&!C5 zk(F`B@yKNrqBUbXxzvD^yCEqRYd?objFXT^rbi2cm7>O{A-g5Auhh>;kqbGgDb+a# z5VuJR{NC)*BlQGr)a&qoWY5T@VBz-kNb2rDx*dO1pi$Zaes*f=RHJx|2f-1$o?W(+ z`qfDFO#EZ5&P!TVJUErO>Jc;9wc*1x)~|1XR48TS^G`F5Tlpf&yu#$9%k|D~@Z5F$ zA(!Rmu7y50-L*O(wAn?;#U%!%Y6_QA9A;WW;)Q=0g1t0ORf?&)pZ(f+u3%55kaTx% zcU2QMA~qcYa42ak3w57RCsHOz9cS@^eb&`Qx)}#fifr)JEuk#5nsg)}j?$SwNSm{o zr$t=5g09s&oR2|9?Si|xK|OVh^6K5C z`mLhE?m)!6z$|vkYSvPeB?=@+c>XclSimYS_UZ0AspF1OQwt#UqWk)No^io-C=yRJhub08XTJF#K$F7tM|_ z&?esekX%BKKuAu%9U+%*4(g@h_NmY$%9}oQZt0}H5Mw5iol*_79d?8Tds{wNBnX*E zGJ~VIlcEx6;qnM8kGhfNbw+)4hlRwRYBE;9JP~WJJXAng6CGzbd)|(Ws&*$owDceb<%YlSq0xa5|tits0_ zN?SP9M%^DuUfdem*&B9|;XPoH86`t+qDm=6Y4$`;O#n11-R?(~XuzdkrB9WwRodkbs?aAZr($%pfeW zvQ1hPz4l4S*iRjCFTCuNE5RwbOC6)ZP^_nqYU&)NV)1YNY@ei4%!MflaXJ+Md=8Nr z$m1?+8Vxn-t^8{I+QlrpL#W<|q0sf~`;<%NUwYo&d1m>Hh<9*tJ&T)UW^MNZzji9{nqB=j$D8VQGI)VnvxQk1iyf_mkD&spl`eVEhi!hq z;X((Om&77;^EC@M3zbEmg>2Tlea|a%Fhz+KRi%nco1~Q?24I$nGm#Nvipk-$+NjwZ z{{VhdBcwH@2jS$1tNDi*wo^A}sNkDRhUHOnbyXt1;D;Pl>C`z;f$5n^MX|JY8VC~N z-O;=@oqxSG1+8l z%1q`x!`i3bkYNmBf7YN-V zMCI~}<)ho;v)q_2X+!Kht;p6}LMiQez1G%(fRGrtRX4RC;YmuwlkgfvjgE}8RipJR z&P!=SjU0bjLlw+?iYoE-w=d$^o}J4PDoMbN+5nuq!qIC3?(IHf6gtBFqO`K002Ur0~f9yG&c z55eEykNalM{a!|Y{t^A^oqg35qn-I#uAY_h;0gJ>Pxi(?7^?^1y)f6&#C%j_H%q0V zH4fTt4p1(kB@eB&lqpF)Qc8M=de(O^P$6Ao9EPqP6K_)SD<$Ouhkv zKowFFg0PbzGE{m;&tuvuLXM8|H<~on5LGzgbpj*N5OQBP==BA z+QT-l%v?xQWa%jgo5~OMOT1D~^e+7>nDa*qbUgr?rQ;|gWkm+6qj4)5{{T^?6J>9= z)K;f@sTZ`L%OVU9ZHk=uD4X6%Xv6aa=<%(UcD%ZRq53tYpszobKuRrD! zv_mtijxNpw`$Qx-hCt|_&n}T=GX$eyZDQs97NbQ=GQdLqRKd5;NOT`1>p_qtF>;0&AJF440-eS3y zV;lZj_^Rl$TrM>kO|1a0`IT)*nT-nNq+)jYIb^*W=~eE&RgAZX4)djH{mR9?!``Ya z{22F~wp?kjPmh*SyJw~mV~O#i@NZ@DOuFTb6t1OLSF^gu6UtR}>u`q@2@35gk|#LA z_?x%gx!!Bq{{V5#48*WEpk1N;rg6MxzRj3tE;yI3sDJdd$yAD|+x{)H)Nb!6^8Wzb zb7cOgJgB|p{{W7As3qb>!20(Pe?eBU$@%q%k8v!6D8JV?Lu<6tI^)hMLr|0xq|UL5 z@jgFsk-cOw0!xbZpL@HRi{jjRd&`%GhP3Hkri+W-dE^>A?>^)#(vF|>tmGAyH4L%P zr3!HE-SOMk;~%Iv+*@qynF#(CfKT8T^Cxk8E>9%;r;2;BIn?q5)DP6F`l78hmkPz7 z%eg=#AB0usm~L@il*3!>iH%I=dsM|Dz(T@+{wennl=5i<<K&t=9;Nza}Fm|xRd4tN<>Nt4XS!bn;_Lc6h7eje%X&ZYhwQ3rb0}L zfhq}+5}e@>#@4#3wRwk)%9{P~<~NLy6;(Hy=PK${rAJdssD#;u}=XcS`L zM=DoH9zxcIYErM_0)4d+A%SDTs8nv_i+Px1dqbLh!jK%1c)Gb{Z}pc-ih)jn`gWG; z)(65$F<{9(?{BD*v0f|0an^T2NMdUZdVmzC;l4toxZqZ#twF~S(o+8bVh9^d{{RTD z34mUXv;oWoDr>JaE|m~KmuMI~5~LXw6L)&KQ!8}j0i1**bqDt1qmfx1acIp+QMWKks~_e&X31 z$ZZ3+lU3k;=5~*aWb$S=!q%TLqIx;jhak1nRMXt@W!-5h(q@%_3WnKC%d9iYIR)p^ zA6ANgvb~w(cJlaZpUMZG{g9fuP2;f=UHVv=9NNiyV&>%nC|y#?kZWl( z>l2na7-VjRL1y@+-MfuiRMPer%1wJ~brUg=0QnWUKR2}{5 zJd-F>O@K{9pf;YyXS^a9ce;psyMq*4g0nAce$i=jcIa*|(SuCpvnZUz^pr|S=&hTt zQmVF)Twh61pjJOtPE$i_1wx+Ox;3ClC^nu&QRs7VAOb|PGCKHi=xmc7K_dD7Tirh zAb~M41V%+rax{d+a|4Ftihz^k{uz9rT!^SgV3JHhtyR!mGUBFECpQk>)Jj$fMV#Zw$Xo2b%wXLri#<3PtT-XE-d!5LbKvn z`9()n7;OBhf5q-jTH1Y80*y)t{KZly#wc&NbxyTfu0F;fz-XNJIX@!ESW63K)veFS z3RNkeoC-_Hp+z_`qNtA{;yb1G*6qTng(uWWDpa~pqjOJHHisuYL0A$n9hM=cdO10hle?#QWmGczT)ZU4hiyFG&hYS!{xo8FVs;J3KTo4f7pU zk1@%vCqCV%{VJC^Ti%x?@>NZ0>u(NQt@R}ArENq6p!PD9IX+#D6pR2AeUBZulhrvS zs^#XsU~-n%TIskK9Y`Tg5@*tL>kyYY0!LMM$6}*qfm0VJ^9|nTdU!Qbw?daRq!|gA zt50<8`YU5CgIUp7kK2yrY|JuYZ^$YF91CnVmn45T`^GKUF;?oUIVdYHlDeOCe%Sc$ zBjb1KRp}zVwmnK`5}`1lC!~*PG0hGoD*DS{1*(QEZGV;WSFy{gwx*HIsan5@JES#% zew_sAoC2Bnz9ElZZLO)N5?go;#L9Czc7q#dUj+#*?$jo$g)FtTNoJMs+MQ5HN>5qA zO~w>6@_;1;+Z>o@c=udEUP3- zUQ>&Dp~LA|ak1$sU=k7K`E`c~(MPb9l;fH_d7Rdj`V=KAaZ)Hq14;ZLR&)h3JIZc- z{8con8)GdUz)EV<#0UeC3eivCenO7@n5D(yt`H_YIZx>i;fLjvHQ)18g}<3qR1mFa z(jRcuIFg&B2-`AeGYNY^32ih@M=APhvc4VFvJURup)QP;6sHroQ8TfT#d?9Y zIu-)dNS^K5^@t`gZzsiAV+6NVZB&?EB<37jru5eDq7Kjm=P69YfUOWW7Q%{)z0O*x zEcv_bu#lfIpK0qjcB&vUq=eDqxf>Ktq%A=^NR-cvC`bYhTzPVFt~n(=RT7Ygk{7l? zBchBt>A`M^F<3c|=%D8L?8IC6HK_3N#Ff^8RBer9P)C^%m0urHkL4m?x*=Zbm1(za z8nyAq{_N6j{J}u}=sE$aZsh2!>x|VqV>9ZIkO#t3iJH(V%+9G!wP}?jgKv;I(m+Qsp%mh8eTgS1%!K@tCjlWW`73q{0Ly0lReCvXezE6jYZj+mQJan^&O_%B4QV2Z+R8Vo zvcF=#ZXCNlrQTYm4f%(F@qC$RKWojg2Z$A-d+dy;fjtfp1R z(HU3Q&c5QkxqY}es@umHC7O<-HKe2T@f-$$C_))2PSEX2n+IT?swY$06_(8gjjZUq zjlq`?_w`)5^JhA8H@Hml=bv&_?aQ_5ypY2?)G4UgoYKuZXG9==C%jbOA$h4*aL~a( znv|f(fuy_j(i?E9c^20VqeAZIjgs*!1{Q?r3(K8o37xlNT=Rhr?2%+WbZb&HiPJ<5_xW}zUV!Qf(5kEqc=25mGdwZ)~aBuO32 z;tWD6tzXm}w+l-HS(EVp0J0usyK0*`2d(M0Q|mBfJ3>8xn>Vjn3uP*XsX9Wvmao3G z=s>s9gR4^mwBbS~c_t0x{MJI*N;UMRPiW_~)RTJi;IGxY7MNxyfR+M81dKk$aX2zA!&szJSR@mmF@gRiCl`3P~4G_D)xAxO3`1pPGY z@Dbl-p!7|;@zh$~wlI{;HVTX+uz4;r@x-F1t!xmlB*%w%L*i;aJJ%%r!q&nP3KK7f zSQXH*O4{DItyI))gv_T|^^cmsI@KsgPziPsSbgXqu8@g(j6IeoZ5wCJg0lO*$p{JHiA$DF$M){USfX z3juTL3duAHB6~oOjBAGIt;wNJLYQzcrj5};+oD8(37j|@Xqa`;1!k%dwE1K2X(hLY zs2h~23IqvDtP1h!M#XYU2;u^qPlmFFB~nnnffHRQQu`_e1dk5JUq}{M_XQ(obXw$( zZpbEN*aKZUz#hO;j>!~Y#y$`S^7_Nk!A}GvnIch&G4ivcb3KHi{{Uzqjz{8-Xz_`Z zlGJzU&~oI@no`l@#R?KY07BW?b$TSeMB6{NkBgDR6PxP6$tSir4I(t zH1|{|3fSi6Q>K#B>k5p~N6zG3d}O%Vh%++yNWl*}rlE&ksl0VNRDKX4MTLMRV_wno zom8UQEqf8+2wyBzjmnVIO<~5lbQI&_cJ`cIKa?H)Nt9SIcH*^EZbx$ARVuN24KkTT zngGa8=@$!V1r{Tfym5C_wSLb?hUQQ=0TKWTC|2A&o~gFTX(>d&Kr@`a9iU0tDbZQ6 zkAGB=rKG1zr8`0JfY}hO6s*!bgs-Ber$S1$rf08z-3Pz`P*XkRqYx>js*|_AlZAvo zs1>R8(x7xqrSRz=JheR=UW>hYZowz$A&tQnBYFz^w4|hHu|EiH#i{1T@P$%T(;%NH zGwIq;>s-{CB6&r3%Z)otEC3}Vr@|PznuOQD>7?yrrz zmX%(Rqob2EhXcZ6*lxxUM8@hLgKnc04MB`9hHn0jw8h$@GIwE$c35X8piK=pn z)*g}{DU!6*=>r+u9ExSL9Nj{(uWeUqy7~%b6_e!~jL(Fk>5h3V3qH*Thn*Cp@u;s+bgb9pXF{?Psl{nKPu1eVx{J(ultHN7m458 zl#ihI{{R9^R5?KWm7s91`rx_U!< zvX?*IHh!ZdqN7U2svwz9{`6C>{{FDhpF;^!(fyxYH|LN9>RbN8{Ha` zKkbTj=l(?P8t+UhO}}X{w0h=3-~RyF@6GCn^|_9j9x`PnUyMS_{{WGwfuMrFrAR~e zt71CJe^T+k{hj=!U!R)UN{QA~>6h<@WB&libdPl#0R3o#_N|U^)?@m=;In`K0B2Jy z*tHI4Wl{vDRLR##h%!I?gk1-Uz$y5q588$m+I!Bg;Hg{Q-?L1|*=He8;F*PMcR_Yx zRW_@l3P5>~pqcGV<=3Qp$FV)-i^9OEil>imY?Oq40G>zbxo6oNI1oq(g(hn118VzJxhIxY4K zdpmtHgiAr!PO){ir2@|;KB`eje{8j_qk?_&GVzkCIQ40#QP*GBB|Ao+8N*qrJyDN} z`qY1*xIYi^Ri@pVrmP`MxBR5bahM+%ut#P{1!B+0vOcF(*w@+X+K07{M)om@Wjx`7 z;}^WS>(%?zx?CyfmsR>^E(>XZ>fYBxjR8F+5&5n~nYENhZqiv#0blM@oL`7>T$>+p zdnVZ{N>-f}m)3i1`+j1S%5-8}cWtIg1?L^7vLtEZNccc;a&AKNO|Jc4@KV{nTAOShG+w+j_bIO}vqdl0q=C0co+dj%!z z(7WnuyKtp`K1z?3lN)W7{{TWOAGS&IG^bz1bVF(PtM=l0$6!tSmbmp5YNDm}+Ip(e zrMAC8GY6>>J~5cy@oq*YacOZCG}puRuSH{fiNIeAL;bKo*Fn_n<;Ix>DTe?~M?Poj2&1 zN)t??l#rwqjm2B0L6=B(i}1*7B=;ENSKR*qxwo091TwX)6&*V0YyHa2zRVWy*8RHu z@g)P)UE1%EwpS_4BFvOa=roKQyt7jx)8rSn{@Cs7fi3ZIKfQ9Xd$uXN%Z;f!QroJM zhlb)IoMqPdK?scAD8q6=(^Tfvy-;itarG%kXbq~=NCDC&ES*BD_t5SNHDvb`H)^^} z1Fui4S)!LkNV&HJ(IL@wDWRuIO!ojLdx=EU6IH66b7e=BuA<^*le$v;#vJ$88Ev8z z#d-3tRRILD;2_;>YMm@AJ*dVO#E*CK&%DDjM}L?gyEVq6#AcrxJX`)Muf5q+6#IeY z=KAcvR|E6)V=dYFP5c7%J0z(kMJv@uqfNegb@@&>(mEs;Bodrcj-|w)Ip*_y55^pY z!K8U0@=%X#v>i%(d54_q^Zqo&xcRIW*I;WJeYeHxk)WEYovoJktx^&Ob3QVQ4tFXp zVryF3QD;0yjvRFFd!#N7K0XR9($ZZQRZcdkesq$F8BD5rn1jZ|^C;0=?{ct+$ghIw za;BUw?Vp3Zw3h0Ehw2$Uln9Azr~O)Mj7kle1LCP;kgAtUZT&+i+_0&w1Ws}@gV`}) z1S-AtkeWgXpedrVX8AEG+f3OhAPJ{JJWq@jZ^|z#Kow&1hGPrAG2MPC^5cA~C%NUe zYkfm4ev!q0ImDx+{7H%YR(A4fz<)(u&hczzwW)L`)%c~~J=51ErjM?}FO{b2a;8JeG>LHRM&eRF zk>!leLSZYJQ$_D?HG;Kmvm^`~sn&_?_N7Y2YOcPy=&2DoPe|W;M#|d|yr=gl@AY8$ zr#BwwT;9dXN#px%U#n!Rs`ROwB=1UQdVB<5u>AJq-~BPvReAfky@$iM2mMHoT`H{N zql#%V+f6gKmrL%t+DhlIXx49SnJA;YSLbEFKbIYFL>d56I^FGm9-_H1UI5}dwJ_bTn}rf>PfP`um3zT>$MZ^3pw)oc2+)}M-yyy@;AkolU0>Ra`C zsqIXpl-e0ugDO{w`fitCVq1fD?;V8&(6+Dt05R&ZzU}?U@@{o@$7E$b(@jGGJ8R~A z)j#G>E}3_>6m@L43cPinc(dL=&{l!Q;|~$L&Wa(|*CN|bQu>H&EovdcdIBRfxtKH+ z>i#=mZXD6z6W8tk0NLJ6?jqrCxn7I*`vq&EHp(`VL$xVU{OVWmqxZyYTxKUiZmv$& z1r^Ra{{VAsc+55#p5?=|C%}F*O8)?O{>O2S560`)Y^Ghh_hL(KIyaT2)GNQ56O`gs z7g)ckw&)ZOXgk}=tvPVT$ZOYMD}y zCK7;k(o)tYFS~nvu#&P&58nnfPB*ZCe8O%x(~GiRKexS&-q4{_O5*nkc2v)AoMYKb zX)R_fkWjDEp44-@PDPAu%o?<%waU{`P6;5(>(oG@mGN3Jj3&)^N$VoA$Yr&To&e@ zQ?KrFuI9Tf(&VO=+X!tys7#KN5EnLpiv#dh?$KT`+0DDCO0F(HyuDdm(vdYOYCBWc zP_E`Y?;DGa2UYF);tXcv(M@_|T;;60=&T?l1$P5Z(urHFEyEeOw9!!^=QzjQdqQTH z`Bg_u-M4*bR8N^kX%R`dG$m=4LanMx(NI>tlr14bTF6c!iK$0M-->f_=Ut@@hb9%L zXi2Y)m+FE)_#o}AnuNOkWJ*{&;nlWyB~mpkCJ9s|p9__zC)E)8$R zKU|xNaP_+E#bdb*zMb-k`bBNbo`c8h61YFN?q=J4;ujW=#aOeSaoiSJ{ijaN$4;R{ zZ2qentf{D`#~f~@4WxxQ(t%9nF5WOTt-al(p4`HY9YUva23TD;+=PcT^eU=(-sW+; zolQ^l^v`_#P>@cO_(i5Wr){dq?lI7{{SYn~$SU&MmYjHc47Q>CvQ;8G_R?`7Y~oZ+ z#6WaY+a2Uoaq5{`9NO6PC<88@Fu599QX6lgMslaPdd#Yo%2w+NSV+~mnHWudC}C(^ z}NH^6N*~>T85fhTS;1GCq2Ey zAIPznNKx9K9Id)kw&fy}R^b~;dZf?c;|YpV(ulij zT-s4TR+|SVQrSZdUaDE7DL>2JNCHymVAfvMSD%d`!ilP08+=vWb#?VCdDhP79Z{Jk zBd+eZ8Y>NzuGER;S~XJHDg%R}B`NllM@Z2{FwIJ>i>P6Dr&&`$NLf3C{{Y&SkSh&z zMM^e+gx>I4d!4`3Zk!bn z@;t#;7TW;J)F5;-nLY*^e3dA;U=56tx=s5>aMG{#n`O?V1qa$fkb}}*A6UM{Ahxyz zVLsEZGDW@$=snPIea?>teO9`ES;txOHYGW2Be0LL`C&X{Bl%7V_Qj>4RQ?0)J+WO; zcE8i_taZhyNFzj~o{<)9Tf3r~vcG=76Q6bC>>n6?6wWl*codF_pY4T|;o*>R?j!9&wx3TL5Q`IR8 zN=ZuhOeg{(8zA{$kUp9s@$ZV^p4mF7xx?RbD%?knXy{RU47!E_9ST@UPSk-gPgsRd#+u3(Zj8Tl<20fy0?akC1VTy)6Sn({b<8RIQQ{0Q|{HhiDe&_IUcVqp|m} z-D$iJ#UOCZy65f&f!v10Lr{u_X0KP$a1ylm?gCNtc^RB48dK6+TeRxno0`9QH@+Oc zzl&szR8dw@tfjwE4j?E4DUBrs4nrh;T2n)e!!E6GDgEG&cz1qN<-AhGX>O*s^$!Kq zDGa)#NNNF8qy$>H<@@gBrrIh8IJJix0g$??>bbk#0{C9Ride7qcO6boP}Lm@63VCR zH~;`1{UZ68!@Ajl$Dvsl9_CEFl(C0Uq8GZ0&SjHemG>rb3YVFEo$VB|NRpCNC8SXD z649oCYZ%!`c;BMt>oLi*3JaBU4`Q{gI+qkSs-3_zJ-o3*xNPUZtvc3gN_@dLsLC^1 zlBBVWR;}gDC`e(|8T9BAIF1gvKf+XzeWORtKUxP=&nm4Y*DxEpSV;4_sv4A!f%7I1 z_FaFJqT|U}jSIp|)w7?}uhK?6R+l@rrj~*S$8@2$!*SAy@9Gvq_=0b*^*LTSUd2_? zf!ZfpAxKKW0QZ0%>7q3uOiO=kIjr2IP(A@2=cb+hn&z+cbbC7yf zYEf}!%WNDC3cFu#{^VcCFu>(oj~7(JeN>R5CP(%~#u2@Mtn%*fYl{zpg#P01%fIEX zRk{mdWcgLt2r5HRgSRy*ELYXLPP4IPAa&QmBPR1xmeIAtRAXShRKDiI@}vZUPlOE& zq^Bmg0H)8AYXEVhB9$oWJHS~tdV-$g&{P#SJ-Odf?h{bS(4`VUm-{+JuNLaX`WAJ_ z;oEciR3+y+wWBkA$w_E&L>`iwrxcu-q{~#}5$?DB2=dvT)Gr=tfwia=Bm$I^;qZeZ zysK!TE-=k(#mJ5J6#X*YP|;eLQ<=L`lN+|Wfe67%NWan@x~0J{p@*S$NTPEf;ux*d z6;hRpQ;`7EwGt@qw|RD zkv$ad912h}OlP6nErmxjfXXsKlHGZC*CqO4M zH6)W7y|tCSgft&A^R4ye?p|uTp_NrR?r+@}BYTO+->kXZR>h&Es9P-vC(~uQkN_Rb zqMMVwlHuP4px|*4TC=9ADN$n8HMIz3ie=Q+JfMb}NGnp*tt60{l3*))H$Bt>s&Z?_ zMk-a?`zrfXRA241JlF1hkMe&X(j-+{aLT=`F0}~r2Wtw**w=lXPk#uo$7db8cR#AD zPk$xT_ONK3-*x{0bvzS>td)F2?LLBr=X$rasuks^LPfULln+i+&9)@9j@_akC7drC zTcB2Kb_6%n?MmmTCChS*_WuBK%QG9@PCW6fzabtJo)50BVXqv51I*L+KXRm~O_<;#=-0RUdcO6(9L({x2-fg8lEEQ^V9m?tNHg(e>VSb}!J$lbs#LMmj z+M%O_vHcWpyHk`^)Ke`RL!All5ie;4Ou*sMHLPb9oLhY~mvpML47o0Q^--j8Mnr-B z9_AAb`C*|;lMp9uTk|FvECOUdxBFqusvyd?3VHPr+9Z3+aHiMbb&k;>$g>pVaPWnK zxMW$Sl$oCx;0YzgZcRE*_CnY-Npz@67*BY4%oIcWg>OqqOn?)F27ps#4_$F9nk#+@Lb6l3Hl^@?z02>F;^)I74IrfJfd zX&*763h4?wrjkVEGCS!H?@fD}qH4F2DiT%YGwTAp8l&RXEp1gZr$7c^X&-OZ4JZ3ai#Pjy=uj zk-z8qBq%9QrhWAS1vzF7RMFzH9|R!sbt_4i>Jz5Fn?v|Q#%ZnbN&f(5$WKM$`BIdE z67qEFCQL8->lc675_p6O?oOw2WiA4KWPfZoCUK~Ucw>ixD_13jtR)L1E2$6zvo+|C z97F-C7T$LI#B2)u3_Q%CC};7uH>y!kWc7|Le=S5POcOYIJI9q5afpqp+qHedp@jg^ zC0dyt(e=&Sh)-DC#?qSV>s`jF(sqyu&=7mYTJ{$>)oFOU*x1@7Ry}U3qj3p6$e&n> zjt4aArDVEmhKW^pMM|kjOy&n~lt@U*wFw-9cnU3Mt=^4F!VG~Y%Oj*fPS;gk@y?7l zRM|Ugyf~twwI2?!7qI%NM%_XXVPdG{)ejlO+&J%VmzeB_(tC9bnk))HU8kl+ifUw%5?W-en}}dPJy> zfmdX@1f<1v9I6lm7xDwR;BH4@;Xj#T&`ihsFir=S= z)Jv#Jfj{?RUgza13ms2&QvP#Fg=JScnIBNg@r=s(pv<;A8BekjixRY@Yo;Yq3Y5Qb z(j=gaQ3G`~dZ|Ngq;86V2lWc+Ei@k(m5H8}U69nEt5q`I^Hdax>Y2`PxZ|Pfp5N%` zuYc{}+ql$#mhKAA z@n5{oLyS=~Nlk)>%0K|CXK8m4LS#QW4; z9m*aN4MOoo*fL zoTb|R^4d%)+kf1fijV!EFLG|~6!^^zKJUxrN~QavN`0ON-Z8LOr!7k%ox_-+3Hazb6c`kwczLeWQ%&|A=; zLP{wRRU`2#i%pl<7a5M5+~I9UU2Szb1!)S$ZPKLR+s7^~2Td2@yG=Zn=ixe_?RYF)O^yT0VsZT8-TS3?Gi5MNK zi@#+8aI3<$l2lYXM6(@5UY1Q!0nkrD`{9}zt^yLj2ga&%JhH=y(o{1-_4=U{>8_|H zEVNXAGRk$3iHt5S*HehWrB!>B=h=+0eZz%A1!$%J05N+^#W+3tWxp{18bIa0=Nnbh z=XIt{Q#6*kDu?!=T1iS$v@?s{{{RWMv^17YRzJo2uX&h{+_#6*@hK;QSMg>lT&G>~ zzVIEM)i+Frr>>ux9$YBO6J=>qCLG9quH}R($BxB{{RI|xM$n-k99bQy5Dh&=6~`tyh}?}aC;7Rk#`Yy z{X1%PdmQx>=npR42UrJ@Nh1qstw2`JCya`CUm&0@C%`I&;wyu-*^QpDru`~)ZC|G_ zW`Y3%Vj(f^tmv$ZNQqC((x-hD7<5Z;np3WVF07?Oq7Rupp55wEOT#|%5LxvRQ-G}# zKfqO9fw+7`b1Ew&e~OTew8zi?0K@&oS5sFvnPFfOuA5Bdq*;u_Hi2Squw$IIv7YAk z+GX4Ad41O1Yf;>*Q#UHsX}5PilWv4e_cDe>%z)@oEyddw(adT61N;$c@=Ntb^7ZDa z&Hn)LH&i^W$HdK0pMFEt$MvKPHLH3tzo>tLFBK$}IJb`SW~I+kN5{E^2A?!phrk5< z@J}6NoUL}rt=4OjDO$wr^|#B4+$4idL$s|RC-{}PN+)oZPMOEmKowV$Y>9%6tB#4` z#Q0}D&TSb@^ApDEZ?>!=*zMygxHGghMQ&&Fyt1M?cnwczu$wlp(4AE+*7rMihLYbC z<@lybbOD-2`pcI!D8FrC%CHMAK)PfV~nVqQ;BUzXmhVrTJr2fO&Jp10S zP1}!e!Ha!;nm)@dP9y~^Y&T6ZbTPPoo}%ZU+o?o=dTWB&l+ zAKbHU0dgeINJsfqaeKIRKl{-f(x7@|Hyu7t5fn*=);I85ZKG;W;-ls#jw3S2=rQ-5 zQ*Bq06jLqQcup6Lvl7Wds+`X<`wMSzso$^m_e*8EqTGs?6tsb~Ed&!YIJd`vjjmZE z`TWvFxTRIkfEl+?Tdp@#`gW=nwH~5C1P<{m>_j1>MNYNm*D^i9De=xb<7;sb*F3|F zQbdsh%7oL~K~W%yl8I~K5+RyuhrWNP(KB|up4`hR0i8kBQq&)$NZH*+ z&jp=i@l(CksZOsR=5$}?t93gqsJl?H=}VwJ5A2DnEQ3tI0HCs1C$1rY0vYr}su|{!&S! zdM$De-WEn&^QwO_E4@|Q4pqXbyJQKF*Y z*mGdo-sJWX=xeK}81GY;A>C9#wAssYJcZD3Fy2NXt&p%#Uek zE$CGvk>TX9o7#U>4!!xdv&OB;ax2&A6~0)1&=c`qmHSBbXKCx+@NmFvF#%l?(4;;plSt<~2Xtz8`j6P1-U1tFSt(xter(g8odE}u~xEiZWi zqs*@a^Ne-H;+>MO5^sJ)6pmAsYBTge*}r+LM{i#5*0NfB<=5vSs^?6z&NbL)BH zkV_zUtMF9U#$MW-7P@Q2O>(P`rWUfDyAWgu1c=V1;;;y}S5@zS`^SAP@M9ZBCCx>O z(P(h6-9lP%pDKjGGaXD&GOinoQsZ<8h|%FNIU($Qg$Uzm43eEmX{|qtjYx)({4=kRXnc z3&(JI*4(wylefi4_j9zG@$WRjmvN2ujMs{*c)|7TD?UkQYr>rM_!Sa zcX82-ofyADKk?@c<2aA!gH6Vj@=?)A1 zJtUMK)yG#Xfv2CPNAlVeOvhN=I9CrBR;9;MybInw_sfpVm>Ou-sZs3HAE~S5mat;k z3$AgnHtYPdp}3J3yc0g2+CcI0DmQU+!sqGdfcmGPT?cZ{v8-a}#MhT9TK!Y*QmReJ zOdW^JCmFT3aN@{aeJk7PI#o>Mdw-JSkPNtx?rjMl1w^#;Rw%pLdo{;KkOD+jAh>QQ&E>=~r^EX0=s(DDNTC zTiR-s<~?d^6d;AEldk0`c*~?;rtU5>e2K-mqfbqKE5MkJ*Yg~H3O-cnTv$kbHLX4- zs*(9e8&#_Fg_iQyTXs_eq zf#a6SPfQ_68^*h4HJI%c3WcgT(4kj5YAHc2r)sC;)*E}ZNZv?A75UCdidbq*iPsRc zkf#fCD%;*tk5GNHlE?)CJ|BwxY3<}5#u_$Ov;+4j4eqCuEezJwQ=PLaAd*CcKtm-Ps^#1z z@2E}B2J*GGf}JlR^t7;bkpK}t^+ctWz}u>fytZ&UCyOWM`)%&$Pe9dZn}$khBpK-_ z7UJcl+9IR3cFjUekDinaUEe~4}{6%OINIi6slu1PiaOj~g@{q!4 zPN|*DF#~pa_nB)@K_Cw>I(P^QTU8}vm1Fe@tE}V7(xf`mN+c+Gq)&P2(jy&icPWW4 z+E_yATkSu}4q;aNJ+AWKrrjD)0?v{E_ztmreZRSC8}L|z?%QjAUKc}IrzTl4n=%sH zqx8kFcNqXgyi7u4agK$`+m+4>xWcX8N#rX%h3eW{TZvnPa7jl=ku4%pa)Y*vm0tPv z`tw^@yQk_@;Yy0zLX3iBXD_S<`AtPshtgH0OOI{7?J)a#>9Op{HBREB+8uLf2kKzA z9RUh9*6=a=9uu+d`-}Kfo$!@ZgwfYjmgd6J5LLK|jR5Ns7PFTywfd#yaj>|D#AxNs z4>x-;xKSIc7^0k{rD!kE+DIKthLatlxtGK=wyn`iam=IRl8@C@`Z#ZpF4}>jFoH-$VB*s6zjwB+s!?xfm9R{5IpKX4Prz@Z3UE)=mr@5=#pjAi6I(|r2PTeBa=r_t ztXtd%wyRZ&otoOqYijh;R7#rN)BECb_BhM5Ax2u>LK#OlRK#*VeeF*yH7l!4vay$D zHm$alpEq$ZVW&DH)`cjD&aKd*JO?NCd0=JAYDaB0#7Gr1gf`sO(37BrGUgVFP$68$ z6R9YJGPU~<%PuRpTp6&|RjOX2&Nj-)KLS$QKrGQ@9!FDTrI84Po}QDlS0;lLJ4gqrp9%{A#_Ea&55ysYc*+8 zDr;uu`bcRgA`G3$wb?1JlB~;s#v+PbRBTdl3voFu71C_0lpbcY0ur$rd+65~*4nXER`?MmI1 z+iM?k*lCdRO7|hTEg?Y5UeJ9f9JG6@6- zh>_3tH99HwG6NoiRYUyAP3_vRA-Gp#6%+}kQkEGA2uS=4K3flpj{S}L>m5oF_w z(iYV!nNy9C=@gXA%foC}vW|?J1$@9wI|Wleh~Zdar9xY#Zf&GYNO*jp8+&$bMqK2j z-KsvV#c^he(u!8b;VDQ;9hx=)(G1Llg+6}Taa%1ORJW-bOJzr*M2LfNv9tvnl9}#0 zpj$n*ie+^=7DmptAZmK?TsqVy`*9aEI2KtAyDppBCN31#TGU zw+=kR@ef|yhAGgNLD+hX3Xl{X1F0F@Js(bfAWVYM#$G@kj6s2mI07$@Z zMOvs^ai?+F?MYEHI)APrWNk{Z+sXh&nC-5yDpTH?wj_8;aoC_B)pmQf)`^+r7tkxx z+LT7s38UdXOjq+yqr$uKn8}QqDNVq4Z_#5_5?q3lIZi~0MUN8VBC4SBTRqo5rA0n! zt~FUrLz|_&W?jcw#al9eHmgeFv2B59!u*RrH5!m|TU;N*YtkbF+Syz~kYm z5Sf)qN2KC@n@esLdS?^2x1Lr7J}VvUsIRNIEq8R_#x z9WXRi8&8>6mozN@0GQPy7kw3#&A~ukPoy6#>+uYqqRdcFZq=Iz^PVrvJJX2L` z#KlIagI4e~O$L%laHJ<_Dw>q7pa3-_z^$x+28ChwnBSPuU;hBGKehh=D|>~-Z2W`D zNppm_E|50m(=NKSY3?@$QYNHOQ&a@2QU3s#;E?PLTX4K|nmOTQoX46)2rY=!%H;Bo zbG#w!`;V$PBiW`N#wS48BJG+i2W}S!$~GeN*IQ_d-BS@@MiYoeBJs=~{Nt=mAQSGKRWpST}$7Y5E1 zh(Bz{2?nPzxU%Ify`&zbEnpR&f@cVP5W+oW69whWzbMgL13Xde3+;(yroZz3Uq{Y< z;3*U8+wn#c+v1h<>H=Ar2=jj${{RS%lR6{Of}bB1Ed~|OJD%#Qe)pO8FXrEMwUwCl zHB#_CHE5L3;nwF1NpY@&^$sFMdUoj$_ZcV1tyXx*;^?bcwioJdPzzG}k~&LB0+lL3 z&!RPHl}be*j=Dq5WdasSDp7y{2>~c*6x*68g$6vKTYSDSNhu6%0s~UfOD?P@cEpti z#DQ4?UzS%`s#{C0rRBR$l@N!5lhb(C zU7|M7z4atfOWnmA95y?0Z?d0Kn5jS%sV715>kUdMowsvHO{*ErRhFSBb!XE;y8I^{ zhVF+{?AY+(P@I}u&9f?|OZ2q~Oa&l6>diequ;WutS!p?Ug#b$73u9(e@AEOur68@o zQ_#Y;mG-Dml);qYywm?$_=B(SaMd;4MOUppN zoD_3pj)-%?V;{s2=DCXMq6%mUC#Hu%2pOjVS|%jn5mQ$Lvb6G5*0o7Om@Ut)ks)o8 zbDE%UILuLv3UKJE-?$|%JbocQ8o?gnDjC+zVWL~xYgW>BkbYmX5XX9uvkIVBck0&B z^wTp3nKVts7_Ob_m$L1nHg=tka>1IJD zXRpA*Ue{F?$sr3_v)>j1idE(sfFWUyqIBLu3Ou!2uas&><|DM{KGE2o@S*SJw~!}Z z+jhFrGL$I_J-dlS)OYeVw5q1u;o*_hqSsw|zPJlbg#fSjp>D`aUA0KIz}vLcq)p1v zkla*Bbx8-fpWzWx2^@4P(PGPZ{1G9;E{-T~eQ4KHKOd|K^s;%WF@GC;7Sv#sg+p~p z{xdy)Y%AG!nnSY<@CeSI3tB%jsv7vt6x(y=qG!w1A5-CND`xc-PIJ~IqqeGwn=*P*Jm{PXJYI{5e2-4hW&(cr1`@(P_? z5$jfku$3vRGwUO`X<&Znbk1ARzp+QVGXd)H`qt+u>O(0?tj{4nB?nL09DQCsRoHUy zR-4D`NtL3Msl=(G%Y^k(NSTOxh|PK{)+Xb92q;2nkkLT^3F|)47RoD|9!(baR?^#2 zcJ&?~2x-pnPBhS_w<5IDzM`cd5jo398b%6gq#%|+G)SdeD4J246EBd49T*6Yc1JB9 zg%U}VY51#19K0pESvoCBMGWKuOuFbHZ7r%``=M?_)F?8L66ztrBS%!WdxRm)S5k#0 zQlD`SlEUCu3uGwY^=zqy1j$5+55fs!f#{>9Y21)k2ST4tl9~S48SnK4F=<_@%6+T< z0P#J)`>|@qe5#e0{$Wzrk_vyzRiEyk!ci}G@|9P#HJ*x*Guq`NF1RH#kXhUDnK6*x zN<^!0##*$(mbh)n-%^{%NF6^Il(n9vM`Ucrf|8Bh7X1oYZKAbp)TR=*Li_Qj6X#746#{{Z@ei$3A4 zsN%Qc1j5>0pB=J_=O#6ZiowE3`rx^XcGUZ|6@W4cQjeZr7^B+6K+r9pB|Q$S3T#rk zp+tmBQ6$U8CnAZ*!By_EENJSNw(JtKYdhK~pL6}uHwCh%Ugw)$sjuW*Hm!=C!73$2 zN=Xuk{GGeO(O!7AX^xLn(i~R2env@%O_F&lS3%bbYk0s=x6+p<60)5tN`RRMq)CX>B#zdIom4HQV@(JeOQyYJjcKZ3uTK8} z2wACbJgJ0RTzFEVvc04@`i|736WsO@YvUvRYZVWN+f#CvLf@(^)UcIHYD$StKZ9$wLPZ-UOjsZTcs zuqgN|-H0Umj$zAAgz6Pkl0S48lQc1(ij|N50Jly50K$K9Rhjywtp&EwRotCBLB8;= ziz^m>B@?|4%bU}xXu487!aMKaxi4Y%5IJF>Q0Z1r{PS&$4UWMa*Y!$}FN z+#C8;FOYJ3S-@;T&pu!u@1lOV{{WhE&J)HtcO0eU91gC<%DBZ=F?z>;sR!PrZlQa> zP{Nr?X6j~tG{mA(<+idoJV6!_ZhXG@(vY+(`PoT1#Vk+w_5106>F(QJv-M5#^~E| zYH0+7g}5gu3Tv;ViPF<{-DnZG%O~Wo8EicULIqDvRq1=M`!eR<)p%0>0L~nN&lX(H z--=e-TA{Jqt@W){Rk>SG)i7CdrS*VPrRbR%fz&`8JG#*2F9o=SvqP`ot~~qg^5=}% zI`!8qlTbE+si^}^ek$jF#rC>7A7}iKnq&l`rz^Vp;0Xm2K*Bmlo4e6Cb=0HDr}r;? z{khtRG4TE)_pWX|$XKap`4=i>Ol>sR8;fZ@B#~O=v=QaMwfu{wx0oGh_kQ*3&%5kH zVwM+=@;CP=tCCfzjC$cn%`n}!{x8Gh9?i7yU!M2Q>Y-L@Bb* ztOwLWwBNF=WxAu@-gwSB-ppYb_4PJV37MotQaux?JyItP_h__X#?e8%D|csNmOQf> z^H&(%yYcj@v-e|ASMDF0yUQWDfAsSigx?_i7pN`zAH^^jmDaA)m04@)DpN&J+sO2) z*FC5uT~ER#xcpVP3X$AcgR#!>OPuqbGUxLLDpy|H?=XEb^I}Llbkbd8LwIXsSgt=1 zA8)9oRyn7*9JeI1Tdj2!X$;WFqhTPEIroVQu!Pl@7i?~-Xr9ckFVZ@t*EzXB?m%$L zN9&{)XArK4!;NE2D7pya>wPU}+IJ4PDM8A|Tzy`lB$537ghdCDgY}2VQC+xWgl%}Q zLRBzx4H5o=RkVN!+`kF-)8z;(I{`%8JbQ+!IODcw>Ozz#UZ_au655mp)DH0doF(5V zB~@~nxFdZW;6F-q__~&nIgJt=rc)ISx;CL>YJ`lt%dAS`qx||2MFp6;_aLiHLX@83 z8bcVnX;+l&o`m1A6L+m3PD zzOdTTS_QG&K`+}Lq{7>mVo0Zg@Fym`4bK99&Hd^X<~EwguGbe(%8N;LQ&}EZ^FCvW z+?ibO3a@x^p5WR=ZpK$yDbegxjxSk#9on9Wxt88s!SLJH_Q#^TLya+G++;LgpC5O9 z#bv%&1`XOJb+6h@^6$^L#oW*78lYNMsRCysp%au{FuW15T^Zi*PgR+6-?+$Q<7{h3 zsESb>x5F1ok8$7RD*LOh+1sN{OKA=(p(jaCv@?|B5>JyZ-RPxNc3$d4&JHglU=kNo zqO}YQkJ!R&2)ElBQKD;5wGw826N^MnjwXn~UK8Z`o2;y+=0W;YV)vh9cJs##HrqvI zFQ>~XnG*tX3qw7TXzNw8{jp|SWt`l9%2ZwU@5FT!&Q#sh*QAwcsOuGcHy?C*t;f9m z#%#n#T9wgv+LwtecPsvDnOvyRTJ@F2>1cPMD)fqw99a_`<|EHPaGBX{;A|{3t!lkx z{{W621817bTwmNUIK&#(*IHCm{{VODS7)8uC=rWa65SFCQB3xMBT?y{Bg}8(2MY)2DcKuKe9l{{Rwf zxUZe+axO8h;))v~sHI8sQeeT z{{Zp#mzvv+I>PF#mM0w#QL19G3r8YdUu;leml}o~{{V#Nps)a!S&<%pk|{RyUj8{q zi@K%F?-Aq+Vg*&(8>pnCOs2IUlhB=fBaDgrS0l>89Zxi6g}q~1l5VY`!~-W;T&1X# zjN*42k($}z>@*=F?;i!uk8a>m<-;7}*DkHpF=(f4#drkBAEaoG?=ETZdrpv+(u%7m zW$=GcCv0Fj1J3Jg^ceHv-cZ;=2nkH3DQVI&OQ{U=nH|)vJ{a3?2#vi0=hxlEW#Z+K zJjDxY3VF#SiS_9So);d7IBpoj>ga^=KM3avus2i|w3pHppaC)p)=*>+J8cww&HG&H z(S43_&U;VD^wjn3xaBiz)xr*%%fbMb2R2kxw{0;i4833#Da2A;Wko^1H<$gGn0#J} zs2DFlF;-# z?aP8Va}&rpKL?@Z9#qRM)YW+f((KA=sp%C^(p~gv*>-41Oy#;$F_m&IXL9LFB0E~u zv+&;3*ft?O$oC|8uD%Je`@7Ppp^s!7~D zJN%Hi^o8z7RxrVw6$m4CkH1Cr^`d z@AB2TbZpn`OKB+y3YTy7L%!uvaawKBS8h$8A$MAZIQVmNyr8SbBA+g_Q_x$4l)PVQ zE2&n6Jd&lP?M%c2DN=2RMHTaVJw-x;_S*HZ`c*VI+N7XQgry$~njx++jCvc6a+fG# zRH<&V{{T^2Lcl2vEy;k1c%5NF@zqh^(>4kss-Jt<3l*!d74%n+^JlS zns~qv-@J4xPU8~C>Oyp_Jb`w(-KhfZ^$)kb1I+TsI`~cuwY!nh3X-967Zuf1~QOseFG)erE3Vc;8q%LIWuA~91bTpE_-a1hcD+Q`; zwJkoOUs0^O>-!=7Livqp2u?eJ+%7kkX_8((zJVfgC*uV`;vGaS?YkV#iQ(kFAg;4x zl<#hMAxKJ;36`=S7&`Ip*rBmj@wMF)JFT)+Hn)={?gzy9{NfGrJb0^HA!m{$a?@V2 zr4=nn9p+#!dH!g>rjN-HR9frHn_G3kAP7*L`u2~#=lQ2&wU5aovwCaQ9SI1id7_X> zn$DB>P6L{E(HRA_Z8f4}Z8TXO_Ni?qx#HSM2|J0D^ga8;dkl_7%Op%X1PZ$bsu$*1 zud7?(!!3(ZJv?F0r6lT;^{frkMI2B`L9oeY5pvtW?$^Y-M`2ca^QN?1CY1{!TD-JjQk}ZIx$2{ZmK0^ z;W4bYbr8xiGhDk?rYu!Xwym-UT{IwmFg@eF35Xk60F>wiTPEj=J8Xmz> zN{;Q~Qm-zwXSY>8TK9sgoJr=Fa8o3=Es7**wpvB1Jcp%KX0UUdt1~*~wdT21PxI8Y zmkiG0XE=@*;qIVQ@!;IZcNDC$S(~j5t;Xhq*FoM4WK}8#dA4*;yLK8X3r$0TAQ0M) z?K~jour-xTK_Ps~xjAC7w{jsG!oPs215O6Vh4Rc89~yn!lpzg*r6``jK@d64G-#@7d1HuG#T4={+LxL0EQXS= zpZNyG?LP-XQBhlT$z4OYM;Ttb8KZ5)gt(w)o$AxNV57z4n1b^~4m*zxtM)a8=9+8c z@%VlOABwg=YM#yh*gn_oR$PYLnpk;vkdn$CwB~s&YZZ%A;A-tPG`%KuKf)s0VS~4} z1EEx0-zdDjaW3?O;57c^+X^h(>1gjXsZxLv05p{VkOye7 zheTUO(Py&b=G+T`O#c8A{j~X)kZvis=NyHK@y(K9RoC0)?wPbS+K44dLQK^m@ey3V zoJMO(m2k!4B$>NGpjO9$`&9dKdoIN(`D>cU@{PfAL`l;zONuj3ATWizQm5`nn>2k8z5(K>n| zG{^-?6X<3kOlYbY1s}N|H0GDG`W!!vu?Fq-b`;CEtnT7aX-j2uTWp{!aaP{)l;S?% z%4N`Ktd^{_7Uj-5_zxfOr|tde=UqyRNcEHpk^$u;Xh<57^@B?)ptlO7^}&9qnHuy+ zJz-SxN(QN`%53JXrKl%wS()`P&BS#Gh$SGXrI^|(8+FhcP})z{UJ{5op(9m2X>=W- zO-gUqe146Npt!~#HNDr zAyex!3oWYoZc$2gDjNKCg|zvl07?quiAd36O6xd7f_1DVChMx2MCRZV_=pkA0BTgV zv<^NAF;#J6s+~{c0gHx(GZmBRphZIOOaT6D5AU%T{XPCm(RO4=1yAD(ZP;8m_bsWr zG6chu@rFglsRthKCAeN&Oor~TN7MkCIM)@BzYd$FDCgHM98q42_!=Y)CZyuB}(Jw{9wLneN_?S?+aa=ZWj|GCCeb4xg9Xe|bIqr8< zw!+cSFH%=LNMT%ZS$iw2%WqZ+~T)}wV9rR8fHC! z)foOsFchjbV*sE(`DyqGLyfX(7EVhj@k7zacH<-IiT4tY9D8e2E4e(rXvLm8w{+MC zfsltAk7{Myl3MjfE%E)d5Ir&YLcX1&!3^$8De*_t*kuJN8xjWI@K|d=&><$vy~C@^^|mIlq*@PF+rU`O0MHLbRgG2BTKzKv#f0{ByH4}1Irjyco2w*;? z66Vk>ifIx-3<3vvLq*Gtin^_3)(G;oFBL~CRo$8`9=Q_?+OZcR&$07)tF*SzC)tWPM+R;du8$dBLXx~j#ZOiIt zr>7Ee^%d@swbX#3WC86N*CFK6+sQ_&*Bj!oTt`5@h*?Gj2`Bb%b@G${n6#~^WHt#(VlQ*`|>SQEU@aZ_6h}BXr zZHWg&wYKB{t*IW8?-G{GAyDt}$D%=U9qqL`CV&*JQ$0`4BeOG5g?i%_4%nJQdTA(7 z37U^shu*Dbz@Xy!vT_>sj){_gr=TxhoKQOiilL)1M7JIQMasK8N!qDtA2Q|DUHTS; zQ&VuL?BwV4ggW4(+YtflRI?JT006I76%Sl6TDbLF|135$)AHoObX^ z6sgXAJ`pWy;&oN*hJ`qFRUmFER^I>&P8urf>WNKAE16f<V$8KrWM_yYtM(uE@DP>8OT=J^Aw(b?LRQd=8ZaB9z{{Xy9 zS#0j#F#*JyD!tYw3s4Ih1y_tx0aV|XN!&D_#wME*kDh-@>s##-%0KsmJ<%azX*TEg z{{Z(DpX`cvD>S-c@(WDunEwFV-8khJ)JIYKBoS!nfb!#MjX zZ|(htT)n?jB+Zql$UxUw=@NKz>QPyzQZ+L<60}t!ktLmrLPo^P^64nNvATe$Pzn-M zmiDz@j@x)&-Rb&vDI>*B!>asN#gosg+)9?zf$~%mpRF*d#>UIUwGR8Z6WOp7Zlg{{YZ5 z{pfM`vB+(DzvW&z!EjLU8xw8C=}AEy!m5_(pT;}=W&J2WQb^&Jt@(?c`e=!2z2SS2 zxFs#ClPYLg657obEd?4=+fUQ5nM19~&N>=?2zPMoZ75?7{B=m%mo#$)^=fkQ$@kjd zQ}dyBI<%yMf5O_*K9HB>XMa}}^mq#xzF68nNBE@btLttK@2{_RvD2!Zb;li1`mC5k zNq19JmqNyTtpOfU(l)s$Bp!tMME?ME9Lsi<4|OpKQ%b^{K-%7i^6CB2BMb#XPbE&J zI~+||iXKSgmflcpb$Jzqns+;eyLP8QAb5gdH#2*1Jkq9z_a`|0v7aRn8V&8K@&K-n ze(^k8%WQKFNy|2@p5t=hRlAMalWsRBQ9#Yy+tQ=9h&!=ka&>ScMGy&y{@rESHLtSx z_R0y?ok!xW{pG^kTjc(slW(_n-%je#^y$;dTld)gmd~>MmBzqJl^I3cyR7FbW+Z*s860%x{EDq zDgdM|bNa-=`9iKR`pRr6oCb^(9-@D;54S#*4Sd7BXHKu>beg51`N56oqfKhFqO2{u zwpu4aE~LZEI;9tBMI~_l1?3#05N|)U;q@0>ZoRdxmZ7wQr3My4lo9!kpz9kC6yrCN z!X|5K)#mM*3wleg%=%_MS!p3zJ zON>7X%J~y0v|>2@B9n5ut8F{3q_*0Bh?NpL2()1_7gv&o0YT=f9J`p~aCp~U%0X*9 zwJQ5Pvi*m-ZcGGbhMURn`m;BH8#qSu{rsK$d^rL5&7Qfan3(Ex1G+GX6Qpl z_o{Ck#Pe=tX3N}M41(H0R=!tiM}gWY`_-xYjrLz=_KoZkvje5S{{U5L#;{s(N6WeO zlOPE?3C3w_+z7LI2N>D0l+vqq#rC%EjpM<)luJ9wE+eH*x_lQdyt7WjSjchI`iWGg zegMXSZpPGZ=(FtA>&PH=Dc9qUUE%CUjoGHIr>0fe45T^(Nh?zJ_tHHL#xC&|_H}hD zMd5yB+ulc!%@;7c+pnIABUe4b7loQ~*)8ogJ8 z;u7OA4Q#Hfsj8Bm=uoucjW`vsluUjRTjzj?&)~1!uN9S5(r=Gbc|j?fzbs+)^APd) zuSi^3wa}W61z|W2T=hQ}(<~>|K8lKg={ZNLFnKb}Pv~9_YB@Jxz~~DH1m!RSc&eQvJ;ntZymmT zcNpTsqWtgss^eEjD%VG?1r(Tb++B*#%2{oFb2|&@N*i9?2v6Y}MZL|l+3XikHDI~! z(&TP2(d_XlrQ^;ImZj1RgjjMjpR!Ou87#utLJ-chM_ zRku{+RyMH;3v&&FaY_PTRJsurm~nRo^t}AO3(VKNn$g&nTSm{!rlmwLaP^kv@meHQ ztqS#DPxAi&x9Jx>DndSUZF6mf@shb0G$U`BsAUf(J$-U*^lV&Dqi2SScTkNB} zjNow2+nC7`kVOT208$%~zVV!6ceGq>JadoKUh6B6wwSG{AEsflp)JbZCa;i;`;B%i zvUa{_^gROKyU+L1>)c$Hws!8^@I_sxU)%c?333HSC0TRGa=e!@4cu>U)Kx8(iV}e1 z&KF1kl6sj(sjeFYY@vGm$iv@dE@cgl3e?n{dY-BSWX{rE@?YF%gFgkkBxb3f!WAE;bo2IXr|}8Tr!^H#dn+2s7p1rv@WKBm;cA*);{R$2E|#hVr= zUG#fg2qW=Ie`2>RhdRV9nN{+<)UuXCblcVDSEizg>+*;m^B8TWF~&Nr$L!A9^C4y~ z0G~i$zpfoxY5KH6{^Ivai)R65wN#Y_s+CbXieNPJDLY5+j{UyFyDr^2RQ{lt``)^X z=;Mw0wemDm^rA1dzY*D*;7cbZUliTLD9MneR#d7^7Me=PLvmE*A*>1a8O`tRGQUa* zY-|Jctq<&vvUn(PvSkBWWtGkZ`2a`*`*_tzZe;d}#2l%U*s>d)mX+(38r?J6f|u8sy&(o=cm0o~kTRd#bEAI(F4_UgE90w@P51re*?d zz@obBl}P4!@`m$hLSC*8xTHwTf!Z8wqN#Gi@)RQEthUQ^$!TABq_oqZsA)4ICkKn5 z6>jFy^s2D#8ReV`pB!N|3un{_NFbD?$N&d#2r4-M8$jg08hlV7IlV$WUQ2;pa#pJ- z)XJ7sq_&QvPv;3N1R<^_cy8$wQi4{Q33ebtU!zcw{MA+U{SMcPb0wnr zdSo?cda1P4I!1v$pdkGs4j9@cQrFWx@deoLuxod>ytfy!H~Q)ao_(~Irlys@HuNH9k`Che(RZ=8|lu1vfIth-ERO49` z)vwB}4DyK3Q5s*94TU97z~ zNRWL;T?k0{sZxlT<0eHRJi8dKci8u~dKxP=p8Zuo9<5V|aT}5d(o2hUdqXy=!>ye( zf&N8xbH!fi@jEUdXtHt>inLeR>6>L0sX;Be(@EHpK$!+ZEIrp6q64~a!^v1TJh=pA{&y~a%`5g3ZD6AlyBKBMN5sewi=lOs5zz*o+(N1% z-fFH6A=;UkKH++n=2@2t5nUl)U*85EmN6Px`HD-zlGCFq3Bkdi`xaaO-i4b zHw9%F<*9=~et%+8iKT8SM2SzlOF?cx>Y=6$c~VOZp~w?PZPk@3e58ODPv)M63WS=V z=C)`|zdvw|W<@~j-lo$cNIkUe5&3ArMPf^upshEzsgCnnKei%P6CNA#_qbln-8p1!yRFg=S!7V2}1YK=9Dm3r) zC5_`5L-pH3V9c4MY1SWjOgLz?-0)h4Xj^mi(g#qWo|;2tp#|=1qEc|yB+bJyx3ud? zQ1u8Ym&kZQ(YC!3C3>1Z4f?lKjOyh=l02_%pvU>1~NYD8RcT5D}l1tNa|q&Ffxy%QJ9cE%~K>zZZ` zq)10zG)UY{6A^(oP5Rx&NJ^ASgoliw*kb@Dt{tjnxtEaVzG@I{ z{{Zs^7uLVZR@uP{{{Z|SPMLq?qyGSb{{U1>TYRdl{{ZF{9dZOtx~P*WtinnnK8Za! z?w3IyCm!N@CMTGjE3#IgiOOr(333sROzA2_Nq0Ou8K zea#xR=2kOpv(ynV5|gJ*JRsR)3}O<>ee!^!wkD(gVssNQToM!b#d{q%_YdN)*^&O7 zehP8p2uP-DPw@Wex?B0IzUdz2*HuzkDw5u$ApCnnw=bwn#2m*-#oQ zRXKItY>ZGe$Qnz{0HI8o=r|)O#_=Z&d(@7m zg`ejsw}Pj&I>;GBw9O%!s!g5Xe1e`TmihP6{{T8Z%V$vHgumTXRA>3t(}`^&MOZYF z?KJxN1lsf5BD1!aG`2PSV z&a(^MMSY&WbovJo64e{YKHcYgE*guhE~(7_42`T>Hmq)A3q9F810; z8aCC`=tua4c;9YMVpz@djp)7O`{T;*7cLFQoE1EM#<#1*!SrR7RL9h9!%I@}^=bDf z$btx8Q>3EbinA8c@08T6i3duXpy*~Nu~y7U1Q=Xk-J3O@*gg7hZU~dO`PS8WA%5Cqi$hs zxZ;+Seq|8YG7~bK3q8{!KBGXTtZ`QtEiR3u)5#iQxy~J((Ek8Z!*Y5&j+6M6OzKvk zekuIG{UO+;WA$mOkG!>>2W@*uDL}pGkO0nIXDE4~A$KC0l&whwt8}|Vj&*cP`9dpS zs+9QrVJ~eL&2>?Ff}9|)S^mgP63{m&ML|<)+5&;nZU+>W`h10VHtO9}tdh8Kd{rxXrTK^;+rD6!KCuNIl7iZSn?V zMvjOZ-Yna23)}@zI{f0Yk;SxW3LR_nAgD}2=Dl-De$$Lb@mlDLQOMURkeXSOq~IAc zl6WJX#3A!T+~hl*B{`~ngrO)P0Wv2JyD0Vwlr(%s86CBy3!~)mTSW85wYoQ(2V*Ka znRrjeK4xq=y=YT2@PiznP?6?|s5uKTxHI5mva!oO&(PmGw?ClDdT* z!=dqqp?iyJnYpuIet{8PDxOkT{KL5Sfo|o|O4wVrsS>J(RJA0yOrjfcR!?LBP`0+_ zznfDZ0+@Rc#4DhmUyKwJE@q2-#C)0BR>VxtSnW~>Tbt`I82L8s7SKBGSL}v0s%>tX zM?(aKz_SHJN_s-q#08suWIM+bZgLod(OB+B6eQzRDw#-0)W`$Y7Mhe2jasayZb>dDT~4s& zXeft#4tIhUi>P2f*R(HDw%bJq-Y8noQlGcBXDdF_qF-Q9Ww@%To{{= zBC%FmVr+mb(7AuKBj?W@Qk#RgJ_}lN3p1)IYN;!iNf>A9BRv-#g}84}j2XSK`libF z0yNe;EP#y?1(TX}Mikc~m8942`T)+_ZLJd+6@qXC(t)fGlRrl&gkMD#5MBy4Q zzfzR}A@hZo1ix%7n{gz}>n_GSR6n<>G->b~-KMbPX;gyER-;se%0LM1LJ`oPHLX?3 zBmw*33DCf|{-sJ22qR5oI>MZ4mKX|k_+F4!<+SB(eo{7qH0z{UxWqxzYOxOVT$t?i z@?9r;SHfC$)kjq8Vqqx&5#LDlh9UsVVMX}~&!=?Pj;~6q0ybJUDUc9^rAPyC07LN? zR)nfO#k**cx8`Y^Lco-ycIgK)LKGbE3aGcpEm9h4v82FMx)c?c z4%MI$F}h?+Ss25)foQ7z1ni3*;?kgt7TN_=xZ;QbM?{Gq+ZeULZuR;TjC_IsD2yxY z_WuCVEoe|P8lL?jhqa(7cua(-m&laqXuOul+Gj6>S1;NMz2YgNiua0V3(MGg7^}?H zpxR0RNutvCZISrsVZEwQI3YCj&Xvz@hu~w8wKX*D0VwI|U-E#03HU)##zKga;i*$> zwY;|n^3J4ogCUNpHtzL+bx$pl-0O75awJo)O?pL!&4X&8-`(z$L{V~1*N`Q*%s&8O z6ACFCGo@9O`&1!x6v+l^m?V76;(r?Pv??}q6Dd-^lGC>xtTUgHf4Ib~r!KGS=5U$+0N#w2_I$!<4a}ND#o2YdpbSjzp#bU_N zt=A^vS+%OQ8_Numu+X`7H=cE^JCaR;5wlzrpL8(jcvVp8(z=>@0*+AdO$X+B%h~y4v@Sm6}ilvtuRC@LI z&D~i-ezS`Q9Q9Y=tNhL_jc3Ilk~1P^Zx)_{Lg<<)^@70q>q=XqZ!6nA9SlmB=3j>;BZCR z6c^>1hc>qHOtc14GJ&IoBBhk4;HjdM9=cXL{{SyWw+R|0C{m8U z03q9J8J>M0s^s~2BX6m|ew4Se@gE{yR1s>!?p52Rw6#M}w9Cd}GJ|8|myYPtpzgVj zIfAA}{;-~F7qkv~yEfCU;B{22uTb?XZj*f!X%D%N_OMuQ=$}^xYMSr!ka*DXIpSSzbvmCSS>fFH?$G0kr?MevrsxmC1 zxPVf6maV8lPspkq8P>bxZ9f#F!>1a{C4T`#b!+!Cm{^p>RujiqXB{Sm!0jb{zaDC? z;M0lJFZDMCyi}z2@{M8Zd)L201r&Be5fn}8OT6l=x7_ctFMGUe!C1$*{{VAY^*!el z%_=QU*=?!6)m$zG%d=^5l{-wP(93cJZU`iZJCAd$ef-XX5$kVLN`BPmFYP{W3llD` zh%r8t8;iQ1A_W&kzhU=_-pg|4%D1kUD!T4hd47e0Bg-Au6}*&rgZr20KVrZ8WGBdv z-no(Y6_ci?m-Cf+fP;6t2ND%95A_$L75j4H545+H?&#+e`%2tC?Kx{NZee3itY9Cp zM6A}dRNU#dI8@q@nYTSWKejzb69Y=|WY4QqJmojorMK24OV@cG9ZVZ6fuK~Vt9Cih zpcPGZ9a|=av>`%!MxgbH3wwZCg1?CFL+Dh3$v_e-p0kJJG*M4sssdh1LEktd{eCc3 z;FZlVuGZylUCCXsHshb_)SJrjZ2PqF5=qMTaT11 zCz5@*;IHz>BQr^q>+q_&kNux2uj}6Eb32QR-0e0Sb8$*DnNzh5Za-2CbGca$cM#L& z=Kl158O4iy)|vpv58eI>v;D=n>+_#3xjBQ8U5Ho1^{xo5&u{4;fLF{`NNhieTlg~5 z$1m1at2W2Q-S^yY^bRd$Eu|y#f(MLUxPCeb2dHYi(ceBvmWomI?jfE*OdN^-0B^S1 zv@00n`*po-G=Qp~Nh@`30jcvwrDKVE@LOi3qS#7SH1ZBV_8!L;LzK&vC*NtkXRyX&A=KNuXYymCOW^sS(sfO~jW?&>a-yA57{L z=lgxO)IZ2Jr75+zqVMw#-wygjrx~|%<*88o%I%`wfllvrjh1mLVzK2@I)CAXbD{Z1 z@I>~s{%HJFMpQKOj|CxxsR3q3YNa4!2)2e@^`?u{w|Ca!EwvS*uzV$tUyG@d%6Amf zkaRk$Z1woZsPM6$=@mXp#JRhMH$PB+i9nm}CCN8iiW5rUld6)Xr&#mN%r7&z{8zAS zvk;3$f=5Mh-pXj{E!=RIRN!nf6sn~tZ2>^**XJIT;12d|s!H$=dZKO4RQT$eUQo;_ zDs#KNHNKJzJf;>$IOrwocnO3wXWSxWSyiTTZru~ zVT+9I89=PoeTrn0i5{UYVV>mIOWIb=V$xh+l?WzfNhhM+>pEwxI(+ ztQUzrxbg;5ja#p^SM^gdTGCdES*$C}C$6z*$8q`PV0P+Nc-Odn=fv3GItIm3(@LUl zTH(8{0>dv}t_!GYtnF!PQivVpsWBgv!am2!o znsRChVJD=C>k-$v$l5eIsq7bM?GO;;Gm#|fSGRbNA7l~jXkJnAy;W%vnJN-Y zw47CrZGfxy`*ueFKt!1*6xUn!sl=4eKt_`I#frx|$2CCYINR9mQZ|ngev0Qv`z{t= zrExAY>Sh!alQ2Q*HHn*C!C?+z)o1*>gF%wDbQ21+ja9n)YveC*SL$k8RW)4=>W-BO z6mM0RTYM%!4az_>jqi?s>dXRq4H3 zaptO$X1Y|Z_cqF86}YW5?wman4)d+#KP!^#Hn1CvvIjR#Xs` zFI^18f$IYDcHy#v-@AzKVCoNAeuZPh8FFOrj%d0J&5fdMVKh%x_;>%DoS}X zAAapys2vn{J)%2-Q<>yI*+s?m@L&s8@8|POz|2m3V{W z`(sGPX2m@oD-Ox=x7fd;>_lwY}NU3qOr9Lz0K5)&K zFm$Gf?2iL%4XqSD%iXPVwQWCCt?4ovNP;5P+o>Ensx0`JLFktl1}~i5+HEu!_i5Qw zwzQRX9bs`?%zRWu)-oDaie_tq|hpcTP)b!wYs?Y zD{9O5&o|>New+N=W_H@c&4SD72VwI60E$wF7{)kNhO|sF;&{jtw6eV_t9zA})Bgag zSaM-uK&*HX(gc2;RgMN%P^+@*Xlk=Iwk=wuR3w!3o}y4Sn~%Dck#Y8yM&8wGS4V|V ztWp)U(`oe-2SSvAII~L8K!C?w?QcYH&EDi}IE}vIZjFXlM1=yiNN5LX#Fe%*Byekd6IQraFc-VUTCNk6 zyJ5y?nQE*umzz%IA1`TZC`2;?l_j|eZ%zJNdY#JKgUP6>d=)ILgRfrmgM?y;R?rm_ zId1;|kI+2p6zfKl22#*`B@JAV0y5RRiia1Q>s57XVW1R;RV|;}5dQ5-uzMRtRABcj z!j|l-3$ChFIB~=!O6UfX`26BJ{{S2$jOnU(9g=h}3YGA?qufK8xQBNxnYWd-bCiHq z-qD>x{{Uh8O4KKaT|^$Fevafto@~<8eY5$mp-v3S?5GEht)M2Wg!l_;fWwU1-&B+!)Ja z3#1_+`4gvxl8ZgJ%BvRwg)h->FWpj9lAsl!hv`%+id^z`O^x7~Yqvaw)KJj2*b9Bl z03dXkh|HbHg}a=b#oD^z`$tsRdu7e68P0EOxnp$+sGxcIH0o3o2nWD)>k?RNw#v$c zE?&vd$+gPP{mG$*FzNu}0P+d^Orl2Pq8i1Ts*%Y6ZK_F=8gmkTn#LO+H^t<8^z9nJPcFBPSZ7;8Aaw=xSWGEn8%u6rjHyA#-$GG^&SK)#v(E zbIiM{(xOhF2|`&pXid7$xQRK`PtAT_z+^IvrnH$t} zbf|6F00w}?qZ>Zfsup$%xTlkmvf7qy>t5m}pBd>R4&Tk_(MRH~L2_q!PW-x@`DJS) z1?%|#0Inl3l6uOO%UgiZq&GM@zUypJ^KLEA{{Rp%F?2sLC|3UfEnD9Ku6!vH$HeRPkDb*%!jn*xw8irIovr;f+J2BV*UF24sF{nf%e4f` zOH!4oltVx&RGrht#E#|#B9hNi2Ah*EpNu_{Z5X?QY4<{bn4NfUCUmR#oR{s2RyI$~ zTGnFPwxjS<#}lh z-9NERP4uj`uu7NuMkb$kA?byig7k_fGX6@^cf-eTPr|~^MWfIPlEi}~agt}5d0zjU^Gv`WA zRd#~Z9|Rh!H+Y%1Qe>~;nV61yi{W4pstm@kuW_kTj+;}w`1nI6XH^ySEuuWkGxggr zo4e1{kQUN{b=G@fHxha(Q&{^FXVFm?C9T`$Q&U=*xL#V-zh3H!gpV86?*+MRVQ-l0ZV*v$5avB|CMpdMGR+Ns(uXR)c%Hw0=bXozJBFye!5fF@*UZ>=&fL;!HL`3 z6qbuqN2648(>LrDP#+agT08kl6S zs=Z450RzCt%j6-I%4F*fF(LgFMfXMR&3x3=zWr#e{U%S3u(mDq>;C|OP}Wtj^!zJylsF59A&x%iE)Vrn3#bvbi;KVQw`!9-&F{tv>@X6}H;T z6Z)+VY_+}2RiJmk{{U?jnsM(f@|}kqrMYr-ff=?Wq$BkCrKeifkf1ewln*dZbtmwN z=q7mix+)Gr%H8h#y$_N902L~1m9}h-(MgbF*CwwPn)HQJ)ij{3r@B-Dnn>;pO2;60 zs8yMxbY_Gh6*TRn6Ss1w280c0lDB$`b+6EBrmYQDS8n|UF;Y^eN(9JACs+;gfub#z z#Itg3GTU`_cZy^<#lGO~Ia7FH%5Un8rfO&`NRiTdb&-Tz!eng$PvfsWi9_jH9)%~k zr-!WEKZmaswpYJ)HLm`q>t6ZMbtM4=wt=D&0F=MB2eGpdGiXv7+`I3m_Z$Gz!4&0o z1h`xQoe9*+2rQ~<+FAs_-*I%6ttAtb=cGo@5mJy&s|g}x2%t1oZ-mh-ZNUxGr^+Tq zrX}J4H9*}GT(GDJROj*17&@-R2tjDB_e(i{`dw=1%|P1mL6+eSw&F3a0M8W8V1y8i0ThQbo zdXNW)cPKiLCGF7)24Ei;=qN%LiB#t?pzR+5ltmDzQbO1c z#q zu}J#U)90iTgxt3#iO@g*r1;8l^Q5ZzuL!}bd*VO|&*uZ}Af$VMqZHcei2S=}1UP7r z9v0DSbf#uN{b7w#Ou=myR{+&|_l0|9D9RS*?8MJ6Ka4#~+%#6BC;AeZHA5f6bsvM2YmAE!kL_I-_m)Ma192R4n9feW62DDE)TgK_K-0 z*oD31u`#4oXq;cQQ@TKQs-x#FUTWmJ)iCjJoxva(YGVoI9DqEUFMoD>w=TEZTskOB z)^B!lrCyV(2rAJtCNlTjxW22{*4VZ+peH)Zal2evlHDLW2ry$NB{x00ThS2T@dekR zOIX~a{7^i$!NnJCblCJwEyIepuW?Mt)XF2BjT@AUGv&Ps0yvogK#&HS83(`^MazTyI zS84g2I)C1bzVrFzZusH;oF|6~LP{H-a3&y|bq{oXO2_z4_f1J{WeQM2k~K*n4Mb!d z*BvC19ieReJ0}}q6)5+WuN1X($U_b!xD1FA`NYoywUMV#pgW0q8{+h<82fXX+AGYZ zFf{neJt1yCEH55jP5z*wkGR82Rwa5;bWmY(KO~rm%>Mu-6~#~b;JJ5rA9~!mYKip?ejyMY zyl;0vO@0X%oRb|9^~%9dh)z~L?X%oQQc~56hi^5ZU=+G(l-fG=CCNd|#oRmiO0V2^ zD+dtuOw{<}-)|3fm)6v(oFbaUB{Z74#5P3x5CV)F7aqJLqNHSd{l}m*+)wEL0E%4F z{jfd9t)lJ1qRBwod}|ir6aN5pA`FX*UDBq3RquY}@T~b7_azo3_N3+vlJLd8^_to3 z6x9YosS8t^N63*lB0ecRhJ>^z9KYQ~mAqS{f$l%s;H4#h*^iAdOlq?Go_mFPb(Ba` zuG2EzI}8An?;>hDjOF7J$Aau_Um&OGR7n2rvUjfxx3vPl0)M?X*r&HIwx%~|UB{An z3a<3yQyWduECny7fulGl8^^FQu$iQ({8VmVx!ujWxeT&~jX&~_?+Us(%JJ=w8qVqy z!fLF$VW~gLrl@edprB>tTD2qR75VcRt7)qjs4q)!>|ZLqu^(JRW%H#!VsF^ytX_#J z6c}5_?)92H*VsupG{#7loW&0t~7FU3QMtYb@?Rr5xUQGdH?g6>hw-OwGEef|Rn#5J4R^>jXcOUERk=fB{Wncvddb z=X?$>c=I(zoJ`wp(`2=iGYUdNdz~V~#o(lFC_aBC*<)&Qt)g{8LVAzi77f)@i-V%; z2kl7a-(bGx0y?Rhpr4QYx`b3S-_HwI;ImU0{{Y2t&B)DHvy@*_>qH<`)C4cF9 zDA{(>MPH zhU(S!^`Lm7fNuN8&FL3ZyjvE=v6QH&u&PT#YEl6tfE4G1Ov&9lwl{1|Ykxwa*!{d? z3Eec0f8t6(-hJEqg1)81KgjjA>UU(7($ONGkpoVj%5HM{#cJc6WTpN(4}!T^_J0L! z^2bs<^-W#RKhLs4vwY32*HoQLZ`>-@gZ}_@I1~FKGE9}+rmlJYYJLtoV+f-oX#D{c zvrp=&D|mW~9SXe1@l@d(oa3oqJhi$3M5m*Y0Xuxa=>)>i6l9J^jjuwWCPciyNFg%! zRSux4N)}bTZkUw%2$i)e3i2(%FYNauy0)muSNzG^rAQEG)<8-u82WDtlb^Dq4ywv| zzuxD$4{(@P4S;gDA-Q+L@TTb6v107gF-=63Q8XBB<)yDO+zHZT%i$Bao-1W!T_i$| zcU4i$^Q^{E%#P|!@w1?&gZtM~{{Z_xmn7HwN#&YEDngGZt1{P5R?@<|{G-d=+>34T z_=Wlz*bA?77}4apJLPMmmAu8tN_En()$;Ta-~w)si0=c(HVY<>h+ghxU2~Fkd^AnN z?0&xBQ`GG9e1MCi=oV2GcBK9Xap62;lWw2&+ZAgt#j5S~0^enQ8<|yUBhy(w!zmqO zAnt^D37}(bAGN{4zk<{|VB1Kgd#)R}5kRY-FvuyL&2o2a6o|HC?^Zs$9lZuhqR)5(_0VnW_ zb*$3Kqd_H7uAgZZuX|bti78z}$NYtUvp-pF#V!>HVdGBd6t5tm)evN@;WE>?iR*kW zM&g?zB~|Y^?gq`&mUaX9)i`$FYaG2?%`m%LXz|uwl7&reb12${YciA-7|=1jwaE%P zN;G^(SypfEKaVo4hjgTWm8Aawg6Ti(O~p2zF6RD3OBvO>GrTJess5tHxkW;mCBUTa zQ9ULI_{9GJaxszH;$mZ79Cx;~{6eO7!<<}Zvl6-X?}j~wh2A|?DsrpWEVrAvB96|% zuC1)dZo#T#4>3Qkbbn-2GnqK;$E(PG<*f1ibMBe8KD9r+Wd1ppahulHvpY)c%+;<; z6@M9&;%|ZSYkA&w9*Z&W2MxQ8tq*Mp=!`BoRXsvOjI5P)64zOH#=`6BiiPLw`)BdqG|fO&O)Asz1TD#t8HQ97mw6o)&BHC)+#4ExZxv5`t9Q51 z;>dca0NX-SG5~`Xn`wh1sZ`9JtL2+(R0hejyJNngV`-kC33P~?-b)bZrEvUKdis*5 z+s7p_NywJ=Qr@*I4V4i-q#ZwWKgD@ib`G8(}6%a7)J!>MY zxYbqLQritwDYsIj37tv#h{^H1a5xt@Ewhqym-~%t9pI-|8(>zuP9s}GLbX$h#JG)s zl!4_kI((wvjUp)a4vQK9`33!)?yZnam$r`#VJ*49#~tdkt9r!)(mBf2dP}` z^3ushjRJ4(xs5w}sp%Wk1Lgui%fcCbThUHOWm8g;tmPF*QiRl_*q+d*Oz#CL^sFc? zMaHVFMQbZi{voV@FoP*vjg4` z8u3ixqs}f{9Bn@p>Tk5XJd@qX?+vOuWUIBgjx#}PZ7MINB??H@Bf!i1;+LJ}P<=RS zMyuI8PqR02&luLB=T(psl`zvJUbXt}RA{s?gyNF(cUXzb)k_6N=^9Z2dVqLjw7953*W~c^# zm{HXsO`$2=0n};J#wW1&hXJamX7kK@v{7pJm$OY$KbRCr>*1_hY^_Le3Z0S44TPyF z!~FA*V@$MdPi)z=AcA)jDBQTzOfY~~fjgm%GGrYmjZ}onJ?3(4eVwZ%U3fHYrbz@1 z-eV&(tV-o)WDy;qQ#kJuH}{NeodRL2vb5ixa_L&|eyXBH#gZcmy_T{Pze9NTIkvW~ zjC27fZWk&=G`&RSIZhdCs-}x|NJ^W7C2Ck$NbRVBrX3UQZBnZl$eO1ceO;t}St>++ z5OfBGLu73uk+g8ANB7*7$C}yd?^jW&kkjH!X}o9`5WL&m*kQJa>;NMyQy{3M0oLkN z%C^tSqLVUvM#*6&*3oz;mBMF`{wT`zf3Chhgj7{2^nwqWMD+Q=GM{rr1!ra<74b$E z_kTp!s@kMbCX@2k7`(@aL`T69XOMd?8t)s zY2~)gs+g9a$+*C-Td;#KyPAa+`<+W0~$g7-|(uXS}5Aw`CW9DqK(_bio_C z#qRgj9*R2*-H#sx`|)2gTkTX$hh9RAfeHTrTw|H6W1jladJm6g7|7{V7nC`|-&siO zOg@zZDt$RpN10%~tZOw0bi9UTb>8xoHyJ)9Xj?D+Csae2T zeY84+yz)nF@>p4TxTZZ@`t!~VoD^Iu^1*ZWUJiIDw%_{gF}arWW!$!eskr2@e6C#iYjCriEg##R^`0vP}k}k-#&xRK2aYG&1{IQ ztS)p~E}cD@XBfUofZLj;aVrlro##mnHudioOTPjXZy0+WRyjitx_!QigN|}5ot^Zd zh&tOEZ4qcL*GfDPSrjN<*xGf`_8n2)i$;H57QaowHj3VQ0NeOHJQJ|Dg; zws!3eRm_Csxmp(k<5X6{U135~kW}ZdUeH8iRT|e(COzU*)HjD*{Uz-vX+~mWs!p)o z{{YI0viZeTG4S3Ie#|d!)ICz^XV)PjW{siO-d zs1(U$5=owuluz48Hw9F?G-9_t5>KIvGSY;~QlB8xwBpkb%Bm8&j@3N&RzsEOP)GxK z(jGySg!iL?RA(F+eX>!qRZ~#Hiv=^7R^IZILUKDle$=#eQyg~eTipVBDD(Ek!mikl z^lLrkqAMv~Uh=7u48WeS=MT1YFwx7Ly6QP@D%E)Y=v&)_+j<}rqfWCsM_9JcKoxF3 zOM<7ott<7EkgW=pBjc=A?hQ(?MuFHV3Y%FyPtUo9H=5=spIUsw|0&{Qv^`GTtp##KRYyw!rSzG@{B?WpbYh-X}YRgTul z#Dn0WPbgmvZcMs_D{@evQS$2*jJ?4U3N1?sZ&+wk#lswi)icdeys(oo)XGvlqF)<+ z(kQCA{5fGJnkg}zxx;n^i)UFUm*KP#wrSQFepDL~KH; zbxQ?%rnLdDNQ<>?RF&jjl#S-srDdpBQ?XM_{t?`({R!?e!|w{Jncw4DI^is zv>h(3ss`RlJSOIw-Ost648wovhaTqXv2pD!ZB2E$+9?@v#_B58Ty%#QtdvaU6+5m? zeQW8A&?~H56BCnnyE{pfPah#c;GIqn`;~pdX)m4M2WBb0oO=-M=X08oCw z6CYcUDUdcDMw0Q2&C*n@j@~MSNN=vM_<>m?%L)!BqI+nfmEPYyy7NarRXUzL#aybj zQK)}X$|^~xw=ke5w7O@xQtX-hWfFF{m&#RNBgmt-Bz03p{r0fsZcVg*lP%nXX`{Tn zkNMP~y7C)bvip0Dv;og$8_`A7p?b1e&RI2u$_NlaS#mbj6=1NvFOqSNPJ3(#RJAE3` zt-)&C4)qU=TyR`zv5;M=%Xz19WVvZ%dIaEm;rF>?_VH`9;#j4kk1D&~9sNFOjJ)cp z+j7hkvDBf~K`lB&_8E1F{N`jp14l(t;(SQoA94Lv5_0}gRhie*X54p>t1`fDA!=70 zWNe^a@!+FB(=k}d{%dm4Dw+A1qg~%Q=Dk5$#ybxXVU5FUhmV>q!#IVk!;Wh?y9*Zz z8{Qjv7MzQVo@F%!reAAvE8BTRDkvvO8NoR0Baf_jpmKTkLjM3^#1ZDMPwk`Z?~?t@ zWh}J)6P8?B$V0gXQsaAAX$eqLf(bI+AbwG57pC2maaDP&h1{NzqHzA^KH%*V2}t<5?h4kXM3>j62$9T#Y{6wj|vW1j6-0a$ucnF^Ubp}@+8^-zWzR-1@Q1otTG zA>Tm_4H8}|QcoDG4djJL8lWS{G<4Dm<$@swV$w5LClOivvtrLsxCyZ>|FU+?pE+xGp$$oK)dYk=> ztg6{s2k33|le~prV zIuBwX%WI0DFD$kigqP%=JGEyw3`*-;ajLh}VikX?qi&sPLS8b}4on%O!!p9x8u+KJ zZ{EnuDWOLsB`PLc0HHu$zditI`yhsjAw9iNgtn4A(vj)m4f~p+o6#okcCXbosR1!F zG1sIz3Q#mn4dWf$Nc88syt+qbk4+QM$U#(!&*uZ<8B|AA=@~jDN*)a~0Fn6pB1>=q zLA`~}K}*wDSZ1m3Z$ur`>o`k-d4(!5i#V&O3tCA?=pd*dtCSXRCGE0-JL{#Uwp(3w z(ISMBKM0=o+Sb&n1XDhFfC@ESY7J#`?|+R)Z_0W_O72ba61ICty|{o%D=mS_l8083 z2Af1XZ)i$}$>oDI3Ej2Y>KaN=+KN&KLDDSHTaYwVE6c_^Qk896D)s_Yegmu)$c+>f zmyy*Y@)WqiLR>PL)2s(Ep;jD003|@$zYZED&M9qGZ2}cDDGfd`GiMg&s-|}L+LZ{p zV@gm_Aj`L79k{g=P1t6@O#RA&+2qBw^rV_<Ji+CL&&4DyZ+t9*Y)hD9B0qeK%>kX2gub`zhI}NG5 zM^UUD6GDWVQ&sbJswJ$IGy(YyW1iS0HtX2{>W&(>P@`}1^&cjz7mB>@kQ8}@KGbRO$v}y;0aOCN&$J*OEev*?TWg6 zsX7n_q6le0Iuk@_)kQf>fC5t19xkX%{UfTk8;vVq6%aQ3VV6FMe@5fcVCC+e07*eJ z?=d)WqN#1Oat5p8V^30N#t>YHix}@Uuln*_Pv-LjqFYG!z##6_$J>&Br!{=` zMBq+>L&Q(}T|-s(y8Dcw~1^&gDk?^z3?cvC_I(jarSWi4XuXI=|`0^BEYuJ8N*N?M)^7f++4W= z(@QX?Qb`(WA-7~$G)2dTl=viVPb%8mB`J9(U3yG@5HZOxBctOIxf0ZJts;WGFFL$u z(h)9Irj$f8ioA71&vJ8$GXRh9p0J#bDa(9|ijA)q8k$6t?*L+TYmHtJT88fd+x z$uCzIz^Szjq9 z`h%_dmDJ0tWgeQ2q#m9A5Y!xV9UE%tKWd;|_dlClPFWlzG#xZXZgKwr+v?87`mRT5 z^CDJ-R;F|MNNWTb@eCTf_M%yt=hqqt-TIM5CTaHJ#+ACF@{5sd{#32!4xjFrLvlD) zc-0X%9PSm!rq>GoDB-Vq-*2o5^goVZf7UA4kwsVZ?^RpAZ;0c$Mj@RUR74$*=NCiQ zA#dV;;)2}%@O)onZ7ktDOt&`Zge{t8fHTviMo-P{S^Tyu4WHW1T2cA)(Ec?*s($nt zdU5*t%NG?e!YNXB0^kWiBSNi`jvjX4!b4MI?OdCt$0fxp`0AmiW%qRVH<;D9oO+gm z&e@ntOf;vQ23^2cQ59LcgL?Dm0c;rGwH`lgz-}Jre!=@wS5@yT-qvsBX8m$Uu1N-x zf!zV<0ZLRMlgP6(iVxTn*zGq4us*%If0@B~LifL{qOld!H*C$?xq_Fe*{DGs^pj#Z z+f3A+FHFw<-oV|ZkdgS;_Y}5s+n2v?MQ9v`n=HB0$Zr0jSy0d;w9>UCe#ns=XE8Vd z#lU<@fgQu(SoeNQBM0Eh{?ZY(C)Tn;4 zQTHEmxM+&@=1KyHpY96s zh!8{>Y&;q@l}sMo^E=^NiMEd_{{VtN z_Yv+-0_Tnjwc9Xm9HHYo203;^iLGI_MO({LMIkFKBzbk2nTr&+F;8Y*#?U2k4j*-i z%Iub1?jxsyhxlFjyCy$zVTQ`zsd$yB38qpKnU3Huc121nPbSA1?!hNO64t?ZezoSx zhQTmF0P134mrmbBS;$z11=dH}=He}0!o9(*ME?NHvs$?xk`&69?GY24HI}O7!^Z0u zGtF`h$(gHJ7E5_T=@Uqns_Ie;atR?X5DEMvIm+HvN-JIu741dVs}3ftALeFTZ_b5& zRnNTOdk{>DX7(B~g-?b<@1=s2DQQY3qB?Z=MdsbwDkadPlhwtmryLLN%?WeTcCQvK zsju-5T#^TgiRb?SB&hz9fU1e69Vk+1(quH3!YLtvT`52U*JiX|vU>GfFpG}eHu5tH^z_0{~uNdbK2A$vV{i|i+{{U?q zyE9K}+06&V{*nFX_9p)Tm^foU_W7FBanB}E(%Z6%W}AIX($uE0g|g`KsScqj2f*nX zpJ#iwi{_ZIe_%<7nrbLkY2Gf@WH^3T=r3vR?GDpXL-C+ZMy%8! z3ILL?@%*Aetf)dr4N!*NONvNrO+Otv!E7`@T{fXC@zO=o-d3VQR)VyS{UXDSKA@=? z=2kMFI?I%uX&}ih%3q9Kn;{KZIJfoHb}Rn?vx|Civ)h-r`Ue-dP~?rNvEUbDXvdg) z#RvM9pD@2ce%bCx$H)(X{@_a{Q#(=VG~sq?byH~|_Z{GlHH63AO5;AQ9Ti8oJeNCX3~aRYmvuBrJ62=u z-xsBRlGDd@Rh3F4ny1jJw*ro`O5W-RXwogB)T7N82l--_6?pKE?M(jeRD%!VM@0S7yrg6khb15<(vNM=$=aBR(M!U<~MWsS~ICB@c(Q+Mz zq*9p&Q@>B7Ubvni?70phP&uC>iS{lPRvDDz3(B&)6)no8EZfjf9;fcGPJoEh;KZ`( z><9E%K5LUnX^U};dZ>-Fk8hZjx}ut^8>UMyx9P2ADcS*?$nO?BpA5ER!=r5|vg{vo zuZ$(LKX59=I7;h}Cje@bC3yf+Fk39HQY0o_2ht?lX+6W*!SaQYWHXW3wnq=<6QYHj z%U54%uC8|mWu`64T9o>dgCL@M{b4+#7c_JcbXwzw&Q`Qh$5n30@yD6BuxOJR&m0Sd#^+O8dy-Y>41?vK?&1&kqNmIe z)>ii{px9FtM}sicTY<`yxPpHnN!@F7e}+nRi(4OSg&r>I-CH7E^(sx;~N z!-dQ`)ey^xdTk)2ZcTNvT8V9^(4yZ-4zlUeBW|vm#WRn_%^|5xbw*Q5aCw%SXok?@ zO3uHOE2l`0c0eAgYX1Nh_FBH4OQs)dw|h%i<}&E2O&v3~wwt9{kVsORPLnZy$;S8C zvC+46DWd#-fI$x?oLxI+?c%=@R@}DbMU2;6YpbrPeJ!nOOQ6X+N!&htV(n(&VGoni z09j0Oaf5rkY z6;tusQ=)Hj+@+REf)}QuLIa*kl_MNPCSvJwdg4|RN|Ji3`{xnzF-tN^B#lROZJ5 zAC)0TO=BsQz?JLB+CxA`Ny#f5KuX;tFRr}=b&-!0pb16JUEd{SxKDwcE$?j-z^}n> zqP9zt`?-GiN=JfUTkb}h zoCUAUdq$Mopw6qzcQyuB=n;h_<+olFUcDhPUbV<8yi3^LiA=leboAOeIgc1S>Y4_s z4h&P@FGU-<1127}*44Uw=ip<_y}}HVLcdG?-Qb$q$X2w`Ys7Pwl9pI`QdWXz_=yqZ zzEj7YQsBMs#yJCA(AN~^UaB^{!+++x{u%@uL;XSJ+` z-&a*}w$Uw3`IJE1Wv^f(P{i{XG709+~yd^byiCrCp7G>6}<5$7TqYRDId7Hvf{!f ztw0l$X}mh~c?ZE_oyozLw1MEIZI3Z?jcyFJR^d$plRmiUk>9wDo%YQv1oU1ECUQh7#dSR3llrSB1Yu%*IVeY(VY zMRF2Xk6D0XS0Tll85{Ia_+DLguj#E{^L?wKV%{0$_zAMlG1z$s#ldMM=(JVYrCbs;vJ2aT!;+{#45;ux!hk zF4tTMhfmCup2ntS^I^s-)c7x)}*-c|Gy-qx^+|+$iavFE?7!y+oww znU91Wdy%EFYGT^d%R5|pf~kkuB(kPY!6Z0*|7dKkmveGa7|TwFbJVHqK{JX}nd@)E%V?ATDwehU2^qG$k7BcyQ4O zzh=mKo@j8al*}3b0DK}_s)R+orqU8~2f9CUzp%?*bvm$?#0~u4% zNUw1I#yR(&IC|{H0YHjcmnoE{9;gn4k`cN@wloGM@A&Mq+m^M!IE6vRa<GpqB<_@B7Xik77o%~TTW_yLEqKD+$t>FG)M*@Tf{{H>PSJ0)Yqsm6 zqh&55FEmzd?Y^S&q?Fu3l2A_M8P826tQ`i1iW#n|4etTqaSF!gGN#Vs;iM|;Xo|Aw_{Ex0xJ^SqbkpMxsL=CN^yXIDdi-hDpF&iHHUUh# zl*$_}PR-y`C&4yKYi}XarjQ25t00g79YK^66KH^u<<3*T-8-AG`*<3isnF}v4kDp+ zx{eB`@+uU%=~9&BB)u2SKEz{jl3vK!`M|ZSxcrR1j69`giLQ zlhsSbs)Qcr@&@df%~5ghPDobHf?`DL0N%;AG@DCzK2=2722XIiUn^XqT9vs+p_tV6 zoKfYS#o8^J*2XyE8|EA8IkZNCUGA+t#Tnog*UmWk4cMqr7!|@ms1m$Vvbb^2c)Yw_P20NPY;F) z=1|y|{uq|BccoYEm_yT5l?}IQluBSIkJ|*2xZbHZ`F;r>wmwVuRDI^xLTvR42cl0% z8u{LYemkmzxRubea(Zf+fSgu-4M=8tbs?-OU-Thsq;*p}zuKxfCBKuYp<^TrD z6>wD#)7;#in^t;!3}zJDEq*J1i`5V4QwxkQP1I2^*mMAA$HCtX~H zD2+;i>Eju=gt^pPJZ_+==%&}Q{{U*&4{*6tcPxR2Z5(4{bteA+d6vUgq@CZUV5_Mz z^DD5^tXuIoO6pd7%(+V~#>N_wx@@%CalBs$q`)}lmAp#BMA&MAl94EFDFHiXt(ua1 zn79izv!csQ^phRM74g3pU2(jKWHmW+uecpc^GNj6pbn@ef3_N#g!EE&o`+RQyuUrV z<~3iTqe!NtNBItJB_(xO{Jec+%pQs?w{X+fo`zNDe(v zQe+ZFvV-KfZMl2~Z$7bSa*0D_B$On41ZNos zKH=rd&^XI@tfCAEuK~j9a{QYM!tD!jmTeoAo?MdJlG0pla}5P_+^K#M%ypBTLU%2Z zY;coZS4w`xeT3h6!uFbbU8#828@h%K{_^8wHseg{Q8OT=OQiZngLiiwtOLztmblq6 z7d7w-U;FF#hs7S#INqz;7qkRYQeWMsT@H1b+xCRpP@%QfTX7$rp~;=bepsfy;nRAa zwCL<(gl%Hx?xkhLEp8~O#hJL_)RIieC&ZHjlTe>yXb=^qb2I4?==PwBY?4$Y2ZVO3 zm7)q-oXMWBoq=|%#@f_NQhFv9(L_0YP&TrBO^lwG40j==8Es`cs_F62;5BBQ21;R0T39Se5O61w;FI4o-TmqQ5(rx`6*^*IG$sGt#*XBAHNc5*j*IiPIj`Lc=(`?8B zR_{tn%48aU!*}>s`{8URskcT7FjHhKf|*JkDV?ipNe9dnPl%2#fV}-VDo{Fu(mLWu zNeC0RMygf2WYy6kUOl6`h!yZe4aCqcbewO}t9ifu#>@) zze2+1rw5z&Lpe*|O%#_F!}|A>-s84cE;T7}Nt;PJpC0iSXKY+*RkJaY5VC`+eJ!?h zG=zUILD2jn!yT{zW;dAv;+J;n6aha@W3)@h52aPFE{ag4w$OqKW@QpAj)6rPNhyua zUrH*{5;pm|ePXweeIB(AGZ`@_I{>Z)Kzn#c3+S49+>Iw3 z_Y-bgkf1b|SmR9!ZNY6FVb}Cclq5o!>ZTU-u4kmB3tTI52~L1a9;QA_5{0xTN6be+ zs%`xc}3$n=!Z6q4zS`C?pq26gm8+ln&cl(RXYA&kliX%<-U}1-BCk&bOTs< zuAotmj->-R6)STaz$3JEw@eA~hkKR>Fr;h(Ry=3*_!v*tvWZ{q=^T0>>d&dsFogg< zaq!8sc&FRNUV4S8e2Ch03IGnCFw^qlnkSjW&yozjOxdDL{-RJDvVJJ2_;;E*wDJn7 zmXwu9ldK;F`EWws@xc&IH3i9aZEh<4{lpy+^Hn+G40RT|g{>|nN!0wOA384-RAsb6 zSbn`?Xklc0jHet8@knrDG-a#f#01kgW$+>tUQa~Hk}=|pTd`y>=!#X8_Lx0mt?0h` zwlpEE;dv!d8>v)B))noQ&#j3cQ}J$5QYYxFO!!J3FgbDAR*w{=py5mFq!oos^-qK$ z+ln0JbxV341+H3BrB86`4Ey~ddlY%exagIXe%=tUlIlu{5|=?7V+9f&cJfi}C$`Ep zs%t`tRJ7~T5E|5{&g%6_IzMeTbjYroc^!KX*$6h^rX6(i;+1qB-7F|0{zwtvb^XE_ zYyol7%TB4nweVIuND!~kwoiXA`l3i%gdvgTFYP>KVz(^{YLP9iJroi;6Y_%NV_I}U zOiP6dc5QTY_V)ITH0u*E-B2pE)+vS6q9w{&W%^e_mn4v8bm`%&CD%pTAsdQlb!d(K zpYZ*+CFE=E$yAA`bq*mYa#NB_Gi_+n;#YDP(i%n;zGfcX?2lqqZ97DipG!azCDXjb z9T0BnD6EDl;w{UN{{Xm$5>s)9C8p~^PAJ&8xU{LX$OR}KxRXA zZtzg&7OWL2VD9|KzilEK8A8%8Afl00nxMrX0aBROBpUFm@-!wlk*?J!E+GyXYcW3v zioB4k4ldoKYNOR%ZENc#PalB)0Jb5Fg<9DI8{mr+r%L?Jd?n)uMKlU%jOiNl1s!9M zwxu;Rx=|a1q;%`y2dLK+q_F}YPP=4!h<4Sqa*4>FLVKtde?_;cmtUk?;?xD7U5{FC z@A4HWkerl1d;@dyD;7WGR#ELV59##iAMB8Rkv)$26$3N;@}>7Clm1wJ!K#v#fIMcH zk-HUQfwJ`6QBq!9Czn>p^mS4n0;tR`fcI#(O9t(PCxZuWNd+^Il(d$SA!-VXyLxtN zKj0(j5+y`0Eiy~$+^Njz1>2O(+&+`|E`>hY1s~AY{7Opi+myh30YS!uatxWfyjMy|W@_NTm2Z~tGeYgF@-2B%HD}AXe6AE&*D?fq+b(}i& z-T;1-71!L)9)NmxAJP8+6yRBY)jWp!gV&7e)j-)l%tL;wtNo%-b31!EHFJPfEB^qz z3jiq;E^+vpe{ysvKGxVJCdK5uvg(wX1+`AoE&Ty9qAka(SJ}r**4MFhM^zQPLx?IX*feNZ2&^E6zKyT)zFay0|0pcl=XNkp0oyQj{?M z4YgY&KbEzjwusYB8brR47biP&K1M6mhuawc38h0w8G*rtW36%c; zG7_l)4zn?sxfK&E~pZ+pnUt7A8tn7*y#Td}v zzyO7&m`yqveItS`QVi&IH8=zvy7oFmgi)cLI+pnI>JPU|(oo4QruRw~)6+<^Z8%AZ~|#;~hT30yAO9on9sC8~XDrL`?cE-jt9C=)qF%L`*|9gg=O zQpovsLnA(XHYT5F0jg*$704CNigFWA>VGS~lkpJ>*=sJH6~iS75slqfS3hkh9j*2$ z&UC3}C6#oE`My8;wfAeEF%5U38tuGMc zwv`oc23|VM&a}Dhb$lKs#Zyk_Uf!0)HO{^mfA7_CrOE&JR;8wx+-L`xVbsJ zS&m(@4j%U=)uMj8Sn*oSyrBtjsHsXiBnfqkR06inK1vqr{;91VDl_|Xd)TJWAJN{wD&kkh(r0Yy0r{S=u=DO3Ldkf;wR>8yFjy!?wfXM&JoVlv&kM*KcM zp?V9p98Vc}lVh?|2(8doN-*O>Hf|g78JvF=?l2f|LBcj8j?ZRZmWj zNx4)lwu*#?r0d>4BCfv6`?1V!ee9jtfv)h~uj;Kr{U5-6!1m{g!RV7f$iQ8V-;y9i$wm9g-aW}z!Y})NVCGjmBG-S5O;aDIO(pX~#fW>Uf}8S1aV!(G87o zbG6dGUJ7G-h4wzLZn$;I{m35Tuy?rSJ(uV_8207mkz0&em zA>?1t>Q!p~n{076Lc-Haxk^$@ohb<_nCJ}Qz9j7)6qj(zM3Ds-TE0JSeTN&8{{Whj zsfqOJAeYVRP@)#Onk?^EdWyFzob-bV3YrQ;fCfjWj4Z!;YTnL;XkzHMZy3$ZrIy1% zP1e%O{v^iTVVNFkq+o+XqBU_1ex#`RX0mlZAB+s)uBhh1;5sgkfA(-|Q1?IkpJk*2 zvyVcQy+4MnU=2KH68Skl)5JgLAG|1Ac*uOU{{SidfT%;mbx$5*96A zl9EXRTEv8N+r~X0C_9W@+~%@5#a!#mwR8J;@%2JL3##>&i4AA!OTsfujmehq{1*QJ z$E-!Z6@fxKZ~Z%NjGjRaxkAkn#XBlUKBLM=NJt&xTiOq36jVIL`=f5=^-;#b#}v6e z<#bq%Hmdd4KA}R(EtL`!xg{YW8O4Vkgxog<(0Q!CEz6$=^B!y9JXM8pPuh#!4piDQ z_AmpDTWOLWOSPml>f8L%LJ|okUNV8*$uO3ah8afx03fUoajs92u`@?*@euKLpQH}I zu8KbUfaA;d1Zy3qWo=(^$5e`h9SBorY3uQTxlU#FQwgK#)mr#I3Otp$x13P)P!_Y@ zHezZWw!YCz?JS@HsoZpyu@iCL%a{5o)m*Lq+3aI(@@f>`<2#;TeZkXHQ|cjaG*;OP zmV;5J(^$~B-z=CwUQbZ59#`6n7T$)mc&PQB@|I0PtA&b5Y`U$gbeZa%HH@o~cNX>1 zmIA#A!h1oAw}eO=s*|49>6>dBRZU`)!)vsT~6LvtqGGZfjc(L9(<&t-%jrLa zSMFpb)K+UNY6_yNM^>bj%5(hC*3wa4BE}W85N)yz4l0tY9o*k5;GpNRXIwMq!5`MB@}us`R$@sH)wm)+v%V8r4MeRV3ZNdL(QdLLo$0u zHw3ta5Jb{E%F?e)nQJeUb|=x*37ert(uvEmQo8dfn$SSRg7YG98;3*scmUP>Ujh!Ky)Lak2qy6E-~_JRlL|i1?wslNB0Bl zs)Qn{?QNh+cY>f2G?&gVJZrd>A+FI?^E?dgE^4S=MdSKxI<~HwqMRV0p1vdJ8~!5T zTt~Pp&g}g=wz&}Ebe1PnKTr@tNZd3!ogJnAg_@@rvS_ZexJB*lzsc(y>$ecOKbS(&4 z8auTFPE(5Tcm#s2k;hO9q@Q*;imq8xwJlH6L#mRb03vWL%wffKvur9m+1#nds@Pf* zS_zk!5+x{YkroB?q(~E+eayw;#Pc-|mll)|K_tkLC`7Z8+9B<&!(IqbhZD=c$b6dB zV^Y~t@GCQviSA;l6TPWwt#=W7k*jw~#;tbQQUMz<84OL1@eSD~! zUV7o$x)(*x)WJi56{`vV{{QNc?9Fa^_u$!Hk+I?%7L^n$69a4a%;){c%SXy^+^lG0Hl2-(@Z- zbqGo&xa-spq*$yA-v}@2sG00^Fx;PKKuYF^-CkvFw%RK!EN%4{xd=!if8mgnRrwXe zX|#1#PB$3`a;K}u8P{9y*36c+jHZ>>!rdlYK->)ViX1rc?(Qp7k2hF$g>2jnMT1{4 zwmo&Sx7-pbDo%t6%2U=X&W)gGsXjnx7AqBosl`z3LKbF{2$#kJEuqmF27yE` zch@GJ#wwE9f{&)406$1({{U819bkVjD#qdrk-9smq*}HWx+g6q+9Pb73-wkzrWUnY zZY5VsRYO4NSW!D?uSl6OP=c%TOS~0jJSyu7u(D7$0+Au`mXSST32LR}0NkWafy6gZ z5~)o@AKUPWZK@7HojiqP*c2zbYI^-3cgB|CqHOqB1ISdRNAkD~{%ZdKR5=nFX!WX? zUg#X2D{t4J>EdMu9*eB?{)Jif)Zy|$0GXKxjx8liMs)}RY5fqSr(NAtA-`&Y7w=Uw zxr)6yg%8asnaU%k6;drFfFCtY{{A!LZ$fP8bdS`9Bx|A5;S}CQF^#$_#}LQ+vQ*z(tWpZt*aP=uyHj@>#$24=vny!#P1>wVLvhz%7?5jfgrY~S^zI;NJHiV3iaT?A-k8x~$+AVt@emav=Wk0(ZE6EaelcXm!sZ1;;3^fEuaC_Tl1I`2*a=t0})U1~J1n`pla4>i+;BcAcd+ zN|QY%R!JWyxc2WG?&JgA(SoZq?-qF4;(eByI#LJd@&4stSjQi84sFO+o?xWn%jQpR zyHvebOH!IQQiC;++Jcq_g?D1dQcad**n{96_U6{o2 zww4^Nv{AS!E8Ph{kSQZ-H+MSe(i*hk@7-4-vR%`RFtRbJ@mC{+;}%Xk$E{d)8*N3Z z!)RZ+!RZkdoem~qSsQrb-u!(eXZ za&e{_1whMkwUR`-n2Md@s>5$uAUwy(mM$U0E1Xqw8>|f}Z`73y!^=G-@`ORPj4mmw z*1AUqGpgjH-IqM%JpIU(bhMQYKWe6NNTp~M6#i*$Aa$Igmz24D^6ecKo5k?Ww1?bx z%#=JEz!%$tOt)>ey7kt)ZDqEaTh6I!9TJp+XA-!)RYhH*MrSr_>C~&@_M`Tiv*H-A z<==5tY;ivt(JWWpP0MXHRbU=--Pp_`nQya!}jxqWMnxVdimbZQ&!&+d7S zWB64^x1VKJpYj(TQ6+SjEb*rOD%!#MTr|_wDOmn9uY^~c=RM`m)T;yy5w~jv0-`IG zO-+46)s>E}qNbs1ThF+qDaRJIZc>z^sU+%2kEBu!=!t?z3LSTMglfJISShrqZUg55 zqX9vSB%YG_LjAJ3<5j4(K_X-24u*uC!3BBdDVE!AK7_jHn@p7_dVuOc>o24!v`KtV zjkTtQCFwB?LW?iB@&z@Kgk|?iWKvb+1QkB2WE~p>pDKK1*SsUzV0Pd&PMi5mVTAfd zwD3pIzv>B{eo@d8QfBK$in@0ZKQxZr0qp`j=!<|Bl?k~Xmnq}Bitl#j-MZKmvbEcH zs3oFxI|1oG2#=ZDPO5I+5b;6PIJ{||;vA1|^r@apR|Ww6RSM)DZCxcUvGg-E^g!I$ zNg35ULW$Z1B_##Mx}{x+_{UrV8MQ`fVVdSt)9s~gDsw>{VITydP=baktzjrTs%QlL zHP3R}_#cEhxPx`lmi`MO ze6aCJ168PQCSS5RoHU|QS*=wS?}VWQ%Tc5e3}lrNk-lV2sZIr3{{Ycw&!}YJOn~VH zO8)?Ds-tfg)XAqr*de;Sg60yGm8rv`d;~^-WqQ0h@!CS9Ml*^E8dgDQ%_Ch0ShZT( z?P|%h8M7j5qR<-w3NL(x8+4I+&Kn$SG2)T$bE9>A_XY z)BtXo@QD|`s-yH&O!i=cZY6zpbO*4C&OmzBiO1dfG)cC4AEY#;rn zCRXH*k{Wl8(Kbrlks;GqB{oXP12Lq0l1dho^wUUC2y#~Br1t3wNZhxe5D5POO=F*F{%daA~Ad zxvYaZqv2TJlXN59{WF2)# zA1HKTWp|1l+~(Q60#VU>d%HjX02-;lpYWXwEQsE)!f5$gmbTw*g>EO+E-HUM zpIDG)5urp&+zFka3Er{$X`x`Wy^19=o{=F2J~jmvamel^2B$>sS-3))l3&USmuiM2Vl}14#HqRl?7T zT~To@sR;y94^ce~he>>4kr4w#Ep^o@DR{boB|9k)4|$|cUxYr!k=1m^tx>}~cS6XL z8A@f>K@K}aw(5;q!KqUzG(5V>P|KttCGF80Qeu>?r_9<$x(R=DC_Is>Xho$_^A;*r zlOobb$ajWKQEl5qHM?^OmeJFuf>5a2trJc)2xBzNCVoNNSbt~uVBLhV8nviLIGo`3<; zEI5f9BXm@&>th|j+!fzvcN#`uVI` z6g2XuA>5d&b7#|P(7PREL3t{$@HMQKR!q*3P=JCKyOm(vq>V1*(j@qUFTy3FRcBE# zyn?g6Q`BoXkGoUYv7%$1Gn|gP#35E@Ee(a0f7u@=Y7wk$TTaV-?ocq`XY)`FqX7ehghFKC|Ml8^+g2xk1aI#&D}y}L6f4wdkHUKCQU;Yi4 z>#05IBsF%lqD@@A{&US+N@|VjTY6{Z+ScNoXVk#5^82R^09H#sw;T+AF>{lDb@zqt z7Vd(FbA{7|$Zc&2M@?WkxjpN;m1DE)M;na)04l$UOQy7Sy~MTScz5J3YwfUsr&8lu z0r=4VfLByr+CIcw1%V4)=bJJoc~1BemDJEUKSOJ6QU_{UY?Ukn-Aa#1;X7lC$NHSp zEB-w{QvB2BA9p;DGV>j&ZKNm5S5xBS{nS>XeZG51;%r{3(`Ui#78a1lrzBHSGJueo zd6c2A#%46kOm`YJv9*zY0eOR%W-o4mb7hu$p8^YxtBMc0Ukve26Z?j#sNu`4C3Z2I zQ#o_ETPa;b?OSWq`3|&_)`2USQd8Sl^GA3vcD9%0(2zs5)4_haeV+47&RdHyxkfp& z#YXGYifDgAthP`UB6XDi0BmDXuUW`ZvrP%4nIr+MKIZD9+=R4Hlkg;&m{f96bg-#K ziDdKOjLR6)YnK`-geVD}$N&-eMwN}Z{{Y)dN1Dj;c43*;y!f>t=~Uy?NJ8FIh-i5? zD4zZhn|HW{BXZUUR0FW!soy|B^Y4z0ns&uChw7i&TOl#ex5ytpm64;~7eX>jV>+V)X0r%BpE zQknj4@*4Mwd^o8jd7x91@<9;Pr3JT#F7H>hO!@TG>xpay%R;j*onmqhA1_yQP_py( zJchTbcJ6-Hc|ys2rm!cBa!UHzns%zGl+A+dQtL0z(^^zm0HsoxGu9lB1b6Le2A>u6 z#~_uF7f1_7O>{q?9Z%y#^>Jt0@7;edU8*teGU03HSAMs%`kMT9mAX||F;BZ^sOv61 zg&{k%nt;|MU5~W4X6d923OdU2^7;E~v1wzSyXKHUc^nbN%=&yk?Lx|O8h1ydo90z+WiGgcE6U<%oe5>ND?_5RJ}@xl_OUvU z#sxf1sS=A$<&TBz~rj;Qgky`X%l zY0El_yMn7lNIHMrk2m+6esn*N_m%IDwx?Fk;D2s_E?2UTaJYXQ@&W3;))Z|N#+ zaMvT|oUO+cxqfR)ZKoA1y-#BRB0$~0ptW1sx6?6CPVAF3Hc3*CLR3kVMCEcaTr|9e zC5FS>*vKPcC*q=Cxrb|N@;V9?-)fDd*gv z8yVDup~SFU{{SqjVOZWVU3HSCg|x!<4QKh)B*cxb9`fb%jfAKi-*GUQ#*;j)X&x0) zgB|vl$oEH3{##JSILpR5kRs?o;u` zGUoE*WG0%Tueg0)YmMi4^LeIB#!Fg;lx@nVYtU)7M2P`2Gand@;yi($G-8OJ$nxmNT(0_*M2KWai$@kf~=}MCil#IorH15K0}qamkVQZh(l=# znNp1U03zFBBWJC`plY(5tf8&i9075luSM1;wY~@TMeIL|ZTZ(M9Q~NNC>7#K3&Zqd5QUf6P#PKwpMNuUP%Zl#-g)Y_?! zr#AI6Pwb7-((%l-)5&?KC5mj9yN{ZztnRhzjz?Eg<0YV9Go0lu(Uat+E}W9LEN!b< zY5-Odz^x*i1r8-3&9k)l5)4~$PNYh?eFx_1q4y-!nvB}Cl1WOH)N9m;t<3MvD?o=! zaDmg%)YLKp5Twj&F7YR}tx*$PQQcAHj>la=P={4kw5ocb6#)&l-a7P6Nt2gtDpUK9 zQ>?eW)cTz!OuQ!#_$Xs3bLvu=kiu3}B_Mn!9dw!_qb-lrtb1)0)yGoFPzeK50p3yBeiOYO>lS(SSOEw`Lbx8Y$QJrI< z&*_;(b+sw{iD~5k6XDhYzwI9%x~t~4W1M_QRz5%X6Uyvc?OWZ*)((Ia?{R)nljN4= zzmZ-ZxxJ^I<*8}_{{XynhVcAf9f+$fV-b&nrgQFhn9j+*_D4wXGL~5uJl)2asGY>? zpa6+>j^<|+UvPGYyP_tvW6d_wTTF({6q!zOSof;7BN-tc{{Tj1Awy^r=?M149U{Ndtr(HUHJ}~v`Nl@5#F|SBlJ%yTjrv{0UI|RC(kPHlSN}9>Bl8%XW zQE8-IJk&z8J)%lW$9r^Dds`;^;;KGh>~z1ac3|xU?fiZbon+@F>(Q798yp}cd)5|| zNdjr;0F*n&qmf^}Tlj zj-5<^KLG~AGvn2|A?_`qn0n1uwHanlTG~-pRO5*L5J~9m?_KbBv)fOiq zx?6vlO;Wa>SLRUD_ZSWwt;`RsZXlu`iMO0lz!0Q&5%88{%xGrk{Xtap)eG9tky_7* zkv=g$Z4CF+bgor=mP#updvP~aIqiRJ{K}Ssn)=Q_^X^M>X~PH4ejemJBOpSqR&~8aPN0!eq{>Ad7i`+q3v$XP z8E&1_I2N?bt5K%!fPrOg0%qLRNBiziQ*JM`mkLw2+)MAC?KA_zieFi^u@p`R#w=uLJGIO zPIBIHr>-?J4K|7Il=K36#g0b?s{PwF3av4(aXeDKTByVB7i3d7nXYRZD1{JHufi9C z%UjDSjOb9AoLuaLJ-USSa&>k!a-&_s#?l@IAxT;i0gw|CFuVB!Wc2tZY=D|NITw3$ z=N?C}Wg{N5<<*w^GW^fcdafk^6EZYZpo=@#%6&*Xn+C(IW{PNYUo$yRiBuZPik_%b zBrVmhWRjW8nZ&iz#BnI7<9xD;u5dFfrl*y8#@%3L(rFP@H^5ZVKv%*ti-}CvH1JxC zHf9TY^i!LgI0c$+D_wgw;!v6udT#8T$wY~m5ifm@+Z9{XC_HXZi<&s-RRrN&%bMUB zZDpy8qoIp+TH7oYt3;vQvXSu-o?B(56BawAR_T!Akjrc(XHpf*aVIf3m*dp6wT~$) zQc@JN*QBH(=*kgQoygseqG$cgUt6ok>XL^yD5T5-;vhVzI~0x{p5m-bJ<7InA5|mD zmjuX|p0QrD{$jY~9M5f8ZZD8jQrf7>mQblA&s{o2z2dA3JOqc8Yu?*#l@|ULQZNbzcI?{PLi$cyWi2{H?T^S7eU|{rQh(k503Fk1IEB@tPwEvkq7QIo3Clzz zWfyq;)1sJs6%26|lHrSU6BH%CcBu@5xoQPc@PXLAs*ara=tYh4lP#%HJ|vXN2EYDUQn;p8K&hW9 zyVg5-cAAY!TaGM%5;X*wc8s>_SD7dl<&3k9uvN-H@wXP=tL@6T+!Ygl3lk~J4v6$DX)l+Iu)U&bmq zZ0ylVwTnjL{8G;T*vTbYM~YQf`#8hjc~9dUj=@5Vnrf&eBp(7qzZeairT+lL#UVFt z?d$n+1dYjku4fo^Ah@IS*UMT6^=oMy3)-3fVv{2%dJN=J8gwWdz8yWd4yYj@vaFud z#dzgaRcokjp_a;(&~;CYKE{Ko=z?^`zJ~Q&)O*(VNqgk1hQAZXDwe7xxTI963(x|4 zN+5D~#ph#H-;QBKZTBSVj6I3_2IOCInRP9{7O|?SrxZV~glB&K~Sc1CH!1O^R-MNN^B(fdUDMyXT!iK$5o+KKBOLxqWUX$elF z{7@VEBAT=rxFtQHYYqEomcFSseX?8cRIgFi(}$XH5xQFHB?G;I!&K~2xu&WF@`ngz z7zNpR*BsQk-Bl^|Dkz%(C21W(yBYXIRPeezLbqe@nJC!w_i%4NS@{N$81_ZB(~{E- zl@yMUlBEp^O!~vHP8>ccJdQKl)w+cx9JZ;l7Qo!u)?!Cbq&+*rh4z+BG(nP+l_kjzK#-sR8c&Ideq4wBUc!hkscx#bG1aaGSq)&qYur}k|otD1i;rfZDR|ZbO`8R zFK}yml@&Qs^sK{`t@wSt(DkzuNL6r2GBqoaCSL$$8U40H>h3rH00=8lxW&58&u{S+ zM{01o>YY)VmPsZgcU9F^AkD3!F{fa@v?PBQSS_ro4rz9Th%8^PfO@#i#W&J`_}=a=t_tzXW1Rj=4r#Brc|;EV007lii~*~ zOqB3joZMp4721Avg9aNr%7(_0OW_PD-H?6+DX=$^7Vn+AEio3Ca6*xQJjZJPTdnO zctaXtN3kFdvWEjmLdhsbEYUu>1hS*_gSP3S5=(}U6fwJ1V*JUbSe}DJ3=PMiOgiy{ z#TYk?U0snB>K+DAuVPOCn08hn;*II>d(*;2t%JEB!D7|r2`*|Tqnd6xx;#K6q#n8- z?S}=MfI!K}SUeJnpNZ>KCgbXtaVY!fje4PEd{d|}pDgcO{Ss zF|{+&7xdu>ax#N^pM1p$E2z)}+*}IVSD8jcGrfG#qq@rMeBtB5Bf}IfU zyC9@s(Q*c$FhNNoZ-} z1S{L1OZQ8PHK(vrAyY#j_~<1Za1~Ati9t(iVJAk?0F<&w z6zCcdK`J4dv)CohPi|=g}KbufA6tC1IPoN}t!0vFWP|+ver{(r%tL!`gM<;a@sGW-2VUsl&|NUcAOP1H4il?0}Zrz=#A z0Yt!`%)oBU3aK`6Y0jy(x%hkq^= zRQ`<$1nf_4@gFVxuSdhjpksoZe{B9-&8*ywdG)T#Qz}vw>5@du`+VZh#jc)fQmLH( z0232SOGOt%x4vh--|Wg}s;xj26C|BA9pd*Kj&W2o69X4CpK|BZSAC^%!@bh!B}D7i zDZIU;fmNpVtWoZwf;@3dTn#Asgn%C%VyTZBtL|;yN=pVqG(dyfSe}l7Q(aP2apa1m zw&IM&*^*)+_C;&L+Ep-*+)$?!2T8<9cH9MhyQk#A` z3vTcVhNczn`KFFLM6s~k0_qY|s3uV(X(y>y@;20APu-!-)RQM!bos@;*}j^r(&!E8 zA$XVSQahChi=2AY#iLHuWPa3iq@*2y8bzBQ{Ix^Q{{T5m9#2>Q02=iP>_Vsi0EEP? z-jJ1C#yYnE{8cFB(iwGNl#-WKBlVPJ7Y_W=x7#Ssv=h67)ew{-T1r6v*pssgCikj# zDii#m0oEm&+9+|=V{b;+LHO(Qh1-<$zbX6|L|<(|{{Y6#Pso4pF+S~BvB!Va6h-$f z{{ZY->^e=C1pff;K(qes{^xXIEFK{{VU^AXUrJQA`^U{1${+ zTyRY&)O0ZEhKMI*!%CB!GwgHRUq3Y6Um9Q!T`jH|3(KoZ6w?M@opEBrcKaug6^Wjo zGtp%n%kDFcU?nkJ&H1DFS}NsaI3L&>-HuYlE4Pe>$%>cCX3MCmsZwSq`J0e2b#G+e z#_pDOwD}ExyMg{rAgFHXC<8OwQ)gsuStj<5x=~rUAKcxpZ#pqgfze9gP)mb> z_;eIiFvig%x35{9I!0gaFG$Er+q9K-nec}IO%bAbeZPB$&%LVhl?O5Ln-mpUg`%3O zl98*6OLmoP)+Hgttu3i1U7+ZDMCTmjlVYwt=zu1a)m6E_Z>)JXKj^P3?H30cd5Y8b zl_z;8?;G#qklR%vIU0)uqE?}cCAGFznfCQ6mh}D_MW-v?OvHoo$Y%mls^l(U$ns|@4*HK;?N-_pdxG67jUrzOOW_nOhc?UH zk6g|_rk}ZPyj!-se-?k!+Qi@g02CkE;7Uj+PYrC9snoXW_U=(Cnzb`0SU{2TL04*2 zoW+A6LqG`|M&!)AG>G_=#NkXn8Q?5Sknu{qvnl2)FCWs8Vcs=dj3g=J|eYn`a2eii74oHnNxyHwflVA9;E)wSA5 ziG>fBSTNyTY<9n@jgves;t!8jpB2=%+aQE{7U!2gEjvs;f}f9~tzZoPk${WhP9`Ux zHOb$)#9N%?>#(B$%W!xACZ*0tr^Yh8u?~9HIAD7sAf+#|=BeN1T3UCT+}YbS5IW9X z{3GleL#ABjQ@N3fEAPlLn=KYHrvpeSag(6{NUASQV4zsEvpL67f%`k4;k9BO@LA^c zUn)4o7@LD4p~k&G>yA^0N|q^0pr?G3AuwXeY}dBbk<}LjjrT)bbSe1eF> z05S}A)=yKC5krDGmPLPbyEfCUxYGrAfR7oJH%d9UQ ziH>&{N-IJ7bkr!mLCUAGj5gWStGW#UsG-xQv`}*Ic)#BtxZW#nHTfk)eM64aUF)hJ ztEzQNaIR~1rAt}WUNA)0R1}P8puLkL!{nyw3jVL(@kwSASz7b0q>Ht)+bP+PHAO$11zuJF1^2-9N4C4#M?%e4Uyb2{TLV%O3>S}#9guD5o-;Ckc zvkL8?_^K{vxcIAAZ88FUhQGO354Wy4ciA^C;GDC{e5;Duue@nitB~XBJCbhqDwFzm zDg7O^gJ@Ec?unV!G#oxI-o#dSkH8&PFU)gyImllPtS%wKo1LHwR$Z9d^SqBX9uje%9QD@q68tNS#42yPI~H=oBnh zBwP}F6Lrp93CJ1lagWeq>@Y&CYq%QcR;JLJWWBV{PO~x2CByD+uM1=G3J1D<-o@eZ zt&)13Xg?ItW4_F+;-RPWEWy`(FE{9^8wg6i8=NP+YZg7JjnR(GMS{PrMI-$(Uwqz4Fsho;mNs;ji8{SjmP$rE0L0}Yn)Gss$jB@brjCL zwA&K8sh=w3LVs*jvc1ZQ{{Sl4xbNF-;b`94E5G{za>e(QeZcdrEA*&lmc@iVrX{{X1kez3g_+D_Adx8=EeNvopNd^J@qhFvdD^A9M|TW;=H z{am8)`h~J2=|8S_p%LCm6J#Z)r4u}E>f+VG@tPFe`=!Neh1$t*zGV+y>S~l*N35kS zXhLK^GRf-@JZjB_`+6%UF3d{y2_30Tjk4W}Sgq{8R^WNikfMVvPgzPgY<-=rykNUk z=X>kSh1{jA6bOqV&+WlUZq-7DTSkfg!=y*$`AlunFs5)^E_Vvd>ZbOx{{VZtm9~ROLb`Tr~(#l%6hY_CB#gbgsC!@&NVw4MFDskfV|n4#kTHo z6uPvZH5xOnUd{5__o{Lng0l5ge5rP!=N?>v=nB+y)+yNx{H}2eRlx9@I9dx@2ln$y zo&&~mT;n~r(z8sm#P_=Dl6J@uGpt49?i^&Rk(`Fc6BoA1Ra=iDaMfVr)Y8^h3RJwu zoQVl((jz%FByUxC;Ly4NG}Tw;H^sOXA$k>6D{88&IuxhWL04Y`8LhlfvP!Lj=Jt6a zpbEuq88#}<7=%&;EFqZ?BuLXiq-e2Sw8KEN3yW8otMOF($(NdH`?8ZuL>g1hklcq& z%ampJ^6s2~wQO8YZN93*I9AmCFCC$4B)Ay!=^;I)FId>WDOPW;fSXkvDKJ+uOy2ER zRFCB)$tH7<^F?S|pLbPYyvl8e0py+f+)C4Cq*7Lt1*IZgr6CfAjS8mQm&o+eRyfO* z;hJ(b?~nj>5IbF0DV7tpJXX43)dLa$sHDtKte~gXh!G$Ts+Qi%eYn*7kL2}Os;1SJ z)~1zMPJuy6Cv5(ZYoLzW)%OzW2SteTyzTyPJj^|twgW=vd==~ak@jE1Tv5bd)opa_ zP*&Qh{{ZHlBqT0Kk&y9>^NsSkvJ*Hxv|w77ar;FF8MTS_vKLBeT~{u7y=^sJ`r<9- zRA_l&r$<5p$c|s_-e|Ut4%uB|*FUKU1tn{Al;vw)fJ{yzVT`%T&2=yhOm1;`YPz(x z+QQTffCssW=C?qql1NFaI>N0@sZG?qEq0Ub~hxDu3kwoOk-NBUYHWd&WTjFm`TZCajiZc@+ABR17e9TDZW$dx#sCgNrCfq+fALZnv}@a6vi zlrua-j_})JuQ_&YTFnK5)SVRT?;hqxNk|1sJ(3BDYf!-%k@^Oz=34qeBVvqI?=%DZ zd39Io%)k3S_Z%3QV(-QtlnFlp*s&%{{Saw@|jL6xl#*sR!$=g zB>@Mws1Qj5UZ?97KNYk4D8Xet_3(obZWKi|0W&ib@QyQ6qHL|yTNdg(vzLF2NL$HM zMHPI?F+4csa};eSaORQqK$%ge8R@Tuehh&w9+W(73Yr&)F9mV}9u*XtBpwhFDU zAEN+C2Ud;8Q6bmk5rMj?fbCWYQdKDle})2>fxW0b9$l+hJ5opyx%dkBKZX5y9fs%If(G(xGidxYr$RH%}IC!tSI#x7Ar!ZcM` za&KYklkae)I3OmROoKfo3l_}sLra~Kx)d4i6{qyi{J7Vk+YXy1Q=(flvIEs6ZyY3z z&9>bv%8rTyp@$*Hwa227o07>BY8A-+u|2+1Sa#X{!9&?sspFFDC1D zbgHDf-*R=gtbne}w<{V!o3AA*DFB|nS@endvJJQ=W~#LnnY7XR6zJMLz&&pstz}Y> z>LsFy`SdXZaVTk_Q?@a0)FpQo|)q96|jfZI`cigE>>QXVO}3GSW6a%kV~L zF3Y9qc$4s1`>5b+N4D!@H)rz|+^OGDLYSyhckexuuCeH@ z;dWz>+G35+acw*giu1SIzuXpMm)w}!85Hv9tA^=Vyy2Ie$C15im`cY<$AfaVt$T=! z)GyH-czj@cf`vtnMZ|M!0+myFwYX_q{I8%XX#g2&L{Ktz613Ke_l>)S!j;ueZ?^&~ zY4T6iG@_Jxh?M4anV6SOk!0e~kkv)ylMkq%T|?I6)cGC|Sh-8|&$Z_SZT=q^zCmc$ z9aTR1QW;+rLR`sf_Wi|t?Tux(hMTHY9ZQ1iN;E4;nnr?gR>|7KZ+h1^MInlkY3&@+ ztdEA%gz~;JZnlaK{W85PRk?x$lG#*;QQ%_bBR#=iHA~)Nd1`C&E zde`}%mkr*v!n%~cfYL}G`HSy3qu}c27apqR$;e|vLhAyjiETOe1~X~u?`8Jmbw6kid-A7o$Wb-Cz>HfIn7vX}15iT&wcLa>&o@=DN(K}7*PGVo1 zu@F8(DAFjUvA9PER*FAic|{<*q$vP`y*hU@h@55EFH;#zQ4Fe_^f`Sxt@LfR(OW^0 zmcK~8M<_H|%(m_hD(AhU+*qo_sFaa2>CBGZqEgYiDz-qK%3l)Uf|{lRGXyPF~?qc!oNY(NmJHj#eQ4E zEW)bN%xBZuyW$r}{?h8bJRX380=*=a4;{cpgJSQ?SjJKx%_+p@SJ-;XT+^T|DYZ;| z(qT^{Y6{VmdZYV&o`Jq&H4QcprPNI$Ln*FN1!~dnRa`IRbFIR<8?FqDi-?xcM)j(c z`g%|JBI7eRM5(aNwnbhZ*{{xEe6e}OtDH39Z!gh79YH@BjKs_VAgNrmB(yjw(U?4n zn%5pkl3mnk6O~XPBm!3@ohL8tj;8mh)9wMy_G**^sRU2y5!o|FXs2+(kEu}Of;b7x z?=m%&Z0~I@jiB_F=|wunANiY#vu1P7_Ivg`3=)Q0TcEx`?EEkYOR^M`g>Qu z$SZsIg@~__eZl*A_D_R<_BqBj>rJNg(*(%?G>2;3R^Em}Q~1WKdl+=AfxQ|vSRM+0 zVhJwa^E;yHYlgUP(S-5smcwARw%r~-M%_IPIua))kP$wezE45iUD>Syd z9Rao%6j4XEX;DC!+^wWeacHr$d`;Z_RVN{nww1y-j;jmZbnU zw1jQ!6$t^8zOjWfx;>H_;V_Q#Zao8-DnYFuC zd$&7Zf}6y{BU?pHb#*y5eRiw8->wxmi=EBF<-+MwwYu}`S5+-bB$EOYT>_Lb2I_jH zm7|O+D|Qgu^dt5X$Zr zw@?XHTZ>w171W=kGHqOv%c49}eSG4pLN^BKmdH@@sXgR6$Ipm?(GuCyQ*`5XpeWM1 zK^;(`E}s|*SSnF5T!h_S@r#wXDQcxs+aJa?k@`bE(Fn%MD3CP_I@**Lll33n2?t$Q z8j9a-M42Fy4)4)EV+fVR5V0>QJ)tR(y-JA_1k1D~#8Elpq8N}>(gbU&pIVgM)Tt;+ za3iRTYbt}(P_tIbnSCMF)DGs2GpCin|{d&{b5Pw!sN?SI*RUP1MDAZ%!^ zn(Cl`2=O*t&C@K&vfDjP71rlk^1tR=C?zFi1txlsdq$RR0FK|l?@>16fN({Q~hpaCfm4l3H)@4)>mrG=aRCf z;{O1VE{@#&3k%xqLWpx-i9aX~YqHqORi_t<5=tI=1RsoXXK(k0+*^B^Z9`MiW)X(A z-s+UDXPOq!Z5>LoY)cnvl8Kc1K_Mv}wa`WWI6!Lijm^uYaV2G%+Cs6r!AcT$FtI6r zvSYKw=v~A~w&ADZs}{jTzw55F1d3f((n|jTR8Xzs0%|WzWiCh41Mp5gWed(b$C@fd#1MB)jglmsy{}8mdc1vl|2tgvX*)ld-Vg|OvX)zUn?t1tka!| z{vr&uirvkuh3i7Y=OB(c6r;p(3$SG^KG8w?_JZu8eH1QtFB6|urz(>PtZmOwHLRp! zP1a|ssm+wAq6K*4nu~+!TF%)^%dhr8li8OPZaX?+6hWx?yV*a+B37DEQ)tZT|q52jM9A zzyYELTj!q?sinZF5ij|0G|);O3~2B|8<+SawlKO>4Zey(wAMpNS!nP~dyYL(!#H~h z03{kvT_L&@+ufo&r^Q%Fm~45v!=wEODWd2VMvT}P5q!XeVn`=U9OM7SVg@>e*L^Zl3B>?p6 zK9RvxmpBxgyfhLL0ML^U7iySyf+wY>NKga<0fjUto|FhFN)s_4KAj=jYLXgOw`vli zJkkOObraDwz^qxRM&%-28p9~S5!Eunm#5(gbxBXQG^D0cRDhPSwz7pRu#k?a+fYYN zvG=yQ>@6=n=%fHWXcH$j~OH@$Pbwgp{VYrjkkMAZc!g zf{}*#8OgFfqnQt3<0D~ol&Z--fuB4gA$5i&^y$M29LW%LliNZ2 zVPOxs4^We3Qr#U%jZJ@G<@E9e(`nlvI``K|vvG6lDu>U{q)}fRS0y)XR`)j)qe0WO zXjXKoSme&2R4exmtX);|jR#pcNi+DxBbmup0Ja|04yq<`m?fq{x_L;HRx#}~S6ug0 z{8UwKrU0MeGchx`s-KFBT%hq*^U%xp#1>2CSI#swQR&y*1n!Fd^F^UDAh7k z%3VMv3$o8eF`y_btQ3*;2|Yr~?KshS1qX1P{$eI$xM+5osEeCNwTq%}wty!Zb8frR z2_G#Y%@qEXkv=c=MI^rE$|%Ne(-K#v?1%pVGcn1{VJfwDKldhnE0OBT#HR0BscIIz z-}s`6*HO#RhS->bF>16eJ5wni4$x~~RRJxws_H$Q{o1ilYkYfQ!t!n_xn(tWVnbIN z2PncEM(Ly}FR2>KLSuj9z2dd9#|v4bCY~X9Yq-B>SGm0V@^;rOk9n@wSNp1_#pC|~ zX-TrrqmTP=xUiO0CSI$rq-uX3b#yB}>QlnV&2TH?dHElsdn-D%e`B!s=^{3NAAGX4E z)oGoo`(uZW<$tN%%x0Vl4JZYCdj6$T2u|I4^qggdny*SbQm{gf`J^3mmtVpV6Qu&H zS{n`rGzpn-ndk&K04V6FtkZ_R$sck50B~5fhdaXfiqno_88a77Vh zZwhuRQsFvrtdQAv0KK;}5jr9DWN# z;T*~w`@XIw7}|OQYJXh^?of@Sr~rO_BDTV{oe;)QvABb;k9kAyB@HIhv;8}(BYLr!wmb6 z9ya@JIqdrd<9a)ORgt*jE_|TiWIW3+RDPjlgrytQ@{%@%DPCTqq@z=tkH32^+VTh# zr-$OPlR0#<+DjW;$GDR38WUQ7cfnIq*LS>M#Z6O=RKB-2R8*{$tz@30ononS=e|>2 zRo*ti4Yt;R1vFhv{j}N%_8smOvJB?nQJESFbt|{VA$bkIp>v7KMOr*bT!!ZCJrWEuHebO*1)`+f)pTkZb< z?c;^je^ut6x(+>%-D#46Rb%1Y60Y-FryJEI);?ctN|Hb-C>p|sX4*CyH}-`%?mH+g z*wW^>bbUat;s^mij%&Ytzo_y=TzGNrQsv}0$W2x2E>*I~mAa0>igwcJ2UDmLo{%Z4QI61M;m|T8^3P9y(#^i^FIt*`AaXga+_70Ny3@-D}2W?d)0=K zS6k{Dxq3T8LG-8+Lu5~Bku#Bl@j2L~bD6X?q*LOdv%FeJqifwEl0ZS%<@l+uQ_7!e zPB^EhvG8}be2bs2l*ug9-t)`l!LQX7wdrn6x_}Y6Iw>ot+h~wC1Dea?;{jqW7keZxs_g!kHx7>i$N^x&jlH$aS%+N`R zCg$C4KPiE~imlx7s4qQp$mt#81!?;K0N|sJUCy-6+iI*_q3(kwA39I7}&CCJvgL!!jm`V4@fNR<{KgIi^Nk zkRy54gTWTw`5wuK^ClYZbCR15A#%s7DDF-;rR@`!r!BgGRi=>VZ?lcF$`?G*v{Y}n z9s_Zfz%{n4-78;T@lbP=Ilbyks_k#Zs@HO|p~e3IORZ9rf?6j-A=WHhhnc(HKGD*u z4}BH&KtCtV2B#uD2=LM7Kg1=Dx_%+(+~{{Zf7 zQrLgUs0A|dt6a5%`9xus5Yztv&JW%Yd}>d6=HwoNBm11IGmq_Ah5rEDXD3^5tY)J9 zc5^7bA4Q~00J_&sr=XCT*TN3xoSM%W8JgkYtOKc8w*%t0nP&)XS-s?)2ja5#PsE(Y z{qD%c(-=(!RFqmm-a-OGdid%i55oP#ac^|&n|8hnd&_;Ua{mCQ21qGiGNFD)h2@r4 z+NHg%C~?^W5TfL~CPZ%BcbnU0SRDa7RuReg1LW|PztpjT_hT42M)cPM*Vq?Szdej!(kWtwi$mYg}x1|+r@n!=}%3Oz6h*y;&maSWyT#ll8 ziTq;k7FTP1x>OZ|-r{#V+zz1$cgTLzacmy!x0+!tG!(DTBeq@<36P2=y0k^(Z|>w9 zDpBVUHDSauR0>LDl^_u`qz>XM7x{KD&@BUwamgHaNR}StIUg>$*$oA)wNlgVBh&mlqKh!Xo9)S{$K2kg3i^gfGnz6H%@33TW7T4oVJpcr1_TeM24n8e+bSmVgl<= zHKE#{^xCUd;|p`m@zpN$>@Ku7poxDBX?VD{L8{3z*I%EaXO&e=kK8KUAPwqNj-5e@ z?c=EZ6x?w+?Fq2Ta%!u^No|L>Hf9MB6Q`<^wzL5j*ew+`mDJ6)(R!uTfZAm$>C@^;_DQ^S}*HmUpjmsUc zxzML?+ea=r?Z)+6l3QF0r>RgiEoc-%ddSAe`?y~Y$vPG1GsCiDC-ru^N#?Zszx#$~ zeEiCBYE=IKmvLrvs!(v3st?DY0yF!Ys4c3<1p@Sz1B7HUFKqcI?r`Wl6>9o5w{Wk} zBn1sZdk}id;}wxP-BPxg@jGMM(LfVjp589_(@LkfB}hVsvY3#RO~#ud zEZ2Ig&CM<hXgmv=JAR>!#sZCW~@ynG%k2tfl zU4!CcJ<=ByKW`Xn9YB`K(hpfonL^sQ`I6q!geBw|jb$?oHOt~^tQXj)Gh-gz{nzl9 zG3Kpr#4%du(U3YDE-syl>NjYOLK2zk(R3)tbev0ES+?gM_(7wbG*H%ABVm_{?c;M8 zO-JG>U(mY8_f7XW_P58p6MxEa9#*vHxh6+$dAu(ewN*D+Raa9`$wOP)Xp-6zR0_)F zAPi7=zXiNy*nGol7;9g_Y2C!`t&PS!!)WdDNGVUnaBGlQS618gHx-BKYgX&k4ktie zaYbt(J%q`SjgL3a<9mx+&@69)_NGWJ+qi4j^q#1;#KRW$3n@rIYe1fwc*b!yVXt{w z9sd9fcnJB&+Xp_tk_y~gc0M%%%h@pUQPx zXf#4`pTAEd)mxs&a+`7m6XR=6wz8!lnd=eO{{U+&f>xG1&=oNL``SH(mH=q0vjC7l zZ@=9C0J#h2xXYXOE1l&45Gj$V`(d_K#|Ps@-FMp-3cqTJY^_?ChTjo(iQ%8pzdjn#W)RLfl6DS})g%0)t)ytnqoA!`2E)*Kr0GJGnK(O0m2RR+ijT!ib3rNFYl7C$ru&!baE?v3 zRI6hzyHJ$?uo8U4N_xS28urkygn_TzI<9*C)O$C{tFE>8yh@z1(JDKTk`pnnKs&`F zHO5PYSan)g9p+7lfhMY?vd%iqF`I+5S9%9f>q;R+?(N@L&#o}n40fwy#_~HknO2l9 z62$J}vA5RQLIjC0<~qVa^v+i=3zQ(Z4 z7```n#Ap%>!cjiGK>{wAsW(Seo@QCHLKT9#y_IaBbWsL=9%z*TLOtpHxD~aCs$D%j z72lzSZlWDDoI0huozwYdJz=k2iXEV|8rq>v{mCUF6nvoB#*?mvC)$Q1xt znsSpbrKNQxF4NTQP&yDojAG$P)O&YLU z?^J~?d8WBdS}Vw$wkQ=(;W*{n=*pq(qxFSN_R7VMU#BTiJD$*VnA|*KCeA`+R$Jlu zE2ADQx3zcUmRVAF&YTIIdZSpr+q5~9S^deQl_GndrK#e6Z>Gp;5LjvFq~Q}28-!FZ z&$Dq~1#QQ$O>M7G6v@Z(rgHpt-*sWY>YQyGz&g)wc(O+ADivw*QPf`$;86p&Sojpf zniO;9W=qt|Dm#)6fDZDUM&uo(6wQF3Pd~G)c8O&#igcBzkW7YzYu*@ufSJ0FcU3BV z!?wC?GPpJ+wY3=!_hLx6(N<=RdxEQ%URQa?v+JXpsD})@_l(~sU#Kh6yk)HxE&LG6 z9mcYqmi9JCM_!|#oJv}}wTN5GcDcn`huYfCbDwnh=1Y7>{{S0YKS6NGuTCrSh0(C2 zQv!NwwGuv2dEpQl?suMy6%D zsH~?=LWRAy6|oEHov zyGOfC6+=0O%v-RwKuGgbD}wPWzE_=6xpApz&|8pQQ;1gipc(wS2$0L%M%ZZGR6h=| zdAr>Hfg-){^Hl~bZ@Sy*Y6U$dnw?Fu*gu%09e)^sZ6b!HS@K2KS6do%T&*{cWxnNk zsdlSVN~m_>QVN~bP14ha`P^Ll0jyV8&vo@Z72+b>NU7pf+2(#vuyDs6G40QW>ID_K zAb-?clAcY!cdQ_8K~hZpAbU{S~(J;Cn*ELyFFm(kCsYDUeSOwECc%zvUoH`~R>C8Ml>GYb- z{{UK|PTx(Z2$=r>dJ!by-4U_GK-DB`^WHR5t)S#6Nq1IO;#7{h_Yj6W9ZKt1LD3e} zyJ)U$O10dWxFJ(230hS?{Q)qU06h_IG)=wh9=F#AH!vA^9Tt!Vuo;S{MDu4&bfW8InbKYmJrsjFbU_*gLS)Kar3^SxciTc?3FWb{ z2~g{#3RBUon>PgM+el_aOhs$bTT~_tMqE&hRE#8o6En#C5sq>jKd4nWrn!mwRmdA$ z2j(K3l*qabd=`O*h|U8_dcBbQXykm$m$A+;x@E|0fz2swhV7qp70(8oir)8TR4r!wI&( zfZ`N)8|BKKlXA>!5Y<(6QP>G3)dL`U$Vfnam+fvk{>)@=pY<0LYDqQy3J;ufJI2m! zVjj#O@iuRo`*_YTSmixEJDDvVn=WyrrG!-5>fTGCFKpYpX+e=a z0X{M2AG==PSaICljJE9&Lv<@-eFvHSngT2FUY7e{?v_+yvNlb5c@USfrnDnNPu4V1 zNAFebtA;VJZ+Oon;@G9T8@yvT3Ts+bM8j0bT_V_6LI`Xodv=LL*L3(*hYrqI-(p`+ za*caI(1I&dPr+1sImqvMC9U0!{ayo0!97|*rk?{<`6)N~qxCJT$8YGAmXd#3zym-; z8*F`Q8vK4)QWX_UQ(0Yc!V|R`{AME0xNTW;Vtc2cQNFq7E>{%s!HPb zt!COW3_AD)H@H^|G*mRst%K?=N>ZiV{{Y(;j8%@t1?RqPn)->z2SBD}1uBtFl*o;u zQ}}d?4@E((bO;st)~f4OT`4<%P=b(UKB7>I$v_{9lEzxRNDq>WR9qoXLv&qK!nYEj zek9H-k=w97v|0_;W@j)kofCyY#&k)lZTDCTT7NE|4}=~V(o`wCaRhiL0a&X@Wg%cl z4G}M=SRlNCMIEzRqNg$1W6^+_=_v_m48Ks6$I%M5*eP02EwB)-;vS9LSHxGUQU3r) zWjnlrUH<^kM+}H!(1I$|fT@Ih!4rggX|42IlVxsl4d{C54!5f5MwHfsBmwD(YaOCx z;)rv0mV(FfrTD=Kv}sRn0Er_*3P8wpM`@q{4=3!0w$vnTr;6B`AU}uaq$Ee8N54hA zx8|-+>*R@@U0I20Hh1`0URyLEPDEcE6m`T(0`zWd&k8%Ezz~AwGR#DdUYmq zgqeeNMO#{uQohXH`y;DOpAE3Tklfr*NlOi>NkWc;aRAC2iO!Jd6Im8;q65$xR%qYUtHC zCFC}RrBWSbyFy{d<7Q$j9nEd#G;pOLhE-PN?8+Nv%^pvBn6bd5OTY#!CXLZll z)=;#iy02A9Z6K8y%;_NzO>~6l7Q*{jTNo}NCciRXXLP6*yFHS&=W(err=V$tt<{oc zqClJzAbm9&pd`)XEXMhyYg^_>2n{c?{{VKG4Y_ya@lDR%Y*^ffY7pWTwD*t(Azx>7 z?RHQqjmO*v5rSWBvj;oi2_LOT-b3N8N8^+ljL#UVwo)MNT~d&C1$<1j`oIO-4IR~L zoMW2fvVa&K3T&v6OyJ(!mYH;yPsToB1S@^WEk_NsgQ`UQVV4T1w>*WkvJ8xbBT$kg zK@^Y8q?9AAbyLObTE9Fhsp@F8?np8w6SyZDz4cN$X$g@gh*BR&B1#%Vpr&?|Ey}d) zPzPyH@s2p?kk(MCv+OppCpNq&8R(D?$jU4{UDbM|_W{3_72>&Glv~8QDKZD?8WdFO zvAdzsQMbQS`kU=EwL8R*)Wsi^YUQx@&qkFOxaA&`Eo(Ud4u`Zt**bw+b8-^o@YgDA z5+sn6%6>iKO;%XLRT#M&`OZD0yG|f)fFWNn4xtJ@zLJ%zSsC<->wyJmOf?J8nakr3 zgx=z_LVR?balp|el;Lsxpx!bHYGkK!fdq+6N^o{69r96DxlQ4Q*pRL>h`3D^b}DzB zqne6A=_zR%ZJn45=)Ap`%DvAeB6)iNPBxbvZNEh`kt{zYXT(->K!r#o~g6ESGRe2X^Z2lGmnJgyJt`kBFN2lX;NO6(DI1{_1dEzbmdnsj-)SA@q0!{!MRwM57bz#Gz~Vk2b3gskk{iL zc*^ANpDCqHO4{+3_Kb*Dibq@UGn^M{DCxtr$O`Q+W$_VY#OEcopmL15#zk;W-9lq| z7mw+zD^spB=&dUrT?Do58%Gt+8<%RcK4XP!5SlBN`)j2M#}-;TZ8GtVnjhtb<=3{i zWgvd$3-vL}rm`icWUrVotIZ)*Jcs`PcVJwG-xA{&>eibD9j93B4}!1UwY@@p{k~-UIfZhSqbBzUf!Fds8d}+?UBeEyINLQ9 zRa-~t!7n({OJP&q3bl!BMDa%^>07y@!Bx3dI`;O+#bqb8DI?Y7Jby}C`;X^-Bj)?% zrmKfNz0z zW~FFRktG`kgyyMf2isxpq#8FxRk`MvxSdBxD!QrrQ2mpAwsQ{@&~n$hYoqxM{`T0Y zw&3m78cHfkb%~?Y45=z{W?Ics9fWGoakgWvnDZC7_w}zpyv=pp`Lnmv!*4&+!x116 z^FE$6JqML3r&U+~0DC`Xd;{$lm@%t212)yZ#g@_3yLH8zsa-;83knDePpG9SAz@u7 z)M*gB=N)ehamgEYx!qc?UvK=+E0KKA-MqX+?@N!5>G4UcmJ zB|%SYwq#FQ++AU&q-ZeLsFb{vDPbu9t4YHVbN85oOl>51m3e<*amM7zBzS4pZlAyY z3N7-t-Rtfb%8feB=iF`1%TG{LS50WAr=m#R*eOBM2i7C*JB5B*Mi9~P6jq#PWw`t@ zZhT(h{{WfQ{o7PBzh*gyBGXr8Io3y8mt?i-CgFX!KDwHxia-N$+%wa#=sQJnYx%Cd zQW}+QgBxvSFpdU>H^?T{x+JM8a0zPyx;iTqZLtBwZLKpzqWNk@vfbAD2wU&`$pCera$B=~NUqML|$XR>In7R9Xx zq?P3+Ml^0EDTdAe00I_I&kJSDKu3!Y?<=5RX&(IjsPTW>mi6uXlQ@dkobr7|*6jVb z)X>tcLAvKltkf={)D?d$DoK%;l!Rz@nD}OymqvZ90)?w29>_4YP zP&tOlNm3Hp)<{IpSwI==y`!Y0Nf=B{L9u>!g6K=_n_O!BjQg81 z^3%BDLPu};V-@_ltW{T@Dk$_)R3|# z#5XCK?Ag_|?bqK`-}=Q}h4!#FTw?J_RP_~II@9Wut+(mQR@#A-ibyeU#xQ*wiPb{f z-^(4-c4$K97VTMe$uBNlZ`CedE($wE?(IUWMok6Oo4X`EgawV;BmlE{`iX!Vf zX#n*(tv*$${X(bqDM3N(d02B4*Is|-Y&(Q#)(LXMQ*r*gkr^wZZMIV3C;tGNMD#ct zc!gwm{1sYmStPnb$2;Rn564t;o7b_vGxY_s!J2!8yD-=-zgq1!A7FN(a{P??mENDv zSX**7(H}!=@j$;FeR1==FnH5n?fTMs6m4A9J1o0F;Q}?sjP7z z>OwtbC?tFYgh#pZyw%34MkiZFDAs^J3+fCi_f;h~Z+YgBb$LT#CqEMjY)%JN^tlEO zfiEqbeMtMQF;p0Q%WVuZ5_ngvTj|I)#6hU)r>&4=RC7!4Ze|Ij{QI z0Te&_uT^$3NWY}rZ6LP4+!r5I_l)l*a-8N@%f?H0-mg~ccde_hs&Q*kGv8LFs%NQ@ zgD=^2D}-d9f)5*ikA@R7TEV4F01DrDyW5W<@jnIGc{4P}Kg;;;T;Qj)xlvNWsvlC3 z>!n4cDM|?`Jyj?s{{Rx}LKyz47ahr&liehB+7vMNiFRrG?jl=qYif~AD2+fNK*e@a zTP~U_h9QDTHkAk*$##RgLLGeD{+*A zyb__JMB}c2A)PCbAIw%zp1tSNI4uj)zVTf^{{ZaA(uX;--33Fymm9+Ak~|1iV!@7c zSlr79YyNTj!bc30I^yg90P!E( z7u>tjmYmPb)U|ar4n0_vEiwj5pF!Fv*?vJ&O07GNaD9ffO!o|C_N&HmH`l&EmeVdmW|T z0vM$!oy9mQ>yAj~M|(<~EjziE)(+H;u&j3*ce}mHy_@E25$s}?mwcjw6~+GmrLv^& zLS))>9iq#Sk&(whsyNn3JiCgASGwBEla&u7or^QwKinfdx^XXBZM_=65KB#|RaGp~ zve|j}hz+)pGE{qM5tnz7!rji$1WPwOhPSG={{U$|(Va_FdCp}?CfVBcB^!WwK>+!J zqt5*Q0JyH0m|!bT3Vw6Qa8V)&qQQJrtxjagL~`opMZe??4!(aV|XQmeC`mzZjpz z#^#MybIPum=lT;9VYQ;rz<1J;rK#|Vv*}d#KY2;4hVtvLDU=cbKbUm+M0~S)l-;BN zH5E`P&C@eb?sfUX3Lub`)D;Rxrn!!Ai3E?wN5dV2HdY*8j! z5%7na^3$SBkTK&>wY>E!C)P`8ORj&$bR-252XZ>YSu4o{I->BHh<>0Qy44|XS7rIX zndLL`gBd0D`P{ulhs8753A3-t0}4_-L$|NQz>K_intJ09%5+1m_x9@0*xlw)02$BW z2D0tWRrli<)1XfdD@^5Rnx+69d`w4Wvk$i#DZDQjWy`fn>iRY*Y4>ZnJyqj4YYCi| zcT$PYMaE#YZ9z?)eR4w7cSel}+BI1n;f|Syq14#o8_T4av0yC<*NZd?hF*=4z-sWvSen$1VfZSDsqw````g-qhN#wJA|WTnxZggQ$V#Iw@N~NkQ(lZI;NK-~cqDmXWkg%|plz1#bMNaz7|Y z2pOcQOY8?Ra?@VQROt<=Lvm-~6+TbLTXPB1RUv}Qx);*AD+0o<*36>W={QoPtvZ4M zJv)43%UPUE`1_sh=nB}ZuA{z&iZOHd75;^%?sN?mCu)@-J`?R5uK?$5xEmaLELWUx z_d@OMRKd;{`pYWnP*AC)%0MV<^^IR4a(30I4l88W8aYF|UoqsCs_CU`Q%GF(nF3K8 z22IUcCB_aXwu#st;BK|_VWlldQn~K%_7!w-%Uz1;S8z+x+qo)GBq6qM>ai=a;0wC1a+V#;MCYw;vCJ-q=E9d+A@&zbrU^Y;&E- zzP-L>+hK|~xBzRZAEiTnQ@~!}UPr#*sCfbUP5Z+n2Gy}zDKq~75FpAlPI2sCVq?2w zX}nb(_OG4EN1C{>ne{O^gW?GuBk>eaXP)^Iw;rOZxztn|WHtfZuG8_9d4n_QU#NIc z=^9gY!@m5jHWyo?uDUjs(y1wGaCFnLh)AU#DC?Vf8>+RQ7x!m}=W0bWm5tOP;5R8M zNd{xMnTMR^9uvpSnC=CY;Z@b(GL!T`^lN zz}4dR_mE0T%8tURsUk=AL{sy=)ca3GcE#~F+}oFrm@7HsuXUV3$b4@={l>DDHz%pU zsbPC}rC@4EC#>SnV|xq5O%*El7dszDj*7Ou$=C6P=HpnqccAju<|tZxk z920JuHBg49`c18ssfN-4nQZxc2&c^}0MIQG)(Hvq6vbYzO02jkw#NCa1fX=(#d1m9 zU!t_dZq~gx)vR1}9_NmwIu8C;FEo)k25)(ZnIz*!ihRQc>6@J_4O*;BhEr6L1k%+0- zNmhGyiY7M|<_mQeLYqZhN>KV$DH1$o;|#XsR)?zkYch2TPiJ-;R&|0@x&l_GY1OpI zNQEL$?6GfQ2yrovKvyfA!G)YBpKI*tKy9Zg((5Vc09vCeyo1}i3v;o$GUE@yUeC5# zns#wIHkR^t2GO@s*mjGY68b{Qt_Q58uP?gG)@s0)(!I!6No%Ag(u zUXr6-sd`#`RVa$HX%i{y-C`khS-}+{+!mE8>FEikbN(2e7h4DyDL!?L7i4Tm%sXphdz`9ojjD_;_LlAUFPD=RF>-j z2>{6TI(WgZIx57VIqAUUZWiM1PPIQvm14A4QuHo0{0yV8+cEk$CP;jKrWzH` z_r5=4{7u7%{{TO6`;KDQaOhPV6waxjmjT^0Y&(G+r4+tZZyl))W`wO{fo3jlzbI}EA*IGD_`x=ehWz4KewNznk06=t&}|YE)RP4ql#@4YC=?rU5Zb?RJBd@W z&w^9?IN=r4Ts9+?`30qZzNnAp(y~%dri0WVY)iX%6;+x?kSe(>N>=YwXkSWcb8=J zQ-<+$r^Rhbp`bu&q=B+tU7}LzAm&gYUTQumH06r`z;N&S*X~B~QDebhD*pg0;q=FC z*Ccr}B`pK1Z0V}v9_uGZZEsa6kHqd9-_ca(x^KD9Bj$AVSSJ(GF~a;*#M`9BY%4VN z544>UYbwzv99imQK!9bP#8j0(4UByNRULcDFBjW`wbf58brl4y*OcPjb;7CsWvr4< z)&$=2mF+RT5mg|&7lP2I18V*tR3v9PL!wtvH6$$|EixRYy7y5=ntFFA=`HOBn@Wt- zr2*zqBTZw0?&`Z6TG2#XPnF%R4khL)S5CINEecvp{)QZmVggUF*BukZiSyHrDXI*G zVyGPvP)D3;+z9$Rni?iuWXCe#|@IX;pg-Gi}-l@jcw1g^B{KKR+AEIp(U2@!48d?TRlTcVU z{{X}NU0W;_e1nP*Gp^&WRDHHGc?DVuQ_$C2ZnSM&Z1)O#r42@{R#QBO+E>XvozvP3j3LjdN@G@)>b*X(A1G4L^+tC0_N*l6fho`96%5s$_LX(R+-!JG zmNR><$L^FjyFFW!HPj?6ms?tqIUa^xM0oSN{jjmk+qx;4spx!{vb!nWTkIvnWrg0K z5#Uw1dvg2Ja;LW|Wi1aNIJ?FTX_xC#@{LMcXSq)AIwkNVd?UuT9ku6tVq$QY1?(UF zHyV%8;y-ZmUajOed>@%JedJ$Ha!1bH0Y4GYsVnyp`^8}VzwE~sSlAXK&`3`Y9VTUc%Oo zFiy2pFYY7miS8HN7ZqCg&p)qKb)JZzutSSdlC&dWGd)NKQ#!`w#eUbWZ89vLdxy-} z{#Llu4+US!ch3zSoWDtK$YhsC5!8jp%U_&F>9abo3_qe+*usBM(vFfN_C%B;)T_BO z=qX8+-byGNqG@Co2bxA&K^KzHD9jazFWgt0O^zIoD5q$rdMU_Sgr*6crKBefYwk*s z&a&T2W@**hD`Ug4cBm*R(Ayy+V3`79ai$1MKwe;y=)J_JHk#3~0BnxPki)7kthSY< zU(3>cA}-}yEG9`hi&|3QB@$5Y18mVBJfM}fnuwPK#H9|#fDft{Ucyk?CYj5$mW)_>B?}+8d4s%e^XHaNu@KdaHp4U4Ak0 zX>4xnt-uxO164ikBUD^~(5p_+=(qtoVI>bm!*KGeuUK1dJ_(5<(GA|@lC5M!6B32G zbrE%4acu=B^LEx8c7Xub3Z}0r$q{d1=N(W3&8a>RBj(!ORf7|4y~!hvzm(=`AJA>e z6d?ZqGT`AUZe0lmdi^2GYk4LW14PFlG6>vs@rNb?$0*j?Tw@0vyFDz9zBV<-uXKE@IRZqQ?sYpUj zfKQ}EWpk0i@(orGjO~T@Bgj_jQX*;TT^0LWI17#`@uzJ=7^ZTaE$uD(NIFVhA6TkE znVv~C=)E0#**r8>TO(ko3z{FeKeN0=oWNKGk+(6dz&?kOu_QKjlRc;B+EHxHV564E z$}}N)hZBE!#T}|h-fGWHEcBvfG5<#oj{umHAU{!pcCf$!W_Xw zP>iDGSGJuA`om;siVzZjuI9>S3WVXBj}-W~ap-|NeQhRNftQ4IO3)E3$GHJsK$I$b z`t^<(+K6s2lo9Nd0Dqn1Ap-ls6BZ=bqMm-jkVu3liCHBlaq>z&UsXw;Zq`56WYq3B)Y4X&D2}3f z#hV8W4OVH-Zcn#Bx)9>JdMZqHl$4M7K+i$iG}s5EFEHMdyr{G8X~rC*sQ&;7oW3xN z&^@Iw#K-3l_~WtoDCJ1`%!RzPGKT?r6B&uwc=gq3pL~>lMtm}E2t+J ztDdLRQJ>N!@5khltJwW#RL_06yKKs-ML!MVsVS}2t+qq zM@6`B$&8kpk5wCe%UqCdkD)R`RJ~F(oQ&f~?eqk+DmOp41*KWX+dj3w^0wimlBv(| zk4Ln6Lh?Lv{{X6TMt#Yira1k;IIsM*6((azCUD))^3_)V0N0ga_^xJI7~9tcrPkUt z4!A&^jLiNqPiyHS^gZpN_5yOLaokFwpXM;iUPzH75A2C1yirx9hg8*GZ(Mt4u6l~P zjWtU|!sHIsOI;-kis_9SsEIL`Z~jrD2~WycO_-J2F-Bb?QYvh1GWnQ3EQCzx5D(&4 z(`fk$%CFp2{Vn=dx<)N%Bl5J;K~ILD2}4fK8qf+ZTlU%@YWEV8-}Qr#E;=hBc`PsU zHWUJS^pFtP%Fa3(Bj3X;BO0(0oJ(K1A8@uxCT(^uX2~qJ#uAjVLq&G0rfmrwljyB7 z5h8gF@c?f$3KhM-!H4hm8N1GjrOmP|&oQd1zh+rY!z{d1NhalTt*EZ4De&J=wW>qs z5cw;qW%WFh_^ZAW-qPFC6GKP90GsPJBa-D+4pCz^YMbp+mACZNY`aqCTtZY~vaU)nf+(7;1OddP?W_#r2cAIw&_TQ0J(>##0*VZ)6wS=Vq z01-8-Xv^dbPD8>nPo){x&qbMJzT$YDn}~hONpFKgkLn;(_M~q)dsXINYL|KrI_B(k zykjT4Ql{fWM8#WG$#R*;3sVbd0WhH=J3v`16~;ZmlTbLRJ{R0vXC?;b#l_KsO+_{R zWgs}O4Yc3+0}Zj{D-_lnrOK6y!^K-R(>U@{;zBYmhbBP50 zzN?lVIJJpVq8Hws=55W-*>YKURTc}+~eXH%Y zRldW$(U#Ok*PIV7eJ4q}E!Tv7KB}LG-Ss370=ahOOFtjGm0zkFxjR);`ihU%wCW`% zeML+s#70e$jgEa*%ZmDqh?j*ewD%54v)OGdde7G_P12z1NdjZ9${WSCzV3;v_U~q; zTJqZq9yxcIOCmKlzGarf`e4NwQpfJP;$LihtIWCY9bi@W2eJBYRmN{wULfVQ zq+Fb-q_nn`hKo%pw4ujTu#gm~j^Zp>ej_B$_2?;8RnBtg?CqT}*K69sJbhKy_9xQ? z_G8D~Yj@)7r@7qSJVB1+&C%R)_ioLs-q~f!jjvHs(9;F2PV5rgp&>cQ$}Q30;IW5w zL4SZ*jNH2KD|BOEA|cd(Xh)c?sq6P4?gQE17IW3BBKBXy*6Z#Gh*@c9sqWbdD(Vq+ zx;CYi*Dju@h3^As3No1!l%ZI>OLYUuvazUVmvo_&;k z&@$ZVi+v@>61Rq|bk^DiXb{yQ#NT#AHTjPDnVyl7_ctdc)=CGFN-tRUXO7!hVJC%z zo!Xz*PwrfFj#|s|1>^q!ty3YZ3tpYENogrxrmz%Cib_FRkBN`2`2_y}Ri{zW1!=-; z+hze34g$6O6^>(Bo@IH#u{W>TJ#E&Awt&UDq4ykRl>Sf%a70jvGC@Ss6<1d8%DlavAXeE}=5?&Gwx7}({ zl(dD6t00WM{Q#<#b_Mqm1~;iRQ?DEQe*2}%toG~GHZMbFqq?@+)QPUR2Hf;rKw4C! zAqpwb^p7%TKKD3PrNg0ogt@vJ0sbr0xKFT-b9-#r+Z$Tf3W}5d30!{H{^Q*AzgKp! z(v@nWD&2Q%yg))?wRiXT!cZES(Rtx_C8tslj{aXA)&l*sE`m6r? zBw!xfxOblY$l^Q`$al^~yETGPxsz4h*`sX?IGWdi^bIOeG^mc^JvG}-=;irl78$Y- zeLI822D<7z*NuDC*~~8r%3NFHu1&~vWD};EA0RsFsN01x7CB?y262byT&6gej&E6| z)|U5@(zMB{ZFM&nl8GvyDFSLBZcvb%%zIWhNgcG3FzV5u^Iw{GmiY5~1TwUrbkMuO zqh6mHr1!WEGQqKrbeyHdxTVqD>lfq8r}XPSVOX>&+UDD*ZPmJ^Gy)8iq9&a*jNgpC z*Ec2d^(ARN%Pg?7Y*wez2VXq`(@bo(RSFwL5?rZ1RIt$9BRPKv)-|hrkvBlR#dB>v z%c}B9b}SrGdcDyL#$8&RTF8)BI*Fmz6V^#Wq+%;2oQf``P2l)F%+B;RZGV)oE;VkV zn-R7q=W=Xq7fOj8=WMi3VHHd6R6+DDtB-cT(y|3%81E+5X1>g9^%pRgU2%0QBTb+s z%|IRlq!o{t$w^OArgCf$wA(aL1zI@;)~=SvRaZ*cWF5h`Qz59jg?k+$(Tx?vbRwCX+T?ZPMT}8mAV4h9O@Ck@D<>;!*-d za)%+=ae-A&YTDze{D~W7DM-P&9E({{)DIuQOEb4UMdaTuSzTaqrDUM$hRplLI_Bxm zI;}015x~j>pmvBeP7+z_BoiQYBgP}{BO0JxdH_~c>}%S+y8i%jXE@8-O6l2F`*j+C zAV}(U>mFI|pDvI;*^I5Lw_I(qk9I|4KH#}e7va20qMn7u>D}4s(Xx%B%mk$&6b=!` zA9p_Yg?Y~2HJ*kl1LY1?tH>@_3$1JFQ}v4^AgXc)q>OrI*JKffG%DSWKBsXCCEu=2 z++QkDDxb%+5a(#7K@AiE7E`Cvnv|)035FW#mGhiwsw%tRscqD+y5i)sFp)omO2ZXY zHSEL>B-7eeRdBgEajSVnl1XW$8I7RnFPuxl&7z8ncOiSy*0%7M5Sm5|+*E>7-f+1+ zMJ6Wg6;3uM!58eY4OTXSztkD*kt7bW-=e9l5L0IZG^%^4{ij}OseN~wSC?LznRbQ$ z08b!vP{)!G{-}nxf$kRN5>C*D2u$Z9eDwsTo~3Sjiv&(Y&MgdvrBGxw>Vh`4dcAcdOW_hx z4OBhSb1~(SvtO6?#O;Bip=MiJp||Btf;>CKmBW-?J}?thp}9t9Qck+Wc`9%)lp4H~ z(3t_LfnGv-2+dMHtewj5@rb#4t2+e(DNjZE#Fw&;x4x|8dYp;d^rdxda9H6UG0vkmuo9&Xv>uD6)1p{ zrjrJ7=)ND!p}vGQ-9pRqczJB5sOYMe_rg^a%cvrqK}kMTkU;C>7q4NxQ7~l59a2C3 znbp#+OJP9uPKHq5v8t7h_Pt6p7!5&a5WzZ30S-0*Q{Pr7wG0}ivp+!SJi<`N(-gqH zKu#L*R^LuUPg$5yi{QSJ^m(HCycO+=K7{2hbuf>lOgi`j)f2UXKk6!M{UriE+>(@X zOrkiY!JR>oU~WAQzuVq8Eohti&{Cq^hArxbDA;7VQ2K&eYzUnudP` zAgkEK0Q== zvho)ta}H&YW*nzNZI?%sQB2PDIpvrn%z@XWL~_pP8`cP;Xl!lJ0JLt)eX2pl7-TXT zCv{7lbtbAed&a>sT*ncuq{QhBO+aU5NRZBa9i`=YL0IOHe{oOrHhCM}3Ug24%+O(4?D1*>W3~lHNy%quWs;nk> zI?c>CTHMC^*XpY-M5)J=CQjoqAV}@r1duXE>J-41duGz41k&=~w>rEonHM}=PgAtm zp2pOq^wN45A@-DRg*j!HYviMaYN67XUaIxASytg%R6@ekNiv8zns;hd_9iehK$MNz zO7G7*>totbQ;#)hahDoqJk9T><-E z!7TW7j*zmX1GGsx0wVp7gf)~N3opq|=^s+1OW_V(sXZbjr4Iw~?GmhNigahqJKTeg zQs3~SZmiLVnE`swLYbWeM_${uK|-W0K$6WdWd+pSdsdz-st{C}D^ky_kRQB3EV0$?Zwbc!ZsN5vp= z(|G(b+_Py?&l}Uf6UCG?>xlCjG)=6xlU~>qSt` z^wc&}Id_o|WWYVNC@5zvcp-C{ncMGNEy>EcFqN@LijBaHJiEWpMFMzDyxY7MuVh(! zZL|1?MQhwm$u`XY02!mLr>PFq2Gi=dp*yzg6L82LDyVmGx;ZF9Y`L^ag-UXq$@!0j zGk%D&@lNNnSETxN{HxdN0@!s!T=i2&ot{C%tAXk1ES-KGHH#A}8dY{dSpW)*P$%Q(>-2gi16ESGF4}0NCBq=3qqicVH4UPSfj2^d z8&W#y7FS9d%BmbKd!^b(RLuK3<@Iu|QO&n{3V~5#u;i{=ORJxM=9LXR zBEM^tGS|oyZg&K2Cu=-N2lcBUUQDVBbJY%cgGeNku7E_m83NH#q3!?`Kk|&-jbC37Y~DV&v_%<|~%>?F-0EvTkqUNIYUAts7T z7Z?Lox7&UUi@kjl{()H}1n3Cr3@!#yE_sPGS67W$sPT+_HBL3@dMzZ0Oi#-%?SmRd z)Dxey`K0zNoPN6Tf6w#xl*b(L%`=x=wb|_srN8(z$)8g4{GbSj$z(mVwYn86i^uQv zxM--m-Z#43$J{?5-0$~#gF$4Y2Adbg8d3c%vfSB|Orat`9iSC>n1^Gw3Y|$-jlSlA zSKH{Ix!kE~Dk}c~Px{A_haFtVNKuhdmthEv?R%+J*%m zgp^zxN{AEfDa4_+XcbV#>E!fIRyFO;$8=Zd>{lw>qOn8EO-oFrl23=G3^c}7Fzaa# zLYFnZ#d(2L#-`iGH+L8b-MMT8sEK$ei0L4Nfo*Wr76#`aQ$549qp{x5YJ|DQldDRR+>4dnGWzFlw>JZet|4XViEFI1)1-X& z7wS=V>;M22Zu^ng=#Y-$WKIpJpdAlQ{{Aqq^)}1(l}Te(LkK8 z7w0_7gz`Fjm4>y-n-j}V^~`B&QKqSCerY6`CCPM9`oT7L&WQXJW@8H#++-22n!ao| zG_&Wp#hWj%z3W=9)aZVVUDLTfWGa0Wlc5oKj!`243k|=rb-cx=!6?eW3Nx97KcZ9) zqRpp>nvYS?L`BSd!ko3ARH381sg$RA3P?Km#%0KO@SUY;9D9;J-NRIh=1l7vv+3(@0cx{z#=6>|>Z0BQpIB=BykU;fRtJ)oKlPeU z;p8lhXYz*eTqUvOpDArg@ld(mb1lV~VR6&J8>=X@u8o4HP(sAh(qdw9tXg4!@L7cU zXSj7#Lf__znEI=$`Xx2B;L5_ukxU;0sG%=o+$e$FYK>FMQkB!7j$O&PqHJK;)hRu# z+naLbQ&8X=oNyrui$4>NwJRU)<-t2ztwzmG*k!aKB1ixTyR1MO1glLdigee*NMcl| z52Oe#`Ihz339k5UT}U;L!Cs}U|NO8{i(~m zHs{qT&8phn#FMJ04z=}6M;hL%pa98Qj?jqu>X~>R2(`=MZ%NDJ4mtEoWM~=55J3Ui z{eRaTWSTFZYPJj5@{;bJGljLqGjRekx4h+3g+a-HeE$F_GB%eLA3ULhRHbM%*iPXv zJ|+(kD&w!};(=FO5ln);#V1)O;S4?k6OmmKn=9uaH*MY)y`eos$uJD^hO1UK%*#q5 zYabv~-Ri@1`;>rW2-jGOWaZ9Q3yTpOYEzia(zP-K%~}KA2z&N~EG-1pIT(Enb6DT-J#T-(RkUw4GrvT75*Dd=r`eLH$0|y}vQ&rKP_PaJ5N}<1`_P z8gzv2Z6Py|k?Hb_v>5UR#*G)5b6Ltf%QLqUoMt_{)IUUCc$KZTMLTY(d74t)T{tR2_K;FQ>7-Tp4;=rEPuT*(k}UN z>0Wc=ewoNouZXUGwW5&j=zvGoBjA+nR=ddcN#Ai5OaA~W*SHM!Hmwgy0S#R{(xWWx ze>e1|OW9^qOwEGmnu_!@R?TCd^2#76q^zErLM^XAi>(w#Fu*!{jT=%|QGUZc)W3*Y z>8({>S{BJlP@^DHM^O=C{?g;pNU!3vOUyoN6CstXcHhBR_HFOy4YAlOo@Ax!)=;la zz0wm2&_vCZgbjA@0IIx$xp~LI?Zn?vT<-gu_t%)^`P#0J+L2d7Zw~&EBux#Z>pAp@ z?nTJo?U63Fkh)e{{j>TDzNOi-+!w!ann zVT#9=VmA~RrXy00rPpPlY4*H5F{{W{u$pERznn&@CJ}`-H(UIZ`Ox0ycnuv0G z_`nA5T}qUT3X0B3>&q!%ueoLs~V_3#GPY*JXrTY!#JjU*!p9a{Zz@ z9V5yxo#50*eoQko2~#;t?==_k(Tl*RphKspR{g$L&X| zP=5fQY;*1X&KLDIYeRGhbOJyHM1CeEpy8L2bu?5v{{XnWJX%D; zeZ5pTM7JRuQa@mch&z3+t;JF@A9B|O(inhN9qpIe1}npr4l0qby1IZpcib~7B3?6# zhBE;Tprck%$a{;C%SpO#(5L;|8L~5Hc1`YaDDr3qvWa&A)d6R9AtI`a{ovWzIRS7> z$hmxa&M8@l&gJNS6EEdZsK1Ob8*M2_>kyXl>a919s#R99ry3zWW#s~W#6IR4DEQ8) zmdi29y5CSXjYmWAh{<6kS+Vzlfi+HX?alR>p;J-N%0pdZ8P+6~U}kBp2=;@DD;^2n zg%V|?rv$ddp=ab;=$E#x9_i|}IrIbwemfvKsQI&xJ}O4>b`I5w;>yt0(38`^$|o^^ z2WYB1j#o(+nzLLc@Va|LO`%`HC-av`y;-)cSY}jheN#m%4CM4} z6z>ylT~%JyOW#lydXv-PGZhIwb+X6&wPt?QO20u*KJwl8MavQVu(D1`{PifgbIFU= zDsz)c^8BJtBGK%X3y43Ssb4QxIR$32CB@JG02E^PM2Sk@F~jl#U0?`h6-+p(Z9_iOZudtNNP)KaL@tqjGvS6d2Vhut$U60I3Ty!N{&3w zoKa-=MJkp3;44$}jT?$!O|)Pi1x4lA!E(|KNP@uMu8dz!MpOHc@@gNQ+18kKUUh5fEKAJ9|aKVG*4pr-@qi(6U$r`Lp&UIj=$2w2H$5?k%ZI0cVFDR(ip{bm@ zkAyuK(J^r?3c{(P6ttkrp_h1WN*;;m4MwQO*6!4hNZf)X3~{fbjlYbiQq_#N8`{R(N*5p4B9=Wdv?R^xb1@g$1ry7EVh^F zkw-;L@|;RSRJRti?kXfql4l--pNP4DHWJpi^e@dQJG%R$?p?9-On(xQ{KfY)_NT;l zy4SAxt*Y9T0*^(v*>%FHXpn{qmGaUft|G9o8?G)Jr+_HoA0o|;!0Nf5bB&;l2<^Ytp~y|9(l;2$lmXVjD6cF$JS)ZDJ2}NE8$bM0(-SQ2Hia6+=Y#)PpvyiHoUMt5} zJL(@J{(RD{wi7dPf7>koX4$#c!Bb57}$;3x)Zx&C8H3g51Su(n;z{ zl)-T>LwR)DoRgZs-<6H<{7;IDR8&E-?JR=lu&;IgnG+R?(9C&Fx^juU&3%DIAI0Ho=5am+a#N7n@0pHy zmD1x@iksa?xYi@mq^5SITZK~6bY&`xxwzZQiA!T)I;~<}Lt$eLW|6J@O%v(B91Y1h zuP5U9ZcmOqj9D$u`qdTp1-D1F2TARDL0$SvEfyR~?gs}-HvTI*&G#!Ea?OytdH5@{ zDQnhoXLYMafT~%{3iP5Y_M>;ExbPR;cCA#) zO&h#)*dC^&A?oK-)+e0WMIYn-3MrR+%=G^NFaH44sR7$vp@-ddG-`1u{OLPMC*c)) zrX9ycKVy+_024$l84ZS!qfW*-PSs0lB{h$KF+g;YOn`t1p;E#E{c3|$Wp^hN>9X&j z(uSyI2|^@0XeSO((JF(XL+tH~;+~|dxEfc;L8LAzO7$IDf&vl}d(IaNTrIvxOO7Zkgr`koSIKbe zYfCWaxNjq(!|>hEyvXECaYTKvX zv$*_Lk81wkImhk~?n4sc+-oiVp`B#uezxV7WEIsl2%@&p1g@r~YFd`#o|tfLX)>8T zgvhOA@}>y==_^HR0E&UtLfvBDWjmvLgNsQOAL05{@BNIM40{3l zu(+Y-7Q_Oh;BhLE6mH@F0M$crw*u)Z<|-&4jH+N&~Nqc{cw5>=THYpK=cRnw9G8 z7ye0kbc|)2o9&P^{5%v7zI(Xm3*|7LT;(f^(It=@By=;0On2H>53~dF0U;|b_p{9| zT|qGe;G_0I%DJ`hI@RwetGKyQJ?U}fACHV`SRU13Y$@!**3;mzu2J63MU*Gso$d$X zugm`cW}V|x?swgisw#`FuZ}2)WlEV*7e~f`_yeq8axjKP5NMzeAG|E$*6_^9%gs*1 z*RMjUk0fIysOH{R82X(`la=1RZK{^#e^PZIN;RyBoL6b1d{>j^fsc`*m3N1llKgw^ zvym_LwHMr~y}U+_sD<2XpEGr6(1|2SnZ;fXTZ*!I1>2qQSIB!e$nx;PTl_a%0R5hs)$mQ=ldx$kcXz{vfO-wXj3-5khHP+&gX>*v(Ir^MpWym za;l~oNg$-QAE;HT`pH@k5;mwN>Yz=`=tTNa7wjDk7FCr zUM~++(yQ6sqLcul)jd|?NS5-x(~U<8-}Hd5GjeNya;|9eOhpGA)!%sW*DAC@^%R*2 zl1%l84E^aXqT6^rHLYgl1M)5=wo~2R-qKt^St^+5L7&h@cJ@@Q6W(id0XsY@&8rj2 zsM6C3N|Z!^8i^RPU`E*hS^oescrhBO$;ufW(z?joR1`%cW{Xh%8A>#3pi34hkJ=>H z%~!TVV7uJz0n3Ahge7xO=mgABr?)=lt46ZCXo`VMj3m`#xFgi`H8WC~Bdnx8hA-HH zS#P4KWG%$ERVpuBlUsSZKDxJ5>itf$B>rLm_{3zpp6Zo!fsnxv@K1IXh21MZOqyC1 ztPljYnaTvZnX$KD1p$!BvDFPasbPF!&v5?X z?wp;GyE@XU1{LIMdD}30=^mlMVWYKx05wsO?vdM^q8;k;^X~6kgrt61n2>R*oi8GZ zEUMC_70>uXf!xCc~-%JVjCtSv3SxzpH6e<*v~QU3r@TBg>P^HY?%guA&dqgN-; z#MP`~io2A%G)juP#;weHZIGR!K+mKY`2vE50^?GNELUfox-n9}J7_NNwrh!F5zr)B}XrwLQ-EKKoNSazIFjk=?xRE)_ zru;BIQcU)6f2$0iwAXfJD>#Ac)TM(E$5#9)zxdzQXNrRsP4#m##u$ z(Ob*37iwng#^`C*^*xQsM38BdQKEM{yLnEgLV8Ah+<0;=EcQdT5#i>w{>R;M_la9{ zS?>es!_VrK7=kwJCY_8XVdV2@((*`t(bW>N(zqI zrb7L_&7*LUGt;iJjI#PC2}<=39=vRj4>Xmv=I?sIC>3f4$`Nfo*Hw2RW+YD?gOS{% z4J9q9TSr0LPn1}#^G;}@p?{o=)C~Rq~P5q{>qL@h2>RsGDd?Zwe7D*j*>+&6lDXgwZA23 zO#49clyoejNk3|PaV0xIh}jC1jB1LFwbZCj>kYV3Esa9jwyJaLVF<>7;EoY*UeYxO zv;!kltR$ifjiuF3a3ILb;{&wlmpMKuAyH*|W<#faVfc=S84iouOC3hyK-=<%OHi*G zC4H{nQp(V@f}N4q29ZLVY=j!AA;uZq!yT((s+Au=k+>3coMt@3hDUc1G}T+Me5uy* z094qku-UmQA1HAMeQ!4+qb&l2d0yq0ID5T1Rc`iz9E26|RdLP!-Cf4+H8l)T?xRwP z5ZEg*FVs$x?kU^~3D5QIh1(nhNh9w@gZdT~3Bm)4~-dH(rQlWy%l!P+)I6 z0(OqDJMrKnV#^Yr?`hmGZseY8#%xte2xK}IoHM^@&U=Z5Ua7oKvo3TiswUw^ z;z1##?U9sfSnvFUak-DJlv&O9+D=+6TKcN__g2R@dse4<{{Vl-7`o%^sUhn2rhPf- z&;$6#sCz-){yEHL5nDcz9|e_juGeOG+hi0@qJSiA-&<`iL9gZPDiX??*7tZ zY;<((5_xE`3(ve<1*O-qB#fyv>Z8?Hy!!iIMO$RO+18q?TFZADClFHNN_vAnk>@^V z*t?4fy{8rF{w2?#zqf4c6>Eh;lAK3H%AV>e)pOzOB9)s;R#;u$sii*ikEHp7Z8j#U zcQX-u;lFLT`$!vkkeD@&@H#IxcgOE8-;UmYPKq+=+$sY`<4-yTyWAcP$lMcx)zWh= zEkRvkebhA7;+~mIR8mwpKnZmYd4N5SNNc$Llf^-fb>)M@nmW-|KHq-tE%|K2kLvn*DQ3*q7PO4G)MNhX~ zro;?M$>gNl64QR-zUTh{+1WU;IQv&!-%KhP4FDcw{*4BRFTL+$-r+vqHYPGle<`mv z#c1D6CGLW%nJm^&I^?z~CACRtJw6cs0B}1wVf`rNv5zJ(sjrusto^xt-&^y1)Yx2& zp|rh=!n=W|hrs@oSN8t^c-erfa@&_FyNx1&Y@j5K4xJ4AW6BN9ZFystxNmqB`;nhV}whOC42lElH&KhSV5gin)d?(%&J}N|U@7#xyZMTa>t;=dvMNsoitcCf2 zQo?5kSo0@IC}ld850&kcBZ1Ej)C#VSxRWSeUu4EFPno$~mXxoWfIZ_+jOkT|+(cUX zDa)7BrMzO0q)Y~Y2gl0{$TdgFKoe)f_d#gBy;$O;ww)?HB|oea3juWyZj92Xce=lF z$X%9-(Dh(KP_+7qBuIhMEqMC@?Nwy_qcbb6n;U%8QpP!w!&HPR`-4SF(4>+5rXnP@ zd*A6+TYL6H7%$ML%M|1+cI@gXDQa6&{S??xBu~G13ijR28ihNG$g*d8N^1G0?LlIr ztE;L|YLy95anb-t(4M9su1F23R;b-mL8|BT+{R&tWXiZDn&w?3tRzh&&T&i3%Nv5w zc)h&McZ#gHttw^rjU7taX~J|#B4gs)Q#PBicP)D zmhx?PMSZ=nj{YxkpE&BKZf%}gvkY~cwFOdMOX*JJq|Ax;iOacK_JS2wJ9kFyREqX+ z{{Zt_6Y`ajI8~U{N8WW)L);T3%pSSMoZ6t$h)li_EhK<3LqQE^3!ZGo@5mI_`K8-5XA5Orr`v6!qP^8S zS9__euB~-*D{<cG&s4!c~QyW~|*zOw7pudQlda#Xp_BHZe|6K>6{qO@OW7L+B+V%$gg zbohulp#wSfDgDiPwl9U-ELO|@O-GJ3bwP2K>Z;kNThbdqKyg5*5_>`)By1EVJjf)F zR5e0>eq69{9xTGRHs1H?F4+Q;k@5gRAxdPRqK27dN@M>3Fo>+)`lhO~*f_Qz-xWty z_WIkJpLtHn$EK$T#P)_RT}G=7q)CRl+cE=?Qd3&c;@?xDEHaR%9CgjXJ5m!6kq5~y zq^XI0xFIF#@%@>50nd3>-DtmZwR@QUB~P*c0O<#E7Ur9PQSg*qVP~|@R;5r~GpCo( zC>737Qt+R*TRV2lQq4Yj@j=Zck5{Mp5vJ$HVo+-KaT4VT`MNmZ`jIz_mn*afJkSR7hsak>vm+X(DlA?%7A=>Jyv-g$6G&q&=i!ZZVe@#Vy^%Hju10+;}PW4Ns@81-> z{fTS01+(yM*SODZ-U_{+1rDho_lm<7kmZVYs&mlz!ovBXT&J-nZ?v2)`mX!aY^l0b zxs5ikM5%~KSx7&^r#BH7JUdUbs>eC2ne}>kYNnR?_uKnoRaG4NEq|!^z@#Dsizb#v zidRp}A}VOLQgB=ltE=Cpe5-w`&4$xBLhh>dYXH!CP3i&pnM6I^ylr)SRqp_O(N84< zv8>}Svtqx}scGtkjc%7x_oDZp6nqygzqTWwbUTissQcS!-v0oYezbC)TF8S%3#(q_ zk1Uiy5L;hBg?2C!mP=1!v(S?XW5QR{IF5@Ud|_r~;W5-t zq%6)n*FK5i4y3!&Jm7EF1ycZth)d)iX>Npps#nv~C3_T?0Z(!MxNqtdW6cUOdf3pX zD@ik!h8%3kE|slJrdfS*(CXVLP(kRk&;#^=9XSO{R=Ac3NZiIbFQ~vw9%!q1dEkM%^-~)Gz%F+sF-u8Kr$5pGgeabC%s=9fviY_?$eGWEKP*ehd{qa!B*gc}F(7nIrk=+E8;yiP5$tW{H z8>gHsO)6XFK2^pW`}$gT(p_Q0*71t-%483=OBD7=xW zDeJ4ZGx{QS%V?BJN6}EHhgBRiWQuU|ls1h>Jr1AtK}8t^bRjhyZ;~-bnO4(JW3}lB zX=XxFI>8vbksQ>btak>%@>%a53fVYc61IO%Y2|Hk#}m22NF_#pSl6sA8tCiQcsG`2 zBz)zypbAJeO>bqO*=;*Uod-yD@X#uxbGvnFnl4MkZ5OM1=u04}Y1LsM%nuZ6+QG$BK;Y_ztYZ9eH9$gaRsW0}sx)KriHTB}OrlCHyWJz|S%W%M^L@^bc8 zBbA)qHZVm9T4+->$-cN=1^$TL{_cm>{81A82 z<6Q5KyKntk9oyxe)7MXw<2M{V<;#3M$&cIks`&R+?m4TKj=q|i@Y4+?X(W;-r+|y@ z0|v`+MTK*IbnCOC<8$BMaJ-Vfp`-$|A=5QUM{xSXnfB2^aOQ_eQ{tG87%Nq_kqJ(L z37Tgv(E$^v7547duc}`14;tmXqeuQ##Vx9?xZDy^>WP##73JKbBOmV9d9hqx#nRln zUjVjV%zeM*`EB@)*}jh#cm*YWmHnV^ICWAooc{dn>b9;A}5g(D=)BEts zUcJ%d&cAfCRl`;LceUWQXBwiiHo`(>xd{PDGL+-aSdY4H zR~5*S)03~7*X8XW1|Ce{>d}La<~>G&R);u_**1~5rdsx^6C8Vbxm+7twRF@qE;UM-Ep9vLouCe+ zl^FJ%*w;Iv{Pn}#LmcfdIs&{>Oo4pXJcZwO~yPl=ow&Lwj1nZZt?+Pbmz=v0yA(ENylBH+yaCzVpW`-r|eMH5HuQj-e%OJgkTa^q)J za*-{*s4AK8GYavEXn3`o2BNC3f8Jg}IMKzR`DmqvA@D_u!+ zjTEK02{QRY_fy3a1dS$uA$jfI#cZ}zvWkQiAqV+jf_i))XWX@0F_^cz!BEG#+rw8& z`h!AfLdo4JO!V&+JgmXRZX8+k*L^WttS?F-#TEl>0Td{~77BM% zNkBXLi3n-PhCHF|;b z(l|*g;n5x07(lqR5P`2z5;mVQ7WNv(tV@GP%N#p_he zXKik>(vcrU9sTNib^NN%bGp z0z{^u2jd0KM*N5_MNy;(xOjPDFDg9=Soy#ALHMp*>uQ=mszFZgGJ=ksiHa=0 zGW3*x%Lo;YksnJGP_Vfds`_jdIfqaH(P`t@3uY_`8zQj(TS3_ALhdYNT=Ra=t%RTRv zafI1uuZ{+(DqE;-p(%KycEx&q4502s#IktChg`F0KN_fR)_uZpeq}S_#mnwGJxCyp zJjGOH4AuU2{{Se|kC}?D>h)V`+v1EGtg35j5~c1V^86w8Pee49%p#>Jaa!!|hbOgO zFjZEymD=BGqRUrqL#|daQrVksGwA}Lw4Fp74A&dMh#LG9K29uit;;TU`)<&O5CQyD z=e2u#`;>cus7=oF-e+A$a#rW>c?HpS>Q$8hNl~a;dv*wmd>9@~Q!mi_XS^bPI={7gth&OdBA z7r1_;9nIf4%4z7gVf}x$r4Us6-s6lVF4FL4wQN%gB59((U0D9JT#0`X^+w&t{pcJ0 z^&AF}M9@D3+&!ASx^R8{+>(3&(K0#DydQBqf$Adj$rZNx6#^4!wg&B#s6i6i6G)U9 zePRzG%5QEuyn*o1UooAs!rD-_AWxapsP9#Athl=ERefuizm5K$t3(q zh&MUs<|M1kHqk~XzySCJ9SK^NBw*o~JEG^FSEQ-)fY-g@~dqENCtgJj7;2(u3_qjvna{^yB+RQiiIRcq1{)FHhyL z1`jY44lhp2%?Mv38}`GKWm# zqgsE5`*qQCtH!)>gl3HLE>X)I&r8I#_5g?M7Sn01yjS0*wl@5J$E%`-s@&4c3qFsZ$k-@S_uvhqL`~LvNK|6)=>vp)JqT6Rp zn1vO##VA-x6}x!aRnlFj5wlG9$xtY*ZM2RkLrL5clK%j1&u;8nvW(K{S$uM-Dn-8G zU0Ax;0vj?Z9-`Jz2ZoU4%cqbH?k0#EH^B+p8vq{wis?aYI2-c`6(|m*j?v3;wZS=g zDJ)lu$$r&+gzzWZgN!+j_sNg)XS^PNw_B_6j9QNB%BgYlQik5CX>=58(WVeeR_pUw zSpaqdQx!eKuAaisBWUa9qB#YXt8vl3Hy?TW)638Mf5BBYEuH1Od6L_*z9z};+;Np! zzxbb(*)A1!>w`&9^Mt=jv?wJ&NlBcD(1Q{Zut}j63cCg5w!?672k}N#X7!j{Zkeuc zcCN@T*&3Bsa>^^JUahXVR39mI>WY%Jx<~+SB=~lV4TX`~9PI~!t=(m%bbvIM8qn6d zeznp+**%coun)TXNKd8(r3PJk;SFl~+>-|G){6jc0t*uV0B=p*QxEP+`9uC=!z*g-Y`XId`lSH?Zj!>a2S|&{O31HR z=L&$VTxQB^jC_%?)F!HD+rHv9%wvKsJa39&;+H6^rKJ|z==9odg07v!I2M^CmjEad zoN3u^S!;IIkSGA50cD(9wbz*WWcMAoxYQG^6C3X5-}f$iw06#n%CX8j9ioPnkLXEx zPE)R}kWy+SKme%rmXTY{-kHuejzZ<%W;nQ3@bI(KM6d_HzoHzX@x z{81yAZ~9oz{KNN!0gt;r?5%J49s8Ak@x^_*)$Y4ExZY{KYgM~By;M|CsXK#(7f03v zyXqqG%;b~kT-Nm=X8paACgx(1;1CE>f82*J*In*+N38p8loe_#rloFZN)SZ0qz-~S zr^fIOZP9bdI;c~93aaC`qps7R|%o(4IY%KwTfqCXfnH*et zl-)jCKuJh0ET6&}-bz~<(41~R#V*(S)Pj=DGECO#=to$PWChAD_aMgju7Wt9kL>o} zYIYiL)cTJhr_`y+L@7Y`KN!|=kh|!YnKN_7?7470DW`S5NL8Lo-1{2_ z688lmcte1*8ob8EcE4L(r=U{Mr#(PHGgPBVv*2<-(IdPUn|U{WBfNLBA5S$kx%c`O z4Qi)hszfbmXryV-fvM6Lbx7L2<5|frUa9Ypv&!m=s?kc1OeIItkTRU(KFC}jb3nZh zg@wM=PgO2B8rgc}TSX=|!^BnHN?T;ipUt6*wj9#lrDvRnH}uyBXw_T)00sT7jYTyj z_aRWLQ?gpr=+GHYK`F!rN0⁢?OEecL~RHIdSed?TjfmdiLqYyk5a!_LyzfhNP6w za(hqd8J8)_T}=rs1S$I*L=jFH=TwBrW?9xXkFx7@N@!Cuoi!SP9wg;GvdCp_Nj0G> z(0E+*Ht>e9YO4#}x43&%-ks|mkflplA;)FV^gSbC?Qd%vhHmE#N+w%}+`6tRx0g`2 zR;S)@DpNaW8|8FQHYy4ts)~^MfRLI_nm~xA4OUfD74A{G?HxC&3Qn><5Cpjxp)_$t z3}n*^v?!mM+VYiNkh-cxH%_HZDl^w#j7ZsC$3MzcbL4gMs` zERl_kRU3ziUWn@J9l1TRsg%!CuSj9j5{ild(3Z9vTAM>_C@I~jGU^055UEYeaMZm^ z&oqsxOowv^Z7LF3#dS^wBTrEKh*{Kb0L)%I#^o@N|fsr=cEE95$p z{k&+pT*yAfp49NpDY{l;_l%96OR`&CeDVUXHB2g|v4#tUYhsy5Z3tNsDL@h(BOb?h za`Jn|vCVI`O({j_UQPC=i?zl&c1ZDX#?-jD>!!UZKTr#qS1k56+^9`85*lrnT~Dnr zrxsGO6iUvkB#+E>F=ylwi6tZ|=WeWhi%`^_x>4H7TvpWd@JvEMBvci6fG9imccq} zDG*^XI*vlzLsk^7V$R(j)Fm5&dPsErk#@F|&=yOYijOM_icvEp&Lm@Zg08)(Jyg4v$Th%~ z4GTq5RkD3kB`5ULpHU6H)J_QqNUF!Y>(j4j3TJAFjpY-U6FKae zJt2o0lzJ6TpmmhK5X`C80+h7vZbY={5%L--i%k)2$7)b!pOiEmL`=r5NSEG5`N$1U zzgT<{qrpPx*2z7m@|-;9RG{QKB{d_ANhv7=6V@Ij1S8cdX@5>q6xay^@zN0Xf)d`s zdUz=z#QEEns+K8SdTgkKsH<5Hk&^cVgGYB6a2l)M6XeocxC7BjET0y%XSOF6gdy_k zJhJ(8k1dgPhr02i+}$i!u~(^8amxHNRgB#oew~7bH_S;ULVwvFi{hNUmywNiS>8hu zV0XbKF`5S30JzkgNz@s3i>SHL4AvnzOARiS$s$@o_JeUj-_$imOP#F_RuUWjF1=#g zZ4#kmU#b~RLr+YRxdU05=pt?=3M#y^y1F3`D%k2h5E51VKoL2ZC);a>#Q^D^*J_h@ zJZEzy+I4w}J5UF27y?^U;EuPyZBYIg49c3XNbNhhp zJiMO>`)2hk(VdR&ZMPoDwpa8j`|%II4;}HA`2n`YD(5rM8S4+9weDckK+2TV{mH?G?sLTw)7=*$ zvF=-j?_cFS3bQJrx>Bpt+eW1Yg{MUUBd^jwMn1%T)H!A^lEr;^eYio^ri;Vhd0%ii zH*uM5Ek8D54Jaz9liP=~o8`BY=qfV2F6V5rEk$f4+6tD^9GEFjnG%sP{W{y_@%Ws6 z=wxYqJjRRpRndd{kCx?1?|bY${Gb%=1XCRay9{8oA0-oo+BXK$mthkThwfZ}RNuZX9B_$~+i>18<9 z9qyx;tv{ucrnm(*(m?c-SVEK)IZ8z98((?zQAaZ#9aFWn{Aj;Pe$ug*67YLROI_Q1 ze-fl$+tcoeVfKE_el?a=n^mT@=bpxLH8JZj%HLGnY2LEb0u*$Bu>9+c@(g2aGE!_U z@$*@K+;`e{BgKZn-cp}7u;*^k;Yv2!uNbT4!@^9&+_N#%IfG@ zQro3zw-Ti~qKSm4B{E3vVl%qjqmE%X`|WI{Y^CCW16q7a&;HZ?(0NsdI{vwqVTr9Z zHPE=yojjFwdkg!nXI{o~TjiBjYM1c~#ainr?i7UxxLc~8LR)c34g!@Sa+wET2*G<( z-mD{b-6kp@m|yWko!cFmSiH$Vs1^YMiD@Ng~Q18q5Tw27ro!QCP(k8l9vL; zEp?Tj)~lMRywtvg`i}ZC2uKJ31g29aQKa`nz1(5AAL)2@legcC?su|J)^=y3p1uKa9-3L+T62XPX(tG zV#`3?9p0DIZ~zpQ0yP7|QDbefXw_FGz20gQb)92$6)Y@JrfTgidq7uq+ndoySYH-B zMmpREx~A&gH8W*Ps!&Q*EhbE&uQn{X^b19Ykg<_esbR&w(J$4NcALAW)GWB$g}tXq z5Ms}a;{lIrmV3xK)1+gg8dpWqhXztRXR+e8_uKT>PL@zdjDQpUk);voqQ~E>>*lOO zE}6yXwUpWe@JNSfyK&VpIM`E0u(jnXnJVc6wAK12VW!cH*=j44kX=zA5fz+&CJb;cD@4g~ z22Y?0!aacVSNZdhYVDQxBI~}GEFnwPCw9Gh#fsx1%gCzUs)2RI-G{npIyJ#fuX{Gz z?N1!$l++v5=+Hc-oJ8k8Op*C|MQ1eu`g28Lz}&Uh%?F_?k+&{)ilx4qhh25%)KcMD zQ6PgUYtm7NzUD&W3iWd!g@96&Aqb+cxG4H-nG@4UR7J&oGZ{*RwX#_%?#6zc)7}q? z-*Sq%$Oa{9xT>zPu%O18>Sy~jhkvGS6;b1mZAq%0A-a_i+IH>K=yZWjD3Xegl(w3q z`~!{eHmsh4rS^$zIE`g30fTHYu8u?oYmrAZBn8pmwU*S*Jwq`iB_d{8^qgr{&8q^# zZp~yRJ)EIX1p>^qNST+w&L5PJxg8Hh3GM|o6;3wvlnFXWcQfx1&NWASVMhL7_Wfhz zJ7q(RQ>B}bN%Y=G0F&3e1$~c>R?#1h%gt*2%_^@lN^JLrE_F6rlW#j`Qix^UWwv^C ziiOiAw9x9+so0BLn>RPo{{Ru@rkAwOaW-oudla`Ckjm7cLJFm<=iK&*I~yo%9IPjcU9>sA$py`FLIZsjFXn%SvGf(3JaAwUR@2cnIXg>2n6b-BpI1R);oi| zL<`QUGd0|J4|(x5 zRsoiftZLw&RzNm?XoC#Cl);Sdpj3~_6v_VpsO-9AqNVwd!e%P>MzXC>7Jt))V_wCZ zLb)!H`AE|?pEtyjD4v3wD63bD{OYO4_aM)foB^vHJLDnIkN`jI_(U!1%NRnJv}t^T ztDVbTT3*T;ZAzL;qI&k+B0A06j;dPg(iOh@WUOwTp;V$vbm>n|0jY^-T|!hv#HCXw zxZV@zFMA&QXfphk@a1P8QDnBNOkT{2hXA)&thGp~dt+zHlCMEZT85@$T_TN@$4w;r ziS+q?3q!$YBe%7q8aEI@uf$XLe$^qp%JMUurOa8bP3o!Zd(|kWvlS?-Z5;z*k+3&- zhUfK%-BboTI+9Z`?Ntq~+fh>J4XGfgl@ZuLce3qjvfH$o0vYUjf{!rf8@5$?LTg%} zs{J-P+Q*R-B)G9XC*WZ?IIC9`AIao#A&aIPmoQRd_;n42#`vdZovAvcgo33M>oOvI z%3O6&HuPYiP|C9-ymD@JY{x08l%-v25ZA3a?MYXeUqJ(tK%Crw``-7y-?y7XKHR;% zRewad(=wFiSxqsWL$5f13PZg@wKMRD$z1t5l`9P4qeVgaCEp~??sXSzn(t9}ud7;= z?Oj8wdBqhOnW@4|ee|oJTKe@w6&MYT)*5>1>l7`${{RJbF0#j{NB9ecGx@?MhA%J^ z@SBdH5)j%KE;yk!g zL!zx+MjBeVQM#=t>KEuZiJr=l1bpQc3$8mQYQ?SM`-2Y3Ob97M#NNek)fYByT(eZ& z6{4L%Lm#7i)Ow~;(Z@gm3HS|uF!h%nZ~zcM@4q#Z^(OM1`Hp7T0k4@>ZELpBSgo`+ zdqo;3ZFa_x%>@%_2}4abHlzSP5ov5dHi2i7%=sTz9Z`vJk|#NsVZk=O+)9#}>D)lZ zB9_BMDW=_hZBx^*x8He5ZH7>iwXMe#5R|1UnJQ9<01l=V6|cBg*B8PD)TW=9`8OH% zrO4Gd54cVRu4jgoHm<`Rnvi!%BekQ!a<13rfEm?qujsd|$N>&GrkWDV8nXwfs_UKvc?e|7 zyccb2>D*OwGm+Zx!RM-y9^BmhJzYkfT#ys^$}F6Gm6>-ZQa=S}I6HyITGmq|=@(eK zRbm%Lfl8YbDE%sYy@sYxs+y>2Bw8=X0fnV0=nk;O!17ZTa#W@+?#$hCBS=OaO*5=0vAb0El;SOxB)w|S<*~Znpz5bb6Y)}>YL{&kh}q@<%fc>ryjs9HS1zlF zS0C+vfGKL~X&kAeNhGAG6l*`MTcfwwu-w*4ONY=EaTw1CSSW2RCCWr6YC!@(ZYCIf zrm8u(zyKvP+^5F&ntE!s*``R)=^%S}!;E=wQFpeXr$s^j?(jNlaay*UNIqf=x=*lx z@1SqAw0_)WWQuSHnuAPfEZ1yh+-N3yX#S%pe{G#?0# zmBa+~nF-~?Y0{m904Ll2$U50aWDdF~ZT7;c769%0We*2aMFl)WsGrG7TVcX{73MJM zDK>}Hpw>lusj=SKxzej^w5tk~`ha`HbkmkPpHZ9PK#{_ihtosYRL1JYH9QWq|bp=~UXsdVUmad&v!y!7@K6rnh> zl){$!H%NYO35aaK5tjF4DA~yTJl1htM`vY?)fJTtrH4`oQdAZ?N-H_*7P*_eR_)r% zys_N|H=$h+dl>fNSB+w}DteOZrk%FYB`r#yluXT0OT5SiMXL6G2aP&O1NtklwDA2a z)-m@~cQ%s&(3Q|Y~S_#MV@AtRQ?=JK2iOxG3NTU7k z`+MUS+;@jZBu%5_6(wADEoQabXjyHRkcA~W$N{W=eLE&e>Y($vDdv=tN=R}OG|p2z zl@LG&Kst!|mu~k(6^n>S@bFbpdxFI5_ZnK&)S)Ccqp1LVqxEO(3p*<^!aC?yLHF~B z`mP)$BBTWct2oCKx`NZyjWohZ-q!GR9kua~-OFGA2gQFmw~#+^9~H8P1>&1Bb}fb$ zrsX7|shte^r${s0C&eL_3ieYYhf0WZcrYh2=-Xk$>ZkY%sS)VU^&3qO&xq)SO@@`5&%^Q4SxR997WDN@bK z`r?QaQ7-_Vy7e&oXx^xIOxGO(L|*x8jz9Vy2~~@>MNM)bj$Kl=|vd{{VBGN6VkIZ}9~)XWRVk#;uIdsH2(v z+VBcO^BB%QbI6?&^xoPSXr9`vgD8l&<#7K12&%i|`yY{zR#yi8l@IfOx;HQ1Q2SWc zH%DPYrzf3K-f~B$fQadNT&LvH^t?2iu>YxNXv)=@0fy0_G%%#{*n<({!*Yd0!WXwID#T>GH0 zw#|B+{xZ1(+poqUFQ5%7`Nte88#B6bCRbq4MoQh8<6V^FIF*PXi zo}dH`WJD+dO;e!N4;6rF4CMkiP^B%Yssv1!cyv#Wi{)Gr0rN=s9_~;T;)6@WXDx6#ruSC#EMlkUhaL* zT{%+F{h`SgT@qaNI!Aby0&X}}HOo7Y{G))PsT%eV>Ki@_*YS%?gYy)9xAN+&@7kc- zY^U9z64Ea?gZW`&e3jpxRGH<|{{Y6-K8VQVDa3`l{7@HSwN9-?!3f(m>)*m30M@D^fCakQabwp& zQo=;Xn92`=M%6bvJq2_R_VaxL%=*y1!Az(2rYzn8VDlgKj!oa(q2(NU^5E1Vldt{K zVk;q08ZQDXlmT+Zc6DLbH6wDD)8`bowuYnth%O_m88gy@$+0Lo_ry}Ur*a7 z-8Z$qE8?7%%g;IWhmLO6i;I;un%lBl^(z~Bz0zo@Dp6WUkVw`&7uv2-gtE0iR$2%? zH2(GZN%wp9>&>|@BxJZ&$oR_ucG}PoY2c)nCjH`g0+AIqejLX#K3%xB2)5VTt*fKA zEY4m<)r4+LyQCPhWxMZZ3$|K9UVZ^&TxCBuFX8UnrcCdkD)pHd`^soHsLTd@8+x+O7@3`)j@S z)BCcxP?1tk;r{@tHN`&H_qs$=T)CT;EgWrVDc@60!arG2wDppfq#z%Rh=J_8VBxIq z4GPDY`=V>yS!HhN9pFz-+5RcbcZ$B(-rvBe$}(EcZOEwx;ZF5t>QyN{i!?zAkv(@r zw0JH%WGeS>aQOcKa)f6p?iO=Mnd~t63VzjmFvMwdI+`J19{@q^q&q}D^dSf; z(K?R*0O(+pr#h&?ibSZ1kMuC7L}-!q4d|72e0HY3p%nDgh1ZrqZ2%HfRiM%hw1M(C zHKL_fGTC^o*k0p66o|t8wetQ&Sek1$AKflanIF@$M*7_;JxD@RG6zyXkc)gga^bH6 zLAJhYHooo!q>-0Ichk#X+@zih?I$|E)(ScbMAuiahh3|rWqZ|5-7x-ds!}|KCqfT+ zx4VwI2KtVrP#w+%&aG*p=riq9!figvz3yAe3PrGalPyaB07bO^=#I%oSop{G=vI-! z?nKs-k^Qs_lum5QX`Rb*3M*AO_1dX@H7*SSQE-FPf)LnI}44WdV(i*zrKO;%TsY=qPUM4R^eV6Ck7%l96}ZM9W4h1@WFd|YmH z)oxHao%iL0?R}LYXem)7s1QeE2;>$_knwPwoe75|mO*LNEyi6=)djS&NRTrihzi2=%@}4zap607KS*%Nj@=E^z>6E;z?ZTh+7uwCsN>T2c9TFqdUkz<@nwG_dz<5zEWCeg=23wcvJ8`nu2sy?P~PaMSx_uGx01A! zg}8+gBQgZyYVN`(L|eGFx@e$n@8)FNgkk$dXzrR2mVWv9#{U5B6Wo?{oa7v3Z{g>x ztWx8&TxD%i7f)+y$`;PmIMN$Kjwwq~QqqtmAUaM3v$gtYgl%zf9%x&vk-NC}p3u@y zLEx?f?6^HIU_W*uiIs~pWGAp6<$9r~;S~<$f7e?62m9Bj`!3*YE@|-o<;sf)m1D5&G@=HuKl`U#f=m1Q~G0S@?ZXHGnl>P1eHmu(BK$n#KWmQV1 zE3><+Sak}dqfG3cuUP*8WrP`t^wupfahM+hRyIC!xWG$c$Zoj1~jN@-0 ze6r5^moeU-Efr{6>Rn^pXD86pG!*lhjrc9q{{RH;ZpaUboLV!mwbt&hfLZSnoQ#7$ zC;QT`ovuRNTBa&e66Cz?&Qj7aY;;3;Bh7mkHune`(tH#-a*pki)v3A`6luvmeZF0K zYZew;mjy$)zhq%Jb}lxcwYG};(!f#xD%?bqEp?fLnQ8#3RjbV8uRB@dgbenXa`%6xx6#qLU2&{&EzwZ4g*@x7tmu@b0H~5q zfC#D!V&_(xEt%PN#TUoGd9H&!seP~g!dQ5^pPlk|>2OD^&8p*X!s?Qd9j zjqS~{LG7rCD7c-QU%mR3msocXH*n9Tw-j%hf;`E~S%r0dZOFD)&q0st+2zi1ety1wLQW|us>7J-djjWq9kRe(I|(Kaq$TN+R0sMsqT5o%ij3pfZ^TM@eZD z{C^VbVdknlmpCp@N+fO0}sG%FCKdPP&x!A%=IlqB;Q{dhO;B*B5^29*D(H_b+t!{{War zYmWM6JM(2A06OdatM~%Auc>iV)Xg-}n`lS@N}q>NOkQuUNtBhJ@l0Tj*`l2SOx8Z) z+bRwX27_3Snq_shwAD1%%aNveWn*yhJ$xdWo06+r;!snf18BQBQt8;)T8WYbqLk@? zI;~pT&F>YWaVS+|)QGTAf-NLW`-GP?u94VsF&qE@`N0 z*<~?Z#a7Uj1G5f2dltF6+F97biS*SYta>kv=UICQAnK{li`sn%O>RqTKc#VKnBAc3 z)-+6eYE&*&?Ey!+ibCCEiE%r;$F`b9?!}>IIbcDmO6AKmcGsOpl}Sm~3YcweR8_0@ zJOv1@{EL2~yR1{x6oe>%fj-CM5I@x(9Yt;b0B^?jSIs&Ye>l3DwrTIJ?PX;kyVt22 z#g4-=%F)WFkmBwHmMQs$U+^3Ct%`xPYb{NK!=9HIji5JC46>GH#-e$YYJmHq~~i!5?#; zHpDSH3L0xQou*RK1e^_%DGfA>biDfTfEsqw$yA%$I}&R4hZE+XRDIVtp$`?GN?_WFu%>b6&ckR}Ul6r?Cf5EP*! z3AE0LcyfZU!d?+sicxH)+uh^#}qdO z@@T2j_loi@I7(`)? zHmhsR?wL-5;RfRK7q*VB+Jzg*F!;=*9^2d+M?>USPrloI$>6^1Fo&%)oeILvt#TGSjZTVwPaoXtyhC z!uA4|77L?vev*S=0U%}|#Nqu*EnHm8Wv$Bi0?VHFAo`#u&;~UFWx257Aj^5b_FCq@->7 zMaCA;1wxeis+WAQf@xG4nM{nlW9bc7(Kv#kEptzPp>BF*K4ZB2j7GVEOGQjkr*)OdDg^d1m^itQ(?z|0UBp10YMwi1J7%`JFVQo0aI{mjf=@xDNk@~AHj23~ z6ot;HNUE&Lw(V5qO>>3UT<%N`Z%dS%cy_V6Cxe7CfkDcgbylr?8@&pYJ4ieX7i)6h z;Gr|P`rA2m+sQnm#1}puw5vSIo5SgrrrZ9bRCY1yEjv(>wP*<^ylVD$WsIwai_O=z z0%>+ceL+V&a+e>dz;b#FYV}Kew)#(|p0bhDywbvuNRpE=IGAn2WRFCr#`wt&8mR&7 z;iE&x)`rZ|Kw4$eTU;sa@`5AzLd@nL>PnY+GwW4`;FT(AC^~E4W3uy*eu1Mla1y&%W%D zH~MOCNmD~@Zb=78?Uu0znQ@?23_Jjus#l<^3}tMG6}29uw|!zZ&EmSLJIiylt3%;z zuv)SD;)e)XWn~H)>#&HjiGXEQW?0@_08y)f{pxYmtCe|f#canvt!0>QFOV|L*f@_) zn!ezl(WSzXB1$)sBC94&Ii6ZNg=-ke?V%5N(cL_hZf$$RC2K2EQUNEaJ~N2PC8<-k zaqWyI}w-xyhvg+FC>|K$=xp@sZW(ZR~#bn_W69R_vT{*Hu1}R_#cFu@jcZ zMHR^54w|aY?);*+S{tgSLUkbgB1B4|8qInlt~KQbEH{j=DsrF~G3#|rOA4N;P(c8R z{9)g6tx@xmWC2u5`K8B^a-4@SzE>);vg=aw?z**1lC_dL_z6k_Y1XQKID=Z0)@r&6 zSE{S48%<3$sfX&_r#&1L?J3>A_oXQ8vXH`3vG55l1AG@gJiE&oUo&R+eA_FmQ?(WD zEBbY?D4Kc-S;@L~6QD{EkOqKE>UA-lTu$j7QoSK#V;#IHcBDF1Hsh z78@-ybry8GRN|?Wr#2Rnr4m99P@dA#CNR@Dk`F~h=GfSp1KKE0RX=I#5O)Hp5ICi(Rk4%XZt+9nfz3k; zZe8sfv%xCfAWlQ1E@x3N$58~C_(v0-;?$=JmfdCA5cbtAuBsBkcP>Q89;O%rRI*jf z{gt>6i1~BfjW%t}Ssk*M8R05MuNE9mwxxzucE40OS)(d#sJxs9PMBv#-l0uFyYfuRr~(wyZCqoJOoDMpt*_$d%9E~?pJ;feQPwbh&naR{h{^!!oQ(%{{V}sm8{nsRKgId`c$|- zgr!d;5%@()FXw`Wh+N%7LP@tIt0A*+cs53G2%oI}i_mWc=aUb&w z2ee0!Dy$JYfgrBzpFY&|oZxWEuCi4abF6dvh0rN!+;!4M8FiNARkm2f)2c=z3OjtP z^zA4RV~T0~mWQfKT=3A-pe3Rw{8EHJ9;Yn17U9(#V_bhxaH?$$Q>jS=PeUATFu4Vp ze!RKerkH%%(3(z@m7%|Z)r~j zp5NwP<3%KCuoe?epoT&WhhwZB+d)-bmnQ0_?iQO61tMZQ_ha*nqS z2BxdH7t={Kp+n6rWKZsf8Hxej6^Qb8hEl3U@uwJ4+0cUJKFvXtz7mT?rLbdFJ13Ab zxmu3_F-2`^pFy}vL%AA4E`b3;+q2fEJJ~HWHsISzl7RAboV*|ikVYw1Z7|NE<61|B|=U-lbhX2bWu@E`w7J` zzJo>R=aWDZ+Px?BV@b$~m>Pk|xK4@I!!!vf_!Ity9R)WtYMrUhmBf?S&+vkLLXMnJ zD3QxbOKrLnsgj_n28bI*>Z`*#bs=x|iv7@9UIY>XU2tA?Ux!>NE;Q~DPL~pzhQhj} zs$|RI2XX7?TgVTBp!Y|LLy>{ZBU&zn_zO0s$?aD6lJI@1lqG3g{{Uoq9}#jUj1a#t zd8P^WH%?*Fg-f^CX%wMT1gSkI@QG<+pjF5ns`H4kdVG_VtSr3PQW|+GQV8ln& z2N4?dH)e7E<>+x;SNnD2+B<$RZP?OMq_&RIJtr7WV)(R>zf5?>F|LieYehG0bS<{n zM%jq~qvILv)xjlec*_vh(55q-ELQr9El%cy!bv&~l8a9qxqFQj2cB6%Bj~wA`;J-c zw6xb(5ZZT=xg9iCqe#_dXmz*6c{VyXD@OEH34}b&s+|imKu)`SB^jnl*uAa`(s&t! zC9XdOYaY@0%Gp72rn9?P0)$<}lD$MgC3x!W7dOth1hosKoC{)7JBc_o)6;c;8U z;qJ!XisWpWwJk*oDjBE_A!!Fg@c75(YptCdwuB~AjL6A$kwTkpLe8jZsUKUlKmrLT zcjcFiPvPueES(CuaV{|)nsLd?y#EtjDS^tR}L#MHKY=$EbCgBzQ;c57?(| zFQUC|)Qv4?U00I6{`-Mp4h!fQ;AE#jqkbXEI9v9#T`R9BTAl?&t<;3b>pwA%+Q`cQ ztykbJ#ksy5u*j($@9&A*;H& z=B^rwmtC3pEks0Alb0V+C0i}Akj4DA2L1#4(Gztv4U@D>;iL{EYB|>O72&$DnS`c) z9U(&LtzG&TBB!p3P$@^2edFUfB_9=t8kJ!mZ=^X{nw*h z&^!XaWVQ{tOG%d0>LotWcwzEbl!UofPr395E{RG0C1`#T5q?$w0FP?L7%g<(l6MjD z?GccbW^;fkLYkB!VqG*MK5(jV1?sMBvUPAI{Na=7(Gnv-pUNp21gYeS4;XATBsM1` zc_zfR)KbW93KRQbP`q6n`;2KWs_ZWx|Kbarz37mbG{HuJq|!&X)-tC)7og z>I2%UpGEDtHkUTSDjL#rP>|`~EA!~(Ym3swWnR~Je@v$;_)0C6PXeDY8wLT&J`!-t5C=*jOI&<-SE~i+t=-suSeK6u!Lud+^ShpLjbCN zI>N2xst)cdYMweed)?J3C~ZL}sQ{6u$|MEAXw^fAT;rk;Y?pRwP}_=9CoLpM*GMiI z7+eZxAsd86bW!%$w!YRhlKj*|Aer?f2}@rHwaD+v3(t%s-Z-Sk+;ZJlRgT>#=-Tyb zUqYLvPg$*ENe~6mGDk(rJdMITLtV)s4QiiD-Nm5dEkS=tFKi$`_sA1Z#c<#br?c5BFFFzE-WW1luIg<&m z%(Hv7(vq)mZP%(_46KjLl!TC-C#Vr0aeI9B?vciYU2)uV4Q&08M!@0WK$~=>Dcnni zN?+ztB+hXWBC~1WyhA}M3P=S&iD}fsZ5pn=T_}|ICsgamDo*9Lgl-bo^2!Q(=%MVH zMy%Lj#gs`0UYbLVZBueQ0*{!XdPyZFUNEM&R2-9ljOeVFOOxoPp`%?>?h)xUgp#El zbP^iD42|#6ppv8Svo_Z3E_;9hN*ov2Z@662#^zT=#?{Q@j7!$NKGsk%C+kk4Y zoi@ltr;_tbc2039s3X8CuLtd}R|#zB3z}2}@}Ap?k_M zIQA1`<2-K+wkh-{)V5ojL+8#FGT{KvPSFxF?U~z4Rhj%T_D~n^C-GL-lzW2ZTCOax zT5p%ZakN=!eLHIblrmdji3XIU0l0kNn6UA&+lPv;OUNa2c2lBjxN?gm&iDuJ4O5Lc zCg6 z{gJrWhi(}!zHWZGa&JCVHuY6!7}*zn7JGY(X@94urxqqcs+|)X)bpD{y8#r;wbn{m z_Qj(XorAICk=Y3@987=_rl5YW+NiB+v5TGAHb#_aQC(a89WKdl3Ki8js&u$Ug=uSO zQ5{U-V4Bg&0^|TH+R-|ACoyw=D@}R8IG-xW?Cs_4rR(lFikpIKu1KXIrN>ce1?8cy z@k&Xaq(r{a!d(}a3fs}KVRxXIO~Ra@7-Q^QG@Z0KUv5b{nc+=@+wNJ zf>TzeD48x)S5`lAcoLRUB(*I>&Pf7f2$Z;Bb3vd8Jaz8f+lekw^0gH76;)Ks*Ed=i zan`+di7l;cI9gc*fibuk2IkbU)KZqObDQcEC~XeJ?r~b4-aR9LB}>s*HgoT9E9W0+ zp4vU1R^ryoQyTWra&Bscs&1P7RCaQkm1W?26K9c0xRz8z$( za*oNJ)RL!fDP~AjWaX%JK&dCV*SttTDnU9Z2%;790O(ghKW17-DSd+d*1bfKwOG+I zd;b9Ou~!rx=4pRyJ_q~Pru!g4#a*8-{{XdevrRzlRzjK|s z1kZ1Ih_GY%JO01v%x0czD#`J7#fJLhDBgOr$M^pLa;U$06ts5kK*i}Oo!v~;y(->h zN=k?%9}^#P$8EX+OZLnhPvT=J``oz+h`RJE*=JxQ5}%X@hq>bTabO6K+}QAUang6I$b z0B2I)QA_W`J#p5*l~>1iX4Yk)*>I*HAI25R?VTNvKl2aXfaCdrbvxKP?RWPmcVx$r zQ||MYY1GE%o6j$*cmpk{ZtC))-^oa2kv?PlmS4r9bh%~#XakSzB_DF%7*=JRfqJ>z z5-IHryh|xsh)7Cw8foDf4;!)EDtuR{Id)@VrF;TovW$lf!kbkEytiLez}gvTjSo*T zi#)7r0?*n^&QY@=%P(0Q%MLtQr6iQopp=41?gw~}kh$FyyiAThsY%6ni&-vNY_(M- zL5j7HU42ZYJtr1^ABc`?fF7yc!{o2Fuy?i8uBGifF^yrK&#aYpyO>g3r(18+t!XYy zq)jC~V)qr~&%1TIhoE#=b;cIj3mER0>d5}86WH^nc#8+AxKOZ^*Sw{KE2lz8F?+DR zip~N6)ngo|k584mXmO)eWb%h7Z*7^h!aRlyn1FV*Yl<&Ck#UMZw!mEW$WiT=#SVbaD3V`$6&l0JfS- zcPw*KSj{Wg)a^yvZ84 z3efZj_wEnxT9V(5WPB~dkEXWoQBJyB<@uBRnkLkpib52f3gw~ji_ZYyQbf$L|dXDwU>SEhILB8Q7Il%C7 z+uAe+>U>l`_Y2K5*=6n83pyoEl@PU!4!(inZCeKjadj!$(@l+0 zIm%idv0&pDkCq_kD(^70WD@OahMb$rbQa=mR-ZfT1uAeK$~r{klTlOQjFiXYcKVCu zfAwmzU2P|DC#f@oB5vTDyLQl>I=Y7G+ClLkmw|Ii`FtFi*jq1x~fUj zvu!A7EGKfMA)xAF*=YjIFK%2lDfGcG>qqD-@@s++#j@E>>#o!+fRGT9t!MGlEI7Ll zc3?g$9_~*q7M5qkod@EdZ*6|Uee!Y@jlXexK885vp6cW7V%I9GqxPGfZ7j-tR$D_# zlPi~#c^ihZh;CEeUONot&Tm~bI#el63HHa_3#$fYY`-d^wJ$7nI`jb4A0kzJbCy%h zJc-O#TPL_|*6Ww%99L0QYHotQtMv?9pJmTY(9lGi+^K4?CP#Ry^BapeEWovBYhA8_ zi$m=4^0=L=OhbH#vWw+G6uT?V!dwO`zGyX?<>S(sU6NzCurJfgzSkX`vu#vV}9kSP6 z@s{6R%e$)d7BdfS&?=sK%VmM8AR7n{0nlxatS6W}BeK?Z-pTwD8IRH-#}b)s1pH%R z#yso`$6TV+Hg{8^30|hBpp@s~)+`ZpS^d*!ldpzKNFW(#V3s`!V<04bU!;c$f_hI# zkzrBtvqi1CmXol^OTutWFK{X@<~oI@vZ(<*C*WY|2`E^gArm(9UbMiO`N9jr=d$3o z)vYRQZZOo3M8gtF)v1#)q4+?LcBsgJX+(=lMEQUKogh~@lt@BZ4Fx8BPfx}W*HI{S zN(Mau-{u;_+d=A;?i5dOG{>qzoHphFxaUt7dW`Q)rP?r|WE}Ei4VDk>TK`e%YmTzJR)x!uJWKT4+wXN<7a5ml#XR8ttmt0`!daTK2VqQhI!jkX6g-)GduF6>i^ZP^;ZaN`h7tXRNiHQs+Lhw5UIp5miOvk%SOd z0@2h@ybfm!@kBPzy%kgP9oEwC{*`oglpoUJ*slF}U7e zF5Ih?RJDH+j=#Pux0jEZ^>(VyXJW`(M)W|MxiGf@S)99m$Pr2GO3OvmQl0EM>8MIX z9}Rm)#X83TQTPD7OM7M7pL zq?sAQgzj(4q7vrLIR~gMHCfh5`b$c?J+Sr*B2tC6`az{sRCr5pYqb+MeW^>E*6Nm` zW>!fianW-B05nO)v%n?gn>ccn3EtrwC9+U#8D}@DuioHV^yXunobbqOa(8eQEKGGL z`G7>-u653o1p#@*n#deiQvTg;j9hSAW2+l?wAo0|olp#Fa7ul#Rf60>wn7r(^Szt> zwP{qqeLh`unTKO>O+AWV<#(2CxMdQaUex+JtFZFIXPaQDQWnLLlO~fWj zY!VUFkBR+|IhS*4o)7^2sttR|!sy?=J6M(yK>q*|dTHB89#$7RtCABK(?wc&PB}y9 zgnpGEAIz(qB<2OJ2~p*cS|jiS^?W{dplA*pw@cfVpK-s6Yb`q+Zty&pQr`UdFw}c3zByxYyWPd#(V9DAOzJnJrBeW&-Fim5 zl*q*2|mkCHx7U5LEB1f!RS(A;8*Q&)|h?&!p z(48!Y-gg3jO{!%)fp1VKAWifGKL~Z%yKvo8k@35?Kho7$+>7oPimo|Bl{9&E8`SAi zj`X^j>Sx%+Uhgw*Us|mX9f7(LQ)-a@+r7*2igOwpMFw3+{lc+3Sf)#$se44o02#!d zKP?ON1p$=7Tn$TTrMI|C@-izOJ!?ze!_CdJ@Z0f-?1bChqAwc}V4|uViSN+en{*wU zSD_yRtVP?uVN&x$yeYZF*+i`fbt>MKq$TFb{J@D3q*)=9Ix-bfXxzy`Rp{rpk-+@H z%sjtEmJ8LZl$m|H8z1S^+tf>_sYOE4w*tymk+;fZG}0%k3?dH=|)jW)*u!3a<`@?Cf3QIs96e8m5*Xk&qVNQ=-wJ8pwdW9hEBlbfU z(u7^?TT_<1y6KCFLccIjQckH*Qg)C>a6y9J*1S@vXm>>5;H&pF_J?Y_U#o6du2kMT zZ>?J0U7n(uZzY1Y6%?|jlsvu(BTpErTwuoPgGH-YbC4sW+fozR#Qb4o;;U2C7TT3p zTr1R--R}xc#lEBuDnOaqgv`l5j<9XDgJJ2auIBS^yl^B?4+QffxTnkvyAuy>)kQU- zWSfih&Z4DJLseHx z`c^?DEum~J1OY!7fR`tCT89z%5s}v^-4c2 zO?_oeTcCIa)U9fl!f_jQke*E*;%Vkd{pxDh6KfHyY->sK0Dk11MRiIYZS=gPy3&H1 za2-ODW(a{>Gl8lx_U&_ak)cyg_wSpP?;J}>#p34e8)$7T*IXNLg8L~>(v#m&2g{U^ z&7#lkJ9BFk<^4hMP>~>s#B$LIOAH#?n}zzB4h_g??w5|SCyInVQB_^#H>MjxM~ayr z(bAiGfJD5Xm&ZuaU2Zn7J4b#@zolv8nlVdxcWi{$&_GzIzLMS-Rc?ElDLnmmK?u-a8gTu+B+tdxjwwiK`?6}P|P z2QwHN)mpsd&Xwm>=Hr-SVyP~zq2)RdG}0Z8(O2&B1gT+$+R;pg$w*9qk^PWtPy(Q? zB`N1+Z8pS+6WSx;5K+=fNlA+4nW#}l-P1CZ5at@tB{u4TXHAdN1cwyR1bn&(Ew*$u zs$rL|a{GG}DhWwW&~?&4LsH2_KV@oOp3DO{7UcjI5`+LkryxF&A9FoNRXr9F-bvoM zb0s}JDu?PpQd6XeFdHtK3PZU3q-YdKrNb0m{ z?4MdviNv^4x++0ZNR)*BGldBgtcAd8ocvjm(i<|@PLS!)1O>`-kN)c@{N_H8^LnVs z{#6e7jn7FAg!c-OiB^iW0c`p?R~CN!mgc&d>G6uzQ>xbZyk3>>8P*jS8F4xSW;%#& z#kB}7UkwdHw=ZOTl~C2m>{6ldqeQUfDJN2uDsuC9MzO&1t`6Ac;(q*a@aBoyI;){o z(WTHdphU`bGy2A>b0`g7UdG_$k=+r$2xfGaPFAzKRHUJ$DIp~Il>j;zspn=7j*HP9 zs<3^m+lXCbdwb*y6;3m4Dy%>S2r3f>USAOyMsphCO&92Q4$H>anp{+=UUgxnrY;KB zz?PbW+9{(8E1l74kK{J(ZiPaRaeV7&qO?~5T85IsRCjI~$aMT7{{YAEzR}fUeDjqx z;()G5z3y_-^UL*Z4Lkm&C~%c4vDBG$i>%N5t*Z{j+55MC;;*WD!A^-Kasq!t6{)Sk zD_*v|a1^R*vtC8ca2kqypAM`oS6h@FOH&~(Z9Al8@{C`*-IB#}yB9?}eN;NF)3W{k z&2a1H$!_9VT}PUqp4C3^IPV}N@%~wgU2XZzrMXq1ru{}8+vOxFxypYS{B7qS`0mUM zk!6wI!hcfzPqO>@;cmx^oS)>$PKU(&5~tj6x$bJsZM7DTG{&9B(Q`_W)!wm0l!XqV zDH@pdKVyIKt}LZ&`4SyZ1>#=&{{ZFA!YCbUf{g2?;;3(veX#Ql%P@BT0G(u2H!ID} zUZQH8T8WW85;`Og0Ux8-e$zpXh(iNI8V7>>*UEj;zcTkvbX@rz*Kz&HqvqZ{9tuq>mU!W=B*10Bm&7Cxob`u|)w#)B1FV2}Qtn zB&+qS3jCya3G{?}rAx;EK$TYc3JE9S))_LQge?dj+LfwcDMa-e!+S|jmoP1@TI-)b z%V6u?5-prkAA1vely6Om+@CX22v2w+mQOwi3A3=ZA5-AZ85h2ARCwojsA+jad=-rQ z7queUlvK8WBtm7=QZ<>2o(kV!SkG`T4DSV9W6$~){ZE@`o4^@Gm5w!8-pv(WdzZa7 zo3_;`b)Uu~@^4p;Dh|~W)t1uIv`KY80WcyQTr?|#Y@Sa!y#bdMN{c3I_4LC51A zQeLMexYTN=l`){##srr7M;@w9JX}p35n8TwiYI%7?(Gfx?k7@+7K|PXb8jtbM4TZ+ z_?YBQJGDrWjRT@{@u!_(XbM!p5o54v-BnS`fLf$Aj-^YZL7(vS@iL3`vGgp?`Z2pi z8sNRlW_psLFUBL~mZ|6&=~iX!d`jCyNYJ1XJ|-@BQRxdGB3g3svFvqG-XLeg%s;Bu(q|HDefI5wPM4kra z&yZC^^1B8{Zt8g}g!_2bA$ZD`lm7sgb@|4+@%ce{9@Ns|$+r8EU)5m7?l(q9s{II4 zsBH8lK4JS{Nr7uB6nin^^7morS3Z}&b{x@kr2QoY(VIIf(5Z(10GTeDDRYzn%emY- z(Q0_^(PAy#xy{^uDTlsg8NN)ls{a6#R@~I#^P$(=Dc7ukGWtqBd~bhJuQvFbD`EL@ zflq=@s~z=eoT*TmZ&edHpOJyiGJ>hr<@Ud5Nzc#)OaU`EVX%cU^<^7S+fr1W$kX_= z?FB~)0_yP5S#LNFJIpPxNi+Q<+Y|){{Sr7fI?KQ!eA(k3{649WywwXEui@5tFw19 z3>BCcl8-8iy1%NbdU3DV{{Y^Ge`x-@bg^3a{>r2&Ox0W;TIvqe?$N4rojxT(CEMFT z*X)v3hj|TEV&m=#({A`;k>b5+w0=6&{>52#oN|)}<|+(xJ6m@fJ|UE~Z_HU}lHElm z4a`&41d$`PNJ6FFDR`R*t#5;6H38K9t5M8kpL>XWkb>DC5iRoswxjmZUT3<$aX!uc zy7Fy?uiC#5<5~Ne6@du8ewCz~bqm@SRlJ3O_S5Itsqd_8x4icihG_O850v0me@G+}AvHE;ap$PlA25`RCjPQt~WspQG5+w@7h(yRCOI6R=?cFWa?1@K z3rA(S*-?HKD@vwjsaJFB{%?cuLQTX>&7^Mwa6XIzuT_b2e~K;-u?M(Vp0 zOKC`I`ughYsp*mfg)FXQZPrO0XRKQraP0$n1)fO+E@^X)q}Th=c+9xYliOv_imEv6 z<~hZz zkd2}4Zq^?^AMNp13HF(xMHjK3x~plBZOal0W#DmovGDMW00MNFjV~J^b6Vp{`O$f|EodT^Mzop_ z3h1HFy^3pPx0A7oRMMKp6K!eNnQ>%qaZ5XN3y(mrtJ@FT z^M|nyU|faov)tA@dB}3hedOABhl*w zL+=?hk^Mo_=F_xGXYIYqaM9#HaIp*7+G@WwVKR`ITUE zhqH{I5ayb0Gr}*_d96jk0m`fY0IB*~I(3y1vBlI#gDrYQoOTBCn@tpb=OAu5q{t45 zfzJ5WtBf++z88@=&1OqrQd_yTOt9-IQqUGQsX8bSuCU$KG1ku8)TeP=rMJx9G#?Z} zVywDaZYa4@1yGuGg4@3ix=I_iGeID5(RV2%6UVUXuT$IpZ;)jE-qlNdxVvyWRr+wX zjk*~R!ZkY!ny@&JQCMD58eD82DCiRv%XIGl0Ff4@6R|G1Pt!sK5qk6Oq{C?6d>U;XVmMPUjuT$czrZbzl6Q(;Y{y$-LxclIJE!PWrS5i)+ru=K zn9KEorzfdeoV*tZFF3QQCR&Yu7}xBw&wZn$aG)ba<(r-_4Yp0T@p_&PrBi-&pVn1Z zKTl5Xs+bZ!I`)ifBb2O$i`v|8k8FXU`6NBQk?N(I+N1o%^_U}Gz2Y8An(D28N*h3h zRn(!8<^dq|@P=l6Q4DPo(-X=OM@L_|Qc4r#Q1Js;%sEwuyzx$}Ug23r!4avVua9#1 zGC;E$;k2M9Y?Z1AyZgoY)Eo6hPy2H`5y~r6K z?X^h@Wiu9QViB|>Xqh8Td&J~1?FzRvrOrvD)jMBUuny+JzhvRy^Huk^v}&D9dfKML6HR)a_|CuvG!L?5|G)_MJHL$;}bS*RIHTX)jbvq#Z@{QZ7wT%CA5M@z2f0u z(hAM<5-^G>565*kd-d|$LL4d%Eg+A~WK6ro(#*PnVjQC7tYM*|xgH(iL&~|=4EuO( zlDbUtv32>n+mKsU3sMC|K~(<$g~e@e{`47~%nUXXYqRr_wZDm2_iuP)oXeV8Y|(meytE?Q*}wCK$jg8tv|av0xrAoWV(44 zYG}QiVHSY`e6P^mzqW!R?p3!QHBB|{_-LLbYnUn^B4IPqeWHt;hz62Y&%|SP@CQ<) z=eGV1qqV_9TX9?*Vh`C zDJv3Ns9{P&$smoTCP%NvJ#C0+d|-}?^Q)QLJ{y-Y-9bk+CBg!vPlsr;$I)k!^+j%# zozopghr%5~$dVTV=H(eoL+}~|B*LhH*H1}zf`o>uHdulSQ2UCI*XIwJ98&?PBSlJF z&y@Bt!j#sEtm>XvN!)bsh75YvtKgCqRm$^927C3<5Z2KVn&)??>j~acsEinWN>HCu z)c8U|>N#!I29CXYpQLmhR{~Dl>)cGiN%{Ptr#gs`lSbm_sn~wVWUixXTcV}`1OeU_ z*Z41-%7m<-ya2S#QusrF?KMM4{S&2+UeJ=CEafT8{9wrBB}Cju;-1o}%IZ2|eZN}z>HfP*UMhBWO;EH8Ns%Y^C zC9`s2nrB`MZ97u56EYJiPrPDW_khQE!r}8RFNyMl3xIb-`et|)qcF1)lqJIQ52oya zGwU92!)C7VH=7^w)oVE`7;+F*omF7+Uk+7c_czvAQW8|6g(W6LN3*z(DT4mo7Lii0 zu0w{F2ar@_%rQ!;ni`gr%zzq99Va-qRFFcfME!+Urz&w>)}FqiriN{(6%wg->)I-p z)dr#kTt(q@`XY$n=lCnak(hGM!QVJvHqfLV_hJ$PqZ} zZCzEteHuJ#`pOa}!IyEb_J}lDyehWBLlCBk2(dFt{#@hIyeZ+bWg(Dg#3w7)V@m+Lm}#Eb{Djonvy9 zSx>nVng0NYJU&oXF@($%E@DtEoPG;cT~=tU7={L# zluhkjwbWbGkOSC6+*a!c2iyM>e;>JETUyf)O@=`w&uSSjKJCzS!Y(K zDVj^$COREDNW!lpQMNLsnUpA~Zb=079ez+51cf5kc&1O0AN>|dVOjqG>Xvq|Q7<_D z_=}rKLYr&Dpg)4ROp1)%{kR80cmDv$jdlM31YDzI~hsyMeZ>JXJ{;}12V11qSMPEj_L z$fi@*LkN@*me+!e@#HBLX$dmaNmD&MAbV0nl5U(fDMO1nR?`i7RSQUvAOd2^45W%t zQ|=C^_U&`JGftwKr#!t3$ofOU#)S_MX(_a-$}a(GPwJFXJMJ@qXnfS5fO#NeDZD6< zD%1yz&vc)p zB{}dRavhY;P(n@pVis2D^(Rx z1eF4^!a?sX2#d5(w6$Wh@Aox2)kTY)F>U3PHWCVn)3o)3e5FV?^92*j$2C?gzTEaV zex_;bl`2o^7U}}Gqrh&Sq9tIFl66$<-J9Wl=JZG|K;W!H`hCc|Vr4~1aYP#v4OdMr8V(Z7v0%36%MP(gC_5&7e))O2hoOE9nvGq2hWKr633%*5SiSBBhP42}^R!>S^5cYSpw#X;?C!?SjmeLI?mw zGcoX44w(Am_a=gb(uWR8D}*4HL=3)A95>;s zQB7Lm-u)w!{Hj5kk~@x_Bd(KUfUi{ZZ4A+9H3`)hYJ06aRdkMr8*ogNnTR`nhzHw{ zXi;mffJvMtsV-q15Ja!8Avr@D5M@`yzYaF60MXgmtiAp-04JfXy_XL)O6f7C0 zM_K(L5mX|ZwZl~Q@$PF|ktkSen^TpHC;2s?DMd+0=(EyrWvs=)LFFyl(@>o1mX%1A z8mUvE8mZNgl*{H|iQWfP6g!=u39-3z78yr$YaO!IjFTjAk^Mf9ms@II#OsOCBj*i6 zF5^Inx`f8f#a9nMX19!^kn1UK^+DEg)9F&uq@P8#sBTn&?;|P1RpIrkw;s0qox{a6 zZPrqhkWdIFKpycNRN!mcQDt4S(N-7g)1Z}q7!ojo#-5=$9mLH#6qc;Rt?LC;&J-PC zhgvZk7G^>7B*k7V`x7m^7x0t<>s2cjQLgHfl0FcS6opfd#wtoLvr=XxCHTb73ZCMo zb8tX&r4%33ziH;w)Emq_ba zR7{UMx*_l96v|G(=F)VW6B!Hdr*TFWyTebC;+&c43>wN(sXt0WQ7-a;g(NvY9)nUK zSC9fa+SJq}b#+^nFP4&b8G*D&>P%O%c{W5U<3+D<{v|Eh0j;S{E-Qy+H+Gbpbpwv5 z^gC2~h{gFIcQKYkiDUtL1BCsvWwJ~KksVWppKhG5e{Hs!pmCt?fJt5B59D8Vcqy3oby%xoW`wFskH^?fY zMo0Fe%dYptRNbpwd6t*CO_d(A@rpgi-;J#LHj7%ueTrDZ-5j0^SFnn#LWQP-Bzj4f zn)J|44aNQ73yA<75Lth+i*`hI>Ox_8BirT9!1K{aV!j5v6&7-uY9Zz*9)_ffK#nROjvZWh&6 zFF~agFy@zz>VRZY*j&D%B-+X~MZn@@0ani-FV6YuC!|$!qgAhQVd)4L8$1P3N$6B2 zJSPoe+6hYL5!I?I!v=<fI?I?nKXl@aYqlQZx+}2Rm&ej;FzC9n;T*=kl~9Sx%f#&5qIJ?|`yv;8RoXtG4zlr{ zXUx@(V_1~}>M9VDy1LZ^=rEp>ab3Ow7TZGhkUUU%&P{2Ik9?(AC$rCI_~!}R?krvD z*t=v`y06sVZ#w~0=l=i=DpbzY_7G+_6w7LwRF&i|;B(0`_g^vE-2hevlVkW()NM6Q zMANEfMWvKYK~j+I`yz7P?k7c>&epZPH7dIO!(q+aE;N@U?RkK?tSLGP>-c;kT*Nw* zC8UquLabkW3bxnqbBWL=?UAbSlBhqT*A*p#~}1d}keL)p4szlep-7G=xR3jd`W!8hv6mN9u@*IyG6}wiEvV zrcV3T&gS#E;9u7i={^UYaSji)RLc+=P`B5RgEh?}+>CF?;Jp zH;K=NvV}F=g`HGZRUmI9l}wZ2(k1a2&5sH@kz|aJ8Y{&6bkvpMs3StA(g2@wCQNCQ zx6lIeN4@@~#3qaGjPOPWd2v9^Njm)J5%*65H7mph{D@rCHhonfG}%&>IY=Ep2!pc) z$3VRyG_kN$nkN?x<9vfCucT?JCs4|cQYrqUHJZwYVkD0*YX9-?t2fhMBCmu=TYM z$tez`p;9sv8{~V%#~s6()A@#hV!V^M&bg_8Zuo_BUf({=OXQnvvo_B2N-KUlRMKiH z9H^;6l!jCShvra7(j;;gSep;vGCTGvUNQ76UcG)5K*($PT=l5V<)v`77I={L1C`nOF+X`cinvzXiG^XxaX@SObLJlSDluU(&fM!k~#%^ z$MFM^#l+V&p^PMgN2u0=^=bRj$0dEuKIXpQIEyso><-6MhH%yq=~HEy20LbTG*o}9 z0icH}UP}8b&A0M{Gbm~ICuws>sQH~yw)|?`O4z0~1EmEU`g)Jtpl2CHeEp2XDw7^^ozZYM~~Qm=Qnv+_D7BL ztlX~loFR;!sW+4T&~U6~*42-Kcrup3O-ur5s_t%}rBWM0i!2HJbyOd9-p{9Q+ekxK z*x$3?tADwaxO9ie5~2=!@8jIZB2w1SRq{*t-KMM%{ie);ZfvDr!c^YpKLte>C)~`< z@6PfE!EStK?emIaY3FExaIJQ}vGs0#spnjE#}(qGI7vT@Q?4g` zuB(ko+N~|1f-uP18V7(y$sS;!y>!xE5Q7LShGeIqLR3P|gb+aSjN5TXxaG>-9JL1U5v zTUUt~d_kyDoZ}UHj|Ll<-5dOz2v=EkQxhYI`a#Q6rfjzGvcZ;sA6#dImR)K$K;wJNw-{e47&22)Pk+xYtkVdXxBC1S+@-tmih|+$lrz)qm z*P`E6t7{A?PX;QMq$LVE1g?Gbi{-o}x|NVz-EL?AYyH2q6H$Jsv%hz|Jz`<%DgSK% zM;u{T?<-_nyG4RoX_0rnU+W%znwswFL8xln%D_u3AW0x6wv*B(q>9~?EgqxPg#^p1 zxt>IhC9UWRv;P3KZ9Kh}{n4n~B5KDHejojh)(FqLpZ@?|ZTNqR^p|DDaW`Y)KXX?m zu8G~;5)$)yiI5Rbm-&i7AIYMz9y6q%t-|rT#JpeqBMpZ_43Zq(xu0N(Vf`D+?=NeBwHq=aiAJxw(+7s=mQ!B;NdX<$c@Dl53$f_*XOPKIA7 z1|p_qI_jK^7mM>QLv`a|x1kogA-e65);K9`)J+bndG~yxUIJ&~7F=cuX@>Xu8Rk_p zE6SjN?Qg06Lsaq5-!Kju0<(|UW0H9e+$N0L;=+|?pn7u-P|>;n02yc_%Hxt9z==c1 zEo|Mg)w`YMqDCI}{k!hY*#7{^KfPTAr#a5EFR`O{PC>rZGsyPj0R)hg)&ewO*K3)g&%j6E5+b^ILn#?Dj$pNm{RHGZ>pJue)|e+8(Kq z`<3!{3j2unWy!f0x9YpqO#}5UF_7ei4$@lRsir_+ngmG)(;IbEhUID^dx`PurrI^K zR6SDrlg_NB$vyDU#(xh2qh}O*amcg0l`Oo4RNGVMy<{~CQi2+9RV~LC&;it#({UU{ zk)L|!9>|r$R)*?X<)Hg8Wf_*_c2+cVS zi9>>@MO=b-#N{{VN#!FEmT{{Zdd z?f(GTE-Ka1-$2dlG-8@}-1fP)%|nU_nu~jcn@4~ViDx;T_Etd@bqywf1?xT=+AJp% zwsqCP4w9O=%b&lvW;=`fj`v+zQ-RjB<~+jNrM^`lr}W#}Bh+$dPZ;!X9=4MkVHlHH zXumr-%*^B&*fCmtpt^w#(q6}#(o!~ z*%)qtAJ(a>b30C@J?9B~l3&#^`7Tg~>XhH9Au}QmLpUB+5S|es)|<=j+g)38)h1wk zqBclF-BP6Oq;p$9tIymW&gXKzCWIiR3sjJM{C=@1w15!OyHZLk!YnP@X_HX;cQ~Yi zRC^KLBrTh_RR?z|aiB&Q>(DC{&6%E%;gGIF00OA%6roi~Xo>lV3e&8QSaKGWJ=C5G zRAG4H52QjST$fm!wSuafk-7m$>t+Vqtf$gOrbcq_->hA%Y-$3^viXVzWj^}Fs~wM7&R;qJQ2zViduG&U0CV@yu;OXX-WS8axu@i+v&?9wT-04 z-Az3FiuDivJ#lt0I_o8H_KD;>Ky?8P0=jtXRip1u?jDkx2*ka;<2Pik>6_KHU6?e= z=22>)Sa~R3-htObM2}DmR{Jx@UvCtb(h5*fwf(Be_l4ZpGFQ;yZ5|!!4t))D9XfvR z70otXR6^RPPn&8)(>YEdGm|6fy4{+mTKy*6SPD_sLS-4hESb!dX(w5mxOHClSfD#JNjHe%&u)!VfYF~Jo)qa%se)j6a z4GUltk8gsu?r7tTM)$|pG=^CZFv9&65G6f?d81Fb#X!doZ>U~S?dL7XLzHVtJ6vdg zO5!V#sjb$YSjie%y=Te!bMLIY^l)gR@?;ag1jyf{#qy^_L{{Um#Z^&g}L{Fuq zB4rXI`{MHeSOdTX5<1#dIS(j@nChSO=o;xY zmg&daoSSJ{Tcrskk>e#tK|U*$MK=MpDkEQnb#Wl;Ldj8)0Q3+x!ixrosQ`_{rbqFP z74b%tQVIlu4}p$RaVU$-hEDx7g_hBFg(EK2EpH$zL(oG{i&VBdRFN*X>XJ-^GES-5 zqZ(C$e4=ve9}z;kP?VPKt92rKh%%Hs#-N`16!hWLY`FV-#I+Jk{{X%uVj8IHr`1bq zHKx`5F+QL|rjS)K2EGv`7}He&bbU#r&KR|~>JKTD!4v-gR3bnql?sh_mMJ|&lq*0^ z-C?JYQH`n*hvd)WH&$wxclk2`)K93NgbOTUlte`HT7_ktWysXG3I?k{56 z6FLDY#CzMYRI64#cRZnPaic7Pp-u_^0C)T1A@?jLSw5CfD?}lT7Kk8+X@=q0jYK%v z7*LLEfnc0cg)T01;ahtV1i~Y#W=W(A8jNy@N%J%jyP`nTq)pq$8@ec)tLXq*E!Z

kCsl^9D(by8@7@)9v!b&#sCMVK`}oxz${{#K*l zKe`e0K@1BsT=&}ejthTG6kC~tfnN!26ro)M{H;YhabtojLc2WV1% z#Xhi~q;SKz5G{%{DEz2msbgV8IJOwrL- zS1w=(w%Jzte8>a&hjyJO-U`S!X;9e5lA}7X@6+Sr?Y3nCYIC$C+mhO+=DYmLFPh+N zb2_WH8}-3-%X*yD(kZ>anYpMB#0W{nu(%X0&mfVN4OY4BQ;96x(Sr9kX0Zj zp_qa4TyEN1Lo;-6L}B}Ze`%O)l)k9l(5fcTP~3HmSBci+zP?U2+F=FW()t19DBns>LjE7Q>Y3QR+Yg!a^rw z8me8v^(^vTscfYFSrb2uAf?-y6v@^f<}c0M=`V~is+b0WX;A=~4SUB~(R}e&Bo_u9>!1Yb@quG?RHUt-Mi(bGwK?_ZK9EGGN~Yj#N^R@b3zH-B zYdEh=eM-XvbX#c~3rQVk(i&jVJYPbs*V#`dwVCnVUl82io%;s$AF!cn^F3^Jd$y*1 zeKi?r+EH`FT?rXO)GV`QvX|Cs;OMqpY55MBFB_s>yI>x{<=g zO)|GO=$Jnd0mUI}s;1|)WXz#bQqPv#^UQhnmf&vS(`pq>ldR<+K=3hPe8Nby>a%(A z&$hS~=~Y!6uYuobb4#zGZF;Gyg_6pUg(xb2AY(kqT1mg6*zmbn%pFsy?Xw8FuEyVY3kRJY0msm0T%7n{@tv`mzf^AKIl zYg7bHsyE6hDKE4wT&?T%sVmXyG`_N*QS>@O9z`gK45O56X7yQwKh79pn<2tC8Th8RrbbXHBnELajNWk=!}-! z(exGn0L28NU1Z&SX?%2wLY zxfH1+NP`v#eN5an1y?1IG3AFs629uiHPVzRFQsKN^qvNDoIK`KA^p_VTo|vST884_ z6WV1yo)9mzM?_q6H0VM$u;n&tt7X#$ MRg=8QmwY1DVMEF0;tEVP=Tx{#7Snk5% z@lVA60BQSzLS0vva1Kds$1-Z4>sV#|KDG~>HU9v*cRE5uh$BR}iOIwrLDXp+g8k8* zu2L5`=1$LYKE5N%{t5D;x>H>#QCCRdb+w7e5M}s8n`8uNs}ixzI)%LtPIByZhn(uB zV_Q95x|nUkp*+s34^Yl>`gVq!R813+fpM+VIIBy_Nu;v0f99?V8W{~zne{MUTD*;AWHg^B?>?~}>y1>TIKfR)c_wG1 zqvojx1iQiNTcf%qW<;t|{AUul&rFq7UQr2>3MofPduepZ1z?goe#p7D=oYL(Olz9@ zrG6Cfa;T^scj`W{ZZlG#c?&4rRMNsK0vc{zKokA(Y1>tgT(}1*18%EOXdi%v+zKCY zashd-Q9{%@uT3BsfvTRzI8`@IxR+{9ngvQwsM}H$&FiSdee zCaK3@xbY1(nWy$cZfQ^(z%@pRpproYulK@Q3Mv`*AoaD5Q-YR+%(VH##^5(nq^#p~ z&f3vbfV^GqZ6(E~na2R`5=b8yuU&GBRzFvYtveOlJGmQE8KF(ZR#S3CQQU>L0q8;R z9Bz6Ub!&o^cW>m6LfR$mo03Zmp$lA;k*M9?BrWp8%|cx97moy}rsVo{9j>lPR)=wq zr^*1*Q@HLL;_xV@zqZal{{X2KRQFez3!@4inh)^~vyad3xgPwSTU``KRr=BPkM`va z+Z%B^YBX1I#QYUxprxfkZH>%;XSe-P`5Vl;$&kI7#m_pg)5h9Z;bGYeKp{^o8-}dS zw9AX6LIMoakSvMx`B=sSsE^>}Nv$Jnf5ln-ok^(B+n3gj|S;6xA zmrYVihnZd(Ld8?#^vRGWUQzLkjAmqPtI>EKNh_lorXMNBD2f)7PL%>aU%E9;GoO*P zAnLTtWPAVBk>S(rZHrh1+z+Cj}HxDkNW(CDPJ<6I36s+QE zM{fMLhtnYeTdELBXDNi8(KBLC)Cw$*l)bUzTvtZ;<(wkfM1q+wY6u$WCGm|ARJ1QS zT1qz*>*1V!<^JnV!du=qd5oqcVYyl6reT@H&?~3*&A>EtEYP^^Fgm2@BnM9zguJ;7 zD^23qcR19meZzSEmV(~S#$=$41pFeEf0m=@y#O|?+!S0pVeGx$Ux zl8)7BEM*(fI@kv%=6rKaUt`2;8KI@foorm5QW97G9TnOoZEnMKS?4<9QQtydeqc0K zbHcZB?GsD8U6W8=3H6@~Ndzcsugt}w+ia1q5N8=BrP%~gKG}9*gxDQ-;h0Sp91wcc|vQk__hzdx`iUa8r}mKoC6BnTtjVr&6-+K2#R@Za6r8b(GFK`W+OU6=*Tt=m2u@FL| ze|ELTs`|Fnss8{l{Wb3tE>JaIgY9H=jYJwx$`dY~;*`Us7Q+6hYPC<&8ZDw}7rMQq z=`+|vfx$1T#e}W*Qi0#BbJN-UKO*4z1XNJ8vZZ|HI{hVJ zLJ=^NFk;JRWA3u0Rq`2j#Auw04c_H$2}bN4>jmDvb%xbUgh^wEN zDF`)cvoCD!hUjU|qyU|xwuup-VMy2(ImzKBVxO*Es8Po271VlU5%UHP$sfup2aYiP zNL1640=$N(gECw{4u&zi)A_5?RxPX{I=CAlWul@aWxG#=OV|LSawj1L1TEhwiZzGRlA z?KRPKRrcd#f0AjA^Xe%nnC+rs=X5I2ywK^7)jRP;9{&LBwrl00u8RKerEFQ9O6C(J z&V5foqzwcSJ4f_Mw!FE9;JOj&RL4(s;0z(zDRiy|<|i*%M{j)$O$?qU(wzxYF2Ztb zo3*G4QTu-8o+Qg}myBZ-#+6%NM{TODaxm+uXg;#ySO1=6$EX z$F@1AeRm#}QELd|>TY!Aw`eZ6x_n;MS5m*#Tpdyp8*kK++qlYLMSBZ{gCme4M^G#0 zA!3XldF=+7wRIkgonQbI5GR zNw(Gi{{V?uW*+|YD+zt=*@SQc-_U zdwNcVRdq8+6W2vVWu5*~X=^lsHje^6>Ndgra^m>6d)r!hrg>e4DnYWK zd~5xISGDbz?H9xriVJ>O?thqFx|R}$t8qFP+M%|Mqj5{ltd|rg*UTbgDaf+qdvP~t z^FiP^KNgG$E+qFI;@kU|o zr`qSV=Byk!ky}k2`t<7?u1Tj*32=&AYb#|b@_-hlPAK^fK?F0Ko%H-l)*|B4+}XCy zMsyS)YJW{X#a2!eWcQu!?)h)S=O08ZH}QsauO1e*Ga|!-5c|kQIJyP)zy~zdgd;a zd&5gZvjl%I&S@aPpKtx?i=1Mec@uWDI+RzQ_x5OTyY^b{-?_D~MHNkG@}*1v0EevS zF&ncPuB#bv(QEyGSsyQ91wo>Zb84~uaoei3J^UfSksB*jxr*fvCh5%U4zp02l06l* z{kpw~S^eMRyosD=+>KGi_(lU)Pe8?0JvB4c7b>@yH3~~=swK1`u^^&BGcn;0x-YwX z?gx;L($3V}$njo>Pt?6x*e|x&`K6<-C70TbuBTB`@z=w_bc6Qb_NR<{clOoqvlhnr zEb@Lmf2F8;vgTQ3rBZ`zT0>4DjmcJkfMZqdcYpJovxP}{BnH;#2L=s6tvc)Ypu3Ou z<%Z*&svB`9)LW9*KcUmaX{Uncf02IRb{-d^cLw&u#+Hsdy3w+#dd$k2k$~*M{$N6s zjr)qGqG#4Tx7&ZQeCHzoAVA;0oMicJ^s@&Vi1#1bSE74^_nXJ~)v(5KOBkaq6f$iB zpMU{ZrV+@zgUbBfNtkjLV{y%L8kY%n<6lr-Q%r(NTWm-Kr6lM8kJEnL@T^_}5o=)G zkvGD%{iS|scSDqOQvbZf2T0pwF@CBnISEO z9RzDEXdPFb=eluq07@Je+x|$`xBmcm*#q2q^<%t6geu`%RO&-YwbWKs2HA6Kg(?dn zB#(fPuQCwD9@^^eiTq9?S!FIG(B7xWYNOBF22tF)Uo=dq>_^Ah)-RL=@k6FW!8+`#_n-i&kW;M**mXk?+jx_b<(=@ z{{V_9_qE1e*ZrsaqUK%);oI%coAGWhRXwhoHMrH%Z>qdGLe))LlJ&8snOa#}p%ZN~ zm{95=%w}FmBKIE93Gh|^QO5{r{(Ed|b|u1%KQrJ;EVc~4Ji1thDcu``So0H8o$zlFu~G!Ke^y>>)2M{f5x zS5|^(2jN;GoMzL9T(La5>z3bcc>U`gQfaYFev;mmvv92{A;xOlSxKftU~A=^^zA5- zxN~03!043gH^nZ(&}u(TQ(s!?Q>7sikdtZdJ46(UdfGY!b!(@nVxFqBFkEql*0i@T z%u<=84Lg`F*@mE??`|3eD@E8ZyzJlFN3{O{wVwogOS9TB{O{ZrZ;V*0ss8{*7<#hT zaH*$iqQ3W1^FeB9WlK_9LdYv7LrKMN74?(l9W0d*j-TMIJjzU57)b4HsOeqSo_;R= z{{V^6a}#u{uDMhsA!}19Af+lMNj?AwxZDc8XBK;7WfZLAC)Z1kwADj^)N9m}w@p4V zIcGg6qVC}Z^$Vb1wDhYzqy5bXcX7l6Ej9jySR$L;v;L~z`+2SNv!DI4>+v7CtCXr* zwOemVd9)$NS&@>^m4anGLb`~qh7I8e>#`yz;H@gplybK*;rxAPMUI}X--uhAYLOPp zWiX@*5Zt^2L9gHrv?aHGL#{igoT+Fl!L@qVw@zWZAXrDWj&MQ>eCC zs7BfhQh~qITWLTN0WBh1h#$nMlMp$a{P%hjM)?J6!EsxAKTsQ=;!jlBqMRr{yd7tu z(gni7x?mKhdSWe{S40+jBXqxHo&=y z#btbm`}{xL9ZTK&9;9d6QxK`ZzTuf>CtE-*cRXtoxVKYnNJtE!>NcDPN&f&4k%<0x zAa%Vz<{!O5_HxoeE1pmP08FZ5xMM5GeaCWbItKS0$}cM@pLJYa*YJzeto_C}@E^HW zud^oHJ6{)m@}>(I3l`>B{{WQ=Q2dKBCSypjVsI@k1>F``k>oP#LTXZ`&+YZoLjGx* zmhikPl$Rd5)d0$>QZDgRb+K+e1=8Ax1%f%HE%pfP7SB_XqBp#Z8R6#T+kA zyCyqTxfjd&R1;Nk+o4K`0HGi3&sfblCpD7%?PsNdZCoR^(c{RLUsKAjnq&~-TzJiH znUx-x^l9g zw^su2=esU;x^c%ZQJd|D9<(a!Lt;zCq*gqNmf8WrjY?7#G69OmbMn^~81~5*iK5WE zE5>ZInX_+lD@N9zrFFT9Vi-3OVpKTxF>0m8F|2)n-5nDsLoBk>NS8nW=rtxhlQj1? z(|D3f{T#M~AA++z7{-NA{@}gAa!ecT%X{#Bll}8U$%uRd2VpS!rn# z5(i1oq&{AVX(cdOwnU0_Jbg(551Xil>p@hOHm;(Exh7&4aV@1I;INo zS}m-tglV)&f3i@r`UDJPT9k^l(mQmn4lF52-Q^!xL=CQ}iKGn*N#XVq+F3rCKr%aw zOTgNw3z?LU#e!IUtv1SAQ`Cozw+x4ND)<67QS#`(sI z)yOwmM=sZ#Nrc>O%1f`H)RsS3!`@}4awNAY#xn+~g4ZXL$foX>!DVa01jiKQw_NSTF*(FCIg zEDTS;rcyAbuqLE_sL10jI?|QYb{#s$A8GFjIRTG3ILS|E(0dcmJ zq9{gmAx|t*GD=!u^gJpk9!g1H<|!w!5r%gcx6xiiUeW4WcM#?V+2OA`Bdqrcx8_!7 zs%cveJWy1Xj^^sm>mwW zUd#GeO4fL|4{)ktaPZ7 zTG!u!Z&)kx)djs7@)sf@8GY<}0diuY2&zsb*2wvrC_2GAw1&Mme$8Jyr~vwXiG zhA>+0s>!&EjycN?vNc3%s#t6#2@Qp>Km@cux->g$XU4$Qh2-u8vljrLx)ynMnc?bP z>lURBrN;@F?ev4ary;i>`AvT)(X!L--KthnHxo7KAu$a~*MXuc$*eEcG}ljaT)x^7 zVsYm^I&rlxdB9Lkn#4$wK<PyfZPI%`@rzuIK-vza7`CFCjx^vx+gQkg*{kZ zxypToC{~_y%d{B<)m{`XR~XhXl{7fBJIFBRYMrrMoS>;n)TAj3Dg4Ua+v66CLJw7z zWOv5uu&Dd)i<*PXOMtyR*qgVi#+BM*p3y5vhN9U zPZRxek)=Cs5ISqbM9duNtKBT7hh>uVnl06_X z)k#d#L_X%={Xa?Rp@*lY)D^RRX}GQPESbtj@rY?os?`WILv6~{I&_A@kaCbw4L-_WI3rP%5suuDF+()&_>-g`IFy45?sq(Bn z7f>A~wIGA_f!=C~ zfz&Czrn+5mn~OIZ1*x}MSDCpPEj_^=20pdAEGgUT7RBctv9VmsP5%H=Xb;p= zC?qWgU4B0}laRJX)GC#}`&ZH35UYy_NZ#5)R;g}~1NTH_2r61RH*`(@O4^e~?(@5P zdugO5QC(3{NwB1sA7xC?*T06`-3ex>t;;itG6`ALP3z;-M0l>`RI%w)AChrFQ%>{E zqjrblHGytedZR4pdZxdN`E1~~bBp9p6kFd`+E9+{Rep)myNciR~U9!m!En?hp40({;SdZK&fOWDU06M=n^JZ_v89%VD&x=|(_G z)wLiMq)$$wF^kJ=<{9<3{Mf(BR=;n3fy>4IpnFq)n0TL${8LAC;BG;;Q@L4zat=Gm z_GAsnsiUtHHB#&qO;s`sx{c8#XWbZ&rg)sk57Yh#Njqx#Zi-gO-_`ypnY_{Rt)(kP z>j14tY>-`V(Rdkm{!-NzOMYQvFwxXeGH}bb9+geCeaiKw!%=0e;{IN;yME&v;)>p;a7wbzRH3ys*7cc|+(%N8`M-INxr$M$ zVi+a2cyoLa9oo&3`+2@vnYdhrVZ}Ld-^aX(OeaZN$L0*WQ!v#^2j^`3#+?^uIb*iD z6?bDD%zohX35U&uGcsMC$x7S9;#9EZ98)s8@ukZa;QM;R6pbmH zz4nwNct}t419I+rsuy8w_Zq0Yu1T?xrD&-}>0`KJS2yXd?5>s4;*htT+?PY4ea}!q z9br?n#QLiqKH;}9>8hRl8SWa^-8H8V&99BIb`omWT>F6^sYaVnvSc>_-@HrNWgF0{ z)>n@`t}bX&p6y^}yqb%0P_JASme6F0sU*x0JD8Sp#@!V#GLntn3V?HaZGERQ%Wc;> zWzyGDTx$hG4kU%8N_k7v9}u8N!YFqUo#k)1=|iDu9zh~h+Jb;k*SF&Wh8odH2QBDe znF-I~9SkKj=b~HNzM2x!RpwV%8skk7_6@G66`IU$W!+>E)lj>8K=G<}`uo}lxwIf3 zGUOo)U6}ZbQkJyzkb`6+V#bTBDU`}$@ z29279J(JQj`r~vJo|+f=0)U)NHHM2Q$z2V}R4va`zJ#Q1UwH|~+f8VoFRqx;ROVRh z4z$u#vDdDeLG6!KpX6_KO2%3g(h}7@4w3aJpuOik%X;h5l+2MS^qeL~=%#HUaqwEL zxBDF{{=L}&##=KZPSH%|me&*8mPMnA+Pi0WX=!vHaw^cU$hl*W14?DI042~QOZP{c zrR`2?S+^IEz1wHrJOF?OuqgeMX1J}{8!a^RS)Wh@wT(ZFcK-U%erxJhbIE!3I_|#P zD4R!a2_-2(*{;1KM;P}o1)0l`WR9o{LsbcOx{;$)52~~cv({=*1H41>8J`uS&%s!@ ztSyr9ppO;OPqBLq0~pPv327h%AnpTFWIM;<2i-PWc``1k(yRBo?ef+rFm2cv`UQUd2787C5kB9#FPJBmxGk3l z1A$PNn;vOa>fmHZN~Sz?oTJ>mlyl9)DblK&-Cd%|=isS_H*hN>td~HwY4wlW?%Oh$ z_I?ZSqr7hp$-bUx0eN5iqlc!S*&3y}*F}J2+MuU4Dr#Jg;Ac2Fu27JC;a3iB+Dnfs zuu`Nx5nai%s@b^V&?0ObN~L+E6W8GY;~PX*CixYB`#Z*7$a#i}x6&>%YT8t(Oa&{X zTCg_zr>~OpzjZQR)rer~;<_=j+Px}Ted*eyp&;qrT_ZtrKh zI)J6AY1{bxU|Gc&t=Lxw%mTkY4YX1>Lr78*015JtJO_B7q1nNDnOKpMURcb_bmIHUc(bfT%>#D$!!a& zxgjp6)jpuu4}C{Sze(v7SbPr;nyplSaIH(#bp@`xd%mg>Tin{z59 z2|?;6MB--Kd@R+=2*~5@^7lKN7z*8w+d~JW$G2A7Ub$8+1ibx1*14H@9+1(!jqaf; z%MWiG8$-5HRo}WFVfY?5Pf<|LF4KO1RMfNsuDa?Vk&lX2)jti)wr|?q0_R`dHx^s* z0PS_Ym{Bg7$sq|yPUN4IqL-YDPNk?Hbj)ID_f6cMbb#+tgjQc>#ZeYYF*R*;DZnuCwZPpMLJ z9o1uWiJz@Wd_&nawv*9%qJUJa>%Gtw_{6kW7L7`&UiSj-b4|nrFYt~Vq{NwRswRUz znq;7q1g3KM2#~e3YZYeZ?ejXMr4CC#g+a59M@<$B#JTTt3$M{r>-Xer&v$j2`toj#J?l4T$uaca{gNKw~UPHQL<+KqJ8 zwE=1&Qjnab7hF_~017GwT1PymI@ONQzS>9=n3&Xc=@v-kX&Nf5f#B+mjwXi0(q<>q zPOy6<(MOpLP(|C7Op*bVyLd+qi4BsaCMy(D>t=d|58n~iko!3Wuz;@UE+5a0C^2mo?m)lw{wS8(ucM`X&6pXgi%*ly3?;z zGz#}^GEqP>lxZ!LTYv*smSMy{Cg;9MD0~%q``GsPit)ZfR{gE^QnpA5*hI-u@DQA? zHG!Vs6skuJO&YkC~jUTF2Wr9JN$d z^R3po;4Up=VY)=AK_Qf=xKyck);8|a*}}_N-r_4o=3lrgxsBbVO&*=v{=~*o((-j) zMP9}ii(D@+E%k<;r)ABP-asKz0yBAXv&S>Ny7gB_VyvHP1g;$u>ulhTe!ig<8GX0< z5|o6fDy8|jl=S?don@>_!FQ67YU4$n-D2@^xchV^bSL(*`*Hg!&vDwU!`ByqfKCEv>!XCP6<4wdP>Bwy)|qqtT!%Ht&e0H_2py(v(Sz1RD> z`)2zo<(Dk}+0&0*E!N5yJ<*Thx^Jgo#c6DLhS1D z2f=OJf3+A}CXKPwj}oEQPiIBWoHPejc00>+zDQ}Ia!kvn$om)(sPyS+#)Q_oxV~o8-+|JhMI-{0cp`jv6 z{bff|NBl7_oE;aXMoQ!>kXepg>>#h$1&6*%QJON>#v_)gDJZMHyC zrKHR`kKq`5%fEW!21zG@AQG6r=UJR(p=G?!V?2SP>_hD7cz(awpWH^`>&mEIEwM^% zY9&eu7N--{M3{=^SXeEkVw7gN$u;@Jc7&V9fz0dA?zpf2~k{{A?#6I9xuz8#}(5# ze8BHcW7m{6saH2G`&5F3CtV0BCNB*DiWQ9;H*ee$MSPvexj(mmdRJ?fZ*Z1XYsb@^ zp*0F^H#%{44W+ljCAle?)1;xh`*?!^qO#&0IH(;zda7F${^?I%r)ib?hUwH)*>lbb z1ncLR@1cnMcf3E-s%JN07>bVQQH^d3n%wW~hjDv@4_%1;4KcS>GKUV`#4;4pLXs2a zB{PuJ!hNGOwI-kjfY2o13>JB<&MAv7f{?@+QW|?b>xyeZS>x#au}%<1cf?2aPM&uZK?` z1zMbmm(_C4f5{p3Wxbna&yw;xzDIbqQnnkUrl`8sIalbil!W;k4s8ZNnM~?pW-?hA zZ(0Nlh0mGN9V$tum38t78HQoDyVdgbNp!lgY)z7tbx3LTl&Y!Ju0fWh4Ay5jV%{yz zqH5;swUtZs<1DFcCY7>;B!WtMY2UO-hyzpvw?w5?6^OF?@2w^JiiHDG`jk|)kOD^K z8IvKP9iiunDCUvim9(}8mQYa6^{Z62k+{x%fSfrbV3h1~)jPQTzTt1A{{Wa}%47i~ zg`}kP_{tx)w{=reOxozW3HwE}>H9lE(cxHjeu+5!In z6}fhdJLoQHsVDoIxmRxx%(7d$9LDiGrQw?8s8hd9Ls04oPLc{jWf=}vy8L$1LMFM5 zq#yGIamDtZlHbKxZDepG{H0o#61$t=Tz64oyR}u1S0RdY*HJHfE|Qrj1#~|#8>b27 zRyjK+&1~@Y9}v9x%=lI2HtEq_FtS15QycGVHsq|P^NqB-?N{{5D_tF*Imiql~2OGU_SKw0T0jB!b4&(EUFW#VU;1+Ah?2i8cfhYH?7|rue zAollcw`93HD9&=X+78>X=A^1tTu{^2IuDqDuk#aa*wa9R$6~FT;NmwGP#)ra+44s_ zU7v2QyqX48w%=xB`E+!RgQ8H{WHcS)KDxC9)hk`b$nAg{D468~w4qN{x$4w9W=F@o z4A|+SvcWcsMJm*_dXhTwoMO?TX7*&o$>{8eFu#ez4pV$Ei$(kGhIeW z1Od2sd5_Xc#=~sm?_&P|dwS|eE9vmgAI~tmjn3OnjfrhVSumYvW?vmgn%O3l(mLd2&w2AQDry`o$NCkjnt5u)ko*@>)t_LYwuTgYu->P zOG|L=0BAHCL?xyI&e^qVKvye{X_CJ?WqEQ;X1PVq*=m&yBaNZSlvH|Z2|7zcdc$J+ z2O0tt-L4w!$Go(Fncg$C@&_(*6~7C|Y1dhCxhwJw(n>*;;=j%L8!=X1@fAJ__1g{) zKO)KtRvH1-Nxyi0%JZ%k;{@i_HKwpI>vkQ)TGKS;~?$8~1Co7<8-pszvqBkkFk zi!XjDH*M;uc9SFip?^zysQQD;)^k*(1Zok^0jXYNk|Rx^YOX_qIG2<1%yOA{{uy6c zOr}Xgsbqxo@jakvxecs;Gm%zYHvZsMj47DsULWP`HnysxCc-NyDP2Fxs#*z2J{m;+ zGd*oPXxA#%bB1JX4P;>fM(S^GyoHpsZYr`qnpJ8Ou#GyIlM{jMJ}R;zlywPVXW|uO z>PvN0-knMZoeD_?bNIxZFt=?&g}J$TbrY3s$C{-|P_fg)x5^2LwIBq<)Z0_hIhHJ9 z^0xl~S4(58siz130CroaaczI%0#SIxV`Kyp7E8#vWtgZAtrO*m*y~>n@27FFOp8iM z1QGL%a}itJ}K~pn}ur$9}K!=!|$lcmQyj~)-+STSv{(ecGbyg+7yma3* zd$iyV6*S(yF@;qB0G%_o0u)*_wne4tM%*JozNEH5J$9I(cY8kEZUx|I`2`2*TIbpa z0h2$@Vtc_cTl&e?l^r$d*I&@C^X@b5LfP%x+ctTBYN-jzGAdQs@X9^FE!&lQYGu8t zm?R()vo69smD%np?2cKkKO|>yKZj4+zfykedzBs&k3I$;&6sU({)>?;FBPb;;m8XF zE!297KgGF1XX~uv(j3D3+?3Gh7l%7V$Ba<0xKNc7IRd7-p6>Y$Xcl{!$}mj3s^Y!t z!{gf!Ky6uNZ7X7PO!Sa5=`CWe@YpsKOcyF{dHQz~f+PT5LQA{WOy$D4*qiCnUi$nWEm z++>apuUeJQAKfE{@lGzxa4X**TWM_#WbRb8d$-f22mwtbIF~k~Uh*;fZ}vCboUUIO z@p)1k(yF)Ks{xk7Hz7CuKs#$sm{a$v%6ZpzdG#he`o|it5!}bBMAy_1qDOwvd=gPs z?%^b)wPVBrV94bvB1n>T%AF>9!6IZUnM4J3QdFiT;SIi3(!#W+q?4!C9rscwi)*TP z1p;L#L!VUdwvvxlD66a>8YO#SXcdc@R^vEM;#16qi#97>^}ph1Wi3o3Nvdc=RIXvg zDUrGDPLhJl#Sq21qFay~fhyV+)X%&$`7}!{q9pXe*VLcJaL2e;)JAml$IB{$H}x7q z+*e%MBf45emqLG3e7)gObYDACoSO~0rRP`;)Ttz^tmoWAvD^^kJ;?+qfr((3N{eDu z{Yi2u>90Z}RwtS$Ni->|&TtCGUU07F;-pN5=^Zuc33jRWCi2~bs-fZ?uTr*|Bu}LN zP|voxwK>-Q8UCWuRrZ=jC zCeL>}=#)SQxCF`~=Yg&iD2KFzP^LB^mLiwYSLy?{+K!ITcG7jzw0ZZreZFMO_hJ-q zT89GXZnr+%pDNwTygO&)Y<=pgKpLzmE+N!`x^nN@GOiWQZZUTr%%?zBk;%B687qHw zKB}9!QwVbyxYt^$8B!jmQ3WM3-P(G`vGI9GuHX-0tyi0dK3iGKAK;lP3S}a#BqM8R zGD^B=APm6tm8fwu>IFV{;6Z9@>?b(D^6(KN^sX9F?F2HlER_%x4{`H=t!>Fi&2SP1 z^-h(c*#LeKb;CkI3W=P#(5Au5dS0zSQhcRSCN^$05!@=g@yqVFwEPsD<2nXg40?qj zD3r&?ta?w3ur!6@PT)Djxl5}03QCM}ZS6;tjoM2{*9ito%N}MLZ^_#*3glMuXT-D^l*T z^F_70hRLa?l)?&nk__cP!WE3d-edG*dMV7efALx0`hYcBvM*{FMQsb09zEonMcByt z4SLOa^d%$8OlUaHYcmKEHDdgqiea)KzTLnT4m0dymM||_gkwxx+GwbLrGkYZfz$HE z>f1cYg?cYO7tZ&AT;pl^Mre>9T9G^ zc@!$Xz0NRJxY=sS^gBMU1LD7h0ZGo%sdLwS(XXx3y1lZ#Yb?d zyQ1vvQbN5Fq<2oDB=Q)mSsCjUS@t69C?`JIpiZtM_Hy?7c&>Vq?_g~!MExcDyFmlt zIDi-&com|w9H$_3yM4nzna^?GXPyzltW}lxwJNErgL~;qPNLIgE9XhtMcg z#~RCL*?I)8C5oa4u$->o{e1`N-(?6=g(Xjo1!qdDNCmk)OUxZbwXm&ZmC&fxz=IE87(efg5Djn-f4&qDJE>b*fTA>j*CI?KS&*ux;IoKWVDXr zJ+BJSILF^N8gdmiHItPnQ^$Ey!dr6jtCp&T0WJIF3ww`cM7&u>oeqUmGJIxO&gk_D zN!MZa`~4etEPmrpdAC%gDVq9KrRK_2s34@luV^}j0|bv@A;O8Qt;Dhm1z@kVl~dF( ze5wPik8#k0@rBz@?Udy3wpP%mTOYkuO#fdkV*DrLKb@z{NUj$UfTGm8KW>d zo(dXsM=)jh)!p2Ec=za=XpOaM3On`cA|B><14TWLwPVF=s%Bi|C+=-GUZt8ljVViv zJPe9flQ2X9?~qjCv(ZhiEaVGcC}U-()PZ}TNN(*$RptZ<1*CP_uCP;Gyir2W8H}cf zn7EyFEL7J~H!^@!m5KReJD3jQ*a1+aEhlUAB{6(=&Y5=?+y%DgrPo^8P)pR#5)_aO zyrldhEMC2?p;8jzBal=TVH|z#Q;u^67@)Y;H%7XGL$sAULPX>`lhDMpcWog-zaJ!S z(@=$Ge%J6WMQO{vCs@_8q0n4ItSt&{YF51gGKZxtBAr)7tMok-UC4cza+SU1yJx6o zu?>UiJcX9jN6v9uxxrb?eL#MtJ86}>fKjd`HkJO~x$=Z9B{c{Hkgb(UDbwa+G2BAP z>d*p~i<;hz9BP}pj~4PH3R`za>KiDSSzCo#h~eY$x1usGQzV}iZt*L&xy$C6u z=N3Uf_QUMQ(KGe?(u)eut=NSf>jBNJiO=|xEb`@eTgm{TU`Z9W_jFY~V^Ru2l0Y(% z>_^ktJE}6y@yvZfOXL{+^Mh+F7ppb!=R7H$7Y`ru5)tKiH#IpyT97}HT*QcRvIgmH zPela1XK>PXxax?>W7{l#<3C9L-XFP0-a4>V@+YBLPLI=BfeNU(D?|VepdRpc@=XG( znCL2`ea| zM`VadHA%O7?cce&E_gX>i?R$-=EWt>rD?YXpXpWV3XnhHQS6}=`@2Up{1&N#&PO9D z)T;>gY0KBjtU}|4W>@B}^)>|~{JU^lgw!gGre2cDwNXjvddsX&;x5m(M;fX>EyqM= z-5Yk-ujBAaUvXRuZRS><%y6VNPsvv1N9yQilp^&`*qBc4f%FOOQR~pb7nWh-qpBMN z$R7#z-}0)$IKqR^F;dd0>Zu%4Rdntkp~js-+N3Ee)DUzcD^9oCbyxOXmkNcY+Y%X> z-Mhp`#w`-W#F{34_+?u2VOxV{Q4Jy0TC9Z_m%M*Sm#L~;+c^*nP2-{ks=LUgqku#d zHunfhjkO5}_?7;+L~#MF6W2G5AH-6TrV&rIZZO+({JITHNdgosl9%WSDP3;4RIGYb z!AhL8N(q%uD%8{^*;qic;#(ss|>X-K?hI zKT4aJEt4mGw!cqdsZyNVum+dkO>~RZq!Mk^FFs~+a0i+=6(_OoJI`yHN?NeGl^FoC z+R_Kq!SUL@ilO~gmC-D#J-qT=%9s|YiZ=;#QTWGSWmPF>$O|2yNB9@CT>Z-b09xvW zlqXQyP^};~+J+jb9FAsthHYq4>dEb2A#Ri@RLW16T@Pq1Treu6m|KSmlzT(%#`8}6 z%_6jv{HLHjAxv|8)D`X`;++zX>BF~6JrQ+Or6D4E&%z3bf~j}-X+iY`9ZXj60GVqPbe3D(l_{s z`#xJx=1*z$Rk5W$k1WrOQ@JrOw{u5`S3W-rVYq9Ln(Cj~E@wuI6_v2PrA?!9Ng4@9 z;%D5~5Rucp;CQdv@3-Dt3-PUvz6$8@+^UxN{pN+Vy4e$;TA6CIOB!G0Wf z4*X4jBfcpud9F|Cl#!`WA~YLV!pH8^9OgedpHVWA(82VeQBeHq17#F7mrD9uMY^2T zTyC`u(@?THt=HBYK>Cs*p_#dCF_*ibTJIKVdw>aNn{Y0DzSTI|(lC;_(X2<5T1Lkb7aD&5020-% zhNP(jbBb2gk<;;rPDtu0ZUVqwsWR(yLoAS2p%OnGBQcTst;XIBQ)y7`%Hrs1-)(xZ zpUO~$5Pty)adWf*OtwfO)y@GPS~z=>vLm{d#N{(;I=JFY$Hq|eF_XKXLEUou2tSIc z>czdBWp{Np?b^M>p;Fw^5w}kX&Lyq*?2q}1tK>VL#t+MA3R+-($}E&hVcLsus&L4K zI+^~{IGDBLEMeEG&axc#`g)Dj3R&X**s;5ETAj6|dQ#eego)|bK@!ZzvAzneaeZ+q ztDK(OHxAioiV~Uxgtsla2z{(*=!V-TeN`$tH0Azr{zg&4a{r_7Y4 z1nP9?Bc-V?+u(>VZ&>WjEYv=LKt5TC!$s6iM{M~z1rT|2Gj^)gTE&r*`4klFi0ufT zQd>Z+IGk?RhdEG(GaH+kg=+2!D%|1PGy|;TILxud`mas#BN&6z)i(_E5;8Fi&Wg}V z6eEaa9p{#b$j(s9!6gwJYP`3t+S zS-4cZr25GOBod$99&_it`M1T&V;Y*RGmCb9<0&5V(K#H2_WR7Rw3@3!&b)+;L6OuC z2~G;(zj2nAjtzE-50!Qr`qjIes8Ox#131Mh7f(}4;*!yjRE&ulct@o%e74@@fy{6# z2gz}ph+IS3P_bEZrUf;OdSrE+;@@SI5_C0HtBgTsP>CyUiCRqcSMdQ8KK)cnEq0v} zE~d2(R5dPzKmdQ&0!V7$rCALaRg(K|bq&zciA!QrKf*8gQRoXG@g? zAwZyYGace%9XV@(3Z=^|gs~Q%6>WTN??))(>}?jSeM-^kx3nr`gQvzcn?6L=$43f~ zyuW+f+rNFrC()%vo?7Odo0cz2uDsMK(z0YVl!?>EBXU`%+)#81<;D0Eb`39fuPW(2 z-p6JT*6;#-6z((b1RV=~DuI<|+qVSK{l&1nw~#Tqta}KuJ$t=ZtRaS4LY1j9^AXTQ zUQ)to*|srKr}&;`*Bx>snZd32s^j8+v}d_2*i$`pp{BGY$rUocQ+(H`6Zl8iZr5K; ztNkHexfk5tF4enY^%Xal_QSjTS;4XnM!WKYEEhk~xZTd!lBU?HVIzN}qT$pN+As0; z`MAqRNa;K}EL!)zms$LSB4j!P){3KVG*;Ul-r7Y%MM`Kg zj$GtC+JRQwmo~W9+KCHIomEbvl&i%|poE{Ofn9$&({ZA-!^dq|(WCVwH3KxLN=jQw z3VJ0VBz5o-5Ic+A)l0VA4N`%hOr-77eFsP%+e<^6T{>i^RlI>9k>d_j0J;Gv)w)cM zuAOxjJa($$!vl3qV^r=s(@7eVrKAu5_{0U@DX@-F8Iik>g>_e~ZwbiH0giGZ?0MZ*dOM)R(B+ zsC|~4X-h)II@VBBl@U3K>7||ZK=Y)$cYm; zpMod6s-?}uZShLRtq-;W>zzdcd=_T2KLm)7ZK?54Ps#(M1yF{bqNnn(*XkBJJmB)r z;FvBrTHb*RDDiIK?H=!h&Q6#BoI4J9i zv`T#=MgfxMw4)@O)Akw zakd>vTW#Y`Gq8GL+6oJjP~B3iE6~$RsD1Rdl&fGu5|Ona>Z9B~3AW6p;z{bGxmPC{ zIky%Dv~&SiUB4JCIEA!TAt5jOVt(Gitu(d%}JA>yK|4J5TnbFZBzf$~|_e#HmyN0CYv7+laO{@0Gw0 zih*}EbI#aUT;t>``;xPeIPU)dZl$9a`g*pjf>Z07wpUE-wKGy22_R453_leX@5>>> z%qa_N+k$B`OHYx}T+i8VrrB)v@$dGo<4!7Vmil^CT$+$Rqb=E=+?gv)QX>uTW+~SU zjux61f-Cyhs(S&D7ST;P6%24Beuk<&&;H_k!FkAg zks&GkqojD31OEVz%nZ+U#zJ`{dyQ#bKYI0kWAE!RZqWuh7DmyfPhZtf?@P>6E6cNv zDOV(C6|L9gd0on{*8W*U{S_quq)f;eN3=V8+xreTk8PcxI{{ua?ms@f#W)iecwTtclHYf-W-qK7i``c3wN8`?C`_cJB#jd(MW2dgZsgAvhW=G3 z6&Jf09Bh15uMYh1>Yp^wa!pyJ`$KzTLxj`%9G7-_xdel?2BF6R{9?~{6PcNj$HlGv z#b-Eq;&&ZnO#m!F)4x3lC;tGLl_Y-Lmx|_n@#Hm^N_T1MYxwTs^49G4tsawmR(0q= z(gn@MzA^s*#NXZ$ICaBYDWU!n6@Ku3MN0QJ@(=4=^ux8aq`D>MSL#=@Ueoc5UBv$Y zm;!^#ST(>O6epZ0t269?&|7K(C|F7TO+?40k_iXR(?QlDuV)Bsb|!^GOsyEUC{;me z0-~+G$*HU$8Lzn?Sb&z`E3KW038Q9;>yq4+8J zN>Km+CQLhHVChM5(W9wK$me6W0gctipc;~V7hNA|KeFYs2c*Y+#(S~H5q8bp^y_dw zC9G`@Qdv_l<25y{u3^fd`I3X>LHO$#&o}NMiKg1j&~znwAG6(!`iBebn`+~$T6{de zO6V_?ZdSe=uw40C-(&SCacWl`%PDCZ9<#Q_5?oPA5Io9g0-!u68HOSma+3>px~0cT zpB29Ho4Z~yXzSQxqZ#u6JQp?@ZMTL#>0fu=F{ZcNAFSa#Gml=n;UHdxh>FG<2;MfB^a*9l9F+ zl0R)%MtsT5oJH+Rj7{?;tE9#8+YLCU$Fb^vO{X7IXxi01D=C!Dk`fXfLjW2y3^7GGhnf-1{kku;Ri(3kt!?70Kn*hZ?I=>V& z)9R^yXIIr~`VAvvWD!`w?8Vc>{8yS}y`Lp?XPd{M@>PL(%nIIG67iB}r)kAvN6@Q8 z5K%68s)mm$$*gpDhs##y>YDv=M1d^@4bq=U#dnuJ;A5mP{PbEMYjL)+=OJy+^2@bd z3HyPi_OXw98Om*T{5pv_eJuq#F*0e;s@E&Gq1P0M9QYI%j%rJaC|vbm~YT9c3{sd}2?CS_owh+^apv&OtS<(A5?n&-iM=Vsf6H zOb2!yi3mzZPXZ%lw}?3CYP_L!m4Y^wkwsbjkCxi6nI&yi11N5gD26tUqgc7yjjaHz z#_~}4NgWa|8Lqe1<(RFyyrnkktA#2qE+~K!!k!6K=Pe}{4mliexB=Z3ch6;-JbS&q ztFu3CoHfFV;>&A_w%gZCh6nt!h-@9ZM!MXTHRHYfngcPnn|6|8hJv0I>-zry#X($(=+zc9v^LoVEr2B;5qph4N=twdh2UQ^#}y@(j0BsszJ5@C(i@SpMJKC10KwaeOxp>QCD z=ZNu_7|G01ict=RQ{U0`i|V zxAz=Ed()A6HkTS(-as~lb?w(!)FZyEO9Ph-MAZr^asgtcUaE-NXCoP>C5okQJQc-& z0+{*JwBb=BLmAV$t#|@LFs7@eGLoiyY9YUIGUy3MMyi=86O{aC33Py>Ev<-3ZW?;Y zmh*=jVpnw`HoK+8w(8Kb0#+w9q?6)Ad3WyHn_S{Jgz{UGA5RYj>QA;VB@RQAMA?U= zYpU$0BKvfEMCa}^u-kHe9=9`m6wJ1p?JH?wtq0Qy3f_rNSo}cZ9qZ*5U zEhi9NLM0w+&$)lM?kRT1J|`Hd;Go`J_MQ8a@il+xuxs%)8TJhmUT)g`{%zJ+5a4hpgmu!>YmK#ut9J`3uRZ#5rhZ ztui1_SlBpUcQIMWjFytNK;E=H7G24^L6*cw8*v$JdRN5}{{Z&S_@d|&`rK7b06{ZS z75jmUqeS}aW`t3i&e=@q1BR=B}8dIfr@04I6p$PyLJtfb84P%Zviwp%^pK{ttdqU zh+NjO=8>Os7G6Cr$+tMF7ja}Rwf5r**(*KFVsA3TPSv(uWB{WyiOhWCTer{Kx5asW z_l#aiU9Bm=!+rcxaoBC!PF|~UYyg&!@`8N2>H?tYxmA|Yxxqrg!ZTjwV`)y+H{K$y zfza>y>BkFNXtk1m_vi)us~Js7k{kn)R;_9fg~v^JHKg&E3Qk8 zcZ!&@;e3!EQ)rv3zuqE=2w&(i#MJfIXe8;UK@ry(`zm@uv)XW6!KU*?2Dzi|I>#oi zp%vJ_=?k+3f8j~^1c<2LXRXQ8MWf-kJ@k5$U(%7>Q^{5=z?SHg8(ik5<|jVFQ7wwR zWTQmhUl}YKI44sm2}4Q=>YvOd;vnf<6h*yinM#*7xVc7ZCt?$9!sOusU3$b^T~y8RAEzU^8i-ts(G3G=QYNt?av~$< z>J;QF>IBI^Poe7w3+VzrQnV!Xg5uMv03A_fN&kLUnBW_Y&vW>R_f`sSPc);&% zQSi)BzAwmpjmIvBuzb$pjwBhHRv%HORE+gy&CUJ^gnO@P(4r-?f?u7WsTGg=^5=U~ zVT-}G<;}`dtETjvAb6>nYxIJ@s6DDOZZB;9nkC*N@3%O6lCaCJMP#BXObagn3*?TldyHha29%D0az(lU)^(g^8vL#9;pHl;Y34i*ndK9t1j0n zwQi`ZTg7wSRHAyv9@-kGTeZCrSfqBLU=^O*M~oqisYDm(r>C&$jw`tGl{W_aouYR0 z?SGt~Zr{jsO}x5yY&UHaZNE}Hf(tG% zVV9%2ACLT0w)c(287H-#S-o&`_NMI?J3B~w1-sG!XfIX=hy1N&Gp&G?rD^=(Kb2Dl zwC6Z}ZXAuMk*Cx;kBWlk7&$SwhBpsM>G&va-9pHk$x@GV7SO51$m)W<%14m9;eRqjbeBW}k`hrx@T+pR84+_buN;(Q9!SGBDJ2Q@e;c&X+B=(>l73(_Gox>Lzkp76;}M z@rwp>okLOY*C~Lj>Jf zZ}kqSTux}f?T_fI!s42Wck8W}N_Q>SOLElR!hz%zI?JfhNLo)r5D$^fG$CK&4RH*tXUsM&$B-Vs8aSV$u|+ zNd)L3F}sgdE@6xwieme8_PdmGEzXt4*mT>SQhg)@nWz)v+AlnNjk@I3{{Sk&JD1(4 zv0syJH%t4K{&2tAp4#oUjmPX8T~brMYe^)N@SJIO_^V5CN-r;6@|^Zf#JS`HA=M&zF|OJtA29yc(vMu32310gf3Bgs<(3HyHI_P^7HtuiSccF1aAX>x5H z6z!h^CsIn2IQO$VMI&fPwkKfeppi2u z2wXUxsF}FX1EM0S_UUe9m6{~^Pxxo?gByJ1Ub^EA9tncw-WsH!v(+h7zyzg1DckTe zixxWUm7ekrFwol3bN9}c^JB~^=)z=`ER>#uO(3VsLbFSJcEUvy9^;#Q-9Kz{St^}n zxj~lVN$c07CB~r^$W2o($@gk&ZMoOqa3G~7Msn60o>188uDo|1`D7X>i&d4H^*R`G zWS=P-j|i>0;JE0u>uhslZ$w$@xbl>zR8*tX$Hm^1B=B`c(@rL0Mk=~k5^@em#4 zLE?#Rn6pvfu3y@ZJub&dZf*LVn-YK%yQjeGta$spovs(HbAwl|`yJhnq#`#tUKba8 zkfDB(nb&KOl_Ug~%7=u$G5LMmuF1(8$qiqn{3EzfUPJ?kKcz_eZhN$?hR^i`gDI1^ zej1qbZd0}svEM0L^fvY~Yfy==oa<>VD5|tgVC+y?T%d06qlZ#c8EWD@iHr5N{?bcz)~bf;C{j^5I?}hK z{RoI0#t-V^EO$bjvF@xkFAi^llbGiS=e(nsyPt9$W0#ww>Tmguf~3g+B_IRX03n_m zz*%C=tr{%ny8i%i2KKE6go-O-;kzPmn>{@!LRu*RD1j;W5whFc>}?m3F1a|CTGy(2 zs&Ywr%ai`-Rl`*MmQKDY2&~@iQ+uSd+Ez_XQqm@gnF6Uo9N#5qw7)bJR}<~S52K}^ zx7ARWOfp`=PYzEmY6tfCF=TEuq@ zWO@Y*J;L2@^>(_pEw;6|WJZY~cy);jDmSXmc@4~ubpTxNdyKSYjaJ+pxKOU01epZ_ zW+qJFJcg%5GsOu60-z0Ns-?J<2?QN{PO(~% zf+eI{ff7YU=bK&7WJx|Hbh*Xcru8iiBW=}$B0vE?x=+R`mo{b2&@V^v{F7M0Ql9G0 zN2Ogd->Iqr8)l$XKehq>n&NsZOxdV|btvKJY%Pr1|mZ1|^P&$oBi;g*siJGia zlixadcC~T&?%A7%E#z-mt*$HjYfDk7sZN`W`UtINo9?|@c#$@dSH(swZ`yUCYeTmx zbycc*DOQwdLCYwu2%5Jx#I56EYJ=`0uGtu4K#LaA zIU%lm(@fb?DJcSqv}~x$>mL_rl+0Oi)i)P@SG`vtp}McrH$TXdCT3xgI*^M9)~3ee zd3xgUU1f6IQjUoxVjGg=DXV){F(xt!s#IF)K`Apq)2y_PL8(S+sWaRY=yx!GXqMHg za+isUCVYNk+jvVqB{D3zG=cRPv@(sUg@aGKOrb85lm%pV@c6yu^)ZKA@_ zRA?@=m0=V8Gl^ebvNM2gBN8yT%`QSfs^}Zpue;7Yu-PbWcZ%kyYgue>O2|y)By^7h zcdN458>^EnLZGiocIUb_mXHYKG%981e(W%7)he2%tYV}QBrPNvfuQ+cIzqV}&E6v=WlUmEr1r0O&me>tJ zZ(vDjnR1oIr6SVlwFP~NbRrX`aH9(oUDx$KR z%_*pB->l&=N?cQt+Xk?!N%rqhuhG)T_DsH#jXMbHumy#3M$%HBUQ>Lp9;zFsG%M#Y zG=nmY^c7yjGyO`ZEVi1V>+NEJ(Jq<%V_JHT=Zs~eQZa)#}vufiQ5Bs%1s0=)@)uiCQ*0Hwk4w>cURzEa*wD%LwbuU!%w<%n*guJ3fyLhIfZf0^z4DC$5 zbBy0B$*%5&9^$vWA8#>u@B~qc`O{Sxs#CdJ)MvY??-f@?ReNC#=n+|vUtkA9KeieO zrM0()?MX1I>YJr{qgLvJa-UD#A36H3y`ppp+2g(~1NrGZ`Cfui>woy|G%bi*jgEO(DEoxZ+m2I30!n+9_mDs$< z+!xJS(!MFqdG|LImPW@N0~rYv>8h`{@eGQ&hwZs70ivU-GkTi6u-G0Okm%B15gxv> zvGCrIb!3ri`b);-a{{VQ1x$yig)y2Tr(n8ETp~`XCy9YYr zMzEfMkyH2%r}Zx9e%Rj7d57C?vEO}8@}-)d#-2sR9d^-3ini)15*W9eQ8qnF6CIujK}z zT=B&1O5bnDBQ7gg(E@4%uUPH_=Rs$W<iky{Y=_voyt_D)QFU~VIf9MKGgbg*9zvjq~EmJ9CF=Gf9EgnN}k@r zM^d-ll4d*sCL~~l^Z{3{vP|(Q^KTgA+!bP(4Gu}j*y^-}w`gI`#&X>72k2s6-xTLj zRxUGvr0vx!Ecj<9a-CH*rk$#)nl&m^mRdGTRE?DpAcoolao0|!E%vyWp>FvI04n9q zLVKA5A_dMq0bL)oud*j9@Oc@ds?%zQH0?1D8JIO1;aVH2I{VE1c|9d z`ha~j{1YJ_ZO-$ou3Ar1%@062FJkdd*xxfP>2UCyS0*o2+5%{2IIsY^N% zqd9@n2e!Lw`X@3Lk~VG{zkGd*DZh5@(n zw{QlEy^rLs$I9wBil~zHJ?iapb$5E_>uYP=A#c9ooLh^Zbu8*nj4IEKxKIH=fxhPu zuopSiQkww#dvhNk*&fd_dRk5)<0cX6y`AM1E!%0FNCc5g($A{;i0A@9J~1LNc5pm< zj!<%QjGfRT*_Bdn*hV?Tww_+*4j$ut-shM#b>n=ps_%7D1wF0Wn|p0+EVN{$5L_e} zmxlVr30N(~T~I2kIT$kcR_-^t4*~jr;+}lh7DK;j?gqJBhpfZ-ZRPgq)RIA3YL*Aj z;T_6N?quX|i$GG`KMvTi?0bzv>-wrnn;ZdZrK0z=DyV6a+s<&cLxl~EqkrkpLDQ)K zL>=wbVFdpGnZLXx@yu&Pt_|`kj(A0yfyg|Ea;r+4Wi?f3OR5BUzfiRi+9s|ftLmes zqn)(%3vVp)5Vp4Us>-6lRQ~`_;s_;E?I^C@NK5Dx<-9Fx0V$2*eh=jT0C4<|WWsP9 zPPa12Z!ZMW+9?`Zy(Fnp^sL{WG{{H|U*sZ^nxt{>xJYuIR&##SHEj0Qgahr~~ zdLgt8RHN7kqH(?_dzPF>18RSA)j98U@myli%XaY4IuA8nEd9rE4tQ^nxc>kQ;*0Ip z#=B*~mok#dO*jgrSS8dE&V7DcU1Gh0DaqSBJ3q#e5!(iPuP8uxKs zZIh4Nl<2Uo#~b`a;^N#5`%6iuQD5;2MSG0*Bgx+7bISbVk#CBsT2!>yEG-oZ8(k{; zG|CTCr^ik5ODW{pJJKQsuey|Me04eAlP^Aa9j7GOE$>(phM(J1>PTz_4cd6ck13K38&wOAV8o`RDxtjO zSFGOp^Yu@uD&e$o1y!%OYsL*6z4P zp@ZzCdmbD-6jkgkmNM6B3Xqsry+3mpb(L7PmyjzJY1`?v%ad`$Hy{E+h@60pHqVsY z(u4xAT(@rQ%!GnKJ{3uqxl@(%TjO;%YlT(PzM*q2xKw8|Ehc|tPQjbGk-(~r_YJVO zjDRYwYl85eL&&UnXCKlf)#aSEa-p%>6Wu1gz|*b|{#4mg&&o9WtmAAjlH5P5TTr}v znBdONa_f5-0i)glrJ6Qf$+5-H(;+X53>&b6r9=V)d{vWX;vd%=cR#3f8 z-AYPHAxQ^WN;ABT*dcmDi{c!|iiHzyk5bjuxU|d8vOyj7@r>T|ddXgr#6N2QL0hC^ zXL@%}p#K0|Le6Tfw$G_b9736ze74C~vTd_a+LoOO0V)xk^V>5vHb(pk+IvZM*z-`6 z{$RT4_La=GOP&c~v7i+#ns(9_5_KT}ct_yZcKcHMi>V&YwJY>%xx5Ntw&kQzz$v9m zdqqQu2x(Iq4I{|eelb0ilyep%J}|IqQ0KB1EeKszTS^R|60!$j3&D?WVA>jzg^v*H zgSCfM%tze!-+lqf9LYn)b~YVsafqllO4k}wVO39rc}DJ_0n~So-EXpP+P2dY>$?2( zLwEBHCm6v)YbmUg`p?72D#y5UC6&|=CRBP2f4V*BE$F;EEB7Qz{D*99 zLkno2q-Nq#1!iwRcLLc0S|8g4Mom;yktCD(wYcu{4SY3%TH2w5=!zGaTU4?)YEFYl zSGJZ#LKjPs+g<3t8TfN<$$)3pxUhgB9mjoI2#$F7wsW4IB|r3Inl@+JMv=ZAUsK;NplpWkI*GlZyP?YUG%qiBiM7DB5bTH9iv_DT$x}*Tn1o?!e z23xRI4K+%zYzLOG#%)Zps?;o{GDzG9X>^I0-UwCsVI=69-eA9C7trsZ`%6&=XbMj4 zM}UTAbywElgQ|lUtY*sN#YrJWM41T)xufPkJwFH;gED_1T(4@B+7j_q5aR*%AbVM!aVi`5;3?wPT8wzxi%qBi#t?e5LXc*B)eN(Psi zyp=glT0jXX+DP1?^N%g|1MVjKjbHr@-Ae(ctJA&0?WML})8B@hsZ~hXei@Q+?c%D# zkkKlJu8~CxNd{5}s6FQ&t({73+WoM&XHDcHu`2?^FjN=7xzC;_2) z=aRv_#U5SnU#6pZDo{z0)^T-;MJ zbYKc7i^v?r&N)!y9;h37re=L(7tG}ZQV_ih#`wdhh&eUXzU7!~pa(X{^*^>CVdTxK zw4MVcIwh^I+*LKj#L}fXzO1 z%Hwr(2|*=DTSPhx!P8M5HSb4tF*7yx7r!I){cF`7uizK9WL?{Q?bThk0R8?iTrGUD z@yEKHC;Dwwr_-iZUvDbf`WXQMZnUB0RDhhcG5Ph&zRhw{x9j-yzUvOVPxmiR;=AXG zx^cA0x7w6xRnT|0@4L^k&SR*huC(&)p_-SQ08!OdH%{tSMC3z+N>PI4u$pX?MET?1j=}-!qc7Al=th%^Y+HS*elG{rPxO{z*Ks4y23&0$9%GtWv9 zK~SB}H#HU{Ll|(^Ua6p-tucah|6c+9V%-qTMtKp*=@9@=rl zC#TLmQ}&_AuP~j=`YSmLt!5M=q=f~^><)W^T+MvXTqAoNdV;tlRtQqdZf-MJ2ekT6 z#y@o=0I$rSt#CmG9`a@8GS*W608|Xf%0bA0h120R+rg(&w< z#A>!8hVw_bFcy>GnUCELHIgPp~h144K_(zHGLmr4fC?-Fpa2 zuF^Z{D?srJkv|O~b6HLWXlS0azO*jd94eBlOX)J$pnqrWh_?e?igBfsN{nM0xa76W zP*FC0chnM-s1qhpIgG?Q>OjW$s2uKY3n+|_=oRaGXZs{`CqHBKmpevZOx0aoKkAci zX`sD~?a--fP#=VPs?+V-(#pk?-cA|6ri=5(l>PZe%(RwP7H^r+gSgg|1FC@D_&(pf zo$V6p^)63T-QJNUK-IRLr6ndsB{M&=GLGzaLToHexQ+hgZWRPCKz+J>)nhrv=-9&) z%rq@NYCd~Lw_S2hNu{&e>6Y@8CjQ+*Q$GIygk`)cCq7~p@7&p$Z)_gpspzc(lJP^0 zQ=v7rEv026b6bY;Pr`AaUqTpYvOF#Ky{!~st01`IfbyP@AXWrev7&)LBlCq8&?g$& zrsFI~Q)>sP5e;~1rK~!p(U#JMg#)SU5z%y0(gQ>)pcS?|{{W=n;)IVvlXYx$68b<+ z3Tg*0DH{kNj-BI?fkoSgqZbU4G@SZJ$D$RYs1t|Geo#7oCs7kRj=E?e4pK2W6jZPj zI!jF^Q9Ll z@x{a2rh8T@viwS!!-`crcR6ygpC_j^^>UbylizHK>mNaRPfLo=Id&Ulp7REW_o$uE zTuG7fW?68dv{gGxZ&KFf6RA>?c9j(XBEbvlG%60-;8Knis$21$CP9C}O?}1nR#r5Z zQr7f|oq&)@(ixKB;ijqCaWP2n6>(mD!16XZ$Zub9yMiormMWK8ZKjhp-AhgsDGrJG zlBsGU0A?j)wCJ>pP9JFKDDhH4wI6RD@38)FMJ_XYX&9#@p`-r*rL9Bsw<6fwrBbb5 z#Vvg^uUSk9#O;=8vI-pv&U5@4TTfd(O-~kChT!>v-EQwjDeBINt7rz+m44$BE`g5< zsTJJu6v1D;wznlaVaQu?xlA+ndb&M3+l^FK@yl=KBj68Y;5{R@XwTzN1}5_Y2ixiAzSC~y2ksA>2Z z`|0XG1miEkD5=W>6vz4XDbRjU2__nRY4{{AD^gWOPb9X^g)R?YrD(9s920+aI&L)av720~=9ptw8 zD{(AG@g7BC+>?%6Vy;XwkHGnlhxXAl0Y}CsloMT4WX($1S{*`qVS!MtN6Yy)e6d*4E_EsSw>u1dxXtCqjh*^yw6b z86b5E*i3NW!MVTsDec4D-)_oj7(%KAJ;lAD=C_osDgt|xEn?4%$^s}>W1ZvgjGYt7 z$viQQ`-HS6uHRP9H_B8L=lV`Xv?8ACw0SA(wvO_WTpFbOCJFbJO_jB8RT+llZ?u!! zkX5Rk`40`suney0i)NNrtQi%xa;>+#nGy-|xP^3D71b&l7^2+7So%Av+_Cp@%?NW1 zT2rgV4F@a_?bL@%{{RTHVkl!&ZdOQ{Z{f~5sN~Gz(~na$S4^i8s?`8U3EU(!@q#g! z{{X6-G%ELSIbg(NoiRS6p!`>5-*3NY_N;o-X}CXaOwxwKTar42%pGHHWGuFbHLJoF zZJ#4_W|LA)S3wvz9NMU;DcBUPjfoNgGL)jfb8<@8@Yse1(LGkYCfrhhigcuWN5&#& zozzy_d|Y7kPCd60qioqs8f4+5DNy<)jEDsZx>HG&iDB zHH}h$Ka(T!fgLdj=&eBTL+P=~aH0IE2yEOV+iUPjR&mw{kw%j65W$$D+hbHubH&(8 zYaz6f2AWC=x15z3b&Y7%LEmszx@v1J#BJAViTTPb_{)R4Ri1Jjdv^3(rhCAurnl!S zlD#pxK&WZ_T1qAFE*bz>+wiV3XcRYlC#BoF^#FnSb$~`ipr=3EQh6dtz{YW0|k+}R^5s5&i= z8-$L|5vH_2XsH@$DN01-G#x&$=FkODQp>ui;j$CuJ-V29fKYYS1Gzlf8z*;ZkH#7@ z283HJi_?+ET!UX^b)*F;EhLE$qB=xX<}sHeP?ZJLmT%m{MK&>Sa<16eZGWs6~7O{UlNzqARDX`o$L(N!&>!&T%bmXxRD%7juzx!lb1InbV+Fvgl$`g+%+6DC+9?6iPDc zG=ht$Q)I0e4j`N2+^li0Ro9ob0#g736Bbby1%me%JMBMcDpBFw7f)MM)74bdE*Z*Q z3{BhFG@VzPbKIIz4GNeT?;P4}v@N!pB%v*jDoF4jD6m5VTHO{umCDNVQj0HCjZ>*X zun+|F=po42RK>0HflbxgxG>w3S?G57LvjV6;%>{1sl)c|h>V9NYT;1Vs(oMOxJ2YZ z?fAp3WdhT%_WtwQ7eSmM$89xiqc4R3B`=wZZeHkAuS@XUEm#!y!?@z_3Pd~ zjfCn{r1J)f1iZD_5SbrD8F!Q)8+1_8+@+g#+GYwjxReglG7yl&IW=C@UhEddE7z(( zQbwqsQ}lvMKuT*KQbOa(w%TMkC~3V#4FZXfoCsVI5#wYfH}_6jy}ptgs1&XAl1#LQ zjO9tQ8WU%5&ng|r0^~BP0jC? ztyCM<^;&$hoX&z@Sm~07Qi_HWeo&c=kKBEV*p)bLO_ZW?8;5U<;5Xk!-Bh)o9J!e2 z3f+YBQZrSpcJie%lP%|=*RhYGx)SO-C0*g)Ztbq>m)RagQ1!jwT-&(^LElI*q){-~ z%|?o+xw4^7#OlP_GernvfV+XLBHr*j0bcjZ|TpZn2;P1v4O*Bc^7p2bu z4Z5XutfJjSl)U?CU*#vw@qw;fLixi)uYdkYrk$j9O*(=3kVw{03~dfN>VTRQs>eAm zA+?|~n&rHKpi0!E`^-tji-E?ap+?+ADCkWRv5fN~xxFvesT;<+$|o&w(UYQrx|Pfo zB^)(ix?fMNS5~#}>oeLdc06g+uY#!E@}Tl68Q~dP#C6>I8|GfH%^=7cH=2JsY~Y>Mc?z5>1F(&oGv?gky6OrAV~8b#wBft zB8s_|h|sCMVyUG42n2;l01loJHxqPJxtmj}BfQm8`fdP_lig6<Dn4C%#R-mHtd!`BoKZ)Y;3j9>|HW+S+(%l=$`$!geOzs^*SCyHr#roV#knv1(?j zs;Vjmp_+=yk?FFN-9ujk25ib)QMT<7u=vweN4dMVc`ljd&K?y`g~Q$^RgG0ym+j90 zv3qB=&{~mbsdmv$&DQl^+Ei4knyZb`g)i54_=*w;*UWmwn!~r2u{cR1i{5(GR_}QW zoWm0Z8~v;Ov{8oVkX^5puUFmfZ`|vuTvL@bZ#t(IwXE(@JC9OhL4>iOHKK~1ZQ2&L z$yT%f08|jeR4b0vxM+0zzA%9qOqn%X5_SIAd?p^`XH8E~`jD0zE){4JI+Apd5SF&5 zL@du!Bo({igC?dpBb!B3e0ZlUB28C-AFEvQOSR@jeKI=h2Xgx z_YJJ0e9nsk?l0Vz72~ZfmT>t;@H5nZNg5B=UxK!LOYPn4*V?rLGH-T#pvNFzXN5VFoZ*B1y#)sa>9DKY#Qt{?z?pK>~*^Ryi+`}LFwcY;! z%yb{11f9!%=@?!t!npqc+J_k1`1aDq<|uD0P%}&t*w7R<(o#t>>Zo@2x%O~3*|3ZO z=S4lne${gq=j!wGx<{R9xS!Up1MMBm{Flkz+kO84ROi`+eo0pDAf=&-x`!QYfTkY= zw1udIg##lm2*i2B?z*~)XZ+P(?(D7-ELJ{iSSlI#u0VM#p&1>>0W~GU2tEMHD>+}7 zE5To)%DGEgCXuqH zEcEUTCZ%Y%we8X;tt`n3k&wwf$di3yB?!t(%ab+meir30r{Dj zpNi3~6*?-!_YyqNYE`ZKNBd4@e5m~u>hW*=20WmjTIW!Op{dXK5=_=d;T6p9aqO|o z+C5HMM-A-8Q!L=;jjSuB4{qMZI3wG&4Gm3?8)D;pFT1tTkA1L#O zBkmSQBJwv7)8H4iIB#n4IB3JRg5lJZGn@VDaQ^@fin}!>!8vwh%1@_^+t!y0Y6JfO zInm^74)ZhkL)?G2*Vm*u!)rXsADMT98;xmXBtAjl6#{#+`<3Qdvvt;boS8Q3dbftL z8#}9QkPksp>trc^5KO)iy5RU-hCB_UbNtHkhbitRS1|Lr+Zp5)8E2Wbc2!dQ?kFL* zRHX*TK{8CW`NhxObtp6njT8)Kr&H#xSM4_X=>3{~$zC%`o3WK6>0qi6nfD|9vful8 zt>?7*AT6M~(kf~Tz$gypaP^35Tc?VJ%u4A^I#C3$@Fr#M4=A%(@n`8Q zmMmhK+N|R;xIC6^)`}hA7v9{UOr&I#>SEmvakjAN-NdZNBZ6e|(a+dCN?B+8kL_pL z)-v6@+hw61;5lZgz{U66j!#5Yj@#Wvt8T+UWQP`$p>Us>GKS+h2|qgH(9Zxrx9?T^ zi)=>!0A|pcq#a!E><8-oB|)t3+|MX-3aeF?WHmXx#_EsL*;=T0mMzY!P4v{$^xK#+ z@}>d1$TU|3^+tu40uZtQ zKk(E~$|Esvm+D&LDe51+PIB2TUu<@okPq()!t<(>JNB*M>mpDRZTVg4y`*iD(QtNP zeD=nPe{Mgj%HiVC{p&ly-R;Ci!{DZewA8Ux?@Jb{sUAwMG5tI2o~vlGgsCIODH*t) zEj05A+rb>ekRjbDjs4qjf3!X#_W50$Vb;bi8P-DTk!hr9&|h+)Y9+-ul6OcMlQAeC z6Lom`DDmk7s+;Hb*lSovwNG`$PzML_9%1gU+-@bqIC*Io%(>^6OH}m5JuNjdLyuHA zQ>jW4XCc(-1!QkywTR6Afl_$p65}s+%Seq2zju%CUDkUj_9w$W%POw~8tcjtZe=BLwN1pjdD!Ir>LTJIzFMsyiwRrpus0?WL4y5>#=A55$ zzUVmL+eQO&%rkqsS$(uAHcKO9Dyq7tX((x=4M``d=sUsOBZ1y#E<+<8j}zbmtaq2b zxV|f7<0HYCk4t<>R8_~gm$)Cfhbn)Z^Hsl#@jb2Lsx$(>(b2QJ{u2p0IQo;-U8B(% zu1}8QmVZxYLpO=>s_=IO;5qK)<$kv(xwfFxH^PeBxo40)xqZGpzd3Dw+Qls+tuB(s zj8mnxT`zUcwnBjiRgd*6OX9Sv*#3-k{wj>GZ1FVVvrMO(BAYp7gf zF3V@PR^F~wt72_z-K(oA+olsfiYqFYQ|YFE!j1c5Qpn`rDHSy<0>R*6wQU&M3X}TH zQby}~Zj7l3RLZA4byQcwwbfbUj5O$)Op4@+h8$Hv1c9hBl1IWmmOv3r8m1%kBJFhn zuzya;^OAI!fQcm--ihkR^E-wT(N&hH&^!{Pm)bHtBy|vn+A;lC&}1fum9 zp1S#ZxGtg1rA|KT$tsqz)(NX3k3@zw_Y(TGs;6FG1BcxL= zt_-hQmLA!#N6`(!F>;Uq4?yt0(%o)zkvesPtnJcr7h;M*3ejeu3rO_JI-b%3Bp=aN z8r|(Zh$YW-*ng1xY0@MCl$ytoPCvD>KD1>q=09n#0QGQ2FbdLYdgrFEFRf9 zqvdf09WVl|&sSX)IMLvWp<_3i6r;28o;=RfJd~+3ocsKuQYgh#Oz#85IkvnT52sUS z^hn$~&PQI6W43T;=&JYa4hl~2#tnu)exQ1yiIp~m&0bWfOHQ+ko+*XYpjkg9oshJb z9T!8tZC)eCbN>Kv6?wySEk9!5i((UP1+@BUyxr1Jg@Gvrw#pT+`%y>luOyP*#g?r! z-`DU{KWE$$*Cog&$`M~@(htPz{vWL_KKOjaoTG{O``YIW(>r_Rk8;}V6XRciTA$r#CKs0x<70Qkit2m-1IDLA z@HJF5P4<%a`D(VZ)0j2AP9+;<{%2%Ws_M|2tpXO30hGa%kuCFiQN1~PVgK(!E+OXCg}gb9(=Nym7Sgc+$rNoxt(6QWWI@xC*pXh|o^ zJSPgEK|XR&gy5_*FddNj_x-TjSdiD%d@%NbGJbs}{*r~zr6H)o!sG+wE40p#rSbt$ zfrOpG?>Bu<_Q%WQpRU5(N)AYL2L}#WbXU zG=(0z!6BhDDpHF}-k3?KBTwEVsk#tRJ$@49cueXHiI@PGeaxzLv!4_;gNkY@Qh?jb z0G_itP8;@0OYa2}mtx@y;XqS0F9h|_sAHv|c~2=c3j>lh>TRco3=)iO-{V2^3Z zzpQCR^!!6k;EsuHZsA))i3P`!dhVRzO>Gj}JC-%v7iDy+ zPbEM~UxX*x1##CdNc)cj+1pT3UI~_<{NQ(6f>+GEAf&Zl6H-#CYHp+yr@YMc9uOpu zQAt|f>YGfr6Jn8_iEsh|DOd3VA~x#O5T|XftJNCuKPS0vxi^SkYf28_EcJ)3;rt}e z@QQXv9^IrnP*HZoi38YZLbt9A=j7UG-N);x%}vs=K9x0E%)(RC_`1jD_uL2D^l{8@ zZG}8d7q>e(-6JeQ-gi|N`-=8~dFJe1zd5y2nj4)G6y+o3vXs;SCZIfL*v5I-f4EtA z-T1S(HH?~6tG{!*eFjqB-G}D^P)1Q}xnj4f`wili$~xO~i*4rCl_1RqT2Hio!SU?1 zwnEvn#6fF<@P1Ayt!H#n3J_9dZ<3L$$?foo*kb^w8<*}KP^(J!x#rZ9rkyp3>{}@1 zNzJ>`oqIDSKAN{|-66JpK9Qv3-9?b*t*S~7oYTImO+jE4EF_&YIu96{mT=hBHp0hr z^i_eL=9Sp(>ZY0Swt|#oy>$NoNydGUSE}?z3x~P!Zrh@dSp~MEI;(NiD69cA z>#SyZZ}kt{7PW&PNWj$BUby1vY?{3@v#eCEG0thxTrIM!d7(tMHft?cI(OYkN2H{b zD9nITXV64#)K1w%TjpuhE}eeSnMN0r-fmV*s?d|HZikaDOMxE+#H#pLZXH9ydM^4f1tE@$|{{TB$@5BPU z;bqG1?fyr(r{Pe7&|@6+hdk5v9QOA@(n$R|V!iZ<>VL#~#V0Y^J~?`SHr6v=`9is4 zd()h^Xbhd=pC9p0W*zRQ-+lpF!`SCIWemMWf0UKfdgwqNA;77RoMe2H?Jt7f0$*mp zei|(g3HLw9ZN&_`f%8urUv|HL_(j^AB)Rg7`3=_8gtt!Bb1bPV2{V#{QVe2ThwM9m z-0CASymTPy7VX7;?elR#vGB53&rn8%M$dKMbYF8`@hmjhr!Le|QCTUR^!HvdkvG~OwO&2MFOA*1og|(G-U!~|eav3waVH-fEP^+64e|U{elGObN?Vu! z4*g@*ESN7QNno%=*`-QGNBDJzqH+pb5)<9PS-vHTa{dc`&aG&?;#TU$)p?*$3Q264 zZXosB)={B$^C@d>m5NJ-)-ZKghkLl*M-C6*FPOr^Bn zU#%TS8lO6@Sw7-7-VD6*#cjun>S(Iy(x*j5eHv1homTHDl0clt!Z6 zXuW;*hlu2sSd$h*c8AlutrcI@-)*ftK`GGd&_)UM4PM)Z&T`s?W`Av)AGciFlIrO> zjTZWsHY554C3=(St?k|lG9>(Cb?k+PD&p{qu+nMi2c=hqe)V$v%NvCc#20~6ytAka zr%rbEbL^I{g6MCSv|1`E>nX{vXVRo)3?E5KCB1B+*?!#zfKSa9^6t z?j~;(Ea%5w&pp(R{KlH-e7br0t{?f+8^bZ)P0KAs6-odoQWByBzrH+0 z%B=07zI$WUwO_rRs?21sFp$f13uSXfLP_E`dJ7$Mbu1_$X;3LwguYPD3nI&DcPZ{i zjF##$)6rX(w{N=lK6`Vb4RP*J#S4XzPQP82s#02!(`%zmO!P6op76Hr)v(+&JTs+N zk#Bz5tyz(M+!bTw6m>jD?My#Ddy3^Rc{it7%C1YwvP_NgTBk}9gnElIB`Iu30rHIh z0J^>Ewstg`$ZSj+4==%J9g^%bF!?as2>$>z=TqWO#Sz#zgE#hRk6d!gg{Gd}b-p+J z%JmSnwv`EqOlj6UtJ}`;<1#WmmX4`j*5|#V$}y~c;Plf#q20@xGOW`vF5fR|t@DtC zOr;=Cz!^r5ZJ~X!`oT6z;Q0e2^E-+35byS(*HK0MiRKnIm zT?gtrLQKG}vKJ1iyvvs)IzZR>gh$={rAXQj%uL!VSL>-7oni(lC`eYvMOLLERvc?n zsOu6Dnw1Xb5K&!9M_%#Sn?!65K$uFBu9_GQFp9V+d%dB+m}gW0Nu0-rLkv09PcgM9 z1Fh>Wu-Qbh)j(WisPutJ0TmmH2J-|EM?uy;ajGq+Re}>KCVyBAsRHUHW*M1eJaw02 zdEO&fXtEqysTEfDCPEz8WVT$FR^3S{Q25WN5Iw|1x8jKQn=>}MP*w4AJNSpW&H~1H zzX%Cii)4RWwgL;ZRgHOSDcm38LV<-~uiP|x&GVSC1L!&?bA{<`{{Zy+FRXn@Wfn&2 zmhn1mN?R*BeIk18<6&Q7+qzb+d-58_?iahwb4G2~Rn)xM9X7i1mOo*Nmr}N9+eUz; zSGTS`6qIS?3xfn#sbph>%ob9RLY_>eB_4qIeBhWPBUMr486aKlgQtqL9xu;mGu@a~ z?xKzd^Mb_r2lIfW2b^Yb`l~ZDkok!Eq^_}` z6y4lyWqW}Dlhpkq;>2?#0XH{m8JebuZdj49O+c9MP)k(RPA~J*EmBDe(MizjB5{oV3hN7V^;Uz>erBz& z3zxLc+Wjrv1cT*rg}&i^zw$q}E>62~ebEDn%61Tmm66^ z(q;%c>ERs#I?qrcN!~NhY3^*dX1cl&P?08FQT*eiqE5=@x)oc<*i+OIM?d>^#rfA7 z{RPD$+mc$+wPP7d5(~8JSN>a{<7q?<$v-h817$HnM(8zC+*^}NX(bQK;;6GG#`BID z&1-SX^A6c>_GReeO1(FmpOMy9K;GKG(ESXEA}G^F_#aOYw+P<_>> zI}N1PQ8-7Hx3nIs3jQP?n2`X93#%Be8}U^wA|3KFj;ts1;}dJq@g3B8kmbnpKR@~)5!U)I^yOWq+#%ypAq5|-uqhd zlQ}j`TYIWxEla6w2qXjLOsD&!L*SO6he_hRt@j_4beB0P_~d&j8kmMd#0 zY$s!A2p#4G;@Nj4O%`#-p=@mmdSgyBls28Bf8y^rfqW#bGaon!;I%s3kR~R6LLww> z4yx^9>XhQ!QiY`{APA3}RbsYn626_ZAOSg_2#<1>j+I7ir349RdmkR~+^!`W$h4|9 zr&D1-C#Pa%+8KLwM@Cu&5&JbPj^InFoHzpMc%>3^Ai1!H7q6HDFTxhs%~WN>^i{R) zV%5CNRxlz9;&s?cEV{6Om3qZ^I~Usp&`ShYCW9&s}w z3<}On3EEtyo58)UTGY z%chKz(F1y{wKXf_D0H7C^bu>Q(F0|N?hX+@<&XPg=aUw@nAi3up5aJ`rfYSy-UY(O>0J~N6~WYR_Y`rx}oarGm?PQ zufiatvAvnFOp6$g{ovQ5RWx>UjOf5vG>k$yV6117%G*4}R=mez-5`chpl+P%(qmt?fDWhaIktq`> zaw$PiHw-|jNh^+La;XQ_u2M%`pvXgRn4==ZG$c)SO3-aedZKDhx~c^)B%^(X1m4}V z%Y8CQU0O!jiBI7Q_ccxi)1qlI4rR4ap-DBZHV_Y(uU{GK48C@rrihqq=8*bDRCUTd z%-Sv9s6}9?T!~9bnG@C`rpQF!6=lY7tEsw$9WsA&m%EBwMZF42W_Bo&2D*5_6X%6J zD)o=;%RwIDOhxCC^9%cmH5Vrk6lbvnh=Q9ZES@UyXT_|o#*IlQU971ARS&Jwf5f7H zd;sUXja1BW#^01tPyYaJ%%YOe$wyTtWcQq0@niCW#W_@)t_qVEr8ufmM2R6II`obuq+cgf7Q4oKfm)lQtXM&Z7dCZVmmx&#bNEkKh{-qd05g#Q{^L6l>_0V zQySeDsHKjLNypWMO;Wa}YF}|dDMM-MOyaM7$plW?&|FHe*>U3b)#Lz&zfFkHTCRzH^zCLu zlp$6x)3z5(T-_yyRD2WCUEmvNFD@k9?HW<{xeGgZw#TVSmEP|!+ncsOT2o4cG_;Q> zJ~NbCtuau?tu<7utdx=;f&jGzBur>NF)_b#i>(&5nFV*3NLy-C8`eyy9o=Wz7Qkwl zkX0O0Ik_nWOvJnjHdfN9X^P<)c5!s4uwoePW=VRhQbjeYhMn4%)wuQ8(n$m2bb_I` zl0&tPB&yzHk=Wy+GTg}6*gRZ7AGh}_X!h&&Z|B}oYL55G6_or@k30g`OH_kXb#bxR zYbhy9dUsL!MuUOv&CKI;$76mySB(3Q_q~C~xzOX9Ze<-Qqig%m?EF=iCx*VAAEd>}q|w#dkH`D~UZ8 z;4!PxowEBW##oU)O~z?_tEE*!<+=W6$QR0-k1)w=GCRF$ir=oQeaDtcjLf&0Bzyo7 zo84Ybbp6sellZS<;keC)G9ewbZDW1_1Z&42uO^~YrlO(=p1x==4-I6ZPb9qd?drPf z`!*$P`+E2EkgcR(sv=!FJR#m!9Ul<*r@Ll-$Yefil&(y4qbR(YPjjj#@iA4%e9Fs< z=zkQa_V|-inI$Mr)dc=9<|pT>=6B>)3zwp$Hm$2ffo(fy{t-U$eT$;Zoz*KiqTg9| z;eT+Ji*+~ZtM2xSMk(3yiLBP7rbP6C?&94$LOIs~maccfH@IgQVHg!UE?kQ6$+8U> zO^Q-EHX2ye2iy{$$VG|^0U7gIrb90z-82d3r@m+Wl)7srwusv-49PTh8bo?D&9~ZN z0z~}ceUNybikmd&r&6p7+TYvPlewPlNY#%Ue&E?q6%}L_sTxS?R^qe~6_(`Ib^@+C zt9KmWnLN9|V@PkB>C@Wp+W!EJIDVGjc+P`)HbGCdX}0W@6Du0bgR0NO>F|s{k@r64 z=B$g-)$0Be*n1lgA2{6c=B8IMdx_yMY?c~zW!IW}L*=3@xTP9Us@i~&AeLBeT6|20 z#wyreF@Khltp=VX^jD5i+zd}1ijE_9fC`L#(0$7DOx}v(!fxy0%n9wuqq0!CP3GX( zR)xpWB7mXQC;)s+Z(Mt~c9s-#hWGiO?p{sh{ol&w977Ma7m(_IN~69@_a%|!%c-|m z)@(Ls%F^mvD*a2uneK7f?lbQf)* z2iss8Sm5ZnJWw17_qGDbVMaZ?nQWWKsq0oXyg0mhQuCBdd z7F(sk!u3PRP+IjOK_4i#C5p#gv)UVN zPCnz+3Yi{R%_*GcDFG4^DeJhsRIX#|H})?2zKu!-wD17K&naiu&JFDuJu$jkKBV^1_)>>u2H z>zO{{mOEuVH8qbFU5_(1TzZj|Hp?nDu+Tn0CQ4LE5gis@JFCIBpg*}zyNOLDD3VsnHX`c)$CHl0G+AV_IB( z9TDfT>|V{oKX=YB#Iff~)+%*nR$4`^Nn46SDHIS?dQCyCVhfcq(&9-!kL)W=?Vyg+ zm^F>)1BdM&;JSkMYwqU+_Pd6g#qIHO%WkQ07_K1Nhw5tI3F<`rt)$Cxj**{mJ_SBv zZj_C^O56MO-^)%ZZMCO;%d~4pTB=px-)+3z_Xp*NIis9zwHE$7qpI~OkV5|eN*NAM z^mHg*k^o6=M%T-E)t(B|=N=}#qcobS%OCF~o6 z>FXV!V&6?~Z}d1O4@8s-yQOhQk*1I&NhenpbOTaj&zRgy`CF3t38#rG^am@LvE!CP z7#aZrfCb5yIrFC`dxX#Ke67qkVO4pm^$}vQG)bVcP|TE5Q>6C;^RKFoz5+E^Qvs@JYAy~tH=T1stjX>DkQ?Mj z!Ku|WITiiN>Vs~iGu~uZ)Q-?AfZeJsk<(R^OQy9`HriyT2CG+celX=IQ%>nbMOkuE zn_G@6V`-YF@Daf!1yVy#MP0t+`H{-XmEu-vnI$t1sVA(=rKCXQZ)>1edao& z*;>2>EsCI0nR$n4h`D31&y%9gviyyabttL)amo@yZ>KWwzihYeWyNYRfAeLlE4Z;#tEwsz+mz~}rWA!FD0zg0`t^!eI_>jw)3wFh zO9c^qr|;C<4$bTldxCRc!$3c|L;hj=ujVzsaNK|09d0sIcc%sMc@lG6$=V^ zZ7hW)FDXNG`WZ`3vGBt|X=U^cG;K9LWgBVAt}?g(0M%q3vhd&!fECmBtJuW<0QQCb zrrvQxJ9Dt)7W|b>fNZL?Jiq44p?RQM`h`X}lkMfa6Ib2zuUcTc)q8ht8SHwg)}P)q z{i?5d{{Y|q5zV=s*DXz<%WW-T+euF1Q|boCNzb8)S#og3!5cLIQ88nHxRO&d^e#0~ zY5so1R`QnJBGte183V7x#Cz`ARgJiZga)^tv6_HA8+qKG`W~Nz1pCIRJ9`-^)e;VQ zr*5L1>MCN2mKGGrRK}?wh^q1%Cf?dU2MULZEf0odUyZ(Gy$Bzu+F8=O8x!Z%TlHD9#dv%&G+agJ@&rjoD?$_M+}!+p|n zZzD6vEn8||wNkUSl%v#C%9S(ud}ah#I7_YYfV$vrC&r7`R@mG;Sh(!0UK_Ko;+g&( z;y-xJIa7SZS-9frC|9Ua>V%MyIUUmKVrMtaad%FnaaZF-D~XdCX+~bi*8|{aQ&XMg zy#D}e%&KhsyXfxMO8Ol{pr)v_uSN^4$ zpn))F*ctp|=Dd=Cdni=R#4k)H+>q;HVLojD_{6Mj04V3XS|%%&^6RxZbNXXuKr3d0 z$Kef2Hi=f2^*X3kdF3uor+L=tX^NXn{Q8gVfxYwKrlYf92{w*n<`&kJibW_1?Ey+3 z*$H&cE8%M$7B6#GFC)yVMWpu=C-+BNF$tH#)J0d$+@=P{DV9=tYJa*NHv|MQfD(vm zd8d({pQzE1>(~1PMm}OqQiZEYsz=?qFEp(}#X<^>lhF71h(xYZFtce$TAS_h_Zh5K z<|?U_7SqCDg#$UxA?~e9!j+2Zo(jVKy>n;zvW1!}i^-kXn3xF-7D& zd6UFrc&IAPg3G?%VIxf_t(zG4Ynv|@w^%N({-VugKxv9b!a*|~R6vk;v zFS_pIw*t!L`=cg+`6%A!aDVF1nc{68sea>YuCZL}Ef;DL@c#C^0P2>r--ajJg90>Nf8B z#*2c?d#oF}tQ(ziZ>5#NofM>~z-?JAnKg9nr*nuCOr<4CW-nshvNEA#!tO|MgPS5N@(itW}qr=qjk<;6OzIP)~lcxL#-S1wB)nFlKnJG2Jz@)ual2e3 ze$~5nx8ST9IUkcd=R2H}cJ6G>C=07@>Zcud^A(xyJ|-Z7V3+v>?i4 zpW6}nj#BmjJkvJ)zhQVG)J*3n^6ih2@{7gtlE$2Rnz`o|)ukViq$ye?d<0g*-v zU?6#^Xs_UcR%KPoY!B3}WN1lIB%iEt`3JwM`VKV!Ggguc4)XyF?b`N&4}ngqy~cAL zRipYHZzWzTtw2lkPFFs(ZRssc$d|?~W1HlRdmSr?_?1B&ljCvRd+fD|vU!Gt)>q|K zb#9j)bgjaC%hZ!UoK_{sxyN3r`D=iRHqoO*TASKqY=62i6&jSU2VaW0>2`|FLbfT9Y(Y?pw94*KVWZA_lm2`}atvr-^g#|}aQy5o$xOPX8<9&-~ z?LI#hruL5|V}r?i^D?9GORs!>_wC-{9ip;e=z5O9O1;$;4kWf+07xVx_U{z_#dcx* zW&&nAq;k;vx!j1eu4^J~8r58s7kZjn)~Zn9^%AM>COsu@Ib*7@>FpZ!(MTFzPc2)T z4m&agh~4ms*5)YHPLq#%`Y&zVtkUX`DqYywJ~I-B+nE73amD>9e{jhtE>vYmSUZ#c zAWpjWgMX;BwO5;od~Tv|Q20}h1c0DPkpy_aEqEnNxwHWruVbwSpHFgy;h$#Dp_$D;9y zF#E|u+zlX~Glb{_F!#|v6h#BMwM@|s)4U$r{BiOzjnxsX5KfYxNM+46M>eQSX!Qos z(CG=0$cT*!nLf+C!HyfwD=<8!ps$R1K%$=V(#+9Ca3j-Zr6F1Xsf4fT@rvG5x*9#7 zrE>nkmkgyfQe)gT!CV){9OlDM8aDFgQqD0@pUUB`lVG++gh^AvM z8^8#t3XSm`dZ#PNEcOailDm`0Ly8~3+~&1?5)3sId!0pnzPb>^U*xTNn$JsUwb5GW zm`gNtEg_~*Bmhfk+tcY2<+Pw#W2+DliUZ;#66x_ z_L9-xDN}ilHruG`1soAg+RbpX+#j{vsZ~u`O0_ooD@jU{q?r&wjSAXE$lc8~s>m+w z9}CM};E3vIffFKA?FD<43747#o~yQ`nUv4OLqt%onIcGB?I>3HHhvNFf*?w2n+v_G z)7(pE=ijUcEP5hgoYf)TYtSkL0%xQP?J2fQ3JL3kFTzszLvm<}jxd#$o0^cMy4uIA zpDxk_mXoB6B2BV4W5q#E#P_w^m}o+}-)y|ogL};58)vgWbT`)R*-ZjmPqv(gChWzGhZ>} zv2IJnt}%B8DThvn%{9t&g$Wr;$MK2cRb!$POLwTFt8Sv5!rP6g6s#R)4B>H-4N_hM z#R{05S;-9K>>|-{T3Wf&r^*Tc0E#+HPq^@l1*On}Q`b2QBp> z7R8l>H|=sg?9nexp$W<}l5gI)SR0;@qwFk^t|Q#3}&F@hq#2GOM01 zlTkZk$t(|SsA^?8gEY9>wA_UaQlZu}r?K$9heEe_E+M*$b9HG_zlD9qKDwKBFPK$& z6$>r1Y`yE}Q6ULKzY=;Fp2lTKaA8^3E8=0SGe#-^sS3>7%c`;KTCmPl#Tv@6tMU{5 zLle8xhY{(l0v&BH38cu(5h%N#ZOz*8RghcBJWdaR#1NGK0BHv;)YJ^>J#{#u=Rjs0 zlv+4QA4gEJzWT`j0QUu5(t?YwUMapZni@vWl^_kL>Cz!ZJzZ73PmomsRcuf3Zk6_nC zhhEV&nYSRZNk;nr08pJ0cB^!{2z*4C`iD5sDq;6`&a_tBRn4^3g(*QrC~Zby>U5uY zfzR@rdk9OO;3%)$L$~*tDT7$TYw%p$W_S)~%vrTv@}hy&)3~(?WGI<|xawoce6zn6 z43zr8qW3os`(DX90aj3y>0;j3as)KrtxM@`mhBHgFN9**-?_^M9jrB51~2VW=m2CC zOZvZUw*gF0Q818Zt)^x|Dh~UVjm{fNvA^w;YkDZh$zI(q^la2sHqqOuBW$`x$J^hy zOL-mxkL_3&bAH>VY);xLtN#Edps2^|AEm2r(^G09em@xZM;PRhUcw&JTCdJNTf}C^ z+&VXkP^2fWD_%->YcVLbhi`JDTsLD=jbx>5wvbh!?qbnr9U8ODrO9*FtF6Jd+$ci8 zm+Xcmk*z&b*z0FFB1dm%OG!a+6QuecrXl3I1=Lg0Shz5iB>1wim8FGm1Oh&>px}c` z0`oUKYlNjtD@A0P0WMBJO#Gt8<%~>D;;<((*t~}XSz~pzJy4C^ zdub5Ungy!h!6YWvIN054Wz?U-DR=Mnt?vZW?INp6R-j2ePwar=)M%#&)n}5aNw&C0 zVW-Rq!^O#$Mdq9i63nTs>@t-;kmEBBCSya^EST%2fkovm=iy{@rNC&XrJI!~Tcd0T z(sUh9L-@q}bn{+%zs0{)4*AN-b(c~qHlZD-1u{?#6;@t1G>*_H>5%8CefLt^n~bJR z%%}bF$u9v`oJS0Ih)reE`*EykNH23H_X{z7+wo? zNn5t!xux_IHpc1x5Y@gx%~qR?OmYxdt&@&@>{}N|z+Kq^X$jtvxe#SNXDBD@BRy2b zqpsmPYee^}-nEEPg}Zb?64&{IY5lM)h7P4!ZSq&I6rzf}v+s7>a;K_h*H)^h)D#la zWTHA?VkN}1+F6f#pm!0gk zq`A7{gy}6-C;Q+TF91DJFyiN!`dXuASMGhi&LY}esb4@+H2h!*b96cdXUA|^o@)IR zZOOUQHM_0A%chMBCQ6wmUyMdvW*&Mi2OYvKBh}iJ)z;)!`=a;O++2A?%}FXU)&(<2 z{Yh46F;>wJOG0okJ{!oh+JLrMaMP&&0K}0bkA&e7TA2@;r*gjI+2UD3MO3e&_Fr7x ztrc%msb^%#k?Saziv;p2Lcp#*Y&`xk=gegwQaZh%^)mXKJeln`QoOboi6_0~B1(^A_{N{{ZQeJ{2y} z2-2-j3ux~S1ct1^Itj;D#4OTeb&dqVLQ&JKELJbTAN!$6 z;kIHloG-{0A;|v#oKxkkP?@U)Z>pLNuJUN#C%}O5JG~_P$|QS+TN(6O2{OTC-_CK1L#@Y&a2XfrpTty z6tOaq3Vfi@B}U3Hm3N-Qm!gHQ@!!i{dCo`vt;wnP^-@=j-BOZ#h)^aW zb7#`7UAZ6X=(*afLQtoQjHXm|`Na&%D!oql32hQ#O{vnL(`Ccr0X=)dUwp>uQ!!&4 zPJu34;@qHO)meq&l{R}7I8UPE>q~CDRnuY)rYY>@&7SAlj8=-o>=24A?KlAIsyAD#L7=EWx)@%4S~oz(=c@TQGD19R4Dec+T3 z2Z1qT#qpcVr&1=8Jp2|p++O=}zBL&)w+(B*$_-q9{87{N(Ow4veUtILafUMQbDW6M zZPbZFwV36tVwr@AZYX-G{bktflxkS6+cqJ-<>H<}cq^QJ_1|P(ZSZ#IT?3u!cjz4l z>8g;KPrd&Dw|BA|Rc0N*xYaIMl+gt>`U?vUx%}jJe&X+WjZWy5laS&uqiiE{hmxdW zKHqzV&ZYW{tgenHc)XsU0saA154az@zqzh!V!t28?ljy-iB$!+`Wrb}uW_|$Co0z> z2_1F;^NKc4xHq>|X!JMX)q5AWUuWEF1RErcGs-+&(!Yka{ePiV-Nx~Bz1P&-FBcc? zw;Hu>sIIAb$KG(0Jyz*aidl1GRsR+4ls&sHiVIuj$-tQj{zFu*Quiyq>}`KOp!!sl?V$oOA1RqiWqxR)7*x2uR_LZ?r2zjm56w_k|+ZlwJ~sw!#zM6PK`LQ|?qrKeZ~pYW}E zd?RP#y{HrtT_4cA(at;Y>4TxL2A}3sP0L>4E-lJgRLKo^|u%=c3Jv$(kYR2RKu`L=aObj~U*_Zx;WZ>(m5>6D_Lq040* zbzl4nXQ~pC({4F2u?0Y#CDcJyvM|44BXRIeT*D(_-Wqld38sNdpSJ%1x2_lWQ|?-? zhdI|Ws>Ez{mMZG3O*B=E(A8Wo47k`!33=qGO)Zr;5C@u4>|(Rd^7nZAVE9|O*IgC= z0JWTF9hbRucTd^_YBi?6v-hawn7@ngueh#W;>vsaGK)_Vq^d`#lJDx)*z|!w^AwOt zpG_h7TpuuL9aVpk**UiU<4%D+*yRrw`#-=gbxu&zCpx(_Kd0X-QTn5t>7K7K~RF3;ZyKS@k_~DdQ5S3mjvPvYnqBT`Tnbm_nvzu_P zH1`P!9~JjIdrShoI)87}tc%>nV`H@Y3dQk82zI`wm~!iVJvvlEl&|YmYWzICV_|F$ z{;2mJg`@X>#bcRk%XeYg)*W{r*iCx>&vg$gODcTOm<5sl!e{a|D=q@{+`QL^; zl5o+zUX;zA*J@}|Vc2ybNU6BhgnufUnX#!Pk21OesfE=T#(+zxy0JCZJ(vC3Bi{XyX4SjH}gR=O)K4(X+a5_H~1t8!CO_(rza;&SjTcsBN3(=xq*DvghSKCs+`=szxYBK$!PyoTyr=A6eV3(tJK~%7klU z)ioJ@WZNnUz#AaRTRRukuJ49Xcs{iR10tD3J55e=sB&eImgGKm|^_xZdg@ z*Sw(=jxB_%&Sn6dIV{_HE=6ie%N@7V-QEvWxw$7*8U0`>lowh7S6NGEqE6&4 zK*;Kl4_GzP7dR#*mt#tNEwmJPwV@~y3qmHdpwtP($Y5ZqtV<)LatDT1UUFQKnp!uO zY4-x@9Rg?Yi7ZS~Tu2%eZf@c$Xm>eTZyw<}P3f9zeU94gQ|<($B`H#tW+go%)4YEN z3~&cXXg({(+~b$cJ;9CVN`e^~r$Z-vJ-P)+z z9wnbF_X`aaUF1#}zu+}b+pe_AVz3|hYC#G*$jBHvOL$)0?G*|=+4-Y!jruqtwAKpj zI}EP5ew}qS?;=!Gh)FKG`_gTu&;-d+l%N0|;a1OVZ7w4~6(c!z`-z&y>gO5{6=oS< z+7XTd_Hg&OjN>?|r{t=O3pY%s5L0Lr?N`?!D@`+NPSs5%VJ35#jF*k~5Bi2!+Qi)n zI<1GhJ&cnJ$D5G2knW(PbfB+?{;IgRr|q|#a|=ymEVSgS5>^rEl_+o4b1|*o@vBHi z`Q!~Hc^==qxf_X1wmJt(w@QwhBwsXpJMWP8!L zJ~QIIyY}nc_;N(rPSYL3dbFti;ueGir8A6=s`hwo77VC_?LR^AhV?~<3L_SjgE@I+ zDQN?gsF9r`Xev*~S-^80rAolAyX|j{+{{XxBh^H=V z&tEiUWZ?h5OL%6TE-a_ZGIg9g?kK5EQXa9?>$Oq?LLp7~p-)6W z`+`Kzwvor`P^4&zUS5ac4W*)&T$*(297`Y|i*Ztz*Tz0vUo`0kW2E$iu%WiqDQWK& zETjgfKgJ7dk3=QJApowtR!VM#B_?}!04EL?5?9TDZAn`Dq6;bxC(YqMgfs4tl-6jG zHJ2s~{IUS)tTR;eVR7iS)7)uy0k{M4hlGt^7#@`L)fB5jNR;KM_{R>XM9ZTzM(k9y zHhF;fL!+WVP7Q*&46OtXk~4C zR3s&~TPhu8En$1LK)g8vZj!-qR`gRLVEjLH7DZA)qWY&4vO3XFrB1(;0SPcP6oVPOY^@RIDI3815e8wnIyU}i4fYJlHVm2vsO}bdR<5!AX{ei{O*IU#U zf`WX&8IVVCd&EWNJ9AAGj28u^Sw9AIWj*DaUP!_ctDUr=SBp~QuQC;`hq3J$A3EV< zBV_2h5*C%jxl62W8t(;cxSu$yJG8YztD`oHVwwTUV5Br9APA6@erNK|+g;WL?(n)L9sf#kbBi{8c&C+b$M6gVwvPDyVDeR#R5Pi6Ee)ku5;$AEy{Q=&vFY z#%j{MIg@0N$YYRE!5mQ3ielL+5>)=Oi*>u`6M~5(BhRbxQX~jn)yyCuDLF7Uew_nmRRi?05d?$zZz1{ zfhsK7u5&!C=!BCHoL0H`DYJv9l4zfZcb;6UK-lEmVIC(rp$tZzB{7oZz3a7v)1D02ck_} zY*_Wtw+Sdt?!#>UFhiSe=IXh=<%X0hKjvI%Y*O5QmGF&xpb zM+Cv#vC6tPTxq9flo?Kax5s*!6eVT;X#yNZCEDjJyfaBtSvwMC^Oaj%*pH06kLFOb?`;Yt265#RX6OFp>CyP zQ>+%(P%apX3Z*7+$aPc3C4pESCGn1uxv^@PoaY}TZPj8-!lQ{{t0HAC^Nz2!pz549 z>aK<@MKcFU$~p~F_MqjEW_ozTMJyGMqM)w>3fh(K1-Q1d0Ni9dLQ-0_C8dQ-9b@FW zf{iKbu1u*YW~oR@bOL5lA2@N6xF#fpglZF&V!ANfX-!+;DC(sX-X@`)o+_<-WJ|pv zEvaj%>LgfnIWDpw!STw{q3&#)(W(-%zP*olBXOdR4WjwJhvyuPbghz^jy&qR(N*z; zxwHu^6f4E1b3d{;=T%BYA`bZLF3>cMDd?T5C3!O4q|6B9V*=IH2>QpyR6{x~)aw3Z zr_u+I=%0(K)peq6>Sc|jl9Ccc=RUKTfQxr%RDqXjxbJHAo;>@Fdvv>dRpp$GIFB`P zsBPXgCxk&g&60w z4pH{G%(<=&&Dh#(`Hg|5zoFE$ztm7%Xle?IYiKHVq&mx+bTSGZ6cTieM~~e@cMrK4 ztyL?S%OpfbbWaU)L?v1i;S;n~20uj~d6MAelMlRCBuT>2ZL)vF@05P{MAB_)n8v~G zN&h~jZ z98%bED@uZrrrd1kQgs9nL65$=8Q56iq4P_V33CI3+thh3jyPkHuz$N(3)+3Y^0oep z9`~`tbT1!n+^+i2%5uANOaiN2#bR5OqJR`N=p0ULg8@E@h(7Z5^9d3W|C8p%h_Qkf!^{x~ujgzx< zMxiDB$9p)o`<=B?yJV_oALP4YcGz+UK~+6NMM8AdTLj7v@T;m0x`~1v{{R^mUnYf5 z@s4;grjk^*$tE~I?eWJx(1oj(Jgn0P9~=c<9as>)|yI&{LMc90H_jQT2wbD zazay~l*Hq<97-!MT%ukEcn^(9NuE^BZ(N&`Tyksc zsd}onM4>4NLrsI_LJ~RzjRbviDr%za+wHJ5Fh6a6XloD69mT8a(@eUD1Jp%FXjhm_ z3HOJ%CQE2BJM=2A+*cEOwqC|628DJ7!rZfOxZ3C(RcaQksoW<$Wh2wPUuS8hLi464 zD$$`+j}gzP%3G7Wl9_4diEa47qN}9LJ{8Fx; zlI_fE`p})HO?nAH{{Tu#KmEkUj;njT$!%de*P!_Mygo)8b}*2PXEcqzD7XH;*xM=s z|cFf4(E{8w`4q0=(j{rJv;$NPDlk ztU#q(FRc=t44eo1VvU{d79$wa>7;1=D^%m%yUXQV8w5^q{1XkH`;)TWC?2P`+{I(d z1SKguy6M_Hd)+U+yap+*WwX7<{54+T?GM=Yb0X%+a@~wRY71m|+q{c)RYiM6)>Nsd zcR&wmN9G4~e(Q4inH+Z(u=rJet#-%lvkS)U+g67-c&Bd&;Tts#GB(>#wGlGCqJOqL znVasd?2jb8tycd4BWZ3nv7k{dD)1T#P+LVRKpz3&6}Y*j%gsxQ9t^Z{bpax7*lj8T zwz3A^(LH0!C-YJ)h8YJ$$Y*#Z6?M9#DwQhGrd@kRt;6}u@k~8Roys`uGr~b03W&Yp zakDu6WA_SLe@!b_U_43uBlb(}hqxyC-LbjVA$~jk)ppsIb2?VGfU5bmrd3{Tb`TY> z#y#nclBIrb=3}ObcDuWDjFe1*288sBIlB!X1w;OtUj0IEF4+)y#ic*!V!1u;=NcaJdf#N)O^0ywc% zO_Y<0 zqJqodp}K7x6YFK>8=K8FxehS02I=&iwCNMEWF6wNJO0|jTy#t7Ov3qhPQ_FNrC*o> zuDU@n-;%4Z;c-FWl@v6M2`-`CVr@{tsqL^!1IxKE4 zB+N_a2&!64dgu=sfHly^%jB87S2l+=AQ936e^g^CMu|H8@uY%1Nq{A1(yAc9Z^=? zX6kR1riqEa`=EL6SvIy;Qb9hGiqLDOq63pP;1!P7fWP?kOm+K~b4&V`7g}iBT=Ycd z0s6#5m(st*S}gdDq&ksQf?Jg}%bjIH>J>dyr4tFMJyhdkWJA&@jT@p$5M-W%_rZaH zpxw{(X8!8`UA* z5%nabnVCeR2mmO{gr%iYWGD248tnpyo}i=F5DwElA@HLnDQ9YuTKGyPZAYb7FNrQ$ zyrKH0KspsC_rhnTQsMauu?_-BHu_X_+L0xA#rqBNg^uz+=M?kuGRkb#0bi9Zk_XU; zJ2CvS6b=jiqx7nR&%D_cdb$n&0KAyR?$75hQDQWNCC?eMT&iI~GZOu}M3z0|o{G7e zmX%f+gQY09HWb{5PeJmD_9`=|j;_c7tvsW`ePiyFTn+RUa$d(6o)=HI%8gNwfZi{6o7;5FT`o^n zw@5bAw6&6iBy<5iG>N-BbP<}hpcPx2BQx}KsuEe$BoX7<2|6a4K|qv(l1%jP-Z=0| zeXgKd96Se zfrZa-0CtJ_|tY zXV{KsDLA~&%H~&6(^BL5KgNoNx$EBl0J%?bmkNjZ7m+H3ndL0CiCf%V4Xvao&u^sC zroa2d#!Zss_qT?!KTjc`U!uGdu$*U(Mg^=5-~ML`yZi^}AEiZCDiW@Q?(P5*oI<(v zTH9a&VvX~e8Z-$*wjSN)jPaU zf^!RD9d{U@hlg!WhH!iz=)9k4n5ATem$q!)ep#K2jaKx8C8Z+@mc2(!vkbFPI za(T!vwBAv6SOAvaa0PsnL}r(V#c1|yB|IM1>I=EGNZOMmAyXZO=#|6kqKo7}wTj6z zS4u3anhK7|s1Ah7_Qk#^v{V4%T%`xvbBC;4^X^uUi*iRCrL*L1mDjk=(y-ku>QocF z5w6*4A~!vJ>uXm$bqdSJF)hEyG6#I8s_yfQICG15BNs)J46)N=wyHFRRn{wDb<&jT zx1Cgp`k4sAZt^!bl50{*^{zh&VT*@A015(ot^2F+=Ln|hiSetA@|Pm87SU>v)SF!k z!0nwyLP-iKJyP7y#6{zc_TJ*;ht274?O6{t@1?DvF~V>Ro07bnWRme-fsCF}h;#R@N;IiGX}pl(`2izRAd4aPmi&f`oaO zG}H!_OJh&ysZQbQPHQAjS^VOC&3n4&pdpciqHS@GE9VYuqss5RbH=yMM5MzQ6`74y zs*!c7p`~+i4>*LVElKj75EOn8JoivU;Mce`yQO?em$SFHosM*DFAeKY0q6e!rAoOE zv9~MnZN}y9AK4!}QqWv*j&Y%LA+*}=NNp6ZRa+^Vx7=D&j>#!eWHRG=?g~PP>85?$ z`x~g872Flv!%aV5hxXBH-IdDYAPd85q;8t{pYAca2CDSVirtE$*J! z$)jFCI*lv(^yyz7zA3o2eQ0oil#|D$<`Fnw5A;DrJVp$)&VmE zuShM9su$c{0R6Nd{SUB@xr3~~s9dP-*BLbmms=b)V8y_B15)I!-|#IQBWaPeiibol`5%=9TM8cEoDj~qU7+}grPkqI+GHy&lDe+?*&ZB zT3uhi*^ZzU137ar$}?K^-}5S$ThnzdwMv>2l}!F#=fs_2rI*ed*Y(j`xc>kOj~33J zqt!9?40j;p433i?$0!wBSyZp(wvZCjDd<+}&y@%rRHbMkiwJj>A(F^@K}dGF8x8i= zm*dpDCv;~x=L~3?jE9(+8?81fmm5buZarXF{nXK9oYMS0 z5*~j-ysx)!+t-+V>d|66h0gSKxYq<)u2mO?4Pbhfs{L}Tbx+hiM9RP^ZEI1R$EYV} zn!@?taxJW5_Tf9Gr|M4jcd-}Q$-VoaS!f-gIu~nN{vW^ap*?+IJavU}MkQ;*uxx&p z7{@G)rMfF^GihxJN<^ljHBygt6Ehx4zrUXDQsK1Q>% z?VFUb`(4Gl%z}oAnySldPNclWKdurSP)V2s=p#<;z8Y5Cd#)617oGd<%q^$2`goii z?;0*v{e*GVlbk-}oFRv2bPe6{Wm8?R7n@t{g)vy0Z8&wdoC?sVdeTVmDABoQLH4$f ze0@%pUU}`e8iOU7jyc6x+Pa=}p#JsQSGezUJVEUni`>d``t~jC(==GbaT;$;6*mfm zHhns3W|J*6)PW!)3d7*;aud|no+Phr@@`pUgtXYoSD*oL@9sO?ha~%=ltXM!yUZT0|p^FEdSXbCJ0BraypClQrZt(-#>- zqGQSu4^Gp7+;vlIiiwy_Wku4VZ@gyI$?MiWozw~VTSSM=a&bo2^!h?KYcdG%h|)p4LUi%nDVzP%DjRW&&+*u14lhR=}z=m&%~uod>; z(eYce%+AxtxYb?Kxzsl6X+DBU)kF;?IBMo##-eO3O!9P98Yu(Xvb^we{{V0kTcrA7L!gwzSuCR=wJM{Rx!PzE&SP?Rv0{~<%Q6)%vW`r3 zLE1(()ThteeI2^ncV)`iDKAK@bs}2#jT3~3TPQABuW}@m!A6R!{{Z%}&paE)HEh*n zRP4C>B|CIMAPqI2z(&=S#~a9~$pHN;&e+aD7CwE_LQm*c{X_RC_Q`9;R$Z@UwR&<4 zkbtA7Kt2(bKeY3E+l;OKYiw_R?2WNtfz_vyr#^4xtY_T6FH~J{nh@SUOa|qaz~3W1 zB`DY;479Qkhi)Nx50Oaw?33;`mV@A(Uuc{xu6OsD!Q6ZOIW_op1z4=Fk=r1a(Mf1d z`bO3V{^Y9kzz3)XC>_YpeFhc#O&;+<{r>>l;iQ|bT(JFMIF_`_n*a)YR@&5qAC_fX`Yd`q| z&OYYGEMy;50CCcE07JaIM`X`{AQ8hRwMgr!O)8$^VNoNGMqj$UP3z88Lq zuNC$mx)`1#E26%KuZwc^FPmAFuSRPEXd6*Cq5rK9oqFJfl{C2F-E z)ZGmCIetpbrI3CUs-r5ZI&~R=s-{MQF*0huNP!^giJYThBoMPM-65ZKJ3_Wo(bLQV zxli*M^_(zKv4o|eq^DBQeNCWCPQShZm{(OR0M%NDu-;^~aRyUMa;#8D3P(v#zOm;X z?qVkrHsUc-%~!d;+p;#-Ht(^N)7;Q5ojBXye;8!+bhPeNxa<2;HzgAjmr01EVLQ)d zPi|1{!{D?oTlT-0wkAmS>^(J4h2z}M9NFx3H>!%K8D*Br&gA$B^ov!;bn)Un&8a_v z%`7`7n4aiF0PE(t0sE!~m)$Ni>%Ep6TWhl}6Q5=_1KJCJR z-4cG`?Bl>%!-@nKv~G1Z6)FKx-ij!6nvU6PF(}nZpmh;4&XM0~K7Ee+z2xepp$!qlcJmH|CA>|iM^ z9HpVXWhj$WxJCY%>AdQSJV`KtXlRIi+h~=PwtDu$1BxmDYw?1kk7|!}fRt#>6UAUi zfu^zZiC;bSP9=_w#TL@$qJ0nHtbEoQrd=qacxdfuT{>owDFd_~@NsQIWXb|2ug8$= z<|i>JLOs0`B3)7{>%|oJ#@s>bM_(Djn;lc`m;{Zd_T2j4-%N$5sOVBpUzB(0YK1o_ zZC?dk?PQfxI#5Kyr9YfBA=|3wy}Lv*--SCskkXJ*^zefj4G`~<7}Z)!P3_i0_Ckwl zR|AuMb)xjjrb>h$c`nqZ>f=V8%9xzt5&=X+ZYdF8Yi&V55+~5WsF%oSlNPI`-;b}! z$So^c=~hykr5K!+*L^&EA{P4Iy3t5m+-X9rbBQvne=l~;cmAMVwLVe$mIBBqS8zZ* zqt9K~;<8=Yz3{iz(6)Zu@&;SX*2;7$-0&+F0BuyYwC)F5rbsGUR0i4`r09jyD{3`-nV>`%d^bT zU(LBVsr3`QmmsJdj0RYgSJ8H*V^C!ValI zFcyW^INbsdoLhi+ZeSNk6 z08fm3xLU9^3+O)>?NTVT8ikz-`bTP4D6lUt#K+BCA#3$l;|X67yxy-E`F%z2s;?ON zd_woubH+Yg+83Upqw1wmdLB@Zm&@;2QNz)Eyi>}QbU7z#)L&eDyR~`iueOo$=GE}1 zaCACH%d}Yvr@zEy;R&kl(40&)_Z0M$;gR^nraE0{p)(P;Vt97%Z6p5JouMI7oEOMO zn)DF1x`_lWn-YJ9I7;I4Vth<-bS%~;3GN_wl8le4E_o!(OT08)s(qrJ5mSsRN!zZl z+?7&Lu^1bA(@KZcAQju-A~Da^6C0_bET?O0!edf;c*5Rljfu2JK92P0G#wx`N*~Ak zmzMA!C*pj?fvaWz01|)HT^iJ+v0X~PE7Z?z>X#Fzc^Mf@K$(1Gcb4WU289uCDUINT z)Xn!V;(xd=w=72{u-CGj!j_qD;WwvCf2*}z0!Qj?l|*@iYgi~yCR37zqAg} zk%vOC3#i`&hBpcjqJO#cIYq}#$r@DEQ?^&!4cou68p)E@6=&n6rcvgrnJH3;N>Ws# z&;c_Qun9UV$sl!7quK|!Zf*9V&UjZd@X~6ReaC8>H!B9$g$?F`O4|JmT2cyB)2LBQ z)DF^T+9UHEbL-$SxDn)O(E0xW!7A516{MTXg*-)iuBtuSduPEp6M`_WxM#JSN9Z_v zl3HzC&&I7C?qInZ+z5DVNZg879ahx?{{RVJ?3s#JY&(-HFWrj0(pDNYpA;{2J9iA0 zqa|moZWVPrK0o5B+hI~pT(;kRnDi&>X|Um=!TQ|Qa+!yDe_2MYF5K-0F8m65yBJc+~OWz%(#~_v1K^M zNmpU5wA8fyEzX(MBBGVnl{C7_r=pYi5upS^E+LM1%yNy*C|M(O9qvSSylMztZ?Sg6 zKJZ*B-bZO_hZFmP@g=DWRT*)rdTvUvQ#COnWh#XK00}ywW=3dMxHK6X&LNZ7>blZH z{{YIgTdY;LEyouMHtI3c%~Ivc*IQ|>xY@53=dE{}JML25ZB-~!H8izMETPt(ZBi*( z6eK~9RbuimUBuZVHE9(K$vG@+wsSf;0R$9W0*umru`N7dM~MP7wp^=h)vQ-r-7Hko z5T>06P(&r3gHW5E5g?&cCkOXWbF$kVMJ27T^9evl>Kr?p249^h@rxy0MT$rMlrVNazxg&;U9CA5A-+YEd@OGBH3BNz|Fhc}HOk zCBM?DHpyLGX1P*ctd&ZkxX>sro}mgLf}M4jXr{#D8-tj23uwt(MR5s+VY!a#6})?K z`uD$J_8 zvgBl@T2M;7HB^LlTj;-qKWoNv57F8bpRW6h@=eKVO^Y8>VPBQ%m-Y$E!4rUF%>mFU z`wjs+{{Wc!CZiRDV*gCIuQ$cX& z&v2JDpXwZ&Sk(H8aF&E;8LmNk0o8BZPq$c>kyh0UwDM;rSG?EtEau}(kpAEw`?fkH%Hy&MXHv>*9ZjaTIB0}9#fjuMhSGk$H+lRZ{k9$o?^*U;~T7=3YP}RN3!MG)UNUDFxuh;>8cuzC z^@63pd1@Bl6xQmKEZjFJlC7!+W8(}#EqboYWbXyCrsUC2onn47oE&BpO~qv3R;wmn z?T+A;w)WP-fhJ~9)vis@y#bx$jk}9cs+G8(GSan9OJ=m7;l_KgQ0$r-klUU<&- z+2!VqDNTCkOI1A*)T@dR2$Z)7D*KW~8=*jAyJInU^rT)9%I@U%XVXknrdvPpPM=+3 znRS^myG5_raWiVb922aC)K^y}<*cYo5jbgwaj8}a+8W9yp2D>@!c(q*b%&ZLqi3nA z8*pt;vZM*iO=0NCs*iL>X-F6GDretQ&oUOd&vXWzd$Z1=e z5~P(60zOcOxj763J5`0g_{bA3ZaVjdq&lM868h(PY$ZzH^44)4=|EC(+tnf-zoe~1 zIU;*cq$Es;Hi@vyGIv|40hMpvrtZ)b_jE_w+B6MP8SCY9%_(6OBBuOFFu`W$DBfqA{-0;Ly-KkxV}+>csM)D%kOj5@pNDvqg5PTL z)>oL%F*Q0Tmc^PJK`Eqh^tJkJH+>?>Z+-2ouQSAj{agh}L$BW88D^De{b*1~{Gf@? z@q=W~3MkB1Xry$XDu;RJEt{=V&WfKYoQX3MoZ_`}kq;};oI|%>!$2WI-bKsby3`tW zo-1#6om26O_4LHAO>t}kAdaMJrJn(){{TRw2UP@lyk!$OnwF|BDsU5(BCcQjPk4^0 zLZ$PROv2hKe4Rq8&TS<*Yso*FerTg-kLFQtTrt1tlRPCpvxq{i#bqA?lD7bSG>$bi zO3gDpVM!%zN{q&_)PfPEBdP>JEPYBIRs^Y}c*;`NC1NTXc+u@Eq=bbSQy!u&8KPWK z?`@gNelay>?Gr;GsuP5h>kF-ABPz-G4Fua5S~Z${qv0Ap9o~SkPDl^D zQ>)2=NU+pSWE365e4+fA{Ia63kso|2mC7ym3${aA)eSQ0lw3QKgpx;S!t)sz;(x0} z>3l;R=mivt;odLGs{J=xv%aJWOzcY!jy9BC4A$m$Gyt+TjR=(A`x7912TE~~_JOZWW zB0@8}SjR=_%#`4b5n_olnu&=>g04#;3rdLS#sud(Xb9D!(^u~>ue~L9eIodnBf*(29-`cz# z-LJpw4l8sR#v7UXSEB*_=k_aRvhcsKpSSg&{K1-DEmq8uk-4{7baA@tw|KyGNS!Z)-pC?PF{kE?z?-ndPY^v{udnK&W>Sy@y3efT0yVW)Hi|8!wXbSO`ooc?OM>+biOcg2(Crfs zkqb*;XavcEfI_+D4P~z2eBt)B1uL~j6wJu$tUST0kDdA@wYt+)RFtVG^qNkpby_43 znTZJDE>)|W#x(hq?QzvLx{E2cwM^lut5T$08YFm#uwx!L0e2t{JwJEEAX!r*Xk(1rcqTI3e z`jq#3Jbb^M)wI7i^0k-y-QsJGJhPEfy*4+qn`3TIm`KSyrsd5m!+< zKW$%e*8c!%9^%{Pi>^HOS3{25Ym%!KJE&#HmiuL-kf#9xMD>8LFk=1#6=`1-Wv3=M zTjpF-bt|HoKIOjZKH~k&TrRX#nF9F#0FLU4#TEl-c}<~I=kkiGfD*TrF0+z;5Jne; z+S!UURo-epllLO~D&xJytnv!KE?kDD+AK2RXs%nfu5;7&Ma_}AfGpByvcD98tjJeZ z;?`<|j-dYlH&A7yG9~cKG^V_eqSGQ z`NyUP$QQ! zD@P-8zbWRJ&6(_P63;Rl&D$Qv9#TD88VjX!wP>esuy#!$$q<>2!eTAZmk62;qzZne zMSN{^uOLu4#ne9&S|R_dum7o30hOHai9;$tKt2;7c=yj z=(u0098lhd^-_E!W;#SI{6xuGE@SV5Ex@mWocuXX0~acAp(KQPdx_}?v(ToemvAWm z0F;}zsk~CI=TID?#?w~iJ*){O+O;hq3t8xPfuvS>PD#dZY%BK(#*<9mLB~k^s7%TDK(;o+!%|Qgo1$bj&;*dlc??%F z-xqLYnzGZ5TwYBbW!a{bH#6QUK5h#o~f{seD}Gy*qi5vc+3?F|7)qed&G9G0rTqUTpXcLMrbhDW5x!bF!Jsd$UKKXJ0ly57rA3aUclZbsxTTH_CS{L7wKKV5Fh*tS_MwePq~ zb7|u9jkXmzk+@6_k*8*TsLNQu0B@4TxJ*|QHSoPvst2 z_aBwXnwEI-=2GGz_QZ&?oDVaok@*1lpyaBDzL{!&QlROGerwVdzJ7c$_b!K-QCG zBeu5Wby3+1%DGFsY8c1uOLfU5%2K*QTU>_IIezFH2+#_Hlp03?-K&*Cj1g~wl!}hAuvr>30=$Ktloz^zUys>ih$^i7cg!B-vT7u= zU(5GnM}<@pQEWCil^Uv{^3tK`>N&KrKijI#s^g9ErV30TbuMPYui@xJ9+8~(og#$G!8ik zu*Whnp`}maRukL^4BtzVR#0hjE9o60AN zv;{)k_9qv5;OSTjYi?Oh`yx<&=T@qhRu1W%Ow8vLoZd!Tr&Yyn{4<7G;o%H{p{YGk z-N5P>xTtgl>F|oljcKvCkP0QKV(0mg48*)(X$2IFW~ex7_1KIPq)+b`R9B+?Yem7=8>25aDmx9kg!MlNr1HF5t}Wcr;y))4C@mE%;7G@&IRu-OE7NRE+biwk1IP{#hHm>y%4YpNu5e-+E` z-SdGsLkQ+vV)2VxXlyr3j)BK-cDhEIY?qsRXmLnHq*M=CYZ`ACyl=Ev-2hOW-bt?^ zZ1a*_eAP8qP-FDXDHP$frC*m(IG?J==5S4o%`_X8wP*vjqx&H;HAF~jqDED{L^Smh zM~pp08@i%bs_3M-EkCk2n{Y)gR?6zS{Jrkdl!~z)XG0psdNHo@B%J25gnBZ zjgh#c$WjlOthE+fTArx3t%MYvt|_%7Y2IXaoDp+m;w#k&YjoYzIx5Y~vYJ#r;yplA z6lXa_YH~rMwnG>#8m2bmlG2rtJ==*NC>hT+M>U`%>zA5WKQNfqT_IIS=vI+(q*(xH zPv$yD!i(+5Qx4OW1#T0Vg#?{au7T##N{WI`l8>mqbF@pEd!hv_n|4HKeh}D!0lhZS*xSp}nJOh!WIJ zal+A6^k%85xz;eGtuqombcHn36!fC0w7a;U?urMP~e0$(W`l8*uMyk)$^wWDj> z(1q%(9$Hq4lUBJ#KXtyQ!f!7%==jTgPPJt<1d|2 z&=S1G-`>o}a~rSi0I1Y}sjB*is6A$$WTDoHLye!|P|(liAExohBxrL@L0%DxV%L_D zLK6{^Q7!c?^&)NcR+<@W0o<~^RgWJrX-wuxXt&m|=HviR=IJdoiuCsED&J>t@M_f_ zubg>OUQ^aItkx^?`g1DRU*U>>BM@6gUha!h;ZogQ$+49rCNns)WC5! z2`XP$rdeJ^II9gp(lU6-?ad_=DYY|6Bd5j;Aw*Ssxc>m8Myq}OWomHS1Pgfb(En5Ce{UV>|tW7wOMm~xGLWQ_6gHm-_@xb25%S{nb&r?TOZ-AgtdvZ2E_A z@1#f7NdS~I&G}Oy=7gevL9VuuRZ8MY0Sg9VP|KuWN1@eQ&#`_G%(%mZagIv1T;8Wo zB`xcnMW1qmA&vf%3)rrTC`crTv|V=k44ybSyF2B zcF7sXTpF?cE9Q~EaW7>zuVy^$amDSbdhx`0n0oEQ`D=*Bb}&NfCix~yH!p+yx< zDqwXIhYC~YQmyj5exJ#0(kp(DaRUoWHO^0aSjyq%SCl|LM3*k(ZfJ(l9- zx2-pp6}9FZY>)vO1tDqjsV7Awkqu@z{241l45FZ_rIv17c0kJ!p#@&ADq{GL+{cUR zx%Y_eG`PpS&Nib*{b=H*6w^q}Ev&Xun)*TI-Bgg0P`~06ZE_(bVEl7Gcf_(H(&nzV zdGwzpv2uPpYs)M?uG)g(qtrbA0Jnms^d)m!NRsC2rFjActeB~+SZ3$Wx(TV^wy0*5VpH&V8RM;?Kd>s zDfXz3)UpSvod)lSiAW{f6h+J%4v1U(yMNt+LL9!Tm~Hi{K-yI~#&HCc3($9Vz@yUg z9F*i{H84eLqJ(9ueXO@lbfBn008$-E2kR&z2hrg*d>r^3)8wq{iteG>DsllNqzR7Z zLNuFl3o5*+ly9{$3Rq+63uUsQw90aUWE!qpPf(R%DqN-J1Fn%9eAP9MncLBQODA;X z0P8cPXPLkX+qmV+g%tOD0Jt!aq*_-ME{hdtXkA^P}z)k+w*j&DE-@%kA+*xHlORb)UvR zMQmguFbe$W$l8M`Bcg6R zFttUB$}4J?QAbAOyZ-eH+2ar5L{MI^HuxIv_$C8oQppj(zr+KH`I8Q5=2Nt2DqGq@Wu1XjDI~V z>Z$237#rL!=$>>%j@2ulyH(XTNLQu^@||F3RT$QSi)e%IR2|AHP}G^jMY^Y}K!~r@ zbE`@UCJ$X_th&I`v? zLWvZ!DoH*uUxTXEB}#S`G|Qx-L_I^w-B6UEtbx`MRKaxuR9h(JBB8Hy2V2HQsuud@ zx&$(~NhK$wj*#rAYjdh_I5M${Ovp6p3H5;6KI9nsA~&j^a;;r0jYopE%Qg1i)uAn^ zNdZlX8IHmwnEHyZCCzT{a+FY`oHM#RW7Q&+`M9+Zi7QfI38tVcXy9C7(rV$NWO1x3 zI?e(sAFNd^9Y(FqTF9P)aTwC~8lH;vlg{JpPR6tn5ynZvHwqdBxNF>XKpD)R?1k(U zK;rw26Q!jBidil>RcNVF9h#(dUtaJ$&7oV4A1uk^s8eTv+S_Mjl>T9%`UtaQy)9Ws zEdFsSMcn85XFgi~5i1`;suKLcR!2Cu{{Y8Rk`goP@RU+BpUhkT01S8fWX&bAuF(k5 zP^~U{7>Y%ZJ5C&IMGWhJg$hp-4{S`Eg|qYu{{TZDYzZG!7s;bk^@iISs_(uLgA}L2 zLJHa7nU-XtqotgH8o^O0dFRzDsBP-eQkm@rGA&dU-O5%aqTM`*=mbbJK%q;wIVO`+ zvc0l(0b^gR3|&Wrf4BK+)IGdY7i|=(PJpE({u7jFnA7MB7tM{+6#eFyy=D5|<+g>A zx9UdJBfQ9zLGs+>c6QE@-2yL)VC1>F4R2DD+zagP?N^s7zaykWbPu2AZUCLTrz0Pn zecA7XayiV1>KD8Ff7=-Y0eI0{-Z{bCafn!x`dW4yQ*HkMF!@HmNb;0?+beZ(=yBAo z)BUeuZ51BTMPB~mYq5HJx9F__wb<-EBd53EUJaCcXRk+h$R%{iBNFqk%zb2C~;g=+8B z{xnx7!2bYb_vnQ-L+&>)3^0I~9>r}+2~7Hwy;A34-)N0%j`n@>9rp)MAiOQzAHIo0 zT@EHAxab`1Pv{*#dYSkBUiJ~}3mtmjXJXLJ{+%SI!)a-{>)k`i64gU;(=*hlfFk>O zndAI&DgrkT0jl%92esYG?q$aNhe#rKi>d%0xm6dqU%VeYa%&Y9UKF>t!??<(6?58( ztHbF!0{|94>A$@|w%^bg#ra2juI{SKe?wQldq4jGkK7I(lVAS;sFZcDO8)?QsQjw; zlV=yYtfMl%)@1n&YKp5bHPsH()HqaTM(32N3I6~HkR60&4$93l_sHT&^Iqk~aa$~O z0xNjLY#t2&e&j+}QB36}_b_~7x~F9Uy%BrJl7T(o##E@e99DrY(E4nxpv(i~524-F z2nqb8X(A;YYpR%gkffjm0r`oC{pulQ6ctMQPB~p(Jy4H9fz{>cpBR_4w0wf6K&uz| z$f7jrQk{luwsItYsZ|M2x&euHMH@P5R1A__+<(f{A@A}AHc$VsYv-l?P)1+ z+p;f-PCpnhgk^TMcq&O9rea$a{NSrRwo2MKF226j=|g_oz1vMdla1{`9m>+P1@rOg zY9q2vq%sxA*D3L?Wq8lBhbXN}O*iWwQ*XKau%wqvrM_B9GF!`P(QR;K+S-*TeWdZj zIEF<1!tHTtqUBhSkw)8ya3m5s%UvRS18f@QQRKNNO5H|^!g(yW{TXRWXC}qU1C>TLgkON&a$Zc#G2S}Es7>Q6%x)Cc z%MGowwRIJBuLc=J#(-{6nK}V7bP;8@F*Fyv`dTVgDp;j++Z1B2c>sPTNH1~z-#Ith zhbCNjXPFa1-g#@c(A=t-x78}Hm-P1*)~0}Xp#-RUY6|l85hH}i+uuSafYp_zzXcDS z#a`p?&9%s+fK-8~*1kGKh868CU14Y-@nkkY`wo|t$u;Z%WXjg+EO?{jnSn!EzC$R&Ir zh7#KKp`}tHij@r^EVPuNv;Y!>fRu#v00Gbl<6|V%G!B$UEQKh9wu8E000AVEpwq@A zEu(aXfK^4_&dA%inkS0i$8>fI7O(t{^6hlb1g_VJB1yW}4-%Ds!EeP%6Yvpb_O0y( z>5X0FsynVP%^?Ig&-@n4=51Y&Q_)$eDH_MIYhtHu#wioAgrEJ$>FVpJy4CcEYpndU znsjfHw&QrjHqEHj-Xx*cD+u73h7vQXBRM_by!e<4jEd0QJ76C_Kmi z0J_@#KiuW6a5GLbfBn3dJb0Bj{-IFlACV+zHJss_0I)z$zxJK#7SZ*Is#NgvN7uZq zN%E^=r{OYUKHO@hBv0yi_ku2bZ&%D*!^vjXMTuQ%C|lx69lzEuZu$tRY_>`Av~@`! zPIJ^oah)?yk=VidR>O?Fx3a{0vK@SsI>@hl#qSd`#&~;+@KsRZ4Amx|jktow!ji&W zBqTjjr0DC?)o!?UrZkLB8XE|G>CT~GIqpq+nY#LWR@6A?2k$5Qezn)X+NbQ@f%`tT zT6uq&Xz4SbaTh9dt+2E=QEkP`fR!~v>L<+fjF$P-{{Rezk(Tp5c_rB~L+OZq)#+~0 z_F6n+MaAE)bJxgx2l)8@#dSIFAmbc!V5+`kHx}*mbjc)cASr5=iAd^6icT8|J?y(! z7PZWIt)3p(x<>#4?sec4r71cMC*Qn8%_5((u?+&K2Q6l{`wa`W z`ZrSE(Ip?t<{#Y=uW>z8tZ@qHt8XaDYI57-w2e4dOobiV%o+7CGhJ0U)=(^N7djkT z-|CfZRHTGYQe_O)Btuf0+chPAQD6N^<`jxTskW2Qq#5^uDkz-~DEB45w96vbZyudH zS`w2j$LS2c^;~0Wk~AT$=DxLlojYKuN>@M!ePIugtq6@=LqdR_=DF$1yRPC`^+`nw z2W3LiugxPOG>WE5{F6Ym4m*e2>TprBj`621waBV@1cZ^a_SOWmk9MU@1p{i8FkVFv{C)$tZ&)_`uRlLW;Gqp{iJWe&c3ubu~GS z@gWUSq^^{%N6a9bDD|mLW$v7PElKWFy)##MsWfugxS=5fLV9+AE{QLoORQ$$56}IPcwp+N%6C3h8&Z3gfkG9w)MykEUQy_qy zPSUxbglJE@WDyx1(W0x(O~%Q6a@ZsJo}|s7N7cU)}gI- zpv!2`N+K?#@K@|(Jt?+k{+3n&NlHgU;V6ilt5u_FjN8pGL#B}k^Q~0d8n*uc zooE77qoFd=T_J}{@J7V9wVhgq$?4zt{ax0ZdC#lcYi zyf`Eq*1Ww1UZbnmsQCRLU#2xo{aaFYqScdyOuDK5G56DyGv}#AkJ<7KN|v)erV?$< z5=5vH?HBM8H>yPIq#!~-PD?Xr5Lc{s!y)P`#28DUuBm2F5M@{|ra&!9_;ikaI^mol zw0On!PP7Ax6Cz{66&<-u%XBENgYDIlSa8hF%HC@D{+hR13tHwt^L&nxb%CxhQJqT8 zuBDB)VbA$$y9lDfeYx_k9Y)2%-)FyCs@X_V7FZ;OCt3Fqo6mcex0LM!{)M_niNV~! z2`#A}YPS8v{i!{aWc2qN)rC#r7gCids+!;s)})`vwa7(>6_UHSG?p{?(Nr@STy?Ol zQi}OiP)9g@zObx1=Ty!&o40bOY^f*-bt=#qPs&VKrNV9%=&2vp2sG-Tu2Jpf>l?16 z7f|wAGMGIj;vzKSXpDy{9GaTE5&9x8LBl?-kNfQkH zI#1NK3T~361WKfVr+6i-qHWg=D9EPZ+CfEMgoJc5iJz!tP*pDM(#pk2GLHKdPJ4sF zWBqRRPEt`=rpk3mnVje1Bdl(3*6&0J|-a^Z*8 zxJ^|s+<#BFk1zrtG8pLQW0>JkQTA6aiQ&M}T4n*xSq4Li&>Tvr+}fx}0UP|u+$0BH zvE?3J+FUiY$)0A>zAJjc^Q@)Cx3vSkN#x_KE8K2Ree`P@6j;T>$oZ)#+b2566Zpm* z&a<2v(vG$?nZ8Q@0KoYF05rI9WznofN56NT%28*kcOkJVGIdLj&A!$IS5J>QO2lY3&Mas%kGdTQ^IMearO0h1WW_3t>J~ zZ!?gKY&HRmq13AUxzhL4X;en8Jp}DTM1CLJ9}7WVi7{ph)HvzDOwwd~?UXn4Z7J{T z4hSmv^}tBjesY8iduo#0*+Ex;IFllS_(DF26#FxELDhl1GbEApG2fvHAJ*2TVD6`? z58(+mK&OAK1*iO);ON;uLk|4|N^EBW)T}k$$tUPxKR^=3Y%dNN@AzSlqECNXYWQK! z{{Vo)-$W8WtOd9HnyCU(VW(NqC;GTr7lp@bV zZBNb=E$D)M*d-;a><+y0*4iaM;Q}~qDx@O6XcQ>1c-3G&QTrj2X5{DfIMzSqyMUCD zF&}*smkwGqx5w<2bYOajA!oF_B1+QGg$t0#G5r&fO4GUppkQlG==>45OeVmKrSm1FYw#!WxjLV{VIArbv+;p_j*0TzV}bwNLWEu17%` zUVfimu)&oS0@#Xqf+aWtXVfWJTC{$FULUd;4sv>k%9X7EB2(`PbLeQ67>8A)CWfRX z1x{>M#HUpgw@FG96GNl{IdJW;x!yr_!^S)zZ=?3_#aQjPC$%oFgCDkfbnaF#+S9t3 zDo`^}mZm)?Z6&|cGy~wgNi0`?R`TIml92CvWXvy@Caw0z?RAyz0%_SreL9q+j=BXI z=@2$Nckt%g>YHf|bEX)zaISOr_rNM75_ z*OtlEB<}bdsA#2h${!M$>GX*}>Zq{|H7S+I@TD?W{E`x8Izwra@)Tj@Tue5f)FHFE zsqa>xfOPHq;xjF3$>MV!D%S;Mz-6Bd&3kkpu8cj8=hhFqAF{01*;g~%n`7Kx2GXLx ziFpdAYn5)a!V6Rto0sM#Kl`gzkbKA>o}xcD^Y#}U1C=ZJ&E8$l(n>udTU9T6G_oXS4AX+4G52a z@$A$&3s%n&t6VC)VaTvj*j&0f1qdk)`$UqUBn(P9zAC)aLX%Z&OsZOV!d$~zrMcBe z>eh>XU_UQFdO}{`O(>mV+U>)tfxP#_E2F4?FvM-kQxytQ@Aj5ZNNH*_^_xm{+JA@+ zhhG?~WO3vj%?nuL9FR!C9EVZjqGc^DMQuwol#HsPrlCO$Fp)JWN-_WlhHDFr2UT}_ z!$nU-b<|rx>K^&r7D)^0!NuXVW=SEoCB0x@%#S(>9@!7&&cAm{&~dh zbM`;ftcT_goujYfs@HgMC23Gn6ah+-W)ujRAda!>V&LIWyuA3!U8)A9?Cm+i9W7T0 zS<_WHO;FWCOW5OQ=&yl~qj?23_6H49n@v{Dbu(|Yxo-aeh>!5A!X_hlAy=*j#+tAfRQp^c7>gKKhLX7~d&u z{h~DNbAg3m)ToqQuUX@}WLo!X)C5}1zy z_d|<2-6o6Pov-B}unMWagK{-G8fg09^y@CdJdenD!;6lK))+oU02-+mY2{|xN>8jO z(qw_&GYj4vTzV}c%abylQsSGLT5SjZWDt4~p%4=AVoyaZ-crD8R9AK8CW}g0sS+cp zP}A@*U7rXwr4lFQQJqsKmUA^YWx}eogRZ4QLUDe=@HdwVHCFOmwlnTZgpmG+uA+4|1V`GwT&pz04fgoR;fUnnxF0RJj1qDoOgs z=?`ysP{-SV8oxVxhs-{@PLt2$0Ey<8L* zajRUS;~i^?NEGOkHM!VG zKh6n|J?H!3CPP%00#1n+bL58D3MQ&aAMJQYO~47Z+Psw2U-^=ak5D;Y6Di3FJBgRd z8J8fy6s4XnXZR*Q{{YU!??np^zM@Xm9S6>F87^FrqO;m?!u?!mn>x->%dYAvZlm-_ zJxXd*Km{AzPSOy8th{1TkkwaaaU}z)LY+Gs6DPEB?xKDd?bNN7ihAos zZ9USM6gf_yq?vd?#fG&^sY{=Rlw7LP`u#WhmeWwvsjlKrqqyr81aBkj9i zKHpWwZL z#O+I974lN6frtK#*T4_lBEw_yg-qm!^GcCdG?Pf_0%RxmM1uj^suLQyR$n+aQC-^c5(LaZvD7NpAta$_I+!F#Qx0HTfs`xVNZPi{f&m{R9QRi? zApimbh?l|_y$?-L#s;HETOCw9riou_r7KcpB2Q>O0--LXPF14bl2Q~TPf-#vIaaQv zHA)+erPk>bTVRtf13#P>CDctClqIWPUC|EXP6^AoBI?`J%oBkgVrT%Njn z?FKz?YPzjy4Wm+{Ek78pcQ*+o{yEXJsYmwvHp?5}_jCxOH21fHV0QY%QdYF(!3pwH zp+|XKv>Hc(V*6^jxVk1|rB|}0?__aX4`^#e6P@?J$sChg{UXe@Td1lOQXSl{QePQq z-aP@tI}M7)(avCT_^gAI_gg1>Pz@BT<1Qb$WtQQPUKF~zhlc+EQDm#kM!jQlvVt3N zwl>f#p5@X^;`f@AjO9FHn;dDzOMaS>)O<#o{xLQu28u1N*c=t3`x8#=9iyQozMldo z8ZJ9V1?LRQOYYoL2lo?YH2B5Vsna9|mRIoW{n1|LG7}23_U=Z-ds1{&Kfs>J`S09? zZ7aBrDCMIZs!%G*Yxl8E8BzSmOt&xT5IkRqU*8(Bly8|*9nbrN!Qnw;pT0rh74mz0 z`#$@9;;Kq)yPk4?^QCPF4%AarxQa`2Wjc|zV{jejV{^my#tUE_H>i&@r|Mn`=HGKU z%*2K(I>y8PKzAS0*Y#03{Ll9?_IK?@((Vz&mYPhW^==Akp2uQkkxqgHhp6ggk*~%J z&2#KF9lj2D@Cv)a{@Zz9E1TSq7DnT%om#3P_Y3zi_ean5Ro1E-@Z-!bq>$B`)>Mjx zw4?t3nNUD8m-s-83zm1|C3{vxeYnq;Rr({^U$MS1hTAQ}J?V#wx>Z1*=#|xVPgd5t zA*Em#sQ&{Lqj?t|9MyeRHcW6)ol@sIehgU)th8E%yLP|=Z_L`F0`3w+xTk3J}2-H?%wl(@RG>W>`iEgQR zGHQS9h>?s%AIdBEsCV04U5(Fn*7*gkcUzUvfl}tifzq7=Y5@5NS(VP}Qk#x~4O%tx zMzl1^5}20Wo#6OcK~xRQsX)_WNtuukac`vqB$}rqk7#SEvRhhcsa$xa#P2PoMnD}& zDd`eeh$Vc(Xwg)8W#lZiA>!@#Hibxy#$}-H1rLNI!n437ueMm>NYMQ6HVJWJqTg5F9 zt?E$+%sw$K#@+df516ryhM`+GBeI7rK9B@W9#w1p7{A;C0?o0mtJ5pRtOQVUi>Z+; zSnA106PAa@55zz6-Bc$n{$yj~oBsf1T*HR&C%TRb=FUQ9qOS_$i~AQ$lA4v3RMjnp zT5u0Zw2)MwwIs{o9k`9M+er@UZWOAF$KN{6{wuAu5t`HVDJ%CW_pxI4!S2f`=IjR_ zt;TSNEf%{bIZJRuZCBK_ZuM5Gr<-j{4f>0C?L_7h1obi;r4=2d%z-(`Pgw|(Wl7aQmNBNIM5Sq_nn)s3 z>?a(2ltnb9_8Ela8LQ5V9Xb<5L#@*BQsAeeq(r7v>&wtcb`vMCiAx(7$?86qd6ikp zX4?_k!MW$aCm9)jC&uB*Oa!%*Wk3N@pHwn`Ov2?Nni_Y3>8zYsne7i=@<^g@w`@O3 zRzHW!a^WT6)4{2P_nn`U0Rmn)K0T1nfgU=O!*zT#-(2ktWh&# z1pr&ys@!KUDL!Z_1F90`zLe_Pj_*lG5()!D@{29Hq+Xv|y>GXi$FPsNa!iU-jkBb@ zZM7ySJlOvL?)IbO{l#oN1J%W^&-UmQd3!1LMUi{0sLiNxE2DOL>^7CEii(TOuSewAuh75!DgL%2_5C( z5kD}f9Z}IrSV(tJinycM2Pylx=MZ08p=Zl+fGLyUydQ14NGwrXsE` z?JS9sPni(x9!oVyVXwDdtFf4cA@ z$Rx-EGu|=2QOjq)l9i)Jg7uefJ7Hyogb*t7H6==}W#-&NhjH4Qt@^nFr(%r_wIx2Q z$^vo&u=to4jd5FyguapK@hG0;_d?GFYaAnbTz8DYX28Y`eAnlXH0PIDdCao7?eGdOdrS6N$bRl~XY!ntPuuVdbGxs( zO6MmMlen_TR)tdPdI*uv=V8XjN`m5|IA;jC%}u_WV;(+gaeLDDJ;ol>IP#O*?*mfP zHF05&MyjTmCTTvN(xt5$lRZ>VQyJA}o!;z+uY&ZgvmmjD*o1WPRCmX=YsPny)Mc3? zima-rS#eIU{iSMkhvvEz(Klgk390Iz&Ops5u^Sqw*iumVM3Sk?_e4yy6(Ghcw&jfa zs`Xg?1v83q*VX0#?g^Qcaqg&6F~*zvX6bReYn7F2+US~`QP8S-$W8+qC&fxcQ&HAt zcVFriq_=8d&6FJysTmC+^jT_E-tWxYDE|OKND|(g03X5x4qOwBZsgokCCiKITF;Of zl7H6>`-`rq6dhDp_Z7|5xa9>-EnU-J^VtDJb`_?l` zELL}Y3x3d-fB;WTNP?X?X`n!|(T?>LSBP3FGTYrdOPlE{3wd(T0A~+lt;~+92Q7Bk zqtQ)@!qz=#r?%4|C22@X5;QWFrYuhMjTHgEnHWD>il4GH!skxjQ+LEnL+ExkuKG1J{hQN+9?>tN;#&rToiwn(xuqQ^@P_%dn!|(huiZCI+oqFrRJOj7RcTG zkOcnO>a4ifrl{Mhu-+Z_3+^Y3g%xeGtxk#}KTcdim#=BXI^ou?cg(d+Wv)kVt5u~%wkvr?Le7NPva?NHc8y<+2t?d&%433t?wip#US-dl$IWt0U_ z7cz_W^0MJwTZy%2ed4t(rji1B&T}22V)ootilG#xLkqP%a$Q;UfFq)I^osn9)jsEt zLT)Z^KHAhvQjt1Hz^-Kz&y^+WYu_^gqw&zgbWb((L{8l5fgufi^n|=0V zJgiKoQ`!c1L~hE20ayEw+p&U^-^)3`#l$50VF_a;y);r(^Ryg zT7?e55;DRJo50NrgWvctSQjMWcGAV(rg9 z;~Y|u_G?`8V4uvTl4s+j2_@~@ij|Ji(W9b7R`VY^K1%AcDu;>cRWm=?1DmUprBm_Q zTKOUsa-S%=ujf{&)ep={NT1eFIDFnI;SIG=`hG>^A`+#jtNdDjd^5gUq`DYxy*ROcsBqj_DJy7}_{{S~_#LkeJcLe_cbbMamQoWe`Qmvfx zGpO6Xlz^VVonZd}eA84UmhBx9mfM!)D^h(%Nt~xI`y-8$L_3*4m7mwIt7QKGB+Ly# zk^Pd6?()~!X9-_N7XeqE=|a3)_2k<$mp4A0i#P2WzJo`HsXq@ z)D(0`+NnaC^yjfqvS*qvXqM_#&%@2yIq#n&Ft^nm((|3kJAFzu5&TD3S-9v^-%Jq7 z{&T0Uq<)6sKwM?fC8VZcg96I>l<~J}`-eTiS8-lk#Z5Isj^i~Bg1RY^8=2|{VGtSI z1-?QDMqMtwrK(hC+B^x(`w5D$ zymYT&Ei2X>Tf6xyt6;{sR3lof_VnBC-SP~Do2wP+BDS4t^%N375&--nxZWciP7+$T zV}i0CVJ_0ij^I={=6*ECvO!&#W37D8 z*4F9)=_QolBvlO^JyP@ zfe$oK9tge44T_hfW_w7&mm>uR425V>A>l1yKqnsJLeN`m2^RWzi0Nt^J8Ca=Et8@E zAn6Q}0!HT)6-Yooln;b_=TSJypl*hW_NWP@M_N+AC<2R99lk<76AIBYFgh(;8r`>` z?W8**`lVvFQKo?Rh66lO46a`W+dWV}2yu0fC#hMQcEX7#uR#O5s&qV_r3Dn|+^>YC z99`Y*3~J~lq^|E5(pB~MrIUZTy5D41lab@Y1H@w z9QKM)nFo5r;g-g}aO{z2mdmxN7BwHt*(2mcGT4n0ey*iu{TvC+iGkCnXmG`%SJYK& z6fL$;rBi{a)QEI4o(U!O9Z}UNrw9Nww{KG|e{6GRwO<4@E}CTQkumjB3s%IbgQx>u z(eqBL;LTRgRiU6AW3FWq=)5u{kR>=BL5}pst`x zPumgikw`VQTm3K$$KedU=T6lbQ-RT7iXly=QWDl1bCZFA8qra|U3IkEX+c#* z&Z?T0of7=cR6LeaKBZEFEgB512h9PKKHa;Iz^neSdphTgR~@$>MRiOx!(<04ltMv3 zP)G(hn@?yk4}$Vd*__Jfd{dXh9A(Ma)^;v;Ds?weYh}gKHTs2ZTFBeu5geV2ZMK@# zTR302CEu?~CSR7atfJA$7uT7)KTTHgNvuG1Uz=SF;xhoiZNev$JN;Qm>V8F_7n9H^Bw|A;A^Ox+{&@+lOL?U{{Tt; zn@-A9(I?l+FP*A1y!UBj!8G72P>1lQQ=)|Tt( zRuX){ZE6A2azTs_zdRASd@JbRTLnj&^rvKbX|=y>ZZvDOT}OT0y_i|O$?%nv?j`Nw z+>4Fo)n=rs<;yiHH!k^$mP3u)noDA9s-OzAj~MrDuZ0>6m=02Ru;N>Hu4B;#ZP+QFDmB4CL3%Nz&0enpgUJJQTiX^P0M2?+%!(=IDO+=2XqDmbkBu;Y&qysPjDM%&U z>L%Lb&A84vbE>tr%hw}Dpq16J>6VJX^8i$#IUNi~w;};Ak)2wtg2BpD-5P&Q>Dp= zQ7N=$e<{+T+fH*WwZX<;8c5SdpEPfEcoQar`jwFUw`$-#JkR*5%W=gdd1OL>+GEk6 zFE}kEXb>P>@GZJYdQZ(FW%_>l$JRWM8bV7w$HxAU7EwnpMyHWduYZ#YSG59YY^H_G>AH`2>P2g$S zGntTrE;TB(XwscF)i%Vm1ZxoY0-7rYf%R1-?+Xb^wbaflca7?`97BZyXL3uLsnD=H z131sJmOkY|)iBp)JySZqP&Qic=RdSIM75TmSj73KZ=$VxP%cY5}_{9=1kvZ=6kdY)0jT(#Mb(Xaa;F?Wue+$xgjG-A5%}-VzBtZmiHRY$$c7z+$s3#uKEO27CRD$DJ<<>%#1U46q^N7czKaQBCExVEI)XsVx_ zzy@d<{{XyV^M(6dd3UT)QuAMQ5ix{E)iil8Gjg6t+Sy{DTH0QqA3EfGXB)2=`*6nE z(a#kt66C*f85#X7Gzo>hVz-Qvp-2g|^&0e!fuc;cqBUlXC<0<$8bi@Elq}MK zj9U$|5_AOgCJPLp)kezldLgY(WI0yF-n24yhi^Q+IOYCK=qL zQ?)wkRUbO>p*bEs#HQ}&%G794^d3{|)B=D^0X_Jxib@0W(|^Q2I8=R9l)Vw-Q}x;Z0C+;jR15w>rxzHR z+Ue9(5(r6`$}IL|?OASD=TE{UY0_u7iRe5!$J8h`okH1ZKl3yGDZ?yjCaP7gpg}JV zO21tv-T|57sQAaF1FKqpOrbpp*YJnfnYikpXSfG6sMkTdUsw+%@~c-7ADW@fI<~9# zelb~u`j)dO(JCm_zEkPiB%?J@*8+)WLn#SCOl?(i=@?X17TeRWczMsF4j3v?);ms$ z$AMabC#?E)g+xP?Zdyl1-4n9ag|*Uxgbygx9YhrzdMXR@t`ALiS&agP6t^k(eBwUh z4OQ;}m^BqmwUvkL_f%DcnWXhT0Q?|U!$Vba$w=AZ+NItz$t+XQGL<}1qzzL!P7IBt zm4fDYS$u?|+{cq@E9s3j6Qq@blAwMc2p}~`@!lTD9ajY!E^N6{@p;l*acY?-=L6hD zqSK)cqn-ACXb6oIzEU z&qr=hQ`Jf6Vv(Nt*7d;0`h8O}$Wchwwx%l^mCjHqgvvYaChEROCr+`z@|)2JOL}Se z!?Y;`p#Z4pH8AU3Q)C2rK}_S$A*82sj*t+M+@8=z@W#~iQI{_AQX<2_{FPB`Zt%r{ z&(Np^CEcb)Ct2wg1Kez5W%kR)}p?QyO|FL!pK<-NS5A z(juJ1aED0$0L)L`L&tcfj^30$(n&}I_Lc2nx5UW> zlXrNp821-4%bTYb`KF7OZjLu%s9x!LH69V8-Z8$}s?04aW(u4$3Yv=ClCf^AxKy(; zO#c9iGTXb4%v+{0fIt+P_U%DT?nQ1jDGCKbWb~B86JJ7#yaRIS7l~T4(cpCGPQ)op zbWd5zBLtqLAZMjl{{XpK=0(D!Hs~n|kORDo;!I9A0HdXVnB06-je7x5a|XAl5@aPZd?X-R}e3kGNMV8_zS!ntL;vBO153x;h|<&P6Tx zlzVDp&m4!kc{}oI;?R7Tx%dy;9~;3m21Fe3;zd+trLYnKP~N3#N!MSjWmi`&aj7fP z7~EtqH)|RKR8m-4DI2vZl*F}&neQ3}30A9H6DP_O$;TWk$Q-41q_W`n#YNV!^#qSi zwUmkJGG!@7QOiNY|MUMd#7#XCoTks$3LE5P=@_DQ?sVuK{-D}t<4M4@W5ra{6d zxXypu9wO&|b~#LphARQFo@>&4Z?c$tYj;oil6fnWu;YHoy^Z4KZvOyBwANeNBl?r( zomC}3NPrcr$?y^9-){ReKjyjfNbdnsw>us)dn`fGwYKn&6Y0~=zu>t}`?~wA;g58@ zkBu*gZE96!JLKs!5w_UEjhw`p2q&{~9nNQS>UhW$^cG4z=t1eHqNp7TFQ|^$IHiW4}lc10hF>2S!>NT}cc`Mj$*}6vn0+YCR z22;^6)!UJ-qRm8+yjqd`%C~#%2@{vrE;c-Ukq!3-p9PL(dHk1eG1)e!{N4%Lr^;xt z>O*b8EbUonogufYoIq7esU!)LEh2H5X0&aIyFgXhIJ@Qi%cqyS%fK#!{@YJGPwgMw ztxDFSwc8rpqGn_oQI@ew<*wf5pCGnQ*fd83fOM+m@0z_4mR%g&r7df7a!kUEnxR0!0;+;Dz!hUTBhR}aZHvelWr&JD$_B|UtTjVSb4 zemMSWkDOlmXl#7Q=}1zXvDPIaT1coKpAiekiUL>Mg|(hF18p0fX_@KyW)ElvADIt+ zz$lm2wvvN41b`aOEHFBXw{SJ0I7hEm^E9*!E%)D0O+7LaR^rs7v?K(~l21S&%XnWQ zqeWCj?Zi+wbDc!>wZ|Cl0Z&PH=6ctutKI?IZyC_IpXzUX9WKM`AYlr{@|*2j`@d?_@^4GP63$QaVGM#2JR}MeQYV@p)!$Db<%NfxQUNG zp;eJt-p>&9IJ6Wmk1ly7?BsR#>&;!=ZtdS!RO^o`a@Dy5 zE3EIbCN*oN_OpD*1tVhPbIIohKzd>qB#OMAc3Csusit#xwqy*^1Z*@DX1{MOy&1F6d znJ;uc?Xo=T`E#gx-WA)cjNIVA z(+KPFRtN8=-me>bL*umjdEg3aYFwI@oAkAAQY9g}6e$}F$~w4un9lLGa!m7aa&_X#iVtNWSxS^D~lm5Q0}B@D6-?xq?lQZF>X+GtY46&VHH zVYK;_N{JF5SmhGLo(UYcYO~9dol}aZu+S=O?$A3eAX15kM5nr4X)P3=rlujb(4DQM zOQ4UXlmuiklA5<3sl3+8WbZy>tO#9HOQ_LAf#$wWxABfP?G-b?-QP@`NFb>*9bz*2 z_uCXw_&iJJlD-O~?)Q6!TYGx8rDmbpw+YnGs-mGI%#T{cFg6I(rQ+-fx*ArlRj80U z%#OlQJ7EO{RU0jkFLeTtwCZY-k=vWnZM1?I9zPjGgQTL0j1rhk&nI%WsdUV`RHc#< zp50Qi8AdyYz+2D2gKUGnr z9^%$2&7r%UXQn$%-QF6Qu5sMBy^w*fs=>^-;l{um?H0Zg!YJ{4aomdT&K2NLi{>M2|qP!#R0B}3Io@AD8uEsQ)M?_G8yIw{X@ zD_nx&gPxv<3=QioO)P_Hud9ZY6@&9u0@ykj91V@K^?g8O-W zo+A}3w+C$RbF@A~LHZ~G{VJixulNEm>|H?1vd?7y_Xxf zKt*FyAdw;JYGc5JUjqBBYuPDu=a4ff9e5i4MArVI_j7$mOCv zpkn}UaaBugr1b=eX#mV>RHdxIby8o6ulz4$b(e2d%sR_OB($Y$zEfqSQ+4b#n1KN!?FwcW!tbBS1oGRN8mklGXQ zRf)+S=2gh=yz;D6nEOwO8d`1TAQR%Ky-$8#2CE=_n_5(DxL)nLP%|O39#7-Lt47xj8AnTD(FtptSfwAbV;Oz9MWH~ zlt{TkOuLkW`=gAKI-#VE{%K=v$hhIfp)S*g)ux1XFk@$DkwHEu`K36fpeqsp_hx7@TN@MC$R_TFaRfM^sqnw~La=4CBw~{{X^Oui_wgV&CMJb8Yb}G<$Tvs00fG zi6i)w*&n_e{*fn=;mS1lt%uv?`H%>+sXyZK_(P4wNluAFD;UzKDd7u>CRIbWVtdH# z9R8Z{Ixm@&Y3Q;40Hk4oCy<1cbxAYQIxI*EL|)V&br=n*l_f5EWcN&mgfkp8ty67z zaw-_@hMdW1Z%+DtFujdS1;rcETXAHO=4=u15U|$OHvP#{Lq&;!xm68m@euc>sh}K( z)SNvD06f=DvxlQ=Xptjd&?Ft=n-F(-Q>~P!9cGwpmeZnlY>BbBLxUS z{kE^EAxUrY1~TfDq#+7S`v5wa>tX<>M6ab5<~YwEe8~)^S_7cgI$>!QUozx^hbx8b z^%|m?B662O49gg*NV+~K1HRwro!p?#gmg0kIQT^qjk&5$-95fG(=IKf1f6`d_&~7*pD9)2%^Y&xJ5;-ct!k>WRuwG{YC~k1$dNH9 z^|xw!kTS3crS!<&s}JVfNZ3kW9;_-KUZ}T{K$+K17~0&*Dm&y&y;T#1;sBQJhpeHq z6%k(bLJr-qBq)?7vM6cqoFi9%P^RX#i;{ zK~Ue4hPcN7G)ViEikYHS>B?QCKe8tvmvB{^mUl?uJ_XXIqalIC*%Tg!hB2e4yNC?rCV$Bv;$r?(1b%xt$Q(x5?T^Gu;)Bs5H zYaIGwxH3&81Xa2885EL#)4~@Wu}W?GSHIIASPI*z>j{z)oHuPo6!v!}l@!m$8xXI) ztr-5CGLoey>7*VbphJmEma|rt#HFu*gqEU!S$?o-mq zOTlqz5(^TPbHyvzs>}MtAApY4ee~}0T1C=@VkeZ-swxkeHXRk;Y#Fp6?hJ}7GhcbxqBJfkGI?GQO5QOj= ztiwtjWibeUL{tWaed+%I9KNv)G1X2DR>U?**GL4K6ji%c-$;0v5znfX1#a{Lz&kYY zgg#Xv8~}v4!f-sNkTMz!O9{uSa(tHN75zTTNPxGT*f%JR#UKDWl0b(cbdGfxHxv#= z>KjP+%xDzK_Ur7=n-%U8iK=t%IIqWX9D4ahUz1f;|)03+>ata8&V}82UynSv1DiPSZgj_Cr=dv zYdImtYOYs$Qb27k6uKq|5}uOqik5TKHmiOBx+AzMxXZHl@(jB5c6nugsd;;5Nzo=l ztRl(2+dy2u9^=c30E?4lK51?~5Nm2wF^vdrp)N=uPJK0nQ8!8_qOOT3;TnZ0sZ=>$ zNOD8+5Co&FYHoK?)ljzeW?Q?;mF9YU1GW*u!aUOWox%k4M! z8sHljviu{0`J+Fhq0MlQ*JIZ_KCvoW_4Y-rNTziuR*oTTxwT#fVm~~&UuN^xnU_gv z88Meup?mua+#D`P9M?@K58FZyRm_(>Vc7N+&AhLRVY6NlX1`pMge;l=0O>BiU#39r z0ubOrXV?h$Hy(~w6P%sMD0nhGS9 z*Yxyk)mGHF8>68@ovK;VO`sJhiTng2XKiX;J`*eU`KqqXea~_=^7UBuH+4A8i4G|? z$_AQcZU#cUrNz3HlA<*-&~%DL&R$nL(r6arj^Y-PFm7(=c&Af>xsN8z>+aaLWm+!p zw@SLJUCm9Q4uaBvQ)=|WQb7t5GMpKVzI=ykwNPBE2Mi4o#wo1`PfcTp>~x8hS81sU zJ5tMUA?Dl@Ly1yVDT5^fKehmol~Pu(p{k8_+!Jg|t2Fo5(iTZbQ*EkK@sSY_{CjKF zUG4MEBTy1TyN0UiQl&L|L*L*@Wl8)_;{*L4#T)%#SE9IowCA~N&$l|u#~5VyHWP|w zb;0U%Rw{Pz9BeTCJE6&>A@ojKOLthsySKpZE?V2WU+1&S#cG|n=JxSO>sz7u4<0@R zIKJVqP6w>zI{=?RV84O(-M8fT zlUcSq=IxLQRQRl0xqJ>AOP5PvcS22R<@kI*ci@?nVMw5OCbdP^5=e2WNCghkF|7MQ zY8EK#hXj1x7o?Dek{d|u01)7b4)@fm7o26=iuCf@4E2wa2wM_H=KcgnTH{oQ4GOWI z6L|jsOrV~!2-D#x)$oLvg7P0fZ7z@Er_Lu(P}?&-Wv9|5E(VH=Z2CDm4OZ_&rznNF z>a12%q6^JIlC_xehzqc^Ts*xLdClt`^xX{-2uGT&>kN6`-HsOyGF%@wn;{FXT^X(52-U4cq*ys18(} zHwCDk{{RyQGZ7OyH3c^$mBN)yb(fBoN3$BgV8E(M3y~aTFyuba>?> zKmfo5bP^Cx(*+#-g?v$`^rjn0NJyWIq2@%Gec}k^14$Afe5DSxv{CZPR;1D)DkK4~ zK?DYrIhAASxBd|hQ3_;Ql-np!B+P!uA(GJw{T5r>0DzfJ7MelU`$-ykd}FDn)`%n5 zrqeXc06N0M(A6iw6Tf{*bqo0jTOU+|K#JbT7Otz;UYd9KM;?$x5kt8#NCip&4JR=j z5>O?GobHH*N32J<)Rd6n^(tBBky_62*%h$7}r!(X=Z-N6e~8S=1alo)C4EB~@`Ns)k*0tt zNIoVxvWR)E%aFvisVN^g>Koo)D9T!OjuMc-O3=2!27M#rRQq0#*#bZWj?>l$m}spy zqLTFA@gb(YP030lmXxp5G9@oCf*N%}OY)UBIH-kNX~Klnv}y7&X0$pizbq>y6Z+Yc z0EEs%tl`(Th&H;5%SA{0*#Jlrp@tnV!5+?t4e{_U08gZZzL6Cu^(i>MR0_2~HK$~q zAh?J^-Md8ux%iURO#3UZ)&j_yFl4<0AX$$hKb};Lxu}|D1sRQ`pTZ>B@|9AZ)Ob~q&nO?}g>^ceeg-I6%^tUo z2uZh0(aApZ+a}T6Z=F|g>~coy1mH9y}C6P-}r+0#TjL>+sj_H zBM&7-duEane{5XvT(TVPgPjS~g;dPorp4X0vh=FC{xjtsJG=4K6DV5b_=um4_}!Y1FSq;yG41_~0BzMHsTKRj^~Xitp)g+~Ta;+vR;_U8@dL|G`eLk|wTM3^+Nu+vw}%d(kd+-}9(uT!)V;?IxC{X( zQEhE9DolrjqBz_%btuRnGCOD$Dcs{;5#)|lxif3RF?a5cyr7WcTUbJ8+^R7S;CHtV zg<-QM#JuLHHw&J~($KzNXDBk}7C2y5i&u2c!u=EepT5*>so04BCX?0HeX`JR}r5N*d zyE2*S=+Nn&PsD|;HxBrt-168V9(pQN_h0wPyWSnTVmu9R{{WR<=7sraDOTBS3PUYOcSkQT3_4 zS2oD&sh@9zewcR0?dJiz;q>AlOBL$0~P%R8?Zgqwx=kpk9nQ6u=FBoM2_y}AcO&-?G%ceJ*4x@N zJn8~oVmvQ)xl=cF$Z8X0w;2?r!zY$H=#vK8;x~GpXcc=c%qVtSw7K80J~r!~Iffw= zn2oeF>HrU39FpHrpH$EC#euoV?MB!|51OYA7kQF*xpwkf;5-z|_OtBsoIT}VE;jBg zpr@&3%T-3sU6kA^Lv*&(TpZYixHVeksoH@*lq%|0M+ll{YEZVA_rB!Y+cKL)PvDyl zY~qUU59SU*;k%v9i~Zk?u8vzSSBfyS($%`kQnf9#td#`$r6a5vZbXa^Z$hJ_ne33U z-9o(o0JnfrJ(YW`tS%PiMut_`8B-Jeu>qa_<)?5xU~%&+nLb)V4oPx&G#0fuxf9Up zdd6XPDr&YH05+wUw7Z9=%BV}srIwK1pbZb0M3)PM^vIH1 zBXAl@Qj}I&*+l|V^8Fi)^JwkiVRqt0IzI-5{MWl z^-&v;ih`-{#4yXYMAMf_*V)H0x@mr|B&|N3I1`cd#)@%X2~Xn}8*5hW-znPFmgVyw zo3a7D#W=t9KON)d-L`QBpsMDy7OOcjy$DssVMEROHk1k}(>{hpP|fEhYX-}G2bx|d zhsgJPw`X_3G%wZF*VI2=8S|}F8 zk|ne21^Z|x$y6l^LQ%R}j@rk4#N8IkWV=yN#XUL;k3683nn)^Alum#FGD+|d{i7c< zwzO2CZ?9ptZs`pIcI&W2;kK%Ga_7Aars|YAYsAqa^*ahP^=h^9Ony{#@rz5aLfiuL z>OKmCA1nIDksEyj#Vc{fTyds=5)u;l2$Z)PAt#t_D)YYH4k-H``~D{~opDwRf5Ec)th@f8I;Fo? zQ|WCD1`^|Czeov#KE2~S#An}sBZ(B+wccsM;lsxs*KXdY@7tT)W;N~q0PW_ij_B%M zyH;d(#9M9KfKuI4;3WS5x+OCeyZ$F8MgH_QQ$Bh({+`^)AW+6{{Y;7 zy3B)-aw^)&n}VwD70)csc_Fl@=q2#&8jd>7C|Vg9r5Bts*_(^GOw&5GPiL`@VR>h{ zYvURJ03gyoW#YOAcez@$sV-fP>6Vz@KjHrXiGPGd@{Uy=GCi%h-95iENRCNzkW(t2jVToN3NoFc$2Mu|sD4!S?qfN`;~ydV ztBq@@3u(+|BaOd7WuO`o7b>|$_Vt)#*9*Qua(3-?xxBY770#_oTaGPTiJFN$VsdNO zJ>LYIEOQ}e$IUsIpXn@=ezUG6wIl+i5(niG!_h6|I3^>Pxp_~As2F>7GkSSU^_OoQ zVW9dZ;)_KXE!2)=HHyC8SLjhN6}1ZN6V#r98zmMr6iF+c)~}E*#@TJ$&L?kFs_K)V zdPkr-OhZ2Av`4X?DM?vVOKPD_6}_<2EP#|JPJ^s4=Y;%llA2qs#p{h7bD^K=3e&n~=t&+1a{zKnkIGPd~cyCQVgvdGh0G zSq!!bkWBQ7P2}cBt(z5(ZM11!#C&??=D8J>41Xn^W=4m+6OXh6)ToTkM2!NMj5OUv zLpGXX5l2lg)TufRJ5DDY19~d03yn%eXW2_lZnw}l64fx0K<-GC=Mnc3l`m@pK&ee- zj~cpNpB3dhn%&l5p0n0)nBl4^(iSyK9uBiyQ^*+Uq;G4~?;&d-8HA^-;=9kTKARTu zI@3jc?Jc0W%rH1rpw&-qd&TeB#zTp@aC?20)PS;{f?+ZOQC93eCgi|om(5H4(dX^H zrMB=9P&J}sGYp9jIZt7>DpVHG5~7}pSRdIQgP!MFFDv4$C@MloG=~Wrlu7MBlnN@P z+5#{_UsQl`N&V1EW!w@70P2UdDpNT)q2nA4O}2#y4pgQ`a;Mo-)(L4TE{Fn!HC0-L z59cf6EaVPO4&?Y;GlBMjf^IV>$q5}?Kav) zTu`K~Y0*p3avGkoF?l0^r$t1vf;nDD6$u{dSg9j&0a8Ho6Wi+$Z@3O}w>BaI4E2(R zr2aruduRmViHM>hfRqV%tqkX)PtF5#sE=c*)GbF6r<~Hgv+N8Z@1iY_2ug9XHp#*O z{{U#fJq}GY{`T z23{SZ(T9h@40Bg6KBA*0Z)K{*%Z0apQpfPi;qoyTac*1DNZVfT>X@qR?WPu6ab>jh zROTdpu^wh29k4i}1?E()s3k7^cS8o@an@mYi}+abxw?HP@%hIcF=|7i9T0?ER-?Ep zuTcp1iu$W+YZkLDqfW4|eIzuYOs+@GtTFX8OKKe1Y^!pc3XztwFy`=+RCz+>6H7we zq(hr}f(J+@8FrK$(A7Q|FDqf#o2XUKj;ODwPO1(iB$M?KKV`_EW`&hdH&|I7baI*8 zZSTi~@cPPkFFxC?_eNRj*lL_lU-r7e)8;Mb6a#8*(#7yPt{atse&FeYa8K6!P-a zExI4nYCDQ?v_*>*D=ozd+^X5jpQ}EY_Ncpf_nU3H1ud*SrYACu5i#)ch?aJKI&@Xm z@u&5wYOcQMDk@Y<7B*WGGX+0b6!KsWjY}M5)zqf)=k7A~QjzJ~*=aiYfS2x%r{pdo zv_xC+fDcsN+YinydmsK(OMD&5fYI?5we*~SVudJC@tfcsX zJz>p8l})-ZQ5{9v?vhJRxB%8u>m78CqT?bJMAbLD^NJ(YUY*L7(h=^w1nCVP2#MRJ z{YoiQr0b!Yj63c)q-_W~Yp-coQFQ6otZ==@#dl~zTE5%5j zLAi~qN`{)uN0m7b6rRF*0ijzRo^Csc>Q1TO{bvdr3;31l*xk{}v}{Tdwav2<25IPZ zk3Z**Uln{V?cKBkT2*S8D~lYBvA3DEBv2~2=RO9w;Z-%4`#rh(oLZ$yEk0197zIRt zrAwrHW3*lFw9dpLk-$pP`qmH5`+0GUl)ZqM{{V7_l~jst;=O`%@=9b%H<>0`>l?We zzLUGm*qP`A0Xwu(R)f{iIk>Ji)l*G4q;FBwMpMdiPGhtycj1_3y|pP@TZXn$e43uy zVv%c?adNy*h;G_ZATHQ{rlL{^8j+`jSuOACHC3C8Nohs&n8{jHH6}+x3X>w%ry<4? z5<>N2eV3NdY?%}EiA!77xF|dPOWNe!UaVDwE=VMSG4P4oYu%@+tlwiCTUsX0wwkGx zsU&KX*TyXHOT1N4?xWa9M6aPqAjs*~4PmHJk}Ro5^9S<&TEc{gNxCJKD-J26{JzmE z6U7xu?Zk^;i z4LU{Nrb1CwKPW`Lz2UO@r8eDq9TNPyj?gRQ)ksFl1@EVz)&!PBeY+Q>nVIN*F%<36QCHt<<~rkM1uu_J_;%o@K!C zt2SYCqNb^!x?-319D3bwpsU;Jv{ua4%Fe;H)Phs~6As#CX10@jeOK9TH*1lL+MlAm zynF7;?2DL#6x_qbIbXI+x!tIC_h&*f+*wa70#*M2FSfCy1Kn(ZBF$;tEQ}-8(Bbe| z_4i{eCS@XZ59Y{T$5UXCCxD?@8D%^VgXBr$|D$eeDqm~*{ z$|htxh%ggH9qo>&@rP2v0*Kt9sz+$~vLs0r3U(N}Rb05b;@guA)6@;98iEIZhgc%m z_^7+4(5616k5vF|=roYhB^dpws#Ku(y>hXK^Z2m47z)BB>bV>`%4TuG+y1jszXguogqa}o1n+EB%byUi?bUUAHd z(N5@L`>h>A6z-B_mAcVLC+cCFxC`SnQg~axVsPT9`y$2f_@&OG(|@(SZmp(SK}(IP zDPGelp+o?rp2S9LEwk8YLiA;&^fR|}2q8&tZ&+Q@*t?!E_K2ElL8(#!b*N7A+uPxnOA-9c4MwsCW* zM8;nI!DH6y*C{QwCofH~qL(Vv=w%9)o=F7~J|-Y#$}~EFRbQ<5%ZqB`N~JD0pV)Jj zTwS#ap}13kYORjQsUmXRk~C2HM`E~agGAlVG4LNShTKw1Dg`>Ge}p)Y%F|VqmZ2h% zuv0D5nEJw4YL_E`0WbK!BwepGskE)n)26f~+B4E>PKHOsMRza@9k_K@oRh22QDQ2d+y(XC+V z?1khW;IsWv{7Rg-!{gCrM3W&S5;AmI{69K#Y2TY@0b6v`L@eN|ZGf#2WymSSugqsC zhmngw$FhQ{oXBmzsNFpPoXi0vdTOk&U67i}yBwTR+wg(JK%%~+`K1+>)Y=1kkV*Ln zd_of@@TD*45RJZ3))yQAp`^NU7>1ib@~5wO=x7O<p=;~#=?+mW zKgAX}=mf}!__sutN(wJ}tq2mHr>TcP(J)Faa5g86}hFovVZ1E zw)8@EGlq@O=#?Hd#MLCLb4tg+MGFd$Kb>FQ2>QcK5$v#F`HD@wU3s{;OSL5lJyHy{ zfZaRXiKI;MK~1a`?s;l;N3Nug+Z}9m>bP%d(F{c!O@x5h66$0Ob+ia~-MFpEY@QZ zVMXZJY@Abfc)GPx>20&S{i*mvPXSx8_GOK!L1-Hw%7vcR)TYwAHKtmh9slna}J zoLnWn7P@donrb3W#Cldo$bX(xi@2};QNu6cKe{GjW`$KIbyaBdx0@%QU3HB2J@c*S zgXwNlRZh?-6B?L+p(3iwB$}ZQv``bIrwWj+#ULwXH3PLu8&lW3qq|C9yF{j9!n$`F zb!K5I1gGmbA+hcRAuc2!D>>jFa2TFBjZ@jLWKg+AnIUJQq$9^Y((IN->h#SHp~Y=H z6T0@c(0fv8M1%JU%edZbJv&;1`#QO%p9tCeH{0u+L?B4Be&urLvU63P6jHb37fKYA zQC7K3g>^y{Qa=G3k;XvxZgW8^Df+ouHiAG{)K=D1t!sJZI#gz&Ntv8UvfZF)g8Wi$ z=4O*JNg%_GMq2eqdSE}R(-MD-e)wV!=7l|Qf76w0-rhn*l9+)xca2{fKP)dg=MUx- z>++PG$EwNx1erk>j6Ts&7?&{Ig;H5VfBNlS(fP6HR*KYSFm{FQkp}KnNbcDreS0`>S z%OfkG?_%~>EtoTO{R3h|0nkWz#))LwL;}Dv)01@M%=`ZUZAh?J1gFa-O#LFY%0KDK zxOTw*0PaCjE;nm->2hk{N|2|YSV9mxl^I8tc~)JP7VeeZ0{8D9#6I?R#@`ij-q?QM zR{WVXITm?VDyf{>;GswwgCip`@sG^T@_o zLaF}%b65^rZz_E4Ssj#=&&wXy?O)h-M(j3xoAxvOtIRy_?*|Ww z-5ryC$v@1g6FB6~T;}|U+_PMkza_4$Q7NV`IJ5$z{3%K4AEZ6K>?Q|~#v5x0sPU$& zz@5wckIrVCed$PH@bFGHIl(nn`UbBVYwcKV;BNF5KyapW{{TtG`EP32+fIwix0%VV zBp?#xS&;HxFN`$pvkbSUqioatNx+k+GTaA@IBsYt6vdApk>hjJamaEjwfdoN+$z-J zQT&5IAU}Mm=HlaENgqumN4oyAkDThIv{7wt-lsguOsB5D0|aaYB?^^3KH0ptmvBeC zJ~z(rng0M?OIHhPO4~3IT*lLKTavZvTj(5Gi>hp%sY;HLpGdrBvR2Intv-;jz7fYS1O3Mez1}59 zP8#;jli#s5TZS``$ zWL-c^2O`_9IJNq!)p@xai}b|*05v28$QfulMvsK$kI6vUfS`7&w|~Pe@fQw)Dx zHV$8CT74hQ8@j8wpVB8Id96?q#M)7HasL3bA;~xu=aGHOV3$IKUFq#MOUhWN+G*dX zWyk51;zG3C+R`WRA_(S_zT(0#!Kf-LfZ0W3e>>(QYlfezU+wW#6YuvW#j;<0{^>4w zeL^cS+KjTPG!@N&5aQZtwp}TflJydy7p#14j@hBzXch^REt2`~Z^}vi^j4MZ5ydCi zf4YJcLY;9fgZ}{FlZc$>U(mFk8aCS#@m$mLz}t}4qo&oWe-cb)H)~2AQ<7+>r&xWNRCZ_aMq0ZQHHEPO`?7v`HHnWgUVW zPn9ABn(k|pR~;19o*vfBdIqcJ+X&Ggj37jWxt67GK>Vtno)G(8L?J?o`fWcy^f3KX zktLo9gXMgSd!xtkJ3F^p*U9x15>m?z56tV%k+ld z>NS6ZM^zxIEAVax^(|Oq^!e{CZE?GfYEm4v*<4WCTB1q+0Ep=S0NjWDQG9jPo;E*f z0lH5YEOI-0{M^E8f>{@XQ$EVBSBs0+JGE1n3zce%wUv&NryN-`B$E&!?tAL(Nm>oP ztZ+xQf`LfxJ;N*SZc)=T(I%xyOFIE2N>KqR08Et`$N>^Cz!<{!ngF0~ZcMTW;}vl$ ztlzc2unez^us%?_b6vHhVf^W5V%>1#YNRN<{SgjLbwYAR^g2=yHIddl>D+s`fu&`$cyJypO#;iATznp6R~TJUGS_#rxa76+?mFaR$^{$7vL{XsW3Yl1dVT zxFy7o%M0QC&YjNoZaqQTCcBT=YfDk}He+FFtpcb20Jt|XRk6a{H-%(@()J%l`w2s& z9jasoQWQ^g%292$vScCe8g5y3Yb!QD#Mkv=ulzHXZ?15b9gkC7R=aEJtE*;-DmzY7E2B7K>NN?OkvON<8M@0ShsN8ODmgx`*(vnIv8AUm|kp$Z+mW9ZRw$D%y~iqA-kb+G}aX~eW2o8vPBNS4+T zn~a|M=}wuYmmPOOE~B(YNg-2~%%If(q!hMmd$*gLfuytw)ZzFE%UB@H8WMd|KWJ6M z?hBb}GfUI*>1BDSVIE_;`hGQ?@t}Qf%$=tM;71ztu zz?QJ?HBuRC{fwYfa*m##6|~dZZOwylsUAcQqjrSFk+9KFWt6z;hTW^0#q7#s)JeJA zQ@O>SqrAQliCy4{w1k~gHEFa}WtU*sp3TG5{B?moWhzJwQu5!4+U!$3-B$o#P0|xhk&i^F=mGLuw0nOK$$7tU_`;Z`T&XN{Xxh02kyR(x80( z4HNC|PdlNdsWz)Zf~pMA3S>5er1XsjEOd4j?TMOby#0BXpD%vtm_Hbu#H{UL?Hwiv{jpGy+NS?DXyJ?XbBa#nXh0$u zCZed2HA-qr!=%e+PND}T7RJ+{N_uOe_JUK^8}|x*twwC#Dw1U&k?;_Yb;y>+0YI)f z22My8^?;F}ARRxx2lpf=L|jc}bwo+IavQD%{{T^2GVUfi{Gs2^Mtg9h>wR`w=nUNJ z-Ka`LxA}&i8W@+hmQYYLHYZo2bu3qUnl=k)u5}3`%#db3Y!`1c)C|!yloIQ4MZroH z6{@HmHW=iy6V)w@0B93$b+cL>B&`6e;i-TmaxN!1Bp=4@NKq+DejpeT%>_vBBp`~j zQl?5Bx=I@nrlc#>Foxx{{@6`ZU?GjDOX4BA>qO{!+=Y3B`bWj74s}tBNByta(iRs2 zFhNeb_30mODc4X0F)B#_ZkdizFt?>!hyp-%?+@%>ESs1e5BtzeYlR3EE-Xt?@rPR> zC?gX1R62=*xlTSMEv=#sx7<-6u5#}kNfoL9y~N6Lkix0dsJi((mCxezH?L={wfZqb7}%z2YBIb2^qi%}CDldM!T_(tBqTTfCqA9QV{2dh=Q zaE@`nR$O=1yfma`Y)kcwy5Fn2{iLYO5M%Ryx_!IkEWRxUu@j>8{{RQ(*hX4|NXvU5@WK%|I=##>u;*K0igFB!PNH#;@;g%Zt;>qthDJOS zmAMWla*l_$Sg6HiLawgJhoqg-owX<`K#tKZ14s%c;%1V)vs#*cN-9WFM_(S01^IAA z<6hlrif;J(OR8LeB`3@A)+KSWIJF0m4cw78++C4Q>dJbKqEitmYhC)NE9NxDRC>AN z?Ru^qyNt|<9~jkanfYr7%S-vBgov-Pk2jZW9M%$`{-A=1S+=E@XSZ>mO(2NdLYb22 zQ7`CdNM=Pn2Aw=2cGeNKQ}Qz(b3{#bqDlesi5}40jR2vdx^Xl}YKmlqtF+}L!w-v4 ziENq_Yi*z^0TR?}x+JaWp|2*YQYwMOH~Dmw<{-x!C`L(FQ75XB<7!snuRtXclFDkW zc98-QCKk21(o*bTO~oDUXq6PS;cc0xQQA=Q9q#}lE0a4`M&GPPPRbQvu#&AoXm)y} z6zV%l8Ps%Ata4dXu)>>6)2V`~qHF{nrrN@$Qe{7=kF;DZHA(8*2~t(GkVw${U{?zC zM8hRIrq1tWs!2V7{C{LIBS;EP)@>7cTFp}{P*Nr*5yz^qGC*2`w(62Z!I_Rc`ceq0 z6x*wsrvom8VA8^fDn9`X)g2o*A+3-OvBt;%MzS$gyH$QuDRt5Vk?4j(g&`YftVPKo zs*#E%x>3_5QLToHV`!YofJ?xtGAM{f%(K%;$|7Z!rC6YcM4+T#l9`UZ1Op_f=pi*q zsx}k=mV?3ubd)M_u+$?}(eF&kLX=^)PL?1}m$#dSSBGrBesO9vpKYm;8&i2e7ZF&0gb2yKdsr7gN+;@{oyR`~r)- zqwIUz&n8o<^}!ia{6CPp=Qu7vuTtLfl{4wn)FnzOJ{p+&3EqBl!w<2Vw>J=v<8N9j z{{SNFw-V({_i;0ed5*hX(4T^=ZcgC-R^fZAlo{?NQEsG}6nEy3YAw|W{{RvnY!vI@ zW7vJX?=BCKL>+A)4?S9RBlIsH^PbdZ`22;p5t70DLr_y_ij6`*ay@}b9B1LZOt@Dxn)9X+I6B<9GL^8pys-$L|Xf4amL8hB|~xW)(Z_C zS3TM*;`UMOACA4@dnx1$y1myeyt`t&)ze`)J8rLINu;Jjsi&xJdsN$nN8%&Go#*dn zOOA4w<7g%aQls%+%qvH89}TtE`*L-;B{&2js-8eEErRwHp)oEQNJJ-Q?v)`7Qt&NPRI zV7Ab`zb(MR9UDmKl^n&r2?@2nTMDKEbWi1wkOS!rx*@eW4cYTe&Bn;5>8k0GS8tZjaMXH)7Ty5ki5J5_a=wgK<3td9J0dFXK z1BXHrgT!=K2lB=$=@Ow&MB9e3&<@?I5(z&u1zAQ*tKg$Ea)&osRcCxnlPSt89!F@aQ))^Zev$MYe83*vM`Pk6GoE)tCY}py`gTtqx`Ld( zPsA8<+@43qse?8u*Ak4D+StM-`(b%AxDnRi{v}P^R<2C5uR+!( zW~mD`wgRcrq^T-OJY}Rs$~0E|bG1VrT2__J%=U!hav(MZ?L zHf)n9vSCn^$V0=9wDI_WBsBPg}Xpq{1@N;R+&imI>F z^x<3H6Cc7l!ne^M1|iQq5<<5vQQVqj6aLYJI^)S$ZFu+^xhI~uR#mI{%OD=W=?oK9 zGaamPjTUU8Yfi9^r8gt=O6nSaAl=unf}oD75_wHf+JJI2R~M%bUV05NCRjwWKHV71kLl}k%xqzhIM@iK)ZpRXk)fXbm#H7n#8FYc9d9I4j4W{Pmy(|GPmrN)dfCR3MWxUz2bTuHG}rilsYQ+ zxJ&d^mCk4_=1ORSf_kKb)_)kzGjg;^rmKG7eU|HWZq00X&HAOQ zX()VW{YCQa~&ty{K&l~6+>%QF5*y*W{k-mCK)y;|m!Eyo`79du5jJ!{1~ zZHL4#GSU{e<3;5@SKQpbP36}!6Y#2c@c#gAnLXW>t{G!r<*Q4ZR+n8VLX@A8*Q9Ab zLfFM*Z~r|!DOzopVC_)EbfW>OtxVE>iqZYpxVXm<7?~qg` z0Y%2#Qe50zo}|cu7AUSa3aX9Wx3vAM!Wz`6K`HCjMhXQ*OBq^KaHnZOwo$#pK$sIG zo`6ad^?J|^P*)8N6?T+TkB0rI^4~I4)6`q6%d=QqDfMqPC|VGd9SXLP2@#=j-UWMl z(D!;7=2jWb`}vB+yV-~HW%6#it4{XCi2GFbdCk0ZoSw0+P~`)1rbnvt$W6B1vZ55H zBxC>t{%Q1!jljiX<1xT@Omwf3$L+3pA0xPJ#n2JkG}JfG@$*Oj0CtagxMwqR&`v8u zM@>a#r%fwqqI5XQRM(Y6OGz1aiWIgWMs?N8YQfyeG`ovv8%ijLg8jv_n_n7J@TM(> zQqz8&ryDs#iz{_#RLLHkqz<|n#y5`aej-eXx|zG)O5XE5>&rooZL}w22AU{Ml;1K; zg7Z}E!Q!XfQn#;D)cD5L1|lc`WMYjAAi2#tUt8T$P-|W$7O?ZmD_PZMauGFaj}6sV zZ?hzg7fT=eIXp{{-aXE9jpo&FU-Vm79oMm6bWtf~75561w@8HoB%-a35Iag2$u65| zZ62LoKVb+CHH@1naVV`eu>R4f?o}Pgm@ZX@a(^RRbE+q;c~v$;bw)uz`br*Zr4`O8 zL1{fE6}WhU3|eQo+yk4aA!Y&x2bMcWFwl>W{Cca}`+qcty@UImRQ$@WNC}YC3NRwS zn>7RQT6Y`L*pG_lhnUgnWfvhoC~;c+W<+N9XVpcjV7^4>`zu{CIYy~#04c`M5J(+D zwTW&sP3phSiT1Qs`^=YHYgosk3Ra}Fd;VfmDG1Uo2-%{^ajhZhA>u8qzCPp{rjSv- zk>5W}WPBitQbaB&%cw)H8dNH?&?Jvl^i4)&4%3QrO-i~+JsU)PgogJ?J5o>OiC9%) z?ZmkWJ5YQl4+I3w)oA@nQbGaz-;^Ga(XT{1wAAPnjN#93_l`(rrzgMN8o~J9(m}mu zwWrf$?bNowT(_u?2+_FL5@X}~i?gyZ;pVXJbKMghzekTA!%p_{46LN0?R;=5MN41iZKL7Tj|ev{$b_InYBt0_YJ=k(Dp;TOSMK*GKd2@$ z(~r8flj-`8e;0r~A|4nvtGT$_pi(P|IA1N~JeI|Z;&|m&O_O9UrJEI+m4!O}L#kGw zB_=^5sZr1kOh`UBZJ{$x;MR{d8q14|+mUN(()nG|i?IIyx5wI_7T_Colae{Uijykk zX42h7vcLd--D7l(z^8bVuc!QENPh_L9(3*}bzgi;7QUE=kBMH}?4R4zc$SOIhpuz2 zJQ}UnBFJcL6l-Wu zYFOX5Y13aA*Lc?nCBi~FFFAAm?nc%+Hky;7fn zo?mX6-Y0@`^)Dv!Ymeqxom#4DTFL8NOZ5o4RDNgN^*WtQyAIJt`;40U`voYp(1Fk` zd+g(m;;{URKdtp{I=K0yZ@d0U9GA#ccUGSjD`A2js+`LAE_$D&VxMO`diu)`Bkn;D zbG%ojzWli!I`1zGHe?ntl+i%U-!4U-oz$ewWimoU#_c5@zAm9&1h!>Dk93HtzuZMl zMAiu^Q71wGnFq8sQOzC+J&;{0a%!hB%Ly!4sT31*xh82YD0OWF9*{99wnal!BiE<1 zQ8j6|HW^{DB7~4Tbuc(}6X`R1zBfT{T|dd$GnKUFPl5^l#n2Juxr zY6DuItOC_RW~D=~w2@Gwxk}Sd2zD5#ku>#2xprdx!A)8%@=~f4meLbBlhA1eODJns zEnwW`Vq8x_OT@fRmt2!~Ymy6|OeR0RL)-s_f(2 z&M&~Z=N?)JO*8Z_vX9fg(pw{GNYPRfoPHJBax1$_nIOEG?3J!PN%LQ?e3^yJWY%%9 z@Ro{bmYKh@pKUoMg5_0nu~Swuu(Y^<8*G3?=b)Tv7*6PA@$rdmuH5P#gsi&%01s)B zm_>1F&3uC758b98f$`ryXBbZ&JNk@%$nZmFRRk?ssWUzi`!(5I_4Y@P!*O-|$BkF! zA9^viIQ;CeT8c5H7ICILr&v{EFDj8iBm$Eq4*jEPveCLPJ!P&M=_o~0f0;P6-d}0w zi|R>`aax%Jq1GesoJ+M+Sg5GoG)UzJ)JEjZUNH`=5v&l3bpQ%=Cs82@oz+gY$D$C5 zdTD^9f;(t5fX<;F)|5Fk^!)+=hMt^mt0w6gB#@E#K;}fPr5LeJ(sqyndO`-N=-dK2 zr(q<63`x-%5{{mU4iPTjj46XnSHbm4`)#(Atstjy`2z+)LQs>)YMwlH#g8v<>e)!# zHBr=l07TRb3KHhw)Jx}aemzq&bJJ&N8;qr|Sdtrjka9^O=u!&*08huZ=3V_AQeALQ z%Q;W>K$A+qG)}=Bu6C)0yHs0N`-YXk8$Bh|P9kMBD#rr|8l*j2i&1l8iI;A$GsP3^ z6iL@hz?qU@S9MDv1`a+NoOPvHq%S)mV-CJekIv`cGhj1X2pC+i6! zsV)>k+HuW$^p37{Ms2HIM!igRbqztD(*!Of27p$b`xtO0&1#JpU?0pzqpUd_lP=Jq zsJA!%DVTMk4kRG29=9e+Awc|Nr$a^b&NM~tP^CH^{UemBaxy|Mc9L2Sy@Y)23U#`o zhgZjbTowf^TDr&()(y zA$wFb>nM6R(S7Zp&aT>!M$|_d>aIDoL>2V(Yf0EZ2jd9_iCaoVR@&KG{{S~x$6DZ5 z1L$ao-LSHvcC4r2);@nkKy?s5^72-HF%tMg39V6!78GtuH$|SE-be|lm7sk zQtvO@A8NjAbqML471bw4L7ube4hd5p(%jw!TC#SyQU;?__(HwJ6pf)nn!)a^Cr3#h z26K;$iEz4zQgGXY=q%KEf`{dlDgePiKEI%+HBrx$c%tQNeY}FslEqUB-V~39zk!rP zT(Gs(M@Dxy;+#GvJ=EY{2WW}VBu=KSql(7gAT z)ebn}DNrV2NdQJNb9qm#7uGCzm8QNcL5AAZkehkSeZG`jm3yZ4_?`;ndfGfv_Kf3) z@2)NGIphj;q1J0yQi%;Xrt}WBG$X%Q{XFdtb))21*0xi&<)V&nit^_!;?BljA8b|< zbSa>-r(kI!Kqv{PyyEG7Z9=4NF34ypaEAgC0#~>nAB0yfEq1BvYx?-06#%S|1pHy9 z#uA@$Nk%qt>2DL-lL6nrYBWdKT_U@Ssx>P~BnkOP!Y8b42Gs|DcubV4MqjF2^j3m& z0$pN8D?d_=y}CO(rcS)H7P)~VIVvwO6SORkEBwTWxVX^VG$0K}c#?>UC`tOHm{?NA zgHxgKgBZRZjFB=#K9C4uEbWsql}aDR%JDArztN6F$EW zj5F?zh?8xiSJ~N1n`U|#kb%vjfxnK8DRSi+QBN@5;s&~Oi8&kSRfvOxiNCzlfTBp5 z9saQkbil5*E;~|fluh5uJ$$e_k|v=z7V31Gea3SmNOi3RM41liA`8YvWYG_GzVGF;BkVK{4a8uz!M{1wIep(0W{ zm=wlBKX`I0si{&>LX#i|n8?l=sZYk*^his!Vo;^-6PI{p^C=s9GgN@8sAc6WZ66Z? zxYbNW5o#+m%}G){X9Jdm_$WW=kUPMoDZN+DHTh2Oqz@{TiEV8YlBcZtKu&S0EsfO~ zr3Cct@qrpDrWo2GZKrC6)Xsf>2o_R|jfiRytbpqPNg|elB8N8DfccmLd6cYBBdThB z($*v7ksTB%M@dcq6Ve8m)D=>(LX@;j84WZ&3`F~?Wssx7$VvpqPSDdOqIt1upZ@@A z79Z;z?S?WDxY8}^fIs% z#jn*-xPzjS0_LQWKf**}ysz#502gyF)Y%gR<65^**sl1_?c}0|*t?|A^dR{t(a67V z%P$~SJ(h686?}V4IYgy@k zRLxx*;A#cBs!D5>=|l36@1m6v^@$kF)S*%G2hnH4wz?Hbg01u@$f4?`oerIZ zEEf)?AQ+OAqC02l}SVZCL*tx?baJB zD3<0h_YXB{!uJCyjf2{^e%7d`&6x&GcgX55`9;~w<>l^Cw|C5K>yA+>oQay;Ug_;ob*j2-Qin z?iwi5$~*v^s`vhDv$`oAUeD^(MFVU5BsZj_%qWcvR&scjIs3xUIPWcdPjf)*L{>bE^9}CGuwnBkUp+Cx{ z1J}gD8d?HWtEPJaLu*gg!ubL&R=c&-x}VX-&u}1 zD_JMZ7%DfO2~%`^7C+?89u6u%QkMQAuh^%h!}SgxZ**28sWRM-16pcbmB91>a# z`;MtSj6_&oz6nEtQ#q_WcW@RsbzG^ZsErJ)&pN46K2xz)etIZa$ru=*_t_z@FsH483eAQbOg-eUOgLLWqocSb^e7zmF+PS6o~>N6s!{aW16j&q7X1oLeM%6} zlVe|)g+Hno{L9K$Q~b~lfa&Sa+00=WbvKuZ(!-{*7YOf}7T2r8zjTC!I8+5fgDXV-dN6eGeHyedP zlaCan_7TUtl`jpPEa(wpnZ$GOAIqzg{i&OFEEiUDI;O*Jz)@Gk)v$JNgBl$dSj$ACMEtDjwJ5$m_ z;THFpt@HtTa9CVqB;PS4l`J{S*w!T89am3FWEkbib-57moS%)GcLLI? zR?X~i(7SoBU0>-~LfrIO00~Ib7`4vw(I(rA2oC26XJ}UY^k+ETOLp>sb;Q@y;P62B0V)IEAbJ2 zxx>YD-LDlN6;ItGOgBWR|jFFW_ZM`x6D0mb(jTPKMQ+juVTyzMFZ3;wFu%%ro$UPPh z?SRM36y3z2Kz{{ne%=`>v(f9-0Q8MZ4t)SGJLg&X#HVYN8ij{K0Gem6(+*#vC_iT?PQ#+6*ly-mA)Ax*Uk zK2TCLi$wuqp-O)-0Ac?C)6~reX&QXuG&+SI=6+hd-(v2U@PP4ASt&Xb{v9IEkNFl^ zlZLkiHGcitLh*~ySS2Z8WnY1SIR>yJ_;C!#g1O|!ah#_kt$CX@mFF2#hE*$1rq~4a zn&mjntt@7HvSlDji$$6V>DI|KfdPN1ebexG3iw&)SjC#t= z@3|xAbs%n@X=Hi#k9hr)LB^OH4Z45=Z`DZ)c}7~OL$f>$7$~2z#y#hBrm39By~k#E zdZkp=JZcfq6v`?n37_~=Ar))PC%%17D^9`kD6D=)H}xdVN4OlTV~r-$PLSXudO=A7 zTFNUI9j~-|s!1zRwEKX*w{F(7Bvof2-D>F_VfMgPLH=r{PKUy9UN^V4_RrqlswXkt ztL%g(H|Pp(D(*E7DN2#+ss8}7EpSD!P%Ogc>^Rh?Ym9g&lsR_2id=fm<978Zdbbx+ zq@gK2$4SM<2g5IK-RaT3Ya{0U=fq(nAr@!uH#cux2qjH z+^ABNTq>OBxS7QsHl5qNR_E=ptl9tpYI?4!E{fj@o#u0ADJ@sC) z%UcMiq~(ly$9+?20U*>GbyK?G+o3AhoqiEj;qz9{+dslc{@O>W5}p;k9`zg%)_XmY5M(g2t`jYsW^ zRXi<9b15*B@09J{d#B<`(mpSw#v4< zRMSMHou{#bBPu3#RTp~CRl3_Ws4qO?f?GmCDpHPs06LiMymHF@k&2ZGlfHt=6D+Qv z3TJWu00!awZ)&0Bk8u^Nr>1jjnBNyupZ!wkrFnsRNBK>7lPJ?{F*cZ{i7q`-o{e5| z%W}-0CY;+?4UmIE{{V->{7^1W<@~FbUuKPTtz#HPs3jH*9*hR4sGCWWOp*roNE)pD zA~zwBxX3?q%@{fbaN}Gu%L^4F6?y98sU;<`{Cc9b=GdcTkf{Z^Bhn7FwEhs4eR$S_ zNmdQ*U)hHwdxOkv*k%)8U3FbX)z(~=l_HXbozpJVIVmM0@jU>Cb9p;FO~1V{)OaB{ zw;agwE5UDQ)3o(>fD5tDWuC(LpV`(i`ipHNSq~-MS!`t)cO~T2QwAP#sUlM2`|;4-dyA znAVDaly?FPR*_zcuKm>e$FOm|y5PF2%Vw<;;GY z8g{#pIVPagV1lJPDHvDj!zq3K`x1QHK=X8hZ|p5^EMoVLv7Vl4vx&XSS!M1%k+fvO zo4m9M37upYs>-Z~KF)7$Z-5z`h}G}Q1Xc;U*SS$B&28nlty13TLnQ0K=|~A0_Ji=Y@!MMLV4ziwQ<0kT`*Yk*iZSF**5o(Uu*}y^ zf>h?yrrGr~?F%NQ0KgjE5^lb|y0k9CX?-l)7&5!w5=c_?{{V#9>+>+v-%y^8B08u^huhMgeBUSmI4q?RRNQm7C(_{d1=($n1YBwnwO}1mCH=7(e|Q=w6_br& zA1n7bD#xg)s=eGDsi<{NxV4g{9};4rHD2niWvpy5&?y7PSW&85cN&KFQ#U0f_{j8$ ziz|I9srifY=WPl^(9tU)x+31po3&7Rsuw*8Oh|@TRatAOk=fo>irTbUt?C<)jP zgrdO%y|z(iR}p*D?4z9W@?bggg-l z_%XNni5pd^n1*E)L|~uh1XbDnp?|eUHeDo z&k60N+}wsc7xfPnAEkTumSwTFSXnRGtr}_imn)a6t#(#D82*1uCwvodDu zg+LN~29f;_aLhX_vC%-?0~!bHUxD1)FmG})&U(4Tr=J+QIcT!2l%vd2PpC+qppNl# zh;14y&UkmrRLNh3Rc*qirEC1^I?iKHltbN$m2b2FHiC%} zq?OhNan(N9!rFsd;W*<>6RvdvFlALh%|=ix+LY`h(HOMjyM*+H%vDA(=#LmwE+BP9ZCjmH>|IE8zMk2FkV;r2hagZXXdiCfT$>PUBSiV>C3c zY)M#|8mP$2q*(1kPKp}vke)goHlw;x&0rr;bs~CwB5o{rc+o;XBx5>-9XU^qX>spW zXdnURma~X_jz3{4XAyj4I;gW>S4m8zr1{EqQ3K-@r?!P#LxBl1ReYrbq@kka*w~2P z$Xc3$M78h`C_1P8uuuhLb5!Nmq;qki=xA7{Ay4>W4HaJo=(A1fYMzpp#vdho#?cCD z?y^9hhoFI|SI%uwt2Y&Oke^}W55O1BZBd(bYgCUwItW-gq?QUJ7GF`+tFK6Lhg8Zo zr*%pqT1!z5g1ACcQL^iR5%h-`+NNaajnQwY2r62NR5bz#4Jao#AaxY)v;vahRQktW zS7V|C^NL9&b55Njq4h*j7SvXtXa>EZHQS=+w1_GyQld!^EBdF|8LA_3NF?@zt~RKp z#HbZ_@cBn7on}^Zp-xBwsI1dfYa&SPtZ*-=i{56XJBH;2co~RU zH7l*uqJ{pT5|W}%%ll!k71uU}1tz2sB@?fBc7m&bEe_OFG@U1}QSy!&QGANmcdVx~ zPT+gWa)bi9uo<<|ibcJ|c*oUBY+!1KGs@RyWF(?Tc^OJLo^vXU>AAM#(zdvfA^FJ$ zLI?VSnlOZE{{UXyPf|4MHp9N+AK2Ao-##p8sRBGiaLAwb5pC0&X=|EO4>lGRp+OTG z!XE9*tb=bv!QN^`CGD)c6?Dd|g-P`>zkPtd9bvYl5?wnnhTTy z~Qx$^$#MvrN}T>7|V}s;Owr4%pyv4gCjpLWAlp@ z#kErrPK3BsEe!l(Fx#UUIa?_mUcV^%Ykfj;E24>Oom$;+g#bJ|#O;-}0*1T0CCLwV zN}Oq9bf=^oAUlu~T~(zuw;OuYSKGkFhTM8p4XFUW7>Z(863C6{Xi&_TLFh}i)YdH$4WndLg6O_O5d$!7RpWzbz@1<575%&{a zd|^ev5TEqv5pue!(QO~3Bzsi#38*9@a_V48lz*_7^aV*}Qv{g_eh~X5q8dnQ@RGOJ z7F2#cA~x{)m4?!-6hN%i4m9d!nU5IyGK+gE6R$;cY&MXf0hYS|0DK-M1qNbTYbsb- zJBUw#`a^G!A|izOq?N!^s1%tQ&+8}|?*Jq1;~=KK`9bRLEvO`R5DcxVowbkxn(CWP zQ%;>pO#c8d{{UPDBcW2QnX6?zGoe0jG`jtChFmpW1!ek0_5F|~QDJy(?|+9w(h(zc zN3p_-G_dM5Dfo1VX$c03LKjsFX|?O+J)w%JHo>YhLppePKoUHusA4E`8d9|Ed%zM& zMKcU#8dK8D2}De?(MU%EJfx>OO&a*X5=%uT1-eqw(vc!lIFGoTkfvjXiAPAm)5|GK z_QYJ*4F-x<-I^o6(x`#upT-dDlv5y68`w3dR(p@JwzN-W4Ql3o2?P6L>B1!+P^rA$ zGA)(%*W*uqwv@EV+LZvUBxDT4X?HUUm5mrL>n(bc7;w) zk8p0GCGBR98g`=dU9l|FCvrl>!87pji5RDURZq%78>JGzhJEH-D|rn(e?yN`SK97q zb-Rj`gsDhK64T2qVtUv%Qa37KbgZov5__egW~ZI`Ic>5SNV(K{bl^!SPU(OL)-N&9 ztpF_A>PpB|b8eK*L=!HN@o_>5B$e&2I=r$8ROQ_39D7P+^-I1!csE^6k~LB5*v<)I zqjXI_s!;QF85GvKl>sFuY0_N)lo1!HM#d;qX-iv7!IaE918Af@#cF(SnQ_;)Z)I+C zHfu{pKg^dTFxu)VlTu%K*9#3GOcFZ9+R|%#dwuc&MU7wMayd+qh6<7^)8L@)LH7XR zE17vqv0GAHJq^7gW9puf4=*jocz~rF+a5&2R+5VFZ+pBaVsfo5T5J`JR=c_SYRhv@ zNj+o~p`g}L9Q>{(L!kjkf3Rzj{~*UyKu0yhN|o} z&jISHmY@=ptce1okVM31xvWppPFdi$P7%nCLd;6;r9_LHI~#O=5C%peByB>_EusbM{4K!M`_`qxxL{8$5(bvOHvWJR}DXGh@x4u8gY&@5ZE7GsEEH_u7*y~CPMKAYtf3#=TB z1cOzv+j1zR)1g$M7p0!9qo9t2g1rY(90ygxrY9G^p+`n{XaZ(Fk)z>_7#E$nv_su; znYf)%N?P-NX>E%e$)d;Q6moj)^*|s@%YQ);^N~e2Z>5j3??>@6y%PoLR@l|nf&zt4-L>GeG#@5)ql{gBz1qvEq=Dp6yVH!86= zUs6K5CrJBJmOZwfh$?78)C-%OS7M?}Bii#%8?j9nj?n}Ofg=pd7zqO98Cz-p0G6qa zf<_e(6hU0vnxS;`;3fShwDkHym=LneY8EI&m?a}bsL$&P2_^cil}G+nJ&%Mb%^)V= z9;8&Yg4p;&Xx6t?uCApiZJ?u|(0t)fbyA>+l2&T3Lu|`*wCNor(IvK|xfCk#PfivV z!)j7>R{sD3VNs=mwW>q7g&HLSO4lj&-TNUaYZb{NMoFJT3Iok`rF%nexZM_8 zwOgOLx}8+pEvaOG;fQPe^`)+|I1kc~wXvF0Ml_$$fYu_qmZ8cjwpB+|*^=qp8&;Kg z5MYaJq*F(}Gf-F3TA1BO4MMfAXq-2~nBF$WBXW&RJ<{apON=#Cr1}gFqy(#}j|um) zoN`>1@v*QD4H{|G{*}D&d?p^&-|8<2uT_t`es{vN%yHZUkF4l&fk$fYO|q0BAtpc~ zKu_i)M*Dm0PEx{etiE~Pe}z$dm)&~{vGn%9Z5}EWaWz(H$M-*0#dQ|6))D%&k_b>9 zGmlqgcom)KA5Z}SV-M#O+f~-&f>g9`DXzUbm+luOTq;&SnNhoNCQ)?6V6AN842hrx zkmd6icQThs;z{_S6*VNFN^>A<-XxsF91;+*v#)dFYM!txs!lgitIN{=0OuP3^zRY- zJylXn`4+mx5lDN?K>{#$_AiTf+Iq(xPt zxB}@xEu{I5x(EhX9yL~IGBH+_)i~T!$Cn)T@QOM*bvW5@Aju|wT_CGFH%#&5s~ppn zGanE9#a`#O@3$+t9lC}3n?CnnN=b1@Cs|8Lj-oVcJbVzdRpjjFEXiL;c9H^@`Ip+t z*KWqD>YcPDr#2D^K_+tjkeQ4x&;=P|eUOCJ&M80b7lG(-+_83NsFH(kc!l{$GD?S! zawF(JElNunxo~r}E2Ulswc04@X-32#B_I)}KquZKZe-l;Rihth#`q(@ao1Z7yB4aG zh}>H;kxazX57xpD0r&UnC<6Nkzydb=_ zCR3;H*d)?RO+L>I!Q-ZuE5oMld2qqNg@def>5L+%znt|NHU`aTN-}& z`)~+nIU2M70Awr#1di1jvZJTQ4r$tiduoi(6eMM*c;z)hk*XTolAnw?*U=9kSvta= zK@*(g=>aLN!KYMK#Q;sgg{e9PCu|@^M<*w`tYj-i_V(PZRI}De`1npVyixRz!D4xh zWGU6<4JaU{LaBTD6ff5RJixkSenv9e(^(4NtbdXd)yD-byG~RI zoTd9ex+JlsR&yZGseMm$S+^y$CRY}n|rQ4p1nrn;iLty?na46 zwpHu<26;yZY)?WK2g*lSwPW;Z%CcYeB&0vTI^#PTqAhjzB z#~aEuP?g7#GAg7RcB;i4Fqrh&NI)awD6JVO(Q9KGG%nR3Dyv<7Yk6hY6{%X3RC@ej zryA0#LO6p)h?({3M2Y>-rwvo$Lqr9{=w@b5NN|t}>s{$UoR{$Gd_GX_;YHCWnpfdm zXP0s<#hZ3BX>z^CR+OpGld%&rnK2_{ho0oop;Y-7H?hVubgtLQT5l113&U`VDs0c1 zZS?BdlswoCB&2QJ(lno5u-03b$l>k4Hh`yik7U|jC%q-^J_T0hX~;QV ze|D(7<(J2<*BXUxTdP@+_$5669|)q)JLGj-YPY*AZM~yIV`u<*BudxjGM>FmECgI- zX-f)qTZo@|m|HYRl^Rl|q><|RWzrw2kCQ~F!)jQg$P(m>o<)QstjQk;lg2m~s2sdM zl9d;ytKYaU$fO`>XE>U9)j(RtHla4kXPi#g73Tf~c%PgVXz$DlHz^qrk{3kY_b}Q_ zH|@ptn!{{2blqPz%G%~C8Cg?Gth-b-R^V+XQ?+goG>eA|Wf^q$3IJ*PDPQkY-gh&m zxVLK!Tw_11kwo(U0PO}hL2BmTY?P~B>(aRTfxDt4H;+X~NZs5_lh!icBT~otZa=)O zE4lW~`q)}J`+wZjVfNuzs`d%)y(3@K+*p^u8bLU*<+=~S2Zc1ZC*%GLnvQh|8J*Kj z3$A|n&F{2w*l_axSoozUw`_G+yn#*1NDF97#4D`-00<`u7U>WiuH)N^s?jf4qZV9Y zDnove+oqD3m&P;~LK?tUILoI<@{=Vkx5OES)RDQh{dDgK$^QUOkpTYyQWORbEe&bZ z&Om7uc;r`kv=vI+2aL0hQoM^cA7parRGq4;srrg7iu2<|Y0*kb2@+exepC;clyjAGn=A|j zxPvJg1pfd6?dDIGU52n0+k>l@Uwjko{{W1=wP2K3Zywp(qGFvXH%qF*O0b`v7~VNzB8i|7H7I^B55FxF|`KAjp}OLSe-hI0RkxL)(t2Tg<#ug4r@#E;!{&d8PjV84?st0J=6E zCPXOoB|ZWq@=l5fwE2)+UdCISjmPCFQDLBC9;N+tdQz8Cj*^f_3H+dT1GH6SxpS7y z1~rUm04Vdyxyma=LoF_CfI$H0HH1vy0{S~+RS()lW?7NZ*r>vl`qw7%hPzOaH4{G4 zfUSX^N)C!f_VKo`7apm}&a&j!TrC)7l4_=*OLUaf?T<3t03)F5tWQ2IcC7u9mjd^7 zDX`?E9ADDHEGadaMPp#(6)cf95JdJ&PDIWFyFLw4Sk@k=qC;COuUXR5cQRT-QYfaG zAkR`y#K57n>ZT+i!8QDekkn;Yiw-AaTG^zuRwAnNa6up?w#>tG_{3Gqom~{RH*9S+ z@j&t$)AjSR(7JYl1M5#&6Veq@gmXYON!*tpwehAf<;JDdrm!TI5iz+&T6BhPt^*j0 z##lPyMOK;4VPBeGDy|i;DPO2sh)L`x6x*rG%Gt5@VhH{y_mA;6@3pk-CB4WB8yIXO{@b6qoku| zcPfU`r__)EDk;`+U&CeDad3VLb3cf6wYqFF9D4Al-ma>g-BD3KF?M>(nC8_c5R%9{ z#FzX&I>*kHtzmKSOLn9Hr}q^7yuHA3ekVhgS>Cl;C@!pr?d{dnpIL2@xDpVR0WlsX z<{y20OB0heQf@D4q0_~0+PsOgwsnFku=*L*)D^uMgwoGjORM5(gi zKfVib|kd%0Ty&ruL@x06I85K)9lD#Ry{@EQ@Yvq zB;Xp$-M=1Bx(A-V*Dh34MT!)@@#`d`a3sny89w=MGY;;?!i~#sA%bVMm zNb*uQCdusfqzZ3PAb$~<9sDD*UIzH0tgIgKRN7Q!7KH^VH3*SDX{dC8pLP`MG0#Mf zWLC2>Do{lA*R%;G$VIlz%g^i9v#;usXVX|3XN2=D9#E9ut=m8q36SqNXWc{#LbeOV z3gk~+6umik7$6-N9UTfkptx0%3r`XC?+)9IQ1XQ4Sn?b7JB!hMi->!-t<;^0BZ1DxRl(MR1CeKK!wpN)ZQV(5X<&S~Im8z&@TsKezoaU}5c3KwwFHhskeEGeDm>*TOze`{fHoBPL1SCkb@bUr`#;)24lTLYK(pnsX^0n#aIs zn0tykVvWTCuxsH8YUhGIrJ$*rWZ7Bp5c6Ur=#b|CHBw`Rcy{r~SB7YrV3tPTRzU98 zu!~M35@a=ZMN!UXOnNt}yG}0qc=6RPI^EYT!YXb{QWB*t#~n~QOppNvEMMp&cHX5_ zqJt+ZOov9Of9^|!I7bZ50&z!qTNH^A6G^$JtwW3n~Q{Fn1RwS~#k|$gI?~ z)lZ>^LW6oDQjwHV95q@@MA0`jR6m+Z8h$XURj9CaM2=fjR7qFJbcVK7IM)J;+@WnE zYgbOX${T+~>qSvBieahlSO9dKpmw}fG|=P`%IX2t^l92+5b0=9hB8n!+-Nl=^v}a^ zh3^SI(~u)wL^`^WKla2ccwc)iK`Jg3eVs`Iw8SCO0FWYZ>XWr~bh9Ncrd_6?tT^Yi zu7(s=y1G>vZymLmlql29Bm%t!g`ds@GGz;{fUSt5qI`g*C-DbJLx^7nkc_EuYi(au zfw=rUelXyo=eCYV9a|nr)DW({zjP_3GUpOjijF0=C?7BzzC9!AiYHMcoHuTC%_^UX zpGX8D&;v0-~0u{{R#L*iT;w4rtpbqJ8WYOB{~b z{(8n($nhJy%eLH;z3Mg{SV9#q1es69G7eM3VsrNv4c;0nE-y1H2GHYA#aVaTH`vFu zu4tmD&JZ>uKEp;vlOf7XaYbz*F?${qV@JD(%)^8bhZJiPJ4~Pf* zm#a7~D#}|Qb&dvKB0{}hcJ|x$h2&llV}vozU&MIw^+O|tW;EF)Elt{vyp5?Y8kd$| zLe7pMT}Me6$#{1?y}(~K?;gjHNZbfCAv>S2n;_3>>XaQiY5P|wKI47A@&~o9R=ja% zAt9=#snUw9Yb&EZt*-fMf6T>g6Uiz{+gf^w%_l?Re^a;*IJVE&HhA=Ri%(Ejo-^3l zar66b>EKg9%gI^{j*)ZrPl}BCg1v5~Z79f2V2MWrMyaP@}E)IN_sJdg@BKCLdd7Vq?n>#56$25`EVJ zT=ykf>S0lJO-mMwORu3!s!9_c5O8vhk`RzrRW?;AY1i=iKu$F)vF-sRmX>`$WC84c zvBMfa)kL(%s_EHNYEr;Z)DMKg_J8@{)gKqm)XH zU3w&q#@}4nnpe1e^S#7}}AuUk<0JA?Bh?-?N3j#W%q8fQ6K9LtR z>RR4Ef>BY>&R$=fMN4$1ZK0}EQrnJ)Z08Yk-fE_8?Fv7px9!{Ik%;*&LXeKwfR6>a zL<6@^lsE2#SF%*S_9ItAV)reFuDM$ic12YiYC0#>bpWJ&NQ<8d$TVDSJ}RHiG3C~~ zhl1&I6~@`T^ef3Xc1;a%=|xOl=hl^bf#k&PkX6BC{Pu{yz;sw;*YiqXDp*XW zluP3eCR1&ar6A}v6ON|jLvVVsi;XBzkV=Hfmy8h8L^SNEPj@<7y-Xt2aBOb$WJ^ z$|e5*KoI@5Z8Hydm@A)5s}8-QfZf_iMJP#7Q7TG|k6=V%w-7kgh3UI2j7_6RSmYd4 zlv}DS%u}tDis~9#qT}^!vUfh6(vr;KgA2e#TjZfNsQrn<<18O9#rYqP?ltxFi%0cq zcEwTFBB4NfDl-s!~jfR(!B1M$Mu*Iz!Ir zqK{Rss^3d?rFz|1z^J9FNki?P;KvLB+9HM9qBRMB?HQ~SfcbX1{{WrhBQM17^oEwx zlX?iLsmCgPP2RGYSS4suPSq(hB11_`#d|HsWs$yut@d2X7LQ(qRs0UDi_sk&SH)zJ z=1;_kHI%7giqvgOTI!Mq;}$?ig3BdmsXH_` zQW^m3F7xRT)9Mt2O`;sT+BT3^<^xS4Dc~y=usc#t=YBv!ie^r_Lj^nHnoNTK09;&9 zXn+&_u!nR`mr=>2zgOK{3nlHK_;!Hi4k+6%zg4XpQkSG99RN`_Tul%)IvS&J&q900L%k@5vvbfIy43m| zMuR~(L!M}ex3)Wx4MoW)Wdo{&Hrgp@tjQ@ga=kCfwCFm*B{T|V&l)I-S9Ym*NUlL& zx;WWXgE8!C1g8p=cdBv6KB%C|4HwXr^7tN;A4upcgfDa&o{+7J<-U@dvgJ^UJ7pu6 z8pkwlos=oX8T=9qL|^7D?OVE627&QT+u6%2sL)A6@3`hki!GF77S-DqP@T-Q*Ik`bHVYQ28UFzA#rVXt@wJM$D{qev@?yaJ_bGdh7fc*Q#_b*T_cz?XTLk+)-OMMus`;L@aBotDyu zSz#W%LiA~nCRC%RXr60ypjlPq^d-*FDOtuo(KGH!^llY4w`u5}AhgEPCJ&5ETH@SA zT&Wk_)J98U)TvVc0NdWW-(;$GuG-*I8#7r?Q!k7j{BtOw?fFH~kP=d-+3&WiZHp+b zr>&(9I?Z0vlAUA-(jLazv6WHW+VV@iq?)d-{jhPZjfx9PDx^RN2@(k&07uygNI~H& z8%;u-+`Y%x-98Ccv@qG;$vb49@I+0UU+&RL=3t0LQPp!blxFt+Ip<5Q8%jeBRj>2T zQak$(7?r;}NI>!EcehnPc<0es5yppkyqnijw zY#Br>EYWB)I8_KrW@e$G=^aCox1*{@heIDEIjB+zkRZx&^9@36%m~VyiHXC+dXNtu zNZph9M}dM6#Ly#TsLdn7aGzCBj%pH}R6a23Va=*MrU@t-P6>~qh`0u;Nk}zpck@QXR#5(73DV^720K&UNg{tS2IT($mQ;2X)VKju zwe0P`RUW&XSN{M6V=B7&*6RoILVS4EmFnt>7FrWp$(WaE!PX$E#%ATLZA!5WLx?UF zcBECh$yriC=zJpUYmIY&Snf+c8!ZA(UvQP$RO*~;peM|eDNJ#A`iz98mhTnCMm4@hyMUC7QA-;O7@TYunuSCMR9{$uva>-K1_fld^+`t zlLboJr;KQVy6)}MT!b3H0v)l}#R64AQ?)yczs1%*Bv(YXp?0f9;~=X|6_$m{>W4H! z5Ry+`{t)zb&XCcfr9)&hHuh}aD}Vt_ZXfo6bG)sK&T{y-+ZrCK=@ddn%_q4>q+Kyw zRDc~RpjaO%?uGLoNsKW$TAvK6c<|w6vG3g{6Zj7!^$*bw+dZDScFWz(HCZ;JqpT02iCW66V!0C#8IZ$cgJd zf)<8KD@}s&eSNxHRTAnQ({|LIsRz1`Kt6EowXBmLNKv^g#1MSPRfW$Ox!Nimr>_AI zB>9pI<@FG!LRx50%)V@bp&MbIogPO*>y7$~OwXK3> zS0OJCq)Sbslnt{zC>^$`)m65njk^dvVRlZdfI{E4k(Jw@+sD{v2=h)P+RKY4l8 z!j}o)Mc3q*HL1&jDJ?A8X=}eyz^f_~TT^5ZfJFLh1M+@xcNC`|YTuY@Rj;>Qx3V_Q z%4D6Z3EC^*4M&%d{{Ygfo?p2iyR+{{-L`kjmi8Q>#ERIn@x`@EC(^UqikfA6Mu5CY zaE&D}4@f^7juw{fh-&X2#b3sJ?Zr&>53p}{OuCJ(A=+1! zJq*=M6aA2GOlr~aMREBHtzJi%T+?%j3d?Q_Qg!uJxO{g;VVC}80@-kH1`wT3YU))! zK)048!V7z+*Qt<$urCdIj^&4ZbXG&0-y5v9wNBc%H?V2nSi8w_xu7hv-p-Cl%aS&6 zE=B;5ak4tZtgif@(79yh{ZfVajV>)yexz$Osi>vL0a30ri3_Mu0%0i%(^H`_7l;tD zebNemisnY@TVl&|M;{d;GH!IaIbd@D<2!R$4gonoOG-A@+yEPQRX#k%f8m^6ZutY{ z8zUQ8@L1jpwl?;!_pS+&QfpJHXy~a*o!_-jy(tA5lPCrl>Q&BnF@sGL?SS#kUG}Aw zJx{zS`2>$mG>c9@5a$}6ij9}#EsRiVO6V`xr`pFX<t}sbMimqBcJB7pv z65To$0ZJ9!!}N-eHtuD%8HO8jA1VEdOYM(h?|Br>xV#0Bb*Gx@7X#z`3x+VOW*>ss zsj;kf#Lz`GBM3?-AtJSrBBiUUlyx!X8~lydM&Q0uT<0%-<1tnkOCc033~NV%Z8MHc zVwn{Q6(Op2f!w+1W%7%@2a6LzLdLnzaRX`^RYUu=_nT|O?bXy)>hbFUps*GRoR9B~ zPRhzFSn7JOG-Y!0(7_{Y&xcGha;H4J`_a!nW$vF=aY z4jaT2wHyoW)s~lWq(|wpY~hr)Q!qBIrV3^^c&oHeUEMa$Dmzz|1!->lt;W`hMm&7f z7RGH2+ZuN9cXNGPrAv8A>Y}r{WSN=Iyl3|p?~&CjX~p6hZ5U`B6K%+xfL2|lr*NT` z!Pr8&=?V9Om-YxyuQbct-s^3W;>px}`T^rOkCtNX0(dYtQ{aYC-5BGHO*aLp4(B;z zWU$DQG!%+DYpw`R7h%c1y^vSNv^1}5m8d()3 zI_tzqb!7_ZhWhS?FRob_pz2fDn$F{lt@C4n_U3y));&I5K*x;870n*6$-W4DbklzNv^6# zQlr{p2Xktq8Z@deZJG>j+fjPCtTL96RQswR1b{w0;7KHF2CL|p`J5U$D%R$1RIJW# zbuCl6MY5em6zu|JpfsNVk3ew0)+)IR&F)(60}LUI6V){~D?`=QC`wRPw@3sXClR*r zm$^#hF2fq8W+`Z-!z==^_QES_5|WhY2voEai%d+|+AQwse{tm1Ds4}*W4BjkH-r4w zK@`j@L+}zXYhD@(0jzVMK$vB@bj>fSt#wLs)Nqh>At0Ru{3Q{LjJ?%X7s%KnPj#OP zeuj}MKpvvRweJ$KThMe>xi8D#@JLJTJxv{9-00>E3vJ2OArq3iwU+__)^e2P3uD892ci~O zTs84h!vpuimgo~){riq=O{8m1$N$7cwFAw~f}6b0ue zeZ-#Rnw4Q6ZS2#Rc}Jh{o*cyTJB5bG;wmdLM=2k14K+yAipr&+Dkeg4`HlYok={9i z)jLOTX|?|VF0SH6+K10$FUp^ke?%W|y|88;z)bP$c>Rm<+r1pq z>aKpBr_#^bRXtO1Cnoa-b!FpTAI7RO{I>k1eum3VQj!wc{$tL#q7vIk8mUJ>J~EBh zEtHDRCrI-^(O=ZEY&DziWosHMQl}l){{UyB+>T9WYTQ&C57rc$=0>UR?@$s|iq2r=qz*mpA<%4|NS&D)#f@GCgwyiR;f!IE@6 z6lgdlcrs0Drg{lzKN!-T9TiMS3JSbB=`AXhtxnZXq%vbpwbfd`%GZ|0wJTKmdB4>S zxs*y-41V;P4_SW!I7x1+sAD#p=sG&&yLE+v7tB8dZ%=2wt3iT zV#Sa+LQwmX0Qt3qxK%jqG)ik`Igu40Dxp$3%gv{ZGE+r6c>W`hI#7hvon_RY?uFa5 z(4n6=Dec9$my$14Od`b5xjOQILqSG!(?Jqek&@t{?qp~+D&l>m`$hNTT+3B>mLAQaP~RHo7bfDk-9n%3dPkvmi1ikpb!EkKfXP%eJ6yFWkCSJbz2wL40*tzpJ} zEt^O^HPpmi+;gex8DXMfuJk;zN{jTaHmT|6I&0w-x3W#z){1GeGr=LQG(3vZkLns+ z)DFAJ{qW%qPPRsplO-98N-1?~pLEZ_z|LU_S|Ml|7gtJx)zlL><4NeAprRDjJ^pD{ z@0Ezq!_#QB;pVcjOnPZroleo@+u=9| z`UD11C3RDdZSf+E7 zwEeNowG>E>R|1>cmLmJ1UY%OP%=QpZLktUWQHJETCXX9jDp&siyURrX0E=<`@{aug zB{%(B;Jh3wk=B%od!o=)ITEhlNcdy-B%Ii1P>m`5tn!uDZ}i)uR(^@0%tzj z%d`sn2Q@nuRyU%z^Vpl;mhp-0F&Sl&AaQhmui|gn3PpU8+*HD^hAH-y2?Kaw$?skpvN_ zlnBylo!TgxX4P}yJM*{d23)M;425#NP$zP~raa-0T0(j@PtZY=K+A?u^-dtCN+%5ka3u21W3T@d4*-fWaWv~X&@BVanAvA z$1mnIynBMWayPeLNWbwfE#q$D`JI8!=2NO` zrmLn>h-$8t;pKUUK$;dlLkk0u`0h<$%ezO12M(Sf`4#5;z9M{d#wkTVy&xN@ zLXkQX0GThC+k ziAIGRxN4A9%?;|>;Q;)6W1)yz-R1@Xwe`b*5)y9#JS~>pGc07 zXr!QVqCi!!l{p|JJRzs(6r?1D<1|QAyiY-WuXKk zHW+hBBSR1uE-Jxhz?4?{7ZjjNMqP}e25G$2lGYGXwuasmQ{59ame+%WVQ6kh_~eekQmf7P%QaNTSlH}Q*Wv|rX6WQjck=StXls7 zX1>@-YV&NVW$P;2nOUf9z@(%BsoX@QB1R0{c8?D(4Wf^{!#d(J%2XB9r?ftD#rcNa zh}`ZiFBi_~LuttIO0+4OmgY>Av>ih?(QST489L&Wmcn+v2!k5;x($rm8dNb;jc9FZR}ev2UKJ< zC_2UO8#HiF3)!I#75!=p7RjdfV>P3ie{!yV4dhE@`xVden`+wq79#yi6%`30P7tFh zOTY;IX|uO7S;HenrJ;Uk@SQp8-DCxwq9T5%%v7L>%zU7Vph(pZqj4)zNIQ>!hhr5fm$fc!h}_T2Yb((rdLQ9s!39Uad2%W~*! z!O-dqCc34}+G>%P8edVj1f~=HvG56qWuuZE+9ZW24ztn)kPii5ZPB9nsxLN!r&wr4 z6EC}4YN%3|Db@y{^pBj%mDnzh(h$9}NFE*I=X?;mwOZQ%6Fq0z8w;iA9aF(C1cTNS ztx+&Aki1P=L!@r)(j7fg%m-=)?Py4J1K0bZj)`&G9TI-7t-{vC{#iqACaPWtKs7}w zeQHkC8OyvahN$Mm0jer_#Q5kTnoy|-*iv@j`a*(0{{VUr;-xKOKoY6^BjkAtU~vf^ zbw|y3Owg6Si50ghgwopR-L9xtMp{Caqx48I8+)3!wHu^x0TpkImQzNGmA17Nw#tJ^ za;04bNYzyC0+o-UhU~KJxNhk6Z<I z93;o9llHN&IBw7>@5g+R#XLuktI)HeqT=cg^U{#FR=Ia4qK-3UbxGHDIU+sh$ z5ndEo1*f>2`odgvP0@G}xx`CQND}(P0y0Wg?@E>YZj~m5kPMQP{yIgr#2t$kbQ;{wj%P!xj)rDEVeq*0e> z>~5+OP@f$?!uq(-BsLZ8%aU?;8hJK_ z(lF`rg$b=g;2<1-8n}?wlnPUjcWV=BHnrQG@K1xj*z-(sg>{IebhyCWpi{1qFLB3T z6KmqB5ce(%$Iy)moZiMgu3a;%mY(^O)9ynK0ZT@-llfJtGlKCp$E%emon}ME$oej} zy}bKe$LQ^}O{#?h%88kN{UX9;prXy~ai?`t(aoF@XtrXtZ#6)M!dfywK-W){A}IsS z5jS?;*#%K{4|BF+Jkfr#yi&paPpE zr7H+bkvR}R@Pu4EzeF{>Mn0*+=Ff0!3`*eBmD>VZREbcOzY`BwTM$s$DR+u4LA~mG zfTOhX4PC~Dv(%~%sD~20dY=OVW@n-5t(-Fq=f_eysE)_J{DR$1v{ThTP^l0aO4LT+ zB`)z94p_7R6~+80$m+X5n4H6!`A3*5eHT>qH4CK86H%Bk*=}c>LD5J-hqJO3tx9z` z#|*2tw9~JkmmJko)XeLtF+Gp5EgGu6ZzkD3RU@mZ{{T;GC0ZyCFV{$wB7s%Is`Xhu z;}jKj379@;&$EZrx4sSXDB#KmrUyuaZ$+Rxst}M4(0cbUq&uBOziNNO3Zh2hx1~DI zth-0aw>cOvH3Pno{7nR*UXB@0)*Ye>LJA&~bWHxRpobw0ZMB8{M9;!e)XRuR?45`b z5LgrnHTt08&}!K4<`CmsLK8*4f~=$4^o^mD;kUfcNVwvg=?g2*0qzv*a)K0`dZ=h* zm232gi}qF}1BGe&g;O`A7VmW-Mr5sD{1j#vW7byhYVXQZ1KTCGma6$opr)@YZf<>7ufKU<)h5&!_oGSqH>Q9kTfp7FYhDrjza}g@2WA%9PZWm*1v@ zKzWhrM)6FTmVt6dU^E2fw8CDc#n7QAO4F4jXui1Lo>HZoaiH#^Ns2d=y! z?eg9cv8$#=l!Z5LV`Gl`iT?l+jN}Yh&Hb&YqeVzr^|u(y=H%Toh-pznlD)_0T#6sI zw^RKC9|Zb#s$!&XLF}?MC&nvRd8wu+T^8NN`+H@NVD#0&FvUxlCTdaYqGn<(n zbBzGi4{>`GOx)u@p9Xk(rx&!*Wc-@v`DWQwwaP0>wwn6Hb@H|eDNn^xi%q@-k+*!u z^eoQvm`ix`6`=nB@w^dsPs?xBva1FwZKlO6ZGeW6Opuq~OrI|XxgSkhw z!K?vb-2i2+CDKxtUmak`8%S-R zwycyP#L}TIR0NfIQsRn7gd1xdW=-vFUUaI8$X?t!A8uI>wmd(#nj34kVf;TxyS7%t zd0!`1Hb14}q=#s%^VBY-V99Qd$Q=(@o?|1oX6G8bs-wN@gS2SD`&Hb1wXT;N_pv{E zcM9jq{aTtyk^q+KX^coJ>3UN&?KEt59m1l}l3Bv$&T{A~Azfr>>-_N}GKVQ3>0uwJ{-R(xC5UQkR4{ zPbTC$^|se!w7+n#t#K(~mYP$$%E$pA0A@^GvDQos_hd9^S;ku;_rKbNZjssm7hC?r zzSFqhAG42gPdiYzb;v2wiikjsv+XsXB)j-*4p~7{%HjPajH9oy3<{` z!j&aTO0_0Ov}#OjR?Ichi_JF|_Zb)I(NL~inC4u~kv=_lDw?QsM2*T4Crveqjn&KN zeFw^}c=#D^%2QQsKE}S@j^(PQ%`=)G>3FRKoT;jP3Qw)D?jmcJgSm*y%h4GT$E%g2 z4}~*UX^z$eW$)wPUOk7~>ixC*WObT0Xwqo1?7EF92v`HtQ!K3={8biP3uf5G?#h2K z_<1VcPVP*^{>8qL4GC2Xi`d(uW!tsc-&N(3x0K-|%=w8V_@41ezsWjT3y+G|c=sM< ztU-Vx4A(SpHnwTq^x8l(8XbO-7W;x#!|6a3Q4=o6JzrW+dDA`JG$?9ZWK&~U;6KQ- z+M7ShWdr8Y+#Zs)wu9v$n0Pjc5jv>@mE=Fu)SE@Q zVe7p}0m*6`N$zFd1tmzw6&q5#vRRp+rZrWH3UrO@ab2aUCU7g>8Y)*>$4KfmL9cQA z%x@Jg;u!To7TR;R;_66~iXIZ|BCU|Vsaj7Rz%{K7cU2H*=^Uwa0jVTu5p4vkb;QAe zN?tI-TVa;&_Y`eug#{oH(n5XW#bIn3+LfQ>as}?7PIW6sExqkpc3JHwd32vDi0*qq zppv4TMLsFM&u{N#x2Ehiss8{@w~&Gme58XStvbN+Jh;(HT1V~2xb;sy3x?3y=-F_j zEyVA~k%IQaKp`f;q$q7nOiGiV%J^tVKol>B#c`<>6HA>qD zwQJ?_-A^~0OVtu3Y9NqE@764lVVe^G3YKyb&i%xszDw+DJ42h;u4%^SA@T6iK5i|9 z^+&qyMubmK>{pTo96`qEEKL|0nxV+RzP%v0 z>>wT}Lv?WD(K+@nwMR8wTQ5>H%~R4PmV?Mf^-rYKM4>_pXtkwLF#GcO(O%Em=k23? z_SIc|&-o_vWTeP4dUly^|Eu60ULc|}TfMru)4nMx`>W=S#n zD`#XQXlT#{`S+2^x@k+vsU=>o+56k=J`=@!h3=~LL+jsj-_%=hLf`>STBsaTN>VrU z{Y6i4)hQ8!ce6F^W3C#{m@(A+XuU7loB;PCo3t5 z`}I_-sI6GtQ0kpfoXF~~!atm=f3@yWm$-bSHI93UG;tLq)bwApj{A<`v0E}3Z2iE% z05lcRQqSER*e|_0+?xfAI-&YK0YH6Z9#26!Biz*v|fLrQ;4bK`r2}nB?X`* zNA}0*OYoA`wF~p@ypk3jK}AQ}=QXBfcs*LB(m^BpVFQlKTGS`cEw}ifEB4RK>6kb* zW}gI~++h;nC8GL1S!{e!d;Z;diG>ea2Co40+SoA?>=gAz{{XtJM~cnAZ`{C{0mt_x ztGDuIBcIS_Uk+JpjOn-wm{3Ic?&M|sK7s2U7#aGaKiyWy_Md|JfGINL+ma_K15RO1 z!p}-kbaE&itx~3gggJdKARaup&!ADKGBDe57bC%Zc?GpPr((mye6q<;tAf{4bdr?8 z6EWEF_W;s}C*>eMDo$~RFUz=9)`IIpiPi_$T355tBq#$ukZe{mw4vv{jOrIsKFqz| zMS)+woHq zKj&T}vSIh@g?4Fes$rT}r$~yHTy@#De5pc7CVwca;rQj~xNoG29;)TZIi4`ty`ka6 zjTagI;je68vRs1YZnHd!XKEi?0JNjE3vop~2T4jc8_navdGgC?)~W#HU#fbc(waqu zPehj1CG!%9iD7y0Q;fK8nrZJ=tHY&b7UfA(oYH&z!~rd<$>@n~+ecEHnw)m%lCM(U zsp==N!0v(6=#FK>Xo%d$tJ0vOLb6YNr~9BsTR*KAezmJ}b}N*V{{SMt{3pld3i?+j zKC!6C*z1%A>Jf|wUE0o5fR zO?pTvWpe%@BLX_wLUah0U9cbuRmn2}m4iR@Z8vsX z+DMtxqGbj|C{VJoId-l@dfJ`L64`7`00~YGZNUmP)?wvidFQL!wC+=_Qb8VYq-igN zHtVvx9xa2#M=X;*7e`!E!*=a72eF(%UP|Y5Q;=J+skVCkNVis^gFg=l5H#0RYfC^t zm3gBQ@@jq%ab5$k&__gWC!%?;6iS*())bV{GAn?5Q7f;qdT0~Y8!SaOY96S) zp~~ zPAV9xK}A(E(=@FL5+II}G2du{xY^_IOiPs2Tq$xCm3!*AuK4f_xS~|ML(Zx}1eyN; zD4vJ6NE+tcNZyEut|(Nk;U95)gY7prIralfO^{k{^yzBrYkWIts2RH1FGw&6aL?d}76PJuKmXh*~lJ;}m3t(I=Vw-)XW zX&ws#&Ejq`_YR6-M+A*as&YiQNk1sR!a-HZTnPqxbP|pwB4>qJ5>)i<;U6xP zP~%k^op(Tv6LLmKA(2?1S?JPMSu)ZfhJ&55Y1AGlTTevp}Dekl)L!E zQ<+65&2Y)d^`4J88B&VAiHDOwSq!6fXPKoI1LN{QxdZ*|_M$AYxQkJOlo}H&Q z%_G3+75m9eRhr$1{+ioFvn{C~ghAfURj$#RB^4!-gn&Xib?FtWn~hbL$*D-io`_0G zkorY$BXvb>$J@B2b+*Y&vVhukC*e4wWb%|NP{ra};51GRErOgSJEhuDR-YnDwcFy6 zvMg+@q#?ghsQyqUX8_5S4HYU*EI$1J)k$w}p3J#x+*R}RxV@bgd`68yc{Y7;C5vUc zm>I2VaLamtoRFPQ!Ya8Q;XuRf!RvYQ@J>m_+X&q*p9NrfZa?jt+C{RO!|m@4Z<4uE zjin~>?hhrWZms4j6be=?6i!XLbOX_3Zt2eC?RbJUb zl%%+)oGMo0nH?u6{deN_R{sD@*lF^G_}$zasm<%IK_hu2tvugLTW#g+wVRkft*P{? zCTArj)XW`w7>K(Rl!Puh_SoN>r6DR;*y@?-)-#T z)Q0L?+|7>4+n^~a1;*)QpadpnNI=majdX5dp{PoZ0ChJk#|xrUsQp9Ni*i!HAgM%E zwgHz|wee^l?}A5)&pG88X#W5zEAsoJTfa}8@w+b#j|(GUr{Ge9IJENR`OQC(mE0f&A-l{oN#qXe?=_tMb4I+Ne~2Sc%w!v1C+Ls z6(Ow-Xo*DIO3SWBslcUp$ZMb)h!W-6RT*IIXpK~~sR2ak*nJ|uIn`O>Zit4p?G@wN z8EXkC=V%fl_Q7XVh3WF%nd~qo(NyQF@Sxi91(PfW3f~sPsV8>+*pnx?_5kHEC?;bO;!# zC{mELmeidT*#s#+FkzNX9jH1ZA7(~{IXrEJvzcC+Mb4~I+S^bsB7SE8AdoyFht9c7 zmQaxxtBSMnem={oM)O96KAykvKM~^L+hw+visR{!fRv$C4zF){%*U8*u=%Xz2c@$-*4Jtr3j*@0+2l^w<$qRzi1jLIPtPL`jc_a-q~%~t)=q10L+1!wvMCc z15GiKH79Ksw* zhaR~mjR}e8pSms2Ew|O#F)v!-RZ+Mh-F-CAL#Tp(t_`$mGa0~UE)JB?DyqKQv+h*B zxp>IlaHgq6NoW-vpq`Nx^s?MKr=rtsuWf8&+RaLKaPAIjOeu8p1M?LpU)vMbn5ML9 z6<;aJ9`a~Zn#VY@irj|E+Dc>wfgo!WaoR8{RQY*dp-zn@o}SpkN)R*}>8Os?0370V zNUX;2+f;9)E7M2>6rQs2fUaZ>3QF4D(mX1*=F7WP_x}Jvw>PNLdh zmfEEuCPe85z4QtaTYPjvF7YAs5oX~IB3+64QEvq5;^7JV)yrS(j`&gFBuRIT@#DoW2o*b@v( zA#qkqOLn!IE3Nx_X^0j`krUoLCTIJg>mQM3`H}viAU@%=Kd8m;t)Opp=GEzmYE{@n z85WY%j2+Wpx!mrGhw$D4yyO)vR9;>E9l2`USxB1XHIeXvY_J9HtNJP4Pu*6=-H?j8 zO6z5xigY}i)%Boh`MZ50F7F{DjT%;$#=CiIY&Ser$}m;AQC9M*YbsS!Q0XOaER`se zuB60qZB0tkqKT2VjnR09!70*yG0-T!aTHtB(U?%_3IPm+G{U-s%*#;@mTN>jlK`B2 zM~^RARO*^SZMIf5tG-m9PMx%lvQhlU;37uX66gAzpjAwtBy4AEH7UV41}T6g%3LKW zyWZRKy_P(?w2eTu-Y|S|MSi=m8TVVk2)an9x1PYJ3&n3&-9W zxs0PgI)UZ-LQs1f&Ui=L$3ERI{^|Jr`?ZeCLv^%Q+3Ico0I8|8UD8cD{Ypo*^%78~ z$Ro;iiJK{*XU;qpeao#cE<;7Ts6o^yos(ADt8z@Lw!q4%G3$-az54|j02ZiSs#~<} zkTe@xQbdG8Oz`I#E29`$13~Jx;1v}Vd6k~Nm1T;GcY@idLZqZ94G1H+j@yDt7U|$I zDzYn`MP5a8t+~~?ajkvD7wTP7lvZcdsZ%7u@1uI^fz+`ZV>oV0n!H67I%sXokSg!B zC()x}C#hQ1*G|G_C2Z_vm}{d|ZvOx&1UhwhkWRvGxaS=QZM=O{ZkN?J>0LADTa%Oi zG*KZ_^wB@!mg5qwnk#ngkFT1Gj@E0ZZkI;+Bq~FdRX(HZQr7B<+;v2$N?9{1Q9VgM zFfps$Qlw;KVr2kb9{sYZ3O?U{^<|`&NVJd#{{T~n=45_j2f;splXa{A0Qi?Uyt>&| zO>;;SE;^!qhH;)`kLHlIEIIs;6Q}Lfr6RFHT7`ozpn~w46%)u_cB!*@aY=2VKP<+P zY;RRbjVLl=f9WNANgB8W$B&qQd``dlN+!_%0IE>pl7T^6NY|m#0f~xU>ETPSY~I>) z&UntOSdJTyRNrn_cM>US+D71z73N!tl#tqcsOTcWZyXk}ndcxhDlNqKH`gN8%ooUi zV(qusuiLu;_N9!`-S4#Y`6njVw%V>2QrIlfD2>IceZ#RbD(Yhe=RL}wBI6Vuh==W7 zyzL)jEwIe`o4?GR+LPq0PUm-}w$rKCoei|m{MruDn_5{qXHX4R=awfHrQ6tITQ1djhl&a15ij_Meb*Ffd#B`qF;8N^J(o=_4d1M%sEuDw1pvL z%=Fi6Y4?)DX%zw0dGg#`WM-?iYNofU^8RDU{Z~@+=&5ldY}Kyg_0w3P&osABspzj3 za6@X%qvlt=>?iHo-NurSKjeyJ+p&5e9dFeI2Bf3S)lvZ>T9}B*$E(khzVUuC z>09+T2W(@_Hs5lexjmxUOI_`M+U-gyZT9qy88knY^(J@ym zl{*Q3<#Jr9S~4wwCgP0b1X(cHb0H6RMW6Ca?#jl)s!v(1M)O+!Tbb1+*I=_DPCVks z4N}vjOcOl{i0u|@>$XD5p9M(AW2Ly1HM;qfU0Zak_0XvNpjmEIosF$V;*vOZrTxpf+{dk3QmyVguYSUD$dc|RtG0yC z`-$cx;*=*5u)aU7*C+mM#F_dHE4H1;h-|Is0)bn2&IFfla<1hi%7=>FSz6qgTF#`B zXVk@E#-(WhZ3*1tj4@Yg*4$e^%1=Wn&RWhT>}>o>sb=yt^p2`QUENv*HZ^J0Zv>&h zk>k}64pj7*i*PT-K)6cw}O5;hS$em<5t5He`%W9IP4OA47_-PNYA=_Q_u8FUApLv`)v}cv+MzR6P5%rV~;%cQm zwj&Bod*-r1Ml;QD&}j6d&> zBm~yxy;ARlTjw8feAUMMK+h&*RgdRaN_sc{0QG9}8>cGX`-fwLM79Yr8JW8F;Uo=H z_=`=`ijz;otC06GkkGF`wD8UWTJku@h$))0+Nsw1-W3&e$@J@la(%@T2&EpACuiQv{Y%efABaiFr;5P(!e_X>nlB}6B(H|{?G zV%KpnPkfe{XFtqw<{MekDQX`Q^?}M}P;wD8qR7hwe7uw^wlFimHbc zNh&C06F-tfUmZNnIykDaw}t0cgo>#5Th4^d%%qrcUzJktLF+cus^ z)}P(~06HZp{{Z8(nRErF&-OwNa(eYjXB)Jq%8c0em(J+{Daz-rzsgMErsi$|>WlvX zZQ6Mw_I>7aqk>AwlBr5lVKDFemgmhk{?@hfN}E@_{&7md{{S+hQ7IM>d&58O3EkqB zIF+dIOUi$Fyw*xL-wi0ublhrnO^aJ=)k&OGj=9`5}2yZ1yR!AE|EzhU= z;zH9aBh3nGTtsoUpeb>QXTIXIHd#e2)!Oe+OImi-GOm)4i(CwpHnmj?*08&xF68S! zxeRWM)>SR`5VTEhh7vmX0yK_2veKZT8SSyaIlWXIzjGfi+n;@^>eRm_6sy&zPj4L} z4(8@z;;lAtMNWyWxpMYwzQB1(#rOHQJCr_6H}wyyAjZSs zyr<-BxdiT|r2$<(mJFab%=*=Ju`{I+{VyYAi`pHdqEvthKA&BC!|ajed|_uwCj%bk z%!b73YH9JCEy3VH0;-$TPM$FMB{utU;GRl9c08sS6ttL*FHF!ndUo)gez5B#a%939 zHAYDHFUwU^{{Ydf43pDk7$3SHGDtchoo>Rg{{XFAtybVBmd--@k3+}d2o9UgH0Wap zV|l+SR)D0_TNI^r2}a+-3S(zJC|O$;uKtsx35Hm zzg_uRptRN7sJ9NXpGZv3xE?6jp|>KTHZ|AG;%C;Pr3YEca1-X7r-i%;D_N0ci8B6~ zNB+D3kseSdjb|#@rti6*L@1S2fqlKe`q!cK8IRbbTFCA@DUJ*p-?lu5t^$<%kKy?8j_=gdrsGN%r zGT`kXAX+d=t4WZ<4h%u~5+=Z-3DBs;}4t^fw$LO;3+{<9a0P_Dqu z;N-QZ=e`wnAusU#At}(SVkUnGLo@Jyl{x;}oBZW`_cU-*y8i&t+G!v5P$7TU;r{?C z`~9^)`N~b#^2Y@_6h{xR*geMC%kdC2xybi?p%UITIsP=?UAbF^rhiApK?pr&;Qqs; zF#PNz_(Cn5ZhCy_DDNwAO5}g(p3e{*2k_P!ImRF03D)4JAN@kKf7KoykS_7Vv+zsb z>ww*ri~Ojehn#;IDAJpkxH6kZ_|Bn9@k8Vp{xCD|p(CO;EOhNZGAh3FpSH)SE3Fs) z7P@41dy7h9zS=5VlG|939%TWj=wK;kVdxZ$O^|ge5HFOl3^yZIDUaK)7JeSQyc3M7 z{G>ZqWdIsL<-dK?AxyWDrS58aA!PgvLELR8qMf#@oAFXlgsxa86mq?bkn;{Vw{o62 zY;h~P;&sfoQBPFDuD0@4eqwm#VaJ41vG}+^pB98re;z3!HfG*k&bh6w{Clf zg;%9#?y$Jl(l68#Iceq$!|yGXQ1G81`*^6qxvL$deeNvxO=PrPoW{`0b5r*F%gbai zY1|+b1U^)iYLFuL$8#?2fEPB`0()mTHSz8nhmAm~G#+%I{VESTfgGaLiP`N+(NOR4 z^Zwo{thuj+`AgeJDCAr_%-FM-rf-fY71JpCw6)ZWQ4Lm6K9CaqJD^PqPfm&fK!N&K z+g{>hIW|4Fc25pz>O5L}it;ZdTohP*P@+ zKQtJ?0*;}oP1dl&r!)c#=hi-RYLv&2Qc||q(cD5n8q2nj!H2~@+k}&?wzdcSFa#so z(Hj)CDLC!8Yvlcc7%`;>)dNKa!atUE?+wz4#-loVa5nj6DB>j;{*)~QOo@$kfr(v3 zk6PA{c08|?`a@(p%@Wy~f~MV4r3Dukx4yG5um~Mj-uUuD6KSZ^`KuoZ&JxzImiFy+ zNLH&V23uz+a6)|2U#dT$VQm>IbAo}=iaK3ViLgSt_KvaCOZL`JCwe^5*)>i^4@8x@ z(x|6uXFkvbuc|&H;E)#zI>gOLQi=J)4yY*j;|Vsm>(H_aR7Y|~ki+JxeX$Cyjnp`z zo#_X}%fc85;ThbGDg7mpxv}+uC4CWXs7B3GQzUK)=OnxcpdSdMhXP z#rF931@2q)H#<$I{Fj5(lw>OvnEbEr$PWv#xM?d-)w$0#u!h#H%+ZR zKN6;)vW_9W{Q|IlO7^?$2iPT^%Xj6gZQq|W+Qp<&X8CNMM)I;3)Ojd<&iosHv(?ouuF&c!-I|!I4U)>d zB(%&Qoa5B|LDuq6M&MROoSr~x`h{K@E)`Ko^+J}xY=UIYpbef zE;!qzYf+|A+dYyRAHpp5MChscZKP;ZOM1qdb9!E9&p-H`Ke8<_XsEaDbHxe~hGjWd z8QoKN#cKrxYafxqgt(GC6ci+4X3F+h&!nn$O3FFGYsQIBoO_1LbGxE1d7ZkB?RaH` z)BgZcLkVGOZ3D^(M_EoJ&Cz+ORn}r#9*xi_rJb!=VNg)?0VIy{B*SlEaM2YJ4LYgO z?PH#Om~o~6o;~O9Y;}BtUh{Pcdy%cRO|^1u$7WEZZgHg~CVh-opM$>lceUUTB{s~~ zKib&%s_!)pvdY^PMb_>yuhb^O3I(p@Rgs3d@87B&I~y;WV%w_0>l z6Way-mu#0i8|zQ=TUu6}abahk$ztan5ZIV!fAx&KR0Mb(RNeA}B zj*^C`Nuy6ikKBJV;MkbK@w?q9#2ax>F=VW_(pI*YSNW8Z`Uw;GM3?n9wY1Qv_h90C z_f(4cB)w0$K307|zd>LrM9ESt6bMmK_{ta=cMstUj^gnik9X@SDE|ODXU;mhm#mf= zht;R7yCo9U*ZtVx&b$1fILI;)5T>dQJ3v<2#-Tw( zPGHBwGVp2_(s76Y&p;*L@vTW81aHRPEQas9>R-ScN7{M(>mj{frgr$&> z_Xhz`GSv3?!@5$aIXa+jD5bbO1?1&o)1b3J+i_BO1+f`A1EzEw?)Fsau@wHDR+@5;2 z=9?BZM4&6H^e(Lb0EMu7{9`)j-2VXjYc@pP(jJ5SR=L~W-VBSkN_NINr~36##9U!( zs-wlMOx9eA6o;=>C)ahu-bKmF=@|Yt-#M6^K6jAH{G&j%%+2-_3jvDVpuCR0Dhu6e zS8}@7SCJ!B;+HR(k4fXL%y5QzFFW1cd#^4&DE`Fd_Kii$bE>AFS9$^X!`B>fmnZWD zLICB=A1VDvePh)Auj(%sRotrOpqW_`?J{(Udn_HTx6At!T$fiD2D<)~R`x)o`6y|H zI(i2WU)d6Fxd3`sW5kZHHAL=J)YTGRc&whp>94{8+~*xsgDu#>ph6Hd0$L85L(#@7 z)imOSC!{)4C2x)<*07^Fc*jUeqA_Ww9a>v$C2LlI2tyizmQcY`o_rgME?37IdZCpD z)hRb46IK|v$ZA$;$~p8lz_q7UEHT~-TP(IkzCvDKF0pyEz>U2Y7nAN$bC0PX)^N2$ zZOwF@yqa`}9fk7>G7jL1^+il!Rd#fg27NS%&rb+P(3r&nlaG@&XLUq(G!~RX#?m`P zW#ed7;}tIURTSIZo7!|S4$^li!=OR$^=f2-1eAX%=?q*Q#YkDkLsY}|lY_C4-f3#8 zk{(jsN{qzhl4Jpcaj_8Po^rRj?xO2|+J78k_WKpBDW%T0lxU6$OpB-_;T0+*N(Nsh`)nwK63% zf}kNb*yp(Fn!e;QNAitSp2B!ml7uv+(ghx>-Km^eEpe@ARBV=Y&nHzPEZ8j#8d?IK zBzH0zAEZpd3qVy1i`Tg5msED@dO;$Drd<#vtahaa$2rS=Mbe$LNh;YRpqT3p?$Ir+ zNUPRePfFU>x_~x=xJ+yNU}w%7)f;VW=a2xZhdiy!bd*h0(oxh3inRiipc#SNS$B%v z{!CJfN8{XO?r<&&5>-`HT`L~1tWi_0l4N3~E4MnUTY?c2Hih@>hgTd|(emk8C#rsN z@=KP{Igc3meAJ+`v{do}wef;r3NroVH9NF)&83k!NOgj?iYjcfIK3tB2@Sc^Ua*SM zOWM0cQo@#Hr03Ab%_-Re68KlA{?t4jP=jdI5z~@xw8C;5hwp*y#a@q>`6XJv0I5l` z(-Jz$bNNPXXr@n_7!MO0Y=5Vy+`3eliGfM2K*&|Wwx}Lujh?$PoIEK2FN66%EuN~?1wIg#* zM!98`F4G}R+vq98?fC5|OH|bDj*@LX^!&y?nrR-2wy}yPgZovTeYJkl_rJKe2y^c& z4CM)`a^YGnsJNwdPM2FOGVbt%l%(njC$vR-?TNvsTB&(rkTRpE z_UY&TDBtbZ_hsg9wdO6)Tt$KLrK6E8xvk;)$LX%M?^3GKlTb1J755OTW}OePp-zyb zc}XH*>!BBTIIc)})PzlsFS@jd!=P)a_|W+LO({-^ulCvO%iVX}-<5eIka1fZ4kI(Y z-EUlx$u%u1p2tIUt0uY1bnM=;sVF73(>WvwpBn^r@U?^7Gzx_@_ftsY3wIWE*Tb*( z)mP3}VyMNk%&QixQbl$rbGTZr8+A|WE!9p@xKpR)Ds0Q)5VujWucLY^g|xC5L3dgq z7_*MPdrr1}T>nK!0t_VAuhOxCesY!CN`+2ZlYwouElIzP#shif@ zdZ<@sqGY%OfwzmLOIbmfJ9IH)h6o$#I*_P$`74`>vA&k1CQX~(vOh|ZOGUcRNu?c@ zodK$5liEp7QU`OaJ3afP&}b8u7?|SJ>U`2+r5UIAyhICCBwIwHv(i0rt!k2)C2u(` zjgF-KAe&HkiYUyD(<6wIx^??@V~fXr-v06$txu;=^K2IxB3gc!5hyP(2$_T6hT#(L zvXS5OE@JsjvT}QoSl8BG)9~D5GRuEFt#<~0EGL`Wx)O@##I%?o#Fitf6Fz#Wd3o5S zGpReJAr^3{eg!UaTXA!UYAO@aE};PH-9m9Bz+5Py!4Z=3QiIvgvEEwuai39GFpCR@ z%G0R+y_C3xrhgydy)<^go1n7k67$+;{H#tcuT1Gkr?Oxk( zgH6*Ou02$ty<`-&s%Kt((1y?n>LL=u8u^?63fG+Ev0E^g9*PZfmp;&0=;~B!kjqVi zeLd2Yr}2%ii15JLIl*}=xt-W+folrqi{00~v{9P-ZJy*Ds&{a0u;k=PmuS&)H}P3V zwY^uJEbXo`bMJRWR~TK3I_FHO6}2gTqL!`0wY^pNPAU*a`^T5Rp+fe2S%|Q+jmKN(LsdI4JS4{Y}QQo`cCi z@@_{qc5kDzt1X+MBgIfXg4U{Vs;Z?I>K;3us9BoUwH?HgbtIi)4>y*Q>RPy~{{R-_ zQCUP9(2s6+??tlHZtX}Kj^#sKf*=BB@;rzs3s(6|&R!j0Qz1kW0%I~8^*c>J zUva7ZJsVTrzUo(GkBNz#E;PvMyP~D%^8z-}r6uf^r}Ae;Lw-oN+Gz&%X?>BpfL369S zTwgUcm8&apY9&WqWGC1~N?8ltRj)wUyqhbh3tyiZ{IN&p*hj?+=ou-cj`1nLAPpfDs88bk3>htkcZPc_M{gY*#FAf-_sRaBtBu{ZfMk!i8|Hs@ut zlskEJ4JMPKKCnH!Zjwq4fgO1ug5P(ER!fk&#xrW9#4PQGYb%hD@B(F)0N1CCPBi+q zs*9!2l94Z44I3?xiEYw;B##(1ji6B`KuXy`{{R^i@zxz_p+ZWQwcsj^D%<%%my8(4 z&?ZQh=Z*Kf)iPZO^Ky>^tusgUi@G+rQ$ONEa*-V(9$Rm!x@>3V>Qi#${+8YH4!r_# zY!nv@<=VRV;uPXsTaMJ#tN50(RH>DBB*uwv16p>sqQ!3KCa*n8vrn}T7&XhD@|?fi zDMQ%zxBEBsih#7u2JdrP=xBhLmgFTauf#}r#V@$`rrz1HgR9n?*j!U@FXWlyNPs^L zYyKMkA#eMh^Cd4c`}U>d3JSm0W1iTt^#1@}|0PKi(;5%7y0sL?`LM?$Oq{_s=JOz00% zXA)GRw%at(Hx~t{?k7_XxlF(kUG)_mdUt``QLGxG?IEWDMDtL#$ynD<2yUB^O(;V@ z#SFO+*HsK?$j&|kMCb_SoEfC_9zU`LX%$7iDkv0?Q{xFP3FeIwx|*daN<6!aayF2- z=BFDAyEQ{~q^Mc)B|wqZaxo=l2#_d?wt81sUu|1v51iw+$KIi7bu0iKSt(bVU4*(t z*C}-)Ew?klLSU?8xSo#lR)fzx6MK75`3s#e}v1&~}S`^Fq$?`}%k)*vo2dwC*?&wFncK>nWIp zkcB-IWieMv^%!w!3lbfEP#fi-N3tT54&LZY!E^)EbpH4s+$e^{MWgz?B9*x=1MmbG z`Lk6x<3+UGTQW1y_k>j`Y-+V+Zc4#3&`uu?O$`y7HFZ0I-y!qwIC-w3VeU!_D}`!g zpHpz`VFlH4O==R#g6&k2Ca#11aBCj8@)Ev+_EJ&n5{Z$h)(NVOa1>5mqa&sSZm3!( zAVjAG$07z+r-s9JWyGZ)uPZ0=q{=0&`f8!BqoT(vqp5zI)noaDk1n5C!mc4SUACNb zQJVby!j(=RNcYf<9cp~@iuePZwgvfLvIlh5?v0&Cfoxu*%DYAO#VLs~N zi7Rc@)OAt`I~Cwz<~$T5*x}JBKc<(_Hrp!y0BU2xBnuis4r_u2sVjA|+%}RMJ9~)% z`=ED#0&W|Ee7+?r=_ynZq9$~ijz6Mp&e0kry@*WFEjmXSG)`~OE&G$S zg(x5mK%TMEXq3}JiYqD-P;>?vbCE5Up(QF-lPQ2@IBJN$1YW;L0p%N${jtROK*(hE z0R0Rl%&JSKs@Ce5NRp5V2Uu#9E4%2i6s^7iKu*!qNa#8)2h|hb@heUK*I8-1Q8!mZ zaBXc>FtnvXNm$$8q3|#ypcHE!ThTIjW@C8boSNUn{D)k+>~`h~INLO+Qh%6$WGE_E zKlrW8Y*GBf<2Z!6Wj_Tl8NIa~RPf+VTcY;;%XV%-$awP4%D63H6nS1Jc%(&GreWt3 zy0(a@D{JhDt8Yb(I>^VGJM-AMxh;uw4|H>|5-H-euH5$x_=}ydXe>Hvu7%IPFSm{* zCl=#ON~Z54fc zx%;niPrrL@n})d$+2!Kfm_Jn8JUz|$^~R3nTVc4?R3(%*QA+w4b?R4&r1LG6%xWY+ zBhbB>?dC_fu6_5_0SFay98aiqG}4v*BjKvfc~(}>kuX~OM7;d21EK!iRcmuc3UZ&f zenhsNBeu+Jbxm`w(8a4 z%|EdD73E%8#cuIeW(nIuKWb**s$T6>B^m>66t`%LW4@slPUZlN3_vC-3r0RPMZAmjy2UwW{U*3BBY%Yb1~+o};u!I;iFvAc|orP$*7^sE>*&Gpg`3L7GrBhGaxF7%)F93o0@e z(GPBNC$vV%qm;B@Q5w^Nr3v>DA9QF`#hN^(WnNi{gWfBbbyiD6a*D=jba8W6@Fsf3 zU&*qef`h8qIDQ}9crF?xB^_EyQLye(vuafoV|7@0j)H1 z$B&pF4~XmdDNF80YmU}s((W1UXEL*~MN#^iOK<4He@Aa^xi-j0Kmud{9soi(C)s~0 zWR1JHcoKtOH9*dHyB#Y`ZD}DRJ>mNX!u{Id^fo?AqAuU94Y-?LRX{~8bs<;)>{* z)zgrzz|MM!K@4pZ4|P#8*jgvzd-y?(eN9-Y2GGEb@IZJLN&&@Li2{yKK-Tt z07=y(p>rA;_0k7qq&kUGSqFsQ*53ur?^krGT&gP^F1jW4MN$;< zOH@p%Y%)pI{{V>2A||*mD%)#n*qfp=rD8x0x_C!I5_4BoWc@VLpwql7!6Zl$dh3YV za*AK}lZB z*>Pk|C@wT+nfUELD1ph^i+J}9RgTA%m$)_Ks)Fzuy05yOBjxhmkItW%j>Yo?)4C~D zgDd`4pX5JuX!xQwEE|+J^9p~h3Yt<+Z}mj|D6<=;>Xod!B9%)+)I@FEl?hfxjz=NX zrMZJYgjb|+s<%Q8)I(nI+I9ID<3I`N2a-Wnz2;WBgQR@dfDltOm5SV(Gn|Av<5e5B zj@2XXkB!RLK?69cdp2m2?$&u`PJ(gAkW|!G=`~YV>O3`ogSAsJ!A7m&1iyqle$`~xsTO$H^48?2Qj;Ol64b0o#DpB!riS6Ps#_9r#>t0jyUB>1YxSh>~6&b~f z*9QzO9R(G7OPQs0btmGA{{X1a(-~3GhgaSAME?L}9190PmMvs=%zsh?QFE@T^0@ig z*|hmmK7u|HJH-_Zq)g}#5@9LgXtqqweWQswEd5PKfrV8hwHr|~?Lvc1B&l7#uhFVwwOHULWTi?ckGDVOWp124AN_+@$jW4EFS#U2UK(xoGxK~Rg>iLP)GtTb;2Ld! zdUu)k9m{u0AL;gImqOW2-_$8;+>tK9FiT~f?NKvetk~^RZ8)w+m(`}>mR(=FRJdkR z>eir{_yeGaZS70PsHRp3{VGPoFruEZCk>MVi@M9a_@|!FjZslH zfEi53M9ansYd}y_-8w_6TT?X400j9`2C{{MqMX7?9PP@UoeC2GiEGe7JE)j!RY+UT zUrR_FQr-Y2f5ZSV+>*3)M#pGQfGGW$^QDr`mqO}-mOt{)K~kb3e2W^axXvvs?iwhI zne%=@kv#JjqG;(Hm@3pLkEB;`GA@%qwH!wfiVa+ukLA>~Go@`f6E8L_*^fO}=C%xxXaSq=28{ z4tNxpj;Sk6{{Y2WeRbR+h(2{Ear}abtTKDN1ZZG5u4a9`yZ{4 znc8&vL3tmLsJuV@LaGb!-F&LrBmM-l`5*I2 zPyNP{MlE|tQ5)NzhiHE1>`dPwd+7=dq9u}HEYZ6IoVV2UQbp$GbF z4mp%U2h|LtrK+rT6x6IK_uo)UZKWir&%g*%T26yhHO1Q_6?Td5wQ)@?l8qOxLbwZ6 zJc6mLOrKJ@;n$%2gXa>ES%!dKs&`pwWIJM6+<8{6nWSQv1-VuWeOriSwrP@AU0ksh`H7cRPg8ZU+^8z; zcULX$E^2YMZYXJfWCd(d5+p+HV5X%)=CUF>#~Jl}C|6rv@c1|E>&IVbS{&Dhu6Wxu z`Aq{AJVBYX`xTjm%P&wHsozqGsw?#nrs`W*0SN>U0Ei5BQs!&Y*NQNIZ_=$9-aBg+ zJ~^Oyl4qKbf3|>rz&;vspT&OQy})pdgEOq)en!6WMT43s7iG;Wt!Y~n*NS>w=y8P9 zI?|KsGF(t9X+db2)IrlSviek1ko$BJx3B2^+W4!)`(S;D>3!pSX}No&<_>o_b~%+b zcW;kumPAt2KHXKZ*D2Rgbf~Qkr69P`+|oc$8kJ1Q4E*VaQ5`DdPui!f_>*s~9X{=? zZ%R{Icz zw6r7%l7y%6v)u^GMO9xFAz^TBHPD|w)B6>3S3PmPUN1NMv!(mB{<5mR>r$aFQmVG0 zRZgly#cL)aOhB5cm&{tq6sxh@N*mq=gQ@tQuta1Iin~0!ovKxA^pPN$9)nmlrAC(2 z(E?EkQ2+rcPv&7PdMT`;J;!YKJN?3*-D#y=U2RH&T4^G4{2;4nog;%u(H+uI|$ zWgl<9c-QLicU=hVjgRn4p?j)n&7E*aZ*Qw z$vT+JvPbhu)~qTa3CZ9W75z(Y*%cAxJ#;ZCg?Flo8;~}v1x#z2iFt_*gneSdA~jWj z>MMEn8|)hE?@q-_Z@gVy#<D^n8`e_p#yeAK0@eY;LqKoAGt~+?nw4$$m zcJ~EAWTmcJ>%tpAR)Js%fjHcFzZGLz9N@fZ+w%n5COS^z= z4g)F7c=e0MM=KSpHnXbp^`)HGvhR@StBZ$tzdiRCn6+B-6%WzXJc4%x5N3J}BEJST z*ADk(K9IBrU5FYYgjL7Mb&u?8?fZ{l7HfWQoKdw;K}3M(q~w#lwwaPa=#kvTKa+Bn zwEaD}sXA!44&Qe8LeH+f(W6~dH2awS&|S!!ynAQ%wRjY`{je&JkU2JuL^j{2HrGW) zv`cZ)OIYd9M28>a@nWL-i;q<2pz{9!ii_nuuIG~6{Vl02wQ1Ck8h9!Nwp!W_tnMzY zM5*`Hs+?IfDp50;Gu9xl@^akCxy4@aI2deV4PZj9ucelKHq;V8l)OHX11h;&08>qR zy)Ca*AyXgWo`iIOp3T7?#}Jvz_Cmu*sdl2IGSee+ioc6Z52rBNYGn?JWU2Ko1njp(E2J@Qt zaXfmdNx^L^N^(2MnEs~oeOYX$qe&t}_li4Am(p)`j912(anS8(tcVqTLrtZc3J@8l zU<5SM28lq@dPL^tv9t;itaF@P?MX$|6|^=g2QD?PhTCB(E^h7w0VJWSO4`-ZbWz{+ zHx5F-D(zT>e@do1ZFJK+c7yqdfns5Ks+O8&#?2FvbGlk^`yE~0=IUu_E$x-9!a&_2 zIS+}Cu4WOn31MvesfL;=x#W&Px^gZ}S9PjUMG#VxZKEg3+e1Mi;(EnG?%=)4XUF5& z0{|UTT85QxEeJwpbdc{2#4Doh#JfhST5--RM`5-909?3@hg}ua8EGl_#g^6vv6U+} z%-%YALtD`-arzhY3(D>lNR_6Ho$Bb2l>Y##9*h&}@IhQrpfkla{F#)ynco@3>`On< zZEN(<5F{K>G9>;HA9Ew7bsN!GZ6a;FTbcJXI#vB2!D3-P*8QLv-mb+-lFKO3FmmB+$2v=jCC18l(y#z zf_qP_6X1nMrH2+8WuzBgc%>~com90bgrpCNCOA%sxdz77bW=l#SpBqnP2-+6$SHky zt}LS<%4ie2eO21AA!%)8BpDzXL6|#G@?YY!E>Dzue=fQxusLfx$|_Y&Z?iFtVsyZ* zRBSW{@|zPS#T|MMv2C)nqpEr;Rzmu%+TRt@=i1{Iq4wGAFE{r=$c;5;xbk~@q?fw`^PR5N|2n9dP z6aBE;rJ^c0(W_pFDgJ0PG9nsjsYUx~LqdQ^0a|#@IGX^3sH9;^+5n%7DX!h3c20?Z zjbl|;I?8sb^-|OT6YhSoFJ%BPQFoGU8ZMr`)ErBl(p525#o=`Jx*>nl+`7bdWxK;cE)YRu7}lV7N5;5UG%>i zE;)VXGN$tDjV)u!6+Ww*h+3sEPNU%(9#Vy4SfW}8Rc(}8=^b&#l<_Jg%4+QpQf*d9 zTtMiVtCl@&DpaZfpHV*W!2pD|y~!O{W1wh~D!NP!f4&0S;E9T*5H6LLR6m|2w<}L;DcxYVVg8 zkMg@m1Bz>976(1O(WvcY8A@{#{n60(cqO(LyQ&{g%l8H(UA6~jE}Be_q&VA`1k>#p zmNuSnDckDUxA^=&5v(6?e@zHOTv)V?6ZM1hRg$IZbX3}lKp8LL;S*N3+VoI2mOvFR zEqM)&{cQeAa#0hMr}UgnG32+Ru1^_j(-)pMMA3WFOo*8xT8MzXI`mWaP9P|^d#^)o zIkQCTtU^vy`~smj^%vEksR~dsIuX)P0g$ON28g+NI)aoW2pvp)ysv~PImimkl#qYJ z@PxV5Dc@~tP2I9VQ(;6-W>Lk$D1;{Pk*jU&J%}WJ=;-K>v;P1zT`AA_l#${?#%PHguVGVA zaC`ti-3>L>M4u(4HI1n=+&%(gI=MoExcW)_(*3Zl;*?1N7dZCi$wV3U?qMlZMij4x z(gGZo)2KbBJ}^7Fua^3|fKn2Z+pj?hPDTw=U&xr=ImmeZZZ(n9mC{wGbv9^~7$Hfw%?O?umbyJQFI1fnH!5H{kszTt z&*?Z>NpDpo_?q_a3Bt-aj@aI3k0#@L{{SiBcbdjo$Z~JfF0D3}A9N+kuSEnYX+S%a zkbW_k^W0R=v65R>R#t*Q^HP`xgLuYF>PAmh)Ncvk@v&)#`}-rcK!^^xhY&F?t2q6VGY~gyeT4_(pD}=Q%)=p zWTeSLH~z*TrD z&#?@vj?2+F9%4oivEIY)e;ORdx;x0^aC4zN^*8(L(x){`Y9f;z|Q?;Out z(oykpd;>*--2h5jp0US+M5x+{Y8Rr2{2*trRXE1#h$;-_+JD~+`;|{MlqjLnXa1d` z5~E^nh%%M8wC4f8sxmICy=*1f?<)k%;+pN5o1}P%@HI zNbL}F8mkqs(uiHw=S^;4sY@HB)u=MHy1XuQ}M7cRFo4cr8>ia+Ygkc=pfMOhj#J7rC%he4n zGBd6|abBHWwy6z-ILnQ72$=FMAc8fI&g(0D*KPBL;6?65TK*wQ++wUVHpP-Uj(0+? zr;R-MJsRs#V#pSh`j%>igp#hG%UO_L(^&m9?N{928|5%Htay%Z7M%@On|X(7 z@_3i-+Fu`-{`B>}%2pIxAzH5F* zRC&x^#s1`8)h-noZV_Or6=M|Ts^OQ>GM`ST4bSo7x`L7A zAud&+hsM3;b&p|j{F{RV?WGbk%n;%F!CqtJe0+O_v&Xf=^dQy$0C3&}&K7F3zio=f zYibPNxMKHBjVP;9uR^bAA1Rp7ZMl2E^@W<=aH8W@8Y~<45A5pv3Mn$n?kMrpF>|6q z(4U8>%+g2JEOxxE*~8VLP#0WE@trkC6c@e+$W)PI=Bq=6f0d`54Jc2?ckPLIvNrGN z;r)MzJa9f1tYKNG5)s)qdlti21EZIw`Ab z=95CBoz9yqwk=4gwNx^s_ARJNUOGTd4pJ=k$<+{x?nu#*pnG8IHjqDHgtgDAOQDG9 zg4X3(0ZAg<;yWKIzqCisd)GybAtY+Ac|z7gC>;~hdsPCLNS%h(5QtPV>ZK?+hZ^1| zxD^8oJ{btAkch+Jyki`=xaX-_mmONWs%K8U;4u@=R8wx6Iwf5dk=8;J>7Oj6q&_XF z)i&#pQk}Xd5=P`}*y{zDh(HHbeCsNa5}1U&v|MYdfw11kf|N>AJxL!(m3RfXzjMh_&4jdgV3x!J75{8P>m2CfG@!eto<3*QUw^XcU!L=ah0MaAwjY4wBYK&Cq^nn_ZHHa7OC0p50)SkFKqP3Qg z*^wwmQL1ll6<%|%exF$26$zsdqWD!!gRIOO7eyB&UZjV*)ek(5y~G14Rtp%CCZe}R zdAfFiwyP`_=BYI0#!m#MZY^X6&;-#scMC|=R8lxZl=yK(m&n#*Nd6paCoJ_}3c z9fHMUYz(2~xDPc@F5X|MZCjNs-9Ye|EEA2}N*|*FKhnGOCS+BTE%%fy(q4;W`{&SVDD!$fU^4zYYFTOlUdbP!= z(tGVm=#lUe5$POG0>d2uhTVaU9sn$|=OL55liZk+Pl+iB{b3CxCWeU(OQP52RVbr4`k5-YpY=oinY~TiTrJtJyu1@^Uf~X6UxPz31}rP>lP5vPK!5}Bul!Y zTa}PFg#|a|J#>aqwipRfERbD)p zmKtKUq#z_6(*4mFc+ujvP9)&xiYkWZG@&;Nf&`4tpAi{$eZzU8_up$+Od&^mQ6 zT~5EcDU!VITUd^PDPOTuUYztQNeL+@PnM7#N#q4FieNob>y2_+%oXKOg%L>7zmTwG zK%6SZ<;n{^hik@)YGU5*T*avN8L!#30+(HNiU8zmd+HvKVYF|_C zAL3Cb_rQdc)mT^>!9XaoOCV|^>LeGfi3v>nvGVO|zK*1?XB?w)FiDs3A@sn{{R@*>i{!J0+F~s zl79tiTo3{+6z*=B0BQ7%UlctH0?a)7r8yj&fB4?4SQ9%=)AWU8{{WU#7;opQqrM&2 zx|Q_v^!XUb?wCE*x7(V+KLsLqox&cbPT9^x%d}Xrim92`MKdVh0*_U69ei|&rC4id zk=Iv>iuIX2pb3hUiMlJJ`);JK9&x^haU`GH6Sf~B&bbKu(u%(1Evc+h=`cc_LE3(j zhi?83L{e zZUkGct;p2XIzj?cHU2L0iCcRj{=F4e`;oP#jMSw&`EE0SsdoG>oiAOJ<#zh=QsTn8 zA5LRI;9%D}IK>(@Rrw>D;O>*6Q}EZePj)`>AJ1-l6~z~hT4#>w&1P|Mqg6T@qHRbn z1eMjg&|8X1NlQqYrL46RjGjjqYE;W5WK9G6Q_t`T_xoV-cMkho_r1eB=k9w7dnDql zDeIDMX;mSsa?x~pnA6i-4I+|Ds-HR-RJHfgnQ}tU2e+wGaIS5@$MIGO_LhFIZESKOohjt4z1-=pcfL_OpwY5-PFrb z58JW!@CFl8;-K=j(OldaqZBSW4=?>x0px#a+~ddmNsn`;f5I6S1%qdgEsRq(x!P(d zDQjFLf}i<>rN-2vpD(I}?(H}t2gLnB8-iuAU)#zXC?TI8hxdF&ihpvy-0O%rAMHuR z9>I^cG~?W*#SYsAla;BQpccfchN!shYiLBOMNLymQe6rJ5~DQ{5b)bJE0;-H?(kPU zzEA5d84((=*0iZVrk*4B=%F_Zd#d+w#r#2@`*Pv#W4>a1OUf#Gqb%dgc>ohY?7wM9 zeafVSJZYR;YJQv5AMpbVM)NXtB*f*8D9bMPwxv|^oR#+RhPm`^f}gs-+?Tl*vLAF< zSGO_lM=;}uG2Shss-~r_udI2t>zn;r(%UaCnEBG$l&~gzdPKGEHT!oGE9F&F$8rcw z*R%osR0<;$t!pUjGBxmu`8un_$#X^$+bG-lgr*?i6?N^YLh3;v$nGlDBcu~lw!=i^ z@uv=EJf&VmqKEYMx^fM|^61-4%0{HAYLMyLCGmJ!ZZ+uNHBjVy%HGe=)4ER}r4!j7 z9`I$o>jxh+jHfM7DY#_rq$v!zG%08Sq`YE%^TBT2k^I3_ZSeQmNj=+*8|0%^^6z=c z?h3EGRxYxo^={Qffj+__cqz{{ZHziWqCu9-`()LFOZ{jN2t{Os0Wpn8{k$M&hJr zvwk_K%<^}s>SV%cBtawvDrx8^68vJ!mzt_yF3JYct?^SUG{IA5xq3(&n`LDxQTQ1} z!S;i4fR1yvrFDwTjl;KtsRP>I

0!Gm(#vj~h&(wguQ`mXhF{Gb0XZeO< zNo1sbYk4RrKa~FfbXW0Mhe~=YUn=EHwrMr!s{`B*xvD!QJ#xixPSCw~fB_!^+BYsW z#+w^MoEMAxhun8dA!`k4xwrRi?_Rd+QF60ctJLz=qJrWUBzI53Emv2~iMv{{D{D90 zzkHn(A>jW2Z~6DQ?Anp{DBh`}si+^!QdUw-=ijssYmN{6*}5rXaQ8ddw-s^Gb|dU> z*oG&>SmnPV$|xG9T^lKJNLo{{>`2s;&}kX(FXj$;_HD;qG+v_Ze{5m2fZkr35G$&L zUvd8cxP|AD>u=uJJ+58<0H$tqNJG3TcMqQ;d4qDm0?-WY9$Obf~p4)CYeZXDn(o>Av9ouTABh6b-IvICJJ!0s_ zvd%5;>K0{kUwt;l^guH_rmFW*S%hMb`c$l_CpgMkn3_~S_fDNa^_Gewr%0p7`7#sk zIxs8sq)}sFqeWg(dugV#P*d9NjfNVhM${o9K_XK6n3{bf8(Yyq2#k*2?x?j3LkU$) zLD-F|Ds@CiOU7_3<)BAJ7VQ*y%d#50_Vg_l@}Xm+P)q7b(xG6>xA4Sco@8>IWgut8 zIkrn}HKK?Wl(frf{&Llz2?A&Eg3H8BQGz$mB?m&Q{{Y+%FdWwM?loa}jCRzrmmEo$ zsBtD=P!C9~WG@TdMX2%YBW>f{MyY|Jq*IDYKM#yXHX4;+mEwv)@s1Fyi=w)%uT7S9 zK_X&Z4_KVXS$LI9&1JuYCqnmF=1F#?#w=+60GC3Nl@$J6G$-j32h?fcpdnz1yWQ0d zlbMM+1tW4k{m#^-5*FU+pdcIR(7S+z(XDNF8@-#~74ykB=yWSm zwCx}^2nT2y_xMD#ZP7zc{{R#VE=VkdlOfy3q&DE21vgZFM^cpjP+~W`#jXLZ_yxlszR#+B{T7PT({vWA`1GQ*sw3-KiT$Hj5Jf08GOMMWg7Fo!fpA zmXUL_fJF!ctrjhjli!?^uBeZVIL51$ax52)vD!2$@ypo_vmK*DXdx`nEze%zPrwS}9H?f*1KX;R3Zf{tDs#`uwj$!#>S8{{Y(768bH5-0yR> z;#79#B!xGQ+uBR@(n56zQ%r^cs6bc0=h&(|{{WA5H%fYy>F)Dn%r?>5GoheAhmY*4 zqFakJn2r%=ve2M1-8DOHp`oBiLO|*8i92CkRyShOT}2ABONy#qsNC+F>-CAocq=8G zG*Rm6<0^5&RwhaL^@8fGQAI$JR|454YXW^WoDN@ABFGY9rz0AyTBu1NCS;DH1(BwxnBzyRG=~zCKE4t4QD78e-3>YOdW3u+N17+v zYK_>owy`j?_`+CGDM3X-%V9swl={rhItVFDWpzq4ETJHZj-4ePr4vqOsZEaEuXJ|? zf2CIxJ`|mb8tXCR2U$#agdN->m0c-)t!jPTUN2UoX}nX?VwxJ3785m~8T6E2Z!PTO zu_kSFRQp@3l$ikAewA1M0Pa?klPLIZ^>eb+BC73gqg$1YB_K?p8j(KHM&Y+MM)0YC;)W`AZlwfvk%yvs}Pb}!ZR%_5i73QzzgGTdkIiK|Oi4yq>V`NyKTz8m{$)ZeWL ztW~?x2}IDPsY7<30wB3_Hn93i9xN60!*{o$WqIe>ZLs+PTkFzNxC=hSo)oEz=i z%FV-}QH`4B2bMdiRawY9OOM)WA8k&xB$Y^R9klU?E2}tJs+zIBn5eyxV%WVR)D2@v zARpo%KNzn~6m`*AA)aRh(O1)BjVVF8q!1631OB*;osBvvI76wbOFZ?gDEe=O6Y`OU zUd<74NB}4|>S>TcOM}hv(^&egNF<>MuVzFQ1O5^J0Aw`n7Z}Q~m0vFP6lrN_`-H$oaEsy6BB+yiNMlq5-Nvmr0Qy88k{{ zWCgvO^{{LDv>Er@AuWAZ1P(@Y**%#GJFX20f3zV2RO_vXDOGOFv6U^d<;|Zjiqb!} zI3zVnd@iDOtXEz^=TC_HS*|xY&Kr%r`vAAg&tz80Q$5`oV6fylrm>< zPJJU$V)=z$p-QOMnRHZ-&O7fE@37Pg^>@ShcFvqPEnAja;ODD|W=*7r?G@lnw96z~Po+~#wE&fsAOdCA z<_G{xO93ONk|yE})Tcs_9^!D`ZN;&ATnfp_7g}x(sJWuxFr`+!Y!nsmxDu+T7);6# zH*Q>x!K${-W;2Y>9mhgnddqO>z$B10Kk-tqWN!#)@Kk8PbPx7Z@YW^gc{Jrlt-rhmeKF1pyt(@Al zxyG*X6>R|9`$#;7CtsI=HbFqq0^`D*Y_(e_n-E-Z6Kxlz+1NVQd#5~7C8q|?$RwMdXtFlpaxUvybMHI{QS4|KNE8oIS46V(1H zTfDuUvd!6hmdy>gVAH@Kfn15^7A%L4vizF{&1_HNnSLQ!^}i;^D-p7$im`(wHn21! zp-R$9RFV{uK?Xl^c;6$o%ULq&*}?HVR1m*8GPvoiZe4FPOJrf82m5}cm3FBL6PTGZ zi#U3Y_JR!a&8xv)B0l$OzA)?27c8?gG){UAAqC-m8rHgY7r7-M{GqZ^*h(*Uf|6oW zlya!LqPmI-Ws{&!U)>2k5fwyLHa4A^8u&{{58Kre*@#4sWUdN0!%~b>UQCYdKhwquYt2)h!qoLnw=iCDJ@^#<@+!*Kx8h2CapUX!4{?p#6>gYTI#*Y|JbtPw=nzD)r@l zF6Upjw>K*lyI(4?EY69BYq7kt+ViZ_-7VEZZ93Y3lGC9JLV=MVDBZ9bjBg~g-u4F$ zgdYO4?7ttH*}%q46_fjI`_0_w9lpJ4qg@RVc+TVA+7E7Vy}uRGZ?~_wqHk78d%gpm+WChPKuM{z3bZ2WOwkk7 zD?X&h#U>ze9KX3-G>GSC)LV}_)}I94O9jTWwJbi=zYjHIJbmv!ggG5-+N$p37rr<` z4X)w?2Y34`K2kohym8ODtR_{WE7;5u9|sC7(=pnNoslktiC8?k{-pWUS*=&fbyeGI z+Nfp0G_4B@NfOdTX$0mt_A?$fS|I%9q-ae56iDZe*P_>l(LZ{>|_C6vE>8docAC@%_nt#QTbm z3m3Bq5=;A(Qsyipjrk6elAAlV+BrKaafJT>nsO!Esf9DODiT_0+aQ!SlcGT+_?Z1h z;N6_YU~NP&RDt4GhzMzl2CpDR!<~*0`|FiQ|_aUDQvZm6F05Szeg$SIW(Xm z`pz?K?I@_nlU=a<$`|IJY(5>UQ-;Zv-z-IQDQzybP?VF?{{VIpOre9sAw3BQ$hp1C zO$`W9cPIN@-|3d(u|892^nyweUqZ0Cb_G=HNd1sB+-63aG^3;E5p;V~4SV(%?n@bQ z$E?m!Ur^lkyG>f_3o`Iai66cgJbvk#gnV4L8vPt8`Vvg>=e=wcsZ^F6iI(a5IZ0Tz z+aDyh>Hh%WVZWulY4Jri?-3`9QES})0PhpaCo7jdgv z{vMKt_rgD_$h-d5fow;*{D=PlHx9Nt@$(PtG0~QhHU83r&1e4ry<2u5&j`27_&VqR z03!bfPiC^s~JkcFbxQwylN-b0j?(jm%{{Y~Gb@`bOg6QHlSEjd)nu^&T;nX%6Q)^(Q%2+T*n>tTeQ~GRnsu& zP};BNA+?l}odTp~6prC``0`wNv!8QX?+_I45#<)R8|K`?=9dowLa%7D1oHM4xP-I+ z02LoGA2X_EPnAimD3MOix=J)mc7b9%o98L~wkl1x;6|}1*$G*Nz|^G*ToRPzNd_9a z=$*%~)eR5rN!EI3BC#WbT&@tXxFogGuSJgu!$_ZKl-t}Z(wB*zgZIMAf=|!dax5@$Zk5{kDXu@l?($P@h<=qg?0&(p_bn#7 zGRtb}s+~zY8Cq%DY|B{$`H$4cin$-%mO=MBZJ9>ZdW((r3I;>Eck4x0y!U~phrTI*M%g}KT~mXjVa>mJy1X)}1{&hbZhtm~5TMp#^!+G+w$aXr%?qt5Mj zOZ#H2niJ|$Q>sO>@H!O_hiF%F?QUkrxsaMvYxp8~bK$qVYo!$dUFIq7O4n}2n5nF} zQ`qgzHczVasBN%-p3^_NKRPmfrMS4eEn`dEPm=bvpK$#_dl;jih@=2z^GO}5+vsaSijXOz-5{*{;cnVLC^Jpx)%e=SRZDPM#?py-y& zQ=P1iufY-3-0oL0S6y7?LH_^&RFn7#LaI+yBMqcce4>aLND;!&pCx(>C9b04_!#78 zRnoEvQIvs@?+?*5UbZD9lcfF;+9v9bZ5G!qH3?`5rekzDX&EMpKO=e}WmJT);%qd_ zdzu;n+AUU=pbD&iE1!Fubw|}0`vD{+APIg@Ag4gk)fWa?#HO1hvZ9LTO!`r^vZ1_q z>k#)5YgM9Q@&-$CJXC|jsMl9a$LZOgf=M4Jn6Lv%EOU|Eh~9S;?$Kh^@9mRCJ^DoC zahELe=Q|TTR@&T3sSPYC5-HXbEx7?E+mNx&NNQY5)w;CmkuVQlvWPcFSF4M&N7x`M zBKGHJWryj?kO*x_15cD`JbVfTbYF=-vs&+-PX3kC?9wSKKN+HSXN6u922!ZUdd7_pfg;Mi@qkpR}Zf zjVIDkT96uowFa|75!HM3bj1!)t80n;`t=Z-n75*BJpirt_bTfqUQCp)5!djCt(X8& zmy?oEtA;GAl`#di0->s>Ki>~J0#H%hfRh&PJz=M&;{j?1sZP3>COdEukI9lWPMxP0 zVwEZ0$QpT4B_RYgtsN9x_r!#;W{HLX2~SR@LOLHs8sc1p7g=mhU~8!Glq9^A%OfiV z-kNWBo=Hzr&`awNm(V3LG?Kb!6qIOLFO7m@tcHWe1$E0pIvW?b^h`Eq&i0E)b-H)I zUs!ZaJ4adc>L4qemyKM-^lNPSDPL) zZEiIcDFT{*Kd7=?aayUx5>M@j^IGbxOmG5&MZGBrI(0G7n=eD-sg?A6zF$Yn>MRzH zn#WT>tdqC+zLC}Osua^Jbb#7^vCzU$@<;&H9Mam#l{M;MsG=vEho@nx{w@)uJ5)PPY#`IMtC1K4+oDB@&Y zbt^fN`r`54of?_D*C6HGm2|h_c-^(ylXa+OlFeYFK~uFA6%GblKnd!lDL_KuGS2!i zXp4LhT0~`#fk9GFfC|FB(*2?R!TUhrzCPyuN4(o|J7!hrBQLUBlTqtq6*WmoWvZ7Q zB`&2!TMLN*1xQHFaV=?xe2p%r%hgjO?oF)oh#8@GO8BWk_MZEsVV`ZjE%#4_-njn1 zI>|XEqThPs3JM^BPW9rR+-;82*F2*(SlZia3JF*7X*on??lF#dspzbe=B_<}00T-? z`4eC7@KC?oAGLpUe|2whJe`Pfg}T*>-7-6+R$a-N4MN;v$&J(9YZkA{*!3LTy~$~` zfK?(%PgrjL``c+RG)3Wf^m5G85HA9w%DU8_8g=;i4~jG89tY;Hw12*iE_)`YwdMK0 z3tVlPO^&f)M$dns-s1#>JGn>>Bh*BdGKEO%As-KFSV4L8pAH7CVdGFjQOH1|f(-@$X+f#5mEDCSt#9^lp&vW(L!s1%kP0~1sbAd=Mc~qHhp0n?yM>0LVRK$|*=#QCB(w3V++`S0`WY3@g zL(B!njntGhk}@D)A)pFeVO#@m$L%ycgUYx0YB zHwzRj5>9_Y%d=d%^7>C^#kCy^mKnb`SKp}pN zlJr!i+mfaEP~^6Qr$b7lo~iry zLx>C`I=y3$x9G0g@{r<4l8Fbbqw85jjos#r$y++!V;S&QhxU`sDY0*Dxd3*f(=FWI zQb9f`2SFK^D#M$J$Ktkr-SRM8^AM_Q9Ha zip^-;sib-8BIr|#DN)?5z{6IS(L~>JWhHM6w^9gT9am(Y&^?x6TqTN9Ty7LB(mh1b zcBZ9iLK1|fBXLnwv>SCXmvWwF`>8#cYfhRkQuh0{wz#{0skx;_rm7Ho==bB5a~@Gs z?JwFTQ_nL{O(u89R?t`cqBc8CMyAH5MoU=v0}{M@j>C?N>MpgDz#j!e<~+{#kzM^2 zfFclRNbw4WhT5chtPbE(Bj&$#I1#dYpMIlD=Kps&)?uh9MnU7A10d~EW zrDaZNI(GbGH(@4-hD}bXo2{x|Sy=rtYE3>lZ&&`WUXnq5d$eW08TX5&jjJu9-t|n& zWS=~q)P5))Lw&Dx%4|;_0^M4sNT_u(0@p%joyJ`pSm-+Uik4pbOSyKBm09rE$n7HB z@0g;-DCsP;EZc2u>}iSl$(r0!wNjLOl654(kyQYHByQAP8uaU z11OkYM9S(b-Wa8gWlE@*>t0U2nrEoc_lU_(<3%wO0|iPs!?8B4luSrT!Up0*jQh@T zTv0WGr9|9ZI#*CA%kE2>MG{@({8p?f?WA8WEuB*_{{Rgnch^{~WUtA60@3)tAK1|M zs7pmnxm&-8bUkKb41!9!OC=MzgJN|T+k*%QK8EC#FYt-HeW7s`ReZikMA~_&9g*DF z#Vst@ZITu5O&*$mI0_#R7_dqm8RmUF#B9GjYpjKytSS?%&#B{-i z605T;hFL1nRTnqMao$AEEuc0Sg9lYYxSjVinF>ndh<1sEJ6P#<94>?(K4E$EHd(`~4|S#o!C>eWR`aZ{0(&Mk6{Xs8zLa1~c|_NL+1?!M!4ys_GE)LA{d(G0x;)ovnx-x|QiKx6B}P(%raJG*BPg{tCE0>HM61 z#Qo3LhcQ+wF<)%olv%*)Butk8ZZ zQ~N3U)IjBn=|NjexiZiqQ_zql5?0O7ATjaLLR^Bl)I9l7(q?}+mWXPtR;{9H?g0hK zojhSqI;#NiqDxiNu&v%E8Ff^QG(<1cGqe((onhAK5e2kGs-kCEiO=>xoApW^7Ac}- z5-Ae#FzoIHJjS7ot6-82q6K?^PnHwW3`34cZR_3vmEfo2iipLBY7&rB{8G{q*Hog5 zG(#J5tj<&N{{Vb7;+|`05w$aQ^$SW8l^+8g1>F+`iR|MXD)E26TdmZ!2&I0Z)Flr( z4#X0AnMCF`+jGK>$h-zm3PuUsMx`-#Iv>%iYfF&qoizQBGl&S#oBNZMNZlt) zkK!P5zG6Y#*rUyqe z`9+rBuc_SQ#locp1v;%i7>BzAJ5?)e)|QGWk$<+)dXA~ol_=@?kIEx`uvDB^Gzq$X z*=$bX>6FfY#WMm)1rZ+hh@p166)VaLLV6jI1CYM0O;?81OW6L{;cXM^;clmF%?dL= zvJ#dw)nN4lNP%E)G}UDP06A&Yo~8yY2$=2)EOP>IGJ8n29n zV4gJ#)fWY)cZo9+j?s5{TmJy*I$RpuS2M1KLQ{nXrHRNZ)Nrd$%p0SEe;LADd=lAp zRk^=zPE?s9`04`x0E&GC5w#`Lnyu>gqg7Oyf0-%}PelcgVV5wZnb(bbMZ6@DPnT7o z4&mjG?S;k?4ZuUu?PDvaNke&!P?zPK%Q}5w#DL>eSySz|J8F9}_X`VObKEI|(1htbFL`y4#>pGyET&v=&Y9DE|Ni(sli?DGBKA0Yvp2JD;IaUCc;- zvU);x6jLrkkU_?Ig)3w`R8|J6^2aDqyTL(T#IMxpCC5(DsT&9PLR=DB(dLM1t#=VH zvOEOQ_3aHyLJ@F;pL*R^f}%o1{{T0Xz}*v|BG;%MKqM(356viaKSc9RiC0~3Z;~xm zYc*}qWBiASti8sWxN%K5%ZQVLjn_Z-6N&&PdIJEpgLkWiWOb}u^_6I~s8nmKq*w^TK`KQ+o_h0UV z?b>T`OgWZ$8;4&?*Nfp7wg6yz%OLnboMY{ahGA`uyP*;|=v?a8_H;$$`^TQhNF`&-Dc}^mrr{gk z+*gJu?wq53ro(;q@$z1z#xnZEQ&Tg@HYQmQQ&L^kPDba@cO|-s1-7C|3Q15orXQ7L z`(K?b4Rp9^k50bXJ|oNU@gE&k&oP3s=NPc|mdBHfQ(xl)!2bY`f&C!T?pKav_)bZD z!?O!5PBoQc^$VoPaq7m)RWx-dku9w!;6X}+!hr-52r>FWfX&)vY#DcI@c5o82wq6Y z+FV&&ItkcWBTWb>qjJB*Vni?m)m8yZKg9_tmPWiT1EE}Tt^}o)0J2Eks8q?1l7ONN z2yvJRt2HBA2iCR`t;tDq>WT{A%>6s1KVs(%Qtu=zKKtoiZ=(AqxhxsF%pv)2`0g5I<(6p7Q zVsD!ICvw>M{+n4&ljmD&0DMYi6idhs`uQyb7jhZ;S5SWH>#aFw*^}6H?!#0cw&J&b zI;OoT1t;|Hi<>sbH<|fL98n)Q@K0qu-Pq*)sjAh9+dGv zwOhEAmA%cVWV*Py)c*kHDhm6C`*`q1Pwe}L`R9c#R~%E6GrNtF#aWM^9i{Qs9}X#(#iB;sYgXz{sCN@NldKUC@lcYDQWm`}R3%%} zGyBhIeuR`P%}AQM6#@cBPls3`1uIf=wuQxcy)&o66cBE87nIZsO2W$^?w6Q%i5qKR z{-H%*&RCS~*zNU+*;Q_7-Q7Zxlc&@~r}XwyqOV+J$DE?ac-r1>On_gcc7ZLcQ`Afe zh$b9;q}>8y;`=8F)x|p19?bo!hgD4P71^4o04TO;+d_an4zh)RK)3Qkf4Oo)_|NMp zBa?eF_St2()?HcR%UiVcO{vG4MLHHj5H}Jf*GP731+!aF6HxLzoUhs>bx*{okjNv3ao?<`H2ZAeO3G%gHqJA)KSb+c%4H^* zwQov<#EiR$oc@bvwxOX_UCHvIO{PZ=(wYmso7!GCc{uIU5w}||jVoj9{(@np9>kQ$ zb%^R&HT!mjHcn;C-|iFoQ?o_E{6zC~^;X;}+T}CJ3#X`VnW72~p+L$fV#HgKP}NYI zkz`u>ImW+=UfTH63beKV03(di(**&zp@!0DB%kr#*ZiTc?UAtRkxQZ>a6oQB}B*2diur=7}wL zt0`?lUo-HA*>V)4U?ntn2mNb>H9q~Kp~Rwo8PB&{*3Iw5d)Wb3@BghQ*8TT>o&I`mF zim+@g>*M&WFEO3^iA;_Qa;~Z`=~zjkfcuEhbtCB$7X54;7uPPpoVe{1FYXxOnxyM4hR~ZgnUvCEwFBdbZz` zeM}T?-bFeDR2Z08Yf7JrB$-l)Q7TdHU|Jff8c}RWkdK1u+6V)r{xDNrM2atpybsO? zC42(OAxkMrQ#CI@BkENp!~s&4)|^%6(uZG9U*L&5TN33|$Y+|z4{YQO8BTe05%v?IE-!8qN zqLP#pGD1>QE}yzBu{TAF-Q3ipV`|>fDQkY78kk?Qgl+f^>X7#g8oK*)q-Zc(X)Bt9 zy2LD&u5;+D@N&5AU~#UhU-8Eou}eb2OCc)&ZZZ*b#Ny}|oB5wMe3G40_f3Ya zhyz&;fJEW7+tFn6a!0w{QadNHF;0S%w=L5XfG38E(Z{k+k^NI`Z?rZXsdsGlPim#6 zkyyEP3fe4c=4zzp4c=L{zR@HF6%E?OqYHJkEMK1C-6eEZRfh62G>kH^@aR07wEQO* zW?r_Hs`CazkGpZvIqq`hA{tirM8Gndr`t~O>vA9{1IJ$D(MGOO=LSDU>uG!dW)(Gf z4}?cw=0FPh#CR*@lxV9XmN|pQN3kE1nWIC5u0VR1dSKCI3I3(wgOZQ_}zO81gddP-(-Rdj{rdIrNE?6C^Ar241@GEg!fSRMKu6qDPcP_2UVsck9UuBWs; z6E#CgCec*1uv!qZnTe8hA|)8XMP9p@lAP*mB0U8lw=f4%A;1(%i5i5RtfOX$XUo*} zFw>0+ejRl|O~nnRDGExwPkykXXo&WaWo7-Q0;aH?C)dA(aJ(+I-8a{q<%&eEPNsV6 zx==H&Xo`l>&d>zNKj4UU%DLM=}L|Q;EJBO zr_7X{3^Bb(Luo;(1YKK(WaxW9<>Zw9zzX%R&(eQbLQBFsVMnwYIv|S9w1C+O(@h~R zIwZtBNjpxhw>naP#vprh>j?XS`A6_pkHAXZYimhpKXhuCkL87fw;9;s5b z+J$VF#6q%1^3--Azn&FSUI9hksQzQ>8U8VqU4J!h7Qe|Kf{;8=lBKkgM&T(B+Y^}c zM=I7_Xr@gy5mgdN8pvt!lvo;cRL7bWmATS})?_p!OvG%BQ}%BKitzs0>?v6IOs6r{ zUl_1hun5*+oc{o;OP{y~KF!X|DIOt;a!?k(+oURHR4CS5dsEaZtaiH9jnPtK98rzC zm**Fg>9I{t%AS2oBT@nZI((qJRypn3#3oZE8y&t=QRJg1HE@?QV>Rtoa;`a9j#pg3 zT|GToN;iY%G6az!(jT7(B{x>9>{m6nK8A`ZJM3RJ;Gb@nO1@U+23@+|ajL3k@Z4&O zl`mA()HL95sI7ifB_s`1D7Z7+qkw6L2<8LYf2}V z-ml$HojvSYFPw8%Z=vJ9AFFk>F^f{Tx|+(Ssn!(>X1qhB|hXuYx?7!!p1g$ zomImV+v|L6?VZjqk)VD9@JbJ2pJtAD_owaqIrm9m&uw^q6O6lcq{QrXC~~U4^;u{N z4~e$4CY`XfZ3h6sOgg7 zDkE_!TGV$bM4beH5Vh}UV{`#T-AvXw(mSa&N-u1lz`f0Ty2vW6ygA1e3$oC(x9f8% z@OX1M!qjTk$tH)hXFtmGMQTDo6;cpwy5wV({mRs(Wr~2bo*h; z9j>5gMFl&P^;U!b0GO>O(k;wn7EGjLbG+3Nws)C{ch2I-{FG?Q^6a-YyjEZGtF1-i zg~nT1ur@FPFSc82E;S8O(Niv=_tX;GXh|xW z(0IZkfViny@0lA(B~R-gwha2u#*bgF7H6pGD-a9MI5r)t9aBGqTP(v{9?*4J73JgP zx%UXsMlbST^jhZ-W}uZHIq{sO3yuQUDd${!v962$+?HFeN$elo1__R%*UfL2){$Vg zwn%;DEtbxMzl1~OuA2qJBz}wl*Y+ZCnCYgsd@+sACqSdmCHJz<=Mejp zGl%4q%VzbC*)l|4sGI~Yny!$v!rM?IMI}=ZuHaG}Wn0nI*Yp(6XJ>uZe{1yE*H`fl zXipd1t0|zU_bbSaQ7H*(nyX@hbQ4IDzicZJ>9TK~6P%h)cPlMARV#hB`;Au^`+KOP zrl^y(m9H&oE0No_Kn-Cr+XtGtSMKsMM$*=*@b?w&Zz0aAYVGv3t4%iLCBhU*By(_0$n?rme^``~hbgI6+@y}nSUU6( zV8P%a#z*hXLm}uqmTSwoRre>jHkP$)4KyTpq<^(Ob@tutImX)0@z?S^%Jc#%ZuHEe zq55|BPDu+OjZUTo&SUPfgWRnER^B6?#PO2bcQi-b0i#Kih9Wp5wS}gWY)4q{Rb&(y5(7 zpH*GTy=Cjg$#N>FbzNqB2S7VOmN5o3N?uA{?}8H5Pnv5ohfBg1iMr48G+9}0hKu=^ zO1~vFIGVcYxXZACW|pRbA7^YpsY$FG5Ly-ePPHDjT}kyGboH9Gf+2(fQjvLdepaL} zxGrO&B4i0)gj(Wfw!pJlrZ*E*^jq*ec;uH#tkWX$T4;AK)Lwv;>(uEX1Y6Jz z#V+#b=^Ilk$(fy3B4vrAp-~n~N{d$J2j$u{D*D7d#OC4pcq>Le&|AY()T2vx{*O%C zOr)Ve092l^^i0^Ir+2vSsB?E)g+rTEwNIqX!IV1L2|&645S#7kyA_qk-EjKvq=r-9 zaUEgX>y3^0rz~x~$$DrLMw; zf#uirb8X}ODnoKMM%G`QiVJ(;{Bs7~PfH=?cDSGCE`1Ku1r(?uB9RX8>WkE(@j04J$}nkbu<8)Oenxyw)l#5jUIQaX?Tz2Hm}(imq{ zru0gXQd%BS;qr#jagSsmTDHl>5To zS(jwCl3>UV=X6oo$wU_A%b=fgJg>$aR^}_fwe7!2hhbE-sBKonA>frXjfxv)By^lv zt#5<_K(a^SjpwM8%3~a}?cSE?n(7OM)6ujt<-)3_rkFGvO*3WE2@007h5d7!Qg3n5 z+cVTc=`+}!MLz<4y>7naGfQ%@tB)g4-gzS?rK?AwQq?tGIjEpz@UP0GQZ&*wo`*=o zv$wxjkUk;0ex>T3C2jp1iwOwY=Z(}l5nn&Tzp-*x!M9t!d4J_Ua9L8-{eP3LmG^4b zm#T}mCzntY{{YO;LI=tk3=Wj*(O{O2GaqroSJeD<{{R7}=uQ6sC0`X)RPR+LWNs&3 zu_XgNHBH|ohw+}*_^@u0CrGy z)5Ap?tJcNxpIWEvw=ufhU3KcmRO6~D+bx7-x4pp^otOzKQI9aAqlC567(b@GtWnz{~$q!Sr% zC^=l(CpQ^)EVnp^C=|BVk|iSt!+PLUWU)2jPfi&3iSBcVYN?&XD;jk)*M|M#aGy0;=x+G_NW~>xN&=0prPMW0+NCm*l^M)MBP+qK z?cJxSd={sH?u~`u^&O7*sLk(hknsGg88`qRK;Xa9pJutXN|vWmQlT;mNSKT72f@i~ z(qE-kPuyJWH?ogx){B&Hahy+Vdh2kjv`(EWTI3V?Ksxq`{FR7yp;GKJ4rYZ`c4LU& zEzUi(m7z22O#8)lYf=KSTU|7!8su(t>sfh{Bu`_kM@k$N%N!guN#kDb;uH!Gd593F z1ZyVl3er=V?G1aYrZhb@PHET84la;apQvAi4Me&izM_HfKRE8y@U2j^KD76k9iash z$x)Ma2jirCx{R5tRDcIl+`@~^7Y{^fPgJl*k^`(DbYC$lE!B>a2B+%|2@2^6Ye#XY zN!KALBI{C8R1it*1mobiw<_-L*{M4CYdBWgimriFTUB*f1O&{U@W739 zxX}?kc|asBI`^1|3DG+gH3+TS#WIN<&c7%K^hd`SqAYUbPUf9_W+6gaF0r7LTX=I`)k?N6GD-{cbC({7-B2Xc9Q6YsRD=|t1 zmF^Nyj{Au~YLaz$l?=?31pD~F?|lmIvGGXT&OF61QkqZ-JqR+=7X4B{NL-f5bDtd5KDb~Fv;za#2en&$M7SvF#$1c}=KXS1@Z9d^~Z*@3@ zPY`n3P1|`c+}hiwQ{T%~O4=JqNK}ee4vI`c&ur^?H@Bjc_Zu{OUDY;ut-C+qO8VS& zl&DZCNAxcMLE2B05;}a~Ys*+BVUUi9Yk8%F>>es!dpY}Z`{?)Kk=Smy;;x({$4Gb8 zf12+HLpXulzeoTO+E7(D@s?{{V2@ddfFghPG!?g}M>=6XE;+00psd z?%Ln7R^CYBzzuX=4g2W(Az3+Vgz;B+!!Q2;F#DO~YfBVq%`a}ZNUY%$wBZGti>)Mt zKBAxGyCM_N%`=ZBb{pxkvu6~n*K%IL z$1}E>YewALJU%yyf*U%F@@g>JRq)y%I`b>N-U77us4&XxY1T&y0_Z zDyUM$_Vc8KgbrL&pa6sli2yeP33xP4F{@%~5UGX!GLEse>QV?k2%@Uo#X(Y#or*nS znOmYV-r$0YS7~{#_(ySH&rik$*ySeMptmt>bE@W*c%Jd_jHVqmN{&3^U0XqRv0IvT zO*gT}R1%^$O+X3D!Xxel3bo;48wn|?q`X`=qtmH%3KG;fp+CUDZE;dk#5|I@oQmp% zq$1+_4^>UY5Puje-hC-l3$56E5V4eB8|ia(GwrCHtnfHnv^^dkmS{^elC2c%Y_lEnO=Bx*dFJ9em7bh|v5XkP77kX@VrfOZPwvxRS zrP?Kvpi)If{K?cw6?Ge z>n-&Ps%!vd)V6i_#WR()_StzLY1BwZ?OKdX4!U&SVL{KfCKGkYz2oz5xO;YKQ06_6 zw{on@Y>ha-Nmo0yS{!Pgr3Q0VGC|m_Ptq-)a5>hqkK5Wgpgd5A4xnqGKN5BP6clk6 zvGmrb`_*%Dg*~s|uiif;`Un$hQ;^Ch2GwqHr%#5km3s+Ry%My>!3gU0F6L$zuK(ZBiYQ| zv?4|`dud%LXg)m={ww!Q_j`oDO+}jIc79H#PSE2w>my64aUe=wy&~99@Sf8f6cb4p ztS>*VqxFBqWwT+Tb5Fj_RHMay@Hlm2VXjKxfo*UR>Rhqwm#C3G{*8D+4B3y zzG1j3b^ibnf=`n|o*Q4>quJI(pw(_;bHm?IpQCZJ)do;!-PV$%e%Q2Ha=>U@-o9G? zL}=iu_ZW+&omy66iK>&fozyydjjKw(U@-l> ztTiR^rANb{;^vHt+Np8(Y|gLtvu?2f8in^hZ6r8DcR4k`|Zp4!t; zLJIe@`2c+m4*eHnMJ~kD?{sAs{se-h&8;@$-}CGm$cc2I+xrNgzpw8d2J$iH2uWc072yDDe)V-^3O~lO0yjSPQ<6LU4nEXa^2*XPWO8z(H zZ)CV5&D^MYVxbp+x&Ex7cVf9<@XZn2-*wYTnj+UTWS6+bt!8F30inQ`|@3RR&8 zaGtQ}&sn;lxDc8gS0>j7HZl+3Qv-EA#BNnDRHKmTm~rw;%4XoOf>dTs(j~0mf2_yW zwW5(0xv(~t9aGhf^QX5QddWjzv2wM!cKdT_ZjlaFB})yClPc>dlaDcI8$jx+)!!G% zJ=H8Aru!GSTT$yb%?b0Wj`ZkL?xgMl^ms2gj zwgI|+h|E)cOE92Pb?c#uh3ggW!XT0t$7Gdl_{srnid)#CXD)}+s7vLp!eOzl~$u?apmVG)0n-ZE2%YJ16Fw-Swv_ zn_!`bI+t84QTjy0aNEEs8vsw+ad#JPmF2}W@7=CXg&}n$A%B?(@~8Gg(p@~OELiwi z&?4DxCvlm9OSdAxqSi;!I0{hJd!&RVA$ciug*58BR6dH0>cxaKlOxqosNy7{u5OzU zsY}{SC@lcGq7+0+vcpYPO5>9dbVYYeQTjBv<1=w66IPHcwxlT>c6qWI9T03{-fJto z<3^6NgxJMbC$rbS+*)|40c*D-V__(q`^B2wf{P-)yfrCCn@LhnmqP{nAwwJb6q>)- z(yCVpZe#;5oC|jd+NbcjIz0*FYYoDoYD+ZA+q=s!e0*Vx2waEenRr+=Dr4ikaaB@= zmsFBw0Z@?u8AZa@(4MQ#8Lnu?5*kym&Id}x9<<;n4Ge_;0AxzK)NND;^4T3HRG4EP zVv^&((*Pv&08g*R0&TF|r!F%Ok}j$J#=SP0LQ;U82C@-iwb5jGs=3umz2GC@M^B^} z=T%jU4O3&5xwW;4%=U`?%cx4xc$9UJCa&E2LTZA%qjID_?TWq4$lOlc{bHe3;q93&`BZAn`C`86>$H>=MddlB;AW6w3im2p zww@i^#zF&}D4MH%rTc^HtFBpn_5T17o`dBOt*ef!Xtr!{w`fN-c7~7uNCsUGgmZ6H zVM2N+zH>8JLk{>J@(C z-lmDqr@6FQI)zAnY04cArl?76bfRkV%exFZr3Yu$ejQ+W-Kuih*G&^Cd(WvYsC2nf zlx^xthj@>D$ZD2@8!V2X3602kg(?oSKut@BLn9>qFf;GZg062dW>p!Th zQ%tCn=_5|C6tX@3D$#F@h8}{Y6Sm7~^UGCrJ}{Pm0yM~kB_Sqh5-_BYYQCBAkCJG! z4k!r-`m38(5%TKT*X9xMTyhX$Nv9RDs&}1Khff$#6-++qtw(fLdF@icQSpLdf~7`W zWU0|AX|0HC$fiJdf>{`8P?KFb$jP=_ZeS!&#vd)B3DqlOszYfY$V|>~=z3IOxS}eX zno;FKr!MdtQ^MCn>rzfq641(hqEU>ht2xz3Tm3L;)uLJ$Oq2y3W+>9G zx>n=aQ>$t<*#%#y7D)8`$MjB2lB z8zbidiHG)az;#>;meBJScUMnB{jYI#bvpuEVZr8}`N~2&3y#`1#SJaHV9i zgWRQ6#C^v8)f`*wtJ@|E$oQ8bT(Uf}qVYp+$?7YX&}Oh#pru8I5C^BRLSRgtyGCs>cS-6M@t6PV#`Mt}u&JZ<(z=B(F%Xt}rksM=~W+ub8~9Ak^9 zTQ9n#DPcf6R#&reMx-S=9W{+J6UHALjd7tmAAng6_jK)LC9M zJi&dD@n!dkeZ^ByT5q_GDk#6oR5a4MsAw$JS(GoL@pzgK2I-`g$MZsapiiM=6nvt#EXC7RR)Q;WB=@OYs%Y1YuTZ2VnK3{P;y+VqifeYlEj#5r9f`(s#pKzwhziS581?@iD zcCz0K9_RYxJxZT-1+vo7Ae4|mGWbOTFl$w*-I(1OZ<^=M4DHxHrZ6a#XlI{Eb@+39MDPwE|z1yeK9LS5mjch#tTz!AtwmHyCujprWV@{4uC<7|4G zz^72+6yg{B)b9kDktsvcWFodCt+BityVk6}P=L3xHI+@3-HcVeX?Yx_fN) z$#;6y#Fp2WN66fdNJ>|AQWvqxYDK0}YeE#U*Px5P9>Kwlhun}>L;DtW$ul>cj_cZv zt$-9+!CW_naaEq*Z*8P8SoYT5tTGBajEOV(&LMJ_^4&bRtM?w`w%GWC8KFg<;ysgb z>Aru|o3}XI%bS^T+XIfWf>NI_Y)M+r;vwvAZeZVTiX%DOn-eg)FRQxIRWIBRD50&* z?G(1@8cThmjiOW2`DrO1Noxy`7PnNEIlk!I=YY({O?bX1BI{2+O>$RVYF@|Ya2IY9V8Bo&+ z{-VCEL-@5aKqd>yWdtnP-l$AYE!PkC9nm(FboJKe>aW+wMbkFE?KOKrD5GeNDcJ)_ zC%?uk*6@E3R@rCt?FiD}SnWf5o^5QZWh$p|6s;t9g9bKgsMkh<Lb!d}`ku#Cf zQD?Tc4HZ3A4<(UwuG-Xg{XK4flT+jN`KsMcplVi;G=zhxBzVDPrNmdF5!+&q{$xL$ z^C~<(#Y@K+)4)``f&EoT8iwU5e9SYLxZ2Rxr}*uYo_)urg9XMP3?`xGf1L0JeZYj{ z?TryRjF_zgbk~n-n3LE%;5ddT5ZT+$vEyHo_G(8T18Gkch@c>zVfWD@2pF!q?nCG3c#(Be*le zd}(L0W2i+P>hytY#b}ZNTWKiT?Lh-j0(GB^S?}He5i`M1v8*l*o=?!3%MF<}%Y8jD z^Sqg<_fJOnJ^>DnBLr6NL& zG6$xGPq#=l0D7eT3yD(Guarj-6iSBmLc9opmqG;MOxLc1q;V9kN9_&6%v>^VR`(Zt zXeQUjcGcT4>V?Q8+^do5IHb=)QrJlNP9`ysn(iw(=2=bS*#}f#%NRZ9k@Ksq&c3-0 ziu#on)FxI_yZF2`i8%#Iuh~jizyf?YgWF0u3p?Tt7-oqT4)V@JLu$DrX5;-KiPzke zqb(9CSXd54Y@1 z!Ed?qja@cBMMBq?(y-fF6%|PVBuOQ<($M+FFO6|%FFjWI-h9I%wr!(-k5AF#`zpE0 zv(UMPaaD@eB+Ku$>p0wiGFa*K5bc`_pH+-mjR8?VF1W4Qr_ZT0Ovn@aqSpju1!aVq zrhesBy=pV*Jt5=KO+e&>uBg=b!X4F2u%yjH$}8&)l8FYcM31@&Q0zJw5W1-LQ58Du zr6PVZfy#Mn^@#EOV0lr|1-ZA^sOjMmvdnrZC}FA=nuP!)PM;Vb+$um@BPwfxlOwxP zA2#7SZFEbEUG1dpF6u{FX$HkGOga}*iF0hoty|C*l#a*7Bw@H`ijS7r>EOqzv8sij zx*BkqNh**(wk2)lVJbFjcLgkP9z$yrLv=QL)kUfBpUlI>8Xpa&ERkKcp+HS!;!&Pu z?h5l*(ve$#s;xFz3ZCE`lwGNMm-ihcMfxUu9WV(EWs zqj3uap79?0x}rs)2C2Qc)g?hZuct-ES0XX0FeAe%$EQw^(sVAdO}U zVnP`nZVF%xg`A{V|?M&9xTD=qFz~i$Lq_%6-XPuhJEMpi0N+kcq6T>NGdTOJ-dnGZ~SZLc0sly>b3O3C@ zx)=j$C6!tXg#I1j6ht-E2rATqW)Ic{e^j8dLV*Tqj#PNAP@!!tJvT?lfDx-MBoHPv z>mM!-h}nly6qEa7p(n*FEw8jCypNp3B=t%ue{Wj{X;Bg~);nEjg)B;nRMMmZy#a#b zbprZ`cxH!b)^qI(Yihoxi$j#Qm5ot95)X`YxD-C(5j|C&$gn}0W!&{Jmk^JM#DtTr zuv(7b+lFb>8N)6bCt`X{R9V?cJ6`zAz>Z~gM#TD}mD24hDRAinJZhw%dM|wiDU|0g z2o1p<6H;2_+Q2(hXVw^|h|t|AkoBeYFGU^0_@N8CN<-Wlt59$uO94Iv$3R2wsz^CU zR{1GhFR0J9H)J~7KajM2tFXggjFVpvn9mt*-NeRZUWNn zT)l_{zm(ai$ELfbH8=z94$^UqK^>RbzY^&Uh!h-Yu%b^EX+x zb0#THO(K`IDZElPom7eHRi~JB1|V*&odK(LYr3fcBVX4-6{Y)X`^j=Av<@N3IK!DS z+TL8WVV1WW;Tp;`GR?-4p_CTbx7Vx?9Hs_aT?2AJAyd$w2=~M6wY-Y`x0u)aPAAqs zynlk-xL0{&w2#`oZt8SCe*^w1x_M{sq3^SnDVB?OD=&y#a1iyk9;K#ew6#p^L)Gm* zq7yj+M!&Kxyf5sNienfZbklP2*KwfzK>q-G1Cw|9^5kr^V_+U&RQ|F*xiDDQIo12- zzFRok+^%%OeZbc)p4Gzs;qL{emtRu)%4w{b1Jup77f$Pn*-ZY~wzW&4lCN;rf;rE( z2k3=Mm<@Kc4M^+IYJL?bMPFUH=UQAdtZ;=W7Z?E56*u3W@wuzof;-Uv1wGwtow8t01Yjagbu zwe1T`c&x*O0u(1d80O%)s`RZx!aG&dP_SFxP@dut=7juG9*DU&db>SacM1oKH58|F zY6q;kkE}rEEF-&!vEr(08+Pa<+|xw+*z@Z~c|eP0Q|Y0*Hkp7_0)o6#Ka60RPC0Xl zo7|42g=!FF;l7J&=~a_gMB6Jrr(fzr1q2E^hd}3VG+%IemfSMFDy4scF&zq1xXnF% zFQZfg^zMJaeKIMR%tYjOK&Uw*dmXPt-rlkLn;W6_)9X;HR5~?~>#RIz;ERgn0=HW# zsjCy|pb^ZdJbj0DXL;kF%<3l~Po)^MYlSESVEAG=rh(9!&1P^~?>(zp*upe9h_> z{{X_I#pU4oYmrCz6xjQr`*dNt3zV`Ubj0lUx|C5R>Sq`vvdiy~+KLoRle%)& zJhAsv*{sy_!24Jn_+o2X;?Yr1dY>xjww^K1?5`jNt^vbZ6GXs$vaJkczjt0mbiOXF zTj=>ZuA1V7hg+y3>dPpoN|P|4;vSeINgX0%?q0yRwNb^;)(}UP8tRYM6TBZP*W~4y zy($HN8l`9c&}EW=7q4%XwFv?0D@IJ~3Q^N)Twp3gr1gr2{yN(tkl%*(QRxTsFMp1qCH4QdR`# zq@(n6?-QJ7IJO$z>lZuVc#+LJNgIFyfccKUtz_IY4UM@o3z(ZLE78KEs)+vpc28(r zN$m&Roy&}PhTT&~k5pS}ZZx*qiic`dOuCgT`Ds&xl7T4zV^;f9?#^G8anW~>a54u{ zpaEJBf}(eigJZ0&q55WRY@JlX6znbz1*-%wX@V|y0o%Z z^($3WjB(O-eYF6BTdHcdLwY8BBQVxaN?52q$)(bRZQm$-7~b$HHZrKS1%) zpS?2}{$tM^^mjc zJ|ivYCMZkk){QPduo8>1K1k&Jrkq=|Eb8%*Q6Xtrptx65)V7xs(OVu-ksl~6w|?NQ zYKD^nZC^(-Y5XWsCC7CWZgcG@Kr81&M}o-#Dhg6iH0(q;#Mid5QUZWKj3gAFV6RfZ zRi7VY4VLkP;A%zGHk)XQ(O| zB&o6!WFjHEW4u~&0P?SSTZ1jLToi}Kto3&^G@2@MvVV(jNVVEGcSVin@-e_kc~5nhj4LWomDVfM zPl9wYEZZ{ft1-#*sMu)4qj!t#Y}1uG(RE}lr|4Ng4n;Lr)qe zEo3{BKXWN&x^fAT)=&$83rw?S=#t9Ga0%EZIt@%a3-wiOosClA7ZmL$a6bvbMe45W zDwNmeHzt}(ic^y(U9kasGSqlxImP2e7V{kL=}l4nD#1v%YAVwu)D+1HGuK$7-^@$& zUb5qu#kDWA=$W_-ljaG>refstX+xE7INy1h0z4r0NK|;ViKd_gr=Yf_f;OPfx{juBhqqK_ zYRg<-R8O%qK;$W7AR zoI~pgO3#*Oq~Lbl)yi&cX698qc3ka+HiaUf6zMM!60qHXs`ohe1JNj|`CiX#r&8*! zEwDi7RjK+zufJ$Q#fp|kXcS@0`NF2{Qu8)8NpDaq7e1c>5p!M>(Oj{-Uudh-Cs}j4 zU0z%F8nryN^+e9!!T}C0I;%`Jk;W*{wV?q3iE3eOIxaY`VU@+0aE$b@pY#oI4j)7J6P{t`8bcrO1^oJWAdLd=HYf^@m&poBY>-*rS z?dUa7w;3IDN?Kc55hswOfovFC3@<1{Nzi41crJ}SAKqgGdGlU{QqV_nHg&<7N!X096 zLqsv5Fg_+a&Z&olIll1vWf)WbVG5-R3PGawt-w$~5Ql~H8>%2={{ZTAntvGm@H|J> z5z;jKC-7E{z+`_*V3Vfp1iMN#3{S`xp0j_~tFN~uly#8TZrML1&;G`!Fq|{f_Y)sTjb@>>cuvIL>ycL*OEQ@y7g{7(a zM8u+PlK`uZjWl`1z1mlunmg;)NzMYELAX&#+Cp0yC0x(jT`O-LO1jA^68goS%lT_4 zyZ->KDo1;t%c-$9DK9k*2Y*7a=|JkDon;PPu(`BVoQ6Z#pLdd|K7RKB%e>gtRks|+ z>S?h{Qc0~%DjY48q-tDP(@xNq!sbUt^;OHv&(%xo(WnQpUvJ!f%ekIIcjZb(tL?T1 zchg(naJJU*GFIe>m>&}o_#AU(cITdmPEVSBowH+zYVRcp;=DVJG98S>c7UVK+sEAh|A?)c>M(jl3p8+LyK=+$IPdv*$3N~AooV& z*)8(x{UZ#daHaNB{{XuWl!={$=m*9wymzN7uH8cR>qScCy~;%iq^oX|24KZYk#VGB!{4>CNO?~vbac~)C^sK{~)%FDM~?OUF$OYW#@rLVb)tbW1*rCDcDupp+#EDxU2-eBwJP$l%*Vs}I6H-FFMtG}Y?;b5*s++4T`e>G_1j(>eBxqCWPrOP_6nLbh-F9_TIo z$GcS>ZJyb$80RT}PkrP&LqSkUC?xMnOop=S9)RJkn*6oR6{_>^I*JPxIje&~t|G=giQCZS#qgD1as{D3sjYk@0<{@vJQ6e!)E4eOL0L7NSx;t%R6H8K&-iZlOb(NkD2EywPa!C zHuyqOFrr8uqejAUQz-fAmYkC4^;KK%sb#|I)TdNstjyvD?n39QUK-d%5LPqx;P$^? z?q?;XqkWR*F^|+G+mh4f3MyG4A6Y_fFU7sS;-O^0O_IoaOzGmgP~sjPIm2O=<5((iIqoTa2%WKzzOQpa9`G<+5w9CUl#tcUh2t2vUo%}=qK zLO*J6_~Qe_cxM)+rKF^0nRJZ=`9enNYpF9c_`n$(r_B15am83cV-D7Z7`fNn)0XDy zXzewdR-HXWnG|*DC~n>@y--)#Cp_q?Yd+=7uQbc8%vD{gYQ;@$GigGq9Z(Z_AayWQ zR|9nbP*QGkp&$*JPmlImSyN7MwVD;+`jLA0Pv032$Gsj^C!F6yMMs5KQeX;RSX z)RK))?16*miI&=|s@+(Da8Ol1-q15kN2KS}!rCf^X`n%GITf=MvDRK|l$IH#Nh|&t zp1?vToHeMs8y8$l@KuS(ncd4i$ZBqNi%?-DNusBj+J?fk68S}D=HR)@QO4Ld22mu~ z6WRd!Kx3*U$@AkpP*&8nTTsl?n4Ge=@l>q#OK+qnZz;U0f{nW+q;I%Z$+Wfx zps4N8CM1ih;1mU%8(Y1}%cC~l4&$dU_g$*WE~q;gGAb?*c8z{_>bf^RkSxvB9R#L{o8*jzy9JlgX?yFgWH(IM z9%5Cdkrk1jYiJd>kJ)>U+an(azy<#+tRD&33l{Qya92!qu72sjW z-9#MCD8l&pNC}?lj-nr`AL)W1x73Z`%3yRq80AyD0#<&oB?3lUrMeUyMBde@>cW&y zUoY^1GEcyFjweR=QPxco|KF@*mBP`WblKv8acm8PHHI1Wb;h_{dp70LanIMo?v zGxAq2)KLZ6IEsb5a~P;nXDhwg2(GPhJA|f&14)?axQNbqmD`CCSK_yh&ElFI`=UB% zKYFS!y5BUv?pxojb|r48H8_6=hVlC;N{W~@VM40l_k{P9qyFqw;vRq&anADQ`z-U| z%?I}VN*40QLZ4X{_Erlt&FnxOfD(i38>(W)Isg=xT&f}6&#|eq7nTf z)Iio>4Pd4_MBlemX3$#PNd%chgkw}I=$tz$pF)s5QY9@Wd+QPw;nh)hIwyA1b*@5N zo22Q|C)iCCET)AHuC8_FloU#WyO`vfH4F(QHFCT3j+$eug;{N?YiPY8)EtBMvzPMyRdD z01VEiIM?dFUZBo;?)dbF+a*d%x*<&}Q`hMZ6w6zpH(~R&I`rurVnq?{JM{_GwUN-l zCBbrtTB~n~m+1ngdYyiKqq`hViJ1UcroWI01%}pI%lL8ft) zC^_XI$d0}};z|wbBI&FuXK}uLlqF3!acNs-2-B&Uixs4fRr|I#!Akl%wlB0O^;`7T zux1F6A2^Z*stxB<(B_zR643gA2{It7tTR2hM_ODT6%sPMQiYxRY6+Q1)cm3%NCKLM z;W0NmQw=G9%_$n~^K^k+z>1DiQ-4;Cs*`m}_zlw#7;yz3x5W{>^NE=C_k7N=_Uf;; zR3hh|a%N5rkOSRBU}XX(dZwu$l&5j=fr+XitO$j5^uRiV?b_D29ps9PJkR00OW zta05^m?}|&RqrT`6h1Qk=uu{?=k-Sv^^TbWPQ45dzJ`m0BetsCPl!57dc)2jE>Rv( zuBFw0jZ$SXsE*fF)`a3&kyN!ME=+jKv@E$NYINu=eyS6>MMq6%90f4+qo%PowFD@r zp4ylpheX|VN@^TZo#_Q5Pw@372x4kNgw|oS0VRHzMw<16Y}Gi)4@8Z);`AsAS|=gU zLvIvaX?0Q(zROJ6J31Nv0DKCh?arwaS3;B@DLv+3SskiA7|l{s)g{>7Ft;ckV2~!|~rClf+5-VZCCh7o}Uf|3(vVxvz zG3)RuVDa}N;{NG4`FD01X^igN!V{6$(n3p%3I70$(C!r|76-cB{$X}|cFNj+64&iF z+n=`nMfX2q!adpY1=ox?uJlz;Ns-%9(_vRdxR&YZYbqUS(!)<~DGvZ7sRBgf&);)= zlaXTZhVKl}_cptSS`M`X;i|oMw;7ALyD?sM{vQgJ+kX*#wSL?F&+PVmua5n`;)}|y z!MchbS;*RA`;dBoZ$qiMT}z8cigpkOVIPQ``|byq=NfILo$d}guv8EJQKI$l6ya8o zOS(1&0ptGw6$}31{CVtK-?fG88t>2eR~&no#;M&;6zA{VEy$~7@64JS=sfi zjiV=zqlJDyrFa7V7u(F}-Y8mVh>8*Ue~Jz9K`jn2p2_P4kcO#1oI*#(;q;CKKqwiS zXD*PC!Bn=LSIJt5Kf^ANG^|g85QSAKG6bKVvB18nV|l*_rp+zXHNo%FT`d7grFiO< zHnIsyhlIyikH59ttsHkE<{iETMT=HReZpQgT!xiZ)@2zb3YpkH#M001h-S*>XjRf% zR!Z8Asm`Y~H!4r+mMX8+(kt_{KjJs=QcM)m9x9+^h0&T7s8j-VJH8N=^Md&=`L@nq zIDV^+hN~9{^6MW5h1QA`p1Ek+mIu-Wo-|}66p>MJw-WU;%BOv@3vQ&yf<93fJWYFO z;Z-qgT*FqHA=iAHBDzwsOLD0%w#1a${{V=~O-w|=g@WDHV5L;sGH;Mk$!US|b zV$TrLg$6-Ns+wV^s64MUL6(I0co+ii%(zvmZqq6OqsEEw$7?HSP~J<4SDTF;y-9gh z3@oOml}Jz_L?G@;l0YFsDcO5S?wDejXjP`lM>WD&PNSlcF-sFg{0lewX$7a&{()sb*VVpX6CO$C+mB#m5@Hb;ZA`2Wm%>+AaCZGd90ZFr>}s zQ!|%VRid)*$DFf_rsQo-uA1d*xl@nSGN1vNT7fbkf|UdEF`;5^n;;Uv>MCAd8Xc0h zsw8q3w2XTE%|(A3Ee|BkZeq1-1wY~&6sh$1#-oeo5;TD)Uo}j-<4hp?Mu7Mx=Y{#} zpZ%WlJK47$-m19UJ-5-++L^o-T70U4txcC%l|oj9A!Ve`zJpj7mT-(eCb2g619U`! zM@>b4#HaAw!y$>2)6&1LiF@w{-}f^6(6w>Rzm?dvo7WpqT`lxk4GI>^w*=iBZN<4# z(HlaO>Bt0`9=`~#_J6S0o*>CvW4mOAv_2H@2gO@CKX<0dxrRZswD>F2;$OapwN(2) z{{Y@`%INY}!>q*k#%^ETA}}<@Ep}JYSysG30FJ?aM;u zNeiXkKt85|>go+YWkTC5d&`EB*zR#`n|zHcO}%<3Y5xGTrI}J#J>z8URX&E-a=_}T zy*=VF=P3^=K7c9Q~87*8HpOdx6`O@NsrG z@+)JARJ0A&x|tuwKR!P0vCfOQ(vzm=F~jt=rE2(xyM4R=0ENwI{lD>C9P>2_@Q-!8 znN@5ve-O@VYVC`Zf?lD%Dxn8WWEO!Rw>&;Ud&FUl{Uo;yKZBV=`xl(^xR{SJEO7Mu zjX$Ii@7@=FzjrP#rsaRNFDu<0rbWV`oJ!e#qnZMTuTIMqMQHFknHc;!`%t|&e&xhk z;?}xK9em9e-H`tP+%2vx9jS9%KT6~S>^om_v56y;F3uhEqpF(!5v1sf4nTSu~#O>dB^~e-G6WRt1tfm+GF;8 zg7GyU1amG6hf!k-$*U`@`8wz+7^P+Rkin{HP9>D2Z<0uyqX_>1#|Lun@0)RBbt9zM z(BZg}bTsk&Q61^Chb4{|3}_BxwV?1&2ke3Di`wVC&Ozp`Hun{bRpgbJ{hEgBgbEv_ zWv%vy>YS&mJE+!zy$f;&r`9!JzYhBHt_hE|y~8{)uX~#MwXPijZ_EumR7Ys|h94o1 zdv2H>;6)Cifa#$WBge%>U%P*?Tz}YKy2{Qlz?`msjB=Y*HSYy}y~6!NOgyzR^9y+D z=cKfaPO>q$eWmxxZgs^QdvQ;wY1;Y<4wVLjL0x)zEXTY&4%-iU-=`mv_S6cKN*Wzb zr>Om^Tz#hg)Eoi#7vcU%$#U*k$oT&Nk$B?pi+xqL&@GSBu8OLWHB|KrZncLMH4>zm zC=DYE`@H+U&UVW)1TkAjJ+$oIx!ZvR5*#Ym4xN8WzxLO)c#P&N+k)PqN+c6p4_Vasr!`W{s9+9uZ;1P#8Xh-Y8!2*E2&+lZl$M6S_w$p7cB>9_D5(t zhR#*TE%IOn`^~KffvFT&pFQE<(eiJ$1eUj6Q-m%)s28WgbN1AAA1qK9h{(EyPb`#vt(HO${n* zP=8|E^1Xq#{)zSWvX|AH!1U16R8Y_#)_)aTzQH||Ts`}99nXbqe5Y>W?3$j*Q+%@4 zTdy%AwQ1Oi0xxaf@P9w|z0HEX`k*@U8KtX8^0E0?=eEO?L9qe5B zY(sDDrD+Za(m|*okVR`wp!h1q;;tRKa(6T0JRxwtP;u5lmR^QF<>vUZ#SOOTr&mlG zs#0cCH<9US{6t5oxrZ^26@!x~#yWVTXSqgob7@+Sz0xLwxuB@ zNs%*@W1WKgsN+4w_gjq;Hb_NW*MJQxN{SO-*sWamwAmg$*1B>W*BSyV*Iu>!7T;mt zY)(}BgJZm@aN-_J#j)(Kirl0CBS750O~(el)Ya40RWn@L*3Cm|rfJZYRHoF8${Hvb zW53^YxPIMSTel?)j)vy6k$?~gI*n;k0R-#Nh4%sNMc*Qc?zfSxbYue4*HKV(@H&sg z6$v@Zg!vQOrz+-rGs?D4=vmeUR%tKvNmzA~N<)ZsmjD56xP=8FQs{L6%tqwM>S5M4fpiKing0Na-^@es{K+zWrASOo!RRsp)QGxc`YeY&)4ZpOiD01;?jmNM z3V}X^q3d)21hgICNm-&@o5(9Ro)aJ+kc#!qxvIHXN&rRIHcHwg1FZCj;LW8>Hep23 z-LXo$lgd#{kTn8EBqFrjG)s><$n;4{ycVb+tqqi)5!N{S6GU_zo;uK!fw{jdVFGfD70A)V&_tq)Yj)+kV8a|lob?SN~1OFThyTr zV)qBf^4JLqm=gr*VaZ@5BuAfxol5f^;o((z{-JO|S=0Gr@||Eh zPkxJ#+Ysc$5GF0W`GgwhlR9=~<#Cx8_u=O-iT<2t6kWZTmu&yW z!5jYo)M`h6mea*yD1IC=SIlSDinzm~NjC9k*T)gygt!6;GH8UrbOA`bHVanWj6 z9wZ?E(utS6)jd^GMX^4MVt>p!sE>!^5aezvRJCZ0lvvv=4lI?vr8=Y3sLp?kHX|vC zCKXEBdjlxgrN~QD4u}U-%dRSp5W`3UQWA!Z5M}NwKV1!h>FFE+TsJGUPs0U&6(2K8@*8c<%K|{{6NtNsD6P25LQ@L2mipBE z^5?3SqNDp^=-Np{ zL>k_KZ4Hg4LS7~aj3-?#s}KyO(i=^Zvk;b=rU_ohLgLB^TwWOYaD}gSzu5z&(HJpE z`=m%27%{A>kDB4FN;*q9(M2;o=MRfwR6WK?DXFLu&`+092gh%;C|PdHiaKS0I*+E1 zu|yn_mA`Tj2qseL@r1e5Oo+)vuUE9lB&Z2>5X;^IDjO#`ChG4>RMOe&-V*6l-M&0G zsvvR47NS(5qG5tmh>Q-4at^YNwR{^zymo~qirXMMY2zOz>V(Hv$ZCF5&_@DhPKZN) zoc&>7>X<0jog0ufloSy1m8PvbK^_suE;Fhprb$(M5*mexelR>BZwdQ{QX;J%fl>{k zn1qz1q!oPS8lEK4fqDBl`p80axk2wn$x|&ny?&7HT%Tz(#@Zb^t2~yaF5T+D({qaR zePbrOHGLN0Y|2Iys`mA6MP0&}gaV!8Ul^6Ps0y`}8=kIL1jEr)XKa?XrAPyJ)8PZ% z-|R)>u=~Pgxo;03u9|&<>!eB#x5SHJmW~ zik0U)r~PS4&wI0ahLW3bKzYO^Mt&*-U&bDrhulDAw@ zB}Gc>5$vkGdrWAcRwwMQ+7)lOdRxX-S$1NM$z^=g{)tLIs8aUs+!8x{V_V={JYL3S zCV(##eZzO9_$#*A!qQ&tYvDyE8CaaOx}3R1K$_XWM==jyAlzo}Iz&m8ZS`-IQy^ZcEr>E5aGQqZ)WscEE>B>Z~D zE6RC0@)thg=)HHqI20HwAq;x_Q!b|z-l02HDaiJS4|1yi02QiyxI5dgCUf=g(#P%o zm5PYY@sPf-%_TEk)TO9PK?dV-QqF30VDVA;EYw!smU^Ci6z)!2#U9n|FXG%T+c}3O zyxD5;$(0Qu_NkT(l$uHSAL95#g~uU+#ilq#UFM6$@XMR`39i0h0HRgy?Rv>>Rd>vS z=>6jFUgDgquBdRh;>wKmDnGI*7gvv&`<Y*(@sR(nFrBhC!(f#A_Z<&hne={m-=!Boe)sgW>q>1)sSsHvoybW85|8DX9Md zDDl}{9Vj6V*0xAffN3O=&_}C#JJ?TcqI<0eU00F&$=~AF7%h!YMPc5+{@7W6Hva%m z_l=&)#yd9a)p<)Z$Y;qv zXss7JT&2jD&Op4@-0qc%f7Vip0*O?IQPv{w?u@74r14nh!ABmdJiBjw=ag2Nwp4zf zj7Bom&nnF9ix@OrK>dquSZ^Bf4>D%?g~FQAX|&e1NkIuxWKzlfvD{`h z8wl!ta*esmTxKTLqA-%UCI@2T+DtV{AcL*oO2$lG5PD%QGZ zf#8{XT8oz|U(SD%v}x=X)hWC}&a)(`0MFK1d{1fgh;Y*PRH>r&#?aQIE1hzkb!lyK zl*IlJP!tI*>XVgKVx5N`T#ynDr2Hp1M2?_O+lsn?CKDIGR%9HaZfrPJX~sj1ppeHu+i z!B6g4$xC#0zBjYJMb9J%RrP4{{*u<#g+DhXC^lz0d4mT)sg{ipKwf&4jh2auJ9{)t zqta@b5^qTWl!~`t&f#{aWxI0@ zKB4NTaP-hr008SbKPGkXb+)YU9oJEILS(qYx>hwo8(Ogon1v(r*V3a z@$|Gm70M+o8pEYkve50+=$UHD%4)67GWs49mz8N(?MX3UXaFl!tbyGUF5K!FUU7?4SD-bnA)eh=s0K90W8tRl%>z#Fo(u`+B84jgg zi12`CkfSZ5sYXnzr*jc=$VgT@IOrC+l&wlS>(U|}DJohexu+(+pGfOmQiNdjN?KJA zfg`WPbc3UM;f)!x`htYd%cM-(D2t}*mzHXzZbsuWNb^M0F4aNYM^xrfRW-0nQxY+B zQF4X0w&Cd@?M{-^ctfszLKrht-Q6y#D^!G~por(m}PK zjxzrMRRRI(eh}b{ufC-e)gdhj&T^TBhET7aC`n3(%+9(F@WY!_JyBb=F7{BDSm+D2 z(ug#TKhhC@RN!D8F z(8R^NSWb!t=0^jnlK7q(MBqS)}UkG(ZQ!)yr|$ z=M2p16rdK*>T2;Cz`&X%maG$8g^*PsM{k4>1yGA>Y8oU7N?sk~n^8cajZ`%-nQD5N z`Fuh=r**B#)?OwXk#Hpg1(gZ){hi^GQ6SVMWx2+XQz>aF!;C4qdQ*#OscaN!E}jtB zajI^K&$U$puq9g0Qw1cDmy`uADIq3j#80es3{v9PPUrxUx_v$oAqpAYP`i~SNfJ=q zF*%Nt`NM^FCs3xc_QhHX^*cvVC<)~g7gX6?3$tHMAX22Qx};@d)}pD8+W^VYP22TJ znt?g#*Qol$RL)a&k<}7Bz&d{pxyJHAlWO4v8a{ z(>cdi>K}4^2$N{BrlkWq!cn;Y07JMqcRbqAp0uu#c9}^t@aPZ#jjmjlH`b$~nbvHF z73b_R8Rjg?8@i>Wx0zTHQ~Za@WJyO-g-=L^o;{V0K~_09J#>3Cct}rn{^>pH=e&J^ zaZOh`UQx$59WUtZ_IBzN)6Ou2gdq=9HMMS#kP-@v;)SOv{Lur9G%7M62`nJNM0Hh2#xojAhn3 z2iAjM5-IZ2z*N?iJ!m#>`xbH*2i$hsj|2V0c>e%u9~DeI&%|7t?eCH@4kYFbN&GiC z#?}7-Ic|5%#Z5YlnyaFG0Gf(tK$;y##VQF{Qb*|b2IlzQMUQRmo5SOHuf%)^@ITvC z=1kTW>m73IdxnYh4q0u#>p(;=2ne1ZexkU)HL7rMAp^@>Am~~$OP$1`2 ztEuXHM{2$SVq8emtZ5ZNZF z1@&suq{tgPA4usZ0Q}KWyvIuc-5#M#dn|a>F0zbL$DJ^X zjj8r2uJr?RcP2Kbo~nAuY3PM^oN~`FMvv-(y2TJQOnn_rVaIpnyF}}@d!4;SFE&Kb z>N}-rK2@bcB`$2>x@Nslc6U=-J5Gt$wB`10L3x@=L^FzJbqYdht?6H*O<4BuP+4%x z({pyCz{NiE8t58B#^}%X)m$y_TQL$a?RZvAit}YBHCe4(PwovT7U!H2ox65D$(2b; zKd;gzMVfno00Qzyl`el5UxZ|LxB-oj%WNwXwIj>^3iA$F-i?y{!#n}#298#VQ9u8GL_q>hn)#ODYUaH_0c;hEGF!(Hr`%Uaf( zoxOWU=5ZovBk4G|L2}0m)j(WZJRKJ72a~f7E6OM{9zDvd@SLXBNuj*ZzPfkOF{s>* zpvzE1qC1oEn+(ONx4Mn8MtXuO5&U)kUX@dUn%>7-aBRTjlukt92d2YUZ6h zYqp=gavAI=n{B?`{pfHvCf#3cHtZiSpmyU>l@Cw!?mD_B7eDZXJXC%W`pe&b9WHx@ z-CIT~=fzMwYT7&x#;v0BH*fhJq*fnsgvmnts))^4>V>Y&7nBfo_C+ zK&kz!nos?oerdFEZ`@;#sIN62q@d)xJ@*{-lw{ecsA0O+LZ_yxr67D`-v0pk0O1m4 zdwVOIDqwpg{Tj6Y0Qo^?U5v~7mk+hC%n2Wer3dNP_N#0E0NQ-xJ4Q>#+zHNB#!}_S z7P!}A*JsnBlSgoEH7$cKpgP$3N09#j@oUYcZN=6x#O)(+2hFGa30g;c`0*H4z2nr^ zb@(6n>Qoy40NJJHTUH6~Uzqr+>rhxnFXF7!+R#cu4SqXXTq&79nN3xlvg{0OANfn+ zb7VM`z3gJYO(_S?k^%bxX1|D4U$)!5@0QpPxm@qr4>+^)f7;%=iLSM&yIi>Pvna%GR4x#uwM$%wD`?W7e}u7AHvFT) zKYVyOxYu~2%0@P_MMa zGk@9#+;fPYygvOKJa@`Z+*LKxxbtI5Zz^72NF5pMy=l(A_xw7Q<7}uZYFw(wl*Y)Z9SEc^u z@GrW;w&9~#TnYR-AJ73O^d@Wn0A@#+*|>3+xnFPh3iT@EhhA-&RxwQCgug?1rd16{ zX+30zsX~>fRmG$K04p3M!;rAeMkrj<86*76H3a^L06+LuYwe#czaK5JHQ4&o$*u!m z(%|pyMMZyj&JG^lKH&V@X~*f!rqQ0%Q)XD2Sp|~PuLVyyq$x7<3Z|S?$3kQDOYHvu zpN>)6>sh2biyC8fb*|Cl{{Z5UDezc7dax%K$lhWFqDnoaX-6L;<_HJiO?+2Y{E5eS z=iMvq4To1#r57B}Cc>z%IBj5XDas7W!%OKcwJip7_1IfZCVGNIemwAgL2=qn^-BZl z+i!WyBcOC{Ivsq$s6PUy2yoM_e@LHS`$)dtX>7cc!#?GxX+v!1 z6wl)MlgLV@y32A^Sw^)>NtVlrnbgR5Mi2i0j6UQ^d4RaYJnL@l&HN6B`@aRCebn)0 zM|TbDN2SASwEZLZAJSJY>ymVyvHGOEO5IkFLFpZ;bcLHl`l+8|4@PxJka|<=inu}< zJvvCmzQNiovpmg8d9AL3k?@pEvD8`B+cicjQ7JP&NK3^+W)niRvgr!fw1h+4#;g!s zP$h-7&gI1^DoK)tns$!CkEu}kT<~C^DOpQ}0^I!)sg9%=lVyjBqTO<1T2%q8!bFJl zdq>9A&e19kNnaGyhwYDa%x&;mA$|S9xyG%`&tq9JZ@QC(MDcRyWeW*>YcLFsl^GF+%p5D;-Qnf zarsxHxE>A|Y3?Z7K}(-kIv=@Qkwr>XGSDO|$E;T3Z0D(3?Ci(2v}0;hwPUFiDG6ap zsdZnO^%?8%i6|KQD%`Nfd^Ac{+N`(`s)dzKdr5+WY1J@p19S+|p4i({QnmQU-oPFx zZ>n)d$y*En5|B3cpIB8z5&&^4O6!x{%yxnO)gaq+3F^byQXVkgtVwnySWgdIQANaH|4y&lscQj`@JL?hj*-0c~8B&)Ybc8L+pTD`Q{w^y>^PelM>$RMl2 z9{V6fWGgV?I#9)-0Qo>jm(?+(V%bK8gJj*gc__RD3fYOa}eEUhU8B#(%cOj^fARaP$U zTIm(VW7$={%VFX)>ls#pN z*ig9)>3n1bZ69uyH(||GC{NWy&%m8yQ^$|W3(j}WKUU?h-&Ja;XuV_tqfh+AB=;yY zkb@q;MHLq%E6v#3)m0ZLd#>f)NW!T&&aV2qW)*CZ7P@(%PXzS!mIx~OK(It3`BadO zY2^--)DDTO;h%0ihwcX@Mk~j5Mcpe;nzG`cq|(whe~s59K==-(4rV)lT@tfg>Kt5k zI@8G_xm$&@UKZp#_Z#J*uj}>&gw;6Nn{Fj(+?6DrpoojQwf5aWtbA{kvn6aNZh)y# z?T_5m54PM7GpC`mJ9*DKiZ>qXP9LCkCI$6?9S%9rRMCi*WLmzqP$N^0&# zTZ3&je9~9SCoJ*M#LsqfLFB7Y+b2@$h%PB%6OsX$KREK^BFS=l&8-JvE7hiKR~WOYrs^g~ zkavhOW9 zx3(v*g1=9hn16}(&bNbPhFinVsn`HHSTY*ZRu!H zUA*lHk^cZwed>3T=stpiiq%HN1-_NKDk@Y2Q`0t-p$I}mfB`Z92*l=@@;h^lNnW0U z-H|hE1prA5hcKkGw&FcNw-Y@kI*6IY;oUoxGd;XKUqseF(4m#-`9C{UQIIkbrIJ&^aJ8dBq2y7>C0Bj*Ww9+eAHwHqs z3mZmT#sJYr&RgXs?ex#I+u)GM@%`bR)f_cQfXJIcI@<$Y~&RZ5hlA1Z>h0EijR z0Rq^|iiuTq6*qd)lmfN7qLhhDlRnVHs!A{1ED3dSyV`h{8*;wANmfgBXolMp1*YXa zW0Ob#piElAWpz(P&|>r}$m<^EJniq%*sZ88Sn1v}LN@E_5U!-lK>;*|>Qv)n+;+7z zSBtg7o${3}XmXE^>`oa{ZX@-sC0Z?i4KxuR=NTXHQ%ntspA>hJ+?b_HcKRS0X@C&Z zX%Y!Y(op3nxe+I-Ln(XYr5!q}=^Q0I08Hm4WUoAFv3m;rTfN(I7L=V>Y{(U>!{HH= zIJoGiEn;gMxS`mVGR8Y@r=($%ym_i#Kb>xwOq271CKaXUq4 zE@sG~Z7NnHk2sTYs0YbVw3+Q5BgP1ZGbz`gPhL5;u!fJB-?13`kmQj%PA$Y15|$E_ z-4@9pAt@3K>HA@8fi6Bo{oFP+LHshj>`HfpQ3#0VZ4TPjPQg-V(7>&4TF40(R$lI!C zITq(q!&b}^#Wyc`Gl*=ZGbJHrLVg3ei0k=lNZZt_M;)|lL#LDFZmP(5n+vgUj~G+6 zSfsRzOD3}ST69y76mC?=@i8YM7S{V))lz1-lOcL!cS@%F5nXOabi8J_kNw`soJ*AL zk^cY!kpfLNJOs5r82cA)LwBB~Co^yy;MVjB(a1`vF${{eqqx)1)GR1zDK)p{_#4|OR^iMTX`Oe6}Y^dsXq;kUH#3*(i^QFy!`gaS8yrr|H~CawAY z!wMF*^j!X@kfP1JFN07~>(yNSA^Dby$HK_`XABz~Zl!c(GAE*!{9El)hw>}|u10O^ zwG|b`ZZTvJS3VU`8dlXVME*iXhrBfC;%~~MrODkq(nHY+s`kmoPgJ+*exbFMOL3`l z`{AYl;<)SAZA*+N*t@)|mss%5J!Y=&kmL@Qt#+22DqE!umlP%sO{pZ42$(W97dX%+ zV3Ou}Ln3K$DZ;#Q?s+>M{EIT7{&gFE+X&xs;PIG}K29 zEvyx3Pg`;cA0Q5BKd1f*wV$}D2wP9Rz1B|G6ndzqNx-r)g=rS?lF(9Ow&0nAyX648 zs+Nc(l`pMQ3r4zVBZRUFt6aalbHfga@iY&Ak#GunJ$E0<`c?U;ro zWg~FV7_ZJ>^qhDCtJ2WntXoi4rOufCpHgvy8pkC<}=LDoT{xam9RvoVeXKBZSmn!3w2I)O{a4H63Dbah+88Yv&bW&Q9Gl+{UlH3=J8R{EzfW#6nAWT-$sI%tz_ zyrN9|cQAyI(tx<}CO~Wbk@DzaKwNc1ke;!^XptcBS+?a)?Gkm4BBKanPSzxeCVmjG z;*jFhZ%Xybb4W9seOCg9aS3N?t-7G3q?r!dm@+mW6w{grPGzF;du>S@W@nDV`4|4nuvp>yM__!a8b7QH&;&lwi`5k$y~&9 zHvo`Tv{cNMDYa`YK`;#}6VYy_sD;vyMzTII(J021q`C2&At_{s77_&G<xm&c7!r<=<1~$Q?;K5~Ju1|cf=^jTdM}dN z*-}AInpz3R8k~CyaqX8RqtiR|9d(5bP|j|N(X(F|k^xu>9lAjQpaj{~Io6x~I%JWwU9jD|(fsPs<2xCzUKr@K(<7Se*W-T@^F?0yipv=E`6Zswg6eQe6By}o5O zo?7DxcIa!hqZmug2??f%R_0< zsi)3Ad0%S0O8b;{T-au}T6v^fEUo^t%$;PVwss_G+{cqRPuXWA%j-LRrk@hE9KU?5 zEr;bD$Ka|@-e28Y+V{R}2GfIP+$EA=e3OeU$9UJ8%eJSft!}UL)77D;{dXxJ0!|<# zfJji0PT}s3$Njrz_ypzcxf0vEt2J1!IqoHve%yPE+bB98AMmH1s;XCtbg&yd0`l?E!OOVm}ZiB5_0ZAVcpYDjEF=3%r zNi16S)v48?t+rn%$!XM8Q@A9tAap%J1`=A=g&bV?ifVFPLbk+T?X&%_uSyCW7t-4c=Q?eCXS%=`DH~eY5V-HFamR)H)-&wj8HX zK}ORqnrc3A!e~&D0N{!0E|%(NS)!_{6!kCv0K!QcYoM9!4vB!#5ZhN4)dQ`iaRtP! zI%%v&NYYUeyF{OHfMz-o-Vs_NEy*PZnvpHqfz?{W2UR5-=vK|)$!^s4fu&$ZLorw( z)v~0Xsqg-%5?HDf)x5;@P9`s$-E0m8QdJizS1(qalPL(UU*ef3z^U77k_9{zi?!w$ zZy`}NYsp;w1<47O4dps1wG9#+AHl2puk;)Viw0usgSz9^YqU_E+WaQARsw`|GmV+x!wao+{BAsw~ zoZFz8)*>(X_TXI~L*S&Xb7Hi4)lE(*_epQGIZcUjmPtu-#!$A}x96^?dX#Lsu-bif zAI#bT8l6OB+1?d2lJBNN^ud%feF4~yvNtk{nrSa~(8g#|6aS*?P# zx~dlqYa#ytMs2NNEwG>!^N3tTS51_*Ky+VEddVB7il|KCKx&zJX(=yVZN;x@a4wArP!lRh+Nu2| z5)n%y6nyZsgqEzpY|Nom3_hltPv-BT9imPv$2&wbtedu}*yB%Te9z35q~9=WQ%J{L zQ4C#bmf{lOPhF`>OvlD77WS@tYE>H{oQ4bQ=uji$)OQ*~`OFQjO40V$4G)S&mbe~-6eb6f+~clWG$xK|s8bW4 zzT@8K{mo#AR0Ml;*Lww^J?Z;jqQGaQDs*Uf2M zpS{^kt*6t^Le#-~clQIFdu8S<8=AQ7*3pw-wSTFir+atl>JYRkrx~ zvPV!x5ZL+sSEW`k`g+JMp^dz_SKE;HGRpqmd0!wpoIPBNfO1n5iNE5%tTd};vV^bFt6-=pJIYtFV9H<* z2!#8k`&P2Q$%4fQpaA`L6bdyt46^NY^*qiS#tnsM5crP<8P9 zR8RM5&h`&*KIHi`o$#z;qmSq_`u47LnDeh8$Ed3i%V|=Bi~tPWZ+Id$uGMf^a2#?B zT<;S5E!$rLKTqRDO6Huoxg5K%p>MWaLHry#AKm(u^K;MNAKUM_Ph=T4w?0AQjG}{* zvdm4ZRmS|%ZO>ZTzSGj72&!#fzo=R+pes}bd?WMswtryxekacxnzXrLwq%9DnuivX zS{*6VuB*~~gYFL#mcc&KX1T9>Xnz_~wV@vsR3B@C zM%6QQHup$*l9jf#lB6v<1K60)KFogJv;Dlq&t;I2vNA^pH)*X8{)Jlm*W7$of7*Su zceAM=jSq+Gr|M6~+1u|0#-7XguaLMS++%l|{0TMY^Tkz`szleWilVg2zKum^$Gil;3jGyBf_C%xyHSaP_hwmYERvToCM-<8#`T50$Ir+YKr`&=!Xt#0eEIF^tr zKy@Qej=!Z=&g zY9>92+AbZFvf@zVGZdCaFvjg_9KtC=Yvf4$7ERB&Ev3$J*Ommeq=y4aQNY%;KY;L7 zKlcRt#PMIgUuc%z6RP3+_C3o~k63Ho%jz7quGHPi-lPiEx9L1xDSbP_e1M7TqcN>mVh*FDppSoi2& zV_!no7WRzRjXQ|o>YI4cJ=m!Q4Gx_(`bEPY-%89g!&065TBWzV;&K(9UX4-GjQdB` zK!HW|DhldSfI5*l9%j^pdHiI|Z7=$Np#s>pdxU)!##M^NCYTL@pH zEhLdBn-&@@V)vE$x}tl=59uWT04uSagbw>e{f8^5>X^)<7b$Ji=m`nV0g~9!YB&t| zE*hw*ezb~9n$+S51es|N5;Qf-e#GC2ZS+U>J9E`i^c$EuPDTiXZs?&hxdun`6zf}X z+NY2T!55N-E#3N5_2*Fv>h@OlO+gQlEs zC#eDw+aXok&QUAlrBmgN+Uco~+EUP%mXS4Q5CLV`o9DcpQ<+6d1eGa#Aku27H#4C3 zHjtKXWI+&&2 zWkgP1m%#9s-b4sU`Yv6sx~(ly&AsGoQ3t-BFcUL-&Wgt;ZQ+Pj$>zr^sZ&miu0>Rr z2-Phpon_KML+_h%4VV_j??}-m@_eWIv#vF7rVTKq5ECwgr)X*HE((6iF&3pES+c63 zK)#|%jORbu9FE-WsyX+K2~$TyOG*aRf=5DrP_{COr`#DjBQMl6&;vq#68^~HYD7?< z6cNjbGe|mkLJO)>;vj*psHh&oIOhaZY@#lH;Dib9)OUdiMzyIkU6HoRR;4%sMCCFB zH|~U77LIyrFT9UUNxin5FTMOJNA|!HT+^boSm4Rjgwxz|i_O_<^&TuRugcO-_rOnc zinH6}`RXLWs)J?O*^BHo|x4h>ikE zC91q3uu_w`TF3(oZwg}D9(t6UsH}dZ`Pa2S;ShrIbW-ert>}YF8cW{3lzh3pZ9wiv z$j8YGdPv=@bR;^cM0GT`BceW#q?e&c{K=1!3Ka-QLYos*{{X`dggc5H!gnZ3p@(7# zi46i%(bMjii0|GX2q{WFX5ObIF{BpQY7{-y=FJhk6)in0>^P!7FxRHDg6-NG;Z+el z#qBg!3&UX5TbWx)lVKu8UJ-nU|ioL&F@{8MBsEf*dz(cY!NSyn}oH>U%gB~2>MvKrKbB)~Oh-+&_Z`@t&6&5=y zRdomy>e>`!k)Z>|Fe{I^&u-&mMOwAUdj$7Gyq!~1%6R>DHCIt=qE zO@MxKiF$SX`-^( zpKjQv2&lWQvX+u3BO9$f{pD4Cb$OWR6pDTOz=u|=09J_r@R>Q+ErypS0k5tU9ZMKxi>*L`Ax#3uxO;MGC z(SFM=w6;uo+`E;1O462T+Cfny_{*S%3k$SwigszEft#LmR(r)f55~UT?JRO%Iy%MU zElPb$E*5qzD0HN41R2c1VbEE(bM<$Ug}1f3%|B#SYdq5#%-q|WbB*`)S8Ahc$F2Tc ze;8-WO-Ru4fD)MksdX__%4DRueM!-8d~1q9kAO4}SBXs;X}viUr2E7$R&x@`(v*>; z)^wCL?+C?8dK)K7>NF?Mhxi{WQnvmH z+ZRsY=?044EOwqX_Q8iGh8RU<{p86B8w;*GEjkSZ<0Io7V13{-!bP?{3A;7Wxe#|+*2T^zj zn|CiE$ZtKU>UR~?uiO6s?TLr`a<^LZ?o6Sk$vH`AOO&*#LLH>E6#__WboV>1t3ODl z^1f{QqGOEFi$&latcJ()rW>Rx-c+PCjl_hZKR_cp4K6EZd)pgobP1@+DN8%r2eALRaAq_xsjJyBcd)aqnrBU{7ZDMLl)%>Mu| ztwAcKxlit&kb7&Y$rFQ9(d3t#(tR}0)fIY8DKpx+B+o*Auzq8V+Drj|P^#E|;a}SB z5&W)P?dO^9UCep@UC5bZzpco#dxO^trAkw=A=i~Ml&7*l&_va%ssljKEc+*POoi?~ zqH2w?{3@=s#))o8m?KWzAj@l7Xrmu^=O@CJhRK9mS8%UeY$>FYcga1v3HR`cStMjb zR28;9ONaDGyjzH~&vTiLx}nN9Z5&}l>QpLLWU8&FueqiWh+3z&ukL}G>Xcjd zuq8!(Rz1bI^Vudq;ul&IItn62QtNAN;Qa+kuvL-E#wSw&JdMT;nuY!7Yeq=^4$p1dUn%6 z0!T@AipK;kft5W{!o1q>J0(4W&eB_OCRGdWRV_)ESXqoTBu7~dQ$p+Nebua;>m-t? z&#W|wYiN=Ao>fPS+MK9#03bl$zi+5ml^rrqna2)+B7YMM%KrcbA!QEZQ_&#sOk%LP zQPofrkabE&$U{*@LcYDe2SLyw9IMDwJa>$_S2VqQxg|)m(ts%xtjT&by1B}-DGnCT0O*4Y?iXgUO8 zO3RNaEw(1LY52#{w7Ag+JfWhcKlny2nikuuc_6!?>Eo|>wbsdXnt8vY`wbes_&+~Clhu(WIunI!Q2rARx~($nqxikhRLLRWmb zptEjBJ6v|5N!7(lMnZ7q{M1y!Z0}dsyO3WTH8&c!3(h#N%v7U2 zV0#80r>dIFUjG0CIt3%x%Ic}uZK`AjQh)+VNuTbC=1gd)li0MHolwf0%Jo#=qN<4{ zLupD!_QS?U6g+mVY3PV5>!~a@y4&^0jg*N}){9`Z8d8Ne;FhPak7xz2Pt4&&=~GoD4QRP2jZEhUd(;XM@oKRBTEHbvll9YIgdj{; zeS%gdq!BW?lM64R@3a!93did6e7eJm(77s$)kACre}M`*Q{~UBcrx8=#rHe#@6d;H3{>q12QG9kn>y!N{-NngHIIC-KrJ(>cc%$ z>i$Pqt=u?=t)9V$+6grBl%*M&z=WejK^H0SFbA{%@}#4nL6qrNPJ#nsIixLn)KqnW zrU;mUsus%n9}ck{anVg%A!%Akmb2{x?m69&IVu`e8lPw{*HGCa zt!569pm8A?eO$8cZE+}N8KUpI*&hsXW-Z26OTIm6p{#PXlo3{jnIM9m#1D)O&Up>k zliSoZc`5GM@l1J#-f#|i^VKT7OI6f1;5%9%_M9wrO}q+flT|4xR=wb) zBdCg89sDDyz=Q6W=JNPQ$VI)kQi6{2oP3K$E!2Pql70pZ4jxESjj5^>r6xi~8Zrv6 zi>MO5-lsPtZtJOswiKEIoSQ4FDr^%iMv{d@;(?S=Nj^NKZB-0-1Oe_VK)8wtV{r`k1jZcd~Z%Rmf$J>RZ31S6nu>s8esG zGz~T>RN6~B0(O+kNlUb0_mau(9miGcEKE@Q4KaWL3NU-N;{0onxZ2?D>9jEYr7B%D zVx%_i5-BS1F?8VB2QuSf=vH0bY$S79`+!lwanbJo0Ez4tXYNkYUfXfht#fT8YpIl@ zSG*c53lv=46)@(uX4tEB0rF=#NccrgdaKQo%{5Fl+L2AE)P;}%J?HR%9L-dm6e$x} zmr*gike;Fjl$2uts!urclN}}68A@3Cy3DA@5i4R`>HFb?IyPa@BoBC1Dwgn!Sy+_8 z_=x2QNg5*xWtr-FbcB`2Hk*+c9d_q)_*nUzE)M0AxBDs%W?;9?{3w zYQh35iVmJx6W(3mnBZ|iPjDKS%i^r9iV*u3UB*U8BS{aDf}d}q0Ay(HiJ-~xtiFfT zE2|2ZRTQ7yI0f*~DQGVN37WaHk+4_l?OxL<>L7QD1uF!>BC9K}ENT=AwL*r*(v6|y-_7)b7=}8V9p$l z5_+NLx(O341!Ff)?kH4WCYUe@U+YL%N3&Nt2*Y%M~XnXr`6 z+_uS2u(BpeJCb!tj|6sK?n5ER@{TtozS>7rk1DO}ns(0HC2Zerjg^B!xt!#T3p(N~ z^C071O0c<&W*EzxEA8t$+I6MR(x|6&03N4w3AW;T>Ie!157zE2%51Y1ZMn5@bZ#H3 z;C?I0c??wclNhEOUf=Cdk-w}_2Wg*OL|IOBMoU(NSX|L8AJELk3vOP_GH-v(OGb};FRbaNfG5!guTUVS; zv_DAdy%1)V3GGnuP>8Y&WlWJA&DA@&#%*uCgi}?KPh3Ql(fq6Jp)f>qhjmcb7}v1X zy%ASx3BucfuqIt<3eB!1%vdDR&M2yF^+t#xiDjkc>t z>X;}@*ZfJC9SjiGji@IdR*O2Tc$ckAu7sWJg0UbBH}pAMn| z+z2;T93oWcm z&IuhLcfb!+Y;%#(Jora1-Ldx7Y`KJ9EL9_IWwM}pt*sh zRVc5>DQ-5lUPtR$^^gjXm5GD2_W}yX*bv}cgUMu`_=smx6}WLajWHqXeUgEvL@ozKa?Bqi(phZ(@Z4NMp zP!YK*{vC7%arW5BtyHCjmD5{76F10QtHt%$^E4KGj)_+WkQ;flxROZfb?>YpGbbOy}AoUDaKpfz4_s{*P1oVCq;Z2coo=vDfIj z;Y0)a6>8E}8S6gdtTNWoI_je3S?f0tS|(p8cW_7qttzut0DqFGOI(`;234ZAdQ%^q zDXvaxw7F=nCrV`*_0l`;1d_FXMz8)FWlQA>4_#UnY_AI2`ehT}NK=aV1@f)c>740R zr&A*d4oh~6NTsXk`epnE)BTb0E1Q-ElBRmouV406{m|$sC2UONi?snI+XUu#^7)c!@*1UF3&sjXo%%K29&AXn&*nFrz2$6*9v#a5%I#=bM@2sQZ+Nky+wseKu8x!v^ zJavza2?>k${!vrIiLHUg>A-GtkO$`%9Asd%d3&B@w%1iQteCo0OyBavz#X;e76EUf z^JYspclAMUSZY+;l0Y);2z8P1OjvR<9o0s>-x5nqygdmfMtVeC_L?o5i|6+^YN1A1 zh<>K&Fo`7xP&##rz15I&m!~j1i)0CDiDIoV5DR1Q=>}TbwW_7abIy4>r`FwKYhy$< z4uWPT8E;jVUS%CpwOnoCq$mXR8bbw}LS3ENaY$!cN*_J;p0EQ)saja_Se7Ei=R;YA^egqJ2p>3nLQp}?nPC#e0= z5M`lSrG_cbV9^S;W$ZP#bKx@YF{~ELzz0-4?--HZ2*$6Fm`V=U=A-!nW=tYXkO4Jk z!X8eeM2fp#?$;zKZ6r3#jEanj?-2Jlj&`c~hvP9u{+g)akz-=-cr|wyy+ot(wkBn+ zu@LwR-BPw(Md#DOvAQ9)`#mcqB8r$cNAW|ETD2}~;^C=F8fqt;2sJO2B|nt}nSK!_ z$cKuLeaL4-WK~N~GLblW#)u~p-jy6B26E)54vW*kX#ly}shHsu zTED1DVM>`X_VAphh$GijHPvEp+uf>MQ5qr|+>H&TM|Pd{5Xp-n`9C^;vrl#kR-7d;eaq`-zS+t$=4qNntampZMH@=Dr-&PH&c7ar{v)QOP_O}yxYq!L8C z9b<{1(GF}4v+iT!nsUuQm`A}dQIidl2AxbTHcC0CXIV;SK5tYMvjnuZQ11ZBQtV!zVk$i!pMHV4+rlk>D8L!B1^wi7(P^E#;^_-v>i=Q$w z;4&yaD(HQ?@hw}HIzsCNg(V;vNRzjq(k@bEqqmNSs?C3;Nj%1ZKY1?(CaOrNW6CAw zoWe(aBOT`*#AHx7HELW(w~mr#mnRdCusmxBr*5s}hSuMhICjWOto$SMg zDr&v6!?1RE=z({CA8+jy7snqN^@ci+;$m)_s1qiSZ7(YT6shiN2fId_91 zaA=?lTgLG?EZPG`sfMX}3~x$IrTLG_8jz{)YLrfI6>#or%QsqT8$A-)qFTr5u1Z1l z(2442-$-q+*Hh6&-(+Knv~I3eTW+`UkF_j)EL!NnS2Qkhv#=26yPC>G~3 z$cEN+^G{)Tecn<)`9&Gzq2^(gWPF8te)*By7g*z}B@HN@t&YJX!YdbdPMFojZ`h0t zy_9=n6h-v(MJWL(J4bnTfYg;2hL!545XCxot-~My)+6q&+Z%Tj^__&1KWjpm`~~e+ z_np)+cD^N4Hk5>{?@WrMMw-SU%=?2%vOcTP+!wbYfJrK_tcxkbxI)I>H;Yp;ZwW@g z+7OfG=`|5Od?LHTJ9l@Tx(<*UD?d8!EuIS8wWI??*zn&4^6$E?N}{o2c4~|D;_Tp4 zj;Jg2v<#IMu)0r1quH_CZz*V1Tc)xYqL-@%QD7XV(TJxx1SY3P$#T`_{OV;!U_XL=MGcK#^MXLT=jdt_spjquE(*8()Ck>peYI` zZodehzPe-$+TN;E0=BW1;ngXe##%T% zBf&}xCx;z*331j4KpK*rJYv;tVAzJeRTg}Soyl{M(Asc%2OV``DqtPQN$4PJo7Te8 zN}IytA2HE1*nSav=KI3y?lkVF3$-k%w#?FM6q7x_D5YOzp72W9u-G9JMw+LR31g2}Y8b5{zFn$S)l(!O?2i>|B1>(^Vzymt30^dd?)=R3jrUP>9!7 zNY*tkYb!DSGsf!jOZ(_-(;{uON3C>SHqpL! z{1iL2Qslnka`fJ>E<10*?T;TxDpZl_gEZw!aS`CdFqn=00my}!>;hrQ| zrm#~t7cjhB&Vr?<+*d+Kr~WOMA#cwe92R7u-JI79Bz`30da8$*L?nUtc^)9 zvlhUCd!IA@36Z>Bp1M`K>x_R$3(==oAYdl?48BJWt9SiV{sxSqqlL&4D|) zh5pBYW3^YitD%ZoB>w;*nT^h_UQ{G^B*L!ln;U*AHxG<`+@JxXt4!-Nx##&MZT94% zlscj)=||;Zui-%XMLOo-$m*@a!o{}HhP5Y{XiRA{+5iC*=O=>)$5!j9seJ%WTT(=) z{(>j$Eh=hNH$9U65S|O|?vog@zgDFG06m~nO2E{*;zpMBXVx81X28r{l66&@|?!u z%bBb9%Du_6P?a@cqa=XaK>lfXtE@vY&Xx5&6`DxfX9mIZ9>3t1w$}duPTNUr>(fcj zUl_2*6%|J2Qsk>=aY{kd4u&7wMG)kmq^DE9b$LP}L4!pfG?g?7q0C#ka%OLiaV|3A z)+-)Lua2T+C~YJaDMdY!KWtX-3$7*+4xT@Pzu=&4g{!WfHKhmQtwP^TkMNfq2I;~n z?U=PHQ%=wmQ}2|7r6~ugDvJf&?TS5vMVi^Q_g5xY>r+)9@rqi?h1OrL^K$QyI%?}y zNzkuQfj-F+5`@{?>=pA$$X=NrPXL^HLQ|gTC3F8$x*o)zGCB|MQ2xpa|L!igl$ z3ud@s|NRF?eCC%x%(0J@y1tL%6oVHUAfk0xsAQ238{4mWewZV>JLzqIE7An5ediM zMKFHNH0rCI`U}fBBNGaOYftU*qN?ofs^`bnyk5}x{Y7QMnAX%x8HB#6p(GyRA}aW4 z!;6beMOBgZVXbcAN`%Q=b6e|$YHfv-4#IIc?mVUaEMQit1yy~zRJxy4rM9W+55^oX zc<@g#vDZSqL-kHpP@ttnr99YLwIV@4nf#!*UhgDh;*U*G`>jK3xYj9$JxYI7UW&iO zKkbOyk^_pq*+N8N5;;(f$H&3~M~50w6|M9iEaXp95z-S>d@E9xkP=hhtTj|&TH7*K z;&eUYK6iwC9Z;mKOb}%eQ_7`ba0LnV6{3(na1)JGp7a>HCnXt}WuryGC;=PLRqb4k zowSdGsC7@rC<$WKZ}QNPdWcrk@3@E3I9zjRpKZbpwusdQb#N%87ambQx~g)A)XLPP z`f5qqK_4i*V4@-6XS|K=EGw(uwJ$qc@e6GpHJoQP4BOjC6s^_OtIcvQnJ&CT8Sy_1mdZ~a5r|2 zad@Lu$DMMETlX|XYemPeJyHwx{_9l5I+D|lEkb|cDm^2)duq6pCMIpI0YF^48*P=k zLM5(-TFdJdd16**q^`A0W#ZpX!q+tFAU%K;CmW3t2C+?`r~m|gO&3}!QcktOsOjMe zUp=%$wJJ!|YN_ZWp?#$hd&>a+$wyG6YQ95&gvj;iBZ!Kl8gfbNpy?knLqtB>lz&6{ zr#%jl)|#X}wFz%*+@&YLbTE8ws%v=(%Cn&XNKra@l?la8fB)dpZF zoh2TKx?l{~$$2KmWA0FNs-hhB!qYX6247Org(^UjPmEHom;qP&7wJNyrR|W))D*Kd z5@kQ6MmSYVMl?ujwIJ?3q|5~7)T0K05UD_&MCIOa((2bAA{zRw#cA!VCq%;7+lN97 zrP^@AiK>0Q5rcJd00{WHske$Fih8tw0*sG%Nh!uQh{21BQm(xYk;LgI#VIK6?V>=c zQXONV)LjjxiFs$q?aisM^PvO6e^^{+)SEF9ovS8OWmJNyP&#c?;Ik+Rr`zbBI~GMm zuS_uFT>k(GOo#TvEtFLb`*NRbUhMnDuR@r*L7tuwB@2n@q3+@VNaonT8v<(0k@HC* zDv*hdeo$PEARyTG)`~B36?@2WX*8-}nd}Nr!f+(PN?JChMVx}UMa2a+K?Lg~tVFo# zr<%%un>(GtWniOCWv6LC(rOgFvjtKfy5MF;#Sk><(j!kothSv|0;_uYC-jK-=%+2h z)*nR435e-b)GngdLk5V)vQW_pk!hvdBut7f3gUTsy*h20-TuC?U(I>Y2<|_ zy;2I{O>VPO-fg$rfAzjF%G4E@S-~aSbMpZg8Xz@GjhQK7KbULOYRuN{cD^aWA!C6D zUk~tFc(arA{{S%Y#z+J9OT%V#we(7qoow_DBxtnuKM0A)TQ+uWeuu!W zNkRFs{*nE^xA)GTUE`d8nDNVRCGp)_4b`CCO96s{pti_yC!rgXq=EqwGKgDNxvX`( z;;cF-+>AcuCWl9jROj~j_XNl9I9arFRnCVB&oFvY-NL27np$e+DN!QxEu9jn4>Mz- z>7;&e_p9x5B?e?kVY*0WHT)N&IA?dDeWu%*G3*rlXrXsFecE%H``grf2SU}3*G_hf zrsYnjo~bD$Zf%7rJ5qZUei5>E_w5%OZ)_crJLEnC!BRONcrIj<{W#j+GNWF5?|%F4 zuGNoV-r{h}b#`rReY##S=36B@ly_QJs~t;1g1Ve3!Bb57_KBqKN~A(&CN_RI!L9h4 ziL`kT(2~-0pwyB05B*hRDahx{(XxQ)2TBDWZ`OO}C4IbN`6adcCPRey_M8P2mtGwfn(Ku>oW)PD-f^$!OAQ@mS)G635L$oPVi*f?#{VEH>ihv;~sXd4L zU*8Ib;+hmK<$!moy;A1?0F7HRid4J`QBYGlYP3kp;APZ7Znta3iaHCo#AtdZD(R11 zEj3A{tZ3{>9-HG-6qD2+?1p z>nN%e+s&QHDKax37!u~yl8QJdpmUg1)Zy=XyqCuARIbyM)L3p83Pn&>Tv|&{CZdsZ zn@_g-R+Xn&jV3!qUT!}mZ`~7(JeO`nGm1O|MHn+fZpiMb{+Qr(=iEMpDo2^e9bEjf z8j?>)v*V|ct*ewA)}g0FpL>on=0D=&^=&MfY%P$Wd?nT-S$xCMFj$IQ!E)puEt;-f zho-s1;_aR&Leh#H!m@(0lWD9H{vC^gKCP*PZYqPT2+EB1*=p7gU*)X-0Awgm)!nT@ z{{YCDVu{<0)vS)c$y5*?kXjeVkMkqb8LK`7GN^1$)SatX!80WJhBa6v-_T$8L%YKH zZ$=xP0W?@^Ku)B3Y7x=`Y)poXUy#E{PxPw)08982wQ`P8D88!)Y#HS`5~ppoje=yE zrBIJZUv4D>Ri`VN28O3_?X^>!Ij}CcEDgUaK?(gmLRHvn91a0|0bVNKmjEYEQW837 zVPU|emXfjkE>;eWR7iK$6SyT%tiw5Ikqy)Wu7rafZ9EqXO2J1eM^rwUcae^l1c1V_ z-<1KVcmwe3`y-pe?!vJP$;p^QgpR=?ItVN6g>7kBduj@0hxc|N6YC19;bBKi+&L|f zLoL+=o~ZNz2mb(lhnq9UMA?KN1hYpdbIL80R%KSzJ!!WIQBW@5Or$m^KBb+<>P#!& zdgszN$w%F%!W&(x@;FLT*vXTLG1eVZat#yBjS=a!uTUc0V&S!B`Jw75;kEu$e4<|5 zYO|~EsQ9bM1irB1i33z+j`{A)CQ~!i{b9C>yaX;^({5FpaFw+yVwFl08&f?x_l+YJ zZZ-wtZslH`x~w$u3Rs-_L}8ExquX0Zeo0Usz+C(&1*j3lJKMUt}BGK!qkY6SieER&6@#e~5* z&rvHo%qvp08*@rplw_5e$WAKvmv5~C^mh{DubUl>@e zbVEaqjz7f~w@g-`}NblM(Rfz5&iy{{T{TUR22`5|J>I zDtWfFI;}2LD3YbQIsGBOaHp9VK^?0|p0k9x^-qY=EB8dCnX6R5lDJhq8c#%@xMV10 z1x4VLr@1nQC7!($jjlECG)?8#kZGCTigg-?dPsC1SdVnn@m70IG~#M>O|{D-y4^P= zPnH^2R42j&bdGYhT4N%I!le{6ok=>Lw?A>MhN(>zXAx91RI=6$jjv)Q6*wmEoUDRlp26k+#75es7?lw zdLtmRbCP}Qqpt1)$PeW(54hr`fT6pyNFbW-=*KM=0_kkeGvQpo|t zc}VCd0A6Hcw_!dhrrAq&`VNbu{uAPA8-0$EPSXH{AtVI>A*7=w%W~(ukP6f>97;Ha zToQAeE|~rgZmz7XdAfEff}~8QW#A%_cZR#mI)G@bk!G#2wJsnKsE42Z;$)Yay1Z`+ zc-!r}${nl!0L)mNjHMR-FWK8mVkDGR%B`L5O!ok`GeDb;5xQ2?-x;j0t6fDysw#2S zsHsXbpLpHzwycB(uuv>ZBRiyZTxzUk>$dw^QcmEM{>ep^p+QRR6Bm7OIZ#S}jESeL zqmnkA5+33hRpomQXr~OPo}jjq7RW!87Cj&)@RZm6#2qcz-PoC{`}Bctg<2Ebc-JRF zsaau2GD>%NpW6UNKw8Deq|p&vDpaK+nWoiIR`->r5+<@qmXd`RZYm<>fsNel0+ZY; z?ONxP7BXz*vv0ADt~W-ZwM*81X~HC)I0am*PP&rl`*8lXB(qLn#rUSmaQ-o-Ws3P0 z-mPr|$a+kX)Wtsk04!v8r=rk!-vtb#vfYKmsZ3rP zTbBUPr*{Q=V`=r%-?F-L+YM}3X~gZ3oXs;FUf z)}pZG-y*0&jXe?+W@poK$o#1h?YnvGIOUesfMlZZ+oQ>H+wx6BRqDFe_TBA+jD51< zRJf)EWo??r5|v-F1tc1(`hX->;?A<4M2!SudHzE^{JWlX@C(yCXNg;5VGUr{%|Evr zl{L1VbrmiIB?I9Q_O`E;^Z`s{@{wAzbIOmImVBzDx|ZDZ*lv4KIUPIc8b%Y3>>$#m zU|g@AnM;FRqJkXH&X&AJ+gVioS{o<|GE%@Kf$@z;34jCCbPLTqr<^#Qt<ec=1l zxa2j}*!DMGY83HOHzh_$GnY{lxeS1bn^>!OoOG9O_a*47uMfhn*`?H1R{E7M)j7FK zAeqlz@ehs0zGrT7CpDgq)w>w1bBnk&n|&=kO>+Sf2~@&N&v?*cwPa18SzPyW+&-l^ zx!wN5f!bYIC`*l`?Er~_J`f&4^{fiVgu}X5^wC25WuqkLjJ3LSdX3|axw-ds8`D05 zGkk^Nd%g>O!C|9?fKydu*#0+T!Et(9ExnfMnqeU-RFgiEJt6{jlGS*(4R2JQO+va=%C*r3b)YO3B` z8KkW=RWEhDSLSzj6}d}oP9HYDJtK{ADKB_vPEtRkao#-T`tssfODWu~(i1YAg!{qL zI4~#y8>f8-MG@9Ae{wm4k5sA4XCK;Jl0v2ffOG?-$%y@AHB;1V3=M9j9q0MU^(pvc zh?xvLl%brG^vD}2+LqFkl!7-O803!JXcU!%9@9(T)h;o7Qq9F!(`}&%Hy09C{YlkM zIC_;6sp|@kFu|${S)DWbCMzzPr3Y~L61osml zC9L&;oc*CmMAG5_O#9DP^3Ft?bqWi3eVN%@Dm9g3s6UlHDF>`I5cgg1QrAyyv~(Xa zQbmMYF{@E=(u!Id7G|lC186|jaCEN>YOF&O=adCl9NEehe2%l;5>!S`8E!UPmE&)yhc~`@IY{!X(?fbQp|~zjE7lDB&{L<1qYV9E^#G1O<|QZ z=u3_b;Zl$QgA)v86?OM3dTmN*NPs2N#t2~mpyr;bJDy~$;h5!J&BdqnDo)Bq%)HJK z{g4h${6!j<@Q<`3_D4G#5)Fc^^2on87g~VtS zX=t~DXzEeTI{`kLK$g+vluHd16-g-Om6Y!B@s5Us%$-q&dZAClT$xZoC97MfZ;XV@ z5p>VWs&^3{`<8>$l-}?Ks^3#?wpCpgmdo_)t)Q(0ibsrnVEVKqaww0wXr|S=pA~2T zmztR9k+)e1M9WB3X@kg0Iy!tz%!kl74LsdGQ0RF<{XG%S^vqFVB)Ei$&u}BB)*B8b z4))P&xLNUK(Yo4RE*TP(C35o-AOw$71HJTVMA^n%?uy9i_}x3g1 zL85lJkAbc{@y%RW#gsXNH3K)j(9zb@HkB!9qD#KLQiumhGKKuQz!`^zTaq*?cL{6m z-8MQ^T7TSQ1GsbNzRoq{3|a90C&xBQD`WJ`#GGIAaE3OC%{vkg?vDLO!3b=PK4fiS zqQLH}zS6G*Wh-XcT=O+bY)K9@!jzIEI~eGnb4pd6v)gdPT8gT?pnkEj>AvA!;0Qh> zp0Qt)oy&e}KP(hI&r?tJK!q&^RK|cu6Y;MIP?U`PU?-G&SW59O9W)X>Vm@UjZZty@ zK+v9%6Ek#D5U`c$Sb<#HBbF$UI>U@*6E2rTA)-kIIuQ<->Jwq*DQRAHVJPk)_QF%{ zx(RP(qp5x7)P~BZEyG#C_O{icg34Y~RXw-|kDk8WEvR~sx|dX$rba?%!cl$2L>on$ zWp7Kt3#IR9UefKC=IzxL^kEI9DV)$`bk|va__*EjFJe6vTOY+u+_frfb4RueUmwNX zsA-fPh%-c_dVC@?8_7PP9TetUhzmMXs^_!Z4k>Q6UMrey2>}aIQlEs#lnIew>*kk^ zbAT09m7m(%4mtus>o}|2)1tFmD6x^-6S>VbJt8VV6x?T`XfBrGg&Ar@;C6}yB#?E@ zEKfpwbc6(nLL+eskp#k%C^2nEshlQ-e7;Humq`xM&=NytRLdJwe@#}D z6wFA^q&pd*PKIcd)HgDcHu-!Z;O$V3wFkv18OdL#k`M^+>mLU=6v==krIV0uRI3|O zR1Br?f})hw2_>sY)k#bfoU$4xSS%%eqI3k#T5HlKt!|tMD!G%!Fok9O8ScZ}mMW&I zc*7-r&rtFRaR8-BP$Rk(Fn$qZ+{s%N;d1+F%>&U-zzG}0*Oy?t2Q&}HL zCXofIWGqT@DrGyRFN`kU8+grF&(g=tWhYhNCF~{$e`#lF$EjH_{5xl7PxG9Ol`EEr^ za-*ktfl}F$e87@sb)38eOxaWgS;$bU!{e;m9jxOe+b(u;wXGk_8$k)$p1O&}fB;8D zTlYOmy?L#>1C@1arEE4gFUnIXO!kWH_f=}gIF$r)HmS?cDG897l4K$dM-^tbB!t@B zY1>Iz*a$xX0!sZ@sEq*TwO0q+q~?y9rdJUJ(-C@BFXAtGN0j-FBt6_U}Q zqGuUl3LqeU$cmZ9s&?I>7rLgBWB`C>kcBZ0+M;LNZ&}Jmv@`EJ!7a=OLWyf1YD8%v z-XtNB#Oj-tHqj(56)X+oaM$4yaDt#Ow$_OgSz5?af(-m(W;p5ss?Q*)Nm`ol3D#5S zVDZsF%5_C9SM>u}=p_tRE!77BL&**$QyT~b5z|@6AF4}55<=&0w6SWe!t%D)S;p6w zJxhIRHK@lfDkLG+RC+X`tw%xML!&n~+k$nM4vpKJ*Y~3y27F;hu==c%EPcXYr=WptHLvfkfN08^p*is1-Z;&-Y>ITAG`W?@+){_6z01-C94iu>CKyyX*-iiaSec0+31HqG6|T{V7K=7 z_a}YEXZm}hUu^UGzCQ$4a{F0rm2bNR8#6~=KY?7=axWTY{6(8z`0tW2*YP~h8+X&X zS!xnzB1l8btPQTR;2WE9IuYPO1NGmH@|!%pn{F+gx;KAqSCX%B^V!T|nY&yP5yVJ* z^)W8(Qw?}YON$}IsVh#0qM;jT6Y$8AkFunaB{_6~oJ;=zLYr&qG(}FN&!IdWv#-nDrkN&IgK8!>T%4W=X1h*ml#B&?VYu5c*UnF)i(xbRs09k&aQ%h0&yy zMFF9COsEi_;a9?NEQ}zovRa_3n_z-|z9+0RxYZQVR7X^^Ftn(lNj_B$q6c`@84#;b z$~(ebA&T`$T^p$Bspt%#cSTg_6hj}SQbf+9_)my|q!drYYt?PbuzWr&u^RkzigkL-?y(J9_Y{{R@t z7{zj&Ya^VQA8;vbZ-=r9Ic&4-MR}H5XRV zl2D@P`;XUCsT8a9y+!WY-qjHyIROz3!(BA!P!dZC>;3EH^Irv(5{u?Wkv}Ljbo2e@ z9~j%e>_QWC9)G<6{{XD~HTsh3Z@1vt5(zYH?)jM+K~1!|^8KMUT^IJM5RPZzX5}o7 z`l3wKlS163;RK6YkL^S^<$r3do96BuYt#tIFW|UI3jGsWPv->eo6Gkm-L-1}0Iobn zgltYte?3WAqi7%B4@VRHLQ`JQfUli6*riR3lJ&E+=-Q6^P79L|;Y63Za~2NjcF^P( zWdq|%grDCZHIh{i?22_r`tDn`loZk5u8LUvlWDL&X@`Scx)=gSRq}mf(*pBe>OX}l z^u)giQ&^n@1Va9KaB)Rxu6+1Rh*@Y(GD;%XGh5P(#e1{)j*!=|7X-*Uvm5awi;CuY z^#15`+Y6rYLDiLC6iGK11p4YDy8>%@T8>S7kss8Zk?ZD?CG0KQ}Gi|yrO{buGmR=7X!82eyfZz~~vyeIP9o=mz)#RydwHR+~@mFpFn^^gd=P+mIEb@a4&bP|}bPSc`Q{jgBFB}O}n zF^5&6rsmGYJ#nb@T|bqX%<13+H!QVMHnrK;6{G8rlR~r6}Q+JrOl3Ky`<82v2a7C3V#7B&9Mc9Sm{r)i&6idZ3P6 zoL+#)c7zF05!<(_(7K78%IbO_7~ygytN~)G^dUMc{q12M;7@ycBG;~f36)GkPM(YA z+7eQ3zagimN{h7t2m)l2+5~mYbW*mshrhu#cZ~fYtrSTvs7wX+faJN+TkLqy8o4)> z_iK&K!n%jZN$A)z5CvT0sB%>rCAWVMfe6%3T>bE3-CUl%Csj@X*7gOQ{d|bL1 zf<0P>l1hxcHHIHJ2$-zPicZyC%9K)#M?zpn%u1PtzyYc&S#i@fIXS~laU~ws02i>7 z+WHhq=m<(x)<@?K#!?7bxBOoyZE@?=cY;FWme~0WzK@s4M0~zNkUXMer&w7vLzS9( zi1~1#DClG4g}tyRlbs$Hm3DO4BfvJ_<^C(<^IPZ5H`3IVMaEqTl)$XyDZDyjzaOTO&ti0ZOs;T5?- zG8Lgj&?8T;5-are)XGnk0yX)>wTnS210eB5H`~0Q(t0JrQUH-Xo7ok;U#q%Phq=X=MndvBvw|E^^y_dRVEvf3J9~tnLE5{5Y zC(de-TX{l5ib{YXqbco3Jz=}tq;R+5sjy@2^3zZXF&6TVxLW(WPFOK_e?x3YEtR%* zl_UvhL{TqtFPYk{&jrIGvtaQmHB%y@rSkjAQ>sxOWlWuQf!gXTU$)kY1mxM6QnrbF zggx#6g=-6q0(h}LKfUFZj8j{w7PPpa7O*EToKy3hsw-f1SH3yI=DBbuqOrap_Wh4x zbZy>p2Ag)R5?J3UB|uJbfM+>$_p%nVRp|T|6SuLCbCJIxa*bWqsThU^#`=dO&~c!m z1hmpxMv>b7&(D&WODLm!R%_i(?Av1DER5CO6jm$jkL`Vw@|AtNKjfDcY{!a_>#A`i zKKJnyw3T!R{6;-Vn9NvU;re@EH&d$ejvI~SIUUc`T@oX%fpqD|oNtJ6ZZkuQVU~8R z_No*Fgf@hwPACZ#EP>`H#Kswt$V+u0d(BGpmKzCgV-SuZK$JIo)g7XpZ$9AM2bCT$ zn+$X3bpT9fISe*YlH;n2*{*iwQr&si${22YTL(=%BUr-V8yi5TtIV9on>tq#PKp6@ zx44^56K=15qTq&uAzKdNGpEip%uXS!0JtwV@-AZe9$3vv=A+%Gyz9n&bgZ!BHw8Ep zrKQJ^wRv@s0!9EYK+wNNEEl)i8?lNP569mt#L}pN$6R}N&F?I(sioD_RIC>qP$p!a zvxvNYA1%80f(QVDr1qEkqS?8uZq}@^!dc(B#UJv| zB4gZtYjvhxLSJYoB>;gVDG%Kdmlr}YQmr^lGp-##t4gNhlJizo=(O_FYc9#v4bo8X z1}T^M2TJ2ki+I6t*4ltiRbm`>h}t;Y8fKlAQr$p6^;aMXGX3!i=jg4ij)f$%eCKOV z1in(+V1Oq94MO^9Too|$4=VG|{b{SW+wzv}oN~4Tn5RlNJhcghB$8w`@Q4eDo@V$d z7@r@t@l6Hw+on%e5cNZkslz2|PUAgKc#te7L@OQ}s7t$Dp3lej2QT;611&a<{$dP> z6X<)vCuD7@QF0k4cF>`n`tg)|j?Npk&^W3rGTPzw)Hej3r}F8cCLy-IN9)m9B8isK zk2JQ=vo`T6T7Dto=2E@$N;21rdjIsX! z)w-|LUSA%ib$W?XopDBHdyiOLF@_5Gp=P*rulOd@Gs|7NRXBj7>4HL2s1Pi#s+F_1 z0X91vc}o5uI*5-YONgtYX7c;}R$Frz9y0g*yvO;5>WPAYo~oo9Fku|VK zx!qIwhvPQk>kYYN<&qlgZ~*4?W293ajd#!K@V{^vz6Os!)G2 zzrYCX;}x8h(Q~K@)VQt#+8*PAhz+zPl244{E%aBCHlj z8B(OyFO4^BqC%WVO2`|J?TbycA+1?vO7+vpK#8xvugq^$I0inW>sBlqtyq}!X~7fJ zd>~TeESpC~J#N_S;rCtF!{SrN$$3qqg)mBNLl^+wtJ?Hw71c=!PSv{(-`)tDwW=cp zjCJJZSvpp#DqxlxipryKshiamDk^#P%}P>~W=zk{0J4meQ`K7Y7hnyeKor-&@g-~y zsS`pH3W*Y&T5WBXTavuhDsMGSGNseC)iRla+R7-fOOi=yk`$tUE~b5lQye4(*yD5E zK$&bS7*e~fXNJ955u zCiBTHDDSvydM5PQGVZR>_`iVkLBA1oUWrXhd&BBdgq!;)N=V;Y#!SK#OJbUhz~WM}bduDNnRkfmxgwAN)NQ0QLTcq> ze8ESH@{UaXO;d&CG^GZq!sJki$q5@yfDVEf6-ja2>ZZhdZpB_nsueHODFa5z)wf+V zoaG-jgsv74((ff@KWMLOSk0^4_IJ((waUwX3}X+qPTQw?5p%vMpe!DNO|eY5d=(HJ zj&06lL*T8vF4xg5)A}`}5AFH_ad5A2-rX^LkDR%iKIE%ak1)$GH8gZMomEpxyuC+j zn!k}xGPNbeoeFf1$4|eW37_r{Fto#CV=jU)Dtsz>uVH;HD#W8TGS`xKWpp-HSWTGU1G>_B%#`c-KQ;l2Wg#nGU zKaYy@KXv)on^axQ>#O!dhL7SN(x;6m^zMoAO- zN)#GUa&<=3jipirI+^r>Kwmx7PA!Jq$x;i5oaA8WqtQlBEfeXBF-XGNSfbvfk)(%h zVn);cdi+fizgRjy)_fz{qG-ByavI3VP4_JIk zXpmzSNA&ktWl z!r-5rSM*J-3u!4TGSg16F>XN$B6bp{-xKH6nB_$x>8RUuHlSp#()oDA^{v$vPWH&T z)mDFr`?k#J@$_2gt#r)I8$;;?6#WEBvCVO+3A4F_YK`2hm}S{vsH(24^$U|bXD~bW zf*cS;RmuS8w#v0S=jsu-l1i3!pW_h#%|J50@~~A(!Uzg3WOg=`Tm_H@vJ(2lT-M{w zO+fcmG54D_IuzoAKGq5;`Mqgg06g zV~&Wi&?J~hN()f@A-Pvd-{nujaLHU>CBmfvx2y__qA_etksmGuH3&|hzCImNsE=qV z78vb998gb`(LWfrJ4q&rmX%AUqqG$pb=5{p zND63h-VsY(bfwsUr3q}5yk!ky2ABDst%(C5DPA zpl#e$Q2USL>WqHvT9wcFD^$`MQe!B zHnq)#DN`{EG>UG`+lon5B~nzo9bgG$R*M+5Ns7Te+RLm(Om$Yv9*Hwh%C{)%063`# zno1+7L*+pz1o)U5LNrG*pwSZ4Qq>vCVEmz$xwTRbjnql%svCmBrW7FP+$x2Uk2)mX zO_H@odetQMF(VzrLWi2g$EqNx$CQ8b2`8w7n|D zC*NVD>$Fd#prBMltN|4PwM9uCdWc;RJJ6+uYFSl1Q)zvdl2YqR0HmboK@hU_^G#E2 zezIECYySWz<{RC^>lRKik&iV+im^(P5~Uq|6%!y&PKQVt@P`CUGQJr%H}sD*<9mDO z8~*?iWdn!#I;Zs(w+I;Z=}Ma}vaiUbsP##R*(0f;>-*D}mW*>f=zdewc^~joy>|?! zi?W8flZLC2YsiwxMZls(n))XQacnw~DWuCz;KdW2@Jl{Nk9DQc(DXh2_IuoEITX9@!4ksg}wmWTxu4y^}@T!HqzP-zUBpaSO8f*Q(x5YCV zmKT*^*&W{z%dD^CIW>)8yQPw;vfHi9O)C@8NLbY>8Xv|stR`~PCu}FSaC;AkRXdAW zt>!UG-L46~xKpW9@dvC)m;4bh#*#I3t_iFlp9pJDBzt9|C3&gLDKZ1RFzCe;lyX!e zrMK37Keic5W07;rlR1rJp`|#IcY7^8VkV)nWAd-V@PV!vrEM-q>X9_HmwaBhJ4)$k zK=kT`j{-ZuEpcg5&TC#1!)MCvnL8nxq^6GG2B{=TNBxrrvfw|eiI&mK)O10vG;60> z24%CVPhlL8XuEGNsUK3H434r998|jvQCo^k_V*p(8NG4Nb+@;Z{dkC)5n`Fy^Qm&@q+e7ugN617t!+h zeIGBE(en9yA1{~D^7(x@`E(WZe7L30B4Vj!m*&*Ww9+q`2&&cRz8#(}aMeXB#!C;l z*%I1flJ?QmTjjKy*a{BU=O5|b5Zyjw;on!opz-=)EXfp!Y1H% zRi0h-M+zHJDg7Z;qK;|RWoepgnsR$UI-jgF5%s0s(8NNUyjzwUMp|iVkxYQs!?>JX zv6mnP<}Q23ea8|Mw6*2vaisqMH_a}y5ou+}Az&BWbhPwaZh1Hj6?&5rC#dj&rkGPi z#g+@z(J`4eLLCbw-3MKNWJ6CiR#AXBr$JiO2%m0{ z1=YnHqO@S}8y`}lA0alkUQ!&WDymr3B%}WTmNhUW*DY$D zvcX9wf^IHZ)l21RqDe|lo}#+MWA2Go+de$T{K*?rT~}JI^$)4Z{{Rg1fefxH!xgk_ z8U>0{6R&X&t~k|uFN}Rv&Fe5HJ!KyNnk8=16kAowuVbsL$U~b|(1gO$YO>mu5x8`Y zhLkyS+!Ceef%TLrt*S$u2UKBAKn#k^$?XJ32~pGC)1o_KjVT%?UpRb2MHM9F88*^T z0G6Yng4i@cK2lJELPuVmp@$t4U~06f@(d%~Ddxn3E~yEYYWx}g#Y`EzG2qqN95>W4vt?G@@K=PA6 zNa@$gVc%u`NEhEQ9;@XxTastBN3?zIrx|b^(Pg%OlK5Zx!a!ZFwQ0KCRvhVc# zd5!KY$UFqZ&I!#aCs`Pezm=joxi^N9{{Yku&zV4! zIfd~Rkh5>5XjiE|SBTu=)O<+z#Tx$rF2E?#wmdfsy1bKo-ztn5mVbWBs$4I+v{NjR z=!F>p{jGo&>oZJ3-iR*V1*QZAl>TauzX>UAAqtyHv)OM7LpOIiX@W_k%jhT))U zh8C_apiP5EooZ64d5@8!OsfHvD`Ba;=4Df zpwX#q0J!-&?*ru+J=NvTN^PyFTjjN-wmvH8D4A>aah393K-L+AFzrjVYDx-PO4TXp zuHT-K=?>F&P1AmT&W#>x&YjrrWOiV~0o?QCucz(d_R40>NkhmSy=(OrvX|*?&X49e zqo^wj69eHJuO{X;`1^mRv;YyMLc%zA3cAZ&{blWGTF|b6ZI;>#Jqg9;|h%I^0rCLR3gt z22uubCp*iI)}>%De%SYB>YA=7;ya#Ib7^X)RYSE38oUrD<1HArJ{HB5m5*7*&eSEes$Yrk z5oOMu+H_Np7Fx67QTAg=EBJ zt|ZVY>kDw!?tet9<18AV8ln%>x4xy&RQ*D=TZ>kuM3V-#Fwhi6LiN%+M2hFmUTY4v zF3)RtF8y(DZ8}83WwLgaACQRrmGEGo>a2WI0A+UP4Ha{hJyiSERZc1Tih@+@sxvAo z)?~$4l1)n1ngA3U1UZ&U)`X-^XF&{xl2dZaK~(<$@%|`gn%9_`h?oRv77GhqqNPxC zSu*3aC5CN~Gh(-fsS*`d`1I+R2U6kkkJ|@a+*d$?#aRa(&Q3oV;UBs_np3ZjNPZaGZyw{+pfWQ$`s7B>mAWW@cDr3UCoQbM+_wZgxl+^Wq_)ye zl_GHgLo4d9`&nrKV|uAM#<+2)sHJJN9jhtYoxk4`maz0${$l=>C!&~lYASqI%9awO zht`ibLDXv&Xk#sE&W+KQYIr6Jy0E)YqN6>>$EL6|x~l^t^rlmmvq$W#&YfBR(OT5c!fYC}@k7rV*@UGeou zmAJ9GQ|uB9qGrk#+QUQDQ~4%M`5VfQm!SL;S(V=hCvsEw6sLA6g%wlUQ#rQvanZ*{ zn#-VuEm&US@}IbSE#VP2#VS2c+L9z9LDMMhqKHFIq>u#lg}M4B#C1xFH%bb0oV&yE zzNjf&Kueq5P*x%tSOu5Kng|_)pxJea2WV+ERAa2AKz&6%Q8S?F6`7);R~W7;5LRh_ zDuL*F!dq=m6q71FWeS+{e#l?4Q*ug=sFILI^q%CO03p#CC{J--$FW=iJX7ta zuRLV3qslpj7dzuGu4q^3s@ndUYedx|2c(d&JXI77)Vr^Z4vh;^w;$8Ew)gzf5&L>B zP*RUTZx$@f&i4kl!R_?lpd<8$cso!CC1%Cjp+;2_n6mMVZH$FncY~rJsC&o{RVDE1Miv?&Gn~AV*I1vlh!Lu(UPwZmoJVD+U0o?dL$v&77EF9# zDt<}itB&DY1v#ar+=R&>b{$BI9J&CaqG)$iyyYLz+o&8u0G`vkQ9g%AY`D=r8X9y} zRm-*X&plN!NCMH;djdJg@) zI{cz3>{bhTYMINuIBiZ!WDc+-hKg8f6K`IilC6ATeYMu9O-Sm;c9KtSf)E}OUej8l z3WvhVq!0VhvnGje>WUTlftZdEx`JE6bTw6Id)0Y8>H$C{ws5GmWmvx>F~H$FfGJtsrD@QC^4HL9127SS~~Yom)Q-jXLh1fVl2IgChJ>W@-#GS^=iZ*c_}SSg}R z)TuKkyfY)0B56bpuW3Kxh6*D}BIP`E{ut|>RFo^$N{}`G09-bSSpKa@AdM#j5}a#l zh9>M=B*+uqI7;Y_s}yBkms&KgU&T0U-wjJ_TY?Ia_L?UK z(a0Fm(vtGrq@O|oFl!`lnjQ9UPqr(_cw=RJ* zVHFphR&*`p)EO5;u3_J_n8JK@zX@()l{-}vyCXatl}?k{P&P?4K>S*dG8j= z>8=#PRJ5j*WdtcR3H9lnSO?-$4_;@P44X(a^GdSg63r2I;rJ>x_nCt8zaQUj6r8Dx znn%>7lw7G)Dk$FopE4GmeuP6eOb>41(5_g1Q}!OJpmHn9)llx>LZ)>mQLI)cYP~=z z5((<{IHGg_#1+9-8yjQQ7do)NAkux}E_YE|Y~!jgc7xSar!c68o%GdOEN%7G23|Mn zfKw@W{{X5YpLPdTV}`<^dYo7!4ak{@>F<=Ky@sm?@wU;aY)|C^3*A(hvlZx)*Be?{ zoRQ<&4T|iZiMg#>5*Fc2)NIqj4u(TrR3*eo6<*jtP>>NfZy~C!UC=c7qy@zSW_?8E z77JH;RW|V>YP8+>N+cLGP?8ix`P3Ah4^aw0y=zI5!HV&+8YU$skt7$b9^(ZKH zl>kzts7QhgDW-}2>8HP9)E7!>JQppfEn3_%f36Me`NoB>Xzc2e`It zf+p5?LU%Ltj{-lpU)T6MyampeoGS3|5OTm%H!Ez~x7Iplnudtll9ek$2-F_&6|HD~ zWIj_G-M;i+A^o2O?el*P(B!uBkmI!B=K%E(+CV8G0 zp5?My%U&p0uwynyQr=URRj3rul?j9=#mLEzD#{{V^CsnvS7jPRRGYe5389wjL~&wE z|`=z?-HO);rf`69%JCXs`TpFQc$}fGHgW=%~y*w&Xa=zZxN)+3z?gydnAUCr`BWrM4 zQ+Hc#skS`WXsF#qThE`^!`?=4^ckG365PTP!n3~R->Bx za4Fu=4m7P$uxB_!<4J~EbxBv)*kueoi0IxCdxm{_!$lFjXGw9Xx2&y&`BnMbI;4-3 zeCWYl4+&JwOBWWsYIEtlk^IIFkHP|$Iwvbofw}(dk9a{ECu1RLSLHCKC6a(V*lz{X+%o(GpHtBbdK=)_>>Q7_44$ECQ87ieS|0^6^TQKsE(xoRwcE8J+bgB z;uoHp#CE9^SnOAXcB_7hxW3waBjwsGWUuhYQVCyeSAmYNwO?!D z9A8y=BjFuMUr6{z%jNdd;U6!T(moOL?HAHM5%S{uY4DGi%jp>Tc8lpB10OCgqJgZ=VXWqJtI)aL@2yVz_LZo3cPYgy(^30be~yujQZ)6E7&+$H$o8uf1NV% z5Fk>|=$~9nO_Uy^Pu&*0c%THVuReWA)}t~B1M3rkP_7Y^iXdob;3MKtY--MdWeE0)M>YPQNoaWQQnXjz7G%s#33vgE2mNlM%^AWmnzPFma@t1-y& z@J~c%Ta%SjxC^90opYj(w&S#aM9t)xE1@nFxCu}0h)dx{z#ZLL;xJp_Cu0IoXNlC-&XN=e!KA%i6ji{{zt zfxBM&-^VRHVc&MBhFgGu2h>R_GMB~}PL@#xU1C%GQzAR=3hI{V$w1w>1kCP~AWJ}= zWvOyY-E%4%cu-Tp$9p!DGy5TlZrar~Wyj=`@J*f9Gq~3&NT*kz%D?du5i?#B(Oj&s z5k@L?DJrKNbz9w0aYw3*1W77stZPLUU=0j$BX+BLOu&zmZtAm~ppaJ@7TM`I`8Q2h zvGU^H00IOoN&PsTn-B?@h|s7WGnkAOqEKoNs9OHn~yo-p)rRG}ri z03)_(LUaNNG0{ZLb5vs8I#L0UoD>;GpkFB^Z_y*HfrQn4N~~0=bU!%Zbr)L18Fc`6 z8bd|pILl&DMMc2~Bql_U2ur1>f|P>Hluq?iwF;0sM}#EErCDy)gc=%4B8y-W(mI+Y zZ%5@3^5V)qUoD7M)6=Yc3i-yNX1>!fq&nLjUoRtz<>=}op?p!b1vB-}w#74kql+@{ zq|6$|mzo6?d2+~DT;ipVAMnGq^z6FjF?mY>sK}1M_xZ)I8HfaGu)a~woY#=kqJL>U zy*B$obceQq0xeKsnBhVeWp&&s+G+|&<^I?zT%&fPg{2|Tl2akj{USc^jMN>fV+Gx( zM(`v$D1~#u?^wmzR2OP;fRp8Lf&u(uk8_NY5^5Kqv3#Nn*n^`eV#OT-C%7#@4YHKy z4Tfl(bKfUUi$MKl%}7INQXB6rKYS4xu8ERa9;d2ZS{!i_T)|TM^)S=fLZR;R zFL|KooY7rsv9zQVhiWC|NE6rT4sNc9IPDAFQh+qo=OuM0lAEL_Km@d$JWE;AswMI= zFqOfpE+xC^2jRT~h%WH7Pr3Qpj z+DLh_Otljd_`{JMR~|;frymNouhO)nf=avmqWy{|U|809{{Wm*yPY<#=9JVMEAOY)z_aC|-rzj)(SDecr- z>DmP|m=pXWSWQ5J0M}Gv-Khw10HjDMJAWAXE>3?atN!*^M3iAx;s77XZ%?FK8LUBM zkkjfMQ+w}GD?<`V+O~>{7u-u(#dnfvY^69HEtfU{RI!;RqOPSXeQR1lAOflNJ);oeBmoJ6NKBJdr?c4}u5}1)qOwQ)QyEGeXew#M6wtIrK+yh+^d{LOiD^p-lf>7wcv*nitM4d8oKyD-lvTf67A2}7>7uoi|C zKtIJfm@}C72oPYk(Su5ajt|>s7Ym!uREJ$vz99_Kw3If4vddygNB{r?V@`9PYt*gS z2;ygu3AoDhT6=mNr=)Z#?L@(xzX-EoIQyhKozZ2S!<;f8SsB$vjE^frNvW)=f6it} zQRy01hmEY4KUV65I?@-k1*8+yXeZV*OhzfK8;bMZ zf0WC2C$%3%%eS}>aru`zU+S1*t=6oIT7xa5nXM$tSs0wyUMLgTY(!S$e43TJhIlUJ zlUGt9nkCe@>VPL{(K>X3dQhr;yv?T7-~9r3#c$ILJ%NuKq&L z>hE=Kwki}`LGq*?x*rIPd&yFeMj6!>Vs<)u8f9PaPHjVTaRF1;S;MRvRG?;@xa|p> z&T{;ZmM`t$n4%OYSy)YFLvz@M)MYaQ%pD`Bi;2v)X+23wY?}9B_Wh4jV>ruA-1EDe zWi6};uX4t#RJ=SP*&ZVsLRF*&bG`i%M~?7HiV;(oWp5h$_0e9XWkaH6B>A?ceE{%} znmkc+zjlES%yOepRLd>|u!L<&0hd7xl8Cmorm3J>+^u>*?m;?(q@X&~sZN0+Gn~PU zKGa&LNT|Bkc_=9X8;XvbkHA1z5*~p{V{JE$&d?8d*3E^RLJrD$X(_@QYQAxzb8w7tcN)f> zT2H2uHI{@#rIo#PRebJCV<M!&<3UR_>BYluN{T zWmhC+?q#<&iZSa_{{Z9}Ua4BbwN#H-<(vV>dy)C8&t^YmpE2r)J;QPr`QIpG1jbyJ zKVnct!PHM{&!p`?Fqi`Vb2OVrK&E)^IkvESulbKe7YM|IVStvvP#`H%oX3nV+TY-# z@A4ao)lRM~$0;oI%ez($frQR|r7r^(J2)0qoW9=ep(Hchm3M0OhMWSKqC;sN47=+F zx!NUT?CGjz?w2-g)RlJ%rvpq=Ag$FC*&|=Z0lAn)(p93y%*!AiXs_De9@cXYCRTEl z_Lp`&kGtvEsoYG_>JEzY13jbm#6B9_%_*7vJQbgqWZPqGEhG7l&Hf5mBIymVApqEILyp!FNXy~aJt8o)O!kOz8qMF5Pr?%2aw8pD*S*d69 zDEvu-BZvV&gnPr?RCtFJsdNX#N6BHO31mr0CtZ5?hZq+LDNRTY;9*epKwegIOg(FBO3CRd?$bzzlquM&R05m>w10?^45uoO=~LwZnZuB# z$5id`{{SCX@}DK)oOfzzN`ES^VTy-9C()$`d(MB|LQE~PRn$S;$QZ&`MLg34)j ze07O6=v!=bl=PHrB0wRER0fCT@gSIziaIKEwR`HAj2j27vsSrtuKr@y52Atu&}Y~1 zf(x`w8)6+)>sqk8{{SuKm^Tz-B6rS5yii%GNE0nHQs@;9f&T!)nM&6`geb}*fzLhx z4lNyZc^nMt=MUB6`xU}{`T0V{!Cucehudx@;wdTe1yZy6G!T;An;fM<20-*u!U2qO zTbD8y%5^T$X`R?}Ty5+b)DTbLKez2%6{VFw*?WuTJrRqfbFS;I(mvCW4EFyDN@#@EX;O+ z9{MRbki-Q5nS-RYflGqxPOE8cdg&bA0yTLeAa@-T(Jc+kE3a6ObBZyoq8QmTmuO+p zRTm#bX3aMK#KJwIpL^(1ijLA=De}+Z1jh!ciDXM#UNqa%3QnVip+K+@bVyE5I2mbcISZ-IEXp4@O zs(^X*$@g5SDtCPMO*82ca>%A_qtQg{rNpUD8Sa$zoIqSUrCjXW=u9Q~0YO7{hG3tp zM@lMDu_jjh$UDeTC&WN9wM<)rLTWBF@+v)L+5t63v8fkc$@6_-9^x^~s!TkcgKvc4 z;QF-;__ak&EG9`kJY$RCG(ifKKZ+3&RThOWrqlC>S60xXIvDwIge49sUCxmD;RtIB zGyJ-ChRZZVl&rSCq?8g$Krpz*iI<9$?P7QOiYkWG+KAivWvM z1;jMhs^Of!#jHZS81_|6vRb4aa>7)u)0~6{D~T!C!c5dT zsn!}!v{ezj;-?&4p+QQ=Qebk@6X9$1MCK+Vq%XErYg5KUU9s^RB@Gsr9?-~`2|miv z7p+94Mta9&8l?mgeq_s8$J9@Xal2SwhOCGc@k}phDUcQq8Jf zbGWmg>}jq+N3FW3Wq9`M^DV`%rkL0r4cq3Y;(7G zsG)eu9IbW7R(Cj~=2VWc2`kmQIOvmWy(k3fq}w>6OV_2dyph{jIu@R&OPUnjU0QF>K$n62ku_@J;;Wa8 zr$pS{n@^Yl^@+$js=eD2Wp!&+_lfIeSiNdP)dGD`1j*|ais&+S)8ia$q<8=8`cI*=? zxc-A9q6hwz@>2BPp{gy_u6WNxDE0ZrCAJUHXh%eCt>3p-0ahQFe|Y}dehKCF`^}e2 zMbfW`xlx5%atlKL08XI!0F(SaVtl}f5JrSVG}hH7x;P**mO+$~Jy`GULZwA5>p0-t z%8!${zOT}CfBagt8JZiVG1Rv5M@0|eT&6xzpL4F>S#m2ETU<7-{uAVt$9WajHsDDa z)5LkH8JK<8_{PnMQ`B1V8>PpPT^nn$XPPWAO6U?LbqC0#&!*XuBgS|?vkS)FI9xmk ztDzn%e3!hHOEXuv^QogXa8OAdQsL;EOnWs=y zwyL6)2;EAhBu~`T9cFq%*&LoTibiE=5yf3wUb^@XJy4H==O*yl68``L{lCBb6Me~? zahh=+ZG7XtM91F6^9*&(mkV>kX;W&Srt>a-R~c{#p=r?c(1Hf9faDgLTVXxBgP2u* z;?8SXeYrsn3A?E6`(jX~gh^1?JBS}697aZHh@5XyC9m&>%Mo!*iymz1PealNa*|_# zMI{AALR~tDZ=tFpv_mz^3+ACx;l+HDM2~hkYu;i+Fp#G3=%(W5z9+0M=G=k$*qt>EdkT{*^K%MD2{HFv|r-N(w ztq#)x3fPgV0JhVlIs!!&l_OrVgo3)|ffD$~)Laf&M|PyIqwTBZh)5HUjaOIOL~s?# zuQmw#a`|GUfgJ^WvHC|>YP|w|W9p=mvf3d@HCTksJ4C9liew3nB$BX^l;s~TEP@G- zm&+M^41Bu2k?=9{;`?fkX!(4-DI>IezE;`G6z!&xs08aJ6E2@f*K!g1SLd$*l>32I z_G+D%SCc77>G;NTlhMoEoMqLw2^!h+r{@t(>a36GpR841$!#a5lKdj|VXH#QGsDzV zqeyg5!Vjet7U38;?7+}RVsZ_PShS>v5<%C$gnS525u$c6x}t=&to7CkVN~3uwMup< zYNUXz-8w{tytGwIixslI;ryk5qCpyxW9jD6Ces~LRLAAlrL>i#Drcu&u?;-Lg`i-u zz>uSj;C+V+Ss3og)Wt~Csc|2;Y{k9 zdK16l52lt$R_OVBvkOn~kDPTRw5^~_f#VC(i7PY%*Q9VR5)h`^Tc4C^;|pU+>V|xP zbw-R(0zo>F1Vs=)O?gCc)hbd3(JvTt*-=tll81&;I_nNOltY;bTc#5sI9tsVMYUgg zn#yFu#MLMBGEr?sb8V$Ve8}xMWzvNs1)WhF^^UxBJ5%5xhq#p^9jIVH z&r|zMa;UHy53D}`*11i}*wTwh05%h-K!W9L$m zhQ{Tr$s@)aj3(r80;i&sd_Bi*R65OFO9YkcGd(93yhVW;m5lRFNDW%jtrv@NdS+>w zY_tK}K7&aPk$K0DCFedvcT!4|R-9&;Nl6TkH|FUU7^AAm@3JHv5&PJ6z2<;hNdOH_ z(feS5Zd6;Y%~Top5yaM-ifWpc*h*aiMJ{dlb}bXBcUhAE1- zP_*vYOoxPwW)~LWuU@l{C|wmoKcTWSNZiz&sS=r%nm~?lu828iC#n$TWmC%(R`&L# zQc6|?)%zoct?Hg^jA+#@G5jsZ$g8?Kb|g*0vCv9=;WADuL8(AoU6a#5jBeQ_9jyzu zW9$bWKnX92Nggusg6-_f^&?dl=bGwtP4z={RZc3Rt;IUxwIxa>CZe?iqHo>>Xot^@k5Lco|SGas@uR|D>?(hre7e13^Kz#1lc#GE>n z2YI5Fw$KGSC9Htg=@Nt>w}6cq{{V{D`^o+AxWaDe{!_mNe_qSevjm|~N!{rr0rZ?% zpluie#o>SYhZNTP%WNs6xw~;CQ~3~L6P6G3ihH)-{roDF$}HkZQdykqOR5P3B%Z%0V)rK;BBZWZV&6090-gLh?fn%00H(U$nvxO% zkdizM^_-&{l7*y|>2k zi!Z3Up?x$9z#R9wSsqh*ZLF*9)`YIIy`p;+;{O0_xaGZ6R;I2wYS}=pyt>*;iC0n) zF`$ggn|BuDBJ#$1V;)OY;2o*A#k5H$s#DaJI-Y%+R*Kf3grIbc*^#;DMXcQ2M+<6C zM9St_@JMowf!Q5GoDq4!@z3IV#CexI>c$lu@?5^2ftMevsFHvtWd8WS*x4HGXkL2H z@sjT;q;5sH=P9!bci$jj}#LR$6V9iEZEtHz23NQjqyWut3_A>I#tT{F=b?OH!oZhJvL5#qpolJ}!3|CYi4?)T8AivDO`I0H!W3yaGk% zStZvrywhM9VXkS8tY6z^5?G^4cMRU<-qP6f zIOB>PXQ==faz||m>l;T(?Z={JFl;X4dAPsGIikzgO7$0MQ0}1!Gax2sNkH12N|=?@ z^tCDS+_kdXmii|^Z9Pg1x_C+wQX4nuClto0Wf zw*nN{K8T;0OO}ZP;{wYi0zS&ry~&uvG0UcHeseL%no#u&=qVf?R-2fXo+eP3AU#yA z{o`Y1#;FN+xm&TjGu5?jib^^p6)i-8pcD8)uPy{(LgVb;a{5tL4=i#;cOztuRF?{D z^kkyjOFbkEw~^vvm3?u&%W%VDn`;$3Q)%p-@0c zQ27hC#ww*=BWYsKM3kvAQ2>>Z->h1oV2!IjzPNPH(I;!asB$h@M~K=Al}ysd^*7V` z19O=Z9pFZTER^t49-CM<9;c#v`CBod;d}~@5XM%9vMa-3nnXAG*=&g^!UuEWV9WcV z6Lz<%V+)TrGW(Kr3NPT;eXm zGBn(t@h29AQ*U=TCt~TF{{ZQN+eCdd%gkmvM589!6;-&m87kz~)YbZmDpH9idU(f7 zmDNf*8UO-d^XG24)=y&M++ZpC>$AxIrd!uzePm_O0jZo)XXL=2r$s)5O=u_LL{9$}(HwAz;03R_*4j3rW1P(-|9 zL9QD$P`0{AnVXs$3Lwmb(ozN?U)4{r=!hFi6S&WBf%r$w3BX#7DVzXlsMcl_))JFY zt}E?V#UHA@XB^>pr{6=1(=OYNyrhB!OTa1hB%cJxM1`%jHB}tl->+a(cKv()rF*Wf zS(tTd4Vm;vIzpcM6g|8ZVgQ?j(n3i80D3+Rs(6ZZR*m+7;eYyr?mr~vJGC%YH^Wxt zF=TZ>6sq}lNkr05c328O_u@a6&+S^aG1ba%wfOIQB_+$pqCTIBtY5u9EYy3M_x(@B zHVU;=*l_jIVpIeusR?l|3ag8WmXeX$W!*8AtoM0-fpBY&Dh=0>o$JOfq7Mrf8c!9W6L z>PbEXX$>}ERMfFJgyY#4Lo^RjI`mP4BajshBo3;0Y&A4v(+PY?0S84eQIf*opDcEv zl!oM?@#zv)&I&H(GE|GiYy8ZVz13F~4%GfTbW5X@bW5ioNXO8_3>_jNt#l(G-3&wb zFvJWULkxn1G|~e~DJdb{sCd5bd)B!)7r%?&)o1Pf2kiYkYp?a}m`=WGlV#)QZ#nC& zH7nD|#~V<(oE5Jm@=A?D)!VduVZx&nx_Yfos;bMZN)QVwnetCF)Q>LrqN8?E6h1r> z`BvrGv3QCjjp;F_4YBmle)KzIB0g#2N2%STd>zA_mG)-*(XUQUKFQ)IoZ@nBOM^~R zP1FMXPo#bdOmQK+QH33xi9MZ4aQc_XsO`NHSCodS;@!{20V0%e4~=uVcK{l%zrHOF&Y zqRNRGs=`|Js`Q& zRXBCNGjvKOgeYi(+<@A9e7~!Sv|MoG3wDOs7C;cA>iMf1_JF?`8%|WIDcvY-Yj?W! zAJs2Cq`CTR9CbRnS7|C~z)D6h(gB=*+g5A@S1e(?>~Daif-(gN4ObwnUbBhzN<$0F z>y{BstaHJ(#%4_OC?VG6&w%aQC-m~vJ{fJ(79%QfBO%qKU6MZ>1pQJrpbyV`-yl!p zb)m^-L+V%D>BLl>6%J08A13ZFen%T~w2+~5*rpTW=Xnea_++4jhvV5K9;%#NpsNjT zJcl*~zRw95m^n9o9#rd}ij`6&&SZ^v=g1~OsBc+)!nh_RaMUj;?%=6tbUjv2|x*9US>UKIuG^y_YYRo16~^i(`A)4RI+%(YjC^HbflR!wMTj5pi~ zfq#s0qiHdfS&7qr5cb>0(+3K4ztd5*9NaDc%}KiXioRuF=tL8bUX6oW$c6QOT!_{- zskXA0%f+@TiVlPPVzTZN$;M@Ed=!a(ykPB$94Ua?IMs!%s2(R|`Y~k%3SDukrq?`L z$0YD7@NsO9wedb|LFel=Z8!~t6zLSFRFOo!AEnSjZ+8^wX_^OBl*<}K1T)N-w(Dr< z6<<3u+|xVn>~n$kxF`2Q1~V6vK6xY6kGqhkrHkaKrwPqoA0*e6ROKLqWUM%ctk&?a z*|5t+B|R$D6&v@CZPVpsjaj8iMI$C|SiPW50XUX~3Q?>=b+*8FRO!nCHCqin*6_+= z=rQYX-<{OYRv5|8#A!cQ@Wp$FsIiWuC*I?`*wctJ}(69o7*e|vx)!h zCHkVMw0^f^7ErQfpru+Px&VYI96`7%KiBhW2$JqoONbTB8277hdxYmgFwJYEB*(Nd zq*kUn4zCo%=*j?Dsl}mzPsP?Mw<_rrRS6oXsKQA772$t-n{EAv^0uufvZ9(=jjBX~ zAoVR+e}yllk`Pghjt^&CQoP-GlWGe+r#l-vG8XSJtn{s*Q$}&+iu+7>StqHlcJlkG-aEa`}di z3uY~Z6S0_@DTmJ;@2uSRWu#h@kM3b4odXfChm?>f?NB2Jb1@)EWPq8Qzr{q5&lB;t>yLv3m={{ zW)Pnd)tPni%3N>G&aS*%$w;My7WX>}zbcY9?n3Q8Q#uJxx0UPNNVR?zFzG!aU^;KA zCH@bv+phNJm2Qn=^qcQ~L3IIUMJhQS4;LkCSU!LE-)3;0V^NlZ$)+YeLmq8e%*szRp`9;w~H+@#+5i@*$ zc^G~_&X{Rs`4a&zme-2wuiFI6mm7<4VaY{w+xRgFM&oq_7FRS}h5Deir-^|s$Y;M6 zA{7!iafu+dJ#ol^${zf7UcDUw%9Q6Um%K@g*Xl3^%&Cp5e%0&nt$h(U+Qmg?eG~m~ zr}1oh!SIM?MqwQ@a)%GNYK5;=7b+?Av|?X*eyd7w2UE!S&*XXY%)cl{22(`R6o=;h z@KGVK@A9J3S%qEivq_KjF+kfQ%f%=Yk4Sj)Jjsb|Z6XhV+Rs)pt{PNOnEJk^=y7CPTG7u4nM?d@a5r{uxXU9}UJV7UFA z9yTnsMeg-We;8ztRr|t@dX;22KO?fF^Glz2$bMln!d>NAS6a^(+D?0*TE49>pJ?UrzPEMO~ev=qMwb1+b>FC#qI!_lg0)bs;cA+S6HnH zUv-x+{6y*#1CLoT>CoMpF$&Mz7e~SiJu)T8vDJ(0KKX=4WtMi7 zTXSy-%nVfgtGw!e+IkC@lPXjqwj{^&%^)Gw6t7NP@XPwelTUaT(YlTu^)kDL7w+8k z-I_63j6XScO&6cZW5>TGZ_=)Iy28WXTE9;`%VsvfE|lMI{lSmkoDNq|f5Z5xcl^Fa z4fOnlOy2HMy>-6smxG;fZx5rE+Hf4c*IK(=n-StouTPb};$K;K+))}U5hr%*ROv92 zF4P;QJaB*BOMeBYzgfwNM*<)1@V=XMwT|1M`55bE%w;&h80wZv198yRFnfn|E=h!5 zq}AXfNDd!atzfhOxLyb6a!;%;lv&e*UD0ldrI&x3X(!rR1gBSC zntBM{J_)^9#srwVy$*p&r3-Ag)W>8&?m1EhF|XF>I=8&UTE4k5Asecc!*O_2RF!qj zCoaO0EY{GqR{dwUujmDgbI+wIa`M44g9l2^%&Jb(jN{50)g6k}A14jSqLxuTgU8r3 z2D}4$!`!xi0LP-z<7zQ@x2@~bj*TO^_^1A^re9j|4}~L)k5@H?=m(GdD=Oiq&Q?(8 z;GXqS)K~YVFaF4*?<;X6NB;m+Wh2eWR{FhzF8Ue~pW|7K05p0P1iLS$wgjI0rdN-J z1V#qjFHv|f&6)*QA8*+idN~;?b@8xYiG$kP{Tj;5tV>K4T*kbO2PL=p+!l8f9>Ju@ z+Z46tq<-r70_eI=O!lGpjDz782eqMWQ?r!%`dhufJ|GWm5Fr5 zjX5N@q7_F_o{c%@HYxRw)jo3cKWU`gYP%tel~nC&->uGWqr2HtcfNa3luNlh5C3-i z55Sl-rFgec)I%ks{A}+;tK2jv2g>nN8F`Y$TR|(RIukAT`F;Pa$0#OTz~NyQVGNJd4zC8PEcc19xFKZ>KLvV>^c_ z;@jOs=%G%;Z%sP?0lJ?Pn-uAk>7WzD-iBC;8>apC?Xw#_iKVxw$F^9L8i8iuQcMQ` zUb>rh-a{-SzCl;GW~y-@Me};_OP=?ctu|A$GMis|;=#)1@>M#DaN&UlRN*?Ob6?Sm zK|Zg{N~5`qm@*ww)|g5T{(S)|4HHlvrXY4f!16we&xC~kT2`Ip9uw**g!UICR%6el zb1Gx%YXS17Cb3TK<&gqI;{|qJSI#w_uQgBK@q)9^ZaP>aDbE|yoRWaPFBM3TeSw=5 z>qj0EU7VOvTxA;VqFwu&PBDIl_-P*8#*(m|pDh!~dD#5(LJg)$%L9XSc?HUAb?~|D z&~UQ-mbo~sY^Pg=uSYQ{oOwlXaX+(OS|R%p2eH8?V*loXS+W-!aO6YZSxgf(=#)2P zG!cikU5xO+^nL!P^#lC!DG7}g{(|-uty=HSF_dN|r;fZ|?3#hQa(7BIneo-RD8QaAkaAo1HC2c}40A?`DH+x)Xw#2z+Jb@=0&rg(Q>7b%P;jZ>Hv=_!wm@wK)v; zAp{liXPBtZlF^faE2~q=`#JQwDo|Ww=ceMd!k)_&paH|OZxTZ9_5g7{k8M{q6aN^< zts3A4{LS%}wsF!J%#jEf>#|Lhxvb}=YcbrV-Isfx_Vu@SzTHC2!;ha#cg*gON2iy= zOnQzf#gL;XIlcU?ih`Q$;2kuHJRuFTJU^XAI-a|()ufMIy!|M8<$aaSz10+OxW*Igxn0)st$a^K{rz6b2TM+-)EPvQmK+&Z8ciEz1 zj2{rn7iE;bUn6$fcI;eX|BJXtZtOKGF^6$PwJ2HoF^iX{G#IPFX+wvzzxTwt}mU12^aVotu+E5))|@T2(&qwxmh@V%%iO~ z!37EVV3Y|P#G5+~xgsP8TiV!Kd?G@RY8hdyWN-_Yx||lqdIG5`MZyt$%dD-^nFIw2 z?A-o1e&2p!R@8oN9hKWnJa>1g96ec_sB+4XYogk{A6`K5`XA@0x#9|Z>&S0We@|u_ z8(-X6B7Xex4vJ^@xj{(|7v2rM+sf^l=k#0XdJXY~p+)rV?t6~Q-_j`YtH@s$DQ}kD zC;!y_v7Qt)EWAdMxrQD7)Cg|eChPZKT14MvE1kh-1WcPXtf89>9Zrb!EpqKslQY%) z33M=la?PKzK&%dNpEl`1`sABNLj>+VZpf1Xjm5fb)O&3;6yh~+IbVr<7Ou85h&(V_ z@EtUN+G5b4jJWS$?*r@D3diYvUphCPp69G%b}a%Cn#VeYk`g{zP!OGrcqciP1XN0J z6J#Ty1xwvjZ!tX^(%n=Gj!QmxOy*{kWJ=F~LfF%_wC=NtyR{guRohnRAeOhOnn)d^E+qeMX>BDS# zgn6`!A1Wm*PpT3=li5Mwaomxp~*$t(~^y#QN-fx1Hsp|CJoO5>q=p-_RtH=?>6wOB#AGGUy?f#MBmstI&3VD!Ofpw|?jtHkYxJ)+kQ(R6yDA ztEAmU^!WL&sAA8qgstYcShOu=5jS)42O?3sAJ;SgfTB#w!?M{+N-b3LTrj4K(Aazc z<(wQ-kv*``hLf&bDsB16j6a}$8ieOEXkaGiz`q7X*YA5C_!prOa=+2%()LWFf<*&p zA*=P)m7nuRHa~@7`bkpk!uiKy{x%`)@&sIJPpgGb1E=POJAhQJInEWlWE8l$%bw&D zrd}bp+1XAG8pj@2qQ1UrU zj*9l}(_$*xs-ALt&5X9jizl0#i%_jI&)D+W4<2!Xm$k3d`1nsJ?$KdoGIE$^zzbepsO100tg-H(hQa<31kuO|-{c1a3# z3ctubtEBuwMyB%B2vLVi%&8ub;L1U%5EsK=R9sk}%35ovT0-tFIQF;Y4TRBFV*FUn zK)q@<*W5%bo>_1EoJe`=)m~`o3oe1tU6W?fdy)w z9^5I%B75{2zOubazB}{~N%wJ7&_4i^Uq-TU@zva?*7p0V_D;j!2rZ19nf+-{p6e0% z_)G*)XT>oo+E9t)jv)E=!`5r)@7mU)oty3MxrXc*owk+I%H4G{Frupf`l7wI7FAH} zst$<%Z~n!c08p6|?|i+5Mx4Cc7_fD-w>S^Z={UPDhjES8|yHgS{;cP z%3tjlZX141!1A&^vDLijPfM(&ep9Zr4Xf1H-qT!Aj^JdvdiUS6J`gJnBX~f2eU?CQ&tdaB!PhqR zM)t&gQEK9^#RRx~HR*{qs~-Zz2YQ8QD$e8id&&4+y@R>7SnWg|l6O!HIscRSwLdZu z(^!oN>op$%x&7C`+8(mll-p6WT5|-miHKJ0oZlsSZ~lMf|BDiqY^Xja zCZ4bS!mzDEW87x%BXXdrx6GxSXR`)tirKy_o#Hp6@$$Lcm8bM#r+%#mBbQjO`RQj+ zB@JojCy^9)X%1o+Wbh&_HYwFIZ6W+Vz*BIgQ-TA2m%C72BlayAl`0--w5&fRp9X*b z9UuLL!Yjal~OJ zhzeAOLh_Y0FXyYV#(Nbr%IV!tNdvx9J~-wg8|&&MgXh~W4PB_`I)auaSprhai1eX#XB#|Ew{F^bKb z9|URF8ldj&F)?tP#&Vg|h%KwU?o;S6o*AJX5Ue+Zl(XwAvk`}NtJl=08uMuQteP&c zBA4u*&~+lsj{wA-HTeRX&a~?0o{xuo0DOby2gdZ4yM{InwFixXwZlqw&tTb^63+PE zJYLjib}!-AN_zE?E?jNeLLU7DivIvhEY{}Lb$+A`&ppyypzR+x$-@e*qW%HiUaOGr zb($(`>kd#iH9&xNZ)F^@X6>7wR|N4(GmC8G+5lblf2VuP;2lM{qsy-QizWj253@|n z?C^`!zr#qCQHmrX$;#468owf0Apkw8(qtkxj*3t`hmDJuXw%@xXhn5g55DR|z;!W0 z^ppbG7WS#tMM~VRl`6PV$MX5}qlKWsW*fyXSmIxF)Wz}$rBE1WbmlPKO`~Pgdl*zF zpA}!#k#Bh()EMSKe^|rB8;H9xnGW-j5%j|1PQUVewIH`4AgBz!4|7@+SF#^tqt$(l z-3gIQwg0AUS7u7Q8y<7!EF+foysg75R8>*77D|ICSC4RddLeGM(bM#CT#paK=O$SY z$UDJI+4@#>=jlPS#|QofpNq^z6b5}{aD=O}q%4&Ve*JF7;`5XK*&;>g;O z^Id_UiDP}EWmoX2x3X{_R5=sov%<_X*!N29=X2I}CEPn3pb+1Np__9yCS)S@alH{z z00;_sveXnNo>CqN*;3jFY}d`rQFXye3EZA1Gs9^ex#j>-9EwCioxNk39U8H+857$*EQ&hgdlEbTM!4$Gigs}wR|5*0>c`_k1n17+#pwS}@_ zGnI#_LW`~6@8532-#q*Rw!$YI9=!E)F}tM4;Qr6Ydf3jV=l=jL>|MU^XB1A&BEDuw zrn1q~dB~FaBL%{*5S~kK#$Pz)jOa@hS_*+a={DaHzlu>Dh7m(XG7lTB1X{N!p1-RL z@^r20msI3IY&jjJ+jC>y6?kj@F@3xy+WFf$RL@Ah@m5ItTKIZtGC1JGYbsbXds&wn z#OhYOO+_p}iuW{e{9Nx13tHTA{(ZqkBim+hnq82H$QU)Pr3mtNFxgu~V7SKnBWMnb zfo8XH*cO0D$CYWhi7y`cW*EK~4~s+F&TakyF3|(hcxQ!0I^3-E71Dc~WG)R;h~7Bg z6)UGrI&Ze7Wc{bHzrP6iu{EY!NzV6>^|LhkqLFXcdthD-grr?J>|M5YF4?eeTE$z^y5VVuHfc*{^4iiS zqtPE6s*)e|j9J@#GJ%&*e&8MNlI0A*?EOZknbyHbkRU-tfcTq^HW4YfVlTFv?(ll) z$mb_)wH{4rgtS^n9dTvHtHY>dP1D!;2ZFz(w%wf1mJ05v#drELZTxaA7#|NU9@hyJCer9Wdw5AGqd0vBlfSV!9$&FX)R(oH#%K!*vd2LEXWW;GqrYUd zSEC3+Z`O6tJ&#|0%pYAUQSVt$Is&fRyK(E~dlp?K2>po)_yF(v(KH}z2uA-{wLsNo zp}ofS4khH($+mfH%*O>XO@gXgjq+IwY2h)EY%yd)dl<)A4O{&khOcTh`g)aBGtT}o zar_xf!~FD&}D z?!KDWG%b|yY&ZOLLt^y?>ZIF-A?60|6+>XMG{meSVA7`dyN#u|mDM;;esKd`tqS!z zk`>g@o8Yj#j_e;hmk)&~_<`BQ2Kg_)dP>k}GyB8jtkj>5X8;|AL>?_*Dh0hlvJUKX z<&!kUn4$zc+rFbVNSJQYa*aeQ9EuL`2c4GO{;Z#`b2(VX(tru3Dv8v%P%5mO{9--Z z1dh&M9cnR1isUW2AarF8)w0#46%O!p{^oO$9(Al4rg&Z{O1<&j>hKZG)6X9C%x-Qt z^jHD*oEblX`L3iiGmd)P1-VjIy7kktO`1xYN=BrX36(n5t zHCG%a%PK9xaYGewLxccYtdvxOTb3^I@$VKFjknrAEyx8BQt2jU5FxY;c^CI;7fqeD zyyWY{gkwMD+6nru{8sVPwNtqs)0sM-!{T1f-y+zQ&V{};0M{Qo zzF%lrHl;bw@bliz515If&b^Qi4oFLKCrI_IQ#!;{)JZZjU>LIZaWA95J4&;{u5L`V z-(Eyzk9j=-`defwmfgrC(XLr|uI%tt2kAc^20w=ux58i{6htR!ZKMzx8xXFGYu#1Y zxADak-euh!X4;arh)Zx+riw01@G^_-*v32U}5P=3?{p+EZ zSE2WnQgKKaDNW-|HV3{2W3X{>;VjGhq*gZH{7oT=U15)|{n_PN6b@81VVST5pd8ph zF33l~7(6R(`lh%}+dBt*K;H3;yQG<66~?g=NLtTH&N`ppZD0MPsNuzc2utHxO7`4(w4e+yafa@lm-M`is{_z4lJbl@0lm7iv{?SLt zBQlmBe?rQP7M#ftRdn^mE$HQpXvCPr%#k+Cxy3r?^S+@O#3r=hM|iC!D>Lu`$2W06 z?%>oGFakTJSeN{1+ha#`2Dr(LsqIa`XaPPAaW*zzGb5EB943yJu`tZg|?u4 zz|v_9G&uzf7mS-~IU(;tz_ij5&>pk`S7K68G%O69**5))jJ`k#3I2~#ZU-b!Up}F# z-c-$0-eBip`jbLzt^?95l4j~~@eY0O8fQmM@PwCuNz$HIWyr-|FS6>pw|aT`@Cpwt zj?Hw6lJJ%BxotJ)blN;FvGNLsbYP?NBT+DoBRtqJfkh4W+lH{FXPUb@O|DtzzUTa5yYXb9&A2- zfb0)&BQcie+n{P?3aS;ZCDGHkMn|IYJL&w3+7iC4;}&_4bk=q8xJf7~iQ&(~eP~qm9_K0~8a{4pL&RBrJSL}IHgq&EM?Dg!ZMe3pu7Jw~Txw#J1I#qspWv28 zba#X&l!(VRROzk;`X%LYNssm~^Ka8({=Kf0t_IT5*rx@`O17Ka4iO1H0HNSuH2+;& z+_*qw45MxO?#wtrnCl)wdCf{v-Y=&>`P>hbbBt8^Ij8zMLLwnpAJ}F&s-@2JNDra+ zz-#>Yg{Nt##TuL`heeSjAsEuI0R8aN)q7T&JA;pldMeiyq`5Xl9F3ZE*8F;F-4IT# zgF0jy-fFia7VZ#0deW%2PigKa{&}_6KqZRS7D}z-0fK)j!Ff13`823~y=FkX8`?3I zCWhm@uOpJs!A}#}z+Z7}?WCwk8}-Z8)45#b^|S_RWR((-vsb=i5X`;9%*(TUTYtr~jjEr(4;;%=v@p+jvj8?8$AABm{0nt6CFHobO>=;KaXP4iM zC>x&1G$iTF8BKq*syRRi9~V4YB5k`O(E3w?4VVxKd{!{8Dqi9`xa}JTsX#xLSvJUq z&E@RyYws_8RNDKy@sad}|5MlV>ppym;+ao7fp>4yvZ zm#}z)??(7X3qqX-1ApP%-GC%#?v7VYpkl_tHy)~gzW_5aEpb3Xkl@D+N9il)rt_ET2m^-q@#tp3!n zg^gxdRF%IKmQC>H%HaG@`iFd!J@Sq149Ll|52C=7G&7UsF72<>GM#FZ>!el_y+WLPJ9`COD&f1!WNT@>LLVNz5}l#S&2kFzasB*0uK5_9aQax zua8OfFDm986f9{xA#(zn-FZ@)B4(Z91Q$ki%X~HV?t`VpWY#^}ACMsymi0d&Y%XBu?u5&|F6MHxnt@Hx68|q9k>-QwPnLXcUf#0?yZ~kDdCV7PQ6L;~cBrclj33(Vb7q~qd z(`a1EynFdXwT`vShS2jRTXkQ7^Lg?;g4Ml3pI?TgW@ejvgZ>9+wAwdkI+HwRx@^Z1zkJU zGsde$Q*T1nBY|Q7rdQ?j>PRH_DP>35e840WX8AZ5b;u36UU(jg*O4ius=pTA=K+hp zVXW!4wyCjlltE9{wpX{QNv!zhq#YheM2S8I768G^b#FuL-GDew`0gtf*Gm~PFvf~2kDH#$$|31H)yGa09rTiBo0|xe zo`L=WxPSH{7OHuxRwE;GR`y;T(ClBP$Aqx=)<81@oh_@?l4kiV>L!L+$)&8*eDrCV zvuw?kEvX!v%MDY+1#k?_`?j#I{kN@L+o6RTD2-NSG)Z?`q}C$E$c7{4UD>Rf&(!-j z2{8-NCp>R^zbiIdHZQxm#k<5^BIxUNX!B3%+=KL_Oeb**8cA-5x^99k4BFg^C%*~@ z01fqX=4MnK!2sHfj`YB%K8aOJyX4|LW<*tNsY4OXCs$XYt-CPyP%1htm zTo?8UU+DE$d)X;A^U4#bcCh7{2%iXFA0^s=wqx4q-YAutGiO-JyK@pH6SIhQsYj0tJP%A4-;#rDherd5x?)azD-|EjU!g zun#vEY6`ejwS7ZU>uBj^z+_-iWi&@mU3+_>o2oyH&3ev)Iz2YYQ;Z1OPa@!LVjXTQ z#LMJy#r6zO_IdmPs=LAXGR;r}l=$v9w8oeLUDy76;uv6GB(rAY-D@$yk82Zm$v@>; z>BP4mjLSy$4i4&t=im4GnRgWO7rcm&Ugc^%S| zLVi%WcARjnVvA8mmrr;B#!CAAU7kXiAD{$95!!h3R?QJ!i(Wp27I)8)f;AKK*kJuK z*S})310jszS^f*%R$}?SkLd?jVONoRrrp&rOJS*xW4Y@ZUE7c7Vu5A&Y8q< z*#{#vS635ORIqY^zk5DWpgYvmXKK1W0$hn{jg05)+9|j}QR;)#e4)_8)YH%i3a|ID zI|d*|fG1#F7I02Lf`AqC5oetb8SA^|Z&bijx`rW{z5%cQvT~ukMwkBqc1xHzgsVa* zi!`3EZBG0-ypqcC#U4ycf2Lm4>liN_IOH7RsM)UNb|OB>p7)5b+J~e3tM5PjfESmk zk0%^N7(D`AN*S96^%Bf$vp{Uj%oc49QC`wh$a@qz8?%Y#&Z~U>yBN~0M&{r=I~xYO zOY3pixlD9TwJ7eyw&VI4wC1|VEaINm-nq{*gY#OA#XTzYBK4rWPV?GDv*pGtH@Z9~ zVXlXFqM~jiUiiyL2c#Gb2^8^Or1%ZaK=?N$(*9?uZ5?ZWw5o5byYqYtM1=cYn{{bE z1a>dWNDPFujWUB<`F(jpxV#pxRJ~X2^M8Mz)l!le5M&Elcc1NAda+MR%NNiZ*WBKA z^|Vs^=Xq+?@Fgj5e&+sJI`3K!?cZ7ZR2XU8om(`P3aYKW&!#iWjc-|8YyD9{O&Q|P z9?ZvYAp2TsVeETQcd6#;Xvv98tlC*dLJ{I&C7=TYDNJ=x{3iQJ6RrQ<<%P%;Q9=c0 zj|8Dg*i*Fc!JjeUyjW`3i`U`mYeYY?tVf_F`rGo)n4(Th zo(?SvicaV#FdX;;$uS8@1%}AZe}HV1>M-F~%YT5$Z701$PhkoU4t~uNZHL^!H8Cr>5rmPH83GCAK$z3@LNQJiE(R zewbW@n)2CK*-)U?^ak~~`P^`wi(jZ;r3(eLUkF1-+n!I!H_nP&YK%0=32;-lq`vJ3 z8n#wbFZjfFKULY4zh~cN?Kj;qwm4YGFQ1G)FG-YueXYWu{@Z!8#9z37#7KOXD)@={ zH-zXaY+-5Y!GvTvLecPm?yp<-!}V&1@iE_bjf(g>>~Pgsl7g1Ku8Z-13VZ2bu~uJ- ziS{(fX_dKXG+I;h^?-2`exI%7y7hmW1qVx0NR|a$g+EIC+Zttq5|l-Rn@hqT znuJzVlT!B*2ei$P>Li8>;Qjn~#p)8q^1_yS?w2t+SW%zpS%qpjt3vfge{#>}Rox9- zLI^$is3l$IQe&T*h;vOpU%_&A!z-^jy4$(IBfx*?5ldWh8c_3@d zEa^2??z)2xhWMbAVd5xl+>k2FTn}P|B8VIaTJ%Q)hbh$)J}m~Ds+3wJ_MJx?n;sd5 zzO2Q=0>j9sr#OyF>K30Fjfi1-S;ILrRbQ3NB^?c>LK+t3d3m?2H*>n`O7W;AwB0Jp zJ8ltZ4b^IR%EwpwIw+A|?Vh*U{5O9bc4OE2U9L>FesB2Mpnfm>&%JQay8-KdrNq|G zYK>Z1mdpOpeA(eg0}G3}$*G*L8#kd|h}VbI>tmd{v^MbNX35zgNmlRn8mpRqxN3`{ z=;(F}{G!!>CaF~IY+?LV1{v4eV{S$!)=xcBQA_2tN0cO_PG`~7i65W0v6<}8$$ulY zpNULDiA+ddj4++k@&XcV>n#ToEF@zw&To;f<_W`>KG{Ino_}s=E~s}pC9h<0cPm-$r35Jd%}V@<%D z#6ZJq8FK6WtJkp~RKP3W)Z5F)u8)ALYh@Yp+9rg*2Yhu(&eh=d zGT&Wo3t&if0>dqwE>^Ei3aVvCkun6Y)5~72N_gwCT9-i0 zuahpWw%f}Dg)S!g2{uP}6i8wayjM8-ZP_tMBP5tC6}Qu(36P9BVK{izMv#~WeEs*OBB@AF-OnlkI=IJ;jfl+&j99qlNtC%k)lHEa_s$$CgnH=im7QF(iWx ztNa_4)G~~`=@T$W+TrPKdQ!q;dD2N}Lv@B2kbd(|d2-i3z{mq>zRZSy1d%tNYy zRfUD}hF{;`^3KoSRJW%1j&#cM0=x6 zQP8)>`;2_mRShS#^%4Elz`1qE>`&)!w{)CMaa@6J<}4#VKT*UF%1sJya6CDdnci~| zdOaG*8ZF=C266D|SWPaLNQ%#jx1RrCH@8mYC&a9HfU9)#qD7i*1BtIm)YiAFyP>C5!!J33K~Cpvb|-mt(APD>v({UH0nAfZHsKs6ewu;9ECc9!e) zx%M8KF%@5ky$tKM)0)`%zk)4PCtp(h{FY$*e_PvaO$+$2=^a{zxA1x=IFEe$PpI-m zIOo7O0(zQbnrx{l0&4trn9;nJa@xKd-T2HVWj>#_UUa=m+&!w4Oh$9ahSn7{V6BYP zuc!wWW%uR274k$x{ZbLht+D{ZDpOyI=f%P8`5@mIf6zXEKW@3&#_{TKk$ZzqfO9EO zeR^d4(I`tsvhpo^PT&l2A+*z9^N}#uMF>DOOEC zVA{t+3io`nBAhJ?M3^)cgTuyu;fp+B<*K0y?5K+>EUHu@8BUQHG;AnBVk3cAk?H{v zxj&&y3@W8NBAK7@;2~c>Guo2?nU|F_{sGizoFZM2qN6Vgpvhix=T@Fh+O(Mxan}`f zCnN$zqxA_MT~hu_J5)F1HD(^+{j-v?<2G3e&b&@?aRt($Bco8Iq{RwEd<@_}18Yl_ z;|ew`ERR>jaO@N_N$Yo%E$tABCp`WFeii4$DXm!XgP&?Tn^Y@Dc_7+;6H|^8;`-a( z5i5-jq5t!;Mb1pu1&#d7`zl}eBPXA~4K+=2X(OKIRVG;E;T@4%FI+1J<}n7{}w zH7dJ|>bXAfbaA1YcPx=8BYR;IX^DJFE$!}Zi^|N}=4q$^Mg3vSZvB(rzFn2I$R>C8 z1O9qPgmMY4Vp9jn$wnyg#uhWktA{O4xwUVddvr2!zSR!Ov)4JXa^etaf<2e7Gt%MU z3{BTIGL+-tX`iw&th>Kve@~>-e`d80Dtp^vWw03IU;@$Cj*Q#J)p{Ev{}VuEta`@^ zDz#(lOA0gslp1{4VuDdz&NNEC z5iw_je}h>GTsK~&=}@GdFB(Zt%{=~^pIi+0*xR=qb$^dAw;(atS1?zPq+PO5f0pjs zM*3qSM}4{L1^5ZNkgQC|`anbozw_>bb=U~ZpiGLo)>XP=RsZTJgI-5Xr1NSo;7=%X z_*l^7e|Y8qNUPOv7}Qtq;wYn^_s08e|6cEjvBWMbr|mqu%=*F@H-O}CQ_$1wC*Me< zHDq-wrI}bno&{@-6FEd`5_WwCRJ2>Tz3_Eu>Sw z^G5RhKXknHQxp#PExdGxga}A?D=kRZvh=caN$1ktEhV|6G%T=73Br=10@9rV3)0=v zpx)1S=H9vUzVEy<&&>G)o;c@+a~_X`u=X_{!h(sqe- z$7AAJAU~ZAN-UtDRZwtaMOL_vK#SE3`laGBF=6KT;<(LWA#N*NVYK>ddXE~92^N5h>jA7g*G%d?gl34l@iES^^Y#xiFDK_iB{@hy`CEOgfK$cfc z#eP*KFWpR+pvDl^e8`)4(!V+cs9Qi}WJV*DZ9(Zp? z^sP8mR?T!!SN`t|asg)qU*TC0<27#swFG~_i}q756?LyGK}8E<)P&Hd74g^dpZ@$d zAN}%&S`}>8xU_oSd01G7sP`HBET&m=ofu-KxP{~l`bZ?pyZoo}QMCTcqVB??ZgzfU zy$W8n)k*Xvt<&+=UB$V1_!b*?<4EHQ(N>{Vq8AEXqXC)x^h=Qff0?T+Wt1a3=LSTY zJSB-%ela8lH}HLIzSUQAt!`{Jb;oFh^D2cHV&s&AaoGj572#s+g z<(Ynwj50f^m@Ly2WHUM!HVg|oS;Kj3yoF~Q192rKvPDG8Ij3b2b!Fd=5g_o(cB!Ho zc}l~rsz`;4&I0bLUOPV>xAXS#Idgt z@jIuWF7Lm<*L7Z?s^(cHEgb9<3{HLFSv~RqN-4G3?>eR{%}d!Ast{3BA;tI~@%PR( zJVo|87d&&B>tQGk0 zIeXrCl=FkD{leszd;Jc5^I0vJ@u+zmrcKM;C`q*lSG&v&9zK51;|-f9m~ePU2IHtF zcwG6?=cG|r%UM2fJTG>w9gjFjhYN(j%hjAnFZXPHm2BNKe@CepJSBcK+zVpR8eSwQ zhpLG3XO-)cS=H&Ex)Z0G^>DouD6dXZW1+~Hdtzp$%GNt+_<3S>fFYeO^?m9`d#Q(D zA7(~g@!M4*bq7Ge%g6&6#-ot zDKQ z<8C}*^%dO~EcE6nX958&4Xt@b=893Q^~3V4%Kk$eY7O~r>BMbi|EDwRW(Ye{7JTyD zm{uik6=-=_;gX9ZO+T57gy_f{|k!KwR6G_jaY^Rg#mOT?0C&-!>OR zpQoxewyw8lueEa7#4tV~ z1=BlY+wk%5Mc?k+!WRCZa4pA9$ECv(dIpzk%KHo z5WYI)d?v6IkvaBhl9e~3wv}goypdjGWnPRnLpJN^dG!bCX2vA`9V3{BAB(mAA$I3v z9Vrm3n}j`rKBb2>!+}gGgoLE@@ zsIKg(kkwnnL;9OAOVl}6qjvlY8T~%EZa+2OSKwVktQg-v0Kt84l!e(Jy;q`ly-v@l z70vuumA=lrQC4eL)SZ!CmS@Kd_nHQA{^|W-(#g`QQ<<7B-PwSfsrN%rca~4PIsfQ2 zcZ7eo1#v^7wa55RXCPr9C~V{BFU11!`NKjDIc8lBt+H6>d)9Omm3ZXsfi?Jt zojZ!71Uz*LkgCx0r=YF?31nJH9Qv~<=jm+_{A!J$M)MzF?H|BA;33wXj`}5<{oZ3stk(&TR6zc zylD;!Qw7{ROP+w0SqN+{K!obbY5jdE1Rkl0YU)1a#z0)qsU~|n`W~V9d{{Da4_>Yo=blL zp@T`AE(ykwL8d=bm=`wxJB`}NyrgO; zT->1SwflESTE(%Y^9 z(cSqRlliW6PwQ{3Nih@8`N4Ujr1$J(y^4`wXhGZf2OYxM znTD#A6IBrIu(+-q=FvKSRYM%tl5Y}^2WR&s9LgX;?N3>Hrk4)!Fm}72Crrg*Q)efCEp{@Bn(A}Y&WFmb-x`eA z`QV|nW%tIYk44xjwQ%%#_y#U9Yr>&lfl4dbMGf0>HhE>AEQTt z>GsK=&+;47KF+6DD_>b3y~Yn;9sgY;&mJGeRh|0>Q1PiQ$%b-YLB0x;mJS|~xl0$N zZKG_QUP|a2-o@>@?a_A(n7`Pe3uMB~$Ax85z0|CJdR>X9r(Es|ukdm8FHq}Q@|$@P z;-_Z1=8~R`ud7FRnIS`(E;?oiEF`U|2mS+)-z)0zDNYf8U$7t+{jQkUkUZp{U`LFK z^P}qpszfI=h{q!_bJF-?$}XOm9aoHW)8hBORTgPXio*1Hq`_mo%J4VEpLnQLuyj+Q z{@wmPDRSOJU#kpGGZA6b{8V>w`1kc5Np$LVc=BoTN6w%DU!XbS5;?_KKr*l1p?vtrT_&hb%{f-_~WC z6yNv%2<%nfmJH-QbXkBK3cA_cKfmT&Qla9{iDMG+)Lt5MCV4DV z>-;(IAraTgI$KE|LMz`4IIX#}_Ea{~EUs6u>r~hWydtnQrTS9e!P8yW(cYTP6H+~u zXFn;bKW(y~HMv@#09gRNx5kh)gnC} zDQD_21DaCfX1-whq}3{C8h!)JU8m+`_8`~BMdk_dotw*T>l51d^K$$@xnvtYnIbDI zyLRx9(*5wBw_V7nu8F21%GDT~l;#J@PngZblaYy!eDC_E9p5I4@1-J5ZT%}*U{K&b z_Cx=c?W{_+`6|WsCsFk!9$@Nub8kG2A8>R9A46Rc)FQ(4g1l8{YYiWvnWN<9i*PX& zkCCaU!{c}PmDWM6X{n^fTJ{Eb^^8*bc~n}7a;oSMF1 z5MQuzY^T~Vjt8CEa5--`A7xq(5lqyIXRg%6o>WA;wm!D}wsr!Tt@uc(I6#N=vZDgPxH}Q-{O`3`Na)zJkGZKYZ#>UBJ5*p}JnaoD!OGlGkykkRJqxQg>NEu+N_S&PN+<9`hr#y50d_m}? zMOgz8=BdnMHEt30r!94}=T+%HcwzoS0O<@mefU+Qu*K)!t@Gkb<2XKL^YdK?q6!>z zk$5Al4RCg+x;sNry2@RNKnoGAl@s5!%xBCe+N%bSY$!cx!fY@T*=y>&C`-B@vxbALE&Vt^ynsRfw*ceRX-Wz z6B=lJsGNKzC@K>(wToD?IA@UsgIks9^TITt%@;XFZ7!cs)rQTw)!0LnGV30GlcMj8 za90%UX*$FsdEh5|E6@B6%~t<@ycTmO|0>C;d?aA2y!94b9U2V}QtK)@_QECdQ8+hD z{9x^WL`@GQX5siG3WKuQa&IG6BOEksn415GFSBu)i0S=Y&Yus@@Ns%Ve8~ytHNI(1cK{ z9MJyzpSVJ1={5^es#AtDQwuwH?4y;>ytv&$k{T}1whitmoGGCf=N9F2JTB-8#S#{- zAYfiec*aNjAhuO_A$Tc%OLK0T7`MaLCLEnf|#Od zD^8~On`ou9maQ6S+Y65|?YVUMgF3s#ToEg|^)Iy6j;<-u#k?(F)TJFBvcqKwbcwd(SPVY7$zu%sRau_csZV!Ctn(VBR`O>9(e=+ zMisO>z#W*x>#qW`t);DPB9481U&y2zE--;RD}zksJ_cwp|NKcu1&N!LUN1@&`)(9k zukBUHq__$y0jFj=s583|;Oc(`gZbJh175AE}sbpHp~e*1c6yeaFT=eN&FOOSRt zR&ufPIvjdJj}^dJlR2v3)(ds6w$V1^VY?Q?9#^qY=Onx!Bt9Gh`&FV=j^0G1rbqFZF?Rj%B*e0LIOOWdLI`zS$bixHPK!7 zSI;+sp>;0efJ%c0;l-u)oNQV5y`iL_{L>`uL4spICXm6=kG*AaCW_)SI}=nFs2s-Y zIK~E!a$tTKT&5*0BiR^}c}zr3G>f{K#epgaF|}HVzb{s$%T$PyOla?%-o90xh!T0Z zH8i=mL=?DMG}Fa^2%(^qX_O*G3LmqI|J@!tDKqmlzFF=&l}38r_0c#?D)-JMzFkLe zwI(24?(L98g}x=XC_^t7~1E6 zS_VYgI~4MTv1Z40KUx4m{2J1^;fnVYG6Z@hZ#rvp-Z&rzpBp5s6Y}sLCM7iq{l#S~ zbuI|oD>bC}?%~|@RU`vf!SV#^Q5Exq@}-zn$Dk1I<}fimaj5AmTCUhxeZ`FVM~_qH zS#)8LH;{v?sn8#hw}M>zSu3mC%Jkxn%b(>$&#;aJY53)vaPADDtvWsrmV)ZljdV;U znVwy~a~k$)4i$ln@a$FFWv+D>bmvdEznReqIOim!kn0j z0KN*BC49)%$=$}CesNgmSSUjM>o*;YV+!Sz;QWa+HnsX9aX*pGpk$Z?lc(<#S)qO2 zk*HaD^&U9=3W&c)zID{t@yn~5_Iu9m2Q5zfh|+%m@k-1}5t|BML!_1e>H0xlLyMb-8uIosTOHqID5T?z93n zfW`?v1_bE6zV_th#|K(U8e)c;RE9M{h}kOu)-dPiqlVr?&{!Akla2F9sXl7&y9#mU z7yCsjE|E$V;CfZ5Q@Jaj3n(dm>&+3#yg=zZhbt31pT~P+M)xE|&Ql&CFg)4;kp-Bk z$w4I)l+CN_%k8sOBLhYCsCm{2cp*LHvdawv&k1UC6_v${I7r;Psy|kS_9*I`Yi|4f zC*od1DxR!RvMe%8D&2;4wAXiP@*12q`UdVABOBP^Q1z(HNX7K(MPVw<$G&*6b5U-i z@{On2{2ARH9LjnsvpBXH96q)H=e!-t=p9$H0Nj+B4b3i0Yib=~`7aLt0O4&s9LRaN zx?HX|{PCTLtC5S|v?aio4*uaYtXV3oCV2BW)1jjK085U1usCVdb!hutd4OAY>h+j- zZsTd3FBtN9Loj{iRZ|<}s^gvs4qojXbfr`K`}y#p2oE9BxIw{>S{BvK1a11MT%&Fm zvY(k&%hFeqMyo+yrr<#qe+gt~n#@~-`o^<6Yc`cuRUN4r&JCd*%Z+C6mbV+7Tc=r% zTm&^KD4OkMB2MD+3*S;kKd;=;x;&h#Lflm;QgC__?GDUAx-<8qkYC@$g(=l^oRsp7 zxU-Mj*!Dy!IoPz%B4@l^vOmCKG3X)1u*>Yu+HE^`td>v25Bb_mw-mg3MFJPCbtsdw z1&a;o?pb?;y$4mR_a%mGr;-_lxo>=;hej%RPF*F6m6Mq2r-FRv$+#Nirv=gbfGV2y zaBy)vVbWY+m9~6EeBf^4I{S?30lAdljMUMqd|C;dl0Q7qqC+iaw!<>Q)B(iJjvj{* zlm2Y2Yt8|!AAwAd*YCPQLZfTI_QwdSHWY+ftL)DAiA7FR(XUN0A-a|xl{WG%{k~TQ zK43wfjC?)nKii5GFyVC($w5@Ho`8J)KoBk;s{b++5L<%qQa)Sap5iRw@6x8c>P@^^ zrDK&isjs zsMGQ5Tec8PHYUc8(zwKm_^{fm>Sj{(T`TGNYR3nwjh;NzeMORl_F1KbW_Htvyk?Vp z)a|0|EQ78YegJoXs1$g#VC3-+8kN54wrN*}9$SDCaXG*= zI{-^D_{q9=q}q5ry2BIgV8E@OrhKy*Be;H!-SUtng!2p^OJ(&3apg$kT*PCemnlVY z|3pLz^8jH6^r+7%m3I57DKTQ4V@fKH({pbY^F1w18g<8nQMZ6_4IHZ0p&77rX(2Ij zs3)ez)_HY1b0RYl&tPZDxzkOGA9uXuu9*{w*TPT8eYu&09%1lIta98S73U4CTI2on zhGOScv~2Tx)59{mG=q8*-cHz9TI#3LcuOu$!_RISsHVArna|of3((O%UACQN>hqCr zXe_11z{AttSc$wcT##e6(?e})48%u$i0W!vi@~<+2MI?9kFDE|ivuhc!OS7!S4jN@ z|53PzNQZBE4TQN0VBnC8mE5o(7iwYNulUMuTQYq~_E0@YO6Zy=-FVZIwlm)*^m>`N zD)`wNF5uG~PnQXH-3|O;Jv7M`aaxNqV9Rdb;LPXKeN)VuRZjins;G8;T_}2Y(-jgN zSZ$XAlr46z*pXZ5yXEmo-S!t$A8AJk@lu~u<}~L> z`s(Dit$BsSH2c@L87lqE{?VU=NE4HSDTXPJemZ^2HWuNznngRxCd&5Pq@kD(xc+kS zfaB-y1KyL5+sBL_DI5j-Z$Up0H<^dLqcuEW;A&+`xTQq=FRA^N5IZ1=l#u>EfN9*P z!~jCxmAzjbJtEN%Ip`LQ^B=&c8chUO;g2IrshADxdem$0#<(3wF*&vE_^AC;)}FlR zF~+uMyS~-j_SBKC%6-=S%g-Kt&1rqqNTz?8Js6!K`z0-KZtuA07galSmtptA{l{G@ z&t0vdew!Yr>L^A+Ztq5NJG7CHy#)5h)YrZ^$FAIN+67J7F4(iv?-;=083%$mXnM^n zQQXY7T_)?i_XK|h8G**;{|5lU4tUfwjMmnT5wSxDFQ)^%f6r)_tp2S5TDcoPIG?gr z3Z7a8+kR8)cj`g+5Q{Zr{H9Si&N2H3Aoh$KA3alH#FQ<6^!`1qbEoVT)?wYmv=Mh= z{bUG|#g2-+}W&yXBdKJ6+5Tu*swXVni zS!_S^mTv`+(KTmeL5v~O2f2nPmILR|mVue1{Wq9wzg}}_r~vRW@m}u%H0n*@Ne*Ezt5Zv^EB4u-tbkE(oWpoxDOdVSGz8`+l zusX$QBm_mx>`qZGU>4OA?j(eaqy(|1XA70G4=GcRM$b1HtRNNm#vJA9RH^MeR1QPg zHuJr|^Ag}XZnH&s#|2vf8NxHB4yk{+m6934*`pCUEn@87pD{G4oEq}jjo8rLX4J=G zlk+HNR^YS4{!Xu>)F?HabftV>U*0?5<2p#u@EGjt6^7FE^MB{E%$Q$}I}W9EoeKUt z#1m?!vQ1)P^xRX|{D1rHK*w6+tsF8AzR@Q@WZmWS2S{yS(_{8j>w}BUmtO<8u^@pP z@d^K0im`1IS}6nrbaUhoMXz6fDvamEK&*+HgF>WJ&Pk^!t?szd^xSptj$t=N1Pg2p z$GEGIze2E1VR2Yq)dn5C3;PdNaOLfaYzZaxEV=W3-JrzVYA{%uV{1eErB0P*6<)3a zvBorTd9|Ie%Jd}ZZEto{W|qvmcLcnn&JlV@%@iAiD|1!Y@EIu-4=J`J-okc1{#-(v zhWXm3Ktx+kbS!p^jP08r9z<{u%`mZF)rNZHpq8lc`_i4Z08tMim}U-CSxFlJAPzsJ zZd%cNwZ)Axmik<4fJ4YrkIx?c)8+>e*pvaNw`}@I58+ z*)$0!M3tLxBI)Ni9T@8!_Na@U*xh$Mg(i86?d_n$4=-z#pxjxlik@HQxS_3DD3uHm z?Oe`tz1+NSi=@@yI>~O=HD7}0p?i#+Ho)J?e9{XNnRO!ley{4zYyv*}vf+D*4yv5p z1s(`O^1OIS$3)5hT;20fqC9(Sr|yD0^OA47E+jgvdwS`E$Th5NaE<~lSvht}HzM{H zkM>!ldAAYz@5!jYhOrZM=2lKfSWY?%w<6X%C)@)3=`CC1Wt^RtA7*#_v}+iZa+-kA zvqXfR2Q0Id;`bV0{)>hza0MQFtLNAMM$-!n1_BwMVTF$$^BHbr{&0N-=^K5q>d%q? z4`BEkdi}LPb0RBEcCT*L!>eoaE9fzzFqtL!#&^G7WI0v)V|FGwOs}0oB>%%~EL!Y5 z+9;;$KK7M`w2CzeT<8^9!oklU560gufg#D36+*&WipZitnO6#gCvq27&fJ&kl+`k9oM|V7>=0!fj@47rWb0Myo0O7q~I*r6+UFA|$3se0~>K2pMa` zU43-m6%4e3Y}ta8Qw=m9*|v?iAQ8^&jLm$Q^l)>^+yKT^1oEiwWGl7_I=~Nh;sDy` zj{%F`zB7O7Vl6K^u_MWZ{;o0DJdZVD)3qZpFx82xEn{&R~;B}f(Gv@Ck!RF2P1 ztI%Gk`nw%@%e@mmD~^RsTl~1t0GX4n$!EV!K1qGsr5d+)e9PjB5i$~Met$|a8E@t) zvA`nOl2v1Tm?1kLS|?5s-C*gsYku)ta*)=1E~M%9ebDHs1d&$37sdP^Jbc{jF?Ho* zw{G;N$iO;2bl5NzcE6X#8Ot{0SUe6wn2T4_oF5zQ_nO7l$ui(sc?gR?i~GX0QPv6H z(j~dTHTCoRbgF?BZe#%~JuQ`s(&gl56nwDO>a}30cI3pd+6wvh; z2Xe6vjg)Jnh-r6S6tTG!7}@Y=DWu0`RmZcyQexCDXe3$=2T~QDaILRdG`;%w2Z+9v5mPn(QdStiea%pH~b=YAiGSMpgjay7>_S1dK z&9g@)(^pz7@37v{e{>t79b`>&QUsm(oa>}wBtUj^pTV}}n;a_&5Y906>?ria^Lqd_ z=vf+!wa0Cj3u!pWxc<;-mlVf5#v+BOu8G&(jW8Bip?3+cB$aWh=s#Im*fj5e1Re2` z?dIPP8joPh%#Myzh;R(U9v0#k)c1i>iiIPU%H#S%d9qsz$w|3SliYGq@s@{t45?}5 zBUAT(s0qKa2(Vfy5aa0~`smv#ZgRE~F;vyhON<%L?yA>Q<7iQt8M2X~EK!_@d(4!z z^$t5z9_qv;7lL*}=5WMz{_~GA!2z!=M}FzcH==b?7k@K~>N=M~K(GKFReLqCW1S(| zFlXCtgn$`jcas)vKd8KC<4?o;JlF8UpjHV%|2&LZjJG0h2#SsUgo=my+JjgKpdy3^ zv)!9Ej<`4)QK>n~X#;Mp-4%JUB)@i!Y_n>NAnlbIm3S_bUi0UKKQh~)EmTaeH5$UU zabG?18Iua1!7a>xs+dlNj*H#fam;v5fQ~%ih?lm{zN{TRaMFM9%+AfQGF}pFSMATJ zOZ~EuOavCY052VXl8u_;bGZ3@qQk!P_(2Tlc!g4>$~@YRG{nFfy-en))f3ssu~c+LXe+0Ex(cjHMlWkyFU}+O zEWsb$j#}xvEB0AB`Nos6y{6e4Z(aBehfREHG32Thxc4O_)nMVTIG%br4K!`snEWK2 z_H!5q?t-f`J{SD87(tA-%(-(L(|L(#r1^sT{{ebM@%FGTlCjvv&X}S_I$p-1(#0o) zX6pLTBx^e#tQ(guT*MZ)jo5?5mklxW69u`P!I2A!f;3HEHY52z67UO17ef>V?mkO= z+8i^>8$>cSHgFteJ=z&Qb{6&DQDU?)u$jnHZQl>}M zOc?jtA8sbQ@=B|7#N4h}G;QYBT0~cMW~0Yf5ApfmM%{c?L_ZHB^qn_5>0Dh&SQLPN z5dp@*mFZH?r^sdXlUxNNIGXN?bf0o1z0FhAuI+-!1|RjWtN!b!Gs>ntRqlh{9E!e4 zPeglJw{kQDN^tNy`Ae<8sKl`GJ8D9TT|r*N;fGpM4Mj(E9;C8EV~LLYEUXeCTY-d{ zsi)_B*%WlL#8Y|Au%%G$R?B0l$AT~NWFv6V&BMLpSfCBj?UB2j|9j44VJ(z+wV#l> zD<`VC(*U2ZdC*w-3HGNYM5He`!Af&XnSM?T_TO|o0yHtLhW`uh@myRCH$@cyS@cpkuj^Ey~ z^pwAHime*{*A&FzJ2DQ{8y6G`F}YmkeOaO|e0Hl?*5-u_-Ddl(IyB(XW3qIZ z)%Xvf|3zrfO#qd~fhTTI>@=QY#-hDsf_y3WR5lZF_u}XTJ(Z4bv2;=&Y<}hVhp@8x z>r+=}#M!5!hD+cZ<0nGG>*A{-14o3yx#(HMQ#`i3de%!5(3>%x3oTHG|k`aCF z!|H#4I8N%TM(&bZDp?%&sx8ft_P7}v3EoKMTSCH)h^zM3D8X~k+X6CChhm{^DQBUl zB44L!;$utoY8jrr~zBNuUFD z%veUJ9f%evcn zs(h{I(Y+gQ^LJeibaT_6bdh>SQ6BzbWz{%|MRTp2-qDg@KorQNs1b0CfK=2B zgDa*2%zzi+o;q7)<6z1JBcBAh6N%K_w}t^_Vz?Wm;_t*2G>fs&z6*(_F9o8l2k0iW{nTN9E&gDOLHw zfnKM>nzT}53oF@EOaC7LtOiM`{{e~KtJ*!gAQ6Lc0-q@2_JOY1ISqwSM&|&S#0ed% z%2c_^qW;_EBnVkA?awLwI`$6~K?}JeuUCv|zg;W8US_RcD!h8=HFoJFKni#@R1Ee~ zsV08sOSf;9&zlN=6}=39Kkf2U^-IM`lt4k96lrt3GxE`+VCk^`UI*g5RhJSF%s}~^%e! z%Q*&s`!)On=p6)8lLn`>iz5j46=oRL@HJ%tB>K)?VtI_p9t&kXv*z|z8a$}Vy8BFp zKk0pNAF^*1e=f-GfW80~9@z8zj+ zrLiwLoA}OHji&5^>e*TrUg@F`YFUA};U2L; zYTZSD;(JAKK1T%?LpCRL$6PUFFyW%8U=b z(Y4HT0@OE$A?4x?z=%5rruIP#62;(!jj@35;<3G@A-NU5nFYtpzuL+-7yv2aJP2do^vb{<9Za*;1gCzXz_Hh+<5ZnPy-MKz zBP{i5;D*hSyRMIx;Hvef{xS|j&b&K~4SXM`!nM`{pI;`u349+HaAG#(>U`mNjWSHJ zo&z1HjbF$r**VTkzff*4=?QrcPTi?tft7OMQ5#IU4erLzD{9vbWi##uhh5J!2i1Y- z?yJAA`&(0gs0~X;-`bR!8LdP=+Elbyx#M@z>-;}mUi{H>#YwEP$PFjO3=7BytSU?L zM+Z;M&eiwdgF7FpDZth0cr}GQvyu(st7SO6Tzgx@(=x3g?SX^b9V#&YN{TGWFOC99<2&bo2^E2qg?}?dUQ9c*`2=Sa z^{$Vq%Z5?N>LLJ6iYoMp)1vnzf2uS}#!BM9tP`+LTX-3gdSTaGWLwRH;m4K*V-qay z_FVF<{94&Oe-K6WjV4bK+y@Sh zChs6xO0(|am+|aU4FH!)Je+raZ#ca9ir*FZ@ngF)T9A$K2Wkla0S$HK4x73Ef>zJf z*QVWVMyIA`Ge)8@9sCpjC$1FVq8EhRy+89)2YdI$>BUc_De&qYF-t3_=tfhAAMPeg zpE050W#lJ!DUD#XVed5VCi+z=u4wfYym~Qj0h1qh{?=3iWi)J?VB!5gx6S=qTHsP2 z4E~N?y?R;4`yI-xx5D%Npg^ORKQ`(~t)XCoO-n&vZ*;pNTss$P*$fr6ztx}rCVH3W zRUb&lBgy(<5L^I~8ptx-flga5mxd-7$y<|9`RN!4@hg9JmEs!39!JW7M+JJ!+-=lx z73J?#8mYxBn~P1NoUHxC^grXahd1RggpEITa^KE~)*4^o1SMy*H1%Vj6Xrw{V5`e? zDSx(5QA($wQuF(&ua60rq>v3KM>i~saj$4_blV_=QhV zpAq#}X7{Pk2p5A5Y)@xDHMcfia&X%HeP+=w0;0&qtOCpEke2y0iY!D{d=z$xRzJT>;)}v{fL+B^O zyI;Xp9`(ku9XdZ!>wl!y+)gi2Es|0Wz#M+`4srZ$A^kXrGsgI+rrQw1)S%)=#H(1{ z!5njyrr1BV8~s(h88yHI=M7$aNnjDQgTz2DBc&%29xn#W4Z-fM3EwP2G|lpbI51hnG>nHn-?)Thlf_ALZ}$uefT(zxCJ`G9Hdh}H zR41S?nqj@ogrs;*zeILq#iV6bq$i`}dzR7g6`p~V` zBMiDfw++jeI;aO!)l_>mgP*OKjP-KC41gwhfRBW@Cf*%>sm#x5A-Dby@-PHayz2vH zQ=IiRZP1k;T1J#^lD#NYoMm#i}~?@gj2US>-fA;VKpK809s;AY!Yz| z#yR3y++)D-n*en&G%b(B&wje58(}Yvhb!B8^t*x0UbeKVXwED$1Ac4s6Q_vPigvd9 zHnaDb`P2^yiROjNX0{zsQC!aM5-3bv-7ANd%>e1B;}^EHnIGTUh zi;=#H%#nu{owkYh11J>hj`eG!Ke49>?hWh>SXPh_#nSTiM3LB4rH- z9C{>tPDhqvSUk9fP8b~BY#B9S`(e8({ny3$uC}!IWI>zm8;`RyMG~)u)+;E{GTGs0DB%}!AitxazE{$w&I^A;N8S48{7&EXWqNX3zqdZ*ZJY5iVwEg=2q*yRJYqd+ zN_l%zom~OnV(QQMsPT)x&E=eO5Cb^hDUFS2ts4yzK)2%~78-1IIV4&lWie&nI6q|O z`BSV6d${J^qr7Q8;s52SR2m=F42G2@#C#J-*je*75gxMN;7D-WcKoeLY%a^sy@Ns3&70I^!`i_7CLOu1**>BQU;^Z@2TSp1V~*``1nVBChk)u+aDNxzsI|8d?HJ zh16|+*JkFk9!TPi=KJnNoNnwxst-56QoK*SMkU8_6#}s6%01-*6oGpB&_fKKi&xjR z+g!*GW2P%8DN^L({UD2b^tk&V59xT6bS6F{WFMPq&(*-Vsg=v%rPneZ@7gs)6snvd zM@f{LkerUXv>$&olrvECur{CxNzf7~&!z+yBSIgba2Badh&;J{SJ*J%oG^Lx5|hZ$ z`OrV+g0Jb!Db&d2M&stGm;bhvHFGO|OUUwfXT{F7pdZy?`vUi=Oh6{~7Q>hY{a6`; zyyzc|S#zJ~6K{G0o*D(%|8iNawR^vz4iJA#n|u@g^cj48*s~Rk@S5>8(0@Oz`46z9 z4rJoqC-oZ-#r)Pzg$dMbIQ(t3_z!?xFmcqtghX(+zXc`gs?`A1OKfxkTQJp<=IhGw zl!v+-rIHS{FV*$9j)=pwsM=;?n<^%`>*o|R%a?R4Vb$)}EOem}ZqL#Gi>Iz`OV=>yT!ppk?pm6IiQ0(`iPd*smOmkbz zTMsgC->i4n2P(v~m;NeM8_55RC|@KKr74)I^ak!pW<;;!{7{M#aQQLb#4v6=Oq>1X z9{`69kEIyG4dLVQ+Lvn6o3_I5(h*5EbWzMn)R=(eM^ot_J+~ZQ7DIDvrTd)SEf60B zbDhcHPL7N?oUhK4LQP`^tIiIy0FHVELv0SrpUHCYEx(Q3$5&}AT{@wu8csJ!3f{QW z68;0s^RDj1R^01=3c!Px5oF3YDm@1WaJj>=je#N=~?q`~gwZ@7Gzo>wNpu zgmg0uJ!pVqVpQyb@C4m$q*WW;xb?br_`LoBTx--0XvS+?+QLdXT|^+QBG*#jcO2w4 zugA)G6pVu9wA5d9_cZeV%<@WAZ?-TLD66)lA8{azq4x}u&vV;PzQDM*m41+(bmw3c zY97;bLTc^CYySgOhd)RB{>TO7UNb_G49lH`{@&9e&&w2>5oc+$VhCBXrklJwlz+Qv z-SdT2ldtQni81S_&L5h-5Kk=g1SN2*n+A1L;?bvxx@^>~SkH^_{>)38vl;p+^Tb7c zufBQm4*(0jKX~<6IZAgSy@Z=;Q%!Cp^A7;9d%pP^F?aS5dRXvWAAX57m%e_s`u_V= z{Np#B9Ln-Ot<-;jIcs_DX$%KzPFzPGi+Yq$7;E#)m@FMJ-W0tX^6)~g zgE^wMmsR%w*7IJ$x>?R>( zC)Jh6=PPd+WeCWt_4+fV4LgO~a2+K>EO_um1@1hJ^#so>%x zIZ*AAA~g0N;1r(m571kVFJOKP{kv!@iSGEW2(sw_@6cB#{o(Sd+Q?vO1qQ-qS3{-Z z#P!PuD{|bO!5pv9^<@LY_CxIq;c?%svT*!3$C8i6R zCB!-BaXR^_e7S)e(Sf;taQPTeu;1c3Vg`Hzo~(e41!2!97>cPITzRK9jjXHAsKT7N z-*&!@F>}iw%mQKm{9BpW)66^x<>;x(zI}V)%KhaX?j&orw=h|%eiCcXk3|mF%&~+P zlJb<*x6EFsJkdX*Ad>0P!fAdQrn&Wl$x~6H32IOFU`#TbV9zp^Ly|Tyv}Aaja)Gpb zfA0c^d-0+RiEWZQ>bR6le2@0X$oyvPS}RmHH0>rbF4fcGz}tikq?Lyv6waD1^|Ylt zNPW`ZP?C7_56~P~FdM@aic-q_+#Hn_<2*yEZ7#XaP|3e?xVLuKkIxZi=FjyHAbUCc z?9r^ph$O9YTP%*`-Ev=|@QLNQFpFoR31GXJczO1ug{bHj7a*{5?eUkH|yH|eH!h#9KM{am&_jcuZWSl?V+XWAdx!DX6A^-h5tyy6I4Kp(uV!eHHrs#HSNjdBm)XQkPY&N{ zT{}*WkXt%mhcLyNwex?SQM1tzy^3RB&XZQf-l2~5!p}v0JUu+PV3cCKvHnY?FZj6H zTZtX|571ecDh13PGs|7LCr9HXQ(eQpg+#ojm_NW$@p%<_S&9s8F@2&=Y}j8)+&Mzy zQm+y(3JaTh83Sj`ho*vvkTOkcV~D0II7&Vvlmaf(tfSz6B+X$vZn9D02)`-Fo6TT_ zke;H=$~pbG8J1Zs>*nL)0F;eb}%fo@_YHtgy?b%^8YlF}6h|Wt$*c%YSxu zHQ@{vw-Q2)dbt5{+Jfw@iJ?w$-+!*x!rDv^R<=!Gb{AU8ZBY%thezg~xl#4!_Sr2Y zdyL?H1OnPys@+*ZuM`U*-W*w(Rb=rb@O7KUWDPIaAGTgka=k8e{H4z zx-oeH0;7HGE51@e1d8oZDE}=eM#c^M_nBF?$=`_)l=#vky^%7HqrCU60ukbJKXip zQZk0g-$nwZ4_0fO`n=ta+uKuq7kqhB%i2)b)q~WY5(lgB0xVdLg;K%myyYJtNBH?4 z;Ki-ZL2#BgxZDX*W+JKH5b+u>G}ql?2W|E<2gg&wC3nxW=hq?~DOE@(=d&ZVZB`7ud0 zEx%{~T&g!xK3&g!t(BcgiPk3bAArtc9j*9-zT$2~K;%zZKHkQ(n0@87{)r}uyvNyx z(48U)MDA+p>cG_?-#fj66CM^qxdqDa$J?%mN>rt5TM)tOUhHl2WZd@MU~>;!b4d3{ z3vq77_|(X{)D~UC*_*%q{z~i5j3-D5J6PBGsT!W2+uzUfhsnuYUiv!*?VV0H1mBI+ssUzO40qu02tk# zH7Z1%T@1=|x)hYHR(Emnoyn(>s}^1k?H4UfW{0%~9lclT?o~Ko-fBfe<099q5~Bb5 z|Gdn9i|9lWe1$NAyw?V3gXTSig%9-weJZfbZku=9n=|sx=*`JBLO%NmrJ-7MmV{gY;v1>@t8ldGpQ8seqxiy2<-g4MC9yk`cjsPxexbOG>7t8n zGK_Pqqc4`BZjrK@r?;Je;Dyk=s$o(t4MWn+%xKj+TRPrWUwZTTYU1i03@Vq1->vF62Ar&Rc8$!Dz| ze|QH8rsqp#VI=(jI56mN@BilfpIJlJ?B$&+J(ebj;#=|Jb2uGqlnEJl|FzQ#d#Lx9epsF9#@4N3Z~k_xzwz;MdgY-{-D z(d%Iot|TCZm}7y+WgHL~I)!#icfowkh|nK7Z;)CiCjV1dW;!?amrPsj2yOJEejfeM z7gTOu*WG>D+bnEgQf!fGLgwL8+`es+3yA}yn*9^kMR=ipd;hwKJ`!zMFGT!VfP@pH zI*RZxo+gL^ax_QjHT&i#QmJlz=D zm*%F#Y_gx()*z;R3#uwq_t1z$4mU%tYf{}=u{$Y|(rnhuIf-IfbGxmmfP8cd6R|9z zPVxcYU5yx4ruy0*G-5)la<4mB$f*TR(|H;&2z-yiIe0V@sv@>}xJWT2W~TgTJ!6N z0p~4UA~%9%YJxHYc+~iMm7_S0IqJ_?gHjS-)TNz>-a9M8G@JFXT}N7JNh$Wtq#o=| zGd@m2OH_(4zT_t&#JSIC#)UVsSdu7$%*nvk1FS4zY*sXGv$pe(>2FvQX0_I+r7J$< z6t{d2qna2hiyw_x2VPFI?@aTFvnzK%IjPFomtO3JwzJuf;lmT%`t5w~+2}|{HTW6r(&J5|9)~=i2Q?j4dhyl3y|}oyqsJ4z-b*>Z6+2^ zW8TvyfRAP*+S2pk8aWNx^dYRs+y;K;O6T-DqY8bqHRVN+rN5_j`ZTOUqCp-29yEK2 zHgw}^kW`o(mxv!71}I~on{Bi85-@EJ2_>h`c!S)v=rftMDBd+b(CYy7?TFYDlt7 zOwEV}F=Otv;YP2a2k^~FuV1X9CHTB~xkFYG=-Gwz|2Fehj1-4N%Aw|~&5j?Fk^Fhx zr~0Jk4$q3XT-T-|4X08hoj}k{6M`bw?i1Xh&6N1VU)yTe1{zj&HSNl{XZI0xGV=J| zwigKZTo)xRL-Bwq(YwKIKwGtYIpu(5~nO%WVY z(%^gNsibM&UiR^7Y>~?l>Z~&#fkY#>9X6kba#AejVY8PvCDM zyqCZ9i~YTwewpFB!hNZQ&G%d<=4$SBCY1(Abi^1Hv5kS#-+Pl!}Pm$DDm4zKmaeW+~TpG#`sdfv+aPF?Z{5afq(1qEdvxtCQf@R(><3=haH zf@3<0{60g?st%8pXRzeu_bcY!7OS|>73kB69dzeyRl3OR#UDUEeOg@LJSfziRfL13 zdfrs+HMm3Ps`Yo*+)@Fmip|GVtN5MuohUu6dV>xxc=z|-KA1k(BFmXG75l}56?Tq3 zBMp*NOEX5wO{18%I#_oDp#%Q_*@bO9%`YOs2MbBjUUa`A^sbsC`160;Oq^`r ze;|&?M327nOA;RA)p?~=H9y^klbrNh)2IeNE`HHliY-SGT3UJZj`J%2-CN`gbInN@ zb$-s8Ev?A@!d}~porb^WkJH8`zt@F3-mQe+{C8{8Z zmdQ}A_;JI+*V=2ke&RBzcHvLTum%gy(HABBFb-ke&m9G_DOo02f2jYEi|e}~O|!ZY zeX}mFF25@_WVIIpckQvF#S6=%K9Vnj!6jV{ssMn5f+8S>mt~R7=WY3BtO@EEwSlFs zAot!~Fh6RGx-H5LY*gb@;g8eur)F+9Z=;}f>1{Nz@9g}IJlgy*>GCOFQkAKQDAl-Q zFD=Lz`-t%m|F|(Ik>emDyL*QisxhtmALix2Umlnv;K3K%OjP&?r~G}+!RQrn%p&_s z>O^$V0VOlJgma#~DZEf?$V98$Yc;2mgXB&13vT#Fn70^3^1SQUs>w?|(j~o!Z!JPt z9)Fy~M8;mb!nWr%9+gII3IW zA3(K&LY1TFYw=hgMgR{?u+g|&xWdtSVBb!+Y3wT4tO~u3NBK4F#{OyQdk4_) zJ0s~dC1s8b?6!rM`n}5t)*P;<7+EJiTdQ@^hag%m%94r^mjaR;i+#)?KD&&` z{iuXBNF2AcyYclyz(+RE%1Iy{ST^66yly`7{okTs3^n0)j;=*-Hh7y}ODn4#!Afu4 zCrs5rLM5$rRDbOCLQ$A5eLL0tcUw1=+{GvFPNL=#+vkhtTelyqoXwi@Bq43D`HpD> z{{h?-RB6^p|6bkj!2aW0BW^MrjrNqX7Z2MP-e61l#jd>lQ5ePjuUB5b4F3)*D~(4F z7VDc&*VEAcc1X6I5(Ma*hp1JlCuvmSp|RjQa5Sy9HjCHP35x|!%_O~BMKy3gnS;v9 z$C{gx0k0huHbG@PRx1nDgNUQ@uY6*x0cxdJA83@%TBn19_@cPd@1uDl z^h0z0M5VTsFE^`wJTgjsoQA5>H3(ck_GsF`4x#DBaIT#MUSz(Lloo%McvYJdJb zQ?XHzXS(CzU`)>JRzvD%q3zYdii5UNH(wKg6#Q>PGSj5%(=W1}&AHnV)u()dKM_pd ztY;6PxBky#wNn0ZYw-p|!CgEUUH(-ZR)?=kXD*~*%8STrE>WsxSCQ>vf3_aMAgUNW zaB+T2vSTV0rdtG3gB4Yx>LvFIybx^7M+Q#mETtW*-^w7ZEVDGi8-z>Msg6}o+RDlC zaoK#Z)3>pkJw8d4f-@%f`^4W$Y&i%3p7(P*q+B@1QAlz5hII-jE}Hr&t`$ zKS?S053*Dq~(Go>47*b&pDfY+t@JaPaYkY!Qid+&qV+;Uf|5Qh=weE@9W3(G zI~>_64wu!SFBRgAbJtKu|Kvt9uCr>zJ2A_=CH_pHA6gEXur0!_;VE}vts-S zMsVNznf%2dEAL}*%s^UGS!vrEA>MPCJWIQxN|J3rdD3;>w+oZ?G7yw~R-X8HUh&~A zbDiR><(~kI7^vf49)+g@7S=Uct#0qMWJz9tZ@)zud2) zIB#*uM&3|5pBNhN-jg7Foh;2IFM*Xy;`VU_+@*$2Dat-bR$r(_;@KmPe$DtYP?iN4 z%HcQ?Q1KNBSY%o6689tC&K1t*H&aCw-VEx3?BV7AQpvp`#YxX!XbfQel{r-(*&xP4NVE~`ZWE0#( z&10-+lRj8Yqq~>!Xx#0<5yam5aYhcWle1;_8OxmPc_DElu1hcoqvV$z(DvSn4(q8J zNnN#)JHYz!kNSf(Pup#&bbleXoVydeXVhRvB1a4nk{*%6$PNb#Xk(Pw(s^ykyKDPq z{UNoN$%$|bnlo=4#S>ef1+bExrgCZrAfLYFU=%v#eq$go;>CZ>eL4|h@1dJ7cvy6h zaaND!owhd@k!o>CT7X8tLD%zwpgxV@GfT8YrEG!HptaKd-+cCBUhW4Cj&4H;_tt@E zACcrD&@bKS_f$`T?#1Q-l+F5F4HdP(@DFghT5fsrhQhY(sqKRaJN)zAF} z!)a&3KH{opET(aYvX3PzW|4bxy@Nmy)pvd-QI#mWhhoPN&zUrB!S=(i zmssBJqM)?8V|_p@M)eQlM>cCz=SL2aLHaKWt37*e2(OGH8#vg(P8QM>z zyywcl?e4_m9H2jlE~FTXCiX9XateORhZ(ghuKwq+8w`?z~i*&?%% zvosBhQ&qBUsY{eawp&{M(xD5le2+sr1+fQ$O-UJ&oyP-xAeYgB#(3)svB0ze3(0 zB6B%_ednwNEs*X^AMwvIB|kPwA3||qgYMw|tTmhZ?=o5XhGi@#CL3IWFO>rZZBVn5 zT;ulM!~SeuYXQi}`tgo2P~hna(OgVZmC-8Yxzf<6!RCIK9`?;O9>@C)my^Ux6Ec@} zj7r!a9l$@pb~!Ee<6Lkv$t=2l2OAD!XKoe7f0aS*EPvE*tS*%x64N}1J62YCl{SM&*Q zHQ95Il3rg%ncS$2IfCLpWq8}qyvmfOZ+Lkh8*{-#>E?dIK@{*u%iLaxS)}&W)=~b~ z-9VXKwFacec`<=Q&;aw>xo(FNUME^g%FZ7l(W{RoHu-8(?DNtcD9XS2`-Y0sSBqu#O%f0ec7FGEVoNv5*Cx?N1%j$$EivY*FPkKyzJ1wN22jFzHx2}-Jr z6xIrVqmhl*I~;~PUZ?X`ilTJ#`&3z(PrlZsU>yW*lDJxyKkc0a{BclZ;-$<#z+@D# zR(_8yDMwQ;#zSvR`neJEY^UzO81oO1hf8)T!%)Lg%>}0i91+cCxBUYMm`iF6Jcc_N zGW|M!<*N-stYe$)){dLqwpP3*`u1WESLI1{Y8ja3?Oa9wOequN|IxvLj)Qr?O%nZ; z?#OcW2uMFopE9W|;0V9t-FqqUg_pYWEeoyfH6?TH+@9epchYNV~7`rH79qKle^I55;&}(=}zypTVsBdCGMM>M{;8Z9thR~YYg2(+PJ?>+_p1^ z`Ba|{PP#(tsdc~1JDoOeP z`a|_`@dHc8)}Y9bnrX4H52j^g-Ha=pA#4om#hu(wH#ROU9_&gqhmLg=@$o!!O(*@e zRUlOolLc51L;x-}fFT_GLROcUP)X8Ifo}3P?bvicKf8kOTlIeK^Y`55#l-%Ch8!05 zkVZOzyNV6zK-y@3Gh3};p<`__vna9RkP2P*JAAHZs zJ1Kf_sUhpv>c!`Jff5Z}e;W4;O410Idlx>{L}s4fT?qa-NVwvn7hb4hmkm%Urb593 z+2GQe)D%>qN=_SO0{=k^RElzK9XyMi6vh;O&&CW^EPJn&Rz50tCre^_tb;J1b@G%(yr=$Pboz`lvow124? zTKM&tz!6IG?MnMrLrC6W|BJi$QbZ;VGP9zlgt08>8^^mDmG7kG2#cbA;&$Mb=nqwo z0MuXW$BvJmg%{0Do>=`uZjZS4v&zl|F1Yurt);*;m_f|}B!W#c|5b7vmD>I6H7*7O-pc=^7`F)=s3MSG z)q;_tJB=!MZ~~*Ay;yPO<2x{-wpMAiX4pB$RWri-q59MZq^(~oow ztyM0%S}^7))Y!NVI!=Y&*1{7b=KiR47+IX{jx&W>V{!{i-Pdr)2aKs6qmTH)X5u`t zER|DYzw|}+3%^0rm3{}cHox&EpJm_+IHCtASH~a9-`R+N;V9#o|MSFH=jMego2uQ9 zh5Z`4um87Nt3iXg$AW9MacrWEIyIqB27B6mf^Ld<)x}V@eQPGsy&f9lyQWY-)y@^; z`jStp?=mtCb2GSpXVZ2$x*Z&WgwR3gia{_C+sn^-t@+Tep+rucz+gaOw1TR^-f$=Q z$Ixvq(b41!kg&KD4uFA|jZ1sZD91jElUO&i+=9^b4SJmh1UL~;0M$n;UXrD?Q6^8T z_G_4_NNwVeV^UMa6wmR6@~SrJ>YCj{o%hTNmtDOUn$JzXKSs924L(|Mf7k_`CFm>N zAAik4rDfdZv8idxEdi3&{HtqGn4P<7kTQ%WmJ;bgqKlSm)|@6y3+p&mEWOmb@XK4E zICF`j;2wv}xEPG)LkZADvs{fKgnw|ql~HZ?Qo@3xIB{s{q`hrgQ^L1$q;O?WSv|Pc zWFNVeDWyBv!%^45>}bp=^CkedLS&G(|ikrVGXL^0947+SeuHxDP`15tCj zdW$+`_giS*#ffScOQK1l{kAP7Ol2W1em*@dpVVlU#1k&TJBto2D^7V%Q5YEbc+qKh z%E=gRR>c-3ojqv|r(SRh-Sk=0h=tuMjdqlT{Bw;c96vjjO&lYt?H5j4Yr8XMKu3Fx z3y&3dJYrbMzHv8X+)oXrV19vIGloE9YdQqgE@J3kZo?aP|HxASHZjre&f@2uguxTY z$D#f&5|Dg*vjIE{Y}_TOieIeBfC3zWoA`p-MV%V&w#ho(I%gdigajJA+ObU}AX6## z)bFJ$Azc!wg6WnS7!PSeh^0%hbF4G@^S~b;MwYV+=QKy_O^?sb{LscYtFmCktm1O{c2VO}8pv-5ia21U~U}EWdvQ zCuhyu9;_Rj09UZFBz`e6@+J8C=Nh@p#l5D-8}e#E!rOaV*!WzkcYlYJ!9T(a+$J;J zh^3t(>6lKD{{YB$kwHJMsBhwb9l8HC=EtA)iZ2k^Kk2PLOz& zm&sj>x~+5dp=0-eB>CV5QDAL)Kk^o#|8 z)CNay@dN#pdWe$jEMVli(RSacidl%U<@AAIZn+7L6P6uCRSypVIcG1DOR^ir3}` ze|^Ahu~}LTHV-c0sbp*@<79E+oHnX-xbtDcL>PqNt}NH)vqH)2jJd^nh=G zmbeQ~fy~_rCSFXQmRa|?e)xxG%)X(#Bm zL>NvEc#E{YIMCaW>6Zv=!R+UYrJ{By5noe{jgw;UyDSlFp;p{h0YA9Hn{!IXy^NzR zoKYByWnIFYHNz)b3kFh|cBpH2{TC1k=fk-`NQ6xkzAlNHIXc^f_YNsM0CE={t!x4$ zKcX=80FkhodP3|&g2!}8jLz^-rHX;__{uBm7Ju=Eqc{_4t77fgWn(W*k7KWXi!@Le z8}5U`3VQ>t!cHx>NH;`g(7Hwk`o3KmADTjTyG!~>A4;9oqQ8a-BY|ygyxs;Ib)=cj zhMSe1jCd^Rf-l2@tO8@HW`H46uT&kYL$lh*zrO42P}kUgPE~|asmc^6Xby4s{ZLlX zb^H0&DuQ2ip*$RWNtcTBh+`@>L$Vn9g(srYIG>87I?l+Cr-ep2vIM#VoWmzm5J;U1 zmn6`eH$F7fX}Y&Aoi>I*)kZmN^+7|6^Xj0qPKSiWOADKhK_6%RxV0W8tXX&Mif}?{ z@t-3n!?153PFG!CHE(8DIIO*9dKdT8xL05}g8O(Cy9UI9tC7m-v^LPDK8g5A>PI|~ z)j9SYCv26eG>Hqg;~AxueV18JYl~(;vwot9p1#CIyh50=5e>>WLDZzNH|Tj;t8 zpQn9ick(O*zQfxFGV^$wD=u7volJKWS?#$mD|X*4G1Ze#b+)gC7*wmUj=dvFSixrr zJvDd;Y+n@{#{yS#TEj(fOzj1KW2q7qTZ}O$g@0NlbA8B59F(sSOoCr{b$s|6{txgz z&?J$GY!MIhlWdWbq7qaOqXXV%&yLy8c7^S}4UCIZOcR%9qbj#8#^QEDX~hw)n(OzX z%4lm)X#`j7|uZ3XNkX4fylmZQk8;VA3u=1EPI61wM)$Hiow- z#!iVH6vv)ohavK7qBg1)yQ24YXF6%N<*@CL6|G)|+esFn)PJ(7gg##Hd6w>l%BB+c z(Yf_9!A!3^5uodrZ*?u!l4v1!yJ^YXiJ)@A0Zm8f;dv5*+`SA58ruoON{@~1;KNY$h&cHe~J2Z2M+niEt+ugbowddyNT-y{R8OMF)9+u#PW;;TfH&ismbN6Mj8)$ zdJ~La8tN>j>V;$&FGBHmvmu3ZMf(Ayho%U1*`4_|(0#TkwK67Tc(_Sd$*jEU#iGtT zHt)Od#dH}wt`H6V^>c%&oK!RoN4>QGR4To9a})1_L4q*NI^PcckkHSLeyiP@^oR zyte>VUFP$owkEYNB_~2@4pOg-4#jzwDh~>vBnMK=D9no)=6jnt-$+h^a!Q}ipr4oW z#xt!W%Po%ilCR_3w%?vyzfDU~Wga42#?+}zxMf{Ou7Ft%?biknj6w&{ed9q&EWP`I zlk`cPEqQN5Xc?zggl%_9dmLL)+gpxts|nvD4oLlB1Y4KxjDM+isT7dS0u-`!!E(GK zo7_pUz{$O8YMV)=%0og@|dr54!oW*3Pyl4kMkJu4u+w!+9BiAlP}kp`iI#dXt` z;j0?jiZkgqA3-O|Xw^+9I!mwF#J0LkBI5xO4g@b?XNq`f8o%(w_`>8x`M&3b{DcHG zmn~LY!Y$wEe>7i9a6+L>8A?8=^h9wsd}5>dUe|2loDGshrIoSV5Gv4ZMP)8lZ4F-Yl}ZkVCb>oK75fT@)gvUG}O=Bzo$UiaFJlFMXJe6bha5Z>|){_^?&E zbh4xve)N$d96aLcTg&E;v;95K!Xm znrxERA-jyUf62VKNY33cq!|h;mOI3>;S!=ljRl~IpcqZCv1(@Bs zB!EpRU?h-)`S_Jez`>Qmq~p9|OWQ;7K6NDahZdH~D#e6|d|lO(Ssn0Y8XAn8`(;`# z_b)a(Q3R?60qz?(;Cjct4d~wL0hLlZze#<)vFWAkl5<+ON;IU;^rlmDa8TVlcD|UF z`EZnTF_+t*%7uV2rAe;_dh`$AH$*=7N`J81ba7kEPE@bC{R^f7yD|1{|D1CyJbNYQ z>LNLkD>DG1X3~5!t+4xaKIycT|Dj4)&kpz{=loWMc2*Jvt1wSVIm2=yGlTQ^7OU@W zNVDimkEMjcOR;0M`(HuB_O>t(Y;fSK*5B3^hrpk@Wjyr;}E9C8!@-jsGkK2>sWJ@gsDpl`{N9Kg!^w)~9+CW;3 zSTM+uuCDEA)qp~y6(&4ns6`G|^>l65h-^ce57MP%i?*?S#mt6YZWj<|1> zE~Js6Jlmzh^6CtImUP)b1}1aJkjwUTeRill_R`g+GW@+shMQCrV-3u*ODP-)4~0Gp z%w?SN$&}UFDEw6*_)=kXLhjVmR+Ij~C(TmKiiGBWy$s{ro;ahzax?y6Z!bhIFB z`4tv*)#HzvVvdPMpEy#_Ht=1WWXCBvRTL~Tds!{j22#iLgeOZ_sotlF^ebiw7N)2y z=`bvR=H(PMP1D!sD8u2@o_aySI;gp7W7JotU!L0rriQ8+9tixg7go<*=ioe*!$?E0 z?qWWSv6;#n{HWXJOff6FND8(>^Odv*0ecuBPor{~?TRjE*kIq=p%%Di7w8z3yP(~V z!OIF1DUT=`b-9||T%Y~xxTMywx3N z+2`?z1y>~2CREJgd}K)xp8rL`{i%OY(6e9x%uUt`5}VUSVXJDsqhG}g?1wjcxo>5? zEB&bIrkme8uNt^Ei?QUKpCDaZSm5|&?zq{rPaM8mC5qF*4(OwEJqc z8IFD%od}jmxpcU~N#R^D^Y!M5oS9<92t~7^imP zawD2MpHm}D_vGgzQ&+0hxrrfND{F4~EDz^j(7Y@Hc=*ac*4|A2+sK=h431WxNZL`C zDy~`$vJyqYHP}DE@a=YXgaO4`$dSkxyNF<|XpC6-aQrNoRF@S;mSn~^aXNWxH;ive zYu9JB`bg8POo8m`N+arX85)CnQ=Z>`GV-4cQtOo)@nwKYRom?<@SDmPY}k{+iv?Me<5GZ_M~7r)ETP@`3lM|^V*_?_O59A z4|-Uoja^APQnb_?>VEikz>eA?YdjxvjDIRm>^=8ckXL(G7^QqS+`!}*ZdtTGx~n|5 z-487RO|2?AgIIE6A@@oHR+ko`oo#rW<@>ebny6`mw4rmI4^%pg?h{(tpmq^t{a*VP>Pimf zN@TULvaM96sJF=k4N%uq2c{$eP^VnhcKXJY`hOUSG3e97=nX$Lz>|*aGj&&%GZ++B z8gxSlD%3`4R}67~dNyQ9#E-9tn}6_HiC48a;%)MzLKiQ>ToL{D8x%vnlOVFH4D8XA z)5>Ud5G(`+eR=Qt;_NfTgyaV@*}15s+{tmgSaB@V8rI zY5vP=_>}`wPZF-^`(arMj2^$o=k5oIZIj}`6>x5G8wx`876P1m@W z&76Y=&IbmMrO-pKO(|jf{v+dxN;;SkCh(=A8vg-{JDTi=9Umk4aOv%~h}~?1+Rqhh zRo*2^9InJLwL2xmG`noon#~kP*Vey0oWv;lM_e2^C(-dMA>o6 z{a1m0;a^6t)X+wEA#>vgs~efvi%Iyx!%b7k0b$156977ZN=tAGJo%S+iD$P(s(4k+ zVWDa;!|Zo9?dQejCV|7^-x^_uOo<5*rlZaQJfW!;a!D;KV>nzae=APmnagq?`PPY} zmx2An8R7ic7m3x>_OwkVhn;=U5$%-1NL~H$zr2^9kD-@b8T^-%06(RP*x1wurbg&! zw;G2y_dfvL?n#RyJ6zk8hi9f~S?3P}zADL=T#`le7G0cH^ObVfFxix+nLZfU87!4z zuSVUI$0RW^MU@WIb#|(wRFk5ed9-qPPwj>+UJknsG*_IXV7cv`=R9Wn1e3rlgrAwV z$wtp0zNZ8PF_NFB?a^7>3N&Gjb6HW1ar^7?V6q3up?eEP@D)-Q%WeO(5M>9&{dNC- zqWZ$Aa`mws%v46HD3_AKe6|@0)ZyU7Iej-ve!Vs%Yh)lo(c+qjti@YMm8) zSZeke&S)>Tn3IBmzsXn1KfsYcmdaY&Dkstc`t5AQ`O$e?1K=t5@_AI?{bCq)aHoLH z*OldKZZ08vCZC-7QhYDoS=r?8%aoQKy^KMjajm^sx^Y1PlZ$FaVjKeXUA zvV_*<3JZ7msih`8@ zMTm>=C7Js0B;>27y+AGNr#w0!9yyRMv4_QZe9jBy_+zn__NwBl8t;-F^>)v5Qy^ac@A=XeL?@A7;d zK#X&@%)jOBa%59vnRbFIuSt&cyppXkYBhE9Xuam}&U4-bPBJT4IWb^_3DGhi6(}fT z2vCTnFH-Pqid6jKc(6&uH>LD;hW^^dwROz()-we&MWmK)weBvy;v~Qxw5qsLITn-Y zll=#{mRDKK5r?I|e-o2Uyzda+%QPs)ro7R$6~H>{grQ~r$cU9#y_A1k`jHnsy5xHl zLh(+AO(Q6O*4S@#K3+GlW6cMS@`BIBfl4Z1k;3#Is&B}`y%z`l zxCfwDpS5s2n5d}|C6e=f7`e6tZ{!kITNI>i-6Cia_7sdtJ#-u$gIgot6> z_w!C_Q?fPP`ZFM#8#dfK0&}po)3@1_4=3hzfv%gY&C+C&xVSHU#xDS3OP_RemacLc? zYr=i=ax~v1Rvi3?JpKNjm->CASnXfz)1G%z-0S-Cf-dX&PP(jeLX+a4pQ*nIqrNx$ zDnC5`JY1*9yVa~-!~b3+6a^Jc=XFEh5mmSAXWh?6S5OUjo#C8vJ*&`}1eenH= zDbI#WU1g{eZ@c2qiFE}&5mA&yds(8&E*|OQhf2}oj9vV|aNhW{A6%hX$70&8K%PWg zXKtuo0qZVyU@Bf*C6K%5kkq;u+@MI5N9I0eI?9#V81urJ*ldgypVdtjn$Kqb>?z=r z2h&N=<*wxBlI)paM_C_02W8dAO|KcPO{dL;`L{l4jLG}_Ud#YtWDurTEn&f9-(oK` znZZLd(UZz`uk7*pwanYwTvT!9f>mOcSd6al5XCAHH75&G^ma&9uemRUZOhY^w2XYJ zj%qkRgbar}g-=;k9Tntzk@Y>fpQj`Z-{=4(hakr zwo(OI@y@WqQiTy}zhSD*U;qh>^V6Imc{zq;kr)gihoR}?_u^Byq>q3swPxKF0`R3%zC|iu zxixPCRgYf|zu`V&QFpN~BLIYiJ%*$_SnK`}Wnm{+wn>$HQD+$?b;2NBT=qnZK8s|s z@>d9t&9Q8wf)~g!vf0V^6Bc`b=~f_5GbzGw^`uxni&)m+ZvHL*m3Csb9?cm>RJ@va z8NJYM9V(?ij!uW=F|dmdNNazgFEAkiP(9(DWy4rr&~5pd4a z!3%1qOLRKIW*)CfPD3gu&P=?H@^N;%oV9T~`r(%w%)&TrSN>c=w6x0lo-Npc7Cj!3 z!s0d-#=@mgr=8C@QK=-y_L<4K3X5xX;VPPz%9PGI{Tg4Dmchf7IoJ71!OIT{H9^?8 zTW6#u#{str+QLfw3k;v9s2_NZF=?S;kTLG*AiKIiuVeRgBQTi z0U9`D-z8gc-idbmwUAc!z#tC)C%9zh29FBdlD4kqD@c#f1k1coiu~ymZQ$4t(&DZf-zizqwmU8=YpGvXY_b*_W)+)|bQ+i<9 zD_iv`>Vkrb#@*5q?;n_u_U|Dj@wIaZa5P?={{VAa`$9&Y6>{>Y+)bNRO+^k4$Lmrj zC)7NmS`B6UBh%a`?TL+GeaY&oIc|9)%_2?a3Gd>!BzfC6BvT>E8?uW)Aq8!OIAlV> z>}M1FkFX!p+%9Y#wawR06y7(UyJ?ExRizj5_Xaw}v;Lv+{W9wDiqw~@t?5Bh>GyfJ zCS6k_8oz71xgE>JmD!}jO;tOVal;dR__bkCWrxw8R$ z`h(I3CWy$`LPK6Cn{_H%Y1*&fA3vp3jf|&qLex?z1V~Ha0B%+YBd9??Oy|-*GEpVb z(FG@@iH>k4;7o&F0Z=}>350DEZ- zY8OV-VATU^l*FeTa3MUw=8W03Hb^792Kgw94HVHCn=F$(lQG|XBL%*SD8$;2Uq{R3 z^nAWwN6Y2(e7;{on!aAbmlx6U`Fak&IN@9?WF;ki3Z|>&>?|%V$!#73eUeg0}yMtd^0Qys${6u^#-V<^1t7mwuS;Ftn zbf!9WkA<7u5@qDJt?^oF@q4`q{&NOHxE}~@_OEYL^|pEmdK6ZQcM#%3i76oJ3^C%Q zB%P(YCr=!@CY#gTWw)Uad{-n8sF`~$^->=c%#AmrwP#s$iwqa^8mzxBgx0D`j%79i`ow<8-LF;N9uBb!Ks%ug2 ztV+r>RM;9ps$AM!ZKe`BfiH%z%L10PlCECY)FDT#wbBB)G+LJ)eGr>nQ%O=ukeB** zh?jz|kxNjV>RX#BU=8vTgo23;1n8Trrsmp^>Wb$eWeh#za&WiOp~oavG+2D~oaNBR z(>J{=PDZ|6+(?#QlzBmF{DCd`5R+tqP%fl*2{G$8&zcTr#Ig-TKbA=N{1Vu zoPi1;t60hM+=5xj714Wpfp)sr8xV8A+?qUcvFZ!66(vNFpj4yRUgjXI!seY)mIsidT3Px;KJ@r}EP13fx@BUoS$ZC8&k zVKK;275>(WhFmI4%(T*az!Skxr7<_fjtT(t-UcfvYARY|`Hjs$_tRL-xn?_acX+Q` zcAu5|at4#3Lw%@b6)iX{)sIQaH#TiQ;RZh7xfg#e-9i_?ymgKq^9GI;S383+-E9*J zmf*CHpGn5=Y~N0+^Lvr*Y{Ug4aSi~!a$D@t(9o#TKcd~*NJ>f6z?lryy}M53YT&#J zCzF}J*do3v!1!P7afu`J*Gs*-^>uD3DpRf|a@KvsVH}&DN4V_Kev9_o?WSp?X}Bb- zRpU=&oO58LU@snEmVlJo9S6=b9KUd7CV;PG@b9-QY)u8s6-~dnpJf;g>l%9LL60!a z#@<7WJeJY93PG46Kt#6?=C6`%u02#2c6%LX90Ev~0cvyou3nkPv07VwV~InS)Ot%v zJAWYC0CP-vmRgS$dF^gtuvms2Hl)wHe{^=d7YDuuZYgEdu)T^39d`WULxAw+nSIv| zik06U>X_`zF`Z6}fPZovqgl(|;jXv!R2OOJo>~}LJEltJOu_g?)0JL{ZUY<8EJJ}| zeXMzv{{S=fPi8-HyVlw%t@J5duhamBnMS0NB@%e6cN!`mB$=%wD>C+d_V>DH7n%&m zH@6r4a+Nnuw33tnbTU2Se&PrVY4P7la=yk8ze;vaafy`bt_$dtylea`PE?h{gqN zF9Spx;dVkASqj&$SmTLJIohUgC&|p0CFna&P?E%;^HjlJvL_sMYEsHm{2~CFP^PV6 z1t4p$3RzIzNIe8cxZ40r9*dof!|U2Zbdq}CK-w!G}YbLfJqFiFiJPH!hs`1$mk*GI1LhQp)S=kH5V7E z9dT|ec~R7uA99t6fvZ9{ykwnW<~Wj(D}X{S6_nO071Tc>qM;NmY)W=C+DGhzw*VAKCC618 z`J0r*rrx`41GBi*%_#u>LC~ryIb4WtgR0PYrU%_SoHS5{#~gjtyz0x2x=NO!btJ`B zgq5tdp`ZcLWQ5GeOIOa(DlE43Rl1Ut=nsV8+gNH;Rzm18ss8}SZDEGnDo;sk;TCI1 zXtQjM;d`hMEav|J{W6qW=+5fOq7ut#{KTtH&@M)EU9D2M9Cz_Mpie&vU`=9{(|^04 z{Z614s-0i(iA)Kf2$9BF2wA<|RcDfA-E}yPpD58sK62z&vy6<|E$b%JZE;CuAn4-f zDg7d=n7uPgLD6U&R}d{=-tMIzan2U7-8;@THL663Pgs=1*^m?mAJ)+GH^#k*|_^=_yk4X4dGSGXDqCf?c4cmXawvhlq}Zd_z0cjc6_`kte2P0&(_--!YNeOddLPX?Bj)GE)K2CZz zfn=O^#K|Q}j94wT^s!lx+yPZ((Pd?7>JmmbEkTWx_Ag& z+NM}25ol9|XZ$7`2~{Y{ndvAFq79A7i3(ZT%2W`p!VHKZWI83n6hBBry2>8#QEmCs zPNeNCJzz(5Q&Bk*xa6j1Bi;j&GgSLY=z=Ps4Gx|L0(&TqW2n4MK`NBKTEmPxLg_U} zcDe`d*BgDFnI%;hClJ~P`^VuA+1nXp0Y~L$7VwuYiG8_ZR_-(776mFpMA3w#lquij9VLfe(3?JpvcTq>Hhx! z)Vd4%!}F7TAMEQhwOeYxPj<~(O`hJ6WUZAFOR5s^MBr{2ZBqM(e+d1h3HJ|}_U+=w zM1FEB_%1KFMS#^+=OL;js34-9YU($coqmwi4c#Ge^HgkIr?m@PS#?^8K^U~<|cSVB(JI7ucD?Lu&MMK8EILJL!Y)X@|^u_AhyYMLXMQA^64wU@#e za(SRp0AJ=kp@(%vgP>Z&q@KUF5}l3_=7zSSOv&jDxRiM(q8NwJ4_WqvZlO*zBT8r6 z5&>XB3(8j71frvshmMi+dL;>I;GBzXVs|D`L_tT%)h?*2g20fF`NQZU5y*TGG-n#;S!YZHbO(FE9C zT$+gJJY$b(3QeMcAg&5T)#Kg)bLdi-bV%C8iILVi(w|`|Jy7T%iAM=VF`|fx^S@4l zePM#~od~7n^&Q7}Pic;@kwS%Z(3o+$p|_BXShi>m z*$DaZuKS4Pt8{<>e7ZvYw9%S@3~r6kdTAXo1@mXB8qys>N!zDrXg~(2mdR9OO<1HT zO(ZMAI?}!&D=2QNN|FheO=E$};@YKkE#V+1agaS>@Zm)sSW(h$Of51B6Ys2cp)4>h zPgEm7NLR)_PJj_>l}!c8f0_q9S9cn(KTwYm97saisS}jvA14|rjwzo6fDg#WS}Io_ z;*MKX#O2qt5)%@1TNmmjC(AkX(m27Q7`W9J)hvKc*+^<5=N%K{QjxbMAwpeXS2}?| z8;ueov>*jYPB=OvTS}yTV`&qVr3|+~h-oMbsg#a~#E46QqU{!vWSu(rN65{>lv)W0CJ$d=FBOI7L*p6W2E-$sMc}k4(E67 z-qsTxxvP}^BaO{);A9kPyDW2$ak~aIjB%zNM}q z^{UzJv2ijVXo`hbe(1fOR%32gX8dnRs=F$Row`{X6I(OXt*}3b#7Dln5BCoiLNRN4 z>PR%H@mRMo;E%JA%>b)Uhmx&_(@{pHu<8`sjZ7^BNP-NcW7v(`BSV^M3Z_8fMF`aZ zdGh3tN65}OQ{qu^MnOf(U@b;zl%P4tDq`AS0?S9Bj62^;qQ!}a36asH~2tp7h6)`UfM+!-KS*apP2@-@omFS!j zh2TK~WPuT;G8D5U1T4nSEEZcph7#1Q0?1bA6tLA4$yPJ?!!M0E9Z@UU>s zUJ$1+lAQOoT8K|mDC=T1PKiR)CJe_|U$&MVL@~ED0G^#8n~0K|Em8F=+eY0!aTz2! zDGO(Wc#6b?>UzKrimE_7g$-iLfL}+;<@9{MUq?#$eH>peVPSl}j;=4Gb)cg`ErZGM~u8-O$(qb3zF|RNuWv$T9n~B zLJO0DiwPNxVIZ!oqp2(C`Fy>FHGH#4>LE!kTSRqyGz&DDNJ?^#f^BU{B*K>}wnxj0 z==prJk?B1nsTEsO-Z~N-hubJ>uC+*`qin6!5EMY1fiOf6nH@lb$X>?-H@~8k*jE*X zT4`%z)Om6%Q37+*`(o2)iTErZlXIf?kPQNnnBNzAy8@}^G?0*sCAKYO*_|l4=VJ-cvSZIt6fNZwu8Gs69SynhRNRI@NzpD9a!n;Vf}GkBphz+> z`Is~)AEYunqC#9U*V|cbw@K8@P9vqcAoW%&Y#RuvN~|?^d%asO)VxtngXM2sVp|&< zcR>VXG-{_V54N3_(hoo;55_cWIQEJ!Gx7pl@}2s3ONA3N8cHMuPgPUe0ZV!} zfo6XJKf)Xi2+KB&$f~xdW0L&Z=@FM~S}qPy0>zt$ra=Z$={SyjYU^hzGJ7cW+M1o) zx1fQX6zp=-qG__scbce%KmmbVXTOxyPc1 zd0p67RXPIZ37l7MoE)_rHJ-**DERXFib|%^;R)1jQ942RTb+)fRqkVoGRA18<}=7` zQc|F$$%OQgjULl0qVoQ4hdM{}K{C#5RV6ag%}Lxr0U=Q3=OPVl0YqRsVeUK<6jaJH zo5oW5y?TYEj?K*KBAs)Tl2>cdZmUj4?`6msL(4X+x~UNJ`3uN`hUkQOA_*BBqX|CAlYRwCg=0rH@D=wib~;zJr_FQa_X+oba=BY>c6v*J z^=daE#P2#J{HGJQZ)Kze4zyXdj1uNAL*J`GR4t2L4NgG1mmJ>xTidtvB$GX78FX%R zvscA$IS3nXWln$vf8N18y;O27>fLs(r1hzo^p@Bl>8#}=8jD`bTYbl>z-=*(%3tgs zEmvC{HN|$^I>kbRM2clLgn2+3$b7yrit=ZyxErXMom*v^?K81^%Poba=RXKRDn9PSDFD+x#nst$%NFCB@r7dmKRagda&(x*`>bGA~oLaXPc7`0SP{shF!3bK_ z)>_9{QYJc89q*>L(z#hyLP8QukHA4YgBz-kjfQ8?Ci0n@{NC^#6HT#&4WHpU{9-0z zUh4QObZlj`bp9m!(NZ&Qq(dn{Lv0Cd?tUf^_L?a7O;c3Sa$h0Rz13yCQLG8^JZz;HIHT>f3UWkF(hyZMzzQ;g|zFp6kYeMl2H=a^p~&jY!}vo7@&c+cd-ZP*mR#;Si#!)(x7y+x!*sOU6{Rkq+3VRa)(sY(~zyf_JQ z$?`(`tLT+(c5*y7UTSC#tj~;V3o4fTG2*-Bd$SP>`i|@IN>M#UCANh$%WYAWVbH5;}0YY;YE5+*x9ta)Db-pdgiycbV@ zVJ;eH9^#%y=BuL{ZhVDt*>9*=^9mX<>7q(H$wycloXY67+qb0xKkfd+%*nD+pHqpY ze24a`fggW=a0$%$FAm}>J!yK|i?rj{WFcNtQ0jtp9rl3}cDBDqj0`_B^;s_`$vZX4 z-ze5NbRUYP8!hUe9mUYgZlx|dcNrfs1V~OHV1}BjI~r@EWHOwUH7>ZXS7{{4Is+04 zPAJM}?)OxNbKV==o@qtS6Cokv9gt{=Yl<&;f3wr$(gJ@( zSou&VP)H_n=?oP7ap<+B3GjpzY+-t$K$!SI$k^5jacL6=uTc#?wNg!JqSPO7e-tse zrO|Ii!lAV!1sNW{NMOCeIwNUD^w}jwmBzKmg5h&z zNl`+<2cgmsx;GTv-L6fnA2^?YFqZ`5U9PFJx>bX_%{ljm+yX6yHy0;?Q4{Ol7<VWD`gejy|61-hZw(B|cgjIHn!uq=j{9~^=t`^iGt<`x_I?Jpksi_-5qB^ClkQzhb z)Cr-eN0j#nL(L2Z}>u(G5jQ2kF!6 z3Q0mzhTPY8Pwj!4Xp>TeHrj%wo}G;2g)Nmr(DSXzO4Og-4YX*U4-`Y0xVoLYrhjBA ziJA(bg5a_eO1@$r4irtjn&`I*t$h;NKsN= zfKo>y!D=beN(Q=}ve!?Pc3V1xR5#R2rRL35Or?b;V1HyqI^KmE%b-(jaB1X~jfP$P zJR^^&QZX8&ZA`(DNe~SYLE0i$<^qhqu$fwlW`PpD)iKbN)D6-=nG!$ifXGF~*IuXt ziV~Fdj)1x1w5*9teIukUgc;AsVJMJ^UhndRl8i@+79hJAt}~sP#1qTpIG=OLIOgj4CIpt z)F(j;;0T_xg@BMs+7w3JI!9N_-&5fqE-XsPJ4(Kh^#NZANDCseM5Ql`aIh88D#R>H zMM`n;0CZ2bN-TYObTfuzhN&L$SSjLVuj2xUlcGYfQ?fb}DB*;n<9$}IRVSp(BVkT9 zO0A8!b@KQ?+e&kBLJ+Of?I>yOswOzmqb?MXpT-7sVu;w~q8KUj^a36B&w^$54UARBidb;}02{kb(km+&^&hAp^ynhrgT)@DdH(>oT*?H}I z1JM$Z+;;RGread=%xqPgJloqS)i4)aVJuX?{GEXWX)eDQt6bU^nyr5uf4#j^b$zi1 z5T%yHi0D2M9W*{F&1-fBiI2VGbcIfOjLll>5t76e#~+e#&?J}XDQOa|t%Rg}K9DhJ zYMpGako81W4;8IJumr)JgryM@+^ObSiX`1tp5s;&xhM|b7z{~Msx6YXG@AbaFRhBS z7fi_@fM8J&FHgTD3p7vG7f8_bLK7sWT|RMf#)=U|lJZI1YgH*RbdZF0B3gVR>1(RQ zvd;An6J@jg=Da#H13X6Vu}n@;HL0ZDrq}Q5!wvx_U)A%T)o+qCr~g@rZky z(9IRs5rurDD7AacYHp4wAzFWmWSFnZdR(-OehIN^Qes8oRJCj40<%>xJvszBfofrM zLW)nq31_6C?xPPHrPU@^ai?T9+RlPgpTbco9qWKqJB$P`bxP1`YHM8eDjg~->`cTt z9_JlX&9-Dsqe2@_*hrs$7l1a!ud64nfjzA83AQ;~zzu7c}K z>uM<>X$QA>n#H)XKtiK(t8pv@A0=*FFMU--4JuZnFpy{TjpGSDKwqA{%EbmsXr=wN z!BI{Grc^aQNVY~Q7oG1|SoDd?@v4ivMwy0-t9L+nv@=k~XeAYONC$vm%|Q z&>IN=4J96P%HHlP-duYS*uVf#Co!fH+6RnP-s+X4L2Me-S#!Bl&@{K5SU^A~M9Oeg zt)r!N0)@=w(qQBW8(Z%qljK!?k{o7gvJdO5c{{} z1hsFvR=HFXPUf5;r^t~K#oqWhA}+`nY?<#k@FP~ibHCSraj zDg1{dT@u_l3`5^?0eW*s90r*iw?>rU=l;N>J>B zV+xz&nDTph$Y!s_d%ZiV9uAk)=q4^fEu)5igX;?K&$BoDH{*r>c1| zy8||gCYwupN&f(e^8>s(1r&Y6<>e>yOr47*1JAnfX-Nu@pIHu&CPL&fi2OD&^GFqb z=9w*4O?G;r)t^BsK8id9P;YLXBfblK#ADlU7yxRLDbP#tfooMNN*zqeaXJ-- zt3@zSw=hAgT6G&ih-Sp;<)G-SPnDK1Z~i0ENt=zz*~a*y%0gOOEtIsLlTaEDoLFX= zw())nhie1xu1DgabyZcvH_9BTc&XLZxK7QgU%$ew+;o@Q!LiK5fMk3q&$*T>u)s)HLnIL>&7jM|wB5{`P zmPH~xqH%piWmDP3;*m?KxN5>wBvOOWiN%|USW&y()U4CG+>|-u+B4~I?^5HMa|ZFt z6O=U01k_b0V{MMQ={+KMGjU%?RL(bHQ@Dj4u!}OODBihNB}%BTQE;sG3Wz8opjNxN zH)xFRbwQ@xnxqpy*$TMOCaj?#t=Mi&auo`prDN4?eqz0P$80anX+_+vqdsDsm+^a* z@ky$BWRci-lQHy(R@v@4s4wd5heQQjZCg2^DpdNBtiEy(;IMK~&an<-k1mXvNofg@ z-6PUEG44ajn`eSL{{SdlkkKUyRHdOwf}3vS!?Mk)N!%<~HeA?-%FOi}Px|4RA))A` z8SGTzdGn9PXd!#xjyN*oA`lStD7TsB3xG5egmZxc-?Gf9I8>v(W z`_;9fNuh+@lCasPa4KMIC?aNkA~m?vRW>lc3t)HaH6sR+jqtKkwJ9Ktdduk- zd|kf4vOY(m?ARk%=QI zswQ_FxtBlSuR;k)Og7tXQ4{1U+EsK9?DOtDN5K3OZR0g#ZvkVYQw+F){Cb&*d`w1V zIQ6~E!dLh!9~$n3);7V`k&d-giSC;<$oY?za=t;TZN&@2o2gK>6_g@MTSG*wOzu!Ic>(?RiwH%8^4+fLFGMRK{{*S$+hi8BIe zJ$gdxK&4=sI%<*CHCM-0skB7884)={8kHo=n7}}RzP!IpEGU&9Dz=h7QO3kkjl7Mc zQh?nl?i3{{t3g7RCXhs@36wY@ZLS$7L`1c2R5DyBiXBmN&MHLD!Uu3iG$0NgdUfpq zjS40pgE`t1Q^*3RGE4~~crXMU&SQ44GByR`x*wDeFH~4Tp|t#c7PWv3fQBE{77$-9 z@AhGoOC43L>E{0c4`>bTsv%B&~k1^Pqg~#cU?(Q z(jFH_!3OIv5}uovZz-3kqORLP{^)u{ZVkyA+~Lz4CYFjfjz{9bcalp3yp{bRa0?7!6Q<7Lj=)uL=sxD@_v0` zgC?o)!U|4RE`55(OZ86xlXXXS;B}WsNa&ql(Gon?q^d+iK{z@fMS>KcFJ7_o>c}wO zlj{Tqh4Z8|L+M&3K*!hm0E-3zR7Sg*4Iwa3U4%1PqLLO1e6F zWt0TS_a;#5ToC58)hlT&?JNKaK<@+=3L)HFbVqcys(BrRY2FgnwKF5iJ!;N{Ya=;p z;RzunvQ-I9ldOdQ0K*&!M52fB+NUN$4E2xz!ADSs_rOFCV)e@kKc*-Bqd0Darv9n4 zNxIBhxf7d21Kvg%Epq$o0!Y_bUqXN39eRA>uWMAZb6%+_U1TXHZOryEhs#2KIVeI) zy=YQrG)vRP&rsVw+ktQelD;%Yyk16sygk?Hv?4q}N&@ ztkwjS>({g`&Z_H~RCL{9I#0hybKMEi65N`QNk|_!``tjgC7l6R4%sRNfSlp!vgTPG|@+zb{B}p(->1-z5A00InKKEeO5iJ=$rkCEXLTol$!F z2xYxfIi7}kLp~`?)dX=9)2SUJr96SUB!k%Zjs^8lMf3(f zPdB10c$sdO#u|0K4O79$3!j_9eW9u(ldAEjoTYIuGNOkYAfSUSVG+t$`h+~T?U{wH zO6!Ff&S|NxHr0Pm%`EAiJwjC?`dgNVq{)kZmmoZ7;H!{-p?QhpbWTq%5^~cQ^s5=j zTm1%6M14AjSLG|)q|ywvGKoB1Og!v%jtZ&Cc;U4^?0UrRg+A5PLH5d<8&;V~UFQ>0 z2FS`0qNVB$d{eE%9_X$dL0YR0VA`&@i?i6N6}wEKpeYlX9wIzx-#@o&Y{h4QZGVr& zZ5#`@kF*}tnuLnykAGQx_ND7j8`KLW17%76pi?(gB`JZVqdx85+g+WTH(ZU*S{_50 zNi}`GiUnSIYm~A|#XoeRoM(ps0W~D@aKFkznmtb(r>xdxvY_h2(s3@yYJSI;!(b!BbBs zYki4cAwJs{xMfJZ{B_Ll~7)oB^wSNctc*i^!-YXrd@QzTfJ5`>(q@(W? z&w{~QyZs~MR6_DZ$N?NpRbwT1{>Vnao~c^BMQboVaM4aR#d)z`;e^m9_XV}Cf(9WI z5!DDn%4Mf`NeITf$p~#~m``7%4#?7@l?*PQ57`Ym3L_mo7ysE%O9KQH0000802p@q zScJy#-Hqf9066px02=@R0B&V>Uv^<^X=Gt^X>V>XX)<>~f{Jty>0Jb*tJtX0i--!+1S!&T-{9W+ z>|Z)b%N0)UYb zAPfKi2>^je1K>2k0DhRk4-#~gJ;r>pXH;`T^Kjj;$+-J;{!7=W!1UKgyiod5S zw+3#4^NNuvrxw~5&Z!_SB?gm}l;l)U5_gt#Rdj|ciE@InOG!vcOGqk;Nhv8yNhwQ9 zbN&I`;Bvk$uFB@xx_>MN{!-=sV^={zLE=F&;%Hwt2`MEdB?(Ds32A9D5JSu_80GJD zLk#7|^Ct&wxF5_H;q8w=qd0NDot)7D{;J&Ef9?V4ZDjNpYboU`N^$V>f6XX1+h{ng3q@ASC!UBLBS;4)51 z3UZQS&N4D`VzN>&1u;cu7ez5y1zDJjqAOgN7)GURTn3JCv7KxxGFa;g2ce} zq~$IDj35z7MP*6JKakNb2-o0$302D%4x;@Js8WAN)izQNM7Y4wy1q_GxcP4aIq~1S z{f`I;xR;BGuM6B)mD|)8?dA*j^Fsu}{{xOG;s)GH!_ONI^9NZW(5l>ERQ-to+R4}7 z6127j%*pF)ng2TDg!J};TOdNfX^_qZrtFILMLL0gZfL5bhl``%%GXEu`J;V<|A;FK zxX&LWF9hdVTqrxia8ahp?dKE-cj5lyA>wbYBIW9;C=VJ!OiEf2G_aGDw3wtTTtN&D zW|-fbSA_pb7vv5{{ZozqM)O0v`Ug4r!ZqALcla~ynEJwz2)Hk9fW~DRX>mzTK_e#^ z0_BhPa~JxP7Uu4Ra)X0ePl8+Ge|s-~y1lfdoD@t(##KzpMMgnPR#8?#Ovy=6Nla2+ z(N$hf)>+m?!TJBTJ^gE;{QtwAkbgw(+1@1n;f50b*+c(xjN!bS^DH|m2Z4U5Bz-0r z?x#EWQ&RD-p8+TYD4sViq|O%k&B!Id*Xis8`Gc>MR{$zxhi_Doe^J%gBO9O-8~zRdhX42AA2$YY6j+D_aYNOx>wu`KI~rH1 zp}jd}#3cbmO#>qWKHRMYKr_mgJedmA5v)Xz-dj7+499+=xMHBCb=}n5MAzW54gi4w z0EPZ#1PYBu0{}>rzpuHTCg+u_ww%Q806c&cAOfTSzzOE(ZDgTqiDPM?qs8e5A_4ya z_Qr8HB>*rYrfRK?eEc!*_q8COmF`2?7zc97XX0bE~uM3|9Iwn9{`$T0pR@9 zKc4YE1^{Z%ZcXq1wjU;(zx?gK2myzQ;}rTA{C^_+3-Gt~#BuEYQV*v#+|?<-%bydc zHyGLr9pKC92SyN_Q|v!G@&7U5-{5a@h?>J)!Ilc_=s2xGFGHZ*Ky#y95NGWR0`)%` z@&AzQKf!Om27$il6kxb64$!=30Pxq}00i{p0RFW{;0)w1yIm%=228=fHXL7n`~4X} z{wr6!RNR*z!tIPs%iNL^7T_EBdkzH)0c#3!$c$e@j@$B*3@O z@M7?8?d zGSNe#YN8iJLqtnNyTruA7l=iORf$cAorwL3OvY!no3$k`i%5F=`!gN87-LrnJSqjnFm=U z**&rDLG3^tLY+-r zPyL>HjfRwlpGJ$ui6)#Tm!_5GBh4-?J*^b22`!Q~nf3|oE81^##B}_0I&>~{F?5A= zJ#X^ov4$rZj(>Mn|mvFA?-0->m^DO7p&%@6rp07SXcK+}J=LMY$o)^+C zG+&ryCSVq3He(KAe#G3zyvD-7qQU}WNo1*GnPSCb6=pSO4P`B4eZ#uT#?Ge4=FN7W zt%q%morztY-Ge=oy@P#)gPuc;1HqBW(aEvO$;hd}>BV`EvzK#=imAn- zw*a>lcNBLu_bd+?k0OsNPbN<{&lWESuMzJ}-U{9+K2knKK6k#md;@&@`~v(o{PFzF z{NDu53m6E52vi8n2vQ0{1$_hy1V@F4gcOB5g&qoh5XKXh6LuHQ6@D**Cn7I`5P2vv zBuXf%B#II(5dA1dA*LY~C{`i1D9$8)Sv*R-S$so+SHe~zO=3XeR8mgTOR`9ET8d7} zP%295nbfwlury3MS9(;2Qbtz>Bhw=DLsmoVg`lnxoodwFM{_)CpPu{iM#L?xJ3z{#8Rr!$ae##)hV(rmtqB=DwD)R+!cc zZG3G#?L_T&I&?Z#IypLXy4<>Mx)r)xdUAT9dN1?|^)KnC>3=j}Gk_VC8*CXW7={`4 zUZS{UaVh80r^~{ZeJ?*V!ZR{7$}pNS<}*eaH=6(^1|}IMv!(*3KBjGEL}sRDxn^I? zrOa=d4_MG!T(@{)v1h4inQA$0C1@33)oo2}ea*Vudf!IdCf#P?io}($D}z^=uOhCt z*b>`X+m_hw+UeM3*)3m_yB2qC++M)`hW#rCW(O~a_UqKwovznA5<1#AK5;yDGI1(! z+I2Q?e&DUvW;sGHqAZdu(lhdHlw?$T)X!*}=w~r(F%dCKv3ju;aa3`>aUbJV z;`441+(O)XmmrgHKjAddCGmBVWKwq0aWX9Vb&6EVy%g+i*W2$>s7XFcHUjGy8(9>?p?mua-aA9o%_c*?m1(*nz_{v zSRW)j*njBqaQKn>qpCc%yrjIt$B4%h`FiQZ?W#(nQ7GqXSCO>_g&wmzL)*F{XGL31D!8bUp{}O{Hpb};_K!? z`N5_)a&H>n%DruTC;zVLy~6vJ4@w`N4P6}S7={jajcAYbj~a{)jv0>)ja!XRe7yE? zVZvqN+oadzkEy_^qv?nl!kNTbn%V3**13Xtq50|s`GxjH?Zr2rEIv&y!Ir*%_WOLi z9P@?pOZHceujSvQzdc{kUHPzTx4Qh@`}@&a+&b<0gAKurhE4U&H(OV>mVfyEz-}k+ zT-Yi7Df_c~*K~Jo54m@=pLlTLpzN38uYp6G!>>m{$Hd3?PJ~XLonAVf!6LC(>;^z} zcAX5kyuj5D*l8dJY%l%- z{%z)81ndidmV`i&I07F+3*ga0@M$5~USJ;-kO&_F`A3n1Pe4dSOaj3p1<1(3r^JxI zPeSm1PXnZ6Gynu2j}SsgKtx19OiG5wf}5iy1i0vkh@~{lxamope54s7)AD&3No%`? zG|icuhh=ydeWTI~w9akSbzAt!@|`F90gKM4AJKNn8^pBC#v9cDp&^rnb$dWH= zh}E_E6sVw=Eyzp@E{2asfJa0`a;76<2!MxAOF+m)B&9(|Y)0?olg14$mS1bmNU{Yk z#v^0FwCJpL?uTzWZ{4sgUsOT&`A;xEZA&>)e)$Xm>=ZzO53-=erv;$EL7u#)s&u1n z^>Os|vHr~ic=HTG7WWv74z z3}Kj0%%@f42S%N8`$vB1h@^syFZY@n00`KWLr`p2cbj<5=js7Wkv zXvKmBXg=munnt&% z-oh}n9(|oMeOWhYAXneVNqrF4SrMx%JiU?X%=K#KWM}>AG1(v%7{1YYq;ef6Z4)SM z#W)psRpa^E;k@WRf`B8CXR5*l zFQ|7Yt^M6+M;m*YoHbV|5w1yhPZq0J7v|$|8zI4*e})rfrRGW>X}xy&+OI%NMJyCt zFnZ$7Z0gEA& zIbD5GeW)15#54lV=e&COymW_cT?lRmMaeEvwmQaj&!4_IE@Ti8Z`9>Co@s!#%sZN4 zW`YJz^bjk8S5+*Kb!#ya=x)1P1y=e#FRJUiV&zO*9)RB zcrkP$+Q+$!>{x(^1&Z%>eYtrekFqNiCl7ynV{;O+Y=VoEUi+!Go3zaPp4pgpWFHTa zdpr~;%b0tc2#waprzCI*76zTB0(HOTD*zTKj4@^!&l^P7ioaQ(#o=hrOg9l^Jb6XR zx!>!y%R~7vx{;$rHNh#T4V0t8qDhqC!z5P@85VeZf8`Vfd=kTy%sUIdx5olzbHk0N zS8F7&z~ei8&|Paxd1^SV=ZX9gZ6{kB$EwPdO-8dQ30ZL*yS>Tl`oockW9|`9IEiqD zK+I@RLm3vhv6ciw9GeriHtjdQ(mC#zrOv+=IQRAbyKkWhho-so zTmDaN1*R5XA`b7pYh{cT5>eGH=FMS$BRK#?K)Syr-7~hV-+#g-(fYY%Wrrs;ez<6P z{q6H7!+j+5Q{1&D*AVkDS07QJYu!3i98!1L)&;A#=jK(tY9iQ5WEikOXcVhzsl38O z<}qqNV|HDr@%|jUTeN-mDPi_0nI~fTL<_ykQ2rgi5zvjbS#bT^)6W zA{pz~EHxT?;+Z;#SdzODKVwz}R{8_d+L0q==YgMu!S&x`J6RHbX0|sg4qGppTaPuC zc~co*V})}f)<5Ln6()G`zx5yGD|l4Qm^r)N%=|oKf$X8zMZfyM5kq8>$~h%tUViP# z%MGiyzH1j7aPiEhC=a?%yb$_8x+}WF^St_NK**q8gb+N`(<{xQ`7!>KnlW@$mm?hX zmB@}hmXn^FCZKN$N;L-dTTR!14zR3<1xC9t`1Pl`VYi?y)tzOV9`CoS#<9R^2=VCv z$Kk#Gg+R;*x|VpK5etCYx@!G#n)l-ti4usBh6O+)6T5*%rb+1w1KrTN^YyeF`1AmJ z#EV{agr4zDnxCCtFZYO6e2OIuXbQ)X5()nZakTRX-dNy}eg58y(%UjrBljlEmSr>43F7V}#X2I@ zNgrFOw%kkNNXdFgo$z>DmaL86Ld=sZ{lX*L8x0vIg)hqL{eB<|Q9{%#a?e&g)2^pI zuceI|EU!kISyr?QT-2IdS8?Cnp3*A=`E^R@E%p90z%L~UU zo}pyKF5%nUARqK>o6q*gM=r?%dQm1dt0cSP6x%i&>u>AZ#Ip$}DlcUquixeJxgW`u zVym&G#41F_pc;R{)ee$++fXirXS9vpbK_~|c-Q;TE;ZgUz1Pz!o?>?4Bx)~lZhy<7 zc^wL-zB&aT2VCwO6_)RxxPS#DUVz7tJ{VI2oxoX4Q7v>pLFYCt^g=#H9Jd2D9jusD zCzJI-@ECB`-(ZYFpuntn-jxqcd@P zHE^Q;0)w8yTn;_g*;0)u1CN8tHS6S-7SdsfbhJYy+KPuWR?2|c-Xi%M``9++2mK#M zAK1d{=sQx<=7#gh*X#o3F5S)*Rs3c|*F(_dBa>}v)?nVDKW^s>nGvkrH#^r z28p0y%fa3UQs{R33u(bqQJ3J%l|!T5rtv}#^@lzY1j0-v+MmZfYAqywVdmPl*($BU zp=~}g;J{&XFeNTc-|kt>l7;S&u-Bfa#fmd(E!0#>J^505JvUT&@D zB2+tP3c?$ln)gob{+wPj#*xg)wZda=W?))Zf%y^AL_*@%fzS@Jeup;)h#M6 zmrA%&tf^+iUMxAnlU2I(Ek*ks)DY2H7g%7nI5j@xQ25z-`81BD6E!jN3%N(I(sq~g z#_LUwpJDeKZ}v8%6ruw9Mhta2q%*@~n!K;WM*Uc3;kPmY`+e>cVR%@7hp8epG^;fYWhUqL5 z^E*Rw=hFw)k3$=_bJxhKpD7q&fh-fHw}*zcp#j&Nr+eRj5^LEJG84JITm^6$tY)sd zb~^CqHu=mHple+fR4VwPCtxwTYOE;Jl0`q0{gHA#2(O`-_AVlVPK8kT-3^ZQ2SGn` z*Fd(jOU`})tt62sTxb2EsJ`x~hFOhz@&;1@)F^|+&){-3r)*El1W~fH044^Ld z%xigrrE}u#rfZLQ!oTDO3?@7^xBk?fzR+Oz{E~204pV5T8og<>z0%x!Nh&8qMm@iwxCSqv*s{;?tLK&4&%ww&!shjd z%NV(ekm7(axVEU5(c>jyp{c+D*^gFEIw#DM`c2CT9E0c@psdJ5S0Q%h zvt{GapJ6oNO3gmU)3KcQebavlVk5LEA5;S!Lb<(ldf-B82%>!)TZ zCzpx$#!^A2CW-f-rv`-(nSYsUvv_LW9NM9)aEhkH0!?d}64glJeXFxlLqw}~FWrc0 zq!|mWNp+UT^B}LBd1v&|lOr%_GTqlwO41&EY!0L0;W#+Ik7GVi!+E;WiOY+QYn26z zCd;7#&|jJ$bN17z+Orbp85%UnrpI=m^{{GSdkYrWA-H}#M-@1G3&*=Rb>IHXr?oJ= z>vV+j#n0NY02g!KuZz@sZ*j6~oqP-j%hg6yN?HRXe=2Zw|2PILbly&l3xG)(Po()b zxo+-cs5<6Iz-w$O?@rR;ba&^pJjXD)mS4gC7xZmV<9QVU_Yo}c>XIXfe$(W5j`+xv z;~lP6epZbIo@{#1c!iY+S~RRj+%B5_#NK&u5i)&m5!^r|+jIt}-j4mc1w2lHVp5FO zNpFcftlM{*Xi3?Ugp6?MhdaC3*1ogvJbkjKyA!%bw2gPROSbRR@`5j;P#+YwwfD8I zUHol*W#IRpM6W;FWqHKrkACdaWG;61RkXJ6Obqo;04v4YGgps9s_wgn=7jNSu1=`% zd6Kbcjz(gEg#&+3?oWd24=|uQ$n97jo%H;w+AEaW(|xHyX8pQR#3+Csm0;p__?-W3 zt+NT(#*ijY=kg3=5;r|^D{mU~At%Mo3GAI@Enj=Fb49+)_P&{Q7WLZq#dSr@NqNdL zOIW1q-Ozc3FSK7eqK7SVFr^(rtx~#y#Z?z;ZZac3ytX7MQ)7xd=Q`AnF#sj?oO#p3 zs#St%C@DCR^NM}mAmC|7HlnG~tZl5v>25brG<9Ph%-0Oh!`{83-pyMV85nLF_NS9i zsllD&+H9SloFvvSqQN9W>NoQ=ZBgc=to3Jgqkidj*z?c=?dnq#Don$V&S7*7Hx_7Z zm00bZGNn2+nd%b;OIen17}s{1-ZwOfirZ4F_PnFkIe%%`iQAFkWzdY-6Z%5dADfC& zXQa3@z$4@Z#vO(Qisl`S^#Xi}!SKnXYZqPj510-d-M40d9kPH36IcM;)#knpc)A=J zPO4??4aem-i)pA$q{XkG@lW#MC;Ue=V7mK=1;#K9`Oww!=G3kxq{rsz_j6c4A(buP z3BSR^*PB)B`PLOQMHrthGDSJA}Wk z&s|O4f3x&OO5=KrB_*&cKDDOsW#4sit6MbF*`P1%lgRbvwZkm2xsQ)H@3-$4&sOm@ ztH<7WcIiH{S;9*`HQQ-BD~6h1`VJrm*p#RcH`&>@Jk|Dg82Bj3oGxPZDr|VZ?#m9H z{S~7j)RJ|Ba^@ku-1H_*y_HJ;xV5W!;7FU5;^xa z7A;e$UeqzB&dT3-MQ}8DUSMzu3lykE>XZ6@JqZH)`n{7+`z$^sEhDG~2En@+vRJeA zx5?CJSo4mWuHfltKQ~;+^8QHmS%-B>rAyqw2-u zRL?~2_@ZInd+9B)etfanCZg%aOKygYpVto@1-ZDC6OdWf?LKg?d)^-?!~{m`4Fc!B z?2y9W#Bl5|JQTQdqG;eA+4S_0JMkc*4WH3$qdXk>fn>?2n)RLT#vZYcOOe zH+=Ry9PWLuuhJx~Il$7LNe$C)&e(;=8&A+!;0l)*)ve^53o~lz8CEOkfF_~S*Z09* zeXJ4-G*i6m#no$sf^$1|U`{m+D~4bJYq25F(Q5Nfa^@pW&*llBn*c{( z7C4^&l+3O?{WhjN)R1PABVGVGjU;zRkc9&KP+MnvJH z@w1Su&1;I1LTe(Ia^5%mLc*ch=3ZzcbywqpNas=V^;O5bY3u7?gJ81Jwh5M%$#VTH zzD^ciNOy4&;h5>AM;%|b_9%;8PiYk0LfDi=`T<`$`{{ zKpe#U^C4eQLuKcr@l;A#XWjZ+ge-Huwh|V&A6Msf#FXNUt`&Z}AHI23Q>=Y=gI9al zF{t1ne_(cQm>gZ=08eK6yiL(~T;Zc2PxRV7#m;a-Z<8L6vlM;opn6j5ggd(}(VmB% zBJIU&%|79i&pNV^)69&0J6qCkE8`8SwD_kKFNW&&<}Ei*6jrOm2Awaz-?L&;#-3Ah z&E;kCJfjZW)a=VO^6`mezPQ6x@8>Zq?8g!(xwveN_jG38cjZ~|JArZYjhRnS6T2Xv zy4%K6)N|`U%&kufdZM+Erl zAsv!SDX|TefqOI8R#u~3gIB6d5u}DZ&5s4D4IfCr>er?n%D~hmZ~r~dCqLUrmCxj& zUyCX=G9?3D+qn{ta6loowfw3LlbRo8uBAsXt!D2y&{&z%YiMNii0?5NhuQXr;$_7@ zyXqPE;hEOY%-LDH!l9!h*RUt=SAsmI_oMFlw70esdyxKpSml?>C02uAMVI`I@j7w-G39-s*cT5=L3RJlfLqA^W3o zD}KDi-qhIAqWd9UO8tgw2zS^o>S0U0)spq@=c!&(M;1vf@u$ zKH3X=%)fYOx1A&hV!$q_0@?O<==`bZHa*y`6T7H(u^?CKs;8OW`(#4D(55x#UyM>t zH=M#Jt*4IT_*OrW)N*^ZZQr>it?<;P{Z%vca%hjcVZ8gNY_ZT*je0Uz;3e%F3Es?t zqI}G+4GmV}S}CmA)(V~6)gGr$>`g!E|5Sgi9n9kQo)omF)?YBxn6i#a`vE~FSxkb> zOX5xwSi|S8+0BHOoaTUC@Rd~O$+X3LiIaiwSQnFpEyMO@oo9=>$Ro&UMp&d&u1dIZ z26QUYczo%~+m^e5MzzD$x03v&CMPakb?WL~B0dD0VBN32Zr7DghjUWBPBPOi$A=mj zGSV?r38o@<51;G3ZT6z-un3DZ>}i$eNv?t#YGpM9)}>;B^uTVQ#QGrmr$W@j=CkzU ztDt(;IvB*eBgNmABB94!=ML|Ao^+ngS{&uzFRL}Zf+U)@L62vA+B*DJo0^w zyT2EmeSZ@(7Swo7MFR2Ddc2u;595<^eg7W(q#AcyL|@ywiHq?HvdK_O|EckXP)tid z^w@#sbhegbrFr5ZO|GY528qeGGb-j9!J!PiI3%(6tJuOJ^-r=r0BL=ucC7 z%N(cXDOe!48xt{c)-*kyQNRMDxD1`qbZey%Ie$a}1CI^xZ1(B|i%18R+ee`6S*gox z#TH=Yx;O9mi)8-SbK<>qTubF}y4ZnZk2o%D>^!{N1a^D0VB+5YJ*#ojgOkj#Yl_8! zGqAA@3pjzDq#{cQuFvF~Z9!weZZ7M<0ZQO?s?{(b9GW!N9>_!a%BZ`?Ed}Kql4nOK zs+b`%h&0g36xJ{uReP$^*achSyf+obKvw*0Q+j{AL{LDgbJKEV&6*|1Z-9KjS=(6f z=TOsAWJ}{Og~8pXS?|1MdTozXwxYT$h;fo>6|d{9*5 zo>-m8KYZ&Oz8*9kc-N)vsB#K9G)>BeXLccmm%5O~q~~hON6=KB+4ntP@~rG!3(p)6 zMIz}c^*rs2LJ3IPMb^;x2Yh*;hZuqxN<|o@9}YVxw%-uXImKSi^U}9_@?HWO+>-d-u&EnJ+!MCzX?Mo{rEVte3ALG@;tNXU28lTvd?| zwLJsT;zos(+3q!Cy9JT*T`GY!>;B1}@XYp$JYRyj2a#VLOrkXN*W4*2a~V5~qe!z? za|??FYQ`UGiae6NWdAU>Kk;?{E6!yjCgaegXMUfXCO4a#p&bU_c|ItGX9>6Z%AE@E zUCX)aH<=s$3KdlfC9bg(t>NxVjHlEe&5pj!T5moY7UADZWd3e=d3XDYss`c)>Bbrt zh1am+j<^qRS?@Y!narteq}KGd5AJ%G7Iw#oi~3zsV_Hl7h0(=|Gwmlc;z ziSV~rZ~+^C6nAiTk+Hx1Xi`;vjV#m&WKF!q0tF|dj}mbgB#&(x_QngbKpPlRxZ7c= z%@I+W zJL6=jn)kXl+R9w_=k(Qh%Sc;j*P1y$JsmprAbahi9jIbxP@=;t#OpSnT-@Z<_^3GA zM02Dg(BRY1*XxFl)~(yiL!x>Qnn(z#sN3~k>peL)m9fy@=;Q|Tp}7GIOOopB?2OVJ z;b|{sJ-s@8{lr8A3rwCrEn!l$GXd3*6aM=GeFlka(!tza0250x#)42W)TCwJ`N#XS zd;77N^^^u9qPZyOuXLP-x+>L{!fs*C?$#>?Jdq=|$Zyon0pJwkYUdtwoN}7^Hr_3)4HmMlf{_MSm&zzUiil} zi?W3mJ1d*zR5(mo!6OZT9=M9+MMW8my%fo-)X zvRgQpfV~t8=(w$%yb0a9B=Hw@BY}L!jQMl<^Xj*6{nC}e0($Hjq=Uu@Q4^dNV{ zw9?ky!#ij}bY)9nB5~$qJzOEwkXwFkz9djEz{X_b4aS1<9n*Lou*cWQ{2XU&=wKjK zXJj|%LHAEfNDsuSXV`mFmuzsltu&sRZY*6$6UOYiLdzo*?XH76zk8wLJ?`Dj?WyS# z%5-+jVFkxn7lx?mC}mF~T<*-6(a80k>xWbq9zzdBpkv+8sHu~#HLZg&+@?WS6-Vx* z)oPtBSfz?v5Nu;+X1~q{HM*?ll9?OSX%Rf=Y?HzL&kMWjEvmPmY;8vydzHr1H3(p0 z@re3$?H^O~&d?GoK3>B1c>uxE{_NDU|V1WllIFgp*j4E^QW1(y3)Fh?@b zv4Kv)#xpq;g7#EIydHccg{4O;p4jPRG zex`GrzIngvv)Q#CzHr8N|2>#3cOy)bSf!~?pZqbk6qYofGJCQgYF&!HLX8E~{|nDC z+!j3om%*CjC!N4wJaM7Sf>T=A+-v||N{72Lp>w%g8pD_!>M5ekn8x#-9B~Oh!Q(A6 zDkoNJ&&VBG!KN|e=qidOouwxXfVymPsHBM$ujad$eJ8okqCpp9%NV}H-#pYq>>X`A z)wi!OE439J9kKIxTfApM?w>i=sL^yM-yy0s8E_E%6q2k=TC2sUT(93Y0M_; zo_ldMb4mJKzR=6En39qRI>dd&U>0Eu1AFxc-1v8jROw97Z#0B#?=(Dh@k~$GKdhBP zGAHJEY#D~bs!7(%?mpv-CmDVG*uMx+J;>&d)MLPZ=}_s*SRRteYVD+bdn<%CZW}Mt zi8U=~w<8`eokdsA^umYGngl|@9?i%*ovJM<#XN%vIV^GzH)(_XZaU`Tr67iTHLL{w zY|(8OC9}0xRmlXKqMoEzdJvb~a6io4QX*h6hv)t>K&D4CPt{ zGyIQZ1J+b`UzmlI@kwXZEQxapi4gJ$w3VTSDq70ZFyj*f_$K;^|#G2i2Gly^A z%JqC^U_g{|Ik)6n+wAjEMJ({L*X&i?yENklf$~0&9Ul`ZE|WS#WKDhuA{EpHpydFY7-&1D3svR1;)Eg&cF4MOg;sU9R3Gicq-in5 z@Y#Fb)tXG@@|_ot1~N1=qdfN<&9oBV52Z>3FfITuVK0^MB03 zGDI3Ky!cSfsO)oBQ2fP6sGD!=4in>@+^{~Ete#OsvGy+dXJ{?D8zrE#p|W`ObYt?< z(m_okG_UxwT{QYNShY1NXxR|Gu#lcM6%4Ku(Yl;T(pTW=xcEiNExS1+SUNGEb53kJ zt*9Y?+h%mss97o9#(0!|ynVulanNDzu8-MW#d5K{(YLmbPnPRv>Rh$3#S@F62} zJp_I_*JlnW-7Z^buGD<|u#E?>+t};bDhQ8}MOte-a4`62SRUy;#=Ou`V%m)BqFUXN&lN(~`A~yAf zE`NedmyufAj!=;*YTB*k+xh+MjPIJ?uc-0cmAXYZ*%s{PYmS?n3@VrnmqRP?Ep=MV zzj^dn?iG@enu>awhiJQ${oD$l34P;WU=z9{uWR_=lB*Mk4=ZorPk|O^wS7@e_ADN! zB>#<{f{FgqS%=OxTzxOoe~=>b?_Kyz2lR0pz}qr832(@6&6n zWLD4^$u?`5S~-+5$9%gRE%-S|4Ln8Na@ihu00>QX(sRvYT@9s;4P?Z+hZ zic~e?Vk{t)uY(a)R*1Or_H@4KS+%`~1?+*s!>^gnlg)Cc7hS8zY08XWrZARuUv|^C zweA$+6f!V5OL7F9^p)Lap$a_V9>~Uye2q`3`WTceJF~{@{q_M8@ zC|}{s%krW<;n-Sw`N(;avz#;72r?my1>)Hao@R>SQjV$Gc5X}LshZdt_eQY#@}i0z)kly6S~EI8SvJ9SiuPp#nB zy<+{0=jU@`1E?mSf@fPJ5yvU#F8N70V&AOgv`9%!NGmbT<-sRPjZn0$wKM|0}I5on|Uy2toTK^eT_x`^6;Q(n{!8 ze9F_n^5(sp`fJxskJ2)k<|lI<>|9KPvrBjJ=Ph5Kr+(mzU+-*I&5c@m`H?(eYSF2A zlGnDNL{O)-QBUh7xkaQgp;&;me|6x6cN_IqQn&iZHFUSkeqPVMeb>5VSInN}0-1l0 zZd0Pq`zPW(wXk>5+?G7zEaFYAw0Mb^Qi>hRmE)WXh4&n{6eUNCP_{0~5hY0<`F<2{ z54EoKq?B}}NlTm6!%IHJo~JJQXc4PFy6R~dOTYKY&@1=al$q7jy^~%U;=722$NsAj zaom?mXFy~r7OLiU-O(B^D54_R5K-R7R58FdNL!Soc%)oWex?N zDMJN2H?Km)AQljG34f|``L%SC#~eox$5oSS5#SoMc5+PJePUK498#0z_`3^LPJ*XC zBEmH@=5kLsNx>ps@4F&eRG;Tw=GB)bb>??jwpWE^h<`e{$|o#6v@Ar0T;przijTQU zs>aAzL}t^#-V-m`X2o)+e&@cilSFw+v%;nCtt=O#v4CR2=gKdy568$%oHlyyoDVLO zXLH`j6)2ni~w?Q7%?JxH;)>Io)2c zZ}noexHhP9Bc@&i^~M9%Q6dLBUDMe@wZ@L)IcUddHQP(&x%d`?P{iMZQ)!g^B`j|*kD6Aj?3y6^Wm#DcF zd)!-3Ec`S!I^MExh&HcjspM1EsPy?-m!SJzo8E^7h> z8OcT8@v)=2Xw$TdV)7Dd+^IX_Nm*R|AWHDH=LwoTNV^Aoy2^~#KN(sbYII{HF0kUM z_|_{#C!Sxwbb;e|!!d6~@^z=7_w}r#jTm%4N=fm^%xYbQtV86QdCV7!?Wf^Ij=IVc zK>NY$M}a&KQac#3DDRM&v${LnD*DhYR?U#(*||=(=SN?^Yhc9IKi?^uEbw3}VTEA< ze9}`E>IjF^FYVx@-j&8_It>&(rTc+@T{mK5I5O5APR(D`zt|phF3&@4T1eOa*2>St z9)AWhxt+VpRdTx4J%$j=mB~mCd|}>Wew!hP!;G>4H$R@pqB|tQ*rR@%^prNjK|@&Z z0yQa{dKE?P82+*u!J~N;>`=LAE!SVyC`6GlH!niJvbR~&j znvSJlu(|jZ6?3^yu2S%F!}_jIy0q+Vl421H9cPEzKP=;CO~28xO}qB{OD?IDql9(^ z#TumwBFM&z?IjDNSNhbV1xmX!xV`k>^`!}&Ol8wds9ER;n`c7S@F{p<$ClB4qa($- zHU~mYvo2Z*-d6o803tj6@m*mVP8;zxy?0-mbDFLwUOFE@@8e_HnPi@dkb{;|Ivkb` zH5WZTHSp&x$?Og=Z@d&5Y8bBx}G5UFJ@Y-C%)%p*p~8 z3ec|&+8>hiN{KPs%Hu)FCl>y87_f*FDO=S;cNzV^hO z=N^4Cz$|2b3g<&UEmj}WC%~iDRMBNQ$9G&+CFHyGQh}7*d~zx`WR&EsWF@5@+a#q& zPo83ouJgnk;@OwAXG-P#vX;ddrZ~DsuLB+5+#6E<6&MwX+FnZ3yHEJ^@J=3+!!dy$ z;}Xlwb17Zvi+;=9)>RFU2Uoj0g7uH8jHY?eaPk$M1)d1?Vqwj=J|s>Eb5LAJ}x~&>559(aI_E5E1m7r|}6pxBl`VfJgJ1 zHdV-0>~0UjoLxjbKfbEvk_l{iMYEvm>k^}TsqVTdSCgqtdEXuXB6_WuA_k<{1HtUz zRA;%qiy6-j=HyRmwxgPI-wt3x*2~2&)xQqD%=M9`r;3j^+p=J4#jK3=2wyrR*K%9F ztWQj=CtpbIV)=B0r-yrH@Z%?|1Ed?BgRWfzEaKDxhnHqTm(sbW3k$pvc(lU#K59(#dT`{2}#XNUN;ESbBFO1M?+&}ZGBK=*4St!-BY z!4oyFHxehURaG3ek%8A$kLp^4f2~5Z!p?7QK+&iziK1Ky2Go)o&&HRz6^4^xAA&}? zCy&cUV+(B#dPsvWz4L((Zr_F&87S?N5&wkEnd?*&aE?)weSD{X=(8y#NNLldbN$Dw zLoCph8tgu*anGZAFM($=a{J?xCaK{F1}0KAV+MyOL;9K(nxwe$Bla8JxMRiR)tvAd zzGa@^TO2}-wAvg)kBIYm$wvEhACnnWYiC4kf2d@G*P4(JH_DdK3EK5BdGpCW9u|r@ z?(H;XH4IrZ7Wr&!p1W?tAP{@A^?6SlW(u{NjMq8&HsP)0y`ZZ5(gtLv)z>3Jsl?=` zQT;CiuBlX>;_p7aZGV#YzO9ghHje(Zu)ipT_&5xa@lvfwp=q`8VQJH}+iiJr*kxp% zE1rma}qb7(LWTl15`saKd2G9E1@G9eNBVSOW41z$z#y?ZQ$GP=pac zAorf!B2uj=H6#N{)4L7a(Nwf;h!vod zTq7w;hoBUKDPG>(Bh4xb4@PIOc3?VDx|g)jb*Hle01MSj9rd&ocWB+Mhz92!06xGO z$Uy%9ga#^`p9kkRsiReM+nLUT(EbV{+gp-G+MK1YQlLI`%ZO?yMpXl`Lli7NT1zQ< zUBIh;Ul`k{wByNGnD4VPGg9L?9%1x_HnEr>isM(dow75ib6H%lIyV-(N-fA02ee^i zm|oLg5FI;k6bs7+rkzC(w;t&W%T*?{r~9jcLa?UmUltA1qVOw11!-E>1qc?3Ku>rh zt~|WbP>4tbto$Q0mK70di1cm7U1T7sNhx-RN>y6rQBjAU@<=%s_T$UQH8)OzfZ+!<7&&`N zyD3}}i9qG49h9vwLP;p~S}8rWB==VUUBP;*LJ1?mN_J!3SW2^k$)U*h_F*J-rkd7* zgmza4p+LM-;Tcf&;Gk%L6~2*5486GV2+L8aCWFP^FaVSbTA$$kI4NE&s`V7F-KVz# zg=DryWZ-&NfLfH0ef=@w7pkyPTGV_v8X!R_H6)L#1Oa#i7hc>Hwy92%po#YBwy7(P zEwb9sXcyR_-Z7V4*~t^AM+K%@-bXuX)M^dcaS9T%=CV#;^+@(%YkTJdM2*FpUOg$) z)2+Uc<0|tItn$T)uW1@6DPk-%C~8-?3rDI!qKH9|9lh98FI5g3CWM~7m@sPbdKQW9 z!FqxvQMj(5I^#+;j_IlwT-#CY9?twY1RWQpZ!oa79FDa#=%MP9*hgUp1oD$`>aPmt zfsJ^hNlkr}?Zb%EXB1^3fMrvKlX8YiDVgmVj2Hw&GExt=m{-e1B?tl3H40bZ&X`}M z(IQGY88imHaO4D=($g(Aq~+B0t|l$VyqHW<>=dDg8gXGNLQZHP0C6#Hz!XH}(uqwe zC|H)j4Jnm<$r*$aGNJFon9~~aSd!7yRJDi8;;*cbSFGomt`(dFXq<~BC|hqTLyB8T zN8KeP3MhLio*1}PCF?_?DpHjpEve)3$~AES48D_uoD%V!eJG2FX)8WT%ZNV;h@r%E zp*;mAt~d)OlqxmVpX$OiN(U`UX;M60Gr=7l5zCNgKv4rT+wa1S1e8!p!^%nlapfhZ z#%1LYkPSV!PUXyxR}{2tbIlr>7fs)5h2e_fn;>KooSO&Qqt_5ud!7~wH&U!NdvaMd zc7aCsjmL1g*Ux-1&toa*1~c5oZduN%-;cz_2LY-<`FRo$mr%mX1kgxQNJ%5OoI$eH z{7BJD`K=mPRVwU#ySL{dQl+?NTo^xl3C$t2XHcL!LB-o`<89~Ck=L(wJiLU*mzEE()7y_PEs|(KISdFuw@@k- z8IJ6Dp;LDla*^ArV{C3`RV!_~;D2wG}QM{XwXFug4fX{c2UHuPlg=#&`UUTP~1npB)d7ORyDL&u zuH*u>7g42!9>mnA}kwe|RXbnv@@eq&?Xki%!?L#g_XDl_|!A z7F&6@oLYjxH4T$NPSb`t2^h$TtyHexifJyx$RGs@c?$^?Y|&kmIIFqUT58!WJgTHH zwGq^Tf&>*t6CO;#S#HE~TKM=V<_|O>?HKt4sm{ zl;2_p%({ru9#lYU9o4h~)RX)`5&^;7YMO#d!9+wa#OsS4pLc*pqTKwLsU^@m$}#F8 zp+Y1?>{E(K>*1yWIO#@En3U91;u1S_gO61d#!m!sOj~u)xvne`@RsO*PCSeOTl|X! zAtF%I{UKk`fU5G@Ht6*flq=bhz^_pVn`;Rf$gh8dwNo(n^#3J`T^X23|&mt_$tzP2?eFzjCJ5GWUT@9D=rv28jFs4ao;q=WkKKs8Pt^l(II_=@r! zQ=tCL8i^k@YPc=XmKGIWxpc#tBxR8b-M+T;@ zDiZzo@Yt|+qflfwCgV05-`p*fzWcwo7TA>{L}*eaKzbZm@on!X%XECqVDWCGAKoNk&&{|6 zM1I_br(G9)_WuCol@oLA%MIgg^E21Cua^64*U;-0Xl*j&_|3nh7p0{|C@Lu=qq7;D zyZY^pAjhJ##oOCwf2sO=7HuDuhTy+jXI-}EFf)CR8QF28!;Nc7l(~@JTMn`387Xg4 zRM{2yPhrBX?hMC^5hv}mXM@6ETR$#4#p`x*PQibb8`}HNjVzWc?b5|I)K;fqU)-3> z3PPHYDW%9RB|@2H--nU!*(qL)6B&Db6Lb;^qBd{KI`4f;5r;in;>(u8q@q9I<;R(6 zy)_QDtAj~Osz@NXoirrmFf#A=G=D6ngSUN7ppXxv{8eY?zek&&)6Mf^Mv=+ZEcrLaG&zG{bY< zC>bGZqi`Vj{{VuJ{{Uq#Ul@O!+8;;%0EtID(fNPR;GQMj+`D_{?Y+7nY~c&%tQN~P z<*5&@bhhEPhF+SqG)|`~NT{Ii#D+7tjkvh-#AUVSPxvMoUeil!Ze{bD>T(1B0K|U9 z8~SSWy1!J=A5R){HqM8<8-J1l7htc&g$)zWPl$IX~n(0BtqkPFQa~Xnd42V38|7T=H({$HN^bnH%xchS|Y@B zwp(!}2U!_|g<;<@y@0kiA5!(_Y$l1+e2r=J>H5;1?(}7JCETt`FP2&It5#yGjWLVYJ_Zlj1u`5M+9WYg_a zzR>)uxwDZI22U1q7Z-AEs_v8D&3p5Gl5 z&9`9kHv{-mvBQ?M{l9AG??+R+fqzZTCY# zK|$nDke<2IJZLzq}69+1<0^ z?VZDDaShyTlVwL@ODl1aAs%8JT6Bdru(Ye8Ndy`Zi8!(`k;O(0uYFw_j^23dcG#F| zRr1VitjKON(9Fp&qb=qpwdcom2zBtX+o?${R7ydqAYn;!7SYqKz*Qo4rbO4K zHyU^f{fU~>QT))zmdSARN1V|loaiX5JNcG)sQ&=KZocVKwWoo3w)nIv(Zn03;+YMH zqqyGRx`y3hMu~9?afGEvH3TUnics_;5zgEetu*T%-`JLqY-D6QMDJxjvp}3r`eF4X z!;HpX0B3ZqLde^j-RTlV*1V^RyWZ!?ivWU^8ZJWe(ys`VJ^P{9GN3a8)9J6&;I8;dAYD8^3q0`BD{{Z1A(>|SjK*@2| znBcA8Uo>Xh)I-ZSw!b)sz9uemq ztsOFV9}DjL8wwx^5;oP{Eu82)XkkeN_Yw{jk8oK#{YL#JpK49pUnJl3pY$}JvZq^+*GuH57US2zqfXQ?XRpPor$^)>M^(v`AQSx+I`T9<5YJekOHs;nV; z0?TSDSs@K*R;5#@2?D3+#a)hTM-{moj7Nc1ABN(kTYl#`!W)kCga+ItG&DgO6PH}E zYsFB6q+3JKFl$D z1N9oKu0JFi%5zO8VJRw7G*Q}A0+K2_^TP=t3d3)D$7x+q0x&%=)`{?!M89LOv30g? zyNlbh(YBx4Q5%(RvfSjn#Jk$o_mZgACCSB4hmwXN!5|owk`XeP4G}~$inS?P>O8sLB5)gSV@k9!3$yGxRY3&|h{W@@$n3N24c%l*uI;hG@QkwNpB#)y4 z*Ht`gXtcH_YfnEiJ89B{Eele>B8n6}dwtk7P!=r3MA~a?RCcPOnfq{JQ)f1%Va3Z! zicwiYhr&{a1}-BvWA0al48NigO-gC0q?HktSdWoU#!*7ZKld<- zIsmkT*B)M5Xdc{oc?6J2%n^Yh6|SnmSC^ShPw?Oo)`^l6S~FVz07jpu9$uhYnWI0r zr8tB`7M?u)&YFXgs`zBU;g zISwURSaE$ZaiUW$QpN+R-OQ~)OX=n(JF!|2i|aln^fYcH6R4_@qU~mD`*gxAoTaQPZvfmEFp$eTPNZte?f zRYIxwq;NVYt+-Cc)ZkqXw`c{v&bD_h^chH-gBXugrC-CX(CeD72SV5X*()DJ_1 z$q4NTAdyf`bnL+-URtdxsZ}U2lJHvK2uiIAYh3!_K7x%^?Ku!nQ`ZI!p-RTIC_euH zZVg1D&YXZyq3rhHIzT`TP(TyepLP@S(W;t$jFC6kYj0vGbQn95c zvjzYZRic5+jO*74)e6x`)7l8{$GTULdIF74XJ!n>)fhgBp1BaNkx_5GHfoyr69CM8 zJ-Dp8M#$cm@lygvp2-^|6e-^d?H*uCRy?PFg!CubiTiwm98@Iu_b}9OO0-vY zL!rd03S#ak%V$zYY3;&|G$|(`Qjkdpv$Gy`D2NK&q$pFf0jg9zD#3NRi2OR>tqK{! zncRPb`!MP%IMAjyEiLz&i#)@TBrY_B7M?*PN$tVR1*Ik(QBa0Sr3Rk8MKD&BRBb)iJ2 zi50`3@{n>_X|YDg8K_l29+;Z9WlD{a$z`;JAZt&=D^AqsOi08iqa@;&nN*0--iF(t z#-3YoI$dsy0HsQ7^d`B2FwOcD`KG8T(v>O49`aDa za`NN0k`RI11`&`{rxECq90U@gl1Eh%XaK`U0M04P2|5JbpMSF3f`hWd?h4hGyhgo7 zeK8MpoVSaeN|?06PbuL*nD-t+d&Y}oB*zr+yyX zce!02DRJjzIKTBNs=mbHttHjX!P2Ezp|OS*@Buh2TMu$wsN;-_baiLuJZPd1Xh_73 zme{zLS7;L(&BtvHfYkaFi)-VHEx$|CbzLMTl`F{r{{V}NRi4>`AycVU8|~9Pnls%A z({r~)xLTKMEryvutYjr11S|}?Vn+KI$s?c%rZ*<`Nm6uBlM*HE>!@@iL$Q^m_DH6r z_JjJdnKD<|+*Mz$B4yMx@l)&xtXyI-Hadctd;*;M9?*T**X(Z_JbgiB_qL9O<)tYm zg!IUC7?e?nPM{wAAIsGgoe;@Q2`A{n;H1c^+ekI`;kar@lqQ7!;P?LkxZrwFRc)Yq z@I+8b-jy^MFGxoyP=QRwcsK|aGzyX3+mBHduB!1s280vbMm>WD&-I5;+?O#T~v*@`J^dI%)(NQDgOW_e#~4jnWD#Z zKTuYGySB%i46zD4eQIXiTQ;ivLf8Y1xSeQq1QabNFhLz_i*(SoPk1c%ACz=X=#xU9 zoFU25b}jLiF2KKQbd(Z$v8ZW2-6sz5nG*}TUBOp(p4x5WJjnELMfp#F-oNiiGxixw zn+ErEx|x64A|uXt)Y^(oKqoZ)SZkHQ8KmlGG^W|#t+uu;pWf$@P zt*J$>+vKg}bzB8eS#uL|Luo~+wk|dM z$t;49>OlSE^slsGl+Nl2<(6hLZzfvP#Q{lQy!2w^FZLyz$2hI zu8UO%Wpcu!f;pnfQs^0|*WPjJ735(ldX)1DK3Zzq@3C%W?D;-+d!a@3WJ)B6H4VwE|POCjzm*mIdun|YTTP+C@nAf$x?DhN4XzFIQcb5G8nGUFy{=S!aA-kCMkl{o9J zD6MN*RY_4Mq?~)pUPKjdc|%rS`r&FO%XGtXqf!9wHO~N?A(IK*jzhTDJQyhFxK%lv zvCV0q@~*M;sUblqdszU}tuqG-F}kMGIH@ac@J}~x+bnI#ws^O3?rpg+xSMVM6`E|A zatkXcUzGE0lrKtw288FATnv-O!$LqRBIhB{X+oXQ{WWv!mO&gjxT6(iM{JXtB>R@o zI3Jjc?+WY3DG|aoE`;cf{5~JeYjX&K!125w>&9Y zA7M+(dROK*Z3>cj-uE8mrNy;qRjy)OT2V^S7Qv~mLYPsvcGUQSL-FEYO;cfhoO!vo zT50|%y)otx3yQW&2}mZDC(GzC0_qREJ*j(*$k@j+mC@;Im_(4l*s~U;s3o-+?}V%< zR1sA4I6t2c-UH$Ow^#-_BIFTJS5Jt}FBmNU`KQ)cr)zG9dMBg&3#dXxa*xfBetTrDMv?IRFvibN$Lm~^72z+RJFQSDfb^L(h8Kd zsX!;7s*gd~G31luX-aT+4Cs;!IQy1V0<~)ScJ6XnQkU2?q2W(vSZwGcarj5EM@bVy zsnkLBpW3Bc9{1kXC_jbT_qG1g%G@qF6^rH7mbJN=P(xA@fKPZC;2CdkT^c=sMNavM z(Z-&$`%{74)5uKBm3~(I+#k#{nM)HdYrfsg$CzxDA*UWPHL0|cNI^6N)|hkhu%F!? zz?-&79%xDSpW2MPPNZlFTuQA+KVxjDGEws%W^oXD9koc7C}>3@ z?CoQ0`%sS9BFuocNUbrqvs+v3sTq% z9|?Lhjv*>JB#=lT5NGmsExCZke|RNqqVk*^-e2(h549-5az5_dTbgaI=`(Nb9mRUQ zUMI6P;VI}a?sr$EDRMlrlse)A;VV$ct1;IGg4{s>U>pLExNGH{x!lj|{fns{uA7Hv z{I-88XRMMJaJ5NR=9y=8)Py*=l4H8!TqzX^J|qGLLbS$xv?Ob?_XLf3`$E2MsEl~a zc2|Wz@D%`hB7NDuqP;G?Y0(DlHI0@nXe7##*p6P;`&1RU>&Lq_}&XsU-cReVaM@$Rkorz$UG0aW<_bM z&CiE8)g%NYrNoG{z*zuCrvpWkAK><@4#}0?Yn%R4`j;BE ze8(eLET1k@<(QFll&3~UIsnGF*0-flE@C2Q9(60L`{UIX>D~VTmG`F|l#SIo2KV9q zUferWON%N*SrRtl_MDsSR64d6g(b4s|%^OG}v=@;Zpr^YpHZlG+zW4?r~ZB%Ri~cIDyTW?FWQ zwTm_PYwpXg)3>VE1xaSy*)eVIHlWd}R)*H%KtIAp4~m`Q^{p->SH%VGj7Guk=o~y3 z&;I}#xb)x4XV>-Bm~olB*qqw0ZSF|wKolB{a>mEG-q`Zc zwXP#h72~hy7d~v51Mzam_JZ2`Gza`u^yWh{%fw%o4&d6RHr94suE{fUKMvb%X%}+q zR>0z90UtE4Qpp0J5j3q1DfV-k&I^h9Ro3Fx0{FUE0)JfHg|mM%E1W9^)wM@n@m-G9 zE6lV(mGbR5Y*e+n&~XDw)}Sh&ML^E9!&#&!8LN-A4ZWiVP({T36W z*jB5{7A9_Ox3>gF8;Vs1%M6!%vXYRa;wvC#ad^dFJ-CJnk@^)t+*VNx+=Px4`vSh6 zop?)#?|cm#c8(dbZ{6W@THBvKeY!KPCHH1J9B{T0H3aIQ10Vnxp=GnTaoh&=5Vb58 z0?tS}x&c%={{Y1NUBuq4xfP4A7siomnQL$G{g(n82#*o@jkZhi*?B8SN=g)R$W-G) zZbo_RMIN%Z*xOp{$+=?ftJU^CjqMOG!hC zE)_1IxD=#{ngdGZjSk6<@`qlfK-#{Qrv(?e6}3?%Akz+vVM_Uk5XnfP%{|7LA&C{T zQ3Sa9w3N0DI#Vnp0#&kwl)_B{r~&$Luat}C4+Xc%9_%E&*0wR0o`4$Q7rfP=Wp$+m zMhw=0%@3Nh<*8p1dUVSGlt$4NYRk(B`Y7+dK&sZ<4kYI6fKga}0! zOz~PR#uG#~qMbQVD=)JK^AmvDp;%00xv9~qp<1MqQgDq^krh&^q*;u0G^=t_?FA$y zYt`4Uw7;IfwOBxgNz z?E9?$08R^4=R)GKODIiOIof~8e}^6=7s!!np|@2{PDod|DHuq-z6b**K&`t1Nzood zp#JeG7)j1*cIc&|2(HK}qSWJ_(&a1lW6qX76h;VV9sp{Y9FG~ zzRU#>d4+3E{QJ%UsJ%sMw2db!`*3pI5Oov{CxGi+5^;WtDKqMMduh0w;jZ-(36-t4GH=&s~j}09Zl3MaPAmb{+H-e zDncHL>1I<3W#p0zYD$oyPhznXGaSQ06!aGn$STuR7ns_QeL7&hK{zCpqhTvOryd9v zN|Trk&~~c=ytJUopKcWL*Q&5;2t9+Y4A#*ihezHeN+V95i8xuUswvR~ih)8jlhh8F z^IKFU9ZeFXtpNF{=)8y-cN{9#(GeF4NFgd#qG~~>ZVZh@s=zF-Nvp*`DVlUi?aK(b z01~Vyv21E6LWoLFUi>H0m20SjI&%diX*pLSaNYb;fi_1$k>j-H$IO_T#d- z5uoH$>W1=t^8LR?BX6;-eyvY!GK6UK(p+a7SVM>{EOrq?mLN@=n5SEE z91@&6l$f+biu2m;?HG`@y>oOawD!$^4HKRu$kIZ5mozW63hRk$Yo@tE&1&}XN=Qve z1k{e)K4}S~oI)@VGp=}bMd0e7v=dR&`dCAtp(jM*lRb;12y9InE|89vr;qilQlTpL zq>PRvB$PR!Fc6r|oC+d@jH}nP254R=U=CzDALPqo)sgNlhdGeJCQQD5aqz!BSKWI`?DVT@YxCscq;Ua>@z? zRH8slPeWWKE7~}eqhoP81k$0$(Qb!BPwkwc%=XY?nR~sy;8v?Wpm@Bd4byd9t^lG$#g@;)&^l9}u{ft)X6|P^ zO0?NwV2gl2+pHUc@_)KRSZlTAf`n)7Rus0u+}wDjZnCz}aU!ZiwsCW5@}0cSRFa&` zcCt^ntFA4!J2VYmRaW3Y+ER-k>U6>LzKpcPwAoSJWfDLFvRKwpkAt`}$j^3x36q1lWBzOH7 zrtVxwb2{3syLT(EONwjkeZlx_@53dkkmet`S?Gs5I``rqYh}zbbUT4k`->PRkFC5y zodql0Ph4u$2ZuG7m9~8kW)&PpJ2jr1PgJiI*MU5D>wz^ zxj`A%rUsN=su&z99ijz{5qWHCGB^@|0JXp&TVcpRy^m3OY+EE6_TXA*SGe(32Z=dB zyD4vJ?$>*;pC3q?Ci$p^?QabH_8} zsnjiBx39Eix40_|_}? z#C63lBbaoijaOW55wYs+N;|VQ9ou!diAzC^YDooB6ewKwCcmc(Vs`YiyQ$m~d2Hl% z!h=^U9S3U*bY#PX(pY7%JRo)UoM>?48xv?127Hl_)bU2uXMF`V1o!2IooS+P^8p5J z$_oin4tapUtWU>Hr%Ae+X3Q@qSLHZEhrJ} zI6upjf1G09@R!rkI&195yn?;+tO!Kg9!aeUKI|pXSH5+KcCG5t3X-blQ%rcYplt1` zG~5>e(h_KW*Z|U$`Ie!+LGI)A58H!ul%Ja46gzh1k>l0w2`BVmI!Xsl30wXgv6@n% z53u9Kr3ac2>4NnsRUh(5AEN@%s_~I|F(g+k{+ueAFBi9IeX^VgBE+Jr64)&q7{Pq zZb^LDLn6&}&rAm?+fY_QxtlW2`&~!`uDetp3#9(bv)JTPN4I=0^?k`&`Sp zi_54{TpMD6_k~2E!ovk^&A)H$O-wc7ey0A!1fO4@nADdWa$hiC)Zivqh@=oyNeqY`p|1L6(Y<9k$R4PynbI;KJFlwvFJX?1XWSk-;!iZha~QgO_bTW)kv~ z<0#~`Tm|L^qI&^9w;uA4wcwz>QQ|-=P#xWv_mqld2*=63-bi^#AJG;4I8unguUvTo6x${Cq@SQ*!G$R!f(FuuuZy(c&J9UAFMxmG*RZV5L1n{TnXyxB~B%@!Gbn6Ab`+jq%B8q}0F zRN74j6E&p zt-bpzaV5R!5*jU#H#2OuiA-D3T`}7!6iNo8+4kWES^{)YZo$Im^&To7V*dMYy&*Q{ zeAx?Il3hMwE;#bbjwq6&Ql%)8d(It*IRyRijwx*$m;egwkkwD!Qe9GgnQIs^fRRe2 zTFNO?b^4~|?fJ0y>xy^G+im-d_=YHl##>C=V=#+Mbf?OrZ#0nN&YuldqG^cRd=wV) z7c)ZcDD%lApa<$3!8iEdpD)};ydrPSm0-6sxgoJCD-tCo`AmGg=KQDITPkrWQdW^d z4js>pY`l0PHRqDBTT}BN=Pi>aTboN}Y)d>#fGD|Hk+#y9GEhxu>nMMgOsZPcw-C!Kae5Fn5QNl( zjD}d0iU83>ub5GbFCbjD$lO;}o%;$4uKijK4the|twu@(A23U z5^z?)@+eUzw*Xd^^eM;G&#c|c>b*NQR{i4p4%WSF8+$g5zj(60HMJX5WUVbneW?j4 zUV^U*!30vB3B+DE1lwwc9UN+wy|{hL!=KC3{t1EUr_{FW^s~zGxrbxz$a{|c4#~JI zk*#+JpK33axNXMT>#9^?uTx1n(g#u~a7^~u83;sgbpRmy6Rb{Q)^hcMQb8WtAJnUb zs@}vXabmKPmSQ0dvV%fGTR=dmpsqs}1~h=8C4EgwajkM5i5}XyN;cT9xhgyiytK-7 zq%yZa{xu=Aq@LkS8>Cje*Q1+Cazxy3M^#px*cInGsFb+O$nyN?>2x%ppcIg*D0CxO zIgBX4iy#D0qwURohuWCZl!;6brrrjNN|I^_KKw0Gne=<5PdfwEPciXU%;w#PvTkr7 zZJ$wgBe!{8Gxp|WyDr)H;09bOL1EGJE_lE{7eZ)6Mo$56c`GE45@R(Uw6&&6Xd!%U zIy_MRpGi<=*xehaVqDRB*!zM;*1QPvp?)VApgZz&<1QqYzVRv=U1`N3S#&3+B;>Yi zf`n+J?TL)kaZYY9SS$35@V5)urpL=4_4BxAs%@uDL}20JI(I`GrILLx#BGb1?`Ciqs3FR+Xl- z@chfv9wKR;c+MT#8%|C?r_!(S_d?{2T0W2XFF^eN0KH4Y5+S$2I|uN|mi1a!Y}0cM zH*YCeOHGik2uW~|DNfFqD|<(4)EpdY4S!8gP2BspM(F4|TopIl-v`;Lf6MTh4m zD%MOWZ^KKJrm&?U^IG=~>?qFLz-@ero|^IIirw9EFMeQ(x|IDiQH`hbAKXxVLh?_j zZNtOX{mF9Wb}kI=EuzKBL>SS{!ZUG{xbi%gRuqJ`0IGAT_F`D=DA{bt7<6wn33ro{ zd$-Q+F8=@z*sebcetVaG6ys}08Cbn2Lb}^5zG_&xNZpctVbnVE-uuE}8jx0Vt|}Q$ z)UxJYvmqZsw_-bmbz>Kn#sWPj{0eV!ALlu0^25Xo~~tJt_w&oPFKLp62wsz-#*xOKtvbR!avoS$U9pbbS)7 zw)qz6wq~!*DH5BL)oLqYBT($9#EhFQ@0IdVs2YoZb?XQ%S}q#c9id@*N3+IdhxtmKTn}FxW0kh2kGrx-sQ{h z7H8 zzo*tzTrJ~26@JV)`Er!`wMKrj| zx;{zWvbb)`y4!w5&zLsG!bE94N0D`UX?f%huQrIxF0J+gNIuLH<xI5_#^N`>{JAaK5|;&h^=n9X%N-AfwZnsI8Y+-Gf@0eAr`_Mv*4VHM zg6H#vg>mig1{X0fuB@=?l#ryBoRHE|u&U%i@55?Be|RRtb6giwppVnmiOJxdpA&9b zH&w@(Y&P3Id4CkScmy*!Yo|1a>!9Rmvh$RN(n3L|E1y8GmQeC@;Hl5b@ zZQH|>Z1$PAGDK;Umh5P`BVUcj6XB@oQJxFsu%0PK?q>#d5u#m#>8jszxx};DxyNK& zE>nur7CU!>Lezp&p(M4M>-(a(G>l_-9w^6jI-CaDMk<*l!An8T(2U5aiZw5FSDntin`Uby`$= zjWCTGIiron%4!PPjsxX+&?DY0+eOQrzE~=cA(=3iRufw3KzCMDH59-CJW?&(u~j$j z{7cBTWk7E4vaT={b5KpNI~hq5RC)%FMc1;21aqoW!MI?hN#KIG@h>FXqb^kagLaO~ zP9djOVLo)a;M9Z=kWQrzf|bI+gsV3_tHnW4bT%;+WH77tvw$kol&OQfP=ggQOFfIaA&++KEO=9BEYRbCNj(rG^~b zCD`mI%51q76RJyoUzem6GnD}A*9rMGMr;5Y)iKSw86^1`91HEpDbr^((FC{<^HK=T?T( zB|qvyiR=JiO{!R3pq#b36XI{^p#4}Yp(x_YQ9V|Mj$*hk7J zqI#s&GzhQwaIZoUA_$}!4#C+>0{2=BHhK|3-+%xw7od$PpG+Al30|Nn3G4=jVY~uj3(w398hO5O4AA&FEzA5=skx5jIfl{ z(B*(26dr1kH@4P_U)mbxpMSJ{UEpz5hbB}8IeSE5R3FU69Q zhgvy;FxKkXvaq$>)9YKe#Ok$G{o}M{ZMk}AT(-zR++}8cx=*xY72Fty+CHO)C9q+# z%(!N}Q}>K{B2}&CEO#lcMk-Jio1k!{p;e$ly@d3|muK$@BO*7SQm5}7nH9h?unO}E z*Q0xhscs(Zw6VD3rzJk5hUBF^;nZi1shJq&Z6p;~Z2M|Roz(-&4~HZR`x|BMFCNm} zKZDM5Kf$h)LP_pJ=%^>&FpKv6yyCGshrpqSb6?u&aUWBBTl*6WaO`dCwLbpsWPCzD z+0Q@@$69*`ajiq^#0BOO>H|{Rg=)p_iyOTd@zvqL{b`_^ixvCDd1T}USYjk=PO8E3 z-CPmYoN4h^-2|TFg@c+>Z-|6FE8#vMJ8*VUHz8zJxzbyuLrk%HgM z$O3f}+0*o3+Lh`p-fak=__Yk^dSDO=t(jSh9a>qLY7!O`kn8Ni zav=*;g}kuaZI_nqLwORV9^eKWJQ9^*MVv;Jl&BPuk!*?-p4=jp1p!48bEr>RoC0xB zvDTzvpyGfH76|R?M{W#hY|Byo*_-ebPk0jlqm`cSW{|GPNNa;4r!$mpx}5VShq=W=nDMg z58Xev4sGwA)Y4l#>)y6sp>w=O+%Gq_^9b|?;H}Na$~qtM^dnQP~mpp z#(Ac)Tf8lvqCxELl?cM#Sxt)DN4Egx(HE%>0U%{iIt)1tMCx8hAd%4a9C$RLmfheeVM_8mTz8RPQ>P1j)E^ zjosv_Fk`oGLFfP~Q1=sx&F=S|-wuk&YqKC>_svr#W%~PdS^0_1ya}L2qeJv2gB98G zw{yFRQ}APLU>Y<8J-cM?>-y5GeUbN&qy4!`kl)g!&|xcV1^vhFnjpB`7B=FJaH@O3 z;uqslTZZz~MI6-%G4q`F5av(&=GoKJ1~Qg7cA+e2+!Go2zr+3O=_;1yD)k$brrXk7J}OIN`VR;Y7SUG zlpNX=Xk{#QLa*D)Z@FThRzVPCsH!7?W`$OzHTHI6Cfs=EK`&EyEn^3fZ1K?XQl`e- zh`n2WT2ja2P==R>YSYmw{kXqvJg~Tnr2IirH%27GCs6oGdg&iX>x*asRGd)g0Y--& z4XQ~F1qC~?#zKkd3p{f*$Q?yH|GXzYFC zZ)y3yo*0LBT9ToYji4^qtL&#B?d22l zj?9veEh$hrC9OieDOzDGt8mfO?E;#^-nqmvf~9lAd~%lEvO6Lk*GJ9F6$X2$@FbOf z>T#Opw=_{ztkkXVZS6_rXneW_S+4inh4N{2_mwV7qy(e_qDklgAFCOa-Nd&qT8*8A zcI?wYr(J`>&p-0_jo#Tq1O-O3gGH*Mpt;;Vn7LrKi>cKou~Rd@Me6bm+*;m$QWp$FUBgC`)Zl(f(Vil?w(r7X(0Xr5a} zJUg6uD{+J@p}-q(rlbmC#fv(Mg3GY5aZ+S>mee+e%vPw-qDgHv`!O-(wyL$}JabCU zr*3?rknBcVbdL^}lR$l@l^9L&FciJJ@sS8_k!+CAyu272}?P-S-$uSelfL@_JAO-G%=E$%+2}#Ed27H^C{_BEWZ#k6D_j z{{VXZnD>;_ta7~_N!Sh`sV^v}Ec~JSG2t6mq?PMFS0DCCKT9hr?lZJGr@X0`Kv`9-J@Dzm9_k^3&Sy})}6hWGrB^2&oRS_X{vN+`m11MD4fz# zNTv(c)Va5JiX!FR4Y&x~?0I7{LQa2S!ai$Ke=2-Yv$|q}pEGPDPi})k2eN=*T|0ab ze32TZ=+o@-(wQO)k%GsCx)Micej20n6gn;ulCLe#FfrT`D@-5F3D1z^1NTOCkrl^~ z(H7s&H8%1byt~s$NC{9%`_$P0{dgZWd&qJJ@z49?f9BwP)~|UEN|tM@#&lO+ zoY7?p9&0J^sEYPNA#)J3so}m zU&#b+>t1Tl{G);DNqn@6adlDXn*BHmp*HXFT7DqD;(h7|2I@_IUQZRG=BSW7lB#q) zaKD%mMi~-J$ql`&FF*xEXW<9*;Q~;#vIE63r`lpE&pE45A!OYb1tCes_XH)g zZ&_Y0CT)=lbE+1mvi|@E0-+LFB;=RVvG&ucQT#2FUd;et;)%PZka0jSkG8?0zYJ|4 z^rb-i4h-1ANekIf4sp)eBp<`l2e6;pg7l#I`Fv4z@xIxoh9L*oqx*2@q#-xY@kEu! zwhXGnl<(AkZavVONt1Zyf>$3|D@3&Cx~B%{N&YD(iqrj_S`9o7&~^n=2J*sB@mYLU zs^gm%`oz2m?AMQZW5IZ3r13?U#+G%D51NYh>o^%GjO?tQDKY*uwxAM%@vrpGSTSfQ zgDWZ~t~a*;6ac_LKfhdhhx%CelHOq{_{@ki$9A#R*GJQY{DO=#MHgPffZaiL!>6TB zw*uRCqkbylgI3FkH2~>Nde=N2Hc@zFi9r7VDMCmC=0$t7j31>N=ho+GKpFY;r& zqBM~~gRgn3-z?5x3WJX?d8j3_6Vo6&aAvxQ?vlI$D&i6bO3Yx5HBGhVf4!k0Q5bFZn@s|1?Ktu`NLztuK~Tq

#c+(YYCuIf+r@Pd@2`G`^P%wbs39NMoNTDx?rot2^caIzqR%oJ@6g$&d5;HZ4mzaq%&8+2*U+^y~jU>0CCHJ7D;O)9oqB}w{lcA6%}=s3|O zwxR(|NJ#2ve?}JR2m{izIg_o@7@GyxqRWcnTw+soO51S$4viyB6$C%kY{ z-TTx}IqvPN{ENMLI_uc17hQn&w)}BFbHruhK3A9;_k8Z7~;iTwJpEd&2GBoS&!QF(k>gp}m=}i9s zig-MM^nS{uw0f@O-{#4?_GCT1b>6oYn_}U(W$oKjuE=eW18G;V=#X zEG?elPOTEt{<66SZQ4HIy6jDfb-qS)Js#lx&$gAe`dbZos3<9-^nf*CIdL%8O8%ch zaV5OPv?P6hwI)dQO!dO~FLGYE7mhbA{^Wp*lUL|9BDWR_ZPPza?3DV{^S z1^gy3j4;jSbv}jbhpD~cbGf^FUeB@H?#g~Vq}(F6azc!#s0nHH2PLg4aTKalMMf3; zHZC+x-?v%NTU{1!zNL}bAFQV;9Ed1ZU%QANdIs8N(1T`C3} z`DGL4T!vg0$Op=7AMpkj=|l6bTg|phgoHd8jI=mBG|;o(&{mjk_-dAA>EwyqtyAYX zr7qVUO(b|Kq@eZg!ahxSgsZRgO3Sq9B@Woxa=#56r|QC{6qvgttfm|`Q=N$*e+YB0 z*`5+HdJ=}?(vqaXinXM%X)M?KKo?qlm`BNXf?bz&=88Q68bH?p%J+4`zFF`}o3BbH zxDd!8R<}oTK(Fh;eq0n*-JWG8ziWz;G~1m}?Wh@jxF^d~l2BeEPFv$s{{Wq?1KE}n zHg)8TFO6!DB-vS0fR^5Ew3_J&saLX~;hfm<$xAmTOLarcx5iix+uKxeOya30!%pIJ zHN$7w5L4SspC>Kg6w5;3%0*Q}KzqKN9jydXiLKRR_kqzBdv3~T>yFpVv-}6B`mj@E zm4_*%3M=?qS)kioLaWq*a6x8IBzIo4b4maQLLTe0M#xDP|s| z4~0Ocxnsu~;50{O=YqUccXNdT%4D~3jAUJ0K}2RoNn=;EuAsvTRiVmP=dXIJ)l(0qIp%kWgQdDqtL7X7;ut6;-pa-|R1aoMF zElNQex}MJb5V52$0<}^*{)`m04NxTs9Z48Z%T5~lpy|-}^ulRHjH~U(mymiZ(2xfhSyTF)uKlx6ekl7trKN6-<;p zqYN*xthkJ{g(r{6J_!K|tZuEXGj31$X#}a2n!}g@(y%*yhZwG74Es5K3tz?MnR6XS z9%@+Q?e+7BV#9lSvl{Thn4|roMnNjCXzaz`YwljA`EngqCv@=iaeA!uF7@K*@x}9jPNQx zSRT$H4N<7-O%p>vFyuQ#j}W{l21}xkWiSZ6Xf&h)Elqg5>RB=_L1Wl@^?BE%G&#zwWn zbmFT;B-DeD9WdI7K=q~pD70}plRK@~Syu!S-sHv&Qc410Yd1whB=bvT@@kQl} zyl9u^qs_PJEhzyExG0r2w2y^dF$X(Z@lch5XF}rFChfNOCf@XjliMngq$#96YpFR< zd&V2GN@!G+mcichcrRIOtWP&jpLI=^V?6T@q*RwsY6UxMgqp-#i;~(=HHsWa$W=Yw z{4^?AhN~c`0bbtB85|)rw&7A7Y&Z{L`mmXWrFpL)=!!XMj#Nu77Jr2w5W)_!X--Aq z=8CS_Ev)qUpqz?u%jBF9*>ci=LoPO=NGMq92%*9aw|h?nb0txPu24BVq>k>mQ>H{A ztU?KrkmGMO`jG2JsBj7pM`)%THttxBLiCo*aG?oBdy3-f)a%7BI-n||DN+S0Y4+h` zo0RcPKMIKgcO}N9A+%-NLxYU8sNaP|uy-ZPKMFpqAMvS?KM08xUgWx~!>K5*PzPKl za?|oo+lquBRdu^7AuU3^DU~osWUfyI=Goi$E!txBIbU|ni7GXq30kX%5!v0%!l^kX zwTy8AF5CF)cii-;t4oOo_uMH*`{Ri_U88$15k(zyx$L6-u1~Lk$Wuk(B&doV&26 zK|}>*Nud=quUs6BQe>^dY6W}iOeE&&lv@GUDgoPq78Er@4+>9VJ-Af07pZ7aYtn-a zNufhkl_d2U9`T0&APFY@XJYwmehb2jSBaOqQm{NH5xHz=xQK(rN#b$_2|)_K!hYej z@A}?BL(tWxTVKY5-S6r#m08Orjm-+wZX%BE-ipe>l^)?0iIJD)H6bBLQ0^leO_kGT zU~pL#-KmA;r;<2?kBjQVvI1ivLjh7hP6CM}Dn$ocj{FMp*iiK}_TZJO^4KP|_F%yZ z$VgDgXM!D2Ez**@f}e+`e#|I>t8flf9_)E}WYlDGraXmWhr0r3yt7Q{gMq5?SqULR zqX|$Vlr?ZPLc9SaP*p>xW(*A%igDQYE$eN_Op8>II>_vh(^$%SMcuX|rRZ0rw58?& z1+`_^YnCS?$izabPk^~$GzrrWaBT?-ZCC3Bw*24xVw(tiA=u+OP98Dvl*`GzXr)SV z-u-ObI5NWYEumtIZ@9QA97&M5&b5|B2y~E>_F@M!x$Nv&R~nUI+xrgx03{_ULE^br z%zmeL{{S%FrQG)=@0n@v6xG{_tAstQ@ew6KqhteuFC3mdu7!&~Orhp`@R1IEB6iqJ>_uH*L8-=LIJ5#kmh{)X7qG zy5r7KRU;uxSg=ro6?v-8?7QfgHAu>}sWeb{1D!G^pH3vDgeu6o1g2fZ54ky*$Z)Bm zMzNUv7>AYAD$`{`+NqY!#*e?ZqV9P}d8;(X3n`iqtpUMYi5)T>xOo$rMykC&<&BaS zb4tkAyH&6YmsPB*s(~Kv>?hKl$C0ljbsHyWHk!F}T2eZwtv{m*G}SvdKygM>w01c^ z{PGGTuihsIEnfUY9thJrJ76!wyP6VH*rlZYtTiEeJK#8@ZS3AVTB-P>03M{4@#55P zp9uSVD~=mbP`pG?9a2<^95?Ar#rjo`wK&;yN-j_nUbR|(S-=!?lxk8NJB}05=WZ4L zn}4?fR6mKdt=rps1&1o#>H|t04yMoR7+8{$7ojaiJ7PT&*5!tTC!#}$Nc#>N$O>84 z4JknmZNf-9rXrhHR{Zq+m`H@AQ#BEK#j&NA7VZ8btJPj&2VbiPFDgPShK>k|_KzMY zfaU5r2l!q^e@+tfPl8wG7=m+V@u(-uzP1l-TtWSK@nKGWVTdEMxcOfW;cQd0S_k|r z2SGwEf#QSyN!aN=3(#2XSD4-dq^164s0Hjzr0E)NrCs%PAFB>I5$)uZ zsVgLohtK~24i=(E=0a6#X+?+%1;8OGph@$RNEE?>kCoxbH!M*rRn0(8wZzx!7?R^Bdy4A*lQknj`*K_G8^>I@{uni+qrp`R$+lA}~E^9;W!M z+P6NHYAQnO+_d11byCfBQO}T(@%c&j`Kk9{sTI1dPn3dN%=b8)J5kT#ihdOl=V<$lREBdfNzzRS;5q2HWiU|Y*+f2rO z>^x&Z2weXF1d!u%nkohJQQM=h*@2QZ=DMjVZ%RsuG70{cieV!fO|GIsmbtnL!^>H& zLrri;0ur#A6o{v;OF1-GPPtPAPnz1I4n1-TAluZV+G=nKPb?t(>!9gvQTJd9aT?V! zuPLwn7OEV2iCQd)mr}14wMiYdRR%pp<&@643jYAnVL zBb!y~qfFV5=Y=wxP*?AV5%hI9C~5#Gl+MSvx&c8#hj?|P+5Ht@L?}$jK2E{BAv)9n z0=h<|5PqYNc}cg-`VuRfPVC7#+0sY!(r_XmgXVn+C3Z`jWa>Zwe@Qq!5!*FTNr!Mj z)Kk;0Y5lnJRj#Vs$Cs9*Y8}~NQILVRshFYd%N|Y57La7&=%?^-h*@ozj6TX<3@N-Pwf^T#TRsO$jyVYlBfXmP5%LW*f3eQ%x+>sYoRK6v3#NNjV2qx63x| z2vL9o*y;FPA7AXkzFH@mz4%0wf2~QbJ>@TX zi&1Ej4Rt5$$GoNQHEZ~Eq2eh6yx@GNB>eO$Ak0cf+~x1c*%x_1`TcIFjA9D{IF2NRu7#M8+?NWr?3C&K=% z3NbbQHIUf+KFfW#$`sY-&x0wSZ^0RrkGBVFsD>zK4%F%TdQ){wSQKiBGLh^ppXTRtzy;4AxeYopsanjCzaSYS`P&Du>!6^#OIq zka>XsxKD}^rBv=vKd%D4=B*5QDF;diwA37Wi{4@^>L*laMwK3;FVlk2D;$9awx*~Q zhUh!$4hN>FeDHxoGOBze_Yq7GHi>SaRs?x;2g)R8MPSrPnO4baO+EvcW*Z^%5HG`& zwfGxZQio+tDS&)+M}lD83QJT=qi!i_4ku5=K%v*&fe@b*@JztGho+!~DfN4{I_{+3xb5K~#61*f zn1NCTK>aw5mc;Q2+hv>&6ml8^j#X^TWVZS;J{*s>Xx~Xc>6-Mg}B_Ji;NGQ&Sv<$u2dcdHvQuMN=AKaIHskJ1kKH=O@?NU4{5nWgg zQF((-v_9-1HOnJ3aZD{S_g75nj1m)tHtkA!KuI&2 zNIXJoZ$+CX*}7etKo3J~d3heHP)Ip@@ZR;^EmE6qF+AWEN97mgNqS_53f_H1Zm9T) zC*O+1%&Og>Z$oH_t+P-G%LJk+euxz`M?>}Fz#{c6N-5U~N|tL@szFhwLNJ0+1g6af zkzV`~LY8ZgBmzk7z?LH*Dc9MAmxx5p=5{H5hNFWf7 zy>r1F-xPM0sY&QgeTl*av`ZlSDPN}rK-I3FufHA$R*L{;O;2VMdqGBP$UrS5jD};g z2)HGqB?Dg0>=i1pk1sC_x|-w5%S9yr02O`sQb2?h9uq*kFr;h5B%AGy2H>}@4Cb}g zrKdl63+y1_mRAzXxX2?_EsD!N-SrBnZc7!%Yh2Y9RZ{Clv81NDRx4Df9ANoue6~|9 zmmhZ(+*Jx}*jwql<4s!bt^{0|O%UPe@ELu0(RQ}u6SOhhR(H9!;e?qP@ToP_P@MA} zQ{9Wv3W~zENjYjwN538kMY~H|4yD#yEUgButtud-`v}hqMKj|yX`)VOqmWU+5Tv^= zF>d=}1UrnD_a&l%F50KIA4yFqM5$gO_2R{Ax@Tac=<(oHHz(V>DUV!rjs7h(M&JJc zV!P!^P1eZv#=1HVsgXinh^1GmkVZr3!jBl6-5lR7-i``2tlT)y<-AP6$ni|O=cgOO zkmbi?USsZ_ds+sUcbZs6hQQ`qkA!qOwWVU6CAr?o_q;BRfH z`kA>~oNT6`vmuor{{W^3*@}I|we_K++E#;SlgC~*vA7>mstJwJ5lRhQ7+g0`#IPzV zJ(x6EwjCXzOTQ3!u+$~iW=EBz#E^$OfKb)-97wYICwwI_lFjV+fyJFATnS2?B})iOP>@NkNk9M(V8RH^2#Twog&KQs z1t>V`u$6Tn)4SV-$z`dgL0MO?vjJ3a)vttAwPv3D70swhg{=)~(w%UP7m5K*_Kdxl zDG6qff+Og{E^jn-j3!W!yi#&P9Ug{fUD`=s|_V&G9Aw{&gsTCn`fbcC`~p~U|HXo60a zroW>e28fSiTq;oSvjnAbV!D*2W~+d(YAe{2+6EAD1uh&?Tg)V<%tv4Nzh({PA#3$T z5|No!MByqPX<+rXNzEp``0@uDqa(1O>hH@7w}K!vLl6`dQBXSNgCf-isEjJ7$^+em z1a(S@+=BTwKjOv+Z9(FX=exxcTt@X(B!SvS8+ida6D3P_+lM)=PE1>FB>0;N?CVue#d zA(X42NCT#Qn4Y#RZzT;luN3Blrz9`#!CJtofUiN@iQ7kqa8Q>_qBIqoZLL~Mm8rwR znLAV>ln$1YnQ(-VGc-67C)qkhczuEfJw^{@{pusIwYxTLQ|kCTZ1)_as+l^ zjH&ElA*};4kwSYj#7o+wBP(R4f}Kx(5oVerJ5)FXE2Rg&9+4O=^wmIij3&KZ7T{2f z@L)(qC2fE}vYjU~Xgl%c>Mg=6+mC3xR)kQ{^e0Ii@sX0p-y-yNVrujw>6lJ8FOT&EM|TuoFF6*dBV)RWMG$` z38U?wA)`e~h$`^UU5 z5#mQ?P4@3}bIXMZv^e~Sol(gsLIRYcdw|6kqU!cQHE}CYjyr28NzsjO6Tk)0ZW{c* z?q#UktDM&U--o0;jfn|CcQ?jHk`|LjYy2syz1Y_F_iP>0=1uHe`@Q@7Zv%`LUi-Lp z9s|@l{XWIeTMq8x9gA&`w5%I>dq&l70l3jjMYIhv)U{HoC$y;78n!sw+B2c8pj_dY zW%fztSsq?br8#46&4+ZNA>Y_ZS?IR{GdXlsF!*F+xeC@Qfoj>ayVQ_2Q?Pwx!Mq+9z=$# zw(F5`kgqwYsS)C~plF<`Y4qW=jj5`$UddRsG{R`J+Ven7~>J1EmQ;rc~+m zVf^h?>uH@-#al^D(S()h1wbm*pqevBji=VJ3fbw?XxAsDG{pGt==UWIv* z-uk1z_<4ON#MpVvQniKf(lZj zN{r46cifA3xVFDaR~y>fnT;CoK9BKH=IQ*fdFO0jXYJMQow<6IY*JA5${dMGj^fmi zuL)CCrgYB_t+%`qH>#9GpSL$rJEXe5LL$%Qm(Ncm2X9+_qJAQ&a>x7DaCvvVzk(RM ze|6%Gv-w){;)j^l?AuBQqJC;=>z8X7Mcd-uoD+uj?&zHgl_Gvj-ldljsdG64YTL|| zrESJ8Pg252u4Eu$3H`~n4yX#r2WK)Asc_fP{wT-)0AbtIItU|?cDl!Sa(MkH!GGNg zZv^Caa{mDD4`=wIPkvCIpjszaB5k^M(9bmbH0O_hx)$CG-|X%8-X72KUQgu}&mor< zo<-ZL(lr{laOg8M8gb*?$PXlD_KN%O4`=wGfBO@jpi(r!-}`n_X;4pvpD$Bto{?XrfqO1I|y-hbl)yHov_JeemkJ82m{mQcNME?M3 zZ_zh@Egw{EP_Au+C0zn3UDm}EAXRGoN=Hwj zj5r(m%<$M0B^#stK9&X@9rb8AChhIT+lLft2j$V~ zBIs0OUbgs3a#ELcd;)!xV2lI%gK9Y=Gqv}pnmXs@pXyu?Q+V4+C!&qkLH_`I7~wzn z7Sq8fdslzrw`=mk^*rFO<6B$~{{Y10AO6S`-+J9nASLMI!QJlmGE}qRd z@~_a&9}eQ#`hKt4pyusvR2@L}pV)}I`Fr}=j!&1j+I30j6M8|f`N24N&f(b|4S!Fi zN4vH;F~kpP{fRZtLVmZIG&@E49pSs&f3s7A3q`&xy14$H)Y<*K$kTxO{{Ui6ljy_N zyh%}hexExJquv&tDVz3H`*^hi-=uF^XbRWGH>DKhoipGoQT7j+{aAQ6 zWuX-6qKtmhz6VC1tM;tU^sVbc(6l#j;$6iNP9-Yxl=Kjy6e=?Ll5@kWe9gPfLxJ|D zGhnWpp=ln^+L7Pj4^?p#q{!nf%$1|#NKAE#)}OfP!?Biw$L&ad9Wde#)%#OsXAydu zWl9e5mgNdh{-ol^*l_)~@pzzoA*WQ=lgA#SD<)E^1(5O2`{JMYlvT3LP00)4m@j`E{>G1_4 zBZvJ#Cyp$9ts{@GDcIYFbh~w?_J&mIl_m<7+o5sWCFa0WYDiL$0V6KTvSrm@(i3R+ z1)!6os8`oj9!-b6VQKlxEz&qwQsugHVEryMFz~!LnvwbVPN1Yo7b~j-hy2z^&q~m? z&(ySxCG$ZzZQLNzp(p+{=%7=jW=>}LpP8J>zMtx^+E%6 z7v2jda?_d?**@?;UOiB~^D%wku$zN}Hx`hQ*Y5}I$Et_sBKyPIjVg3?Bq-@pPr`h} zXZGXO3*R#r-UtOxK&fOTcMWML^x+dB@J`t@J|o(+6DHQFcS$`wVJGasdA@|Fn-_>f z{Qd1Dgvwe^f10d&&O#%HnG07g`-Le9FS-Zr)oTOnI4PMc6i|8)DZK6x`_(;!>R0K< zyyU;j!{}S4^L#=|)`X~zr$IRPoQdT^woUr=+R#;ccqxTE(31IR{8p!NT}z%?l9VsE z!C%>h-0JX5of82=i}lq&jRP*w*uj`JNjZ%1UQOEMQUcSM{)QGwc~PxD8k|3JzvPU1 zsC<~#4enw=B?5!ApkvG4RDLKUevq!I8Gk@vLO`*jn%Li+{{V?k`M5J`5e#jwsSJzU zkBBym`W-k{2>=?T`a@R#0FKEDO1rf{z3@`FC8d59l!MygOc@0h*F8thk zdSh2B1;xuv#j1v-v=UU1p+i8hp`HV(yb~*nMmvg9PV2Zlw`5IAa^%Qbmh;L+YFdp{ zqo#tSoGcocI@O^K{Lb;A`BrP$_kZEQ^aPKT-bGo`(6rTn&@1qv^d!bX*(5AiUmM)JZB@#~6|Pt0i@zzPqpxM@Wo3 z6*9>Oai-+jQ2He{vZMsm_y`$Rv^akRt!dO%XS=UzPkKRP%V>_@cSgs)p(Ym8k6@BY zbiEyK&`?l4yZoHzS zPR#PcW}`*sSx3zhJG*tAKud|GU%RZI*MjvF@8sfH8@F~3hveuzsz0v-*8cz$2F4UW z^RDSabeQoJ4`@=mFd_03Ps5;IM)lkT`0eZvdjidH;JBbT$OjY?yQ=KwulhWvyF$nO zEHp2DQKQDiUMK;%XQws6aU^!?LH!sGsS^JHhsWZa7TdE|XWC|DXB*f3tAj{b?8=#8 z27}E7*>hQ;s+jMsaok9;qinc0^vFsQXG*-T2||^TK3ZXnf$XWl%T!gB%&v7Dn|3uX z@%vM16sy1=s}F6DyotCf*h;KEuv4dzRD}SPn`}TmwAI~;HsgIJT8C^veT7xM*^-UR zW^HMp)IS0#uS)&E7`EcaR3Q2mAGjOQdhmQwZy>@mHeTV1fL7w!1t5P##H$Nkim@MV zUWo36RYpe5lJA&acgsK(O#vjHxPX@5O08h$n@|E`I?CH|*3@!sMJgRS8ex%E2%pp{ zi;z1NUg3fzP-PYI0n-I@0Y_`-gIGD9!G`V>OGb2BC=R-+a7Q+wD#QxFI&-JD2|2V% zqH9GCL#JEM&O|4OXQoB!GH#7){KobkqsxFxmA=Hnl;iB}c70h7)lmct{qC zNzC?l;NDs~(+QzLtvyv=qX0tk4z#Wj=}QL%q46a4_Y8T>(?q3E1QFCz2_vs}=k6Ht z^3;HO_G8P-9$4=QZafsg7 z&0}${lv`z;>^HG#s49BcTjoCEjF=N%2)suWT1lsx2k#2@`!TBRjmXP}bh!J$W?P$W zI^ErD9_o@74Jl0VY$B?g$pTf8)OvInDhjM5=5x|My5IsHv^b`aQ{p4*!+{t^4xvWi z&z)?Li8lFe!H^D4G_@gLT1dm_FJ*|;q=d@rA8&BpnHmW6sWt}y4?yt0Fm5$B@>`Z1 zJ>hOX6CQI3TEc&#;1#YX;oWa=>6M}5(y2FnytS`gyPXsKLH$SxcCHQGtwv(*>@`=)rJITQLT}dZP(2vE2{F3xrH{KM2E;lkL zge3I|C$sH1c*s8LZ!O4fulfRo@jzGAn&2pn)Rt2`O^)12&Jg5O>4PliL0afvX&uKN zsz9=ap#i1DRHyUQcg8}OjEZQE#d%QO zLx@#tlZp0Vu?~||Sw%v6x1nf5jexW{LX~A)@oTk{sj9zq9TK#3=@h1}wHTU?K9vzG zv`Q&xAqjI(NmYD3P9y+P?{_4r9DUnKErPO7-dD1nF#WM8>A0o1Yg*Klwz9&O6cQyH};OXEXGLQ2$ic2b8tc%KAXBuOVl zQdg->Qj^=sB|!_s(#E^O=Xx=m{yIq3y?vXo5;C={2Wsemo=9 z0dnw3%uv?~(Rr^cz!Og6+kgQT3!y#OJrGrDPOts zfN^f);tmT2FBt5lo1)R>&*UqDn||>m9F?HcOmrXMY-4J`T3KVqy4s%=&ilT6zHj4y zOF1-fAGg=VO_6;;SZ0M8uRP1DIjw1}bE)ZES%!Ky3Kf>iux=wJny*OY9ix0nX~~gf zApYy(r#$}vd|^>@Zv6>N1|uN>_d!i5R;`CTmqHW*k;$%hKZd;vaHq?nq6tA!xd5}QU#Vq*mrELo<^e64NlVZSaEiaH{7J&e2(%~6) z<&PVPLE@P|YhAxk#Pmkvv)*8}HrsF)L^z@r{SHe5KBKN5$8g7J6;|gNExf%i6e^!{ zn!N475s(MnG6~GkRY&$?H{6nsHFXDTEqZMUJ_>!3A`+Q6d(x>t5+pY=+uJ)=BD+!~ce|;<3 zz}ohtEEK8?7Ko(*s*m_F8E`33v0I^Aqw8H!*&D_t$k@AfP4e~lO^#%xm*p)bNkWMt zr%g!d*BPcy8taK&)5UIkYj8(n155rAtW~#~Hbd~4Zriz9ob$myF6v{rtyS&#dvQ;@ z+t-nM&J+t-vECTliZPA^rdH$R{jjGdTJHPOEESVS;w`e;sB@y~p6nXW33UGed7`M- zJBq=TWR3u+;mO=ZadavD-)`29s;Y%6?WQC?UnV#-rYy)|PZtDwJU$?}0?Rwz+_yle zXcJX?1yh8sCCS7p=9(vVkm2rKMm$w6wFiqCgXJC1Y#ALT$tV;)US15)I_1w40YQ;yoVy}AOO0WRs3kW`-I zPRurylb$V7F+S9MFQQL+&&Tr~Tj}2xc2@BvPZI3$-BpLC~jvd`}Xb-lT>9%(WL^0z6d%SrP?=mT0#pbZWq<+-{8R;dW=?GB;I z;{M>Z$nxEHds~#EkQ+&PnvgP}sl)`9R|N1X!5%^?d_g9=dx~G9Tdv%Ohf2QHe@+Z6 z=H?RPB@c*OyK{o17k`;;wf)iOx9;m)CGsOEsM1u*_2iX@0nfVx^IHB1!&>&8 zQ;yedyDekReWX&L65=}hh8xd767h8u{{V++xHg2Z=VP{-c$KJSU#AQW$xgs3SK4;D zO)=+5oXJ5s4MS1vuhWRhpHimm+B$VALuXJ%q$vdVRAC)c{{VNA-AI`5Er8Hb_-jo5 z%r+@O81O+DQd89pliCG8ZUUtgLOc<5Gc_Z^+hF#CPuGVPKwt?bO0I!K2AVp0k*1g$ zrtXX;yvuFX*8^dTZSXqOeD*~Y9ARYsS@?qPUs z*9alVXv?KsNA<9^WgaP{f+}$fcy_s#Ym6I{P~$oyKFf}_2&}#RoQX4{*vcAMG?>3 zTp1n3wIi3~ut3QE7jRv3?@LD(OIR|_6#-}247T5m#5v-ruP1cy7$r;P_SQNAizUfN z>MaaTMQfKlLa$4m+k=DBB#Y0diAka57nG%%YfXaWYg0~zAmQ9rnQsLNHg|6m@g)BM zl=S-Rpf|}AT3N5&Ik#qnE3?80ui1%c*^@k!M7Q_?ej*V?H^Bg`FOXen2(4XJj#(d4 zMR2$N{W zg^cB`kF8p^-p9gatwf%9W0gwe9j&>xX5fC`8tpdswk}V|PQ=|GB`Q37)K(k_E-;A* z>ItHgQbsg94t^`R0dVPmG2pVD$+xdDw*`USQP$X^BeNdC2j!Eh^|zOJpCf4J~i1=FzSN zOy62&Y`}DZz#m#IZ`zFBFBZ!%wAy0Ly+~#)u@12bv^gR5hMPvB*#fFi6~l|!ZQ+Q& zkVxaF$=%&H7Lp5!v=`q*+xuqNC?xGinj+5H(P(@WAGTYlZofF)8nrNC1?Atn5@}}7c6lM>|#v?!R znZxRWDyuSPE_$ifQ_UWhcMjs?J)RX7C))4B4cmG5$wX|t6|yH@9fxq)7H^dKWw_jh zLv1SakVO-chC~yDU1%x6K7)H*kKWZRJ?ZC-6r~01I~$sEKivTT0D9oUX3!o<&-dN$ z-bj)?Jn?{Vr14Fuz(>T|}<>r>7K^x3Qn{&tSOEsQTQ) zqFOknLwn!2XySuDj=erUPnWRe9U`lx#&$!$-b_MPPvOl^y+s-(JD2feQ$4dLK47wH3 zIUBDa2Ch-7zKi(2ESH#8+nY2cK`3+2d;{5$6s{%1pbe{y6g0c*FXnW$9h>OOiY;;# z9sQ#x<`R^wHn}pAN>nNiWE>+KqL5S&n%(bg)NZ4y5_7(jIE4wKwtRqTpZB>reYjWL z^m|lb-`Rf>lN|JRgIw#2IS8l}2_ULD5_X}rD4%d}RhJ1W&ZLuJs<3;&?e2bz$|>SY6G%~O1o$ULF~e!DC@yG9n8to-gb&DbJPCS zj_R6v8sj*QnUz=R!@rV^HkY~=p!`X5YxAwgh8WA768P@pUtCNW9Yo^3dNsH0mwb!$&I@m#kmGDfM1T+Y&8Unv zwGq5jG^1rZlX-6bknmH(ZT?@b?v19s>ub9}Hg7S;ldZ*v6ysFX0)3c`y2x5GUW1`l zdmQd^;?vJ&txR|Q9$48^jXQaYvXV!IL#lf9ClhS3iNdOrKX&pgcS+=s-}Ili+Bx1M z1Pa!RNy2|7)KT6`@x>v^^ub_Y6%EH0fK-5M_G8~9&lIzJ(|KQoH%F(-TaE-VxZ{rU zP;leqTjHM%^W1?~FrnS_*p`x&N8Bu|E3FzQ{uVrO%}T}J87o5HttH6d2~mqeh;l9FNGf!3B3TJ{{XB10Q^`d$b~5%c$xPSd%Qn%Z0Y&3quKWg zAFm019qCWo-RKYQ{b&QkTZj>%V5D~WYN^6Mg*htT?^_N_al|{7RVk1G{u0`T5BOV? zf!;eo$zbsQ?G(_Ys2-{Sf%f2Ai*oSqEtfQ5-WuKU;2X4<%4XJ=gm*3}8rw_iN0^iv zXHqlAor-vON_7`_EZjZ8R8bsXb6S!=6i!{fQt9;Pg`I?ViX*$f1wIup@fW0<{{S=Y zNA~O;@o(DVBz4M_cAU$lkd&w=NYJF_Ny|JzXGh5}IO>g!?vKpN&>D;LKct<77&|Jh|>1Y?Gv@F?%LUYO12GNgWP? z0Q(LsUx$RyZ+M=5AQq!;sj;PSmtXNse!-7?h)VwecCh~PF1tHTnmY{8cIZE^2>hr@ zjmX!2_tuvzSJ_)ha-X%tOQvE%UPGd(pzg0sCGvDjH!R1UN=)3l!uPmDbW8HtW-(D| zwor!YtFoyX^zFe*K;)bY$hj2IQ_@tSU1a|1Nw4U@fDaIt`RM7z7baOG`A$%GR|jbW zl5g{Ic!U~lR}06Q#CQ@VxYA0)NJ88>dlC*3njrp2r!NEqInDqdN^85fuBlSBS>!gM z)jmVR>?y>2k)HyrF>V`TJ~8bHJ$rKIvtPp`j_*=GyR3ou zRH}c-JN;3B@?YSU8GZ z{RN-ZfH>ElMFY33kKb>#d3zfA1r=_hrUm3 zQlW&tQcgiUX6>^-z7nmrG^ol@b+xrZJ}ik9rdTwV<)WoOW$PwwT56_i!s{9$lnN9X zNw0N4!&d4MZQeSQX3Zr@+!lJ8QbRzWdZ~r&z9d}ftOYqK8W;kJAJ;i$>Nj0Ptpyij{ElB-Xw%{LIa27YdV)F7&uZn-% zV7C35=!Xaz<2_PH0*OweQ``W=y}$vAt&W)a;+$1XmAbaHG(D!~Bxgi6(p0LE?VzSB z@40UUs###}9N;&Q>T}sj*Z%aTtkrjYCWn3V5B`AVWtun9E zk0A&pAlD(58n{(1$Dox`2&cOMl|kvPLCB2ojKZl{g;8e#)vB3gf}@04NHsrZ3f>FN zSf&tj3UyF}&W;CGFD*)HJF)6j z2q-d*Ad+$7(M8~OLHtz|`Y?mqoQG(&(gt8u_h6I=#b{8j!S!IYMQedrC{PtScZ_)& zt6sgB5kxBoJdOYb;#Q#<*0lCu?Ba@A5=P_OR?XLIUAB4xA4vY?KE5VZ^mPZcVlOp> zit;^psa#faYdHaKp;Udxw=a7l`Q*Nn7EE<(i7h1PbpYhFp4#IQzOcB*&rzio)nk!@ z`VSCzg*ohwM2mlIyLHHj_g26OQ8Y`m9dqr*nYTAobF?wv63urW)_e!8nzcyI49O#{ zGR3^AYgU6Um=u{ppjWf$!qJ2aQBZO;!j(qrLZIm+cbbEO16AVXg)LPItu=j^H%Lcg zk*M+}Lv<2hzT{VuK}+o|NKhlDX&~Wyd%0uPrirxnkjL+KGKondl%QDwUv4LD_e|YAGJxj#LaE$#{>ZsY{|AlLi2 z^70xyB|a4E>-ktnr=2N7r?DfnoGQrc#RHvA98-cduuP5TESp~5ZzVg`=|px8`u$ju zjv9|bjC}MyF8dPn7|NQ_G!}iiZ~oQf&?P_)hxpd4Egqb736!p{rOK?GRmf$Gw34-r z7T7WbA`_@}=x|h56sE!*QPA2;qxb+M{X+%`6GSVx zkyR?WQwf2pX!2;K_wKPa{h+Q(Q)1s9D{5mZ3#XP-L=#g()b+)d8=}ayRj-h1rqF1k znATL^Ta>s8E0Dqz>X<2^DNkJS6zGJNAx|>n%h0BnXaYi$LOP5`5T&ExRKI0)w99Va zEn&FQPu)DP-CZ&s;&A%L=UOP-g}#*u+NjP&b!@yeEeAjQqZY`WP^;3TjV7tBYENz? z;!$!PQlwe}Q>vjoxR!^CkC`d3RN8{`OKw#qL}Eq;sMl(aDm%<9q1MPM0ZAvN4{jVJ z-cY7Nk13ZTN+^*TM71rlL9IIu1cX+!(IrUZ_Ti#XA;1()eJg=MT~^~BNV|$K0X+q9 z6h&U`!Cj*A-^(VR+$h#ndqucF2EB3WFCA7X_n+?W!k$_Rt#lgG9&-@XEeN5{sKQiU zs!F}t*}hv}fcXGADuwkZQ0PV)WNd`d7L_B5zjP9Z1q0cx6LyB;tbjpbeq8qZ7SS{c zAEz00r0=k~q2RRJ*co|kr_ik9FEc5IWU#R752Pul24JN^qZ)$C$EyBSX|- zXxxNE(ZC}a4C&Jg`H6DjMQ1iBOkJyMBPBXrS^oejSednVOKPxv(I;Knf;l}#UguJE zSIG$)tB^G5iVkjGtChTLrWmoc^eby^tQ!TEC=^U+tfUUgXvON!-H0r*@bz3c`cWy8 z3fgc5bV{l_Fli)CZCkArOC1Y;aYSJ$seSY|p26c#PwT`~oK=SB-M1xuCwhFfGitKS zX}|z#EN0SakfV9xKIcsqFVk6%ty-Q(iVp|bBb21KDndyr=|JHm&|^Oj!V&OsYmZ3 zFZyQPsZd-A5sPEQQRYyPJFttld>?fz-IF-fIXkR;8M`CxtJEcG!`z@klecLwqu(&@8@kh^k6_vN7i8A*QR+ZM%R{B)%ct&Hn&v zV|cASsY(yp4i)@z1XpeQfRWwfZOqW3tWr@=iikg|xKr_Ke-v-Qu6z+#+Hfjky%4O2>`32DB+^TpRncrd-@JTTG=DC6uYq5D)IAw52ed za@zQXBIvG%08(^!w=Jm(LvyFLmkCmqTy}sTq~UQTqr4K$b<=!N==+y*LvfT$xXYCt z)quAPi&vJ}RSJO4v>DS8_c2?Mr7D-Vyqq|714Utsy^m#tx`z7Y$-0dws#u~EE=f`c#Qs&OdrUc9Z?;785$wgc7rid zj_d9}FPfwS3Eq|_gy3&z0SDZH+8xF*HL{lb=rZZv30eMC0n5%uTX2pDw% zgz+h)(cE%sXwQeq5+Rsv+o!)5G8}Fz^(U@HDa#Lp)@|k!2_e34pan49**t>clDF-* zW1#9nOlSVur#vt7)`NiwlPoWf5LAh_t((!5c~Ul|&R$gWj0qG;_k>}U<=wa53H7a= z;Nl8@TkKn%Zs?P?uBt&5(1s0T zRzvH+eh|+Tf5oKmDVHypZp_H}II@!J1p$#=LZ{kr`Is1KsZnz78G#@K6a1gJ+fJ{Y zY=!(e1E1G}{taxSrOsT}f_-fp_5SGwe(T+L#BIHgVo7r4dQ)LOZj_Z4*h%=;hM$Ho zo|f}UoV1N;d(=b7+|Og*OSVVb(8aFHYYGtLElV<_YyJ0CUZ$M@%NKk$36MA5BBf;8 zmvzf>>NF4ni?$nLXVugluy^5BqGMOL`#@)=98seBskiT z)07>DS8g6hV%Cld7EHPER}{6zjCysrw-zd8ZfkXoau<0oGv<8btu1b#B?<}@SHn@$ z6w6pIKM)#ws@ayi$NmuKHQxOfC(2OsDO=fD&d0a%7VLqxZu_NP*@{`x@Dz$Ot<9=;z>3Z7N2%_G* z1(oOuL?+k>)Fc<3LxlV*6i;R7H3Wk~- z$95l_bVa3vXrkiI-L^p|SG)?8H;<^^biVwg4a>gdU6E->L4^dg~uFAwNmH+)J<^u4VM%^ z1`+^FZu^~gb0Kui4sqt2tL9r{!NtnsYdTi25~9PiTkY%~Vei9+;3>hyNB;nM7advx zAv2TqmD)U5S1p3ki4Da(#Ac_=LQ?9$N|uzB5EMWaJ+#jPHsf0Ep-!(FCu4lf`24q5 zqr$o*$s4ndcXEPbK;u}#hCrXVm9RCWU{7T+qXmx#iHO>L+)^A zf(r}2L@8Tb-GLgkgqv`>aoBw?+Tq=Nmry*`pLQ~eRa~^Gq55#Ut*%EJBxcLme&{JZ zca5B&0fjY6>^^dTW)b;Igx~Rte*%n0xow9jrWCq%qt6-Oeo|E6U&4G*#7)|*r0R(@ z0n(3tjPRGr%8qWK0<>K<+#9yce=Qy~X3++HvMRTv=-zAV*0&yJM3>xfD6K>xN;!8C zN>dnqKFpoo`toS|7fSA&s5=503RKhUT=8=KG0kowd-~PXjTw>jW1yP@Xt?dyF6y8` zHMp-wN)6gmRIMtc{-oitw`EWZna2cka^ANSQEh*R?kCdg$R*~13F>=D7&Wzh2g}J-lU(>F)A_8Q&m%bQv4kX(OcVoY8tOA3P-@Q5TYxeP%vM@ zt;#!c`3CVRMkgoo9lBg;qVgYlB9gX{<1MIEXP7C^30&YUrELmQ-*{wTm^^Xxr@s5< zz4f>+(XRJ5Vy0Axk^bosP?YHir9~@B^*Cpa+jj9bfT^th_P(9r?*I}LJ7Tq}OYm4#ZQH8ocp{Ve68R=N*jo{gnl$SyB_s~H6yWW|MmQA0Z2a%ynrCo7 ztIT%Z%gRq~r6=@Z*T}Un85tI*a;tZkexp(6T_BK8Yk^Z!;XgdA?hcYL$QBxqfeEIReVImWA!5j=?OOn zQ;Jnh@eT?YkyHeiO<~SvIw@{V){qz_S8+;N)&m7W{S8jP; zN^Ee}SJy;z)O}+O{HCSbGsiTT>~OO7l2{{mv(+UW+PJU)eXphRZ zMR$$&Oh{4`>_K<6x_rTytap=_(lAL_4ad-@tZyD7s*BUN3R0S8JE$=9M!a%KX32BC z?P)GOWVpF)eZisCG`%HBt!Q%34K8eBm*W*mMUauFsicCpxZQ4dXV)g_Gv?n{#_H&! z{+1m>Ya0a^00@%Z$n#Q3Nkz8L@PC@0*@e8nLNJ~FC`Orxs_SJc02-YM&+Ndtf{5n! zLCsD($J5pGX6+6#-xuo>NV0Opu-!JJ<3i7nmjh)EfCiQLf~nkbM8j_Dd+Ui|jQi0V z6{2n(oqvq9hIt9;oxMZVr|nEQ{BdS}LkWJ;+8HVV)Rs^{A66%1lv1j%6xSi&5lZ`m zj`rPxE0b8ULw&HMfJAoluVET6v=cI#gst?~Ep2+wwFkB~uG!pC<`*mwV6HmShSTIA zEoDiqa!BsNuDEDC6ihSRNHvg;r5Ks8to~!Iv1()`yocQ+eMXhSe={F~Wh~bq=;Q52 zEgLQkw5`S>u*8Rkl&CZ-o_)aK%O-CiqOuas`Ug0b8E$Q;q)UBiVJ~PWT@ncu&j`F% zl99E@y`qGaCePSRZUmfGg9FpW(&1%|dyM24nUctS#Y2^O4fXM)fkT%{ z;uhmH#!b;pLUdug@_fMRgXv55uS%Ee&cwR%ELo75+je(W)YT}s9V%&lQi@U%Qc@L> zI;-ra5<8*+Ka@tLO z^3jI|(^VU4FC7thJQ0XKm<`(BYPv_9hTz`=sYR_Ys|OG?dNO0qrJs%N{a zkH%BYY1L_)E3_MfKG!-2H{If!TtxZ75BBx|>z&}KA@Xboh3 z3l}-7kaTfOE%mo7aBPn}%mgyES4zMg-s}y;QS|VlwryO^)VDwv5)}g7C`)$&s?om? zN_z!ypJhg?wsvdDfK@psd5g4KpHQM5XjYxM(-rC~tS;3xVLDp8z;G&>Uu=xKmIf5J zVJ}WHYz}~^%htqnr*Z9>8fqSbw3;600UxIo4c{0NE6-@8{sV%Ty*bECo*UZrU;N!t z+BuxGNLTH}gKZkX7I)mn0g-T0{nge{XS!9y(NUx%duq~#ChueZ5man3A2i=u<|;r1 zUqO!@ zASsn_6iclNQcit)aGZ<51X4iAb?JnXpdzYOPGW?0!NCbc63r=sA!Lg~gk(#Fif5l@ z5qA_|fhawh*9pjtR9$GPIZ)vO%B8_r0bo+N9aol$1ty0FK~=5+3Jp!?;mmj7rKrK? zi&B*g&raMIp?j#UJFrsSQ=(`Vr8TZ}uR(xwYLXI#s!>DV+;Ei=%LN*9CZKj;LIfTK zuA&vO8cjOj(V#>xmK$d6wHrO?9Vw}SJX6e$v(SqE?|8#}rY!5qq` z4U&i?l#pwXuS_7|;lU^!uBbt&r<#z$)WhwXq2|<3pnZx>2*D$`k~2_sO3NH_x23=k zq}y*XZ#Xc247$4?d0Gxjw%yti=#ki$72{9Wi#4w6m^Y?C9z7zemw(&q8DHi^OmFdN zr__gj9{_KbNV(c(ah=)BmgH0Ow&f`Ip<_}7aE!K?OX2S~%Xg!Kjq`UlF#iAz!r_C* zM|)E~^XYo@8ePj^U*K-Y3aM&EzDP^pe@TZ8YxLp{@3ry)*KF|N=qh%@ywEiHcd@!R zf0$|QP^H%2zBY^MiF&pzWvrBk8Cx#*e)!K7Tf01ssickIw0wS38-C8HoHg{-X&ZAv zkOF0;a2*+WZlI)8`)P!&^6hqx(5F)49yM(U^d;mt+sO_xw7A*$R+1Dc^kPU{Fz_ln zp~H&b2?JCVST(LfqqhS@1=K;Rh$4fpQGrVbApA)+Jx&UayrmwUMjO!z_i}6D9k^4= zK(eJ%D%5x2NunAo(KM(Pt~}PzpsEb%?612CAy2Cw*^b1!Guwiw{8B?mjcYk9wzcRk zIiG}QP&;t?D{$2nadzXuA^B+!&wlUssf#7{t@hDCk{fiPDp2mA>Dz@`0Xn5-cP5@% zrJ|&$H*(xAmLnxJi)~F3;?vj?o{3Ml4uiu5T$%LL(Szwt%eCI&xFj(KJAOn6sJON$^%F={KjC!qQN9gH+UPGa@1q`qiM5;91P~&SsDOyM=P$Up>;E4VS)a~MZ z$tiAq#o4JwWiGgaKcqNZ6YZARH+7;<81`Gg6RSY^L zj=c^R>d^w+45)L*it^4pyuFVvFJMXp3bQ>af)psjah#`^dYhuM6 z!T$hzs9cJFN{Kz#)iE0?%E(qveg6Pb$nxF7$lq144=O(&-uCkP6peb_H@oT?k2+qM z5k(+FpF`q)T4odS9+a*LPl!Vg1!T|kPxI^%Ove&Tnl8Q)JOHOu@llm}uRE4PKhHm=&3!d-O}>_k(px%9N@XiF{ub$oY28;Wrp_?n zr@mlfR1PEA@lvfwnx@NS3Q(05b;K6i{w4E@yYELB@r?AQ{{R*lUyN+{jY(S`TF0FE zSrq{#M!0vnI$FY*>D0tS#={sapok%U@#HC^)Iw%g%SC+#FWrwXt-Er9a$IlG;Xf$co~Vb+h4f zeZsZ8Uj3Xu_=Ada?k;Pz-XI%f9k822goS+47LcKt%Cs5cE-kEBc&i=O?qkM{qGmV9 z7Ka*gJCNnLjeE~AyF9`@WfM5c+GS_C`e zQe1hPlP{?ain;MNboCii45YPls9Kz|;gJd+DZ2gg0>TS-*YzvlLQ1>?*a$duEl)IK zkU*z%raDSmxIU)_X;X-8LcZFJ2(br>C*m$eKbvs9%s$e#+uTJ*^c!BkfGxx~2!=L?884atNcG zV`Nf)coxY3`>BJSrpkO}BsWw3;7Bj}ta)v^$~eNngglicY58SP)Kffpn@gwn%JiGC z{m`Shyy783Tv&W>w^}ZhnNmaqDaD2K2EE;wl78E%sBujhxzF8R+nnojwK&sJ_MNs5 zlB50dG-M&dm1aVMfdpVKHp@u(k>TAB8c|DZ^Y!v`jWV*kOcmy2vj!qnUZS6NF&OQ) z5ay(#+|r&&!?(FRbZ-l~tj=#MyKVVOTMsmZwM{*q00tP(hlPTrINY1e%5=sPJ8ZkNecw>q_Xt*oBdnCFW6&Z1 zNbCTL;f=+FEHr5)OWEWmeQQ98-#5kPz`qFF#Wkj{!y_z`GwLuF_{O|xRMPh(==jkz zT;==OB!?_Xbbt4%Ek4nSIQH;7%8HXbd;(`~?rh#=i!4a@3w-ui4m!BsBOTc6xK)rt zZUU6$?8U1Mx0y{<4{~m&^--y$=crxs+Cp5mZRtr*W7~GvzpjmpSw`1mg+U=_@@~nQ z$RHEy8uqY0e~&g*#)maIwcvt&lxa9zZB~8am`S{(=Fm|e_T^}C<+(qC_B_z4)Hi8% zF+WQWj5==BG|}!+@@~eHtYK&Uvhll#CSRvbwW)cm#+!SK{_;T>O`BqFQWQxyU7;Gn z5oPj!4=fcc)653cs!?rjTjOWw3R1B0J-cyV<`o?#;;{o*f-dhQaC?DCmB7+ag%1gj zyRK*{KFw^ZzfK-E1$)X*sX^WDjB(J!n|B@Q!f(Zs1}?(gc9`-hsxy0qiH!w6VkV^`TWFn`yf`f_pS&`$SqFlQV zT|=$QVY?nq(5lAPJost>dxPwT@d?sKDzW7?cei`%@Ucj@#Xf7#!rR+5wtQL;(z z8$RWi*7g_q{O9z?wK6z72lWaSzCZNP2BGRUPzR|yKIQs6>?|^w&*=|pXC?yz;ZHQI z{{SKSV?u~?)TO~_SHxS{Q%wg>h4Wk}?Cy9Ed1tcXxl+V8j zd5%i?aiqEN?6RJEQszFlT>jB(Bocm{6gl20q>!+Rgxr1|s3?TUbdK7TlZgn1iZ*wI zoT#vRhg3p)K~5G;9&}oKq(TV`S?(EN$~gofwWMiMl0CG?sE4Sn(-|4&evmK}O8Lvh zhO0#^5Pg)#sJtYpTL`Qap33__!;eu1nzi{@qvBeJcf*fSd(2yTP&?21IQ0{cG%dqW z2k#%OVWN<41PN4B5*tF6d#lzOI0uDZo*TW#bm85Akt4SV`y zb~X%^+N44uNO@2kA!e1yUM%p>5co`wj2%Lm+>qUQ5zO#9Tr{GL-I}mMm1B4y{3JK~ zF*0HvDu!KDUCV1W zr~rP~~#+)uf+RdFCF)QK#q z4QPy8DM<=6$k(?5zyL@Z*NSaEnF=b>#S`C;5>wW=c%?|qy5`2-420=(=PgPJ{{Y7) z_27g508#WQ>nTT&Qw~%q3?UCIG3;J_F(g`(j!;b#Wy1Jb9SwSuTqRWfp|nAtodA@& z+N+fmM{WaDVbRPLl#7rl05V7&;NinqQR^S284q2WZNeCBNJu)gfO{~j+TapSY)6{B zKBc+SY3?2g^lh;=S0EzuxOn$%N@IW8*h7gBCM-uSwzA`3cqpU*3CLF%XKyy`)vi|V zSy@KN>Vx7JLw8ekTHj(UVYhctiZC7PpdYnTHSev!g$*~i3Wrft{>*HKCU`6?MDmEq z-&>MPVGiD2S~Kt}g*ETYU`!24m#mckmJr#=tSx7rgcuG3^&#y9&4d)xVolG(SpZi{WX z$A-V{Sxu?nbI8=CiuQKlQJTj%)V-DUg$jFjmv6cl`VT=y9OiCL7E@t)S9tq1snZea*`czE2b0Gkux`llSX2t4Te_3|)Ev0DP4YRjl2NMd?wqrVm zgUi;5vn~>D9)g{6a(KA5(mx z>JlEvxAH8gk3r+Mq%*eS!;E94B1k}5oT}HYRBQlp%D8d0J9S}(bDwS~UB;zEcN@BS zjm8)+9uG$f3VU@bB97Y+A=0f4mFfWW_m5@? zbE68;HcTJ)AeGN;+C$QwO2(fN1L*hTI?C}Ywsvs-6+H=Q$I!b4p#U98Y*#$9&kzA# zA^4M5T_m#L@fMsykI=AiF-b=$E4EfLn;x^-E~jeEBvde@)_`S1s})_`*BTXS?L&a; zZ>2l^D9A*k*o zp2Le>+<(F$W|&FG%(vFLR8yOY)8f>x_s0}ThO4P9kMM#JwWq1Ca3=!z!tjx7LVLcP zBSfQ00ci-O4QqmsjM9K9ND83LQwVZP#>${5Dl(}37(ztt7b1k=tcby2fTL4{+(J+k zT%|eGb?btZHAR%1$fj5~RZ+s!>FG~)0lE~Sev1k~p*_9B4IC2nS`?@Xk%CbI0pH#F za0RtWMuQ0zHSm*xAt<%ct2$QzSP)TZ<$Lgv8l%vye6?T#DPB?lr~sdC01jvxbU}Gz z_)pt`qB7S=Pi1|W7PWe-Ri=lfJ(w>_bP5eZsiqTfM_PfT0fb9vyt6MLxA(C)W`-f~cP+uR!r_LE`ig+;!|I&V$lZml#ZF29z809dE1CO`t6+PpZ-(&kqP^2f_KtEN~H1q z)zL#4@FYRk-WN{@`moyP4Trwr{5KzBQY@C`#hz0zeGXCe`_n$|v#@TvP2}D!Y8u

rn)wx@=A@o4vc|e5{;X zX;K{occDOjj8!fjcW!ATcr6mr^2+J8jnt~%9_6-5K3|+-JThA^4=q6VXOY1n%EawB z6lw8tH(FGB61j4spk#U~xlrOvI1rfhm4X$TNdlkua1L!z`qze#GZj6!I2t2RtuRC@ zDHS~{g8+b~TTP~Q_Inn)b769x3sKadgdq*2t!q+`M5Q2;?7^8BD6-4aDpK{TCAQx( zL(V*lqM;zIsR*i=SxKN$D@udZR}05VDBGDHbVed7RmSTE=^@Iu8bd2vis(pjB!OHn z(_K<7eH%oda#Tx1yM)=ShwgG@NL(04G%%Gsr6o$IQ`|7!?+w*BWL{>2g%TV%cTLYf z%4<}qG6<^3CZFKLWzy$EhDW43CMf-M^ng3D<|$8gie=pk(~eWyC8cVfWLD%rFe zxj{Tlbn2ZNs+)>Q2mz#_N$R27@5WB|EqSGC+jUZPD?$`#rBbvV^uf`blocK_GfmM1 z_jyaM3FU-Nqu_*XmRDL%N`UHowdI8?Mw-p@njHJ<>Jv=8oOp9p4 zeJTno$;+r|MBlZ-;Bb>^o zo?^tCe6|`5)}uMWFBd%1@;!oR@rqL&`M~P6G9FWk$@$H zwOT1H6d<;K@DCU{53Jx25`dJ^j;1`<+i@;XxuBColR`4>z!rq!GfBAYvZv7%}yglQp0hy5%m8cEKo6aYF5Ekr~zQzVk zxlmUUg}|tVvQwyar7)u%RpN#o+$ex861_wUQn(7DxX}m!QJ@t)*i=SP7p%Nlh}n8; z=!n98CtOfS`+DG#TtyhGjS`VW`3)K>m=~^1+xCrZz)#|`5I?jzH(%0mPs#5%BGH^x zj~}%sl-2qL3T_TWzTcIl*`=tbq*QoNKCDo0w=Kod(CDw2{j~%fwN6ey;OTcSleg2h5YC{{RsQJ=oUq8#czzSNrbzmQR=8`77Fp z2b9Cl_r1Kngzda;tHt%SMUeU;+gPB7W{lb+LuCL^B@HwJMFw3fiyW6abhkjN7P!U- zbvxd7iB04b)}WscVZ%}dRG`o;wUyH_npUF*4k&~zyKtFa0M!T*72BV73<4UhysvLe zD;f%{;dIq|9$s6db*@yVJiLb;Km~;Pkm*R_FCAuq?qx%5F@VFDMj( z6Bat%Gz#mTF&R9;p%*?$f{Wtl?Yq{|*!xbyG%D?8DK`d6N)mt@s5R}(;fB)3%E@0RC1LI+ehJ`;1|3BzthWi; zcz)e&*_U}zoV#v7(?(=vCscGk;(S#U?5vw+YY8m$BtITc!8z-pNX-{=< z;kL5VQks11HP9pRr9g5+DnvHZLro<2;ibec4dS1}bsLU~y5gbk{M0`LE_up^W4b6oy(s2^KdL zLCHh9p3DbPC?rTzT3Ax)wj?a}d`iQh@HwP_c}>U7$+q;fv?Mr?PqXyle=(jY3tvqF zLYB?mLq#WLyqu4VT7PaFd9CqCIvEJym^Uk4;BaV(wU*sJEo(=19JXc-X{+tz@JJ6> zHy4pl<-nC5s@96v>%*qq#CfJB&QAnIox8fKK(QvBo-j!JaA9*7iucLO;I?aOZkLu4 zn`mcAHFHux1P?#XyT9l-89cCY{4PHU;~4GAa5j~ahRc)ylke$=jJb@F zP@-nCmw+j+Bab%CX~dK{7@DHw#9X{_O}V^rx(kt*ZAejZR4Pi- zT6HzVoLN{xgP#R;UMZ7%+V_h*=OMU&`r4QEDXAyeX@@O+RBM<* zF5oHZlkLk21cZTM8XTgMbML3ohb(rSPC)KY`L0Hi=$zZI;%ak1FhL~~RUr12fB%H^Ok+krviq21ZEFZ(v} za@$*?-}pA-vb?DGds~Y_oQ~>6N=a;}cI88h_9r17#6np0YnD5>F|%4s=w%dhbSaPW zQXbS&loY4;wI@HU;$k@qRYG$IJdu#p-HE`8Dj$IPm zMpCruM3pN)f}=092+1EL;weziNDYkzN8Ek5Fpo;5T`ffyMDLqob-VJ#KpaU{V%#(~S9gqilJ+z=KA7bhK(`3QhI2{|R{=OCE z)PHSPI}twnP_M%z06WmIN53p|NOM;WpE;!rnO7+;X_W?2RGMf|Qql!}j1sMKS*A+r zo$8A{5ZaQcS2p9gKj#H62EnIIdO}VkzIG!aZADe0Tt(Ai5*D~L^P)>h4k!+4tNn+xDpwUtNc8%YX2OO6rE)k@u^md(IKH#zxsoz|^U^ z^Vd>ZwkNu+Zv@;`^Khz*H+Mpl@c2=eXYk?2kkwyx$*!m=mz}_<>eqI4V0=SLKV}>l z8iAJpnkLD=-qJ!*EoB{^1fSJ}i$Z_U!Zk-5-4~RoC+13l_<=%yRvie3B#Yn#Ar9o8 z>y%q9NeA~<*utF+NE=a3DBt6x1cxjz*X{Cv{TM{Xd=g$)`HC>z?jRZM=0WdDl%Lmt z5rN4gBn1BQEW5H^ofU@IFj|nXm8~u;goE_s&TgoUw4`Ok)P(O_P?`Zhi#_c+)%!45 zg$j!jIPwUyti$r$QZH}orbeQlPFPR@RHGz!-3mQ<=91~xJ!{l_PE_~f&S(n2$lOMa z2}C<>DIQ($v{TZxr`v*-idLz)j!<=FR_JUjg4OAzp7NkSP7Xb3H#qZQM_Oj$!d(4}dT7XxCMw*l-;+|USBDCc{bxowF0_TKg+n*A)cKTMGDzw7S zuiPXbu;DuRgtK5jc%%tRZdP9=(;B*|2_WF5go-DT65eF1UB_OgU~$AWMzL^y+FQ@X zW-ZoZra@fKBBRVv&1qVJ@f4_66pY5r`KMIK;q>rVj0XLa9NIOFca1<$=PPe}ZzJzH zJGLgmg|i!$(+#_1wV^^Un9_8q-xg&{>P{!{*rRCxjJUp>%B<#dPQJKwx-gzWSkFY< zXR!BABg)^rowXg@8z+xLPTOqGqZ@Suc&v(CTD-JS+Ymt#_{D>hAAvw<*hIl@ejYXHp=rq%<071E5M0asXrp zOmf3E5Oclf8kOGjcUhM4`;j!X4*;p2?fk@dZQl88gSPCCu$iv;366$L=N@@o5|kkX zlaa`0i|wy&ZEc}1mGt^nEqUIlvNu9FAOzQts#)YuSA6lrcIY=-ryfOx3iG4ouJ;RD zthD0YPPC*cw-PlFJ`!u}!EqZx#`)!~l$v@}hT`8>_^5=Bw`d2&shwc_%J-G&=vHNM z#h&SNn(7((Z8AJ+SKbmpgvYw(S@sk21|{X-vlO=2Vo_(5tqX={#OJ@WvYF7cL^tUpDSzBS%em z0JO6zHNxQzJbOrHJI|pq77Db6%77-SJ%bY7_aLfQtt@FScO>{*8&VbKY95eR371xu z&=IJq0Xb#w?Za^A4OAd>&S+Kdmc^M?)>UsS4>+&C3QmxH!`Xq7g{w^1Mqx4P;1@o( zcR$JT?3#nWpM3lGK1OSaR+a-H_a z%t{2)%XPp9xxz6)cNNz5zseq^tttgYG3Pd@;86#lW=WvJ9FozL3ecrelmvTlLsYpy zs;D3w>;C|A4IF5djryQg0Hp;mmZ3Ro$wi4!B8E=7%f4|yyL~GX1!+(+}b4(atP~$$U?kLdjO^ZE9NZ? zXms_#fJY^2brhb@W&wF=nH@mvz)>5lps<1ksYyM#cVoyotyGW!BeMWX6@^fC)SP;> zDPJ)Hp{S4t!;c`XP(>tA^sh`J=+l5!wKz(MIzsk5jI=1#T=VS5kRx&BFt#7IT_HDh zvrZak+Z`1`$9V2g>`>{44lR|P5^B`V%#t^WA~bMSD{E)U{2my&}7$Qv0&_az3xw>`}(o-xn>k@>(^O zZzOr~6i6QQ*P+A)D{#qb`W-^CWv~%lHoOmtvQ|6YvvI{t%X6V$@$rco+daf%QpROs zx@avcDaqnxwsjm5=x03C3VR23EFPr~)VHRQm&AUY3I(`9(4waTNa#A(>c^2os!@D- z9R#7`pzXjF<&>u|I%@q`@m^k{s+#BCFpJoP6ef}hC%3Z&b10NcEZ!x@+fw$kQDZ6v z#^A-qOv1uU-!+C5!dgqmG_yr&C<299;Wv+Vhzn}!o&v6_T79xWV}eV zx(GjV;Yv+9YEQziq+#u*R(L)a6x$(yv%762U_U~Wk;~ApXhcp#+A()4Wg#lO$3h~{ zK<@;2N&2y5i*!LddTt{Q^z&3JuG~#-e9IY&Kgx9ojQ%U)zClWVly{Rct`48O+qZOx z&)4`+^=N*)F}>NC%i&la8t(#z+k0nmMwc*`Fn;qj9>ogoz7ga-ilw;JX)z%n_#IxU zF%%ACO4CtJ>{0T2A0=dJ&`U?s7KefEb}u3BxQ?$52iT*f-fq^jqWNkiJq)<@Q0~kT zipw&x#Az#Nu1vJIn^>Rp6t4>NY`6jP^AW870IFAoUs1%Q87QRSQ+aXoNAU!yohwW_ z^(M|owmii(S~(($plJuDrFWbj(fXRE*OAm{j@+*5yFVFGSxvUy%Z-@O z8chI;+o4Q!$)2;NrqfV*l0K{&+5;+h)OAy_iV6h_kO=;lW+=2M}sHI%6%v{tzDzm!v!NAD&} zO{GXV*SO)0XcNw8+s-ZZ2Of1#x)PL^6s<;y6azltk2Tds1tiIehti=;&u_O4?qTX4 zlQaPqp9z~q)o!{8WuF7qqif4Q*g4B`HrACw6@Rs)tvN?cWxI3sA`fN z2_%Nqr4=++c=sGdTsu_$A*E47$aRNXd8&Dh)TW>jn5G%M^-tQ$r1BNjMx>HS_u-_8 zb_y-q1lQZs3GzaCqiy^n3dEbev_b$$%L<|)@GHyM^70TM9*X-h<>jzKefSD5E$}ER z7BS>2IPy!)TWBY@EDaJzipd9|IS#(edaK<*L>>icx3=-$+SGAY zbomnFMz}(7Jqltq$8>ZlRyCMh*%}$Y@e$Dj3$XkWBE_<=oIOA`+H5;|zMWpvAKf z+sm2g>3z}ZRSeH^EGZYKl zBSX-*NydeY+ZHxMLDug|&9ZyDE07%|`AqN}ev`#6-}i>$+{$cSt}1pcfBBuc05uhX z+}kbD3Hk;nopw%xK%uR1Y-y>~_>|o%PJQ?ya&%dypgSwsk04p7%iGfj<7y zKOs7mmrStaxF&(3sqN`P?B3q>3u3+$_}gZCZ62U0_Oxf+LEDB*i>%P7J={xebY0}x zL31U%7&KIp*;P+bq!H7;xOa9M$i|Bx#yQWprBtcDwsdv!hTDLr(vO*o8|GH7gH-~K zT`-0|{{W)J?gLcj;|STajE%=l72w zWl2TCL}V7jjyXhfwv?JWdvH5Wz$($sN$IglJe&K=)!+uB8Z3K#K8Gg{x`q zdDgZkMBQ;CwCVzewJ9o96WBXGoK&uE!z7_-#t110HcSri|&-u%Xi*LueZMvx+itQKGO0>>A=z- zx`^oJ@rjgz^^;hmnhwmsWuh)Tg zIwc?Yl&GtF+V&OYh^o`uLM!%Q1)OPbpO?jG=KX##DO^Vue|Upjcw=KqoQuK%T#8#> zq_*NxdZcR0EH2ztc_a>0m6OS1;#q$|>yiBj9(E^Q2tO6k0y!=onyKf9v?SpSrxV zaFUeOR0kFtTS65o{{Se%uN23ZcaGxLTav#OYf_`1(Tf$U2ozNN+upL9@+Y-i-p{{0 z@h=M%TIys2lUmXUT?iQ*ISMLgRRF7tsvv?CI|--k!jZPAqvSxYVo5da>q=rlt?*G- zPt{3#2cJ2MikWgZ9PitP*MjSdaz(X=8(dc%T0%AqffHsYS`?)`s|sW+RO%GB(K*YPbyAP&k<06Q^j#@R)0 z7>+SUjsa&`YuT<|WRUeZ^GyfTu%!UPi289M3sq9-q$I-gqJ<5roxOYg*m}m*L(KXt zt@4@>&%CI9+%B5Rw)vu>6i|Uq!Z2jsLcEy?I;w=4^-Vfq!&yYt&#Litgww*66VkQ) zSVbqrUXC7+(exGS{fx>*^KF+v&%#$2<@IA6&&OTaZR-7NY}?cHY)irZZ`7!ab=c4n z6B1@8jRtnnGniQ3Q zMy|@@Cn1Trk}?|V60KNW!)t9Qd)w3pB;@2x{l+`0ZNgU9;&p7u^AV+p-#)2GWa$YC z2%VOK~;1i&??no5~yjDm$Vbz$_VW>Qat_YsyJ!s6nY z%NQIIQti#^%4#$B{;WBoZ&XdoXziBT>+k)T^$>?G1-t}m`L^lZopI_Ue8d{yY5_@g zDk+k&SRLZU4N z3z_$UTmeh9)kc_zkwBoO6&%NI8aQaAqdF&)7UIW_qKIoUK+R+j4|iNPN-{tKAt|?1 z0+$p5J1OnMkP=hRK~-eW}lzZ*695{w_ltXE?Mq92GFkJG%BQUU? zU6sWTDg=3EQKF^38m(t$!Sfq_S3T|^I+UrP;lOYlQ$F#qt|^dRPW(ceb1~(Y9SS{q zl0QZ+5aycSb#ox9=xzZ3(r<-*_NfqdkP$`N0MC_A*qN{E@32n&hbyRq=wT)VdBf7YlcvT?!tq>nm1)rE& zTS~&iB}STvrD#nnf;F{Ei4pegHyf0g&~4Ejn=)F^+*r(^PP*%hAt_1_fJr3fL4>aF zB7ud^GyyxY#LsgmjwY8l@>Uye`XcpJx}6O(#gZ)uT71Cu&Rke+`DZYrR~ARQmBxF2 z-8@8Y>Feq~g|FhjFPoO-h5lDVK;lUKRZRV->3@{>#^t$BD!@|&#sik40rb1%;|JwEs9UdMr6U%$r|HM6#aEn$8h?0 zk`T8Y-(BBaIr;eMt>ATg(^I-YzGq6_yMjhGk7;kL5SJ3|%#futg#ZAjPRu^lK@l1mZ(Np& zE&>K0&u%os&+2G98q&>ZEFmcwK$n!b4}X?^H+HaBja!C(ShRX!av;vms1+=*vJakh<{I}PT;tDW1$D1Q4iT9XSB^2pCeV)|rr1u?h>4aak?8wri z=_$;6?M-=^Qjr{lZ?x)XP%%-qP7$MMo~QbM6RUShYiKa5WDqk$axEb4R!W zWL|cD*Pkn0rru#P-~lnv>XekIp1*oY#I!QXG;vjno5CILCRFW}Yh_Dp>Q;pjtHMF2 z(TO-G4jLpKke)N|Xpmmo_QVAbD5wM5L0_v6dXmF0FN#BxYqzi#5ggaPrv>W?+*h)= z@k=&8!VzI!yI7ZXZPjaWzzRzlQk}=M2)(YRiCc-C=5f(TYqM?p(X>Z_vXID>0#sWI z^H!oqioGe<5z;-vqN+~O{bybYUh%bdyE1M2bNNyfFT^z!T!)ycY^px3X;Oip1bZlC&JV=smRZJUXfTXQ%YbgjNHK96ta>I9Ix=y1+@&=bQ)kRW_yJWd``60BX~;U-gBg+Ap(7aP@3Hf5 ziE`;a+ur=PEC%x?zS5kV-<(<7_K30V8)@-)bIePZLnwyARGNGg2NBqvs@Wy#ap0tP ze{r2{2Sjb5D!ptjedJCX-0*joA5Lc1xL~bLMt%6LOh79IDg_S$GfIf4Am`T>oc7JL zy|?mZtvwnngKKv`BZ<26;HOulR4M0req{XL%66Cbe=ltfxxKbE%{p&350iE$p0ubf zw~*sYQEk*IAf-bTjCW=%Z6p#v=QYFWTEW7SLF~=JX>ook11h>_ACcLeYrI+F zY=|%2EU`lprhL;+IU!-)DSfom4M!th>|xVwxjfFQnjTB2Z8vN#i=#?~9CEhE+k2@y zs_DLOmdiA$)>w$NTpcNM$d}_QPLvQpPOh~$w&1R>EoEyWVIgOm+i&AWlwlrY;)DeX{{W%}DcgoRYvQ_Rw^MaD)S3{I zSGQm&3YPLeXzax%i(!M}O=)g~>jWI@oel`BF^aN@8=ItyzR6&U=TAPq|>Ln z5m~|48432!X@=;Z6~Ci*-HT}7(L`0aZSv#1>H12$z zT@|Caa)byCNK2OYqp7e$Rq z$d?EQD585t5DnEj>CpzZ#XKWCHV8pLkVjEWIB=*!3ecn;%puZJok$fUPys_B(*;Ha zXiz9=^5k0XZF1xzR9pjwa>N{l5$D+LK8igfM3yHbkRRcKO2c^%^h6h=X47$q0F zkV;JtehNZc&064&ZBf|zt#FD_W`n58ryikMY#>B zDoT>1nkfrPs6huJ4@_fBl6x7cz=gC=Zw=IZh&(^RSiPahcKhO5k2IzY$DvU)ZD;x< z_xEE~!0wpl0k6=kt2?!%fb}z0sXxnaQ3W~nV&DiWplT3G2}z>*9)^{`qg4XB5|w>e z@-o6wC=W_>$lxl6se4L5u3(PLc@=F#M3jsQ@)O}7pkcL9m{5!{B2&%Aj)3HcKXSPw zcazu#J<-&>`2$5tP<80Gwu2Vu3-cFIHs?wJPgFJBJI`(xC9N82nzAQIYIGh6ytR0U z29*7N{4{%NrK4yFsD4MlL$ISWpSUaE~# z&u(&d_uAT8wBKD^5Kg;XpYKshPiL0xXg$=$lE-x2z$w*M_#IVqDY3D)rmkQP@fshg zJD%SBZ@W*Gy*5CxTph_GW3*{{R%mUkY4D((e$ehiGOo=C*V7w0`Tg z`u_mnnwNe7u+cwc$oAIWVbyCOK8|Wv9o;L-Cv;xK z=6ZOA310S2{Mn8vnNej#ZL(;G+fiuylq#H1vO9YvVs!+CrQCaBS}ua#+E`9Pq^XyH;j3pOs2Au zJ|Q?toNbjf;#8ey;p`(4Y*ErXRHDjMSYehF66201Lxg0KR5K&lg<}b!p{-Mgb>E`R zy3e;+Ur@0{eO^JPgD8LjdD4k{AdZA9zY@*Kxx$4)zY@P10v6>X0%ApjJq>Dz#+ z1-eB57!~4H=@mWx%m@ezsDvmK$vO965Udm$b*>BmP1e8-IgH0#c<6wvkZb)P*Mk-y zS`k7GH2Zqs3w2(qz#4Nkq3*)b#=Q9x2Yl_sULlEX=@X)YP_nHxKq24)3?8^ulRjvWeD1^eDRPyf;1ZMKZ1{Evq!L zj{-C938pF6S&OO7DpkVY5p4j_B|WdkJL7pTRg%X1rfNLd>ra;*0QD+y$|QYwe_^&S zE_eg$O)^`a#?X7t9-*mIy^quO+O+vj@14>o$xREEOHd0Zw@RcrM2~F&#rqlCh?qf` z*U+=fmvSbOqTQ4pC-y0#(Kl~x6gI}d+Hm&Dkx}@$Pt4pGQJ$xt1dpPayxUpZTMn4o z2c=SPE^hA~S)N=xIU+OW+-{N`mvory=`Vaz9a(Wl=~s3Znnw|-Nx^9w27whIie*wd z921IIiY$XsQPTh#0v@HcD^t)685M0pNhvk&IPwLuNj(Nx(BsR?vxywa0~GSxX8b#Q7;lZ^8h?jzNIHRx6Hq`GG}Xg_>o{oNO#gNtD~Hu%}W< zsT3+c>{X_d%f=cjcHOshyO1>a1$^y$a>(WGBsVT9wl>gR8cD4VoXv5HPkARvG^*?y zJ1Bhc(&Z^_`%Q=E;xenWtE=V2r7Af9LVy6*vlZLygn}}9XcfB|Z3&z-8kKI=gtq`R zwvx1l%0dVPQk2Gd<{ql8h0)P~CWLsn1u=t=+w(Zg!4Y#q?~44rZdD@-=@Da148) zz6iB@%F9eT7K!BNLRwJKp0Bm<*iFc1J3}K7}%GB zP?dN~r>3gY4J633ntS+ch}Zivno6}g4L%|bPt-B*#hJ+%VqWekvvJvfAf>kY7F+)S zj!Jue+(-CkK6qN?TO=yrw?8AAki#U|577detNzTQH+Lh&PRD=}~LJ2hno!-no ziSBc2RE+R7hLlehEwJS6wL2GNgEgmvZa5MuQd3%tM9FYlB|4M8lxyOsYHLf|N&u}} zVcILTDe0HH60EtW?n*tmG`7jFWm<54tVlyJ@lZEd)5sKxxDZk(sBR@gwLx|L zSeUhyr%I}&?W>zz3P*XkN>X&7BqOl*;(FdQ#Z<0d+M><~GjVv4uy;biff6 zmO=P_!G(hgijw8EMAk;FdJ-x7*pX~{sLAF8aY^o)r0R702sn_5ri!a_=J=v3^9EjW z!5x70!?DFzWFnfBLDf5$G88G-I`zZ2lr_Ve?r4(1BPUhu?8J*rR5YD5MTkKwnd%*t{n>5*04e)c%Z4EAOT*%SP+b22Tuak*P;s+ghy{{< z%yb}JKOi+(aW|HmCC2rn^-!t(IQ0=s#Sm1RqG|UoD!rjK81)f+!hl?hu>}f1vc|aP4nD76-IOw6Q7o@qupE-N2*Gx$+fml@!$73Hhqz_=Wd(B46anGTnLXz zdAQAx6zbB_4H7B^P9yS}`ztUE?+mXBseC5a$z+`2Z5e-08gdB+WvX-qnDZhkGyow$ z-oJ)av^v7jq^T=PsGy-th$4g>O2c^*+8hprLS0(V3(H&xAr$#ERC%fL$?YJD`>HU% zTT^m{vwFJRsM_87e+je#4d<{EP+t zL<*-6<}TSEKyeL76Fy=)N=j0Q>Q!r_DwErmBpC!ll8d;?&s_D9rb9zsk z$Z9|0rjJi{y{&QO{j#1mL&Qz5!C#q1D9Y zYlE_k&21ut0GACw=sIF_ZHs3|&_-uF<>aWPMLmb8?82E1wIuNLJ;f!5-mzI+ z85qtyJs#A4e*Cy^_*dw+Ur;e_GH;7o%nRL~HMz7Btw9KSJ1Ge{&=G{Jam}-!V@`=Y zj$q_L>mXNE2fwrTHl0w@9`LZTf{0R?e42KIq}G(hwma}!Q=+817(u0Kn^!-dvxgIQ zZFaDOMWylO0DbD9!P_$&D_s*&X!8D$Dt!o>a*L@;hrjbn%9T^6M zP^piMH(uC9uII)Z*~t5UW^a3~x>fUkw*C?Q0x6g|G&QFk6H;wip% z-ma02AUBa(CeqH@%K*mRfY>B>CI0}t-XB(qbM+hP>xB5fW?1+433{Qob1W;YmZMAC zMRxOZXUs~EnvalDd?+cRlD2^r>xz$W_ugjy?a*G^7^$yS4Hlo?4%KapHH>#StHD23 zg}M{HDCX{+w(;0J*$?SCx9YCBWf$Eqy4YAc7CFm?>Zg&7oL! z@h#D%QQ07Lqevg1V7({|V4&N$Ij9epNBN@*d4&+d73QAFpXk$qBz(`|{zmNHOgQKT zMEQ;-@Se`xVis3@JEq`x#?{i9ENGKs*}Q4|M~|(`%k;ayPY|)rcAd)ua_lz>(KX2K zxS5dMZSNyXtT;6as2-h}^fkgR?q#ELCeA=w{{X2-P5FGuoG8h{;Uq_p;uJ?F^*N23 zezH38ay->XBokUGwGt28i|hfCNFLI%tM^L{v5CWqZ0@g+cIP{PZcrcxbSaMIE~YGhQBfv@O8DcD21g&6pvgRPd_N?b@F5)?DY;VG?}TAW&2 zv^!8h^`?c+eEYoVmrIkrb;97{o|^apI^1yTQJ|+&gND(xQVb0m4hnwYE@riHyBFQP zysal07j9asMqD$Sb;T_p4PFo{(=24$uXt`qFC82f zy-Wu~uB%?zJ+j&tRxOt5@Ulk@S~_(0f_}9}d1}?hn@@i3ExX3tfR07kw#R}jyVJ@H zrh95_E7TH^N~DsKK%u2ZHGQ#lc|DtCxMxI!YCIMlyfIc*nFqya{{S*JogL%s(J|rA zw58N6+z^i|Jyca|^adP1I<`rDJk22*>|+&kZp) zS8&bL@lc-U@|JfnbTP)XR7(E2S*ry6lu9TiHovbCgwJB}5C>^-#yMI{C#6R1#h`=p7I;~{FQ1E@8xw*f4b zS4&$aM;Rl*HF=&ll7jgZO41Ihe@CVu_aLKH!?v@M;u3De~6jmC~V0^K2kT6Og3qA5fvPL%-UHF$*Ep zUNNk5VF20UBx3+k{L=LJ3tB6t8{}JW|LL&;mMO zYeZ0LgDJ=WGVj1R)Ta?36_r6A`~?vCs{q#sxwJ=Xs=T0n5jP65KG9O@wRqKOr)Gw#A}Z4r=k z%OQfE3(Lw*p^q;j>g}h|k1rs!E4u_CFB#E!NX=9f1KWhWv~W;a>FJ+#6Gf2H#~*TwfhmK?aoMCBg2HsAHzpB5Qh(N_gngJcUk_!MW}q@(A0Jw4hTIt(3%%OQ zZ}(3T{{Sid2cc0uO5!d1!`J@+!8d)OP#kc1mb;ru^I*?os$GowJT4@R@--cxV-U}6 zi_CNV;q;CRe!y<4EVLeEF0Y8@Jp_J%54Cwp4oL9 zJvC7F*`WDIr@PD2QwmMNnC&XvX{nBX=Qjm-U3k~)3GNt-n&c03Dfq34J#O|TjlZ^J z-5ON+u6&iLpZUb4iV6(qkb0Aej9w+uH?8_LLS}aWg!yrbxi>Fj0J%u~F$zQ)kic20 zsnaut7mjFuyTMOl-64b*@knFZYwg1j6r#=nd3cPnG{VkcDBfZTNXn$=+Hi~5XoN_C z(sfZnwCw4I?qw6E%|*O*IMc~`JBp6`WESKhMJp8UDWMoNb4eMi2?Y{J*(+%AP^6nz zC~k{;X^(Pst;cUZ1wxvD@{|r#3pwZQt}VFz={r5y0DFBZiRDXQ*;dWa} zo3!V~+|dn7Mk|R95nti0KBJ1Q_iQDnR1OXvg=)q3f-bKv>8_35k)nzwS~u3wbxc1R zL~_~F^0HeYCY?v#3VlZux$w94hK>ba?Q>Y`*43^Cz6)skwP*=V&fMfBj;}S-{M8fo z93dW6%|7rUIF}IDYR3YVV_hIi{{WiHL#raX6m&V?@T!=77D6}kQPWt;&LJp*Rhb$| z>xl+7p)+x!@m88t^}^_Z$}$P9MkzE`5?6w1I`zQO0V^66-CQ>E%2Ta*C?skMp|3-M zb10N04Mk>DuziC7h-ilbr!!BZ3i)V&genJrQcW@MEqbZcyPPw(VOpZvgnN=)TJD!( z;*_V8Q=tGHk_T^o9(8-Erwrbx}x8O>MZ*p9J`S^rm}T zSAV+-CZr>gB{cp|3ipwLsx1-X@(rSMDLxOGMw9em%JOj8Y!I%o-)VON3gUGB9@B&+ zr%A}ANCeZ4#}0QgCQfuejsR z6-KI3jcK^fk8zU@1G6I9noGAwn?ja|?G=OtxKa*~RA>q9z_pbLDP`%RXHJgwzFSua zYEyJNkZWF@M`k1B>WV<-sam#J=t7e1v*X=hvnirA5JS!OrC?Bl+l?m;kP}5-VNmim zMi=_frAciiw5LAoIdkfxi)yl!y8A0-v?!q;c=!f@fzrP0Jr4vS4FMy#d_lr`9I+oP znkNhhwG_3}%PVnaiELDoeSl$?N?L#|6f}`T(;_{%PDCqo8vPx(OfMrT0N1p3VbDcF zU{{xju16kTTL1~6H1^}m$W)q)d3gwxl=fHKk02{;b?wvkzvOojrG{S~J2BwJZW1Yb@ZhOOS+QO3};+LF7YSVr*U!xHhn}XUc zD^(|JfV!CQqK+=yw1f{T(-NIWobG4d0L4!K0D6Kb#cHwHeBM-TbKRGl!k6gSC@%WYZO zf$sGY;z1XladI}B-4VH~= zr&5vur%Wf*Q;inc07xVg+1Y|2UQsI2N~%T@^3biqC_%1xRRZ50Lbd^2nDPWIKEB*} zc?bbWK~8n*a0m(%H_#JMIt(bsfP$!E7L_GQI#!YjRREPhYQL9Zt_elpl?YA4Tc}5u zGK8%u$T&x67*WiiMsd{? z^rnu}ApRhrI+Or@+(lY(SDcT+q*zHs0a>BwaW>kORePw7O>j3I@{Ep?r{9M~#0nsd zm2#FnAF(0sRJeS$P9ohYLZ*V6&`nMcjTI`D+yh7`yP3kbDumOy{_rH0v@$P*hXpNR-QBv&u;#x-Yb?CG$9ti}0?E4LTx0~2 zhEl31nslP6TxS@CfOxWC`v*Rj6Nq4kcrbb$j`!1w{l9EBs z9ymG6Y66lQuGOcd$5iyS?(MyIkMl5YtjTDS5a3#TM144on;k0;1vh7ufz<#1s9jSw`-}!s%dj1teEpqn+Ih;)Ja(#!@Cc^5O*|>;ei6Jn-#>L?+PGRwE*MaK2l$V z1WatWi>X6}BpOtjXOBA}qA%fLu2VFAJAwEdBH;}#HR1Da%)ZE-N@Kc z1LpEHNL2e$499Nj?#%XYQ^f8hcFnb=TbNUi7T!rA z4CVmr>xetY)ymCe(ioj=+h!xi6oe8gkfm3%xZ$N(c&D{^;8IKMUYbIbr$9-rW4E)Q z!N7{BL6gNa04?V=u<;R*`h2^4DWTH}9T4+F9eJSt3W$*~`HiunP z(&=WpZlKFab_A3(GBipRpR^1^%Q0zFVlJ=}y7;Q~7i%iqmf;x^+zug4y4g@b=qhm9 z9Eb5LQ^#Yy%4nO7@gDO-Ew@P8M^zVFH5K<^*~&yXsQ&=L<@6~ljy4tU#e8nza1EiR zlCYg5EE=YnQw(pfTOjvO1oItc=#RWOp%uq#66HyGk{WfzENV`n;x)pND9It{s@hZDcv-g*uRpH2_mQMft9DokczJn$}Su$a3Dw6rD6d$W;{pbnV2bbBRbg zNWeq_ZD?X+h2f?ub+1xI0)6;%5Od7|QFA^vd&My(ZtcAsFUoZ5yaSKHuI^D(NkclK}kWV zrE;jl%V;MO3Y8yuC9|P%=@nG2Uf9|bUzYllHMOnRO4OIsl!N9PP!7y%7Wc9>qKlrg z7Pjt`)gr&NHoyfanKiWz(DLyu1@x3v+l)*FCa-2~(g+v1%zJ#w{*i~@h`+%>zXu86 zQ*Pz6Bb@E~vQ>YDX=yYBp;*M+8vEzq4QOF={>{Lc4v~df8hZ8^ogwCulhg_y(}AK(%qWx3-$DgsAuT5)(E^|kb`I8pn?B@(K+n2Vr{}Uc z6Qc{z1e<1YLPnV}ms45D)H6sS6&3ET6%w=!Qbu>yfGJmxIh&Tax>e$57jG49dxiCc z#k*W1$Ar{AiX;88rlv@Q1d!^CeQhaUhC*zyL-z^uB zX5D25i_TkGf#s+nASpyCbq1Z-inz~S)`65$c%6reb9%h;PlC~#7YD;azrAj2l<-s& z8Mx8Vg{d8tp&$||THwsOzL1UqKARPUw`~b7AvPh}EzR85H*M`_Z+9skVqw^1#5WR# z>#N{vNhAT8#Er!Bbo=h<3b_Md^AC|9Q;#B*3h#CZP2D7Hi*2r2mPCkBptq<6E`TMu z1xNLaFafd(my(y3_E-q4O=&{z$2X;}avL_QWu7F2wzPRDg<4ZlszMDb-G|n2M^0(3 zQz0mDG-yMokoV-cOj}OjOVC-#yKs*jBmg^t2TU}$uz{zj=u`VW$9*JyEYvGs>Cb9z z>$dya)35tl;;p4sJ$1OW(wJz+TPmRrHd9KH4qceb_r_{ATNXy(T)I25n`ZW0lO>)q z*!*}YcYk_0+q=t@r`(4l$hYlTj3IDst9=qDwyh*-*7bZkx@r}sAh!Piac=FbgF`Au zg0yd+%jQ2Ln0%64bc4vIj@9%B z_eStU3mQX}OAGmS#F^aLB?Y6>iSEBdd`oV>TCMPRY?iIlE-l8A+w!9={{WRzO59Ql zVIxqb1xIciSa&NdU?jGto(eNF`F-1ryA2Y6)5lb?6VMN**K9k;waBvNU!kq<<@G9T zcG3`(2M`=apAk8voI*|AEbC-#8KF{N{krWJ+8^Up!_t{|FUxy|w>7>zjy310io7kl zJ!ziIDF{QDheWVE{Xsle0p`qwYF*Ai}B zhr2fp)4HlW`8#%7(O#Vydf0c%RRYp_(L#oYr%ZHy4?7k0yJZ@64hNFrUB!r(8+P~| zuR*0pG{knvGca1J2gEz^G6+1CeYdR%F?Ls4A>I-TPG_|KX9}0m49L6BG4&09+Zeb*}9|JII^^hGsT{t9(^N)QvZfU5QcVL-C-sa;x}20lkJ7Y7 zt=(P~H>q6|6ah8&;G=>grqLW#w&K{yy-5BoY5llLsEu$cP~%@uJnzk4iM_=(IDF{F`SB}(v>Z#Lqn;|W`z-nd^Y2<$TP0q zK=%3%=|xf9TPr7ppUJ(6(L2&O`i}$0ku*)#qtQ)}q6?L&`!Ocs9<{ce*}R*#WZr}w<#72$?0aI=6CL;3ZN)2692!=lttq0r zI%136dwGrQaGi0bQM@mD?j`Fye579r)HOv9Mwx$C_B zJ8~VCj;&ryPA;VG>Gsj8cJELQA-E=E}w}<7EHDfvHZ(Bz+ zM~-9fJ->Uu?Ym@lV_f%TvFNI9NlR`eOr=gFqs$o`&rD6teQ9&=xaifKziZ?y&ypKQ zxi5ORx9=-H!t|7_(P(1RbAMzcx>#?^OUp{wK_wsz=|vxEv0Lp`{4?EMc}^q;jy$-l zuYb1X)s!|u<(&?6Wd8u+s;1HR70NqqHm2HiED%D_O+Q99+7->;DIHT$>6B&m%y6YS z(b}I4d$0{skwNbRRMNP_jU{dQjh`_BfkK@+;LeB@J85wC4LBj~dsZdUGT!-xM?_s2 z00~iDN3#v6xo7k!##|IclIut$wuKN0>S=*^qP4+tWMC(Xvp0W9`|{1iylc!i8}*%yzsf06 z>^9RWLSt=siWcfrkSL_4z7lDQ6}NCF~!mGD*KSw^V-=(a`&_ebppDI@JqW( z(Z2lN`;G0jVBHo;GMI?e$0RCM5#>6t*aJqQRjAUKZnQI0^l7{};^+7d=ifVW73mD2`4Ein8%?@MPPC{3E2Q-pOB37}?&I?#jnqwd7jERo zPvyxo2~0UYlsBXHE9T*ax^~sl7TrrryE4_$0zwk*V5J28(pNCmIIyB}=ms3kyDj5)OM$Pc zH@Dk&Qbt@UPg+H9(`PRkmg{o1qJ-P3-b4lrnALEwl__L+=|B*3_F+cuYjLi=#KJrs z%lHB934YV)*PJ&sz00k?W(7oP3U$>vCOdjsPw$;d_)kD^((id$Mt$nk`V_^c)X6!u z08`esw0a=tO~l;9+qUFZ<-}X93wq|Vht{_1s3deWrE6MK4BfvhTf`q?m$%tjCkl!h zdVj${b2t~1b_8b=DKMQ!FWrUKCb+k#_htszCUDit%n~N+iISiR^ zIOCKlN0^jk3F**a@n$1)eclOZq!=JLJSxhSGuwNbUG%6MV{<;| z*!)k-Tt94{>pN$h9k7kj5h^rTu}w2t$mBJXrCsALn91%jQ9fZQ9`NA0Vh2hONIvRo z2+C3bt#srMyAp0~RK#<1gv`ELeq63tB~8A3O`eq%(VVuo_i6Wt@%`0U1M~NYoS*4K$I3gMr|M z@s^;J0Tn%PljsT{aiY`;(x)-ogh)WsRf3e0LTYj3WD0=?^k9-FAvv}l-nMSd{j+wR z=JuVnXxz6L(C#~dVLD;7`t%nMm_l;)nhxA3IS&;Rx-wH)*hw@^>vI}N9yH{vQA zHf|iTM%|lZezCeNi=Xlefa=)!)RiEqE&{@|tt6z>0Z=K43;b&%XH!j9J=yN?-DjtC zR}h%o-(5bnO$&J*)7gsrtD=BFEwE@Sn8HX~#RRGYm(n}2C;?x0nfl3?+3&=GDiVm0|2v$70 z)B~9IcjMhDSc(ruf$?gt1Hh5W)|~Ts0h_a zHcmiwn;Y9BdRdv@{CzB|&f62oKP<8c4?=iVY&e?K6q4Fv34E^1~H&=IknmRn|p2(4<8;00|Do2zulG<~k+s>Vb8P?+M z(0ygLH69$o(RSx^dR`&F9S`6g`=|DOo~2J>PQ7i(<>)WFZkriJE)i5MDoLSAg1e7U zah0-Ln<-BaLf52%3z+@j915OxzcI#~8;QD_5SwcKCwYeeBpUdv z?>Dj>-+B#w3S?pCCC6@DHoXf zxKJ>Fk#p!3n3Z|k`y$HL+u6Qalp{9g>E3#Oy-9J_lC`kiYEp#Oln1jDt(B3-RhoRI z#pS_thn|5~&B3|lU5Jd^rJH&QEwB!1HTaj_aa_E~I!zm(S|&FQ9lL6EDRXV?)V&;E zFO`XCYsA}X$xo5koJnG_b@$}ZC@ju=hxt(I_dE2*oeEJ-YnMI3Lv4G32C{o@l6Zy8AGm7(L(s`K{#->a$uV|SphNw;wR=O9dhD`(7itcc_0 zBZVL6XP41}%?>8;P>zG>NyjdJ!&bOT(c_`^riIJXi?>v*{hPRL+nu#$!)-d4@~AVX zmbz#3;P zUI^yZd#Pj`83-#ac6$Z|0di4Ad?K{26^$ljMny+$NbSO@1b#G!Vmaotsw<*}GEhB) zcHnA?=G82rv`?$?28Y0_v8n(*)c(vFH##HH+|_H8*&aVRu%L1^6~d-HDuFt!(N!ra zX|fdJhIPxcrW`~xqDAfvN>?jY$~1PDHHVPF0WlwOD+o}fKth$5&@i;{I0V8AhL=!r zP0-hhgQ@G57?~8fWm1eMpNbw!Pq@oc+w$f#g{did71gC-Bn1F-p~I+TG|@Mb2DsFs z^I{@gISLW#n3{a`q!N_tR*_Tf!KJtAWl%wK8vc{TH7xTLx9)mfDllabm6~KKXb;hb zc$>#t%T7sbm8hA9iba;*nk)|q_*K|_xY8WJ_JRtb(1S#hcVi%+xto1oD^(wSkgxHU@jgbB>l=1oaH(}GBW3Y6&- z>U&NTA$bggQjy(-qCimCRRX~@Aa~=-%V4rQFqfEyix^ZOD`1jAKUNFW5DKF5E->OG znNr)12U4X0PTrVnE&MFvQqfpS>H5whCEYMGwA87KZLe_s zNTOm~d1~U|E!(0ZC?mqP4N4zgAuhLsgweVy4W`S9rmr?wySRR0YtEG%(WzAW z0L6M-jn%=Xs>cR0-VvY)cEQGb^K`8S+hdU9ZuEW8BF{lube!N_4yKXcxIa6=DkT^oZeI-fO-HpWC zWTneC+Iw(IXy-oa=CqPKLoHE1UM%+eX3pGut*WZt?u?brh20~-Ck@MS-6q@QS-kNE z%^?axt#Ocs87U%^($GzQoH&{mIfbB6M_ZE{`s-4wlv|De0E;(tJI#_pn0f+&;JH(w z#*RvDT`H9dg$!qt$u2&Aw(tu`@?pr@^uLMyDy<~RHoeLAy=eWj6>Qg19qM zkf#|~9RVh(J)@>H+;&a3kq5P9w-yhyki`C6Jt?%3q?*&Vio@`dAr6zMR+PY?TVu=1 zDNe53c>)+D6%O;;k0C;7?i#(lG2{qLc_bf1VJ(4OS@&QGN2=ISIRTagxE(?vcB^ro zKvec%rMjcHP`63zs8oQh3GJdad5*q3m8hSt-1^INVfNFP=d_>B+~ zK#V7ELTyx^c2mVdzEpH&{&N2SW)v2{aVWn%;1Tv<*yGg^Z9+oFSD#r?KR}?ufsJY4 zn!e%W`Vx~fUuQ^;%TZI9q8mUbF7gS&&XK%=L=uHFB{~3o^|(4I8mKtJ zB0HofB`+_l2;=lMPx5ZjKH`Dp4D&s4G}3vh+r?J52QR+XZ&`(o*B-9Dk6Rsg~%*E#MGd$mmCTE#Uy40O-RNA9T`?0 zOX$faW{HOf1O6)A_cynAXmCf4A#T%2wj z(_>Da>_AiJIF}SrPL#;uYf0cEQUX%$&gQy;Q3oHnS(t6Y^Xd7Jl`s}o2o&y62Me~| z7jRA+G|@LwgBzMog)BX+=ct~e=&f*>$H&PXxkrvA9)r7(^1O#UPA?)xMxRa)mlz0A zY|M16lel4ttw(V#gcS`cO$qDYhNl!-68`{;2Pux!Bgyg{`PBwd;!0HJS#}T(Amp-j zcqxc3dby=3_c>Klk)C5hO-gH+9p2m!S&_si54-fH+V_UzX)b)W-WL=hHP)Q^VaUfu zMx`rz-XvjT`;?F(Vam}zqlo7p$H`NB%+N^*gO=i0X_Y-2Obyz zPK7Sb$o^p!UoY5SsF7ePQTUTlUY+z9Q}8FZgOS_0S@%&#c`IVN+C$cLB-h1I4GtAJ zX=%kA?t6X(t1Rx$Kadg=9ccF*8Z^lUh?6;|Z1<7%x1MNN8jI7r<} z60C+J%?voT3L$H0r4jAI4!(adoEsJzCxyR^40~L+TWO-|l0ypZ8Fpc#?&_okkY@xz z8z%15RvKCXP|wUHnvbIh{K)V}b4@s>1*>h#f)=SO)*Yo0o>*xkn^fxBt@NiEi2PK2 z#ARw&$v_`=Rl%Odo(RTJi^R9x*1sv$t<*Wy6#$F_j)@~$pe~m6MQpJ90F_XwT19KG0VkN~S)|9B}iKwkj6b#qQYpA0Q)pd{LO^&A%9dbeN0Z$Ej z99c0|Myj2k%KM}!F-*Wh%V~W?DEQD0MEzK|`9uy2I(5rS)UjdOHpeYUnKpOI=ruzJ zGDRzqt{`q_c#@P>+;y8vDqNOj?Wi{h?WWlB+D4L+`j<_4=5nW85(~o_sR_;1n_kKT z#WUXFB`Ih>j6;CS*@daXr5#4SF)TMfPX?-_yAV9oxfZx^O{zR)$d$h#wv>vON>!)b z;Wfb9Zuhz2p`|yz?et7xcbUOJb1d=WKOvO3Bqf!UrP0x0CY2hfX^j@zVBiYJaxCb1 z1CmHxt`fqCY@qg#aVW)DxREC+$pOM?s1eh}hm4?A$)g17i;cS5t_GQ9YFX(;SM=fA zZa`7e*@BHDX|j^Po7UQCPzQMTVQB8dlPU3_1XOL9j!>dAi%Q9+XTJ-4z)>#8lwL;E zai@!7&|EaGX~=u&f&5Yy>f=PhO}{*bjcGolktW8>Kl$CE4Fj5WB>s?a`o{H>YQs>V zvRG+tbd|IzEyz~Xkgvm<9^V)3#M2*DSClopkyo3yvVye7VFYv({{Rji1{$cT?&D)s zz1Va1>~BK8s92XeBQvF5cH*K!4j?+ZI?7OV*14-t9_(d#EPU_Xmc$CShgGrI=WQ9V zu0bF>tB26J=9g^bc#_%(=rt4i@zBBoxQ=_461R2hR*;ai*RW@S@(OP2h6HlaE(5nK zOlQ{L$-OQrXC=i1wx$e(2Lg%c3M*5F8AzlS@96lbY3owB2`!E6es96lE9$7P)VrN= z*1%KDlDy9@om^?wTNbqz*cgbP9tu_QB?H&ywag#`f?5QrE1sEg3#&t-BK+&+&IG6|%}jL3ckh`Fve;4* zLF$v!4kEWLH9Djvx@jKi2`b-WRGaOFEz(OO*zEX7y*{ZSK3%<1n@p9ZNF_l@Yf%)= zqYd*Of62VX@zB%P$scYTLz9cCq^zJUWL4W<%t7oA#J>+8JSiT}l#khk6Mr!a9hmRAZ5oK>~&kEyF{T# zQj(+IoWp7Fvo^6LUEb1DFH5nfK5*R@=r4l&_Oh%#gB_mq51*OPA(aT$CwE$zTS_QPO@YCoE9!H&!af zZfIij1>`NI0V##lJl|mjE@ls~;^z-UjSg=Dv&p5BDS9O06`g7XILu^jj^-Y)?P{;T z6Z(eRd!upNHa^tbQemn_mfD{sNbTGE2*&Xm_WW=~pT<9O!6!s5Vau`FfLySZ!;%0Y3b6=Lqb zka-oDEM60Oi*wMOQe!moyvCFJz!iK)*@_fB%eH*JUqwEJxnKTWuj97!%+=DWwODOX zj`>B8ac?B-doyn>xtopPw-U-)txkbbYf2Bd8U|MJ!q3c#KDTlKbM_Y%7vg?R%*G}I zsj8kf?-<$lmEU>5+Z@Kt(`@bNvEOHOA(b$>4m`D?ZKa~LRHUS44s{r#+-@6-XqxGv zqn*paYPemCZ<3xyTipX2dDn`l9KFV$?C$eE$8d1mrZ9r=l9ZG=Ar1nhtTy6_3Q_6_ z>y0}FoP73uaf)cU*LiI?Eb_t$UDhk9^e&0+ugl}pvs?=&lD6f-@Z^DF7ic$CPgI6o zU&)CXw6y8cqmda?7+y2I`N5VJ?<1$Gl&7V3KK1;j?RITLkc1labpuoTRaW##!#o+t zGk3gxRpW`=hWut3U4TPGg|``!m3gaBj< %Sj}j?D2f>4)@C1x<8hXM_xXalzvwG zOSiI+OmCp`i+CDUdvJOsPV(lKwig#T^^a@KnCcfYcvFsBA)&80tx2dWpzx-qq>*0J ziEBRYzQ+tFx%cHB9Myj7^1|HK=r*=L!iK#01h48d^8Wyd>9b%pzuM%*ZrI9|7Ay9d z5F3v2l_`2Pp~jpbfO9D+>Dz`qzVC$D+y4NTbYZmw)E@-*y`PlmHfAhc`Nm3+C~415 zKq?;fpKVMOnQTZ>{uK`i`o=e*Jb`jkp|o>#8l-t#d0>18Bdyj!G)YR5;2%L455!~~ zLPgkU9zDZFGjNxqE=cCnbGdB+2-qwyw#DA(CRdc>OLZP#(9lWOm$VFLTg$nYxS!$3 z4_n5IY3(QF>m#_ojS(JOPQEMYmxp$K+~OWM+WP}$TN<-k?6HuCT4@0#*(w^Id1tDY zwUS9V;C;(=E%oc>kkq)9_T#qrD|{|0J6Kv88V3ZezIfk_xn{__4<_uJZuYh;)e&wH zT96R0;z3v?C)x-&YmLfY*t|a7?qT#4^K)!$E^ZSYK|w13l7c}L zG$Nfk3v}#g_f3RzH1x?pT6_syG20&UTx=Y~)~;_{A;qt6hz6DVM!#ygcBT3{=4p-I zc*ByFzR zdG=HMox%OrX|JQl=+RCN?LSl7G6Oe=sE^EBelrV>)cZu1?rs>wg%Rbbg^*}EYQ+5Dlj=5e0o+mc*<%Pw0AZBf{e zR7)?}i;#%iD2z zc3fEUzE67F7Pg(Yl{Q^%Yi$Z8T1tvQu6cK3)?1S5%23;aSPq(}d7j>4a}gPALuGR{ z1W{5?R-B)I-j+X4wr$n7k#1vYZG^{!`iWDs0#wv>tm*)#`w(eL)aQwr8hNE*no8)I&$ig7Q>Ug~_*t&1orXf3dr3CMNSfWwLgGd( z>Pv|PlCzKqs@XVJ@W;)JB9Xy2Ak^1vt8j z5>6Lssu1F#d+Jif#z3JdN{(1?(62bkx*&~j9!$S(n>Nnd(_|?YjgJ{|Y_P)CP=u`2 zN}`TL&~(I9c-SG3xs9Wp#aWv913X7rHkd`H3R$b<$?NbhtXX%hEI? zxTivi$Yq~QM~B4^xW-U=0%I_A&+2>X?@6ip)1nU&+)PEc7kAiNk1C5O7e>KRK**2? z%vZM!B*{Zxg*b~5GrjJlgzp^gx8??$zFVJ53MyhkZp}~;om2OMhuwnw>?G>XsXI9N z!1%tjpmx6_Mw@Sv)$xTrZLi3_i^F5es!C3ZIh8>m_u+5E?VbCdQcLjrh7BHlr75Xg z)kQ~*e3+=OBg=V~ZwX6kNAS=*DxA7>!&zTwPOSQo+UwT;0H}Sac&&PXjCI;2UK@0P zpEfnX>d-R)DGyW+al;$9>~yO>r7Lf7lKbb{j4%DI+9JJ*-3C* z04ZyhuiLuUw~@JSvmbgT(fsgSvF1#y}P96vKRRjqV@{DZ8sHhY${`fp7k8 zm$|KuY{sFXsE@kO%}yC9XkkHV8FkEd;$Spz3fqF(uD5TR@=BH30TlgMV|sd< zRba*nIWl29rV_S-3y6{pTTfkBjcHG>;RjSn=>dYIt}RM4C|XZS_u<1`qK#&}3T9rO z6C{U{*kXy&TSx$9oqpUmc~dq`qf5h^UML4>Ed%d`gxwrwkDZ5*Ktx9RWz&eV7 zT9uti>x#sYRMlE7ok2vSZ$!qUmslQqC?>6|r2y>2jjV1h(^VILaK^fY9AaZHD7UN1 zcy*4WMSUIka@&qYGkvuMLNbA*6n2ieQw~W@R`@CuU4Iojunuif&2>?C5|=@!3P`0v zz&XN+z@vo<)o#4u2wTfi2Sh1qP!tsCO)!(5NT5M)kA^o@pjBkkw+Oj}r=`IJqeIg^ z!+`S8HTob(uUsWWD@_8m?C!uXEe$J8IS!Z-gajJWs#8Tl>yIIGmbyryR4O}tm;^Oi ztWX@X>^KLV63sOWS`v_Z`!EC`YwA|Cr%{Abi!|ta`eVz;GMefHW!Zq$4Nyu=03>JK zf{?rl0ZA%Rr)OpWfRt;HN*a3Qgxu7iC^ZV?6JKr!g$AUWW;)}@P-;o)Gp}XV%Z`amwD83e_m?Mz(h+w4umWQ4;)863+RzA+}{Gq)>dzNdXTI z^FXg|36REHUy4Z34~R|i_qhyHt8VV7{^PiP9tyK>ZV23)ai=cUg(GzB>iBN(fl)x~ z_pKiq4Lij#m}Yh?*5|4-)YYosJBw>|?n(SN_#f0%S8LoC{jnomx?_>4{i9W?76Is_ zoQ^9H-(1?ADy}zJ>ua|A;1xUVUTCz+Q!)1>AH!WvO1Q9+ruy|#4rAUibH(pwyaviB zv&`1QkZTFAp-oe6h`hpNSY)Q*Yc#BeNLW>R1!>G;+a=G;58;}HL-}KB{{Z5O5Tq@s zup(OFHMzHfOKhP@Fs$Z*$H~lAy%SES7CQ_(VP}~yu~c(gjq}K1gXt^RY1bXkaczk0 zi1AwR!GxTfZLo6oVQD9H?x5hCK^%6h>N=IJ#n@XmE~*FavJ>}7{{V~6;y85a8vv&? zODLtklfj`wa-_Q{=O2)=X#FC2Z|Fg--<;f0PV8E!3f+#$o2~NskCR z>m>+=uiimg>6t#vD-=vDG#nBWUA?buPZU<=abnJo!|kql^AwVsl=Oj_Bl|Yb@Q%~l zhI3yCo6_DsDni=S$nLjr@K~_zX;+HeXFMR+)1p-lmt~KSl-j7|XxsN77F)kdG6Wu{E}vs18rp>s25o zy+`ZDAQ}SS42#slE8S}!W;{VlG=+~Bf7y>Bp%OF6T|Joc^-9?wk6`Y{mzR)9@ae7& z!^sqYjHC*aTGI*`wM0A-#H=f(6iK1ds^m|*9$rRRTAOhzLW)$J!3LNRissci8I$ui z=ajMpk1-WJ;Ubmy;nCD4#3yZ{L-CwuLO$)r$S9}>FQj0gmYSfH^(z^%E>W&Ydd)KG zh}NsgLM83Ffh&gNgwxDz)q~|Kxz{XOtnMRoXjL00Z!AyhXeC8Fjj(Qe66(t{5I#ca z7WI}wU2W93!dIsv**y+f(BdZk%rMk&Q~2xOIi8fjV=_f6Ud%IzN=TMnl>2ZK4X`7{ zSpN8gf&;}SpsPO4%s)3i?J31FZMf^NG|IX%+s$M=O`7|$3wIp_ROPB&+4ojwMsm?} zbfxHtDuxbfak13^`!QDo_y9ZuE(**L@s)X|eDMR&~C|=RT zuK-FY{lXk%kC~L)2|cP%2kkg!{ERK)Q@HFVe*l|vci!HIs*((q_ckl~aSe62;co>i zZ?^6q#8EUR@`Hm?VA~SdSHk0pD*FZ?E_Z{#_d=ex+AaO!nREBX zyAaZ6ZXQ(az6RPpNd=gf>&w)S9z^#XIS`dEGws7@t*)m!0-ecmYXH=MpEjN(-P6iU zmYcOQsuei$(@;OfO-?86c5TF;7K#S%c-gbzJw-|Oo*3KF2wmpr?a_Tp3?wv)cWPBm zEtVS=81;For{C|X$)sQptx49KHr}uRt2W(_5o6();zQ2=0G#D5ZPQ%nlh%bIX6P^M+F=4-usos+m2it z)K$pa_i^&uj@h4$s?a>$a`PMNlk{Ue#$?})x#@O|D(9NSHz9A-&xmmSip<~aiMJTD zZPL?r+xKRXBFlJhHsnWeRVKZP6{3^W9BlB+$rv2o1!RCdgt7WKBp$s69C?UV1#6dF zCWs%Z9Vya1{kZZtLZv;Y=*N&31UgVv(9)O@L`Sz&Fh~GY3Xa?W6+_ciu*eEzFaT=2 z?H0qum1$0xQOu%D$hHXy%n!E(4GA#hAOQ6=9r!z0;FCHn!B@hQRP@2#)O0#Z!iJMU z-`5G!i_UdYgKISet3H&%?0QY@syLMaQk<)mGQyQQl!6r?kO>qcqOd`#6gpQxa;^cc zssuV{Xi2BH25LyT`L@uM+rjNFDud|4wk3M_R40sj2Eq7& z?8LvApo%4RKiyoZr9sc#gbI+1tDX6I+hkmKe({kQAr4zD7a1!m6)H*)kU;k5ht`gv z3pnl+eQ`U`7gK#TJmi~Q?m|FXkW^GnGOGQ!w`G5l7cSvlN!(MO`Eitu;Vt;m*B(Lz z8-2Bw)Du7{P)1m=W31ZyP#wWy8T|CIWgO-b4OEA=x0U8PUqY@=Tzu5%^4xJtARg-1 z5tn$FV$^6AhabCdCiiMwN?2w-?S}>H(zc|;S;B}nRl#{NLXLmhl<6IN@f^&JjF0FK z1>O}?PQvCPkIj584Dq3&YQ1@RPNC(1R28VxBU<88O8{6@L>YH^tv-;tEd=d%Hs;y6 z-4x;+nF>3S6sNTHKm>tN(;U5jjkq@t3>??gy}6sS{4OU*@IBGZOM7DXw%WJ+g`(&r z!BVw2lAy4Kb??g*+_pY;yWVXUtG71=l#$XW0ur2^&2EcDGc2vamzPwU)3%kwFviVY z6}kCadT|N0byb^lYnyeB+s-zPC_0ny3RA8zgxHWb5=WC~H%(Jis; z%D8Olnt3Ozh1@EW&eh)1+*>y5Xm#J-X)_oZ1D{n43$CIAP>mg&rP>}V8;fmoUemO* zaDQz*2G=Hmxuy0UO%8PMue4wwxwnwf4E7gMwyGSHdESbAxT|WiIh`$?N4FB<7%8HF zdAO}ss@xmAr9j$2?oX7HU$+D0MiKsEZeak;^Kv)+uGRC_55?q>U!xxAXNo}GymCg7 zdAK@MsT(y!kwQZ%1bssT<+H^n^V0p)qYYki=&hLQfyk*0xJdgjiGja)JY>ZF%tvU zcq10JoKTHeCM`s^@@Sr#fE}3dFYrl+j}#QBQX_>r?8X{rAXKuHkF<VN28n7@OGxhOpITqh(CG zX@M=x@%ttqr2#v)`h;69ft)|@bn%r;YU2y?tw36OmUJV7lMW&&OKJzn& z>FF|PQ*vg);5XJxo=!|)s_H@_JCXkYFyg|?oVWwsRLkpqw(c)T?(u)7qE$|8NLU%5 z!jaVb&;}ja)YO;Eq1Hr+BkWOTkb_|QC!oj%W(@@ zSFCpB?yewicYbA)ba_Pm3griHcHEYtPnc*Vc%)6OZsRT}lKtYz>L+Gy32wBer9-*f zOt6BJ`sYmr=C+3b0)Gb7eaGr8Vk1 zWVYB&sEz#Gz+4LGDdc!a?XEN{ZNq(~U~<56J7*F}rQOp>`NSn|TWVZ)#J;Z6VK9gt(-UJ~@3z^ut7}qaTCL$?RQ(!S zZAnhOB!kq0mK&V3am&znsTi>{%Wh?9_kC%ScHQar%duHg%=W8TPG3pHpt)^TbD`9! zDLJcL^*QC1sSsK>Vq+zSwK%XyhR`N`!aGtXi&72Zq$Maj(prNQzs_ht! zW@;{)_$lAOE_(V;60q+SZpLLd2-+0nmldV91C5eYO)1uxMcLShuOz%U3xNKPX#)4K z?;C!3gLt-xQrpQ*scn)g?Lv+n!FI`;R`5~x){ z04wakHekHk^sF7BHAxHdf?HR@r71Q04i)*9qqtRS#?ZJsF{rGTkhe)X%ZbvEJ8&}1 zMxbL&Q-;$fWJ2o=-eNS{3sXUEbtMWQ^z81$X@{DYhHw(Y7x_`tr3>p0EFTJ6MMu$* z7;NYp%qbWz+zmoMD|Fwk1UmJ}$%+&K0kov$)AvRQ%T@vaia>S0oy(M(nyZ7dH!abA zin)2UKI)`J+}80CMF~%oRNE$jL$Ljr$ujn_{{VBxPHiizHuh?N_U2zh{{Uj7bFTb} zaA?8#`7+`R%QXt{pKS&#O`$;c4EfUn5xd5g6hXIp=-hXXHKaJ8{^yfZA4+ zHq%U7LXx#8wkTXzmqgU}3~L)N86}mVj#u@%wOp0m+`}?iH%$y>jo_jvcl*_mMavbz z+hc>Onps7Adogg9(iue`Lb6!17j}lRIJF?&F4rr=&d-v)Sr0-M+^w7Q5 z%lkO)$a|yZYNn(jJM1bpuqdUaK%|A{JH`pzKG?fC&|3b2+WLx^>?h_h?8eB)Mdq?k zsane~uP!!ED8b|j+dk!Dg6u`axyNiVA$rzQt$r1zv?-=p<2=0Vra)L28u0U7IR&-g zzc%=I^M5@gMlg7f_$F_yFIBu->NAb3J2zmzJ}u7Kb&onzvg3ITmf2~<5D8JOO2s|c zlkF#Ja#(zkn&1wa5uvKC{%C(PxGmp{WwnVR;DTxhR5xJpl-@?(dw*iazQeIj+)frD z-X_#VX?|Lqs`QX^37{3s(01c@-5ZEv?4590UDL=ePS_hc+k+;{CcH4enc%Dz@bp2- z{B;6t#r?-=-By?hE0s19ooPWGHF<7>knQmtX4p>hT3)yU!o%rXmgxMl<*@AQ^BW-a zY5K~ay(00qGx1*XZuLIexH~6uFh3AUd94nhf?Ib$RY^%XWzgc0+`Qg9B_B(5dsgN7 zc-r}_1<-tkkjcq)svNZxAZyfAD^BclRMwp(eH^g5_XjmWeQx@V<*pvP?3*te-Py9D z@e*8{l(ocWONvu3A*7@isuC!wKQgIN9%eIe~4f+!#SUnQgK(0=bsef@3k|FMq?|(Jwr(={giRA&UlFgjinVRt=x6Zq2U=Xn%5n}g{{W=b zzfzewx0v_Gs6I5>8{>JUncKgOZV9q(UBbg5mCcQCVNb<<;D8brt%W3kl}^h0G$b=oHVbe>`~_y^XDya#l`#Q8t#QzCP`?s zIh8ht*?p?lO51c4njFEUM`kRWlef6Mm<_mPK>9wlSlQk0+&g{Uc2btqP=l!UDDQo8 zF7D(@+V9)Kai1*f4K|b|wqvEm`HKyqB8de?rxUom9j%N)I9os!A9U{9?9JJ@x_4BC zN3tJnw*s3{NHrwWw-S-CC>YvEM11C!TvM)OplX_S;d-Y5q;O3KnW<4uN3Aes@Kelw ziQTd`7Vh4SZkfAc+9tx;(GfFo$y@9)8D+(kCW(1zD^VcikWN^OzRN>xBUtLy=7ZLy z?QoLaMCUf1@1}`as~y$0Ql=-{SOEcPZRrD1O0%Uq@P9PcfG7iE4z4H&f@R7nF77LB zsog~?j_Xeq&>9m|0x5?cbdBH=u-QYvl*ha6#N03O?G~wv+!rge%}2Mf4m*onnGTSI zHq&XUl%*3u0K=&=5k(P_L(&HtsjN&F5=tS3oo+k;;!|&PahBxT>Pg*OYV)==I^zTJ zD^%-sUu2ZH*!jkr1xX_?F$s^{wim{bNZ0y`x#clAY;XRi2S;8VK#KaS#WrJDtsZ+q zL(i@{h!1Gem62TOs-PdE5-w|tO%zw+l5+uBxK8>?mA7HrW^wlDfx4izrF(&Q7No_N zu%4@F4kt*}+>V%`&tsNt#Y=%=(CxF^c-r)s+$Hg;*X#XCSm6&!d^O73LixO|b}1+} zyMm&~4M|ZU3rbuRS56KTfcjUcaFFbunLveU6$;k#hu2R@LvAll-i$WZ z*>8MqE4Js`n<4p2ffsc<5yKtnt*C`I)mKwOl?s7Ou~4(_ZfbY*!~s5_w0ZvkFI#fi zol+M8>nV!Y7ye!MChKqK`^~tlRUvlglzxIzp^3(CeZbT2LKGF>VYaNl1f+c_cFX9$ zfVo+jt?<{WX%{QJG@(x0CR zniHRP0V!4L3f7tT;8H$n&X7UQhqK#&O7$T08Vu??Fj5LTT!T_bsis}{^VpN>sah!1 zR=Rz-BbbiY)ds$`!XzbHP%Z@&&<}XWf-qZAT0pLSxCP*KT9K%bMJdr(LB^?ZAYKVH zAoT3Vx=j!;^Ffr5Y1g|2N~|d~28!`}vE}4kpwNSu?ZIe`T~SrGtmtS5OeR>B4+}yF z>w=vTmWxR}J30(`z#%{$hXq~;8VxnBeb^!t8ma|Br7#Q2QWPpd_uxTxf_c|)ue*0cbL0two*oi1!rs?s3+YK8W$i$ z7ZT72P39;FbtviMXq_sjvY5~;JA&r`+4WgQTeQ(Mm)f3^c^}%Wor8RAxpsH%TVn9V z@0gq09!P{P!#31+gejeojB4W zPu(M-bdJI7#O0-^r&G8nd%L4vmT^WT-d%CUrIe+nK++IJXr7sOeHdCx;xrr+Yix5) zr;1KuYT{dNvE%;$&Mqz>muXUzCbgmV)|hN=93vsTN_zWdnM01DsdH{@ww`P8+w;FA zX^)zMvRj090D6;$c36-HnJ$4s@?q7;zT(_wl}1v}_;&mPfx1TGphAPF<-gL`}hL zb|l4H-wIgyw3;a>Q%Xwd?@1#L?O#VI%&R(~DMEYhNZb2;QBeCkZM9DP6@{fiq`arc z%$DonsZbiUoH?DnOay2GjDfFY#5~pddnuxBtpiC3SBQLAZ$&5&N>YkPaQ0vklp0c# z)i}&*R+zA)3S=|EsDWT1Jvx!s2BK5UUM;7)1am5k*HsCor9zogE|>!q7m3jZ1u_|7 z0*zE7e-=CuU%?J3S{|5i!=gZ)R>=e?_YT}0Djh^=IaHBB-;X2-Mq2>p4@`I?rFg`A zs>O5Gnh$Wo2&5@C9opYE3*L`eK)E2)C};Xf3|C?r$mcazO*i zZ2>?w5=M0*wZnB{GZzIljq7)RzPh!@hqxuFQt~XqV z;Zref2%GT~+mpNZ46vlCdYeMOalZ{m(TGX&Q%LSAS_~`<eQ!|QAe@*WC4<2`Q};+~eiElaCPt`q8%Q&Vk& zU##NGj@g5Y)o%NEbdPW;Ce^Su#kz`3yhwC@`=?*o(-#X(y=fQj)l}~{HQc=D6V5v< z#YjU9C6o?{LPCE=CmRH88VV}pv&jA>3ZiSPzqbY^sTHym2WEZvGi0Lj7`frmrlj`Z z9_pZ?(*;7kk1s6a%geP3UfvgxdGLQQ5Bi7gA72hUX6?(i?t510BGGkVGbMnP z1tmd9akmq#4k1G&A!#*Ahf$48&wsnI3WXtuAj0Ok;n<}GSZ(Sp$sxgC!gZZ@|;0Vy;f znjfHH$ZZm31FDrJC)f^HI`u-Z&U#hn=Y2Op^>b?D=eG~1*;g5MnfvP6^D~ufod#N= zJSW9qm*ch^NgisFP%wA2@X>?}#(nFJ^R06O4_cf2710Om33B^c<9nw?!bMvr)sF5v5W$@As3frRkvQPKOV|VhSU6P%*_# zYh3<}9FT>&F3f&Wd}(FAaIdc}Ub@Dtt~lC1Ct{aV_b<$0*-nr;6QQ*GaazjcaXil_oO!$FJ*2CK1oxmZIwQepKfCs?sS?kE;hiA;JwsS z2EdEPdyjcXXM3gQ{(4fHaqVTMm}sh+)uyMzOcy=)gf^9fQ$?R_hG4F91-IkfBnJpQeg|yPC?=mXqNa+q}PVatr4sYK~<{8_wx>hZk=)Zwi$i>w8OO zq&P@I6i5j)IUK1>Qn6NXx8zaE&rlbZ6Jwg2@v+5|LfY;!(!M+1G}RPS+xkqx!Jb&0q=3FDL4tR)c12 zi`*V8-gg#I9lLHuksXM{nuKbvQhMTteRUbV9)+%AET(Qe>#0#YJZHI38!uawrJmv| zU#}BqClY{WE>vY)cYcVJ$Ts(?*~kJ%UcCvzX8JBjTaTH#>VQVkx-Cwr$nqIm!0A3~ zgY;7ZWsHQdY{4VBvNtu!p%Qetj>TZrh75;`Y3#`*L}SdB-MoT8{LwTdqTlRp`z--2 zyr$3%O;)r195@&80l_3}XLmA|ExZ|ZLWueGDOJjjtM=fBClOBJ!^}-dDcsi#T%k4; zl8Q)BB|X0FBP=j%(1%q&l-xbEO`CSW zaSl2i6GN#axKyPmA92HpFB+mFu$x-dI2hTIz)EA84YZTv_^Y4Mgnn{9X~?pi@T$!A zB|t`o=E&4&H2|!sh1ooB)i=3(>@)uO%Ap0w@6?Up7mG*n!s>XqroWmv3RLT<{{WreS-nV7 zma&$kwDy#v+zl}of3|Pl>c;Q~f||p38ygcTEJnU5<#MxnQso)Uw_?9;4S{-;LQt1M zeT1oB#H9xlLEozl+jEt(Z@ot+FR{U8z5-a{qaEYvqmnrO@W15_PqS|*MWZ4-RxZp& zx+Q>VQx$6j)4e497_j5FY*!8FmNE~iXW6}hb!77UdBFqIsz$T(A2sZaQJuTXmj$4d z$+W(HVU;JLNe*hr;%4gsWiY4|DhkuMS!)wV9-ur_hU_aRFm2fzuWN0jK)+dhd#`J@ zlvUP=AAcUS$bRy1T{jQ2lgfF}@GC}!NV7JdF$klF5SkYqsPio`DjwCDZie*|!|z6L zps01Cw9w)FSr!9F5Tb4|(zsQvNippU^U%WCDpRBe+R#drPhd?eTst=nrD_6g6f!ye z9VC_I$Fjth{3lr`n8Bo&%Rx%gdx%gNaS1Xut>BZmju_q2;X)B+6Z6q`O3=ehTJ3GN z;c*Dm=}xT%l=kD!kg$f1DT8g>BMbNdqx;>$z_>-I5#)UGNb@ADB`Ep@F<`J{onG>$ z?g=3gd!tnvt>q87sf%^8FjN-Y$6r%`lq^><^y1NG_ENe9m*ydn&NL*b`s21EIIVwq z=L(XdO@7>1Bf4cdDi!87ZEMx_qz5c42A8GJ5^8FppWBDg&ePzk(^@zkNm^R={{YO% zY;n;BRH<-5;pN(Ub%u9rDm8HPr=+u%c z`@NWJXOv^e94e+VTUK^NU&I7MY;jHZc{?S`b2_8O3iDqh!jceHkV=#(T{v}fxT1-{ zuI8b!`wHgrCrZ;m)flOu$6A^lB>RmqIN53{v)hQwZ!J;;akClOtr;`jJGxu{0Nt(V zX!(9dppvDmj>=>K#7CJgpHJOZwldq~!TsWq6KaN~g{>t>?!&1hJd_ZKB?pliw)8UI zic|$qR|6cVj$jG)t&zCwyH-Bslv`rScq(QyNtoZ7wSlY!vW1lljX_Yv8!}MKa$^JD z3g0#==GMqaUG6)TRB*jV;>G1ENtC1nq2;=tEwZEB4rdC~Q&d>q=AAgF zjHd1l7?YiaDY-!-OX_jz(KM!-nu>cc!ZQyF1vNC+Bi^a19us$05|M7wRBjI*?OaIN?9-?K1{Cs=uDAsjwoF`U?J0<>bS%&* z!&(J(tug0CqCz>1QFv=icV%g?wCi4bp(n%>-jz=;A>RN*OlnD0J3~Dn-`e@z@pRMhgQXJ}4JNAdB?bFGxT;yyL zyY-&ZQYYW!ff<&Tv8W+nsF12>OhIJ#&8>+Ilj1y;%WL*WDV+vOT6t~aN|voYiY^F- z?A^h?ZE)C*okh68*)Ak^C=!uhb}Lt1(`%j66AIL^pOrb<&bx^~9)#%Hex7)xkuYvP zK$OcckyJd*yUX6bW=x%Ydt zSCp91+^lq#@(EJXtiV@g4jA0-42x!HBmh+1pKo^AuylSjOQl7U?|#vdIlG>+b<(i7SS*|j9CFL6XTPpx_oR+$?0E`#9? z3Y=H&a__A~nk)T=+lj-c<-k6rZTPo}nf_Xx3hT#KnW;in);5J*uu9+k#% znA``>_hfMTS3>PAfWc?{SQ=^K5|-VAWW7#%YsKLxF=E3~ks|h{3K>e1k*bt4HSRcL zeBcprTw?X z*I1kELEJc3i>^#5sT^-@3q4Sl)k9vwQmWxWx^JV@NNSqg_Jap7qoNe`CUc&exC-LG zEgJ$jagnA|Z9gG86n)FCBtO4$(Ke;!R^9o3+E))P@ITY~F3L&t&rqxLF^ym+UQ_|@|dkak4dV_^6tmW#0*{{R*C zbvO3c8vYY|W3So_atin^Q{FwJ&S9Kebql&jFe*K%ofRo{K{o4Romk6CMx|8uP-5IK z7&Tcb#JI^ujZ)kl$#mNnXm=!AR}OqK6&=SLDN;!70bpUZ&9oO$mOD^XmA*#z7j)TM zR=K>@zvFGq%RKJv`ywtaH*8`*mnrvGp(qa;0F);+sP_SkcXV1~aUS=UN%StQ?ap&Q zx>#Z}0MX)At8a%lwTi`Vgq!8Y>+%RnTP&66MxNnPKpmLNWZV`LJf4u!S5jE)OvSyp z1^sbLH_`?Zs3QlvFywY=PkAGlQe;lLA~WNFVE2W9Y*T3fx({o=U0^NG9S z$z|gaY8un}l>%WZMB8yb0@Rr5B>w^(x@Y z8@3}BgaLAi#fOrLM18304_?jziV=X%tG5(O;5aFVxBtE zp*E7jUYgL+K6V_H%;Qdf+Q}6%ZT5TIGfL4`TcS&wPL(w^pa2YAZVOsEiP2DCW94^CK_w=VUUhMllFP|aZEZLU zYD&SO3JakGcGnHXLR?P0o$>KVGZYM|9Dhc3yMMsX6(x7$h z#MeeZ&k$%7YOo_Yga>c8bQy+mKogL+O&?R z3i^LinzFLGm87wxverzPQ94CXD_o8px>knOFm<^nf}A-|fH?<>QAADYT$9-)+MB6E4JC|inZhe_@TQ4vlWEy#mg(RsoCZG|E zE`JFQM$y*t{0|f=v$Z!B)+Y6rP!ZO{QBhE=AE?h=+{Nm9V%oMBB3kS$TP+tP+@sI7 z#gNREJeJ&VNiRO=3H#L(PPomscW5#ATeCb4bx5TNs@S`S-#otNv}6~7zV9$8`+n6) zb)}=Ir%_K#YP8W~VWg=+7j6rcV7f;(tGL8a$B8iC(o;6csv( z$nPf+eZ8m8l6fstxomN;XlSih6>L%Cp;8v9WFc8K_k<9yuiK6C;_5nTLg%|!<&TkF zll-cc8tZuHSgZT@;acoYvY@(#%I*(>NcPjNInTRJ9_eobRneWGaV>p)AFW@nUlxe> zFGxI?Tv(-wO@`whqoa)6(AtRhl&p_-Bl{R&hP-5QH= zj`Q(RDGjYETxTogxB;n3shtmQJ9^?*bQ*D@`I`-(tygdCKA-HItH3*Fe{gpc#%+7= zZzSLLoJbB$rOq^x+lY%HRV}%wc!40(EOWKq?H1SWWbYt?x+fd3B)!HqTV__GzST_k zb<272{`%owHJ!87w>Jk9CN;KYl5zrHaL9Bx+8>}%px54e@ub*@XS6d1)vo5UnWnh6 zy%x@G9^DlO^*t+gUA;wee4X;!4Q@8BWtq_*gzR*rhQLtEx3j^$rLpT zR_aGKwG=AKA{e>)PP{sw7t6(cvHn4>Ca#ZEOFhHHTa^yiQ*4@#4%Hmj-ZX(nTq?#VoliEqaPEJfHV{x z4Ku`zlbCp@EYuIsv{grY#SxJ(n6f@?g(Q!er7A{M#4b^kc8b@xi@c%Lp;42iZ7n6Y z3uI(Ss3$x^t75^J;V0%A4+E7*qN(o~^6)6ty55Vtg+`AdF&s*DB)VvWoheW->fYPU zI%rfLUh~OqNT^G%Y)#tJV8w(kO@GElbq{@j*AjAKrvkL9tw*^n3DnT~QyT4RxZBiI zakjk(*FQ0(qd$6vAnvRlID)MB{Hzc7913VVM7oz)Drv#mlbHIk1o-+&vPU>@)htBZ za^ojVsz^vVsQ6Wb#e7DEB{n&a6$LpWT`luKD0Crbt7lfsK?Nt;Gsm<+)Z`P&Qp%lC)Z)}iyE@^!-BjpsSkutZkxHBt zwHOtQyK?TqQPxxdAax_I3g*=U(;jnbjnq&o%77-F=Lz{`IBUULc3=rW0YEJPR33)~ z=}H1nIuYH3WKIwS5s}B9ytGiSWqzC{P@>C8?xipkst^!VB+wlD@KAwirnLvR>&K9+ z1PbPB?7$#cN^>BLyAA~kKWev2y-iFDb6TEJ@wjqA2YQco6^0`7M9%v8kTa>uXRzDY z+v@o%1aWk~NE%mmBB7E-dOe=pOhpq}pyIOZo%`-x)ghxiJX4hxC@|!OTwVNf^1R$d zNlF%Trd6&S2wTrp5he4BXwlNt)82BJOo*14%_2m{MQ5oXQG?iuew-3#F~_Eg$Ay96 zhC*rJpbgE%+i!6`u;c`@IY5Odac#2RPi+ImLZ{#C z_?&AQqLXbKw#9iNF=r*0Xg=ZZyH!tM)M#;WvCrGjz|g9d_6p(+NT^b^zV8ku&D=Y6 zc1V(EUZli+KH+f%QsSaDN}6R|iE_Ry@nm5hQ5qVm%h-JI>W~K{+lbDR41|)x4ORzL z4zw6+6kR5%NuN=oKQemetSBeTOLPq~J`^Xg3`k8SLTdy)D1qzLD!sTt&7n?Hvu?+> zMruvo=HgvT3QA(Y{r8vG>K&&NnC3>`ecI7L<$UP~jha`9L0|9k?{3k>d2txZEL4pt zN`8*qHoU)d{CFiXxW-$JH9je*LR4sa;ytQzs5OMvfON--@GEqr5l-&h3tEB5WG212 z3>g%fB|Bt=+vi1|(`a>3m8dqb6oZvB2d`W&1G-&WBrahRFtnxnh34&eY7`k$7cLwE z;I-R=Kxu;^dYlb5fC6<9Q%p&{#z3H9v}7kvDIw^NG~(HDCAXU-EeARg*@F3L^JfJY zDNPC#q3k`lB@v615v2EHDx%bCvJp{J_G85bO4k64vXClkl{#R>DB`r#Yfj4HA2k|? zX-Nrm)3*eBs=Uq%M$l(%k@UU1D~iSpZM0ic7|Q9VW)mV_N847ngB2}ue7b*>aINeEn0m*X=W zip(cpF2rEfmX>vB?4n7`4`u}20-gM;k?6%PsWe?wX+>7JT053_q)nrf-aVWtnC?9C z5T!VkC=x569R9J17_Z?2QlsCFnD7YI<}_JTCCZDU9AZIgI-CVTAX5lWm4qHC#2873 zB*D2o0V(^*Xh{9V1q1dBL(7(j#Y{zpnMao!wYjQ|UU}(2DTrxu(bGjEZHtn+rjuK$ zWwqy(Yd~Z?nmCowVCUIf1*YScB1;NZ;{wS?z7kyB*|;TFRP{EW;6g%v(SVz9T|ZKI zqQSSVS;;osmoiuU;!}@T;tEoZ8GP*0aV1C#c$>fkBNoZBjTnlzEBzH=L1ioXB0Baa zaVdT-$h^p+l-eVK+M`2>ICkXHdK7HA_#^bE6iz*CjZ)?ne?PoOvPc+k|4Svi^+Izu9U8SVy8G)JWhxAOk*5PDy$Ebs?W~Et! zn81Wr5Vr@YJ)O8$sC1$&(2}EylUpFr)4v`-h0;5Q57Xkjvx2WLBoSQe zUW2<10jNNPZzD=q1PmyT6-E$;3yCzPE2ID)K;XY&dK5wt^hIsf!j;o14E@;fqE4z= z<34)z3Bs1E?%d(-W!#*1w6{Zr-O;nH*B1qz;NqJtDQ`fK9gbGC9V=KsQiTB|0Z6Ny z6|?3Nmiyi+Qar@Xi_0dh^z!v6v9@ciK>@WAN|J=>O;k#<10mO@C7RV7B+(jeE7hk# zAmxNaYu#%B<|Do2zTyz_jRQ(p3i^q{uQWs?B>_rgC@=`9bm#*jKD>Dm?hDIMC{hnY z(D&og8nGT3M^a8I9Wh&+QmVslI<-=c+)w=v8C|L>`m)38Mo?8Xz17Lar1*2{FzqxU zBVB>{P!pLar_q5x3i=8BuPsm5e=1+hdv~~y((jTN1&2q3>1M{t4u9i3aZJca4sO}L z2lf8|ij3W1&D+;b@dy6^2>prW=%or{j5m$LO%lvxpE&dtp{6$szOt}*u0q6nmQuLz z2>TaHC!6MZ3KJ5wmAlm>)}u{2V&Kbn^w994<+<#urT z+A(i=dn_=+^+x*3MP1}OrD@W7V)-L|(y;iiN17|*r5;P%8&Y+*V!_+{ZL3V^7Sw({ zb4#jHrb$uw)RIUP>=>hM>j{|o9QRZcqSv?1V%}Jx5w?UkRb_oNZfh-zjd#T7?afM- z#Fq6o{{Z$+B%p+YUfo^T$~%>2>2F&?y94sql8WC2B#v&iM%R3f^5}!)_SS~uD1@+@bfS86p~X_t+&bWw z1*K-MhsZa}I;dXJaNNzh#brLVS0w9D{dk{!v?MAWrb6Tz5>&q5MMm7O5m~l{7bq09G>`>;j3O;3Z#4QWm7|S1Xq0X+ zc7maT>}ZRODppnH(IEHtU}0^~Uo@@D>_lXmQAKIn95C$XURAOKk2_kDQ9@7CiwU^D zYk8_)m4keSy){Zn<31m{mYQDPgyL!UXcQei#|*y}zizc5DBK1TQ=kP4xa`{#Y3+NY zD-O@Mvngz?yJFJYXsm^#glce#(!Sg~!pm_yV@UNEwlhGUqQG0*!5ch0b=nPU`-Cxh3iK6>N6jr%j5RY}63mekke zJ>V?5D50i_2kOOJBbb`uz`XERoDS21%Az4aLWOTOu2k83ibbkT{%Nqb^Uhf!3O>P% zT_q#}lbt$@S>iUcMb-P%R7tHb?JqiicB#$0u5N9^vG<~E3|(*OLfv84SqaeLq5&XN zp(dT!jY*FnH z<6di;u6xQ_-hcoJk$&dL*9eG-D&Ej(3JTVQC9|jsW>Pc6wYJnPHL9oPJGLcfNotIw za+D;mNPyOrABd@6w-Zs|9iq)A?l7$=wQrTJTC_U_l9dWqTBV*zl0~J9hs>wY+&cU0v-qh4@_=rvW zruN%Y5Pn>(E)U+J0>4SFB;mt7qg_-D{_TcN1zUedw@sScQmpSyXjlBiu zlN_@`wKmjMKRmGqNyalb{YOYwUJf&gOF*u?ZY_RbI`@YT=EgXPI~2{+U&8 zj}=KtS#5=|YM=+-GEEcTQH+ws*$LzgigHh_bk=s$5oc~+acThdE>!b<*JP)WFOTg_ zqZ-AwWCm4kw1*Nybw`NRB;-zX$53qR3%1&UGrFDPzHs+1C1WN@?zd=b4!v#He+06B2CIN=78AoZ*L86wIBwo_fAfZKJiI9u-lU~NPz3hj zXWO;T1h%9mjmkPW%NqIv=}h(Q2{Mq7msv>4jjBUHkEvLdW$C91DqOh6IM%84X}4V> z*b}#u-M!`l%D!zyrAQT4p+)r+!*_grR4P&k8X@S?h-UVa*{R3b4WEfAb+UepH8-ch zIA#&998qaIK~H~Xq?C@JxP+X&py2W3i98c~jlSAwLMZOsxCWGx@LC!Ggcs7YmP7Zu zF!nha)ll72DY5c7oep-1`E=s#qYHtroSip(L}PhHxJHj;*vXW`k4cQ;(A%ySspce& zWT&!;HN{39%YN*@w(`%FO7$%pE8F}fw%GZ>FgIUzsb~CvJI?*1Xt^;zdxN@s=R%9LEzttr-T)#scG7q`J53NyJiX@;!EF_W-J2CIt<^SNZ7Whi zL7_A~Fjr_Qo?CoCr6%B7zIyJ46CgCyl>iB^RD;{?zyt2pJ(eNYMB#&HUDZg56)A5> zK2wgYrk_?I?&S^Cs|}Tzc%V<#_`c#}puH+P{!#%f$1b|64EqKczcCQd=&bQ<`DA5V z?;k>3+dO3|@1i7FFU^v&Jd~l8oXMaEBZxcg+ih+t=KvL>Y<9yTkat9JxSvv-VR}I2 zbQ<0XlvF1y(L{jySXuY@gv+$tt z@-COT}%qnekYvEfT?fzp&U&#oIwnPtV#Q>3Fple5~cv&Zx@4yT?eu?{Bev0bM5 zJZ!`E$%zR-eU+`o)zp-!J!|d6)Ymq$Fj7rIr`lukR??C3-s`;_6qUBNREh7)m*m|n zPq?J~qG3lvNJfw=thv-zrYl)2vj&yw3rgGDP3|3CC4{NzyX}``n@e}tjoz;hB(37L zAUcs+C3HxoeVC=mWi89kNuySu4r0?5TFG11^xWPaqThh(n%a#yX>qEi>n+9*;~*ZC z1tipW;#(83VtK5FrBpYMd~PEJmh9*p%7kp3>$*0M=an)0@!Xqs^h>L?X-D0<;nbzJ zLTU*;n7iaQU97hH(WC{7?f&v)EOI0d;xWgNS37HO{kwnL&29^wy60(NsVEIP%FeZ~ z;i1!*IK?tp^9Xw;;}uZ%*5*_EH)^BX%q`n#Q&M87cNX3UTP)P9E7}GpZEkI( zKJcJaY^AP36YiSTH||@raoX*ntNpD~?Xt3ZB$lw>6XrE@wtumk zXE8aMpu+zEM+Hl{gJs<;SHT%5b9C*CQUeEcM`dO+iW#d?lU)bcalLHK*A3J{r={nT z;!f%I%Id=H!L2evIe?;A(VT;-cH6qlwm#lyLvJm&0k2|`LB*o>IHojs4hu4|hUUlK zw|k>LjTMF1o~8LaW?No)?dK+3sGtZD<|s;S(mNKHtB~)laY4%V=N)u@8hW%_R@U#X zYa1O5h2FhWhi&@8eXE?8c^75E-4Sk1mCdiq4!_zrr9z#x2Nn!xbFP~U znKMJGII1RJ^0$tC<0iLx0j>s|d7^%_JuY$&Ant9s$XrndHsxijCB6pcv!X&6nkB$! zwdOR3^oJ6p&>t0Y9dRGpef@2Z4}$8T7ZMLGRfqE<*t^`bqr1nqtEGSu&qd0;!MFFu z%AU9zj>&P_liDN}rLgp4s2|-@fJ(jTCmk)1%~;(wvAjF!Uq16YFDs6DY|*)g(lqw) z`qq#)&hxhg2=~XGZ9t{Qozm)5p6|R0%MYZniQ0u(W!=msg-X08FI$g(T&Nmt1}VpFm4#8Ti>5cjCFdCW;G2x#oec2CDno%PH>i5=O?=&tVkx(IQ*6 zZ!&EC!EM~`aTV6zakI&4OA+Q+<*l?l(}Bd2*=(fHiuYhI_a-Lt+{fn%IC#-M#_a6= zF8odM$El~nzhZmx^zKRK%c6Hi?cteFuMe~2PPtp`bE5ppLCCqK3ME3c2d*pF1B}N& zVUA#0d`h=&n@rwX<+l&DEpYH@{fj=NIjfp^yJc~{!rnZ5cJ-T#(1C53)C-80D6<#j z&|WU0f{5w`YIF8sj>g*OmUZYdt;P>XIS%vq^nG>E zL4TRIpJ=ic8- z@;5q=G(7a=RWtK%*sNv@43<2Oi3qDl4SA1Gk1l+8^Lul8jozG*!dzd#`+^i_Yk%XQrLEYjK~7LD~;y|u`TR!(q6_-RHISL*11P@cSidsgM%AB ztb^e^=|TYZ6$4r?3|MznhW9Sb8*xLk&y6N--pd3tFYOYnKIW zuUiGT5QHhhq76XE43B0qosELOw7h?rcjq(iol4w0kGS!<3mE*HKbLU*;3yB&Dyhm) z%hDJ`mzK;`Eh0;(N0_vrky4bBDnRHfjcxLDg^0vxmM%MYY$XP6-Q6zT7(;{@6Jah4 zw16rW5=xS!lTlEtR^q>Wj-leLw>CyL2UCi^zK^-jh;|PjZ*8r}8>&5p`?TjP5%jrP z5Z;O8uSq~Y(Tp>?TQctb{iDHh$9aQ7B( z3!e7g`-)|uiBGw1ks1VBeZE+l^PYR*0cvohAOzR1aK-1g-MGlvUx{S^HlIqy``g~E zURxRR)})hBul#+QRdJ0GTafckxF`Y7v>IcjD7frg74(DoK5w2#+?-3woGGy8F>bf* zM}^gQ^`sXdy?Lq#ZlP6Ll**QxiVA1f9G%{bRi&--ZY0#dN_v-T{#|kx_xRZ@A*V|@ z^VX$v)6|aCe~3L#?o+z1w;kQMcCE(WbXD5ovmyJ5QjsyWI7+HgNT2}p#<{cQitiCd z=+Y_bSU&c}$&cJF^EG%i^{!}op1oYR52)@`+?P0v!kf4*Nt*OX_g;I;sYPXYyqm|S9ph^$1x+Syf!CvDb;O*<_+?UHqc7ThDvA0V z;WT=T^+RCc7xx>xNsacIsef@Y(jIm?B1LjdQinVysz^?K_{#StRM|r5IF;0xd{54~ zjjaNV`2x)T)Zbg*XGl?X{iS!f+T=>6k1tHM;z66h9 zqHe%>v^8*S3N6NnO1jpDxLq!iWn0>r5g|#^>VLCzbvbmyE2zNQHe-R*a~u!N-sS zLRYdSv`rea3rfDsNMjybB96_(9UT+$EZGp29E|drQ0P*ko|v-5GlHYfXB+fzPpfYw z?#L(&8K|EZEm35$)94@?(1#LnCd`4RpcM@^*MZu$tv-aT_VeSr$@2Gq6kBJh(f6~U zg9bo7;I%OawWI*@DZ zRw5;_yth91AS-@5H;%p(iQtp%_HjuOx12W=lu2Ptm4B{D3P{3nJk`F>KVBmNp%Sc-H<%cMX6vBR58m|=P zUjCR5E{oMg*W5Y|m`%o|9lTJLV8w9?PN@o7bSwB$0k7AD+}fquQFt^? zjepU>QJDHHDC!mIPuGtkEfkZakU_y*+OG#7)JUPFMidR{D8U49jFe7-4QbRX-PweQ zsxw_g0S1{G9{doa`G~rSJ$pMaDMTJ%cbqpuf>mm2DS;x3$TBlY8B?wbys@O}Cj-(~ zmxDx_fm8d~EfA*$*xM3!EcGyleohlv%$%S~TSy&%by4gXUI==W24gnMX8KdIG&Mln z4B2dzVl>T7IO>55QcyEd`*7+u4yq_S*#wrxN9t6)3Og2POND4;xi$Qrll#><5R-|f z#?Ulbq_df%>T+scugq=6UW)je_r z9|P10aVDLL7_Hx7ptv6R)lS&t=eBiYGzuTSaRi3cfrII5fBiS_O1Cq2+si z=GD=ftL86#1MbQwd{j+7&v3Omre?x&BG7XSNls%FE4+O6=TKGR+aVOftstd)Uhmu% ziZ>o|*1+i*!d8s^hYoBrmoS|QM(Yi4DAXw`)5?(EQe$1BDl?QHe$gvM2Y5M*Tk-qh zj?v`1 zoNAdN78jC2=mu#W*aT5T3W+2Rq;)t1;bTGW!FobBQCJ8kFfbvj-9>HXn$xqt3OTev z)K)`k1b1LM3JRlR!jM7hO5pLJE=qBf;!s^_T3r;-plCtFt-l2oH^i`1RU*q ze8qwF;i_4PyvG!oZni!a$rQpe6yhf6v`msoK*>X?!*MYj5xk(b`NFJ*O2VFbMi%3m z$eN+l>h@FiW8Qf~uQ92DK9h$-P} zfqS}?Aq8PMX*uUyyKzm)ZU;sxO1@!saky}38kJ?Z$I-7lb5qVo*gIln{r1qMsi^HZ z`3Oj{9y zw$|ZNX^8{SmNKv7NCG_0QEi+(q5#PO=6WOo#dkhX}_TLrE*lH5coL$r$ioLQ{# zFtBPA70zB~b4v(mAZbVm1E9o+wqW2CX}EtfhtDCWBZAVL5`ATO15sQV5TVfCkTc9+ z{Rsk4m{9`RDJ13D)Zp!CMK4S6fzoozEb$>{@TmFGq=J*5Ts}<+p+h7E3Y>WqnytX6 zPTY9{-2`eNiu*nI6a^XpBv1+gfe3`JB|ai+k?ZWhQ3+Yo+74^%_h3RPa@eGgZ@V5s zSAZQ9Gd~VIlk2M7AP$|FO|n$4JA&6iCr~sv5hkdN;Ds%!lqsk^MikJY1y|lnwT4vYWjc^e>>&U71i1}hCEm|$SX}|6$hsc(8 z($2x&(p*3Eqb-Ue+cj$c09Xc0xO)62KC^pUarvzf%6;L%S2vc{b-Rt#xc5)Q2QNtU zC8=~Cs+k?bUfP6Q0u@ov^}*0B$6WHjRRuy5 z%l6>(LshVB;bt`ym3{b&m47eYuf-O9xyxRY_eUdgn`n6Mo%I)YI|YePDdj1n5tK-0 z<NrNJ#O})`v4r+&Tt>MPbK9I~hqxbG$&H_9mJfS3%Ra*@Rl!C!{Tu zSE{~lqI%OTcz{ufMpKX)v!)k5?*x;bK#eq9HTpZSnMG@=tOpkQkllPwYrSpe_@9jV zUd4V!i7>a`!?tc$>aPX#k;kn&m6O8qJ(7hn2C z>$5hvaom#YuAElcv-Xq8b3tq_t;00Ow1Y-9}-8<-CibNRky5`|eUM4qZl zBp{!pV14A-_KopFNZj(4hMR81Zj7XnN1KcSv&iPE;Wo`vN+yo8Ye!X1i8p*&MUs3( z>CmZ>+jV4Q3YD3l#2#8P8?;u86~UFM`L>y@g52hl;|i%B{3&Ux68DwsOA2e~2c{i# zrY@3&Azd0iUP}0Vm;`n7nduGy+P{$ps2k(02A$sD!Wn`@!F>5cHZ`h3VsY++I3R^C}5dJ3t_G_v3iTg6rlFQsZe|Ye^tzwXLVNG1r$-hsYYzMNgr0dy#lo(hMFhxHAWw zBD21%5R&96jmnPlXGv`pp=lvmEcj@XQH(1Qn%wWbX?!HGpJMC{;k0%PSeFQ{2b$LS z@hg>5pC9Vh7758iw;f30OnwH7=fIJIU^!Jz9MtWd$UI8bq9Y;Jw&Yb_9yTSz#tFN5w(`gr{%TL!`zK;Npv$A&j*`X#Z(`*}qXf128DDafp91XHsyOf-H{bNi}TZR>J%dX+Y&$lLMQatGp! z++(Z?twgAT4?sKdS-rZHx}Z^CaM<`8A3;4y=I+Pa2ta8K1v&*%q>5*Te>*aoID2$l zZM!wOw-n<}t<{YwXb=If(S_T3`lJnvZ3&YrW=#$_>rOBlR%uC97IW?c67b~#6e_!M zf^jto4f_?uEAF%e4~Z2itv&R@pE}naQx{|9XwfCg+dGH?>`H0<@)y~K+jk#=i@LD5 zJQ5Y*GwiUMdiJ!#is223FDN8b_5!bVCaf4`G+F-cSu) zx3=mVQjIcAeYlermoht~iYlbpTx4;1g~7+xq&`0^ZXGkFY(^~h^eB9)9g-h)N&X@S zN@5Q$XCxwklCC=^BX4#(oX`TCpR;X-nMAi^0)lEn2f{msDTYa2X;rYk*3p1Cgi+1S zZ-p84TVRw%U>XIhuwiI1E_=Xssg>^Jj@}|c0*bb7o>{uwE^WezraLQ80SF{yO)-D7 z+Oe`KkgzPrbS`JQ3~8XMl4H4Hw!>;g`u_kQ&GNw!*CwSk70ePTij(m(J?EjTShLPq z#{e?O4vCF*-`gJE`FG~neGTRi*eW^uaQ+>+cK$pSUm?G-aUC60j=p)dw2d~~bF)#< z0CULGAOXardp*>viiUr7;NcT$iaE#|qU_=mY}>DYdY}P%!bYRky_hk?+QcYLQHJcu z+&FZ$fT=FeY<*CqTlXxMV;hob<)PxGHhr}1_hJU&jbp07uGTWXSd4PJQlLHS3RceC zPQlQ(A{3>_(n5&}LcN`szu_Zg#5JPA_f^EW>%!imLXh~u*xQ2d8MyZ~#!^%~;OfW$ zAm>63Aa^B;qmM?bzi)Cv;@}ecQB8c`WRZQ^ap2is(CZJh=bh#RB|#}8;_25F?WXT4 zVq7&>?&!v6whdvTtu`BG+~arWZhij%xl5O1HchW@u;EhPBQO@A4pqhWNGH2@OB+Gr zuvo9{u@#nSD*i&LzcH*>F115mW-wm-&jN$=HP1zm4C?lx1=}_gv(k~21GuVr~Z!lZ7wshQIkrhc>q!lB= zS_9ci;?IxYcg4-9;6`{{SH&)>7dV)Ei!qs&;A_ z<9)_7O%QSBxN9_DYjWluM1>~smfqRh`;)eQ(&P=qt&_Dk9LD2a_ZwviR7TfPtSR86 z4C$JMF$27=F6}Yzmesvd0l{3mRgcExvr)l({)k+4J_>F1UBrAx!aS)TWAS$RUi{mS zTUePQ^0|>FxgGZ$aiui1LrPK&5EKO~o+}-*%;a)5CR?b>9t|}VR?hqFd?phxE%DO% zu@=^~trQOq)v@3F38%Ldh%Q{^2Cgy`z*=I*>Zi1$HB+!-LzvtK9+9fZ?PalafIZY4 z%Dp@@zCAGTWhr14STY+fBEZZgn`wH#XSJWKA3ZYUys?_YZC0 zhf`|zc&T5JJ$B7<*DBStZ}2bBE=)YbkZz5k)&1&pC+`%L^sZq**Ad%$vxYeXV9$%r`GNj`Nl7P4JqPRld^ZgDM_;+EoZ_$b{GD41^MO< zC_e>}WzCVk8Bq>fklKfy7tXVq_mYD-i1h zo|FlNH4b@>JsbK*&TNy}*l?pI36sNpDA;;SAuAKuZD^*F97>4#tAiR-RMmoqKvT~7 z7kGI#Y1ixYd5D`GF56Js?Kt9DS4~N6BS}5SJVrqHi->4bmyDgQ1?IIHBHW`j5OE%e#Q>v7vk`rE;liP;UT(omfp|FwMk?4C@ zbiH@A!M0hPmp*is8;uPFNOenFfMkY&{WxiVm4XOM-rxXJczv&bcXY^MjJ?i0wOupe z&&#iU?&(u5ysdRk$Fj+LR6ATyi4)#z3Tjav2J75vanF0pzwlZPi(%A9HQ)WI{EcKX zL`{-+mczgfD%>OT?%_K$H`8poUEH}XIuSMb2q7n-IuMX6jEMK+B`d1nD7ulqk-fD9 zRAZgh>r!tY`d#9_JY{Qc&Tn@I1q}I$E}CUpC*c+MVzHm@PD1(h4(T3+rfg5kz6S{v zIs?h^)l2N!HvOrzvu?4*jL%&R$89=&qJUzfagn&0)qBZW&6eD;wG@UXkDwr8cN%GXef?{3&!#j7r@iSGw$r&wT2mGqYxkMRJxrs>7FHH0-lvs$sc-}Q4~aL zdnwe)LP|fi4UCG9busB=ka(iOZ*1*A2$jN27Lb6{Rk-pq2tqZi`+z4BR&ABdtBC0kxk3BKExd5lbwt-mFP_l!P$4fy%mk@ezBON+^+y^A7(2 zb4|By>=oU&VpOK$Mtv!1N>g=Gf|lw)NEAJBV0DCSZDf*-S&!qcx|FsU3#A?*M;o;q z$GjZ7?L~0p%Up@gX%ZBMgD5?peW)hB&~a+T-eWBQkW1A?m+pKP;>-tI{I^AUt1-cx z8)MkpV%sNn*kfFG%7I+jE&%#tRQOwNz8?tcX_>}dy!W)%lMxe$XQ6aYXgf7$hq7t7 zV07Hc)lWBut`pGASkJWv5>MZ!%ha-(WDqh)_u`KQ>zk=tWyT${02@F8G!&NFLI+00 zGwvPuLPr-ms$Ld!FgL*jrG@-M;^~9N)L9BbyiSd8g8Z3PEVMN#Bes}-X9;`gIH)|u z^B{|<$*HyF?Te$2%eO~VSxP6(6(Ab+^*EBhY_SRz1Ax4?x+TowlX)H47Mpx2cI%k8 zyr~F5c&w!!I)DxxU|1w15mgVq=6oTpYr1t!`=;%?SZr;{-6fmsS6oUIfD_SKB!fd- zN?PKC(;qDKl~u{^50!zwbsh>+wF`<}@y?Y-!Pl|OAs({7aID?va@N++~s?8VbWI4}^yT zxEnv(>#HeU70U}8I2vf9OYY6P+1nDxZbTIy45pmclke>Z*^5QKN;z~I6`5vs-1fTh zZ=p9~NqXBYsnBFDB&J9xklKnCk~64MXhsirYZR^Ncp9d7i_5EL^}w(=T>Rkb{KQZ^X_xX2?^BMK7 zhw+atZ7xE$qT#ikT)6tQ$+y0c+=$CPiUOdH;xUMB9j$R@e~LBnUC-NY=h){W>b5l; z$1tTE)QzDo+U$MTZihP9MI47UmSi{W$<(V9`$(@_C1P35E=%=&=X>a@>sh7h@)d(D-y>RVI?laU#NQYc0&UCYkEaYw6ANLjyQ zwk7xX2k`XiX(~}qDf3O1?J7CstJI}HdB)VYLtke zA2qA`UJG2N4T;P!d15W2)7PoJ{dZRn+*F3P_UB9Ga<riUd2Y!HwOqH(Cq)eaR5h{nqCnNM42LQqtSWN`WcVLWhYam8Fli-Nm)dz(qPI zXT;OQdc6uOynUd&+c#|_`Ige^$BqPe9|QLLK&i_^jyn3=X-^jFwWSGCic*}(Izh;H z<4C%ljBmVX7FBHn-=jrT&%Zf`)SoBpTRoq9+^@TXjcvYEt1{fOA)9babg72jOsLS> z3rayC65$p0<2b|PaCXh$6BAYX_+IQxUvqNt}xR>(?L&NKU#upRykXXaN4%~ zNim?yyuRFc(q_p-NUbf?K&M`J!i0{}adODtGALZ~2>^0cOA9woAY*uw&0U^F+SDmT_J1|Sg|Sv`oP?W5Ks~l`>1`g;2nh1ywvB6iyfM^f@et3`nz30Tcy_8 zy1I#xys&imSN16Dc6vGA54CE0Y)9L&p7|D4cS3hvnNA{Vf%6fi$pV0OVesFOlSefB z6ujNEdssZTw!XCxx#+8#ooUB~rqB}Bt!n;au~bmjfC*-}b^YIK$*0(h4#&K265ncR zC(;i$&wUI-vRh<2NdYGJXhA2hY>Iuja{bM3O3;s?DGt-SJP7unCVD>St5c|EZqA6( z-u}!B&6!D61#%))4hqfPY(}8_63^MUwdL(c{{ZS!>PeRwAMD+y7cgpJDZXvKzRVXp72y)Rmlmk#TzJx5 zaSBSDRTLB2LB%&UZ-v~Fh+Z6A-A05f_5$N_Z3Y{NOIp{`y4Tg=Igx|ju8T`6#yDO$}%abd@8m~i$!R7954oqa05l-&0joR7^- zq=ta~75%E2`mwXjwsZdRGnUGvO>W;L`39}%b^}EtE`$M?#jgnJ!V*`2wZs)-y(`+D z+#F@Pt!=QU+i?==%4$gkl~mK;hc|NciYQiCJ@fSrMMIyHd=!t&qm3iFs->oP4nr@3 z-sUl0<-h*H$I`mnwx0aW{{WZvC1C;Id;uh*IlTfEDX0C~+YrMd=$K3GlcJ$mDy})uj1rRkvmJ}3+lV0jm4cm6s zu!5gUV(vO+2CYh=)kE5F-R|m2HOR8HW;&mK2#wYe zbe9@%I?Bs0Cp5T{Qlai64jCaFG)6Y;c%y|vkSyH2$+jf?w0+kieZ6VY?XG!D=&Da~ zZb(?B9E^;Vq18iO?Yk$MwY?|AXrazaxm!0)?mf|+t8d!#RNkADcGi_0i7u6D^GiUK zrButqmMR%WvC`BEEB4#-Vuz*9E*z?wHocI1MSP{YN*e_txsVTcb{WBm zbf%0tsVs(Zw$18z(PHPax8~VWh>Yv(BQYv7f%ebPijnk;H=7T43Gqg?+%`8NwE+5b zNX%}V+-EdNLS51EG~zTY{dwU5l$n|l!ALsB-a+oArgxMzkcmt<$u#i_EA(N+IlxT{ zWiAceieF~)ZkJCcT1|QaXnv!KSh8_A@lldkNZv_FvX!Kis3;!d59q_Hhj2}UNIIbl zA()LRUmNNw1GE8Wu76GnSP&OcYMO&UN$!7Uo{lT62NW(wAncRGl%Z6w#G8_La6 z(Z56*k`vI>;Gu}&^+6N@G8y2=T&g%gNEVHoV=%BSS#52_N%` zN1y|+dvIvtFID``emiFA#Z8Z<&9f}ww73~VY49L?ZmYcFO^|d2nz57QCF(pAGSr#3 zJ;`CbOO5jEPj$xF>>(u`Pj*;Bv~ViLk&&&mbWAlNEAGwM)B`5VVP3aV3TNF?eid{m zZ|lsAEj`fS6m|mwK=Wn#0&H0tD!0Q`NpW=qDPy|0Bv#zYnaO2Gz{H_= zlWl=H%gx9o>+FCj4YUB-O$KI}42B#K(W+%*6xRc%_f%VPl_KRS$#UG*rSu(x-H6Gg zE?3K2X2i-hCHWOl(cvGxglL?H)ozIB3QuI0PPIDKgs%1-uwMxQC0VeAihhsSq2-1wT$P9A3^JiE>C1J{_`|f zYrAu((w+0@l`gWi|@XM}0?6ry1r?0da0O6ZQ3{Bky=7kps8{%DvHnmY{ z=aL)n_LQAPH7B#C6?Ld6qV$D?+R>5H57&i0Q3<>&Cfw4r_y^m8`9Qt%g>U#vC--TE z4VWmuK2`;llUh{M+lKO3rwzyzxwEtdNIzZ@IPjb{{TXJ=(N?WFXV=|^jBrOh@h1QUj1ud`=RJEl}Dw$PAmFtd?>{b%v4`tIR=;}2MJiQ97y{%%G9TXwr zQwwNa;<-o2%zTX55k)*C(wX+-=pM$5$ry6^)!g~SQkJL^U@Iq3s3+aiENSevSR|KP zt#^ZpfK?mmAl>LW1+n97bQTZ9*&Onx(>i)lmx21Ird zi#n?{)d;x5l9GDP0Vah)a@F-<;gw!n;ET)H^70+AME77iQcpUi$fAt@eaA-cWQ1wm~WFdj~`m8ZvCd~IX-6GRwKlCe~0PoQkx1?2~R?t%A(ASvWXFv zD6}Yb1RPxrLaDM=yi_8=Bxa#dFjmzIiZGH6W2PK6lqol%&~o~4tDJ$fSSck$(T(kZun|_3gJJ;pOqbuRlimoslMg3paVAkJBw;x)T4YSXW-zo}GSPDc+*-sz3Ok6-8PV1|;+tXac3iyCf%|84& ztqlrhNOq}6hwha98)!T4Q|lx4rWd9ga-(`azT6x^q_8n19qLs}sz6v|4L!fkX`OM;fHt zF)CW%E-aJG+_kiL&JDp9MQ)d!L)TTO%u5Y4!sIx;0F6W?27 zvvyCn$8%>HWM9%9&(yj@$bXgg>)AGYj^)_=gvRu4&mG6#w9AR|a~;r8!<6hl~&{_ zN=ibUZi7#V_E!_z#_}ns3TfG^8@p(W+GwpCcCE!E1l?q^?3Gnnyp)ml<1}+4XwWW@ zf@_D5dBGQFY+PKHmi4u%YM`TuQOx%axNmcI+Hj(O1&ic(6;uA!*`I|%E=yzyip)Mu zC_5>NmG$3QpjxIM738;5s&Kr^Es_i1xm#q&6- zd^NSASGlB*rnMAJd#u}Z22__=eIfdUNKF6=rd@F@ zX^(u4RP3gHC>cS;Ro(Ss=B`Ow_6ug-a#ey7!s1DzO{CMJikCu3t~Cr^M;&0=xckeI z_r3>p=jAPke*_*Vy|dD%AUhGa_YUfXwqqa#Jg1*g4fp0WroT=eKYT>*<*>N=m3G^) zcZ_FM)xcmLl*io>SmN7@5G;JQyW+-fHwj^ALV@U`sY9=3CoxdE^_7-Bm0Hha#tJ~o zYl8ZEQF+|`$#Pk499xs>Lt{!@^3y^(e-0FG%Cl*&N^f!Aoa}?8t~3dpIR}w0vXvTT z_3ykSDX%j~HS9Da4-N*-Ce#H07(nO0A7{Pu7m{>261A z%XHK!H{IN}N;v9My5F&{Q`$|&>v0jIE|f00R|-h$LU9c~MkX3Ds{4-HcT+GvN02>f zaT^?*DW`cjh?6BL;51ekw!9h_I;BXK3thR1FyUJvw{Cd`p~p&2 zrFAtY5%acJ4OPn)+gM0wpee(8+)0Te$h}|V+E|JHsV*T3aP|!~2ACu`Nn9!!RO2JM zEm-cC$Qwl)-}e+NI-~9zUha)1L$4$@${q#Q=~51y&RCYV!Al^aWfdzayzQ+ITP6hh z(;i;+Z*bhCOug?#$&~n@ZEd=$C%9=%GR2bL4J1sXTSY*;-f&$>)g+Zlc5T%j!!|mg zT#)Xf2`foRP_J>rTkJfKVW?6#E#VEk^TO!erE`x15I@lqjqx zG1))~#Y1lGsbieE<3hCk`P@5c?HbshR{*H?;10_x?^Xw zjyPlRT%nk{xx1W_YxJbZTPu3WmLR~iJ2qRN*|OahQ~>NCLk zuD&ndtvsP}+wVj2{yks;pK-$zpSC;-u(xL9N5tAGV~Xv2%EcRe?b){1o!dL+*8Dq? zC`)N|BT^PAkRbNrrMR60lSeB^>SS20N4c&ly}%JK(} zlkSA_kuKG1x<^x%Pn5aH0dKq>`=<>>NGIEfwm@VaAzMYVe} z{&kpfTrJ(K8Mezkjnq{{R=W(zw`)zUa~id^bCn)Ei-Yy@W1-hl+MRNhB`6Bis&))d zWv@(7Ip9=*!^LO#Tu8XPWKMTOTtFwlc&P`I`1fq_?)1HFjibEbaxJ#hhFARA03{LM zTdnFT2?|)$gOTwZFxr_(uPp?Uu*T!ar`y*9YmC2iHqr?hPl#~!CL~89xQe)n6x3=5 zv$qyEwyK)P1I0S;duiBPJ8f?r3U1xHotYlmwK&p}T!7$hX(0xwC#q}IVlLMtA3LLW z7XGziyu*2Rtd)kL{JlyvTPq|hu~j5ezoQePMNOCxQF7X7NkstYN^q)d^0Z1)9I2sC zUX;g^ev*J!h@A)oE3I?=IDbZ@$voC{bP7PA=yBp2yd1SU@&O=@%y@)Cq%J3kYI~7( zqZio1O_kSQ{7N>WJj4jD5HkLi4k9zGaJD6yIZo|&Fy0QohKKAZw%f2mykFs3TxCb+ zl(Zg0H0(WIqI&d-l)-V?U^)8#Q~lvT7MSJl1y0=vgazkE2PKh(* z+NZ(^!=8YU^9tDvr4A&70qh_Ua92i(UTCDtHqP9)Yb>qF7SkeqlGwJ|op5QF6&^aA z6ha(YQ>8WRsl+ALX5QXW8=M1=C1tYP7xz}ic-maX@jePb+8lGf_bQ}6w)W(UqAkhS zaB-nf%0G!-!H9gm>a(?=k-$}1cF%3{*(R4;##|f4OFM6Y`FD4z)Y~?6$!)NZr(}FB zr6kg|Bm?wfuYKHHeW9 zQd|HJuN6r9+l_R>F;Y*VXg5EU%=NXYo?e!KIiK)Jd^PD;dvhe`?QC5ax8U2C{{TyF z5`gn;fT|QA3I(qtL0Wg==Iri$wk|%eiNt6Vp3{C>@;i=xl1y=fsr`$vydmj_jd()U z6}wb3xc4(;s#fs0Ar4D%L{q>3(%O51MmaZl_agf%A?iIXJckAKGqsnD_XA~u4c_=b zSK*ABsUYRzCn1UJY&-fo+-TKMz1)#DeE&u|)Wm7o@zTVnz*Ecu6`-EV;tuLT z*FnH5U7l$IRxI(lGJg)d3YE9E^R#UEMca~HkRzj9vmK?p89+IyVHF^K7^q?~l1%dI z^o6YMUCj-&VZA+Pof59ujT&@#JE|H}<=t3A(Ogp{x4gFiBUZ2!53>yNw-Q&&CqSQV z+-xPHb_x=m%X#H1QBlj=mLqDdP1MzRJZMET8IJrQV^0bQxk6NHPV6buqD;uNQ^56A za9<#yGA&$m0E(um853Uo6_JurWPMXUb?P&h1zTlWUX>n#vXTOt9daWRky>=Os2x=& zBYN3i(BV*pfW5g4tj3C%PApb{X$R=ScOq8`6QWZa^3j^GQAYg6(Ur2Uu7nqp5H{!l z(14YS_3jw5Y_M5zPZdS($m+-Scqq4T9knZFEgQPaYClK7ReZ7?4#l}m4>Rt zZ-a*9Qr=Ko>q$;+epK5sTz1iM{85B>gC1ZtHLU_&%eN1|60n(+Eul=^gUnhs$1Us18DNafp)9I zNLtHk{5JB@Nq#z(GEx=VJJNDEb%op)Sh<7Pbnr~Kmv1rKg3z(;9YknXG_Tw(ap7J2 zXP?NnFXtXJOo8*4213IRFlvw;1N%1}JV8wot9svoP5PMa@eGY@?1i&AWMsn7hq($3LRm|@k5p=k=KN>xHB(zw-o zU%qFb_!GLMb1Rj_v)(wnZt}cXJEFt9CEFuSXc+O>D0Vu~ zbFCGaJ%m>rqxkjCG3F^7S}&}TY^yED0J?@kI)?yLDL$3vub*} z(n$L-?Drh~+9-#9UubF{QRz!uL+Rjb9!maboXcucOuF5@_FdM|TcU)4)Mej>yPJJJ z`qV@?RY`X1vQc60pF4o56imI{w0O$*bh}z*+Z77y8TXMQOEgIXf6de&+t38Q z=~!lK^5*8qU2pF-R$be?ZCq6*eU|6MrB}FQrNz&Z^GZ+jf&xCHi|!uL*(ymKwQr$d zd#d@k0%4Zkj~vd5IkbA;-`f?3;O)Cqc=oqcwJy~8TkI7q_`0YO^mWCW>$TQbWI6u; z7l`7s#&`26WWyXlbRJ3|+`gu7_uK1xrrP|CwYXBfZnG7_h^bIHDZL~S?l`i+vTa)j zl1EUdHJe{|Ln7<1J*2ePhb1dVcq(LM`_v2%=iLHYuQ! zN;;n2wCHJ4;qNx{%(m5%6UDs8sbAsTwi~sA8>&TcJ{JxL)~Tm0@y74sz1woyTf*AA zW!@i&hbT)DSXO9yJA=aGY7`)YmqSCAH9fhwEwWGvVm0v`!SE{-+}mdF6?S5swD3HS zq!nWQI(m}ZmrcH}O#Y`5=Ih{%)}~|FF77M)=Fp$3OH$)KO-N}aRaFmzufwQgE#JEi zSmgXm8}3{)QU?c*Pp9T0E1?KiDxIe*B&vRTKIZ-dbJUMsU893&quqe zMYh$s6?fTt?i1+QJedua)WzP{AO)?JDvLrN2&S-;nDHdjr0&MvR=bMZra{v)J|>S4-;+=uDV!002w_EZVX)xSS zQE9@;5~om_cznWi_F~Hh#mB*NZUa5?YgJjh?jA;KHrl#R;WSVR57?r+)wXOIu3PW+ z$6!neZEwPq(wlS3aUsO0p}+|WNvJ)e6Q)Kzx=@8!c$OorK~HHMQuVgqW4T@IEk=d8 zBDFHi=?yp@is+(NG}fNun8kc#E#CJrqfo7l!rihK$l_@2LXszXS1a2r?sFd0>=i^z zwYI4dgNSKHnV_13?CXlT-8N=_E2MQTBG+V zQ*VCk$=hOmzCFSZlrIor+^vamx)&kMZ_Q=E2 zl2gQ*C+@*<&FSmkJ{=r84MV@~3@Nwd6w>r$JVT?!S*v_>nN&m;294px1qk2f!KM-Zxy zc@H5S)@hZHY|*^(Pb_;~uP6SE#MywtnsRN1%_fLqD1|oA57Q z-l5|sAvWO(PfX99Z)g+#juqLnbyzg1y!1U6jvhMxuhgkl&bK>z-1a*JQa*b2_>U1P zG)Y?0X;X_+j*7oDjg6#~-kDg_=0#DWqPXKrBQFzcC?o3_Y9CbYAROAF>QZPxHPzPw z?L`vtHitzT?yD<}!hYoy6k)XwJl2F2Vg~jufmLe8-T8;7m{D8ET2hrMqeW)g=x9bF zI!c?UNyPQibzrqlR_S!K{B;3JEjjxuo(35}HA&sY)Y>P3be@ADiySjHf{K|V9aXSb zYEN7^;+oNJmlQiSU>B&g^G})Z{{Xs}E1Om7AXL7!O$q97k!=#)XzPq<4Q1kRY6IAC zX95y6DS^5h(Q{~1GK7=eh#S9+TQGze%9{N)@rC1-IT>44#L1C5Ew1l%bpZusF|AM> zn(?6iiYO0x7;}x4tbIzZZ&yfR0ldf_)XGiK*OVo}kfg_d#=r_S2~eo5F(vb-nk-n> zwV*g^7oC+JNk+5(x&a zujU%e)8SnXBCar&uQdXSRvgXa4IM`&)b28Uu%+6sajp#ygeWPOE8Rs@eYlF7Ydeb^ zr6Anmw0mfqu32|2l&NZl`(YKMjs4;e(3;{g_LnggFfT3T;zIP@#eRa_wR2J(5}Q)n zLeNm4kg}Q@0iF##?Cb)fs35Ypke;4NLMH8m=t5;SsO!q7sLvjYFw!XP5hcXG_f1o! znULk9RAGUMagL+7QR`nD^C15Nv(7%ui1e@wZI`IdoJ&@2P70dNU0f| zuvD#9{r>=A!Bd0^$B3T%B3Mv_3e!( zDhjFp0Cx`q+Bhm~Sa~OyzE6ZCyI$I#xMmB!;8KcKL1r27f;x=51|0|q)RpX>ZYW#j zLb1sAc(9?{>~{AlbV~IUHPr~Fns|FpN@115g=m-Bwo*8mW#Q>Ww!YALN{>F~-?fND zfl@nH*DM1qswz=)8SQ!_P7TSpW7&}Oc#1^Jv*L?%P`1i?PRE(*+?e3Oj6r9!uJWKiekfZ-P`ar9+eW^_+I43 zw`_CKIke~GJ@;$t-_sPk{et$I#%iwE{qr02lu-HLqMJh^x%oLDDC1Q?&F}rZ)muE%Z8^KN5u6KK$@dr^v8Pn!m5-Ms+Q90)TJ>+1F zu|BlHmrP@WngdAY03Top0r-l9`}X%^{Hu|crrcoHz_!#N zS%@yH?sCUs4HoT; z+)i{giO9ar<=435bZ%(djvLooHP$qk(yZZn6o-_Lx^*F43KTx9I=St`asiHyN+`^4 zd1l4P_Q|LrR$ak)k#?CTTkATVC0%<-uWmX6Y+*&D0iFw+cZTM5^~BDr+Bv9r zXv4JbP#Q_^#GWSb%-={=EBJhgx<2;=+DHl&=zDP}lX~};?ZBkg!kwL%FGyZnV8AaS z02NI-_Tj;3K!v6Y<*!vQNB+e$6vU;d_f$mhM-n>*WcGXTx#FAE7ziL~DyFW3zaB*; z3=>j2JFs0>h@nI2NXngXbWkX^002N3^77kACWA719+)W=MGspgCoo1w3r2zHz->n& zDh(;@!HWV;t6xQ@DFHkq2QE`Lc>*C`2&ufwZ5y~-$I*hwTE173MpF) z2T)o{bD%vBW;2Zb-gHj$Vp@LQ-3^p^Qu@7qg<&|$maY7;79P&u@{w%hs12cwySkkr z4PBdT0EQ%-fOrg&zux!acWrKQH6oiw4r-^m_L;LLdtq3Q5%+(=IcM9Y+Ga?%&W@l7&?^p9x8)zJ`DJ~>+>Y7uQDftCTTKtd59=_95Lg>H~eU1`+d0V~u)wkSLO zxLzcfZ5HVYRZmgf-Gz!Ln1@I;B=t1?7%M^tNuu37M`dtg0!+!GG~Zsh@(QEK@n%hF z&|x1bFXkcE$`n&nYVS3{gNg?@EktI)KX*!Y;S^^&iPN>?m+y9_5;O%ofDc@&gD`u^ zj!ompC~jqy^BdMl)u}Bu+hg0L4Dkt;f4-&mlPLv!RME7pkH_Pkt9dH^(nE-B9?cGR zVD_N&gg26hN?ys!xSayc?cjUY2TW=+@*i;RYBr<8JEp3?!>E3CK+qaT+Zb=FB{fQp= zz+&6cx7GC!q5ccEst2KqWS0O)xydSTDtJT62S1}4^PcH5V0bQJ&&DI#FLCCWyE=a# zGkdZxciSA5W{%>OeewV=ID=C}<~^9ya~4<5fQXO0crH-d`Aj@Ft##Xaqa0J$miHG5 z-M0)`98I@o?dw5|l+TGNM1v(Np;|m^$d1e~+IAT@Vj}6)HB!C8&S0mx4#%*ul;EN0 z@Mqqoe3hvQktNatOlv|)iRhH;BBbKkGn*KzmR|PBE2z{;;U4E`b#N{*%xMi8EtM3N zB>Qxeg<9slwHy);^CC1nQl`$|yQ_}(Ewg%T>&uoc$zxe|*>h3Alpf}@x8wMZml^Is}nW4ggO)#ol{)1?ZhVP1-FHrD`V|GLfULAW^{lP#YsZR z0#QROToQ*9@crUc26fNHiuBOEjj3r`wT?>KN6<(;s!!XTWim9#cUc#9kkKJ=l7d#H z5!q3eDj8gjTTYrSKW*Uyq!KmVL(r!tt#ev!Zrr74WD5DNsVEd5;}O#pBO9eOXsXlY zy^7tnk-!B8@((ad51~o+8<#fj)i)l_n}nNk6Ovg#Ln66?RziEb@m{-! z84txutp^jD-8ZI;>I?}tkoWD!V>Fva{afu_*l4cZ84^RKYW^i@QKwN{N$va;cSo)v zqN#TWxw7J#Vx8Z`929HDeCf>S+nd%F-khi4t;I;2cXd8z$TqZ;r72s6iO6IzV9RWr zXUw$7J?=qg`)9q|kmKZRFQk0Wf{Cv?>ic~w{$t*6T>vDQ>v`9_M>9m$gBGi8y$pW? z06wLfU+?Stnu(@!zLdvR(5b`tTm{KiswF`OzqnTqMY9?;AyplBJKt6`l;LT;{V*fS zwM=Q1{0c*AN`AaDeZ`ziE;UGoV2oppdWr% znj31+a#fl8t&({?CZ43joyZ%ae4@2pT?-1;P~yR_yc|9JCe?#b6brYWT6gH@B)L~z z!*Y^deJ@<3fmN%-;;1sr=5S`V)-g>2Z9Udj?3n2T7{lYs(1lyL)(xe25k29 z;uAMoWcjqG-pJa!w{`A|HHlE9+fB{%GO5lm@0Q3>rlnAH7>3McVYh(1YejR}`$Hp= zy=+7lf_SU{0AcilxgDNZn|%4GEr3%kED#m%)Nzkq_Y?qhU3-P>1f5_x;DrKnw$`=r zuH|oRL{PZQZIrHjw5vgBO6Y(RPhPp=?Xzy5c^8=N0a*8RvCv{}{DvBktHDc7Iu=8y zSw9z=tiGII%xnUbSh%~Bs=B9@hV=c4?QgtB+>&ESx2P$Ed80@p-|Wi}SNN7n(g@A{yUe&D|vv;jpl$5|p7TP#vBTij=!0=lN@0#CL`kzPBI{oj3mg zQrvqcBgxxSlW1~NCAXS!gwZ68H3T2G4Q;kn$aP()QyE_KV&sUn6VghEY&@Z~qO_Gv zz9Q`UNd=c5(4M`+5|=w>M;d^u7{26NjU4)?0{J0#wa2&Fnu@UM{^34q7HWG*CoiWF z^KJJvp72&X9rG2$p{#JCYI8;E?Q6Jgn0HyxQ8`eDo)~?%8%Zk5Hq8O{Vq0xu+V&rH zME4guTRUj+!;*($U#@U&NtXil(Uj#75L`(IQJ_zV(>zX3i;RzXs(}W6;jgP7O072y z=Xvdymvp&Y6KwO8eqjhxK|}n3g`vemDCr?GzT9_qm6NGdt>w60+?$4d)ov+bLV#^* zR%IyxBN}Ed&5%dX-V2=fHbz_PlOv9$BqwifDUsJAEw#tmN5t|i&k}zy!f30H zvy|!r6K7gxBH?_P^D*2_t{dibIZg!X^IK9co26dX&bQiU2?XnpmrCCqi;palIoCoQ6TmZ=e>FqBG6B`r~6NkEXo6ykKLLA9Ma zk9{#6E<8LZ`24((J42$A?)+WJGPVO;*>$!?g~zuEA14O%UWpjHB`Yz)Ta1DgLbMTD z3Y_|9h&z4LXY;=#+WDYY^eWGj*_WdnGB2l;a1Ut!{;XO-S>)jr zdHG?{BrGjp48l`DLCYd2aOI!`i(f;cGGttCvKmT?ACm17f#snBy(!@X1oPV%B^9tq zJb|EUQ{#1U2K3q2TV;ynZ*J#xzA(g#Gd9)y>Ln&ZK|&f*5;ZQSg*5AkTz>4enT~a6 z8fa^xvE+6hO5S!v8PwKHqEyG03PFa`Oy{|Zx$J8v?*>nrj&#Vf+#9M zCmz3Sk~5?xxLM+cQs?*-qhv9?n-Gt{7c~nW*PY<14 zLOFJR(Db{6sB`B&sbuSY_9&+fDVeFEKHM7ExHgnUy`8RkAe~VLU~%AW3cT(c!w)Gn zEXB66v8%gVj*Pvu!cxt1zyOPHYiLDW<5j-X*w6Al@V4yEJwvlD_qCR-D7rrq3rbK) zQ7$@Fp$PyJo_)ZaG|TQb26fX;=Ho&1m3}Rsm0vi^wxV6v zkhXoD(vYRFiPEM7S28uBropFuF^tRkiL>hIY!V-$i>sjgkYs}crQ|}wB#p@)7+0+M# z@tW&4Nb-R9aXFfQi&%37ZuQ%cZK4y5Vw)G11u zV%&(dkGJ8+E$Ua8 zpM;WWq<8jXG2XqgV_<6|C#%G+p6svY6uRO`t^fcI04deh_h+gbfEM{rZJsk`LQTXW z!nDa-N(xdaCq$BI@z$a8hqgicH?hoAfIuPsbt4nPH zTv@I_3gKo#OSDZND+5VUV)-V^^NVU@Z(ATJme{&H+Jj1SpiV%MQ`?DggLh48s4?$m zKI6(QBhsBS2H^QrD%yz4FJ|0_Rz`rX-89cGm~9hb>-V`U9r(L3e+!yoS$0nTMd^22 zw*LV0A*tO;v$nv{5nqHUN*#OYiTlj0sfVSE*TqqAn`0u&pEg-)E5S+E6#d;9UzDRk z<|O!lJ))Sezzj1 z8*W@>8&(>0@>qWQ?j?rWnXG$j=5r}$X7kS+UTuvc=e@0vFH6*4g6qhN8>)_!q&li( zDV=cNFR-!^JdnaiigmSn@3-fOG|bk!!m3$^aJJ7!nQgm8nuP#IlOdNfBBGj9Yg|(< zu{SXMO(WK|++OC#-VfnO*LsRLuo|Ydr9I+6P zLaXKXCAFNLG8$ED679F;wjGahWqPa8#E;jHI@aNzx>F`)?4{H=ASA+KYpto72~vAf zk`K4D1tvy~MG^!;=hdzRBHOCM-S)w9vo`0FCa|`W;m(RuXbJZmKEh;8yy5-|H+W)2 z?USN!5*2W>aE9<>VDJ6Ab-Kj7%D%?(w);S>2}6k_f}*kEsQ~*9FPFQ*7hN(+Qk+&d zkL`u*c&Q+Bx{J7a6-<-FzNGl#L7ioXJIZBADf~V{UIg`^N>%Lc#+J>^h`ePU0_X4S zJ===74up~`$gYv?QMk897}-_0(Dk=*-fe*wU1Q6U8dBRStp`Gro-ixhn4`WuI zohoU{wq5bJmK~1G+Rt`qw457O-&2|l!#EMGnTk_WO{+xb2j$!fSYFS zgxb-!x1t7_klp>zx)J6zUx^B>M@;c$+q=T^3v}5cB*)AwYq>q9$8LKUM>9ib{nv9} zJsji3JI{nSyxohxZC@ujq2}A}E$UQDgjF3WksZP)v>wsbCn7pyy*4)Qv&-6vVLq|U z9|iG~y1l)>+nE>4wNZ9 z$20Wcu3&f|iiEr!KGx+NKT4#81F7#ZT;7J*6*}?hhrl?Q#8*y1E6;oH(f5jp${D+BDT_uwD1$^+sTl+6##QogmN> zNv03-Y-31nh|aou-B;?SIJ}=>m$#9%UWD5gsE_%UN}va#C4UkFg8D&(B-@3v zb+qx~u6Fy^s@OpG=r}H?dS|t5>-VND^*n6be*6~o+!WZJl&H3*qlJ=^dlD;p*^`#5=keWueXm}kqk!-9gAlPN8Nrj$a7C17_~6z!+Iu|p<>(A4DqrsOHNa0FTL@Biza3G3RXd|^?_N}Qa&1ep zFIK5m%S%Q}Xj^Fy$A*@jMH~S5R48!E9|M^lx+^f1t}Cb?lj{Nf?BgmlCM<#n_}ODL z?pvtZM0o3I6~`5xWLo{k(pj3f;OHOXqrr z+E+GqF5o*md~IFiVJ7GNtu?ubx2;r5s%#o0`!mJV60@lM4EisVmVZ;=XLMz*cc8m{TO|o-7IkYJe{bcuX;d7sF=>-&6e=F;6C-#8ywizSh$~WlyzwOy+U{y1 zSX?-*YE|MrxKJX|dmccXE0D;G3@mCWNd?Mu&jOm7*F_xew3S>VsEvEC}IMH6!(4SB#+*akl|bON3Ao2skYgeqgFznA%y1WoAPtkfp}f z;aWoKTw0H4?!)Ir83Lg*WYTpK(?s8ywzkCNlOojHD_8o)wKlKxO+L&tb{5)Q2%>uy za_%#uk`#U$oqm-bCeRXQ-6pLJ+wE+jZoMJr6{QGm4FaVooPYwS5!V|$HKoic*ZYjv zG@JlDR(TJIIqQM93|*(on=W?9zD|nV>wTK+mXh=Eom!CF$?{xq=}|!$(9~uzRQXBb zcB1Sua~Ylra4^TZ~%5i!3BM|BR&ICH3* zFI0En&8c2mIYKHGL+rp&D%T>%P!-kF*@Gi3l(GoUfZ!;U^AKc=BO?-xH9;<&RC4aa zSE9a7l`8B$KQ|N${rx62nU51KE5ctR-}|_jvb3(Q;HcT{$mX9aEnHKFabDB7TDsXR zO@k8I9K88RK1fS{xS9$RhdK~I;1p&L9$|2Nhu(2U_CC{y<+|N_n@O_gG?!B9jZ)f3 z$WY`dg)Qy9+LXg`XJrx?Sc2Sp)H5a99@YNx~D33c<2q+L(I%xg{m(s;jR`aL^%Dtp={A2?)m$oZjZ7xObHZ_fj?g0DCS#ehr|O zB77>8XdG~K%7Y<XaB7I%IHG@ItaePf^#Y!HW{2O%Nf!5=j*aH5f_EsyKa7_a#hqDrMR47%Ff) zE#7ONE$0Mzeqd_40_m2jR+ul6gU&RfBH|Q2&! zRF^5K3js+#L9P*+Hn?gkj~wo4(?rO+an94brAl_+@=_IFzK){aXZkseMBnXONyiEm zJ7v1BA?H!)QKjF7r6o&UlI)j~E2cZ?(6j8+Dlt>O*iRb9bXMyf{KKS;`a+Cu+e33$ z>WhV`)HI*{AqXo${{Sf!7^dEB`?xeMG*xVJN0V;qVOHTpZP~is`OMK0gz_3i^BYJZ6t zr;5eK42b9eFr{iWDy@E>ZXA|)YLJFwU368-g0<~9$(G2l5Rua|dkzlx^hE1k4OK?C zPs^hBTFF39ehTJQ7!;!w*a(3=nNeL`!a|ug*=SJm*-92mfoVN;f&lKqk;YLREXkgAO#8KC%ogtgUKOPgrc7n2t9l79U%#F+$0KRKzbZ10ir!oodBMdCj=}hBIUSE zKX)$Cg9f4pgcIY%kjQ_73}oh?+x$p)EUC{lftVe*Nvx_?chZPP86c@O_a}i*&6U<)wa#)Q?2uBllT3|!F%y5c z;J6fUPi%K3#j&djQQn)5GPmf6d0JasdJCn|OO$T@8Dyh}?V4z0SW2l9}j4_We!x+o~UMStafJ zyxUB+v^@=FDpz8Y)hZssGsUuDs;yf~<~FGol>}qagrQhUXg$5S_mo$vx5CH}x_%t9 z?Z=2-LnJ9mNKeG0LG<84P0vnLwp#nWAwdZOy_LkwO(CihXI02_YM?rEJr8yaTT+CC zC>7JIHFjX>4zh;{BTaHq`!JV5N|RJRnq&vjg*pNU8kN0hN|ll6PPj#RZV(ERK&kJ- zk*4U#X%#dTC)SY8h~>8a96N6P@^&B zhM`x@{{Y4AwXXZOQlO^Jxe;3)Cdf4kMLR!6GrhsK91ocf6{Kyw)*L*kXh-SAN<3%H zFn2sCTOXBX2zMxn^0wyva-~T<63oo1ruzc=j@(FXz1jYZ{CreC=h@|sGujG{ZT*T- zx>{#hWhxx@yth&TN`cgcr9!D%6f#m&GX!lG6t zf2c~`w73TVewh zdMV59T~yY%q&)&40-8Y;`f%VS4rcHDxi zFQEP~j^GLTGxcZY~$#-Nk;i+pQZfZ`)C7+Vhhr+YGEF_0qh;mDvp!4X{{Xh9#J0=@mLB6}HY-`Hkga+F z)iCyk+2w85xFGFIF4?@M?;&atSau|s=^83YQr${Y0PQ5=)tB6On|osN=9Qf7y^FhY z7wxrUBx{c{2n(xQF4bW4-^X#$%3HD--d8P3VJ{Urn2J(<7SbwI6Z^`hXB^Rg^BK$T zt9E}3FQyw>?$cypL^sm|a7R8#tp|_)0Nc8z&9-`ggMVf(joUjB-X20p$f#>g23^=% z=P3MB!(Ra&ofJ+#26t3WmMgGZ;1o%^eKzfTWxnfjXPThQk!(WN<*>tXC2DL`petFa zG@zy|SiSReG=ZK;p;_MJ?Cp)T4v^e};mJU-+2X59%{jO(((h>idDiIhRL*9GgpzAa zXi^*L2k^xT#i9HT+F#?IpcI1_nms=4B!yeHWQejs6rSufKH#A2E-hc95K`>yz00ax zt+t)t+Bnfh#i;Zm8{^h~*ps)Mvh2G|$E6g|$#}O3l=o2)%}=`$SU%NECl8p^C=K)Z zuD1Cj6_Yw$)g?3STaR|CRNbR#yRC@#$w7x;hdidAcVCu`bs%>2IFh`_KpW>hwIMLWWv``| z~pQWVRITqJVMg4 zI~CZuTG^#(>eoV~8GGxB?3*1n^>2wct$hApZaey_Vvo zzT&@J?+hKCkY`JQh4b~{4#$M}y;vVF?K7E;~{9}=Tj{P}yeNKvxwdlMzjjxHMHEhoGc ztPisjOxm%#N0L=Ny~~;-Sl9wCeL(WVFS#PwbBS+T=Bx4`Mo-b=O0ePkHp$$Q2fCSH zeb)i>B#Aud%n}`2ukPndit18`==&0Zhb-E*U{j~-Q1bU9EKN*_>Be03%P}`qwWV)# z#QBAUrew7;nC&0pRzM6x=C+lm%eeH_TlRNyvgY-z^LlH|bmrg4-1%kLJFk!&nc)EPi99f2*ca>FMo91c%t$-KXW>e+l@4)`%*#uSXg3WridN6 z?hh0}e2I8f1CZKZf5a>G92@Yc!bImKK{dVQsFJI4ec?u*ZVGX6@=HOPmZ@SU{eCbj zYBJeT>O$%DQkajE3d6}v!MP=j)EX42AAZCrS)C2(YE=rVyoX^qoF(`eZ^NRP8>P=R zeO!fB_}laaHfLK<%?nDB2>$>`V84R9nsPThV}MC=w_T2Vi+*$S+;LUXkV1g^NjOp* zbDOGXeVrErTBHZATWM{Q$(*Mvl~S5}aVEe*ug>_&F2w5_Zy{cE;i8HlFUzU_03_hi z#OEFgJWK~Nq@FP24b{yXmR{ZCeXTQfUSPt0bm)yKD_d}wrnRk3DIf}v0If+h7=+wg zuFnw0zH$I@pjPdP+svlpe5|u+f0o{(>P=Q*LboZ{Zc&jPOGCPk!lWpL173s>MtHTy zc*7WdbRw%TlN~plVgud;gXavsc&x-E{q!q9HeCb-m9SXXhLq!Rkl-B4Fk zs;44rO5!Iohq#W_&JvJ7CcdRRwe$88sozEOjxO6rVp{CXaWkrOYeI1@IG2;eByvbk zna9!US*728T-)|sTf_&K!o2x-e$@-|w)7?}b)VH8VJWFtqMlaSQi4F)6c;qzJpO%%2w zPn9IOwE!VN-oL6(a4op&Lu*@#T}X9w)Isk93zr5qr+`fz3+9rH6(p;X#Qy-7e?RiA zw%LBUrWYJ-tD9(c-zw;}y)o8Y@df;>a1@ZCnAWg!>5Mya_um_1A!7k~9t*Ggb@?pZ znTVS-#jvs3it2ruQ`%LZKb0lY4ZYCUEI^+qrmBv+TkW)^b`3%09=){13E!BQr*$0{ zyX#x@e~`P|IQpZwtB0bBo$^Ns?!F$r$=P?Cq7^!~>mNk8^ zn6k*kCTw7Oz+C6P_O4q8a_d*j;#=V2q18r@CG@I7R2>8*3aKWb(wUrS%y^D6>Hq@4 z<+pv!&}~bcO+J+{Z=Mz3yl%~@&EH~r(r+vxL=`PHOO2>hc#~Ar1DLHwD!2{jX_0%I z6M-!>D?#41~Q-lac3o0HJ8fT9HyyHQt^D-KAA0^~G5J+$(K(?n$ZtZ z;nEI|Z(JnmC+!ql=oFyL_uv9Cpb4>em2s78xvOtBnXW?A+mEDzmfZjn549lFj7eHS z(&5$3R&w{oNL}R~38e~t)^1EK8@>CKfR!SA3aqxJIYL5^K{+0o;!1cXr^7(2`23x_ z$kf-GSyX#-Z#e65wM9pbwib}2YZ-z9tAvE^I#oG+ltKMe2_c>7Y)kT3y<8Zz$Ypd) zb*hGhRWwc<98=ypo2ri+x)|TlA)!vR&1Lq3jQ#Rd0d3xZgW4;G4Tgq_z}uklQNbrT zscRv|=#+!v2Rh@!L_qgA=(+vte{muUdnU}(l9!({B)-B|Lqut9Jv%%{9T(ZF0`|G? z;J$ADc=t;onkX23?rOPgA50vr%@zkYR>pw}O{p%eg*hr{jl=Z{Rb;ShgrBp=;8R9p1<+1r!wm2Fa z>EN#2rrY`Lv1#XS!bpo6JW|w_HtzSB>So)VlP=Iec@DDUWdf(+Q2-HMb}!bqT9Kg7*E=;N$FGq*_^eic2Uuf>-RWbi~EJ@RBys&8n_lcFq}J zR=Bk?uRI^fa3wMPpS{)%5di6NZ#Jc$^T0S(JNz`qP+FN>V|P8PebOA8R?cYW8xp3gdT~^j8@E+OaT_WN zNHnM@)D9lcw(-+8vA%?;`#tZSwR!X5Bq@7kdWhOPourADJB_AOVy`tKGJ;ZjL0BJF zE4TYe(F(EPg{5G-k8d-7iy3M3g)Uon?$f)xmTmWl>vdEilCYJ~`wcN#xWimW#AvQI z8>Y@@QG_SbQ5HZt5ZkF*YL^CoQ5cFP>^iGxhAA1P6diuzUzM3VaNDjoOQY*5b#`@3 zTH09niW1t5Gw3mG$LO@(lXjnLRUt&FBsk2yjxv%`?IbK3Y}|gDo=VOqO=Oa^LLo_UoiWsiRZ{)s`a90Sf z+i?e6d{ivQF-x-ER@*tnr<~eYEB)hR|1v zn=NyqT;a#KKvt;IK`0-jVx4&t<9|z*kz*?-h&_g&q2pbx7YTALt;|5VLONfDO&m{e zL)b8t7pN{P=UtYbIQ1>I6BZhRg{PCCp@rrKH>VxB#yf9}Ig5+#k6yT!mf$kuXppqJ^_ubJ2OWmvf`v&|Wg3M+ zN`WUVM|Sgm&wFz%o!fe5jOqShuASfQU8XM5-y@HInQc2odGxxK_8(8TPz5Lep?Z`1 zry8uyp{l`2s7`IOy0=csy}{i3GjdMadxipnqF!zCU5gW24$<1 zB8RM(wMe%}Or{)paKS^0O3)5tNzSRpNiSz5i>a2^4`fdL%IT`;tbgV?j**kzSw$to z2tHr{9Sug5H1_e;KYe2ED=(|gEV25?+p4x~{lm7O{#WIGF!PeR-dU7>LThdfCDHeQ z5KufSs6GNP!?p3xw{kATNF4S!bv69GAzFLqePz3G4BT*Z$0#9>kDnR?r?=E=rvuCO zEgy)o`XWP0-;6xPq%TU;*cs3hnH|F$42J*laYq9`Hfo0lGmT`Nr14h`o3bqTwnVHIgg+b&-y>>bH(-CJTi_6xnG z=H*+ian~Kyw6vWDst8{1a7O5^hLSWMZWeIDIH`h)aA`HK5Jq&z5qBrFa^X+STRimFhXNPU zzQ_JyZMQpqLi!*i4dclAxvozBWPB^JHm|75wVR653u#zn#f>ISkqe|oCA+WA@w~t?t{~ zic>{}FtFG{df{sf%eUyMy^crA_iBxkn_s z9n_p;9oGK<)2*Adv4>qh)T`daPx7bdmgoUZpz*1~q_5A0&fT}yD((RUel4}FOI}o}nsw%}SM8Lg)*hm-8`d71%KAS~wQ*lD zQ(m>GrZkR~QY9215krRPP)EbzA4$Lz;fAT3yV9c$IZtAZevyjyTn><}8+cX3BufDQ z0HW9$szR4h-QvW?I#1!K?9?Pmm9X(alx`e1=gNl$8)f;o2U}t!Wc+SA>cePCd&yA% zVRp+!9eC!&8FP++RUO{EcYWH|71;OA(3!U`2|2~MLv|~R_Mp0y>K@dRFxpsRc_4)u zirn$c-nd>IJqD^Hx$*V2#d(odxWt2}I*ngWBAM|hL2v}%noaCrLPG*6quS1E|0U|b(r6!w!rl0=V)jS+~WFDpY3Xzw%I& zpdXY5vV``XJ-G83M9yhCLK=0ElsNJtPHpzdqc2xZw39${>0B@5rl#Xcl#hL7=ywIj zSBvFR+hHK&06^{fa91>fl(uklYKXy)KJ7jwz6?hu%$DO!LW0T^@PHIw= zKmc_o4}k+xjdL(#bzfC}j(slizvbcS>iz1Y)HSh-$15%mnHt^_hooDckdPwTt)XX1 zrM|Kf>#5GQ0O(F3ZtkbKltUjA_AY2u_xxBfuyDQL4|0MB=58n6l%g+(wJAcAg^M*BHoP z6pRf$HR4aC{{V+1&uA#Lie#05boXV62son4#S75%uYLxaq=8-tPG*^&xbSTdtONyh z6!zB!MqyYj=i%vt5)rzpgHi~fRskNUHC0LhBC0t6J@_eYbVicltZhn`HqOI~Z;sC7 z-iPGk3a;O3R!%+KRFgsnb{)fG(mI@|89l`7hnS$(#G$VHj_JGPH)*v464=!ehQTRH zQaeos6%HlZB<<{?wQp1Pa&k6p8*0?#HI%T447l5?{r486lvIzW5)J8VXrb-hJVkb42ae2$$}Vrz?xjJ+d9sIzkJx!aCX*0N(lZ2tguf|7Ir-h0j)I#(V_ z{ffg!Y|-MItfkilT(e1#5rBFUYiz!NwQiED7ic}wd4bxN% zbr#Mhr5gV8NYau!hr0;%r32A*H>aj`?7~$>ENzH|w4@a(iAc>#Gyr?a$ADf1codw#1s}#JN7;e- zbO5gu6sx9KDm7L^L4CSO>3}E+LZzjoC|6J;G1zeDqOH^~2}s~*n->j}wr{F&R~sWs zbkdKt_bw-{b#9o1yTDq_p=z75+;>oXE;Lcq*MeX^7Ui1qxQ-ms#DwcdyHY4|PrdEz zZvI)JR+}#2ylEq9Jqk0raW3|?>R5oAlL@ciMsY5Cb52V8F<83Tw~=c~t7WF~v6uI# zeJVYb3RG1V<;7f8gYc~-2EOG}6|0*GV>B(Iu|amninN33Noz_nqO_?$3aQZHg9F>Z z(LH05?XP|B3jsj3ZaHz#nuIgvmACHD?xFT#cHeSd#qhkQ7dQ(mkB&Bo zKX`kvr4f48{#dEY=)!(bLDsRP5=qXuFpMvC6epCLbkDm64MEnoN;B^`A&n5d7;UG~lg?831dAkvfpcNvDfE0s=zbl_HoFP~>g2kmF#U(ViVJ zYLF#35}h^9wZy6T6jYxQHWH-{><|!2Oo}1GNFbF~odqy-b3`X+O!4L7w{0WiTB=+}Up%fM1iVtoC)h6Rru7g@r>B5DmwOW)Y9=Ut4UXq5Z)S%Bk{0Jza z42svpC`Ut&GgCxR@Hi-wesOa*pE~Qen@cwmN}skQQ7@jQNbXm&w-pV;w1k7qi;BD8 zH(R2kCDpz@2|tfHo>tyvjf=>LcH_2KSCs~Sf~oeR4Jd-F*3dm$Jv%$G3y0jY+PFnF zp-X4>oOgrIf`R`4QU3rHU}xJzcB9XMHLJCj;GK5MymPIWl0`)b=zG$07}0H~jsS%~ zdcRhOG}J7`-Eq62;*JP|Rm}I{(+yE1C`5%M`#UhdmJ)SFg->U@3M72Mhe)Sy&~T`Y zH!mo_YM;G@60LO+kl08B9-Wx;nL$JbT@jYkO#4q}Jl4@Sbcow&I%`a;gCX-$f)F(O z0Luh?VH&CR$H?;U4ZNl3Eg@tc?6BG8N8PfdD93RU?ea>ildmd=t#id*`IUWD`xxp# z2cdLVbHMyN(#NNSX(&|p?lKfR!CJp=96|7^tPZ5^DkGBD_hvv&S~3`Z5Ix%Z)|+a` zZ+qWj*piPk@r&J_8;R^vWZal!kEvK~dj6PPeFyAGjb>;3%^z4_TR)wZA^L6f^S0O_ zDRXqeW696pjb)HUJ;V%Adpj3aMq}tdYGwJMyDhV~VDT096&3I6ShRXc;Y%`ylnuHf zoUT<;o7OR>Y!81qDezq5-W!o`!g{pcRR67hxmRU+u zq>xoYN{(j|TXN0tT826OWOGrQu6ECKWMeuSc__|nS_Zn(okwmgbX0ki?Xvlchj!Ny zU-k~?^!07u8|vfpsQg$?LeUAeX4Lfx&c4>pBK4K}o`0mDL7RaHP|mt1Ch3m1I@rggytIn{5eZC&Kd zSX@hN+ggCeP>)WZ;-YIE;_tUFFmF44YVG$+YpuTqY7G|K&8RIaPqYz>USk;*)Gm$U zpoKx(JDTSgc*)^3b4^!p?7pxaJT0>~P2eVPC|wy_5*UooWxZ{vpgQdd15g1(1MbHj zKejzNN5!v{7u9=z%#=;r(N5M6nsB8;p&*Kt+Kg)&O@AqgU>LQ)M9 z2X;4H6@{IgLMU5GE;-)$oUTgg*B1}!ucb_0jW-ngCn~W8F0CwHE%Ta~z#kft!E|Ns zPAPrX#kNaEzMKeJ&*iIf%Waf&K;Wh;a0O8BaCf8=Y7=hoyqdse`H6@`_bx2`qo91B?8bzGsF-|v;= zH$@uG+|nKd+-ExPx}T#gNkd}Ra8w)I^W@dVHy^)7S_jLA^=IQz6wkVtlaAc@C=0BG zd7{>KuQ!XMEZbOiRK`-b8$vT$QiKwvW<>x41iCGZ0#M@{B<>(`3ATrv4hQvp<#h#u zYp9ymxR&yPWe)b#A#J z=<2#QDWvvcq}!^FqhlpZSoW>bu)0~=(mziWMLSPoHv4VeIUv4<)uu|J+a)_d2?tKP z1Je#<%w9RZ;}SM|FA~SnX=(?8kanKZ^wo8_N*4}By;&f|)E=2W&~+&xB~@uL)@l?* zDn?z{sbAzXw9$aACvI)+p4T3!NT=!GppC-L&W(DwvuL}>lVgtaskO;ea@)_a72#T& zSHei_IJa2IbYcaZmR)A8oBYDVnxOU=IXkD+L|mdlT67q1mI?mAmdh$ z5W{tZ9}`QHiZ=D)$StTID|+;`0vTMJiqHW$RECpZqPT8sGA=(I6o>Z~4lzLuQx|sc zJ5}l=XcjIvw(Ox5b$a0u>5^o=oct?PrKkbhki^a-7bGqwN0(QUsOPh`w>m=@VEcZg zz+10%D_AG9r|87pD5{ll;Ixwl>mToA+NLnkpi}P0eWw2a&T$}Ntap$--7Rn@)}$uI zT}IBgu(iG<6h%m?N?cB$uBq(9keG)W)GH~+)~&BIi1`ZZJ9kaA-Yp(NZT2YfmSac? zApPsQboIyz<<}HEtdb0q8petqYiiq8t&?xdE8anA&?q@#-14fclg(GK0%`Qu7r@f8 zHjs}&YT9093%t4+QKP;CU!YeAO2bVOkrtOwr?Y!4*R7)N#7Css)W0lG6w+1?L5P3E z*dNk(t3x*XjD38P4BImdC?K@7p1M>)sKc>#)P$JMo=LwZ2bQ9inkJOeDh+FeMbB3x z&Gw)osb%znR>zc)+DO8&%7u{tfUaOIVAHn+DZ*E_*l%~29u`|%i=kizNJ|YV-2mhY zw2ZLM>fw z2M?4Z-Z`z23Du!5in@n0WqpKJhL!hW%O2fA15hedrI$YNse1rzN=2E8Bw z$H7(Xw>``^rtP%z2_*W-sZzwdmipiBOWet@&%4Nu+RV$mI1Bk?0!o%ijMzY>bMC|} zEw{61j5>;D^7i{8<8b!z=8-nnEbrboV&fNPzXk)hcUoP>T&rv;&#f&A2GY4`Swc;7 z3G0Jh?R$I2hsaKxC}^5v_r2YOZjJ_xc+^!zw`;V!?at|Rn8Pf~xIVX^X@=6%A#NQ? z2tdgqy@M9WA`nIz(@++BeRS!hFL0xZT;u!fO_9I$KJ>m>mw2{qTckUbt1SY4Z7`op z;jmUInMnF^7r5*sy|I!8I*xdtJ_=uE=9!4f#djlmgl4qy@b(>3t9x=<`74t*j{MvV zOt@QjHO={vtumyN{CIA<5hx#tr{6*pK^;Xs*jEP3jBRkaaCW*7 zDWY$=qQbnj)!<8KJ~M&4G2W<5QolGA#FuDKy=DRRCOo_(V%Q~N`l&tI3xdb^Y2}v zM|H0$36Q~-G*EDq)^@foT4oJtyo_dnK+JSbD_=TYk{Kx@|HVYRhNx2vZ^~ ze=eO0e3a84GO}zY>Gkm|^mDfy#^d9s4w3dCX0z#*3Z% zo6`R1C=1f%iq9ry=i7Uo=OaVhZLr!}X=S4EO3#NMTYEjHMI#kSenOLq7vQi>3P(xq9SdB-ed zGDcYoj}pG0ZTu50ZQC1kYeIFnsBjun(jG*1hgTM=53J%a2RzUPXqMtQn^zhp-OF`a zEmqg>GMz3!WaRP%bsFi*3tHn}E6Ya}7m(Z#TSO#x1QR#tyPn#kVU$znrizeKL29{I zvp(#2TL>hh`6D+b+t@120YdGav1Qtk-HT~pv}P>3HT5lPThAV=QCT_R3#kmQs&`|K zj@iEIb(fNg0CQJShDC)xWy^VLn^S)re&Uu{S|i$$kG@#1oV6a~*f9PkunO~0HwNME zr|zb#zrgk#isvS7?UA=_k{xnH(pHruy0JxR1v;vr0Lv2=c5~gx16xw5x$E0JO{=7p z{UJoVf15UgT2jT8_w24bQx|a|39E`y^pfBrxs%_EefHFuk^9b}9F|G9d#h_aw2nHo zod{Ef&Qp@(M|lnTaTDSwQi2>-RUQ>n6zJ^S9l>jN8HnD%?xCk7%8P#3+k)&(+8kD; zTIho+NkT}^Dyekt#N4(QaYgQPRcN;!ChBlypd*?u$sB^MmesPirFw6DXPX!y#~ zG6tRX6s8{S{jP4$y`c%j3m;PmEh9x0@!vdLH$LaLZTqAckl{{nHf5n|E+t9_ zO)6+}%M+W2XGd!fTX?Mdw>|EkD|#Z4v}xw0?c3?ohIumbjlsL@D!t!XZ7he^E<vUE1I zsG#cc02fMDnbQkFkDf@w*}*ZNaalt%;FIDCGus^1#qSNvWNP>1$g{2IorbnuY^f<$ zg%PM{LNooCmAk-PvCVM(B>khtLbLoD^GXJyL1iiuLS1T$CDx zbjMwpA`Wn&M;6I+6g0g$e%v%tAe7&kIcz$lKJY^frmIoFhK@<3wM_MV8fty<`d|)Qjb^MZ{T*0=A@Nr4&=deUzcZ#1E7C(QWYT@lzdx zmW_x}yc2hG-qW}3=3FuJQXVv6pWUbxsl<*t%uM<@L*}wh zw2P^_sBx}Z+)qnHX?AucMp4qFgQZ8`s-qTH?S?VEOggOAE%hYRbVw=`mt?xwB1mDj z+!m=5>ai8Y6roBzLTW%c;=groGqgIMp3&ZfeZjYE@=@!@#31%vg5k5RDYDO>^X$e_j9CLMNK&iY(BLxHO`ZKi zid$>gy22kUnvO}W$onQm?nG_Nx9*Nv?~xm!eZPh68Kf(?j{HesGR>!zAgEPc>9;0x zy!c(x;G3IAon_h|7Cu2>KH3hC#@W(HBQFp}N9e?kS7_CqWIn}P+r83!FY<2q4r<9j zm1o=*^-Q$NV)1I6l9y7Fmk^|=P^C#V`au|?OC0b&P(cf9g6hvB54c0x?n|0}t9=lg zt+M01_+BV#;=o(iO^H#VPn1m$?$f3^2e!T6d@m4P{3k$H0{&#bDr}l3 zUgHHGH7y+dOP73Q>JncZ$GY*4A#HSTZY#Sy^EzJi-<-s4(g zF5hc*-#;BO_)fhdwFFkYKUH|B*VHcy@#h}P+uO5;xh^Ll@n0Y|F_c z!J9_EqG6-C_$iB+cw39M*4Ti(wY?nNn}yUfBI?r!^I$g}^i%{olnN~;;z?1IfVdPhHq+FT3ZRN+)vo4+?}UuWn`{s70eT;(0v%Wo`I^SGX7JbPFdV<_;hzx z#mz3XP?tiQc|a^FAdH7zxM>~RBy~6`S#9TIdW{N?S++hOS`jJl0idIc@Ave?gRQ|D zChoeZ;1un{T&=r3Ja05|9?O!3_X;7&gq6CGq=qGg`IJ3Ej*&t!2e>xnz5HHWy0*1b zyJL6VVs3osG;tLA3Rd3S!MV9&IlkRC&~Z?laZlfo=RD!ob*U~bM2dFd41OZk(u`^P z6oz*>caW@)1fFFUu=s~~FO$B@DgUhw--!Ga*tI%$Y zoL1gTUGAyY!RW`-k2~=!?snRnULF4cW=Cdnq`tY0F)_qj%8SLA>Q9eN5AO=oEK{$$ znTfEz`9~T&>BCJ{t%U5>dj9|y_=7+((!5Q5l>KXzd)6B$axsjA`6?!kBcbW`<3lg% z0IG&2Q_Xg3@|#mkZ(AZtNLz_<0baq3qq(F0)UEro4(qyb^e7vf?W@?Ior_l{>8?ii z+A{)3(wa+!5u!ekU#AuLBy8Jez71LyErvY!2gbiq3CI5cXkOGJ`l;g^nL$Xo?PZ}k zd|xjt6Q8qQDSfDMHm>lm?OIm+VEKl-#2?hS3~{YUs9^x4l$`#9gi_L=)SX>A_v6(@ zi-Krw%zCZ!TEw7R$s$rOB3QEK7zKny1Z->R(MqbyJ=cWfNSUJY7KOiJ)*J+oJ4#%KX82 z3zp%rxB0hiMX<_}kePP-%tmfjWvt|))P*5MuzL8?ns?z*dlU3{qT~FhW3%{qOx6+? zwzZ(p`#)lLZZAe2lXr#b9V@Ppf3D2WzP7ER-Ri{Li zk9M;d=k#90O*};pwNgG${I_}gXCh6*WGV+O+FI10Z4QXnBsQGtxKospO*Ma-Z?3gYo8J5a#K#-5)y?| z+A_llt(aEHj>?C$J~sz9&};T6@D`|&;NcRit6V~6G_YWf_p;=eEt{dH5iB76o%N}EH znxmElC+6Q(ejpFqfwGyQ$TBcEwLMaxPrnu=Sdkj;$C4>gttBZ3JhT4*aKNG^%RQ{z zro(`V4aZcs5|Jh4d2F|+>D0AnM2^#g25O>i?gIr>&^UtK7Gn+EH{GUmF;zHWJg3y3 zENUqON^3*ii3Y<^tM19U+67&&kBJv4N?_^=abOyJKq#ad9`TPhB{^ z-MiX`(gQr&$o~MbVLOW}<<>TFJWuNVtLDp@ZH`+xSCAvPk`$|?r${}YFu$1p0Edhx zbVJdRc=WFA*jJ`}JTCtLAo{dK;vG>KQcqyji5owKJG%Eu9}Xl9%iL~em+NX~||!MXwx>LAvFttOdw;C#B24V6f{es0*?mbkZHmHcmXzE)dh)KV-o zgG8RbH6FgqJc+H3P%2d~DQ$OkH${45A6iVAKIyxzN|SWBgtgV#3m_r1cO^fd3>T=q z+NaRi$7>ZMZmCc4sPUoMmkBK)i1Fkc~b5$?iUG5Qq~GX zDp4dUN;&EJuwO0OlBF%oaXx@iAKLfUHq=Y2t8B}j`jPTc=|m?Y30dy;VXJ5oS!`d; zy{5DY2IqF4dXTAC2?gblN)2dwo|MAfAr-PzQEee^El3WpSU?GEb^tJmZ4d)Ow{@10 z9k*2Or`Dnu91N0?(CWg@iKsy&q68%YBTz1$qq`B#;+)e&D%x!^Qt5E{>RC>VydYCQ zw*+3UsDpnP`fw#UDdo2$>^SzV@Yd_I-7Y(hbOsT}GoZ_2JUBNL%SxYi;M1s5pJhyH zN)Xve3!ya~5OYXAghJucYsD(;zB##X7+c!tx0hvnr!<`!VTKe_sU$Yk0IzX6w@z+I*h?B$V-S-JCu?= zm|Re&Wys~)<9ENvYDMN!CBDK+5~N3s7ywqM4O11il1VB#(!HH9S1=?SB@$_ct2$(y z6XnVuI3w({4Gbx#Y{q2buMGbHnQ^qT-6>Bk0*W=xr28kC_zs$sE|W!ut&@vyuy;xVrZA8!=VN;H~-?-;LnYS><~ z6IFvxrw8Z@-Dq70_;4OrMeAA6di21Ef*!Og5)+W^>TsnH7p=lcu2rXIJPJ&dAWc9A zGl5BYiyk+$JP;I%S;37YPmIix4* z&pb+7K}d>>y?6b?LM7yyq<7*3$JDHG%7xNJ4@zJtl#WUxI)`=(L_T86WF#o43Q!dTw-JXe3ob-8_ll8r26tK+ z{{VF)x{?xeuYN78VXCD%O%YF{4tfet%`0kQ!D&Ebs>%XtYul0b;S(J~JaQ0??EB1J zqr2D_5vy(Pq^b_#MKVGh$qswast|DzZ7H_90Q@dbvbMi7Y1BR@>Jp~ou{mM8N{0Sn zwVx%<#}IQ$hp>ZOSuR=9??SEGT=|TuMoJQTqL?IUa#r9GkQh)DH3(pl*l=(vk|i3B zg(>z$D^(BEhjIKtB;Y8V$kYB#c$86caYKTU;U88!lR_mm?e~m%d2SJ(f6(O5W!jiJ#~JpD$Xccu#;0x(^Fg(sTmgIw+BhAW6Q`*D$`nJgLNS4 zLN(CW36uqH0n4Dz@-SF|9I0%`Q)$W}umv{ZJA{>%zOMTZrx_$|*RiKDfT?DZ&S!Mr!83tnYy>*c*qSmdoH>$YT9%XKHC%q&zTRQOh$k8U%| zOWUkuw0FX^JDhc%X5Y+=``n283bB@J&4YErUE6BQGU5|iP?iE@#!>y(QToP58ipq| z4by7Vs?2hDi;G9}%085)jVthzpKd1rpit%OV$zf0(rMUeaB9^ykr_f%pg~Cr?8xE5 z2?#?aBoBm-Wtt&3igTy$WtJn{HAa_G6nA$BHw5Y)BCtsB&XvVq zAy%5JrJ?DGboZGerrYV~(%r2IDN=2cB`%-1S^zAwP6U zQb%a=;Fh|7;W1e3c^+)mzOiO^MC~@f4t0wa;rnv@_+sN(N#qI;zXskavY-|3<<%_=C+}8+KJFSu}wmRj(O3=}15S!6Z zpfdo_)301qHeOac>mfS<0F%XDd!@H##bwRBgcmdfz=<6p?MvteM?6{O8Y)${6+C}B zTAAa^F5iTwza5V;_bv-4qe+USB`qZjtx2dg6vaWfQg0n2faoJnTGplAkvAI1T7B`w zGLyKj4QmRxKJryvEveTOkU9ziaW(jKEj0mCB-|G|w7Afz=hAfRysmrNEyr4trL{d; ztJs)MDB^?ND5GvFr^GGW^3jl$+lqkx;Y`j=+*ixp>hRUp==|1g^5mv74!E@;qL);I zq$?rPn3LJo5W{HF1qlIGyQQ1Cyva(}8qvgkX(wfHzbo>aQY36mog(`dF-jhs7RwAJ zO3V*3O4~_2^d_CSah=~d{9yuDYIy}%+g+>6=7&@b6!9jBE0}l_koZbHl2;+M0s{9Y zq}$>~xHB=-EeO`2;#ujJXsGt!+fRI2W*+9i9q#ywCSAtueeT<94a?X|_;i~4bP54m z=>Va&XgeG@(4$!!om8{7WX_K=e6}u0wk{M&E(fX-rw5c$TTiuGsfgTzML?x>Q@egw z4zdj>6G01DY-Oc1>D1y$(Wp}GP?7GhKwE}1$6~4E>2u3*ZR$lcO0*GBdyW~)X&e3~ zgX>aO89R%5`ED(*fuhZ|%jMqU&9`sBnR>ZC6t*BjhbCj{0ECrjjE5CKNX&|2KK}mJ z)wPthqtigAu(-^oLPE=M=aI&MpRps{_YL+0xlm;J&?d%Gkuoc&EjrtYI!acBfKs4E z2qL(Uw+xkm?bH<7>C?pW$LQc_Q({f(;~`p?DMaC5z!D6R$u;ToRZw0k@9BaFz+N0t z6+JSfm1-XR9)&m#^q|!yS?HpZ+Hm9wh2YkpjOifKroE#C=_tY=QK(>gAmdvLMUklps5D?&77;cY;Ntn&Bzx!)rI1pGS>USO%Q?981^@SZ8EWLic>;;;Ns#E(EtTF0J9 zR;5~j1W%{1wy)KXbOqxZycEX|l(wW@v2WXrDfUD%w;x$-kmJiWBxZ8OVq7cTF?i;$ zmYa>D*|8D9AWYtyN@_rnF=1n&4LB+KaNuKP2`RGCIA{uN{x@V&+$f)y?h=KZz0|{& z+PEmEUILJ%$b|Bhrr-vyiqfImg@k3P21p!}f>@|Sa{=x-=k?%1QmE8vS11d2@cX2Q zt8fo7QQvivq1;PK6(0N)#>ad@saUTjjQfrw)QrNqZaZ3>cHMZJZ*}C9y!$d;ZA(ct zAw>!m73?@o)^IzE0&?8d+fI)mYNuvODt27AWjua2opEj``^t$pS|}SBqeh90*OEx? zr;<;l6kLeG)W&sb^HxAgHBgdz3=V^dNc+w@1tHi|xi?d`9hiy9ivgvr7G|DL+%QU1 z;d#@o+7UA;IxU-zd#h`F9Ca$owo7nPXzw}xbTM|TPCK}BE!EQXD z>Y}(cA<)~6g#br*#ATlI$H>s&@v64p_G32-%Ur{vk~kyNHzxCD=DY13vY3{Om8uid zr8{?V*i8nbxR#1R^46_oP5=cxy_m1={^(fZLreYcqop-g&f0!nb3230^8LXWsV>nZ zN`H4)7@4;d$@g1KJ-+0ajasdj2bzSn6_DsDNeNmMs{8R}v9`9mr0!@Xz#Ue1m=_r0 z>tg=^gpsGAH5a;J*>>DWaBOW{x3rs*6n7Ms!rF~)Wdevo3aD1KS5YITBwdVuoCtTh z;Hr{mBWyh(zW)G2G*Lk`|?TsQ@J^QOm^fF7H`g|9wAprLy{2te#Rs>3@|Le6 z*Jp;D*6f#+L0VL$vEo;-XNo5C-uAets83X`;L)iiYQ3=So$leeufc51Uh|*>^`A$! zireo`P&;FXtoL3c^rWT2g*iS?Bl9b%O0L(ZP@gQOBZ;q+z%?n-C@bL=C0L+s?%qw4 zm|tbhPZqO~@T%H3Z+dO;_NNoKeaQGePHy!d*TcYgRBgR~v~AnXvgdRPoVYR*Ajf$= zdR|%;=26h$I+CJ1Rm|gEyRq|OF*Dt16wz~bFD(_F*tnP9cs18uQpY574gUa@_fmH~ z>I^tP>Kt2W&@KL}g04ZyA)dV3TnUYDsXC-77uXB!oDIKo`#zP|%aSFJ&Q`)|zwsdt` zwr16Xa#}?DZN3u|k(vAEqa_Q^-ZArwD)9Tqmhh=xpnaZ1|QradxHHHGN$?w6y4Ow%Hva(JJg?5T=oU#Kb0MKccB?aO`Ao91G!>{3T7Y_< zn0<&C+$2Uu{6uk8{n-xS-PfWQYTEGdR`-X!JXyH*16M29McZ^NB};adW-+J2+D&Rl z@na?49l)OXPJvTrrp`A411>9`X}Mp`(W+ z$yVUxo>j0e*K65Hw^=PJ1LqLv^50uxlm%o^1KEoNP+3_!jb7zclOvbR!Uso78k`4~ zn%b6#TRzKZLifQ4jHvXZ4LW=!Ybma*`#W(L9@+EcGDv8}IWKYzylu6%ZDrx~qgKuD z%lC`n+aHB8)ab1RPnHp(4W(lxRP^=^{3$Lc9|V0s)gK+XbC;K2F7eYvHf>$B{gNt| zI_mx2?I7xxHPw=ke}xGo(Icm~6uh0|W_=nc0@$#4r&~wwJE_NtExT^}x+6?Q+Ex_Z zr^zZW&3WXt9y>`DDqU)#PhbNNFt!j#e~4(JH->up+AsTd*Gkn;?n&O;vyq}rw!LdI zV_IEPF1Z~maS3%NN{Q3$8Dna~V{I%b>hWANzqeLnal~V54FRT!`N11UciOD?L41h0 zHXBoFm0-6$9W9TY2h2$YVyYFP704Vz?p&M`N&_{i)mgh$x3W0<7ktK)9_>ckiK3R=mz0Ywel(M4U@M{Oy(xx4_l{53;f zcUKil<4fk*yl&QRo3`;K$xL3Nu(hEoO46o=s!$3hhd$glw!>fi*pRc`9FvUh^TC`! zw-Q!U`@spvdrM|-YcpuSUng8+w9@|o=>qS|MU1rgQiLhRD3xQ`d+W=2sS^cK+hYTa z01-g$zscAKUCE~+N_^OC*1jj(7d`I>jalSX+28ql6wvEwmbMdLGD-77fC)KxK_-NZ zMqY_B7cGVI78A$8O=6oC-m!h$5s3phc&LkR`t0L`emtG~vggCONOeL8g0wu!8>XEk ztxc#9dug65*L|D5JthZ&-%8Ff-<%9gY>-{XL&?&K)nN7W#1@^acV-USoZacAt#ECQ zt+1E!6eZ$Ur`=3P-S%52B;uq7skZ zgQ-??YHt21-te2BWWHIhrgBsBK3bxAq?G(C87t^GpycqGTX&YZuAp0919@$njA8M} z&3#G6t-rAC#W!~&+aoxEQ9VK`N>lzhr3M(ZUp#ct#aTz-Fn<@RfPD!TM=kLEsfR?a zMX_6u##$9|h=7s@N(EuedtW5;q;4uQUg_=2Vf^L*5@z1!&MDqELuA^U&K2_JMSfN) z%as7ont@Cwl-hUJ(e=o}0)MugP@T6(9&8ea%Ds|b&5#U8(wpw6O1X6~bI^bgr>#1Iu&MfI1ly7=-dnGIK z?WmONMzQ3eBxUWUc%8I$tG**eRLM5= z`;G?JhYUR#Yi;#uN>BiyrYUgPI^v`)M)xesAOYr|7Wr&Xh}6icDq-LXNyz&_#9WaU z5)UP1w!3Vs4jc*=Q*26YZ8)K&RL=>eD&A;{ZSg#{G+0ZF{_+$xew;gs;I!4KkG5ou zAdM6?y?BL-(NxX9a5gKCP-(5DE8H^`#lsc5e7|+#p=6tsZF`h-+!!}~zb!#6Oh#&Mr>VqQ$H6RG zXyB$~&0Sku3W~6)3Pt`4EV79*K3$0-seJr2J@~D~X!g6^;uWfVlyWkT;8bM3F4<8g zs_2w{Cu_VoUVYy^YiyEgpqd=3_F~8Q{m)NG(1l8N#Iba}XI7QH&u&K8S6CMwHr-3H zy&|es+LRX|i3z21Dn8!a@ahZSn`cbz`c!sH5hf-6Ws~R!B{A{GD%$s|)o7gLczdQH zP@(RCl7FH=!~#vXm+EKme&^W=1=&viS&36_JPaxP4xQ@kjGjwr*{=RPwq$hB%68 zq4fQp#Y`l_kv81pKx;AlNkvI208vC^Vu%L4b2O0`N2etmyAX#}MEJTW5uH1*xMd2D z6Hrk~oFB7xUi9WZGTPjPx0=}-j_kxe%;OKLGl-7(Q(-~WW`LEHl1F86S>KoUc9^*) zwiT74p=h0?vB%$T2rci8AbF#Y&}sG2N}jPjA>X|*?&V0dI_Bu&EyCiHvv-nImx>fd zqZV450V#Dk=~h8tWtbxsKGpXgJk(xXqoUw(94qMyN$*Ez89dZ83J{;C4OU=+DSFxh<#!ETFum{3*y10i`=}pE65ZoPm%N z)HL)hX6nlR&lDmgd3=0ItZZ)?ONO>2ZYxxD5E7oHySDmz(}A3Q!eZPvujFvZ=?d6sgsDk9Quvty{f)NG8 z*jw9VUvFDWci5e^Uo0-FJ<2Ymyo5TM5(3#ONg4GA7V8YOH*kqqebTBIn8~c}Oj7(! zDao~YGnKgSW4W^9?Uuik?=MMpQe{M0^Gue~+Vrhp0w^-4ZXzx*me&^|Kx*OOtr2c1 zptLZZ8hk>IqatEf9F4Z)qe%v!fnTiRd_ZZ%SKEHl7jAtleq)x-B=pxCg>M`$aJs*@ zVa|@t`V6L38bg!T>!iAsr&2R^hp$Yr&0EK`4!O&Vrpg6PXn9d~e#895{I1;mJizBs zwCWV1;BM~PGGjjaUWBNvq1P6a5t2iSszojIg?IOw4P6@e81QL-~*MF2zhlZ_8XxMzym*?3WezeZ&g+1mPc_w z_u@hvn}@}!!Z#mcF!xR-0%LLT)PB{`8yBoUNWP`4v3AccTW!s~E`gf{>wwY%Tvvn` z^TkjNdTDSUW+Vfxr4%|WmhWKhybJu>M;Ekx)63MDJMZ$p;BG-_%hwI^M9wpnC>2Au z%pZ@~+$TCAz~gcfp2~+52X-4zgMS##ie~}&hPkzHiy;xyi2{fAdqSWbtNC*JsN&XS z+*;X9@>btrDqrK9RzbUzp(3?%AwyD|1XHsVCks0ds2`j*C{uW+Z3@uBi3YDB=CT zd9=Q%R#|>}5txkO0Q-T&1eS6{YQ=T5%tb3`@Qg#>A}fK5>?a|+S_eXWKHNOE&zR3k z(pMNJ$SWDDk!{y&bo8ajl2Y(JC~Hhu?ebB-g04@El0GU=+m^Sl7WvluYe`#?lq5eR zsR^cnI}g!`Mv8@4ypzj%ckOE;?biq|TrGxB)&BrCbqYcpNHwAM97r|3>aAbj2WX~i zt9xGJ%$ZQOgBx)WqdKRYVb?sRJEYY3`eW50N5u2gk$o3>THM~Qy+UlhA+jZLpxj+l zt>1V+)$&$&gepUW`f8|cAJR}g0mBTgMV7YZ?ot^$?=C);{6~WN7W~BhMs1!Y*>>MT zKAqia)n%~4jR%@<@MsduRvANWSfi=3GbsSdAoL*}RH{dI<7&&0xcTdi7(NmD);qfM zQQODmH&+p_uf=g-w!;YwMqx@-Ddqu0r9W;uK$f&oWI^t6>Tz7x^(Agazm#sPX+L#v zVl(=xO;lP9tFN^<<=*1kd3geP72bW5zBAz&UM;9mt*L2LCa9dirGA`6V$E2uIVRD` zTvMk~KVBq_c_^UeuoM&+^)*N9G+KnDR8u3@1&k${`mKUTV8Z@UL^B1xgONE7(Stdr zsW&@a5Xnh32~9xhgEf>X0#WE`j0@I9UNN}M|A3AdP5%Pnb02-QmXj(MCN%%b<1 zqSc_35TZ3HKMn!Gd8KR>sYI(f_TUj}R?V`xE+#t(E;z5gk(nuGHO*!`@NU}8D1-Nf=@~$_F*Kl%Z=+v z3GA*|RxrGtx83o4z1gE}h{YBSq`{2q+g24?i6U%fe32>SjNEkQYO-ifRKcV=CQ#e8 zjwhK-78_PnPLO|ltuf&kOA?Ox3rzxr6nbH5CO%>~mI{-j%c{>{?H@)K@?3?}Q67nJ zktn>F?3IQRR{HYrr8(B83y8{ZBf#c_OfaGiXh5kL_m)zAbx{8RD74Vi;vO1T9`ed3 z&V(v$Zn~FzkPEc%>*!BsOgxUN`F9F}c=yu&XAe|4viq^=-&M1xWEW0uQT z(312xM?wcqhXp(mWUPRr&{w|#3JQcMK<@Y8Q9733cV~lil%JWR+d=NZytJASAX7fL zQF&|<6mtaAtuP=jX+$y`r4LTzt9e?MbhG5%Nk;%m`#Mayn=g)K|L?;(%2)n&V9hSv!XP{Hp%O`SOjWE_-)LmXw_- za&s*f8*x+sohdW|w8KHRZOzZjQm#)<>YQ4bEAEtluY~7Zy8Rfj7|T^^wMFn+FE3z6 z73G}LYA~mkib^YTh4awd=#_+>3O$FY#70Wb%xy;yrg8T@28V)>?Q@sCHbYKHqW1>P z35{_dzU*4jwddDQOiN=gK4RL3;%cg2+Z#)>Pl5WTdXif-Jv;Dd)Rj<)3O$sj7xKz( z>LHQ^M_}#4fOMiwDZ$P3DzyVKhwwL4-r&$B)T7yu!?^;CCaA&_+A^oJ1VDt-O3-Sr zQGqQejdB?psC16Mz3#v`Q3!A=bbN_wb6cRaak5MFCR%wL`NkD|ij|y|`I8Xq95`_sP zBbn{M(G&^vn1(`qnrYjGI%t5%j3>pI z6&UpiNEC~@UfQ?Ib6Zz@TO7FCJf(gMdX7SF7N-=f*2 zzCH>58|N)#^R1ohY4}_}Z>3f}$+7p(4BahzlG`P2*zTaOZtj)>j9IN$#M;twO8)?f zwPrgz@s?iW?s1Zbv*A1n*6|yX!!;A4G!i%BY2rO2(dqW9W5O4WyUr6K?MQcMn_rQo zf+5)_KA6#NZUOMceaa*_dmB*%_E#EC+T9S{(T+VYStk3yUE)}0V@!~aG~zvD^a{zy z^s~#+opLm*3v4N~MT0GqnD(TcX-wPKCr~t44Bhs^F!Z^^5$Gr-dST^NBxtj;r6-~U zrp6Q-dh5Y9{{VjQWc+8?SHJ4V%as2B=NnZWYb}BLP72+>b>M(^+X8=l`wA64n)!7A z55O8%btXqY@P-Ze1>c9(jsE)JKd60$8E?~XFH|L4WhDOZ_og%d080lLTaN@k?{*0A zljQL=NgHuVz_Pc`8VyQJrxFR*x7xLaB*#K{qA> zb5fR&pQTu>-nUA4t3_!a(7OTae#0AS**!3AIAs3-bJ*}-mYRi8=Zb6446@4}!V>0? zRc2Q7$VlL$8&>Cf$Hlh2y&l;fL-z*^l}vFpdOCq4?$5Tkly$f!sxo?=LYcoVi#wZ( z^8@s^Z&P8w=H*qy0Kc>e$k8za3RtS37&s!YsH^q>4E^{({(atLzi z%f5;B(hXNO`!zv5K-BD3bNUsC9?41n0Bbw_1t0SsYnZl-ec|;dSE)O6XHm7Go7dp! zW!B@zMF~xH1cFL)0Al6ar;{X(0=kuleqnlC$XH!Y3cugX>q@QrWjuf`uwhF1$50tc zf)<)BDGnUS2O2&WE~l4t(yN&;%2rgs3OgakGS^2UHi zhqKv>G?=JEM~ct?0J|>C#D402TX#mxz24f~9HA9=D-PVTy%9E+kQ!8(OVETVM0*lx z+;IteWpx`vARrP|!xxgVmCv5+6GqSlPbRk#OO8EmhNy8|Qj0NEqOz2eP&FR32O4#w zi&z7WtDEoO5==~E-6acVwoZk3wcR1Qs8elH8J7l1X{oo^a5~zCs)~gGdSN@qGFXco zDjX=DVq%us;j*yN%pi|g)F~HsU$2|nblta3<+!fzf4ix(aGtP`rIwmYNyz~sfFyJn zjmB8PV*rXV(!i#71?9$0aRE0j@D%R_)+^z519`rhtpR*HK`{*uN*BgU}p;WgHChEptE_N0? zaw3YrdN-2#y+dshlz1A8bzOQ9rV8<%_j#l6iXLtYZ~U!Df4E$HBX{ae9nE(`SKIlI z6d7(!lCvpZEX(Rprl4~eoWR--lx28ykFh}S3+Fc@FG%-`kJ^&takr>V+SF^N+}WG0 z$TM3e+cEh4{*>Qdr=#M#@{q@}5> zvgtw}KtH;$Dlu-x&otKUl1B8nijm-a7JZtrg4*&(;dfga;sNm$3ti!d!MDEyo!IkebP9+EC^xOz|0;+ICqm1;cv4;H*1GePr;CbFClHUMNig z`$17<(p1~s(&u`<%#(M#ULJBy(%|!&oqhLIRHdm$sU+fV(!rMT2;&rJ;1zcFAuRHl z9%|Kk&rh+9xe8@jxw6NIGF@J>kV* zx*?;yP(-ss_X^MYWd58h8YjKti7cH;(9)7SJ8)u_YSl_~&?S0%$p;>)5CSefX1OYr z_Z$TZU)@_)Q0>+BV0A%8Arj;H0@(im8iO81>1rJgxjPkJeVfpiL5@K9hm!q7I}w86*8-h#u2`0TJ>k zM#aiI_ipVgj>EKE*=K5ATw9kra^XUCZY{(F6$hSaPNdLfr9z#Uh`QVM@0?0OE*_OL zX}K74^_wDh>JE7V9)5_5pEys7s zZOd{lDDqfgCAQGvai*4Ba;M@n6s9?%(ipSbkIRgI3N!XEq!~dz#_iyiSAX2jHRZ?l zE@$vpr|whr7j=Ff(}LSps4p~)$Ghe_hAr0gxC()BN+7LTMM4_{_tLe;NN&yjX@i6? zioTjOaVzIW!tCx-b=;Xt!y>M(C-kaT^&!JNcfs?mn^wonaJf5srA`giZ-F1nV%px2 zrs}l1@YbSfq?-2Ozi)SLODokR(HP_9UT8bX+x#X5R>5mHno;pnlCMx=O5L}cPTt%0 zf112p%40x;g#x$U(nzMQBBq_E8dcT9ZK5(rebBLcTSwi@B8Yp(^(5O)*`ILS5jNxU z*s@ENAyM3VbA~$%Ed+%J6+%TQMF8u9Pn2kcJ)*TtV8T7~7@zKtock+jTd#bVwYFT# z^DSEMH>ft-h4rmoN?4IOxX!rQT{6<5QctrHdy^$B7^x>`#2QHUD=x&_w%41I-p*rK z-y>RmM61W^i;&^$O~ju{95ZKcVsU33B01a<;;5oc>frlHc&=N5if|+-6RAg1jZQ!e zWc`!crtUArZqJ!3$)$gEdKW}~X*a_xFNWX7v_WeC73t8tpC3mv<6m8AeVAF;v_B2m zKQFjtJdm5S?g0`cV2~B}X%$k5sjYFj-a{jwQSh!(wYav18h-grd&IjanDaKNuHBhr z=v~dkd1-k-W~3sdgm+gwIJbt4)mLuk4}ObSOVmE;DpBnT1mN-j5v|%zsz>Af>38F8 z+hg9=YjYP1-rRV}wzVw<*x}%kgsDgrtu<6)x45v;U)va5NDkt!`y+MSVK19?3qgB$ zRa^9W-SrQ<6n=YiF=SiEDAwp~G=z_PsadWvc6N^sYTX5WYpP-V%kF%cp?^U1Agmj^ zllc>HL}PMqTea~r9njn|`qBcHY5+r80K*)P2FlJ7=CA;!uv?EelDunQE1XGPDY5K{ z`!i-+L#t`IQt9()G+V(WS2A-azZgB%OkCU^od8|kVT3kzEf3Ylid03`+iq$-uE}Io z$8%hCw##Wsd`e$^1!z0)(8BhD_qu>7C?$!+MGGqOtOm{4(RRxweAFS^?8J|qc_|b@ zBBcoS54RcqXFU1OK=?x3Hnz@&$${h!vYxK3!{O?XGQ5U#3#8FYFg6FH3o2#KCo2gTJEgy|+WLs=o-}y061E`s79#RcSC&l6lI;&h(?a5`= zTbb0fyXc#7Vdb(EDXP zyH)ZN3pkEzLAUNy7R_z!!i|3}YF66ZgSR)gpZu#Wb5$CKN|e&jlmc?8R!8W?Gbx3A zo4xg`ZO!R#ly6i#67}ZoA8y(w-Y$-&HMSc|a%_W;N)jq?t{%=w<_u<}l&0~{Ti94W zS)2nv)pI4UQud9B4*vjR;+?XUMYzqAExX*u3(^$ql~k!Mgk+J?Ae@Fe77pI|w36l2 zpy0l4Wd8s$V$qMc#z$VK9ervqx$nMi-iZ&~cOA+s1yo21D~iFW>`DRw#k(De#l&dF zfI~jDixp$HN|??%I4{vLV;-c4CXf#b>uh~ z5Qg5|lVsexV%N4UE?jKzpt@c8q1 z%ylj!YRpm(!s%scNw0BG7;62)YQd|*R^MV}CVd+b)ku3+jd^Eb@4c70Vr<7F-YwIa zkzi&{fR|-9e8$nIRdc6IL1uIK+xs`d=liEZv`wF|v)D|$cWsW3W}!vAx4gG@{^V`r zxcAd3iMnS>BJlyX%V=TNk{a^DNm0locH+aeu=e(t_C*iC0;cy1ab9F|63=y{r0P5q z1}n0mlI&SdM{pe0hZd7jm+p~?X>6i!_kb$7Wt6ywSl&mdl_c7?Uf+|o=Gyl){_e&2 zuT`+1xa>7F21r?2{3M>*VxN79Xu4@*8`8C$9_M`6$2Msn);%RhvR>Pdygb=7HK|sW zAEy?fkLe3Jeo>M;-f=%lo_JG!?A|`POS`XkuGe-A>9Vb+99ETA#8hcY5$we;bnWSH z7#m83v-ZD!SmGoyb@acIwKm^XSBWzh7yYUvwILugA=Xxw!9Bz$FQ*yJ&u!vw7}rGN z{{S#o61tvw)lNJe$ounrZW&NtzKfl@5Dv(=CBzY@UX+8CbFNsRZ;iEYYY=d43fsFo zzwh#QrLU5$9+h!~iAlJjun)_SPNCj0jtCn~S6B;@D@6}4k$Dy!!sQ-D%Ukf{ytKr1 zpsh=2Du9unTv?;R+C6~vQBf{8hFTd25!JO%1aIBV5;$CqktfU~5|Rf)pHL1OI|T<- zI@H+RcY&33NR&=sVKWdW?I}>53kWY3i5Z$#;TQ|8zTTRL){E9V(1|_x$odqKA&V~X zak%lOmL;&CFeOc#f`@H##6%LwZBfNqt}WuofSh*wJo`K(MTv4ur30B#X+i85V@r?U;_Jx}*WiNqtKI=Jvn)b868l0H;i8dJK8H0q$#54R4RiI{6e4$Z@C=XCXI zmz*rVf|a(H5V6}u0x(9k!;XqY{Spds3NpTIh)k{9wVEOqF~1mwuY{;)kQBv|(=g{Z zq5KsaE4Lz!vPLyc_BRaq7CEx6)?mWoJeK}N#Od<@Igkx-{{ZC}WcLn*MT@6-HI=%A zvu>Ab+^nTZXW(q7I4 zG!jmd5LHk+$-=m-Y`1q!4c4VI-Ft%H5si{*Gz|uems?BL2I01Dp?j3S<|`|thb^xG zlVsDvO?0RXblvZ3BbLwxXyx;35&5RNgC^(6@5D_Lr+Px%yoUVFed4k$v8^g>%!t&# z`a&bkitEtdXKh9q*u`&d1%=S5Jn5o$xUR47B{{5H`asrzsn2NgR~CBO*>X5%oRZa- zlHoM3<(=fpP|K^3l<5vKQ~(Zt8%M&S)7eeR?cA2tx)+v2x*}>vp=f(^d+q+^<6Vr( zQaivX*KjrQT$blP2jm}3+v$5ddd3DMqNg^V<76x3?r}kR4e0bny1}Ej-%LH z?$dBu`LP;3zY}?azI}IdwAn40k~a4(tWG}T`4Rh5Zq>qvb?uGUx*+bmd<&k|hHeh+ zC&?3TF;WVgTunI=*d8Er#J%?P`CGO?<`N!h)yR6)Mh@H?+;i^YkaaLurCak^>##7(dCGyY=yR4DbP`&Y9mbzDTwys;0~`ys+k#?T;7)w zN38zT!JNw7O37eq9SA@jy})8nx4{+HiL7OH&Tj+!e4vUfKs zZH=Ot8)K0#ds}ZVO4(}FZ@f;1!kwnItICe*6PfC1o*?gb<(0&qWRbPU(gI-Z<$q{= zsAYRw;2c2q0+#sWks|HhMzwCfK}5aRVu$uVc$f=^oT>h^;N19Fd0u z%|_m(cMjRy$i6_eZ02ksr`7UBG%vOgtiqO@ices~uG_YfCeg;KJ9XqCo?msnAszGw zB^Itzur?G@7csI=y;Q?}`3U?u{-tv54a-@QVDzV7=Q{w|{dDk_@BpMt+x9gn-t}AW zM31O180NwN;U#aR{3eVC8Ia8g@{YL--*XNyXxAj2Ap0;o__ zH4d3q0~o83axNm=k;aPoa~})wUAtskw;h{pBFQRS5tP!Ib>N*&Aq475)IqIrWxB-M zhIIj|T-)NJ$Dvs)5K? z3*#)|^0ge)2YfeHO5Zcl9FA*xL7*fl(f&EwPYr6@kd_hqrXwVMT5zhO;NKY* zALhH-S1P&E(H70go1WPM8X;Sobt;bqw1l*jxX@Nf=mk$qQL?f5a}eGO=+9|#X8R2s zk2P@`q~1N}9$eyS47;#5)cXW>)C#RBbiV<>4QRN}>Tl@@2iu1B_p-3)RQ}`G7_5^> zo+h}Sbl2LtXTtvgn^pe+Zmu*QR@r|$LN)H2Bc5BGXiJdfDwG5G9QrXcX`Kg228*3{ zKjo4wUt=j@@H{=L>FzFi^oi?UKGovqcC>y*w?TV=U7r-lkMUUxM~)~vwtv_*9-#fg^uwWk7xLGQMFC7%UmV= zNJ3Vin*A7pxx=}h^K@Drrr~6s>*!R=!S@Z+@xR*cv(gZTP+5EpE*h?f7914Px~+Pv z?)a_!*G8>OEyug9FdHWBC&Yq~T8k}~ok#+t4MbLlyB2n|tkUAqjy2|;%iWukk~ZDe z+a||vj?>X!TT>x1^6E<46so}N6u}A6MiJ8R72EWc>YIgq7jV?AtHL{pd;b8|FR13? zLf;$f ztG~M*dukHwaVW8^G**=D9k^^$v|T`olhF3zx;05a5&^9%4Ma#<0vWBEOBCXcGtDd{6XE-VS#zv7i}6fJ20(Dj^Ai) zu$=R$Cp)mez-| z14v3vp_fryJCQ{+kXBHMjKETo&`ddNr<$aSsZEdFC%U=e&ruCZga=x795-`nmRp9D zQ~>IGaI2d{jZlT99X`wj5X?uq+xhQsI}0i6o!lMsX?&YNeN0D6v!lD z;b1B?($FO+G5%3O=0GwusUo@Lu|wC{g}zj%=`NA>p&-OsRsb}r)(2V~l4*x+wx~@c zy`e@S+n<)_meXNIoohe`(}kI4N@YG*q*XmO_S$wtd%9inn2v1q{08dLDq`>v4yYg}L4A zVW9;rJ-4skYzri0v2?TA!>+`2kcD}!x-Wm8Ve@=3K_Kf%Qcwzr)G1mTR}(@o#)|M* z#_5^Qxi1`7&)QL$BxHY1IIwYFT8oz9BE9%E0s>HhJ=x$}8g5DvDC?+uux~8|P^m>v zglC090#^DJk5L6sp`dyS;c*y5bC99B0ZbS+i7uovP%;30IB^?7NzN$38twXy3}Z;T zMZ4+K9cHi;Ne8|RQc9F7TAYCF#XB>X zHm{*=LcZZ}kzTT)^ml?j{{RtQcAetN=On|IWZBneG(EUsC8bmAWA$q{U5W45;c+ zaiQ&@9^4;j@0cro=IH3Pnb8gwR$Dri8fq4!|! zHBMSk0X_L)GEu6trCr!E3WeO)2Dg;}u{{Y?Y ziLZytUR9^Ft|20DwYuu$?kYo`J5=!QkAWOuPG-mk(bknC<<#i(Kt|$0Ug=l@j58?aYG8_ zgs7U-)oziP&j%pY7(H%~Y1iH`nju>fDhGc|2P73)!IzPbroP-6d2L!(w*f>1Es&B= zXdReUt)lTLbmfr2MyWFNB0xQUfrY`<5pAP0-PmvxMMG#QNvNedW56#}3<)_ALNMXY zs&45j7Wxf5LW8v6^+h3S+ET4qR16pjrtU)q#c~F{nd8Npgj%7(a;mFPX@b*KQa}rK z(i2ck6WNDpyPZWG?&lJ#i)f+yq)@BgYl@ayKp#S_I6!=ouWrYMv#vZVw>F;Th|T5| z);f*C*z;w?{o{n!*n5->Ml{Pzb+yJhtd+H@DroZJG zu3xy$TbFA#zcG7p!lQ78QbLE6u}?K1ih?{t+l)(e>{)Ex=0U4`Ifd7qw(l%i7=%~D zqCzRv`={68UuUUHew{xsec{LML)sj;i5uT)$53e8A6Yk;DF;mhuIA`XJ{L3g;r7z* zt;M5+R~onL3YFeJ%GN(R({p1;r#k8IyXYJ%=+o4@KgZmSu{m1E*5l*-!o~L7pgi;; zT3=yAd@nSw5)Zs%qA?qa-uazTI;-Yx_3iFkZ`>GfBQ5YekEuLVR*Z@&J25Qcd8&H= zE6IGT{aB}d02Fe{Bax*fEGM8nrkGqyMdwCno)M%#(1fQ+aEyWLmIS`3o7(kPy<|;c zJX39m(rDzXqXeW91rUX+w-jyKXwbTk^37)F-nqw+T;;X8epN{XAL10n3C^gh?YB0M zqDr&FUDJJSOKJgeS1Y6l?Y0L}{I=Sm{{TA>Sp_sH>h*mw0^bPAI%aP?|!nq2HD#M z#C{<;<+a)XofTF3kb2?+v{H0pXLmgmf94n8-L|ogb^55JE^*kF+vefhpFMsyIEG~< zNm4w*lqlz<9;BRxEgLsB`$(VADpq~ojhx(#+(JV@qBZcW3)d$904%}XEWop_BHl&D zVnA_gNA3U!6;*06cN^vLT8H%NuzRl3M?Oc$^C;SLPu-R21-bVTGjZPCX401y*^wM3 zswC+YD(vmWZ!6uWGyuWxEe~$~S0Z`qne}faH0-}k8xp}|-tF%#uJ+?tpyE=PyT zjFgmz8`JyRjd4@R@B8bC+F0(Iaax8Ou-N-M30pLMRp3t~zs7!;xLP*VkeQoyKI6I0 zaJ>7^v|L*Z7XV9WAG%VJT(E}X?gmx!j)&5LOYCmLZOCjY_`}>)6y@$E+06QeJ3DSe zcWdnVlc2DcidI}@x5@*`Z4LrxDoRJujdyQuyWhb4_a3+Ily|A z)ZE^eeHL)U7@N0wUhjANHGHOFu3MgDsYyDNl9=gbLX@Ey0o#mn*L`k`yNpo~9J;?X z*$suYSlgSRT*_94x|LUX*VLZD98!MWAD9H2_!YQqLRl{{SF*bJ?30m6+T5 zx8~w`_FCF9e6&9qG6I@tttCUMb0J46;;p!IcDdQ#1y4hHj%!NUJ7W80TSstueCPh@ zriq*CCJob+Wo_G!BwgTb?coqQi=v_}i3iJdL1=8Y$xdkK55GKFyL%(?Y%PJM%swiI z-i%$IeqlY!+Q|UciLL1yWX891%Z^5Q`-E#9lKA^K6*iXJUWmcIfl8%y<(?#aua|T2 ztf--L+M+)#drl^1r+W=r$lb58S6#`rw+y7-Z;@>hjiIS4ZbjS5SdNz*TC}Ikr8OFK z#+7G~wqz`Y&ZEn8A40|R*sGV{%W%O~2c3SEYtQBp$kuI{vbN6NwzX%!Y`AHHZ(+qa z)P|E3MXQngC{dz8@l*mTF_FjED+#WhEUqqVjcB?R{{Wo4#jS%Nw_sz~-kSLGT>YK6 zud{a@*LGbmkKJ$gsZY$gT%;&Ew`53BTGT>@npDsLC7iro%geer7! zIw477;r1pcK6-86yd4W~&)%XEpzc7g!d#0d&QM!u^Av{?QkoDm%A*teE4Y@KsT|mW zt9({<-M-4?HlrhT%|D64ilXXu1zuEafixYM(}U=-Ad%g9(xRgoK&vyBJR4Mc1Vz`0 z%(SZDt$h%LZT-Jzcc%A6kZY(P_)%e244IH0yroIHk9>nU?`2$wOdV{KYTi4)Lsr) z@|iRWkdaQ%Fy@GQkAm=OFgeKg4K@8Z^%tt|1ZXDMg#{_akb2P|`|vs<)#WK8`D0K* zo{$Ics39M>0DTF@i&GNLzxlk?5uSvt8f#n=%}Snxs8aVh zdx(A^Phvz)-Mcy*akwgR2_7XThM!Tw?UBAIlVN!a5AEB;P=g`@nbs`FqITc9Ota2gFHN_oCP9w!A8M*dSkmG$IV-aP z3%Z+Bl#J;#M1zZHDQ2`!VC}#nm1MVXAMtk0;@yROM6(4d2dCvK~i;r zG19F`cyyo?CZJ>q#S;sk$z0z(kB_Dn@F$wpH#XM8SmR=R6LrqvTI{d=RsAUXEZ@6dd~;yrNS@K&HZDoekwJ8m`LNySY zfnJ#8$v2+i-0?aaX!=@B3GfT)Rx7rf6StGH^7?N&T2-y6I{Jd9KB>J=gQmBrac}buW1P@TWu8TBxRm7pTTV3D3RrNn>uh zXq3mBCCyGgP>=yB0QO;zl1rm(dNe7KIEo2moB5}LjN&yuI#V{<3AjK~AB#eueAbj4 zO*G~_Kxc_O)w3nGbZ*c%QCIfAQR zdihGt5yst{Yo7)nLyxJXooX6WrNn?#3Tg!oIp?%H{kVs}7k)YqS?DK$2a@f-%%5n^ z<79h>4B;ybW6fyPf;oZh1aaX_RSMc%AHKOKV(;!xwIPUnea8(2Zp-wC6xj1L2RxP# z5=)B-HBbaoF4IhH*=_xgw!V(c8p$sJKU(Ch4{5Ub3`E&Jo{M;EQftUpO*Kq77K>~^ zrzM{^MKmUdI(=BSIl_xMj@pM%op#%8&gzovTW88!QQ;EZDPgarOQ1hSBJM6g8gWxr zIM+Uv&?ouZ+bY=Gj5B(Ba9t4CDUTfixQ(inR;L^_R(T2xKt*#ruA_wlz8qDIE%y&o zmiAA@TW?DSEwQ0pP6orz_lj%F4eweNhYcuTpwV8hO8i$hcW!taginzZ+j# zs%oSpl=RelF^KmUKFDG6xw&>XHH0Nq`i?72eO2|%qGs-iH+C%d2W-qLOy=8CnPop3)RB-#?>L>o zvNuad@KxQ*B<;2)EnKPn@?QRwhEsQK_=}LLx`$IwELXO@*wnGnTMYu{tn}F$R!V@R zO@(o_{u?2=F&w&!Qp%*?QrZ-T2~J|Cr3NE!;FHaxPZdA5uOW4*d)uQ&6;HWGaxpi2 zU!++dShc#Kn1JdP@PMQAn&VZ$SaiSyf~9g^{oRD$yu$Bvl_zmm8=gwy4b~7uDgOXE zrr^#)Wln;GwzZHt)DD9c+}3a8?!X)to7?@UGgxx>ajG-OyIsZHc0GsiX|`>hZbM8t zdX&*Ykm7WuY5?@EcrGg-Jg2xCyrXWdzJ6{fr5!K|aaSZavUuBRF6?}ZHICTIOo>1Q z13&;KnW;qMF*es%*L@n3Rnc;0u^1d@oJeZ`R5y@!Cnxg^tJ*etmn)4Ag}2_2#70d8 zRu)!jec0Z1=049B*7>9O-lf5v&E8pk%`^Om(%06jMdI6iux#zkJ84;BTy7g`j@<6c2FAbh4rwoG150P)apf{8)nRMT` z1X`_^2&sC`%SJplpKnM$V%lpu>C6N5V#BvA@v(Kb0asUA>awl-dAV*xQeC()jv7-$ zlgD8qpiIwiwpuqIJ}OfK4B8N@xaPWm@9R2Gh844t%RRzwUjUAjK89 z)bU0(*Qia`v+j23HtP+tJZOn|GGQVxP;k1EkW`{;RMw)HS0Ss~hhJ2?VbUIdopg^I>VeV6EzU~Ib*Yt*}=GE?u zIE%;5_`J@tbns8xkE^KZX(9%|tEnV^c(}fxQcZk8Aa(B@F$OzlQ9~tF;`8$?9BUK- zq$Z930M;)4xVsW;J--IqIt!?F-0<^d7F=i)rC;6&83INh+V;ZEF+yq&6;f&ler6LOA{qG+3Sf z<(l&0)5QK-ATb}rPCl!Ttw&HaU2B0 ztCg!`J0Ed)8FYGBS3s;hM{T$9-r;{}UpCwpFRZCAOSsL8A>^ScDWiU2Iz8t+Vp48P zpPXowuF&!Yb_+HxDjY-2yo@Eo#40sA)4u3xB-^;V*`&gbR2*>^Xo_)E{sbjwSJ91r z-*v|K2K2ZS=v=RH+ez5RN!HQ_r7>CKXWV5fHwei^E3eH*hK7d|56Bb16M} z+BS}xqHlNW6W|u zB8n(%6;in6Jf_i-3c2HnUs3QK%eB1$u{atvR;#b8>xS9hce^FqZX!$@Jf~O=HoYz= zAkcN~`f+iGwsFmF9M@8-8@~L;No9QKp3vsB(N5bV)gK%0HyDic#oF!;096`5BAuTS zY7QzHUf~O`!>+X5G&xcm5WudvRho2yRk(t5XJT2C;7G%)Fm%Kcs6-R}SaK zk!+H+!aiCBLV%&|_T#o;h!7XWT%^v0#8;YF+Weorb~J^%w(pNyQ6wo7*h$pe$XKF+ zlDdPD!z`ZIwYwF2L2%}zHqUeNShLX^$c@}bB)uZrB@o%(7E7a72GpSH--_ap^jCq@ z5aMbSl9SMkN5|11Fv`t1dex%lS4T_hNBi7#&zYJ;uy-%ac_$0ou}VCR_?}r4Y&a7Ix<7Z z^4F16k9zF8zXkGj=Vsnt{6(u`>2JEX{hh+yDJMhiW^!>RgjXU6#_xd2MViO9dQtqx zFnNW>JF3d#Z06r<)Lj*Q(l{3Qr{2TeTiVY))o!pgFLqmExRqJ0pDg6XNAXl8Aqi1V zp7Dy#>a?+Kv&5t{khGrH<#Q5bnI)?XjcEr#S}X1AN6{NzqV{#8nmFPDZCvQht}B=8 zD$1Kh`7Fl-u&q=L$wdflWM}ZJDiP0}uI|gQcV^b(TXJ;tF68{ceqGzRam~rtid_bd zrugaht`=GOgOv6T*WJ7^aazvHn<3WsCmP;ihnb0SZNOCG8ju5#pF-2Bs3SbcCp>lg zHqJ8s;kVF%)lD1^A76_3$$6T~SX#duD~OJ|vD}ZN!}KXfZ&l{AJpjUx+!BJ~>R9!vL2qH(WV(h~X?|m$0oB|q zo*fCoYTVSxP5qQ%LHSWF#K&!FTT#jIg%O#{-H$eoD6YGdyQ#xf7G4@=5Rl7_)megn z?%?id3VO$>Gboq_G#5T+1`O<(sZh7@NU?IgeJ_IKW!Cm`pIc`omec-H@=gSO0<&RGrjUXVo zx8FclX)N4Wcdyfo1Gay}vzk9i{cE+iqAm_(^r5tw8*VsOC zz;gFgVi&b9rR2@@pS4_jpAS?qJHz%<^{qR!WZBtyg>H$_1Bh+2w94 z<=ND;X|h6L$r7ZzzKCtM!6cvX^ubA|6nYO7;och?esR_1TU(JfZNp=3CfaHB*)B|_ zsUAbs^4#z#V5L+vAogIP;P6U0-6n-FugwKoCebw^Kd%ikU&&H9ujYjo?T}Qd$@Zj= zs}cAWC8RLW5Xq?G|uK(2K5VF$W}Co4MGTjHl)K=mVV-TQh5 z(raxoZM56lWwbfxIP1zRARro$0Ie&9?lW$4Iso9Y{ioRLY;Hu}2bVp>nsU`Mwoj;? z;m0=Fc8e4Ua;@^;RE7EJaJZGJAqPs5p_W|;$E|acMA`vWGrW5zj?PXenZ!5>Xr5~I zk;udprqyl=J?VS;*j(;Od;+Bp%MKs!yXsZ z6O!g|XC!eaGuxs5BCa~Wu|8F_D@vqA2~>G4Ad&!D*>zzg~5&{ zPpxxPlzDT|&ndk!T)r`0+PO+n)~&&3j^(xDwV#P?GJLh3(^TP%@LN4=yL$_{Y`_Km zL%`~)(>pha*t=XM@wV!@Un9KDeoj@qwDUl&fEA>Gub^Tw*2*Z{OG>-kl#_BqCum1xEJ6tBJVa%qpUod9{qls)E7m@4~qE2FeNXRy_T`U5lII3h9?D zErENqT%|=MU2U-B2a{5TsI~&LKUOvVut4=LH^H^#uWL^S5Tg&rqbw>qBk4*`O3H`o z!5c6tZ}6v&c$HE;PcYN)w)Mdk3T5Y;Sthhf(WnpfagBFY+|jOGC)g{3$z);h3bNRg zD)H%1oUxi>UfowiW=~eB$CX7*2Yx32Ro5&I+yP!{GSanIDv{F)G1NvOJP@F!no$*~ zAEOgdjVh{hI*y_zH0voVNKFL+7)8oL7o?St3Ws3mdvL|!KVi`VvM5FyrBP*}Ii8-} z3OihZ9k?wLp<J?@E&MuvrGu{_;6ChCYiVj}%*U9LL7~WuyYRaO5*G{(6iR|q zjHS|*lquj+wE{sYJw{k@8e!$4o)D9#Fp7OO!OB_2s0GmYfgb8$ui%#hR5L*V#MNU@ z?+<a$6y|KYJcL7kDw|tQ8PGU3uSQS0ko8CmyGw50W0BXo(#mhV% zs?wD}D_vcuvlK{{%$@R*D$|HHLePqn?iiAyhG!DBCt4fSqNE@Yl+@8R>p_KjDiPLF z+3e2%B2DUzB@`g1t_;>CUg`w+r+7;_zf z4&kDV&h9V!b4QM%t_+R4W1V7!ZED<@&||gz+KI@hpsP};QdCYJe7k8*OP?8;x@F0b zyHZpKdE)qxCMsiXDNajiYL5p=(1lYa6(4pA+h9u9aNFF<&W9cx((e|`R?NKqF&;|} z+~UV|DiT?3Y%&ycQhd-xq5GztaD@2wyil)ZGLk*Ov~$sX;OI0al*bWA68j1qO3G4A zNXq~kfVmEUe}^I0JQ*icE`UHk3GEmfB>coSli!7U5P66|l+(Wh4*!>(`j#Ut*YAI>(Cra?7Ucb5Ky$55J9dR8wlPg;Ffj#tTkyV-E`jDzWZ-t z&vM*yZZ@c@QrLv!fVPz@sh|}nKBo#=ZTm)_b%7ywxi8)aTgjp;Zr!_{*2^&=-5qKQ zQl{iXZCVoZD>=62pNS{9<%Wl7SqVJAz#ZZgf!~=ITlp@KmUtAUauAO~Do#e+IDS*%-@YkA9L)D;_FELRI0ua>w)R8^@dTUDV-(4mn@6{y5T z#k<(*5U%*F+!u`MjMXBP8qj~8hSXC1Ue-@IZ6H)v&#)TS|4T`x|(vl`)l*|IJ^S$W$7fr+_zklFZD zew?uEIozPeIK?r7VPxLAD;aVVDqy(gcnd~?#TSeuZ9*fHq!5qr3 zBMGX7X@j6bB^Bx@4?}>UUaA}lWIoIo5k#AwR^Ss&KTZq>iX~9VPMpSPzXC-NBOrKs)!zhN@_?W--WsgYgM%YPlY+00Tf1X2v94K74N_=OD>DvUXzfaS6p}m*pg7G zPMY>t4+3nr0Fz7{Hide%LWCOh)lB*f6~TD9MH{7QH>ZVl3M5y%6pXp*lq;4oy*Bk- zJL66hkvC#(OMtw%$)5#>i#}aLMZ^VA5_9W~vk{7|s||RCz#HSajEgHYeE{^lir0-2 z-s1GxF69YIXK*RW>{U^nD52CUK*;v=!@Haqk-T+LSZ?&k474%PhmNX*au)_|9sM$W zmvp23pK?Ss7NBning%=WC%}|WVLC$6f8!EDz4*m3EOBHylSi`(4SfY64spLctwdSOj-=<2Z9N zm^rJc9z2TNgLc;SvMl#x@oCY`{X9mCs=l9pF%6f#(FVcF@!Q%L^(819iK>_~7fMyc z8mXld-BkOrvTa`LLof`VL*lq+yML5xi!$l)zWYA!KcmHTVifon2yM@{$!>fp15{~D ztu8j=PIXF>NI2OoZX=n!Odx`|w$3c< z!RpU>>sGYe%ouuxbu6$Fe$$C8OmvA=E*;HBo958{mNlIZwxo%X(&G*hO=%;pC2dbr zm45zU0_hjzZE01**L)e^RW?1NxZYPxl=HBdPuNZ~-t7Ch@1%aUZS1^VS%Ran%#=@M5vB<`H2fJk1xorC=^Mqz?PF`oLEe zcIHgMDn*Jk=@6Z&9DTI75TJ4*y)jvbAtwN6w0w>pC%hLlIE3GGi)`E5blf*3^4idO zrkiRrt*uRjsWr}kfJm+=C^oz%WHGHTYqRh))0MkDLd4MtlRIXUB7AC^LAVfuWho(M5biNamQph z@>^49H3WpD>NONT{5ja2nvV@|X7e24?nO55U*>k>ciOVt#3O4d!0}aYYHxkXvagW$ zHr2VCP4eBZ3TPMg3PZImI;u!j}r$ z3f8grivXHHWxv*HbM3^lEST2OpeR`~a=q1YC+Syf(KD_805|eI&FLQ}3eU8z%9Q!k zQjavYl%D>>j6b|*_%N_>FAJ?dDiXWQyO8L?5d@}Y-QD~h#*()#;k|FY&CDiiUR(}2 zVlUvfWW7>KoOQ=a+y|gKMLN*pLhlis$d*RNHYShW;%KYTZw;-4^Ja73aQ74La&0j%H`eF zxZ}uOI%t{YwD4-zk1dwH-N+j+Xy2~?0DxKdJ;ppMrT4@A(WjL{=iX5{8Vai+TI1WR zBQ1tHN#h+(V5lA`Zr@9q6fWrMH}Y!iG{5ni6$lr-trbl-4bpQ_|zadR6;&c26mf$-dqp)f-NPk;BC- z@n!FG@H+V?d^u)`D|c~x%RD=cvDaIY#*2}4wV*p0WK`+^kaEK<%M2OME)Ym;pwq`i zC9v|Bdrn8+Fb6&(LOFh#q<>VNscxR67})5OVOG#@Y9;kO0TO)n-g)4ZY6{SUqyT6L z?Zd9#cGZ{{Jco4`CjIX3y~g!1x<+()e7!21x+Voxxw=W{C=N9ex${7asmq{3f~2~J1N}W&xp}_0XkZ9zI^6+ZlUgm%bKX<@av(iF6`mlTw^;(=09 zlAsi#2U-m2+k^!`G!;i{g+)$@O8pAOZ@o;5w=RmjS(zY1y12=VxBLL9URtwAtw637 zvvZ3o;X<5FSWmmCNTEv1ePael(G0;t6;vbzR=wSDpoyt4N?4BmhZ}HFDs4e&C$UL7 zfy>%3gt0a4q2N;R&h|Vxri3Sy-PL&A7q)Jjrt>QGD5VX(aFY9yAM%m_2@82jG*4iy zF&S@%g588f(AxL~F?*f5y?8Ud&m);NQqITQH(mb#bao!mwHFf^I;BZ;`El4sVxDy5 zn)VK-vk@7L?Y)a@A=ON6&7pagm!<~k%<@yE)6+fOt%Y#jQEts{YyGMEUN*J5JButq z(@&U7iU7H#L&OrEnc|UY+=!*T5jb?T^IBeKuot%Y^K!%1;lu@BAbt;TbUP;4o3avX z@~T-wZWNx1D9)Yq#rf`Zt@mBUV$ijg&iC7|f^lu`M{jL%Bt7{K#KoQ4Oi9t;TVp5g zQf0IwL>@F9Y0C8${(_D(pNk48R zoEsPba0;<;G?#iiioSd~z*eZfl>I>S4&t-NyImV*J3M=MaljW1Q9k-YnicA$IFzft zl*R?RH;u%*k{d=6)`O_=s_d3q)zr^HZl`c+8T+rNA4o1~k@&)&Bk zvi;?xvv@>Fk4HqNk`9zSgqn}qylv{&a9a4{e=<&a_HCu+Y}ZMUO3-7i(2WESz8phHK?r(4QbAx)4@>RSst7=7pI-i zv|@4I=R1bMyCS~wW6F9v@?$B2f|3gMrJ`J)^bbB74`lBjzN>QksZVG5~(?uWl-NoSEtAYSs3}++$=izlxI<^$?&s4xZdp zLsp7hh(7&mo?U|;YiPXZRVB-P)HSIp2v6^;^kMarNqqv3$V?c@l@3X?-+3TSTT(t+ z3>B%32?xwlYf7e~ojROdqPuOi2Q`#r@z2QvUxhhrn?6!)6Ska4AA)Vnau)n~QJpoI+wqX6fUoY9r~`|1 zUCNW9oht>&ep+FD&tI5&O0=ALu&q30VEh`wrFkn-o{=HLRk-mKE2ltDN(@jk*(q-2 z>vHHFr?#NOT8N_fLZ=?Cc?WSZHrKP;yhlyFX-uc#p$Yk>+(M{4#aASnCHf=Y{(=EfZ!J9n4RG7>O3qt5BazJh zLap0k^LJ46K-cu23eimaUu<)7_+xbO{>|I%S!`DsYEI*v;@Xcb&pZOA$C{zk3L0lj zRc~*xe~30|T~`68g3z#=Uvqv0>n69Qr1RpK=;6)hx^5ONzqt2Yc&;+gk8DGadEt~a zis=O=orkv;6T9HG1@SVft2bxzIY}E0)bs;_QJ?9PXjx@PMc0%()vTb(X@-DUO>?D5 z_R|bq+#8FC2xNK?rY-veipJfs@=jb@K~}OS4Dnv^y}5LHM5*zl#g5&%TKu9zNb-t^ zPjxy|8RNGuLI(7W7f8X{{1!$2WA!8^tgaB?%U$K`UERC0*Ctm++-dZdQWcs30VIP? zxLv!qEX{B8q5GN|HZzgZ5!2K+4pBoy$Cp zZ3Bu=SCj|<1nKB9{ZzunY9hN-H;W3{{UyV9kKo-bf=^(hY|8DWC=62w5ajZLL@8zkyAla zUfdjROIMl#pF`Vh!$(t?KneG?HrFS|xW-kHGcD62AkoGhC{+oe1ffEiV!fE2$>ZLk zT6WLG=HYkn>JqHJF5ed^(j+}=a%<8ST#)%tDpHS9YI@=&nV4A04hl}=V`S%;mkN@d zoO{DNs$T53wwTF+5U;;3xDu45Ign|HT+V(kB=c7tiQ6l;-;S?z1=2l_#!p$+UVeB} zYE26*uBbhj!82JF$aqnA?#$aRwPvrbib-o_TYhD!Ag|3}*HAsZn1eU&>7`ZTEEHx? z{%P*oR?|f^E7YASJ)=BCzlyZ-qDqHsxyAYNr_1ShA0f4V>bo%RSG3ZKIgv=(o-5X; zt}9Vuay%&LvYJx8hX}olBqiW`!$@#Y{qvBdZQ0k@qTb`d3s;chn^8bLlr-s!RhA`^ zfJ>HZn%vXdIn85Fu~m!Z((yjv?ZR}Y9uKIgVAg;hzz8!-%_zCgIemL1#m=o0 z!)EU%tV%B-^@f&e;worq+0zZO7=}tKqMh5jrsCDy)veoGp5M7(w;`u|r@AQ}nz5Q> zan6bBqjvsnmvMD-PNN;Qwy!loY3vwg0SazN*ygAUmtpUVg_cFW$tWt`{RP4qG%m;J zqz>auXnRt7c-|a-FtC2-;;*NG#?50qN1=10e)@gccUJEA3nuM57U2Z}=WW-pp%LEI zCAkTvlh^{QjpJ|c`>5U+_VqOL3+JD6yA5SDPo5`vZW_>qLT)kMj{D5%)>~4Nm8UeQ z14@J3anN>C2e+%ma3)sS(8^kQXp>-ErdlTa>_-X>G`O0Om8Z2T9S9=EN6)(`~;nwMC8cl1gawU_otF$kM-~48(4X(CVxbzAL76H%BW2u(ln~nYOdr zb_(9h-3Of_dDqZZxq{f&Tc;LS%DUze zr->d2=ckTmcT?*AAt}a3i5; zodjfZ&m1|Igu^4Pw;MIaisMp<{%W4Rqi4Ohtwj;5oEb8cP3 ze05g$W(2yH84Sjfqzz6XDm2u4NCuU}Wv#noBn|}&CpQP)I)p+7^|?p>?)xZPOQ0%9 z(5~*I2a?IIQW47L4GLAdZio_Kw*mWN!qB~Kyd4IabfF!%ELJ@zA9iI40sEW;lqxUG zqJzYxC{!!irwWU7gIfCXNo&0}uyw8f0B?`8>A{0(!8{ClZq61NExM@@-g8jTGg_XM zI5XVZDN7k-W5R>YpQiejt0qb{+3saSR_ z-5FT@VPC(`-Xd3Q^wWIPO2WCsQmI`bX^!?^NXIvJuR%EU)psWN1kVZdex)V;agC#K zepg8kl(kHk; z1?e&#lH~_Msr4w(b_(FlfCS<5ml~8(^;2q?26ajO7(*rel=~Se^FoPs;;Q__jKHN2 z(};W#_m#6e-*r2cT{Zp_mMwBBK|m=+iuPA5B;*YVp)GZFVwqPgM!l**jiMx=4rjBb z6G{XMij6eo-+)R7uO&c$R6scLF)L5^m=ux=zqsSc!jP>1NZ9u#ia`-%1qy>y>Q7#n zm5f$8DrM`7FNdWuv>h}9;_38ZCb%mJ;II(lf7P7*qXm%}t^sU@l&vI!kgskDQjO*- zFT!tJt#-}PI?&Z6x|W-fFt!)vGMcWSnuOH$;p|jYt>p0H0w=vxMTRmB!YY(O0MaV6WGMzpVq593){<3 zqa#j4n&B&Zs+|^vcZ+BoR9)%{lQvrtLwUuL(NXRS1vsMjEZTN#1&LF z^2u?;saevaxb|Ze!aLq8rL!@bRXfP=q@2L&F=bV*s;zX^wCwAHxM+Z#R_&jNcZ`k` zZxpFd7)l=_8N!`JlokPCZ5CvJ#@Ck>7zpQnf`WpelRt z=`%&}R44_FCY7(Z3CNv-v;+qU?aG)VA4I9tQBQoo}Y}D?07(xtY3WB0*I?rAZlpY1bM3v&~lBD?)uerFQ#rhFaSc_l#I6 zpjF0@98fK@?7`YJUh@%@B|y_b&)IO|!ecAokJt;K0Nz|wM2)6*`@PFqDxp+iG0j6JgCM zSW)VoMwBMKF*_BnNK|a5f2inHisAcJw`pFaIXUxZx&$eUb?Le(am&b4YFCPYP+}7$ zX6I-XQTdSaONwmH+Ocj@B|HhZy2)ED$|kPJDLN8q?HS^B)zoMOLo+7m%>mM>8SS5X zvRoQ*DhYAapa_X2DoR!cO9!FD*N+6X#$Guip;9PAHtXDb6H^fEqZux}9YiE+0;M9R zgqj?AtsE6o<)0*F;AvEmxo)#BZe~AApHyG^niNfg4f!mx)EH4Lk2+SQ4W;p6zh)w zkXwXTp~B)Sr0NifNC4+i+kD+l68Y zyPl7*z%0X$3992xEeH6d5t{a{pOdCDoLlNZ{V?>qQP;6&T*qStM6%Os_S!h45+WsQR!imkgyT)&mi19tIV zYB$}Mb++o=Qc2ZKM0FU5$>H^?x^2wS>7`VY>AJT{J-Zd=HrRF z!5(Q~gF-r2A)Rr8cWT2o3<22s*P81N$KUO#=P_kzL%@{t^*6?RCAMx`V(G&-re;LD zrPSZ-vE_+vM0EpFRD~51l&Yj@!kweaW$sv8e9{~|Q9JXAz}d*xV_pjnf~!9zpiml9 zu+tk)%7D4RPg1Q<0&#?``?h%!dp22jXnQ6r67Nfj-u6Q#Ac&8;eEolzB z9fI(VhgJ}$)JQ@BG^wXx#Er8L1q*sol9~-Z^I^d7X-Y_SIiYkk)DLm&!{_)WLDgG? zg=JcIVItHZp@5*LZ+;I{S}foYuVc&0*z)oi0Ggi8?0FT=q7?*Irz&)=3|fd~MpIHn zKAaQ;1=T|(AvMn<*@rERA`*p40Tr%z@MRaOj0BT~I!Z zRS{kqGB+%S24uPrDce#2{Wzsux2oe`6?hNw?Ca=Vq1s#O&1Ty>deYl&k#30{lT`M$ z*h$AkS{AYfH^48KJCAEzT;EGAN4(;Fr8?8emVU;Z`m2W?XjSKIn~xByr!eiyKKi;w zz1omtTpLE2Dj*LxPr`ET9We`##|d3fwocvVu5oY}W!0R$3Ocr?L`X9>hWXpg?(LOy z8YfyMv>#%Wrle}Fl?voR7>T*WGHCiHZ&OwM*)k*-HZu8Bw@^W+wN)-)^hF71le>5l z>Zy@MU3Xy2RVFd}#72Eb?-Dx_v#Z*1kM2Izx-ez6)Od4U1F%0bqCzdR4e5UmFnlN0 zIeN#jK-^>ME1La3-VKb;ml7-`>6^7~2yn0}bNi>MroDty6-+MZ$YP}FbwKbdPu@ML z+xw#1%?)ddxIMJ{Lh6?idgSBYLT(%l)UmWLIznD`OV=4|r+q4#`*FD7JCkj8I$!R& zaK`@t{IO(lZZEbTV?A1{m21CSF3h!Xv|L)bM0i!H3@t7ok9tlo@LsY(R+@#)nJgX6 zvx_BZX&x%0db7P5cTJyc^L2cy4?E=l01ud;n*B7!f4gL?v|W|?bg=TQOXo^A^S_`e>$pe7LJ}dYa^!NuznQ(lXJ-1(kxP@x}o=z5TpbX*+4~35VPCC@%2?nWc!9K?#w*|sY7eC zD|E8C5hFVi+@(=Ua8RV5ZX&PcZM(G9Tkw(1emaPwN-4Xy4WD7SyKtVQ$|NaKQK?-< zMCXUtY($rS`-X}qbneJ8N6L)UsB>`dMrOR+snN%hY!s8Rf?!Yr1q4!qjX*tkJ(A>_1FGf#^wXf(xCY8Av+R$sh5_m&c;bpc&kdv-!vwO(g+7CXN?wF+$lh@D6N^$ zONvNg_CsK;Wjcq19?S^^CdZBnz;R~}S@{ydyZ1ECONqB*Y>26wd$Rr?GF-z9x#gEq zr1?zbdo`bJF_-TR<8@k~JT?5;0*I8w(CA*T~FhVL@O=;_|>P)@Ee&g7T zJ8#%#b8;g;0#PgOM$fbN);7S*-V!XDBR8(f7ta;Isa##h~zwoIL*Ly34@75Q(F+ilE^!@dy8 zeML~mrL2^PD5cjr&{rKAz*!g`g~OM3XUsJ`$}w!T%-q)))@vLmCf%+I4a2vvg*fZ3 zD9{9{<$Xh@h-*J?8V<711)c~uKdYZ+2$MtbO*+?{Yj6= z9J9RnLghN?xbE%<(^gA1CR}y6F`5RAwA)(9(t$uLi!R^U);2H*+R$mtswVI5c4IAa z>1CC}tB@oC=ue%3(@lDI=UGX}J$l-VsGRZ-x~xIw9sdCL6}v|!xmoZJKiU^BI-ari zwMG*5-QBB9YwOnVn_a<9A`ImzvfGPj(MbZesKwROwbOKS3o7|p8wil_QPij`0;IT| zDp52@G{dQ0&}miZp5hWi(BqE|rRwXaR#+kFP6M0~CnKa%h$`;{n&1r*fRtFVQBcZ_ zefV&W2}O)`j;t%{I4+4!qP&>tSrv%wJLqU|3?QWg%XpL*$+chXU7+lt!Kf19#Rj-h zD1PDE{UfKp1$_WQC2}UXak_a*Pj+Vs^@MRNkYW^J(F*qFaqh6ZXL8*Qw9>UTA=Xvc zw33y5V>}(rX$XAh4hX=G!jx3xrxvAkQk7Fo3t9l82V6L#tlF7b1v6064?>~`Tq8n= z*~v61*=yNR=QLKm-E4xeKjzQAP`}Yb_2G=y?qj|ZRNbwEC2e}ceQDVfjDz5mZ>tA{ zR3TG>6i2eDp~HDHroRbN%-cobD~Aqv`7XNt-Kxwxcm)%pRt= z`Q$oL^9(mO;<*f=RH}<9j_L!S ziXe!stmklHBlvYHzoQPqX2zieBy|@iP)B~h>fq0E0OVm1n*fzvSScNUw6FWzHgk$Q z4yoC)wuQ%fU+w#D$5|7nIFzM8YDy7b8;Y$wDH)tZW^*mEguTk;vo@r;d%+7AVKz?~ z+IEK6kG6KhS{i97UBX(_Jn3=>qMTXx^c{m7!%;&S(zucBK|VMS$TBZyGZN&8c8 z-S+ghrMD#k8iibC&k}R(rg`G4e>j!Cm7w8qF1LLht4}18%pQ@M<~HSTUNpBvu4bAR3Pq z$=Bfj0447EHm&~vvmBKs$m*NDZ3%_gtMdT<&C<$}urmk3a~%ya)57RvEd$_v8sVV} z=FGO=gXA3@)OB+W3rb7ecPO{Z!?Wzx2%ARUy$Fe5f*eeCLQ_MVq^OhHNyFvA$z<&*DCaSJnT zTQ^^p3;b!XwOj3-e(P|@TsTGddW}7mr_`q23E>NV{{Y?d<=i)X7B;sVvfcN5Ts)^8 zU|V6nm94ZWlmO#_Bqq6b(Bg^QZOCoygaM?%iB#%NeKBjf zvb1R(ReI>2-S$g2laZzDEr|0ff0^lXcB1%L+i+1PExp#tE{IwiZAwWBs!FrPDo*3u zO9>9*c`d#l%kBJEVbN*Qc&pdPH_ls!akTB-%G5iQaFU4<8*r)S!c&r!*gd$#_e2&3 zGb05YS7!F#C7Wc;f?s)~N~;NYTQFlKPrFB4rhpGq>=@2qVRGMZsKLinscDkPgv((; zAs`Z(P}jE+`4Os|#Jm=0lpJv?6x37hr|ZOJ6va|>htWczP!E1oz>QHPlsAfjx4r&- z$8^|s^`2(6D_5ThwTh{0I#aHl0H!qTO^YG4GCWaXTc0sC-b6tOs%^QvH`d$U_PwPk z$u6*E2^_Qni%W^ zR*V7Qatf^>EJ}_pdz*a6vcr7U)k%^Npht$KuC@2)jRreF?IMq=ixGXmdrPjS^1<;b zsd(L7H)>N#JcskLp^(3riqnpY*D5I@mHKf%ZHCCmDdp%@ODVnf)tRrI)Iw)S=e{dx z*By0N3%rGRL+Xy75FIP5^u)A#8u<7CsS|g<0uGgOeA>NFY(z)4GX^}UD2S~uEo~$O zH(f@WNhH^^5f>f3xHYL(%xC5w7}^H5luuaxrudPot5+vwI$cRCn59KkT~HKGWFDA? zcEG-;NIEMx@AePO^Bgr%M)vh7wJsNx&fB&(rLY5(TZ&5vQru31MAEdTEf~#@BtUhQ zn(l7++uS|E-KK{{QX|TUxo`Q+1qW26r8TJ~XH_wObn(R)1kqqNaLqjA^pjIm*uF-F z+_gB{0yuQmmCL1a!uFX6sqqAcM+Z4|dJnNBFv8zZZHAn6Zm1=+pb}J1Ks~1qWXZzD z6I#(mvjKG@!vjbp&?~LBe=h!g-4>V^uf=Q|eG?Fu5pI`pE0o$6Dhr8>+ldB;F{>O^Z=AMPo*k8%^TK3#Z>xkK1X_eki!OJ2U6Mx8-B$hOxNhKi zyC&XVa_1REMIkFgi9jd(Q`?nrqjzj=mEvX=F`vA66%u=Kb8{0uQQ}!&`=f~Z(`RvU zzW&)aGicviKH;@4GWei$=zdPd5wT|u7lbP&pEA@ zR}#-jQy7Oj-lr~hCQ)yg@(;{1WCDbf)`4m`uaDxII2Efp?d%krDH?n8?8CV6aXqx96cpQ@^4z7PIUf_pL zfm(L{{(Geax`kVQKfm2xoFTt@)ZCO60)>?%5uasom}fCXqv2gou{TMUPLleoF$xl% zgp{*$P;p5gf2+iOx#Kvv9S`8TD%AOr`=vK-R*mx*>$hK$_#X&W1x0eMSbq$#Q{DYI5sFv0(4I|Z*$qyr zGTs|QY+hr}wA`!=%8zhKL%w@yTY38w4lG!F?9oUa)u36P=-F16u{t*}Y8NLxK>BOm zTN(FvHLlv+-J-V&Qz9~kr~ny2=D)#5!W=~_PQXSw(_?oQ+VP?yX#W7Ue4+2}<=+1Q z4?C`7ZDJ>vB~BfNS0yaEpDiyaRbUf94taYquQqT6d>0(E9$hy>xv95?_;YUWu0^)& zd(R>@v>MzoopQyvmd}{VrJ|Z6xRn%ymDeIvI#6ef>wE6|t1E9hIQr;y3wiAS0Bfvs zITLFv-cch$e0&#CeLZ?o+#D&p8{K<@aK*dJw`&%UkqZsHmjP`{Sy`<#li|lUZ?52e zFK#0ZE%mRY9h2-Y_SNB;?h;f`Q^9NMH`A1hl=(c##a6HSeU&Ng*|ggMLTE;6eWX%~ zSp&wO-s&^Ly9>BJQw5#Vz1~N`KzGlvU&T2)1`e(+Z;D}hW9KceV&urGy~i60%emMv z?oA>}{{Rg$lxW)AY0R6c@apO^siq%y187M5Pc|=njeRN)u|3R!&n6(B_+i>pl~+|R zHrrj|CDy|+q+D$m*p9O@Iik?)c`X*yJ3$31$2-R};FYp)Ek9!KOue)@D>*Ina~{*_ z`^*9GcE48nkD$J=G(2Fib|++2dE>i8$)*+oI_1W z;+{`yBtRE7omz#u?ChzA*4@)kT5C@%ihB-cJb2c;Q+7yv5r4qQ0+Z55a2-Cp8PBgY z>9W_7@Uf)Uvo%w=R|q>qu$$0=J7N>A0HqgAYu8O{@4-6HB=$Mo$qe@0wl>mGBB8{i zG^I*CevB37soYmD@ktW)rK$E5Q*CI9jcG|E^bv;MU{iSO`Pymj^{*BvVlxO>o`Og1pBPbW!Zew&|{9*AXR41Wj&$0jFM~xCFaKh^bL0 z9-{Y_v0QqK-3xW7kTRe+@IufQbSFiBdjV9pLXbAiSGTt;L753dgk)+G3B{u5{{2aE4XjD;uWRINzRg=7e=MiO%$)!-FpGm80#gZh4- zrivrqo7<4Nk9WPeH+}X-=G+%Y34QmJgdwDAu7=Qngf!AdK`8(XE7DW@Tzqhi0({u5 zuw-nGMcFo28=tx^abjE}xB(3_8rbihD?-jhlq#4^ofCH3I89Yj<`uUgtqTg$q?IUi z%RD!P`{7yUZD2m7(Oz^wxcW=uJ;vH6aNc*KjW zTodxcE-TRkUUF9BYf(yd4}^;1(T}@~jw+*)vy?udB`KjL1H=9y>p_PTv>j4b?;$tN zq>({Nb@pL{*;OFPDv3C($UnW)4b#ZbC*P3kmOPE-Ac!fZcm$v;B~?(TXKn>}qwpe$ z^Wi`C&9=D=+*36E z^FpaqQJ_hv9}&iGA4Rk{RXXeym&?JISV8N)GK21`JQRS7aB~Fwikk}Pch!vn1a};T;vWlxIb>Ryh z-y2cMy#29maJK|_GHx?%R||#Fc40NOI<}>;9w2kfkEA8kpdS$ccHm=U+|qhZiWXd} zqn4Dfr}W~aU6rwT`r7rtZ5~}?lCFtqKQkH~j_qq@4JFEz2QFbc!892Gi=Nkr^%{yS zZ@Sre3mWQCwNp0DwK~nlNnx)dM3gC&D^8=O!JSC$rE#Uh5216$V=Gn-Y830o8)=nY zCot5b4p@x(TyzS!x+hvvkPlKvW*2KjV3nU`ujH14k~{cf8+ZWS?ALze5Rc-dWMC`xwhw*LSY zofslZZlflZPZyS>QCzz9!V|$lnn_W`HyQicEjcRUg$H5N@T={FbI{bw{<%Ad3 zyuncvAQRIDYGrkA8wk>g9_IX1!<7a4`O->M+Qvm75mEHuucauRT1M`$;mLg4hLSy} z93Rpb+KQAmkT8CLy;YqjFa``8Me41>0O~s7c0>Y5y$%~nPD2Xx1c-G}fZ3;T81U5} ztRYZ@)B~8$3pJi8861>aY###B~lhf+`A7YtVbKqirj0se$lVA9pjKFJSRqNTMgs{{SsFk}py`uvtW~hXpbT zN>T{>FyC}U*W8ooN_MEeEJGX5r}e7b#8#Z#h(kiGIVc93{)(5=EM?Nb4aS};f1YyN z5>FnL)JtZ=dCk88;}ntQB&bvRIOyC~Gyd7Sa$i3(yRZxAit|vHBW(WwB%rd$Y!Q&B zZ+04F?3SCdwJSd!6<{K<^s?fkvkZ6ijWIFZ1Od2OlycJy={IWNVuWNJN%VBQ2;;egfo2-)d z)1`V$XRa;)^c1`ml@zd~B>w;shkg@a^5m+@JI9HA_BO2eqdi}>SD#tjaVu=yuTBUm zhRu@Z>^V)8Q$moU*r_?@MpeT5sy4nL_JrH={kZzilJi$lP$aip&u?JHb{M+P%vn=F zkZD8iz?LJm`Y(B71Pllo^{Hs~8vAg7Xi5jHrL_VLdSlc<)QH_9DNHvZlco5p3vJHO zQfpj9Wg>Qt9F)C*>tSKwRLQ+#qjK#Xt;i5il?#JrCF@W6xomh%f5xS8I~;;&1No{( zDekwqd=BNoMKRH-DI=)qgT~DjicSf`2?F z)_`Dy8lwavK+hmMjth zd9A7ixTbkm2Vezx4uFyZw9w&Do0K81V5#oMmym%SO>5tS1e>T(i9tD>uwrVPIHA&< z=mjcpa~cwKTX{yka3z`~2Nf4v<1XdDEwS54E1hwS+eq&!eJRB?tn0e~c!h4yN+HEP z$CC;AmtEuRE1QiW2?$7_rB6TxIvy)`bPq!Lsd2jCYtIyYjy$+2R8rEPZE!y>Qz>_Z zAZV0_9ZOKJoG@>f37Ot8j!8`%X6A$Dphybpkx$o!{!)Q5cJ&q0M2|0wA7BR|HnvWF z5I<%Zw$H&x+wX?EN9v)DN#I?#$T1ZIdu`O+9SNjss#E34W}ONh0;Ak=#d9^Z?Qa=L zTT|k-9hcqf>`a|@vh*C0{liwQfsWHCmvpK3wM0*C2tVboOWL z#xI=hwZ*Ht;5|d@U6rywF?h_N5Z;d|=bsjz*qFR^&D^EMvx|0{yo-B`C<%7A!pll2 zKMLDl_%R8N+!wZQrB0lczjSu%4VjLwXmC6Tr`oL?j&Zc}UgEdiH(QMDJDX7(jPXcY zk=#>iuCZLks`SOVx#4!;UT9W-X|tVyw{&*nsjB5}u0FDC{s5J^te3Vo9BZoC^DZoa zLryC#6{)ARVqn1EE~8aL!(=XI$@IRUApZa>cxjvK4{2PMfa809+j^YpNF;#VGSz*Q zE@HEAEA3k5RlqQMzhWnGoNs)?x$cTqQ}1(~3K^0dn&mosF*^(TV+yM%ueDrnTDqG@ z7)B&gKHiI_nS4MTA&Tas_XiNVHU9u-z7DTdp0f4SOrm3I~Z%w=;R$ zsm|j-t!~(JYu~pu(P^}i^NK#Iu1$cWhJ=ocHo)^xvYmSSNJDI+|Pc0z$f{6{%;k?TqhA7LQHN>GY8tM;b zU76z4VQrWt!$p?6B`#VoPEN2#7E-WxNA_FSv#(KU#!yYo*r=~!>(8>$oH1ECZ+ zW7|#1Z*i27tk9`n%!bmsvcW+44_F-Ppzd4dJ&U}3DDb})+Jf5OI|_5^y2ULkKw(Ao zYfGmiugn=%pyGFB?7Qnt%RSrliTo7i)jj&#XL@YS(IOSD#?wivx2{{a5Ed#W;~niTCDGl;m{0*+-a zx{tVIj@qc>d~SMc(nJj*z9&V)((Q|D4}R(4&ikWZ)AZF6dy}8FhgjpAdNveg0++54 zom-b#0H;YSn7S<>_NBce28(YtO=mr4!0`4VlXgi9dTj@k-$x(Q^s0|#xZKuF&Bom) z&MnNAp|8w!#QBv{l~btZYI@>+1_pbBKow&AzCqS?Cy7a=P(ntB*RS|W2A`}PNJJ^B zhLSTyV77o|9-8@JHz6ui&DFNscA}IGvg4Eow;ApCy}BX)0FmFVYnYlhWL#IAt@~J^ zc}`4QEJS*8pSQ5JEr?-jEg?=CsDzV1Pi--wNee+HnyiWolRycpM|o<(71j@ ztCY5duHU{Qhg)?{Tius3dVp4!68eJZNFV?yN@JLJe%#!y%7@#yn91?bF7NIC0C2l} z-L~ki;%K=8O46jNfQ!b#+;<0?X4P)mQS~iR3&^5$6_Avb0HO#T_}7h{+%Y$abv0bj z+`c;IcW`5CqI$#ITzeubZn?Sa&6hO*okF6W{t-+?8E*!(D#>ijW5>i3e&uE3sCP!K zLhEjtrKKwQ*4zb@SFuY1mBZ*~x^pW+6G>vnHeQ~fLMn?N9+_`LeQdAGN{U}+#+6e# z0x~!`b=Yq@q(|X`LeokG8Qr2;B9;8Tp=%kGx2RO-&{Hmh5|F;>(bt-)-5U=$1Mt;L zyp6OtA8T87sM-6ud$Q+pgqauRA)pzFB&9WIl~RVa6wjtAJ8JteJg*b8q%`U2T1M=& z+m`UNo0qHn+)J%-Y7_?@_9E zePMTOX)_$k_<}yvWAJcOP{66|1GDPDi3fsb=M-t1V@U?BCoakl*M|b+cpwCox^0mb z+E$c+6i7h;W=B!)!ap+;oj^rYsIj;a^_HHW6pL_&8Kn4H8qg?LK~tU|#|Rpx)S*pf zMQc%7QPlPfC|VNby0G&W$zwV<;GTm$j%2J>JGzp*pQiB`*KK9bgi%36rmxB52m&4>Q z*l+H}vKrULH@5!(6YkCFK1IJ|S>3-)jM07QdYVFz&>mJkpit10)5!^uHb?3dE6S6z1eqh zPTOyZBmh(3B{i}U*?3hU0+i=hZ?Cgb0L}HJLDguSm-mNDXe%swm z&+by<-l(CkN%Z)xyY7y{e$3c$KpoRP)jG$Z>rZ0m)vnI>oXlp>qg8kjnW*-CSl;h1 zV`E7=QE?6*2|digj1?txZ-6+nZ|!H`Z%x%6J>KM|m+~8VN*VwIxpKndPL}8b=}m{W&7Hqf{wF6i z`wRCgft@1aLJd5asYs$g_LGR%ufrQath3==ES*H?jV=I!*is~^Zc`-F4c|Xs6!I${ zFF=c;bK$jo!txzK?!g0UloEytl^kxaRj^qTKGwMka^n@EwX~e{f;;>1Y{KmsWTUB~ z%J)}uEh7dXec+-;FMEHDleYPl>$`U+?*N;2+SHdM7hOt{Jk19x4Ekk@i!qX4*3^Q~ zIjnZ?am9p@x9xXXckZcD#p)N1IU3J-v|rq6#d8g;Cea~PTd6JAQ%WFaE7K6NY@53& z+U8WYs@C4@<1=<%b;d_#G+RLh=PTvnXhAoRyaW9-=^ zAU&YyxF0>4o-5ZFq#y&xrsczMO}eQLPJMD@3I#f{RI)k(NvSxOwh=@+K><~-


% zPXzi^sNt>Azjp@RYlt?mWj1-QIVy8U?%Y0NhnS@~TIm!SQxpx&h_r>~>H4~haqYI@ z$X&+VbZ4eMY3s?B>ki4@jBL%JewlK+{JSRVwt({5lCQK<1K_06_;LJ2BAa?jqi>L2VZS-r3x_{#(HhtteHC-5#6g?9Lpqa{lMv z_G>(=+L7}7Xz5qu3vJglHc>^;uSwRVXvsab#!-&$Jl9!MXxLrQKYNns-ND$aOh(&g zLQ963Tr1JfU#Imb8JmP?Irg^L+?(F(9@VyNuihrfSV|gc z2S_KiYd#c#+Dw+h*hG`ugJF zS=h49lF6u#@^J>E-jJrnaLVr2F&Nd(*Ve2U8?C85dRomD0rkJWI66w~yJwGW@CN*_ zOFxZVt?`;|EHmX0T5-mLLYJt6L0V&F?G^_l9_eQ%%OinYY5AAlmqVE_B`OO`fkK5O9-|C%Ht|bn_PkVdl>Y!}{6`uoUl<IwH%}X$IUvH@)XPkQ?t7jE1P?EI=v#RS;yYrNX~%b2ZAYbux|(rB2=R5 zZ9FwaSDJijuTh?30dIS6DWgWA1^zE5Y&x7s1uyYG5b_^5-!9vKXjwmbs6( zglQ_!00{~wKW12l-CK^|7Z8!{tyI5nJ9U%Xw@!u`150((sSkB}Rp*{Iv_H76ws!8e z%XzG~Aiw9Ww5KAn*a2F5$i;6P+&pdL!z)63R+YFr2f47&dBk-zcp)|h{{Y-OedzDI z0pBIHQNX0A00`&_!;Hn1yrO`DoNT?tld*7f+CU2M><&ve#_BPx6rjf~^fNF)iuqE{g5=t&fY$9|Lv{ZP?2WB2yO-j*5+}aUNprJlOwOp<; zQst`Hlrn^kX-hyUM0eA-7W^&KPZF}7;b9>@<8)BKiui3vkiluhsMRE9J@~d%a#@uK z;x$$)kFHua*^egNr%@1}CDD~D;;ty!h(Cf zdF^Vfw%KGl2r`z=icwHP;>@zH#>?oZ|B3|?un59IXwo@z{J@tb;mf-2JT zLuf!!+>WG@YMNC@KSn8=XSnit{{Sg^G$xfQ{{Y+$!9^XI$l^_UMQGI-dVTeOcG-@? z+I*Vp_wBmR+{LxotCJ$B)PhTIOw~8oM5HGh6MZxi;l%>{q`X6WZ+Lx3 z*UV_?cr+nQ_p`FMyDuay0t#Co2)arl9I4@C4d1VhGHMLq)WH^mOA*6oX zEKNdDjiO=izq{JkBW{x7Br&1HD>Rd#U4$GY(t4H8YR1Dj1y`$m?&xMPUwO@7fD^4n zIs5RehefK(VG~AyU2Yl*ZhRLCl(!V5C@7xYL#8R)mrHjKLYmnG3-Iu z@U7@1Ir4XCAg?NqGSvD$(~e%)ho>nceQW6M>Mc0zClRRx&xPg+m4k5U}Y}Ys+p1$ z29UNM0H5&=8&x$V1c4k9+Q=PEaOe~$q|}2Q)b?pt=qCtWpW%?C>_3_mM_?s48DS!% zs+9MfS8-$qtBzQM3VJC@O?8~Rv1RKDokgOY)8Yuvvj#~uv?--FGE2FuGg1e2aK+K7 zIL?6q_+w-weCu^rZ58(5JfxYPoR4l?Segr9`cdfJyCZpG18g~i%U|v3M=)D z9NYR)eqi_^E>?)v#x1W$+z~Hk;DxF9v4g?LYeK1^?==Y(Ak-9-gx?28I-!PQ?P)}q z5nQQC;T0kjhp>_7e=$cX)~6x3MpgJmC9DlvLeKY(lCj>E8n~0@tsqd;<$?ulg!_RR zY-dB3Vy6luoR(4>(UzgyuFN_Kk*b?`*NZoQA=@WiEHPUat5eC3I^z6>m~9U&K43?U zT8>%dC_8Y{;&2KU>Qsks;&v^?)NWnt2GrunY1ShJO?e5e39CQ@mwCejd0t8X0LwWi zZ%7*>Ywi2Q+ZyhZWuYP!lX19PjN7g{KJ_K0j%g|H!&gX92sNn+r8>0`iYA%#z{2`C zC;ngX$n-9j?pB5C&>h+If~Rcm+T_${K-|={e#4DZD$ctG`j;o|2YFk{Mt$n8X<6ni zg#Q3(RGL*atuaQko2ArU8J4hX#ZvbDoAX;mLrY=dFGUd8>e%PC|)fLOV+Y9!2j<`l@TJ5W8Dp$2hIOL(u zr%*Jh=TAasU%d@;Wu3*jwQm-k-pI|Cnv}bArFPTHnvjxz@R)|X?Y2hQ)nN`Eg>J)s zX13MX>6@-2=@lN)`xFzpc6T~)id4PnvM)B6ZTwQ6g!8b#>0g;;D{6=QY(rcOjhXk_ zeT!Jc=CE02{{Y)e$5+6BeTIkXOlCPO71CTSX;<&Ia{ZWU&CcLx@TQ%$8~dd=Sl)Ii zq*7W~P{^fT5G(CCdj@(5IjHG&{mgu-mxHZ4q_Xl~O7jIO2EDzQoNST2R23Rwdw7MZ zQ9XW)97chri(%m)oQ4xC-Dp<9WP&nw1Nq5a! zl%&zeGs~tNxycB|h?P%%3)E7=oYth1)RWy@&KoB*UR5hn2|f5KcjhVhEAekiJHjTR zs>8Z%(whe`l)k`Znqti>I(I!qH7amF)K7QH`R5B`vhuU&y^j&wG@cb*4BERw;VwPd z#6tObk+92(BfP+fg=>2D_dNn4dhR4f=dJxP=i$r zf=zqz;E5w^#U@LZ?b_yr7{JPXWT6Sj)kd@)>=DMOe7=~^g7*EJCHS_Lx`H3FGVFJx zf#Du7bb<$8ei3M*eYb~ATgay7*LBsylIoTUmXw!T=~WK;^(M8?3bEZyPO~i?U?$!3 zT4YR=^L~3xBTavIQt}k1LNHf0rAcFeEe#w>`Oyhc6xE;cV}*IIvsFU?Dc|kDq7sKn zL8V1I#uw?PsUqdR7DXj&2}u+cuU}>gMNtcaFbSmyrWFyS`q8u}Rzs-5>}^pV^syPN z)7y!NfTD|)$;$>gB_Se}`)P-^(Nw5wx#+J4rv1MBc@i&SNkS0IfyEjEk*2lG_v298 zF!^Jxa=&--ZMY&IN`$wGV*4k4-DJ)}C!$&vY=)uYkW}eHss1Px#eXV%!so3=Ys78i zhA=eg)j_*98FhQJSrs}5>&9_XgNTsSMnFMGvLdZ`6A zYo)GCXIph}=i8la*s|JEn_EvZu;Ls-g$Pnq0R(g-5mRFeg$lPtn1*&~z=d-8_mr$0 z=jl@Ixi8m<(7B4qbhSm@mL(|(lXgRYc8Tafrl6tXT{;kP8+v-+EhA8~J%O|&#cp#e zPw-w1KVsy!$gR57vzhkdkn3$%T( zfQ#D;nYR>bLT5+uX%+r4i0mcDmouSv0-!gAtD|!+o5;|p&G>HH<-Za3%TC5wL#RDM zl4x;0!ADU_GEn%#hcuxaDMZ5n$RA&D0K=3>V^ZL9WZ9Rq-8WHbR^ILnZtv@AZk|U?wfJN3TR4>uIeWUOnus= zVF0`)e-&GmcI91=(nhUU7F;8=ge#rb4vp?Cb2pVyZ4;z@Xkh^#(Qu<$rDd0%tB8Z7tb4zz5A5 zfB=rnS8sE_FDY#*wQZ-_^Odp}22P%$boC?{9!QJ8RT-(A-xTR+eMQi8gwIVz#seB}zSnr=}$>vvJ7&k;JM_YqMAOaJo`K zYk2CWkH%3Uw4rfQu}t*t-H9@At^OPNh}mCIrP?ykJXA)yJMqP6}vNYK1R}3`xLvicb@mL?JdQz z9K4n7UCgGRn`Sgx5had}pr3@1Kxx*PaSJ4clqE+x#@b1QfwUoC&&uJpZM?zCdzs<) zdtYQ;TtnFkdCF`G`}Ej7{sw*_`_f&FSX#W7S`)^YlOVw5BEascG z-HNwqO8IvAFKMkmr(9&6-F0P+o~4LMGs14ix%Io zidKR}XtJK@ZVS6PT+rg$s6%b;i)D`S8ufODoxqsIYBtv&aS3^J%Tm!*QnhAI2U^^qKDSXh)N#eOrGBkBER#H(~jX-zfs)K;$zGc0=W8De$zxhvdJHp&y#M5-a z+19nlxZ5Qt5V<;JI0*3?X-keM*S{5gy|t{Hl$P)~d51!;yZ3!%4ieWJBS?jUp+wf3 zCI0^V5v-OAh3Z6#c`ZYME>T`|{{T?hvi9Q3klYql-yDq}NK_nF+R0ux2%&qc;M4Zf zie-iAn|ntHxpFPq(ZrSoC(U+!`f?bU5P#y~G8}bkJ&B;j4*R*$JKeO6(zy5yJ*!j2 z_AcR{#mGv?BOGdV_Hg>s&OQ6m_Zl?9EZa_ZIqqnuP`+H>devgrOtiyf6}o}k=UhWg zg4~%yS!58#c@16dS6f}>val`x0JenOOh0+tKTn{h?)LQo%Y373m9iMP{tvR4jUo0n z6_tjdTiaqf3#%Sfa0-x-oe4Ow;rn%o#yn2p)gjZV+Lck)fsMFs)Kce=B6T{po(rpV*o4 zb{^o7WIKfh60IoIrzvrUA4nZZWUpBIv2dR?7@x@?*T$MGxtnTVmGkN;;mtKa50LVl z3@I)+iewg$d%I2@K0wtC&4|Mx2LdPPpoJp;4Pm zMNvn_7q;fma6 zuF3gg;{~?-w7i7JjNYdZJ`z=4{_J5JKeTuDnFifYa$VAEO?6#|-CxYUBQJyeI?l*O z=L%O!jW~)b6Mo|xRilO2r+(It}p>KW2uC&UM^1>3eIIsdz zD^gEEilyHCu#6fw5{>&=&Ou>31Ka9@3g8M(ZX^6nHcH94I?6#yK~Mm@goFJZM0ws2_#R=x?Z zZ0xc3SB{41h5b!EG_Hhtkoz*%d)a%2={s-Ce#0u!A=V$5dATw5-FvO~{%X|2ifFNIWRxS!2%)QoFb`3R{1-2}yK`RJvAorm z^39n1YaYi^SMj2gf)?3Z{{S*i4+YV`mHySib1Sn~{{YH|QC?*<{X}o;*QmZ%n{tk#t+xo6yHXLY z8i<^V>DZ?ZXXqW6kl2{EVC+SIm~|?*-b}2!%Qj0jg{F=0qBpiSF5lSjmvp&7j{U_* zqv5{Sq#n~v$6<=$Ih6tdffs0qjdPfw#2+ldP_V4(3@Rh6S8Xmp09 zPyA!YFfS4#Zb?x-Ue6Ig+gVnAdx=#6&!|l?N!&T-F}_$jt%qQ2rb_QU<3Yh$R~A84 z4nwK0OlCyY%Xy3eqDppAN~nQR--1yOn38TYg9$5gt4QhxT=K)X<8zG$r8c^_V;j`t zKoxJ~esZ)}MB98flH>7`OsN4$32Q1+r?(oW+1N9<2f1@+aJ$KA2{6QK=~UBxZ@V=2 zY_c}wS6sPWBJ13@2OQRwx^o^_sQ`9kUc%v@5F$^Za+dGhY?5GMB$LHPc6l=7?ps@r zFH}T6(&kq*CA2IPTKfhjuC0S%A){3Vhr4mQ22<{j6zk-7;j4YKyEf~IOP2*AI$$*4 zfVxwl1f&C6<?i6a| zhTq-yy|H$Uda_(xx7?x=o6$|G5hUU1lo6L#SDph_5 zB3^bi{{WHLpBBWgmDIURyPEUmFYLJZKb%uSf`;W&fK!XZ*Fb&tdJ5q zAt`K{3W7=TD4z3-cQKHXcONm;U7NGE#l3_B$Y5X!M7CeIswcF5O|OwF9&d>Q!CDj7 zJXEf2BBuqQ*j`-q9aTQ&uqM*tOYtROUR2bU!wXha#3ywRTc=zSrYNJUT zzwj1+xjd+n+)9!rkB9;&ab|}Z8{Jn5fU|4e%RRd~VU(i&e&DpVWgOtp7I_B9$Yf?NiZ?yZ^eIl&=;PEj<3b&cuphLv z-~|rOafraA`xLTA)r$Q6;KRfF^&`Putv@MSrtozL`a$t2UH_m-1t}V-=%M(wVoPNSym(kr*$JWlUODg(Xpz%i_ALmX-E) z;#hZ24LoEQ&=p&s^1-_7?wau(NHr8u?*7`_{@uE#N#r>5(Yn@_ktj}6jy@V~0S$mw zPka6<@YDayRWJgd+wtS1n`zpz04A^$7`+DgaC{wLG%JJUh@poNz(iS{;6+UghSbm`G5#(8C?gsZ1XOK`) zErdF{C}}=3LTid;=WH<#cUW&p9t%;&eq#4rG5IflnA74D%Gb->g?`)eBfrHBUtqaZ znN3^WZ%J1|l_x5UL)~rb7$nwO5U$wX;^(gIO>KIC^dT%51+eakj;>#^~MZQ+FJ5M&I8dHeJS0rK2V%1puMh z*A)H5wV!S}*cQ9n&g|Lb{{V$46$;d%)iNc4fnxsu02V%_vVl1_&ve<1I;(f2g#Ok0Do zW-~(aYNVxA70VMi%o#BU5n8Iw;@)B_qZoA*MRVV`(sZKk>XMU2#-&gXrxqA6jHd-g z%I_K8?Hq|UzBf5Yd8?Ulo~Tx4ojY*hxTwM4qNd#!T|88MbKDnoOma+shnDdr(m>3d zPelVjS^dsZQtFjDaYg>|F5fMZ(j0`4tyBPMBx0Ai;Q`=}6|(FMi{yZZB}CpA9q;+M zY-;$r(AU|9g4?O8ZTDRdQfArQ=~)rpT3S-6rAj#;uMih@^g0D%#pSi3Dp-c|&a^{s zRStAO&k)UbNugIr-X7Y8PW?{CzS?a}sdfoFj>C0YTa?^zl4Gc^sjo`*9BqBMxq`KG zs8=8UVR6@e9_u=qex*{j7c%e81Kb1UBR@Z7zQ6ikYCZr$+jpr+U%zwb#f~g?Ee6HGP`p4biA1Bs8?gu#28$` zwQQWrvjLp87}I%<+Puf(w?XkBRE;Vb(-}_1xfq*=T1};R71aIaiQAsVnM+@KgHSmt z3v|4*02Dy$zjTDeX>FG7Or)pJJrsoJL;A6_+rZ%NvB7diO8d-@%W$KHiOID%y8gQ~ z9qSr9H~qUYZX#Pu(bY$BwP{-~r!6WV)O#@tk=*U`e50d+wQc_1N%G$_qnK5X^x4Rp z57X}@M}Ktu2 z`gV+CJ*3XvU^A>_wQF2xYx;6sXZfhvc-`N$-F0V2$r$Qs9BMyr1L!4u)NDQRxcIw! z?XE%EQod6AwWJ9U(yE5uNl9(iTpg%!3Mo+b*Qgl!54iAV~@xq&)yplsVH*Gd~5{16H01kNA{>t0;aX0G!=cN zYk6mGZKn5xe+#@v_$yBL$ywwsMbscP$?*>Z!z4p>*BOkZHh_gFnsxv%4Rsu?TWU88fugGRi*)CvGQW^}Q7I&O zPE>uEaQR!}wK>{LRqo)qW8%C>ki?j^>`Dw2V{p@9K!DjiSd3rk_gbC+2kt z+qdP5MzC!-wr!Eu1a8+ZEzyN7@(1LxChRq$^M;K_;DQ z20BDVK)L=GR3|pjQw!Z&VhzSJq}rz3Aji8jh51V{BuYasX<5l6Aw+_DVL2Y=#NyFY zM3wUoO;qvf?}zy7gL%(z@%I*P)o(qW8Wp~58HJ0C*>yks2hLpNqc@YMkhR zB<3q#{{U`1)Dm4G0<`vwyUqfXN-}|J+F5NhN_5w+TmoK!cp#8S%nrEn^g~4>abc$9 zeHw~G@m@(CgwrfcVns<==KVBdPk{uBu?hVXSi zqZ;(y{x42JaxdBJje)c&mNP${2&GPoT_uC2Rf{B7_#yq}RNIOm-Y{R0Z%pIm1hVsCYfb~E^V0q0EksfRpGv$N z$?K4wN#ICQ=gXjxez!9s$8iU>UVWsnN$)2WT%OFvT>MM``WDHwzc72F7Dtmip3%wP z@b(&ysaH+m`6_H3?W%7I@@?a1O>(83zqq0%=?GIjDUl5^-%oW{uNj_SwAS<{87b>s z8MePV*K8q|*1bdTGNAhnN3aD)c{}ov_1DFJGbYioV{!$lnyE7O-O-6oskG-+N0;)? zB>w>dUeb0D$7eHXYGP^$@0G2}&k0-Q>A6Tt^rAIdnua~&edhMJ40BYQJ z+~liDt@mhBps!MU6Z1{CU!u5ZU4LxqJ!on~V#&f7>ik`+#M2h38+bk`ZHIIXZ96uHMNrgJUFSfW!g6koKqe`YM$omi%ypXJ-6n?1sM+YwN}DnY194QOdj+-M>GD~Tn8FmbNsC7wohU6rx zKnbFNp&lIT*RBZGXq283+9hjcv57F}TrM+H1*Z_}Y&siLsq+eaz?uRyj>?{xNJyI( zYegK$nB-)#lsQx)gr=Vnt$v(1>(M@BA>2{hyGldN%C|UF+s%g+r07#>RYH^Bmu3@{ z64m0AC^wvOyDN6d3o1{VU6SgEQj}IaAlI`389MM^J;&fX+-Pl;`DLQ3$n<@R)d&l-JNbU`@D{oCy zVDfG-l~WSv*Y-~$xhQvgjybO99_Zbsp?AfeQIj{o_#`BxD@uxV{{Tk}ONiv32?8A> zFfg(somIJRy$vvw3#5gHWgAdrg zn`G=HAO#_jrK;3+iXUb=rqKTY40@Nvi~Os+#~(>W9@`A9wKA6JQ9@}3j@mHBQ8d`G4qwlGB$m6qg~OdJZWTZ@PjB&qZYIHD*AzYV9yf-1Ir zKXA)E)4@VJn~N??I+s%!YD*_2S0VLczb%pKT7DCK!sgPWRK(rfex|MAPovD8AOq#0 zG}4eLGZ52-O;1PR@>gokX;CQgQx-&3Tt0waZ%nb!-O|E3fl*E4 zX(^&omdZN!(-phCMHTxQxdXYT9J@-{08=5+RK;#_oN(Pa|s6}E9J84)zF?{O}x2lg(~fC-qwbKEW6e-bE73c z0v>RE>wfUAqZc+x*}!<_vz#1{h&l&xX;E7_5_?B(9mnpe2?bHic_g+@L=)1v;7}B4 zDJ%F*J-BcHqA4j!1tkop$p}+mg*Y=Rsjhf1)f9z=q$KpNp4=-MOpR56^!@3lll@8V z#;kZzlH7b;p}D%ZPG>h|US-z-at@nEDlg^+|4EjovUj-3u6KQ3RI z9CrTzjowR39^N3Sj{5#wFE(=jD=uF$$CtMMVY?c%n#zVYHoD zjJxn|gjTkQU_r0dk4A*K4wl6LP{?#WxKsp-iV!6v3U+ij@-0w_>7QQHfucqHP^myQ z9S?pLZ77p-jdey&SSwppEp-rxtq*n_1ljGP6|_=>WGkKlt>lP;xG&1L++7F#QmOS* z4KfeujHPkx>XZa`(?lmG^6uNximECkJd=^GmdZ!z#CFb)uJjdM@6G3vw`3kPA6o0Q zcIAMOThw}EdVHjX^Kw_m;+7g>ajuqq;=arYB&g{ni^WrpQQ*!)+}pD4xFAAp=(9>Z zl$X*L0umF^Lli6g4a~DS?HU!*I~%$K7}%?2?Us7IdjdRVX}N+2A@fsw*xk;1?nJ z$>P3I-H3I1*4(uqDEB1^7|Zhxti^pi?Q}tyXvd*ONo*GCp3mEeu9B{ZW?Bnv_Yx`^ zNh&_xm~SzlPOVO=`T8D^y}PwNQ0_FZ#x{72-}FoGuc;mM#3PMLjNJ0lcDL2~)OWBR zq_$L};tNd_dudMWSuGt@QrdKndgVCU{sknD-Gz^ z*N*h`HGADKE!%ya;l%ujCM=Epwn9?f&8ZQYF_SXf`BD%a4bUm4MupILkU=J~y0&x~ zBG;WB-U`FD_a)9eIjq&LszTg-5tRaHT5p zCc|_G8d#vriV>J3Vx4=8nUVQU;1sSR{gxL-;0l*150WHt-yq#g{oew8reQ)_X?E8T zrh!dpr8clL&#>W4Gp_AhNc9S@zfKo6{;QB$?+~F_+yxfs^Mh-0g_+MXraTL>+_y;T zXp&c{75Z^*8`1DId9j~ZSS9afA-a%@ONP_H=%X$1#+&D2Yd5=cWyGn`bb_?ZhOcvx z%7FT5i^dyq+TJu4njcP!4c*Byx@=zBY{YK$zQ*Kcd7&0l^B z-y3?z!TY509fkYO*tp*#!kZNy5~%M*Q)*X>s00eo*BDmv#!GRRX|`|=0F~UmwB1lz z?8l6hZZWvhlt-m)pHEwFiupTsZJmbJ`F7f-7iWIqE$=axWKk()MF43^N~%cAj1`v| znes8iB&EEKIizkacEy(3lJ?dB_YFr^kA|w1?oMRfx0P*I6|y$o-a9de<85z~wx&UZ z)P)7CB~weFl@U^D*BeeVV_U!+9V47xYIR(NxqG#czjFzRjPk`BL0S)5C${}T^M2Xf z_Iu&qbLH6W@>_Q0wGgJG#FZEtILSi@Kow1C+-cc~46kl6cUH~;gUqIfom5ud?@ntU zc-NQKI_laE9Qjx4L-UQbPRxbLcB`xRy@SXuxVs&})h%v9mY);0`5{F`4z_Eg6XH%| z47L^QEC$id4Vpeak>V8Jbz`}En~-D6?dIX=J_p0}3c5V%QnRHM1k|dB8dGCfVY#WO zO8YwJHqUk3S6ES=3WX_g@K@kI8quQd09wd~0!WYRs2 zF85Cb1f?1PNX~!?*REKxyMl}AuB$o%iSO+=8ZTCgNP}uEafSAC2=iV1atmr*LbQcK zc~r|0zHt|UpXI8SZMb!Tkn(jC0Sw8>N^MH^P>e`Epw&Qix010;CDeLXLNu0x`{}@r zqfkwt9A_|}{{Xnp{{W`7+vn3|v(qV1frF(b#}W>zR34(9+-L`;ixv{4yHXiMgcnT{ zOo{fIVY9&Ei6hMuyYOgbN^#cGty!e>{TM^cUT7sIIT4WJ+NDt0Iz>-Ig=m{~6iv%i zHP^!872cJQ57&g<5Gdkj1eW!Y6;VU(0(%Ju3C6^zxIPGrmRHtFTUk-9#R!+uhP27s@Ggc<;o4~27T+P%BG#EMM5D@05b0ZBk`jM+w+!XAmEQ^JPwj44 z-a4kD-;9qrBFfDaPn0z*lnUf4Tia8NwS%_=7<>g>W(xy=;|%;^S>DPhRldNlbF%Tk3S#vs*lxe6BLTMdF9LvD_4|-sVE5xr3#N`32}drx`(DVbd%z&IGhF|#&0@l z1G+T;b5JKM?whA9@9TyA_C#xYB@uSjm9&t=7!3FuX{0A1UZ#UGad5%dvm0g7IcN1=2h(l-KFwk6IVAn%J)GWQG3DI0d(DFI)HrSydS!jPbT@PpyX zpd54m0CM-N_kY5G`+)0CYrne(*=t-|erD~V(zt0}N%88Nkm2o(wLf5O-Ns^EE+|rG zMr|a$NJNJ(SN4`(?LZaW|bPs0k0uM)!ik)Y-yluV>V*K5M^b_w3J1XAhtrOV%3z?rw=m7{9z2&5ZT-n}iEJjw| z*_V@o(Q)qs<6U%5ZZExWzAuxvxvt4>X-(OuJl&FPoeF8`sbr;TN-7jWQnTV2SGKr| z&)C~#8tW$-)M~6dGcA$Zg}GJ_d-rOPcW(LJoQbMHN*v6{MW89e0YgbmCIbUOB0|4Atvx%t7bNJh>?YkC^^o-8Nf2!eZgtTyZM9 z&P5BYL}%|EQgzg7(kL+xosrQ(dOjn;UG_zzZZ3zL@C`~*-Zz^T>EsD<_GCwuXj9Ea zut#-e$uCfb8`MTxlzWC7S-tj{^DV(0KvdB;$n@p&&9}4!g5UsM5#}e)3-HFw^!2wL zOndI*X*Ui^nuDm99SJLJs8Eol0H5(w?Z-cDJ5!U&KK@-=54yX@xfv$lurT4R^dx)q zRd(Ax+qPpSHI_AQD<9tO&e2OGictX8gBmUUyXJLVbXeXN@*7yG7@-#TYbQ5!)y+*KVxG-WuwpjqM|7IV{=iRwi^b z5|rHADgYFU9D$+toK>;0Tn%38)oPikEJ^8wqeQF6n-Q(cIc=ejCeH|@-DZKB>z|gN zX{LV6HP7a}hIH3br#5cVHM5kTCV9O92Rh>1X7w$=f<{D| z4D!dVEMGII(2%~&M{Lv7XsDkx`h>f__0wVO#$8ObxTYswlA;ulpNKX`Pw}zPn-^k1 z6GYPSTw~q-?%dAjTH6`{#HvqudBD?}#$;uoLb`p}3#b|eQLJx>G^JIH;|>YreqvHzu-nGj+Q}&o?-^l8 zWTbWSk&>NP__=#=n(htpX<`Tmss0Oe+1;hc=7=P8N8(lUuyOwY3-E2a5?qXTrtpK~ z{CZjteO3GG8O69t|@s7Pc6%7!CkgUw9!Vsp?x^neMDKd^m)6APTafghhKKnJ%l0V zRp+HCS{-R*R2?FU1QClq$ltHS%T@}5#bsUU?7hD1w*l_yb2ST$T&u)){wm-1U5kFa zGUSVJX~a*6RJPq=^y+O?RSHx`RGx<&m$mm?c^ff18%p@El)LMy_30H=Yx{YO9RbFk6;ZULnH3SGjF_a z%e8XroQGvFA~De(spH1ht3JeTR86sJO`u3k9C4%%HDwt9R&yU-80KYt*SzypV%j*X zd{8Zkfwf8dv8%JIlen+blEf^)Lgb-Nkd6^ zUg(=Y`C1^HEy13-fWKeugCmZxMqKnrm|goHMgR+)C3OI>fKT3d!jDng`V`!wv8;hyBs zIH|?IOS}x9DaY(ncS4w{thvvn3O%($B><0RDEA%2$`L?l3e@d81-BXliz-5!_Kk~X z>{zL{Z5_1>YQ{*Xmlc#DO7^K=5kB*ZrT$LltrZYDb$~a zy_l23?Hf5{F}>UsTX6SFDFgy&cMb~G-u9Ov@17NxYuk3HJ5B5KROz;PX^y3} zN`}sn;3@;OV?Vdd+S}y=-s0d2*!LdDy2xCmf@we@l=@N zm^=^-LcU&~`O@#V-ifw1JV(`Qnxu%*6pcZhS$A~CExmU891M&;@Li?ZUgS#wZN0-i z)4ua?nSL=H1`WSbuGbgHWg7)2*~2`i_Nu`^jMDP*^=Dke9i$PUpQ^wmGfdITn005ki8LWG%IqpKq1vb}cHWT7s%%RjpwQD$&+En78QS2o`^;30bt;jh zZP|*UOub6ZiO)Pa^IPDepM{W`)i`VuFWX%XnCoAZ@AilWf|Ty<#BM?u<}_1Sd9G}m z#VtnMR}!G77C=>UReMc)aUA?}0nu4u%gjwmeA~AKcViStDiQ&!Y67CZ%3>bk&WD1q zTVyn;D!}pO*~g>m`OiHGKiO9>4k_7afLhMfzUpqJEb^7UUE^xpVY0m}$wEV}Ql%ge zN`*5A3vGL*!*U5KFS$5pU06AxbFXr7PWjw+OY2hB)cH_#E&_qn;@A}+5_@vS^@!b2 zK|6d3;4Sgmd+WvF_ zajIta%o$r>GBN~%P(tJlo!nd9uaGv^Ai4({7L@Mf-YK+|qm=l;mCob)>GPkrpaSiw zOB#!62tNSqJ)JP$ZEkZA`CFq&8_&g0Z~oj1gZP#?y-+_=2PHdtSlmtQ`~8)R<1UTA zyPPqQfi!W?9iMz8Ay!-7{Kg>63PAeqo;vd0&p+o#p z%t+W`@3!uVkgdD9(d6A(VO}=fBT8RFRZ&#-_l#P%J`_sPFdp-DS)Rb&GFrUaN&LAq z^%SqmJU__%SYXTEFbmsfb09xqys+n=73SSfnw?4?1DZ`rO+A&v4XwKDG7jjEN4G*0 zuW6dZyHzYg7){CyN=nrypp?Y&Gn%{p-CB}jEErN-YY$CeE^mtR=6uRS7BV^Zk+_;j$xvYXU)2{?HIVf zsXb4czjuBmqm(9pgf$>Lcf5i~yhv>+%7NAX+!`_zgD~w0JnkFv778ztkgmmf%dhFe zy-9XpShfCV+{pxu$uT8Bf;{zf?l>VKN!wxA(^?`a-FuKIAIGJjjKL0^H6R`;Rj^S$ zHt#9&TsiQ=8~*IjN|FjwEzKnr=|V`S(T3MhM)=60n8Llxlo8^)f$JeEzJ62ItK_OU zu&WG!@#1{{eYH`SzsT=*+=8sDH9dvs3yl*}LtbSJZi-#osR9x$_tg#K!beX(| z2G?#)H8}LBA>_6_s$02x@lfsy+iST)-Or_J9learw;a*w`%3t*ZHHpD7Cg3B5@c4J zZfr*qP~&eloeM(JpbAu#0th`0I(et5pz>VT3wi(?#)xfNN>{5%9WbM4o*EHEO)WOi zA(VuLl1f0Jsq2I!by88l)(sY}08pSsYukqNd?h=!_`-=9`@4e3>8yf&k}+4b6qQ~k z))erLZB6;&OjjOtNd_ySWhp|tYCCahW7R@<&sK#LlDOIYvqhp(D^pslsUr&ci8jJQ zDSl0$w`hLU8gRa4PD z_zEu+32YLYb;~j^H7QtXu~Ma^o|)4HM=L8NPcrq#H`N}R7r${9CVN0R!l`FU3)o9b`NFalxkKOC_ zVHGC+2+9_eiYVjBH#JHeRG_WIBRv(%i*7dIR7RDdN+48ySP4lX0Te36Dpk5VnsPm+ zHMixe3SJ@ASK3vECL)y+6$bL|V2>8%J!2BMvqMt0EA zF9eTrf+Pk)^hJ^9wwjH4NT?k?E|_crmSaU1S3M^(Z~mP5Cd8l@^OoD(WgzEWQFV-L z7#De%*AE57d*S{@?Tq-4xaVqf%W0(M6rrEcF^x3Q#dc-Ls&FWUrDIP~;x+bR>zsOM zqIBvBt7E)R+56XS?%Oy@ZkuA|7Ja=7NmFWiBk7QUpppp{NUaY{LfbsYFmU8mhb3tp z-IL&d!uFP*Q50QMPv*1C?u9!)k2c%SxJs4fej>_2Ih?%Jew=HN_Xh2)r2^u+zsY9W zl2YLL1y7*}e>IL<>Hf=h>F!sZa{2`zhOh)syN*BK~XquENUP;%&V6 z{tubCBkk0zc=rnI@n}GQC0lcp(hq0tP6*$d&my!az7Vjxr2GQwazOt8$($nX`*MGD zBM-?|2!H#DJ%je8SRb4hAxW0RC2&QCD3YL$niq?b^lHc*C6Qlsa1n0VkLC zYs`|hxHk4YH>5<$f6Cw3o?BzqpQ??VTSb?gFY0NfB%z728oMJZWPcTIL8e_RgdcSF z4HJ8w++l79zY7h06#FQwMW^#s=I5ZBH!AEMrOeg`0ZXv%cCiulq~+pUVfM53GBDNm z4RQCtKDA$;v9=WswvtF4;@5tyKGe^;@~`E~%#K%+JuTfhdg&;So7_7xQ${;v{7X|T zhIP}s$T%&|KOZpnnNL!AV}Rb6zx_Snouhz|ezRJ8Qx4(ipU|%%qs-yoT1;(wjG|0U zxxC@A4d8R|Yf@pTA8wO~8FnpCZm-IF)D^xu z?t30Zhm-A!@kO{;@9`X4h&>2IfX`9T1sp_6+N;*<84ezTr|iEpScZ_>w!Q~*4q>OX zSL{`Hb^2HJHO58H#yIx9jz@7Um^T&?ay zBO;`oLcd5DcNXZU#M&lL+co}Ms+QyKdlcZ49%$5mabrzBoJvQRf&4h8Q(>+nI)y52 zE$E%Uw>cWjX(~4PvvylW9SM$Wida58W6Mn`X~WRZNQydg7Me{mxw*} zaG%$NZ%4eXxc5>P`jTR!flR6OoF(NaK58uE3aJG(7%7ae2@qc_w$ztfB@QQ|l4wC4 zGZ;6NoOz&%6!Nrh+wHeQGL$ZH5s+!@)CaMBaPHqK`1IryUDg&EML?lef79owJ*~xC z`WETE+9Vr$tUnUu%Sl74OHwIU8`MQql8`_>MlaYs-xMzna9EFYdrNhgv2-ywaj&s< zOJ?&g9rE_yQ=2z;v3id1CCF)6W^8>%v~n|)T24o^8qPaAZFVZds`>f4dsUdiO-Yzb zM=_{HcOx(i$nS)S?ONF_{JP43CpWD_?v7lgjiX9GOp4R zL9GHw2|3UNrC6@yFRlRUo&H;VIjvJ{;OrXV9gGgRlkX8mkK64ErEWh{Tw$=}I~#_! zD7#)8)Svc=f7d2LoFn^In{<^9@$ipkGuz$mZEjD?x2fNFt@9h&Y?bg1zn7vv>Bm4l zM2$YxQum)I@+T+Ga(?jKQg35XQw+MT5t^=FcFHuNBiu1ayv<%-e=)?ZmkqS>xVBOT z2A>{h)=I4j#UXj(Uo=5dKBP}Yq%Z=#jJShY1JUyZ7xP%%(OMbbNe?(LF_)< zF_zE`RIRPxB8Q4gC_eMuhGn{^eq!=dQQR;sM-Cf;(Qwepgu5BuCQ+ffsE-&&{l;_OAYW%RXu zRW9EclC_Tk%}1?C6~{uwaNyc6j`vZ5N%W@0#@x!*ODIhd+0dV>60uzpsh6JXlWy+~ zDuA>pu#YuKNhhu*8tR1~Z!4K4Q!+-_Vnb;t16^}L?!vA$Qqbod!>VuIcAzko8Bvm@ zII8)S1dhyj#ibOsH$VJNs_;<#&yHJ=9(=GBkr|5RT4jbt(P)-C$g+*9gZEbu1*PL2 zF!NR_(nvXX(-D_eHCl!TxRfKP=&P>f;+aufaWa?)TD}uZ@dceqi=Enf@1L!1Rv4Sw5CL|UlbQazZY-(tE}(S^^pR*h79A`k^h zQhY<8#b#JFSIb+9gp%q*bpbhc3`5DGqLcFxMTI1cst;}xd#aUbsx2kIIy;CXGxlOS z^4L9u6)%psEOk`t#7;ZDxa)NBw33&v8%JH&Y=fHB9=cy`HP5!Wcj30%o|TO8RUO@G z>ubi`H{pL378X>?g{K=SP_0sG2q5$$7TQpyY92I00bgM4IC2wmgh(KD%ew-B9B(Ze zMM3It1r(!Tl+wBPo|q6yDMP6~YDSe(y$Hjgfpll`@AZ>s^oz)nd9W3rbWUWJ`^{@>5XwjUP{sdOG6CQB*^ zX|yR>QCarmUu(kRyPcC56(p&4!F;&Qh}5T;1x{9)YllFUp5?J{#UW@s66?2jlR&88 z5%0m=>IE3gXz@U*?(2XQ$4WwasAh27nx%#?4r#w}-LfsuhS2~lEBDDzsrOR_F6mP? zn{|@5rO1Kg%Y$mtZLQP0S;xHMpN)|b{p1D5mnJ}n`o*P}6p%_sn0u?9LkZlzjWklY zj^9Tt?e~o;xm~`lvTwV-{{VP=1i5jP>#A&K5>&30_G2Z;NKA&S=w8;dY!(b)&|S#>jO?xyM;b zUSum$qseZ%UtxU|+QA3dXhcQ)Es#*FFlci)FRcD3<@mg##T-k>*T^kGlgL&JZQA7) zK267!Dmql^Qk5v3DP3uwOjqNua31Q#vl$rKa_FDPr&Z@`so=YeWtZW+lDP5{;zE)~ zVHL!kt;nxTfl)J?dSJsW4kUUMG{)w>Z8=YWFWjEDYEp!T(z04tex}^257IGdgAs2# zcu`_m4c~ue@;Zqg1rQsjZOy#HyRbIw+p5!W3?37|CMzH7ofz474E2Xh1wY08}1yLrZf`t2VBEmyzRc7QL zGoWi!@8j+I=O5f|8()7}SGjn*cQzQ^+enkA3<79#oqq_g*n!D?d!FWP_**&k5}ozC2}Uy{!K-??PqWCUOe3@S=k%e zBqs71s2+XIaGGiXKiOi-C>vAxM#%Q);vOo-FsZvI86xZwv$#n(cpbQ zZ?=W_!+h?ItoI;0WVtRI5;n!n7be3@EI7*6(`x?F1cOBy5krKxhTn$iv4`?_DsQ(t zo@+lp%cTrvs<*hrLI;wyC<@RKPj)T|&3+g<~SHzFG) z4li(9s<3I0g7rosoX`Eizw(u8kN*JV?yuh;sYgM{Ar&r>PRjnwOS$B#X=`e%lz{Q{ zowX`L%+2{Yd51CsHi7$bnpzIM(Ba`9>kDVg9-WU4oMt~`zhZ|F--F9=b_{6G8mukN z(yMPRHOs^9!X@afB{`xg#WLA(TB+0M%Lu$wr?DLoY49Yt*?ut!RcfJH933L|(Keiy zIx31>N1l=K3g$3wf{M}#IMkM$@qkcIP-~a724W!;Succ|IE`R-k8TvINIWg5!t)DK zM|CjS3rSuv<1F}ALc7#IW*2dyS+f)yu+)^VLnw5Vs3a{k6(WGw*^haZPNI^9G9Iy9 z+DTE07CX{Gt_|jeG}*#Mb`+M?;*6wMs3W%s8AFq?C_#o+2nuaNokb1=5WHnZQdFLR ziuK6j(tF7yyAn=FZRRzgbSA&22nQk;r({Q?xCwOy0UZxfgeGlJwbf5MYfv3Q(Ek99 zG@?)H!*|A{ZKVV!Djbg52BiW=Lrn9-YAXa6O>U`1C9ZbndNq@8+n&1I?h4To-yNm8 z^NOWtl%NuQ_+xdJhRLP!ngFLUn9H1`f*2ZHNmhG`xF$axZe||dcK-mJ-p!>8fwxwK zt*C2Q))wjLrKEHn1~RSVy$$p~z&_z!ciLXkLyNf*PAe$-mDb$X?Yp$?4Bqb!rR%Da zS1qxgNlkQs4LdT%SB=NXaQ>0ObyjmdZH%`vyW*ySa@Uk@cP-(&r|yYR)XML!g+^#L zvM8SdQj!LJDVJ_KD{Z>k&e$-x3NM`9#W&e(gQYBSE;=ZGcWu4NY;w09yKuLq#CeHv z_%c~RKpz&T5Rr>kGjUkV{)psjo#9ZjZNaeewviboh3sdEX#?z0o6HocB_lKO1B(+R zR3^nkRO;AUBJs)GfwFklZnhk~?^}d>bTUl?X>^r3={?Ofm8|=*UESM;JYG6Wm;UJb z)`8o7lQ!eeL17p^`-M+{8k6h>o}Cra<-Lo1iMM*U8t{Jg}ij^xnhnN`N3TNADwrxFyeB2JlQ{ zsL8Y+?VNAuG_EMOa+|Br(eSs5-0Uq`i@~}_e=<%AW%OgQt+zf(bC5SHLA|-faooII zUr+(1$v`QNm1_QxDyQ^uN$&nt=H0e&pL#<@rG8d+bS$}-FK{T>MMZp7vf|z{w)%nG z8{?F@9#rFJy1L>v<1wA`R(w=Kfgt4CXaMab*BKsvb<2o_me5c+yjNRx-?WhKxn;;- z6VUVS`jlbK9-oI87*v92f{qG--ag6x z07RDLx;G$pKGB%{yYY*Zm0PWifmKik935zoX_3O&yx1~%Y}HD3?_{`J`SW|KOWRhH zxvueSHdd&)1nPZ5`!>f_D2~7}Q^;M5c#ECIYukSa>+68Vd@Ai&EY|(EYKv%q*bT-M zdHMjSS4`Gv)RT;c_U+TZsmrM`7|87*5x0^$>Xzabis&>nReEKHY<&uNV^J3PlMiQD zZdd;RJ)<$P_f9AzEyWcN4MudulM`&~m-Lu?&;!DE}-afqx8(wY;&bI$pFyGn9$<2-H5O>t|KH3c}m zX0$3IxafOJQ5YVD!r9ET=8&Hhg|#--$8y|7s>1=>)!tbNVl!Z=WRp*zcawr-a`9TT zszX4U?cJ|=k(bMXrNwk#hy5w-y_h3zo>*}g%ezTSk#?Y=hQhuf=PIMjQ11AB_~-r8 z+#8FcSplP0W_Itfc6jh7yd6*Qh+2U2V za=Cz}rf$X6QKPHjA(W9tsDBa2dvUC6{?7tLw>l36%YD@R&z1*Uf4R29Q105mgW~DIqTusLUbcok0OEwiYq6-?E)lt-VNjRU! z&Lw49vz^6g_@p7?g<9`SEJ$~0-K0jjMqY_)`RJhuTj)?g(W;`L9;34vp8JjFQKpNh zyA1e+i8UQcR^4|^^2UbkcPAsXq4$oVw$v5Zy(bV@=&i@ZHCi_2$X`~Gx`9SJuI0D_ z?UOe>=}d9zw3?-Ds+CHTpg|QF zOEtFbZyD1gYKyizfs(~f=3Bgitv1p1WzIK;{{RZNG~M~-Ag#tz1um&69aIv~2A$^_ zrgyhEhzC=&T@SQBFk71S<~9z$p)TFMO7k=XIGc<{;#VzdhEkE1Agy+WCFYHCyX3pK zxZ-6V+I;K-QuA!-R;XUYkdn^6l?6vVx>Q9VtJ@5M7I3JTt`Z=K6dY^eU&ZH#^Gi~t;N)dRP$N~ zxRhs4qYziUwzy#THCJ3m<_5vWlY6B~us2rH<*2eO@C+N6Ndbn}C9P|zpk9U1L+Zs^ z=Mi;`cTojt)?1esxMK3dnn~eRR{1_j<4x474a;Feg0|8Y#L8<+&8r}^>gvgo1sI{qCIZ-1ZB27mfIqQB_gAy5zp_zI*LN_1EHof%d3fQnOgih z6~E(gme~0^C@TFx^`|x0a=EqJF5d%@5aNfHNU9Ij(-E*^eq{T)l{D3aMqbwij?n*?>@pLx#@=gds;)C#P_v~A#jLz~B%NZ*@xjCNhVb%Sy37Mv2$rCTnDK=Tsy zDvuHN^~3BWxA!mVW3(JqUwT<#vG&F}V+6Z`g=B1Pvp;Mxbhm8xgrS?#!UzjU&Z!CN zF>Cx*;7icwtj=wxX8TbqVMMPxn`_%`Hs^NE)O@=gNN!w}x2&{50ZKI=P94E(EQIyF zG05a?qKT#mwH!?pv&WpA>w!#8a0UemTs;yiOjPr@5yWv?-oWkw4C#}Cv{u`T7Z_~H zi0E-HSHzMC#$$72_}@ahOBa%g;x49|ghO=gYYz9bJ7nC}rfv{c4uq{Bt+jS7we?De zVHjH%l%C#BrWG5xHodM!0@t)vH@GU*e%WUCux?im#cg(#rQNr2P$gz7qbAp0)O0v? zyQO8rM5B*1pKUE?vj*MftsD~G;oa6qTcS?enJKteHxh>#Q^_F?rNTg>h^1;f@Y5fR zkCA@duOUMpFF3A1DYjW;V(!?n3{n}GP2&P@RU|j$SK=!{mZ8X&$D?svH z)uO9=qiNkGT_>u|5Wf@6TALb$NUkq783qOlm5^=>JnoqEjTCcpwBK!dO~xwXEPNV} zK>80uiKuVmd7vt-bz^l1_r{3-iz4=xbt@kpT$ze!afLBo{pdkBkNVJHFmYa-yED#5VscKPa&YnO0x zE#;!1%yj|+&=sbET@Sk#>-w>Q%Ot}!lNUzrB|?6n{W@GXg|g$hYz?s8!qlJd5f+k{ zB)9QZI!0<8n9w!{aRd?wpTTm6<`eSTZ=iMDEu(^keI@U|z&8cwh<8wF%TD7%sfiWz zg4$&yc$+0-_tQ~M{9QYtkMmgD$asa5{#tUbx0U#dzl89rzLri2wOe4^EN{M1vX
o%v7IP>x6z;|sf4g5R!Ts(9cK#Q%klTuW;M95&XFF`^JLiTs4VKnEe)D5M znQU1}tILRWs3$trNd}m6vGW4gCJE3K!@V0E6StO`w zReQdSb}g;Ua8tPgse{ByC{R{61!Mw)6o6*|4U9XA_{ zOKhVm+|#T4JxT!%DT1O~Qq!0UDW!e5*fDPEh;@8a)pG`B4vE|wP}=G0N*h*lR%_IE z?s1WJv&su<;cj&yNO^}7p<~!+Y6#A_41;~jw?Y(_4aPSB{{VTVO%W&V{u-UN_s-2` zUAoZvoqvz_6U`Er5GvBpg%L#*g%qe)rYM)UHW^5t1f&-&F`aPL=ZoOGqPX2e8Kzl{Ej)~eK_R$z|oVx6+eT;sAY zV~O%=KnIfSZtLy)EXF!1qXW+^1dzBxGh%O<4!QCcSrU?JTX8x-Bk3bNbsRaXdRD8D zj@P>eohhv^jkzwqEvYEw<26kNRnCNrF5Kr}tE(kJp38 znS<{k{@=SFx|7Dv-f2)%mftOfnic9vBRXaQt`8xQ{z+4(kM~xC4P;CJV&!DL5LzW?M8>$?LabP2)(@{@iu;+G6 z_{RpzLR=~+Q#RH~kkg4)lGz1HBf*sIEKW}mV z-Es}Da$fA#$xvtAr3e=I?6Gr1g~^cnfP^%bQl*tsrZ{Pt_FC?@N_7C98n2_4I5%8v zD46eb^o7Gst@)KJ^6T{Zcl4F%LwH*G-;%C(OP$A&JxHz-t!+zu>ESG@C4wT{4$t{? zG?e_)s!K{;RFJV$i~Zinc6%Z7mmtR&;NRXpli;B&cf^>RACoB~~9g5n892_vAcJd}wSpi+`RrecEtQNdcdP?8AhdgH|}8gpznM19|6TCyH+Nk8KX zVg4P|kyH2MOY9x!SmY*)JG$_NCr{p}f$TKIjq^HQhigmKV8qHIy|`wloGOhUFG-;V zUv32>s&VZbjGJxQsZm_=o*_s{2AL>lgk|AWuSHa%o6={=D7Y*(hYEGBrkM`mk3@+i zeJUW{@1lMBLec$;G(#u1N=8KCq{;oRfi%!4Na=+Z}x6+2+E?Xx)o>Yam`mvHqI%Gbz z*|E4j7Q4lu{Yg_%4XeDwMgBkRO7#T*rqFNHYJm8k@&RT&?$ zxRp_3ipm*M&=uc<0*YP93FT-Y>Y*JwaN>*1D^S5FBVXH(B?FML)8tEz@z2R|B~+n$p|hWZwo)v}h;ktej`I(Ja1 zOLy}~N{ouQryTOvo`Kf5jFC)5GDDEvLX%BE9Pt}Afl9$5Om&v%tpuOqIrYO*by87J z$q`&|kyRRlT8_hnK_I7r{YhTSecW4d;UjL|rS137YL}qOkqtzQ#Y|goyOmi?R?qR8=sUyyZon{LVk=ln)4Ab z_n`p#6wS8P+?IMpdR{-yEBjLl;jz6g--%VbcJ57{^Of?^HUue*q@2L4Qk76*yFL#T z_f>?oSuM8~=C>4lY?7V?kF=(g?gX^F(SK)i1*YhMUnz%?A{)JO$$GHTU7;D)IRcQk z?rf|yxVC$Kw1H{#$?6AlTw8&)c1*3<98mJ-z*{awBc)X9i)|?%;}MA8?K{Ui-EDnJ zVE2D+M%v3WSVt#TzQ79TM+b0s9C}Z%?+!-wUwgLPw|u)a#`yb(Ak6ux>$p6^+C#7T z6iHClG|^fUnv|nKi)J$p+X!6g$m%=-!*^Etx!qUs+hU_|g{}qixKXt5AGe9{T+K*z z)%mU~Tcmi^T9SKu;-w+0iCrw%`eOsZZb1X4W3vU~l6j$86&hrkmQT$@NOpOkk0wGfDoqVr$msPfuqd?l1)E& z9A2?|-sT-0s8}}e>@Fi9OX40|{Tvmj+y1Zk()TLfFA=5fg`W$Hbtrx_W3=mN>Y)Ds z7Z96Lgk{w&`sFRaa_YE}R_%|(+gV4^7!7FT1p?X*)RgEF z9!V$yo@%N^O$Jy9aV{pPE^|z7fjr=C$ht}M0aIwyr5uJspv2@D$^`*FS;aoK%*~;H zd_vvcb7ACpR@scIi)F#kt5!ul^}}wak*aGwyw?8n4vCMrt&{eZ!gZ2z3gWs|JYZgR62_H;oU+c@HiS2qWcZM99Q>-WMj#U zQkK0p6=P@sRpLMgEPASBWgK`WZQjY}xJnVBv}Bb30Nlr(0ai?QPo=u?QDx_5#+~Mg zZ2|x~Nf>P;Ao5qdZsdGa=qeq(@w8XbCAh7nLrDn=@A{4zn?{hdY-aS4q!$%dxetmI z#AnJQC(KACWYZzpOjfdZP(jgedo#E_Pa5c`-tOCrx4hwXG$|w`3}pF?e@6w^yJt2o zBNS0=rkV>i%|jq5TvKO?nyd6WqDx^5MoaP6r8;4on^gKTig{wXNvZC`S2ndvwbVxT zzU7n0JL<)}W0IDjI1xFd^m;4a2T*$mDJK-{nRT7hUN}`uUD*rU7e;k7(Mz1MVuNhl zE4f)*5^R^WB4x4k)`hpmx@X);#*oQF+8h>bh@HiqF5)y%q5@4#DcznIh=nTTWeBAP zoV)9Tq7Ddjq}SMcaCU@+QIsT#bs*tj0s>LSl9T|W)1@=PiU8^(C9#pGy8@am18GXM zr^8$;7X$>XmeoopI`zv1duWAh5Uoj9RHvxH7pjal3Y1mZ-;W}@P=}}k-H$IVn-n$E zU0H+lU^)UPSwkeIz9E->2g?XN!nUTfmP5A&Eu!;{cp>RbEKy5nP^hAH5`K>C4_1^# zb5_-;_F&1WC}z}=q^RbT(+V}UK&}_1PexO{VK02PH?sB)+}U3s-0Pz-Q$6_-X8Eg2 zXiJVZl!T?Kl^XRKVm|)g%w;P@MBP2Z8-2}xM_F}r;lWuw-RMhUdTF~tv)cFF*L&Zu z(7gMUsjNhZ(=DalEryX9JrzS;M4Yi}+1OUKh1II(E&l-DE3CYYXpO9EJ!G2t(?yOs zjS@;+5*#S)ZiBNH=0`%PGVdqI%1|jvKJeLFULz1MH^|nv*+|mU%L!Qo(zL_3-bBxc z(NUz^c{}+GkjCeO;D=8r@r$TBw|o_zz+Fvg*9Q6Me(*&*3pmsKmye+wW03gXKu?jm zps6b8e8hdZ`!JJkBJl~k_I7Ef-iiLiD7)<|`CE3&oJ*ta3azHrm8~hQD^BbIm5%3C z=VNj=-s4+P>Yyx>x)vAA~SfvYXbOo)g}Ftk#$9_aRECS_rWz-*)uGy& zB5pb~=|KZos(|mqLNrn)B%qfPi#(*MJ|g0DpLoI-4$&`R9b{Kmh=<``jX8>G8XUcD zJ7i?2^imSqR6zF=h(ybvu*NBBzKT`Cy!qdiDA- zGS!)0rj_yZ=ukbiq3hd+B&8s41qk1qPPGHkV2{^`xz&=?`-|w;)6K-&y)f`hgQ-zj z+P1|grlht5YU`du17vj}a=&oHnRMcwR*zA0wk(+r+pdVbZRx5Be$b`)jU{K|1t&D0 zb}SL?)?7DHLb1m8BI3qgkb0TmtX|yOkooIn+_%RZJcno77ozvJmVCEaQBJy{MvsL%V6ufBkwojZ-TGaSdwinKX152F{MUT6qN#%0z1LRk7;tr zTh!A~xtk}An$|-UfQJPrY}wOy7ZY)qI<(Y(jZ8o8 z^l=<$dE(L!u|WPmPwn}Kv?!Ck7DbL69Hoc1aFPnpHqjNU}3oxzDl&PTVhLEy_)heJl z1BQE#9W}&3jCV?t*jXD(3$rZD51`V%Dk$DJ3Ac^GceqY?x$_PX!VL&%psxZGKm`&q z7`9?8+XM}7FsM6*{M}u>=b6zjad#@c#@~wB?%REx^{}@wDBPPOYX1Pcy{p0JvR0Lb zpGf#t04Je6qOrR+n{pJECwX(XgNN9C3gNQ5fb3{6PQfj^v@JZV#E;p{8jrG|iM*+~ zc;4o_w!(|H=epZ+$W6ivau(8zX$S=BQ)Cbpl0gEvX?K#iWKhR+0GFr@I2CEe;NW-6 zO=RlD(0K9nc&frs>8T7TBxDss`!Rbis9BXLwFq~@OK042{Wxlr1S!tNw&l&>&Bbs^ zP}p~L!D+2Z0R7#y)gHZ`EL83JU z-15_uWA!bIaaxvan{arUN&1|TYXryka|uK9ek?e-i)GOtr2 z!;JHWhz?45G15=472+#7d-B$=n~y_Dr>R(Wb_is$aM}~nC3{<6S)#d~sy!q`^xQTA z?OU>5=x21H=B}?wLrC)FGK8+JhEkd#K#&2gDc3w@J*n=5+4&@Fj1B1FMbclH56hjt z$`-70;ig1DmIcvqLMn( z4?M*asuVDk5;U53VOk{Wwek=?4%6`C+9QcHLZEnsM^4Nj=T#`#5|b_fv@6R6zurfF z0nJY#xB#OL$6?y2CWs`Iikvc1dc|F_M0&eOL~+c%{6w?2)xLyL@XZ{O5yoH*-TVA|*zPhFcX;Ph}Nc`*9f+#lgJj z{mOAxnX&ex73PEdTJZEH#i2qvOJ$bbNAI?zrB$!IVs=Y%N~hXR2PEm++fMzxZn5n9 zH*ZM2-X$z0i)0p*@{N1Q$sl%FmH+}0NEZjFV=#I!rT~T$w;uxxG zXq{B{V!gPwtfwC(&Ge=30->WukJ_~!-0r)1^RQUs;1R@J0e9~a&2?jS`eU_7zoPTD zZq7Eur0TfIlH#3`rT+lN2OOz7hg##Dal7JS5o5yoNqyP-e>6!MrkbjA%{yBFbU=&0 zjqP_>+kVS^o4GdwRX4ZZR*1~#6fL1N%#+)VKWJZFy`}E~tz7Ng`wJN75Jq~QIMqLR z508JF;l{Y!E>N1Eux^Y$v+RG&Jy)(~^<~CN28k(zDYI}VZ z(*)DBb+G6NM}IKRRC8)@MaO2`TV`d~eaT`Jc_|42riCALwkceaihVe9*$uY&^1@(G zOEpxF&c^b#$L;Tt4R2i9dP4dAdPhQp_V`qK)22I^LQe(q1(ODGU2*)j_P-wO&OGKX zQrFg5o4a-bU4dYA(v=7ey9he9HiCREx=x)`uAGNcj!N&H_0{%X3~Ou9<5Iqz{!;O{ z%udp~&$tgcptKXq^!8>6B0B7wlRQ{~4vFktZ`L?aIb9G{_UGDOPnHmRQ2vc<@ zzJs?9JAp8pBWZ|_R>nai)kRb6_AXp*-O280{{WeB)EZBuod? z_X|W;B|URPYC;z=ZzWor)Uau+tHtcDHhtA2p95lq^qm0oE?@1vyCIfKo1xSQCa0Mw zm7Wy6&v=&>!MDrprB1yX+SpTph{z>=B6|{?s&IR$qql{vd%fg_FB5T)nexQXa97x! zJ5JANy5CUM@#i60+EH@i>nRRB<)Dg&kVpkYWSW{+5&7%+VzsPdq=jVJSUWqcfENah z2UTOa9o{0o%dkY*R!id>dc_Sfn2jmM8e7iRs(6Yhq?+bEn94Tk$Uw(QITbptklxM2 zUrQCevkyZ+3dK9LtlW3Oww-}*RF*e)IZ2M>mX)K=W)nnF9tu~&NBP*G8|kld54m|A zF%3Oyq2=3F*sL^I$U*D5AeD8tZN1I4vg{jX&1YPAmU!*7*?YH6*Uf*|?v>v8;ijfuJT<`;&0I!Fs_G%x`YWAjpn1lsBPTs#K#ua>Tw7&9{G0 zG*G*?+}_8~ThtU6c(qJHVKY=4c_~U+wX>R1Yoq~8iN(g&`Lr%st?nh_kq&&7q2nEp z*3-0n>=6`u${+z``G|)pcI|H_|uR%YT37#vs+rVTCHvA47%uP0D+_( zBB!A4#MTdST4UZzwIx>fw{P$BaW$;>S`=Y!^i#a9lESZhvMr($YR)@Nt@lcOg<40~ ziEF;?v7(G_t3SnlStWbxEA&?bU~zuX;hSTTcH9q`ygrRH@2-tq;<+nQS_G1VQCi`XOGgDaHM`x#G_Q6wYeJbo z2_S`cVoE!{6i_#q>KdlBi<~{M+TVF;Q_TW=ln@G)PL(*9w#UAA0C+0aM=2GX3F7Ys zQMqT4FMFyJ&ENMcTW@U@sV&0}d46aeiU{#6{g~Reo&pP2g2weZau0KNb=~XBWPe0{ z3JZ4KvaPnrlI>f)7nfv^6&5-c3M*X6Utq=N8;Ra%TSbT7+Sx$RTK=k}iEe^~wjBWq z?WT0Y);CpNnqubAAce{;q$S9%Jg0#0x;!K+*m~i_cFmVUqb_cpYTlutQMLADo0HB- zdSaw4N2yUHQ2S0SmUewJZZDb`$C2_45l6~n8G zIHdT1DULTYcWm$Aytb-**t@HAO}5We-OA%};fke)UL#tGIV2O;6uiDJ7CNnJ+;*1c zyp}pkAt~p)ZnkLF+cKuXmw&lTU@C3V=hTqW$vzY%L#B9+!$jjDAc_SsyRPG8Z3Gfe zH4RBjGHBZ>W3&YWm_0K%y}*6dir%nw>WQQTlA2KWV3L$b!k+y-a-<#{-tO)@jj-LV z^5Q!VY*cCULS9l-6oR5U6y___8Rq@lkI7pGG|+{#_FsO*x9`ldGp(dib+>44%Lgg! z8JnwZZMQDblCXxLzRPW?3#|zI%A|z$ka5fTC@06<`EPNm`bmPgvCU(?VUda-5Tl*< z#r!dUSDm`|G)}adWZBTTVZQL0N`h343S!_7?g`MB!Z8hWk%8;+l1p z1tDoFNl6~jhS%0Cm=?HIGsNd)wRn7PAcb%C?>+48?E)Uq<_j3V+fdh&2KYbcu@pv- zw74j*x9B*^vU?)t(f)0#x=(`Xj2Ct+a&Y{AJ|Y-tRCsLtDFR$*17@ z28ZiiVGjUlP$&&L;+?toWyeU0K}U01*K9jcY98s~cUvn^rcOlIdy4sSx9+Hyl-cIl zqcqezOMrPqvzSp>u71j54+WKtu^7mxCaXf;8)9#mY~lSbXwWM0%sa1>t`^o}ZtGlF za2h(ITq2RB=>J>Q#VHq=YbiXJ|o;w=mOS~FJ$5H&Qx6mbd}jQV2a-J0RR(PTgG94F!x zDy8!;He5LZZMqY!BHa?$;+=0rbvS83sY>>CV@cZ^YCCAj2BfZQ?r(GD$lV4JK+>fR zv+9P&W7)AS65uLb+8cU7BzZ+3X%(o%4erB|ORq2*1*78o`(ba45Iwbc3ban+fd#Wn0Rib=5izzHP97~C)J>r~d zzS2<}99`>HEiLV>{{Xe^;Wy^C-r{rsP}72iW2$LTbH;odEv%>At5)j`#imBtw0}&V zwAtKuiJLM6D<N*%R5nYdZTp;iOS!)#*LZ1Oz}lt*xLtm z+8vi>v`vjT(6E-uN&>o&uDJ8^7f?QrIwf0y$6rI&7@<)$=Z|4qr!x{%hpn<61x@+w zDFBcuKzng!v(Gu>MMcT%_@aKg1l?z8t@J52*#c{g5*F5=mHhykoIDvqPP!<$FdBP? zsLg@73|!R_M8uGhPc2mn6hB@QzLk$*BZ75=idg39;8mHixjyl1OetkjTWva0=47EK z+l=;L$fT%SZKb@k!_riL2F)jL?(m6Fn^RJ1BhAxYVB|l=IF`G(WS06BRJFxE+VZqh zwx*{=ZVRX-(3&g}--3S-`Lk1`sV5AW1c zxsJ5PGoOLb+!r@NRo43^;kP!RKaCU87T!g*DxkqRf`gD5*S`?MB=GK_byp`~ zVti$e8W6b^>j#5v+ZTJYbcGDHw)b<6KN;kLq_HlQaj{N-l2iwGyB$~C4e0B;UWXre zFPeXt9?>@)kTS8q!*ZuFIQ|<~coN-O?p9{rN6$0jO%fMHrl~c|cV~?QDW7Sd(an2iSxVQMMtD8HCjmKK9>F!VE=MSITZI;F`*_z!bxVyKujqG2c#-AL;YAnB& zxw9ZC1~Z6v%5}A;s8UEE`*G7bjmsPCjFFxrMZo)0wlj>QA)T}hbq#%M=x=jz7QyEK z08FrU4%|#=$hJX8@?_AF{l@1JzXFC;8k9&^cqbfRW4P`ta{ggFPQI1(n=jjJc2jML zqiCyJr8!Y>;tuEJE+E}aH6#bF)=9<<%FiJsb#V^)4zdD{MM9tudvVkw+bX9m(3LmfgGXYH|1pq#*R&uyYnu5nGuHW4F@Y2~>DwsR=&uD7%0NUM5 zOI6{dzT2(2=A|LU5GbMCGB}pUx<IT%CGPw1|E~~o0e%Pe9 zEkvIzvi6eLuVqa@#I^$Iu}1e&lu&!N$@5IcR<&JU;g3z8n0*^?I(=9A&r)xaVicw`TUr9qADYiMdewGL$+w}-9`yYYN^aA{me=&TagKwfw=WN|q&{?kNa*{N=TNgn3x><> z#jY@Uk3M9__m3}E?c?fE+oycpWgXH7mk7`_bM^kgry7&XljkN5zr^g{{VGxmWgI?LIL%5J*b(R8pNjgM+04*-6xrX++=( zS~POT;E#K>x}&H2!$ki8oG|AfB_0$W>StYx$DpTQDzubt>6!(6;FYNR&@nZV9{PaT zk0!(@=^?t=2o@2QnecSo%lh`sj7Dvph?N@t_WjJV{u(c{Jo|% z^84=Ib1g}V=WapPRN2e?oM`(!XawA zeDj2L_8ey!CUne4rFC`=Q7w#a{{Sfa5hzXD-hp>qscVllmf1F#!M$mHN^}PTm0u2& z<}%M7w*au$k0gb`aF9@G%`qM86WU+aGl;wx0qPnvy`TUhF-HsJRckQ0WOY z>^OMlRU+!4C`LmncIAWg=|n9x3q7^Kp#f_4lp`Zt@*i$I--si;Ri#*{?0J@(WW-Az zbcPsFX#%yPXh+$ND%P%y6^h--({Sd~KvER3ALkx^Q$_s-p`d&PRH|#!F8X1tfCp5m zI17s?G)-B$rAmF!l%lGsX#+|U4pd05Pi7*e6)Jw%^tB?tQlmv=oT*N@V=Q{9NT$&w zI)a}O!#BpC#N=24qH3Dc;y7(o>rk~`lcXG}l`x4)HN>Ls6r>WPPP$GTngEw!)ev!D z4QcTTiuP06gj2|4MlCiw?&+Mxr)^p#-)_=~5$%wedQ63qdl1T0ewtGae5bh4yh?9< zlee{RT+R(>=12qUR_A7V7UVgxlX&J0!w-jc+5l9RrMlePwt_}Mh8zvHNI4fA6zv$W zS#3LqipY9LrDR$D{$n72m1fsmN&Zpp`)T^Vl`KTv-i*CHhTGcvH1i(rjsF1A+qrIi zR)IoL*$Il*c%y1Q+)1=p+i~~Grigc@;Bo4uv?q}LDpWtnUblHSb|p|YoJgE?X8@(P zARu}APheO|QlIitFoo{ynZu?(bSpmG{J&zYpXS^XG@hOcqFrv+JKWh93%vKPcL{1L zlR87IdDWiLI#Q5w@4@*ZhoKIH06RNQJm%4Qi$btE3jG)&2I`!9V`%Qp z?Qlh>Xzl5Hisb1DZVV?!Pw7=?E&h}y z?YM1TxyyXpc(U5+b&E3HYF**LGt!n>QA$o-r2DaFwcF;EnZcv$S&jbybNuN%yBM7_ zym&YEpQ#g@${Yd4t*MSO*st$<<6?t$UsM*9DJFqU$4`UnNWsoR(%VSe)O<=x4X2;V z9;>-LrhV4_pRGe%tDiTv@O-ZF@S4kH2(T7PBQ%tngr%mmigr^GbM9$h>Q@YJZ7l6b zAp71t(+hBKE!(-`GjE@C`Fl)LSoV00rYv-x@|J+qeX1uAvs_In)ab3&7>iqTR=3_f z&ZRZ&wx->2NeI$=GN~uufr8LgKfAmw8Yg3RL`PsOvJ$EtOFsNRg3Cn;?1P=nIay3K zSm2?gtjlf1q1JzNB<2s(iIzw1C~2fVVJp__bI!;JaehiDON-CDEU-4v^&(F=0Y@)E znQeBDHbp)aftD&J0@ZD0<0Tp`XE`?Iqs@78oNQ@qtd)?F&4y6=+%< z^4)BA8{OOpR(|(5 zHO8BaD3vY9t|jzSz>ut}4{jwcUr32r9tH~J+Nmz|wsE?6t7^=%PoC#0%el@@?X^0D z3v*VKBm1k2tO}PDejrbNGV8a>=M&1Ssj+iW^fk~cNBMVb`<366q#I4WcU!Ag`EEsm zitAkhdVP4Y?bk9F^}B+@e=rid>d-}pLJ|osL60@mLu*M*A~#W90dxdVurYzY=OmMx7IR5F}HA(IN0K112 zDwi#os#I!9T_6PmTo?kFz5>KyYbE>aaWTiH@pv z&nV`d6DoCyrh=MfkWv2t7)Q4O(yX?*M>|&?R2{vnkeY6AeW0YNG|&Y}%O1J_f)=B; zGLn}!q^?}@w%++`l-H8Zgc83cqe2KI=Zv3mThh8N_U*=Ox+aS1s+0El^JcCUvfBO1 zQjH9D8&itw+@8#0dAyWX)h{#)tTy&4d+IhZQiPwgE;eS{dvU&-LfizmZ&<8?2e2Hz zoKtOgLAre*1ciFM*~ZDWoxmRi-CCloq%g|R4{E8MJ95Rc))xU+DrMB{ue-q|ULhi6 zHybF_p_+Z;47Uz5&w%)+*lY0|zJ(}lDNk=6En5pmojYN=rd@WVkrm}t8i4L5m~XW% z3$6Mc#j2y7g|@jOcs5N_G$CoKNTE+r-HS|VX&8)(nhuy-rl|ttX$Um}m8Zpnwju%t zxFd~(q?(M*7f3nie=j(Qf`kK)5S8-~&7y#g?8(7i%Asr4x#mFW*9m}83y`T56|N2C1U_OMB>YsbvjgS$ zqL=GcohG`G+w8!GB#gSDs3<>+9&CD`>I*bgejKnKSY9NplHf?DoF0g#L@4j?!XG1HLgSqRC9f9lR&zgwRaH5jiGP^pAeyTZC7BR3TzKQsqzM&s-p~Ji~yEW} zl7|4UU{;3{aWs~!e){*{qJy^_lp*;o0beOgNgmOb2;fxZlXMCeypp#PkaUV1{@ft; zRahd>F68Y#NnX<6J_p(iFUy%PY`|t}C}>dHO>^xuS7s$P{gWK}t~lBH;1)eu3p(Ti-yy_-0n71E!!ePtX+aa z9!fh>R*~XOI}LH3Y~0MkarRbNN;B3xcV~;K{7?ZWRF)Ovo zFEomw^3s;diioWgp`fJXb$8-NyqiJOSPPaD*BPpY{Hyma3rw5w(zT{J!5a9{Sno*t ztBH1(CG!s%Z#|b~vvMBM+|yXvSAUdXNOA*|aiH4B)RL5{nPWZQn2e?FIt!?t=S9#e zE6gqH^82{<)sABC7jX+4apoFOYC+U}MK*b(fqgw(H;v^{clF0*U2hT{lW+=zCc_e6 zaJr(9ohk_>U56HZnZCCLt(g88k&WT6C7btuvinBsD&W~nQn>0pC0CoLrH$0Y=U{Bz z(P`c-DG7EV`Ja%M^GnPWvnUMx#~N*qcOi2Ft{CPM^!B`QhJE{Yjq*>97EM%9i4de(J!w(j74Bx9TF1x=iOoxd{8&IqbXS(a-tc^b=vzl`Y?&r5@y+*3BR@`u_A22r zWYvlz`!Xm}kSKEP#+nX`84T8iTQ5l7ns%3|DV&dH+4t+}@Sxe|6ot)S3M*A=Wd z>xIOfNoa%ugphnyJ($D0tK2*MBcgOs^+w}I9|hCjm!8XFcWgH-o_{KM+g1)0^!j~X z-XTKwOAXg{p~rQu@wj(9 zq>@1)E$EIbPj*<&_UnnL{S-?cjZIK_c?-45(+!8yqG4eG-=P*le#BsM- zSTreHY6?jNXOR`eh26VmiOf742==PRHsVV=woKDUF~E;lHCO@&{p5IrNT+K=JU-intjhmr|qhZ%xckK1nTOZ>|3a_ z_8m|wZy`byJ4OPjWO45pZ6~kcaP^(q}SW5Rpy2W{9hGug%V(a^6~< zaU-D6kZ}ijxbxS-fr1FnGIdvcCvG;bA>C$EbuZlmPt)m6Yv(!dL|PTKII_ua;!VEL zD0&T!IA(LeWPIsMD{!dxkU{q0o-<=w89d9UnVx>}sP-w`*8c#$@_efpg}WQ~MGC1` zHiBjwr)e00Y5c7nib2DSV{4=ZPv!;4RSu zYI7GJ?hB+e%C_6?OsY~80<@uZYhU>~tMp=x+<1)Gx3RoPTPI<&471#j%JETJFQM6Y zS50UuQ0J40X3ybpZ;O!s4en7nLR6q%z{R z+;@U5cO;D{aV=z05AiFfyBbaIeA(HEhs3$_39$xKFzc6!vZ~zM_GaC(T#eQMk7|Vl zk0~uQQYtdc{aDUs$xAo7btQB~o(c;xWNv^3#6QdfcuU>9v@B3%DqWXqMM79?RG|_i z61O^KT_9(U*X*7pmU#aFF~W{=_~ZG(&&!zHWs#b)HP08Cb+m*jBqepFGc_Ft*NwyH zn%0*SKo=j3B=WpQz)PHH7g?_R4&&+n0MP!%*i|UrywQC8e*DlHTy8R(LYZa4I)oq~ z0p4-RSbU>$`?_1Ve=ZS0`&ZK4*|nwH9k{l`v>!g@-A?pW)}KcNA3a&O7aIC7;EpJG zsso&dahHAFkEg=ohs=EAEuB8aAR6{34tswrc|F&0cy;K|DE|IhTwpd0jwRmceH`yk zKebWkUb@O+)wK5eCPI|*eqDx8rN1gm#3Uk#sX6o*-<=h`yIpZPpbLxSzPiZC9fg}o zspO>l`IW>l=+Ei=U2J{OK0Po7xAPzt3Sir zG5z(v?JUYimu+qIyK~f`TWOtmiuX!CR);#83W>}12QqG;LINkd|cw1g!nK=xA_HaNATQn~YS$t1OHI4JQpm`hsL{FStm z`=|hu^y0$VssY0BX&lg7CcZghAyN$!VDtV?qnv+j0w6sQb z1NP#~_ElD6pQP1vhk<=uasJfVc1@SPEmN&#&mZO8l?^D78c5VmwILuWDmBb!@5ecB zPTSv4bm^iX*Xmuz+E2^|F3TR z!|7Vr`|-6&)K? zmzdtWiQ0OAGz$^#$8a%Qf_Sa%oEsHKQC5cC=~vT+^V_hSv=Mb%Od$%t+wLRGbzvYW z(i>9KsGR9fW-|ND`<=F&x%C?GA#a#|$n5Nmn_C;2T}~jVZ*+PFj>6n`Wby;8C}xNk zIKj$$Jm|#->rO1#-to-gNtmfw{{YH+S*@(PCr_tNHRetl-!m;6s@m1pVBg~`D)|ml z9A-4Od`*`cb4tdsROCCc6TNo@mP#OF6nIw7%WT}f-@J5J2dXE1NUy5>!Mm&%UAkE= zt87bc#d)nEYG^!_08+AnKxlHV9c&1#og0IQ>P@@neUgIdu80#E@KHv>%wQmbq3rl?PS8SKBs7<#$*#ND^(ymuyX~#y)npi3$j@kHNPSv2C&x z($e#Z38?@LWM_*+&k|aL=vkGMU1^nb8fmKNR~FrFEJT2X$n%{fdFUDVPw2)SxNO@} zS8#UEEiD5HQ#FceALO|RXDmgBHot{D>RY!LG6+^9Tmvf z@5AIBl8bfu3jh*8?Z5fUa!OowwNR+ZVEZwXWtwcS0gr(zsdnVe%wlOT#L%MdIp4GP zhU2|$XAl6v&^V%1PZ z2vi3bum?2gj7_^-t}<|TUd&YU7%)duR*SW_iNUcA3WBQFvlS=_ zW4cy}e-kBVSV-Gnay4~j5sI|j;2jRv_kw$K!Z+5AXS{IWZRW80jZCAQL%mmWD)Dj6DL$&@DL zl~k-DZa!}d4MSEm$>5J%6xrv+tV8#IJ6gR z@F|k(FUgB~-%vpAR;C+SIHTO>yMea_?i!+UL2&;h6adqvjcZc|;kt5~hwE1Ri zRZ~-MGP)X9JjlmU%XHad5;uWd1r`!mC3HuNO&3G`5%MJ4JlD1j@?|2+Dss}72>}u{ z)x@T~HFfvn3GOx=zxb`tYH%`k{^}gHwQa#RJ?Nx0yVy!-h}TYt zk&wtfkwt$*ZnBNGwV%@e5RNIn&42!vzrfq}+z$69f5hT>JTh*uN`qE~C<`pF8iB?AxP>c845pjjrv>f9+d@`AOtA zWNUE4mZC&qs;VGVR=H#jHEcHDnj5!F)9xNi3(f9$E^HYChrZrwy9joGwHFz6(0Wl| z3|&DXP&}YgZ00ROiDEm5Q0St%)4QO;;!38|FgJpETP&0A(4eLSX%iHIPbDC^M5NS^ z5)B3+t}kPVo}#SyYYDDM4Fse=R(m4zw0Qpjhi;O>!gowNgV4g#q^UkcgrP|(P|Ae; zIE}((Svw_bRhxF;WU`tEJBj}Q1=p|4&fec9epR=t>kavdfw%Ui;EYCD+ss)iKEXh7 zhU|~wX2|DL^{%h(4ZUzJe;@(&Yx{@zi|Wsf0R>8505*f5`#mGc$|AqpT?odyW_^h>cu*Bxq}{{Zgk zTC0dFf$ys|m-5nvh7l1DI1Z-=h*p?<6l1uesdNhYSvGzka52b_<*X@fJlnrE;nLE7 zy99tLuH%J1B#AUgppo#=Aa+u}rwJ!(Qzk}KT`Bz#aq6#py7QA?Ev+>aP@(XS+!f5K z@MsA9(=?xneJfl*tdQZhMW8Oro{BFX*Frw0iXq|S!Mss$-sm?~8r zD``>aNiynaDrumM8kGqaol+Wwbtg>#=y27*Cv9~RM>{Y?y6Dg4`*1{k(Z67**XAKb zc?ca(l=74R01Fq*jqx}i1gRhkDmU>?fDu;c@2BqV`HcMMX|@9o588Iw|Q{s!X1cTQn6Tw*W$YZ1jFD6v|I} zQJmFfdT`1`DI1b`UW&F8U0qMxhW&acjXG4L7W+lw{WXhymf0rp zaeZ1@x5Z)E4=>!7lB4!vv#L&-t& zXWt|QNJ0`bDTy4d!|V#viA#OCbug(NBhs>5&wj9WuQ9|-@9StIH!8|yfVX=VlAQmW2@*23fp?_+`GT*;1DqW^YuSo zpHgk!y=67FU_WEvZalVejivAPtVU=)NEFnsD>W;&s!^^1`+<-*rUnN3Q|oTW&3`YO zmAHJz!lsI&?=D8){E017H-6@qd32IO9eG8tPw=1}Bj^~cUS%ek)p@B~M&H@E+-o{* z;|-^yUut58N;4pn{8jy!YEO!`HQ)*%3bOHJ6Y$_Fi1z}uHR=UO>5nAn5OfMsa>|%W zLYL%(CZ6Mjl0=}^;7IG95;D;lgH#Hn*TRYgbWL-q$GpOLBN`4WQro;azjqu@#N=BO zwk#%^w*}SDk&IJHlAktEFaqc$HGhZ!0@v(g}OK8>#(@Shv`{JMrXp zmj3{7WZg&uU}5HUclaY4 zgVnD6O}%?_jI9>#B--I6v2C(OZI_gTmySY)wI6O+aO~sQDHshNgnJIt+PY6V?arD0 zE;@S^UA$gzH@U9dFIPA2lb<@Bl_Ax<;)(4laFd8xE~Iv()oJ#4%X@Z)#L&^`2{S>h zDc1;jYD3Ig`9h#{7(vadOwCkn2+Dv{3ISA%j|tyqwv?yl55%fz1NGv;V{M|XSE)HKs3R;tWvNlWHqe)>tu&a|2LTbv|{$X6ZQ-n5wQy7|@G&V<)oQo_tIW(>b52Q6EpC(gAfpn!uLxvYt z=S4YbF&cwVfwu%keLiXf>=Q>?ttvhEN+2t$dvNmcrxfXtwpg7U%(5v`8yUL{k>(&* zySEPwvuUDWG94z4OybI$acOWA6r9E*#+oTO-W=MaOrt`R?dgSB4Ny7B$8JudkO6 zeeIVt7M8Uo0IIoZsTzUGn6l%e4~t#}VEx*K&v&>gS}LuFH1cNtyl2hhVzWD z)zapKA+I=~coqX;Da=sRb;Y8=hB49>Ese*r>4EMQ4o%IW&s)1}I)oYOn;isycXXO` zr(9TV@{S4%a(0g&eA@K%pAe^(d6hfOao>`V%gIF)&Z>&_$oAsR20%E~WD|DS;{j`R z6;G=c=WPz8wxR*2ZdjF$=qj#L21|8P%stkhAeB4?J3H{;U9A4+0DAZ#?_)(IFDy{An%-|C8?k&4Sr0PlQmJA-En`Y+fkglpr zC<|f0RI2zeVx?>vTxj5{-uiN4qRe;5N|I~iCr%nkQ>xdtta0_URdZ*SUMN(wECtAH z6*%rn6j3=;cH=kPhJe*~4{YErEvE%fAnqI1-ndSeWs~x9q$HtkfDIC^xaSmfO3<;YfF8eY6yOk&zL&o%J3+VlRoU;95WD^2 z{V7$ah$`Y^B>ha{w|e5^^sZU%mC$xr(}P+E)T@UzUD&+yPbWI_raBwDJigkWu}Lg!qg939lrTBb;e2%$by&pAi;XhHoLebN;H-8zCvra$qBCTa*(; zOOmRe@lk|s>(ZNGWCmANTkYPlZS;}CP!g>sOvNbl7p#Dac|F^$HGxn|J(lg||v zaneHEab#vbA9?=(yN8iFg;{fYMy|fsA1!B6sh^fghfaw&v04f=tPdpW+ z=$n6VyxDB2x0rqsW4qU?l3DESrZO$5g>~N4md>=1q_lb{bX`Zf_l$VH;X{-I_;Log z`f3PLhcI&mi*awewf_KYB}UBu0G0A(XwX5^sF0P4^*C>|w$;V8hm|S!m5aEY%C^4D zo2ZAlXi;t3&HHn3?#->b;#!=ICcyzQCq*hjZRMxoP@t$FlS72@yE<4Qd~ZEL#;LAH zx{ytL+nB1?G*MZvN&DY-@9oFFt*gJgBG(SyXC;Raht}Jr-z^{qYBZ5hK<>t6-7ICT zlyQ~{ImVS;EBSidx0%T8qWD|Cdug9U0!ifJvn;(#GZ1Q480Nrc?swZ=g&L8z~ftx;@uKEZom7m{J~1r0RQ z=uyKQPw4k^F!-_a)!SxHNg8WibC;cIb&+k3kZtvn-hmS#3nLBga+x8zO z=)~_&_@*_#gUWX!P&j|o4%^*3vML{uXBO$QZTA&6!b+X;&_OzaD^M5Hu2`PB-==6s z&f2-HByklApN{QI8FJx`zGI|lC*3qO9->nM(Z&0>a@!`qYFk$en^ORk+tTAS^2&iy z*GJq+l~og-dE&z+e%=5*(i%pT3aZVWb9No?g78Q+Jkbl+1oJF7J<@v+Z?Fi1FPUdeDem>-P>!1y){^;?aAye6kF80gg=yhYmV%x z)B(*2I!PId;sa)7t)}yvj z*=^}(aw#pw-s~q$K4G*3he}6HRL|(fRkg$M*N(O>1(nfs=W-x!cMi+;Mq4E)?G;6N zv zp-%E>F-hKeyBtJ6H#ZPRuT`*iduV0%CE3N=RD#lJ%S3}aV&CjLlIgSW&9!L0StL6a z;LDF7XmzD&G^xxUp!XW$X2)?ub86Y*_nPrlTFh*Vt1N_;cNAoik_z*@N4TXTTG1vk z5z;?CKa zLd&PKd~Pk{M7OnVQSP_6w(E_>7CU@^uQu%_sA;7oB}oS&d_+?bd27QIeN7!ia#NdU z3fr0bBzHjZDRs?}HBQKGHRjm+O)WtZ6Fxqbr?96{6{y1&=Rq2@g<+Rv5b_%Kkb@g4 z8xJM)X5^z6_v%sdOXG{0mwNA@fczn+8v)davd8(GDdUvISDB($SfDIT* zTH9tkx`sxC_q1&8G>qP@&&|OJN%29n?fLT_U$HMqQ%!^IBABS;w+>C;$%EQfskb&3 zA*Q!C6&?V8#a=H>oLT9!e_f@*wegkje7SCq=S;rtHydsc(?sOV0uq%doPnu0!Z+sU z&f5=uQ5ig&$J)Dtv3;f5c{A1W#zwcBwF{wJPS3}CanO`g@W6tVXA3`;N_jyw1Nwc|vC2 zxlEMwl{pRSlu*2)0Zj@`JF(aOrMa}a$2IY=2XH!bUnjpY)|qT>;F8|rM;2DCsWhjd z9`xV5_Wsh`S6j{c*BbKd2uoJmgEFO$pQs61!bm4btp*?W4-avB5M)m+71oJ|V>e}n zHfPCl!1GUbsl&T@LEGKR>wR9^w%caOPMC|f#Sz*{tD4l3RjBq7aRGzDn@|{BT~8Hl z-22V9t$W)TZFo^t8;gz^5>@d!r4=L|g-1MTpXq41@Hm^09l({)E$PQR3CFt^XKvlD z^V9E_3v4+_3zA-2^B!y|(6*KRnoxU%J28XeZ?f07FJ-{@K_lp0xs}_SZx?F}_J*=J z1m3yxpC@hD)9m-X#H6NsY_2V<&1|WrRDh&}gys{bn36j}#!9`d9bqXybu#wFu}b6R zpRsj^gJ;?SGQ0} zKSFhSYvS##yg8?j_T2rQ0Ea+$zaIH*W>eF56OOg!C*`4`PLYwOok1f!v&Ad8d!pjs z6!!bf%PYOd5HW`_hNSwH(e;77tec0C_U9klb}YB9n?~N{&Ta8X0^Mzf z`qIi&X_2Oij_hTfwY8So#!24s?}`DF!%CWH5OxOUNp_oeS>m55l+n+2io5xs?S4~qzT8`i#T!Lc zJ+m>ieKSmrxh_o&-O6{%V!xLmp=eY)8?TQOu4Z*O0iYw>Oly}?UT!yr0$T_=`aV2= zb6pE>Yu=hms(*h&9AfPId?vr%;)9xh||j0ZZ0OSZGqd6r6=Io8%;k&Z7e8{k>^$$ol0e zW=-1RaAf`z)|+g4>zKNM*0}?QEVf|NnwpaCYE)l6K zd$?NBK#z-=^180zrMO#?%G#Tb{J)i3rit?b0G`b0Ojf0|aVq-_>`l=)llPt^9ceul z4M*9C?P{b0!QsIayUS+NZhSR~wUd+KtcTG(3S zDMTs2G^JnCSs1%UB7kejW*%+D+w|b1+aAHUZRP~qnfbY?0P0u3pJGAlh^xGpn0?h_ zvcWbg7jaKmlGQmuEh9q!4;?zw5b{V|RiTjzX~7h~-9HS5w1Y)BD1@YVkGKpP(VcG9 zM`R}~Z5za-J*8p0PV;A7immLsMMrMGdZE-;y0~+Yor6G-#9K;7Q`Caoc4Htk!gK-D z^(WqO3g;56@qiLt?(YV}0!7N_EfAtGold^85|9VKw-PoO*=}9Z%~VcrKW$?Xe1rh0 zF7@?sY;I%Lt+!%&>e^MK@kcxj&>YHA4Nf)8Uv3;49{wwtHy`E|hEo&*qs3Q?w;oUR z1$|ZKD$pfGAx)Rcs)m$kM{l4JuZQr ztcFMZBvNVRm4m4@`n&NrvI%U>>5r&*_$>Fmu$NnOd7-X6yi{YsTW^ClCfc~}&ShrZ zdc98zMY_i`%amAU0;NHbs1>igjIlksUzNR_GT4^1b6MWX$L*|DgQUEp<#6Drmhg*b zyl(x+bKCHfZLr)YNOs9;N=o z>rXiQr0@xyy|{~X@kPa(9_xUpH#tnD=XPFt$DsJWN z-QE^&B0*E=NZV?`cf8!8+bwXHCgpK?wqr*CtgUV+Abk}$S23A_-WNQP!6CLbmF4Z@ zVrxmMqUmp`>xI98eLC%qAlq^TE7tIAF?4ma_o{59J2l#m<5|&O4quZNmJaxai=eJ+hxkQL%;?#B#LP$$scbWl?t(NJmVJCoJJR`7lvIdT2 zOFN&0H;&4*TCX?dLriYbRY>3qwOg9`pTWPAs8hOP?{IN;58+*}`eB{w5H75@u znx=|6{xUTwuvXF#;&KUB?)TyGVuACB;EY(ePUPCsjE%K_o41}OrPy*_j%!-f`ASGX zP9kq`R<~M6(0+v@iQX9-2UZDO-%(bVV0zByt+%0xyP>Vw5v;ym=N+dNq;&h%gKO#D zPBYBUY4KBP*7To3-gbxPYjN2cNhe!t!kh=J>MIdz3i=Q9{y% zE~-PoD11HV80PEkz{bL55354!9@hS0zql@&Z6hf2&0JYBLQv#+v4n`I6m<$pTx`>> zprK4=@q*o3qUjs2E>TGUa7^@Vje#Oin9kdUZ@4r`Vsla2ZE06{(g4GjW-g=Bq=gCn zr-^y$YXEnOjJIzITqHMsyloBCr!RNt>S5O=W`wl9sc*{Z?8S=}x?^j=mAVB@+`Y7X zu{-78Lgh}bd_~JU7Da|c8*7fD#`;}ud^HDk5NoR}$9`iPgEuZd<|5ZqP`T!Q-e)nl zERmHVrCH=|rC2vjw&e7urCo3Fyr?GtdpGj!h7TuO5 zP2H?R**8_(A~h=XiB32oia4^;mM93!^y`N9mT!E*hJi_B^KnCHY6G3%tG(-a!FO?6 z?lSh=H()*k5@5K5BrrOq#83i<2 zN~%wax!~9x!6mc>tx)^xVMUX^4|(91S&Ox{?$MWQb&%vLe|lxW6|50T9bItUw-*y! zhgVfB{9T2Ofr-OKR4=w>ACmhUB{5iwD=`t(X;>hdfJy0%Pak+%D=w>^^H_Gs;w^{( zrER~pxl?VWe z<@8K-#i_!yC@Q69x%KbEEUw^V;S6Gu+M5zsE*&g}q^Tv1x3p_9Hw!RzsVUNA5>l|B zqf#+ej{bHUg0xeFw0%RuD{15Hu=%(!kER`ofNUhI|Z%DWNPhnoVe0d zv*oe|R7vZKre`zd$XXVgv-ZO-ArilevJfLfV$#fIDN~w^G`6FjXv-P>%*4nF*&()X zlTQGkeaRX7HsX5qZFEOU=(5IsA-dd4Wa=$;gB%^oRPs`eOY7)6<6bVGOD3z-&nt=!F$3Sc>=$6E> z+*-KjySrH0(dR(KI7O@cAozLgE%JFa8ojT)|7n4&aFdLM@3T-H#ZSpHPO>l zTx1jDWMshmx-P`+we9|J;$M`WQ!?AR;rVh!LW^#vFra^l3jmLHIqG30h3|z5?0Zud zUm32o3!2c=eB&MaGTgmUZku{pMs*2al)RJ&@U1zIVaAhj(|K)C;IR8PKOcM3N&r=< z^-mJb%U+tdZO%Js5~17EcC{?f=#vb2r9IWirw}_1c`Wilyopw>?O8J=gDbDP$)#P!5BxI=d&hnPqL)#eDqQ`$|l8A6XBtzV-?-4+y~?)=`EmI z9{&LCD8s|nAJkQDg|b_|Ly)+)9ao!eRX)U)m*$4Y?Fya;G z;TuJhY3;qMdPT9cPY@J`*71S2UBQES@8=~$<-4R_EL2O)nP}#BIYM}4T=Hm6& zYw)A)!d1Dp7jr`~6q1sZsI!)R23Vf$vk4Ld;8kzF7XV2ExC(`PH%!=EmH9qD5%-fS zT%ItQ8TNwnE8DcL^4HPc?P$O{^pC7BiUtUAw%zIGr3xLC zszJv`IB2={BT-Zfh}2DUKgEK9rBb2Ds7*2>reNoQOR>dcs3W%mP%y7~W3=F@%xgjj zN$mPCl!}9DcR+kV6sYxTS;@N%hj}@Hki`GrnSZXR%9)t}^TK@1Sqh~7OK8pM_ zS%3S>?m8Pw9$~6lNTO4fC@_~^sO*k`Y7%SlW$wag@u8`uCp^wPmC+Vi8crJ_B@_cu zQ5oQcJA;C{-iK~Khw|&t*0JSW?awsg4Fz;93l#R{i(bul@yPKDoBseYw*6SZ?*|12 zdc=U;ztpcKCA^a&OAdksJ~&LAR(DsscH!w-r)cg>xCl>!XshF7R;1*5VjL^P$QfuV zX~Hu!>M4PHs+YDXcu6IeMy#~w^kL*WilnAGqq=gXGw;IbnRcrL05cV?0GclZJ5l_= zT~fTJ8zWl#{euN#bgCCSlHOX(-7U1ACL(f5Mp;+1;U^lD>K6v7>y4aH+iMNBhhBpv zK?IO=rNoLFWtDNIZRsg)vQ6i0{{VMWz@=NeE-Ek&z7$(gaU))>B~%0ojJ}LLx?y)U z8DK*lpi$h{TTMa%Sw9+qIsX8HsxMTxP9po7F4BN2GwJ+h?RsRgoDtJ>1MbIkX=W_dCR>E=jlG=Zz58mr&9_3> z@k{aH$Yvwuw&k|dVG3I=q-v{uNUHitUesINHz0R))ifi7hc(RI-JC5}DIeQ~SzR0!-X+j+mMNN5}RKd+9{E zONY$w3eYN%zImIIIT6CG(Yj?#X=ITVE*7JYb8XOlc!0gi&neWcyKZ*d5sV+eiJA(HCJ{Pw}4t`B7k=s`ruN&U|N6%dUNl`k|pRRsE&kC{;V@| zsDaKwR|7&in*hXak05SJl z`%l!3Cx7g1nmoQO`w)z~@aQ@Sbd9?Bol{ofFYmwg za+-bC=s(3r_kG2E-FGG=UvCm_vL0ASdH0IQ3Rgf5Kn}nL7+uWyIF)9y!P?rgjA#@@ zcT;SHE)^>51K2a`LUO_qG|@fB&98t_f>Wx!lqi&OpnGr*neSd)a4DDz7WMyta}>?Nwf(_Z(378 zRZicd7nWK701rit&AE@ALQ;DhTgpP@%W)A|ZmM!aq#?ARbtI_ayZR#)a(y0E^1G#i zr9*p*ku3H}5+iM}ZjVt(c0$D(NeA~wQ;MDTT*?SPKocs=%r0A8Bs+h+jl$SEOs3w#^Rc?3XVq;R@cQ*vba#=?y1ia%qbf$x}RN?jZKp1GK_^qyDz7OQ8?3Gc@l+*O$jMJ*!S;1Hxs>&d2m=3EU zaCyaNnG_iw@r%|afJN(BE@&C<+&mNJe)y4jRA=r^E&0qa(+}I%s%~!x z9@C{%=V9LxBDNk|pt4Sill$KDiP&!zQI^`Rd7v7p1_vS%+sSd=DjyDcA7&&W$SRfA z%+wGQhSSL^j@maV;9v{x>eaSi1Hh{$Y?v!a@8o8I3(F!G;6D;boPQnlVKwvXrkMp}-gs4+_WP^BYb$xjRZ?Ca07*zSIpN`$FG056HKKxf zD)%BpR~?T2rqmCFs)YLmFxu);8&$0Ad_!9x1Zt>8_nj6^zG4(L$a7F^>J>@Mn%Ax} zE!UA}MHg>&&jmB44g=i^pg!BKKKqJnsUbvAWsGUk>bjI#ki@wkWbS>zZfb3!+GU}| zDF{MbY$-)KkO4Kr8_mmaYovyiPVsvx_V5=(D@4SR7DOozMwY>1LkcN*g$gMu9YObE ze%9%-usOU6uiV*51Ir#LLMxYU7Da35kZ;l7XXWm&7Guq~Ee=MKoRo#OptPtF+(s)q zg2b9gIE7}!%-JDyPt-d_ce#AE%REuAIm!zl%Tq1d_qUb+2%5<9>Y$U_s^{&*)rH8K z5ao_-_d3HNC7bjB)xM=v6VjEf39co=zDSLL6ak_3U{f-UG$=^+VC-#5Ax6-g`d14E z=#g{kh6aYTt`ez9fKZ61T4pfiuA=fENgf`6;1W_VP=YjrmsL})6lkbO^F|PjLoq@r zPi8zLE7eEhLsYqOp|$~Brx4l?U@K5fa`)m!7R+S=(tKDOFDHZdfP2Z1Y%JC%M0T557M zT-LZuYX1PZcP;6|_m^vo=_*xnCHYHt_2Xup8ZI^A)tC`j)D7e zNt1J2edW}w?4}LB>67%Lhtx+WJen_ZkZai52T!93jmL0*c`yFY;Tm*{>nqLk&nBtV z>D&rYaOd}Jiv5@$?hBgyOV90A5I6Z>Sy#VmY-{%6+xDBh-QMta;=@lZC_n%g5*19( zW-m52PL?p|IO@4yaBh1`JIBJvMH)!g(3gMMyK+@od%|+HbV76wwBg{x__(OH_armn zfvO7U?larlGiM9TX>w=XWj=;Mb;PM45t#|6aK&eE*)}NaTCLZy`_AJddRXR$f#4Mu zS6-AoxX&EBFbx*W09|Yzgy)8-+2EgB$&nLo?8NCPTg`qEra-lE06<@cv5%2d@X4!x~@$O76Jda4Otmmk3q{k$IYa{FNmaFvnD;fV z@x=a`uGY!O8=q;%x>)bgfz;|y%gL1)O0Ifz^!MXC+PJB$a+fS{{v3ojE`i>e>wJb5 z&PF=2!p@WH6#a>X50G~ZrW!=WH!Vn@^HiT50H$R$t}at!W8|o8-4_lT>XhPdnJvDQ zO18B7AdPL&Nv?J34jJ5I7)1hgZ@667Rl=%7wR4u?+H>5xarDW15`30EUTe{18oG+s ziqr@rwuEB4f3dAE02cr}4;6UB_j4hOd3BS_MZ$)h(i!9?E|+%hJH}+G%eg*jn=ezSRYI=^PI(Fy#C!5Q*7#ojq$b-fYzacjb z#OqpIZa>ScwxLYSDE_DH5ZlUI%oj0yPfGV%dOv&$VHhy)PqC;8t0?BeiAxt+sbmLEh;c z!hZT!g3RAg8z`aT)TFg_Ei2pjW8Iij1w-);bFxxcKh#z93X6BKX(g046ciN3XzFlBB8Ov_qod`0Zq2$bx z*iAi+xSTeh9d1}^#`^+sj@&n~OxjBRKY0GtE^;3J-urWK*b`>n*NDwYvRhwyvvHSD z*>BUra%HqF7ZWuV5qT32j? z6>c*4C@S;VXelKmg#tUVSjqO*(AH>+gri#2^7JhuZg<-v9rCvUu7%^Kl=0?}H}1&0 z?91!WCoU|uB_R?mv*J1)Vd&2dZK*1tCoGS&V$HX*%({Z2pp6JVDu=yx)9fxt(6!o% zcodtmIWv&>n)m$E$G5X%zBY{pNNEd8jgk~lw5WKHXeuj$?meqzm4L-?I)X!C_a7;> z?>=tH-7gmCACm%T7YxU6rR}JHrhEX z2%&-6SC}hU?{qH6&kkIgi>3l;$-|9(Ke1PFv~9Xd7)z|SGa6lRll7cw2Jg@o7~E%9 z2OnxSn*(LKD&YA&g-##af=PN)zrYxQUfF3{ht8T2;VYu5aG%c-QlEmf1di_CiU~s7 zLepQ1LVf+%R9uwF_~wK#+qR_5zUlFeSOL?)ZgeH2hz zQXBb%)Q;sgN7Ie7uo-8z?ptSYrG1NxKR!EN5p8dO95-y+^VrxgqC%QWjI=DJ%i7NuJ*QC!n9)S;zJE}sb*;^Bx*{o*fv=M)O0GX+Wz|GuuSN0yw3#d z-kz@azWRlG+4txPgJ!sp9E_$k$YDuPGz8H7c!b3E%F@z7D-NMpw`b-HBbkWX%cESG z8)J5D{ksbjH+c-Y#j*X9yci;#JPkT+a7mgPoolHG-EjI^k!*6jA_PCVjMq|hXVsHpcGCoVG5!oj4SK>El* zW0}e3b53hp@w@5;bZdFtr|lj!-W!r?Q3ykTSg}TJRZ*ipLupk&sVXBF#u5>8k!wby zs;jK`jpMPKeeZdr%V<4xTtD7iV{o_a6mRSO-IU6@ODKzHbwtZ~3nfZ*9eg#%WNod= z4BkX-&<-o*7XH|8ip%FGlCN>$%{cf2V(mHmt}WwxxZ12XgsJ7(l(2=&a1XmhGb9BN zRYB7XyNQ{(Y(88%6U|ur4YP6Yx;GbEwWC0$89Q>}$-IktleR4lL$%qKltzw(B@M9B zd=(`i3X_UlnX!$t0p_o8TRpzVn?IIw;crY$;o(JYxUXhy&9LO;EGF}QLJ+BPLQ|oE z{3#ipy|~8rE?(w(6QfG(4%zK2c03le2S_-n65$SY@{PV#0?dVKa3Ym_4$ol4HMczj zO-k0KwR6o}XaZ?&JG-+d!`l0OIv8Y9a*#YU%7A{eiA#8darM0FR1Qw@)5;=?;R*F_ zi5|&rNS5LwGLIH?Ca1r=;ud!ozeQ`Yfsn^Su^LsD=$|@kfv6}a<=E17I-#9?g9#To zqMTBcYqds|c%2o-(`0$Odljimqg_XFuS_D#AnqvU_^{M)SeRhrY5I+oITO(Nwj>{1tzCbX(r^ zOE&8>RA1rJTuX00JE%y@zqbuDyPy%%7O8ES+xLk)%aRA7TL@^84d3Pchh}06imKmv zhSI$Pw5ciY_MA{>owe$at)9x&!`y&SC(QW!*J*88lVn2R4x@m3C%#8d^!9zx&u?m*m9uUDx)U_mWHWi<$CkZ1x$O)J#nr!ZJ( z?BM~SeB<6*uIndu=efsBT7R^10n4=bB@|A zV|?3_s%4n&W3J};YX;GyN~B9nO^Ioqn~yO#H%;zp2B_~T$s#=!s)SUMe#|d9x_fDF z?ldUKAjVtF4e9eFJdUbg+WV7?Hm2j-$hB-{;@r2SrsHjPT44!z1t(E;VOgaK0=af# z10`#FJ-KtF_glqZ@L6nCmE%~*zEWP#iTD5<<%sSULcK0=eEWR}ttplgw-#~x^ed3DHDJ1DN>MCds7{?<6ss$A6)^BqB=x@?7i=EQ3+h z*0@^U_%!O6U1EC06KY=4J})kMl1RxVBA;d@Mpdf2KGShEs!4Ng$x2Bxkfna@0qnt9 zd>nWv>zoUiIG`g$R7aXjnFY4>0H>0jbRDNoRmZMmJtVYLuUcBStOuF}d$==sdVbjZ zi)kjwcfM|Y`*3}hlX#Z}P~t{?;u|^}B8f<^TC3lTFLh%ww=uoafFm5dS6}wOJGXK7 zF|yp&XK7Vt_U9{KES{jZKOgQ}(Yfut*5g9f(}Y$luE!1}B=!TC`ms;QZT3GF*_hvX zt7qH0?&8~UzRG8HHT)5&{Hs5Y`38Q*-Vqfl-j@pPH-wEVG2JA$sB|W$GUhGWo;81xk75=YH?4-QbF$^9>NDqQEs4gPAw~Q$6d#97<&yu zKAVUjwEKQtdenZSg7lizJnCuqg-}whb_=3VnUEThpw%uECr~}$(<~e3~yiUif!!+@E%BvQyTHO8AzAYeI?3G^-N!yQT38J5Z|mp4;2c?=aA)-TpA$mVYrW zz=yhG0ajfNr4OWaH46P$pM!A2Ipfl)6YM*yT?<3PK^u2z+-2#xMv=E}Yw)Noy(tPA zWl>B#{90!G2BAldvu87@BTHPLJ2PY(9l`0=`;$`RI?|9_3L4Z-bRc3IE0%P_rNdCH z`)6yi%$G)Zg$-}W*PA8IME9C5wIODLmDEYZ_8H84bR|&tWOKncRVoTw*qg2Qi*T20 z@=~>=hgDS2jIhr!WC*A#Z)xWVJv5@6EeUa3&Lo8jB{7x>=~7K`0e1XjVK<#g zwaIr0w(GEw31AmcDA5DOT(G_pCcZsXX7amyQ`NjwJu7jwc3ZY<$K9>rR6K)_QgZ;) z7I-WKXQ`_(zRrBn^4_9bh_f)^Ojg`#Y;|}EG^wsEv2_+ysuoGiib^wzm-fr?t~Q|D zrIRS8jwvdtlk^{EDwoj~I<#7j-MJG%(oU$AzAe%N^JO)3CAi^1wusFA7(v|+prUyU za>5C!VO+0Mo|3oSlBF#~l_U}qmM1N(8%0gO%1R$nQ;ySqyIcI3aOIZLq2mNsuV#2- zdt}aPV#gskqnoNq-7AlEZE~e*6B|0b%4DjH`r*bs{RV*OqjRI?8`MFnnK-G~6Sm7t zE=S4Bilfy0K=)#=p0{wCzAI4KH*7Y2Ji>lhZZWMhrrTynOOf6rp;ZI}*RCLL;UraC z@z;Pmq^6G}F=kSdu-ncmNTZ%u8bvx&53xnUfkWI6?Wn4R_>7^G@*J!YmTsmsc7-k8W3sj#mjVLAoAz}%lmzZ zx$EjKr3$)CZ3(STH+G*c{J=?ZkK}&7ed=(NOx6KOX@FSIleX&xXNU? zF4lf(8_4+5rv55-ielNca;Dnkcoj$PhT9jmW#7CNG1~hr%f}gSD=gW*ea2Eyow|bh zS4Fg)X-^KXr95+dS}7SE@au8ZFYvzgV@ZSIr~j?@2~QL<+R! zM`!|=f!cWjV=QvG_XiRgAd#UnxX#gV-n&O}@wM;Iwe9=FcT)G6;e8BA1t&r8&bS-f zv2Q<^-XS-`*qhr!1N3PM1cm6T$HqI(Hs{*gx?F}KyXIS4Euk_ReJ3iEnkhe5w-y=q z^tW*IJSv7Wvo_WjUTkMn=Hw0o9G^Baw+-7&_-IC&Bg;>k4yw^T`Bw;HcTu(YLY3TG z5xEmd3aWmgIKJb?+fF6nhvGxL?blGl(&EPQOqlPG--$_}B{l6GF{*QI>K zan$XBXxvu&hbe74ruW%VqdbeI*R)*|Ww^f~QA<(O+_j;=fZL@jQb_RuPkw=UZ!?GR z%?*8O9}RH8&}0=9{W*loE!8U0NCcfJIgkm+6O~9EP98CuQ7%3zqKZgRJ^hCT;Fkpg z1quB)@k_?DfJ%xi5HmgfaGZ(VQxf=)-O17g3eu7f(TLfCdut+0Pl~yPeyk(q6!*>u zN&(r|t`(V4Q%{pK-)W z+t|DTt~`_|MWiH>I(Ol+1+EnG*Otp`OKrGTljc`S0jhu<;|RzBMydrU2^6PZxNp*& zf~+^Dsq40K_SJQ>MY`!~haf`JOjx$Fx^9BS`{!-u;bin`lD%gjJJ0)qKos-i!&=X2 zK1fe*yAgZRA}^#-m(fSktx20I2+#8j+tgFA)nNQ6hl(hy>(w#5J4P z54LI1Hm!cK&;1VeJc#H%~PoMFm0=(iRu zn=<2ajd1do%!I{TEdY>uxbR8<&b6*DH~}|70i|P`>w98t-a6!HBhyp$Rak-PdS#u4 zrE<3;?frocM~mcygK)S2ny7-?T2oQ}F~q^Mm-mPZG?%&Ulz-X8`46gr3YNVg>@=lO zZ5NJUy|xaS5?m0i>QFTv1nJ7Y!Hr0WpnQ8j!&7l?2A>mZnWxi(&W1N?+W$MKqIpg z@L_EsXzO=T^eUO%ncRzuEus)d_noW0fy3(hQcmXe8@#2#R@~T-A#rBQSSTA`Z?Kq6 zsl3G;c_Ynmp3+sG97&QO2F3$Np($gqBJ1;RoMw>oar*}a0XZSp)asv7ol~Z+xKD{h z=Tb?j#9WfO{8jea2;*%UC>F>-AZ1hU!gN4n2<&uuG9o$+N?vG?dVFID+uoe&pK>LGTDw*9yJ2gNCc8;Kr2&)n{G-$*FYK|g}PH!SCFKR&~i9M z(b0L+f~IaY+s^{4oR)(OG6_?z6r@lL&S$e0JY-ZAD>mL&u8{d`)TjRM<*m21ZG>A_ z_|(8W5dyTb7t*REXY^rwrhTvqH;S+A?%rHoN!2ESQqAee`=fcfvvitK5~s<4*MX!C z>JGn7Gi;7xS&pT2CIe|&SdVaP;+c@>puDpHir2LC#A6**VYoC!nJZ@G%76pJoX=pW zt`&+>*MfrQ-h}#>(aSA(fKZ^Wc(R;)ziWB9znJObTM@3XQk#67B8s#U)2e z6VMNla6qHUC)zrXVZ>eTGC78$ReRc9#=OS5fYrRmB}O;z74H3p`{GVudV5L2#@j6q@zz>==B>C@7-kfD=@+^D>NCOd$?Bg`|=~Nfa5J@RZLbv{9lK zhk}}K(NtA?=suiBvx>0?d#ZbVX^FPB)JDmtk>)Gjql?X>x?^g~VP{>-&!r=Y2_~gf zKKwoNliwvDYC#B%d&ZuQjLLivwBdRjK4XMKA4$murOQgcL2v(OOfpkDOXV zNJyzsH5jGu98+e7fK;s?w)x2|ZVQQ{LT%sVu|DSPwiH&{fB{O(t4O6u#pi6=Iy*+S z+?5M_i3#O?=ztoJs^u*H2J?wS<}Uo9`$FB7e@ z(d9n@=NXXD{nfUjg$nz|FD+$Mjitz%AmPmdgRCT{Dt2I~t{NfQN`kY`EU>hE5^^JL z_Tf~D9Uv;Yy5Q|=uK#v(>hn*hb-Wc ze-!xR%PfoL^xO8>)Gs>tSOkQMGz+IQ>`oj1xMexCfj1?X~8awSP z01n`BY3Kg{Dd+0r>s9%)sPy-W;=Ef;7R=XkSt(Ez>=;gz2n3ZYO5rYmjnr1uCY{F~ zqIB6Rf|8P!9Vajciv)NCBRKoDUs@Xx)_CjEcE@QeEy&v28VhYQKr1e$)S6Itl`9Bi z>|E(V3+Hz1+%Zd+kUa5u`&SdK*L~=eAPZtz$P$NjCo(ha#+cb;-~yby$74zO>*`6{ zzl?7DqY^BA!+iWE0}Iw5$3Rx4QiD-Z!z_*=_e0-x0Ps(C_V~8JNL_CqTgatJO*p=g z_EUi&+w|oWj-a%Ve#3}yvf=y1PtW;=$+v`T`qFLx0Mh>eA6g7{<6WmM#frJ9ZPTy# zxI6ZQ1A!^-C%##GikC5z`caD?ru~ZJ?U^&?xDY`?LeK%GWD}Sxk7SOKgHhNI=ptWr_}wLBU`*vO3~Y_i-kr9z(CV2r`h^>|VSo zBq=YJXymFlsu?CVs@C7)ZA*3M7EDAhSxQnqBSY<`DchP_GK;Fa0gmaAyQYec)TvsJ zRbw(7O6&%NJ4;A3$t4+;U~8&UV@49Do^7?N#+`v}c^*p9VTSQVI;G%NpRSk;hX56S zYrOe#JJzP}Qfm>Q@SThh1)phK!qb%koEp15!wj5tO6@59=buV!>?G;7x2R_Gf{O2< zt|ToRs!Mds?hve{!e7bld#r#!{_L2fLhP@uM|_S&+E&uP3@bb|fzScv zqzt}{ST>AD$tTjeGk5#fM5v~7o=(lMv5+o2rDd03&l)=c=?e+z>br6DD>K6^r8{a{ zr{XROp7El%a~RQLd+qhR1JIui)Hu4D=aIqOsl1TwDQ&oE;z=2f(kX)k2T;0A!rq6r z*EM=f-2A+5lUU>!#_s0VgxiSuK;@{ZC`+xBl6=J=gF-SR9M8Nvzjut>&9o!fe(O66MC>ex(# zB-FNCY!xIP(lDL|OkDN*^P7+BqkM(|hb2?ak+3+cI`-x#Vd?NDutz zZFD10GBlDaN{m;naW|002VYV2t990M9di1X77s#a$X3pf!hTNqA*aAso9Dz(K~Omn z<*1zdabt;-QmzWSc-umK#}zmFMU4?Yqjo?<#7hC2s)CXf4Mnl~WJV|*(Pf6NxI z*f0M8$jAJS`xRilOe?!x=*UU=$$h{2%JF={M_Wb@@{h4+JFI{1GXDV1@BBq4#=12U zOi2$#`KB0orO1b?NOiO<6{A%$)S8O+VSJ6dpKj*2LPBMQx@Eo8a=djlt^|(|4-chG zJoU)?Ba(R$JByAstGg#@qSbDPA8$OQtxP`^D@y24rHwQx13t>)rQ7@!<~qyDxPM!^ ziLb3(`>VSwvw1OdZ5fT7AW-@}Nw2o#CPGAa94V`YvUsf!uQ*WZwYXFe zPDkH>XapwmyoXo8SxzLp*mtd-e;n=za%XPGl-ju7>lie2IFPMz9}6ke+JS(Hz1k+0C4`K-M($Rm5ywPH&_orn-xVeBfQ$1T2u`> zDw%g2S+TeATt*(lP*!!H+jH5@=%s0Ehl*dbZ%A?Yw|a4<{5t#dr@_1INxmhBb0e$E zkc#J}FE(gtl}324WOn=i02Pk%Ed>a0@&5pVvSN3bI2ke1ik_fYe(#|m!Ibl9N;T>o ziv&D6A&Q^y$ldm9{QhyJ~G$B-pL3sn4EyUO<ZCyv`Sq=nvzhYO&Y<#w|nBSwD#3VOOG{q*?D_gOMv;? z*3rpJk^MmL>qWMH+SpFFur{U1)i~{*0uYrQU<0l7B)FqNnIvMD{@z)=-fhRe_VX)V zi@lj<#BJ>V0EZK~1n}V=Io&*?yCC_DKGl%C6?@!kX~N19k`$m^B85laik9@;Fj_!k zh>orl3t8;Oz{q8CEuQ2S#0^O+s9Vqdn_B+>lQxq+U<;JG<@F3^DN`g}CcESpf3F)euMl_~wIczWtjk-RIn=HF&ZQ#p4_IRc}lGAG2 zTM85lgeMT}K^ibYOK8bK)wBUsM`zQDHp-IwsnSgIL*iED-AryBhB&Pv0nHY`c*%?on=+ z+mo`>ttz-06}aFkRCRt02g((7 zz$ikA_GgN9_?kC{_^n$Lb7?#ecZUTz@T*M0;r?CZD?RDUX6LrVFkv*)g|)4hljGH< zgn~%=v7T;^<7DE6?dosXw#|k`w%MNQCzi>J|a`+3_t zCu6(3?%SG55o5l~pl;i5WuR28CZ#5+Qi&Oyg>uDjbK)|7Se|)0K;`1J{@-Tsvt0bH zJ}#>90FFR$zjvWno%y)7cNA<#4Y@5sz1$O~Bvw)qxeuzOtz=Y?n)c#{h_Rm|avbeg z7e{X#^^V`hbVMmDf`&K-7Wv$lIoEr2&D(TYJ{z6MK&#A7twkUzmBfzsxU_~ZE|n^u z*_%A28=nFkQ2w;^>Irq zDl4cJLUrY(APl~thWrz_^=jER7w(Ux4Sm~6Bg}rk2*7;HLPfR?c~Tv=%@WlKG2k?o zTK@oAs(6;J(oQ1hxGpNoWn$J16Hj;U26poiqoPY9W6KYTaH94(azz??%>@3|Q zOOF7da`{GExHh_!wFB>CBC>?CK-VM+f@z*uj+FGfMIml_*;0z!tl=wa@WRl{yLr}{ z^iUm#4(+CM$4N+D+ck&G2T+tP+k)A8VaFm$L#{G(zRFLDwO24k8{Akqn$twJmQpF( zqqRx*i>o$xa&9t-3`9v%(mo?WS`ThLXJTe`0wzl)+h)}2tG@nQ+>>Ht%5B>|-_}uDD`S*~lRKq~5}yXo$9r}2vo6su66GuJ)Y?LxKqj@X zT8v)t_LE&nS&ah2^S5@Gt6qyh3%#l_g>}DOW;b-Z!I?TVHR@GdQ*A3rtqQA{~c3aW2?1(P>Qc~xYQk2wH#jMu8+-LifXUiOY zQPst58#lUEl1b&yMgSFN^(D&lH+JKfw07p{es0KuQVJV#j7xBJ_(0UyJu8vL?_h1F zTS6p}zWV3>^mh_mjoF4LlMqmzz~kGN{@pgsuW4&stAfB&r^1Gt8L4VLiAc`4yly=E zZ7qwBC6w&lqHv=F0fl?78@C9cpi$?RPE7z-<;nxz>TWcwV7D+7)q3e5{+3Y)t`vtMzgcE z#mD!au-v`wS98okC6N6|r+=Nb_@cJ$w!)?EMK(kC#}X70s#2}j^x|sD(&GOB-jLCr z3L7t$vA{FRw-uMgHnt>teC1yGBYt_W?cJ)Mnvxc1CP_z$wskA0CY1vYvO&&pwS(e! zKLri993g@ETi=DcfO)5%2=ZM1N8h{qm-i$DN89{EW|Jn;kdDf1bLTS%h^r3Gi+d~3P(V1>PH3zNEHzbak3afbtFTIH=y z6gyodNlu_BYtUjv*gNZMkYRW5z#1l&+p`mrw?8u9;u2_7KXZi)KI^dGW!8zR7SgJa zJFC|$TaJwF&-3TQXaw`CeKU1H#^kd$6Mfqlq z2%4iZlp+^vVj&J)Hj}We%y|!~R5V(EduCJ;6wC>gAjjO!iI;PcnVl1{6lR}rH z6oK|r89vLr7g+bgM+$|~ebcgbKXOBI(?OwAKd2}%w^to)epZ5?GHfOz3z+9x+_lA-X}f&9grFt(OEpQL=|hpi zTaOUYCj>pc-7>z|Ij&NKt>w8ccLp74NfN37h=2kW*-Y_XmlX>cRi#0>F65I%ELiRr z8Bv0+FiZz)O$35S@5r1~FPZ_MT8=92N3-7=r!z7N2n9zz%tSqvRyZaAYL22&s3hbo zLxG}RNA)7I<6EvMQYS@i^mdgXew;5I%#1Y!F}cP;DAXoAjfZVz^7W!imetn2QWQ&P zx27dx%eeeXwLaQoO=C1o&BtTE$c&h;FxADjvo8MmrhN`3EOJqGX`-!W@Y5F4ifmbI zkXn*|6icw(Y}E+Sr75RnaU*$p9Ni66P9GT9pYOJcNwq33^8`mC$q$5}>j3l=#2w4) z61w8pK4PIq{H-!1YosTiNpV_9Zk?1kklV32?9o%V?euU>S1QSRz9;e2%eO>A65D{K zB-9h_#*+n)@W?Bl@)?2iAOajZ7o*Yj5y5u1YJXseDPM?zQ9w8U(;lEChsO4n_L ziy;}1=v01*l6KwY)Fx-lMP*t_lG>=D+e(Z{Oy`!> z?W<0T6Du4c)Oev!UOP)rZY?ozWr?umP_Pw3GwDj=FFNw*>NFuwY{JG((EUMHQ}7+X zDL_AGj`~(J1+?_0GqHO!PZhO01!*gO5TKRY;O%|qbF(5TYTYwS_U6Yxh4lm6dogW$ z%=Y2Oip=7IJ3Hea08^Q`%qC=K8%|1H3J4h!i0KVuR+i97RN%cqOv_A$oM2QcFFrkeBEI|ctwXb(IOHObSLkQqH^vv#iqzd zJ+LknD>}$SEv5NmO#xG$*WTA_U>19u-!_#t3o?F+^~F;ok8cpPOjd3|M!2P$tf?1B zGNUA^zdZ^?2p|-wQ|-ilJ^oyvsjRb1&^f+^%}bWax5VDiZBtb$-dvXCa_dTUqNJ%r z0u)U;(;?RzTe1V?+Ct^|FtoaKZqtg7wi|a|HRncJVtS&c*EE+_?xbsMpWdxLAaN(S zEyd-9NZ=K7XkR-Sn&~G>uHkOD4Dv4+O-msz{OnY}4Si_iU@Q^ssK+_kJlkCKuHwiB z-dKl?bX5|=xbG2TOS!xK<#vDr=}HEogyl@BT(M@qfv-NbLd9M_AWwpNK;E{iw1{(q z;iNo}w-VvfMqMaQBAsCNX+>tX%*7_8uWq}YxwILGct>~?m8OJhLXTk>HKn`VqV@gc zZ567fzO7^0?i@pKlElX)sJg;~s!7sFzC=e<}UAQe0v6RcdH?DA1X{iaz>82s( z_>`rZM{OCX4~M@3mRk>RgdWp@Kn+n<4JsnNFh+{%yh13jLa9pqKj6X8ql{c`Fd9=a zWSHqzYST)C)}YXwC|p8rV-Dh!SG>2GX)(^~nq%}0dCSxKaF`DqN@Q$dJd#_Ni{tu` z_1Z&-Af&ckUXqe1LC-L7V)r8pSew8i1qlf`eOT~LPr^dFTLMeccyu^c6o?O&)Sl1{ z4nx^oTW#N1sJXK<4^COb#W?*+T0H*%PrO}zE~UlAlQoybxa3r?nr_vUG-CJ=s9!CEF zl&3eWz=d;cxBRrH%(uh%Yei8i6&3Hn_v3%>N%0CF^7$b$UyXZ3MbV18eG@fzNn*8Db3J+_ig zi?!9HJe1P4D2^QiFX>kjEy7OWQ$Rf>XHs|H7Uy(Z2g>jAw@+zELyf&Eam&g6e&zWb zg>zt?Rt>osLOD>#rBAB8y}E`Mj~!_d{v*jg0TAvj*OJoK+(h5J^RKf{u`D<0$BDS# z%l`mlo}b`Gp4C$ya^4(FnP;-30OGUuEMQrjw}(ih9W9e;M}eb(qdQYUEj zOU!(__5L;QaT`l5CAC7g+M6(1N*1DpAePirtf|#lmBZ%dxFkDDcqaRkwD^2UW7^K5 zc$aHmN@vVV&ba!eIHkxH`H607TIo;XCZ?eF;$`wwYKG0Rv;fgT;F<2!NOdfrx#d-0 zt0igf0Clby$n|ozlm>*|c3R`hM4-JgAuG*=sAL{Th!aoY>xlQ7DeG@|Q^hjmxVbJ! zdEG01LX@|ir2>l1qG|2K6pkdTG$M@wHzbl5kO3p9?>%tNN7X!QsFPjs4~s>5_Tidb zJrZypgFfsse*~y@-M$a)8-_9`AvxVBNh%Cu{)CGc+#rQ9_1Cn`730C7ms#JqI9 z{{Xp>O+1mj5SboZh&e7LvAry|dqS29u=*(Npg!p8cZvOqCR}E3CMk0#lONoViS@Zf zTl<)KE0dnry!TAI%W3|?0+;2)X(OUrVXZBs4$QD?i|Hizim+jJWrdEhum^aZPqjv9 z3r=b^%Pcf$qP6JV0y2Q2Kv#r!3<4{iRACG$YNAnI+SFi5Xl6!(X+hG8DMWaWal@J> z1nP#Cl?cgo14@!X3LTWgd)WQN>7+TdPVMkjF6$EL5tY^?N5hr)kCYHje$?Teu_`{u z`=okO6B5t?V&faNL`#GEgHtu=%2?a|kA_(4==E!$DK6G-_x(wH&aW67HGE zyNXc&Y9THB2nsX@9w|YpDDJfaN|aUaIA&T&O9Z0^T<577G8|GDM25jp9_+Cz3yIM& zyJSoqPNs@t@Jbfel+*ndx1tUov zl)`faYiEZweJ7&ax>Tlsd|2z+0vE)ia1e-&kU$v^ZV_{6l62@|f;!{QZ4zQcMq~)H zno}~^OYJNWq$mOiB7_>@BM>}7d1;WA2X!#es4E zT3G6p3VxhMxrA|6R!qmXtu;b4;nhLY4M9R$mPOKFWXDXEC8Z52NF)ND>>H^!@)1yg zrK3UAYAQaQ9xw)u9!Ag>$*g@^bSJ+&| zI{{$?E;qljo(J^E(iTFV(6*yUTPbuDzokTKNzRA7VqLb4JX1k-T0PQ=d1W;n*nGwS z6jPm2fl#VIIiAcIpje^SPhlM}G)s;Mlxx(@m%8@8_qWwEHuR=sH=2g8DZrwWr?E+> zIHv9>e7RdNURIjp~CT)WG35`1xz6>~)fbf-Xb?!6OZ;;xWd6kaEAz%@V`beC|^te%W zWsUS#F*aD79LH)1)D%=93e0rw!cLD>A=O2XEk56`3Au@OJ$b|ekwPds*E}ZngsaC> zNbz08^xx|q~whBH($pwbIbTroN85-xo|RXl0HH-P$WKdu;N++#&R9Qt}gRrAq~}YJ|FQW z%o^I7u54U+D`Z-C9^u<_Hw1Wh4ZSu@DXpzVBhHHQm|Zn>8j^6*43s2sT_OJ5<_t}L zluu9x+Lj@5UdC6$x}pQzT~F&cb}ykE!iTxo8DMpV$T?fPkM3ncR_27Y&;*k50Uuew zy6z9W64u*GGY`WPg7i!F!EL}A-0_!AgU&!1dyWX(*ek^zUALHc)+6joV|HwnC?UJV zcM0l4!k?!KKJp{YF@Dj@H;iZ4lvw^D=c^J;DKE*;k zL@EPYpXDNW6c9dP0aZvRI`ufrw@l#4K)Xk>)>5+8uM{e_fuLyyqrVj8y|I)=+iWa! ztw;n8rly!QV^oe$NF03%)_Sdm(m5u@XJI;7g<-L@l%Dm*?#}^!4^UMttm15>J~c}I zsHB&7{fYrmrLM)A>F0B`Dmnj`So5E@Hhl0UPrZY=vwIz#lX zP~9?C;pfr&R8fCdlW5x!tDcQi_v1<6&=wtN+Rr1EyyBKyZ)b%o_mp)G--Kby28*A! zle(`;a66#5N(m_kp}-Yg91+Ovmxu!*dY&Fz?b|fv)9^)os z@h+04$Yo5K$pK)thMWojiu^*j#J7KB?eke-c%Zev7;Tx&!C`J^n9+^mr8d^lzRX@W*44doyMRQu z#=_nleR`9&x3=1sw(ksPBT8j$hY}P}RNB-uxR9Vmq>?aQzqzdPaQU|G4r+6|_Et{= zbMp7GnBVFBNt9U>%Akx`l~1WzUq@?Gexx|{AXK<6Dbd+`63?{!+6(x)a&BzF@fWQ<+#5B~tOkN#2iEbDax_>ABFqyGT$ z6oBHJT_}JE9@-p9lhmu?p(BtVwLohfw^N_84_inG_RSD+2=&KMml7}aW1gnQj$`%8iEQ)Bi;@JA#B;0SndTOiO9)maM`0Yb4^!DxYv8} z2RH1igPh-fnHPUMEmEG0-v7%nNa=_xKMic^Q_80UQUD=mk*t&pr0sSCFD zhT+?LmmW?@-8W=V6bNUz3@5}!H z=MKs!Va8;b852iOP~r5dkLpJ2w=bOA$n&;@#x7f8<1G(c=f)vSxV5l?=|N6JYp!Fr z8%J#|ZlS|O%YZ}2j}^(C`pVecZfW0SL!oh~BZ7g91OTNXnRcA9qP6u{TcDy>vRJMc z`N+1|pu2gvs8ukcwzr(1_8=TX-{vi?n^_zJpTg~X?1Y^VFu19f-@!YU5}T9Ams>v? zS_%}uHtBSVnZy7H2!_&>>WS{l6l>1nM)T8Z$4Ku$)7rL-FJ$FwI`5@qY#-*;9fy42 zeU$ZV*4ezLdW=X^yMlxpKbe&XVF@d7J=HjjyvOFRTl`lDn0z>^hAStwaZaVH4AKuC z6;jH1!{y>WA!S3;!W{nvfG?$YNKsvkk_ejn*~q3uR7(bxS;O5OplV9xpXIM_hp
^7r zw{YA}smIxU3P1#`sHG($hp%bG)^afper)&Pk!z3w-1g8!Ex&_7--4Ruytp4M2FrfuxKDr1V0b$!B6G=bP_h?~Od=&cyWMkA?H z&dZs%Y#SqaUtgCE`L`J6-(`m3)hU-8qLlJbX+Q=+Llhj9oYLO6kPBbIT-rg1c{;t? zCag>C#j85)aVI#45_J6YNLrHHiWH`;BvXXE(&V)z=9@;hk_yxipa=lsvAL`*Uu@?+fyRpM+f3e0ESyotBbd^4 zQa3A2yYF6Ry4xgf)Z8Svw&6#*NpW6K4Wv~-*TO5^i0!uwR=BX{Fb_hrZ_Jd}+l|q_ zTKy_2Y- zcCGtwcJc(qL!##A;g)wu@dO}ic|yG^NEqkt_2-G4K-0l@&*a}_23R`r?bXLb=H=@I z9o5R4duT*Tq{_F%L(^K!gr(;Rtp`dZXNU|wK-yyq$)P+Jg}b)@0OGriu<>hnt6jHj zPTKA8EmmlVwZ(?gLX)Hnz|8we#!ZpFGQ7R!q^_yKV1=|ID91!IQ9xg(CY|OR32}7`L?@v^E+@N&o~=Bk9W#Q(U{LSfaLN7P2h1*$xLB ziiIsdz3=y6g7QJYqQ1s6#4SunErl@_A_LiKSRFe$1)$!y8uswnBAiCf(&~>+&QWf)v z_onIF@gmqCOV<+wU2Ng$T(i6O)!r)W%9m5B)#4Wm17&|Z!W;{+vCMS zT&K*lx8qQ@^`07SJ1)?p_+^yP{2mEk&cIEZb4ruBA_qfIOPSjh0vyDguRbE{*} zE|lGK$F}x-7C=$c3b|Jpl3MaZDrqjW(EyJ8x?@+kXG-Ur3!w4R)nK@1jja2o=;7Y5 zKFjW<7sZDu~k|>(ew5Bt?-MAkE0EGL?p!;31;=PcYwvfeP?tBTt>uG3@8bl@k z0IGB6lvaJTRdGXudE`eERkq!BqXKkpiJ|`hR=tk06x_*6Ks}FT^?)$tyYBFDOh;pJ zZqyP&!SvBEN8I;=JIt-`jX;G;P+)dG~4+#j?wLhkRjWIo`}k8U7QL7r6NU z0CZVar9Ev^&Hn(#vO}q-e}_C?Y&QkGjnL6zn~P!J+nrlbqj#8CUX>+CSMLG{>5D6F z8hC26ON)24-teM1Z<6dM+jX^QQb;9rBOq(kbippmUl|VIpJHxgx43KB#1O7eJ*sq# zk;Z(5aa@PplL9j>v8!5JB^rU~Xb2UqGA*MdZMu>s>8V{Y-e{*d*e_pFOG28xIo%-g zp9$`bk#Se|sHN!iJ|cw3PngoP{uN@Syx=xDR?0L5YyF?Tk8y2BTRT@Y6%p}0f$LnG z-JakpakIXpXssv$ihoumGuKU$F!9VN4%}m11oA+5w5jEkO@k^k>^4H%P%1?$S{mYw zA#0mOwZg~6AaDwKv|N!kBQhyPEjgpO^f;v7Tb4eHXT#mLNP!$uqFy9XBd<|hGg2;f z5tf|}S3RQ)Aw%h_$5@H-H6-p{Am(!0w8!UW6THiZsH^NcpTV>%#nM#MW1CJ8q z?nXr9QM*;PB*^kuX)Qle7o=vOM{Yd%ok}F2fvj~xV)6#VXLm`9DR%|KTtGrXXi}i6 zR-+QQOz+EzQB`+#%x*#5MA1Z2coFvtj>o*AOp{chNHwp~i+eYlK%omOpR^_43z4Sy z!*f}zH%RbqEIles6qZTTl0E&nD>noZ$|@7LY|HCAV%XdXP+uZncP*!HH!)%=7ZF9d zjfTNkB7>+I99(eG+s!orrB+kC@>lkDoo*HBQN@#=ZE#+MtBsTyDJe{eEoG?#*l{~~ zvRvSLT~$uP_r4fh-nQ#iZYgNwJ-Z6$4FTA%q%DLHH2_6v@5Wl;Eri96E2v?8re?@Z zA25+@`&wiRtalEUqBeiEQjdn7%qe$}jqy^Nl*1QJg}yu0bM00Ji96F!jrOZX66Si;aV$bsM^hxMW4nLk*lwmz@Q;s~Ot|ZzW2^|P0pu|>Wdy2!h z=W`uORT<|?B-s{wM(u{!Q?>_m+|nym#&asve+si0b+zsTs~JtV^_#;)?HYv~SuU~e ze=9Uy0+Sa;sHsrbrnS%L6vVytl^Q~;;c{(zgO!eMbPKFT-azuxq&Qg2az%$D24Y3y9m&7sy3T8xpU!sBbMprnqXxYzbrWyBpNbANMWmdfNjb5tLD z-d6qo4f+27n~^4JP#$?{TEd=e(14+t%RFkfwruhX7d+-NQX9c8p?2reUn4`^yl)Q9 zUsC1T8%iESm8nXGRjzpD?em9qt>;i0Rd?5F_bYNZu)=-d6idB{*<0%IY$^>!nBiJM zG)kIt#GH?3W+rjbd9ep`Q5lE)yzXZrsy*9xzqR)U;(>S6>l5AcEhV)ShMQh_b;}pK z94NSDd=@j6-11sZS>mNZx>G-5$i3Y60}!Ly%PD5o^NBL$Ax5Q01k`{r8vH}qKT8Ew zrJIe0)ILYiMSh?p?Ty36JDBZT^Umh;3WuJ6g6qfFRvF3ksOd-0O7@Eecil< z?-E@R;1@9V^^vQ>Hv}g)owC5dX)a1k_g2@K5J&;KB}AIzpl@$Hx@lUjAjjJ}ZmOD5 zRc>c5F}5NwTn{i5wKlO`Bmikciw-%>G&~eb$rTFK{!8~1z0N%AwA=;6?tO_ZNoJsc zsFIPLJ9YCC_#K&4a zKgGqG2|tG4Q2P~*)O&<@6ZVwUaUn{tXJ#GMJLo9CLU1j0676*tt5Jd{3&Oef_Y57b zqB~tg1F1S_Dr?zX001^>c8Jp+8>c9I2-LLpwH{Xm9@r{ zD=W+49_){tI8jbqw;{{FrJbWa#rf^AaL^gfh$*UTPi=8GaQeautL)Yo^Dk=W>p+hZ zMMA#ZLaq8jjv_0MYVzAF4GONEmBC!vq}?dyfDN976zPI_NX&B2m6`C9k9lcIpjO#V zXC$2KpJrG|7%a8{3IJt~P^T3Mdc!77y5$yYZPjcoD?+U zxnqumQ93yuCiLaK+|}aUV3&KIomZd9Xs>5zo3zP10W5Y^Q-cY3#{{W+e?Y70^zjmIr0h#>3 zZRkxSf0%q42~xYBiny;}H}jm`>N3q`aTG4?>qCu6n_5>--jYe|$+L-e?Z_BEi695S zDwq4wnZ9>UKFRJMdDR2hKWc33-S_E#gE#E`!?$?;Up3j{GSJ&G_X3$|rl%!ZmhzkO zw5>gf3U3X=tzjoluUebS?d{QH5@}zP~iyeZE8%emJyQxQ@yOmfN($ zZRrUK?s4=6)ATill4i8`MG`na%f2cn2VA++JH^NJ`&9+r+@HyumvCLre{Y+n?2_V? zqx15eeE~`}IvXT5f$T~~Bc#YZ+(}zzCu-yDThjzhU>*bY@k|6T93-#AMi|vtjE!%h z3*;}m!a53Ih~8qAcxf)LaZQH6sg)gMqPjuy)RWZv@o3uZtfgi>&!@{Z$B|R?_^{%* z`tFESr$PX38zzaU?<2bxq62uWhRSC;ojL@NPlo8aV9;O-alO^~5E^G^)DTOgJX9?3CA7(vRJ!pzg#$3UVn5LXzKF z0Hli057UNIG^rTgla$#$FbavwG5+rC6|tuxN+j7TYC2Tou7-zS6)?);W=HWFC)Uxr zLqHM?Nwe>_IMyArYLk4u&QL|Z*AhFjWj>;MA*NEL9=+IwpCn%Hn877Wn<(In$uomm zIFdjE=_=su&qjC4HKH!@^*O#H@P^xRhBnx%bN58i@o7X>iqOqRYRZW1PA4q3&BMoB z=b^qF{fjTl_h#43@-dN{nLKq29<#*#Ny@KK9DBqJ8y}`FHjlMrx`N!^QoJYe9my

9FT6I7&tawjp@21vEl zLbS$YAW#mf&$k{(xTP24RucRqit|@L1<`cVM=I%3=52_A80&Q<^x# zYH;rA;swAL-Ql}pA)9LBsVQ#OQsNdIbc*Lhnjn2RXSgofAuQ&qy_niJM7$qE(M5LPyK8Kvim7psp5Us1r$}>(r+f+1Wd9E}9B3ETccz_#XW!iA$NM_` z*pX@6^A`$aWfY_=KN-{~Gu)~g3~&46GW>oy^EZoiUm`ncf@V)9+joBTe!`%=>$Ifo zed)XQZNVxtcBRgJwe=}FN{lH>kE);23DxxCTL+SD?VBbaya7-fgBbf8*)840jw9Hc zN@c}_1TpI-G?psOKy3$Ek$dy%K zb?~0tR4;b@HEye;Hm`W%mHGI&e>8FK15yW^Z z#~9HZhO%WxNgY<4{+t9)tAa-Cff@BFn);+7;M;Pw1P0roO$i)o*CXNs+A#GxCsY7%j;6S4Rq8CI@0if6P^Hbgx#6IS(g0esrDGBM}A#|Fy&0L8U~7~$CbTXS}lrOJZZO(_0~Dha>`J3DI=+EJ+=1Z3GMe_WHE=_ zIJT>9?my-pCcn$JXmJXfa(^>#UR_n8&D$E>!mVLmM2J@Su$^=%H2dcxp$c=q#g3BN z+cGR;3s~ZPYnXRl`I{_yqyQ1%qST-YCst|g#m%jvqxp+f5CQENIwA&wqgGVMs)Q|) zk=D2c6}Aah9= zyp9T9Q(aNCAs~U-L-d?_iL=Ty*cl+2C%3Z;^dwvm{{Th|*3l7ciq@c3m@+R}Mw=um zlARpbS9S_rkN2&r)-MDm`tB2^y(#^w>FOr-EN{enON~kCl>&YLWV&k-< z4xFDt<}b|dMbP3{(arw=U|my#wVO&yg_M<%K(7A)s~V_?FQ0c=*Aq(UvHZ5=S5UP_ zqLxWX?7}`x4N(3xBy9mti*2{%(2{C(4-679=!bfF%~$sna_k`D4=cCfx^B8V#cBPp?}k@xP6JPwZceN`K92+G3yQqS z!dv@-@8-|Dp{4t5wI9IuJbg=nJsn1ckTf_4#M-<=+B3#glE%qy{+In8mGsZIy~@Sz zOK&>W^&orAKNEdpnDqUe6jIXCx|HTJ&)nshU66w0{bZXbPBFUR2+GLR( zl|52VU;_+p@~?aPLc#XV(r1{`Mzss#Q*mD%+?#`NZU@n=`~3N{ma;`jn<>dAw5Rdn zRq_x3@LkW1v}bq9{gn z$GqoNITjK41S;e>NhMXGIbc3*$bSrl-?|r@xlD{TH6d-0+Le_4j1k!>8aS^VyK3>P z0!X-A1mzV;bxycwN{v*0vs^{o-u0h(N?2f74vZ8D1RzDON z6=KTB!(u{T=WBqwa@FZ*cg1VF#DV6H0dPYIgh-M2ZMM0*R$RvHZ zw{2?q1J=18bDB=_qRq(dU$R9R2HHhFnqx(f>J~L*-5%w2`0<4|wHpOr5Xk=kk}&C- zE^OS6>bxZ!bHcUf+kxPs+A5Djb;3{(nwpOddgp;W6Np*wCv5g#@|z8`b)KUQT7Bg{ z=*LQL+##h+u&|{v^J)c3{aBD=!;KRV$`s&+B|6K0-gN=eqNBgxi1}MbDeGpEf_;U8 ztzZzN*baYQ3LI0D8b7zUF67wP%Wmn~H#ck>ipt=6*L7qKZ8!l*AS@J+QlbbTf({YB z$HRXKf^pQxaS8SpH+hM>ZOyC8C27!vrOTY#${f=o%P*3z$-hb4?K;#4#Axxz52-3y zP>z&>PLYN?V`kXlnC9BHf!uXc8}GQ;O~*53zi@lnCPm?8`ZMv5kPw}#?pNbiZn zlk}RT=O4F&ovFb}Q%lS4uXY_eLYfBpQD@wO=ugYQb%K%iid&k2KFqMATzRCrTsfg5 zy4p(#C6RQPM7&?PeCE+C}z zQXC01{W!5%RV-h}14azk&j!dzh{ zfFvnN3Q$?^t{Yt9TK1Y9RHi#}x;IhH6jy#OvN)Eb2J zC|XqbSDdveWa=6PE2z?-V+;8^ox6BvmHzr3fOJ+ToRxMjqWk3zl!J<+qv9$Ov;y2q>B)B}*#H z+7C>5v82P))xxUcY1^<{xr+TeZfytHP-cC(vg5*8QbGc#hcu#&%u+Y>`B-gw ztXdrZ00ZE+9?jcr+l&7IFD&xNh|rG}Wx2`nlLQL-VAz=9ETKRGiW?B!h{D#M)}96JE>Iv~KZv`KDV+o(?HV478nE ziBaeRb2wo$B5O%1M;~!>B%z|JaJ6oATjEH7{iM4tUV!A4l?5W5KuHxCqTl1%_w|ko zb-`t%V|=O_t2eNAYzY=OcTVti#w=(b++#N79+2Su(O!bN6O7|Cm2i{Gjda$|+R>YD zK6A^0K-@d}6Pv$kvmVfk9@Mb74A}61REJ}a6M(HilA;AMCu@Y2)`W*u$C*{!uIGih z005&K;HY;b?_M_C_r1>iSXcJtNszVe+z>sUAu2R;dKCre*5Plk*@ukPXVlF=MR@;F+DsHZM*t^GhZT-n>v@x`u5zVgQ zAjVXO)Yx=U!Ze{ePD(YY%PeGjJ{!ohHLrzYB7TN2AEOO2JF(9uXF8hZ^fqFUbzm|G~&I8|d=Oj)y?(34WrN~aaB zM&FCF_e$nC*u2SGE!)~tD_e!SRF>9!DWUkg#vw1H{{XvastnBZy;wx}Z zSpY8KaTdml$DD7rHmGaKb{u~?cu)YD4}I&0=q8^=IGc9mUwx5umEu>~?%3@(u(&AT zV5J+ctrXhuY_s>J_i(_r0eri8Y)-hWSMDPX2}%j*4_vU@DSaS>Fr5b_JGOD~y6Mvf z`^4&0+=ddt)SwEeD?yeiQ^{4T#NL&wjQPYUAO(Kv@~FqU$VKwPG!UQwu0!p@4s}w9 z0u<<@Yto_BUDyhsqA?08SV;rMPsF3tlY*nQwMxla8*NTFpoZE6xT0tP0=PRHRIxM_ zP5AN`aCF>OjVua?O`qWh;`(r*0jZexwJ1j(p(OO}7)_wnD2cftCshz#CZo&msKbWw zKzJz2bnUs<_LUe}qQs03-pg}AaE_*wPhY1Kwimu$gNmbO@Xff@)B#6(eT}(bySZoC z@QvqmI!KWsrBR}e(xnWBCoVkM8PzGERm@y=(7VgF6(s6Z%@W+Ja!jk7k#f4Ynyx_= zLu08*0q8MRe4S39SBBXL+tlN#ue^)79N(ANoK?4D>6aeZu71{ zTuEI$;l47kZJoUmES;(Nb=8xt>8@0|;*NDDxl^VvO~sd%;zO-M?LCXOAhNd=Z5$NR z-uSazXIyr@ru!Og{sU*`ZRm9_xYvf1C>^7;Vg@`!(Nbr$^r`#J(_?xcZ5zRoiXAzOv8J~iE_DapSPZ+M*tqo%e0Zi7^W=@` zyaRA<*SB4=M>K62&xeJ2yP8*0|v^)bHj;#C_bzAx{IYuxM9)J%*YT52weYa4A*tVkXlVh%}-L5MRs;TYr(L~oQOWj9#a4Ppu zdJ`->c*_6;G4&r3obtIFaom)O%rO|H5gCLfqzZcxh_~UQj8y7Xy_=eO4xZ@rBk(TQ z2~MTS4z0xYtkl-HVRLB^Zh=^A@)J1mB}-do{cuyrjv7+fuZn?3>!8ImBW}XAUAvQ* zQlU&yf&11LpAtvt zBNq!T&e=yxmOq*8gY6yWGf)+wxVo+D>Ha)us1j})NFkS_0jXiOg%LnA_)RgL+ez}7 zLQaix+_xR8vfEj#X0KqS6LZnzUDIaXxAvs%`EYEvSigHM$nLb)oZ5j%s=yVYIQMg} zt)_*pYk_b~e=9g_osH~`nljkjIjQpRv?5t|?fUY-Et}F(;A)3RUu`Mv7^Fd$FSZRl zmqEK}$8C{(%pVdSQI^|fz>c2~0Hom$I29!YQ81bq-F&%=UV89%#f`T z(@%JHrCNt~Ofqu-PgyxAjpEq~^AN_C&{`GH_KM;{#_92$6gF2A4a-O&MaN=Dk1)Fi zdYEayfC__No%pmzmktzgS-Vdj#(b~95ZRWy?Kn@bw(o<2If%S2!F_W6yyD`sf( zC|5Ub&PRyj@f8>$#?+vi`wlm|2zsLj#c*bBZ!SX50<5>Eeo~S5+eOs`r6Eg#RB1vw zikxQK>uoW^&1lthcVRo2;NxiXrzK@xyu~l2SI8@JGLDxtIG`$fO)-|T$kRpD<0`=a z0DNeq);CR`ZrY&DT!k@iB@M@3YEO*NR=p{RVT^o|7J;g^yK%v35Oa+hRH($b_d9FJ zG?5-lbR<%WN`_d2bZ(AXD`~i7*7X{KknNHnxZ0BoN?nAqmlA#;q#9>jNnBgV1y2PB zW0JP&=>&BI_YX4rXe9&9KM~X0ircg*;T!sznme+^23v7gDX_GmU?@_CiuR04W9~>4 zP}xfcIlz>sX12$H8|B*xEvN$8bczh?)M6^;;m>iZm9n&CZ4?P=UA7h4**4Q9A#vxl zww`Q?iY+BL^`+5{KtfwDjcoSir<&)qTUgqAp6zTfK#^>H)>B@q2~`2ly(b!+q{#?( zDlTfbXUg2UoDEfz+5Z5FOotp?n{EOVUzZLzkhLSORO^ZcRw37-+P2-nF^;mL{(iDJ zy{^^SUw!yf>2y$PstIaLazcz@h36b3B?0ylGb#dD{S6W ze4)cdMmbj7XtP-0Zd6?pxf+U}F67HU%c#NE3-BpJ<4 zyTUENxp{4bnE@FE0BQ!V+$WFNw~<`7L%=0--QdODW1j#Tk)o-bdA+vQ*Lv?J^R=Vy zXKrzwi{!|n+e2?8rAnfK8mZ9ZL&@UbH%&AFVmp6vMQqnpbr&k(&9Wd%+cuVM*3)c@ z88Smmm6e(w5UEyqVqVH3%2h@>M#yRfDy9Db$%f(b-nl;xalnM$_GaXdLrSSZQ5+v) zPBSj}Mou-Z@OWFFW1mcm$Bb_KTDaNHR^?eao0)9NPL*4Ce&%2zodZ|7DHZxDi41GH zt(a^3qwH6BC|u#-=Y6F_Yt=!PU%iP`__l~V0xIwn$YaUk&2V-)h^}=OgcbGh)2D6) z#;EORDYv+r)AEW)trDPB^w$xRr_i{B+Q?C*xd0mT4?D5*(N(ht*z zENDxWJ@KCpF~9coi@UccVY-*r%Sb{Hq)`B7YAcB*Fi~@VaQm@wh{$;@0E$&d{Ex6C zYzwO}9h4@qDZOk+3DB&K6bSZVw9(UozxMZiw>55EN;q)}ItVEsg!}M=1r~!dYMd-e zu}5Vok!^X|AZF&Vpr3Ux{0Qm6R4**9T>k)d)CjEkcG)RqQDOY5l1`#cYnOE}6S1d) zbB=;`AAHp(CR?dmN>&bz%4ddw)ZnFz4dxW5bme>WzB*%$6w^zxA=JF5N>7|z3e*5S z*nTb@(?h{ltoEc4UK@|zDj7mcR0Xm{di}Tp@U0Gb1By6>IbSIQN*wE@NA0IvC;%Zd zH!<@mUIx_Mx2FOu)=8H)RZ1o_SKt2tc$0&cIPy`m?n@XxH?(@l3RBy@mwEpHbA&x_ z<9nM?l|ZSon?xpnC#_{E7+6@&?hPujk?!^m*c!^xd<9R~rb}m`&ESR%o%O!<-ol8Q zDf2FWocc$xwvq*Z%EN(-b5FcLRJpt93(x(uGD-gc!$6lU+(qc)V?3YdPH~yOM?s=@Ko7aDjbE38K>p?3umkh(6|$vTvp)YVkV zzT81d-xY1Qko9}M)YiQ;r3S(p5h2#C!%B(LuA@=yIAt^RR%~Q|YIvq>nC>VgRSDHl zk~8U(ME%~VTIkqFt^@1shcZ#r!;6xyHuUgzb~-% ze;I9g+qZO2R){v35uBGL!ZNOw!P1f1rBDviF-)H`Bm zeL-9+2chORjk+H4^?k~W@k~h{c@vE7Yii@UB_+Jnhe`_CV4jpnU%VVj*=)&V9%a+I zM~5|F+gnHK${6_O^@+iJ_o)BtqFysUBT=D?aZ0 zHw>bd^H3p@l~T1T>T6zu0U9HpMY3oO4_@pjkSJi4s8)b`I$%*s!ccVrp-g&+YL7=k z>K8z=k_a6JSP+qrqs}SWWEVwBG6|&sA4UhJr6e8FjHkkiY4G6AZ4yp$T8kUbi!2dc z2-Qyi0CoahV?~7f%hvK<*w;9L6sJ4UILDLkOBPW8`!-GbsqL_EgDl65e z_F+FH^GXYb(OoY@+h=%gZcDjZw#L=D?v!3&t{m^fBf9!yDM}7DUvU&eXeB14Y6BKL z1>k_I%$|8v#3|ZO4fPPs_Ef)r1Alj^4HrQ$bQ{ zKzOY%5EAd9Quz~)9k0#xyZ!HEGdUs zaIqzY1+gIqN|Ll@F}=No!E%dD9Z<7*xXZlFRQc^RnNB4k)wJ3gONvOQvXwPs}@~;RJ;HuxD{o(0lXd|xg%oRy zg5l$+uEEDb=D^T+(I2+Z&xKsjw-J~5LkEZA2^-=3BkEG_>TwIYyb(|m5()Y-Z)&Au zhw#%>I#8`hSwe^J4!BdSsd5sECb1zYZd!b@IzFG)Fyg8>u#N~98gYV`0{qsMn{JMW z9!hjoY9Fb}fd26x;lhr8f>+BcbhwfT)lY7CQ76(?#p6*y@6QSOhJ5FEFPK+^WojJm%fAZ}mn~jI=}?^WBFuIgmEm#LmZT`4kyM&e zp!SSJ-rF)((ZCfWXL~e10WGhB0^s8+c|sb?ttjeB$kIVjsT4lkBGw9Mk`E-++))y+ z07{OXJxwr|SW8lWkSM$xE{Dh|8i|Rkr3tbuCrP?v_%z`wlbg zgnuP|B>mIHJlz<-#4$&w<7d1+gz)CxJ9muvA93w}GtGqtLyNUZNTOYY^1Q}WEByim zM|L!3P_z)Km>h_>w+Ej!6^bZUwC=*fFr{9G9wYuQLtH45MiQ$181ih4Bcb=;P$LcY z;lo>|s1dfo&0U=_=CA})RkjYBEBWYB2hHENl!b*Mlr0WDwxssWI&f6LgR1E zjps$QAaSWbV(X2b11u2R&WBQdGu@2{hcBKtw*~rvL^P&jEk&fPDJVV_5(m|TIlh$I zCpo&L^~;4F(ASt2Kabmj=$y9RSGKfTu1F4fjVx(BRb0AYg_TBZI#8yL?z3$z@nfI0 zH=U`A&5q=nNrw0ZC$uRCs#cz%Ni`U0b70Av((zEOJ6|`F#9cP#&E=18huBvix#!Z2 z>xvwaxi*8&Ze4`65~sd7cI5v63+W#c2V;Z$pgQ9#&TYAE>h@~Y^n0{j{{XbkhQW16 zuDm%r{$Ed{+2E>Lj=H2!k?{eFlgjDH8rLqzeo9t@aP=j~Yf?PN8QN<@c@B?US+8I5 zFv9*&##F3tycXt2;$wBI{{T|@4E1Yob7}fj^$mM@zadubn}2bIDx<_Lt^xXSOt8Of zmjHTIV-tvcQpZuPLik&09$RQdG=#R;KSFV!N`2+hR^asNl-mhV@YDckRB)169c>w; zC!ow6HYvkjRB05VtJ5qMIEiDlBy~NQkGgRj6tH0Aljb^2P?b?B>rUK7Pch`DE$ziM zNzSKAI`zam=TgjzP76db3cf7+GVQ{xxkx_UE!yL)+3=V`Nn7r=mcTjHO9Lz`9eR{@ zO`))omnSAfPD^Gyyq&jJQ(Jd#!c9PNMMMLU2~9s< zEEjSfkwsA0k%IR)s-aY?;Wa&@5iVMjIIA(}y5256tvx@&T7S0Ow(_CZCe){Y;9-_c z)PSgL$@hJA`wF{$uq6wZtIk=U$szqX1kJx7 zzqxH}Cm@p81L^kS@bT}3j#?gzSr4sVZKT_bZRXpp0#bgQF7ZM=Ynn1|4!Chema?^) z=2e9p5)`683@6NN1yZN3JnjkYqdJ_V2hf!iC6$#aNfpo8h_=5~Wp%AWEe~n#O~V@X zD$8Y&bcwg4nODgzAgK`}1?Vm~dj(Dsxy{>KNY=>e7ic{x{x4@=UBLNbX)I?G;FE1; zQ!WGt0MtN8G}0=2@h5OW36+xwG%498CE7Xj9z!6g4K!M7_F_CfoD}(q+|qpL)0~Xk zucf4=c_&JfsP=q4MjWzDQ%jgV)X-I>*WB`$Yg))P0B4u85?*Lf&bB2sUxxS0I>~GA z5l~T`YBR%-=A?zQl4r}PYEGg6>M)`=%amDYc?4-$1onHd2!MJKMf|}ODpCDkNyn&B zbO^e&fJF%>sLu;Elt9o5%2YwkA5IK~(S)rI;VJ}((_uX1X>i;_u_UzO+HtoKprolP zK?D#-VZs-7tePUnih>ioZS=O2wpfiG(@;XQJm2b8Uq5ZlhYN6Q*p^-0v(Q<&Zs_XN z?anx4G=#be5cq=2$JHL&2j$BSs<7U@qyj_?I;p7uQH zYEpnoE8a#mX%{+0#)#XALP}4>Rhb0zrdVexe#a11v$=YXhq=8sZGTQJ>~|+$EYAlZ z?xOk>hAAYxG$lh;wFML&${b?$UAB_jJnV>l7NIW0pZd!D~DJ03__RzI+h7sPWN87TZ4O-Z$7b zt3{4n%k{-&s20XqaZW908nW>B3Y_t6zR%fO#n$-;PpxHGy_1>A+;l|?y1hx6xqp~! z*Q<@b>ZhSyEv~7lkybRg($kWXMLRJuV>C0sB#&0PQ!>`qHrEb@O&;9{MC^N>{{U*f zL%dq9{{Sm;ifbBLkd=nX1Xha#9F7ycu!cye?gco(UtGmEQ#hroUm`nh-cYXFvhHrM zeq=dzT1YyOPl$x{`Y{)Mgfd$e#63#$w)cxIvLZ{9?$=c#Zx2;gTS0yn*SDtf2mIPY z>n+DlSy2e7{TQlZyI}cGRJtu&bpB-FwqcIcRaM71$C56)(%*iEazbuMeFhc1rcxu7 z5u_u_9~XCd#+){dtQNflXt}md=w&Xl9%wqAN+!H+3*5`K%1p^BOt)TjQUwBZt!pY! z0Cb`3#g5L&jEDyCRBVn$SI*+|sVJ*2KJvA`>YXX;Q&Wd#A;~gr!RDlWr*Yh~UG}a+AuR2+f#e6C6`)Q{ z`LwS47j#eC8=t{^69yZAbBZio4oOgD@NWK%nNbY=OmRCk84O}>@ zm&b4|fAuxw33KB{m-wye4a9ZEQS&j~Wekl?xIr|}r@t8XUPrreGy+FR`WIX6tYx?M z8FZZ|sOjPq)#-bJIOBP2Xj_g<=Vk7wGF9e5EozS2L19T6Y2s1ut|^_w-IuZ4nD|C` zEt|0&vcp|t+i(X|t>g4?R+w&G`*!B$#7(OOm=+65@t$$!${PrCNDBo7l#`$UF-3kp z-a8TRnz+?#xXh*1##!XX0~pdZR2zKmOI^!;?g=-G3N7&=zWWZ_;!>K>vIz$&)k3)3 zw)L51t3zCM3yJr}Qr=9wuaT6YDe>vgj5hB4 z(TeAC_Y17-yrA!S%SZgV>=<(Sk#t$^N2v9#nQm+27E4^&Za3$Zr^;a!%&oye*Sj3I zVF{W-O8QTjyB5&&j(RP2G)XR6SeEPo0IT~Zx+tWcroE>P^6mlj2C7GF+7S&%rBm{W z0*YR>cAl7pR{$uqQo>W2+J}^ z5;)DPGac6`pbE3NJB@EVeoeco_i3Vi>^MAo#5-CqAo2~#yJh0|P!{}*-AQz6&<9%f zk}(r@%dCusIKB|1@wXV<1-GAa>OIMa$a~k*uKT<%aLbbt`8h46wxud#j3}W4!k;YC zy>W2B<1*H)=xCr>w(Q*7ZzC_7;tRbh5wh`RvwYm74T#>^ zTr|B7sc7U?mv?qgZSISIHX<1z<`+77Q_;6Ham%6mWtm)qWssnlS1VE$u>j;8*v%)e}>`WxU+ixSF@P=fZJyIt#P0sOZLkey)tpt%WMT9&U57m zsQzhn3kv>UwdpEMaHJAE)oLX~9M7-}Qk``D#gG$`OL)$M#I0V>2^Px3e5j+ZCm!+p zzJ&@}t*~e=@He-UHg!X%AD~6X6 z=|HZS`+5=7p4Mm)X2?QX^B#1Or5b26G^Ti|-`*AtR*8kgiR1tcN}VnHN?ZlB+?#)v z^0b!PSs8<#DOWjwY5`kLGi!MObp+}!Um_5cMq(ws6%Vbn)xtXmhY^jLk2>*FlS3q8 zplFe0ZmXLL^IB-OmVdKxeHKCP!q)iqxay##+>*oeC$7cZQg@s??3S8JKbNR303N;# zW`K^g7>Ubc{$4=dB&%lI%p8Tgq~X)W9E|m=aYanTQdneyomy2Z zYc;*wD;W+UHs@_kPL;5-m8PBwVMuZ*y~Ycua7;_BnQbhW;g(!a`t)>A9r%`p{83YH z@WeipM5S$pABZG?UTGu5+QyU9y9(Q1km`jOk=yN(Dit5vnVqtx+$LFN7KFMJ7#g7G z(1X_&dyboi0PZS-Wo-O>hoj-}R?msIr0vgZR4bc-GGMR50(6xluw~th!*XN1vr)lx zPj52~xn`gO6R!B*)#Q+bhjK?gRkb|ShT92SXQ-xQy9k+& zGM#+0L8#F(xkqkZH!jwbZ?U91b3&51?JVgF(m^7Thzc zN?*$|Q>jUFPf?abF15mY!qPAVfz+a3v^n|R)dQJOLa!XBZ(JSAasKS6K)R#y%Lh^_ zu7?)=yFZz1C~B}y&NZz+{1AiCK~PNekD z54M|a=C$Z}sS1nUw`FnpX3c8i8t(+|;qG?a^KVf0&gfw>#%48XgwH5b@9CcJc2D#HLWmfRHq^``nyJWN@no)%r z#^W^Km{w3SQR|4Q2fkGGxAMYRiV`YLYVB8P!zxgzMsQUV)Nb343i)Tg#&hLc+yJpD z+R;BU-f5}Igs1dlPuf?8SPKrME_d#JFUY%W&C#VWp}lou-etYd-K zMa`Skh;waIEz*j`$gGzXhK&%Ut16M*jsDEI5Jv*IuOTFF1*eE8U9Y(yiI#g+mUHX6 zM{pThlA)zZ6*S6}!%U_;IfT5aov)bym*G)7643#7)xuA+h@kI-e$GV zwK&A>ZHzZ$a;odxg~++bwZQ8|NpJgp?YJXfcCDVqr=+es%P`tTwYZ>-KxLJ3#+8k_ zjtN7YXcjBEtYy5mbZiwgT%6xl*|uw}%T3lQ;oL3msku>I8eBrhP)0gS2Xe_`4sQkX z?{mWYdxFPt1u&UqZ#dIUq`30VmfKEhQj}-_1F1NlGc~Ow=mMsg-y?%tsVVozwqz@O zS}hxbFlDvKEZic=3T&fNN^=UK3Iya2ZW&J?bX`m}3R@c!nKwj&hrvet=e4KVw%l)= zD0K^$J%-*wlBE*G6_^I2!l92^T!$JJZuvfqM+95)ow*xV<_*Ot{>?X7_t;gb@p-n# z9jE^Q1&S|tA?m}p_-eJD&dbx3r`~BFVxfMxZ6C|Nul-F~lAkRx_od}&MF^mh@R!^F z04s(&Y2n!T&!K6PH>T!B`hQSWFBTMVYQ2}Xzh(su7qAK1*&q{2Q@rEULr}B?9)~=6 z0z|(B`AnbP)<@PbjH&cVq@<8~czaG5Cr;$GSOoY@0KlY{sR;!tNzb?0_2I;FYL3>` zS*|1IEuY7`UHd)pZ~h|WmmNY7(w2`cNfbJhm1~J;E+P%9isg>y_NOV8$$7AW(@H4o zm^mWT$rl@3n_PFIvjGmF0dD52fZ=D75Z@{9v$@6XICBfvL=wV z&iWJa9)ftoXE5a6N9Xu(lF$X#7-^O>8PF;Bluy-#8-$E;a*uy?GWSRL5E^<@X5p~? z5$$IR9o~1H+Vet$FtXwtS6}rFpaJyY!p;zN)h2RVrsIeVuc0@F`t-42c#9Vbxwv$H zX7$D5QldLZTG#q8r~Rnl)Nk$m$u6&-roIaZ^%b}6$6nFlJ-uU7b@E+QX5CVKwa`CK z74uj=7KE!iZ*ed4t{#4pnz#P|TKvZW*WgnC% zQ6uQ@#CZxp%29PB5NarUMjEB+i>%XDb@q2t9z|=3Kq)oM4}LvFWL-kKbEP{ljHp8P z5<<05BxEa(FDE|7k@EYt!1>4~LIRX}c)U6Nc$UG*(Q?wO+rAp(-aQKDU1G;!1?F2b zrz~i4qrlizMn|%k*e-zSs^&~&=CnFBMlzr{*i%SfMHH}_ru*c11C z;YY^Q1{{!vQcX2F@d2ijmsO}@mp*dl@!dA;KSI!?#^W(j`M0vQjsE~Kr?7E9KvxrP zeqUQZVa1EQy=HS%ZL%C5c;Z_Gl*qbBYPAV+QP-rap-V_4e-$NrLduHC}hdPqw%_fsvQ%iQ#*;c>@S6tJ~s zN|dD&?8SmJZD`(`cGJ_%LE3CwmR?bDmzmPLlWXpeNBk8K+?$H~x-PHYHx1TB(&;r? zq|K1>-+4>v;!}j5qY=|i=W0sVu~?gXNW&v&03-kguLs?Z(^Fq|7`2sCH7g2ijS4;@ zDn)2=!K6hFD@suWm1cV>Tqi_&uO*#I2|_g!*am-Q0*HoGDP>4WDikNEJ#Zl!t8}!I zpiX13XOAS@^l2=mV_(8YL|{^8LWfIW0-aeN)BXXEQ8#>vQA~zIY4Xci0zv9Q?avB5 zCdmO?COVXos+x9Slu9+grCVA1$B+rsq!g__@qu>)RuNPl+CWXib=hB6-CZClaq2*$ z(beWav$QUDI%i-lC6hnPQEFG!(mP-#ESNs(w#9sW((S@Io#wnjjM{C_QwIf zv-pvMEpPG%s&{m^!7{G5E#JQ+7p_wtlN_3%ij+q}YtVLO zg&qsIsmn_%TQTm9Qbem>-LW6<*Ji`Oe`MJTS9uh|Z!B*WB+HwLz5$|U&)jyKsvO&! zcEsiC<#q~Xh%L6{nJ*OrD2AF!)K9o#Htzb|Mrx+8m@Dr*I;W2$;o~pMGoH8d&BFZ( zHx+H?RtoMHrIR!^tifs&YOMbNmZW1oxw(a_qwNLEjTIahM|i99%JlQFyqkvcW@j+o zqiHU?bacYB1#~or5T{yugR52|rMv3jr5SASDs&3v#kg(GJhY>1Y{*eI^`0mwiMMQ# znuis!N)S^SDF^AP!>dSK=jx!cmr+FBH7iS7Q=m?ZF5e+g<;8IhN0NmUtqVee(x66S zgjXFAmb7KWCO2vd`2PTdznaeJvqosRt1?;=1mh3BM}NnQcW) zd5{RF>HZ6XSG#53Z(c4o9l>mV&3dxF>|4FAN(*nskkX14+R}O`5&JU7T;sA(-NM;p zHEG~N`Q4Gk&u2TKnWeHc(1YTFEiKd}SKO~=9=X*~VZaq2yTIJiyIr>Q*BjhsS}zfP zcGWsCVoGF0Ir)i9cNV_hgjd;$;N2ExQo`X;p;xwSe3z6Y3?;4=926UQw#&6jTuYP} zt#dnK$z{eI6hFe~NyN-|QAw)^DVVmJEgsT6VJ0C7Jw;FH!jwKJu5m*M$vqRQy`9)K z5@uqU+}RRDJ`v=Z!5$Raqzam6s09AJLGEoVo$6PP(o7`Ww(b7_FkBvGbXux)bf!7U z@@l@UwVvX1?Z($4@oa6T%g#9R0uVDQQBa@&VS6@`@S-y>41vV>DU))-eKTJ#Fs2;U zMYf?t+!-cOu!^H0*eY9WEM!>GUe+%sd~ow-h-% zfx?eA_K>G<`z?hfbgfpWVyCKUnNxq}_hUNFVdHV@hlen_54C;N$XZA&a=?dhYtKJH z{l3+oLq4)Do{G78;mJ&FNc@RwzFSplzHPVVt^=W;m_&rU!@;*eaSBI8CW;1?9dVvz z@kZ(*%Jh}dd5Z|I3t!==U5)iu`N{fX^$+O#nfThlwYNUV<-S3%y=b&;n}xlp5j@$7 zLoTo9;5y?eLJ?Er(xcD{;*X5k_jcDo<-yJ7o3j%4&exDSSbmfA74t5%lHsVLa?&g8 z#*pByEhGeCprA!ME2+a$62Ts7qzW1fv<-d?sXK_6A1MB+=o;l)Cy85o=xe8XTdye3g z)1?}4HV4oKG+nj)hAu+h4*1F^bQ-snxgQUWyt?+{=pf^s-eaZ0pQC5uv00xY+Coj_E!ejynt<&TA;S#Nht*BCrwDp zRWTD|xNW7lFoW;6h*oUI&bqg?Z2A_tBW(UU-n@f8b+=|&9KBp1GO04-Hny86WjPg9 z#Uvc^pwAAnx$9dh8J$2%g2QbqyW&J5qzb0&9hbST4quEbGtwQ~sn>W#2bjRA2B}d5 znuA(b6A#T>05#*mG_>0{vJx69Tg5T%_Iu1IP;XPMQe#@K%5gUPtC8JqGwW$uRF>Q+ z2~kNIfN(aMn`xiW#{%KXy02vR)%~nuNn>+ZcoGd0N$1>2pa28gPuq#cx~jcILFZ(7 zZibW;@JhUtDA6T!$QpJNgE`Is(~3>8x2lJNoOx$xaDLX>oNYghIX+(??z`MTu0*uI z6)~B&N%B&&<^9uY9x|k9C0PpBzdqTSY~97wt-X(;Fn!bFwSMF6?XhcT3-OZ~B_rPk zntM1X4KkDF7S>V|+1h^0Xue{{%vvL;c0C5A6<4=B7)TOEBDX`Wijap0{pt({npeD} zb+?mB{G-Y}#ASi=QZI=qMJQ8=N*XP#MwB_$y#^7MaM9Fs0Xnv_e6j{NQddj87x2#K z+dIbnzd4JKo!fjKHvP@5^MAjzAwxu0Sxc?Bgvg@QLXa~^$P#OgW$zyQw!49r_AlzW z>l6**yW8^D+Dp7_^4;bl^6Y%G)L)?W{Q|sGKYMV;4&66i+qZgo=KEg%0FS%nKF{HA zNkSED(30zEQt7QpBf<)ksFB-TS$4Ml%f)K%#BJQ(fyc#XTko_xc6^gY7V(d4gw#|X zpVQKtd+XDV?c=vC4(_{*2B6+`C98`rt=59kO=%%$LaLu&#fJmjD{Oo0xIx9q-PEwVpLy1g|;A~9lEho@5 zRh(AKlHuj8cxR-QFzgRa+k+jvlqNxFXf^Q2CFU1=FQy2)TK&c1-(<_(O%xTmal?@bAop4 z8MjMr`Pw%tYi&xoTj#RnzB9(HxbQScBf6&%*{It4M+ayW=Giif_87%o$bATn?dk4c z?~AwuneNPzslWgLc~2;;06UFv=MvH605ud*`FA_!U)7_kYPDtJjXo+@;`dh$Udkr0 z%Tk>7sWKu-W?VN*GFVVkFQk;I1awkJAc8x_BW`1Cjp=ZxRt$B-khnRTICE2tqldTd z?&ICT%t)IOeTLOML%0X019EynN>o&al~pJWVx+3QXbmx+%e-xE@5PW|29?lgwTOFl zdwGaCCrA#op*pJ-ZP}cGZ{Q7?YhqJNvG04E@3Uw_h+BbL!%14l?$o4yyiugh*t;Vm z$USTedxvP{o3|miW5H~Pg1(-XxRaDPm+@w=cBHh$!KT#GM1(lfTt!rqmqVU-#(Rmm zEb>opM@VHe6$8Pfm@#daFw?@B5W7W2~1G5#$z4ujUw($ltq%BHPdiLOD544`ZFyvU8RH)y@TIY1NI8y}q0m<@CwG7&9 z&;ot9jF&@2B*wUkl#4R>)uAY15fti^kU%v6drlrjY+fo8E4-^?G)inqM3~8us06wy z8wW95>4pwr1xiP9*EXRuB(b<-aNdxbH6ltpU%!z{+nqX!D4?^yhYaJcVV z9gX`?y6jJvM4h#BOKTCODML$&ONhxuIb=I9>$jFrMG13_AIucPzPT&O?%LM0x-L}U zs!Ow?M|dt3k}`vX?$G;-Ksk2n>=@OoZP_s6Rm-`_<+gPxLY*5waPMJ(0ZH3%f7CWtvr929fob@ftcGnXEPUn{c&RB2Da3$R# zM?fU0B$0}Z#>U3<7Y6WG+b-;8&BJ3a(3&fD=P>WRjS}5txLFmsTZq~ecQ9>inFZAO zhft!k6{a&dwspP9E)MBiozHb|vE{Kjq5hPk2h`rsnztI>n_b2TAwM#x=#Yfe{{Tn= zDS)45&k(F{;H0g)xsRCiTtq5#;G}WITn5dF{y;kx*xkDDkDZZrTp_LGENj*Rz*R}? z0~N;Saxa(4Ysn#BZuVZ?x3u}GBLk>aL0mt!+26d6e4E95hjH3FYSf^qR^>$|I7v_m zLPE3xNv=vc3`=e-lc2X~k)h5igRn6M{`vAjA&w^UJQVvk)RnVoas#|?bX2S5p4}h* zmETdFVH#wp5L!b?9aH`-y!KYP17{)XVAHG8tA6p}cw4@9 z;oh86x>d;$P(EV$31vp=!ElvT&olz(L(@NIFI#(fn=spZrg>+8Jc`P~}K0}R?msk7XgGy!FjU~a46$==Lt~1c#LKBkSl{)bAo>R&rGPCw# zDtA#@F|2WRx~l=lyLM!WF=wHWrVvM*>7@be>5SiSUo=!MhS-=|xIUZ}fV;_)6}KHr zB1Dv+lp?jQ2167Z7Kcl!*s}Avu~nmztQSr7ycKXRxj7M26-w0zAONAwjiP~5F1K{fbzCb|Z2096 zw)YHged>n9ble!3BTBL=@|g0H3GJ!IU6&?os~9x^u8qI}Wwqs>hfpeo@%Ht(?CX?C zw)>i%L+_{OZKoilsX-)Awa(NonlT z-ge#0cepB<6C^HFhQAVuVkv$Sq=To~mL9P5ofT4G_ZksI&bZp5N08zqGUi_csjUe5 zdogFUx$0ExmMm7KKRC&3M7s*fOMYmeYoSpcF$cK;bZXDB93q;YDt5k|Z7}YURI91Q z65oMaJ@kALAC)e3PM}6d4r7vqCN1jPCe+QbB1@_)A1Rd&8Bxq(^mkwys0+L+q%;XG zA0ESMPtHt|x1=QaC^U*;X>(mdluRvlgJXYDDRIb;TAEUtkf$3bAgt&+1{$^9N@_+J z#)uTlxPYgW@GXKUj?ysa%L%{Cxit3{^LEa*Qj~d$ce4_<7(X;>sC$!q8#e}^An(My zhSFLeHt7k`PL=v_r1%FgRW-+N#!h-^Qd5cr@ErBdIu6KL-u2T!0szs z!9zgh!~4%->QM|c&> z`QFxLF*5ar^)MbGSRIXcvEO#%2wSR)b+mvTkE>B}BeZ)lMa)<{Oi18YPwYLz4b6ZK zG<#Hqc$c@XyE5qvUcOE98RX7|MFnUmAZO6x2N!H(yatNrymAoSVIJD>3XW`z?{eAm zkgF<_q9VK@NhGU0tC-4z8xBJU9m6R7R|oB_`93D(x*aH0$&W{w*=LBJC@Y*iTNOO zqzyh5jWwwA$-YN@|4Ouexnijk_NRx4nQXOM&!CZth*O4U@M zNy>-2+l?mejdQEwumGY!UB8TfQzPl{QdZi$#A*VUbBctz{Sg%@J_G_tHTUAXb8dR- z71JMn9RXvxg+sY1$<{sPezr3mD1hUtTRIYwJhw)=gG%){+^|g&TLa*^19o*=Otc7no8$EY-{05y;!|f4gOx*d6uLyjluK=4 zNl_G14L+P$Hyoa2i`vu3sy5K1SxBDt{_s<$tSC^K^)tU^MT#P~a&|1h5;To&#A!uT z6s3CN3%3$IxVEtCN`+_cgBsk^oY79I6;ZuG?b&v{pS5MlTWMA4GNiPGD6XlmmP)%T zi$*SfsYvii+*|ZU(_SH0{{YLvw#v?ng7giEq zSL+J*+K1CI7o?y3E>Vx>ueO!9{d{_wWk?8WZSp+EI|)piJT;T_s2ovt9mifOBk28Q zZP{oGGBShX`hw>XYqq^Hda7a=5QzjQrhV81qXw%&6VQBE@^XWy01n>Mg&I-~br-8s zo(NKnby9WY6_a#Tnq;6OVL$V%+6*WpK9jy3e!XX`&MzDx^9R22@+C0Dqf>4pK^cx+i## z%l0lmdSYIEDzNTtaSJ#TNe2fik8qgE!G&a1po>z%#NT97dJ(#h# zP^y=&EOi2MM2gaM9Xg5V3G3UBHXxvGE{jP+tw=(Yk>S(OQy!>H#GaKOZia&A2Uc@Z zQ?J`WL5OUK_m+czl)gPvVE9Q18F%751v2Rz7D4Ne5QVQLYI>8}I$>DSE-P(BAflB7 zlB3fKFLOm4SSkfwd-3XoN+8`MO*+>A;)hBg3Raqh9=#44r!3%rS_eaz$B~c9sVkIm zI9scmg4PWxRDTPLxzCkeTRT%|y|!#Grmyac%9U$|mU35^Jj92VC~7pLKKxK+IV?I~Fm z((0O5nBcE!l#oYe7P8qe#_p?!7dyd4U-x%4k#Coahsk06=W2e6=st~8gK&Dax^3GS z*?c9nt^7f@Kp_lT*lDRfH0HD<0u|P#r`wA`yow0=A!uMxHXW{-_T%PqQo3jQNus42 zk8xjj70yM*{{VM>={ic3rO0*VI^xOf)S__%HRGSet-}+7wX=*iCV&EJIF$scDH=)0 z5Nb&2okwOF6*wLOFXgAVuy~xzn%0Bt!%`9Y*PD*x?s-i&t>4~HOoly0;Vl-0w3QU3 z4Lt!+J)d4Ylm%>3gn)+795wN%Frw0c{?HncT(ZJ`S}TQCR>?Ic zstU|FPG=tSkb=D1G$NiNGoi=4rQ%Vhn+_cy_=m-hd1n%TGVetvFn~eK zf^fTS+65@7a&LJY?GA|+>9sAEwgargLNlV=>U(?60_GNm=%n#Gg5qb5O04^>_w9Ru znYNNvmexX&bID^ufTep5DU*LOZ#cI$OK*Ky9yQ!jHodky$d>fTyvRjcebm%4lB$9d zdwVfwv6H4Xg713~adATLbSK5d8;fykxo#=NEe{f&eZ;oXQ$z5iQ@^_sZkI}xR<@29 zO(PX4pFei&`xdma!mrbrrBz)FB+{Qw8anxnQ}+0KiM;_I+4e4Q=IJKe#``{O_)0og zGCO`ny^&fSa-PzRyGEjYn25gIamU2bT(J8Q@)1GR=~tJG{{SucNmNUpI`7p=e9^~t zjfkp*(xta$xSEsgKpy;4F85hK2&&hx{kCfbWIspg{tKX-f5#kY#IU|YjCKqyq}2z^ zTu{;%F~icXNN^Vly}GdhE<#z|#a-?3Hul3nk6w4BvI^ZYr+m!S6Gj{R#T5*Or5ve) z#bYFfr82zBvMvUyBE9armC%s5d6XNIj;Mt|#HgHL9b1n0$wd(`wr#?y1hg zvc-Pj`)#Sx(=C+%gbdW8G6ULhdz+~3$gY)4p~p{h4@j*Pw|lZr+m{ItCdX8HF^wE) zK-3=mZMGQ}T(YfSDzkWb>}BY9r59&^D(*nrQMNB9ZE3E?w{8?%7`S=sbqID`MMv^z zLXCduNF&^#j!FE%;Vz}Vj#dI&N_v;mAIVPi#lwV}864KU8k+Od^a{87p7h7Iy=!ld zU*(TZxAr1$je^{EH*U+59SBAB)*D?Oj`M1&Tq#GFsYxM4AR2YWuV(L?ov&{dt>Z~# zheP*&PX&?pGqd)4-ty-qijW7zTDR{~zom6tRc<5Elv$L*;>eD*wzNIuwPa zR3S+sgdT?-WovZFm>$zmLiyiu43WxM@I#ttLC}IcRfDy{xv!3`+m_vPlMY>`a_#6_ z5?pO@kCo@3jS3VB2QPLg*+?zhcw=)$x~=~JZ)7hd#t9hX?&z8lH^(GB=CyFUF}c`M zN=zHPMlKC3cPP3w)7-2Q8wh&6%7nt_Epp}%@Ez+4x^4+3UD|BF!lT*Ud4GLwhLxr> zN(lgl(z2ofrD3Kx7M@1<=vQY^Eh$oE417DhW1AzA;#_& z)U$9=SC@8<65Gh2pa&`_dk#0rv2e)H2;{zRZSLjdv9r`Vt*-*P;maPF_m;x>n~Vm( zAF%^ePL6_9(tDk3RY^Y4mMVE18=}>^h1NSOyf!%sG3TZJ2cM}`mtwfW)Q3Srfu#ti zOjnlO71LfS)B&Xk9eWM|JzCLU#r9*!R+J{SCoCdlN^~uP=|i4>y@3*<`Dha9?*9NA z+mCsvS}lu;0+q=2;6uXo7Chf(1I=Ed-6zb;NFK~2gy@`+1m-sF!f=es_StSGD&#Bz z^A9GD{n4#KBd%1Y7w8mC9w^|DPx`$5#}Vn$GR(PA<4uagsY5p~ku4>Z|s33X8BifxJ}g#aZ=9aL%5*AfY!;;=;x2&My|Aq8qvT>G#+A$zH6v>iEQPeXzR zFLkUGd5A5$m{ma@!Z0PEigGDp^H zV3zbrtf?g=Ev1xJhrX2<(p##j<8PZhA;X$y=TbWlW)w~dLkx;~Cbi2RNj(}tsOz6@ z4A#{qLL6qAj;FT{Q7ft)qDj+Sy|^?)6hf36Q5rxsP85t(pdnnJPi?%;+FR|b$!YmY z@m7UY0AoknO`|FoC3lWSzW#?&8m@zP*%td$hL-hoAgQp5ikf!f>Sdr@&iqp*xC9P- z(XFF!nL5sw-H?Xkx=Nf|iU8NGG3$L>Qk(7@NaDEZ0Fpcrm!}tF$Pt)lKm zsTl2gUOJ5rf<|uPH4XWZ9|?F5EF&t^ic**XAp=Du`$);7hMZJqx$f>Z+{Wa$=Dwt> z&0?M{T}3;wA1tTVuGl9OY7u5Q~OjXrH~B`Xh%XlhY*roIB+ypyq4OuxM#l5XII`-C{(}*V(I@tIlja$kR4NZHp{a8__RBC99A|ODa%#cPy z2VQGxo?KkQ6UlouP7N)yHMbT)=oE1&$P zB66~S4xW&!o36huY2bfS4)-xvZhPbzddrHFxGydT38^Xh=V(dy3~D=hgUWBHE;`-K zJr4DswK-iS?fq>*?QxKPyjxH60?O`BOF0Ur%=g|BA9~R~L^f2T z8 zQt8sVt6rVBQPyxgP=wkXrQ8ncjl>l3+`ReB`A?vs_gRAr2-OC6J0>!?hcBKA|(@A z{w%y*ow)HS696F;u7lj8)Zs{xTNpyAYoHo|G!<8dwZf!TBK8EiY?UsHX!&k3ky7G- z)IFF;u<}6Wv?I%q-AFG4j?gKB3ZgzbEz>C!)2Qh^dveF9Q3Ppb679^~EiS3!rHL`z zRa5|?A42@rM*qWRoBxGC?ydDm@{_Vmt2oc zNn2(vzMa&^&OMbgBhSawcszZp?l*feuA5>@(A^ANKloS6IdU5l6DHKJk`0upg4&7{ z*-Z#3QV1Z_R-+$Fgnn#Pa0C_bdGdL32S!LEfdzC&(&r%T&Rw4ui8;bMyPjMI-g{tQ zrA}K4FE~M5R~%1>l~5ED*P+G>+?~61jc?7^`=Y+^1DA^Hp2%+t9My5zTD`z~grBKb zzH3FM+q`buX4|l5?HKk+4xz+cR{4426+obuQUFCh5(xxm4lbJ>&cwyX5&_D zvM1fG+al=dp*s;!icPRlXw;E&l+RKr#*sOvvt?!4@z=5AfZ` zK&xHCcHwQpkCK?RBl|?AJEVYri%Lx~smAMKIE9Jb8?(rtp=)3}?(=YZ4%dq<+r&m( zlo@E2-AhajN?lY_)8B;eA8i{w{sgAD@p@cLXWj{oxw#j4UzqYQH>nd-ptL;KTq_7C zG}M~V_KZo}VeRA9f{Cr};k%f0NbMA3O(VjsJ?9clZ4@tB&^X}gs*%v($c|_#N#Ye; zQ^|WB=n>g|V^=8Zi5>MNEx5315_M(Q5Id-WV*aiKE4Odj{{Y3K=+uh+g(Pm*E;BcV z?UgPWVoZyS*CjTV%2JiCLe#1bgFHIImbTS^aH53W9bbq^)x@~{sWF7PL;@93KtLTy zCY><-?hR=!;1o5qZ<0XPYDuD#+}Ycv!8JDh!?vebXDPHuZamn}M_QS4G&qpp6-u+A z7_D1oZLWdxTpdpZe7xJ2whj%Qg@pKo?1={AZK-xTR?VrGRD$rTSuap1fNB9WJ(!NV zb-$TH0dlcevhA>qNRR4PA8X5&({>39kV0eWqajKI;s&4`hqR1mx$JWs99lIYZ+kyK zG*U!dPFSx7FT%j z7`^+b>!|Tn$w|H zCziHrTgPdkiA(ZWwk;A>FeDX_+JPfJ(kd}Q+j)mtIEM3EcW|~`c}6kM9|@{>#VoaG ziy{qMj@txRUMiv!gj%^SR&5s?29b3c9r-h-`y)d&O_k~h(Od8WdqDhII zW-e5jYMU}*h=Xnfkl3v&L5mn`LlqWpFus7*Njp<)rp}ezrc~V~e{K$X0qhus$X)f0 zkgd3ESC%@R#X4Vg2n4F6ew;@RDrLrjM^D1JUGDN6QodT;-4ZGWr5a~JiIF>79S0Rs zlFjl>Q^5-5rN~z#%x*)jMPaQB#z`a9@^l+n7wVSnzd_GaKcJy{`t)MPV-bw=0loRxxrwGdFZ3QGg>Xo)OnKu_g-+RSX zUUu0$rxnFHxF#-6*iy&Kh@N4Jiu5Z=atGgyM`vfO9^Eu@=WgolY{MRUs#)kwo*3iq z8-8>5UR2)9!D<(Z7TP6khxCe)mMjvu)XN%#FJWQz`Qi83-YSytU=2RiL1)DO%$z$QI9*F8w7T~p~_HM@(sXUX3_RtQPZec7K3O>nlDAqP+;^wl*jr^kIl7;FPKOz26^6A^ zx|}JdGY$E3Yik#iAM+ie-+L>I$Ykd?mnZ(-AzgXVKQ9}rnEg-kd;526U6pOJM|2{^ zZEb356}}q>Pnz&=B~S{VsA9V8b&e9lt>Usf+fuA?=CeD`JLN*+E!cjF1#Z9=Di?cTLnJd3KO7sI-E!Vrd<_*7#7FPcN z&2XV1a|+b-h=8e6Ty;(@OLw`lF?eeW|f<`I&Oa|aRs0Ea%@9+Rhnvy9|dt{rUv_=PH1thPxgY3R0Tmn8K0 zk1~lTw9pDu6-(>60XnT~Z{Z+wMFmvnx2}!>BV4V@*^A#?*=K05^$Jcc|ODe0k~0&$I)= z486c2MSawyl_da)51k`2NEF02!nSqZSju(jQ$6y^HyO8?Xw%cw6*1yoKd^7B-Lf=2 z_9jb;>u<@oCL@hFrrB6o6;?;4XBw{V%}Z@=L2Uu%xtF#3F5f47*EUL)8qsw9Nzy9V z8U{dDxKkYAFTCPc(JMH^7#cYsmg`AD)%Dt}@&&hX;~0l5--1 z3tT(QjMYTg+X!w4Lb9*hQ*(z6xn*r=2~&tkqK!vG-HHpGTH*rQ*%*JA@7;G^yu z94MI5ZEm+Rh?t_nl~knm4(v=rIHv_tp55Y2PKrO@)+f~k+kr5#Df1K-Z>( z4)K;KpCV|tzr~uvs*kxVbH|CY_aud(#iiKJB_L#Kv8VOo$+hu?*Fk4J&)m(26!Yj+ zpN6-5OAg?JHgkoz>QGOapM@&QzeikYySo(Wt;if#Gj^MHGSd7x=R#Gw!*TibHr z-w>7#q5yL@0jF{4jE4<}cx!=b>CNl8V|0vjUYD)g!W%Y(D)fZE8PF2OpcIEfg){3> zhUXh2CigTUNHa48w$zw)0;%6MaIK*aD|GD1A(UKJrN3h65VdAJvyh?g#>KQZQ_Fm* zxpQ2p-HzKu8@&5XI<=yxj!3!f`3^>u)7JS(HPoO`evMT7F{!|9)>xwU3~?sh$Np_HQ+9 zDVI>?>u0lUkvQpuK=5fvfH7S@CQ=5EYl#2 zsED9wY371yO%?0y#$xTn+l%JDnl7rd?SaMNB!jM`s3VDYG`)wpRN~sz9zj!bn^iz0 zD2)D`TsKw~x3=}>v2NCH32?b0!|)TTx;8b5k*+t`>q;A#?y%B20&_UUvKEDhpz>Yc zw=yl3KRLV|s+8;(J#G0eKjylVQ$h|@7^dIb7HdVS;WDj|QmNq5!iXVRcj6*dX=A7n z)|Qtt)!|l4Ph~KZYS&dOGenH1BC00h*)Z*!!tWWWE=Pdlo^o`Qfu=``q3j15t+n}* z+&Be*-(pz}>xFbp-@}=UHTY6o3yi_=n&nQlt#BUuhJiG1mdEkYSFcgKYD_U@-FDcm zxe3AYlU@`)BjQ2Iot4Iov9ck+#Qg|ZcXn|74s6WS6;jo3l)|PdsOb#7r_5(FQ{VJs zNiegKSa;6JTB$RMcV+u)?u!oMvC%SZlNbtUnuRvv^er@~s3|8DozY_)mhKMlS}$*N zGedmGH1l0z^&PpUY`X@{cgJ6Y7GhYI(%gD0l}2p^l9jhns8grA z8%Jy8zP7(M(=_zck0r_f0GQ_DayZd@Y~W_ZN0j-gH-vjeA`@6s+&+=ypeZ0{kZ3M?UTlA6<~ z%{Z@i?6BA#pAxK{)R2s@uNnnYdG6kbdlz83+~v<|?Gi)GJgr(%Lj1L%I)`By;>U&~ z&UdXfzI(iuSHV*6N*6<6^_A(~=D!Gsy{xG!CY~3jCO=>D@NNG9ADM@gf0bW*RQ{a2 zB>nHS5&G1n`PRlDO$4*{G z@}NG_e`4e>N{_D>{ZxBfMc|+-?CXWdO10EMtyRcp-f#_YLDEXs3bFJ?F_axbhzV5* z?4}5f_t6v%&C%Ny=#17U)scq`$ zys|iQ9Z;$Fk#)Ex%F?s_q138oPies%+ND_9CXVDm*2>AuiYVd_ASmT}+tQOA6HKJ3 zM^T{t+%azjVSP|*Kt(gHdoWZ=tuShoWD}L};0Y)S2}z=bO=*B6pdkdLl4>*dW8Fo0 z1*65&#e%0x%0VDhQ@G>E2}PEYn*E0j(UC&15>;G!s8DKhgi$1@c3>T%1ohZlZ@+m$ z^4)`CZMj>H;@~=&j)9@ELP>Gel(yKQtyZ8gyf(8+XwX%6JG!hfvH6z|4IY#B4kzeV zk-te6K0jg=Hvt}_CUmWbHvTm+NWVgI-OI6C?`+5OFgOW;$MnZ`IY3{~>Yhh^^ z>I10yRu6L~a{1+#FEO+eR<})5LA`l9cy5;76R#3p`GlA>y80+OVBLZwwSBk4FsMD4N?imVdi zh)q@0ev07~pesZnY4C*rRh?;>9{di8b-8Y*K^Yvv%`?Cwi4dYYt|4J5)u%Np9-=px z5P<+`LQ+zoJZ<}MDZiMGErlO1IwYqg8g#>&Bt)w}E-0L`QTXxdAoEh)uFARc*o?{v zQ>p@^y^=zA-Y$@b3WnM51VQ^tWPn$Ci4$LQk+Y4 z8#4j8;S4s=IaYx^^uo~Z!<-^>2ogX!Ml8XhQJ}oLxON1WaO>2m$<#W!fOU2kh0QW|zp)sosv?;qg zUTcZ|(p%I~H0~E7wYoJXbCCeq6(AHtM00zepn zld83GS6g^ui$(->xl`se}GG3W&73 zdX%Yef&Tz8VDzb$ZXd> z`lM43JChUUuo8GHUeMe=Or^BYu7+-HjcVoHyZD`}LmQGZNoid?)Tj3pbE!R;HY+PG zI!>39(z#=FZ5wUJXXioyeHApfTd#XFc0V0YJ!iPbD`CKrrqoiui6)fD<7b51-ozY0 zE0S4hd4{}nV3w1og;zs80B-HA#{7%O%H21}+jsH%Q*T=(OOS>ekf_e7$3QL=q?DwE z5!RT)`_YM(@;JmzCXVE~@AAF7?QpVOo0m{Ix>ufsa+{sEb&rsFr;|6X+Kiai8}-^# z*2ddHP};DBX1qIdD>SO++x+Z8dEcp+JI0=&Z(|d z7-xNC+2bud3ZL7Xj$B>iZekU4O;r%w9)ZebNq1`Rq?U<4F3jkZQO}`VbyMlnjN12M z)YXn%X^Z^S$It}un)(mz{iw$pdOhT=mA>}w;+u4}O@nBZ)AAD3hXaKTt5=09LUY16 z-J?7*hZ;~6P3if7#AI=@Lcs9nG=ZS~tEJJOLr4__lh-V1yb9u}#Y$07Y4B3(VMth^ z9pks_!RQKW6XavMnrK3-+_UMIX77fiT|Tvl&4}e5n2Av?fY<&A1MeKQAkiAj?vqNT0lY4wFapb z6bGonff>zN$SQ-vJMdnxi_lQXp;8h>RB{#VI5f_@>PS-Y){ zGbZ~_+&=StNA;?eLOO~ZSd`P7R6TnL(tmX1aBhN`BMDhF1bsO7CE|yv9XjF1Bt$AG z4!*ea2T4UonpG)Jc*CYq5Uat#DG1we8*!gCD_YjuU2`hNo41+-L2>7GoK5S9G~}ml zMBKk6XiR9RR5uh8Q>Vf*r7MdC)x$v1Wjj9$=G=8V-U!`}dYV7jSqf#wL;sfVwR!pzImsFxKSsij`v6 z)-kyB4GVd4RKoo>OOsqoi7J?cpSdm?kSWj{@kzgTzPfJ>ZpOqYD4T&wS7mb*#6*%S z5gzRl#nJZU$y=Dj)^Xy^gS@yNue7~psci&Q&7WF zQ`akqn&7Qe8 z&f?>K4_@DAaP3<{uUDB{mh!7KF$ppe7L7R6S&MPPw+DnbKr>MpS0>(D&f63Nr8*ze z#4VR*Y`dJF@`XtC{-9K?%0HF2tDYiqN_TeAvcA+Xe988m&uF zNLBog<=1xGu(|fHp!5`fg{>-|%Uf~;$jWFSO>yQ{nh+U?2Ty#YrZSY-E-Cd0DgGaxGBR=vkw5P)mGiWLK4$eNDA$zTv(wrYOh@% zc^FM-JMi|crm7&f!EI7aNzd7Yd7xt|DuykKPKx+CHkA+SK=%D ztFdp-1%C?PQz#$r4lDV|$mt%Cp!Xaj<&UfM`xFzkCAo3rOZ~xAaZ;yGc}Mu#XJ`pZzm3u>r#!PlKCdq(!b+TQ|w5tEb+VESuSk!Vx&HTuUkVzGuu#qMjW0i znMtWgL=>o+Yqh&PHE;r_#M4#s^C)AI;qHu4g~Wm2E1>!YiCWKnyV$M~FE&|* z4blSA>^Ud{Q%)gTlBCw6po|{k-YMcNc&kb-HvW4_+bkxJZlJ4|x_O(DeN^n-rL!N~ zgK%=j*%Ku_d5W0K$+^I7tlILErlkYLms8MJ85TQh+HI?s+;tHH=qPvtRlH|6mG<$l zWCftyMX4iBrA%h3;=|9zn;rNvBCSoxjOtX=jktrQNm5lp2qJ_WYPQowG<{4p1z^_@ zPXvXM0@n^pWNxj~4cIaxZYj*jwvcVI1gDCaAmyNy9Y00@;~{vV0*T~VTYyCzbV9kw zTmJy&Uy{A!a&`G1+PJBYs2r(U6%anG5x6Txofrak?ZbV*$l^U{lYE1|Bl)3?*FHL= zn#grB-U@Ppo+4C9!v6q;!C!?HxoeTZ$+<4;bC&Rs{l)hr5K>?B7p$c91lKToaOsxu zfl4UvnwP4zHBwg(@dp0@=gs|P;~jw0Hx1KyZbQ-jZQ{gcW5q_dU3I{fkc5H;NhFd0 z7_aX=>1l(qH#Z*uG}F?q-MQ_KOLgT1jfe(P3DnR~aX(s^x6h#ORof2vCe15}X70ur zN0^)CjJG}#(mI5aTVA2sl7M@$M#Oi=Z(c+n=JZ#Q{{RZDGje`aH>NUU8?DRf&7@Q7 zq58g+RPNS0*5A6zvtO*UEO&SdEzGsejPywjsDBf0r7F*`(ucDeb~7Vybr+ipgPM7R zRfc9T7kK%MS1d8Sav+4YvK?GK>9u8ix6O!im~h9`My(3ea~|v=nt)4c=DUGTwyo2k z$g)Q91p&G4vaVZ`a7paPUsek_tQJuwTJwqqyjVI4<`yKO7?%WLJz(pWer z-IY;esu;VxE$yI>x(U`=Y@ZbqQBpeM2K}$;Bx+UT3fR2R*XR|s*c$_G-6z}ch~7fq zVQHZAH#JqK%IiRo+Or0@$8%Y^I!ih=T~)Gnd_rc;=TW}3FIa6ZTe)NAHtZ$e5}56{ z?3!feyo!`^r_+k&TIG(g9eOPrYvbi+bh_W(1Ddw8ua_y%ZX0^-ZA>M}ihfQ=%oWq3 zhBBz_Sl5R%uBw+IB#;=~C{Y*)muj^ns&CA=5QLW&JUYmxL%3kK0KNJnS$_r^5|doJ zxL+*CSItaTV@jwJJ;S>Q$qz`?77OP+)M{92WWW>WJLRP*8h|t^C*R$Mh5_KGAi8iK z@kE)qE@QF~hEpZ+lR-TZPSb#Ou#Z$K798+9?;nC7>m8QbA<;5 zCyTIa4^iTs?>q=9NTqTDxQeE=RyMo@n5P^gQ5ko1rXA5CAxW~PqC0DFr~IDNg=37k zsOjVlOl$V$mZ*iei4fdb(%Y&u1KU%H%S$oG!B(>O@;G#j6?x=4>}|1UeYsPO-urb1 zj|CEzkWO^P-wR;rNGF2Gx2A3@I65m=6;_Sc@hQ<_#syBvT2UQ@gH<@QKnHJbF7n3M zV&6|pGNyTtg6%Dl zw3}gO(fh<|r4qsv+Sz%uy32x&wxorjLY)XzMk}njdktD>Ro+`Trf*BYAwgV^>9>nH z84gL2Yh8x+q|n`IWCR7jW+^ovG>zA>Lh)#N`1XbidH3hVt}}%^QB= z!Zs`xH-r-C^5rolsLr;KY82yg6oM!^(421={_ErJEnFng#4bO|{#&!w*%rFo^)M9S z^_{#oowm!y+);L%B}@y$qEKWtCj z_nTdg%Mr>Ht(?oU%H7gGN-N&W+BX@C*1-_3QPlV@r=r2I z_Kw$u>BiH#-*0ixeGcw>j8>FO@}wjcGF(ntMJNYmImlm6CBeUxd%UW? zuB3}z(}+ELdp)yJ4z^X48=xNZ&obA8!HTo?`(A%d#-5GbmDm0WZl-P=vKwgIAx zhI@m*=Dx_e&vkW59Ct5i?)RiZyl$lI=^ASmWVY0(>QFxkE!9At%&{N2A-T5IjOreR zF|ls3`3H1MCqum|SDVJySOv9OE;099H6$q$ps2dp3Kh>PkECLw4&#LBAn87pvCG?w zusWbVV?09jy3;-zl#>}vo{LEoBfhvT)$1NKQdqn+u)2>5VB6O#iyl-+Z#v6K%}TD6 zigm>!BE}WA?fE4DQAm+*4lVZ3qvw-Aw}{k0@2SOlSil-B4$|rGaH@H>%g^dks?ty@ z7HS9Ch-ZowX=@1x%AIH-0m{7Ma-huMi_Bp>k=?e#_g%|jw>J`uN|4Zb{RW6mtwh)8 z!#terWCW=U6}f%eLE%*ndi35c&EK0>V~GJU_S7Gmm`6hFwdoc1gtyb%jR&(hlE@u5 zGFLMGYIi4_lV!L*>q2SzOtaV(yY3fg7gHwT5;B93!gPcs#nVym>4{G0PVbiuJkw9h zZJZ|k(lF{5LcIR~5|L4;XIx-jQtux&iJyDi@h#5zFSN6+pLm*Oo+WLp3&m=x`E0<5 z^r-gfC9gBjj^RpPp#p}uuvXb3tb?6M7W zNOu(_YztudWnN~-0cBKysT5K`C&iy>#cwZmYtj|-ZsSK!TZXDkYHKbnOr_Mg^#Lu^ z6;s`aSz&uy)JBTqX?Gi)&2fl=-_fn-E0kZ^FQNmJeMg{40aZX_X5SBXnO1rXgacbpUK5?z>u0wJqDKR^f6eiVSedf{62* zrlCMPaYePq%-feoMZDy5*0ST7a9zO5na{iL2*ec4XH;mll!XrF?iU!#rHqLo8t=t z?^kGJ#*uSttSbZw$lnqRu&Md zR;h6(GwwL2Me_H|>Kp>t?yoX9X9mfm)5h<=H!u4xyj>*TSNO~)8~GU_L(S5oLX%F+ z>s&@z;bHtlH>X8%&+jXtw_PE3yovD6)O(Y5mEKGw`TK&{=v@?iOA94L=hU1RdgVSs z;jKC_q&r2mqt9a9Al6o^DY8*>jcmSI-nPFUUA3w_m7t(%843f8>oDlwFmO5*(%TJ} zF{yGRvP`Y#5GPP&VWGi6VTy{+fb$2XjDJSdurD%RlfTvm6fWD%5f`FMv$tf zA@9YjZ)9G`ivrBImK}z1A?AzC;w`A{jdz%loIX-er$KdRTYtUO_hC`E=3|R_r3TEm zbV5PFG5ODqw&lNr?&imC46_}!B``vk;Yz&Jl^+>B=D3*IxrrTn?7 zeOuU9xcxJ7Esi)TlJ4DdWOY=9A?S@JN=-*-DaJLiZoItHUE<}_J?*mjov!wdA)j3r z%ZX)Aw#qY7ND5iiK%z!Oj??Wp@A#pNHlPZxjkh44o|J3p7;PgXT^BI8{&Ut(P+VnlNGUe$)nIuG{v8*1>aHgDTWgD$ zf0X@&S#|`$xA7nIx9e~U@cw<4rr~I_)S0ZGunb_`rg?TOkGkXf zms9TuI&z(Vd86vD?OZom@e`(%u2|L@;YAKnF2Z{T5vmXnxGG(H9$rC}X;A?=_MCYr zBPcf&m3wznws&qY-`!LxkunI<9{{XWd=9Omz z@Uo;B%;nIiSGZ!!WPgUL8G-MV>U9fjw$l!4LQ*w2005JpZXHop41g~WFFk40dk#F4 zO6s7hrF#jc3lWe%?iv{F3d#rD}Pxa& zEf6v__FyWE3J8Rn>P=2PL?ytW!)Xa5jJt8&*nYCkNCHKR@jB4QzWoPpil8BSe5xJD1IZ4~ACSn)lK zw|o5C=el?Q0GQ~i-RARlyh&WE#P?^&cD`fIIHj!W?4#?VDa!NtaA}3pF(?UTg39njIV^zZv=mGRDQ{C-rG7lb zj%`xyeM-U_Qc#ytl95x?WGVF59&;*}4yy4^p{$){U2>*i*9Z`Ys3YyL@PMU>Lz2>E0+iH&%J4}x?okBz8=DOxR)WerH ziHUHnQP{BpmXKTV4~Mi=<@6Dr zR4vbR=^4`1FtObxeGQ&4D#a^r}qKHv}GUV=CJ)nkccBHiF_+~tix+m*EswTVi@9 z;=R7h5_hbLJO1*36yjE;Lbjy#fXio7gZy)fz3$u<~@ zxK?&rk7|gNf{4ttod`lZfP?ksExC%uG>;p51%gnS%hgS(muzt~R(h*~Km0r&(;!9A!xyLsYf(mLs6@vtFCx@##L8kn5Wke-DAS!?Uv+~1hU=AE}~5U1<;fB95%^-U@2@B zMD@1^Lj6!b6T4Jihdrgv+lk`{JQr9f;MYnauX6q>= zN=*T}FacdVHB+ejaRaxvT(-lax-0K;J6CUhM8Il%6-np*8@gGzy3>49zdD$Xg7MI; zUo7F!7Dur4#+kG?TvObZyNO)u-agjc*y39&H3L$pV`~UXkd)LQkbg!t0{X5{5K$JJ z8%WlqcyiLL5QKI$z=Am`8pFzoOq(PP`5Ei=g4Be@iy@MMGBY66J|&O5N_&Z|F;~oO z^5NAEg=>3bxdos{bUK;^Jt`vH%5LF(yG?}JzCugxE)Y;Ui&gNGPTF_SL>^&|ilS&2TFdGFRg@KoL1U8{B87S0d*p$IRbI5r)_>BU) zfWsrf38*M|=>;kZ0F;6e?mpZYFFl$d5|NttX%F0l! z`(W)$!qF@)qM*CZ*|yv+OtHu;mDK#H2AOwajeZ%#F1Xq=-iGwg%bJUV99mSZ1R*Ah zaP+TQVi2)tXk>(uQQL$(z>m<4go=#lG4C-K%P2Vvb#*=>elR*=L_ktvQ~@2fJ{T;-P}EwnbuN{9f_4TPmV zIsu0*G)=l_fQa+4!%XJhjEQ)}BoC{l2cM2@{hnlr0B}A21ps6@B8k@{hu64N) zpMb@_?9Js)wH?&I96O+w2&+UW1!@N)_F>qLt*Wlv+=kSOKh#u8D zeTRL!7~$_V-1)DYR@9;pqACCsIdnPWR~!>%mm4+9#qGc(L0S}#3TaY)Dq%jLmGdgL zTz21S)>bNq5>mSN5lj+B`HdkywYo97s$%qgx?x*)uKe`hyksSvkLm&#g=c-r=(p~8 z=Uuu=GXW*c+E;&sV>!o{*x$K_;dk1V-&S8WV#~P4@gh3^0L#zW`qXQ?w(Zkt?n}6=dZ{Ej zNKr`a9etQ~fYBU4t#iOMB$1SL%dch~#%fYCI*nZs;rnxtHid!q!b^`espeNeYDqbE z<5$H;(*tO@d$`wvT#Su80pgQ+X8xx`aS&@oq*kZDy9U2~Q@-EgLu&KjoD5F#_VgEH zsko|Tw0Y?V#-#-Hz@LC9nQd!$;Od&4JP=ZMMdenOo_Q@!g%l}a4w0!|!bjPGH!lOw zl!FIsE(jHCkAkie?yuhW3(cxpE$_IcIE`y4ZNDfL=>%6ZOtD>k(R0?>Ym9BZsM4a8 z_0=KVH$;o9gg11arLd<^m!BljDXx-8Phc1%_lCNu3p@iLWn*m(6DyElS}mK7WIeyQ zncX&^hLsxb%S&Y{D^8^&OPSRNBZy3khhB!EUN+0+b+9rtvE0E-xJYs7aoKGu^IUNW zWgt|Q6F>kRaTn%ltXB>Jg)Lp)n-1uyxi5vR4v_E)`LuD4yt-^-iB|`XY%%sd+TQ9W$W(`%N*{ffT#TZXsA@@5 zN=Yg21km>5=6~i-de47}ltxQSL8U*zeOUagY>RwmS~DmPsTvjZvBO@AxW{&J_SxPS zUr@H4xaICEQYG2sd24<#aR8Jn%#e)7_@f^NZtyZ*PU$W#%PLK3b^BM-9m)BC%VTa$ zt+qMOYj_ad53nbd@6zX_?WGDttM91z7sa$w{oVp2xZ%}N(Wf0fo!E-VVDYeU$T+*e z5S!w=(Y`WL2E~o(9woovsjn=+;9Fg}_`Gj&n|-(iXK<3*S&k80{3Rfqu%_3I?!RL# zrj_sN8q}_X+0kd@W2DK)8zSjCPTRIi}L4hbspA(&m_O3arH0Ex92AzW&Pi9*gy>qJUwff@}&8lxyWC+k{$AtNk1&t^Np?cMkL+<&&p+Q_JkRVBt^<67m&adz19N__OR zwG|{Nqar{wCkf*k_P_*&t_nvv6J2!YL@3g9MIc_cuF;tF$828bZMp8CvhBhf&M@Mc zUTQ&72=JcKg>JEymu=~lp}=w}9DYwDk8yMoIfRNDX`*1hXOiR31-|p@Qr~%XQkJzV zQBsM_l1^t5w(V;h(l{s^nIw_@PSl=jEnRmE2H#8<6w^{$Q*VSM9Z6bfMF==XSb;V0 zOT_nu6I_tu9#RDppwI*ErY9iakameFPRKft#Q+a3b=k?$kbVe*iR`i|DDQH+xdEImo`?8SPL#lc@*+M~DsH&BygHh8I9lf@1tuG%evzZv~4LkzTH-_}E$k@jOHtmV* z;^9qxtyDIZtPr9Um3C!A_G0(tth})bUel0Kr*3B+^3Vv;t8urn1M|SNs0s zi|RF|T=^t3Y%i+f8EgUP78@yhNI@%cYYPDN#ub{$MdvS&KPTk$s!)m!jrC3_1NKmLL2RdV5+u7@Ei~?v~P*w@K z_NDF`?AzS5wd3ni3+%{1AeDcMiQ=opx}wTUwWo-V)7KtF2v)mi2Jhb8U1f&JHBu|2M*{zhwK?8Zg^0Nak;u7U0P zS3{4^p7L;`-5b4aKebeDV&`5~^#{8n?_N!Q6W6G*gh=Aw077Xj1(zsBw+f9^b1FF0 zc81Kg!rPc_>(WhiHC+Dy+)m(K?mN$!x~E+!prB1%l;kK|r*B;DYmy?{rL_CaMnF@} z29>BJYs)@|6FEAVNE#~U-kprRYly894f5d2_q)8!C8lM{kmD#&@dT~aC<+1An00M% z$l(<_QAK5AbeB%)97$FehP^Fwr#J}mwqC`Q)rvxbQ@ZX$@Y`iORF|ZHRtNMFtWmd5 zb#Yk3(&zO!a&Dz+n+vdc{Ot=EBaEMk=7aYARP?T`arfo>!JI|6qTYQ(bBtqmSS9;K zwp$S+0!Q{~TFRbwo!}?kj%m$zr#X?EqQ(*%;zI7tt@%~jSQ}9;X6QZs0MLC!Ebrbx z^owTMY1lklu%uk>R>Un9y}%aY73L_^zPFrKw@Q!kCqHHq!Qgiru0;1TQKyoS-kZZ^ zHq2UMp{X3zaAwnPyvj@8%c3;LXr{}HDnQFTmEVmn;^tVJ@l|DZIE#CS2L@5fb=jM4 z*(U3^BJO*N5eeB1rg3LeWi1YgsLLaY4u18*Cpm*vzHF==zn;d-?G@SByDRx$ci(Py zm;QFiagNh0xV39rK?hYmquY#gGmqAE+XP>_Yv<1GDj!HoB zN>C=fwoSGpZYJ9BnR0YdJwXPQBc*VptR>ZV1cju{45WNgjh@4Kl!;2ZNmB@X{{WTO5N~^)RgM-%2f8?;(VLvP zA#N-)3A*SVO1-thQaAxb=G~;asd*?uidKA7>GfhfsimX(MGBQc?Z<*nMAE)aW*jG- zL~|}j`ljhnP9NCC*6malQf`LS3Y81r+}u}%#*DaLQcYA;D3F2eIJLtRY!zAlMoH#0 zP>$)^uDu>?$yb=>l?aYJYs)GeTk)6Sqd}s~wId(}ichba1**}DSK{{To_r^bPAWui?QFS@0vN;EVGNz|j< zG1S{4_wdXctl)a_7q}(@6)$+y8%_d7v6(uYc zm(YgN6ol#&q3;-&?N0c~t*eE;zHWp5gs5in&YhJv{Su!XpLM}ZjM)&^Kb6Ex?TxUs@sU$sq&DN0!BzqNykXzHl?+_vn?CIarBkLxqb6} zb$2d8U0(YA0ao{CTwZ<0o^r1(MN405i8UnSMuIl6fDTKZFK%R(Lh^MbEZgo6H>FE( znsgsd5|TE%iam^v4&tn@7V}>6+YYg8vfKh=N>ECgTS!?I>q4MUOlAACW?IbB@-+*l z`&r%Wm5hymI;|#}tB)p33)BeLSFO_L&3Pcmc{NLVt5RqNV;I1Z@e4r5Jv0bJ|nE$2UVb4MUu z*kj zHtP+cxU40=m~i#f=4QUYF$ag)!fQV;@mIas-Ki}y{zZMzof3B|7Y5n+TUfa|;zGl< zr~;kXg#0+d>J@bU;BDJOm?}z$_a*Y+nQ19=%G5`I8dExSJw`0p`&N*%jNV!7l9f`Y z8*MwS_F^wr7eZ|jsAIMUZ>VQX`!QDBSq89lGm6so9s+5f=Sjg>r-Nupl98bxbORyu zV>}?Zt;8@jgarj&FAmtb20YZh8;d2Rw;^g!r9tib@qUjT=GkyqwcdQ%W%%e)FQ~oU z9?P;V_n4E*GFzJ%A!t4#hZUuC`Z4X1k1|Z=9|aTMy!f@Yj!=1o$!mRVOUB=W=f;&X zf0h(o38iW(YDekCg_v1moYR7)O}uO_tlCc+CS&`^TAyiFn_?m`rDRm^jOW`*;&r&9 z_Ry+ayRopzScnQ{v2xwHUo8TB^ddv40Sc`%J(-MEG8;+cmo;iTd%JMeu}D;qB`*B(MZj(dBYVXOmMs+uVNb+ydMH^bc9{k21MQCcEZ!+xQN@~owL3EbN zg3n>;*Ao!5g;Tu9FqGMSG1L|oQ>9hz#FSM~HwHHyO0PWj{emAKU6%Soiv_f_w4V_r zVAB`PkjWi#x!1pUHj!fJd6irCPT=9_yE=4h+mobjXPngm)@kK7&`25-m3u+Y8YV)^ zAnTA>PL<8LeBRxQ9n&RDl~dH)UmM;yR?|6i9<|v~a6dP54XJAmmb0R1Q`fT;K|S1g zGTtR_Zn4E(ZFWiUu&Ud4UAu0+6}2}BxxmOok)=p*ivIEyN>dsgyiSyV5KycOBXG+z zSRGIk+BYxW?t5|#n&`xI%d;$)JAf#*`(S(`pRW+|?Zyd7916KwcPd$}+Q;bT^HP-Q zQaLMSIl3&3qE7ePXajLYZiRB<^h|)7{6Gx*@mzFYDRbq!NZr9`Qp|Fg{{TKMWoHnQ zcE3E^whxhG9vhX*!bhBB~|I)`LH8822j($?W=%6d#t|?KP%b$_GbjMbzE5KI10p z9o%-}(r+g^B8ggh0a4vdb2ZQ`kKHw*`ggahB;UjuIYayRYTLH<{goOU9#lzfrTFO; z@cDx<55E(*oP*`J9a^eZ;lf34k#|ObrR|S*Q&Sy+%4%HEr%+?-!jQmLPevC`uWDS;{D!aR&>$a%k z5sjngTy{L0a2nSowWX@(`@R1BCzNPq%~0T^GgwoakN5N`Qg~x@+N2ZbGX_$8q6s5P zO29u>Cid*0U$m;8{kH4Ey{C{|QX7IrhW%;1LTw*271rGikV<^kloTo}-Z9itS_!So zPcXi4MVxhw_>c`+<=E~#^-0GYsZ2`T_6vog`?+Gam*qDR$uZn4B%q^PDOCU}G$do1 z^SeIc(ZojYbT01MUCy-0F|Lw2I)hp%_j<#Rxj4UZTbXb)uTh!}rW>N}BS457K#cL0^va;St#~J}zD<*qnZTnj&WsC{budE zKGEJgB0YlGZB}{PV%X$sc`7;zOQ|BO%#oEz#s2`d*RPrf^%{`kvF`NaqqV$XV53Vy zti17SZO#kfOWbx)(j>Wfk0_^3qPCx^H36MUYl|i(LfhxGrV_%}<05e4xlM9$c79K` z%>*REoo}Nu zgG%-j7UGloAICg)ib`6_-v|EH*_+gV-!sFb{{SlB0?7qrpYd_4v;|*4YF0z{dvI0d z?0I==qe@iJcH_&)8sJOHb{2|Gu}aZBxI@iG461h9oQm$)xj=Q`nZiOHjD-`MdJ0rW zc4Is~w2&52XZw!Qc<#g6DhNsYjTc$0+Z}G?iFV6-NNJ0Im^Cn@qJ>2&Io6#<9KHQq z(|xlOqj4?-5}8YdG2)=aM;<5yt3nj204hn+G3Pf$Ykbr+JW^WHMFlBN%HbL#d5cxH zG@kC<07`<0DJ3SDI4CHzA=FR6y?-LPCz#kh~HB_V?j5<&RH07yQ(H zKc^Nfckv0f_Xv;v2xUZ)3OsuI!{fw5Z z9wgCOy|3uUm1f6c@xGn6aaDoTm8xy#V`4x>H52me6(|~ZETrO63`EcPhe-6Svn}4t zdk;mgRPX-)DAU@Vhj02b;(aFbe7W4OP-dx^GXr;7IaGGV)XM%4HRw6982sMo1Uz&kD( zq+kUhjqGf4xJi(e^S{z`RZn#9t?j(!xi52Wd6$RWKX@N~#iOlqO+X!cu@N=&vV2CX zXv6I6ZHNL0+S+;(D1{Y$;nYS|B%C%Rt0P(m1*Vn%03X?eWk(Mvo9Go48mZfa!Z3MP zXNsM2<*+SiG7`v++pSW*FS~G&pJ&C5YXo|r3zK)`B0QUqiK!vdDH}xN>CGsnMW0V#R{e)jEcOX0-jLtSF)Xc zyhoO+oJBXo3Pz-@KrL0&dpr7^As|l#h^y?d(p&Nho_kFl)R3JjC$fj2!UQLbc_@dH z3X+5XT}h!|(@N*l4BXnMO?6i8MOqwER^ogkzO|tCVAMlZmVy*~2_9nUry*P?<)n*{ zzD|OqCADz`=^o?hXpcHOBmE{+483PIA{M|nQ%RqwO$w?^t!ixr4`&i6)& z>+By-{6EDAY5N}6V&i*26x^2(CQJvnL39+NwEfUAPQJ`dFFh8mh}zJ`K|EBCI`5e( zcg?55f<}-#fb7I5sXD8BAZs)Nfi@*x9&t);qNk-Z_g4p~Zi&zcv>O!r9%h@vu~>Rabju zx+;EIUx++^)gV!{9_r#F$}cr?v3c!ipa}wI;nCLu!U30`Oj{es{4GEspSXREVx0^l_yF1LB^Ml0JlYl zW+Zc7dbp-MmsX`ADH$BJ_4@`T;-fgK%jGTk&p z)UWQvLcRmoYB3bti(K$(r)9xP$cqR_FIkg*wcc!7o&qOZrLgRDw50TD#FS;ylm{$I zMU!>C!(@!`DzACB+kIgBdBtipTu$Z=8NG2{?wf0v(Q?}HTI_cug((lRRaEul~=@2!KYuQl?!xV^u*!rwYF)6-oPOzN%>qo}s8HHQHYAwy6D+;M3mn$29t4k0z$ zSWQ$@*QlpV@ZpZANEp3GRM{PU)&8JOv*3RxhX;J=tvQk-43e=NOKqWLM zD&oG)(`NhQXVv^rD@AdB$H>KuZbY;G-P#9wYfrT?VmD)SNkqmJx7DOX8j3(9_=%^p z6HbZBzZ2O+(w4(0am27{*-_9>T6SP#8Y8vKWm{#k(LPt4iVDyZkrm4ckd+SwC#)J2 zg)Ih|rl3;=M+FD-ve-h=<_8;4AwhmDk9ItqMgd5+<`U)22`dE+B&k5q=YkF@P>QiH zPP;o9vmMuI6~jWK&~{+Nqgz|m+Ad-Gg}Su%@|M(P>S3hOG&Qf&ibiU4)x~z7X))!t zAdsr;_iNKS4;67$mQz)`TSfy=Zz+~``*~4vUn(O!!a(iASsg%~K~nl8FPI7FXiv8d zikPAmr^!t#pLPW|At^NI6iLi0r@sz@Z0;zmxX23D*M_;T-+(E=M3JmfDHSB)SkiJO z0Nf)AIZT!QA?7$GMJfBVAX7?`vDUq#4q7Ukxu|3)(4?m>&b@w|E)oR}uL4}v;;lS6 zU;!ztIKvlfM3q6iTOMWjGg~DN zwDflat+<7xf)s_M5OGZ1*q1&(HJn1YZR_AkiQF70u5UKTw{uq;anGr{DpF@|_CE^c zi+U(sx9*E#64Ry7oWf)ZEeh=v?ZeC+__($N{9M*?XKws%N=r9t+SRAfcq)j8DWBQf ziN#}(BLM18b_~ubtGJ;OttP!|*9x`OD5N8ZaHfYZGjxY4;T z@NO-;9Z8;s)VRziLR(omf;$Mrt^A@#0-$zQ7}>z)5Pi~hDC~xB1~OR-2w_M`4^WCw zcGj3yK<=6*aZ7F2UUf(lzHz&3=N`LJh8qe)BjQo7Krq%odPygOm$mZUGeBmfs8Sgh zE0YM5BjwqBdQ0R~r8Uh*rYo-?xoUSUFvC`gkc!P}BiA=zK#~_6xKSPD)NA&TJ=kqI z1ke=f$iK@Dc-1l`g43=fC@V^c)H)g;ZX%{MR=5s`<$)3%yu$f65ZuHs%w=vXN&=f2 z`sLk*Cax>jucT(H4=A}I=M|4x< zb+<_+)f^YnYd+^=u&r(Ej{&WrSFXppw*{ur8WqKoV@ z{{YgC8)vpl*HjK6XW1|R0IZ%m?aqrG7rn`I<97O$3&k(l>=nSVaWM7rjnE9SsOJ=Dpw6y>QeiZ)zhZ9V9OZ8bk?*9P! zKAtM*yxLIG#ZSUU3ik@Ge`5+mZ6!-)P)cZUi?ei zmC|Y}mNGq?y02xLXy;)KY0$2#?yqdI5Zc?%VJI$pM^N;t$GW)paM)k+Cdh8DAr3Ga zl!ua}Eg@+&LWl%|(--Ru)N*P76+r60+Wk5#BQ>)wE#F6Knk(BkCA5_``a#Q8Ts5!J zTupu|)dl^s@+uNcyDa$ayQxOBIrtsm~7LB{VFcMHI`fRG|XD;~&?9q)`_ebt0Zf6zk)_0jf~8h@zT& zf&*fwpspPv)F4X|J6P59;1Pif-cyJ*TzY}+CjjF_jaJG^4nc)#9S&xgDUc(AwbhXz zID^_jKd%R>FGBJbYvNP1_LGlQBH*?wN$3`T%D~YXt)f=3%a0oE0qGLgZ)Gk3^wc<8 zS`ezGL?}?!hL}QT)FQ@Q9ilU0-J6?YZs>cnWo`%U8-l?hPT23PEALwwrnOKYg(T5Q zB7_q4WZkcoF(gwQ&!#8_Xyy&UgLLe4b5Q~xP`5{ zWh(K{$J_dNs1{zsxZhztblLZtX6j{@lI*)&c}=r(VFVI&#FOQ@l?`<2#*XZuWSuVr zSJ~}Z1F;5Xb;Lmf+M)R=l!HZ;Bo5J$%M)el39Su9L#`yB8^1xpf)Ee|=E*f%)g63T z@?WK6%Vd#9U!m$ksz=*~Zf#KsBf%VPDWD0HAS4?8?_3-t$@L}6B$uLIty7zM*B^%= zhhq3i9%38{O+W^sqdYdfkFFxq%`J|&baS=M0-&iLPWt4VR_VRpE&GX9i>=B-uD<+O z@eop6Q94o-(trZFP+^2vxL*Kcg<2Ddv$Ml+=Zzdsf^w^vIhOAQxL3|nzg!^4KXCzf z83<&5lF>-SOq(+n*HlE_uTq_7akEZnkI!&mkX_OrAfn?_19xTBm*}Ps?r7 zEyu42A!W6BttmblD0lS5AWqiax)|c=4d+c3*%S_bC6^M<*5`62r7CTUk|Eriazg>_ z1Qd$oMS6WWTKY2h>A^VLSxDoY5*4}HTZ@mwZ#%m-^2u-}jZ~}!Ks6nN1l;-Vj zm@3B)QtFMJl(Ce7EN5P7;qm_f8u5&6!)Jf-S1*2hMb+Bh3PM_1lc_ZZnU36L8GOC# zqacbdmD-zQ6xqY;RbaOi$U8f1-KItfWtizCs=oqN(v-y#(ihEe1D4H_yNb%m&t(V- z1hMjf-OycwXSpp-$8;@8T0kV~>^bF&9zzl$HFpI_+uO8c4H?BhFVHX7JM;!Y3Tfus z51f(`fHTBI5=R>-ua_3m$tbS`?Tu-(S(WEC5wk5>7ZRG~`*8zxa_4haJ6vp0@EWBR zxU$hwMxwJ-4&hufEY(T1u%rp)uQitdB$PHQk!9?GN!CCx@C%USnlhokPUQPR+;j~&Fj|ybuCUoVY z5h7|HV#=*g$R(i;1;*D#4&((OS^EUx5*{Y*3=UM@T3o^ZPHw(GV^<*62}w<&GKZtXGTy)~x-0@Tz( zuvU;$@l))^F`U|X*4_fx>hDMET~)F7_HEe5Hfep%RZ84v#rHl}zDWFIeMpzKYzY$g zZQfxDEx=@jIpcDnI)ETo-`R-W)twe`h+!0w+5i=&V{K2w=5_7KtPyH!=v8OcU#KC? z`_-E+)or_15$qB2TMpF9OHI6nk{eW#fkEP>5td2!oNHaP*y9yx=F6<|YVSv(b6(f?BBXin&D`8L382*K7$%Gb}TY=o*`lv zxv7j^P~cNZF2P%*sxycr<^t*lCERpWw_B8@73*RKW%j5u38L`X?W!~^0XVGpJ5)Jw zmv|1%bLP}KBiAzxNJKPUuSiNBI4FEa(RvEv0fgyQ{6G7sZ{XLl$wDP9( zl_`f)w(Ku0D`X0x$4NPwR1y)EcxAh&>xf|-+?w|3&$q|jT|4fbS3TdjTbfb%MwZN( zMNUb1WlK?7)KsJ6Cb^p9X^63m0mM))HMo{(Bzu_BYEHCQ6Vb%moZaVrhNm5VC&(RS z>(Qt<3VP%*nRhx{XGGqU&2>NJY4^V~AdnuCIi_baULQvz?vzVOk1jNg0VF6Agn_58 zv!*UvN;q9+(9>0wcV_0|Jk*4OHR6?cvzBhwIjfH&LIm~ zsUUcW!q$V|O>Zs;VJe97UgBF@(8*F3rddz{RXThmc4KkE*@s$Fofi~uob#f~hjIl; zJlS^JH{KDAJ%UxpTQyl%g@X}jPP#4i|%x_q7So#yF(L1+0dqN!KiFZFG*XiV;O9@aX zDtmDYVFk>Ms1;TX!*`B`P$YLrDAX;bx9xV)ih{!t%me=<})NZ z^E53iDzs2NhZ?VM8u=%w6kLD#p_<-12C+?#qIr`r^H7R61ryE!U(v?w>JXgTNZ#zxB*zbxW7uEx9Ea7DCYX&NXGa9z0e zY-ByQtkSHGpn|o&`dvzeGZY;?n7P`;G+_CJQDIqZ)x5w6T56gzi}9_@U0=66-L^p< zMZrwEh8NwX#Uv!xvWFfSds6;@@K1$P$uK!6p!1&-gFHwyPhL9ZRdLScwC*cgE07dY zi@Z`shvKNwY4+vch}`}NUR#iOjtYNc?|0*GTJ9`yp&o?s^#`(8+54u;zIMZYX3F18 zrr}|ffmAl90CC2(3W}twFJ>9+jIYeX>v=vlXjPlLdw=lj)?Y*XnRNsz`O6mN+^yC< z{SAc%=l=k6yQ~sIWHJFlltn46Kcg4jyK>O;3F0^`3%S@=y^OtQ!tD>OM%Ia{SPbha zT}FaWx7&*~!xmp-HC;tuc+UNFwOm}bhEpKi!dgR0(?u5!kOrYj*Az_sqbcSdOK;d& zNkhDWsB)d}w)u9wrMfNin)!FiQliK;2&QK&RBY}9myK{+4q^vIhQ7R%;~-O}f_t+V1K_y|fAB{oPT z9IH=fb~$%9w3`fWb4?f2t%KcJZ?2<_!%8QwEL;?u&7R@`bt+_|+ybgIKKxfU^J^Jz zg46e{&}8j+=9}DrJ8A92jpSgCnx>W)4s9L+oh?&@$j!W;^`fMA z5-~x)hoG&)6L;ps3Rqe^_>ZuE^n|Gu?x?02TFucQDb4-2Tn-6~#%|sd_>GOn^s3(S z=)TmZqyeA16I+3eA+H?OCuY7*r$%`vtov$Tk8pu6`{NfDof4~2ttv@MhF##|KE~zE zk**v=~(_E#MPgWNNi2Ue?z zceie=nRa?~RwsuQ2WM@YU9}!HLv`j_e4!-hDk?pm-0@1!vPotoYa1xRX=>I`D|+qT z46U0yH20ddMsX=Y6!8R;jOH6sE_GXc_iQ#Mo(hihI^#>)_jU?=hggi#+b1xsBq(+E z<&A4-0xp8(zV8P=96%}y+R!0h_*(sG{9T3wTZ&~nA;L&uO(ZW?c?yo6{Ad%g?8ur9 zAz>CVMeEUcO|vI(G?P3#oOf`*9%>QBaoyPXSS0!&>5ZwCw3KH<)sx{$L){ zCA)_L-`J@)F3sDrcxPaaC z{{YWTsdkLu>s?5;Ng4eI9MRgdN6z2#e&5==cX=?GqJJ)uKF`>`bprEPJ$o^+D&hro zC#F2S7NCyYCFUcsl_)2uND0oEF>s<#MuNg2EViX9fJ!t`YKM{>K&cv(l%$NaT7pN~ zaN^n2DnBt9NK#f3wKkVU6oNqoDjg0UbWo^V zRqS)b9^MJM+{v-h@5)YkAarS3TXAUAo`fAFRAEy33MW25CaEdJBxI9Xb*>XjBD#H~ zdn&1s1{Iy@q zd}-MYy<+2beeFkgc)D*BgXojbDi^uat}udJ^oS+G*}54jXLaz$!jdxmcTR9U!RW zH9u8UsGmt&iq$g73w>{`zq22`%1lODa&vDfg`CRYSkiQ%I;H_?%*3m-6KuX74`+Km7PZ;_npD3k1V983LU4A72hKb!#WB{e) z2m~4)&u#=ED9TC@)|MNi*;Uijd#R667QQHUnQ*L-H7Q+BX~%*q-$ebk+8>^jC90v8 zzBiZQLP+=GMmXvfS0j+=DcH*vJ7UEWZxXvufKt#sl|7>Z8Wmz`<&<&h3HP=(S1j^O z#%&urZ%C0H)1_&O*O0<0dad>8NcPm>yq8UkYMFVK%;IY0csR%M`LaY?H;!)K=zxrz z{#djvEkcF9Uo9n4$GvjI7uVaGn)Te-|6t+ySJY;U7Veowr< z#+j&b4aOlgW!)x(f#2=KJT~I0#eaBX)bmWs;x`N4RN~kSIC60yX(>IqVshRla8!(& zCa+s~6v5rw2W;<4af`X_&0QjaLWNUWRA_32Cp4dKELgnM_KqC|X4|u6S><9PW`b8I zy+H9Ic=G)rHZL)IdO;>4*Qr`b6)lx?Bz9w__EzW@kkQB93+I=2dwA?k$uW;`s;ecE zN{s<0tucL$O;#Hx3MYF!o4w<3{QeG)cmDwEaXRjc+qOkEg%sQnQgtEK0<@r%f>JUk zJXc+`tp5NK7rj5lr7P%HL)=hsl9hsjEn+6n80lQyMi*Dl_s(o|N55S0w* zG?dg~CmmAlp+QG^r(AHzY$z;sBRb#^xKR^!-x=ejm-G|S2dCSDke7+&HNI8WZZleK zR8mqxO*-JT6;q6Nx)V=xxp0MOmi*S$?H8ckq#zFmZF6`Qtf*VGG;G+3zsmnU`7>Sjt`D%1tzAU$Hw8cr%l%S}O zYK{?;;Y(Q2&m^X(Qi%x$p4>K(O@v@5_&`@mXOBh_G9`EpM_CD#Bu8$vD$-P{^vHg^ zESkN#pxr4dQc$gEu1CBBfnE`-x9Vi7xl_#(c(}T2Bqdmy$%?2br zAIwU_3Q`M*I+CIdDi3Jx!-lr0pIR`5C}c)Qr@IasD3K|8!!k|2?FM`T&(A|pTM3Zb z9cxl^6-%I``zkQ28&wOGr5>Gq=gE`2?UQ7J5;KCgE`)$6IAU^G_dhazYaYnaE7t-?~ELi|Z0ibt@DU>qva-xTWJV%OAiQirBZ z$qLK6_eTM4r3LZdIx>cBQdUAmr*yOr$V*0|2i_6oI1gVBb~6ZzvG=8?;Bu4RHm$p{ zZPa77`LD%R3RQ$RAWZXDZ+xiP(MBPCeU zg}{W_TVSnCHlVEgQ;iPXQHu$EB$$s$L~T`>aEzspYT|UGyE@@m{v@^k0Px`;cqy9k zI_~ll8dwpni_$6ps7*A2PhOaQ>0ItBILBLCwDmJrriSHa0FOX$zttMkB*MEUY{fQo zMo{=iPPlD$Ls_A!u;ApleZcEyOKZhKc11=-(lYJ{Q}Ej)$Y^9~q~s1OWK`<4Oqh9u zhGFHlh+d?!4x#1DF?ZvGXOR25JTxo4gcd#a@%aH8BK)Kq2L zgd7T4jtFfD?!a^-)d&IZ>(dCh5L39G07C?P3ROF5FaRadmj3BliJ|?|+;Gm~*gdp7 z6T4f7M*g9pQ%?ol?yKdUmmO2xb~`VvenC=-Xc<|hK8!=!?fX_-2?Iq`FLgI{Q(2|&HTmKEnA&)NS!4b0sbU-vcxWOYDBgk z$y>f#(n;XC&{b3`urIc~a>WQZmDkJ^R^QX2-L-HRt2?>{5W79TCzvwLZaPT%m4~q( z;T2fBm;V59YgGY)HBVWuz)#ath=_I#&=Hqze=Qf}**yh*WP7ER1SQLSSbFI0$1dnW8c9h5B~ayzPq=+8 zZAqbB-T8ZD(vF5S5zj??ZQFMH6K>pG+6uSbUAL|Dy4&mmg{XMS7q7b*ldjw5fbwX& zE-VAvYUf7Qk{2;KJ?5nE8{C_VMjYo|q`0g#s`=gAF1KV~ z)+lOJaEp9NOIT&{l0S(gd$D8}29zs0IJUUdC5A08Aqs5EMx|w^QnM7Ucx>4k9a<|K z7B6!E>N+VSWo;HWWxDTbZbK-)z({)7!qU3QKpr%ObQGmP#Zxnq`DsId_e#@tw%!># za?Y>fH6R_GHI+Jt18H@YX;XRitvj&1d5D}UqNmyuM&i;nPmQzbbC@|sJn8!m!qoWI z^-@%)pG`DFM{9r_$P$QVAg=EGbk1BSoU# zQc@C^RHUd^y96=>kYWW+^zjl;LD_><9*7k!bm~od_Tg_dB8Dsj-irM=5c;9$Lq~Bj zq_ZH$Zcu4SQlmNwkh&Vsj_eo9MWq(+x?Oe0LrG~asFb#Vbfgnn4`9Nmj*Vd_N_5jN zrwKYh6L&DB+a2AzCB`)MT47$iomGQ9gCa746g5aGAp1cCVH>D~&FSEjw77f^>uQwk z?d=Xjavhg%?CBe0d)(h@J)+kI_vYK~4!od%+8dOj;|e7BKvI)l>{ljqxb~SOePSGI z#4DYQapmM}5l2a#paYrxNt?U&9_!e*$(wU_*d}d#!39c8n@srYVd13Es3pKPC%H)^ z9>a+|HsQ0skFE!Yx6o5HOm(o0EgL1Na;Wc`!O^LqN`P_ z0xat4q6o^lr}bd~fRKemSBa*Y_tT*IapZ%YRN=C&JbRo4Ne`u_o@~CRP^B+OSSEmV zIBS0Xm`Ym~56m?2PFvfs-MrkF8XSZ&x0G8BHd3k++^jXTaWRUda}BM-y=cT2qU&uh zMszi(d4*YJ?5A87>Bppz$vDSb2RsA7tEqM44k>}gyDxQZ3*F*g=DV*(%u}sx$H=e3Pw0Wo^b1 z+tz&00SiJ^{5|-?bNhf!`Zl`wF4)`QCA@HEQ<_#&6TanoBrc*VG5HG zuOWz{ps6WX%|{j-9tG0?=?A*A4duO?X;`6kU+Cnd-%h)Qz0Q|MHC4n^f;vNoNH|^TPp<`7Sq`M%_dVDCAGmWaZ>)rOhrv#7OKoadLw;Kgiaj9IPytTdQyk*9VAcav zmpWy>)YEP{zdqqF{%sTuMATFhg_{pjZF6uN@T6w07gr=U+$03L=sz%B8e2#i{`Lg5 z11EcvUQXDVyWM(FxwPtGI7Wj~-m%;|eK?ViDA*f9HC4;J3$Gp3)xk(Rdd~-L#bz?6 zq|B8<>q%e4q~roX?8SceXG-eRMQX!Y$8O%5r#sRVl&I+x>;kxs4OSm9Bdv0sR+Kap z+5O|!4uDNO#S?Dku6KnyHcVpEyD*(MykMZ*55X(VYttX8M@qnQ{ANv)~O$_6r8=^Tc|iK z3jvQ2H>#Sc32M}9&~^+)(OR79tpEc&7-)!!IE`eGYwW>jP!poA{PoK+_@2>gb`_}3 zoqTa^Mt35Hbf%?h2dyiO6J~93eEz5K3lHwsaQ^@XgQc3e%Deh0>hZ0HFAN%TSzIS^LUqN%GPtBO{Ln%#2 zNLj0nt11Mf3X|M1LETwsanL$faXc2&+s(gooxf$QdN6VD0ZKlsc{<6)+hyNOygxa& zWHzLZU)_z6ho(p(rWsl~B!2Mh9v=KwyJ5Cuy}A5G>}mUe{i^ZbUG)w=+wg38qehP- zpP_S|n?vecc!&vc#nC8j2T>&C0LMl??VwbyG#J-7(W&sy5$|a+WXxl1%5pqfe8YjI z5P(HRaR~j_ZpU<$GaqfQ=4VqyLStO4Z(8IkER=b28~)Xr9Z9I@eYlyn%10=uIx2Qc zZd^d(*8^0Z+i6J&6`-v_7lZO8Iwcbh|rD z2@oZeDat4NGw{~6>5RjDSTM(4YpS~ko0*peAU)M`V+ifAn95T!5KR^e)D3#qEMd|@ zc`n+H;jW-6NwV9cl(HC)=f`%Mk_o6j%Hpw+n5&*9%u__OykuLp#@d^6lUQXaC}No? ztze{d$F}qa=Hw3rAKV<2c^8EOK_!~lcuNG(zJtjw%!_KhN%TP)HqDs<86+JOu-Z>kGT>Fk9i$(1&+Q*D= zGBKo|LbFjV_KQ*+OiObRNJ&3%${MKZYoH8K=ee4F398y`@pg7&sf`T^ka~#TyEk;q zXJ^_hV&S@)lG|=w5ZKhpltKy$fU3!^VD!ervN6|IFqx!r4W|Xmecs7qGdB&hg@DOg z5){wGzMnSk#PTllSxx5op(;eD(h6hNgoGsI4SR7=+}rBn@x`Ff1*~@Gvo<)EHbOd_ ztXOZdx<`Ev>c)I#B(3%av<(te)i!}u5!*rSrW``jn$GYARC9Nqa9DAqq$Rg9LV~)H zi7PnAP$g9Iw+=Bmt>&pV_u6~jO~v_6+sw2TsZD4)hDM-gjjs!nw-NM`xTkn+T$#Hv zbktU>ZNa)&Ul&|7 zpM??%TQ|02Hfll_p+p?cbt4x66N~Fbh?}^C9WFX5r@iF3yWGF}X%DASDOxCzL+lvS zu(6*iq%L6HcZ~U>0jNdxJPCJw!4BIVdKUbH;VMu{MKkJfzC<;V^(OmWrhA@>t6RgI zoYx#)u69eK&4@A{0#u+it+k-(G!^NL*LCgBoQtdqmC~J}?mIAdMj21KN}4%qcy0a1 zHqAe7+!r}5v`mR11ts+$&ftO)Irgvxnoq>mfrc&i;_F%de=hUl1hN= z(5t?aHL+EJAN6*QU+M9FD zak8RKKomWc#yx__z4@1dyCb*|7uSp}4=Y6M_ixrTL;mr>WAkeO;?=M|Eak{u)amIzj ztCw{0K2^ITsElAG#}TC@C<8#AqreVXW1I0^qQ8SC(&@e0*0t4r1Kq!x3@z@}lgqUp zXH6&oqT>9eZYfVHLl!cWJ6fc4NGG5snPaPAwPP9uYQAB=Gd!*?>7`Y^;oH2n{qN;I(sG5*LIeT%OXKcR=$u{mW?!QMal-Rl7?k&G2I^h2R z5+F+yRkC6?xc2t(<4dFAnO8?N_i}Fsx;hZeOAu+~prNv5y}^5VsLOove2u-hT(d{F2awVOzcQyORH$?Un6}|IugpRo4y!la z{l)n?jBXSjDz0w5?Xx#8%O)h3ZTCxh)?aXdq^nx0LTGZPG|YAm;s$W2SPtOccP^WHbVGl_nStiPTFblkl&eqXz==dKxHZXC!GHq#H&>u(&z+cE{z z?fp1~-n*C|E+-x*_7(GX+JPj2OZ!_~PKzW%1R*{i7@;Pz^X(b|! zf=^}&Sx|wm0Rcx9Uk+bZ5CI2J=2ORRMv*b4wH*cL38)DgyFHkl##g0QbGZw6&0gRX zt7qe_@pK_jHq_}rW;Dfgj-B)vmJ!uw7u(Yv3Dw}KPT%SO0FkaS6#DGK3t$SFFE-5& zwx**85GrNIbL>N?+Pl<-bAav9+Ht+*$aame-bLEDBq<WfY2S)W5!3lSYl>>HG9N%zY%CE7zyz@s_AsN+^%jq^~Ph9o^vN1 zdBwW54wNczl75^sGSyx!@vufH#(+IZ0Je|9p!+a|YNY`Kf($f$+2w;(70#*! zO#_lgi{F4D8_YpbU@Cynd{`lALfMcmCn1$H%iCNcQQGRDlqo_-T>k*wTmn#(T#BfH zopIy~3P}Q^Fa~%?%9IQR2J35}%fAUDwX{}q)1 zoTSthhj((`rLR)G;v;co$V(50wUS(U1f?LK7LnUt%vz*%7F%Z}SUm}mdTFF7O~rf6 zN>U4&z96+`K_jRY!x@DFx?yE;8m2lUxK!+d(pB=FBsQV((2o2w)m4Y5Gr$SU0 zlD3c$rira<-&_NmRHst%+A3Hn_>+fQyK zV6$m$RtdYeUCSjgx80)ZJ-S3ICmifK7Dn4{Ro*g{E;C6DC9;wdJ{n@VaMt;RJAhDd zHh03;)Rz<`ipRTmt+*i>FC_XVy76Zoz}`EDcC&N2zEUPy;-!9EaOOa3SHx@9fMX-YcRT0qOno$6ca!V@ z!PvIp@2j*WL^1ZPn}i!thQgIbf>djkMuMjUcYY&~g{$B!JnKfELN6;|{F{YW2T}^n ze3Tl{`_37^DoCQWLATh`sxZuW(11fNhQfM*+wa6dl)Tibm|$!|feO{S<8pIEXsS|` z)`KC2ua&J%<83*s4Cl!Kpi(ho_0>pr-MMA8(8q|ETp#OK z;y-R6X1SH>W+-D~ak(%u+GsUUfCpw0FjtCA^7tlKAnpl&%gTn4M&uHQGwdU?7M;0e zS5;DXmQ1o5D(UjD($XC$DN)QC<4c0wV-LQjsyw{Qc``h<-9AbVNeb;J6Smf3im#N< zxI2pT7XVnQ)KsyRI+|iZkZKij^KyHLn|C)DTFVWJ{`Ra$n^8qllW@AaB^E9r;GnmX zg?|slpRi$DdvQuC{qExc>Q{EIJ0>KQH#*sMTsepugkov0PwStJP>%2r-8@D#1eko0L`!yhE5w63Y49itabqmY)?<+|vxe(G)5 zauyFDRdEY=+1F>fO^u;Q47jA4_XQE2I?l#%Bw@jP#mm}x6P6t)QvuZ}43Y*%slryMwS2>0%}Mn1iWz5N~f0E1FaJw)TLD*{U-{! z5v3Yb%Z;%~M23E5E2$J=(O(J9sXa$rG(1zrv=zH7+vooP%p`ogftiTy1y8YA3j(>2~$q?Ws%RY^uQ$|@kF7dlKg@-6)z*(fB{Lh{F8rtKFd2^`0~J4+%Lke zgz2EdH*5`vt2WSu&c0~$t})*n4qT>?ttnfJQb5llvy8^$;w60z!vY{^Ba&%I10zhy z%;A|fhxNVol@o3-setQwDA3Ysxu_#@o}%dj9}PLlwT@%o~hk{;5zce}00#%u8=HLX!sQ;Fc`Il7#^YVJ>I-XaKjj`o*p~Zt z-1P;w(&8Oli;OlB!#8xLun1xBngtP3O%+0%G1~XyXu3)Fo5fRdxbud{n7G9ztvt2m z^l<+G1xXM(gOLQ~i)&w1BI!v7Kgs)RWCZzN0-m!}QtAQ1%tKcdvFrU7?GscZm^^71Sx`U1YYjHl?J~ z%cvms;>&`(eZxbM;m>^ak$G(oS4{$o_ZH!|uFN9C3vy1BiE@de1v-Rx*A{u@Z%qP+ z!{Q|uJx`zOY>vi@CzAyrU*ev|iQR3Rlt*@)>R9$$*NW8rttRGJS399h)b zWjP_(iI~$ZEV~uLLQO?0kr;=nyJUea02&f_q!l&uZz;H_C(JZ$q~}VU>4sCc!AUkw zhSkUm<8hgJwj#P7OmrYVw2A`dGapAxGLZ65F>iaDS7}sI2@Uzr7W~R2qz>Y&tA^br zJDApi7(yzMRAHlq#Yz?dUe22F4?NGTLPq2asv?IF!=G6!&!g2z=MjMcxvPC<3bY(7b#jR@~nDM1JjYBGish8J)w`l8Gmv-lH z#*jI#iWizEB~vP>cUKdU6~=Nj@>Mt0oW}nE1lp`L=y5B3;^GcR!rg06rxk4Um1^m4 z%l(_W+~O#x>Xncc%AOJQVxkvMTpxK`&MuVQztPXBX=qABiT7J1G%BM}Nk}AT@56}5 z5S37H56d_7@Kip`kf)z;=cuZHj55dkOH|6bjiPp>4=p+8-HQ-age?gKd#G?^*sZ`M zigm}AfLkai>aSn$VNfU+j}_bxL{XDy{HHy3-ri1y-Zujg8DC9E7U*qL=qos)-;#1s zkJE~-Zk;Tid`bHPto$0!mA2%p=A{vzyDW4bEwqd-Al@(PxFJ{$0AaWJM{~SMK14bv z{AD*flh@PiV=zy<=b%I)2_4&qWf#yT!sa% z7WDVLQ|=Ex@>Fv*O1Y))%7|-0}w}J*tiOg3qZH;R&AYL_r#bt3)RB? zK5g3ZI5|4tqU49j8L0b)EY`Nng@LW$7IToimg-X@&0JDq1yxaUM_Q#+P8=5|aS9rv zWyO_O@l=u8og)qFs&?nmIQEU$*zeIUX}HuN(ZnTMbtL3PMR5_DwDX`X=B#@=G;D8M zC3NPm>#VKEFBdhq@(M~;rOa8VHK88teF536>Bh!C%09*MAGq%8%uIOu=j~A3 zg{Tai1a_!&IJhbhL>A(cnw=_7W>`HDdV(tAkg9@BaA!J&85cs5Nmigv6lUI^8za9no$mgif3TjyE?$9?#$$StUfYDjbuUv3h7LBS~< z)1%;#W=olHl_GTa%$btbrzE;UQ>kh-CWrtJpkvHnXrd&J>NP1h0+C+&NXLOf3ec#E z5|BQed3uXN?w+n_J%*LXfkx?AORpqQT#8RxXc$+`MJyf*e?8Ngo=rcXFh@SABEjH{ zH1dfxnoeDG3@aJYBp4;zC zGg**x88L35)C3xRqr`f_=D6LzJu&TDzV+R4Jv;Me+T<;@XwvPv+dA&lJF1&qNo}P% zgta3<@P`tw7jAi;tJ~ehyN=m|Ap_X+r9iLJd>78`)!M9V_i`Lf(C9TWfM{nTxmX3mm&7;7ChySzB7bI&~nZXv|>>t#Q3i*$=7(KN@J!>C&d@{;k?2&aKygNVGV1ECuhW8Dv#1cJ@g%jtfx%sCb@zPpPSM%hGHtmrYoR zU}caM#wmAcZHl>ky>)Y!VPl}k?u%HUWPL4+Qobtd@!rVez8u-i-?wU&FLuRQKO(Y~ z1fGcj9X`BeS)85TM(d@Y0bOIXcGfR$V?(9ZP@!%8&eticlHHP0;(jEhf)brNP@-y- zkC zdSK*jyR+{qVq6nC^LQdGCuo!9f@|R?zqc6ga`yDI#n)N@x}Wm*zHRL8Jf51p+AfME zN%?2)S2dP`wGkL6#7AWS$2M15crT$5k8`S&szGASHtMG5-N}P*)=XPka@G^juATT% z^U)GJUZYZw_XYE7?Wdo=UGyW#0S&&gH4eZLsNzc>kDhl?fktIAw$^Rxl8P3y@-5eY z?>SbDuF{;gwvWGePL|tj<*2HdvftvOwTiF!l_wRrCCJ?t$D;zDEO201qZV0V>C#v6 zp&*16(uc2Sw-ig6yu+%}t>O_11gQeVmZq-hH3|9G(?(hAFv^SifKDDL^UY0@23Hyov^BD^gy!bu582P%=7 zJ^1OoMU+-n;0`ojKlesX=P`U-?Lj&#TE)RU9qImA4yGpTedymWUkqQzzD{VcwFVYLX53bCC>8}qy5?_$Ru{>jmI4S053on7Va4YjwGHb z#j?1X_sHVly9|iY8nw6POmIe;}oa>cF702&5Cyc*ksAnkePn8E*@C&c2nQqT2LEtFfR@hYy&@rD7gU5gYWkdQrxdF7s+N*|+#=gnwchTD8=L7QZ@g-fA;lMK zgKVlxUUS3*sm(1mJ?AP6FL`ugh`WsxZH6X0i>9#X2~W-SvDw=)pfJqnH#Ny=D^u_t zQ;dRXkSZN}@fU^8nVLx&sr}ip;KFCOsk;QS}I`Jx@(L&otj_*bhsTSt`? zHt}f+*VO8ut$Xp5?k(o|GH$hS&~RN@+rHXGZGn#dBUd|*p-c8_3_0;F??qRf^R(6h z$o&8qK3VA4Muv(c~)}?Z3kOBeN}tRC|T>=dTxt zEvkh{aB51AZb89}OAT@7-_0@Qpj}tHY<^D{YiQms6)Mhge#whu*_H&hOPV!Fai!F( zzPhPd_hOftyL6WhTaL@cG7C17zy)7<+tppSYuzSnt(_FyH%oe$xlc6tcuS=yKZ$w$ z7|^y?Zl5be()=p|?;n5Rf$p-i)!Yg2RX>HV^4#S8xf#}#HT#9cJ2G2`TGXW|tw%p@ zG_B)g)?-L+fCYi}mpL@MfTyy5K(B?c4{yH`5jKHU?qzd|ZA^T&4OCH6;P&CeVCrcHj0~?lSeJ-j$lD$gOLTEGVi~bAUkQ%H&Bn-7vv#vDl znC3dJYr2;bhYldFgYpLC z-F%bjb*q)_L2I+zS!Oz$X$+w(3MDzGsm_F)Vc6SPEOW#+_qF1?b9CR`<@PHie!V7& zk0Wg@hFM#RO3RcfB|@Xw*Bb27(5^@r$~RK1{{W}W#kXuL-4-p&8Z@hR<%$|EJb>a` zQUNI-^-o^7%r}jM&zX`ik1u03NZWd{s!?k>|5|xw8&dxXnm;EUgBq zC`*J6zRYU24r`%lJZQOFa$CL&*R{MVXuYxTcP*j3_WtH&;^OU++HuWayw7sSp%E`qvhqLW%R}u|kJKK`Yj1KRms@&*>^sSnT`g-#1u6ND z?Gf}W9B8{EByKG_9iy9~=1%nOfsf4jk^cZLK~SPLVz!Mh1!~omJ;s=`$jU70-1A*= z^sMIJ<)2Ucmxb?5B26(l9s1Bj|MFRcOa`?1O0!DYrPGTW+?8?+&PIP9D3 zw?58&fB;;^K9x?bI}-bS--y{tj#|5!ONjauK=RyeMJptp!KC9_yW9+lz)xC&0_Hp> z!MVN7y3C3ek`K_V?#Y(#9&XmQZNZTu2H8`OIUz^|z@jQmJBMa?pk*O9cHUq*xpa=q zXvAOnFB&mih4MT}Hp?CM9Hh>O^S)E68dOk8%_A7&Dk z;z?Pmb!TU945EQ6Zq3BI5x2*R(qD3MA-!GTRDPT{$K!;6lgU2M=FAUk8T-i8r}qW1 zi&V){Q2zkTDIv&-8bT{h-rTU%7Cxe!5uN8oTHu~jU#_w4Q5s@; z)8b7LUxyKyTR5E=s0!h=^Y;w601~<(wWPiLoa?8=Jf-Sz8jpgVae!pn)?I+Riv=N< z9bZCaZ;6pnv3B51k(yfsxW<;KB@G!--}j1pgS z@rPNcZ7ew84AOudXQy0sy@No6dY8)_rNjPYj#XBx)Uwra^b^AO0-a7-cO;0*Rz!_z zln3g@N3vL3x@B*OuiCm#x+F=l?Os23{tK74u;ct=DbjT&J;@3^{w8US%2rZRd{;YR zom+oWqWskq7o(7A=0qfqZjv!4Gf$L2@KZR4^u2Z0^KF2w!TD~~NU zRF5nl-aRtIuQ5c+cL){g3lQx0d#kBt)o*_0`KU`Vn056NT6AeDI8m{?kWq7P8z{af ziu%--W%TRLdy0dqoY`%t4sBE+(De zd1LLzjiY(FL`&VtUHqsV$E* zs}8j7o&Z(l*6JFUa4oL6QnYz)evB$^t;YtUMY8u4mUk+Jx~&9zXm zRHZSHqB5uMB>>@gFWOJKD4SjOwpR+zE1mSBGyOyIh1|LY;WoyX{{YCXC7!=t9f>Q= zG`H>6Ha=B8l+B&J_hs3qMayx_n1>E+Dfa>oyaEOaKq_V&1)P5i0%G0ci5BUEin>#) zQ6!p!-f%~>QCYB0V`TsU6i(x7QkPX$y|{~*P_0Hcg0-SjC>K-E;Rg~@q2wa&0&}m2 zy8@FAswlM6wzbEQqIxPd_g4%~jz?^@C^`jrt6uCK1yCiljY-v~r7`5YUsMT21$BGy zD9ECaYd|UwOf)SaF2WE;;?Jq+fJ#*Y<3_a^)Z@rTt4$Tm=5R-fplDej6wnTx1`#bO zaPX%OPxA{~mhTT$er_Qs9S)|}4{vo&E;~|R;!$MX*S7}DJJPI8<&7_BQPQUr2q08e zndj3Ncebkvv2mhe-PwqU?1q$Aa9@~aH>9LAHF!WhwHen8u6QfH7DLzQQQVNCTuPHb z0RuYJ_u+w8SdRprBCY=bm;|AK#%WrO0Ry_7P6$)hxCCAzW3-vBOl!_V%_$K_LYGMZ zE9k>sDE?=HO)aB9bw|sn@zb=7CnXbz8iuRdZ0O_u%S0m2Eh&^Dk>K%1)yd;tzNV&$kn{ z-wNjL;78MRO6i8wu^W#TZ2Ov5RYrB@JfcNF3LR;RZc<82;H{1}3*jiUXY@6Sem9GI?UP6cnym zV=%+u1Q(&fbf)dzKRJmlI_Rq=-j?h;QHaAu(iC$7quq)8-LY%xQG0Qb^KE(u5QANU z^L1N}77J}Ir_EhJ9_&{ia@K{Yvfb$HEu(3GpybtjZ-3zF2nlu4!^HqD?^l>~Frby1*{g}63$_S~*Ch?e4pTNQO{ zT2hdHyi8f%!Een|bI{)2nlxyJgTR*;)BgZV?iOGGl6YP9weNzWFXUVeFv8yQqw{@cb|G`d;9^_3Sj zYvPMT>KD#HdSht*(mV*sacTf`3UY(&#hsS98AV#!EMGCYw(Ynf(^j4e`1)hwA40s1 z_%nCS{{S*}^}IY8kF+mSTeYdzRB~=Jamqa1L!s19vl*`Vx!ktRC9M7!<`$XSX?8AJ zMu$#3)5*q)z54jF?2Uu!7JkRrJ6Z+{fm$eat`f$AE&+Ofx?j_c zdt%n1QvHP*-EX)`yIpQbjckUpxlO3iYo=i$ow(JqQo2@#FmYV1v~8lWd*+?g*Mco{ zMfw+xb@UvT&CxwatqE@t|M+KyWHEydI}Oj-4j8EOK7F4aWk-HwCW89^ncoPywP z?CtkKmLvCER4vOqTXN$zc4N}6joxkh=FcAQYPhn}+HcEz(M=S&MFk-D_u^**oNeb= zr3(!eOLXmXi;;t{vR2tMFRrAwyPG?HDo|rTI4iyL@PG#PbVe zmY*o1w5zjG>N~K|yWdWEBp0nobor5wmZf|(HKr7Cs`G9sp+UEKn{GnlPi#(zYLzzP zd`b4C_FzjBw$a=XD4`xpie)4r#a4v$puz>UPAY1m9%s3x4#&NzE7D2|Yn?nohxFl& zT!zB5uGw1onKrITOg?mnmOE5*<2<-uKYY2`AsFOM%PV+pTb2G=e3L!EwxFP&C4_{8gdiS(0fzDvt}+gW(J+>pir1zJ zLYY9|tI5l@rdnIBqofznnX%UtKFIR4$OHj zl%bcKP<@rDw_5Qw@=lcxXd^L&q47w{Rx4lEhft>SKB+;XNHsp}C{hJJHk)$RCZU&u z5sr_WRY0$5LGRBJHk=hha!3OeyY%hM+uzeqBJFN&;^}ZxzP6Xls@TN%=3EyTl**#M z+sI0gsx1p5sZdbr4i?Pq*e|WjZ{SrX!z(qd(0dIEll^jf=j0z;+n-`?UHP-^yE)B= zZj%l*uGu0)h8~eI)Hw6{q@@Z-A*6(-I%k=~4YRf)#l%?|CaTMOdi$$`Gqoj9n~QQ1 zSSoxe0RToql~9zN$Q{+hq}*zp+b~wC3PQkfDf09vQ(tXsgi3!HKt9qOO5`E*odh8< zkH=2K?igs{ROy8iSOFjwsI>e>xtt0$5k!?CM!5sm14<*6vh^q^d}*NvrW2Kc?JX&5 zc{&|bkFfa)<-oj`7kJy_nY&W^9qV@g0Nd(HT#s|KOZ&IlTWZLy0PAe^3Levpe)cEh zFGPdkD*ceMW@j2=)68zIJ_r1G6)tkN$Az^n@ormb{{Y1{PU()_!)M-6nj}GZ1teGa zo34cpwHVXwr)%0A-9cGa6Cn446uL;w2vKWl738c9uaO;2IL*y<#a)>Y+0 za!DN*Kt0%$iPY6Fxz*HPRlk=#oy$k1J|J~$mQD67nU0RaC0rhLCZ5#O6fFJO^E3;U z_pN|_UO;uBqNg932HRmCpyJ7s6~|&Zvtv0S#=azm=E7UB0H^)9$h6AS233R+MBDQJrx`&qC%ti?DkocMa8{ zk*(=f#gsVukkcuu)csijY|5U{qoEq5hx<*>iOTOIMVu3T-v60PzQ5g&O@k0j|( zQWjWj5TumkMp#n%KQ1s9aj^3vBav~XI!dJ06scZuAH{~a!$V&cwdQ^l>Ke3+vmFj6 z)lF^^N_<17TnY*y3UUXa{wxAD7SuWvPfpA#&}f>DE4n-o(XjqX{NXH`e>Cs4w1NCV zF2s%7PvONG>@Ui+cz=Sg?%R;t6ZHMGRfmQeaT`f%)Awqr)!UM(j)up404^WhXa3>E zSf5OA{%d`2d1inz%PB&8JWD^b4qp0IX4|+{7=ZMvg}CFTG07z95b=a)}fve-ZYPsl~)f(Ui7Ud)anTowMv z(~@#B>eeXigg8e;flNiaim^g8N7~X7QmDRQ>=mVO4k>p+hbUyw8&wZ|df>H6`CbSm zM@mSow@rHHGr-k1Fu56y09s0*D5VCWBSlZugyg45Pi>n=jTMaB+l7l>*-VJ`Nq{n> zm%>nz6c2exILtCujeX53x;q1td^RE7C0gz_-n%*7r*dg~az5anatisT<+-8g=uFo6O}NKk$D$z)}zKfpd^F!;96=>(?kIw zBo8fr<7N7=bY6sAMFTR2N$mdsaKIymBC7JhG-1zu6mSI`G_6LBNN92g{=mZLI-Ee!_sg&vfy z3=4cyNo|cPLU8ISVVLTU-f(2<1X5x8oaTe<8tZB@45t{dFmq8x6*?((-= z;B6*!TSeYmnwt-2rIvtEDa$^C8IJMXabbsBA}%iQ3xDmN)|YcQvT1_@Ik^M6{WVM4 z+=AZFzuzIT0jUi$bllL&=L3i5N;LCiw zo(n$k;E`{y%6?(<#Tyi;=0L+dt6r_sne(xB(}4a2&H>)Y<6;cyI!e>OS~&p z5t7?+W3oLl9-uj;i(eM<7b)+{@~xY0Y=|aHf?dg|>he&e07`XqtvfKK_VBW?fCo5r zC-`pCW;2(?(MEbJ#aQ^>l403b$5S^Z>T<~ga@-Kb?U&dHavX8uTE%lD07WU*n9T0` znLN=*ViheKE{DhdUTv&yVHZ!Janvq?&$n%UC)t+<_}bRHv}XcawBED@smD@{I`vcS z#w~4iHbQ>8mr(dF)w;#oVKM$*J(Z~9tFpM~ckT-ny%%ZkD&u{aTw4@dg|_vANi^zf zP%&#}eh3^xMuBHd*jJaKrdJ-P9aOWteKBmLY@vODbdzho#7IL)c}a1Hms(~iRFxi- z#4a1@f9Y7}!?fzEWJtyvzWadd&*4R* zS7!EKvJJ$%TwDst_A8cvnGY#ty0isR`tgjs5vuEL9GY9lc0k*9$x`P?2rYsbjZvv; zB8dYMxZGo|9y}BVXEhzHN5ymJk`u|CnRT*5gJ+OfdEjC&6QP{*)Kq&Z+HtUB=d-bo zQYyKxazm8MTn2!8YOe1J@tU~m!j#TR9==>s7jTdVg#-+c4`>+5H$HFXr8MBW6Jl*q zjLe4T_PTRLd8uBE~X{ zAIv&wUtbk!c*e}lpKQf_F@q0Wk>)yzP|)PH9E1-_e3D2^ogA+T)C*(j{ zJ|m)~cGm`OZhD>Yw^d>;`$+nhER`Mnrf8Ucu01^Q_Z4yM4c)e#eKXZ@j}393d(00Z@T|}>p-4?nTv~f? zyY8>A3~v<_S(oMuwwT;CgJg$ClS(R;viT-P#qO@2J+ubh4z*-$jsn^q1+OlqI<^KSeHGB5G;hvB=RQikZ@tB4 zml`XpEp6*+g;56D+Ru{GLMkdpi9{U9#?P~`cKEv%#@~qX^{#8~CgHx%Wgl?=0D`mw zz$oe+ugRAciP7AS%G^MGY8noLp{j*qm8V0A%%$U-Zs#+N6c!T@A9%({J;Fs)qsjYA zVB2?SXJtrIHtOQXP;fk{ht5(ev*sxc=6aOUfaDG;R$Cd_l@-&~*i*qjAc&zSxO9!QSY*_TPf+eV&ipyf1G2t_+nwvPqXpGyT zK@!lkt>m{+tk<;x1KGpJEC z&lUV!?;}x4E1u-AgaOcNRnTum8{cf*c9!hnt>5L#hjLk&0tAwf(}`L_T|!Cdx`e1x z-`5z2b#YTROO1tdX!9fnt>Z*W(YVx>WvX0f?b6~UMobwExYCl849|>o z7bQEj{1ac%9kmmEvCCsN+Eq!gltsx+-mSgUR`C9SKRw9enf4of2& z2OmXre2wdpR<3g_^EF;&|bhsBC?glhd&(|&Gv zuQw}UYpBSmD75uA>I(P9yI#q+Hb>0cZx>2~b98AfvVaP-f;yV@I9Fh7*zad_vhW9j zYu_I7&5E^UrM88`isr-{)xzs^e&;RUGF-(i*-{j(OL4^=N>Yi;l1@4nTSmmd*60@w z+vH@LVxztxS^Ii#b#-rh^5D(5F|=_cTpKwEmQrUNS0c zS^iM~>e7KvjMA8~*)>u#52z{{-ClaF z7g%h~X~vY(g-8QGg!HB^IC}ZbSpM&AY@R#IRiu#K@j4-A8osRG*O=lE&bj9!#MnNK2a>l)er<4d>X}n)c#{z1bRtlng zaHS+9ts!etfQ%X6C(ztmnU1ExwFHAC$7ehTYwXrvT+f+OCu-`8+ zWEDt*=Fw%MiXa4%1uIjQFuncrR?`C+qgQoyCHTyxiDbY!K#VbGzim!Un(o}NCCP=y zlV{uRIFzYPtu+7~^PsK^EyQzKk28mgO3vPE47-dVR91miFHroCeBCnb+hcLyrR*^9L?#Gj7kwGQqd5VGxs{Xgt?Azww_Wo!G#KlFGx2{h#!h*<8grNgH zUr)Ci6qn2#S166SIW-&;-(=exvEN`_F1GaCVn$eUMX|&8ZzLM%T{@)p=ZgMzLo@}f zG$mcOZLqezYvdh3az;676Gw2lbcJAJZ%4TyFEeb6l7>)~j;RK@96Z`nQUJnwi&STJ zu;gBO&1Xwop?6wwAUEWU>p%xe;@-#6Vp3YVs_$*9QQzDpV0rytKeSwVfI5{lGXlQA zaf@Ex3oNs#!OOdFm~X~5q)&dXk56J`^5-2jd*CZ>=Y-*Y;jrhtiH#iHOi4-!HTES$ z97*i0#A3`C-SEg2BfVP;{h z`0r}$e^Aza-x%e*6!NCUNg7(0pd(so*s9cy{9rg7)b|*#Dcl>ucK&;A*kd&55q`Cf&y2Z-+AA%&NI2I)a%(M^JOZu-ZAys7$83V!;>~SW z8_7>Nl9Ao)`YDBRz{bY{qjN1X&m9woeq9q{u(xn}NTfX^p+n-!PFQP+uyoS&fyAk8 z(VULT+&#xo6+GqcQoQe|k*skofvZaa4k`2{YFS!46-&7_4G>FNeM8y3i=S z>4r07U9Bhz;#TpFn?CWrTg&ZBEJk(t zRk?orm;E2^8V3cv{i~(U-713oA@-{rpSBuf+*7ljwfYy(Lh&XN32UJZ`sw>u%uq=y zt$OxjWb+Dx0)b^&q~RW|DuRf$J(zJur8-K{!&(8^gj1lxgTSR$pk_@v;H%0(;ycd9 z+nk%TZ@iyib9C|bu40|tn|YcSWyLYwKte%VV36XHsud2Ha}1g)j`gs)xUzFC%^r9a za;5sq^ux4eIR5}2dO@_l=uuQ@H&+=VgQlctk?$ZJJ{K(HHCP3&Y4bAxZY~edqg~ti zj&iit{#$13ZLi0WbMn6pw6OapVftUEWQ}IWJ=&ZKAlC>tXJKgnM z-0n)9<*P3#pDBPdsHc>GMLqaUnJ4g8YfjbUAwLa$Y9qVzMaH7_l{asQw~v4b1j+Ms>eK=K z+GSiTQ{bHZtYd=MJdj38P!-Do)#yX=?M9hUQmdef_34BIiWf1UCzjh#RJ4sX9f#<| zg`AqGEWoQ2mJoGN3IG7pTu(%(6C?4FsTxj1^*>%03LbKWl-k^e>P<~htp0<7Ox-Gx z$J}(v6fq!ZbdI$@rx5aSTLeD^Q8JPVG^Pl=Wjv5Y1fJ^lW5p`hRcJ*C81gbEqzynE z!0I|z07i&e#d&ROQBqUor}%&F;SQ9!D5AkgIZcxFYJ3RC-x$&oOSkW3rHR z&{a$A0AeV+&Bm#nx$rTAv6tBE>!cqitDMi~toIpzql^|y5=|W9&!CLAiOw|Z>+llmUPU9@+%55*E{Hs2V^yVP+;V zopn|}g=G^0dm}n1DYX3q6ILfhg!eKUNDAri1+#IwZ0RjDw_aitfK?kUE$SfZ74JBw z?t)pUO!gP?ozhC`mdWFNzBZ+~*PRKuy4z}USz1(_`igWI=e(v$R4JX?kF&PMnh{l9c;V#!Dvvsg zONgmt(1L4=PU(yos;V8E%TnmKkyAC*w^14oY^doiv_7=PeQ%`%A1MVPBn-1Y+*~pH z65?%LqR8{T?8n*?7xxlMqn=vvsp2s3!ZZt$w}$V#yKyghDl!W|CrHTl z;?oo@uLY3Y-afogjkxPivm!E-)QHLpk0A;vS{9tbl2bwmJqW_{+Oh2|aHu?#b+%4u zBoB?nt{e!^g(Pk@ZdYElZawL6H+!DujECBitTaNeQsUxKc&lmljY6Kqt02R)i>&vy*{SfHI5L@mO83)Kd zT{P?V_IBdWJ+n&6vG?z2Rv(KWYGNL_%AT08?P}LLGSa0N6WjpnjQ;>XXyy^px&yZt z(Y`WVeuZAWPeX@2=A=eIOeakuiV;SL?^b!^M(y;)vCym&zt$ns2e(B^cLknp*JNBB zlX+a2yDe-c!)$R&bxlWVd#o~bWl5r=O;b)7S(f!=;2iWgsm=g z%DMemo>28$xCK+43FvT~l|iaoxleWWAw!U`<8e_&vZ8?NO7~&EmJz|xBr=UFq;OWH zQ|`beM1x$04vHQ^D}7sXJjw*OqyV4>V?KxL!o77)EnHer1cKaFk`H2PY1b?eMx|qx zx~k9WQsjtx4r@tW9Fn9ZC#UZk;tx9NMzM!R6--HP$^jzn$s#_7ZqTEqLfH!TzGD}`|SMk-5Mje zQV@em0zKG`kf~NRCA3OZw;dtHgwZ)tN2W5~!gkb-~{MCJ&`mx4*YT3fu^o`ST6DZy4$MQEY>abDbb z;d2j9H9dmKx(TbvNnV`yN#3?=r>3pbAqaQQdUq8|e9>`dgegx$3s?HLMN|>rTvK+A zNzlC8$bCttaWKX?8<2p1&rc3TGw!yjHanE7bk>x_h^{TS+EAjBw$xHVuV^D0HFZ&T z_idUm;lQGOa45-nrowH>D;@%(s&~+0ULo!%IpjS?i|gakow@4=rfvndyvLI38(Pk_ zv)o;Zm%hIiGwhT>($#Z5a__GrC4TWtUs9Y+H+o*TPb){dNBJ8j#<*H;R#%x)mb8_* zEIO5i3J^s)V*dcQ^Gt>@82FBgjhup4wWEVt@anIcm#p)IqJ+nA1gj-zS5K=JCdjvV z@-cK&k?lfUZFAl7f+MXBB?LGFN5p&1SbXQNSzp39oonKoZ9u$)DTdj2MmmCuTUhJv z7(iB@g)=4PwQ5MOtq?Yu@!s-;CsqgszEZn>j5MVRb}|sTwdx+#WqmVxSl`~MIW{*I zZ7YSpX-;|syNmmR_^0FB*d{V?Y`+4aygJrbVk2Lcwts1M5%p6DJ@}D;!Ur7on zo9`NKJf_GhJV*+n{{Sd%3guy#Q`tzbpSMmWHSkkk-BuseZ~a5|p%Jt90+mB!n4bRt z?yrAW3UnU?sol8m>NnayYBY8R>3pZk+w)WhL-LP*R}Fey;8STg-Z{VOxBj4xvBdlO zTE6RSPhWBQWr6n72N1aOKE&Ey=wf0T%Fk#^ari5f+|s0M`IWbHpxl?n@L4x+kJ9a&X8S^n-b{PW%B(609EWD&a)=0~6^ z1BQIB8b+m1(0g$m6hUe^FXTJat{12qiW=a4GO~w~`>n-g#~Ka=skJSVGZi%L!)&V$ zLdiQnZ06);aT*e+e%fnWa#Bqds8sufaL&XM7eHn_Q?*Dbpz1pgCzVXC$R|iq>s%X> zF+&cNrhSJ5=75E`KrMukO7&NEcy@Ru{Zy*%wW+Vb{{ST|r5u6(0KGfrV~4X*7qzG# zZ-u;4>>XUE>EN^N^eJOX^Zx+qs;pd4*l%Stq42sDQ{7WsZ?-~=Z5JGFSAX0(s?K^z zgkQh!=_G1w77{b|sNmDT-71ZrkInMOpnIX~7L31l1&w(EISxUMk6E!9?_em8nRie=zo>kxWHPCaUpa7L-m@CE`F463`zI zt|8@evqc>eqCRy=02C*-op6(%ROiYi4u+;IjZU{pZ!DxDvGcpzG;P&Q{oaQ=cQ>d530h7`7|bwlsg|eU$4J;@2k**FOR_nFN4mp$a60q5IANNnUtP&6mB z9L6@dCJz-H7cEU__}*IZK?x1?pk@>FRN-RcQVt0*MJlI?l)$FQB@W}4egzd5S&HjKW=wQ1rk=IT+e+%vERzMvEsLvCJU)fe>#LreHudw3RmIX( zc(o#2MDDa*S0!w_7%d@gJ@a%qCZy>Cz1YF6;Lb~JC!Q$oR9&Y&LuWyhw>EF;kN2xz z)|wuq=WPq;A}Zy_?nZLX+8L|y8x2QWfS-i`4uXK5+)M1ZE}^od^$zr!s&{qdu(`~2 z*$?6A!E_VTj^0Vx7JJ2#;)d;#%Z^_o6ZfW3Q9^+O-IjUd6WTV zgnPqOFDY>T*}BcQ-uG?Ze3%=3YmzN(s) zjG@O>q4^{tBtSJS#hicuVaqJvAgSP+-DMdGrD|0h=N?MAwYno)Bie(~p`%mf#1{^= zbRc$6V_Mp~awkF5J?|i7+RLQaLo>{!kg&rfXEx77OmV|`1NT{cFLlU>R zYZ#bG)m3oYzc+Jo&;9a zP!7(uJ-C^->kyX}UB2?r_~7zB1C1656_Ck%)5}q$=DxE#veB5Kg-}f4$9) z!)419B52Fz7CB;}l5w}iBJ|n8~vtDG>^zFi)QV8S@ z6-M=+;=>~uPg>wo7UI4|+x9!H=2En=ZDGfpQSL%X#FiG%&$(+nReN;heoo2q_yx!5 z*SS~C!F9bzaK7!)_h0i4c`a1~=ow?HY^_8#;iEtT`R|g*zUJxA;#^hQ<4w1E+MG3d zxY*z+&GE>3qS;YHskr26)@hg>RbO^7Oy)(m7u9k17`i`c;vH>>>0^J1Dzm&*vn_l% z$0$X{TWr3TOufl;aX}~xN-CmMe{};Pie5^2au;1J6|RA5m}^^X4lv6pr3FgkXR5DJ z?{lXA0BMU8>V?OfKU*7Vf=Dx6SB2SYLFiRVAa*C;j;`(2$QZj$r|T=Y`aKKg$9em$ zCgaK;ll&GM0s7T)fg0nu?>2qAX>C*PSEt&GZ;XzvJo3Y7A6e^*&Q|6-8)r)XL1tJy zeAzsNcF_u#5Uk&;%dMM?_FlvE*R-U9<$<=YT#qpd6_s^yJrqV`Nv#KUF_`w-6)ok< z@1_;%;p$uGeK!mk%Q&$%rkwzFQ^irMY`df)lGWw*QEx#{tU68~CNo7l!HM1%*QWOu~pk}03sP4s2a$AvZ;U5*RY-@&_);fQL z6(n;;?6htj$fn>kW)Yw=mW$R{raVVYb!q^LlbtbM+BPyl552^zPjd2h_g5do0*V_{ zNRp$iY`*GNifB)lL7%ks#l|Vkby-c#gdL_0C1vCUc+LV2?>Tv~a zas!PO<1u6$&`@@@uGuy$*q4?exftry%CpP6P7%LjCT$!ln_}jDw1Mn(2v_%reL`H2 zV_GhLK{s|BoR)anpf$@9lSPNZT=$#7hiVMx|6&JWj#u9tl@j%Kre|9noWDh!f>}NaN;*Jw~KDrmkM2iEAsyU z6L7Xr${K8y8YG%jWC_JnbnVOcF|oPT>MKpy9nQl401{|p=`pv8sM|x4BW}(^-P^_C zCB)t4N}C_bNG_!+c(~a#=t4-wmwkaY`1V3L(u#J5JIbrbYeC3=P5ukOpu zrP&iCOktRyHuQjy;88RqDs};f+dm8~q9BTDt6kOIPQJPhtw0Jj*_MsTxExb<4ZU!^ zCO%Ip95>{I0xC5Yl0f=#CzQ$B+mGTn1M5|NvxUiIb%HlI4q#PTP9xdd>dC#VcO$dz zjR?{Zr;_X$Erg?3sU^e?o%qgoHhR)aS2eF5)xY+qXXY)k@yPgG06f86gtGwIu1FH5B*ZwUwYQr;2l! zzIT_PTH9{c$<~;mPb4XXfg-snBzB&dEbuvwOITg8(A4Od_sNh%hYK%*ZIP^8d(IJQ_w zM1qSs&RRt4N9(Bysl(b3Z&Bq(yU37;ag<5&mf}>SQULH{PG$S!p=eyJ+!H4b4|O=I zO~{sC!#7;`kfS~#C2IL%=uufmc_$j?3R3t%D~@-jIKz>18Z=DC+IlL|w)k5Coup~S{6?kk7` z*lG_onC={W*0*ksK|&7%>)4xuj;crDKQz5t1_#pe!j2|nQFz%kA%+UImapJaXZo6dG zIB;!8two-gQvK4^$y-9@Y+FL(XmN4f%^;MaNuVeEY+JYe)M99)VHE{ry@Rv37~89P zSVKrOQO4in&GUBKD%=)JOII6gR~H^aQsPUkxXI5_dehs7nH|S%8^Cd)qE83gT!qvI zNX1$Tw0mFE)wXS_&2*UhpMGR^)o_e-kC$|fMF+eLW?3EkFPK{13!`?=V(mPp%Kcn; zrOs2~cJ~(Ci4xk|Ze49H1V?y)50a7noqmevk8M5SBsU)M(3Io4-JiF<$A1V8riIUk zIR5qclh7HqIVw{NDUgI9odjfcQ?nc6wdBY;PZMV{1vFqPjeDwA|a9HMo*g zw%VBHx1*2}0`!2P_G22{89zGfsjId&4%7JzNQL|glI{CXY~D7e^xJDeVmBjbktk4tU%5-tD@!#`%z)Xt;Z^-Jh_@ZU$cffS{uQ@7C9gYVJb~m$^U)S>wKZ-8TKz zX1Z^gvUiLa@1atV``X?@K%gzDS%cr-gYHd{1MSNo0jJQ5+rIE#+T;Ycjxx8>uFlQa zEb|0@Lf>0*>GBrcerzjS&6O=`QcC=?q@2DQWsH|En|3+z+sGCHM z2$tL1&>Y$KncIVNs>dqG;by~OLV~>70JSQe#F}BIR_X4!0gR|4Qlzk0=$mO-B%x{0 zD`B_pD3ffoHquL~ayvR!4QMos&(Vz6A#c$$s=AME-T9Wx0P;;+vv&0ErHS%ONs!$c z>9LX#aJtweR8u2^ernvTp6031N$zXqyXC9@~Y zJU&-Zhmv(l+SW~*v9GYL@eq|a0#Onc+7zcw1lJTL`tyHba7x-Iu zmp1xc8Y8TsweY`pH-o3~P;KXD9SmlZ_&g0agO>3TPO&HLS7t#{m3<=aBsjzOPMYWab6 z0WRsTVu#s{h>cVqQ9&Gn>U#7T1gl9a8q}(4!1Wp@Xa_4?&3mv=l8S7JUARcSH+tAXy`#^n~V1^5) z4~m26OSoTBgY5mOfw{SJ^5ybY-pcfUx?YenY3q*lxW`Ao?j*i`%o^M~_ljz4atBcH z>Y}fgjorpKiLA4+IGFMHb;|t8@3ZczQYrA1%K%jOj854QT||}bmD3qlx~Lc4I)P77 z)Z%;vS(Vx)sHH*Xil|diQkqmBVZyPtsvhvwr6|vk^qF#;c?fAsQ65qgp(x6L05EA9 zNYzGK5C~9x%^?!rLPkMK{W!0w?iZs?d=_J4nDI#&t!=>s-fn zJe;5juhG*8xx}FeCF&KTzKkT}M5JgP1zLi&Liy723H+Qp&ZRXi4xSWJm4BqDQ(4Q%c~y0USQ4qRT~UYB<(LK%`eQmQ?G4 z76j+4FjVLf$E=rl(1pfJ3TiUc63AH6RH2<|-H$p4jZ;Xn@JjxM8kC~bq%a3pKow7B z4-2T2@@Q1k!}kEx*EKo&)LUaLm6jyzj*BPmMW<6Lx{<*%COv4}7Wl3qkkSz$#I1Ts z2?wXTm}{1{_kklth2n3Y4_IlT3Qb1+-7zY{FR!73e(i*~boX?`EZct7=T?O2z0%6W zI$JA%3SZdQOTO}R({t)kaJZ+5=E6vK#X;kE5&1z+|+&3Km0K;vJfTbZB zsejP}yW5D<43a;kf~eEwVTwO8cTgsF=;V7i)e~ztTedK0Wl$kzou}E0UN>e;{aiIG zFWkN3vxi8IfkL-0P%Z6}`xcnVpFKlQ&A3W~O78%6;^kr69;a4L3mVP-W2B50Ks8W4 z;pC0SxqR2ImtQ3=1L3ZaGwdC>&~f`-2n8C|iRSy4dwffzdG@9pP1S0SUV3a7FY^sk z4IW`bAdZ^pnap-#bYk49XK*x~bxCriIdD*ZU-vYqk{~h(Z6uEK*A8R0rv(vleja$F zqil+onvwD>q5^-mc0%-p4{#viW;#MB(W-}bMykiNvXM0oO%Pdz^S`mF2%8d+3zo znvxO&Xaa#LNT34>2%Ak4cifZlaa}>%9FYC7$bt07U=y_k%PBUA5a6z)TBHLV>Ql8_?FyKSim`N;1;)O>N=LfcVK zxYH~;xQ-i^`=vI=Pj!ouuLK2S_*05D@2RcTbg=IB-8+A6z!K%O%Z@O$E$2Lz&>U`r zbSLP>ce{5)_%<}f#I4V_{lB)}vbVijcQ(DvkQMl(=@wd83(7yDM4%TXbC2{j+jihN}&!wC~EMIsT5J~I4Uq+3DTUhNFz_T0Mu7Ftz1ut zc&Sg1>A;{5TQc2EN_-=(eJhW7eMnsTDARn|C)j7&ccqR>?ep!gwDU7ss8cc-Sar8D z?Wo3My0FfL-fMWR2X5pexrNX_P(?LYYj`(p=I$=BdVt$ekle{DMX!^s?ErpC{K^tF ztS8}2f#OL$a_z;Z1C$pr*O*x>)-qx)-2?sut51kksK;<9rDHT2C{mc2d$m&EFbL5& zr8oZoM8|C@30jkp>5-_z&3Y6da556Uxji^@_ox0<+Iu@?anB{&@4J%6Vt|)6z*3o< z({Q1*%b>FQP^zO(ik14YSjbxv`i}+7d7K^ntnPfQA+B*XpH-R&rKSs`dBL^gwD*FbF`AE;pRfacJo zhEiJUHE-?>x~rE$AmG{_L=sn!J;yT|S&qU&09_h~|f_6ZB7J33( z{{T}@Z4MPBFBFnH#V4(p$*9(plixvtmnV#5=aEGf9fkZd8X$E#&?)cF2$!`;MQ3zH zLmliXHbZvi30v0~tcn_Hkjj5bu$$$LqNPt*ZS0iJ0g*V@(79#mro@@NlQVHQxouC| zi!8Stwx*@m>V)YjEuy*C+l+HQ82QNseM{`qt}Ss9!z&9&3a@s8 z7%n}&>XpjLtrLg?okv_q7g1K6vJ*-GJ;MTsV<8!Cp*j5n24E!_3R|iQYni5g>^XC& z6E`-iynK|)b}z}_(%b+%wcoGPC29UIGDN8W`{*%G+sy0AxSmTo?oGgLyBIms_NukU zi1M}^Bmt!=Y?U8-J&3N=}v)UUNF- zXQ^6!^B$29qG{q8Qw(uaYN4~%l+{IyAT47)otUwQRaK*cu1nF_tucmr(q6Pq+7U)$-Z7aPj?iA&&^e>tYNV4b@3QKfc7bU4fHfG^gMka2 zWCR){XJL?NRx<6SaC)ZYh!w_Cvq=i?O)#IBf$gFwVdc@VKFMvUV8O_eD0 z6z{`lJPs(quBAY#?2gu(Z@7L^yPDp^PB{I7{Vl7Lt_qJmSH|RpB>w;iO42>}<+i`4 zxIy*Mf%Y!%+$YTJ!+qUaO@Hbtym}nts~0Qs{l{W%adcg#<6*fiPFfThl!b!QLd%CF zqyk2LO);eRr*ch?v22i$)zmnw7xJIm`+dQbmK&CzEHIrkQTHKiExWRL3wP~ZwQ-p~ z4&Jyw5tBWz+@M-jKJX+2lU}3@@oCywS#L4ynp$-@S56Bj?jG5<#%86rgp$TMfnF(3 zZtXke*(iQAcT6^cE=bZ#U;{(bBTO>C%flT-byNFWWnWyzAUM{fg?ToA9 z-CXcr((EfFg2DS1`st-_0suw-dn+Y zZvOzkN{~a9h7$Vh0JXarBn`TY>r4rB%MthKvRC?MoKQ}b1SFkCq-IVjIJ`6OCaaxNwd~&Qiw|o~iVgu#jn#IS zyRUZ}`-=*ZHDg3!QV^bi3gc|X+DC5{0_1JOlb0oOU#Y26Hw^IP%htQ@;7D%L%F-jl zR;_RLli5V#qq=ucVCavE);nk0DJT9d!1s9tc4LS(2rWc}%Wa{M5eg~*6d1`arF^cd zwOv<@f-6=AFi;|wYt#1oEUB+IN0>sK(yX%U+k|W_gtCFjILzgZv<2MMb+T;>-E2F^ zJDU0;yzyFKMNt8$?WE99FSeMI%U^jf5wIu?xnuJTGs^BNv+{fDJM!Z7iAM1H8%R`? zx2y$d>>yBMPup*AkOP(vzMA8D=SC`tabDKnn}c&gks)R^xu-nIRUs?TlU+0(><;R` zwT21~B}aDm4UoIM4?hSh_shK8ZD|wU0GJJ^#$_M?O)2fJF?j78Bd8ZzTx1Lpd)$JE zt=A)TygRw?v%_v8t4w<4bo8z)^4U?LjT$We!zx`VmFI^9k!8HvzniWXiPJ-6%6ytu zO$~dys4(K@&QB|)p-1B~khb*5^Tlnw`8L@a6zfD*RP@;i@Ja{5l&c^`J8-5WZ#;YM zC^vTbXlaRpwMO{*^Jm+(30Ez^eoET#E;}ViTEzxcGz8*5FKp|Hx>%oWY@;l!{K-cZ zB-r^+U|nOO=r`7t8A>6il~g5EG&HYvAm-aQVDMIZ9^&I3DqTV8PbaQ+%ftyH-EeMJ zN=Yp^l@V0rayeos*-aC@@kML5%v#;Xe5VI`l3j)MN3*4=PL)b%s1>WJ10jU2TT@V- z*~!w{6lF8?toeX}N`u&NgQ6!;o(Z~Ls$+6>#>sQrGWON+@0EjnXrcLGT6DLOQLneA zEL&pUK`?s;p;0&W{k)du84UxKH?#3(%9pwA)>}2<>EcVN(WZ(TLG4fjENXYVg6cbS z)VbdYv}|O&3}LRTqt**M5SoXf0k*BEOHl;}g(yy%kA5)=i>V}W1BolCtZZY6pu2-Y z<}a^5Q!Ae({u9}E0wA;h0Oj4qmDkKzp%B88&lkx%VCGd+w{&?7b~> zZnxPENSgZVnQ=tWSFl!t*^O%xi;mI;#?eX&E^^Lp334~DxRlk-H9*5@;_?#YElDH+ z)OEwFjT=-p2dVT^OdHHAbd9oyEQpg1(&`+z!8()kNz@9ErFEjZVv}=j={l%Zw~oJx z4vwbQh%3F`1wxYB$lh_Kn;l-Vs<6sUoO>%igO*wl70ZMPo zdPE?~SzLvyq=j??*Z>_cA~M(O!5Ue^qo5kCVYe-|(iLr6>pv6O=k#G$5TeYEYTA!3 zN1CfCd3DHYPyl^+DPzTtlduD=DjIK%HqNqrh zSwtlX+lWF=N+Cxqy|`9eP^00=mnp-DAAu8e+irI`>9#E^0jS}%IHy#6AwC|S{8D#J z4s->)mdD#0xY>Bp=W3?ER{Nz^t|pIaOUXlF8AG@mP3>}fh?7NGz#rg#T@SN+Vt_AVCR8`AMagt`O z2r5`@H5nYp#piTF$2p+ru`b`;N+Nk^swum!X}>iQ<=d4UN7R+PqDfs%dqAcYv53Up zDIDe3o+?EZ%=E45I#&qn>Dyig(vNmk!dsG^xj5>O?ObgwgdHh02?@&?4*u8!D|96< zEz^f3(_N15L$>9I%QhhLPZF)u9<#S2iD^sNkd_@eI$d~#{{W+m)^66s4FrXBy`SbL z>YYXaDyMEC<@?7bKXUw2ZZT?&2_VLh(v+g%QK1AZV?W)SX_f$VxRSazw43hZI2>hQ z04cRhvZS9ew73uPBpgv2JI4jJNoyJOq}ME#$5OPa+`^FX`_H=qr|6~@7gBhp(qo|_ zluh|t4{XRuLhXocD(VtcKS9IsmYKW?iJNQM$;GJ-R~GE&QKRGwKm%2Us=ucWo1VC0 zjgw_rJCgH!TeA?>foKjcluC+}PfAxagDbVFybe_++bhcs76eWDt?#--r*_X!|s0BjrDoWOsDbMjq z6&1yu?fw}HHHrN*wceF&F4tSN`+9NA`(#o0taMu2vX(q97tn+@*#!RZAY(h4-Hfr2 zIO(eCDX(X_%v-t|Rik6t#~Sh${o_Q&arSNw3$ERIf{sUc5=Ui9 zPDkB~c3uAfIb_Ml7U)(@vIpUrNo-y;d8ktjuKxfMFt}Q$vbG;mlH)F+GylaoLq)V&Yu+!-S&!h%C2d* z+m`*p=)F!%N-9DLuV=p(TU?Qmqp0v%Mn`Ef)#uA@q@A$TTzzGD>_+qkAbNWB`cs$l z6xx*D8w+_Qk{Y}~TMs;el9s~Kw2Y5XmoXpMtgviSLU@9M^QX1J$whe4?zy`COP_Jr z4Ji(QRGRn_MrS|OjpF8Q7Zyiu)|J=~M>4)&fVb7iC(U{z@_ev5{oHVSJ^1Aw<+rai zz*p1X%k z0rf{kxG7Rsq>@KbN@6nwedQj5z^gv_#ya2`o23(LkJ`0v8DE7tH|7@0g??EX&}G}x z6M4xDO{nIo`w&NeFG<&=t2^hh1}tlISjurt0F)Z$l#g~ZX(Q@2E25!riJ^@ZqC(zn zJmrS&(G_vnuZ08sCX^^J`)|-)H`bsx*)GP&Jq25X`WwXRYeTJ9~xZ_IF-l_(DpQSYuQSU4RQTRCd9 zz3nq*5lj2j8mp1EH*{z_2FrW9K2;3E`{&Md47bqzSjO^Lw@HvOv~yi&w>K+d!HJ|i z8o9~O`;sR1ktXuukS#ALA*R60HaaN%Sm+I*k2cYC9xH@9!MFwe*qiritN#EsI~ws`Z4Tue;x)rp`dIFT8k?EE$TUo7*j%YPCSa)&=nnp>$5Tv?%$DnR+j_2MO z8mfeGS08O|lq%@BaW#{1?kp=8~&U!TKqUoRt70MG5f|N70WZg3W7{ zFj^-uyj2G0)lSTL51M=sM1U%B;!?{6AdLQ;B4a~T0dk899eWN97@}=)M>RL+{JPXs zq>m5pQx9uVs(@vyYG{#^rcy_iY20v~0#oIZQ|d`MtwXLtxJYCAk9I9hS-aR%g(XZzj_MFR2?a`}X_iN`2MQu+ zx{-&LsVe!W6|KjrrxXXX*@Kf(qP(7hqFutmzY%z$B&NBVcj7MRM=f&$FfamA5l{el zRsLzje^KyKuX9?8s=4V(jWYOfJxB!(wLmLXO;U5~k-uFh=EF_eKnx2_qKDv~p zS;(LiOgF9!9V0_OF1xaA>S9uvr zE!)&b>Gi0ab!~mcvNty7*?Y>${@>lZI^iS9Y7i^z?!uvzUTdn3TAWl%y}k5&Ovq(lQo+Dlx{?oCZJCd**~8FP{+NR6Xz$G;8Nbkc*7_5y6C*`C|3oWTBDC!9TM>^y>;Xf>=Mo>7c zFoZ3%gvZk&qf>{)LIo>Bf*8_LOj>y8lcy=T^z+uGK6+4il}zjI!&{9L>uC$MX5ax zO;n$+6%Grc0nteuXDBA+B?!r01y{dHz^B`ZyC-V1&G!XjT`A+)%8IswO3)@WkX4xk zD0?v>c^G?F9AqvX6y#Qds(0O6aoxP-uuHnamm+&G7nxRFp%ttC@psIAP$t=DLcZ}z>J&#{_u3Ehr zrO0p~_0p|4@V0M)WjbGHqg;h(heRIOBbrV@- z6jwjd=Opll4{r_UzIl^oJ-+2LXIxEzW(*=kWK4NS&Rd9gz!ao}r7Jbk3cK-^_cv_h z^1?RSN#%g=d(^gW$oC5cx8RdYS9y0M4Q%I`8=+BMn$vU9GJf8cxe;^$bhm{P zrqa6p;p#o37K@994~S7^HhAloFqoZD;FB7bs1u9ed1^ww1Mb9w;cSs;q3vgVz0bX- zs(y=aUGl>g;cL1jZiMTV6IPcQAmvIQ(Tw8qE{O{Dy|`?Htz+j{c2nw|6g(s( z14E>xRHm^^sxjo4s!qCDe4B8&LzbCsYts85QEd~@5rUehQF>7{Jo`*Hf>Tsl!W63asZ_%^HiD?%G4BSZ6zNXSnDn_h$aPwlu7b3uW*WHEsp#XN zqA0PDGz9j0Fa;{mw{5zpQiJ%gAZmipC0Dz;dCsKOSpSKG~ z!8R!KX;Gqd`_+TBv`LbrNIx$g-xabyzMmhNS|unwp8QYSd#zOa;7pa|l;XIA#gONm zlsc*eG;{WLcj08g<5i>1GPn`qGZ-0xN-`u?rAZ?oJAlFubyJwQq=%@|U2Vof-=Wgw zXc7qO1`wS{K$1WqBxj+280{mst{C}g>m5WJX<+6zEQB)Z+qI_)yHXZ^p<27U4tdBHI>Mig=l5RQb9^QwNM<@ zUY|0%mHz~qoqo9rdg1t48~{=-$Y$pY=PoUK6Op1=U>h^@OgGu3krW*KD-WjK(zab)L<1Y1OQ}!pljaaTvwHI<+655bYhXon(C#v1uv(2?u-L(o#OR5y)R~&VEw4qebM@sb= zPD}FM04)<|{#|Uc3?^Az2vKg`^|`<9dy)hzt)|y*vb*ZZ6GrFjiNXT){sZl#{_qMfq0SBad1C0OJ1u1RA|x zbDY~^on7iAM?>T##-X+jjXgYNSSid+M5z3>ZHT1Njv}1h{{RGt@2SplmXlrb^C>wW zEtu%~sEjp!YHt-Q7hyKTKhOu(PwhzZIsX8eou`?|cMy8fE+IefQZPS?OX5_dU6KE zJDYK?y0;wXd-Y3v{*_=&^NLVyTzKIV2hb69-tP*nZqj^RPoD4fvEgvy6P9iJLBWmo z1VT*NNoDsJC2L-jRjF!G>Fp;FxA#uyD#wb&K-onaJ&H4AeEa1-SW?lo#MZ~4q9mfVkd>T;eyjt?s!g8?d|zRz7Sy$cjHD}CeZ(AlAS)OtmJ$6|1vM;S zh8B^LQ;Pa2k7ND?EtL4vh~?7{X;ldJt%6i2Plqv1+z4)|ClC>|rnDbf!Ki@?r%G0{ zm`-%U^E*V;@m=fnMHO~$%J0yPoYf{%ailFH_lvgoozL6%n5=DYdj03u_NE=kX@xZY35wi2(M5X7>mzH zTEvzfA2MH13eNB7fe>1vI;$aw{4B^fPCkkzZWz^6qpwqzE#7jXiz;$U(IOP-pagqG zaThy^v)EeDCqoGCSNOSNE@$Yiw$ad~_{j+gC3=r;Sd4q>r!8BeT#mIP#Fd1R+1rM1 zWlFHepiY*rP8gy()Zswtoo7k<4kNx%wW^qYQbQPNNo#e2wHBEJS5OJVmav5m>6#PM zWnHGkx})SCDS66`DC_RTHhv~PBh(f5x4Bu_bUd|XQ*~i+61m1(QUXD7M0Lu!1B{{@ zLL{JFWc-a`_p~%s2b`{UIbf8B+*MAc6w;Z~8!pMWb4II<``394qe7`LFpb@mQ#OuIF&R1Rq^xeB zJVM3ZM$NiMS%b73wqM^P&A5w;i;PrGYVsXH39oQg8(K1WU8$%FUmkc90DL(Y{`f)dHcF75+6VX;9gOd7t-nj&DW`(0yn(YehW`NNZOynXyWFj-&B5tcS62U zcEP)C*>(rw-~RxZAxsVk#Tw6)89AXTa=LorX2UTQ>~l+cRdaFe3yTTkb0q7prCm-7 z+Rtt)70%CyjXO7p zcfGo9(1ZBB;ab+jWT>qOQ8X2;XfZ!&9M`s?vKj?T%S&a4$n{%_y+|L+JL8+-xpU;( zgL{6<5G?K6TT4m-aHl1y12dLc;sMuiV_(whM)KWA~(iT7-( zi2m>0?A2*YKqfK|K%D!JQgGh?04sAP2{dY$+uL6mX;wA8as^|oEy#U8h03=ZREVib zQ>g>X6&*Z1*s0x+A6nL{T*QZTbdl3UX-`}1&n{HSEWe{PC2l1tNgci}+%z`Q$TSL7 z*%)CuxK%Ov9#ku>swj_sZ4TTPgb1hxr3Co8XNepu9T{;ULhiX)Fk?fwr(}ywqSdsb zz-|&BTL^5WYOZQDB;vp3`IC})D`a-8hRif^qJTG6%ikAUoR)2?Zr26WptI#R6RT2l zsQ?^bY_pc}FjrEsJ;k)IuBAO11uyY7%1*_#NL|uj&fTRP)~Zn=nIu%=rJl+gsVLE) zT7JUYV;5|C&0J9(*Kd(}xUM5xv5QM+K|d|=xKyBODO@jQCzQ~7YLm^emo6>pqGWB$ z4$+8-Hv6Ub@rllr7E%h3v?OKHl*H7RlQ?w-&?^2v8))C*xz#I8)oa-MS{1hECfKQQ zr4pF!YFJqySH+)94V3pYx`3kQEUautQ<_STW$&G(KXBXs014UQEH;3dW#xhtq|gcq zbQrLVOx{uIa-lzR*}m`wGlG||yW-DhvZm*7w>I^W4WYN7AOaGFns|~$F;}t0%QW3B zI;-|)aa!8i`I3)yK|7zBcq-v;HssnQ!tzJmC6-W>AfCabj9xKYM&jxyU?-I9?((5PlHf!pKBa0kVAo#s`oh`e}JbN>KwdZ6T(*GsLT z*l-)NTckSMYd}zx8*Rdx6>95^*BjeQX<-f;5*H8Ve=?a%YuCo$IvQw__lKyRufGkC z@o18uGi^dzdJ75%%}~ioB~{xy3so>mu(G zHO_PL+e>JB$ppr1nyP}maLXaIFQ;cqm8WiZPY+`!Q$Z=|%>J~tt&?QQw{fCmPu_Pn z)XOp?M_UZRO431jUS%~bSHh9oh@Gq2sg2iE+Aw`;Pr1LD3tz*Gc7M!pr>SwaT8#N` zO1MgK`O;i*$u21BQ?9tJYg&|gl5@vTS;E+(4QQYX=k4X&?jnEwErfK#ck zO-LEj5m_lZVk+gfY`X3Tf|760;;Q1OATQ;a(gIjgwUm$rdJI=A9^=JmS+KzW0Cz-| z+hGAdH7Mtpsl$*R1RGg`W%TI!h7mW&I z;wigwKJK+hzu#E1z-2_aCP`3ANJ*fr2&p|Qil*zdo=b8gZA#SpU!1UqAPvLAT0B;L zUvDk1xvzUd&9yJ+y+^RJ`lr1eWys!Zr2#%Jg;WQpOh|UG1958uohy1tR8P$I_p{1m z*&Va`n8C?ZM6gLfGC~J+eU-+r=23G@PU;{lN6l8-uOIqY;~NZ@T#It#OT+gGkrerH zE<$CZSt$UJ20)RFcWKBkv+~G37HRaZnC}!)Vm5`XtMqib)9Y3DitqDmFS4kciOU=A z16M+7hf)FXuAqW5>5VrxY$OyStDg3bS=R2LG^&?ue0#fht)Fz=EGc62)rcxx+wWUt zNNANPsR>ycx^x(+UFYGmk&(ZPvC9H~ z7qE+Xp{s{5uP3KBZxQ0ox81C6TH8ntF(qgz3a(m7IKz91G%~mrI;D1}VIjM>gtm5c zg=nB=VO{wbfb@05ny7nPG6~jY zA9qfIo%odUN0(k|w*>N;=xapi1cFCOeYlBMhD)lVo}g}mB>x`(@=H$m( zUMOfKx7AkEj*72dnB6vRGU|M3^1N3S_hUVdwA^6aSAvgkn@iJ=GHy36pWXJ<3w5?c zoyR}U`A191O&ddP^is7P&Utjjs>>ys#l#v%Q6*$sgKupu!*Kb53=MCzsu%o=wxuod zsg6d7%GMgl>nPVibM_2vQf2N1w^uqYRx}DY=cUP5OxNl`A#Hur+E(~1-LZ`Hx1<`VGo3D}+#HpC zxJAAra~GS66?Z|@mo&9PE8Y)WXd5OEm$3~arks~PcVy*Hw$iAwz#*Y z+%3#SlE=(6g-I${Z>R(|l>npNTH&?!?|@8W0M$rgJ4t^fu%4r&r_UN%?shwrm%eE+ z;JDJ8mZDS;qt<};<%*u+yoy$V#H~NKxI3F?l5}lV&;C@2zIKJ0*we;zr!+LU>8?vz zqDVc%w;6@LPvs#0*mEbZ%GEc$It!TW2HZuWSs&awiP7tr$v z^42IbI+IXFC|gq`>kQH9G-)eO?&j8yIkbAqYb#r)isWSNuXKood6wA-EfJ|IX~Co( z(rK5|j;?NQ2|su*m)Bc5{uk{I|60SfqqgB1q2|bnbL=)N);&Z)Y6Pbi08Fm7iwV z_T|A+?XX#hU8#M<{6w7Vo)2@AnsG;Jnqqd{rIX8;0IEdXoQrC)%#g%c2!7zo09s8# z5T$glyRIfN_$SKHTr^e9&%W*L32RThnR`C{eBEsBG~H(^T$G_<1FBM&0Rn@tVou`T zJ9$_~ih;u9r^(pM)M{y>raNzncQ)s}IcJa6SBX}J)55Xt_Lvq+>+6YowRw#?;|EepYC49h)O6{L=I46L6ot`^ z9Jk93)xmiPiesx3wro3NZf#XsWUZI>^`WIVImt>2Z3(A~%w9KlOgPDd+$-0V#bAybzxXX-WjtQ@&t&%Qaggd)JNItV#_rvY zfx0)rJ^1Ts?Z5h;bG)7D4f*>tTtuED|Dw`5Hv$o?30PpAdRoV|rIKV4nRboCDVehVZ7vX{Hsb|D zLXk{~>t4)sZr_6<$V+bA*B(R)E=$f`;$)h(Ei7*^O@C^pTK4lxK6HyzefIHZ;vmJks^Xe`6gcGF%sjp5+VkoA7tkGx zJ6*92IT!x`;&S;#kfZ04ML`KQ_l#`h5EEuNBE-|KJdC#1fI8r*z(JLQ4~MT@d3Y76 zpaQ-xXLb>gr&OX1M>7Zns~qF(J>m0Fqc{L&<4w0IZIX zYln7Pzo*4j_f7evqH$|&Ng&}Xj zLcs=v6qR+s#y&7Or{}pDc?VNs(E8J5?@0VJ5CJ2bqj;!v`S3xf`Nse(V<(De?&0P~ z`Y58kEpRh>O1Z6~4`1(A{JyL_{5~%fnC@0^{{Z`Pz5f7KjsX7v_t4k=)dL>#(Yj!=K-#M&Cn>xPe+Sf>qTlGND$l!~`pbO(0P$Eu%Z*PQhVlD@*cZPF^hr;= zxL%d)M3=wwcn3hjj?9MfKe#=MD7JbSLV}%o{{266N`Lx;49&Yje#^b!?K(%XDtzal zC|*&Owl^inAB-Y;0ARoX!C(dk+?--756ZUJ%hf5lDqFI!)eZq8dPvwmDx&Bo&-n%$+^ z=GWLn+3yN_F;A0b?kQS-?n8Z5TD6Wx8Y$Ni`al2_K#{S>{;B5bK05xjrdoGPv8Y-m}p$xXAfTaf8Z7I}1Dk8KMrZ!wYF_w0W zfx3nYEbA$ilI~+G_>~WVTK9J1MKcRZ0{2`)42EG=&0LEEHL{% zQ)@5jtBOrloj<~RJ-9bsi@_zR7Mu%hby7TCJ3+1yXS(oGvwQInx*rhy<&xI&PN5<* z3tEOm6*doCZ5F1mg1F}-(;eH#rAK?ZQB;&Pwvq&JHBx}qfM-u;A+HIlwqTFAnvflS zB*rB|LuL4j3Tw_laSEu;srF$A7VSxv^cRqT&Q_1;OQ#J^a@mo8HnkO<0%P_DOSJ$P~uuP28BVoxqHb6 zig0-+=1>werB%47=-z405aQIJ%)-R^rXSx7*r0Q@m&I z7^{)GZZ5>SZN{^gYK<0GkY15OXcRnY6d7STucRUEXf*>*O5}|8GS!!C^BwNF!>iCM z#aljW+dO{*axoK+_X_X!lrgGA^HQHQP|*Z|P-}*pa(kCW{9!{?Snd=!*lxhHcT;Jv ztwB&8m1v&C)*UHzrGk|vwd+xd+o<}OLZxG}CMko*C3&{)*y*W9bmNdiBr?ub^9k<%01g%N0OXK&Q<0>&rTB7e9;2ziBntra zqiqHoJ`@#`)kNVQfSbRpqwSKl1f?MZwwYyuR;i=K1ybNGr6@ubl16H$Q-uNKqX;C_ zr3el%huV4c`HV8Q+;OUWNo`?7Q`!as<`X1NZ-_^v5TSS`~TvlFc z%*chu7V%s=)Go*|%>!y=vsWgKtYv+(w<#Yi~71l1Ii8Ds-Q- z6ih}+OG!)<9XS=c@13y*R?WB8BD!hO?lm;vtt7u1%#~9o-15a9{cF($VVDpWg zhLO0@!_ch<=FxDpZYy`Et>I^X=dBm!t}eiR1Qe~QG1+-0qb`FP-{t-}E$$|X+*|ur zyZPI@it;NE`ST;{kd(kG3Lxqom~e?y zrpnUJrLC_rf{6zRBb`x3*BSBx&yuoeRoU$8k0TI>+?jF+TP?^BH1~VqV)jE!~6PhL_g_$K%W!fXnjM72&Qx4m~H*rd73Ce)cRIk^Aq@BS(7;y>>AuWK* zDFHcDsMD@z7C6WORIYv^k21>zD}CK5ZGsA#)k44MkA4ryH5#DbelzY!b%v7_m82## zP)XHJ+MG9VsxxW|OnI#&1+P$;XtBNdwSuKOEJ|+ zo=b`?wiB&S5F0?ul1&HKTq0u$=t4-0^jFs&LuHp+Bshf%5JzDcNggy4NNF{3;7UhC zj-=qND8yA{B8na6!lk97bH1e?0JuP@16B~hKqgk zd{2JkDl!zJ%T7K=fxT5aKmcdgA4E34YVC0tXzXJ(^2GsM9lU#;ahJ@=c;Qbl)Pye) z^15uhe0p`}X#u`GM294CFCadZE&Y*llC4h1QjwdMnTpNU{{Yo5?-P}DvBcj`j-SeZ z!Bo?8VIay|iQ*KQylMNx{{S%h$D~xA-Bv4>+`GxN-Q#?;xaTkqrkp_f?aO|#-Pu%o&7x(^=?uwf!9tf3 z?P?AkTjQdI-9T!nLmO>4C!xN@8(mqD)EILDs?(t%wG8xu*hVB}c8R16XHgVEZ`(A> zLy#DseuH{>#t`yT3Qx@;ge(d+wG5K525G}*d|IhYI~dP|QmP?+DOge!(<OIM=x7;(@{Rtg-A*DEy5^4uS^9Ox|V1ULTGz2>MwN^B2n3@wZWR&rNn?E zVCgj-y>LbDrM9Ig9^uy>K#I&DM370K(rbvEhrm?kA6Y*9T*&>Y`l7(Fye_yOkPZC6{N%LNu+oP5G4VApo4P z?nq4d6-ckEr4Xb3iNZ1+6M4-7`V#f+0ozBRzd|;4Q{ZafY*JY0LHr37%igR* z?C)eipH?C;vjSAU>&DTQP|SGg zON12X-A@NP zc0Jc;3J1cZ;OC-T6;b)Tj=m56hS}2lzhL~@i$4x7J(?G2I6F*~PAasnWQ!K7W z+xIB`eA#m0CWIg%2~`tL+*7UaQ%3IJ0 z8*bXQ>5MKO?rveX6CR!l0euK6I!PnDw-;xwgOY+FhkH^~!l+Py^AxR&pmlB;qS)H1bvvr8dM4c98D zQ#xYfWFBLqxU9b>$o^pTXxgU+-Q3$c%Wn{`8wrihwJ%fh+?4YVDJ50X3POM%Mk8;q zlF06+KcuTR7j;`($I&cd{i;-CS5- zazm*_R<*Mo1;nPJ;)IP<@b>mo4wD32w3DBZec+rW<{?}Bymj?-v;0{TU#ws}#Nlry z6|5n$&B0O}KiV_pDH?LGQ`ZYsc@{)Q$f8O6amxT}`W*sOLY zY{2>R($61C`XhtFyj{p!Pi$WxZg%c_V{64!Npe^b(;7of$DPlw0nJmzp-uCm<&uBtnxjNXi+Q= zC^r97gxjoJ}9%bprWs+ivMex87uqFz0GDPpg*okG-!qJ<%@57SnpY zzGPi~QM9zu+$aeoCZ{~JJ-u-kX@NFS2!SpQ@m6b{~enV zw$6Xgvf0~nT=o#=poWsKfhXHaVV393{EfMc4|!fYTeKUUziq^o=mxY_*L`lOdukK= zFKAj)eDry2OwC2H(9m`gJ8_cZv4TVA%2!TqZN(+G{gtz5I4Ea#aR%mu-M4r%aBZ0q z*<}(Al`U(=WW6k8%Lf~f^>5TI$|BNw|Zj|_1KJk*7W z<-5~=J<*CGK#;B5e|+yZEa;yt_8FfkF1YEKWxD zc0NZMR;qB_5uq=4+HF>;!m)gmN=Z&eloSMZ_u{8);>%Y#+)CAQF^TdFV-Af|-spCF zcF3^o*6}V&dPz!-M<6?C(;5AoLC^ZYBG1WS#jFT=N(AMZzplvI^mdft;RiUU&l`m3_RZ;uL*@rUE6Q4$eduth)6=ZUZ zk+ovAHdXRmv}=jxDVB=rT&+Mk;(z8&ji!p4>~k1hDR!x|viABHx)X7`O-y!(amLyw zsP-Q0J<8nD5Z3Wfdt$`=(UMI9_aQwh=C)C8Wyx@&YC<0=DoIyPy#^M+mau#LQ!Vd2 z)1Tprv?#XGvNtW}b0!-a9;u*Kq>PV#ER*G7Y;+}On9ZemEWz}m8<-{B?fY6ijx!!( zhC&l`cj7_PYC-o?3++2--Eq`PPrJ7h!;fnKs2o)|+-ysm=KQ)Fh}iK0vJwabvn*{` z!^(?>cRy63pwT96`*!7S9mex+gZP9C6(5@YXwpIrK&hcUJ1}g!Ad~n`(77$O9e-}$ znv7INYOPMp^`$cJbr!wVZH;1q8Y78dw51d+=Fl{isOOzJ3}+c0(>5X<8Lq3QHva(T zo!3!18;}}9Xcs@X75(e>=Dytx$t^o^miy{x9Vs4TrC<)7_}Ou|&n^bGoC4!*!?+E_ z>)sBIaay%8!Jxe2H4lkFKxczXY)=rAwZBiex1}a35f#uJT2GkaIVanQxh>0&1$(x> z0i07N{cwdb!rZCpQjzXM)L35$B#%Z~213Z3w>!X!}$NT*^?>BJi#A=-tZKKeGrK;;l!?G2eLaTnyv zTc)LCsG8!#X!1hOvQV_XprF~YsbZ$)kEvKZve7gaUX=FQdu`QCZC^Uj0M$;bRT3%N zhz!O50J%yn4{73$nN+iJw5mLpLM0{SB>wP|8%tovgxRx7 zTpV62U!ij^Y&3>h^Gf`rli&um&m9qV@>o4fgt6L4eQTtCklWiSdh0gsrleh}gj&+1 zr9~j8#6@~n9QEBVK7*#9F8KVf8xz&$fM3a9_0MV(B{AFcTz0ieY29350?@qbyQPWSJnRG^(?wu4y=H(4V>W;I!5NI#Z^- zxH?aytrVrNH5%j9N9aN82`Aat9!6q@xetPNmnL1t=)HNY-0d)(X_*LnLJLBx8q#t! zJ-EJYjpcD;NZF_*VLi_6ET6s_ z5EskiNacG0&=RJOD?Xpuhj>RYw1M!iBn7=rD?pJk4k zLMq@*RlC1f$g;bF+5qZC=IDJ2l5<^x)3Es#<+kj~4@9*w8_ifCg*xSRgG1LU(;E)e zzkS`Hdxa{w1G!k(ak)bjW}qnpVD9#%+U^!;lM@2f5wj`SYEDa9Km==6c!|#9Tjl}= zrCxRh>U6L=H#^;EtrVrj+x9l#yGPp*qrpX*?v-*a~Dl5}U}$ZexwbN*3ix)ZurG1*3fE z%^V83c3Zo5*=fE?;k`_7<3%HF>n6+FmkZ^Z`G1y|*_P^}#ch<)wgIX}r5ck=RxfWS zxMN(vI4yr~+F037Byy9-M85UYjod<-P4eL5N%&Au0=u)!;wR;=JQbQu#xUp!qD6Ob z@x`eMODniMrJCys3HvbWEM4REG)yjcelh{>)TBv!&yLDVjl!KTNc+Vp(1ZH%B_1N= z1z5b^`07@Kr)L~pc<%jpj3RBvQ#m?|K5NQQ@YI8l7>C>zmr%#6v{wy-n8#mF(#HGA z*Sq(=$J?d~KV{3qA0kTRVI}yKI!* zpj*}=G#pAIl?_#B^vHJmu@`rTb1%b5R$NZ!ifl=ht*9K8P`~=O+qN~&M4f}JWlKtu zr8K1KNh8D*fO|2oW4m)S;1MCAT#vcGFqU?1a~*C8^#m@{^2={$#sm;z(E+MWX z8r(B($*LU)S_(9P23ciXYk%#WM&Yw{d_0#fJM$$s;qJ8P1E}gkmiXS`VB}4YXWKS+ zk+&giQ}~6>QdAQ2u4)P@E2t)bS1eO^1~yAuvg7Y{XtuqHnY7E}Tjjf>%hap2$9$2t zcLY{#@h(ogB`MSv;&X8=jWww6?#4N|@DjxG@+no=y90gOUc@2?R*EAV`+3WW9xw8+ zH+KMlK!3j=x{tX!S91mcnB1{F{dL z!A&`*eg6P*ajx+6o9aH@#L(An+t!v-^ATM_(6c3N1*v{AqfH{yCM+UfKz3hsnk8DmUG%kmCH=_%9UBdP0-)M2P!G#GkaqN`ly6uqUk zTyc3a^Xacl^x%cM)mt$#Yem@)%d#Kyf8{gk$7>Y%zG&Z3TWW=0+t^x^1wH2+jof<2 zyCZ+A>?>D&TLj~4oQkOHDwu_MkQT?L5l+xR zlA=KC*@C07Wx%7XH}O#EKH6i+LZ2;e&L@cyGjh0M;XZQ>ArmE?<1MD&eJP^c4Wix( z0+pVCqd}EU5Ne2)30&%fPo$(fuJ75?m@HZNts8Y}6hyUOu2a^Z@?UKO1#zlzZEGks zC+kU@enDq&+2k5F7?9ZmupL;Cu@sOfz26vpgPMF5r(=+1SY{>Ew3)>@hTU3{j=iA< zr`=p#I_Os`yKOQPFXQxMNWGPNyAB?gBooFB|cb&mj=o1sckilrol1W=y(AMoLY!_8IKZ^+*- zf@pxgy;8oIj_Xzc=9D!T{C{A<{KAdY)B)99H6Y-oQV@r=0-&WN0xEk+!ez_dZRCro zr6_6ARbHTAkCgbJ1Ox(^Iq4J!r&IcHnRC2R!-4@(ii&D;{oe1h9+F=aR@5Mr*r6IU ztF-5y8l_8#SwuB0hvXqiLvi%^fl2{YS4sod0Z@({i|MThHK%WO0Hd>l(58l<5`Fmc zHC7aw)RRGvBLa!OflyL=>S=;FY7x)Cug1%DQ#KaWaVo2*gwthRNGJs$eHhp=4*vkR z1TI6}$EPIld{ZxbyKl6CBK0*8f`Hc11x~G0rbwl5!uDo2QdIWZw4Qw!3V`msgtaYk z>=FK1rDP?q#OWO}>=>>~ldgekcN`2-Du#g2Yz+J9Ri5+SFXI1s@V!uwu``khJH7cXe-kmZAehJ zaAyJ_shJDLGNm&Ce5Aal8LRLgWU8@W85npCgTTv4xh zuBX9C)ov2b!*~9kj8&enq>TG1hgK)vQTwUC_T;XBaXON?k`+%bwHWXp(s9JKT{Z+hsD+H@t|fq37ecPzt=HbRerP+_S}1xqN|* z#4vRZ3OY@*fo;n=<~<~8pq<}n+ZNmX;tt-ts_$)!A!)bVR-Fad^{N!4_9}*iV%cFN z(!vYdM{`;BYAa|i3t@LtITd2KF8=_-Fehz6zE^aXu;heZ;;`ZyZln7E%?(vtwO1ML z@Wo_gjQKPRcI{TRy&y2TIRM8fS+X zZLcG22zmF&)TK9fa@#IxzEJ)f2%u0OI_|y6eB{07BJcgc+@5LopfD2KAUPekQgomJ zm{oCo+Bl{|WX6qXvEJZiWx2{oL#bR1xk_;kl6#SUHA}@FGpHJ z6jGY&IZ*%-Pt}8hn#kgu5p45hRm3!i?W9z=^N47D8jVf?$}&sFH$rRbMw1l{)hCt5<&{8LMz@e&A8ahE+|*f z-OYF>vo7KYRgzRzqP*x~@gb0^jJxxuG|nwt(==hOG*br_@&)5$OhmY>iMG>E#9%r# zFym^dqLMPNb|@Q@2OY@pa@qTHzOFGxhg4^XPL0vJwq4hAelN9l((LWar_9B5K(rk{ z5|EVi2fGb%_E!->(IKjn&*to|uRQ1((_SkRw!`~|-Ed|*O|981I;ia+Q^XKRp+4*> z4Z~xYRXV)I$#H6Fu8Nv6Tw8{z(CcKSNIfbIF{6nZT09EpdBfQu9KxD;a{D`YdR^lS zMcvZm{x{q+A;enitK=rdQriN*x{^p8*r4sqe0F)+3wTHt&AW3_9N`qAM2?BzZrV|ZD8YGLTfV1XXywJ$6_O#daj?6C_Rh3#J$wV|E zI(2DLQjL1kEEZ#;b7;cpfb_03@|49~IRkJ=$$F$Zf@_r$Y5K94WS?0I`cvEcLl!lI z(3;lv7VFvo>J1OLVT*XHOz)sicbv6P+v(erWhNw8NqR|78j2A@_ZBjBn|98rgZ zPYnrAlG8xZ&xu7x*@cUOO^qRICE)0cQ4BVgl6-`NO!5^p@4&hGC82JphNWr`IDl4< zg(E+vxNFuEmPUYe9ag6`En=z^!et9Z5k;VlR5_l}ksj$xiz4p#+ttBC8u1Y}!DJm5NW(_fz*|fiqi~8d$+58H^0 zm-5Q6;6I$Aq$q-#cAW7`3t;`ky=_QxRCKHml{k^Awd!e)Tarjoxp@O^+56$Yup;}Pbf zT|{VR2e$x|LxfjJuRt=wft5j3=}qg7`01$%1L+jOXEX_|@AA*6uc7|{%?f$8@vovy zme~!i6_y&_V=T1W<2U;81G3p!+*V2583TnpDy!U-2DZC(pbBVGp1?*nYeW1{7cS+$ zl)5eGV%k60yAp~(MB9Ybp;Y*QcSq{ReZMY?ZYZ>_*JH!&%e^Ye@)fC0TLObXAtZDZ zIF7(vj-@reFx8Jhpg8eS<^-*QtG^cqU1_4pEwTMBPIYbREk+WR9HFEAn(30U7B4;O zD~qr%5eEblG09RwhljeDRr6HPBEx7$6o2HNIzugzPe1`UZu^FgB}l`Hnc|_PY^I!K z>2(@Zdk7=+;5_D@2B}xtzB5M(Ae@d(6cPv(BRXL~;G4QXLM~fyELDZo9STMWWe8bn zO7o8`Bm)_0sm&Cpx2_PDP)3SHj3L6Qz-2sP5}Q++5ZB{FO~QtDGa(5 zNNq|{D$iD0d$DVX=oWi;J@QdTth47hQw|G{ zud%k$rKtT0$E3MqPNgFrI$3@JR7Kl#ww4-tBaM|B1gsICN_4}&o`?=84l>{xbd{vb zH2CO4vLL#{Ysm^y6p($@0|aICPvB9e#N0-8X$X?7QUqe~WQLAaJ!|ygaVRAcOBb>_ zkequ{josb0z1MOVdv%Siw-s_$XG;vbF=-V`5!(QjEYr4w5V>sYt>$AI!tSpQO2xOf zv{!bomCp?<<>I4TU8>(Ia}3_Drpsw@L*}kQg-Kg!>yt?-B>gy=p6TsnS}Iju-qJGj z<{AV_=Nj7*Tg=)nCh-jf`7@Tvqn8C~UoAyNKVA~Hdz{~RP_JA<#yi|K2{kg5K})Q( zq1df7BMwMSn=lJRFH+P(G=tP&c`palqb;-z6h>p9zym~J5|jwdsng~uNKb0QMFu?S z2uj8jvA68jhZ5VY$XtTeHLidVMk}|MJznatSXk1Bg0!0xdha;4hdAcRx-K#7`<+gF z<(auI`3tLbaOnrMbpx(5+_nbD%qMdJaOXv`ZOze>Hs2>4juE?vs_QQd?dyEcMVAeu zDpQ&njd^Bb!3kgd<;ODB?CFkW&rVyp!^hqW=*H5=Hg4Ry2bzG>Rm^5>8zslww#2a9 zqq+q(m)mp9SXBX_NTzt{_Fh~vKBqfH^HAZeUn`+w4QihIAB4BY>)figTCHn|9W)|a z;YdOoDF;?!rk>1KGyC?=+A7wiUiLR+GdVl1maw&+C0Q8$m2UAPO#EAMu^d7H$K6OL zN&u}%RWY46xxz_0V{aAH4g9q}*4(~%I4AbRk8I=pk#M;~d2*-8aHi29Hb`?$MO5iP zbHoNlXJ1`W-w5Kg&8=q^2NZe-ym%#UJDqdf8--fGU+@n;s} z;}X_t{Nrs~UQ1;LgFL7XWRZ&9i4JKcXn8>IG<1|5{$0=J zEzvEHA-Tn{7a>J71a&#!ZSBD(p)tO^evJfGH?9-PB7COey|Cnmb8%YI*eUEt&k{Cv zE-tGYqp}$o*v^?A6mNRCZ0{lOCQ4k=kN`sJR7O{y(9$IMT&tw&%>KLqyOz>kun4Cl_TtjZD{JKv9bRgY za^C@R2H(3qFK?{weX{7RR~oOFfCWfe27gvI-KE?MN!S}Ej$9+%4#?$fZe06u*AYzq zEOT@}8+UA^MAZ`E71HYpO$czOPg-^E#O~v}x9en$Jk@8nc3A@iw$EW2DxW3DcB}d# z-UE?iM1M`iy(LQtOO#lrept|0(=-6FVbYdb-j>QKL#jdDvaEBr4EZ(~%Z+F%Psyd< zh=7q&K(ABN5*Cp^qK;4*dzjyFT^@}{o@+$ZEUihU0j?JLaGJ$(?-eh@vL(Hw?ds`l zlIuiPBgtA@ZD}2Pnw(Unm^yxdwmUpvk00kejZ{H8@exIB#k2;|UyVgXj9H+70jkdL zvf9uht=@w`6k3}0^{>}}Hg!qcW!wr-YV{ppIPI)z2eoQSMpWo9*js9>5oSpU!e&Qv z?*kE-DLE^W={jh(?Z0LlZPi+6n)gX*arc0ID*)s`8PlP}t%N!%PGQFep6)5V3e_Uu)W;Z(h5UnI*#Nkd(KV*35-aV*q>liqgk#;PM3BP^O-fLM-CT|d zrsgxBAND2`?bqTEtOah6HzMPR@v+lkFk2vAWATvq`d=Cs1G zol`fys?~6d{+VD|-*b{1c1HJg)ujzgZROirWRakw%F49(tDZBB)oq(}{{X;0!>7`> z-rL@7IAI?+D<`Ldo%t7!L(%fyk9?*trW&CErZ zjP(W4w_8Sir8`V6>Q-xK^5K_ei7w?6&61Nx29nrSJ3++@Bes|&>0wj+XqbA z6)i9$@jO*B`0LA6!v6pkLFsOirjuN? zq*wJ~cQqNK2=P?il{jFS6Z@&_#8NG7TgWjaQ#E?2XV zFT7*BY$@ZOL%O2Rcb?K7vPdA6MUdiXj}aX!k*{20xNLSp&R>~;bup)3QtRE{m&0W3 zzYn}{Y_1x5%I1~li+47~R&+h@sV%D$t=8R$+p0ePi>M?;wE4_t2y-7()| z-1rai0O8CP!E<)%TxQ^87lM!%XzoNVt?VRmj@jK-{h72O$8KBD3+>)e5T_KBfILeB z#7|s~Ilnve(#B+%npcAQJ+Pv|+T4qKMv8{ksS+P5?K|%B-dk#Z+>X1J*CDC0!AF?) z?ursJICm!7xr)`2HO7t!hBvqv+susC?u-{kXq%TU)5S~$3ENUzB%d14?s@jmQy#X( z-!duTGOO52}6eu#vfRAP?n7sUu z(V%Fq+pA&0dsn#XP`q`sm9=)m_O>M|WM^M-OHz$WHDpKGgmT#*GUKh>Q{APuzFy42 z4O+RC>i27ni@Q+M4u!+NHf2U5s5+>zPU`25o$XFQ-P={%Liv&Tn!+;`l`Ub>)he{@ z9n~@(&gIK2b)+i!Y-LV@KqN-fpxhZxWbV}Y3u~yI}N=ojH|dO+1}yV#UT-V zNP_t{Y$+_;cW&vEWt%CmB!-()pIG1RF@lm+P+$$ z*+ebL1Uns5+jmn*T2x!89peGFyJHik5))SVBJZEfeC)Dx3>0|Ms*&4wKI(FNb1nB7 zD80&Zjj5G5UU_aP_-KliRQ4Qe*ZBJ=;{rec3zacjn>BMIoeP{?d@77={!6}XJ<+=7 zKH}N9U6AWHPcpj7)0c@kW)2->Htn_KEvBGwO}5`~@3Aw>QbV0kR9SJmwJubrUQ(re z!i$eA)M`CWC++NnafSgvv-{lhrkuURlUkuA?@99Q&07RiCL~ZBWn)s8pd5ixiOboB zv0)>MPNwrtE;ohE#6gbW6~gr5+HRhmy;|EtT8Xm<{E%o#L|3Rqf5pZv+*Wy6>sNS{ z*Zq%ppJv}ib)!$%xdXVR*#7_pb+lXLGc_ton0apMO%+XDG60Np)wDzf!{+I|MxH9G zbC2F^8(|frGnMW5npf<=NZxAOgPyKkr2Lf0++K-2Pi>Wb={u72%WYPsl6IUyN_)x2 zCijvrD>)1NK>JtFZo|&!U?z<6cj^Uvgow+F8Z0Njw1(Olc^$f%`?5ZwAyI@7Jc zZz=ON5&}peV1SZMI^hy}UE-jxGcAbGJ`bWxRkBhT-9DnWmfZnL54g3u_L3-m6sa}( z@#e=~DiY&#$OpO#k+`4IH>JymChb^z$JY+mi?`d7hb}1=xoxRPO8iuH=Zk5e8oXkj z+%w2tPYiX1y-)B_)8$3fMUyx&zCWQ%?^$uZJX-nE^I`P2GF9J+tdO9_Z*oX|avQl_ zk`}p=V=c-O%8HPYKoh7dNYhMMHr$ueTL)ce@_2d{ad~fV41_{z)l`-!keXCtee9w& zys3w|+9`2|LJCtt3hHU~Vj}XP%Tn5t2S-HI%nc-T`ta3RD7>`P3R42mN>%7Gl^pWO zW55zpK?Vk_k51eLQP>xYX*8mKtP~WuE827l9M5hQZ7Pd#qY9AX)Fmz`)~QaB*lL_x z;WS`bW#IS9KGHQ3YfU&=M5C*s2u&)!?)($XO{kl@p|v{1XH=aEB|v5F!V~dUjlU%U z35pS3IYYQOKot?NMIu4?pJ?9PD>LViQ z$eO03zXeLhys%Z`rbh|Lhs;G*ihLv9a2*8+atUmOQlgX@5$(bOEJDK25&%kTPNtu; z3Um~xctw@~84svnvQ5yXOqo9AC35aF zot-6r6RxbN=kLUPlRfUMja8P~$|xJszyL(TKr|p4^rjQ4aQC59lcbc6m<8^ltY-*$ ze}?V?d`b&rN3ylW-)(R!3ht3UYd)1>BndN@6{NOYRXTy0s2y=?o%M3XVd#hxROz%@ z8-}uFe|T|JErJCTO!LnKYy4C$Lj1gYx&)^pTS<)YPlZJI5!f;3zNwrs65&+JmhpR9 zx{xvz@4^*UJ63d+S@+amF680qsFTwb43vKQ=uMwLRk%s}EqZU(&trfS>^MTt<4*S& z{{Xgfzr0K0SZ|S1QfpjGSwG!S*$B&I^euKu*^j%n)9$BRapttOHPcp= zSM=aAQU3r0(Kx`!8+OuG`=(dUZaeI+>`StrT9TfLtAx;3y9Kv|`5!`YdvrzVaPdkU z%;m@V@^kYMRovm*Bxjd_%X?aW!GmqdD{Dc>l_|U<2Kcz~G)y&GX?x1n1m z!Et8SbGaD}2NNqQiKZmjql z*WE!}N~C()IFu-a;UOg{Y@gykvjfsm4a;fbv+|82Hy$MSBxUyCLr{Ffj{t^m$VnwE z>La|9ffPwUsPQR5haO4$ljQqA75eZXrjoI|^+ytwtm<~FBdI2}`tVOE9x1zs91^S- zUrXq6I#89eQlymtQVLB)YlNj~JQF#3Td7Yw+i>m@qg+~XHyLsYK4q4hQbBc81SKBR zkQis@7};4lCE0HI<_v@r)P~(QRavK)skTeCqe@Dn%w=pk7Cx{oQi=%qjJ>a?{ zWp1-0{E5%F?Gm3MY_)B9if>wFLz(xs>w-lP<4QRi9)_Jp62oOo&7*|~Qd_@l{FC~c zYSNpVR&*$m(9OxGAGgRlC3$2ez?#=mYuXNYcaVpxRU3o3Ycz1EPPSN#W-2>$TOmpe ztF(Y>4p@h|1UFS^wPQ=@6{q7Xdw(?oTVOhd2o9xLiV>!i#&epvrv=fQ7Z%wYL*S@y zFj`TuxnFtOBeUd0M56t*AX1G!kn#x2rBp!1tGA{%Hf_~%p82pg8EG3%byL3>ZWec! z5c6H8{{XdZ*Q>7Dw6SyBvt5YNmeQD#HK>&4Ri~%;aYNj;PlqQ9+VU!BR@vL!j2D;- zn52jj*3=p8IXFL^7lnEK|{ADPq8rw9|uiZFJuBtOgDLQJB<9=dNP*3)W2Q2WQbrYzYLXvkZ zwynEzYuUCMWn?*802)B$zY|oPGQK@5Q`=hG&A(vut}owYrd|Hx_&UsoTu?o0;;u7G zvscx9DZoTywP^Gu%Or4C>#NBvAe}w+%9uzRloxN(h>h|XU5ZoaI$v#M{{WL(VHa~l zVNS536YMk?Xs;0PS;vxS!VyD|sqHkv(FB}@dFp@;X(z5kR|=M-+Cl`jYAMqRd3vJ) zhY2Sx>@HeRaz@)td|lm`TqMa$R?)6>bcE{&HP79b+lAn7yqVrVrHR&pwXCHn@sfNg z6!)A?wY60lU8>z_NO?Lu3Csr#YN~s%RIFSON>--=LbCniQfY)`OF|X`847VmPNacU zB_M)M5>62cYf_g^xnsysK3S;+BQPpYUYGhc3j2EZVZ+_ z{zK}7uPxOjma44*K%^e|e_kdp(6YD`tmA0Y04- zbaFG1-FcT#Q1ePs9&KI~B}qC+AoL)N@zQp8vD`q}SZdb_h0pm52`y)IFBEf02i6f& zl9ZmoN@2lJ>uQZUOPTg%k4GpRQ67V(QVLh7p3#pYRd@;tbvW=zu&36*iSu{#G_+L# zm&%WI8#p^f6n<&n!bG=D(TlpR^4fJRreH$DD^T#1)Z~5mVVA)&<|_X3Z?*R{mU97L zbgQ4}!ou!lqmx6`94yC$d zCqKGYSof5SWLt>kCZQ^)c3Ah4QF_A4Sx__yHSVbQ;b%fYssiv)fZLs{s{uq`Ild^$ zBdqmKXQfUQ^3jLmzDQ49yq}4?uDvtbDvnG+1YufTP*BU5gg_7+F?Z|z&p)uO6 zq3xqn=WNF^?>3m}%)cR^E7(fL9a^{ERsF*1TRYUHu6=Py++IngSY@PX8DtYoEtz(p zD7}La5R#({auxEii0Gu${dhHz_f*D3zr;$dqxX-|i6Y`uF(}GYS`*N881q6Z9U`P* zU{{j0o(Z{L>{j%rP@Os(1a$t|N*n!nWsv?+J_!lHAXRsh^I&-@=U+|RNk4eqj>8=V zHLt`YAXIi_j7IE8$*J_LJGrj@BZq+dXrf)jffsEdui`Vws};TBYqU6!d_DcWF&l?> zzO0+Z#;}?y?3-h%bu6&ToLZGcfGVR?-HU9OOlYYz0b=dLO(Vk?(n(U~t8DJ*mI-iX zZS}3W3QEwI+E7wJ9a3^56>E&Wv*fE>7PpAoc?fO%xuZ1oAjDmp4fflR!|8DZ2}tnN zE3{{UdoG?T)1gn{IGCIR+G;q3Htdbj$I+z*nzg7t;0TPRp$Zl3sXwa~9Ns?jrls5# zskJvY9mmTNjaGSH-L`GEq`3X5x33pf6C41PhESf`q-X3HYqjOPe43L$op(0R7jFiQ z9Zpq+$8D^!<+*D^j*?1UsmOb)OleInC3BRL!_&bxr985)xOK)-I})NPiH0(&21kN) zK>4q$`$a8jJrtrz%MIj9wWO3J=`A-9gu3ENhG9CXPPk6lm3ooBmXJ|Fm$2I2cCM>X zCo^30#(`!)YQ!@>@hP~{DiNhXD!qQ3DqZ5M5Zjv7JymnK#I<=WTn1GhUX+?;*-@8V zMz#hSDtN0~ucd9bNZ_LxLB+39#;P9`1uOMp+5qXI&E~INQqy~#sO%vYn@e_Ui)ixK zbZOW8(^`UQs(_ySL;f1l`nc6hvo2c7HMA)H*522BwKL}y8@jBYyIi)BAOqS*nhtnJ zC5DPd&>ATm4&$ zJj<52Bwt`-G$9ZWcXYTr;xT09jZd3gzD+g=Ysjenlo*QIt1Z`w{9{@ zJeXx<(@FrTB;l>bLM9DLoVUVFF9wpPemvrh6i2DU` zRLSm`Bha2I_qRKJd2kDw&I#>$UgD7BcDuVNjOC~ng91BjEi^PCS`sQj8P^Rh=V28F zwNEm3a))%0N+*uR+1tKs`1h*~mV3?H>#pe?hZfrhqs zw(h~qJ)+_`-OxGHMcFPEvpCnLYqV+a8Mj#oy16gMt-z%PC?#|?2Q2Z++m>PY?@$yK z3+X>^ESxq&m`zBRU*UTX|Nob$;@vWc>V`{$KiO%Xryv;n`rd zG`DYuZb)k0%U~YPl<)T8t)^M#O*oY&m)mfQdOtwWs zP&wfu*m(On#Pniuf zYqmO~h0JgP=BWp~_V;1j*uv-qHAq_Le5<3>C=w>+pEmt=M07G{Oi4L0u1F z!kGIu#ThOYDGZ(Dms8P>=}Yr=L-QiUM7x`>OhY;qDK2RxT`QIu!;}nv!=jL%Z$3!C zF4x6X-RHAR3)`02m!%NE)>chab!U}(v3ted0?L4~?ZJi-PFm-ZirO~So!M+4$$oQe zkhQWkc=YB;(}G`LyP}*bhZl*9tQrymk*}9qOxTV##@=}3HgbPA*fSZ zDS&*k0Vk?~Bd!^FtTTWQ1!JAEvdgXQKKBHx#oNl!w3g#HU?(9;s&$c648=#g1kT}| zajSVJTPJ$5#oaQ4LajZT{R?qOZbhO)55Qe2gs3HGAZ95{@ta-arH7FPZkBnQOUCsz zz=O#LHQlier9A2X$eY#(vYY1=%TC$MX4QWwk*%pTo+mTB%Hjdr0`e=;&M zQ^)#A0OLri=f5x@45kpOqN?_v`wIr9^K#J5d^p%mw*CX>b)M#q|tU9fxs$QqYF`Ya!?ZM1=k7+c8QkMpGSX zE2s~%67kuVaa6p9;bg|V5!(y)hprG?zDjZ}scyIUgRLYj#51eLq@?vAW(RH%$TD`s ze7#(Wg1fYJ%T2-l9d%Q$HrV(f7JavI;ciEI^&)|b-rSpQTsMG9DkN)C5DiG71QVZJ zQ?@PsZPPiIvC+JUD@E@PGitiolH#Q5aUSz%tAs05H>X97a+&h6B)FE|ni5u9O3ezi zjLk8lU)?q)7jdG&F?e%4!aCEb2fVgs;MrS-cHX!&q>w?u z@i|y7p$=iHX}LC~_A=nIR?zZIw4z-pZ{s$FVFdGErtUhojTi0WEWVoxxxsIhr zHD>Ny@hQ3lu!b5gmAUS-ZYBm`90611gOYgv z0CO=9ZNF?$l`US+LHRMj{&^!`4ZTAX;m}9u|(J#rpqeU z>fVPfK@znZgzD-sQoEi@yLSii3s=Kf$BVyXqCVkP7IYXF7RQ)`z6h;UDnL1m@j!}K zMtWA^b!!yu4oD2y474YsmpOu3B5UE#d=6X(v$VG8kS5 z83j0~%hw3q)KWdOam1ectg`6V!J$bwUh5j>ij>zIpJdOVRD;(_X3M$pR7la*>g6|c zEu{%2r7C20d-1gPa`GsdB~H37GyL3RZf`E3=x@8KVR0uR@qY|#&Ct%}56k4+sUpzy zb~UOiNNi{caGE7WWRiQaIk>PH9HrY!G=yT$# zy39*NHHC(oYzG%Xl2o!zM!C}$EFQwLu?9x`L@EyD?>0*@GZ`g1K?t|23-aJyxOV0m z36yLxooItWR#bwTd$4}lST<;9job@}_NKp>Z2RqRttTH7S_t|mp}oP$S59KOT<;r_ zS`~SV6^8BB_K~5W+YV|`01_&75^-|2+t6a;=#QCL&R2Nc?l}z8k<`&Z6juiF?Wb`* zq-Ipo+i-QNTm>hlCnSVP5G7`o_s)iA4;3%)U6otYkhbQ}L(7h4xVI9tB(9~TDcvTW z`e}-$_`HtJ&#aZK_G=F|QzT*)pahg2*tOgf%F?2T6otaQDS|2Sd2;lW>Fe2ra#ONQ zM^}qb?VAap-2tPNpRG(jscthI`P{hl4>^c7 z=0Z&pRr}N(=NEfTtk+S*tXDPN6A&z8XjEM*b55Bh*+h9o)f1o-+waAuJX=`W1(8d- zW_!zunD|q5e75woGV>`akfPt1Rq&M#{89I&B>br%ZJn6i#koc>^Ia@?aT&2X_IY17wUc_LqWTe$x^K!?(4)PFx=R-8%zb6wE#<8VIgl0BmBijd6idNX9k;k! z88Vatc{Ji1BnI;ncopoIn zgTh3sXPQ)nQ6t8pqtu*XI149Td+!_sn7{ywATEoE{oz*m}9F8+~tETZzDo{3+GIo0n?O<&CQYa>`sN(pI!v z-zp)r%UeqFipc0SP;W4N92pVbcSP3yWCe80w^02RDn-thPdmEuVm;k(KPCMLiu&SalsrCN;5u5Np z`-z#t{-i8t#8bLsI;3{#m0)SE-}x+ZcYf$Ep?4~Nwe&}@PX7Sgr5?YqFO#kNZ#Iqj z$TpCj2GxJP#RIT>z1~AV@NuhHn%l7WwZC9XMmkG}`JMj&h^D$h0+h&hR#T9dC!fKk!VQ)QGNnWbzDkRkXI2jI_A`5nPa9s}cE$Me)`iFacyS?|M z?W8t{ip`y?YzVU+b;)U2E;wbHC8X)5l!}wuG4nI?x%u3_?RG}qD4;SnP9}tpsdxVX z<)iY4j^B8hE+&(tw9o-hYI*vp^dD;U?RwiPyK~$J_U8AB{Jp(#m1UO0?fkQ$w;Fk< z&%Scjk`|FeT2NCRWBFA4#WT3LL3kKrc9jOJj6XIXm$&R3sERjvk-oka6#YB`r&2Z# z9x~xe9OrAEkTX*@YtYDl$~K&JfPvmCktM#Ftl0|!~hUvP{f~4n>?ZLXr zSPF%Nqb&OOVNgm?wt;yq(Dkpg0tzEV7ONFe%N|CM^(WoFs*fJEr)h??2(fep+i(ow*wew=k+^+WP+hlZ0eb7)SfX zN19Y!?sDAVz(_UuI8#15V1vhWY(b3L$eht?agfr{!uh?p<3*0jpeYpV5=*| zNE`dpwdM}jymCCdyDvnI56)`b+7FnYBmlii29zEBxM8>Vv~%8h6xCU`hiGmru|oOI z;y5acw%+ph1)0#2lH0*u^rA_|m3k`878PwEWBR-jTs`jo2t6!Q-#Y`Y)gh}(=$wcFXd-(EtTk8g)>i=pW*P>g_003=ka;X^?OQ1>_3&lzq4@5}t2@ z+sjBGr3FL321V;U(Ue9~sZ@r?X*prN3PIC>=|&r22pPz&dxuON+JuwkCz3qc831Y4 z<$987`Y@zpMAhzhpd+!GS#TyZN?O!F4JN84xI2vkZy~OQS&fUhHjdQVSDQ`8XsGWQ z&qR@H+{!xD5m6lvBq0RlQZvgjjPp5&xVf6WWHW^p%dod@KN)L77$yq#%M6;^VYcuCbW;L0&3bIcyDTMo>XggaW(eN?Z0;I%X^b-MsQm%u%_3g zY`T{~AS$D;OgP+`h%9<8V>OM&ntD>*zuWg&sAaLZ_a}%{SBit$n%%6=wabRvvLLv& zUIvuV+j;-i5dmCl$V2@igkvl%AvF?5+}LG^RDg%mRUnW~QAxFj*Dt3FQDtIr}hXwRn`{ z+j1b=r@tz5225qflG{zvw0Xu!iZO?okVjE9l?}ah6 z+b_8fY)p>K$Z_71O5JgktOAuNnvqhHbER?2z0mxxZOze;ZRN{9jMG|=Lg`M{{$%$K z*|BwsnKtj-0MAT{2D~uz)TO)DhEm|=%c~1tu67F4oLK{=C3(zw!g9zP>(i;g?zb<75* z(z9*xd0{?MNi5sb2|noX38t31D@X&g7f97bj#3{RGfUjXtc0a7q?EXN1EVjm3XxS0 zotLI0q8M;T3LvQpDgjgg6JGp2j-3jZ<_@hmsW*dI8+&qIZw)8QenDvnJqDDkQP1Cn zarp3LC&g1clYaTFSsi{Fm1n(CdgR=^L&lcQKd`dR$|o#AmjiaN`U=3b=?%Igs-r{N zm60HiW-1$@kyzY^xwX|v_DgHcnTAfM5F`6f1O2B=?VYIQ-;j3x)OEit+4o4$U(!OfsZs+=p-n-id5k#dH55?U?6|ha zNj-iR6YLAG&U!TArk^DYPI<7R57JlPwF@~;PEeizx#~w zc>e&TA%E(r!QH!E<$FGUPxY`LgT?7b9r@fpe0vA%LL<`02cn<*rnM3LQ)M6ccsJp3 z9tg+2dx-x4sr3)olv(u4!OWdUWg$dTnxsl2{{Sh4or{g4sUP>NaJcIy*pDaE*9f+d zf7#qe6a6=2sC`AuH~cw`^r3HfbI1Ksji-coIw5SQi>+B29c{Y5r^K^^w$>q~5s!AW z&f_UnU)Ftxjx1a^HJ!h)EmkX=vLhlT+AK!eeYBwXzmD9F>*BKFtuF-uS$zHY?OV1^F z?%WKHEZw$Y8ezOeVtY&`OO2%pD#=yS3B<*VDEchBC!F)`3Gq;es25bd<2^*xB7lzK zGQl%0thDe}Esg&Gq)dBKsTL=0mj3`vaHW{{D4yiLG_aJUqNHbsH9Qj+v;emN4w!Nh z2xihr=xdKJRZ4s{YB#Q6;%hovD*Q`w*qKs5sVQB}fU0!tt|PLpqz?5I-nx%i`&VLr zGpQnXuMM4JMGJOoZM_~(T{@JqYxE}v*wheL(z*M%vh_~?08sl%WbcvXSxP|3NKi-~ z;sC~vVIcEru$*ec+f@7t?IqFm8eyZmJu|aSV_&l9g73XO?m(ogn?+3V~y$kWyE;3(r zu%sxOWh# zuWr{PQAD43;*GgKYRukyKI0+#JQuvB2`EYwNFXQ*SEWWD+wMDv=Mc@sHOY43&U-(b zwLa>EWI*5F%gS{sh+SNk~qvy#P^x^Ba&9QRbS05C>moEpBjpT{YqsdctFys+FNn{mHzn z*NCy=#^Sqf(UQw3L$?U2lpK(;O08kll+@F(VyTP3$q6LPUJ2l?d0oX7%weJc!kIDe zOS3p;izIoCB-JtGA#NZK@<_zg_RW({tyB%I)RzAL0GCj;f0ZPvN>={bh7}x9mXMba zln`s!O>mQNHT~qH_nU$KSX#f#*EFe5MzBO;Q`tKsLbFN8jLGUSyjyHmy-A$i!I1D` zX`D%&OZ4oLG|S<;^D@L1~8#S@^rR|IY)kxN+#wb4i6tExX?!gm)W zb5HHDrRRc@_P)e@*)6I}24xC@mR7UVt}55rBTg#+0D;><&jgJXf`Ma^0ZhFH*9nC- z00=480rwnMFK)}urE1vw0S2vTPDht{5z>6NVy#~r?2R8!gp5Qs7Ch^!o08_@Hha1P zl0O3vEUi>=)tafzRV0&AYO3dnVL1(;Rf#OapHeEW7R~3a!?!qnTtj2AMuc>{pXQXL z8DTHE=BUtulC0dg-1 zUWIW=zZOb{r7O(vl_U&;snpXE+rG-%tV_%*A8WFA#uES}u<=&jEt1D+x7(*VbbfPC zkd(D-QuMQ`nGGNgmBw3hbm?87T_=jLg4!@a*TE~j0%iFLIYx{?(h?47_F=i5v5p$6 z5Vdst{nV)v)YYp`%cII90HuHdQ61!BDocXyDfD&?gwP-^7WGYSYnrmKq2Nt&1EwCq zA!Di&JL^F!Xm}`sacpg= zJk!sQu2FXFqI&*qHyO^lq^>Yn`?aSerj^7lYTwL#c`IJhz82ARlTvvoQYg-q>T&4S33wIN6usVhSMDwXrvPXZj6&R**1obUWOCeT(6N=$t2((Li zau?@>tw{kyQ!av-ii+y5Gzwx{QX-)!rMF~liP2!rk!(xyR?-yX&W&zrBRtMMHeO~L z14LVWvuh~ob81tqixr|EqYzqYBe5-{eV}48OUb0-RLodwc)k{d9Qg2J!`8gSE zC|5ac?X_y_*LjQwRbnp|jj%Sgo;~Ml#CAL>thOMfG;F8LC`xKYX_&}Xi!AuXq{fpE!lG87pBNOV6~)J7W$vcB>2rwzWnJCc z_}g4}m>L@8NNp=-6y-jh!2AY}V_Jrz)}9mkakE@RT-p|bxl0)F6otY<0(Pf&E%u8n zZWr2?#&MNZ8WEmHGl-AE8s06aR<>>@I)Se>3cGr#iMprl-IuyAQ{BEKmsk?kgoGgr z8qx_zX(P89CvD#oWpfxPuusfgwak!5Jcp>2Ltd2XmTHE<8320YYog;rDkkiw^8wT* znI8{+Jw!cW7ed`dNOdkB;v@E8Xpj;uR^y8b(!DA@v=~Z6=gmq?TyF@st^+eXm`)I< zSx5mcD0ZIwAuY2>sZS!kcyCW7mLHVS1S*yN>R0Or1~7*uS$gEX4qTU3-E;z<-qD%* z1|y_~ic;3~&NNSGFwr48?oGIc8y|P&q$g4OaTzVpCqq?lw6$;*c%sqxtfqmK6r-w1 z&m)g9^hUT1UF4s#rY|nDm`^-AyXx4=mTqr6O>@>?8 z#iihy7Rke?E-J}k&Ar2+0=*=JQ}An(jg=zWq=8ziMJeyD91F&cDx#&vI-C(FER(ec z)Z1w!S2Z`6zdRk6gOX?XQSd;Wy)7oHksToQC27Nvc6lKDEC7oe0<~!p*l?>q0;)co z9oLA^V@u9~5PH-ZIn6*(>V((!;bzTHnoV!ecU!T^b(hQ;^8wKI$++l*dlyzl{}!+cUOHJ6da(JG_P8ols0W zU~j=(vnn}z^TvI7YjP*|eigl8a+U(hjx~`!_ASB-cPP)g7V271ExeLOnjDBDyAZj^ z3)n)u>|BUs;ob`M+IKz5@A6E?xGLtuNlKCmNm^7h>T#6hEFz2==t}EsKH|8zE$OME zvQ|y+Z*I`jGu-lSsX9D`cvO1<#$kVlcbHt=Lg*Y;>y5C6KS%mdmtD%-O)HA}U430! z1S*srqY1%}F5(fdbHHlSq0Pm+?ruJ&l#M~bO+z4Si#|UZ#)X^bHx%!2Xw@d{IM=r? zqzh|~H14H1=}Ll7o}iP3vrdQ0pgN?sWH(K@CBlJ1zOK2>-0T>;2Wu9jUoC1Wyk3%k zT!s`gZKV;BSMO7%FMX-nGBw_Om7(AlJ$`2UtrVa3aC}YTd>1jLNp;Bq_Z%)fYgbiL zRC~>F(Q(Ea+6D9SdzZ;4Q@Eorr6{|pdDG&-OmqqTjmwOINuH}GNSSZpYlFv9MTy3d z{!7UNKXAo=wsC;@{{ZG!ugqM}k+!ctbM`8!wWlsE=`tH7y)C$!_8nq_?Z%Ojx9V!< zTs8BdmZO@q97pN0@y%OjmAAF4ct*0#9@ib~2sW z+&5xwV}Qn7AJ6Jt72EE}N4W9w-W!1%qdX5jYp2{FY4pMA=E<~gw++Dz@2$%Lk#@Z; z-$C&|r&5*I8>O^Y5N&>$#Uu?}k!5phdE8Gj(1$`_ zR9DuX^x1bFNxIrL+!e#0XHkuJe2Ssh5Jr-k6PN~v5*?kxT{9jel+~`_svnwd-D5s< z#{i?7p;Zbq%K3QiA?-%A>@==5i^#nTovbe^D^=CMM|1q%JiP@KC_$1Fs+m+buN;xx z*cvZ@@n2LwD;Wp70OR1Sm8Smf-GJnXX<|rO3S9s?bsyP`w&o6%(wKaKow|xqF1Z#Y zloF+Rh*>2~8W4NNcrC2%5(+5{otzR|xVQ=cS0MQYYlD04==O-AxUJkJA}bn25|lbp zY3!&t>&yfISunee7s*Zect%!H9}c3MD1P!1l{(4+Pr?qA*XzX7pkY*Y-$(*|@idut z9sRfC-RCuHw+Hn}kbnY`lz4?Sj--lWr@UYdvUg7vt9EYx0IN42m}t;fL$3RUhih-Q zyrre2ke4+YbfrgTV;u2ejc9*UsJ@0~a`H&dmR5=c+#Aa2VQNyXGYmDLEWYkpjXgYA z(=m8xt_bF^9nHHY$HwlHLTb6c$@`XW;l0SX&XQ%k@YCk>6dBM{9Rq-|Y%vD6Mf0b) zx!Er-3*Kr;d0doh;gc3s@rL3G(mTj089Z1+lH57}0C_CgHfHco7~Hu&$l7iD zcG)QmN|=WoF}1`9fij4pu3iZmkODl(t5Cmd@KHA&f;_{~9#mCZ~xBfxc@GKU)b5;dOyFzK! zrd^zK!s#c%LIp)fdF;e~8wGOd`is5QZrwZY9LpapH{YifQ>NXLsS8({baf*du(tp% zO1Q}NJi5GBtkPb&U9E87tc#S%EHQ0JRSA9PQqy!JKZuT)iOxvaEu8mi03}#9bTZ!L zV}>I~y}$$OE4G^-(Ek7zS(~(7xAQM=9o-m*?vfmN#^Jj1fF&wvC&Z9{h}8Duj$b!EtKpz)E-uHJH31~}t3|qGn`YI#w{AdT5n#NS%^)QyZ7s5Ngn>!{ zOtC=3R%^zWYD<>mzQDA=%E|X^h`h~;XdAR+koDhd3s39Fvs^`}%K6ueZ+woas0h*erB=z^$lzKrlJAFXoY?slRKxH2wNUb?j7 z=!+4il(va!MJft3=qcA5{vt;@CRdmrV&x3oq-?s9oSaA2s+Te_T;m} z%j&=cXa<8as3`?X{6w2B=UAa4& za8NYTr`bzU(0$~AgNLwj{Xb7yia8s}jU#P8Pt%f&ult*dt;keOhqi7vH71~6uX3A9 zi|ndnLIDZy%|`@$t)_uZS=i3uSDjR2F84NAs@+NwKh?I70M88^;HO&$NTPo6M>cY2 z4sOVspO1Gdwh5}EGh0YYj7eoinjUGuQm_fAuS3%r7j!!rw)c}7+>|0to&@q*-oWo} zcWH+@#zN3Wqmr;Zv(22P%-2{~j#1rjZprqj{{Sm#^IKu2Vnqw{Jhqx)2tq+tYBZ@d zsWceWdv)5(HrBBg*!aY3^e%Aj_jfYep8U^v{-+Keil}XB2-QT4{x7E*GmTa_Bq@cv zN>?3`(?ZpkepJMEO=x=?f%T~Az@1qDH2%CnQEDif+oFoPg>)U*0*V0(N&;#|8zZbG zy7;<}vmT{V9c2khbvA$Cs4*!4LU&ThkSwd4w$IPl03h;^DT=q%32*aP>mh zdR3d1aC%EdI%=TMu7D3ty_mARlDYaDMz!5Y1{(=snGY;Bm4Xo8?;1{EQiYMgQf8Arcl;u*Dc6S^@Wg0qGr?6)C zh*2ikb4B_B$gY=~BP!4Zj6vDx^`mD6VJi4>>w-Svd}s>(^4$~fIJL8aC8y z+(Eyh?2a+GrVjRBo49~xUd~cH#3jTdL36|?Dx9)7%kua!aeLHT! zk@<&`cXj^&jBKzY-W-d!W9o@2+=jfbni{KBtb9Y+iA}G8w~Fw%4|zuSBY9rkVpzgy z8mD%IC0AHAO-`eb_9Wv>xE0ZJusl+18+_}{-ZiS#6;fu-aY>HA_Elv>o`3*JI9cxu zYJC(X>O$QdqU$>X)x|q@)fN;RdYn7Ab)mHWA$EMarTI#9f9UO#M5nji*1Kzl{{Xyn z?r}BH4nx)HR0I*tZ?lw5hwz{!_7EcC>e#@+3_amclx=+*WY@_&YeZKp`#tejLO zYUfULo#`c8g5S!sV&Ar&>1r*()H+P`+gy+LRa*6UR<(AH%xJRg%u_cRibz3g+oy$B zA0F%ONp>S9ir|SObqUa#SJS5jRP9`iy1$v^Zu{N-ZOZXVo*gL1U8E1Igh)rUc0zQ>uxC4ur-W+wi z?jVP!rFtOH_>i=+77w`AhZ)xS&bkZm!NGKQX8U~0+zaIW4+Sw!`dC+!RJP}n&W}yP%B`8f8ZRbKk?Ez#AX?CZk5INCcxk&@= z97hV3X)W95kMF!&2Vr0Km)m+ni0VTGZ1w6nNP4P%2VXIx9Yh8kM#CUhgdYOP}wt zaz@WI4|J6lvZhO+r(G#Y$SRelB+m_0YgkG#F>azN?X4?QZxG;F%Si&1&%X#U`q4?l zN>No$PzhXZrur6u+m5(_tF=k3aSxdkVOy_fErGiZ@Lc+}Hisq2d4K9sr5sQS?FNr^ zZyDE=r%;6yH1;(~!lOs((#Ta)%(NzU#@B69+1%xV{ zFxIpR=F?DwN;=a#1$EICydH$gX}9o}41&;gV{DQYvZ&pLPI*Uo@cfe@_S=l&74w(^TmlM{j-s{87ZKT>~OT zY1ayQiK6z5(t1;>df^i4A_uMXj_e(-q98O+Ev(|BqVVBXk@Ca}Q?*!Cr;3Bkim(MB z?HMTC78f>ZBWk(1H4^Pp6lhAc6fLTs;>v@+4&&9JDlSMEWa}I_B?yj6u-s3Mzz^Sd7e?AW5&$Zy;?=#SCWvP?PL;ru;a~bMR_dOxVU@!{8YBKwCi~3spL7XjUQWFEGU}Ylv7$# z0)iysY9g;)JZoJ|ZELf@{2---{*~j$DdTz!VSz8@lwD2u3htxeQ1N8yEgV{Z(_|*?=P@FMhF{hR2?OD*rmIh&V2{hN@={TLS)?3f2|3SM=ya(l*u^PP zG$vX}r}>ywxgkyoU;f$$-&Ob2PdTrXc2X^0%k5ec)ReqkL*>OUN=VW{NbNYO?t9*- z=(;1Z{i$Xe=*S&Xss8{HpCfBJ#JFuM?nKOT1i@dqm1SNOP5b)z;r!4Dy*)} zI-qEZ=gq^P9(O>)GRHPw_=QZpEKMQQHyP;W8LRQ<_4?OAz? z?ETV3Fv~XT%B=fF9*asO*e#StW}_mMQ2WLo*o_i5(jK&~l%ym8E7q8D&{KClsB+qX zuUcUWph7iJK&3Ty;n1%no46}A=v6X zW2o>ve_>rC{J>rp56rgl82Hm|ZUats(1@ybpXp)t$Y=eVNbp?YzRhKS>Ee>Nvq9+X zHP(QVJ%XTO`D-St8_Gve7ihQ^FoVM!XAqE|D$^9xuoWj)>&8R4UDb5+>0M#5q4M#u zk3do-_2*Q|T0?*ed`G2lm4j$g+~kL)F`E%~GekC4tn;Q-rZY{?h6075bVqHsGo2fl_W&r9j`G?_mp(oB z01H6$ziKgca6I5Lfl?H)sP(0AU&3y>2Scch{n3u|943#c49))lhZO5dUdK}s(thZn z;uEXfdf^#y`B9*Lic-@zw`;6gq>KIUha}D}-fR<{c_N7jbOeFcl){#_xpt3n${#P5 z+fur-MoHwGN}a%`QdeZTGv<6>d9U^2Rw(mBi+`!w&SIRQi&{EJnu0VAiYMZa3G3~y`s3+ zA$jRsrE}6{>aR4Pb4+5!pTwJ1SrVEPm%9!v9a5OzxE2)?PKLsqQTgVGB=iJ+k&AV) zqQ~+O2fYIx3r#A>GIZMI!?DB3s)4RFh`e(C(1lee29icu2_(tR+gzuu$T;K1J-7 zfK_M~J9-&lE_1+9bo)bX&st#ISWlFrdR#Uxn^>wzY0qe7|PJ3;CtkkrNmK6;NOvIMVWGTv5OHDXt zk`7p}#Rzv zJRL%rtk&B~sHL>*XL8JH&+5fW_EKE5{6(`s;Gc0IGMOy~(6q)Vt2hxf8XF{ShwyCtC)6rnFkp#=t{k9inY#>>o;q#?^?g{7%^6)FX9l9VPo$|E;#Th95a zkdSIuUZfmoM{P0DeG3VXxQ`b;8mW3`Fus==gO+vn(-mQ~=JMUs z!DvkGU90A3ex*Wwpj|i3)or^ePDXBIBq5C?r=cWe?l`yXcmz<8M*^~b@?@Va;B!p@ z3OD0zwSK-E%m^?|&Okyavr41|5Ryqfw8Z{@B?L4sRG#1AFR!|UV3G>xM;2|%)w^v+ zV{%3o`GBY?gefW^Cm?d|$3E^%gCx4@X9eBc0}VVl{{Sv+4Ho-Pi8j93yMo{6XfxC2hidTuDzrj0|mswCymevBg6 zz||~~X1J%LI@1aJ^`%K}xeAeocCG>7l^>E}bS0ySQ8kZcU#-`OmpK8U^6u+#Ad(a_ z%N9GVv~n_mX4tK{JTvvXf{{0cn&-K;!}jmRhQIUE<|T(MRM_dVokC4noNkOMwJzlRFZw;8g9h6we!zQbzI}$ELKcf zG(JJnsl0uUxMw;(PFn-bKiR3!la_tB*QLwEID)xy+h|@&?{&c+hsWEEtFemo=*=xC z)Z@Nj$R6`tGJa|zC>lzYw%NB#o8EW;dQuNCZN>ipvQ5fm?zqxLk@k|sr4FTTIIT&| zDXo8a^cb1h*Y0ib0fYn1R{OcNwA5QQ{~`Av%UY9F&6s`XX`3A%vt+wlBHSkD1esA3&ZWd_1RTR<7qI1kyD z8}mwljI$PDQc{-EN$l1zE+(nNO+tR`%WN;kq5l9e+!1WG0t}ftONydRbbx?*;sZF@ zypkjMin(moS~l&jx%dLJ`_qy5<8ZNSffCR1_KPAA!d#5PS$X!+N~oz?i6^q78Rj={ z-$NUTbzD~SoZlE*iCP%X;kQp(5zD_3ZigDO?4Cb*%OU33ZanESmg~+qJ|!$vq?6f9 z1+|_1v~GN^sZJ)p!DO7+t)l7lsTXS_G4G%9;_cJ{*3#7@&k4oUw2w*64@QaBmjF}BUHwHDJ2%YWii-clwQDoT

y7cUce|&kc3TL!Ub)iKHT5`@ListrG~yDo zO-^Q_kO%$TA0I9C$t`RIQ^X~5pMqI{UvwQ`0;%7q=Q!5gF5kI0!e7OrKYH7_}b}w=*abf;!U1h?W@=SLx+cs13q|0to3zD@hmRU*zZKRs% z0IQ(v#FH_zv4Om3t5Rm{rj^fdrAi{Zrq51ja$&D}6H)aXDe~6=B67Aql&fr=$E9vA ze2IlBA9g&mP)E4oM@MtXEoqd;9w_>*I}I8PXXZUa<3T_Mp7VsKg5lhhlQLM!=$GJL zb{YT*dTJBZ2SP!wxST`JWpGseu2&j4m2G`C%-S=z6~XB$`S|f6xTfH~k>XZFWI?GI z%s1V=!($ZFRnvWrxU!a47kktk3hEyW-ZrJnZK2=7BHJZLaZRnD%0j3!NfjB?W1Kf^ zcTASOMvK2YXLpX8-&TO&RDaF#-QOx%YDV~M)!hQZ^qY0LhjD3Y}!S%qG;f)EX;DCrUs)_@`Sn@00?0 zt$zODkU-)GCB@!Sv^hI@?z_-fdM(P~B}HJ0fE**R`!UwLV(7(n?JcJm~E?}>J+GTG+X}E*fLADACQXcpprpgG^)NHk9{U_=PvPTcb6et=Ugr( ze5i3D#gt>AhGMNK%mAWPqg?y3i}z2txA^Px3;;9%;l*}G<)8BPiOyh!-Q$biYf)Yb z1m)eoXW(yBEbYEMDw%Dy2oJ-wNOexQACajg9%saQ0twGNYCBIWK4We9ms&f8S2*w7 zO}^OQZ-*bNS^)#sRZBT?<#W37Ci1#oBm9-xb;ae+gcYbX;G$_>(LfG2J)s0~bIm@)SbD)8;17t zLF6uDp@6CptRNs)r+ztOx{wL7?O@~=)c*j>CPdn8Lopf>tp3Ejrs&(Z1;R@8j)$Nm zcVi{Y$K}3(bk@v zjBXZLI3AL?)b!hgxerfvR;hE=;eDY|?N|V0aR;~~2G#zhdF}I@GLaB} zczvtrcFSP23q7@^TUkQU9i);PMr0s&jBv(6`e8G*=Dyuvu=eh=VJ#Ku3z+`7UA0)Z zMUvlcZ5Hz?64x!ZP?T${l&J}z>5h%;(`1$s=W4!m{%kLzw#W#Xri+yOg-p9CXpEw^ zfh8+B9-jPk)r53n`O}?vl43$?r78xB6{+co3aZDKs#}f)7&57ARo2Im))@AO6(>CbF6YkcjiDka7 z8w*rtJg^huRcLbVt|l(={$sc*jw?6h#%Og&S1a_1YEgKSGw%1{{f&T)L1r^=oYa)q zeriZ6E1($~p8P-Z)qBrVKc=f--CXy?7E(2#`}`*gdnp-4QSAQ!y<<7~-WX9Bv+WiK-!i{=EWf)q1(+Gn`AYe~VbWT-IO+*eMOHgA&-LTzJPiSZ)V>kq zU+*~hl$QrfZzV((DL~{pj6HV+5g!y2Yf8$BfvSnpk?+D%0HooN^H;0#>y_p6F7}AO zhT*rLNfqm&Q%CjV4)1JW+QjZ*%ddYbUiHn>JX!ly7vISENT5ZtRS-;jptd_}v2wuz^+P{%iZVp;zHZwH~p zcjx7vPy3nc&7gncTo?7hZx*ju-m9(ZN0z2H?ev5ZN{8M|k@bfwizeB9Y$N{wm0hN7 zCV!;R$65Ls|VWMpwE++E*JzNtAod}{u0LTH; z1na7iYe3+O_WSUHaTf)g3Q-K(njK)2>quHEpYnzc38S@5vS;`Nj|Fc>7H;cy?a(HC=wcUUAYWxG){%HY8nd$2tPB+G-~khhPg zjq~_4JG2|_-W z5CL7{DrsK0hBjqr{{W1)!|h8@{VZ}sl7|lA+VRlg$xh$nAa(8VoG0W_dMo$5`=jms ziPZHB&yv7~Zd)#Xo^g2l!z>Dl)~ceJgkD^p_QAHwZjPYp^9nF$JwEJPSn90iO;XHIR8j|1+x1~;C|iJx-eI__M1r6+fD}9O z!V@ClXQjmyKy@b|o|udM<*GM=27;A6e|H2%SjU%_vE}9Ld3k$5CY>qqVJGoM>np@i z>F&YE)fH(wrr~!3h==iI2yJ5rxc| zDa5rNBS=$@y7Q|DLJ)*hsY+1+DH)TN3nRl@)OZAq&C^)YuP3UXNNxIL<3CO-+|9nv zDDre0-NTTgDbl#`l*5q~8Y^f7fTmFSONs}?s{1hyiOKxOSD{kyn}S=Lmo}7OI(k(E zF_x#aC22}lpgiRoApokCQO_aN&|+wXd6Co=Lf!50ZTCsAA>7%$MTX~=)#5O4Mv?#r zTYhR%tvS;$FdZo$Hmg)R6%*w?S)^%MU(JO5>Z&QEp5fGCW*Q_<8lq%r1w9Yff+rn8 zOT0F}FLahk%3OC5Pjwagu|K!n()F45HZH4AQC5YSzbPS3HZ?TmSz3>`!f|wKs8>6_ z->6XW;Lba%Z%L#X_iQ?4}awtCLA3q-2z#Eh%khDiwBn zaC4$!zokugi`C}k!uI<`$%HS%Jh~^dJa9_zb4lv=s@llS&u}rw%6*fG7oQ-0R{L8aI8X`Mwl$EP2fFxqa zlD&>1&`k+6RIC;%Tb4cTZCb%nqs2U?*6XodQ%ZYMOUY3}iWiX?P|%LJo9U=gOe7SF zrYKHc0ffSOCD9OZtc?>zeYgrIYQCOsj2> zRQ$RfXuN>4OqQXU#}9V_&c1u@8Z-rcr|csm#9G6TkpBP?&8U_8bJV&l}Q4X$4_qTWz+H&^S~+^?|c4L^2@iIrp7J5 zdu4KH=eDu`0L`!XD2-E!!f39YazC#Ze~DvksMTCfDYeb!t~I@FClt9C{xD0oO|?}J zr=dHO>}fq|kx3msoMc;?af`offKjq&eQUA%bjNOd1MS>@iw2|AtyiT-mHy9ntd?aa z8m)5KN)WO^D^h$!dx6GP+^uff;mj_%{IxtBG_f5ENeWv|%VIwv#D_g!GZylY@Q~WH zp=oh7t#j?gLVU6LdUFd&u-lAoOWxJ4PEKRvxmT_y-d0;`Qsbr~-Hh76{?b3*j=sd@ zg!Z0e%D-toDpS0hF}HXL7|lovn*1rSc>{EB8w5DWhVI`?nohV%Q%OSz(m|mal^Rea zcH^LTHsXrf5a(A_^V_!@4*vF6G!fD&s^;jls?EE%HKsHK zAQ7G_Ic?C^Ubfzvm8W2PaU|GxUsQCCN}+DcmC=MmD<8e5rmt=^ zTUeTC{&ZMH<=m}tjGuO@9DO=UNXXX@{KQzCipq7@Sw1sFk(FzN%9FPOs;zpLn$s4$ zbQG$pbtzpl%$i~sH2(l{i={g)YI=*69=1?5b6LuO1fKeniZESQ5z%QKAt5RQB=s0< zc&RVls&VgD*B@kUwE2lYh^|B4Sgxdmogkln7irRpy@~g(2~fIFJ9F>CVT#sD*d!zx zXIu!-XoP{{uqZkV8N3oj%VecB)~=nN+;}u17Zti%%e&cw)eyM~l%#t)SKEgaMnWY4 zbq67qSn?rusm*O~Of{W!1%F<27?!pgD0}x+$nXS$pgYP0CW}?3fSRkJ@553_DCNa?AqoT< zpKd$?C@8X!4@BmBaFg3aaOV}0Gsp^_+$4;{#cB{T{he?N&nsY>lcaU*$Ebj4SCoL* z3Z`eX0*ykP_87`?+jt331z`wCCY8>#!>CwJin*7Z0xF#r>vXsl!FAGO`EG#2pwQJM zP=oBm%Na%mO1ZgglJt#GtW1(-{QTmnYEsJFTIy7yO-)a39W}%xt{4j_G)M0)#hr6; z<{Qw50!ipIBQu3S1g5sEb6KhZOH&af#C?QJfcT`iZzV1=>t`Ysg;X3KNE+srxKtE) zi4q5}mbh>uLKSP|4^7*5b(e0uZz;XId0kYkry<3NuDGn}8ReQcUc{`^upDK3lea9J z-?lV9AF*zGN4!@S-@?BgZ2RMn(mALCKK|ssB%l1Hylrp43JQ;zW1f|v#?ju>gc1f> zP?Vfzv1YIFj_8yc4;8K4ZLC&qFFFSRBaLIylrkY;C(AO%j|3WDv~;z;uO@&i9|%kaUm)IWDP;exVJ?xcLhi!w2(p! zxPq-K&-g&Wi;7w)fI#bKy!}V^;k8O%=7KbbP^D1x0O=INrri2hjNxa`N4wlGSuP6q zo)DgHv6!k)ZX1^ph#U_Ux+pWR#o2&FG60OB(o}M#J{)+GQf6RL=G{ptP&s$u)Ne@_ zrpEY`e+y-_sDR?=ql(sX+AG%1XYneUdj{exdxka0fTXgQ0@M6KXep*D(Hc2kvX0KuJmgQOP5*HN)3l!EVaP;L+(cLHJ$O(};zI z-l0@viESZ8P+vj?DoaWLeI#MMzU{|6(XzDtA$m=nmS6y7K$^d)h2!Wb!d?VXBo&UJ zg)s@f)hokc3SSGTSt7!^CM z8HaFkE2&dtp=9Ji!DfU!YM$9&zBhZk()%()-eQ~wSx)#xzpp}IRb^0*w(WEz32c0eRG%1w&0Vy@7ekP(+ z>#j<0CcFysUlLI?>QDX^KerTEh#68A*4athKowhza$QIoQh;mNMl@Qkesewl09s4D zvXIdTZ7U#Dk|;2(m<7p(Nu)+pODPU1#RD8FhcynLqZZh-V$5#Oys;Ueg@UO1M{ZaU z>aSah6u9(~B9{DvREm{nojPH=8VjhTrI?23qHCnql;Nu-y4SZLJ(#uILOND;eRO(? zXG@aUQk2;!Qmavi!GTupZZ+bY%X!5TlCpJHgFNuFV``0(X%2*yuv*ffE0M#J(waP} z4`gmD4T9#Wm#E5~jTB{pLK3Rfld83@Aaj-zTr!L_R_&#mwX~GFX7wos<-c}3)y{Gi zkr7O`!ecCP~J50-q z&6U4xGFw>82Fikcq^A(~T?}^>f5y!-+rp{+W1AqQ#u>LCG3=fi6lji_4$MSd1`}GV zjxy#_3wn5?4sF}3Yg5renW-gBs-vb09!->@Wp}ovE-$45P_u%ydvFp`t0TFBK;a5$snbM37H)b{?tdyQn!DIMz` z*0u&?TvpLTbPmW)KxIt&;uW`ZJk_f}ILv*x0q26uozb-|Q5}nEN?%HV1Ix;%ypxYM z!bu&}?kRb<1++1i#lbKYxo!UdGP5QzS{2bH#ZVs1OFGTeqN#p3!zD^lPVH?jHr2i} zFzb_#fl^09*9H7A(_K@uxW`Zp&?n^Eg_cgiP~ z&3MsO+tr}AF(_fwyrr5U3GowC*-UD2UNx~)_%2$!#5vbgW~7vUUmk83FILzx*;Tq+ zhC-PtY}ZYC<%Z4N*3E>|RWf^jdh53J3MrOf4M@C3*~-07a(XSRQe_a5oo%R$y5&KN zBX^gP)*H=hw_T?frZ-G$(x(lZuy)SPn)B8e>uGXO66%zqMG`@v#V0GCmgsxtt7h7} zD$^PKp+Z!m%YwOspmi9mO0*~)L?Y#|!nHc;QPj|oG6UO##cLzRfepp{P#=8JDn*dg zmQed7EvGX=!KfqIOgDQAO$w2MX2{;5!3usAWm@2@_<|XMElJRl5ZaQVQiUiFuDCVd z#GBc(MZLyP%EeTKr&^Ll5#OE;Ot>>piS>5jwW?H&;=cD9zeBQHl60{ZJr%4n_rakek0~nA>G#& zY%Uej+_jEWZP&abgB{r@$;DYJ0)ou<+jK3Gw#t4NNjSNiXwM~+w=ZzRP-svaf_u-~RV(p8Cm^JJ zs96hAdUj#PI@yK#gvU3QoWNXOYOBWLp7V}4COWlNpwyF6qe1nYUToOLHnbF2K5p`R zi8@P(s*5)zn&D}hAzk3ew#(9$i0-9LuR_IAN>xLc#gfY9lD4fi3p>eLNelX3C{tZg znk1=hIJ$H?u=Pe&24g0=LP)P#<@)jDC`Bd|{ z%eM^-jtTq`1n}GUGhm#>#g+L_MNnHS0;)=qYf*+*mf~iXl|6;YI?`V-aSD9hoRzjW zFAv<>pFVu;=($b&qF#Kdk1tYncu!~*%;JNG+t*O!Dk(ujM9$l&7}#?&aQhMmFBnx4C${%L*}V?P(4uAXQY>QZn|OA(6@3 z*+u3_qe63r!`@_P5X5TZ=}wL5!W@~$klDEJ9lC9B-%?NA-O=>_0Q4FVsg-H*gVJ9*wK(ArJS$bS( zq(1`kqw3HW{vAk@OHk{TIox{lmvxC2NN0w*a zVM=MI)CQ57nUkIP)gA46P2d=-)4?^2x2#@Dv?-p1VMwOMWp zdD*t-W47~4a5Q{JSg2NAK^V?AziqL1?Gnt|xo$nD@BZY$mqBYK7(vjcjkkL4o=4@D zH&)@hnY*&lh$R#;b6O-XBvG|`VHqng=mxJ9`*^S6&nf+5e+LYDh zJAKU@mA#-`$ox^c&WS1}psh(ss6E6SSvJMgXCB8h;i95%`x!qLm6Ci_Oh%Bl*=h^R z)Rfk`O-5bVc;=3xR9&NpU}4pDSB~BrN7F{iv#P#s>wZMXLHL3TmCNV@JaVRbKby@x z{F|csfwAtLi`o#|^LVedPdr6_Loj|_aolO+(bXrwpAY`zr!=zGg_^ddV{adf9z#cr0Y{2U9t)s%}8am>g>q?V}P$8S=>D(ec8oD(PYZJV;{Mp&0Bg%;O%+XH`BTzCiwJHyl@6iR@{wNTQ_nU6Z)? zzT$6eY?~CF#d)4b%3EVKt;GUpNhXO(M{1Nn9r)KWo08KV4^++o1uIHV}0!=k-vbHSS$LPsR`R#Z}e?vOK3>skys^3q*&R|C*8p*iY%j@+E` zP}C7{dODOq3Vjy$l7Un0R#?OP|HnEwdG++vi^53d0jdHj0ZqKwY=39fd zBt0JKsHHE>e66n~DW_s+YhPwIeWTk;$Y+V}99%t%jQg$mg~i?2TwBEN-J;_ZN8Wvz z1F=X6BmA6o`sVfW8l>X)udn%tr zJ5|yAzwSwm>KT}*Ag1SQp3zTv2}`se=Hnpmb-i(ocYzD6e=1o3+`O#&%qpOA@+C98 z_v>FQ(?2VBydb<$Re#T(rEBcQowDe0(f{XtQL9{?5VGyOQS4k@`S+P<#QgHa|Pfhtf6(sg&>94ZoweM%>+;DBhA z4KfobQvrB4l9@RcKfQ zu(h%~PH3-^JMbE!^tZt`cS<~~THax0mk_j)Q<2XuqXoJIimP7N3=dH|ufC&GAxipB zb|K_>S}t$)Nq zN&9e6Eq45(2+={3jL{&-CnmV7S5mK$0y09QhHD(1n@;FF@ zWuGMW=YU>9p}wMo9?$n2c>)bBq$KK13VIWPsw{jKX_ZgWfJa!TL*3IJUO~{65PpM! z`s5r765h+rz`VqX<{S0iV1hHt>&Xh&)?-?&ct{vDGNBmE)4}JzIDh!^HOtCzAxF0 z4O^P}qQMAA6aWF)fjS`w%P9_m0IrczaHMFGXp5AkDg|8t$bC3TEED&QAM&@In1i4# z%_p*;q>M}9jU`nL^%ZO!t$9ghpO;VyZPjW@x)4FBt}e_vE@*k`cULsyiD!{5@RcXa zK>5|b9798m`u3eUH{1qs_EqQf@%QzS7GBu6DllC3O8 zX(etgR3?Eb1e}Hn`2$9&WUQq}`fPXb3J(9TZg&K26;qIkiLw{>pqa@!9zZ@$Q3 z#VQ;?@ez=Ps8lIUrGL6|?ZzF4+1V-Zl0HyCB(`xB`Opq zy(oRy?6vXug}U{jO^}r(D+fxbKo;DXWRP#^HJ>M7IiMy=Zj?F#D%C|a| zNNEdFL(KGBYNu4F8XDpUJ%oz#E|-d{-oV}0SbLu`Cr}eZkw)a|!-?Vz-LKvC~C(Ou3emp6FZZ8xa| zrCW8id5T%gv!swK+%b&qJ7&WtLwT;x*mvgg+s}6(q=+|X@0RPIlP}Eqlstlvpmc(T z4$jO%TgT;GFrO7~yr0j;&C{-ha*v3w*G<34_KP>1ufwF?Lsv)uke5=KMFe~D%N;4X zEhC#1=2E0IFPa^>&D~mVV+Cl^^HKDuW1S$xh0MH|%r=a8g7Gm?;ue&ZrA)kXN|L0W;%ka# z{lMKxGbXnl@GD*HzVxt$(n~wT?l>w-y(~LSZav7vd((@G+t2XHcU!o^EkwNlSf_Z2yPi4>}>w#YQ@)0Qc@nQB*G_IA)V+N!FbxEe1JMTDY?KHh^KK;$}w5}wL-W5}y?q!V2PNd>i+k%3~AL0~nP$;Fm!Rv>rI;#z*uY>KwbSPA|ND3&+ zzXymf7a@{?;ZaQi?e4)#%3XWQ1m)S#_F#+ETKP)=)KM#HrBD;#)O*5rExITy~yrhMS9P#ZD z$qC(Pj-d(H6bO#Bc#3JO!UiANw31anK5F1Or=8rza?b_3Y_-T>D3@7KAf)BgVqt^9 zQ!Lo_%1g$nG9&IZ^I+2=Dv|*NcV~qvmbhRoTg&_DuC2OxWPk{u7+Hw1=)`erZC3Y! z%^xZOD52A*u;Ej|BF2#?p*9?BDxhWhDlnM}O|GH}?f72iwx+|qEe&qVN(bDrBBAyP zO#+#HJ~R>8Lr-QJWHGkaar8>wprmn^msV2wwy!a}(ZKr00;?wQ^o<_!9h=1W^h}hW z7xI0@*5c2vT1R`r)4srTs~FDW?Ni#~`$JzxHM?z|`1E_1?CJjiFw}h`i2XEBi(9jq zcy{CX75*HQqB+uRmo`)s5A897jXN~5cc{wsjdae1=YWq zw@zkbDiuquIF|?5a>dpg$X+Vav-_NE(3(6DQmP12ih{KeGEP0$;F#)!rG6pq!PlJX zppvnWu0plPfl4&h4wdGCT#aaZuq)1OP{?qKiuJ%GQG_hi1D<$RGJ&BscY4&OA+}9M zwI8Tc75u}fD6}o19V$~!A`q7Xz{vundrnxYxwKjgWm_H}h}?cE=KKAA^X0A3ke;8~ zzNZ!Siehsa81ji(=I6LBy<-#wxh+n+x2E4hQj{|>sI7zXD7cck{g{rq9`91H;+#Cr zlDhu@`Cn`fGu|6hcV0JE(6;W4%V?U*Q589aN>fpsXIavR0Z6KcTzp0U0Q#2q3w2{} zVir%4krDf~r_{dHen9p<0`CnZlQE8#yy;ch#qoy(&9$xt$Ai+=ZUC(#hEL^6BHgx)e`Sbk1Hpdoherbuec)x7G+H4}))7yg8 z1s+^>q`0+c32am(=m4k>W`qE3>{`~! zN4-jZrALqywdSsaF-&*b`M6zDx*hpeS&iG*=ci`Ukh2EibC;z50NyN9bN>KK5Zk_L zSh*zTRlxXE{Z##GY38{Iy?$bx<8TVr6MmZ77ykeu8KzJB+*$U#bhT%xb1v|GXL*l3j|*?^^1- zOM`7%JqtHi8&U{Dt4`We3fidLk>Z+8Xh~5ABio)No5fTL0Y4Bm_T2 z+gFXXrnFW0l7xVjM<-rEc+#feum%EMN+ip?6e*RE2ug(tYJW(=wsU*QmQK;dgs7%W za2-%?sroT&g!`*4nlE^w?XXI~UZdDX6p=TN;!Delw=M1lqqwqwYQDfQNKGE4XN}%T z3J1F~-<;x9f)t*$KH6f>4+WNF1sz1$TP+pnF;r4|E~mE=0YJ>!m6-NqJhcYe%fC?l zcu>lK+SM60BsK=L)m7=&^k9y<(Io2^9vY#UwLtT9&1qo?9TF&OPi{OVuL6w~fN#VW zrUWKbOm;G-?`Mw_qHQ(1TcU8YWtPNfGd@T*%*y<$r3c5P(9;YZGk8@=411c;X`xRT zzm27$lH=^ODO4n>DW}nhd83HBw5qq*&vP4S38GSA$;5>Du7TaDSa0RW;HCazW5AT+ zgyhhq1wQtZN{s&iZXxcWj=BXsWpgm5rB9m<>9`_?+O;&}iG2kRq^l#_(Dve;l!_|q zwvDBdxC|kw?`pbVA)4M*sR7! zN&*c#2~9Cdxs*cf7QO$2k65l;Yf8z(LB4}kvcw>niUXT?srFDTP+skN7Y9b zw*a8&kOX4UZDhdR2CFN`W#y7hPKo2TqiqS1QpEehBTYy?V}4-Llh>)lP2_h$-ssU? zuooED$I|!emm1r9RIO@i=F{mF*XBFWR<#&r>27JkSecH;6Qin(IU8!mL2aUixCILJ zo|y16o*^C9_7u>dqq%ExBaj+KnBiKMYE)ED(}KwyonZ4r9I%-BM+7ySY{9wiXbQAP zQxaJrZLL%&1sT+Zb;9>q2M9=a0-|wPHp_ksIE7?pLX#cHa`yzbP`1-gnIRgq^~j88 zk zlBVrecO|S7slP2mjQf*|U9Q}OwytuZWH-ITWa){7rxwrLJDTZxU{AEa3fSUQr&hMg zPz^E};wv>_1Wor1N}t-9OX*~+%^)Yf;MrCk#QaHjMqG8sbqygdxP*{<6c9ym18bF% zAwaJgOaxah4sv!uj~WCZ03(6dN1FW)W^6BMwR|@0a8tR@2o@ysOlxk0M z^|;_$jD^};k|QAbi%YaLN_&Xz!^rHTmQ4>88F7@jwYTb}Xro=ryf6EMYRh&FkhK;M2UA!}Wwd?avWUeM*{OphGmwg@A_830F{->H09$)39+VUvUwKZv6tG8s#VaUnv6ibV*h!Dq}_NZLGt z5L@khq%w6x93AMYB`at06~*B6%YEA7qyU{y2~PkAN^7r7DQS|&MBj9@CRvO&a_G`f z_j>Axy~lgxrl{Jt1y?GjE6T6boIn2NvOm5QFZ+3y93wu&+PiJ-$zg2|U98h>%q;2o zhvUA|2|b#YkSpxNi(Iv=?&)KJDk~+nau#Bc+M7$H%X5&*#%!X?xXvuUSFdNw5CrqM0}=FPxrY8*a$zX3$@iqptPc-3xtG$ z@bow`1U8*Hrr_cCkbhP@yo@rX6JMTMk73CEtR*54Q^vt?lW&z8TWeE}Mp_msGO9>F zs|;@75-_*LSulAQ+s@es?v>JxH1z?p=Gr4zA?-KY-gg}>2}B`Gxax+Kgn&xEymS8J z*|WtfrhDlaUGcIz!DD}F%Sd8AdDGUWt^WYLH^$Yl*zEV~98H}u2qGl*l+k)iYDbQt z6#$y&+gw!k<|^LeM?H=%6<2=ZZcDoxR>3F?aU7L(i@e>oxTQv!ra-ME162ZhY7bmr zCd4tr-U|z`-S2_Nq1va`*tG6V*#_r!hYm&QHm1wd=A;#+2^Bsg-f>pQ zr(p1z42`p{S_^yt*iwP}aHodoj@#_~(Yug}_-&$inhLNl>@bY$!I= zgy~W7Duj{r;YZ^fQwaAQ_m?{;@~2J*Wq>X#)!F&z0sldUOR+6m6R_^dkJ?Y(O} zR*h?)h1iG4PN78#KGROWTBZdJjiweqfG0Wym!F7b0c#;yt|jX4a-N~=%-pr$Rd zM^YA59m_`)r?(}OeAa{=T3n-owNHkX2}I5Zp)a6k5po$?* zYS461OK!e14wS3Dy>Vk%cuJ6?y)ERnhmuJL#7}p>1aVUKQS}-pJ+=^oae*xt7XD%* z&AfyFd_|>&fl=UV;y?bf)v`{ipV%_#1a=I}>u-BcUzH zeQ9xky&)w)Bm+*+4lsK@#=GY$E9$~)Mcdi0&Qs*X7~p42fwZclTrtL*%hg{i-rIik zls>f!j`P1cMSQysxu7Kp%BxifLV5~kjZ=E=JAJc_YZy3+G%j834#2$Mna93YXWLHq z32(1?#@I_Ofq66wk>>MnsS|4FcqUxfHdY3vbkH`K<+*cWnmo8Io5#mNxq5x&ETv+`T#`A32-T(%GE*{BT{{ZcdbF0)! zkFD znnH;EL`Ib#l=)^e4g?{q)yt^rmA+Pb`|)b+Hh%cDyHr>lK#ov z?H0v<4|vv{*tbig!lGlH?*gTN$tq&udegnzfzZw0WZuHl{7R2D3xn#Z#v$L>tB4Ng z)E9GpSngf;*=Hid_K(_C8vN$0=(c`hoZ9LJlwM%%sft1Vovbljy{zIJx6kUuGtpnv zw*1pNZ=d>eE0_aTkyDpuE`_yGE^QX7s1+SMapty&t3?A-L0WcT2u5Jl6jDk>2&Hqu z9Ke@bC zL|mW}q6J5O5UTY`)T26{hwZ>1saOaVJ>A$(%U-H7+BDGpp3ExKM0S+52>`f+jD~oT zwG=8VCGe**Q0M}xpzg%jR2lvx;kR`-P$>JR)v4rKQ;^nym{L>7YhCVZRo>XThUBIsH>IJa zfz>KoNeUgKBay@|;hE9ek~pZW^~GYK;=akP?WPo1v6y8nHri5#+D#IaAqfdeQU^c) zpaU3G@)x+0kjL5vFjaET^O)Ley!v9<+o=fE{FAz3Ddp0oEnK5J1dO}Zq~iYouq5e` z%_uM6ESyurRr1+Xm6TH=Rgv}_Y9&mtX;qSfY7^N?*8xouq3BrQNh$WK|dboiA|*NGf)0qPfZ#noi+JPN*$tU6k0`DrUrHEpr) zu669i#f{Td$2Yl8e6^|AxvezdksyR=$S1skkQiL?sZat3KR}rmd&|-mrjz$*Svd}; z>cWz~zJ+GT|Any4`u zdit?iw%IJW73PxcNnE}rgQ|dA?oOl{_nOcQL|f`rn{5W|1 z)ogt=-Qc&oZ5HVuhL-6|t4gS|tpzo&)ry|_wsE$qy7nI+!I2F*Rdim>ZnB$3>e}*B z>xe#b+>*K-LPKN((>%b&4+X@LT(!f+cUDH$Iq?%jNA8vLd%G+K*M7W24WA-3nGLe8 z-6(lLSL(;mEG6HaY{TLL_&2-k4W2?s_yFg9r7_%KuU2(JO)H5ujZ`BaM5|-j8@qM5 zwsxl0xb9`PX!wB&M4;KBP}W&Wf@%Tlh}-<-rL8>YhLhq|qY1V$Sq7I|#OH?bAQBRE zHQjdg-aOVrw!77aN?dPHQ*N^&I{p(zwOra?_o!|W*c3{dG|ea3Tts0n$#bB1_nGyN5Tx?f zUxNd71?M0~4oGp;|gdi(NWM1ZCfZx^+XQP zqBYwe;eE}v9@}spn|F~l7aN1InRZNw?;?o`E+CyL=ml}j9o2$Z^9+=@hKgxb-oKY9 ztuOYh*4|ijhpSp^&2vY3?RWP5^KjVEl{TA1$uZd_1Pu+kLNfPL8}1hC&b4d7)PT58 zaAI9$k&-738sa{MFF^GryEc~(TexqHt!-?pO51T;zjU%f?l*ZaD5=HkDXU=CnG$K1 zSf-BL+s%9D>;&->ikM0hp^WB48f#hZlJMnySh3Pe*Z55c-uTP*I+WvMy8|i+UqeM8?c1^=Jq-(?SW;!O8qp)EbbVb{D>4^ zRdxnOHf?g?b)s0{o3nBwse1g=M1>U{K-A`P#4hHA(U3-jD}QXVO}U7*+JnV6ITO-7 zrH#?H`Cnu1R^j}e>MX6(x8!EpNRo$CSXFYZb4l$v9?VSQ`;9!dj{VB}F`-mRsZv(;1fw(cw&enC|uzHnM?A zd$5#T6`KOuTQxEJaI%zyIy0Y!j&I z=yB>VBdJX(o@X8dRFs5(oXnslC`bf=JNi^&b_Uccz00cfXbmZ#86KZT6-^fEZ3QGJ z;--Gi+yUa3s=`2N9pzuU-GM<+w*%l6s=GhjFy4sWYhuVvbgQj3b;#fsi;CGO71Vm1 zsf3?eplg=OZilTsm= zSaK_>QWmce$PBRLg%t(GMyU0ZL(-c|$!$%gD=RAi(t!7jIcjNi4KzvWVQO27NbnUC z+tRr5SV$<3EteH*R)-Qma2gsD(zL^#NxNP=)P~e&WETTNQj(ITk6^$7RVUU0N?Jmn zAY?~D*9A}FGzgJGH3LmLO+ohIuTqg6B|WV>hGo%~_WtF!!`}O7p+MN~@zF5|N=1I< z#r@+)xTRTPDXrnSZ%f)74~l6l4}slqJ<&hKrk>E5w+~JpnmH?2v)?E1j^k0FizN!X z*j!QV)=FP!SG`0GQSu$Uu$x$qL%pF}@%`4%iBmV7t#N8!hqGLF3r^?U5^+$u*cmHR)fws@g?6*=rV{XUT#qdHJbi>$^(ckg zzcFknzlHBTu_d<|$#JH`b_{?kPxa|VaLu=O*3YDX{VH$eu$S!Wf#Y8S2>O%O+sXE6 zDru>#yKZ?|Eh`loz4gVe(>B)+dqrQl+HV@P)F}BVk*+P?o2O$s+^wm&X6@MvHB<2AJsn1wW*bnb_v1L+owdVf*cp2Z zWgd{WExX-3*57?>rF4FxtXwZpeDAm?v1)giw+cknd1!6BgUGLM?&nNbaetMKg#-G9 zn=9&5*EDg37lE;4d-0TBH~5bAuAZ=cE!sT@mBrS{(p%G{W}5clSj7TkctI%@v^T5=8zm&?mLAnd zHUQ&7p0KT!OJskWb{V-m;Ho37T2K$TVj}8k9%u@<*<$VDZ&4LMJ1je!QAAX$qH0b< z1#3v+la=ip0xE|cOW{gvP!XwEN#>|xlT=oEer3bjqI^E=NWmoq6n;W4FoKGv5gL3o zBe3@1JmiS66{@s6Leef9UhTK#!%OjJIHWv^6F{cv(0zEzH%-H$xonN%x;tQ5HtQVd z4>bbsZa$f_uIqrO66-F4iveBrjkM6OgEt1#* zfE`LtPMER5D6bVl%G;A41qj?(X{inc6>6ym!fA_*v#QXstcyBdM2wJ-qEeL);S@AK zUL2_O?Tp>RTo+Lu65OImS=FQz)9Aw&%SjcAIE$bv3cUQ%A77SGfl?@-3CrFwbg@3= zR!^3Sk2Oue+p?rXk0MGVvg?UU&nWm%l_(^r)4Q$~x}HWvOLa6*_7KB0(;Fe9NdTWh znK@fz@m9=LsjZL5!+JeAkh_a$Z72*mq#mg!D%OJ)J*AMy-1Q5VbnBwgyN`szSUIA~ zUoa7>#|!c&5NxTp=Xpbqq;KY>w9;&TQ^{}Tol2BeN>-$T6bK&7BfR$ZO7t{aNzqOz z?`(JG6BF0-uz9uN=tMbhl(ts+cHYLw$tul0K+6+b_d-x@*GhDJ`>IY}A25srk+!Mjw5}~j;)w-R`K9IX)p%&|0+zOO ztHnGcHt9=hRMSyRwTg^GOaipqxYQ7(out!}7TP1-R)r|hdQ?F*>G1W%Qz`>NG^*7$ zyk21d8Y%Z~!LeB%Q`>?0GLfdGl%SGNy;!2%$t=1zMZ4iG?Ja%jO;XL@lOr=7a5lxA zQDX|#=YAyv;jeZZ+3oUQ)gjeA%kTdHE-s64K%&POYbL{-JZ3ap#BNFNegB%B;TUBDuXpwp|hNp9Wk%`5Joal!m$RIg?Y9kYiJ z6m8Z{ybVzkcxg%tLuDZL>NO`1{#^J3z-1gbRV={2x|L~BQBP-xiee^vHl8ZYkIzcM z#Hkj~cU0fHVTCNvYEdML*S{6(n{d*v*vzDojtXCf%k10PTW+@$r#Ds<30K#O)5RRL z$Q}GJP|+_8Tc8Y@;EQs2x1>k?FyfnXTTQ*ilA-`0=S)D}Lhi3hyW{1IvaP2|cta3M zwX)k~U8?|B^kN!mrQuSn_EwhsXcEYGNC5mP?Ee6{h6kwgQ(!T?k*_yW2m?{?!4t4h z!C)|@Ei?!O)2MyVtp(SVvRhtU$!>9a@Fib9fK zmvChxLfKfVLGMU7b$J2k36-7DbcLwJ#l;~FsRv0(p!S?Lwr^3PPcK|r0SJxKL#wgP zsys!PGaZLP#LhSYI91KSPsuu-3M6IS+Y4nwq&V-uTMDDaN6bTXJw_}nWwY+T%(E-$ zucO8t2TEPHTnn5@d}btoU1AiYtY#ZoX-Wo}jIi0Tf9|cX3a5{`{{Z`4{RdDL7v1mj zy~~GX%a^~RN!u-uBtuJ0dFnuPQ6))0J$o^@;O*?jL;%y(;<*0+cu$d&0!!p=6&|_G zLcGHD-Q*OLtTu`TSF~xy-ERuy8~d+HW=?VwsWh1-{()M7AE4q&5UQ7Rf)jP}gPjhH z!6y2o?H-=tKc3?OOYVU zRCFx;>jk#d2^r_D1mY@uG;*4fteE}LacAz(DH3 z&jLGfY4@G&DJeVYlCQ$wVgWv~xpP$tZInIM=1nlE?VX?AKGKps{KeX0)gyxCbDUvzy4 zNWQB%t~w*f{%A_0FhK}0BP5VJDwJ0Uzqh#88qn1Q9qY`riqeD7hUIQ{-#nSH-?eT@ zTJ7=OjI^={uC%VYt4j8C$F}y)w6VJDkBVD-cOvUAj|`LnPX$xF6$i{7`eR6QDzMhN zs0S3#A09`o5`Sh7L|G96^C#j{l1#A%!Z@8zPz;EEwbAV z6*N*?Mx9xp(PU#4+}I0i$D4=#;_Qy!Zj0PxOtNJuClrJEh3>g~SBp5ijMkJQZPoKK zV`u?1ZX&S!~MG0EcB!X%F99M7Q z{{X&>dezqvbl<{e9;F#6VVT zX_n)NFFvIy(1esFbnu{>kxJ7ThjY85ZGydtk~EU#*k705%eF^%VXkC>;i0}hrBrv8 z(z)zi(R|u>u}%uRTlIYZX#oDFxXC`sk*P>J$hmfC&xbYoqUjN>k3 zg{%VFrM?t~kZOA8QHG@;AHPX$MlzWhaPg;=e0GB{$zuT&P%WzyT zIzpbgzNeI>)ammTs0{1hh-q?1+ZYcv(&sCb7>)YRri=+WX$n3i6 zkdw_THWa4PN7t4Slqn{BnC!%?xVXgN^H31(DgIT= zs<u}umq?NI5YBM8rP1HkbZ&_1qDJHs!B$|CS#t+}Cn@^WxBo%)p+Mkk3 z44==*G&S=70C{%i4gUE!mSt)3P)3!0@%qj<{z%vci|b9D%+JuSNd9PCn7fVRjJTPH z0CL^uUhhq$)TKoVi5)AaN`J zsdE|&x>Py^cARw6*ti1TVA5)*?h2K)BXV}wnYty&i)oIv{NvG;q5RN7LKF%}sijCY zrYW1=+U52nL-Bf6&)M6{2y(r5KL`a!d#>kWknZMcBp02S{o-PnxxV+9@5*_~DGKYU zO6??^OJEy3a5Jlj3ZK2DwVLivENEPH`cpb}i4o+*j$R7+#I&?2IiJ1E47)}rn|2z) zZn-(Xm_ z_1AUS;UaVDn*5f2NTmn?FNYFSTJ{5q*65yC?A=}mMWA-R$_tFdVw!_d^eL;5?)MGT z$@|uA&O%?AYDde+b64)fMw3rvMkDYxkwIY4(Li-m+ur(X+~lRkw9!Rd?bl&#jg@1U z97d5jmu`~iph+^w^7U!U;TW640VIx)dX+Oxf0XeOON?8NM$Q5h`} z$mhcHw2-2O*XqN4DyJIf7N&X~P$VAe1y{Qd40=^H4yv-AkY*UZqqw2M=^E~7LX$(E zn9|#)(iM#3y9}-lPg36h04_6hZm2yq`xgGJT4opf2XO3PnIZWOmp3@P66L zTtr@_H{ZK{jm0-DRYZ`)X*+v!#Zy5JF5M`$3W=|P`*C-;sCX+d#n6f+FUksc;hwuT zP*SKaRvpC|L$ret57YkugT_nW7(C1&{{WbNwb>t*o3AfC5BU$;6@2{ip@^TD2H=oV zQZD;LYrH!?70qb>0KLUCw%`8%D;|P>Qs|w>AQ=aN`f67q5Sj&Q&Y!H}{-HYRRxn<$ zjDlDIRC{ZHVd8`xQC%fp3Dr)xG0`}p(v`qDl|UDiKm=;cVImRbVdx=Fw&y(SRk?kp zJ4E}bOkChKU|2S7y)%%HCaOpWpy`9iqa)^%cM3JxE5^ zBqdwH3KNz?A%LPfRECuYQaTgU>chKkDUJ8Qo(+JcrlPrZ#KRQ|=zHnM1IkJQicY+= zise&@2wPCAw}V9LYO6pC$Z5%mi^SCD`{B~a%~Cn9dQ(2|7fQK{|1bt}Z6)D)T?&hLH&C+4pMT*1eZ%Q*5B z0+C9Z{TMA0b=HeOf&5y%m?PU%tYu+s)TA7#n8R&Ss?GW`x~9qMzkn_ZQ9?%LfWnB# z(os}OeLiI|5x8L{)yg+3?*P8W*3e3YPGh&TBa9*O3Z?DOx)n|Q#kP7kf1y5nE+(#R z*b#2+ck8vzW>rOhqLYgz)4MU+Q6w!;x%AGDwI09 zcHpSV*|Bz{o#%f14n58dm;0=_OP_I+h>?=qHWd1Yn7Y+!0*FY=oGltg;%7CwCUVw? z2z9YUH~}5>I0h8+T5673-tsPX*VT|}Lqz=!3_5_()ls>Iby~c5gZ3_u%e*lPD^Z#u z$CiL|HPQ}vy~QWGxQ*DyI$}=>rtn0`ju#dPX+IN+GzlKc;QEy!-i8WuK_v__{+5&T zZV}*Gx>vN|YpQY7Ts#6!y%n-l+Rmr4Bms~ea1K%y33_a4|qt66%tacKFbfhEWbmdo)O#9}u1!0nIAAAPV_K1tYt& z3$x)=&B#5~-Jd{)(Yv;W_T#&&X)oVnq02Ifr5+QHg5Vv8O*JTaHO=_(yD?K7en2bb{S>zjJmMu9tfK=YCjp8>2Rj2 zth-(fwd*p~qtBl5oJ6-21yYp!G^T%EIo9Ig&A1-5^e)!cDKXDH`cK%tdH1&6hjitu z)rsqac9taGBt=};%^Qv!n521&>5^+xj^V{*XNw9XVCryfN3ncM+*lhc-QMUf9vK;s zf=?||uhv%_@P`myc>7{--Q~T{az(LHIR|jJEi!|$R8SgG2`Wovel-N7fD=r+aTnS= zwqqd4B)n;8ueu1Pid*v;wKmos>KHLk4;?je2D<6~D%Ji|_j}2^a@N^q5Rqj`w_GO9 z(2}N{A#uV9Ie?^seuIqX^A&Fs9UIB_wE!-f{D@B^*0)jg0~>WD`oFE(60;VQT|J=pVZ4WK}8|_G0ZVqg?Rr1T4w71TvLKsDQtgL!jziXr$p+ zgqyC%T@VF|(y1rA3DGJgTcwpIno~~Ek5LrJ3hLjY4Uf2?Y)h{ClwE^tyqD8r3vQsj z5hKJ&B8gRUsG-L;cS|b_`3ArSqLp7je=V3>jCS6Z^tb@}(^XopxA{B9>%8rk$5=NT z-N9;|aMF}{UR#)bD3E%es~YsgfVnyd zs#Y&I11@ucN(&IeSdOrIp|PZ38>3UJiHn?*k*Nwx%1K%Y7){KoLI?ZFdJL5O)N&0% zRti(l_l)H0iTNlM>jpn^K)GpWZtPhr)Q zKJ93}gj?o>8&WbKLgh00WWB(5=c7-am?@6OoPx7NtfMLtNb8Dr`>}UW=^ll#_7gt| zf!<@N2C2hvzwSNXyEgrr+jKw6w=1+`F5c3Urh)YMl|V@u3`EP0ipIpqN4!F^L7TbD z=NStf{2=8{J#=xk<7RWs?(J}GPFpvl49~Gl0a{esXbd>nRRGk5499U8*n2y>MRVJ5`Ld5%=6?SGkRO>L(sNsj zs*ZKf6pWauU8%EJ82}1DK=8MpQCvk8dMb6```deactpBA5i>&wcsAO*2|*f%y8z&; zImUH?>s7OJx@K-G)U=YJCRB$KvzPXdDSbYi1+!?cc(&3IkV(=Mp0(}5o?3E-2_YZ@ zpM5Z*3a#*$mJluIRFu;{XLb}o3KTDpRmAwT82qg8VOcgBGQ3zWk*17iJkD~Wd(4f%N zigu3NH=-)gAs`wI{=5KcP&}*vQIdrw4{OJB&xk@ zTrbHsVfWK~6*RYGHkBmMf~0f+;EF&VNgYUQTD6ju={{X6;wQh|hGZ!i-jdUj&uB=) zQl?wMELr>ulJk98WooayQw9J7qGfw0ywG`+jMXTr8lK$#oJ6`(3bjWV3BkAbMgIV6 z&qdE}Ugg+lAcYALUvZL1>8K!pucRC~W^}BaHnX6{=0OajZcK-lHUYhs8{{ZVh z>Uq07*J@eY_sB>~z#PE%;;;P^h{1dVllDTkJtSNl0>Na@_8g+-jr9)vM>F2Tq6jdKsAC}Q`({PDF?r!U-Dc0(=KT&5!# zZO|UIid?{lk;P2g?rdcGhmBNmAY5&a824xo+2l7}Ovz=&6;bSUDZ%>M?rK4@+ez@a z6xfzO8QgaG3@j;ATw#VtHL9Inie>FMsoguORis%oMUNyW-#R{l$u}!&eg>>8#DJ86 zRQ~{WU;|I3SYwfJ^8$CZSX*sV%}VwO$b;#IJ+TcBn!E1#l2+*>`#9Fdn*yHmiC9G; zRmrVMN8DFQk*0yE;{Oo z9S)_FhQ-x#wA!$?HjhF$+hbZuIbnF(Qsu#RxAHNyC4Q#)Z*wceHon2Qs8h4%=SNA; z+vR)7P?6_?kILlP+y zeM9VAvzh(xS^G+|sQ^zx$pHY3WS_4R&-h7I7(bcxuI=ppCFJeJwd@xwe#w}ql3~VE zPp`{q3Qv@h2-cz0XN)5yyKG{Zwa(V7u&`ae%G_GD?Pi1{6SxnR!rWT+vXD(Qwq9#c zr?}w=ceQ~(5^;6fY~LP)ix-DEGCZX!PS26#16^RYpij`OJ9h1{aM9`}*S49Kx~_=P zCCrN|BHWESD&aiQ(zYB)N|HgQVBy45wn5a>P>*q8EGMPng%Vo~enurGHOU2fTET)HN|b}Nt_adH`$qj2ggBDmSN6GnO)Ra3XUs&%U5 z^f|2xthiDtDTdqN?Wn zcO%3rQZye5Ny{o4*9;_uy+D;aZ#&F%3L(BnU(Gm|0SieXL;mJ27PRQmW*MUIFx50< zGNlDk9uR%srw`&PuQ1UNOmu~-LwdD}Vabz3z%-sK&c`_@G)7{$AV7f)DJuq*9o-H* znx!_|)SnYZgQZ9{`mmg-SimXl>^pg8+NgxMr8I*0Rzxqf3_?Dn)mua?=@d>#wzA_M zZ;;qP=miRmJ86cJT>w>*&cr#XVm+z~08-<)s#1Y89dR7{rDA3~r-(~TM~)OU+rS^w z?)orEq^M7zhytYDn3#*2!)u1?l%|wM$$v*~Dmke=#ii`R(J0cYMBJD7j67pCYn2t& zMwJN;D^V2cdg8rdk2UJ5(KET0wcRvLF?mbvuCbgTQ$RxNi8nw8H459lrlN~d-#UK` zeL-EMk@~RZqeR}Mtrr;R2^OH;KY2v{(Sc)Vn930~vKparEj8=}RQ2D-;$Sk(n8Et}u zk>d(;I-jQv#Hriu=qQLiWrrCGaoPndqIKt7JcdH$A2pOfRFs&%mp~1r(%O5q)L~T+ z`5Mzkl`m|$*C(Q-uNjLTfv(}-iq=d<9j6tlZAorRb@)P&;@_?&dKhdau#sIk07fe_ zS~Rs^tg_OHXq2~}=$iv=iBQQ*hX_GX%7oz@HaU-%RIc^hGTYgQvx<3aJM*_)zY$VY zT$mF_RC=0XBROIsoYR7|Y^=+!Z2^D9MEY%7n_6k{3Su3<7*>g@V{&S3tqsDJ3B#ns zlJW|BhOI;FI9n}H@OdchzVH}Z;DC?W}O#?`@X^ zXHybclc=3VOJsqXy_jop8`+j+I4Nsc;da%Mrj-hvJH-51a^h>X?XFh&OZ+6h;`Wi1 zAId9iohkCvLdi6(M}9R+eZzMaNv!g{)6H_`Q)J^Nz&>Fy>J`yZi=0Q=Or5;HugK$6dLo^1 zrL9V?j;X?rWfBdLm4wL9nk@T|{mdxHnmHr7VuD+#1b+$a!J5zlYUh$rA!)B%k7f+` zS`&z*siJkMZOhu1#FbIl$ze@YF+Eeu6eu|#A zTzMLje8eFt>aY8Q0vtjIG!#k3*Q}LF15d+5lkC81o45jthurZbI7uD4LBi3XTB+Ab z0Jul+lkcXea`YxOB71N!7(Y2H4+Lcrb76LolIB0L6VvYO40= zB&+Kfg?0VBcCf&i>?mvjcHVTen7DK?(sW0StJBY16YA_W6b@ zTzmR?t<$lW7dc(S8)Y>t*Vv%%%rAW^{{YYK*`vrWMA>%O&}tP>m)-vI;HP2nCl=nv z;yjF3m_-ZCR!{lC-akLNofA!45U11nQBgH6L!^E6d#jD3%UpU&6_wRx3UyxUk%02j zk(DOxb{KnP@>8o_9ElbYWhbfCH7PVS%9yWZ32pRYU2sfc#ZF1#MIARs07_Fz(uWp> zoz-4XM!cKI+Zxr+<6+)hwl@Q9xpJOD5a2`XDNVAm<^=#orB^KRhxc!9V5gzc#= zqU(RlX8q1%ahKmD^47JrKEwMJHoD!emzi^}_nV8ii>1ZKAD?o4THSf)R93a9o}`nF zp2EeJ(6%_m8U~A!Hy&Pl+m}l-x}M@mK9u^}aW98#Zb??sk!ML^yQ7M62_S=102S2K z_F^9;BL%=~xT(#*45rD?Y0|3EdOERKZhVzK<+)mU=C=F?BwCQsDJqtwgr`!HdQ!N} zySI^@maJrFLRU_HSn(FKUd=46scQ{AYPoVN%9Xvho2@cp>$=S$wRK8E>I1J_hBOVf z;w)L?K(M{vvBiY<^MJ(HU!~ zRr_~utCr}!Nw`l?QLcv24oON)1u2V#mK~5r09e-H-4kC-T=P_)3R2uD2_#oAFstBE z_V*z=pogV8(Oj#|XRwLV5E91VkuuW4VXfr&Q0BH$`8X@*F0S;ZNM6=k12ZRnu4*k*K)Z;+*XT4vHP^AnG&S6 z8I`>1S&daKWHt%XkaIM`SNAsd?hlc{qtcvV@mII)bT-T{cNz^!?+2kyKW}Gn9fs*9 zC(7RU+T6I@^`lFnr7Drqs^>s~QSZkJcb7l&*=GitvAh@CZ^~xGI8Cb!O3%$xvuHPHk$P#X?B(07A-B~0#CVpKrJ`E^dsRxD zGRc>S{n1jVw$X{RA285Rq!y#8;NlXz$x-tSM{iSyangz&;l#YqrHcgD?RTp*wMar2 z4ofaJ@d0T^maugcoQ*ou4RThZOLq?tmfLw3TjUt?$e{P#++O##>b^*jC9dN!*Gzzb z*mqI*!bU(Iw8EHcM#E~>4ge;*ryys@$sy)a{{Rp+kLT+OytOE@@0y#D~i z?-CG{UtoeyXj{P_uM~de#npzCf0b?BfRm?m+n@6(GuFgYZuK3?4CzbAkmP4VNT42M z7P?})*|~hoOFb0}Lj21>W89K@RX5vFrpnB}#oep^-a&K|Zv@XpdRQ~`+8LxKZ>cYF73hIu*T=eIe=?C?G#D)3x+x}bD z*43Vo;k)cxMz!ZcV_fH_FZ0E5oA$;^OI!lWgL7*+5Z3)#gwEJo}|{J7qV1H zD+&aPl;%5eN(ruxr>VlJ$l!s&kRY8(kdmaDn&G^OQ=>|<(RQSp zM;P&b^pPaLAAhlPF&WSk^OvIps$~zd|M-~RH*TilBK?+pf}SpX-qgRs8p-@ zdP;*gWG|B3_o#{af@?e4&&-9T36 zx#jM{2a0Z1qNz=2N$$WeRbZnbPQCc@GhIcZ>fInr7j=fut6O~Fk~wj@j`-r zv;cJ=;hF)(NIW@qO^cWKlJp(&g{yS!=a%J2(u7_km7l7(i_HH3>7D99JR#r$zT%pQ zWh|&Csmh@H{g}h%kKQQ@SJVKg59WjFS8eb|rTx#w55#Kozxnr7s)Tnhr^4Z&*YuAI5l3=Bf;l zYXCa7oAT+Jh#h+~II;>fXcZ3CZnWXRty z<^(P}inOIbI>AamnO`RwYw@~t5c4;>xwH{)}@kEr4mUet}hnQwl)SdP!0<`o<~aZ_ZpIF zeQ8OA32Z6EkR4=w;wwT&c>Q>DN`(_Eyth-4T4;45s5DpZ6VkYALXFhZ#ZNnrXmM=L z8QUBCW?tO4?^{%cZM>PYX1o@+q&$MQ=EBU7(IZJkGWs*m@tI7dd1u?s+QjWx;mgog zueh+-IdBl&S-zJI_hez_d`IcQLaAY`AevLDS9jNq3{hjoS5-ajd;538wkpwT-P0o5 z8bNI^n3S}(l-9avf- z+oN{cQ1SDu&J?IR^7BH7b0rY0=yEu(Z`@p$IOj4abWm|up2cohG5eM&ZX4zY9)3P6 zvE13Zxc=2=xAK1YfNYAkTaxvn8r!RiS}3+!N-0fMpr$!9Y2j}soZG~Dg>_$8JGHzq zm|G!|xMxetea~GqTnFTR?R?~Y<$T`Tic;m>FHV&mYSbBJJ{Flra!{P8J*T%Fvw*`z zkF{(uicx%X-8-u5Gk*K2&0O>B9tm#SwyzgPuQIGQnO8=({6{9QyK4USC(8p1nHZ~x;1db`X=T?Cim5u3l#bKgiDT}H zs1|DaDHD6xHl>nmOD@sAN!>0$w#1dWuCBC}8ERyN1+A7sK-7d+;`?!5#9!P@$2w;8 z>p|%rD($&&_O|W<-Q8ML`qLf))$=W^c(Vj`@5IA~io0bcBnr@6C6>0H*J10KoVT?& z#8OQoqLiSoN}lXw@xmW=@&fBTh5rDTjD>{PK&+Ri^=%1~r7L(&dL6bq3-X+3=BJ48 zq|o#^<2v6kTmsP;=DGv2cUB@7nau?i#Pt1Q<4cb3P0r7`Wj!X@sZCm4Y$c&nK)roU zF_Z5*o7UR~NGKOn_JbRVlOkk+#E`7_HrV)^cx;=F-{iTl{D9ii5%-kXl^w?uK&Dli zl50+#aY@-O$wP7XPwjS}rx32MTiuksmX~L_H{By_to&NxyuXb(ccAX1t z+feHUpOls89vvqlDnS_Pt;dzj$7LI$Z@tZY#oBlstn2w=pGFGMs)Na#Noul4 zy-(ZQsy)Ws#lhwwKqQ$TE#@dTvOuM11x7T@y}BlJKu*dUEC+dPTUcjv?W7KAIuAmN zH%A=q3l)mje3aTgPU5w<8E9}X#DwZ0K|c@Ei92l6bI4jagYa)8vmr^a-uy1kf}Dya9Z^pu4iBv-ssGlUnajG-FPfDQ#0schP4qf^(n_F!Bn zQoU$Ef&00R+`n!J`k-N0(yqX8V2VojyjLtXN(mMX2%*4`5xl^*LO>K9g19O@SEvtK zp6v1DAnJlsU?l0tj+jZtg)Sde;Eg~MXnTmkOnM_Uf{URVokA->O+EF(C6z1EyWA>x zjKgS&6`@n*paPTGkS7<5R(qjhnOU4?DcK8b&5>;vW*aJFQjnI#YvCE6b|l!r#Z;xs zwUYE6DWYROiD%3{2MMT9?#m6_!b+SM4RbVFe2g``me*Div*UFFqz>8)3nBU9(;sN-)mmjckF0-VQq!BoQ}1cXtU z4#s9*l=={vEfhN1(vno3z8Z{<6L$pgH>u$@%0gs zXV#r@?xLIrEmYG>O%{;h?9>4tQNckeUZ~FP0ag=j`q<~&3Q)}ZtPRr{yozVSN@OU1%S2O0=^)_|LVXIC zd)r)mp8)AoRkPOi*}b-wxfhDd>2jYm>_?+cL8obMb8VlXDB#VCIx5uNv$}^^WFb?o zO}z2nA$pwITaS%4*Yd8>xPA!St67f3RjzvutIu$?9H{`S!rNM&8ntzcDMiO$b-?Q1L+N?!|sa^~zR*h9Q>C9)#&)^&zd9X4}L8 z78BB?GAIIy`(Kcm|v{Z+5ghTDb1 z-DvPgY(rBcj~b@EmA?uTUY);g8`*GExu1zl9DLHh6uZk?McB5hRQKf&E9KFh2~nrC z;%X*Tm6YdhfR%vwsJbvZMw6={MR(;~B^thd==CKA!j7uslhk)%!vHC(v|ZNxkRq-m z{{UF~s0}Kz?D!GeQ5jX&X2?*lxdRlf=Fj59-}8Q-Vurquh#yY@`u_lGr+g7t`7d)y zaB6kNq`0MJ)yImD)($lL82!MxyD<8KRmIs>v|o31lGME^`T2~pb$2@8Mq}y1x08lM zN$^$6J7YZUYk1Xt0Bw!s&l^?+vd+fti+YIxq%#64qoF5C285)Vj=%;v+aZO-&m)}X z(5}71Zq3VS6C-2l(ZO5IwRGIK4ZCccx|nV?@>_c5vdN+nNyrbQ83tO`DCZ(Cg?5h3 zySbjhuAk#6VM{A+sZmH3`Y|2UtbGC8RB^nx&u;H#jET&Awa6xn+#57jT{RAbd+}$) z?n!M8IE6~wdlJ(i!f8h;&vxM*>uOYm$BNb70#Ds7m3{7?@eXyygJ-;LWzd=x&-qT& zUqM$ibp+6BXxoy{(uu)m(9Xq{z;F?E;Z){+iKEcOBBV6irKQvH%|9aa^0Y zjvN}VHhqn0{$tdIVc}aw-$Sm#yvq59X;)RFDtjr4_2%uCej#44{j#^S9_b{fRQ^7; zTrKK?`|i6`n!HE~)CXLt+lIEemoeHYT%Od5XiIshzS&@1ist35+rs0fq&VdODs1)w zYl}_Q(4B$6a9Q1kALVEA+-MYO&io@_+CV-ycDl0uW*YmS_S;q2-h3i;1)e5p$h5LQ{Z zy%ZH8-8ypm4m|kkj}_@>1s~rOrz9;%RRYvaM_lngYS0*|8JTM0Q&K=uR05T$C#f{U zpsR?GAu7uCfR$rJ3URcpB}1?bB|L-# z{@MUso-M#hain=(P(3Tzmu@0tn1-7-GuMZw3C+NQ)q3Qxtn@w)j{|5}yNMzJisY!|P@cC{t_6n*CeB zH$H7CDh(;ZN$ePTB8my)ws$As?g-rQ zFK!t*F~dPl+E);_+1OyynzrCJHPz_qIt5`i=EK|DWtM*JsTU;I%#vx5_E#D0_Fh?C zrvHAZ3QpGpHvw_eQh^i5A$fKI*T}l~!xJGH6L>9Ji`GC0aR?}-;Hq@(BIBdGhGE#t& z%~A@or)D!ejkUvMF66p50g$kg*hbO=mNx_i+9S0#3^Yo0)RK@Xt4KBPz+>(V&LPP> z)u6U$b2zBabKh=LHk`K<$|K5Qb&Agyn7N<@|e5I#PZexWk-a9~8 zxWG!)ImX*k+XXp_8q*~`r3UeP$)u1L>hKw zj*`YnGuR1VKldH2vk7S86Ll6Xob|aN~epa94Obih|Ez}I*(vE1BUbH)biKk z3GK-?>5R_XicDzfT$@xk04f@MNmh9uW+Sjy9Z|d1ruVJCL)d&mVzwu@9ds7NsGhDl zLOy|9NqLS{Bhomc=}e~IZ6w3kt# zAW%@#x4#HA{cR+x3!xFp07rQyBPiTxTL?jE_)y#{ZD~?!SV~O@Ip&-b3n4L@_@_I) zt@FoEf{!!+swF=cTudsQXH=sZme#bDgP}Ck2&Njy@ZqA8wP|ar)_wS`^Ok0uxeF77cXQ5-}=W+38!`eAgu+Gbz-$2T4U~ zs=Wpr&!cLXSrg`T_@yhgl4ZSmWVRYv&rYC7`i@v_Z98NdDIA5H;CH>vBZjrmYN$tC zIO$QL=rGHQd&y}9*G+V^fK;!jRN$94(8ck%4_cD2#NAHs;V`?@Dx@n0)7|%r4&_8G zpLL!aj)H>KzGC$&%^4>{W~UX~ozaht;$ zcN3*w0!5m5uldakEjIhaGyP`!RBRoi+|qnDvVYP?+MN--GkS)a(Q%#{+%6#$6;uZi z_Bc|0%tgDo_(qx;o9$C^b_aLL+KX4y=>4iDzis>e*SX5FU+r($uNL)|=i8*Trk`bC zR*ORDMn<@~<8t=*aS0-Db58;mUz^*vnAyxXEi8ECQ+>oZ=1sZgEagB4Q`4p->J$us zr*oEv*|0;BZ3!;AR$rPy)Dv0(Lx{M^5-Fho5ma$Zm)KouW35A=@DqtZR5a`(iqQc^t{X`x0I6$8i=F^e<4C!&5-Tm*{za!7NeFE< zE@jl|Clu+P$2^s(HVK16Xts=GM{r09^4vkLl@8ILLx6ocb>NQ-uZ`e=s*!F?Zrg>n zHsGHgNwq$7Mx0w(W3Nw!i9HQFhAK_IwQPlrBdKavHhB|n*`=>R;7YxG5w&J?pNln`fsC`b_}FH>v@dlFU#F#X z7iF`THkiDCjkRmz0p5yrQ2k|9=Jok#(tAHvHK#U)o`ucwIFeM64T)*mWTIOLM5zg@ zBB8};6|Y)T6}ynraZ{?hS%)&mO#xT0(pA#Wu=bYo-W!(G<uTnF{Dcs9xZ*L-sBSOVcje^1>4YOEy(Q@Wi?A>2UBX}1QhEcl9ke58PvZjDS8&c8|lq66ATqYv` zQ8ieW~y`TdsLmtd9-cYdOXSZ=~3ZA)M%eNOXy`a_Ekc@%reYm8)s_yov%o$vTZyQ z@|hT|SZHK$X!=bQ=gT|`x_Eb%9OFCu=<-w>k0bGxs+L@jAq853SD2wnl0ojpTW9ZB zb2c9_7^Mv;uU*XTEzZ}@e2ne~Kx^wv&AGSJKa=*x+lbqDWLz$BOhFMO%3z|e%K z-Na}e5=vuGkueVOR%w9g}r=}(o^s40|>9_2=*YQi2IcsK$pKl&`RCeN=G+P_b*P+iPsJl9qJ<<4UGA`*5!Pe!1QZ7Hz1W z`rT^Z9RapjO3id3J;N3YDsJgl`0YFaxl}q;pKvQvoW5ruaGFPD? zYcw>>j+kW8N2@GvCJHFy#Y&}ADahh&+yabJUkY3k1DANpORJ(I->ph2KFO{3d+|-* zarI(lALT1z?0w5)+}6bX@%F1b`GH&3*QtHlrV~cz!L~e9dw;umXZ`Lo?#D_00JMSW zD`x!E#y_~+Tfl+()i&c|%e$1@U2O!v8sMF304)Ciba3Q-nAGzQEssTl?QzvTIV$() z4fAaE#p%)YE6W1-`wKmX&b3TtnRkHa-F3QW(pM%g({t~25`#{i39;i4v zU~Zp??Ab^|gZI8x@8Yk!%^1h~TPOXShdTb%*;~>x^Y!Kb00qKybUyQq9#vkAWcYy{ zFr5*Dk#Ybz8u#H?(D{J^>u0Drb{ri&X*`iG+#0B>Ri`pdXmFWMfU38VC9Z#`PA6=N zegk&gu^CQBOK-?-T|Jqa;=zvxpbF zg=~SRx2_1@2(?3_;uv%jq;Y}w?Vil=>e&0#O)mZv?TRu&Ye25Jn_{J0H;QyeNo_xL zq7<5Vhh`qMD((BZCoQSRW66x_7HWl|Nl@#Nt_A!RGbO=C9IqL+H^s@uhLp<A@EmV+o3gwS4Edyqxl@nfr9!iN;3TSfA0PYBU)Dn@AG|vIaEI6$q zNq*TXHPM=-+e6JuZ~SP^2gll+MPD|sxOjwlN$Jy)`;XuJ*|4}zfh6?hKS&(Z9L_Ngk?}8*`=|Y4zcK#+QkTo^ zmupMiyT@}H+iX!){ldXyic}b2zVSiPJ}-RGD4F{DXu(pQ z+}pythhkaf?G8`jf5103(nrZi-f;+{FE9YBtBzEM0NTfBJ!=G8uS-yPZa3NH}vefDv zBy`LQQ1{{NRvlxA3agdgw^q{hwgqC&mdXOBA6H&rKXnKCrhq*Ys9E^(P#Ley5_ zK0sS)8Gh=HEtUqX3!3-q3oqSHY5L@pGVt;e3IG+)I`ys}S5-`8M2%83^|Gl2xI!l=;g?RE+ZP!VDBmFXVL6vy+>Mo zc))W}P!PJ)Wn7M?yTMvMUf5E(A8y8R7bM%QpCW%2l&*mcqgeu-_^#~@!SYuDpjM`e z_uj3ZGT5Utozm8}rEv#)4YMl6Y@cA0Epp;ZkrHz0q;EsBeLq(A6SqOclQAi-CyWD%m4h0f(L>}4{=2}{i(q=|a-K!}LkM&L-&vER+ zl@3t)u=Km=^shtmnwcI$@zX7qmXh>U5|HWv@h4EsRCeL4ko9`ft&|Qt6wSn!{u$ex zg%66(iHRRla%;}>h&#`xP9!)?t4g}yfth>fmW>rMOFIoeI;bw zR9&j!&4nw1_MNX_?*`jO>h(g!($=;-vW44xi*xcLw6r<#EEMTiWmVmdHr#uVX0EI1GQSO#W55u^!p+cA9x~5zd z_9-VFX^*;X#j9m$sBi^*%*ok4&h79&LdOt(f~B4%x3#;uHs7|~LK$Or+b$Nz7UBR( zR)`Kb((*k*&@o=zlbc&VF}#RZje!Giat0YV7L^cM+jX~8)Rcs}%FAm=74acj4Lij~ zEK^P$ZX7v^s^zVTZTFSTI-gp!8@9u4v%uQ1Se~g9OeWrz$xf8F=76vP?d#o)j~jAb zGmoWpHtxhS-rgqHUX?I#p3+|BgSq6QV{qHEM?$zs>x@ zAl2J_r^93Lwhp*21gcTZo2qX3QJp{zJ_@khRQ(+DTq-Ussou2INj0V|*3BJ1Ld9<ZJ4!$mI6~E|^GRy5y6SgA*1oHnoX>YJZfk=oI+GGG9lsGyU2yVO)oWWi z)7%bJa`>%sRbKp6X84y&V8`&%E*lj-rah*B#?Ic7bd-d}N)q7yndmEo0MS<_e^uBX z3N4J{ys)IvYe=tl0zi2yQ-xHy#&(T~xLmkechGhcWdZUxc8`gd4|weq!%2hrDwa&o z1H;+uOs+-Z*jrxm>^-x&8r_!dI>W7}L+$?nL1?MWcZ@FyBxT5!gcq6KTZcyik7WRr_FRZ`-rF? zP9!baLsb)gbV}{Aoe5QFuAui}WdP%<6)97ImlRb_xbiy?s0lP5x@(Uj*ToEkk@Voj zg%mvrN{ogQb83y&vOuZq_Fz$}g7SqmP?3VL6lJm+1E~6GgOnXhT?zgiHbYXdP%0=X zk1tfLgbHRezz`F#O3`TA|{c`Xn#SA|*}B2}<-6LI9xl z(-zxq3mLxn%5hsaCC1GW#v)1cB0Ls_FuG6`3L0V9c?zw2i)7EK;+zs;+vZwfO<^I| zPzRqwL*hL%?HEvv6kJzQMImWLQMCoZ*M3-rh)B?tbu}IM5Za@h??nTX4t25SpLvC{ zRO1!ZLDHBbqE>5r#+HL#DJD`J3@W;T=%B95BtZ&F+CW^IPWc}#AUhl^l%Fa5!AY-W zFy37nthSCmV4`ajl9Nn;Abaz~AqvzIl3iPNn~GB98(P{5Pk&Q_lkOCQ8(G3`g!TbU zK`UC>C@LnMopI&hjR8Og3Y;wC%gA(uiV_Z8N$HPKDjbCE_MVtdP?nBoAku(3{a6VS zYtqLo06M;!`5a@Qf1A zf|JT0dWvG$&0N!G{V;Jy-NUN( zA^LMsSM)Zo_5Pr!_ovNn?!C_`V@m!=ns;hYrZww!^tOwb@8m9Ss_StA%K1NJfJIev zi>YU#KyVtLbukf|6t+I3-vZI|%&!Wr;@{-JVd!kZyayfCRjDf-YhSjw!VDz!5*JsS zi2cCVYOd{?W9qfr6yki~n5>U7xhUuzF^uF}Ns8?awEEdJ`Vy)prO;8M${j%KTr#4V z^APBgocf=lnDHrgx{F|FbrD=6DqNwXw2v`Ps0~M=sYCVS-h1gpcx+<^v`T0|%BF=~ zFxjo5e8|Ayh0>nv2nhWk6@`S;JgL(HgrE}m)ugJv%o>THbyRP@V!(0ua~4V#SEa>- z^&ObAV(wf86%TURH><5DTJ3%Z0}>Scce8P&=h;eU?8B$s7Pvc@QBheLV=X#@D5?JI z;a)V0xo&8CZOTis7N<+$<%ZO!R8`>vpwAk{Gk9M40*=vg#^dc499_7UYT}DQ^zFs- z^(n~HqbLHc9&jRqyDTnu-bIIsYi-!54-$0d>o=>g{Ty_rNtnP2e$@I(u^?~F0>qRY#^E!0&Dej z!wJFZQkI5}1wLa4Z%Sn`T=PK*s+~F$Uu}Ca7jj4%t9_*LxPTJn#mOuy%JU;SXbQZO zsUxroj75lnJQUlmJPJ(tn}U@@wv{CH@MGRk@I?8SBdw`u0ZUR^S?CghU$+Yu&?zX5 zQj|}abHbJQM@1XbLUf#B}mP#SRPx^$og;e{KiYT<2XK-5u#&*uiA8>@w zfx7K`ih7v&!wnztqzYmE#wJMHRRLqUt?uQeXp3%1b`(CJE>o^4ASACK2_C~-II^^m zil%w2owHw|sz0%=aaVM0xV0|o8cUQ3?e6P>#fVwG!BOYUTgLrPi3;_& zAl;;S!grF@58b5-t{z+1J)>1$$mcEPaV>Imaf{ZuH`FsC?(10}C4OCMe!Q^-x*>C6 z&}yx{wz`UIx)+@llD^2>5-rS$8?D$Yh7=nLZC)>CSfJx$ov)`#-FF?$xr&{95*@NO z)wpfzug=SMa~f2or~);lb`IQ1OXm6Qriu}f+ewdiUZ5z8XYSh^JIBh}EK=J+b3-+* zl_Z)B#c9_Ttd*r=4*V&Ex9W+&rn{cw%bl(qQPpj0PbOGk+^#8G5!-aT z83|X3DnTi&MglyXmJ(5%(~FI@24W!jIVz#Z`&q4>zY8#fs%AqBON!Fgr4{*4X;?{D zyF6={oR`hm5_pBoo2PDkhIOoO1LCe#qFwpzyE@-!TxQX6VJeE!^;JG&bWk97*Qv%? zbqgCWSqF;VVRNLj@|xf@;E>^NTSUF5ymmsHV$pwY8;wVRR)OZNZ5HyUQ(Q{RjgmWt zPpM+BKXKX3j+)7&d9rEiL9BqZcbJ27>ibK;$F zJisck7`u15;K#ekbsMgVmCtcji-VA_Hb9r+I#E6w$U2IWuNqaTR2*)$FZ19AH)ulR zoUwJS!nGj7SKQ|Z#0VQY(pdB>~9n|S57$mH%9+dw8+}6*- zw;j!u#L`mo!4&I=oO{Egs<`E?{F$hYZ)oXxb}0=tL_Ke1Dgf4sLX?s+!SQFNqtcDI z&roB%36mDpc$+9OZG^d!mHmSZpt#{F@m7MpDcy-T&d@#W0;@x8=QwonN|Wx_%ao$K zqC%y#65D_@hW-ITs2~AcHjRJ{I49=m<<&e^5bV&J0G$I3-pZ%Y&sB#O`kOUaQoFF_ zi~$;H>L`(B7l@*hTK0@cK&$xxlF+5F=voO54~0Xn9i2rKNcSC8B;2iQk!Xu~VYMj^ z0Ed;Rs*VBHr@s&ul7`4x&0aD#&X(9nHB5z-v~ob9?9a0jp;BZ$C3e5^&fGk0bmNW3 zx33nvWL@jGE(>}4oQC6k%&1a^wuoD!RU0WO%ORJ3INQH=87T51Sj)V(4g$JSeN_CE z=XUJ)CqZ)73F9WE)YNk0>|G+8$=pj}3u@uLEVj~)h*ZdEBA(4d7&|a|Nsf$h`U>p3 zo%6MAO$?EWdX#f^^>gW)Z-)iTm#j^#kL?>o#@$%=rJ|$uVrJ8~S=qiATz-XC$bMk< zMWCzaj8oL3tEc7*>1q>dau*l zk~RMT^6U04N&2kjZawFYLu+% zMYgVwsVQ<=ohvjAg&G2SU`xVf6XZGsO5+~w89|vYXeTrv^}*wPQddN@(4;V#WYqYn z-GwTmQ@d<4JrjhFmHD9GR)_>=n64RKy}dM6oE&d!`hZfW^Cj^bNg_Lq3Iqgz5mVRBv??omy1#NRB2=YGj*1INGi8u}4B0D!CUUpShBtxR&@;He7qkL1_)PQjy_NKqP&5m}4rSA3ahqX~lSS zCaJARAF~H@Xroos^pnlpLBn!43p}aB4RR~Dg|e$f@y21uUA?x_ptPmAgQCf)DK*9| z-3^VF%RQ#nV;FA{;=0T7_1+D=wxf}m3t=&L13{;uOFpSNM~1nFiLUpq8{%QJrPyS~ zPUgL$97v?cadbiz@~X@g(29&fcH?Vg@i`F7$-4T6fm=K6+*}Uiz?reO{!n4m(Q_(1 z;FNi4wTB4k`B|w?xMQN699IW6QAK084|OqU+J2&KgKN7!Mek@yZR?v)B?22LSS4#p zx}=gb8H{F|=MrYVwSQGQU+wfQ*R|V`me_0PdTZ9sJ{lEXnU;j5uww^FreRqc<5QH; ztDhY1qNN4Qx4CQER;ae$mzJaV&naZ3Yk7K7KHlt8rs`pH%G519yOzfbMKu*q&Q090 zw%iME6YV>jlB{-RCS~qu9$AxdeE~~I2|o*U(}<0?i*&9xGLLxh^(p=Dxf^cAtf^utA z(%e$M@Sz|xpmMN}T1>mTi#F%g@sxq%Qb$5ND~gNleroERg4ANk3mjql&QPQ3 znEI5kjU`96mCrm~A&fP&3nH23M^~9dUt&;C-%&l;gVzqCbqZ>2TvtH<059K5`=gV$ z{j-+lylp!pjwiO{ceJQEb)JM1&s&>SGZ+<=bK*M9fkJRzW(boN|skH0Cy?eNoQsR?P6i zbX05eAh2w0o5CA)xcqSY*J;yZqT9ac6qrs00HE`_wQ6}4N>fT2<6i7WLh9o#e~IqU z`@SoTe>)p`!ozPek$dIBkMP&@3xPIJ|MTO9VIWSLK4{rDp5onMDAuVSV&!{=|5t*_Vh~Z<*H*#Flb8m2M=w{s^v=*phDNVkTDr$ABRY&n-4eU-<)?9pyj8cv|diXA* z{O9fLg_eFPr#(VQ#Sf~L`$C63C~e4l$Dg>)-!Y=N>|7g`=n5(S0GlMdGal58Tsy0g zHuzZ!O$k&_%Dyf;{oNN>_(4BXM&>AO%jYgyhYCKOH_7D;q!UDkO=uI@TAVuA*PR%c zOOCxN6S_F(+-`fe#@c}M!CX(vpN$hcl{Y5#yEYYV*>?*f#yO#$K!WzA~Ca`{MaN&9-DVDXJ7l zLkdbO;UK(Ja_+?*!bz_(Hr9^Upj53k;@;N}wr{d*9VTZGJtKnU?4dRLJ^JYjjHFC* zJ8MAQtCXLTXDq;LQc8%gP!2lU27x1EE~PmynYYKT+42T)uByN5T9(;$T}T9u{@$3q zN_1Hy45O3IT*zyP+Ek}Q&9k615_Hy`{V`sz@fLv{$np~nVM?V3OijCjjS0oFUrask zv!$g}EM0BO&-xPEIIr%AgR!#z0Lp&Fp!T|P9(vv05;yG%^8R7*+#KHmdMjJ4iW$^0 zua@(zlZ>abPU*+Fz7Aa@`JjXx!(ZaXL^e&;`yTbPvmV+f%egq@Wl2(j`ETubhoXArj(zP* z>ja*$`qyr6up4;ZH6Z%AFP(IXxV4c=(uu~dVxp-r=p`hi^veZsN&>V{3id0r10IQQ zF&1#qCkmz7>LRKMRX=i`%y}+>QBIX+YC9={xTCODT0M^HCh!^~oZPeSX*BEb$+xtx z(lKPkYQ>1}*9P8bl3AgkJq{nTwW6n)Dz|kBb*I_{(QUXp$G}b$aG*-#UHo9H>YJe; z3KWz2abJ`gt&%-jhe=gNqt~Fpf-y8(kPb(o>w!gcv|1Do-OB;eyd=?NHcx~agY3d( zzVcL3h-9HaX#$kOi^fD{q=KX*W=HL=4H)-=Cz=SNbw^I1ni|s<1}e4Uc*b$}W48^I zASSg?pw>w5Ckr!GQ@tEhhS=IyDYjPXBT|~&Q2+{c!tN@ck>FE9nj^M1ExG2!Tayj4 zm5jmCX;Fx&#;X0XC9S;_NS4hGpQgA)T%{c-z)0@Ef>B)RE!9=ffkdbN<`8;1(1BVE z{j|YXmf9*sC~5TIL$yRN3r3Psq`jwCmY_mU!f1LQW(ra2vijYU^RBEU6&mSELO`!@ z81nJ~X(Qpvy)Y}t<+2hkS4(xZ`63c0I+l#JB$T(Sw1I%467*J0wZtyv1g@%Y&1cQ* zAZkTNzpfIIl@v?KFcGea@;4~+f94IkeLi}2rIo6_~tYFpAm0;*eKvPfyV{3%K$ONv3&^6o09H2Ml5j1NUGj@eYyqoJudiy zs?Mdg5RyJTf$jZxGSdYLD7uvd)T)^Z=Z_DPV z_Jt)Tp6pohF05s9?{VU#@IE0)(L9x{O9>~8&z_2HGfsv8uOFI}AH%+P4r$I}! zr@Jo9Hv?3zdO+(<7e%;|OqTaux5-kHhw2m(XnaXD#13vcv1b^Tmdv|P>Gj0D5pb?c zb<(FBN>x!*q~%=w_`tVWH8fpy+ZHv%2Xj~Jm^S-ccZK(gA|4pabis zGwqu>lPbYV>b73@+qU0oO-EDRTrk?(K0f8J?mf7~WjlF{$(w0-s(>msYDxb986^EU z?pwT<%F4t0rF=)S!8Z2VR*#Lkg#x;;pfaFWPh~N0F0QL3V+bj+H6AtPPxI1NkwC-2f9Bi9`wGI7nZrTlaV7vRmE$1 z?m>vgNp()Hfx%pRZ?GM?^RVMXx=e)Y;XqI7OT5d*S56V+-O@~pbz0n?L9?O=Bb#m}3FS&aH=Wb?Y$H;2v%`4(OR8t~rE*(rY z3B%T|_A=S|dy? ztTf`AG+08&(xRT)9I@tIwZwpGLOHZ^KqPTNHtkYroqFH`MeNmfE7okC$iGp1O3v4i ztA^&{3wswy&V^M8TQ4C`lBH$$h(S(yka5X9vByU)9%AAA>Vf*dQu=H8iqE<&>21(! z^5U-bXdw<6UM%JE1!$GLwQK9``T7)(f$uM`$+*CBuQstqB6H=P_=gA_Y zrFCNcxf(u6)!;Q*F2HlfS%JJ=q9DDsKHs`7cDZTtWJa2{meoZ;FVKue9FTR8aN$L3 zZVk=OhCA>6wPLwi-v&MY-yRTJR}LgpJ*VSHTv&3zMjk~T%rlDJM*&LL3z`*J>sJ1j&(q`FOj@GMy znAvH)HPo#@=T6MAX}h?NI7vJL%IqyJC%J1`@e-`o@Z-)w*c(HXw@u#KAi&&^;V|<^ zQdCMc8k!MN@#2qZxoxlR*&Ba!t&=ygudsM|r<$L{3!2hw(x0B&A`-Pc;PTN-)JdQM zzWi?5-=fD}2Ri z7)4UbNFa&@eylT6ZYo`flNXCO6X_&3E!LX~>?c)devBeGDr_$YA*tY@h_2CU08}XF z?82d5E)7$ir+f(VPLt5kkaEJawyjZ9G#)F_r!A@N&LpYW>jh$_s?*qOhHYm`Wi%no zphY(<$h(U4x7%S5lK%jfl_&rJRRAh9!Rb_`q>r~}jS^McXx(i=PN*hSRM7m}Dh@Y8 z)5I{VoKx#y1FX+eHr@h3dH zaczN=3n6ofbykWf5SXIKQdOEtXgo))aJ@op(0a;nvg`iM%OgV+hvw4ms)f^E!E|>1Iq&Y>X0!2tQ?)UY=jRvVqiE|+U zB)!7|Be5P+i7S}u&Xpumg!j`4zMo2ah`x|WKI(-oksXa0dWNP{NNHf?XmhR@Tn}+- zSgV%4PfBR5RV3mS>gYHjNLp($ zL(mG5+kzBU4HidHfGF+avH{G2(*s51I%=fRS=XinBUK7Qf}8OCHSNcjv{(sI>CoX5 z8aRI>JAVprni$hNai8xg%l8NA#eXNoU9cnEO-4mjaT^a6I#a=fDcNzFP^eOxNcvO< zW+WNKR`RX_7Zoacg(*iSyuis3Ai!lKuSnqjyhUC*vXuK~X=h&&nRZ~5p)?&q2M(>+ zI%t;3%kQbnVI(7uIl@a|YDK!z!u16Z1dEn~nCVb4BNIK+vka@lvJCVU4>r+fjIyr%{#6Z&H=yra+lADrq+F)TX$~HWOntb}Bz-Os ziY|Sfc^H%yLQnfS52y77M>qvP-}i*R)JAM1d)JM1-whWt-k)__?ja8U06%Twl+{8? zNF90uhItl$0H%1iAD9cM5Fe7{hLf3jW}wvd$P8t-k+d|eml-ssMvAVh?1t_mXYJL~1am~4>19Ivwqq{OUHyIdr8Ygg!x`F@%jDd+lt)+; zBOa=9$SAYqobom9$GXB(%0n`RBT=Vz5|t|uqBsMj<|%}zmL+YaYOMkG;KiteMdjvz z4_px_lh+NzsVZgCjT$U8{_GG-l><@3n~B_cBH7xo)KygvR6I*{x|TPsmLjIV6|EQLH=4Y( z9}9+^X<8g>UANsqpTUsPa+l^KwNgqxZU)wzXsB6)@=ldFtp5OX6OMp~Rl+mETIwYC z8B1%@5JKxyQcieZ%uFN(v!@V`Da?|kfA8oJ|!i#SE$vqAT@b2Y{uaHR6x4@*W=p?vc~RYnB_m z%1#!5Q}V}eW%O!Isre}esPVZ$PhiDUBJX`^TYk%`G`Q>(1^BG)wxijteb{Wr)k{eT z2StV$b}?_NhSSK(P^VbXN{T1|88 z&k)uYj$o-2R{hhKifJ0a>eV6M_Vf#M2=ZiH*5Y3}ThiI=?>Kd1d|vVZ;Fx9h^l(7e zxRX?m342{Sn3XxuvE13u>ciWEI28&i!IlK{xldepHa(VPNg5xOF=#4nr68pyqpoAF zC>cjw%xFlW+_qi5Hq@7fn$bwk-Ypj)a7z|LAtdMK015~>QxPq;d}mUv(%-{7O&GJa z+q}5^sGo(TTOVy%~(nES}5$k|-*r$xNFT<$*QW%9zZiPz&ko@2v(ZzHgloXh>+qTf;4WqcGLQH0xQ}7vmGy97KCH$n=Qxmf_)xY6W+srw9IKmZD%*Ata7R@<&2?;qw`uW;K-OLus* zD2*)Y)u~zl9dTe;QuH(=PKqj2e8f_|#a9W=J;I#XYjWROes5*+_TVkpW0a0XCwX|;ZHBsp zYSU$5jhA}fukLsD{p#N>Ft+uXl&{UWha0QQ3rS9tAQ}^%c^pN{c_Z$GO>J4s_$iA_ zJ0iv#hn|<8gm{F(LR5s+PN`}?5hRjN(~B95G*MI9;_X&JL8?x#^sqvtA$39=p(;;t z_fTWVu5m}0wYw@ZoMrSaU91vyigyekF1ku`Eey=4P3bJS!;4DLQlZ###Ir5|;H$S5 zq;XPa%w;#LRpQk1ib~i>ej`AdD7Gr0LEAx!_Fd*VgTlRSW{ZngLU@h|ttFNNt3YL0 z9;c>QonxvMD*D{Dr{6$1xO?sQwdx}9G@FO^`hEcO$q)Ri+x6l$NF~I z`*VD9U8ipDW`garTa@Fh=?PP+YC7BlQga!K*BQqD0KavWubB`4)o*w z(oUs8ciSv$b^7lXC?%+}=MmK3`B`;k^_n=U%MgG z2$)y7;=4CpiODLF=LmMzNt#+ zdxu;`T**_ax?`dvK$YExHG+P6BAHv6_>>KBfGOOu5ycX959$ew$#F|GN=AA2VOe-4 z@q%5V2a>lla@(-km9`;RB-Mmg;!A$%eHY!!i(H~_`uJ^O<>@tSYtdbIka{{?|I`(5d z-Y}ms2Up>ETL)|=XtcYGx2V1{q6mSN2GWF@&}mIFt~5u}qv%+hVnTc`NiYk|fp0Lx(C<|7FbTkJn@Qj9` zO2FC_amUmVl!aIKs&L97RQ&s)FfNh;-(OWKr^J2uowscQtK1s6q{h^0a~15S7fp(G z?bxQ_J}gA9N1T4~%;$U zli^8B){?p<#QZAKdkMrf{+MCET zQAGhQv93HYY1K9J7D3CxJ5D;s^7XJEZo_{romw~P;#bO#&lh`Q%U%pcxvL^R@$`*A z{eSUXolb>z5l^ojIda`s&tMS2vSBjzFb zuD06B90Iyjl~F^X$Isr;W@fa`wSm-Yi3BgSUz=T-X^`0_cQdM1Fcz9>G+e;^yljl! zc|7igH0(K6s1hVuBFSOc=0Zwzms>7@sUUPBpyQ&u(|#neYg$2RBDB?TSLFKv^0`^A zBn7W)4FMECT0dvs9C;sl^LF63*UpphptWclh%m4aR~=m*H~>31$rrK zQKw+WX}FyHzT*rh+=q|Qx6Qe49@W^iV64}A4;@rX>e?K>9`v`t{8g;Ab)NdCU0CR^ zF~lxYp+I$_-k~QvTKhuA-);%6c(u9(k^X7!Wy5VNTw^qIs88BgJ`J0YRmGRB!sg2J z_PrF3=K(D(w%8-a1afMsWu7`S=WJb&5eIwz3xi~^$hQjVTn9&rt}Unqp(R>{MSG}m zdzEC&;+C$GooX{^YGQO0fRqG+dSWx6R;tFw&`>6+Ss^G{D9qxIT3jsMZI7p` zG8kG}F1hp}u;R2)$SlX`7@XFiM4siT_)R*JXcU!C@>Yf@T$N-0Rp&>Sfi^~X-`hCC8d zcJ?=s$P3s&s^-IMY3CH{YSxuz6gpD17;x!oXtr2@4G4ODefR`tx{Ejzk5-gk6vB)~ z^AL@wCsqevD4AsB6UZwxQ24 zpDn!#S*1+?!^pTQZc3pq2oJF>D13#IM~@;i_hG}hs%_h<(vrE3Hl^)JAP+6bia>A* z4+4fm5%cj@-GwpeBhsOQ$V~!QP;kZ7T0+WELTV~IFlf_q;)Lu89ld+7i=zb(QCh8{ zl?3VqL!|~-K?tE*B^nZ%_KJO23a=K?Z9;?*T|o6eP6mZ53sGY}&lP0uZ0c9K_4gFW2{5KyoYy^B!S{Oc7F+#*IqP#G_N#1_8dZGP>lj z@CtWlVj~Br>VImx-l;uG?N3XbQ7?JlTl`M{0C<%i)w1^DR+iT~;Vo3^vy*J2Q;sNf zN_)t}4$<4vUtTl;rFBEy9o8)|az_`((tgGA+snJYe#!L@x_2~Z)vA5LcL#JR>2))H z`N3Y$b)LBFd2Cw9L|||fTtBw04oer-J}RGWYN9Vw7q;(~*rMOKS-9IT>qM~$1S!NV z4FENEr>9Q*S6vZ|^sN`P(N7eFNC+WNl9!T7R+<#$Q;!<1i1TU?(ICj3A*Ld-U47C6 zXaoXJ(m=<6s!rnJv9qYBi=1=2*hyQ~XAwXR3fky>%EI3)^$yL;1!+|-aZE`t;9l+W z5uxpEFpvyl=lv1$uk}}DMf`wWok_eo)WD!DHX0I1r~VV;t@-T zZPWz>1`wQp8RhRCapIoZID(ty3G)F9)_d^T?4p-zv`%Z~if{70y5zMX$kPgfbgxpv z&`2Reu;H`y2ys&nKcz?eLTaMjBrp_EMob<5?s*3AoKQD9cNSEeBI4k3zzYTzz7uH<@gZr5{u-EVb* z>5!y{igHjXPO8*&$2)H^CbSE!`%!;v96HS?tgoqE)3z^sTXfm`cJR#WtOd5!${Q|+ z6vmJgQsGZ-S&T#W>l1MnL9)o&H`1(sn0@JIdx3qt4h|r4Tx5rFx!N}@S8cZgYDR^Pl_EmJ?|5nWe^d@D+h zq^NrcHN@4F;#h-%tmSQ;6~mf*Q$}NTMxc7((}GUf`q4HXNeZPYOej?3XG98xK^^!6 zpela!sk|&Z3)E)}?;Y82MWb!)>(ftOE^ez!aoJN2G*+|NkX9;tv7Pr{6>)!ong~Zz z+%z7d7Ss89-1b;qa+Y z!_v0S zAewc=_Vc_j;@}(mDFn-llLX(W!!Jlt{Zk__wBM)3l3ZY1cZas z5ZR>%W_VX0WpNx-kZOZHo3OdM>wx>dm0sHGHnn#6FzVhS#Sc?KQjm5Wa5v|sQ1hQT6G|F-m zPl$j%j3tH7*h?Yu&ZSN6ou!$E5$6Na5SuUgN(0vUo5k6d`RjcuwHWCHt4R2j+9-Nc z3Fpl6f+OIEW;WUfhsqCMB;)k~%Xcg0(Sx?5-lg0vPQ6tsy}>p{Il2y8t-*dI~=Rh_Y7HZW1EDyYV+gj)1d{Es1vIyV_UmpT;87*$+(+` z$1r&0tA{jiOP=JrUvGDR4B1|KL_DLL0ttNQTzX$1*e?g|pn&{P#60dKgf)`uhZ;=en!TOEt(7(pQUxGFedgz!_n^E25>^#AUuNQDZUemp2k1$u`HyKi%K{%#qx& z)9J&;Fat$xd*or^P=%XnyV{o>=NVE0R20;N(P%U^1QAh>Jk@wzu;`CTyjy1^iT2Rk zl8+4L4uV!YR+Ev!Y;{6Nu!CBr^AVbrDW;O}NOaR$ydYOB@UV$1C&HcfyN2M{cV}E> zlsz5f=$8rPrRa~evD%YRdi5FM7{wGV+zgmgfeBJgw`1%wz_@CcW5pIykgDfOV2jJjY*RDg$Cr?PUO>~QOaN%&l?O$t zk_b=(>B4faF%~K62&H=A8Y9toBTYg2a1Jz1!?aHgi*6HdtsmTJ3qI_rht>?M)q8c_ zi0!pb+k|yBHRe1GfbxQ(<2^H1Q_h97Jc?tm?LC1j$!-5#lkbAIDlJgcTg!ZYyHAio0*|RG!h&$R_=n0JP!k01@o% z!|P9lA12}+rwd;8<0}^k@&?Od!G4p=8~S~yuX3(LAz+DXw#yQHv@$;Yy1J+p5lWHR zF(BTceG5Ce#=c9S{y?aP1Q`>bi3%HPlPXHW8et-(MMNY4_2EXcs=<-Iju^U~qOkLL z_h@k~z6HyFrF*Y;+)`b)Y-ZQx!;aNx9h%~NO3(KbO84RPcBCxD`-W@R5g)=AY5gN^ z32**aeyR6l#@dDWjl$&`Jb3LTmKkjp__5H0B_pL3+l>DJD8O>eAEzZ`zw2fp-CSsY zTA-W-4L^r(i6VhO#YfOoqCZA8MupEgKX@x&!ijnQK(U5=E;i!Ul1K2Q)P1;Tl|RBn z!yo&Ax{I?KlXJdDVP7tXP*S#v$PJQ{UlmVGWVgQo1aVu2-vrDEAP}rx<@H0!yG~KH z8#Ss-I4MO|(pE#1PwteegWZa(9lx`m-z3znM(4j7dmrHd2x@d}k5>1s%7@V>WJf}x z1o@94*AkT#Wsp!b#12olkVIz zm;V5DPu)jZ=gUIUhY@0M)}o$bYlLLkr;$!0?#n*vQBON<)ew_rkdynsDHv+~p(x-} z$h)Tr;uE~@>qxRi+zW*BFr_a;VN|V8PM+*Sj8uX`&S6|Xx*_req-aume51DZ$In%ViTmkZ722cfTG5 zKB!YJ5j3Wc0+OH-eVCtiD)ebUsZs8nNgnAmM4#MzcQQgEqjE`(x6KV%$yLj<4&%pP zMk_@dHe(Yjx_Zb+&N;NBPI4Sw@KA++Y4>X>N$#!|YcC6WixWRL9LZ?e63xA%B9Ql)oQrMs>v2%rg+%x9T39%4U)q$nSccfB5<1MN>N=NsHSN-YhVN|@ zh^bGDHP!=Y0o(M!(^6X$1b1Y6aRGl1QCHj@`Hi$GO~)+`xRTAn;=2CwjVtw&hCL_2 zOL@SW)g6c}K43~j*L9JSoXFw4q=8iO+n~Q)Rfe?e==1iv9VcUg&DcmOM8#%()h zVjS+BXsLC;SOm4Un*@^RS=7HY)4RJGg}0s2qn9e=eo)LoX{}U`vi2pvbON_+3t_hN zank8ISD+ZK<};7C(zPo^*_#>*(bdyHk~cozx7)WodwsUfwV{RFtKuw*mx|(`YmnUwf8&0@<^d%&Mn&nF2 z##0s2&_n7}UJG)y@1vtqTpO%sFG0I>1x99vRcqQh)Z!8BJsO&;-L2D1p6H;Wj^_!E zZW+s!<*A)x%PUv}iuMEDi##T<(Py*ieNuF)lWwBUcH~>E`73RRp$K`_!BGUABA{ZU z2Va~(S|&xY2n3E!?TxrC4$2WBytSeB!Dm=CsLQtzIZHP*)lY4_ta1*XOB}lZfIxr0 zY<_OnRl*2AJ7QX8G&8Q09}vsfYk}KSwX&AGfeBsdZ1+GO&Mhemaor_5WZImS4Jz|& z<)mD~MO0$HL_6&u6I?#Cj*>nT!-!0BlQ`SQay>;oXYKf#ir;s!y5KN(L{<4jih#Ep z)P;K}XhtG!Zq2p8J_SqWv4Zl*x5QO2wo|X)_~lj$giktUsW01S8suG$vW)uVXfb8I zkITI48g(j8Ft+;=VR)}bp2g1oXxw{3^UD@1Cg0lwXpOe~cK1!iwNaN896?foQUDrK z4Np^z=WSbD$BlV&`ip^}TqC~kEGOKNwXE{n6NgdyRO{+u^KIE1hm7z1eYwQBeq&}N z=#C-PBq+85iA7Rs7trFVvfwk@O&!BXUzI}QEBy-kvzLSp)% z;|nZ%ZLfaY_tO`*E9MvQ98M5qSV=p3* z68=g<^otT6?BR}J<9Y0fEX+ft3@ieWWkM|iKgvKL;`jq9O3 z8az~y$~K8mIS+KgaUXjgN8W+cHqvs#J-0A@t4&n?{{XZwr79pSq<4hW;kM<-{JpcJ;XsYCw)?VK7~mR$YJEW-NP{yA51qh*9JMg-a>a5LM8T z-CRTE?ce2fH;S?CY2j_pOqv3nd*f>|+J1f|$|UBG#pJA1=Su0rd3UVIE7i-bBJ%*UIfuTIR|W; z7UH-?N++zTS(r zZrOKrO5a!g!4Ms_5^tBcOQ90o^CGFgH9C}>k`#IlxWc=+eRSws1Z_U^hQCVeU&}rd z6%*vVxR6z>KI*7DuXgd&i?z{qZK*CI)hXliPTWhWbqa9i5u%B&zZ#C+!d|xBy>0_Q zxz~Gcd_%9~bSyf!{isd7y*6FG;cuHOWE-;)T&$Q*%vw_FG?7ZA8g;H0%V4i0Z5a9{ zxEo^{TkUrQJpG9mrj#g~dx|Nlzs5-YSdqxsI!M5Cs8lu+KFe9Ug9EGLn>(st zcIzraZHTq4^Q+|BCqrGpS3F4K;th(Gs;cfw-!kC4r>4AR*I<0c;YnhOZHoS+n+Tk zn$)>|VyM$-a3N%p0P!d*5ji`_RiJa28#-~z&^P|*)uxZ=V0_Ifu6cza9#=Nwa`znx%RD0EoW4w%{cJJ_zeW7&M<*(m;`<=ePSQkI@tDc93o!dc{}Es#S=b8y*jaYJfII+D9<%OF?$ zOi(+AZqsbXJA+&nq1lUvi}@Je-eFO-t(BqJb&4cA;2u%^^FSzl__#UMg=T#&is!1F z3R~Tm+lBJ%>(>Of%A+L=u??X{noD3NiKn|fH?Xs0Y}1+y)=EpMx}C{yw9#>8%hk>W zxih~Fg7T#)YY0nCs)*H}Q&W%~aNjESg3x?ahTVb3t;2u`tp)driauEcj+vY-D`*j+ zeyHq+-fbmbOG#L%)k^g2&kdm!G)|=|IG-hMzUr0$)N`j#qZT`HqK1@ei$@7kfTv!k zKh=XuLv<;sX)dCYq@f#H%p;YdH~Zs%e#tq-h<*@%rsrQ^#48$SlDvYkPT_3;X!xDQ zN2p;*Q66G^M*aG)5}XXTVUUUEtP5krp>j}&XF-<9tU@s}s=?f(FHHB$fG+l-jacNkB?!C^J2y9Jl$3+?fkVqhuttj;%=h7tycCcE`fyZl-q9;7)7Zu(u|4a2#t(2&EnSgDD%t;bRGCcXC&kV>jg zh=NJP{=~vboy)Q(a3^0^HJtunx7<53Yd!G_Jc>8gczaHZEs27SXrNyMa+wKk?jJm+uX!Y&NkXV3D4jnQ zD89<{!jd_yI+WHJUjG1uRU2ictfjp=3GE{Z`SJLLD;15$-6}%k+afNT(`CS#jWK3k zEo{kjogpo|Iix7pra+U5uI|4K$4Xjt4L@Sl`%{P#?h8>XNNvh(-|boQIWDbQ0jVqO=YG4XWrV;8Ko5JG z033NPczR^JFO~T>a_;T>eUi-EC9+hTcaZRm+-6dwg3D-Z4*&}3_2YH!C8x+*M-&aH zaTVuMxWBOS!;-z8^5y+5Ye@r#(4kGqyDXgHezWXNkF;3YKRrCP0p<-|n){0_p=&xt z0V*oY;@xRx{60C?GC!#F@bFY?{FHmYJr$Hq=mGq>(5wFdxpu{t`N%$>y*61TekTWt zn`Z637C>13=GgN5(2xaPbd^x!pDkyS8%gh|B%i+svEo#@ldoiNA0B z6$9OS4*j!sJ(}wF@3^%Cew8fju|4I&Lrnlfg`HXfP@(CK`xAX{JN9{l~ni?l?s1E*Ae+)TXzpav~9D|c*h<@eXFKEyjT);zo)yo9x1zV zA$_FPwj3X)9QWDzx^Au+{$<&}nK-(w?PRAGE@gR*#=6@zJg+n68B!3CXhyI*G0;=8 ztSgZ01tki!ZWS)o&KJkP(yM;vZcw46G|p;B!wkemOl#ZX)Akj#Z6+4vB0djk`oUdg z{{XZHY7=-P(`M&rYah&ON}cXR%LD8CNEqaf)i{fpnfh^GLHmtvL4clMeT96dE8;la zn%XK7DN6QMnDtTG_2LC7*@1Itlx0FLw4|E$U}_Mt!Dv9JB9Yva0u* z2!PZ(aFcRj$MGkvqSs8p#_+ow-p!jwdAPpr{N z77s=H<8Vuj39aE^jP$B#*D$CG{{V_H6Mvm$yGbo2TQ>gQenwEh;M(}BNn`w=`9|Xr ziL$weY_QYmO48E_enQff07Xh&0<`SLNs{kvv=1%v-BJ2@E}YxHm3zCUhk5Lw{XX(| zNBo{|2nbC6b%xPb#u~Mnk9MgTuT9^^6Yj)*wW3=7PTcQ`Vf7`}KPWp34Xn=RWCx*OuFJ*Yj;}W_VKZ~lmz-z4*C46V_XM->V2gV+HzGs%rTs+KW{FGdx20PZ8WZkZdcx2JGbDH6XS5-$R#HLBO+EMzF$xf;_ld}K_F*KiLN1pQSE{`ZLY--k5`e}C zHD6;NmyZ0|JbU?gaZe&|i?dcOxwb6R-n`tLRS?=3xk^n^J5)#j0hu`rbN=VY&v%wF z#wtnuYDx_382dh=ed-N9g}@(I9;x?Fs~&29c*$YN8%o5bLgTHoQlSb0UDf8e23~vQ z1-M5{lhHNDL+w4Q9x~W83cT?vo_97~*M-tZc-2MR`tysKweL>1M`YXBZcB!~HK2C; zbH%peetw0Wc49z$*araes^#}^MUf1}ZrqVA)dK9&YOs{|XNvsO2NlwG7@sNCOlM?- z4HQXAx{V|C;i{RUDz!4DRii=d%Q|~8BQZd@c zUJ72~r`#@GnhMmVHj_mXdMcexByp20!?;WUx7MY`TL~&ti4{*!J8?P!%pU6SMuMZ3 zR=1c72{cF@m=Hw}#^(nntL~36X-We~)#3MjI0a?4h`Fiu@@)LXX>-;5>2lBSxH+0vg9RNKKxAf}+@mKk3( z7OJKv7|HH{a7gygR(_yvupYYG`AT)IyP6NqHwZHlo?xj7EFmNhal~se)~-6V6>66Y zbpSmMA$7;lCH79>;(5kwr*3x(w22N~COow|>yFf*l__MBPie(Bc9z8}3v>z-Yg;}> z%M+@2@kkr8(1vf9Rvcc1wCjj8IRym}ajWAmgYCsRg~c1U7GcOdcof{^*fzH!?JJxM zpt#&!fQB#VNm9#iFrpEtp4BAx_F;Skuj+FgC@Z2vbw?jWZ(*q;RSvq)D$=9$j9MKX z6+w<6AlxFB77|D`=x{D=6s4s7K_n0dZg?#efGYrj*8$B56+x*;)t6>HRB+QoT&xmJ zdf>H72CQlsp6m!-qVP2*0EC1yN(okDP{8s@NZN$9%7WID<3D)^HZXus;`?;74TV1L#xc>AP~f2^(vD zZY!WmX-dBhlOak43KbbwFh@*OF!nh2k1`gITG=-qYc(12F5YzHmbO;*hqsj&%CN+| z&W_+Pr^`Z~G3(lt^fc_lOvWklRJ4$$Hm>l70wa4tuQeHSO!0KTzTjT8Fxhq1@<qW1D76ie)t2EiaBg`PDZ)GIVYb>X2NV-TpcI;Q(|_C=!wnQWfmMj8#n{^IGqD5QP2G5OJ0ys780=V4X-mLa+>s zv?(TZl)DwRh%wWo4=|Hjcj89fq%lR_$ycm~$ut?^%@aPPIA=)sv+WpAf>DFYGM37z z&|xwXfyh*{QcZgIVG`7HD&NXA%vZSM(j(Psmd1KVzX`8ZYEYcAraTEiO3}pBQ7P-& zk5Mn?tSKa9p-SKp!-5TH87$@P!fzEust7i8f-^n1C~XQ^vQ#YSGz1L$Mm^K2@R3Qp zQxv;8=_xrTGdU)mnzO^qO967D@7tf@fS#6umoKv%Dh?HzR)e9%uAmi{<=o)_OM8mJ z3P+gZZHTWnnxLkD5)OF|{0rI?E+a1^bUK2WYJn;$QWnydlS-@#j2)(`$lJK&JXR9r z8cF()>=~detzyP8`c`7RLBxz5=efVP_D#;u4KwpIuZERX~YE%4-{#-@g3S8Q@R+{C@U5YU-M~8bu46R4R zq%!_Q(w*3N*7Uhk^D`IP*Tod&XAHhc-jabea^UWeXX6l1HrXt*X!I>? z(H5&+!f4I5OJbGp%M#X6=%VuXoib5+l}!lK_ThRajdfI+#QUpg?0w7p$-1`uy~^#i zr7pp4iy^q^`M^O!pDV3f8Pp8%Ee#v0sP1fJw~Y)ApyrY{HtV_W>($e6ULEr%T^&=7 z4D_HA;X-TCC=>v93@*vKiW?kUcCmB-ySKs;zXAN2`9e`nm2Qp4A+!-da*)F#{pK?J zk=LGWIe&s3(C(a5y*2%6o^VW6GqQJt+=rhkGi~+~Qi7xY`x?WVde=H!N*uziPXH-S z@*E%%5SAK{pgNO89=}c)<)2833?&^p=$6}ryS>p6L%v8TTAf;QAS6_0QH<{J&~U2M z@yFiLH7_>;6y$U`R)9zN+%pkOqX|QW5Q)9cY$W zgv(5a6tJH%qyz7!7sF=2T$RLEQp z6fDCXGgg!uvtJL@gGAjbjU;1?|sf*Tg!wCUmQ1iZ0)~IE!%-!ctgtsMSOYQ{9PZ=ax%=E2600 z*jmG4NN%bqw@hk8=*_OxRUv8BMH=LDBNlo3n9eIW#m?Dh4e486BbfKeNNOoeMUq=q zl@(XpTsXF#u%l!kG29VFqq1bM(ZYJQGp;KqEJyit-qIy|U)qV;<=!#~3%q-eDG<#^1 z#HorYPt6BXqww}&RNP2Zxw<2Hd~vly&4=B2Yg4GyXsVOqr)DbFa#rN6lOYKhrg~IF zh{y(wz-2`D*Y)DTAyj5TM1CqCYz+|Owwm=R9f#?|Ga`|*VM<7G^qhor`*aW4gq-@R zMz)Do9LcJxM7Eho%85Yj&$|sJ8mVh~(H!~7Af$dK3GGgtBhu9KtffegJybpOrCLow z2%xCLX&?kqNZQWfuP~>LzsZ}N*_*P5QmAaF!&C~>vrpFfJ+-HP4{{4$@AZ_HwHW;*Um{xgCamCx#xmrp>4gO0M@Dg%X{(}H%R7y z0+#oO%Qi2%BaqT9FL;IYeEVnK*-xP zZQ`3RZzp8#8+%M!)o+bdq4x7SvZi$ye-7w0Y2 z1b~$qj-#e5@p)q08ma{DmP_+N%~>mTwY!GZb|Rbd^|DkLT6(P`Py{R5F`V2^MaUzX z+uLl}Y$$kuqRV$8Jp?r#U5QM)4b383X~+bcQnc)*CZ@oLYg*M&EO%Ss_X~IxW-T|1 zZt2_2zNC>BVit$epg|Q)2<-301@`WSNW4vwMiFb%mCc>DDox6AO#KTvi`RQa4 zTDGi(ec2pZGEDO2WTTp?VJ-yr&w%Snp>`hNopG|qOr;b{+VV^}q*qtns)?vM^c2Hf z-H?slL-&*Iv66Jr7N6bGEp6bxC5t+V}`&-`jFxO|@ad@akV$RbhzO4<}35lGZ4?Z)@o8}{6{ zrN>tCTow7T#6J;m9}7L%IF!=jdG>wZ#ao)w_$scu+itkrn-QTZ5l)J!Ipv_%xUg;* zO}0g*w2j~@)Ib2)fX56GJrg|GWMJ%U#7cGJn*8Z3zMmTHD1NTqZs zD@h6lfC7>T#BScnJ-Zx#y)T? zV=nun>|8^eh@7msPMd>3eDJ4^8&1+iz}6jzPGDzS3Nf=aS1M;(*c`G)*+; zo^|cUBQJ9@>?`y{>1l&4@imx2A>3V+KxAB7;cH1N8uW;S|VN8bs zTBar7JR`6WumByHyKP?Mi44w=iZ@WQ{^WjNXMozl0Qa6KQ%96NF}=l`FyHU@*9G*u z6)ag)(_so}^a4s0f>czMWI@EP*zObZS4FLFx#Ft*(Cjo@V!^MO^_A4oZ?HIwq{t+osC zTeHyW(WAvjyxDJZFDGAFHLJ})dr>+4{4_iI@q5|RhuMm9C1PFEw{%%|1Iz-8aLvIM zE?48bqwg)Ku^LIp9Fl?1A(IcMF0CrTuv ziNvYi*=*Wk+Y~!ylXkhff#ynbL2Z=k6rxhtsi(gbx#zeNnotF^W9=-TY|ue2QSJL} z7&8*=Hc4Up(AXtKH1^_h*B~7ht3S-(-xa_yj%%l!Dd?V0IdMcy`Mvify{mY>#y`oi zT4@R?WvZdK-wxERE>psgM~6D+j%nQ;?RGmLVYg^4Y1MrQ?bqcbZVUr%?_B0e=GDKd ztC^e6Y}~dc^xaz;{HD$8Xv}gI$I4cw)U;7aQc2E=LXx4|o;q^_m~KYGJC^7kD!y`W z3?s1ldF-BQ=$+Dj(e)6f4^F#j8>e>ec0O=NRkLp1CPqnFMyDHNLMUt06<6JgmvHiC z+8D@fYVba_rS?xA+o|UT)TCuqm*YH#7gkaVPx02c)orA#K>8Os-$X^jnt2qp1{2FQ zOJO2|$E&{tl3gmKEMP7)Qf-T4$-K1bjKh+pG@VIlrj+79p*|%8LDLm0-1{MrG_5Za zv8KC-xw_FM%(31q&8p^@pDZJhrEBb6MKY&;7k%V&wt%J%!B1-LqBtb}LYDl?_svLU zw7T?o>Uk`uLN$~CXfXc(4;YRj6<2pSNpV^xy`}`4wZQH%2By4z9N8L3NgK`8(Z z@^JSfdfRwQ&mf{OSZChdvKH}i0d((@HV((&pG*>V^f~Rb$$OD87j%%@#WEy3%-vX{vf-mhczXnSB2Mn&$ri4X2v=CBN}_ovE^goDu^xf~KA;oe2%hDpT%hr*~Yj z(IvDCgd1{RRFAMN@e_86{K-C3O_4?PBic1$x1N}Yv|6ujZCLAyyMlrFm`48qXjlIL zm!9RqIr4m|P7+s}Ka?>3wCY|vAZn~+HTf)l39I+S}6(%bwLY7a#B{B zDI{b;#;3Nk_ScqCx;KMRKE=&_#M#*lZc1j)8E6l6Dl{UdhuNx=y(_7OdWho)k2XB3 zgVW->S`9(#T$Ixhoyb4?L)PZ zf#ec|71Og1?5@l0<2B*}X_mz{E6L~y0G?iy!g#Eu#}+(!m2~eU;?et|Wlm|SDSF*H z99^NjBuJX3CCE%xAVnP<)bLV?Aa((kAa9%3a>3xOm`MayQaJHCCUjXW1Zi<d2DF}`f0&);4+d@RIKx* zF}ujc#$H;tevP-+32YuypL6GHdlwz5?Xku!N|OxDVl>sdgXS%um44tL8X99B?%p3O zcVyPt5*!UpG+iK=| zf!uP!c}&jXxq!taZY@57gK6!3jgGzfm?mg=;i{WP)@I zb$aN-%0p3^(%UT!RBfaIS`Mc(iiS!&t=l8EF}N|h4GPtE%-dfI>x_&uHPc85B%My0 zQE-=jZi}Zs^VQl#xz^P?kgs1Ox13R`Wvv;IOUg9}8+%%MHgp z&hXt577z@NAQR!qPTR}!_4O4Z^KXS6`)=X1GRBxU1>98HSWZVrmf{H?Mp&=nd+&&B zWVjZeQoe2<$)4+;;jN9$dnw|gE>rYJ&Adakt{cyAN4C9VwWTVY>{(^iHWZ+ml&uLp zNa>3<+V4&`BXA*(p~r<)BYb{Tv)g*f*EbX_Z-S%q(p2dk5APgo?U=4&lG2GI$`zu9 zq10h#K8PCHq1kN-d737*CZi1QU+xt%j1-Z+wbKt+fZTWw(&gm*=qk?zkO6F>6G;>Zmp z9<^I}9V?H^JKm#GmnpR&J!`HrYX-PsBf71K^eu;PIJRby$3@t$&h-ZS=nvDj=4rA+ zH;uI*lh`F$oM-+XIR5~)Z7s^q-%0&z=?`(_t&O|6kQd2eIj9cP(;M2%8v3skX#?Bc zfYAsg0;H=t8rQA@h{(Hx!+=V8h`N)jKA1T=Cn7`}l?c=YDcOUy)n4-y^}*>cSvfv9 z@RUQhNJ#7bl+u+SsaTZ6JNT7Zmve<-3DOX!K|jTd6Zot!wur+Kh#)J(k=NhA~~&y{nW*!qy@7>Abw zV9C8;Y=6~8f@nrP|?{Ulj0ENR*wlSmqCVK+1H@dmg z{=5GG>OZvxz3<46g%`K|<=S)#x=vWnFWz?mcD^|O0HohwN;|t5*9v*>{{X1|)w379 z)hV?xaosl=@z+qM9-S%F8j5@?Sr~V1x9sC}TEGbOD)vu*%(YQp zDLqLBhoaCx6&Ni16o$cn{mX)^ep&CtbdJ?*ZE&J6mdPTycN`{oBL`E0*dwl5snBD= z6^>e|6<8Ii9}m@p{{VRbI<3&BnhWyQT3W$Lq0FT97%TMRj2%={Lb3xeKW;qb=A99= zy!>$Ik`ff^bg4xlMwQN$>4FXjLM~eeQ9dP_(DrBS$CAEU0pvL1Knc=&NXLp;BH@At zX{qnPwbVp#sG_hJPg4A?eBtefz;o|OkHvg`UR|SYMnahqUUl`z4$D|1x=^PTpc(;M z(z)O)>|lmtC?5Idsu>I+oz*2VZ9D$Ξ6ZJa!)c0FGN_!ZP;diNTs9fK|Kh*}-xt zBZ(dy5=Q6W`=fO&l;19Dn25yuyFnocKp+f&_hF62^Q1bdt+lW*_{x}DLU^e8cmDyP|oM?$YH9d_SBdEHn^cHbdC>qQjj zQK(XwLPpg=+OvpLwUMOVwk7@yh3PWjOKm8p{p8Ys`>>VRs}nqPI&XrmoL=#jwkELZ zicP)1hmv~$%Mg*gg{s{&dslCNEY0N_*J8ZeCaSSqmiq}+HE1g#Y0KN5D7(fNNf%J$ zv)o&oZImoL9;_EOZDy=H&gj$`A2Ax>Qze&uj*UcR+-r;G%e^nMZ&whxi@f`3mYHXC z>K&?%8?++xwXcvM+MT)HVg$=e&8Ie4Xn^XJ2T%uYEOK8q8<1XT3eMody7JBKj8)B0 zo9q;orj;op7cfDgY9|fUZ8?Lj4br_$ns6~r%znQ5>l;oQDrZtl;g=} zwP?Bu<(h^(j?_=5Tqc|?fbG>x8K?I4H9aSl~W_wMj~;!yL+jmiH5F>(5al(+`6!aIc04dD64%V^eIzqp~mGQ z#l;e0riJDeT_jeS(-Jq54xa^4i{6lFRoQP;Jjllz@kZydTWyI|!;|Bt%e9aSQeebs zpDD*P1eB#9kUL2jsr0X^znM%up(h_;C{O0ml09Wk5lo86+W%vh_Hjc%)UX#3R&fFl~<0Y8)2&F$Y4QXDW zw!tau+G|lvX1m{O&w9WkG$C}KV|$}zgPq3Vy+H6j3CGAA{f+gy<=<`6B)teU%MqNH zLi&)}X;o9d1;F6`Qrt#(r+d$OjJ8G7v*9VV3CVJ1A(ou-+tdo2__9uCEIppwP=vz5 z>atZwbKYWp@tpkYp^qABihSi&Syw;@F;1D{UfLrawOTb)47{zlzxR%nQx^KM&fEND zX502zR=d=72~(tA^q~x+%xs@6*Nt_N(IT|2DwvnLOTyv`y5}*`!(uuJD~i0)Ib&YU_GUY<-Rw>;N$C*{lDFjx8;bPjWtg;Y?ZL&sP2m8BlY6RHMLf^|D!g9e}%A&Sv zp;DauCo$~7sFE_(VZ`=oB%ZkSQNme;wyu{LFHL~g&9uU_zS0y6iKwk~R6(}^i@84y7PeFq+kid~LthKbPH56M6YJrZi4ZE-Ic(Ll(5h*tVl zT?wE{y@MWeYK!J9rK8!MdSO1T5`dvl(G{%*2v8RzIuZc!<=AL&G)EGx(%malUhif+ z8C17e916~avud2h1QiK}Uu07BX6ti9m3^Y|* z^-piV1xxi*FD0T9GtU4*^CZz>Od1s$kbW9r7lIR(sb`FvLw4)jr?NB&Li4RCa?20# zquyD5_miqn5z8_)T-_{_L*I}&`*C7mRXni%5CuK1Q&P1eD!e{u16a=vTvbxq%(QhU zOc+e9qMV8pjOr`d+;FsES1&G}*H_Y-m)lL1wn%LS5`UqWUtz)o1!2TwZXA8?37#Q5 z#8tSOgm$EnTog2`3Ak&YM2h5vCM1Tuq?InUS@?#1MhIL;Qm{rk!AzMVs!^qD*-n^` zm0ax3i5>QVR#i_;O6&S?+Co&sGJB}5;X(|ks7Ly?lV5Fft{CJf08-c|kO@ekvK8>x z-H4LSRhy|9Lt#h4Xh%_mQQY7{6gCH3&uQs_Njr+!43c~!Bh=tia<&P`>g(8W1rw|+ z`G3;GYNhm6p$2hY=95YF$F)wR+o$v zgeM0VM{Z{z?Pk<9p@}|oiS{y*vG#$FS(?2z-8UUJ^io$7pximG(CUt~xpb8J)H{>r zIb0(1@0DE0n%Xod_j9RIw@%@d6jmLZEpik%f~(Di)MUszq_+omY9|746^2 z(~NEESCV&joWJ<>X^?kvE+IEu^dGQgg12{d)lQn~ zSP_7)yG{;!qjot2$o^ccWHsw4^Rj&@?G1$%YcQ`9E-IR&smEk_I+79ypwN-uh>V{7 zp5$s3<7)PD&l_HnQvJh$>^BgA)!o?Tqo3WN*0^a7>XD+9Q@1_6f@5B!hbPml2?$4% zxZQ2Wvn%qFe?h|R-Y@V`l6I;$Y8^$V`vZMUw#eMXC9IO7$Rv-i29I}&@JIguYjmes zM8E3&DQiFbzyLuRmWF-3hYqCPBd0*T-qkv_>r1kDT)vjTy!lB_S~s3piyM@lN^#x0 z&{fl^^xTR9 zMJuMc{dkdf=BE&%hQMbcwSR}+cR_CKl!WzBPG<*h?IV&&-Mi{Fsx=>pU5-+u-4L}T z*0eZC?p~ste_%Q@P*<+}cXpcwJZSUTZA_9>r3Fye-G!{Pk+tN}H_L4KrCo|jqN;gqjdLZxmN=%3gN3(cs_PzPhod^vD+*8x zRcT!Mj7;3_+j$w%6;}z{JIiUh-9nb`d~dqN^~;22*mfI^6(zE~(hfw^EH#60z#{3Y znY`JzPM@p|6GwP)w(OMfW*xY_8Kh*S5H%>BMwpbv?kiB;T&f$3W#7Ht>oiQecO7n6 zvf6sKD|YM~i=Tcci<6<2RtLg5M70e0yLX$0vdwG6$vcH;j4Yng^dk9bvcwqI!8 z#`9JmVrQFJA!IP6d1o+kApJO;R{|lIFM!uS7&GYwERDqP^Xih`EERpuI8XEiX5MnLa!KuDOcV0{JJfh5Q8`NYJ{v@R(S(Oz9l75=vNMqvg zQIY24XyT5<+`EQD=yl1(WqL8U&7@XmaET`7A!%>8{;;I6j_?#q_svdCiS>vP(K ztu+bp0i80&F}*Pn+sF$!uD9(z;C=0>5*qZUYOglSj_iwXmugHf76KimNh7wj#3#!< zTh^-R>*!y1(pnPgqPK3nTufMPdBLLOcgiYMqB>U$#KuU#B&WvCHb_VUsQt2i#zNy8 zly8@LeifipWOD7pdx(QL6)kA@ICQyHeprir+MA*H(C0dsS2C#<<`b*SI=CGaR70spD1K}!8;m%2V;=kof!`h`f-ni}&LP~n4p!zXb+}M_|^|bI;eS^83 zGfXZCI`FHG!Q}@opcfN4ub(i3NyY%-MO1riR z+BdXz<;EmX7n~F{C$uWRUK}>rw^ltzi&7Hb%VU-D&W(Pl#m~2U&cxYUKbmd!r>~Cj zBcWTm0|bZN+p@rJT7@fLAnA9fKyeARqGYnV+2);mJ$#hMaxP}EhCGu6e6ApPb59$M zmyLZ&@q{EzZ5O?%x|?cD33LI-?+?6f#CD~|&PVFOp}e}nUeFp4eW@Jo2OGR_-9!fm z4Lt>O6J=fZ2MhB>{{Uspw>KHd3VO{ZgGllmb6YMCc+!2x9W{LvS!`r?Bb5sIyMVg5 z+na`LdxaY6xArHm30$rlBbX#hxe@n|%!wjHmGVMgSx7l{46_)8-S)BF<3Vo_u6?Dr zn-#bm9&~vkytPvQ06cna-d4u${D}p|uTUkrWZY88DSA3rnN<~%kUPd>yAT^ExHiPL z)%qR*SUcU=>-&DY_>WT^siLjCcgcIRh_@WM_DwFP>m5<9(cMO%wp3~eHK-uw4m7R7 zwrz5ZZ(f}yp>uA{?j}cXOCw_7@j?^Z({~)g-n_4NxL)FX)uP%LaB;UA3vI`pbHJ5_ z3R0%Ht@j@^>nwX>jQSR_`EJC$<=)dyS9oKwm8KANW6uz+u!l_-TjMzgDYz}^HJlGlez*`roDs|*9XLA zjk$96sm~VepP#t0neo8hqT!;;`_r>=*@)r1y^t}HokxuqG(AIdji-F7!^S*w8}P0r z*oq0W1nLW%@(I@=z(A={sj1A8PC64|?YCIM`0jJKNthOL7` z$XXT~krCG3VO62j()Es>(Tk2kF)h2JJi%Eu-k93o#Wi(ff%X;B4^X@zxH*4$@oy$< zYctn)+@rVPNVrIjl$M*F5rn1RF-jpRB`Qx+IvjH5#@=z`AfD3U-6f7R`d85Y?)HY~ zJCL->ShN;M#*%ypR#TATb9K9hAX}O&w{o+gv3Y%N#-NKHngUX@rnCSnyjK-$B-q#F zT=QRgw552hH+S1*e+r5a_Ku2If~#Dou{e1<0z0;t>D)ZUduh3skC2d`lZK^HPXO8u zog*+31xe2=Y8yu@mN%ro-L`KEtQ)zzXK$Q%WB@+#+M0N-cHQ7E8?Lmp7M6lS+9HJ~ z+gx-dmCGe>Q%E6kHe(+xtfi7PwE9zJ)PbI$DcRkJ#YOpm@KR3DP1@lIGYtxOMyT^i z(u614rBDyA6O2^qvc(JVEO>n0YojxZj6(irD@3!E4tAZ`_p2O+}1^u#=#+l++Rc z8or!!zV7W_HyZJCcPZ#|xB=q3leYf=nQhCsZQWsvw{Ke+p(mHIa!-HD-Lm)o@VW1_ zINtQ1aDC0t(})Oh6E$d3NErZ-kdaQnG1dD@J4WN%aCrC_Zd-Aw*Bo%a1sClEeOl4Yl`1;qztoR=hok~U%MO~CH$=#e+;U% zE;59-f>-6BD2+At_tzReIDZ6smMffTZQK22EWu^e7XJNN{3l*tRvJz;DSUaM3VPd; zZx+is{{RWQ%574?4@YIvih^lEQ<=pJBOGp&mktYc!Q5Q30N9;Qt|?!A?D>{O*PKcS7N!1Pp<+3gFwLQ=Qny*BC8lmRsU?~0sFq7h(&`kGl7$E(*@)}g zfJZ~didz|K_{fqcQQu2TEO#@}5;GUaC@#634}E!ARNyb)2}q zo8Gv2arBy`NP%@xaXOHYPg)a~vkqoZmdA>f0HQ!$zc}_vZjxA(yKA0QHBrD0q?I0l zMD)c&a1aG5w+`Dlz1?NH1r+jj%iVYT#rt|%cEp#qyON6^h_-~O4YDLX6`E^hvxRPTiq2|=t2A*1yQgZeYi^C>kz7tiJT;t_~ z;%NwpN>yE8k=dCT@r^YlCcIR&f?EVrFBj~>NxCAo-&IN)%eTZZi zy>KhkU3C1SHv~&g_vNj(ChqcP-yuPh3eJ>PJIIK)5bKIn4Hl0u0hq|+lYcVU;`YTL z<)^8AD*pibeq@^s)x;$6)lWZ|mpSmXEt|afeOA?6xk~UMNQ(P$TPTF$R)w~=A68(5 zB=|*12AHb$S83(la~;@qAZhXA^eYGF7rC2kSWS&%0lRlPa6UC$k@WogOmqJL3(8u9 zNjKQ-tw$gO$y<5*4y<)n`;5B@H#1MxzIS$j2JSd3%%A)vOP4L_#Te|eb&}$fkg3S~ z&NS{NbF-O*`ho_9O|Nm zr?nhW!LGeUXihBdVys5uk!8;k)Kr%Iv>;Fn8Yt7pBimDLT(@oNHJaI_w_XZVnQmmv zSk30^?wX1r3C#UnaMK}t$Ya-!B=;MPeZ;*ZLCmLS{oHn`$h7SZw7>n)+38z$`9`T+ z`7Rp0F~B=qSHe3JhS-dAn9}$BPENCVCdlC&e*-igpGuEX<6%aN29(u_S8-5n1s-MB zDnoe_``AWA?SVQijkvWWkyj0M)btd?X{Qk|xbRb$`$x?nV?|teDc0q{JmX^7P4Z3i zi>{WPisBn_8&S~bd=8BqgU0w9a=?97b0P5|yJwkelvbj3l zDlEDCe$w=Z;bo(0LSER80CIs7shPXo-T2=7`7>$Ms$@n zht!`ms009)6G{SVahm71ugT@&vJI(|kD+gyW3 z?{zu2Zcf;?X3ma>uNNi|jcaw#N*PIYB_NQJqDaVl@eSQh;JXpg96&lpo(jeJckGON zrIXxTII`X#b@3{=Z#xZ+&AYc9p1~zITScmTsP?;&LPDQmmz3HUM2dKT?8b?Qk@H!y zSp^(GC3C*!w~8F3kU&eA*ANeg2;J(~ov~@Re9BU97goc{juH|hut$I%-Ae0+Hf6IC zK+!Lj>a&HQ6Vy^|f+|x2^}8?15-nDiOnYo~lcq%30EDCt-z<2J0!w&Ifvp0md~EkE zfWAkbo?4`Bv30q@j`r>ITw+WGqCVd(Ep4dDT~wuwInlB9;l=%oMZE-69w?Z+oyD`f z);Nc9Ax(RK)!(RHy*?AsIZo){x2$<=za!=vSV{O+hr)qRWezD>PTAq6X{6W4ggwo06m=goL^9kg6I~;|$2` zIQI5JD~QQ0E?tjwa6PBse;Fl1M*j$6Q*>9T;%n zSIq3Cv5t2z@dk+(LP#Y*f!~J(#X`r$d4vHal2cTqjb7|0c_HYcma=C_2hzG<>Lx^6 zm!oe^*9hP*@Yg<7YN%FG}MiS&ysOt{CJ52r@IaR0R6f3 zEq?$#_McMhH|Boww_oKI#2bVX>g~$d&Mkd&^AizO`st2G*<;GwGc(4&V)`AtrStIC z&-6F#3*?+7BjV~wr`L_tHDY+u=b!k0&a3$1E<8>B_rjuMY2B^T%PJ94VyQ&Er*K2q)H4nuyv9OML$jq-UT;l9brIs#>;n$qE%2l=LkRLV!HnT ziEr4q{{Y*xq|E;SaUT5f<^?9n7JQPSnWcL$nJ6kyN^E3LQ-SG4W9!9eSQOMLPU>{W zf*!KA450SraA!u2*-1y4Mrj>D!s!XpvlJyGyy5k@qOaW(s&!k!Qk0H{oip#moLm(@ zAVPLTNhn5nsP(`BqO9F~DgC^ne9s8H_{_k#w55GDAFmDG{z<;go1-E^swi(R^V^1x zXbW_a84S=B4AOoPf~4xS^3&Lp^_+Qnt8E1}0BI-b$Cs9xJ-BF`twL)>9Dwb|myjhY zJ+;S|mVvNIQ0v-r$Cr==InWyWJFqEe)~5wZ6|Rzn1=lrUx^96{P%0Xz2kzGnQhs1W z+$Nw?+;QdaFe0v`nhjJP*zy*-s09jyY0PIqk0S!HrCLo1{{R;SYpRB*H78IR(wH$$ z6@=lfsc?oSZYk{+V@+%#+}eg~NmWXK?!^8rr;5OPrMP01!WJ5kbyfkm>Uj!7ZWh@Y zxW38^S|epb#$7E9PZSf8p%xMn>Wk>D5#A|GCO}RjmHk{aQT^5`KJ7*Q0m5}<(DlO? z1$e~V8t_AG8ECfes{@OAttvoiEoKR|nJRCu# z1r%$Dxdy6#1pd2aiPB2#HkBz}Hl0R&GFEsa+@qpp9zwC4aRGN|QlXC*+`CkEl2S%k zZ!z~&70bGAK~+bO5bPb(zqZQqagyO_9sW~9P(>raY&2bll^}Yy~amQ|73RS)^&o zxRlya21P}IcV8kVxof%6Gx*43=BxM3!=kp=Gq$fvbGZY4J+ALi{# zTk#D?ty1<*=NQa@d&OgRjvj=^@>0;Ss1p+GV{{S+GOVv^0|eD z7a$W_Qk^grO9He}0W>+N_T$LS8ls8jF9g(*^# zcLi&AR_C(r4@=wwYi1A^&ONk(X!zoBY5M-q0a4YaEyTwYAjer@y3_yw>D%9pF8W$%up1bpBCS$4sE$*& z^0y(qb+JfxM%l8mB5m(A3TmkMoNyrY)_t9rr|d1Xan@n4(Z`C}`@z3|9?Kb+PnI+a z7Pm1TMCdF^c}+oa=94wxnyCyI0C7njr75Wc7W}Ax%e$lEp`xJepDX9%GXDTCsC9a) zuhhD|>qpfO7VqvN==T9k+-^<+sPN*pKvS-1_; zwBtJ8o3ac=ubBQkg6JO2_Tuw&--sg64ky7%KBu^v+v($T?Y*gJ-4E^y#qt8=+{>xoYlrIz;y7i#zrn9^;Foi@deZ+>Xt0R@EI5(GdcTC;$a&Mqd0^u(y%T zeD<^oH|TOJzJ;u+jk(h{9}GAEmVx1b|ZLc#0Om`Tm|l39D6n{p9O zOY;ry$x#F{k}E;lYk_d4h`Uf0!Cw^|p`w%|1cUWpI#rHI5T&pH0=4W-G3qAHkc28y zN^?y;m@*-iXH88?MSJ>V$UMcmTPq-BocfWL4A|<6E653QU~1bvXtG??^Gt@yCpHw( zQnZB}tJreEgp^Dqvt*;+O6_h&ZhDvdf+J|J*qEz}CxEfpGe z0ojCHhNyHjC=Xur;2*^paN>ms6zvDI075%nD7K|Qs%U#~r5uQ4TWCN?RXj(g2eHn%6t5Ss^ux;qkZ#F3uEG}BhQ4iOC&GI{r$dV@ zU?>cOiE3MT6-Z!dtyS@N_Tfaqf{~Q|hPAO7$AUtQ0Z=&sTrPdpL%m>hlt?$!G_-ly zA=(03@2&`BQkaJjX~{O8OHRd02{c+j(_g0sJQT|uTK=TYyF!rCd}-2?T9Mm^vawom zmrQX?`3dm~c6Q=6P_6dvs!Mq))*Z5--cNNDy(=K z`Dr-?CrJv@e|X{~Dc7NWz1Sg+r4b5SB&LHi)c*k8z&Z+~OsMN^Dw;wMzXYOg*?~it zWmL1v{a=wZStyu8!mZ(DFrG))RHSvL(|)cI}Oo~y(N)I*En(oT5tBH{h$t-GSE~z zx=xGS)Tq#SNFPQR-U>-Ya>Z*1Qa1%Pt9DAcDIObK36B7J1LYLq&OB;V9_xKt*7{S^ z)8?1CHHkkFip1CJ{UE}}>%&%8sHn`VKZekvnIC&PbHvpyJnGN1slsr@tkqj>zmgQG z94Q=loTy)#D0}FPTI}0KDlPoAx=jB75@}KJ{;`iE*Hkh|$d2C3Dj0yF9a0j2NXpCPi!>bmBd5e0@84lHrzudEJL=i%nL50&S!-JQt~32LlQ3-d=V?=)Oa>)g#sX{4v+ z$|Ew-1o!!A6~z~H?aY0d9$QxJ+TP%1GEH!q^;2A(ab5$c0co$>gq^dkh)&?{{zN#%OS%B=~C zcGDr{G#o1PMs*zpI$UzWQOG_E;R#@!fKH1a{xeQs=40*JVi3lCLvar3cZENwxzMi*DhWO$$+{d1$9d za@2*I3L4a5A%H$9TGXX0AOIC4;q{&l zc_j^Nimc@~J)2;3k}zl&&F4CPV!VyJIhOr+<4a|h2Nzo2R87CPUbZy9#93j@E0*+z zl!ro|Ae0o40%?_Nj-=ZgG+Wt#_Y&f`GkEU?USB!PU<|J%ayS>7_Fq0()g~(gY;Mb7 zZ~2pHy6AAVt044sfD4Q$`}= zLWg#eFpKWf*s<&gaS#BL_?iVW0*U=c1@n^4u%|R5h-nE*P2CyS_ebr(d8uP;0tpeD zT7rKR8UFx7{5Wmq6k%)$(51++W2UKH$hw~Jfr3dXDf|$%OPf;9uq4ITxHn{o5<;k$ z%|-J;Z?x*Dp~Y`2cXHE6+f||M{AMmjR%s}#w%@gJ-J4|I=GZrMtAuBCeD1iuT9QGa zqDjt`#&3N4$u5AAv@3SSTH@`ot}wo>=A);7*{i8-OWo2+zkLEZ6z|BKSs{x23I=GX zb4`ZosgREcY_phG^B7*+*jefG?mt(Y?)V*mjjX-LrK} zy`fa{ANf?Wla+sX_It3~9W~US8)%J-lsa(u{j0MS?DHUc`5YL|AKyFKfKk#k)1veRo)ihQc%4@xTyvJJWn9RO8HZJDfG z^X68HX5&sw*n1-6{Tm?tBYA4 zH8l#GkG!{u)oW-}t#pNE-}V+>VU)c-5lY)ijT~*jGXT@3AuVnl1IteWtrynuUPc~1 zAdvSi)`PojmaDba-SaoymI?CvR*D3xr??zQ+26VXSU5Q{%3>LC1f+Y+PmQT_x9%Gf z;Z0>SnRNmvqBMXH=6e<$n9fv1~uDp}Q z?xeM}Z88&f<4L8$Q0=aA)E5~cT@@L`=~rBc@5P63 z>MAbvUBd0|TXH#%YPLo4!qO4CqM%hy#cg0E?1Aw$Ul;U!*uM71cznD5Qd91`g;zU% zV|!FCnlg67-)5$s3anf&e>)n>E&)%O6_kn6(~{QiRVvG`w;KJumq{&u1Hp41Vy4q*H2slFN|cx^s3pqK6=^BVli7@R<8QHx@p*lC!>LpQf>Z5%-dq;p$7)8E+q)NOoj9$^}VU;R(| zglmftZ`(UD_Fmn$lqq)>lnT=lD!dwq83ibU1}`15*heDcEW_Lj3jD=)^Y9LvOEL_n zkD+smmN$neUpD^Jk+|+oxwCEzIxO(paY>G)w&6;`n@t5l>0EESa}SP&cL>@8!s0IB z?)>%jvn05AF+6B1qQ@F=A0_fu&bV)_nPnF%9j-&MCn*ji&qK{NmkySWYDfyq;T-<@ zv9Y=Nc}*TdPh)m>F_y45){W|8PQE%RQ+V!8iDung1G@Lsnoa)G6K=H&a@g1j4mji> zVxdGSNUnt%kzUMIFm@NSTT07QLS0aOYfas`yk(u_GvBkTraqEADy>{DHmMt){VvxX zQEc+&KD$h1d}xffNo^_vDumLw*(~kk7VQ!mybXP8mU1}=CA@p06k#NR=v_DAz16?> z*QJfwv9_$4Fs1SIhTe&AjOvo&;l8!JsKS}$nV{^)FmDV!ou1@uvQm<9AFX{2?RM?T zWwsQMz)w^h2;)x$MC9(9I5Hw$cLIw<%51}M_S*KsH)xWD5OnFs)PYWlI$_&hFNXhFPq4m_L5 zeC^&F%K;tH80f0O&D{Qiqbr{Gz3w2Eu2T@qG#|USM{T&#+wK9jLPytJTqYB{22dcn1g z;;*OM`f~Lucj+CuvpGKCBOy+N?c3q-kTp^$%Q|&5#^>F+8p{S(@3-w-h53B6mH9ac z{+U&_X4<&DJ@(s#;@l1>6x0shnVep+_swhr)Uu4`5=Y)Y#-%AYV!7K&Q8EhHTEHME zkK2|MedJ_lk+sB3!$%EN^R)QueP2@rt3`>&guNdsOUyKt>sEn6;?fRh8QyPmS=$m_ z$!+^rx7ka+matO1HBP93YH0F4xRm}XuWPO;?Vac(=v@4&kcD${12^CAW`wWmY{yPQ{ zaCnrdJt!;KF&lMzQ(l^>jBecoYb6q{W-zJl{4I{OaaGvb5Ke=JXbL1*(WxKo<+V)h%Ko? zJnt#ZDnd;GQ3ERVrZ>o+?h5f-Lp_=6Jv=I!ZtF-OC1$+{r|ZIrpO%BppV+VL$B=nw zB2JQAY^Zl(DR?D-T{Qfw6xrvE&mA5IUfc3YDrgl+OaeZPbC2dJ-}VjluK4_mI5#sW z{{S!TQGc$8X=BxIB|7sVTH`vEw-=S!W4mrZ&~&-R2c!MPxz_j-rdqPG@z zI8$MIj^7e_hElJ)Dm0tZvEsJN?;+_9DQaL8G6|=PD&<;ZAi9Gwb%lkte(ai6ZuS`+ zxr*FZ66F2(%S{C$R3KHP3rF-aY} zTPMT-udbS|KFnOhHRO+vNXv->(Nj;UJY=~1{wcOvBD8$@`-CNcp02G-hLR|IN|XS@ z7)u*+=et1Ar(})N*|H`Ixz3;-wGwWPySa8w;JNKh-)45hxUCC9ZdQpQA?AySOR27s zK?+KN1QIEU++IrF_7N;D4r%5TCT9tEj-11A!pDwB(vTxXlXIBGzTF(jn=M*n#z3hj zwAZi>q?~}miJlp$OyXmnXr5OKq)oGCm9=bAyy@3L{_Z`*%x+Qk{tqly0j1=P>XYm;pJdFiWi*jA?8y6@BrMUEK> z%}#*FeAk-g%^q6BMnU_MeM5kQdQk_k`nrkxHl zU(4RelHzw{ryLKvUT4SXTbJjLy71fEvjJr9bPs-TSR0J-%`_-7!@@K1aZjlT4a?>^GN`f(2{FR=rhcm5zq44 zUhM!=5nFlBY;4ueG!$Jbg&kk^mV%=D&J;)xue;j**r^y}g)E6%*JDui{a+ zBjb`*+r{uTdR*%i131MdPTkoIe3T~u6qj^!<543j>M9_23^;FFijR|_x^fCFit426 zS?xKWX*gDP(Io?_uMeSXf}-=@+KK(6S9tRxzLW5fwn?YijHkO%wb)1NsH^o&g6KO@zJ z1xB^T>>#a4T$4F)c<@jsCCQ5CD@v0T6*DFczwZd4B?wY`aD+|hVOo|7ZmJNv$?BM= zbbd)O99x0KZKrLCsLUtu{lF5D^-~;;6lN{b@HyA(Uqax!Y};Dgo(%))r|e%RddXc6 zQgb-nn%b&Ev^-YIH8r5i1*k~*i&~uMih%;bboi@Lg@X}`kUD!5FjAaG z;t+16s05^E*l<#xM4wKRLJW4U`^-?n1uGVfHz-kx+Yt+S*saJUq8h z`-!DAohRt`;>_06tO3rbN8+HV?FkjFD}@7Skqx4Qw^VhrxbCf#>Ir#8G^g(*EA`?Q zMo_KG2{<;bqC`q{3TV=L52Fbwc;s2o0P!Zc7ix@VT4+7^1d`Xv28NmT;1Sy57DH#a z3<~#}d{E5-li%#el5TBM^D#$}9ptziF6ycVN%h{1MsWjxHk)5T;r!1s{Y|fnRB^5fYG=4?_MN*lWw8 zQlr&<2*JG621Jhiw6uyeH4{|>)Q}Ai>BLpeZr&FLy}_jTl=|BrG}^VMi6z!lGob}N zK^T{gVBw-<__}o{lZo-2O7|b54hIB+D&|x_UOc>jaXRQg(@(bsYVioYFt3)53f!KD zv!~U84N9V_LXAeB3F>fSDuw2)qPX@uAP){+wGQ$*`MQ>1oa87lS0HKK_s zOa5eK7a9%*lnDg%Cb&qIC1GwFSp4x@|6_<=TF0=>nb- znF!XOqNfp#eN9%UjXhRv^eH2Z`5Jc;a=oW^!HAoJU^uqtTa>>nKONr+(Wq92nU8J~ zbGn#9VVA`=>N;~>c-ow|Wag_Zi`BLnw!Ld~N0R|g`KxbQ1B1i>>soynklXVXXjdDEktSPAyVoB4(t~V@<_e%1c<%PoXbZ_6GRg zHz<2{&9tY>y*RMbP>~tWFls$i2pT}|IF89~yE{k>j&J}4H@CZ8n#^4`2x4({N73tD z67{XIuiQ`QmyNi-5`KBea;^UW&Eh|e8r15gDx_yX?wsk4czVi8Sf+B?X40xI)0YMZMnuCP8>W&>m2|)&x z!X%|bmJ*VlnOCj^Ie40*O*TS@Zmc>92_I0erw8%$ZbbB*&2b^NhA*2tbXxA^M$)=e~Ty%b;&HMB6qPt9u7|^)mP>a?whEdrf`#y@i*SXqMuy z#*$NAIe~0hqh&0@FuZHKqE&BE7mgw5XH0e0A~ z$9dR@j-m=slql&^3MipUfm~qSrm>n#=a}%}P)%rPy7%)}m&Zf3*>3@Bi%0|tSBP9l zTFra|BbXlib>o{w^ChmRMwxLzL24fcWD53sutJ=`D&_Pc`Fio)i;n#~Czb9INi?m8<~ zyOfE>aeVvSmx@pKRtk`z&?uT?)K$&VaY%siOx{3IcW_8aSN3()>^SrIslC4XTY#Bi z01$f9rV6EA`XRyz_F-_)5>h>3X{9skk2$qTwe(pH`)GTxIs$CT7 zbUHpa1v4JdFmg~v&Xf^s6GMlIv%vC`#py*cOG;G%De!wR?fext z$gGt}*J&-fC>se&X%tm!QP6b5M^Q-Q?aO|Y*=fP%DRj(r&jeF)!|tKwt*4rBNEHMM zC$weRgyrCyz{fS->W^*~@Q|ljI@A0eslpC5S6p@RtqN-SYxC1nRVaZ^rwo<5%pO`v zy(GDGl%$-&0OUG>Oh#Uhd08;-q)3WV4p{?+?le-qV%Rs*x%)7M7mTS*mete>)8WDT zG)PG83h$@o&-r3{aP@=f3hT@KhsoEim&tLrHTurvi*)O9VA~nB$xCs=v1YQgxL#O7o3YFJFh|tJ`aK>iUcqcUEcl;3X}VrTc+=>o4@RT)C_|M004(c9 z#@si1PT%z%VcgcpZmdC?xUDwphz5W=^77nBQ0>wwi-r%mw=%{&$Xp8L?ajHHj=6Mk zp0)$Vin89l8(r-IMXpQRlbcRz0z%Me4{@o*{{SDKl4okd6>>i3yZU7P3dMR2+g_LW zbDVFx&(!wq+P{Zwdr}w258lcoTA9ACT2Qyt9!i_@TVxQ?T1cvi$kQAt{{ZzF*5rrig8B`8>Ya4qLG4$w^9J>a=!<>& zXyh%eWaKTazj-oTMw7;Nn>=RaO^-X~TBE3X8yZ+bP!&}zKoORGF~I)-k}kk^dkwnT zah5U*c9GT9S|1)XUqUQmrk;7LDLKI>k5 zOXj9Lx1&x9U*W_EwvGF7=vcP6l&JTvqYvzx?vkpzh5c6z3CX}nEN(5yH7Ot5Eb~@X zUY~Yw);5}yT!9CqD511~6r!A$m_GAd6bd<6W0$5SEWVE`VA>I1?PRR~0GcU_BP->F zMU-_;N@Qs!qP3~+$D>LUL!uq92Tp?r4Wcn9Im!r(r6kcwN{YR}VC@N{=Sr^s0Q$uV zvU*7TzjlbDZbsQilD0=@nHn{6_nc6+BMb5qzx>JEF8M9%^Gm0a0qDBDnwdXQ`S zaj&#{)m+heCaUfFZ$qeux*Tm~30aPZDNMkr7^-eI)YV!xxgxaYtus%|hikbw%cTf+ zLR539p{T`Qq${VG;KG{8y0G z8s|>x<1^p3jV%RqM`N;15|;B;M|tjRlMS&AxUHd;B!KBiuj$5rh{v?OZrisrAZ-GN zADc17slx819=Qx%B8W9)6Wlnt0%zSRUoNE8Yy#ou(zadmBi&;V^dme|9Z1@o zloG&bI|O-VohpU@0J@kyY|`z8DX?49jhoUG62jD*azdL*i1rLx4OV#g!&p_l;0bDf zWW7}qwxzlfnhb>!e_k?;-~hQ@DcSkU@#K0`v$?k%O{-$K-)FB&P}@;LC@Qtmi2}Xn z5IAgkxVa5iP2-(rKF!iQjTbtd+%Mkm7aOeNOq+{~b!t88HK))r#`_hZjxf>5aP`hr z*H;dC=256OAY78tsu!e$pK-!+#Xy}~-HOpS_b1IgY;#IVs-RM{?KH%OB&y!#j;&1Q z9dV``WXMZJH3p`c{dkm~+0HczpAE${s(L~EFXY=2Zr*IFRSf`0PttJwnWay#<|^@xYz-F-Z9bo7GwG5J$=GN4kl9F=U0q#yixhxnuSskCf;m(%=t zIPzTI+83o6*Hs;j0vR88k}27wV5Fx}!U|ACYkPWJOA0jj zJU`&VK5Bnx>fMn*A}iUF?N7;u_*yc zUPSfn#v7QobWZ6Zc18mr+la)hIu!kKI$cd0b4H-YN&)`%vw>^XxF+ml8!?faM3kuA}HVV#$xb^CPOy z^H`skxJLG+#zmd>-)YTgRRitz5OT+_q4OG@V(tqHm(g=>ESR@u2D^N^BI4dzC4L&8 zal|bhPfE1MGf$|dvHt+vt>oK(1(x^;ilRXQDm^}fiAND}2c#&tRVKKN%FOnoV+DKn)X){`{xSj?MY^j8v7L+wELx&*$#^NICd`JQf2MQZIk6Nzl}_)nQBK=_ov;6UB8}YTN>zGX)8rN zYQ?-ZoV#mr#XS$pNI?gXs=2_FmnYYV!7E&l&ox)Qi?t^?F!ZTtj62Vdk$LN!O_m%T#AQ( zm3PkN;Fq^_C`p~NHpRo`ZW4}bjP(+tpbtcyU@D#Xx;ryrX_@(VpW-}JDl4}3TK@oQ z3&_P_jL`ZMcZvN#@<$0qyF%F)gg~}m796@jeUTxOu$?;6mHaEGX!>yf0CV>H9hD*T zx88386+5@x^3Uz2x(i4D01iW_JQA0vYhB@i!ndoa5!>9B33Ay0P%Tf)OT}MGR0ti% z5xuv#iO!vj{l@^T-TY+jn%Y@E)n`|>iUC6o)y1TOQa&T~<4JuA!Xcu+~v@{rX^t|gu?=p#LDx#>QjPcEV>DXBel+ixaRl|z!pUOXaaF`2Q zh_X(jUBGywe68vqg>PG~!+hbcAApV54tJV29mX4NHz{VIg2O5WYEe3YRR}mH)$OKK zKwEn;3?h^ql3l~^w%wN?CB#~^N)395RQHwrM)Nl$-0qETUF{{Cf0(2q!wGIdh{{l+ zojMp$D*pf|oNRk%w-}2S0?5OUiPWxF-<|5*xoO-boVABaH$Xj#D}L!Ad`ok-NPTi6 z#lFOn!W5!HU3MDTBm?~vV#{l36EZzb08}c4&QfVGg^zJ?4(3!`C4>Q`6)NjYT1Z*f zp1dXM8454E14K_XNn1)K$90)Dt5hXUoo$Njwa0BBujQBkxV-v=)~1+($=yY6I;1oQ zPo-QiHWOaev5J<8{RlZ$-AygG5=vAvO)y3PbxRn_O;07+e?t7ZVCD`Yzj|rp$!$xO zWq|Z+Dr7Ao$#z#0pf)x2Tzb$P{5nr&IJdjoMr=k^xeHKe<5NyWePjHv_eUSPcD$G@ zlZRUyMwfFRPlBcVHME}NPg2{fW5>7)gBC_F@FCn=QfT`kB9_{YoT^d=uWmOD&6IWi z--c-g9OBYyxbtOU1}}D9TEs(`z~Bh*m+vLUc|H;Vd`An zxMn{(`Wk!3?M5TIlzC(3OQlr!luzld6mvs(B(3g^YgIe#l}(vtYI9PZQ&zR5r6eg@ zTUi>1yQU~vR$KYKE&`=*+j4v7S_zorM*38vFOD}J=Y?ju+gNt?|Kt^Su;ZA3&P82DKXG&6HEGZU?!b;M_wE0SilbY6b z_<;H_Lh{!NC(VhnKt**y!`XLPlzF%sP@z;fwWT1BVJ8UQWg0X{+3knOYMh&D(fBpq z8XiGfD@Na2$}PKZ<=X&cc;n!?<8rx7xKEWjWTnWN zC2CxU)}mCUEY#GTZT6PY#LkwA0J!HVlA7jIB!;dilQQni$!Fwma$qS+02ZEFMrMa9 z(ET`GR{+9PLnf2V02E(#zZDnR%_2jnVUH~+ITa}RV^K8fNy2unt16bf6nztHONMg! zr=WnTKf4r~Zll3}}6U9b0vS{Kxe! zeEi(Er+Z9&1MPC7Hs@SL{>uA{D=f~0v7c%rp+i4qF{@@HA1d+aKVrak<-r%x6ZhNp zg<^T~VRx9h0i`qKt*KPd2q5ZZ4gx9Ps}(((+E_JN`$PVx^PTbBB+Dzfcp#6(m?c4@@ z&7(XeXZ|3s_vUTsdv#y=S7vgi%(XDxqjTGZyLa85c@fx&CIQyV&ui>cPACT25(m!9+xf^zV)wY_MW!iJqc6G|N zcHPaVLS&~_ZN*B4q_jxQM>Tcnj;pl~m4WXRuO;)o=*JWK7;EWFKbLSHF-<5_F-(RW zw9!YILe;d2)n`I}ybv!{9&HIx0kV4cV991F$A-AWaPqG9+uI9lFdx0{X$g%DN;GYu zLbn}X^jktokGm9&@wl0c$G&$Tge|kPn-WdKn2OPZ;v&ATYwIhDxSM2fCm7mxe&1;j ztrj@`Wy0dBC32-`3Vut05Yh!Im zUpyYPdHw$YR6O5luxya++iY!lBFnMiv`WR&e7Oi=PoX`f#{g}!{35*$J2$qwWt(#j z>zTBBg-@V(FOwbU?hO0ybF$jo-i@y5Z=}|h{{SCfCDv;s9h>=ddPuoncL0wsdMSY; zaF*5*<<|cIDLz{rN%a ze%%83*}dH_{H(4|=B?pyRJ)Xmgl8^_Kmaan#mY9?b@Xkf&;S6|fMdS!7I5REiq0`c zv@eRArzvxleEBKOWRcbX01u#0DBtE&rmjzQvD}iO{`BJ6BZU@d$2gRJDlBd-uw=-6 zZV(WaYVoL^l+O`zOlqu{_&m)yt3Bzfjx2k_l&zAti$Xra*=@Ug-EKQj*UQ?+%%!yx zPwgB^6Hd@hGhXRt=8nW$!>p2!-90OP><%8?nMdSr01#UO?R-X|bd%Cf%(;4l=dJDP z{{WGS8QB{>)&Bq`Ms$GDayx2mlbr%m!*x*y_m8s}KXY=e!{Xw@TYoYC03AF^>YvKy z*>>m7-ES<9@?s=J4yW9R<_q&N7%8sob6dX^~qwV}7&)l|-AJ_`~ zUIyQgm@dp@X-(4ZwT9hzf^$e;g#`3S#k&u1=q(+0K7@*a;Ipi-84Qzg_P+ykK9Y`d zP3GCO*KFd86x2(%aoAg%y6(}UE*G~x@=}JJi)|6*w_4;58Eu<)+?QO<`}_x?O>S(Q zRAv=Ec+!7U$G7bNFzmIF8dd_aT0(ILlj zIvOP+ils^K$F4$JAyBP%qzj5-bJrTFPI+O;>29FVA2|v@uhMay_ZhudnFoMd{{UdL z+>p@mG*VZseX8Ayo_DQLr59DPD_>1T=*T9YNH{~Z8BaG92i;Ep05kh|TJ9H+J}p&3 zOlFQrssQSH@u>N$m|bY_i|G5nqLNUYwP7dD-xVhnoBS2j*|L8c^qI+U5_KK1dAGXT zB<7JIAd{c&ojAwxS1pTiFxN)~)c7lRO|qnE8nmu;?r^W0&m^X`RY$OPV{x}|qUF2D z$a1e6?U&!(mbomdx}mqI14`&xlS=zANhw8t+b!y1>Te@lr>pWx=2pI6=HtP-G{&L( zh;r`@)hPQDs~ovMLwATg>H8OZZPvT{KzKELxRj*VDpwmL*+o_pR3KEEP*dse!56&L zDM{)+k%I=FNns$W2+)IGm?BlKsxGvYkOH#p%N|Yxsj4it)T8lGC#df@0+(ylvB474 zY~+ZKIIS(c4&cc&ulA8u`!MFs)Q~EU_yP80DHT&x<;X^DCD{@kOHtYyYPTFvZ7CI? z%|s5kk$-m@&U}Ytx6Yf(n?5G)mdj4d z*;;PeST2Bkl}{{&%F|jBmWZ!u>yMrJfByifZrHNO8+S%@G$y~QpRITPKmP#fxtzVS z6A9WJMQPH0-mm_jp>j*qp9=4;1?BC@#@v+!s2ir<%t@MAbUz*VvYI8A*>L~`p;{Cx zL6{<#?S9?&A0N7Lt-rK2jsE~r=RMQg%k8In?0wh9&#Y_v6c}4Ny@RGNB|uame4<8w zqXqeYN?1y?@==3P70#;H$sW(Q9-=8LDPbbLxC$aQ!3u^7XZF^hk~Wvo=L|XR+@V$r|84hI;tW=P^=}9u_J@|GAgJ_ z8cM+W1{yz#tl6HNvlS~#Dxje7;y7?!4CjIYTcLmti1+7@I!MWLzQa;>yD$`U6Pj-0nGEs$TnKm@pZKXdi0U=UUrKKbV8VUoM`Y^^zg40Cm7E~?T zhpn-xG^b3fTKjOuYn>CA?a}7oDL^OTBetEGjebA^p2Y=GmaPsK0=3i*&eAZ9mxh`p z;@`CjJ-8-9n+IpwoK$3oAS{*(<&o!ZV$y%A(*YO9jGbPN$}Ey=Z}+L znQfokEXQ%Jos4svYzVC@Mf8)g+mC7Ec6`S3q|8SSYm8fzMsneDehhOOBhR>EGDuNF zMO8@4A6)j8q|oEzWZJwmSJu97?%UXAzmd@jFu2h(oo1Cvjz_n%7FAe|LWr#*gy0Hs zR83%wMJfR!s&D}U4LVhr7*;f+SG1qv`vwk=QiD=XS%15(1R`~-8?#;cou2^tpLoii zY@;lIRY&Q>y_1a=AG&Snc1m4{(<)lj>DGjTdyPQ$;#sP@Q^|VKQ9PxyLhEh>kMd4f z^GHq~AG(UHJf@_ekTg>+wa*@Ns-0Uf0+|;FKK6>pBmf87i1Wo;ZQ4vGie}`d1;=OY zyM;BSa9IUebmq zv~^6Nw&ewKS7F6xGV&;?H}Qims+oRZwqN`_PuCivIxr0MA1$G*`?7I+8m0fH+RN)l53xtEZlsw)MkrY^!$RE@+9oDDoNr z{4F0UroHF861c|)g1K|Nmv1*sZ-TUJ?i zO|{*jWjJtJw`+UJki+F$HEIbJU7Ym~WQok4pLa`pe@y=X$M*BntrG#)J?uK+`!X7wKS;!R}Ie;A!9-Bz;vPNK`a1r753n+2<p;}&Q617OD7 z(S*IVGWTp_aYrjtVQ`{e}&ZE10rhAW@tJ>Vat z(!WO)-3uQ9?Y=x85PAV#4p~X=}MMot$;XT;oo6XmAl9twK z#T?hrmGob3cH#hA`RJ+^m$&%;+Dq7uHUZqny=i83J#^VGKHNF5ZlpB{aZ*Lzr-f<|y zO)1m`1%Oa1L)(XLZ4^*jqgh#~s6Bm{OoZ|YsA{?^xjkE3A$l&@{1LWZl8x=<8-DGY zmdi~uZV$NON?fjG#1x}0%GVs}jO}#J+|pi3KZjb6TJK%w`H+H6-oc5sG@qmg{e@8Y zmj-Q(#(nmdrfNzOfRd_#L6OU@H|gUCMa9;WnA=_`hSp7@^|@k3y+xL$XUb_VIT<<} zPnhVhUh3hFU#dG-8uCu|#4WkX#@7xmPQO`_lsQ4mNVrM4H(-q(9CgT5Tnwe4pfdxd zaTBm%Xtb!(rlnl_xbyh1X*KBri;jH9Gtb?{wEqC;$O%$<<}<~;!Pq6u7(1^Bm#Q}ZU?1G844LO9aC;PlzMsGU5uhaNhlMwC@QT*DTf@CF(_oUQUDe9R|Q2|c^<+(oEgCLUMpWGG}Kgn^*Yl6 z;CZIxqf3HF^J0?gU{tsJCQ6Aezpf|XnOX&nU<*f2Cj5+OXE z-}FX3(!J)O^csRyT3{T?r30$GrfL9Crab7;+aX0%%1t~~`YD1rwMwzoSn`TNAnNvD z$GWa6HF!Lp={pm6yp(>8n8q{K`Kiigj2NV*@+7s8nKJ%`mWEdAlp?{sA$k@ z;sdbP5)4XSC02Y>f=5KAqu+;MCh}-&u!<0twzq6u%XCpUN+d^OAl3~H*dB?(9vPeo**;S;9VV$s`B`uJD@tV~YCnTXg zPLKz`34!k`1hRXIRl29j+8b@4og}H%X#IGyF`Y50G)Jmpd=od^J@`6YyTJ|xKAZ~o zBwFNw@^057p?~jA9W|W7i@JZfLhrxk4v)j^evS6$!T3)c!0>d^7MSTD?{P})+eeuz zyj-CBx;^;8@IR?meXxZMzi-m21tYr}h#IbMzI`1<(ho))UA;L%*S=A-TWQb1orbHC zdBDg%Vpb58k`88v8J_*z5aZm@C+{tbw0j!!H5=Uz29B z{U;RcMphObO4+wQ-n_UT>l9}97;-ZXYW`M|5PF&?_Tt3N#cy6KJeOl%gYXnZTMK`r zD64ABNgbiO{){Nx?Xf=)O$O1wXg=XdZtVT%9P4v!&ZqAI9c0(1L4^)l-OWDmNL%fD z;UGN+ik|rv$GGQOwnJ9?ESWAh1*FSm4XBosgaAVOjw$$i$Xg?eolRD`yKy&C+eX$l zqzzJs99pk&HlHIVWuio7y99i@ZZx#1YYAAQa?Ja%j$ZyJ-7tf|r+Zfsa}G98=FpI; z^KmcD!b0D&Ol&8rOlOTMKgGrEw~&3{vI+6mqQ2o*7t_Kdx3uRfOh+O4vesH{)G4w? zgy%&8+l;Guxsp#->K96OatFm`dc_Z=NB*PtyQ`N2X2&52XbBesQff%>C-vgd+uTrV zJl73R1(^4@cNa`8o4|2Z%F@Nf*HETdFC=DYka}mHGzsNcU5g8~Ms!PE#nR(NrGR`r zDM5l)-J)|t)uJzQWl}A2i?T_O-9G4KaHWKuE=0+})d8v1MiMNFTg@plR3Q~_Fx~xlzNsOy!(pj35MM1%&tAN5!p^H=R(|Z!c`$VPf~HHv7I7= zg3CmmJawuzt7G$?Y6O41l<^5mLokZ=nqfJcQADH{0lKD^?%L+XBjs0cM7cUbo(W%8zOk zL2U<8B&d%u)kN{Zl&{d~2k5R>l9 zJT+y+Z*>}KQ^(lN2o7^}DPAQI#xyh?8?3O!Sz zgQg1`T#6jPXcmSHWdkSYoO}b@T&wiqN}w?UDx@v8)KqNk+EYE`%2E&-Yf%VLu5}g9 z>cwv&UT&MFfnBg&ABV^*sZkV|@xs-vl6Lej$TLar1h~3s1ozpb_41f1kZ9?dEJxG5=E;|Q{uj21#9xu>~F6$b~XNcn3+8KP<=s2=?z2>FP7 zr0&{whMt!p#YzhaWzdumI_dUeHvZs>deyIPSO+!4_$G)%mfR($!2-S#1|^zm6*V*doYuQAP(}?+$wWEkN3fXYR`MBEB)wV4H7oJ?GkO} zN{0E?l+e)EIvQYI(>o|p<1e?Ct>C5W{f!&@X(>?B6?2G^>?#4Ms3xVq^1n_XZKgKU z(C{kPlCc-Iy+epqmI9X@#j@p4q^gc2^#o)=_Z(cFecG(b9;QBpKW&#Z#cRr1gOT2@ zw$<>Edg3YZl54?E$8ae`?Q#r#;kG7P5CX4uT~65?Bo!$GFp>0RaNY+PW~ty)+m;K6 zC_W&K?CbKbmZY-zDVY%YZ?>+!9}W`ATZTWWyc4`eL(PlJcMzhET(rApu-)ZEj8_HO zP28b*U}}qSLXx#9Aa*RE6YZ`on_5WcmX17hSvPa6t}P(*A{7la2{HS_UZD7Aa$2`j zk?a@n@|&@=;?jjKO8`34hzm73l~j;DxR-*-*leJDK$e~=o1DLgcWrk^ISrYkKzOcf zTVhC!GDW^hOEO`&<1=Fs;!4|5Ngr6pL_TKAA5h@nzI|h1lHTCPbaM*o&mB8r^%263 zY+cCat--h1L~}5N`K>z2YZ_8(pRo7PjB^HlDX^JuIzNHNzqBu;8)hp#=e5POy3H7% zJoMmGLxDMBe$u@j1LXvKh1$mxITW@P(?@{ zD!_#r^Dt~ zBSBI#Ju#$q12uOjd{UaUaH{4n&ko7OSkD~xpa(yVIj(3mE?I`VQ_~%C32->fLgLiZ zZVZZ&f^?*j)}3+EjIoW6>u3STn)&>4v&k2gBZ+4rRepUJZhfad!O6a!_f^$#M=pgU zY%sRMqqi+ll+g($v|4}!S0kst7$f*Pa@~Ti)No0s*g3SQnVBUDq_H@C{#EF6{O|VW!sNc zc&QJLH(k?YZ#xd_v=E2wE`s|-KqdAZ9$T!gxfD2pLaU1Y<-@Z2(a}5T^{b}F&qs(hml#^XO;CnHf>`mG(`t4-W zXpIk~4r_1j&cSv&%e;?UX}}*>Eh=JLZ^V%735WzHV-78+Q&CCYTeB{RhLj7O!Uu=wXBsbPCpI9t2)%wXI}hL zdy{G%iuZH?g0=3)W!%i^537sXbv#rBx}vns;Jh%j02!GnDoE@peQEtzx?xRiSx>q? z#g%V1mgMFC0GR!$9SDw^1f*(BGoi~4=2149DSK=0d*!mxCvRDvOEE6QekR&mLX_a8 zGb#ju?!`+jjJGm}zmFxY?d{Kf@`f0PyUk0xVkYFf*(N&%NPBNlT9ID>lR0Gw%0L z7_-1-N}oAPr4p`ySK?BuKn8e-828-I($#ae%138!)Oe~LynR&du0h-_D{#bJg_x}( zC7xg&V=0W!g#pq$Nj;>}nACAtHrdBU^H~<>j`u4mnCngfEK)SIA@*EEh8_WIK7|D# zX(FJ7Cn63dU0N*O=Gn|WF3_M_wl@5RlP*Av!B7u8YEqOFnfB8SCYp^jN?TY5I1+S8 z8PT#2^;;) z_E%ER))Rm8A89RmuHH28sry}>YN(}t<;4E1TE8!5yL=z>AJnoh&MQ2q|c5tt$Ok+43T`@AVbUIG1$YMtmGT)n|P`KFgfDzfvOkN+vsraFgN- zg2I#b98&gp>acID{i^TWr&lKN=pSOQgzKO=)2=Q7)CFc@Dz2V-Z}hReE;|>9xq{a= zEybnoyKI}T*da)l65Q1GfPSi|Szet~0C-MB(Bqc3zjWbrvsi|aKu1#gCE1V5waZ|# z%ZCG3gVW3k@mBluaOO*P+T&fP!|b8O-nZ0*&2>mg(9|n(lC|nk{xz)$(_WSC#T)X= zwckEP_lMw&5%iu5YX1QGg!c|8F%aS#{K;tVP-z7I%6eDNA-F1qO?Wfz9k}kHqWKIW zrxaaMB)wFsQ2_i%84Lr#AqZnfBpYD_r8; z+z+NbwgNInd9BwK`>nQ`scJm{II3>F&u@ygbn==8{cA<-*KY5(9!ZvV&8wV!gT$-T zx%OS>(2q53W+C4jzixX3Xm=%DiyliZ{K{6-?Ip6JbY!H0O-T0RA;#rdyfG40v;>h) zv2>na3omSUG$cBuv?TcWs|V|Y^BS>lO@|kX_-5epQ8yJ+gn$S{MZ{kaspDox}}ihseV5VBQ&6dtFcN~Xu~he7U_}CZaNKIc+=_% zXYxVY=pu$p_2><)f4!Es*>2r831Q~QKaTNJ=pG-nb6@6{zH;vD*<>gF)+0t!_m2**)G6sT z)KWa;4l+*_aQ^@ZyK=d_y~)fG9^AK^0>V(t2!$<(Nzec#MGn-JRMxbh7`SZx#&UP_ z&<===0rBxz*K@Xmu+}!sETvMkr_iCMR?u2f(`Z_|O29u*#r|p)ja$?mQ~JU9 z%afrZ&k0XYE+9pX7CCk2T&_t%8%mU-dxm(d~6cd`GE~ zjXWy4>w4{|aC<)9-sE)2D}i|DPXqKL{PXJ1W7~ZoaaRiPq;a(_`@S}8(RtD{qOZcMS)kmO*KvdjTV^HaYE`l*sgd zqaPnh@LanE%P@G3pnvWq8hC;Fex&x}g}~yzD%?Epd}qzxSGBup;W#gkj&hP)mvjkL zHKsUGN=X#eUfN<0aL)XuO_nhlLmsaq(aBx*jkI_y8}IH1{mj41ucx2s%hsyx#!T4T zZJVU9rA*#ZR}R%x0_>$U;4)mH>H^f1liqQ7En{Ehvx8Q63;KN7J}1>U_PXJ{cbP8W zf~}jxaT6uRk`9TNmsS?i0q9DcAfM=`+k`G<0Q3$eOW46t<~x8ID0XGq;d!~wyxd+c z&$znd^4-dSx}vpx0<;)-#oaBw8FgEHUrlC1Ush2RL{xl-b{wUCWe+O4#~Z?-hKwKl0t?O^3gB zZtlMB%OrTVDUqbkh|;Ax@k>h!DM8Q@PE@JJN4opfX?1AmqiOeo>F(G5RPxxYq<2!t zD8pI+uTMLAAKE=kq*tla`ni_H?3vT1j%j;W7gJrhO^Ar-nQ?u5U5@-PP745@0b6cjBdx##iHhW*U z&UkE52NUR0CGsDoejv0?+sBFuA#PtN+${}2amMC^*Fj&NXrP+q-CRN1w=;b&Uz@1s z;IBDY_P!$AL-PLXxlvSI(YHN1a^&gzcVv1@Oo_1DTBTid{uHR`BS>*7Vu4gNAQPT6 zoIS=;#z5(Aw66-R2XbIASuUpA*-(IWqTtsqZ3S5Ovyqx_%8hEDaDdtsq^y=*{pxpg z$5di(jPQbau0od*EhjA;0bN+)y@;Mt{GmN!Y(#*G%*MXRwk*_y%ZF@Y zMunu&Nl-ZuMqq*}2*rmXVHM0KDFs^LLa>ZxQd?P@BMltWP<>0F8w>MB^@X!wM7Q5J z9g~t-f`p-qh4R#~aavF`fb5%ltc98Z;w{%K$;ScjzyAQIKF`eFI^m$kTxx0hSL!m!?jUL0u2hbsoSKef4qA+tbyB<7@LK_t`exw|Lu-#@?Sc&+|%)AqCY; zXUzGsR27xZ=ojluVIP6({{K&E8%?!(w*6je*ns$Gu3 z3K>?oaM@_0Bno=iiBtO*k?pD>UUj`{^6Qz>m43`4Iijab{*0Rnr_DsD1Gsd=K^0r; z-7;S*)097Nf46BAO z6$(n*f?l^V<}4#lqtcYj;pnBVmEeT!yegj)h-U!o(rQpC$}+|SB{GJKt`4EOb%Mxb{&tt zH%8d8N%_ldrb^KgUbHw?*)>H}f=5oc)jL<5yU5~Wn&467YN$QK!&}*89~=g(1w#|x zZS0niu!B^j)Ai$DwYFzaG!ou^7m2=7~g9 z*UVaeE}2reSQML^s%*N1scLgcPNH(|!NaJI#^{QvCW^~WuW`q`(7e{s1f-!OJiD+W zMdp>al#g%Mg^<;4<&s4ZNFNZVU#kH`0P3c_fUqoc*eIc8>Vw(>(}`O)kQP_H8Cj~F zeM;(R4_~Jb6-9`}01rU$zo~vj&Nfv^Q5t)JNx)S&aOwydE|fVZSfE)nPjwCzjVTiX zkFH=P-CgIW(}pu0Rm&WIhE(k}UyAWg+ES=~T@Ny~1KNiaN7;it$E8=G20Oy55|7K> z?h%!eR)RFws3M~flO1TaJ63|)uAq7vzlI#QLMzgznp1BecyJnffocO z&*p=+!?8BDwmNjx8UX+f7^yL-1fwNpt{Y3o{QeW6{rZ-l<3)7ZK1yx2lw zWUcinRgN{?w>vv`OMG@_G+Qp|Y4+6MNa}Rg?8T^VNQ9jfD-SygXxIvcc= z3D$#39%70;zRXR!K;co%Qe?B1C-7Zw;l6HfZ~8jj7o`t6X3d8-Fm1p<%>`|PUceLv zSmsR5(Gu=f0i{SQ=LN3`z=kX0A>Rb|17x653m73@%Dsw$C+L1G*pm;Bn#!jPD zE{X~Pk4TZ~L6%AADS|bmqlp*1vJ;wUdvHbWy;qx#3W={yg8)XW&znT85sfERJ`xQ( zYC2%0wFDjsjI)q*G%KNggDgMFx*Qji!OYXl?(py>+_o`QAK*k&1 z{CsiPMEJmOOpW2EHP?TXJ;OY?izAQ<8T={P` zXG5dH+bt}hElI5nPAL1&ddyzfp?4s!T$CmC;XceTDU4wt*$e+AOr zjO_LP;k)xcS52-5)Cz*^!n!k#C5kEpCz3B|#w?d-05R zpJ%#kug-(~F;n!ell;+lKtX_RAdli`52x9VS}zCX`womH3fvvh`bMUAX_v9l;!+g*zp}HqgrZ@ z$h@~AD2CES8zV~3ZW8HE+c2dT(rz*!Hvv&}p~R#t>PT27xnyvJk+s?s{9O>$B5^bG zrt%ilO~P|l@bvZ%&8a_jf|moDh|eMJ#bK*<0<)~ z=%(eZu`wV7ttp@cY3`*k=Vsf8tXoYI-NLyu<{wkQu1E45&z{~FSIV;Ul$0N0MKNQW z;#%&{XHBz0gl|DE-29?`6Hl`d(;Zf$XQLJ+M**rNXOPbfihFM+v{D|+X~?#BmK8)e z;3F=$BUM*$KC=L$3!_DIrKtQxYRj{(7h?rgqRMaKgp3;}rhi5g(LecxunI_0TTxG$ zPcQ;=rWEqicRH(IEhJ`|8ub8U-dbC%lzu!2FVPN2R8^DxqJ>-fNa=%lDn4t+w@|gX z*<80NpYKgfZSk$QT3A|XT2Rxb2=;WRHhCX38j28#CA6ecm`yF#QRpoxQd$EcktYiY z5;BzKNF_v%hqErYa@7)EP|dbVQYl3q+!lhV7L;W%l&^_Zr)CrnN2w@78{L3&s)f#~ z5e%)R2N_+G328p3kgDqQ7XvvNx;0+p$zF`41_xmsiw=)LDRNXhtjcGnwz)~ARNCK4UUYL1kvABKfgX>KDVoH^% zM>0-*`PUL|QkRF4SBiqGf{5x8Xg$4fxJ=~!N^?b+8fr{ghzn)R)0Sx%aM#ots>Q{* zE(N?&9m&=lZ7r|}X^?c2)YRdtoTO}_Ej2t!ZZLJqKt0vLM-@9M_Y;2LQeAZZ@Fs)l zt`V6`*tU%cw4f5F7JjTUCq-n5dMar6U3vu?eHb7J)4IAQW5liJh!v`dKS{)G$HcD| zul@)NdQ^ea2n0u+0vRJos(Rp3MZgqzrj+qu3O89myb4uH)MLm)io#J$&tA+Z0SPz0 zg>PIN%}JRS26$-q)dO2pn>of{And$pa5P7kGa&mHdw^aSq)EpUXQI z$jMJ}bg4vtql$NKYFPJpxc#adyC3?7x4Ij-@WPq4p@_1UT8p?v>a_H*D??9@OEC`>r&FCFJQH#8Ba!wyy_JRMoyl zBHke$Nm{Am)H^ALu8e}6#c>%L5VtE>PzW>_Ag3hiN3m+*AcC@eotRj=MBIjY5Kg)c z4M;!k?!kkR2S=d-mx!)M4h&ck(GCX%B!zCcLyV^QQHwJ=Lj0Sep`{}- zNfjRK4Bqla&B3)rnr~Fc7AGm^8z=8`&bfY!97K#KTNtme%^HgQ&~+y=Fspc`^32fF z)KpKsIcJIZyb-;(yem}V3WcEy5+*1N@@bN#eR#0ra2Izc-5MuscY5zq~4(RC!P&u(@9*?^p!6zc zBB{lp1XP-M2V7YTR1{92Lf)?Mw)}K8Kxe)_?Yh*b+m;i1fKqm_K0{tXD%$Sw_PveE zw2DZ*-2pL?+JHqWaM!6w!(?223MhTo?lK;kBCb0jWklh;jjFm?M#!42*QT#Zcb;LL zA98M@_TA%{)l#K^Q7Y!7ngmAHGDlrb5l-&>K;*L`5kaX^QC4$7!FD%n?K=+2vB2Aw zDDJ|w+mIWE!;d=pl9Ac1)hL`$Z>KW4rCRMR%tC`y^@nMjA+Irrjy#^#XjMN(A^!jn zV9=FfgA+MzdQi>VrK!}VEGc?T3t=Ra^XShqZHub`~Rc(8}8pq;T;N4YhFy&?wX+{@g{x7L-AVXLk_r0o zx0;(W)!j%(EwX4xbqc3uwBZ>gPkuo0s*-ruYEQddBt9ao$gDK5C61(`y$JSW1iiL&7cODoS8ZUgqrI?fql;6@ zYo_&JiE@MB43Z==pWQU;`tb?@idq$FmBQlOIlMw2W^QQ~SvGqg6&-N_*0JjQhqBytnfsDb7Co%96a3#`u

6TRUT)j`=h&!S3$XvXtM1m#Mp8ag)M(BD!m{I05v0g zhy>9tM_RMbp(CnD?Zn3F$48a7>2DFiMs~+=%eSwZ=V-@hd=$^>yNS`u8$#X5+3-44ZFn_GeWH|eh>cl^fo=w_`Ol(0X>X0;sE zR@v=E^*_c}-KT!O$a|P};!<{8Ne?*GV#_HNPnHgfsERFW1w$^(X!!|WxAIN7h=1F^ z;-crfOJ;9u1eRA0{{X&-d$sf`6}UFl*J|G`n{xWk$z3fEEt%|8D?_L>DX7T%@zQwQ zj5n6;nlo0oR4<cizwe3U(Z(e8sfug}NFnB!sBsK}|Eu5q+kE;W89hq|t#Rh8 z{{WaiqQm}AEyTi zx-8^*3Xp`hjY=7c0%$tm9V-|rQjploF{4JS*Nv^YVqPuwNp@*4uD7+a)vg~;BAym}t84V@&R!IW2O1k?oaoPDfZ>$+4pC1G399C7_TwT61EhI4i04}v}sHJaD z6Qh5(ZZk_!%ZYm65EP?B<6*?~#CLESMj}sAlI+CoJB~0pgZ3uZAwac9-&@Wr%(#al z-L6UV0itr2=Vm9iyWH%2WFOrhVus(av6*>;hwmS?H9~CZWVYN@sp3DjQq4ZBO8IZD zs<#-5nguEG#{U43vTVDq*9z4hW80=W7;;O)#L7@kY0{dLoj|A+#Y=H*<8w1)>LaCU zTQ7MxA7>;z0QsEHB`R$O_EcS?dAd%R>gPu-hBoS5SNFA)olYq9NEoK&E$&UrTG6M9 z*S6Hy#^aqM+O~{Ya%0JRnq$uMVZH z6>bslQA)7j*uMd~TsJM^9^J9oVl4<%BaQq&W#Fn+vP1FEHCpi*}n@^!~!zK9cbOxrRRJlItwQA&*qjMwW@ zdLF%S-d0e06?Z#EbY4-cXcrzeMM7Wc*7GFaAE;g>D97oE)28%RzR8n-i zLJ}V5ZG_$@w;4)9C{XgVAdnITYl$0o`=M8_U+cK+D# z=WI>0&qyR1k3!S?L*1Lbp_gs_yF;7-6gmnTdi)aaI&RJ7n>6N~V|7oZeH?@Hzls=ZJbkndY|JfbJh=r`KQWEzxpzOPMx4AYPFo{bElyZ1 zZ=p0{sjaEkO%vL+4;Q-{w_!0*TjCre;$A^MrO*EWn@!Dmliclf!8%6L(cle!+66;V zaA^TZRfZ&lB<7VNU7q8OrSa8sLuK@!Uv@3U8X2)iwXLvAl3R7ogV?j}!VhTyJAyKK z)dOhT*ImPMfoZZrdh2kwJlhbU#%WHx`_8LM8*K>9N>RuRL*=rOT0mux(XCe9v$k$> zS51Njmp#US1@#5!1JP_>PJ2`N!+S((M=fraLT-B&-DGa zJvD6z%xvyDwzjdlXTnmxa{TkIszb&?#R3~~#geopy1EmmEM?YRudq3HWMw{NgwzAT zew>!wd*AHQ+fm_Rq*m<|G~(|cRduf#`%^p9PYE^5e7&}I-PSn&0B6=S_`fP#R;>(1 zu)scZs9L)|VMz>u+*C(QKkjbiIQ(&JU(J)t=^dDz`n6`7zxYygXSL3p}YnBU4%)d_J$Q zlDw81XB&QAoPTKCTkZsH_3CSMwdGkh(@uw&win1k5R#OFn$#t3(yBfbWsF-Zf0Eu4 zz44OESjBy7tZ)`M4%t}6Yand7DTo?tLVnL*_yt}4eQ#^6zm zW9RtRl=+S*^-%)^+ZdK&EDN4%G^Va6#Qw!Cy0;ti*)UouZx6b`$TV~MXg;M6-gji4 zMBf&6HqErLY1+)D)v@hqRaB;svso)N6?OFzpV)A%3Sm`Lf!#b4?=#Ot-hi; zrX7a&;n~|+kHt5dwFD{l65vBokWXu_5EcCpahms!cFBy#Qa_3M1-^b+dvR}{&JQ?& z98_@!pNH7Kt(Zh;Q5l61Ej1oG65MuEr6@ya8d8u3Kma;pgdNu-%Z+Nj&)VEsTf`${ zn?O85ZB4&gAxdT>tE_0UB&;EZ7a^pnYbJpy3Q#1T%sgf~S2my;dXz-Fmex5#3&l^R zLHz5xF1EPVixh40or2BnYHfG*RHdq?D(NDEy-q9}YaAX6%R0zZzToHI6(jRJu&Of*t zM-T1;-JR*i zWSt*GdbFq2*VG=*wrocCOiiNwmfsyH{90*k0zZ_Iq@}f_c!hc#snZemdrInC?t=P| zt{HCNSwn|2d0+x4zHYg#rEZPYy|-PNlKrAJ{rM%pw4Dsx-Eqd$MQci;nCr|OZnuSz zyHW!A=bA0Jne#N!rQd4l-=i(kqv#vfm4tkDi(S@ZJxQdEwzTK$)W$j2xt3&ZIA7wr z^RwAt?WHtc1$^ONy=QWc>EP4BGxo;Ldt^Zd1r7x#;|Qr4 z8sl=4ZNS^1an$ay%DQ_NTW^`Tb+3}(I(FA4gh>liBUWsR0W*s*B+@Ab@cd zNUzg@MrtZXl@Zh)m`sHFH-NVdsCJAdVoJv%TH8ugd;Oj9K%~iPmFb!GVa20p@S!&W zt*e5VB#m#pI|_{t(~C;yry3Lom8ejR`*7}>B#g?%Y<<6S-TW8MJ2u&EH%o=894*Nm zrjl172(?H_c%=g~L=sIqD~ODY!H);TsGMcnqsp<|0ntJllt)_1QUz(yUD0)x&)R?#_}fcIdP3BV;d zcJA7LaY=Ee=d)~TG7FDMeR>n5Wk5SlD%*!BZCP3qRdCw)VRT3?01p)F+ZR>rox;`H zH<5J^l%%PL5*Ev;AfV*0epsYuEM&ig4O*`lYv{4ll0b4$y6(s`=D%?ai$Ps}UOP%r z2&ZxDh{kO4_=6<(f_rtk*dAWbb;>=Na7vtYZLGPdl3Y2GPkF`b8HZ#shK&GJ9FAGj zJh{hIfhI^@MQCv|4YICFa*`c%g=tSt{7bYZq4h}eRH9Ztbkdk|P{$%nSfwe`9!`-$ z+0trjOc{)-@hH?evZ66QGi@;I0HQ!$zgK3`)S~k#acD{w90jE-G%BO0?8^(Z1P;{~ zLjWW$y_=xI#xHUp+RVVR_4Y)4Romz@k z8&EavtuXf7kh9(DDMF^JJ)>&P+;EznAA?~n=(#UlT-E`q(m*}El)>kFsiLF7D}Q+> zv2L;}a5Rli$Fs2c1;eDhfO!Z!`Dsof;H zq^wseDElzp@-DVfQM1tWO;vBbaoSkiyi*>2k2SW|3&}m9NgW9Gfrv{+bb=CiOQOr5 zLgc?3xa7^F6@{x)w#VIL$vJ6AQK0r=zexvGtKO4U+#gKNJA3p&F}ADb><#4lT^0CR zxWz-=pVByiw|_=c))sx-s{=k3zjUP=KIpqN$&DJ|<4H(4C2C0qne;j1Q*J&NYCv3f zE%nm@XmJ1|Y~8NGN6en%!A7MkH6Lyc+jfph3HOs8YDt~V+lmJgZr&$-h*!4^ZMK{;WLt7htqN-Qx%X_rKN?5y+*M!*@CAHYK&rmXX%1LPcC=nC z8WT!);0;%U^+D(cg!RXo>Lc|n98Gc=U;;B7hh{xU>;tX@Hl-yj$!)es3DPUxa0Jyx zIrS>TdTHdC95KurYmlz+ow(X+OS$c+K=J@1$G}Ml*h41wFZ*xXLqEXYp1vE|S|ARt>Ert^-n? zzLL0h%gS7fbYAUNNpM4D!OSjDf0dUJJcS$@+1(+!MZF-feb< z?q_c_?8i{?LXw8if;uj>CcW8XG42)1MX+K0;n!!20fLe2A+etu!@@o`+O+>$)|<;CJ@snsj#Cpr!Lxgn?Z8W%}?1 z5J-#$$jEfZx>9vQC8boFRCZuMKuQ}$KqKnGl73>qU>v~vFsx}Rt(4hWCCa!*LU6SZ zwcwz`11zcbV6;N#P@v0I6#$=Z8-kYW z0!FC<;;^wwP-DfZIUH7@3d;Zyy0utR6pYBnr1w&Vv|6N8DEEH6F_j)Ln)jtC)agg| z44MZ&iAUXti^ugVUK$YBCeM{JBUd)Midc0EjmD7J85iU!Lq79~7Z2f4SFC|I zgpNsW%Ab_t-OS>?DY6(sL8nC13R0D2T+(^`USl9aF6gd=9EQtp3sKlcDB z7N)CEi0Pp2sl@R36++$q`6(8ZP@;10$Ecn;Df73hI~qU~qt^yoKku-|FnVoYPXqQ8 zm6`tlxo7;%`$Ftj=6$#!+&x-dpfIgs!DzHWdYJ&GnAS4)D-b=phoaBcNl+coo6Nj4 z@E@&1))Z^7S=N(MTL!gHs0fXl>t>RUPtS$E$qfx4NF#Ycb&*` zc}DHK*Lgy=U18iW>bOXj0${&dDU;f3hSylA9`NS1ILBwN#3inMNlqIk^u5}@3}yYs zZZiDDnJFh#K|c<7j=RFb{{Y>hoX2jN;&27^N;jTP+=`zusJfbIDxwHU$a`?o-)=zT zs&kUww}as+(Y3d(eHA4nHu{}AhKA7GVnE{Lay6?a?WwCd$n?B z+WA9zI;6I@*(FAb1sy)zOG$)|iUQwxUAWMcsWX=MU7ckCwj?NusS8Cxt_y8~W1UgS z4fgh)ILl78RUYiN9Gg00psG~I-Ff6Dfgw&fRzhjmE0#3-KXxz|J>|z-rQRhA6-ZhT z0tyK0^x=0hrAD>=E%hjumbv$fxKb02ZoAT7y(tAycBDd=XTGkYsjV9a_?JP8J|Aq) zba+rK=XG}u&cTEO)bYqJPID)%u3F@WWZL%R)odNDAeNc6ThyGS3Tx;-b*I_kVprR{XWGa@UNgAPT#gCxl@}iYvIl-j@Ql2yj+ME zX>4ip3eePrA1n}lCq--fxUFP&{7jR`wjEFODZF26E}sv~-XAVLI`KcV>DSPrE?nPy zdjsb72L{>|XV?){q3y!`6?MG0f>K^}+?13WcvIcnGR5x+V?RKWb61{9k(ar*^9wD- zBOLryWOV?nwAreU?h4YloRdVt+8XMJ-fmF##l2IdD^#MN?(9@mN+y|MwC?E&!H%(X zj{!o~*^gds?_F+1ZnYg+x>v+=2^IHehK)UCT&1;aQP3muCcAmOCM$9hWRYK%2U61C zUxhUwQw(LeWM@*G+r>s!hKr#7mVGOueaL5h@*D@5*$@|7J(1Kcm0g`j?<-XgkUPJ4 z`#Qkpv!$9xO;p{EI7~X7S`R|&1bcK?jWWy>A{1EYLvZ3YkcQhr29T7cG@um4B56z1 zHc&O@Jw@ zwwRubbj3odmkq4_U@2RCUw15?Ik!61gUWQekn8AabZsC|n$s%cxrb~b@ax51cT~3y z#gBG?D$>ji=eSn3t#+xGXIccf)OMV7qcUQkHjW-EYqud^=Q$a2&Xh(; zNjfnslTrW}3|1Z1(R7nk?{V(< z@-vA8I(-2Z-J8X`#^17AQrwgaew{(boLg#26QpTDGzZ);mM0e2rgO&izItp)<{9AWdNl~B%aD(Lh*5`d~AE1s5dqYXI8*fLck?81XDj|B66^Y{R-!` z@)APg0nmjM-Bls$rNAk*f#*6;gy&V0iOWeC8U9{)ENp7ToRG^$!b22tKT>k(Cr0m}^=Jk+~@hiQ@ zk3Yxz%4XWU0*Dtcx^*P@PP38E-;7&u^xZ@yXBMuy*{pUl?AyukxlT5aovpbhStlr) zWzxPuKM6|4NA+T@b^LgPhY+mOUXE&%p+`@(HK@aQ9!n@wTh-Aup{R8~n!dR?KDNCy&a10-;^kwx(?R&$Uk_G23BNr*fT1drzxABV+Dx&v>EZ$l^p4y**;}qVm`*zTTIdN>-thLi=MQ=_c&-Ci0HY1b$6Rsh4~y=+qp%q*^F65o zM*6TJ*FV2KK$o0DE)&!ZNhlf8opEQu?mULy$O~z;wsE6Rg3C9K!NTtR8?qL37wr{$ zd6KANep#?#+zQ%6b<#;nYNU$LiYvDrDf4H`>t?lF6!==%Oj3dfG$_T(kG6x%u6Eeu zCZ#&+HKxxwhax|#v;&$AFxta%T!+#~s60VWFZNZBm)8W|_kmUi(+hh8`E!>z!+&0U zyU!y=j|Y#SmYi}g;%nBFElNlsEAvZXB;*H7Wu4r0^mlOH+X^x>S9sBMzvYSyJ;pxk z5qj#)IHX=3O0sV*B;P!7xM%Gx^8vdS;3Gz7LQdJdVZ1pnakI8IWwBRlks6+GEiJPhpnOg`};kr2KU1=Hjm}p(;QqLCM?l#?>yE(6QUJcQ%Gi^5g zp&nzako=3h<~*iSk2I*!q^v5xsA7}3;cS=D*uvTenwofXT7Pe^{!b4!PT}2BG#0kJ zYN{OP=3M1Wy4)TzCtRIz`LYyAQ;xc#wJ4uyIM?8Tu*4&1paFA@_0#UAbg$LUH7PYn zAw+fd-~x|xD2AeIT4#mY>ZH-;awz~07j6&$r)-ZzVd*a+m&;TRB_~1l(+&uzqBg8> zBZ{=1scq^__lWl;ON!ll6K=_Q(^6FPEUlH2Pg9VtF}~Q`64~MVlDq3Gp}Xga=#tuT z{-i+rKjN7lo@Iu)+lf}Sl+yPNAb-k|-~23CyNJr+W%VjgWr-V}4_W&Pbn_fh8r9iE>2rsYSinCYCXYCEHLWP zl7nj?B~+yi4_r$ej4zbN4O|sT*sF-*5;>$a@G5@e4^G^R$&jF3_SEYXD{Pm1wJPIx zYAe8sR*Zx#XRcVzH)nD!v92sEbDiiKdsge)PR8Ho8YFCyv4`(EhuEjR)#*=h?91lZ z+imXKZQ|j(68FeFlIt;0xamrk9CcuXj}WJSV8uHb-3acYe2$E?GnF-4K4GH`?IkJ9aG^#UlwXu14 z$8kz%gzIJG5)_qIpH3$X;PFycSEk%la7+u666{h@66&A2bpHT#mYR^TJI`W=b_(OJ zs^M!hZW+|iq$7;zeUdd-ph>Mjt_~lfOx2}H9Bs%)V%(au->UA|mf}k<5KqZEwNR}z z>Vx~k>BC3peF_6`U`69PqyAv}{{Y2Wn}sAxFS4;Ii2*BVr3|KNhkX{xKcil1_UsZ#ULD0c1^*_4iM7i1Sst7&j4G*Dpk8A$` zELWLO%bTOo8PpCH3$<#K!W&LilGnOpZP?qH=y7eRl_4;U`El87Fxy1BJjD*pIgIhj zvDszrI;4lMrljd!Yv||Wn?lc`+V9IUflX+*W9v>g5wp(M26&PbXm-OHAIuSxJNW6@z1Kj5o17@_SF#6wT}3aKzjk-M21~9oEd9pzCAsUh*83&6*4s>Wmh_1-T{=+PNI)e@KVZzz0&y9X$wOxYrIo?0XsN2W z?VZ1MlDTEQyG!JGpa6cJzd+RKRrJ5;JJ3b5f*?cWu1k!~)0U^PMj{O^#jvux%WRra zZVT95UeXH%y<%9CSX}0Z~d3pn`Tt0^S2kSikpq2T7eqkGX z(|w`-I^(Gk_YW~$+93=D2P0V-a1bQQ2|pkFTUDyMp9sR;qU`+HiA3{)*az3>36JK3 z^FMF5?ahWfhg`T*(9z;)_H*`;^51^k_s!jXy6$bmbb9lDzQ}S-;_o4P-jMsM)TONZ zdxk({1qCtRxU5_@u!!PsOIj!b_|KclPkC_Zr*V7SYDwZx>GT1h(K)tW{JQLWyEm&e z3w+yzaIcm8N&^=bTT+ISpv<78Y8A+lmOYfi$9WNrh?Kc1NdX_=k>TN*m{ppAvbWdc2P-Q%x+EtENSj|;IU@2nc}*w z>F?JKzkhWJedsF$fZrs^!uyswtYticcR8>oi?e~6l=2y4pAYLR~+OoQh&%XBypty*!EiNWt zNC(1%sZ9pjN7N2@x$dbg@p#7BTY%R;-2ij0iy7^kF7uh!=C52{$d6ekhK8UD@S)-K zo;spDt#?N@a|PpjSmHf+z4ATD2-*8WG1V$jLqVq7s>(tDLwc5@M5I)b0Inx+u{UpF zn;fQ{BaewyjQsL%P0IU-MOx$Z025Q4-?VLL7M>@vZY11XwfS=Q<2KS21>4%jpO-@0 z0KS;i(vo_sG3!=+=dZd=n|hP*)W^i@aq7t3|iF_v!XYiZR^XZ5DXGN1M#vd0d_ zx`VzqyS?agrDh)_-DiykTw{ z-MHutT<{CX{{X}4{{SBq2Hh`r?l#%CO`6*(w{AX1gu?es@^naxWb+Hn!pv1aGE$cu z=yZTPacY*z8^?(c1|2*r;;WKM>nMDN@pm5|SK6LD7iHMiYlhOe@2%42+5UJ^w9jpo zI=f}1x4g3XN;1${Rk-}b= zIJ=IvR_o&Z?<)7Ychl`T0t}0qW~Nj!*;CJXWhp2LG|w!}F_w1oac1wYogLF?#t#Zp z!`8Qs_H2!}i?WUO5_P^xb0^oubJoqgH&)SdTDHFCowt7wZ_1wJ8E?cdGhIOoG1h{i zT|%tLMBiXq*3#bjYm1h(^S@v7AKI=%m$}wXUfOWy-(I-2 zX}((>X5j12Em}mjl~Po3M9AYJ{5DROfM|IwdvESLmTnk)$C3kw5?4jNd^shnUdDa5 z2#)O6L#j#ZSj9kP?HI>!*CAMzpHtowD0@+&7n5VQx%yZ7bzk2T@T- z@T3BJYf59FCUj9ZQANg4M?K7xtxz0yY`E?X_57IWR4cQIf6QUj6zNkbDyEfFI*cKe zy%Ae=w^XLk!#d{t!tNnFcXrl(!+MKuVb7nMxkt1q4{hlSgCoyM-Ab0~hzde_QlOJuQg(@K*~+)YYdia{ z9jnmpA*<)@+l-bZtqYK{)1r=zYM}!>$8I{NSjU3mdt1(h*DuS_P$JY`X0X!80OCML(zY4Cl7jyusacX=3=fQ@D*PtHs=Y01t+L!uzP=WV`zCi zRbJfG(H&iAJ-BRol!r7B%D&tv=21FpsDdJu&b92viwZPkqR9Z7W-wH}NL%PV^~b6b z?Bm5BwiPpRg6Be)njrrGmN29P-wHD`{{W|QnWU%hmqSXDPttL20dvHk5%s5E91XOc z_q9Kq)`V?iHIaL9%|)cLYBek*5>k>uQ0~J^wzxM$lc_?M9^7|*v46Zz+V{oUq%AXH z3zfaau-u4J0-0I{lbcQ;^gWnLTTu3bZWdIDrR5HE70I5 zm-7-^$eYu@Q=IRX=<;5fl ztf=9jv}Yimo97-S5OLDs8PzGs3=hf=1Xo@nh1F&TSX|qHo4V$x2UV5~0wbhEou^dw zJ#gq6AY-bN9j!r0p}|t|ol#ZL5)OUm0YtHJMS;qrrWat*d(10!Au3j;(m=t0q5$$1 z#15)HBhdF_$hATxrz#CUAT#pblbGhs{NM6y^*!1h=iLA=<-W`drktRg0!d*Dtgxq zf4bm23YTugyJ*xR_Z_7@=`N}=n{l)yPHNVZ;Y})k!G~6vsN~>@F4vkN?NUmf1#t6e<(mJ25921yXO~ zph;IL%lSpJvD))>?V}`1el^Q0*WKuFo&ZL8h(A^bE;QQ-pbpZ6i3GhTgt4N{K zTI0Y~7*$|4I-2`1k6IqId6ug~{^N%Y39OPOX2z6+fuDW{tu-T5r5{_5GQ%yZL$4?y zmQpIEDMo++?KqCTnXxme$3*iVWi8CYHVU|v(XENMT;i>S{AWm3>)i=$+mwJZjT~l{ z0r#4#+l&u0cI*4Tqwt0FLt^0Bx3K*u`LEQK%}abh!^}uiVP&PI#*(guNLHbjykmR4 zgi9G==D9Zobn9t^ts1m~K80&}Kh>u>b6(ns$6JQ!4h5D%m}*2DWOEVwhJX-;pL5Jr zQlc|Rpgp-|jH?OSyk0Kk+%yv7uBNnGM)~}}?hVOk+Zi25z^A)WUjP|E=D%L5tEZyNe;<_TLS`LizjeRhYhu19}wwx{B(NK>iQtlM!~K?9+sG0yq@ z?_|sGJ*1G{E4cPIX>yZcK2Aabe+^XCxwc#0i#;=PbnD#Kk;z=7(k)iVNLmzGX-O=d zB{kFsn57@16)bj5+owAjXeKC)R)f2+OzqyzmLf;Y+zKu^?Iu#|-dZF1h>nV^6?F+2 z^*+pX&D}$3!F=7sikgl{J94b=OPeM1xS3BZsjUKt(obnV>@~XT3`c@SIpmdYxGD{; zrZ;uoHuHCC-=W#&D%yb4E;AsMp9<9B%h{GPjo%~RENmbVRnXn2juCA#_PPBq(L85kagisM*i$kgkq=9T6~xpNj(N|hS@IB9TsuD8Q5 zHZYh`4!KndG^wG)SbnPD@B>0{D1xAx>J?QX^cqt488Vk79RDaD9QycSj@gCZ?+2j5@f|$j%{GaNGM6eWX&3uLGg_(-%ltR*_vl5<%(j!CD1X%sdc*Q+kI?yEX#d5 zAym|M1BE)2d3ZZ6!6_DQd?;OH1iftpJ2ngUNltfMy@3gv;_n>;S~q=Qx5`nd+~(1Wl>3S zSpuSKUY^W4YGafh>N7CAM>Po$VsQL^}vxriRLW%W4z!Hi4cJS zHPfAY@$S76fP@;7NCQq|t`U_TvTH(;uCfaei)|1eSTr4=cheE~a|Vhl1}elzI+Ap# z`Yo-b3T8f>y5XFS(^US(ZkY;hEu`mBO5uc&D?gklq|3Wy8#8V&I=QJuI2i}HqA=un z?kQzs$l~DGI#o7jUt&POTeJ9U(#RcmRUSuXpbv#WihU;n(45@M?x%L(Dl;J>nW2Wk zA4PE}(^XrBZAwd_H65c0`DGl+IU(CmPRe%n;DMkf{$V^+x5AA^S2J-crz=D_yOzfM z^>;BE=FFE5`K_E2CmDPWXyz&H(KBtpWTD;!A7QWRRwLARC7xUL5z8Ea%F3XxhY;4oHV*43==RE*`zu4Z%mN|yGFg1xn312g0&QTk3TOgbzxaQ^^2 zt0S{-58C$nR*w=3s??HA6Ob7URWI#> zt{WcbJ;t2ZMefY2V`CXzXqcaf+td{Mmn|u!0u4rgP9{E9PX$JPLOwN3o9_3tS|X=R zg0~zbngRf!>s(9N;hg>|12?-Ow;CN&A&x+1eAwpQJ4B_piaSq+huet>@U&skS8ca& zmQhD)o)>N3DBNHF0H#+|C~K!eXew~V+Zxu63VQQ#lHua)) zQ9?Y)y^ELkcXDUDd4|_U@UQGpM*?~?^(E?}Lp#bHq1y({yR5wb03_em>QgWiLb%N; zuSoVPfd{cz_Gf?CSPquzM)pR967|h{w~7d-aU!P)9edcITq}>&c%fqP(U9i6~^0l%yk5xF;vo$H1(}D zeWA54?j!1*-8JC3=X-AabT=Iq%%~iHN+hl)^|9&KmTpX0cPrC)g7)bNj*G};cBfn* zQ7D}llp>_%=7d)gSzWn#ZYtk+dK6YKIghz`4?QQ-qQ1`9Hz;P=CvO8f%_#v9lrlXTJZNtGQhC0<=frmVpg#n&~pEph+{)Kk*3UAw>XIA;?T4SYgs z@4e}KP4!Rl{6;sbkKyQ(5ehk*x)^piM{LWFUD?d1~*=BlKcaw5sfmr6`j8*Oa0>;x%*_ zC<;B&8m*g;Ev4!Yl6&i1zMOcJSYUffUyUkbK?@63l>YF@kFyPDh&{Ti4YS=F!B!US zdUn}u+mDDWJ|;wI$6Qk_l2%4%H0O-w-qp{M-to|_=Kk(Dpke45j|56B`pr4?E^US^ zf_^HpkxJ#z3?5r~NADEM3(I2dSQGNcHp2qz5_3CVGi!{rt%s753KWxCV zgYk=4E!-myiD*d4L>|$Gjy_zP@G0z&3GKsy_kv5eO;Q|41fggk`;H$=A6sanZ7vHO zrPt!K$lGOBm&#e(mXKF!l%q{XnWT}}peTL0;;9#1oc&+ARle>fU)yy4@KwoPd`F#& zC8Hipbt$0hrAPiGZthH8Kl zYkh5-cI~%@WuYmOm1>DFfmA4V9J5L8#e;9>CboSmqM&cRrw#N~r5qFkXSeLwoBL_R zzd~(M(J_WDN_;EIX$esViLXwba>e%}HOsN?F`ir&CxD8=9Mc5t;iXEoT(QF&XM4tS z?Y1JK+k3XOl|YopN@L4PQb<9iYDFjr>5SiNZu^^ObW2jx#;a}aM{P0pk&BCR8t^oy zH5~BYBE4>qy@|XWqj|LjZuCj6oeI=G6I`o_-OY#+0X#mnP1u{9HO9vCP;}K-4^Tdy zw=Wj%SvuhHV6AeqvOG*<^rdEC=k&*Uvz3k29Z+!r>y#SHGmCwE7 z?A7iL@!1SXA&>Zg;E&k2CyzG=6Il80k|aj6E%`6X+|gkYq)v{7sj!40Do%%1wA6uL zb^_ymWTL(}>rq}n_#`3hp4?^yxx4f49b7z0=Z^1_wWi*#a`wbGt=o6cblPLdQdII7 z2g9tUg1G5?3oatGZ_E&1O;^v2+jnVke&c4+BPT)VL2OIW|FCNkaZwFrrs}<{?n)ut8Hq3h^dyxel~jR@_p*CCA&Q1ZTxbc=ZT|q9PU+%zE&Mas{6VyF zqM{M^CH~0s>$6?ov^ymVZRePAO{s0GB9c;i9BtF%VVa&`4x;1ReEsFACKr}vc z!CM15`hXSjKyD=w^i=eoT#yP*p+H449_%=K>WLg(D7k0~R93we+18jCoDn66Aj@bc z#6?d^U=n=@WGD{`O-g$);3~Y>wLv{3p{1lZT7#8y!-!214|h!pw_c^5ktbw&R-B|A zFKjsIQop3z=@iSlxWs#U19CTw{{ZtBQ+J@5u^9KC=#Q`{mxE<|zcBH8&KfDJ^{}J7 zR!S0j`|)$$t*$Cz{#BFql3>lpZhy?BZ&D1Z2Rm|naIGL+9a=~HqENrrhF!C`4#GuW z1v}pu{GG=z^n>*$pCGv*Tmi+!8;0L;w84_=ajnd_kvWH)LyB-nLP-FFQayxM6@9+S zW~TQ@K0J!B??Nzh)eskkt^_-1quQs-C5-if?pTOTj9A+gnl(@+%47HGn)HfK0dN86qZON$DLHN9*;lbFd!S3snm z!Z6}M4ux27m#=}{O;B5}TsE}v5^?#9d&m2c0O%Da9M#!%t>t>D| zLVmF9OXNa@QPdHT%TV@=dZ2|~k+MMa_~Y(Mo3Wpdwf9rS&9L(H*{>=!`8BOsHs}59 zo`W`K$lr& zLu@)$rvi?ZAtxX)i%Z(veY`TrTh`(nb^1!%8Q*QecayfabNsm4=7I->Kui3_ltlH- zS@O)N!u?`#ok}G^x1&Z_LMz#+T`==$0PUhfKn|h$ijCjCWLx>7R~G|MtbWy1`Hn+Y zer4rY*3b}!Z`T?CJ5>2_lV56%FB?LBtXyRAqQ-aqfEj6gD_?4lCT<(N$jT2&TfSOP zmWt2}yD5oh=+bDat%P-om0^8c*)0~G#lw$6zPWz7-L^HAcb!Eg47ku*Qj}=|fa*DW zF+%NiDXu3DN_{(^uw{%bICLv$b=_(N89wy{2X5F^UjmLX^%jV#@9MT4u z(HX53B1@!Erjy#E*PsNDF<{-BzS9qGWtr6Q^r$Y|_NytmZv+;suZiRbiC;{fj(rMj z4^0-xo09P3Ti2DOGfSrOd7ze}2L)El2G?IHzQUqP)lAQ3K7MyMyLZ{yiv?Q`AU|J< z`*Znx{IlYAg{xD*6y7zM%(Q; zoRqLKRFVx)+3E2sDg$in&t)XaNG`5{Y$J#HmUH=^{MEsUjo92_4xEih;NS7|FPGc3 z;_bNa7aQgB;{D?FcX@Iymq{)u*W^CxwJmE}^CXjw^|i)DZ3|dz=ew2C z%Hrp^)RV-ND+vkJPNSj2sN+?+TuNMq%GRVMEve_0LRw+8l`X}r=}Hm>Nl`ffFlnb` z5tqEs4O2EjB+>}u1i8k54M|-o=DsB4pHKWD!TTe3n_M`Xy)tfhi*#6RqTt=dL*{f! zlc`E~LL2}QqzY4@7{vR3xNY+pS>|CUq|;s$^aaqp=-9U#XA5as-jBN4oL>M zfXHrpI4%SQ!^}LEi-y>_3GCf@(0rEjIwp*@d9Nji=_M5b3O!X^g?q5^@{ro7RzTTg z0Pt2dTiV;VxV9G(alDsgT(?r&lgZb1nbRiWDMTfx4UttA8>j#dr`~a#+wH(_U440| zXTUr?KDD>qZhtcz%sw+tuZ;Nm5ECzUpG+HC@ny9ZZ$6=SvktL*yHzf?v~5xVq&UkJ zA2RA_r&Kz0>=Z zc%~eVao(){FLkpx>NvM&x&qO2y{r_8kLm~RA{A@)uASJhZfkk+5(_we*tF^+!@zpf z4$_w22WoN?d()cqH29up^zl9lE7~`h$laGdWaR>_(-UmU{88v~2~ob^URs@p8A>&y zVbq5l9f(aSLy7#QmrbCvxA#F5ucM#Rs~9WC8e>md3!#sIVOJ47mjS@xi{_f z`lnl-en#NsJGdTOELx*~d5{ea5QQZnmeM=G7*6TTu(J_I#*`kk;~xXBZrwbbIG+#G z^(G$a-j43w<$T=eikn<3i{JD`odZK~;5Fcl9SIo(w%7uLwx<$S*2R#9IMmZ$T1w)Q zNL}n&T`!O?lH#iT?1?es2HR<*J5tj~&q@L4E7^}lXx3`c#Vsw=vQwy&L`H7tW%vB6 zWMwfJ8U?LEPACqCy94ARd1~PLcTWb}Z&5Ft&2_vc++(53d>dL2t#Y;I9x5SPDMOVp zK;E!qWQV5M3Z6CaTPJNN#9L41T@%AkT9tQZ=(p~T*K*t&T@Ep8Oq982E&yFiO{i!p z(~u`kaTg7dZMT;tD@|0?`BNrC&xjt7>V<4QP_#BV?ZAfZajQL2VV z2t$sx5P4*R6r6dh;c{kRA;-WZDBPjBEl-QHZoeX91#9!2OR757Ni-yTaGX|lWONb- z(xl_bWb5Wp#QKV2-}&cxUo5+eV%T9yyG`D07}Hx)rA&KCJ|N3uRWh$ZLDv$P-M?dN zD4dYcIi|UdR|ki_;H(ZlUs zBl&NVR$SaeCY5JDNBmdJJ;o|+H*4LgNLL#|MWvO6O+k+5}+1fd*dCZ=U1A;lVPg^@yzIrHC-`6}Ehy}w|sZv$& zn&1K0hc&pSCd#3e5N1;v+Hl;bMs8C6#ykgGHLg$N? z(n38bBqFK+Cpz@NJZPB6QtNF#lH08SrM5zwszQ;_t1ZB|R13H>PQH_u^)m^yU zrRh(eO|r%62Z@>V`?|cQ;zsALIf5I9=L!Jr;=@9h8_J4ttxF!kn9Xl6S!{Pymap?# zO?qAWiq!0P9fm-*Dcj{xd_(p70M@Fda(5cqxchotw&vXATSqJH_@yee@5?WkAx>p! zs6Jb@Q%)nI&`F`_NX4@icP#J+O2Wq;MK_+|tdzB`>I0vOVGDYaL9Kp_J(4hpFOjxm zcz^(d*_UP+kd`+jrvzqK)F;E(aJf1r&wWzlc8vi7r@Ebfyds;q+NTUCrE5t2xy*4~vNkNN5V?GdoYyLQgI$=sXzfoyP&}m z-DsNpWPz5i*M(flp)ydS4z%M=EeatddvG37@s$D_387QTsr}sWr4m4`9Bm{}C{%SB z;Xf@Y8U;-wAYIaZ#+VN+IgJujcFCunDmrD?0p%f#qKk{Hnm8$z4(3%X*{Zb=)>G9w zdvh4@6h`wjsyr^r$UT9XKQ}kc}NtufBtkV#P zVMJ?~367N#H38e62pL41Zb2$;;mw%2x0|c!y2f=@$SZzbtP&)Yp~UJ^QlJ3|u5>tQ ze&Ql1C39aRdFrQW-z61PfTH3^&1E4pBxl#(jBe%Ute;BUtr;JXC^;s?rh|t~JWI?T z%oU|2aK?u4Ont#J3KCFWe#Pm|Ej`rO) zFG&)LTGA!j2I#-CBu=d+b3(?d4~6G{9f+dg9BpPmMYhp z6Zf(eQBMtMF_~`3<-5BU%rtK`(Y>>=gAt55Ia}2C8Z;lZCtmrZe_TuD*ISO;#FC8x zL{!#*t1*c?9jR`>G}I_P!QH%t!-%Z`R=d>Krfx3u)9K@8J9L;Q^OfF1PTk}gDxhuT zYmmYk)u1yR01~QHoa>!2h<4|=IV{F$?{PmYEBbvaw7dTR+DtCc+i_dML+{^8j((qD zRi?+WE!;oEYUW-{hfU?kjV>n%Y!}c{^JrQsyUe7hYC;m9?2>y3Jya>6&$%Vc<;At3 z{tSA(+`U0y$7aX1_V>}bC zfD-e^Y=&7(odcQXpsq2F!_zkl?k88tTIM_(Jt`5A&$kxr^NV_YYKgQW z59_ajNU+}-w=we1+WhukDf{3&R2kDg%o6$%;i6%VyK4!r1#|eieVTtBax8Siw#Ae! z4yqf{pG$HQ;<5>EQfO<@S>q_r+e*xgVCpKmPh(%(egQcGjv;dkpP5yv$u$l*(eug> z9Bn~psjUDb-HnO0Em;Q6iLbrUb2jeaL(>*dg<7a_6?4ILb|s$ZO)GN0yaC#4h5=aR z3D>9@2=IzkB;|)s(S)e^hzseXgC#}UKA!lx*oq;Wj5{k4ebFby zH?_PG$I`jq>R$T};mrFb+l<7v)HrgKhuu&CDqE=(srO@H*lYqdkoQo{HIe8A-leoT zk)*Ve+o^S>ftpkZIeRg7c%fWr2CHSj{$Q;nA-8du&xq7}IQ^=yZY`&|wv?j}bJ(QVv2 zBt0ilMY-ZP9hLh!?cUdqLH%4)dYB{GML5@07-)2@03C4Bi9+(0?QDuIL$;r895yjT z+G&k=rPMKBVZuqdksE29NdaAODK|)srJ}Cf078s5$W=if4{ipCu6RR+0Tm*Ly8?>m z1;3G8(kMHyDF?{C#(O8|ljNv;Bkez1UCdi2bW&Y1BCL%~J- zk`zHrJ+;TEi{>DTC`mpVWseY*jzqO!WnYP~q~t(XvnpqT^H90TNR2CQG#4Fc1FKI# z)33i0aj;QWaQZZ?!)a-VU)075UJZllC;of zYg{DuRZB$bEXYonsT8G7N8g00?vyNlzMF*vJVLS^-}f;cBMnuiDJsoS;j#Op--N>o zERP6Mk8KVM(unC+;k5!NDnE+23L!Fuk0-%V)b%*^QIKT>&{r<}c%%cCe6vxf#a_7b zJ720VCdl2vr(}HEV@YHrG}`J?q?4U==TV63n_(uZdmonaM^7Y;az~XNwri*$fl-xm z>xlW{EgGpi3B1RW6M0P3cA8~Fi05!tj;b+8H}R&`VU7+>Fi7^~KW-h&+OIrGF zT*pM>L&(urZRH6M<=q6GC&k%=xs*$JfeMJOsY5Cej|QgFT?G2s(w?y1qTU*1FGz|P zsD%2b0nIa94H4j-700<9FFCl-45F> z-{&zFs7!{{_nK-?MP-$6`Wh+@V{eIGUKIZTwRux)$%Y_Ep1W}?)TZ7+2`AX0mJ+$L z4%I-#VEELkcRi|W2x<(*Wz-KFN=lDTrkIT^=;FNS$tp$KTZTMKV{NHt=9?#2rAevF zt|9NCFH_i?lN(gk##i*t+^~@;MUdmIr4A0Dm13)$jJ5(tg3`Ih~I(iQOKC9YlG z)e*G?w;mfI*Gkfy)tDlrcVj-lTOTN&61p#Z-!tY~+R}|t>oAtmTzU6USU~uc>PR1M zCB!mv3Xt1~dAKNtemJRjX5vPc$d0A~Dh{NiD66T@7AqFHjHwGZ+|Oi}6l=6f7Hyh~ zL1sja!3SHZ1u6N2RVyOBYwg2ptBD<_Nl$V3%Xolym^Dih?DGqrcjB?6`GgRpzS*r$ zzYNGe(j|E*_v16LY0LqErRH@F@l_g_6P6bxJ;f$v3rTIRpa9u%$iLq_K z+Ni$MvgfKxNuMRQV}%7evZ|dUunOYF`83cfBo>V?)yh+d)L6ifP}~^lDt=0eQk10v zrAkrAQkY=aB$4z3Ql%~~V7G+5#*GVu{=GeGD}G$ATu;V#P|m=$Jf^n=*^{K+Bs8iN z>jWQ{LFhUt=hLqHw06{Xk$IAQXX;#U++FcLUf{zSq)qz&0K>=htoQkIeqC&DRa<%Q z)Ln#4vMieGFClHJc%L$?@KQ9is$Y z0D|b#jjmgOQmHv-*gEybb5*fwc-gG%EuV62Jj1?YE9KM_3zi#GprFig zInn26QougdXG&vC*t>k(yUl;X78Bg9&{#Z(kGy!Vo~UjnN|yTGF1YH7opDMlQk0`o zr5c)&O-aVt6GKG;!RLwJy~Qh`#H9F52*~?zrBgXr3h4g;p*}= zz0nE|-*G5z;|0l0{KSeV8X=WtX;GNPdhSS_bOB+ninLfqGjYzOf`^h|!<8aC!Pv7J zBO|P#D-`z-abcHGsus0tx`9V$-Y!y~L#Uq9WuO2JvXCe}nsAJ9v~W*myL*j7K)T&u zbfL*hOvR*7cmrUMa%qQ9F-BiqIMS&xSH=KJ6=aS=e|yFc+lLN}_^9K4+I%QBua zDf=yHTBR-&=k$yNV}Bxu%!Dp?yws?T!?%hQmn_b-MJc7=e@MeGmz%{)jGdwV)V*)q zTNpM`F4ZwgS5GbE)O&##PJ3H|J<_G@GC2OA9Mv^%<}EH+_FIf8*s$NXwx$ZGrHUSk zAX2pM#TOQ3^Bg(VY+I#~e7t0EqF_W!HcFJm*kwX>wtC`nS2&Iemv7J=TBpn_m?=t4 z;gj4vqB`OZSlmYiY>M(RN|k8#q^S4pfpgogxS7w!X-*|+D(XC7P~$bdf!5cd{3=%O zgSu_*EzxlxBn}qe4Q*Y^_;kgVAY2w%l7f6GQB?xIgNZ%UiZjLLw}??azT6)va$$D@ zRQbAGYh&(a?$B8CAl(vF<0IiwR%;#KMk?6$8;FZf5VY*9qRMWu@VO=2qBSkFUo6a& z!+dIefHP%Bs6NbkDT`guR056f!GXNw6tby7(E9=7?Md^*+Thoozi}Tnej3_%&&;?Br zuZZH-vx@klXqc#y8hx@tn>kYpxCb>d`GUuSq}yH1iOnNI!d>l6e%$e1x{IKzwmRfU z68a^_T2~TQX;`3FXv07h%bwEO6(4Smr(ZJ`<-Eci8*F-z5jg-U>U0s0ek%y?7R8em(;I<(qxo_QCE*L-r+z!%GVXi-0By^?Mr<~A z+{aiFqp;$Sjczav3WakDM{%<7nF9OBx~syXv`0N& z?=CRpjn}hV2HnlH*r7jkWjJl#mBpD(2BNb83r~Cc2vjqoA$}RDvY@ zg%m6$6;%#r-G@U|(ar%}j($UuxG&M|$tT3-)|ASW4L`DVf^mgxlT7X7KhCKYbAY1{`%fDP1HKE@&orz*s)&P63q3v{{ZtF_Jw49 zVnz8MP+Wqg4Jmop&gIP9I@5?tQA=7w5>HLqqRLAhpFiVrM~DvHUiWWkP;{6KZ|DBL)TfLB0r z>x*xcs7`$ohRT&YDYoOhl&M)Jt|bDerAg_AH-t5YqPiqkwkZ4?U>un`y$ga_aANPX={%*NKt7^3keAD8rL#@j78^ia@xgK zH>pkRt-l6F@1{rjYB==|!VqI_%LLo5^NV<~!?ma9W7~oiaDU5jRQXolT2ioTGp<;5 zv-eHJkeV`@rMs`PEv;_HRGnHbVP2YXx1=FWDTKD`i9<_HmjY6<2BFw#*BdRf`q&Sl zbBv7Xc?vDRtofP}I*ctMn6Pe%moDDL!Yk0GvL24@H7B7S(0d5NWK>YA9i{8xcylx% zMKYsZc09oIZmwmOYWuk+m2dhh1E;^H5N%^VA!sp44n{sgPY)kbkl|b{o2J^P-qN<7 zO%~aqtwtO*!NFFgx0=pMjv+ZVM$2yYf*`6fx%-o*cqPk z*PfbtRBv_PH|do?lwq@&472xO1MbY=k9jKBWw$Nkqy3SQ{y=ODcU8AwgcJAlmI-p!se*HMnD{>Rn2dNjjg1%!PFw>o~FY z_G0WrW&CJ<)kE&A9U0l0&x8?AU{GQtvJ+o0q$0KL7`0bIv)06V#e7wO^)YILx1?}V zpDNaJ9lbI(Rk>41yHyMtD zQq%B(`f*=RKPb<@<2Km1d21iVwEKLwOb)wxX05i(HEQ3;c>4Vpp=zEf(=rk z04P!SY0=MtC-{z7w05tsl1TR4$shELwJNH8)q$v_XFzhr z4$lQ?9nrjyR#WvYgE_eGr?YN46VqH%S2V~P{#xp_uG8%+`@DlB5Sb#bx@r3t z(F}eS;v02>I}dSeD-@-nCUi3#G_tQyYzyil_3&u`_MfErD;69&=?w zV8-#VrsFgewRG_fIpImNm#6p2VQ;nZEp>dRrMVF-)1(KZZTNQ8M^2`SW3q~=UeJ-A z5sxEq1q+IH?Y&@Kq+ops!f(s8+I+iZx;eQl1gVc9l7ge}N%!F^cgrNhsmF?ty2n~F z(fu*DwD4S);M;7-{Q16a)@aL3$^^Tj?rb)M>TQQ56g-pyC1&QJJ4V$eL#J@Xw_De7Q%*cp=gATfm8okg)TIJ*HO7^+_cf%j zfg{gV7FEACrb58gv7U8irso#6^(&dwG?L_D@F0$-4#YaGTo?fDg=n5nN zR0mvIEOYi#&E>JjtJbrumK!l?7n^X-mi-C?b_WOD{_%dF_N15D4Wut(FwRbp?p~MpUF9q{VY%B^(LonC6{qM@lrq9m+`5{C7+Qd8E)&lReeqLqg0G;BU zHaF3_roH!Qy=Pl(U0p*@9F$T*f>%n?sXdrn+!8fJW;BtaMrzE4vlH7<9RRKpbLyT& zZuT!K69CiZQV8}O{>&oxDGCq~xc0`BkfOZDZ2+&k4cuy#VbLij8v#J55D#4Yz1Rw) z9w|kb$VmaQM4YvC!GcNWRP?b&Vb_~!WFt`(S= zYU#Qy;>i)D)mHM9tgP4R0~z*jdbQ1?9JURu+Gn->a*GsJ;VVv+ zt=Q>p(5h2XTx&vtyNz*98`mKot$Cu;{{Y(jWWtYNT!;0S=sL~9&3`*_*5O;*HRqDt z-M2Yfl=)7N?HWBOuMqDz+q-?f9gz1V9`}mozUyoj;g6XS_YWn^iMBa$Np-yR zpN%Im?XFnWx>g?+L9XIddw9KWBq&67=KGP*D46antsr{>tA)r?mb9v3%R1BzDz$Y! z+!QHSJcNqrDWLks0*Jn1An3@GnZTnp)kTUzn^M&Fr&MSfq3K@yCJj@;UTC=ld`YL< zfhlnCfKeKczW|Q4QbFt4(;i-;DB?&~k(mDgal+-gFEW6ju!?ZCBBGe`@(?DXwV=SY zwLnMyc)ry|KDS6eU8EjfFJJop5Hg*UsS8`L=|IXs1OO4iG<8(j$=D%US@#ctSk`kgb#7vt~ih2#~Ts`2CE|P7xloOt;D8}!hue)rOHiBrg+h1lZw)|CL{6bB$+!buRTW2Kr;y&Ay_5}+4n0Ax8 zkFGKl*UFZ`aoSsh$xgQ+CAo2yI+Wz2?#P@aaN4y&ZRxzLo|3CZ!ML`?*fga8iGCWM znTldHpcSEIeg$JT^}JC`5I&8I#Q5Cb zxi` zZOtFLj@T3#4Agr~dnt{hW$idH_97PQ?k+sZT)W(T`*pc;uDg_;lcQV7t>iy%>;X0{ z@r#P<@X!H?S?3J|o&iHx*vTmjXsJ9E{NYWd3yL_$d~-02tBY!m`>Xz`(FS?s;!6A9p#|QmNJc`zvwZ?-#i$ zDt7B3$B8|GYRG*98eNsRjv&#FymN;!1sNMW#KQd(2oVS}oyQ<{#T6IPrwDkSz-EIXQhniUsmXdhau4+hzAgTx!& zP0GT-fT+O`FUgSBqW-N^(mYvqc4CWpC!3OO02x5$zXevykG2-uc1IIJMvIw#p(X}w zw%Fk``Gu`c8rHN;KVCE`G-|B&(jKXcmpiXA2DueEB+ys85Hq@~tuXNWp(NW~0cB$) zC#TnjZP3+B&+ySX;VExCl@Af9@b(jh-y!HwH^B6e7jP_oq_|<7m$mKZkf6nS-EWB= zOlMT%N@>QKN)(kPNd!=Xo-_Gy6S_iZs@_bFCW?dlncUlFllP9>Z;$r_TcT>2TMBM5 zSN6VTKnh-vY7R6_w{GZT=^}^)U}5t(D(z<8d(&u4avK*1<2eOox1qYNX-bkvDJeDW z7`3tVP#JBdj=pVP!LKlM!Kq9t<}w(op89Z!@$v3?ceEhj&~4s{l4SdHsHFHrJufrPqn8F zQa{Bb4CJw4I%u{%x8Iz#r@L?j&GsK}?NxJk`fTKVr{>9v0Xw49ivIw9f#Y2O{pJ

TnDB{v&vH75q=f3qwXbdz z@`7mT$U=yz2dz7@z@-H<-QhzRLZ7w;rMo zF%?r)Bq-F5%zB7?`k+-EDFo=R*M&rAJk(iniT8J4Is*5f)QkCD=mhs*oh2;Grlsw> zA6>wzoe)k8(W-;YOdT3Cd1$V908}P|ziuA4K%*=k1Zcy7Ns8`NAYsBpYEafRxTu7u zvrtj@;98qx9y*rKpa6u9-2R*bD^9557dCT{>2kG&n|k zU{roDG}Z!8_Xv(jv)V0<#ZzM37gr8JWoEUfW)@|sXre`#*0tr{C@W^$S&+))Y)XNDggN>KERQLXp%W6 zb4R1?+=){pcX(@n=|njaQmk#f;Uhytb_1kW2MN5FB&U_XV%;A?Tufz=N~Vb?GlU%@ zLS|%nOYyk+>QV>EE~mU4FC3t8O&wPy*!*{W1Xc>t6G{>d57&i$a3&76G*%OU_~USH zo!V`2bjF7fva6f!Xi8F^fF`u5#3pwkETK7d# zADR@WN{oTUKyH@vYP`wY2Q<()DKCk*vf&GLK>S^L;=Yk zW!xC;28y&?uQK0TYL9evrrR}I1B>wwXs$2u*hD!jT5R>hxHzS_D{AH@xa_5^#-b@{ z0Yuh@x#xzL0w~-)DdsWL+_A=_keErFSLQbA$){;qsQN33F%)#*6@26-4(X}UNZTc| zIzuf;d0#G60+Hq@9VGT=iuOV#2ATzZ+Y?PBgQ%J-c{6lhBib$2I2U;>+99PSh=KL# z3HtNJ1Y+S2E}*LDhv)8_2#gcbIVG*EFZZj95TneH;8_G!#}0kHxM@5wFlYr*mae#Y zsU!fY*RKy)e}h|R@GlqMR_cm8o<-bED0o$*0|}+4GJoS#KgI8*+i2}(#(eokyq0Ib z_pG-^{ZWtQ+vv4?%ek$bzsXjz=N|3(33G4RUP04$l-jBmw3>axN)`T* z9oUQCTdA-xv4>SN+TDWtsl=}y;NU-6`a9$fPS~6uvL)_)*K}&x1_N}14H|sOP9S($ zWdo@_GU<#H9fi8fU3A7PQo5saZyO!EW7i=S13Wy7iXO*|zwoCR;{l?-M_QA6<(?egU8$%F=5C2uJ7DFVH{Fn2jl!)#h8HBpx) z@1rehvDw_w1(E}+AHI5LPQi$qyOQHYe%tt9(bVM^+3o8}zG3ehi~h_Wpm*V%E!L%2 zudXTXh1fsK=h5SP?tQI4ejhKHClZ^HnZ8+B`Hc%KJJ_W76f4_Y3!-~9NBM{+&`~(;dxA8u0>17*pmDNA zK7-qZVVy!`amBiY^dI?q@awX@Ii5mkw65d5t}|4?2^y0U1BN4Fpq{EikJF5sd}e8P z(0B#4cB^fN*%?20lCGB5nS9t!+@5~cWCxm1X}1yZx-&}BPr8TMjQUuaDH^R_-SXN3 z2vOC}?RU9Ol|JUcZbY>yYe+~+YIDk%wOK|wC>D8^ysU-CM33h_atTO1P>**IvD(V-L~oe#e} zdn1hf_VEaq9OHVrAHb%r{n}%1&l6#vHpy}E!C2^kMM??pAEOU3m*PfH@d^ub;mlKY zH{mIa^_lfB%h8wfD3Ov7oYoayl^V#$EnjRzt zBkZl?Zr^q(J8N`CZez=6w=yH3DK8*pUtt~ih|A(%WiOi%pjOSjxGnQmpDT0$G^VOC zo#}^Telhr)Pbv6qoo(A*H3PRvBQgdiqV7%Li==V!S^WP1m&`k^dfiHRBwg+4qhVd` zrR}?HR@}OZmmQApn#${yRE3)TSbu=-RqRs#02K8#L~lRK)-L7``*G6Y;Hie}^z+FU zH)O1Bcki%}NJYvp3v7T*4LXYZv9@A+i(@q!T|&730C#pDFKP*E3)|uqU)?)ja_uJ^ zRo=-W%v~idsn*umQ5nz@DOzJj#%3V6_#6trw+7F?uoabsp+wSKn6;^EX~zn>EYym7 z@nMdDEav9i)e%!K(51k-o}!1K!^XO)kCdcd9pahX$-nJ;4nE|Cv)H&c>#yz`f`^jQ z8UX1-dXQXoM1YiP9j3Wrsl0b{u@;_lV=@}>3qkFFZrix>V$ssJ5Jh^NtS2q{0On3L zw5I<6Wp+0vS^Ut_oVB*JN=R`*C|8i;m8cSEbg0u5U9sOhb@ZNG;Kcs`PX)4f1M-KP z#lR)BaJY~BynfYE_eT?QhRsr2-Q0h9+kx>`jJOZBg@KvUunJ8(tBp?|y)hSG%9*B~ zm4oH`MYycT@onDUqxL2Ng!&W`rWD(X_z*z^p6u~qZNySHsiY97)8cL;lcCKa-UyVa zGSGCcGR1498b~D5+e{o?#o`oY#un@D6>&MDGwAf8=<;laRK$j1-l8d`0u-GO3Iklm zNX8wsWgpzvfN-r>Q+E>4@$9U3P}DFF{#98vL;nCRy|#1xNW0rI{{ZIZhBTeVD`O}8 zhwNA;#5B%ZfBLNd0K^r~_1zor{{T>XgZ}_qx7!}m*;5-uf6>N&*z2CWO*|Y{)A_El z@^=oL*x%Nxlx_0Ed|9|QqNPV}Y?;v?ipY&w0R>-pOSSNUq;_KQE-miKiEE`69c!C! z;RX(!YsE$hc_nG8!cY_JioY3i|LPdHpSe#ZpzpFRafC0P$7wbhmaj<;6tq_RU~$UTojcf1dn0rZiFbB^|x+Y zZb7>3y-vZcUx89o*J&fYKcfgp3w0b7{{S7AhVh|A+4{tT`yP`X=b^s)Wxqx)PLgp2X-7oIcA4~ zWtzZ8kdM!ZQc=Ws`h99fn%d5Vq>U>9Loh)HJdPhZx>Zo19VCOqB;=)OG(CGTm?_JM zD%kpQxpCY}p5TwllIAhBZZ_9gb}TabS1C$bYSPSN6{Sh9(@vPK?i)#`i}*lwD$Cz% z9sF;c6Bs&xPwH3q)ZXXXTd$V*({=61zICSawl<5&8dM5UmllwdT=i)bBd9o`cIz88 zt{&n!MF?~L#XH_C{jNuL+T2^7rV$_1|45tY0+nmj6LFt+j^Ok z!aH|t`gfZ4^^tSCwa1WSqiaA)B{?2yT~vi8OUflIf?Xp?70CNBnrthJ*Uxa-8Pd}J zC!UL}H?}Vy@|J%sCs`4I;o-n8Y|70T+`ES$Z+UU|3U_38?s5cO$I#AgE6^TS zwN%d#M#m6sc|h0HE#Zwe80KX5n=R7bns+Ea#JG z7$w`mL-8IfO|fa)yAy88h!4TyDd*#uFnFcttih!Al9ay z6!r{QeAZjWA%~K$S>c~^F}S(nH%xt{vEU86u(pInKXzJDK3drYv?!O| zKkFnuq#Aap*RUK?cNXk}2^e&SuX)jITQ9b^JB~*f#UhAn;G(^OvLege{E@z#aeG5y zdVaJ^n5)E;A#TXN$t#0wXduuR9(hG%4v9`(YlX8LyvE3*VCs@mK7q|GgzbT>*AgCx zq6g_er|M3xM}CJl&pK~Sx4HLp7oFk;om~ChyF#doiETxHD$34fPdSpv_*1Jo(-nT< zcSbuKa|}k3k-=PhJ^6Z{Gk7j$6}JYb(!PppoOiGv*q`jF&5>$fB9YbPs9O)WRvUT=u3EwF z)Rpr!>PM6|S1bCNmymqx>i4HY0cmSVW<4V{R* zw86&+ppZ3u&)zwkJdS>Arvv3Z+`K7GE2kwVZGTWck+{|8-Ppd9WO7WECXw&FgMLia zC1B*F!h*`9qrA{p+lX6@mz}&H$GR*ZTCTi0O6}UA&ctkna`2llAk~G5uFV3 z?ZYBxDh)@|U(~v1=%bmrKb7`n%XIS1?oRhxbDWP2*F2OdAq^o$rIyesRdl8?&gAX; zOnl*xG&FcFx%|KHTl}q&ebdX_t>xy>^vgu6jAuSkrb@J?f~1dxSHBbsSpj_l-0~L1 ztS%fB9W#5u+K|kh?EF{|io7%3Euqu_*hNO5eb}^H;jS)SNWodv_UyE-x21?Yh~@| zw$j>`hUH7FmFdg^QZ%Ny_u})5?MxAX=?<$e%Xi}TdWc6zRGxn`@3wDOoAr-y$gy6x z3(r4ymkDKp;@@AGvElvFdoiJm9lN6paHtC=X6LqS$5D0H@_XJ}g7?1pf<6A_ChF@pi+qMc)>ed}65IR^BX+ zcYYi#w-z*ZirI>!w?|$H*05HBq6zkAi|)nPSoo}&BimPzTCC%_Jm2d2X-FP^fS%VxGzaiwl_52MdW!+kMJJo3tp?FD_eP zNO`nOxGcK*SY=8HTZObMQuL=iBQr`2CveaOKvF^mk*bhqIk9XTk(l&cX<@){p9vnj

#z))_m8Oa7Af}O!((XVlYN9^f>SF~=+ud_9C&~VRO;2! zASzA@)_{=~zhR4p6B9H9BZdT4RhmulcW^ zY~}t}<&2H3DnH=6H??1sICmx^=G}55G_4OGxArRMy!NTsyVr2+wcXjjD{nqoUD`SO zm;50~Qgc^Od-2k{7kK_Pj%C+DVQ}DAE_WAU3?51Lq2c{J!cJ+hTTut06(3Kx7hto= zQPu&dsM0Hz4mhSR)jKRL2j@19_3efqT0q+&XVC^|uCqs(Tf81+1EliP$=%~%-)}HLST~@8X z20Jj?T|QOR6|B(Gg+iI*O|#f=HV=5!U|BBi-HWO)QAOPM$s5O+wo6vK-K4>a9Yd}z zG8ILk6{Sjn2Ru$~jD5Y7zUroL-1YWe-j5nCX>NWpkrm%HnWV*YQBochDW-Jr^%&Oe zGC|F#T={j0bXuza0LlA3iz|?%LAo<{Zc`y{y3V2Htda^86__IrSt)v(#@%vIgRGp? zuife}y%f`C1thL2mT5pM(2_Cay;4*0-AGU^9Q%FP^%4!5sWiutptz+4R0*vVYu<1|odAKOz2BoAN>zBIfKO51fJ>m1fEA?W+-ZYRA@dF9imfzN0HEzS z3MXE8L4|e<>(xClG+u1gTPa~Q09Ktw48uj|I;%7VBAFB0g-Awo*6L`WRHB~B;Xf^V z%txRpO~_?qA{<3O&KOv6O)gLH%9EwG1B!5vPi9%?gFUrXc0Pq9Xf$+#t5K~m+^Gm& z2NXPdwiM!l2|oz$pu<`vT%L_uaDGP%oP@QeT)hkJ%q3R7r8^v-QOMDel?msGa$QM2+ zy}!An!E+-9LYoJ$gsOs=VqNboG2lyWQQ)lfIU=^ZkRd5`0;JQlcGJHE3Y$65j|CWW z>m$tCH>T1DlHz=Sy;_A1p6o{6uoqQXcEpB-zJ+jjUvVA1${anrkfH^;^JaXmzq+YS zB}XslsNy3xADL$0d=uPH@Ujg)pYUCP=MB8fu2|g305se3yu>S@(&J&wzPwa+{ppSh z1RcP_iEcWSd3LnA!wGSw$wL4IpJp$(`~IcR`>SqrfYev2w=9xFaPhVq=m(BSfFMP|S9PVIAIEC4uy6hqpG~UR{Ep6-Tui=}^M^Uo6|3HoCl(H@VwQ*!;ANnR;Q_(|>nDLZRtNN9CQSH0=EuDe5Qam&*KJ1=F)l`hq2Y-vK~%vzS??$Vmq z;~*0Nj6ie0(x(x2mAv2E~E@K1JmM?;YE4k)eu%zKu@%u zn3$gNk+!ZX8pj_b#*7sz+Hij#T35Q5ox5()?IUl-{8U=yAQt4NGpJXVs+wjqr`e2; zcx7gu6C5Cr?J7A{bZ=|0R<@Ty=H$@Ftq(dZ`ibY8r!sG6FYY_?)(cZYEsETLQjaY? zR!2&x{X-8n9?gptZ>Fwxq?H-n{^(q83+mR9w?Gwd-515%c4VZq@ir10^N1Pqz>4O;ir!tD>ihYCkD+XGxgy z^}8uvV6BxW!kSaFJU9=aL?DH-6zeWF>n^FKr1(~xi287e z>aP@(k+tHE6$)h99M`@!xow494b}>1K3YXrLEIJ8VR+_qR%y8VVT`^7-8~NR3fXL) ze%gDHOhmdn_!Yr)2@V>xrFR96pH3bN#vVE@f84#Jzq6~N_uKXR6@iZ_OG+M$@0h~q zq&|fOB#yZh*0>j}s%r$;hAkYEq=>#+gvuWxEi7{ z;=1d>mKa=zNGdI%3R%!PsObiS+k~9!(=pRsy3aYxt$efZQP#rq6DRnu^4V{;~dAeI;_WoTz-O%H?ViJw9&IJ z>nEixvrT@c@ZaYl`@`B2o?Xw}cd-7%bv$LsOQkjy?@8#d!e@Wx@$ba#`M7&RKg(VJ z08Re@^q<&|vBW&5Bofaa-qw+kSK*S=L;g}m5jl%N`@`Ctj9vNR8@+`J-vjdlxu=eA zDhIdZAN`nWbGCfm65oSlxkT?`R&UdxoRzd752XIUoGt!X{Qm$hM41w1*&(zQ_gbbrs)6nD z#tz=_*b$&9ct0;$a41Mhjec01#UeAUJ-aM%opC?$kF6ks+u;?$Zto?SIy9#&{JZ5j zyn8>zABXv1@`OukY#sY$VnWn)yc>|TQ>Q>d!N1;nR{rVzK85LjE%_I?QbFwh02Fzz z%HMmq8cd#Dw6wB(P9_8EK(D>vaM}Ipgbf&fPoX)B^3!~H$~~Xrs^6*4OLBcQ%i7lI zynzROP2DU=c}a%}_<|sgq#&hUYmREJv6!YWn@f8PqX`Y^0cAV)x6a^IlS%2J)t_+O8r=l zH|N1k$2e~^ReVRcq5xjrtT>_Z7T8$>=_d)BErn4-QkT>GRNw^998zN>@=?2{ z!nEy1BF&k%P_~~c47nkt6s&<-QA0ks5g>92Zsu}3Z;@uWC7955eYLlenkb?&E0=Ff z5XM6H&q@;0Hnp^~0o0ldBK85q z(JOCtE04RXQ$@n<4bveOXg+A{`7{taI#Uf@GY2(28*UFI(BB(^_=`lRBwHgg#FUn( zh)7Oq6zWIo!jVeHa4AMMW^&O(+aGT=F6P1|Ge(kYA!Pve6F@14t)ea6)2T%*%h~%` z=?QTVjYq~3*-d_m#2mm5-fey+LY7RR|P0W5z?tHWJk8i%Z%YIhSQR&AHIhoma^P6>3Xl)hRXY9r$R+ zyeDP)=$34ob84k3T+2G#K=?+gSKmzW=gjU%fz9TWoVV=!(;IE*HSKit!94e`b+sus z_(U+12WvEvk>gX2R=;KzYh3PWDK8q%0S5N-x-i*hC9*Vh42Y>$=@?VXOg|EGPj`K8 zEde3)MhWN;gpag?k9lba$v@N6UF5xFc4Z-Rc|V z*!P#1T1`u+b!Z};DpE!sJ~*TlpechdZX$hJNGf#M{FA>YxTp%=Qr6OvQlB7(Dl`Qe>7W%Rn4s^y z*)5O_k5X2}**?|5m@bxm8Wp?a-XGaJQ=RUXi(RBn+cLJL(YLMge*6bDwl!5V~*b6pX$J9&(?$V(VyTSXr1pG}-?xGt|rRkg<>mkK^eI^YS@)Kr{EWH-g+ zaZo6nM`f_nT{+Hebykws>EDYisANOh%`{U=I8?U{edOYaCi1_Ni;CK8dqae`_+tu1 zfyP@WQ)E~eA-Yzn9hhu41<}wrs|eVb#{S@3@%8uWt8H*imCh>Sbj6Lyx7JCz-Xo<2 z%RDDiQ(EUooD~TjgFftQn-gx$H}U4J7b^F^dfLE#PRs!AWO7`h^r7jibNZX)hE;K4 z;j-;72^+ffvZANdRjHRscrOA7{ zjD0iltXA$}7OQQRGFh`(8bAw>8%+akgo^aW5e_2jEpgH~61u+ka@g&=0$)z5Xt*8f zchtV@=6&3`_eZ?A%qeNw`)vxWcuXrGqB~Tf)SuCgjoSN8ENlQYd&`A)*7>>4+?Twb zfxsv}&u)pgEspMMZ zdBXs$@}7q1ucxBVOLM(OZ-q_0vgA%I+0Qnu<1|$YoaHs50}IQ@Lw)G-ORj$>O+z4Tlag&FHg7<)so2CmGZW@ z0YtSGgr12{RVtNIs!y|{@wlk%BMfL*fN}=qPpQ8Y>Ru#uae@1YC)I8}Kiuo(biC7xi(l#_!`j#*3HBk1qLUcT9Vgt@4_d zrAJ?hAJJFBp_)k;=dAW7>mM*{fuiahF6qbQ?KfP za$L8n`6?NbyOh}O7UZoe)`~QRg4!rEp*757pmv7Yjn-{^gWhuG4ePjNn)%FOqd}r~ zdTaH2ADJ($Y>oG9jkh<1w3A_%6>bPuB_SniZ76h-+n@qMbtouShN75LJAe>3sk{_^ z9{uikX*#RRCJRYYL1=`I(lf;`=9sU|wv_TKSH4{-2upoGdZz_rnpdT3#}GmRmz;{f zF{jqq9Y9)u{Ww&$g*R=(+ER*cbJeL#&UGLG;nuF-rygufPKEQq(K~kS&hZ{&6AA^@ zZ4jhIeXl(^8s?G>Mj`La08z%3RxC`nMnLAYx{6EO9JhbBLqo0Yn>$XC;bnH@1m)Dy zqY+lv7-ltVjXi4BlHM0q>;SL>>QP*daK0B*UfNbE3s=Wj0U!>}i^SVDq*SPJRQY%O zwbhL%qPdDn)SY`a$W*M<1Vs<{SRd_nkKQS>x8GM5H5)w2(=V|4IPSu|H1klX&Ja&; zM+ONd*d%E8Cvb1pM<701RK?0%jW+AGIO^wbvqQ4CG8>;U1%{~76xzTEKv^UVRW|Ms zjGjigl4`Y0@w%jz&L^>;0u%&y-RTOil*tFL-Hh>c{3a!kyKv9V8Al;ce6)buG_GgL z1N7lDhi9cP9_E=k?R<-RgtRS`GDl!7B!P&#oE%QkSTXyiW;&rsmcC@(53=N4EIiAo zI-2qlMGjp(n6FQ^U}@^+u9kc1_2Z|APVPG94av8!s9-o5jX7br8wmh-x@3E?AD7$k zO3Hz#R^6Y!ZlRycItM8YTg!aK-PgNhX4bF8N?f<000X3rT7f-#aR%!fZzvKtD=c}N z_S}}YnwNG4>)l3rt$T@raFN#^5VTFJo4ItJT>J{xcx}l$ zURjqJC3b6qilRIXe4=yGY4%|ZUFei_yNF8e3~TLV>u17Kw`T6BSG!z|pSlQs48Qr5 zg&z}Z?4plD-G&(~YvPm4jZ9%O@m$5^%k(B*>$J|v=3B1ZotCw%^-(zNgp$`I z9SHZ-VYnbn+ZUO(#kw6WEABfrwye^iJ=mSHb8aK*3Zs;9wVbl4HZ8_#O(Xz@!30u( zj5#(nOj(g5ofEwiEjyCjyFKM0XkYx@`hcV>u|WGU(TtMW{cTQfK3gLRj!ry^istRD z)3u*@gsIDoj#2Qw-fe{T6%NCT&9+Y3;h?+XSoP)$A&z-yJyDe6+8g@ewFJ1RWAPh& z!&z4K)}1uuX_avglftrh^#ei`g9p1NwPA_Etthir>yg|I?hUsYR_IG~IQuGC9$G7@ zNd;7>rXI;N9dG5oQWQPCtkK_j@#xZ2qqt!?x+kjTx9$jz*OuO><=rk424f?N-K#_b zk8M_qebsb&*yrU)IDUp9-$k^)Shf<F6G}MBG@(P-YClFIB$bVKP!uZ7XKGx^JoN-uc5&v--+N;3v$r*&GND~5re0oa z4yWL83Z+7la~M}0x$bQ4TP$uh3J-O5>l>G`bP}+8!;0a*GI37q;+^q7Xx&>-Xeh41 zbN>KSG9s1XbtL6c(L<;|Mms-f?$|PRf>xhsnurI_$N+yfRiqpUh8drac0L)aCIwep;SK- zRFgsvVaJ9-0>a~{^(S`wa^bTE144KZRQC1P)qex+&8p*71qTlbcJLhOxME6SXD?$PHf%{idy)$~h z+Ww(ifwDOjm^Lm;j6UiiEr^#gfP7KI#L>!Trt|h!Jaf+f0PUUL3C36nbY6}jcMoHK zFgTs$*4p0&vgCNvUs9qUm_?xR-#1&|v+Zb4{{S`HS(^@TmH}==XAF0$hV+A90j@U= z#N`dHQ{S^vsIvY=4WV8lm?%q$_G(pe zvD)Ke531yBp6|SPEi@lOvOKwWpMCUa>8|s1f6t#D`G1P*qyR`OlR=kY2*zc$Pe$sw z^nkZs;az#V8p+3qKd7pSw%;h+J8{x~v$#NC(1~rU^IPLkry7j-_d6ijLx=!y@m%Ev`&)=~C4#lB zR8~|Jd?PO3W-QVw8d}>ox!ZpE3j1o7M;^=M;TM^_k z;CV|}t#A>0) z9VA!WaJB9#-O(R(aPU#MJ<)$1%Q|FmsT|dd^^arSvv%JA-K|@)PTpJg2mWns+~nI6 zB}s9W_>{w~`Gi!^8sj(Z;f2!88=KWfE|Bm1cLFO}TH?~?YppB6a~qK$-&)#xR`YrW zTX&akKPi3lOoYsGGu}hQx}_~jS`;X)M!4N@w-FbXH%cyD%2-UsweQqBny$AOi15~f zp*XQfDv`BdP50HRnoVhf7X>32L<15WZErTpmeNm+&<>IG;jyPA_d&%Hb;UN4pr59q zm?3xx)GRen?(Ex)IPcPDJ#4#N8`0-RX#RemN^rT!EED$-hfm&CW9S%L=9-|JvmN`+OBUWixu}gU zC6?9uy@HrU)6quW?Fa`dmD203BjE-S7tpsXV@f0X@vQzdA6=H0!mVmQlqD) zDz;ZGeYEXvrl;&#F5$(#4CG{r1{VEBszderyCJ*kV|ll1d^p<{`x;iU*x|J_;Ro6R zg>e(w%V{IUQTiUIeX8Hx3n*^#{$n*AAy5m3=eSfU$wf`}BpO#56qEr$T5Z^X;F{Zl z?4=0mG}q;%sdcSM?d`%YX;zFbIjpS{MkAogZF>AcNh$j9pjIoW&z7Y2$x)&l&)tQV zdpOpqMl(ZQ6y<-50s>N0nc?N_ zmzv&|ngua$g!y;*jump1Ve)f3(((S#xlURwc3_mTwX6$pvk9P+L^Ph7TSy)vI^q^f zQEtyJAEc*nSib6GEc*BkK{9z0k*u%2?DqFmJJjdE5pCA`Dm+$GK$N7T!Ua1kfw#9I zx^Y$mjPnn@z%HhKS)Qf1cGbjO`>?N9R(AgYB}R)b{P)c`hLd%(MYs7HW4NL!wil=^ zv6u@4b;mb%XKmljl8RavM_OsE3hZ9X?tE3A5^H*aEi0~-9*@`IrFe?-8W#E8oX36t z0B4q2i+(pC++ivWge%HoO~$2|%XK5EY%ZFQ!Yhq@*hOP)=eT>!ZxzjQWM|3Ui%{?3 zz18$_uB)LQs{Lk7^oPbfBhjCyoBCky&7BSlyqCYCrKPt5Lxbkriy12mNp;mKt11Ud z2pvvYX3E$fjNQ8a@Oy-s4noP?v8_+*Q53Bmil8#fATh2_+T1Ly0nrT>Jl^c?7+da6b>pD@igwz6npdgo zM;~v!hjzHY-giCt)5sh4dnLP+1wPM^uPwNNwpNgzfh8kJ&pw!lld?FeE}1ALp@)Fr zQqz86v(aNDwS6Dv4~YKCe#Orx;XIg8(vhej08dgt?>OBfU|;|)T6FrNDQ#w{P@qp# zoC+y$N>SS>b-E2YNd34F+JuRki?M!8kCbzDst(J2YqZS%&tfn^p2XJO6oq*cgGvIF$IzW>8f<}DOZ)fd))UWz{&4dej1s(~BIokC1VqabkxC534gvV*xwHj4gDVtoscldfJ@vOO~HcJ3aVs9zH?E)l1Kqh1?lM)1S)Q)dt+*?diVu<6Lbsd0+V@i+pQ)kso2TMQR(5 zIK5tZ0HH;|O*&J%7-x9)eAoV4+WH0+(|?yPaCd4u zY`9%_713?j0-7$y^CmA#N}_-&q0Ef2sO_j51%zy^Xwb4v*LU+7IbL+Dil%$U$-Cm- zT~*_4%z#`84Tl*}PJz&ZnuGP?emMh*h-7^nO2iH+=1t3La_zSGr*dvi^u#$IH_^lsMQ3MS&?8mB{2RFq85`j})HS2=aIu0X(ne1^V zF3}oy%dJpC6QWo`i(qv4fTy#sTsUORBSWf&xou5pqEK^51w>E}Zdf~AREwKb8e9la zQ)|&)xIIw|5?#teYH@m<)UN*Ts|}FEl920t6snx6dSIj{$jfr1M_X!QTdfvJQ$Y?b z2}+7eDiV?fXlYzh@|y!0doP(ZZF9UxSA0iu^42iYHIAyVGc9u-v`Wrz%kA!b5j`v+E&N3dCA~wu>826`^#1_J z5oGP_g@gGnE$Uo!>S6HN)Ik}8k?3)c?&~oepodbcr#oqsZ1$yvm76<4zstW0iopreUY{lz3KzaX##x45_Txt7w~S5#Y?Eu|@j zBcgzW>8DQ0cVpJr@+7HEb2-L39*`Ayoda#$S$;L#s%xc0w)1Q9{;`lb;?o=6-9cIW z_O|nALSgdnq+R1HV8-8DQqqgOSx|owa^DwI&$kQ{RS=MBMKeguVEEnpXzBcy8aO4d zdsBS`0^%`N(z-5T+-sFIN^x?Xa zWSvNcoCiowJi>-cNT4M(2d)eoRFkD8DMPqJx52u_leZz>9=SE;MYzZ>hoF#a<}u|h zDb#y)f-tjM0!8r>QW158GUIEA99oS-)gYuFV8EwQq~YaKfD{4Hp3#7!aM_m4rM)0( zI=iq(HmLq(QTCxiFeOQmatzIBZK5rxG_@(4hBHs*rz5EX-X&MmSoMK00(CNf-$z zyd@|kz;RO9qQGbqttwS3Uw#mxcjtuNzG{x?Xm{7BKWM?&o!YvkG+jw6C#XFKb`Mo1 z)_|@T2JhK7jyc@CEp>el+uC)@=FCcf^JC0PG|6cyIp}ddEkDF7i047mFHq*InbtT* z=v51tY~P1&HZDOrTe&t0hjXoxl~$id8(fW5w`<(_meePLkoIJ@?t4*|!u3dmMM=z1 z3ijX_R8~R`mlU1NvC$`FS?45${Ih{HuWp=%8{Lm}NNssXn?)q-i-V5rt*v)#RdEo~ zfv$SvIZ;AA*oV1~suBu)c<(OcIC!r8@>Hg8llE5YV1+lkEH;V7)S7~~A|Wg9?HJE- z=9O=OS8dO19vb7!CghmG5lT~6C_FTc{yWZ7e0Q$+R6D*9CGp-(qdcuHvz72$ye}x$dTrxT5 zY?7A}dJ&Co!y+YRPK%ZICe;@57K2pDe%`bbR8vI>O$|mRg}|US+}A zTH)86qK%>Eia#GMPIO2<8ql05N^r|mV$T-kcAE~_VzfVUxlLF^IFZ{fxU~~Oko_kS zIUJ-AFuF%j1!~*7O7|f!js_PK=v^h^4@Ztbj|Xw`l!Uo1QbNbO60d*F7Z)(`H>L&v0Z&EF&NTYr1(;xGf3|vrYtcdn$fC?cO}_Znc?u*Vmm1f zLu4L}pyW@q_TpOJAl?cRS&Q9Om~q^7DMp<{;W?fFOItzI>IKq13b*=ju<<%=T*tMX zy>1=BDYspklJNONAwaf}8X|^*q~n`+YHUOEu=7p!p+b-Q@qztt_ zThGv<4_E$)TH;PPxbhDl@0RcTQ*94E`)HJ;nQ1{#OZo{GwI&O083>x%c764hf^!39o|N$Y8dGhjl2ER_T}n+wH;yrKtkqyjUs} z(t3DsS|;yx6M|ilyJNA_0`(gAt+YDo;+KBqOBEOS7o;bSm7Rx6q9q>(t7jY65DU0hQMmusBd_=^Cg=F}WbI`$l}Q>avHoFuUt zI3ugyFC~RUx!ak#LQtaONNo6Hnb3YyHD=e|V{A zwq=+<0QMyo;J7sfusk_*6#H=0=B^Jiq~P1OqxS&%7JfwbHj(~F7*Fv@xs&Zqe*m`R z(d=9NqQIpPj^m^p6Y&{*N>}h(f4V)1v2jbWQu324LW)faKm-%thn9D*G5~AEN96JG zvKLSe3yj+%f9^Xkt80zo#WBctX&1@RzI7l}wzZbl2qKgqQybP!*|Orj?Q~pmg2_o~ zxal61br)!-Tz6s{VqA-yhaGtUs3ersk^LUH=hwq-9z_D|8E!6%0X0Yl>$x;Kx1_)2 z(=t?k+%Fsup9K?hb94oDLfIF8xVYk$x0e`HEb2WvU@m;9ldMZ_C3d_MvD-_{q!c!_ zERTw+J#fL3j;$2)?H&g-O%NoruU?=}L0X*s_;vFEHEb^DRk*j60a67!J8|zdIS^CD zakKL+cmDvc4)enNyka{o}VbUPZBGt zlmo3PTx$ z5cz=ajTNVAbT)5!TDaHUix?$u%#5WcI)a+l=*EBY^DS&#+jnvqaC_TuZ;MV-vR=T%E; zVTK=BTPj!viB4LuUo{UvLq#q!iBefb9a3n4kpO#-ZawB8=n0=`v|X-O=iyl`&t0yn zLX@cOB)Z^Ev7HmypIj$%c^oXO%@THanQkQMj1p9*ZsGn(yQ$S3oioj73hH4^ry*R* z=rIW<>a$?tr|tF@N#=!wCk3C=e=%Q_m0LE$aaeTIT!+xuBzDwS^kO#WeA%~wQ;>F_ zB{9%ZNDX>36XYXfmM?8jvt^UBN2nRmW@0rrJ?MGO~>9LLR`hgn}dojdp6y3 zxktEvH%?|Nu0{(=TS}cm7Ccln(yEMDEH^N^w{w^&K&ktKW4|4H98wzfI1g!EoV5Br z^wAzPM;ha3^1kf~Ps~Y_`B729V`WRr{+A-}RBXEZxNS6<_LH-`CUxUdpJbVu~J z>DDj-z9X}%zgq=${{T5h5odm6N!8^`GqqSo{{W;tfjaG61;LP(6$^^&Ooa6xAj*&W zVBsk@W&5=+Do!5S;iDQvJ&TPWnbnhP?JiW~OD4;)#M)4tvdfWmjRl6|B15X0lp#wE z0a6kO1mmJRQE@ER<;@cI`!;Wmdtyu}q7cGTAz)fYR5(K@Xy zq7$EYsUdV4LXYdmxKo3`-m(hPAr0RDhU}Rc9V#l z)@lnlOPu659j$efxe>f;eKw!5n#LiFw}q`vBO{^C_tAFDh&iozR$BPUe} zl+NAb`lpOlp-r44>Q7>F%*cCn*Z%;QH%zin#kN5O5)=h~>3s=3-EjrD_D#i`JjUwP zqIU1RHaTiU^V0L;o1VCRP}+Se>~~;Wq%k{{_bhddrD9Ol8VedeapZyuTW+-g{6`_% zh5LK8oqaEu;qC&WJKNm&u`nBWNb_7YUpL%s$6TB0UzGkzIF-70F5{57n~w7ETe=dr zD&y}fAQC$UL$G6`F79Eni&_E5E*G)2xXE0Q+PTM4S8+DZ?Atz;HX3$*!euWi!bW0XN5ZcBYq9#K-2nfma-^35br;5Y~z(b;9{) z+t!<<{{V1Y+Ot_KtT`(2c70(DM~=!$*0mL7kQi-gVEN??V5uqYOEC?kCGRu=eB64v z^&xomLCIoGug{6c8(B_93+T{d4rLe+p=xwXa5PZMw@C2A=U;m%Mjd z@2(50?kMYLZ0PpJko=UT$xF@WPwsoG-HRJqs59LcqI=VJ4GXi{mr$yvRDg3n!G)&OfMrzqg9IKB&Jdy_>lwU3Yh; z-z#NLeQR~jGnV4brPZvJ6vBC0ml*dUT9Mc^<2KrT$$Jw-W_;VNB1oO!^M4Mw$6m)?7EKNCY6LBT=IDK4s8 zhX(%uV7)-OZJU}Hy4W9PWxnkZz_mt`9l-NzN{&@Zk_f_=5VmN`4+4oD>4rfd;?q_2 zPw0!+PZj#5+mQBeBkdb~lb&r6fV@Bv+YqkkVIV4GGC?3Ks@hQ{2OvnH$0l#i-``_o zGeq)YKcx!pKF)Vj6PAQp+{V#39^Xj)H1-p9X6>#xDp8Tgwz&yc!lJ+juU`*bW``+w z4v#BqpKW5^b&Mw5o;%`ciq!7GgbDrv;yb^ELYQ;-{p(N0Qx|MZJcn3#DuNJ=zp zSdD~qNn5Au@nCU7U6L!X}4qs zP!doHQ8}7d7YsJtlKR!tO7c2ZWxF?(wjU=QrMzI58Y!2N{WIQqG1xcwo1z`2LfA)} z4N;v5)_irU3Gb#W_>SQ+$;E}W0IQDZ?3~$VHfB16jtaca^eM_k<(PY-%KB7mo06L} z`dT=&$KD${P_(SFf6E3`@Ngj@O7uI;jMY)4S>}QKGnAVD0G`mqq&?clxO>3WLQnZ- z&9M8V6pbs>faot_M}48d;T#X5gVm|eZp#*xiL-2PoR14yM4o02$(lC1j&i)|NM zIu(AjF+1_LTa?3zWwPi}JV1bQ?ye#B-s-lRyMJxY`0@>ArVX{3SiyDs~qo^YG;_LYl`wnAxW)46vW-Dyw*{} zRZ_|I+RIiQ6uq{23emW%i`%=oK4XakV`m$bvB`EsO49qlT&m-!C3A>#2v`PyOq%H+e zd9o1LKP*-KqG?(r_m1O;Y(`7vWHHDn6}xibPErv(2U;rRlRwXP-*J%SDXb`kwz5)k zG@-69(FQraP1Ry@`OSBiCWIyEZ9dQi5`*q0luc`f_Y4DwQns#hML|xBM7_l^U2{!bKJ^kHCo&*JAQL%Vq;6R-BICur8;R&@rCi?M)1@m zvc}!xrQ@Z^pyih1E@N=*`;$~@L?sRar2TZRCjN1ZZ&RA8pM%XTfswyNVkFr1TipG( zb(Z2K+rmR~(eS7c6`2|5Tsbfaq7MU#YX$r5=X4G_wKne@lVY?>ZPi`H#g-I8+X2rC zf#^vCt`@f2lRcCIl~8#u;#=D~u61ah_Rm#&2H9*1nQWGq85&kvLKFz^sZhf^p4r{K z)dfmgeqiyi#YY5D{{UJXUc)890^$^&wPmlWZrtirAfNLsi(?CeN~@lwJx}p( zE$!Q7p^dpO@!L!OY%Al4Y$2pAIvh`hMEHOicZ_KpAGf!-%Z|RUbn;xu-wyKPw=KI^ z*y#52RB=yi8-!Qm&5m1nNydyYqHh%L6J ze8rH0ish!6Wn5MAn?I$c_CQB5=S6zL_a3hiWZY(e3IWE6oxZ1WEQ^~Phlb}~HniAJ zrQ|@kI9B@$LbbN!chG7I4|%AjM-zB!Tk`>q_y^TgHyoJA?ghpv?v3EoYIH?6Zz%F_ zDO{{~o=x44?)f`%BB0u3t~(7nh|6&70&WS6#-@TWD^J>vYZoNn8K&9?%Py0tv( zT|TwU&Go_ilZ)oRfG>?Jdp&DQsNvATMdC<)~Ln^A)$qbCfnL*2No@w<8}jBU(JF{aUd}>|Nkxtf2CF zTOa1f4Ch|J;m$Gom0aWA9_2nn+I#o;HV4mH?G43p=z`)JFFL0Vgg2=8lmba3yBaok zeOP0sXqV!51%vG_&1Q3UvOe0K4C7L>WAv@b_Z`O%Z(Jd@V39o1EAm%VDOz=Dt9q(w z?ZP+R(y@*nwt`g72lCCBxw#Qa=CZz(BHw)%Z|g?oPSLhsAm48ew#%<6l)T$4CE`ML zsX)|a)MB{@cA%01_cVa7bN*Iup}%Ylk)(nNDtYDJGUXeadmD%@x2TA)Y+F=OBY1fd zRS|N7K2p-9ASnIaC^D(9OlMnPa9Y6U-1MHOlR&rq{jst)q8kAq>5ZThRSDibButLO zGtVbl9c|Y2GNeYDMH4_t@RB~PTWL+kRZG;*dYg28B&trnpaaa{mAna2>O6Zk@2rx)B77Xnm%AXQKRrdZW6d!8t7 zU2yQ=8~~sm3k=G3@>#4|1aE0`O?5uMLGe}ASE24-1uAj{mgG)lOzx{HQ~r~n;^F?} z*t?5e>{Vv=izLnuXSe(j3vZ;ZW!u;27dU%X%Pn@NLxL0c`!u-E5Gkd^`KGDQFh&A> z-(@>GXyNoE;O*u{7xfauPfz$M1AB0{E?-!Lr}#%~Sz3q}^GLPct%+t)Qb7gA*CDVp z(<*o2m6lq~x-`3A6(@VQFP=J_8>grI6;ig^F8ix>+crkn;zn{L%#1Z`_%ODafHFHVdptzKbTidkQX)~1IMcKiDo*IuLgeJaT(X7?#=T*0rT z{>0cGgZYN{En;5C*-^qiDGHP3DGDFrvC|Jvak0FdR8)PK+`fkWC-x*+AD6!>IWXw6 zZ01wYx*l~Q0Cvzvz#iq;4r{}*o0-~mP z!jX42#I+{w!{?fHdS`NlpO@|2qqcaW?S4+`-Fto|uGz4o*O2T(e*t;1aY(adW(uxb^8y@T0ThDWC&HU`Ach#OsqwZgw zkR-`Wq_&eqxRtmRbQI8q5lUib6_B@{-LgPx!@&gDtBY%c9K)sKh*0FGoLaP#M5nh7 zPO4(wDhi~_^Wh&R*){v2sQse^dXdMrwN2QLkozm@LWO?39wjSyD;ZMcbu5vqAbT+J zW>)|w9lkkrNezmubQzLsg&#_&dET$DroJ4!-L~f<@-plRmu%X0w^?G~>Vi=oT&g52 zC0VG1)|Bat=XCBknRLr~(atLMvo@^L+RJuFxHJ_*K-g3KwM%)B+*UJNi*X3mr!z|7 z)9+0v6eL*D*H>r~yL-~pw;EEc_V${(HxRXR3To39(ZnXjzUv|? zs>1xV*EEiV9=Tv>6X|UlTR;?-wJtW7?o7{mX3GZO#i;mCTaCd&rVA?Krhn#ZCr4X{gQ)*}`r}u(XbML{fEMb+sE)@zI+6m)- zQ+_2JTyImZ_c`}VjFrr|T;6f{(%e#(>#n+@wXI54WRi2P9YYfs-j<30o=F+v?MX(V z6Gq83)zsjjM$YOrJMGD<$N<))Q2X%ps;Q9Rtj7Xy?Zch@_)b0dG_Oi+v$lkp%*1wVH0wH_00n=;=~(986?2IB zhkkKgx@+5=J3iteMY{PKU6veWx6$`a6?p#lO%v3NyRaKybT<1M%c%!PP^yI8*fWpI zNo2xaSZFE4=npSiw46ctZhDO0Qta0|*4>M+>=KmP-j#Nc1f(#m^?aimm8g!1Qj_h) zKR4cdWvC&fypIK@?LW)b;mNocGs|i5pS5ka87F-=xxC~=iw+a0 zmfMT~ttF79>IEjchFD><`>hU3sm>h%zA3kI`zLjRm9eyklBuTseeJN%;Wr4bT5hZ; ziA^|_A=cckbqb*r#=Bu4otw3x_plgAY7j z=y44^MRfKnqi^(G$Tz*Yws#e~epz3Qx$W_%Hx0y1W!T>`$aFU6foW2b6`4tt^L*eL2u3d!1>}pDQdI{mr!SDX{E5MDQT2Y+eENAF((4>f@V~ zJ6`(767d z2_gRgL%yaPVNxZ*VQ|_9Qhyq4$`0xkU?Jaj1pBCxcDpj)h!1J~i0m&%KD#aODNHs$G~*AxpS z&cvOoevxCjms-18(DYYYlC4^n7ImFfJ+;pg2Y2I|Y1Kr=9gCL@(mMMTF)z~3sk>;` zGlehuMM=>Fy-<-;{efN%H${Qq@ROTtWg{BKey`f2%l94f7T*$0?~iWKmjy^f_FcA1 ztaZ8YrNSFJ3cEE@xPI(~xd8PIR9tXfxR4rj@D)fAE%!!N+wN^wfNX>Ju%1df^g7Zo z;wi1&%p$rww(>s0r}_N#$(L&zyJF&oAXue@A=?}~Yf<7Urce-+skYHUKsza(8#3D3 zCv^j=EV&!$1qjpBqhFka`p3=_85)670%)oO&{qz=Wru-CdD4y{N_(RD>DX}_0Ej?$ zzftza*}lQ%vpt1M+!C&-e#rH`H4dS;p$YQW(uSu#-pp1k8Ibw%T;O5w=j~R@oSrP$ zfX0zOjZHl|R-aO>%Y6GiBBIYd}S5pKc5QMw0m(DAefy z>OaFw2bL2jUK*vS4XFzNAx5V>u(v`fxKhs8y2!Av=200ANeNJ7xRk6>Lp+sHhIb5O z_@R#R(VC4>$EHfT$W)n)ee0R2VIY2tD#*u9iHok@Ty#!7zQJklKTe|6Nb5@WVJR6= zL`Y^J)mcrQdtr#aT!@W4>a>>Dv;v@O*-&v#$U^Y_3cFZc#9BInli#;(`E9i|wtR`m z_`(l?XWh_ZI2n1A_2%1%3j2CbnwcNLy* zJdM1$Cpt4$l)-*oj#Al<`^$Y&2nj8uu8Tgzj6ZpJQ1L|^riWC|z1!{4U2&%3JLWg> zB}pTudDjxQw;+lr45f>sbwE}#hkZ(>&1tgnJ@zAP*(SOpel!%I>nzK6O7#qZ3Dig< zvlP9}wHnIY$MFTGY(C+>XEwrNR-HYm!MXncDE*r!dAWFz$&zO5OXH3_m`yT>(_;w( zRa;sDkU|t{nu-yT#bPe?T|*z1N4(Xn-2PasWqFi1(c1c|t7hx`nm5cD7bR?tcClYu zX-YPjS36|rZ#><6A=wdJd9bd+Ltu8(7JOfQZHNL^(E9%X!}Y2TNAj^LGaCUIX24arY^0Pr^4NET&9J4ZbA7nH6Q~Y99)Ixh3MkmyUdy<5O|IE_zyAO&+U2R=I_#tu6C9zH zEd+tsQ~>&MVUW0u6-uS0Gw0lIZnq}Y9`xY0C2LF3LK24E(xNpeQnWO!3Bt`fRXewr zRNPIa)e0fR06Nr>pVfrGQeJ36w`X}|{rf_lK~+Ca9CJmI7Q=(Yp;49#GlLQgzi4#|ja3XdLX#<=I*O`?>cFX(mVXZ_H4<4q z&oETrD3k?xHs{k?IMRve1Bp2D5@->>!Y78pl^H>`9}As^nPp535P5+VR5?g+C2cGE zIbgI(`H0+j&UvXSX-0$|hrb6+E+qE~k)$C-S{p%C5}Ma8quOx0O%*~(fLb{s)hE^0RC4S#x_Fj^@9-sW29> zmY)PDXW~7S!v-s+UNBo!{N%Xh{xZBHPm1317f#-wSM9*vx7?zS+s*=TjBxz5P2(n- z6;h$deIx=aMG${pB3Z7AS2GzBR4(=H+uL`4PW%zMonML#yYRVgR8GbX-sCY zw6^i=SFEGDtfPOSEkKZ>---UW2!t zi7wSDSIkTyMKa^4Xh}|-z{ree8C>J0x-gQq&A+qTWshypXBAxeJ7%ff!tV28DpPCS zGF_DvppOHD>QV_EpyOWIxwgv(lz4!-6T3SF@!t!!4|V3L#kmTewAfuPs3i=hfRcPv zre9VcVu+3I1I1Q%iyZhEYpoL*t-pKRS2iuy$Sa)thLi~gfJ3u{f{;?eNL>xSjqy|)`=->kw!J)v^mo)WskwiOj2E;wmar=TYbw;KdUs0tMq zHfHYRd1b`6!7;a_^zCPV3}$*I?x%SL0N*a+fJC5$3W-nO%xt#?kjI_ z8oQc@9}09RxaC5Ir5fZ^PGtK|EX^T-QCwX2q>H_}eY)ap^WnP&LkUK>uQsNfNK!+9 zsVV@CK!P&qhW7U?k+o^4O5IvX3%Xt?QtgG%%!Z#*b)kY3q;#kU4X^$ZpJBR0z?EaK z_a(`8)RrUsq)AC-)H1BdBm?&1i*FOCR>_i_7qku(POE<4n+o8jA+oQ;-2t0 zkTTPmI4rizE%*2%$C8K!^95GbjQ3RjH~A!*Vy zsZ=UGEA?SUI*F9>d#QbQj5OtN^Q38ZMSg=uih+aUVU>-1{7??R4(w)nvtPa z$B*|tz9*)hOtTxIiz{{A*m1ief~eq2XlSV_8D;>euVyJ3OF;aCU&e#jw0s=xiwa`# zE3f{MEJ}yL8%Zi`6uO1FlS*m}by@wGgJ zbLol~%`K(YK7@j}4h+ZupWUWC=O(7t(19=;e%ue5 z5k6d`8>kS{N$np+aQ@z$(>Yui6lHXl)|XV^s*_rK&LnNWngXrmr2Y%NMR9jzgj_vN z+|<=7oy#bIDV-!M9SM`u74$AH#J8(+8hz#8*sTKwB4kDL3rJ3qJ=o>x9sEMO@;GY{ zuYzyPTAowUqg!;eO%$S<*AHVpB|=<63E;JY#ReF9YJpJJrF5VJ2_iLsuR8%Tyek5<%pAKtm~?u@`0l9D)5ypKdl`Mp{TM zzKQ}AGy1Wnx=O4VMq9-oDQ>Amslu-Ca74I?Y?QbTt!GI4a8%>U3QpUuxW?MLxv4-c z=F*(cZg^*M+PEiCXw+p$ku8?kk#RBFLDZiyM5R99O)HO46aq?VT^h9sAxzy?>qr!I zgo@O4r7)~XX2C*aFrDj7EWfrvjC(U zZ$ztVl2}sw#ysIn$x$7?%p~%Xo+RM>L8Tz1GM4BLk_j~W@#i|Jo0~;o_zQ!t+|dT@ zHs2&cjc0T)x73BNTZJKJfjz+Dk-E2Jc21*(R->@{e?C?&rXC8{M)XIz3RCCZTMUe< z5TK(^VylWr`-=~P(??`3-TTSW2hj6tbqcv&-j0-yymhoCMxAoi(*VxmFuXORijaR} z+{PC@{1q7tjkmaOj6|d8w#tyqcT@?~mYq{T)7l0#J5pqbJH=s__N|6zFlqs!4qf6) zt5c2ym7L0{)9Avc5K}s&#olI=tT!N_AKXY3`$j$FH8rZ4eK1^CZRDxfLg7Qk0@_pH z<`Sn;PtletyWy3H1*CSycCzS_x@i-TnnO>wyhF+0h zHQVYlJ!z>J(Dso30JW3URu|m43-UJP{qVbcV`azfP+blLRi?nN_=6Y*NB;m){{UL- z{Ad1+o}iXc%ZLCe^eVkGrW+%rSWP#qYf?zhQLS)d!k)(9xDWZKI)^<#Z1xhcK3i>~ zsjYOC&2#kA9Z}jQ7TDCUoqw4bI zQ)SJ$baq46MiS|1PPGy;-}1teK^lrcG|rf$Z``cbnD;(EiD|v4+xGc9vtgKfoLA6| zi(_$r5G7}1?MrUWj9Sq9(VA_yTu2r1xUUkk>r;+eH}|*4-zCJZ@wUKXu>!HRtpIug zue_DcTv2U&s`#UOn8kisxWqV;p-?p_5Gh``wPSXT)9J`8{{V3J7atpsGOBLJhwN%_WH zWy%3W7ZaoRy7k6QZ+7Tz1fC0{@;JEe?93yH3zJ;u!92gfj7sDOu1y`G1@_#vZrGC2 z!^}uqm1Q1LAz|9|(_C~u*xXi`26;~$Lg9|#Y&?$No6n8(*Gg!se!=7JYW3gEt;e|T zhxZQQlEXjDyXNGurmeQPi47%6OL_sI$p}F4DErPH<*w~8)~Cy7NUnf*s{Y@y$Zi|( z*q(Y3!!fWABXC}g7Sc(H4OJQiE2!7M6QSBvr~w~Y=x+raayI>b z?D4eQSEfHj6xT8hF&`_{?*)bpql%A|<2FcYTZ#r_qNO5)roXQVnE_V_?F)?)0&K@6 zxZ}%KZXXCakTD-AQ?}6v!hCTz6z?8p-*!$u*)WXD)#ZLCNIpzR=%<+b%Kg+JeP)1W zaKirbJ9x{SQkK^8TuLKmq_3!tPy7e8Jv#B!J(acD^X5B8_bvHoN@QFnsUR)a2<#mY zo{3IF7$$o>`^k+?i+S67d6tlkHB&n*IU&Th6Xt8lBc)Ao6&rP+R(YnQYIp_356lzP zqubMPJHb4EEU#?sm&$kT$%WKhZjC5XDpZp~)N(vywPX4?+;%?M@+0$K@g578`;EMQ zYHulr+-jq{S8X?J36mg$DPzX5q=breAFSf|0?A~Uxnp$hAsv-LhQGdEPAMXRJ(SD0 z0#HQ@oN82D8z|KwelmU<_MA9lYM!~((M&uY!dv5>_x-D6YBI*57XQAuc&?);k~Enua0dyBE;Za zfCIPt!E7bVg!P~Y9&(>jLFk$ca*p8K5@c>m-NARp(AqHJESnoR(^8~ZGFyF1mK{N1 zvP#ya01yXKN~u5xZX(-m=R7sMN}9R7WO0{2fL}Y`n6IrVJo~w0@jlIx)4WN?65RQF zhnAH+c)GL{%aTKi1f_=Rq_h)FS*X+zPLbFpJ9ol8A$Iy4l%gX|gPMAE z$12{i`K~^d^h+A*TesApsKe%jY9iz?eCETUQ^w&E1pTnh5S$mELh#yxmvv&gO@Z0arX{Zt)jZtda2!qBTaB z>Dz?g6*~$oM_^mP{fokX-WeafQIBY_gxAXpLFuoARs}XwX{hKVNgwd=ulJrq@1)0P za9`72{{X8O3OGVjwUokpG)s~C)NrGB-bK0-RA2T_2IF3eD5IVDH;%Rs7j7Nhw_!<% zbGG>=F;u4s&PR33XT?sH8UMjn_le;X&KYtn6miE)`4;b*a;{#Wh-oe zew3U{MYuza2vqychFZc}R-9+V)}7BL^)GncB(yEE3ohGPSwUKgLiH6N-8IUX4F2bm z-JnD`sQs(k$gW)HOyi=FSikMQDBO_t{7b^7SZ=8WEj+DG#8Fiaqd`=fSHBS-hfR=! z%p!$YVa!|NuDphybf@m`*j6p&6kTGWwar>Wo0guACYe;H^x>vIFEyd)e+4(1+cwwi z^H>E`9~Wucqsde`65O>sj|lTn2(4+6?fP+TIafWk0;@i^!7@2IU!rov+Y5mEa;^J~ zv^>}LZ-7!k>Dh?ejE|To=Bc}UJ0W#g^r-&SiFSQ*E>5!awSX5S1ffSNXHMJ}9IN8^ zLUBF@8;^+Ug}3qLg5N2ZrcGEvPN0f`QVn}BTdc&=3Q>C*wjhE1RaLuuZW!^o8*svk z(q8d2lsHF-YG?{$SHatPmMtZ7e&Ncsxp^4XRrf8;5ucq%%6&;&fk0A(f^*8a(rlxv z0>C#->l-Vys*G+Mv58ift<2b09oG+vP;*jEJ%kKQL2B-;N}^w7Sm=5~Xql$_0v_GC z+t;<-lR8l+@c^|bBqyMy4i{_=64W_F&y{JkEfJcg0lM4LLY23a6Od9=PGk&O+QUvO zK6S%M_)%D!irXIG3byE!!VlgiqbR0ZZr`k;9_IbEWa`{zo1o8vYyF2SQdD*uw0omu; zxfa>KU$b1GwYY9JhnieMBKXt>o}_*DhUWjNnGqBmjQVA~7KM8is-pyc4$+O(pbt`z zwBGqiWzts{a@7LYEbsX;rAT{yT$gf`l(#ZqnCN-LBxy=ipg|x3mLr{tu_A%3qO@9P z^CjIy&*~^hyZ3!KRM_1Yc}=-ewnV9JA$m=76(G~whSzXo=p?O0|2_vG1OlO;x-UhV3H-J|4urjxjIk8+s z;@=t-cJ4eiIq1o|ajl6LR+a(zNv|Oi8zc90uRuRuGf4L<7*7z$nYO*-8>g9 zeQ{gfv)uNbuXEc`ZQan2@Aoo_QZt0O6p$)3)Z=OG&PE47TnMM7a)*2Imr>m{g{HLR zxvRJ>^4^TO4YIc5s1GsF!qS7(4-ic;sY88g;sJAgtDWb=WCMDGUr$j{qv7b8{Ms2n z2h2L@U$kO+5o+!fP!IXItJP4DoRrg)Shyul^aU=^{{W(EhfTMH=m1Qmy_%eUv_Wf> zq7<2KX-;5=7JuPkNUfo-0Gm4Me-fwfN0;}l9;i4*;?eV-hq&Mt^=4FDS!j;@!pOxd zxp7A(CPw-e(bzku#kiqy*I(3Eas6uDH?8ZIwpE%lQes$a$zk>!8j~F_J1HR5RREEv z9PzSkVP%uu&2~q0vNtm3B5O2YBlRmKe!t2MzEm|h`1|Jo>PW>V-L;eHTE0V1`xg9fpCYq{S%6pdexp(&V+`EeEi~ixgT%^gp-kQmo zGM2h2#=xiuG@?N0DT|&f9}TU$V~A0qQ)a%B@?$iHu5yKBOnXv+aaxZ)aHs3dYWN36s4;`aiTG5v)t}(Ar|KM`@Yp6z|xR#BmsZqit4<9Ly&NQ~Om zMK4Gl5)EnFh}h zE&-qu=a3Ff<&ali%wu?%WV*Z)O2QmD_%4gx)5DF&3wWLjPavu_`h4SPRLUvKV0f^J zME%)&PNIR>NEw=ANVJ4?EM?NXv(}(3+q+=xt8=#*%L|txq686O{Bd`oId)C2)#WFU~1jTw%TW9eprjeRI-6Hn9 zs`d9^k9}3H-qBnDzxqHXy)rR<(qc$ z;|p^Q?Bn?@#!=imgK=>Uli670I;#%Qf8n;1WMh9!@rpO; zSlb(hRBm6}9Iv~AulFbyxVL-Tt4d3fl+(e*gIub6I|f*o$>CaYZnTHp9W)+=mF+$5 zVcZeqE{9zmQ|VXhmwjjMz2AOo{hLaEYS`Lr2AtB5nSIhKN`(jjb_`^jd$q7zMgIVD zLy28Oy}vP6`3b-50gZrZMP4}y_v!<4SuLE0c)8yd2?%jEo!%S`wi1)&x)58XGZZ-v zn7nP+KNLQp#2jrUOVpENw-HXy?&-%PZc+aLeE$Gi?d_c>{@l+}bli$0TBNwriC%zI zGaa~!g-%~edMdZsF1*A$iWL#Z1Ar8`6I6;HZWFtXgnHAgJBS#}LbD%_Yqp1!@K~tf03RjO*}Njw)-W<+V9)h^3ms)iEO7{de;oV&YIq$oFdERaE-jBAfZ8n4-G4(LCfC6(Q1K0t> z2w!R{(Ctc?yNdjP;@tw8tQ@(s?Y66Rl6LihZUD;3izp;?aOFkG!@ZO-VWRn zuN;T!aoIOz-EEtnazmDf9a@%TY=0V~>CIHw}*8(lP$ve_LSl;=4SiFT7L<(=D?Y) zZQX}*-m>A&*%sMW;{bijmiX&Wm|i|Q6rAXBVUqe*+5#MEsy6t$Lws6VIzlw(4)FAa zsh4=-%=2XUnp6e?evzjTAB|mo;uWSm1%W!SDwoV0bH$r$kZmtoHXXX#YGDn2CuL!P zmLBodMM>#ZCoHj`Vc&6aY4IAYcXvl+XMQNb)8MB*IOiWFS&DYzyPLPl*|(@lXniW*wQg`(2NwAvqtFzz*A)6aX+#bUVlfAyWx{LQ`@)#5 z{wNz<9PdSTXzek{{ZHd2gHJ5o~aZa{VRw&{mAF=3e~r^GcC1u6#1QG zw_J_ZT#4*~I4D%bRX_rOjIhEi($Lcr-*EiTw>2g;?h}jQ(!Qx$WT4q z;}di4XTAc*b5#pI-$e!6WogtE*h{_k>;BBP{Iy6+{%mNAanN*{DY4vf&(~Ik*ACN~ z?RFg9*kpX)cu&Cs-8Dp3Dnuqi|kzZCfQWB zEqsBr3g>J~UMnS8TA zeShGd5&aWy*>lwb4w-!=Pg#Q3{B}Sr|3?cpJY&e3SMYHakz=csK z+HMxBg!@#+l8F}g-iqN&LC;?EPq*c><&6kI_qJVGJzOD8gL{hU%H zs}<>ojpuLe{{Xq5L9sQsRb|?{g5xqHuLgXkqr?dd1d*ufZ~&3kxQxo=BY*<#W}eiO zF|VE4a@Xq$kf8BvPr*sjgTT zFrazS7!jUKr&5;;kR3i#l*p#J8Xn4H-3gSEdxbeya&`kwNKL612f`GTrC{VppjKU@ z3LXj%${k8AlO|9?*0rfCCo!nxYlO*G8+&%s#UrX@pEFmUtwW)yt^%pw%d^1%xsr0y z`hgX$WOdITs#nWrAhvE~y+8An{{Wnbz2e*lU=@#3H;?Y*}5gON=TBEF=N z?9_1*8%Hex(l%>;q2Q+d&^~iv?$lSz0g&cY>YVV5t*9y&7J6<%XK);ydt-3GJsNm8 z*&o8B(Dn9=Lq(&Nk24ibS8TmJ-V?d~C*18CgVJ_w$hy;Fg-L1UsGgbA#fnYqe7uEx zRgyOO*rGYLGa+52+hvm(<+rV02DMVY?wGX60~|Okfwyu-?klzao3}3gy!un!rKxf5 z7ONqV8XRd(H?6d|-<+_3yvVAm2i1;w+%^I{$N+I&o3Zlq+-+x6f?A{QCc12m=W=6V za&@`+wyjGx=He)}`*Kt$G*B9Xdoeqhx4W}%odc=js#|8_!z|-zcG~zS2JPXOAGot; zv^2IMTU=V3lPZk{W7Hi>Qm8T*n#Jyy2)d50DvP-LSi^cFBc>*veh9B3Z@sa#Hi9FekV)%}J@)~PE0CeG`MZJD%%}eVI_-;2+u7S9(_*rQi9eE~J-rP2Gx-FlNLsYH49RC2MnM;}$w&-dSuFOm1ETu3XDErEj zfXmt&(epaT%}284OPMLx7PZZ|%wJj+EpDvPBvYyG0~Wt_7VD)Jd;D9eXx&^>KJ9&* zwV#&>sGs}5d=JlXs46fcG@%Eg^UR)@k+rlFx2uw2d6j*!F;b4^qNOt`TZ>z5IU&~x zDo_ehCbcBuZH;VVil@@)4O4?`+F{AuHYoSWiniL!*rwwx!pqtuEun(%^KwxSADj?|tq*=QdqtL!;>$j~!GCZ6u|1NmWN@(TxV- zwb5nPV1lMAG)?Qctyi7R5)-!@9?(Mxy1m+l)^c3K2G+^LV+oKKeIDah0+T{==wvp{Hi=av{jl(x7%EY(R|?c3{S zi6lKWP}ef<7pzukR{7huM&!Ro@9D`vguhE3AE+UKKG*oxo-GfGZ$IzUU_^Wf-Tu^^+Tu_9H`Hn%7-4Z)US_LrO zxw}{B96hO5*<>kKM&a}y*tOfX=z`|jC$Ohjj-X^UniWG)L)tL@H?jW!38+x>wZlhg zMEJSeY-&@kTOz6~Q$o`&5VZr)=k3CQGP)epmjd%^Ylz})hV3au#NCXgT{U>rf<7HS z%vR@(-g$j=3c+nCb*p0oTK={4GwI?YE&MUZj3qiz_+)YiZ;3j;UN~pC7d?4L`q$bn z%-H!Gb{78tlqqd%Z9s&!YAal^PO8*{N+_n8S+C0#*S7=nDFbfRqT(k(QX`Swm+i;C zX$Q&r5Dr`QAf+u7lh-fsVMg50qQ4zbnltu0)7Ik5ogreH2w}V>Jrkl@ z)AZw@vp3Bcv0nwn`$G`tZU;P6`^W8F%YOQZ%{As$fgm7(nII0qj&Csg_3>TB^Hg9S zgoi6|wNSQ_qN@0f4RHD^dZ>ACh~N=;_vcZ}O9V>N#z{+}()Nt}t45hN%aj9k@<=RQm>SbN#fN9jBC{1hj zW1c&r5R#)qTU)xB|X5@DGxPNskEBc+;Jge{Pz0P zR$zbHy!samcJ~?bFDP8MOzn-ep1#9}a+KZ1Vphv-wCbKyfdx*fIgo3XI!@zqTG>R# z$Y^M|ux%^+l+rpD)CUy&W701^&(c4W+KXS_e&Z3a2eyhA3N_(-D zekUs4{{Xh_TjG#!GPaGCNJA{DLewguPMP9bD|zJ#Gb25nw*gX+UgR4@bOf_#@c!w` z4jYM+nNd=1SgG!{QqNQyfjftBaPzIjFJ{ptz=Nu1dNwNO8^3bNX% zteF;$1$s690@R?1K&)lqHT@VYmc(&N+hhZVi-CWbwZrGVNQ5axUnOI=p#@#eJo3-z z$5eL85ZeJ&^V7d~1^K5BN9#YaMqE$nuYGcc+kbl7_b%AldtNM+L~>>N{{SA{A=g`M zEk&sM99ol@6*SMKMkn)IskZY7gGoNG)~FaglIzHP()P8!ldrO$tw_F;q&2jCP3-ji z^EU~x^RczzS}R9tfVIvIYf7up&KvIczb*{e z*}SR~aaQ42ZAwU3=n`l~GkgV{aI>g|y6+66*RuI-brT}b!8hy8wB9GqX}B$cQnIBj zBDAiN_4UMo-7IP8Xi)auxslE5kfAmn(u=n_*J{Ox@?}QyT^iB|)TK6S)b?TKTKUsi z5>rjLhi$fGFdD9ePPkaMRhs9x_btJjJ)X|ddgF7Dq@fM8fvUOnJ-}vhl3Uozb1-8C zYF9<&GPd~JV=SZq0^!f8UV7aAt9b?urpBk>@wVP^O>FA{Bp2LK^32DQGA_B%BllC6 zQgP9Ha{(SU-mdtU1@GVErhVo716 z1tcv+%{c%7=zDuG=KCVsk z%;K~gYg1ZLG%gdv?k&fc)c8Ab8`h_-;0zT?dp~DxP5HQOJ9}-~oVs4Gk0FaywVwm;ms*p?TBeN>vk|GNABBnmIeMcS; z|+eja9p`8<>Zo5mFn!&7PPfbG~|*A zsO-YU8Wx(U>@>M1p+2sbGB7%3w$eQi-WcY3PIuk}=AglW|;RXC=o)-lgz@6Oep+qG3rY};PA-AA z$8B<;7~EU{SZQcr)l9Y1yi7`BDZC#Yv^4;WqX~Xzz;CxS`)x+uZ74$1A+_s!cig2w-1SLQk z9CK7;5W9uVAR+=h^yHFAJ=OhqPF5t^>Z8qk;zos~RHZ3QCL;-9Azbz55X+vic&6n0`q$7)X}^RAp?J(_ zFG)Y)!&)VL)wiCVU1*$oi{4@|hyMVjRF0T4TU4`NLgz=XH&|TWEliR3YWeeDY4VTi z#>v~QEuOi9z5qV%D)_6hw9}_)ZE%r5n|wf&`vVE4Gd#RU<{j%>zTYS0qX)q_Ldt7F z*QvuADQ_^WrDRr^N>G%JL~WNfWPk2qBFd%XeHDK7g)OMu1GvME6e>)Z476)XsZvsD zpRp$w{@TY}fb=Ybzc(ScLJu^T=*}c|xK-(IikUY#G*vS?5ZJC*L%A|_WOPnD8)(?@ zfybJ#^HmV7ZMLyq)YqmeJq2ohdmLyIOMs%F91+HaHgl?hL&Q_X^kGhfkD5^%t<4sg zGOkNbq`+y{RF1+ML0KPWAL4C%`P$DCqBkZ`S=h-j{m@G0XA*koBYocv<%--pW%^>i zSO*T45nTwbS@=My$3tejUdLL3--rdnTQ~C=BXSOGx-rFD*Wb5auk&xW23ojU?`TS) zJ**!n(ocl=jT%rvKV15i`XWO5v>k2Q9g(oF5T>ZsbPRHh2O{{Xv) zoVFL{Sx`OYXxjVL))Mr)R+VBNw%Iq7R^*|?z2+vd=Aep-j{gAFiVJQhw0BA1wm*}# z$w%fejZ{N@+PB+Gg*JyohHLk36=^w&^u@ly`YDA<%=1{;?cYFjK|-}&*F`BLEe5|4 zHT!V#a}Y^e329PzCXX<4&kg!@+h5K+>&rWTiZ<&g)aB!F-6Go>P#^JPO46mCz!DA% z1};m7d!t0sZPY`&xPg1lfB{`s3puCrxcs@c{{SgY+!j7>naC~3LqWFo?$^HDV5p&; z8idfo8C7UHD;o|e@Iz=Ke-N;2mv8q4-cC!9#;_d-bAwN?kW@vqzd66l^LgGRLc8)6 z_hvIK!Xw-+dt&M%615Xo7ZDG6Y0s*LCav?9%+PXFOwVUCt|i1Z)$njXQs^%pO4+xC`ZDjM^dydy;86h?8K9^QM7d|=JR4?Ae6;33tSB- zLY?e9H9Fa1y34xAR29D(N0SjOHtwn*k(!QJs@?9E8;~2qt53uB%cmCi$Tg)^RofA7 zw=1(OL9nLjE)z~Tw$_bl32aoUNgZj84=Z&XQG(}CIdi7jvACY)!I6g+*F@a8KXCkV zZtI`47O@pnxWkOrmd1imQ>Q&AzYgPcW)M2`Dk<bgc&BpbXMZ0Rb zwGzx)NkY>^%280#>S~nsRASY%ExH(J?yXfLxOc*N8s^94g$X*gil{*~_W_Hs3nYAq zznIlP^8!HX39b;SMMj!iSsi09-~Rx+gFUoE6p3mpj4G%vg#sx`jO&KC4dkR^lvQ*F z!?15Qe@455b4ZB%TUGk!5?!g)G`}I{8hkUY3+Q}lIb#BLZV7iEl-d%}s_dz@g!cOx zmkuYT=A+c9YG$bqYHi9WQ#$t=<5aqYn8@@lVX?g|xoPkt?OjCm(R+(KVDJ^`cIIb^ z_N9pOTuz`8sFBuGJ>+8$?H##}`r(!5`qx|jW-ExBiu%38}YOYiyM$Ug0WtXt=8Sw>N{rJw=J=Y#ka`0KJBgcknDB3#1|IXQWgkM zN~oX+>M@)5a^g#DGn~UiqUiqs%GNFm{nEzF-l0SKmvOC|GSc+6($?DB%s3C+7c7hv zqMb8rqL|~RXD*)I9`9=U!u^$H8LyPU2ccI^X!SimhObX-{jXu#n}+1lNt*KzmfLal zFC-`>#RV_CR~PNOv?RZe&6V#TO2fCWa_zA)m~EqtS-ht&n@3v#jJ)mHKD;lJM-2@Hu3_a&YHVbx?Q=l z+-Xc%V7l^~Cm^LtL)i*tOj$AAw6TJrr0eNf{(ro=OPV>&Y5Ef>ezp1Wn;d^gAdcJm$|@tXS6C_A;3b3c^YdX%Ha-mv)arY)P7_^R1; zan&S0x*^)&9VJGEPDWcqDxJCr_hK6{+q;%Dn|WM)6<5u#-W+s~5#GSvdC_0X4-I;8 zAlYb6adAPR%a>t8qClJ5n6qc}0-kbv&xv(-cr zky$A{KAd%a_zja4C#i5JX$8*98J*t5Gyno(Rle-9>xzw-V*xbc zR;81b?{IBtph@<)w`4Mx4eThO>6Gm#n)Y^JC%KH80jditNk*mGB2t)?r{pN!k~$Tn z4!-K)Sjg~6O#vpPq-jyxQWHzt;YsXoNCfuRP{ZimQK(G|0Oq)%>p{TZsJX%CK5jZK zc7owh)Krj#jOo{=I+M4w{4um&Hv8ihGjBZq0K}?=xoz&IWVN>$aocsmT41)eib3cK zQ~@7I#mJBX&a5P+hmvUi2{B@-YK2`h&j?7S8Xrx!TY2E-Vu zc2cJ@1CA+1GSX-PKEZ|FNJzx$ofNsZ?;C4w?RJ`NQl8zC9V!9323ch%!ae=?fVqM4 zKBt1TV{Qyh(~4y74c~CM&iU=bb6VEgLK;r3NrTHv$tc8f{~g#ijsPny4t)q`D5E41K@wQOXSWQp&62ZChm-%mW}>Q8mZv2k9= zpU4-ejfG8doejl=$jBGuNn33+Drgc_Q`inEo0om!@nW|U0pL7V(c8Y+=JQfg(h}-% zJOK6o0EJJXSAI|8jzi)-;kdRpCvAuJuHf2_rakh~>e_KFxTJ?t1F_V!EGa4q1u8in z{II?wJ8O`DO9`tT!C!J4UgsG(hT*+3H$oH-c9QhjUSiDrAWNfeEaV zC%)X-cMJ4;h1FLm_G?@LMrTM%iV`2l2AfNH<@?I$Fzu?FmfVnANEq%Ytf>-ht{4|K z63UWj+=Q!A6-??6XgD%)MoiA86gw*gvZO316O}UqTKZL5~)Foek zNVE3*eY?(RzucDr_1bMh$Wi8fvGz_C%k=Zen|40W+?!?(7{uON{YqS`b;b0D zWPGqlO&LK#jas230xAu0l5bozhV8PxQrP?g>TcEU7!EP^?2I~J5}LQD+?TqxcGX*V zg+kaf?owbww>c>>;l-3(Q?8dAT85MbR;2cd3`ybSvVv!Hwz71CT56!TOc@ESz>(lG zygU~;JxaA6*}6!0ggsjRT#y}%9XyCbd!mT#vE&7x~TL_Fd5&|1fgeI*IgaDX8XTJ`E*^R4n$^QUn z5$8qBn+h;8R}|sZ`hv8`w!=#F)k*@D8Fu1@=B<0kYD%*pM!jw=83FRyb^icXaUpto zqH2Q0&`kdTT6Ly>-2|z$l^lQoVkKE3aORs)X!hllklI^tMMj!Zq-#C6l$-wmOci<& z{{VJVy4zuJhorl5N|usU^7;uhuklw9j(t^%-qwSPr~Heut{c~UZZi$kHr<%8)1G4V ziY)}?jYkuZZMCy&&0zb32QB^k`hRswo{hFb_UA2LW8PyWPDr&ri0eKi)VQA#N%jh2 zsofl_oo3fC(P({y+s%t6_RKhi(n-8;1<5+hW}L4;5|R(UEMc)`XWFd4YI<+t zaM>lnRTjcqNj0eU;~K?dSm!z2T-R7;v(Anf$4NRUN>?UYDx#}H;Z^ZbC$G{5Ck7kA z%AqFaA8`8;TKT7IML#bgr=qM@sZuk~1NbBPIW#-g9;z15`j*M&jyPdD=U$N+0D`IN$Jy}2TaTHKqV zi;odDAzzDbEVLjp9UvbqV@^hu6vPyE4Kon0=RMJE+7zaF8Z4_dzATyOlQo7KL&_=@ zs7MF5*@&qy^m(TOtdVYJJ|GVoCN0-_#bP^$%>*wm2?s1pW3d1}sAxk70CJgM31;GXeqsIwm)dSMs_8&#TL>_Eu{)1 zsa_uJI(6(|RH%!43y}H-mCpYFQ+opUuz4Z%UpB+rRXeSbb+ zTV~-qW^QP}KD9K3H0(5~X$nbA6iNj#50|iPp|#{wylz9waU+QztSThhG@%IxB!tu- zb|p%jv?Rv2UXm+Xt1Pm?hl#9_2>HLtZt zjU@poaY_Ivgr_8sPm^YuT>TU&+r_1LU#Jrw+pO``xm8ESdDoGv06I?Rb`_kVzY-WfM0i_C4 zee4^Jy?ib12ri{+IDWc*pG7B~<}A2fH43RK>@@b`;8a8)0GqEl86OjfDm15v0&rS{ z=t>r#G_pz-6ncOU(cgn~qWP^WX1td{2v)TtHJ}h^K8zF~e62=Fsc-JA%@!*A?EbspEay37KxT%x@uSX$~y_ zg)WNtNC&$Ve5I^^Ivhn-tF>~Hj8odvMD^nh#X8TttT%K!<;;O4rxj}X#8 zB9+d#83jT`hKEGAwvxlQcyZ_45X%p|+FX|B!rD}@3XLh#4DvF%I0Q77QKH+szVnKTZm5!M)BQSN*3wbmMz9(T6ZIje=$UkP_|_* zHb^?un&sI{L0;jl?qqDTHm)2M+Yy|-vx(6{{+2t4NSCL%SAM&1vDsX??(3RRS+!ZC zscyNZx{{P4p&-_|oFygBHyFbsz-Z>BG1!dea#0LK$nHae9Y2@1uKoDS&c@)0cE+Dc zq}#R5+YKrT%zv(NU+CjF%6?+@<+-}KtuGgfn)ubJ6w_XvaSPjy@nrJ(hucd5WJ80sQk~4~W;+dqW!y9M$Z1yT>h=0~ zs#fz#Zov@*f>!#~U!s`mczG^RFa9A(=xBi2K=B&a3x`n;RZ;fQQ9vn9s_BO*GE$o< zO(>*{`gFl0O^R5zp+RY(ps!y15F{rR3I71MkVVCX4-rWo+Se}JD%DrZ`>~bP4@ynj zD0WCU-L{RY)s4dAS4(3N*qG*~ z$}|OjM7rM)ikPjL<|uZc=`2eNh>fvp`LT|6{giS=vg4{LA@h7RbkZ}s1r zyGb#b+p?H#xefSAio)7fU{gN`$3u4i0Jsz7WG#Cs>0C$N&dytHzzZbmB=A*I#BFQY z*0ebkJ99EzQk)5(dvUjD!bzmT`r*VGJDV7tz-fLv9}%Y`E0Pn`ANdu#@`n z!8@tt*RMX6_ItOM-d-jl{!pKqPSHwxR^+bRJ??`8d>v>kO`spSF;SjsZYu2VO$>d!EjuUP?OmzE+#Fu z2nW8tVzJd*0;0q(pFP|iS$BS%%Bt^qWwpGtgry@<>yQH%GcW;XtcI?qGOYEd(J!bA z-J0Egfnk6kUKnOn1TN P*z)aRo`zXhHSjO_wcr8-TvU!PP4**c`NqT!1d5^`0mV(mXZTN14?rM&cqQtPGnU2&sA zl%{~_$Ws^nr=NA*xsm)9Mch5C++ILiMXHM-`hVq1kN23n8serGLeri}qJq0c4@^nT zxontdTq+byTR$f8P{TzcY=2K2i3;}2+ut2rXTgS2zmo_F^G>dcs)42>FZVs0CddnzXcLRnvSeEA77Hw`wD#6{V7y>Fl%$XbkwB85KD-xiyg%M)s*c{wdmI?% z^pLt4=q4wV>GsVaAvAX5awPYkJSM+pF^=NMU3x)XdD#|!?5#hi6?DK-lU%4e3}+>5 z%tY@XwPLzDPK_))pswvGCl9eY+%$SrcI5v6(7)8ESE7}=IPZ79(I8FS6V%}!yatd7 zIK1}*`OF893m@%EkITNFc>Su^$e%GQ(1%D$Ye9-wiM|U=k?!$Dg~DlV#0e@$BD4gG z)7mg*Lek(Ukh!>cjL`B_Z_~ZO`?nzTEz02fq)c|;<9{-gwij4!Nc*bWWPVEm8y3Pm1@~i}pgdyNsEiHU0N{3R~jekxB z&vzi3i0%AGwLUHItgW^bgfi1-;;w<78pzq7`=v=kC8TVvLiywTz+4Xcpxmix#JJQu zb+NXJR1}d!II=35kKV^s_U^66ivFAz&rb6>lV=`UYA8O^srXAy=`J*flz;$H2_~NN zi<_hbtjo!7txB9lx*6G9_iye;o%v?1PAx@Ltvtrm*H7}X2e_?%Vh)vT+OxLajA zsmKtoJB|5ozNgt;T=R23y{KePDiUHqi=-_Q*E(HrUZwlZ`*-hYEJNG37c91>LTJ)f zTS|{gt1(8sgA} zO2`B2!ncsRdqNlz7)$0^Wdr7S0IRpEpHi1^P}_j*?WpSD@imF@_*)uFZrvPgl!;G@ z=APD&dJN8eExS)@vdTWU3cUSGHtt^ipD{6wr}#kW>HRr-Q$L0F;DNbqQ(1Jd&9UT2 zT+JiH<(*aWW!c+_?1*%XqNreF7V>qTD8H%?Qu4P|%Imf^#m1Ips0;nkv!u;+14%%p zL9jXm=`>LyKeXT2T%}v_sl8aL(QX5uYWesY5n6NN&RcpB$ z8+seXIyb)dVk3WrTBwvZ3KX=`lo?QAd9DLOJ`)P(x}`u;ZX;UcJg*W@fj#FD&%0W- zI}j)nR&~X1ElN`Adj(Dzx?8F@W z;JU8P^t(ff+BXVW3Q9m)(v>zz9kfkA#yc)LH)|Yl3a^9 z^3{qW$v-b|WR$Xi38<(Vlsn1CNntVZ+gpY;an9z;++JL|SJzYZCe!oL20Gnc6ts#m z0HFFkn0{0&0Jx@vD3f;t@-4}7OE4iFC`r=Ye|VmxhH7TA5=AZTSEilwA=2?mv^>x{>A?D_BItB#9$ z?e}x6u@Ll@Ro*Ih-rt!<#oBva>*ddAT&{j=p{Q2o7acAa6<(#)3Mbo gd;cNztu zP2b4o_gd0QsTUv3&g&V+oZCDs+p0OV<-+K#Bf6-L(XM?(ab}0LkcxG?g00_oX6{dc zw6E#uT(9LGN!?t(zPAq{?@NKXZVk-(o|xgNLr57m%Srv#nn(#kd%d;BjbmjUyg`f< z0cFxT^2lEEQc@L?eZJUCnF&(lH~#=O0*6UgZWS-pBXeqG8Y4Fi%4QOjHguY5N$7iO zaG_vG!(q^Bi`ifT%d#8XPpW@%giim5!5o>WD{lJ8Fj{$t#A!@PI4ce>65!{#o`TL-R)Al0X|8`Csv4{{ZJOKkTK~-w1#Eqp|*( zRWEb;ve_Hk)W*r>-@>Naww;aZcj6FMlsPBMhT_(xAt*F8)sU_<3w@6@zA@J0R}iv0 zj_yHuxS4$6($?}_Q-S%6`gN4Q`Tfxh9KzLaQfX7%r3_(LJ)6i1o@=kIe=}G|G-_!? z=8fz3(@XUyaZF{y*r-bB^eYix?IuW`3Qj-FrVR5-EPCqn=;}aD?Qyjd01JxfB$H0S za7jBulTuLrWiU=Z5;bu3$?3|>rbXX*xd6V6XhNYrYCFXRe#|w4wE0*3Aw0PJ#O?Tv z9_kfWd0W=^qAo+ZTL$oQ?R*<}x%hMJ!+i~q;Tp0_i6jySTwJjIx69hFflYlYFx*}D z+gGy(I1|&uL;DlWpUo4}q&U%`F(&G!A|WlXpTqR&LJ|^@n)?95Ir~R`=Mq5I(x1oq zp2IPrtp)33bdy3ziMG8KlJd2`#*i)J*emNNr~hSu>QnY{%Zc4i2J*B z?E6ea57N9U-p}6pJ*cDeL5usR*iZ@l)qOnHpM~Bt{{Yp|`qnU;yE}Y5J*emNMU4BW z*i;GXPxA%ierMQBY+a*mn+Zm166H>n<)tAp^&KH7DkPF|Z`s|kmd0-}&IM%s((mj! zn?VF^BUC>ReqvikjJV$0u;gurE>A>p_a3<>QXAG@SYN`V*Djb>aCW#|-5Gj@srJtN z#*B77w*3c0=~w1m$GzKs^ZO(#iY_>0zV%g};?Ge#!({xuz3FSc z?Z7~hWss#P6#oE0X1YF%y_l+IHm$wEuL879t=wDOdFdp%o(lQw9(Uur!>vqUOt*<< zlkG1$UTi3UGEqI4&2t+~@sD&agWCPcv$=5V(yGE+dHzQosH)8Y+nuUci7Y#*EoLiJ zsMi!3wv1PfqqK6?t^1R4hi_X2V=7+Qyvw(Fnp$GJAnp5;RMQb|>Pw3qQk;f(uI6@> zn8wwO>I&<&`-uj5^l|BMT;2TL{{S`n2j$<#aW*#`wF`FonBUQyR?@S})Z`l!0!s?J zPeKS%!c+*3z$49A%!Gv~Er`Phk2Y-r^~OWz?v1=oA~t+PaVL--73O&g@j4&Rhn%_p z0G2&O@)g^Zd83eSyq&*p%3q(lZxWX&=37w*$&7;P~XiwNh>6i+fs1M-#JM64djiYOQ3?4l+(Ke`ImA{ z-3wKU^w;Xgo;@Y_t0L>qby?^m+pPYvmVDoOyhyMWA z1C1JxU-OAZgwM8@f>!iLRgue6O7O3+m+|3)iQBH??2Aikgz56xS;-614J+M+ETM!kfzk~az%!kp+- z66`tXKYba-l$^j)Nc#J+co};$!QlNX9Ddr!4l%fW2#bCQM5PECu9d~-{hIHg@x=4@ zzs~O{Dh%7UfsCXoEM(0tr%Ry#ENSsnt1g)5w(R?cZg*S;QHPrBtB&T{&#@!2FRY9d zr-J9N&ZWhql>npDJZ*)P6kL?)iGR|%YwB`RXZlz4wR>?ugKTNHA5j1b!cTwrxWYCA z#98Z>lR;J3z3PffovCFbMMp^c7c#znI^20}yq5w^5)eTjW;f9G_^w9~m2&?8FLX6C z)WxDpnW7b%?C4D^nwvv4&XoA^jQ7fC99KZ*S6qHfZJYhjUhO(se^R%8u{~M81?!&> z@l5nUl^%?4jo5{aX(cpM5zp?CS}k2iLEntOww=0fK%RYk0_tA;cd}PF*;%@riCn*q z#KfpPtEydfO4R6a#T6AC$totKoNXq_qehJvB*P?t)yAb*&Mx&)yLfWfaIso#7THZp zd**(1ZK%mhzE_vwa467Fbfzg;-rdK4C0ybb#k4!kE+PXPSPfKPzd0*za*pA;Z(Zkh zcF%pfC@O0);}??Rn?OJ*p-34dVrL(QxUh&t7^n)Zn9kW`=QGTDTvSze-F9@g(_2+i zb*KAzvqNpwb)X1J3Cf-478^@Ri%(jsWHT1Qz(uG9bQ6bnzYJY?^}WBoChd*2Yq&yv ztL#NtDJ}1kUrLQ4#YWHd~ zw>{b}%SfBHqP({4cNvRAavfnNiCU?oW;=#9`zwijfMg!><(!SIZ@PGk;jW5T;!XL< z+%>vkU$&f~8P%-`pA9c1QKht0H8%1IqLN3_I|#%!;KMGxO5uHCpda7A8kUqrsC zEzxcK%eR|qTawJd5yb^nGzpYa08ptl$3Jay=3^hHitEk!Zo!#|1IC}JT3e38+wZ=_ znHJc=U<$2B1;v_noQ_z|AiSTbfx&K9SW9XGoajjeihn7iCnnPwpwrnHkBagP^bkY5#BS!rLGPh=~+A=w?oBeZQH(XdwyeKO|o^Z zJ(+aE{aUE3)$f%VQ!G_E7EE<5K!8(J*D4N}ax__N-uN_90xr)VJ@}4!4*5kMV6H(z znH(nPil+9Kujx-{+bT_mYA1W&;|6U>klQF!hZ}8AhY}T5kfW(XtxYi1`4IU$w((bM z3@B`UZcz5;-?8M+d~N6=-x*|v)DEQ(1qZtiV_{=c%X=!6&507{d61+=Wpdm<+PZQ= zYucr&A@$)IZYV0PINV`#wNF?qY|XOlb>5~O$#F>g+gufBVYOE+Zzy=b$rypZl3RwB zjTQSBXK!sFdxudAwZ58vFfF$48^qwvoyHOPvv4~ITkN}%(*FRMwaI-RNPP_$#I8wE z)HRh2UHIbO>2~6Kd0lq-42|PO^yjdh=C!$jnE2rlyoyvP8`fv#*=@D@ZRUPvnaulw z@4a}|Ils?qaxl|2>FF)`??-L<6n)7qB}4@&RP!jEG&pbC&hK1FdmW&U_+-;gO3B|o z%vwW^lKj8;pml4`n*N>(=8^D_Nlsv#k8V3gGhJ5>2GJxx{i?o?(S^cL024s$zaubW zNSh_Nu^3c|G7TAJ3Dlx}fMCsKMu|D@84GId0t{s4vHmy$vz~hFkWHEkHz~?QDRX_ot7Rz+}jpg*FC3sa>;dlg~xJx8Uvn7l+=(^4J%H}Grz;Za^9Ih zAZn*ETY~D%#K7tk;E*FdyZ4r>WxUPYs#}HMkZic(Jl7RfDMGpuRC<$49r0Q~DgXs6 z+q{+Op>v!*uh^qcQua%(+ZG!=<+jstZGxu@OG~K%KpiDWsH%El);{sF{3}4&_Hsn> z*Kt%<;Nb++=bl)Qs@CFQqGn4%mg0c+{){0n1n6*Gr{SJ_v~BJ)(PHhlBk#7ZPPl2G) zRC<0zERO_w0YlbHF9014%E5IxIC2wLPAhnPFd;-Ac}V2 zGP^_uhZh9<0o`W2x+X?ypS%F^k-GV2_NZ>aYE!V*xT{azBmkqB9k_+w-9AGA@K(;& zOkF&$BATl}oxy3JakB=|Ar2xy3f9txQKr2*V?Krg7`6GbQNB@8M0a!CVXD))-}6f2 zTVu#G*QH(*p`xWzuVy`rYoBt~fvPrg?lETf*FYC6dCQQt45;>YcC)7EC8qSsloH}f zSqU0bRnnw;vKZ){vw)T+6Cl-ak8=0*oy5#!F5q=tB-w6S8rU0EOF+F!*h1mA2#w@X;s?$UoTUtPYfQAGZ+SAuocS{8rcRgf)&F zE1u((EsK*2`-a+Ow5=*R6%`Ytp88?^+;TG9R3X&wx!3?;R3ZR3gPy8>Ol(t@KF06(mAeX z>J`;G_{QmGYoDLALej#_2Q;<@Mt~fN#CGb5&cy;$_QaYtZW7k3rEr+@5*Ut&s6&zu zhf{$EueTVSF_P0o()UvIKZ;kk++23@+;@7u9sr4#e>1zucpyIc?2ohr0M96KeD}vQE zCZHMua5gs*wax(ZNL^aQ_}kPuDD`$dy7cD*mX>s@Is}33#G_}N@N$M07WLJkM)%7` z&UbPa#gUR!q$DpwPIUH+OT%|Kj-|>9Tveo#d;v8%uZp(C^I%+}xnyaVTrCtzX$ft_ zQPi4(k?h0VHg4n0BB~pCZ97v4A)|_&?fVaIwX#~#X^w(nrmIU0h2+?!4Gl3=pD%FR zqpHy?_U)OlY3byZVsZA_Oc*OohA>jwN!R5h9L9Om4JXUY9mJ>b?YmBaMg*+K9qoN3 zOR+|+4G)x$*@oYYP2!#nxoP)85?!BYMfrGFJ1lpjG>Wha29(hB3F-9X(B8|b>PGx7 z*cuj!C^M3-J0{I{+mWr?ayVk@r_peffD)v6X$3k*(~BN7NcL8$ub-0MCx(H^GK*H( z46U|dA*3xtHc07QNHfd|2Ia9ha7<2N<+go4YW(;_?*WY)amuC_qjX9+b?p-Vicetk6k}vW%@ed-Mz}Yk9 zTOmg_))gUf(G}-iP%fPWYBWs+dSTAz%Rbqlw9z!#m^NQM!NaTcD){ZsNBe14P1UyT z9Cq|;?7M5R?w6-7iwS`edOW2ml8mZA>z}t2xwp*1gqp1e9^At7(i=jmUh4d~>~JQ@ zx5M0b9lX?HP`M7NiS3|{i&jmV9-@alOxt%1bc%%DUfjysmYo8u{AKAC?cvM!&MDlR zo!dlf6j_oLxZ*O}i^`6q2K1sq00rx*&n#H7+oVHgCT^saQQP=QF7M5|jXHU&O%w9Q zg8T>MHOh0Cb)YKS@FEnDPLWS7bw6GyEbe)(ty(mlsC&K_pR4vQr+!!O+GE&IzG1D# z?B2KIeiS^mg-1pC*^KF!!mRGoDo08s-(;t2LF?kC-%kF5?p$%aZySQ=D{OL(-08iPwtKsR^7Z^HvN|Ab!sfPG+yXDsN zt{^hoVM?p76%|zF^ zmp3(r&whV|Ha6t6Pi+sun{BnkVG3<&2819a_R!*!d%WO@<()(juJ(H~FEnC6Dhg%q z4@my1d6EvX@Ed1ds9sWoFEq*DdScY@9b zQ>2=!p5E;4V(LDk;3@ieg%fk{T$UTVH*a!EB*!m05Zbgp zfEJxZR);J++da#*$6XA=x`-^A#uNV=Ky)l`542 zLFv}Gy~=uut!;@5MGCKbE=W+75o$DXvVt@H&v=nJQqRzLhaOTo=w~l8*5BSu8DAz1p#VWeDO}t-Z+ojbBUk;@d~@YtjrgM!-KU> z{uH~9!na+aW6W+`0<_fjwyl3&SZAEJ>5w@}?5ptmcSfDosf?y;ZZd+|Wl1D;tqp%x z61tADA5uM;47dl>m9?u*vO+4pNT!KjL2BWh`v90@Me0bGHvOk=ijv6n7s@m(*t zpDfl9y@!BIA-FCRW4&#?LypX;EU6DjLqHYyduk3WR`K~#2Q-zJ-jQ_)ibjf+S1!9t z`Eq&+<_C4fiyv<@y-}D?5VOzBhR-&W zGVP;B@l~;#XnoL@DQKx&xdoV!%0b&38)n?@5!}3PfeJHDBjx}p z3LqT73h%{7C6c^nz$&zTueEV<$PCY-j=e#8sNWu=?p8?~k`o(N$+z2A$!(ZUy6k_L zc7abVPK1>%nI@wkPAr=rw^o=&lEPl+b39dFy8YqEZi!m?z++p%pwxYX>{Y}pNKj>& zi7wWNOP(6n`b6VJev1+f4kt%@T$Py%5pIwg{TfSAs2TD-+t#bP##IL!cbDAli@W5HN=#R4&tluR69kxXi^PY zUYZQB(6A}U2@TOfd#9AQF3a35&D)#0U5@1x);y@Msg<^>Q^HVio)vNoga(zM#E8@9VjK z`OMeroNIDbCO~@P{{WZwDby-}LP@Vmj8}JeZ*Xwid14lxrF-qZ_1rfXUSpklJlFRzdL{ zYEbLjL5t*E77pio#ZCNevUt;UZHt39`|2#arH?u0B|9Z8C(287Lru1%khK*Gt1&@} z<@Mz3lm?9za(loM`p(Y>1F|9j*%4Y1dkEZ06#j;Lxc`3z-!j!HFS|Ep2I4 z=yI+)Q8aEAnKqdUi78E~^ngm5aSo(^l!8Vj**93ND5c_faHvB;3IO*Gm{A$ZOWCal z?$8mVP*I|XOd0ZYg;>wC++A$+>$UBz%d|Z2fGJ2)(pNw$mSE#5-M0j^sU3ljW3C!X z?bj50>Jm-wNR{l+JEB#7zOID zeA6TM#ubePLX5dK;F!BJa>?^)b%G|fssvGKFjBC%Ka%0f6XrIbA zE2N{XPs@v>D94idJV{o=aXmg;vYm}8Cew8*hZ25Q5cn$>fi5$S+mFFoGN1|>h{L^ zTM0xxmir|g0Vt>1aCovd+}xC(2a`M*M5M;lOWnM-Hy=WY#@jT`!Qw*tli1eQt=lha z!$LxYdwet`&}B&I7~>4g$L(`2@n0YZx{ zY6mR*v5dzgf9@UYT^WUs{>_8XovpHs1t}HjPp=HBq=Z3n$JT3RXRBKjMp3QCEY_eJ z>s5Ti$`$XfH?5EV0P>^st}*VseOsNNdOp?Cos}6+?dxl^qI|5yve|u}z;ujc*%=R> zb>_M=2>>>&s2mg84X-u%ZT8ZDVXskE50z(tTwC?~Db(_M;5wuI(ky)_Jpxsz&0DQF=?j?B(8z2&!N zp76b=MYVRne%n~$+Z2G*D_oyb{5w>S$LK%BCluf9E1yEsM|3gXl$E>vP;mpBZu1g8 z;wzaZB!ojpl(DJBGO7Ve_3y-vBWc_+;ibe-D80$t%NZ=(*4CsbkI=+f2Zv-nvI&uQ zxGb9Inv?qQ@3=?)3nC z<}j2fB~;Q!@Zs&Gm5>igX@8^+K7~a+EW`P$w<}8kkmc%1DnFw8g#Mgd_k5wTFnSha z+G}F@Wh3t&wOq*%0s;a_t|-m9TSWF?aYdHHX%sE${w(l6Fq-_b1x_}TPb#=OW~xcd zqUUd}RsR6e9A6=-w5akWt)+~p;!uSH?-Z^!PTl}ueS8-uclWGbLR-WXn{M1*r!J6~ zmfVfN{C68kh~g6PLYpZCB_^E+IUGN@IJn+HrB!+pxh04ehf;`TTfI-+NYe*$I#ty_ ze4npgOc&YZfE`EE4u^L9K@vuZ|*mcpfwWfwEBMZVJGFWAoBJlf4BELw4=SM zl=5YtaqX?waPr4Hnx zepcSwPmnhp=Jt_xe&3FU(5tJyVhn|(lAw^IT^$B}ab>|{T?;i?{_D$m_XM|sW?Aj( zeoHJzY|+A$qz>HiWGbH0OOX7f$*pXQ3bt?Kcak-Z;OstG}nPu%(9l>ehs@ZJAe{`!)#+y6Jaf{X6 z2}`N*#bscC+CPYFESvniVbkX#BmEi5Wwr{r^Y`v|vHxy*Mx8y$4 z4l?6zBq=BNw5Wqn6+=u=qI4N08fbhe>t5_Oc|~H2 z`F>%MWP-w#r6KBs)SSX_qd2Pu+mGHVyP+i_gx9#?aIM_|36CS9%j@6XaFmJ9Bx#m{ z)D(V^gEiGfJQL$-yX0M?I1xgUbgH>@I9a?@K3(6zDpnJur~u@XU*0vKsrC#Ei$0Zs z9ggb2s-MzM02J5FJd&F(Ayo4v4Et+>(HqaN6nQF*TB-4uU^rW%h@NOdGO!)0Tp%BX zSWtq9$2Y4}MKj-HY~=fP(l9y8n|>|J_;=67&)cbONfW#2kykQR-IaFP%0}-@Y-8fbPza{ zwXSwHN*v>*Q-joI@v~m8)(drxQluu)1-4w5pdhRODOzo-dpOrCn5Cdwg zxa-K_!nShITwPDCM9b$`VN^UjvlHzWbp!UhiBG@>Hx%8wwWEUq0bJh=bkoF}Ij;Wz z`V=41<`aJ3Z{8`#_hWf6zQyEof&$QlaccOVDMyX2*St{N$)_s@r9Bs&nYgr$X1;#EM%=!puL%dYWM0w#iDfcWtMon8@8dK%z>MqQ)IDPauNfRy+2-?BLK zmU)II!QR_NEZW_LIp-T?_gi<*j@T#~kgk=jeb|oO`AMv8Jjk7@m+ijTu*~F)6R=Q) ze=Z(?cm)-{$U6x=B~(1+j4U46ux- zp^INM_hBSG2+~{%AP&9QSW1v{D6B`Oo^jv4pE+k}@@F329kcFDuPHCZS{zaveWE~m zGr zrZ)cdgQ>`_L4zsC(i>KXqB>n^L@kAalb^S@8(Vf>CjKVK;A`kB=SEw3ZXLaSC8#2t zjW~`;!r!XjP|;ciA4=_Om1 z9amb?&2FxFcC~?vWMZ4ZpK2`q(a*S0f$T_<{Y!JCwKg2O!*f*-wSEh3u99?~to5P8 zsdkPzkvO!P)5SA`e&yu2V3B}=MRZobow<@lw|GXh@$VmPN&8&d;VSJSPTq?uOR`qa z-fDWOAc0PUEN8oGYI%UZXxMFEWBE9P|0rlaV@K0_}gcR{CtQ<$Bm$+3=* zd~M{RXQH_oz#U0KlTe^V2sp7Njca;>c}lb1Pc#8%=#l#d!4nwQko* z5)r~1VykFX&%@bo#hr2u{k6nxuH~JS21hA-zT4v_>UfEo4??Bf0S4W(eO~iJFzjtv zn%bLQ{PPyL$r=%@OS{JUG0#+1FJ=j(`dXmE2w~A}M9mJA{ zRYeZNTK$+?M2D74^HK0qEq&j^bhJv z;HUYQX(jiIYWs}@j?bJKV*uNdf9-> zs$sW{&NLb#2(v_&FU*q#5u&z}M-ro`p3dAX>&NmboH&qO?*xxF1*L9)i)nf)E1~@= z&%XilPbrKHzI~7o*=46}Hq{{nbsFDC>c?1i*~Uy;YQ6xuKt{iAcj6w6 z!14NVDyZ)+O}*~x>-&cI%PE?sV@3&NDJ_QKu$bs15>0fcJYVgs+Zdo(tT^v8UTO8l zmnN?b$`SOcLt!P>hr|#~2^H!+xF~2YR1N8ld|+3Cd)%J+X)8m{X$Ukmt{jS0Q><)@ zhXgp^l9O#})QHTq%WF|HG?MbbIVatO{na+iL#T+|FLz6>gnqe(sTnRr zq=QNvi2Ct&qNTO(>&^YQjS*{*F1+C8VlUa5J;T^=LiXuI!?ZQRwnZpPEIZjqmnYOpXhOpsZ9 z$l)$HXd4Ac>;%&kyWIPoHYZ&jDs_6iO#Dg7yL!cP{{U*&R<~uy6wKSGh|&^=Qmorbo^Qkj38+!a8qQm| zHS8am|?)CXB>$k%eIl2q&)fk{?{IFVJ-wE8KEJF@bU2T59cw(YsAi@^F-HQij3JBGJ@ z{I-xhZ?w)Fr-b zh-reU49EvT62+rST%8tDzKKFt3B zUOB4I`9!YV&3`l}b%C{_{{X2#f%1VV?=)5#-G3!W*nd4a-*`6Z(%z$)P{e#*@|l<2 zQD-GOT-rC4B^p3!M0*aQiyq(pVN~tU{IHQXz2U259knI*Lk#??HMpV@2i>Q?4{_F} zN4NzUmAqszG`P`6pUkYum8vql^tciN(nT{A%My~>P->uH$o0G3L(Z_tdgT@N_l{{ZFf^(APk)dB`oqe%Y%dN7sW#+*a^u#cGq8d8*GYeH~RoJKTF z3)_w{D%`f9qH^3TNo~xNLx&L^6m$M11MAb~lNl*Y6(M*j2>(diWx45SQtMcC3R-Iv2 zPgzb_7sL=mOU!w9XYVm<38#?bni7>&K`Aw-w--%}By8rQiy`k_&9m&A(BR`$&HbOX zM&{fQZEq=1*_i4Q{Dgp_+7zl40o#@}3;c_sh`X3v<$}We)xcrkRpgoZd-09VD_1Lb zD_wB}`xLwB{_PLWEQQ#?_vt7 zWiCTcxxqYaySZ-dt5g-f)aIkGP{hT6T~a}y1gpCd{{SOLofO1*!stLelZ$2Ucg*Qf zqsCOFHj1muAxT1YcMA15Z+mVU0+id?Ga(|1A--Rlw|ay{>U**+F~;4{@MfV-1jwLD zfa(n~YHTfjIj!`p*xo)2LGS zSCGn4RJKz^0qix#p|*2vF!?PSu5Ruw*_2$HmyiOn+dtQgO{vBkV~=+Ao?VJXjg+-? zn0%L7lB|^BY}ZnCD?XToo3>vuF_Gtrdynr>wzF(3Z^Dh(zcOvnwr)2cF3lF~i<@X@ zE&WJwuPmsA1t3t@R(L{Q(ZwdE)S?*w05BIV1%Po-{lnF-DqXh=V`^{5?5y5hTIXA0 zKl1IV*B9DA9rUkUO3$%v;)ItssDPua`<;Izz(*lyCxWRP3mn^###h0~zbMUV)`;viLXT!IIj|hSRz0(vSt%SUq{v8H4?1g_R8l)DLx}eL3UTl0 zmcEtHt&w+uxwzwL+b0#bA*3m&?zWvpDs5*YPW)tcz9CSVTQ=Eok3Kkh-0DhJ08W$k(-OIhs9zbORo#<=x{l-$)5Tei zO5>P2{{W8TZMpJSZj&8tKE%WTR@#&Mv_0VCJ-}vUw7D;Ct90&G#EUnJY%*v^HC*HR zPO$_>uI`+r3K=K~KuA5pl*Wec2q6z+|OsjWI6A4*MIR<$6swp?k|3i;_#IiF#}>73$pP+nN7 zT9X%Ut?15KZg0nmP=r4oQ(&*m(?uw$84yoQu-H!UDMb0OMomFWkhbK6PK5h6XpY14 zn)9Rwyt0(*9wHP06vR$K<<5YBHKLZpbjEo;?Uts4Mcueg#o5CUX*#BI zx7Ofz>QO=~*-~)R`?5u7XJ~C2heqbUDIk}~uR%kG-`#R2KkZebQ;mMTsm8*j5!;79 z-Ett4wf5YV?Bkm~-oeId+?NQ8iz9NzTCKXl9%OY?2M4gzBaHJcx!-qs8Q`=m2EzO| z9_Rh#2i+7?tQ^26QR$v9w&&anIWZ99RMdj#GmU(D!js*Z8DZtW#uQe0y*AE1gygvd z5djpTZJ|-#NvF7UTx+y3>1vM9Be?Wa?PCa*%Q?i+yCkh=Hj^KFS2 zi(aBG4W&rd$C{K>4$6~^pLP+_RM%}-A240I;;S5ptTy0=n5h(_Az^g-am*WvLFQ<_ zl=k}{NI?p2epkvg`kYaYq@>ls)j4$-tU~UmQE9i15ynq~;ZNoTxGi^N7Y(isRzPiPvutB$elMZ2Sn)GwCbn4GM4H)E!{6-Bt_GN#Sj>}U6~nL+?P?~Gly zJb|{4^(r54tqbqs72@E&ldQ4J7CU1}a;3H!$aYZxV}i0wtnT%%vY04~Ef_rkC?&w% zPg>$Qg)Y}oVQ+&<3e&E53MHz&K;kNBJFpa9swC)0>zN%eY9zeWO-`rbP{{Pco?=L- zbDn0197D*{_4z4>XP%<2wEpb8S~j#L_pSA+Cvs-!#7KP$oc@O5-`wJePFo_dw5{g{ z%)45Z4AdwGKu%cGccbEhQB{EUlG^F^B(P{z=54M(Wv?zf(HT95%PJ#3b|_-z(D4Op z2G1q?v__g^mk2s9aT`Lq{m1RczGlOiDaf(R_=FUTcQH7fG50K1iR(sb`VJJ1=N;fu za#&`M?*!DjZ7zG7(%Xx?W2Hy;f^<_4ZL*lwAB99+ZQa1(K=7h_zYuzclIoP^@s8@1 z`^9bwf18Q+XR%}VRVM3XZbuy+6jyolyU%yjy3#j17GHQBF0Mq7Do0a9lZzdu_Qc1< zs+o}N_T{;)WL$;k-I2NY+m$Z+Gk8gAR^pC^UVdXvmyovIZmCwIL(o!(8!jI=4ECZ( zhq}3oaBXXg43UCkSMw>+17be?=a23G0EtDM#nJc%Q>l3XHXMDkm{|uZ;u`xj;}i7c zRo5A{ST3QX!K;VbzM}1xfgaaxj*k*qWu&H^C=dY0C0@uJgC9iUmd;#b^$}}&=j(oK zW(KEV+bsT9cq7eU{48`f+MlBfUlqn1#?;HZNbm#gE2cX@y!0qcy6Psn(h!*%DUky) zjEf`u!dFD$&+x!fd(n{*xNQ|3GtuJ?Av>fw`G7Ib?6Ki7O$60RHC$0 zliOT#?%~VKF5Wu1uHD&tX2wixWy}>JKb6J&cc|*wR?lhqHb1L~AIE3=%F1?zIR5~q z6DOGV9~|1abCa%2Y`a`{pjhrMw=K9a!-!gAETpALML`CgF*&z0bH4ShF0QJvzV^+O zc&P|r1O-*S3dCD{3uSTi{p6{0oLTQGcl;Qc?mVCOgVd^>nUDVfY9FUHZd|v=Qq@!y zl5!e|#Ux6#C&N{w>pV}Akf7C68&*X}ERG%6&HPd8QQ3Q6`&W9EDfCgdgL`9o%aV;Q zPQP4!^yg5A)SkZlSvSSkOPU9ORPN1VX#|cu+PhHuaYQRORL&88jRh66f%ZtJJ5izChZS%e$8AE0SJv;_C@Xc}Rl|lm^gELTYF= zc4MT*b=bT!UeQul&*p6gXZLK8$Y|RB#n4tiE00V}Ap%@0oWhU-lxI2$kG(X;P;n`E>|m9U<>8jmfxcNvbf4r9cCuoN@x!*_Q(CnSNrR0OFp z*(@9oIEGY#&sFn@%9ePRPEF*UwP*$eExUia!L>FCPnN`4&a&Eb&m^Q%9YKhZ(pxdk z;y_$GmYJ|#$iOHAh#u7(3na-(4o!7a)H*3{lhfF7CfE}x@}a>$Y)JWb{#+y^5Hy6) zbnAz0XGm1r&wo&ydy{-X*mkEKLek~#S_pv}qNpTOT7c>7z!H?lV;uKXs_(XT{nLD0 zUAo+L2t|72$~Ey+^aSI~U~-knT($wy-YP}hyS5dEA}sb|0??9$EjdsQy)oxF1s7wF zl4tcE3IV&`yq!`cD^tfZ3aU@P5!VywwH!7lhKe~d?ic%;cKdrPmnvGqA+p#dN>M!k z7>?%=L7Iy+dpoK*V*V!_z3TXN#0Tz6CH1;)h-aW!Y81Jw7NPr+&!7Eh7f54wPH`x=#V)A>;0X#E zD@qa5=)vcEdU{VaMqqePdK(CA)pYDgcCue zWHQ7iKW^FEJiaE;#ZG0nwnp+H6wc~sO}1xDe%m%9mN?TUMAX6+y-A5t#SD_85s&ZW}8$LKv zpkG~_s#Pr*&Nga$JVyw{ZznYM-3Q@NcO}gdVL@>u`9Z}5*QgZsVRlDCr#9w~DjiFN z9P=1fHEgDfE_6w!(Std)UI;AvuwN+(R%HWK2438Ip$kfN*iy91j#4UhP#=9p1%SCk zLScQfr!MaRAHvnw2+ojI*GiD8wzB$6?`jdh_=HVjCOENVH zrm(wq%ITX(Eg5{kpvg%Zi3Xrq_ ztw7SIokl1a%-zM_b?<9^&W#GzZ}zpdmf4CaTs+RQO%I7Q`jtYPusHpsHn(@Alsf84 zi%PfZ2+yTbbFMGvCqGp>7H1|J)uTtIEUQ(P0+>WwnVzSC^8t>g{IRHj4M_&0Jh0|D z<#5y_ULlOqwW@mCHUwLvvqVQ6FI(C3l(nU_A<6(~f=*=iVT9LwCO?}2_wQ505mwdTFhM-bF?L97qA9O zTFpR0N7?s@Wxp!kwYc%p(g@R49I4_X2RA{yQbNhj;HJ%)vFBQBSLi#2)#b{N<1X3| zxlOW_6r`aFM*^)fYi0eK%OWG;KTAk-hoV#k;%P zY0{O(+P1|d=fd=V@>Bj>R6SF#yBd#du+D3pJIA^>EH}Bi=G|R37HSwpeSE)8YxBOm zPI6_=Q|c$iwGdDE*hejLUok67$6AQs4fG;G(?OmY`I;vtJy(#jqI+m7k2Td^b0~oG z4@1|#2o$eEF&*lx1J}O?f=yCWN1}JabLC`0l!M_VH9bdMPQZFx2vz&2`q<``^so6g z^E9Y$@6Hmq&QHtUW3#w+q8*};+wsC;%|bO)0JJT&&~+&%8vfFe#hng15V(`QIEHny za0Oa=PwHLA+_Psu33@fn3scF|2unXm%{gMv4Zl|vjowKVZ1+e^hqpfBXXTs|8(JMH z3I!}vNg}4b1&y81l%_P; z$_Tz&bm&uQQPP(vY1$~E*l^o#=Y!rDeuUqCZ1WQgfND6Xe#6S#r?c%V+k0z}?-m;f zTGlM_C&*?pR^p13q@fA{B}CMbOz~o$Ygk-M=19O_;;2~7`IfPR$cAS&G0T!pm(2Xe zfajOXTa}=`#9r=6r_}a69?!=A07<^UoSx-o$futAA4yi%^5<^zy}4_cTQ@hQU*FjG zf%uGv7W>aVEof^~i*%JJl@XaZ%sbj*wzh_VbO#LwpoP=_0F|p*?(Qy)Nq?7Go;uY* z){8)sHFo>u;mbUDwQghnN}niFYLoYADJFzcn6S3wz6QC1poIYrJbcsHDWwX<;?l?C zcFSFgbGL^kxYH3|)8v<4bz!uMRMMpV7+JR=kr&CO60NX%?pqWly+(m6UAV`7ZJGO$ z71wS(9sR1}TfV#$g*3vPNLmK4Xst;gigp}FTJAeJi0C|2&ECq$+Y{zDf}S`p(LX18 zhOo%p_6hde1%;Vwh9%q`c`my1NJ^Ga+YPV6H369!;)lDtr)`Y5@{36;RqglX>i+;g zYAzV?)|Ciz#|_E!>&OrB_aR3_yRXZ$E*$79E-@lI#YsxgTnQsIkU{8iW7`|8nR{Dcc#a7-oE*1h;i`>3Yg~8l0jioAySnJY%Bfhv0@%#t9h2^fLUdot_@zhx{y!8 z8X5g+z?mlSN(gOF7bS)+wn%z^4V1|UtpF83>c3r*!ktmexh9xyV|h-Yp524pD#?0i z-oyQ>TOt{bN>$C7456mud2#9+*Hs>);mZ{b*r~zxEz7h4aA)%yg*wCPMh(8=5SdPGn_G(w zok*hP%SfUB04E)F+B?xMG*gHC7tIgMJ}vO>7YFg6`%354*@5P}q*Nb^=@`@8<3*I> zAE>G$VBHrzpLB_MwmK0i67>exn{J@wrq(-BplEQ)s=<-N!*vmBhZQ~CxeCv_RK?2K z6u@!B13uWLg{dzA;?hMbn)EnOPP5B>ru$MwDv?F7D^=bSI zZYdc*_?-c}@bV8`lC(CLbY+s1fC;5Ct`XHs*y7^1_!DzTN|!}fR(KGkf)%wNZKCU^8SQL3u{{X8MSTPj4bL-7wTkjx5zDVSn4cy}g8+Fi9v?wboW>Ra56XFhUO3z}D zwADr)r#JoYc{O4DR!Sdbl`TzaZ4Hp1&}%Z0Q;--N8y3Pe=Coa-A(7;sN{P*`qAAz6 z5^=3sF{*D!8bZ5rz`04D3$;FqVZLsBX6Q;Nt-2Q4SC}KpN`WBz@q+gkgO-Wezy?>T zTpigja$fl!;RwrUsth7pNlup1)ly9<(zu}6z{c7G%&m7TdokxUudt{{XkNs^@NKGj9ius^wWO49tbPVMJ!Hyy6n23V<>x?8ZNS z>NpkB_AM@9VCJ0Dc5J(CsS&|Uv_Iu7)dQrYf(WJ~*)#TwdsKQseVs@d`}WIkQaVE}-*ZMo#`8<(ja#|rlAQ7?^NRL$X093{5iZ3+I( zO3aD@LUGg5LlhX0;3_D%8rDmh_alRi9o4N9tCJ&(HzI7;E;o0cl_`b!D_&#!P?VbX zb;LFXQzfIQIV(2mghP~JCbcU60C5GW`&FOjCN!IZ-3LR8QA%<(9sdAcG0gSB6HqRq z+3=QI0#ffK!<8>;$oxV(XiD8-Nogji{@Eu_w+QXox?WMYrN5mBuOggR1iWq5=<#9( zlF&dVztI$?8%XiiRPVg#TTcbaUt3e}wk}bFHs5o0-!$3TLo%bf;|`@FLxP%{P&7E- z_8?>I`dlg(8uxxlVapdVmYQ(}inQ&$$;dbM?3Sc%yOZ-KN@_cA39@QkeZbhMD>-ML zEV)Q+VT8z9H1q|TSX^W;t{VuE+M01IpxdX>OIvyttB)W%eY=>ZOxN2Q(wB4&beap7vRaa{7Y4*o1-pSF;g! z-^o(={{ZI#l%-sfnF`94$sqp#gA8q=Pp)o#?g~5PNe!!KU(sCB!nJ)SxUgV9oIi?gB`w{C~bp64ix1o970(s zNJ1!&VFLw{Em4^*SdU1Ol%)y?AZL;5hQczB<|GNy-Y?snOF)z@Rd2B6-f-cubg-uH zBseNN>pog0Te!_Ln{nlf>V#Ws6;qO2R8@qs0Qj|@%xXQgj5V(%V14e9&U`_-l2x_esFvZXB!sE4-P0O1zFN?ah%74S36d^8s3DrLJ=@ z+#ojOh8_qCr$D8qfSgO(WUYy#2qjiCILp?w85-&kv{^%alzHxWI}$mnkXpe>!l_v_y0 zj|O(#xXFO)Zu!cijUhBeK`N$HG{w6A0C7UtbRlQB9g}w@$!nNslsNZg&)WRCG0WYK zTE(ro4Mu6k9P&_B3PQmEiqPeTnJmL4wjC!`FSqvm*7vS!+rd5d>kP{E8@O6=A-2_L zXJ%_u^>jYRqmdzLXx&({{RP&{A+BLF(Pmcxb1bT zNHxfw+(YG=R@GZJ=;(Hdy}GT@)y>m(imRtmpR4pheP|K(|+Sz{vXj}s2nQUipsA(hAQY=>Pv!^!& zt8>bmCB!n+A1z*TomtQj5PVf6^ueTi0|~B)jjf!no2HtjeaeViKYCp@{E1IFCc@}q zaq`sKkm^vCC}~gx6p~VXqaL&ZXwNWI#L>RmS{@-^t(WTGingDkPJZJ}(Rea)X-D|c;Y*-1ko;pto^`Gvg7 zm~i}PDN*kvV{CkNYRc>x<+v!RxSuaCT5=&-{e75$=Cu|8=t6Z)akZA6)#E5muH5~Y zJydIA0!&VMX&q6!h+S{kHg8D0(ruiH z#Cy2xy~O&w`?nU&eGN#4^O-5MA=P-3QfN@3li5sV_ixJDe-1!M9M-92hugRA^WhEea$-!-1w8y~9VN+FL@7HaiZIaA(FHJbLx2bHJYeh{w zT6$usYhyF@SInM5&J)InjlOQqmWLw7aha}4r`#ZdqOYeCQZiV*3OXB$D~f?fBztAY zY$odcw%*EIxyDfrL(`bi)|p{>M6Qdd@KDy57T$S{t)qg{^}6lSO04OCxU*7xZ>aw8 z&ml}NX=d)8pql0L(Uaw!3P*vh!0OaW^o;YsOcOgQycL4}he;zN(0v$4r!f1bi|58I z%Fv|I=t^yBKNCx)oajvzk;ivDJ=_<>=w)uTP78gy&v9whv$&Xo>TBh(DJw})>>82? z!ZTdGypyOcBa@vFFzm8!7f4oUp$@-rkkn?R2NDtBTB$^e)RWndU0-=o7m>j@ zv%`7U4TYq8#+6RaLE~M;#&=_7z7)E({{YRUu(0HJQUysu(oIGq@cEeT*j&&@txRq` ztu{7JhY;^lX2OkhBqH9o)Z<{3p{1=P4!^oaBxLfds)x3U?8)M_B&m%MKS8`2k8 z{#rKUB$?9@s9ecuN6Z4X8Rzt4D17x@$i`7GMLsT+7-s2oPvaV;#*~C5DO&SAo!D#h zMM_0#3OXb<(vr2f0(#fkhL>3`hSaGWd|O`XE|WDr^SHS0YO?Ya@5qFj3*u33p;G1yvI{jwdMO`kh;b<%WZU3(*FQt+^mw5w&xWnnh_b(pU|9ccHQ2= zk*v%gNL=H6*)71?bcM(0DIa9)UO?SDvfp8_?2D!5=P_Y1?aV@uo3CQzaJL2vwiY~cDYzVrhSD5ZA$>(FiSYWWi>#jFwM&tW0;KtA z9keI(V0^%e%O`j(oOznI7+RJEbs8U|21@xZYF=gcUP^q&O4bD`O-%<*xbgzhr!F9e z21V&FTtbXylj8{;rhsNJz0EonZ@MP;sJfx)>eXbp?)XsAG~45#rCw=q#E!kW<1OA3 zbi%GZt+MlOKwakIdsoO>T1-q6Aiwmq_32H;>ruWi6Ol-D!Xt@W!yJL&54mJxkgT&@$&+>n%(ZDll@5! z)!TLD;T|pv=*_EZVTOfNl<7?p2^63iV}>r}tfcx^*Xt-cVQ3tJ-|+UojeqAHHuDpY zB?PD`O0scd{G*R~i{4TRamYDHU3;s8we>}FXo@ZfQne}f2|c=wJm*m)T1pf_LK2@E z*IvS}+lKW^`D(X%!I}6+rjB8d+WeI&%P|3U2T5CH6{+{r7u|&&at&E`c`iJ>1c%Um z#aurx+hUvGt-R`m5c|@WfCx1OU}yDWcexh;-BDKi3v3P~5*7adZHC*bLPDuZ8e=f? z9Jirp!NCthfvM(;<&PDb9D1dDpm?tXPM9xIDPJfdfr{#gEg$e9Z4tLEKNLfcr|UmMeU_mkEF?Q0I}U&j+#=yd^Y{{UKj+@M|m0A){kXRt|4 zd%t-j2pySfILH(D`@4HxAuFEVtNB-PZ@x<7i=FRg$lER0NL(n6C@FG@lG~?EMLMJp zry7>k!9fj^n#We5bFXsujos#6DI|=yq>2w6S3s=X{{Xn(fgMAP?lDuLu#iz-+#c*@ z_j_*KN`3KF(6$}Ky>ty8NfZfnj&j@@s;z}_O*;cxtNv&>Yywc#maAg?$PlBU*bAyx7(iq+x`hGz>+PmBNrYAywy?UOu6f7LCRcGuH7HZ7 z)VDTmuS(ZA_r1x?R=z&FOleDJZ5aYg>4|Qcq9aIakVBj!bQEm7^lafYp-uYF*q<7kSj{0qkia@S3ER*Dv)m9 z%nOqD=Hs2bFY+PJ+&5*RR~w19u!8P{29uI<03=f^SuHYgK_8aig;O$}tCH$-G^VdS z*Hb+#es@=!O6)rqoc0VmrqPau;-hz1(x}%6jHkerIThut4+M$BIx!9Os~I7X zlD86*Pkt()U!+7XK_b;X&p3r8hfQ*z@Ekg^{(DqzP=DG#)T+;;^qBl&>&iOJi;cy? zv)WZ9Se)LU&K{tkJ6rD9lj%Qdv(C>5N|dDpq;}U8p=?hjc$)RC;43#L0=#Wl4os6wb4habcL0D?|3X~8qQ*1CrsC~b=e`9fKPAdm@I1mOb` zQVWVDM&&CoGtPw94eiIiJ!##;$cfbOT&&x+~(X8E}rcHc6uF^$AFW0U4%K11~_AbAx)ei^0khb^SWFcay)Q5&$ zdr^!q?3>ltAMS27+SZ>!*Z#E}1eE!1awIjWL}*Gw#;3a^;Y)3odL?P}rnkMx#+V!l z?HJR<1@ljH-!@!~k8c9v zkzrez2u7vqTC?rMumM2HKcyw^Ys4M90qcv)@iYi!zJ*F7ASvy^TdBh&BD)1Q`5%m>qwjR!n#k+m0-Of5*-uL#xqeoQqQ_jFLGTQLYymY!c3WDRHSt{e$&*Vfv7?JBo~a<-8MyCC=e0JVNoL zmkCs-)OPrrk6K{~k<=IcE-vRE1CWEll;(4=CL_R)9TN*9@ z06g=zR4v9);w&K1ODD3pN?8dB#Bc(K+(BECQ#SiGt*B5LG>pjcEkmg;Ep*Qf*&~|O zBWBK$qm9S5^G$qIl6n*xqOk zTVo9TF7!Us(+L>Wmjq~3W|qh_095;MS}RNC9P~o@EB1YO^%8z-d{(6tBnl{w&~RRW zm8j%uk20Ln2m7+Yx(PvACq0-c6JgO=7ZMNBa8|~nGJne@SU9jO=uep?E-cd{u9?@n z4~q>JQzV*{?=cw4OKcjel-84+A=L!?%XrMFbFDf`+$!Zw+RZXMFv z)fW2+J+Q0jq^aJuJwQ#`u=a({`3B){x3ZQk_a`mveETUwUP@G#hwlXCT2~C<*!B#V z${VRu5AG&TNJK^6=v7?YZc?s_xk{e7GZwV1*)9^bDPY%8D(j0pcC2e?-4$8$nwK3) zoaOS~^|&=1n~l+_4aNXRY_QsCKnL!OHyD;AUze1PQABU6gv4p=0YGL@C zie}&)*(zr#Y}ogUqZZ3c_Xz2qG0iAK8uJFF5_(W)iq`J1{{XOmK&hg=_TMbe#iJ8D zrM1;dpO}8)vYvcYjjMEO;Sx2f0BYeG)VPJICBu@NBxO;EF4^15_;2w=2`7rO_nRRX z<7Ln~tqmH5Rjm7->$^5K?Q;8ak$jtLnQc@Tq`J3M(|TPBN(#xKG#IkWXvpTPKvipO zrNzV)dxKg-V5%sbWiW$@~PAn;1XfO-AmQD$}? z;GFFmxTOhZfa}x(k3&jevSk{cMYm-<(Bq8mk}lAgnRD{Pi%JCwsn@MY!cNJzrnFCB z+mG`oO0xFfOZKaSHm7Fqq{VhiAFds2HQRu$Q zGD4bfmaVan=&VPcw@RdiY1BdhS77hNXzpqLnx7qw@Rc`t2hqKUXKg*fxux^1i3YtZDicAFZ?)j_;#d$Qa)56g(@1qe|}%_wsn#}?e}iDk0SHpWGhWB~>|acV3Za}K{QQrrP8G|~#xqh8=JOFT5U zGc}DIN(|QOwZ~aLSGZ_2U!5oBb?SO&t3Ov9)5?*gDD8(I{4bB;pY;|SU{rYE1Jlh` zw~ceE>Di8mhZt=w1S5zVu6DxQe8Yt9{r6Yf{vWMzHeo52{c}2%`c5)Czl^S#w-t;c zde(HNvIbyKG5wqaCJs;Wmrj{=54P@eGC zT(^}E^(r4x#I?DrYO6O5q?GYpFZ40UYS~=9!uz;NEHf7B&8qaF(8ak{3ajiMU{}$N zS8ZH-%qy7txrp;%o+@fuTs>o7+PU2Ts1;lv zYhvlhW1K=|?mKtkbDxOK&*xc=sxu==Kwq>1aILhRbW!;$Qv{tL)gUvyc4QF7J67Ltv`d<(0V1q<XYUnZk7sQ$ z!Z!U$@way8r#l+i`DAU_%0&X5a7G%3w^*RWTko?3>PoRew)VJZNDUsv#=d8TZs0#t z(%;*&n7D1I_XwA(1I?{2HS%)a`Gqm5yn;|dkSWA9qLvye(1GQlDIO(~ zXgZwm%MEvovW&LeQN95^+;~jJM(956#K?S9PrUdKleR1eFL&4tCfd**YpqIc+GGei zKn9-NP2%?~7g82Ch*hla#J$JI*6I=HQA}7ycZcVY!pCCGD~kYiRYo?KaVWy=6BtrU zQV{!Hb)bttnHAGy_to2WdFX z_r^vxF|Dqot)sN~NoRbIr@9(fD!A#tGt~abX+991LW+U^0QnD=KTb4^^S|~Z{{ZR! zrIqa~WBt(xIsmY~bBO$+-v(ox<(xJ?!NaoQb@lm7r~9<}oO^IHKMguBJRtg0!uHe&ZY z#}ezJEF{xg-@JW^1mj`2xS(9D?1Q1;lKE$0ZM~ytiz+0!E!}3Q`7qrmTW&OhLY4>s ziW*`!&NWvJrInWD)Ns*2Q7$rLsV*?`*>n&>!9S}8>!Q5eTD9VoQbmh+u=kYJ$cXON ziIxX|pXmO zQYt|RH*N9ayjW!h?KMC8D?rgF+HiPoLy$Viw?!RpJ;f>t-H!9^!gPWI4G>h0$`YFA zhfRM{aSfrOgKjbdg~cH%dB+Z=xN{_)^M}oDic$#rQ&H&et6G#2$UXQePvd$-bqGR; zO2X7S(*<&U9}1ujL}guBA9evbjZYkNaR%1qysoasSyO56L!Ex3i92Zk3m)GXpW}x# zCRO~YH>s!{e8m&)?88EJSL{0Lrd%D62M zd5FP5SgFX2!0*SN&=OX&M)LT(wwn2UEt!@xvKDL!# zluZ-kqK04ro+;Vgu?5pT;+W(2P0ljo%!`VY5$i*c;_k_NjqSN@x=px6FKaLWQ0XKR z2X+sT*|ARmjGY2554aZ=SBBxDt}4Rp56ouSwwH8sbMbtamvSglK1x~_qK2lva22V= z10mX$N$|OCm_N)D;x(2vJ4F1@c#&~`>0O6!xQI*xsZgZzUFNxU zw_H`}I#pBtE#7e_aw`{j_UncI>vNR!nQgkD>+iU*R;3jV+UJc2J7f)~mQPDSS!UA6 zOZb-Qt&Lr$BM+cG8v?nvtq;neHSP z5da!=P1&Be_>qX=4%_Z$LCpCG6w{w-h8JeVxN#<-Gnc!TaWDc7i<@4kd9Q49R`+Lc zZ0HkU+o0Rqi13!$P_@3>&MieDC0-gRXN_BJVqIfx((Wso`-8co%49s&0s=|lrP^6) z(I)U##DrX9I){K!Q%yGNR8NQ44iL|=?}@;8CAMIJmn@Ooh+S%Le{KEqAlf#y-4fpo zxK|-{&yKsy0vm=I5 z6Oxvn6%;bXC*+;dR;qGrbHxZxUHLV0_I1`QR}iUgFg&yal8B+}hIpG-dB@bB=kJ*N zdR1)qCc}qejS6kKz#NR0+geRh4vjS=_F)Ne1j0>~dQkx}i#LuT~gW}{8p_TYfb2AovHq13M=j;9A@ zi^@)X+#pt}lX{n(^IMP8edUQoX@S1&#n$QoR=Dp0U#A*A)ryvKgT{*z?nTLVLfPYo zSM67_ZI0132%Z#>yQD}QTNG^FK-oiHldHNNC^`0D(SgySDE&iNQ(voNmH8C5|k#DP*DS? zUbuEI+ie&QsZnpheAK1QpsJ5HFBach=ZTx*^vrW$e$vA*6sIVuF1Wg#Aax1Ro&$6D z9yi&DW35!rZoe-wGb-!LiWWP7S5juc*}I|Twwo!5H7QATXbMURr8N)$>)A|a7JJI- zD$uRpJ=$DM%pT|nJEsEJBwM4$g%S27M@bC3tf>kLH6+&)RvpH;X`}co>nGUke69W- zg+W7L%i33ko1})&%PRYn1rntL+u<0vjol5*Yc4&gy0DhDs7xF6s^;%1TXz&V<@;k6 zNifWUKmtRLg(WmO^aSG5X`F-P!)vWo3n8&D&PCFO=}IK~#M$k_F|QT{q&d~*sHJ-k zg1CEalD0ewhRAKqn{?63f;)pn3l-InAX(Jd&q2)UkVgR8J>Vs59GjG zxb+aAqtD&7y&>8r`+MVh$lOr(Dz_x0Um(du#xEsxeKML*aSm!7R1_f~N7ZfOoa`oOA{qyb#Ju)U?6a4`gUL>ZByG;^pzg@fiE_YYO0E#vkb#1tinXxxw4c=aD%7? z9Ve$;K+4{iEjJlITmcY}wS*-pIS+0Uu8GTFqpFKTa*`TRNFuZ+yA7WDsUZLXSuMRJ zNVhTwH9(>FU_n)jsOaSCwaSqSN*<2#qqxp(JcFf4Ak+{>V2@@Om8~L#RYj2C>b9X- z8!szvslpR`hjB{Wh=7eSRWbJ;ET**~B}AO@7W{NHt3?oe{lm3$iK4kap8Zm9iw5zt zZKq|S-Jx}Pn6luyki9-@nsuQ{@g$91hKCfb*|ws51;dD1Cd1#C7T1l4pLNEIsr-Y+ zGH#I!rM|qS5Gox6mC)cXi}4q zrg?*0Be?HDZ31)K{7Gw%R}^1)USileX>;RIhn#J-zSsa&N*YzuzM0}i!qw8)g;34p z*=L$6L6x_d-%KLuVwi|`r6lV@z85n44kB%GF0*kD9_q5>FmYuV9|@z%hGxs;4U+S* z?{--Mq?!lx6|__AQYsEE7SdQH%iic5?iIlbuE>xpnE|hF^8U8UNuRUxtfEj!{omRnvkb@!ETT zbZ=Yi9k;WrGWR|D;Gl@Mm@GLWLx#PGXcIx&dJf!Xd*8TNybZaU?pDUp;Q~2Oep#NaH=JhWOWF) z8Eu>-Rd3r{@@e%J_g3fx~M6tc3t*I6KzO}UE_7w;}*pWMcC?cpmYMH{g{rn z-20yC-_4WjR(mU37rKqM0D3@4oOgfXvASDlXI`z!pByPtWwaY*NNpp=uQ5SB5nS`d z&u;A9-ITmz8|FHsc&~0;z9SuA_sm$z4?^I7txjUMa}THu^LVpJMBTe+mgT<3a7j{x zmf?`XpQ$FHYDv;T=qr!Z59N^3``CMC$vF3FS9SKRVH(#EA~(w92u7VhW;tT|LS9$XeYsF z$bW)w=f(Ge4QXbo{`lAcX6B^R^yA)3{1N)&=1@N{#cA`65Ic3J_T%0|{1FVS+~&=tWl5ReEpJ;SpUgAglQbG#Pba38&o2$BWGd*(bu z1Jl`odj%3<#;8<2D?{6W5hcWwMv_(0J-wJs92Bb&%4@Gm8V{oa&xj`^^sxGu%Krf7 z7jiPaT<_!Yc@5i}9n?%}{=9Z)Z(U4SUoiW#EpyTz(}__J9ZYIQ_T#uqXF!#7fRf-f zJ}D{eIDeTx_OJCQ&8yQcLLh$dUre#KMX1D&sgUB*gwlga=}dCH&Fh-tJuB!fzTSYw z4LKp$Kw;T#d6H0tr9n#p14ttyLUGa=Xrnf)YlU$-8Z>ghW&YTyK}zIeaTYfEm@T~>!g$H9RSE>h_9FcQEnEv*0j|j zHsLg>_^?ksNqo3VEPk9QUqsqwQ9A8Tyq8*2J{o6Q=aKed-L26DLuBmPBPc@e2juNNG0-Fu=)E44XOlAGMVRl4UlD~I?T4GP6C7(@JyKF8L zZLVz`TI1hN6}sY%pp_2C`cFJ1eJv-a}HHx$9>QtX&;7Cf{~SxtOKopD?eD?^~F zYHn!pp>6xjls-smGvuXRKeAVLA7XX6YK+ddmQVF6zv+f6^0Lja zBNMx@((HNys`l>KyBnvaN!P0l>1>25YNVB9y%`V%4jV&w&XoH4!c(PDNRcl=DQo5* zq6dT$Mnm3kf0>WL1a_0OQl%IXZmuavDtT(?d`cf_IQPuQ;FS4!{7UDqtQhYzyST0+ z+hgGzv&{r{cnxiRh-xmebyJM?j(*=TPmY8iP+7j3p$t92Y z&h@UCwRJ66KgtnAO;Sw>KK!t+lciEDn^fhu&rYN!p5cVxXYm4gEywU(>MNffixqFa zdb#xm^YGCvqM6GGaR_xNQM(izmI%xynAG>p%#mW_=`^TZ$JyHmZg(xmTS3?MuD!o% zvpTRvsxs0=6blU+W$jKd-^DI|@m-MY4Tszw#2Ty{b(l^$_#91IPJb-VcV~wVtTLA- zcKgSQjnCjL8)YF39|`2XQ8_l{_O_%2^A$qzO)6es7MDadwHEOwS9)V66l z=sv~rj{X))dnjqAzVEcA^nK;GQxS4S!6Id{;PM)>y!}bxyf<_mvu{n7&=Q9bofJg|flp=v zL((XsaTwVyWa@IQv#!oM>fUwI>&vOtTIPH48F48KW5nUZ0%=?wktT(I4qcsY)~n1G^H1i$1b|M%t(T-n6k~f#M(E9ss`RPec45bjrAQ3`)P3 zy)HBfb;(S!*mSs6pM^B3?!%zkDx`y2B8^=}s-&Fy^~(!%Chm-tCPlw(^gxFl>%sY^ zk^`&q+$Ozn+DPh|m|T_`-4-U}ITbsJ#@KMx#D>-a{f8_R*b~ipy+LlhDx^jf-b6f5HzVRl55#tW)^D-H_Ma+Njpim37K<~ zRUDSn1w{$>cHoos9swQ*=ygY2b6~$EwwT@X+F@BgBwLkMlAuW?I;yX5!&*tmDNzFt zH5TqjmdLk>F|5~h-)&53`AeGFTLr*VT-$JxC{7O0Xr(RK0&VCy{oX^ixN_H3JPEbE zm8RVpW1)Cu^bvq1STLW=D!}@3+x9D-*t<)**`r(}8G2iJ_FHvHNNi*^gFtZV)q;kr zpZn3CySKk@bM*h;idH8;`a#y9^WQc~m&9CGcK3YRxISeUFbPWg~ zIg*vEQ6`;m4czueicIXxfYbym4^3Rna7>1iXG=eW$q*iiCVW=>iAqWwOCT-U1t>a_ z0P9>PH$9s;B_Ff$Cqwq4JIm9bDQum$eBAqn)fy$YZmlT2X_+0@7;!-=2q;2{)Kajj zcV~xI`@QSzFQ&R05Si{b~hGDTbFhOpli1&@>5q zOb!Xsw0Vvi{+yQHtM;DE*_@WEM#J7RH@%DSyKCvZ$c|$;T#&}KHc+Ibie-@*1D<$W zW#q1Itor9*V}T-r!7|^ti(5Q_jt3VqfYkWvqHUIRYz{_uNo}?Ty+!Ke51gdu_2Ne( zPcR=8(-04mIsz@v#m!hAz2cmT(iVI-H5GtsqA)gLMT)lzIp3; zTK6^5)da;$7T45)+h1+2X7X+w)#NE{#c9@%2uhBiXcJTKt};!}vJA5S z01txbZH?TRt!>Yn#Y#}UaVqb1*4*1=irkY>l|?~71KLM!D}U|$%~uHfxdWe3K%s9^ zUZUgjX4jWwb8^F+%1{h=@tbY5t$M*A28XjUtXcNP*pk+Mqrp=*_V$AzXk%%qDh2dr znPb#G%xVE2Fk85mfixfU7dvra-^c#|Z9hV$_Kp+y%Ru8oy9K(eTZUM8&4cqxP^bw~ zYIF8e7+yMdR4%B_Sj_s>G`5c1x?68*l$$c(l{TcU&wsZM@|S{0=%6q-X<(SXifU%( zbYu-t-CB|;72-In`9hUfzNu)r{{ZV_lD8eZXFs*$NSI@8(DXZlEC)zxGMZMh;*;=# zJ-FICduwa)I+a|*-YlK`j%;nHWkpxTz49zsySshBg!q>p3g#vwpwub0ol*h&Fq5C= z%OD<=v|2gPS-|IMUYh+Xja?g4Q{^^#rAEJAFuaK^kcHEh4-SYFoTX`}ZW##^?u2>~ z@j3o36l1EI&Nw125_9Q8hQ&BwUO_o$g9nx8C$)-?i4jTu)daWiK&~aNO>|Z4hr363 zD@ni-U6mVkTNrswTp>pjtvXEzdMWcNJ=7S{_LPM6H<;C8ea?ZbbaEernjcYLV7p&> zLcZ)>m%L&GINs&7+ioqmMM8|IZM8)cLT3;up?5@iup|Fla-HZ(Z+5pCNK3 zNTS!Ckess1vl6gEsu>K8O-a=@P0=q(h{6}~yvlX<4NeU3H*e{M#+(66v2qEo7|un* z?On8Ii=Al@+=;k@?7GuM1f&6*WB5t0Tyo9MYxstvn)+8|vIvERuH+JQ-Z8qZ^%Px@ zwDunodf@HcF^(ZQcFDM{Ly~;19lcC%NS@lmVCYCu&b@H$n~oalG*Q=E4(Y>3YO0s1 z&IjImb74&6wqTpIJ+x4E1G-L+6;si z#`cB5x}Q=lz;?b|=S`G}+b+tB;`Yc8E-Et^VdUsd5J-J#B`HBGQk2|PK81xO%y%(T<-fkKTtdN4bEC1 zKe%?PMX0QRmt-+KJmYEhgdJY&O83_ZE9_(G)HD1P&*b}a!wy<;CXSE_fAd%E9@W+^ zZQx4}G(uoMAlq-2;-hW5AsU4i2BbJLs!0I%j9`05cu$OWnu5Czxt*@G&%`>kc;HnI z+&+f6t`pj=)|i*ed97ZHIg+{#gtq?xmaM_(D~+DVyLOWbKSH@jHTiDKTl>+`%~iK} zv39mT)u!hi_Fmh++|-w&xWP$m>C@u?WlUZ1*&A6J92_-S7ISZ9B6mZhce|$q$BnwK zcAJzdm9@6S02V;$zq@ORdO2Mb3y3uUgNYllZ9GOdI8dm}WH~EI#LQ`^^dq~km$&Bk zb_`3?t;2^6TBFB`ls1&B_=!acCa10;>@ZeV%%ezC`CZGHyqG#bMLYsjzjGSc*$r4M zd~x{2_{DZD&v?mw=^kTllkg!4rd8=&HL%6oLpgU?3d5D%7xy-TCdA@=XsT*#vl(Pb zl^yWogaF2cB|@22g*C;&*Y@N0g3QL>y*yaz`jwpFKTEv1%hz~~T5J+5)`yf5{^z(d z3@%-**5xr zm@d~eI`#8#6eSMIlG8~QT2ZK^0BG6=x3;+Fwq|9>~ycEd68-B^f;_gv6E zsxO#-jZbBjb;CX;NJ#aERDX)--JkOu^`XCSGU4n^pKILPfuub->x1v+?aNAIw~RT%z#rTC(xaZQe=*slmbj*K zYF|YSg?4X0XPT_B7yYZYKdJSP*sTxW`{;^!TK@pbfAb;7sUOVRXi8)8#m%8gk~Uk- zlkEG$Obd3_*kP%Y>;C`*-0yw%f72iIpZv%__UZYiyq!&Ph2cRxNZPTHQP(|WVIhXv z!2bX<^!6eh(Z8H@i2VS6=SI=a{{Wh*5EuT*F3;uR3)>KnPT+@u!{5QSsUOV!J&6AR z+?~L0`bX;j0M5DT>Q|F_%aFNF9mmwx-E(O2G+R_ymg`gYAjeYMmwwa$8X;6fF$8j@k@cW^GnC z<@FjUuaqxx_Z{{unXd<%i4_b-N=ee#3e>6&%AUB4mKCV&Yf&6JDFoO`DtUk=yr`&!r^S6ZI=8Q(U%HjRTRmLw3Mj| z?jYbhem&&7uE?Ja@5AEm8&p@Daz@*@v**H+lq*qfU=W}c(CH%#uHf?< zO3^7CQHNKitEqff%iK*O!3URJSvg0<9a5lx}x&^LAzi-iGx?h7^3&0)Kg653?4SY|BEaO9(Ei zj9+n161P-Vw=)VRq>+g>Hh~Ff2|7ZSk)^)@J-+XL4!qtevoS6$KK(D0b(MQ5~~UDO>I{Es|Q13DO=#d_Z*_#=V#zl{3h~$y@IQ zV|_4i4(s(}&igxuw*A4IJ==ILYpz~oHm6vJ78F%WdF3{hAg@SB0U(N!GZ=y0o31Q9 znYfTuqmq5IcE!e4>{>u*<62kr9LP(atbH=_SEQ~~xN#>Z!$XpyG$n1F%Ne&3ZoOMw zR+LDLTU2m`d+Gm3xA`Huodmb{fz059Ge{JQy{(38&chSJ*zs+);YI;pZ zwDofOkz6Q>vs(A#$x$M+oE3aj8%E%OyxT;Eoj|PsQrq&+`_Y9VeH>Of+$?ugfx&c= zAEAzN-~OMI>IZ9XJ+9W?F(lkw+Hz;gYUFM4Qnf8*#I&YR?HeRCqC1L#>6YL3e%jk9 z@=l4>tvb1TKgDWWQ?VBlVFpM{%|JaI{*zrjT|SEQT${K|ZgQHI1zT|eO{mmVECmdY zu;W;Kl~MMQffz_~k_t6@KSmL2sGN=ozvcGVr%W!Dmx8c7H$PxoU~ZM~D}=~@I~B%l zj6!HQr=K*@<`sM?3Ih4iD@s*PCdv-qyAqT z9{Q+lyK`3gyOc^P@#rdXk~MYcnqq@*JBHwUEAI4NhZ^z% zIPZ2w+^^*0IHt1kE>1Re7$Y` z!D5mjOWV5sL(gpwWa2!v1qT5KBTksETWtth!q+Qh=W%9y!R;IrtHPeBH?JaZHa4F0 zi3Qtqi1J+d$ng-;(zGWnRH!37LT(+o91&JBRo`Q8X)mseC1|A;nSS$QhSY{F(39jk z2y;^{tJ+A_TvM3mk~KOlLf}HsXwaZL#=+z3+@~Lx3PB4bsV%F^8JhGMlV#kk8Wl{F z4QnB*nxP&}*!xDyXl31@3HXu=l$@uXE`>Kr*PTM0vb2^4(SZ`B2}>(Qok_x zTR9qHK3fYqP_!vHFY<(f*PRkx;pED? zb7f*WysCGlc_eLurM5-14rrNK_Ws3Q~W&As`AiX;IK(ZrclQ2A~a8Z1Y}TfXQA+N?cv) zzTtSjMA}!2ysK>4=gG9aA?Dj@=U0+jE662EIVQbMRnH5U?T&;fjJDm#K_65OBn5Nr zyPtbnE-HbzthbwO!3Mruh*J@e{oqO{ucHm+v$JsI6*AvDl#TxIb$*pC?QUjl-|Y5{ z-?-%4tkNE7xl$I)m6n-Ovr$M*Xa*uOI93SBhMh|7xA%>~xNB|$gDc=w_+I+-(5Q_> zf0SUYASEer<*5XdkjqbQGc$_H8quqL{^;BBkRJLI;*HAYJ_8N65j>$~j@)#Fv{fZ3 z$si8#o)B&aBzY-eyfCb7Dv_Gy?@yYX6M1gLRBPoZOGQm9O!ZR>jk%OO8k(K?fN9nA zP3arMg!g;tjT4w`4W*@}NeH;cOPO^ZB~pjC2+NGgMkrXCN4YT5$zL-?TK#0FXGPf@ zRk^kv{j}aE@$UB9mp#F0k8_B(WBz^IFx+w; z;MmsHvg=36mfW`zSEhqfK}kKh!NfH^K(4lR#XZ+Gb4S%a{s zFCnQ3R)2e;NYo8F9I??oyxTU{R+W%-RdJtp_pSAoN2V7R(_Iv^z#gtP<;~bk^5)@h zZCbXH<03rurRPh`l#mGxnw=#e99FlFWm;d=Y~CwD?Z@WAXl?wf!$96^rLcW#`e)oO zM@@r~pia~RO3+k*kdgbvNlh`5U3P0G-`t|vt$#GQYlHmC)k~YN)E5=(EvdA(CI0|; z#*4T2$Xd45-K8o+5)h2kQl`R)9^6;0`&p2hLe{rdtGm1Pg|>~ad-QT<#oxRY%2L`@ zmz6g7xg48fMCRi!PDZ3Xr#u#IkC?R6%OokQF755R;a@ei^@QuaaqWUFisQMO-1dp~ zt8;5yxlBoE=9@}>B~`L$2U2kw+YE@1FtmcCrrdZ}L|z`!(dt!lUhL!0>Y8_zcD!^m z;%p{iQ--IZ8hd-OY=>uNNUK;?9_zdtP8}qbwfYom%{!}W@BSC$t&hjsf*#=Aml%kR z7R?wWszFnUDk)Qx12Nx?AGbIhmOmodr)dof0q@6gHuiUK%WY)v=^O|Pl5cuL^-$DB zTv5Vzxfbw}q{(bUaFq9iw)t1tj;)V-8dwG@qpVr^nn*RXxcl8(Kt>-vT za_C1=Esr>A4KhkK6Z8x?JIfu({{U)Y?%(Ab{+)i2`&4y&+WWU?&P}^_ZHtc7xfF*5 zy5$B;7t3VRP;N>PNUl`^n3%H2!*b53+z3!txY@0r(<@6)rC%N;;r{?aJI96FxN^^` zS=?F8(u?Z+AjCtkVm8{yX&z6@FU<=`1gp&i*D7Nn%LphJw^4aIHF0DNdMlBpRFr)y#1> zsGbTRrb%QBj8v7;zBKev>JlPs+otzogb!c;VDt*oK%6W$N;#{wi z)MO|pqED6y$b0du?TCS~0*jNok9j1Baz+bieVW zryO+IBZp0Bl7xZ#Fy`9U^fsMIE9;Cyoz&(9z?XNXHZCvDmHP%$LB}fX$5mT4510|bylTs3YI`N)aJiwi;Q?9V(d$Ccw` z?n{SWswc^Nn|CZJV25mpj|_qbheILG&++kv?YT< zs@xkEv|VBJC(P3LCu!c~Y`u|dzV7YcAUkl~R}$fRJa$v%qB~wuNlSSZ5D2L?IQe_| zw(X_v;K=~_2#LB;eMJ1J=4>&zx&{Lw)RlX#KDRlw)F1D5dlFUg81Xff`-|&dIQt*@ zNV9yEH6?X*{{VGig_Y4p_rF@am{8!lb1b+lD0tY4>k!R5fsTZ$?Z%~s{GD66oh+)) zGQXHSY$mU!iUr+1upm0~#_7H;yOe-x(&ov2r1nwOk&TN5`7gI56Br7Em;B>cTQG(I zXrN8O>f&4Ib&a{VyoVMJnNnOXSFm_fOmuG1{HNkAoE-KF`j7G?YB1RU0C}*qk;6sv*S&w34Z)T7A2WbF zKq)P?uBuy$)T;bFJ-FSYh;SM%OP*&)YEhJ|C;?piu=+IAu@052_-Ztsz4%cilv*SK zK}z=E7($IUL| zdJ5j0O}1@ldg-`P4*M)4?j4Tm*578GKu{6Tq2> zTV0Pj6nRmVt~;YsOXw60GtV5?I4pe+74&l%D`bQg@KOH&D@unGX5U_$sInMxs7TbM zcxgZfekQi-D{>V#xP`69y1onNOVvr+&&jsGw|3+92(2Y)ocxB|Xu9&V8hpSqG&t*h zt9B%gm8lEnmiNZFvsKM49+WSlD6U+2b84``)cb06;q3C8S;c#~_f)bWXdQ&d7iL>d zOS$jCzLhCRkg|{Xx{*`t$0@dG`LCl-hI(j_C0eiUE{fHiPHU|`j6N4O;YS-wq>#?u zxDtHU7PlN#95oaP_F&&R3Xf(Y;S_UiUA)JFBheM`9`S+sxKe+F&J9x4=F2J8ZR}d& zDFG=+np9{Br49)N%wUprQg@iyUI$7bt*2u$hnlLGeq>An>!7rx^)*WY=SH9#n-nE4!+T2Uzk8+#8C4fdq@fY0O=|+ExZfIG1fe% z4Yif6Jw7s5T6=3#h&|~iUHiRjNbPhUP7*QbRvA**ZAzn*SG%q&!XO1z=*||R;LmH6 zJ=DT}P(=BaxVbbsJ(qV(d&&r+;)rBiP)YG>9f!9b@`5Ag=9Ck5Z6E@nQ_}=|B8kf` zwMx2mmq`i2`Op0`y+tFlhyLXk{K0)r!+ntvl2uO)Dtm@Q z9G!jq!|Pv2?SGWtQZSiq6s!U=rEwbbK$l#!^;*Bs4Ja%;=0$-YGx#HrvXsx?-*64sScVYrWaCyPMC8U(KVTh zU)Xka+~hh^kQ!+m9g6xYO}Y%7h*`)Peq?v@AIe%UoA`xz`c9&JGP zrAY*0Zot*9Zz3qOJ=Y_d(p$u9^o>ILyXU@7Yn8neao+k85aj*+Y_lqJAeyFI%V{ql zf=vimHP0Ew(Zxw`k$EG9RqMF0!xF3q2TD-h*Y)zw&wya+%4XKzhl6~Q%jAt3^ zgC*0CrEMEd-^s^DJpn68arCO8_L~Q#QVtk}Dds5ik!3#K&D@d$=yS`3B!G|;{;2yg zH-nG<<-WCB-E$FO{{T|2UXv8GaL&_Ozt=SKss1y{ID5Q*^`AnD?TnSjHCaW18}kNQ z0W|mG0x1>Nc7lqBbS5N>LI~~+ljCw9$dd+zY+t_yv z`zr4ub5Gl?{$HNL5p!7%EI3Dkxn+%mcw?fQ9}tk!Man&;+!r>P+lR!?s<{0`u9*J- zw?0c!&;I~e4Z;cPk>sX_+*OQ=`)0%Xp>-ek?p4OWE>S(p&RoN1+qa#-I0Ev;n$r>K zHyeznqPE*Cw1TB5(aO*PUYL@@ZL0@dv^GFZRcm$cjJ$aH9AO|(t^yg&?g@8$qm6_3 zyqPkJKXiHSsp@;YSnC^0P{@Nv02j?0o2SWg=_{+8PqfuQo6l<7n!UzQB0MQXs&hKD zq=cTzXPyZpKst)=i_FYnJjztKZ%cKa+L%^!qZ^Ub14RN*v)W(0++*8VPKC%&3U-+x z9aN%yg9d@1xi!pWgbSqTzYj}`@*+MO`1O@>%ccpX%G%8q9iCPhH7%ej2nOWd4$ za>SOFi>okvh1%5zk-i{MCz{zpikIefAc51_+lTRcs;N8bXoPwkH7Pzr zs;_NnPMBBBO`Q57uEUO+{{Y5z9O+-99`g`+h~v@iZ7{)VadJzE1@?65N(t<#>x9t_ zQ5Pd5tHR<4J(_@~7i+4BRs%iFw6F4p421}Y>v3sH83jhCNYX;y1gKO-M7`mSQ&xQdFl(f3j>K5|VV~g>AOIyi*Aq#3b)`&CSiR zh0UNUv{*6!RF zOU!#>tqfcpQ;D{heBBzeBTxwk>cSS*y#vKBcW`7#2$-dK0!;@&mO~B;JXBP=Bi2`= zBI#{6I*2Ki9aBB5rSz$NKcien3DzLlchRpLHFav7js53HCb;&&!hgbyBW}-q^97IpJJgnF+%Pf zfBR`#e`k;W_|H*Y*}J|x!~vBnQ|`w!+q#B}w=O`@BgT@GHKxje8S5Rmd`^lmipfOl zW%Lb8Z6Nkxl~l=vrK_8Mw0SD+wzfAZHdh+cU`4JsWaK=>5<2_w4~)tdU66Pht-~{k664!W zEur=*`MH`cUoI)8NpV45`r|sywwDEKm?wu?wE7c!5eizU++(FmC!rm1<|xy7#Zg71 zm46L7j5n%s9KA|V4wyfdi&W~{P83`jBdMsTeMTfK0S-|4QT#Hdo*Lb4?M_s&xhRBN zr%3rbj-qt=Qhw-@?$i!0TWF7mhe;S18!FR^8rr7p@As zMUJQwV}{h=52mFK2GWw14xPA;pF4DHocTCVt40%LV@_T;qp5dLU59^V+ak1v*}Oj3 zWNB@>f&dwJIOmrIFSGn|ZGxNl+mo zqa%wZ)5phmdfg~$vX1R#=dr}e)Owpv3yQv%ePM2&O?-(bdD-^TH#Ls$8OXP7oVhEC z+S^ls#@2^IS4Fo>s*_M@j)UI&9vs!EjmM?KpB2Mhr0%We)V?yrI+%F2zMf02QNK4X z9NaKtF7?AZp5JlO1Eq(p54PS_B??rQi5~QWj4bv#^4;`~P)E|cMla2Eh2&jQ+mP?0 z_LW-wV0zr)A5pv`b>dyKxA&IKxm_k(q`!GwYh&@ zzQ$w)!`#;a$!Gn~?>&xx8S-plWqyT9_)C3ohY)W)o4+?6+}}IDaO}xTlEu2%{@;UZ zl1oZyDSa~(r;0|cL?vRR3gwnByT==SlVOdHYXF`=VZT~@YgL+d<2jAPT{dFJx$W@j zr`AI0TOXT_%8CjPr+v&@B$N?*vFZTl{A2Xv6_>HsAJuQy`xjMPercluo%Vqv<+*K~3ZfAp`oqcZuU zveOr7$kXj0NJ_BE=d>AS>f3;Qzp*K6`HaEH`Z~?S=x^$AR*!IVuL7^rJikVA2`lGb#*|R*T%5*e^6Qu!89VxCu8qJ-kf*~7tGy&`jow8CHrgVpk zi6_=gPujY#`7_(hzfgS1F~GhclfUMiw5CvOLf-W4YojTs#jnkRL|Ltb&lleTI#!!NTW5-eMzS--+oJ~VRq+R zT2L0J%~hG~>)nS2KS@z8d|di|q@eUmtC6GWh_@U<5#i4=lT+8f4Vp-=6!=RBAG`G< zEm`HsDs3=Shg7wxq?4wkwgP*45-{EUW06m--EQHjX!b9gPtGTeZ%*^}#JF6U`FG}R zR?`Alug4zaBg{efd5I(6j?U~&%Vov6ql-%UqxqP~w$pF`C|Ke97b~{T>X`x5*!K`D zy4QiJiyu~_`th@Zcooh!n5IZP$ho82s>1BH*Dl>pH3_bP`@rW>hSNnP~DSsBm_jd z0zh*w3Rm7e=PX7CD)6?R(ChH3`?_`=_h`%!u2GZ{v;E#;2BsZNX;iFb^b=eT+ zx4LYJ3FIr~krMAsGoi$QUpswEhIY+7_TGF*FfT7wtVG|-=H+1Ta zH3{beoCy20Q&PJDISTvmpcN^@rvW>5XB_WdN!+&0oyYrLcJkd9a-nP6_UO*Zl<`2+ zvJ%iVN=9l?qDN^MipgYc?Bg$)!L1z12qT*AQ5-EVdE^y=**Bk1K7qOSXYXr{%a_O8 zsj-UYLfco^t3wlEO<~B6w%~14b%#)*lBSoEfXh+`RrPQ*WB7#9Vd9ohSy~Bg(7hyus2x-zt#DSx5g9nDETgYiW)LAh zjzkJms=3mb@?D0iV&Y$#dF{asg%2^t8*o>*O33>2z<%BYH-sVS* zBT^(oZd8WZaU|&>O{HNf2ezQ$A!Pw~Sz}AQXey6n`P$Bgy}`Z!ntLlmks>-)q=lNR zu+y{X!{;)pWV)#)V?hBzl~5T|2!sg<#z6r}?f8Zl1s-pOOMH2`Z59h2?1L=`c3$9N zF`>A$0+y1cYEp?gg=Lj73w>z!xHuIa_pmgMWhWP6`LXp}8S<+|okS2j&R7E)jbvac zhiC77hheo`?YFzK+j8WUqwxk~t@R?C4tHXe`^K#&El-;Zdy-DBIXyNWK^g)ETm?qGPvWF%_w`3YLH z=rZeu_a*EVXsUf=W0l=5G*$~~@gERfT99V?irTQ9La80865frkF3&Xs6|20p>l`T= zqP5uIvCjm)T7I6jDsktec$~SsAZ$)JUR|=MyPaO+wKvYx0 zNUvOU9Fi_qEuu8k31wOm!qjP*p~42`sc)5cf}j00Msbc}c({hg`D7 zV&bHrjWQc-B&6xfDq;g&H)dvR!?UDxd)hpzp36 z&x>=8g+bZpTGo5OrRKRN*4(z9O|8VURe6o5BgIip+&4Iv_@|7|$JN1P?`RSwCFI)H zgjd0s&WGKEzYQarUOd2g;H+N|?|Vzr=b}txTdm?FtDiPPl9VNuhN4Yq1v-<68|++x z+8V2l7dXa(2waEtW9s%-BwlX2CynQ>2OZnpai=bf{nOW#p+zOaq|k6W ziY#F2;;UErp*zU~MMYzU1jw5*P>TvON}hRS1fc*_^~ANwY+|Vk2;U>9(NzfU{#9+y zHV)T9lGv(Ux~x&fgZv|~cVY(fAYGtW{8ru#_ne_ww+WNH&zq39i?#Dor4kih{`^Kd zXO(Pin|f(Y7j-`@zs;j_ai!Y9>DSb~)b7qVxha@jZ^_e%e&@I4WZMwuKItXsZe)ht zSB<4mD5)l?h{$6;WxngEl9F;cV)KHq zlvqd{O=wm_+zhq9;`Z)n1-(M6r$))d7qE%fO^s|TaVb(kffS`Wg@1tkxWr_=zhlh_ z0;{bo@wmC5Yn;(i0=F@>o)k;v8uyP*O;! zBNjcMo4sx0L)=z#+4lJTK{<>A<^2Mt`c{P}Yc|}%u4wiJ%}-U{o;XvCuJ-x7U0{uD+t+eikr@sx|7OfwL!W}YTb5_{pC+8I$sA!QeG|rsY#+p%Dr$*baUtA@?9kgkjUQJZ59iG7KctM zzeW7TxKewmRl{lCZK_3lN@^T|r1um!57A6Y&Dgu(J=p9T69Z2Wid)g}sV3#iySnqY zY+FKY-S_t1*F&h=)}^{u+%WpQ#{)q8H96uvxT|(Lts0c1*=vWiHN=n;MY+$(bHwtN z6`ct`w+H!Vcmz{+{{R(reIatDb;K)6%6rp#UpKDEwlTc! zTX}_Ti)x&R#K%}=PNlySge6HzwNtmQD;9XGVAm4Rbykf2e)00Kb&;ncn_G@=IPCOI zm2BL%xzVg~nwj(QCpfX8S=1JmXh9t?2GYp!U`+o26391J$ZP9R7pgm#Cf)BF9$zY4 z=Upx{EUd}0Ot>F0^Abvng*w2@1yZ=O?IVYT9Qo@r!ps|>ug+>vgY$Z{K@nkmp zZ-rCN9eu)>uU|HfTRy^{t{N!iPH87RyD=D8w07}Y$6w!|(z=MQ5lhYRsqbeJ@Qn#4 zLZvv{Vaik|%ur6EJ(aE&YxJ*zs$@k1>DIvX>&IJfaWfX=^V`B~V3kNjkUHL0`|TW2or%Z~-EKR^(IVm^z@Gcfu@OoEv@E>5lUh?Q z>^;fhLt|`>8qqh}+rP}3NMv;dRMEJ3Cey}SSXx}Ww>IqJD+UW?D_WEVLYJiYQaz^? zoL<$M?&GP$KvsphJBeqBu`#v4yoEuZ+z;JvcMI*w@0*!DYFq9rwM6Cg3~jLETNGiV zlH+TP0Q#sr$s$ZG+p2>ulb|qg-U| z<)LA!-$l_MaNQZdv7ub4suT@klXwGCv>Ec2)kCeQ65>ZvDcB7$>F;`5s#ez#=01ztw_)z8 zGjxY!ctLpxM0Ft~(J2(6r$dI;S!f8QRGt3XyI|@9p+vWt`-a6Ge>D7?aZOUyRO@~r z*fYfZGsNIXDn1)|!@wY}FLPbxSH4o+^PP0O^YVy!(rQUU0yFmIj(6P?R_yxM)xOc% z`8a6Dp+?DeqydpVzU)<=G+LttRL#d6fp*%;^5Z;wg&L^_w9C4f&i7WzhW`Lp4OW}8 zdyRF9(Wl){Ez8UshUz?~rI^hq9Fk2>XIw>LyLD*+;HGn(;JmvZ5eg@nbC)qiPQIrW zuZhJ}ll9{J1{w&(09jT3=eU&B&M2#3=>w@an2suNRmpCN8kD&an$yElJ25q95G7Qy z6Be|UDq5jAZ-&ryIt-v31H2xT#-oY1>19_w=P$(58f&Ur3O~Kq69GdxfqJ>5gM|_( z`wJ$$pjo0QNfaX(?{N2wR=>k= z;<^v9{ja#m$R&u44+Y;F4Z7iEuv=i=okFKZX(^MV;nV;WPqQ4AlAW^5Ufv7p#@@Dd zt*DLsKu`y%Ez5q}8yaoe#{p9%M^h?j6ly|BT&+ksQx;v7Y{~9APc@bIi#au=uTw^g z=YQ32J8s@$-7HGoZnody6lNv9GDB<%4JAYl+;u+J+woY%MjDd1A9n7^vlDeZQi@po zy>u^{ZR96XnTIJ!Ju(Ob6h7-dw#98fmm7LzDpz=&l;Ns{Q|zWWVWE8pG9soz)Jlk< zS6l~9sLz-!LeFTf6hef_S#`&kmgh*wihC)7C3=FzLCA`arw!5C>aJdXZ1+UQ^wkmt zY52C`w6~``#C57y{0wW|rjEBI-h=ioT>Q(LSul9GeW6W!F4Iv>oF;GI~D`9h}tb*n=BPQNmK%n!;G+FPd8 zY50sHXdbRT@`WYyO-501WSvP}M`mDtoGtRH7hR)mOl@X`UgdUum_gQ*OSRsV;9^^A zLe!kiI#!rPkx}71mgC$j<)hVtzGu|WD7a3tyPC~<(v753=*M2|i0NR@Qu(vKq^@dT z`Ifvr7)a%QC}^4id4a3wbQ}{tX=?H4Pc~ow0L-HGucj!nV>Zi3dcGct70i7YY+sR?ZVYa`6+4=)D#K!RAB6N3Qk5;QB6Ba z{F_C(dK#gTEC$-G0R!3FiFs{B00k&)&fRV0D2`ggVC3UGd zOd#^qLCg*tXB+P6Dm2puPIae7RB>V3r>g0pQ+Lz)G0#4Q72-atH$upesGV(y6x<`;VOSPc0>c)5~A) z9I#~DrO92168y&!;;Pzy42A%4PU5dM%#T#l8k6cjh9QwvrhFiz{{W99QyW&!Bx9uY zE<^6G{{ZDy?eHt4&Lv81DFT2dn99VS`977>?uhv!z6k|KNGGi@^{?ikWQSBUQUhrY z6x1f9kE7dz!~Dkj(s!T8cc`jU`Exj#9-%q%YYOuau>Q687cUN;&vrB(=GD09$D{Tv zJFpQk`MZ}79eu7{uVG{_R+^-BCmixzS7?a=QIDzXl3@B}=Lv22L~XHlDkHM77M{<3 zEqfv!tcyKsKJL}aTes$LDh*tZ7Th?%UFHo1iHi3K4?+^tqBH)x3>|6X?CTKr4n<*=9HYf`|yF0 zSfFhXZLz6AI=c4Z1tA#?;-<})w^s@RRLC`BV z>FSh*I=}UdrKIzDwm}4y9^nsj=Fd zhn9m%3jLq8Cn5n!Ktri1Ab5pHsTIo-;ZhAquZqf|7R^+&sO8}q;ou53XZy+M#?!E? zYNK&4ep{KkI^xiFaYTVQ1R4cYCr5~8Fx1>XucSBWLT$%QdbRNV3T=525=d9yQ>6_s z`K+i-+$guxXCjVU^*BF!QKfK7BI>-DR(EUt%R&AslSB36%hfB&hX7>T6sKq^$ALgf zHvxBRRQ6}s)bwkU2m7n-c$HJ^AcOT_uXA~%ddrUlPW?Y|-fl83TaMuL+mo~leZZ8e zh!jX600B>S0kH$YK8``ar45qQNB1?6%c1k(LwzquWqvd$Al8Zpv%d}Q3#gw&;nh@A zbH`?8;JiwV*kOm{IJRS0(Rfhk~R30J6^``GG~8-OHAqLfck*ExO4f*7dq+l&9Wt zrvlUDDWaMqq48G??j6(?tJ?nFyALy-UDed9q+lCDdw*%eUob7S8l%kury4-Wl_x)^ z+k~8b(wW>EFs`Xe<=NLbk-lqTI8Jm`OlIm0v29Z*n%{q4CoZLmdbt?ZB8@hM03n%N=I(4pmpuTtBfoKl&VJGaZMQ~ zr;3dC&g#4F8`k99mp!<{N!*iTO1WDcN|OyLVaC+s5p@UEJGDZB^YC%GbBzAxL65O9*a_q<}zBPOhD}e`y;dWdW*e>D=Q| z6Ervz;ZV$B{)xX@Fo^^o;XRo3QPi=-J5JTed%0U?_q`!;{3CC;%x`jBDe~TYCOqUY zbuF=1PD487kS7p1{FJt_2Qj@ypin2cj9fQT7Rc_J0zvetc3%$I81&<`Ik~O#3Crc; z;Org6$4ZeIQ{roNNMY0^rRyouimy^dDh?~y?bCB6NZP(K1u7j)6^A3WEh5A+>5_Fk z(yj2X?BcRIHM%FA|b+s1?MH`rFRR z;^eglq{t~>rF*s;!|)v3H`MC#oJ~A^(mz^a`mE;(UZlDEkolfY-WyFj#pvHBU=N8a zjUE(6;kFjwN&=Rk*;1$}od-(d4`FQ|!ZFRZF6u4juD!wD{{W9=A9@?p*G)V>N|jyL zL6vGpW-h*3skQY%YY7>B7#}GY%v$UIpV^OjOWtBX1u>kG(q>3hc&a1Kc_`MTr?!JC z;k%nus4GckD{>YhsZKDGbSXdrMS9R+$zL&fryHQuMKi8FMei{fZAe&9=rX9nQX|JI z9S9B@5n6U(2Q?f`>0VnY0Gj4Ia1C`*zF=05(Mr;fQX31*P(o=vH7B&ebmD>BSpNX_ z=PgsS)3j$9>MOZd8F1t};8djQNjiK$ew=eGHjnD6?fblpLrR3wk8_0*>dKsul~mCr z5PId=i8n&xI4C&s(K0|PJ*rE$J|0t{3e?!`#5AofS4K?7w?eoP`Lyq}TkY&@*pn2A zdzM3=GZiYK`FN>Pj-WdVigCH@$Xy+Yapt)%e1R@-G-?~K*1kXGDdwljak5)lUUN#l zp+r|1WYrUry5%OSWc}vJg>R>J`@sHMBtIRT_zIc58(V^*vN@=HAqkrl}e3+Zd@vpR-Ab zL2zmvl)}%9w4|cHWB4T-BSkZ#keymqG9LbzTE;rPG|9jCWg5HrvF@)tDxC||kKz=} zQw}bJnJ$UFk4xdA`a}GhePOMcH?9}lEv{qxiGPV$UvQF@N{q@QNJ{E+)Y1^4PSA0! z;VxT!ZAa+f7b$lmY&#WvCX5b;AGLi$?){0rTxQz4ShFEhm}z0%Pp`qG5!4V4efY1% zl7+7z*Fu}*cHHws9NwKmfwu=7@7_4v5INrS0@-wHQaZpvY7=GY#d zy;PmgeXpM1FhY7#L|@D*+}5Pc0bp(T_cEy|H<*KPPS3{{R$>>mGa4?h+D&x`H(nwGdQ}>d^P&03$%$zbeFc zHqOd$@Cl;m-Ran@mS)A#Il;9awZvafK8Gw@v(^^w^w-OG=?>d?`W@8XZZ3TCxCF>pH zVo;S4=U?s}g1P8Wt`L{JS$A+F>Qa`!mL0he(WLz+_N{;W8vc}Mf;OeaVPdI2j$CQe z{!ZS-m+X`)T9-vvju0x9uBq>^B)wW-{xmTcPHbR-H_xK?KuQbgnIZ z!p$=kc-f<1G7-g4J2$g`AH_c2XShbwz-j4MV18K~K(kNg?ZV1`i_U~|_ZChm-|tob z0No1l{{Y&?C+|53$K`Fq?mUIL$a|EspY0jfZ}9`|G07NN-R`~np$k7MSf>j0xe-5= zKO8)_3|>^+EF_--3s%=VJIg~aW))|AF2L1+B7Z4Z=X^3d>ivq6`ndF5&!;E&xY>C% z`@%Ug@b#gibd3@-FiTKZH_TGw6oLqBiV@U|@vH1U#`!rxGwgCd1~@)?=7=&Wh6B1DVlRuUrENpYcX>JsYTCMw!xAX zja7>DgRuQcZ++js&!%18t)qOjTVbbrToG~>mAPz{DPg9AzEg!&aitn2gmtbc`|~Y| zf+qayI_h-wtN#FLcAnzMTu5nrwwfS9t#YP_u&A0hP zJ%J|9)bB!l>D~VTQAB_0b?dIR{{U;5cYiwHzo~&6rvN|F9>9!yF}zRz0JwMkPF^04 z{cjRH-dYOo2YPM_f4srJz3?^uNcIFD?6&d$0QiUh0MzA8{YC6PP~g4Ow5=S|xKn$X zykp5#j^Hk{ZwO#Ub+j!7+2Abzsnn1FrZzp1p0c}um>L*801vTppLn}_m$11XGV>$o zc({}4rB!F$dp(=mZb^tV%w%c=Wvdsr>ayGh;dCdV>4|B(OSUZAZXrr-j|*D9B>OV# z!*ca>yS9#2^|2VeZTo%gu_2|1^2TY9kTdP^3^9}h;{~6R+|k?=a^Spe#~0?INkx1v zis>{xaK-SDwW}NJYqn`ZrCSFtKHL*z+;NkCvAzObfc;5FW~P_bD#H@w&i8AI^~c31 zOxjz2aK}{5jdb&ZMz?nKq7x~z)`%qd#tbAaO<4=$G;(7(W}6vhG9#&{0iw{Mq}Sbn z`JgzbJr3|s-I>K4hrcGG9?aQyUDq+y=xLjD2{PzTKq*Y5r9Vj+hRAMvJ8@Ya0QIL< zyMFfN!LYH;C%}Hb3F*yz8O)xXZX0`U@~+O2uGu(saxb}Q0ax4#x! zk{)T*GTVx252eJNbfr;6c4HCmR?W!eZ5l0g=`|zA_N}w>{{X&mxE!nGz7=c00MkH4 zeU-22qJMsKo~nI1dYQTKnBdLR6MkJbiMy+DZkR1O_FKCB-I60YbGXnvx9(#!s?rGWOS!i<*`bp{|G2 zrZ-%cGCnhEJ${cxKTksDxC#nTP@o+SH5k-T;Ib3fQndF9VF?j}d~8NuT+5OfD)Sp; zC@6v8`!M5}GJUGH}*WuD7xdh>U@zRXKase~23 zCQOGC%S|?s)k;!;`%YMEkjO`E0eg)AOWVgS#G#RbN$^sqHSo+{A>R}D=3eP*ac)h& z9vR*8Vz>R)718zs&%jE;)vZy<2~x7D>>V)+w{w3xY$oZTHK(h`=}t1oMi5z z@cTWg6n4gx{4xT;=xA|0=LHq`m^@UqvHdS|FDdP*yT^~McI%~<47EXdm#!_wmGdZV zE}Aszb4?aA)r#(0dD_}Z<;kMEV0KmZN+T>ZBiH^3Nq&pHQ90%$ak^e`9GgYX+o@jd zPNxvf-+1Sri|S7%?RIDngX{kQ1jB#R52z_KE;iU(4aY7vClcNI2hG7tkyhe$AS$ka zbqw++5?33Z&L)EAP#;2$vhDUpVnbO{-a4dtUY31HC9QeZ(Qr)e0>oKoQz8B<#htedhpFQ*c*>rOjiA8fYu_ zCd9p|yetJbS$lfxx5NbpQ*&G?u?s3sh%GMYK}j7!1Yx|_R&uwbmXMDfwe7hN4Rc7~ zXhP^uD_=L`zJR!6a$Wg5bM6?Mmh%4q;?bvHZj<0Gq`vr$mk}w~5(*0=PAFS{@>-S|5>wx2a(?{TS0}GFJHsfsmXyk$b~6wC+Q`&ALn_k@Z!BY& zP>mF}A8<=<^5z;8{a30!VR33pmxxZSNg+uPngrL}a>N(*`~cF5&o=wKrmi2S(vsEc zca>7VB};;^oRk$28G=3dMVJU{N>L^^-Pej!XVhN)Q3dE$*TCwZmdKu+io)Xq7bGp- za=(ob>~qHPdBUM=fUv63K0>HIop=+0Zz7K#?s4!=_}t;U#AhTX%WqO5g!#+zRx>$v zFAQCU9x75D&^#)V-t`r`N3rtR?tMJW&2H?*z=`u5jL$K03KWxHO0>+z*J1OO908Tyf)dCWg6zPj(u+9LAj! ztaa?NyH!ijKKLXJAe@hOJlJTg&3zPQ%G-iVY(ua#;*k`9^t~#eaMpHru-OCZR%v`wkNGx=1U} zw(}ZVcrKyvXDn{p=O5X%Zi#A_XhDZ5UVECA?s%q~lq^hEEMnrE!;XEhooc zkitVrQ$XFDhI&%Fjpjwc$iyld-f>QOf8y&VpGGDQHHEYSfl#8ok@9sol0uuA^?S~c z*=e)L6C5ohB_^$c0zgOy!=6L86)C?jyYyLU9kG z{DaB24bQWmlFX;9a(sn0sWF$WwUvg#$w|x&RQ6(X8MN$eoFs|8E=+UV<0Fud$(`lR z2Zo-t0P9HsYRbJ1c(SUiwFt{{N`4CaMi%o^S`?zx<60GG-GxF!qpo>P#keu%J6w{7 zPm-cf_^UW!kchf6lZI}WD=Bl^d*p5n!wTO8b7RGp`a;BplU9qQpn#==(wIgg?ux3F z{kiiA9MWr>Z+iJ|AvQ@45Gb!HXkLxLqvLEzu?tBFS|+QsRYb27Q#n*3KQlD8^JHQr4-K*jEG^ zmVV-BJ&Qo_oVye-LP}#G=_)thv8iv0^0Ek3E9@ep5!o18?P|0x-`{k})8e7V*QrVh zRz&599!qx-tOpW@Q%s3I5r)Wzg%r8ACatF1b+vusXi-k|=YzLes@_&QM!R!noZHTA zJMu^@Uf@D}xHGA8IyHSpxT)SnN~&F>!)C7~J6^Wcnrn8ajmZ%sytF09)TEH!wE$E_ zdH`_>+qq*0q;OX^<1MZp(DOieuAA_^htuZ*K{t5Ui#_jgUfM%bcOsU{@1-R*C~SC& z5>Hxn#vPm)JNfmf0Isvy`0cxewmk$A;jfCUd(VmXJk9%j+_qj$ZWV^)`lTY+(p0kF znypGtc92e8xX`hg`>3E1IhCTsv%3!-`^Q|zH9C!Gk!~DivZXlf&E4-V=AZ&X63sFl z#dwyFa$JVcFbb$k&5L0su8_E-QODaUAuj$WaCODyQlKP(XM)HDwYt@7p1RZQ!QFW0aZC;+jNn%{P}__p*CQ^9TBs@ly7U?2p(P80 zB121dQ%*;1VJ|O0{n3$Dcy8v^DueRXA?*wMwJhmb11vZm=uN1HvK2zTHJ-FKzY!v$ zfcA_t$TSvFOX8p9%T#(tBVT_UpCyM>>~~yI5j80-&>V#lM_e>!9gd80Qy<+Ewo~EY zoExW=q2IRbdsV|}G|YybLHU&|Np6QDq&^Ch^c;HA3}A71byNGT#xD9|3;>cfUpIUH zAL6_FdR!0^+GgEnt+b#3l&9ua003ks%+ybLH0z7DI!cAk8zSmUn4b4*?-c>1!c8eb-|WPw zD%R*IQHIW?5|L5agIB>K==DdLAaux{+<7$k2)k zeHc)2QCS20H9gp62cSe?x*I5_kPlyHw-OC-qQEmT(sD}bALaATI}?aE{L7~P-4598 zsA7;F*6EI?lt?LB5J;^$RAVIWPT7+o4I^zz-M=q=#I(U=I_G~&Xu7?*{eSTt$8uSi zvFyS_?PkDR6VUV&G_U@~S`QE+mH?DVA z9&zP3r7EBkUZ$t5bH_q9E|$@AJQmt&x>xyZIEh@xa!5gWQLPddmFM9l#3$&jaftV1 z^xM&N{{ZFMI^UD%UD92_$|qcMq?+odQIHzZR+f$qYRLRuEJ*mSU^KStt6Bo`f6$-2k7 zQ`xh&zFGIj4;Aw@>Je$U`h(>846QGr7nzNRP(qTRg@oirN2M{;*f`59*bjo>ZPzPh z$;jcZsmsIg8n*II!o1v;TDMy+QKU<7B&kVlrD`Mr*9~)Ux=0)c6#oEc+{Xp;9{vEX z)II7`j$$jyY3Wk{1Mg8DfS>Y+#twFk(}^cV*8{oM3>4y!Cwjr-`%qJ9wOk}PX-~Ut zHU9ue;c>BU9GcJGOE|~62~_uy`G0S3o>j5(HK%P!i#ouD5~%Me0Q#74xRP|%opE@w z+ol^s2{o$8^84%NvI6C;p=zCz>w9??kkoCG(`ak}0eMR4?nuQ`{lABqSCQVE&P`DF zG5WFO4old(n~!JhNG#g7ELWmWD{03T8hJWNPNP73jWIWg+3;G*9uA>bw;ywFGFIjn z4GQ5;Kl)L)?>h=)#I>r|d9)P^Rn~;Cv>eNBPz;ZMW+$?CjFf4`N9>K-{CVkk_g5-+ zHq6~S66WQrbXpy^$ZU|y#WiK%lTA&mWTA>w_Rf!r=o<{w7fcRe;)uskCbam5SV@hl zeIj3oO)3*oDVNiSLyBTTd0G(cxsju=pA*VT5`^Yzq5%CkbpxvECO7ZsjJb3Q$z*U% z+S@wbJozl9EJ(>n>n1{yf^x6#D;LXA(;DYI)xnlx?@I6d9x=PwcDBiejk?>G9h)8; zQ!#|3l{(r|6t#hwI=gWNHc_psd{Q!DZYFzVrhtXh$X8j2+qVAzDZL75hJgn%4w3K1 zBXe#(VAsWUhCcoO03y&hfT<58`invJoHZ+#_h)vqHg~R#husB7Hhz2M9(3E=ziGQ>iz%mLEfODPmA2{<;&hz=R1=;P zeg_|CD0@z!C6C{Etkec4H$?B7%KWdyyGLo+8{&Z`*wl98TU~ACC1giHO;q1nkSmCZ zFm_inb(iW@vD^1AjkOU7>qQ;iyA8?w)b%&;^ElQ1aqdhAtMX zb3Sna4umT&tl>ZHiv*DFc59dcJ*0AHs5#I)`(>dHwIjmRwxb2IHFoMIlo%U)4m3Yn z=#6*0v-3h|2C53`+y$trEpfUPD&QKJwQ(w-!a>_tb1!Z-I-$nLG* zIQJc>P-i51ne}NZ*?i-j{kmkaw{LB`3vyaqixsl#Dh0P1QqY}19?%XgSbeLDjzKdU zs9CP)?l$3)CtF;<-8vAUNKjP=Zc30^Ek-S@A>M!_B+{oBQvEbpWs7>I1H`I%68iHp z*2Au9W#tf|)TEP_(TLXEX+pMp&A!nPb5UK*A=)&`x;CSxnPHv7lZ*|vx~5jO6H=<- zn!B~MQZCTjrS%Jp)M<{MqPC^IKx>stLp)cvgWhf0S}R8d>_b}FUDc&q z`ogWuseW4kg4*;Tt3T+BTw4C1vk2w)LOg^e)uU1Icb?q6m~aJAX4MXxOEMM;-0}8O zpy5g;)j~OBn1Dk}sX%q6ct4yF2urvrAB#3_!@uqX!R3wZJCF9rY^dEJ+FZNIb{vG7~3BD?f(F4 zH0s5g^)@kqDfUN2I>9Q?uJ8}+!>b$FT?3LAn3fi*UotI5F^Pe`e?DgH{jKeTs3VyZuywjPlm%}}9z3v7^r zDV0VueB3!u`oubgRb{sv6xt5mTGUBax@d+7wMSF24U7-(Y;XQlzgxinIqhV?@QlJFP=!)%@0BlfAe3!ux$2 z$w$)o#qL76OuEawc^1g2c6ILJrpi#t4OBrUf~54sgXd&aY71MbORCYuS1Yv3e0lPk zvZwx!VQNHo928p84Faj{!?w&7p4%Gf1xh4~uu`YAtASqhS#Jf{>@TVs7 zgsy9{?S1jFWip!&49sjM;eUc!LY#IpzH&%jrwUMz+J#VcQlN_U?sn!Y#zff5nC>p{ zQ`oFkzsovD$OFd)`2PUMhwbHG7)=tAK?H%}Ibz1w)g&H`EkTqi)q|k|s;62E^1!~7 zOs=(U7he;wd9nK@+Kp=8n+ip;>jvJavtGHyVdmO-loh4$;UcI?vnlFEC`TwY_X(ne zxxqZY;u`wXgZ+r^80y=PM&*r>VS`|oA*b#Myf9rtbA7oWpjBj$Q>UmnYBPiyd1VRO z`d6$ZhWH^HuWqn+H>h2WWBI#_hT{@d(S;V{F)KrEq@h5SxRmSCXa_=Yyql6nnl={{ zy}s7BkW|V7aN9VonVZvc-`w?e#+IjU5T9-Z=`v$Yle>f1sVq=ITIgFdte$vC!2`96!d} ziL*Q42GGqD=+OVHYeD1dD-yF+YYvNy@1ta|@ z#y$PV4LB5k!e`~L(m!f8&!w+W570Bmt`It@Q*DR?->OCu{^p0kr>()=9s}zLoX<`? zzX+|l_6X4w-9nP9oxJq-JBKmu7-?D*-h6b?@uCf{PdujRs%=o)O)^sMw)Bs++#u}a zLy=9sUSC2u$EPk;Q6*4qjSu2o>fVv|IB;LbqVOq5;F7$*PoY@;CiDr({H3&c2XjZ> z8)ne{%7^nXEXMI3Oqt70r7Z;w5L*BzB^uP8n9eshbRw|4ju$YH{VKM7joKMA7E;_X z?H*XI6;rcuPacbqK9<(LQY2wW!o%J~ZN?>0-e-KVbOaIssXCFLn!>t!IiYfjn_a>R_6O@iO0p*!1n z=`OAek{XhD`j;fO-wbblMwdO^r($;E%rd21r>LQrk-_o(>&}#*9P$;Z>=?{u$Gz_A zT^|;Cku)@>?&H{39khoLEfM7w2}91ubuOVQNukVB2}x*L+LZRkDkgS{Etn%AM6C(0 zbp{}A7y(wOI><=sY6{oljtCtoOg88V=zM^2P8%(-p|#EVT`jC1DAI*QVrt6NQnXPy z3%Z_~tsSsWp5mN+VWj@*f50$<>`RWQW63fxL8^Z2jlBXL(l?yQ0Ion$zje(*iXxyN zrxQ4a#V}c7pjgHjj-cz=Q+AI%o}`XV%oQwt-|D&V?KflZ)rl7+M6VlDujD# zF{5sMv5<70b+)I$VY>sk_-~Uom*c09H2prc2XbcD^sj3Og~eRiH-Afx<7jqG$*+<_ zjV)SAkfPdkl#I#k_F{hpdz0y`)S{;?k4Vg zM}(xry_vMZzh*Ynwxe78D4*f&oarU{i*ow8EfOFeMS6AT<_ADkDoyAM{QZi6b+p(vsk|;_dKOkV<@(`t{8q z;VF2afP1LVlD#?P4{itHP8vvF2vSxmY2Aanf*>{EtB0->B>FY=LZ=3#4Uc{__e3Wa z&fgkk;{O1ceXBL^ai-dz(8qm+b1(8&3SQjj;aZeUu+!UE^D&}#v-zy`EQ9j5{{W?% z{%_P5ZQM%HPz?`Z#t^!yt3){PP^LXZKQZV_5gc$V0a812z!a=5HKOGw=E-l{H(M{K zhWEABRjX|s#c~thg4uFPNwT}7pd~6b9eNyZ{fm3cr0L^N))y20abu2_-Dgg}{+BPG zJ;c?975ecLYD|%E+wr70i!}Ku(EHKSsv&Dml+i?W6d3C3Qu57st_ryf5+0rlsh^Zv zj;E11?4;0}4Th(-hT|+_3-6Hs0Jo3Ue^Tpz%0rLNTvNfn{cE&$6su9zx|)zjemU|3 zK)Y^g)ihM41*iq;708S|28pMbuvYON?|wbjlqW@iqOUJHl>?P}9D1oQFs>K=aPd^c zz6`sl>TP2C9mMwk0O*d4y_CmF_Ht_5ye=I4%2?;i6FT*pksht%s1I;%Q=|JP81#ZExj;)oG$=T((?i zN}jhbI>1A=Ar28knENXiGP7?^uxbeLrjgIJyD|AW=O3+xLm>-WqQtO zL)snwk8U|jd-mfm0dtz;BRqd-T~ph>;bB}F$ru?YhwSnDd_Y=$Y@BJuoaMn*&PKKF z>tmbWkIRnjJEVCrgsqQ@QhwZdCFUU#6{J*W-G!OKDtIAMq7o`H{m@5D z3-~0dK(YR=pYH31=sL%O1ztajrw!1s(Mf!IU2aX)*3f7ps1=WDsn_Vigtbt)xhNCv z-5o1Vw?aykPhhPuvsqOE9GxL`H`1>eTKK=yzcG5sxxq}`)3l>Vw(#U95}gd(9s)Aw zWn}A5lmc0NuY#O@ni2~x_KjPjfwb4zJ?6#0PV^c++=9pF}) zZeb^iEd88EjQNW9h@n(Kbakh7aPHNv>l!Gmk!XaP@=PyGah|%tbE5#^<&I+m$%wQZa}5$sJ)eVrGNy)W8{zpQ*3*dPciL?7YnkVU~Qx>Dwa~ll?N%MI!1Ksihk#9;CS+# zK(%h(-C~SK=9&wurjkz5xB*}S4< zex)$o_x67@(F9;LdK8h!e09ZRix+#?`x4m}=!o|9CRv8pne$614O8igL zTDGcPxLh!_>L3RYqn)kC+%G?HO|;*(C8$Alz}>%FZMFt<1gui07L1gaO@KLvRYSp? zYwjH!fKVsL6>yIX?oMIb`!4gd;IC}VdH(>NZKUbcmYQ3MN>tGXgrs)jy}9ytaM=g6 z(5d~6jj*}OIwuOYg+#M>k8a(!R`S}g?SF)A*p~~0XyuTC^K7#6!*nDXF?ak8SJQQ-@ z6YiNyo``nG97Gx^#ibgkl5$tL;yUKxk)2H{v1VV*4afaDJnHk&Y4i+1eJaP7c0$GKI}E1=A58vY1gk@0o4l) z6}a@hc5za76{|sUAq+g)6Pc+}n?4brc6iV>McYMMO6RWmVqEsNMv9eoR?M_80%5qF zVD)Wcv>lmyaee$k01vzuG&2)&9q=DwLzcs>Bq`>W$yxC0LeXFIfWaGi1e%8eq$IJq zU>!{%7K?B&Lxo@jkxHoP&*{LKWARHy`5HJTP5HT#B&G050*}SnUuALcF^N|B$B{@6 zGc{<^T+l1=6ZZAxJ~SrDmi9*R$!u#qsh*K4*f_mhO##rPU-LR8Y|U zm{pIDnkQTQ5wSmUuSgepYD#i#A2Rg^unkxrmWcQz9epc#m4%D4JWj}Pp5o#CxOB^A z4hW6BapF-Y9DF3H!le>(CZ`UX(oiw$K zUA>~1RV5(NDw!2ZP>iTNe$#^}^0qFl48}aYRHG6>EDpjIO8YQZHmMn#RPli!OuG_D zB)Gzop+iGdnu;6}VNpcRD5etccDoo?%1IqO|YQpCc`UDW-q5A1@tG-8zuGU)cB}XJv%{|~ zo(Kh~qBm#c9o4qgE&ac^q*`CNGN{vJw6Yy>hYeJXT}5e~4jDe$HxW>mj@ze!M=hMS z$d)0l(Y@{#(Uk))DL)NBt!r9T(+8f$7xGFn)bc5@bKkq7;Fkoe?3V+pEb_J@m(!5N`zFScGqF z?j~&4B(8ik-0bp4mvy$QqpYasr1mX6QhV{|nuN;d76{QLa+>UDw99tiG(8Q%lLa}Y zXh}IGL$raB!z4fvn6zohJ=lO<8z;hzxvfiWW3%_L+@#_Jl_2d& zdZWJ46;pjX=zFj+aZnk-My}8B^!493LyhjiTHJ#j_^ux1#(1KiP{cMuDoC?QUD6eY zupu*|u&Ez-lnlbV`e%rFNa|BoW2vCkKA1x&Q*E*WQrH@4OtBj%Q?SfI;#B1#2Hv!7 z%N(fkl>E!O3g(0Rsu@swF<)nyB&NI;gL?aTE!)y+N-Gxa0hWV4eG}V*%Ld$I2}5zQ>vCzC+a>|T#)yj))_ zMzcs#!OVF|0-g1y3jBmE6{_?&OQ)K2kc!CL*JLY0*5_MNYgDN^X+!P8BO@6V1rlsD z?KB}!O~Jq%vtWkR!L7LE{?tA#dm`HL@ndRHomCAI{Azuw^ z+gvPu6xizZoi8c4- z!&?qaTHQk2ZAlJ!iz}!q03N-V$8MR_@>kM44a-|>*7Kq$)GDY71e4o@dMc(oUjDv}yf6PxvPt!r!tb?--WeH?qyQZJ#n6SdUzuL-#4J7d*Aec(ei=E)ocB zIV5F^Rh6rl%DH75wr9y?^6v#^KQI`Lwa)gq>?|n4+ctJqj+9e=Xd@OJbI(mySKFO! zUF%e#w781ZH3PPz62A((N)e>E6H!XjuhWG>Nz@!sv>Jdq5rZ_QZf#PE4n$#C(;5C0 z;kJ*wPIT$}aOJ8R@wEzf+Z>49i)maom-3CO=x%R^)4CsFe~Lw=0d1E;s1uh$J5C-L z!pbukK`Y7NQmkFeWJ_aQSq}wxwIx6SUdmGnIyRM3eiBeo_HTA|5l7|0VI(N1{_k%< zM@%*H*q@Abz|bu2P2Xvk3N5zB5p4jf%DK8nn(aM;y>MANX`;35+&qkTQ~0JzR+UL< zK$0unh^MxTM`LPetXBs5hTk5aw@uG-?1jcm%dAh6m}M{|AhWDH;Ylf?wE>-RpYDtl zcg^W~)wULTd}GX!b5^kaXZ+-VRj%73U-qP1kdyRy@j#cimcVGW@Ap>x^Fq1#oATVa zufvpBWvMAsZ-l{Ofh!a}NynS8Ey7LR&c8ZWifcvu!1*H24Y-z_*r*X1ua?k;(Fv}7 z#|pb+**l1;GdKQA_g_R;{{XTR%hFVT`sNmEOoTR*{{ROK&dsrJ?xdvNS*LJ%c5Uxg z{PnxiRchtHr3%p=OK4SVN>fHWcGfw(T8iDxnt1+Y66B6r^&>qiecoewK9E(UFpi*B zp9sN!1jg2#2@{mfbd%?u6;R|Y@w_=cb-lhu-txCgc4r(_tUaWmg}LBTkd%Q!K*iG; zWo>HD;kK1o4tnud`-iy5d-mYMHhs{Wz4L!j-qo_1yc6Qz($Wum?|$45l^@&-)t~yO z+LtVw8-MRve}-)9<->4XNUE4-Q;DK$N}8!O9^5p#y@K3T%nCyti@&^gOi~k1N_bp7 zDf)_ShgEo<&5k+{RjzIIhRIDUl2Yo4IAt;9g|0 zA;zT2WyJ{(p~S2$Nl^racJ%9ub-mPXZ-SsnWi)bYs*^Ud9JbDdCQBs@#wX0JJFt!0 zYTXp2fTQYEmoI-=p)};hu%g=_7lc z^d?N3ZP5uMP*IReEpFN`TQ<`vnMqO>;sfYXNfZSY zS)syxXT>&l%x5K9_a^SOZsf>j^=wSovPy}HQG~N?iS?EHH(FqicOfBi4vmAEyyA6`1?8q`+K^bdLz$@QeN#Y_8dZ%wJfQkwO#dSl` zPa55MLz?2>)gHEegntpZJX5I)kf5Zp)_GQ@zRF_Dkhl@CVs_TX>RL5->d#``Fs!#2 z?ae+^;#3ep6wbZ$IM1b=IohofJY?}l(mAU~wWX|Ep)(=BIYFcc2o?&W#+jaG$^#9CW_EubZdR?B?Vp>LqS<&s<0 zHEFL}YQqsT#{!>RSj8!;f+}oQB_z@+0?$FAKeGn;nS4?ogkk?ax2#ODfvr+8$r?vtJH!C zsK-I<3wRx2()IJ-zu7C;bKOztP70NBcQNy}^Mey@Z>~wY?t2ZrX?EXbl<+Z_TVqNT zmcmjANI4P>F?X@smJqnUCICv#uDgkty@)d6M;5n$Di(V2x|>Kp%u^apK`9HM1G}ak zn>f4@F>Y(2pmG4;cS4es9mOTe8i2JV)9V;WW8m;he{x;@;-zh^&m7mat!`PYJclYB zvB0FRD$v8p313h=+qK7?`?Gjj-m9@56~q&SLF&`f4E zg0=aI@oA|Q@50R6mS>j)m-psGe)3<9>LZ=42@AGw_al~#P?X1D0BhETG{H{Tvv(x4 zUCheIqg_Q2Zav?8qo)4io=owsfR_rK5r|n zbIBv$f`sMmFVU)YvD{!?p$8!>EDUFXSbzL^42o4L)4VDItier3{`kE zRV?h?;Xh?=2>XWLDRCu14|$I^vV|6$X-&8)I|@k$-+_5=Mxv>u^c_N@McrEuX zpK96ng|DCX$wuXe8g;hZZ8s${mL;;82p=$q&aVoxWM@@MrgYliHbKOpTL$0f1%FhiFpYH;@jJkrt+zYlFc zG1%E1M@C!qsO$d#$dmPPro@xNu*+*O6q;P`Ax%5p_$eCKVqC$`84pun$(_I z-0k;jYR<4exY&XJ0PZg({V=Xl{!8&c)pynV6DM>2PFzK1w9dLOo-ekTu^Up~nI7olYCvgCPQ%Mf$IMN%D7q2TO)08XCmD; zUen|@c{aswZKcNDvDq<@>2kGgNGe#?icplA8s(0d+ZkD}Ed;W&p~sTqUD>xF$KSdb zU*<8q)FA4~1G2wvEX;5!v|&<*26GPJ;jUEKd(s8oWIJVwdv4wB@n3PYNs4NhTT&9B zm1;>NFmVN)z}w%zSl}vot0vjpcUZfTEN(O$tD*WhCx-IfmmX>6O~blpIQ#Oqo!F^{ zR^yHs@!fPGv;au}B^2lY!R@@9WsqY49V6PCNpEwU$(PDW>psA)^xsJwa@$Rq^DY5O z3Di{cYpz+E6bfTHo46N6)Et)R-p5*&f(>@B@=fY$ss9y^1AtLD2(VSQV+8hoE*=&tgld4+kP8o z2%`&|j$D2sO;_KG0Dh`g-oc@|FGskAIyF=eZX5}dDXfz6R@*Ar$y0}@gRYFgU z_o}y#AI&E(qY5StaA}}MRUbj3hC|^sz=Ym+)u~y#S`yGHX+AIr@C^HVFzkRvs-JZr zE=#uEopri|e4IwzQo@Ky^3rILQhZn+Ix(S4t5Kp(`F7fIO_tK4*&vDzqbxXMTgf+Y z2;pIhYH=x`{qhE1UKRv2)TRU;Nl`jb*dTy}fuCR)Ts^4*$5k9@YEnT*i9Mur!1;~w zUg>v%M&rJRdN>pZU?T}sc!WV4tHCNDwn`gO2yuC#Dj*dPWpEvB@kcN^w4$(dmS`-qPE!O=P0AoK8ACu_6U z7~G>~iM?(W)5ShHQN7vCJg2$09hZ%!$i8p6j!uoSEv>qz!aSsvCOc&*QBQ!ZkULH* zm^NA1*#=1$4Q&|eF!DN?g{{4ceH-gjSq>rS=i-ry*l!9kAdTHhHpW&8ulS= z3SoC=u|xe<{+~i#4Yivi`A+)3YEE~B`FUXu+&D{;Z4n$;L6ag4#b`}Vky@l4xFnmW z9s7?CDaEGA%sxOI($G(c{i*Z4eJ1k0&$R8=c8`tiXE0(k!eCrQXX|= z0;8Trp300z*zcsjI0`KjdSjyE~ukktKENwD}o)Gh4l zwD7L(o`X-kMT+DSDm&KNMk1~|iMQkUrf0Q0gR&dOcYBtypQ4{>3P7}aEcJbQNLw5c zeT~pgC5OUqY$hmMeFi>f09o0P8gRtNRtl8~I${msF*z$E=UkO6X^zZ%W+b z#`msXbC$MAvf1}O)RhBmEy-}@6LMOKZ6Pf zxqexScpi)?(tI*E+PTI~+h*a>@Hf3i>Xb}EoxRrAzUdX@sUjJyg9av^$kg?q+H>? zERc4@t-Y~rNZPv#Z2i@$&m54Ko>9@}Jie|tqnT2$jm!F2thmc^>8f>H{{XyPoRGsz zxO0(JJ^d=@opD>1MRbY~4M3t|Pus0NH3WG_u-3IWXEnW4&9*_!2nbt60f*vV1i?EK_g z)wRu1GBrZm)S4f0!EWaN0J(3iP;of0aq@FIsy}(!Fxpy58>KoNU&X-zV+P2HD3Q~) zm~N`mr@Yht?ixPexTn$BuI>IvoYBX8dwl1^SL8>NU*4Lc%CR zT0Wq-U-HI8yQ<~mfqTgH7r2jT)pv4A5*JEind0EShFmm~lgntS@ZnmfX&UB~7}BUR zQWibl96gIv+V9|7Kxhke0nm9Zii&4M3^tSEN<;7jV78D6B-7i|1WO{c0l`pcG3piK ztIw?jH20)mQc=hLm9p>6j_Rr!y7yw;u^s;ayT|_kF#AHyJI;Ex{ zWy6-9Zi~rVofa3jGqy)I`$}o&43CvWq`glmDguSX^`ut-M$r4H5Jz2xvI>;2Jn&n}h1U+tDbD9-v}2NJpG z>*v)+r+#OVz^v_E_NQZPJ;grZyqH`$DRB^g7DEB!`<*Z!M*)sXCio z6gu{bgl)TGrTtD+24HoGkWeAn_Za}D+6|?XE=zckhI*U zEzpHLKv6&`Bo37r#JkU7HPms4o7JiPOQ^pq8Eb1Rsoikkz(Z+YM_27#ZlBLwBBef; zc{18UEATqEI(4Rmn&TFaW@HE+;az2M+{`GgWggVkwR(l&3HM=cV}3A%DGn>=UuHuL z>PB?)9c(0J(>(DZcd~EU7$KyU1!>)UJd^bKfCPG1J@88laiwy#peY1{Ng#@kaKlKp zW=Y?;RE)jS!GolsCcEknf!az`c~aKmv#y5}tsr;!aNulgkK8H0`>BNfs9bCO*SR-| zxj$?1M2OC^m=EHGqzJl9` z%AJSZg?K3~AHf)EVhsZPKHL&Qn@Jh1QRibZA+p=Z4z{kTAQ96E76Zi%zEWsiR+=U~ z$7Qy)Da(8R04_yO%d0dJdjagj17%JNQ^9u$$K9KPVBBJP3UUq3Hh|}a#@tA8wWqs0 zjw0lOoK~G>lZN1RFrpiM{lTytb$cH5BGYbsl}g*FeZy^aN;--8eOeNu{7C@ekhG1j z>Hq?!Zrt0b`XwZVDcX5%(|RTb?Th$C*C>%Bkhsh2>P1E-p}Q2JS>{t{O2S^IPv7N-#WXN6MYN>rNCxe&w^C3JpcNFp_%Cy+;`@d#Q+txkKoqKlaBsJyCPMvjc%UX}ao?seb zJ8Xn<8Z^|7Da@1o9>p8E4b_SvB!SuiKcf;r)igJPsH>42WPRWu8g>zb(JAvST6sz! zYET38W5FrrwP#L@yH96n$F*MbT7(S^G(B)ND6Wxj&%X^9rc_k?%ctNM= z!o=vIx<13^(&ZZmdv`WEEAcdYez za#!oF=3UE({5PO^U~#U#%6_$0quH*PDM(xkA-OWbs`PzHN=WKKQi$t|oP}l~r@?P? zv0P7^=Ix|gTOCrDTU9D+nJPF@vI5$r3`Jl607$&2wK&&}ZkGG~n&mQFtDDb>km3B) zaM@|L(v_$vQ9!4qF^=!NEcaI)(_YnG|g*ufL`<&+RL=I=x{1D|dr_=iD1no)lIUR>{nsy>ag4Mn^}fBl&!D zT-j)As=iXK@RFw1#UytmWn5(}eOE`#2(F5-deDgMuT5N@&y(c4@(2lOJt^}I;{O2K zw}0+USl4}HBLe+7AF+Jl7&;`pu;2<(vuuj^x*n9rTQGa8=Pb?Yd0j&E7iMzuHq+OO8|x3bj#Q;f%MqQyALP4HoIydluY2K<3tp{Y%)^c=2Sl zD`nfI%xwTDG{O{>fO`0bSz{@WDKRwaTg=;T)QY)+p;m5Ww>|uoxUTlSoV~pc*7S0P z5CRrZ@=^%`nG=h~*d0>`QS+!=W*ktPCTG^anZZDb_`n$uAv;r{^3FaQdyyD0MVbp=@; zOPh;q*m-wp-M12ayJU5|Jml(eNmGoe2{f)_JZJsG$ICsCYb=!6}cRuj9Tp4Y|O^8}PVgiyh zuA|}^VvpPxIjn1%x)1X7=Dv9&kZDC${{S%WOb>4UH zQ*JgC6f|V5K~y=4VjByb1O}cfU%T6+T$eY3`P1C7-Hjc|HmM3JYJudXWC9283{KwM zzlbVl5sr!8>%dTz=6s8Shs?UCjC9emr4+K;E82_V#}U~#i>&&c$bInX)0@q86fpPgf^ejj932v z7MX|nitgM^tIm-9NT~Gt#Eu^q5yx>V>XU3vW9Uf1hI(0iJtkNQ7SNPp881jc4u zxx6BmTL@VRO{nOoN$yS`<1_aLc7Z296%DvHwVZa3f0L*OlAsoj9@|@{6qoG$5RJyn zDNZo`V2Wq@N{p>CpDz&w6l@)c%^1 zs)75Y;Uwh57O9b__!ZNrz@qd7R`Y8&>tu*XaVe8cQAsofL$m3_Yo>KIOzr!oT20jM z1wnJ|_nQRdTkkf26(A%yp*=B^B6jne`xJ^GIu^qj_F-ywt8#ZHalE@ncNo#lxSR*wpnEcxnwG7 zjDlX^UA*YUzeCwveY*fMf}d928{(^BX?Hu?*G$w`Eu@ML@PS++?Zc69_glqIL9lR9 zAAR*ow^*DVYi2VN{$k{q)e*d+r6h{yn8S8Sm47-_8uHI>LNy&pCEd%!Y^Ij&jwJbD zX?5QTJ(=cfhmqs*1bffoqa?^-8~nI}O-BB+#COlPY>RRb3DOqcv0w2{SXsC^kHqqs z4aaQHh9Hw9a)#hyPt055!?y(zQrmG_09UgxaOt>KQ&LJoUDF^nU<2q)c{|s3OnJwb zc6BRwlIcZIGwd2^mLyw_gaJidmpyeOt6C%}Gj4L0R`j6Z&wgVT=j{%3GP< zYgO{0wKqoX*^&dhKX=`BgMikytCZ_@Xa4|{-E1GH9S=RdyqcDS^eh}QPzbLTVl3XU z{YKiDL|I&l=J15jqGDfSLc1kHxlTUcoPGXg8oL>6N4uhLHHd%40s22;dfPo_TpxE% z@8Vl5^8QjL+TWXKboNvrs!BeL6Xl%Gg~eB{wuQ8Zg%6=R)#`JI?ocP&p0!^#o0ktP zdF~;LlSt8-0LEHZL+wTvig(89xYa~l_T#QI(1*2et~tJzzX;?za^5uoiAr(<$KIJ~ zN7!*F+>2fnPJZ8B!tND7a)Wadux?it!XJSxO+#XPP9@e>r`}6o1t6$rTp^LUXn@g8 z>}(|Qv76EASru}c21_NLgpa>m~~it{GW z%o_wk8myFyCGu@47N*qjW{_Z4S*-E?DKeF{VM z*KjkvMD9HsA@G~z)_MKr&BMDB7-a# z6%b$qrADT@ke@8kJ(R+}dh>BiCq!ivA9&HnDnEm;;U2Vq!h$f`(p5l`li7xjIwz4^ zfuKsU){y0CLTN*WZ{Vb%U{4z^$>a^vrLSx3o4xsvO&^N_OU)Vznk^_PC)f@uc|4pk zx{h!KX;@q(%#5p8NKL6xfQuTXoaw^xY#2OV2opETjKH|#nZHSns z)D5D=&e)f}eF>PNf4WwR(kaut;FDfCg)A`*k(_l-bFmy{t+Lu}k36ZXq}K{pl1a$q zFx9z{R3mE2NT?|O=CN8J&JZoEt)G?@kX8*!`#`QGtnU25RbJ0nh-3{KCc-UkDa4>Q z5>=O&ynw00>Z<<$O;dr*p$Vi7R|ci6S&-S(0q((DH4w>a0-LyZY2EFLk#w>Ugggst zR=+5zHL8VEohyh8%z|%pjtc3uA(j$e!$kbQ?Hk7FyG4g8+T4{oqJp(N2yG+9Q?Km9 z3x1e}fll2`EUu%Rqy53OY8g0q6>1O2UQ@&c%j;S3g3G0XU)0aJ0FUW)% z3aY`mE;07oR|B|bG~DSaDU8K5s)YPRQ!HDf9YU(H2$9Evfuzq)_msxav^u51Y2>|p zn3{m3C0evs4Wy$~$+L4ns#E0-L4&X@3~g%)gLmoeX>CB%#$b&lMx7K#Ojhz28qsL` zTLsnc5U7s-0KO@^I+{}TE>WVh>)J`f7fDdHD+^H4t3<^|B!!AsGJ6IZnG~=D6q0;} z1EEPc@sSKccttAD%Sj&e;J#vb!&GgU?yE=!fOV-DO&WzgY)`Wk>V9%s(lSkJ)7giy zJXJtt?VTfZ3DI!8?75MPSJM)@ZxKOAYYYWKDT2$BRkBYdc={w%vv86{2I2vbwsEM)O7W8R?tQ z*3w?|By?fJx|wMle=O({)4Kp7-wmNUH8?0lz%7Kybs6n4X^wIx|+ zHa)yDr1@%ZunPShxUBvr_bbb=afGX7g#&I|=7NJI$3+tIso_W+FjmfFkR!=#$Suu0 z6bG}UCROWoPx*4vesRP$@l7=jnOChbYskcWumZ=n{DbCXdDB%OnGMw^Sg5W;KhcO^ zmy^L;MoED2S_!P0r&ty0FrUgl!4PCw;NM-x4mHiOBjJvPa_4j4g6{0ULb-9z z*DHG))rheoc?`vlJo+O@DpIRKmNlG1%b2-ea7F~>t>mD)mD(e*9BDODoB$@M>6U$% zn=DYwjMXGJbB>0aLXnyP3MBwmpGFJN5{4sG3s&ERq>zqMMwAB`l~%BS`{;l6&xojGk&w&tMb?{iks8#BIMKJ<UsK#V}#Si(qnP+j6Z!uG`Tg#n{+7(P(@Ny=2;SYViMF``GiTqUqqH)FwZNFiji^~ zV&!Jp_r~Lf-i-~Ylgd-nApoI9pGd>2hJv-M1<$)ep;w7``DjflKm)X#F;-HI+%%=Q z_T(spumO2PwSVBQ0H;>5Ql&k%5|bx_4sDwR;A0oM|X zDh{DYuc#qv2^CPNR#gVR!-oZciWHf+^e?OL$>Y|Z+~sZ;w)$<~*S6qtuN5QYwx7(p zl&bf)lV6oADQ2sBDt_t2^`J_+9Jr6Y8wPt@*Ih>1#{M+qSI}R}r*>^_@xsF4)Rmko z%y1g>^!j~ab?#hg_VvA_ml#z`YpqUQn8G2OrQvl;q3VqI5WaqWQ|;*-YwCw}KNwQk zxnGTWhyp=MlQkhvBzv7o^2b%}E3cWxfOvqoGk;i{9N8m*HCMyZq=mc7Hg}`ZaBdLa zQ_c**N0gP)zWipJzR#jEd9C-i*MGzgu9R1ob9$}x&$F+xw#MY-A0B>-$akTvE z9C|yc$f4A=t3e&1Sftu)?40cCjwcYbYu@Z&?d9_1m7(6Wo9a5%Z|AQ}T#Ibu4aFAj z=I^sgPS(9beYco;N0+Gy*3mgAjVH8M3hgV8)Qy>)w<)!7by{^E zrSV6)BHdu#W<$NiUApUUTI1X;&X2rh_EcM17yTrt5OMT&&Pe2lIfkHx@ydHS86%qg z91f$>o6C1jkfTa;xKO7a@fgBjTd6;Wu-vT~ON5T|JBI1~&U);WK7_5V)F9<88|GLWL_-l_4kV z#&dPD3|jSZTV6N2&720glvB3c-jVl9$znCRQM}Zswxuso>0W?h0^=0uj-_3#GqK&b zK)4U;X8!fxnK$#!$S?e~_n1n_trHyiKo4~>v0|wmxE_U>-9=f+^s45!ijQA-!=E)e z5Y%Y5L*p*-*9VSWQH7m(rvz7ukesXSuR({g%9zh$38Hh^?vc>%ku1>*u9tg=?>KPF zkX1`3iF;2_QjXB${l&lT3WZ>|t>#dja_7>Fw%@f`ik`uOTwli!_lmE!-bplQ)lZD@ ze(!B4L~BwULqQ^{rA-ApaKrKF2Q=CHV(I*oiQX;bp$x=_Zc9tkq!y5!Wi+h;rdT1* z+g7zne`j42!7vA>UQT>1=Jwm<)RR=wq?4bE4q3XbM>JRLi{d^M`Qy$PzEx7D;o#n8zD1J~UlPEwMs(_q z+(=C@q-ixCB!W-Uap-q-jDPA^=}X>r^D!u=l|^kmJ8)+FV}SXiY27$B&AGP~!2x`K zb)6nNbDEU%eoKKbx;_TQ7Bd~A8SdfT5gdebHc}cmfxxYIYj*zm*l8eiBdm=L6${&u z_x}I_a^1DZtkGj{dke`*ZE_h*wi4r+4+2OI16-(noNCuoTjcC@KI!JU`x9+DX$m-+K|^2Yn#x9(;UCT)q9FvLk$+dFe1F#&D2tD95ce$nHuxIY_R~H8D%`846MOq|tcRLWBT+zFfl%E5C%y*g*?f&zh(}}O`Tx);QZ?G!- zyPqa_%I|6YsUgl?<~MZ*Ib(GxDy>KJ736j6(n!L?Z{z;}q~Bouh_UVrf#WOx0P1(` zNiTA5CrE8f$mEU3Ewj~^ulILQdoxPpkZ@;VESvDg(fD*Svh!{{YtS+JIw! zZ`kQl8Q#~Z>31nAYVmSuP9~J1iXQ~ivbbsZJ)z>k>r% z%hn#V!LB2MKvfB|s>O*16*b{KYk~;YXG_ec{8@S6hBh-s_FX zOoyx7Jay*Z74+>o#t^buQjy-8p3HpqxOAt9I{H`EyEx%#(WBUy*S1}ZbhtGGQiDwU zu~y1Nbyu8S!+aEXFe$~3sPUoa%)`R+MNp5xhvJq~JN zER889L{%jv4makdU+UB40;N{3?cgG%p*Am@Q)NhsxtoI1Zuw})QsqdJ z#Bke9pjAp*+yEsYW=S-~&mBGaaBCdmv;38gHK3Uq&_0Fp>Fc-GF9kn+y71?w4WGC7 zZt0CicMmhL!xCUyRjQ6LZ_)}Kbt0io=9HXqK5pQ;tm=8ybQ+2M9hm4iA!8fT7dkdqOiP@pe=ptqlfhubU0VfN)TCr8D|*VJ58sRiX#1o@u#ow6Sh$ z&O)`R>iG-q09Lq;xU~#Zw$~<8MBGIAWT_?P6oZ(l`Y_N+;-p(_#k;HcALxHc!mTtY z#gx%zcaBP4tp;phbzn+~xG#&xloMCC=o#So2 z!Mr^U7W=N?Om<&{%E#T8LQ?0Lq|jEHLF~jx&cQ3%YeEvY9ka>VvO>TMUscfl9pG8M zs4P-<7pI-eY2_<)6(n3(hj5PE*o9+KQv7KOL#>iJYE2JiaW9bHc9yLMO#$YzY*%YC z7jtwld&KioQa>y2R#y;$#>a~t4tyosoKzHw{_q6T6)V2;Vp!^RRcV%=mQBR}0Nnz< zpYU5N@{9FjE%xLp)wbGLBA$KY;VJE)jIoIMd*_O2r3##j@}r+R`?d9-*r7e`=9w?Ih09S5$9sQ!<*C4QQHb@IOoD|V-Q4ZX+EE>UmcfgMh< zbh-otIE1Z1E+J8kKs^diZZZw^x=v#R+k)PLTtz)pU02y1ksjUMlG(BTV_L?V`pds! z>r}TW;Q-T5Twsxc2CKFtI;3T-VEjkfhty0;OSlCpB&w{MA#@%76NfhZQ){zUQ6ol7 zuabnHnpFc$=>X6V2pyP6yWT*^31FfPUo?cemmXTmjS_Vl0rX*T)~5dO;(%^5>OoJ; zI8>r)HLIo#*3+T}1FC|(QQKXS>4VfOD|uxO?ECm3N-F6A!`tq}{>6|Cv}e*47rK%4 z;3fyau0;M&TN`KZhPmzr?%VNq65uayxolLXQ0ZYR62ihY45~n`HXiikZXl6@H*~If z{I~7A8KDTgfG zSMB2Rfb2X;{{ToT^XjXGct>S$4=C=t7ZliSn}X44kmauH9@!0-ns})oEh<6*1QT3n zeYD(HmpL}E&NQmTd-Jq$mRK1jjxZbn<>*`tvR&>@&9^{~5R)zBqNNPyQ=Mut*B5rp z4eDPsZ1RsT4O()2{{3Nnm@zJg<`Pz=yx*6gW&j=$?7}$vR=m{e6piLmNWK$7qD69t zA?flznkJ=@*p~u6)CA&C*hAo%GdXfjt|*FkzVMQ|J-KH*lhrGb>Z(1;BxA@Uc64xTFUz?=+ms)=4KXb`X-zA;~z4BJy~ZXE=j~H$N*)dIhTRSWF;=O}NNX zl$MaPXrPhQ?Btf+*G1gR+Or*$#vvP;O%KuR{sE&rqD#&9evVVw3vu%&TSxc2udFglG(zx@|G2J#! z2)h!VjzA|{e%vY@L}_9V^`{yhZRFOT6?GkOu7yUHkWy{R(-w0IZc9Pd#$vrk(}W!W zDsLH{p566nQ8njk+-;$6+XJQ4AxWON*=*ODk5ifC;*NmYQ za+hqo{>Qo6X^BKyu4g5(x&$oO_f`6i9x~}?**&8)1F5=%@G+LyNGa3Mg?`FmQSXYX zgTsNLLCP|nOK6id`7B2SM2ze2I@=*ev_f1_N=AJE81#1RZvcUp4(C(!qNyHXNzz*# zNCzt7KqTjVP=PDfNl;p94w421ARMr`ab`M^A~{B{-xUO7!L?o&?rA~4wb@k+Nl8+L zXqA!;Fu7`V4p3KA-G13JJwuEXg!NZZ_F$uel4Z+WQ5qizC{R80I7q&z;pprOe4C88 zkfWninXa_U0Y-_|^22GZItr13Na0HOC0}tx(>^-%smD;Dpl*RTbIxHY{6x1caFD0J zc`YT72uQ5~Q7073{lhz5MyS{=nJ}xBVC0J>qipYO$zZTD?0Y@FN|SD7^@krrL;?`C z4~a?XUff%(-DN}t9#}jSuxov3i1(L^w&RbIY##>IJqING^+YFi`#aNuxpDOS}HfTb|%uj^PfH1Zrgel z)(CRU<4~Q5%97I86U0&qa@0j?F{5U(^Tm&i?IxvSTVDZdHd=N)qJXJQ{{T&VM>os| zj%{v*8u;Qhm3xU8uf@BrnmDWE-MPm+*Pt$c`rPAQ6XL5j&1&7m*2wpL$s$TW6GSGG zq$V?HRz!@=Rt<5dY)pjrj;oDmx#PW93tVeMSSVbVy#ZQ8>n`?OQLUCHF;s-mAti!9 z`*9Q9M^aDNnXC0P>g0<`yn07EioRY(LI#MOJj8QTz?GHn&_RnBv^N6i~G9S zbhoL$DM@Zi3Z)1&Br6$_wVHyBD}EQ2Omz&6KT3wZOG8s!HOa3=dOOkDX{W7CGV@N7 z1Bp5aNzXj;#jj;UC(k#i5VEfD-CN0re?x)wE;g=h&@@!UgtwGU14_7^8|GOT=~wIgopWT+BV8q*{wvY9QZ3Jw&Yij<1e8Gm(e?yc=QpmAvh zZhx0t4m%^1G2Jvg5uhue+`Z{fgrV-Z>D#+O8(Lg@T!-Qda@}EsIUO`giUA;~RD1E4 zZJphacxaK`K%Q%)_or*H@fi#Z40A{HARj{0`zjwELE+jJB6HQAWR4N# zJ5X#myQPwQ2n9NVir7%02N`}epF9=SCWk`4Ihqb3<;9fNmrzO`Xo>()RFU>$Nv}|% zjFKS3P@-Mwww$#(sZr5LGKnpb`=-4PB5xyB41D52$5j@-j^oojM-fFZBT1*Jz#@XY zv!x``zRY=?QtPgNmFJ)?PxVCR@a27@x-FYl*RiKFZEY78Z{Pt(PH7RL1F$b2Dbx9(e&!ryk4YIZ)T&H11@ps;{E zKvZHmyQUm~ENG&tGhy6fi)C@HycF%aW^dik8dGt$R2Zv55?v^CBqR};@h91ey@U`! zKCU%d24UA1HuVhv0IEw2e4gTx-OeK|xWQJmsEr_=x#K~3e?>tnBC!#Pi(V6}hiL3BWlw4NsSl+JnmLs=**Bgtn`m-7@*v(> zDN|)0BDU2~X`LztBX-A_ZJyWj3U_+NY%gMbKxkE-jlS&A<1!{%oMG)Cx|E<4)CwA6 zhjWUN(jJnv%Y4)fVCruqWsUi1nx!rzB_tx1)Dj8%Fp0#>-f6^pg52^)Zkzfua35*d zdRZt{YnY~z4kU0^GB)L>(xW#{Zr#>TALS~DdNOBmT`fG1WwTr$LWGUQaa*iOs8H1U zz|*9RfCC!0a(&0dO@0u$tFc4Pj&@WJeEq2}^Ag#6rq$^?mSxYr+vdU$P+g52FIfBb z>D5`%xNX~8j;rt}Q~vN;7FzMG7s`4HkxfFhIdtyFQ&DqU5UWu5K+m+}$`GJP{C8?olzu` zg&(sXONhFS63xBZI0x|&o)VDgmAJS58B#Ys?et6Got3)VX0KXih97p7A(<#B0=?RR z#b)+1#3voD$Psq`F5_V;~FG%Kra z?(N5LN0%=3CH9;65T{mYUwOqd@OT-(EARIe%lN#Xr8w=9pK>Zxx0Iz5@f8wrA1n+U z0(Bkh&k|G8=)p~z()oEMPNj4KK%AA@J24XzYVr!Q-T+|+sKvRWMx!ku!)$8<~wqi!J zNtB}V?8ke|C9gHYR6r>NRGd`z7%h!11wuyks&sRdm}sJfy3uNHPjONf>Hh$IiEX6n z+th^%kZ((CAO0k}jEhg*gFONo_4ZX=Sg#~C@ zin<@ISZ#I)Vx@Ny;EZd4=A#c*w@<|VL~?V;QU32W5t1_aSd7|#PA74KkqRce4&1|! zZF#Eh60JIpcyg^TbyBsl$mKVM7k+ zntZ}ig45C;YAjUhec}QVNCLjna>0UUa`2oDw(MCBu;EIaXzJM5MyA2e-cf-vnk9 zqR`T$5t-~!&kd&)6i%(^9aUgB*O+dbLB`y@%eY_QLP-23(2Jb7ahNIT;EthOXW7lT z$=^`5eY?D7w~m}bjbE6Z*}RO&t|AjRnW0h~Q6()U=c-93G5|T874R6MpyG(zfi2Hb z=2PdL?$FKV&1APu`O9673rs_BD^qmuPq5PsaS^yQ*NT?Oy}d)Hg%k>x6jhL>B*Ro+ z1ib<~Gum=Hv`fjMXr9xA-pYhXWi545t(MhEt%U=jwRr3NLp(6@RJ6Ge8m2^DE@((= z=iOUKpi5fLL#Ne&Y)v|-i7#Y4(UuUm+%BzETDp^+aN(^0D5toSQl2(gZX!c2w&I&p zY6T=$s5R-2OxC@WD7Ml%IYWmf(JiOUdwS`&ZPDfJME)abWwzx=PK_@H#!Ad(QZbrc zZP~o-9idw-p8A3I;5nLrs;?t%!M!(x%j;IgtnrqVw$vuG3i9eTsL*(o+l>nojyCZC zbyzOx%!|9C;3yMHI}9r|plYvZ#DHn4uCeV1%gJR4(%Mpm_E1wS88(sPs@rUxnPE=a zwL34&GF)se;+GaGrv-WfF;Bj|aDIza#bV>~0Zvx7wW1o-t9ve`#|~=qlu7!@rWu>( zpGtgjv8vzd3!3(i4ck=1jx~JV^OAl_#80~RSR1a0E2cnz>#MNxh{=i3I)*}I18 zG1Nc1=SnQvY{??3Ye)%kbRP&c#0;4cML#CaE}E3kCJcScaNILzU+-_3GbgC6PAF80 zoPh2F5L3(6YgKx)$A}x#MI3M0i}I5#T13Pg{l_>~pk>A$1 z7r63I8vq4Wn(-JCyijmP|;Uf-N}(G5emslw&X*4XfXhZawo z(Ctc%M`7&527hfsa5|Zzk+8d}=G0O8wRbkd%KIOGZQDNR+gpk~&dYjeJqlw>)RiSb z9?sFw<2IWKG(>_HiEp~CW&npJ>wNoV(jGP&$^_)i@k<$JsA15B-*rHV<2nbR z9Jfm}Q+H}t`a=s)^J+oyyOYu-$%zfOsNB@y{o0&6h5=p*2O&8C`{|!K`OP*1cRH&& zVrCmwa|)Gvej%(&6#Y8VE=ch}tSWS8goJuj?Dl9#olu=DwLNyi` zNbjLE!n&n|tn{O8J8(luKyD~jdX}V|dsLTOE6onu_XLF~7< z0)Y_MPy5+Frvv4bB(u`8?aJbmg%;ukqpJL@u%n5hG)!qc)1QAwnLm#%(qvoXHkPgK zDK9k0L0Jvzt#Mxj)YV<^R^!iVueV6@k+GPN#8ED+GE~Zl2_b0$x%Xk5g>5*JT56zk z6g6-{RL_4)Z8I*Pf8#YZGnD7N0ZyMA_WoXTSu z=}N##i3YiL;oL9kP`TR{!beJYY#ZIvZ|-=PD=o?EUD^YQcHMH0;@OcM!KZ}@5IT@m$N z=}n$;+Klf#jO$Ks%QIp9yrjAV7dUPw%rU&g)gZ3AnqNxg(BmfAyS7Ypo=n3^(QiG+ z?d&;=Uz)ZxaMUg^K=p0Q+zBRivu@kt!Q6JVO14Nv#xm6WC0ELDDFmRU#1WNpuYNj8 z4VPt=V33-(isM|5ax%Dr>v5xnLZoZHs$Ifu(p6u$-CuH5%3F#map&J~y5iQOnJQ9= zIMblVM*{;IC<4T;?j^W+k>r;XUux_p<#WHcP2+fOzA=G0*Y^B~KZ?nQr7caxafedY zwDk$|HBgS)3}M~n#oI*_CnArn7fpU$bJz0AEukdEO8S>1y>u3Kud9B3w_Vv5<+puP z%tvmDK}3j5JQg&c-_%q`c{tbhCG#DIh1IXS`pV}W>42Hp>Da;h`g;fKRPp`cWPUoH zgz}{!KiViD=^pyvGF$G}-Q6dOw%~gelfPhp0GbYRWs)3+6D^ltLTUg&Nc|@XGq_gn zA+*_jm0Ddbf{AzT$NhhwC-&24ZdkDqSCjh_)B@$sRd9M`D zAkcWLTIYp&lC6(LfU+nFsKSw*QJzzRAt_-boR8Pn4uXw%tC5W)fukU_@iP}dRXGWd>dDCJtGy0E#6Ln~^au5AnlsiQ@B zc^8$S@r|h6C)kC=1%}fdD+uwZR97rQTkW$c<3v{O!x-VJu1fsC*tWQ@{8|iwAahyHpuQ(#Weo_$>KfRJ)r93fA6V&;xTUg)>F1- zw=Lc|it-vYO9tYBU!)*nHzBfNaG`Y+{v&?gG1Y8!SKHF>=9k8t>vG(?cXMoJwnXbK zrbPG?+?1&-O_3a=2&h|aiY;K&1J;<$^ScTgewPU=Q{uM0vD|6y`kMH41C14J+#j4C z;#ApDq55o>@{wFnVZ}LcO2|rzMN*h%p6p56>^ys#3*`X%{{Y26&)i($cku0<)9D0$ zq=G*=ec2rVBZ66HPgbO^ar*xNyC)ABv9X7YquM`eV=r?z3s1Xje@~$vYxBnCJ8}}u zw}xy`+D&tHcMjiaNW#On7@0rS9?|g$a=5w|R0v+}yC-&(;l+3c0ZL*@%;^XUHof=S)p zzj^0=TW%YFbMJ=bZJWlkb=y&6L5&JYbK)yQNUcfFuUzov%WdOeldXf^9P6rWf4H)@ zEa`dod@1@LtwHPMc#kP47UXI*&&~(i-HF#dgMkVNMJn;+h}5ZtN`K!*3+E3Il{V9O zEe9r9X;-3wUeM}4ZW5AV;($RXf*HO|+bi&p60ZLMF(0=KzG^YeE%LH>D|zUD)|VZ6 zQ}r3QIdgk1RPCJ}-*mQLp}#RTx(x#8^wO(uz6<|UvZ@*W>;srCcpd%#3%m%Z+V{q?N-KqY_{Pa7u1)b{%&8F%&-bM zs){C_U*Pw3E9lc0;D7sh&3GaI0PfFi)}30X)DO(7(Y@{{nYVXU>v6V0QhrZs-xPue zZEV8D@xTN#)O$J6!l6QYs7LYNHxUur)3>x@8i4ZQ6s)^s! z(?#eusA^|R}F@4~rfl+|V9wJezYKebt1gKmVCC;=r6I_!yx(x8=m!^%OWLK1PN z;xIPW{Jn2n&^4IA5JX8MwiW?kn$1!hH+^x#uz;orvsBLN<2GR#` z#?Oe0%Zt*v$1D6xgVVqi$T!qmlQ(x?U-P8MT9+LQ%}Y_LhhEHj;^UwaoLSo!I)PQU zF0*$9_UU|@y-rGXM6bKpJ(#UWDSOFRDPR%C8WTQSke;}{sY+2HL;#QQ(+ECR^(iP~ zaA2a}=3z5LrR4;UfTR34@ws40n`sD3jb9+T(=p6|w5Gll0tS86!}xM>yPAua87L@~ z%0eA!w6gBD8?K!46~ebOwc|wY&H}@#ZX!CANgqh>#A{hqDrX2u3m#n!6Qom47DXDB zvQ_gfu(8mJA8jy0@l_wBvZ__o{4Kp{l9S)1@iTr2wGAyIi@I)Ok@C+hvyEx*!2jp8nb8ZaH$YsIZ@j z##5CKc%~zAmPC8aQ}_&sE$EOZx27*xeBbFmlPnyK#k<;O)7@Kn#Qd$rrBx3zs;iGF zrFb8Xv69+YAoUbb1V%@GC5*)|>S;>lHjHn0o)2~7T^H!>`BMIF{89Oe@tWaXn~AVJ zKy1cZ8~cw7)M8U>IuUZVt=cKbu8~C~_}W*6RT@;RTXbxBAbFA+xrJ(5Pq=Jg5M9x( z8UlY#E3Xb;tgyfmE>UCoodb`wkb5%JMR7_v%b&VdmmcJ>CY^2eh4SP1;YW<;seUKi z7Wt`Emv)QM(xx2oGN{*OJe37DY5+1o&A*a-+JZZ=*L{JurG$b;=t$%Aub1DPEYN0k zoJOQCOU^#~fvc2%iN}hRuApXd?)$?Ob`1;x zqPzg2v;Ipit}b%(K4&?^v}#EwiRN`*K@*5CD2(aT-Hudm0KSt+qDWhYqPd@D6^JH; zl9MI4wa1VvmW9*&+&qEwNlx0e5Qtq&C`63INTB}!Ck?-NrK9*)ii*@~tf}zf%N$B- z;Z?JJ2=2laMxu&+MaD9{JoB7X(3iH2{15*C7Q6c~J+kNN$+Om}oz09K9D~rf)A>4H z;#g(#l&RYORrod?ZZ?v#91ck$wC@I(+q=_hXY%@VT-o_)$cO~B9t*Zj?^{g5qy;W9 zbD?pyrk%Y`Fy~_BXkAzMS=W&S5 zI!Q_pH!-+F`NUh_XeeaXpBL0=hVtY3UBx|zEa!~@RhQPi;coh1l_9Y0Ndroj0nB5vuAW3@ zi{-700hoAI0m@JJA}ffz2y zuEtzN2A^q7J)Wef5J%UC;Z~-cBS?!yd;vs_MLkFZK7$8o1Bznz5>lKCJc*K8VmoC{ zzMz)UMKdQM--O)uv{djw8s|TxYEhe&s!g(6(PSVt^9M_*rfE>fjPQ7+X%!l!3kwY$ zp-G||lnqS+y5JGI!oapPvr>?w*_HscNw!)IP;0Z6mX)*%4mO~qIGWTb8iGB?rUXU` zAe6Y#TD^zre($zz@$K8IX`_1lu}_t7OxIvaT1HfKr7H?Q(iAwaUv0lI)E!g1Yx58~ zC_j4cgl|3Df8A}sO_n|4L3N8-R$UAuKuAZJppveck})%DY+{TwnyC3+(&BtmMddcC zXR4h5%RD%uMb1GXX$2tk?MH4Ej3M(7@Zvu{YY^hJU6CbPS!4s5I7#mSCbDaymUisz z!(fQ*vAcuOqL+iNLT$wcyySp2>FHdCc!;{NhJTD!tJax&i${M9(uS(yLG|e1*;h<>#B+iPz< z-M%1PAb&CMQycN$KHS@}2#Vj7-;~az=%77>*S7|rVOY&>*oe?1tUI}uv$%A?j<@ms zDxHn~WIm^wSPjpxr#gyRTN(W>oJ5|~z51X$M>_4?d9M$TZmHkDdZL!HL zTD+a4>+f2fw!tvgrM}u*S$eKHt0an2n4R1EF54T_=6i8EP%E+J!d*!c!S+rsLXV>7dhsaQ z#~pe~`S)l^w(_MWO$U6zO>MN107Rp;`=s};1|pMt4vu}v~UhTYMA2R z`4?&K55_jTj@u-@(P_mcDOZ@&iK3FAuAq!WURyykI$kQQnC#8voZWGnRZQGTdfd~w zC5NAirY2liSdx&9DJlsDsUJouJEkbxZ9%HqyGeaKxl&IOrJdRQ#dNjrdnL^(d?gDJ z88iVxgwTG>XHiBP>VUe28<+XLB(IjgQ+!8ohcQS4SGIEpAn%wc_1Y2ABvX5O6p4#>%l%-N^mbE&~?N3ASE<>>|oV* z7xL`<$nBntIAVVoyT#7#Ypm=AxF$nsM||IS&?-IF z=e)C<_)Y2x>hcHYU9q`mV_Y_SynA%p+mb_#sjHxVJFGMi@>QuMQFWj_op9eDu%Wh+ zq1qlw6LIf+jPP`r&3skt;@?tR2b8#UZnzw_#r|`NX^YLBmr~s3UnvAM>nibA+C~^; zc8`}oTSkfID}S~2eq5%GDUElrF6VzrB3tE7M5OCmi6okl--m6Oi0D*HY{TX19MU$* zjTY0(dwMeMR(%%f)8Hsz-I0@#Zr z=oe^i87yiPL32;>9)yZwy|rznFLPXZE9U;Wia6NzicqK98`EzsmJHq3J#Penw1!qv z4Lf`$xQu;+yuzo3S&ogTUJ2P~-W+_9{quP|&`7CDn{gzcr8sWwO10g{n}JiY6VArFP+ZC-_9a*4eAA=s~7AhkwBu($KqUC7KAe+;vqnoDlcV9*DU5=8|@cx~J5y^^=RYvpYM zlJ1W5!rj{zTfVMeFe(5jBD#lf>BmN_Ih+s!9!?`dAR2?%apbC3ZDA2!Zz{M&RZPGc z-~vWk6IFmH0Pe?=B(q`xQ@oX$CQEBVN4HH!=)!MaN^OlA6JLOkevGhCn97gEAu<>$ zGyz$hD8W?1?X?NW?Fp?q_F>YE0Ty%+5_M#*&vpQyC<9)>Toox21gS^2F6;_b2CZ3X z`fv$ol6%aggpYoB^%JPlL(z&9YP!;$m@0e;)CsSDNZBpP8LPsQ%z> zP_D~TJSkZUXRdv?P@j5;jTMbwk4e|`LgjsfDaVUZ?~$K~`o}`GvQ#VnQgEd47saB> zbCHHgRoOVVXs9SkS5R{At}Cy%BZ^YnAkLKaiE$}uL8YG^4FUaFjg}QR1>tb2c(E}i z+Si!coN=hXey6wt0K1s zRDs=w*$ba4BL4sc?+1+eo3Rnl6+d6N0NCvJ`;fYMuGo-Q6R-t8=T1? zX&hFGnC;tbZOnkyi{Z_k?{Oa+{$M=?2iul9q)uwNW?i`*G)nF6<|I)vV7FROtW`C@ znX03mf-E%)XYV%mBdIBgJ;0+{*gdCI+)TL3&soc^B3u=Q7*WQiB zgVQn70@u+kt5-zO569SiYcFIn!}N#vZ2r|w}4QuVWkgdBJsDe zO4_j1PDu;nJuc9uLJ^$->+mPviBz3c_w-8{3XQ!TQZp0n=pRFF98h;zpi?%QsLR$X zYmw>~dMTmi80_0xC!hf^P_DmDEn8BJwH}pS%1KA+pdF>N_lEkD@>?fs+&6@}fMVL9 z%DF8h_lRw|Pp1=i83-UA^MLiLw3pzW0Y0H$u~-KE5c<1SrR~D!r`^E{%^d~Dr_5?B z1cW41oipBLR37X`Gj>@${VjD*(vqI;MsZ=M(fd?`=(@(`kJJn_UVNryLxFKbh{<`k z3uPe%M^i;fG^H{)dv!2-1r~eUjDdZ-SUgn?)c9(77Jgan}3S^)N87QU%g#zMM& zlzkQR2dhhjv}|=l+j~07`)qDLK)LybBxf8(u+uFwMaX)TIsr%kf>be?@4n>0h#dqM zJn~v*#qDk8Wc4+{v5qAE%`5E*Yu9h5o;LKW%eF2d7X$#Owl#oSEV-b57aoyyt-7L-N*F`bBcFnFXDI7^hVfQFfBfM&| z3Y*-p%{qb>4tSFMS)fq0Rwvuwnui7E_|fLmtnNrhzcpjuj5onEJ9eC05J!uyavW?m zeSk)x;!~tkKXJibl@1i5@WBA!dV;tXST^WZ+jJ9kj-|5OXi*NN0-ylOrxgpUc^>+K z#cB4}R`A9eToqsSL+XCR!muQ5eW4+n_at0eR)$b~#;u52XdQT`EqT=_ue9Zh4kHWF zdY+(iH)eNqM7TS@ITGV?xm<61hV1Rq`EXjCn?2QQQkC6P`Y~d-xobrNwr#z#ekW?G zX6fCsuB&yn)}Y#zknhE2?&!F!O3ulV00M46Nl8qVe`*iip6oFuid34Z8V4b{2qmep zf})@R7TrR!?av4~+9JshP)%L)zM)=YF#^=nsE=PVQ_hVl)z|>;!(vj_(e&|A?Yei% zz8qQmuFz?o02(J!#ncm#>*bE5xVjXN6%tSA!p3g|$K+m+B<_fTOH2qpVF&wVjU(#E zywrADKbk{+e7Va>b;4U83rQ3mdxjKGJ=P_wb+N~aRHRTVzx+6MAkwL<_YJCfMnp)h zil{i}Vn==#X$=X4jxrRd8tW=FggG7J&XNwKk`?aD1d?EyzSI=_8?2P2M4y-=?$unu zIcI2fR7rqR5*tEk*E}3d5zCP2mu6c< zr=lUn*TQSp2~O&Sy}7B;J~y?|+m{vJnk*qPShhc)fkWYBj?Z==0=?jzeBh$g*jamF zTgvm-36+uDLMkxINeNpF0JMcsZ{ZBzq@_o~nu`5cI=Nf?DT^3|%XdE2SL?w_my8h7 zrhSTnXELPqPfQ-u;#G{}D+&XZ{2RiA_}hlURrI8sN;~3FI73L%X2Do;ZP}o%tR(x0 zUL0HeN+)z30(I@#DQ9p-N{(S*~<)!hF1HJ zJjaizbT4neFd1w#ViJ7Fha>xS`#zM^^&`(4H`L!L-uZULw3WtB-(f*#a9JfxVpDF< zh=nw%C|gNJYetl-D)>P;;%j1UYm7~&kt4eGG(V|UyN|rBvv>ahGaEW8H6EWG6)?DO zdn7qdvY9SPRCOsjnn#6nG{}-Mb?GXH<%MNz#Kd>%3hh z?+wFye*L(&OqXB1-Q_I1CRCNCs&U0-Qhn3|AOIM5m-6iGgmDg*w?Gx6w$?`S?8zzW zdDNd0qP@Slef6t^u&xV z(ux{)IGUuuwUL?A_2I}&KtY#kq=g)Z?_qHwLCvB$8O;u%Rnty`u=e5XGtt2^yof!7 z6!-H(%_Z^CL$A9UrY`VG#m{?&z{4^0qS~mO%?2cg6$Cgfe8lBNeV8>7sGPA0Oji5z zr`LlfGJHs6E{8nuvtE@5Wz|I{fRI7e-9ua`i7Nwz3cUW3_vM?MIjfBLQ`9J4;p1l$ zJ#sFA_ZXJHl!Uh!W<$-8rJ#{QYpRq4oKUy@?Dm!pdODwNK64d_+UtiN3_6 zwfLim*e*YB;{CmDTY05S*{#i=tTlw^+&9Gk05JPf&}__L6!My+*!zEPM2w)$-?zk$ z$tq8m3M+jx{{U9l#7WFuyz25)gEsAiZTDjV5_fXLjYKsSpq_{xOMM5nm~wA!&+inE z;kf?*xDvZAGa?a~i*$Jj2`f=7G-wB3-S%LQ$lP^w^%|!mZ`l_0bT*!(mm28;;DSxA z5`}0h5;}+b%oa?Q$l~MeUW)}_@uLT^Lf39Q<4!{L8O2tu?Z=ia$rdHMa7jxN?H2dj zQj;B~l2n%(Ad#ygYfM{Pp1Y42T-T6ES^3%)66XMPV2Ud;_odHF&mc1Q18nzM?QL6j z)l9R`?nQA&{_x0;^$OE}+w4d5o9t01EBbQujk0!K&vwf6&9dh%+@{Ek-ru)0Tq|%l zLPJJ_JWk*@22SeWGtF@NRbzGSJT;ZKndCJ60aZwleIPGO&w5_kP*$IXq*-k)4}DhE zMlbTglm4r{t1OE14&hS+eTml+Poo&jeCoYBZCEX~bUIhJY{Nf!Mk8Hgm3IjV`c#uL zI5z(PqTgaT*7Ed1(#?2RYDG}!Q@5>O@KJ_G6P5erJN$D8{TBNXo10QZ*IpU~AX*YJF=-znJ7VE+K5 z)MdT@0O;ZJHSB$^g!U*}ZJ(*3fczUP_lk5gyMOKkq$hePy0(SQ+IvMQS8LY0aMSPm zycFVczj&uDzT5u*bR^B`Z}f4;wv)RNv9`gE@Fcqu*>5K#Y8iwc!-L{1^7j&S^%1H| zIeCGxw=w`VUpW5&n7^)z6iu&x9DiwJXYBTpy)>_o@DhS-WhDG)Hl8G(bZEg8* z*{_DG6cruk5!X|h0L}K1l_KKng+vs% zXa`(G%Pk(Q7V-OmEwcP+v}NQ0tE0WWevCU~n&U#FrDNHsK}@!o$@3edt)AjdFcR5x z@Lq=WJBrP_TY<(Gl2j@Oc6d_A=|mSkDN&%53ezBSQTAd^B_2sBDr2E;IHG(iIbu5E zK&36)qS89NrG~4Rp&sLe82TqOK~SdXt3}6TIvXE-0b^hIq<+j>y4xkLoR)2F9S%T) zz^LJePN&^%L}^&3unbkBG&NUuun?qsW#OxgWXE{X_R~S`rYzRCA?@iZuaL(UKdGsa zy7qe$(&VUV9(rn2nou8hA+i=Dh*MaMp&sj#lhbcG?@vvl!P| zVcAyKloKj2C?n%Q@YgLtC$k9RE(|aCg+hM0u=KmlZ( z{{Y!HIP2)lLeN!1kZ=r?5-&N^fj_?t!-bEHu-ru z2l+zp?ABBrP$rb>amyjg=_H==P4a1$nvvHJ0%(dzXqKL!qO_|{UE>Q!!6z=Fi8Yw- zA~bPIC?O)5e+a=I>Z3KZLI}pEFZ;MHQhfymnCeiV6*x`q144zYB}cs8!UqNOLvG|% zwJ+fI)SF|#Vqa+2@fLd3L%A`3?6P{77e6I*C3jwNimEyswC(ayKx;`-Ms>#F-iZ%P zYnneS3=Vv?*N5v}-kE8R>L{q`Us@d?(-GWZ7 zcH_+znAUhBDYUn!C^kGk%p)}Q=2OO6wY-%y{J>+D{VsAQfJT%<3htt&wbvS-X)E}X zS2_M?n_ar)^Zx+Fn=&T--+Mb|+_z*siwAMcOm}yD@JvV=kfkMV_>qwyBod%1MNT^7 zab%xqXz5zg@&^ILubSIG0~SL4u*m*~o_-4K-TweCt~NMtUycT+3H zQ5f(N5R#R7bm%dAqRF2vc%?=Z^8k2)!R*I^YcoMgD3IHSO6WCq914%}ktvo)Q)}f> zmq+i8hXw>1CwBQMBhaY+>tlWb)>hF-9m1(URveN7vs+~wC{FFM$Zo_#HjC3=h+7lk zZPS~~e~M30>^Lxbhst1D@LtUdljK{WTqB}n2h%zmzXR#&oVpR-JFq8EsSYwk>N`TB zZPT}=uuQf=lGA3oK+Pa`;k($bczgJW={(SzUwATwwGQTjS_X-JzYk_(0M}I$FSm)S zn4i7ie*5fo>+${HF_I3b$}Js!{Z^jt<-&Sp8;Bao{f9~UF7t`>0{Fd=HDOf{{Vq^KvbAEyyH&gx_?VG%XdJ}T+9djW~F zxHHJ!pg0B1-&EXZ1CjabkZ+tP$2V9zW<-P~yBk)?ilnBK?pug6+U%1`Ayt&NC>o^_do*ESb!}P7rw@D2#1By2cmmE+^TNZig@g9{b zXaV$`Sgt#di;P#y2kBLNKFsH3bv&mJp>)2_`9AJzhX_a9$9Ss6%yQC)+uCxW~+j(sIOj(~kix?evk-&NKC zn?Pj0=3Q_xwXRW_6f23i66%#fo?k9Hd+~C&@6H+EXyAP#{8bij%Kqh!wDQYGFysFK zm-VLIyYklMq=YMT?(Ms^T&S#5CPddtN$zC`6{lt;E<3?yMOe~RbapQ&9Z4M4{eQ6_ z^6#Skqr}!LS$muq-P^w6toF1!L+r52Wc)?<9aTu~J$o?wZtrWXgU|4&!Od4Uhi_oS zfz>Zuhmx;M@VqvhbMYxp`8jz@WwerOl_*KYx=g=9&VE-^rE%cfRhgttpE_J?yp1B2|=24hd`9Mu@Sf4Da*!N4~|hga9t;Ei7@VaYTqJdvLcynP0A@wl_I2M zKsCz=WG}89;LtQh$J$#yHkhisTDLJ6%*TuQJ|u%hShYx((Qv{ z$Kw|#&h*t2m+wlR=g#h_E+iCaN*0C^mDl0zr8?(~Gkf{s`$u%7)Z2VWf5P9(_ zdwp=13`@RAyj^$-X5N#zZVY*L$oDli#56h^LW5~(dO~Yed+?Q>Uh%O|Nm>Ohk=dBJ zW~-y=B%@DyJK+0@gK#z;;kTs@K8J4xdX~e%wJSoKMz07UcHz8R%J@2tHBMyhRx6^d z2_D_)d()@k@tktQxZSv=hu*lxZgR3yE*7W&r8OMsf=|10m7qE(`+nKsE2uT~pW2_6 ze@H%&;o7bjP2AP$W0#MYO^P?-55#giZlr5j4bxEs=c=RGhBNOR#VW;OwC$GOVg4Y{ z`bvhmo6*mwt+jO9@OD=8mu-oD`==$#eN!4?>IDTTlxh{NDc{|T9^c=2FC@BF){3LK zdsBuNA+Q}Brw>BhC$YUh^UOW{e&u)%*&4J@VlywLP=k+4TWzEQ`2dg-a`xh_+t{lgKh+o}ZF7OSkMB{0;4d8Rt8?x323 zP>`UU@tN&w3(Gr6D$}T2e{%B}{EkL1E#Q!;e)Zc4+Z^KcrWWdQO|N#rl9W#}1FLNx zF17hixv4;@pkJg zo@l)6_gQVJk1(^ROKR~{QnjJRU3s+R$NSE2yg9Ch#`glxIs+I*IfW5T*xXUGIiHRt z?O1mWsX3V%cQcdTS!KeZp%1oJXacoorz}Xvdv$S-e_87ARUw11u+PN!$47}*GUp3y z#O6JraGAX0B`rdj%Td;}g6VM}j}RnwRAQJRoY@1?bSr3|CuH1{kC+K9Tvgr>EiQ6i z=}N9P`iO7S+vBlSTxd>(u?iM({V)sg+z7UMO)US#;H~5IkCxd$PqZb6bHI1+sRc z>FvL%bu?17^L9;)t<1}pL{49r>t)Sg(4^BU6WQB}WVSBotrqs}!x@@^z?!O2&KoOh z@13^PYvr{QntG{2!zWU%!j&gFj<|x2#g1EbjzzA3dY3dWK9sDxI$EzA`;pt*HW@Ax zZMC6;BIzLoR1|KYC*G`B?7Y|T6;*t-(_6(f;xn{PcG$e6cO;NF`V>4H=hL&(DT#f1vWNf6x+Z+D? zk8T@la@$&MGvGYSN>YGEkhErMl~SPMFzp$xD=csKO0!${I@Zae+IWbN4+Ptj>hpvc zkf&}tT5}Mp12Pv4qS*lEYH5WVvF{JVAoivccWWL#C`bd)rX82-m(ix*l3csUa@3gv zLZ+(B>)J^l!pat)J1Pbur|ph$(g!f7(Ra^lm>k!N8c}lZ)5jcI{ZD$RANrc13_p~{{0J#HU?5Ou1^q37bXQSgCIl+PY1Bmp3G!Bdn5JiNV+FCbiPPbm$cYEh`t zJ-8ha-*N(a?an6NyR|}Rly~?V?u1)plH!Lr16cLZ`9>M6Uj(n+91h5gz4Uio5L^u~4^gdvmCr3BOII70Ay zsm==Avh}Z~wNxFAQREaYIkvhI4{^mw-xBJ0sA8&g?^aZZRx6rnTb6=_E3Roc8EVxX zZ6#bnop?_6t7NUGaSmxVdz^I_D@`dcBp$CxmpMsC10Bt4=PR=>rwt37op}vM<36CE zl^XXPH|lauNE)QAop-ufCk@MIxjkyR*jCc*R~YUsmzj_isVhR;Ac85`gs*McEPW3Z z1u}+bMEbWvx+AoGSnQ5+v%%xvR~8g*ClV6Pla<&&YV8PFJ3)PUmEvyO@QoBa=4fZLnCNe5B8$4IVOCMe73ClNcM zVCM=iVnGZk^~heP{U!01`rPy7dQx9G^Rp6?)#q?-YqpK7(&8a4vKfNX-6u){byb>^ zm2l$6ZeB|eq@68398?wCz?X$jr-$pOu|*rV^GNk~e2S}YAKo_ir`^8UDT%lCR@}D2 z^4mYUU!L5h8h52B_hLrdVp_A~CFjy~_8{|1#lp%Lw>Jjqd^P|LL8u}&k6 zEH*V#y|`$kLYVmf0G_$CiEWn~9~*L=>topN?s?a1HNIJ9G?$74Va23{QDGvQlSAKJ zRGv5gIQ)m+ zTD2)|IO$T9qI!~X=2^Be($PQ=KH<+jPBkSHAty7LI5`Nwf{jS?(P)eiX&u*=R6EER zMnIk01Vw}{sZc#S3?!WrolsM$EvZL51Gph^sZyTaMON53f}v5<5%fHll#(Gfu$8q+ zsEui@Gws7?3L+L%rvwv+DEs;Tmn0H%)IXPcwA(c>HkO=`70 zli7JQahG-U(O9^Dm6h3O)p6!zwC!_47R8kR0Pv3tQzh?{hXw_rLHSYyRq~pp6#i6} z=UX0kHcqKIr547AL*A#38-Kr2{{T!OKa}VX`mR`)`AWrGTH$lW#v`ZuG`VVF)Ytr% zl78GNZ{K-KspSHiUj9+wEw7&8?EQ<0p13V`PfXjsXB%ywP_X%LdB-bF>NTN~z_Bef zDP~)UdDI8UAm~b}2T(D!?H$=RLJqgb(d%6Q0J=Lx9zOG-vU!v%j{Em~I}7GzNs?w= zW+_XPC2H2StzAl#O-audo2$~NFk50c#Y(Gp2{$X`g*z?9CAFIA&!Fwg5YkBEuMrk7 zbhS3+uoCiHN@-k&W*Eqy1HlaYVYbqN`=j;Yxle*cd3sQ>T+Tq?95hCa>8mHIyKvDs zNlv*_r%B7<8RNw$(gFa*ks*46WP3@V!oE^FpG2b`)+zVz1gH2x1mQ+TD!lm!6ibmq z4ABwU)Kf~V;oU${u49~(M28eKl6}J=fiqDT$U^zZY=V4B?C-(`WO^fvIHiK)p9uI) z1@5XVnN;Djwq{9fYe_9Opi&R8;ITZork6s2Uw==$y?f$q;kWnJ(+n?gZGz~I#Dr^M z=HFq`KGrD;*j8iR?CDUvg!st#h+&uKiu?*|)toxSo7$+GVE_PluRUJ#=EWVY&> zZPwOMw54QJ(2m?h%eSDekD|&Z-PiCqw8vT}431{Fl1jGdQSHziAEaQ-!ooj&F_V4V zS6Kd}oaZ(;+3{_~k~8q3NA(;y7+85okvs0_@Qk0KY5v^OqovKeAa{5V>rOrRGd}t+ z_s!2ZkFgz3GO?9*v$_veQ>WR3w+P?9f!o$fe=F)eb7oc3ZaCXT_X2$7Fycu{ zmQTcL2(MoJPvSC0Xz9!>>%5yPJbn0*7WA>=x^Py2LbpF4GMkKKxUw{$t*V~-3gTQb z{MJO+pWsoEXNny1icsjKNuqQ2WrB4ijKmbK*4?%_Pcl1dKH*#(qj|oRaT{L3gH^&o z2d9J+hV>{7&qzn(?Ond=#UQs%+Ms^Hfz=@E!^uQEtG%|4$adAWn712ZM1+3 zO@#riN39QL0rwp=9M#`$ZOf_SGo2XJTXS9;XX;P+lbkT)m!%b z1YCrA*9^l2HV`64{A>{+icXV6ouZzrw`LF zsTOYR%RD2w#Aer0__NF@>;5JL@7z#7%?dT$u+A1!$a1pw4@>e=pmXM{5;6k^4%mUZeP1n0+*Es9bT(wq~}r=exje>2I~M zIz05M3$CqzwK){2DOR*76xZv-{@2}BnP`lV5?T*E6>D|uYwRu1hUfs}u7+*+I(I+UG9>B4U*2eYLka_l!ITCHxd-lewVg<`a#e7cf7xT$XG zKY~`r*}wf&6cYz(ZcD-vyLFOF?GcjnX-W3f(=1?f<=~CfC3cKBN#tcfs!=>szMVQ# zVR0n%s*nfPP7)dGenorxZspvTh~a)hRW({$My}O7frl*I@OYpbLgeuX%6Kb#E`{a8 zj8A*TRQ~`pVK45OSHH3@62x8vzSTc5eu&5M1fkXb%oV%RF~BDyVM$67gWP;|BV=0D zI#unt(U!EJq&8d9RB}1j7agm-nqNb}tlxfi+xR8z_=c)KaM!!&%wuYfQ1MW?j(D8M zbe>4KStJT|?ZLrCl{(m6V>GV0WImiaX-wn-s8p}5aw7(7sEV+)P!JAh2J;F@jv-GT zl#~Ae`w6jVDs;`y{{WdB#Rs@=rHNWUYoi~Ch4tI$J@vb2-)yZ$fl*BfsOZ1hjwjt< zXug2#3iK6rZ?U7f3R^C=m4#%GM{R40<+(hS?)jldNrt6dDI|*0xNC2bV_&ZX=6q|+W}H+3bw$q8El_Up zml?*Zq0J>oSF{n=7Wh23vY@Qy@sqMz5TwfzE}zTjb(U0o)T|{56x5P22XN*IRm#dY zx*bw&i*Mhf>QnKS%u$;4go+-SQ?3bpJ6x2yMWak`|_s4hiD zCZsjQ*V3z2$NvC*r=eW8T`N7b7|v?78mjr~W^8y|Pj-H52254Sg4~uWQ>hD*kVQfE zV%@YMe796-p;QjxO4ix?!l851@lA^vw#V|BbxKN<6eT~X0rcZglOK0lbu4-*tqXnt zkvMWbz~paH)-9d5_QO|Qqc+!jb?v(;Yju}o$7QuLrO>3E6;(aB_Hs*`TMnkX-l$Br z{jHtWQ=#qmKsqbeBht^MgubFUI?kmJ^}b}_Ks|s5=rNr~xN=GtN zu(cAd7q*K2;CkQ(*!~s%;mG~+y(Y}XJNE)Yd(tPRH;MlM>pcYt{*0Ob0AVj-cJ6$D z{p3NgagP1KiL24arDYSRi_+CUbr&0&NB&;}Chy4o&y0TKvBMzx4rb}UA?QPvFg#ZhH9-#SoQ-#XG zvIDkWKWam*?U)OBPPnK{l7y|rxJrpw6(pQ=)(;FVq7QiYsJOFo=bd+QVvPHhpkFqA zsCh<5CGU4_P~TIVcEIzI;I<8X$F=Bj!A|lhpJpO*x6Ybgk-=%3TNukrZn8C^eLs1z zdT8RyP+<*RdAEG9U12H#N!zy9fzslW)6Z~Hfb{na8U?W87!xy?;V@2~2_J-rQ7<;zP#JrU<3EJ{MO`9u{C@rUtD zSZbO`&Y?v8k927wJIWqoG6AXX%M$)?x}hH#>NH9B?X3n|X6XsB=FljF_TU`@PN=Zo zydILir+*`#U)QcV^!vFuUTwL($0v#|K6RQa?yNYAHRWq{0XZK7QWD~5Is!ZJ5^V^w zk@S;77FoJGfpxX-equKDM=u{({k&DK_1n*B^<2I%LTn1#w+=ry5iMzOywrrw@~J^U zQ>3-hrxyK*vwsI`#cR~C&hYmco2F>wr3*h|ZF)NAcwUqD{@~6cnX?vIo|$ zJde%>+K{C?>8kl1?8!o}R+4?*0-^TfH2(l&UHw7z{>9S6^G?_Bf#3YU`I9<#=P_)E z7UQ0ruIWgrAl@L-pX8xmqX!+2cl8I>k$>ior2haR-&gHSsb8J#jB7$YK2B|2%f3eh zd;b8t;ln#5dB_t3`KqS~Ts@z)A|KA`+AIG6)*3*kXX9=E0QWWyJ0w4-5r6xrcl2HL ze$?KV`Qk}Sg)w*x>CTE>-AX^`M;Lt{WMTfGO(gu%U6by@ex#Pa&lhm1U)}g2h105j zeqxXMEDRWSKHj>>no0Sd$bRf!U-%~O?EK|-?)ut0w`4WItV4~vt&?r9T~htU$0Md} zbw?`*Qz0rT3#|zlmBw~*(%yE-=4ub=%~!W4=1(VgYUoT<4jgO#3cvag^-Fd1nQ-L0 z<9vMP^H%G&+hAK_Jo~Va(%{H--AYhGst9n@0bGW7vu{m>9#^DwQ>AD9tM4@1Ys?}( zzOVrLEBjU3fAfsw`1*cl)5WTbQnaZTnWrk$W%BCm#Xxp8&-#bi{wnzY0L|W1AN@F| z{NMf*3!j{({!l{4r>Ht<*YRneu09q20AsAr`iI&6E7E>%@?-x1+mCPfApZdSG28Hf zPXWMbfBqMh{{Wd7Q~isy`{(rfQ&~SaSs&;RZ}=$Zn13-%>*_y_ZyZS@iec^7H#4yi zi?bI8-&%aRD0M0F-EAVODFUTmMkjXPYhlIQ34`lSN{QSL%uU|our`j1r>mWR!Bs21 z&)gd-Rjgm^IY?V8b~0W|iE*N!RFI~Ms7S6gDQ^7e$Zr*dM{Mg^m|Q9=s<0cknK(|- zxgK+G`hQIAux+SfD--1{FL|pf^Atey0SB=4G{qtfy>laKWDP4#pSV`m?x&PoD4Ba} zkT~@=`B$IzB?f%**lyVE3X*`8>QGToHKi%^Va>({cYB6f4rv^A_kc9Z^5&^yb@P3@ zkap#^$tQ==Zhpr)JLr)aXOm`JvJknP4{I>Z$>l*jhrv2q-^&iLKDq01uOnCR$>8?q3Bd!FkKy@ilC-`ut zE4wiD`FCtFzs8z=p4An&I~Q;p=GeSU!^@xSKT@FA`!?j+mxp%#@vzO>*VyEt`z^{7 z@FfB|{oItHM0XKTdk!?K46U`xdZKV}^c9Kaw*BR#of9$4ccfGPKSE6TsO}0-C)h?L z0Z|UOa6)SbPnP;nRr`l3`>-h&#Em$WI)H~!=K^7+rv|mA&M`m*r(Hx z!i{pUSmdfGd!V|M z)=VSupd71C(T5?xsuk8cOLC)~&9Vyo$(FQ+U+T6glBwD-LfR`XA36;iB4FAt(`>TB zCpJ{m1l5-Yd1c|k4RH$BbB>C3d#Xr*b82#&nH}#jQUE2gMN`{KVPNG znc*W!yABKriV<|0gvH)_THkwNQc)cub0V&-AOhljxpf$gy|rt#3r*S^nplrZjY5Lm z+poiTIToc=sz5X#3`0w3>aBV#$dR=~E*5zAM*jf4v-0zhDAmORiW%3$GOi2qx=|UK zLNq)SkHkFf%$}S#UAuO1{_B^v?(tNDi=CFGTYV6`oime+V{fJ=(gVQ21(<{{XRa9J{97-xos(=1ZMbW?UQJg{*fPpy^+p;}sFuEf3XZ*S!a(m5r46)0 zgFyNfE%mu&-aEsJ1)Fa=;w{OY9o@#u8vN&Crl2RlLDQ^?`!Q(SGQ`;`{Ys|MaOc-qN7a@|(m;hM>D^7ERWbU5Oc+KqcO5uP**b~)}Xp!Zf6xwFwkUL1Yk zp$*+;+dGDO?t8xA>vs5oYjM`VTY`Y-NHixrYqkK|^v z+4oy*lEuBo@UgzodA%+n!x_b7T3Kk6k~;{caWA$de2waOp;W%$*~0|=F*>bKM%=W; zl&h=n$}z-fk`GXjv!f)}wx_ce%bCEUrP#5KX{z6jHk{4VDfrQ}GTSH$eM_!iUv3b{ zU-@mV6pjZ3ZXH6pVYPie?~SL$!QHFNE3sYg8fHhcM4sDmoKk=qTm^b{6oH*PF^%Oj z?4IcL#7!4i?X9TG8wT`D0X&f&U9)mTEzb0a+nHA#x~3wSZ!S{=wBi{^SO-;WtaoN{ zA%VHKEj5gV3%O&v7ar_@gvfz-=Nkm?Da+LN!3A+3xB|*h;%SmpD^Agek_>CP1r%h= zv*9HtIM+5JAU4ZA(buE2`d^mZZAU}PE~>R1=A!}TV|+qI(PitPDb}0EHN9uCJ8{{v z-;CP`V%afp)C{Yu5wv9TnnNiiL)aA0K22^+a}>xA54s^b8aQp^t++J;xs2sQYc5i58$!0bY;C^ zGM_t=WTC<*R3{~h`?)XtvuN3Kw%m=sdAz}vXJa@_)=VWrl+idQSk+tk(vaP{wTHQkAhk~3TvYe=trB$vO zKXKXG+(yT@S8yo*0NEM5jf~PZPjQ6QC_`a!x!ya|Z0#GAnJhK9BsUe=aN0}HGKCr` z#J1r`R+NIOSF;w)&z&|lMrMkTvt5C)9hu1sQfXAx<(<8_y*YAK?`>K)1Jf5{c`CGS z6s1v|b)}#$`~9n!bS!xkIu#&nzfjY88eM|j z$uEh{*`8>AG80F{Pc_{EyZ?#%21Go-e4gb%}PO1%|OdMP+>U;;OTn-)oPbtg5F*AMIi(Q%WgsW#d7xR zw#JyWGalz9wW%krm7K@F8*VI8$)xe3?0Rqh!V2PFy@m#Fy#K|F;jG~n#OK{NAy*lCw{g9SW4OWA+ahVCOK_t;3 z@;9vwtFWxD?cItR-rJS2rOn{`X)#z`G?N*%gQ}mzaZ!hDg8rup1=rZzyW)UG(^9!* zzPWpFZ#(ph<7yQy~(poEnjIlNCNdY;-F z!*Xpu!v6pawYwTkmQ56u5K7Xz5G$uvRmGC}ACtwF#_FnNo%iB9h)E=L^c8;LLgV%$ zDo6748BV;ClO5P?sRSt?5#mYfjIPfsbSD*YrF>TDmBCp$)zo0EJQvRwAn}#P4*1?N zR|`umT;~`OS|>|P84FWsPMoL`MmskJk`A_M^e>rPzXtg4747x-wgK^X4r_zC{$Z z%|DnAsQyvnee=Y7&c|w#ws)Pm_;(3Nw>r$G8F4iwwTE<$ovAph<2yx)u()l+M29tA zGd=U%8F>C)>YWZ;KyorsPzOw_o^;E%7owc8^k7BHB>NA3Jd|sJTb5QS;H^4i$XxK0 ztTr9Px$cpyyI*e4-51%Kgc}XA6sv^S%0{JmY(qniDu7hcIbuHZCk?wgr6HrlrLAn| zxqn1WE_Z+xKERC5wcPC28J3IfvQ?JtZ(o-_)iOJ=B}a4sN*!iGP`1HUB*J|dG8bjF-U}byB&q1_yVzWfr%?Kq z4kM2g&SVl}l9bd^y-pnhm=cy4Xr&<}=S({Zgy&Vd+HEPSj#+y%>48Zinn4ejH zu1LN~DjYu(M~`;Az0H5P3dXm_X-ZK|XjY{fB?uj=Q2=-1 zFDbY!ESc3SfC*SJcQ;Pyg{8%Gj^*g9hP^Fq_#2niM$nuyHYQz#FVF#!U6>Qz|R+i)vv*WFL*X_W7oLJlY z!9q%$?MM#Ouz{Cfb{ov?D6clwgQyi-ZtN+WtH?)$d5RS8Id#JYky>Pf+@-|m$YxnM47 zUH#(;lXq^_+SeO&MN4*NvNV^96Q~U>B?=~;C`KFP0VhR3v6M6)TD{iY?Z@`c1-CDA z?>kM7d=xdmWZW}a=X=3dn4Y=4F7e%_ z;cmThYd6_1IjBRlMU^=~fzqubPJa4gUgu_A&ic}d7a82EOK7NdP#)?0(l~-6Pe$6D zU3%S516C)?c0IX;XWE*$`?^oRQ|AeZFg1Lcdhzs7N$ej zcpF~Sw$Tzg$w*tK(!!iVO$Z87yD=m&7`rk$zy$cHQ@ArXmFaV+JO}($alKx*F5yz$ z+}n=fxMUK1*Dez!OHm}}Uzl3)JlG*<1&ssfRe2?u#*#R?o+O{OHd~0xRr1+GbofG0 zk@_&_r6W+O)_ORf zu8N#pcfQr!b6=fs)rL=xO$u4rSwPjP zeR0{gvfZ(nDVBhI*%FYFl&xe?WOJYxZ#r{kT;o!(mimu|op6Ga^A)G`-Re&F!?(EG zdvWW(@r~h2YjD_|CE(j{_!PF%x{6Tv(a}TOieBN{@yOn`XyB|kZ1dxHL-lE01lT)s zYH|JVc{8!!OWR9xOGWC)#TEArJjgy(R7s+G4`wS4h0g#vKbWu+(76Y{Zr{us`+MKi zbuR06p5P}k2h1r6`w}s#2bxEtA(QI7uPEpr=}3P^IWQnnu`andv7NCO1*Gy(v0)&Rp9nOd9R)EY*CgBvP=TU_0+Zn$ z%r8`lsxpLx9J3=HMWIGpZ6!xY%dlV0 zO>P-fu*nY5E*cZnU-vLtrpDOwN0Na=gc=UaI_fD8E6|O$j_%Kk4TOnc#H!bTR+avb zOeS6vsL<*)M_lmQRI-6C{5QQi0@U2Aj6{z>q z2`?!YAT&WDmBR@cR1Z2QWxFI2Mn|XDfCh`uxCJxp&o!5j^A$qUMHDjY)M5)D;ioUaE$Ar_e=Ey`? ztffnlA;qN)p(zz5N>u;=cARAqM(3IZy3H_SYE^gNzOsEbag1Q@?dE$8y~i~O@ew_l z4?3iZ>1_-Z6(3;5qaU-cZlvjG;;329;^VAaSCzy60AC-SOlyYIxp9A`wN122y(Mw) zoUfxYhu z6_8nU$ddBbr(ar%mZdnNwJmB@N|d5WB$|_%&l{v}Y;5Uo1&hOM=boo(R9?1RxXCZf zQGH7!gp-nMg!rd&Jh8M0E0W_zYPkgtqJz6EAFhEkfsb>Pa--(TO6%e#w8T`LRhlMI zs40I4_Z%it@g}^pT}P#V!GfidS&HMJsL{5EX~CS-yIcw+>+sp|w1D%y=Cn9$UeFQ4 z2`IXpec%c358)j?tPh%;Ix>`_M+7BG2EEuTWI-cRhE0$`r`v_n6k09dnp4G2m=ug< z8HUMFOV6JzzitW|0v6QiL~W%xIrOyFOrZFbJFA6Ds#2XDAwMB4n{;Z_CrHb%lY=yL zDCfu4RI|s@R2q53l1WyeJ1S0Ce{CUhtL2;04Of4^DehL=UnRbHGmu@5sMkpX_pu+6 zlpihRw3cPYO4Xed0Cg$v)rv;(vbb#|VW3ZTO!hK7$0!8;pQqBgskm(WOXm?D*BO^J z8UzG^oqdB8yKBpiol3Uf;Bn>pFnbX8t)psZPnmCzMboQkCj%w?qAXsPCfc$CMg)r@(XOrjDL|?N-2kMRx5_kB;8#~ z)QqL2VkwzXWTmt1`H z+!xPv?1sO*SJX|NiGjH2#RtL^nEj?Jpz4l=SG-pdpNPD3RtI9>i|2T#nXIBGfJyAy?ZF0(=xlU75<5FEv)MDeN^a?`W6{(FD>o}`+p1r?!-!Fu z!?GD}p8aYVMA=*(4JqBWdmkU~HGF~ob~o!L?)AFM`)2hQaK*H2n;c25Ly+8-qsbQw zldEW{l_&-2T2xckgm>ey`xgt~#yiXuCC84Om(KqH%x&8*GvvkZ0MWzNS0_-?fu@=P z)EsS}IUkDW^Jh08QKY^BDp&(AeiC+40nHR*E~Gx|N@;Tj_}j1Aw)7h{ z>6te*$XK|=X+dg3UR6bD(>!Kd<1-tfqYYO>_FrmE6~|PqqMLS8E~9PQ{MDZmD5tbi zzRvt|UT*2eV!ndmt-yqOlb9-lNIow7@f+s5#Yp)9VMTPJPh2eo#2zS*heVq2a4M;( zPrDAjVEClGq+KOyAt2PAf|z#{iIkDInvXeN+Zyd)MWs?w+?6;NdRI8#%rZ;`9xKB`P%H>|*ZB_;u zV;Y39PVfV!G@ z;bbMXUQ&W-ms}VfQWWqCd-R&JeM9V_TG6@W4|GNT+-JSW{{YbyqISFg0OkOC*VjLx z5TOHoCYhyCPtoES;_cVKF8$a)30CWM?o)3+F2rLZL+c5ttr9T>WGWT2D|6Cl5FY~xgDtc{w(MKFntg7i4VPqGBPUOY(zHp%y6)ORil7?RW>}n*PTT~Or4{T| z>e?!!a9{)4T|oVK&i?=no9>mboA~ST1xmeqaz(3X`gXeBBixvJLax?^W5*65N_4p_ z0ull20=UupXNz;Mz3nvSv99#)7F**wm}+auafP2$w$vq*%dtP^qP~8IQd3W5Xlv}o z`2DcwpLEr7gk9K;f{$rOIM>z&%HrNtv+_m9Y25Cx;KsPdlL|MH=MSD~C<;k&KxL;f zTsOFz?P3{j6{Nc`tX1DM&s{Ax7kO-(PQjf5M(MXmfSZ%@nn`6cP+Kbjy3~Ms@bhnWhI>o4%I8!C z4Z1%t)|Zw~g^VMpQE&^^9`kc#Y#Zh6nMq9RlPI@sfCUOx@^r0U$_kuon}R_E8XZc; z_Eo67yQ$&DaHDBmE!Ml_t<`i$nGUC9~ z8*Yfxtt(B|_~wuKrLD9u%71he^{3N`3vYc!+>Vj!N&B`!TXH6sIs!X#rW<8uEuwdb zi>;+LrKDaW1tgD|U}e};2fq={V3X)h;szH`@J-lKU420aQU!ro=kt+FU`KSmPr`a)J$0yIVxrMRt5Q%LG? zFLhFEU}(`5QPfH7?7@L-$ni}b zZY7i-1+}34%z<3{1_sF0sQCubD=0mwG{2drKRMN{2Wz3cHm&hS{6z);=au99DC>iw4zG+ z3wB4EQWoQFwuc#WSkwwN$0P&PXNHmGcLSe5&_c_t_SVc}DCZF2>qvuZdMPm+lOFH9 zF0fbyN|7OLu*j!*N)u0hB}4P4hzZcP+D+i&^`>mywdrALRmX4f)%S2hGzM?;Gc7lt zdNmS0tQ(^%`_4u;xG&soLUFOeyCUzkZrkF)#L?K>wszc9nKG^->RD4Q`Lt6-X)4HO z1#pkgT(#k@bxBfDW31(j$i@HytME6U7F=yjTqAD2KeoYi&^Egakm^b7Bh63f#PGGa zRm*&pspb&o{l-opsB$jJW7?gwG2hVs5cyl5N_21R`4*32Om_bO#sa0zPusdjUzOPp zlm1Gcc|+{Ouavp;rV`|=`4*I#t=GgZM?qI0PI>xxkq>1@V?Sb?Sr}KzoG5qXtxtkW z8=s2grA@td+|Q&F;zg#;!9L?!kEaQUZYrWAzO+9UsktuY#1c!4Km6q-tzHF`*_#9C zLBru4$BK)A{GSM+k819FmCh@t%9P#C-j-H`kU$<<2_D=X ztjJ2*hXGX9TYOEdg{7qap0u04H+K|WcCswnBVt{4z380Ld>r~TmXY3^m5l7NmolbM++)n7*yTS(-#*s4DWKEX$X`Z0;Io=k*k);4> zU0cY~pcCJQ?b}k87MUrkes|TlkVx1^3Q-4T^%Za1&|J1!ID*d}Kbs-O&|i|GK4IlP zQ}>BpK&pd0hFB}Md%CJ*B|U?=5wKIp1N8MP)5msOXCiTxhj~A-p&8dUUwPXUQsjR# z$tnRM!_b;!PBQ!HrH1Up9#=!*EbT8bqTs{xva1@lR*84k-X)SaEM4UHQ7szeGo)jf&8iHK9$WSM^(J49NOYn=){-ID$ z&f@8$AQex&NbWl=!L6{>hi5giR~$d{OfdV8CE|4HB)BV<#rFcj`JPTr7Px)tSK2@0Iu@diIKagTp>L)<%`Kc}rFo$Q7eB-1WE;B{&!YCWDF zq5|UAsgFv${{ZP;=pyo-(o;Kq^_1H8+y@NXB%)QNBjym4mR5Tkc_>lr2OEyw-xgUm ze+|@kf#SKxa(2%liIS5u-SHeh!By*i@{tk!4msvDJ~q~C^&ZSz880JBVw1%bWK2((V^~`qT&yJ`u8Mz>R9l=8mv2xn;Ls9_;{ZpgiP{Un8T3eeH zugjxKS8JBzK4 z?47FEPjEPocv@~Y?3mVzw6`gevbJEo1($>(TN6Eh=kfy-s(~75ZhfHK!&$V{h2A4-hy=}V# zz1O&j8rKr|{U=|qkGJ+Os(u#ralt;L`0nw(ICGF;ZW%UcZ%MiBw_FjfkX>zp_mwIm z`dw`0N|ey`1mlzQJ0AAJ>_;O;pLHcpDLD?unK@UA%ukl!uzc6<%aUNLd_5T2e>r82% zo77hudG|duM~x};tqXCB@fwieAwyE31yI+f6=!Fj*AL=UCQ^6A5Pyfq(x#tCJI`r) z^WR&$ZaEH54O}*zp^28LY`)FFOKd(PDI;1M2@TW}LEDErit=m(Ee!#~nuFVWat+gT z{<2s?xL21 z<-^FjmbSS9)C#LqmSpGI)|in!5Q&t$rdBBF$8AmxomX=W|5R$_44RMS6j)2n4t9U)#*iE@-uh|f+e^1~D3EdXj14X~Cu1Ki?h zLF3@N10(Zb;@R=7skr)jp4`ZdwujWz%V_~^peCqRkaUtV2N@gtV<~wiPCm7{Mcvq& z7f+GH&_8O7kLHDBg@4>rVDZ)2n=SnCr!r5%dvPw#+_TFKbF~T) z-OF*9yfZ_~O+K`tFY{R1(i*lvcZ)8f4V+Q~P0NauwgO5iLIrX?xI_K0roBl?JC^rw zkL*N;=ApC)g`PLPw1n5e^D&kCFmc<5Q%5g5xbEh|^(~kApKLkJ`EP6+%Sl+$r0R}E z3GLG67H4kWKY1+Py9HxC7g% z5AUXVF6xUo9Vw2q3CFs5#_zc~18(wNmuj`Pw;tDo^cZV#e=yU`JXKvlX{iB5p8Q-f z+bxmW7@qE+O;;f2KQOc0WWH_|1~>u}cWQEXDA>Q+6`yR&Vl}&y7*l(Zms>6%BveiF zk8V~;ZMdQ`1AbEp8i`4#O7`~P+hcAs6%UrzR4Xp?%3p4K-J1l;Bxe(=--tmsH>Lpo4T%7 z-L(u{B|9N$66QR#mkBxjzy}eMeqb@eK9I*Lt6#}prp9@j6Fxvx<3c`U&*@NvD`hh3ZZ^r zP4O?IJ~xYNxO9h}x>KHAF&{LcUTkgs3O~JEgd~F4^CeK8zYleB19vjDTSrA0$&|Sc zEyq_?6PlMxj^zky;}KZqoxr!lDN&o0a$*3n2O3|%ZTTqcT8 z%dp}5UP6VE65_UIQ;$A`m5k0~yXwIrmsIXZebnZkI3_buwS}Z+pLxR1lBy~TS2fi@ z?Onrj+WVHlx@~V3F1vPYY1YTJ5K{a{lKL|pgrEcL!DH0XBbT)P4^ZU3(YTw`8+@_F z`#*ibv3$c9`+R9)I@?~7qS!7cQbFjdj8k&hQ5@Y>;=PB>x@a{Y3S5Kg9vz7|-W=V0)z4gow7BWTPP)R8Dq7h=2?&aXr7BVE$gB!uq+PnB#$%XeG^*nn zHYLVqEi+&hqN|*N3g5=tp6{~x3z9Dz8*^EmEz7g2&|XbPPmMJJLojKW#jg*Ub?hQY zz2=pb=XTR9XE5AVuL>}`?(QLW&38k*$#6x%EIc7ZU6I1ui!G3yOG(PL#eySsT}Fxk z*jWMybx-N&T-c+FTFDh8)PYJ@u2_@`*wgNWDjKZ`PrTqt@>47+B@GtSq>ROTFxRMr zM+*AX{{ZQV-|AiuDecYc=_76K8~*?$SsYt-d%cFp<>7`@@(WJ4A;%eVB3oKXO2f)X zq3R6|GtK#w#JbthT|J<9S6^K?tIo*9=bd!PtRi!r$bnJm1fO@8h&~$8cc;{M<;m+i z)^8qd4pZfBD%jg6)TOE%b}qMTZPm+s%j7WFA&Z<=+LaYAHl-4UA@vmIkaU`3pZ0%r zEHKv&g^qKfC)ZC>>Mh^dY}Rhgw-T|fxC54z^c@fDr;et)7Uqvh9;CfKU18bzdxh`& zHOri&!n0ge59i?AWGJgkab;Tz?K-uDl9U1#NAC5MYILTx^-`zRP!FLmf3l8OvvJE?YW2gLzTa|%cU{IT$M&Y+WdbyaXkln2$Y6X( z3hRd~-q^zG`ON<{*`| zV}bQ5zDEsnYi(J?oq}#XJ81is!<(+qXrf$n45-w(b#22#~{X zDJd>Asv6dyR+-ZkZRZWu#kDP;(C};i3TtQFTU*?pJ)UH41X7<`gl_y{dAn~ruGZrV zHOr706Q5^nv21}dsD=BCxK|xbxLaFhp<;m737P6ugxIz0qHtU6B!kfaJ~B#)$56`Svg zYgjoe5UP>(V`lNOw|%K#DNBl0+CQY(*V*pGgBw_tcHzaBiHXK?v`b5QpE(Oi0QRRm z#u2yoN_U-aTyQC2+MzNr1D3j^&K${e*q_rIB9Vj|r z)LtbUO^R?7`F9U?Sn~Hv5tLbEkwqa$?iK5UIMpWRf~2lFlo-h^Y1sm`&{jsW<|0vVf)2tuTUcp1ggv*ShzemeHqE4!RtcYf3cNjbB|c z8J2fUN79ynd+VrsHK{VUdbX{(z#o@yb(l#}q7bhsMI@fU02Rv_?7)`;=~K|iXll6- z`$}qT)*PXBJMW?L4xn5^%1VemwIi-H>tp;e>Qy-!dX0Qm^y+)Cx&^+1MSiRb0+X)A zUUO~JCel5*obfGcG*K4^RGty3AXCMMYCcvkCbjr!Pj(DAq(DQo0oABfcVTjqDBEq2 zpa7@?EP13Mb7+>MvYI53+m;iN07{L=u45yfSV$B-B^+gnQ|-d7e~L-%N-VOXp+P_f z4dy12H7K;v2S2v~<}V18hqm?{?aOYLabjyR5e;cVnsoTqRoan=E3B`U>QPwi@SbyP zS|zmcHRVYwOy;zX(v^O!AneNzB2d{n9L!V<_Rl!n>W zQY zR*LSo3LLPgvm!S#G=!AMis};MZXrU2DoTL_9?(WJh~DOE3fc=A?jckE09@RI>1Tlb zK5i?IFKi9Bd}iemWPO3hu3ByGNxa>Ja$0UPWv>82h*OT_8%|{O#qVcY-`!ob#^XR% zW!yL8ptK@JijkmS0z0h}AM+&5aVwIflHqI=pdQeWdMCRbmf2501g<9*!LKw>JlC8- z)2g6*Fb#ban-~B|!sMotPyr+T-Pl#Hs!8#L*O>E?7NspkN3}J=USUp|7zsVG{o}+a zl=VK^VVt~FkoZw~47)L9sO^^s8ITP-uxfP#+4l`#NwOfswheRMOOi%^DxChZFbAQA zi3r~54vDEgREcUdPn6Lnh!g^tLT8ev7|xMYMVLTPfD}p3EHP^flJ0#H!w?NMDK!K3 z;qZYZWhlbhG&+(3M?+t34q~DuQ5T;Hsv90^x*B9VuvO*eTWO$|4wWu5|G_8_>X=av6LF7avHf8c5SK|(-8)kK1&e%yujZ1KFneiSzPw=8 zSwkyGBZBE1w%+mx*N9rC$8c-QSZ`VlH4uMRJn0a_XGA>6(q*XInE@^(vby%^8CM?j zC{vROBShU(Ml3l`$R1*w3rO$CQkamoZ&Z!-s4VgFte$DD7X>*^`FOhWHsBIhR^rc) zi1G3$6nR~k>zo8b<(ukXC-+^$L_PNrQRsaq(P7w0yfC)WT;cHza@U?00}c zFL-Yi5~DL&Jx|NA{ahvZgyVrqeglvDp*!sy*UI*G+h*N)Ewac7ZIKz0fCsXeQJL$; za4HJtx0q?oT-{TQ`jh5o;m)^Ax(P7ujIKP${{Sq?bu{8iR4NluP+?gfNQH#_GL5ARNXe?6U7CRiAGyfo}tR z#vVMAD}M52!uz@JUx)0ww5?7Qq@p8(F_9Fda>Truqr#OYgF4Q zU!w{~+YB-P088sB8GD(z9BcCj*x%T${{ZFb`RMOHD&8E&H}e7Joz2S|PVZ{8BZ@81 zHvO_w(C%zYmS-nTnk+g8TXC8#)etnC$88&XcBDuf@=OApe?qjb-uH57ONI0LoyeUu z`Z=mE`L_D_+rG7V`(pAZrOoxoTQ6i<_N#Wi+qYMAzU`Q@ZpgU4`ds`z6x6V_C8dOy zwAEP(og$X4g|VM!Zdr1`O?1!|8@&69E+!@iQ>%|Zw}+?eT!Xn=ISTEs@jK<#>Zit+ zgo~ZRN|clQMb}nO(~I-4(6`}WA3#~;yNXBrPbC+HV%+;m*y_@$R(pL^iKFxz>W zaYf9M8LHNH<*Pi=K)P$UQX8^b(7cq}C>_0t7{anMk2tQ1uw^0@O66y;Q`>}7rK(V+ z5kc1i5eq_s9OWC7-Nkv24VbaE<(5N{os!``YmPPHUu_fO#l9>rQw2DxJ;r#)AqSF* zZXAodcQ)WQn>@urjHT&GU{-j5!pusdW}_5;~_P}^wBz7w?wzbS`gypTpvmK zx$dh{lxbQD^cWbW1g|~<*3+7+e4Vzh--gKE5FoiG%?b50HhXC*47-s@T|N+d4kv9g zO>BTyRb9woT{J|}prgBUmo0n3C8`bjYFpf}kci8D!~Xy}2>1@8)j(ux*BfRJak5Cm zU&V8Fs zx1ccS2_w6 zS#QA>z1O(dQ>seLhZL5{RcOB-f43Klc*CIt1(x4R_^w`)3R@eDbj%KS1zg$M`+8TaNA~OX#F)`GLM5dhEg|Jy{GfXG0x@U2 zo$=zDM-o*t8)Xx-?rGoxwd&a0bsZo9pbZCF&+El9EQ{U6YBS@b!q}~E6O62YP(p>%H_?y5Vc67soxpExJk7mWiv})3yHV;SMoM(|8 z?Xh-wZVZ9DJ=fbpFw*aLWEk4jOk+ELhI^^fpUiqW^w$MUu$`4AK&41_;c`GH;@FJQ6Jmf9BeKz zjRN@R{NQHk6NhYaH0M5DM@0q`Ukk-HA=BZmR z2^%gXk%Ek_i;u0o9cVDxkIO*uzU67DYRsJRRGMO1wFvFURZ2;+LtXm+03I>{QdaQS z*CdS3=)`@iJlR5jlaN7fN|Sf}=jEaptdNzg1R|gDY0DPOtim>Qs-_Ct%y&^yb&Y9g z%DAqeL_sMh_>W9gSo-R%_YLY43P;*8R?WBO7721;E-Irs;?s?ajHyr=IW?ke?+10w zWvWy*2|b?{E4jCV)QbFm4@KlPU-htGSt`dCr2ZSW`RXW!N znbkTat=rNli#I9M5&_av(EIR{CLt_X3LodBFCwar{IKPc7jaGGfxMPOjyj};8t0d1 z=)#FVECi&}D#)y7k0eWylWAAOrk>m{5@_^Zdzuo4$HH^d?!ptbKF73C9kLpxK?g7^ zhn6k?Cb=FddMK+)9sdB~!-;+s6z_;47L7?_QVl-sK`*Yx#S7 zYgsE)yK_k~q${RUBa|zrP;sbj%cix9)p1W3^73=Ogd2V4t>3mgHMr zVNu>^$VpI1&boySYnV8*HrcLyR&hPU!#Y$kwQn1hPt9$a%S1+q^OLBh1p8=xm~|zi zIi@1!R*!U$uQ#NgUEH?bXWLs!>p96gQ+&9aP5K&EvWF1sY6w|QV5uqshMft;OLt=< z7XD*J2wJyov-7&|$ay8M{fa!@is3Dpeck(d`ER%xTYBwriybl1;SDn_#2|b^luAY! zSlQemCUA~l*wz5_!*+0Tfa7y9JI5L`j&9(z@mwE>$uaX_CQH>R2N?+ze%nXY!Y)SKdKHvC_}tO zjHUdf6$Ge)0+(75?-)0hd{b{RgOX8t{)qu0%Xtb-Ku}V1?d}-xjmbGBZ-UvY%tpu} z+~^~<4ul?^>81(f$WsSgwZ9EQ-d)np`Wry7JeP-A3Xm3%KC0pT5X#f3OfDsj+_g>l z4K!*)B>`RP7=K47`8rTYK?o>IW3#jz6RF-Q7SIYK2!K?bMZX9lhY9(`I1LKX+Be4g zwQL=^VX*GEAu|;V2x*6aS`fDiwACiG_8d?zv8;8=oaU{z!*EJ08yg=%M8ujmE|oj@ zgKs?PC3&FgB-E2$-0;{X;w>A&(6kg=CV0x+HB zrM<&CWN2{op@hwB{K~1KA@?uJ+tY9e3={PgDjVh z7u+urtG^J;Z8u9Bi&FecwoQ&gqk&)Solcs3I&pQ|#`-4oOhbU+e@-f$m=f!y;w%Kf-=9Lk51 z!j?XvC|n{$oY6wG(uYd2)%0P>35C3lVN4e-@~x8E^6KklLqQ(XgsDX~W)E(vV@B&| zYDtG_+)D)#Qk6F;BT#EqJ|WyI*@Yvzp`wJi+HSaEdpM_^*K69hHLxuK4yEL4hb;vr zwG{*qaPU#;v1NU6V=qgUdAeDyTXI^Md7`E@ecQx#VY`JEuaCZt?mF{Iux=srCAIin z9^T9m&#H*N$Ces^ieHjb9jRbyEE+l6;Z_oQlfsZHy=r(hpgH1c+E$%~SSV7mCsdni z0!Q8IZC$;hxLkom)X0zR4bgt%9p7f%yLHedTO3J#8-rYFk;(hW@1W$S+!+u@w+tt@ z60m}sy5Y=!PzrAK4Y7D)>vP+?_lX^1-*4W~kuz~@wuwly-0D2m(v=Si6iF#a$WYTF zF$-YXnhRgcf|9(si>Nv?$BKk(v1aY-j2peOW7hfbLXynZ9dVa~Ye`Dct(7E{C>kjw z(DsZecWE4h-T3yKG%DeD~8A3gA04-8j(s<*uI2zujZHOcXTk@&RuUiZO0G6Hq`N;?<$U5 z0~iJOYOi8C3a+Nc_xBYr)X__}Z(P2fFX{6j^27!a+9ZdVQ1^#YF$Hzo+twe1s~xv@ zF_P*Y6O?+x;N2%jE!dnX{{Ys|3HtEQ`x5u=r{C^=H}8a)pIdw<=av=8cA6*m!^HLW z>B7#~SHN&6!@5}G`{69N>z~u4G`McuuWE?ENh@>AT7ai_mXc4^h_SG89a;qWz1+pL zYVf{leR%SA*yev!e2>4jO!Ii!Q_*dWX1eHGNqI2gA+@O~a3w)H$pWK1b_Z?o@>*kC z8^sFXuIJ}r$YtL&dbH{Gs$lX68H}^9OkdV`G)S+tnu>)2x?xW-8YGtLMNV0hg!;;M zz$lXz*fl1ARM7o65hW6=XmF|tE%np%;NEICmV`?v&njV7N|IzsQ{jkTHOpLhmfKQ@ zxw4{=3Xg#*Q0eU$T33}$?hL{lRF3p6nq4H@Yz;XxaNMKWVkWeHL&$7Wwa6&cpK!wQ z#!BGqb~5ti^=)+pI?YY|N zoI~Z&Q=Rw9d+(54TieTWyK-e2vj9<;Z2)QmY_&v+cbcDW8+CMz7So5?w$SJrG*G0y z$e_0vikdd8l;P@W?89d{)mNKKK+&Q>f?l0+V5$EA2{j$P1`}urY^?n;QFS}e52`Li z;OFLrI9s>70Fv>R9d9Z05N_K<(Nbv-zg)9nn@lz~vR^{z= z<~^NmAhmav!TiVK^3n)^B17m`g3Y{Iy}LQ4;K+!$2gIojmV&{|6oc7}-fVdkTdX^> z7q#Aiz7@YXpHWY8U#&h;ip0Y{(Z=eJw(UhLPJ|e3hvaF`wdYh1yB)FHOnYpx4AO<# zub-R4%*3kV2iy9`v2oIN^^EJwQE5P)l_T|H%PvJo*zJQv$-8f-TT0uG&`Hn6 zO;6EGL(Py@yF3p_)iy1Xq+J`IDfbJm6$IyAfNJZ5Tc0tBJJ`IJ=#eM3rk0X3MtS@3 z3p{0y)mUv_9Kl45A;RJk+EefnYhL^}E(d~ID2-~1M{~qNg*4;AE{;7lB_TZmIst*| zDK8`}MR=uU;H)CL!8>b>NIXecE*^b@2K0L=8xm@^+ogxdX` zP>{RX>HW`F+)cszK<$0$^E`WWwnn+_@0De#(O88ouRJ6;QBkBCD0UoJ1i1M~Y@ZV5 zyu4YfY2N568PA#u?atiC<|y5Fqw9S{l7Q1fial}(%REju185td(NkpHc@lMUZ^Py- z;cmY{-E#K?w|51lL3Yc!Ry^i^#5ABD%3?*95Ek?hD5K6=^^Xz<9-+-iay?^mbm<9_ zt?PA6q<}tiS^~%&DpE7TLuy%l#Jl&#Pmh>X@99JH2VbTRMcf>hKV>hHZabO+?bjBa zm?FFoF*$UkCopRfjwW=E4P<2ab2u(S^xAhG|7hwg;#vkhYXOuz+b(KzjAV0z$sqJ3x#w z%G^-W004@pxEPs*@BwMlL?`Iwk?c?DH97|S=Sva?&3TAr64Yf0E{!&_bnFPdJ#%7pts2EMKf>X(WC z05$K+63w*jwr!=%)-E%%L1+k@O;&k!K^+MI1g1J8BxF0W$oZbwZWAGM+FnmFs_#v+ z-fgKAW1-Dp^w3l2G%5FS{NjHus3>)R^_I5IN(FSqgLINrmp~ehPAkmqUSyx;m2Y={ zw$n%RntqY{S3Z4UesKPsclOWQ@I5--WbbY9wl@u)`E7P(nq*sf{3=h(sR~rq(yzRx zilkHmbH<6a`&)kiahKhwBae#2`-R_3P21V>O<{ zGY(w#^vE+x;=bcaQ;m6zG^8vgRRKWfiaqaca#1{hG+MqNy%;&69b^Z3ll30F{VGk0 zU5Cj~E(Ko;1&2XC&%SI@xMFrzO}uIp-}{%hoZc<3s4ix6w+3vFR~(aa-+PPIuGHBZ zO~s|XwX|VdBeeapAOIAt*PdiN>djQ8s1Z{@02Icvw)Y+OF5AjvWyi%}JI7>T^S6F{ zlLoiIQBQm|=>Gun*y6acB;WVdqm}RR^r`E<^Tj=s5;c6}erP*$iyh?4&me3q(`(%Wce2~pi11~{mE_HB0i`IaI+O@dNcS9d zH)!&A4Yl&61XF_GZu;Qt4oJ@d2dj-23hq)?(%ftscZzr8a*k8tx!!Ab)kasghnS~9 z%|92?a8n|61E?sOseNs`@u(pr)M4l-^F|6NFV|ML_oAV9Ph52?6;?azFr4|vn;UZw* zqeMJr}bmRrr$1$o7};kzX<4az*RdI=%bgJ zWcXy3$nD3fye&~DZZg(rxe8r+=Y={EzD*H&TaKDMwK$aY1w{UngzA?0KmrZgHl%>j z0YE4yidMJ<=;Iy=t)gqlr5q_8^~4QU)@|BSgLheIPL#fAI#7?-fT~2o)H-h$0%#s5 z-%JT83`tfCmphArkm1n2u4xnl3>_|syM+^lWw@zKCJwD8ueNKF&AxzEevTVHynnh$3fn#LD||lz!YnV&s`TbP|JX*sa0`GQk+qt zs+375={UZ^?{zVinj=VT_x%`DI26^|8X=oPRN^XXkaNL;N>rC{qq3HuLC%F{T7rs@$ zc}YBbvL&}NP%&A(Mq5e^M0M0DptIXWR96XI+@6gSt6Xy&{Y_M%v-n$aLrm+lTV@N+ zsdSGKYi@v=oT?3R0dbObbXJ^p+71SkRP&Qy-BG`9F)qvot`J@clEMN&LXrWX)0a$H zEL>l2s?PHe7_TUKD<8Qow#oLrsFc7|r(r;4sVi6&)|8H(%3>nUK9W_U^6so^Y_~qz zD38Gqdw~?gj-?;-W0*>?8i6foQmt*E9-raEXU_4>I*v%*Xj-HjJ(Aku)2aeU6a-TY zCAo0XNJDDJa#HPz#cpk^qxn>(lbOU7&BK+J+TEZo+j=L8nEW8o86o?W)g+PEG@$;B zb58w#Rr#vzj?+dNiwfLDCbdpf27?rqoeI?p2wyt?0G^i=?-sXD5Oa}~{mYK#TbG|SOabRRcyRPKQiG@!kTF#rVfQPUtvhpDV7Bl5^gDxAp@vQ zO4IBDxCQ2&QX*29B~Y3mtH!Tp5R;0O{mP;4x7MRHsFuWe0Doxsrlk@vN8EIZ1dD8o z&%C3VFFz?|$6VH`pAo|l{-q}jA%jFCQC8pz0<`PXJUtDnPFcW8?LzuGRwy3xuLR$jaZ`n1BT9QHaDHo|E~R4Pxm3q(jrxz$;lH7zn(LcZ~NDxHG3 z8D^lOPs%l_1{;O7pp#Mvt~|$89(PbHBgWGZ#lj2;*&N{WH1dS(t087UKwz@u*pBXPKUZC#Tsg}U=? z%+=?(6e<*ZMR0g`46wW(1UK6*3%Fdza3OrpdX?kFahIr`PuP)e3%s#lxf2%Pun^+W zC6r4@P#IJQC`j(dZtRTdyT-NTP|vZXE*wwP(Q!)~;ECnV4jLObyw`=`eta4TfQ?MqAiLm`=I14s?FXhPIlChSlq;uud%WSyTZvmxMuZ+k zbQ93`p!;VxdVgb-E_?E3+g{?kU86H?j~#~)8@9{`D9Ex!#bI0SZ}T^2hY<*wXB4yMT34t|Svd>=*;Mo>LFNB1T#KwLLRO zT7j|L-URhk(m?F~i5Mu{5+a?yA1~IOyJPaP^!K~fJ?ML4e0bJ!(qyGKNsBHi>d+Fk z8e}V$JvQicWDgWuYzUUM5w4XGuD975@Mf+vcB3(QaGD=XKd|*hAMvE=1_aO#zVN zik3h}L0nkzTcAeYe^p7&Y!M^>0EM7cLiNBruVVbaHt<|M%Eel9WbrY6&kQwMDZ~;Wiw67p(aRqXE z+^db=$vKCltK@GExcsx?2yx{)R#bZ{T=6+>(MtHJY3^Se{Yd4vV!0}wOvS5BCUe6muU z`vPf!5hgbompJI9xW{Y5Xf+bw9h8sZX+3@CJRvM~O@=|iF*okoweM>?_D9=DhaoCM z;0-#EwCGR;Jt{FBd0-@ONm6*s{E$33yh7+U<^0b%=J&%_juO4?jlUw#d9gFMp1vii zVoG91j+mZcZO0UkF-?rg%DL0C8Jl})1XmtwTxu5D{`+1}ZRAMMLFf7p*16{P;D>Ax zu5wn0#fMucQk0Mi^$jCF^NS?*!Zy;XI~9~Nybu*t52|+{za?ta^}^`m&Yx?qm7Knu zW}BJRX)B^OhOccGP_4x&gBwnI8$ka6L}LWH{$X7a0R9+C0kB0!L58W)G}zJ-Q&4-U zTq+4G#EK|Gb@3j~x!xpQ{`T1Xg?X1q%)VNWA;o4?q!Ud?w2WJE*OP{g6)z`>wJxbl z+lqGP+DgPPA{`S&sjn=og!hnZiBRcf(5PoaZxHuW27e^&cwTZ96>^+VAqv#kO-QGC zIFyGQAvI9xy zM$cwyxf96yeZ8y5TfWO~TAGh$xyfn@fjW}NUZ(Rs$si1C7Xx|v0BI2WgQhBXMcWx&YE~{4- z5`)@sU}{qAZi;>MQnfwmhiNpg5JkT1usxzMNdEwRjQ6b~Upe4N7a++2_P{n%R;a1D9<{{REsfSam zQUX<|3N`r{&+I#UmIQIcn9B`pEQa;wXvenrT(BGEa zfTECc%|6kH9F!!os>_O=kEbNPcG?yTh2`6{n`#}F;y_d6WKAiU!L70qhEk~4IH+AA zaT=7vyx%SRTH7W9oC#TQ`B0DnQA$m9P;>&AcLj`-kW_8vMk_|qa*TSXRBv6gHpbiI znLD!MbJ|F^$Z@#{X~@Dn!!)`BgGosX0QTc>$ZdxH4$}8f1*-t%Y_n-Xf44N;*<_(xD{ zh)DvP5-r8$P26F|SeqsP2Yjt7rjz@rBMiL6{ff8qJeE~= zfVBYQ%40oUfGaF$J5lcF^elh41%^@Lp#?VF*8?oKt;P64f^{04x(r(%d(p(K<(7_~ zfuTw^J~vDV>Avnc--APGI?@T!vO4AuOjYi)&y@5tRe8p3L2TbPT@xzzyQ0s#sus{> zN{tKwCgAx1b?gK5;q|SXn%cXXkGje=^3l8$XX+1=?A$@dJGXo8JDWD>7Hf2wa^@{z z2t#q%D^jakQ?nBo3qHGmPXM4dWyy};rN0xjU!0dMa&^O(xlfQeR^+uU-O+KGHZv{~ zRNF2(LntKmDMpct9!~4blB0rgh{rbC2D6_(T4X4gRu*-+_tVK+p5lMOh`Q*dVsE~l zTyt$qcCL5lz6k9U$FK3Lw-UH$K&y`>1Gz4Xh>UYgE!B2a&Uqz%7Ku;4JTb}K^}S7E zY`my~(4eLzzTDaS+lz=(p#J~}#Ct2$6Srwnrr7Bb;WW?!h)4+@z$uHgMvM_<(+bVGVW3iT2B-C5TPk#>_x}JAPq{&9_ms2>YV958 z2YeMx;F-gULpJwN={j|E{g_SGsn^W}VGx$8Wu%_C2HTpVy67bqp((m&)22h$WHEg=J zy4&Weoe+lC`3+D^2bjPsfcJ?@;w1 z6{KyPTvt1&MJTHr`csz$-bVibE>A|PTg#8ZmYB$SM21r2@RVoKAiiil*yNMk%&ll- zC+RD`+1dOJG=-!80EB+TopAiSy&J)cDfMuSG!|NQNqvd8CfrF&WkXJv$8UZVw(dqy z$N`Y+=}j;DIe-xo*jf*$D4UAC3i>$Y-ZiDevXA(-xvKIZ#+X-WS86-kp(Tp=HI z^1;L2MxU{GC$Rqj3@$k)&&S7>xkrk2HOX+C6t+JHON|Y3*6Of|G z%yrJV>AZgDrHdt$@fxli&unQ%PDmji3D9~drDX7?>~$Vv4W&u_?Nvn&amxvRdE0n| z%yz#Ze{`YS;SKJzf|$){KMMTHgY@8kysggyivHH*If^5(ctdzUIwS&!&B&6VlqF9L zheA$W1{9NdLe>)9Q+9T?>GK9S(yCK)(jOrf1JNg2-AVY#c|w)X_;Frua^q>$rCS}? zE2pxpa8T9DhHvq=Bps_V^Aa`ICqLaIoQ z=>vB?8&bDqHk3zSEy4vowH3v1_bM`UX;)@`U8Vi$07@g$t5k>HxNmmy%pLOVi?xl1-&Kh$`ElGVtb&m4uC&99 zcW`5#=gV_wqq85E=`8WacFJ_*n_OYRo3nj!j?CX*&q%gR-H@7;^`psC-!JA@!d%oV zL7B@Aa$EK&B`hti6D%KMZZ53^zHJHQm)`WVz85VKBl2qv$ay-D1b08F4FzFiIzrUu~jcfDoaEpRU!p%P)5R+gtK8|4x7g0dBg#wZt-j z`D^-m(s1-2K5~5MoW%lD3MxpD-L>U{q?#1u@WTVYwbhE#7iUQtOJCF0g&&vo!NKMK z0JWAmeHcxO|5Fr-dv!-jpS~Z<2We$uG_-^q_R;PwE8{Rx0%A* zY~~qLCzP#ejx9+QN(EIyr3&SRr`_|!D&|okVcYvlhY-mLr>piXL-cpBZubwuSmep> zPVrH%!y9i_jZqm&o^arQ8mv=@4Q7 zpq$M$c3}tao1iKHP9^Ovqu9sqGGtGq4wUHZ1<{ka-UzCCwf%aZs@4el}^5ggL4 zlqbw^0h!WL53?DbXS}$pU`TgWJQjhrJ0rMqFQ=ARJwM@8KdS!#%Ga9xEOCnYV{>lH zF7?{+W<-f{O|6;DKMhk=DjKp?l~5RF5;95LE`=d#M(h#1n>9u+EUdv*&egtPgH zTpZqXX&t~60>wtYyaxV{YgVQ8z z?W)zSP}e#~%|3^wTicfFW#gK{O7bT>>5aQ&X6~)y=v?>Q90jg7ChL1|fC0|F zJk@6pS`^-=5L|IRPIb=~?>H)ZVt0ZhXJ?e08jE(Tsx3kzvTIusO>kv{rkFwJ)+%F^J)AT?UH6S5ghg|M0m zbdg?vu}Pd|=s%wKb^ic)?DEC4Z*Gl(V<&Lh=g%Q3`NXNli!MiPz@V)rrz|qdcLvTl z!_J_mJAbp;Nv3pFqne~C-TwEpG_CbNf ziWNw<*jZP&wXGzChjsMN>J{YqM~UvuXV=PZp6dCO!VKMb@M}<#x$WL&huk4YlYK6F zhf!XhBfPv5`@xp`GyedL#eyC6k7)RUuRFE5qx*;IO=+H$eN3pID}%QTgb#&H#%*=~ z05!vhU+xw+?}Tsmt2N3peV?^8E*_b^O+-*roDI4BwAO@M9bSLrR|>`4JY9Y?L)xtH zAId*cWZXDwo~`Q9FNC*ba+>CB7bW!n0Fr4~cHQxfC*uj6ow&`n{TF)^9_zMw3d(DF z<9)+yPg3LCe(KNwo}{ig0DEyE2IjDT2e;Z23HF9s9x*@umwv?Ga$B)$^N^p5PNB%| zwnx8GMkJ%k+a3-F=}~iH@1yvmaQX@b7khmIjD%5Dpeh}n6TpPo(MmXqCsmVq zPTR8ByKVcKw%q`NKz+Yh1jD#Bq%E>VDhUU(4_aGF^H4ckLvtTP#ad7|jn#F($}?}5 zXYTU|6_WdlaasFTO)KofvvE}sWw&kM(X9bCq+9}tN>^Nf1KonYTJchkvjAv|OG2NH zw4l{X84ziLCbsSkcB!Z=lh)^fswHNcl%N2>bg2=1#-*6np=x48hX_-Rr2B_%SXGgD zsJn}gHPqyU+dHD;3gse9cMEEgklTbFs0ytJ9Y$F5NOusUZ)}5GP`-hGFMdzjo|rf% zmVIY)n+de+sHkqquogmBal1)rNexLDNn&o}rb=~{N zcJ907$ldSTwtTyl_T@mIK4iz#`>O#t093O;G6O7H9}8iKF`Nd9Zv$*v33jCFeQLbj zB&iNiDA3})a{4VM#kwYR$(|m+MR5%@%3YBq`vk?tWyGk_htxd8e!NLz?#nb$d8;7y z)Fqj1w%{RO0r(U%#DwsP0xa6oi@zo~PM|EE=NC zt7yBm`FMJX-O@cbavu*^VXprGx;VY%+&0WKG-XOQJEDkjEwTcN(H-EbqywcY7;5WF zaXVy?&@BG|aNXRp-O)ncqw22|pJTY)A#Rl1w_d}Oc#7-ga&51}l8-Pb)RGX`BE8tq zxA#=2NJNC`hBu@4p*rlFj*%zzDk=v%z;K|mDdo2l%-J)8ggrXu_%q%4Y z_SdCw#}$#bYv}1gK9wX-qhTrNoMb1T3X`PDf1+SgCKgCv}6o zmzFz`3hCQ}Y}}K6VS+|;1ZY=IJzdN618Lh4;%zPyM;LBPw5E&K&AQ4^+*so^5ga?^ ztd#!dk)C+XHfu9fCDNDr*4w`!+fJZl0yE+O{XtWk3}4IQUgNW(?Z~_0e$w-zuJpvK zN0B4S1f}+Yg4z{BP*SAOjQZl**xJ8+e8hPMe(m{l3_jR~Lt=_jc*%O1#FX3S&AXfEec2eCbNK;Eil`e3wjDYbe}y&wALW^Jq;bN>Kl50&Y!AE#f_*-I``NYo0vDki?nO+4s^7Ev@* z^S)|=7m@7fFx>R`r`}iym&;8DiFs3NiPyIs8hgx81>dedRcr zt{?(PSXFbv{{SYyI4O9feK@A%-c;mGvu<`xruwHA%}QE*rrt(=#f&h%w~3m7Dy+C$ zGdrl_XrmXsU~`i?+f(+Hgw!c2wV)?2V{Rm3y7@~Fx)tTSPTl_i5^G$2SaRg$gC(mn zy46XyDF^I(1^M|?pX_TVaS6WfsNO_$67*|okWE#lGSi>_FyMzTaEN<~ zS8RevIh_uFTMj=LxJCZ}Z|x92?e}t5$7zS{r7~Vw(lC_SnH|MAx7hJP@SZ8<$9$@YyPnnaR$|LVE`1k{RV1#vHo$)2he zS={?j`9;H$c<-Q#2P$1#QPA3+ZbB7WRStl$OiCC301;PWiO;n4D>1e1#@&6lOKtNJ z6H9fH55g7&4qcd^vzOjk7Ui`kf;WG+HpS+6MCOWA;?$J`T7qg1Tv`N*u@g^9MsbF8 zgY(U#o}MAo02x%XSV~BBcJ>OAslI6jfG4_{;WTj(aKLAAZWGpZ2&1i0hewCCiLU7+FqQZ`q8E;4HkP;dxz`8cgUE_ zF0@YvmeeN9Xo(0bs8WHc6vIafq~czN%Z|9)X+d$v6cU61QdCU<7*D2&)8?q)ulEXi zTG^b*vSr!!=Pbgr`j56fA@jypC8j(X_P_#~`Db($sqy}@nt-lp13XglJB9YsYAVGo zjK(@<)E#_3PoY+w_ptYu819*yKYeY~1c~0)9`Db!6 z7go~x+E#suYMq#Ohxh_%f26fl^+z>?cJ}?av62EutvSSKL zuO{5k286C$jSME8wOxIfVI}(+=}g2=d$dCCeY<1Yt;d9o-wSkZ2MW`0g=`ML79BDH zQk0Y-#S_@wK<~r*SWi0xz+@AqnVdVKj}9ngTc2=M zBR=eT4N$1IzmQX6gQ>1OfCE(Fv_wgHCr-aF_2C!H18P!CjJVJ)ntE>hx%n5={{S32 z$s0?2+BVZ|O?nsLQ`nTJRvAiF=7$+1Q0k>A*^Fy+cX}Lc!HBlB^sb58eTBKqSvGdO zG#^L!sj1J&v(y}8L%(+G)&3DMSL;q7Ki}JbxU5osR&P)GgW3KmK3C-T>Jg9@jpbl) zj^FQp)xyKSw%>I&m-4ZH%0cY^02GAxyB7z0#!tp&m`bvNJygZM-HUw}g5`W7k zRi{S-`g+n_{{YKZo_5%d_3rOvj?>ExvQr`D6|G+g0HAfmm9Ka$Ciq*yLtp-1GB(uf za1~GZAC@OPJljfk>1bYGU^&=5?UF5TCsY0i%zb)A}w!L9Hp#E1p*pihwx-9La z9O}2vcl0pD{pGYgig^#^yCgq+DIV4NWb(slPo2E24N{aJe!At?ATZV4;k5gzULVVz zLYl}Fk74|~xnA1qPPM;nX)TlGq#{TI?avYK?){UGz@D@6^N|L=U`QL2)7L%iEzL7( zSTC?{((TaEaFrm5Y4XOBit+RBgv>^0u!nW2zdc`_Q58t>(gMy9VNu?2_?ZZH*E2$onZEix~eXf_2qtAA8t@O+)N|Fv< z>|-0Y;LCJf;<^`NSQ~o~!^v1ioDHmi4SEdmU!uAR0zu?%FW7wJ$2+r$`8LMhaGpU^vr>Af-SNi5nZ)A&@)@fryrqna_R|n&cJ#cTm*`ZM9`Fy3cG8Awbe~g2+B%6b@lvcZ9xOymJCvn z{9!QPbwNt(sly_tW;qb2KNe96=3JS?)kb)V8Cwr9>zA zu(Yx@fj5ngc+n!txsnuyz7+oU7|(aqbW%3<6cs79>&tb0Nj><8eDzXnj09#pB_$=- zPK1ub)ZoaDH7Jai)bhNviVxjS`@3+Dn4MFlE!!fcl!x6ET4|vtFQ*QpIwbWgXXTXY zS@Alm)7gaNm>?GCnoUAf;(~}IS2cTZgN;$zXqO?Srj)mzG)j6WwEY-;1E`tF2`Wgi z-B%t=3Cx`dWQq=z7P#GH#_7JZA)0~T>CR|AiQ2?5CsTj0blO%eqcP9ysS}&$w z%jbDFeDv|P+LVvK+L9&PU30I6r##l{-QZq1&$tklULOV2{j|7bwg8?>q*lGh4Pgoj zItcFV0+`R`iE9wtG$ftT$+n%V7w09mw-e?xfZalWwQx2+P8`LJE*w)ya)LKfioosZ zF_MN{Qo>q#iquHR5nMt~ZtpXsD^0cAM15z47tIH&d!?gy`l9A-$!2vj?a$rJc(YTQ zR$;|vD(HJaTWTLhJA=124v!ST?*;Mqy?It#?ukov9aLX4e6cMNd24yeeV;Zn*J*}B zJ*BYvo65DJpvx0iG6uY@Uk3x7V9?$mB|Fa@zQgb)Z1SzaWV*!??eg1C-?Tv*+Hok% zDGD^fTWIoX%|d5wx*;IPbMCkrP^goTH%8*W$AcRG0CIln<)k>wS7a?qakkf)MOJH( z7>3Hq=jeEaaoc#STVh=Z4Gz~u*WX6|vG%tI`e@_sB)W0s!sj}~_1;{Y?0GRmZ~iH9 zc5tFrLWYpAlTnH__RMw5`s>A8?fb(GiSk_1dU@&lRQ~toL$X;(LtJHcbvf$(^AKnB zs}V!D&*oF-ci!KV3HE-}oag3Ewbqn;zZKlb&*PgBKccu;cI^0s-Tm{7e&PDRYDk;; zk+Rs;3x}mT4{MhuQa{l_!>4Q)j%qpG>ub0$rqu7umvJqnw{GE<65@tRcIc%5eaL45 z&5qXmMMPY8j^WxcE~ENK=k3wCJvcead!t}G8h!5PH2xcIY57VJ;V81&z|sjPNj)>g z4=;$f7eSp|7T>e;S5o6l?rEv6i=TY8>dTX4a%SY+`)g|M8-0>NLokcBTY?gn+8Q+y zrf8aLSM2^K-~L=9GB7;HWPCQ4EBY2NK#UJ zMtF*Qv9X#O0;H|Rc29Y|P9B8H=S|xY)NIPt4!5;rc)IP|@)Bh-Il<-Mh&3xysI$1x@)GFB4fYDU4JB z0N?bekk`PzkW-p?(n7u0DZ})+hacSef4q@94{ne2(sjqvx22h}L9*;8;>%S-Zz_Dk z0Uzbz)LW}4(^t7Mxb5Z&MtiJHbLk6&Wib5FYPtD|CsPQX?v#$XQwx86U-*Q~cApaA zP}Ik`dRXBminZGAC~-a@rA=^vPh6Gk!G`m_euWVBzZns&7f5)k)Bgab@Af-!Hx3fp z8>&2OEY}(QTF(+|tCJcdjyB@lc?wZhr1b{9*v@aW*K%Eoxba(-H)`Xd$NP*N7se*R zxJ;S3?C(!Po0%qB3(XQqLe%zYH5Ksm&lddKiG=f2ovPpB3h27RtJRZkSkrghcQ)-7 z#v27v-cU%%G@?gUA7I8l%NNTb0dBKj#|%J{smCDt!kQ#IZsU6sG2*sLe;v5%amN5O zt85^pARp-xab&RCtfgM5xBI#7(VP?sbM+a@_q3_GJGtoWtKw!V8jhc%%SbRT(62ar2ALDn z9!{F-s}1N*j{2_K?R72K?{1FXf;vCpV><3H{*&qbOGoXSfA1^k{b7BEdP29{hTYwB zphQ&6nLued9Z9L6uW83CZg((H72G>HgfP>=PJb_dc0=wbZOm<2&zOZEb@m$PhyfHp zaZ|;U7kU=2{{S(zg(0oGAgy4hO$Jr^ap?T2)~nYZX>9;a7Hu2ll{#D6ueua!lc@mb z^x{kA;PFuGvbhI>Ten+x<1aTMJ2pXiCXJspkSk8pt1K|NxPWu2c?>r_Ric&dHwhNI zGZSwyOc%-%^GGU52wgMGWN{y9ZX}nbs&@YD!3R@yQ3hAYK z3gb-6J<{OX)4(hPZ_3y=gWl1^Q|nz*S1|2lB?d$5j#u&PWP&?;1~G6W&Z6ojR(!L;RLx>shv&8BUtQI-DdE3;|n47T7r^5TT-0yd-7VcY0ccUz8o?Re5#uPNf|DuaRF2dkL$?x*fw0A@sB9O|pQjnyt(f?SmzB78zR64(R0=Y*fB0TwTDwNLB3 z#O?jD5(*MoarBisMpYE{V!=DaR>8c=uEB5T-4}9DyGc~JTC~?viC?=E*F2Xii6M=k zPTLv;>*;v-+k%ZKBD4yn1}3c<9OWN-H%wl&DLSwkAS)rC*^4ZWJu0Zi)zk_geb1Si z`hoGfwNQ8GiZsWnMW9>9(-=w;U`dM{Iw+*4B_D1d$81~0Lkx&Cdkqd9TYN<}yguMb@_bT5Xca0@q3@{}Z71^- zg}?HYhTXxdd`DKV?c7L1L$n_JPPTTnind^Lo}OEAB6XN5nw6<~fzG0xwZyH2h0Lmi z%0)-fb~Dib0IRm;K!wE|mXUY9ZRWhaop^kYI&2oe35?>`@i@p7Nja$$9YsyTz=>n% ztqQ{TrXbo)^G$79c#qb)=$*kRM)~V)rIQA0Xj0+%DP0bljL$=!8ow5LwQ*5#ZV25U zQ(6;ko>7Ojnte*+i)GsA>!IeZ6A~OKQcXcfJv5pSMs>rNZLuqLdS`FGyqR>2RIYw< z{{SsqIQ!Kz+`kUc)y`(s-1g1J&5hSo(-Mq99${rpAta?M&_PuR9cXbalf<@cX@H#w zrBiH1;eEbnT!B*4T748(;@|n`ZYhc{J?Yll@%nuUTwiyMt2$j7KF`{w$GXf+I{azP6}{%=5i^ur+Y_zEJXs2R%(ps) zI^MOYxRrtd%d(ip-I9~0fUcMPA_lwOXw_o>0L8l|{{X!<{n`6-PHp>aN89`b2vbOM zLe`Zb!=Q~?KqCS84t~7 z@wzh|G)`jdWX+gO*Y5(Pd&i_b^LU?ZfBmF83f~(>?zN}xk^fW;+Nui+fp(T=N(i>!rNZuxM0Y-2OMS`dxMEe5>HSo;;+9Cvp0>6^a+;P#LFyo zPpfvt+%`S+2~A0T`$g7Ui1}Y(4WAJA#OPKwBvp{vu3>9FJ_cOldASmgJ=<6-WwOL{l#OS$7fYi`1x_A~MUpd8rlI zR%lzk&vaRJnUGvjmV~oZsihN8I${T^QxT%G%o!EuR;m4SjkK@Zvua!=hvdO>GwF{N z2`!xZ9KNja8rh@~8Yy9J$u7ZFx7D?kmiXr1IY5t*oY-w9r9lNMO$IL8241I?ns8(wO}`e@(x&L!l{h0D8D3`yLaYjkSoX@?~$ zG2c>9wpP=+JX#!W#UzoKtw&5pZahqP_D_N4fx?<7=yS8;uUT^t1hCeJf%JPh^7Qau zRU13=vf!t!kr%u8=Wy9Bt7!&fFwq8cDR82y2wI+LWgzq=Ak(uPos;~&UzdLk;Ezs= zvT#3~99&TkHWJrU!}ciWmA^PYOuUU?yz$QL;N8u-HWu(~Tek~N8%jc=NlEz ze&)9>M$9e8MB9DV+qo`Cea78vIF;#f*K5|gY1a{P?>zC8$0oeLN~`hhcG!KWosQ`| zbg26Xs^XUF^yRy*Jde2c_X$kg8>?+Zl;PWRkuFSWE=Ee|mAVk)Neb+)RqIS>?atZT z!q?mZ)O`U^?6wX{?q@dD?x5j9e`+PW{T*}e+qX*I8^dYbHF=$<}wq1mxMBM={tz8reIi8rA zhd9n|3b}Q)UdDU{Fx3hRT31wz#z##tA^5yscq;sxeoFiWeJ6fTyRrMX4sY$Uxnfie zvvy%txZ1*lB3xZg0hy+yBT@F_miww2+o1NE)pn0#UjG1)EO@0Ateza&9;xKWZ|>r9 zEs8pvP!vj?x4cjR?;tA?(fmR++t=d|NBal1tzrV@xc(Vn4FN52yI z$lm!V`q4z>+Q{Oo0IGvF=PT}eeZt)4>a!x-YjJdxlpR_JX*4IlrYEoQEHUZDS24SZ z&70pEDb>Dg<4~;$xh#*}QdoMQT`@^Gn}B$&4Y`X~wzT??IL+*)ZHhg@)U2Yo)Y*bAA68z0QJ#co^^wGIF4`OhIk7RF4 zHOkJTysfa=i77FjnuZ^3L6DTG4kdITjE;SAs`j6EUC(+Wk>s|VIV?B7eYwZlV?6jH z4Shzm6{SUfrSrDjl`RP+RRnd%PPO$bi(L&CsUb=L9LdOb;aa3+gwZ%yQ1a7KI%S99 zRA+Czj;Bcgif4lWqvV%WyYZDKGmR-U)sR>H%r4zhB4QIPSuQA+q%g{~5URMEWkFi? z!Y^$Tv3AFMywb<1?^2ikS=-!S#_vit&NktTdSUGSuN_N7bx2b|5+=HxL@oDo8jnCo z6sfHse;i z683iiriDXZ*%PZ)?j@xtgwbhN?&3(gH;NS>)iVx(5q7|rfpw(>f^tbTrWrdG89Y`y0suUEZdyX7N>ZX#KD|hH4oNqp!y;AY-AXzPIzPzI*)iZ7&s<##{t@$26 z2nr=dz=b3LNgx5&3Ekr&yNI$!bhpJo+}_P;Ad)8aICAk{Mw^54AiW|+yPu}3wW@mr zhZ8c#b8X6lUXRB?`f*y1W8=pQf$bl)dA-NTI8@p@>0^i56GBhsmAca0nEwFms#Ijk zs!AP$IV}XJX(v%`f}Q6Qw!0?7tUJ#K{{V=7)n3VWPb6b4`BxAA1N#*yn~ zKIpV>TMor}nbsG}u;N^xO7a09q-sm3PN75JiMxx)E@J3(f}XIdc6iInS^C)vg*;l} z_Jlt#`mH>{Yho48XIl39*?2s;*7(jax0H}_P*GB8pJp53;sDm4x))ZX7qsaP?GvVPD93@(_APeX&NQt zv44U`e|150e{7PZ7nDQH%3LAE=F;Y+pduj+YIE%5c&kO}hCSV#$+-80yt^LL zwIHd5+!Per9+xzLmfIwFib6?MItq8;v!33rlA2_n!NE?~UPj#)0^%gyo_8~-7ZgT# zZ}PG70W=rVo+PF;tK5eQmh+`&uo6$(gZUtEP8o3H%^=9WsiRiORXwK#duW`nCY|GO z$(WMbs1e!TJ3DYbT3wCinD3!3DQ3B=9s-H{N=f1E#p&B@%-+rVj@efoiq{Q3L zTOCtU?N=X|OP@5@^BW2!0HgwPt!Qyn&)ZJ&3k@i=ZKHQ?-mfrJ@?71$LX5km&+y{4 zKr$Zr1vyVJvF0Bvu7aAh(^WFABAppmnCGfagS9sm?a8RJSY}@rZ)?w-h;d$&rdg;@ zn6ScBQkt4n;EuO6(H>mH5YxMwJ=JucIZh>J-}2dx}&6A5I%NuI8mJ zu49l@rlBX4=?bPtud^Y99O@^OMT$iLDL(uON_neT?3Z3jQ<3e#dTNuqaShQ*`npBRaxnfD7B_|c-$&S$_ z5fXItB#Qow70s$M8&p|$N6=`7QrJB*!92vEiKOb2MwwNjXpfhRietEaI$w&e=c9 zmZk2C^IzT)(RMsV3KmilPNjD!{g{rsxT(=43wq=Y3L24kxW>DS)w*-AZj3x%g*}H> z)9gAGsVi;9$Wjz5kp{ShxeSrLE(=JowDKEJx{1PGsrFx~TLp{LZ>*%EV&*Loc+;H{ z+bmqIbKD+NDUj_nrAs6vIZX?$m84V?QMj<0#r|!%)0k3{#TCm#B;&~Xe^LJchoJAq z+%vg&7kYBe!QC#RRnp6HX@+hM0+!~&Ltb+d8x=H5t+*9Rni`6lVQiL5=6YP9Y;6n$ z%{tJkJ-nxhe3!bUJW3k;N%XkzAge-Gq|>sXV%?2pB2gE*KB@SgMB^Q*ke)c<_HQ>P z!gTJ9=`mNDDLqz;Y(&P2f=*(kRLcweqn>@@qvd;Dt914LpJfwEd-881a&MHz_udmO zwy4w)w#pWmmUaSu5ZoAnwg69IC@0&8_STU{s2Zm7$l^Wie^1kjW27Z02}(jkeEl8TDcsKx7O#z$B_3ohM;5?fZoHD7pAMS+IBJ!Vlq(L%`F8c! zr$K>5N9+?5a9o0lX=mX8*FKm=R<#}%)kgQ*WMpm~&38*<=AD41oO8F!OKPCSlEQy| zfmL%5G%@b+3+C=J%Uglx8*|E2Q{5;%#7R~Dv>0zBN$X~D;u8|> zDKNI=!K$t}+qy@zir}M1Bf3xFu99$kb=$aSj$h+bzV|jKUWz|?GnSCGq$trOl1>>Z$9N+Ip#Y#KBi90&NYZ#DnU@gI zQ*qN$GBxbN@44wpTiR6ArktnNwUXkAQ0O|0L&*0y>ZRe0qku`uac?imY#OSfxJVSe zG^$pKZA5g3X3v)0aV;DES0N ze#F~1M#3)kmToPwq^-7{@uVpvDJMST(+(iNds8W~QMlBj*Z7kqZQGnE68=qYMs+N^ z&?mrXa_+>2CE(pOk5f*AU5;FHo_8=Ct8gXIt*hsoA&BC3r8ik!`ZA=%S4U@ zlj%RTIPbreZ>32803x`MZ;dvU13hZK%UM_hpdFPxqYQ^}?*9NvCsFn{4_!g_pW1|< z`D*$!vF**ne{*49E-SS_l_DcYa^1A~jIgDqp)Ly4jD;|3UGsYRn$mPWq?T{8*oduM z_JdG7Pwias^eO4%h5bTZcedH(ZKUk^c37=XuqGYO;Q+GQUM)I>e5;7o4O5vpV?^8; zYq(=;n0Tynx7$}8dn4O8;Cx73FNyhj`b@UQ`DnaL8heZIrRkGr%5WOc8o2q)d$B^g z-1p)>rxF&`XWJ}2Kx0~cg>;Ky@z&kg{Ci;8+ji8&qR(cE+Ys%nHkR6H2ps?((~O4y z03!KkF0QMfF<6Lg?SyS;4FN{G_AF}@X)1m@Pqz3W4JiRZRiLFX?hDDPJ>r?&V_^eE zjaSX*tbMG`$L1ru_IeQ?Z(ZI%+^}0!TV~*@5TvAJ0>@F(qZ`iFxi5;2HC(6MOAjLF zt*y|mz5ZFoee;X2k6f52k1FEwp_7<#vcqZWdwe*|yNh$taqb!|@3e5XY=68LLbt_& z*|hdn=)1sRF>yt{Q-VfC!J(BbXY4qpVK1kA;B^{RXd90eZw;G>XstXK#UCM7>j(l-Da=lOJ~Z8+CrA4_E1wSS0KBP zxCK9PWeZwdRR!G_TXb2IShcL8GF>T-6-`=uD~%@_!sgVhlPhw;B?5YF30Ao~VYW54 zn~QHPMb#gPC!no+@Yebl_3+V2+1^580=laMX4=c0Cf#mo3(zA=Xew33ZKyDcj@6&AQT!Tena9W_WKT91ock0kP2Tct4U8^qYGm4q35KgxvV>*0EGCk z-zDvS0^c`FV?tcJZHn^kt%}mFw+ZE=KX@38k{+wDrLFHwc0@0OZM(m3&B>2_RM5M@ za6Y-Ew9!x3Sy4y;W};3tS@F@mv@15ojGop3v{qwVJ`+5{rWvHU}#>!m+wge{W*c9*Wz>HuC zGm{*UE8QG@AQ0xnM1SeEDbPpx)fjwn>&(akiz@fWE|7s^nFOETOrWv#DL6~yJt+}< z&T7l?uRQTy(c2NW+f$}BAr38aB|h3pL0YOaLFh3;+#6D9AJolOpSE{>v@m&ZRX?Au zwr#`7_iM)Ay0><=&0->xDXNs~O92W^XfqVg7Q8K^qJTBrLZWVK`0g$`nW#|3UoUH47N24Hm}&iZ&AW&H z0Hs>rZAbmz>F8f+UWyP|*_(}O%gmJCU8aa59EZBNh1~lpKX9vdw-8FnpdQRs3&~eH zCWX@Z8VkuvnhFM*mzG+M2V5E$Nlh+p16&0YiZu!n=0{dz36&udFkBf5<%X%)HQ=i! zsr#hJJ0d%<-7SB7WH_Ohgw*HtV*RnL+WC!EY25qA!)Ob9R5P`?1iNHr2b9vtYzL%_ zj!EtIj7?;)^`DBZVYdl&ro4jVW$Tu<;phOuHN5vK4*Ls`s%MtKT7~{DxK;$f0USqQCqg{n_#tTM}>Zq#X|9$3z-qB>p(h{ z>;n;T9aHO+qh(=JZqGWk4G40hzJTG)lwERanBPm~#YAU(+)SA%ju)sQASFGRUo9Xn zpyY{!ijAslwAqUZPJx{X)&Br@5bSLNmUp~po_jrz#6LWO7bNPT=msZojHeY5mo+pA z;W5yNl2#9eC{<5(SdqAs+$uiMRzMV0XhP*oY!BU5YWi!6b*ZcBu2&K8)ZWH3U zX*3^3EH@v^ReLYysLnfvsmg@n5Gw?+rHDgRmcp~8X@`(Hr8T&KQ7n`cYEk-5AI73w z+NAeXN~oU*A5Iz>6ZSAsXn<-5Q{RNzbwwm4)=g1f)7yzga8VNcNuzpRi3>_bYNyeN zY=^|GIPZlU+x0BSsUoRAqXT2`6x#RW2unNOTZIIbc&T5f2e*=sqV>deYIFvQuI_}4 zo{GP%Cb*Zk9`W@kTe3W{>rJ(|Q!i0f3nWncaMn+aN->1Sw_Mldx=NN!4SM&S0iUTH z+w_hK!R;h@7HH{NqMZdshdP{GY=IGlon+oT(wADDHpOs(b+yTI6~+t5egwA33Tej9 zprC#DlD3)6b!wwBHnCkmSn*ng^*#A^ZtG904UKCXfrT|D#*{YIM+)l$^tsW#~+R--CaNgS)iB^XjON~6#tChYx z%`Gbm3PRGd2ssf@aiH!gTrjU7>(YM3ne2u{Hym#xLiUP!%|BZD%e{Ic^tXDICS})! zHp1L_#|qh9o&av8S@=-c9hl<7y6)KWI*R&XF3qr!fbFZ{E z>?>Wt=#rCWj?<00+(#CbB~9rdf+{fX$9CWOfxTy6N{h&BY+!`D%Wj^4o7{Wp3(~gu z+1ccO++H=6<_f`CIkR&K^dovqzZV8Bc6QQrm z-_iS$S~Gn-ZFe*%5ZZ0w2~i!SAz&Y;5#xH~dWZ23w5P|nSeCS(C8y{gv2s_?mfG9a zugoLV)w^$6qHXP+weEYK7Tnvh5E+dMD{>iSEwtG9KvEPy_Ty^acaN6|8FsEe;<@Lx z*p^%FiNgR590$-pT~~CMd~K9SE?GR5A}Wo{T9BlvBeyE!3$}BeqUotv2C6i?a`xPX zxFK1exkq93q!gqI_?J>BQ&4M4VqKU?UmsGf+-KsBZk}pk+Vh}p6fNfbY_dfiAg59G zR}hzR7|j)@9dg-qqLHJawvZ5t0Fl!Ss-_4Wl2zs7@=tkw6(uUn9=T)Du_q~@TH59* z=y8gT6cUkIXOO}YaX|}e62cu&Y^t@Td_P7P8fv5~?y5oR1wV-FPIis)`S!^uC_O*< z-f?@|W|y(7gLt>D!d1#2M;nuyFaDai!Tq%+-}nPCU5#vM=Jh^=M}2IoQh?9I2_DRC zS%zcVar{mznD(1L zEq-(cV$E$$N^8j_OG<{5PMC9VJ~ghWOfojRf>v~>n`!LdQ2Q`Si)g;NZ@0{Jy&-^g zB)C8YR9a7WW+k64I$kOo%Ne+BMFF5G;pKDWmjYehFE-*-6qf=LC`Mh_k8rZ&6V}I@ zC*E>@K)CvVh+6Y^K$H|Hr#yzir?!d1M?+-pL0I9r%d(X-yx{zY;;znrivc=;{GHigsk%% z#<7L8;GFijYK;OUV4Tv^VSfE17b|wrfn>Q$kK+`y{hc2ur7d51q?!SjeieHvqwTKU z)|t>6Q!tq48g~lkg=KvJqKmP&X(cvWhTYaQ zh)X(?B^7Ob%IXNgpnr-qm5NO{rA{_|*wZG%bcHz5iWYiioGo2ces&(FDaE%9bu9aV zI9OF@?~SW+_LYU!syGu;D3$Wt$gYQRUm)(C?Z+^FQX=khHlCYx$-s3lKt{HdK{X>z zRrX^A-B^}Gc68#q3%0rGuJPfuuTF~ddJVZp+}oR&6?S07UemV6eoJXV3kS?_k*BHn zaf|ml1JcH~n(L0qUf0Yn8Z}+65%mGEIZE$(+0dF%ksYUXrS{ShA(SX2_*Gi<=y9Fj zU@oj!*?3iK`2E9Yk$a(Typ;RLR$dRX?j-FwTQ1KYec@$R`3_s6FU@gPs&y>~I*df$ z-`+W$ZE>P;Ym2jNg@vN3!_VQ)CzzA-Tz6)CQiUtFK_KT|*6{}1-Prx|q(1?(`-9lM zZ{zKYwHthGZKZD8e>G2;`3RBXd4(W^>+%wfRP3%B%aWd7jMYg&j)E_Npe}y;qPeeO zau*iw$Cu_?Ez;yexFia=mSm!)pXS#Z?#s3}R*r~qT=Cqi;bW7I;ok~a+x)u^cFyuP z@?Ld0X;RQqDeWf_*ZXYRd}fNlj@`ochB2aw`QwUr2J)X5dIcv{iaOCLq^gd2VEKT7 z+fRNhTW>cMBg!1rLwN0p?j-Zcr4@KZ{^bd@NB(4(-;^kjR^dpec4roYZo+C>6-}2D zFjJ_lSGwh-66WldQhp@W!YTCN;?19QB|i{&8n~qY0OvTA8tx8EQV;sXnkzosuwRsS zFo0jP#aUg4%Ucmv5gzxpqR5`XDP>3vg*cwlLdHiFe3sp~^C}%cR#eE9UI5fKsGYk2wlas!v0O;czNC<~lVw`bOsx64V_s-)*FdSNlbPdSb0Kq!rW6gh6kof_(B_ z!|@%>@=UI*K|@iapaGYZojnE^zE+ZpgoKt7pbkR38+$*7GT<&d{{T2C=qb=;h-=As ztIih6uBlbxQ>vvaMIPUNEAkyyhimYn=~jSM`|A5J%s*7OB>A+G+*s*3_-bq3b2xtk zz11CbJ>+@k18uEUp{7_FI8`MYYdLd$BH42 znK*g#1anA9Zqx&ET#)H;!|`;_vmSy%^QseS8WMt(0w`71lo)V$rpVM!iLAPl`YVK` zUJ6Qr$T3H5L#_zN(IGo3ldSu`>&M&cX78IKgLjRMc(q>bb<7zyi-T7t!2qQ|TE89I znH9skh&vmv;!Z%^@S13{*wY%}aCYXT?Tx7p!E?FJv_kT2%&o^9aw`g=Q<1%Hpd7#m|VFTu?~= z05b#q&5|4*c>Ar_+GwB|MIS z6AKDbfyj=yO~E*0)O*OUDT^^*!=XS0d+IS2k~C_!Z8-YnD!eeSJH#{9N~;z3Hmgk0 zoJy2<<9=?vfpeE9YuuM?UDcnLxs-uNnF{j~6;WJ>sHPmi{)JZE`*u8p1E`%m6V7Z$ z`RA0jMYnONCSK!|#0gX5p)D*lqOTDo5J&{(aO&bVNGnBG#!Wsp*oG2liSmc08*bIP zN@H$sAD@VaTy2EOZ6Q@cw9u&pc8;{fT)6g?H-zpS@@XSgQ`}p2lDBqd z=9O=d1qnoXj3z^5`!xz;%-0t{9JAv}>PE&!wv zQ$R&?%M*=uiYeI!)fpQ#tw|3|QnebLD5hUd5c3m%pGFb0?StYjxlY|ceu0Dalz_c8 zLZ@xo5CBY4v)Z*Nl7B`G*3?$tzmie3SgfnN+9S@kHzL;ThLduNH8fphtZPVGD5TVO zj+n3R3#lQtbD6{iY}xR#=OY|)k9E!ftAhvApQc^Sak(*Xf1C?i*x4y2sH6zFhYH6tjh>!%XEdUJ_5y8F5AaxRx^ z)wV*HaFpWReNp1baw`c_j;NIcp|BI5OjEAhJo*n5%gdulT-u*3O}t*EsLaG(d4}y=88V;I{!G#)iP2_1PO=;7nwD#2DVZ}E^)T)feefWCL zDU-t8wIC-xy>PgANfs1oFR~N~T>$nR87Ah{D7PgFNmFLE>)A?s@#KXQj^6i|Z!1!? z)kIgn5P1)Pt0pK`Ko{6I<*RJ@KTP`tvfUP8>R9lj*$zj5@hr=7dE48|~e z6}#Sc_hUYriUk=-1$lHMzZH1MD%&$Psvg&OW_7$i!u2fxrQdisJDEo!`GmC5b2FQ_ zHT!RGd%fD~>f%L1zuiToTl^eINU z1c8|tr|0p}vE+cSn=dmgv4BdjmpjDEWx2@`B_uY|N|cZebj3a>I-f{g&D_yfKj#}+0X~k9L(7#@_BDkmA==v+I ztVB0d%G~mh!Z&<{PzE77twiOMO2pPP`OQ#S?Jb^t3+B=g`{|Q{LeI=Pu&l_<(yrWf zCO(D`4}#(A>b7S+qgt!Yw0XyR7NJIR)>6!N5{8^Z3pEKy8Ps;-m6XNV`G?*WS}qfC zwKLR|SET4FuDqNRYR>6pM00yA1(DiS2Qj!9pI zdQvM)F_Ky-TWD#j)+=6Pg1HXEf;MWUqzL5MEyQGu^&EvgxFnLK+Zb`xCs{2rcLeV# z1o;Udyy^Ya)OX=4cpoFQNM!O1w~b2@(qlwLs@mU`386)iT}~QiPA+Yu8$hTHCP#I+ z>ZALFbT=bG1q2NHF%fjGmZiNMQhIcyL{lO<;i3fR+tU78S^&%}X;-+MFK*#f*6Kc{ z)G6<2-Bo72#C0d`)@psnF5FmcC+KjZrDbjW$jx~#p&!a`d^K(L62+1Ci*dKTg5wbl zic!bn=$z2cu1HoX-O{uVwdQrm8wE#Q6=s>n|k7hVvakj*c&><1* z*2DqO_#rkreHi8JsHMY~Vd0(s0BRC#>SNaikjo(aLs{r@&kTI9r3{YbtIw#8K-?T< z!aSk4V%%cJTCMIvwY4EYIH?m;^63mD=B15Q0~VdBh!+=4J_?20Z2thsw|4paWIDc5>q4;{LC3A$RiWD8sfo zwp%Xw0BRo3ZaKa!TvugD@Tg+o{nK#EZX*`<gnBtuYQ%MxBK~YR-TPH5_DX#^L_fG{Gw;n5?K7@8t z`%BZ#!TGCV?J_S6DsYD4G`N&VQBo=nU=+}gVZ}FmWJLF*gQC*AZHjfiHLW9(nLSDF zgzXLtfLwQQNr}aMKYA+W-Y(gGn7GGiT`!H=_f@*j8a2&@h4s-*t4t)tVb@afm6m~{w>&{j zw(enXUjV1=cMYA({IRK3L)1>$hqETxCdIeOv_+byORPMtWRNuq)1k%pX#NlH{Z2PK3EAJ5!vj<-!BD>*|*)|mORivsp*0WCw*lu zTc$RMj1nk`X1Z5#byCt>^JN7g!UzgZojF&pOmb%D z)xmcL%6Rg1R-NMBr4+89;-Sz-6?i9I5-YIayrSdrkm`?8p+UUoMb84Zm{7YK=PP@joJe!O2c6VIPjmG@!p z`7i>ddtOskD@>QBUBG5$enPZ>-A7YiopGXG$1CKkLdo$rFhOiYj-XY0bMSwq>&E1{ zZvDe)jXwQxbUAUkKP~DwP%;Pa0%$u1CgHuyICQj-6)E_QxY-P@p(Q%3V}Wf_VOzF# z-nUG>N^uRqg#svkQ_i1(2n{7lQOdbuHu;x8R(DV<9y0SABf4!HR}TQLhS1r&r*1)Z z`Mcdt-?pi;U~PIOa400I+7jYAM<St z=x7Af%2Gfgy)zhI2piL=M!L9nUWSVhcfRGcyr*qDf<$NB6p*q~qU8;9t_u* zDX*|$(6WiAK%(D$EA*<@shhT1A2d#% zgW<(%7SLZ+Y{|Q(H08f3LfrSax4#qj+!b#gXz-&ZR;mhh2AHq2inJandfQb$*Pz4u zcTlD`e+pVqBxGoC_!SBYOIoON&b7lrpF$`W$ReQEEDLz0s+S}G0A?yFiAHcx6MRXX zd#BAVhapORxQ57lO2v)%629GRmK|L>iVvh=OAmmiSp)BckrBe&*Bj|YpVNlXKi}0q znm=}`M}mU<*rX1vxPk|9Ml3l00F3&TQzj_irAIT}2zkJ3QfPn4G{iOIz2}t*1>2B@|GrVnHN?W)o3Acq- zs#0>#w+`HBnMO6D`bhkqd1uq_r$0?r{ol#^(r5GUB;9V;1?}jTdGmf!6nQGQMIp%T zDJNTOd6X*ZtVd-qcX#m)kA+qpx$&5slb-tZP<@*4T`=U2%s11v;M=#$HNS1{d-1u) zag;4Kg+|sKNkAmZT4+7v4Dmg^x{durg!ebzYYT{+#<7`t8MOT&r!qgmPBqoF!Qkmmh$XP}Bab%}TP^>#`-N^i_T@b5* z1bV@!S6UzDi|Z!NeC^H1F=srLFU>{6YO;~3U5Cm`0CmSa%d@c3xY6HOU8e^2&qmQ! zpJ7`|{MbD}USnJ=lXq6kYBlaN@g^a5za~bB45^it)AN}sgpg=xa4_sFSv7052=n&? zF6ij?&=oaof6fb#cKw#gwygYp$CoHJSjOuR!4hJh$(Isx$JDsc~Y+Z%ag zE-(E(sopoc7m!8J)>fXM@KaPD&G(VkrXQE-`)=K1Aw%aE4|Ps#l;zXTfS^5CFL(?`epdlh>8x^GYN$E|L5-Vx$|DgOXFyLi35X!C@l5x3?bmXfj)=2KV& zR2A;Um$*<1TtsZGNL2pTUi{|ZmP%8r{tLQ1%H7Xn?FX(~Y)6>5iK@i9rGk|tr0Y_C z5mGb8C$|e9a0B=*klg-c7VdL3E0bGed~QAGakFpdxU~lNWW75NaW@U}{1FKXNK=DW zm5Oy8p3G|*2V1r-n(8E}&T#PQS zp>8<&m>LI)l=UnG__FTAJyfHHsFlPm*pBE|k4XtW@k~3k;*_}>B)bIK!VN4GI?*9@ z@2&=6?(z(z1dbHzSmbmgRuuMa9YJ)7(b9@kSe%>4eV+KHwxdGq7t=>b* zF=zM+6<>&T>M=8ktK6eE9U&U1XYpA}sa)|=0Vhx$bH$`uk~k_U$hN#vyLZWmX#p-J zc?yd4!hbC91aW>mQ_cf$C#{`qrAu3SmZP3{i848cio+BUy|n_VeKv@XErqHwe5Oy{ z3ISiQ2tMt>I{6rh;3|=~HJO-^ZZD$J+l(ygiVI+>q|+?BFndgksl=$fo)RE<4N?TV z1J`)=!SXGs1)z`=fGMO_n12pPgQ-nq!7<~4iurEBy{W`|`ts~1(zx*oosQR1l&BNZ zxRt|YjJIuKiqGxwZH|&b$Sw%;ADy}PBJRaLTT>@SDN4HO1t}k+8`~hM3zaRdPeX^T zNzh{k*R9-=GD$sz3U=bt7s9f8;!Dr1Xt)0W5td^$oOVqfbRUTJ3@DIeC{iWPFU#Cg z;%T929sSh9BCIh_R#6Rcbq_>&g$ki*BR^=tWR*Fxi>1{~ExRkpeJNV#QmqP4XraTn zo&sxWxJHLefj4vC7R6X@w5|)uAE+HyHj&;H6sPAa1!FZ0} zw+?!eWwcVq5%RV3jc(T&HrEG{cVSFPX<4_JqEttEdk!jlmkP_4f_W`Jx0{-484Fv$ zuE_pSdx|db-u&YRdeJ)ND%8^Dz7!6$84EzvDu0U{yWQA)m&f1=`WM(K%+~Ug#DyOE ze%-@q`lNo^@{DL^;dg?20swjBr&0H$3_IMI;c7%68*v?cMV&|M+)hKn zMMv3&`=;UZU6IHsEt_W@t~I3bDF=|bn{QjT)$-JzJY6NSwxcBo@Ks4E2T?sU!SFkF zM@}8cFK&(D1UAK-%IBK*9F(L5wgS`JKx>VRS%McnOP@8-CqKGm5OuheAa)dt2gw9M zo+TkYxMfRMl^G&Jm6ntslU|(@g&t#!YFDkNl4pCMr!LvG?jOeO*ISi}bgNWXn_Sjg zj`xj_pj4iNs2H#2?xK8jYlT(oZdlxjx}MY0x(#Nr@vO);`^CP+@`##{;uw>uI%+ct zLx&1w+pF~wJpMll2Y1=k6=Q;A4U%r=)8iSE(0fi2-Y~b#>Y_HtkeC4E z74`Lpvemi$5c-g_rLQsDro+C3BnpzG+m`UBx5J1|-`)9nRy)9jVD5%EPlb_(foUGl zz7p(DiB{-uf5$e`>;5_c{dn(Y9T&_SpgO8Krz>Q5M{RKG9tsyH@F#o(mqwHVXmiAq z4F;-*aKqI=DwM>#bR`o$Yr7K?|adat7{Qmj2 zwizQ>`Gl&Sr{S&`%ae$6PQS5oP8u+ze^ET0v3EZi+LN^Rlnuc9S4PFg(-rh8Lv5vp zDB_V&LUjNuPPl`+zK^HfD%-F&mF2b3GPZ$I4V@(~Zp&1Y;%Xw4>g&-V2_zDGGsHo; zs@pO)^@xN9=-e(rT)U#Esd5}yVx(8ZPJO*Ah{>i63HBP#sk*A)dz|crxclz;jyOqC z`%WQlt`>`ovkeKBwdzq;uh=mRYNx|!ytRVkC`qoRBxUcwNN7-kpRv|7jPe~Y;s;bF zH;vV8I;&uJn&HEpRMs@xW;&!l{3u3hClQhjQ{gpHTdd@*NsQ6b%YAbAm4>$$9ti*s zK=8l)ycHE8W-`fW5tnz@hgOp5R9m=fL_D{a0tE_b(uCo|hi?_;KoXVX|wpm3U zZl!)vr870pyA-?)jI5jzG+J)vzP5d-6t5t%ciHb8a8c1*8X@2`N&NMMk1K zO>v-Q2k|RPtj7lMVj!9-@^(+EOFrhkTbtzsO)W`~mdlqUB&ne3C?P5p0H&E@YzUnY zC91A$u3IS$e>Co6Z+e;X$IWj>%iXINBoJ`e%@5p8K1%cN{bKb|p8}1VC$-wi zs(kQMx63~vGH=_8Cgqzn9TVZmL+$8F#So<<_kkF#=NQ*B;i9!|i3paps=r>ZHn`rN zk9|a010lz5nrN$t)Y<+1o`lATOU1KVXB{KKp(3T(Qs57q3@J&Nu~_+*%wo3Kd50eM!1w&X-ag^Qw2^U zpj$^JGodUD(CcUI#7^Ols@2}5c-jyD0Nf(~0GKP?upXIexL#hd!~AMt=N8pj-}V`%JQu^d&=h|ZbGDb$#Z&3csR-wfM``y@txtgMhw70s@ zD@`6~a*9c+PGI)pEfO~@51bLQ#lw~HAV38HCfbq@E}!qm6X)hs{I&O%t$%3 zN29idSzYE+9Qwpeo8Zc2ibR?=3U)0nO+8JmhinrKb(O_+pJxa!MM&yBH+B<^nH6!I9Ms?L1 zjP#_cnR~GpjeyB0Z;F)6-UwhW@Lb>7UaVCa^BP5wVjRy9(yFLcT|KzJUF|?x4QR59 zP5R*>Z7Qtgl{V5Kw#3PlK}8`$m(h-IieQ~Uj=XLA=z+sRVR ztz(O@`3c>$%S-uo)Z!s9Dx>9o;rg+4$X^NVtIcMZoLIHI>1feS+qz|`cE_N~e5L0S zYLk+Oam8~U(hiE{mY8%=NBbeIskf#4@s8uuQJP#=hCC|6?nu2puPLVOvr1j|i0cFN;e0y@ zxodqYFLOK#2e|kzp1qf4UD>9im&6N00LjwQh{mbR+Pk14fV=?mgJTFE+MO(vk}5?0<0Zf);|^Xp|4A zuTG9+6(9umn_{c_FmLTVr;2cUfrb03FXaw=;G53E-B!)Cmp_=M#Br-pUA(eG>i85F zOsGX`iF_8=zmDKKO+Zzh*}kzd*}1U*9IHnjMv~>cxiVptR!M9gfhL%o%+t&&yR>cV z6-laeU&CRxkW)kEHjrsb>R2DI6>G2Mm2rdlp?4lAEZOZ$ryz*Y(@%3r7|1R^npX2* zKY%?;9Xe%$r4qfl(i{XTfjIIjn^nP2&mVj>b!&?wTc;vacXLxJ@Tnw?L}iv(+^weKup6s-3e{FQmOFt< zs!KZ1gF;8MyAM3Y8$`gnF!K>zL#>n@Lrrq7cu~pKqblUVlH!uGM^zJCMwwNJL=t1f zfGOM82!y2)cao>;+5yizA;hl~tX*n5L?Iz6r^DWG>Uls2u|w6VO^LIV@`_SVwBjyl zl&m(uB8;f1RHX>$aM;M5kd{)UM{GGzNa`@7Wz$Y6nTy^?CAl|g?ZrSUTZ#dyVIO(! zqH`>Q82XB|n?GbgvRe?lgUF8Fol_iAsHIeN9k{vUEn|tk^2hU;Nv>q0Mu_$Eb8Tx= z1s3>-ec%X5BOrQKxC_hXP=KG~F;ctYRpRq;Maoy4`AZttrKExqGt2?)%MgXd%GVb| z1qn#1UzFnYCZKdb;KE0Z6oV0n(g;!%oQO3C1!(}H7FDWlu}U+b#*h_Pm>?gZ;=^z6 zDlPF0Bo);E06;v8BX9bF;Hln!lB8}Za2$VCDBZ@e4S(^#VW#ef&c`?&N-JkQ*4efZ zz?JrpowD?2l&97fplH8gtue|H5Z6(6MZ6%i8u3lN+@F(f-i0_cJ1vwE+G=Zw8&7-? z6m~(yq$d9WBun`%fx2aNEL&zMA9Q8Y61z$=Z`wU7soW`gF!Fkr&I5BH!GRuJ#Rg-w z6~)MK)_{3rl5yR3?+t)@m&W_8Z@neU=F@WT`@{{^dqXk;;|X$$ZK!w(6#CRD^kR{5 zo02ygF2clY>uZ}fmkmfr6FGBmw1<3%QPe_DmZb%D4(vw_(=Q4s2KKtpikA?qA3a)P z!`nP2g~mjCjl$gx``S~Gap_7z$@-N{OXjDoxObseHhwj++{nSGtry!Zl1k%k9km*o z$}P}Gwt_2T9KVr{vz6Kylm7s20?R=Rw)D9w@{o+myMY)+J96hxaZhe8*#u?oQRK_S2s4)c)!T16#hHy{wBnF-t=a=hl?qqcj)L0wp=$uB)UGz(yN&r9$5i{E zs$aJ(cT2U+a+c~Dg%v?>I-{1F(tvm3%jPPjye671#r{{<7&l&O-4@nZNL`-JR@0Rf zAjm=$Pi--b_gStM;I5MFFGtNdIBLFwKbGUlj6T$ znB(QKwRu$!W^m6tdcNcvS0?(Bv8!Tn#g*2i?OruX)8mAda+HIH*|ILLKp|u1_g&{{W4jQf6_k``Ozg zdquw;p%@|)!_7k|DJ_+CDeTI)mdNAXaTiE>g~R5ev$^XruEyB6O}~0sAztju55km? zq$nPpl*T`IVEAM$a_DS+L6?vRvw=u6uCahg-B zxWkhflqEWpqal?w`tc=)kTzgcHsOKKwjeLf8<93O>xy|VkhsrsLtVQ0u`hQ!Rn7~E z(x%rEV$htsl$t1af-%t+(g(&J;)U z30v!utoB$|7>=~l!VcoK-6d*y(9#qm#8GgkQm9B7bva?Zrdm0T05w59qvI8E6#9Q+ zuXa0L;=Vvyp~iLVbS3$96)6Q{Q2-wNOHnlWghP1QCGFK(e-KGxMa8u^n8uorl#rUJ z3evrpF*QlG$)*;(v-e=B<31Fp7E_EWhsKq49Wdr86KR>HQp($na$!w80MtJ1L#Q}# z_)#Tc6MXUeNt~_RjcA1(wXoM z-IDDcgq?Q%M?WX`k}KSCsck&3Q#uJ)R{g-3pXLFfT_^mu;0fNdA8lt?iLtK^d&;i% z}edZZ-sA$_&%FT*2o&56a_Shq!u4@y!TLn=y+d4ZWAoN|TxT>1De z*T%~mEypDsHN5vNy696MbulX3wf({zT344tiC5#9BXtTgJiL*~q0vhbx2y%Sg+I6> z7xMEG6ad5wn3q>TtTE>YGLvqcrDA}sFJ76{p2X+b?81$~mwFJ-xY!3399+5X(!!lE z=Lbt-qrw0`ULQra1C$(_%(UpM{{SKLVBVJH*(I$nK~soXWl01THmao4+1ZKgP83@{ zpsKEWays%pmkksN8;o|}EZ_eC3AyeW`+DB^30mQ=K_s0ZRDuV;7P&KW+KRG-svLVl z43qj~C80ZHeq1~(%sJ97oP~aUwotXL=WYnl07+Fy38e>aBBAazNC`}X4Qn-oTqVD2fc<8NN zx5ndf*3^h=;!}fm@7cG>Psn;-n%a~~g>vhVrWj+ftY;o7&3%(|$?#F??oFs^DgYjx zF(>6HFEJ5%ewq-yL$-saJXur^oV29rl7odcuTO~Og+NL9-h@c4Atr||+IHYlK3Z8~ zOIGNTu2KOl!)b23p4|1b5TiJ3s*G(smi-XbJ2`(%eBpN5T zsKzQDYgN-S3IGzTWlh485`0pMP>-h$-f9y!A9*{aJMtEiDgv8qBp+^?e!NLadLmW$ z7$mq+a;2#u&<=+cOz}~k<6RZukN^_?*oe6fJ#ce0un0gojYksJ6>zGI$;s28QXRWi zuuopoiv&umalsPZk9lzzYQGxBUg3zW)BV*cw;AseitV)N4!US-O68t-ZHON7eVsn) zJGj=7ZhJnI#MS7iP}tUg5k~DOq=bQA`cu{sRWGIfDGKY1kN6|hoSSNQ$jU*$(H4x=^~GQD0xHw9}<-s=ms$P*A?#VROsN=}6q>J{S1gVz$+e4WE) z6(vGuaW>vTpcGRK4@{f|xNP>c-uPQ%{4n>nMD zjwf!~Np3U=zdQ2x^ka2!8UC1dH03DN6>Yn6XnV($+InFYZmZBm98$L%D#gInfQoaX z`M$B5ZSiZSv81ZxnxmoL}HuKG53R#>>XLRht_3YLxWJsa}Q| zaRVblY6e*WmMiwzM@=Z|HKL!n#76`U5eipT$=&tOd(Ug`o;96nfAIEp<@?IK$b8j_ zo=e_Sfb^n5LaKeZ)iJner-;{DtWPPqZXuj<4J)#IWyvtP9>AN-Sqw`W)SsA)q^*@u z1K}wrzZq6vAM(^Tam}RNzYT}hB{Lm(;9h9yWx2IB0?T4X3oQo>uE;MG8 zxfdx6T>TX74jN)~7q5;N?k4$Mn>N2NKRGVd|eJOY5<7ssp6L*@_y|$8;j3f&&X|4sdoKk-`nTX*D`Vdx`(YBJ`=0GMOq4!<3tT~BA{spR(OfFY%yh< z9R{Q;9!BcseZ4Hi)Bt|P!@cDkPJO3=@W*wB762o{m8bS&d=cswC|TTlgm|eYWDw&@ zWlAaY5|WZVqPUp`pehC6>5!t4V>1Z>mhv6_rkQ7fDHgh@o!O5ur7a`GihVd_=G93F z6H08eEakk(fBD_X*Epft|~RyH%d1o>O#Gv3Iw&>e(HN%9WjlP;-uH*-wiDG z6gnx_4k8K$Nb1t0<{crl(ByL`--ke|WU+h2V6MLnLBXnK$1r!k!nUE~h%4xS<<}+C zaAV}*`hBalw$I9Yg{>Tib>t`cvwvM>Ty8RMZP}zsu%rEq}ajnh<#xm2TLxVVGkOJ@#kHI|93=km(u zwh4oVizX zTp_{QJEHLHi+n_s!(nRxgaR~->C+mn@7m3bd5z+^*S9^xl6gFtbQM=(_qFyl{u4su z6%q{+0*L`tAb#-oV>q_Gx`>{hOLD!$*xUeI6d}8KpNZ^h6K+?P+navBF3Mg8q-Js; z;?0fDUdZYJRZPw8d{iFujS~xJY)&`aH@8@IwL1x=e{>6U{q1tkzrPN*4p#0csuDX- z1&q0xoK>E)Z5^q!3~^+%$BeRcljNkDlU}4AxXvyzw=#`fwo6^HX94$G6S;0&R$WtV zrL>P0kKG-(VdqI+Dt)$*fxs&N0KGS5s@uJGjjwWE+Jwc?Qd@@sYx2YL17M&Ip4?8u zV)x%h9tXo+enY|NNvX7lmi;lr6r*Hyt<0z;ifD3J9n|#g=NIu zHSEyUNq1eb^N}apB)T7sa$(kGAkcz`P*6wIaUW-KxFxt$L3Cu^4U6GYk%oA{io8j?adFHjCZMicw1$+V%s9akOCiq(inXm%Vc>< zA3;t&idsLeAnz6OcOu6{A#1pe53DC!NUaDNic|FA>|9hG>FR2&yvNELirssAX<4SO z)fziZs!PnPO59U}SHv>_3{*3@X&Em=R=2UXMYOi!Sp`b2i zdj9b^f)@{Vq^%3^t9ZT;0u)^SSgNe*Ic8301LnCO$Y&sxt#_+Jqz5J;X;Gka%T)+Z zN_%nU$wbEQ9_MgNi`|jM&_~>#Sss^#Ubx&laE|f zz*pVHNKev0!+YsXXrypGxjv;RROCA`4J?G670w9g5*M0#K*LiidF3tOuT;dS z(@F@Z)l4DQ7rdnTitti06`{`$t-ci2X4EATP_!BvgV~2wO$!oq^|cBciUNd!5^L2* zrWoHz(h)6V4mzm)3NEicwW&ROaSG8@Y!UmREyq;=Z1(gxeF@PpxeF5trU}oa6G2Bq$!hbP{1VT@liOE3cGQ^L_4^-9`55>yg^hR^PU%Ls*o~h zzNkO!@@&nu&RkXMD`oE+?d~#o-uGplV2NtH&9yd$?{J@O^0dTSLdu+rTAXE_(!PDM zVE}3tiQ1eLcb#FxjeLDe=!t%m_{KMw*`K(cZCMiLiLKf-8OYxcNv^VX$VmKv8oH53ZvX&)n zXubdpcSf|SD;*6&y5Nq?PI#2!-ve&ZPL&w$Pfj~Z?J3KP_C+}RmHW`^!l5F9q$|4; zjoi-yiaP6RM&nR{gXzy_TCGWx)sb&d6*$t}d!~InM+-Z1NO=U+yE-P0X?EMfd_3)$ z*_VB`l}tcZkmJsU9IO0TZTR%=Iwz5BduF#)4WrZN81JX#GyIPlbatF0PK7LuAdJbc zTq*t2bqS?YJAIvNIjs>0{9Uy;Ym)XveV1r!ZrSezZO?$#tp-R^R2SnUf<dSJ7pnF5`TyCG~VzY;w$9~i4R_y(_mlpBp zUkTg}m*H)l!DN;H06BoC;=F=$3yA8NKWcIG104SVGTp8m!uc_nfzaH!j^YhHh)bD1 zYofUdP+{p&p*Q5LgQu_x;1jsGrtGIGPIwBbxI#jCdG*h(5CuyHC#J=lVTXB-BXPwO zZHcd&8kMeI`~<+$MHhG|6D!nwEwsJAI$qF_U$$x1QlL^>S*20!r+y(XnMUfY-M^i} z=8B(jT*^w6+LhuIDMkgjH6cB9mi?OCOUhVbqz2MSAt5;do*aW#sx+A3$;tGnpK!RPy6T)2Q-G7* zmLi_6SZ{PD3TTp}OwMQYVXTVJA@@jAY=wpF8-ty7#ZT!NpSd-+s?2d?rzk>w`b%{e z2^;flD>QBkEXK4R^faP6d+_HDYY_>K`)@_`Z}g@Ir`wvX`@QQ^)JT1&nNTP9kV*RT z!^}m+BrQpATW-5yBD2t(RBhb*K4HeKXNI5DBY8r7L(N>|3k{M_Z2Lib0;x3OCkeoY8 z6_i{J-*8CgO-aM9UVd{M^9zIX1Z50ZHj$-6gH zi@mJn*MVWfFLA5sz$Z5az`RZ}-@%Vlgkd7ExWw+S&Y&st5G9x-u<{+Ri6&>E$?D%Wz~HrAA)Qnwn_ocvJ{MO>?N}Oj`0;4x4fT zS>_)QP2eSV!VSJN7U*rpVGJP%O&UT#11{l=u322;QnwgjYg<4NGDD5F;p!>V1#76C zCIakM$1ZN&zD=PzniVHN^ECHr#G7s%)5Syliwo;T3~!yE8sWR`q|Hc&JxNTKt3r|u z2*~>}HDPez%BuE8#jcizf|#wGBeYCpmfl%#fvD=Kt|G28QwM6L;@kH0(MDW>wl@C& zA6{&#wg&|1>QRuRnF&rpwZx1UZjrjEyUP|p?Gy*Hh$G#D@Btm3Cw0lJZrg zq&l>u(2rp_vh6z(>%dP1L+%Dsqa0c}u4npU+K_i%?6d8gtd}8MZf}NW%Wj@x5C_Da zSywD_3xAKa)>P1yTPranWS3PqxciJ{9kXeg zBqk}4%2cM;U0xwx%t3Ap+kRk-QO0AUSQ__vpa5Y_gRgEvF^6nyD;04FlBDm1LqS zF|>BqEemTIG$mo3)x}KeNYPe>^J}>G<=T8lpu0q%f)Ys2yBnlT(yIe|tD>z}NmG7l zNb^*4q6J7Dd$2EY1$aW4o1qT19DK_D@X(}tI^jVzY7&sbTICZ+aBIxrgs&=ffT9j7 zRZmZ53)iYTDy4*^j?>WLxp)b23Qc}OmapwAPQ!*0&(u?xqoN?C1uB)ubu{n7GQYt) ziG=b>4Jou#sORoD8l$4}+*-hGBgCVXKCjnlH%OLY|lD4Gw|htf(;l~Gof4Q{leX*U|@f0l-# zwxg*-F`Yg1!VQ49sTrhfZP!KG@5&F0cy>>zxO+#I?Xj+>n0Dp+XTV(6(ECv&UZMn& zhSmty;#(+6x`Rp)Q-(X0hThk#a@9v+o%2_{4dS^i>bKL@_4K38e2>7Kff?BQlZ)<7 zTbz>WRF9s5Kz0n~$rMr~$5I^BDk?~=LBrci*siVHGYxva)gtI5x0uBs)E}#ofY_Fr z1QA;P^>B-wRP{?-d2(%1T!iV#!|; z?`xig_NV11H6)SWk})Niwhpy(eFyAR%Q^Zk*#7_r54C=XrQvgh3g*hfnYpyohUjWE zywGqTcZ(c(cseB`(!1*#dhJ?$3LfM{rE~UkD_)mgxi*kHz-emf^kQcW%CwaSn39iZ zQQxRZ{{V&X_aI48&?8zWRduIW>I2=0y_nkMTkBMA?8rtIL+D%vYivZmSy+W|C-Cc= zCOm?ZMWweJ(xdMH;1C@ z!nT%2wF)0jGu)F4U8?MTkCJW0lS_C}M9e5TDY(Tt)gYw_&$UDk*NEsDSpZhssY_sS z@C9DaR*Gqz$Hs6Ycv@kLGfk(ui)l3bF>c&?KnI~^oxORI;m`4R>tAK|S!h+7#6rS9 zxx^JFrzq$dwl(R0-4EsmwCv%?+cyM1*>k_!)-Fmx28ON<){*+ zoeiZ+G(843ExWs2dnjczT;aQR99Xyu+-l;vvc1(hJ+L(&?%Z0Fnscvs_hUzrGP8>} z3M5#V*&T75cM{A#z>~>qU8%kDvdw5e z7N4*6FR&;RSrJ-gZVH6>g?;$sLGhK|jyfts>SNL7&*iJn`%<;B_;(}Pz3Ri(PZ-`NZP>fs)4XC@UtD7j z?PFlR*kxKuROB+kfv!~`*BV@SoOR1O$kg*#{M)lJV=tD%2wf`l$G*K~ay+XQ{{Vh$ z9h+#_lai+--ebr*6bTA;v^}&*>y3WqP|CZ&ECSLGHj(|}yY0rFjxH!-+Zox)5fmELFo2+S z0gxaL%u;W+abPrBMlZMYATGM`uL5j6pKP{|cgY;r>8U)U z$ozE1*Gg}OJ1pAwP-U;f6>>vs3P33}PLe<>rf1!aXAgnCY;BRw(P25Q*@%79?b_W_ z{{YjMH0;foE!#_S?ufScE&)n0wW(|}vqcb80RWTGVXorC&o{o%q&9}+wzNJ|-J-oO z@^>4-X%ZEmhYF&Cz4*XF@Cg6NRi%*j^Y-!SJq|K_fcU_2GzDUGwNG1zT z3Kcd<(xcr`o*24sE>4ljO!-1sO+x(b{{S|Sj+@*ZzNPR=Xb%1G6*E}v-3PqNzZvX;ZJErMwLgK)l$jO-RC#q-mm8Y^5!NbEPST zM-(T5+(=3?NvCKSa>vy%b83OVG>Nd$Jp=EfQ7`wDo`(%Dn^2uuzogMKS(2owInx#M z8kbMat$KdhaEm>a>2f-ONGBpYL8oOgD~q~xa*8N??m3qh#5s1O^1Qt{W%UVq<=xY5 zm1fv(ZAWb|uaB_cVfj*Sk>sB#RPltRlTwoPzcMCK~X6)_L%54ZOrZco801sfs7k9O8`t%E;@!i0%F#uguc^vD&FQ7RV0X6AX zN)PD6KkduqEO&M_3OUP$#eAz`lzYNPUtSY93)JLp+;R#2YU1Jw+O(2;3X+rT?XCmx zE52nL`=ZCeBR|W$X7MT(w6vvlsB90kVNr;>@kxJiSsno+Im!HJg_VdbCZEwk!2S_+ z;*$J!%fT6Amw54|6*TKnPFbKOB-7k6@4{}pQKQaT_#otR#~jQ7Ef7xteO79QCuC(&M2>7ORH>+9Q~jN9}gFjwr3j%qa}FtwyCoL5)(!!0lygZXLd0}251$5n9EegSWW_ItlHy%Vs(DJ@*3Q$Vg zDJ3B!=R=krSlit;cA9fc@|U(Qw0mpGe0ch}*^XZJ&h*q(sXutB7bz1!pQPGAu1GBdV$6(A)%t_$p=$v6N)m7iT1WmXa`b& z?V!V}ZAei!4Wmj`-BSZ`8D^lZW~6n~E874>GCO5A$xR$R~}tZMw=HKsuc2Dcy&bX057aeHTlU zJ9j_kOpeb_EfrsF3F(MDL;?lqY^`ckC)<2TaJ@0K%T%y(}=r` zUZ(5?8>)R;TJoUA{ok(yOvn7SC~Z}x zQy{Eo;vb@zvS7tzRV?GZ#K+zi-O7Hb1WiKFEuDz`CBCpRdRj}WZ~zY7UykaQu+!TLwouky#J4XGJynHzg&x-WZ` z(mWnqCjz6kPkWDP03M(mT6UeAt}e%b;;22&%r_f{d$>8nqaAm5QZ( zN535b!EmvEP^zg0`+Kup=bKg4>tu!{JQLkm`EBU|3(~FZq?&RMYP78|%wuK~6Jp6OY6_p$Xj^o21dMKm0CHmk{s!t>2b6Uo`TxWZX9n@_z3#b3{yKz)I%JWU{oD zPxfFCJR_zonY?suvtuGY=sGIa*~mZV-zCF+^T*bkds=*@n#f3qi^+xMHkk}^l~6jD*6$|4-4M9u@o%+E zEzE8s#zfwWK3gWV?97aEW*{)$YvQ{%GuGQ9PBdRIJfXg1?z>gFhME!PCHLCWv}zjM ze?d?L)3T*54519>TKy! zO*#?UF%5HM_+PIzD;4u4Z@a1@wF3K#Lce;x{P3)42R4g)-O$9hx}XzPHl+nuEQN4u zPv!)@>HY~TnmZ==<bqWTs`ZvLxDzLi*iFIZyZ z>BnzKQpA7yPjNP~Rg#q5`cet{#w)=qzNgtM0Fm;n_-|n6t=6-xq~0DjiY^jXE7YdegfCZ=PR4RFiSBv9-}Q*m;ZFG3MLYVMR4d z%Nj~lplRKa!s6mUd7w6MNZQaSV|%k@iGO0}aD?-1G=QHnISKKmy|}qp-AG!fSzL56 z%Tl9e7~4-=-MNXx1Eqd0?uW0t6Mk&nG*y3)w=X1fUVme3Z<>b6Bg;zWB|)t# zP|L1#?yeP&DRL7p?OP{1)}9b}vt#}U;$zC1)9x;hCB-D=r>GscWBtK6eH4p!Rh<-@ zb(O~4f9TKDroe}GnEvYDwwXdaR~z#i)Y}EL=~4v+K{c;#A@R(f<2f7M;uOB=7S>x< zXj#pt>kEV^+N|8+6}{mrY$ei@o|f$LUR;`sxrJh z+MYgEN+ji8m`(B3OF=0KtvSsLg(tP8=u)%ja8MM~kLzN7&i?nepB1XmfVPEe;znYW z!j64Z7-{MuKCJS-X59)vI=sf4DmChKj6Q+%3Z0XVk4kA(Rglc}I5kvo6+$RgNeDh7 zMyw6T!lf(^@dCGhlqTa%gV)y?u9wr#q_c5=ZM=jRD&OqCFyDLT`jsGQYWV)>8VH*>&x z>O#lzyCWBCkZ5HZN_s)E85biC1^mi`6~fr_TpEf|m2n?6{Rt#16}}|B^an(5cJOOk zvLn)@+=?5Hg(@JXX+@k!_Vd-F}HcO_i*j>_J)d-Ped9%;)YSHMFk z3iDQk9{jtpW6fsm=Yyx66*FjUE6YpHw;cgij#l}G;*#3z(-Gx-Dh=xCjMwC~T>$ID z;FFcV10m5K-z=rk;|grk>n8;m?@Dd+{wtfFv38F!^2Y4ib9nm1k!ag2sn(r-T3A`0 zr$T`Mis=bR2j7h|ZQMZ&Bs+@Bw`HXF6N1eGxn7m5vUdI9iF=mh>?^F8j5976eNHr9 zPz7L~hKC?=OWia1j;ltBbsEhbEHraYGDDm5pu+vaP%+FhjKtwkCQoTZ7n`o+dH#!Ta?`?LkjsS zH7Wi^K8fv_{L2{K;?ed6@{f31h_B^ugWdlCh^?cNppnzC4*FtxGgU*Lfl_{Rj=0@b>DWwvIC zzVq5CsMeXB=|PC+zN%lK0aBC~0^wrVnu_(t^m zsMdqtOwTLj+zh%jIE;0$x z&yVI6%QANE;IqWowk@5xqC>e_w_U?^t!=6l=92qlF$x3DmHBH5De3(zQ7${DVF{%P zBhWu;x?A?ABXH0`Xa~|xp24ruzLNg{Dxb>2XXVGSIVkTAMvch4p~}x&mwuOSjOyiBQk-z>eQH~(rD_030~pr&->%BYIz<{5->^G+FzhA9u6GrB`7Uniwc8Af zJFG`}#}bzvcBLptJ)YBw<{k*#?|3U7b<##E3g`a-GVN``_>0VIeRmDf$H2c42qi7O z84B|rYJ)t$#;1t9WS{o!6a{0s>}0GzGsLw%xZ|Dn=Pd2pb?s|z+KE}Y6s&7$X*x=C zBM^DLwJofgY6`RMjnfV6Ae{=HVJ5(aQn4`$O1wy3uxs^Vwr$_wt<0J+%_y71H;=i z^%Ua4@wP(2%|;y6Y6@H(%BNOjS?+OO1E5Q5bT&uSaQhnSHl8XsF>D&}?*)Ih8=_T` zD{5Y(I?_h2wF*EOhc7I-6y#LItPiwsTSLFD&+x_wEfcz65Dlr*A_UdrNiQ0{hU2y%b zs-vcb?IB?b_EQDARxC;P6N=3qRI6KFN=uC-D^cr1T4T|CDjM`3G^S4CX(87dC&u1K zKI}(4YNv6BnvA4*jl1Qzl{Zes0+UYq5nMHuSz`CXni8;h<>eFAf(Ov7S!O@>t2u%& z+__8Mlc$)RnQd;g`NX)up{K@@57*so1O)d;wp_B?0g-r*#=Bs(a z`>SNxwkG49yO6_Ctyf2arnuO2p$?%12^AGJ?!vcEk;mOCzh!97qS7@>ulj-DHrJ=l zZk^^0KS0ACsk~J9z*m4-M>=|cl}&SErk@U|r8N66qh^zlNzJv%{nV=;kvIj>z3P z(2!KN3l-&EFoDb^A4(i;$dEUe3UYn-W=U^p-L6*I3A;F^gGIMiN)$j=p;ad>L-k{q zGgon!y^T7rp_^|XBVuFRG@|*p;wiHeu&oATi+TBO%6U>2;+C?7J46o=CV&jFJ-4Ql z$*Cn(H$9XF=!Sp*QXGkBX%$kyAG^?Ea%=G$YT}};ab?HdY5{~@<|rZsnq|O|REY6p zNU#1S3dBsimeHeE4{EVn?R%FE7=4Ms4`cFe$bk!tw=AHqgtxcv3J2*CsfKUe_6-y= zH`)`3wtef4k-Pqr`x5gUN6iH4Yl!zjr&IjDb`|>A!Uuh0f2!YVTx@)a{{T$;5nGnb z-urd1{^{7ay}KDkiI;G++#$15(0P(sLTY=fhnAb7%HiEHyc6q9?zYwCvwEJw2dO3? zC8o+zMGmHv#G9W)#jT=DoEuGC3CxW0?!gR*#3=7^z8Ph-fs@MBKqx2%6Db(QN??bo zn$nd$a8U6?@v-WJ^K^it^xzYQ#%oo0cqTh`9y(%Dn+dpYneRz>oRnePQX;fF>8(p6 z+l>=vKp4XEuzuv-`PU*g@CsXgcuq+at}Z432|)J0^aY%^5M99+Hzlhe3Hq=PzhFv! z0Q19CLH*3kL@6%4V8giGxKskJ&QFb+O0{QxwdV$UsW8d{{R@;rAXv! zY)K6iI*CoT>k9aS()NN6xZ&pNye84d(39B~Jvjz~zU%l#A}qXFwW2K^QlY_*s-wAt zoN?{`v;8Z&ajXo0dX*35ZbP!(IRf_EZwFmr-z4|8fm4>a1w@;1l{=w{#FHXF1gKN{rHo5(1Yft@|Lrq60a zmXRIfOMPj=?D%g08P7zF5bjBhGT{hPJk^xxuT0Jm z$lFF3Xb~~jGj-}$K^4cgZOaBLw{qmUj3SNBQ;OGGb?b^lF5KBDI4h~OY>cT~ul3(G zJ7;0sw$o;%$qWYE1o)4dJN<(jHp6e2>Z}90ZRv`NwR%NKe|W(F(M4%dJv5RjT=8$+ zT3Z#HY(f73dIIisnp07&=+!j~1FCh3f(CuKz-g-L_+HT%)gEthLkiR9AS87qL-ydN z)yYwI)eiI0ZTq`ow`FPczCL;z(qg3emWG}RBP#Z0aW%KYp?PuQt2?SF?qUr*0Zj+F z8){piM~CwPlEY~UZ75fVNf$7 zq>2+Q9fuAgwk?d+M9p~FWfbvUntA5J5H;o!pBH@q7|&$*%GF_T5sLUCf=el&O2Ucj zfTHur@j~aGPSaC} zQ6#O{C#!3rE&M7vXh%kDz5dyw5z2k_&YRn}Pq&oI5cfXM)9Uqb;dT1YYqLm@m&ks5 z9u&4Hpq2C|F6?NpxDPc8G^a_SRr7wSMQB+kP!(6wFxb^TwWTVd=M`57S5s0j)Jdw7 zF(NV@&^`UQERq0;3^hq^Ji3rGJ=kUEQ-YFYXn{T=O$Bg?DC}XX@>GIptFJ?Vs$a`n zm69p-oESK-c|e3_6vkn_=dB^tqu@#J!mz%Esh!2Sds5q%^h-~PN(A&7QopATeB{&c z;Evm^vM#Rza%DdnyT{Xqkm{lG6Bvey(eb9r^VoJoR)dLUIV4kAJ^A9nZvxJ8nP)lF zUGw~ry(zDneOj~0m&6vfZ>H9xUuHC>>L*^NhvC~;uRt3parJvpp7V^)yBYBXsDyEH z*Lyp*9d&)@yWwWOvUL4DVNYN7p}iEF{>0@Q_<8t{?H+xd`?tu?`^6KtP3ZFkFhTxSx);L?gM zk<@n+jRCClVWPzD>dD9}?GuQ9G+l$ZI8%tOdw-2@d#%)M+e};h)Gh>8+qpuB+*wVe zzUeB`>J%$Mj4t!F(@QaRbX)DG<^tN*5gB15AuoQtcEUievEOxF-v0>`L7R(xr^IERvU)vDb+tBvyB z`!>?^XwtQ~oitHOj*u&dn>S?K!+h5}fGV}!?(Nu5gq4iqT4=c4bmcW|Ya-rr%ZCC3 zMaQJMQW`@^)JdVG1u@lAVw)Q}ius+6&)nS1A&7A+fPCY;B66=NZw=(r=($9{OH+|t zNYD#NLR)XbbNEw?&n0lj$Y{HRXk#X`vtvl1=AzBDVQyCV+Lq?@s7$y+Y%)ARgCRA~ z-H6LS=K!slo2N3x_3$dh`kB1>+?lt0=c?VleX%J}p#dn8&=ka$?7SU)K7~^4c0J!N zWc07AB>I8d)(b7EyQAR}!!gpF)JY`?NT{w@#_w_cP9<~(H!MQ~O1HZSyQqTQ;GjI4 zgD7>92qVjNI)y9jIHldRbyLA;n6af-2fsJ|W1G2odyP0gJdz^u#l6es<&EJ8H#^8I z#^_FM#%Wa){qc>Xww4WKW<$kuKYH^9NyHLc~9p}oiIg4z=E`#s}ybT*>Tm{i&juV^DYb9KJo zd48is*0~MR)-&AHs7k^4lWu*XxMA6O7i+}1Sytdn*BD{Oq#?BuHEC9)s-4}qlb>g^ z;%^lTXWTgp!zC33MjosgMABJ#n`sXn7#%Pzq8~dY+&R@tkgm%Wg|n z!Lu$J`v$tamkIv>J&o-u2JGNnmf}>|kqYs1Z!x7nI?OuLu0dpDVC@WFz;XRQO&30X zVJ|=KBWJApKjOK4!ghx)yy5AJ%EH2)mvepPtkeXQ+n;o+AbUnMY>_@wy-OzBj{g9< zkJtK&`i0{i$v)Va_PdhXax@f=CQ(?RM@kZVyD^GiTNh{-O4!~uJ!-5(UR~SP>M{76 zavVv?v@9r!c47)lMAfYomK@|H(xFxFSRS)wY>qv-ST+;si@NtiN{tqJR)sDTOB!8w zNv#$JN%orJ-?BDarIbEFpjpRtJCNHuUoJO$&Rk;S3s&po-Q~9SZq$P9_jm3oEyuOQ zPNy7w_f!E|)tLYasY*EkQgO3x8|GQs*N|M-vaxgJFM~m;JXcnEchG+>@V?rEyKQf` z$dQ{%U(7Hq?Jwsbz2((vOJu9fXb?uE<~q|EJ|}!fES(M-70?;((N7FMV;xcx-_Y*y zv+hmtxOT?zv^OGUv5z)8(omo7r3XPt3!n$3E1nhJR?c(*(T+X|&uuQlZcKBY9xJQ2 zPC(mMNa8d*gEKx=l`U}^b-%l9w3TLSL6F8pYl^s%cGPoQz2063<1{0J_)h%Ztn>LE zX4Ulp8ZuLCcK5oV&T%QtEKRzl=&LH}KwBvpoX$E!Z`%1W^q&RHd3)y1IU|`|NQ)p+ zfMziC$byMtJEW2mkT`P5>YGa%Qnjobfhx=;2L|&}UQ!NA)mU1MHJpcb6YC+#&p;eg z);7>n_Eg*r#0#72Nn+L;Q653asY~o5yAv?klFF)^^7ObYricXDepzHbqo{ULp3c%S z;|Nl5+;wt86KjqLmmwY=qh6vrz8ommMiW;yVt68MUH0^P(bBSigdf+0{$@5M8E+}# zv{vM>DVL)N?v%I+R;mgb_m5^0eC>TgGhuXo@hOicBhcR{bqZxa6KPPTP;&p0+c<~>54t$VhJw-GYJ4>fFp>C~wOEw}2cgWLm8A=^cN^_J`Q9567cSh582DNV0a zd@$-wITqRnXZNIc`|%$os+5Z$N>Xdo4-m?sW05Vc0r3x;Y<`o5pDa%WA@j&@_RGbR z1qUEo5>8^X8qfVKdC|CXOSX3NUjG1TTLJS{H>JILx)jW4<^FaGSmfYbHE6xdw5E7k z-7uZ4PEy)G@UWDPD9(|!;srtJxQD@2qy7tjTv zRr&>Rpp(#?MH^mpD3pz~@Tk$K4(xh6hJ!+!8wYYFBEM)zssm=IIh;N7nwiUH;ES%* z$)F&ljCjlvUo7twS$2d}RRh^jLBe8Ci1-|nFP6x#Y*%Z9q=d$a9p@e=ph^%)Iaj4H z{>Tq7C|sM|$VsZ?*Dh`gOz04!nx0+tpNO_dC z2>EfD0BO=cP8?YL3ARd4eMdLx9%bTHMy(|v8uSAdteeB6^($^NIz*qAbS1b9D4Lau zj9BrX%T;WU_t8Da{{YVSM}AmN_~}IJ9HTSwBj1Q{RO8dG$}>{CK+EdGi+9m5x@|&O zl&wf7xQscJrVtHMJ`zhWAwsq5Ob9E&iCGHM7}u#e52Fv_KI&m7#F}>kRV0(~5S+0e zaDDYRY(2#z+sQ;oz>kF@p3re7-{q()ujVEuAL#BENz`h#sWl$9xNEP#}xQ!-9jVmGT^JuJ}?>;u=Gs*%15jpN>8B*OYGmC5462Wq6L=Wt0fO;#%$(d*fpv=qLWf$-FOVl+yRGgNPbRTqZ zUn(3n^Zd!{uJIFbfgo=^_qqe@Eu=WwQrokcMOzd}N>b4JtB#J(O!zQta5{#A?Octu zti{gacyQ3Y!`Y|mSN7fcTV8no0GT+)k+}Pko0S7+?u%vW_iMXGS{_^|a?>eo#&#!! znhhxn9Xgy+?t6}BurND~7Nrkr*>8~-5$Ua0X}5jG1esw}Xbn9JRz$m{XIb)?E2yCB!(F0HH|) z8sjmzw3b`iMX0LWp~yuYkaU_FE?QW9VeQSC$%l{jsvB#MBeCNoUeIhH49w4%Ec66w-v=fxwqN(=`zp-5|y~>m84d^I$`EoAl#E0 zIU?=3TayRZ4f)S~te{+Zsh_N%_^$&uM3 z%J8_6RTK4Mw-UQeRjgNDbhxvvVU+ljsYE1UVh1GNHy&j(@0-_cTOzD2NzFo~; z{CeKKkzuymc_L;>}MA&qDzi63ITP( zst;vwLy#we7_lq{sO*DkY-o;^wv^VSn%4+NAtgHv04Oh&Z)_`%x9nuQrN*tY!$YA_ z^3QH8+kPYS>?(uYDFDMu>RivYl_Co%{nJ2C(T=mWfAE6&X_5YU^{Zv*m(ynqdZgs; zPvO2r-!E~GDBtYxE?fh-SbZU>)1bK}yNpMozeeQgLYQ;Q3QL6=iU`t3#rvPRvKV)= zXxr5pI{u%h1=O9F+L-;rZun()Os&kFBl0 zzhVVQu-#K%0-2Hr-K{{1vhYg5r~yO7Y}-$M*=3>yjsg7Ic>bTJinqMB215xqOw9K= zIB@iOiSRu11o2fO>iKI>QJ?@(UrlL?i`zv}j8Ley;UM|$r6#1Giw(nQrXxZ;(q!%V zuBBCeDw3@|wCRN9XbI(o&r_&Vk8R5nZ7c=&jS5sV?x*U(0#4x4MD5xRs9F-j#Z?W* z89n|LR9EOYk&95NxrY1c;FQ%v3i)ab6e5;!hDa~~N5lljPO3>NC;+d&UY<2R2t<>EiH>)laBUC-B6|2UZ{6`DCmx>WM?!0 z0U=e(+;J3ekylkH-N!HsH(yiRitlQfXqRS+Se%mEj(F;jp-O??4i$?B5H$#r?g?Bt zE3@ezbPLwhotOoul9w3IA`%tX+lq!}FD~F8O1fdD^KJcMd{g?$+D>dgTt2Thi-_|6 zM}6InL&qRj$KCyymf)i{Ad zYEQ%tSfdG+=wS#4X1YO24MFz)+!xK#sb7}}nw2Dx)z0LBT76h2nuY8ED_A9IyE3ID zg(V6!HS8Yj5u8$Y#;jPGJ;fwLtKs(x<3aB)4WX!tqWD3${CmUBd4YR1F#8RM{qg zkJ2$$WVKpM(NLuZmZXI?Pqh6f4%qr8Zj6+1Z;+J~<)l=odo#gqPr8iAD*Y6EiHemi z=Uw=eqAT>%70EcOJVT{{I2bffUv?#;lSLCVYKK_V+QG~ogYCnofKw*0f{)o9@|+3n zsVU3o!OQOOI{x!N@7}(=svu=Zh}i zcrPlUx`Fc5J>1f7Q+9>gz{`FgDfY?jRRgb2ia|*TjX0;db@oi7co9tD$XUe893nwz8QL zrABW;TvEzwT&in|9mKZobwbo&wYqBnJd}lPpSvx#cveXd6w6vhxgm8>aaoaEM?J7R zS}8U^Z!N0ho}Yvb8+;LQv|6B}G)Ki8zP4b2Xq=TR7wbiC5G!m#v4yx1HT4 zdK+@c%TCLfx_!_K0Y|s^jupcJBf3&q+Qk=AgE)h6qV2t+URvD^ZwRw($PpwpL*%$c zN(P^P9OrFAV$($pw=YZQsnt(A?$dR?Shq|y5FRnoewetS}ypZ{M*_+@;*cE=bxK#6*S2I)e<3?FS2`RSm zCm=d?KW-V6_6{iqV#zrkwWqTPN~fSYChVS|{U2r%92A&E1K}MFE7zs~mEsVSl9^UJ z#B)+#i1J6X?#RPi(O8OK0Vhq{RTbE>41LhkG^G?!e$k2?IykP1%CoB6QBU)oAk>{Co(#9#C=Mg5H-mzO~pme zF~yQWsZr}*xJ%7L@}Usl6R$Ha@a5hmeCw~pV6i7kMy5)9Bfo^^+?9(gr~c3^{?LE!#TxX=>2H>{pc#`T8b{D@U9bHA0HCCM%<$Y<8%LtaqfcPifC1h;;RC=BG0?Fh?nxJn<^xQp0uh>a_#tgaXA~hv{{y3 z{nm1a0aWVT-i+is84g(a2{C4*P{N%FUP+;+_OVoS<=&HoUQeTy4=svm)!IkohqvgzRfwvTF1-4nvDcGEfwSg0PTMDl(~|#9rgW8@RedsHZme zUA8h*j-`35pP8=0wU(Nl!uJRW9BJS&1{-4?VRF7)> z&pjVaLqg+wt z3$z}Q`%*iTxb}DUPKdM$k4;w*ZPDbl38N`heQ8gcf2B90g+y{K+o;KUg+ut9g25b zdX#moRU=uZ5hYE=he&=)h>ICI-;kuY89nYSgrt2W-~+1Q60=Anh`9JGceuCIO`WR-;^8-<|nbsMVI^Hj{D^d5`8wxVO!XvSRS7e7R)g z<}PxZR0K}iOJmBA6**cc+O_B9pN^xTsKsTQ?sq?=iYvUmtAUK_X;n_?y+H*mK2XpZ z6y#N(P(^U1*5r}tm5$Md7gU78pSakN=ty;MNOXXP%~YbIXaF6xJ=lsjjyy`SoM4P- z6sfW99HX)>R*R|b!4KnWEEViQHPe2hZ<2qURx{k>6#PUgP696 zlKE4!t_&FqQ1LHY=_LI9|ewI zJQqh*R25Y#+lDzlu8kCi3GFfptJQGW_Ol8^TOvaUZ~~1! z5mD2oD&15%tr|$hJ=G4~6?xkZ7}73JMnhy2EV!Zuc$1B>xK@e{@;#2BB@~^!_X{kg zc3r~d!aS6a@HJI3p~0eswcwMI>EnS=)3%Gt_ZQ|qq`J9lD^k=+QBse2#GFzFsQ!mI z)kzy4Zd@L{#7~fx!Vaal2S5%R-rZHSQZ_dJ^1l|BTZ?Vb?3yVl0M?zuw-mQD_0%hz zQUbnfGn^o~iN=^=|a zy6Cyw+qESP{{YThT3Z1@Ju0kE?x%G{K&g8I77LoV(R40DmIgvm4|%fU*H66pqyhFT zJZ8R090J?jJT=kFR2x`8^S9hokKQHVP6MI;0Cg`Pm-mr`ySLkIMDHlCtb?U~VsiTM zA+PX5CzZ|0Ji5i(?-i}B%MsU7NlI#itJ#(u$2lBAjh5ZKjZ{r_TM=${X)L^%3vOg` zgrcW3Je2}69oTr{($W(aLPLcW%U?Eb#b^s#ttB9NY9Qt**_I)jjDgyatd7cDjVq!> zk?I2c!sv+)cyc4kDyjN9Vcd9YYACsLS3u~5gU?)rB3munEOReKYESo8<0zE_{Uea~ zlZ9V~vjb9wEbZ|+1$j2D;!5ohE;pFUv`=xgu0^%QB(0?pnpLEpn9nX_240qG7KLXs zysoJzb09(5Pa~X?*3L=I{|Zq;X$m zACb>DaMu^{7c^YAzV(N;Z#;p2w#v0M_Of5M!nGxosVYJgC>&byC-mbL?nVOk_UJk> zR)Dq+((gQOKGD%k;Mdpn1=ep-JiqAU)XxKQ{{W|+LFCv{wwEyN>#pCuGN$DuhORLe zAIw67LS^01_xj;o<1|7Bdvk%;M;cSzSF` zxewEVOpP#r6qVEiQB&Q9B5i7wd=v`ZaX#Qh(#++$LW7Enn`u!W3CQ)vmA7vL*+q)) z8$T+3u1ap&9;&%(gSPaoiL=;=a@*o#(%3`oI^)rdpAwRih0{uvpH8*Jk)6TZ6;b%> zc@I$_6zF5u?=z_?Q}$RayHAA1eM9TjgL$O!3OwPh{`f^K-nx0eeQ&*~vYBEWC^xCG z*t)ow$}S~>;PWV4RTJT^bi&y8xLh;=%{JXzcGP0zkmPl+puHW?Dmz~5g92% zPA*zjkdOsFG1-Q0#l&4#B^#f*hZ-aDNHNR3J8}k9*mY+Em?X4w{>G%Knr=%oVfyl`3b*?AWRq%`HyYtN4 z`$g@l$EB`AvFtnA2h8o4r)>D~-$g>wR>Rk=NTH#kfIZ_8@$T$Eb!sd0{>1+PC9w0Z zXGtZ^@E_wvE9`Gh-=C(+v)XL?Uy3)?i*MPP5$#)eUN%5tGf6ellsj`@hPWj8%nZh_ z=_lzUOW9GwFqZes zDKvp{0F^9MsEV9F$8M}WqezLuhr++HMQ$CDpUKJ9D;-hdTl$=vR?o`^^IF=wvu1AX zsp+Qi0{61XRkmX8n{fypV+Dr>R}y?lQ5E`d`TP4C=FquC+V-HkQ*vU^9^IjP_1i93{~!TmNq?F0?=&xd6$%X=TBO3@9)d| z)m)@UXN|V}doHRfbcuK5w&tVQ3JUOqJ;JcApd~JQVUR7O2?y4o>+j`}>&8QEE?u*0 zT%+py$DG^_DnSH-Nh)B}{W!4O_fEl@0Y#hUdk>emlc>JFx~1+m^ga25aDMQOzqo!j z)wXXNdrmQKmi*+uM88D109#}VfuBsUo?EzhyV!#ytrOk1*{t>|Z!#AE@1;*$veo(A z$FoYfUw2N?i503T@06JhPakp>91ph;_P5v)KmeBirfD)$xE z;TG+-(Ym(+%a?UWzrt=zr(89wfrNF-rZi}B_|q61LdoRYdxS_`QD0y9BT#r>^Hp2E zL>;|jQ*TucNLJ@zl&LEeI>|xGop4{B#a5xeXYF1XeaJ-%wDq6ZxijmZ)%P}9XK!yw z+{tF!mq}X~w`|>(C;_>VnU-U>B??5=QrecZA;g-b3LHO@Yf~RoV~Px3D*dy66!2Vm z)`CcB1+aPpQ%|oEPLjM8`r54;sWWG7sMo75=APuWH!{&1&xII#E@?g>PrDOVFoO{# zRdRR5w@qgvn-S*xx}8w?mYP$i(}s?9N}Wsle{H*caoN+QBGqJt;+t}K>1+QL`ElQKsT zpyI2}WV61!8krXx#LJ|oX51Ywy!j-o(=TAALlAP$*6me_$^gKvho#E}-*+c@+HDti zts}~c?6tD8-n54lgzBLNf~J_b?F)B2mV-r$_nz;`41Y&jkhh-!?@u~XtkHj&oM8pW zR@{cv;0dWwqpJtArUP(o+4_kNs%^2iovWe@b2?9{R?C&P@ojlgZm?X2P-v7ml9OKS z@kMovtvaoOJF9n!iX!Z`y(&(u$L?fK4lShdO{TbkynL^kGpGlXl!e zT_~Ow+Xc!ym`HY1&c_Sc#W5)*4Ys+ie%f z-m0e(cng)IAg#49(nSL4ECN5x!9N{mFphSFqnb3=3v#}IhObCukKOsj3TNy%55=Q* zBvZ8|ASOghAYrIgVQq@eXzw-Q#-Ix|qAZ}Kt9!rF0E z9{tCAQ-9fa9gA*Urr2ftv$8DfnA&`j;|VUI3L{X=idML=O?vkYRZkya$e-@25kG1t z)CUtxw!+Z%?Awg0R7m-Gjmc#+TSQW{T?oW|<&U2UrxkX@WeuIk@GG4*B|B@nNslG6 z%j^dmd6j|`g&@$Rj>BHK&7fgygdR&|nZfag4h14ypE4vm={_Zso*7-&t3>Y50MS(R z;|m;{p6G`1*-(oV7oT*b=m8**I%0cmV_)Y7IA|0O>B}*e<)G?P<;Gljn}$GsKw+01 zR;L1WQi>o@3@4Mg^KMOiQSh;JS^Wvnf|i>gdO zvUPnqVxKSb30I3z{3B3nDR3&kB zE=&aEYnkn##1%raJ(?$Fkkt!5%s+)j+;B=0RuiQsD)hstYNO*iq>@SMa73cr3MA)` zFC}82n8LXM+xB7D;F&w(O-W}|*1$g>ZX=}l)Z8D-Qj}ptFrMTm{q84ijdV~KNBD__ zyHZmnqD@UY)41XW_v2HxToV4!evif@#FVW+MNTBJ?xcXIeDq(#Df`XV_58lW+z6+V zqaqXQ>qUSbNIyk!IlLjKLzEuFyYmE*o+=rcatL5B-B$CoLnu#)N?MHo09L2CVx3nw zbdDuoeqF3RPUNlEr%nizjm@_A7Vo!w*6h8=VU3ZrRtk#_zI2IFQ%sbB;!1yXRYyLW zHr3l+SrSXfkXZL{ces2mhTQ4Q{-Nky*>cw^$7r_gyA;+Vv1w%uNuJ|tTAp|h8h`|K zHO9jpL^YsMa&68W&mnOsnB%iET4c~NJ6ZTp3s0W_8GkI~n_l@`87jEgBAAjxH(qvfo6|gpC$5Dv5 z>QWNw97(ENQb;K|6N+}~Y;J?4+O(}NwZc|imoU-8nr+AY#XWn%Z3$O5-|o(2RQ!uv z2V5qZnx-Op_TwlUdmmjI0_nD8av*BrQ_k7>gL>?~=(Nn_SyIz+LXx{{_L$7MA+ywf zWP(On(+huY*$qHVrQBHx&=q*L_vVr7*2QXvZk4ty?}&*?8g$v53n3r@{hQS6#02|h z#z!g3P3e=3y);fJKbse>gxsU4&8utj$q7>`wnzvy%cq!dh}rn@#HT;sc};!Osz2yc zm^Qx$dWN6NT)(z2S8v8+UKbbSU8&PEPAct4ebQnsGVGm z>Vu3e`*|0JnY!FWRW{%(!EgY8pcF|~LY3)U3_EICrDxpG)7?rI&sN+$ezrz+cNtLB z>PD5whU#+mliua67Va3Iyq4eOQ^KQ4ifXBd<$u@ljZmAat4o2t8W8WyC5dQ$kI*x=; z&^&uaG^w}qx;jf%H5Y71lc+w5WaQsV{-rqrz{cF4&lY>Fx+rP3LgGnjAT#iqA;L+- zMW*F}kLnE7OXT)l^z7&|>H2zAXLs_==9!PB`Fy=$ln_Dk)Wp>E=mD-L2LAwz3Un&p z`$Id8OqKmTYU**n9M9pt6&!tgy+*z6TYt~0WY}q1W4L8FrxE~jKL{Wm{8Y|E<}wX^ z7J%F%tnp5cwb4VLmov!RuOQJVs*2dyvMr9B zO1&p*&zD54>LEV}0Dyh<#b5C@wOvjj&q6BADgIx}<7RTW*w8rAj^3#GSt54LlSy+d0w>Dm=J)aJk(<`Mdg;<(f8xiX;!FxS2c6 zt>6*|U^)Cser?_;yKRUOEjHOnR9K+Z_XSBL*13Ce8tuy)wIfulTRriL0-fpE*JOm3 zWi*mHW^B&XS=3e`nd%SD>gZd{2QxhuK6qw@B)*jk7cCpFfXl(x>b)C##i z+;nABep(U+%-zhPr7O*eAg)@8_*9&L9Wj2|_u@;&g_3tm9{BL`dX*1?REv8}gW&_G zw;gL~{FeHc%ABD7IrXT&lp44^jk7k>Of`HUnVMvJ%}j6XOnySsMNKAt}@XfQ9EcIv_)L+1Ih z8Ghe61wKe?amFYpZA4T1<6t11)|Tm0%zL-Bw>B^Nn-WDepyEAWr|T=WHm7!M@^SpO zKn+C!JpFzjVf1iLpIE<>r{dQ4xkJ&7$&2lViYPOsWOU)rlX*Rd|A%;ShGKfC)pJzO{e(ihI=@!30@WNvG& z(B3u)yH|8s9aA@byK#=>Nzq&fqS|x=L(~yaMqrVR=F0jTn8cAdIj6yM&BPMg$YPbH zt{ji)^^FfC3>$NcYC}Z@C~+kxu9IGst`?Lc+Rg-kq}`&oVyfY9bP%>&I!|U@*g>jB z{DVRijkZ$bGVc-3M@k<-?kXBoKFmr(!BjHO;8VpY%ORE(A)!{wf-url4Jp?!FoEEo zM*2B3xcc&T->40#cnc`}I`a+1D?Epon)JmpaX{HEWN)E#Cdj|?=7qzN3+WwPlWlEC zbjfO~NK#ai4M81xlJCsKPK7vH->@A4N|~Q$i}oBgeT#=AxXCk-Muz%; zu}0)oUcz(2V#v3ML->n)QY78usdWp;cGAD3;H?zxS{vpEBJL`5pMCx zd*&hWMC8330ENm!eP~L@?ZeRs@Z6X`sZ@*pq=X~4_#(_6?KWLL~ zb)p1!nrhD;EkPBks?`oUYqvA9*33eAE+Fp~E^C~V?ir{w`jZMRv5zbkXh8t>kaF+F zs|^mTn(w4@O$sTxc6=@GwC`J5Y4VYDj^k{-Oxk!2O}H!EsvJk=G2(a#3GUR~hRXS} za;oEn<}BMS&TX3N%A?$^k2u8ER0>FOr4*dIyGAhkmKfezyT=zhE}?7Nz=WE)8an0N zD}+44p7|9kRNj>pZMegQDnYF&)}GupGZDc%`7n5;3vS?T>#AqUcvV3`)n)tH9Du^n zUJB6#&OVGoN{FV^;YsrvQK}VOh&hb#`NOCt5qrEsZB9ahO+oJ%hnI~M%u3{XHQTq@ z^Ebqp4=NmIScu=uQ&JpLZz+CXPM{j%_p{nTs>Hj$aU0s!4N7=V$AQ6g9L=dpLBmoy(+UX+X!d|s zlb4%{(dn%9H)?1L0O%gi&IC~!yoTJ@Rt+_x#)^Zu)XFiG=NY_U{DW6sI{U-Y6 z;yv9W>|tzvK$ynNxk`*+8I=k%Wt5;rClgo=H7ZQmT9L zKj)Ub6;^9K(h9EoBE)uESC(Wv+rNoTl_@NEf7d!!U#Q|j{^-s{Q(R@UZR<{H#VSpz zT3ibyqO(v_mVUx8)vR)NBkB@F$P6V7xYVVUG(wwFj-^z4aOOErO(>f_8oH@b-OWAL z!;;EW5ZctGAv(K!MCXS2CtORR)h&yH(gRRc zg1hc5i*YW`7v|F~=F4OUC=?1v%pU6EG7CAH{X(C*x`oZ4DogbDA(U;mpeAZ13w!P* z6i_EkYJp1hp~Eb^Z)>VT_9vu@0=`s#G3#iw{$k#sTUvat#KnqwTc^e6S{i7p>GG+L z%IwwORxl3*^P}?%W@693P9Pt&ex*w;BnMs3t$$86b%Zh0P1z3kXbyNPc@l5TR<7Do zxMy&st+*!Sh18OJJ8)GY5+q4EoOvviBd9pj%%Am%Qhv0NgywBqM6HX%CkN8G?-}!J z-fmUh31(Ws?D$+Dj8IrVh(hT*w{=yntUyd5hgJQ~T69T0)yNzvin%em zhk|vwaqXHupfw7@@z&Gf;#0~U9r~z$3=kdTzDNO zL7*yo+1{%6?%fE7xNXq)oJ5}*-bsll4xidI3Qy9UD(Q%I6kNMX-ceSoA-8=|aMs|^ zF73|QR|RCHzSo~0SFzaIhy&V@hb(xitzGR2nX44Z7Ui<{+#?#>x~%)k6qJ;z3f+$` zsXaoQdZ+Bci(2Hxg8I-i-815GQ)(U1J!rJLnm9~{oiliK{R%9Db`2NJ9 zw0JZl?ER^sc=SE%8*jjR<$mNLueX`kIFjfpdC#xxVvwU+xrq` z$={(LwN~S5Qb|J6QnZp5k`ACoS%c8vuADs)&;XpaEw4SvF(x?)#^JW&R9;XZQ91VE zRywG%4HT~_6ZbvxT2x4pDdi-Z)Q>RJ?829Nx~b+aKA1vmtwW_V>^Nwp zV^S{?L27pQg*H&FziYDn)_F83DTa+`2DxUkoO>SH$><6na$K10%SRU`UEgpyb-B;F zNl`CPdDomZtxJn5Kc^8{E5hjrR=gHF$+mBe6oI#Q^T%pjc16Y2J!_pNJh<;`P;xtr zS0v|6E3b9^9J-6aTaS;q)=Vvw^v5l`o_VFd)SWJ>LV?tvisne`PPlgr z^c7IoSo$uMaZjSr$9KC<_qbeKb<1MX`w`V0O{oh(Diu*z>cTk-<1~E~(FKK%ktbbs z3Ej^AvhLSco!HW^hBdm{zbtWZmDJWNzf|G7oF`4 zEU6cLL$qqI>o}SD0;}gJRDodiOJTH0Vp~^LH1kHfv&x?QFuTJE8$y?dH_+NQAl^!qFj|IA<2v=moWe3Cx4te#aBpbJ`LW(=K+$D%!v$!>G zAq&0NrAk;@tL~u3pSLa&$9UTXGFW|C@X?L)5U-9erbvwFHTLC*`L@*Q)O+gCb4vgx zK-j;RA6EMX=#DoIbb|AVAu3a6PJa9+9@m^2kel4^n_|)dT*=;>YinOOMehFo_>(8` znGQ%(9ztl|wG{-6^QJXCXfQ?7O;#zCb0(C#s-US?jVAE!+lu5%!;ajaeErmtlAQyz z9?xxX%OGW!f_00Q-rW1#RKmvk;RGZ#8B_`QLe`)&HR>@8A)nB#LosNMo=M`kf~=|w zl^F%PRDRmv&$XO#Ms8lzq}Mr3IoobYtGPWy?QOkknRQDr z*349uElF;PH0Uc%r1xM(B3EisGVVyjsKBYE!`FT3`@=Hf?MQ+gM?;ZNw5+PBITK&6 z27Vc|;-p~ANueQIO_S@E<$ht5ziGF+>%etFyg78Ga>PW~+Hfkv9$?o{r;8sx-Y+g7 zn`9=!j!H{4A8sPuYDYBp@i2IWBi=no+PKqe&E4DMc*5Cxa@yHk%bV+JGC)YJ6R3ho z>;MvQFjzWw(^L~?85pes;oqwt&kuOtqg}QarPgFyT9g#b&R)4Q{otadDmM6w_(>I` zuME0`99uB^J&ZkXRQ%5Io>9`nLHd55wQz>@ecaog>2ur{`>d13RJAhIf zlQ{AQ-+m%y{L}7*YgkDtQmK)rW)}|})Njg49a<`%xf@Jrt8tQihsLH!?!BD1jRI_3|S|UoG6qN*~svYO16!QT~i)tF8 zi&~x3T_t0&J=j2$EP(*2X>0%vuVw%r4u8wM>&bC-xrMb6Q&HD1)r`?}fsIL9BYL^FE-|DP>SLnY z>ETyj+llNpN6C_^r@AnIF%Fzn^x*zhv)S&K$>QAA$rolCN1C)HN^MQ3nt-rq2v#+W z9#Sa{Xt|4gVJC^bAdswVo_@7NW#?66inO+pqO!w43J-QIPQ8Q<0cRGNd$Q_OsU(m8h@7TMGWwFOcrCvq zPzHgZSsW9*V>l*mISj#TvNHv8U8x-okP;8laSt{|K1WqP+)RMbg=;uN)vpTrciXo8 zrMSHn?5~dotX^bODCmaHmgb07V#sIC;MLRza48aEv7ezc;%JZTWD0 zB<(0^NT#1XU=Gg6z~Da%dw9H~w=Eez07_Qh&7p}UGGcmP+R;{yjy9!~{j66E<*<)} zVW}lRWQ$HhsE^i=nPUWNYvX}@YnUWdIU-`W{!P`+BIN3fK2!M_DQE8mYp z;FmxD0Hw`}@F`EFt-y*?UaQ@vv_Imb0x+&T*S{Ty!6-HV0BHM--cl03P1}KmFQso5 zqC4sE;ZMO2iYeT2cqIov?GNz@2~Ed>Z|&;O%1zGRr6hd-3;@DBqC0TL5Ci_u=Lk0M zhtvK3M0F3la9l0rRp>zlBMX0TJvaX5jSfHU2XNfvC?nIB;dvD_0_AE+`)Pq2ZdK`b zOmyIyw=d7%iEZpVA`hl}=zP?Yv1j5%)9l=DqLydoTwHFW;~v`4kyOYBMX zr8?skRUTWLnkrJHoU_o)aQT}?YVk~NceFh`6={|E&hcDb*4F<3+rEo!5VuQ7csA^v z-8>+9$k3%|W-^hggwTZLiTU_$ z_e~1RFlh%W_u>2-T-Lp3^!if=?<$IUW83}<=Nr_YBunO=cjbO(e`-hBY2^FNi?e%1 zB$uW}wx#r;0y}afB@Q|g5%H-Ylh`_9-(wMEVYRIlKH~H~Lh4eg7h>IUmgOTwQu68t zgnP-$595rK4!dXxIoD0Bn3cbqbSG#ZrVzsjpgMwPu6e85|O+fKb>r63o>ZDrAA(G)aD?amuSoAbQp~9j7G(S~vVEUmf zDa&cR{tn!!O|~4Gv=-vJf7KP_Rz0-HVnPd$z)FR^vNF3~2_aI{`bfoAMV(Y z64snu$crAeu+d_)WjcL*gU?VTG2fm&V{iNQPF%SPSgO@j{KbXh}4y0W#y z4xJ%2SxFR8#ItfP>8Mn@t+y9h2SBFoO7wljF&1QT^Rle-6+#rZ)hae(qvIs~M-k54 z1R4;X4WAo_xPG-9SomXgvPHeL#(1)R@<}BDL$LPs#C_gq2CEH*)OQ6d*f^r-)lo1o zTNT!jb!I+{LY&^J#TMBu6g*PY&MJAtDY)%2okEI6zuGX5<0Hi>{kB)O?-%duTx7zv z%Ze?LO4Q6nxZzidy$Bd5;!z$UDlqe~P!u$KcQ0*tk{exu({&EIRFb)mwxW`HQ7AvB z9{fTN1QKKJk}bad&9-aIyWP8G?dN>mI$+3K?jf);I#g3!@$bZ891=p(FGXDL<+@Auo=uA)dsI|L z$&}eyNeAPp1L+ud_~dDT+p`nz6nlGZE=l9vv2TIA_cQNrX~=DHEjFJzM0Bu)1g&qo zMCnoN0AV)ey0|9pz&11QYOB1yM!!V4+T%*QN_GKtv2g+QCD5IESEiNm0bD;9PT~

qi?WXS+bzW}}r?A_In%&S*WoQaD(wYJ~5rmtVnW@9})DGm9Pp1wg zVuC%}a3pt&l}P&2gN)>H#oe9MiKkata{?lmS5RF-5irwRxG#Q9*9 zSFH#*T0HE$(nDz5jY8#%I}@6HEAD%rAY3-n`-f*-+*20|v=+>Uv^u{qsn*a6+at2M zdvRmM=UGxbRdX}8q>-NQ5UMArXxlz_BwTlv&T%f;w^Vj#PGJW~W^>gqASWU!KqPn9 z5SjQ|BF7N~8BHt2yaN3=Az4Qi(WSV{VE4Fl-qVz`98x|%C}945!a z?cOD{h9yaYUMUDlLUo!X3IUBT%;>V};7Z2JT{ct82A~y7ZGL3yvJ|ftw>1>~HO5sN zdZw?XZx3T*hMt8bTvwtbtz8m@Q|iT<I^xQCx5e+W^;EfFs^gkI{xt{$i7ee-4UOjH6yFQ zK3ghIO3uGVc!9kdl{;g>722|Vw^9yj3P~rvnc-}4q=6@xWn(JUUN&Xll??k*igXFHy85D_4hUZu7z&yhD} zt8B%x*)J|fZtCMrGa=c9xX4Nr{{V!ln!TW6t-PggmKseKpW3Wz+cT!7oIb^9w?{hq zRCd`Vb{<6AD!8Z;!>rsQg(2G3pd^}%XYkX-ZdVsCrh!r$KPoxNUZJ-As@r~#xD{-O z@+~r2b(JB{HiV=p1SX>np6a)XYiwQBQWs5Oaenif5T}e-s0k(3f81M=QWgb&QVtVrysHwJGcO9~1 zNazTNTyIj+k&qQmXX(R-*~d-^24tlNRV2ps)nRquwOt{QI-xBPF&!p0s$wkWiCtc} zw)?tP)#0|hsuZCOtbUV#mlG2Aj*>NfB-0E|A~fZ@hD0#@ z#D5WJ#~X1?ct}I*l9Y<}R3^Alm^kW-{n0ldpk2@FH-E{KIk=G{yB0!MuEh?7OUpv0RZ6f?$=@@4G4Z?Ic6bV(2RQxq?MPhIGO(1+B#T1iY zc)?CC-fPUWZr+yZPd-9gZ_h$XlzRrMV4sCtCv3{XI284FeXBru zpCQJVnpPb}2tA^Q3A)L_I2UXwn?IJJL|GDFWucuu`3LpjA86oGA2%P0YRL5gvtoGU z-4M+Q3JO@DICUM9$Wij;A_t0RUj0+?oOT>c&BYmp!2?#XYENcZcNQ{UNehhJUMdjY zo~rn@wE3{Fjm=o8N?S=?J3BEmXO564YrO0(&??ovJy~q){g(N+*5cHgiz{U^gDOZ) zv68zxv3T1<<*q~Acr3TL5S)fN@J2o71y;{3ar7dHukI$|E232EZiM7B%qJ{#wmZ1C zL7}9tp1JSK=4Z?hrB7lLI`f+HBa9iDqu&T^k zI{O^nnVLdl-|XzjZAo&0OA4qWqoy2~hcvVj5LKY`jro-Nlk_PiizeE-Cy{ttzUE1ojl;oI8_`nsJj@f=p&vOANKupz8HP4Fl6{{Vqc z@60FjnBu=ttnC?=FG=k}yT*>_?}^bd((t@7DT$ajMYQIPI;y5yws|Wib-lVPGwH98y)5j#$IAQT z)HSi~$iy_Et(9kv+pfH+E;K-JC9(h^Kv0vHTvE4Gvz=J#0<_ND!52wvf~S(8x86_P zv#-~iq+Q!~W*qr1t_*Q1LQu;JaF);jSMH{R=*21WFa^8W*uSQYs&(ys)yeliF;?x( zd7cnH@U;Ag6f&(cB;gruBOVFtb`yG2$&Jy;QP9+f_s#0j<|{%&@?8ad0E_2b zS3*7t#aRxY(Of0+Z17F~ZT=-BJ;(`46#Dsbl?Lim z*?GdFPT+di0xbo21e33vIh5IVnXITZdy(AMdsF8A!-s!`j{un*zoeo{=DS)ASmZ~@ z_Lm6yh{uL7cm?mp$B96{&rKy1M)~tkw5a}(heH@V0&V!Wq^6bAnkCF_*4f@5hk+W3 z=m*z{D@8esQ1>baL}fH z#OIs7(73l_-Lc$q6M9hkoCnQqAaxbb7QA+G%vpZv?ek{@$DvnOZEoGSxZ$;Mv)mh) zg^H=9ug049j<~;BWLYBTeAd~yv~?U6NatQH*&Dl(Vm{UJHVeJ(JAXEXXiHAGljVh| z_oxb2*@{+k3j^*oG^*eGd%JI-#sShz6G48F;INRH_t`5-g;M(|K|%K#8sUK2Q6Jt@ z@^@0{uAyxZmak3RD`iBNxeo8S7ZGBR+lI(RRivEMrp*QmHq?QvI-(BgU&|m7fU3&) zjdvTJ+AiLdVaH-ase)yaOOf@}1gQh!)7LJzsLQtxU%FPUYrb3M10dBdf3eO>hR{St zk8~6H;k~ z?Y8W$lU)L5lHbCAnScnHQq{_jF!@>d8*8N0K<8w z1W_+*9UV}TVz?+gR41v!7L9aLq){t0;!*-eWc902giWEr&L#Z@9*jvphkCNp3hjWuOnDCkTW(XB8hA-4+3dXiF2abK5Z+Q>_&rY(2%>~@&Pne)u}Bj;tB(=*M{!c z*5iV3?%>@MsYven*xpYQszW+^Q$tU*;UDa)WJABM2Luv*V_lt44!nTx?+}is>%orB zfqVP*a)f<3ak#+EU5^MHhvD&fnPJg`~ zd7z#3R=^cq-C9rdRbQ~+4%T5_{`7Z>0D7o|=f!exfz)Xu_29qS-h_YeA4<^rlC#MH zYq^nLp+o#QFZR9ik@blTg% z!nj9TR!Ch?=7Ufcl4wR5PqhQZ2u`BjuZ)7ys2{7oL%H+MsOfymcZ(T`mx~>)+i+A= zsSP;&yUj>*N{XmTT+cqtZ2h*zGS1)23zqxYmwo;BnnBU*$+l4_-*P5Eh7qHF$F(S)4ZsdhSvza<4V2Q&WThH@vcqBK|W{euQ$ zs+B2D)#H^Nw-geEEk0?bd^)rHFcOb+uSWit0I|@D75gJOA0;yCsSCxD5oL=`vaN-);ErT7} znAp&It5qUf{{ZaTeq9U|nKsSiaU^BtZrh?M{{V_waTosR-S~Y&_9=&U!aou0N(cVU z{{ZEv@zA}Sy2%s;f6lvs3jY8>@Gw){%fCmkAd9x`Z!zo%VkiBZ-^(GSsSfe}?0kRx z`??To{zRdIJGqywrm=O3FzffTBq6raD`;cTc>r6F*lK^N;f(l#(o~?cGj>(v%eOT$%M_5Ds*_ zy-gxS8+z7#zDY%X%qg2^@ru6on(RLk)dEL4DiSlqQyWF1%6gQU#Fy{G`6Ft4!jF+6 zPs=T`aw&Z!us*UeGlje@lmfGE<6`{V2fd?EDy}Z&;%JvCPfLLLgCWvSmOw3%b0VFX z(B{R7H5x8##BX^Z9WT@DYtl1EWfstR-q|FIdQ*xG9M4dD=NUa4i z$6dw8%D!z<%M_SRNp#>>$ z0+KQ*BA7(m4@cCO`NBs(trw4L-{D0{+1IkV)kRi&ku|MrUn2>NV3RjP!21vEPTM7? zelAMpTZH4*>k9%DC@2Kx=;Q)P9McuDEG?HRg>b5H}y5Kw@%&UEuAVJ z)ZJS~TT|>a-;&F|T$U6{bu#VlXM(*K)dQyY*Vb}^HSw$0WWyWIFM=#DS_Xj8|^kLWuhnkaBadinu z7Xmuvs=ubVb<&wg`mF`VT7f9I71Q@Y@5j1ANW7p$*6h*$0GzC(drmxB5Jpmqdx7); z=B;iM*<3bHmh0VdT0+$zIuPD!G^rQ_A*8DJ$Q+YDb4eDsX>p4rMb!v31=Ru7KzDWw zAw9sY6{iu8Yeh)aG$YP+)aHT+=t#pwVp5*Oe&E#hUyUg-kdG7YoRShd>xkQ!i-L6= zz;RNqA^OIR#kN;Jmo!ehFxd&YNT-q(Q%?#3s)#r>t|!bs@@I9FBSx-Baz8C{_o-df zfqJ~jzql4mD9dxphZ!B|O;UskcG9@BTjS%4f`fdocCJM(TwNDxThO`t%GEOb@+&Bdp?zl?Hxhy!`J6*XN0tr#yz2& zvAb-IjDiaJ!rqpcb`9Tuwpi}zyxAhkc@}HBXcHnZSuL)e0 zM++!Us|?*6cH-(%K+zZ9T&`cU$+udyDc8jum0YRYOa@QBvny$CCRQ+Jt;sm2ikF3yWFjB!mJj1Vn^{-hhf?F z_TP3SU9qs?-EGX#$0lE&519@FQ0kDHCV=JzC@~EE#X07uV%u;tQYuu~C+_=x3ji6mnT|G|zjC5}%mI<*d!iK2+;;Y)ErGUn)A3P72bAhH zmDiz7I$_*>NxAY;0ygI2kbsr2xM>|$qbWLgL zQNGnuZ|z(cJ`$T$M+zXQ0FODEWTHtlHSEKmAn{2HiXBqz!UL&gwrv*GHzXRDoOQ5( z2WLaq0~1PbJKM=Iw_efTJBBk!PnN>ce(lDkA(9hH3h4s5;#S%aIHnRxKv1-<2GPbB6Rusg(4*oCx+NrcWKoYqD&-$DDiUM71I_HO zNq*F!{9lTr^(PY1@lkUIRWbKftCBeA$8T{+wwB~nq%$cJO$YH%B#Pm{e@>{P2ET&Q z&GP_SntSkAue~5qxeF>=hZ9aEyMNbu9z^8rp7cVm7r#<@SA5T zGaB3GkMUBtapxf{S^2Uu)^OX;LGrTwCv_Tl*pJhL{za#h9JrKJ8H zEShPcHPK8V=f(GeSLND8U5r@*yB$l=3K%+!EMTS7W$P zbpGKaf55@MVV)>FnLH5(cY=bCnP#4z>cW%#ObPP*SDx8?7Q=t0Dm069@JGS~2}k}0 z8quG5CrLaLTIIQ$xxe!-S|$ao0Hqsy=AeI!j6IHE&IYNa#11?{rM!jdzR$ZiWz%um z8J}iN!Ot;JlNDv8xKa{SkWw`2rBAmL@MN0NMHu@{Q~v;hpzod#P3qTi`HjoAE(%Mq zt*K?iyBXAy6r|MBiXD`~!uB^N?uFr4L(j0$RJ*e-jj2Bn@&WyLDn?hDA``ttS{h`u z_R_nC7HU!$u46uc;ZYJx&vHPS>dR}yiu7pPR_>K)RhPhI_Y6cq*~8Sq@Eo^_nTB{66w=o#O|;RjOtmR#7`QE5(~@wO7PI z`bosrMc_873mn2^QT?T|?1-rESp7A`1+c5tuI{akO3Toappi=S>x+35A!qL5N@H&2 z(WD?`4^xJB?**k#V%Tb#ET~hjxZ(k&PRI=t#>#ax;rC)v&yt6{TgZ1(mY`|aJ>vs6 z6pLF#Pg$mtYCX6~DMhn?78Qwjqf*={Z348^a~M?cNJceCYA5H>Qiv(O04cEq-o3Xu7P@pTNM?a$xcj{9ZQ&Ph65h(zYGo^FO3@qQ%ASy!j zm5i%CZc~}D?VF9FYmn~nB`~XW_SU2=mo*erDI^h*rZqdMNw;klH)#`DaPZ-xo4DtP zw|^$@Hf&cXosAGFE^b@E=+tV|l&Mu3*U>)u_F$G)l1%-0Cix5aE#voiuC_prZ?M^| zb_*mVTkSS>Q5xS386^!gkdgGBr1qR#p@;!RkzGpX$Xs}o8r^i7n&cifK> z?Bp2{R6-q$`p{COpWRzZ2d{C$E^~)8?l!f};FJ@%w{+K84{w{J>L2S}x=m2bQ2b8d zjg`dGsf@OC924`Bw!On*T&$M6w=-?NR5y!zHwNczO~(G=jR-tQ`B{!Q(;ifm(yDHT zgF4~VlZKB)wOyhw5)s7UB5fGjB%PaSDh3CZ32E-ljGW;e!eGVRh` zjH&S!9g6yyS!}JB4xMOKDwR_)o+IX-hLloJ#=fF#@_NZKhAw%92 zMiG|z{FCT0(GDr;a@kuoNu({u2)af~YD-EVrw9v)3iE8C2?bj!0!0P-BZ%!K#>eO* z0B&SU{v8qkY*_EBOLv%wO1e?xANW`=%}9sv*wCxvMN*60QS_hTDL?SAhvF|@{56Rx zPY^|vq^*0A0HlJN8c>dhCE))6h)Lj+{4u^L>w8Z9Zj6Xb?`bKD&2%9$o(TfI2agAR z`)lx^b~)6qQAuxDCJGmK4Z=a~QeJUH{bvL@2KXfYP2MQ9n6XxX!>wH31EJQWpRZML zsJT9c<6~YawIOMR)dTy=B-i~c56u)`!-8(P_!9+5{32o~NGYHv4kNoBDHr3C zEQuR6DN>)eO>BPg(~$bk91F6WOOH^8AUU^RWofd)f6eT1-83EjFuYeqHJ2W%+t1!D zGm$RV`}WC=X4~v7O^`=+Bj9dVGgk)H8ZNHAGwChLe&oR=$kPia{;&h@<8c1G$({MpH0 znYZ^NZLJG-GoxBi>J*gG*6XEo6eLu44EF9ewqJDUwp>K-k`d+#IY#BDlkrDT@9?Vv z-4vIWgCqqFO_8U(C}597=__#aJ-O2hRR=#A9Y#xGccRFi({cuEv~EX-fdvE zq?%Tl9FJkdjwd7JOV!0!_fFmy_Kt{XO6%_)N7%LD_%Qsqv1{sk3Wid`b60KdvlwQ&l04!AqkG_L<3Qn&n!6nRfn2paOI~! zv2^3RzU`U(*=M@3e{JUj!Z>-gEt(95iuD+E74u4L>l5Il+eBBKj@&)rw_Ew90QqSu zjwwo16=h61GXudkHqIPV)vI;3d=*;KD2r4<0prFuUE~z|IkCtzO)a=H546?yeTKEQlMKB#s@lJkOz6n}B#)A4%+?c_{ zrkavMe^|$)k-#LyqdBJ=8+J9lpc;bG(vT90w39$R;}Pa}6I5@KjTD)-6*TL1DYPCAg8FN0$60(@y~QkBG*&J`GT;P6FR;`BU#r!0yf{^(ABW*iLKC-&&@ zS$BzJIw&^ck?qn(1N(%ef431gxL(Y3w>r@4YhS{Ul7HUe>vtk7(|;MY$o4jr@GPZsUw9sD|_wj!xGf1 z`}ZqL#bO*M9rIGj(rBO;T!(fZ?fk5+lr)tIy|(qSfV>J7Z!dg-zPBx|Wv1^bsn&utbwc{I^dsZ;O63%i2SpW9dm z3L*DPyoIwjGn;d8N|SGm9V%FDH1eKGg;KOAbxG_vWZTyhMCR`n*0X}8zM*1Vrn76@ z36QqiE-E|Bs=Ab&B|%+#PAhxK<*fy2U8?9Lv~dcXyLNgr%56?pN5sDF+-J|7ZUWgn z?DpHe-Gsdv0mLZLH8dx_nPNuGh3%?_zkG4Ng*5MF=(5Zyi7iQFkhYIeh|7XQsGnNJ zPzq0KLQ;^DXaJ@WYv`TH8X0re-4fnoyyM<+9Fe29Mq^5K&yC^`djX~s`I46TV4Tn% zN>$(h8s<)Te93x~Ibi`vwn+ocPI^ya?--l4926big-FrlB_I<@k>81$=&SE-T%>C^ zIq$u>4lS+}mL0XlNknE`Nvc!UfTWBul6QnwVG z33)30I8*Tk_$1%AUC5mH|elG2j>vPwtq zP$Mt&uv2={lamJT8l$ZGj@T}(AjX!(daFWIKEs7seAHKB9213}dgB*TqV46_y~V~< zk@e?;vi*mWZ0tmVS59E$NV5X>zYX%!ZC-3Ct@~ogQA%e<2Bp~OeKadaXdOlnxy(8P zRurUrUOS(5W>rU9ch&^;w$od?ZMAU%fpcOkM|-bQhboixj7#HtlJ@B!s;uSv6K?jC z385-LU#Si;of%Gr&4(Zi;yB1EB=!P@5sN796`^f;su}FH`(H}vn6kY`ZACeyyF9g| z9Mx9EMS6_z-QB^7yaKRA*;|(lO0e8HwKqSgSQiPrV{c+}_SXYaEmEh!hb*Y1DyeEq zs45gc5X-X%wcb|p@zFtKyJvlB>H*Xf=IQCD)D@jYH$E-6r$He~X)Q=iJL?^56h8b* z!Jf8%0;_ZFi^gbLn71vPyf)hDciR)-I@j(w732_q%1$1?B^OT>5-cWbz#xSnSUpQ} zHr!%0v6~!;Y(q$GvbfHXMCI`cR}}mCBI+wnu!`g`0HphWtPV_JNLTWgn35~vY0;P4 zhDJDuQXLmzO50=e1LYm9Evz=NYQ$029sVe^f zT9jR`%@a=VxlkA(?bWgUQw`Yn1pG-VS0vh+l?vf!YF)l?LJ!E6+Xd<`1`4J!AdOdZ_8FD zR})ebmM7*JHM%D4#Bmq=N@D2v>FjZXhmdz!=i_RJx%{x(xA}4qmRkJ6wsQP4xUO?_ z>RFc+!YkIEU`8MR0J_jSLd6n)Emti*?4W%GO59uOuZ=lQ{{VD>7mjYH`EJuX`FKH0 z1vs01xVD#6nk?3Z2a;SWQvgLjY@!GGHLeVKBhGsz+J@>h53hitBCV&#^fY0wg@cgw%b5S z?-XMW~IyKD*;+XGacDs#8halQC*9nRkvu#vpu>ja(6o`leI09<4N4HkU(6BY`T{mVI<{O zQj(9S1Y^3A7dGTFgNm@*%h!LX**C4rGi34`n;&jor#mLsw_!lEc`;(3C77(FzZKO0 z(@08kG$yCF5*~SKh>Hmm^>EceTesDJsZHSx>t5yNE4|v@=%djTATosxzAu-I-Ee8{ z9zuB@5(b)pt4-43GGwPEOMOx#yPJzqs+3Q=5pr=;@qorvNj!};yH%HC>_D*EY_L-z z>z#kUPFI?`gW8cnhZ$>t$io7F+jeGY-8D6;c=2Au;!D_P@wFR`!GGE?as(;((?U;$u z+rg&)0Pjo$f4xo+^IC}Z$xTzr-FMm!0FkNn4Ad0iPwB_J)}(qKq-zf4+qTsOji(am zq3}5ThYCSGGEFdXBM3~)GFMQh4ZF+Bu_4#(dwRzU&FHlS7XkN^fplV=Qk_o&M0!^_ zU8X)I*Q%oi+ezvllMf79yZ34d*uUy-~O?@eEL4~li{|JnJEbSC@{4cPKdsAz6m+c zQk+)}Dlc-X4L^ahPx>PP?j*WqO^M#AIE;Kei6!8EEe$$JKjh%y%uUP_B$4X7gAfX3 zZmW!~HK`0QKdo3=DH;<=G1FBf%Jqf7F*PZl#%C=xq4EF+E~CeV#O(>A@bbAKJL{{5 z;}hj=TeIK5wI!s3@2ZIyDY;-u{{U~wQWT$G8wDy-qVepvl9NDI`$E3_@!@}}SZ$en zQcRy;mU5cGJypSP=?zcb~l_G)%;NnImQe&kqs8z#za`esNHQ5s+2IKz# z=<&0nbM|2Ex?^}HTZ(mcQTg7I<2JO$xT%Dz;|NldT&bRufqmAg+c8BVqjtz_sI??7 zUTRTMuj#>J$j~ngV{5?*jq7kl5{Uy*{+&Nw4swgm#E%8C_npv_SCCKfYftMq^qCZ& z;XWZJuXasPR;SxSPsLS=V50RHVnF1GNZoFzgev615|TB2=pDYy56*$j8}P&6quag4 zD&@#^iAZ@an$f)T#9WtQ#L-IH+LnqG+j41DPOou2xM=Bmd8rW>p%ql)Ow~i|rEo2~ z03wTepjU9ArE6Z?6VDzf!r1~YKTrF;_%kjNjkiIn>zMISpKWlj&ZPcLtKW*B;va4j z7YpAku-)ZoDbG)RdSK?@BC^;cSN+V)xN-cqbVx!#P#Jc1_TaM4Lv%#)8l#8qG8VE* z^sQgK3GBl&a^xQ<(tuUL?15U3wz=RwVcsj>CeH-^+B>A%Q2Q>fh+kUfGZg8Gtaeba zP^$iC2H8k02{!M2c4Wo4)75Q12pLliu5LWXRXwq^^4UU&qVXvLh;WYcTphCnyP*Xf zPVBoiK$$DcF0{cuaHRf1Gs!P)zC(>_zgccOp3*lpmISsQx>(k&DVZI&5~Z?8r8T4x zTzX5_7Wz>cj%QHOb>3I44?@>HryYAX)bA2*=Ga17u%l@vTvnc@dgP@GN$l|qK+iO; z6sohq91Lh6DxMxe-xCAN$e~mLZ>J=N(hlU$vt^qsQ-j z6Ddl5Z!5}L*Vd6F5=rmW3Bk>Z^KuEt7kv76h}uwh^~OpR-~N zA~j6iYSYt9V6U*858>qx%qLqOu01BS0#yF)1a&_oiv6bn_-YTB3Bw=OkEF(d3V(Ns z{{RiWiv4xK{u+no0zBWXzexx`^Nr`9@Y~~`)-YKyZafl=xdjjZ0A2q8Nd1yGwSW8@ zANu%D@Q0)M;cWM<{{Tva6s23u)Skm^iTd0aQ}E}b$v8zn>#^xj*TKAz+(o)2fAY9j zZX;{{SdP1OEUife}B!1wuoc>l4!B;bD!lxul=nThXA3{{Zl{ z!`54as$xMr6McTIy*L$kcD4NjxHA|306B#tZ3=8#=|Qj6zo%phF}HUL3cqzN#RLBU zEHN&`)W%9gch%PeG^G)>cOuE?YZX85oj6a-2@jTfLX+zYiJMU{_7(n6?DJ;DK_BTW z2NOmYzYdUufpGPiv`&!+jBX1#)yMAu4`xju;lpoQk#-^Hnq}O5U@lNvVez{Rsa4YB zw<%5$?I0u%w+g!UoDn|;^r+tN>KAxi{4wFNYi-k#8cU85^r(}C;kl9g1yFt;3rD>G zEI`x0qKdCuqW!p>3ZEtED^HqF*@nv(5ifS92grtPIPVTsj`KPI)Y!Nj~b!Wvz90$z1nlH;NqCh_o z(nm}x$?Cl5>WwAi>^M`*2_qU5JUdAk5h@QXt-R`IT<~u@sS&ZP7s%*xz=x##vV&Bm z9;%@jZ`4wrLe)|~rCcx7L;@wSlyU%(-JS!iCA^@NWbSK?m*nMJC78~sB`zocY7bnF z8D^xTMG$T4VdgmVLdNC2(-Fit9&PoH-ckimzL>3ot+GU&ta^dnP0+7Gl#YN31BU%Y zCfL!``i|X66fs#>w>&D>RVhR=eMs*eK>1B1b`e}2s1()2znG7x?go`~nZ=uxyBcqa zX*Xyu#bRt{{F;;!i3wE*)P5Rat<9WraZAH9ULO*N@!7lr5Po-_sYt4}j?`6q3>hE!+z0mDE#^>uJuiSkEAzee zJfTHwjig8a04YiT0M6il0Efh{emO6IMV0y6`jL10U5B*Q{{YO7{i%3aG_EqoGg=4pjCXcpx9FrDb<`2&j$W)gtTDuBu$RA&i3wcfPNc^Em z6FqeEjMlujq+0@2)S?hc`oi#YPDKaF4rv8GvpJj+Q*wfotWbdwHK$J19C%66c%k_K zc_!7%)UP970ZjREmVsKe^2pA;xs!)2yd^R?IKq(lM|#ZP*ZGidG1yC%WO?x(Wq^eM zwGxU^kOFZ zZ*;3BF`UyevM5Js>4<2~s&-cBp7vz_0C#n19fF4vR?g{AmrT}(2bOh|)}VC2$o*1c zG!0LFJydETo2l^w@Y1*vQcf!q7x#>Us_PWv4wzu6rKkk^qZdHw?~&D#sW|lba{EG z_XS|g-%KZhZzpJ{rxl)E^_jR}ael_+zAA%cl#RhUUB7nNE^<7{ z_Sf1%oM~xgl_06LgdZ2Qci_1V-0;BWQ90Xvx`p$;JBo@8G~@de5yxJ>Z)&`}{{S#r zB;D*Sg#5G}wwREfQmS&F zXK{}h-u4u&xXi|PEyneAk8)kmJf_P9q(>TA^2%3GI@j5TS&hOjd1|Nje#(9-E`spt z`u_l0t7hi=p(RTxr$7m((Vi>DcX+GVfxZX zjkQ4~(X1NRU4FcH6h2<5QI}Ql0Ym$$z||!rNa`MWAb7Lypup87~l38LR>CDo?X$`o@A>NM$7 zi7R`C4INL@=utNq_mfuF^!1dJf8E}dw*s}=ubZmC!pew6+S?XNhgwxu*9uAcFtE9W z+Ep=|9dO-K22F#FrKL`7ZeX-Vdot24@#aBDr$ANZudlZRn%*P8uUpA8J|LM>uUq$N zI#Tg_J*wwGeyPKOZ115%#^|1NR5aM>Ov0 zaqq&)_u}pFNOC=LZAA~e+c#SFB*7Ii1M_pTg3Ddv%Z5g<97H+{>y;bCq17 z0w8QF`c?f{an_S_s+0c!+DcFElk6(X2J?|9wf_KVZn(oZ+yj)`6|gE=BFdLq=i$w46rYm zuH&jp$a4)_qPZMj>$j3N1W|9eZbtVlu#F-l*sVQ41t=5}74@7~vKVOP_)Qg)ADEUg zL#ln;{af)4?|E`}M$h?avJF=@F;JrV{5{>+fqoJEijw?Ur-(yaxzB*3$wa$l{`G2@ z*q~A#bqOBLE#z=5##{mJlxE#R?Mjh{sXj8euo-)lZn4`}#1^*+`%{DsweaAM&y*x zhRb0~KE$Gcdj=kf){|`QVfZw6v6lg_$58FGizIEy?O7+CGj&ZTL8miV?XS!yV(~6ms&f9eV0C#(OM`a`sRvL75>z4U{D{@UI6Asgi#n()w_(;2Wo#~-CQ z_xE+biWA#w!@wmbIZI~Sw7VO4Ms?spO%$=DjO&=kox81%G^0CxeBMgUZSPmycYn6S z+nld1Cg6t|DQX4fw?DqHo`b}C(+m#jx33iB?3>akLW(_A-gq`w9NVVxk10pp_o*{j zNZRRbX_X#1LWTSL0bE9WwE>R?f7TP1RI zQ{P;$r=H$Hd&qiGV)T~IojQQ&pX|q*JrRw)PN~&x-jSG1ODYN-bN#q|23R+eYd$Nf zIY%VLC<*yRlTNi5bNJt(FmB%<)T}eT?JnyVCgdSM>O%np)c5K*O}T*Lm39aL#UxAJ z$u2BI3QGi?1;q_?_a_ZzzbVW(pwUEGz2hOeoYhDvSQI$aVH&HsatpNW%eYK%XHVVR47NH26V1 zm||1bIU}3Hp!{|EFprv?d1yxOOHal%_jMQ(D)*M1woXa8SRHOeGz7?R`#C96+i?`` zQb;&n9E}AMGGf~9B~M#-)=w{O^q^b!ebIz9O$1!#(LTTw;REW#oOxcJ3Jz_#8@?c= z^!{P~pkzRi0nO8r0&D}-H=aXO{hhmC@O4Hb^Edbg9h(p*l$Xt-#i+iE0K zni?8o!v6r$QNImufKImgujdn`&4OF6ZmRyQ8}TFJh;dB&p^%hQ*sE0xaGYvl+w3DKKN%7*-g z;-sNOilI^n>Dx?A+szz})mAUQhS;zRQA{BJ->5qz7}LQ>txrZ6vK{wj5jz&KY>ccx}}fe zZ`7-<`TV%OPHlD{kp|mm-VtNwrCV<82T1P-nuzuHR}3#OPyN!StoM{IKJXz^4dbx) z&ce9<6T0^7oxN~CC*|G*+3GtsxG2#@bBKHOHm0px3f^=@BIyRGk%jKsG=Qq^I* zC?+hZ%1|mpM4`lj0+N%EEVnLZp(}4-0OWc8pQq|va@v+V=IoDolV!E8R9gxfa_c2J zbTF4mN_7IS+mb^FbHNTP>#;(w7?2rFK^f3zU=L z3DLLqcJJLNeS2qZO~0ZlfRf}!7Wi#MR*EPGPD8g2BXgg@1;g0E?`WLO@h>fWN#l#f z2Spf*&kutp@Fhn4^}mWXIJ=ioL;lY13LU9TCkguoJ<~VcE8jf_icvJJp2=NxjXlb;&iS|7*qPkzikVSj>XZlUo>ZgHsg4>8d`^?g+br?##G%r2vaN)Qaz;@IC3e36sE_iUR7uSi_$7O z)-wn64128y%YGL%n)py}RAzpl1a9gJLGquVOd8QO>sS^&o5Z{cvPmRvKNdEx4g}l&{ z`EkK(_`1?fPlvj{_i%qOFMO~IVRtTgw4bbCo@h>fTrYkma`2qJqXcv6lXMhW{80Y@ z0s8Q7H5;U@(|FQqMt{0%fNb?g@`_Pwdfee7LsCdbT_>~Mf*X3Id>(HNpV%|p*TyBO&j0TfiF6Pok) zi~)H@4Wi0q=}AbYf2RiPqA-nUWe}5|eTS|Pbe~EynNYR)#eWtF){*kcAi1h1ue{-J zFdz&iYx925fb&zYETZN@d;b7NJ?5k+7nLOgFy@ikD}`Dri007&^NozW&bUlIs0%27 zdP_?|2uUg(2^cdbMRaL9P$%NlPi_XOCyh{QUrLR_BMulsU z0DF6I;3j2z%vbb&yn3tME84Xh*0=;9Q52Ez>eKY&&S?XJGaf3E%RxqfX+7O=(K}&5 zDl=r)_TVIcp(q@;2}sB%1nWyI=}&e8s4pV^P)fR$Q@;*6g7r|LB<3;afPf*=RywOb z(}FKiW5rYAt$p|-n^Y*vK}w*h_TcCd(n3W~L60vi0+KZXY414l^7qV-_g4d=^4rey zIun<_1P<{=bE>iwYw=Sas6;}k#U`Jp2BI)wSo4o*BMEAPfUJj5pWi0F+$v)OMZlV--TGRep7O%=q^UtU>g05HGeA7&5cqIp3Z571B_w+eLxfeR)2nrh5_7&0h{c~4Xd z(EBm!fP>3g`N=&kv7K^OFUD~A4RTt}h??CO;k39o*vN99kr z3SS5)_aq{q06m8gRaQ4Pt0aNc(!DY2r2M7epKVhtC(+GSE{?5+vsl8>i?1x~2+6v0VC)-5#qa1}@L!V^-vPCN<^ zDOw*)Q`ofJx5egKf)Q@Dh3Y+&aGt+LG#!&aPg}eK%e$WeltjQdxj{GUic@AOvZ_-ROZiN*7atXW#@h&On!(5^V{FD$do(1gs?t~fc6hPaM@9uz$w}2A z3A?;pAu-YqZX@#i6pjyyNZSZ0nx!cGDJGa{iyrcGm;PFi7VSk6DXZc*vvn0xF{t-< z^=lBh9tz2dOvcG8MNMnT2hvf8rD z`>TaYO~j*3P0XaWaNOz@-7EDzOyZ^V^gt7xTGNDL(d zpzHMvO*L0;J|dFu04geybD%$?4X5CgwD{Ck+p0tBprsG%#0%X*tiaGK1z~tSvnQo0 zu7w2}{{V*Ulope0qI zJwU*=au7YKMm&Y4iPxWMiNLk>P2S3-L`)PEH6x~YaQPB`QrQy}SMk!lFa}D5PJ)e@ zD^`)IHU9t@kJW+mYJfDN?mp{85THTnfb&wmNkSs#>IgmF%nzD_%12Xod<>6V97>7h zlxe%^MGm4*Zde~R50&U%RsG(HMSh$Indx5WD@5)Gvq-1(a5Y2apci^7JxTT-@Li@zMl98YCbPD9^~{dn&g1l>Y$653>dH5te>ne;CKS#pSP>*S>-l_ z>eMpqt`A)jaw1hnE>^MCUs8&?!l1T3M zoO-1n%nKq)fv5)qQj$hdE%o#?`mn#6lk$QxACN%t95`dDy(FC|WP-H#%~jZNpiE1u zhHgHp_`s$-N<3(Az*;~wH2N{(h>o_(Nz1Mh5umC(+)x$QF@vMDRUPt^RDi1YoFAzP z(}Ep{sHclQ{{Z(46)n_Jua!agO*`;3URtUMUP@Ra+m9M}BXyJ#B2igTj_eVv6+j$9 z7{>urBk95FJP`Gj;SI$2we;++J)(E!m7T18I0KL>!3yuz}>yHq<)PYvDQ|}0;y9J{6S_Q={R)(Zt;)p9= zD5~6bO7Rj1?7_N`i;#6+@hj^%DlJfIedT1elc%!RQc8yG|IsPHFItp=A<5r%*fagQrFEk>;j~jUGj4R?n|Vn^yUPvK zrrw7uO*)AnVNr5C5>h}<1Zg{suqbU!A8rh92ZATdxe|8u5#mCv$VnvTD}>C&E#w5A ztAs<+DXI8ZsE^TteA02`AQd%*b!gMNq~JbaFL?+rn~mDv+k||?M;b2|Vw!16lA+Nc zieXBn9LgZp>djMMr#v$<(4|`%1#UQyd|4iyGQw_o1s;e1N&Wiwo|s0dUny)}sG%bs zPBd7qU2s%NEVVt@Pqgz%L%I%axRA7{dqKdt^hR_b@lS|+gKTB6nOim!&l&|jg*e?n z_5wy7$CCp}CNkJ{9w-@`_5#-0?e-MgoCc(&i- z;U2^*SN+EwG>5xq!vo>+=gLJ7`3?*sGBS>9#|dtEFDH2Klme7;1?fN0{95Cu{{Ssa z8}TW8Q2q&T_d*6n=D$i(Rdc+^x}W2ILysS=!cGE9*PbS+M&sgUfKeH?ZVkAUsNL+c zr4d>!iAiYn{+bjpNbdqo@V>e&oWJbVtNXpO;>t+y{{VPlfA9tqH%%+T2?0QxzRy_F zwr&ilWnb>=?CaTr{KFHEA^g%5rEG^vily8V7yG5Q5Bw|@(=ep|UCl?h-(@Ct+Fb((o)$a8j>h#nwk(s7q-Z{6H%&ZeS~Gmp;n4N=6A_W zcxG|+vQ>UE%(vL70+|AZ;#6*jj!FsJ8Z)g*KG)`XxJU+P8v;jPChD=5vr`qvzq%jH zCTC%fyq4Jf&CZY?;~7c=y6S}=wXuc2x_t)ZN&r_4uJXagp;)n-aKRl3trS|B6d1zml2ee>nl0X>&I+K=k&j|Ue)Iu>iH7Q94 z+4kXf%~S~2m$DW_UA zQ)j3+BZ;DsY+!1W44QV4^x?HiH5RIt2UQ=eU>CJo{N_Wgdvn0lF93mhQ7RNQ%Lvwq z!YK5TC>BTq>A-o2^wB3SNhYRKtsGSl?oBsE2ftK09uloUbh_IEo23M{PRbL_vG;BxLPK;-B8tUd#x9o(sbz=tb9I&`J`zf!jk<^$Zxp zM6b;?2-e(7pAyue+5k1cHX;&VGL~A|>?t(}SskEw z-Pdm`18Un-{{W^a9??)gUKjZo-OVBMvZm$!(9N-sq3fJ6m7~q}Ot8lWTLac{w z6l(O1SW1)yHB^qcII2XDwhALE*0?wZx}lJ;lMeSoE6-*fqN)aniH8oZY2?(SDxAT9a zgLRZ&EF&I&v|(C=+sj&+0X?Fp-;W7EO0|_*nKYp$zU&KTuNC6JwN$!vPy3DxWnK}W zzF?7>s5>yv%}W@NCPI!%SJ8rb+M)7-H$6yx%^hf2)WYb#?DH5tN!5fDjwz&EIC!S% zc8A4ZW-F~VD#b;%vak(mJ1}y+MeRzcp`|h8ohVk?)lf;TJ2C2`(HLdtLcTLWhYWR6 z4{Z?NFjR9i_M9CMg^!sZ#4+s<^rJ02J7^DPbjOf%6yuF9I^%^%UX(>jv>;Q&qu+^3 zdxnM@s{O^I{LMHffpn~?RjE-V{dk3zO;wg5q7J9O?ZPQqEEOnIJP1UPENdh8y7j@S zrBMqPg6mqV?ZHV(HMCln1v2Z80R2D$zac0+MCa4lgLD)tML0zUbvOq$h{35%Hsv~y zp*4_u5>-Mg@58v@^rli=(C}L~YH)%K=Cb~f{a6;;)0z*R^eHmMb&Ixkef|tPkIY>+ z3YuOORmz0F0*WHWUQPF{C7f!5&Mq9!_T=ciDs7Rq5#9|X9@v5_78AdE^w51Ap zWYm<>ow=MNjN%cqC*4l4_lGXt&$AXqRZ;Hnbg9=>F2x~HnpJD5AlK-`t6M;; zmt+?rlfhdqrnEH6>cV#)3iX4i^$E`&2`y%qYfIAq7k!^9%xs%;Xv|$gkdSoD)WH$R|QEuA8tIA zfTqY$J!o*RmXfL;<>Vj$p+oNn0P#YM`Yn)&12Spd{^5hi!6r%+2`VG>)ZpkeLX^v( zP!&&U?!d1gR9sJ0U=Yz|FRd98NIje4?C^A( zn?yKqCrvwX13GCz#du;bo)Qi9yC2_S%Lk@n#2bwcv@%%}Li+!+N& zit^tyKeryLPC^kyNJ#3vHRvmkB0Zw3KQa9;(SfN&=?P-b_;iO{MZv|kDfcT$O$xh> zaPrH`F87ib9bvxeawgMbP_ND`{{WPV;$^T~c&OLSaycyj054kA@Jd>{$AJEf0k*f~ zQf;`n<`OmL+Ya3o54R~!t;K|v{59{cSZOOGXwau|%^^C3n!|9GDyivc&=Pc$KyU&G z&onQZN^nZaw^){iEmy=LL!vYqoH`b;Q;I-cP4`n~L-E*@<19QRve}{}07k64jv^cz z)u#m_(C(c=GKY~wtQv!Yk^(B+&t)!YA4tKB;dlzAyF*g$t}UnxnFX*E=8;kdw-2pY z`Aq_0c3TLkPIh*_;djK{rRM_w0C^}RVoc*@@Jv4yhs6_{ZMa&B5jrZOsoLmD#R;Y( z=C^!jRO&mXKZ4d96J@9(EOPRd(J2FQB{L=lmgX?HuCL)ki6|s{m4=dnpT{dt1VpVUp$i{g z5$Y*lEpPKlJq>->^D6EMQYf?YHSAaUrwjYu2(&?|(obBwGQxgakfMrtAS)w5+oa%p zrR0|T=?WsWJxRe_C{Wb|A8dets*}?Mss!v~WbXNFA=WnJw_8}NY7HFHdJ%<%yNW5f zU>c!^9&dyKv$kf0fl^CkVE+JzJvBGYM4s6DwF6PGNdR@Lct&ey_%BCyABttqZR>ou zAQz<#K@SFXNVYDci(Vb6HHAQSlP! zC$hib!P_uAl0He~jCr=TD3mG0EOk)s!mX9QE6!ue)z@v{pTAnj=nZggwxr7nE#^T9 z&Qh}d-9NP8LzunN=f7pX%f_*iA7_x9xh%jN-SLiWg$dq9hsV8R?b3A(bS_a-4N!4HV&0nS;E5Lo-56c zh?<^C=yhe;ffIyjB15p?@f4^U_viX?;_9y<1b~e7)L=T2Mk=EXU0qE;0-fYxKQ#}Q z5N3O-B$q``T!6yjPXydX7UFZAilF^CD3qrjSVtmGj-{nCRIr2b6m|P>yC(E6c|jj9 z*$JXl`8o9|!lF=-F%_YaXc-dlJ=LxY=Y&36mVRQ*{BM0@9`nKvEy+eJ`(?Ex%1h1w zNhluQZU9aKLDud(606q@j=d^uN^GQfkSMC3rw%;!_$0iELDZ(1ej?o8FT+ zh}XYWDvYIu-XSc{Zi+M-&Wr%nBIeN*n9P_G40nngAOooj3K{!Sa8%(G^9Og)gyTjf zW6c@#J=jqNP!2dq>({VhDNq7X1;Lmq`ZL3ETBX_$Dp^se%Lge(8lb|pbTy_2r$m5Z zs_-d24t=<2YgD@Ol$4SVN9e+aLK3Bc6>;FsMEqX~!*@tQUTk8BHrxv&bfydDlsy3m zTvn`>2>0N@n8h88G%*rzuxf)- zP9OD^r$K?DDF_8J0Mt&cKd%6Z)8d&WVkWYPZC{QGh)2w!O zU@C=rh&@i9W&tT0(;i1!=_jeLc32vsb*-uNd$0@LYkCD{a{eV_!6t-g)DcddxORca z!VNWhtB)W~drst&Y-Gn=R%gc~pC#l45nkmA5rre3iL}CF=JVwR0cfMCe&Yxk?$Z<_2^dUg0mDfsu>@?2?X+WMR-WIetE3;v&C-;2E^ZIpT!Q7KW6N%!5nY(S% zBRL(NsS*RO3MNtVt* z&=jO3ni3OHT6bVwF#!|h!B08t>~x_kweG1h)T&Xn!CNQWUm2zfX9%r;a7|g#;kK<2 z-Y5HjS4j|NB5Y+NTV&BINFzMDE3OF3f)Sr)63Vv>iHa4t|j*MbdG=jl*Hl8W{vT5|d^?>#DQ zbVL@H+^UocioPWk0#DX(+43(2q}3_<(MUOa@ZYGVOBaurXneY;_34B8sKIGRkM0cp z*bg+J0bsK4Qncm$A%w~R06?xf)t{PyUYXYcsuWG;^mTbEQ2o$FaB7sGi4%5%I_KIk z;)0%pSg9H1Q|`leIHf>Dm#pR!f+0o7T}?p;y8wl2QZpTUG32C3FEonQf|vq8OE**s zof(Z)z*TtYfm7WFY3YO2G;s$-g*=f$JYD$pQIIY^2>5zq$O;F~O)>;NmvMLVjQ zeK=HV5K#bL6{r=bOeaJuA2JdvO49-~MSE%xf^2H|kU;Jj5h!XYh?TIHZX{|eLPuZs zR|+=hqKJwW6D_K*_oom$I-k*pFkF+(Ha7b3LLyD1fAmS@p3Iei^PS>@<$^L_v#Ou; z#DsT@@a#|NLGq))YKHAK)Gvx2(v<`DW8PzYQf`Ib2#keWx+d)E%6bYepQi~K$BH8$ zJ_wpzjtSE}(O%-ATo>jFTtGY#RcJcW9V`BtjZPzpB%{#>N+apO9tiAPx6tFwM@Qn_ z?v~)O21=ucDXa=-OfBcQA{g3ur2)Km6GVT0)$G!f`mm_Ia!uBaD<^wyA}D1yPx&NZ z1^Mtn@+@8(+i-vKLO=0o{b1mqi8*`ZTC@E1xQ0SPY2Tr-`mkY>JlDQOs6=ml#G_B2 z)9ykC=olZwf&*mLvEEyN_*ZmzSEiB&=)wMUq4{8fRl@AcXMVcLnbt z1We12M~hugVG6-o=LB}Kv_%W=I4Gd3yXtUSIRn}e`+UhUWTnOj`vRR@iA^<9 zxh$>aax3wI)3>D8n z)=^{3_F(>E8Z3E=!778KAj`DvI7`gcEYORSQcZ9`;sREhBe9)J%{>8UPn=WbD4GDi z%A5#&ThdYmy$( z!CT9W;#~4sj%waYP^NicdK`)hC*;XZYRSv4SVY9+b~qr8i^$|$v>|iZxpiBs+FLwo~HneT~rlGJ{qRIm>m#?)o0CDaKXBW2&I8ws4FUF zdvKjf6QWyU$tA!RBr=4Y$<~r_Asl7OE?d^Ol0Mae)g3=P!a7uyP+?};>q@5QA?Qj{ z?dY^xlmvv!a!SQkfs%CgS3EqKy(~Hj6%(Rq7esQaszNg(X!LrD@%YecYtKRddI;rZlHyR+g$2OvoeI zhuGg09h4e`Ub%^zv(lC?N2BjR@vnxUd2>mQ*n-~08}2)aAsA_@d~Rl#Yu|bQQwQkdTG(gfb;nOG=h}1c0vk zVMj?+-OOGRC&Z-q@_S3f>EJ}fnOO^qdJ=uU7k2Tdf zPS4&a3Z%qH^|F1LD^R$lz>`f2K^Y%*0tbrfm6@iRCBo*{eL9hJTw3)CMM(N^^h*+r z>6j#2l5C7N;-lUSr;|d4!)g_;al&&pI%u52=bkCO^J}3dr1oYp7)=z763wr1M7JSr zy=h{7f7UsxSoaWdA#Z*e$uhXGGV`Kw$KTde4;Idhd)2K~!rWr}@2b zi*FmuRWEaDNYK$O$lRN0ZIK^rC8bj`sSc|@uLpJG2u)dIC*DmZ&Pvv`Bzb8h=s>BU z_u;Qnmtz13F*(2ojMR3Fuunb6#xPbz#b=|d%dU9$%oJZGz6&e3JfIR*RFTsQ395_a z9!Mi4W2&-N2wsXK1Bw(reYg&?hK))sNJ3PVc|pLFnoV=U;#wr7hg9QdaWvWj1(Gu8 zGg5-Sj7l5YjtV{2UX(`Vbw$U?(*>wSs`#4}DzAPU%Q11`M5I=MqLoH0+k)p}Tg$h- z=LN*6&Xjh2A`t|3zyXk;E#n8gFDxC zv6OAWE68C}@|T zVbGu1gM9nYNoyS_obY*1d|T3_MZT&opa!I&x`9s0rwX?EKsc!PVJ*q22!y@Sa=l0r z?-Q4DkxeOa!ZIGr5=*B!J%-9Qa7oWhZA5`lT*i1w&#H0dggvWr&#}Ja*4YVUx(Ax2 zAr4dynJgR@DcsYd2gC|mcayg}nv!I*L;nD)wfit1Hcu3nlu-va<@Lo>zR>qF-|WMW zG`V=Ng{a>5w2(`!FW$?4sAJ51QJmT$cP-66>dIP{(>@bW;u=a7^``*oQCSjil^W_9 z9?Um76y+TLY1~o3OBcy)q*8!BdTL~r(Id9oKdQY_nb6yiBDpT zH7;29B)z{S$q?O2OR3PWF|@dv6JCn06^7u(YLU9M5+|y8GU}9)AB3>1lob)PhodTd z6Hmg&ib=Zf3ysU7qM8TOnQ z(HsSmw4~7Wz%O)_k2HH|J8|R$Ej+{5>?a4QNSM~%ZeZ7^Ot1)@C{!gYsVfPlX97t> zLX07ipWck{y-_F@!M2lHyY&nSUI7Y~1$-u>zXB+Q&7v7E1KC<&DuYG0nme!+UaHDo z><3LSubWVi4O@A?(;sFH`^eoWR^9gfKeq^>^3jIlEo!c?3GBkLq>%prD&PdA#GO5r zz{tG3(MnuTo=I!>%6k16NjgrJC2cfREr}{8sn&z~u)A&=IHVnH;?$~%dj^BeYEo70 z&-7u>%|wzGvIJ=oxa0~cPo|wb1XmvUsFQypVZjuI6}p{Pnv?ug z!*WVG*-9kTR1WMIolugstw|k7>xF#8S}hi?lsHiENXmm&WYpK&hR$K8iFU1U2v&ro z8lJfF1P-!~r=Y@SJy)u*10m<&fTEKE~FK(v*oD@BQRDe2b@+>>ibPFz&}eh^;xrNy=C~EAraCJ}N@@F`WF13`@4$*-?wA8QS$Cr?6G)X$N`*0wy1OahX z$kPJn)f$UZqI-S#PN+>0WhjsZefUm9AqGWq@4;H?FBOl4H3EkL)ZxJtwyQ+yJwHws z>L}C;Q&L8KG2&OKhDtQ%Pr8^Iq-j;)O8iyvcAsVi5a_l_N)!&W?!dM5LP`@ll~#i~ zj0tH8GpJrjHOSztrB{g2WT{ol>c@gmHF~Ni;yL?p6+pGXAzEoQ__FT79))KK73JZO z%-7qGFD;TvNF%czURtQ?P*KQ^hXR+PQIB<>7-?!kv<`?lf$j$nyy6mWj1)iPQqF5o zC%%|Z&vQ|HfV5TF?6i@WWe4oXzG=ulTrEZ7VEy6s414CBz4Glrs_}6D0OXYlRQ1Qc zap<2W#2CBW6_J#@{kZqeN#d0Gb}bBrxB+#=EROO{68VSoD14BGLh1=8rWFMv(!3NE zkrb{yR10I_PlqBp90^`tja6%^sY+*q#8;dV=H$B-#+_xt(~T`94g*4*@aiH#s7AuP;=xguMhCgGt@6W*@Rw+TWC)q3ViP=Q8*+> zdAlo2x}rXHdXUzC3NqF8;&c}XwNOljuDLE8k`i57a@2J83XzDeqNY5wB4j*|fx?n{ znw;>wa-_Oe!d#Kj@3#v1s5%1A&Po3OjyU(4z0eSbT!jAsuTZbI4UDBK#)a>lk^cb6 z3Lj1b%_&ZhwE5TEp)ypeb=; zRpF^P3L?9L4NlM;t4w&g3jzgB4$rFvmE|hYs`jF#RQBP$Qlu@5i%lr90O%`)OvjQ; zLet4)Nq7-g2?OiFh0D+zW_TyWukQ96OC?ot{cgBV^o<8!b|wqIh61ie%G?yWvDqeD zkCT!{k?ki2#eX#pU-@JyYD3;cSC{+9Nz31hOsCydC}Q`*gr$As>sU3^qg7&`bMdR4 zufmSAstS_xWRxrb>MPgUFzt_`Z#x6Hr&K8jeIrV$lhEQ)8QP%cwmnIOx}Mc6w_23q z7XE6LKZ}ZiB12M?Q$tZ-*M<=q6#8`eocT<6Nv%{?e2AJ>^2=I!Ofd#^5Gfc@naFxHrozEY-gp{-MkNXcydQavh zZR7qLmMv8<;*x3-fv5Ch#}~q?*T2k0o&f6uGOnJ?DKV%_nkZCFxKfl$QKc)_?ZrbP zJ@s<1HDXuVvKJUQv@6DxERM?LaOxRLSSX2m0CtJ}bA2~158MbOYKJW=LVJiK7E8Ao zF{jX|_}Dz0<9#Zsur!R0M;VY?Jf~E784yoOew;a`sk6mG7!nOZ`Y^iB`Ay~YRr{*M?-;fEf&dL2|BAi=G6!t zrADKc1Fw=GjeE@`Q2v}2saheB=s76Ry!T4K+!I=(kIuB++@EhX?afP@&wb)T)|}6+uOkvDw$V2|tP!4OWHK1ClF%XbHd+ z;DIia)L>lUMeSC)&t_O5kgqkgK`G^SE9j;QUQxiR(7JmSL*8)OtIJ<8XQ{3a=9TI! zmsWdegzCLTk1*H8nK<$ls-$~>!NcgB7ih8{r)c|dXvs~egiO6OsjV=eUZT&qv z9R&!NnkIo&r)CXQPbCOswh7Lsuyn%SXrd8r6Z2#3ORoIAv$r<-+BYYVw-Oz2HjOAE zxzOjHSd+zA$$2M3IH+yIn2R3|ni$daKB?@=}NEz<`b@wXPHLkWp!=J@{35 zXHf6Qs=TqOJ^1n!?Md&)s=SIRbyAOB+Tju?h_u$ewWu9&=YSWGRaHsV?ifg+TR8Hv z(?Ca4g1OZ+PAX6?It>X{cr3K!m20SqsHjk&jK{kOH0YM4Q5PL`B++DQ?)G8BvgT-ZXWCN6kE82X;{@e$e{{R%{<>Nd;^X|Utd~YdwdTLQW zrwxmoc%>N|M*xW_+Ki1)=)qBt84lWG%ga(ys)B1yxCb2bdW`GEi@@Eg}PP}s*^x^;9mNrn%bi%bkv$ub|3qgDgulq zNdkaZzW@q1L0MFaiWAeI>4glyA_cF`IF%?B3U*fnu82)gDwIexrG40LMwMQvK&jHB zQVIHSQk+z+LeF3yU2u2n!FW|}hjQ^6j?5;}M5j_RnF<3e>yIxv)kcuv>Q0g=L*Iew zNR|Yu?Db1aLB#-DO)?bv@Z`AwjR28S2TI^lYkf)viqE`bz$AkL$yRkp&0T6RcCx7T z3vvQMAW-4YmP$f5YJvJNml8C8V!K#J?l8aEcQ;JZh!(Ps~YeZlWT8JbP2^~8yC{~({Ytp## zEkIk93Q!*w3Q0y<6JDbNDwXP|C4iI)P;}{m>MP#r@ijT%5Vmn%3v?8x!%73$fvG{( z78H;bPJ_PykqZg!$BI@o+fDjTdJvQz@R5cBQWK#aP~XfG^83t(!;t-$_nb-}BqWFA zy(Psu^7XAI-pd=xMRgeUqW&i|K zFO0R0crgYmlO6v6 zi)|%GtvfK~t*Rs4K~(#HiMcj5-JQFnY)h1VuWV3F#{9NIV=$sX0Yyp&w02>`ty~*a z+B?QM-8I!Q+iJef!hP6mofLhWd+z@LX4jchLC|M`qVjgzc;j);Kw2Utl=hV@57Aso zM_}RzPu#mU;kV!}RK=ixK%z-KxNmV}V--$p@_uWnMHD|Wv>g(z+_4KG(M&gr zONui8{x1Dn6P8E77a0_80 zu}Y!qN_JzyL`Sd?heY(#B~W3@E>bMFEHr4_1Gfmvd7@M014==8Yg&rWL)+}b zDJ3pW;%ErJmX7b!gH87hE<)Y|%q4xq<@zuy$O1sekxP(jSg1X|%y}kU zR*Q8Mq0YGSTm%lZlRzneD9AEWItt*Wc?L~0I3$Qd$)yc?VI+bLOG9~fp1ATFAiukR zy14T8s~B{JY>-H=Tokwj4hUkAr0E&p8tR}zU{EX&N`bCjqrV=i^1#)k1Chsy^4R+Y zeVFp{_B_13-Z3H(Y(BGvksh>hG)b>-Ot6caM0UEUxgd2k!77#LUJFPy)IPi?=QR(OR{E3tN9(}( ztzPoV6}J(n4O#Zr3btyZIkZMW2cfPNYpRoTYKKW0D_)pVsS?U97yxS2lbtDrAxTPx zaV07747&yvs!0zeV6Cd|O zUT|LX4`ITdVjh+4Wc3xn1E_PlFCfSY73zC1%%xePak8|;+wOnH&UgXsZ38p)oIh)+8e;E$Z%|24 zBe^zH5!v{hG(PU6{R0R?7$g9m+{@6yR&P_$F5ptasS+p+L_p4nK&M{XobaXWq0ZBi z40W+&dhsbN8gw{gRP|?CyYM|oD7~#+`1exv7mCkjUd(%`dW&SQuA}b2f`EmpmYk9g zyko?n0edv((*ePIL?KWDr}p6W2%}Y;h^WV^f~yLls%xGTdC@8ugD9Y_aEqT*A*)(@ za7v?et$ahQTp zXcXyCk%{YpKmb)s1F3OOU9S)2SdPITdZHAwOg_g(0%4N5>Z)Gc(maT3N5ZA$^ivNq zF6nBG#Wa}|P?ac}6P6$uA?jA`674F4f+{J6sH5JAvip$VIn_1sr?89&C`@%t-NLlF zImM*pLQWXnNz_!GjDG4XZvNFeJM;elal%miK^o_QPRqK=T#V4mLtvk$6EkR_?HCCu zJqxZ!=)z2W6S5M7b*R%VMLjU!CQ^h}>hG4pDil(P(3~eX1bbmplC3W4n$c11!%JiD zC-*{(LrXv2wRUXVcZxor;csWXvN%nNW z5`e_EL!gpD>Ga?bbWsbfUHCOh7PSh8dnwnZ2CL4Fi(v5hN6>NPS2YGoke;;)?7$rt zkcnB+0X6pYz@RNcC1mB;G3qZPEF8+Z!E9n;~}roAv~r2Mt8dc93MuoX(Vv{>^2>r;a@)IzwhK;Ux3eCiDGS1&0I!R;9&>z9hU&aX9ZnPSiY-=^ z$en#U<&SwzLb0Jey?bz=8YIh>6o3IEEDAehTInh3Ywg2j;8vPPvmQ%z73yGDn(D0S zr40ZFp~Hj$%LD>C*1bj>qoERlk}{weQ_Ehe+?r-^n)1+8sU#<_L4ZQ6r=?&Q&D%;l zhKP_QHlg4B-Ae;6YH@h&@uoy9=e%tH0NI>JB&+J;7vcQ%xL8Ovel!P^NAWFy5BuCN zz6CGbn|8uN{5uMOT3Dg*;AZxr42SMC&Iy~>Y5t;pkRLuxcj9TWnlZkDMhy5-fd5{VVLtR0;gX@NeV(zI;g2A zAP;H7gN>YVYDe+AQ^Dif5{q`LzLK$ReuFxOobCSrCkGhm$L^#*8MOoA2ytn>Dw=H) zmF|w|{Rzjv3ZL$uVlU&?V~fYNY^x^XHPdF6pZ;Zg`nWIQH$Qay5}(|bKYV*oU(4H$ zzx?Ty@A+5%0IQFF6La@Zv3vWL%kPhBDs6XU*Zz%@mr>+9m-;wtaaTWdr-M6XPmFs| zMjgm?x5yv(6_3&|AB9}~(!KcYpWhzUp^Z4?VZEPcmcQALeid`~N*~7UKKS;nepY8s zlXw3B7Qfkn{4V3}mG8#we)#sR2v;Sa4ZZX1@|XKD@4~J=>0bP9*YA&N*jldZG&acw zoy@{{VEM{9@1V1YhOu8td{;SG&qz z?7|<1PxnfD_{E>z2(ygKNuh%GwE0edR|tL`H{B@1FKPFOwGvYf6%-cLtF<&BkD%cR zVR$s6T05xTD$`VLyKJ?BLqt=s;DX)BX3o)7zu_};SCgN=3VfMH7fq^8l^#O^jv15EmQ)V3P>s%mGxu6z6c!Z1%T23pcpqwCs{$MisY4PPj_4e5xT;_ zjyqLcdW+p*3G(4xwWd8q?=UR`AS)r4egcR*#9<8QsGoKg5rmacp`bdN*B%kCRc)ai z)xwlYd5FLWB7kHtBm$GELI#kdmO~1)fTB6{LisI!5883>EdyUwfgJMylD? zLI5cloYZ~T6g5Q4wCunY>a7`A7Q3@0zsOWLx zSCRIQQA*dh9t9QL5wxo;{kT#jgOlpZ8HKm4cJG}D0;e-ipkm#OwP_lvPFE^e>H?lo zZFac{6|n@XI`~NGiLs7{!9|?Ro+(+qy=-1<+umRui8VNL_;el!zl=-bg&RuL;UoE0 zKu`2g=^thX@W^}-otKnzSUV=bMOU}yHDBnxm-;6Peg_wVV%^6mJWvYQJ5AaW+@=-S zrD`8xNWn(jh>i*H+|u(Xvbf)A{_V;`UFy+XH~!j-!5{mQR1@5Bwloqi&m{KhYytW( z+FiK~I43RKQh1~(Tz6(VzFTpolp0c|{{UVi9kvH0D<0$P%4x4 zVjg@9j5;YOb5WGtdc!@XEO{EprARsbSb~z(%`_@ew@q-UL>F(GgHM!E{{SZm{JWCP zoD^Mg-%0T>q@Q*@u5l;LfmH}Ypnf(|)K}otsw~7cl*cmG?alui8fnHGt zl+Kv(LWCp^xEe1kC0eix%VKCUO;3Gs<>bL{w-G8$8A}ouLfmz%9?*qSp88RV^XB)^2iie_jLQUWwm-fWI5embXWC+>Eypgyq(57I{yH>uafNV+jjFRkvzqm zXDn8&AW2iDzz!24QxXbT9BDPFtDKFYCN)lM_Tq_)4OHw z(z+-MZua$(3q9fKQ6qd7x6+a~R)b8(%a7ZI{{Y()_mZ;rjPhbQ*8xa*VGVAezVq_k zKhhWh+Zyn4ME9}7ixQdi-E!2Vr*|}kb_GOTKCrAecWYnBqDS9`*H$E@dUM?9bu9ir z4aXEtgQd|@avhmh4UcOi@l$d4y4Z@ABa?kQawgoAsLxA)i3KTD8I+))l?_8bFRl}J zY_1v2JA=KL`8WiJapG=FS{-H7uHe&@1)s#VHoA1G6rc(2sKOKMTd+J-gk8_Ymsf#J z2RN&dSSFiYbWT7e=9N%A#}S8P-gqg0_b(XjI)FxF@wX%EN0YVQIrQ@yAEj7K?90}j z0$g_+6z|?rHLGKDAc|FL+t0j)RuJ+~{BooaIvq>Igj;I!kWtrr*B0N1q`mFOT*|Gj zcf8*=tL)gVEUHb;S$-OWZ8SA0N=h6mp!N(Qm)o~YfVtcPQwzQ^lFk_!)T(~ul(ZRK1AzN?|rRnGirF*bZ#))!0(tH%*YT>QNa)lCO zbD}oXw$S51mkPA4I=d5#tQ$&cSRGy}gFgC#84GDu8eOfA*)G#0C+0VKen;s%a)-20NwB0HS{|z%(lcy%VTc2}uLl@dBK-_9t*52$fcv zO)KugF9j$XJX4C-cSXC*^KG#%t*<9olGs@K2%#TF9zl2I>P1rvtaq9#T2VV=-?*Ys z(|hCjkX@w}D%W&cLH35Ep-CTJC8EpPxKYhjsc|>$HEZkiCk#7Jr`wsUm)*}`$dHF) z!IFTk?MO{<zaEJ;U#B_p(_N#@{XR~kAgnjHlJt%s~!q_2KX)hWMn1{>xDT- zZ`F4Vr0cAV#Hen^oY5WN$6Alsh^hA7%x8r{oVMMzlJlYTrerO>GDXe1Y?1g=jwmHg z$x50_b?sK52m=T~X70Y1Xr!jdv_@Yayi~atfTPbxn0YeN+QFiT)THOzP;qLv+VIHV zjc1pgz`BXV7)n%)>2_Vo#+kPARibmQ8U|Wz<*({VIDZFk{NqMCswVfl!r8TwGCrU1 zQCBVSzVzX_7K6CM676@w;%#@R$|_~Ww2-ABg@9738q*bx&9yFZ&jI5@&0Mxua4fTs zlMlq+Aw&svL<6TIip*^)^N1dG)JP+r?mU<1W_i#rmF+pfJvZ~s+=X( zlmU1WIvgmxwKzrmP^=nsJ-90JD&m!;L$9+QPP!2_Jb_YFJ-7`~#7jiUX#5M*eyn)) zcq6@o#SQZ=caN-N)PI6crM@U*Ak!m5-PeG3_Aho9i>r1UX^nF+l zF!&(yTDCF(q3_3xwBi@MhOZ^!yos;TOaq-&;|&($1SlSdZ3Okoxbj4zvL9+Y$z|tK zg)jRsNCjTu+lLN`#0n#^y<9mV(VYr&z|jnfeBpiwm(24e!->0~ZMhKCfMZKbi+R+q z_nS%zY6UaJt8ML{#2?et0?qfn{{R?Pbr{RXqy7b4$o~MABnb_nbGeDpAz&3Y#i=?6 zxVY)AG}!wiUN2aLpPII?`>^ThB`U`Ju6EI?Ugj%-Uv8UZVgC7N4rAP_0^pG^p1R_^q{jb33A7_uIsH zDLm%y#^dbSbu@rOC&KM$eyXQk$J0MPS>eo`aN`?`F`*%DNO%~o1SPOkqek9QG#!9q za_3=W;%8HsXsWh-+1vLr21h$fk1c;0^Jdgr_3(kb$G;$Y;&wVlE6bl;F-;W?()_d{fD=AQ}Mxgb_m#DC% zRMNE_nDX%pTAuuQ0@n@Emx_gRI2Slocm<}RTGF)Xf>j!cPU|JM;o9QYB)9&Om0T|1 z=QIF|YQfavpL26aC-A zedFx^02R7@D|0$}*&~tN^GN-e_xp2y_fQ`1;lAp`uTNrRv`z>8y1)CY-|prg@1JM*pwa1joSsliTQ$X= z!R9h+?(tz4>`U74OSif>kH7UJU-eJ*20a$+7L*I6=3|Lr8k7@Lq_Eg%eOZZd_)CU%y+jPY$_V( z4@ClwdDDSzoE^=ZhlJnVmfiO@#VQn7`_9;xnzqeU=9+aXZ7-E5B_-V|%=9L>uifqQ z1;>-kUa*_W3+oS?U?G$yhmM;1KES2*`U>UrQ_Z~D2<<7B@~L0^5XGszslRZjqRQp` z$6;e}Z5>hQAGHLJL0qt+bZ#3k1HIWl`Mewbq{w;_AN|Z>d9~kCTONk@?%Ihjb6&Sy zpX4kJu4J}bGF0M_!b^k#qy+YM;d#4fb88uu=Rl8X-~4_?L2naX)a^Mc!W5a;XJ%>p zkg}prN(}K+k{xQbNZwl*uN5F|w5{byn6C;{svvu?^2#*{k$qB5?3Jhl75HnPzYVY7 zs*uC7fiYY7u@cXTPn6;rHiOoqrX@6@j+2T`kms5eQK*VjvkczarP^qklae)oOsW1H zG?|J@(aURUbw1ihX-)>z)!L%opLH+ZoF6{4_9ZDE>I_L-KBYxjGKh*?O#!YOrHD&* z>a7(5fmPIS?VJ-UjwuUqJ4G_s_>xJl*@m}=MDrfqDv8k!Z!bcIQAv>hWl6;$?OPRA2H+x)nNTvS#|KWU7JTwORaCXfq{D*Clb7vDvG ztM+!*+#Az#@P*FPwYH>ZChjZKlI;b!k(mKbElF--maR3+Au2x1C4bxemAgyc!Wt47 zp7HH`h0AU2uGslpXc_~mPtwpmq$4~b=^K~5L)x!*-GJ4i9j9-+h}Ri+dy%(=JhuG6 zmOy3Fx(=r*dvN0cu&=UAE{tds4e#D8F5P=sc;^Q=R0e=_^y(ZCkaf7$$(p=amwU$s8;pY)IX$s=?AS>B*FbQUMyZ(Crn`+=sLjdgYRRHYgU zAjd%?y!K$20PF$;7Q|W%mN#J8$P( z15O&QkdMhP)Ks{3+l(Go88#Nq(7Kufk1jY{#yQ|cH|5E=??8n zf;Q|~6D|$K+(~(5h|QN)TLL&LG#LXyUZi0wzTMu$!tyj8l`)R*-U{aALf1d3+6uIB z{{X2`ZK+|oZ*9k7+M9MMmwDW7rJGD*u#YinL0G9*K|z_tGS3?=#Ns9oc&%qIm5#>M z_Y&yFar+gJU!vbo0O?lIY$$ikW}jv*U+s=h?yRVH?{EF{>{<^+zMwmHn;>`m<5&;& z%O&8A{{7%S`SvWU(WgD5Kjn6mJAb{kgMYR;KLi86dw=hrVsULBL7t;G-QqUf+nZ|L zGjL9EEnDrfFHoU*&MzdP3zSfjqu48+4>xSD*v_IjClL3$2P3E+N74Q%*SbF~Us)Rl zaZ+mPr z1Lp^%^8%`9N1>i&eL$+=WsguYGTv1?`ZJ3^`)hmXS>OKvaX3-KsQm->rFg!8Ic@bO zyQad{uJxrD zxIdP^fmkyUlt-OtD5^=NQ!jRE~(a!wt=GNVV5 zgp@ZW2yw8WE2U`)6{UL)D^X3>(BZ&}tv*9sEmdfE1g7Ut@TqQnC%!8r@P{V z^AZUm2QS^}f|7g+RCEQOFoBf^(~mkgo=Fn+c&{PtS*Q&Cz1UC4e}PJQ(bH0fOU<0B z0T>^blfa?#;_*|Lr#-_HZgTd?yv}tD!D3lU(iWtI4>`3k2~o%js8r(7vTp^o_%dh} zH@OxD_Y(Ny)TdXgOT2r}JZ=e)rKWQUivgdQZD?^CkjYU1qN6QT!@cKx$*+rt*xo2YfJ(qeiy(VF*scPu1Zt9>aY5ju8fYK`?ZORry;SGRtZTQPi1-(SdApA1 z9oM%Tox3)&n>Td@tqO8HfP9w#2fZaH-Hx&Ch65RYYy_RMjO{K#AjrksFuk@KU7KdgOAlw9-2 zUaCDlyl>lr+XHiKlqi+?`@XDk&QcC8b766v4d_qY~DG*5k2*7MKy?8{poE%g6sXHAX?}gB+(q#aMHe|-VlueP#l+?76PeY|YvmW^fe&|H}T7SGyjmK%q)r1f_jR^gi_u=sU(dvKkDH&Pj{Bs{a6o?hrll!wCDGV5pF;f z*9D_q_R;$e6Z|$W0KNFM{{VRVR;@8?d3t{ejTNq#9KA;<=WQw(aWa6jr^rjcxP# zM25b#LAeKa+qcY^E=07kZ%w?cCDb;C*63QgD?k;dGHlK4Q#zngbOzVLOJvFyRWid& zp(#oQDb!*TtyZTzltOY+hRV{Slb$?W)M&wRqHSCuhOCZUW;_)x=HNxH2+cvJ>Q?Hz zf#?Py>(;jF3bYt`&0=?w7UAmFxnRw8SE#p1(rbeS<(+5zI2GmXN+yFo>;XEULjyxmfkK4}cQaGr$B--n zvU>eE^;eOSwNRx;r)CcDN;_OAkK0;Ahs$r1r9y+=aI6DeQz>2Glohw*DcUL3B%Ylq zg}z)ShV^cCv_n*vm0rZ-!qU7EzY!}R$*4I8R1?|Z!rz7zF3JxS5!ovJ*+=P4JTWdD z5l(H0oV}eUEN5gUP)H3=pxCpKPc0i@kUlKwTnjj)iZk;C zC$RpkEZ0z^Tq0D|T^zHJO&gY+L)A{^@HyFvD2gktHYa7MS_dViFQhogoP zw-fP1>Z4v{V`C~*w>DoU?d741kDSuhkWyBZ9Y{Y36f_{>NI@~+tHU{Hd7v2GyTFs? z$?4ZrL@51bCjs)4_#-CS{G(JxbfNHJA}(9WNHZs7{R5In zGj{{VS3 zZv3IRUrLh87WB5DPEZ&G{U8iR-d$Wf_Y|G97L6YofHxh=b;9c|Ot%sxyQI97BsMFg z0g^zbSfbs>A$PnrSB!Mb^2bi0Hp-qrD@)52{*k5_5LB}`qW)}xk`(%tUv3-GdWjuJ zLaXtAOTP&@l}=-oWs8Hh?Qb_PkofE*+Bn8U}(A933k`R)u1lP0*^!5xOVkd$-CTjsDMhGKH zLFh3G0-lvE&YdX@!;ajQw$fQmTS|x-=Y^hVo5U)TOoz|<_4_<({kTSn-x5QOQTk_w`%i4Fp#{U2rkz)~- ztG}4}eUQQir4^(&uLz}1M;1-Qa?Z9QQcG#7sBI?gwqCT`28Y*53MyLaTUvED(z#Qn zF%mUhFdw4m*QF>wE$PpbCg>^o{mUIFbFld^n*@CtvAp)u9-DU`61hjcQ}tz{9u56v zEF=^zfb`4`>}b(wO3vnffGc0~b9W!2edliS!qc(0<(A~P02M=b3v%t5&}ULGg~h{! zqN*~|ASN~R{ee_C>Fz!CrW~==QZfn@`q?(6-nT`D(dPLnxl4}2tI`ytrPo3U0QDdm z;dt9s8FFioDD!O~BAa-J`BzIX?`|#f&6X1m9xQ|-+XOkyNaHXc~^8%{1XQ?_sr?i|#?*|sxIG+UP zx1q1gM%IpDajAzq#3cI$Icd-LmvOVx4LesL&_o@ydhG+ z>a9x2Irq~On|{OQA5+Od?u@_7!|w4a@mn-UM0S}44KkFarp7@j10hUocA&C8o=cza z34GP@DvNJvN`HB7hbvL@uL1NLV;9ZH9dKy6A8*IgXx~z8pprefh^m}hW5EKloK3&^ zA8c*ucD<&{lI+WpwLc&5?P^~12R96~a+Pb)8Uf`%%7Wl^5O+99JOTBbb z4by+`d%7un*sMyRs@A=_>PYtx(8YpR&nWkq^(tMpjig81m+MjeqRlUS-j`^z>}HoW zgsItVGX>D%83u|{l9bgWKD4GALyGg?PL(HrmH9}zyw9yjE8z5{5ayb{%X2iR;R@L(5Xk85{a-D2V} zJgBT}mZP$xRX(=s*V;}r9DdGsFtQxib>TIm)ri_We-^?L?Y_vwNhc!uS{luMj9+nk za7CyK3*3A8*M^;1DE|P}uN;g20H@98ce^DlO4v-Q^JsjKG`PVCS^(`XB}Fv$;Xdvj>=1~I6_-(Ae9v;&;ji@JTVu%G$PzwGA0*0KogsD?a{U* zMY2gp{W8NTa`XxQ#n(VuTBmwaMSZ6avQ}+?bdEgKE)yi{pk)2Rbp5HYw#G-O7>=Tt zssyC_@Ioq#9JQe}H0gjXsug5?81ZeFnLSEuU5mM(ekr#eYT-8RY$Sxf+&HROLa0HZ zSxKj}7d?ZAj_)AWJXNbX?+)Uh6}P3H6Gw^wd5ZXZ7i_OSgMBhv{$}-k+BW+&zCN|M zvf`OV6P*G|y7mAu(y#6AGPURnf^B=*x*RAbdaApVi}+cqoP=#XhYBs7wNfctDN@#u zDX8le&ky3+*w&3B3VzRi=N>C8&L`GWY;k`c3jYA^d;wmxj@R{Z@9kW7^um+=_sahO zRo>FIR~B)~zCD?5UuJRJ{Xk(K?JRl!08AynzVgF_uJ)8c-X7xT3bkyQ0G{SY>cURh znDhIAId5_E1N~QfO0OQI_{(GM4nB!}+nafdP0R5H8B~UXl2p>rqM$`c&X~(PleaBx zAP$MNTVH3lef~!1vUisNIjS_;v#(uP!O1J6h(;-aMOrATxhA8&xDbrewQjy8Qi(kV zJWv&5y)SWw-sOG&0CThMwj7gYw_BW&S!w04OQF;tRZY{%X>JwMip+j6t%{{TozAE8+H`(cdc)FZxG z{{X%{gZ3d&==0ONlnUF^ng^*x(qK^f)rC)Mv2PqGnq2q$aYl4-Km0BIi=3SG#c=q` zlr9@*(5l`@_xakiXJl9jlxii!q=_G6y6rr%|a*<&~^x$U;{IecShkbC^o zYj1J2!*ETv+uGe$8%wu05Z}#QE@+CwMMjlzWhx0DMj|Y5hC{?^taq8##Zp>mJOx)p z_9qWncX{9K!KzlLLdsAoH$7CBLLy25z zzxU1T*<&@U-573yhrw5VVf4=m3lHwTDrwbQ zdUp599%Z=G!D~Ib?=iWg7o-RBcL05Vx7*^Ye$d<#Y!?yE};(BxC=XnE)ZP%;J#-&~2Nq!v15-6kPP@zXpQc`m` z>%EhcebtzA_^+K^+}g~>XW5vC$G|QXI#b@XqwB2n7Xx8 z&xfu_Z`J<*tLutLDUjaWW6iEr`9zX3{{R=JGEIoa9L&XauXs=Y0ND8U9w)EuR>)l3 zeJ{m$YD38>S}Jo&fl2!gHT&p9u(`g4$@qtSz0)Kdim85yrj4H?)CUV)bdk3=M4N@W zI*=3<5@TrzG$Sx*pJp=d+IdkvTW62nT@Cr6h4*&WIVedTQ25?K)KZ z-o}p-<0)lwE|&KkfclnlsrhmePrW$0WjlR}x+InWeHtutZ+FLYV`B_#rR*G?uiCjw z%DiE2vvZx>iuMza%7r554&S!S=MtxjlMv7eacD|G0d5@W)2=yhIe=_=BLsXaK)!

_S`;#&N~l4_B52KbjZlERl5|mBoiMd@UZV9+O5q?%@(`MixnRZR6a}bQ zPeFviN;RacYB=z6A1o}T3w2awva&j)XM?qbuM!6o@UdPuJ(;H&wn{`s-y_T}vdgs7 z{3X<=j=h+946-n2R6XPn%6;uqy3@V)g|_o*yGY!ROI@kNt~4;eEs6tDl7{FgdvP^k zk(MbzF5sYYSSwlKG4ugmd`j!zq}}mi^#C^&l3v>UD)qJE#*;2XZUV#Tebq7wQ}HH* zbU5f8qni1lsMIc4?l#jnmWP8@xY1okcyi6tGz!P?Kjpv7 z{-d34;m12AHjR|arMs^rUYsy&(b9?)skOK@zETfHl0|YPVu89lhh=1X8hW4LHCk5D z?fv!W>wmc)iPQc+rmLb>AIgu?rp2;6tKTeMc_)!WbT$qlChRcPCs$GEn%IQG56uvc(QN~I3Si7BUm1mvwq6~t|>a(J0XvZ~3I*tS

cjs4@6b54L?6q&%`~sDrpF=rw~4!Umf+hqm<%=m9YEs0tK?;|B1&BMh(di?WoEbp#qyvk^H5Ra$Hz4Qfh+HMlA?*1u*WWB8M~ zlC+039UYlr!F3V{j_9zjT(}P>Y zlD&3|)1C7r$Ws0yv-J$HS$-sFMu;luO4K@bVM0+3r8#YQ#DCzzr&cC<} zqx>n#n2EX~s$(ACb#G)*GEv?ug)9fYiICNZdH(?O$Ni-JqX)VEVP3TUP)}{J@%c?B zu||j8i7P1rh{`ghI_sFks)K82kZ!`Y&Y)BS@53vyr!i-wCcIU7QrJ;H3X@WB!ZM_8 zg0+5^_ky_h)W;2OWGu58%i9*~nXLk%-IAyUDm~hsNj~gcHm&2WvN5=Lth>5cage}V zL`6?QBi1$h0bfF1yVQ2?p}$afW)>6w06@JNY09fFyxRnK=c^qjzORSjj$b{$E?9Rv zdE0}@6!_GqbR^~6l!1mlzlY1Q1aT_g`G&n08;#(a zh5!|kZhh&mfFhUKL$$r~TT z%`L;UO;Qp<2uS!z81NKHK2SX33iKfj>U6PTfmCrxIc|s&&pr zf}kL|4{S)=n|+DDJhd&=XwX)=sYkk=%qKK_D4%WH{{S)m_XS_4+AkLFJLV179oJ9E z?TfOUaVk{+Qy&V2b4l&RdvZnp0Almbi&@#XP)VAGFY^y=S2z6zac$p`y-QfGR;Z>$ zw~t}JcD&3=bu`bIgAzC-WDAN~wEqB#d$EP~^A_ji`p*W{b{Bs4BP=$v*i9WN4IY|m zeVTh$(23*upwuvs55#)c-Hw)?<)M7nwVcstc=`VTFl$gk*ZZ9YUD%KLl{U)qN^NlT z$wj{j30KA8?CaTt-;qkgZsKT~JMYuI={Bi1t7Jzd2nh3(kyNgYjY?3Z4O2>wb`lvu zAn82RoHo)l^$ts*K9T)KY!6nu7TxH-)qUkQz4ZyV8uaVxB2h=prdYq&aemkP2< z3YAAvC`fQ6O3OD+BI_4%^VXo2pNYPY5VdWqcWIfn`8$my{nvQ^0FT@4R7=#C6mRYT zOVhnH5VuRJg7m?HF_lr9+{rO%w>mi?{Rk*&A^re$NG)?i>xy zI^e}EML2qWOPSuPcqZA;+jc$9&|87Yn{Agrmu|}NH#vdhFl8uIloOk4>VhvGeRcFn)*s4%OU=LkAwED4rF)T7U7gj$nk6#uaO*<=+_;Z$c>Td4i?f(EJ z$&j}Nt;U47OM2cd@38w4N<)pU2?Z-kstF>U4l*6(+uMulhRYMFP9Uw{w*A(&#acev zW>mXSM=xLP`ZT7zcjRx&pYqT%=R3xcx9y6~w^nV|>6h1?3@}Yl+C#~5fdJ4{lR<*n z_O3Rt1#!6i$z+`Bj+5!=ZGN2;4LkDX^m?SYnES7H+g1Lp3N~qUCzoz4^V#&EjKsCdtUIW2A5?2v9 z(Vm_(U8v(84B!jz9qj$$#~i@8ru_muk2*+w;-x4Mtw~bIuApm-}J(#JGvW*=l=ju{{U%I zmlJRwrk(M1HVgM3+BWRTY13@EH4UUT9PA1JcAT)C=H17{-k})wBHQm=p{ht*hkJ7nW>ed`)u*6a zF6Omdxuer80Hw4SCwXx+R4S=XYI0SrDJL;Xxf~3}XwSl~UhY?E;}yBI8Xh1oseOX{ z&OPpKFE)B*;K>g^Lyy?*BY~))9Ma*AbL7qi-uAui$J?e>*W5O+^FKDi)Rey+H_nrw&96`xr!ZtO{RQ=%*>VAw5vj&&garfJ$v} zXD_>(9C>tBzlM=kaM}jj>v(o=eur>N4MJGvIeC( z`_ddu5|Aor3HD*6+n&*;fYhf^;ja8BJZeAV^(xJMw6Scy-R$=J#68Ov(Ix1p+&1?V z^}c74y#?6rsVUakKMJXyCK)G~z~CuVl^q+4T#B~|Bn@iu9-@aV4@pMzLNa2u$R#St zHP79Gkd%%@P#sFop}}aJ)F(}<15BhXV^fZ1m0{CiRV=mZt3aFy)U>3N<)nVxD=)&J z;gpw(_^9o<%tyaZX>NdgWnRq1D~fC~v`Ffez1>+z`vw;HAzgOSaYK=3p}CgXV7EFl zk`$L4RY3(Ot{hEo1k50+^^L{loT1IA)pDnhHrsnskoQjAw^#7{bhZ8-qu9%!Je23$ z6-;w>;I$ClmW~x&%e3!J);*Fti5jGI#dU3!sE=UJv)_o?DXC2od$E}D6}PUPmCFg3 z(OD~j(I8KY70mpy3Ploi6Hj&%mLbxe#dPYZ_3@=dcN2(rshIv0Up~w?0;xm-x~l{{ zQGrg;1D6BwDh;zVlou`mxxOYAtg&%yOI(KC)hBwFg{07f~6;MJ0W3v%>Yg| z_I~YhLk%~!t_yYYQp-OlAT3HAKuJI(k9H+a+`g?kPqA0uyD`GQ#BQFE`%*>9-*d7` zR6C?6ZSvFAhnp>|c63m~Y3wC{(c*x8fEp>#FJ{qL)o2uU6YoSfVu=9#hODdxvm3T>@=~I>>Oil{O zCSl-Fbb_KmIrsKpI=oWeq;e}ntR*gKQjfR{3l6Egj4IOc2N$!v?Ne`8NegY5(n}3C zf-1cU1e%|IHC>;#W4n*3%MR`y^^(GI8x-bvpJQ8+f~#lEkKS9VTC=D0NX6i7TUK&e zqbMa9*?A%ecZfJGZHuEJB{vkzE^x^b7cG!*z3njj;;dKkjF(Zz4JasLHcFbih z6xUR&5%15p2Yw3Ja!0q_H;xtRDAku~aMLW_n%vxC&Hbs+-)aJznJOcZ2e`RbS?OcTW+X!skIan^pTEk$j8$u z3$kn^>x4Vdu$K?-8jMHLJ1G1FG`X?E;4PInk2xJGokFAP!toBLP^4z4VWJ-K4m=C2 z=<$=KhSozVIngB4U~XGSQ4OTEWl<*bB}bs`!5hUWR$M@$lnn{!FqkL;`}O|-vd8Nm zhAdu!_iv}}OysL49`BAfrcj?H?y!nZwhwC)wLenhCKmf{NAOVV9wMLiUg?5hl} z*t#teM$+cGr%y?(KC~xy*xOIhE5LV1)1I1Z;lX#~o;_mvPun{ua@#i#Iqa*07n^jr z{{VoyxGit4cb>|cETyKNQkGY$nrWSJ>@M29G6T>>6i(#cnAgKV`hvNwY2IFmFMLaE z*c>Kj@wad9O@VwmBCvsk``+9ofIN3xs+ar067D|9p#*hfANFbbOxLj?+ zt;mtYmvHnd71m9+SHBe_ZyhUIs@du<)E3$Gak}=$D((LOPSy`iP;Hh-_O)^T+>`S* z_g5X&x$`A|DGsVS%17PTNEIg<#?sr4wzvKxi_fKEd($q{c;?6Oj&z~>Reno|(w31N z1dgDAoiS_qc>EPV&wq(ZEr_3mZU)R?7V9k)R+R9%2*->Jq0xE^xmzgJM$Y+Z2?dt) zl4yLi*Ysi*XjpiKc#8605{s!Zm+>&!{{X?K_F*5FkHIbTvv`%{H4Ga04WtgwEkCmX z^6~x9fZokdh^U{K<3oFP#FD731<1F?1cW4N(WqyZXBn2^wv0`nTRz9gOzT}rh(8Q~ z?E;~5TH@-FXW`f?BX5YDhH) z3{uH&Z0@V}&ANUwwx|9Xa|lytlvs@-iCrt;mfNW88R@PZ+g?5gbvKGe>lYQvdSu|c zIr(&QUhC#Pt0{rw7`3wK)}*fl7R|sUnrBIO?9s^3N%2(^)SFhexkGiL8*PNP@09P95*zayZ73p$ z%Akz#n)ezQ8Gi1L3w!Lf)x+@YGS{e%YPI}}a%3?TmH~2F(9!@%_?%Ec1Kv(3c*|x@ zaL{~KshhuiR?cwnQfH`l?SIcU{i|jT{ujrVxHh6&Nf76Ga=uDE>QhY1m~sp;U2d+u;R!VM;(u=2i;K3j@KHOjB5dp*Ak=}P z>p|YT2FD6Dz7*!$g>7vy;4*+*($h%|r{NUnG1qoEOM6(F#*Pc;J^tOuUtGFZ{_#y6 z+sGSQ>$5jam5Ht;S9^Sz-g#lrl!sd>si^6m8txp_QrbDbg)_AFrQM!Ik{xJ5=0vMA zP|Iy8@}|(Fhe~NunvGQU(zM4p$zabjUJJBNN>gq~<3-;+;l_MLwS5uo9zgWq!>{o( zyN!!Z=($bvm2!J=NVj~_-E~ij=^;8&2ZVNIOm4ex%7PHa4GQC&?bY?xNzz;zxM@mK zfGQ;0rAnx2?p7WJ-^CTAcFGb5@qk zPj(sJ4|o)ntQ~Uu6>5s~k0>ak#&cI@9yL9kqp3^#Cye<+b6l-zvTYfS!+FM6{KF|t z+o3SoP(E>!F-^7-k)1)Wb{An$^0BUI)jA_@8w>ZF-pt;TK6`5>ORd>A%v6V_TVyt( zk2y5|0G!5I9yA>3;z_8%x^+yQXww}>6khYTFMD=N5v2z1P-djLXWS7L=fa-HV~n_* zjeAmjN$5rwXevGKXf#FcGf^TzlY5Z(lAw@ex|O)41SBCKl^W!b0K$R5B)qb;Wve`o z?Cz~Ml}@^fVS1u!jH25eWF@<#b(2~Rbi!U@1YwI5ha66jm%Fs68sQi4MUC$22?E~= z&uX7_{$;R1@#|Cy z{`Z}^FYi{VT-hXhywwb0`4{y}?^@_gQ)vTzNjwr2mKiwco|8h2B-8?Ns({S#aM%8v{l;}gD4o7&3cciF&IClo%m4bz-l=~*mL)0Fe z5V=!qI_Y`+yCUA*N0^ozOUX?$@Rvx$q#Kme-fFL0?1~@ZxpPC*jl$o5=6%(4wJFhV zl$c41hkyo<{VQQ;GTBbu!O>QA4>%>kqwRl#G z{{ZEcZX%KxLv+co0Q!bCsD1#k`H#MRYINWi3|;3k=$L|M%ffM_p8J*cUy>w{=g*z7BJk2JD;V!8a@1-$?_kTMll-O&> zit8WCM%1@$#FsrfuuvaK)9k9{vkB7D0s2lk)}9NnX5fU>tyKn{hXYjItIKsD65{lP z=1+D#LhS-wi=x~+!Pfp2`Hm7aAp9dUpG{i_ zw_9UoZl?IDpj_{~_pF(0i*dGR-LJgi_Ij5;FITDihVym5T;B8UH;FGwn(up$thj!G zS^?dRs`BT}l)J>O=O1FrXd4@N0YpZJO-EDI8RtWWF!hMF1Llg3B0N*p!7YMW=fF(08&R()9J^O%{4(`x;ouC zkPa4Ws)1I2SPvBOZMCj}+p`t=8d`=ij_*i*SbEn&_63!_aqtVAaez=NJw+*wb8D)) z$>kX(RmzmXkt%{(4GPK6y8;k`wM8pJXvAbjpsM1ER=ZZt^@+Ur?#%k6+xY(5W4F5U>8Wy;1Wb6UnOEGz07^h`5v~wE#K;LDLkAd8kDe-2Mx(n|&_3wYIq` z)PD|xQ|%Fvj=aK$I?ZZtEmlASH)k+i5T@QXUjn}Z6_k6?y-Wt=9TQ0-y*^R#? zcu$r^1BVv>0Eg%m!Qqcndl!}=9hT(LbIbl0K>{bl1%x~^8$M+p&gzQaiht;acng61$}Fq;O!3Nk%7#&d2fvhsrLcHw@*`BQ2*f zWXGE_5q^YAha90NQ52$-5_3J6#&4v@zbogp;I_40Pq*2cw%YEod=3&2M+3l`{+_jS zRn8nwEyX$h(roKWszEcE6{)Xkv9B|CV&2L+K)Gt`wtIoE_h5Z}MNto06SyODZ1#6< z*S*^A9F-buW?N0Y=F8F{E~#|98p%NbR1=s!{9^sdpBXHDaFAc1_Tg@prp{}E)xz2c@|Ijd?Kypz zP2)s%w5>yxAOV&WM(A7XO+GHYnD(eT5G86f?5Actjsn>#C*nTrc^{=I#Gd?CoN1Rz z66poX0aHPicySrgG?jprr_MZjxT1T`ct@!Rk_r(nIHhK@PTVXdLFI+hKS6%0{XY6= zweQ)StF$)`;oe)8L`Lk>?I?!iMy+w%)TW$FnGByWx=99vQkd3uUh9I*r>}`vUv;~4 zdz=F8}WqLaA)0DB?w4wM*{vWoU*nTD9Z4&Yh5(xqc=>?UF% zTD1Kt`g%j}{+qQ858g7|;Z_QSPvpRDF2Tz3V*l}Y1|OK{SG zdb}tfyK>|fw1ycfrlhQ-0r8T27{@Z3mj3`Taq`w$GP=K#+p*hM9k=a%Zy3T_jktLmV~2K%oVVJc#X+JF`>Yn%B|r)isw24r8b;ae z_CFflsVO9taRGU>SFKd1|8ZznSBjz0P z1vAJzu%D|I8;os~`gD(CsN3f9km}bF^sA@F+}r9|_7}D}D{WaW+g{>=+~@97Vl?SO zhLQ*iSU*V175lB9Zrx+htL87b_pYNS9YHTl^r69ggxhkU?*wf7!D67OdzH z;;TxU3LG(OuWk8M1vPPFj=lI@(n^jmf0r)$N*4bB9`Yrtai~|zw#_TIKCapYsI3H_ zqXmO_wT6cf~v;(;BHjn=K~ytvSis?)S#JdvWIy zlx%fQxC$tQf-)zk*NHa#Djsp~rX9)UJ0;Z~@;MJMX&o4pta(ao{{XwD99Vp5 zrbpl?-0%6^jJtnU4|Dv&ygim6JkxVU@y>vDZY>cf#cdQB3g|=jx?p)kra>Ic$jwaj{V*%=B4t-mk*!N7DL8 z_Y`7k+5InT{3xsUaCxkBUw9Gi6kYcG#JO4x>(CR1-ytiL%I|H2Lu4=GYE;yLFKw#+3c`^sZ< z2}q#*+EW4>)V%CtN_X4&9ZaY;V~=P#W_sajCNkn`xvTk~-;=$f@IA6KUnwQImsZf? zX;LIM%XH~lYN7Yz2=1E!y@YyKb$(Ns@^&{bcAx-#hO4=Jcg(ym3yyYGhWoJFa^Y&V zK#MKqM`=K~#8fu22qURZG>cpmrY7{dngz+(?5u9Tk=-?`kJ_HL?o{AV@L1c8#wV~g zKp#;!P~ffy?-b3sTY7u}4}0Ezo9@k4_hM@4A0mjZYEn{t-S{!Y+}DX-{6^exWe`(& z$I@RNY~91St~(w(yK2h~=P!^PWeaXhcF|Qdq&-lrT`AjKBbaG|*~UYnlG--Y-sITW z0qP`hT;Ke?JxK8dk7oLq-0tv-ZCits`5NPSvdD+>tv3g67|kXfu>=&9rIW2%5}g{B zNb8D@$$_tHp3zE`zy4zFq;svUgSv!qiu&~@*RMYcD}Q_SbGDpi#;p8S+i#}2ACWAa z`w0!F^c-HzuZdjm1~34tcI)O{>APe!^2BR2Xu7OL=rp=svKyej?hC%QknEnuR6ncbJ7G zSrDZ8fKZGl9fV|{mhIz$u2<;_yWJ(;*of7#RH+J-m~nM7(=I8`GzPSqpe0$Rhc3)a zHI3-$I*KN&ozcIkx)s}A2Id|={L8qK2c(Zu*QPeNs@<0r^Xc21_tXui1(>(K zw-Ux`3c)h?TZ_psQ0LkPZmCDhHti~8M1OL2ErlT%6{f^C>b#-o!_{; z%1?Nx#V36B?ks~m$n=x#TbF2i)jk=*-sG3tpKDKu{bQf7u0LI4@4g}447Dk#n=^Rr zX()aafPASjo^4v2d8LYlB=t&#QlXY&qa49^72Yo5hOHp3`LVgN%i47`wn&~mV2=Vk zezC-IE4$u~eQxb9P)tGNu1uK^XLDOk=(f%}Shwzik(#5G(JafLs@Nuq%}6GM4fkbt z9xJwpr!;?$isNqYb^>b>>&pOL4-(#O{{Rn9wux~*vm)@VCi)PP>$TOskTD7 zltx>}iifvt#ls~oYSE=2Q8@$t>foYEDLA17YM3Hn3Z}BU_mhHEI-m-sdi1!u&!>nQ zDja)p!AhJ2icyDGLr-AFS>3y*Hq=pcFXh77ugbck_fOckyvyNfNl~hm@f>qIA6470 zj+!L|DEHqyWWD8VpzU$_rd_($1(9WQtI@PRntDF7otkZ28(EupSQ|tR5wdcGNh@cf^t$#MhPTg z!i9OHgHgpkc{h1Q-|X3LvLlu^92Qd^>g_xaB_gR#_7U>( zkWYGEZCLBwr;`_U40k#&i+$zVj>GN=p{KjlY3fYv!x_j|S!}$V%89Q^*A}>4MV39O zi@FNh+MB4yY&fOcW~B)|rA#Xe-APMlQ^8v{M@h-V4~pjJ7IL-gXI)bwJe3yPDewY9 zMF>3rwm?b00qw?nj>wjh9jGg>_ioIN;eqV;Tsfj%r9Bbx{{XA_FLE~mw{PSB08mZ` zU)*~!N=#cu>nTR9XbDJ6X{sY95;QNO#b}|M_Y1YK->}?T=>B8HYh9B3#ogf?G5LDF z0i$2dzqEQhbp&f(3`}Q`cvpPhHoob!PuY9VXvr~eyLxjOW}WI1sx7>9f^7+26cP zdg4piYS+78xW>5Iq@)B#m>WV{akQG`u}bMZdok792Y*3%2!-SaE*|dBX)bZsChjV^ zhlu7EN4UcEd)b!7OS8;W33FOUnzVpu0If5q#+fyU;!t%cb{$ku$vysZ14qOg+;l~;C*yV0Kmd@vbh{BXyh>1dFw~?x-(!ywUC{q+G z4fBbbUn)>zlfdcqtsZ{YX04&~p?8}X8j5`$Jbe{ZL7!MYr{$%GZQPRUNgyc>F%BPy zboh&B8f+VK&PMeORR!6ZS)+*sHFvKrbFV1FFu50zZ+nu|u}3V<%5k_V6&WdpbT*-v zXvDM|a@Hf%8Y-Jlo}J$BQKz%)ObGA`Z6T(itR(`8NJ^*x_hLh+CehxMX}e9q+NBVj zH0$A4?89>4m-&<;cHQOON%wte1HVJ*mr+LcWO*(gA%IbhDA6ikGLuB{VJf=tU|Q4x9{s*_Mif(VFkdZ!88VdoQ@cOVD*Zz;L%Gn zyRrv#$qSob6wChrWgDJMTcq0)2Xn@=x8~doR4q<6n(3~L$sX)SU2Kb(!6Q%=nhxdK zd5vVA;dctCy~}0MZo1o6$JFC>^HJoubki*8GR0yaN@O2PJ4N#6KGOvYH@5=zZtbe1+ zzSi3wp}6OLZ-uDsJ&z_u#^{8!UQ(8bZ#2SEl#!WrX+K6UoxTya{jxvdZlz|O+>&W9 zvOyQeAPpXkK_}{6IN$!dAYJUYi*<)&E*;(UNfu4UwK9CCoeE2CsTxnh55rSjZuw2s z6uLy(dRHuMuGmWf4w>~F#aBIfw!t3v##>){L|Kt}wMucF&iPGNznrR)r7Qeu0gTIi z*@i;l6QB1 z+u$u3EaI$lx_w@iCd%4-mut*jqjFmwfg!qrhfqK|ax^^%#k&6h0dZ*J*&9kKMjvw6 zWEdXmxiz)=S0s0mAN|khQvJ!#F~WhB zbM2td7h8==pkn>SA@SRxm={3h0v4orlt7^dxOrv5imkcWT_BI^z zaCABlI(;DEleAGxSUmY)6wgCRaY;Hf7S)|8N)=PH3~nQ<3YfH>k~EJM%N|6Et7iV) z*7q{}Zm;ujH|>9d>In|Eol@)(Gj zOPelHebhLmIN?G7_&^B&j80hX{{SwP;F!y}13@g+#WxXkvc-lXLqgOLkd#p&DG36A zP~zgr16xI&OJyV9)Z@h@D*BWmNopDbsgtTCpQ@N`^C{2aW5p(_@g=sUwuB)@T6DOw zDnO_{AFBj)%5@Gopx41STouoyJ21h(6)F%1F3RA&As8UZ%C!10l5nFwTPN{r8>c@q z7zUrwRX<)ct*CnxF%gp;3yvr7jd#$i)hRFUS$^*JgmAL&~7`9L6&| zi2gBJo;+3%k;|b#fI!1H3MX)s2wAOq3#Q(}S1v7<-a zvl7T$)Uy#z6o}7i1(hf`LX-g{bfzyGPjlT_HPOas9J7u2va$JDBbw!b<}+5;hxnt@ zW~rdGzWI4a;#!GPiR)5MWcQpgzr1{oCEAp1ZKEP!3={!36)9wewxZg>s8US`Ak-Ya z*pCvp8jTdeV0fV#A}+Y(haGv<7any*POOqjlub!B?ZbIxE;Txovmt8*0MQE-u_0`> z0aBbHCyN10jbm? zJR0))Yfr0HHYVffjinOxgPG(kf2#TIfYN@np+^o*MgjYd`oC&w>q$t_?H~1i#7^_PCHDR*v1+pV!ynL=bcAjRUYPDwk%VL;;*|~Y9lI0 zb*9`ckOzc*%ygS=8_jYXvaTY5CGrDoMcFr&*=Z>+HXA}xDVo+3_KZa5qYbwz$+l&4 z?m;C{tJFBr*KUgX$X&)uO(h-SP5^p)4l*GSHvy-aTl^9cXy*<#}LG;~c z`FlSS!@nbPehAxTZ@G6wAH(&kLex}S&`P|zQ&x4yK;toyv4WlplWym9lex~+lA^}Q zO#+lFOiKupCvZbirR3s5sA>VvJSp<^K@;e)R0EK}IvOP&vnlx#7)(hr9FY;S3vpap z*4k;@l@bTgFi$NNrkoZV2ueayG}E%Udf3{NbCiX(xeE8TVac@+-REu1P+Z-&E~}2G z0shglJWV;=v=DpihXRdJ^I5hUe8EIoZLIo+UJ<&+Oyx8>X|X&Mwr8cycUy#`9(By+v!aC`{gajTa)Us=w z!<$9at`tMoot458FECWAIl%0MShoY;33f|q2iOr@X4qSL ztA?o3R02hPxbSUKs^@X8)ZD7$`vuM=smEc@OOCSZWwnx$vMNv&s8*e$8@AiU$}O7U zDhlS^*SYQ6eE7-tfkVrFRCmlrR1qXrtSkh$K7~-?c-i_C8#4?o>Kqcqmv(M#tcfDQ zc7DkGump#rkcBNE>H$^iD}zQK4y{d|>uaO@O;YQ8%gZd$Kh2A{HJurm4kV=I{{Sl4 zKW++g7Xz9zusHL{I3RPr=4_!GgO4_z>}V6MQY@*A=?DJ#9mG@K1{RMf@vECv7jv~O zo$xrf=~9KGoj8hX4YRpDG(nE)f9Eo+*Lm@lvU{6w6pDY1iFmVc1`zft<<`);s1Zzl zmCeskdy8f6E@s~seTQI~w6=7|W|V5YJ*V%xLsSjQ4pTipCyvo(Q=e>>LDrD#-y zq;`@ld<<911n{$3ccr|x-WOn(n!ZptLq&4~mgY~M&gH()XP2jb6 z9af;EgIpA#j45M)HamQk%{vQfOJP!Mu1pg*DgaOa06T5q%X}+VT2v4af5yc2)yJEg z*0+#4s2%Z@ZH6jIe|nT2v#0GpLbjf|d5&)9pBrdLl2aaQ#&yAj_)^;pA0|6M3V1*n z4`wc1$juQ*T4(~t`zy9V>b!XOj}N7D!LpE^xX5B%4>M?#;={Pi6^#+c&?pqJf}pc+!5NRbHOVB~<9I zDSEwtU=Xk^a91{{>tuXG9&2d4s!y!h3P#G>aMG$KRm`bL@ABiT_Tvuk9N)rYLg`=2 z%si%_tzDqfIjv&mKlApdN?N zTSp+tN~pE`lVtYLd5Wete#}NH^~#@_DX1cEtzRPRW2&iM%x@3)moAP71uS$5>Dz+2 zsL=wIcBa|fd-HSIHeS-UF7df{6X}m@fQ+>%S(PZ$!h`@+wH%U=PCO*(NnYMXZ3&V? zKo@s5F2v)XMEoTt{{XpU{Pn@W+OKyu&;+Z^<-`!&c;Q_eNdz_tqEZT#+Hm&Wq;TT7 zUon4ixH3I0sEMafKwlF+us2QHeEP5F9p8MSZhMCOxe{9n)DV*qgf@VCLQs^Cqa1g? zB(6$I_^-D8o3s}DR?;ECvHt+|IH(cBM%+KqRX(g#q1EAE+`H+vA?@hfOKP<21f}Nf zu)M_znv)#|Jn`IneT^1T`hxjsyXBnZ<)QuJmD1kaDxrpup8A|i=&PMq9w|+sw*U$9 zl9Sq$l1JNzZDUB)Fq7syRd?q80=4oHXLsy-s&=O(*)*=tdr9(yg3+p5e4{+N1?ES) z8RqKQ44irP=cZAr+k0i*sd4A!FNc|G{Rlk0Aw}GI>N@`b3ha(M=4cAr+xFaiw99^o zW_SwFm8E0CtzC;rq3xl?cMH9xjowAjqsP{-o71#bmoR+pFo(eJ4kOTf59#Sv#@V>- zw>1VDkdZ786mms#&m-DB*wZZXk;pV$+mqYV+Z{%UZBGRD=}_#iek4q)z9K8ymZF+0 zietu7ycz<7`L~rPZO9Dl4T@2A<-O_*J*#C&&I@l3E!%{WkbJgTNh$>VMu1~0-`k|R zmR8>M&(ls`lDZ4B+Yz2=$=`dHu!@p-o=qN;!|UJ`i}d?!+5An$duNWk!0UE4PtIIi zO;s{;>n})se%BVFNydeGnz1Yu#<;F5DfG?@mJCqH@gPEO6wSnsP&cRB6`*L?(-JmC*M0jIbelL|lglQA~TJqxCUl z7e#daJup2bdaXlhdXz1nXH0vfpy_9d@>J-}O=`fkgrMtS&=hU8s-OhYy`4@C(C$Gb z_qK^vHM;9G1VpyIB}brbrLXh@hnDtpynC%RQIh2?;vV~e`c$W~y*_eI`nbf7)sf3J zx#grkbd;Yh4WOLJNurcTaK*C=vM;VA8W0sHaCcJ?VG$+a&K!sBUqQZ%IOCM|Y%ROP z9-3@dEz7*5J@XN5@M8z2OrGHZa%3n3p|+A~Rk}hDGAA2s_*)EhkNv@*9xIbCdx?iQT<}1YJfY_+OFL2o$o1Z11A_1repXuTH9p4+du~^{9Zpbsci5$O974h)a zlr!@n+q}iYd#nAq{wD*SI6IS_OrAdF@I}Rhw=?fpi7ia2ZKuPN4kcO~844@Hb>7xw z?4^t4-a003l>MKl_;a^+arql-!#E~OcWMuZp#HC6rj_Kh&sbcXLx{F9XS5MSvhxjNHaB6SD<)=EYG?paCI0}Jxf^w?yL;X9B%0UDOR8nG*Z5M_kTB?3M$Q2y zZe({7qv+r9j{& zji*W?GecE4?4MO0p}6hFzm~Z!d(jynn{?$Sbahj(i6Kh*lrbB3w(VvVEedkWa9!J< zb?5Z@*Fd+}kch&9o2P?;ChCt*#`Bf?Q-(zw**O z__$&Bm8Hq)C#Bv6WILC&*&8o-H?5u{Ra7^m6+2?Mmer$6Wo@iyT_U)*^@86*%s}aV z4-mUWYmB+@Z4Ri_F2jfADE|QRvQK6*sWgzS{Kr^YzAe8vt=(|kf0*Yw+b68)F;s2pz=&>D1y8(>#s}Qq16;K4E2m3bIOQeym6dJ=G6Ny+^VfD}83Y zRCD*7KuqerTO$-|a9GGpgede)YlpTHd+IA9D#U6@KRWWqKyba!^9l9(~nuWHNiH$j-fr${$ML3+HqlI^;PNNHAQBh3TSdC^%pQ91n z)Yl~Ufk}H>1qtgnKBl=RFOL-%FwH!y7<%Xfxk%2J`+*>>Z*G%Bj{ zEg#&$SoY^kYUbqLwhM5vD{Bbwj>+Um01XHK028@=0IBPVk}dmw@3Wp=XK~fu>1B}8 ziPjj6F>b|LtN#EQ9mdo>_||rV%V98{EF>;Z?){@p^Dg7~Cu3ecbfNYdkD*8ok^+vb z$3u%*jb)4opO~s=;-_h@5>J{I;c8K+R$ZeG80w`1XpFW>mV%zC?Dyd7)Li`al_=hM ztJA*GC&ZW4B~1SSk3Kp9KSB;L4&eYDK;pm3Cd7Uf4Waw=EJ_(R7yI&Rx%MohR>fpYcHh z8fMYPe6^EWc%4=~-0jBO{IlHOglIiMOMX(#4LY1y{I#F4z=gV4a)yZ{E1?)lNYbr! z5qDWy2vUZWC-<980Y{(~S_MEel9eRFhCeMiaQTWYLzqR2>nZx{}^d z_8JkbLhVM+`5(>GgU26lJKP&5<_*A>*$-@e(CSeUjzil`l!C;{CrEt?DGn(E?%W~3 zklTt*Fgy6>+SlG=0XC8yN8LZC>EX~;cCc>jZ%HAyk#IkFY0K@W15Q*2g&Us1xHd-Q zzU@8JXpy(KF4&g*9l>FilH90MBRE@ims8lJ5lo2(s3Z_^&=}kJuHkHvkXlBIpE7uv zt!8vGian-;C~KAYgJW}bgYa$96Xoual0}J?5Hq52KjmO+8?9=oh5l z%(-?}ZM#s5{J1@H~fQYVjZ;Ht~ zsrhz;A`Q)jBa~~?$=Lx0q@y67otSjS(IZt;J$?sM>Dtm) z;Y*vA1xNn?Gdu<|UhrvX52bX6<)i-q=CA(%l>Lj0@vBff2TXHpOSPm*MSUoJ_*KuM zZW@Hw*{eJl6at}W;=CrO3C-}Q5w6~PIG82+Y}v>e2(m71t1sc2vVTrG-?Z}eVp_g^ zeqimydxgiu53nidl5*@F1~jFHEe=XXJiHbhqJ;se?I#7QaEwD_7n4CjQJpZfXof*m z$ZuUt{J*Fr6oW@>mZMr^Xt&djMDDJcLKo3q#J{g@vcGu!fmL9$mtn?s7N+q+v#59B zZ#5@Hke?C_M^Ts4k3xOr=WtdCEGi=i)j{Q_+n$k&yCfYdiuP6Ick}XgI>G59Nv@bZ}?Gk zLzS%pJh+!OS_PRQNObfXg+ER=2xKJDaCO6Ip?jt;+VpMAFtokq7T!*iL}_rO>h7g6nAt+^rSua$e;iif)#R>y zr(i#~Ey$M5(@dDj#k>kG7(reC=~T1M@eeE=~SPGdJ@gJ|l=@t50kIUn?1&^5t z>y6rL#CjY#eTSDibq~@Pt=k^)i35zeUUDs$%$l(7=So{D0FDK6 zgsgmSEpVX;@54Tzxtr5|=}zq7PC}osE}m)~xJQcMRleh;L0Tt5a;!;8sij)es-Fsy zbH!70>{|?*517w-ni0f)p3g$JZC(A5#k=?qqfZgU9~zHOGohyf6|LlN_EY(U*)AKy zjx2VL3-#e;Vdz@+TF!t<0>XS7Y~iJbL_ovB2p232Q=tbS2oxo#jenVzK%+SpgicWA%D>*BkjRl z#~x{`_PFFynmHoMq-ZY4ZodMDhJ=5Zv9kBjI0`~CR4< zA9iu>I#pO7+S|4Y=zYJjH6#8{e!3=IV%jEci$$7L#}La)m3%_kN|vo4tx+3xfL6UR zDtnD!*!ruYjEi7odiq3vr?F69Sbm$i8`4KK+_)o>uF$T#uGek0+}xjPenZe4jdMcO z-h(B!mK2pVw5Sx0mFPw!Fgd95komFH3eh~cz`n)QkPUxQ=hrgsRP0@^D(1U`vE}9> zBVLlHDNq2eB6nt6n)0%#Y&b&K=;*MK(c}@ow#Q0a|d z9M-v|bC&v@#%RGj3XE@3K23Ah-4NubTwAmQGF4pi#4W=Z0nu9V^GN=ZNzqY0W!p|j zxj%l2$V+CwG=z@P`_vz9FZ`e4r70xzILVxNg|mgCl}cwo zJ20I}Ist0%{8|3+z>!7Zx_$Xu`YmhLw(sWcyuMEV0A?=njhfGMizQaEbrY9!pC%bW zZ_i^&TM_C=H6=s4JtMa^O}d@Y@E`+UStIom*OwD6#5uhzR4Yy6KR z0HJsFJf{tODT}p`kTUK0iOTKn#?sxY-@LiSz1Hg2%lgv`)raLbZs4y=9LvbNf1D=r z46V0iT=(|d_E^xEQeiASq87{U5K@pxBw{CVU@2t!n$Rg-p}(7PnuVPm9ZscdRr1!C z09`9uoO49l8ZO+WMN#xQ;T7xKfa*w)gbohuDA_85q^rgGep{}0j&GlBw{m1|CqS{> zADg=+vY8Bqm{UZsP^Gpi;Uby#9BJLLl5u)WPU$pQKjsb_XJLb;XP7#Mp4D}^iF>xu zD&=66%KLAW_MOHmoK4OwPnDnNYRw1*Zg*EDV^Q2!cTZ_-t_}+h*jW5|YhMIzjo|7D z(u$HyX;gg_p;}ZD5BR4UUHJD3=*&~AD^)p`G)$P^?>6eeKjj=|=ZzM^&x93*-cYK2 zhQCe#rC6_@rI#CbXI-N@`D8OY#%WRl+6KR7H=fxV+aFrxPV=y~^dCaE5jN)0l+xdw zzD>19q;qd|)%|H$7}F((RgE_0R-FP&pC;XJ^KAAR&Q7-KN}RYj-;%eK2k=yo2fqOu zUJ6mo>(%2_=i{sGmc_m{ZT|Zaxz?LC&J3%h2U`y`{AOgK$57)(C8bFODJQ7Q2wyek zBh$q`ftu5jm;FNV44yH+J$i{9r}*=INtHj1H(WI(+jYhShfJo>($Hv?w1tGJvzlqr zncMx4dDO?gPbl2X5~C z+)QWCqPEmRh(F|qc;|iD;oo3d>^GP*8&Z_R4##3k zYfVp-=Dh(cRWi>m+-=Ubio6#joTfp%%BDW0Hjf+ZP0elFJ2uQM*PDB5R_aS~QzkkV z=91HCDN=w^KqXW?qYYnf#-DXtHuc@ytUht|D%xGh4(|LW;uWek6dK)DjIvPe!X98s zh>!*4P*p?TX@uPBls;MoYp66&{8h@(0HTC9!Vu2p zou}bx|zlDJfcZ4c$2C@M@%K>q?z;71un4aQ^S&Di+7!Nk+Y=EH)%jJS{aqsY?Y^R!OJZhRM)Q zU8=%lZaXVWrjsGEuzG1p)NA!(e{8n5O;w%Z;@o@vDx$(&UQSe@{aDMQ;o72NZ`hedhXjHI<({@vGA$>eRhZDgM=-8LXt z8F-87D#jrt$#zkU9wBtKy|8X3M;>iIV(2`Y1%+3>xa#P>5$arI?}+-*O+&u}R1`}# zGZj}@DVyVn;=&)UKm`pT{bvps04hG}p@xWs!77V7L`97Yj^dE9*eH82N9vtGD!@=p zuQOcmF^VM|5tJ{qeIp7a9@Ju|yDTl{B#_|L3Sl~+qEdr%n{QfNhb`EPPGN00q#yK1 z!^WBs!!JQ0mxQn`2FvLry& zTHY5=m9!snx>Y7p$oC`xjYnvLAgalCrn9MeZxly)z`unmB<0wWacRiDkd<4;wzD-* zC9hnWDo^ja>b|v;j6!qomA_lYdIA+LbO-4;N{M5|8~K#cSqy=}qdL(Q+zDMj*iEk6 zv9^;4Sz%2;jY5Jz8k-8rp8R#D+hA>r2cd9o?>=TuM|w)VJQ?d>ad8EOt~%d5JF$I5 z@b{eKvbgVhZTOPhY`m>*7?(CsB(S9`I;+e8^cW^rZ+zJITv+cAqHON`k}5ide1A@A z>f3Dp0FlqDRIw8AXx$%|rzl*sp)+I5dh4`9+H0DmiE*1~lhsII_tOI<}MPxy|y`*i!YpM zdue61q2BG1VI}2(Q{=qje3ykgTUv%K_Aa!!6^X&h2KO&TNe_YDx!L0RF$<^1Sj58 z7ZB~J7kh2i;XS=eZS?w}a5uvZQ)p{`7|9V{@7N2`kyL7S_ zo+PiLFj_|hEsi}J&;a^BZ!|8lkUTZOf)aIF>mdYz&}D>N>LufC4OQp#S7Jmac}J3E zE6YqcjaeP;@x|H-ttV?RSmRk68&gDC683eCB`_`O zze-5TT$L0zUt?+P_u)pm))DTClruqhaBAmtgokn5cPp3ODesqzpN1nKHh?Q{b9R?U~ z81nL^ezzrPgU8VNCCBADKgn$<{dniD@ks8rh1H*zh-k?j=>2MrG;$Tpr?(h;TSd_m zaYj7B(wJDdQi&y^)F=;T6J85lT2id5TzaWsYPuolm8$Li`5@$_%8HFt>QB`5@6=BI{7@mwR=}~B z7}(oYMuC{xsq+t{8W|4(RUh3R5zvz{5e#K{(O!1`)Mbo8fP2S@{a&T>W$FRd%ID5f za+N=eEM;HZj?#O5*y2s`CssyJ`Ipu{-M6Q2+mG^ovZI2MwX)bbx}>I`<0uS3+W!DG zzO`nV)L}jWcfF*D_N!AM)5wVx9_=h8IPR=SrJFrV<-U2{SJEHz6id!0w!cOlwbe`m zD7H)LPGLk>*f46Ld_bYZ-+X)Ak2Q4Jd*>m9max0rJX&=GdP1lB>uPe>2*ZbPw{Deg zeUBO7hPMGio0)D(fCv)Mf_x<87M~4zfsCUqW01wTQPpRP=)jp;{}ZB2c1@Nl-e6q zPFnelWF7^lPhwJfXQ`d)ltGUY)x~fesFQbbX5l#kx%p^o!VhsL0&TYi@4NiAl*%af zUMt@{S1%j@_eauAPt>GsgTaWm304itL{b!o)ByNs zqCZAF9-2>V*QYF-v$y@fd9YkmWx%^clNI*XY6>1F&Tr6{Uxh8&eB^;C8j?CMjCld)EhKJy2$V)5<^<_~s9 z52UP(k+9s&dpg>^U26(I^>OE5Yzh8Y_9wsH*;eqC-qMCo9Pzr(-fVkN2dVi8{{Yp( zZrip8{W0uH{{Vhv`AqL=TRp~nQs5QXHf2};0KM_a`%{L_+j!>~pf{b|A3wXx1W%O1o>cNaJh^WW_%t9s|z+>PY}<8?^)mel zmhMqvG|Qo{A@>%YNEM>AfJQPt@Zv2kty*JfX|E;Gf0oU|k;r7;+ld_ZhMfsqmX9S` zI+I?w=H^M_yE;fkRK>v7%YNnN4TEC5!bQf}a-Nh;fZ){It%jDel@8J2#gAyj$#2-l zih@-ixYu&p;bxX2M>Vwv!%({lt_tDw5Ru3By(8g2DvHzTt~#jf+-pER0et#B(9a+B z-|Z*|xHpIcOHz3L+pDyQ$sbNwLEAqb1w#UH7j(0P{{T7O(wI+red0w(S^iUvpptS) z09UgkhCkU>1pEP5V(z|Z4P|$=DMa-D0L0AzM$U+}b_F9=UrEP?&9EQ15;AvNG=J1S z)a!%8d}$bum)Vx~mC;W8x(PYg_p663+O};!2vGC)A1fnh>0Rw9-p)t(aFyc!jLZPRxl_uNm?x*h00&_4S0fxg9qZJ~R2j<)NF{{Uz_oVL8k z@R2~jm5r*2a$|`;6jq2HCFMY-VY;R^FVyENzo=^uOqjZrkK@%+}K1A@Uo5TphEiO#)t>C3P|c5S*teo(Tx&WsjT#=0AVf-m3e=SoP*Q3#o$0k? zwsWWb*N}ZRr8w8X4qhv=`zzfGJgh9nfJD(W)4;b^8u$^fuhC6-JXBvpko^`6R(?sF7xLXBO>yS8J-PBRHEvhi%y3|NLdYmff)dVe| zq7@1#AxShTH3tPnkvt&W0?^-^;Uo_H@hxd|Dx&VK>#9SCq9?NnmnRX0gt%*#UFQZ4qExfR8;-Tqts_!XQcq5TxNuI0?2BC&K6j(Y z@Z_CDFjQizn-Ky>9&Q0?ZibUV6u669mdrNNa7@LET zosn`7Nw*rf_ac&w4t212AF}!u$aZBb&sT!`G%RzH<35MKsf+=1U95hy* z_3Om8mlL;mye%X9GcI)~I|h~!i@nEnd?H{oR z#r~|fIOW}^x_Jw9zZ-0!<+r$Ev9INfcUn_*`E*nrDj2%%wv2g%XipmY*H5E*sPyL{330Agw;-JSw>;*T^*Diz&K>^%jd-tB7b;EJ4Zzd< zryrp_Hs`2r58T&Cb{j7w+AddHf|A|70%agL^NKko0aU7wV8h#e?~il9YsE^5$aaT2 zZxND6UD7z{Rf*Z0jk!2;dTowS-T1CUtGSItp5O+4+Ghe3y?NK z*s*%L*>R0l$ICK?#l>hqI*ekp67E{#EGb9shB2*?lA8mSIO~J_H|oR7USFr}Th8~y z+iomK%36WuuuFzG(i{GgRoXg&C_(6O-zSH*zktU(Lz+?;oyC8Nxfd31EPG9KT&49h`Fd{M zlF8%_6NMvm>?@C$<9lyXow>pC)kUR+FF8#hbu++m3+Pv8 zzcaQuXqyI1h;BG_aBIDPbp2kXS9lxL{{SocZoEs4XL(ch+^0%+PaRytq6_Ovymf73 zTzAL(VgOh11yBrT+i!SZVkGB5#1*-3e%s=A9la$2qKI$wRxF=o5kM1tEi-SZ`ra4K|C93pNGaVEG$5HJK`DK$}tGZv{R|0nvvl(my z%es8Xc{#Ns)O-)1sKfIJWl=ph*_n9JJ(HG>PB5VaA&%yV7pD|RmUWHcZAh{ z=GS|(V!f4kIr)Afa1ogHnXhV7Y$<6-`^D432W?IY^4P^(XcBR?@;ZzJpHh$aH!|?I8TsdY z@_nk=Es#&(tv~aZ6@Y0(Mu;3)KSnEe+us{#2y?+JMBBZg+!;a)Q8+WiiUI39KDEbh zRD7$od7qbG-g_yzDBG;tV8DpEZ21p5sH$~KbIT-@rCwm5Ipv-?SA6E<$X}UR@UH&t zp4XoZh;6LIb%IHusMehRg$1^wN}m4!)ryL`$b~c~yB#gXI-z*KoH6?vX$p3DVoZ zd-0@p#{U4z$aHdY=C94B-EH2}e4rBeTnP0VgZh`!e)+V1C%0#`5`5O%4=@EYQ)0CG zMRCl*E!q z)F~lE8jihjMr9gG?8d<*<*Nxb9U(Z?xt^XDr&r~vLV-O8b|14v9VRu62skNAj<&Z( zu%)CC+u}6qh3-%AiArPQX`Q~cKsL7i+dIE|?vG7Aqb+XoUjSYA4Av}B9er)xwoS!T z9Br$Gj%t4LHfsCkDq3)LPHNT6vquI(_14phf=v%FPMRJ|z9G1q3yNjspSt$6R-7Cu zsBk(T5$XCVQaxgN0OkJxNN>BpAW8oKX#4?peFJIo^}PX2FqsR|)N8vqpJzg-(`FW_7mYy7PZ@c>EgFlxDora0q-#JuRfk+bEgsM0F?6d z!RyxRvRZ?&xm>+|+3gfD2JAlaeYpc6N|0S?Kf+F+I(K62v3s>Oxe(lH-?3&r)cn5N z;>L3a}@0@aPde|gqUx!GBd6){AD4=Uu{Zl)|^6u(6lWC6saTv2sH<$6}hwy z6mY024&Ld_a4kB7lgRg%chmm>%-43y+u?dsdB5ZrbxINtmn8}fPo*FaH>!Z~ROg1U zTZptA72_Yd9`i{x^r(#o_fAHqQ0*UH90OHpWx#OJZ=~iapYEuf8i)nJK!ysnB<5+A zGtUKcDv)%|2|87ox0K6^iVM-y7am0@v@Ir~NKnlr9)udg{{TM!08csk2H>jY#~xo549wFu~@k5-J8A_ zIiuR7f8E>0H-$>$&#g~Z^7-?ZH_xcPSbi0j`?-Wh(`blS@{r=ej3Y4`KblI^5)w%# zGNluhX^R{-);Jp>8(qW4nt;dIE94DXCx5 zIOhH1xgURVr^Cs2S8cW=czZo{dE!)`>BoX3@+HX^oBK$xTznD7ak>pa7b|I`L__=4JkyRsP*^aUmEZlsGG_s zNqE3HD5G&`Tk--8QK@KvBP=eRD_3xI&i- zNl8|KM^q|=r?hm(J9kfF3{}ruda>V9>mSSi0Cr^DHa=Crr16eFczF8NPYWvpww<`i zy4OS&(4aKL6W3{HeWWZ9hev!OAxyycXiHBxw3IBk(nD(0l~jXKm^kOl=(=SgcIz%%DC-&nueBL%WGD3D zu_YFjTB+3}5>=V%I$)&HFVZNjx1w#h-t^}98)eu1!gnP2aX?^e&(pa}tw5nx$A1ise zHB5umvk0egtGVe~KsLSD%8g1SFtD730-GuQSlxSC zN0vcc*WSqKGY_F#2U0W<*gG+>7U-~8(GHm>v$F}MP=Q~=;qSn;wNC1+7Z7@azHzOx z@w0ah(zWv!6z&v{#rU&we8aZ%J`GbU1I(+ z)6%&o>qW~{&ULu%D+Po3R>!#@M3HUVNtX2F!wrQ;Q)!VP2OI#SBCPxI%w5T~49K5K z>%Pypeh}9E)mV_RL}yO?Q0Nw_j3vy#S5kaJxFcj8K@weBLV53El|b5ZK++5_f_unT zC9-X+LG&n0Cl?P&Ww!TW84lb=1w4-V-6{rsxDpbcXjc!?rO3x`dqzU(sSw>z?^d@H z`VKda&P-2GT*vu9;wS7Mxxclc9TKfj~x)SAXMi; zOe5w5U@C#aDC;Fu6*(U4dZ|RfxoJ3s%!eON8QJPT5oUNsR}5qO?&Z4 zS}V?~A>>eh8;aq}&W@hS3Z#*WE^;v0R++aylSIL-I+5}8C5gz0ChJkTtXr;O=nAf)-#R=$#GQ!aaNVL? zkWxZPS7mWP5V@_XS}B>YcSt&<@@TSqzh(&6RXIapLLZuj)CE&Y`|yulcp=l8 zG4m~o)xm=F$>34E==?%5#DJm#d|u&PDlMiXMwABT0sFyQA5XH3D|X|M_NLvI`zT>0 zI%VAd&!})Jdc$C(kjs{7{{S#uGn-(C4)*vo{fnhCRi$gGsjhhJkK7A~&lBrJLg=D9@Q_9!ZK+fv zq3l<74bo8v2;}+BT%`Qdz#-)&33WsFl!S!W-f4g(7B&V74hvSvDXtX}uQ?e)y9vv% z;ben~*r`Z49FJx^iibmkwEZ~pN#PhukG~Fjl4R8k7C`Xe!igtU3>QJD40!-TCo%_j+b%2B zK+Jbkf6O?0l9Tu+)T41xe;uJwwj2pbCbc=J{W!;Dc`jR%C>n?J0i`qV$BEH+07~68 zQ;oKsu8@#_#x(tSUeUc%GShqL{Ku18H54c64FQN$6sQZ<=ZrR z7X)S<)pqkAI=6(miqjT`TDBpOx^wWXED$Q|` z4Yw9LdCVnVOT*ae6c2V7-Qz8;ff6(n{?htbO!CTFt z!2bY~-{uz1ze}8jBhs%sKFZ}k!;_WrN_A;KQqtY7>f?+)(h7i6miOp8d$APkzHb`t ziN{+!f5-J8lWAFB0VF4+)Nt|eJiksW;P;egJjWyQE4OfnZbiS|V#t3{@(YlzPeLUQ zC;&k%R2*V?3xhnSLI?z&Kd0yyW8yJ}-rbOoc!rK1kP35fg~Hu)-mNzhYf_fXcUD3i zLdeJp(Iofc$Fp47rmILa@K77pIU%>JfdHOjir>plhL%Vf^f_Zzlq`lRI+WAI^MqZx z`V*g`-HT}Bl;->kA69uyIGe@av>Z7?SIS8*=4Ph?i@YmXg^w|?IkH}e->EC zKowmBA=PeFP6nur0cfS^6efu|6NZoGoy0U<9<#`g#Qv5xoEIq@aPKdky<93N15qx@TRlf{u}K8`-sK;}zZt5H$b7D@jAIM*1|^7Xo_qGF6S zv>cHFvzA>jid2L;LWvoCU2rI?MZ6PHO4G9fMHhh8CoS&KXnRf@$eyvP_xd=szTLZe z?h>65<+~aiDyPO;NqJQE0-=tE?6rrw`40ukznIK}Op!tkx}Ve7t}Dt_2U2mnNLMt* z5C|(xd{nLw)gP!Wyv29m)JGaEN{Gy;;IuZ^&O|*h1%UwT;)W)aXM3Y+(zDNH6Wh1>K^{QCYf`rqsV;ToaE%HVqAknh&1 z)R`~Gixq{*l_|tE5+i9-j=JlL2tp8q0;MTLfRuq!av0j*psC=w!H&^)pV23xTW1X0 zQO*3^+Yz_YB0DAxqFF$Twc&<}*6WK|Dfyy`>t9C4glAG?p6@Z$JQg#(cXXGM3p-sw zG$eEM4@cU$*Xx(oguhX{ojWIDNp=SbZD;Fk(^l7fn9qmj9s-|q5B3N@;~dDM@iZNI zEgNKPmg1o5lUbd|(dhlEkU@D%b(9iDvXF4*M|DO@wKg%<7Z^E$LSE*MN_hVD=3r6( z016DN76KttS%1OI*`XRbFD_b@a=2Hre7$_k!-yDQ3LYPRQGDI%;FT z)kZl#0{c1~`sIS=d?m#k3!o>Dg6Lbj8$Io3=Ec&?g9p6c9%rO~o5XYC6|}Y8t@oRJ zYn`fdcME*Pg~piDlB6vv6&a?u*6cEH+{Pans4JOrxS8yxF-p?c4n-OxA+N4@e3=7Y z3kX;EFd-Si7)yi-R-VilaY+`VawZFXrs*wf2yPTv4?5|{B`vb0ll2TT&Q92@p67Te zU9o8C@=-CoOM^C-sKfv-)*d|S`{Uf5K`uf>L@)x z#okxFs}Z)hiS3~@aP}0hyR2WzyM}G5E#f}pwV)x}9^#0jnNQ83WwAvYg&77eh3Ih*>y!Q(}rEu%s!sQ?b4II#o!M-taraV;ZhGR&iqy<~(;Y=)I_m>3dPWkD zb=kj4rc!pVD+aD%_99aGla1pzL%WHDtE~-3ObnZ<)x1ht+qYRc8&J}g@2sK4f@ z${m@Ie?|aq%Rd$8cE>Hlprg$5w-}^=t=kb5=oGqsoCw{vHKB52Cv0;Agk=2-q~DjP zrTd?+u1uR^**Aj^jrK~TIm;Z7(o(i%xUNzCJQf;O?CtrP)e z?>$7g>Pq&p{tPIPStTn3b?!ab6hOt-pUbDzX2;|WpSXW6A60iBo#6gt3S)+M6W&;V zYi@_0^W!wb&eS%SAES|p6hqz*g}{-H@13_TrNGEq3OT3V^!}Xx0OGaHskobWY;0~w zEh3Gt+511>x#j8)fw%9bUSQsQHOO&v-u9f5nAb!$q({2joh5N6$JJ0;eYzZp(yd4) zsW{d)zH&U3qh@a&W5BGFIf-?Z$%)*!8gWq*YN(}s*qW1yiffkH*REJiDuZvSzy_}& za4X1Egr2=Yt^qZ1LZkq49eUvHc?ce8mTeb#)*FmVrM2&unGI=ihctx^NHrA-$d2%E zSJlFav2Q;G+3phGd^hTEZos!Z{2htRHuNA|_Vc=9F*vlHK71z?q!4Hdky@nvc+c`y zv%@7os8-7uZLXO^?zwWi^WfWqi2XG2rqf%Ds#hlO7QEdB3UN%?pgT0Ikz9hp6ZGRS z?q*k})vPtEkNEzj(VvzrvC){8;C|x|{{S@``q#@0X1vs)8nh&zh~pBB^{&*J-6->r zm`K#*6`n`5O5L0O+_K6{bPb5G1LTxDLxV|?DZC^9wzXk0qu zH6O8ZM`p0sdwVwPOZFDQa13tH(_Jg+p`(YS1nJrPp3%Sdj@{mSen-sNw=_m(LUNQ+ zL#e4khwiBcpo7wwoxtSTd2H8?VN>_M*qaTMlGZ^&#}Jg>@RAOF_>z05tI+^LDO#&6 z!g}M)HRP8iB<_vlW!SrR@40WX(Gt~Uj_j$9EkJl0O=vYzq?~~5IEmbO*sN_GBaJ8( zyJT&t@|gxh7$>NKqNKi(EEjv+z3reqHU>wg>(w@LEmG-wE zBi$wBqu!L)=v@iv0HZX`Ta1kx~ix0Z>(*jV2dKx#;u**eKkXOcMztn>>hl< z6cqO3D%@SkV_I1Tr^R$G$?PotV3zoXx5J%SO>c6wKRpQtE`lyv}OjF;W|EKv40H^Vx`7tA@SR z3ek_ZbW!w_jHb!U{1}fFQXsE+YyHudJiG{7hSm|J)Zndc7o6})0c|NmA(-d^!OM># zj2%U?5h^>Z$YJF$`wB|feFlqdX*!amk9%^{{WSDck(**_s9Ep)U~T+!*X<=P9CA$ zM#kh#oqh}{O}}-If*wg3O*f2kEASH2Y|cU z`;1xLkYc!~(}$pePCU7Kcm##(=hH);j{Q?|e+tM-*xI(qDY^00!PNtt$iM~Uin9vue`Oo+}x6^md%x6gwi1Fu0FEI5p6arOkt&nvC~aU zAR4&p{7xv98e|2#3u0|;#BO&{=1(;Nk#i=T$kEcNXE%CV;=Xrb+1D7SZ0}%-M&6JT zguJ?D5hbJGeKkM4H4eoLQ8$-sY;uh*Xz7#0Eu*%b{mX6gB60IA{^|W($I5i zHgeymyQ4R2gm)jY#&{~v$X2zwBc;Tj3Y4~pax;UuRQ4$2mb3l8$l@fe4F@s$XuI!a z_freGV5BY`WNRC#Ut!V1>+}V#Kq8pb1meqQapxapXYbd$W@-6QMduUA?(R6iOWES%MithxowaiG8}VS6>_4R>6nZm~42Y z5HKHAPqo$hsF^!wXYS46DG|2L-kE-o@`XewuSf+)wMuCu4|zCgXNbGHce>J4j#oEl zWXjC;un)XI`i{QEBX9mFd=%AX*gEey#OIV-oHsZfku@ry3NfsMu3`i0E*(o*H9{kl~Wg{4e2|Ky-|92 z-fi6g@BUHO_O*Sj zzS>@@6-OKKqu!88e?~i2(fv&hUk2`?F+8;LBrbXKF5qWmZi%+3jwU~sA;Od^AX_I= z57L|{`>@ePW5aEUE}7QeN}OKp*9*qv+!vND&KP%DZpnt?P@)uvC~6OOA)RL8;#W;# zGEhTm$lUEfq6v$A!3svGZm5$}R48=%FlRvSXp>|jc7TNGw?dj$%Djc8I@73WIe3N- zJV(hjOl+_$}wYYXp;BV#k9>uvZ)axzQ6R4u&$c*?>6osVGSyvrhb!l&RYtbV? zX$ydCC%MO786a_MnrouxXQ@BSn`m-mYtJcqB}(INB&f^Xew3xYlQuJC{o}i4OXWW_ z!hRzW#d(Pp4U#msj#S@$otDzGS^Lrb-ecAK570mqUFX`L%r(9EZMI9?mY*LZ{{T1s zVf2k^xr@YpqIqxAw&a^0$2I5;M3T05R>`=s@5?y+cMEQ{CNsJg+sPoML}^-1N2M{F zZLRZriArwWqN0^>gB&E$eL9vNv7bd}+7t`)^~Qp*9;``g~iJ+#v`7N|O90S!Yt!Q>6)_ zdQ{=fgj0xsl-1tFXT8Zp`3aBV{XA-#V&3Os0QS=oA1+fT-UZ=qQt4EZdqxN&DKDEz zdxmb`+OQXL+?R;9Sgnew$quCi`;x55!(05lt-EUXfki8h!(QIJHWczRpm&2!Uu@&@eR zwP8)tC*dV~dD37J=#Jp1dW+Z1Nu>Shn5j zO09PJfybP1jSG1#BhB*Bs+p_|jvsA&5xBbyet|`Ajoj>hIr79hoHX@%S6;qW*S9S& z>~<(=wc4$XrKphG6i5jaB-5!qNj*+FZq~{>c!L-y0^u8*iSMpV(wexG%bH8hscSXW z&>qYrRI5O51ha3F`%sKfqL}CX? zlB6hS{%?LK@zy;~>URY~-FutJXO{{0zQr=$?^mbTiD24R^CIIt#v$3QZD~TBefJX` zrqBrjs#1Uz#g=T0jE|-;vfL&TIj%bAI&mlKDwlfO^sVY+VR5a`l6fM+(>rT!&4}Il zD^r@4BHrY;lEX@Iwsj2wN(oYuqo=njoM4^W!sNbJ&y!Xz!2G-Kt+_r>*;+gJ)I3Ei zL0&cE;5n{L#(bSbngh_|nwFOa^1FQm;JN3 zZTEXc&LqZC>rJXeUz%mm76=469V4YkG{y6`x6HP_4tNAnW_{hoI`azlFqXIwYCWH= zU4B6P)i0v^KHaxHHEeqd?Q&jeQq(3$Tfw&!*ilGrkSYaoIKO4Pq0=F3akK@J;rj~= z@HRpLW+|fEuq3a)m(%6F$n(WT_1!1JxaqDVezfh zmlqCoR0EuE%ijCSZvOz1n9W?barwK`F&gq%OvM0)G&fag2S_xj&l%ok>`yUy6eVnU z3nyE^BCSJoP|{Rr4@29CfUE(uL8)2y;k%twqeiN4^wrHD^?c_pIOW&ujc!{;&z-Mv z?bnbNiMF{+xe8lg5PV^$l(zo>rj+(q77ewOHn&Z8rB?Tj*=AcCC9}A=Y3Mvhhdm8= z16AGcTzqTB7CUCZ-rQksU|Dw@1W&f!A-Lk>zY=?Ckj|1x1ZZ=!+A-95SYwjRO5S07 z^6lN-+*vs+?mu+Wo~6p!4pi%)?6rFcVg65%> zM+hUt64@#=AdbBO#?LcoG+fsko!7@j7&5g|s?AFM-k3^?N6bbwdoVOX<|7Rf6g*TI zSTrNmQJ+~6(%$OWvg@3iWR#z2Zcyv~b~%^3a37lmb&q5u>aZiys(WgpQbu6Y>=?;d z>MfnZs8qN^iJ+|y#eiwm3xlNEOX1Pr8I0Q#T;@!B;)D@>)WU zb(JIqiV^L_T&r0?IB2BNU2B0xXihD!=PvPXYAXwqDd#jtj@YW)T7?2Y@51&lzDa{y z#H2FPM-&C`G-%?dWo+%}`3f6SVAY?A0DhB=1Z@0~73f*ByMc-yz7|Hn-;2$?uOI{3 zRLK1*z@3GXckZM|b1@D5@QkC3c>%=*3l56*E#^f%{wyrc+uxocGn>0Omhf^FJV(f` z0t5)L!R>NVYxN`?Ja237if((&iD1&F=ul4hSK-?_$pI#Z?m~z3l8T&UcfRZ(6}96% z>w;7?l>_WO*n?pTe4`+OeTTOIg`%oy;HVI;d(q}D5Zj#*sBfukx`4C=9VrHi?awS< zJAY#w_mp_3S-*2{<+riwjL?#;+Z$_bvEgpX5{Du84IlV8-y4RtAG#Mf$+$01@1JU3 zhibI5n|8($Pg*k2BkCA*%yqE+z@pukx<9;qscA8yctdO<8ajl4I{UEjK@j*w5)ZtX zpLo>>n6dQ=G8Iqvci?=cuYA+gHO^N^-tda7eI9_4ENSje_(E0JfcEw6h&rx_>wrF3FhP<2^$pKd!T z{1+27$ey)fsyY-9aIcn#T(%kEN-qM8G@uO!!arUN0Z6zb2{r7cIvg5>B~&OnfC}r_ zaAvlv&MRPg3RAlZFa(j+4uw*+`Y?K=+=U9L1b5*%1quoy6v5ouq9K?bEP1i&f!q~M z^@#9H4mN_LrmkFMD0k2_&_8ZD=e*K`BU-v|u#wUk9rP+{(*EwDIeYPj#ur0Lp<9Ze zf_fZ!ZY$850E71!?ZoNye{*Z7rIzT=fmfN}G~ZHtq3Xl%+9U9_pmv z`FBzWB4J_w0Jv(WEfACd20&&pkUpTTpsmuiRHZ$qEGpSGO~(YIZEwxF+S#pq*)h{k zd!T;2J;w^&GuE19%`G45KVs`c!$Cu1q7sk?xgcY*?SS%IPX)l&zt4B9qNQLObvRfi zQa+10VLvP+yu!6oYe8J`?=3DR(QESQQKc!|Kkne@zG@Nsd~t5NJmM<2k)%4qofL(( z!j=@0Gf5Q4gHeWAvOZ1c(4=rqF1=v%g~<)2$oz@46eR6kxyK;l_SPfqcT zF6q<@F|%ncT^jVu%RY;&TrIP<*D?AopT75$6v$oclI1363aoH|7mlqW+mXW0d zBS{>DznH)JHDv9gl9EXV`DwWwo&^mnjLC zJ|(6qglO$Zc>o2t0trzS%=O1D?u&_KzIl16uh_nZ>`Msku&^8wR3xh^;YkIAgo zl+Q7fZJm^pJNH^o?E2NH^-sH6xNg7YyIQ=vM%rnJFe9LiDP}7vU<|#fB;xPA@BQ%$ zcr+}Jwb+B@I_2Zud8(=nl&Bhv@t=aW2Rev$T5;C`G}fmsnPFx%Cg%W*PBEc2V>0i| zK1lQ21HviTD}j`Nfq~97DFCfCr)sd&0G#v`RpkWceYC(mwN9DAb*F)>E$z-B+3~b_ za&8Q&bnK{0vlZrjf`c7_+ssI}#k0hUt^n@VW9Kun>rlheK7}eQ)Qnx++AQH|g0C+S zp3jKs}$+%>44QIQjJG%$DeOZJb|9_^p4SU-`4A)TjOJ~TBWO7rk~@<_WQBQ z{pWZ6@CA049CDVoBF%diZ2d1|Fm3I`$Ik0qmB zB=j-nOxpXt-x-%BOp$Ypi1FG&oN>h|jR;B-kV#UMXaLNfnCU*rNE>eQd{-L&Xg%A= zPx(*UzO6kK`X;w=JZonwa;>Sk+@CETcHgu+rBU|NDsNF*io7OSO?9|(Y#ob2;AGYONV^5zXPEopQF|Gs!RET`s#O;rd+ryhc3lDVQYA|y*Gmn5O)-ovQ%ak zlm7rRZC?_B`_+Fy4N>i3d%X)s*jpkzbYgi$WjsI2>Gl1KmT`bWLQ!Tw;B&pW2ZQ~pPNg?DP=X!6lYnvf5> z9qDY&m|8sx<=w!nu0E2HR8pGwYfjqXA;ldR$k(&G212ocK($%Qo%lUhmJ>?UpLQFB znOT<}4bB{kb;dJup-n=Yl@Zhhw&D~hLV~g*19@j0@s>PP6t#`)*0TvBC9WI@Rd#vxz8ykNuHkV|tZ0_U|-NS2K5Mw^u!>CDifkV3%+z$G+ zx%XuEB<2dvcb{i6SkL0dJlGDQ&D2xqQ-u;mdIQrs;`JP@c&klTPa__~M+y{Sq-$De zDw^Tcd=odmiXZ3Q%QD@w&coYo1@FF33$^X&NvfP;Gs8rwt4ecAY_?4a$f|=G&u}un zbYjviUj02mZT*(OHamlqx)is7bsFbd6rxK;3#XyGWwrN2g6pp{E z2)VUOBWYSm04Aff_KYNvq8+slbur*kN>gBbCcf-Fi8g!mRFm@-Q9NnJNv#sQ)P)av zZO(q9j1RwL2pQ{LbNNqaO~t|g05AUlP+Xc|jSc6OKVCV3i?gH}sjGl(w!7CgagC>N zz;5Ydxm}~Ra<@E?wx4K)D4}UqMJgZyF=pDZM=nM-yi^jYJ;1z{-wQmk9+$O*gXySU zu(@#03ZeH>IKs@r2|620Cc;3kKm{p|(f-oM2LAvoFPgFUQ*k-f`G@;TNE}DQjDn_k z_SnM5!nWh+BmB}ZfZDbLg93K$70I))xL0N6JFAIzN-CWJT2?_P4ERRgQYpp^&N7-+&K6zCK`9lnU+uJo^XL_>4YDck1d?ER5 zk5fFcwYZ~p@tG_qg>4rDy0BxJBMy#>>@HfPvQI5 zn3du%0rAs-Uqzh%0P0hT{{Sq1PgZ}+tI)iJBX?M0y5BDH+d-vDk93}ZxzDN8035nW zQ6)?0!e{^lBnsBl*@pH{n3z$u(0Klzr=l--Z>yWP)yA$rQ04RvL-j5JJt5cLa#V-a zDoll`$q%@tEycLAtx8&!Nm7(aB$HBeINjrDU;uCnF(Rg?QnH7sT=e^JW*t!q84jkQ zS^Kb5foVerl53F|8X)Oeal(?*@t<5-OergmCAFyzHsfjvO3<{>rAZ(ZkZKNN29n*f zFtyr(hB=)Z!<(TCz4um|{L+06vAtro_^++MNRdo!)fKd*GPhRTDAp7ZifdEtv|K@_ zhC&vRM6A~mf?0O{Q>6a@B18492O|qJRsrq)b2#hv`u$(6aEYZP1e6_UIiXY|BkVZd zi-Uk8n#tSgMoN-$I3gOAtx^ahr*1rj3z5tmdZ?XfcK#Y;#RHD0RGL<}@30yuS&q7mk)T6I%8Qn=^d^uPpQfV(&Hvvds7HoElwu(`9hTxZS=K9HojkR8Vy z{f#55Ro1y+LuwE=c;-BnjWj}feK-t^UiYCM!cgV}jnfeoQeY`5o0=kRc3}3&TFD(N zQ9+9%WovBuN~TF@Uw&WU6hh((U0DOP<%)q<=9;uGR=L)=@(L_e1MbI=fVb4D*0}QW zciI|C;K*{7aUc|*q~(dr7;3^)4fDP55ymE=l#N+0zX~#NIHaF#)xi;yI6mf}d;_4z zyzum_R{bGm3g|+5_TjrpsuwjGD=HwVT6FaKuya-Bv1Kv^K>EjS3JnoFw5M!+A20zr zY6#E22e*^nN!&NxPiB?|XQos*lsbh3LJ8>sL?)T`V9qs5Magd;?-iDH>w<)&sl~VE zCKb&JQh-8`prTDXGQ_=;`bvYzv#GURPxRoce6A9**zQqUpKg-v!EeQR2yvyQ>1$C1 zD+Cdao!Hi}O^gkA1RAfOedfQqhjKbtoLc6F@bmzh+qcrhNUC=K0K^-nM#R6RUUOFD zxZ!}a*GNbtpnC;JvkbF+z5%7y&MhaGB|Eiy{{SI-W68!PX#MJY!_!`B1KE4unSDOp z9NAl)h*0deNiRr)^W{jksn=Jj!o-$=N;MNwlTP|#hlsw)Vk9xUfj>gpGngz6?1)^# zOWx2cOYX`WcCmIVk9pjo0yX{!j*2ls^ z?w1(>{{WNxQWo~)9rLlEiMyY0?VYve3P-lal=ar*A;ZzOm}vllRo=5$>^pwn7Ta&O zqBKZtrKVDvs)#~#q-WhsY?d~$+r}8tpj@w%$y{DcWs%i$jY{T^tox(4j%aQhgoqYf zMYV`gMo3DETy<;H(^86sPq1SE?j)u6B)*5NuOwmE)Vp_KmU?NvK~!wajHN5w5?X(Z00R5!SHGuMYp5zh7Kjw+i^7j+J1 zOn7;0b*TfG75zT758SMcz1{{KmqlJ%_S28muO9{Sjk)*6>Eun*f8U$yaLV1g({@Qz zy~(+!Ip)flC3zJoQ~&?~N)V6|lz^0gkOn&c03VKy)*%}O09;9sxRPlLo_L}LPJ#h6 zr$J0eRWWO7woI`@q~~6}FrbwOmXG|)GFW;PrjsTlw3g*ZaVcAFAwq;HDg+ViI6RiI zOx~FXP)D`Bp4Lw?Cl?=?5wd?U1aW1x4swuWU+_Iz>62kQc zmI$fPLQZ*CJacDub|s~ZKQRuKJQr+!Uc14R%-wvA;p^#XA;*q_v?q!BYPp!w!iHU) zxaYRHb+g&3HiA>1Q-L6)2&xlMSn-N?Qn)is5ooZt02y>277x@)J5=Yx-j+A-KU!|v zn`vEa_PaHLV7J^KxXXE`{PMzN{nJe{=CtWpsVW)b^R&BpcbRE)L;w#pK;M1b#%-RK zUG*2dR0e^lsTz_+ICvFiHuvSe?}h!<%TdCz)fU}uaYSdBE=h8S{M>2weTI~4B!lQ! zj$iY4Xaz0p$v@^lun)CO7B4~E1!+oEd7XOPkGD-_$%u&3yJ@B>5`NrUZ96rOXU0ic z=1=ocotOD6V+X3gZB)}@;En*=DofTrEwwo*x&%#@&(6P>X;hNB;@M-{{8hgO3i?)Q zlkYC)y#zjY2A)9BeM(@rULo4j+cx-3Ti8$jj~%AiPhRH0#hTwAZ4jdx2hgY&m)Ca& zvOUiFS2sU0%fjyagM}rP6{HHW}Z)P z{{T|w97jr$3bH4zF!nyHrKMs#6gtyYJ+#8X$*~Dm%<)y$-$c<~b6MB=Lkn7;d?xoz zqY9qN!jyZ*vbeOdwG~vy9aCocHbtf~?(m2A%Rmz0sC5rSP>_R{^pG&T4(XVB)Py$d zoxWSN4$%6JhwWc;yBBds+}z_AJ<68FbfLup!>aZLRV4dH4R{X~aP2NuD!SeL zPN71)e)L_zw`2T+z&iyY4>C_eqE17uC1AM(D;{$hG;@XH<{*P;tdMGfcrh z=3iDYL#uB_Kgha@Wc=;E-zmeSNR1t5{H73VmO9H8;`>&gLg1XdeK)Tj1rU!R_)=Pt z3FXI;89{8xZE8Z$w4F&(RFVo*lb9nhhc9ndh@gOL;Gxe@oE@+|OQGAg^4GUHRS5fz zTZqCJB54TFhVK#4piDr^?|~1dpAv;eGClFyi)@fG#qLW^Jddn%`*_fW&>gSuweA>* zC;lWJ2Z;3^UuTHsxVg$a56QecImS1IfU4LPNPDIpsdkA&DilZw8cF^a0#)qCGG(@= zM;9l&(kOV23%GX1{KaNt4z`dIe{|^K^mF6ZIJ(#g#d!d%H&>GXlQTKOY_QqhBO9*4nHtFpgKBZx;znR}AzRDZE z@%^IW-~c%wOPI=)*iXRbc+l;8@p|D&8Y;3&kIFs`hOx@w{{X@d>q{T~k_(cx6UC5{ z;(+mfCi=fjafj~jjHCBcGx=As942@DKmH_vaQwvgjDWFf1kWXeX?un#~VTABFM(yuadQreLU!fdvO2-kPGl}B$$?LrhauAag4;@yJnwnFrS zN)HmKd#}8h98*Yiop11Nzh4E@J|6T{#yoiBJI3fIeePJqEi06irrz75Fi{^pR3f8U zIN!G4Y3(e>RG#yfCU1|-K1(3u6|nN6I%&j@N&f(XsD7e1GrMEeed_NJi2neD%e1(Z zASDPw5gm{~2>=14k?zJ#xbDTaYu(TSx+i91&Gy*=`j<4Wf?Rgx_G5+i?JlJ+r7kv& zI(#l9EFQfma>ny*Yb>#c0J!>W)wR5?VRoRb!sBb~g_k8Zh)Byf6bYq2g*C(W;vRK6 zD67r&Zy2L>Dfw&ImYaPXn6+FUNT{kpQLEfK;!@uh#_PpV@_WYOeH-qC=W)cOXh;Dm z6i68WJF>*1TB{Y4NiI%KM99^XCEXn_c?h3T3B}_WYrBO5bP}WxEuphY%D>6w{)#Jt>P8;)2fa4)(di zrN60UJ-xBaVYiHP#Me=;8jo7|o`}oQz>fQY*P8G;@@P`Eqry}O>?BlU=OxQJBrW4Y zzVW_hzusoO8jl5#Qjl{% z$oJu3D4F@SKwTR%Ev}BMj5Q#40hSV(FAH^1-W#~+o)lP+JAK0qYL#n3-?V#saA5OP zvseo4_XzS*xiio)+Lsr6`>zMcMY1-sH73@leE5KN(Fs{V?8=zwO{l>^*D1g#9&NP`^J$F#04t=F zTPEIdbOAc}*}|)H#Ao6;d&O~M+vbf`oz5urXE$9p{1Ku#KX)_|FzO`5_ zzsE9yGA&O)H5vWmOZzd-9p+$c?JKJL5b$kYJt~(l=A@((P)1`IQy*2oIiw?rP_JH{ zaN)G70MedUS5WrJwkTv+mawm-ClhxEs@y1>C-mJA6B(BQ=&bv(0u;OWqs^;dzYDe1 zNi@}1zfF>Sz41CZRTC-$!%y)k3rdDH&e6})d`E~_*L$H4#{=M1%y`j7Dpy08$4fo+ zT$>(=EyYnrxpxdj(xp0x(E_D`?KlStB?2$6sjWsE$ed9F=IK$?YC8rKJWz({wJs$1 zhDW~ulnq)f?wY=gc^R>-3zebO)7yge1@4vYV49P!*^hMv<^r0V%xkbqT3e@-rqr)? zs(!pww>y7@qPy&qONuCG%}E+_)PH*!z;zbGLZg01Aj7vSs*vJ=`csOg;^eKnZ1GW6 zkVaV^{8Z|$fuU-%N_!3p91(+cPJN9lw_}JhQc|@kKHw5D!a1k#3+WCOl_aRGzl#usPRNH(QG(PWDY}}xDvjz=`R8A3J7}orwns8#Coxwf8Yib; z3UP^d68yS;fyr;(tHu^v84ooVaO(x`OxkHa5Qzm~_4ry@#16uYyCmOQv-c8yoJ^jj zf8`oFuYEDs({Nm87|{cQBd=r2$X#B_VZ~B?D0cgBaEgp;SAIQIjY1R?nLZxuc%qfL zHTF{g*3lKMqS-y&*f3slHrS#^w*u!?CQ##|K+nA3z@#2^M&qQFjJn`a9Q_q6^^q-K z%shP#wJk!Jx>x=(LI+Qy9L?TIN6rE0U2oV(sg8TSBlW79*`g9Zi}hm~iEdLih;@Rz zh|9YR7HXP~3fFD51}v7eYavariXVDw(;CJvNCL|;NBHGqc1SJlJ;>-+NUE>WLVsQu zXC(d!POuu>HB~e^q5va0V=e<)EoAXRC0djK>Uv>XgxxRg(p=n|d6SuTbU{y$%6`SwxnCKl`B?0@f0o};;EBy-C(?;0txwT~Q$Q+)^F4zAhsz6n zSX0YMQ4H9vPi_Q8fUMq}5q7TXlD`kj+b67_Vqz@fA9lN@!TzN?v>VrwjCxlVEyAod zQ(p+j7r%*J{Ufzk57U!Ww{BT9rng#bE8LpnQ|;qFhT^eK=en{qDaQWtQpd;Q0GfRV z7yGU%kB>BMRLixljmO*8c>ZVsnC*&G4D{Ex-;G-XH$}TwJ#I^5CF9*rX?|tx5p7fM zK|xK!W#?VeK_E7Qy3n4=Vdifl$n75b(t$I=V!YedGMq?UQJFm4ZueV?)va@%dF3y^ z&UH)uc;V~Ik1}^V#IK}ww*E{EXMh9PD27Ub_8#mOsLKjoky0=IJ@jyU}rNP3?Ev{d#!m(Hrq7&(l~nphBtNT+t%Q@ z&RfcJ&3)pPlhi3>*F077w?D9N)($`-cBZ4skQ$XRKxTxk^(3ri*+GV| zQ>#?^;lwvYqamnKlAo8kw((KUm8i?xhXu^>O#qPcO__d5R-oIyYSo!vV6GZT#Yja| zMQZ9WROuB_^W9~i-f=-7{{W0aMh#{<A~m`4HASmj<8B<26XoJVa2Ebq~$bR+4a3JJ;{4*pfve*hH|e$xeGt5 z9FN{dQzb*-yO;9*%HfIh{-sk{aXr zStyZ&rAtILUrk^3cX+>NUhMv&d6#<#&Naq&OH6IP^u;9+tM8FpbXw0Weds#IWv*H&k<3iOW^A1Ng*Mq1F7EFXi#^=E;;A+R|5 z5Lf`~hL1>GHr(<RE93;_HZi0a?rCk80W>`32M-8G3$sL#ukiF%tL+Lr35u^?a&Wx;w z(cE$AN-vaF!a@N6)im~DM!KmdL{YZnpM;R6`-ttslS854o4zkq)eRpQ9n z=OBH>#k#R2v)-3l(yIM9t!6fbmDtt3_lVJ6wzqKdn3G7}dOQxG{Rc%8CyqI9Wv9y6 z{HHit(fx&@=-O^Ap*6?_xm9J*XNq{*m$rxa5|BI&t4STi!emGN?W+sCSbBc030e%l zQnG>~xw|cJnHE2Qr;%(_frcei|GPt?fk$cU7#P}BqeTquiTP8l; zohxzPZcpxw<#R|x{mV68WByR+(_m!VW3efq#ghxSqqg_ldZawcqHf*gHbOB3Z4q0y zgB!!vd{3=Q37Qlu)`ElCi$7A$-3lEtQ$eUu_Tb$}h7ye?T7Ps@cKa|OgaRUrCR-rZ zqupEx0SA^)$e4maNfkYqCqPL)A`+x|fT;ki4&hOUaTLkXs!REayr;Go7$7TB8@Wtc z>Cq$P^opMRbN79PV`A&y%BT&JZxY}Bpt(U2V78H7pSK*+z`HhKs+zbKVj=TK7s*=m zhOcl+k9A$s`tf1fPNqsf`GTW&4kGL&z#nj~?OpC5$$0!p)T$ou5yy4@=&Jcq6}P-c zr8iYLsFbJxJ8(@rQ~v;#2sKPA%9X)G^-2h6ojiH4x8E)AD<2;5-rs+9ZvDvbm1ncZ zO;oZoQr5FXw%P`3aYQ8}GH}NwlaAg#w{lOd?0m;iF356EChWh;--T!Te&LJ!J=g0y zmSM7a%ax)y9Y)rO<7B1`HRYjc)_jywwX4e&CYEdC47>$Eu3<6fz%9o|y1SmJ~a38_W|=ZajmeqbP9%j}~3{^%61!KD2@bYhK(t zNV`>s3KX3wR%htxg7k!tDzy5W<7qsL#y4AiwXc}GTfe?rTCEhP7zKJk{*b7~d)#ax zys$kYt_x@EX7g?*Hp3yKAMT&0MZ}11yA|S-R4oc2NmW4#29zN5AmfeVVTtrUE9r&H z8zU>pI&Mx0EHfikqM=6zw-4_dR#igTH=)EPz#5j6bU3WHQ$=7O4O5Ej;#!XdpWba1 z`c5UT^kYJW$H&z76w#7FeP)!^m>%(nnRu!A+eV_gviA4kDy4c?f{6Cx)m|ZP4MqhN zrD5zCO6Q&cy~#{>oF_G-lwrn>|d0M1h43>E^NA4M7WSqd&8E?2h3h9liTgPN=C-Voa37 zRVJp0Lg*{pF-)6e*@tOSUrN^??)F?T2axhPxuBnE3G=i#=U{AkD!JLV#4&WIhU)V| zM5NSHCmz(H^c+9O?aM1;)k>8Eo9+%?{hnNs5YGXi_5~%X{Z%DJ!V0>owCRg1mm+~s zonlX=YMc9hC2Ja~+D}|39D`j~hCRTpY5Jd;=JL$@gTMqzg#@L~-BCkj3OV~mIrn{n z%hFFZ+#R1Cct$jF1pcK|Z+qFOXXWB2TdBx8ejlS5hC?tKC>Gs~+fIMH{t7vfHg(zZ zxet|<9IKem6-V4YLauPy$8O(rC3M*dhU2MN1+?l=J-B);s?^Zm`}8Rp6$3o$i142| z3XxNot#B=JOq4fF6(+nzapmBPo~IrOV70)ZUdNY^PLP(*XLbPaM`sm(e=a*x?t814 zBnpp`x7m_As_fYZGwwz;?#akodm-gP`qw^wXWBmqfOx&%V^`Tv4#bhnUyx_2U5eWZ zepjbm`t{ldK$c>)akg%!$_NJ_+{f$eC$kUp?PaF*jpsoiA7vJIg^}4V-blvrguCh` z-(?rioJZOjYRxC;r+zwrst+7{H`C zD_vIRQ(vq|%Dk1eoxTKqwa@sjC^njBUfgs1MRhc-6-ps#J#xa;P3VqVqy$`H zE42={{`(7CICZ41ieN zG2dxd>hHE}d&6qVw%nO+<6*xr;}X;HCB~dh4r8ItpyEK?RP?!KR**?-_Rk*{mBZVn z*Sm3k^4z;k=Nx8bf>VtmN>owj#wymsxl*BRkbU?<1JFn-tMS)xU)nZJI3q`M-4aTi zl;=j8(?>UgiFdpZK_9Ao^eraL;|o0iJjlfe%ct?^2VMnr~y z_V;70_S3w}20PHWD|f@?WaV%9ic>En)VSVf{oR;+=Fvh`d2XXmi375xhQ7=j%R~|_ zONdi$G`2&r-V~6-hz>Tea-xvnApIjAxv+{+M}Sf7au*iv>W$^6(4cMG>34-U&zmXz zlHG>n`BI#?TGU|`tp4(wMO!EQoM-u7*kbOw-msFulEf-{` zCszt6+po=LI-cZCC(=~kwY>stXg)*Rk8;er6z1~{Aq*h)v@sa;CMM0Zg_A_h3qeaZ%0pGx{=+IIf{{k<2`yCGxc1}~dSYTXjhQF_}1 zy65!7WF=o{$6M{$TI&`X@(br4@!QUBT4wO~iYq6OL9dq0d)1%o#G##`L`Aj_GLqci zj;k*$V4pA(rtN{^h&jIB1cJ(btPtD;<(pl;DZO;xl2ps7@=~IqO-G>mFxFdu8dSZG z)?j!wT)XGp-MB{Yn-VqdR9H@cT!z!`w9>^IOMa?fb+z zyUS=g+ix{>w#ReKek$90z_98?3RRKq%M&c$;lz%R>W5kv)9=QCd$}!I!qzZVWcXc` zl8}ep+&uK~6|c0Dr_c)H9F;9{B)p^{znFE$JeJZB(mchL=|TlUQY%EHbQBoPT{TMC z85J4_g3Br%U_zBdI5epH4h)YPk7glprATC&gs6=2G{YyUSm{KX=2XK}uwud~)b?Y^ zX0nR(`aR?pEm65gVNH_LQTN@QYKsYL`^O|(&ak8adjj*utJ}PZV;;^#EDO9laLpuF z4Hf8ir`N!JMHqd>2Ns1WYC$1K+A#XyR;e3bgetkrWTyEQ(U+LQYxF3LXIqO&7QM6$ zU`rfg@BJF^BciiEAi&1nTIow0Glg^4C4B{M37XqmOxO)Ix_sloUR!50sFiHp}5F6zfr>;LK$a zNL2p-*3B1ZS}f1a#+e@-fjL~~LQ0jGXB<~7mN z4N>rO$l+R`VNON37qcWbddHZR0q;o|njiXQ3mZT06E&wOK_;Yj;vJ%@az-TPG2ve5RGmpZG1QWf zxbQJ2DRnAUbN$>gPC%kA=Vl1@)hfhIY9tT`Pqz%@PeT!Pkx(myiZE&jtfquz-f&WA zlnp`a{+1H+QvE0*^*eCRrFcfQ`Go%f7ChI{AE6-bnHu)}%^3n4)lr|qpm7Dc2{3A{ zTS0(b0---WGy=8nJu#N8x64e8`U94xR1fxUroTwVb3Wo&r*_+bc~K%(k(GVN6(Jy3 zxWKCNmXq2t>r;<;h+_!M89|wGpE(v5mG$eTK;h;(YFspqMMvcA{L6PY#1+*z&V1ZB zQc`FiWv2lD02#Ea9jT9hnFCLueDuF>MDA~aD-I-&5fn-2aO}pL0)bP3BQa2Ls~tpO z%A(3~BDv??o(h%?T&(0de=&Y(pmK`JP)D@qkDu8&OP;=v;bJX`GMlMZc~cC>yro>) zqsUT1Xij)3IwLu>R-e<;Ww<+*UXfQ`X}Z>#Aq4)sZ9TAP>2X*eciv2_YO*PDJ$tdR zRbnPGjW)Edl_U>-Jybs|h=sdVW1^9m~ac6 ze8qMbepB8uQmU}TwMJw7NXSbcr6(NSy6yy*jw`LUZJ3K68+{c^-kxE#bLIW55dveGYtpZ}v*hbs zDJbws%G>sUs)S0*`bqtmC&@F?l^I!m3nIs9{{Ys0wEoNw$uWEJS$zwCz-<2j#VhMi z?7)1J5g(P8(1cBf)F?H|1Kyw6f%zsR{{R)2(1!d5(mE<$ecC@}2jrNG_@ustx@?xJ zQ$(UiZBvhYn-H9oo`tvImaq7^e_DTLJ@QOJ_>27rMA_|06fMZlw@@eaVLz8*1CW!@ zj5lVsEB#!PU+E-%tb65Jl72--7xBWZteT4BId8ez?lgQU9ImV&hE?9`A&aW}PF*N}{V zm?#bBKSnBNcrg8o=so86qASa6@Z=}ZlSJ0zJtrei&-F@YcFNIwAxMZ?_p?$cY_r&AhF3$u%J9YP#1+RtN z)m|@@&vuL4rBnRuC-Q7w`TNhjylfmM=f}s_Mo;k~^8J6(%K`FCKr+|9BiIym>MGg8 zyMNQiGu^iS-o08c(RSs@R(THG5h6H}rZk{9m4Zkl<_0N!$9I#3na$!FY3y2GW+$HO za={#*32?8fp?vQ_C&*5Dj+o+zmk;jz)xpTfTH{Wm_NeF94(j8LpXno>F5`}D zurGHD3)Y>vd1eGwWW}F$aS#}BryELCm87{;kb2^q-A4<2L;7@cg#q-fEAsPeb$_}b z4<*cFgNQolH9t^aEs-1x3MtR9E7^`ai&9GZb2M~H-eEt{_Tc_%WEzV;{RfR+k&J()nt;PnFGHbURo6pC=DslGaWK?~v#uzgpyF8N*?wH5#1MFIV)tAr!{{Z&t z`u#eO#K-&=Fn^c3@J1WDKM;cd0IR3v=E|tO(SCT2pj_5J_?&y?uKW<4;U9=c+w}YS zcrV1OweExA3%kCOexwW!%iZ`UuHH=pi1sHHw}C$`^6O#g_a@C~R^|5K6Yn}Hg9t9i zB#GWFc9kB*^9A|0Tli|h>ni8AxDw>MmmS&m=H5#Wu2b_XJ@(&zP!7pRSpcabnSn}U zl6#*l?#nr^-~gxBU(~*ueqET?ZDvR~xz76=Mb8EgX+HCcL`z+IG=(3lw+1^9hDC^kN3OO%}O`Gn*RWrp@!oS@OYF5dtCSrVsPH}U*+v<*!#kK zUN_w_ZZNG=-Gua;ythMdwGqWOuM5;ZAUcX+j()~??4IiKPqxP5b=FoW-Oa<5Ij@MQ z(vzPg`e-p)hnU>x*H=%-!96d^D^!aUxxjjJe zcHg)%cBY$|@cn@G&jg9Efvxm1x9^J(M0QK-chCA;hzr5}|2e2X2vHcZ* zHSreduW$bF$o~M&W8a25#e4hCe*x?W`+FnM`luDdo2yrM^Bk}I<^bZ~$o*(Pyyx&9 zz>YPteGwp3aE9fQdzHCnr2hb-_8W|3!-4wJ2YOC<4`52HZEr-44E;ObU#IbIdx-rK zFh7S+_mNKVdE!3Ac@`f;=s$Pqvg}FzJ5+#203mVWh<9l|Kc~``nuX}iu#}_H z;@>~Ir!TX=L3D$Ie1!ffZ|=8xQPB(1N@?YKPGw0Q595-n{{WQ1e+`$#AN!T(#br@F zCORm6Cr3%k#&0rJLGCVKe+`$#dAph8fhd!^d(unF&(lH}sr}2Oyob>46c!SAqz>zw z#QjJ!uzeyuLO32nDp&4{w4w*r<{f;qlV9#@j}n9b0B3%a21(%=H2(nbl%x6{!+(}? z^qZ!CO5GfH=|3XDOm+>yktd46&`TZ1iLk1bbTJK{JSLvjBA zHrbeOa3joeS|hEu%dMfu7NQd608s75Io|6>3lM9lA$3pW>oqj_$r%0xj{1k_SIgWo zJfu|n203~YqWUE`_mqdmi+#RFtAXtafhPi#AZX>DY>L2Nl%>N+|rT?w2GP~(?Cy5cOR7_ z;~w`V@#1{@gwAV60(l}5c+1fWvTua98*2Xm`Ich8OPKJqlf@VNl40TONiK1RqY!*N zH+@L#D%?|lQYhBh<oZ<1La@hgZrZ6l|6{_JUi)Cr2hc@G@Rm( zQme(t{)cc+E#sOY+%q01`&>EcVN?f;pe4hCNbvq5N(BD<^<`GD_5! zINd%{#WW)4av%I|7d~MWsyj0>16rrm*NON$bw}H_{lm7`a*MU%Cf~Na+0!cu3JFqJ zLcG9q0&*BA1-}l6?8!Lk;*t-guT3%uDcg>>{6RM<$?N|Bguy;?R`X7c^4%Vhy*ViB zO|nuFdL!{kfAEw}3-j}MrH#`M=upY@f$70Sl|`1>Kf`sEU(#?Nh)v?1hsF9;44+3n zoYVmj?gSo(@n`Ut;&UT)#DzzrZ%$^U!?^$lQFea6Xuy6aGE=4^f+!54% zCjNiuB85N6y8=}t-0dUc zUBVigLR9o_*P9@z9}px|3`=@!nFEaR4G$6F>0N!m;5;18kSsQYx( zaE*saXfjq7g^Hw#5Naw2Bfl9qtteMXO3!+zyDb&UWf~}T_4;uhBKN68?ebBCqZenw zMNFoiVKLTdI{X zFw(MU4w+|#vKK_Wgj^-1BkDZTYA_p67k&- zC=^MzsZ$50t|h$lk38{y!+~dZ>t>OBwaDKqKt5bs40)}+HEyV6khV~?CcRWZ7@gag zxvwso){cB=tDDDi+TwAt&nv0!xHrVOiVAv(1o(Rw*_P+wKSZ0N@b$t!6^jZMl(gJC zY>9U0-djULNM#_#DDSF6D`6|=OOW1x9Pjlnp6J(ewXp? zn}jC*FB9!UwdvE*np6Vz18yh1R|}PY^7sz9{1A+rM+X$Z<}X0Lgn8#?U3R**(_0gD zzQWPAc?Q!mN^X|9M0gfM?kx{DT5D~?YWybe(ybxaF*T1X34x=EYj`_rAcN(mZ{FcQ>Y3ggsiow0!~L9 zlX};QT-OQ^NcI|6>EgbhSx(7qnJYLwt#I`?15d4MZER{yejJIzn|c76UOhRNRW_uj z;ZX0UBo74)IMp_jPcdohLVm-Dr|49%;IIy7PfU1_P>{DOsOf^y6a|b56@m>310mN1 zq7)JC2RsTo5g-tveb@v?6q_~Vwt{=AY3!yNld5$C2`lOc^85U}M%(oLvH4H=;kRzQ z?PRrX3$!B%)EErNXvvEiHBk;|LzPmhDo;U=&}>Kf&Bm$}SXkmE&W9mc~J#3TOzc#x%3 zA&o^;Ih<8@F4M?la_qTj05X*N3ij0QekU2Xh2OM*StP;osZ29plb}`s3gRLhS>kwdCRKN2B@_^q#eVzChhA zr@!W4>ic66hmFeJj!HTAcITK})coT;H*il2dO);pPAT3O%jV#lyDYHoa4v4eb%PP) zzNIXA5!xY1Qd3VAF_w2~=I&g0F5ny&Lw;CW44Z({k3+toSIayVA2CTMEk`eYIhYEa zSI{el=$2FAr?UzsrEIQ)vbZC{0E$Xb4XA$c7(H}LLN#ANf01U%^y$BL->GQcqT^3` z-)y*TdPef z)T`6NgRW=EAYpMPSHPw+?tEGO=FxC>^XuY2Ok8IZ(w5;J#u#9 zSb3B|b|9;bpdhI}X*4<0rdYwd%5OR&bi>Dz?Jvqz{haK%5jYY$cpp!%(!PCRI;EjK zf`hle7))-cU2Sm~s#Qz923S=z=tdzp8krqqFqNR6p+gG9?w5pF#aLB=QmaCfkpTUb z#kL|97ptHdcJ&xp=Dbsw0imM$Z1k4vcP~m_p5J{sl-}01A!-7dvV}Ht zAL8S)JAB~$Om*ZJ$NvD#g}+Aex&wz<`&UKyn|Z{H$}Bfo1u*20hSQha)KCye#GY2% z)2J+i87l|6^w{Rzy|V24rYvp6v*2$i_b9260`DOJIY6@7(WqyZSy#6STmJwRz6#BW zGg~-@JKa!1_OAfGqx0NaSwibn#<bQ7E$B;#eIS8`xz$Nw|E~EV4rCpseL4R5#z5-JDbrr zr%l7X_~lpK>2tHa{LOf7;XQ{WNM_|Fg1R#55CKX>00S&^zQ>Yxntt6vk`QsC~p&;4&4Sv;$syt2cbXyOt>yhG^cRYw-wz&)Rf&HZoT z&zE!el0S{H6;~2^I!DLF*HsSCFB2300LSCrUC-V__{%>^p#K2br=wPtRPlw~C$RoE zFZH{D5#0Ud=VhThzTeug=FRkg*Tcp)l=f9O$z7iJ4i{{92ltaV-NWJb{?($kr=%)> z`2&4x*e?rL`{f)r@^1W54aL8K`u_lGCatsR@dHY>PU8tZiMyZcW8OvIib!4C*N5Bt zQFF9C8m&4`*4+&b&@RY7q?`xHUHGR$_S|y&e`-dN#$Jo18kl>wHm>RIE}vR(Lyo)g zP9x30IUtk5{)=BotnJ{)_DW>_l;A#A?K}K_*nhz-T0J~^9(m%qTeo;HgYmcXY**}# ztUCECuN2e!y4;#jdl$?H=6AHX&y4+P@)q3T%dFe3!TAdu%X`-cjEN#dyLwk43UO#c zhzkal8FcAPakp^{k>+04aM1f}U$8H#eUF`&ds-KP;ysl8tLSIZPaFC_-(HFRH*QVW zv8PPm7Y(%-Z%eB;YlL;kxHRjGJm!=+f9%(zfB*Z%w0{im01t>?bk9rfn`3P{R?*N8_DPFG>D$nfg{{Wb4 z=Q!=@t=GiM_gkq4zu|Zvw-23UX++um%gBRPxUZbA&U0)g;ubI`zQZaVRbQ<^qWqflgymhtY-|X<8tPyh$YHp4_s+ z{$f%vn%C5UGtKGdq#F00n2w#wVm7AfG$Y&DhKXPiWvyA$D*o;hdDT0OeFFYe@Hppk z`VjTuw&!h2UDuPp8G`M%aY~0B>y%q+>?{@$8bgULG?5{fRYVa|Nyf?BYZksKUhQZ_ zJzXR9)pIw0Z>Ngx*{mcr%{8Yp)b2k;7dPfTrzEk~}Ts)y~v>#{n^ePqhQ zJxaZEE^@5z5;T_93HF88v)_#KbNoSTJ1Mn@pg8IE2$Rq=8jzu%vkPEfMo|-T4|pqG z>4wt-c>12;U4?#q*n4YxZb+7`#W{r~5493oPKvgrg#?{pBBUIvi|*XIba!p1hzm8} za7QK*;pay(GU4eUkJVN5wR!Xd>CQBx7AEDrv>GZ(&}YS6N^&Npk&cawIegb|aPGR# z1l619H-X#K5xnn6KfOsX&*^6cqmkd-YnG&idJ*8hgL>ac?-HPo{v!cGgZv!BQUAH{T zZQk8;5KByt!CjV@8dO%BRY_`uDx#e!j1#{Sxvhqyi6j32l%LeQZ}Nw^pOTW`ID9P! z{{WZ|*0@z}kV+7wk^-w-bF6L)vu-1yO9@#OKM_4ovjllbF-m)8%Np~>{Iha!ujlro zcA0Q!Mvp1^W;@!G+eFfbvld%RZo4NR1p#q6j`@R=suVuUsyUvRgo# z5bvpcvWvSD^$=hJo|3si>qg&i^l#1a_-gGX{y_QL1+M=94{}CnhzSkcq)U!iTP`~9 ziL(&+g0lo+vYqlmll@mr2ssAHC1)LQaeS! z3ij2=gV##p5%rY}Rfl;YR06k~f{oTLWhM(W}-YP_P&l))(Q_CBc45(7BQjE)Mau_!#Y@wE&ayWG* z)HbywgsDlWNg{yea5nqy>`7N05$=0yVCcweXjO<`J9>Re%9`W7>QJoGhi?`df!$Ww z7`l$?M^Axg(spiMujXm$TmJyJo&+FyTxGh+{*~t0{{RK75BsSX+(tc`llq@hM%-M@ z!}pst%H3?^>+Q01wv>i$58Wa-mXxY#R)*2yr)Vb-*-qleG-aWw_A9kBT1*|+RSbVJ z?Z(A^PHKp!`o=)}F5Il6)~>=VrXS8(9t*@m9ZC^b`F4% zN}Hs!U-WSz{Dq}H1rXd#^?#Z5BFmSt%>dXee8B*jQx@|sK1w~aRn7BpVd&r{=08lLcUddQZF?*b93 zFJ3uUmpF%w?{OHgTDd0qZ?oL5%muvUB+qcR97}GgA!I6ogpRnd?e6E?UEQ)L0Y@4t zhV$(0rZ*Wi(;P{Sg}`{zm!WqSU**rVqq^*6^9|Ukr4UH9KuE~bp>=)T_~}+*3z`pK z)V_H;zXh)&<;I?pl8@!dw#hzUGTcXb{3aRw4vZV{5A&n{0Ken^0O7~9ua!;JC(qn> z2NEiVm7skIr*1xQ-#@AA?RA4XA_+CRl#+XJgC9VZaYFeWu+M32#pmuB7hHrjZsTYXuHuDwcJMb%1B3U^9X)YrQgFUB6IgAU&WqYitr zi;ctzd1$7h2TVGK@<%3@HjH9&LXRm*3wEd<@qM_GiOx`05)MjO+7^jZTf57S(QsH` z%DBs4Vk5#wnzbnR46z|n+jl#_D#Y@SnqxlkTzQpOj%0(�$W3M{{T-KM`x}2@}T+_ zN8j7AZ?|6W^AFa#$v0@rN|X@Bx@2mh*By;KL>2Sq-z>)p)g-HG&nAK(DL&DJJhGgA zFwYgB+B1hpNA1F$1sCy__^s2nCILeUe^J2nApBvTD|Bs%*&mW!eZ=G42o7DID9GBk zBhuWz^G+4=5l&)16gzuD>OfD(EFSGA3G^h+T0gvu4ViR5`YBJmQy%il_v3MW<+^r6 z?5i9D-<}isbU%(q;)PDjniMM42h)N1a*;Xs{8EB8qo`6!np%ME123rIZiG{skHtAH zJ881^pO#XMd$Pm0;;TxHImQD@t}J@sS`q3~a==5Hg{}pyg55K0h7{#1+ws>NHQgsy zEbl`4YuN!^xf{Gm{YtR3R6y(_GmQE&wHKXI)C8Ai!)v``Kc^1uTh%BDwZWqaR&Fj$ zP-mnJ{)}m{I*g*tCN*&>wZC6&orSmxvMxUb2o>s7^?yLb27Xc@G*%t5!e&*uw=%6d z_G3Mb#H|8GRSB-73U%$roa!$%lv6LK2x_}}f#MrVL2+|0@WW`OYvqMLMF(7wi)O?j z*+}c+solyZJRI)uVf{gU3S9QYm3$=WwHd^X3A{s}|%V0x2Jo^Of}Y|Ejpt6RhW00ytwg*sQhdA=xC z_I>A~ZEue6x>5aD9+l&K)dBj>v)yc7m3ex6sEcKlTTsbyO{p|8B{ay)kLhDL?!=*( zSv^Z>{Jc0zx#6L&ycf>>z0u^>wdp~QE$SUh={&8~UmgWYTjThylS1^VDmix@BlKgT zwj*h1Ju93$jQ;@p@;oS{8;!s5ORnCtj<>s|t_^#>cHq=s{xcREiic>cwkE81p``l( zKdF5z-8jGGkk$z+h41h#Y!uw!t==?ESR=jKG0+_?Hm-Ob4bF0sjCi<&0t-`^tt|#~0pr*a`hhusKCM?9epupV?qcHb9YAqD zo}<(H*U!{fQlXfodiycXLPgp#$3&y9we0RVa5YWC7mBD2O>k1`1RHxS zA8uBElWytj0nG95J~WZHC_}RDmfb5a341tQ5j_C8n2xX%#I7TZ&zEc((5IspxW$dak3~uTte^YRycFI z_nldXk5}toVLvXgbZr>DZu=UkXh#7$f$L7pIP(%6D=Tn{f+^fF z<1+IYlVv?bEhjKWW1+%skvfT*ucjZ#gNTOs^rO9ZF(oVhcDTgyz^J1(*klq<_}J3)ANs$y`>qvt z)Eux+$4YPgs(e=C)5@Fxp}?N-Yk>T52llz~O)g#FS++h8<~#ayI-3^Qz01>F=*_w1 zp1$lc+_|Q@!Ql86W$o_QBQ+2F!|h)R_P4wZ4)h|Q=8SM#-_-uE9aq^K$o=E%S5NZx z+fDB;T%MSA+m9AoxNfNGdDN#-36Bm$lR#_#02hls&Y1oy2mJp4{UrXSpZ7oi0P(B8 z{{T$=p?#y>xJKHyTu;mr2_~yXW~BFHxCU6!e1NyZ@gX$a;45jR3w6%9E4K;$F7&_P zMJBz#(86jQZ^P6I;C~i!PD~l`Nv?2gUh$1+l?uWh*9I(W(b zcyII6;)*faqsim96sj8&gWIV7+#~bp_sJv8apQ#4+7v0%#BuMRUil$^uy53tt~`+0=jv(^=JbPxTqfByhY{{Z+(``cq> zMpOvMcHGiIKm>#VOuI4KjJqL&)V?;p%szK|%1(1)KxUzEP~uh5L;^;AGQwYoxjg(N zB=`0uq4f&FKq(YH5ruwmq`wUbCH;Y8CWZ=Y+f2Vk2yb4JIJP4H0POO}6;#1U9o0X# z0rS!$ejdaUwhfgE)`%%2dsKf($G&z&@59)(PRq8C56KU!0rRh3{5^|dv+YPw)d2_k zc^oD45+#MCXwUMkiG+p7OD!Pv3(}sR{4UVyRVL`-1tiax##V5rCP{{p*sFugO@7QA zi5;L$e5jLBPb=`8DkPLTv(zAu)q)#2Qt`eY1d6VE;bxVfTHRig@dTVL@-rm7$b1m( z^Y05N=VF+o_n|63rylt!JX4`8J_~>L;lT1r6st|yp7Z6t0Q#wh_ZYc6Ligeja|mI} z+z{%AmH5(BdTDL)KHA|g!{zY{-#u)z{?fRe(W)HV#-(TFt(B&Nz3T%GqsPkQO%h)< zY=>u(cKU!#rVPp8_B!h&SCO8$B#fBY^R`3)Rbgmb*;R>_CWb0`tVuf9{z36XOD0O4@h zNgp(?;rE`8Y7N^DJp|Q`G?b&K7@whl~9+CT3%}3^$=u3Hef8OJpw#ODr+59QLo(AR` zLvBE^U*s;Uj(nx4vdon$Qz1YkDyRoEjC0oi0NahWm&}NJrF{tOZ*Wdh$K|JWZetuP ztD8^$nrp8QKr53_fl`X;l51RMVQtq3N+bztD|t!>B6G!d;B~}^v+)X# zqaJjcBf;?$i-Z%k?0I<#It=T92!%Rhs4-}9MAurWPp1=B-s~kqOggRrO~U0#%Mis) z7SU(S_Fxh{2uf=0>4Q|FfW4J^(wG304Wcm7sUyF)3bph^DfMDc%UkA6a8tvQv;A0| z#*UcFm18dd05WEJ;z)mh0;FhQz&wE?4d;~MYX3*G$f4QT1q<-71U~bPh?4U zI^@xd4en2Ot#DO^fJt%pYomYWr$0uN=6HqlOT~VR`1gRUE$zN8+pO)_p(z&|kt%+4 z*Aki+WJq(ywI6Aq>;oIdGSb${KA782O5r`-x-yyDgDupB!;s`X0B{OIR~OGhf%)ZB zbT!0};wq!~YfMgSunol9#iv4wMf{_+9l5 z-j7kd?!fq2-n*R%KF0475I;sZ*KoGCuRfB#zU@|qVeQBN0F=-CAv572C?C?pHt=4# zc_l0&s-;b9g~z;^l-GKJ=Tlt5mHKfXC&WURkKqeFC@@qDIP&t%DsmL|W6RWA4QVEz zp7D<$pg@z7^UEp>0Y{?Bd?{5$YuS%c0o0^<$kc@=_>x61Mn|fB1E^}g-aQfewc$%w zqfbq{b9!2D@UFI9lVOEE_|>q>g7fiVCOVed<0(qgbl2g>Sncb1<*;Lig88Z4T#c=^ z^4eA_`*s3j*c$=Y_H( z+kFMiyNKFfjl1@;Q0GvTxiv^R{Wzp|Vsh+Q{{Wf4Sqp#svD*l@83X!n{{X^Q#)EYZ zxt2v|JaZu8zJneKXjX#SjE} zC$m$I57Ul;lRGNmsJ47G;FDb8ZIh&_4al!ze8pGvV1qp>Q0(}CN$&90*QhNqAdhQ= z{;WIvdPNx~(u@_OJSAz8@&X!=8wQ17k}KD#10MW(M2mJve*#w~zdHUWjk7&I@gCxg z99omR{!P^@FqFx$&0k)*sj-YZzLNaKlON#k)K_YLO7Z5;Cvyok20D+S&0jfCUI7h& zYvBNoqa3*r=@ec4a@}f`lCIwT4>Zh#X@We&)X;(t+l5--9jZc6qMU=C6nL`Gntz*% zOj4?Jj2%fnQ|=)kb}QmIQS$Ih#MSk$`F(6_F6;Cu>9XxAThXo#*w&ZUkdTmBxJYJ{ zHL39R$5`wu_cIv4IBLFM{$)1wa&Fs$;^k^1ain{Eg5w*9SqCb#2TyXba+4_M3Q=J% z+R=^$JU4x|HubY@t2OUKZ|!jKjdg1$WF_DM3koDqX&7f|IblAPCk8l1+9=U{No}do zHlz1yntr@Vek$(-@8v>pSvYz!p0CZJEmQcYdiGNb45>|@j@5j2{$fx+0`=YMPSl*l zO~{`uLbRgn&==e9#}MynT&L!*=`X>;4bCXGDOpc-1a$<1`*5e?sXxNP2^>pp zHPtn0_EZnmkA5rYLHJl%JVR$RtLrrWnEjX^#ZrHVgdh7Hv6_`UzoA3+W8aD-zY7af z!n=HpMNberH39mtKZ>t@77z>JU8+xsG7?X7pWwrhltJ=g5nl%FVn{E_K>q-gt$)$S zzZ6OS9uP-@wyD+q?nu?!tbWW7;;Ape+sq}KSEt(%cm}Srf&k2FLCgH4;2V@7BlrYu zH4EiK^NHEfb{DOi?V>CH05r^vx3*;$=}M{S?*sn!nB#8i%w3#+fNOvHm)1YaRCL-= z4+lH{0Ht>)^1iWJcXy#K6uAI2LCd~8)2=xg7kDxfl0freUZk=5vC;jexpBiVSAbjz z`LD!=Z#jwm;19F|`qki9A6Yy}@?AoPUlBj68Yj;c&EJKEqImAiy6Fy@b@3nBguZPT z@UVm&WouOh92M;=!aq2J@HDM$;&=*tXwMTRconj+~gpWPhlA4p72f^kpsjpso#`nX||r<2am9>Lq&R_B9*64s~H)6 zS4ztssOOuf#Y_PyKPh!*@>Jp9_U-wEaYoyd9l16Qxo+GSz0_T84xtcOxBeOMIuw#i zvyn|hzqc1XjR%``sHgy}%e$9`(+uZ>LR{ASYJXCuoawYA@n1UgcNfb-ns<%&j_0wq z%7qAzZoJ51Ij8j42vGJML*;{+bl`kX+O8N-$Js>o>Kw+Or=(Zws*KSfuf(HLaU5Sn z`bfguJ6eD`b-+gC0hCCW!U|$6o|=caVF~0;AQeqzER(3>Kh07Bv_LJukg5*33?(b7 z67bfmwEqB;#Pv$I`ls7Va;(m`*yOFF@T>lK$4|2xPip6^G6l|mm`hM3wv*CF*ne85 zyTP|D`?qjFCrODiLu61=sC7dJy^@lUjfZblyHBA&e7vf_w7ip;am17FO@CG?TaEnI ztFv?jCf^^*30%hY=0Zrrd?ebZ+kzcIRyT&GHyfRJP`KinZT;c3hq)mdpz|Kf&6G7{@5NXlwX~%7Bgt{m9h6cM;>%pcg?d{TdHs*OtwvnQa!q< zj8nd^YwVdJ%~QahKlO`2~jH|oudyEMDgRR*t7DF72ZMe(`VfqZd#XGY58m9SL5=wi?@%?J22N+uNx~`UISE{{VTV{{X!nk@g?) zUqk-@D=z;4*radWkbPkO#l+~QsBVISnSEHrE2`;aeYz%z0+=!cWG;e$W$(wAkb&TO z)O|QVT2stK?qv&wBmt+xQhmn+d+L;HXpA!9S`c+*LD}rUa;hPIsLEU+B|U4~gS1rv zkz>rNjYVmKkwiXXGpvk(6z{>rhru>6gdIRms_O1I2EK(9M7^x%;px{rE!3AmDMZw2 z%dlhJXaOh!uxY6^r%do2R7L4TuB$Fu=QpOLwshy+o&n~8D--pidGMvtR!&CX`}0l~ z6=>btPC}Ll$Rlt>3hVt6aV@rH>AsBr0GNHM8+Y0Xu~!T_)+s;mJN2)$>(rMOIr2#W zt#m!rQ8@Y^ZhwT+=wATuVCcDff6RW=mL#}8*HXR0nD?5AK~O1=7xaj0S&(FhAK&kD#> z>NCzLAt^p$+$l;E?NI|0@km@i3b}gLvNG>Nv7aFh)Wb1jFqT7UNO3@Br2&~I7kDG- zZ5Bg4%cOZF0NbG}C0PY^S7t*GTI!@})K$pXyG}K`(4VeeX>=c%wrzRadvD1dTY31+ zsnw^rmC0>2_F;V7#vDwK0;YzF0|HxdyKNveBdiiV6<<6>LrQ?mb`G5}%X2%a#djnl zMbfXzpJk2r>IZoxz~E}bwdA^|TNO}LiLA1bQMZKl)r zpY{*&UFwm-Fq~S#y!!s>D;4^2*3ykv%lLEhBA@Io9<3^U)OVi=!+$M0{CY%Wa3#G; z6uBr@*iHwbHvE1>pw|M!qC#$f)34oU_MCV|oBlTRqMibZMN@JKKGy#LW(TAp{B7w$ zt^-M+MZuDL%V)3AhLSkd2jg-hX!QFiiluGH9qC@0{dhN=n;e|Sf-v6z$V$9+Q6LV% zBoEU}IQfQ-E8jIb8~un$j!%=aUzA5`WHLeeN`@PohNAh?o@o+C7G7fosh%OY$|&pQ zzYy2_=SB=CVsmQyhaF84D)wgPV^Bv0-dxkwDfv}b>UiKp?Fw94J^Wq~%XZt|USJIR zcE3EZXb+Vb_4m0d zePb2v&hjnQhifny&!8&o%auVVBbGD!XOPvQx|Krtoih7yGL(rLt3~KpaoqKVz%R`T zR7h9XpLq#FoLCLoW++h&g(*i>G6NRw-pAFJkI+>IypJv0H&o(TPyS}FsQKHj%(ZJo zd9H)nZL>yx%C(N)E?@&y@)9d+qASwxVM+f0&lY&~t-g7u@Z0780OqhFR`Ve&>wBNN z?_ofzlI-YJQEa$WbgT(q`K%9|&k&LME1$aWVL>^{mVxoxXNq>1Pmj{x1I`{lS_5Zs z_fN2`S>@|O+(<>baRaJEH|zEz0~3deXDyZ3{nPAK7xO9PC>LKvJi!J$X-x^=f;5Q_ z#(kt}RqiblsYyr#o|Ws2$Gq-m;I9PnT^IRHu%G*?GT?Nn_5!|Lt-sh0aK{@kuc;Gp zT?XNFCB8SY5Uku>g&eyJgnq1aUdkW#L2}=4-Twf%l{0beyk7Z>g>4J*H#2(eL^>)3 zP%rUTuD#(W7AMD7f$3GpT?lO?dF31awvfK9=h|$JT_rf7QroT)jd>cZn&sCURg6vz z9;Nf@d&<1&%&(eH&vSR$W^)Gtan+`3oO5bztCiC4ZVDv~yLq^wmK150qTD4@v=4qc zU%j`Eeoe8~p{+Ibnp5m6=-=fd8e_oAITY)70Q!L>ePX{t;w(Si_hTSqXu580DBG$C zY?JQ7#k5H@RIb*3LSCNjJjLqAdGapijLhA?!Mk+2#Ai^I*6+C{f^V(f-=7@y*lHCehmP zkq$lTHtf9IP}Wt;S3w{j1C(mAZobQfH%(#v-#)SJQKU$)GOz|c=8>t+5wZ7V) z{+07z7PJ%Ri5S|S+)=RZ+_B0Wf5r9?rCs;s+8wqjN_CVa z_XnEoJDO-MWP33&w{g$Cx@<%Diiq5qXz}>@ZlCbR((l>rG#^TF`jX@8R}XXNEAdT^ zQ}eAG0`l#)-!e%-km?$a%Y{xB!bM7!dJKkGKIF4yrR$+?y(WnrnUy&VR5wIW$lkB4 zkJO>Cv09aC0A@R}6ID7|tWTp`P1kdJvh?|3vRmA`-EMoH8&s$5T&vQ~Q?R{{Xjczx^}zh4v>GgL!qiK0~`byF%(b^~#Xf)BTZOOm<<) z4xdu_Ow3txsyG#4_P-X!foWll!4@mmP*0w?={{KQ15gLwhEK^7bW5B#uLQKNKHK1t z@+lp|FWH1Y6DZEaunr3cjrQ}xf-rABw5|->(i307o8Yo|zixLC^nZg0{vcDI!CT;* zc5W=%#H^PbLdV6PUuGPevqZ(%rnIk!f6diQGx>~qhP6a+yB5Os--OeI0Z@&(1@lcH zbS|86-*Dt>^0E=cP#?GTudAJuwDPvCy?=N({dE0Ggv4LPoqB2du~UbFy-L(@5)WD& zERZx>II;mXsq8rN7KE7cRU)Y9r!oiKf*n()9-UX%kK`$BaXy9j3#EeMC2y_3pt1h| zdyf&5{`VaV+F7e)@A;40MZ{m3upOIc{{W`nv33FG6X8N)FDlTUzg{$XB{f(KP}Kk- z)YIiiuW7IB!hUOzIu1)eD(VhVbyu+9-fBVckfIM&lOMKvK=nCsdA}v7dlz%5MwJ>x zrT)&`MeZpGv45zmFK(M$+ylkq?O&Z;uOu=CbV`5TjB)G?>1{j!UsdluBDzEQNkm=O z^C0xE3?ow0dAhum)}QlzuXA*Fc7cm-!-dbwLr>jtC;pN@TFiUI;N7iuf5<+!HGR@u zthZq42)C=BFO&Z;BCY^kg-@!5{m&*{AP$!wqT_hmiCxo~`*Qger3V+a;w1z3RUl{f94I9T86%OaihuTjZX1=aOI#~# zzDADN zLac6gJMg*aDtx#90O2+Im)(PV_m*PBZ7y-^4+IT5km*uD01l>=IPF%z(-`$Hk21*N za_V@L$++@gDY)YFY*a9wfOx-Q$G%mRLhHAtRL)D~bhxD_YNetMY0$G@s}4o85D6ZU zCi2H9TiWyGZ8WGVx@yb6s|506Uh8^mJxb1WY?(w^{eiLM*ZzTLKix}71MR`Z+<_+R2zi9mnZz40G zl%|&$mT zho6c_fa6$_SLu%8+N&FI1rGHsWBux|pW;sNUKoas1t>@K`@CbQCO+p?I_MIfK%n}4 zxbeStfZ*K&O1NTZKG*# zEzcrO&*Y2TSW+!dEr(F^&|6i_yuwPnC5>4T(;2UIE@N%DYeyB+Kb6c>^JQH#kMi12 zw@=o-YNCH;quESzh~~S#aqlFDBt}c5DX>z0BZvvlyrrX!-7C|4w_=5|dVAodO{p(dkn1i?!c?xpgPNr28!YM>5QV0qeD0bt>h1#J#$KQZKUlcPb3R6i))U~JzXWD6n zqd_aE3YO-x8WoB?wQ2}lgw;p1V&3SwVCnHxSS56qCH@LIcHbE!hMd%Yc|hfekCh75 z9pkx$eti`B1m+J^cbL1j)v@1`#1nD@r=FR3ubaRzpJB zdp1kCL)<9A^{j`x9oM(PT9!0}a^QMEI=9k%Jp+$3r7QJ+w`6*j+FN$bwKmSevTdEe zx8kz~#b|=-O*0N8cEf0G2|xf)4(xQLwX0!ZYes>5>d0m1yP48RYT{~wabBn;Ek*lW zG$mQ6`KVTB+y)ltkHInXr13@7WAzrZ<7O%W_NVsa-7)wk&hw7~hfdVzWoGm7$8|J} z4I}VH>75RppUh+Xdz%dR=09#dNO^_siTqH%$~>5#?Qe~h9;T=D95%%w=X66WHMow%GOJpfoA zpy2*4#Fr+u$t$VQZZF**PssU>H>--0~WGLL5ZOy64gLiHHIkLqL81Y$|~ee1c? zsP$DY?~46BamTsi{{VXN=_~7w*FXC`x&Hw2+4d$RUY&9nV8cXd_@!9-R=NzU*S8D8 znm>YU$P|+xcP&_tm*Xm4FN7^MdoWs~bgl8^RqT0rc&$JHO-4L~3ZROBC%d-*j?l1j z`myEUl4dG}C%g9Ghlo!h3+-R>^z`dx!J$g2=hXXHec(pS-e)2GST!8-otG74X6jg2I z+nPZ5TU6;vPF}?=SaLFUqllDD(FpK5K17Fp_osUgPF z@-ECWhcpBvl9J_fId#Ty-HD)nDZ#DO5VuduTdtto@rNIXID0`0;E;7tIt~ z_E9Uy=%mZIrhWeas~b0HoYp>18V}aFi}PI_Y;8yRpY}C}y%@1VX+=+zd*vrhba{Ca2&D(pgHNfdUJnKKCHY^xng0Nnzo(@rf%1_r zan!EeOl~Qt`vAvR><8Bv(!Ov0V*nebUri6x7057~3nj(r)!Gh67HX_B%U(^XhXzjP zok=(fqcw>3d_TU-1X_)wNX>>zE@Pb-&tU%fJ3={Xu3D#y^>O!*@A8F~|P^fG&>7 zd8Zat@+%bTppl=|OkP$N*Hsi+<0_=aaRhd0t~`#47h%?_ZE2@?BlKWVUdpXZi)hT( z?fsYppOk_5t6BZ;w+Bqh@t14@rOE;A$B-aZbhD>FFrNPa5XY#J^A^93T01L;DK+oL zt-z1&t|)ts1uNzuetHAI1|c|m&s=yU-0G#9YmNNEXcmNzg(LhpbP^ITk)O|ng~s*A z0o9-&&)Az`)Q|wEZcWBMbpHU8#~pWuGR)JDBG&z`UsHc9PY>FR^gZ8WS7|>b=?u#J zzxF#tY4ag&4c5UG&cBQ*K(El?#_`zsi}2d(M?3!j5nNCC#`iaEX~s5x;R~m7BgO+! zP)d|dRZ)#Zu3tc*h$S^!h$`=>$EdutE4C#-DP!NO{5VX+l@e7bTik{pX-&yUAo#T? zWImIH85!h}^BTTm{{TOjXR-n;eY_z1 z&U=s1MuGL!bN>MIjM{E&56qj?niR-x9{QDM-4@UWk>y>i(b-phYV=nPeYmD?IN1h9 zMvf$d?bG!wN42-c=K%xH?tZ|QxuNq@??J^Yac~P%+P@VO2VuRYW1_UGMx*rN8w7pgrn8OGs&?5| z6hP#jG`Ou^Qf-@?ZObn90T%fdMM>`PV(VcoCTUd*=KlcO$?0RqwT=t#k8wz|$bHqb zbBwPVvk$bOpiMzZ3Zd)Q9V;AfEEHcct{&FR;pt50-nX`rp*58O)E5z#+kx`lf%+tc zyKi!dOz8AiXPn>bRz2mpBh-a$5K^@2l+vry?-BcOc-ZREEz;G?zs|PV8h_>Wy=SFp zbl7($(v+1YK4u6A_wm=-}p-S=(W6&94D;} zMmc%{yZYp6oC#S~GSg9>9CD&(MPWTVNY%M_WvGxv^K4(DDK+X`iKut~01hm>S>G7( z3aPu816c3#1zcejc}XhF&}tcFl`&dyql&mlDl|$daMQwa?7_Jv^wklWIm>WG#gFan z>P&_YX0{fWlV7Dcdykw)C~@EunJomZ=sX>#w@=o;MjKZnZQGrLY~0(qGJ$Znz`#^G|-M6o$d;AyMl-pK~*#<+X zQb`IkZITcTJ%L!~{`T4Q+%iu=9@>7z+@F-(gI$iEE`3ctz~TDB;8bV)C{m7NvmE~b z8tW$tC1ksp>4j>P@{onVPDdkyBn!1$1=Ao#00g6TC<~IcTjh6znv5p))h+@SD#Dp- zI@Kxt7*;QZHh9$@3!nh!fi5e-v_b-!b*HlgCaF&?Vzr^AE6`wSh(@bA^3pRH0vCaz z(M3cNr>|}Wd#M6RIi9!_R2tN4L6>>Qfdh&)G)1NoV#*ytn_gc=6p-kM!KwPJ}h@IfR z4~~@~wSN_I_haSsi}XkkiB6+XXpz?mQ3X)yT2Kvo_F-s-sTU0uvfH~_`u(odmYHfZ zK1*aN=bCPnNHrjmdvU1Zn0f-rGWSdp8mSj=?JtqK!MR@d1SH0`xE@p%l;f*)ZJkL? zKmcY2EEW)SKBZIgcaD}K#)S2{?zW{8<0(qkPi=afOv{ref`GNbvPD5@=VgAewgMG19NiRIiz#XB`8_F*?=2}b_hWm3d_kG0Zrn=M|=V19vwos@EP z&A7l4k~W6lYy_rEu}|rY7x6-m{k^2_T#Y%@p8SwHau**{)gk<&S*XL-K7;8w@d3Mz$tBq22({4P>eT^#(LVJ_kMk$QH0AkxC-;= zWj{uECz~PWmwmejBt>O9kA!_m{g@uPd{KT~o(O~Of=E&ldybU;%nw|iDIsYef)+SO zm=mEXNa!@D_F>EuIHV=DV&)&`fji{?055&-87lIP%W&-$7JvZOlwH#*hMmQ#<2Ual zj{G8r?>GMd5L<8MqTRpTeL!$yzwnjua|LJR+aoI1^y8PU&r0tbk?$^x@j^m4(`gZr zTzyC0Tvj??W(rufa^Cqr`8wA&=PXSc0sdAC%rHMUgn%u`tD zj)v^>MYVyOq*g;;qkun1qTtWXK2Tp~+iDNH13>ytr`SzCg~%WKL(_L8Pgs0qu{ch+#HjJuR1{ur@<*0FSt$zD@s z-tZ_M@$~eA{{WeAwjiPsYw*)AqZxx%{Vx@6}B>YQKdZgo_dlR->EbhE{_gPRqC!e#2 zwQ;BCBe|m+j*k@q{w=`{eK{~42hw$+eanUBvODS@=3*qZFGy+$>ibtKU%MESPb}Eh7Kkt zeO~wKU!wlykpw*IgR#Rp*P+1kL?~9Z6jnO*uvAriu7+|dD0rymL%VTn)T!)+cG1KU z_8bt}6jN~T0*dL|^snL!=l=kFPwl}SY(ihp^dfKEcD!W-uHx3C(u-{W0K&rlN=f%) zg%NRi+T`VNY@ceI{7oV2=lvWPs3SBlLvbz0N|l(;0@JEAt*ckocjUq9 zuZehP(K*=LopRT@9o@T*bfy_&Q)!DW_WYx%L<*3a&|{5VV7(KXp-SB9$;@9fVTUS^NpEfw~!tNEXqg~gR0{;`+E0oY@_NcI{x zj}A;f=KD|w^C-P6 z*ZqcFg@5&T{{UN9H_2Ogl%L6758rRKcz$E|)ruYxyyw~Sum1q%vG0<$^rX$L-`{Vv zEz$XkUR+m;gzp*mM|!{MH;)?_IaL$MyBu8KY9IdqvI}!?(D0q^r~d#}Kh(m1hem|5 zf4mUL_3trMQ=Bb*QT~exAE7uO!z1xY)^8A$TlL`b=}r{fI*;!Bxj&^iAHw3~P#4eQ z5yri7A;l!Q!W*2L^+YhN2jP+UltCo^gyWCvDT!1G;N9R>uBA4zq4Z~u3kdvC43c;h z=W94V>D{T#NR^+!OLlqI?SUO=#i5pS`xGE!;>nE45)_8=TgLyQGM-Q3qj z^UwLqdaAwiw#VWvjdHU`w8h((`IkmZDpIu*Y<3kYd%W88#vR^BTl_KiO@5+3TJ4X@ zC5M!5Bs^TuKly?EOM}+rfKZyy3Rl^TC{cDj(NaFcl_C!zY-x}b1;Q2P*!8u-0SJ=R z=AC79R!2(G4KXjbg>M_{3cI)g{60~`+;_92*XmzfHvWJ0F!VUTJ-?&LB?&8bHI1gh zH11JR$7$G0SjBu7%AAHKFPL$RX;d@(<;}8^5i4>^qCp>bYK{r1uU!}`-r8|Z&MQC?{-pxL}7!f;puN-?(g>pXimXMo-`^1ese&Ljlt40nbZ;EU3Ek@yP ztJH!Ce&|-NfUwd13BsQ+&lKb3nxqRN?~>Gls1M85q2rSkGyqe%BfkyJ(`&^C$`1uE z+uz+0P>~)qc28nc9SQwd5pvVbBjlFyOU+$(TWA{4)}nI>NvGL{h4o3z5qAKM4b8X_ zlA^Y0+>?MvQT_;T=DWZkm)tf)FaqN1SF)D=mqj6#wYyZ$g|oM znx4w`;a@3uPVk#kxz-CS`FFl$m5+@Luu1(GACo}rnr%tvi)bdV-cp{xn8uh7lSTYd zH=pJmzcW#C<+vKJVjEB&W*23hIi|+=l$bdyYB;YQ>vAr=JPu|4iNfD6=}CO|r3!fy zY7`Wm!MR#V>Y&@7sW=~(^sjvPr2^ja*sgqAzjL;Ge(lfJoHjP42)+ZFQDb*(Ce^F? z!*aXqSXdwLaDODHKZ_-H_um|*nzu&aBfC?-vjOtw!FXm{&qS>ofAL#%^EV#gS8y&l z`o0B!B5E^L{$ z?ZI)GaQTp`C8KI_gywQ8Dqd9V#zWn<$jIXyH8u7w$NZ;D6w_Qiyx0f-05eMY^|d+Y zuuzfWDa#mp4MOWX)^xt9uPTXs-ChbP_)aUg2i{ZgI;{(4oz)fQ$3a7cfzcVwqpFjX ztu)HD!Be0rvF0ZpArC=914`q_geUc}(9A}ANEQ2WWwG~SRZG@&NK;+;6r-p|HB^U(Z+i}{Hb8T+}^0B;c zA7I1$djWpDlx|j1*r+&^qc`!5<+$Qlw(Y(9t;TbyV%u(vg*xlbDA6fOQ~vJ!u{tAt z#DR>|l)}}p+1r_AA*~vcc`o~UKm4G!d|kI|7B^;V0m@iwFlp=yJ- zcGo@Kts8RC(pa=vA+>9Z5tfoPgry8HG6Ne7Eo&RnzyJ%8?(dy*A$+8lA0PN4MjDE> zFP^V&p5p6c{o)25`H6>6JQlt~Y52K*^57j$o!9*wCXiYwaMGeb+J*7oXn9oo1_64j zaQ(J`9sdA)T|0dDN9w@Qc?c6^>aFvBS?&Jt?GM?HQF&n~+R0J#_m%wD{{UEipJ2c* zFH>Tq`1ghM)2d8|{{UKX-~#nfLGC?Ds;$*~%l`makUv1dsGIc^qST%(N!G33dJ8nF zOqa<081)yriga4{KMurkzna}QKLG}+lJ9@N0^#lz>o4aw*1jkGN4m=WO!MTZt5fhL zZwvb1KBMAsmo5$|wVv{%ka5M?_Yr>~{{S&xU$%>y>?OTFrWAb{T9QRNQw#*D zA*P8Yl&?%kv?$wz)SKa(E4RjR6*tCI#2)}bV2JD3^76?w>z)O8EMOOxzMr=qURWlb zl^F665>&Dhl&I4lJX6SZUvGbvR`lGg{$0MDquTAiIUW_d;@eLt!&Bv6Wgv6kV&ks>7tgNu-cKIrxD5Ns>rURg-a!CcS#G5GbIY?9;b#iT=Ez)-k=?xSC#k3* z1F!-B!-m5MQBqHGQANz&E2rM4>ljcn8YEtBN#cvCTcU)iLW8f4xD(|d{{XmKH0J6; zwINB%`a=QyHSgo9iTRjC+?OxQ^VFjgl!e5PV`@^7QldUq=Nd&tIuWN7?%}zO#7O@D z0bgR&yA0>Ib=%jQsJrb$fr}S{QZmFw<9r*_nCzw1wYE0d?>4vHZmSApTo6ZfA+V_MYnI{m0C5Y|bY4 zV1sXji)=eXFQi9WF4Eg8D{@NLsV5zrA(G}*autbsJ^Ck~vlcJ+0s?rSVOg{P055qK@GU<` z{b*MI0PO_+T@M9$+*`j|p~xFt6<0s#$~b-f{cqxZg+?FC?o^%){{X4KwH#09-}!Ai zQsCKJ<}oKWe*+*;JFY(dyWe)z z%GGtcS))L=GZxcv`Kcjpm(;yVY>6W+EIh&X5LSuX9@k!A@?SMI zDbnGg1nLOnzGH0rT4l-KmA6P_$Y1tsy5MV3?gd$6J%-A<1-snj+TC0V3gd3M*r&g1 zyg_o`Ha1U3a8T#af(Zm3!Z6-giwz1qZ)}a~X{bQ9@&G2kW(vnpuQ`W;m!i9>-x)x4 zLTUbPEU@rZN!v%1N?O2gyT9nb%9M$;E9)!rq_=M?ub@sYn`GRP*^Y~*@Mv0JgdGir zcQmwrY8*7=Xd6Q*{$6spK*uU)~n3@hm=*P%b#ysU{gq8)Peqy z#o&R>V1a=rD$~ks*HW$Icj|d3>Y>1Nx4{oUMZ+AT6cibTx^)5d5BJF!JsaSgbS2AV zo>XDPr||fT2D(b0O$HoW__J9=BGZCcQ!k&}$>bXnvJrQ<&ucbZ4k6@?jT`_mmeZoNsO_hY6o)oH|P&m?GX-;dN z;*}RyK2Cci*_uMT75A7gkd^aMwz+!gE5dEeom~c%3Vxg)$|wwkqpfl!(Lq}5?5t%^ z4p=9a5c#Oecf^-KDzGzN)0n|s4H6D*R-eufoQB+Lr=jtF%oEB*^AUCKSt>vNPPIeX zpX(U8TH|g*PFTo}slRCZ7E@7&Bi~M>eS$##wme z?bV`t5j|=B3Ba}0LFKLCLXYRAvva(%wSM@VrBHX~Fy)S_Mo8DpkLR6hL@!mGe>E2g zcQJ6-w&WM)I2>?%z24w7g#bK0I@277-oyU@xq;~YYw5q`O5ne37WhBy1NJMk`9W-p z?;v_j+>h9KM*Xy$+uOe73QdmQ`zeNe*l}iu3S4buEnaS99PzXEL50}vGyt9png0Md zl8bEoWzmQA#wlM$pQ&_qe+Tl3aCuzKxIzX*sn$t9NN*cstic>gIPLH!o}f3<3O6Ok01N1L}A3Vs?n9FMgaC&_=9r%lg5 zKhba3`&O}iKJvGlul>L7HrebHin2bSG2v`m`^74E4WS?Uc71=fa^v%d^xwJmp9}FH zGwu#Xx^fKbMg4niOwMV9H3}Wl8`HwjQR4AC5h);&Gs_qke5^WI9NcxGK7--((RQEY z_jboLv)P7*0^XnpGSmABTn2{rqzs6sw;b{pY|R}CP+PP{q1o30T}0Wj>b|T00POQw z=C#3|q<1~0Nr!Q{Z%bqcUP?!an0tMRARR)K3X)AdxZHa$DQhM_<^%r#lrBU3(U9w# zR)3J6{%0y{`T1=douk#47H_+Y-!Q%RvKoq~s6|(|?rt|u?2|ZRo!q$E+?9WM!|Dh8 zR+rc*%dkc|)G_}6_=o=hgf1;bSKSP}N1(+VEqZ3v97#HpQ(7LU2Q@@*6dckVjQT;M zk_jJ1cq7h;!28Ln4L~QU!&xiIKMg@RLFwIw?*%%Eb)WKjcq5qkm6Ct9+VMZpn)mI- zz1ng7Nj*yo{KYo55FZDhv8n>+`94Q8@+@@tLl;>f6YO0u<27(h7{WPj%B4%0lpcefUsDUX`pTB<(N&Tp6ZwD#z}@EO^>Tlg)CLpypa2%8Ec;Vc4_ z66|K&Bnsu%9cOKH^8-lc0{LT>uwyTzX#2~{OV<=xLJhY_}9TamY>A1Mh(hBoAc;U<*m1}MGMl9*?o1-X7)B=YxEbmvj+ z1TTvVV!Q(`@6nD@fqf9U;+#lVhr718auX#rdVZ9aIWpGTm8BA+Phnor>^BpM?Wli_ zeM+&s0XD$+5TP^x%dfO@^3+mCwI-N>yA+VCWLm}#p&CwLj^J=90unvgspv9Lk(%3a zG(DuCfr7D`)5SQRalC@fyNhYJTjCZEe zpQ{}nyqCw$w*sHOe$~ex`#?K+gT1 z8PQ$c*gu#Tx=I|mL(;fWAm%NS$|4sc;uMzUz-i!;Ql$b# zX_h+mc00Qr&08w>IFU)`5~|eLM2>3UMBwl3zNlF z{{Xr1ztj5{$h{H*5^4fQe*ApeUvXZIv!hRkx4!}s4yrXBu>6SZpY^Lq`*7;me-I=m zG+|bfJ=4l%l&#ZNk^vO%6vm%s`Y6x~Hethk|0Gq_l5>S#DBK zMQT@Jt{yevN0^GVK8tuUuTp-gc&m(Vp(@%8%U%1B%G46y;8qZs&$hbtOYR1kDeVp% zPGGElV&D%-t8ab6F_^h$IuIy5ZqR*IKCrt3`Sor{+h@>T(6B{rhmpAo%Mu|?D2TR; zeCxRs{s3d706H2Dn3eA%#C8$I!>PmS_rFrfzbSUkxy?;*b?IZj`KaIirSaUF{IUoY zpgCim`7ZW968e7s0JE3168_iqfw{_b7pLtjdYu%_EJU3wB-c75yie1O$Fof}m(fqM ztBb!lvx6O@3H!#MO+Qll8Ms)TX~M5CQ2Q&5;mxArCddLrRl7onD$m_q1rlNCy5psq z1+lE2gq(=_uy6q=?|RfS+NnsO94kYill*`alu^LpmS@%6kys4!{2mQR39>_`362aAII=DAAhFMP|( z*S$f1jBc&TC@30(=icQ#T))Q03Ez9uy!gHU0QidgXN6})ge~xmgZ}`KBv!=`kUC*B zqqR<30?4_=AoMwYj7nk$@q&oI9Wj+KCQsa{Ri<>I#okF3L9D7r=K_fPSB4;go0jW@ zw-eb}6GcA3ipK1kp@CR-gj6y+(x5z3)yZV7KoeYgtH1@+kI89QRy}&{OO=~%{{S;j zwe5}2`YonIQM@W}J8O&gYuF!`Xg_rRrIPpRV$HVm{$~FG^)Iixw({g3 zG`6lMklCQ9{7&Ak&tk?prPfWY7tddwdElB4Z!SVXKmBZ7K(E92gff1|Fkh2u_s>d` zmA&}~Jv?IZr|yZ-3I70pg9|ozuYB~#g16rxpZ@^15&`e|$e;Pl7+AB#H2gwdOIEkX zA-Ljn@ypHk11c~47ck~KCbX!ltelIMKb@}i-E(~vw`siXRoP?QH)Vpd43=FVbMp>? zM#<_%GLHDPGZx?ZoA!m$UzI70na30N9riDiNPwa%Y6i6(G0RL{@jk9y7vj|c$2&ZC z!$AYR7YXxqEtRk$$P^b1zAnrxs8HQQK3?sT~CqjUyo_irvoe z3zG1*&Ytm%uf*l~v2qUdcapg?kvSGaYHi*?zuvE}1)8iIz0LcFh@9(3vBUG+8Kg2+ zy+og&l1J3Ow%b|!y}Kg&jcb4D0Dh#LpM)Gei-Kl&r0as*LoSwce?q*F1xZ;R5C(N5 z(<~!Aj_5rir;>MQ&|E9SpR1bvFTFc$2l!Ldm#6+EfmWE~9hbLk^;H7Pk!`G{(ysDW zR8O?yvUaX6D|~cufuLyn7s@{2W~9E(Pcw~6o=?`kE5AJL=vH-B*h@ne){oNXAN)VDa*>7UKuI3#RKnC=+LWQU z_WkdC?%l1iw!;XQxp$q)1)pz4aas`N!sjixG18@WB)IBodj>rKl1T$26>%UBSG#XybcRaqibrlp)KN*}`Pp zCpuCRHNPcmT93CKtF|yh8)C>>bZFH0o@Dw-)cRM&j_7Y2jmfyAym&tI^d10c0Q3Q% zUz>+L$W*UT-0>u=IrBT)cDPmuDLS_d!%Z)&*xAUzeVAPg44_Rra*G2YSdE+lZ??+;bnAt&QM z&G&MD{UZw)ARa!{+C1)Jyc_=jNd1Tb#y*OlIr4SLQ?Hl0uF9wX019wZ3VKajMx7$(Z?taP)C+aaO8m(ZBU`4b11`9+@fEhnI=e=A z;f>WPenwXxyuP+HrOPa4{2R-L9y1MD{t)< zmO6tmkzb?D#+mbXD-#x$9s}4CQ=BRJdBRcos}9ak9ZID~BkS|9U!1t{M;6v2koF>J z+JBbR1?&7LVX&^vPDIY0_29zl>%|ey$Mb0R1y8>*YwzX%0BZCb>Yc7RZ)UZ~6Lx*d zF47Q8V>Hc>P|`e-nyX%Bl*L=PkzR~Mq?7QKp!Rz9!cEC;*nqE4@Z`P`V}g}E&{XvL zam#W1WqkvbgGJmA`#yIqmt^((GW)-;mhHJ_wejxmF>bd<M z%0SG})1k%hw)WDyD%TE8ciPpBcluV1w(xZ&?bGOT`#^Vja7F1(xoqSOwIBjDAva5P zJVw1d%_@`eXZQ!*{gKU%HwMJ0? z0Fz0=M*4pgv{?IU@I#HWwU?N(#XjFDKeGk-`-JE4yKh1;#?;!xC>tbvx>2ML)sKE5 zH;VV+R^EiAA8YMo(@mN>fADGjnD^oUCqdZ06L_zF6>aECOxgD82?7HNuW}Pluwgf3 zB|OlKeYoTj@Q1bn6R9mW5IWH!nDcI?DGBYZfZ&lYTgQsFp()&(#>Zk`hwf%vt?``k zCr}z*btk5MG3hc5Z^Y_-DJJ5lm9>sCN1!IUxu1l!7TV;ExU`>uCh=@lM_$KH6&LXz z-yYQ&HX{3nl<#3){{U&+Ng}1LK-tL>`Oy#!&+7MO}dgg0+ZJb{L$jPU*gh>jfc17fSDFnoO zb`H_qK8$S~e_(~T)b*!($-n%YO*FYWCt28FxLW|70IyAZ&M?8KB ziNyzwisJyqJ!7i ztcr*}ZkwhbN{Y<3wiAE)Z~p+mSLi6N657o&+eu+j(ttFeB`N8bW;&Jk@&E^+ ze8_Gsk9(csQ8jSR*dMy)e&5@K{{Rq?FZeT2Rq*!IpaGXN_TWDdktz67Q8##(YBya? z5JzFA1Y{&lq{l~9yF6{SVgR;~3G7J;{g`lX-kS>u!dGK!+87D)%>Xq6!ZZ4?VS3}l zBAYmni`(#4G?FTRdoKJg+tk_C%&H=*3}h&zC{qUc#7~xCKq|`^d?6(Ju&XZ97g;mK zYxpxiz>l*Ee9%PsHX_4s#HlK6DLsam_ss?Gkd0*6Auqz?X;M#JRKdP%q`p!#i2nc_ zZATn0y7O%*&Z*Xs)824zos*IVm@aUtK~bsBweG}VDqsbsw0E2rsTze0+JPZ8saL`~@SHlDB;Glv zyGtpI#Ri~8WEzaUqZZ~y?<^|Bg0CsNbyAy@RG~zu4}KsoT11Lszylg2p2{kf&ke$K zR`~KF@|D!n9zqhGvf`+0%%J|!Bq-4Ir?U^NWTjIZ+itf7^uPH$rTdR@=iz<-HdqkD|4?SwLjjOj?FLIOrKw%s_cWRk(YD670-Y9%2}HZ=CpH2V$> z^Daq#9^Vuac-LtzD%*r9*-IMa$nL#kkm8S3OUC zp#GeD@fRwqNZXg+BG8z{NtCtN+dL1592vY0oNIEB!E$=6*=KY+`FKoT3bXV6kiKIs9=jn zs;^MCrifH)Zt}k~7L@=riOWg=uV$KKjDLUI zsiJo|PS!wev?O&<&l6>aiUIDaOnFAP354V|;udd=t29ISVPgbdUdNZ0vE}8rxYoUx z@+$Ix{okt|M*#-D%mG5wn<%G64s^_ndCBBWHD7A~0F$(h!FK$-I7{U0YI-y`wvvYq+G)+sGrYi66HLdF}CD`Db`97&4Rb#wo8%)BA8fb9_^8m3M;B#!_kKHK)6w z!J7Ic9|aLlFT^U62e@D=QGB&m{eQvv-$Guh<0++-r($jw#ZpB+S`wcJ>qa1Zg3|mn zqn|IH5d|Kq*Rf_Tb)o zkqefdMz0k}uV#2lsmOZBOID`R6JHBuKsdiDPRaKyM` zgBNw;k2`N&{yT7I%&gd8rr$Zrp9%fN9<=Vl#@tHE9w9c4=gECqJ=4S{4aU`SwLD|Y zV1hmuDO4zTf-wVgZuS~01-;aAih`Dy-EPN@!zzjvhEfx(5(%fX4kF5QXj5r%f#$VY zR_loFlr5szSMMH!9{g3S&BQosRrRO&PIAYm$R2@q1(R^|6b-k$Yj;@c9S zs%GJlIx3xQLnOTQGyjCD_EGZ4#+Ev+0J>ibPTsulCM^INyCf4JKtT1y=pD12zp z57r3!72$uHJv+uykk3EHkfj=eC)9tUiepgo<2(xI55cc~;Y88QoDkxuo_LOZ(rfxs zFtl*{g_-L%V9TyaoYFC3piUWbBs3>Pwz-!BfIy` z4isFrGk7nRAI?dO2=%>px$Vu>8+Y1o8&hk@YV8%&Jla+hGBTSgY>}xYoX50dj{B`{ zMs4op(%-eq>W}52LXt8_X@|l?B@r z7RfydRr-&Ss1%OwhUtd$dRTonp^$F?qHTh-8m{VU8El-4+ zcU2=A_shJlSq7REo_$DgvwzuIW$HZ%grxqFg8a0e2#dqi&luE}Y>%qHyjen;C~K(G@qKZKB{&~MMNcp z_iIL2KgqdzIsFKuo~pJzND$l}MQ7B>bej{skj^9zL*tVz|GN$2V*aKH^Sj+56ID$dV@RNS7-(jS57xEiT#` zioHr;D@m&&fQ<2>_A2S$EhA5er?7sytYh<`Vdh@jNI5W%{$hVx?h5(ehvfL0y~;@+ zy5+t~Ki?-C?^Bl%{v7WGq<&Q6xrbD1$ax;tT?ap5P8msZK}b5tq$-CKD|2cZZ*uL0CiT-q*v{%;m`_ZQ{;$}$^I%)hnnyJqUZ zrf5qyhFEa@4LYo4{o=FpaU}iH53qu|Q}T(D5bnmY-@exmYfV12@yiU*Du>*0%57g! z?h)q}N=*(!w+2YCYQDe!0POz&+N4DEUC9?jAfZ>zM7k`y&@bg|mgv7=MmNsZGM0`b z#c`kJQeLdIOgtLF_0;~g1%KKX+HO0yJtgn;5?r_3cRh*MYpAI+;>Nir?S+c}0Cb}a zm5!#Qk@Z*gD{tk>?y*l5bf9;z)BgadubX&FqEp*n+l;N61<-SiYLqpSdeC8ZzNseF zc@!!sR$*X;eIl4e88w!VA=g?t1d;|D$`rIt(G^;$RGzph6xq9S(P#c6j zov&LV<}*ODIMe%?ynv|JK}QPUmZ0q;{v0>4fz)>6_z>s5#-B?R!O z`&ZsJ!?@V*JXgjyWV_}n=eO*ylT_vA+Za8WW34d{>$73>1@iB6LFMITALc)5RCL); zl!QL24u}V-z{sPL3ht2OxS${D(137K!jz2!-T5{C&?pY$gSnMTDtGylyo=AE-%?Rh ze8i`3e85*)7}HpP{t3ksx=iZ9%jgAbAD8({cU&Hl7sVZ{0^Y9W9CB0~7t*_fs&NHZ zNFAdwhiW{jM~58@@?OzVH4aKkWk2$XE&jYrZHIr{)5It(&_C@D(3lstOnt++Ba!`t z>1ZRRG)P)MRw1vNTRVrPP1+yeH`Eb)7Cs2bjH=`mD^s7l9xkCyL91xK?Ee5R-PvcE z=tt6PvLgWV8!F8m`3J?Raal2s)8NNt_Rj6{;bC#&T0X_{@4nWpk9FLDILPDms&o19 z+pQaq(MH?7LqZI#oxdzH5by*kKRX^>yKszL*VHlXcY}x4XtjUJwZ}$gMsjp+ z`h|Z|`7X9Dfe8Ys13Ysf3%aCgS;vxVZ!q=MgBO%8VihU?)DeW*NkD>>q?oc(@oGY; zpR*oLf)X2vF_=B*O@4ueI_t$YaMc@2go-5TJ=h`D1wzm{Q_~)9g$Wf4ZCwVFPTT;{ zp&S77rhO5r_&{98q z9R7-7(J7*CTO18DCB0gxr6eAmx#0*$z6x_G_h`P?{Q_SVx;+MZQo5!|kmEZx-vUC; zXU(;#%W{%Wc=B=Gy{x*DY$RQO5CXowYvY&ZV#T(*((6qe@_nYN&WDx&6tB~b3!tn& z=Gwjh&e(KRB0*RknrTdY;uwb&_tp7vMTG5%T||#~p}{{0a!2uor5Jm5YtrD{L;N-pHUo1>0gO0k;)?Rs;`$l&n&nW zUbsHnP@~i*Du&!};vc*@c&`j$zq?cP(x0tQ{#W*k@la0rem8WqdaF`?q3v91Xb&u> z>H6`Ch1D+Ny94=Ya|Zb0J%!2b?ES|s#Kz~l#$CY?)|93578q?MK|uT=SyLSw+D0?w zobGj8N%^U?H&?JX?y&u(bYb6CoXSB7-t&}CPccYW>c+siNAM=1!_|*9Xa4}cl7ff) zksoSs2*(2&&;4vUv;P3ANgnMCpVFLpd+;dNkE*V5IgRNhAoh82{{ZuNPs;S6{2l~l zpH*D!DW`qOX#W7n$Ngx)`AIio;0W`6s`=~^KiUk8`VS^I{%;=geJMY~U`NsF`=40T zP3+(Z=~g2V{__eJp%}I!zxB@b1cUzo<<@=w04W>#m@N|j0EM(5*XwVe&}sbS94E8n z<9}BL^8z@H!u10N%Fq?}HTnl;AwTlo*Df&#sS9XfzwClkT@f{;oadA^aJn zBKJ?s2h?S*$$6C@dNUnt#gMIkheH_#RZ&ut*@)bw&zEfTUP^mvZFr@~Mh}YlPon0| znK|WaerxYYlJD_2`-*i(=_)wlTgijvBzTqemfPKoH^2ex6i3oRPk*-zky7QMVKbww z8Wh*<#GQn``i{ta3PN^ze3j8zpd9PoF=}L$Rrf?vcQj5!x!VpBY7=MM@R}&>O4g;g zudbsITb^pl(cl!;*pH$&9}=Q$R~3${1ys!8KQVcA6Y_PA>z=JQ4EV`cnQYk?wFi@^ z1bO!xb6Jt>>5W^nur%blh}Me}{J~GtfFs52`hoqc={9qh=U%Vkk|+fY9$)J?>WL!N zeEsth8;QD;_m>y5{{X(P^&BVVqqX%x7N?xd_YeLR&)b0VlHOt^#V(?AeAGw(NT<`N7AFmE#VQ{EX_aUc(<`?G^a=0<+S8KzcDc8|--ZsXXO4hOh z+J!DfXrtVdjAy<>s==fD!|h!;`A?6lEg>A3e`5K!VLD^Bp1Eg^TyGWK6CZhWGmV_4 z!P;p}D40!G-%Rn*yJPN0OheVylY04hxTI_+s(%WZq=pYzN)X!ek^dn^1`Yb0V($k zhB+0C4bX63PbHVA*TpNRwEqAPW)umN+N7=u*;gJ-=dKmlHj8?$bN6=ZutIJU5>`~& z?u@D8LVtBFC2Ci&96rZdneO9^adX@Df_|V>ZhrN)n91dFplokH{G<=8h58X&-sOPE zLZvYKAOtXkrFjN?2^6M6gTEcQ1)CcX9)

onKS=TnJ4YWQFL&F?o`@2{^>(=O}t zXG6S0gy9(gA=%6;@r!t#6C=B@ypX}A=zTb#u z2TuH4J8fa}S*YVa1p6ubR$<>A$M{aynqVqjMuF?8`vUs8yLuM5ehYMAuTcB$GjBHH zpMUYWE-s}xsYS&qr1s;qZ}A{oICXMgG4Z>%s_CWCH&Qt-h7Ii%R_;gNmbWCTa;;ap zv}H|ILZ2*LUTe%yuf(l7kF?`}FQhNJeR(;<*hpVtu_ONgZ-|a@u=`pTjP)2=B!iaD zwq8vWne3(&Apnw!-1+!Rb_;3_VC{{YTlsbnEwpY2&) z08R>_4^bbN{_H|tW0#e+ zJoB78t-j?2){QDhiKPmAK_eR!J;uekwJzyCmC1L#gx#<@jAvN!1QGRBT@D=dd&T|$ zb3ly0qc&0GejMl4HwMu&AAs%V+`?uu)adC#++ki}WkG36h$IovgNs%hCvA|6&4J;~ z@e4NG*7+^1*^gY` zyURNADW$)Z>4&>bLio1kcFcZm<@l?){xSG|>D&>&7%0$XrZwI~WV};Tr&?PIrE;z~ zvi{@ExslHM&iaW9>gM0J5aVHB;pMac00=*=Gf_;*>FmN`IC2;q=%0rMs!W6viXanO z(z#)xJ3>d6zXp9)K*2eukPTPYXXQ2da`CS<`Viw@Y~7op{{U*-8@l7UQtiGA?6DOw zpkE|g*(uc8lu1i9IrTW{J%M;&$AIoNJUlC>)yLAfYri%Qx!jKor%PJUYAdfuuhy@$ zpURJe9`QT8cN)O#RMwUHTE)rPrXDLVW&&pXG#@}jTl}fG84ID@xA#AysZIBPMFR*7 z+CD3dXzHD=gL3crw+dwXCRt84Kd>3>{N^MV)SLfd<*%1 z^A{p*cvly?Q~u z%^9x_qwEB)j@YXEwG|Cs+;dbM%I`^=s_Hl8DZ%jlL;8>1TNYd9DlGd}W!*1%LC{qm z;Sx4r^#0zX8xLqFXumZamp*=Egsi}{u+W#&>R(3B{#mS>qOQl?(xa>pTU#!?%7F*3-7I0Gn}XMnO1l@4d8t28Q+~_z zwQ<>$5qSpTAoTmkna1`wVuDRnzCS`wOW*|j}IMqh3ijH5>+=wM0U;*2L}T$`qi+cf*sG4I7A z`=KlSw(q`Cw)TzR{{X7lb^aQU(}4U=Ke`bA0Bt+(lrY*idK&JjWcFq-2PGff3*XyL z`{e|7blkt}k@5+I`+4;E zml!>FeoUj)XCteZ_BSWR*&FJ7c4KZ>*E@T&AWspHP}6}?)R!A@CW%sxpg3yr^N^0PC{inSCpAnEI<^i%2a=Ct4YK3=QiDZy{#eT{KK zNzgp&lL}DmG+Z@P5a+hjFlh1gEl7T4v8Rku52Z9`{y~1Y*iaB>Y z2%g>Ea(a6Y=1&1nd`Lcu{{Zy_&XxHXeqlDS>yS3K+_@CepDDQ;Le)cDyw(}+?Kps! zWoDR72q6CenEwFfPn-FO+Y@|7Po;zZ05MU{;rzM(05kp`hWzyLUhrbs2|Cv^Z*BoG zDGBaw-84FVfFig%Htt?9IG<5EeTn6_b2e?~7e)#D(Tbn`QazJ;$1C zZ!~G;74J2K=`;gFp=0dBnB&W^Y1N{ank}Jp-+SfH{SEslRN1w?Lw;`MOhdG8{$+7) zkcJsmvhLw0pw3hkeWwzZml<>7tJm9qWUvxM6d&iW*!6q(vxmPtj%$||*>~UDyJSPS zfKA79?pXFn{#gL(D77Wm>rR8qA4V;EZagv~yb*r|2bw6ZDzqpH-Darv zktNFE$KoyUME>7mPgfIJRHLr*JX5pgT>ZHB;v>N}{@-FQz96yXrujT~gC)!d;v74Pl#A(O`Tpa50g zT8QoQm->zr{9Dk8_yasj7;)XC&@Oz30Pl5ww;p~|(3Kl;o+!}ciw0WUaec>Hziu0x zxe3#D>|>f4JY}`qASs2Yo~O)T?ZPk1Jg1D?5EETc$l{HnuL{^*4|9wDwiEnLMBr^0 zIiZom`y}LJIbQz&G0*E^6PT4c{?NBu9vRtc1Sz-3?(>}fvBE!&%9Z}hw^|Ml*=wmv zq^H^EtdG-=emyEz`zGJT2CfR)C;tFRmVxY&@cjn~el;o&?3;fTqPPocK0#H|b4Wdj znDG4v3LNaI0kW;fHLv|%Hic^4`s9K;M8~W4s{#1rzJx#7cK#(AYk)S%BsIEj;Xq~m z`N9X#YlE{m{$V2~$%t@qN|CrC(GakL+fhY>{* z@>k`<=--=@Iqpi~`xXB6a!C?*I&JMjyXsM?SeFONb6>W&g};Wz=zkJ0O26Q<8N6$0 zZF2UH3hC_NxmC?y_HlU9{qy%fPW_TM5QxXsldWz}XaZJkW z*k-Sk?vv>nexc$se-G|;Hm6pa>EdfjdWBZOg6?(#_<-4b zx1Bm(2-na>N76`Ku}pxqRSF79g+i-PLGHyFr(YGR7^%f%tafLEAW~NY?KJWqI`HPo zekhv{b8d?iDe#m|mt#G1WiO!9u}$g+53o4naeBrBr%?KhY4q?=`>P@>u~W@j7RKT| zbRd0nUwB-L`DS_w>q(g_2!rl+?Zp)S?O zO2JkDzF63IcXQeT*ynL*B+%*y&{xSP=G*Dhj6E#+tz(TidJL;p%9*z9cQ)EYqBz6^^-kyYQHm;Kjwo4|a<)eNEJQ~)%fBSG}8 zZLr|dkUK^*P*%kw5uiswgCq!AEkiG7TmeUDN+Gt{+w-S52vJ&4!KYr+*Afs#(*bg1 z=NDbIfqkU@Ra*ypUcWD10=e5a8d$eVi*sBkX>O`zKa0smlCIim$5M9s`G*f3MFb0)hq^x^gSM9)j z^L!Bh0BjEwDtKlM0XK)$?AEM%=bPZY{jlE^ulpNlPxDmZYnbgA=$)De;jOQN;qLoNM(LB(-~1tT zie!Z{33b9xZf6rcD=hhx(wBWo4xceVj_Tq`u?i+mxu-Oc?f4IeUyG=~`9Txs3NQGZ zizI+lJBh-SAvZz^?}Jc*fL>6itW`?A`Y`oVYg<63R~~NW+W=8Uwpa2Q;y-kN>|~Yu zs$;fu822r+hbaO2!uC&G;rB^P&-_t(ib}k!#F`>o1K%TBNuQBy5N-*lygsE*7RN70OJl^j*E*^&wwzI>Z!Il^!n#Ki;va;O<43rhDd&n&+NSTaAoVcNqBlLA8e2-2WpGMh zik1h)pLoJESh2#PKDNoZhK^BW%S((nrrd2vr+78Q4E7`|HNE(7Pi`aS{q4cqt~Tr1 z4mBw)EPg4sCp2VRE>Zd^sVwDCK_Dq4DLp~&dxFp0kWyCJXOv%h6C5WB9keHRV}2T7bV%`x{vRS1rip5l)B&V zCWI5!yC=AIc~B#ebW2VgkErweMMXfW`TgB~%Vjp<^Wd!3#PJ+Qp~I2-k0MprxOy1c zO`q=jqU#8dd^#a2gXn7-VQ6P1fU_*;YIvkXdLi53&~K}adyIHw{{Xze3Bq`9eF;(@ zqDx<#kjDI$PeL4p6+`_EI6%w-E2ka{=V$Yd;l_A7)NaGG#k;QZ#^l-YXRcD&ODjvQ z-6cmUqDdM+NF(XTEBA%}0Q-m@BCF`H<*nje0X`3>)UHS5mB&yyPhj)wGoSLD+n0+f z+M{7=Ez7sKJ5EDWC#Ezm(PRs4+&xPt{L|w>xVLcoq<`}j+E=|Y?UJ%Q^|(iSKo79v zV*E|3lg=gnDLsCfw$epW6uNtq5ADaBmmH9O4CBRX{{U9&Qa`h_GQRSvSM=cz$IyNV zR-g5HfQgRtq-_k-;6$* zB#tPe?s{z6P9$oyMsA(ievBqFk|loyMHlI>Wuz3U8*QHSDE_Q1_{02CKZK%9`fb^5 zyq^oLr1jF_BkDMF_|%>$ld+&K1b%uPZ)4-##p#jTD%SEZcHPn+TJtrHF4{os>E@hr z&wXVty@|)0UH<^mzJ&f!oK7lso=$K708)K^L>yyl<;8lGv)pczZIZUL8*X*IQ9O~W z=Nz2D)#jyN31DU0TwJ>waYndv(NTXo7JV>VOgL4;*+cr*d1d-pS{+eqcFky{oRrfF zC*MFZ(NB4*;GsLHh_&g}$Q^e(%Tde49ijRugsD`QoH?b~yghJ)5Tsnx?vI5U``e`CgWzQHw2oBHhiKeeuIOA)>pK(RqtQ>BXY0M&#BXLTXSv9ZCTPCN&yN= z;S@fu=6D}k_?Csy!|lf$wS9WJ2}#D*P^~!( zFV>o|!mI5M@&;lr{{Un9`#y81g$6ev?iOB}^Q|({TvS*~15{(* zh#v9#R}}taZ7w9vH_K^o^4r7InqQyR+_Yc49PBOEXrB_|?#kb`lU5}#@{)^{zFZoB z%E=)4gAqN=k|{0P=FrjKZi)W@<<++_WZuVu(B>M{cx(26zDObxl%+K};}Q-luAab@ z0*L5vu&7N8YawU925X8ZN=v+;H1kiV;FOTB^12)+GI?z3=#iaR&}s2@3^jUuz`lon zBWpBtY!6V^=NYV1FNDx~|iBuTq!Lc+YY=%;3`{QGcJM_f@Mxwr0Yj{|^H;$ET9 z;A!eb`I4&#u4`=NdM5Qg%(SSX8#3_fRX|7Fwj2hZM_g1pp%DBEWByb2El;rbmEG3E z{NwFk7B=)2(o(MTOmWO3MfD2d)k}?#1qsi)4wDj|{Bbc5_x{b0RCKp;iKvdWQ^7y4 z5<7g-@v0|39c^4a2)_RSWifdRd`Cf`Zs|6IKi+*y`$5Beu%*krY1S#l)~xj=&`<>T zc437`QVpU`k}6OuOaXbSaMCEg)PE|=!f(Hq&!=Zlz>>RM8>O{pG2^#h<;GHbdQzC| zUe?jLc*lUE`K9@cjK{jBJ~Tei3Tysvn{q|-^8Dr%wQo|N+mg2Z*|m(zXoY=@r&LG$ znNY(X@7Xc>}9M;KLIaec@I8v+3jG+S6UbPrIYU(5kFUF7r?`Kx+#^xf%GY4A@hTK7|%w`E-K?!~#!mV?X_kZX^D*fAt0O)UD}oITi+8#Qw~3 z=i;&sOX*kParmzd?KmG3DM2_uJLTb=VbABjIgP6zRM5`TfQY@OqCQPhWMJ=g=6ktA?&tyjA5 zZki@y+b6XoW$vy#a`XC;h7#YH1tiX&I(Sdc%W_Yb@Og?VrFzn*EHa+$^888dy|hsL z00f?fLC;TzwBfs(MC{0=jhV^2=V063w{3gjw!)C%dD!6%sVZ?v2`9#r*aj@vjmdLl z&X~@Mjkq>-hE86&+tfyh=#NsozQ~~5X;=8nGOzd}6T`SU0sG3d?$BWm`e`n*n zA87rELF*ryk&+#^DLv*2f3J^!x!E5SfA$vv_fNEb#2@`}`JRTa+fsl09uNFHd;QPI zc%lCQVK5(b`$z0p{{XH>H2(ndO|*Nh!}|Dcd!2rqf3Vmex_zVeB=>z@^IM84`OAF| zb-e@r9ufZlaFD-WJ+F(TvyYz(2TI6T!(w_Hzb5bL&JUdWq&6lxZ+!?trM0n_E&E5!gDph%#m z0UQF9E$crp*$2cA>BE~>QOUi`eN9d$WG`E7YCm|On)^u@oQB|aRYse0O%g`;xEq$@ zklXhb)Q`9G|a0x)wIB+kh z-ct0px~}`{h-N{BxG$FHEjKf8x1!?0;W+vvsUf!(jVjjIAwbuzCh)o2iwE_>Xzxg` z>k7HJHpQM+Ar;6jVAn%UeT7-``MY|~p!?mbPkGy`&;J0<@ns#<%@5vh{U`qbGNga7 zdu#4H{-OT>l>&b^FIh&uV)mztwC&Y?saW^>t(^PK{{W=_0Onr*0PXi|{l|aQKk}^) z=G*HK{7&sdPM`DxgY-)iV=?x%_#q7IrKKzJ&IhSG$kAmlMu z0bXAEtLw+AWM4Z~18d0Nr6~aY-bQR4b<7 zSwl^DP>>P;Na!({WF<3B=^gPn)cV%xh_sH<(U8O`8wCLH35ukEiUm@gPktla!8~cz zcO&u_+L9yl?=N!fw%mzk+M8%tqg;tn8C=WEwxuntL*i0fh?NoCMmH|U+jU+(N@&mp zeJ7WrN)^k$n~bPt5-U)3k^Mw^8YmC6mGvihXrjkhk9p0K|=zC)qqu1_f>r0zTBl6!yYNAdH{=ASnw{O|t=u3NB;oJA^!mM z+5Z59Afsl0NTC>1CX#VVNzjpz&{gqa!>E}!lCY$qM=bmD!H@z%B)AA)!f12L1)^A7 zkgxZlPt0eQ{R2+i@OIYLgUTFTy&gop$Htc_QXsZ2Z89UGbU9Jb>X>~^p)I*q)|I6x zs03FNxGmXviE%Me&36`^KAO<}o*d}=`?grk(Gy#8OJrxl;3`1lLMkdNuOA9^8H z=5y)q*8G{9Q)ffEafdG3BC2HGGrc8;47lu}3Vr)@*lSZ#8)&YIN`f5&GKy!7w{CY1 z9E-fN3nU`6Kd6ll{3^KXxV?nF+tGZ(TqTCQM=w}BKF>PZM7`a`-U6d$UK*Nlgu+i?-&NPA%1Qfite4symw~c zLj1&EuD2k{^(cZZ8`z=_%TlII3pNq_nZF!h%JZm`zU_kZ8%E>QB@{=6}q#KnoZxhOTpqspu3RZGDUC;&$4YPNYX#Li0${MqfzA z%=hZKDU#EQOI3nWO(<2~3P`~`w4u2nWbMgr0agheo)duby(s?xK7%_>a)>Bo={=M+ zz)GC5>#LG5ov9?680t^9hxOsR+f?oePlDh1Vf5rins!j(tD98)lhu3qdEem%pKcrU zlz*IR(NjYovveq(|Lh)WnH%VfiUhWP+qAq$n*0 zp+h1L9NXN!H-f`L3w;^~0)DEdmsWAzLinE0=DDsEBD$84KAP}frW1UF#aqVJ2K%)6 zGQoeh-I{7dSupOf9c@T?0mPBzx@(c{#`^|V87M=7;Y*#fdu<_aK^pib{FxTlO3ZuQ zSpXV%Br>XfO2e~p6S;)k+gy*`KE#sbZGJZ2ONjn3Qb*Pf2d*y^(8F=yeTg~vFDwv+ z1Ke<6`215>LoYU;Vof%=BvU{q-`5NI(fFn&IeZGy%S3`R8fn>wVpb;7d8F_tAHkmh znt@Mle{MVlQJUfs1lTef>InlfuYL-R<|Jh_`bYqpd-`G0Nqfk7HL6dr<&P94t7Nk1 z28mS$0)Yofx27g~&lSZ!3ZBO{VHh8z_D1MR_T6W=KrZ3<0l zFpR0pXk;Z?t3#JuBA=r)?+5C`ROxt?qY^+h$G;AnAG${> zCeq1C7)KtUzb?70wFd8VfgJw;cZDufEM`xEx7LK_eS;jMcKXckL!3Q?uch|VI$L%< z++5a^>mXHbo}&VaRqgz-c6#@xuD?$(62Xxk&v$m~a!yoB7Whv;sQUu+52qJ?-P-w? zMh^{EUEd6oZFarOkMA@zddUau1%2)turDXbY=istG^zI-b>rPd^1dfltK+Nw&;w2* z)@I3?rb|ehvF+MiBPIEF8EGUQ+E$q6PW8FpiAGO0pR_Ncf0Y?Sw)4Q`{{Ymus}ax; zgW7rwWlcIRiJ6+EDvDI)gC?|EDAs{F_uzP=0Ys^~-T6xmqS}+C*J|s)dwcNjDhzhb zXgpIK8#?Y^Cn2KyUHqN59_N|)Z*VgZr^aaCR z^nvm2sj1F~+A6viBJaM{RH=X|+org+^fhJV%n@Io-TwgV6rX2@4f-05=E{jVbKR&k z^OBVx=vM$^YWJBc(A@Wd{{YewKgh?r8olPqfcgF5o|#qes3QT;)$cY`3Z-v#5lP8S zI(Tb^fnrLstqa@jStj5Tf8@>vtU*~&Av^A${Ps{iqe=Zo1JurGxr$#EvZHUcb-b#b z+D<*x#qNoGR*1H5GC((uztBIlV0xIFd2;xr2CO@=O7UI+?gcpaQxJ&c=2N=gx3?); zbl#h;MMuY%9aCIgnAUB0H^ybqY(>5!)h>#5j`I#1K99EZ6F{^>XT z{{a0hUr{@1?ya|Mf64y<)Kze`5L7bE^uiE)N>21Ai*yK5k3cK+Vq`nrPzO?@g0i>W zQ0TQN)9EJ_lkpX84Wj}TB~_p_1ogt+VktuFqO_~C_p$FW1s2IxpmvvK|aDwHT7{^!`?2{ zWwN+&|1)(F3$C`h?p+j;U42oU`}g z=l6y;?}W+SsNAjp0QT?y0HmipZ_F3b3#vj#sXd^>iv8Cv%pil^<0Aq$W;FizO+VVb z%}V^c>Hh#JQO~GvTfUC>hXHfG$lCs+HkIpf?md^e;@RzSuMr$_M2D>Jp$(-eT}qTl z2_2^_G~Anq9{hZbYk?#hDeaHjZ2hKY3Fm?Bjjt6Yqz*nSDJs$S^7e<1M& z&Sf)sXOdq)@@;n3QWoMG*Wq$gplD^h$tI*wQ1d?1+Aq9#e7@{W1E`-0YxJ)B-n-41 zxak{w%ZHjVgNY}BK8pKz4rEzBSzfVc`iZ+*wl>O&_R{qK0LR`#9DTE#N?VaD zI(R`fbx9ru>qG-zi>1eVZ`a?vMG>Sd00HCaD>LnXYvIAM)5%BO%1L{?EBmeeXP*J4 zrj#^*br7uk@kCmbk3^Q8DpGrn8WiECp%-xU$CB-B7NCJn-tmN;86^n|%2jDWKssSs zAOU!+k_qg`sE4I_R3f7ydoUG17K-e{L}s8BnKU@{QlU6)n~rU?28Z{Er{avVN`8*) zJ%-?f#(M@o)}HZPhkS3$gs|JdedI!XK>It!B%_!)LQ4ywI;=-r+f(8A&#AWuUbLrZ zBM0kqQA=9zmsK*=xRRvKZ7{AQqvPOTzqsMs9LWZ$hnFNKqfncYw$GL`8qrgoGNu^1 z#jZ{nlpvz8qi$W7dbDosnR=PGcOKt;xpqClZhgp;Aj)N0+j)nXDOwhj(1Z41pDQFp zIi!L$1aSw%exFj2hba`zF~|XF6d>vd^slJj%ct{hvGdb5&JguixZrIrY(Qo%+2m)E z%nM%cS))lq^w7*(f{isKX>HPKjV7oYj>_(Xp5bqHeeO>2_4KB_PEkj3;u=Z9z{YqL8zd5a(!N$d?FmJBuTeY;C0b9-Y?$ksH;QqF2}g^EvWNArq-i!e$y=IJooSjtG{(Ku7bW>c zCQ7qU9t8lC?mx30s7V&Maw!5ulHv_gum{!pu+~-tsEOVMB0pxCRn!n<)Uj~l9Z9&6hB5i z=L7=P{skt#8N_CE<(8dQKTyE=;TT&u@;<~tlszI#QCSKuJfM^&gl2o|)9J^J!vLu% z7GtM-gF{V9qgSuYm(Y!p?LN(R^$Txi=WBV`*C@%0a$l7D3oR`KrGk=Dq>+)uXm=A1 z4@;yK3q@~YH!Ij4*i8rkT|uJ0s@5mwU+4uX4*vkwO4QvdbgDJSRR^=l!>0Eu2tUr;pyk5<0M19$-vauh+#a6Mz?<{={x-mcxs$tRrX0AeF!jzwy=);0l-f0w zCOV0yN@v#?uXHn3xjUHO0R^oN74({Z!n)`3@w0F67Bfe9(#Ji`pgxYO{i+uHvORb) z=nIi~195H*ha-?SpC#I2+iw>5Qdo^PE6^n_RCk*)sD+QcT2zvu)KK7id$}2ixH33Y zH~@Zve`=WTPi;o*uxt%@jqGtCkZGtM6+DL}-`%hIlLJ5zhVKqy9vH7NYbG7k?^WVHV&wC$y zZcVQR>DG=hyPKRVrS;?{h=&w}prOT+l~S*09dO?@-E4ez?l8TslnonNRQM)4OY+U1 zpCHJuqI_@@tLa(~70VfapWipQ-kX1?y{Qp&$gbyhe0@Q*ew=0}y!kQyn}6y*`By@- z@^OJ2XORB@@Q?h-p>X{A{aMHg7S1i$w$a#E7V9MR{*h{I{fbH-${yKi z_oR^f2klH9<@wS2wB=2Sz4*6(ZB30gWO5~*<73=$BiUz1d_=lORA&^^i=5~SNL5o* z^}{@ubZ+k9bTR?b;Xvj;NVMv8hU^~O;;r0#7jsB2BEM3QR-=x zPrn&p6f1wYoV!%2mMWT8--Rfed5L+u=Hokm`7im3;C5s41o>MuPnInXPYs5ImBJ2r z3VTXIe8PgN04tEgi0))>0)3{Q&IqmO>gu5CBouWh3?N5(CZxnA0LMq%iphZnbc05o~V5*9!J$f7k zP|+z*hY8K0D(5cj(-#=i7-rXKIlm#NP^CKH1ELY6XWnUr+(D02mA_Y=LBU75t3Z`? z>xiPKnrOa?{{SW3=d~E(zDn5xaKeMUo7_J<{hmaK?8$m0h)p!xjG@&fN=;iSG_H8) ze$;0zp|*5ya0k-45AzeZFK#a5mNt^WDW{=wht~KE`nl)yw6xK^!sseV)RIg%>+9}U zW-~qKD_wlz(@|BqcKR2$4FGtd>HTF^70fDWNGByErk(j>vStKPTp0}=qH4n-x5YZ@ z&*?a3(xoNTCxRvh+aswR4kRt-4K-07MOS6`8}qrj@Q)1fM+)6NGDqFe_^!zg#Lnq! zcD)Jt?7(lyZPi9qt<*M3XbOD7J-F6z{o9h#0TE?D&>H=^EC+giS}gN92`_$NvGS7Q zI&=h@@#_6bYhV4KExd^Cn0}GvHE>I7`Cl`4vg00Zlc!kxJRLRbOz|Ngq zxBR2qvEaJmpdY$dEbl*>f94N+pDuCrA$7O2A=eyY+Y;YkYCLwhDu2uW0F`tk9jMze#u@U=GyqifRQi4{X(7MN&)3hGm7h}=&10!B zw)r1Xw-JPf>&ky!{{Z~I`BB~4-L?M!@T~s;=Klc9kaw@I?^+y@wf8>9-rWBHc)#r( z>j~?2?6J8L$jeh;$P{&z7_b^zQlXL1pnbS+eYr9hF&O2Q&SByYO%JdG^`|(#-(aj} z6G3jqNYhY9RYCe(LaC`nfuC+GirnYap-^e`XMhD4rl|G4Yqsgo=f}zTPNCLMW~3-6 zeWMO7t_*Sq4j~ri*gb`(fk!V_X*b)|#`il&nRIn;O42BuYU@BSYny{3i;ohCZwQTn zqku#~Cbbl%5+o9o14IkxD^GPhdvWJBiDo{Fr@oZ`05z^~^o_&z{uJ6gbGCPe*R#yI zO|@KZu3TE0+m}gdn(_&Vu!SToXHG*bX&Z03H(0x23~dFqCsKKu_$+ID_D2`EFMev_ z&TGg6Kq;W0;(ZC#%|D!%s9t#E4g14;lZy6bw`KBOvPGkDyu()~r!^W$NiIfl=Yml3 zG}Q_zAROt38Sdv?Pq0|5HRCRzk>*Akih)Y|S29r|!~%3eQ+55) zvT5w;iUW*J;ugqTEj244Rm zS3JjdJdQ#YFNBP{It)6E+GvayhJ1f3&4eh4K(_Eav0dTjKG=|`J*lGndEkMjWA z9-cS6+m+57t}D2v-e*LO3P5H&mrS>iy)qG36X61cP?{Rvu((Jrp{w1}Ys2jJs(xc9 ze`_yHFd14$5FJh&G(5N|dwYIf{{WXQr*BG^Y5d)`le>YI(zfEcXNz=>;;T}RF4rv% z)}6;MZ%lgY7mbRo^O_ytX~)&Yde1wSc^y`+r=zGJuA}N)spSql*;@y9Tx=UZmu$Sz zVU481-nK~7;zNF-x|nHBBDYsz5#;O|J_tVZrb#ifV9{((Hw=GJ%uV4V8Z zw3_``vV6o`!mT>qUPTHb?I!|!$jJoud2~N!Jy?7aA+z|c*KFDfNzlV;r&3TWg*>=? z5r?1RlcZYqYYcqExNWq_J{<`qYdy&WGEa62_Cw%}9M=bdMB24&=_!)j%Q7Owk~Bt) zg%Ks1<{VRl_rIbt!a@c+fKGmLRy#vg9NlbkHv7R67xOTm&J^T|C1bevD0YlQPXS?4 z@mz^UoKYks60hDJ_#>M@j!G9&0r= z-+(19z-q9EOJpf0N|Vz8RHYXG0EtM}78g|~;a7GE<~xx@@=|k(D_WFQsHoJQ@^Fc9 zDbRYx0;r2*b?fwZP$WTe3(9SEr$NwD z9_a}EWe4RWxSyb4C=j|*hY^~uT#sBS8v;esIIVzcB{zFSdz(u|@z2|gN?VIt6o z=aP*Dpi~D4(kZ5-<%g`1^rccJHl1jRK0ySe)c01jz!x7Bs{jZ_TL+@5Mh?Q`#U(3% zLOT`Gl@zB?9chJLz&IpZC`3}AGAG<{+%P8r43|teH*$K;o%ShP`l$ zrO^~ZIfR-36~ZaeD=IaovmQr{bqn&y=1IE`bMI}7CV$J=w=!F~A(G&2CVXc6f~?2} zt40l`j;IT|1xm8MlDVq&=_8%C&pq0< zwX1&Iwxny55q8wM??qw7T%sx&aZbfb)C*}*Qbv*q>x&NFnbvm#Uk*yKyfLumvCgu2 za~kqBrD;m?sQXu68T|fv35LUR{V(n3ode@W?zkX*Uxusn<4R|8U(JKsu$O*WvB!nP zpZ@@?f9FN+U-pAJH}K1(J5Q!vuMS-3vSUY>?&i733@SqkW6aQ)Eo&*&P&Euj<@=2^ zvGg&G*N(LPMM_~mEx6lThRf&3dQEB2Kl87hDDe>uEg>$YVF^k~fu(3ET4NY^YXE50 zntK;e%Q29XPANeSw3FhZhiKJI9;vIC;*K$PR0$E~#AzscGScHKRTo8|f|Em0k;0-< z4N@+%>l7tDcI54|xg^|L-7KxU?a^U&+tL=6*bk_Zpz~l-ipqp^IGV=3-d!Xf@`21l zcW%(=OS)x<*9%aBJMOpm|mILMAtFMc3zn z?>nC~1RiYWD5Wqpfzo>LELw!kfSt`NJA_~l7`z&Ni<48&k>)9&DBun z4^x~6uo^29u$HeRzBV|XWsM~I$rUK$eE$H?DeKM~$e!Q*>A!#XRwQ8D7cKqc?J2yQ zYaD;ji~fdw#H|z5x2TA#3Xjfv!38}lxo<1`9FU=j7`IK=-YJCJGZOKv5&rPg8;+*21qKgTBx zou9ow>L082BMp&;{{Z-QeSftmC)c;GCR0ZJRquKZ!+LpNq#Ph~IS=bQ>ivmPvT%q0 z0Nb{fTevd?)_^!?W0bYE+B)lKx;3 z3QIn$cZy0r0&Wbi+y1eWgvM(l{a1Zov3TrUAO8Tuv+Mn-%@gxj{KugvDle-&&`M2p zIrvNYj-T5?Ru>zOCH+@_^8WzkL_NO1o-i~20Gsxy4{m;RADOP-bU43J&u$jeN=CWZ zcJzeh?P?n;KVBUF0C;4806wSEKV<@oyX{upl>Y!Hr_@jDs-5ir0QQ5wF)K4oH#SDa z+^TDpB;IWjw)-=qKXgiGHwpBjG`SLJmVCsiMJhDZD~%su?inxUbaMbG z0SCv5!aLvF*f4etxU?>HT|y5oV^QO;hXShi=TrHN@^1uk?#bYvN<8T^Yi?b$4qUEq zl>rdsDm2NSN}xb1l*{3_5ZGI;B{Wb9MQKr3uH$ZrBb~5YyQZ8-r_ePA*XXCEPkvv! za}~{tuJSQ;2bwS%jX9kteFK*t4LD0H^Cb24b6T92>YH`Er5#Esl^GfPksM<=ZsEyu zmETwFT?p>pU;wAfv+Mn+X8!;&zg|d;s6*8L=P{i4!kvP(bf?6)#b0h5H*(~Lo^!wX ze_~|*+wJ48%d`IgoBseZKx5Z$t#+lE4f>YdI*AG@Gh13}KcKdXe#`;fc`E+^HQ!h4 zM|W?w{Xg{V`u_lGxZG3u$$FvV%S#sB=K>!u$z-Ui^K+EAQeD)l(LxMxiUE+KdSb_A zzi%O61a*<)rG0^Cd5*(juO%;GF$2g7f$eov)d%y_=UctTJ-XBB?$L3%%U+0ZW8E$f zIHFB7geEB`6862wy^+D9AzxYjiVEZM--=@kop9!X%(Q>zQ!W1h^PJ|*i)t_O9@5)( zoyhZ5xAAR}_b1$1QAj9N%5v4^#TowqW|XOCtvfMiJ;QyxHI<{R@&>A){{YMFt>nSB z(mTo=Ug1jr0Lm&!`n@z=^g`4KAt*qB8zp~9j3tIBLR^K96x#f@%DT)^icZBrB@Bh zyl>ArCjGpw_n8hFy!^YQ`71fp5~rR~IEVR(TfxpgvQOGo;91(Rd$F*3n%~%p$8FUq zMc15=rDj}p#WnCdfIUKqz{(q-DLyXKOgm|!jB|=1J?7Sel$sE8 z{TOQZRXn$jg)}3^R5=1nDGGx6J_ndmnOOHJqLYS@u*L9uweZvIQ*%q^H>Kb>dUg9# z;vWA1td-SeCh8K?|a^4R=r>!@a4UUje(6j!KKFTGudbssz9cf#Bqj#B5 z%oBf}q4g>$iM*VGi%8n@CsV&@qfBWp|KWI2+a~I## zch&n4{{Uv;f1uy5_N929zrAlkQdJw&_U=NFN*~06#$V&Ma371`e^uXC>_F{}gm?N3 z`u_lGb;113KQKE&lDIypH!xB&NpRa_4*bLMS6qAhpC;ZCyZ-=}_9pJ_#@}}vcK-nL ze%17I`7HipTiexSE&l+)UZ!~lJ^uih_R((b$J>%qFd$66+1&f75rQJ6mfU(HlvjlS zmex9uPBjkS?-^z$3zt>gK{`}^(e_buuY5aMZ)V2bSTm)Z90g4e)8p&n3h!(CInc7S zvPxB$BpO%f#=PkocrI-r?cxe3=GKs$(%JTke%u#N6dyKz>01rD>OdbZ4Lfimo+!?` zBauV5w;gd*@Z0ygBqSOq#5(oCBO0YV&X9}# z4lqg_Nd&XETt;ytF=I zj`^pOw#OOqp6tBt>!Vhv(k;_jgoj)SazpOMS{p-Y2U3kl>4_*UT_aDaL|$cE46Ph> zA8P#HH)h(pZB5s8vtAS&ogL+^*ONjV0E|F$zw;JXt1kNEkh69Uw!C{C#-#gASJV6k zY76@YO%{zHY2iV^6hNhD2e%E_;+)lX$MOi7u%UX&-fr%zD76oocjx6>2X$wKmRUXtTnm1HYxaf1 z2(8d2xqC60G%k>p)hOP&{XX0tsVzsw8%|TwE$1shZgbmJOs}6K*-5SJHl|4 zcaqu;rsGkz{mB%=T-^O~&~i`k7QGMy%<8`6{{SQ_g}{DU+%QoD$zh9*?rb|+sFZGL zl}}EBqa735%dWgTDa%i0g>e^sV5aMn!j1m`!WU3K5_#tomD?4jndEDWGp2Cnv;P1n zQ3M@unhRQ}dL1VO^W1~U3w1|YmI|%)uVGFV5*}y_Ag#niDxcgkJ*7Bx=BD2+giHh~ zzuf?*dT`s%At&Yf5u{t%DHMvD_6lHp_agKo0?w)f@~O|WNy5yYsE%zG>9MU{A*VlO zKTZ$kqR=5++iqzDu#kHPTph2fYb;?C8*%S3BS1%e5>M&E%~A8n8iiiikfJTWz zkB-X)V9@u2TyjS1(=9*z-~BCLQ#(|8w!OFfhyMT*l|r$QR5cpVew;VAI)yEh(K_Ec zBEGuPi6kGR60(}Gs7SSVQOZ?RjVGsER>?e-vWV8TX{B<)fFx+OB$HEtLb6qzQ{f%h z^%tnN^H<-1q6#fkqOQG|8X*B|k0D+?4gjDD8cS(Yp-b?ZeVAfKsr)W9L$;`hO+wUw zdeC9e=|p>8Rus7UkKU7CVaKREP==atSo3240LGuI3H62Uhj=Yi>{IZ&@5=$^1Yp;A zBFiyp+3%(kl1BKa4G&iD(*FPl?OsENz4;S=&o;g_<2IUmay-~lUY7gS@}jh$C>Fo3 zJh7^5EwyiQ*H3VNhQ*9`UwmF;ofW01t;F9CfF39GqDSTaUT1YeXJzc$Lt7(@?GBaN zHgqxNw&STeNR(0iv)Oz~tvfTse&^gQocU0Wh=(r}^Jndetvx794413r|G@LxF3^;ELt~HBY;4>r3cvJ>{bAR7}+TW3w^aiCcTHbSf6-A5B=6 z--gVDdGVb~?I_hQAlIj}vkn0qly{t4p%h9M>#NYfW^#UFGP)k5VdOJ-rgPh|+!L`e z<4I@EjbQP&W8J5fb(+u0LdluFOZ ze4$`rxV9cj+*VlaWR@Y{Zn2P&N@SK~BoC_*%*b3;y>1`;P9LpIONX&}tD=nh*wgxx zu0K3;-jyVChVm_RQ-94@(3KDUuM#omuSW;F+EW+p%xCl>{{a1L{fVu8-5aZTNn&pC z+}9n|Ix-7!=iKMaxIWWLR-%MCF~?Mtlbr#jbHiJk$!7T6N%ZQRLu(8Iku)@TGypw- z1mokbT;=Wms;xbJ-8-yXyUlENqM! z{{Z&v_8;1kUG=5wCDKa$Om8d)1L zl?6!k8NAd+LYMi;{{VQgg$Clk{-HoNP84u~p2Pc6bKa*tPP&p8>IZo%SHypq?t}C? zFk`tdzWGWwX<`BXLp_N!dX4oUkwMQ}%Dw*p%y)16{t*7)z5C@a{jZAtgn#sf<$96z zB$|Kq``#wKKlhI7{{WuAem{Bl$`Q4(U(jd$A!z5Q&ry#gCEj}8ejL&U=Da6A@?;DT z@lKW87b1Vsbl^W9y!+)OcBUZz0HDue9sdAQ z-lZnTb}X)Z-VU)L3T^iP0GO_c3POgGlcnL27#HWR$f0?n^m|(s8>-k@Us8pPKQUUA zDy+$=>r+#nBA(XPp-l<;R_TC+9LWc_1?UKd=%dl%rKG4N8vF2OfRd8nl8*+^q@TOh z^x<~Ugw(u+;T5MUmk?41!fGpp06=DNTN_+gG$+m}Ke`Dy52FHD)5M_p$BszTJ8#Sc zwKWMNu#ts6S-grPvP5wR@^-}JfDr3zuR~GUg$0pKPEn-rD4eT2*i9+-f=Uu;tP#}V zw9r#j<}2pB&>&Dq2_vo=uRDr7g)2PpqDawfljV@*eiKqX*z!AdN|yl0oTs8WsfUu1 z6HkPYDn7g#r6UYRCb2psN7$Et&+7L?F`PnepP@<8iA zb2#xwYp99{ji**sr?UY>ezJ)^!y(3;9v7H3W$}{Gd(xpolkEoy$m5|)G4%u;LH#xH zLzxe)ReANkv+=mKDQY$EQi&w{&Iso_nMnC<0)S8A2gP~Wl8i^24WbmONk~s#+Sd#Dtwb;qk|mYMnEZxR3KpVLYf6u&5mV95s8Uwq zjX*kwyruLyl4u5fm~TY5Td?VAmV$suN&~)}38F-bN{{Tk;pA>`;AorBb4hA#O08C-{_kcMGC!F`F7zP0kkRW>aG5~o zRZ0kTT?b|-90w%?xARwmuOjQXA(fcyHfK&y@Ipz*8suD8?vJ+#d8ev@EgX7O=n%}M z&p9%tx$`psqEANIAHv#u2e+;y8Y!vbn?GF!fhd=jrD`hao)<2nWaiNbW+8035<@}w zuL?TCvj@10vOeqsfFo39f(=4uTx@Pd21UJP_=l?`g0yx}_u@KxayTh?ZbP&@Q*F?- zP|%uoVT7!qPTE4#$Z=H^pu%2SSiA*+8E2vnSRW}T)`d$X3TP+jI8_B!{{SrM?7&eN zye&Re#a5sScaenrvWn?K)8!Z0LG+9l%3cz!K1I*~)`ZjBk9jEmXj%Cef8!ha@Fo;* zI4wU7cd^9%cpoJj<{G{DW8cP~uL0z*e8W-ueUTGfaT0EE?TJM^wY*hDp5de%Jh!tF z@gOFb7sZ3!X?LkP7}%^knB4FOe~n+Dj7v9f#^bDkM9H`$Z~AUZP4VL^um1GG%l`TM zX@x(wqWj@9JDyMJxh8Hcpxu_*E)Cxz+#J@FCMzndgedjv(01X5R}CC7^)3zFM*@liqN2f3B-JP)y?bz{knaWJ<`97MBrD)bs4!2He}P^PVB%KV zXB^WV!aa$u75Q0w5lr!TgkdmltLX{S4^IL8m{4P<`=vKI3vv6w9Zja}bNLsD?$WFq z@acRCY;WQ53(0|WLDC|Wlh|lKP7Cre9gRPRC{m9+jg0c4aI3)>&vKIbUa#0RFgweDO@?WFvRbC zR*4Lh7c<5@mvpBt)QUR2BqZhhSVbf^a;(eB_3O?hgcx#Soo51IUb-8_6xZo zHH^^sgv$2L-sPPWG(Crr;im-4pC;pXpFZbzp8eYGbbZ-3E3CI%lPXM?)?9JdUU^De zi&~9Tr4mW&h7w9#bu~Vp+N2ofuu-4@G@#HAP#h6-q#%TlJ-AKAtHjj`mw=!s8hygJ zKbnYR^r3!bAHqJNffAy5MGPdMqy)4SM_j0I=gdhMNIX*XP2+sD#E}BPwAc|a5iV>* zhUK`L5v0C6U>fJ7j5}spK;D*7siDNg_lHSWk-#1v#Mfp7iEAln7Zz$&Q(BXmB%acH zaLw&>hP6^}nXDS0r62l(zr;lq($3bpk7O;NzF7pxG*$cF_Mh6B?GTc z%o(ntP0=MdUR2po;;v#X?aFnwo}A!OZG^b6lH$U1(tv7doiOF5zJ%4x>DGv@=d`BB zf4bUX-sA3=R`-Mi2rEmlr$=?hA7#{)N6%C<;qj}j{3i1i#@%-pY$2^{?w|+<{#^( zGD2DPN4x?@KyGVSU-8m$@9s>m^&Rzo#qaj-1N{d5f3+yH`IUP4jG|ffNxKqzNtVjL z-!yO^+}U61JL>(2Kig~v^c(g5)V~|^Q~bo?l$idicPeyNnv-pFuYS0uzfL{<-Io5V zzOUGW{j%HN&~LThwLL8V0Gxm3O=fgBJxpA#DNO*6D%oUt8G8gYoc;KEH{Md3y1z&G zrZV=2Z##rNKB9kJREKW-^uIDYdO}ys5UvD%{EG#(M2_#fLIxE70B~}kQlr`aDVzH* zfp_k^+x`jf3;xhQ<}nLSt`5QXm4_*v>>sr+NB;n{)5~d4HxEj?c4~ioH@IiBU#=4AhXcD; zH2zrj{{Vt+e=W9Wg~Nyc00{p8%&mu|AJ3Wp094%3us9-brhnya$;k5;5(VNV{7j7) zF%{X4IO1N2%9Ik@Dl`KYUANqJ-rh0}f(!KJ;a?R(-<^v5D;q7_W(Bc|>L^a2e!msm zA^5Gu#Z|-vC$Ltec2gSxvU@nLS%)mw4k+*8Q@_?9a3qh_f_Y9${PTQJH#J*qF;zD8 zbdP6<$Wq;vA=I>hl;%xTaCX}|oD+9tq8f2r`R9MkGmrKvAFy$X6TSB`btmzVX>5+J zl`6=tox=}ewhcO_uDzad?&uc={Z;zazFwV~ciuaNTd#7w6q{|YT_eQ`6w=#CI#7}9 z6*z_5JH{IrsM;4T8@C;%yvy7N$yUZVlDM6??^8E5>V5X)uQqML#W?!UdW~p3fMt$d z%3QkQb0fHw^bZe_4v$D>*(k$~(2uVxqpB(2w>k5Kl%N*wtYm#ao>$0;EqgNw4e*xw3*%l#b5aLxnpdwMSDCp+zlPM`_1|WOGOv-N`T5n_`TBiKmG|gA-WxweJ9NDk~?}A})6TqpRd8F)q{M%0X^R4y`YsiV&cx99Hr%I7~qr zt6nnp)8*pD{ojxK;ZcWcHyQr`MEew5x&Ht^FU!UN@~t=f< z`qY7j7d!VD!BYPK*Y+tJe#&k+HS)mqxuf-{`QQ6O{)b~Bz{RU_P6_xMMU~WQ`dm=N zS?+c-#QP8IQX{aL;x!E){6qGp4e$QZ*QEG0{3D3>UGR`{OlT}q+4u5Og1fp{@^r7T zr`G=fm)y(E99Z9{_Ekoc{{ZbOaT-GZ01NcvxE?k8vm#ZkyuG?4FAQhyy{I}y~=CCoB+cOb9w-SvLdyMHa0 zeCBW0`xJk2{{Xbb$iSb$eK6WHs0K~;Kp=J>yNGb!UhjZC$o;4GRY_ZZS(tyBaZgig z_K=#d{{Y%t=h8p9`ghwAjZT%S={?daC_mIE4PD+j$^KCP0I2@N{C-)ix!gPck^cah zH0S>S+DY|SBEKiZTSgr#rmDC)NBT&@U+*2z6cLYa_#-p&$BpMRkI+AAXG;G7v{~y# z<*8A2Ue1V<@T26Xu7bU%=2Kh;`^|mHgWLWH{{a23;p?o9KS%ha8DI8?eRsZvhHZN; zD#)T1n>@u(*QpAdpVxu^0C{pBbbEinIB(^LZ`V@#eIKzk-v0o!1Nn(Ol8Bp^Yg!dr zha+vo>aA+EPW4B|a)=PktqBIH+8is$RJ|Q*V3$m`w@yoJig28Bx}3{wPcoDAV-ft6f!{ z&0B>XTB%y$R=ZSE-4LZoPO6Hi;ZjimC26nvI1Z#eD`KrQ%ddV6N&qUhr*?kKdZ-ow zuTe~S2|;UC5Hgyh^n{-(r9-%2lP7R+MQAHt+#sA)1f>mKE8mYJfM~YTKZEVSAgT_c zwA1$Bo>DasK3F|Q5qm0^Yn89E?s>LfOtH44H%wStpK#2bcL3@6mm)`PhO0xEMKP>e zU%EUXV|3~|tXC^w+l|rYJ6|p^8|beew5sg;i=j@cjO&cZ@UGAqR$%AQUaIa6%@+}xeNw@CzDzVQkY0L zRP+_<1Ju(2;1t8rSAGpAe+MNvw{a9<^QiyD>RgBBwoR5R<>2o!Kum;`0) zd3gv4rb(^^5Tz@1&P}=BGCPz9<8P=V-GyV2wIMaTUNui>m%DAiB{C1nNj?7n0Y1*` zN(+!mhm#)W@ZgpsIVRe$xUN+PGM=eW!>9=bMBK1DirW1&UTvz5(lkjPClTHUT2~9+ z9=6eX#xE*Lo(eWyxzL~sHK$4n;uOGDotGg7V<`rnS@vM1gdPaSKzX51l7K-@mG>M1 zBEr*J5^^{MAfR-Loazodl=Bv8-PgY!N!4gj04KWw*3}p}0;bYP%N_wJ98g|K?!gkC zV$;f}i$M3+0FCA?N)2ig*R3()5cMy4VAQEJBjG;GdWbrfm?}EWO(|TkHD031NeTpY z$ATlu3F?ZJbP`E5gqO9M35=)t;*OvDz+lAvpu`Y>9c z@|J#Kb*MS}FrywpKw}7GTpvjAIHZos92@3HMK++U87@0e^My(30VLq&TB8ea-Uz~8 zZ3)Ow`~Lt|5JYq(Y$c|hEA7D5d&pQbE09Hfryi;n!m^u*O?04B9;)%T)ercIpsGd_ z^O5|8k#bLJN7;e%SG=FpzJ-pFpH(8 zi{4W9rDap9oiJ0?%_`QfYC5a-V2I0bqpE~c9$s6QH07j|-GX(W1XK}w%oL?5k2Tdp z(?zzhMLH9Mp+;G_$clp4P$>qW&>SizUNC~Z<)#Na%nyB-R{t+y`8OdkIL8gcI|@<;bp!rOG9ql~Y- z`KcHWEz8<33uWL@Ni3Rn7f0;Eaz=uo3?g1fR4}jnGBBT(y`tOYuLJ#4C)gfG>%p4R z7qnRoT@#UMq@LfsLBaJ!pjpv${szDCsK=_kqQbitx&wXbdisBp9J5^C^bxob6Om)WdXv*QQ&~WXbPZ?KtyWRpuc2Q9XUQaG8~`WGbF2S018S zuc{46TN;*J8d5xFp3EgSp(`3{y0%?eaUzLX$n>rvAH_(<2nZrGG|$_D5RQW>T>&Xp zT}Z*awe1$PSMaL@bZ!XrTIqAJlkBDos_~-7%Sa-jB&V?9V;LzPu(R?~7uZETmHl`R zB@dVaz_!Uzp$F>1ohVH0TYeEIyX?oE3yKEZgfO&9Qh@#6rwVyRC-Y51iyVW}zg`2# zJ_x@wu&uu(RQt1+e@p?lB@fRuAV%DiU+hQMfca95_>)jaZOQFH`YD6_kw*MUsJqrF zQPQVNWFfIt9<{?)*}Mvr<~!z;l;@h0M5iH$a-}1p2(9DQ1pNmH2O6blwMlVCfgfSO z29mslLsC_kZlLtb3x;|TOTa>ya?+BsN)EovC*?VKND3|Gtkm_U1IuzXR_F>sPIM!v z$C}!u)$sIN!S+s9weOqOD@ z(&HN>Y=zV=1e$v3`jzp4=>Gs@xZnt|MoLn8c}S=|dqr`yvtZbd-3y+b-5iPi^X*H2 z^+Vc;)27!MwV!|%8y}%KEB0leIy59laxae;l+eBUVYb_wQ;}`VC^wqOWPZ#xJ4V=Y zOU1tK-gSBQs4sExq&q+%Fk3|ev+{sFJ=lx7+p#d{6|Tc_%=!}ItB&`yGYzGANeCoV zPyvcwZpJn=&@EeSrpt# zZ*_SqX@}j2dXdD(CASJtNpCW98Faxsq=}bQM1n_z>F>gRTavwD2jvmlk2#b;T~&%H zIntEDD2lzQUH<^19zwMRW1{ip?GWin8CJFFgD~od4v6AXG=!_^7;=y+bs;pRJ;NSA z3NGSO0HDm^k4iPjvVl!i+1rMrM4)w0Wkle}Kw1q|&oDaU$mWzrD4jZ~g*?QKO;T&X zDa!UIt_w1G7||B%>;s^}Z52y}sTJ*}5_4*WV+he1V;-XN%`1;l0vsfq9VWN(zomtI zrRuLLr@{_>rvzRIGBlbKQ-T_yUQK&VaAsDh3KcF9*D;3)GQ>DO8g&P^9!U|Z%0+YN zapmVN@MNOYj_d&lD2z1%Y=rA#D~_ObQc_6AfJF0}-X#p1MaYW$n}ht5g?yKCMK^5m zTZ<*s{{ZMK`iJenc`xup`S|=+-+*0z`3cXce%uq~T)p^&ekcQDybR1IKg}2qm3mj_ zBk@{(7jQ~|R7VYZcuouCIRLz2=7U-kf>5-SDJF#^P~g5%=774ZWTv$TDq%!bx{?$r zk0C`%Djp&_cHy-V`qqWK>q`4^;t+?dc`3)Jc_VqNQpR;1JFp!tLM2`ZJpm)xk5R~c z#i&XCBZXr}LpHUyP!x0S!OC#Se&{@PEUEi9`Sep+>B$?#GdXsDy2#rnUO; zEov*DR5prq9dKqlL{z6{lNuGmGqYL`#zKL4zO@}N^2yC>fp)2->`r`SH;S7gN#SX$ zABo3Qb+6(93mQr5(-xTLuyNEXQ=rSo@RXvxtLf@d1+C2F5_+VEDfCoe@3Y1p28g2} zoNp0dp#;7x;du%MCnO>4b-(f2_8)F!5??!yc1xFd|X zJQ^vSM(#Hg20Aj@{+D=-5b%$|GOnbKOmQtAT8B!FY>xD!LcxWnmoKImOWyQ-_^G1`Y zP#ccv_Es`Y3woZ>P~Q_!y76iJwLKqBU_9@wD?5qkTy6VW0X?Vh9#%GH_V*1>=7r3z z?{@31mVfxaYIaBbuzeOSC@b}wY$q*8=WQANPGH~d<{SEpP#gU=P*C*?ic!l+LQkPd$)K00K)x68&BnT=t){a{bbukdeY%*2mSfS zzuVj|^#`>F``x@h{uk;gNk5gxpnQk@ac%zq{xh|ITOR)aZt$Pf9@NR*^WIhe0MmU% z8c*e2=n+&O^~tuTz4uE(e@ezZ{@vj}s6DIS@1FAi0QhgHp;P%y`Uzk5ezUErD(<>k z5B~refIYjye^7f;jPJJeCtF|sQh|H&cl2W+f80G}+guK2-~A(HQ2zi$%rSp%uulH~ zPTS|FcEI`kqoT-Au}vWAMdb}yIqBM2~(K+{{XpJ@!B-~ z4*jV;{{YC_h+kDLKCB@ut0ViyfNl>hZKL+9@L=x#b^Hj{t$oje$<^W@(^lU z3SQgwIc9xi(XW_9M*%d;DDMM=9Z~ec>8z*nbhQ{G)-SZ7jj3-D$W}Hh-d4Xuwv8;s(4D-f$y*p~`UAdXB4sVAw ztM>1`_Sx&dmN7afk)=&R^l%?X6;{0kS$#-9>PKqs&q})-Z_Zxh$n&nY>r;)ixXi|K zD(#QD@0|Q(P$F3gtAjE+(-p0SjJ%g19cMWXHhy;vM_6}*9JZ+gH(|rKDPy%a37+Lr@p<-OauIM!2yfgSh=w}J;<3-ZcH9qCcTCDv0TZu(q%s^4d@w%ycyc1{xy zL9f+7ex(#$eOPY|=#5+Vm2O*_gQVH-x{40uDO!HaN!sq1>F+s1-S)ZR-@}TEy7DKe z=aU`lX4rW}PJ#@|&ON$y3X%!s&LD?E9RB$$8UN zJhM|$efY<&Zzh=aJnFhG!p;kJ21Wn@6halIy@#e7`H9(hLQ|rvkn4r$i&Wv-wkbQS zb6B>-C84R8=?%q>>y-!&I;|-xJ=HjSkFhrswlHW;iYqmd7Iu%F!l8n+9))w={J!~F zhqSKiY6K9HqU*g*U&HOjwtFRd8Q0X>de=I)^Gk;=>GI?TwBV`VBW#*mT(&jVmkx-U(R5x5D$I?GE?H35ii+{@O;mg! zVTzD=)nL4$3POmkL}4$b7)G~<`;nwKA+ke_C@HmrLI^bn3qvEA`h;7Egp4gI_r74Y zx$P|PND0+&Kudd-g!R@2l)MLWfX~~BjKl(Xgss%`Ro%aK>sigToDaf(QiYV!T7fLi z<~Q6eEWB2E#DR{AtbGHnn@n$snVXW718Tw z{h!ZLa@l3;k3Df^xH>DL8<|g$^)ifFf7);V05pD|O3vo*v;g~%JdaMFsHZ%C`#t=+ z;2$&Ur;Otc+FV{;KhjPM{?b4Ct@eM4L;d@Y{{Z3N?f(Gqrvy*?Je%FqNnW&g&LI9F zA;&-aVK{bsMgIWjH`)Fv5BL4Q{{RgC0B`>Qg)X)K0B36NM2SATIO(?(X`u+#2hyqO z!qIk$A@NEN)%#K>^8w*#{{Z^G{3%Jl`#cfQpYMLVwh+_t6b47wx!867)LITQ4{Bon z0L%@+_-$S7{{RGSulqhbDFg|=y*6?w{icBgeOjlSC_7PW{Xz98_x@n=2jR8*QPlqc z+5O^1^Ox)MYqmSb;Xr+ApM!t37Jt?I5=XzebHdbn9s5wD@*4En7=&E^05Gk)9b@7l z+ciJ^Ea5@gtZ?|H`oeAg{{Uxy{uFy1{{Wd8N92X*b|O@6znLADIcQbdNw5F_%=}Iq z3wvXYZyCPN*qce;-Nt?kHlLyY0JRrS0?eb*qzs%y=)Fe~Rb(^NuZ6Nzp=vW_C z97C`%aMbyg_AgrZt9M*HF}6*q@DD%zOM{&CV&c7(&0LeTcsqH$$lOo1uFpf*oAOE; zOV>zDB?vE)${IqFT-Fk#qy;1bI@cK;>5a3vN2@EL@f07ebcQ%hC9@wm>5z)JcofCA zUQAUKl>mI2wWAeqD>Bx4^F499ESv%X?I*e1V!p<8T;^yleo|Xfz;ClqEH2|-TN>!M%8Ggn$Tu2XK_Yr zR^QP3a71593aa}1JFwfRr9*0@d!_W9r?ocgDx_;#B|73k>h!0A$s^nbEK}cB)sc<9?Z<-ikbo1@1^}WZco%DgH4iMQ z#>e`l3a1Yk_WNpQ2-V5GqJXeEnRE;Da>R^1ABNw%AvG()ZckTL_MADOLDW|MO|%ZD?Z>o1S0YdOAb`nNw9^erII8mx zlmHzCedd@uA4TU1rAA$Yl9tleJE_+X+d`T+I22Z4ilrq{BdwxOi?*0TXvZY9HRgz` z%^$t`u!LnUD1xiQqN{}0s1WU!tI(2rMjV7gM5P)`#&oH%MJKZ_eh$aEyc0QLYlf-G zA9LSX(@3qjK&OqAD=()Ku;0C-g;ZR)m=82Fo1WOxI(`e}wz#$N6eQV}_< z=Tw&DIRMZnO)$zx$WGgE5MM6;0Q^_mgj~WBo@-F)hWe6mEpW?Sq8(9IOV?33AjDKzi`*FSt-`y_pL!*ps%FoDjf0d7aZa2X;{_1y%GB~o` zO6YYy^&ETqYo4mSqaKjzlL;&nMrigKsy#$ywD-B)n%TWo{N6lSjR{fXQ7wV{+Y z(q^)hpiv+#6dvsHDlQlWDx+U*DK()*jq215RdHz7uq{8g9{HyDq&`k>0=52Nu@4Ha zP1JtqP&ilS3NDF4&t=az^5ENg)KSz zFsIEhFC25xSFg#LE)+P>oDn?0XFB3kok$tbRCK~VcT==Z*=!inNlUIyvm}(a9S8^d zI91^`4RS20*82EcNgv|>0D}m5hatA|L%9{TzCu!4FA5o1;RXaDCJ`N}hxPq@ceM1l24PPQDx+tS5pJ+AR&iYN~@I z`vOi4NQlL6Vxmfqh%ixr(J|P06Oj)Xu=u!47 z0Bz*(ULIG7K&=vVX+CD2-n7F>80Lx;;wQ&mDIAp~nu>$d5TUeENHsX1O+^5~lIu|+ zW1tuP^O-5Pk;>|>gkPBW%npzAoBV;wXT%? zYaH$;X1^b~^Sr>rKqza=)UDm8^IqZT&IMd}dvUZu&DNyao%Wqpu-w+!D0<`OsyR)Ye}lR5u1NH2RaJQ^ z+X9bhkeM245>w%)&~Z@Q7WyqGXp+u8GP{j%WJZg#k!#4z(IK$ zRZvLOMjEy;RJ@aDtWCL*17%)j5I*iJsso|^%=hB<92;We=vm1n_w62)Mx&6ODp$mz znZ_qK5?1{nP6nUy<2c$4OMuk(;lMg5@-;~cPk&4`B-Jho&`^>{p%^<{7l>PZxG;8$ z#4P|;Kqvg1u!0qeCb~*!d$Hx@Do$hV!FqxM5W&RPzuASn!jUXmt=a%OQ>G5r=#0i9 z<-kE1;U1!l7_?e}&!ESPiAHNyxCDNZgC>fu0T?vVZ}UiL2kGv|s*B_o6pkFtN;&73 zwz%`1RUlZstv=tY1Pk3Ids=)_44iQHLDAPh| zPW-S^)LKz`r}sV{%ozd@1J|>&0tyAP85vbnr)~fR?xntz4$`l)2BI%fS_HN`^Xq^{ z)otMz6kaQ8gYl$eE!UzmUY0Fqb!a=&gpUrINY-v#fQlA2OV&-qIIxb;d8C8v@U zOO|@Rzw?d+5{hDHf)qa_^pv1SS}2?;<~e)IZ-Or8#B1Uws*i365VZsV9hJwZk5%dp z?0SeQwNe2cFa@Z=$RdRM@#zMr%RX21a9SX!Uh==Ak5PJ|WHnL%PG=sX@o-ywsr~`= zJ|Qm5&S-HN^mo<{qL;?y|Hso{(^Gr;Qs&yJZx?~DDBQc_rA0r^4-h-0FaOQpWw&7 zM&%pvsDAg>z5KUw{{ZA8{%80wA0u$R_|!jp>slSRKmP!c4S&V`xFN?(c%ux|UwLdc z)YJa}p&#=H?7@EvlfePaMfbk6Alp*_{{WbkAMpkZ++>57)QEFY{qL;|jkz;b1-(R{ z=zoI_V7Htd0ZBVE9X>CuEJoYcc<0S#8GJmP(6}F7sda(7 z7^0iF&W)!rrc{>c{%Xb^39ncjV3&Kl~v6q+;o~_jIdFn?=ubT_;-QX)>*n zE_0rY;cV(gwq12dR+1{3jE_t(zP_GF%U(&Uab*k*lc|ksPl#(l_JLJ8`aAU}&0dzb zM)TaJM~s{Pg5 zc<-&}nzAbY0Pv_H@pF}Ox=NjMl;qo#g_TN_ z@|3q$v(%|eB$JnJC>OIm(h~DhwF?VIM-XgcjT`{ri1~zoD_--4jg-+k=nF*Dk(NC~ zXC-zcDL|b)F!`|(6nwJ)m+f~+Hh0YsAB!LqN{p<~w$4NmhxT_)gwyv=6&ZhI$=hBI z91o=iliQ!|!M_x_2jVH%B8We~Jq13RevAmp zUj65LN%>cI`&+3`Xi-xa5n=<3lv@mw}@?oSX= zyvF#id2Skg$Uo%a68Vo2^)Gdg6`^H${m38Wj5qmr{okn=N<3D+O22>)@^D`$j$cwY zm=JQO=tLz1UpXG^ z2B|?3+s`Ct^k<$FY?NN|wOahmKknds!oB3J44-JhI+27cA2D9q{a9{ZN;j5JdR|$p zY^mPz1#qxocL0Z|4kn9I#L{RYI7eyHaHo*}00dzqek&g(I-m1Jk8LnML;M%MWvk!-Do-4IDH+3p4Izw&F0yE+y2F zg!KbTIB80Qp;Ct=`f>DsHMj2^BkK0wYYQ5i*?XOZt;DFPoej&5mQ_K}Yi&gKoJr?) zRu$*2(&SL`JilMIsvB%?t;$=`e`4y7-eo{Ok|;;AimFfIh9J7)9OTWvF%6OvB0Q&D zLdSTb1}U%GF}M9jtJ61Ah|(AP6LtNaX^bl7^MfFc#)HcL0E33JVjyqtf}S%uJGeyQ z>qxIawZ|kka}p`qfPdj)Db~?6@lTm?Gq|>hscPF?-CDcyPwl~a;!ZlHSP8e+Ahpw| zs2C2okxQgPYqlg)<=0Q~h8%UqgQiavfhyRMK?!EPPP{hrBStYv;;?1FBmr-z?<+O^ z826ap1@9&PE5>{<5B(aS^Ob<>9tcDa@j;ub3F!d?yCQ#HJ?0Mu?~-x-NuLct$8$sew;`I6G zlzV&$AE02~ctrUiwimfM0+zGs_i4wx@VsnVlo-EFKq>H$dZvI^>cg>cnYzJ+Y^gsu zyw&AbwDhUKk{l4W9)tv_@5D+=%^Er?+nrLO-VPeNEfUQjq}3@TfD@fX8JK7QcckD2%ZDT% zhqJ|GZV8~*w4)9(WrVeYvalO>r z+avs1414hhcp>~Pz9@9=hX4UkTA=7@gZ%sa5(f@EN;;j~;%QrZN86_c{6*dgGlm`| zZT#G8U&5Lv_^5CniJt@?gMXNzle^MEt&yYLXiw_E{v`_4>XhIkZ=jt zZP@nfno?NYETP48ZD&thyKwT;5fq=iqi4*?9~P%`dgcMdwbSrz5n?T)qHqAHsI0E{siaemNyIV-94H^!ZKgxu0c8DqqBQViH9l0DebQDe-aaFowGzAN1?ERQW z%0w?paP6!(NGPR0-Pwn>v2jVdWE|UDZI6Hudi(Hs;Ho@RBy$93_h4bKKnX)4%Xa-n znn3o`3i$yfW=<=8fie}P4eBSRL-pcH41ZFOxXFeo65YoyODNG9M5>1&TK!nCTJ6rY zQoY?iT6HzVvkC&g4_=edl>Y#S660zg1z$Vmz9@h8R#_UC2|#wJj+kHjQ1~R-z1n%A z&++37Cs&eFPV}1KkK4cw3ClNSO)8snc7rT}q_sqz%4djcj4*1Nw#^qNZ2MD8^q>Q_ znVdpg*|n<2Yj$11H&q>^_0vxLu@~htnv?TnTZULlli~)x%zLGX^^qx|*r6jJevyU5 zk*0^tLM8ZTsR{a1g#_`Sq=|kit!e-qK;ysDs!9-hQc8bb9z^K5rn9x@f___F+er|Ax_?y1EBFHxR7>6-kehw*xa5;BwuzzVynG0 z2W?2E3HXh-icn*2e(73&GHf6Ip0o9gGWq*&6sX2e_e#(6Ho^Y@>GMBW$G(2w#Sh`v zJ_|)|Za*f9zpyFo{euM;_TDSshTVPAhF0$5WK;hDc$g!p8WsAJf_(kAit)tW9z`fa zaH5 zOT*M31GWyS^Au0;!(ZzdPy4dV#UwTcPs8gUwFaM390tiG{wDD5`AT3LyKKQn{{U|? zXZ|Dhtp5Pkhk_a)wQIXbJ;9VDeWYPSyR3pQ`)QH%dqTnWW5La z0NwXyIU)Z5Ywz96ds34=wYX9?kai8}$ILjEx@O*p{RE z?%^oFC0t#6cAXR+fImv`@ON6)G4K+jpV&Ls@tLFB`xPQK3mGPs)=51wC-vhs`DpwW z#4=M3Xhhhp2?Tx`%{#OY*MoTx_#%EGH;MMFrpswT1Y23C?ef!s@)_X0^YZwgYBq=v zn@}i*+7CfS(mz%NM&ATeaUtSSu@$7`lqEjokJW`*#`q+iE0Ey;3IvS0VPO+$lWdL) zbhlAmL+Zj#Wf8>1Zdq)A6p*3r7y{A|i5BT)lBE68eYp%MA_)CjB?h4|MO4VIW_TH=2K2_BEbTQ(JloJw}{aEq41I$y2X) zB&;V?^@GG#d0;6vWXGf5V<>OdtyHOTw%IR?mt z@>N^T=uXexQU$-#<&p50KL<+lprEh3<%x*49k&t{KJ&b{30GcssYEpv@JgBRzB?Cee#*NKT5V3$|T!Tf=x0& zre5QOrP;RL0Z3bScJ0Sk6w;UJrpSdB(+LR_2T@7=xMeQSx9|#XF6vx;;-EfS;rm|L zvA-pT8*M<46bD*ji@SF8F+^wr$Cd7rLcq#j|>5hG#FdVv)Igd&x zZKzQS)Kagb4Zd6>{Mky7Hmc_nM?yjPK_9acHu#RD*5fWC%CNO1Pk>Ts(Bi7wdaGk@1r|R8bFuODU_M#MeqBmUdfUA@^5*CcIk$GguE=dVoLg&b01kh?Ml2C*9E8KE zjjF0o-HbiEx@D83`jbZc!F;o~Uh;1HDr8k2he{P%WPTMKGP&5f2>7t6doJc-ClzGP z`p4=`)5e?aDgOZSN@9n9lt{xfv@dxur{jBKiT-naspYi&I&yyXbhig?I|+mk9+@C{ zXhA)urnr>GcINu!-wgtV%lBImWZIbnkAPirx{lFwa|>SHrka9`{K`~lm2|>H zfuKn`Ahh|TnF^`;@!;f{g=!u(Dy<3YfkyJe97q)D^&AQ>l(1g}8WCI@rF+X-`IHq> zzAP07ma>TRao~llSyQH;#BdVUFq427p;HId}0TNo=H62b8lBvi60jYJb*-Rr;r%0mkQjck-JiI_gXnU{} zDH^SGp6n~;6g^1FP^^HiST#vVxxsDZtzCpu*@Zws<^(E1HSDH5(n=*zx~uSt;LFS2 zB0vCwD^Z>e(oj_l+fS<^2|w=d$Ao1h=YWPxSJY^HKGJYLTai5Ag*Nno;(n*Tm@N=| z%G5=Ug?Qd7BeNc$Uh|61nDi${PL;u0${}l z>4h~!s@Nptdiw?jh!?OZ7qQ~JjRge*o)V^rlpw66Bnk{2c%T#%liNen0tz)20u+CA z;8&`><+)Hab;qbzsu#Sq{{RjR(pRb$e5Wf-2OjAw-9cnWe?I{<_Fy^+@o+*WBkHH! z(*;0Y5Z@}GPlmWLjHHXo+(&SB9R2uOIFSp&_mIBPfubjv7T!tE!}MWKD6e^;ZW|y_ zBzrQ#zDh4xL!?%Owi8bA*8~hnl8!Vp@U{s*(Zit_NyzlC4J%T>`E)GQQmF)W_Toa> zfiSo<98{&ZcI%gQ29s}POU$hKW3v`(EMJ?+QnIWXoI)$F@9_7nwJ<@%u$=f{_SR{Z} z@{$H*gd}3>&fP&xR%thTuR5t!7;=B6Yd+7lzb*^vQ_WY)BmtE%T+VE$ZP`$++b_BI zH?)1(E8xwd1T@489- zSXcXEK5YrV`-wN-KGZv2n=GX2cWkjk*jh*G$G^3r`+*bv!n*tC+Lapg!?s*XOS@h% zufhvy!k^ku{m_&D0C8^{{-O4zW_>Mef(MS|7R+XSo-@)IQX!1JZ`b zoUb9hs)ahUr6>bD{n&QhqY3@cqh#)t<4?wYsV{!|d*B`=w7Yuc&C6)x+ise+#JPww0Kg!*Gr-!ntzw90w z=KlatH?Kc!?N{bRlCWR6Hpk1`*iC4BW{9Bf1mZ6Vv@$tKDjPxdD|X}GJAZA#Nn`=T zm1^Vkf5CQxfjt3j?T@uxk-Joz?%-Psa#{gCQVxpI2if0_pW0oqwz0M*PkGc_2i#xG zE$&|+8*lDMT7SiHSD8Xt-y4qE8Jb0hE<Me6w7+MB;6734=5TqfR>Y3JQjI-_ z1bi+WkhVtnqY-XW;;4g`p2L8O;IR^GINlQbQklA>GU)Q%>J`~vW*wPCgH;(NyP-82 zgoQB*j_;PDRiu%tvk|uqAxc}Gw$&@_M$tEsIQ}DpQ&O*%hMgUSE?k*GA5p@VPCqcp zb8~<31YEOeWhwsvBmV&KmG%3)xF2N6k2NiA))1{9cB0q>fS>5&y*~_x0c#2p{(s~j&tP;40c0N?bmq-WuPb$k`my{{H$=U>tJR=KsZ%>QP`6i zu<*Q{OMh7kEklNbPfp+W97F|SF{S7%1+6;15!Vuy(S;du`VAA5yBUNizMudZ5Br#q zC4g~IqBpqcnzJtfYax{64_{^=?(V9mZL#`lv2VW0i12Z@C>8Ob^ib|2y9!%mUel(i zw^+7F+7&JA=XOQt_H!8mw01yO*(&(&`YTO9!QGr9A!Zhy@>p zpy%J)iJN}iT61cu-*>Yj-2{qOwE8aDvH~@2WE5rM&V-YR$a{TbN_48t&wVoEUofc> z51|c3e@+=(_Wr;hg-KX<-#X(%DNyv^MI3gQl!ok~ zD-{JbSAHWV*cLx^Tp;gmV7FNcb!`6tLHu2On;tCtK7=~7rAq~04QOaCzLVot3{ zU-!o{#uC{LRo1e8R$h~2L}khLH`gGgK446zQ=K~eSe0jOabt^CbwB1abpxcoP)>%w zDt|}r(wlZs{vap6(!b;2f7?t?-9-NY?kjbFSLz?JEVcPcdOUqP6Sk%#uO(@^N=m8z zdBC39VyU2BADJE1)<1|i_z&2=V*NyH8*dkL$0>2um9-+vv@a5&+ETTt#R%vjNCivH zL<35kb0+DqWwq<$Zvb>(L^eKNOZ-GPG8(<@srA%_5~Yw0Ps7-8TaYUuk#3Yw)O}b< z6uE4s)=BYJxHFhS^Ii}iF2CX!FG^pnW93!+SQ;;Riy|t8YO42kU?_@b3o3|JO5{5Z zJ<@<&QF$;c(N+2}?v!D3RpSRuDxfQmbRqNBe-Ay17%QTx_nxg^4{>ar-M1Q>4iS{3 zt;DTqO=v;wrZsJ$w@t(~rB*k+cVM!)Epeby>b*W*24>LZPiK^`*l}N>?MYM>0S~QfX<(Ug%5GS zpz1;+K3N{D3M2n_iPCI_d zl`!fo9V6nPIsJHjX@q;v1ru|dV>zh;*T!zR5=?zsx)2Y$JXtNax$sq+z0}t8PS$vt z{{RRONvB^5zh)-l+I%!nuHLbO#T;J}Jdg^ZE~Bb^wSLSxcG3kgcKVOS1OCdL2A_wM z{z3hi@Y-m<-wgZ9Rq)^ zTrxX*UJ2tjQ;7JHU*vdHDiMMHh)xUrygU<+?sxd0UD$DyjejOaNB2!K!Xs}F1gu-7 z)#8#I+K}5cRA7CS!VbkoIVX(Hz1#{GF3*7afbuANa?9< z0X3)Y)M3ML4IwgxSV?)H<2xwg<~s8Y7O(T_AC zG34#ZvE9%JAw3Vb5E5fcO%<*TiySJ68%*rz(FeQ(hSJ$Pl5_KHKn5o=v!z5SJ#g4X zAmEkjjEz#v9mlz?i%E%bd5KvD!=RNWWCS%+aJKcV&ExT?88n;CLgKS;)-o*IPyuUKKkUCbCt{=w;lTes>zFDD2w+**) zVyaYCX&sv4w0lW+=5cng%{dP4f z6-8C_*<3F5)(J~i6$cQ6hqb5KT8iUDgBAluiw?=}G995=yKqdnsd+D1+i>&@cnauy zX@R-Mic*QauJwI>B@OOg9%&I5=39`$m5?<_>Ha9hM3{j>uGqccTt!N)d$){TW6f<1 zB_Sy%A)a3RR5F;LE27k}+oPy}3aC4;Z%m5gsZ3CvB-JE!si4MJmbEP;E!zWl9~e;$ z!qIaJI$G1-0m~Izh@V~x>jdpcPT8+Z)LLagC-=d`d!Ur3!r_S)MXvl6<7HrHq*UMx z5(MS4#EJeDeNiLJSO=ib34E*3&BQ3{TW@r|PbxExdj<_1yv``5<^YwL*qizK1}!f+ ztM^kX=KLvSFi&H1`*BD~%Gpwl}Xo8|ui6LyuXE1(4#{{W?jjoZ_?HFoBe;4*8c#lgq^c)JP2 zlro=hoZ0<&Z~eO>Tlqo!uQTuV`?WsIb zUAdu0+2Re*?r33^4%)h&-=_<+YrLkiZtHgOs#0%)H>1E5MrY}HBMQf}CU*ph-*&9y zRJR+`KO@Z3cOa@-80V= zeumP2l#Q!7k?7&YiE0iiW{IBzLAE&FA}6Js~yKm zXqu#)>x(Wsih#?S1t#6?>2Ad0$~pt6^am z4a<>YZbQ(Nk1;5!`sx(RBi&4Nt_IMxfpC`g%k|E6R!!ec+j{XK2I(KnBQ+wvEWd6n z2G%pgEa2v+i2H_%d#|S*foqDjGiyU@0G(B-_te*>J@|Y+0U5iJ>c0_Cjq_()Y@z0% zHkQEs?KG(N<%3BB7&HoU%ONZHnyTl!SVT0wLNirpp5k!>b4slr4(g3nS>JCgw4@)r z1tyF4c2g8Az|gIuY{#foUU1bR(@i;^&g^CPUlP~gG-`VB)Fer{wc3+RsoPvlVC_Sm zs~f6*h)U~M5!vFgWM#Fsl~uhi5(Y_ET)v!d*n3#aAQi`3uI1)(L85`ZL;6VL-R#Tv z>qWF#HxUX_rN$aO)6|y|GEw&8v%5QYWq8h@DDM@c_V2uz9BC0f#qszb)VV{u_)hJ# z=17j@()&#k0`!qecjbqj|ay_6WNoM9YFxBe;* zC4}v#6jQ23uI#X{$S)j_@J?7faXxZ&wIm7(gy|%ahp}LsvBn@$ASgmlr8C z70!mZO6Lan#cGDraWYp@iKgw9ZIyp|3L~hY#A&i}nirt*L5oX@G)sVz2<^wdNz30j z5!Uv_)x;ID*;+?hXMtumQ^6wqv`sW9lh#|S0F}g81K4_Z;IFckypoaRVRr(P*2B%J zj#krF=|vCLaOh#!?r9^PlyXCjjcrv(xFiqplaGD_ABs!+ zhHtvCt(j*@qOK@O_mA6w{01)&yfe24_f`(kv>*~L&6;)-gue%i?<2P}IXbkW4%ls| zIb7RN9Vwsc!fvx*=A5%5FWp4dYqzw2=jm6l3Skp#7~+-}ayya$$G4?fK2`^4?ZJLt zByToP1)(<73i(B6uu`}s8k7c7v@#+j{oG-nZ87Q(1a6o3t-RDKzFbm!*H7Dk=?O|Y zoKciUkm#svj^Wd;6ET$7$O0H!E%+Z5>@vO~V2hS>LT3 z2|*omsmlZMi34^P;>rQ4-5$&z@!F;vg z`k|PF{OJwEgAK{Dp2@haAx^rlVp7;qKS09P_Hs&S&`L&I)mO0UnnY{8>XQS<_T!3QIaHr1dJd*$7) z0sf{lQ(T(jMSFf-1bw)0a4<7R$kdZ__mYao1Kd4SNh;f%eW<0bSG7q|=&cX7pY()=G(2P|#EG5ycf3_Fj z#-io){i3%fNKio`B+{KJTw}L5+P!skwqP}ag&4Q&?JDuKNyw33@LNm~*OX zuRj57m|@n|{9x*;nj^SlN85G{3kB{DRqkn_T`aQgsO>^!EXUK~9x4olF@J1SZ5KG+ zTsS(YE1ueAbo;PWk(viwEMjR*;lFTAQ??olA#uVp>Ht5d2zf11pE^j@6@I|Rr6e(4 zlmrjik9i>v;!7yoXjKy1J|mSh^uaz@a!URx2NbCvW*7eK#~_ZvnyWtQVNqsJB+h); zZ#3yo83-^}bGo|4rq>)5NuMd^m}Ru5GD@8ym~C<(fxS;~3)XoHi?@bA()S+#sP{B_ z_4HlA>^%*=*1lrh@ye1m?%49AFqMVqPTe}8D`_|_9DQRH2IGlhQHuG znl1Zeep+`%W{;SByl0#A)6h7dpe{;tPv*bRJc1=zIIh(LhBnKjrH5g*iMN%YDAQE8 z&=Q)Cp)J%NipC?K?><)j-4?^kZ?yiw^{&3zKb7nbEuq&$+(Pt&T?=yGAwMl+X~EP8 z=}}Xnx&7jgyB!_2J40h=1Eu$zxTm;3n;gbKn5;*T!_0hC_44{CubK|h+tHb|*)4Hw zcG#3mco7;)EFmcr2}o=K*^MqMc&*#g!9cm1Tf3{-3*~WhkBLdw>+}}(z3p&a{`A~A zUS5iWsIsjfeK>J{cHwC5o-|6}arMYEtA#@N^xa#A@h5iQo}9G_ik&a!61tTIe8TDu zuIzmI&tzpcuIl4bzSCf_HtevBGvVlO*cKSS+)xIGg%5f~FpF=UM0euiD97_9#3#h2 z&wqqq-#p|`!ejKI2I9C-0F)Z`gbLtI^QepXRCVT%-&*82r(6|Ui7GuaKI})zdXxD5 z(M6&{&_a@Qk<$qXjcO94f`NHTQ6!S3c9WejcM79M6SIn1^Z9Rz72|0b3kc!^QKX}xY6(!DH=cE7uU19W>GTF&{G`s@V#f8+w zX-;Oclp?9A_hpWZ*q6<@x2WJ37x#w|E;{_zLhW?)tNXXO<{1sCwuJ>KXfr1~URw=H z#AUpHikvK4HZuxJODR^7*GaG2hpmsLMM-u_lWq1GXmLMqkU{X8QwV~ijE+wC*&Y%10D_$zr z1r)u;TOGa0J~IlqsuG+&HxfI2nH((xieZxJ0bKiMr~cc6oZaokiU zuYsde$$VLQ=CRDOdbQ>|O~wBJo0$^lN2QAW;=Go@R(Rw+`w?zU{ACSn?Hs@=7O~5rndIfKC@{G(>cQt8XCSzEU4GZ}Oz}V0?mu&slOSp$b(60PBR` z5ngS;s`>%^syGnt?kc$6?+gOm`A@DHQb7T*bb=4OW2bvdjdXT>PUg5T^C`Oea^75L z;Q)O!U1f>sZ*G^|FE$bxI*_1{K^??>IMrq7qUS-HxI9WmmFY)qJd(d8DF{)bl}v%d zcH2gIARC_Ka3x1OtJ22ZX_B_%AfQ)GQZuNgJo^}3z$r^@-Eug%s?oZ=G1+0PC8)_= zR0fcckzU*t_=t}XrtSCq<4UD#Po#J=B}Yq9Kxy{Drky6ew5B{Uk@z4zz?hm6xo7KB zimu#SyQMJ&N{tU)0u5>nE0ua>jElH-Z;jPz>JH53qP>ca)UJPJG|?1(Rk7^k*<-ApxEz5B%>z3K0b=DKz5=8!?CDM%fa!Y;yFo=QY_7W`A*DLPlC&O33`{{Ya4 z9ic!J(0gfya&7y61iL$pkOrMf58qy$_MB(mb}TeB6uJZiP!FJV!yBEwA0V&zZs6TG zP?Zz2>GslIIvFj6SH+|eO8c<`Cf;X)vAei)@Tw;7r>KSJN<|QobkGrBvk48l6Sj9H z%XYH@XgYTI<`HCR?hx|jWC zoD{Ifp)MX$yt6iYbogHLlAxhaYA~tVmZ}l`*T^G^^asQbrj6aM73}dee@bv)?OS9c zyBP$YKQB%zsZwvV)8GAo{{Vx8zqTOcosR8ab5V}}0Q4`iY<6jLE!W8p$Z0DrHtJbY zDw@!e05ro3{jU&VD#f?^t#tXrT)5TB+ZDaHxFo`$(%RG#ped*W*BBn)vk^={;Ja^V zWlhDfc~N+@GyoZ#R@};~kDQn6{9kt5?L50gGWcivMFF4DOh`eurMMpIgUIg)EcAJz zCvoAu%L7WKKw1!UE+`N)%d}y=-qyKi5SO#wmXew^!4!$dWIU5_1uQ0-lb?$Xp~Q5W zxl2iysP|Lth^mJjF`~Gp*=SL;rlN!B#Df_@qKR~!M^u+^a(JLrrwHt-mHP07+guad zVhV35b8117O*_qhRv0I!G_GQlKJY4?;2+hGFDG?|6HZA_pAj+?u9P0!@iAb=7Y!2| z=?Gz=Pwm^c8FteK;SSumOHxQ%olt0L+wR2nZo!=a%?hW2$-UCKgz!+Li+DNc^ww13;bY>Con8gC%?dfB9BsD7jZL9XhVU!5b z-m9ph0c0gTlr^p<%#B%$mXlXi>*(9_*6>prR0TDL!f1V%Nz4S*Eevl8wsIY=#SSZu zC24VA@k&4h*Sk(wkD1L1+lKQTLg{PV-)r#cLwwm9g#i${sXa?&N3^ z{%z$(ds;~(c2^kge&61xT{*PwJ-`ZruQbA6X;fv!= z+cHai4=jSER420y+{&jdnyAD`!AeO4>OGh=Y$T+mBgIK}8(Q8_N)zt`EKAv2673at zC30F7qu4to`94dYSX$@`J=oB&*yGJ)`-?DIRQGb@wWiReExk%3G4T*7Ud&kL$NRh$ zGS@Ur+Lcc?p4@&Ex7*UCC>)Xr?Dyk3%0gQ8w&56PU z&s0J?4_2-$nsm><1^IU+zG0~Owzy8TDPGZE@ZnP$Z0SNBi%X9!Ak`zhl@Hg02+t(e zSwfL)8)3%)34KXP>Jp+z&)CxUeb{QV)$;_Tsa1VpaA4R6wi0 z&iSrQ85>P1QuM5XIxAAUefW)awyI&bASTSMs-%*FNm^-IVma^{DG2YHQZn|_=}}LJ zg=7XAc?CIb&?vFnl2ENW4M%CN6X+*`Kb{fD`({~CI!1U}DBObL;i^R2yG}!|0p<9J zq7|JhiCa5zO0Q(EIs%uiyCwA_?*#&Br#cghMXjESm3x0cO0j!Gh;Fl@$8l&0OMn)Z z2+Fv>V=VMo{&P83(Q;3weRIb*uFhh~Stlgv1EzQ$lzJ=*(=b8qz#?=kb?m^U5Un7S zsPr8$G(bupEGjyvi$rcZbO6<7aeIukY@6AC0JdnYf}8M8mSr34!F13+m({$Z^ofm3k8l^{G;-!DNQMseb}3} zx1d?3O7s)OOIFRla3usqQT`D_hXw*wVta;?pZ5C=$s`|@QBS#8SinMN`C3z=J&u3> z03)n-498Q3a>8hl6bFh_{0`y&01&^eSZ-k`-9Z(1ZCna+imIPs?7@7pm5sq9t8C96 zFPB=()&9=prEz_(krHw&l&Fc zi})limF$JnU2r^C6)y}a5|#+@c9D)?y7@kOftsZd&5mbG=c2JpEDo*va8_TTY##Zmj3{}JCy-Z&#p1K2fVhX z{3ur0sT2SJa}>jdjZ;TB(M|kQ72n70s%!$_a8jU=)OHMLTUIK{7G=HV0@>cB^i#l= z_t&*xhm@w5pjS$Ar7{`!aP=;+uP! zmkUm%%8;;grVh>stSYB(#7R;l6d-~D3KSVr3QXU1S+R};(Nm4))SzASK~X0q<_BKv zRWDdpxq}S?wN+btwIHS4ZFrBv?e3;1`Ji=MUfFwe)mIygRR>l}M^HZx(~RO)8kMKV zAT1P9w-Z`c?bQUgL;^cINy`@Pxoz(Ps2j@X_+&1$alN<7wZ{rbs0pd0Gwj5J-Bns_#2T(&`i|QlDz}Jcl?OU|ag^_CZ3^jb*WMA+ zs^@LRl%e$@AmmLe*BAujRn<^C-CD$HPA#;A)~Bul2a;%ub0BMKtq#W7ugid!8Ele3 z)j`>fGZT#^l>uZq?9W7$B{lgX$nCZxo@06(PE_s0R({*1g-q>BzcN5-s~hRm^83j- zV>lseO*$h`nL~cQipBBn-j6we7SU4Fd_eT2J)YcXTYnlHD@B*?Jdta- zrH$jW(-u39HpmG%bft2`+{Pd^sxu3lbL0S(TIND+5?pCbRUn@U9WkHFWEQh+aN^aN z_6Rc_N*2;W)sQ0zjA(IQxXL|)MDT;cqVYirQDM_h2^k9eu|I9JeBq%`7n|%s@d~F~ z9+kJemXh=dsC6ni48i+xBHrHIpM^ll-TM}NZ9=Qk7cM%V?l$k41k!$zIdDGHCl{BzK%cO^TE$_U55?1)$rK zk($U&dJ3Eq@b`po#TZeOLRy}6&cqfh20tOO}yH3K&|-cS{QLiY1hpeWlBm^v)q)E zQ}khY@$)}`UN3J+BTzo2F>L<;O&s&cT3*^5OSmqPbnE%^Ym*KamrCpTsp(;opKiQ4 zx7)W5JTy@A?ki}h3x!XnzhzRj^V3DgXDvgMITvf}DJ>^cwiR5{cDxF9e5Ii;__FD* z5;r?>kv6Yz`VnBF7g~)L)fr^>`Q_I@?#mHo+7@ut zNiyc)!&(H6^Sga3AA;=mU?!dwNs#o%0q-S6BkaRxMA~R5lp;=UsxIZm=HhgDjmK<% zt6`R!1L;Vv3qp(qQY@6d;)>Sdov&Jwe@Ve)VXAf307X#hdBPy(9SupUTIBvc7f6I8YBN@}c1N^I=^0GfK8;1qOG-rt|#`;7a? z^$uwocd8CTj~RB(KZkDO`&Bggc4%!TYl4)kvt$PyQoX6uh`XKVaz{)cFnSc_w!pCz zbmYqK^Bp~d_NYefbKSebQ)-83WhVb9=`c%)9|foR>ce%Tl1TW(V!DHQQXedKii01%7=qQ8jyt|;RF03A2P z)}L4j-)`o9cH7i#t>HdHNu^kn6D0(DL&#U@!`OEgRxT#Ku$fJ^Ft;DL2dI(y)5;&J zo#c_Dc5JH_>uaN)WC0b@E1!*yhv>ua@2iG?F@S{;KWuDXYZQ)W(_h%0uzBx;w*hGv z?T>QU@>l(|=qqk?P=DmrrF8cUOToP?CHUG}psxFIlZ#lT5A+=m*QeI3pC0X=CFBQC z-ooU|W$tR|Q(=;{+ow?U9$b=|pLQ=8hhuJO(D7B1?r3v%Blxj8o*-0vRp!_{Idt3F z6)MuGfC2Z)MNqEOLMjOM<6OjI<7+C@Rn5DTb4dA1UI_K4_1|mUoOLcW94HK^00x~t zoKAeAr#B)zRhr;?42Bw#%&1CBN-5MzbNcY|-G(r#d7F^;_^VLf*o2TgryT(GKwcil zB7s!*oc2@iwJ>IANeHG#JVfP3Q#D#H#=7$46ym5%{g3kUT-4H3D^*cH4`mH{<2jPd z^)33}E?g0KTUC_0oh%(d)mdRk?R8$8AnuYC#^IYlWoRjAAz4)E6zg1EaIt`~z1=&A zD)-r-q(*41IZ$>}ELoN}c4}7AZ9&pXTRpTObQo5orlHhHj&0{(J{CRo81MxV_efK; z8;u=P?k+KGfX;4Bl=2vDBetq^fr86C5emJ)1Z!9>u3bo2UVH#m7nS{ZdM@DOCN=w& z;_qF-6s-l44=z z{{VN{UOz(SKQI1w`<_B1TfIK;>>Z@pPMF)Paj0BpAQD1?V#QKgNvA?wR(+lLe&PJW z+{z1WS<~r1uzt|Gr)B<2an|HNBQccy*62N_{{S!2xp}=gpVgnNO~#8p;@umQmTof^ zK3{0SY1KnTTnbGgTaboQ5SIjAVA; z4~WvHgT(7!5Z_|G{-3`lE_mcc#Jckh)t(;Pt|cxi+E6Fn7L#8AZM6oOKGi8+>}k7q zu=jI->D|ygIjlE*{%`Pj`m)KUc?UiX{io_(l;GdXFVjy3A|&{mP;VX68%ajzy@X7C zD*}a0wm@kF{{TfJ8}8oi-ZH=nFc%LM#k=qGCAo8iw7<)U{_Aufp zve2VwNJvNnAOIAg6vg3p(+VCM6yyO_6J0h8 zr(LR@SA?Gk1P`wb4y zm(!ikj=4(bxGZpr>|23F_t4r@0}rUxbqAnVTynp3c1z*9GO%b@(r&?bLL2;BZRT|( zW`oi{!BV)k=|vzZqe0Ud3t@MH-etRGbSV05u7Ul5!NeW`H#d#(N+`E1(umCUICd>l zM_mPlKqE4YyKqx&s_?p@MBIo$1F8D3q_7ZD?6?#~osUsPCk+fCEXkAKgW#%9J#dsn zCsL(u{kDLgXX6pc`vx50tpl%PALbuX1vlJt^y01^;S>J=@Rj#Ra9SLZB7_p+kGOlQ zj`@gx1P?;_1b?1SrF@8fXjY*E)yDHsMwHBr0l+ENtr7aM$Q{*$ej?@cqw@1})wi4I zar*+GZ9`RR1!^&vGTg19^MomiNy#00<%OLY2!e37`4#^F3b07vawp9MGGaVHpdy%9 zK;#Byp#(R(j(ynhfz?SHfRq@u$#SSlNhhH8;ly_CCV@7V^^MhbpU|G({!`j-Mx4JX z+q1#tq}G(vkr`vEJ8xy=+EDRa6W@H#&g5!1G*NZaXWH3RcHKhtp>+1wvl>JAu6DWL zrb}I&A0A*HiX-X3D95LKOIo}icky7QD2JD3^N>POpJ2lyB3d;CZq2f_{K89gsrbFv z^qf@gIn+>#n_`k&R*+3-Qfo{yqO$p_bK;G(>}!-_p90dVpw~Rk4Am2S`h|O~ktDSd z9yFYW8c3kRTx}B20#d3cw*~VGcjcut7u60xD=F}rch;TweGHT|QJ0YWtKwVp4%;hF ztbSc}ZY3*zV@slxE1L<&JNF94x9)M`zKHfWDP6e}d@8C6Hu}X+oPs+IMk+0_(P_@U z3&y~+{<$OEMiTi1Z=PFz1KzRk2Mc_%Pv=(OfRFgmk9@KZok5#AP%|Ktk9@ixoI(B? zj?|17mGgVN zHxf^rki6?v(wEVsojKI>#HlqaH*08--*HK8X(J_66!zgxgikF;`-w!ySR%SWrac<> zmaC@YQ)+!W(MK9k3F)71Bmf-OoZ6{JZB9}V71e}?5DHMLWH2oWh2I6pFU)V^7obU?z#zFDfy4@4z(sSTuo@9N)ShJ80L96 zkXL>;;iy8lYPMVf`B_3c=RYajfj+~7n@HWjqB{0S#-gbvYFHu_tV^W2>k<^SIpd;WT&`cNYE0EA+FAc0wStHSJ5f86p>vO9hqTx;7LAR5~NrOQfW;j z`!N1Ha--y(JZ#pz4xP8#Z=)fsCb5Hy~O!X`xFwIxOy z-qh-gr3nEw@Qpc|4{jP=R;ufVnt?`fpgbQ-N*PpEpiq5SY0^~Jo`-%jtZMLuB_g3= zBMNzF8v3et$_$A!-(QoHYC4D%NhI{Ab}5-+dgvD8v!wnCBgL^Js^SpyB!a4n5-UUA z1|8zt)kMd5y!m9VrE!MXwMvG|sSYrt&`{Qd5!qh+TmxtqJl;%8hzfXbXRwr{Ga*L4 z`u&(@_Yjztl8h&AsUnGaVwnS;66z^`ET9~;y22I_Aw(zQ0O45NQ2Av=8|E{gf!vS* zQhc=yUDX&h;Qkty!F9OQe@V3wn#`jZ!W zF>u7NxP?UA7a@!*G+Wre)rnX+B64)b$0iIG1xd-t7*CKFZ7)sUw}?8OfpC5}s>Ie5<) zvPre_O*SS*U2~7BM=ygFpJw>mhkmw%Y}KSslT+-d=v**LZ%b<>R?C;Lroi$C-ZD6y zmNYw`6dO5KmihPM{A=GZ<^}N2?P35SWFH5zBK~n#VI|tp=)C8g<1m^nFME^7p*|Jq zHSxW&$H+R6;{{T57rP)h@#~J#eJs;%gII8K9P?Cv@r*@c!Ip*Lm-FloiQ~6y7dwVq z(bA10&i3q)wNbnZAK5O|lxRZ4blRGK%>-G*osJf^$7gqs8@;FCapoldz@Kaz*}b9; z!4JXzL|&+^qy5HyF-tef#VO1|x4hHYHY`Qrqw^D`+v|VxIU<%*{AKM84=##PIm-KB zR#-(u+o+UXVv21_U>l8%aJK(_lL=R|NA;HgU7t4R1)V~@0Y?oF@!MZxmS`#CqS5#C zS@?-|hQb=ru~Y#?iR&VhWbRqoOU1l)9tZyJXUQIjzl{T23iZlHQzc1i>reWVy<=&T ze0HSvO2iu>rD6FcDLABw(yIRTmURuB)hRu${N``;Ugsv`@K(}C`hMBLg+|#lVGHVJ zTf0&lYu6lFw{or(SKwEJXKRqG-5RMea~^@0@WAT}W**gIyK#Y5iP6?|F%7;evt zppPuw_QRmv{bY_J4cZi7E#7PM)VqCO7K2aDvs0XLX%;0OzX|l{1Gi_(S_GmYK#Pxc zI`Q(q%Ku|^I$7ExcjWNBcjFv35K7(sqRrC8&^e>ksElkJDC~XK7T>tiqNQoXSC!G& znltZQw`e>sy##1gi5L@!{$BG|V2~nG=Zz;5H*`&13jQ`dIW#kcCj6s^^!+>i;BQPK zGshASM4G0Bf0z(Dfvq@4Z*h|Mh0FR38~P;Onuv+wi1G zW~>6w6s%R3J~1r(mIm9}UBPeLXv_3d?GY#TQ|k4YWd}Y+S!MEEnB0= zE`mWFld<^uc0J%n{UzPVOr9sQtMJijaKr$J8{K(FIR8?9spAd%qKL#ZP1&lz7}Tv1FgV6#BdrK%?eaKQ~1g>&oQ?YZehXJ6m7#`Bg_5*SsWGu?Lc(p6*z zdBr0l9un3tfCm-OUC(7@#}UyL(!hSqEA;51JAKehC}G3B3?c~-pksn|MSM_ zM9zs^44M9F%$@VFxeIo#qcvS5x{gxhR1|7C5f3qFmq<=ZEkB|Pd^O5E0#!T;ZfAWX zxBW?)*c+lZhOKwGQPwMA2(HDs8y3^=A!t9>`nI zctCPTU}9wsz(%pPPbkm{P5X~~!a0pZnyiH^lUn%ayc zq6V_OVI($4ufZs)lH$%vZfn-Iop`mkuWdzbo$22)XbQ$cdpbf}SQFI?d_;c)2HaD% zzz)2TL;O&f$FeiSCQF3#ZG~D%k+IVSLjTz-Y8$(I=^f*JbA(tTr2r-MJ$W+Ze&?Eq zh0Jkwe$K{PFK+VroGO17y)_1^(x6;^Zx>r&|#AZ-jqI=G`OKg}swT*l?Z88-jMn_&c z=J45ao3tyk=}d)U(6{aKQzevxDwIsc;tSkY$1!phb(7uGJ>3)_-Xc$2MMFlwWMz;S z4wctv1`||U9`0bCk^BGY*@dUvG4e4*9V{20bBlQDL z6fBdAyg#K94Pv*D-;;;`8ec?Rz;x4`Pg20CXO{UAr2@M#2h-NF?E{%%BrjjPW9P_l zYHa0n)Np@}tajtFgC+57#Ph?r5(Os;xc1lBHFR~p5>Gb|fmWBW7z$4axUP0XeO#T| z2y%P%NTmxhw6vjX?#C+hbe!?SJI^fgO$4`GBXX4=ACoNMTO~Y8SMB<2CVsCe)rmGTBn%g(m)GtC)%5Clb@BmaEvK( z=v@HRsa|;Rf#ld+1G8#o5B`xC`>F@ZxtmWI;L7W&xa`uglH-S-st`=^#1?ShI(*rt z>>2E;o%$mGfq6leUwVl<>TU6VH=ISFGJ@@DrLR^Aov!NSPPbRC4>RRFE!vp@<{I(u zt2W>K;i6nwn5(J0KH*MJ+mYeCkatxwfFZ2ZhKvNI+YlbRG|OS$aQJi`lo6NsrO5U- z4dOht);c*Xsf`9MXoTr0#~3foX9kYva>{&>6(&2j;ilSRWR6U!P1rT|_vyyW5N_W1 zDGtu*r{C%G5MKtt?L~3L2kjqPoa2crcTKm5RZeN`r@fxYq?wMJL#~geyx{LTirHH% z{}@VhK2@4rbT>;ss@B_NM#&@f77thzhy6OBl!{`t>nnu;(r!5-Qv?C z-xW-3s7O`fSmsX29!m0dRFnmQoL_e~iPTdqmracgvvR7(eC?ZAo4m;=^9u-Qizap7 zn}S0=&RK!ZN&RDSkk-de&&eItKKl${ z42s?b4bJRUP6}A-i1gVUu>UV}vGpULnl8vQ1ejzi4pzr*q-s62CgdT`wEqXd0D6V) zr(-8?T#q`E86MK|&BjEA20BcRDp~iJA8g%L)WlK%BuyqIGM_m#s^$1Cp7{Q=TV}|6 zLL(`0q@~h|?9IQItbl*Zd<~rv+;=?E#97ncGL`EzT$|g4zV#8MSZgXFD9DevI)~zo zA(m^NTHfITn}w;~I5#S!8mS87lHNq>&)QW5As(v5wJ&I!M;;OdaH;yO^Cb8e;=Q4+ z#UaXm8peqZj`9847k}LrBGVzO6qkf zsE(CMTu$Uooh_3bE&Mq?O9lFqFg4vvn{HCsNI6;QP?@#4ccOoIGry8A<6xWIz0lXl zS0+)A5i6BD!#%zC;N||3CaxV1>#go4pwSkq z;`nm_=fsHENCW&ZtGShAog>S?;?0_{{K=1j%RLUcpA*dXp}ldkaa6R5LX%gYa@mi` z%V)CMs;%&vnd9-^zEEc+3jI2H2AnPn{^jQESyM~0Otzn{oZ6V76=%;zHvG#v{f4@# zdEi7tAUrVvCi-D`x`IrvrhMSX%!`=|9PRGOn*feOy7_SjmX@b>A~U4*%YyM5TBgD) z%388=wamdBtKroRAFbP-2w8m6Ne+9XlRN$fDq|)clY35wXVCKcAbRMtLHwP0Izi-> zRIMT%YctjYgsv!nWc=vDP{lj3{_5R#S~7XFvZsrddoMFsOT%m?AVY#LwRst*H;=0M z#*uX^+tt>$nQ2jP{V8!-chIl5@9v{TI}#{r!`R+&&Ah4W-L_xu2~L5m>(^h9Ywj+5 zvuV`l794Ku^_0}mcDUyS=XF!o=@7A~bmvha(54Ql_*Xwp7M1n`0`kiwZ(SF*TIJFjE9U{+XuG` zJ9CfGWG_SVtsR>U0Wl^-59Op@6Sk^VRf4;MkCHET2Tm zWJlcPYTKJ!6~jHjfo{Z-5f`fYPBaW5P5!~bBX+I3Y2yjLB@Az^tc~Zn;pU3>^b0x) zL%;flI$#hbQ30E4SAp|tDrBPc6X|>%77y>08uK+wNyzOaltdr}uUn!Qz9y-ncuy|V z;U=+}#NuE%U1n!j`0VhSMp6k&{V7F`W2T!kfnwqO`r)KIw~?kZAKwK{M_?$o~;qGTB9wGv9V|e>B$fJ+ABb>vc<9kxF|~{bxx$qT;cox<`M(-+`H zFC_9(=jB#W9!sr%6c)Mhx~F?;aE^)EIW;OuYuPMybs~jfkau|UOmr0}B^DGmB;oaN ze8=Rb_;vkoJ<7s;A~%ftROMWC`D9n2KmCOJ{j!EQXPNloU?Xa;C%3xAR`Nn5R|0FQ z&#b^pe*B@(!U=VJ`26p=OUSZZP{FZr?Rs|A(WlvzN6RPf3L6-nw&vneUiIy(;6LvR zOR{SnsJ^5{`H|1b{q|nxt{wXFGvazxZ-;5E{!YBg#l<+1~v1YpY6sSC8g*5 ztol5qw$ePtQ9Zg-q99FD6F!E=DQr7K@f>J#Dadz3%MIsbJ$(2F%XU^UOz2AjyFTcD z4wOL41o(f}5nUH@sqo+W#3`L!Cx`#tl|^_;u9;E??}W49bZ{c%gf^L3#MSqfM`sJkbU!Nod(U-G|kTHTGDmGjFY%pKU4)%c+= zIvj0)A3we@PHKhx)&IZ|IQ!R6f;ok~tHo5Ssbw_GQ%|Q45llv4B5SysCv2{n8G97X z#6-lr8;e6BDqt1(ivMg_`^5vK%u+Ur&0@r@O565Wy!*s|cjcQ{h|W^pP}K|uAi#(4 z`m-|1cK$arN#MgbY(~zoCkP9k;2eR?5>Nj*4B$W2j^)x$SkIo3ccn`OuIhij-&TY$ zfb?&%j%u*Iy}+DdZa#vL*Ja_Kjd(~&(rwtCWe*hju~uWUXNe`lO6ZR*_&$!cK%7280RJgLOO$7D2$QZzW?U6b z$*Ol;9@in|Zi@p913)vuamEL=NJ4iPN46SI68bm^LP!5L%1cuF0n zwX4D?0|TQuUcq)2K7-kkC;9*2GenLfS`+>)}if^XX>8_hMhsICOCQL2^31cCJoco;Oq`+5U!@X9 zqd67nco_Ljv-~^s#vDlEc*U*4z0CaUduj85|GRAO8sq9>qL$ zaFZY0iqdGXY_F+6*oOG=$P+!W+F3b8%8T!`aqKty@2s9kfz-tHeHpH@wweW&uxE>d z-Ov30_)j`aZhAOMl55Y@p8TF$BUpboNmfKMBCPdVIo+K;w*&x*nA>nV7ILF`rV(zu+aw_*+z|3i9^SELlSCJa5ZL}~k zM@d{!C`VvGlZq#TLTx2mx41}Gg8VrfZJXjBHc6hyoOa2r1_}xxJ zsrM5Ob#ihZeYI(l)++N=V#&Ne0D#i`L_5)UguxNTGr{Gc8d!1#)V;!jdOCGJTt7!! zs+>gBQQ1<|eaSDinCr}uH&7XPg~)!ZXG zfPRkGINFG{8K3t3OLO0yU#e~P@=U8d33jR0Zv;eI)QS^Mh1lD2%4D zC!wAgK%8r@LQA@yy~vLO5~1wA(>CS~hv2R0@D0eRDU|k5f|j|JZkp4AKp29bAsG^LhrBaRxt!s zD(B-M4fF(3+;`4?!^F{siJb4`zLJG{Vp8}W}2B^m<6Z1&3+|KZ**(@ zmz#9>aaK-OXGtxEeo9GP3LgF5D=Vv?oRy7`9Ea{RV+1`l3b@(V!dua}qb3wjAxTya z>s#Eykuz4yfkO>6)mr}GlZ$t+e3Y{`)(L zpnXY2T@ggLVCQFV?Tf3WU>5WSR_7XAZoD(oz&h{5-7YA{ngMun2J27OqjxAM9nIwM zw}mBUT&U$yPe{ufdBXY!QmX0LDZJWvtwuzuuIf*St>j{x!x$DM=S)*D0QZ5*ztn=k zyR>6pRYtU(2t>J)iI{06$*O7$e5o79vf4ur9Ojp`GIer~LPEY3?zF4?Y-Sq68Xb>| zl2~t~D^%E7>Q0&#F`P}=-Nd}I*%bth~U(p7^cWnOVJZdV5VmrXt7ZXgRYC4r_n z?(#4dy$Xyf7R8;NEO|}#%z34)&-qWFlt`AzL@4(6^$O|qJnzeanY-`Sp59Sz!&{%Q z;{NIUsx=6TeMPQc?))pXtg?q9R$-p}2b-T|_bt3uu<>>L`FC`0lJ))O^~VuBvkKbE zG$QG&D(pbr-?WDjG}d_MCqItFxQj7>Vsw8P*MeE0d~af{mZ-^WW+JJZy~5EeYZxlb zq9CF9reyns=S0l;f34+3e)osh1~)ZMzG7GA$;St#@hf3MO4D}i$%sv1(U>3H`3c4M zv!`$CvI=T(=Ffl+Ebeb}gk2+iV)v3nU0oKF=o7yvDL=4vfBPP-?Y8H%pPt(01s{M4 z6L4A3)W_>QA3R#j(QPY=9<(r7x!BJ|Mf z*ZX`SvnbY_{Xy73OnY#n#_36UXyh`!X32;w zY5c$1r5|PyzjM%}?i)?6K`?=K6?Y|Ak@D|{2bDf@5qGGtqxEm}yv$yX7S;IXZs}(V z>J(ach>P_`?gy0Y#dVbaW>g$Eb1+sxJ<|QXPshfWKu(e|OOpoE_VOPJOfXyu9cUuH zx@<)~^m?nwlbFlA*BBd1ET`GXjh5tv2rXXO;U9Sy;t>*)9*>P;?&c%G&#(;c?%hhA z%c*jyXNn-rez>T=l#`=!WvLZ}(kGf;2*0Xh$SkWXB<5A@xGBBK&O6dAUajd)#!%`V zcb}HC@ppp7?^0r{F#rxvQ?a%gK7X)fcGklK46UAu0hHNr9AOespNMX+V`+x5)uxo&~ts_#)M zx}Fb8MHs+~V z^lV%`_?AKLC=$h9#r<3iev=GqjeJXC{QUV6h zxb!YXeak3r=_5Vt{57)Tv-bvA14+_JWCZ=OEEQ8<9i}vauh*DMZtGzz#Lx8BegjOI zXM0=L47p<3jS~3owCruSSsR+V!kp*gYnZdp=K5cGM{fW2-feDR-c~kA55GiU>PTz{ zZ3=gG(;d;55>Onr_<9GjBLAV&@_Vf&92^!x zu@8P(`U@#+3n-#{1k0TK+P^kUrUdn#C7u82ws5RR{CN3Urxgs)*A~bD)}EuG0HI9A z0cSfnsyF3c=bd&p3#dIC0!i&dL^Aj28iz`~_zh`Zk+oF3pZkV}XZ_(64Yc3VP`WW3 zjbP^6VMYFo=9Fl*0G)MYo=f}tTaD+<9LPg&{3fOINkfiDBvDgz z{6zC1!J@4hjO!8XYl{|H=)Cy^mamz6OB)%VM&^>{V-ysSv(H&!h<^H|(ttPj z(=wgAqthSsE)r96|Ln%y3MoMcZ?DwkCx(btkXjyhwn=%+Z5=G6%U;Yu-$OBg)-!d! zGI)(p(8m2gem3 zDaoL^tPr^KP5O+Nk;y=2!O4`KEiAX6C{Eq6LlhX>6f52cJne} zKSZQ%E5Q?VmioVl+C~51H9x6p$P5at;7mQnDT`lK_5jTD!HR;|nH6@}SsDIr_3(5@;E1AGFf znPvG`0ZB=NI|eV=>`hYAEhI+Y*v17>UwbA;(~~#V{+BOz46Oh`p4>Y9?Bk}4#Jb~o za4444`gv06iF9Kl=hgXKaPf&Yri})h;6oJCVjeVfa6KJ;q>hYS47%j>COoLdi8w-% z;2{YqMLo8Yw2C~J1J8reGOKJ1=8xiv`VDqj4V-m7bp&<4bX*jz%7;#Kt?q90Kz+=- zh!Emwf}!|uiW9}kmWNPeK;w@gr&ZK+w4hwv%~Q_3(G@&g-Bd}5`N@+$LFbNyffCNu zF*|U8zou2=nLT#p^u5AE#vd@mMdA?70&T4hx`pSfEwq(wLBzN)0DnE~4K~-)JWCB* z?#VME2W;5Vc6HYjTIvrNJMT0(e_R^u>#rd{@BssePfdX8WaDmJqGrJZjL1LFv&8zR z>t_Mzh~H>(_AL@y2CBX2pX&6Vd&5LBX*kDxZW#$ua~=PrRR$gEoQ&Z=Tk(7ASAAi` z$+a`fS6jZ>lX!G4wJ$hezat{S8bLcNgh)Qa28;RrdUlF`s082r=s)TcjL4=F13G2uHt+PMw^Cyr7+KeI>^%G38<@+00?iNNR@DlP_$>poThTnd zFZ6B}7=sdmqkI-36r7=Y!=VLJ68F*EG`z9Y^nt_Dtfa+r9OSekuZ(5bB0sM3KW7|Q zGmrQG{im}nK>6b&l{6S!B4N`Xw>h5=QhV{>N@gUtK_LTqX_ryUm{TwiwDMKS_JY0J zm@P`7-pR)Fd!>tq1IYic=M4?hzHuy}#4O`7^i*(>vWIwu{q`-%P#Xg5B7Q|_7J$@v z&Glg*295*S7EPu(^EiYBu0!Jj=GGrIVAki}g#x(BG&xF_n!c`+kGJ zr8oIS$@Gcz`U%vMV^+6wt44RHs$=54-QvsAYX%nyBLFe%zwQSIA)2o7vK5D6LA|v> zNumsf+KMk}Ki76TS}h{Om3Yr_k&Uq$b4wkd}-^r9*+Vp&r-S#ldn%0ulZKS)Jd50*ym63CHw7%TDVYM)JB zTl6Gr2j8;@x$O^Y+$p+%7Wo7UCFmvYJQJvA*ImcOlB*v6 zDfLbEwab}Gek7EnrN%SL5S4z~4Ts+h*lszz#xD?GUdb*{ZfZY&-r#c&9#BF-UlSquY`mYwG97_>F~K zIavw*oN0e$7{*y_6+kRloJnLtdOGua1p{d74N{2k2`!HxSE*QDIG-a@e!J7Fun<8U z+L82=qwB9D(23J@vfu<{!J?iGJj4LzC|`J#j_tx45B~<;u4GY2@4=4$+g(JM3|N04 z;Ic1Uhg6qNUoLW8Q87;uvT%(TTg;V0bJ=vt?UCVDOwR+2L}jKW`Oa&4l#giQG|k{4 zcZKg7jB2xMT^ka$Rulg;7s7-h@UeO+(uv8EzsUhSn)<8WpH(aB{;a8s!PWq(M8!3h zh8!t+pN(-{_3dZyGP_LF_dNX^*X{3jDH_vI*E#Y@u}xKC<5O*;nDD!ofm`3#t5vVvWx!;`o;;TJ6}mI zxG|&il%v|$ykihngr2yBFYdcG$T~b9vc&Pf1~n5vDuLtp)Fm<(Dv(e54o&jj8Obb! zUA$Tmz+S1oTg#sT#^jdHlkw?V&kmGkB0!BtxbshMs)CmOAQ>@$alul4MsDQD za~XG~a(v#JU7qCG=hJcC1___lUJbk=V@A5N`Du%`D}>*2CJYC)v^k)sx6aq58_#EC ziAgjV&(Eo}N>!Xo*jADjUYs^8#M%?bll=hNY~{y`xb5G@0!@1?i~OtR1#7A+B&0eo zVSnDQ)nvSohCCF0=ciVTOKZ)xg&^Yuom5RUAYytW%V`PUhQk8z_)+3gf|h7Yx>`n! zTou{=h5j^Hym19Q2a7o7)YPP>>xM1STgABt#VfAxKr#(_j{)d5S0ZSuFa2RJSo8MY zSo}Lp)$#mjeIft_wQgbn{h-Stwkw94ci(GdJ3bT#-L(+k9Qs-epXO@v(*{FVGSBQ# zKh@F0;3~X>t0H6Wmj>j&YI_q&a6}1e;)8wxQDb8r=}*{2xmzsxxZNelf%UvD-EG2q z8_nqVJ!@^9svi}{n@RZnlc`k|;re)V`zC@>m0Ig3EiBusam8O%m20z*sXlq^##DLT zKF`PQV$2ewua570m$YK|#GZ|Dek0^G>>3llKr^r%>OT<9HV-PyxcpluRaO6muEA?_ z>iASWMkw4u)s~$3?W$%5MQurlN8NWmr>yR9$9K#$TdZi@Uj9s($-OIs-WpG46r}#s zvhTm`FEa8tIPOi`@pIE1*Y}!Ne;a>45q&xy_dw87?dv93gx>hLTF>BfA8R0XA>_5_ z?Y@d|30m<-POZB!hr2R>jvn9AgWZR<^3`Gh^e1~cnKZpcfug6_GEhaL`x=x)vR985 zY0`!JO@Y60*K_Rc;fm`86Lp>d+XFZFWdFx!(SHk}lx$qG^H1uf6<060GzNHNVuCsr+ zGD=c*9;{jp*hP;MAtKIY#d~bmCHi@VC$7m3p&!d*GqW|zmcF53ATIz6DRHNC<(J@D zNw@5#JgCc2+f({fb$r3yPR(+OA)A>a&wU*zrUOpflEck+Cc`@J_rKy7dz#(?MVp-^ zW^JMGDXV`FliMQV;|WbtUa?P%3{QcCN)r@Fv=b>g&X8JPmJwm|E1WNwRz43M&LAmC zX9%IKr;<|x3K)RB)ry0aEs7gdA^iFVXS~{eYQ+D$$X9kV9!}hu zOd5i-t`_u%F`Z*|L2MN0|G54hWeqdN>&q4j&%??iE$IbLbqgt;oDIIq3qhT$K`=nei>?>Hl*>*&1mK6s{) zhpKfJ%xsHeLt9_~DYTAP9yiOq(yw-to8sA%OIOLhfyVIv08~J$zkt>{CnJgP43{u~ zX&;o?Ml{>ix0|ECV3;$(DSnGIr~vB;8va>u5^U+4%iS3;i?Ex-U^ z`gV`UeoQ_@59N_PLUEtQ_&xoWv(Kt?3e2vIUr8d;-fxNLZKVKHx=Q{(V%Ea?gxTES zzWG4c`kN~QeVx~e>NXICH|JTE=vKgpKVPVGwSmt^lfbG>B^K^OV_YENU5uI!Kj5W_bM)~;@G*w+>o zAz57&;(xtXT^>13%^Cg>ck71xRb_)@8{!?r@>ICYt=zXzUVPolY#|Qp(|T!&0k~F& z^LC+^YWFb!saWv26`g3s$~WZ~;jW0tA*6NJV_9JkxMQ`x>fwuXcA_Y8q5dM{C@s+! z4g?xg@ANR#%Zso~}u)%;~PDnLv#0z(!S_ITM}XldoIk?{{N{(Wk2( z|9)h5NTkNsaH%JL`mkh?)k19APkMr^bE?>SueorsR^t0NXuD=eW`6$@#Gfsx1zjwleuYJac z1piU21MyAjdp9wZ`*%~njE&IkYBckk`-TDdG)S`6JV2Zq2WsGPi@7hsLf}t&*Y;N4 z%gG6g9m@I`z+kj5BAi*v4AtliZpQ%b!_hj&;djulo8dCIsmqB9TCf=FyepgMxX3>V z=Z}hl(k-5YQ8E|+r?KnR-<$H}Z`aOc43paewm(caxo{sj=o;|U3Cxw{((W}g53*EC zxm&*P%EkY@46iCG$7_~|3@mcP774`zR%O!F{WvQ<7#mh;wSid4e+-189GLh6qOfW_px$2LyeEu3e1o zxZC{-B_p0|p;9~mJquJgzAFgMy)Yw`dNVn?x4gdq!@8kQx;wZ(lA<$4)h*KI1&e_{ zcJlf7o;^G@2-1W1U;r8aS7OnRsNR9ThlM;yi^d1({$22Ns{0-yj!{fJLJ+OzUBAVQ z0hEJxbQN>dsq0tz z$oFSwpi68p9Mv-mDx0nglr7K9iEyfI8^=YYBMbbIPkI6#UFMu=L8{z;gDv!kL$?hz zm}@Wsjumu=l1TY}L@39*lsXFj*-3 z{^2ayWKshnWAfT`CC|bdT@;+Nv)&#p3I?uo$$gDl`D&Nb<4kslzz5YnLG4H!eM0_6 zI|9HuW+LM-`K-w*QblV7t%|wQ53Jgd`VV-6f&vU;Bn5{h0S`wN`VTi4^7U8 z7J#oQrdO9&Ej4BfTr?)f7yu3j|J~?Ulm$!EWxp3&o8C`0G$En?Ox6WmA5t5HUV`Uc zuVMiDxAZFyJ6(alG(5&X_Y`M|clR6$xTT*-IQIJ!!Q>uiaFqT3TOG0)cWqyp~<2WtWStSUF(NBgeFx~Uv~7#bQk z69V>AMM!JFFK3c+U>E>45b53hmc`j$B?kjo#fAL_`+`of!6)nz$b|0`X)GaoPRDA2 z;0~=G%|AUId5EVBix)&UG$AxCeZ`!nf0|oyJSDvv)h*Qi6Q8N={H-7Rx2%t=N4-R` zLtgLq`q9f*ZB?-dV)UPknu@W26fi#UX?33Epl{BQioO5o1L1WgT5ldfC-Uuz>TkBf zzic1pvQD_sG!ML8_6r8!o!TVG`X=-Shp@ClL*hZCtTT7q0_m4Gq&{(BeYA@(F zE{!D7I9%l8Ffun;oEQi42!@Jf&mu?an83(_{(yAU7qu)usd6O`ZY?U2yq_%<%f)2T zf`L)J8m4szjV`+L6D*`BX>2g zjx3BNoLN#20xJAhNx09*UN#X2!8(&y&fwHKEHUXy&uM5j)H@n`*~?i|It~5XwEW7w zFaI%Jyc${)(je4`n0qsGk4vXg zyEu>FeQPRLXMW>CZ%aD6#Qn^Ry;jFBoSu|TZ(X3Xz5m2{uwb05-S>T4DkP&gaULRD z3cq2QvS7+?`N-#NSM|5#b`TiFqojL)C}&h9?@D)@H%*rDf!>(S+KNrfw!Eb@Q~S3q zd^~BW8wV#~N8Wt1YxnB8wQm(2X;a%Xo=+@Bc8&u9$M*^0lk=|!*1tHf*{nhPEIN;} z9duUzeEq6Jv7q%5UV+WQL4||xIvZj{ymqnPN}eEHGYFvBRL_4&IY&6_vO*#KA9O|P zxevWo&hw%!Bll~6F>+qdpi9|8J@i9YcL_du4NP1%?lNak^h)qy?QvJ>TImua+E!7KdCJ_!#($`G(`VOqV_XtKR78H%sZ!8q40V31q) z$qlU>i|mcKo3`_Z)}v^iWeS|OwfS>R%WII(I{BuvTXq&m@A>=0uNncz0UE(eO!fFo zD{iiREZ2pv_ZS9B=|w9~Jy z^e<4{TqUM}Nit6FYXqS?4L@w!eLL1~C*(rt_g1(XG($Nc6CE#)ZI!q2)8p5LJgHS{ zS+N}PY6t-piNgPWaTkuc^P%^(L=W%J^PBzKm?utx*e$Gc!PiECGFX=`vUl^ozt99A z3?M;a_8m{Nn<`PP$8{zxuo!(uui2)(u zz#odCX7vu<6aT;mciuFl=R^4q^8A(FB@@yGk*HT!?r}1=#g3p6O5J#DFZMidav^(s z#eGYfe{6AQqvS65Y1;9n1gLrr8p_zi78+xSI`t_(KAO51g-Zx(VntRkbvomoaipPzJSYi|D09WtjmgAg!X8 zYy>eypy0QNn2L>!77W1FN9QK8c&3v$)TDWjV12cY&XLK&7`zox=KL;v!-YocWRiaj zykofjJLuQ5USZtr7#YE#8RH|4YjF5~uh={#Jz?_NLmzbPtCXceCaO00W zx$mjIFGIqBVxT0ey{0&CshC@CvUa=anv#3&OO->;2g!fdg(M;Ny-3 z#y)g?89^|eRT=KbjzyT!uq`%Y-w|uTZ>N~|QxbIV`DmZ<*Kg`yT-66|lp0zHu5zt| zurPq6M;|;0K{@&FTO$8(T#^P(hX_8yecNyNzcSMOEIa%RJACsBHC^ZOuASAN9y|&` zx`9L~*YM=6_K+oxSLZ!<;&~6A;Q66$@3);KdEpZX<@21UZ4>6PXTm+l794LMfrc zXsaeL;cQp?>4*XJTD|9xG67y}583s_^ikA|0- zaiyWNP(M?K;%?jCgj07d&22rARS8v}t9^2t%5>Ja9ChgzcQ|sw(TN_~+Q`EmlS#Sl z>FNiT_$n>5h9FPiMa>xr=^ z0{xUT#GW}bM7OZ7IX6qtH~r}IL?}Ob@5~9v&@BHNcnq5V699GXB#zbxeJKkp9BW#2 zqS7u5-h}ym|A*9#cy0eCu79J5XS)YApIqSqMY42peBLB0f9mlS)SX8`WBl+P_)^te z82@OxsO{yin3RgP_sU8vz^{|a7(mQVg)B8S{NKZ;6;;Yg+7#h25}!F8ktKr|fD36g zWbN5z8sLFP+0FYV(L&2O?IjBzi>W7L22wZNP0mTJhHiGv?{tyRF{zMlU^zCq#OGP0 zzWv*w{KpAJwB1tYejDB_tLm4uGo43q%CE>6Cn1;E%R{ELEB9_z=29{FdkKBTwV#d<1;!*9NFpZxOZ%T4?1f zveG7|y;p1~G#A8sp8lU0mrXNTGBQZ666=whzB{hyO?CE8ndTCKvj4V2|4{*~-+?x_ET6MVNNF<# zRuTsONwUw68K8b-MV8H%n!|4j7I*2}XgW&t$`Dwyd-mJPk`y|rE25)SC}_kX=8D_p zl~z2jF7uG)@o=uKG+Al3QN&0PI`2luPiN5QH6D0+OEUPJTRwTijpTym@N+GM51_!X^*epL}N zHU6Ixt~`**|BtU!qM{tBTuG6$M2^Hlazu_KQDl;PC`FhRp`1lXVo93gt5M9ol{-gn z#>N~a*ACa%_3ZcP_wU|&-p`)T=l!~#?HN{aX2&UY%Re6*cdJ;>5Xzl}b1KCPSRw_e zEvK+x$DyJKMDeF5#58Xw8q8a-TCE#%DM$K}!)azuR3YrbRr4MF&EubNkEfEI5sBSS zlqIJ>2lObw3Bx5ijA4jMeS^k5Off=0YH+pErVd^gZcI730TvFtF2SL;r-gFbEsg~An1 z&uiL=@wOKu96sGrB8`K)mgnN-sSzBcmDq52=*#d=Pc7E)%lEE^)HcF z$?fxw=^iV81`c_2{H_lu*M>6no{9*C{QQx8^+2G1lai*{Kt=LTgzEE=F)B+unSj+V zl{k`8-$Pehadvi;GHpejD>}a?wx2Px<{lAnbQeiGuPS3Mc+j$GWUMBA{*Kw$#ftI* zrvQ53?E?SYbLWg*-*2=XborcJRH*)_6)WAJf?ms^I2|^_)}lME2ITP<8y2qVh<+9v zyLo)v;Z?MtXwZvCNcvkhHd>l{pM71J(QM$%splqUM17{RFEG#BP$tL$8bcq^QV${@ z8s++!rY9VpB|R3AJ{edQBJ9t=Iu2K+NeJgZoZHZWwKxw3{Z^1UZx|Bhz1CPMX#cd$ zCiHbV|IO&SbIr18q0NV6s$@vjNU65YlQ(J7`GKA8&(o=J^ua927@0w8U+IlZ{*$w` zg~i=ESwAh{FRGl?9Q%Z0XY)5jf$IsnNl`RU@5v1Ho)2p)Sz;L~;S2hLw5Q2gNM&YPA-B`8wwrzcgI^PeY-EX21|6eERmJ5@}1^3|4z1*oBNsC_UnHP)In9HNOK&V3sI2C&zdBSVNTiG;|Hrnl8Um7zQ0HwVgo{t;UxynWK~Z=R`Ug<0 z#$`B7pHF!QW;6cao#dcuSjzkf+-P3t{**tJ7u$z2q_N)9lI??lpwI+9S8-0DzUyVi z;`p?m&+PSMgyDCBW}Z7PKT@H5m+sxsr+ex+WzESKra0<0Y-+oSFU8nK=5gXTdy5eh zwfmEeM)D?Z9v+vwV-)c6z{mI+g}0}>jA>*+-@$-7;W!z?6Zn7|QRY=pjhp=Hc6Z)W z{c3dA{1Ck>@13h;KTV-teh*nZ7Vc16UB7=}A=D=RjMpw~lY8T(VXCs_L&dpcHXPN6 z6-gvcRNg(9tGR5W?@|Iwd>GV}zwGcCtQl5!UlMK;GniEH0CE}=F5PFsZa*@uxl^_} z+aZRCZIliulKD{RtNVs?ztd>oZnQ{V#GzhfjELz6+L*Q6#oIR|T&JvHu5`9u;<;?~@Qg!@@O^T>|-FH)&Qd+L7 z*g~^!b53YNgQ2A3nZ@ey0(^t|6XV|>Qf>N&({vik__1Ga0XR?$s(}JTv0MW@m8I_F zhNeIUBgZpsnxca9c@W_p-1h70eOE%;eVhkbvO>BE0s^vRnISi-fEf#Z#bx?u zO5Z-xu9RMyT`%Wi1pPE_ivb$ST2CP7HHx94&x4dmh5|$F8+6Q6KY|{^&a_$33jmt` zA~!7HAmrZyI6k^JvgjZW^014mcm`R{By;Uu@Qh64+S|>kJ|4t2;wHXsdm-@z|7IS9 zB8~^6-#-`AhqOI|4C&=csHOiHv=FdnOiml1bfls(X2~%on@yLg#l5U+40+jQk;OXu z6}Sy9#2wr^zAY_iWW>$5rT55BG*~YiQjc7xg5xl|mpuA|2iXzNN;U=PIV>B7T045F z-E(dn$;2svo;YUze&|qOl|twnd_S_n7h&KQ4xI5f=Ry8#fk(zozM#Lx4by0$sq3g^ zR~xj;Qmw*1zTux0RWyXW2&_=0P@KqOfLn?}|(6Rbqw-4}!u5 zyOLXGkS;s8|Js9@GANgO)BrW}+^jIUdwM`;_d7K1@4=`H5Ary8xE*eInxuogV&k<{ zU@~xjE?=311?>}S?yYG?tR7CsFbSxD92zG*^Os$jsmZIU5)N-K-nCML{=H20Ey5Cm+yEl#MORxAhuQZ z-aHR73SPgT+4kb+H0DtcK?zkC4?HnNF62CTO3jsIZJASQ05+nCJb4C4=e|^;%G9=& zBoPvA0u)*|kus9+Pg-$TDA&en3qx6OS!1h*$?&}k${LJF5_0Ko{IT8!gJO3S5(FMZ zmpW7T?1Y9vn?pDfcSOD-8Hy7@rPNdF32|nUw?#D5kA&{(OlFNnCs}!HMl1cQ0hStH zBqcX+%Z$#+xbv6CkKI7_+-k~I^*Won=E+H|;LHEC@n#gzkgb*x`POmzw zNp11KA@IdD$(e)w&Sv@@Q=}t}d4~3;{Nj91g7wD%pVC>ZndCa6B zo!kSnyg+|x(~`R>ToJ%Y1NC|SIg<0xfd@gxL`lm|?u8yhk_=nFMItn2FZU!|qA=t5 zITyud<=M^Ssas39*~S;0VNDU)`;yM;<9tDuKu;e6*XA-X6SVzMOHSmOupnX9mA#7j zOagwiIn!NEem1b`IAvsN`#(MG{hn^}y%gkFl8hF(EOobqsVjoAU<{F)=mi)4g&Si> z4U**vasu%q58}qRB?U#`hHR*t|L|h;OQ<2=RJqpWhqO)a6#yp~6pUc7?c!*m>*VE2 zGGYyms05dvAQTDY(RMsvJQ!e7CP=ejc1@IBwqM~v26biG{((!ghkP?<4WmyKxoS2Ro+N#Tc$8ybI?dJKBUK_Hh{>0LrU>u^KiJ zDG{;@?5o`gnw^MLZe$pRUCLvI|n-3=fiCfSer%u>AB6u<{W&3*s7)X!ASW8IU{Hh@Lz4?;sqVQ2=QDD+T^; zLIE$XoCmoCtnJqbei+2~_`a)`s!wR-yu|?7U}SBt;`*zs9zsl0TsfXRNbqDU?RWF` zvXQsu@MST8tS~qpN4MuZf~h2qo_Sr$gQ$*fl&4Og<GIf!cBTj z6#X(Vo6dq;S_hFfdnYFEDvlRhUFR|+;Cm#Z*!FG{fwBwwgp9XSF3scv)Db^6Ss&zk z_jX3*|EW2G3GxxlF!kE#gA&P}axm3}fUF_BQ@acg@*Bw2;a9ne5eMT3b4lwBM39_y zS^=B^w{+Vn*dm|wfW^Zl>$FG4mRS*1h4RJBE%f@C9o%C6jbrHmV|!~)dfZ=VQ@ZtYu1rAO*L-5nY-TxGeFJlit2byou`>L;D+1IzKHA&}_CtF@L z=KfyXS8KKlNd~u9lJi`CGy1Wn4YUUFqK4?t$Ak2m|4zY&_wyjJPnn>cnrwagw}$0c z1V2*kI(U#bW|Z%o_imBL^^~4y!R3 zIA&o&JKrwRetO>u+1IqJH9cFcIJ|%T7%WP^`wlhsNBl@BysB$#^_RdU`12MJJ0j|EMpObXZUZ`&-=^=*Nc}QUwP8rDEbZ|X9n*|7|4wGG7D(A z=gT&TiiPdG`qqpl>|2iQ42@d2EK_5AT)4$a5x9d{a)-W;q3tSa@7c6ePE1nx`H7(% z&SkreG^1LNgFKe5JC@5X)VoVfYZ7@gIp(ZlJ3#jFmi=8cy?iTdyQPP^j0d~$;>QU_ z!#a9J^guN<+(g9Lo4uZK=w(C%(d?bx_tO!Hl5M^tD43&lwO?m(74@_|NFcUL-P(Vz zt(HjXW?)a=zywyEFPzuXK*9QpMjT~U?_YcI)BL=G)k73hrP=#SKmkacR}IJi%uaq-q`>2#X?|4NOJ5~xu%?bN{xqX+KhzA+e8NM0d1h5}GU5fss zbgtm!yF9!M|4M{A@PY9O38@34#LKABdJ5e^I=0?9!BoKP*V*ev%PP7Ep*gJkfDy%t zifwHsnn*S#SXZttX~g)vt_k?|D!spbHuezBaz#UCty&f|wWT)=@E{e#8A>ue-Su+9 zA4r}io|bxVtP`$ph>wbDvx&0o-oPpcTD{k6@db~@gqCHv&&=HK%Ef92g@!uYFSp&Z znB2Et$7ue6An0!GnWH#*P7NqVh*yul=eAmS^Wi8;1EwNgExSfle?Alqrrq`#>h6J5j@L#G<;DWr6vcyV`9K$x z9^pJ^m~zUF+^;)P%e^ywyA9DnCUqKU?)?ZmcS`287Y~9>2hv^#O2xEn9^6&-*F7!v z@s|boO{;IaNs{bRN$`!=YwCk*+If)bEP{h)E#|iD1zor`&7UNaW|^Z|mLRQUEY?6b zqmtb8ZczkXdtuZdU&1L_qZ`n4ZH$m>S@k{yZs~4PYlql{adk&}+IZ{Ug8(vqm`5gr$TPi8dxS%_us#gr%F0M)G}5NRvP_A)5y9Vz{SnF zY2*!$WY(`e*^|(=2Fk2M(j5y<)j@v z+dNW)%iD`JjqtkgXhHOr0FqH%6iP~J_%hhi^I2$+k?pH2`O zSJ{s#TPd4ju#Mlfxq03j?yo@zDyH8jCe>jFl?C{Vg)1`j8=U?#U_9j*xe%p^@lE!Z z600xviK{E*(az`5R?6SL8>H<*cijn-h24G`NEFyO7d!LkkY#beqiV(T%3Ut` zt%Y*Mj&X~Ik1xEWtc^qI<)d>S*gwp_j?7Zu*bVy0$!X3GjiACoNhXm@XGEgM!Pl<> zDZ6~%w(4`hPSKneLpkBD-c3t8AGUMIGs6ql|F7Wq9wK)tP5a@N*pp)Hb7?o|QDq(^ z({DqCZ!MDt2|Lcbn##9xt=0QYYs8tHmMab|0nYK$-C3wmfdfu;C*07b|6}%N{Dx{E^%2D|Fh&T;jCA%Jp*QTRUpo^;xe*q4Ozc?YzMO^oOP(XRJnxT^Ql z?+|a!?DNM~)-G&Wnmgq9h;eQt@gTGwq{{)|F_E@w_KzaF(6d&%!uqE6?r@X=X32wW z5U?3-MF2PS`hC-)qLa60(=co{bmV&?JDg)AzZz-)6}YwpTW&eEuddYwaZhs@$UGT9 zE8Bj)E2XBqmK|$Y_Eq)3PE>1t%EnPu9>hgN77DiXTGfFDu|@M#dvFP;c6Vgrnic4b zg2~ zfohR+_MYwhTtF=Sl-KtCi`LXe%<9Cl?ONK$HahaGMs^2hP>XfC;R?w!0JmlD-$u$) zHEgk+eACIf$*?ptTJuNsHE43|WpSeTW_819hP)d5f*p)0VZNAQ;RjtWN6((+PBm`b zX$PURSVh&I+#y*1!#+Z+cz?(eyR&)8znd>HzRK)2?2FL{cZOo6h2*0UW?8Gi*9cK% z=%VW>SeJ86q{sM&KP|463zDgQNfv(Ix|5hW-9+1YR8A_26=pZ4VyrY`P<%vVbYny( zLd@@hZ)ew%ePHlT3;H2-y{>sjl|horhBy5`27!&K`91bz>G(-?u2ky1?eW%Pc0_G4 z(Bya$w<$eqOYmnIOAq7K4T6Qgr+KrzYHxWBJ61%MP*<`cgRW(ya#T;MA7{^wzfY9J z)Hr@V|8xk@ltKYcek{UfH<3F{F@xq1MAfnX(#-9a=jUt{}x)%rY;!xmRO>#1S0Nyv zlxWY2KcGZ@Tekl~%BbcmV~!H8oqYXC*+rlX+SwW-oB0jXutISl@6aWAK2mqXL`d9=FdrDwi1psKQ1ppfW003@fcVBj4Y-wa+bZKvHFKIG%UobE* zGcIayXY5!9SW{Q~KR1)Gw}K!cOu>)=nHYv_5SfBnt=`<+gh&=iSZWD=lQ-`@@9!PwJ?G6m_qJbZ zZvoEf$!W;|ApwBE4`{y*gh>@@B>E3_J&Nm?OeS)5Cu zObl~aObf(ZEMvtJ3g+O()7|y6rN^=vFJA=V18LM+v<>loO<&hD3ql&|mNF*FCpTp;~A%bnH)S1x= zw$2pLr@2XqVe_r~3TiGP)qwgZ}2sYA7DEE89#(Qf%AszqCjiI5`sIgR6fpKsR4xwffH0D|2zAz zp^7MrA897ss|wVNCZc9s#v(e1%@wiv!u+ld3g?QYTy7^=uTU#1-wB#v#3Af2pnO3$ zXkw2h5~bn#)lm9zbg3Aoe zL@~9_tTz?)>9WO&P@M{g|9Tu2=RclH*S6=06hgi*O29@V`C@jIKm>QZ2u0a)kw7eo z#85O!qWo{0)4R3u|G}KJot5vi4m7Y@RBQj zb6KiW!}JV9OId<+I8u}vQOg~q(=VziC7MMN*{z*A`isYbLvIb=G#RE>;GIY_yiD%w zFutHm$?B#p=X>&Is6lozYq7Og57SAT|@A$Ybk2i{XT|iB`aCU zN>;LxmHz+H+cyTd4j#nHS+Ozg4?y4SB0X_Z(;JwP5nLedpO#4pBHl^>=X1W(F3$j) zD0e3?=HGwTe_Z?=TBj#D1EAHJjkziPnS+N6WztUo z5?}xt-~oVQCPQXkaz5cQEh&L%f*|l4dUAnyQv$$Qc4{`03H~#3!3;(-07y2JBNgx* z1mzkiYs$?AV!Rc~?($+nCQ%642or>IPeN98$bBuC4mpmH6t6rm8tP^D4&CJpr%x-hVnDQR*R!104VmvxEaTapv;A`y)i#G0m}UVu(4Nl zljYrHGhSgPCY7K!RKmyPA~Q1->%-(pBw}VNUarB-W_C7QLe!{WCg`;WR96W=$DA$X zO4ODKi!9_xghDnyf(PEx-zwjch_&sIe$KJ18RB{T?bx5;^bG)rAH&+rd^;vz0Km2n z0qA+lA9sLD~zPAwaN3WzA z1T(2s7Q2Mpd?r?EEbGt_Z3(Er7B~TS;01bv01yh;fDc3<8pMGlkPfmyJ{SVt2N);< zT3`g_U@VvjCV{D7CYT2nf~8;uXawuQCa?|c2K&L+;21as&V#F<1>6GnKr46xULpuW zM;s70#0&95f{<{8kBE^NBncUSA*IMzq#Buu%tjU>%aBIo3uFhfA31`Y zMlK`Qk-NxG$O{sgWKVJ@`I3T39Fmw6Ps$+WlZKO2q!Q9-QVnT3X+Eiew2riuw4Zd0 zbe_~gx<`6U24p+3C)uCOCX2}l&Ok{_2ix8L*%pM7V>@aa|(^( zMqyIelxRvSWe^3Ulu*V|rcoABR#Uc8nki=~*D0-(*Hj0p4>g=BrKV8_Q&rS*>Lh9% zbtQER^&s^;^%nIBjYjjNh0sK_RN4^QDB2j>G}>a?2HHN_Y1&QNV>+GgMGvRT=vj1> zZl+J7FQBiZ@1>ui-=aUWv9s~B;oBtJ47JhORN2(otg+c+bH?Vj%}a(eBZwhoWHA(s zO2!Pv3dT;xNyaV4OIsJ)5L=mTzU?U6iMI1?*V`Vny=MEfot+)aPGXm1r?#75H{Wif z-C?`ycF*ly?ZfO7?1$Nx+E2G%WxvnF-8m0@V(#Lvv*wYQN8E(-qZW8pPOHd zpW1J(-(J5TnI6n|riMA6+01P9_wi5lFZEyMf5QJID}*(ORmIxC`ZmBeKpdb9m>bX( z&>H9$m>D=WaBbj~AV!cls3_=@phH1VgF}J~gC_@X3%(Q5GbAl!bjaF}YoQLIv7sfQ z4WZ}y(E5n`6!%%&=TsOeOb}KSRv&gUoD?n$SBEbSKi!wwSJGG4x1sMvwmmzZUB+I^ zz8>KjF(9HUVq3&RP9SF}XBOuW=QUTr)o@pEuku`Z>AWi5PTo)aFh0hw=bwvoh)j+g zAGtlUEh;<;k6IFSN#G{P6igO034Rrdght_d;ayRnXryS7=%UzNoGqRvJ}jY1`b)-3 zc1xa1g;KM0lk`zEJGwY}P4u09A^q@vEBf7(v1F)hnXDy-88b5G)0mc6|5!A(A@)XG zK%63OW!&xfKJlaC*Tp~V&+TvQzoq}v1Zlzt3HuVsi7AP-i6@d=lLjR%O!_XFm8?o$ zo7|crNEwr|FO`~_kvc2&Vw!Inme!conl4KJAiX)mJ|izZAFlX=wgO3gI9-kG${wzUKX}MmUW?M{FKR9a%VX&B#}17TSP5mZ!-V$y>1m>=W#sB2F<^ zaTk~2v+>(XnR2%Bj!LGQqq><8Mt! znXr1I?Zo1V-&Ap{W>@`EU0A)d#7`{U%eFQIH`FbsEiYUC!-|3xhdvX3*0|Dp<ExW;GAjJ2e-rEBl58@BG$=ZT+hU(a5@Y=h^9sT;w@(v3fUG4hKGn=&>v zZ5D4{|7FmZi?+CJsojcft=!tWt!P`z_95F(??~U#v@?3=mR%9MR_*rRUH_HGS2On5 z?y1?+zIXKA$NLQX?(bLazu7dR>FR;O2hKI;G@m$_aq#fh$zLBh6n|*%VcFr`N2Et~ z92Fnkc1(C|>v6&HE#C;f*>Xa7V(UrK$?c~kr*@w1cY4p6*faajCZ7HJT-v!~=LeoY zePPgrOBaV-Y`LVkbmy|>a@&=%D=)83xJJEJ`>pf0b>I1X*YK}C|Ju+ZZ29VX;`QS< z@@`zciQT;Sz3KZ`w`zWH_@VB$-|fab{5!kvCf_~v<9k2ex@Wle^8Ta;E)Nzx41M_J zqqs-MTL-t^YBRRA|1|Aqub)@_67@^d(jMvxNwe9Wg4}qiQbuv)wBhC-)S3wPYyyzkl@g`Dw#~BQz_JLns=o3eZa+rBBoCuBQAjCf{0eB`i0_#97Pu z?q39WwC+EBy??IU)3z-#&{);bWA>&S33=5wqk;rIo6caSm~XCGaV}}l zho515G)$G6~7z;aN+8os*NKoB=1=y2esxD*~5DQ+lgHsd)#5r4; zm06jUS(%ktnUz_Ym06jUS(%ktnUz_Ym08(;J2NHiYU1VXRc*lz1Z*!GlXsxkWYv~_ zyuT1{Zoaa70-i19mEeVA?)SV(CRZ9yiRvG#F9Q+ z7wM>iQ(x$Ht<2?I;jY$n7n9`qh#Rk5tDB`|#mmxNi5%i#Ho4U$|GG7PDg?CAkG6vz zEpvM_JAr=TFmQH7wMFgZJa$9dI7a*pr+KeA`$PLBY&+Zz{8~1q9F!6YJCC5<7C=td z&!eAW4?XKUt1ZQXt3K!#@M?UOll#~iZLe+m|b01m!yiuH@Y)E&7j{nEr`9csPq9gwo)T`v*tZ9X^cK6Le zJkgNnlk*lhhJP-n$Z<>OISoRPAowb5#xGW&xb2p$^pC~8`?m>U4dsmgCFc-B{v&Y^ z{Vz$egZu}M+uRJMYQuZ#u38}XlgpL=XBoSdP7p-KGJbRV|FZM{Qb7OzxbZjL6U_fZ z?}%#6&jaI1{Et-Xz{qQMJJ4z=_7xQewj!dc5k+Q zOQZy1AVLGY$!{8D4Ma3GUx)%AQFEXG2;0}f_?JL*fqlL(mN)GPF0n}axDgW|FY2B! zt~@LPgCi6W4I2mzf(s$IIRm~G+c^8O55ly}0@4iOlJlZIbpQ1{f zu&@$XP*8o-`s7F`#7qPfh4+zxaBNTjkN{0UvcF}Itam&V19a={Jl>rAHHZZ(?w?}< zv3_bHFDoV>Pydg};$_}?b9=uyi2RS2!RQ9%DMVu<{ysy@tWVB&`#Rie z11aQS`de*lyuEJ@z_;LYYZ0~4|Z^rtb1OaEjz@Pm55?28Zi>6c}Rh!{c*Ugz>wVQhSE4OTH_T3is42{a&M zz~B4Vkeb5uznP32Su^Uk`_Cc4jDXAzg#{&lM+!C}D<(Piz@G$3G~d6y88HO*kDk!@ zJJ`LIl&>J8YH)+e`g2Jkbl*_>^PGsUzqfr(6p|_w5V}KnS@@r?4Q3|{xboIt%V*32 zAwr~z;y#po+cY{>`Ak4h=p#xA3oqwweyFU0Ag)rx^TVqj!`A@osvpz9?n0uaSJ8C{ zKT6kyPOs8y9yrm2pJPgaxctE5NhovpK|xJBGNN?wmLwu$BhVEJN8k$IJ)0zH)?B@k zIdxM0PkQd;>z6c@|BK>(HMpGK$-WA zg);tI+HM*orXCIXTd^g-%A+C;Yf20RRMhuqvfPdUSJHFKh`y4d z)M%})@w7d)E?ec|%Xyi`Y|Qs90dqYGs+@^}2LQKW(EL*SkUquNV4i zLw^mZTl-$<-#rA`)*O8aD=`8xj47*@sq}N z%m3?Y-VvVk<{k*w*}Hr+svOdpff*qsjy=a4#uKS@X&A?S+#-h(QWLr^dl1m1Y3)N2!8dkQZbe zi~N7Ptl~2gkHd>8K`DabcRMjMRZ!wT!3|^UWnAz$=z_c;6FidGnomA5`JIfD`V0F| zFcIBqUvV4ZtR7adbbGHa$hNaY;L4{kn}`A+3KlGVIr6_>i7Deh2>PD@dDCF)%KL{4 zD}etXVkpA~W%PULh)cn&TKJ1qQ86%~ih|+q7=$7sQ$G1t{K1CMZ-oP~0JtFxdBZB; zqZH3aD#w*GjJwjaKoex%g!Jn#SqPal`s7ET2MlNM5mEy_W zQ4dRc*~vJ-vTY)u;L-v^b%6d|yU%NX^T&9)3ir}0Y$q63VF^vsNiddHLiZq|4TAM5 zZ)TMD+E+#>VkYk!-25jYh(++_|Bc4~((p!uD#Nm~z-#f3D=XQ9aBxydNW>Y4Xm=b? zh$;^;TVTaT3NXZJd<`Jegoqfvg{=t>R=F6&O@$hgnC-o>ztkP%?9Zjp%s`BS=;XUc zfc398#D=U%M1-FH*W@Chf(=d#IO+kos6QkS8k#Wi7o7lhAOYqrx)nc~OWX+@Rl>c1!3#Eu`P(9+DCm)qiRmaV^dJnH*$a~7B! z@DZt(9E`OkA#mQJna3)05b@4YgNA~-CqK>ba-1p zG~t}G0Rv(P2Zvw5?O8MsF<+*(Qk-V%Vo zsDGpVA?%Z+Po@8dop!4?{zf2hBa(Ql8*(vl@eyw|7gR5Y))p4G7_}OeCd4eJ65XiX z7#+R}kN01lS;Tdh=g??Tisk8GgI`|T?@pnrvRH&24pw8L|s4ux}YD2%nYpF#zg_B z=fEZjguxe>^d{qg)D-9}b~p84rukdsAXAP5zQg|3us|$=g@(q8p#@tp&NgZR2C3uH zsk);CZ=y&hjER)m2`GV4koEoVy5gS=MAC14fDMw}V~DG7#{ zpZrhxQ}N#VIvK*uzj?5H1PQSGC%1r=4Hd$jDWO1)YYGj5g;>CZ2uOf`Ash}XR>qxG z`v=PaKqjKClw28_bSa0l8jBbhN8)=ABOmltYOMa@(fa(S+|q$bBLJ0aMOFf;k`X#6qnP@M=dgCc#c5CxK8j)n+u6gQ?s?_8?|&D0?A^+K?y z2*mzor(ER2`VD!%kR|h{dGU7x%L;T<7LRW4jq=`GEJo?zD~Lu!d~8}KL9M_RF}fCD z{?qsUOYsySzWHO}|7>95c}>x96TsfZou4t_C;yjg|FZ#uh^C~{sB|KZ(Im#5AM?v~ z#|Q+KL%eo~sqvc|*>4^3->mW5LEur5hfDH?+{f~_F@DnkK|Qj7J3Bc}SVN!k&LxEa zOwi&hBVXD^@d+wjh8{GFG-55rtnoEVS^u%7Cb%wq7N_pnGxtl`55~&x`AySHYNSMS z65nn34j*Y2hNP;7`N66|z|YwRbO#(@Z6j;c{-E7zmwHdRiPoNZ(C_e z=^RNz7*()ODx;(l?+XRRa(6R%2@&`oeV#IFqebd#*AKZ2QT z2wHnQHqV}HV#Xf5DlpI{Ne$ny{zXC5Z#2O2rh#hjgzA(^3Qj_b1mg_&0t5dDNcBZV zMgbUxl)&JG#>hqeTaWXrl znvpS!x=BqOMzcp?mGH{))~6KJjAnc1q3Vt}aIjcJZA5m}Ln)%IRQ^x96yl?xA@y+z z?@f-Csl{?oWU?~y0;TUG{&rnfe0x#Gf4LWM;H9RI8kd0uMDcEUqle}dAof!O9YlQs z?Fgv8#pFSSy;6m3YAm@g1yIdQ5Z@<#Amr0qnmH@<&ht|M&^eobc;UM#5ID?s=B0&1 zLNd$m*_rfa))v)`!iU$JM+5d&ZNuXDf|r^&A`^xx1M!|C6cmOd5;XxBsGy;L8KV^R z$^V>gXaWvpTH8u!m?}504ReqsPEOHOc!Ni=e@1xzfFH1!L+nV}|H+d1Zw*WwEXS>j zdlQR*oJ2x|WBHraByX^#Fb{fASQyjSKNE}*%t5!T1J-vu+OaIS=FqBe0d4Kx|KTYR zuj}q7Wg%fZG#1zm0J2f&lPruHM+IxfZq7{44$PuY1fKHsW{aJd$Pn(2_Qtire(Ydw zHE^^W+mKn4^`&c|6aBLET9dE8w1HDugz6gLyEL}nW^E5TJ8-hV53Oy12e=wxW)9_b3$zXTcEmqO-xl(1E^{A zW*JJ;b)!C#(qpY1OTLP%RE*H)ZjtPdZXkkQ1w73JnLTC#&`@G)@ho*oTAIazdFH1)ejkyx?WouQ#2 zWMUhrd2!ji+wxRa-Ewf~c-La&wQ@epWuouTt{u;0sN|so+80;zQ@!@o4W#OVJH5os=iQgB> zf@1^J;Gg~g-))@xMWLrTNtkcvYddox&|y`T?0h#PEuS0PzJrelI~su{Q9o}l_j3fNVX>T z*nGnWyk$BTKu+HW(k~f2pDno|b-aV;(0fDtb&(ba)A^7hai$Ep%DtZJKwh_$Wh#3>& zp`zIk^Ya$>MUj3=j9;GRKREXu2x8H|BA9&DFdT$QLyL zLQJr`pw0hiApZ9TL85`xzyuP#_W5ND0?Dyk|FRDt0Dc$j^Io?U23vt*thfMeOz#`l zA`S}p-{stw6&DIq-1|3ZV5){203ha9hD2NR6uK|ja#G8w1r~?qm3+RAUh}DvrqpNA zvH6XQra=oDeCCM<0>_(;n>HL>Ee~;?O-7PaBH?}`sF>CC&CtGA?j0(!R%vkqu5nT zmtF_&{F)l=Y&Vj%Sc#OaT>k^(&gy7^{1uBg8_}m2wS_LM%TqOPd3i=Jk!981n68go zNAO;h_DOh@z)vZ1Ft>?^X^pN_A3W$9vn*b%IBasz8Xi`f_Q=_mctS-XK_agwAS+dT z=@t5;P&?n$ifBKyz2Pch@jI1nM7>XEC8u~y&=V`OU@vNKJ z`<-0d#$W2WZFaU}e7aV4$MhUSO$o5mzwz^CrIGjX7S+D%?UboLXmPlVh(DyW#|$x_ zwW-_rE~U`h`Sgk0>zS@4MV=IAhKZ_{46r|7EDtl2>0DYb6=7ChWX zP^2ZIGO5D(gKui^2dBVH&?@8A(EPiyo`F!{TJU;flpw>nnhF2neJzEl@gFN({Ke4( z7C4HUp$^3Ty%&=83v1$BQrd6ah4)`vwWqb07Kf*BnbCTBjx%f1i$(p|jhRhm1`3N;(hSv3e(! zAmbL!#ZACHAyT)XYSh03A}zE!I;~*)IF=4ZHurqzrFTv;yp3U@pBZou4Y^8Bn$t^0 z9sHWfI?P(kA3*KQjq9dt%JEj*OVKPjMd9S6 zif&)ZVo4*VlcTlv^@#P7WvaZC+5DN~?vG{5I)3YxH-X#x6b_-yTCS|;gpY>#W24p< zj-q??j^bNL7Nv-&lF@DMXJ2?U%b*#Wnm4_1dG;wMC$V#ACHG#9WVpRM@QgFbr?JnC z7{Q8GakiVl;+3&tswxeqMI7S8@K;@7xdNG1^1kA+L1vkwHu5EtnI<`=w$daDSXp5K zqYH|Z70N~<0U4wd#kR9UC=!jQTl}i4X9(3^Rd^rp82ej2^AFc_bLMxSsbp5_W<7Vhi5zY}932Y(+gDe78T!c8T z4WyY`?$izkGo#^NWFux&)m$^26!}QALHC`NSt2ushz1AZdl!DB`!7Y1N;^f>Gu3sK zzBsG1AlG_Q(Q1oPt=9w(Iib2mA-&BRTC=OBOmmAcgRxJM@kxRaVQi5d*&#tiJUT6O zms$@lF06mzFjsl7Ye6jH8H4zAPRe!csd?mvQMyX{g|e(w;I^}z^B7fXRS(&je{O^y znXyIIYy)!YdZJ@Z?m(2ce8XsikE3p>++F&>S2E*xE;5 zh#a5W^BaPSwBBf=ZO-z9q3$OR+n5Q>IGGl1%Gsab*3WakEqXh)*YYuaoX>X88e_VC zw|SIl^1K*PjM9i)6@+Bk!uQ4b?NGIl{1Ml_mRx3~vWIeIliy>esxP&(_Y6by&kM|* z^jP_=6%;`iO{6|!3qs2BrFnwSn*^6F&t4|*S7nyIj6b()V5gtosMsAjz6MNemc1K( zWcT=NmUX>z-d*QjUKES3s3`}Oqw4_?t;;H&7H3EU@rGdcNS8bMl+ui6)FXGey4)G! z+~PUB4^E|St{$OVFP-7~pAc(}a(xkW8Qc_gGCgvqVvkXzZkO$4OxY9~h*i0KGD=x3 zD8n~e{O||Tdid~%tIW_rL&hsMa&aCFdi4apWjpfA9o&Uto1DN%!n78NolR@qQ}|~Y zX)US45nL9B%vulVL>KuJPSjSvYWf~n=}i~8dG4wF2>J5bYAC`{i|GuICOO=t&Nf%UUi;P*vPj zPBuqg50G$U&7Me8+nQcUKb5!|R}btxH0Y~g{lPZosax*rUVl{aE~E#xhWQJL=mb+z z{H8HYp+d_t@nhQ9br+|Ch!$-5OjvX))$Eu)G@5je8t$SVUFp5%!L~ZXwdm*%E3+&i z-tK`(^Oe*ml0ajRnF?;X@P(@6xNal1d{?V;`}3)(a`ryOj?7aWGF$~!CEU~dwst6N z^&;JhE#xmz(nO(zLM9kXktiEpG}m%WBGUy&>dzXOM5T3CJ-OtK+*FJ2R(7Yf7~9j( zJMIc7kDUfO!WM^Y6H+F=cs9v#K<-%fsZ{%2_@ z<`|L6N|v0E*OCBnr0R|u6G%b5x8Pt?DF-m&7k0`LIcF;Sno|~H-UJgd6_x8A__glf z(u{OaLan$le57;+)Q@J0Psbzn(~5Q==uKV#ReMQV;((lprO$&e6BrQVNPyl+C>9p8 z^lECA)EdE-Vf*Y81}B_aR}%X+$RFg{$HN~NCsG%=lzL)OR|xF6IM?hrDajBaN1WL# z^w&HZ(`?ndcs^B4xSLmm4n!LVWcCr2?)f6wGrL2}XWNBVi$@1U_!}u{8Y0f9h7sY=*^33beXqqG zOQ*w}GGAZ7q;WkV+9PGX22dbU5Ts9W%B{fsKuntq*gY&N^=;MciHkdtAzqY9Y%*YA zm>Bsm{55t+V33Xj%AL=B8v`d}EEi|%f)B6x3XS<1z^*4B{k)h|zuDp7A*+&ZKUFna zkXpu;uw0sQS7?@H)$>8Fys)F!O?ZNFFNmVb-cAqHuJsu1=T}3gw)Vj!pZYuzid=bC zsjM58_MF*n&g#wMKApHLiz-VSZLqHQ%p8Czvlx_8e0Esk%2V4kM6)-SY&v6h+P5MM zJ8C`q;&CEzbagWt^RYV~64in9V0uO0YQd#vq`bHuvlgCW8 zL_>3FjLr?sNw3sTBjUbfO*~+DAeGa-v5B7;>3yN{u<-?*ah1Jhs7Jg_RQO!&_nQ5H z@wAT$^iSIlT> zT;=gB_&2X9=j-B`cLypc^2KUx>1sQjO}Q%q_o~sOkFEhtFPmGxo{sfOu4q?tAMY3v zB#T}H{1VIoJM?RHv=WJYuGZ1-<|^RM&ubHC`G}q|c)1V-{%>w2aX|2vPD)1KL8towS3}gpo>YAQ#j0A{e zjv9qXIVh6)TT_cr28w~acg5DKR5UV=Wqsp)^@T>mI3=d=qC&ZJ)IsAlo+~DHJV|YvRL}8lTfR#*p5=en&*d1Qgs&9<& zA_`S3HOILWR11uI4l2dT{{&2|jCErcQEi#qR07k;O<)9>1M2==g5jpFqOC8L{m*n& zrV7P<1V#^eVrKasrGk4P++X2wAg9r7jibN(pIH+p7oQlK`$fIv-a(MyZ>SqB1|~Kl ziSC39mXwp9_`fim0a6}_jOyzT_>X4!Z~I;P@5~G&QHIP81^gNJzM~kehI#}TlNohT zQJ*lqYGeJyt2l`WL{-Urm4m>|StCf7i(4EXZ>cbm;PJh!-Xe&kl$4@;sr-tqD z(H_xYbEjw8*^rcDcYO1bMskJOD+_6l@YL|02*K5PFD|dBtqPlBajUPUQ}-t{f)m88 zfJ#+55g-Uvj%59Z%0`LmiPg1!FAA#F11gQ;3`K=r$4m~EB*wQintc(|(aU*bdZb*) z8!@^Tk3*8(%fP@V6{D(p__<{3xmcuqTL3N!EP@l8KKKB zTHbT?^_2!zx70ZyzX&TW$!x{-eT~A}+%NU^u~vhx>kdkjggk5sJ>tm5Gcp@h(M88> zD2yLIk9g39m`)|;ja^X6&WKeduF>VC^tw(6>#%g!!r&pvuuD`^@T)WK`scP@20Pyn zP#h8f#j2LGOd@*DUg-$|}- z9=@1L;9UIpDzfTBD}h~;&8h)YZJlAaLy8;Hgu@GBfAhIR4*@B;$nLmQzR0KF`Q1#W ziFPeKWVvG*Th>#6NcZ;HVpZc$rt;v!Sk7dVr!B6#kRkw=`i`~17 z<)5n$nU=h?&?m9D$L!w-HH!+Lw!UpClu1e?L|J88mmCz{^?2I5OrptxCqnw|>gjSZ zGSRpXkwnOSC7yNNy#BZR^*ud{mnHg8Lq|-7`$7X%_dd!bTWMJ(6<5}#DSAA&ZE`?1 z-3>0&BXA-oJ9tAszaro}sMEw;W3{7KS0uS|?72ESk6HfE`N1si3`vI_v3!Cq@|#Tz z(lgvAE1B#QN>w3;kHf!m4jucH+3h@-IKklE-<1%!;zte%*zohD2 zL)}Hho^|F2=aKm3Lx;LD1?||6jSn>sEo~=P>S#*QNiO&TZ4%pv9d=m9_y;B?SC|7` zrz=^FeV)Y5P)8ti#qm1nH6c(kT$KpMo4En)D)*;Ibai_#2X!5n_+6&zHUMVp3q{)n zR{YJq-z7J{QJhRT?GW*`RMlzP`L@|j3VfUy*n00!R_UkCXF8`ODqJW?$T0sQzRAqF z*>iR5v)NLtN2ubvuBPm8uC(?(qpC+bI&vOncHHM$XZ75CbxP|+pXS!~=X0oIbT{+U z2|pDDe=jAY+$3;`TR2|OHY5*|Gs8Z5Ge=SCNjRz(KgS>=GQPZ2AyC>2${rrH>Qr=y zjD&;h*B8i=@>My5ZgRG)!yYC`_!uj`S~t#D7wPgqd^x+Cdpj#avQuT@3%Z_9sL_6X zBQbrmym~*rHd$Be>f>rqVG^apXid(Da&}pT^sC1SczEjRAtA`9*)x&peFzLbPsp&?XRBxwe9_EI!Wa*R zMS!FgMKm}^ZVdjPF%xJU^q556l#vVa0&Or1?~}^nuqo4-9)k*zQnzp!EDModnFMH2 zG-XJCMck=PL`2))%Ic|P3=HuhPi);P}i&>ruX(H{~Mx(1M0AT6|qB7i%=%s zKPx668Khc#9+kbnOB{GK^_NAeD8ym^xE07ofdR?Gmybqe{jTgpc;ZS=sl;0ngNPUc zU+%qCmt_EjDg0_;K{cA%St}ajugDH0QA%~&jlm2M4)arI5Ete^58lPuZ)&O$7&)LA z&~O35*hH+5!t>wuKPGO1I3+Acebmot)Vl}|SaS^G*dpXQDOm>)I3A=hCA%F@_AP%O zPJ)-6eInVC|Mb}uor*eB9YY*h!c%NNPj&XGeOP9*1R}O{=AMiC{;_>Mo#{thM>ToX zWJlr7p}0%qJt8{;fWraxHKUaRpg0M1G`v0JIfT6K*ML;xL}UA-)Y{WVr=m9Xv5B}z z*HZa;g`TBz^~N-}EDpM_SV)qsx39j2RFY#%l?0cBl>z#-H-Z?$V$@17q+dQHjs&am!M>cdU5=~>^G=={i@rMNi!q-(*W z%b$uBQeM>c_z}Pjd9JvupYi;0X|oyb%loih3>wt0Q-*V zI$QP%qld-kgR`V3U!|6ri`#y@<&w0Cw9ZZrX-HTUn5m@pAAHnqj~ToXxXkO^w|WgQ zR)pI<=T9didcH4mMexO1dDuU?;RnIXsU6Pbj|Ys%+d?BQlx}_tR(|$m0)4vzuPx{d z(uj3nUs@Fw96A@r_N}>2U2!{VQDt|np4-lUYnDsiZle9PmM+!ugk)s?B%!IpWtnqL zbS1w@ZZ}xF>?y{QtyaUl>1=~Ji6MUeZn4t?JFbOko!IZ2pJk|q2e@(tRU%2ZQ=6X| z38PJ(Njs96=|&D~#}U)fHGN&$D$xmoMuKv86_~l@dY}Vzu0m6h!?ZVL~;E9dsAW5p=UlYzQ=HN zE-Yg4#c54bhZt3GI2yC**d+%;c>n0!HrI5G489?HRNFV#)j9cs@I*b?g%WmnKkaJo zd!(u=kDqMjg|3CDxGOwdIIC;02n*1rvA;~7o4QBqKB(xBTA;hg8=MnT*T}Ye=E>c5 z@^SbbdytiDz#|r+)yfA)q!ULkjdk9X?LSOBOOf|DwV012@Jw#dO&*xjj`>b=_^rdC zN84>X8G(w1w5L+ZMe&|#t_W%8;YEpy1>FZ-qBFKGyQ-yN{P#tSYcHPE*G_s_9Wb|5 z*sDK|bozAs;Dcfq&PBih&w{nq=;{+x!Ok7aAy!$h$_d4LvDUH*&Xa*7Higga(#Pn0 zzLR{8=wmf*cNkOa_LuYz$bl-HcP602i89C8fu{Qp@)Vq}j9cYgf zv(4NuHA_4zIo~f2d~2#{w^aCQ;ZVavXQN?_TD{e3DG}2pw@N+v_P}7O_eFjC$MUra zCuL9%dP$3h}AF#{98 zzEcjPtu=uX#~(E&@vK`JrB)B!j-6fCQ~(vW&Z4E!bDhdqsaus*#5R*c0iSn-1_k6R zT5B!LCNF42?%i#u(FCiJ z`;e-8YBd`uxB zKdWUR`snxe3$QB!NtoUgSm^&l2 z!qymXfW@1O5-lZMY4IEgehioBhr!v`Q%7u3&C`j~>+S1~!Y zteK{A)M1W7!xBX+%$lLB@_g@7`nXHE_3kgl&N@ZP6=82sg0F zkn`G2K#Ht~I?Qa>#`2)~a%Xb_{&G%R{Te{!H*CEZz7QeGy+kEPZ5O4~YBAO3SidOS z*@WSj6VG^LOmv!aFrTE=mkuY!Y?&rF`|f+|d8nsBXw~HWSl^ufnI*qSC*X3xgmaig|0q!|f_3Ip{qsJf;%mlC zBZF6O&$SRsMYlgcv6SG8jak~hNMhX|l#yUYmwX?suA<@g^ji$_?u%!kCw!jBp~`%6 zMtCZxlkdqJEWf~QHAPuG&3rkd9bAGvrC4|sEmGfYRQkwfAI(v|)D)Hdiw=?I#_qgX zq#>)k4B`E(s*CSvmi3#um~Wh$V_QVX9BS;w)6GXJC){2?CH=Ui%lD0|vxB%4Bpr!E zn?K$Cb~VY4|0H9KA7}o_T>c9ag_G&+rz8~@&PQmQkHY9Ksb8Iqx}n&>FEl<;x3izG z!%gtQdsAEYW`4S7dht{H@z_L;($n@P8~gFRy!Fge#@^J5?<@^)Rh`GVbt~GE((H8a zW@;I{c+cO`3}4oJy?8l1a%Ff8sI?e9C8N1Atgmw+JwQ!uK7Hq+-XIT(SoyetNu{IJ zeY&;bO6c@dX>HwnHAN@J>`D=}Tcqm)iI6dAg3v2x_^S+ML{bSBji3xSbvg$zpMjHjHu}P~48{7BDWHK*C&A08<-6c1M z)(`9s_f1`_^Pgh9Yd&Y5!rE?Ho7tsuKR0_{%~9od2s`sY#<07SpH+scIpWPVK=$hs z1L=-&SGgI&UN>3hA+m_&kgsJg5)RB5gzZLRUWP1|^?cH;%vE0Pn$s5h&=j9weNN_O zt+V{SC{`k!pw|9iMM{!1(k#^o$Hk- zbZ89W*wQ8LB{hL{l%)St`L{g}u+LnXgWr(z3gLe5e->RK@3RqGu#=a_kcE{hGUAgZ z8K^h5R(l{GQ7tIb0M;3W7#W!&VwG)dzN09tM2|yJI(Q1cXxtQT`D72mAu`+mF)uwX zG(K;k>LgZ%awq^2@)99YM~x>hR86Mfp)?>-?@L5PtSIR5>xl%3zZPra<(n6Jh8fLF zx4{TZq9{q^`I%BifaIgsEm<&#Z|mDA3P;}DR@DAo0YqFlCH$wJfRxIGLg^F#+G~h} zO7K%l@M!+r`UO!iJ`9zKaYKrtufX0|G&ZUMWQz&Fd%2_2n2kQA)U;Iv>gP7Q+g-YM zt_kRm^ae~kySL=4|4^0pug$`g1yR3#KvKjMNO{)1(U6|5cs({8165i|2kG370R zwX^+r!Q4Gw?|UurPt$E3!qxMN3RFM59#xbnnR+dGoZ5Z-r1}Ng1P&qYKz3uwZUo+{ znU1-YP1&;9G3=SjXF0F%H-RfA_yV%kM$1N+s+u=*w&p~70@oXL4X`sl04s~xoxX>` zTAD`bE>2Eu&Cj@uZKoL?p< zd<_k_=2E^F+MoXb)z><TF9ka(*B#aU>#LR*p&t)v zEEme^ovCpzEj$e}hb&*_jEvTu;tAa+p28HHQ}Ec$@3#FR&>$ll!TvFIlZfpBOB24z z(UVVBksrrwb&j7vc}Tl3mQr-&~hKPW`?q zDU_(A;ixMS)Y})wR+sN-wBQ~YTQMi~keGFP?b%E3T27yCwqA=gLZt=0vGKKMrTH22 zg_dpGhsmBgUa#@!OpmjXnvf^>1#L*P3qNhp#WC5H1lD05&DL`qaU(Un%}?5&TQy17 z8b9W-f9togy#C4V7_pqCZlfWcMX;LWBdW))qbSNG^D#GSq)GHz^090@NUmg!O&nW30KV{pU_n0DL{)V3Ry>!O~>UqfOJ6x(z%HB=p z{gNXVEh%K;%cl1m<<2!3F*=(_NUHIBbgc_2$yQ2g6KACMXA}rEKTd;5b2Y7qwYiM= zq-xO$g>vH2^O0p}-(HgqBm>HgTs)sbe3eR{sC}-x;vQ5gbYZP2X;Q9VNuMCi!(V9f;XfE$)m=aqlEap7 z4jCGEjNqYhNE%fnH?1jF_M3mIYT48Cn3nG3G6D~eKbP$XF8-TQy$s&xPfp7W;vJ+P zihL@29P>Iio`BopnQpg}*N+!gwdyWWk35!#Y+NdIo}6TUaEO=v@!V+DxeRranc4mt z@PQ2HdsBW2X0An6dy=Jpf23K-qw?6Nc+B0To60U3SyK;QVRd1x++)vI)9qPO_5`+C znJKH+4&+Fol4c4B7!$~*lhK%Tj88aZT5yA+WOv0kdwHXh&}~&rfI~wjNldf` z<33t$qn7ftP9V$qhzKhu5_J_;F=ZB8H+reUIAI6rsRDI8F*=DT3|RdwFgo%;YAzuk zdVpB;l{ZH9r>_~Bl4}ubpn_=mBwOsQ8Y;vX*IZyuV!i)VR$^(?uyT$JG_VRJ6?$Ay z$wUe^lr={emoIUpdlFuMi4%_=q0eSo{<+E3{^wT%5M^mwD_>ziiDdP{w?PMr-;0_g zV7XB;DAu-|rZSS&=*Z0xFzDMi3b# zMHjis^$>NmxhntByN6Fb-uNIg>oR!<29+irPmk$3LP`2rX@{x%*K5vB@pV+k!&_x0 zegRd?LseOe6elM)nvp(jvJVp?fGwm#z<3#-%^X5T6T?9Xtc231v7>6lx_gg9`Yr0V+Qx{KrS@cIgocwUND%vJEOJA++*97Z6cGNyH!k^~nKG zvS8|;r_X1t865yfpgTndk*37hd9nskM%Im@=V}(^P<`v4Cu(VPMr-w*i~xQ3wATbD z0@?_|NZRvR589Olu)L6XS&Kl`AEm&QC z&`?*nvn*V#ljh$<`#1tX+{a;0M^zB<8l=v(y__{cYZ4&5lq|);b>D77eeogzwDy*? zt7j7gYN~r`AHly$FezIg!{Ed?Hx^!d0JQsnvc$d68Q;e`yP+M*L-+-iQF@WF%*m1C z^H#B>a=3dU4Pvdbz+=rpi_~e(vqz@PjAu}ywMWMvZAKjTR@MFIwuul2Hr5(HaDF-Q zdK< z#b21*UA7`1KE;OQ2W^Vs*(HkJ>tG>NM^&4H)2p!EujXGLp)(xr*KKb`yiK(Fpo3sT z`p{ad9Y*6C@R40?-W^aTTl_<-JB+A_W=lfPpl`o9+HwDk;7MEq;Wc0f>SV5c{mfaE z?+~OHd<|GGw=qQ6dFcEu!tf~px!OWFN$U&$e4G;gKB3l=Q=z6kh7BIwDvcjBMPJKW zGM|1EJ%9>5>EFeDnnuwy|HRca(dyG_7jk-jL;us{XW6m%XA)qaC7|&@+hMz*?v&Ks z%?D4}fsUM8`&~Cpzq=8t9lW>eD~Z7mG7@gXvIh*=$X9ja=m{1Fy7zDmojEueKT=}} zrJo)S_;*&cJ#-IM00>TK)0CmQ4Gmrtkv2NKe7b%pmzGh7L9&$5{f@<5n%wcM)HQ0! zPO5oq@x_D3G7?qo*1|Ihm%c{(iz+jdrD^Tll#zBb-H*@nut|nsW}D%$7aE%E`8joI zikKf0B>PtqXI7QISdnY;1inixS-^AFT@vKHpJ`*#NLrlfys*{TruR-cL*ely)P=5Pd`=9oMG|4bqWHTUkm%Wc!Q;8v3TEezX-+$I-EP6kQ#g^ zcLOC7O^|Sx`Dt;5 z`wywaRz0S(W>t2*_fx0t17^MHAZx{?7SPyk!jF&l9v;n{A6h?1+O)UMg4STY1BY$wU9;?s(`raDa}uH(X@%YFo}z?B1% z=6y>I%jx=V9I*k$afJ|F3tlutnN zk<8C3m}&IuC&=GtR51$L^Gox2Z~QDP7o8r)^k83Fd#_(b=Nm4+PtN6uu4!f1x2V~V z#Ox}CSPW6z6bzDn6;|p>yeZD**#wkFsU)4`r{`cpRpzeuJV_@QPklzQ1Y2I#3_-p^{t%JnMiYx0hkE{;uD>t=fh3f_x6>CiaHd);=;5760p-JodHZH-Wdm zl`Ev40r6Kx39^av=5s?Kknkj!8Vt&aS4NZeH!9m`ND5R=pwTwh@glW-W}d4NxvAK7 zgY47GaI-6<$w9WalSUDaZO=~Y=&hOH2_NBd4xcR1WGJVe?%9nksw$z#*5jH`k&*1b zJ8OVBepI{fp!;Zq=Y*P_jH76+K>OidXoo8Te5?7?^ZZA?%hD;)S0-dDEcMX75_W`h zJQ>HDk3bo%8mwTA?TjAoJK#sT=-(DqW}N&o3H?)LUoCHCj22&N{&CT`wk}d^>kne~ zBc**R#e%GWi^B0amC7~@?*pQZDp9@&7{!QSF%G_0kAuY5QP2tyQ+*e+>w9PPNxW%L zIGy*u(2Eg8U@LUncC2QFM)7$gNCXU|ltAlqyq8ld4z*Zi z3b!~jEqt2b-!G>^FNUf!D3oiz$xA}Xp71F)uUM9)|LnC5B{6cxDOQ&WYID4E?>yoF_Vhr4_=*imENbEvpWnis|9GJ7RK8gcsoq1tCNzS9j*by{mt2P?pghU zFeO$=UDHu8k?lYLoGsI)EAzHi+BE52~@*9f$l#8O|of2QHqY{hhshfmZ|9fOwBanq>NL$MpL-<*5fTbk~3(8;Ms;7*MLimAIBFd?~NzNn-$kwMOulHZR6^Fgn3W0Qi~4a zJ~}$#m8VSU%KL7gnH&&P^9^&wRtMLoEaa5dz_SSql9R;w&}zlnK`{8mLr=v= zjSbxgbNnx>nioD2&;*wi(hU$63=Pd~JDZw6uHdLT`9Fl6bx>OixAp@q(&8;ryl8QX z7bsrb-Q6X)OK~Vr+@ZKja0pIucMk!II}~^LN>9&u&%N)R`6e^j`ww=K37Ngtv*h<| zT|Y+`w%{pJM1@eM;%5HLE*mD_mohVBEjqnbQpfJ^yCHTDV|leS z5L)~FDzJ4_;nlX`^_$g8^v0j=m>W2XIT}Jip~_^n(cGdjaq8F+1dy|_B0t^QRtYQw zxO8El!h1*3G{HWugh+~5Hu7&vjb^2kKoV>>>Y`_bhAjgks4M=%Fdbu`-oB*|f`!G! zWL2b$cnT)y7y`v1*ny%cZ?Xoyb45`(suv2998BBI_Rlo&69h)>ze!^M|DZ*|uVzI+PxtHMzp*m^2bS;m z6DDrdmw(>>X`JoPJn|E7s3_@A*)JepYTXG5CcdS0v)Wc~-7d$l3G9%z{ydyklNEZ0 z+}}J(aDAu;?HT)A3T<2*Z^*#;PMzV_0Rs(;Wsa9~DxyZGyk+j;s=`ZbyPxB*i1jCG&`b#^~8hPV0Cr(+E z$oQ&Djg1VliJ)StA;Aj*f)I~cle3A&D!vS6>GrX;UmWCdYhdQ8R5ix+zQ4p+uf<*) zD`^S7(v(d+E4+w4pzkKthgr<&HpYlve5wxinN!ON!-m4ksKE3Ug!Tu0Qvdbe1{C;B z4DV!Cm=u_V-bQsA@mC4I=7$-h8BV}>#U9M9i=w1vv>@sti&{)BmI+`Ll-reN^KG5n zED=vUI3r`rj_2ip4Hr-A)E3AN(SJ=6K^doJg`qVIqd@htahMsJtsPb&s}^v3rw2KNPVW_3aOl`!zN8_GlBc?9KY>$keaY|u`+Xg|NAjl= z)|OlhiI<$k2PGp6B{f5O658&X9V=3*?5VrUWG77&j;t;YeLMh$U4_k0DGY|>DJ=9O zreQyN=X&cM_S-i=Y-ULxDlW|V$Jlf%4mZz~dE@F8k!=BKYF+SOt>bFMM{BA z$<>v)*qBY$;N6#Ky|;lnN~E`-34(e}^76ccwisO2xIK8X^m%2U*-$@F6XaP(PqdMi_ur`xeavdCxa}A^VebzOh!N^LP$2guHB#lj;%3dh5bAbqa7xf{a;=sZ_G=R}`+witn+(=s>; zjX7rB)~ZjRIM_V6PUz5v8ZbGJu<&H9bTm#wwwFrEBW5Q1jhA!7kRn)c>2{#6x6;I! z4$ORJQZ8oVXx5yBHC-&p9O}#DtQZXK?#9u1aycEgruI850*`iOW)N;(oeO+0))_L= zjwlFe_(J?Wer8RZv2&)?^mSB;?3CvkkPh1^iLxD~=(X4wU?w+IF7!73Zf`tob5^jn zR%~pJFcImfLUqAAnvU2Z$jXL~9OIN%&-{S?9Ry1gT)6jYx`-yxCYNW=1-!oDNmt*H z+k>RAXBncVHJ3v02|D>!gwv)Ux4hN>xRRn7{Tl&IGdiA}@T52SmyN z%U|NDYDn}z18hoD)Fw9n-V){@AzCvJobPMS@jXXryoMb$)X@Mg_=Y=w;(`d+Alvbe z5y{$^O<@g(RnjS4QrMTv+CggZTj{CKCW{;mDSxEvLN7g{kHFw1TpZ5HR`nsfnZ}e4 zzRDu^cVSEmtg9LIXK#`uR-uJMZ61|O9^AMtcN|WdCVaZcUyXAW+4jLlSa?caMUhVI zh8!3@sQEtLdxq-^L5(-dZ{|}yKE`-A6LR;J<#~Lt53^9(F7(VGL)$Zv@XudN<+g!%j|Y?IeRO@GV&{Lg+=4t zPnBu7MP&SqX*J*!OsE;d=?=fX3TKYVMMV2fg&M&+;CRsbmF0SGD-=_>wy9C2w!PjBW1t8 zH|A-nez%sM;)Q(F2l=OJp}-Tq^_e|C>@$Vl`7asQ&4KB=>3@hxxuA+BN~|B`3_ zCG7qz-5@+G)aWFoLg&={CvN?x);MEC{RxijKPAp5-Gv08n|l5aZ|5aS@?WQaulfLf zR~pIxR)#)`e_UlwpaM_#6D(l%^70@#5qD~9m%5K4)zw~e)^>9!s2+fFdTGGIg}6Htw6L8M=$ zqEa$C8e2+~6~=m1lGJXM*jsK^i(R8%E!+4X@z{ zH~cda5Ttp#mZ%)&usK3ezo&L14wq}tJF?YhzoqE$QKouDdRGVfOwwjROkYCub}nL> zzC>irc}kjpz<-_US5l*dAs^P$;)&f5WA-(3K8ZrHFr>JoesIjsEXB&O9z#KiqsjwA z!AgoA3(540&0I*&r|+9Pt2xx$rWPNfV8#>z&8)^DDiy>xMQCj|SXOP{9qroiSRAEn zEKxq99q1Sjr(Jc+-ijgl?#z^oZ1fK(AMhrJL~0txadKd_ClOfpEq=9z4EB>xXc>Y6g$Z_NSDpCq~ZSHOBHSnRCxqUSuZvEu?sbd zBTrRCIpRf^r(xOj%e)geT}TKTc1LT4q|O?j=5GdO6t{M5kg`uUqi!#2cX$xGWtniTlmtuDfc&=}vwJ zA6r615TAt>=*7Hp^d5tUu~kdfj;`0%PY?H5+_f!bkjn6R!%j(f1q?n z2(o6tQ#ivwwTVo`hxP?rH?q)9;ZH+hhx&@6N#fF}pw=pGXUea!M97q>HP3$5YpmMU zAn&WII9A15udract~{YyYp6z%TDyH2_vgjs{nE{?-VG9{t@vwOxi9K$3t!3y^=wq> z6OEuUJ@h3EO01?S^`?Hyku`Hr!|%9@@hu0$R1L$ry5b9Ot=r;zLO(3&52;#GgSXw& z7POR;Izp-4YnT}E?9xq53@(kQ*9d`5mPlU}T6L4~G|-~8d%qn)%Y}MQFWCihEvB~^ zZTi}jDeATpu$c}I47GnwHf3KVFeQX!5cH)P4NXTux&7_ktv`FSxZghP=a9yTD#Z$A zhk_;JSY>p@eQ4#-prChI!=|)s?@FXsEqWDRZ)?J3wlTy5Vkh*_ zT(55MFET^2h!-bCkxiMSf?>leW#ZptCHmq!TiYs08Y~F+G6YKjFZ%6o@$#Enlw`As z#KtV$H+JP)&^^1K?Z?&&dTGuTeMv1B|H2+lCd`06r^qaeRbA@Ur~(;8& z^!`EyY=bGKR)nkH(U`S&^qM^@l+%w^LWKL%9(!u0{WE!iq#l)rj`{UJBJ2sD{xtFN zw-)>x-uiEK27~p<=kJgCCuAK48b7zMfZ^BG(hm3M=<`rSb9w}%t{;*4-b~&fsOwhM z$|nj?oWru@02oTrUwH7w4?0qwaXSs0!vX;niorpa72AGKdDGvpbB;BWd zRI#Aqr)QhYh9sA+)h>xEA~@Hjq?e#1bNea^`}CDiRr7sp8vn{3y?KDX{yq&7&_GTOh96wGB$$zi!S9 zZ4ix(u_3OsR^6+3c1QX%%4~gyV4qXDSW4xTo+ui#d^O59?a&D#6vX=ycnuP%UZoUo zGmsN)A#=GeTmzMsyJ|VK#F#I0drjbo#ZxyreeG5?$<&?jrHtrF|KTHGF5dN=d?)TMI0n==w?qf4ep>x;3BGX>ty??V&*tg6(YxX9v0t7%Ay&K< zO<<_}mRehWip{}|zv|+N9fKWtFcAqv+qhsP*|bC*(OTQNH=X_1(BJVu@NOv*p+)J4 z@3fp^*mGNU&yG|Qf3r0n2YaHC6E%ITu~jRenM0dasOLI|O+KZS=;Q~lTKQf4t|t## zRL{A(X2{{!ifbpJGAB*q3fVkkZl_1R>QtrOp@$1kuKpun#W*SRPMMfZQxnv-&zQ>f z4Qof%-Mjfg?96?1+?)Ogd0(o}(4a@}gabL`@-XhAjuT112@y~3TnGL^!Ee|7_9O4T zl-W`KD0}zFg@+KQ zMQfCDTJ7Bw>lmYSj}hl$=vjI4=Bb@?zUN5q$0JLEwMkvVkE%uEMAr5>S(1Z|{0lBP zDO@T_-Xkz-HH|mnGv2Rw1KsZM(F6(F$hoA6X1YAV*9hXTWe;@l@z_S{+G>eYc%X+L zR{P6}8P{E5YM04fJzcFvYz|YGb~6aQ^>uQh>heBoIH$WAO;DpVk4qS_Xc}z=q^i&Miy)3yxB{s9tT}td zH#Qt$)*}jEv9PBnA)Z3-Xk1FIV@pb|Q)qcw*3nwSFBm;_nFWdGq($v67?odi=k9^f z(saTUOlA(gegmE*ds|!~Q9Q6L;}cAA;_S;`JNrv&W7mBgEN9MGUR-m2W9DJqWyR;evhjE6nNJ}LwA zo$Lh#EH2MB3#eGU1MlnN6ca>UMo1OH;6)}$<_G>hibfAn;pQ?yetyz`Kvun7O>IxP z0aFQLatg&?cQz3V$%{yeY6mRL7b-N)QI-YJYTV?a7gkbzZ=KW7Q{Ilbk)-@DXnE0~ zur@!>%Ck?M!iXXw0Y3`O;0koHFwGJbGPtQuWx}XqrQ$TD3lOqCy)ZygN`}{BUu?^q ziJ!eFX1!xQeK2}I;MozwDUKT{i1$tEsr>jW4hX2R6O`sWS9<&F ztCdWfBJC0Uc}^20M+~JRPBk6`qV8W z)%#kP67e^R=Ltjb_V@8mpo4uz`g0N)3aU&(q4mZk)}s*7elhGt4!kwyHH`6WGIL4m`B(6bKr-kXUsm zD*jf@_CESk7~am-7kKX$KsDrL*q~UyV{ag{npWRjFR;X)ob3BViO@g^Cf#6RGV~`L z0av6GR-qWC>KNzLjy6^tJD&no)UUAQ{ zg85fGn8|Kc7bNOv$QWd@TX-aMzp{RLC7adCMceWRrj}o*ut4dUn=3v2RDCq=SGJtb z)7USfAg=cV6fp+udy6YSDvU`zE4boq6xgysJh&E64EdfeK1D*-oZ#+5nzYL-=jj$l z7G&bX>_$W!XRXJ_4-y%)JNOLcN#0$vqGvZ(@IYVEC{l@?I}h8zU(^1SBB8YNuJ=NAV_9IVX6 zXnbpiCRO(np95~XR?74;g6|pJ<%T%OIs#U>?&#`kCslVY-x+h$Oz44UbyG&sj^Qj8_IByi z@5F8}&flUvRPyTbJpx`D@ZHvsxDLra0zNNa=A4Sww><)O25-yPJs*JIMJOlQt|+}B zfxA^1$IGB}g)O0cftGGxP(c!rq*HL#)*)CC|M2*vwB*eJ#dogsPrk)R$06K7@y-$p zybWHq8`rawe3OrWiFXAHQbY28a*yKW<_xj@u03SM7KH6^$J zRiur=411&+9XJ)}Ojn^h-kbWh&{SxoO-p4w{rnX(8;B& zp$9W{7LGfTo>5EC6(qQM4O&E_p+r4sHYXfxkMu}1a$KbG8N{UE<04cpN$8L=)kgHX zz55b0BQbW3#lrn2>x9o9!_J7Uxm3{g_>^D3b)r?0$pq0tFJ$iH6vykIb96-J^{!Si zbZk7IH>z?kSRy*tKd%?gjT~88u$6@&c7$;`gCW%_riNSzFNtfVrBb#9_K7T87lB zB@4@Ss3lLckeJCwouS#3=w)-;bMGdmS-J@q%8#yGNuUW%4O9PMD;IDKLgU zg+=}!X9@xYT{IghVUdDs;9A2uF1-A8C55R4MoHdePRaCpk)KEKc0u#pB_1amgP99S zh3keMgCtH}fg^4(AWJ;SkZl}BM#V~lMfrosf}^t^Ug8|@xEwOzPxuf7YC(Ly!?BRm zlv{e^yI0~AjgGd6WqO`*Zmy;S}G^XS*Ah#HDiP&MD)2wj*eL-W5Ig4`NI3C~b@=n$s(m2Dq zUvI_-#&@+Bmo(K|``lrO#PP48>-$s;<9bX66iI3kCVLpUerYr>rjp@LB`ez&GrC33n*{k!b)eOq?Uis z>_TAGx$KY_!?6t2!JFM^i6e9>fPhwOikClyzDm#E^ZIyja`DZ}FdD*fVh9Ek=;&H& z39k{UZGORrXo&HkebF&Z`AjL~T_7}JH*d|9wQ)&rdXl;jd(Gj#Nq(KoGn!c6*cuae z7&Z1<@z;ndB~xzOimNmLRIeJLAyy;WH4bigwQ#8=m0Rnt9Y2 zF|KwJk-{Tjm8tmH4lI}Rj3Bug?@ z(R{~9U8~-=7mt9JUt)D*aA&pO+xRXr1BwrUYUjXQwJN!L%;6OEJp$C*#5WJmNOeTE zurIEj6{jU{+ZC895*^ue z0ZBW{Fo<)LrIF#fo1+P2gg91&t!oS~J#6#xc&#a?Y)GXU*E$W3jDqIjF@L z6kht1Ak!gD)P+@1L>)g6=Y`f{Kpt}Cz1X&RPfHg@KU88y`@^D6a~Ib-3NdS1N7zI$ z4dbD|ywa)&FYUADU7NwMu(;~`i+HZ&w-?;~MyS1RZ^;|Y+yl*-1!yemYbX%eG$*MB z7z$KbFVeUuq<}`PVU*6y#O8{HbN!)c6j}YLv6w{S4ar5bmA~GygnO=V-zM=udSA)s zf)!7(9T-*=TVUuJ=i6@RArtnzkpF;4LfJ^v(lf#3J-6n1Y7!ynM0$we1K$xmT9Tc)RAZ3JWm^Oj|2E z<0koC{bATRT0MwkIZO-z$o|}>G1+ltU&3wOF$Yk@G--Z!w*D#Eet4Qo{LA_Z@vnK7 z|4{b+LQ@<6-)0BDVH`f$-z5NXPqo^Yg!zI`O7~yOb1#wmKK)U@zd^2!n&T{i5o4b; z#D3dj{1&L~9b-cQ{2MmEH0$qGIk=!d%W?E3g3sbxQ{H@#`(uLmkB{7M3(~)o@?V8n zGPQp`(bEdx*Q$RTq`mBUX_c2Qu}l`!nur@yL2=Q5sI$EhGL@&XX@OcJA7wB8iZRB` znj*oCkZzJ^G+p(T)@A)vee6_B?NIWBMw-BGEty*_l-4D*=cjj(m9>eyX>4QHd>an% z?SW%Nj0U)d(CKB(dfL=@2wp6VIpzg}zY%#zsH2b~X=#eDnu*LfEb%Qin1n~(Rzp0G z_JWF&`CCFkBj3(m)zIogjjn8S5B$x?<`@$WTILU6D0bu0%$mu1X&Es;@dS6okB^e( z@>6EVug|BkD^s&>TGUV3xlX5aY<$S-h`Vi51qYp=iq{H?-IXmZ(|P)oH3hZS z;WI~U^FpOR>XiCC7~Kz+_}fJr*vo{IwLZlzF0da2V4Rkg>6+6zmT!0((HSnsO^ZQ; zW(->H>#uJ1Qm%r-T|W1Lp-uiQz~n)z=IQPl9I2ks2;Xc|7#3N52SWbMl$rGNa#A~& zHS9LoaXhFZAEWO1d%Kat6!QiHs8DG61 zy}UoQYgXK&t*{?^FOpyAxwEJ#iPULCj8VLdjq&yZFAe)_NG@O#4WoFzO%Rn_9$zJq zOfh;?PYr5NAo#s5rVs_-hu+V{3ZQ2&8AZWJCDeGqTHYWGWvV1>RrDN~RdC|80;0m_ z(a=bbp?pkut}jnvEU9Z=$$NeFBY42ekd361!H1}W6BsO zXmBEnzaZV0bU$rBr?!1aFG(Qf7Xi39&tAZfH0Oyo4%tH@T}T&!#4>9{e?g94LU}6} zD1qi^yw61($6>mo+hy+%%(nlT)!+3I5KDJj2j;ns9c#&K3<~pg_wI2z;6+gFNmO9{Tw_68QJvJ zAHt+On83(^O5&8MWIDNs6A`Z?>vAZah(21N#*0hPqtBC%8n?hBd9`fwO=17A#DAWw zy-s#OnbC9&)6zSby>hB*$;I1O9>Ws}XS%)Zo^0w<)w}@xeAzJ%w>jM-al$`+_%-Fe zR_EEp$XPt8n`v4w>7n2dq?0TgeCWzrNPHxF#xU?;Dp$ogZQ}3v(h~+QqPWl_2FPI1TeyD&; zT@G^`hO7x&t*wik4~0P^yt5K_8f+Q?#uSS54sjmdOxrF~pt8jQZl*e0mHI1N_23$| z)A=feK4V)fteE$+DQ@NOETl~Y%9nImJ-C?^swNMlt`EBwdWooKVWu7BCg5*1-(;UB zZfVw>eGBO-oo`&yw_fP2tzgZ_aD;?zON=eZ3YL~mm8jcjN7n4S7=npL9s%qciwkwj zuW54uLS)PQr?c4&;bHb?QwG}uvw8S-&>OPJ3~f)h=Bu3W7UBcs5pVFb)(djnQc6^u z*>wT#HyS7({Z3Vhr#7JObW~=RE_qbA?}$#ZKZso2LVJ4ccO)$~d$WG3vgB!oT;`pVM{KUTN0BSZrbanRDALTG}X@wmo&2%qlduXkU(Mmi5Lddr!$LtPin4e#&@7%K+ zW-hugG!99oMpyMh3FdCK%xHDzK+^59+jl&HY#J7pm)p1eIgUgd)~|}xqqMC)BCII4 zG6Xg79|9LtcFIpqUw zqb4vZmz#|93a=ux4Dz-iz+zD}rau^nzN;by6vcCU3;E(r$E`Rg&AmT6sdc5gMRGpnf$HfGaD`mLPuI=cozS1D- zJ?0qe+;!MRn*_fU*uQ6#e>O3C0a5OfX0^Jl zE}2F`VtPfnYK{pJ)t11#YcH2t{hCbA5N^5`O;6sS-Ro=Sj~q_)gI5p5E2A~vX!AcK zzZde)Eh5{9{DSVN7^d|8VB2*ey?4`2{c9bXt(th~@Q*`ly6QLc8u)Cr)464SX1ULD zk}j~Lz&RSBk%GQ+8WWx$l~UOci#qD*^peYW2qz>Q9y9_r;v*g&)*l1ae4T{a2R6`j3P1 zKcpGh|I2C}M*X|;rA(8~XM@4CAsp|*zOi&^JK44Rs(0pBG zB02__%6mr}S($IKyQkkS%X7;(6Tf(In=(i9g22%=J!$RjlRzy_Eb1r+)vHb*=ySO#aT~G} zfr&tc29C)rT4o!eL-34_>~3dovqMd53ly#OLS{0_-5p|>?)6T09=fdA2G5YY7`q73 zmLOVA!)GR3%=H5l$jb+>vbF5!EC&lUe{;tcCBNyu>AxrB!Gzd90x0daupA;W?5#Ck zI7hf{Iyg7nw#jf~? zUanI#)I9=Td!@081i$WU4j68XKBNmPc!@kus1Yu#D=G)}fO+ zqgjmCmyY-9UF5F_oTMITPGT9ZgNJfV6pbbg^2=uDO4x0R2tn@_H?vWf86iIF%t5vM zE^1aae!iqYGrG6Fv0wG=PM#Hs3>=3r&zu3MQpJg~;KcLYW zKr3389~wVzrU3>{HB$gM4^G}a%sXs`56dSkJ_2rMJ%SIEaTMe=k{71PXBz0i9YYa{ z1g};z$QS*5J~~fk*CNQOe}+iEVF_P}{}xtxn4;~tr83C49FF9Puz-w4r9?lMU`!LLwNx{f^PeQn(3TQeiO39Vo}haj0){ z*0KxEl98=Ch&i>{;2?wECiow7=Jx)~KrKm(cUH%VQ&koIDO$3!zYkU&CprN(xxx3W zQV)4eG#Rg^3293j6yAsRmiIW?qzjn3pk8FeE}JtTB3mWmV7kJ`y>^8q%Gh=WZ?Rv!bbwZbD}XE z&vKe18YB*6sZE?*{&tOIa#T8k#^TeXu^$V(bHQ+mGGe(itv3^x?N#{F%DSfPPh<)NuyyBp6r-2DJtt7TlfP43svX9BB(uq!HR@G>Yitwk#A_oa!N zo<-OTM|Wv?^%HpYrdHb?;LF_-wCS6y?+KJIWBUDHNr|JOZd&$H0fi7HPqh`>z zP8Ee0d>&ut-AJis{Xmb&igz39fbvQPp?-`$s2YU!LV*pwpJ zA$qYVEd^b8)EefX7oXzl>d?Z6JuLA9g#~9hEgb1PcWA1OjvfL2ntH~Kj!8|p`Pj4tjin zvicN*_H0vHqTFOm(K>162-z+8rw$7eX2vbDow3u489&RFS__YHKislhElDmXT<7~d z!?#1-ZZYgMUy)P#kvy1^a3qYP7TsND3lfB=N#wFsME%Y<#uw$C$Rom@`~CKo_-p8( zHkk`U)_%Y!mb(<=DN*^y==b-O-v3zl{g2NC0*%wylmFU3gML;2!OT8oMO3g)IQ2it z&(k8;H#%p(YSZA9;{#(4fp81cAj~hPVBl-JOG=I*5AQP+$3c~S_>KX)S=|T7uR)-G*>!SVTrTjNdqUj zQ=iid(ol_}#K~FSchkwxC%l6tm}MRFLz7_iFj=)IJa(b4*}loF`|Z#1v?2ZXz{Z~n z7^>0)4B?;r@xQXg-!NVP{~t^7U*Fo_46J|K{XSV-#~~FPMzaj`Iv}yA zFSd~s;;_x{TJXqWY(B;BF`b$-)~oil zyY@mtzz$*O_$Z1O@-lYr*)E*KbTlwm8n-vdYJJq*r?xfLy1$veZeO>{rtY3dzrnAE zvbo9tGQsOJD_oM7x5sGM>G<`Rh=`?Y@J``)1dyy=7-tZzIm{NEX$p4N_zh6Kd21<5 zt%^QSe;xmMiW<{_SsC{WF36&e86=+X3t?3#@~=gRQ6i{$d8W}xNybWx?3+~IggV?C z-_)aPV$w4&X~S)l@Gk1dyyhH#QGu|+ne@ea^tod6BS1cw>{NBa<=Z{1H3J$r%uD}* zT+736~W=&&m$5>FzXHjp7C-Ynm4`v}3xp zL`##s(Aq;rYIqq{IjzNEa)=ps`s%9VWpTpswuzn6`v?>MSTaHT!{h51h~?np%A8wtO;DiBiKSf(Y$h}^2g$? zqBaSm6bS5+2Bcqb^F&JUlheL@B`KE)Q>>TB6|i1@txSyBEgs}UwG0=D-u`|J-dBGp z4m)>9e}ok_obV~%ns}!+3X@4FF*RRF`jlj8AjTpavl5%DSuG&Gj;_jtnH;vTY9b1l zl0&FhEeO$o4zlYPDfsbd3jB=8VhGk5QbyXc?$iu=sg#U=i{-3S38zR2)ie0^%B*+q z7=Ya%%;I=F$&(K8DygY%{8uM2a*1mz@wFRD&CkM z_=ZIaxxh4yk1xWg#mTS8?NNcq4I4QNzU~zcY!&KTKDrhwPfTW)-h>8U{5ES)Nu^!K9+~E;G=I*VK zho)1}8WEUS&BJw*x9m!oR?i;vJ%g;5^N4Mm%_BT?wdgRIK&eGr#ny}rRaU+F?}O7$t!_hIT;$wd z(!X!rmTxjtSZZ2mP?J5>W3J3q@&SJwW}_A9ow6a$Z<$OQz2P{wsXVY{>@zlM>RMPM z4Q0M;tVaE@$i{{gIHVI8_OoO{oG;|?!yWK=Tiw&LD?RJyH;i3Ppy8FJ9sd0mFM}m_ z5>vIrXYES3bvZ)B6P3eLcESZqCxaN-at|`1moklbkAR@n5hs71ZpP+S`u2!wi26RT z$C}el^(|*>r=@FzNwzG$~pCY zgjZW9?DQ>-K+s}K4aTP#A8`PCP*Cxk>#JESNN9?xwna-8jIo z{pYx994z(;BM}oFOWP7$&OT$aX0>a^%GnvzkwOBa`8rDJGbWD!L_=P4hZJ2BAPfU#l$mvpL*ISrMZ-87E}>ijy)sidJb_ z-gBhqGuRy2*r$W9;0JN>@o8yWNn%&rSm8?9%rU31OkC*e$7%RL3r4sPp~)ml#K5kB>26BbmZlzl#PShH)uD}pPbIr0H`6lFuo zu-8zKa2y06E0B+}t15f;ul=$tg6;s`48`LTUSeJa{fLB;FLwFh4Ba_gO@-c^X4?pL zQXm6)SQU#(%;ARe1H1}_v!5l)nyJ4Rq$7e4mEcdLCqz_v*(=z-lKm`8|D=%LkqZUC4*tX> z2n&m!y2?A2pTtF%^nw1}Nn}_%Omtlxi|OL=;Ul1J$b`K2@SD1io)sQ1zAstfO~(70 z#j-OK9^~sm{Bqis3mm)yuk)G+>QtGDfmz}nW3Px?g%(kf;g(RY0o0JUcl%U4ZO9xTeLEz5~5eba5jbnQA@4 zZa{XH#ph)>#NlEln7Fb|$6Fa2yP~N8-t%y)mQs4L?Gu-6O|Nn92~IlL(P{G0*0wmA z{8ZDSTp8-JsSksD3;uPBw$}Hyy#~Tfx^SKsW@6cWJi;lBwa|q(W|@&KX5j^M-#$BA zKbvpPU;8R@ydfj8aEaS;3^q)Q9&B)KE+6VK0(QkPe|Uxu)fjLAc;!{4G#264x|{DY z?^>Epd+c(bLvLhGM+qHN>5f`_8(cY3>~J(qje$tqXsLcxqiWw5lC2rYQ0_%{BVrJO z;Xk|@kgw&)!VlTgp1OK7b%nPPewx1iS$O@k$RZAX8?-sL-hn&JR=0{Vs=vIN<3xAl zqUQxNs%OB(tV~#zg)$qD`qy+;5Ep1YBGjbfch;HHn7u1bi2dSMvMQeRlK)S#hx$Lv z=%2DS^}@f_UaJ2=!2N4viOPrqAo0w9EDx6RDgDD%{9W$DXa4(7v2hehjQ$>SBE&wE zS~0943f=dXNPV#E8MO` z*^3-JcjSA2{RkLj>FReOZB3k=y&0x5pGcS7!H7OJC#y;m3n+v$kOEZVWd8Gw{pM@q z0a5_re>1lKDtA`>iv#q#8{ss#AtCU;({6Ae`SXdh0(zleL;I1+FJOhyp z1^Ywh&4l2D+Os1YnO(g5Vvw>VzQ#M?!IoZxOOK&p@L{?zw-4|0;>nb4^=ga8^wfJ6 zj;PKls0kmIeAG`(D^Stb&>OdqNS8y*ERyRdnde4JP^v5^+n^yx_N zMcj(t#vK)v1QD#{7I&pj}aPj0>JNPF7i6d7sVXpgY4^alzlHUsLZoGLXa@d z5_4>mz>byEkAM#ezWj@x_a&OqbqY%K$ln4_#kDQmZxaOvawT57$JN&O{Q9XVh3lhJ zs+4KBxZ<-F=;s6F-d}AB8EG)G zcAzoyZdBlrX3|KSj5u-7JW6XIP-RSuu=LmWfc{vO{X!wKcO!Wfw#%XG@DuILiFvQ- z_3OCo)i7F>o{SJJHH>2zvN|?y9`z=3){qX15b})u&2pZbrQW?WMvPIbGx)~SA=}C+);Q0 zxL0GPuhF^!tI+}fkFvLnYHRJZ@fI&y+@-X*yF+nzcUp?OI}|5C@FZBE z#oavtiff>_!@JwP@B7)$_l@_*`)93{WF#5MNXB(sbDr}&QLa+g6Ac45D?C`%*Lj^K zVD8qQxx9%nMP<&R*C9u~>=(G}{4%LLum=D)q&o$l=w0xbyMe7XKaQ-Z^O~bU-Jqk} z_13DvY*6>ys`s-ka)3C**J!5;=7oG`mVh@gD!e9jCU;!DkK6QK4#S~H zPd+fDDIuA?0e~s(bfVUFu#h@mE<-BH@}z-R?hRLU*QcB1#cMmicaV03-G}%4dLW~z zRi#(I;7$m8`8*nj7VV=S(Db_>@DpiZelj^%^%vw~iitOnk#puyX*iZ=V-VfW&b{m$ zr97r0f`k@bZSVn5L8nkUgUpU~bQWa7m1{k7Hi%NI5=<-h%TV<&+@B&|yjtf$4_w+0q zJ@^`I1a{Vo9e5`er4%0kN=xF%K>^#3&DZS;{$T@~yQf4cof|a>*I4*mOm$1tA#G6w`~rRv`}O@K19;P>ogqpQw6w{$)r$r! zBAl~eK{h<{!Ts8QK`)E{P4keES?*v~+P!5R2^h>Y{vm(0c&eM1IIKI`L*#Sq@Jch*( zz~#b~venNrNaa2>+T^P{Mw2j-F!gwNJw<&I4LHH{$sGwG*$&xuV>TE@lc`EBUv{u) zsVQf+6n|2r>}6A}z9qX?8^1Si@wID<1E9wgdX+6B4NpYOS zcjja)p_H^8zULcr&m1mTN>sZRHgyc=?5zYd4+FnUZ9rCe#VKX*7;WW5+nBi`DuHq7-|$?} zqfLJut*F*{0PK3LvYM_3mTHw_Gp~~O#?IYmTvR!V4*H5*%@~3Dc1!0wau@g1Y|7$}X6Uj|Y(QQ9TL<5%c1eZSmyFt`VTT3$aFDuJYa4W46? zwU%%sQn!Ljnq)j>8P}Ii0Jvcq|B;>li>y#k1`;hE#s7cE3h-~~`G4BB|D}NcIs8ev z9r$nmGzrD{4^$bua}fT=V+v*5LH>7>R^-PeQ8<*lxQLqE>Q#T1EDujZ0D!Fnkw(k1 zsI=XS7mSrj?Zf9th%9UgWfDZUZsD4;Mz|U3F$vd?NyWu5I>^u2I_-$aJ^oOx{S(c4 z+A2CrgK=a+F)Tg%>ebWB4BKAM9`P-A2`EPFeld||!_V*z7 zMBi6bZYtwHrN)@Q`^P8*VnP2)7Kp0xSX&3J`%9M@+qc%fAEwNgx7Jqet+r&QU;D%y zU4y=rg945t#=xlKK1YT_ZR>{8o%R)S)~xn7c_Lah>Qc7oz0`~Q07h>ub(Y!I<>~Cr z0~R(H-`v4hay6IoUu#!o0F?0*n|q2d#`5-JiHiarHOYN{F6KPfQK5>kAivIpHTyZ~ zJ;OnZK-qa8cSv4Qt@Z9Gr@i&D-WJ04g0-O?Z?IK?_XI6ei_Y&6mDQU>fe;C7Ddk!< zEGxqnyVN=b9JT7YsGNkUDK=W>#>TNPYqN6=4fYMvVyiofB`GA#GFAo>{f~2wWq`l# z%3F42hR49w?g`7skudapjXKHTe#J_1>4JG;^#Mx^NtuB-?nf^%-@GPSD<-tj&L`tS z5vx^IrxUQMfM{Y8pt~2<#A-fIa_$;8>K0dJR*#(ta@#Ffe~Pqc@Y6)W108Vv(&k8b z^%1k2Nq3sj(u7VAx1?C2P8kf6uEZ|wVn7$GP?Xp}RqVy3!NK9iNK}^4sRf|n7?z_Y z$F4G7-ece=@bwg(===p-nyPH?&k3IN$mz=DQy0u^i6dTT(&_f{skJmYw zO+%3Fp0ayut7Qf`nj!mqs*;AypRlYCN?kTa)_}9p`Af}oAGPO6=sX<VC1#O(q9T^s8>*N~q;FMk^FqnkaS5?Sywt2atu1|{MR#p>yhOcR0 z5H4Cwzt}7z8Z5+dHrck%A3R=mK4XwcTN_{-D3y>30GvMwf`y?pzq-+k zX}F6aqtHuLRLIO>Ng^fHMiVoLD+dditiugt&p9U8Qr}|giv4;#h$uH3o>Y^FLXJxM zc5fR|;)}%S-LTH}4u>T=W-#qkq)!M+_(z3pZae_4p7|8sYv+AjHCy6^7;ReYFApw; z!%G_2>Wu^8xJ+SIv%bK!Tsb1+u+7^d4mgjDOA|eU|E|zz=@Aoh+Jl~6mcm0tLzw#K zP?xigqYI8zq}->@jUyICdur?n%UaUMJB6n;XU;u(lo>Exhn~SfVy}{h)SJtee7ug? zH1=rrSuq^l2Y>)DV)JPSi&tW>V*yCmA%=|kB7rojv!Mhy0~ENh-G3KXp;U-7o)ipY zI(Yyv+F{)}id@3^)jz zvYH+eXTC{Sl(?kW-W(YLB5vQJLRT+w%z2{lA&)sGVp@>-pq$W2HBNq;32o}m-Xq0@IpIU zkB-2*p}|??4C)sWf{)WF77_jG5loA}89w`0OS!mtbDc{jL6JmJe~* zm?!W#WqzQ(ND55df@gjHuDVC(7>SH9Ex)p>TJz5?hV@pEzI7L}f?pBDQjP30FGer4vavqRT{my zm`hufnwwY?WAyP#Ic^2Tc6k&5-`)T*w3hsPpWN+vticW(*cEe_KsrA~OUO#(ynLiL zhWm41O+%JP7sv29GjrEsKc`o(KG^K$AxYTWH?c)#csvGeBEzpd==p`Rxn0&Oa{?Ul z9?P>hWn~`!xWKa?Jf3M9dBL$@IH0B=yrIhvyL|a#d^KER# zoRU``R$r#{`m3J=JmyZK7A#5wm&(o_>P{aqalMgnAQ{=pQFuAsW1Vd!{{%dYwj@F> zY6B)Mw*H`MLl4lM;QFOUEs(ClxNqw#8EJ7saS$ZfSCVVwG9(bHqsk5gW6d;IIA!P* z7{(l9`k3jyj+atLZH*RawB0Pqs8Xg0e?3+?Ra%=vA(&PAC0Se942ChU#%0zN)A_AT z2Z$BDR=Xr87}_I9zMd7@<9s}N5r~T5VgKjMq29Bw%dMxLk%yHNpyoG_7T(h%<|ltZ z)o^ZAQZg)vhq=x>L|MapXrYdWb9!*EexD*rK$wz)-TwL}U?UGVZX*ND?#&9>n=q>+ z76MV6pU~t?fz&ugU*+znidGa7X1Fx=&pUUnkaD7#3((XyAsSc=5PDRuuJXL^> z1|8c7Z8w)DbTx@t@sBv{8ax9oS-`ndaRR!d(MK!!J(-dIyF_Ud4(3jcjDmko5sZy9ev)dijk#$d8n@I!GJzcZ8 z+;BmZNn{pl_cDIh7j}et)exJVgiX-Z8u#hct!ZMS?WYf|}QWjuX z=lf~0j7SY(#@osRFl<8Zus z{KyJI?1ELV2{^9lCanG4N8}pqz|Uc}eOaV|f6vOXWslDi(I_vROM0p2;vc-#`?zYq zipHKoO6_NJD!+uoOB=S?m-nLHk_WW&ia_hT&9;{}3T9`#VZkvMia2;bvh>&og@jq4 z4)D>XF0}Cm+hAQY6wvE2^|NX3a069`s@w-a4bn@J2A+X+5xKmU7GG)f+Olo*Xu>JVF)n4lSEwq*x>wr_yQT%n!l&68T$3m4e zya#s+?=Lp#j3n0tq;BH>LJvN&%v-~W??|8(HPGP0mi3*)cw~?V&ezjp{p_v$rM2e5 zZQijS0W#0AIBy5N9`H=UajMZ-jIiOjEY1%Xq2&j_&9m#md+9udRRVV4PTEyQ!28xk z@6TNZg!m5t6iUYjz%vvl5MEnzCS~FRuQ&NAviBXO5JLE<@eD6)R`+s@wW}yoNU^uA zWYU?dgr&aq5WVxH)an6Hi(0OAKuhS(!h%yIDkc&p{LUx#)VW(fW`LAJxIb+$BhQ&V zRr?1w^4qqXk%)^T=pv<_lip$XtA<;3m;d|w6vMduQ>I-?`W}695vhQ+cf9y^gW87< zz7GJy0sfqZz_3VYL;|U*)~AI;o9N|;elvpsO^ZTs=>+&HFGmu`ag(PF`Q0wMN5D?= z5U!UxGfVq5lb6Fvrtw>a^p3{4e#=n{v0+ptz5V#LmSyF3FJYo#sDH@y5`6YmV17G@ zr;U>|e+c=C2K|<|w&9+s%iH7u@Bmn6slJ!K9VTnOrX_$o1X?cnWf=1W=nDD?$=mxL zov&m0+(JT5^|W4Q`EMmRw-p5Z_O5Y<>mBc#u{cj8wC-Fmx712DlkD|1$~ZS}Gdt=m zGP!6*QzcK&f=ojz3Ofw~m~1c1?={u(v>yOIqMK51%ChXdEBYDHDgCV|c1_}CajzRY zOa|R~S`Xs!8lK!St>OB&D?P$3dEMe+3zs2n-D=~!hl(GC)vqJg)(}L?EfsTFCN*Yr-dHkSe7CN}7W>S{- zYNplCH&PvWRyee*7j=>`52*`?zH}80x%x88o0GOK2;Jygn^pN)`Bp}zYFf3v{-agQ zaQ+o+ipKiGIZ)gpDxSmh`LEEMGyHxpY}&Pq+L@M8r;9AiY|tM2IWu06Ose(cV97Q) zKSx_&>G7Gcdcy-i$#M#lAiNEc^#I6eq{M>E+g<~U;{5C6qMQU&K=t;*U|P3h$JKE! z8;#QjEpU{;o%#iZzKbW811s@TS{A+2){$WO8pTXfYL`LQpl9oBqvsQ-m}F@46-(Ci zbGj(vghnom0E3rPF%B&Cs>0+e8ZnheV~I%^p4V4NA=zK)7s7-ie)j_f(GlVB@Dlh1;OAY z3C6!Kzt+Z_2?TVvmDsplS>EJ*AqJxCU&j5oWS+vkAziQ6WN^Fm8(qA!(M?VJ@D(Tj z^|pap*={AUpitm8%{wG#`v((~(k7*q-s(Wn<1#9kdUX8L*JJs_?>X?Z6r?1$29Bd7 zdd-Oi134M;X@7BpX23uVv-={)FJoyjaps-6E5IjFSlcoza+?d&O+vn}8^VX(F(Yz7 z1ch&PL8u9Ft~2}g@CBT%aV3jLWL9X_BBsXh>l}i@Jdsjy35BW1Uj z)vKTd+9xVu;?NfvW58EW&HgO(KO=9(2Fy5~?$*eb{<-V_1+K_|Q{Tk|Uh`=q%$TRX zYXcLMQ^aV+bpEuq{xz40`#WEq7NCs+P?P_zgU61UtF}(U01TN=xVLC{H);gg9MptGO|Hbb?OH9!=BAc)sMp{R|E~vZ0CDH!) zI*cbLV55alJ=2WSxEU_hb9f!Z*J5KwsnKXxG93;P9*lL%kYR97<&AVd=c}Gso_L^~E&lfhmj%>} z@hSgr{=`$4Mn$E?|MVaJ{BKI><^P@!Kzn+9;j53vyWM)#vE%1h= zY+-~Pksbry8JtSy^&E|2YSN49U9BB@0JQt{>(3@BJqeq3aanYsiki`y7@*|0$gf)< zW9vyZts_s1ypz=DQmI=uqr47loN@Q%zFDfyff+?ACkctz1O=0 zp{@uO4gpkdR8;rM{yr}5V{HrK#Pg^Jz$uS(CjO$BRpV7)G!!l0&Ma5q#~A*ak^pdC z_lai%C8jjFPuVCiSu3A@;{m|tQ_WgJ`~miRT=7amui=#Ig`D+YU`&S#X(of7@GjL{ z6X$a^q~eN=4BIQ!+A=06DuavT;m-1lnnE#%9k#roU;Ys!T)$#$mWruk+Q&F~`gg|LX0`Jp9f9$XL z8k+L$j`@N6GmYy7)|VlHIa=+Pa(^v7;c!w?!gM5!s}&YwW7-S(L+xNQ%B@ZFL~2>VKG>ADS%nUalzL-DD*%eB8 zL`37f0m@xm;#Gu32|sZs}a#89fO=M(`%jac$_VBZ*#u~sQ&gzEwKGFy_M z-*6eCL*{&7`D)eA$59J2XBt-OR8hRBSKM`Zt0SXyQf(b_S$NkMm_nhJa#yX#;%pN_Qw8}cjQ%5SZK^4ldbcnOxkkUImbtf)>;EH4}c_Mefb^X${k(ZJd->bJFD7j$7_o; zXw60~Wx2P2dOzox=qJoB10$)1b<5HFy=h8t2I($SUxoPKDN!Bm z&3Bw8OSZq7CM zMs%OoVSe_FL$P4QrrTj&-b(-Iv5-F7I!!d7^jD~7Fh=7#xEyp^}f_g#bapacId;A7hqQz#kDk2<_np<&# zszs-k@wr8ZpyvT&c@x!-4|3w->{Ay9w;V-`tnT1Ogdw2P1ija1Tn*mQb90;TQfIye zbej}w_Gd{An+rNH)BA_ap-Oq;XZMpHxJtiOBVg@^hS3{|?fK0zA21IW6*ep=q%`}P z=Pk0Zy^b@PHxe@=wOZr^q;%h9a`&p8S55ek_32H84)S#tiL#Z?uGX#AMDw;HgXw7t zdzCDWw$;F%s;nK%;sZ4}l-QBi=GJ<&edo$RV9LfL%sI)z(r`U|?#8j8%`h~+kx0t1HhdZ ztI|XC2H0qCTKfQ4R9LfpeOtJ7$#T#5W~DwZc_YOc2P<*nDN1_$IZbzz8nq6)R)Y`0V}P(i|-C1%wAT0duy39+1h zi~}3sF_6~RCG`8IY&~1P{c%$cC?6OJf1yIhoTaluLE90@OB^BHc*UVh<|Sv*x_wzG8jMrp26N0&HGHEN>#N1c>J;+yCHCKZ%t z@_%ca{slpQUHwUCenj>8cWLCG^2yUjeV72WhvOR5unO4NV6QhO6}d6*Z|5BTIRJvMf!HSAP%RZClp zJ)HMe2ByYtM0ahE@O?Bl>}hg+hV_Z{=$v8Y3c{H?&!yeIzFAnn-L{A0YfsEE-$t=i zB}a3rFZ(|&#(&B|e9O|&wTs8hAq`aMW$t_?E*wLBneZG&!npFZC{D-t%aR?^nP za}?*wpg)sii!>76V(E##wtT+6AUxlHM4PKdY%lWYyPi4Kh^#JNy>0FX02Wpaz} z=94reiJ3HV$wr8ud;M@Mrf(0=h zGyzcLJ;m6F5+P{z$VkpEQs=S86Qm|xT(|-aQr{@Z=I4O0V{$_euV2o=jhN2RPq*qX zUglN6ujJw#2Mr9jL`hwKE!-?{>E`raS=`a&-&fbnP-^h)$rF|1sLbgJzBe`xS<3n{ zGBAccHV1Nn;_?Ldg+6NL%bbsEC{4U?dWF?oA@NN3SrFEfNOvaAOiJ3!bn3Ay7L!sI zYZ?r9ZGV&$h=2S2=l-USpcdU710qtkqco&=Sw2FCYiLVQ_FmUpsirkITX54vO8~P~!Pt}+zaI0m8_5AE5V0RM! z0xK+E(QhvIVbWZX04F^FruLvS$pqE4wo4KF7S}#wHK#WbiveHOx4e$)kb;XDrtV(O zl+S-Udz18lm9~HLf+C zYfz6FuMDV3jZ0lS<~RnQ_U9}n3b$Xg8uZ(&KE3)fA1@?Rn%31CC+d@NawpJrHlMAz zFJX9jOOrR}XS3x%-8y=ZvFhyRNt)&&LwsU;tjZUF=DRPW(%pX77??uIzrifGYnUo8 z@zTN0qVT=0RWtUjQf{SORx@7JhMl%u{&A=*rLR>Jw5XV`T4u*9nkhw>;^^81+T46i zvtPy4vtGrX5py-fo1~}U_$FpkH2p*Yg~qD&BK`2~M*nqM6OF(4a2Tl@8)y_7s1%z@ z5i1O`_5Y#TlgXEzOcMO=2OMOlC9yyhKc<1)$+ejZC}gl3E}>h?8MoU~4tM}`JpkN- ze>Tp;h{vH*H{{nQdJ>MOKTlI|tq$TGduHQI%@e%fgO_qMqiiYkw;i@O46QH+ZDjOl zp{OcppohH}u!FEr9S4+R7r#%`)&8PQJ*? zIV@r6O;}mX+sT`Du$`=W?78x3gU+N~A=S6dj-_Y0j>i+7Ym8TAwzJ?`QIqCPlAtV7 zHFo!02B}(-C;EPmWpzZkYQgq=qI5!OrrO2$N^P5RG`lpsbP#b{3ORUxaz&i}sT8r= z(muKGe$}UczbO1GVYT%K|GkIiO(pc6`-1YNS4qNSGyyKwDA)_w;0vH`_>9%9VD?*G z?{Pc%*`LxqWL4wW25k#5oUaZknR-|s0C;TWfrz{-9r1giwj5C#%1x=MyxIG0W0-=o z{CHD=-z>(@?Q^~tKlP5crJDTm*y(-R7q2KykJsT{rRSyh7S>w%H#wld)2alE`}d(J)!=qKCo(o5NMvf;y<9iPH`b+23{?1TX53HL!~I3LvAM`gspQ1p!H z>)aLQtD?v)yZ5h%(o_;{7#Jj^VzeZOHb_%4E0omlCx7Te5t`m`hacd8jNHh4=wEw# z@`fzYG^h60s+RogGv6}fXe~VT51_IdFhLlax8B%^l?KS(ZA@)Zq7}%bSn7d~V0ei< zqh6^4Ta#kylI@aXO69m2FuBUS+%Sh|7FZ6JZ3_`zIOBdLG3!zZ^Gv$9tN38vWt_7| zblB{*9dO3y{)8`)15hYk0CSMA?uibb+j@klgn*KmztFGk6o0uoa6nVP<}OnTv*+Rx zyHdJ8Tzy%tX@>qTcp}$WffJR!=B`{UZ0?W-UH$jEL+(qu4td45|MV$NL~+*?@NKO) zUSv@2L2ftgy9BzGg;l~+9Vv|@n))Da>tAZoWg3b3%$w(DHyBG9VUp4?*lOVk8ecPk z4l9&o1GNt)HV@y&E2NHtd*7^O;pt_=F{1{R{tRcYX>7xn)k=TFrgX3MW#-KJly`Z( zLal1BJAdI47g|ruWc~$9Lc|wr5=khNxyPME(NMyY|F_*z=W#q2iNO|B)z;+>`2=|x z`YsKPTDI~~dRp-x@n0V8bZrg-l{__Si6uTzj4uCBEbdeSi`#vTKm_-@-tLvo<)cb6 zd?TdTO$4bg8ek?7Gx?%QRTUm;9FmwtFn3|ctd>flnG7}HnYZa$Rg%*5Sps$5eIfq0 zqU+I%jDHGizwIu_{#Jp%d5iz_X_Ool?!PqmlG~Uql)X1wY%%xjQ45|Im(8i!Up4F6 zaK+HIxYY9X_gbrjXk}#R-1eP$4;afJc1l>zOzg8W@HF3*=rJ8w9JFTuy9&LLhvX9Q zVP!vBs3hT~U#QatMFu-!jXwasf)jEU!Q9(gAselW9_x@^Y3l7u5e-Ng!rfx#I%sY-yj1$|eLt1+v<{a1n(LO^ zsKA*6+D)15tte>Tqg)i;kQp)&J@OpX@cwlOOgcuf;x&uK|FlK_CAu31eEcorM|=E+ z;n6?2!Dodrmm5&1<}7EtarBo>jI$?gawnc|dBJ)0d}cW~->Su1a2ybA9lLx|2Q4<% zt6WB0o9pL!@7Gq#eSy7#vJ0#CPw77q_Pjru{9c{5sZbGSOJ%PoV$ufDLq~TUeRb1H zJD0e-LyVZ?HI?gG{v(85%Mkp1r;qeXK zqi$q<_EFzS@@U7JG7G?#Jfmdm*94Uw6a7ozO*|=lQ(wftu0{4UD}Ve^}6Jl(qX^R z)Dt2Dr6T8CBZD4+4N|6D%N2eR`HuwxmMPC%p0i;i4Qp5JL-sH-`=0PFW$bBb1hYn{Th} zLrF7#OZUOfk|X_j5i7L4Ym|mag%9^b`mSc!X!)J!=Zxo3>`fGj8lr4lCbGk`=+iH& z%IBr`A%!UNXiu|+5DgRmH%iC!wbY-zXAUBM~(aw8TQX`^M9P_|5d?7`!x6Q z(Py(@OS5+$vA)rqsn`wh>B+>obZ+C-wHlA|k8rIz$gSOh<06^bR85(WuL?x{$s5Uw zD?OF1C!!J(ND#C6Gu?3$#;=@NLcVfEkbNhW_42+ef6<2+OlU}CoKYEcJT9^Im1adp zxDgy(Fc?^;a09^sp)8!lMtTThY*tf3qsw5dsy@M~+cU_UinrH^LS{rb*aBAx8lD*H^^}f7z%;^=UE4jYNhS0rlZTo*qe*l@ zC7=J=Wtb=F%;%*ui+nAd8oYE|L|&CyFHbukRG00{-_kw_*yYJ_zpGG>NPI`+*QutA z5zCg*IGmd)uocBbD*E>j9rfi0aMJvx?>fhXDs^Y{4C)_^Ise(~a* zvREoq9L}FL>pWEEHGrq6Ni!AtIWlEq=`le&cL}RtMfH!{O7PCohD*?k1cyZ!Mzjja zIaFM0cI|c`B^x$yZz-T%U4Y-8gHPS4y?S zrlt*K8x_*|@ye9G;>Q<<)>EgNH6;5y95)^&M?96iBEQf*cAwHj?yWZxIY3#kli9bM z3463$*B0wIn%Gq_V_K9N@?4eI{kTZoPL7UZ+v5lxB2U{m0OUvu;+lD6# zK5nR|C#q}ErI6t`4aEibz_Oi6kxY^wJ9=&_db?3!jyy|r2exuLg66Q$9xW--Vq?Gy zQsj~T{+xb&+*^HX0T5%wfiN~Y!Kw^RoWK94Yra$dVJN8undb6h%j2m{z2{d|P^>l{ zPt&^koDceIzcN!cX*YG|W9MK3lXaynYpo!9mSLY5!)q;lpP|#hU7nj*Xd1SIrxVXu z4I)i1jcQ0^4BYfZ&qep-jid2FY3BGQs~W~Bb&^A1cap5~!KBu)@QYP?DF>BnEh*&9 z^MJ+Tj;1P%?+!PVa%pq1GK>0o6m#~;bA#bDcNCKQANNutAH4_pFr~K^s={FbBs0wc zB$KoAig!5VeOCcEy03gwVN9JpOYPoKlN2~%E`m|SVD?(2NK<*d)0!%eP|5aOIJNy((QAq~Fq{lF|dES6n(q4xtu~EhvhEDXS7W5_ z=2x%a&MNtz9qk_g&YU{=t}*_}({3;4S3LaX`&xuY1c}_la5R_muIVCkwN_XbeDVw* z0P>fEBDn#nb1Py)nAQ(~PBTN1t!u*>@93=ufIr(oA&t-2Iy3Z{kI>eEZf7&)YKtJ6 zv#asR+KVpoYgs>e=X{NqBn`{OesX>++lcji?DgfQ?Ev>|16ciS%1&ok&20Mf)E&}N zZ_9cus!0+fhryen{MPhG16S2r!*jO~H)Qpkis5$gk*Ju7t`^H%&)iZMy3HXMwlVnh z7M=sbLH+20^w@AFc5ysxH2IVLJ}(Yh73e7ox^goa5w|DBz5yf&0W?f0)V-Ns=MQ9E zTkKbq+jUAj>#417x*etAFP)4-MUKj3lkee2K zSeWujGvcT;&61jCGCj2<8@9F5p4pcdT)+*Zz0?w6IzEo48_q3 zy0)AfSsM*@_mO2$QJcNC(GRF~^tPSds=8N&z**XQokIsYR?e%59PMBg^Dn(@<*W+r z>i8mGxN~6+EN(c1*7Ta1WL=URxVH-nl$zVwR1Saf5lhyp6b0BrmUouyVlBt339iLX zMF(EkDF#^5Zh0>8zH#is@1%LBGpF=C5YMVQqgY0Z#r3!!20Qan@r!#q`b&W>F13|* zgp?GVN%6kZ(Qfq-J)T-PWp!-1iuPzurKU*StvEE_=oW4<9pbXRzVG z+>Lr)|8hwwKP-fGHR1WM@zSQMCk@<`LlE&@U|0yF8~s{B(3eT^WtZ)qU0dL2N+t)8 z;0IN#zxAvUnhB8_lWeyjMt2*?wY?RE(g!iG-3gz30PtD11`1^Rq%&Je&U!H^`($pB zBz6Z`iPu@Z5ulqbNY*J)$wwJS0oqH&X&6h7^S#I^y!qxIY0>;*)armL<#B0b1n|d; zV=i@NWfgqtnvnT@Ou=Y7Gj2}L(4KH19@#_(1Q*qrdqpifR9FK3shvm0L z^lsa6Q%G>L2e=Xktk;Xu%~>T?y0i+Q#8&E-ila8EYpAWky~EO-w20-epo9z%{B6}( z!g^&vQFCIHbxEn#z1n%eG+$(*>TJWforK45*m}-9gxB!L>4|ml{E_|8$V{h!jzep< z1F&DQUxCioSE^gqR>NR>K6%)?#d@EG0$r)wE|{50wR@TIfC%sI+f%vnT5f&aj2Cwa zi*x!|j5A5+pEblMi^AJNnRA*kyxcLtkD6$9L%7TdvIQS+noC%#Db?CEOC;1`Av5kj zmUVuUU>^daqJcSS?!t?PZ$xdzy|<0CEi1H@E*0CZSuU5q##!6Xu3)UPD<~Fa=4i?n zG5U=uZx{WARd17?Vt>CR2#x0QRH6EVQsQd=I2w}uv0VP+#ehTZq2oaE z)J-R$U*DL8#=d2+3?N*gbTebHWipzuUp)4sv0A1{=wP)T4pHV?67yZ3yeVlQp*r!) zv!1M9uORPP+`ox#Ctol?4z_r5$g}U;>f>nU?dtExdayDhb&nA*Sg<8VJDb@G+uWN> zTf}IWD6wH$iLictY4|PheTMC%D;qoM=QOOQ{o2OIn;4N;e(Lt?Q(4`%YH>mQe0b{+edFcEA0vp>TAtpM9!%y*Akc;B%L)0k=^`y|H>g{~DW0;o<|}qh1tw z7__6lAvnny<&;LNu{!7Eodf;2*L`VomeOnU?t#4za83s8qXQ$B;ix5dm$Wgz2!An4 z1Zm+^=&6I^&Mf=fqI_SbjBsIN4Eh`lnrfDKnoFOo5yj=>C!ESZ%sJqjIjLlk+kFxI z#qT?O*GEY4Md;C*KO=H({v^i=wQBZwPxme=%Qe;ROtZ0}z|6ko#~MCbncbs!bpb|- zSqwZ`#>U&q>^-%FoDgD(lJdAbw(`6Q$H3!*+m_9HirSDSy$T1!(zDug%rG&`mQ3RD zh0N-<2Y_NAu!6vg*FEYYFGsYDax#zT5^`#FrN0R0bl)dKo-ZtEljgK!^-6Ca99^i0 z(1VA#xDFl_E)eZxV@vjEz|M<`;zqm?{s?bmW7(h+YmqEE(9tQz_ece%q?fh5TpqCL|uT8 zUne2CV(8C^JCqsC5W+?`=Fy z_p0s$y7<_y3FfMy`J9%u0bVD8l5x#%f1@+x~t>Q3w0g9Ne}uu9~{J&TZS$StK)Qf0wO zO#h?F=Cy5a4%kRCP#m^x3F`>h2pdZl$oMiKGF(Tw_J%TYUH?`}b>dpiLn_GH2s=uB zoJy1T5faM^tU%19*-_ADG*K~>XDknk`<{ubfKmVCV{yV1-Z{(ou6d9U;D)dxpBz*Q z`-CSxq5V;_!7DP4;K@*7{203IjY{1j_6L;hF-=AW1wSZuOeq3s4!<1gsEIvOcIzl;#JK-a};`lERRu&RYk6fF` zVfZ#bRclt!{p#C3#c_qKdGQNt5EhAlv8{nZg1Yc;aj1fNt0PpToHWeUg^l_XPt|zs zitr%XT0NBTd@RDDE>&}tPgGsESGcxzRN$1pS^}OGTno{qTWSD_YvZ<1h zOi)a@uDF;TpPJM0XwT16yY1|2)%p(Az8&EVswa+a@eH1S0H8O~@y1$tyMe4-xw)t% zeqmdl5E_E@ntfER+Xu28$d0BqQ}dpfV4I$v?CgaJOJ08*{?Gy$`56|^D@o?!u-Kzt zC-+<;WpVb9#&_G7+SdI%WosJVVhGE#5uB}cdRvTR!yjzoL)+=oa+=c8xNs86+PDEi zXx}SATl&_bga@Yz3&7dM0BlYk#o6e{mxR&EO#_qLA36 z;9P964ZOBp@LCM>7IbL5*3acU&8TM*%z9&GE9K<$@+o4e0?Ch8mro)v2&r~#k@bU> z^d6EbvcOC2UC`M^cPglxJ)exfLP9?etb<4~?MB!AD;!XS8;-1W9mSnB#;0FW(|R~y z`gsh381n21e5!_t3QAh%_G9Y7mxb;>8y~w|d$%m#>{^+Jky|I%C*IEoYehMx9bsrm zbi;^y2qulZ5BX#|bTC-t6qo1S57|R^5QadH102j=-z49oP&OBntP`gzrq)(F7&3W0 z#xzeH&BvC7vOJy!4h&;GP#}u3Y~W~rE6**>`XGNMd&ta zd{5(=BD}}&^JA7|S&D}5oi-LL8I}?;Wq$Wm=ViHp6irqgdUkymOd8*smP@YBwofDb zT*E%*96jT`XCaZn-kHFYhz`-^pdwpS@&p@xAMT1a%S^KO7bsRT@-|8U6DY}*NcfDaP;H`?(;U;!XsK-R_%j3qxbEtxZ&$x^^+(kn{ z3Tj}4u-BZK#5u$o-?I#Ebo_i7_7IxYQ?Gw!vVz35r_hdV; zeIQVaO=(~5w(qj#lxP>9O+3GM=xoQm zq=M8ZAj@Kkg96P|G}jMwz--u4P|3(@1G9?Bap2pdv7F1LwAIK&Lm(g2=vqbAVs2d; zMti^}PWFHJdaI~7mvwEsqYwxXAVBaWxCeI$8X&j@cWr3g8wombhv30!+`1bl!QGuk z8*MCDPU_&TQD^nmsJrgxx$gAeO??OU{2wemV;MXi0odH~R!jW` ze}0iI^U}EL}cE@yDQLzJw08hZgK)TR0cy1FbY6js?-}8=LG{ zVUK{jsQ%N#AtY_hLslP!mC@jMuyT1OYgA3mW^w5<1+W@QNv`bxS0L75b$NNW>f!8+ z?!9rbKr0oKxaxw~wfeptmiGlv4%8j*4DbYRp$xzZOTv@Mnnm8s#i?};whE#=hB^5o z6h{HUuhBY5qED>^_G6tmJj7X^`)X?&4A0NMo70y4L=_d6v1N5&-cDGi zvggAg1xohY`PqfcuY_fl@7*@5ws}1Qa_?WA(Z$gmDs}BSI&FFBxR`=^9SL79W+zFx z(gb)R=VkC3tKgKm!k#RKItpTO^_pH|+@{K^H)P6XP(9wIJY_`37svcL(%;_~@4|b0 zw|9s}h~o!(z~OQA9qz5_qhmjQZ3vN3`Uc%#P;Xbs41tdj=RzGRHeiiZq`m@Qo`c$? znjh1yt^47O86nXs!jtK zi8EI$J81Wn{9b_*rr}$+T6w0(``5L1eVv*7&SOdSOTt^Q0#D1#H$Zza8(U{~ub;d; zMLPWVLv+L~UzScQi_08#c>Tx(3Zlhwj9~Z1ef`2?41+n@o?6i2U+Jmwzu{a0P3$It zaml@njd)qUt7?l9R6@SFY^Rql6g0FjZB8H0IURP$o#^6KP5{Ajx_#tKuS&IRZ1VZs zn%6*ZlUpO$;w*Pn3HYq&&sEkh6=Pc?*y8vfl7fqOaPKd31~s@)>QT59q_CmCG{43q zP{wcHjUJeVsqEl>qK`vc-f*Pxb&3x1{zQj}?O=8z!n0Stbbq6Sl&r`;a62cKj47j3 z>e^>l^&`fh7*OmZ8(u1SRz#XvO!rI$A1f{yJPVe_9kb1wz}Jd@JNzb>j>plMpe}(n z!a8YD$^A_Ib>WvC&+LE{qQ-OP0kv%5mvY;Re$(qS9dI0yipyh4zbvvuqayP zJrS(!m7zTpg^JBodAfbz845zSuQ1Ik$C`(?D6kP#?q1<8DQxoavwTIjgNk*qL94J{^bqr~hZH#l#`*Va5;3kU? z!xkn~=L{9KGXRwkQfrNANQhIN1*#5+iK$@G2?#vE%jl9N6ZXX8jl}<1dZvIyQ2_`H zDgHNl_Mbqce+L@=LzVb1+wkwH|wZ%A&7gxJG9Jsc68ZO47QjN0ShlGxv7t2^>;NN#kuhG0h^#}f@@wC2IYMnzL|bl-lmD(lcIvZ00JPEA(KokVW9`ZirDWvg5(*Nv;_}^TQ^V>K$_6NRM z^^wx|1PXV1Dfaiy5iZf_%*j>Sjmkq#zN(hjnUpM;?(R6wGhS5MgXLmzeTE>$8^U$J z3&+%+MftPfOd7I<7@(?L=hhjn4xxRSPn3i5vT$66u{w+k)S-SCf==8X@)Z!$xwXF0 z@f|7)kJwC&E^uWi{@mwFHs!o+^%T9G+jKG;lsq-!C-u@zMB7SKow6PU_w(Ynv3*C@7236&*S~txB42>r}`qlB9`ng3fPxG3sM=36u?{U z$okT3{1o(q&?+rjOt8%GdY)j?HY*A}#oQ8QS!eKdTxf3ll?7c;wu}%uK(+k*fJX`n3xmA{bG{MTFcFK;>rpcIu#nWobh@N7@2R~-s?Us`-#k-BC> z5$T?YKru#Lww|6})7O(xp)X6)l~LKZ4!u2o#@x@goZ$%UJu~zUjVZmb&rPIwad$fT z0*zD0vm+dy4~hKiw^|~C?D%_>N4;U^^;W;|fz9w~a zf9K?^X?1j9PzmeXIhILdO{Q8N`QER>8^AH#9`sb4NP}w0WBZ=k?l$JgxI6OvKDyvu zy*8%%UZPE2p6IzQIGTU_%GNaw7I0X$zoq~qI0~rPJdI4ld6jpqjElk+C#Xbc*}Bs_ zL$GYa67mM0I!n)bP|7WA?HsD5NqXF*XYp{`F9r(xmZ~&VY2IW<`Mvh@*;sfX0=
    -fQ)@BfixYgkZqKSC^yrx5LtgD7sn6upq4MJ` zpy&HErBqLJk{8Cq{>qG2ja>$ypmjE-eI&j-Jk^@~j07a)KG>H$qrQ6uZf7O%`TDj3 zH;XB*aPsZ?R&&#&x`pdkP8Gx5yN>#=|Mk(o_q>z?_ICf*)l^F1 zyr^x3KZA*Y5<|_ZL1#@BDS?Gcy6N)_>hnDx$R>sf_m$E`_dF}xub}HkKq?)poMb6Go3C#&o1TFyOqPI1;5-Y}4?!^Ed9n6;VSU{ta?Gk#e$=Kp z0^I^Fk?bRAyo=d~f7${mm%9YK^ra$av6wm(<+y<=?dimcsvHf0tUY#N2>1_0Qap)X9^82g~=7 zIjk}vCw+8?Z+(5IU1FwJfozjMt4W2)t(C{V&vdXj9NU5n8|GGQ1Ih!`Tjr)bKR9&| z9wIK#%BxTMQXK|XGXwh!mG^wHvz1F^=DSaCn{3C5$c%38`?7|14y7P=4&vnuJcs0p z!<4Bnv1gaLgstlI<6?#nj>NE*J2;eua;zQ!8TKQ)njHQ`B25{Ko`PQJr9+U3#TY!H zUhpvX3yKVdzg>J)oQ=b|L({N465OvN(s#au1i7u=!J)Vu;}gQeJs11gH^34F%mNAnCCTG;Kj!e{t&QS=-ow}XwIjoCgf+^$jKGw5z9%&TEN>D1-7v|5EQpQ*0PqSV~08ni&NiY&p*UcvNm)tIjjM}@-9;nlujr?v$ zl}f1EBd4TBmzy5)9|5091V!3)xNG1Ng5Tw-@96oPHj?KIH3JeXdX)xW%}utSK@$rP zdWnw&4)290d=QTSh{|4DL&c#Z-QrT`Z`kom8md=qBhQp<*7IAwZ)HW*7q!@S9Mg<$ znT;NT6%UJBavuR|o<1};>rU|MM}P)6d=EKQ+&Q@DLabx6b(TranF@;~_ulD!LX&~U zN+|e#J1ZzoclvJj&h|GQ-v(4gPp7uMdx*bYV9SmeWZ>3-WY8ukl*NrX0}?xpb1saf1Yt;=@Oju;Bbg;~##XFdW@ zanm|vXVir_bN1fqn1xFQfg|w5djFNy2BITiF5xrT9J}^_^P6R8Ge6tVcoC!|?(E?a zP}J*uQAXAU_y0-NOubimHpdC-q6x-4Z5aA~+kYmArJSigP^Xj3rj>+h6Y@0X zlSWA?sU)VzPAd$I6E)88%6|6TVqpwsQn?$wEdMKyy)YkPE7WnCTaSiF6O^0krURYK zhheuKqo0^8O%`4-6%_F@r`RZ9)_$dCRuE7PMr)a=NKPd1Rc4PKG&C-L+qW7?Zca%2 z%}GtDkALK(X@#)V0Bfn)Z*r0<-tHqD3JvFot)xD^{fTe0W!9adaA_`WmRZBBa=P21 zee;%XI_X*S&-+J!&%8C3C!AE+zBdnWsjA%-Wp{!#9)$IIqh z9zNudJA3#s5vKh9 zhXv=I%~8Je`+qCJ|LBKjp%nvN&2K+bzWp~k5Ce2<)`NK{G^eipb$ppx(kMkRV8&YT zaJi+s#dImi5aMl`FxGEl8iaWywMd1@dvqYA9`dv`H%qY5bVKNYi9T(`CxGRIYoOn- z|Nf&BnDnKvh3-aiXO!bqCk-6O;hEkndN!HLIOy*4cC3OfKe@QcwNA{paoGjE&sip{y#OU}J^j75ML2F0&)3RPuj_^h>H1tK(C*7_gJ#^>FY*#U5ie&8 zbVtsfIvmEmW@S0T31doR-Yx8Ox!WXsRkvWB$ws(zT&gR%#nLBsD}K+`DTsK`rGUox zbq9kDjQUxqKcNcIY)n_kA=lQDKDjICy6O;p#QZfG{S|oh(PWypJ22pd-nND5Og5(T zS(-G+k?@8R(GmyR;xGAVm|Y7$8NqdEVcg64Qc1Z9QA~|-W9te_;pG)ifN68vS#hw| zl44k}t$9B=vbM^hqBHIp(qObQX|duoeD3ad}D`E=kWLu)7SkaVM0^ckXg2nWWM0uI+>}@DesDJQ&jW#g1|x=oN?33uAVA ztIC$T%^PJaP8X&sqWH6}%ROCcdKZ`RiP@+3-jvAZzHf_t<}Pr}cg|A~FpqSJ=MxUc zEP@I>Z42Xept3rI{u4}&`(++s>}~6t40kw~{UuoWo>ER`cna1s3-+dt zMci;(`^KtR&bEZcu;8C36JYp{zGVHcH@T$#!b%|vR8FhU;EBup(1_JQesHO<&ot3JKwzb8Lip<=bR zI$LUv5!doRJJY#v?QarP=63+Xntdq6kXypehluU*5#z(dzUWNCAH849+iJ2y6FEoF zL^rGsL$a0g|znuzdSk|B4RBYtkPhhn<;KC&h{&k@jAM z^7t!y7Dco{JX5u-O;NCI>C~kUpt*GHqncNygVYV{xz8=K=BveZUP$LDWrX{RNWpvJ0NV` zUoynmY4*rhO>Ndl9FoiX%I23;bqwe6=hZDv56`)igMK%l##Im`%znzP#9<@(hQ1?NvW|6u>>6<{99qh&pvD2+YeU+~v>BAnhJL<>tZCU2_80XASShZ7rC=(Y>j ztgOXXb&xii*etKJe*a{gl~mdk;q$j<#s7gL=Un;an~O=V4P}0w9S>!xNMc|%ARBPW zCLG1{@g+5NlesImRBB!L@YRd-uRX6yH+Bu@ewR!AEo7s4$uzHC5bs)kw5|Pq4HZ+= zPltUHhBgN)_!664uovZV^%5SdWkvMzGML9nhy*lf!$#tRAtJqQKd&S!1vOy_0=)zn zfqLm?*pmJN5QR8bZEXAqVL}|)3~xH6P@{J?df*#2u56{6P=zHnd9VuZu6{`na=Xxj zjX{AF7;AUeWq$Zf`@2!LP2(jFYDBlO@#@=Z;tmgwz`PN=W(hU>=fgr5AxZfklB5YuR~w4F9JMj8_DnxKZKZ2V$_MaYXb>-0P`kB z&M4Vgs6*kDzPEa}cnM>={BfH{`tvQ#!1U*7`s$1yC=cBAUvx!4sJ6~Qe3ROH^Naqd z`Hn4x-o@Xm&It-mr)qR4L8eC{$P6uMVn7fwTA;lRR>1YhH z$qRXMLQ1BGBf4Fl?n}m?w~Sm;DLLlw5zCz@#+%fE47VMI={xds_yJEp_y%U^DwbjRAGr`$T!WO&X$yH0@9$iwGJ6|N5#=Ah;y~W?S*WXalfcyC+qP% z-PSJ>luC?ZGsFE+_On07zWsg1=?a`pk`#)4T+6P9R@Ea4%x?;kdW7%C!{iZ0+uqIL z5>4J}8E|PAuB#SL&{fiGWG-1w=S{*A21;i4Hb&sg@o4Dqdmnh>6MU0$%;YMYt*yeT z2HhxNLv=|hf*b0R=c8h`CwFilNMS_lbb2&azL=p{$y;W$^+yD2hoi9L=W0ps)ww=D zl}`x*96HdrV!5caaOds(qt77KNXY{mv0M4~jkGBEO!bQW5x_fWnAyZQ%189v}@@ z0Ru+xey%D_%iY{-(vwj-s0Xmc?h!yH#$4fCvuJ=^2CZ%PQr`Xq06fMiNyy1MU8Zk| z5b2DGw+k>AT`nf)!^TJ(!<)WVo+RCuf%y67+J81ulyO>@VlJpc?~N=UvZ>kyfHSEx zi)_pD`{e>8Fm2@iI^JE`rGp$R+YSBIDp&OBV8jn$zcWXn32eDSK zds=l29|7ztGb0}`YNmQT>jW_kI|Ta*9PX5fqS$AC{JxOqa1qH(y6kAZCw$iz=$r9E z2+u48G(!Vq+pRRbLS1&r=y!4GYV> zec3-ZJ2ydFqwhBTu~u3c>XsNjivBS=e&>viH(cz!{#pnkZCB+5x5GGt)4{J6cT!BwFO%RL%OJ&^C$xJz)x$=UI1-_#2p zoh)W^c=v{ooBmrUuRxt~L+%SxZcU^*Hd55~Se>WgGn<&Eejc~xPYKROZ?=^88{?e8 z8!7LR8s7=Q%H_f_pw~NF{KJr|X8id9|KHnM9?OcD9(Y`}+88EhEaX1~+)RRmZr>I# zsB`JPT`+szqL7Qy`FYNyj9VC-f2A;J)fSzU$d01?LbX-LI@&8Q#!sTrqT-TaMWM?o zr>Z@r`#k9F)Tb}<<18=Bv%lcpl_2V3?29_igjcFQhBPMvZ3|I{pPyA__?4-dg*4gj zD-n6RRz$suXhFFZ0VnAzV5{Y87zS z51Z7uxjyH7E8)z}(}^~RduX<9?A#ZGh1`v}F%RxW+hL?i17NOfp$jaJPDbUsnS6vi z5!mVeR-~1U7OYS#bL@rO%X&w#tD_T2415#3m<v2U0SZkD#FOorc!8!Kf`>~c)?ZmwiizxBv!L2*%ll;)A-(v5c_ zYT^FLi7duZhCYdsckp}VGnf8WBvphO_0NEk2faTu&4xQ|f`;(e46Zq8a`Hf&aU4u^ zb?~NabFNj-kl$;|=nd>AS*-($^7A{Y{p3nd#CQxG2j$yvLr0bQCLxNejo;7+hkpGU zbmV1&t)A^w*;(vVNpSiG8xHpl`zP2ITREQ|xygQ}c4K{mov_cXCRo<9Qa?##e)K?* zEeU!GzxGlze&Npy%=?Ko%UaS#OxvC`=x?w^-{YR(kQFALXt?O(W^^0pXeTmiIikYn zP3UE4Rkf3|eX(9$S_Gae?~GAl%Jh}OHv+Y$gwf4kZWZwn5-yQA=bwAQ>~5$Oh3S4U z@H!#sW`@`)wuAf$4p&PR^p9wdem(`cLJ)X-j&>>n|Z#$h{423 zC7qI<=7cS7`%L$}fRIn*D=YT17xb@f{5}8|l-DSPw4>4nT=m%ndd}IMuTB;-<$h*X z({*K;#=<)f8M>nDV=C?Ip%vG4_zH;<6W-(t{8e8sd4pra(p*Z)45D<|SC(|Pho~iK zo2!p4-k$w2%%UKN6DboL-a1vPi1Xvx8B}hb$#cfHbvHe@v+REJ@SxfDz7}3$dp3PL zr_MmTZt?5?VjME^SkV2e1=0RFsb`&RQn|`D_Kr(;3|_Op-(}{L-j<2^mQ?9W|Gk@D z;P`!cXl`$(nh#~WaC(;I^onTF@M&K};@A%6BJ%pl)AC>2C-~J;i23a$L&c6)&cvJT z+$!Rp-egHygK-r3rItI>9hX#79t^-u&)4vs}_1>OY2lW@n;MAIpY4-mPxj|ejH)REh;AWSk9yfINJNJuw*A^^~cm~ z)DFC)+`0~SF8XqF6p%W_K3tSXL+K&L#e<#g1_#M-#54xbBo7ZoK+U^7cG^yXrI%Hf z-MSk5il9G-y`1r4d@XI=7~=is=t>^)a}IFq*s{PCIV-_n@9%40gEb~&Q+4J+<)W0v zYFC|k2rDDUw4ecZ}6&|#qj)N`ynlJoQRi<|HteTY6KXo=%x&?zt`yAMYmqmHS`JqsAm%Z(lZ zSJdLu;%2uV$PIS^b%X^gA7&)cdxD=Rzz55Ewk&iUUF46_Ln?VSHHo?(kY&e@Z{BDs zegE&2kN*w$c>Z5ohH_~_B09ET8YK^zu~>yS_u*Aq zqG82_m->QT#VME7is7gW=_^X!BemnkmPY?7-z{%%=gS$-zFoPD>+)^962d#EBba@uONL*o8I&eeX z-@2-zcbx42S}6YiH~>^i;E2NTGkW4rW5Q-)wzQdN zi2noUWU_duHsqXDH>}uUK$52OTMgFBS}3K+)6Ef-`_BeW=#-e?lRuSMnkg?D58s|* zYxSC@$?_I7%rsQXDBCAWi!41ejSarm`Uaow)MQJIY{U~Q_u=Zl7qWqAmX3B7PL*YS z{*BVawxvWqZz~wC;8N8~;p?5f@}%a1G;w>s0p|b3px29z46W~`BX<|ZTvU>{d_ii+ z$6`yUWaHS7OJs;(kYxGnS>s*d_zFMs9ma4@l!n!PX@Zjg1pMJQ;gt4AJ_q4I_@hH}?Q>-yl181f_aKql!m5+cXzBcD&2BcR_ zenxX&UnzC&h5f2lmhNz-T#9!TQ=_Ns^b#P-%PcTed`p3m`FD}+bd|EbhMdAgP&+)c zgS!8rLJ&{T@imP8%zQ(a(8}pJT8LNx!>vSXV53T4>-U8|5}l3UJ?GsOD{s$&H{x`h z$8EHtQ6o|&!yObBdA$S9?}1ie50}6vo{?vSs-gQSlXZzkXTxWgjw_pNl9Oe@QAtP2 zB|QwRUxuQ?i4jX3+ck%9bU%&^B~OT5+ZZWtEx9+*1vggvblN3aZALVZhwMj{qaQe}Zm%$D%fh>)07)YaRhYV!Sm@o+@}A4Q3^wS8#{?R+1gNV!{wwfupGEuuX3$KR-J|LClLf?Q&iqID!=({ToC+t(6iE=nsErAXeXPu1fRP8H8gso&+@AdU%uD$}s5=izMZ&F<9^{%WO z4V1iP7|?{Xi~CV7(5=?`B|(JS`34WQE;=5PSnd^@fgDa(DRNo;^>pPTCVz`hEdF2K98z&t zD&k4g+aFCI0Uo~cC)n;d9weE$q% zJWjnzjOcGgXUgc1D`Ann?YeZv-Vh9lsZt^_Me^~**dV^(a@)%a;lHxpmx*bJbl7>7 zF04xmJ4h!P<_A3SY{U}ylU zm8wB-O5@|zRyr%B@<@ABi$l0KN`uYeJBPJZTt3}fsMM+74o3jWmrd_5v^t8G<+QgeQ{`@ z?`~hS5>{tH)z@CZ`*ZrfybV+$wn2-r$vkHJvlZR%*q^@XR(eMq)u!Zv(!UmuG{3q> zIem)2cT|6c_HDq@kHMApk{e10&nx|AMb19*oAluocK<|Q_1$8n$0Gp$uePr?a$Uw8 zD4mf?l$-wR=g+wqH6sc_lm2-O?sl+bcALEV1q5I zZNU0_Q2DQx@4+HbnPyP2;D^+QP*}jLo7VZPg#7}W!BNinw)8KbTuEdcJl(pq?@l1D zq;v2daQKLrgO0s`l+4ne*wAlpHQnq0yA0c@c|S>?n43p{eGc=c!w?)6_c9<;V2=3O z0li|!k~q{S|7-|*NcMYmad2ll%Nd_cW;*|(COX$-;|ih&C1K)cU)eeMw0&{2Vty~{ z^Lz`jJ@sp#tF_+I?N+HC$UF4<5PuLI&Zl5!Fn`;==*4_}r+IQ*PH2mQ#lUgJc5>k5v+ zmccAh#X^Hu)11;M?JiQ}`C*2(lg$@1KEhanX;rhpG6jgoobim*e#^8c=ZPcsf#_75 zH;X7S2go#o;uexij^`FSVs3*}ThX)ei$an()9y%TPM;g-XPFc4wv2Jzi{0FxpP;bz z8DfxZON1Z?b_>&PgfL8QJQa75@^f(plhJzhQVIicuAzq91Sf}Nwi?8!m4B<9#Ck_u z@xA$0i*r2BlzerTOt{?9&`wqf^lNsM7utMbcpKHMs z`x(ly{DvN@}-u)*=rIN{IRxw6hK5n_LxX|ua_n>X7S zS_OI>IuRk>Sy?%qa=?Sbx&EeG!zSnn_v(t_`L&vu4*xGQu4bJc_agu0A2V&fu@na@ zNxPQtKWLS3e)o!&Zm#Q^oV^2M4BNLfl2i(ign_Gsd`%WaSDLnEmB~(9F8x@~{NWY3 zssC)?cHd`-2_;`G12EHSHUYzHJK4$!DzG⁢j-KG*>6aKuIQ_e<{$)V?JZ&4Dt@Q z%eN9}YEJd0xD?3-E9vCbo61nBaP#R{s;f5U5}DHEEOZY1E!(hc0d*$-`~lGP|6$1e zYamH-m40J;#Ub!#%%$mDTKnpmAJhpj<=h|(0q(`k}ptf6YYK6Z2FmmX%nPVg6P!;V^>glLg9pmaPlq-v@ zHBd`z6#q^?FZ>8#_^VHk*t|0QDC1@F!)LGCG^ntGCO=*>-_{wdmu_Piwm7PtIX89t ztgM^`%GbHB*9ii#ckl4E#ja#<%xxlLg;B)Q;u1h~8OZ7p@MduyHq+#4#u?UCu)s(L zRG};ID75yJ)Nw)TI@ryBd)C!FjrG8+pP+@{Ox3rP6XQWNPZS41pD8ePY0G;0CvCaj zOShivNyYn#9JGI0)cp$5$w9yLCo#e{UQv0Dhig&E@f?Q~K{>7EU-=NJ-&jRo4vTF+ zZV%sbL>8DZE=TF`Uvpko4iWO1wPvs}` z-xDGE_Wy~c``6j`Kcju0wfHdNCoEuQtEksG%3;%g>5nm`8~-R2`1e%34F$_3E0>dU z@vtKHr=Gp6h@nqZ?D^x)#Q;pb1KWK$^Jja*b5P*o*G4G>Ut z_q(s`ieA>W!j7+O=~YB?8ZBWowrLyJdhx7pV zOgh_p@fBn4=|@0?c26r3q;NiB?w9DHHEI8A+o+M!JbgI3l5$1Tjt%_PQ$OQ^aLW{S zU|hZF{T!)7=9MyKh)2f&`Sj$JM~pGON)56YJ|NGnM%aMX=swpi{Sx(zVm~#~u0W8P zxU!5}#QFzmY;82kmweH`XDC;r05^=S*`zSTYuaz0XnM>R7q{dh)kxwlX@F7Lux37}aeyu}FYFPQ~qvea`kXL1gnGwZuh5$AD zg?E~{kqDeGkZ*ZVnUCJ_i9)tltT3x0Nk+fO(l4bl)Pc7K`6IIvqHiT`I|x$ptQIt3 zQ5!aSx1uPv%=PKU=c2tIkXl1yLfZI0u3$?oAE!3a4?X=&;~gMgt0_G!$&*dw?bEYs zMU5$w^AA=Or}3^h&2LD0+rz$qz~@WLg2>Y{R zqxz>v2cayG0(&u2Q@0E`%fjLjH2)^Vg(O@m>e^d#B~JO)^v5Xe=n;f`i5=u>>!`|W zp?pkBsq^%1DIIP-CArxNBVKK*|MJelDUTM9+HR&T!(pl5lQpBk@1SK*P*c3@fS4MD zhsf!P($@;>b_ry53QQo%{@CV{DC_!b0~Z_^x7qi%)n|h zvcWw2dVa&5=&%$!Ey-E7fqC1MrMBQ$WieZAKb-A2w`8jtQbkqQj8r)b5IP&E@fYWe zfwJce@;w4t{D#JWdUB>mlrkhKo5VbYS%mIIetr!iC6qBklHDl-J@#cO|9ZU@X0J%4WGUdO$tJK{?#`g2c!7Pb0z znSbxRh;SDym2)h1Q3VW3o8znS?0fym^A+XtG?*nNvropNMcZ-%C_TB4n#s^wTw3NR zQE|4sH@vhaaM_vK-)lk{|AnSC@G~EQFUZqJVu{B1HEav&NkQ`x3a3Nokj~biyUGzi zHFYH)?c8U<{s$o0TH6v?2x4OW5*U~_9jRHZA9IDrmf~>qmic!Xf^?zdhwtpgc-6)f zL&2O1SIw8EZolz{M?gpb*9>kU1F;FaOk|UlZw$DV_}tdnN(B5Ipy)qd*)u-FNT3o9 zsfrx5w2H8O&rYTa(VShA;2S*w_)7kci4Jp!ZE%-MeDU;IWuVYZhuQDk=eukupGN>k z6SFe0&1mnOayfa-1OC)`$fe)6^wfZR`-2`|-Fud|PJ@Ffl9HOBJ@G<%QJ^=x>no8&yg zjyg9M7MT9{9-CYK3{k!n=@s23c!Fos=;-=|mn;TfMxgnUjA6Rty4k?rG3z>jEwb-5 z9~biDoSu2Q@;s2xH-jl}POE8XH?AQPHB94qTL&nQV2}|&;X20<7$Ai<|ZHdtuENw|-^LZc;#D1@b3{&X8}iaX9e?qkFnb)$W+L zvl>*SUZ5vRP4>#oxeGGn0e=O@kVZx|e9TZ~*<73sgZ&ftQc-NlU4*Gi>s0rzw3ocy z!Wf&qMR2N}LZMZoMp>{dEVi6B_UWv}zLZuShZL`H9>IT+DgRr&VZQ)NK(xQan>U72 zD}^Jqdbxh$5-J797)pE8aely4ZIL4*rOgT6?S9Ld7D|U$XwV+|(3ROxT~RLaZt4EZ znc<9wlO7{1tzPP!IzfP34UVxyr*q?b-&fdPDN3o>fo#0?;Cw~_atts5VEK~lK&~Ut zvq>*AMYOWXL>RV>6`Kq*ZW}pjiZ97AZSHl3*m1<2^K?F)(Xi?I3!Er~1s~m~g*C+9 z40dCGWBbm%z@wJENuUDCZ_a%&9h$D7ADl*R#wnu zubF7qJ$2R$yDNU8>VC=SBBIoy9ni4gGRC?&vUMTVbd@tl!FR-M-)9;SWonYcFQ+5W zfR!ODl9m%ovz#~5l)7x^t(Z9|efHk*w$jFLXx?O_p^YJBbgb13r^Mgpv z{agwRTK$0VD2Mv1L#dgw^$i1#avO_xReM0BeTB1) z^Ho%wq|P9h;ic=iACQEaq_dJ&Oj*?HC3hk}&4&Q6eU4S<&1|K8bzUi&!y%--(5qdN z#a!h3G6+1?n3iZxt1;jw6&3RlT#%7k6sd4Heo`e(0PlHYzi#nM;>`=5!2dspNa9~F z4aBNgR{!3?wW`Dt5^o6C_9xVxS!MSb4!2lcDR3AY5U>nZ{Z^EvVMp4*n@h2_pX$X? z!>8|{qQ;cTyOu2A+o6vp8G&=gFSe^T?0gUgBd?{JPf7T7-WyzmMeH^fqhdOQGPEED{IHgohx%_7@C(7Vdh5c z;;Vl9!wCB=;TUdy}X7`t720$4Z$wX>JZn zJ7afZ#@X1P>0^$LCNz>PcUSF~{6Flxb5%7*$D*k7hd=T08;GwT(X6Au+Wa*|I!051 zpw%R7J)=j!a)VB*H&F8JD;X`#Zp1=|r^m=zJfFqsTk zDho$6s{A_$PQd6wu;9>~n~GPKh4*QrsodAzw=j@xtZYAY}*99REibU`4q zW^~>EQ+oZw()}lDect3hWSKYr(O@s@NKu*zJRXiRVKUjNuPN=ovr6YgVm6Gjs;{1N zO9X{m#Q--$bnz|z`j#f)VMD~C8^i->Kb`zA>1}T5Hq|b%x}$a6QPjRU+WR&!@gj45 zUuE(^W0Ic**$8yH>x1pQh#N$nDqB0EauXUHewTofZCM64mb3Q7DtV7YeLIu#kNLRw zLieKR0x(#7E>_)sI|URVfM;=I23)Yq>v;rCL)~9?JECJwWX~Wvf)vGzP^#+cc`53b zQQc`$at=)xPOf+y$2l6S1bj%B)2#<7mUt8DXS6vw{bY+%vU5-oLuWh8;_!q71Kd_eIlA_5N!Mh2$kdxBJn%P;*yMK+ zm^dd`uka5(GNr_V$^U7F6S!dh5l{I8{#=uKPjCAnv0ErT&RPBufMd0&${Pdl`>ts% zN%`8;ZT;ldjoPOZW%*|P!3p}63sqBU&MZ`RUx>U)5YEInKl%GMK?D<3d{F_L4!NPk z)B2c+^00xtRhJZ%oKwGlRa$_<<_dL}5sfk^wES4wYa@byPN()Nak0s!oPD!T{)kd} z8M!;5Uap{J34JU329|O0js9masS1ul_+KJCviR4#_5UARZy6M4*R*R7NeD>@kc0ri z6Wj^z9z3{va2?z=2^QSlT?cm<2qd@;3^p^k+rS`$!*}2B^SrzEuKND0TK}%9RbAD6 zT-|-1>m2p?E)5;I!DY?%+53(m@-4g_xw+VSO&Kvcu-spZ3xQpQ7Um&f66HNuGA>ew2Glq)!R+hkctAqeb}79w^F zKw^N*q<>!H=;*de!o)7hvdUT74BHvo1{UA=eBW^)!SE+#B!>#+wkDS#(IM#d=zv<7 zG4~bEhRJoaVO0+H!o5;L^QXG5+|Y^nL+s#;t2o(^CWfq3mm3FX+#{z6lAuYu>fBJc zJW|B$CiKi0bzX81HS?f|Rae*9_wXkc-3$l1D!uFf_9*4X6^y`^qv{L!eC8V)8CKHA=>ORE++%NM8u2|^lWuy8H6nnE~&J($4$qYhPk8$!`+WpF3IwfFF zFV=qm!HX74o3H&YMCOdS(o9*?2Q4JJ2YZ}WTJ~QQU!zEM!Z{PtGWSXV#a~`bHz;LrRlH$u;S{BSV#P@*rLxkw#5! z+1zmNLp@6;@=Y&&&#}2XEm4cB3R$Yq+R+ceM0>Kc6H5~YvUKTY~<_<4?0Q^8r)7TrpdUA2FRq)#x@VI2`kRHD=~*>^-N7+ylo+_pH7 z9t)e>TAypAMMOL*5TVixTwGug`ljA?!I8FLG;0sEAvg7SUEe0*^}&yZye};Oik<7Ff{Do&xaiOc&3S>;xZ_^_ zn??D=PBA*(VV-t&Cv%CK`~zv<5hwMK6RS?__sC9%M*yd!SUIeU zrVt)H-AIQ`av<4GZE!!bM{k;S#Y#vr%PQv#ldJ53=I<&T9V;o|P{C zzQ+_%?eNrh`sK|h($R3c;v{!Qy+c%x6t4CgN&+R<>Dd7p-Q5)^RUF#7L;7j#HjiL> z37xYeoH2gf9=G>{>4@3`_@2r><O*IE}$J@;jogx@r4yfr>i{eW8>|N-@iwqk;)u*Z{e-b^-N*i_GWvF zgo365Zi@c-uwP);X@^pI0w$R#2jkKV{TSSsxWS5(r!b~!I=3<)eqHX!JDwNg#^kSd zlhYUce;1(iO`$16)Gvg%tF1 zYAXi3#3{>@?%=E_bS`+hDfCCB{c9+0Y^&EVEZ7^>pWo|A4x~El1>Q3(o#8KkXpDPd8%q9~8>~uxYRV~@2UF+T2`^khpx*RrD_G#)N=OWiSXyP1z$bEvoq?TKLy(RjII}zu*i4&igMgo!}4zu z*wB}LgVL5HG!e3qO+Slcq;Vbr8Os+Q|27dNY0Sit^tC@OQ;O@N!fnLiIeQKHf|r&o z!{k9M<1T8B5h*)R(6Rjh!tp^OouuPy@$b$D75ktJr$d?7?!W~L(-K2pJlj)^rTl^< zy*{CVm00twECV~Bmt}sxiE$eDT?7)~_Vo8G$io&PO(2`n4mlUpMHLDfk1bWCCS3vp z2KbwJpTf{Y=!>a%nabwO2EF)zuWHHFp{I`kJ8QX}A5%mf-PpC?wv1U@8`n(_)W!0d zhm*&`&5mbQmz*Ie{?yyuAdpsSbt2;%nYm!Mj|cjUysvOJ`_G1ae_X2|S|x03nb0o& z2j&9vTR+hD(v|w0EH##fR&WmfAWP_Nv!bQ!C3XboR$q_AeL>}+auynhI(e=T z&Npw8$NwE|`k!&iKbYDUG!woo{~#w>#a}XtPzx%O|6d%HQm69}U2!^8vsewRT9Me{ zBREexFNpEM9Wq>VEWK71A!Xgj=DrjN6nQ8%irYrcT$Y@Psa^|_n54EEM>22vrX=)j zoV5&)b~?|?G2AryrS)3UES_WQxrLWct*H7lH##r)htA17AI7=#+6G);Cw(>nAlZWzopjz4%_JP z(|(xHu9n#?qQ;FUICk| zf8ogc`B+wqc1GQ2YF+$O&9Svdy4kcIDWYd=geBq3|p03e9U6u{NSt*wUZr&G* z`rkh=?#xvNuaT8ZAs4n8Dl{Z~m@`^MuX@@$M=VO(6U`Kh6SgJ_L;$-Gq;Xd4TC$OG z_2#K?%C}i1*WndG>x3n(BUE+y-)la`^c|PvzzvALqDK4#;rWx{@BeRz017ld;s5L0 z@aBJP%ox8HfBQ%$1+HwA>2oB?Y{IOOOV%@L?d0(95Nd6$+*xkcXbg{^W@mmqWfLmJbDunvbS#y9COYU6GJw*`}$(EpTV{L31`skF;6oX%a`oD_uO@(y34B*sW^0b zS&3MxNBXCE0fx;jso_wp_8Cu2bZX=kql;GdmakDB-;ICtnL;G5`K<}b321NBizo0FI8ac;6`c}kbUBk5i5&Vzl6Z}UO!_oFW}Bf@5X>r|=~5Iy0uJ#x%p zs`AW;R>&nUy8<^>6#mVd28L_UIm+9?T{e3WLEDx6SMD6p9ORJ~&`um%q&T8n_jCje zvB`q=dYdn(SYHL%RWAQSg^{JoRJU~ej{xgl2@yqC>B8fYV;6%0XcTR*Vq9ato-K&Lqbm@IZK@D#k8S$g7T^<21)L3YgIZ`MT zh-eGU-qKTr;6?-T%pjUyb1jsnp2rAgGAQkuiYP>DpMOL{ub8%qm0#RlpjlKCc;3?- zT!EhJY5ZJnBFBo1vN%df{|K~M5~;%MR8+5bddf}dn`~S*&f|{vjJO!@E$~N;Hz$RSJls->G(LsXN*ghg z7H(NChp-|MSs+Gqj-PgR#)Z^AX&5@L3f#@#nb-Cd=r~@-@ipAhRcK+M7L-Rr$s#cM z%j@UMGjuJ^kn34AG#@?eZZCT*YaP125htMHpP2;g@2EBNtyOpDj)TY+<1C){_TCh2 z-t7pjo_3iuqpi&ElA1`}&WLSqTZe`ai-tyO3hr}NaS6l97dtM^;4&TkcD^#@e+nV~ zf)s4e7Ns(Nu-SBE)ROj+P({%Zk5F89LDnS4UeR7H<__aRP_BSUF6eVtRp?P8(6f2# z;yB0yfC27iUNm2y?AKDQ3*m` zwr}$5?-D`^52(2G3^%LEPsOgG!ODfrtiL-v&!<}wL1cg*$(K9cd~eW7aM605)DE6- zaFajHCR6TU7^QNUMkVh08tnDXe|Et=W2X@mDi=p%5kgrhGxL1tpJjZFT}N)XC|}C0 z4rQ=AnT%k^=9vkdS=UxF7|mc_)65?T{Vu%!pQIPA;&Lmn4$w)YPV>vtlZz}pXe(qWK(VJX+f05 zwwBH{HsJ@N`IgsQ)ka}og4LOXp;uLIJuS1gdPk3dO182{&o!qUS4ADG+C`eQ-_S>Z zQTFX%_Ta9;9`GQ@{gBLxI$0o?+9hmdE?)YX)i#;P-&E>Oafa{`9*i}}Qrw;egJXf%Iq>|#g3%y2?JK5cVjUyR zB4V105&~tj=5Duk@L`drCZu%6cDnhVXF7v)mS{#LOT_YKXLG{&V${t(Y!%psG9B~} ziUONQ9RYu{5;(-MBB}NFby6MyE=vQIE!k|mmx`NduICq*6nP^H>63(S8(F8+G;dc* zZW3C$YpyzHyvmMCuquJ=dpi*vck)f_Xg1C5C0hryp#}Sgr_YkUW9M!tKxcg^=!FKs zxHU?}a1Y=-INJ~@M#Wc(oOA`5CCbyP_@VPK=HY3!O26m5g;;Wmy+CjoS>1t^sGW|5 zx&{!CS?l6}|5Aq``=7U0vws9|Y_gAg1KBoKoXS~1tv4M1gaMpv?w1s*Rb5PZ<+4tq z7N)Rw>h7Z>w(|%$GWh&})QA;E2U!RcK zirzpE5+7ElUL#BxXUKTjrtLd_kP3D-5GF}?u!UvpN7fvj5AWATpNMTQ%bg#0py}qh z{1dWA)@lxqI`?L}tpE+huC#>aLm)O$id{vC+vE)oOkhJBobtP^0>(~ZG)<*L`*TyA zYa6ks?@MwdGlPZ8B^pv+8~;)_gf^oq{-?7V7hc&&423Yfiw#}foz|?h^@M*+SBxMJ zt5AP6EhxAwlQ(4JOPxUL7(!&wQ*J4I7tjCZLGiA`Z@Tn7G22yK%BvCg&x#_sxKmdI zW}Fp?`Z8KISRkBNqn|a5U*KYaUiE!0+?Q`}enwfZ8$2~uC^rOCbEqizTQC7mGS{Yl&PbRv(~G=jRxU;PQvb0Mk=phWD~cLFL=f^N(RnK3y`Q{CTnm4-(#>4H z$0)M%IML+XsApsgS0T`m(rZSDwRlTt!2W5>Ly6qD%9lHh zjbfX_)CB_z{0NA@^0@{PQIrzvbi|CoX~T`@U*+L;&%PLUt|IfjoO2$I` zsRAFze9Mn2+DlaDd3{j@%@k&hy1W*ev>NV~s8}Ut*8OCm6F;Uc>Zh|G4yf^unWyJF zVsooxPMH5G_pXHRga7qBj|!81UbS#z1pk83z?e$nfJ8*v$2|;SqhB!(!3lX#g?CwV zWl`q`LlonJPyK$8-xePs2l~oe@o0msRw`eu-UWKj`*FHZn52Cj2pP&o^g3Cr%hN=@ zdqF^9%sgdgF?FEZ%#`6aZ(VluBWYnSJ=j!s%^_!rA#S}y*d47EKoq)V{=Slnr4@g( z(Q7R$WYQAlAcBIa2vJerg8b5%y2HbVD+bDbZhB{^?V2HS>Vlk)put8WQSEU_8hc2e)Jgd9Zv&zkU!!T9#p&$mXtHanKKAbs3=KVG0g zb*)tQW9j7=A6Et31O~2|vtyw{yc4H3dG9A#k>$=af8C9Q$Q;U`XK8Mx#-%h9$E1h{ z$piY7?trDG8N~IrUxZt>l5ff$aE-fzfNiHQN72}!%FqZ-*G+2>#>~&hRb*W9WZU^v z#X|+`4w4LKde8_cA-G_pudIvpP;L@ckke!x`d2yhe+69sbJ%kdimM}@Xg(`{srp`A z0Z{y1@ADU{W?CI9H7+8Pc1bI!N* z_6>w>hLpqDRUUpXbyMVU7=kKS0-*{ap2DEi6i!uA@ZsJUJmP7n|64qu_ir`zc&gip{%fRQyq^7i;8KmQ6 zX9zqO<`GMmCcas2hg5s7b^5_^JL_6MIq&!L(i=!tC3&^%EkWG3R{L%55+M&{O_Yx9 zY=2TISnitjG7Q|52x-*mkz~Hdu8N~JIVYYlghfpdv9g!ao^1^%SO#&u)qe~{O08aRANw3 zsBOP?%Fs*Y3!L8fHyJau-l~~S{yGc0s{P^vBhN0vf3aU|@vq#<3tVlr@1;)w1^-&A z|3j{&|M#%Pzb1RS|HnuraH>e1KtT7i>-Ud>Kfcf`eiko)h|_s$P`x2f#rXjcmoc}M z^ey6y{Zk@dfzpTZ5*YRPSX;KYG|x%0-I5ySM7IvsBF)>(ml=-;G*77zwDSLF)pQ-`K2Ezi?R-MX?`I zSPw0kQWZ+s_&jYnK<5>7S>|{#T9g&-_x5aOzX%$3c;UA1mm{KlT(#%w)2)FZx}1Bi z{|NY_D`?F-_dGVn!8wdKSP3ul-1up;74NNYkHKx6$m?H-PG)cpQH7W9nC&1@7(D1O z0*2*w`jw0OoceU?KGY6Oxz;0T!kM>iu8!I?T$)h9$b-8|oH}rLd7Z)xHP+m6jo582 zJ+CX6Qo1otD9yxZ|&cI1K`RHC}OgM;BDARF`eC9{y6fdVc z$Viodj3o{)IWeF{aoKUKwda-CQrv=f*CXI^^9tXjM{D2gA!)#3AkkuI_x-La-Qu+@ z{8qcdgC!@~=GDVeOzjX{Q@b>GNxN!Pa#Jv^Ws|^$oJf#eeU!CR4km{Ls$9?xm{Jv- zE?^YdYUu}H7z}W~l_ap`vJgyKWWW0h8m$Nv!T!kIM#=uJwWcsc_j?(lbYvV~&AYro zO;#l4R27FjvsX=`_U9j3isA#(DR0~=MLv9%XKjEmH4S@Ph0K{k&8FB@e28hCOeB?( z{FFyw&0?xD0FY1O&A@8~gXY9CMdDx`Y5Ge(K$QpoNrJMcC|y)^c@hL3EYAJ3LJdXS z0i!2&fCX`e6puI4ZG z(;-~Fhaev#q$T`!l)jet9)&`Zf(y6c?EM(0TSJ1>3L;*bj~Jti@pKDDV*7%&D9O;ZMD`T zt<6<_LCg^awg#@>j%uR5blj-mxCo~!8`GrCbC>UZ$ucqh31$m0ym|!8{{wP|cxBNm zrH_D{b_6{^Jh;~i_6S&(S`cN@iYfi!!#=6O7dN&2klP)NbOI$cVFcUV`O=2-}xD>$8`qTH4au;7eViGVMR0<#Zux zYU|gSmIHxg7D-1S`@Mg@y*TFjIpxxPU%kX$NhGf866@Oy?KyFyvoLmaP|4kMsraiN z{5WvG<4c-U`EOl63cyELMYomq(z~puD%SR{clte5`UfjD-Hm;LilSbncM4L)+OwrO z_C_AZTn}zje1+W-!#kG)<}-xvj$Muga(H)#|GY2VU+d3*cNwE3j?$rKMd=Ir-jE4( zl^R9Wyz3-Vo=dE4FOu}BGBxwQgtBC7)w_sofs4ZX4ah{vi_`NXDNa`*67!|Gib8v= z;K7!4r_KdbR*6MbndCW4+p>luL5Ev4(}w^eefsC(X(V4(t@TF9FL_%JOKR)yaiK?k z*mBfI+G>&129u;mcS4}g>Dj`(9~AkBq`v(QG@x8sQd19VuH=o;dD~1z9LecCcalbJH!v#~E}+WBcV+}B`x`c{LCvJ9~f zvMsQUR1d=Jd<#*8daH2!@qmj){krZrzu$BFbKo`mCE*jXPSFkINI3N_Uo+c9uJsU}lBGG*$NupL`~k9<_obEw%kr3xgXPi*ge zk1z5bnMhA7Y_)w8(-#b6LXQ9~xETOqfsh^t`gSSdZPSbXJVJ zPs48$*CYK1#JZcV$gc)>L3>zX*Jd+mXmX?gH*-$U%yBcn)34ZzbLwul6StOL1gABc2<4DY z=y`dW_QcjW(8j^>({ELNa$(KJMmD1xBp7vLHv7)c{7d;Q`MX;?qt*6X^&#ytN}MK` z2gu0dR9lJYzp{+W2A}0kYe903-N-5oH5NA?Zt2{I5$F3u`=NxRU z=(?i9i}wen7eUt*xYU*K2w)ADQgb$xFALV01?oj*2DiHX1@ZXKBm7P1<{8%=QKqf4 zC1NTnO^w|h20}AdI@7Jw8F$icl(B2idszo@7sJ5$8;(c$?JYJ)^7|D^Af5udP}u-P7PEmZbdf1DrQdL7R2Tz%BtReBnlsS9gl5V7;9A!l=M&}`mYUi|n9^*- z+^NYc>*|ZXfQ356b+iyOQs$izL6foOnLb}!-0n?WfFvwujwU%B8 z+)|*Q3c|b5MB>-R{(}i9tz3jO@FHnxENB2K=Y?OC$P6MpJhEibk_$BrY|l&} z4Z&*~2;d2Xl{U*m1WefPlDT7(kT_e=lZNoExC(22_5QLKq$HL>038ytDN)3WQ`Nru z`VWM^>%$8ryx`LKA)AwoL zA)+D4Iash7*#f=fX{Hwjub_<6fnIo95M>&AlEj({8liG+1<<=m+-Jepnls!Z+_X^! zn;@#HPolg(^fmJYSriL?N_t`}Nz`6>RGu^JYg`Q zKV$V0dzlVaKt6Rrd_%Q$&c)^6%xTcFLJ981JvPo!`i>nvX4lk3m<2Xgjf9VY+6g6F zM^>}pua+FI-sU2-c?#OIZdY_+oN%4J*K5Xts&#%heqFjrXCh}%-z_Yg?9!+Qk z)o6@b%qcB06lj<3n#(>qdvM>6doSZ zcrXI*K&q}WflnU;@+LYUU#5jxPeYsE2(f(sp7w7+EbF2EC1KVg{?lu25551kcHSfp zX9r7%MRVh65kN--tBL<5;{T}d|0|pUcu&F*@38AzT`0GD&LB_h72>^ln#bZ&Mj?7= zZnokx7D#)!o&SYx$HCDvr37QYYeir<(165C$gB!YF%!J8G3x>2}c8wGh$&UA1@ofxQyCnUtzy# zwW)2xpA|)9q34$U@)@H@!G)S#L3+lOun)2jLc92NIeOh4gahAHLYim_dc?@?>=>g6ZbVbbzV6$pvlm%Jf};bBa5%WbzlrOG9$~SQJZ+; zUn8PYbfRa#;LF!%_=k?kfUjYoA-pIzVX67c6dzos;&s8@Nb)7?O$7N{JPBbZSYBO<=p zYM^_&a~IyJN35Z$4FoAQ?gJ@^kqEWayJ_>R`-6QIlV<%$=)A)FY(qn=J<(GjYAEA5 z-26feCMxWhx5|WA!7UB|WpTxg^6ws4- zlK$Vd@;79lu>Zny?sW6iz#2?F*!hT2qRlfoAMr(x>VN;FU=WDU{m)&7Ms+`5p1O`; ziZOxeIUc#DoyC5`*N;Nnnjz0TAHIdDk*_3|CoyF?*>7K#?!dFe92bQ?cXfc>$i(~@d*@ePd!|ABhSP|0#Y#Jz<8^Y{A~0fKFD)9ht%7OhLcn{{}n7xn&A zPxIoRJ`VN#a-Z-QL?Bw@oQAl4Gpl%ssBCSbi1^}1z-yi$Gtq=0YyS04_nT65twL$- z;#mRe{F5cPOS>NL=O;=3$q1uCK1RoYvfS)YaFu}4AiO{b%6!M6=)rC_Fi1*3 zfpw_o68syyux*4ndd$9)4iFUNdsAiFygoP=H5H}mG&Skz<9ch4G&!Gl;b~#RHeg*G zygeg+Fg<)tCcj(z2xSB|ulQSKXnUR86d?tjHtC9rbBXcEc) zNSAMeEFcgGoUL>qAZ*FZtQns!_tBT ze=r9qWUI~8E?2Y{j(j1}-1+*J)OOMe41isceVhz7GAn$M_(=2yR!i+A(Mem(fKrR9|M83g*O?OgUm;!L zxpu=y=`Md;`HZI=1Bj}Kz#%Kes7BqP%caIz-s&_$*FGnZK1ra($ENUBBM-xj)6YgK zvN3;+6Cp%Bct_(K9RgCsgQVtWmY2MjU3h_F4lq4=o-xobEV0#SBHSh6S38@h+6cRh zLzlL!ggd!qY$Vr(2d=(Ik<;!*yn}1KEX#2;u&eC&lR2QAJ>&_`?yEDRvkjVI1(&D- z1DFBxT-?3-jyK8iH1BO^uLYmgQ(7mQ^w&wm-){ZT;A5n}L=1_2<+gW1Kq;JW-8boC zc+Espxcxw5X8Zo1`cL_`EbtJVH-JXBUNf_Jj72wfr+N+Pu9L_*;{rnc;)g=k;gIR~ zM&~iaYgf=B%VKDE`)t_?-GdA`fIC3sCTpXo!i4zLiP1L*0mrj7K5|G2(KBq}wbvDO zub+ZU*iZ+K`jNsj*(e?x_oq1mQ=w&v%{X zP?LEv#^6aLc^>QiRw}HnGmC$b+CqjJgOw`8Ussgwz zqgy&2XliD5?N9^?Vw@XALOu2-@^3kG%{JQtgbU4c|DmXIBQWh${duNDYlC4zM@uW| z=|v6a^2WF0v1X)5g8x9nT6Lq}#ygGC_Kspll`0;k{tnXlUc#m|oMG6XM?e)Hjvz%< zfs>}IBe243`|~4!^B+=Tcj|)+H3U2Y7+6!7jTOS??nMo$&r9~H{PeRJQ1Da(?H=_h z2dkjCcEetiuaAI^$&~Acb7L8?M}UVv1XXZ8y3?&b={s>Q)R@9ALC=j-v8_J-GV?X)F*`7@ocTH#+Jd_6Zh%Q}w$8`Hp@tCGvC+H?P#uAB6W%LBK} zdtSGiarmtcaVuI4;it@^nfO^TArife_4$%Nym>r5)7ulYD2#Ov?ySqb^cYu9E{b7< z9q1u4{JKL`wJBMcMnzw0%dTLWW3WW`0b3a!%GVERSp_H3IC83hH7WV;US2%RrC zdlhw^w|QvmDzWpkv$P5v&;a3`b&Hqr_)sTy6SYGS)7YXBsZLmWu!N1De;U;L;W?7a zb366Gi&x9N(%s#9*vHt8Ynych-!*hpSug z=g)iWZh}bp*d#dv4ClwTPl?QqdeqL7F5dix8{YHx%(zALIFtIM=k&Pc*j{z!Tfqh< zf*5Eb@$-!w;8of;ZKIEXp@%;a!Gy<452ud+8Wno5j;nCj*4XtC#LkFt3chl~Y^M95 z&DjS{efd-x(}Kr^8}tIJaI>iR8^e(zwmnF&8XBD%YW94X*vSc9p+&wsU!SsTL~n0j zrX0y;vm{yEH$CEIS0&Vu<1 zZYdO(BB8wool~7ntfDE6Zb|0ZFV4^}5x5Zs&5h&hCvx$%iB0Mgbc-ly=)Z5rz$wr% zpDG`I(kD`7YM7i!@d$|N$@V~3NT<^n!*0ny1Xj%-`QOf3dhm=EP_3aznC%M`6g7%| zP@{`G-WE_Z2YzIfD8H|g&?=%uaSIrbE&KWCEhu_fHq@ti5zFiLZT}&QZ_ha{lTa`6 z(oEd{>zf)AE8l2Z|CZ+w@cHvNGeY8kyHpD5r^zl+!R=mn&LHqo)M21z?>Y|AqOU2V zz`9iJMV`lGJn$QEBx*WaS&tnnqWFr(x}sAhk3T%|if@KDRF-g-Q-WRkD$*ZeDm^R3hk(I(L z#O?h`4(ZkO12UOob~QPJ%&FiBSP9F=QHPa9`VEk8shsw0zoAcTK#bhKn$@lBpW|*U zqP%w-Nt>As3ou&1A^zNGcMXJXqBPv2bP{}W6@Fv~!5dI*i{@chX}q4-f42U`X$#x# zqo*DQ2HU@~NwNFI5!98#AHTl|BN42Qe}UUWlsFOQ&R>D0l}TIxD;xe6G1GL-0!>jS z{2EGP_oN@7@H8~|)Bo9kV|ns%_q`lBo_cWxjb}{gG0v0XD^?=9L{{B;MtN0q=U&Wi zR=n3BCu-nfNr;rzw3+uoW`;U;LU8Hi`MABy@{Fp)RDGcTLzz#9=rfRhYMe}9=qdYC zTK;y~f*Y(yKzgG9oFxEG?*lv_nd2=CXTl!@zRnh9EB=i& z$4fY7HS%NEs9MVx>zE|iwg{Z&G+GVMc%HaB8Byenz$Hf1KFsrw20VBs$H!Ca;bKVKvnj zqtXtfw#YV|fz&S%^^o=>x*CECcex43`m2pCMXA;}Obx!Hz0N=b9KgvG z!KT1_-)F)k>!5s`U|yG%s9 zcHzcvl_=6~UT@gITWiYPlT)qu+Bf%Y{&h6S{p;%zz2J?dZ{f1&z(O?F3TaMEYc7t5 zomaWmp*oPg01Yr(DV=vu$nR<}wHZ7DB# zLR2T0K|_pRmilj{r*W{_jkDHEkly(4ssq2s+x%rvcZR>|c)a6;(FWN$DZ6(E{Nf#O zm6SYZ4AN2>^Hlb2SvWYLXJk2`$B~KCBhF1s=(=c_(3*F>d@{o@RrCNq$R`FI-S1_} zyw?6Jl5m(bij3#yst1tR5RIYXZ z+6p~k#>DiWBA>zs&i2~10j0MZ&(i!fT-I7;*iII<>eM3+*|Bu<6ubiZU0r9A^^Zk# z$Ul^3cptwml{pi;a}&$UbGJYO)6Kx`2{Y*#3^W{JlKVLBWePR#%@Cd-f`FZ>oa55k zib+D$z{VC*{I|=)Obd}uG!$e`U6cO~L-2O)cAi(Vdn5j+T?G3e!8457;#7Hk;X`W2 z8FeGuO5C^-ie!1P==VOu3lSob_CuxffY*JaOZ$vCjSWM zxhO25KlJ)c;&eTBvl>hrWmyehI-d*aHV;#||T`y|ThJhDmq zD8I_i|H)2P@~aT-@sA{`K92y&D67AbA)aG2{*+c#1Xk+w8?8WYr>=u57R4`)3%?S2 zWrMu07>jKS)WcTV-1U*}ED|=FpMv$(%X+n0{uF43!Nm=EP9QY95ye{Ttt6T{Xr>A2 zWKFa0Ie^Mg`2n1Brg)e;8v)vfl`A(~W{y*~X8AkV>gMTrT<5*aBVbOJ(oty!kJll~ z;kiAfeQewCz-4m>PY(3ZO|gsWASgpK~ZVNtq{2Cm@nA+UPmayE47U z9w!419mkue+-nzI8xTL%%#N_*!$k0owRFKxD({oS1*OX z$Q_qf>TG##1y(39GF`YE=P0y!zi;g9Z7J)}ci)9UAn8Rv?xjvH%VQk}8}ScH?xN!1 zTq<Ps3Un^T#{JZr-U9hI@sczY&d7WdU0aAcT1D2Q{8%ys({$KK|{fp z8a6Ox)w0h<6aO$maDsp7j(sdPP;s^!o%Oc8Riijkrr~RdlV1jL#Z|a1g}q0Gp(bq4 z2u~VUd8~-Xle7rPXqQy0Q+TY&Q)CVdREHT3@2|`#wepXO_Vxk6gI=AjBBLAl51`dM z0(uD)NyWl)>4xzm;0KaWE9)vA+yXkXS7`2Rg?zc-Wsdu-)VmTY=1FBDq{ z@(5@X*|!76gQphqqzFGR`m*iZmQx?Lc@nvX|513=O62VOitUn=at#r*D9XAW(KEhL z`yhQL+dnx&WFTdp zH%0%Xdcwtp+o&n)w@1rz*A`|bO z7P72I?{YpY4|P2Ptl}?AVb>I-zH#TQe-ZMH_f)5z@a@BK^w5Qv197VXZ58<`VtQ(o z^tzj3-(Q@a@6`ZL0Li^9q}o!(Em=pmDX*Cxoq_lEyldeuZQnjsgk^AiQSQuu(tfht zHR+cbo@Etl7Txz5wJBEJ%gwD)ut~et9qz@!Ni)v$o^Gu}X5>|aY={VkukkAQ`h4MH zt2ei)HKs)|`I-o)iQT~jo7zeVWissKW~Jz-V(M;LQuFMMPC;7waiG$IOAJ|SAc+DF zPo-ThC_t8-3f}Iy$s72g(yoGs=G?wfO9kqSy7-oMC03AEqj+REbr`z@Uzh*&tZm%( zwemdd$)V4y(FdDOohZDsq8+_cCwvMcG6%#X0Hrz``{^es-gzKGVW5TDV?ZFoNF38d zbT1<$(>t@a?o1m>cYYB6U>&RX&IjFd%kc~BVDV;wT3W3=!e|W zq4nYq04u`a!gGT?f6ltJ*w_ML`bax2{u_ zYqHT|!Z|EitjH>qF%_6pqUV(kSwo46Lsb5VG_n2RZE~N-TWzbDnB3!Sy>DeqEI8_t zGulR^J4WbLMZH=gUA-CIETSIv$|g=0nC{Xf#^AHARpc)~)wj9SBJH>D+WtYg#~OB! z2bKrtSDWw-o05)#aM~x_eU;;^AJ)tB>S*%&t^P1_S#IKOBhT;1i=uVItFtmLvjXiG z-SlEOmOCTS5~7TRuE=1R0$hPa%h!uGQg5jv;wZAD@|U*>TK`@BU#!*$|NNV&6J|gZ z0(6w8uu*v(%cMn8nn?S--+Rp&0#Hed__|4M%}sC|DUv;Tp$IldSb~uUS8E9lV7oxM zWXFmF5L2RdYz|KDTb>sM8UE~P1*yT8P8oBp3w?%0H5EO7vG4~*og8~&#^@cP8)HNw zWA|lk#fioqrNY2z@n6NYDNJ5e*cyio?x3@bv#8W;{<&4Ie3>kzU-pM~pDG*%nKR*B z8%<4Vwy_Hzab2XJ8nv9|KF`qf0U)Sow2))B$)rN>;5H-1mr6&98inj~x(Pff*do*Y z)r5yBvMU;yGPw#eKf*(^!~=QBX2O4DhP{V%MZVMFd-+nIAn`fdORdm{Qf(ahxBsi3 z_Bg;75|sPqps$_irOoiKQvCgYO@aRlUp@^b)cnL3bM%L~>zf$LK)n9;sUB#S)WiA? zB#=og4R8A@qvXiSe<;!Bc#iumUXo=5!t#5?#{BX6IbnX&Uo3$uqI2v~ca4|s;TMN& zK05pn8A(Rx=YxLCFp(l!^uF3BZ%4g4X<)f_{j(0N9PI1a^-AZI)_}bPU%DJ`HHbb& zCo!Ij=Me(2@Tf682m))^>LE(;urEE0_mH4vZzHG+!4U9AymQii=h_#~Z^ol(|Kqou zGm`qI1F-Ng>RVeJ>Fq=uwa)%DFf4e>MGQLjwi5}e6Ai}?35P6WNj?XnB||ae7qpxiomvA17U%J^b2|PO zhOjkj6F-=NoMz+GQr!Uh?JF;UaW-RH4j)gXOrsMM96A)Y@jdpCYKqsR)8s}w>Q*^_ zua>hM>+*#<{gy2(fpZ;76ZK|tYXb8pG0w3X>SHs0 zBLZSe{4e&g|M=s_4wfnoBh;Rh4ib8X#^ZcD`{JP((b&KqA7IP*Jj@(dDd^uSS>b

    { zjB_PbT_*EYcAnd3h)-o|pacW#;B6@v{w#l(ef)3bE;y=QF;ju*<$2#p4>GY-xW}*H z;-aSK5M=MlK-lbZo{|PpC6ap;zcPVWKQT#4s0DRQFSOzyK`8?pj4h7*v}PD15h+sn zH86h{t6gtY6XJ8*5#vqfc+Zv++(diJr)fHMf>BzKAaVG!WhTn@B==89Uu{|Uy|JhH z(ZNjpfSh&6Y9Yr3C?a5ETFD)%PDKe4x*E{C1&e+d#{A9G#ix6k zZA@D+?8bv}lIBf4biAycGxJT~B}H9OGjEbtw*X^c`LKt=a3hPngAV5Hd&QN-&-_xy z?RD=ZQSR0WmpQM<@=tqhb9E->vPDE0UOxi(%p<)D1EYC{Q#_mwZ%p?sRa~mv8*}G1 zzzObDO0Mb3_j=M>9en?As*ZNQ?otG}b}B8HS$ZN`pV*TRI>Aq;M^nAAKiFA_SM}cc2SaLg6T$jevE^h4oMovFDg-m%< z?jaVNrmJ|-_fnYWu&N~pO>+}7z@-TR@FK~&bfxgym9%D-I*86tS`oXjW3z5Xj<8Bha6?#FKoeM=8~#3S(3uXTXW)G z!!NmfmCt}3|H)K7f0vm94-3=gwRKt7jGa|CNvC=kIsmkny|cS3N-)?Kk?P0Oe#Nc6 zxtty2j31-S;9X#uH6&c}4Z>Esb8Hbl{+BGrEci{8!+3q~nK8X_|FLK_c)Yng->_Sf zYr6An`F1<;_zCWhVG)tV@_ZxyCyZs{B4tw@G39;hB?LMqdAcpv z`+*oyLQyt2pJkRy`Ex9JA$n`Bz)a<2an%$MPM=R+Rreo%&64+yxsIr3ksE}FOg2Ns zac`$-g1=7qaK#4ol?=jNcSrxSVWKW3$yHIy0_Vj&k0p95#tW2#-Ku7|5r zfbO$q@TsTt`pJ?YGh;3yvx1baWL>ZytG1MVl^5Ed1$}CB>>j+Y67wFOh*!ChE(&6G z?6mYWx1|wY9rf`mP7?-5_?o(|lz(3_Ayu!~t)F45k(^(@a&grRO(so#SLkAK^@lLr z&dhXr1ajwM1<^=zm>-|UOoXkR#r9D;0_(TBHeO|xhi`?Xrrbpcf2;u0TMdue zEoUHh0_bhh;|m9BE(81*vww?*iIF8ctEHa)NShHlGKb;%XP8E&8LN3E<+?^Q_8VrMP{#F@UWT)tt5`cjB4 zpR`uH?p@ymRG*g3SaM`r4#zgaygx99^yA`P1x#w?fex?sgLM=m++Coy4bxENJT$e7 z&Lc=|QLV$mRjsPE5Rup6#R_`4k0^jw;?^0FrM+_~tA!k2n3n*e)^f?%dqW#Wl za=jA8o_LtzciV5WgUE6(i%qq!tnree)lYA0Y^j%>4dUj8myK*&LmjbyF|lE;zdi=b zN|Wvwf`4zgoq;f?0lS?&kGDl$P2v`%SMNE^1|@{-bWV?K9H*H_Q;xYQeKeEgJn}y@ zUv}+@$o#fr!*$=vZFiqq6jS@jyv_LgDeR}6MO1|}$DuF2WN1w34r)kkYx#F7_bm?1 zb?APG0G=rSpy*;t<=5uh*&nMQxy`beMQCnQRr|v%R5VebSw-0q*TDA@JY*kh%EI8L zk#I|n6x0K!d3|*B5jw^XVKvP6;UMYTOV{M%^J=9ct-{rj9r0l8obC7&-6tztQaq0o zW#XL4c^kat;qolF8Q;c<3CI&Wv_5f*y7Bv4bS*l$N@^z>xmwjWZ1tlLQ{_HP{C$A2 zV`(Oa4;dD?VRlMm4lZg4$&1Vq(iCXrvN84WuZJ*iC1xht!i;!^$sSZzZwXxr=p+(D z44D{4NTLab1%f1m_ce9Vi%8R%G-~rCNR;HP>q|A;r^_);YHKADQgS*NiCP^oeoR7AJvAfZt%%MNB7XS%wK?s zRpU08*8D`OCek0{`)PBm#~iPV>^QUUtYBVrZQ(A&hbJq#zGN=r@Z?yw;fH_z@x_n( zK@l1^^5JC+rLPb3?mEJNmu03bY}h{Jtm+pkkC?fw3E;5!s)*E=bYFM9jr?Ri71{Ou ze`XzM0M8|z9=>MWUlZ`p1mDdSQHjoMjZK?tlD6V)uGSzb94$V`oW`9?x$Og#LQpohvJg2F9o97B>j?qLUSp5Cq zwMW4b%GAzXIc#TVXSJ~>*_IOTE%v1F_DZ~qMMy+ja@r5f-I=-q)|A9%ZYl6I5 z;ZPE21clh)>dUFYoSe-)0!Si~clxiaZ*34?C{iT9KI!%nV5|Md&BA^mH0ULwg)iVnC3Q~ zz2HXGFD~uI5pGQWMzWo*X58QN1UZPVYwZsMx|T0yb&*80`+ysu-uRkU)ufzV(a9BS z%M&dZB9mc(ldGDPh3o!$h}_x!Lj&Vwq$a3C49JW&b$AlDjKLLJ=pX7iElWsQ64NUn z^Kl$YJ%CjdAYBk5yN!;AMU(@b-Y-FPzs{>PhoCl!jnaWzs(J79fGqjMsF5d{$@*b$ zN>1N#6w}i5$8i z_5y0tD?>XVNuB3PdSOZ-zM-@MYVfbgadX^B3Q*USyKoK|;Q_WpVxk^#nzfoteeRLy zw5qNd+>c(t-47R7f8d=^EGNdSDU_mFqcgM7W4F1_^`UhY?48B3yk~#-q{+` zC6t2E*4?gYb9pT3%X(^Snm9i4M>bu4X4c;5y&}17ElAugGq?tOBtw>CTmqpYXcF}> z80p-2_|S7$OP<5rnpvv#ER6oov}K3KH5Ht*tjt52un3}*zjK8%4RlJNV(PP1PysPv53H?~IR`>;`4+7RB3Q)z`C_nuMatClaV z*J=s#6$D)RV;LHe_6`Iv_$xpn(OiUrfrkiJw04XnORqX%Myf?_O=bVn;eO&2za*kr zm)wc|=!8kAHwA$W9a+zM;KQ_;iSH$uN~l@@zrfbw(6|-f9uJET1;drWn|YEW=R|c; z&(HJnz^iS7tnmd&;WR{Q!+ZL%ZMv}21N}|BLZy^GeUbX4#uS^w@^6wcfH8n-ttOFh z=iK*`zyRc~6>Mu@lesPD{;zZaxMW%f3f8b2R*`5Dwen>0-fV%aWnN(vih zy<=CWpC@ngtWeKVYwhaFpB`7{9dsB=V{q3&Bd4iZ6p^8ah+d)I=a~>9S0| zPl@NtX}dAYw6(hTtXV(`_D~{II!-0Ya-&8;co?R>5j^b0xwzc~4SD@Vif+|;G$hud z4EGtPPpfpri1U(j$o;N*)tyCU+P!M#zB$40I%oZ|N-aI_?BpMgK9Q8gmW+JS=9tC? ztvz}f7|PQAGlD7A#guq&OXvJ>`WG1x_}gzCaR3Fa8Uw>^E((VIj)o4y9eh@TtD*JH z*GX%y&MW7aJa00Wp(SmGQGZ3?(zLyw@J$2Z_Ap{q$tN+$r4wZ##u|uvPTN#t7wy(^ z#|@u7_@V4|X`D-`3AJW;))0^uYATrr_uX=l@f1cI?Dl3$JFTDNxS%)Ujuk-IJ z`9peV4$TvLi*O?7kd?S7QwNY5$L9yiHQVL~W}SoaG~^zB1YZqDUFw||TDo_CJL5~O zrsNrf@;0IC#N<<1?=2P(5#ogyRGJLS{z1V>yk;|bDweB=Mw+fo^zeM=-f8zp@%F?! zV@rwF*w;tg(Qm}tGI_L!&|9$N}KvIHm9?HQrq&+Ci*_yG5?I;ow{tW{R zi*LNDZVDqn8#^TV|Ry7j4$RM%}OUb&#@6FxJ}!n`;FXkl`1o+RFt$>hE*XDdsep zcO&q9UUXpmIn(Lg?>JUnuGAf>3$pll`B0Dm%^D8r>L!uAxlx0bc)w#t zUi2e4Zc3Q~kpJ;Q>k3Q~8?+oh)tmJdQkr?2shY}%%1LP$d)t`n|2_<GnxoiELdKRC0v3QPRiVjfQvOSs`Cg!{wVaOHSFbV&Tv=_erar$h<8p`>HIQX*_eyxMm~2M(+ObxJ zC9sKo4f5d-RO~@^l@*(@Ju?&#pGG|vaj2~%aw4E85dL-cn7Xj50SKE}-(;E?Ep&m3 z+_oRu$b369cndAtzDvHDx|bvl@4owoGg4>#Z8`9^!jHIqxr3fmSlkf5iX1l>us`dW zVw(DUEQsQOAv1nA;I*DoM%xVNqcqDN|L2-G-5vd3__M~)YVSNrEiO(DO2%hlbT6L0 z|Bue_jZ?n7eeoFQ|Aw9&JoEl-uHo%-KI0ve%ReECxx2R%W7mZV*mXgA3&5%{rmc4} zM7j$Jh!!%KLT}2TJ%Q_qxgpp;P7mUHh!ez}U_m0$ZA}oxf}GH~q%H_oz0q53Ux+Ec4a#O*vefUtYes^Zg5`R6KI+=nDM8<Xept&H^iI$jQ}oyG+tJ z$^TOTAE(qB+)*nlDh34_wA>QwEQ1A-_aHF6JDF_)hS-KQAPaef; zrG13YUCUCUoUdQC3|q1@jh9Pi!bKL>=XPq@5t^L;eW#oYY>f3-`lM7?;u<;nMxQzV z#ph@4I%-qX9}~VJyNE2G*~aa;yH5q%8GEtKrti)-y025b-t^yhcXt~gWZ)x9D!eIo znckc~Ft5WW^xMdGqv)uIJ6LFWhR%lVbhR&8n%ruJnqtSz=)sp^(^=+KqlMLhG>!*= zKHDpn7^gws#4pXxVb4)wq48e_l|woFl(tZ}nzCP?=jR=UTU%OfJj_QQ-&&@LZR1i7 z^bgKo=nA}35m`rXkV~%W8*dB_@jMKbSZ|P5D9z9vZY>QidyWJ;ST>TQ+NA&9#PUA1 z1`9pxl9~temrC{N?TrR}pAystvl9UJT@+N({E1)Mq$ZIOJrxU#f~X`_E}V&;PDSHq z`q!6tnFU8!Z56or?+TK@U}j$iK4g>lrd^Z#GI>;8!Wx(5N9z!@5@hCcF_(F({v(IL zG~ERL52v?ZDwTd=1zqTcLQYGPL@&c*EKO`MGol=?-NuJ6{o{DU$?&rTG4!(%A&%p! z`Czl&jG{Hq`o@^~da)bBC5x(7rrl`=H(LK3UacD^;Ii}+CMHf(-w2dZtLIda3-gFn zGH1Q|OAS9Vi7CE^OyLW9gXIv(Mz4eX?0XHFz`t%b`~zzq{*c;=w&w3cC(dS9-BJCA z+@=EW*3fBK*9@9gXTARy__z%k92-I^o-{9N>~3@i4;(p}UsL z{7owOWe-TlW+P;ThncXvrml-H+6?ofZDZg$Y(4k|^P?k^aRAzR;&YKe*Q_sUh9 zm8GNT_?VnN6_#bQ7G3q$;72JvKv$)AYcX~Y)QW=46C9^*@!tV6`z6N~Ue0MH*kd-t zi&<%-&Eb#?LF7AT>8_W5>sv+j&cxE4CQhb*aATIWRtBH4i~?55177RbzFA7vPNo|; zO%FOWK1@I!BSgMAY00%Y?@lG?t1*O(Pa~qbvMHSU=>fQH6j$07*X1wq7g-~XuWP2z@nfEiOvv_qLtMG_5 zcdvbO@m`P1I3I?EWb9rP68()SDb(0Zl0%hbl05}2Zas1yJM`gG zWf2N}cjMK2m_)nK=AG;}o6gg)AZBBeh!ecY)Y_ZtB3IvbOJ4PaMbW9u|0gOMy-wcM zb?W>~kEP&wz%rJ9UQQFxzSh4uw#NSSiF#;U3s#}!qYce><=I^G0jZ?x4LD;c&5Eu? zyc+Z3?%%)8=R)=VM={HTf}8M)mxuE=1~yFS&&OwTig0Y`RuB5)EZ|A)J*N^dnt1?w zk=1tD%r-LJ{o+88WcfN~Bag`@e8e~GIf1i_tR`chq*88N8}jZbu^4V*lZE^Lnb~7LkGvDnj=iqS;5kEUjvwDpAN__~i!i z$Ol0q8vlWd= zBMRNKG4K2&?Iw$q7QoZcOTWoJtU1gFb)aGWT-Cv2-e0I?y(2WUv@Nxz$44{zLGC`G z>gBm5$+)JT(Y}kRY5V}s6Nk6(Pjh=AvXfrS!3ShDQts$B)H^HT+1mVT)wTi@W`3y! z6$d(}l^k|{4-pJVXc+;wz`VC2AC3zFL1*SqLDU5bney*27QAcss&2z(D|A{PNN@2X z-JNw?Q+53Q;ZP(Fnm$~lc8z7Qn8QLdcr_}!+zX-?*E5g|0c5znQ$^N48*SsTFLM(S z0lQrS7PrpMAyW54$b@SU8P>WR5=}XNoS^~Det7*@Nne2 zeS3sD(o{#!31)#&vh_cVo_aFgEbZKE4?=HFs7RS~AeD z1#G?u^7nTSRxFvZ;G~`gIw5H%pbYE@d3hC@`i+|_(Y@yi6 zL-^!)zT1}{*uw0TCgpbi#KoNi9Be~qqZ)zIC?+YrxbnKu9!Dg6?LYq0MC1R=0Y=|9 z9%$RIOk*myNGDrOFYII(x0Q87NIFa}+ZOZAZ_$ZPBmVdgXZYF_t8AX8*lHXvHG2M}!)jQ-&v@|Es9&P+gYO<`!x5mLL6Dv; zFE8okiQyal+?lJuddvP*r?SBP4h*&vgN@vN3GV<)n>j?Zux_Hku+X|oy4U05^l%x1DC#4Ve4AX}R zXGC${e`Ac{LgDr`f;%EjzJD)q6c>$xgR2h#!$qEyQYAN=p1c>E|8P`|l27dF_ZDFe zp;*U%IDRjTRvq^YTORVZio>V5ZgI60^;35;*!QX=+`>V(MKSO^O247o%r5Ur{N6LEpi)!uv-J>mj z)XZa*3co};8ZxzUNhM+&R{;VuM@@7)+Cg`E{Y-KDI5UCzl z{`AIOtQDGB&KE2{tfYHj;dPAlykVM35`#e-8xluSw5DPDzgr(l@;jq2h-0|h?Dy7R zSbY0cn|jJEa=2l0s!DTVlxo<#p2tbU%zBtVeai*&t>z3LyTebj@bG3omN5gzm%%NxRrL;_z6Q|Vd5>a%72!=@@5!BuM8lb^OFu5ZNnd$r=Oa(T(dV(K)i zFXX4N*ddfH5C4?dFl~~1a9h%sTMF{czMNm1?~IzqjgCdNNAZ`h*`!U-QpJfqbNSIdp><|T4X|JtFTeS4cAJ9S6f2{#N<+$eG7J%v zEzp@bRdbGUW7e7{|Afq2pA01qD1%=U+Dl^|1$sM$)0h}5W73jYJMD%KmfiMS ziFL9qCqrXu`h!1PtH#&h{PD4at!QHG5O zy~M@QTeiZrGU(2yiLtpjoX$Y0Z1Tl$QF7gKotpO)5JLX&>LT^KwtgtTQee@b%~rho zrPM{NMxuwmNrW7rCaMkT*}bfxbrTE$)tiZWs$u11#}&5PfiPHVI(SZ7BbRyC>Vp*xKU8L zWhtC&w54xT<(bTt=_6h{ByXfenu?UYjB)oir43D_W2Z1o_?=N6a+)??Dv%{f{=tr_ z_}F4Q0aMXdveqORKQ}b-Q^>Er)YC{^Iwogu0OZq6hr4OfM>)zAOSy5^+9Ao+$ME!k^kl_l(4pIX zW!Fvq$QnP&;Tv4gZbd}Bqu(^_<)z1WKar68k@1jHjU|6_L!{|?1cNlgRh@Tq{P zUp7*bVV1)Wz6Nfr+4eCdJxZO@nI2ijV-h!BR$hnL2&TSe6v^hpTDq_Ut*vgaf97`J zjj-oG9JlawfXowp%KC%*{%q;u_Y2_G5wqCl<3yiDo+5W%3A2=^5})r(?LNN^^f3#e zG>d|Z%am$#ZY%mUR-+5FkI=_xKZ7FGqO-xE^!4Tb<3r@Gd$xD5ydM%cb-Az#Bv>I- zR5eCiP$7uP(A)FdOoL(eq&}&ig~h(wtrJ6BpG+?qezM-Sdb&v(0F|Uh8*>xaic2b4}*{$XX0O|B`{hW87?{>9= z<+(+~M4M|D-^q!d!$gZ~SHoDnm+!zskubPmBRhogo@<#9*#tAxR@Tcx6Lj`^68%! z!gDQVyL7>P@;vM1Wt6x-@AndWff+ltBT~KMg|j7K0`E+&bnmzRZ*XDpW&J&BvjHtw zl#XcO)F!s5ov}Vw-~i(dSU?8H9PB=KFLV9e`M~kz%Udy%k^kx4{EwTsto}RVzoKQQ z`v1+rYr^DP8SCp+;Va{n9{$j4Z@Y|SyvY!0E$7|CW6;mCqli;6>;nbSWql-0c<)W^ z=|7xLRn{_>@n=|JlS>9#Ei?F|V?BvNiOGSo)E6lOMJ#Gt^3>=sLkXhdC-7%*P*}duYT;Ov;M>R zLz2So<45xr=K}HAU?3|wm)ljHGXArv9Meo-00b(LTDt4L@q|-BnvZ3%$upzsP5 z;3AEf%6OR59E&(uEj%?LTk7`HP8~|fOd&p$3U^ua+pbX%wXwaewL}rNK!NGIZyl6N!Fe7b36DCXDUy4TUjy=aP0B`QlMctCq%av z44-|tad*G#DfGx1)cppt0#p!(0@45s8^{AM$C>f5;t-mdE8{5$C!}y>@(H+ z=C-84JH~8Js6q;QAV*%h(!n_^v*W2Nz4tBVR~_iB zOBS{Xz4N?Xw%6&?{&VXX#?=|?I=IU0buG`QH2$y@a-MktyP!P<_V&?)#V|MXBJ;tnz{7fs z0;bKSEK!Dv8=Gv0cQ1}l=BHg6I?G%DSI^$7`utv#n zU*+&?qWslX)5HsLg?~61cY6(2u9dh9$l{orUwNQKu&FcMR%s%h$Tt7-Q_T!YxW-cp}v zO35?4GI%#NXZ;hDuATx$)Fq>7{}1P8tfj3`^6JEIqy+9r=w`BQxK?e++B&i={}-L< zgkGAjDLW9qxJCiP^;;UG4?~IOQ5S`vAki~a-dR^KoStp?#9lR%3YsvnfaNYPI1MYP zuj6)n7;sUSD2yNVG_Zy2Y5v#%uOv@Z1Jz!OB3rn2Lj1UGTa?nWI1!hwr9Xr&JH@<5Fu1e;;#w#DHZBYw)4G5ar8?)Oe%L7w ztQZP$pbexcAt=Ji9I01Dsccm#hDspW4X=jd{Ji-|RC9leD1WatTbr4$X~3U6b^0UWSD;zy%O+lo=+g-(dyN_8iY7F^ z(4N1Ro97Y%WNc`ad0#;AL1Hf6)<(XH);U)h0TV_J{_szwVyzir_W$ca<2cJJyZn5% z@%bp|IpT^N1pu%+Ki^wz`h9r;Zw1#oKHY;p#-Ntn2Td=XH4z>G%0M^Dmr3tT;G(Cm z)urY>aeZIVR9+SvRzIcR-ppgTC$L*`16PpRdOZ$qOH>b$A}yIo9IT7AX_s?c6JWXn1eUs>QzO3aa^ppaoIU3| zx2KyYK3GWNB?r5R+$I)700fMR7@tZQOSU2DbZ4}k3#xRY<(HG~!ZIFe{n~?Zy*&MS zumc_#mx0SaK*)+^0r2pHyC)=r&fwr<=~A}*_Jiv>A3D;(%E0MzT3+x31`bzm?U+nn zB+n6p5qGuwX27%vFCK^FbCqtRBiujwh@nL`t9h_>&DUkS7{p1d3wPi6cM0^3s05bc zB01}mgch9yX#Dw^M44D2uqV$+owa%9w^ZmEdfd&d($MGVRfd7cDFumrKZOtOFT7`^ z*iAZv7_WZem&2}h2Ie|9zRwK8$yB5SU!Uo+?|fGU^t#M6#(q;b8^QbGk~fbjR52U#?cq$xXl@_L@E zOymp%xfWh0CD^?1JaIS=)(I(Hnc3-ovhzNfIY=&>D1nL!!w(qcCGe;^#6v=4*_$%c z`)UfEz4)lj*Lk;-RDnC)@9Il-5N6>V^}3^8QkJ@=UJEZ>H*0MwJIxKDzc)mgw(pW( zr`PaY9}ED&Z>MkO4dyhe=mXS!d4_BGtC@6s%vBA?d=KokA>2s7(0v<{>&GVE)*=|} zV+dXb$!!*>JR7E}u8g_S{~;(3P5lUuf)G0H1<{dxAB&a}3aUD@+62e^6t53IIJ^(w zfxwzYQr#50PV&AdZ2n;opq>47dLg|}+)7r#K31QFojQ#_lI}Xrd!>*I8#uhA~5;0F9^F_-Tz)$D$y5Gb58HLX3OtV1Q=QK^ZMyrFwFD|0g0aaKYno$ zJgfS%jNJx}?odiCEcPb}^Yg&GeQ$axrbPEdX_L6t zz2;XhWisH$o{;bXV?x04LVo@m@j1HgG)3hy4s9)>XhbsEj_cqcDGPH>_({xL%!$9> zr3ghcqAIwMoy#(Qxvo|K?-R(={-$#yaTa#fL+2F+)L&^rG*?R)Dp+jGY3qyRrFM`Q ztu??zMNe`zU6gt5(A3v87pyR{#yXK?-a!0>_rd;5z9cp!v17#(J{H;d@AOW^14epU zU!i4yOMpYi`&AXk4h^15`N1vgiC?CeENetdh0@9@@+ zl<-yN)YS`3mQbbdu(PsFsY_i#bIH~tuEnLD0GDyOT+GCEEqjR$6`2m>|E@R1efc4D z9reMOo5)+@wa;b!mh8R8LShXjcr|0#>zQQmrx|Vmfw+<#a#Y(JW0L`M%N&w$>k{b- zW#4!1+`iF=ORe|W1qtTXry?hPj2>9JMxmC`83w>(RKHyth0DU23{NBkC9N)9Eu(ta zvD=j7xjf&W!)Epei@x0Sbaeu>_^STE{eFypMwBi!E54H0Dgkl=9m@z_19^FswadC+ z6Bh3oE@-B7Q1R0~TWYIH6M4CSapajUjV#x|*ZJFz@{gYIz2FpO_H*Mbz3yK_o6yOz z3rhs-wa#ys%=NV>>X$XbZMqNMm0j>E>>~==MD$-3z!>mzX_&&^vP+*^=(4Gqmw{vw53$W6b(2%4W~P2U2^x9R9wnt(4pGpQuy zKHKJ_-)1NUNG(bIygGg_WENtJ7grBRze?CVEv3VeTMP%!#eQQ zyM}BMRPimv4u^tH)t~@SXz_>0+(-GPV33JUbbMku>7o>sFsSHaEOY`PdN($GpCeP@ z?Zuywbir#AKQm%Lvp7zWcyb35DS*`L{~G8m*=-P1t8coTNfEE7U>2_|o*$`~%=sQv zD8r*KIEj6}?0*}F9(xeEln_+@aO!TkYaz)xONI3W&eD(F7@>{wq$&g~xz(10%dhAt z$PYU{ZOQwX906>)x806-a+q=dc+izoN0}JtbA}A3VOU-|Tm8e){Y8;ew7e-Rvl`C_C0whQIQQz8v?9; zy}K_6$R2kJo6-QtqRtA-UB^5JJulv@;%t3o`ZSTKpqXk?#!1`0j~A*bdj(73ffeFS z?Qb=klf$;~;#y|*0P#M;N>Q8e`+6@y3mcZ_ODJRY?)0wcX@=yl(`FW97yUHyrYqM4 z3gHygI#OKuY=Md~QQ>+FAdz_tpV~FJ<>#fU!%Faivb$S23DRikB9+SNcIZ6Bfr@pi zBrM>s*NO^55#&!N9MPJ4vgNNXh4tQo2gesNEi1G1?kThBUjk*Q6otgaDcGyyZMKJ; zl-@`^-gi^VX%*LNty8{slO7F!X0Sdp0u(YhTo#(Q+&3|iAa^G-s&uh;aZw5k0E?1G z-FkyOMK+TU3H_0m5MLz7SD@BW)>~QlRQqGP{l{N|or=&*nc+dh4!qLy9Ko6YL@G@o zAlLML_+XmhF8C#==5PLHj!-Z?71N&K7f}lb?_(w}_hD&vYurttbQ8XO%O0T8PZ)D7 z53-G~&eLB+RP-pcV(orADc-FUU|7=DtMsL?L@b>+aszPRaY?v-|1oE$Bv^B+HaSts zxr2~Y#7DgK_3|i(fD!*$OWxLdABjD(3zYEhmlTSOw@olh#>cZ_GYzhbVJywrlKBm< zjcNLNnAHnk`^pHnqguOUN|g>+vj#Pe*ihGN5Y)(Cy?RGquB7TntQosO3-2j*3XKak%e zcP{a}i4~SEJ{Hi=oi=CngCV|Wr$aa8m{*@+3-s|`Aw|8CkR=+>f6p}ReH+5n%vhWf z2m9!!2;^_g@%t!LD~Aj!!}o01viVY=%YMn@zJh|&WztD^bLp3v#nP*e`;qJ%+VmCS zgA-qDAeOqY!w}jf*0(iK!%`}x=tJ94rChi!xjbE^PJSE6+oAIQx0}y91s02mHCb&i z(YIEbjV|_e-`M(r{%~41{(gKjI<_Bd%^-`kM11&bU{!l(5)WU=8ebLUu(gG!K_njp zJA|mVu&!U=7d+aLCX64xPFgo)tz8Pj67rZPSD!}Ay#n?It(}uSoz5HV^kdu*>$SlW z_M*aVi`cZOSNcs{Dr`H2`PtNR>OWeJhQ`zejzAaI>d$6^$hOQ=UGEHl`*Y8-i{+9lWTc~cy5pM_ScMjLPRj@qa{^6}R`oBQffR~w#@U#0># z{f8r{xvoSM;yyW7$UN6sp&6x_525ZwKJVTPX+yqRm$X-%$m$H6{oUr-G}-r(vWxpN zr1}xFWWQ#30eL!0=Rkqbm=zgl`r?Y`=bmCH{`!zu!yM2y{KyQ_Q=%BE@~PE_FnGh) zLj8m8+xWD;PxV%St+b?w1AS8-y1Kl#nFj&2xCh!r9}H}+B$YnU-3=E>SM)IU9NSPz zeZYIw?P7Zq`80t#MJ8MC14Qta{!f{A31ZT;@ecA}!I`;!p0?GzCzG893fPq|@i2}X znB3McPUA)SF=Wjflp>-!-Q`v~E2iyc?u%NyS>vI@N7Q@B=Y^!VaTVSrVS{PMtdK9s zg0~dpp&F$-wj-t=UP%j^@TSM;vaU-g96dzotnpPw&2*HUlfmAF?>;Y5kNO1Ygn|ymF zfLBLG`v*^O`74~=%Gzn`L3-17T5!yS=m?)u>;z^6Y=g#bQSV!BTO=oiYmW@Ym4up7 z;Tv_P_5+AF>nn6x-By-4!x;80U?er-{vf$O4j($)l42OmKgD&E{M@NS?I=XOfBp1- zUCRF_Se-rN#hhktp5A2Fow36_V}$BS2A66aROx-}r3M$HRW#jyV`{Dq?C}z6g6_C; zdyC!KX?W+d2qzxBy|~S0d#6JxVkLSVm%Gi&-MQopE!LY}?lc|X0~cBLATG+t&3c|N z7ZmZd5PGnF>o62y^CR)$MlF0uogu~9SIDKA*)yf0pLD(22TO31aQ8_mPE9yY1qEm?YhE2N4H>qB5+quG|y@cx`^Evq;YAx@w<~EOoX28db;&H z)of6GwsRfu{{B2u=pT-CXKAXYeMNlQm#j;?G>ItpH=$9g13v}cPt$MRIpb40#4(Wn101 zsGS~WfdVz0jSKB=T*Vi)B3n8g)a1=&y5OVqsHr;}D&GDddCwn?}bX@D~dP6XY zjQFqfE4So~25{|4vgd_(%f>1$a#8Om|6E073?biG9qaZ!vnF}xCCoAI#8dypRQ;Lw zVfEVpA9JC&S`c>o#)q-nP1C3o9IEBTt3g-Qhc1M(`7CXDQ@CGyAhtzGm|fLFIhLtt zkbtsLKburaBW-uc_5(FvY#x+FGE?<3>`md_7A6mY0yFgonzLw z{L3}G=;SAy0}IS~A|b6B6O*(T?%(ZI=Qw;#_ljR*?(0UI2!h^{@0jjW^?kgRR+T21 zx?&^;E7gA2=x<*GrmDX^I-K*`#44g6e*_@+P+-2gkS^;ciI9rp#GpP=S)8PxCO_iGaX zOgG;DaMZFvG_>cPC;x|xx9p07@uEjDQ4x5MZk3XjZV(WV?k;Ht7#ao`LZy_H?v5d( zr)ZI8Xoeg@V(6h68t?Ca*SdGzweA-jh{)ogUJb^4 zGwFO}RPMtlbN`(pQf&Gk!6O?Wer6QYY;qDD-dEpTufV^W)pTFH0)rqnba6#yP;?71PLk!Gf=3e~M5&RU{9 zZmitayA`bdNB}+`-{iwvA^`h>hk~gI-wMVhs{F{U&DT8{UFsY?aQo#OSlSpR1kFa@ z^e4xpPW_*NnNk}~?6#s%V7+;ZH&XvuTkxTq2H;iHD~8!u(z~tVvw0%C-J*P8{b?p+ zZw|<><>|IIJ~rfBl{u%4Z_*8e@Xlt7PP2XyQBoC_h|ia#p2>c3UX{EXBrP$aGeu(PR;-Rto~1&x z!qbi>92vE;9sirRkk8ow9Jo8QJJR*dMT-LL_z#PY7)Q)MEhc}$lTUTn3FKksmt`9oCDH~AQpO<<%BUyBn+T0T{?cV))`oi(*jdAciY1~cn~n&+3- zPi5^Rc15_(K34!hFIWQs2Xwm~UzZ4@s?5nMTT%z#ur~MnoSLobhP~orO>rlA;ihFy zC$}ab_6>JXqxweK$aW(B`*rr2%{l+NyXTsdR0;wZ)x9jGV6rcmF6J#rCgDlehc`$Z zS%CLlV^sZpWxYlMv({TVtfs80*RGz zB;f~^!BQiP0}HWA?Z0V>7&h117B1@VAuK;B>%EE~0FE-5>LQ+IR;6`$JX3<3l_Wfc z^P2v~Bl9v`#q~>h#5X?!n=G!h+IgN%eFp0osUYWd_>5>PJiLG3vYIxU=`K&ZzzR6$ zVLw~{UHHjTR6gAewa=?-vs{bCppwz0X(%=!u-Mm^SCWq3sc(~sC8?)GpbS5=Z$mep z&>3`=E||1=xR%bOLO{Dz#M0%@g5HzAB}CjF|5npT^OhwZci{ZZIw%%l`5k) zr#d$s7-;x7?BT;bzev*Zk3c2+po=~5CK-@Q|HJBm$P5G@=}x9xv!4#_7+m#6;-(ND ztbOh;S%2kEo^VjL$%=s<5(O*CPB;IdevPv*jEIMCy&KVD{=w{M{lUY0F?u&FHIOt` zA|2Q4+D5fvkCaZTkUJ`=j}JkeAkKwQWb6E0e+=V$mfKVR5yZ&p1ahPV(u`_`=mMDX zPNF1d_CT97dxe_P>&9#3g-y@ft0oNQl$8C~}>-1?~}TOL5N7Bl*&F|lug=761^CpdyVLwX|^ zpTTimKK|oed~ZtsqRNToqgZgzL+;{pae;VZV@lmJ(_A~Z;o`5uKv(7@CYFU(INf3( zqNSXE)M16bv?MnC0rBi_esy=W1#xAQs|>YUO_$B+t!q2~XI%Fddob02PxTR3Ra5YfRW>fq1&ESbu zcw*~06Nqb4B_}vBDcH#S+X1Z5T45b%6di>vPj<{Wh^t1$%9lg>M?%PBdR2xcG@|vN zY;O7Fo@n}UZ(D>wlTP_u_T1{-iNWnR$Va=zK`xTjVk!@tLTQ3RtXJx*OI~5-Ooz5)6|I5)t8eI{r30taB)6u(vcJ6(X+$R4ZknN?I08 z#^21)i(+CcQ$(jLQu{1~kE^iDc_jC|EhRF*TWS5SOmVeAPEsMR>&%F8jzd*{A&qOqgd_y3#30Ne(Os)iPVZ(g-H(C3joDHy9duW`OpkM8NIwQI z-znc$(I*CXw}wa_Q@rhVRhKGUlF$Ti+WEeI*fECX=N-~9t_6^csjf-n&Q-t`0;~b< zp>)};BOzj-$zZU6VdHHdVNaj&yy0LwHL@vghn&SuFxfl`Jr2-;?mJXvlqCe{7;Ztq zen3O;+_WLD-G+D*2QKc$9!xhV^+Qs>(#l%yKfHyJgSKZo)qMT_O*r?1tIgt)+AsV$ zTW0Av(rT|0O{OF-5+-xYu4~Bq+YBj%=MU5BzMV=40k%OLw!BQ4K+PK_DQ1c%)41}q z8()jrpSyvz+9dq?dGkY5g63nC6C^*5rDb3{WfXQ?x%4I@zABN@)1A0FF*pb)oH!A@ zm0s`i&eAa5-|O?p;@J;C(-GdSvn8@vVf5%i_)$gSZj(?WG$D%oye>})oQZ;EHA zTsB*1UWKf>W{Yg+fD`QWU3YhZ1tqom!R(VfQA&sh`>qzV!7P>{eB8dgpHAD-TFT?~ zuJ`Vg@mmx@fld$Ic$bLBUY~d+Fbt<(JZhPo5nLi14BT}!JF`|e8Ow0D&YrAvx%+gr zh*Ms~K=fBJGA(t7za5r_Q|fpBT7BZEF`lnS&hUwH&h!3K@!R$v!H~|P#eu8I0;Xb#A?`5X z^Fg7XFh}vGx~PbZcMn)oeECKl?3M36=5dIt1H%L_k^}?1F6vuoU+M`n)adA+ zLZAyHFbFo|BMzs|3pR8`LM8Es2I%-D6#W?Zc6O_gBy09DtqHhN4Na`78|MskFJ%DT zWNK$p%3gEQD63;=E;fZ|+JxQrGu|EyY#agbce}5WIY=NAq-9KR*GYtWY`inZg?LlJ zilxZcOtWOx?k%-4SLLz0!YYkz;K4c9STMJ`*!Lo0f+**S3qI*4f1xd=&#Tje5=Os< zaU*{Z%z3DV1lP|g8SBdYP7JnwmZ6Wesm{#2CZjq%v|jzeHu(oySmxM0`8zO9!01w? zMrb?Zn>|~Z<$0q>?f`VPV{3XIU-Nj}<%2oU+>mR-Td2%#d=O8A39z--sTYYaBzSOe zWO*AP#z@MUlEuC|goA}kVdSf}*XGl)MX1j7ZL1kFQBf#C$B!Np0Fxcxx?3}j?um4w zw#b1v#%;}B5JvgHy@lTpuM_zWoMbY`Ok(;e)zXtFN?AMI;dY~&F6Q+OMGl*e6Xrk^yrX!IVq z6j#Yv$=*(%-;r=|x|?s=o1oP1?)3|Zgzc#YSMLg}OoAs=(9yGrudXZ4LYie%ExhE) zNYq1SaHwU;J|0+pm_*K?^h#*Hu8Kfy_A7hJBFV|>$gqsw_MpXvsxpe))RvIv@7krz z4#Gr(hWFq{JfqmN(wtH=b?+skX-i@4qEs=`Yt=h*paf%l_bHy}rvTyILPz~{O~;NKEF<9`FjYDw^NnXrzI&5UtlO_Lk#{O z0pP=yl61r1>$oWK=%}B7zfF!a7F&c|y;`cqvp95GzRHXJ+|Rp{9*CwXH73eqI5*KS zyqTuKoYl6@n<}BX_d$)6wujl1?M8&%lBaidq9@|r(zoR8L1_@Z*+}n|%<2!DkvO&u z{QN$!&CfGt*_3XFnE1n!4mINwgm6yD)cW{Jcp|LHQ#?vr7bH!*Bjx9&;jwdB)0!di zwEG~~Ui3K8)jjK&2~wXj;Td=X<{K4bwC`{NJ@NRK8YDz^J+&g$nc8lD>bQqxPCt-G z!n^0Jh6l_aIXIL{67)xj$s6_mMR;LKB45ar4QXDVS+giBCDydKD&Y&X3qFxho*nOU z+)3Of7JjdQ2N*}OzNGj4MiaA6jNEt_qjPSm;XZ5gD%&0wm8eHP87ha+l zt0^gCQ`?cvL`_Z<>cE9ihie*oMp^F#zlMQ2#eY6`iVdvY$1d z$-7SJLb+U2tg3{z9mrZ4evcm>lFl_x1!a&6@%KRqH4+bS)%@?3DC~f8 z{3_)8S92%4OqcSOqF?d)s@tX4+80?LxR zT>l|R$_AZEzMtHu8HK3BQ7bfIpl2ud$rVf6){XXUGT>su8`k^Y9wbci`{3oQ@Ke_{ zs3iwUxmr&rfT8k-o@`;AUFXWB>>`(L^Z<>@t=&r1 z#sg&AG{B((4m23<89FXYP) z{3%4gc^~XJfw{;+?J3@D+r@lwQE~|uHcepbpI~sSRagvaVd1dj8!2~Xft~!6Vno)D z5)ay1xm-pnx&Uw!#_kDmmHuaJbP%)r7_aL7D`Tc>KW9iwtV3+WV!)`Xk)zWeVN@BH z>(stgL1IeE_=$AMf`vWJ-~l7ls)-S3mTNg!T%9QQ^Jcd2_pZ-L3+;=6N92IX?7A{9 z_j;A2!7yFV58aF7l65xUVJhtVQ0_NDtsEkGY2k#Qe{~m8QB*w~`2Tvy{~sf{+rCfk zC%T8mIU_K36udNZOkRiDgQt+NvLzjuCMS_mr%-33A3}!S|9O|bW^>A4{%gdBg-Nc-6%qMe&<5AsN zt+cVto@XP)YEj%_30p1i@r{USN>L!qKLQp`;?S}h%E#G$CuX-MS%6|xzSSQUe@Kzu zu9}!WXXE031hr*Dy1SO}Gl2^>i)IXA0IUT$w>Z&xndY*x#@FQ%5X+Y2ru`(ZV>HjW zus=WYhoU0W1`lTCT|glSAC*5w8M+uF2velns9~6Tj~i!{Jmcz$2tEf(KqmN z_Zf&kvc}%2ZCKKlrDK0j!jA6!074oi%I3ysoPWy|_kUbVN|jQ?_I|=O#>(@mU8$AP z)@|HBw*k#h^&lRbH-F}0GRRb#0Y!yclQq3Xis>b-3vyY&xC(veA`MyQ0PY|qQ{}kQ zPcs=0RlfZ!V)-9@ONMdrj%Ss7a*_Z@B>gPKymX3G^p~v5I;Nm2VH%z14{(~7jB*ri zHL;0fw%))?N5mqY%4eh4m6a}kYeAqLqNu<5YaYQnvuHg1@ve0{MnX)snKP7b;u=1k z-G0v08^qXxJ$TV_M&`hXAYK#q0Ja9wyc~l{^_gd=zjrP7l9R%!z5G^?nX=YA3n|g7 z-K}j0yIKSG>!;>g^c(Xy=Z~bv2K(9OuM#Nz-i7|IUPv#;9RE@`Qx7!z#3RX6jR0&1 z23N@Rv}XP#iFMFqdi;Y!rUkkkW=oupnYgnuE$R=N3o<3Dfc-+|8+%< z9b68#v$V6tC*wfySs#LRdpgv4zS|CUO0Mb84;_!PLVce%sY~aYyVo$*rDNJ&e^Og< zLmk;nEtNP4&Ux>`ruakLnr)X5?NO@5($t*tyrDTP6o{)!SS0yz(Ab0=!rCSL75@6+ zVEzFwllr8W9>#GmV+}G=c2wEDvA3qg7k?Lj7iD{$Qzq!T_zask_;MtXbZdYA zHA)nUqz*9WDexmHG5_}L)YwX>F*(HVQnaHxJm|Vrl`o-yqF@1VKIrg^=kf096eM|` zBr=FX-g8TQuSmb@xr=gwysWi{jrq|C^6+y5ETyZ_LaL$kDyBx$JtF%qO@F+mDh(&| zZcDC&1AQ@d$h>}~9~9)Ded>q>mNY}f2lGO}4BiOj^P=yd_v4Ag)S1njBoe~463Xq+ zsY5=y?%?crMk#L0(zcNCgva+gU(Z7{%hYUzOxezguN^*ho+!Ry07NZHZB#GC?9DKU z`th<3KrBP5NpY3<{`0+_k&Ty@YI(Nho}u-R(J0pG!&j-A!c*rv_?xlmhYJ%IJXfk)o+r zDcP<4jiz*4Qds1}=bkJ&LFX3H{an*$QV8k(cYR4_$rcS^R4fjT*)v~X3Z(Zlf1xBM zvR}|GnmoC+sPhQXF7VLNF<&ylGO--^btUzL4T+c{yqJ}sfsfkZdm2sYg3--AWUH2j zbDDx^R#&Ejek=1qU}m4hIQB>o?y8#k$&<&^KLVUuS{2{m)N#pdcnRFuKK<+wr;NRS zj*_CrE*uc)tPL{GDL!Y>ws{dbhfW*F3E3f>%?D+_d;U3oyX@u&=pbt#iTrUI=&cI+kS4wU(_JB7hOofRe z2mm_D0F2TfpDo8>b}esGWJEP@eC%x0~!|YrC2sl3s4`b5b4mg4zyiSt8Fmx*|-1Y=wO$H+@V1`xa#jC4YdP9~+9dcnF z$R0gy?u|faI#5eTZ&AsF$$-XGZSZ*{^nlOfeJhBT-S)gjuAM_US!B;D*3Cg5l#nRu zlUtLrcxutHuj;_f5Vk7+`o)#5MgtAp_O`=p(+Jt#6Rzi?hM}cm;iBU0J>6biQ83!( zRZMXx@!|AYs!^3O+&+72C~(4@E@zv`<$f3-vK>_R;K5P!`#$p$D9c>xdy{6%n)s_t z>kQ1|Zd(H6$fPI*!>`3KwKHq$>GH#YuNAqECgJvK!)+ zJUok?{T;y5jl|h0n(au?vIjc46k_oGc3!bAL;GQyBm-++Qzks3YOExun-Lg1sI_D@ND3dMG#_K6#i3Cm(~rubM5mqSPnS@Lg713`(000l!r-iQxDwygczd@O zh{SX>dt=}tf9*P~gLyb)WhAN-eqW#ER=_^e2;Xmqpd#6Bm3^)MN}HXV!+-UrD{m5oeZOG(D?JN(be!)aRkT{b zrvNNYK~jCwj+Aq`&LE#)me>Sq(k$_lAm5Z1l3T^@-0EFxr=Ha5lVDej#UI_mthSy? zG7E3AnNmfpq}Fao<~v>}XnJr)5~BYbC|=Nf|3CIhIRpjyHM3 z!G8KdqWQ|p3-4$q=|4QMAVQP|@qo#SnsU^hVpr|#Uh=Z&LbNH>KpP41tz<<+VQx#U z6gBVD30w7^a9?u0>X`3XLZbVue*<|K8IC_b^T2n`RV+z~ie~9Q`1y5a z?6(e}9BL_{5S@U$5PDscYgN*P71cOQQA;B=aC@n90-7ljm+#Uk&Ze# z@lli3LRuA!*c?)#q;=bbg1+?F03-bC9zk`4>0Ug6FH@AwsV+u^f7?OizwJiV zdv0Z*(&@l0j-pNWt?;me{nDFQup=Lc-3mmV#hx_gs>-^}cxY|$XSlnGG2IRZ!gm7J zy^k$j2EoLY`2$~cP=cn@4RmyBqqutD(-n^Qb-@>~o zzhA9QE800Q7HDK(iZuTz-QvOI_&%U7Yx_bt04!`Ctd^bEv4%-LG3Wf?xsQ{T?TM%^ zGCDIucQjlz$7vKvGK*xHX{5zA7WvLEEm@HfJuewxQcp~aXM%tp?w1uc27?=b9xU*5 zBWS1AnseLCM@DVSL||F9(2Y2X+?&uXq;{{6F{tW>wp}pZr~k_QZK#FJum`sAh)4+o zUHh&TOyovpF9meWu-{541m_RSxh?zg%CKzY-mFH*EMJ?0mc{c}!b?_k z9(~}GO&qf4=bXrr7KeaqcY#?m+-6dRXP29RcS`h0>WUmTV*!i{(?JJ<3vMU5F-4%B zzIebyP!7#;<(9i@+8pt_c2RN(kgho_IiDMCGr||mcl%=4n~}@dWobP}oXnCXUfx*R z^4U994fqKWIIxcWrwVbdhnOH!yqg<8$Nmc{LB`hBn^$3v=gqL`SVSjIj(<@MvR|(L zU1DYL&C`Db`Q4r#D>`k_h`-&>mMuY%d|8J`-voBZx{vS4sTKXmRR;XepIa^gNUf^o zKZ5LNjC4Luj6J>Gy1oA&!E^l8nb;q@_njL2YB}XEE&Q8-{|F)q_`}|RcB@OcVe#@N z^y9`HgY?VP{{ETj1M)hws9q;++^!}`U4gkj_3}z_{Ugwq@pc&YZjbzy%A%IIX}oVg zoiA&|U<4TwyZ*l444IAQ50bhzx%OIwJX==pY+bS{5mN+j^#8?UV@tkq*mr2ey%t8c zCxxv%e^|(G-0Ndh!vAV(dbO@B41m8*Hawq#o-wE}`!69h#YC(RY7fn)(1A)J_c^jE zLu(;Qr-x-E9_cr3$31~WBs}xQZ)XpDY@9HjQVHK6WkLC|A-_1z!Mv%!)w1<9QQwiC%3+l>!HqN(=Ix`r=kEg=IlqPn*O^#}xSNcHgQ zmvpL@QgUs!60wZ_&O~^E*qcesS#Qh4<|)Gi+-yl=i&)T(R^D;}AOoqLc&B&}rj(z1 ziHgK~Ed?-9VsSkf%~`Um6+yYBH*s6iw-w1D27K zCRbckv78+ui3rr#2Iai)R6BnF1i-j^&AX6A>~rLEsP{Z>zbXVtnP zx9sT?p-9(kXL;r_PmyEG#9brXIS6n@D8AXaQ?;&s^H?l-P&Y)srTru>qbozZCQOsQ z740WUA6Bm%nUbv4 z1Tk^v!DzG<&C9&_!Qj~J=eW8w&nuUAQ;?M=&S{tuiK>QNu!XZ`;z$`?VNfoOc@ipLJbfu6pCh9ps?!Mqx!ZvN}Y) zW$=zbQrx+6gqpYUq;EuP!Yn@~Z+qJk@%A4<->4GjxUWTZy+$Xk&VV?opJ{<<${L|G z)Jm+blL1Kf5=&&}0T~!Wtl88y%#@Iy29cP4&_it6G07NaCPW(%%g=*w}jc>5sM{9zLcZ zDu9f2U7hzKfH%JmEqd-b{qpp)y^x^*2Ku zMqIce2az?8Kinb{p#;Hqv9RHFH?{9ruZ+c#YBqt10eb2`@-McSsr5!R(ul4HON9aD zsn#FYg+HA%3!mcnL~UdQAjv`$}waePH`ws?jxXru_fk?UE45)5X+? z@hH`3R0=Bm6($5&i>xfwTR=&wv`?LJ zsEo-lNF?sRI+?}{3C6~{N9Q6Jvj*n+0~nWiR?Kw>CbiFLBJ{#xDH|XT`P_nE-ZJ{U>yp=!dB|%o@98|-U1#=>z^v0zqf*KxN_22urfdOl>1T{ni!R|8-t+5*)oQ=Ft>^nDhM8U)VWT873{^)l?%?0j0ck`KzjPnSydPKDH;uT7vovGuP-5P*e_ zIZOXwK+WNxJ|teF>i_O44C3fT=cwM`TFxcY39-M(A0-5yb^(8d6`4n+tuo; zde~fb&lJ}hvf>+*Ktkvk)IL+!#sieUpS}ZWg>}9s|F1+j&oIuEQ$}*Ke70B&lOUs{ z-E{x6BfkcpKOi1IzoR%~wYaQz5~uAk@}Fw>fcj%T0>T$+w3V_Cz}#Xb9jd$77yk$% z?`p7Z|wS_Gwl zxPG8Gn(Fdl(x_$IzQNq6&6`X_Ypq262#NZ>Xw`DErf;>~EA_zWbwz^D$ETA*ON9`t z&QT+$alUf3v-6~NRDn@w8qbxUihMuwR_?RqWZ|DmIgRg?*T6x>G~!T% zv};bcFLJ>An5}eo*Ebya7o7Evpzzm%nS2H8y9=@W&SsUZtrDszwmF0IvY@SXj(}h? zb+_HJ^?jVy$7(%2&^dY+wpgruqsv+A-ySlQidESC*dxd&qTe8zRlY$K^%hKDx6q20 zdp^*N&N<>bPm$Mbp(Iq&=7u)gq_?A4)(Ron$ENTEY;0t+kDExvsg>4kP+m1P%H@Q; zk5QAFp3T|6;Xu>{Q~^$*>PJ1z&U!aV2ty@$?S_y?RsHzeDWtW=$HHDTA{kPEZhnuP zl8tvN+VbD;Z2>JxBqejHGLN<(FK9btG!0PQ#Q2?lV6eU8I!i`Y(o`d{dp1+vgHIvVk2Xe;LPmaSP38T;Oy}jhSE)nM>FSs2@lwr^v7a(e_84bt@M{zRP}gZ|qJ;Xcm6 z_a5MLQfe~oksWQh1bAXq7QJdM5<1HFjYTQvtTW4Z#Kwqom7WX+G>Yh^D**^gO-^r{ z$Q!APnSN5aUBaP|wtX1s8Cq>UEOnUi9@sh$Y9_*Hrp}W5-TomNFU?iAZlWKQOX8E2 zFHALUw4$xzDid{f_~%CGYXE|*-XAV0{<_Jx(ZV7sy~Al}cY2zmaJz0!P2jA6ar5M0 zd0_9XSTXE)=8^vagRHivB9kI*oFMaRVti>EQaBW3j2bjOqw|wxuD0TwVwupVZtH~H*&%(}Ys~VwaytdY;1*CY z%TJKagH!BbHY;)9?5b;vi{THu0GCEiyQ^=e85C~Yt>)6mbAkHHU5rcyYE|^ctvII@ z`_!M{o$59nrTD;gS(SLctGjcFO_-h32Rc%id$iumKl9HmW_6^L+!wCDH!fvwBPU^$ zReR>!%4K8G1xV2nGtO_jc5~*~OP=u|ds?CgQ`#;hvz<2&pkc(tWT)LfgE zN{*U{=hv_Gmk7jA(#n3Ua5lgcLBP z7MYxmFBA>b^rkgeH-bj>=tG@JWUydFyWsdO@$wypyB4Z%vD<^LkmDvT;Vf4VXd-rgj zmaIcec61BUvi<|W0rLR>fq{y0gcDr5e4bB=r8{=+u-A*+v-=UjcRfS z#sTkb1b#koX-}jc5=|5u@B#>#fJT?ybWj+wFsr3hX9&4Vpe~bds}n2^^wDG%*7W_`6rVxgdia& zq9>se%yzrah%RINOWiM5CG8r2r6$8#6=FBQkT^lfSFJb$xKe!3dJuL-M(y2tGO?B_ zi@nl*JGHzUn$`>G(m9t49~)WECHhnn8|@SP!z_7x#X!~DQJJ17G1XdOP59VMU(ltF z&3!Q~|JC2QhYX4pN19ssHtq-xJx`Sc>C@JNtUO$1>v_87l~4Fxq1yR>zRKD3*qx^M z29H%9!x~sD1CI z;{#A?X_{v6+ATAiiU*wEYi4qOZoSs>I?(DF#ZAZ{?MRa`j%fjV)%m;?zbr}M4zF|b8Y$oe8fiVsvF4jzS^q~syXAAu^y0cH!vi)- z6(Oh$Pk8ucR{M2FWjFhd;^yttZlG#j+CPG-jQZpUl67@nN9L5;DJPBSKGP3dGNUrV z#x;Lr0jSWobD5O$U$^+r*iy5(7@vuleoPTJfBAp^+s2#M1+CKpxy?PfR5GoHeXa`u z>8r~Jj(Yl!uR-Lq3tuYjB&^=W9p_dK1*x;zv||Pw{h-UhkTy5h`{y4H zKeI=b^cclVa^Zx#(N0B{i6ifq4sgwh;nYZXx~;{YfrYsPCKvVZM}ZuAl64M$w!~#< zH-h`St7|KnWtL0sBo6Aduc!ty;Q_r%YIw0#5htL?W22%F?dXkQP{TdnnCi*BV$_^# zKG%cv)$HfljA!&zO?5^R@n{AUt`585Yv*ae`pnY@9MU1 zXX3kU+3HIE^Uq>v`f7WvW7-85wL5xeA@XH!;^2zH<-*W-sF~kx^C^1IJndyGLPCra zu`ZKUm8+t_bO*A?=KkQYRGtn18w~Iooe(~Uusc2sl9;`E!P3`$ zKGr(sy)4TeJ1hUZg6HuW_uisM#^)lV04weMnY8zr1Cr*5Q49NYePi+L6uow?ZZ!&N z!+ZH5C0u;xOIN9Wr<03XnLUzn6i8oLzSp7nNx}i8hgnr}6Dv?(3Cr$CPUKgkVyM}b zj(EF-9O&CZ`-zw=sHV`wcd>KfG8$AD>|ReBv?dyO^bLB6Hn|36jr}8d>@GNS@+nl4 z{wf}D5s7Uk5skZ$c4D1}wn+^mJ__E0w^U{1`3~5YjB`xCtop8C~}+jQjiT`k|D&=H44Vxr%+Jr!56-Y2GUg88%jdNjOKEMfwU&dQ{n=u*lRcje-S*USK<2`<2m`X2pXuw&p`NQI% zU&C2bGLOrs07Y{g8_SGjwObd^FtbpS=crRE4*=#7NxU1Dathw?8ck5PXfD;6vRfotz0O9&nPh?JIdh#@l2Kx04O0(=X#2(eo+3*RoXWyaR`&>18ZoWy0qw+at zpcNOD3cNyAS~C@TJc5wsBmvk=o=i2^0Zer!G&2gNex<}c;>#G8=Y3*3ztEMeu~1VK zuqQxoCrsyRy=kElOCh6_b3Q(&!EPemyZFH`c*K}bD_Vgr>&2`2R=_bKSlGbbLX)Zp zkReJ>O>ruG+hCNZNzJ||hJo(_*@qI01pC-K;+>hFvGR$GM(h3OC+3t00oSnn7`sUH zv_LhW=?DM2G6Db3mjWEift&c8xOQB4#Q(5|Yuo!vcKY}T#Z9D$vgE*tse+2%A zjQ!a*bW6*Q-V&|OuA7>4#-m{sIi14z9rfgxb0r1tzlqgXTqn5&p##P<7jn@5=~P49 zFnsPB5cCd{mFv?0y3~w~i&zj3R(SvCa`5B;@I;%QyBA)10}3_|19Y8Iz8PHnN04z_ zG0*3_`AZIRz`875i5w8U@n6jKOUb3V!OUB|{`M^V`+-mXaiK_=_P0}7iFk_jA4F5S z`?xz9j${kNzP3Er`;rIA2)hucpy`3ox-_9i@s2%LH?4mJX(th-7h-U#@%PkBEe4!+ z4k%moYN-o0irlhGyVE;sIo*TWu7v<#B+L_|{m34p!9=pm?FzQ3G5hUuK|Vd^XR*Gf z$~^#-Kx@C}Iqqe4CQZr~vfSK?XgZKK!iMr-+9ZYQqjbEbseYmJRW}x4X(DfMQud6~V2U86C=X>m#hB~ z+-L@Kx%ahm+V_RH@^v6p4#c*$cl_cv@?W1v$29l!n10&~6r8&;{&JXnGg1@nW5GSq zg$ugs<4yak8f1LWGUvF-Z7Pbv=~-i)8jjYpAKTz6zZFuBm{9t$`3T_sGtJS|54uOC z{g9%J>7Rf(i^H$_6NZ$#Zkn+G4=?=Qw2qeQndtf{-(qO_{MF%|sccGUQTqcRjYI)& zYS^3oyRobm#DG&t$`;D$Su)HA?93&(+tIWUs=Tx}0rO6(Dn9ENy1us;tmWPr;`fuK zls5ixcht{bdq`PBJz29HV<0gJor$mnt5+88!b5Ez`wQ}Xo`n?bb9o^P6kbhLhONrq z$pA_Ki^0(1-q9%Aq0-Hw>Li~^FX}$wus@2{HaB0)x>%Ate#?*VSi?SdDUwZe|F57y z;I7Ik4_efS_KIFBp9pM55A@>}&9gp`YtUgUkVJMpD9H|~R%Nvm z<s8=06Q!pLTgPrB&KXM66K+H*^q@sPmNtY8EbxZ z#-Vr8rv=XD9!FpCSxi(Um+Fz9TOH$!b^@Ap@DND1O!WPvHY~f#4!=Lm-WD@jicHJry(S=`iTvXVp*up;Yl$zZ>wHW9oeJGC>q zs9V$yC<{TIaS)T1N3Z9M>A!MB`q(cX0{dJ!dl6jwo1DeKN=d$_hZYw+dA|~*UmwwmcUtj!P?KVq-0q7w=TfDz6{}#GA zFGuqTve3>%ZD5eY#Zr6@?|iPATl3}hvpB{y=x3fUUtUX?7KKNKzxjXT1qiuRX`ZlG zedXq{na0;i46NtiMk6benhx2ZH^z}00_IY48E2f6s}VDa-y6TK%NL)}wz*~V^h1hT|0ttm&M zP$SHhmqeoh6G;ji%ApV^_jftRq*HR*uZhF5lrh(d+KwcUT(JqL@P%lp-_5L5_jvoe zZCi~qd-3V^?kndya|hQXfqbJSC|mr)jz^k?v2GC)34-uKiDi)^e04fRp$rv7v(D|Z zz)WVw>{x|u7S+ww2uwn}B=JHAIM-lO93*<`57#QaBuW3P?b>#3eml?^4NYE7Fy3!0 z-BOrnkBLCWgirgD8EwRB;ooa=7Vg|}^+-~>e?n|J5JgJ5)R-qJxax&_kIvG)(7AsC z)QYS2W@9(n_T9Om+|!tN6)zn|MlpV7Bt=Z&$Jq!kfz+2W(mt;$j`aP*1=#1B7)$0w z{Imd!J4XU|wJJXE{#{aiv|J=i>h5*nX9WHXa4pd0b!GU&Q`~E zhp5Fr0;lFivhw_~Cnu;YN<6b}U$21(vsAN}34JRDoG}olaLSNq?*Ue?`rz?r_C+?| z=#v~oaV|LI9oAuzU*9!EIEdmraCa&^Pzc@s-*N72u0(I&-z~BK3sgX>zf|`viYJRu z<2PU}sVoCfw{n+ZiE-PLtfH3cE|vcPiO#V&PLHPQ)4kBCVK_IRcRplszZUTJV|->Z z0?zBc+Q#Nu3Tyd*>b$MKPTbROMehLguhOT{6VwL%^;yl{i#TN};duj6OY?_l=7}t#WUwQ9Oqc^S|ecM~m zH)L>UE%C&gpJlnOrNz!pPvMNXX*3@MmywS;74*DJTaP*snxb*1zB8xD@*Ku|6^FN0 zoqy?+YQ81C>obdC<18l*Wi7JfFE1|biyYPTjDK*6&55K&=7x^aDm1B8aVDHjrS}x5 zXa%i)B1WWDZAYKWlBqE%A1(Ac(Mm;Fv(-ZqbIG0B}FvJ|@_dinV3Yr3Z6>l&pzBNJ;+rD&f(N3P;?$JB@Cgj%bu}v>)tM(tO zhTGcFZE0+pD>O>X+KSUfTOvw@i*ha7u-3K2no)VI$Y{>S zJzFgcn~~gTSBJ`9Q#}=n8WxUD>8)#tJ+)jsgy?z<8x6Bj`HK~o-WHcQTtsGG;(MvC zzhdFsi)Ya|*=$KO!(UO@3%#wlZ}3;{E}qzq_fWJR;oZ`w&2i(SK7nP`8neWl5x)IE za-KV2ZH5?aF3)ym@W>+(z)xP{*Lfa+bunjGMbwqs(6tviIz?*FW;mbewdtqQR(9Kz zvQC$I?;JSqvdHA74~E`dDr0M_R}b$vmDjmyHuY|-;bHT0&a7=G72f*O>^y7wpmJXy zaz{SJy>```8I-V#ES> z!INzCnD*2a5#`q~`ct>E*skOmB>w=?f(wXbos|zFr>P27&V;`UJ!JZC?N{uNN*s5$ zGVB90JEyt29wfcw0)D`{*wR~R+I+Lj(7EGoUxhEU^az?ZfY5l4Vz7ARwKcPxch%0- zz+PV1#OsTjaoWh*>KeO1Pu*2U?5fXU#pBX7?=`Q`ljYsHE@kBH#b{;SSq#9(SYJ%g zdn}^vf9`>=Xf>vXRl5-?x7uyzoRzr6eiv5#ofpU8WzSd}k0XoP))8CIU>PZ$^C?|e z&@1~>I!>jZC)tf1I-U&kHsm*V4zx4(_3!u9bKsA#T949bKIn5qlT`I2{v}(t2F)zO zc{h&YJhfLW#qJ`@UFWn_-FyP=LJEWngBxfiAp)=H1r(9AsPzj4;glG+)2qD3ZeG%BPaZuBY z*k(EloB5=qPVO$QqNmAiShHP+Qr1LG3*=qzWpkHxj1Q=p8;A!Le^z$OM!TK6n39tt0=U}H*wN+U8WY~9zGcx zo0c{X_ZK*Zfm&3RyB<_Im+YFGzj%r4wv>w_@Ppm(8V|vBVS7Ti<=LU!fyPexlQuh2 z_kw;9HR@EMkeP>$%^c*OGu*MmJjZXo($tz;-8sUN*T4!2FG0rVMI0RbHrZ=4>1*o~ zkak9Yl5ray);nhs$4DiR1KhwX)31{CJa;CO?>y#z7@m)`@VApT&f(g*JL_ET8cPe? zNnZv^)$J{GGsf=M0uP$|UzfQHcVo3P;=X3#XKLo;v@qYra@LU2JD07hiR63<7o6O4 zxh-v@x|+B*mTxIU_4k84MD89xb&g^Ti=5euOpSDegI^Dbug-p8^1|Hc zkI}wg^9y#>PpWUG$E{vJ=Zs!kXytG1_WtO_4%jhw6ETi`8$j?}?R+{fp7NYjt-gn* z!^~4=%W~bzCdy9Or4u>=K`n42L)Cb>MvV0kRW<(rFzBzWpKB{SiW`_@}#>?Jj$JjSjwN#eG+XZZtk+&GcuU8M`J&B-9$V zw6EU2rGa&{kCbvvRhXA4`HJ?qN6tA{^%kuDKg-Q_t@S+R{)D31R0gG6T zDuztex2`VCaK$6dbQLwz#aN4}iWi>olp8H84*iM!UuC)dK&l<;8(Nbv*xJJ6Ut&ny$=Yv=hD{c^w+rX zm$QR+wDa!nvAt+CX0_@%5#qjwXO(#t+MnWGA9>(1@KSX~f8k8z7gh$=;^*hJzy^07 zqM9xZ?2oHcZL2gzLwjMF21E5W(R?n{xC$*%yH;*?ORH^L;v}$fxNv0Qy-6Ku?OP)1 zVorsLxGqJd#I1qA#w*)JxmzIbQ5Y8L@^;?$3!?H4n%An_vC2y)Q#Wi4^DZ^f#U!5( zh$T&8mf@G7v$&2%v$up@%_~{}si|9Ig;PcB?@6P+t9fQT+z*1v;fJ(1Xh)Lh;BmFO zjvjX*^fSqu>+>XzcYSDd)qSTI87Q9{&EzL3y-0A~mbf{u)Ogpb-t8i%=4IYjqs>Ux zfY<$%^3_zH6j`zv_aaV1pOfY`vJh*gs;%BVQ0Q6Yo9bRR+4spC1EjCpuSLhZgGP02 zW>>VU+vLoS1irQL_o`FJp=ucwE;tC4ooYNriiOBfy=A7$j|}ERQJ_A4&Z~`=Zmjh9 zS5#4)c3$gt9%#>hYfo0zi-~^@_{N&_T=&Pbrnwl?scL39 z0!xxz`m*rjTK6P)(+`tfS~uM23k^Y!EkD;(bucXfZC&2=M;sB-6-CdiDwX(7i^ zroC%V1&LdsS9p^x5GP%HMO9sAJ|Gsit=evcoqRE$oxw9&}Q8#8yoU-pg>v(ANCU;pPeG zv`-ZiS*)FtK1k9+4}sVD*0qR?tkzPe%}PN%-Fz$dB(YTq!!^{hvY8u;i91WrY4G=| zuGXcudZ?~#_A|YXlys)1i(H^>wIgKq#pF=3Ak$9Te$ORF{7#jLYG%w1Wv%TU^d1_I zCCj~R?peD-C}KAYfs8Uw#c=X%9o&1mnV)a&H#jk`ke2w8O5opvM|RB9Gk(y?Hd!3r zr0Q2PXzALDskPfEced2Dd1y7#tC4W%s3UYX4Y~O-xD9j^RAtoX-|B9c95jZ*+&UJ} z`D&?5;AV6;qix7$uz{e~j0)v+K5xpTr&9E`jG9+c7Hu!6^VjBHxPho%%ZrYundBLF zGotj?`W|{kPA-mJ`vn_?X3S{ca_eJtIoSndX`tw?CUZ_jdxn}Wjx3pn(9jR7zW)G^ zivE_~ytkave7}*#TdLAGc$eidQev7u(>m_E4t87@S;{e9)@Z_#{K9>TujYKGdhwej z!{6zN`dZjn;r{@L(o`2=kDx0!SzkP_~rhs_Gd79gyicDx9Jy!OZN6NYhKG(w=X4K z+?gz+g`l69_`{kQ&^13|f>5pGequKK} zE-U-~$op0=SH7NeTMHqP$720daV5R&rUu>5 zjy#4tJz&;C$zzGscWlNw=sF!&$uh9qpQ!d$GF6#3AHywu71pohY=@)%f7@3Wy@Tn0 zlQ{~?_tmDy+ICmmld{YEFWnqHmXckDJ6ANil{)p+dw(lR*7XT!`_D1Y;?2q3T^$c0 zeIV`l?f(GOJN2#Nd(bcTw>5E>80YgA5226dGjG`890=E_o!@%;eE$HAhM>}a>c`!- zYy3>DERPN`lG2+W%|C)AztiuS$>aHV>fgh8*?w)uAM)Nldn;#980~K`42grc%l89X zua>?xD$FRq6}KnrS7yFcJqMX4>luFRsqaajMiOJ(n^imj3{$c0R?o7$(!Wi@5Fsff{&JsQGs5 z{MEg^cPe|%pBZ9NxjJ;Fzh~NhaXy!LcX|4b$YeIh8epf~84P?S-=vNgxPk>htryrl z*Oc&XB{xScPL@6t?*0^K8_z-hsy623#_iqL$2V9g@=`gXY2xi@Z=Ruf`T4sEuco_; z*7ytWs?gV6?X@!8ce=8BH*s3J(<6+2ox+{XB#jrS;`8_0+;Mp>Dp7hB3?$$+?Qr;I zXkCvPN*Ww*22pyIIqO}1?!Ix$}QcFjG>Qz}Pmf-X+D;Ytim7=E6ZW5|59CVJT6sD`2 zas``m(8zeHJ{AIg&p&FTdO8;E$jKPFYfsz#3zd2r7Ol%eA!6nHqPqC_mCT$qEy8A8 z+xH}Vy139&U3FF5PL-({!wGus*6XOJjTKvw)V&FrV+(t%b?H&~s-dBv5CApr)96UdC$y;}jvXVy8c{M|afa<#VysVL1C(G7kx>1=P$9PqQnJf3N zONGvy44*G5mHQpZ`k4Uks9uvBnB3@ca(Hc#FS~blvNu+}z#qMOtV^4J{df)j$} zWkRONvY#s&TuX&llFYPQw9xXS{4?`sR*3xF;D zmGeGN%ur19*0?RmpvD@-+mMJnnF zpS^q+k;(CIId`-4*O&O!kBbX`x{@%~>&NFY`0Ulq+|PBHhDJ`!NFdgVge3PK-xC@w z8ds6+E_rCvT6p;`&54q~VIvl2SkUgB3H^a|ZBJ8|cG-)v?FOU@5#YKJbY)3I#@SW^ zU*}J_g>u`}rG74kJIb_nKEBF+3hlS4&bW+um1cv-T|%=7QI#hW#K^!6XldJ8*F|a) zp+@5@jD&3ffGTzS*Gj`Hy``IX7k_XgPqkc5jISn(PWCd|unHgQR#PiZg|~MJ29(tG z)9qatP^&l=?b99C`Mi~w(UqFirAWj5+E=cNbQ(sTnv}_-(_ar2WiwQvMo{Ky1=K%c z$sY|Adxp4z2&g?(q{_0jsYRCA+K}3hZ$*-{?2@mCv<~+6*P$9LZbq5eWfLiM+gxr! z;2c`g?eedRS#S3zIh%@0QfinHymy==rm_ zJFQM$Ghb@z^LL`5b}OoLZ`I;LE96%4+@(B<&$U4>`Xwjxsp5~UY^&`yt~YWUhS=ve z{zBLEu?94{Se+|F$@T-UwRj&dMPz39PgNx)@II%19onD%dL zzAnV41%CB6Kf=40-urzAqrQ~=BW*uVdrKNEF|pn6OqerneA|OuUS$Eo5J(>b29)z& zm&@E$>8;W{pE2e&Hz-BYKNDX5r~aPzc7JK{&#Mj-uwdt(N!RVoVIpWPep3J(-{KYz z`BX0kHkYxa_Vv+pd5${ghOSelkL)?z4u96|$<;l6^G_{(Kw9zlCUAGEt|r1wxIHy` z>&aPSxuF_>*jJHABe~KL+ zdhCA+Gi5nv(@xdjyaVbVgfe)T@<*C7IVNm7hf2p8E-$T`)?7u|p$!GjK2$zymuoev zl&@BF6O(b3Xp*d#v*API4}*9H{{V}=kbmks^mBE87FTg_Uhw10{l)B2xvYJ@CpBx5 zkhP?^fYw-+^IXT{yiN38uzR5~2-&5zAm}=X)mqzBjvV>*!9w_qt)@vz=#+=?t zYuNGFj9j=#WpR6LtuI*FL~I1;0C$4(Sdn{`g#jC=ekO9{88-hPq6)lew{w8F&@1A5qf*|ON_Q< zjprm_?dRP1rbyX^mTKCzxA1o~{{Uua-9UAvD!xaXz{&~6TDVEYze+iIsy46vKZa>Z zdYQWc#E#c>9ai-S#T42p*8-KIt9EG@Ei#cYG;*PGXF@2J4JX(YIY^lix^z*D^%N?R zUAc^C@EsRw&SfG}xsID7vn0^q9DG*)0MAhxrG)55xXA{_NN-OPx%atRGgX`_vk31# zL$JVi8`*;! z$z$y!w8-CL<2k$buy-)Ml`X9+T9wwsleZeKtmE>WJTQ&_bv})K3Uh|t^+Ept zT9)(e_u;c#dmW{gN4f2*-*Xg`HN&xurCI{z#?@Ut1zfzDT8eA6q+B^cF6ZG*-;SO) zOExDXEx*e>M|_2&?5fpl?9NJ*Jp%OgvVY3k4{T%P)z-F=7awbw30+KkeW%L&)0&gm zjNN^3*aU*KTW2aBZx+*)4I}kKD2HWaCuE(Em%NfZds(OLRN3vZAj}o%57d6whj49f z9Jj+uXzS+A`;5D!YnWz?2T1n-z)y8R0=g;V+}kffvy{YlY`S$zLp*xxySA{|cu9MK z1@jo6<|x`N{D)Lm@-uD9z3BOzyypdR1Gq1-R8XmL5kR@*L*)x6jNK*NjyS45VudPL!wg z1tkPvqut~m7mZ^sTk`{=(gOv6jT-&wd<_hqTav(_a;S9DHbUax-^HSFqRqar%hw{zcyT z%+4z&{0;}W{+AsCoXWImtvu+g$Bed@Lc}j7jJ>t~q+1&ww6M6{-b|TkC>=L((wZ8t zGk=6`S|c)=?wF_iR6$W@&5$XOT(~e5gAqBhv*6aYaqBBKQu~Qyz9vzb!ClWv!=GVaWueDAK zQxTb^+1m+mIV+o4L1S9y27_NUys({0me$>Shi^+G_Wpk75a~bO7%nD+JKyr$)fq;n zQ%AIX56xR=dGY=d(*FQD%iZ~4z}xv`G-bnCr}n?^pSP;e;YXof?Y`7!jk$)%Z}XG= zUL~09SkbaL1K;COU%hYLp-s^hEw{wHJF{;uGW(9w9No*o`B6w3Lrp8AeDqv}lO)b{ z$jPcj(Vl;Lx5#YG^O3fa4Yv)Yovw{7i?g^lr&=pGdDBuQD@uglA^P#U#_=7F(RbJjSYP&Xwq~mft1sFp7)HF*eV-y!#OP8*xg$*FN6I+y|%iA)4m7D$2c^O zrIpcq_noy3YJ1Kma%}qm**m^H>w>V(T@h_1%rUxXTxjm+FcNh>Yvfn4nv>A_Rf~zE zo9)B2_OZCiXI|58*AWd(LDs%2TCwOZDJmX)`i|dtjpvNW!-$QL+85S2MJgHwroUqA zeqGS7;-jagz7E^FuZ*%=!UwY{du=g_NR1uXb?wt$v@2w7wbavlT}vB~a+`~28FuXR zI>R3ui-ba;5DuSW+Kznsn3pH(cOSCxhiypXJKg)au3AsIY=k8G6{htxgLv*{^9JUU zd_94Cr+?=iYENGUn8m3ZyjrRl?|5zP>9btM$(p!|Ab;J5KePe%_%BPwrMTi}R)(wc zrtmiqI~OVWk~)R&JxQmveJ_o+-4C4ePKlaRyBd3>a5U>$FQ;*lN{7ff89K8GbZ^*~{E z)Go<0x+6kIhqYxOXPEYmHXBw+@>+X9?ki%mCSa#NOl^^LVm9efKnL`xep-FRd`Oh6 z-yrR6qm7mFIg;fiX%_Hx?XN;rgIFc`<)!0l*scQ@fn!DwSh}MMGmS*a)`< zIuF#hx@V0E*Y+vdzReTHsYKnJLz>6qWXaf~u);lE%tEnFFX=D^F^*Zo@YC z(J|KGB`9?+My_~Y{f`I-%*0SvymO>&n@xb>7P%>*l!^agLRn#ovU@eAoP^yHVj)$;!r| z*T%kND@&;U7Z*?-zVEZxxL0gydJJpmxS5fL5u?RNQ{my@qZ}5|vE|YADpBX7YieYv zr%wtkzstP?^S+apZe}=8%UXCWt~by{@+-WQmvpu^=`O!*e-*WH=C9@+lGVN4j(glb zVf*T~E6S*ZFy) z<7z*cdPx~~YfT7$^Zx)kqx`#3`Q_eH?zc1&2UPq=`IMI(sgKX1)4R?!<>V1ckzcpk zuR{)q!_Gx8YD!mXfAEbZ5?Y@HzM=J z#3N%3b*7$br0tyxY{^)BgP{!!I?|qNopirqMnuPECl6<1h!vveUOJYpI+;%olApPb z{J{AC0JU#h5KgV>???)jjb;o)drOcmBxAO>u!eP z?7Y39wh{iNz0B~gzF)OGbHH+V?U_c#>N$H$9Z}#%u&ws4>Un&dEf@YIv<|&W_Z(E! z4<*PZU6G!j%f)(Y6GsANN+%B5S5jAM#l%y%ol3>4+gevu*v7c3b8p3!kVCo}tLYrp zsCQuf<8-c`&*xnXRYMZ|Cx*Z3XXF^R2ho4klb<)pn|Z~zcKxXS^YWSdHaVl-#<_Z| zD%`zlE3b(c>`JfTQN>i>{nmJ`_1)v`#l)XT&!V>j3*yVmZOO;mb29*_`K0WZTDgUz zs2?zbyo~Xc%Pvz@lx$v__0=@brg>{GZe7; zI1=|hU*5bDYA8AdUk2Qiwns#&oA0k*XJ`CO$1%d&yCH@$puMqcA+@k|-hxBXK=ZE+-4lfxw8)^`)aJ_ngTbC~LmpdB@@4>HCzjC+uj zD>A#%5f-1e~;uQ(ft;Jn$BXJzC-8rDpxqwg+67-RzG(V8mO2LxzH5jIqS$ zUJcgSds`s6WK&VikUY2skUB2~DapD%huAm_D>l=bUsEzaqpwCh6T#U$?&0G8F1Ok_ zEW3xvxDhs?qrhma+{v8XWZQn1Q>%9TB5v0B^yjkqa^}`;;frtTEZazK*%%J5ya24@ z?s7PpJj^Y}O?{`w$NGvsp8o*#DZaQbmiVh~y6Rd8`@M_N&DcT+h&8%99I51D5e8uo!x24!p| z&u?eNM;?nzpk0K_y@tMM46BG93Z$Z5gv_sn$o@}?y(m>%kS;~&XDzpUdssV&sH48> ze>$yJnX-Ds%==CF_FLqieQYHYG&8idrl9-6(|00CXwN@?7BX~b+D}i}R@mNdR$aU0 z;jwT=Z7ql(7rg%fW|SWFPI+>tQ_ke^QQe|80o%U|Jn@FjPndKRAQN7TO{|yRV&)Ay z46lW`j%Xu%8nxfB(5~JtZBfR_$Dw>%O7_Mn?<3l-*vy(|BJG6Hcx}mC=|^!RU$uI! zFFm=V&hmD)z}Oo?1TgFUmj1=|-aGK4=N5{B^)cvFIZ^=t zL}2W#qXA?D0c99DTNn!fC@^raWFQC*R>J`S!q#LU2m&y5Dy{gLM7zd@hKtW-*{n^` zW;EUX;t2zE4jP}qe1pvxvefn*BvUog_6wKH^5SV-bzh&k*v(0wLczCIS{Yk=j@NdV z8h^EUcPXBp-V|CaIbACrL*;Wi>tE&Ok-Id_(7;vbVolllt6a-+5M^dDc7SbK_W(}5 z)pcghnjPFeH6^Mr_D=zU%j92ucZ>$@#2=hZN%2s5_bPWR@$g-RrO11?9kXwC{s;lz zxJ*rO@L{L`nyQ=8-nS0omCjaiVfdpVcN-Zc#ofC=eFlzj1T4ksdMqVHspUQ3cgr2x zxVKa0S4z~0-c>P%b*H+%Wd8s%=-8g63zoFd#(8EQy+QoJbuGOPb<1rS-!95M=g;j| zt;&TP@X*IOK=NIsDX1T{TTJIpCOpY8{{R-64?pWxV=9#=62`p*SK*-^3d|&PZa}g! z4At6b4vV8=O%-PX(z}ebywaz^UWBSulnY+&-oF(o@Aj(@lPfru<e;waBm1J48*h(-2h2$&#qwMCyZl9Gl)Ec|BV@8LZr5gJZbK299~+$Os0^LPx)KDS zSHg;y6T`c+-7bjg`IDE~Z^MsQ@;81jg`u^O&Mxg@y0)I$b_pT4 zYne1;R5`8`AcNEpr<;ko+eR$iaguAQK6iP8gmyQl{z~QVN_*2h&5^;ryDn#&hBNZ9 zh72DgU9`R^?cCwp6Qq;}MgXZgdr!rD!^XREK9|SmtFcmDe@d4R)92KNBMrHCU!&hs z+nUkdqWvppnYFflPWtq=ea_qKMoVpZZrNNNA$)HGQcZkUhNFep?)H28Pe-2{mu%M4 zqL*J)I}ZYF_=8?MF}X!TqfAe9kPPa#woBdizzmr|?Mmr1c|fCAJgO&S&MAZ8!H6?YxD!?JQ)E zwXtQhi%Fn$9K)!tzl0Tymn6S-RQdhItfrkB=+UR~iuX9_p`}efWmihnk3E-Yl9`Pa zAc0VI6bJ1t?j>*W4m_0^O_JPB9~tKpw&e_cy2r`8JJ z(Z8t(rM%#ro=)QPVeY7dp6or&N^vE_9w<*^o_mLYsrD{6nIUpcSr%1d{{Ry1eg6Qg z?|&n;dem*n(oyLB9naO>?mqkdI(p#rsp{I}ArGhCXvA_Y_BsfszsA~5-_%0cYe425 zYCBDO(R}hZBXWCU-F-TJrw22KzmIQpl54#iIdSlU4_$OxXHvbLN-Mz*K3)oq$Enkg zL!~OJ$<&ojgwbkp)Rhhl?4=1?o>Gc3tuoczdxqBoZtI(g1WW>>yHDD$ggLPj_f5D%wcsLk=jgGkoaFNQ`c=H&1EP_w=do>cY3!igHMm z+fj@gC|M7wpD68}vi*~e$474+fi1W0qrPjp0n)VdUY6E6+IhRJZalc=j#AwGySwGy zd$p0jJ#$tEHKX@zuRyHB6&Pzn7+GCquU;|sF+S<>x~`3lbN>Ja)paoJ(Vl;hn|c_h za^B>l&r4xEe%jPzHfJ&&5}*QbMp%v4G&ROSHerF zo9w9>|Yh#KwFnE9(0<{TIQ29qSjOTT~OFQlv*xYT*-QllmNogxx z-iEF=+`LK}Ez28ocy1ge&HdfOZg6rL8ysPWLDN%H)mCS#vsk)znsez&(`{}408l2^ zIU6nI4ZDXQJK7IxYG-$yTZrS;9LMVgh(tS>NDKMQ+mijXR7tmd(WZp5W?K`L5-tt))pyg`<6#Xlsad2DDpz zG)KfpurhhtcM|^qa+iQZ$BANJZU(Whag(P-vfeXqp{n*&66#|duJw#Ov|I}KuFgIj zsCi7R=qVWUE4$cBpZbctbfNHFJ9v}sIN3Sz?=kjIdoq%M@l8j({oNN^8wOp+ChmND z%wLk+a^5|`(w@=My(l^^q%f)2RC$%{J7Bj)LvGy3d5n7_nn6dB1FPhva~VFQ<9YX) zD}(k1>-90tynVzNNr}(zT%0!cbZfgsU9DOiG$5*Zww5+Lrk7nk=cMEFcTFmvp`OU} z(S_kHlecz863vlW;h>3=HLU}=?P#rOO6nKDGh<7P_qeyz;Z*b|-xZ@A_={N5+O^aG zbkOuvUBq^x%^T)fdoJ$dYx`>XJ6r7UD=@gKT!Pf{)0Uj*^775mjqU~h6`)(y)R|^_ zK=p6wvFSI9d832JBkwprjZc%3NT7w^m8GM_ZnsIdtq&8-^Dh~yD%b2UZRh9@0mj!^ ze8$zy-C=-vIJN*f6VvXhr+Mvqovcnu7p>MT-0x4hA&}el`$sOpcaXut$o~LXMLuJC zRMV-dqjvgAg*@xhirk+<-sbKzx3P-nEc>}6f#kdNtgfCubW~-dpvB9L*wpgdonmju z5x^*DX2Jj>;zd3#Bwy6dX+So1uc z`zW;?ar3LM#pb$kk<7VkQlXB#Y&5I8Lv^C5k!`cDhkI(HH?PyaUji~1EUCC{)y0Lx zZx-L(`Dfw>&a1_8_|?&#=ZeXp?pGgkJ;w8axU5h74f`k^2p|9{;axlzgUI7uPj`=) zk|_S5`6}xRv&K62B^1|jk{|ol2aO2UnyT^QB+IXnk9p({@$~_gg5vuNW0d)}(n=%w zjt*!IbG1Km?n{JeRUGxpL$#kod{oXk^wa52Yi^s3&u=yM?6hU}R@6$_4|!*Jpd@&> z)l|8~&Gr(XGjF`{cb~m!Y|Z1ia<`Uu4!O06xs8q>!s14QLh!Wz04BWM5;D0vPqff2 zv67pWcbxVufy}eJmaxi2NCfH8T+HL1Ew!)I%=pUVXRw?_!5d3tZ7<^2p@$&oyO>B7 ztt(Z`#`kA>k7@Et^6Ioue=j5@k5!5vQ0I_;3J5*@pWZg|3JaTi(GZ#g`hJt$i z%jtYPDvz9V_fn~+6QSpqcj#6ee$+T3S@3DFuHhB(DscKBo z(yOx$r=7phmF@Pel_^x{lq5r234{RvKz;N#1jjqMtOSpG{$roj0&WazaqndEM@kxk zYC3yL+htZkvm5eHs6CmpHuhFFX5n?~BltiqacMm%?_QH188oysO5|zO)IGWCD%U*s z$sR~ZT~gnQok;UuhJ0eDL(Wep(rfP^b9d`^6whVXa0Y;PgX8a6x5qBxmCi<;rZ|t4 zF79t=+nl@F@8a#~jn?z4+IkOj zoc%V}yL=LSvSS|;eTs^MsbSt!Jfq~z&zip6w+*?t?}jIm*3{Edx)$Q7a<58;ZsI%p z8}?YW^M3J!cYD={BSo`({{Vqv^IKB-+GmQ%Zr~66-)gmOn{Hc;4+=Ri33s>mrs1-M z-Ru&{*gQ4WU&en?<+(LTx*ETq+!L}%0g|L1zSZq9G1H0i``2Wq%O3M{N?F?Ji4F5V zXNv1u!$m9EvPj3dOweSE2wkz~zqFX}Y*uUUbJk>xV;ze&bs+cC`$bNKJVthGq#Ad9>kcN{b7adVH3S+cIGdE&RJXa2v?EG* zs@E-|)vU=xxGi#SZ99!ZqjwcEvyYycU56kE4rHG#{{Y#0POF=95bN5^E{hG*bq{N2 zCT0)&=%O1*iP@2@#c&RHiBP@!d z9GRxH{$hQ#{)I0I6s3LCr(9GQ@dj zkL}Y>vZN<~hvr#0%)7i~uDa{s0a$iXpUg5L%8FFK%AbnIfk2-ziC=sgGInM6y&>o&^F4~~{!z-54mXSk6QgI-z zvfzg7hW=w!q1#1>U6D5p=?>M!MZp#!ahD~gMW8fyYPHS^g^O2Ok#<>KcQvN9sqRnWJx#OnO(M=HENGX3cB|c3ebg7nI2SO!d}{_2aAY98e0>Y2&Kn zUPnnAo3t~L#cvy1p{Ka{E-mYNl9Y5at_N|x7e_QQP>!O#M~b01(B@>=QbxjT{owNU zktG9&uC@J(N{c*hZYtE-jj_5+jbJiqL0wh2v&v>+qcvi4f<`cx(zI7>;-x6NnAxqI z+>AN^azKs07gSf300UL)tYqkLZewaxV7a4#^6_0;H|eS=-Ls+dVg9Qu%QJq1co%_U zkc;n8{L!$s^}GZA`x|bba_0H|V8Xpl1?j7?oO;F+ey^d^;!weYqp$Br{Km9@s|#`M zUNhO>Nvtm54ra~Te7$Fq%fShKp6 zxTh^6E3x;bCcZR1iofLs;5J;|x3^Xrdu~bOEPb{s(~lZng5z%$)J+lmxX3-GQ117i zA?;tDGHgyVQ|S9WhqUA^*-!HD)+v2Xy{}q&-&g228`9?u-tjKMy1LzW2P?_Ei*o$m zBxmHInrq9ijgXQw?T9*n1<>K)wi@+3zbTXAT+^cY_P?;%X1tn`?R-sFrpKO#HN=d^ z8jV*%)auQ0iCcitp>(aKLzi`GVXskKO@ik>9p4;XxFQa<-F7oUX>mYZZ+F;C6EKiy z=-)>J;JEzGHDOZk6?0|UmT#q9zq~eg1#$lX81ja5{LQ}hC6suK{z<{X_Xfn6qM6<9 zbB}y&Yk?Tz+$C0S;UN4ltDP{OiqLk(T09;r907-V-7xL}i zguT*#dGC$HF_s4giuje&v5y}QL~!ifb)lyIv-nJNX9Mq@tSjFotd5O?$G77CW#YWW z#Jh^L=z9JKkx;p@_MUutLiC-H+Wc*bJGTTdxtIh|&1^$Mi%bDhRw zUPQw2-r%see<|ZEvlwgx-*IpJLMWwps<@eM<$?Yt#9AR+*dO(7bF7Lp_xKmCU}2aOhw7*MUzW1%CC`zBW&Z$;oxi*AB9!~RtDPw7T7-t+Pj-(;+pHfxDr;8_ z7op;z{6u+vVqZ%$lywY`S_%p;yyC}1e7b#ERkI|$zV++)^!F4p7KL@a7q-V=Ny=v{ zaf5=i&FW+*pmtW#fU*JrfF(dc5Cj1LKoA5006-7~1UK|5j!LO0lFsW>(Q)r1dMMdj z2(H-Xy|u53@f_v~nmSk+a<=p^KIXzY!r}B~81Kkve3!ty*Td}Ahtv2jL0m>8%*@M3 zFieR%&l;!KMnV#aw)WnF1hpbo?AYAj9p|2V&i(iO`#sO|oX>gA zdGe>vDTDWi1@_ixe74I(o7;Gt-gg4~Z$aiHCMBsWi;ax_wTSn4_!qcwU}7ykkdkBP znnx*#z_zRWi}tol4L>{nS#O{(bFH1noWFRlT@tiW*_o z&qxd?NeA=j@<@lg6jOn8j$)!U<&uotJ>d#Pdk zL?FR2>*K4jWqryG8@omw#{Q5^>Xp8OtZ@OY!g6AAQ4XTp=gLC8))1398KpPjA=zA} zWOJ77Q&gj*aMX?#Z3eOK=(>7ok7Z87Qg^%Kae1MOQ!)R-i)W=we;wbN0uB27l4UYS zP-Sm-tr$TzA^1F-1#Rsmi@`+xi8LIjUeF+HW4Ovzar!(cNy>C;`4W_z@)!;WM#H5k zlap#ap2M4>f7KV$K4XM0`oRAto(=_SqkRU+S#41>{}VXN!_<+oraZShWt}$KI$C3v zxoSuE4;$S!uw!dnd+49BOg-i}O>qTPWSNJuN!;91I7@5zE@GnObih?^e7N+0b%&M( zo>yUs-H{8o^?(HgiLXSByRiGkTvHt8{8iS+_)RyiD!PZN98rdaY~Y0U+q4T>a8JFn z>^~slDc+_2m&Xzv`ACv#b>4k(Zx4z$PwMN(VWC_7qt$Lu)55lDpZsriomOZyUX&tu zd`3eT)O<~0l&G|{!2Vq^cpv)66Jj$5N9>G~cHwz1Z>K5+_qGoT>p^sANyBe_MRI#jsLu+=v)EWq1o+WN#7?mYEC{*T16MdBt2?<`vD#xX3d;3Y~ zufW@)JiLXSK6=E_SC$!UvupPVhs~fWC@ri^$ZQvQD<$?B|IAq8HN|@DhHPgfi0K== z9v4!Z+Tv$QXNOzd&)!UMU0_9Loci`D$x$Y`GUi+yT1~}%@7>>TKCy~yoA9>UPsN~< z@5W1usuyVa{P6&DUS!|Ccy+Eh=~w5drc}}LkN7Ik*f2)@^Nz_p5H<@mXaABHRn*)y zI~XGINrM7x0$rQc2HRs<3B(Y$jl7Aws!BYwEdq9IpSf-3;boXu)20+!$phhwZeQE4 zo7S|qyao)ljWkZeKB8P)q@CslaYbh(rfEydmUK&qu$)5{1xKkvUg@>x8X1kqN8u5Z zU>~C|0RYR6L_1ZjW_F!MZE;lhd%h;KVjKQL#k-Pd>YzDUUZfdZdhFZ8Ekc7oXH}w5 z_CPPkfQRF@EBTyGFvlx~zt1Uqf5{oV%QqOb`!TS(AN*%<$k#tb^(h`cMOfVVDP?G_ zHxR%YiUr#kCx38T-IIw$j4{ys(Vf8PP*ewJ*2)HG5xk? zi)_@C_5Pk(v5aCv1UC(_t!Y`l3DV-qKy;($9#dX@fRdExu?bfrtcl_VC=F9LslVFK z9$S*@}+i?zobTgi|hm(tqkkF0+|4nY5YMR72%E>RvjzW^`_^d8<7lj|rEjDLr~| zc(XD4+CldX@)vV+Rh?*y@369)s~7O&1F;oenp>+H zLU(9YFDH@PR&Ro}b@90oTPNKF8a*!!(SUtsB}U|@80*<7M98GT*?wEtxj{gJkdVHp&AjlO$P-PPO<=~B z2thrVC1sh+RxGn}?nS>21a(26DJjgXkmj{{t46zbSYu@TQg>XP^r~U#0w0$HAIjh# z*rrRTAlx%cb!@mSkk}3s1%oQt+L3IyI4@oGmcoM0T#nW{>Fh`5fPyqTUjJXLAigKW zSr_6FssNbYH?QNVx?PtNep^4ed!l()=%9#y96755{jFlUSH={e{>gWWQ5t>#?X`R) zvf{&if836KX;s>2%E1Ow0tv%RElA5dqM)0OV@geuI7u&#tRx1`Fwcc!vP*Sd!&B(FUXY$U|k5C%V%d)QK`oRQ8PVHFre>?NCB}2UN z3xp?}RYxwvzI&pMj*1&UQuOr7D-IY|5cptO&hA(1bVord(>X z_JAkOs`LoQOTR>#bi1E-1VihD^9)57k{ndu%Ua%DB~O%mdL&>l@xydYvGX037rjQd zbxF7VnetRGRWgCyZ< z^XjDedd)el7shNot8qOe0b>q zb=J2y)_@bpMN{cv@W386L8s$ai)n_9hYMg`h@3EFu|+DQE;u+#$oC3wchg0F4C zRMz|PRlnQeb96zb`92_^hi(^QoC0e06p5=Venm)+JLFwN7z%_2_~}>*UHl0E?j^LW zS`z@$6UZRODu50?&%NEvQMv+u^1vZXxQ5N=qj{J9eF{g;T>sJq@9*%!sz% zqHi8OYN2yb8AZ5-EH7d+0Xxj57(efugBp!4fU=%@ihS*{@Z=C*&IdLNe{7E%)Uxc^ zD#W5YC(8P(Srs(YmRg6Sy;gn}H!1GW_V?CWY_~m|{@{yqL(oX!4V>zey?1brzxHeB z)rZu0D%Nb{`+XIr7Ebbi`p(VTQYvWU&6f?Jc6YFiA9#%m$fIk@fNif{&=0k}W>m{) z7(jHrWbw2J+^Ev5^SQn?W82%)*PSv|F^c|>#K#rRe^$h|T65wD^nb~q{_mpuD_PVb z&g=UrA)s%dc_oCmD3sXAsyz;oW4oCqRQfpSNoqY8nE7UfkwE_QAD4YPf&sORQIAIdwE1`CXg8xf%k&i#Hzyq| zHQDVr*VRNN$fuAz-GH~ckr)qNnl`b*&UDBBMzyOZ)eWR7O%@|RO`7IPJ+A$6pM5+0 z!nB}7>iggIy#lrpV)zp_WR@pE=14GeMiqZAKxon!^*zw%ARX}3x9qj$p)n}l-yz$C zb3mUqp%f}?YHs0H7AFRiL+!Dv4yu_uxvf;Ei83r8)_RVHOzwZw!1kBToCmDUV< zExzS4AMTozdpD`yIHH6$-T5-uP~j~!qv!O5hdJ~4MC%qHA$Qb9O~M)lF+wLEirp1l zUc7z8#wfKRH{}9OeJO6%Y!el!WeVGsDRUDBzB>h5vekJUjfK+{iMcd|LVYWdyrG*$>bNa@|A%BL*aYX#V}Rli zNK=P}3l{W}?dOl&VEW!x?}!KIU+{{DH?^h;mE4nhs_EB&jgs3DUBs35w8eaTRVK#C z08?t6>{(RBpPI`Z%P5uHR6@fq>aV}Og4?-ndXX}e-qsaHaZHNWiYl~Yy;KdY_p zkzrEs1)vn5gL3Svq|c6!LT zR!=gFA+e@2yeoN)eQ_jy0~P3JKRSqrpV_-{Ri#%&o|`U2M-~(oxXhYaSTmL<9W=^@ z{opNrxtcvtBlgQn9u8+!}*ewbr!hM)?@Ie=A0dY}%<8TvLRsl};jXdNqWi zuAj%b=T{}@KLe3XO22G>1gqMwZTp2=0by{j)g=*7glNw%#vDjlMBrwZ45CO%{bf=K zv$URMPh03!`!G&)WH|$;}tF~gwtKikg z{FPN`8dzj5-C8ZKtLE7iSXO&>!OrDPSc1q%YG=Dj=xKEz4|r8%+U+5Z51vrrwZ!{R z!?D&QT8DmgpSb{iPaq1IyR%6-C+9f0n!?J+I+dP}t+EiaDG6M6fbO!1KHS!>P&PP# z9R(eAHYmQl3{pyxkJ%ryYFYkG!UGP{q+?IGvi$I=%wjS1%>?5XYV`f)5Dm`s+>XdCa`PgY_D=8MVtMBGwk}_} zLi4lHL$e`;&9KD&y)~{uU`+G-$3Vun&!UfNU;EvtQX0WT^zNGFZ=}1lEMJV;i+UDL z1utXJw2OOrVK3`t&%f91ZM9^s!mUc@XX<#>M~D2%-`Bxv-&(e;>*B#&bIWr{L=z6F z1w%bmyR(62%s4*J@bt7$VkJmUL9ruZ_*9%WKG$rx^h}vPZwbV3qy?FoHtg@T(Gj0H zj?Q|Gp8LC4L`S?=dSt#|^H?GIjhozKCe89-)ef~ zQX@8P)JMPhe+OZ;V{agB27SoydM}$8hd&FEDtev&Oyt3je!4r=7`5s1A5WFLMrFbL z8aS&we##HOm06m=0fnt?qCf97kw;Eoeq3cTr$(ANR`mSIuOIvST=msEs1B?e$sh&Oi|4ndJ7`Ijrl-~2sJ_b19 z-r-ula=B9nSU)(IV|muk5@d-(c4?rC6-$dp!y$v&nzI3l8d=am8%^dAG@k#&d?V=b zu|cFu|ApsyH_Hj#r~Sc~f8^z>cE?|jSD2Uv0R?kY^Lwou&Ir<4uI#Va`@k9{8!u}w zc~BP?iOr?W!AlHLj`(wi+x0?T;6T5c|QFGMH)^TNl)=Nw9tDYJM zVmNZtS7+b+d=CD_9>483eyM&*sok(EwkCEmc$|#tvG~2)jU2))<)^(Kk-|{@sB7QH zjz<_99-@0M(&hE>Wz7Clm}+Hs>?T83kk**;TnGO!&w9%yvk6DGlc2^}UzuXUl)Y4{ zx<)4mVm;6CGU5tS=!a}#JkYsri>&SMmZtlcL>*2gec8S>du;qOpcxkMt`L){ZvnJoEhCd|HTyJgQ|&a3AI zoSPYE@j^cm7*?+QIK{5heQ_^yIgjw5@+xCn3OK~262^7NB8VR*1TTpc{b_7 zB4f3}kWkH?7hdw2k7JjT1U7973PP1tMQw!}20p?cPic&cM&Na#l81fBJHw$sd=MX! zYP&hL9s`R=*DMpdHUD1{nlivNg>P^MqY2b~Ma%YV9&?J>9ZvqOQA?$v`AZ>Y`Iy;QU&aq!jI zy74)dggNk9@U*&?G-E*Z*%icd#IE|9qBvXeNho-pMVq+~_+Zna?Qj3vO^)H{W<57L zSlJ>iRP6v2j-R-Yo**;sItT@5{A6r=^W4q3hOdckRxNUM^CfB0V`3Nk3)EKPP7oVi z-f|N04zD{=rOY`Nk`k83KH5^`VEf9i0tJ)x4#mzi&pr?uyKTXuUQt8de=Ie^vVNlp znK=(#<%*Q;Px(ZKR`GN0D+OFOiW-$@uXzROKulpHaVIS@?JDI|IVbQ~l0=Y1K%x?OL~6lpGUyG8$@$7uMM5HnL^mu^Zm)G{bL<=Mhg9ML3m>d#X7uR!I; zK=+qZzh*5OWH^)RyB|`Qg)#H<-!1HToxwOIdG z8|{;6MlOjlh4r<;+L$jaR>BGqE9eePZj#5OZyo;gjto)HSHdL-cs^L`b>8d>_+C>O zAf%F}lwdz;|6XJp$>j2ZAHYo}%9#LTjl93O>#i<{Iyl`p1Ss9{|KM&cY8C(XH{x=0 z|67{u(M=e(I@s4Se{AFZTz}6?blP3zraBVM{gjEbeAcbYVRzyOEA)&{fRFlHRUzzU z0_^D%&QZJOmbAbZupz)RHrm2!f4-$R>#`(XY2&s|(COG`d>nAr3snFLlHcMvReq5w zuc2|jXx((w$X6h!^>b=&h!^!Ahf28y487@g57>9sNlxUzpNG~z7OJ}no0B-TBdw6GbmlNAv$qQp}P+k){ORI3Xj4E~4ERJe}cgojW)tDQi-qQyCkwy~?rB@Q$Vu^C6Wr-jXjFgr0PpISU=^ zTD%IFW=(aSYlAOe(M5Pa-GW2(UN5R?xg*&GX0QPsM1_f?^|oduT%WcZF!Fa#{;TW} zX4j}3!%T%7xV}clqk|ZNhxT2+U(M$tRTP)es{76IN&a#Aoqm`d|HRXrV>uc8|1xFq zbFHW8-X%=x65=l4HXDnpS4%TeiKj+fE^+mWZDMGOCnI%J0IG4-A`x)mU9ZH?xo1H= z*Axj$$|1H>1CP|X1mAS}IsVSoVlMnPX}N18`#2rkVe#y^{@af+jrjKNxM-g&bYjG1 zo!nO~;a~w4KA(ndy82(AryxLxYppM#BBSs{;12aB>(a&qBs*{N$3x72ide!OP z$3LGLofGxg-A2o-uE5Nzdr>BbO6 z*-t@pz(!BZjpJ>JrS{)5aBXA+|9~qVk%eSa1{^9Q&}6~aA~mCk0l$b2cR5kzextu` zj!vOc0~v&f zhJl~8+yzV+>#T$hQW8vPu{?|J)BBGlJmqfV_*QrT3|ZA2ze)*J9lV&AY`G|yz-C6* zJ7_*9l+(<)LpmomAuBQ3@k=Z3fOU&42*0gz9(dC23*PczfzFYEIylgqX^U!Jd|9Zs z0-brV607aSuAN}@k6#bwgv>wfs`On(ufc;f*frDh>a(VtwhNLuD}ECLN@vUAXIj3| zlL1D%nGtm=U2K0Y)E~+Rl&OVYZsvrzTjfZ$_+e-6|9Q}vIiWavE?I5GMGsE?(6M}Q z%I{;A%G8gydPSNHV0rgt=-F)W+ameBRCQ~~)RlB79&~>~fH&A-)7SZF9akc4mW3-? z@9D;v;CzBoh^+IUT1-eW+v@7jaqfv|-Jajbp27v2K~>1K65mLqq1*9fXrklr;~!N6 zK|$(@T}Z%Sc4I=?p)dqdu)ss}sc07Y>ocKFlO(EG)Ni3vtn@Zx+`Q*iF3qx5m|`_X zgK^1U-dq7bsrf{)yC-O7W+m4was#$SovYPv?1u9WJ>yNYWH#+$Fk)EJJVOJvrn^EB2MT=3rEgY6I9#vnbEGTRi-W=)|*C4ec0EV0Yx7lfm;&E75Pw*;r7A1#U#XIyE07pe(CX{ z#xSBFy63@qY_tW=dK*sSoffi*j}5>7eZ%Q)JSy>q z?PlB~rYDBH4{uX0BwB4uHovaTmoT&BaK8e2Y8;-2i4HGkU%(RHw?06Ao(%_mB5ZC+ z8a3ZBWaOSz2sr|;$KJW+eahlE$cDHx-0#3VKg(V|a))%nie(10_v}x*Cl$V2_4SHZ zy%*&2;dvL@6@y4|t~#N=09PIf++F`%u^_$xjmOlm4zo?5;Zx2}pN<@WCFRadtWH)@ z(l~Km#&A_&famkfl)7>bQJ%F%pN-IWEL(HQit}b6;dbks`4`PGu~Hb1-c<{i+P?_X z#H^y_a4Yg=Lvm8-l8((Qq3UIrdJyX&fL*j*7C!oWn&N@-y~_lrda4-1!FCD87mXrb zJoT;=SOcwdlf1X}zLw0A8)gYUD2%i?K#+&R zyz~^&SBfOZH(&X0e^%YH)BD?YuX-x+)HK}2BHq`6bBrKaQMboNlQ8OFUafFRZ0PEp zA6U}lE|Q$@Y)Do&fm^1fk(L6gNmdF4E4^mY9RYh(?oMJB+a=J?MlVzq=~$lKD7oV} zt=lbot1kZ0=byb@y1E&b)|!>?q%7l2G~Ch)ci3ZhwU`WDvz-PKg9zsg%|I5DQ@~gm z^|}P4Dm+CG?V9mZ@4#q>mwHZ44l}%J0%_-C&3UM^>FWBb_?ScaK39Wzj4Dq-I8~D* z){tS+X(TItXdAP~Zr9)WVTN6ZHz$1E##O`eT=B?Yw>y5QrsX5| zahjgp9ZF9eG=vo|4@VzSLiYugo=zm)?rsS;xudR6@6_qI|MuA!MzCVq6$qcxZkSFU zHR(W1Y~hHewdZbR!F>@cL29~lrOCS%@Ie1W*9~e0>fH^KEz&EIThK2hp?nVPmW)?o z8wGN%{D@-*BZ#}YTBi*~=#@J~m37<*jEi&GtNP>;QV>e7Va86VbjSI9VIqeSTQAqZ z^PS?PnS45q{XJ{QS`_t$%OxUgzn?a+@Og;#HAP$@rnf+f?ApP_c1UbFmMh?Pxoj*5 z;KI&pt=S}LXN(M+KREcDVi<-z&!9@>p{aEHO=8Es8QGWIG*SCAF&-+Z9yNQOPZmG^ z7UcR$Nm4aBa;nm~os?s##!E{;s7J9~W@pJV3u&i$n@)#_WjkH@o8w^zslWgHNT-#M8neQ;0}+l0 ze{5S7TN^Kxha0Q)B?Yrb>81GU8b_b)HzW)w7>JZ=vuoN_@B{>BI)h=d=rFF*DDR60 zR7duQJAclXFI8OIa3WuD$NAS3J;}Y{C{LS-n?YBZvK(Ai!1*3aB^yhY z1*l1HfAeX${B8!b?>CQ00mFvKp~A*>g}{LR(dNLH9_t7}|7sn6sG57xhEi*A8+K=0 z)tOLyxY(d*o>M^Fh}Iw-v-Y zfci?oOaBom0KNS|QimxPJ{lVj%9xTwG% z84>SJ@16DRShFtJag@?NaU{p^2_$zo^Y&B!<@^$cSQ>JoPLvv(M5^7+T#zir8sYho zPqV9^=1^-@3sPy6)ucmf4GX726FqYzy)_UBbZBKMwi{YAOh+CnI|JhLf{k!Z(C3jc#wWO@pq}``!y3v=VBs(^6G;LM?YXm z%UFPk|NG6KWo<9WqOt>BDI@+bD&2Rg=1`5tP<9e^GwPZ`>pW_l(P~)P;hp0z@{cut zNjsU<3uSkutQDe7(rl+gV&|Fb$N_4f9}zNye`I3# zAS+bB!;n=%=6KbYpcDyL<=}N6kVOAQHicx6epQ&W3rR`j3Fdrf17;z<$2Z9@?vBOJ z=$C29bFc64-wKb|dJd6sB5cq*l3S+9&%Z-(aXN7_fl8ym}dyQU*vODVH(&UNN^I?IjZVEG51;bJUNNwao;%eZw(eSFrVr;rYZsurmWJCilyKlGdn(umoPm z-?5Cp+%-O4NE(ZA6J60GM`kQM=z@xG)WtV>*B4Jso}`H61Q}Hd@nT6FS#zf8#irY3 z!z=iaVX4_JY{&@!BRNQ`9BG+Fo_;U=;TFUlIc(bMir@mgXs@a|HpY`0g(ZTp~AgcAV1hUsFiCITaT5gHe@fUZ|piG(Yo= zCNn*6wNnStOZA9_#3&U>P@t1bg0!FW-!P3Bj21+ZT@DPwl?gla&YKp)2xYtG35vE) zl+u~!2!*y35pqUtY0g@+T}Y-F25eH+^aBkc(jcYtgy=U0yWKqY|6R< zNx(=>0Wdem?`Y&4ve7SIp%K~2)wXm1okdPLs3b0`O!PQXvzd*-Y^tn`C5Ec%8?q)S z%#4gokh3Etfu7x`EE`$+N4uu~ngnDgGj)!L5DcK`OTFhPkEumg)_YW~d-ETYpCpY9 ze(CgZfMPgi()Wci_o1-})y#jlKh>(tzOD7i?8HUugr<-o@TusKLy>+g#sB07W&t-w zjKBb|0g)KVbNUlxX<`PGYC&%53AoGH8LVEx>)J~ex|b{Wq`4s(Oiyq6T->C``1`YR zXFo0DsCpIOiOv@N^xj|HNj-C)Ri-8}L-CdGz>fwopqZdEr-(bA5A;A*-c|~6Fo*tq z5a+@Q*NkjJG4~}qV0M+fI0Bp80{H1xXW;ZR2n&k|nxcAcOA4C=mdr&KhC^SSHnYu% zGhF!r_rCH^eT3wVI4J%RXcJ-91$O<@x_*&HLwl+_ITb!y;{ZTatMtlA9j3|95Kt{4w*?%i3oYqpypJ z=wrH$s!mxBf&Mn6UQ>3O)G>2r{0Z|HG<%2px4HjxlZu&Ti_fnqYCn|6f!j1STW%X4 z`|nL9q@;$Fl{lqkHPbr%Orik=ezhal-3_!dkY2XXU96ffR#ez(Gi7~^K5>bhZP-bxw7bpjiAx5kgS-lF@6_bZD2o>&ABy7J1NRr#s&ijLN)}IDiVD zRd;E3hp!%Hqw(?_=|Rzh&FjuR4hxAztXRo99+KlAMJBcxH3YMTeEri>;U*`|o$tD7 zj57v>rn$#@u$?d#2sM~^Yw&q6ErB(+d6mMR>OifW9P@n8RDZ|3+SR!5jIdo0-KWcN6h8+@f z6OY%oqMT@Ji>z%E{T9rMUa@j)7(H(+U%K*NsZB`i!2>NYj5y3j&;3uTnj~tES%s=L zg@e|L0VOTQ((&0%qW?M5cFVbGZb6d&i5GU!M)8TV@<8_^-sZ*n zA1>h@m*2ZX35fPt)6s2F(2DPL^Gj1tot*<8Km85ZFdL99lC#dUee9!+7;W<*)}KtP$Q7C7vk;@`*E!NS z2hMEgq{Flmtf$1~>s7zzg}l~!{CeaRnn&2*PxnGLIt@rnGHW!nX!in2VqALkhNyj} z+OlsV0_%+Tt&_fYj&edHxB~}BZjjbi(_UBo&aZhdr@ZA(%+F9w`lo`&-o(%WfTV;y zWNw+QB`h&F#d{VZ?JZZB!jfqGl9#wTZqM$JS?u=86=H)S25kAzy}+pEiqrf^V2~{) zYD(Pk1z2b3M%Ua%-IED|Wq#PZreIINXmErkx(F8Jhr3ofCt3B?C%xHX2Ua}POc z#=I6n=_1)75hyL>lZHxi?sg6;GYrf{d-A#>oPw&>>xo8ghP!%~dC*;Cq4tNk!4bTd z>{5#;eE2ke_iFn~v0<5xf^n5W{@gqIzC?J}oYFhxzg^*CJ#*o1r4RR*{C7bWH9LY< zv)*(4K>!Yy8QI~~S|86W$?-ajtu|NK%M`^eMM%A`c(eBk8@JqOGPdNvbeq>ZanO{B zad5Z(H=^s4Y5YE{L_ilbLOEg@DT^-oV; zj8&#N(43^C)|d~dW5zyYRxDGq3X>NUV7{iPzck$Eqal&K5}oix$k9O`zW^#a%RK2u zngeY0<@f+o|G+$9uy1EVafsoPRD2sdjn=6NZq(^TGG~PKPSy7i@<2amk9%yw!aXgC zzH7PTyuu;Q6Gq2>k>P6XXU1)r-7S*}$0|t(>77k3#l{Kdb<1;8;3Bu57eQ)(E~`Ih zHDhjqRI5e&+Y5G5+HcSFwbtY_B14pzqJ1*tdHWpW7D1N!ryDRW-k!JpnOP#92J0#m zyNbqeF~f{|>-*1MPV6$DaEoMkYa4N_?mVcNU0q82lFfAFW@U?s* zzw<-LEdKPG0u~_RFB>#x;kc0f`6HWxnPG2lp9sqg^Q|hy<-vgZ(LSW5wBdM5a4_JT zK~)w-F0*SWJ%g7g1`b*O*!SgD511?}IUriT@o~({9W>T9D*aPzaOJ#^C*mZ&Ut*Le zUo?n}l%a6jv3o*zS+*oSA7Mpv2aM?q$un>K#km+ zLLlmP>0a@m`yR6-sJ$f#S(eD)-SPywFGLe(t>o$B`XqF;dMrIk0;nad=xYTBl?Nbv zPd7$Z6#JWBq8tRi#3XTYJ@x*vQ9LVb(agIsQ^YQn55!Ft(9Ty5f_vx-+9jAJUH=Kk~}iP~wkBfHBv$P+7EKF~-L z4C=8!H8z4!s5L)a9{Mb8*vR5;pVQ{WZR>3ABu{&a0K|@Ftf?tP{{X z9Bz`}gLj*4RweNbA!14&?iRQ*R(5s8{qAjDS7o1fQkErE!m#tGhg}*uh>(e9FW*;K z%NrwiyynFu^te>=cuilM!d4`bGt553$4iUZXmX`0c1*?2Hw*i_U-Zk#bML!ysAVK4 zDrCNrZU{1F&d4!07I)#aLKZ?V6&j>)p;+Hlu^JDX;IZlavzwNbu<+r5bh~V~AP&`k z8)M)G>-M7hUI!jH?B~O)EiD=#kafaF1klys6l^3W?mUF}DweB_~V-v;h>y7`g zf9$%tjha=&{hb5wN~OMG5Z7*x)V8c6DIcK%0rCvY zJjCjMTvNtREPwl5?#`nJ8;cdD20sHVxF2#@Ii!12pZ}k5K7b5e<5*q8N9nY^q&aYq zf?uH`etP@IR;cO4XS9Z3OPE&F3v_Ib+y>3VIXG7L{N|#%oo=|i51_U)Slj~Jz1bgT z66kdeKObTp09v>yTKvnjzf2$GPE1GCamPl$?Cd&!O~tm0&KrYWwNvx8$QQ%0#q>6~ zV2E4ao{^IwPVR#Tg%4PXvey+6YdDwJx%zdWU-A$@!|mE4d*UBQ+!Ynf!~Vrpkay_% zuBMvc-=IZn%diphiJ3vG%MbF=)NDY;;P2DzCms^E&Vh5fYu~G^TVJ-m^zKUt%Q9px z%G6WwsDw!U>um>Th1in?wyCy;FXUOwekiz^I8Va8P=2Zd$*QZ)%h0v1B@$Jo+j9c? zziSHjb%*6X(9ab>nJvJ#wk>t1l|ymtk=@*$yN;d-QTVSG-%r&21{W`%!h!yk81+py z*4*TibLQ9B9_VWa!7w))8U}TCm^et_V+uQIUbHW%5nkLyvbI4Kv08RrXmt3SZaMWm z(s{y{1XAr7$oRSiAl0n0L7PL|C~Fmz?;NpOC+!;AfZ|p@f(?^*?|76@l`tChSvQUI z6e@&%odmuc(?-O$n{ew=+Wu|ul%Z#P*!gxVNY7jUSEpuXua1yOZAWhEiy4I^Q+S;5 zcMdz|a|L~45<6v*VgUm`i>#O#Xxf#NA-*x%>};4+jG>6sB8)@1Q6Zax5uFMH!$ z?rKe1+}<-tQ#aAh>|D7&5+A@}4+{M$J!5C!-|oxIEHnQ28`k`<=0 zo{jFV$=(<8Ff#v%FpTQ^&%jp`DYW#Wx93>u-$5PfHgUQb9s`u;hB>mz@qyFm$=h8b zB8by}`&~~9s^OMSXyl~Iif%NRiIy?&wM${aq_MZEDmaDL8HkQ3_m>drUy0-zSrS(O z)})+Q7t!f8tM#@9)yw59-}-UU_7K|{Vx7`4Y~z$;N0;%eA-1PZu@gM`wIn}qD)=)? z*kxCR5$e$edraVOmy|zy6YEP=_^(pD&iM@1=`7hEYtS6y0(eftQnff?s_*SirUJEc zX1oY13jS^;7862!x41C(RB-IGZs3@h7bn>{Q^D9>5}G@y#g!M?=WK^NMSOP9RCWp& z@AS*B`sn+TEG!=6VNzC`%uJ=zC5y2X$ z5$k;gLoTYy!*51aK_=~_K1yT5_lIe`&ugPmEwm|tUh=dpHQG@|ZZ0`#ngATz>yj`r zK=q}^pFcLgbFpD9V^R2MhhuiCFAZ1m<^M{mqi_Wr4d2J@43*;7jtY+6_xgC3Ul3N0 z=o5zR$uUF%dNm}Br%P9P>E`UaMvNcv-&1Vz=(cf0V76bByrZBHNs(Lqr63<9?&~-CKSRqR_JM!;*EOzJK`osY9N)#8GyJk6lyb zHEwmP%)fyjOB4<4G5WbI_Zj|bHmh}ApP9iMtXxxQEsS&WDp-+fFFkHqhKbnY(_wJoT{=ckDWzj_q~6b@?+YrEnw3-9MBNQrGn| zsnYtJ7Pvj8w;rFan}DoKsujHKfBSwl(!Cu8G+fh?njN>(Fu|3yv6m{3p@~@EW%yt> zXx?%@oF^nD?3%(vNc=c!u*ETj#?2w0K+Zhf00m{B5+@e{$rWb6Yl;ew(esnxJ551m^4^#2$d?GJu$s)xSRY$lWJS$K-sdci&u-4$V3UJ{ z2dbTq1wDqBA6E8^r66R+aaw0OOxBm>{Tkr2MyV*z%|UvGJyl3}A>=s{D9U)JO-KM0Yf{Xgelprf*Jqs%HPaQ^R||)Zl*d!s?q+dbYN{xHp1EJGEDiE;a$B|9peR5F?0$Fb@U7kxQ6# zxVcpb=FLntU*hAj`tGORS-Puxk?s$=N1!FFNWY?r=b`@n&K z?WqZf`|cuB1atVtf8&2bJk0Pe6Gt;r?&11C?RPO#D~Xk}eO*A&U99gP3Fo$4rgzR6 zxB9EkMHzoo+&*VoIu#D>t3m}8fUo3P>#2D;*d!^wpj;Roglp1ov5t>dgkA}#nyzTS ziKN?B2@uRIh}@ah|6>5Z7`(C-nEn7aK*+!U0!C!bHAT^xNeCO{ju-aa&ige$>fKHTZ+En3y&Z2d%S=vz$gqcK{9KDt!G&XT1qajpSBZ`o=0b+swP;}xRs&`2T1 z2|kl41#CeL-b;xonJ`h}%JFim1abVPINr*i_*-l*vR)}mYF5}pRA zg|!480#HSwfw&NRC!qFs`j6PILM{ArV|8i%e%j1aagGR>*Oo=_%xVi;&5gO)I}La? zokH1Sv*Q|FY8t`}G3e?n7H3 z4CSsxDk<{(dU#W^tCN`drk#t4%)3dM36RnPdcOTMb5+8FGrW#?rS>1a1^7!rmeb1| zeu2rK_mkgDQva|l@>gCjJZeig5+QcxQ;U49Ip(YQN^}3pEO2aNu49n3Sn4Mws@qNy zPGakqzL1S%XcjeEKMSSJ2*I%{aUzk2F5^%9x7a(6TJQ0>aflZg4y;spddV#O0PY8F zp}O_H_KRJO>0>p70lzY!4(*@sRFr=;L9Uds80jG{a!_=phY=oxNJoUy+qs;b(gmu} zz-dUi~ z9y60gflD&yv??ZjCWiwxljHsc!IjJ^f{1^xtJ z2;J|xH?U&+GrzH2wULks^*|#J-{XAI)evA5NYsyutrLzc_~S@%O)=l1an+T4r5MG0 zoPA}H*><>kO`(0BoQw~1Xev6OPVp|%$j zPwMoodjvYRNRp}vnv(5r*7?{I=SRit_<`GGc7*;fnwJ2c4t-Jx$rB`L{hfN)Ocl zZAaXZ8Lb9rUwO!AQJKg%1+Y8}ltA_z``vEdUV$So{3j>c6E#5v4d*?_=#&7zT^P{f8Y^ZCX zvYM+aZl2d#?mH~sP%+=?wGJDcey~;3q2_ygtC$8*8w$WkS-t%~XgbTVCcL=s&kh6x zrAt9UYAD^PNK1E1=V(TZP^rJ9lF|aw3 zfRRK0ED*Wd(0;@~T*^SQNDgOtHdwuO#Hd|`{+`Ev5Swu0a>yBruL)T2@Y4@U@*eT5 z?=H3oSKADK{?R`RRyFjS2%y+qLC&alFV<_oP3en6or~ISzQ5xl*MQ({(}BzdrukK+ zlAFH|O;Zvv9P)Z%JFG1p^8EN`-#)*1s0aNK_Hpi04&$=4a8sDaATES)#_U*->mg;7 z2>|JznDU^s;MQ60->o3~wI;XOPp2OR*zTYB9OQ2rVRb6y^@Q2jSKjf)3^E7NnJ^X% znKhL8#E3rsHn_8St8|}6H_yT_e>97JDnz%mSXodGU|dOM)12&6k{1qD1R6CI-G z$5cqS52Ai@<%sl!)6xU3xxyFII$XcZA~IIu?;mc!TM|Gq*{Hgjgn)K}ZFpq=fl)7; zy!qRw=To1`1|(htxTC}dr#Aftb9ChfX{szxhR|r!kAI14R;BpOtPd}?3QD*aVd_lM zAA*?!g;%hI7Qd@00)=4pH9!EKu(vns{)pG?-bM%&K{aAEfXq^*=E=81F2wKlzzCc; z-)y42n+uH*da#p@B z?odA*hb=IOO`KM&nz3%6=?tq^s)y^JiCnhct!rCEzNJ%R?Jsd10Sz~V-Ok<*nATt4 z_2k5B_x64bdySpKS^a2bJe^3{+We&EF5ijYf1^v(z#Vg#z85a4dp;Vc@=vI>`+jQP%k}ysV7vj?4W)WJW0%) zPMm>glEL2apS~MUjiN$V<~Kh#oawLr@vW$HMr6p_^xft6pj0B2pPDzfi*uJ0%y-u~ z+?NNNTEu~QQcryih%gQJk(e&B-WX`DIC* za01@n5dlEGdN`J9kl*U!XLqN*hC@Wkmc00Tg{r$`YL$<5*Glc8d(KvtzkE;n^}V`I z5(x_sNb-KLU9etym=z%6Tn?p#%`1Nu=S0{c z<=D8o5?K~n`8ahke6wngC8pmg#v2z{p6-gV`@NATFVXp~6r654NwH3^#tX3@MjvGA@TyL5_Zz3YCkJp2q@VW z{3xd3&A95MgL1zS$HkNC#rb2Ijl*rWhR$$h=}@LCg(zZajH|}41>rIq$_90Y2u2@^ zn=>-1ek~d~ajc}L=^Mv*Aw)^yeiJ#x`b1Ax$s7ppbM`i5k4R@X5>yTqZ|?_fERJHTS%@0de% zFNM?0)VzQz(H7|5PMFjiHU*`=-tN;?yasHM_kiPR=jVDZl3suW!Rxwcoo12V_ii}Z zmex@Py`3by3;O4mM6YdJN!0<6qdm!C?9EC&gHl)nyM-QB^t9=B#LxR?-R9E;UucYS zbYn$1sN?Hos8_0#vnc;5;HW*R%`ATo4YQtJD?G#-O9<5zu3OFw3vAL2f zS$J?;v#sF_YUf6Z*h}5EgNXv+DUuf3v)$7Ca|&t7uvIgCyBi&TG^5HmA*fPDCyN3_ z_yqJD^;_Tj_hW8TJYaPJto{D@Og7;Y21;GsIq~es^1o1CR^0LM%FB5NGs~e0k2fX>d!*NpP2hyr0%gCgx7aU}NZAR(Kb1!if^Cbi`a1DS9#wQSR zh+>)q1q1C;veW@w@-%8 zZ8g8>pA4|#P5&OV<#<|^u>@yz9|_{ImJ!+X!axf`zAJPk{5vaGD$5+H!hB>Ov(NV3iNfE=#pb1mY-B>j>q=kwX`*MRZo$d(O5 zV%0{%kG=X$cB-~vn)kSDmH!a*Vx>I6#1?o;}j=OmAKBvf(aNNRD z;RcqA#P-R|8)XYyFzt`^zBAJAfDWk7xs>(cM75M&#+R&7vj@gE1EUR14i@>jz6@dV9CruA+}P#HuLMD?%0=qSmx4 za{~I(i^=_VHhGpdCqxi5rRMm0c&~kbz*L4XxUm2tU+tl}Ynqm`@>m$8GU?YaaPi+v zBJ{avSxZ27VerG|vhg}6rd0k(<0!psORrFu%aA7YK#K~Qra=(fmm{VD@g=7ts`iD@ ztdouJ1q~UzgGi6w2tqTpSao~j{h3#cK9uVUv$ebt);lyhb;vczPr5hr|-WJ z{X?9cy?kVSS|-~|JyID5iO-OOg;mWoEBUdiI!slpD7wfKn5N(ip7B!YAitGiU_Ekp-mE{h z3rqsv+=e~fv(#L&orJ|Jg6bN^D^;Ut>PVNVXMGX_f9R;t8b*Jwrj=hkxMkvqf|ei- zr8{R7Mc$;Y^?@M96;_w}8VX-XHL~0VwqO4K5*ZVLa?L!P$sd+oA)0Esn5+sv)NT8K zN!NlAW!gzJk;YtkGBR}D|)%`|-Hj@M59fU*DbZz3<@m`7*LSW&r4 zCjMrAvzq*xii}##Ox@r*_JUYy$571B--HsN>1_iee)6CET@|75JOQI`lCW>(qg*2< z$zKG?cUomG#=svsvb)rI+-l4bHrp5=&MFSuHFO8;K?$g$Q#6iJ=1_Y$j(w>n-39_F zd+WIM%VSR_I3~%H=(9A^EQ+()j>Bum(eQJX5M8$Q+%^Be@5qbpMPgS%ywKpxjc@*y z2OgW+!QOFEe8);I#;$fgPvoi>FP;yMn;R4+aPt)E>Gk0ED*9bl>6=%^?6*Jy?=P@( znCz>Ky4uVaQ^(dZYAve4hm&4}DDcWRR;lEJ9Co)iX`06MS!!#;0ZZ5ezvk71SG>DF z+Zg?IRm+W1-1@R7&h_LDI!k$vY<@XZqjFncI_?@kyqz(| z`4)!D6gO5qW^<1|zVJJoEa4n&?>R{66-2t!bNneN_*5ceSkks{_?4Ji47>(N@S@bp z=EQ`B!DHF)kHzT)HS^j~!hQddP0ZGP&~^Q<@!RA7ZCU;jV0;5$lz-#ZXVqu_JA2FX z)GzOy-h2eb&4_xtENIvNJs$qkc4rX?w1c_E@Jr9XOsB_mQ{-n5o2^zT#5&YjuEb>K z?AejaeEbKP^O(2$%j&Nrt1`{^&47|4BvKGToc|GJUq}LY>Y&|R>;ZUF(e<9n?X6p| zUi8~(ir)|(x=g%ua(+3v@mj+$@k4>6y0%|IE6{cJqPQKMzyUu7%aD?~dC`xCtWLcZ zS>BQ)+O^)F&TFzssTU={JhM>cQxo~b^slyqs8LC!rYI^M5hCJy3?@)tOrBu{{v?}Y z&X%ie+gQzsWuOz$gf85U8>16bbs6vU;qNMR>YeBcAcOa;O2Y1K8YHVfNp~>ED)ZcG zVa?&`i=!&i<-^1+tT@VEhUQtn$;<1h5vX**R^~{SixwCttd*Gf2}U<;Xx0wJ6yGnS zd9h|HCQ!oFg)*4wq$$$|a{1pfzxVcckIG0s=A#KSaU!Cb?0p5^mUb_qbAihC)LN#~ z>B$@TW)1kp4qIucN2!OC4DDakM^5Lf)gCz!VWMI=&1>DNFEqb?6~z?TO}#X&<1de% zTv@5uW85FpyK! zuuPhcsQ0$1oy~->&+>O!zBq{*2yK1G|Hm?5h&6!0~` zYf_u(YwO#|U|x!WJQhuUPM6_wRI6xip@1$^!Rl4|41XB1vLNdkV72!Nm^FUO%0dsM^U38^wLa#-m==nyV zGMjVxGTtO6IVBbKI$`FA^K|Ck%G#3T8B?g!!Ss>N;e);f&1Rd!w>p}>r!*JFd7VrknMUD_+f^gtmE-F{ zU{`nJ*aUNa0`~~mc92t32z0uEDK;)v zM+FoM>z8!sV7&&UKDhLGHWlIi_dt#XZ2COz4i{RYPi<31i8pk=KF9#S+*nd_e)PJV zC-^BU_)K;&SGTzskw6ssiNQFnPc0H6+aa}dw8$zZC(p-z!~#UKH)Hp`$Vd{>WHj)< zeXtP!R&CsU;R)x=a?mJf9lf9%k-GR?U+#g(%Cx(G|d zB}#?ca)03-Ho=ydSvQv2`sY^E7newpDYIt!wp)m5M(|kq9HGB8hi_GXX-nr&NB{h? zi;jUeb*EGNB&E2XJ81NbD)1&guZK;Ive@1MwZG8bJ;QA8{2suMYBxr*Nip9ivfaG6 zywskl+>Ba^NwhqY45COQvv%RZ<4ch)*MNtX!UL#8hrLHJ%!|xY-`LzOY4*5SGz)O= z#M5XMl|4_+JvrJLXzoio70Q(9^YPll_R}~`mp?T`J_tLXgVYskmkqEMrr2qXn2BEY zEx}z+D?|>q3cX`8R+7$NLH{aRCD5^B7D6fzbWCaARnjx;Me7{=wBRQdhX$UYEg+j( z`uy9Y=lxtv?zgb_oqQ707$4s)bcJCE3X>^<<`w=@Nj}j;P_Z0H!hf8~i!-xs!p~(; zNx`nYDW$wRORu2JAr?8F><;ou(|H%5`QHYn@IFtb_0cPFpl1^-1$CUQ(KtTSn7XAH z(KDzu;J>2{=-}|}SGffZa#CoXmsas_q)rFAf_k=}e8DcW5c1*7j^lX|K6T~;6p#*J zlc&oWylI>S-EEKZObeQH)4*pofnBjCW>gD?)Z{WbWCiZjcN~6{LT9s2WtR}1h~#u; zO#V(+cTBy61e|LW zh>@+V3g!vCcPQ6&usg&Qy^NXuJ7u&oWcMrbYV9bjp=V{yk@uGwb8n%Txt+Q3%(9?h zjdf*j3EJgDo%__nOwi8WHK53XrX#REjH4C|?Ye+B3^E~Q65E(PM8%K|pT|aC@sx*H zd*!=-@GF7|X-;xYCR`!!i*ivh(Yb!4=%K3LZ_B+e2m>s>y#^$`MT)=iPOP%(i%0=A zf*Wi5O}C|R7s25MqGIH!rv9zI?Q1-WIh|{T`J&rCr4^6@S1bOS?Z=p}ts1|66dELk z@4yD54;_Y1=kF7&zIP3+Y+(-4ts3|-P7Lw7Xy#-Q8k&tsoxWN<*Xr-{#dT)#8*f1% zG&y{aVhhU8%@!;lWujBwPx$1LH{aFgGjW*Y;6+CViWSALvdS+|_LGlDf#+xZoM^Xh zW-~u5eZ=BuX3RBERXOhs!&e$$)6Il3L7y9f`S=;;rrvzb5BqcDL_j^}bD{|&;d@EI zttQ|=S`AngLaq$*vDmqHlkCr)bfjxy;Lnili|&6`^4WJX5J^8e=WkZg>lXNfDZ_)D zW%0&Xv}Lk_`%v?Kz7X~o;NNodUX}?^^QEn3KPWloa&n1H&a2GOcFuWfY&`{0MDGqi zvnE|FwL4f^W5Ob2>8N&7I(yrr9sDTYjLbqennAVkmmjriIK}lV01plShA9jqV!=?` z+B@?OKbd#$j~wd#%g%P{1R_%Pcw)N0Q*{ufnpR{pI170KU3klLBaf93h^^C2|KZWL zQ*q2?@p}Rot*1ceIniUp?Bm8Sc%zWp^3ID`1|X9W6a9&TQ|YK3RkH4j;>g*&4v{(; zA6CMv!5`p!8_ceaRKJquofOaSDJ3J_bdn88rB(U}mE_esIvK0tROmceUcqR`_v(@b z9;lZGV4E;!X@G43!^HX*h@f6d8NsLu^o$7?;S4^vM z{Pn8in)^riWdJD{^m0`dYsI`zJ$IyVrbfHqJ`| zaNIz*CUbOIKE76Rz8L z&xJBF$i!}rza7upXLV_{U+i=a=J?j{#D0RAA&b3}B_<~5cW+!Z;%|=Jr2N8?L!;KQ z^8$Yt(qUY2u4Q&a1i`y$YAzjntHI34B8RYCshB9k?4TFW+bE;tW?;4bhcc^C%n!i3 zSyX4R=UU%)YDoQU)t$qgEGriNd|a;~XUUT0Fo*1p<66*oP7`rZuURB0Jm>d6ggQ%= zlV6^#K1)}YO!0lH_4e`Yn`72qYV$=^3M!S6($SAO#^a|}5$0IN+^)xP>9_8$)zrrU!;d%Pcti~)OvqrI-EF;lJ`B*6tg_p_5f!;blDd;T|TbY7( zedC#zr-kzt=@dp+43FXj_(#^Un>rVn!I7*cJHkv}4nU#w*hrmy-519?G98FXB5nm$ zd6&Hoq`>MT^is56a@m9$X<(JN9KbZ0&V3M^8(sZp)m<_@=uk&nXqoxJ`=N23^&Wp7 zVXlTPt?G9Osq3KQ%Of^iE6}eeAT*}gWCrtv!_Idjyo-!u3V8QYQ@?9=NJ8}0Vsz&0 zfLnwV^2tusxzg;-yk?bK37sZ~2Fcp@M182meR!p`6R3Wqb6f;T2D}o_S9L{HaPgC~ zVCf_;YfG)yA-L_~c3l!seZ#?pbuR?x?xgPc9=`w^!p0Q6?OF&!E9rX)^4C@;1V$SE z#ou=gTnln;2%tTZYM8xi-iC+S*X{Gh1m53X#;51rN*=lCeCO58CA{gxq4=qy&0jlm zj^ad|Weu2*z~FK()noIdNuM`0=1-3h>pdZP7M7CdI!MPgBcy>Vc&1EIjQX^#jE*eQOIbz1k=i3cC|W zyC&8TL2L&>n$dW3X_7*^etO!yhR0W3P;w`|wH`o3;~>ovgCTA!C)_7(RuU0VMfizd!lJ`K_hCWBKF z1K2K=o9nB>BAIRA<({+bXJ3F0{D@lAmn)xW9!7x~v$_*>TkY)@^5|>8biG`_fi#3- zTg`WJ;pCmzS**)d;-f1YJFjCXg?2xV5&NZK*NB0I}Ob~T543e~X6lyR9Ro*tj2 zIs760!8TaLAlMFA$r&`hzONa5CiERG)SU>Uy@>8kf9;U^|%9+{FjOy_v9aAVtvC}JQ8h2QaRD({$|^_)qo-LiCon{oAu z19fLkve98ky9vLzN63h}Z8##9)cd5p?Wosu zm_-U&bK%`AjuVM!aLS9{=AU(O?oK$BlTINuGj@0}v!BU~hRi4vJg2e6+ zx^G(JDU0C`y)PrWhr%92C2+jlaG4_!>JJS+f?Jj&e^q@Bh+;ZP#kV-=V)G7x6gb1G zwyUGyqf4cxtF&XZg#38!t^E()SB0574qKQ%4N%B6!2H`+d}H(cSykDos%C3gaP_HZ zLXNj639k|u5|}<4Xipq6G7@}z`^Jsdo5}2Xb_z1|uI)kNtr*WCw9xaK4elhH>G^drtRG;tDcT*T{oGsmOVMKE4#(u!J12h zvtP>>m?)`w|89SaSrDdY`dHI@#m<)#1*IuFO=Ag})6=w^3SK_EFwzGXY|yQ{9M&A# zHK886Zsxw)!@_~95_esY*iKPvdFfOyCEpi(^^S4vf8IbTieUnkr}8(u)W3;d!48#r zq^Eo5tAAAUbL-x|29zrOhkCO)T;y9hjEgITf#ujucFe=#XLfO})guI4ZH({~_QSL( zOvC22$P%Grx?2Z{X~*T+`8!EyU*uX0!9qDQ|6aNIF=d0-r>7brydx*p&G1X0>i)!q zWwD=?tX&kWDpv^oN+{Edam$Bo!hq~47yC2t2JT{g#y#qN@srY>dkXrgTZg=~2V;@> zjjvJ**b(lcpHHE>5o(^P5$!H=gbht%*~s!}Gc}OOX;A6su1V#G>IZa#v2`xwyhVjx zRUeS|^89SLPG+@FVP>Qltw07g27VK)T$;WmEFCf+%@{R|HJ;WE6 z$%YQAaBDLdOaW)m+GUsgt$WUxCoZ~wEfEG9;OIk(d~sr3p2cr1VRMLqJ~=3h6j5#T zEjk)NCe&^mHoYHqHOf0yUI;o|`(uw@0 zmgy%L2CTKAH09kBd?w(z4c3CG)r-xrVpKrO>Zn&>w*T%vy6Sx^UZxxtKt?Xg>jNrx zJ){G`1RXP0MH}@i*`cx{P1k@|d7))eO%&=EYAN9*NTf`6|GiQm4OUtJc_5y7{>J@l z#)=JGfJtEB^t)k`$lOgbla-^5s>A~ZBQ%6^%*-tah*lR;_e$_)UOZ4@&GSxX(Fk|Y zWg!sFl!cFh*(J3Kv$P6_m$4k(gJ!Xg`mVJ*m1HK1l$}u3YI+(w&mtqgW8g_0*IYq$ z`o~)LN*0|F^&r&+koM1H82|aV2UgQnhrGLMI*3yDzV-VzI--MP0`eN4s?4j15GSI` zMKlzs*C$JM9}Zh-xE8Bg^4CIRt1jR8e{aJq&XW#(CH1WY?}!}jJR`c~C*Ee-HGK@+ zFAUDJtUggq(=Bx#l1KixZ>oMjNj{)o+2srSFW>uZ#N(0BaapFRdlQD7VpC}Q#pO`V z^WgMYXw$D=ch7VL;*@q&+EJEuS~8q*)EN=Mph!k4=Gs)v6I9Mn|yJq9!=f~*LO!Qq8Q?R>VelgzJej|>gm_sviEJ_%|D~EmX z_{*10P;#Rk+we0LZRde;J^s}?=4MGC$ z1S#jfTSg$5+-Bzvb(3xOl=Akmh>$?xsgzTpl_zVH?M#~P-VUSF9yS&;@DO5~H>;&D z4t83RKxPL@fJ4v|&IhVhU;A{CeX43jN2HcNm)tA;&G+u{Ty0of%8qa2V8>3!RWRea zyi<*`sE{yy?w72;$>olcP@L7vG7H^EKMC1;5HDKM1GcYyrgcM!7zmhWBAejc^^?Jgu^Mde*ey6@o|VT)NiAf) z2y66kUbbh4t=&9;3$(Mld8zQ|s?(_&ZiOU`@0-=3AHJ!-s;q*`TgbdQ^KT1$i?952 zW)YZx3VnG7I?_!XaC6s`80_YwePTm;fJ!eqF;*|x z5L~LQHgyefX|6y8?!O?*A8>cE|sxwmYB84kpr?}Z!^+>3VP%z9Z5aOZ0MIpp(+ z&C0Vmb9jjnCFblp?`0E+4TBISnHc@j3xVC zvz+v}RR}n9xli%!ZA`ZZQ9Og(@%@26f_fqE5TEvySZ1%OW5gdrlpP6tiVyOcRWBR@ zdr@e=8Jt;rqb)7*x#6pcJ>Oq*kw#-9hrA;5k7wn#Zrz| zWxFLCeqv~M7J3cPopJF{NjDLtw>%t}1_2a{H<_4RwAa{CrU{3FAD zNm8df(#!nU6zX;|3D6=Gs>LtKb>^c~)9QIKk^hfx1#ldJzk)!iVm(_vjiyw|~=@ql|d<-dT_6<$>6s10h-p1J&t{$Pd@C zGssNns0#u*Bzlb6?v63O0l%d+wv(O4M|(FWCcyqR0% z4J6i{(@zkez&PCthT5n`7N@)qy9OB3iS9gycih0~K-*aryBsB1fa=3E5%a(zS zQvq8hx6pna-YY6*E_K6#;EYYRBBWa2@JruAg9os9M->qg5Sr!pzMx;>8>sPDL*u=b z$cINf%N;)|jsHW!hgRkoq6Ce4oE#a$D!;f~z>x8rwgsFJo2#F>>c~vFfvbF!*W9-I z(og;jjiKtU)UdNrp???~bjw+Ga!sgM+yw3>>F*3Zb5NI=kPRNt=#L8X)W?nr7}Swi z#{(NWZ~@*NE#3x#nNpzK$G+RjbmHPFye%+cvcI-!K4{svt{L#AB1>P?5QRgA)gty9 z078)la!WDw)L8T^=7D;*ch_f9-TS!-xNPJXFF8@UK)0YNToX+$hc7SAbL1|fU<}*3tBI(#8Mt(jHe;jC0A7MT!FF8P&4 zf6^#UBl#4>saF%7qoja-oXfg&s=V*3Tiko&jb(qk zxK9Q$uU>>uTW)J#RKE<`#C&_^tX%o!{fcPh+I(KJsM})V)b@l30UDn?kw9s^pB2yL zwDwx?NYtIJ=_A$POM z$x2BTLz_Bp$f!%3uD%D0vj$=ejUX{?3Dq@m2|3o}W@aiQU*#H`omMQ&(b-S_X#odg z>L|(O@L-924_+TpB>M4ZW*oTnl9hb=af6zW&CAZE!j;cyj;g0%8$;Hp-*J<7O4sY1 zulP~7tZhQQB(=1(faGYOmq^bD@0{PFic>A(7rh>yj4gj*WgX9q!db`-2=V$j>=WQ7!T@dVr`y0%n^9vs!gc%A`XiNG>)bk$K)}T+&+Gcf-``&rQNxF#cNglXqE(< zZv84d^JB9A@8lXF@x44RzS50cS@vaMIW0_@*8I{@hbyOec&0*8SmH^Oa-+_UU*b%@ zV>0^L#b1qRQ{}7+qK~n*;BQ~f1SevgqlTcsU|u7?2c4qn&k!4C={rvO_!Vo3LrMSi zhXOj+fP|Dx7aw#MB*fUM?sR3rV;+~F?^__y)GpYo>5R<8l<&b|(n z9jp9XM35NE*^eHn*iW;Xja(=4$_^c&+`PT}S&s_*iek5M0blujUk`-_$~qPO7@OQT z>^`H}kha-=rIxDh`#QuKcchs@I1*UnLD`c>Jq-v^<5z=q2Hd6X*$LGrR>)A^`dP;v zJtxu4`~N>ke{>!&jO;qw6d$s&l1pkhsk+~kX{BUXx-*@zJr!ZEe(=5jubi%gpT|^D z9dF7-JS`zF*c;B~6t_Pu$nesWs8JXwE$vg{B_xDNdZmuAjSL^T1_+3Z8)QfyVTvt& zi&<0`Wn(3|{Xb&eUzM5InkaR>lXX$(=XyV+;)%8mV^AIMHAu;y@S<4g;MpQ~8sYcu zR%gmfe{q%;N%`#K_c#~r6QHQuo2mBx9s3?v3=Z_d6*X~NYhyIv{AbUhffx2+&J{lY z!l$9@BgFP8d-K!$xLA`~2IJT%hDww5k155$E<5IqMq^P~J$?G@N+DBJBj*N3v1^9L zj!?A2a>Ss!KgfqMztf%;M8@R5Ery{Jw`2?xd=*bc7^*+&*fEvk`a*df^GQ z?fq^`@*(q~>gn~hMR?7^s^kl@9D`%te2e}bmOOWVQEf{-yzB$DzKv5x&9i{r7kt|E z7)96NJ63HEldP?k66Rsz)TJ~vPnPTYghdrWPo7QOS}Ct}>SN&eO8NeC6mQ9^Z}czJ zj!f&ndOfj3IDabbXmxffPJ{{)9Q!7bN{$Ct(o@0j`ew|lg6QBDR-y2NZBf`@(a(akAknw?&;GGq$$d>Fzgb8`{K6LregZK z07QT=izLvuQx_V41S~f)8zbl5G()aJm`c4&P@|ELo#qTVuL1H^cIj+nKV+)oi2>1- zG$$;kVmgA&Os}R$L25~Xv$Nezv29^}krJ?A7u8CUa|dweq1`6X_Veh17Pq>uS!R8F zT}8N^E6`Ctp0Z?5&&@=@q}d+Sl772OiYUCNCtxf^EATrm2F*}U>KMC9HbYNQ#8tp` ztx5Gxg%uU~(2L4_v(j0|Ei^(`c&XNA>118=ak<8@VcD z(yHa3)0=0KOPq6|>#(zygNTmUB!PZ}`Sd~Yh#Y>-ijVJ>StyBs`3;%gm=9J^HJ~1Q zI3YW}df^gV!-uhi^VS@%lc=r%V{PsY-t#HVc}Vv0IH1r;HX-ys-_*5wge`d#hfS>a zukf_TkT1`dW4c0h<^mqdghrxrbsbR4dr8iq5*@dg~c9}B|%?^F*YG$yAHm1~f8%&dh=rE?_%%dV4 zhr`oq;_)zdkUG%UWQ04vs>gZ(ZcS%bpXNMf?!;ECKg(E@7R=^Z#g0u09)ue~o1;-a zhhy1Nl;znmaBwdDCKlO+yhTS#X1}??3wZ<-yc_wUXE9dzJAlRP0avMHN`99*4W;;Q zp;&Y1w+lLl)+SJu)F2tGkmjdFX&Fi6{wbU=JB^;r(jK_iNMWr%?_}3CAj}a5tV*}(kb*xqFty#@&aS)*UtYL*n|zM7M_o5bU5NPZBlfp!~WDTQi9TZZuRu; z@;m{~>Q_{8dQ2h5SyTV(p^y0x2OZ~==y2!N4=DE{R9vRlQp`^b&qSW(^oF9$ zH9%{7X)dVqkp1X+kH2Y{=nUpWrl0{lF(y{MW7p-);s?Tvk1jMZ22??J5uOO9Cz0Q< z325Ek<>kIX^hlb%o26A%Gkib5P3s!)kz#cITvE(VFv(-8uXk=}gmvYv)z)G<4#T|I z5MX9k?%C~U5WV$s&9v^6CC2c@DH$?wbA0*(8vd+WvD-Z581)TN)BQ>NDEtQCEv+tqNQrn)tY9nQ zz=gT&G5IG<3Y5h~3^!<9V*5?uQV5cT(MXyVA$#H3`Za(%L?504#`X%RoVoYMS$uWZ z8|C53J)Y#;a^{0KU?W2C0K}z!(tY=tbkJf&%wE z%leCoE6Z6EU?wz;9KOPO4OlaA7^!A0M8~Hs7QQXYjb2#*&H-bXzOs%f7y6i?V{|5K zUoIC{Ih9OBZRvAOU@S?3&h6R?`TL@C0!^-UTN6Y%n!?my1BG83DnK+M&Uwi&NdqRw zAtMIh^G>Y2eYvL*99?s^`d2PzRiydS#K%ID9alQ>$AP~V1`pX9ZmZvwawhJRPGjFR z{ORhh(cDflS)*O+teITzR5A!A59B>8&Bn<@vNXEIWq0ZC4Q*;o$XEGtk9P*nGkx}> zUFYFgwD3BxEBDLdztkKZIUH(da&YVtebvf;OaDx%KTA~P=Zn<-wv^;q5{?Od61s{|NOqPQzZg@z$Sd zUYVCaWxt_60%y#Bv=ZYG&f^8A|9@EXkUHhY2(c6sr~N{!*09=U)6H!~4*BJGW47U7 z(9(|@^u*etiFbzbe$>z4kLG)Wos(DNTq5om`JZY?M&2#UM8tfcS0nD;hlG(Pfi$R; z3Rb_A;P>*tPukdKg}@Y|nZp$8W|<+ObGcWVp!SnZ z3u1vI^Mrn>?89%(S=FPba-{x1-r-XDY-o9jaMb~~e5paOD*1q0L4pHX!QoH2NU55q z%eyMfFU4@ANw>IE=~!b*((U>H7|iL@uH%&VW3d$a zy~;%F*hJzKq7%03qH<+NJ6JZ5eq3;snFE}WGF%UC&x(M1QYj%%EzII|LssQY91zWy zrqKT&-CAvrpYB?uF@3Ag?kzMZTF&)S>TW3sfQPJp%Ij0AZtr`9N5v?>p z;7CD)eJ^%xmkpJh=)sWW*PRgcikR%(1+VUBoT(e(8RhWOd4p$)7iM0wwRU8pNyu==Q_*P|#XB#mXQ`@j9&Pu1!#zcO9U1~_tg zx~Nn7*scX{;F>rY4Gn{~<#y#YL#_c|NY?-&(AKV_kDH`$j&H_o1^HO2)L$hX=yFR` zpo-|&{C=R)2&QvyhEuNI0_8Z;>jZ~Hq9n9N9$9o{rk?7UpQpN1Ry>24I?nRgoO)h7 zyPKq{6(A7f@Yk<~GTy76r#UL+fFMT-T=)Xv#gXb#221yi;QI5^#=8V#7&p}_kP z<8vv4tu0hF*V5XKuX3$c?+SUkFOj2d_N>c*ZOXI z8{Ql*Xl!{-Q2bma^uylxb=j1M#Yt7eQG<>V0}p@b-Hq`^!R;ft@eh4$Mb`iWuyp4) z+?5elDsq?&yh2eiJ?O=%iGi{5a1;0_<7q+2Z`Ti4mOc^X2{5B2ad@ z{v~yqST*5>cshY>iJ3(65|SvHf40D&&5d89e_Mv=5Y_h!^A0lMzY7$dqbgX?-&*&; zYK$))^%IOaK8Z|FRYvD>h>VbAHEVs<2d`MDrFrMGMp17dFF?hsmD!)j<&@nD-_$jm z*X83(qA|f+MiIQ~jseAeS>?W_|B;Wr@G~ebHAjltz^Y^zR!JnRkS$mGP7c5r~WS2&LCi>Khz9Z)JM{5_9(&ATqbCRs!v&X6b4fs0ouLkdHT~ zj4RlSi=M}Z*di{T5s-^=zJG zOfT5kS#ao1?!GhqzgQIIWboVrEw zkCH&mIpuGgw8u#@qfg@t>wut2ztv~{G^nMn?1u?F`am-cWpJ(?aQ?1$NU^l z9wQx2MQvLo(5!jXNd6C3Ul|pJ7k!JOBB&rBT}nuUbc2F)jKqMn(l87?)DY6rIdpfA z)WDF^-7&-f(m8{47Cqz)ckYsvJ%au4$NI(0Uck`vz= z*%(ji>qb;>QAvQk(26Mu)82ETS7?6W$CxTt+Gq9qt<}EiS(&P5R;mrdZS@7E6xl5? z-fH$CT6Xq9Xc;M~2)+jGQh2dyR8U5V$9kje=yRiHLoZPOps{bKX`MreDoRDDVKS<@ zI`>4^(yln?^*Z#LFxvOgiB@!yIg}%{e*lY;wtlU&uRN>U_C&vt>8(6-R`gZGcCww? zMB8#TnG^Nx8y=hRPGDmG7^nZLU#hbEh2K_E>26cv0HnxG{B=+Hu8|M8Z>cWvR2S-K z^UZ2xR4~^RF(;z5eX#M-rrgCS$zC3De#m2fJZnAAS}>j$U4nzXuNgEfNE$Oh34k1( z6P6V47pa&i=}tP`>dCG{n>~5sufsB0;-+A173vbVOIM(AU(5I7mCisEk9&9W_d~)0EcDKSRj|xeh;IyU244{jcxK^Gt zB!qqOnVFN+&nmsVoJ1T^`O7W2Tn7hIF@bjU{8dW)$$!R-SGNa^_ycLiZYfBs7XEXF zJJQAVlh(04>WN4Wrk-}QO-+aEwb0!(Jjh3B&b>QR+H}Rp;lTpIBle+`X`f?PQP_yg zBWAcfC=8X?i5w6dTQW^ywMZd{dx9)%adw56=G)$rKP4U-*o+bx{IyvNZXOz#I zIPD&UCDTS~UxApQ-jF!dsg|%8lqUf9|B|7sC-*BM{ehE^*<+BHh7a%bb^f35{`c#< zgS)kQuk|+3?HuPA9?`CXCMy9`AS5KHTY{100gx}KI zDKDu7!)ec#-}18cAlh!|^R&2@vJf2D{y}My9r4NV`TtEe|ES)x9af*ypp)(<&es(0 z)0tQw+6Al3#=Qn=-(?xA^NJh(By5d1Sw6oqBJYh@9~tK_56R+0FVH^9t&W%q*BnXV z7fnwr(_V~@u#}zC6Dhk4DRwHeo0F9{kibrA+;9_if=5Pm<)$$_HulvoegPUvXQKQa0>-*j`R28;nR&~7XSw|!!l0w=w z(X#EjE^H}8aR%XXH=cTn)v{A%J8&!aBz+eFp^ohOTNUI>i=lDRLglph>Njz_KC`Lo zOloy1>ex@HXVnndM!T^o*huoi+rbKqA%Z&V6e)D8vtDZJTK2RnK%>y(wGlQ?<$3oz zVxTys+TNm8;XWH^YcsHLfcYxu@T-NL$CFBec)5Y&=}}OEg7$mLn;gdg1B>W*OZAQ< z4y}ECMav*og{Fks$)Li*iGV*vPwKBsdlhVumNV@&$=j9iEWl!!QTTL!a?uj4I<7WAAb)+6X=1%r z%3$t}*>-}*c)G!M7wDYQ@tST~62*UB=Ub+kMnjs&&2@DP_@l>jp6qtGtf1Xv#lCRt z1xUhK}b%#Wbq2RM{;YnJD65|(JqcJq8Y#SseI2IWuuT{WPa3xBP zFrugSPZZW12Ql$Hoqb$lva;;E=5^Q3xM@05I&K&?QV4tXW5K7a!Bw51^HjYVCn9++ z=Z6t=K86+9H=-p>^Qh&K+xI$;cy$m)hi8FH8DVutxbE5_EnQ!}y;`T$5qe1lB5NvJ z#NCyb)RW#Srzs(mduMhjX8R`bMutpRcXFgHR-65MqjF+BK1g$}eU`k`Nq{f1W7eO| z$T@7vabIMh>9%9qvW6CngI*2h|Ku{wADAdow2KLG&;{MK?UV@dBsb1|GC3KKgFX>cc8!S!tP+VqHIsp zGO>exxmlcDMg8FbMuCWkJg11wCFJe)Wt5`EIszs7?OGtd3hnt`EelPCW}}Wbt-VQ9 z72&MELUktw1{O}+A)OvtAEgDaFGaSMO&VW88lOJr=}EXLIY`{QiAUv-UPq-FnC_K1 zWZdsb$DPY-)x*suv)6qk+F0dx3#K3R@Q@6z{^5wYB6Lr^PUqI%RcG&rrNoUg3ppzE znoNj$!ZTfrLWX<+1}yuk`M2tq&U3(Xw%I-+jqO|0;w-7{<3)={lO=~bPAB?i53F7$ zfe)0F2jRu`d>?>E&aZG@dBYYOlaebpSNdEgIkJgek5J2_c=nJ z=(!^>?O^+7hq#O6lvf}B8XpiI^dCe>8fU|!%g|=YYt3q>2EIqY^EX9mu1wL0KspbRE z<+JqrES>!}V!!Tzg$)*K#{1CK5K1guMT*wu_TVVb!k0+oLJl>aKjyzAU4Lzx8QJNc zY9u_H7%M;`e}Z8|{J=BP*aJAZn#@JyP*cNM62F^oYVap7XR#kl!qYHf6(ZbM4!v#D zi3R7{KvTfHgX2uhaP(!#WY|9Go;%8A!4mE(aa?qM!IJC|H}ndD4o9|n$eBI}fS%8K z6TNkT9(B#Et|6bb$!Gy~w1=-#A~Ja;=B8CPnbUo>7Z=anl*;cvTon2hbWFQ;`?x0k zG<`MLt-suXOIRpAm@?-?=ks+pT@mRkm(#1~>xYgcFT>5);iid*Xl&m9BA#ZXp@~meZImREaJmzjUvez-C*h2Du*|zC85J~ zGzQ`Zl&Yc=G8|JC`yZZqG@u=<_LPM=Ra)nvaRDD80*8OTP4CnDx@awIa__Kq=wZ|J zz~KDtJK-t!^)}4()oCbqi#|`JvGgjE!Q~;tV(9Cr$Ye7#@fJYT=5nfGNY8hXD)z+T zB{u%gojPx2n6VN{UN0sD2q?4|mg{RRj7bND8{t6)u__6ofSrY1BdQr~phS1$S zfBRZ_&TrBHW_OsGfAEgtF^&H?eY04~Ob3Ct^&btng?YLTgs-4kpNONivu;hNSF*>j z{d;KpZyXPs8R6(Ebq7|WMJ_%+DQ^y$=< ziu_X+%h2+{7EpX5kPzxd(BOS}cz2oihJ%E67M0w3xublkz&ngYX0a(NJu6;#A}5p` zce7@>{*It5X>JM=8BQPQ=tsH(&e$8a09RI1em`wR4nVDT?E3#+inCRx-rN3>7zb`S z&L^hG6`1ocfq_7XbkbE$z=?b7X_J?#OXyu?41HfWMGU6o8-hB)n^(ElxzWcG$v}6S zl4}28dPP#*A^o4DURokbo(p}xD2~(d$>|Ok)&TtM!*PBO9g3u+ZhN4o<>n^Pf+^)z z*ISf++)@|7v#XGpj35a1c%3(dPWu7-pS3QuB=uuJey0WjR=d42-R05e6QS!!B!!JS zqpq@qq5%;}6O~wmIL#VCf|hYjmCWl>ql(sMZyC^98@*Mh{wXi-*fjkgPMKtuVKl&h>m@n1tgbi4v$b3C z`!h-6H%Qc;@hhVvySwoej?tWa(F|y5wDIrb71nqGv3Aj6_1s#zGgcM1Dl%2=!q@@; z0e{G=wzHB0ZJF@-D+jJ|{hAW<&m$%xAFdaTnZHO)#%z<%yr#z6o`uqjq#`LxQF2?Y z|8NqVtCwMO?Ioz`&rmrUKg&8xFYJ-y_Nkz$P8~tdmE@N4B)z(?@`p(?VX10uM@$7* z!~I&~`C%GNoFMTd2dMm1vRXW>tk3OP;D^L}o>eZL+~U&?dv)!JL|wO{2rR zn@c_=d$~VjlDi`0<)q47mUwcG5H;Pc>5p5*e>kebhmf6Vm7Qn@?5cChI`@4`O=FGJ zRv(+g3D1CGJ??}L((T2P`F2GLb3s1T@EilYnz`aVXCV2~f!{uZLeBb7omD>z@Fg5A zT#rpbxO~*lWYW}*fM`aM=a-CopvrGqn=bXHcmuv6i7{B4bH)DZ74kyTn@Bpi82Xu% z(39rZ92KrfA?4KrGRNSs2i}1FyCPAA4VNQz|6zUqHNn`uil*-?Is1k;ZPrx}5?nlZ zRN;+IGeTF_T-K+`n2Mw<{9zV`oxK~cL}xn^2ZeHG zj%lruf+cG@U-gbm{_a=jZ29_okM@yk%nt~s_i&U+b=crLEA8EK&q3$&SN*4eS=0E3 zbWyq@f9J@JS1FcF9t!ut5@#JfJeKBJ_BT524G9FK8tbn(<-0dAW14!T>M9zX?Pc%p z)Augu&|ekMLI*0h|8V|zp9d|@N%#$Maz3Yd!bc!f#V`!)+qlwKo-NQ-WW;*MAV1xB zuChA1_61#VXBN{BkUMR)$oA!MN+spC{lj5DBD1+*5IXf)D(5=8x>7mxrl-GITFQu= zI4bweITu{}iRBd?mjd76%{kKP$Lt;*cZQu^e}4`7nSe9FFu!agsSe1>8co!oGrU*# zPTO5Zd**+=)>m!zSCJ|Yq!ht8~=`_D9nbg zIciC|*4=A5u}l|k-0vj>cH4LDM%Q7?`a}(sU*u13A!~(Z-`rP}&`UqOmuqn65r`}R z)SVq+I4zn?>pov9Xzn))j%RDwEVSrJJd}*<`1o@FYL3kDOmvq6IoK|}mjy@N{skbL zPW*arK+$$Puk`-mv_9}jvv!v_N`LT8X8P+UGJXF(jb`bnI7jp_qd$?SZ;Q6@@ndCT zzx<&^MePSyBKgzt{T?7XZPYCx=(3mQgY}=G9)HNP$=<}xXCLAJaBFUdg}pdEtdbX4 z?%Q0PKwLhFiV#MSE-V)(P1=l!T6I^kwf0aVPLX!sYub=$Lta z;SSm$eXscl7k81axcou9awv^3&b||A9~t0vdGOGS;j5ftI~Eg}ijS_<&);bx1zc1h zF#Ih8@^#J$*i!5sMvr^Jjqf_xurZC1?&Hy}E@mTrC!zGq)}U@Z*BcGD_sab z$ToTY?rRK_*rgZGy2p8pC*9cXY|z}U4ZEnWR3 zwp+HZa$3_XQa!QUB!hz*FMITTf>y3M*5&nn^~FwkaxLW;az$pGph!(R0{0gK1~SjN zp~EFoWABzh<-3OUdToiiU+Bd6zku!u#>4W;OnKYd1jcQ-b8o$?6@U?0G$Oke70UBC z&XDIuhE0_R`U9U0E9<_lO#Fp<+ZEW2;u{Uf=Rpz)lakYaxZS2`^$_|lg(qik4fqV; zFf1F7Smq6OaI5jUo$R#E1r}E{*xER6Eow5uy{`65nyZ7o->?X2)x?@-Zb$5rN1o+| z3{k1R{@qvCoqwvYn_;a@y$9(R`1FK6i|Ceuaa%$n_4>(lwYnnaLEiSSP0R@I%&vn* zeJR|4U11?@nT0|BM@dhkJhNTq65FXfrwR4kJ_hgPgbC8NHm-#uKR^1_Qu*JD~lK~O?86|}gR>_)$(Ra9>USILyiJZB%v@CduO z0nC}6kZQT@`k-h=^v7u!dVEVfVtOK_V?HqK-Hk4uXb5maS{9%P=f8>m z(eJBS8%q}3Kc#HnCPW7AuGXK;)#S(Tq4H%u-l^_ciy$4jE?St!Ew)@M;SOI1ai619 zstFajT`p?D$+PM1^yz5J5qk_jFm9Jxh7HFJw^Q4-to8#$h$vIccrH45YDFbo6z3KD zA3br6lFI(TMK6INqiI+JvNcqq|IsnwZw$VHjnxOqvsIsPDa>sNGPUgpVyorjlcGoX zYG7qiL2%H;DEV)*a7Rs{ZZKTYnyem}t)>ZY=}&moy+VKbj*bmDuu%utX1oPBw@0rO zVy=m79sc2{dsR%bY?BLkMY}E+q~6Ttx3T%a>__mcY-bv1CT(h^^WzuHPAU;y>HQW9 z?e^Jtn(9(X9Q|~G2NMl_0kM5K-WYCiNCcP+o zWcd==Wj3L%*aDBZ{OTFmbfT|ckKw-cow#ECPLjQyp5>EbCg~oQIMKE))OT5H*%StJ3O)qq=JE)Bjw*C}Tx6MM|MTe^1Ew!EAIWVVf1x7+ z3M$j-QuDji{-4mFA8*B;MG~?JD}V-yn;C^_3weUfb0?~znG{f3Ntd%d=jEvSU-^(y zIH1AYmV-1t4hIFKz*V*1*~U>S9}17C`;|$cX>FUOlG@^Gu!2fXoYh4>8J}Q*d(?!ZaRV$WplL9g?a)v$fi^Ybc@@n9jg} zbo*|fyHuu-;9{h#YyKEoFRqhgmpnz2w$Mymo;)#_XYe#Dw1kl+8lbbN%#Sa`T!f!R zYD*ow$U$nO7BN;q^8WLeZ`iRAyZ9u9Z~v1;zrc3R$&`D^q1F`hW#FTe_ZtO#1Hv2a zYfXp4ry9f`@lN(D28K8$f8bA}%axNczsr8R**?zxq}w#?EU&J|MVpuOIq`ai(U3V? z=ELPK;_bl8yG{+`=R8F!tEU31cL!?Cq`W>~JO|dA2?X5-soDO z2uv0v(fZ0aTQ$pDNPLdj`4nyU+~^>AEUPS@v5AZ6mf7Ein{d%G#Hp!LfH0~7E-lXR zpRN;Bk7Ox$XLcHk^Ljt_HPXw-+m|p@7z)tl{N`bq`2mS)T3gBrP1DoFp< zkn{V~9C4-Jw(-9q@3Zt~C2RWqOnO(s)&1*2wm>dnIrc>U;E3GA+oXmM*5z z+=2JK_3irHgW3915DNGg{=v&jA4Uq8+4n^a-(6glX9r^1ZtbRiFNEX3iJ(s|-Rtrv ze@S@6eUr**r>B?R8XV}$=k9+ez`1dGNJ3seEO+SqBFtr4OQfu~O7(!Qb&zfnjhcr5 zi>$Rs0*f@05n~5w7{?<{Q|c;k9TZUOdE zWX_=%=N|hnAii2{O5cn7%(7X!ZUoks<~tz4OL0y8cXFGp%kd68Tf3$}&;p zaLMQel;mEbzRXEe@-cz1Tmw(08ESTRE*POw^6WLq9$i;b#$> zEoi$|b;lIZy+|Ho6<1;erK3gw!$(kk1Hz5#fxmh+>o)?CiO$SRhiBB|2OSxwe(6^t zbWP5NxSSoQt&thOmONitFzW?O4F?q>ietZ?ZkvOmaYP2U!BCQ~KxA%J#1dWJETOKEJdRmC*M*j{f z%@CMW16$bxgIZMJe(f96Lt8~x2DasJ=!5y-3(?pu1On7`wd_&jI@wYTTqDn(+b`MTxMdI4Ka2MdJS1$Ht| zjfwQl-!JAJIuhSJS@$<+50K6?%Gg_)A>8ixeb_=b+$dbVk$_b;&Td#rEps#`>qL6K z$EV!aY7Wwfpv9wtQvbt=*{CX^5 zd)>S5c6Xdz{jTd4<}rJbV}INWMYNtNCk&#kTy%deew@C}#`d21xn_oBcwpLVkmcxyeO=r_dLg0OZz{HG1!e}aoR-K~)(DK8HXqgw zf;p7dZVJVpPs}_#i-hClZCA_B>=uHAW&ACb8Up2Tjlfko4%)|xxAp$}M+gCFT?ZxaDX{OAbv}Ggaao-$f|j>0CdZcM zDAUBwI2^C}P7Ck-!{Oc#jdc2y^{WrDx`V(>l8g5K9ep0t!R4dlft3F~=AxB19xGth zW2Dn?F9Fc_|24B{{G3vRfcPUIOT1` zB~ZU_TPH<}|CfUOpIhJI3dbb184PiEIoxmv4RRJMUnyQ7jni2Z`SRIVsqHy!<;-uD zA8*!X1Zr#Ye$>1##s7lc$lpT4)G=T7I9m*qjf|E?8Fe>w4X$7Q`1zVng*WR&ALE{d zR#WfI@=6wjFrd2HA9+@;Xco0sbnqY=@)& zwVm%4#NOrQ=1MQ;gel&mDuOdg$vT`EbJ~-}?aivj# zTCSSUOH~R?v3r5!ZuP>b(1P!Adx zr$IW?sBp{F{{EkCeMUkEEna}Q-;0xfI63^)W2gJR1;e^v1^jY)u#1%hQP#+!d94Qb z;I~Nni@=ZcoSUfcJR1jhq%m9NA23pVET4+lBTVU)HT z3h|d!6Z-^)jiMggJwhuNxOc;zZ(E$YAfl9LpLT5!>Op0IErljJZt#?!`gz)o*v#roadd1PmvoEVv>eKnwZhdn60 zk}6w|{~N!6-sn%wj1I{*oVu{HaZFb&1X+5tXw2rk@suC*7+A@+zvu59y1aXr(Xgto zjvkR;nMgwO_0!HJ(0zFp8fp4-@K8Z-ormJA<^0(uxaIfixn|;;k{AP#t8IPbj2ND{ z|9a}xGM^2}WA?x4A=V_?Su-iRLq;*5xxT^{I7-W#A^H2B*25XHJ7LJ6WKrsqGmF|g zv5eMEOUEENKg-_2kl?B6++AWMmtNdJ9&l+8uckIKx4$`NP= zSl*H>YSi+@A{)-hq*+nvqJ?kP3k})VG}@Gj9e0AD&w`k(n3oTym5SnDT!6i{LL!|0wH}( zD&Ut@P$+I8NZkfP-@Czk60a~^o!s~wv4T#`#EvXCmDXj*xGoKXntq+~!7b)a>T7<7;r;>oa2_Wo?7G$d z#focCYe-47sa-{$S^v&(oy?i7m{~3Mvsg|vzi0H$@4_0xd&rTl+M&E+V2DuF;6|{We`QT>MTr8+}!}2R^uunrX8AaJR zS&mh0?kJJ-m11=215D^~9OV=#a-7Iq3q-kiMOO9iDJDeuvtG68v&)anO)2LdeHT2$ z&IBT9OWwKi@+~+2!}+;~yZ|y+V3bAn%4RmO`q>0XD;ZS-o>e{fn9?4!+}@7sjmJM6 zT@S)FF-~UzpWb0wP?HOdqwqz_Lpka$E2-X`;gnOW(?SvH7kE;bdo{n1@~{st*L1sP z$P#Or%Ic4|Z7_7miUy~{N0xZc`pwMeMFAE6aGnYNTT-b`U z^BihV2o-lNiWt3A%cwyVHqFl92$DcFdn=1=hZ3UHw>C2NP9+TOO-W3KBW>u7XXtl3 z;jzFoY3ZTGSll5M_Qu7Le>g&wkEhIzLWAUTi`&oVTg*6E$1tWTKV#{AZPq>3z2)V}&Fy_l|qX zkhdF?9k?fO8nq5ViypVz9h#NuCbuc^WJZ6w4^aO+%T`Wu)Zw9V!oFruRv}N5s2oT5 z>G$5d+d1Q;$tshA3HZEK=5O|QPux)Rr6xVxzWM6%gSknY;z~2(tt*ez3zPkuoEPdoePQLhOEp?i)mipCph7ZS zbdk$+9@`54dzp7{_<{;-$nd{HxMa5o9qMK#6SRkaR{=0pl*_e z@1UKLu)sy!Mv?bF9LBQcfLhW#&jkzGMc~6dkZMt_0e=FCad|WbHlZ^KTDmAlaWVN; zhQHUNk)VBqt@5t6uPXF#J`#Vu%G;>UDOZ94?l@}gL=08)9nZEc9;gFB@iwbevlN)% zV_eEt&)UdT!zXGSltw5)sS6G(6OS-Y(xDX6c0exn+VFJ$acKH9suzSFtx2%Vhz~i8 zpyd80B62xq@9vIyf;{rd&3$2fFvx98VLRLVYjeD!qwpWjWQN+gWjgX zFXbP<`T0({zr{C8Xns`J8@OBBhBh8v@l7p4N=n+YTkz=lk}Gt51uS`=wgu%SGRj5T z(AsbUkUn->dk#|EEvikZmb+Vff%xVfrc>$@s4{NI(qrL!z5(8uI6ciyH-RBwD82WYdzbfUCQ0;!xL zkqk>66P*UT&;-&^Sh3)tpwTA{)8^Xbnik-4S7CJZ@Yer>V170|txhwwiz`j6_RdBM zf4l^r6Xj8GihDXr1zzlQb-Xlj{h#Dcx`YTh%EVPHE85-{qN z|6cu`KgJCSCj1>&`@NtpCj0!oJX0x|Ci6lUl|;h|nJghwi}q%cfL!e$$=kTSLxaNT zI7`_TC%o-EV6|bH`|LjBH=~QWB0Kk=W7phLlj67+?0pL+T4eyYYUlk*W;^T0Svms( zP5MgV1LBowYN=ReBAi-~*QtK?0nqai_wVgo8EN25`7iqI zbQ}NqG8OqNAz42P_m=g$s)IvdVdNKEGOP3-x(T*faYIbV7$=sh(x;#J7ua@J@UK*o zH&sC_U;90#(qlSYGSc(Nz%U72l8eV|k`J@RE;`J)!yEKC7LZanaJY3029w3syV_p2 z!W7)VFoq26kG7ZPDP*d1o31xM#HZTd=xjL@T^Z#OC*s)}>z3GNz@{Vx$lL1OVP*7N zVc@i5D%XY8)Ka-yx7`Yj1;p~Gg8byRniJZnolQRvq|SNCA-w))A3}#A0FHGnianS6 z2&=6>;PtwXL`J8$OHpmtvd;WoDv92%TmCxY&g1-EMnKS6;nMbG7)`BQex6D@fwcTCzoE2-_C}O$2PU+q^lRb!t#jjQeld<*Ww1B zB~|st>~h|K*7uq@VhhYSlGH`~n4}^$HrZ<#CJ%4vvMyt1n{h*P|9-aU!^!HRr#=+% zgTH)fUWmv^f$#UBl&ZUvDhJzoFF2`7{290g=i82X$H1&o-lV3~JWtVL>~w4vyOuuB zuHwhPT{p~lP@f&^I4r7`xy+!WGZ$_P#o3gvRDa|bFarl0bi4o$g)3&;5>m8r};e7gaC~?WF(Nfh{(PB`1zbbGV zcJ8OgVkW!>SXY+6L-kUJ9Qf~(wcq=A)8E+KI1Yi?-TTJQ&gN?}OI#gk*6NY%eG78Y z2oD3DPSOGTC)e;iMIp20rbfN)s3e(fUtc%tJ_U3eCEpl&pLq+IU<^&%r{WTRG{e8^RpvmE$!ngCLumx06vS+}-vyyZuDcv2jY59hctC~HMuG8IyJwf*^k zJ@@sm*(zp+9|3tLtI;QZ@-2KBAj@83hEb0y0INrGBXTLyzdL_cCP(p5k`FDhaW~xL zAVS<2Gql0v@8!?MC-@8jEsHu5q@QY|6}_*SSqDgAr8sVQyand{Iv$E{Rj2>qaPeag zB3J7~c%Uv1@m=hc+y+z(AT*IJzfz~ET^pf9Z}*qTC3d4CUjpvTkq2+jS)}@8lpl9t ztnIz%O~(3aK4Rr&<_3S~)!FcF72_cKMtxzDUzskuVQk&8vLH%nNT~MN_#=PUfV-8C zyh1_}wa&qYNmIc&z&3GUJEs2XMx*DijMSeMhX|6DmWxSo?Y2SC?pbQ=o@vg8jAn;| zdP~@OG~%dMLS#aZ$FO^Ej#GdOmpg6LxI4+7r0oY@s$~n**7(t^&O@I6()Qp&)|*+k zhsB%3^Ye@NlNIJ7D~?U_8_nj2jY69i?}L9hPkaS^xx5NoPmd~dOfyjD)B5)6Ew;a$ zGN4w{`W7)=`>S*=R_=w3ZM{j()NsBn`)%jEuEZ*^jp?dAd4%(sa2Wbojb)!S^1_s(04e3 zooq|wh6wkKSUKsU_zf?qp2ny^J%`8AL+hXJxcu6}--j6h04e~C^iscz?hav5Baf0~ zGJo6HUFGrgkWGtY1GII{W*z zLWQPkJlyXG}Z`N zrTPn^;IXj`AEmknFi(*LJXtE0C9%GPm3_FtCEdEU&1 z`H6j7wqV=P<9(5mqmBC7GZE(KJzdICmD%OttgjVurbApLNw#bCrX&iuHtO2N7#K@kvFj71;Q?gE z@)=r*2zQbnn%k7_V`{%SJOp6gAvQ4IP5T2D>DjT4VOjAyH|LB)=2h4aYFsoBu=9(n z|M6!$OG(uq1Ty2_z+YLdw%<;#!_zaxmK^K5H;O+~fy_9JX)}aOEwL3LkbSX6$i0>~%ch6((m$O2hkdT9<;=?Lb#&VV=^IFe z99rG3Ro9=44Wv#~Sp9ODXJ8PH*jE50P|%MwSu`kLr#WtIR>h`}=nx`rLNFYpo*TWxVAnm?M0 za;6ugKd%kyk#BJxq8Rr>sNcG21tm8q{= ztK+aK`iFy>+iT!?ovLJeC71dN=c|(qOj!c1E%7iq`k~iuo3Nu9>W_yHVp>XkPX86E_+B^B4PGO`E!X!x5L|S`Tm~=LnHgSo>tKR>_@okNehy zPJG8ruFKrcN+wGmGCDfo9}dq6fC>DP&_dB>ipnv505P^T!jd1$?Ubi?1?L>*8>=lI zP%&wCr{+I_l@{L`nA{VU^6KA_cKL-n;}qU8`VbI8pYI~r@DCt5>S*mpzgtj_mK znIGToHHxNFXo=-L3TodV{9>KG^d!;y^r)VryI31NrW@vc&)ukU3#e9x9L zrU}Z}D1CcXZ62v8OQ$}#GLH}eeLE`C9em>ey&L@Ie5V3;F%W1mn}Gs!Wv01*E=qKx z0Of~P|INlFi&F_}t2MYgq4;%W+AnGQSTH*IFh1!hb{l60@~B%Q(m%zCYgzYF6X|$a zuGKyk7VwbkLV+~~b)&4dtY);ro(UAgr&Wd>Z zROLpO2=9d+C+XS(I_oerYfwyR#b|g6oHr(BnE1hk2*{?)5wydQA%Yk~ckG*;dDIsT zc9Vlb?`2MeWiOV;@+{ISDm>Q@iqvETrw0qG6(9%hJ7O|jC3L=Dau&T`8tIQy#{&~J zA|E^pp8n6)gryxYpj=O2d6>q0i{Z;{vnLM+HLBN=IU&sUkNV_oO5hgKru#q1`6^X$ zaqg)8+{CUsspP4;CA;s>8(@$8oypol0q@wo_Q#ly0CTfv9Z&J8&&&T_BwuR7k~b{v z<&Wt`_&(IY-&k_OH)f$RtP&RN+5r+IiDjUyogk~Ny2ZY;m&LM^mvfbWeJh?5`mYyA z>$7VWH3rBKLB#!kPeXZ)rL!Be2ez^<8!rq{g2ZnR<))9qW~EkjH~G=m8$X-1Vs}T5@v3 zvotM-rWb6N%u*C{j_X3jcdP|RL$=yES2H4=oisKYV=t#HtMCpOz$=rc(Z>-)GEWbk z>KwjCF5GH5`z8Iu0eI~;odegVFv|+{oR~uUhI|7N<>O^_cM&3Znel^%7f#yoF-nW(Gk$zqQCm<+eZWi2a*UQiqjp#|UcR$~qnF zspnpu5WO0)e5&xrm&ie5{HE$3PN{YfPqi9v^JXPtr=i|G=EG(qp^5Imq~?8OvhSM9 z-IkdnHKBp-%o-+mb)UVT(cD2j))Dcan;^dr5g_Z{4OcxAk0zRl1! z8{(2}@U=!vf-QBg5Cf|YZ*IE$5Qu|Y-Vo-HAcln5ToeSn4crL7(8=p)ePFQu;vIYC z4!H<9GvQ!gl-AqkoDP+jV6%|gfJzmG42CaPKhb^|bQsMJlL>90U>?u>hodJMo87bw z4P2fUtsPUwwF3m?|3ywh$0wxK8y7wC$ChF)`4WcF;{wsr1f*NDHeHCj&%U<*b-&j? z9AvG7Gj0yKCp*+m_v7;8qjHaal> zO4mDurfAG)s4u0bK5UztGOai3uInKAQ5~(tKdxs@>g)@|W^ywk52o78NBz zf-+_e&v)%$UAn44a_WE@u+p^4no_!T*hD(_almMcGdsyqX1&hDHGR2rfQeqIT-f`I z)|s+l%3zmsj`%J7H`NttHLr$+NGS9+Q@+f#bK3I_sN7g0Js@aW5LU0sATJ-&E4v8W zIuX^G@Vw^9YMP`4O&73Ywq7y@0f-1INCL`5^mXsOKpzPwpt^8;@&ZT#VRO3T1U1*t zVqRUpO{PUGH(h^;M*EhA=8VK>{O6dw6HdN>FsDKb7M7e0z*FxYm6J3p4QMtr*7))M zCmUTC2#e}-PCHBXp)dxSUlAm4lKx|fhv>Q{_yjNwA)#xxQ;pC=ZiUTYfhfd&5=K^^Qxw@{Mmn?22 zh&%vKRkjk)yeSt<&d6vGbnlPQY5uk`Vwy|g)M9zgLNss#y|}-4e!+oym*>z^K3>RB zyfZb2^08`)ovD6^8?JGvT=0$R8tXhM$*Lx_6r8P`>>rt~uGo>b*xY)u4jtpA6D^Ld zj6!S&PK~{IX>YbNH5qML&y@=@n42K*dU|5t5@VZPJSG_*(n!A(SA$J=fZ+hWK=G9^ zMX5F*!t<4jzax9J4=7oD!b`iVg#a_YGN-?DS4|_4iKpFvzC-XIEN2>UUBZ1GY5Vu` zA)QI&6EoM`7ns>BkYcjrI5c?Jl9#&@lfm+$Cm2To2LGt1Kwd9$Ci!Pu?P*m}!FCK$ z(wZ^bBkEa?4_y1M{F~9wSM<7srGp7{2-9sh+hX46ZweG93PIh_RM0_G)4raW@Iyh6 z#3VqQZUHM|4*z;A%w*4hhxA7xba}@1|BRoQWAcP{+3)tu z$qj#+ju?t(gHNo?x2+s@-3Z0Kcnki{ADKy;4qI{`X`7y0cJS@2m!ghMy2(e0-oaox z=iF4)cBgu7e~mNT&m?DWZ<3HZte?t}4tV02#ia0T)v`a;oi?u3;t2q8sLHR19Zva_E4NXR><6N`y&3`!9U!b*(N5HiG3* zxemem8bO-C+}fC|Ema#)UWnj+5`Pv!%*xopqQgsOk)=vybZ>aS zV}q7vB3h^UQ$#W0p8s&1^~cTUog@&;NV>WuP8QypB+a6k+2Z!6dY!e^9MWA{1D27& z1LE6*@C-fDPisZGl@$d16D?K;Hb_R-_pUZRiWsB4;+T*|ZN|klO3LSSUFp;e-kTby zW%HWsm!5ri5^MBtxk2x;o^KRKyzL`qf7Gle+vo?9HBAcFilm(*2z4{aRy99~XI1Qy z7i7v0hrNkdE-WPf0Wm1FuOC7txd$2KuxN#SVpLE@D_zyo3F_xo;XBSK11{6OxSB|g zsRUkab{_Kdv~ z)QFwfd-VVPJ^G3O))bWP`MqT8eQ_~4=bxEx14IKgz$h2 zp%XU9t`@}Gzh}=@boCll@8>58HElcT5#>DGS~II`DR zoko4irXYLahrp?I{f)#JI`*wWDZ=utJzgVY?7_OVI5Od%lg*rnP1pkHqD;Cpw` zwZh-vqVDQ{6uExQPF6Wb3*Ks+Lrsb3m6W?ZXE7kbSNgHFFhG{@S+Fj54%VQnIz%aK z!EyCgGRgVf-@}DO%B*^scCik2$Yt~$z3AfMht zY*I$$5zDuau7-lb>&01=h&?A32*EHdI)yuHeOrLXM4rwH_4C?KlSytE?{jbac4K`g za7RED_aLbxLrh%(hVx)6-pz{m-k{i;HoF-byCa7%aV zg7aRTc)uU%QeD~kcwU0@hXXiH^ZMk3T}n{xn(7|!646%op3%XrUSGxa`Si@}11ws7 zx2LN$erR5qd^X^6k1j)RpJKlp9wNF+Dmi|FKhpyj$;67*{mv|5ADB{MciMQeE}&|y zTI|c8mygM0c{2;F8FQN7V54t8W1p&N=$7gsI5&AI0-5+Y1;sKG*(68~zDd8Yw%;We za{gfTwSgwCe5g!PfXZxGhePROeM?D$gGarG%bCM1zvtPB*^S=UWoLZO{Z#k&dDR7j zx~$Xv06oGj%e-{Hm`t&lHvr4e+cZNTm*(c0O$#SX*x4C5YL8uf=P=Dek9djW zlS$-)lZzK1(Q_|R=!q=s&Vo(KZ`u6?KY(Y8q$N1uM8anJ+8COa@@{=qwAj=BuZY`L zr~wF(&i1n=!|=#ezhWuCZ$J!0#(Hl?+Z~Ne0ve%Ps_XO{iyL_14VtN`(sJoW;$s>2 zOI)20G{+efS6Z?2ydUx>!y9P&*Qp{?{fuE5odMHAXaJUzqq&u+mo74YBTn zLRSwD!B}7}0&YQOBNYKGAQR{QqNsOuq~Z&mVT%C}tBnHRttqI6U`3B0<`I`P?85Za zT1w$k(HB2*krQCT8`7r+soLeRwa1?iqxOm?6Ck z3IPn0BI@?%t2fh2Bq-EZqslDPO1;wI7b<%Pd6@48O z(TZbT(v#&WitaLFKU!=-(^eMX?v~{b9ze>H;@Au;05r*{EZKF7e0-?%kK)!Ui}ktV zL{MycY5DCpv$RpVS96E%>4ns;f`MJRZc`waz_h-~QjLITF0g51Je&EFH9q>h47nB?0HGSNnU;mwWDEQ^*MRo ztkBdEryT}(HwfmK9*;eleS7j>s1LJ&(ty9Rud9hj>+r0H4D*#%umQv$T~kNc#w~v! z0fj}lD2d213+3k_#5E7CUjy~ON`^FK^*2kIRtKhM_w^{utsJaaFTYZf$a_*zi6@_$ z`Z4lH(*7Y|XRS22RpVyr6wnNDF((fL*&NUf_FplSj0Ha|EX)*e;vH~U{>DsGXWc=( zSk#cZ2%xYc^)srf443m}X>$1g3AJLM3n#>booz^^;vK= z;HkqR*2Pn76Sk30o>^t(ZxT3Cn+eC^C_%iQ#p&`_TiCCwEX4(~LE+3SBH#Z}02}X8 zXlYDcsH$y?1Kp&$1JM?aonuvwTU-45ur1cSUL|6(tTh z<{>FuEce!U>X+B`4Z>h}W_AxuH^qG-DV+>|tPiziG!slJHuVuUM+jSmy*jQxdG9aE z+~?Ipwp;vV@7|?>t`GNJZ+7n6H{OIbtHeybX>05;>0UZ1TLv=d`&qM6UDh660+LT@~pvV)Ls~A@Mvqy`<9`YlSD#)X6f zHTFjT0dryG61C(-hC1_ol0bt6$A?5`?WsHIt`6w<=+~>wzG&NJ=nRT-gCo7=k|K8&E%Ux0#Exx=CUg4yR0M2cAM1DSD zhUW50yIC#2FSxv@tafY>-GOnLo#{`COioXt@8gLXm*4UhKgZBL{k&s!aXA*uvauT! z4;tw;-rLE%aygKB3a|t10tVfsvTF}899^F@k+*AXLIp*PHirs4b8M=eOri{rxNOmS zXM`D{t$!4@*$GBbwCP96jFbiyTU_h(tkmrHo*ik=kQ%@=-E%Aj{Y&=QjTuHYqPlQL z3EWyMH_kbn`*hhK2oFRo>me|1vw6#+6PoC9(fP9y>-S4;Arnpn216q|t<3%J`UYk2 zw;!beC*n2Cte^G1JJA^Vh*Ztz9KMX>M4gzd1KxeD@3#v)9>I|hOi44*?+~QtGZ^f> zzzcTm)AHYtf;Y(ny}W=_nXd4q()a#*4xuvQ>&xmv8_?uVfe)Ql^39e;{W^n~c}wOJ^p=7|4Cg6)ULz)yMEJna7{2)5S%=hmiT*CCE8 z72kgpDWL&-x<}6f;F}vq&qVxUR}>PcE`R%$0>q!^2#h^q_kv` zRNt3G*$SJ99GZ^RI`47R!-NmjdQ}W`7h;yN@tO6r7xvDx;>%Ev26s3TWYu-q1sD&P zOm|^ZT{25ejvzGD3H*+g>s4iA3z}ot@fb^82lQQshvY+*5eHejxT`8*={loZ@HQH6 z+E_X9xhIl8T|sXSFCE0i#@%IKkP=hKQzqJGnCT!)&H=BqDR&Y8Y-~&}Ky8#BUJu<1!Rv zC@C*PPr4y+=LE@A&9OQ5TEWLhMJz{kHB;Y^)9N3E#bsXKNkE8jhx@zojp<<`$T^3m zUlGi6@_0SX>V+kb*f?e0ZA51PO2n^17&&3amaUMxc%{U*9=kEcY`5tJInUFa!WyWc z|H4Su$-$V^q3Lt->;q@7bY(G|Blu&xCcDskah-1YyG3VF#XC7v!GWJkY>eW+_F(RG zshE9d9c#IMbn@W3z`{JG0AiRqp)_=KF;qU_w7fje-DR<7f;^f#2Dp5dj5F7&7D(R2$VCy|Cxrm?IsF%ljBT5t419 z_%49x@TMaqy}=M0;dg&E4k4;_OdL}Ug(Dr5b9wURsi}KBFfjVjlV|+uH4{ibw<&Aajq;4F9KU&~OXpZG zd=~K0#b_|kHtEo`k;6AQI2h_lZar^4|L;g+1Htc%K^Nb&b3e#jtdMEMop`Nq`m#OQ zG#C<7YQ3IEkaA3`$bTKQ7sfF5|0vSs8w2&>I`C{viqOJ&9R_}_!3x|h{@K(V+cbx3 z+B&)&9vbli zA!zjKcm09i4p^0t7NmgiY{?QT2y{)X^UwYh?r+jEXlA)$WSL_`eYidJHfZzw7=N6G zY^`6d73SFqRtT!K5fxVfsOSdBFYG4nD1#cp)Vl_f5-^p6-b2xP-`$tDCry-NVS3Uy z%7OWcA%2w^-d~7yMR)$2L_*D$XIB7lQf^Hzhd5DJD?26s7$f{F!MU+fzUD-vvna0> zqogBpm=cabn!TAFE|yEtC43*0Ke85d`%$Fu?Q4r5wm(QdgT2k7E1I`#^cL&!+Dg}2 zs$cw>3F0M!-@&TzT`)8!$?5?QghL*s>wH@Q#-Sk?=Vr|Nv`;yhX3JJ8{bT3z8gGXM z!~7>!f;MG^$g)&>NDY+USEIwnU^)V97x1Dl(P+`6_Vr_LM>Ir^d^smCC{`CGSCz35 z4uSsIGv&IG+&dnHPoQes49;_Iduu$xm`V@fWTeV~*F^}HG6JACbzNTAg;IYEmOsmH=AeH6V}3>l;~J=s&d0jO$9rgjj4XXj$A8pl>EF$x2u8Sb zzi%?+=)O6c12!$-c9Zk)hPmp#s93){EkdjBy1BDv=}cV!Dt%us?4?(7TFartsI|;a z1MOR}kpGMfzGCE`7I|}Ki0=JF)g^e@lx#6A)OH34=e@J}LO!ERqRX+~ftfMP0twWG z-5P3%0@V61EkhKUZ&QCB>En#obu$lBH7q^8I(&N+8oOqzPVjC|!3U3lLzvsoCnJlpLwS}DS&}m3z=ro`o4FTJWB>6i2 zQD`64KQ>TwewH!7+<>O}4@@!9{bt8)3(#t!NZMYFx|uaJIP>}A^Wyx&v>=!ZaeifE z)~wPh9OCG>zaa9>0Eslg1R2f^7X>CJ#v1)cc5mMSlSSB{c|sunUHl(*Waj>5 z(YDh4-S}%GDl;Dob%P0qxc`5b@jpsRf&$I~uJJbSX%gj!|NhgCm3lxWDV2|-HQv#E zVa&pseJ9_SJH0JF?vsg@m} zHvP!hpPOphZhDqJe4wOt4j0yCjey^T2qzf^1E%5{g`dhtwgHXdLbVU>&#uF=O|838OK(2kyO-FmNF+g4YPiMz z+4#&{L0rlfgQHYyVXQ|7eOO`0pC(?%Y&5F|w$rtfvA-mpYyA$QF?if+#zdm}F>i#^ zayV2i_I~Q8-mYaBMc>2GG7C z@~`fam_QE8SoCN_ zlc)`;W-Z&=Ls8`8ak${6b>Tk$e7Zucsz_%_!l6Ae;CEst1LUMaR0W1dh~kG^jt9JS z59~gYJbeLw=`*IDxE1*9$fjvPJ#;R!g2HR78svn*l_a`WEJ|2uVDd{25Eap>P9Rwq z8NsrGatsZ0`QDiEZ3=oW>>e-7gprm7V;@*95W*E!wZK&EMwS`l6F!T?K%j7AFk{(8 zbjszxrIgJt$l*o~-b$>Za77Vw?|O02lpPgi-z*^~N%ju<4lAe1@Z?Jl9FBcWy!KUd zfk9f!7Yyx4CwixZ`+bBB3DszN*E2|S3{W@7H;-l|o#0^PqcL?#1_NN30ha(F&jy<8 z=ArDwns2?X_c_zL-hOjkQ|q{d8?M9ny{Jvug7tG3jBOAyD24ONl^D+yqwjtA<&XHb z#{`Ri6!i9-J>D!r;Y!tBkTi?{&qLLq@=lD1~q+gb44T8I*v>G)XKu56*MC0{Ydw}o0^yZ+P_H-vyG z#}{l(jR%-DpV?7Y$c+obf)Sy-* zHC0gr>OGW@DgH;HfBALLAE@aUTrGjmmGWB!d`MfZvIl{$PcW9DPGu6xXkO^gP_>2kI~s(s6aqkNVb$uPHQMv!UY16%+lIY2 zUi1S`he^;@Ms>V1uV>?im;5=tU~C@tj_f77TPs_J&nyV;4Q_5;{eZC5y#5>#aoti8 zXg0-spWuxu9x}klQ&t7szP>B6ID6*56H&htjl~WIkU*~y>R17komYBRHWnRH3p9JQ zJ{w%hqfwVV*PMVm*Tbhsj`Q*9;!I3r3dpF9X*hm;XwwV-VLSKe!OE2|=**8v2Hbx8 z>hP*IN-qVdV4I$Wj*&+9R(3PxFdmDnsepB2tL1d|0L5jA7mrG}70)}q557r=UBuX` z>P^|6|Aa*!l5yvG=<_x(hwVy;0#JGlQBF6Lnd{|X>+hII<9+TQ+Y>3DGud;>&C_TQ zG(;~jflb(g=+>1Mzzs-8#l^)7)j0o(9HIQ0NJ|*&Xzs_OC-|iU0=pjR)yZaKirk&@ zu{9J^#zP_O^W@++J4^cpJCZt&OWOU}uaIWoNk;>{RVC`qrlLWP`R~4B8aQAw7n%S(c=w_6&A$`^TVS_;;k*Hf-t_SKGdaQxE7|MtF6V4KsM z9Wid~H=Qp!xVZ{!1@@3O83n5VI`Ygm@;9M^Pj*h;Nl6{ps3asJ8S2>IVbRytnEd#b znbGFXH3G|)T{8VUB`(;Xen{ABjHtgZud^M|dVk;nvm%~(!}O~)Qyutw*uooh4?b7m zD!lWAF;W?zRV$yp0yK#gV~DO$+&n> z@_=k(!O=J4BlFDPu@8H9qq~f)S3eSMEiz?(oE!!Pr*>G`tgL;m2J};PTHk$Imtmyq z_M6aGP(-ILlQf5S104w{I8Ww2TA5=L-Pd67JlIovNg^qm2#EDbuAHgcR^cM>S(sWG zMFZTo!PoxXpI$jc^O0|vhuLq-25^{SZjJuZswN;kKRz%H_?o{yyHXgdl-MRUZIy!m z7Wd+OtZd7EpIprJkK(>DzTx?XW%;)Nq_*Lr!@JIg0y@_5Q$fFgj-v4qbynT$auXlX zxcS%Q$-EtwrEZ9I^L>Swa0f8Q0>kr2LvG9rqK|QN%(yiM;62kY)iDT%q0*6ANde zjMj%>_97EOLQ?v=XT9Wyt6uDCjD}p5QC)G_s&!AI#?RVrgY_t`3?glm**0z3kLi$S zMx%&a{=Q=M$E-dQrjsk5gPp+~E+>?#HVEpK5t=OR)Q@o}sDt@5fTzFx^?^JxmRB>6 zj$JFIkbwJCv-9xX&GadQ%S{7ZybN(O?H`5UW}@3NJo7-csad)-1nf{ioX#l5T^Bnv z{+Uyr^|p7v-W{8J6T}qZA8SdM_k_1+Ph4LqA@~PQ z{F{8NOv7_8Qhn|1<^W%Tc&m!unkqW?=$j~D)0nrs^saFrjY#kquN!E{_es*T%CUFysC7YjvZnX>(O*QR!hE;j%JnZwOqYxhfY;3Plp{qF0ZAoUqgjw zlIb6YED;QxTPRX*mh7Tx&CyP3fP@I`RArLIf-SW$isM}YTd8=mvZw5Q>o=yg(cHNG z#u5N?x;C@H(fE3!DN-_W{&}gZwc{NrGEzQxD;NVV%w8Gsuv*dY+)JK8Q>lh4;Ck|U z;}EqW;j@Ycv%aN`GBpzV_x<|eD;Hlv*xc{6V;VPYe$RfUXEvfTCrDJ?T(DV{Ok8{a zh}`)iRfupNLHjN`RQ~*x%Lbo}`?C}9V^&lEC>Z*Gt|{|bCSt$x`Y?1LwmB5Z+`4&l z{R*morFV2IBDs8DrQO-%pj;(QWIHLf;2*^!+v%Y2^8k>x40A~C?I68iy})yiv{`i5=o>8h<3Hj8&BthQ2OR}#gkY_J>yZbe zT`CUSnrHd=akAwB3y0Sf4~_61ZC$TMbdFXgCqyJ-XyR8gO{zyiPxC6Wm*-A{{E0~C z23aXNSJ;(ByTv%c_{eN-y!Ri)|3qZTFY;mR`d_f#qi&u00M%q68(dLxRn(lN{oSf7 zgA8Z2Is92TxaG?Mp;!enNRTOF0U@ljua{B81ztjSZy&@5?rMe})xLTdQj)1M$vK9P zZV=5lqh~+|czi*DL3^zv5)V(Hy?$&hG9p061_<-yZb*u29`KzEZGxC-?!c^aZCMMkNvO2Tu&%%~ z;XJ2#XsmwqAH{qBz<_c#Ldxvn;aGX>VQED}?vbk(*$8%T3oUErjwd0~imU4ICl7R$ zR+whx2E|_^~9ivwrXt+_Nw<;s?SgTKP zGg)wZ-IncOYoWg>vrabE5gfx)2|}~#yIs(J$Nq^udMJh)uZg8loG`mmECe>no3)T- z%0t8wUER(vqn(lcHC62+N3ydAZ(ud%t1s&|J9kx_ zeO)(-sDY{4ff(IGTxf}zuZLBQdG=Im8gjWZAD!JGbZf0YkSAvG3w_$)U-Fc6GOK`K z9~6Ydl^6AA^(9Fva;(iw#Srhcer(MA961Y&AWG{!)lxJaOo{KZ){a{UX0BM2nSn?8 z=vVApKqogd7Y@gdKw*ciM=FOJed$@PGXPfe{Y;Mp35kgmQnw92i;}M$-7o+^L9Y4O zVZc3rXF_qU%Gc8;dKx^0gsKU9+1@}OAW22{-eQxi{eNUxY~K#e6Q7_;e|&I+oUgjh zT8;L6o61XJPkB`0+7cj|GoWkPyE-$=)$Bj);X1s$`<~;s3}`E@`)QU=+GNeqp0HKP zbdE;kzfCSVphv=;zWI;Gh|txss#2Y!_W80 zIGT!O%T(32A!Cjru#D8I9kBNzn=3bu>uiddYcu{hR0x1~HQo(RYq()mdo|ARfORQ2 z+t*r;w{k;Nb?bxpw+jZXFB&pt?A`uFnr}*qH9>6?2qrhNEc>R=4bv%nnWkU zZ(aAM#t#7`@Yl{jAz-N1!FK=X{9xK;oWmc^a9iSpdyDgHyXDe&(ANBvC;47!kD4DLrt1Y#MaRL+&6vpKw$(lBqkzhy8MtWk()cwNc9-GtEPpe zt4vKaD50^JR4tkezMg4Z@fN1@@H8)muFyb0Btwt-~$l@>n#nRXPp~u=cpdid$hRk%iI);T6xBj$V3HF(8$RF z9;jqDjfkl7c^hjPP3est(x=Mm-|q7$Jyhyw!orF7YBp?!5~cLL?@pENCT8SqCZ|8j zc{e+1JCkbh>ha_kh3;soN-uObq%ldchZqdc4nCS_5=))@dHRn+B=Rbv^E;CrGPwm! z#SGtepY(BGiT2r~-m*wwV+jv13UodZ{N+7=x|C2{!T!RnkqLCAne==}Sq4F#fkMri ze`X9nz=C-d^5s~jI8}|IAxkN9lhUjK%*u+jdSGScSCmZF0JIOlPI16tY?I4;mpcG3W0`WBZwCWo zGv0U71xDiDfhaqYN?gblBM-knd`9ug*xgG;no|+bQejn5r4OAgQZ@dY&dYx#c-!KS z$mWfH&3K&kN@Xo|+@^kHH0`kQB$i3-?mFiI5gi#2$aOncGBWJ*wxuJrkC>(K-3BLj zu!}>R-uyRVYDOtQ<|c2C?ldd6SXeg>#zyf&q!+UU21caZ-0Mo zsg7v^A5*Wul)=*P7h!`q$>GEId0(6!yp29)cvCT&*Elvlc;PMmq2i13rXMv$Q;MH} zmvebrpDVL)o$si&LbmPGdgz`D>`72ve_P+BVzk?x5eRu!Aq7>&Z1tcPN4M)q*Nk{qOushZ>x4JfrXehu-^a@n?Of6 zvUJX2rtrMHr1kw}sR+hi>F{~Zxd;M2>%Hjrd!T+G8`t4?K=cw#*m^u!%|MeCXuro^ zP;5eMV3QgV<}||ZPL&K?b>YJ^s$^syhwZ;?#bcvW8HEn$+O$S~<|I@4UmGz~M|9n5 zo^uJ-4}7^jC0i<7{};OtrRMu5FlA zI0x0bT-PO0LhD6#pFkWlLmmtTtt^M_uaAq%HhX;V9?YN+sByF}>78?xVcZk>;Pfj= zP&=o=`6`5{B)g;i7$ZwZPYe z7f*JDv=k}FHl5Rn%2sX^kLlV%p|inMFzX}7tn-|64WbgZTl%rG==2j0{@VKG$Hcn2 z`s%iB22XE{j!r9UhG@=efZ;WV4jSTyX;d+os~)n%-g<#$+51>8E~CKdz2Dq3JHwSU zQ$IJpX0@CVh$JspVIg_tVR=a>qSf8^Y`&MyjfSubZs&)(E_*nuFGM_6k}+FQ?~MV> zr!-CUJs{6OUXp@jHb9`?o^^-r5b@dDox8P3F68IG;C>?8iq4O>c!#8tedim@h6fbQ zI_fUU_Aj{N25Ce3@zYzF--GY&KIx(fbk^*NaQP{6jN(sCn{~@I*pzkG)N%`?yw1sC zpXSKF0ucc{L5YE#%=Pb{_U@HG((9oMPSuk>sLYkoxRl>8=|14#c5h=E)14@lnx1m* zz=GL8n0R<_DYSs=y!UTubNoLFbL6I9Dkq)Ja_oCTaAwZnsJXT0$L=*9B#LbLVkh$9 zUG59*FdG1-O~EL2!fB}}&^R&QCI>a{@mHv8)edv(knk0i*II^VQOnS3jlqhhVh#=4#JhOCRC9TmB)cnJK#8*&_N5 z9ZFTU4JJjKW1?kjP{kgA0a-t!d13~hS_-Xe?N(U2cO}@=jFMfBYA)xflcq1gwST!5 zY@bh2IXn^fa510kg`{zdkFoZ;z8QUdy(@pezRC6(Y}MG9r>;dem|kO7u`^RAvMsw; zU<>0>Gz#BQhKe^>qYfv|aaY>G3X2dVp-BOEDVEW0`5~~3EAoKN83HuK2{P}zK7P=g z-lL{WC%#FVOHy(zn_A36Ka}Ylj9Hnyc%N$%En`BOQx9Bdd-A!V4m$1~`eA?OB?%jK z=+T9*wzD)gi2@OcHP~E-x13(PEdC5(ir|p3q|{3Fd!gxxEx|UrJVmxxZLLd#ojMw? z*%dKatD@}gO`!wRloQi^(}Fse_Ao?W2&j1cuXTr)hh7v)BZ(f>O}?%j{r){uCKMv8D6F(M_o3sYH~9* zh839fuI^3r3zwH{c{U7o9$s)7?YlKp_qw}J?kxu$3s{*N#dyio>-5VBSRaU?y#T2U zv-35!B7F-ileOi=vlTo1Z)-)$uRo*@3`;&(Ect1=x6{ZZawy%MG&_0z=nxJm_MKlN zu%CamO;wL+J{s8((8&A!{&qT9K$7+&E4^C;zd*j|fJb0W$-&s!-#1wN79((1y zuip7imMbYAC3|TiMdbUh;ub9Yo(4J2{iB#Yy$m_ZWqKQ$GIKTl*5fyxIyM?Ng$lS% z&K2VdD*#qA%ZJz0^*d3s<%|2?NH#??!PYi$FU`T1L>@Yq zQb{F~Q!gWw(P!`5k|NTp-1k7%r^pdvxyTq?M9%oj<<9l^rD>FU3%8T~9R z5mXMs;egODz3Z1mn_>_z^+&lpf6ZZK?5)q9C*FyB2+SwhoM4zOWhDepR^pJBqJ=6i zkpmE$z$+b`V^=YF;Dvr`CNNFv=FG8xz(|$$kFxf(@%y2o;nigecZQ3lN)1?8M4sG~ z4^%1D&r|x*@SC>vX993ld(L&!wB=5rX+cECc;mOD2_KTQH&`}tZm(^A=^sT^*^2WU zbQpf*k-}B~N0%vPf;4aBc%ojUc)fM?rv;P+1Kx>(9R2`hnpw7I)Wrj|7URQcVoZshpSUACyx6GjO z^W$}wx`UoULhfdJKc*I>A31f)!p#@?Q9b*fTF9>dkVA`+1YyKqr$9vNkuRlnplLx3 zn${o6Ak(FW&XVd$7fc6cogp8;wxv{)wdd_;sH9ukZa9kmUcFc@Fk8BeKFS@etqGILcl)Cs3p{|SOWeArM zVf_h=+UFQqx8J1m-uROL5)kToI1z;e7?8CB6Kp?zQ~5d+OJFkJAf8n!JINLN`LdXg zOxpk@c1AnJdNoG|+4s(HmhCX$YP=JdrTx0_>6hWp?4Jh>)L?5H!Y1#`NN{;uE)(uL z`}$hh3ft-(ptXDZgq2s4%CNOI%kZ_L$98f`k|8IfLW^&UCJDB5IAB_Jzxu7HV-9oJBX)r$F9x|VCgZRcOJR>Wy`Y)^MU=E2Nu;K^;KB4 zT2s0Vj}7$NE~4~gNVHXfAu5^E(ziI^NTd5YJD^>-QZyu^NoOdn2~`HaR}0BJQcdqk zjIYI?4)iO0=(f0FV+R?jaT)P{0XYE=xtqW)8EqRlGWpUwySvPOZQPSOT5r)K=_f_C z6z06ax9Gn^m6t0dY*ALq%EWs;u^*7dD9kJ-Y;oNiXKI7c>bZzDoCwjO-Yi<1)wln_ zhVpa~Ie#P~w!sRcD1>TaXi7)YG2f9uBkWNfdPlHH720kD;k}&6{72y`LFt+iLMIE2 zrw`j=I6(bpILI=tgoGND+6W4m1!*H4ca(S;*I&B?8^JWrC1DgxL*_xKjj?fC&c%Q& zXKyC1dsrm_qIY6QS-^Iti@M=#CH7lN6BYfAl&@_6n1$H){)9;U+ocVg2}{_Oq@qqgtUDXXGprjp&TO3q2PJNYSLol3L!r_O*#5oKGE013(_jgVv ztuIsSJVE2!D~r9c$!m*jrOr-vp|y^)EtEC%Fes>a*!7IugC_4P~*~t-9s+d2F{Iz4PO1? z?uX|Si`zMN|0rOPI{Uj(M>8EKG6^k$pL{!TVaI-dcDd~VNKKFm;{bYKWiW&_8CH~k zY&3b*tz{mW=u-@O$2E|Xa`k9Q>1_*eH=N6{T2Uskb4k=)!~uJj_!c}pHUH(rKbG=u zeY(xZeqi;Knr(coMWnNC!8&v-R(PMjL@7LSg*Nq(dvp;T&eeJOJteocL18+Bo%{7@ z+>^Weg3^k(RIow)O5xE+(b$+a>%LHzAHmmzq!&AFZc`X6cW$wmt9hy4qsTCnYK0hp zo;ll}u{rky{;c%oyh<^8rH82OUegy9JssE9IyC;3Y?B+%)0-MjEzk(vbS>tS;YqIU z{9COS6qz4&jCssBK{i)L|PNW1Bl^(}eU53CMX&Ct<(5 z<4~$jkMEOTZ~DOZnX?Pfa*wr|rM&6Q-KNV|w^rpug$Y3qD3B?d>%&SOoZ*&!YJfK`I+ig&` zZJ=@U_=;K*hhE z)>M6esN9?t#1C&a9CQw^w{tMA`|I{UDM;!iShIlXJ2ZXu(@l_zyhCcSDS6af8c>_{ z2L#QvUNhtIZ2gR*OPdF3v}Co~@pcRMF=AX8riP>05r&Z6awU4qkJtUe{3_oQAP5!Y z!VdCYwe*QaL3aK_m!N7b(ct0-+oO&ZYflm#85)ZU>T_zSNtr&5wD4T5=<=OFC;tc3 zx7OXKf9vyy@{sux9Qs4i$8NthSQzKuMsUTmTm5w{1(?cla#*jifVvZUXC&AlaT$Sk zLJ2dNOSlrFh{KK@t9|tFPS&_ymLov#K`E4Tm8ccz+ zk;!3;4HebCh+MPWp6kL}`Q!&#Wx^}9c}W!t@)yrK6*7}i-Ui)ttjF_%LPpsdghtEA zio~m2*0QPJC(Ur~B zrdc2`^-X4duZ;CErna~r=f2ykzRYJKCiLIcch}@LP_<8~vxP-Hm(LhW5*qTf#U4{utkD3FdnT3+uT)*D|IMugAFhSibJH)pDFDy!q@`W(bTc)A>X z>KESE{d|F@10O9ZP492j!k7}-`AuO(ImZ76lGVpnc)+mqu*s!UJ?sw*+KBz?JFDhF z8`k$q67-7of_lXY!^h-Dv=iX_br$g4@7WUcm}B-+Fg)qRcnt%~3EXeiv>;eny}tM} zyEWAXN5wMP+errUgvjXqjqL0Q>djRj2TnjC`7=%6a zuO(O={6<7*e=FcVPkmr)$K8p*-{u0`{^f6UwK9Zz#Vb>pB_&IhbtXR;aJB9mj*nye z6M!7&x$>g+-5q<1-`4Hk)fu21J}IEKZK@xPEPIPiJl?!Mlsh7q9WsH|1K?A>FB0Zh zIpHWZuykKSW1rRjL}6m-o+9{r}`*Z^?eD`XD6n?A8CL(qF43X?&!(G||j7 z4W}4>k1QE7s`!NfJn#L%zE{I}qhcg&-9hLbHtw^fS)Nr;iAnZ5t1vi%^?E}i?)o)k z-+diM>mDh7T8(iK24+9yxLpJh`Ja&cm{wxNl$kOrAg!g$Py{RUYK?cAhY2Ek#GUT4 zZdRVxg&{J%`r$%tZra5UG&$LROg7643A*r;p^RQ5ofvN;7UF)4{oH*&L_GQsvn}YqNXH#Ae@)b#)rdEL= z13|3>*PUaRh8eT~e0o+w!~$O@#>hWQWh?ckfwdYN3stRK>g{EEI;%dvW~JGxZQ3}Y z!GUwI%u!>|6q7;*@=jGy2lu&38(=d=9G$?|Y7ONjtc3|%3HGfQTZ%?ENjw3`6tjy6n;UkBxa*NV&7VSxFt}T8QWM=xlOs-ji zU|W~YFS>^Q9(Aui8)yGS^G5M&qyN#Zd0%+dQnl+u=Evy6iIkzm?W%O$Ek@J;ZIJ0+ z-WCCTO(@nLX+x$Zf-_9hY70N|9n%r<3mzOulmi?4D+e;~=u6qN#PyMF7lxN*nlj7q)PLmwW>6x^0FyL{z(l7_= zLMhADjfYc|-?3&}bGC9gm(HI9hr$~x@4-DYmga4QUWnjgv$VKa?Q367+KGx&!JCI# z@YBG!3#MqmczvCe8s)egtfmg#nn3>|-eUj_J9e|MuC8ctP121NMF5>fyB1Fbek(=< z=LAu*me7F0Iav>yyL8 z;#rQ5>$b>(#M4eqZpsyK(d(B2P6}~}O~^lM1Vxg~W}e+oc!x8^zfl`FyFV_VDm6piK-WgPREB8K zlp&%<7 z9-II2=QIw*hx-<|Uw7xG+b(9>)U6d_w&kJUwIeX}3LH!G+T+~IH`@`U+2pgBLT2Mq zmsUf=i_#;4!uv%=nya|ra>Y;-jR7LMeL}j3Vzdj*o3lMP44b7oA|Nnvu<)0ot1q)4Tq4&NT1qr3;F3=z~b}DIJNAA(Vq66pxO@x6FK;f+_kT$X$1D zimiVn)G<`QlA7Wm`J9OFR!Q1(w@&%~M*-e?`&);$pavM$<@ZYXAt~=!ow-ir< zma3T!#|_QumTMjI*DeJ3=q)KYVhZ7I>n+J`tD3zmWJzI#BIi1G$V)S)oxlH2l;juVRkle^&3gHo1tdR}$>o>o4g}QxPbAYNMi&hRS zlqSrxx~y6A{*kB(!5?3R`zZV)IkLYygEl4dw~p5nwCR}{*_16n!VMNiK-$HA94{m5I59df-~zyA?rcJ% zr5PM?AZN6Y&U--VTIq4M?^(H@XnDX&91|2DJgK}Qq)PAPIDh@cN8sX?2(!AX73`7_ z79bDZ=u>Lmo2$?>1%BnXp-CW+jD?OWuNtXddHM)9`}V7>kWNf`S6j$7)=u=|fH#Tj zLag`;jcYThwZf^-^Tukp?j-jHeTmj1k~fGNeT;_+I)U4GsVB_ru2v zQdiQho00!WjK}U=*x5v>#r1Xj3Hzx6u1y@rdk*%Qk>@Rq86HV0sEZj?)xMN1S3b3X-zYT*!V~5=HRE z+p_&W^K>QYZJG}#0Y1}OuEjD9(PPUEX{k=0Ql{voW6E-1i);4ytL}Wete=TPZ2w5Y zRu{shD;*4RoTo^*@EL#gJtqJR=ZWC#|GR-2z}YcC$&=kLuPTNK6W#uFt@M{Odym6iI(L`ys}L`Q2i;CG3v z$_y)ch&fc7y{SMs{Hid>@$g7v$!l{tK%wehoI-_@@?=V4?0WA)%}tj&t!YV7S%Z(@ zZ0d4M(y-0Xo{J%gYS@)&$*|7nj;#0ADLpn#>R_#Hf7cpALbDu9@CtYF02XO<@G8vr zfKP1*R;74&hG}q!K+xGV!Ih+%GR*W%dHc2RE3Aj$Weh^%ql1@;b8hEGsysBV@eJ>$ zv&Vh9o1~wXRmVszow$D3myk%4AEogYkt21;SxrkIAxqk|j2d&@?^E08(E-&vbxNLG z96h?7(2aE$WWI>|ZMjR7O{P&gXTwMf_kGzdr#5{NOH}>tmYB---+o_~fuXFAS{zF| zIgq%#l3r`jD&cc972PFh7zp#w5jR8whQOuOWc^eFyToPGkAEa9^Vs+oiEC);LP=Vo zM4=>&6+h(UwoM)Q#%L7_-ONG^hUCmnt;9)?(UG_D0ki3H_9^EptfD=pWjTM8AQ=rMu#+GcWi%M)?fBq z!Z-Tk{g;R=CzZcNEdouu96Q%>i&8((gx1!0l4;Ruw&Jdok=G*G>zG*zbT2VVRm){= z{GxgVoOYhYg^5AZ)*3SK~(To9tDGy5jD0CNX0`gc-#|G9e??TTWq(%3=sAyd>&=~T4^v2F$s zmxkOGWU>%=k-WH|sP)>1cmY75&Hpy;_)%xZL5&U=PYk$DbCfRQ{Pd`+`?}Ym12TW| z8Z+)pAU^RKus+r*NeW!UHHY-DWCd`LHNUDMK89i=26v@^fVWArSp45ONTwFzdH-r> z6UVftKgv!ako7K06*^&@mBcHp#3p#=LN0rJ`#DGKkh!(3#`{A%A;71kg+Ac4*-bxw zKlgL~cRTJIbQkMZSCVU!S;6VeLO5YwwWsZ&V%2D_s$%7h>80cePv(h8hui|-V1TKT zd9Y+7*;8Yu@z69)Ox98Ct>`priM>?ok%6zr;Vt{ukIhOComZydoI5-Dt>TX+apG}( zL*m<+?qlXW+g8Rpo~S2rjY-| zln%qw(uA-R^Ix@lynO#i?o?wra|Scc+;;Pit`2e&R+LVZs0uPG_f9ju)S~0tt|q__ z_JF216eB9Z(=v@Uq!&U)sahW(2(4rHUS>`q7Sh`yJ@dl>cJpv)iLyn61=uWqujcSf zvi#VfJtyYI*Be!ST{OOpNTrUGmW)fuQSBQ!+XKNYWmIqA16; z0;*NhapqtljAgcwk#4*qme~wLkdg1QH`!G7)`prej9Duz0-OW?oK&6goVd>3X1>;* zU>(o&Svk5EO`jv6H<*XN)z|gfe`@j(+CxVWiI2Ypy-9U_aAejf8ONPtRRZsIUeRrx z8@0D^+3Rv_5MS3` z;g4Z&ES4DYx}&ySb%}rTnbH`kd?2#Mrwv>RF<_hVLBU9lc*F(eCC=GWi4p06<7}J< z=8s^1H`dv*W0@v5)o1wQG!cgQjlq|Bh_?h;=T6hAl_q|C^7cOx2jM;K3kF1= zuFLe6bqun#CODf)T1I<}QX~w@w5Mc-Cf`_+>Mo0&KS$_V-am=ApM6hVbd$I!S?$8p z1S88gI}pbAVqE829k#CmCsEt#0+MBug#&a%?xfeYg4K`}J5?*qVv=!N(ivENX1*4f z`hiSL_x!R*)wd$Bey=I&h^(fxK77yY)xiW%&AL2mc2yB3U73P4I+`x?(8!6wGS~-1 zaY%7cC-u+b=jLsPP8-J8@8eYE0lx3wf2y?bu@HS#^?427vp-QtYF^zj599mqwZ8am z!~{xrf|D1%pH?g6AiIdG0$K7x!v~R4%ICmR*xW6l6sMzn zG2}!G<~~X?$-?<)V<#?k$v7Hnl8QLAq^4SZ6?jr=Z{DBs@GmX^F8zw;K@% zA0*9=oS!i&S88_baleTP!#^XGi&>hbybOBHJaWnkdodDfGlj_)C=BoF^L8sta2hy5 ze{e-gp6+?u*yT0a)9OW?Q0#`-#kh_ezAeQD%FC1Tj4XLEg_p$EviECpw2JN^I?O=J zoB_6jQhgoId-erZRtzh3dx0|d>|W+8wCj25?|;Aj^z3Ho_YWUttqhb3<)H)C^In<| zOJL>x7#H7mZ)`TAuhu;!Pk~wQvpLLOO*bNS9XTwixHcC(QZ^@$Xir@2`V;Bf-WqAy z;}AbqTDQ(hc)Fg?>vtLfV$xQyd}Hz&Cn{HThwp&eXy&bz0GZhNftaOQR}irL$4H}# z-nv0tbiW~`b1fXm%r(5n@_=V3Cf{q!V5D4N!U50P|Ck|u&ZyIS?Y>7`8;8wrwWfp_ zq?X`9buznSE)~Ya@{`h&Z%)p4^0uKBiu5%a+d#g3uLw&kNh176K- zweP=wNqF;?lN=Kw0AGY9D28=U((WZ+l0KgR+ z>7NdRdNPy4<_oR&XFts=?j!eFRHC3>lmcW(^A%89b37kY*FK?x`q^!1(7R2mRELsj4<6kWVSezNRCW{K2 zc4MgJ;?40pV!4iDjc)|~P#|(QO;-?CK2r;8a%g(WZA$~ciKM?0Q{mEzQHmtoF|y7BoY&1!TKOm``7T+d5& z+ojPDLUMQbkB+#3c6Oez(7ydsJ#S})eo*v}>=F34Xj#7h7twa`jaFz;s20hukw-88 zht-A}y?S!*&YK>s&|m+jIC(iaH!pwCh<=Os)zJH?u{lPg^B}2TfVW$+N*6? z-hcHi=B42`MLH&X?Fl+?#=MmZjUy@gxav#(>}~c`0o$FvnUDYXdEsyVw*ysGUowj& z;_;7(-mFk9acYk4hc98z#1bb9ktc_0z3i*CErdK{gk^clK$+E4x}&mO<#o%XviXUV zGQBaU$C+_8(H$r>jT;3>`q>w2 z-O>waFF6HWs^+_ke9{EV zK3w?Pte_L4p})46Y%j3mSpSB8!mwqLv<2tzu-CY9_RrjU>ZZ?Tg|+)4AZ;=$-u{gY zURD?|GqG)+^R+r~tLClqS#;z;+n&q)O1gbOopkjmO||XoEtM7{jE--y#8^yzLbR8d zNNrb+m-W1+#Dv>&(-a}ax=TxTX^6cD)*>|~F|_04jGtGB1#%wKoH*!=x3F|~EsrhY zOl&_pl+A-g9t&U3zwVd2(hiCp09=Rgn|*)4mN(&JjQFIAC2#O9wUE-c@3F!AwJe`P z$}N18K3KDj7xR}?b^6g(J4mw>)fGdsG$nP`(zJWtXA(nQ>N11g z_<2j4MF(T6XBKqxy*n-?u~R&$=Ls#9@AGmEwN|6kdlqGCEi$7RA*E6CDn@f>RL|rm zas=diDm_r8JS#Z#1S0z6wrm``RUQ){7;O0b=i;<3ErS_I^BWCp7z<)pd(^ za$j&DYl77C53O2USF7f59$GE!sLpj6O(54PS99&5=}7YZUXX1^-OqEs^>=45n9!m9FQWO5a~Dsv!(V;J6GMEBn?t^2#er8&Lj}ioUckBFyO!m}Zd%h8SF(W`EOq_`z_7G`$M8*4n<{#ixOW_&DVN$dP1=?uOxX~mxFmE=?QDe=|nPO zIXubG7|N=fZDI!Xwdrgmsv=8}*28tP(|_H;mvK;CK(%%o^p%1NfT>ymmk=X8NvN8| zNK0v?zc8|}A(<`Y!p7db+VeO;xUIT>U+cO-Rhn`p?YWd-n{wsW8p{r}2yCS?5tS8lOB}@9XDL?;D{R{T!AzN0j4oh7jLxfG=sp8R>?trF2 zTm_?j#b*6QE{i-krD>4xSWTUcE{gl?eg9^r%~X?2R@Ye3ISINsyK$yB<>d~w7Q{pk z?8sPXBs_aM8k!+9$RfdC*&`Ti3|DS1ZPy7&vk=PO>J`F9?wF27he_P03pT!&In5 z!I!D8CJz$P_}^qtPph=^Ya@#%dN2uJJOLpfSag^QJ(Cy}}^#X_(5`FM<3uBp!qpOPh z%S>MV=^4XTbp>p|Pjm_?${0Yov3_0JZ^RK=s`_D0_H@;Wke|Zg`H>TZ7@{aUi=cI{ zp=YGe>PxFR_WQ~}3ZpUXiTe;P4mN@GfJ!RK6g~N9WM&Ez6LsglLZ$DgMmhO_{Uve4PSEI z3?D;v#oZTyi6Prglc79op73d>Q@vpiJ!FjBhr&+e0(NyUa$@7A(gzneS__nFCJF7u z^N}?BQ;tlAKS$EU70z8JH^x4xd%b*l$H=y>L&#g5k^4m;J%2yATRt?(SAoCWy211- zv(`Bt^s1gdlpU)h?^-V)oA$}IOfFD`QPcf|Bd`4Ez|g;=_!IR{wD9uKK$L`70i3O? zX&bq#s%feD)6VNKV!SkQ`E)HKo6aT(8e#WYw1)0dR~OR=x|5dnCR>NaICl3PkHG#|1GG(;I$P)AVc zASD(9*8h=cLqKfIMHoYP;r-`B9=~Q(Ij6rVaWf#8*8SFZcPP%QW5p}pmm zzm1ihduxQ


    d9z`07y8@DXz8XJXfP%Ybq~#FukN`n|>ZmG5(K4dLE@xZg4C#y9xpy9+=vyM|=rAAo+&5iyUm{2}}o?JXii4-opATCm#2?in{g^#U5qQ4&)Z zeDl-6a(_z#PVg}6kP1A5Z9dqXfp7^XQk@@eL$8uVWk3i=ziIC@K(n%^yDVnnYJa7db%do|-FvPSeP#e^xm9 zXWnvalteP22#!{Y7GPBo7-Z1iDGNm<|-(fmLX~>5MeYK^oUASvOM|#Gj&9b zbr<)Kc?$i}B3$>&uF%De)jwEUn}KZOe6slKnoD=Fr10{Fs`!rWja(4KyDPJOLi$Bu z@&@i!pi>$kb*QM&vud4yZ0dF*^uHgK0*k^vCSz3F`;czjH@5pLI?kErf$zQ0;7<;6 zo(EheCDA#_%z@C$Jk?{fp};@KWG;=_JC)^pK>d%<(_>3?xk>E=1yowj0Yhmr$P zjkL&;h)MR)5i8h*HL~fE0F;euVVY1}U_v!$jJVj6qguORmqzfBGj-O;-2H+qm>OjZYwjh&kW z{YwDmcp(QZJ)U`C#?}49W4=mkZqDL$H^qe5{ip`@O$)_mkJbP_qJ6Pdxhm-^JR_3`(uG8_``E1$8x zYOZaBVkfcIj|VtqihPQx(KE3y*0(V*kd8s7LtQ{FaN!R)6@Thw@2wLT+T_%vuR#(5 zY!T>oI1m;u=1+h|&jRN_W9@J`bTM&8jb5?sABpT-n#K@ar?q&KGiwM@&H z2tI1aU3xU+ZpTz^ouD_VV#BpnsU;RTmb0Sl{p&rCXc{U9`62BWL0$GP>cWrR;N;u-O7R0U|vVw_rrCC_{FNt&+B;6sn`pc zh|*7L-L=T?-I8KiSL#hi{FeD&{v<{oJNwFSBV`OY%@4H0eda^{(}8jyIJIfwk zab=)$mcKUL)d0e@jv5Dez+Q4KceKs}gik8frB23d+t<;&}!rN-%Yr@h>xG( z6?ibbPCvY}I6~#+&smtXt*s-s`hs|^wMfUQGyhU;dHN9p#5kkeW@VyEYN<2tEGN1- z&pbBcUAqXAb$R)bMX`s9uzQ)hC$D62Aip=N{F6>$viHP9n5h(FK7V0(<5;-f-jf12 zqw$;!pxbkN&)jb?5vFT#y}q;G(EiLnt` z;_U#r&N@=_w`>8J)2yPvUALM!w|h*J$+)xucNw063x2Mz=x#kBje#oI$Q^3NM;lxP-<%!lY0TYNXdRn3MrHM_mg%FDC%GEdaG(_-po%p)#8zdQ7P->6 zmv@wYjir(WtF!664!AhsMotFG>oPQr9L>tNxs^wFF_vf+!Me1jD^BQ|O{my%dnbSH zNT-T{gG`RDrc2w}^OwDkTKIvVcDq`*TXOw;R6Z;XyTap z>F?>5qK8Fg9B{shq%ReQ<$917(JRwUIK|_HBa*vwd4=WVu|)fORDZNx%1Vb^bxdb6 zddZFj0;$UH;2+I=D=bKS_5VyG|4)>l`WI_Y#_^m zEa>BoANcX>DuqETo$*9is8|p7B1oM7UFLV9|BxI^@31h)_FRqpIulAJj@r!NUxn$TeywC2aV7g zY|@CpZAWDL(gBLTbv6HgvC9&ot(=ogaTtt2WI^(<2upasw>P<^|AdDky7-` zZ{$O{D=DKT9V*2EBVXCvMd#=GAI5NzcKonve|cw=&Wd5*cVsHxHBjlTD^eAIXLkIc z7+(l_CKvK2Ss@)X&rU;Or|D9jFNugS1smFg=Zwa8h`bQlS*l1*Vjul)qQ6U|H8U9D zPje_w(VkbqaS7QKIdp87vXn zQt@0_+U0sGql@q4+2Yrk;{W6B7s!l7`<^p52ic#zDU&Z2uQ4tq|3eM;qr2W*bzcTBwWa4~;4~}P%I006wXt1kgBtn+Azt`+cKfd5<###0>$u+mRSL+VqZz;a zLndLZtrPZ~I7)1)2<6{q`(f;)GIDQ^VMOrb{#B#FRoLe;H^jn<; z?D+Yp*{#>VZ&@59Gp6&=!|o+Ak~XZH;$bm`@r3@IYldqFc^(Y;l|i z5cm!=N5r3IIwe-0iI5)|kr)3kUn@>ORFRw2e-IseFFPo0_8&wKnR9rbU2+s?ctdS>~Lfs*eAMWfiO6EY-xg_)$MB0+g0Fc z_c~W9d8M;|Eh%|)WkzjXDQN#`(m9a4O@7cLz{AZU@G|-iQxtJB5Xd8>nnRZR*Lt$l z$QOj^6`mqrP63{j)?rJWoml?v9laBu~jL=zaZ=Pj*FRNR9SC3SQ zhTHgoj;|GsaiTQyqs$QQoJX{U$F<4Z!M=+h5T)QUK>ms2I*aCtu591lE^Q^m(!FFB z)736mqniArq018T{SJlG>ba$@>&0-!UMtn_)EuVO=`#n!&UEVt)Ut1O-*kLW6B1{7 zlD5S-1!lZag3366H%gh-XW9b&TZgkxQXve%=1GRGD!QRLQ6_E6!j@eSQX_$@-6k+(R%I6XNac!WOr& z`!c#((TR5rR1ZZwCv`Cf+VTY2<1k9m`9mLfOR$4;*2=hnANC0$cHbxg41o~d3fwBQ zE`cb_D9Zrno`IH~&$uo!JZVa;bM-|0)zAG^Ljbz~n`HtV(4 zG;gh8>mORAP)h{Ft_6>T)8Uxv;J5E#&VOWYT29L8BfbMg7@m*+`CMv9gMX0t9~KFv z&#^^iI+hNzG2C)LS?AukkSF`OmVzmT^!sZa%H5u;?Bv}Ge)KiLHU^|$C*AN)v*u_& zv9bf;jQE~jtPCZ$)Y(2k{qUCFcqk>kiU0nE!AM!i*SOl311Ov`eIzNj(aZT`@JQm> zqw$4GT^{B6IQU%p@0)&PzF3e#9GUI2;m;%e`iXV)a?7`WDO>J#6l+)&zXv5jayDFS zUl#JhoSu#6Oe8N%oklYJ$jcj4(zI;bDE*c6`f89$i&?Ewkx~3OU7RsH6#(g_l#Nq2 ziHl9`u!4gJ<2s3)KU{w;52h}a!)2BkgV=}!k)%+_!W)ax(G=71P0UxKrCS6JMDt(= z@BqT6buBp|oWry^9DmWu+=p|V>Go?IA-PAH?}H!Ii`8SE7)4m8&bko_>**slO519&)alEmcQdypRp|dqFB!jR{-c@UR@%2({YinhqXmm_;?N9?Haz8H*XqDcM`HjG}}XMhinDT1QX; zr%grA&SG`$Kth^TUG(d_%Qtt0v`hxG&Rv+SrkQ;twi}rJHDU)EQ^lC~Gh8WiYd%Nd zyhT}v>BBz~EuQCH@z+t8kD&MqnPiF-swpH6lw7ftU+s|sG)8skP=)|%j#u0EOu;6n zsR!-WOwi55<|3eUW_Pzbn8Al-vWY*^I79L|tk9h@y~A(YdCx2R@t$LzQ>pjWrY7}H zsVc9p*qSi}{);y_Y@{?-7>CHF5a5}pF3VJ(F*J=U%-B*#Do zc?gpoq2TAg*KXP8F)m!cDUr9nP0U)go0j7eQ4G`0B5 zw-mE{4o1d7TBgh!>P2b9xne^4YwQLbN$581 zNfD%>|9K!WVPNRkmYm(k^Fn_^Lwwr1D;JKAYfB1ck7GE65CNM!0lgByh20dUu9PTHm@qfBiq`s74P z`6#-`N3B68DXWil0a9UgsAILkQVmkj)WE59IOVnK>KW&x+(>zel(D=yv5je&r>o2n zpQm90h5Z7%qb@Qd@6%~s)ZE(5{tY1=?_2>|7kstK^f^05bX4XhFU@(uc`FwScJ`ID z6G|6OjxTy!;u^_-ez2m8i99`c<99CTZ)H3>(59Ku8?Q?HkmY{pLe4?XV2f2O#+K z=JMF_WVysmre48cCte)FLN>@S7_TRA+w5t5slGJ)2B(tn`f$&875cz^W4$5g2<47? zJs^o6Ql2LFknI1U_od{es*hOjpn7AOu zNm*;I=$DlQpjNjv^X(xW!NmcL&^dF0_I)edu61wpAKaXUrbklLYCq$xrR%%UaidA7 zoB&n4VG7s^HnVjisfGBo0{2gy9?56aGihj=u0LF%&IK;_Td$(NT#T=AHn;ZbG0NRT zfsGUctW;dI(32J$c5Wl(f^aR#>ZM$ASp3z4F0vWDqurcSNqlj_9x$rceBTu*-7UmU zCG16Wu?|1KeY+Yr3Qii*W1?w4PHvizn#1SEeaurN&yVwxA~bgKO(QEZvrnWlJ!S@L zHk6EVa;0;h9wa;gMcr@x6F#ioK}j80zrQ`tII9n5 z#C#{A-U1!41slQ;ZPU6Ng%>#VIvkXUG-g=B(=mND`zLybfcN6t9%G6Agw(ySntA>t-y`R)zB zS&oIa(SIBYTk3Lny&k_BI8W!WcuQ78D1)2gq>?S@Qs44l+d0$NIhyRcsvb+1~OMiIC_pVo}_RHkiOn}W1LZH7ebl%csbD(lCwfLspj> ziqTEBuYuzX%1Dav80TKKQXf=8<9|yYmFQSozO#R!N_QeOzwnQwcScD%qOLc%l|1>L zOw<)m7tFL~-!+-Qx9(MrDv!wJ(i9qD`4U)$GggFv`KFKpII!0+N+je1kYVdhp0Kk& z>n97~r*cLbH~xJOf!LA_RKF8EGr7BM+5T6l9jig!xoP>bn&@~r3}$NW|Dhq?gBe{d zUaLfr`|Nqc%b1%U7YQNndglAl5myvGJc1pGE`xyAzq}Xa94HPDwxr+Mw>O>mVtoQ6QPbjvUMJuhG@;_ee;h3DP?p=$}7KY@*hR8R|t=BkvF9y zacOSmp0{eZQ_q#yH)@Y*N!!2dxug=``JS{R z?^jAtB**?ZCY}iys!a+#Xj2b>`-l}8GxAXsha{RifbUyO^uA?6#AgK&np>lA;?s+GWv8CH_Mzk!x1(WW42@_fp0 zOnm82I-bCQ(K%R!1?C$3BYD5THv1HmN*h8Xr}IE@zOA*?Qq_JyQoo}c)8qYfOEo7s zp{IO1m9TKQ1}}04D(&x*H2Q39s2*CC8Ez2{GG_vy+vHxl!fQf_ZDHZ_SXxG4O}@G? zP(hDVCfBp3wW7%fKlj7;RKz6a+b~CVYMRT2?hSse%R+CaYpUsFGO(Fa(GW^So!K*b zBB|~P?3Nkxd+*C#QVgZN8@H9e6u_cqM7s)RTz%oaGmi~cq>TEB?QcqLo<&3ug%LHr zY~Mg)GW@9#M3*rQb+E}qU5rXZh*UAV_EbyDq#w;}7X~V3J|s^;b5m8i%<+Yn^FMnt zapElOuM-EStsk|y4;^c0Z8OtMyo*>^IrVv@G0pugOoz{SPPr_cBWj`a#oO8s8_Ep@ zoMb+hPt;ZaKLAretiMv=q`aX;A77Hc5PKHNMWRJg@PBdR{(llaFYN7kLqlIacrc-; zrB?U#e=w7fqw@dQbDQ!N#WNqb6;8~>hWa%AP{$wXCoo!tTP=*3Ips*Uf}gjC(Ga|XCHn~?)1>3tIb;hxcM8NWwa5xd*q3s`g39!w6a zkJsOZ#yB(sk7&hNKRMYBIc5*RO?Lb?qrIAE$422ngH*nZ%m1x^UIO?5k2cEixk}g? zk5Qc?p_o}qP~wvg?32F{!fU_w%J>~xqNcjsDL^NPzbqQ zfKlX)h8Oc9&q_nEQMh0Y_&hG%htIc>5$bc!(r;&ArbcfxU1pl{!oGgnZPCaYJ^-{A zdLb<29I#bBeKC>Y%f>g-*s3z??wA-+l}~_D)>BJZYy*tAR=a5|RfAUcHI^9^`sFYb zDST9ko!z!InuP%LQ+ALu>D!7-SBT8<`}JzYV`TqACD!)PZ!t#c`%*9~{x$Zy0~;V@ z@TxpSI#JIl)0%3ApLOe{x*}t?bx(LoS=K$E{*l;u2?Ir(&+g zA3b|GlbE{8GK_>E&VqU#Xpnl2uCjNtevz=}t*xq=#~j>QVF3AFL3465S)cTr9HOek zdo^`#=?s_Q_O?q367p90_K{4_tFzxk2FVfJsIfsyM#fUFzrV>3x-6jxHsF$?=ok_` z7=$op@pdJeLQH~UJOC68+!L|iL0In@ZS`5gd}kK}dCRyTE`XUT#6g3&TdIuC?f<)W z!k;sKH#y5NU$iSzY|)D-V5e|&Yv0kbZn$g@LV%5|UD5MXzCClZFUkUetUrqJ!{s|> zMS7m{q|}y0&Yc8S=ygl$u;R`B)DFLi2vU8jjw@HZYl%B0i^8kGc zVy0KUFW#4}f0~c*O>$dEP!AB8X6adKE!i7UzM190`2XE!OzM}8=ZqNNwCoc7~Bl|kXF@O*pnY0m@wQKfv6n!BY?<7d8Sbp2a;J09Z;_{{0Er&hYV z+&unvUXTQr4u)WnlxYoy-yxU<+CKU5H9?#d0N>b>uI_qlu$lYoGJ(Dyhmqx}vbIG^ zc&I-5uoi?a;pkn8c~6UI+k0ErQPvqVuKu5s|!4AW?~pYP8B!R%_( zODnOG(C|qWM#*-Sd{{@DXN7u6rcyRh$o-XHdf1MOjU`i3w)wKPDz7*A*|z`uKN3M@ zixJ#=;r+<%1sTHHbBR!Omn)NNVW97}Kcz}PQLIoba$zA!RxkA*Nmw?8S<5ndwzN`S zU1Y5YW{^ZU6HP3s-TK>--Q|5aTPd$Ye3p>^3@3I`lotZ_Ft8XAc2trE4VRP8yG9E; zi|OoA1j7y#*S18_-zCdCFkg4_qTRA9O^T|ubHvo)Oa!~jT37I15eBFOyW)h47R!J*IB%QxBA zeaq46fNwr=P2@6W*Ig!jtfAw0C2YP%JO~)Kg7V63hgi(Eex1|d1ap!l8XPX_Vxbkq z3n7Q;k92?q!KWEE8)|`zTZ<0`FdJM*S)+#fz#RDxfg#->8r*E-j-&d|0SCSQptYe- z_JR{+W?Be7Vdc_IVdF*y?rxRO_?LQ zv?~d8dbw^P`ECd4qw&lQO;2z8F7nk4?BuEPdiaQi{w_+hka0u?61a}DTLH5~q z<=uC8zs!f3x#!H8IWzbE&iT*19K?)2UF?A&S&x9tyg8OdvSguB&1m{Bd_mgRRqk7R zCXLVIc<8`3iUYVi%nih7r9-AvTl0QX%Q~S{>yW5o)de!&(m#`-`3`)q>QxFBVlIxX{f(ZSHjDr7JVtCj;2mOE(eJNFXrU3 zzh>BGJDn)9NF6uSz!%5dzV1}&CDHmKGV9ug&<>Utm)2Lao-C= zpk5%^nrvKd?Bgo^M0gH&N|xdfU67n4+^?g~bS;hLXSzEWm$_$6k@57U>zv^!a(lnVN8OnHzx0G;t*r`SW!^*`YtG{Q?1Mrd}S$W>$T+`Hi1tsDOJX#kcS5nD?b8)^I(z< zcI-(av*74G)r#eJL{JwV-sw`6Z#KOO8Y1(zzc(!U4ju%?kR(D&h)<%e^_2nsod^t( zH{WRQUq(pWA3u&R`|AK-t1KpZ1K!g$&xpS-+%N5FsEUny6cc!x84uu->iZTPtG6*K?aB*ats~7t&Q#7x^)1$#q;>vojKF#UY zwg1~UA8eZP22%K##f@Rw8aZm;?mU&_M%<#ll2s1Mr*cxW{K#$T9&_Ki+>sL%-IMFw ze$efqW`ha^O8fPa)-INdmM)~nFJ$o6*kg9~9kP?K9yD^;vA^uO(>d7N-=O9J^JYiF z=e~1u$}X3r6akr_13lTXGm~jUZYN^1tcS}eLz_nc$G%aEHo?e1zfE6?V5lKHJ5c;e zPd=SBs3UprwUGfap-7wMZSr>rs*;EbtGlb-NKQJ#Akn_D>W4UTw%PVbhqbIWcrkB$ z;Fpu}-ZSZi;9#j#mPE^-*#5r-FWX;>$|i>^bbUOm4fe?VG4Y#(8n0wk%0VIIi=ola zZ9B>-l-4ua3smG!Z)zAz4vVi*&_a57Jj4pQf!hyC9viT5Kwsglynh6Q{5rfw>AZ2r zjtHQaPx$R27TT7Cf$w7PY|n0&8r4Ast6#U&Kt&=>DfIepc2Zr;+4=T~71PN#%+oAv zXw{R-8k|0_C9*F?fRe+eWL0JdeKRW}pz*;eko{sOB`psTuG^TyDu(RT_tY>r0%aKjct{gQFve%1;GztyS=`3NC zK0(b9y2#71kK-a_L6=ExRJ`AcU#Md?s9vCi8O#qx9sz01kRXzN{7fjpyY3Bi*a}ap zjzW-@sm+M%ub{>@z9Bnwz}Yv0tTAp!3|Q~G_94C-&iEB98vQU13cth?hWY6$YHK%n zNV2QsiJGpdNGyd{-_zU@Ur6nn#2$KWlkNq@o(hN_Zj(9`^r0yzs>2en1f$u2K@x|> zlDsU^qq4%}^x7x1ixC0AI!*kNQTwVbB+a~KM(!$=Bz29-Q}Y!ZQ}}Mq+3%h34aIjK zZj+VTpb1elgUx4wRjy4GON5wnC9Y)ynJpy9Kll8jr{vOV$;Z9ajGpwAZ7qGfN;<~_ zpmMpLy5_Eykxzb~%m!_~gRJ9bQOn9MiW#QU2r*YW^+=idmK%wuz zhoZl%n`#W|*!75#L2?+=X1@yAb+(d9m2Bx1$1_|-U|M{+zE0lGg<`d}liwS`4B4FSQY{iRHvbO`kV)s zajlz$MZsn5gJaf?(|HFA_rausK8c{a4PT9ApZM+j`yIu|dme-NE8>gGg0U1>)Y&Fq`w>8tCQ8i zbZfb8@QV2b$PDOhvQ<(dl(Ws2?NG{o1M;-x8;Snj7At(5(DdQDy`UPyf>V=j1vjay_u$DG2T;s;U26`M^e>X)Qi``-Suy}GDV>Wh{Jbo+k(E1+rX$3HUrfl9Cagc8<>T6~ob z3Gd`Oef`JzK*XMod-E$Ir5 z8yvSV@mHT=i}=HgrK3YOb=U>?hEQed`G@iw@@E}_)|S`d*x1x7{F|HW7u27AZM<7Y zX^q!daZvhS9tGn%9?XnMi_rz_LLMkvFMti8ZE^F|fL=^7L0bJF@7V_f%I?abl~@7Z znNK4*R5Ok2i^o8O)~N-RqDHZ$*t)i}t|499?ox6JK5OUVX7yTk^x^s~$VfD9Ji7fA zS*a_DhFY;vS%J$`el=LIL)_lnQ+hfzlps8KlyB6zBxhQa+0$IGQ?

    lXnk^E*i50qWmcF)SMbpNx1~JKUSdjJt@A2*~1||u1_{%4uOXJ#Y z`AsRIAkATp1bCJ>4Oq65)E1rqpeXvScZ<~V_{wu7se*_~V#;zBMJ1_>b`(ll%<*aU z96|6hUZsp%cMV2TZ54WBXXmNoH4unM;lDRo!nC`E{9rTNlqAqUGee3pVI>9WY5S>; zAOZpv0?B>qaBr{EDx_Xn(!~3bkRP5jrJOCfT}x&u_E&Z#G$43Cu2ZfiOmcI2g^NAr z3kQdK;%&FT42fP+fA9e*@hphjDmzfZpkP~}eDQp8|{lfp@@n)8M0X!$9X0={BE zOaEZE6bV`UtSAAwWICAC4RoXGSt!;hCG}1&Q60;<>5SnMRn=Kj4_TpIZ6PAe?NlJ) z(CGM|KAbkr7D7suvfqV;q)>!AKAumJ=Kr5H=RXP`GJX#@I-TGoelc%X81Hs$Pz1qx#kid9>=(%W#;V0DUESHAJ@|F~4jxWu1YM3dD8!(~uOO4MMI z30VrXfK{&QR(iISlNdQFaFga9prC=x<<($!U3NFCe11@;KXrimg5QDVjL6SxV2F6w zXJIU}`S<#QJnYk;kM~)0YwE9)6T5hNc9XsB=HfJND4lIyyY^J`JSke_AbWG}Et4w~ zd~E1FgJ-{`B+J*SOG7h$>&2O|gE!nuG39@^Zemh74|K*;NHc5UHbhg8#P6 zb4Zp-M+x_PkShsOafVHBz1sPoZ2D3EQR-KreG)oO(UIj52EmW7r#T6siXZKMDKid# zC|weL+rdAyTqB!e{zG8b*^Xs0m4&;XQQr*1<@-r{hJW-caB)NJ*!bebs~v9DK@q0oWKjoNH39wvo}5 zc|>Eppp%(js&SG==wKahIfv6TD(jwRjK*fq=X}=;XljjGG_4Mcd#ed&A__kDsyyBT zlxX4r6zIsyGTfZg0L<71vT%=Lc~T61eyOd>eN61G2GE%|-U8!?Q#D}6cz#`*CQO(r zVPMx#DcVX^9nWyihr;uxzU+}D$!CJw`};}I)qT%hllqLZc2b)C*Mtme@!crYk4ja+81#6ftoKwB%JsgJ;3y&1 z<}*GOa0tJ>A0>5ZsN)B!wN@sQZ8G&@PeMb87s#*iisGZYe&8#;?iFdm*}f%%x1sYf zUmqj1dUnu>xCxPm&RmFnulP>Hoyyp8*h#g>>W@Fh#NFoae-Ax%4>J>XXw;UnqatDP zPM)ea+^%>ZnAQ8fi__`i6I~~L(5GVh+yaY^kK1h29rMgl9h^Uv*h<{2WELw8Lnuvs;tLm#N9pYn-!CNm@C~D|-dst44@rMtD;_0wa=Yp< zln3@!_qdj&0rNH_W*9cdPah=deG3v)@%-;N_iXVEVp7Ya z91DWr(|86${#+RbmyZwmw*rOKW0N--7QOo^+#j(Fp70vt6-)Z7=|MhhG&7Fc$cD#h zIe7TB;V1NWqzT*u%-VErTrRu1C49DX*?uke$&@z-C{|xV>tm2<&)Nm}&jjJp84C`f z7;0(B%Q9mhy1rG6(a($L$7@mR*`l#`z(ohDESB23*hwazb?O>pgx`pMrCz1^Vb6(@ z$8ZBH$%89^@JTrQis=aaON*y@-)=MVSl+#0aU}2i;u|!dv3f{M_12u(l9OO!Bj8~O zrepy0cg9%SaOgHeYQIEH@g9Jb`HQhxm|V-{CQ0%--`nLUxd*r-K9UKK0}>Lr;9S;} zRXKjCbu&K`oh%G>#4Jir2Uutnm4u%uppXy~^RXuEjwZ_{`Yh^+qx5aix9H`dhy{L! za1zJHTCbddFr4j=7W*ZQiC1nnF?DN?=Ebb81LS^L{@@XwU8iIIxTLC3SMzum!dd!K zEACToivgiAZCUW3f`FM^6m-`7didCCQTARvp zx+`dE=ZIi^lWGntp#jwbJvWK$QtQgR8RK>BY5oK2&jw*rA3ph(KV$penxOTNhHP+w ziSsR|D(o~O`L{n;nE{L57wTi^l*s@I%m=)Nd2!7Br_oZ3d&sq>*|rb{H6UK9)CU`_ z`1^OHl^GfQlkYVzFya~L^8P6_Rx@mKTJ33}qpv@h6}Q||HLd2AK7C7-f zM5=IRNe%p*Q!Ht-BYh8Mw^G6^KpB+zynKDp;YJeRdnCo6PV%`AqCx-i*BK$gTraWs zF12~8WFDet)9j5QDq^1AwAx(}S~s+|seCl`cM_cFQmB(?M?~7z@nG}wTH{~^q=EI3`A2-g1*h}O*S9tvTzfSO;vr)8Cf<$}rKPe_&&dihuy^4*tJ(!I z_$rdN&R6E)qJK&VjbN-6!H_$RK>2z5fkIIGX_x+relS+iU`pmR`_jb zxjUsN2G{aNsqdp1c8VbM-Z7=xgilnAw@S&PL?t6~A%*+czHMj|a^S7L%M&k}37(|? zXhYg-y`mx>B^w*Oj{lrN-G)0<$bbEA@LoNu=Fts_+=JpW${P0qQPRi-|mM(crNq zKImOxRY0Wzn>zkuT@`?DE&*NXlqR*Cu!8%$GKJa22pEznzw|F79s!$MVimeyAmJCB zcsV~IeS7AKojQ zMzFYo(>iK3({@5D$|WVu7l?d?Ma)lfplj~U{(_~e*Cq4ga-47K5BN($`SO<`NFeO7 zRRS{GE$^5?JJ;D}6QE7=vD82c6&`A9@(CGUEdZ*w-ZL*#Tyx>wZp?Ei7Qz)J*}2A!puKwL@xkaC?7Yc?xO{%jk>L=4&m`Js=NL z9k*0QH$~#;&oYWueLwH5ckD*4H&DtsMLDvp18jNp!+qK$L&=zyXFEwkT)QiYDZ6{w z1q(qWbev~nbRB?S`&@*bA53ohg zBVJ_E%lObXR6S(a-Dkv(c(qtnouqGF`~9^E@f+HQMk$4~1eg_MnOfLxeQ9V6k-h>H z3Jeq$=MU&|KX&?~OQ%Xmso~igNEX*r<`=TxDdY}G7$btcMQAM(#s;kuC zXPe((ZA-Ui-!$h{pG}fxb0-3!63qZGU}C{SwJ-OidPcFSb{|-0XfS#&8f5lkToF6mrZ61&xrP+8`*PEu4h& zpYJXa#73)MJt3+Vm}+@iybp2zCz2n0HtppB4aPDEHuUg)@|ya(6ADl5;wD@&TibnL zjd7_@d-xiWCvjARD(cWJcrfRRVV4ruV6((=k_DWq8o0!l&z6GaqJMRP-f3?NZqD4e zWqzZOsXVcF?Dd#ae zIgO)g;r9Ao{xn%hkHE6bJ=}5v$ocQGZ-06crsoJZH6K<@?_Svr93k=&R>QTbR$nFQ zHy;v#wy@fq5kFU0C2iqSpoRmkKMWrb-w6+ZU=9p23Dj$EHn&N7?=J&a;63t$A1 zc9Byo5&{g&fa|Aree6cK}*cE zR=9-ZML_1tzdg=^!miW!c77!K1!;KkS?eeuzhor8t1&&c4a)#QapVbT1e>t^FD4Vl znXy{R>o0O%f~DOIS+2|!3r`&<;XO~EWq=1Wn(rPM8%f_~(+SFd`q0eHbJ9NX{rG2} z&Y^B`S@{MSQ{v2Oo6}c^Hs<~1<;Bw=H6s{Lm%z7&Xscb!`|g~6qpKqzkqff0GUIIE zJz?}53Ev`%TSdMr-*ycUP8l6?Oj?#9dpNPf7iJKcUXOBkyZz+ZTvmDR)t%}u75|iV zn68c~dGLwk4DIkzuEvyWV`YL|xn-Ak{yDQL^(OC9rl;aYP<)pX(iwAJbBO5sRqZuY znCaJ1wHV-=Y9>%`w@PN4Hnb2E4o>*@-;*JV+oL2!Xv; z49{zS1ct4?%{n%yzs%DDYS}lCd|o{;c5t~-!9ogH|913u6Xo^|r$?K4(w=t%IK zL30!02eN2z=nxH(Q%Ry?-IiGxBsIy6TY&a~6Mw5hh%ALGv81m(xi(NI(WJX2xf2Yp zcD03*=Tz_`##PAHY-!I}0?C;)G2G6-88(HPD4XSy83-5)3C=C8{!mj4mRO|aZB+## zHok-XTtashQqv300BJEac>DWRlt<>_0B+sr|7Ym)VVoBnbm-kF1p4|7;8)?08K!$zw+3ebeFW! zQdKT|^dK-+#Ne-=e}-aAId{mMw0!2!7@?nCla{|oM_97n`W}@5|8Z5zrWZI%uEBL~ zsb$u(ig;`@P#mNqM3z6@C+Io%g`ZB^BvemLgzMy#Ml&Rtc0Y(#4^>!l-n;ps)E zUJSK@-7e;hnissWhypV;K@{!|CU;+8wPAMGI|yV-^YG>i)ReHbOe#1HdM=;)VjRRP z7p$~75bKcOIE%b-oVu=)!)O&u9oK5PRKvQm_GZ!4aD!VfUVWKThBX@BDZ7NzWS&3k zg=X^{P@;eK`md&qr*GZA5g@m*kJN7muHJ7b(Q2_;gLzrLF1X0G$`cbytUfFl5Tp@d zGoa|!YZQiQb{iXE>3?6@U2_*G^B`H=--Q9NkVx7qK{ba)SY<>v823{<;W_Vtxsa}( zDtPAG8mLfa|7thm1%Gg1tWHL6rDBg{z)HcNOFLAThboevS!Tb=i?M?5`m8D_5%$N` z$i9CSFf5zF-(+$iy|yf39Zh16CD*b82Cr_!Ebz0If;X!OE6lGkmlbbt{({OK=;oh#%v?`;S1U0W`ix(Gnf779!hS+bi$OA!%Fkm&RO0We zA+6jv^NFy8iJ+Pzx2yaUr2!GA6H%cI=M(y8`8m>Ram;S!34M(!Y7H8kDsQyyLXxoD zDhfXWrCVOf6WfzF?gRl7%GpFT?L?&8cH~8Jd?1^` zU-fa-GtX>JIGZm_S>_>^FI(^1jB&;eUr0B1$aB$<7QJroXzc0lcuNeD4NRebMzBy3 zm6%|XY%qhRdNKq%CDZ|=VzWggS-oDs^}m0l`c@~5UrC>*ne38MUx4uFJ=201c3L7T z#a(OuAEy30oDJ^}8^=Sb(b}uE)t+53O4Z&o5qnk95)zx5Ej4OyiXuirVy_^Iwo2?; zv6Z4mh?d%`-}mQvuHW^0{y*nBf1KBKpL4&i*L~mqTMZ}H!9qkx!`BGl8N)hCyHBO} zqfgL?h`wMaQ+ddgJ-y{F^8SM0Gc|v9EN)a-!JGEQpvc#me~M6v#VRG;rEWy2Xe7q?NB{LM(#$ABebg_rR)8lLy! zPgUQRudf24l`p$dghl2>PWn8LPUm9h`8l1k`&X|zKHRU3BHGfEbS=K^TOaM#PPh)d zKCbs`d-VIE<{hj_4S4GqI>0}G7%U$`Uod0q8ARELCDag1?Kaob#w0%V1&3yfx4%8c z8ClRs)?T;lR4t=CC^=cA+GL10zUUX9_%i=Uao4+Wgtn$(a0*GORCX3~O&aw`A$fsT zbtqD75?vE6j;YF&cAv9bP@Q)xWwk5rJ)l0}@^fv4)dp$P>MCQ@u_B1+eEK8>Xrgx3 z-+zF4ctB{m&Lh3ss&-|-Z>|x8Ec{1-$&9x{Tr=mKy-0 ziR!P{5278uKUXhsU$1A5R0qqSL52HeSWPSv{QJGIwnwQf%?tIFcfM5zDt>6QD-l*( z$e&VwS9kJ8VH;awtJVY=USXYUef(@r;E@v!#iK?^FIu_0RwWPQktl(c7PVDTjkMRv z#U;$zUhikwbB0fI27hZ=d_QtgB~%5aNVfdp5JJyODL&$y2aaY;5`XkuQC#HKC_XD; zreT^}x;jFWsmjBW6LMny_@NA*A`q!WnlTeMEu#Y(i_!Nd3bfvlp!BEJ6@plTWM05~ zAXn`I^OCtKF2TkWo_}O80dbpsP_qEb>w=C7FQb-d9KNNm#BLr^#gg<_DuzM?JUGQL z6{Vve!b$uvKQ?Z4M~&8}Y4f=1Jxr!t!Ce3FXp%QG(x_`OM&K)ZdnJ86A_j~Ugc&C{ zbE3FsZfJJ%D%zNm7DIRxW_GlgR*?5&KN^2a*{1m>7=Z(_FTKr(P!wWR{NG=PG2B%% z#9bzUSz|(~4S7y+I4NYVKx5QCJMpXhEruad$SvBbbMi+6O^H&f;8T-N`)73B-!(#P zeN8w)z*j3)Yc$V}*8|wNw!)3a;+sBYefwnX!_Hg>mxd${R{dF+)C<^`HhZ3pZ%jei zmCP^QYI(XV%5_BxgYuE4bQdSl;CW&p=;GDmU+l7o#K=N!$er(i=JY@4?Fw5i?o8n{ zUn`t1)h%D4H>Ablm>$UtS~hPp*nMnSjc(;+a)!3QFl2GXhNNEm^xg6A)mp7qm7t%7 z=(Y?yJsVizPgvzxen92aLfATKj94aj6JHj?!54hd2lx6(g-YCz=eIRwBQ06O;E(4O znL(45rt4)H1z9_8-}37*w88g9>PMLtbD9BV;L;%AA=Qi}vvWz7yOz6qfRw8~`wy#_ zH_5y98C8g^K@n z$>>=B)fZhL9|$mAD0@phRLY-jx2Tb^&HO9cY~+5^tF#~T@=8LDpG`!b8=gp4<0i5d z-_9;_*qHdHV~-Zy6Y0(F@+<3gd&udyaJsi>2BgKoruxt;gk)NAn1bvlp_J%d4$H(qA|9=EQV+nWE9h-S>^2&TSJ| zv1;=zS}beIcYB4W0t|z-b8TXDx!d<9fm)JU|1nbY5p|AVyjS0+(fM zA5j!7dHuQsWMLZ<8~V#O&A??UvXkr(RbA3fZ+`c-Lklf`&cbl}EDjYNMPJ>^%^p6^ z=mvI9INJppdbz}NsU|43Q<~8z{QFAy(cAYFXd_1p}wa3GBt9`Eo4W_ ze=sH!-<5JJCD^>iVyA2uiIe|P2Y)w#8`5L1LfLc-7D#wjR9-7;0ccglgybUF6!|5x zbZNf7>aF7_NB4Vwv^BbJ?J)7$pG=DBwKPggjxC&8`!=2j_e_cirq06EDkz)4zd?x; z6W+)!a(2P#xvQJ_O5au4)%>-|CEA+n?Ih6w9L= zC1_rvL-4Sq_SYXKy-f6VbzXpx`Lu6MdQ59|ouL)8wg!U71iuUZUjv!HN!a1%gilhU z_0!EYvN^dTc0|f?fzPrHd&-a}{XSFE>K7}F@z16C=#D_)I9$z5kyu3v;?K*%4$31s zgLA~F05hX1zgNTZPxKx4D{-+9wp(elI9<$-662?Jc=p6Zx@9Q|_33h?2k8c}_F{5> zI^mj-N~jWy8`5Kv4$?G8It@pofD4JPE2T7Aa$J*sMrv3*3nur?2dWZFZG8NyoK~-6 z^FpF3sRS88wN`Zs`#{OAy}D}2i3wVthb0CaBk;Fl7b#f*3~?1})yLjsDWb#Z z0$riViTvRPe;URrKItw_gF?sxt!1BZMBkZLP-*B_%TuvQlexAy)FUbS_vrgsHP0ro z9gS}^%d?nOmKMEE-51(MpjG>mRJ1`LRnXCt+hBdD#ZtKcDVq_a=?Ehk8g;IxC-xku z2arOXx~2cm0b_)MBRYZS&~02;o383Hsir=I`=u3Eg87ksFLcS z9%`jw2%(4BdacGjc#>xg-d@!plmZo3@w4)NQH(*#tVBourx^xedRcgDROUZGb@|xu z15S-vjm%XZ$la>}0y9#IpxuoJ4f?lKIX(1{1^)x!wdyP8xwMEbg-O_kKDiQ&T zT>83{%;3+y_JC0g4Cgs#XZXY@9>CC&}Fl^{(Q?gN!$P^cZz6b;AeuIFn}r! zb?3444O*1;40T9!!904&BDW z->vI8DY|K(`r$Tl#&KgRS&xOVoP=2hK+L7m%qM6~$() zfr$F@A3sm2pYxd#xL0134-on^COXe}H{~sr+ka>9C>pV`7QI3p9)(M&@Mx#SxB`q; zwTufWjWB~V;dcW-QPJA#TTzcB%urg-XICHEJb9vpf2NF{wFC{tODw>rDQfW3%_ChA z*IFvCpn3rW-omK`%MWg`SNO(n2SbgsopMLM!A{ey!Bm;f$f;Zb;C{g0El#&x6-DUu zQmOUM3I_hZrIC(Df2*!7>%oGUuDY$RzUn`KzLsp+RITqhhq>Hub}{)=k3?lebMYzM zG)U-DZG!j97}|8u3d;6s@O0wBIbe?cnBd!bjVWhd)mf1>^K)Nm{&1PdbDp^n^>Btw z@mIvh!Mun60A+WPJyHxVb6U6`I^N-9j+Qq3oSD^E1@c`;SmR^rQhQFP*n1j@Oj$PCpEM*}ak3m|+`8Xx90niwX9~^qQh2Q+82RCnZcVIVOZryEtd5vBly`dj8xIZL}{H?fM7X-+GBW zL0qP?hS5S9(U+{xY=#_GZw1^ce-SL?2jD3&J>UlG(NYRUAhg#Y3%bPS6>GW z9h=gmWxG%>TJ0x7f+|pj_4r$XrV_g-y%}Z>HpUW-5~A_PaZL@PzWk9@RyO0!_3h|5 z9@dg1C;F#@a7(q_uX>bX7QMFT?yyra*Pn`Tzx3IAJtm}8!NU?I|Ai2DQZsb{#k`zh3FZ=%oOq>QRml%Q$F$JYBaMoFx2*t$(z}p136=`^ z$oFz_%V1_^{p`zfxc?cgEp5*JN2Ut)xQdE~S`0W--%TM+awSD>FMtF7`bN*~zWw9# zUv+Z(yT>D5Yoq*m${%x6=dT96GViu#32godXc3m?4>G4q)1Q#E_v`3#W}SJlL@M1j z^|5xs9^LUMt?Py_x8HyKTIYN2s9o>ceUWAdLEV;LAHCOxpi7~xpGvW5Ctd;EPCD(# zvBG}k0Kv@X)=&9?B-1~Z-;cy>?61t2x@gizpLX=yoea=5;b$X)!*GtdUj87^w=<8N zdwrhA8CfyMa9i0=9SCKJ6L2L^#`}ot}UoH7z?+wI$<9v z2@M(_xS;P9?l^qQoUsD!38jf6#{=xJt6 zcoC;qoK`~j$dD4Q5OTIQ6ML;2Z1zSgH|as}*Z|hBwsN5 zLW)M2Jw$iMNZ_pkx`A>wl`S~)>z@3+S+3_|N1xwglY0l2!uYk0TqBw@`T9!#-ZJt%*RH01}F zo2gYc^l?__tI|aE+>g1Uw)h7q|9!SKluAh}zzuz8wnG4y(J$>!12+v4M48|PYNvkrd(=W#w$`LkaLb1o;$#|A^W<4I=M z-so0a>Rs~I+nd^sG0-Ynz~lLt=h$=GxIL)|Zo zD;k6RIPNxmds>#cKWXXp+sN6&1N-K=dRFe7K~Fn>K3^58!s^NQS09KNm)A95W8StN zp~iv2yCw@i2V4hgkg~$f)yy|P^w)bTqSr&_j(7XGtU>% znnmx`^sW7>1b??Qms$h{n8lR#Uy!MQbWxv~fjc~HP=tX}kXzBXZFpA7uk6&wiAuCf(-H+^^`Qll|JPbkeuAmm7JsB|G{o%hI61A5-qOVzWv&H?TUAkh zO0vjsk+q)g|AeJ^gNB}lw|ajbo!q~28#Czg8Itp z;SbdV#|Jg@wkS;+{1^u8=%H%Ldiu0LU2a8U8&!#T75_VbSN{Pf!!~2^L%Y5#r2hQaj0z=_iO@9M4D6n# z*@r5jzQ@%sW4ww_J$}Z0uxP)_al^I2`{Vp`aAvBwS~+@EQhk0zd$V#%H>m-KK;;FV zrpzS7eDf0g#J;)A1NY_bcJ_6#9xJV!nH{igfMgXD7SlpK{t`X~OKh#wlww_QeI#@J zW?$J8?y#1J9_s3q2sI#TK2N!c7YMGYTSXps)QoQS2eKM> zbkEX_Hye$dn7GR43ZMyjj-0T{AS72M%NpCHFk<(*J>ud*b*uO5sV;GFU3>;^Ldrc_ELUnXeHa*&+YK( zqneLO3hx&SOnK7b@v00DB#pZe&@PW)PGU*|aY&FmdG=hK;ofT8ov4Bh=?4xl6Z&I| zIa&eM8a5*lB^)PX%pPm&_q*eoDxG#g+3e0PtON;Ksy|0l*rQ5w>2`in5aj#7gRL2n zq>k6rfh}V5ai>uG&gBI7@s~O_+M8{*#czoP0Yc)U#7aT4flADv2}>DJT>LAo+_O$u zfli2JfGX4gt(lD~DlP*8kqMjEqRjN#mu+9Yop&fy19XG(#-h z-zP!D;YWWd&b=={6><1eEY09b6jYg&xFs?@pY~SLTu9>WHp3P&nrRPaZUQorg|x~_ zm?RA+`aNDSHH@w-IfmZ+5nEhTe*Pjp4wzAp?<&BdQhBYY5jhWk5lJ)|iyg;#6PL3y!3GTW^v#L7*1~iBf;VEX$MDzNtwX)k{m( zug@`N$NN~37Mt7$g4f5@@0e5dMKe`ZKHPndw&to{`nFdFq{bTbH!LJo$qBpN?juu^ zLGl(V1ABWukC!MrIh#BljFo;|$G#iIv)K4EYiPh+465T@6L_|r82Wa;*qrw*s>M7OVBYt?8 zu$~|(Aa7DHWGy|3&oBqu9Us)O@~-3uDiKCd5Y;jz}~guz|ES!=Yq6aA?`*n zGqrvk4QxtW*;K9WXV=G@G14QI7TU-C0}2yQzm5CyQ?wO>l_*&yPoW#D3t_f74x=FM zUr0-ksrVm)q;{2gGh@Roq5R}0kjea*iL8J&ALnVD#J$ox3i-5!SN;&aQWnkl*T#75 zq!}gH{>ZFr5QYL3q%0xYMWyRl3gtf9Xr35*5yf0us=y3h=i3SWwXYR zwSGr6O_;Py*?3r;$U_oC)>$@yJ^O9_(b?7Wb-q#$r({F50gQ_K+Ni|6a#7|A+AIIZ64jfkc^o&CGVn9Z)Y|U zKz%yfp#0=+o;v}i^GTFUeCjn`~ehpOUsC~Glg3G z6}`|q{yx#r7(=ZnueBu9E-3SI$pcw&x0HW9^Q_pO1#knxgVtg{NWssjBI!(pzf6{l ziSzSksUd+`2zH#12ar*R5e$%B{)voEOLm9S{vWLncJfH`(>N1^QPk*3vvD0TnteVk z{^FZR;@!>m$4XhP5y}6LJjwF^7Xc4s#GI7W;q^3|w4)A+mD!jG z!R2NDtvM{pfAn7Jh%YCpbhnd7JDFm=9)E(2Nk7C&n4pTHp`{LbE$H4S_RTsbDJg*` zrqWtQX5iHo(PkcXVH<5`xwJxC`uqo3%rTAkHilm)QW_)xE&)X2OaTNKmS>4nx~y9? z^ps4cCc(`(!JyM=YtosKz zx2a}G{;*7{IpNMe1Pvq{mt|h3Nf=k}TU_f`pGbE;Lp~?V~O)MI{`fYTJ@MAvLsLR@*$`2FB5Ahc?isu@uqLy}S z^(Iz%33(sIp7C6aKFj)yyHB*$34}=aIAzJ8(NYkmflHI zGW3BnRwF22)t-NL12}YJRgd>>F?)2SF%Pl{fs4v?y~jCuXy(f;^)+M0)c#kQ-QQ76 zf$D*xD{oTbDI}Pd?6-}W#NyRWc~UiyH?-KUSjy-|8-FO{-UNSxC$<<0wp=6r7dZWA6L6fZ3`zk#Nwpo*+}McEtU)5 z)Dl$t(Nwzf^SlEdWNNYWr@W}{%z4njXdOE79?s?|?W#^65urDlXsjICK39xu6Z%)f7AwFX`E~Qv$I-jKq$WLzp zN@V;&c`Ds@FP9JP7F=+`Njxt}Z{>9~AP2 zCM;fj%`PZ?Bj{!gmdh83>prcG$dXIgLssqrjuexXhd5DM@YUp3%ny0*pLkjXORwko zspsCpsX|^4B57@urrzzKV8p);17_{XtSC&AezC;LMctsa51s~`-L-zL_-PhU8arMC z0$oSsr*5O(RYt3$ZO*r+6Yj}K;Quht9PlZ-keog`B}j_*FTvo~bCMcvux8q9X^&Yl zO!ai&EmdU=eRR$p{J&!4w9&Bm+y5RHc z>BZn8odd5Y@t^IiH3J<)fi4cU6!}@7TT!*rn*I{3XC72JzcSiUN+Mn%TM7<$ZwWDU zM)XPE|CO5Z?CE|*>Z0}cXRL#7@?IMV`x??~jW&wy#HG#sZdjyNH>ZO&+NNyP&=`>+ zCFlaOS3Y%}oussOa@i?bYISZbigGXd!KK~@_0O2_#SQ(nt7%_Gj!;T?9G7TT))(R! zaypDRa%LKTc8HNW2?;B3(B@9W>zX7DBxT~%fh*OajJOK_TH&d&+ zbmUH8G9$crY)9N6O;KqH{NYRZw6i{~MLU4M1KEw)HWrY`kF~sbIhbuy^Hq$#xPP(d zhQE2ehE#1;?nDsnRauz%pH|-HgYHGatmg}^NODbwColB5vjsjYb*2qz8JI_CSX(SE z+2hiaS%(T134WvvkJ&!?7mbdNK6_Wj%&K*W z1=|a5=eu*R@JHVRTU14QAfgTR#)sB7yK^d@B*P;iTw}&nuaD&idu?l!BRHdfv%-hJ zeLCM|x&I%nMIn!N?0NXf6kb$Hn5?lxMR_>iPK2uAIJlvh#GT`TJkFPP_T zn!yE4JL^*y3S0N?y?c|BBkYY?1_DKu ztJqS-EBI`(=E);TM2T?usV5F06;{^+`!{%7n#+at_QXqMcQ70M(8BR&yKXTzHNbZh za+Q&kfl}ro#nO*`ZY{EQ=vhtLh{x`bfANR%{JuzJs%54+dM=kTRW3xgRcJQ%#T!3H zWWAVM^=8#6NKF2)znHR6B}67n0gzAzyH^Oam z&Q;+nUsIQoy(Kn-QG00$bvWf2%e-L*DX0LpYGAZBH`>16A1Fx2*H8D>no6e4>bwa5 zG7(isUt6CusVeqM=V@#fv8snVYzG3i(r3;PE9I%8+0Q7>>3VYY+f|G04-I#fj^i)Ktpv?Tg z^DVF6Ms4SBz&ri=?^hY*>Inq~p~a}V?!5&70CW(goWZ9xl+S#83iZ=HpFMVdKnqzy z>a<-!g@|2Y$f+3dlf5iD+WJ?; zcFE}$TwGNs9**Sy-TuF(QI5Xo4O$PAG$nJhQz&<>N#9pizmN|-s@0rX(=AgkR@FaF zvoS3nXgimWKbH)DFg=BqPQ*p}e%fELSP(Loy*&H}m_YyeR)y00U;tjMDt~9=HEAZ$ zM1yaIM*n^S+_V#t!W2rB2KW#8X!iVo=wz7qdIhz0qhIZK^5qo9bby*Aeq}VSfmv(6 zmP?dZY*vK^1N8l9lQ#H~`qwt+$<}ZTQ~>h>pZR0Ieb`Pamjjl$RwBltd^S?U;G0i(9i%s zcR&Aea|K@XRC1LeAKPyID%sl5^F|t<`bUSm{IPR3-w6w(Oh`t!nLP_iW!|miu4wB7 z2(;QJzFrY$x4%lb+6jD>INaJxYe}jTQ(ubVx5V)1lD4<2I)dkq$IwRUwS*Xps{7I| zcfR$-2f)O;ItTg1Dkk2Ue6)2~VqFMZOfS^OUcUNn7nIWNB%aRL1Ld_?$MT z%}(K9`Tb{OXT0@|Kd)?x9&y00zJ4g68|zv}$CQ^}J&O^+5f+x!e0YR8YuyW0uM1kg zloI8W-UN4aPI4x%ReYSy2#fzoqnw`U|8f2e(L?M;{nKwq&W>67&*P4dF`g;pDa$n5 zbB~kOA87^}N)$zCUDZ8Eap{riQhQBTptAvg=p&-Js8zx2%Ox(@)_B1suB8&=A9#23 zMxzni(hpwo4~|TBWWx`+3&=kWH#?BNFVr&TfJ7umYAP5Jx&nHp6ySWq0=q6y*2*?^ zfNV-+%Jw2TEiR&&Co8k=;1r6u%-Vm2U{*@Rl zy)kB?XW~_(IBA}X-5V!l4wbtKyORq^5982DXzB{Atd0z8K8sc|0`^S+J1uz9lJJ^#LrB!)5xxCWG?X zE2C1i>Ua9Q`9Ma^$Qd;X&iA5DXIt}bQIz9{nfsIFTls#WrHOio5-MFvIa5{@IzgOb zP0CW>R92R?Udg$ZcK#2LTgzm;q`eN2ehU0&RFT1Awj75{8?6=QP$jepU%B)2^G?cw1EUq0iQY$)Qf=z!Cg6Q z9)5IKsg;YT4OMQOP2voo2tX`YME)Ep9a3bTnW{DP?NWfvK`MRI=XgxU-q{?VcOUOwYgO*j$3x=mKv7|_cH z-w&ab_v?-v+=oxT)4o35Q~hBRH=LW5FOa(cI8OMFfr!!+z{Uj;hA&_5ki=FkxxO+A_x^(Y5EDzf^ty3UMfq%9d3zb!eOf`kPTxL?LV zU0~`BcUlM>2IL6OtVaybi2=U&GiakcBUoQjqfZig5xqpBFZ%U>eNOFET+~KN>E!E5 zqxJhFvm0}Mqa@0}f?uQLne&yYZH3olFY|ls zs|qDF37cYLW#68ybrFX*=2xHPbaz5GMx)|I632kLfKGIs86qVy;>az*rdJYI;I95Y zr+J*Dk4ls~-NKg9VQ$ps`$2QG7^#l#!*RxkDCDB{ZU?UxtWDTH-u<)XKR~Xrtw94p zeS;6ID3kqoc!IajI*!?*7Z4N`u5%$9Bzy63Uh2|nx`kj$Z=L%Uu9S-YvE6Z8*9H$d zBW4%{STZHhvH5knTHZ>m(&-9EhFI44qlm>aM#55+8A!|h$&yGZ{)Hk*^w z*aWm|)pzeOphQBY#q^sj3mhv(Ez~T3Cdq{(Mo0~-Kr|l@FK1ZwZzI_mCvdk@=Pl{W zMbHn52G&9~Az}<=XWe)+u4GX#2DR?V!&>9I!WF933fDL5RZ2JOs&>ZHLNTtR%6P4N zy=&Fr<-58sn3*^J0W1id0Xe(m36gc|_c4`=b*ZHU|NDJ9*rDjDkgyhmP--fz8oWsx zv;OvPZv;L1_N>FUezdZUllocnZWH!5bZpzj^ zRR;fMGZ(3Me{K)-{pf|1*%WQ+pD};90X@yr68Bj|lwuTp8A-MjNS^JO^PXbmmc2OV zf=Q`^nGw67l`fAzQOGWTqBBA!5FCAn3mQ?)c>3RZbZRu_WyU)VK6I4uv`8r zfrx!VpKgnRTutz5v{GaLGv0~Q21)T~M^T2N%rNTp+Q*38DX|JVwKF>7Xt&DyO5gqj&>F68n2=a>BU)qP6Ps$cb z6HBBeTe6s1p95KIn;J9CyRdY#VtMUaOEX`sVdk!o8bzqmIEZWHW&Xs_%DA1W)cnKq5yD`gIHb-9e3 z-R>9m^VboQx>tr-YfZC~-c0@5vf!28!y*ji(bL=0pFD>${K6QPJ3woK_zL_X%2|&F zu2j}ArDC;<&}1QU_fjp0-2duE%(Q4-$RCw+jmGjYzICV3Ob}q2{5u9p!wHS(pQtgj z)Q9>1HDZ=%HVfE3;}MHxQqT7hAkZ2_`~%$l9es~-WZ91&J799$sw-i$S|j*L{MD5x zGC+qX6)CKv-tyO#a-_}UAHd9vr>`GHIE5}s4HsIFnvvrCBHi13ire&1(L&al_s>Z^ z?p43eFmHegLVIOXV((C&K|eeYV9pmKon6c<+Es^ga;%378g6bFJN92zGRi9_2RAMK zKBc?jUkFR)`n-<)qEkbQ-IAr<6rQDou~msf|H48qzyAhbo#^qz8YqJ#ob#)pFF@owHO&2!E)*v9h%tjVPa|l+7 zZSkegbl_+Y+XE$CHT`f%i2GGDEoJYn;cpDB3#6B_ysq=r5$BtqWk_<(N9`LJlboqUToOuWOgV1GX0_%TumW{mPDGCD`pGp0T-DtA5(7L8oRMz4s=<^PO;5D<6r$P zbG^|}!(nXp)hai|$vjkWBi`t-TjOUp+L}j>;7SwtFX}xNM%jY)*xOIFhNn&?KM5{p z1n?mCEwtYVUv)kO4c70m><9h>B>&A#d*bA~5PEQ5KnE_6;4oF}^kJWo8POvC53n5< zLaqv~QmZCR`f{Y_Z3s7{^t9O~3HN?{eD(C%t3t_@wwYf?O+&*BnfGr8Tk;-p$z_xt z3fX-$NOx89`O1|JJ7(L(NPNcJdJa=X(9bXWNF_(tGpN!jR=0}xy1@2m+C;J^uUQs) zR0QijnGetM+7`^Z_|~!g#~kK1=?26hTuWFNEo~d{!hd9pp>>gUeoF3MQ48zS?^Iq& z(Jk}GJLWCN7t#T3z3Lks`r^!lwU+cDtW%-u2@Us~UX#VW$3D7`ZyDg^Ju+oye)cG1 zN~b3!HK3-m6CFEGd`3_?0(4zNjJ9EZV z()-AZIU4flptsaiGlo_^PhP#wSeZH%JNpOlagOXtE^|{fSNovDcOd%E)ztG2vh<~8 zngfr4Y3ZXlp{K-YH-J9$&52h{(^}MAwj!WCI&;KNSg*!%{8dV^Ypv4CrTW6?vQLp- za?k8XArV^z=8vwHG-&ummpX7B_)s-$MOv67X4V)GN8i$L6Z(6+=;f-q zt&P6!E!0Z#gDnwp*rxm*uLW{z+S3WHld^b9Ocz#T`fN%9sn7vosJ*6EyE5Pjs7CB1 zD1qkp0^38>a!T#YOoPdruQ=w_J-JHx2b(Xw)QiO|1X6%P4L(k1HE+SuroT-C3@^EV zJMmE##3i{Pvp8lj+o8dmfWLfX`=fL+0o`{xR_PRu4P5MurH}w|y|h_#{m{S~;||Jbv)F%t zG-(l*r`vB#@4d&0!v&_(C5Df_@_FwaYgrz_T8Gcw7fMwtZ&^}qg`&*^pQijN&bowS zSA*f!PRHJtKa}oU*S@#%m%A9I;lujV9=9=j{9T4%bHBb~(g&%qv=-7%}StqM%M z!n{$9(*OFoJRx8xh*lh1D{Xdhwluo&?T*ps7c5ey&4LJQDYJOHi5cc$Qha$vb%Fo& zpzl{N?v^)BNf89O9hs?(zNuv#O8y_f|4Om?gSG0aKStNT)*|)p{zaP1&s-6!e}I=J zZZZmpcYo*O%pV^NS!}UHHj-BxE3F=Wou?e#SODJGau*^QX9Ee4OU&bQ@X{>|GVy-$ z=|@GG(7}u}6+M2TbIuuahVMfnZ-s6`i+vg#M0yHNgxg`QNQ6YIom=HU0E@j&%~YX8 zj-X!V*#iF3i9$L1i~e{k_6t2v_rY|2x>rJ*H<}Z5Ofj0d(Mq|%3L>{{$|?M!=}X@I zo=~kYpoD;t?G^ZaI{eE0Y^j1L_u=?i=f*8XZe zC4MW)E&qtKJKj={AXXCIan6flGm6A;bn+3$g+ZZvqH7iM<)uUrs0L`{CT`>-K=wQ2 zKXgt>%ODrpCaPCHQ`7}VdD*R_>oNHZ8V7{1Uor|rXxUEr0Rq8cq>gEDBRml9P4@w3 zE`~Cgd#RN^P7t-`VSvoH`*j_Wc?T0du|EMl{#mI|5tC78SIr_XcV*8Bq${0EYR=!av-8Q&D- z5UuszHD3zKT)KoeKBEcVWG6jhQ`UJ8PHaBZT2;%Pbwa{w_|kDCKF-0g5e3&iZ8>gURPR}uHT5^ zmB2N9Q}I)fU-aSj_(jjckX7Ff0AA*Kmdr^DIO>^7Qnf{%Li2VrcnP4rdhd~qB$BVQ zU&XxF+o$Np(e+}Ac6R2y2`)&yowaxREH(#hVLD(rl^R1WXkpQ=?{D2vY{7yBRV17Z zZq$-(r1CVPPw_%O|L)%!>4ch)${U-JPAbFm+w;vNf~e&~6Yg`?w6F9VtbDA&(z*ME zUn;DMDx7MQhq0YU_03gBl!zlL)4J2Y+!8;kbduKhV!qT)NxDnuFa}Ff>-E{aN@=J3 za%Ibf(SJVVTYL_k70TQvu)g#N0-eXSGR-@-4wYqd|LPJP`D1+`ebQl8Z|AC+fq=}9 zg|JqYQ2nY}FlhV58DRaXmUne=r&7DV`ft?k(_6!Kt^;8bzP#E)wy;sz4TVG}H1>&9 z#WnNT3Z;?Qp1zTZk?2;fb)XRa!Kv6qjWAX!G>rJ^bm`Zdv}=)?=w^A; zKK5ua*mu9T60?x4bzRM|01_5bNQhjN=bUtI0u2L{E&K$d!gTx;CGNNyMIC_kV7kf? zKtgp|0z~52{s}5ZM)(4DMlSnB-2DJEK+L~DEVuXOxl?(A4Y1LI_+d^)P;Mf83TyYi z1k$Alou>I>&hcTDPDhJJmlbGQ!dC&lg~9aC<~|I=oxQi4Yo8aX6&w*`1ecOkl#adw;1p?&IvQo! z!K2=Gxyf*1yibbV+eSlc4Wjy_<9kAOO^@q;S0%Y~SK9H9;T(4ME`@A3jTwedAB)i= zB!QU-kDNV|O^x*uMw%3nHwW`-)XtzDJmz|#ZX5oFEi#A%GktS{8nc*N&_w<`A_{~a zXVPTHQA~=P`Gm$z*unu^KM*J ziGK>m3C00;9Klgz#{3S~j^DLeeD4iymx(v?mQ!T0#7(YM{w8KII_tmE)Vi@3);u9A zxo^}SOgVmGKo{D`V`Qs?4}pFqv}{#1!hqwphM{j|a;ehy>dVPhg~}&uE8BSy#U(C`R<(DBdxpj0BE&VYD&WeAJ$n-RSZ!>rW9dg=Hd$*_AtAW z3QZr@&ZmZ{rYyxFC9&nbV8oIvvW3jgVDN`xHoafao&Bs9B`|aJOk3(_sOC4AIToCw zP+1(O@CG^f&^grxvyUc^lxbdCQ3bSI|^tR(a!4*pJaS+Ug*2j56k&>ttQ2L*20 zuOR}zsL?-b2_zj>pw0sgdnw7^dEvSLf~)9hPHqPdjaUKy0nXju+eIWM-d;{SV%;P; zSb5@1)aD$1_duRV)Ze5?-W}YCe!_#_yUk&4lrh<@_79*8ZOfWUcMa~uo?sB&w7Hiz zrB5}@uw8e$UHwi%qTJ~hn^85-n~ubg=Bo18|BtP=jB3LP{(gfME3SoN#l3iOw?L2( z+)9BK3vR{Q;_g31Z5;YD^gZ`WJk9y)B0?h}>xk(9c8Z&rGu4?N{q zrxT%;h@Nm*11mOybp7>Cr*)_NmxE{|3Xu)1yZQtblJQ|W|7&UTBR-1etW1qhM5Xa~ zpR>9hNj=Kbk#WC`md3d+lrzu7WtQ`@HSlIOV3A4@h~UajRnGoJK>)@B~0JE$0%D zOQ}naC9loprt15)_04wj6WVJt%VNY#qqEG%{*iCMmS;Xht<7v!_|}F-CpU-%bhQg} zlUbimQ1opUnlGlmDJamF=Cs3bc|g|2Pm6jPFS^f@(Q!ptdaTm-1aGjN^nM2&-TMv^ z(tMlQrSY04X_6&~8x7HC=pM&!`{79XXG_aaNzKGxb#n%!?&**9VbuRsMKs_2&k5i6 z!(J~Js~!y&WOYE?^XlFkQe;yaSauSn+hrn;slOd_|C4~l6J{{RU&UE+B|_k0#FR;;i0U}1+$!m z%EF7ZkYq}`Kkz6kVT!5znBD(tK9XHG2t5^19mV;-E5ES5RLEmh|9_@b|8MGM6eyg* zNR*OSLeos_qF2GFtO|BvneG2!~cZ@1@7?10nHdk~t%CEAmOeH;w*bE+67 z-Wyp*V=t?o4^*klL1M`>igp=*IhqH#OZEhV=r}R5jdJ9lC*8vmVRF%G$O>}UPiL)19kMy*@P+q$e@e9KJ zQ=k{gB46bcK?v9nkFdQxT@HQcq`^PUW9Tz|fW~+IW@8N~{3~?v;;fEewdGto;pLYQUIqsiS~mTc zpgaRG3LtjVMsCuL40%!NaN62gjLBW%BC66n7jNfzs56JDTo3WlS`VvP?2u0(MsvVAa;W;<u zGib6IuEXDH|K!kQY{ba{T+Fyt_HR@3;z#Q7_KKP)>%)zkqxRWhpV)85T&XC1F^Qk? z(p5$si!rN3e@bQYQJMrlaijBYSc#V zV_uQ2F`XCJ_-F6k%4r)oHz}*cUk%;q5?gSZ}{7}4xCFfb+>fvzD zQS-NZT5}vu-ufbajB{OyaU1s4BP#h`A58($rFzSb-G_Ns{lC;x zM}JSNw5&wfwoz@RFwgZ6Pa0NQV|d{+WQf2+!*J1o5Qc;)#B!mcbs87gZDzoU$kbpt zpdVo;V2b(|-Y|~bepNMHU*;1XnmC&+Q|r{5;&;=Oa;T%#REg^3GP%f=x-2Y%+kZp& zc^_NqG>v5!-QImsU~ajf`ig{?l@^;AAI-w|P&uA>x#_^!lJ@FtvP%bhwstnr6u21W z!uhb8Z6W*tO(K=HP@IpJc(!b%h7gMLIQp0PTPNYbgJ*hu1%Kjb+dn6}YI(0Pk`jgH zujDexsM&|6x4pUji)1@NC4$y}f;{?!Va9;AUEHMyPw(bx_xARVcuK?H-rfEDEnWD% zH!1>K*Lf|kUi%DC)wW65_Sj^8*LrVMnA~FG$~u@h>z9n4Dv>BEp0jE`_6(rts+N8r zGZ1Jtj8MM|AWs@zHT+C;O*R7UV!j!0ZgI1uwKf=u{YmuAt*ozbUXT@rV_K*<78Gs>bgeM+b$g`_c!^YHsAWWPOD1=aaHg9 ztjKl;EiU@p7u9F~_hyGpbqZhEi!88W{FnPgZPuEK4U3qWN{$@66j$WfuP<6W16~9z zE-&rG?%hfW4{Xxo_bJwkW9OX_x3(TNOER32PA_wiT3|c((hz|Ok=0}WHd@YF46pFo zrwRUm+slN;yI(#bse8vc?*sofk9+S#7@NLwNzdq~HF=eY{UNLg4k=;JSI52GJ$bM= zW(m}&;Z~L3C_gjHm*0v2w|0)wtDmbT87qx;XWnxC%Ufl8w~+`tGblB}mtKjj=LnzX zmILfnSH$%qk%F-RzE1%MF=Yk{z!(xYO_GbUx?e=rU#COnq3S|mRF8_7)am|fF zoO82(3)9I}w{Prn`J%P&a2`mET43v!>07)Up1O9&!iJltvo0{a|91AKWZ%p3xzoIF zZJ{!0&dQCtK`T-9g1fc8lbdAEp{lmmQ`d28pbwY$t*;Qi;_0?b1N%fk6zr**Abkl_ zD(Bo>-tUOw(s%626N8{~KZ@kxuDxpcW5t(@=clWx}; zY7n*94%S|>c6AMPT{;mxuS7=zoc$`s0R{^1Tf?k}l&wRp#r~YOv=u(@046Bc3o$n< zfh`qNX!>LcqVZi#*?WC+gYdVxzjP`z9Qm~itLu(PEsPbYxj8Q`Z|p)8g2L2A>1Dc> zI~$e1mV_mek|r0O?ur=8s3&>ptq({)QouJ8h;OlZlBZ4T)fHW?uBt$(Wt#}doXRsG z+qBoOA|}9?>&VK|*}vA5e~T(?06M<9J6agM=11CoK$djPnt$t8L0xyTPf^|%pt@0s z8FaQy5MgHh#4xnAz60L5cgn)Op=snTtdA5a{AM@ zaidRK(xf^r$A#Hl-c_P}{Cm}I0Z%r{uxE~Rle(%p2y*AG*tK9)q@8tHSbuf7rN6C| ze4%eRnu2l}G*oII=MMi}+gO#Ic*@@{Vchd7(e@X$kb4xFWGT%s^HJp$M)9DoP$3<6^%`Hh23vvBk-{>upZ&`ETWZ6`nY2Q;y01ywLy9(G zbTgCt_&eJ5c*R>|>bupQo)eXM7eHWp*^2zplM6_Q`C>PHFjCiRrOXr@iHYZR!K@DD zC{=ODfQqxpQW3W!OKVfjX|dca^6v(hArFmhu@>=DchS)cJ7R!mfI|d}J5}8)P2ST- z0R^<>|HS0G=s^2|ydBOgM+DRM#3B`TtG{0NtoPpe%n#LNCf*7;gu&}$nFd=Xr=iek z29`Mmpgi-uTZO5w?I)#gWK!gp|5KX(rf)4?uv&6Q%6V=_S^5_I!FI+3s$DHO^(ZXF zNO+Y#R0Uw9b39$tc=z{>`$$}PNEMh4fJOk(-|M-dx2S~?LoX;fuwYj=gzv5lar3)2v4K5-4wcnw)oJ_c_ zCL4+H57~HApkra>*N$<`g}?Ju7k@B1bf(3JG`(c_lQQ&#iF_0lAr?HRSWmG^Et zUqsl>loZZC3SdsbFY1rRSPvB;XBa8Q#VsiK=&8a-<#H6qA-11q_4oR*wk~!3Rm1q- z?kgu01%zx zB6GGQ>t{RUp4p-525O_d+CPM{{*qUn<<;oK$JHsP+ObrQ&{x#qUrh*&ut zv3R?d(B-;ube{1NsKC@W!eC3ZQZ=eq$7Sh_3XKnSsft&9vGaM|Y=Cx1^oN$!2~fs5 zu;x%KPsV?E0=r)Hwqf8P<)b-r2I?i~JO`+$QvV#jzcHBr#!@Yxp+?DBdk-m7grP{q z7rWlHO1gy!BG)BLK)I>P67=C+?aR%golOqE)mf4xB7p2Kq_dlvsxK0PxUNt-RWAy= zmK!15lA5(a@i;GQaBQzAylRObY@^es9l+9dFOn#RXvlr$(0ZzLiVmyNILjs#W`NP) zta_qy<3F$L3siVpjGK)swOW8?pLvK&mqB288h0a8#sct%)&MO`ib-eDe>tC$oS^TQ z^Lxl!h|>#J^tGNgoA-qaXeQJ@?9v%rm};1%8tNXVz+-P2VylfX5z9{F_@y#u9Yw?P zRm6b=BA;CR=w-O26MKi8kH&Vt^TJn>lzF6*P~?y5%zpcdER6fvto33W`J>%9Nlsov z0+SjnSqwmjUNyXll?BSIFshj*TW)d*9bx@O6MHQ$U0!X);!v&K5bc_$V{>&pscnb6 z|LJ*tTUga-_vQ|VF+I!0qD-BM=H;HSJjIK4;S&-0wukP#;3QJMf2F}**!jVH|KxA> zlvPdap9a%o9%qt(?JvTS# zLsqu4nz9?UhnM-2#oJ{SQ@6~>Lt!lqe z-G#Xw^BM4oDvthD&^qT!PT^yd)TFMs>rooagfkFZlqk2m z^W^m*D`&|Ie2gVVv{Ra7#}05=APa~Ft50gk@ILO$qs2acG`U3|)!IanMso?9-ii3F z*3640qHkq7Xk@4$CM-)J%8cny9B44z)fhBy_UoA0_d0s1T4*$8=M7f#q*jg3>?ps*uwBI zb5*UIzBCf>IRgeTDOt?$J+W5@y~;3H z>@WoD=!sRXkl_6~4{Q?R@ix8Z#1vIvFk0>0BS*}A z_l1Co1`CM7*e=cS(r|QW21ff}s9gAyq=U~7aO|-hq)E|4o@h9(>7!t;=Po%< zKW_enKNRD5x1j55?~~_6%w^Ta&@!h<&-tzLl^DI&=!-cT;vHI^xW zNY7$`KrHbRc4d85jtqL>!jA3WZ68C_XYORn#NeP-`Lk5RbtKO0r(Y7SFsHV%Cs7y% z*)QMlH4-sww7}G_N@FksEZ^oRNpg)X#1T}#!uj5NOH8VxVd$} z&E2+n2gteV9XnO-_lSob;SE_Y4)YCyxpcki{hb*+TjR6AjfiZmzvY2yzeXvSb0bnXA1_i^ zS|u)3r4Cr5mF0$2`oDnMOzW;bzr(!J|JkHzVaNDQ1a?rU^mD4{|CiMLXZ)QXJp9lY zU7&pYbPVDoJ?rMB(4G1}n>YV|$e!@9k=9QWrXZJ}#n-F_Et|0BA`|#>Y__q|QQD)i zJb*OZZ%=R3amr9`SVV*$^$dV8dIB7n)0)bsL=qHl_VDa1*vlv7N-Bh{*c=1zh3Dti zZ`lLM;-e1i_O}T775U$a{TS7-m=KZ&<&B#^^DV1QkbszRp8SW2v$s3D_6ku#Rpqne8uqc zGN>FPFut*6VfnF?*V&!xIRtccfKV`LZRb>xqLaV=mQdC?_0aie-o;$e5!b!Tjik5o z*j)z+PSQf^1caAS55oh}ESTPh=n#Ok2h>V*MS9-|en+WuEPS<-7A{cAg24B@8-c?O zzM|=g220}w+!#|#aw%s^kQUbrX?xyP8pM>O6dSbp#^4Hz#;TDCZ|@F_Enm_Iof2co zZqiXr%ZZ*~a8n*5+4eUKNToKIB|!vC0-E@$i;-%949#P4cDPTbJ{ok)l^LdJ}_zh)|}|$*;iFLOaqI zcWKuD-McbBQ%d}tMC+9IO!T{d0~0ND@l(SxYSzTJF(aStK768bMbG`Hoc+?5C@?!+ z4(xB19D*#(q`+k^?K6qUX%$qmj-2U*zf@k%$Y5;iGTALyb=%|@k*Z(EA6VAr2z*jl zhf#f=lN>i14U>2*RN~}{zawbFW#geVqw5?hRMl%iRGWrvHBZ?P6!o#r99LINYX6;} zl+`czbYgbDVT!U3-|-%KV(>_D7<4-Oo?_c=y5{zsq*$0K zgU;o%ld%Ynu5YszYI;jzt>(AqGk~k_oS(S?wyOS_PG)hyAf~}BUf%lyV|Yx;>8qDm zstI7oL6jP|zK`TVx|utb+*)x1?Lr?k+M!0$%s z0t~yYTVXb4eDchVX%%qdFViB91FhLvfu<>~Kj?=$(){BWY2Md5&k?TcFHwY_2Ik%y zB)1WcGL|S*aDfwm$6)n`%bAxvabM%mH~9Ql&s%4!FI`oa^uiu&{yG_s9k=PM|voZ2AisEodInylcz%=&MyeQ2*LAcHnrzJ$!F?^{JX>_^B=@WI%1o_v$>S%(TU zb3&a)v$?49hC-2uC~uA!N-#Z3?RwObM75g|RwkCG~$+;Apd~ za{RVzFjN5pFOaWXzjR6eo9COTHRU#jnG$~ojbs{yAq9vW*MsK-4E`3$87?xEPT!Jk zjm}jy1Q>i#(nqu-@(kb=9>_()=yN66AMookH^__q&*;Gl#GwcvI1YfT24lIvQ05)6 zreGDPfcZCSt7di>m+-9wHUJ0ywIIz~#$zwkaq#JexEvI8YRSbg<6f*{JCKY(L8QIj zB@%KVNswsGzj<&_i70Pc!^sK?z3Dtil6xrN^FvJRbQDgzT*BJgF(e6^j!KJvxO-Pr1-tYSFg;j!SYgHSj)!QF<{7j+65bkA`C-@}U z4CEPrQUcKzAneRT_(kl@WADM+Ezbbz<5#`eE#w`UFA)NqMdqF&&HhXWK=J^f3@uXE+-dJSE| z%lRJXTgrm#Lc)ir3lL(imMyEVJ|x_ufWtrY_%%o9@}4!$OttUFn>7PZbOd@CvNGM2i^}b8OM{fJP1k){ zz4O?9F$|&XSzEt~(p;O>I4D9J$xFyiXwvNe9G5&=+i5nBzkp=SZqhP?8T_ zBZAdaAN_xEa>jSv1w_fT7iO>u{RVJT_D?dnFbL?FkP|HF>Qo_&5uw z-ufTRpD#M?^LBVsPLf5E|FuV?+Uzw*N{s$brng30-1WUF$&Ar15N{;r8K8c&S6Q-S zKQ3%%lF5UVD4G^0qVIF&@OYW{@QJ1d+ShdT4*R~C*yj?VbP8gshR-e5HtH1ZZW-1a z!_ox(XKdl$oTciUlFTD!y|n!u9-BlAlG@p##pEhM1LQxVOnEXiqct?Y_}NLn;BQZB zJ4z(SRoknZH85(A7U?n(A0jx+9$*g7Vxz`4X_lwG+x5fr!rglf&`Y4VHjEAH&-G|) z-DfPH4$ex$>e_$_ihYYKY-!FmlYDJp;)LEv1XBr9+DFj{SfdoTq`|dqcUXEV>>|1{ zX?qS-9zRT4K%!M5Jj>)?33U5pQk^JFo&gP@<_#F1ElDvCFJp+w<#hHtPIRs}Ls==T zgO@9^(wr@C8o`!C>5VF#Qzx?pf-y>qZL-THc;GB}Ks#)1j5A8`V!K`2$3R*1L95`d z%hvy5{f|<6-yHcxiJg1yYOd#PYmGkI%{3-)?-h&#ELY>Y1SAbzYQ&=-cGC8Dyc*KB zt5Pj58;x(=QE&j_I2wWe5h8b=xdj71J<# zD5yMTeWym_ws)_5CFp!LwVbShN+w=dPWE!d+Ja7&JPL5V{gJlH%fz>gH%&H4zm*~n zBWQ>=xBD(Y?4|yhovnY+ZBX=TtD0x$wJXq$SDvuGsKxs}srYw{)5ii5YxGZ>ZFEI6 zSq~j~;#BNWtPv(=xN=wM`;y=dwPjA0S%$;Zb1M6g4c z8M);fq>?j73LVU?zB`B$SSXX9zQ&6!{R>(05{l2EaeQ)f*QMH`QFk1=%x&UPUai z-_9t(h6ESnPA}wEpTB+5*g7Bmf!6l#kLJ!tx}k-D7Wo$+RRK<+YV%inJvk~NW==y11171jL`=9}SKC zer-M&{n#-k^yW6+hx1W=P0g>!bAK3ZAxZA&8ZO0%R#GS_eNtOpzs1v^|I^#L5ixiywzZL)0;C3a6) zOZ*I}QwOnT+5L+VuWaT6421@^H($|3Aw=98Ms1x2ZSAB>VLBAplhGlwbS{-B>(&HU zR2xd0mbWom+h%z{sLflQ(|*5N^mqT5owDcb)wgE==3}@~1qC8A{A$2oy_ZGK@{xV4Tr4H#FL zo^2yBecJea7flb9u3%V)YXSEb6&bvWH%UyTi`DFv)dOdK_gtOI}>=Dl+g|vc3?9)ZP)6%NtG#cN;$O+ zFQWro=-4wr`WEXsyP|TlQttZq~%kE&_?_8w@boC-D;>k+dOZ{sPd?#>_bEZ z7jH`PyE{|!X&y1!5Hw*xdg?~5%Lm!%DxXUK-nrjvSUvp4d**r@UH<$66jodKITY** zJZfNI5P%!16F#lbQYl$ATNdav6ueY)0qgof*Ti&K4w;fj(D&P3LBCo$ieH2F!eqI) z+9w7lhZK;eaVNUsZ7N*0bk2*`3AR4~x>TsGG{^8tebTAyW*ktl?ge_`9w#oL*?#il?dsmHf-YWG zQOYu~&#VOu|MO5DoF>ipH<*T}oMRi|gG^KIZY_F@H0yYI0aZR%b$H1eq=EnH?P|&bJlFwOeIFP5Y|bLpv~-{H zY5ms0$W8Nz8^J@YENnYi-lPj#0u>cr|9lW8;l&#c zGqr52Z5`2g1|VF?zsgANPp@GMO(@Izj~ux7>g+_GF^Gn2K z_baGWpN)(3Z~r)EhYaSw7P6;BN~_0Hxry^n$qMG)ML|)R$1vXdmM}e;dnR}PuQ8)% zwD^t1@|&?}_xFS4A5E*mP0KH3+P&t#+D(Q^dG*d2ay;#2p6`4{iC*lSbXZ5&O`+>F z7C!@i`drTX3~@iFsUH-eIJY5eb_;F1Sp5nmzD8T zafR&sDQ_$FSz|wrFTu4|9-}Vy6jZXjW}SdBv>u!l?HoJTV{N=se9eD;)IkG@0ZTV} z`sX&dyERoOy!Hr%d5yUK-E}{wG8Ty20CP{JUpR8!1rqJ8qyV1RpV6qri=7ybJ!PwH~r zDgVz9K{}cSu|T`RAj8HWq)~e6On1${aw@LjDE;AN*JlYWsvaUz89WU}H=g`gR+dGQ;-dsQ(*gw*z^q%kc}7RQ@m4$vO79 zk8N?IKhr{~{7i?^O!X`*!1Fwxkwd^7n})6IIaMM1neCP% zR#|3)A-&!Xp1KF4C(T2$Q6M%B(|!Pt%3bj0NK}f`?(FOH92Vc?+x3tm#F1*>Y`yNh z2Xp*QZ>|X2oePEoK*-s$0ZPR$BY#y*BsY6yThlDQ5gd2_tEd#FEiD{%uUJ!Axam;3 z>L?gpN-8w@pzAc4x~AM58H8a-_9fW_OWtOpxNg}mv0a=VL$t!i)Sp4)EfPo~ zhrLUVNKu)a^7t3R5*>For}SydMKBCe?ghjO!Vdi8!j5o0F}Q&0ImQUI2$}6HI{KEY z+TGVHb4ltrf*NCOl+Pd)FOMd}S3E<6JOBK7`!7%DWvP&PQnJ<>&`zYL-POi~Pd6^? zT8!eR8njI{^qCi>ImA*mL`b|)<&Dapd!zpPddhpm|KlGlL)`%X9@;;xclZ;+wBOxa zuF1y#J}TtXABVTRTXG$or`-BXCh{B2FwbqB$t<^C6HP!4rBbu}bkW#<4%DcMQc$F%Qi2prr9(iZNdz{|V zWt7k>u(OY{kHlV$y|ymMxGuV7U$Muns>fcjB)PCE`wmf8BJzq8O*}A;SAT(MCSSL) zXxTKJ0Yzj}zfgpND3w2f{r1VSci=fg4oGg&9s*VGUja=NV{orCKH1y4!QtUsfp?9n z_eYt*80KG(?lZ7GLcg7m(~R1FVwv)k5wfr6UF9ko&~^y=q<o!R+@WB)~LV&wTw^vrw<4RO2$75)7TXZ>Z}eu1Js@*a5|N2c>|<9 z$*e9ESVPQbTB?JUl9@SdCIdxo>^M+q+=|WJoI6DWcI^kDm`t23DQD9r)2JxPJ}&*_ zf!YOX@)s_~=){jt3jT$r2*vv^I)yUqdIc>2hvYv$Cs|J4IIDZ00l~BTUvZabQzp@1 zw2*W;8vXT+9Ksg_2{oc43RVMFtZqw|Gc)u1|2fFG0;SdtkMTM9_wnU5*9nx$Yu3yw zZ@d&r2@u{_X*HV2*JeM0M+KUs^_`$;F!PZEljC^}R{*;SE|ZI-y}GF*N@ z-84rc4{TdZyt+%&O!O7ViF9GbP;&jZMCBh(Jh`MZJd!xD-mU9WES0X&35yt{UlBON zTweTk&r=<$yeVb1PD`|TMIOFk>~w0PH_fW%I>p}Xv~(*pb}*~oB)Eml&iJ@7$I_^fJSd_WQ~#~NQl*na6!N;% zyvC=DO0I6Q_Y(typ?Do5ii)XfP*NV-mbB0*z}DoF ztroDC?X}9GC{{GjCkIFVtj%;1^~-pMZ5OZUcN2`=x!r0rTYmLAC#wRd__Wc_0M6f0 zneGq2*RbOXWzxj#L0loOrv6w&PwY>AJJ;yX05NGUQ{snt>-vtq(-A)ao%lnV=ysmh zmW)LQ?ydfm)}MHw)0OC#5YA;_*xF73oDX zSm%2xnS3lJ(tRhc{tTe#b=dpATgbbM8q26Y^}bLGUtbr+)xwfm^XG(WByOrRXiwXz z%pR1U@N{;ykaPgOxEaRz1qg<__r9pwj>w939muE$_MS>{)11jN^cSz*j6aJ03)#7% z=0~y!Ic1aR62Jya1`>@_-3=(#!1*#sAcxx5cGk-pAV-9?AnAbQx5IKLxjSRkIURDJ zH4k&kA)zSUC42=v;1w?W`84$%=JFqi_dCUyyI;Br-#|Wlt^Y-7Vsbz>GRSWHf-AVI zNW1BdIawGUSyf(K=HOLhu5<~Gv5T#raOkq0&N4NJOq6D<`>?9hJp;509a|;b#tMCf zVe@gQYo1S>eP=P6D%;Da9dTWoXe~*r!z1^l#!$3XBAXuRDdFm6ST3ta@Dny=+TC>OOUxr9&7Vl?7YGOSfkG zywn>rKs59w-Dd#RjnC+zMS}MY$5x;2J z{OY#32V$HP^5r8LFDKV$uFnK$OQ&2CJKqyL*;}VyB)3yUU^h1+wz&Hey4NiMO)Eho zM}k`eYTyN^UFz8^mNIGlK8moO@6~clJ7sX$vW*iX`Ha%&NYLSH?2zx2Pj5G)sIIf% zCRw$ar@L5a0Cf9`Y+1YGk8_5VO=}`e&T;zKQup1rM2Sqh3mJ#5IE3iLnQ>GM=&2NL zzvs{9&gl+2W8YKd_Ssf{o=q##ryY|j{AZyS=q@;}+`eY&S7PU`2Xi7jQFKsa_+vWy z38#OFi01=5cr$H9_P)IE71iEZ9-pK@Un>ZgOSfjzqP;tMoN|{2UNSU{D$#t99K2Pf z{pdUXC3fO5Pc;(sVbeCV5!=*R$zwThi`$mvg7Fd&q}7)!8V$;m|8+2>@w08*I6=%^ zMt*3qF}!r^Hl*C|sm}fIg#g!Vr^!eGBO__eR3%JV?m+|{av?!}+&E(5Z-DGD5X1D-+545nhpNh; z_Rx$44+PYYdx48qvb7uI_c4D)e=YoB9w+jB*mU?*B@62lZXAk`Stqf25fTGWd9?Uj zGo2{yXbCcC9;%|L_~VWmq~;3Jh&pB|?ZAVcl!jIk?8d*!)n_xBE*}@&7Ipp+-`@`F zh^rS(HP9V*RA$M^y8n_v*CvL&(?-WszTQWLXSz$u#Wdf!h1m;I2sJ!XZR!WAwX&>nEBhQ$n;Ocsl=HNC3Ey;yh1ONi6tGRz)%0;c_y2 z@Mk90jVY`)_;)2__9pD6NmL!|AytJO?N!8Yl*tTj^D20 zA~#Q*li(#)<wGAH*S|LYmD1LLzI7PFF!cV|Geu;bRZ$OrjpFh;t> zwkM;93-u^&qJuUa4qOK%7MfwHgon)NIq@hX24i#c;hK5sbLl&B$SJ9=zpLa%4+_Hq z*+C*%l4%PKYVNE`A5CZ+JhhkJrO#$y2m=%SBOJ=5Rf@NHpW^?`V7oGDY>G4aJKMYo zrBHUaL)| z^(J67e2}BEUUgnrqy8Bkx!*mX_frw%M;vDbL4o3>nw~1z*+-3md0#`RPmC$HoxhCS z*90O`XW}a9_e6$FP-b=*c)doKUe37R*9ecfBJoqTi95J-xWnBZfL=7WXvq?fJHfsK}WZ?Ug zBjwsAa^GQL?R=ObxJ2=1HFeZkTzU{NDfLbJ60L&Pv55LWC*$>Dj&4tarR24p(CKY{ zDqJZ4TKI2G=~GraoA`G{cqHZ|O&rC@x(g?-N7bm?4@vn~pBM(QN`H8<4!xbL3W=+n zjdsMy-I!K6Ho#D5Kc>2VA-;L%-~CoXpU>R|dtyWE!&wBmMMlbLPM`nW!Ll`q4=>}= z-zC&J%OFj{nl=YI(ko>5Hzv_d%%pJ)x?=?)N_4A;U|Quj^s%|tPOtGANOx$-Sei66 zf78ERtAf5U-+Dd4#15}9h=~XFi@$#5W~)}e!b`-uD`@QIq;56K-^s^h$Eous4U^71 zGykV81pyzA?TqrWmdu%9u+-ge-T>L{4>Wyk@bTd{Zu3b{(WEyD9sW9flSe3)EJ(!z zVcXba*qo>(nAh}g`$r*uXj05fD#0pu7CqQEW!OSsyBjHFs*Q5GCcj@8wLVy;u!OpFN*@7OnwHIyH70H zSdtisV)c*w=($GXB={FF~VtF1xmdv=!_Ps|4-gSZ$*K6N;_EpRJjd5Mr?%#>E~ ze%IBY_IBMWZG!16Go7X! zG%hU@TG4Tk@K!aXkM6kJ*Jl7|DXs}k{ZT`WPBuKZB^}<)9*07pw@1)|vyGTrCXZ$e z1hc-@*I<$peg{=GF_Dhp<%hT_)=nN5;q>s9urflb_x+MhnNJDJ&Z!e5Z7bv@NX(Q5*dqvgpt@^bRI-#WMHVl#Dn}*nA3iftG>vM*Hkw= zF{(8Y#94PaB{0je>N(|gd=!@(m3Xejm&n;3evkA$7A%Oz<>D zOula4mfY6~=m}W2bmzSJqKzqF1!d_k1ZJkga>kOGYJn7y8OtS{iVi>e+x6lgUvqtT zM1+_LRjj0E8Fbmc8tS4z)cqU`NDnk#=MSJ-5Ke6Cz;b~u444A%^jxVFy!5wkA z7`1E-%5v7aOOo>RYwgu&C#9;axlX4(0FaWRJ<()N8Nl1Jo0ZMZIffkE5liM*&w$kf z$;9|-Q!}OR%|aiU!7jH0&%4!@S+pj*By(V*bHrA~!ZQH4-^JC*LAk6WEx&hrAmOOr zPE@WZiscGign;3BF!E;r0Vp<_=W$cC9sc)pX0YQ!iw5mJ_7Wbh({V#^wxe-qqKx2l z+cSVMazN6eoZisUeJpY1sAB1=rjpr3p?duZ+sME5N>jDZ>vce(8rlu#eRN6TZOAH* zb7C12r#y}%f{?A~th(`vkSIKcH7#_&oo=Hd7b&=I#@hd1Sk*HO4D^;50j|^&H>cOE zP;&qjQo16GP*LwVfqrz2>Nkk8zy4V)OMF+V6c$_8O!OVmj_Q#a9{2{#NqtT0!yQ{E z6pn*@iIl+!w%b?5(M}*Q4N32xVO0a_U5sG12a#HvHbGSm|8^nje*|w>TMIh%*h2G!#bggfOD?3%| zG_BnD(Y_g(D}P)pKC-P2mqU@U zTOvOzyzH?wJ7A8~jEPA83|a?9g?|_`$u6M{oPe48ea#Iodfl+hfv|Gvg)UZx!!iU_ z*=-Jh^-{lYPR(mP*-}m9FrNYKDp@32AdS?==JbtN2O;QyYlqb}-&uCre@@4_{9Vxl zNb5&Dr4J`eP_R|Ez}{`!jOZ1ikW*|1y)*1kBe3(uig1)O^LvdL5-DVIp;Sa(O$Ko@ zG)fa^Cm-4=%L~y+Ju$rcX;p<7ihChr`Mtd*n>#%^v1PUhDP!(AW6qKlCi=+~xwrB; z*(&+;z{7)GUAt>PSdmktG%G2TK(D!FwP4aN^Sz7W!9j6-E=8J7w1OSZq`#cf>zvFy z^F*uj&=K-$rq-%-;zIO@QG!NIkBI++t@jLu!;RjC7ZE`aED565T`dwN2+_;x%i3rW zL9nb|qDSw&uU;0rdascpdWp8$D$zS3N|f;Y{_{TXyw5xHe7w)xb3UB8=gf7Txz2Sf zMZJr=17HNKz4Od6yaw9(UFA7qX2*7P+!lZzEy5y{9}Bj0OeN3V|19J2@DSU0Pfx1u z#&FFuIW#;q{px}-PKLQ{*TATTalEMU4Z6y(m(fn`%A|mIt+}%BBwAqOM|P9?t3o@? z|DL0QvM!izM$g8U+jw?p&cK7W38M3L5*B^qZY+EnC$RlUZilfTbX>P|x(qY*DfMx2 zX;uoB*eAC!F~${sPorMXRPcqa_82CZm+WjVGND1!dg|mO-2ZzczPO0|{}XM? zGt;<`7i$%J{_QnrHrD>v7uXGnbo8Kc-E%2J!xIjq;$kH5;FcO;m=?{Qu9pQn#F7RyH38z zDNN3sQT4&uLW+L;*d>0jl_Oy}MBfprfNYVFa8)3N7LmXRW?(H(&Oe)5o@@$|z3BEs zbl$hZcu7zTnQX=lsTES3mR==Ulayxtw9@JgQqI$`IUH{7?PGfxXive_56COCDEC%h zsD9ZjQ$+`+Sm~4;#wQA-|NiF6B!u4CEB*FB20ycf-8W!b$9`UK`Of6ZkMqz-6xRDa zEOse}E@_^zSK~>$vgH%2%Uy{xwS4UG0flyTV{eo8XdObGL{DF(dfj-WC2dRyeX)NK zFE$p)B699rEphvYy9R-rtu_sNM4&(zko2V=afQo%({}iNZ8|THOz}tC?xM5*khU9= z<{b#4sgYuiX-w0T>8|dc(KmW_SU3>pj5IDI2*%gc(Ykj#_UAH;j1A~=(e9XOmxJ4? ze0@q6awIrH(d>B3Pfc$(DDx9mN=c&qfMZ?Huj@XC&RBE_a?e~# zIWNvJ%F`tSLSQcCHMCxtd9>Wqv1%+Uug=5OY^uS(t2N(;f|^vK|ZjuC>Qs! zd&g74w;QNCY2D5In@xJ3P_Qc5VO_A|yPK1%5LcJ;e zDf$zWGAYU>{M48=V6euKB@!@sj0v)HCC3&f=}K*vN0g`k!cU(}gmP8&J%G?crgEE0 zix^Yn63vut>vG;t_KB;UNbgxJ#2fooIg^r{Z~a4%%&b(*ve`4~<5USI7%8J=+sqsp zCr#tQ__z%J~};!E?et>2!f__y~WprX(yhY85m)%L+eOWxE6PCfC*LN*R{@vcj7< zPGL=(m6XI=uvvf1xeq;uXrJnT4c7rW|7-g3^5#vouS8C;F_7xWu^wL>V+10m;Fbq5 zF`m+V6WPY4zchRcnz`$(xy@H9m?L2`>2sASm!^`;;D zGuL+Y!}W*spfNp4X=$`D+P3j-tLamdwx>@-4T;FV*5|z&!1w*G^e+>pxdW76^RWy| z`#Eg|Xkdv2pXUzf$)?gt+g3X~RMoEFyG@Slkx1O?OdMjP!OOyfEOCo1(|3GC{{vi+g(x3~5irh4CG z1=dy{6aJkO_Z@SaR4HG&DiGB*dQGQhNkBzqOhjIJ;DXajjxWpyt z3g--d1r+zamh!lqG=wgs1+bI;^R4|HpDpUGQ@=LzMr5lw-An_6#IU8ZE6dP>&bSUm zr@WX_?IW9uMDYADwJI6W6@+akTIGrEz4P-^^!+euK(2EblN?cNP}|v*zy2@V)V0RF zXo&6{ST>dGKb*^cqVf&S2zq3q&RISV6J2}CIUwn3`T9f82N{Y%T4w=40dC%@Dmk(M zpPz5t`1vR#464Es2)c5JAjD(BPXq9m86w%;BOBsOh`RHbnI{d^ntgDDs@DXa@#!=# z=b?prHird2><&=WZ96h4De=0=rG;V9=Ig&n2_x|tQ|mL17*${X1GpU%wVs^62Y2$@ z#TPE+*f5HNxW?Q_xDDl>nBE+c7}F5z6^R8U!J-Xb#TNdjnUpf!Ue(2r@kL{KC^VMAReBqdqIVg+E1g-=G>`F|dA>|%xEGqK z-umE36yv!;liE3OvKC5Sxi;H@|w!^%fWp z5lPPB*1rQZbL^VRgHf~#dUJ0NxZ`0FTWi|#$Rfy7Kz zTzdt-y(^?MN3vV<>pAZN>f&uVy!G?sbO*9hx@PgkwiLhD4UmG`;<`VrLnRX5UkaU!lRp&eW8{F~|dM?Rk#X#$j#?8;FS2#%ndAlJ# z#9ReYYFvaYl60a_!2yhpcAgd9$VQ!-rE(P)rTrx1Td7J3ZDRNpVI%D5w&-ApOJYfl zyzKAUbTqE-9r*B;^>>i+2Zt7}0Sgeg3d;AIHM|tRV4zzGJnt~AKbbSNfwgz2-d<=8 z8&t5vfYO3sm+eEAr2^iQDPBlbHsvJD%$pE?ehKDjtX?bAk{0yDA#2g-C>liNw5Fyx z+S+AgP{aS&p$gRwwpVsSe|>s8sQAgPoG>2w^uVQQP{IF9NNDi@?zGlblKnyn*DF}f zh#4FhGBm9UZNARKmq4RI2U4-Wf7Q=?OrF_JI`Mt+oXH@Z#QV)5a4DO1r|h6qhD;NN zB!D?R3ljOXhbVU}YvS71`Q_n3OlmA`K9RMX-wRsv>th%YAzpZ4f}MZMo*}v-A6mla zCEf^kN3klFmH5uv(wIGrPkqTqxuT~z!)Vr8vmCF>!aPhgRdRL9ggBqjLmM!4xIv5F zWL)P-fS`iOOQ9!t6MpY9`ru|W<_e@)i5Iuxd&>EGLHp?7EqYr$?-{)E1QRty6E(Le zB-fiT_YRO=lE5dY-p01}G&gWnv%M$7;&EBsesCY#Yh*{Sx#h@_0>QRC6EHWSd?J@1 z4QE%VnHz+z0R*RAb`0eF%yqS?&r|DP8mJL^o_V01i1igHLJKy}10ecPEx;ZoP+>*k z545;jROE$~LJ(X7%Dz|)ijmzNL5aryst6e{Le>&+6pBfoP8OoMfL}YFhidu@ykfC# z;v=CQcNZZe92BARp zD+LZ{W-Jz*GceSMXN57fHPOzTQPYbVgUSFR>{ zh(DexZO4kypflzgtmtEM`qkT1n)|JHJbTJR)$J&WK}{g(PlbAVrHAD4m`75%&}ES= zPrp8~B%iOf>%ZvR*a^i&yNCRnr)ib*&W_HgjctpY9pZ4n8A=;v=bIPTc_&|DB&^q1 zJGISwFGTjOUs<}cwl)vgoYu^1q)`Y2+siBNiqUTloUc~V5zEk8@rUu`6B9QzD)yV&!{PX3_$MogXfu+KJNgL^_I zPrZpb`jU53=<&KN*-zn*k2&8@x(HM}_11bTpr7d~z0XHOP!zr+I;UPP8)4#rT;l

    nEedrANCC*f}(WmqqvD`BNFhVV0e@-J!0U_MWW|^`udbBSI~A z`)A>?vEK}H@VdV;ru-17F)({eF=A?hY-(|I{5IT5#IM!R31WrI0OXZ;9I#xt?LE9^Ec{#>K4;1MTdJjfW2BrgCC8f3~EP z;67)(edYKkU?%UK$Sb##_lz&{US^#dbB~IpIhaHj=nL~*a1o8cP4580!G2U{944K- z91i=ZWjOeeU?;f4MH(u}8f#VYV%58E@x7(Gka`Pype`l49S1-z@9b5WQgJ(!S)RI< zuji=s`m2Q*!|vCvH&nXOTnmHJ8tpIl%mgSBVehh}S3I#cNmBTJ@%8Je4<{3{5vqUk zBg_<|!orLOkNFXW*|F;4Gt)oP2ThSmx>rFMkuEG?oce;wSr>1}f^zbP-T_!#70($+ zn-_i_I-EpmhL5hW9q5_K<>8IPghu~37pLZCT6`x*TE86n(%J;MntG(ZUtT3XF1n*E zcAUN|#a4F{hB^LWWT2;ePjf?QQKU=vyd)m#PCX35FkE`p|DP0+#CtE@CX=o`GlMXFrrK5gu8KZd|sY#Azp zyS1iD>5M7&)e6=}9M%Y9dcK*ezOHGwqI3uN4fX|l{0>ZgnY(9JE_&UU9z{FP7(qF- zr{9yO*u3$CURoxLy+?LPX0S%50KQhunf<2&*442~B zCBKLXxkU|@U1W0yrjLTm#(QG&uR{_9-}zi7{An8^tJiq-x0?I8u0fi_&5*jx*Mq7q zsIHvR5?>#oJnVwHtuM_=)4dX8OQ(C|gAi;iigtvscPP`^Xa@iJk)*xWI`uHV2s=Y| zWt;Z**u1S_N$~7~Um-PjbQR_E6&K2$Gyb(){F++VF?p?3S_7fWlGk3?isa%qkISBl z1hz!zh*WC^W4W!!?f~JrasN{NV!4`3M#bAB;Zx7)RI1hYG23)geq#3F>B)NU-KOz{ zHO_?zYz_)@89Z*ta|s#d!8hAzqORhtUwEM-I;UQ4o|{-x{xYqLK<>`!EK z{#!ZYy=i=7r+o!7O$KOk$euFrw^8n*_*NLGDVkEb*%O{}56UpNIv)W^?@ z$uIjW@+vzYme+s#53eb_0UNGl_nlYvk|WG5-Kd%i-!|=3Jk{*5E-G5rbqenyRn0|` zH-FHEoPRfNE#PnUN!@qvYyg2}#e}v&zY6{3;U#pdIV{-al>#*lTXKW6mj&U;W-cb& zq{K?MifUYWk-3dzDMbNmo#we#J+JZm@uCwu^M7y-k92e&)ci#tyPn%gk%4`y&}DhUZGO9ep?{&HA=EWN_JH6ouLfHZfMoR@;3Z2HcSdp?U$1vWm|Lb5?85EBL<>> zO>uHxp=N1wj&QBMVIk1@n4_#_LW|@#F0hP$6#a&DB|$|@=-XPCU?1NHGnjv7Tf`RFHleX}z@$>IWCE6c?xZ1*~BeuL_T zwdb6DkW&gXnIi|C5?T=h{Cj}C9?FOhvvr{tkt}n@=%!1$w~ltr&zV#tYV;F>^~9L* zXH$GaZ@2V}%#D8ZM-fIHj5POFWhr_5usI*!>_b}DO|}B4v}8Vhf1utCVd?&@dqOHb zbBqg^=Z|0n@sf{xSX;U=;9ZRU&D$~s&P&CmRjz|g^JMCG6Kf;}W_D`SP%AK?fAg*! zVIz}WL}zQ+0g{QrDa$P()*eYs7$Qj4?@ktUQk@?xv-LKbd80ql z9%uaVl~eGlp0Vp4V0LJa6>~zMcL#76_BR%beP@*AxSpch_@7-{CiLhIvy{>UFU_bvPiyxw@R=F6w5&2?Y;poAK|<=8Q3k!U~SnVyG-l!CLc2mAk_2bRCs zP7?O*jbpsf8?W;1>((hL*bR`9Vpm2zm_Z@`yEr}X!tJef&%>xW#o&McVst-0FXn${ z&(r^Zp9T;44^jW`=>ap1i?wXvR%xP+rV3+j;(D;S1Aq#xjh+{;hDoa1&hv79zL4H~ zgug;rp4|kiTxGFidHU)_&4tqxI78}v$aaC@7;zUAPPN^B(h#4n55dbty%ZRDC;|Re zsAD1eAI0vfQjjs&^BjY5i4R>!R`ytMyYjqDpI4*f)_{>*?!r2KmjIeeW|3<`GO1@n zGz@hLy|o{~MGh{ym_ljT#*u=o)Pxmy?8)O-t94nXmy1(ro-cVR?f@kpzfWF=-qY?R z*2vQPY`VyHOJq2^?OA8#O?On`4DG>-)XjfrxxXPI@-)$Qe%cmwbIa&H$Y`6{((D@^ zW&A4h1f?;yUZkk;8j_?8|DzpU_Dcr#9js@G@MYCZ|Q@&q<1CizG$;C z4edWk5O(SLR{Ol3FbKMhNWNcerOvC&A1_Bh49k=G!L{j~mNz_JBa*?j>gA5tHo!$4CxCX5Yb-Yf!G2fA`^L8br z^`@gfvYRRPkRQ1GNljwRHku5eKvmPHBfAYqOXwrz`pd@|{Ac(m)omDikhoxQh627l zrXVG?6K*Y%2VPPrh3||8ely+_V2=vUHRwoeeeJyN#RS2-olPMig~W@=PiMVIKMKJ@ z<`1La<1$oDgnPkY4SU^P)Gg|2Qg`E>o0>jSNL-EiS6fKBMaDkQ$-mNT0v9f}d^;tU zKHBXPa4P4Uy%;Z%y|TIG=Z3aWa(+w!Ud=+M(+i^LnycZ4D0&npu~!OB`UJKsZ&Fj_ z($!ZeCvFI=QK6-RFhY@J^ZjQS8TZ-~phO=up3{Tt#^0SBo9)JzRwRxV07&Qo$24Ct zR=f^g^SqHAzXQCn(hM8+7Hg0**H!&fj({~16rq(Vggt0!Ir`D-@W&eiZk;b{&|HzNmzuuBs+{V?D2deotykGtLcM|!WpbFwSPeG?_JiwK-cw21tj1t>7Nd{SS zAd`i2AosXY6|AglX4O^~KcZV}40__JcD$dsBOI4#ORQUwz0V zEdI@}GN!LCgDuhTk*qta+B+AyoVT2Z9>ycur>v4ETJlBJ&3zfD4s5FB$NVAk6~3Db zP$uj!&VXdkS4Y`LEo_ zy46WG&)sh^$}EtnI$ZntUtvlaS@q-jchoM&m>rz#8QK=<$vLc&@NdN6is_e@l*i^2 z{eCXTSG%L~IdAo@9bLAzH;89)NDn+mtX=92rVKL{GR#QYAXTJXiR*BuoH2JT2?8c! zGtBX|f7hAu%f2+Egr5!5O7%yBjxP_5O4L2S$uenm`1P`6x93Lw$=6TnxGm!>FNc2a zikcMuvHyn>Yn3HalpCDroX_Yg5RxcF|M2Y{z_xv*xwogAbK?US+K}8V4RWM}5kh&s z)TMax+DNwm@2}9+`A9r>u(s#>UKzaJ?c_^hvkCnR-q21jN~4dn{tS+e?HqqH2XrRN zN?s-cr;HPJ>XV5<=HZyt7TQ;mLoPCNEs@V<=w2ZyW#%X3P2%-yc_&YBB@fxBU<#?& z&H$djLH(Wovghk^%ozjj0G>I`cK{?^Gj6{zE&p$V03l7#jINtx=6-PvRF6DZnef{4 zIHFsaHemWG)9ZmRe9twI4LpsC8W#)gb`H(o6cCxU#xMhPNM^V$xt2NTFZ|8$lJRPJN;;H$kg@I>w+oRps1Hyg zD2kj+dl!&e{z$%jYRvYLRBC9tmR-FT0yGJ6E6;&YXS?)xmXFQt_sMeIQ;5r2MO}kuOUxCTi7C1G4&bB7 zEzXnqcGv&tUB@YjG(Uc^$!C{&kJK^OU=Mx(UGlpF&<4(AOusZ#UOALPBA?pbd=eGv zAx_O8y6v0PZ4c7XQYH9?!m!o6`>D<{8G7%OcWSrUFu9}=vlscX=GBJpcad9}x(bf= z&b%W$J5uyfqTPbQNiyhik2`OD%<|K7BE?^o2USncXTGV=9X&cxC0Vz8vhh&w=Is@u z;{Fd0ttz3~dN~(zTMU|kg^y3_7$*$FXHW>%Bo5J{le@0c+1=E8zn{fKN*_Y z#zIK0SY?<@7dka_6ypEr@TVYJIe7!%zR(bd-B2HjSu|7bf9uNKNBzs1yHd*)hqu zS80c%A9LTL`7!+3X%Tquy0G3?0W-Bn-rq11mdc!-LKnM)Xczf$W6#2=`Kpnukga`% zsr>A0MmU9QFT#G@F09ZT|t!7olP;f0!tNPRP?O4_JrPrb^XF z6^9v3gB%!a|9hf}&22=@aQa*j5xzfz&$ImKkYT_8CMLx0U@kNrv%1O(?u`L~7MAoc1 zq)&JO7wVP>R_YT*^D?lXPNd!Xb^Tx|W`^*A^Q3=db#wZEDC>!FM=J?h+5UHB+;a}4 zH}0O*xfLV`WT}iQ(YS^1{#P;Rb$qF*!u^%^1-FceTZagA6gokdMeXF^;_aW)f|{CD zyK?TUam4!wKMykM%{N0Swyy3Tddgh;b~_l)6xzb=GK=NL#PWxGf3_3Xy>h^OJWae~ zyPFTT8%I83bz*t6U5J}tU^CJx(B4j2E;peOvD)-BT0t;i@%Aiaiuv0ez_C#6Utf{p zz61i6&|M8znU7xlN7(|Lh;jz8+u;jgQgJYIMFC3}AS`f?L4>`ugggFSqeXIoVn#Sb ze^jt~XSl6RF_%og`E*iT62%C7vuA`-`#7$5lfHDVlSU7b_L*7R^{8m>v~RB>7xL$e1XJ>JIjs?zo5JL~V>oAmE(td**G{Su>z7o4Y; zOrEWguz&CLOmOo37neq1D~;Hp9hYf!X}~Ih&Q=AeQ0PN61xW8V7$H7&=mfzFfu?kTk0*)3>e)QP9qPpfD67Z;D_Ai%}?^}c)Tm}X2 zU}xVVGbz9N>~mE1K?(TKe&*w@Yq z8kJEb-{u4>)D z4!m>4eT+AZ4L&)oKDP$-ku;Y`$RC7;D7IB6c+kiQ>iY`y&O|~U4WA1=HzF05W)9d> z2h+_dR-@(Fc$IL&j<$N=Yru+)Z+FBnXBb=<;4fC@2QdHNf{H&z%>v?)1_RRFyE$D@ z2m|84Yy)povo9`68m>LLDmvzMwAZF~zdl+~DE28~rVBZq99&ZR^>CU}BXVP+Zf4Gd z-*nRy1f_WZWMc%e0#R`v{yZ_HZfob?vk&2B^f1?wd}vO8d*r8sSY75hNc!6_$Pa6y z`8$(28~v_Bq%0fD49SBi?h6DhO+=~h=2};se2~!HQMP8TOutHaL1ld+xp@@yba923 z+w|ofAgz*3Idv`OhZ`Tp_y8XHDQ{*?ApUa)rlCE+(%(s-K3I#avZGII%}t^7#(z*! z6}X<{=!uE8>Vq-I7cUGQq$or@s)|;Vp)B5XPFj3Xig_cXDi&J+AFOW7c|m_dHo`Bv znnmr9<$-27)CeM zYA`uj5Pi1T;*U?pr&>u0@tsE-KomR+?-!rU7&g6|Q$@56|IynR9(Py$u`E{lZrx>> z2P2Uq_3NSYekl4!eFW`1w4fcwg{%zlikrNtv)!m)Gp&8*qmLkC9Wtjl0M$XZgb1w6 zYNISK9^V8=Ucm41?+QJqg__VU_}I2ya0tofpdOXDz7=N5+Eo(e&+^Rf%(fh7d6%eUK7o!pokF?Kx5D7=O}2(dfyy0j|7g{bhx{i`TwCU-@-;T@#8-&Hg{ z#*u5%MeAiBO_5X~kcly*Oq^$a6^QRO&FS=B-oI!bzJ9n|xC#5xOxNNXNTHHPi+ek} zL}I)CceqaR!};@AyYcmc!A5ZM)x-z6kUHwZ#JUf(_?~UlW}DL>9_ke&=Ts z`G#f4bs9plxJwBF!L%i0oL94|TNRrDRbGBeYYi-K@O}Rj__m-VIT-~!W6W)URN`|L zmB>bPHcE47P`g#4C^W|?8Tbn`lNL0XLUl*VYf3H@uvcafgmcvH)6571^_C0IoRnrO zH4>r{A;_vCf^Sn-%&IoK+M)Dl;C~3KOs{x1*@t7RP_|t{Jd$H@+dcG=C|>*i_9wm$ zXnv58K>v`SdiW}{#h4d4(!#QA*tS@zKw05Rqx7O;hTqrP0<=Rd_16;^{+Grwn z87yW)?nSKrLcASY;QTn`&Ed=0_RZ(S-;a(2YDY*KC*@&U((NdO6@5s1N!MUU(U+E3 z8^&6TlmpMqxOy0~?JT!UcWv1ig|RA;XJuywEK!KS`eVz!+feaCnF9%$_vwuVD9)=~ zJRCM2t)bB5^*rPQXe7ocQ=y9XrHApqg8}_j+VB~_mu^<(uL}=e)CZyKwp;6mL zDaxP5+7$pB*YNKF1#D>PT+dh0Nc&6=(JC1Weg1p7StjUw$nQ{(udH)Q-!9~NQS)+_ z`2nYUGQZyMaWfZR-&`}C)xo9J`RM$3QP!o~A!wj;G6=Hf7eN_>?927Y_~MEmtyVCz zmRARXiDs%3n0t&6yN#tMIVg?R^H!litFdF#)%B=3MXE*CSiVo_ns?%8`&Qg#Lu{O( z&#Tn&1W*xp{i)Olg~CwGeVG+yY2`HJ6tHonKSZWPCR^_ac`D`milh!CcnnGy`7Yp0BDJR^Jd=@&u+f*~w+nW?Ve z^dK8sAZx5pj9&oGBjG$SSgkHPz?|8{m}Z8MUS;*!rX>2+<<+#%-(jXbQp$h@cK_~i zaxI<9-91TG%mddE=gZGbMcs=Av5xB<%2$)l3LOvOW9pY)x_5w}X~XYk3FK(2f@M-x zVvEbc3EtTQ23k@wigIE;s&#h**6;0|BZ`J3#K~)L>9=oS7FI_PkMM2BA8;@Z2DfyVccZ_5_Ci1!69+*5tN$rUD^8J%|NXhL_CQp9w@^T{i`+Rl<51NOf zKM#~`e1{xXc6ZaP>t2&N3Zt{Ukz40g&>FT!6QC0)=GLb*B0;3Rvl4j+c%3w_fO{d= z-RZyDh{ouznI3ALCNRHlPc+DFFKw@lN>2(%!u}i**FJ*UK5mdKREHEEFF)zrrJBHv z`g69JD-ebSc0-wy+1tsu@yA-dH6Hp39yR^Lr_`LBaZ&R*@Eg7@_mG_hVh+g}&gh_E@fB8NA&tmMA?=%N5O`Ed)`VGUx7s@kF6~8nvlajOMMUBvntJsU-p7Chc zH5>d}`rQ$HaYQLy_nLiDJ?RQ05MxGYV=5_3RUgbL{?qFuG&EW5rg0z_v%SydWzG85 zyT4;0`{PwRH>~V(_f?C)eyM7dmj~d3q*72l$BOel3H+P!GRNOTH~Jj#femu+6d$Aywd}L* zON}n>G+?8Vz^_#7qw8k*z;!urj*(IONZ_%iCRMMH?of{1fVjsTTXnR44ViGudair| z%byu#5AS|<$2gqnu;RBN1KYU%&c*aHJ2rx5fBR5I%qY7& z6)n)ln=MAbjfLX>Myv`wcKXV(h$DTzrEM$C0WCLJ;e4-Nvo6}x!MG^RBkaVLhU3r5 z3!H_=x;l{i3fwLcJ$PeXXbkU|(LLdbzKDM*)wGM@j{CLPgT8{p($^2+GgT+N>4x)p zu|r=7bgMfM$YBq%FAN*5@0Hx}&IX4*tUq;$v|z*YVXs%*IzkMj8R3Q^j98f%-p zW_OU8zNJ#$XI0R2mHQtv1N%h~G3tS^IBQ-oxpE%rL88xtXXtA32P$eI&=X4EI7O~Tb4o;dVA2vaN3cSm~M>4gw0(uqdVe8 z{;S357^d*G7QMbcWcTKay@%=G%YW(*5IQ|=Bs2ph4_r0^=AHkEV{j3S0A#u5Q`je-eFX$>klGPlLIda~IO+g}i9=HF2w>g)nkKJ(1=Es6NZmwA zO%&MrZT18S+B=eoRc_mZKK40|3G&dNEKkn+ly}gLF1{;b9x>b2nV|0~p2SSOw4S#$ z*xLiF7-DXRTQkIyc%*k+YE@Pvbi}pXjb^@^;8ab2ddAQ{v^NnZe%X%s`hdB>j)ofM z71<(&ZIl&x@ATf!cw^OG8IxuF#dRVF$o`2;{|*q3o>x|x!VWJ^U%GFno)}TD6=}}# zQxp~y83u4__S=S1lYlckHoen3l027{pND-~Be_hbo6%);YclPNqe66KE+!Cdf1Rm#D*zl*3#@D%1$b((an4th{9weB={3h;qF#vNMg!Zsqo9V#R0>pDFfFD`F=Lg%0VYEf zn%*$wawilYES^6lni?EFrqW_x^o3b~Z^A?6k)835qlca(PEkCd@MP~sH0^1zt=>JV zl>(Z)(cQqcSiQ(&6{E!B`A>bg`%)NqsuUwDWLHUR21aqpZ`vY|7JzE1QWl5~&$=I_ z#-vM7#>(vuJ*e_6e&rpx@WTGo*Hrx4_#W8)Hcku&;X0kQd;csi{g+5PCNRUBhoWe7 z`N_KzX$SFN)SjpWG&E1U&;3kIN{$XJDt;y%jV(!OTl(z2!{z~9pt9Qk?tIyXKBn(| zOaE7ZTW9G=h-&+L`0}2I)zzd`f_`~jbSh@^bnaN&Vfk5vNaoA%XX9bvYm5C&eMC*J zxft@)iynLFqcMa&Atoqj%SCTOf4)F|!B#R2%D_##JCnTiqJWbw%s+D@?kre9MIRIT zw0cl5&69W2uB0z6!E+apm(QUno>B;>ICN_Kd;R_J*!L@qhND+;O1K`vUZ9Q=vchExoSWn_MiNd$#!(}zPD;E|BgHJNbq6!7GuWwa{}Pozlm=+AxNSpt zO#1CYF+B!elE16VlpigmpU9a>|2F@6t8`K(+w#UG=6P6Q1YqFAgUeUM_@9Bg8G#x9 zWNG2%pCn_|V_h>>_4&th52Z80?f}-pPnkyk%#8C2x-WeErx-ZQmwgmRcO5`HA~u!#c;!0h|WJPNvKoKo~S zTT(N2*3wRFx4MX?e^E+sNws+wMaorHE!U)dptc>J{VHeNRy;2;IDK((In*(r*4PU4ljG+!Ec)=KkHPYu7~5G zu8uZpHm=_h_vDH6B8tt3M0mBZ;eW(MK}pAdWqq0P+O4gNO?`1fIV z*yupDo-qAFsZ`AKHBbOv-$c%(e_eX2#7VDiT9Lvxy`|&Sv2TwKxL;!(!sLbAHL*3e zg^N6o>3ZSrIr%Sow1cCtPXlgHwR&;LMd+K${M9q$HfhpKXo>gYO*WQDX+|AF^zKf<4kOKJ^QBeQ>`x zWWS@x`IxHbvR>)tr#+JXHuCq55wNn}6^;1+nIZRS;{TPQ8}@zuvF4t*GGn-@FK41h z_u%~OnkKS9ziCajagMWy3+`W(%E-m&D88&PO;+(hLI9`BKO`i_@$_2^>t zd?%4jLk*8R&;{GY>s$nBW*}P;ize~(?O<9Md}BuMv%GQ~{U&@Do6l<#BrG*(b_a-5 zTXw;;ZZosv97JZ%>(aol>)sxUdm(2&OqHXLs>q>g_X3aRmld5(>tNm32*0lvNHXI- zVMwl^E3h8@svfP6hIO@dsc8Ka)iZYl6eS(Cu+x8ISJ#7lS0_2aSv|ij&uoghsmPkC zoPXgRnr5oIK3nd7JcD# z!ug$1jT^tq{oDZw5`t6v)M^GFO|QK>Ka{|imf(jPD~Krjs@+}59Zeihd{y6(i!LsGZ@o5+7-DaTL&14$Nm)*r-f4 zCgHSX)x8(vY+9I8@lZit7&fh%C0nT!tL;)6y$HOo!=w`#9u$zEMB$ zd8^0JLBGGlfA=IZ*cN1Tg_ToZ-t_oc{jnI2of;B#+AhO~JbT{%?Jp&}W0e?4o$h{K zu`+EwYbtgxvQal~l2wtaeQL1zQ$VeRjMrYpkjVj^*Tm#{g{MI_?n@(Cr}@6w@ilD+mR+WVq6KYVB?<>RVk<58ijYIU zSWGfvM@Qekd#wIZiRxBzSfBdm!Ol{u{<`NGaIZqmRjUd}>hfuu65GDK-@;^N`xu@y zN4RzvFe0xGmYwLJGl=V<>N7mnd`d^WSX>M$yv7BV@0>jxjD&c+y9kz(GBGPsoYkL+ z67hQ>`LptGABQXdD_E4NQi|(niLz<$dr&iQOG4rmXZ&ZKCbPeP zavn{H)XYNkCoN_7ZMX|*viTtT2IAQB^09sQgXZmd{zawMcFT2vL)>VaOWJkF4TiU- z+psQ|PBau|uIaP*E0p#A$zHmJYv=3D#MZVu0K|mB)!zA`@FLd>C9Z%)t!uUnVDaMO zPuef?oje2RC2M>EOj>idmCjy`HLH0GVtWp=ik$y7h|&!KUU7^;tnK zmi+i)^A~T&z9uVX|m2SXo82=t`CUGNlw9EIL+RlOfJqz6?GWa&p$xh384KR zKkC~2Vi#cYI}dpz_U!4<%jdq`w^_=y|c0_hrMsh46@TWzf^C4>_eW{?IB7@_^3Tknq-Gq=_U6)7NtRzFN zs9cS?2pzNVx#?VS=gHy^4^XzATU^w{W8L_HrXR0z!`$%GOHy0vULbocNK$bI)A2OZ)UoZW1XR!jBsJ<7T--)$u?DB*q6a)FoE+UpmeXdjhf2p+;)IW z+?&!LA`^G&kkW-`<|WB;Q!V3#@h88VJ--?|HQj76WohZ0pEpvNu*Vzw>=~X1gCD<+ zOu}hU1awP;?Y9@;7LyDYM43;SDKi;?9`_9Rs#hqrn)Q>cTIScR68dx^V4RMT!KuZq zGL3(2i45G?a9U3-1}gdtHedUx9@S+RoNub=9_}CFKZ{j8xJ0~)6`bPN&{#eIO{QBO zzSl1{JJ4asJe#>nzqAumSKrNTr!LWbi6}h~m~sa#$FDy+(o4B-L3RO!76b&w7mWp1 zvin*&VoBF?7(69o@~Opw*ph)#TsLQS57WpQ()!Zd3l+B-_q9MLlcVhV&-Cm{?yVAx z7XO~APyOUJsi$AjgW*XPn`j1F*p*;xzi8c)6BwT=e#;V}6wHs22O(C=lXLF?%qmX1 z>R7P*dSo!cf1RbowmSGhNhn=z9B4)GjpKAAeK{!ymniCgG4@tLaj-$#=0I=)1W#~x z37+5(bnt;;@Zd7ITW|>O?k+PhSa1#Q?hKOP5+FDsK>qjpw)SAFwzl@X51+2C>$Am2MXJJk;8CNV~xZGhpjAwr%%sh$^ zP>8$Y|!D|IWY5bm30M#|2+(?tm@o`@0)l#F3MnG z8``V0vAgbyU@3kP3WuI<;+|ZET8<{SE_|gIr07M8q^XQsv6emuP|;i34tSEvWHM(m zVLTE}M1cnkcaHF7rIIbs=;Z0YXu9r9Jp+Z+!kuz^I1qSQZ%oMUIEZcM`d<7>65*?D zV0gb-%JW|_rCVsx({N46QRBVi`mH2}R_f&*(tenGwdnP)N1|uPBzH&ST1Lf^T4dNyAMA~! zCLG$^S#U2Rz`{x(Ds6jexD37WpM;K)`;z0rD%(h=!gN7@VozyNu?UPL5A~*{&vld^ zg*pR**S1H>EU$O*SCik7o+)jRvi*{hfHpuBt;hxT^b)Uio6z-C{u_C6J~K*A+qSzZ z#@PJU9mqzdio<3moEN`uB8iouu$Dn2M1v2U$#d{ptle&0AT}X@ql^a)LS@b-VfW0B zE(8&lDLR2R>$XK|kFTR+Z1^s$3J&tIWuR3wB{f`#Y81mN9>bpsgCT@wnD?}O^7jQQ z(-$h7*{MLF#!D_I7t@zuXifz8P3<|NXhe28#5?nBCQk0aiK1B|*^n~pHh<(t^f-LO zRV!1vyecQ15V)eN+vJZKGkhpD#0P1m|MB$sT_gk0ac)=P)rqSq6UstuxiBhfb?9C} zh#TcN*L~vq-0-j5Ybrb&97SAny3`;g*fx?SM=n*U8`vGQ4q1?&sy^i3I_GIKO@F_s zVITV+8x=%?H9DD&YD)%DmJ}A;V!M_|fo&i5y&2m=D%1wX8>~_(q7~GJkB+Qgi=1nu zup!mjPT)aTVMWWMFQG6uWfJpRI(7@nACl(d8%iNj-Pq|dFX#HIUf+=jXx-?n8Xva9^~IZuCtG>r9b_sy$rwofhE`U(eJ6ctrq8mDH`-TmpX^i^ ze_E>f{)M#MOP0ad%1hBdNok zuPJ&Qa&xeZ;FlF2^JZd-g1iDHjNY|$6^J_@H8w>WD+yAM)BOW{Q{8H;(S7oBS$p*b zLTV=Z>WqyVE~Gvn- zst9Kf!_U5@r~8FfQ6US3*OuZ;8bHqZL-Euw_giZ8lyMR?p+yDO-<}z471hQTaocVn zAF`WE5INynF*h~yb95_@3DMj>0fI?ya8W9vr{L%|YE5N!7V2#Ff^THPZrPxs?^|YN zFgG`E=F@LyWuRq{+2h0bH|S0S-V3#fgxD*;;fyp_yaDQhXbSzLxjX~{}cJkhh`YXA1H>l~6& zaV}mvMdA!g9RC2>78#?bAnJ7X#+`X3c3JS2S`Z3+LYG{!S}P;FCV0uClfSz;YPYdA zA-KHX{?ReNdk96E!?P>}w9I2+M+?8jGut?}?5b?MeY0*-d;U@Z`hsQh3fF(t31MLx2Y@wVFS!w7xdaJsf+ceKqu# zgA-*ruhO~BtVj_hz^*D&ea`N(rL!6Awqlp++a?z}VdXUC$|jE*+$Z&tY&S)}3BI<^qg1UNZ)_4g_el zX3OSm)*Y8PTWu(Q&l_B6&a^0*@b`JMbn@ZOSoZOy$&;3`n2T_t7~88*+d{Kk za*$n|&oVjRVy#Ry_w}c}Baqt&m% zv_SFbf@i2X>V04vr+XhNsjvS43mL64)BI$S_iOA74Y`_iEm*hPTJrYt1a|4SzevqI zokUBTQ_8DSbOP)v8JSfxL{a2QZJLbv#hM%Y%v*U5{W`n8a?ett!47r3sD%MerAH{N z6&Y6<7j!UfCx6kIQ2j(Q^H#F+gvR=F-*kaSn+@V+0Xa|ekJLr~0DQ3dQY)~QWHsE6 zjhmfWUM>JAD4K>&Tnj)eB|?KGv`pI8P6YIS(pqyI$%8oUg|}cAQKlY=9y@TK+m?1T z%}Me71?8%?bS`cz2Bx7rRxd)X7Ha+VIQ%&5RJQ4CRWhld$5y_yTfZ95)J4~g&G;>P zsSwRE5lrG~_pdG~)LLY53!f}0*vYg06qEYUbD0ZcUfh!m%P!!&qBZW{JD6&k-KZ+O zq9FGUOtsr&q;;_Nv@6+8`txRkA&wHoY$A{nt8C1Z^|6&TiLKIveIw>28ID4;3!$kt z#%s4N!r}Y&U(e!VsW8zcZ(XXfNgCA02fw7%+dX#jz#n*jkeTh(s?Y%Sy@rN$BQ@hH z`LRk0ATBjN-AH!pG*G0*h}Jh>+lJqnVz)Fgq4up)EW^AN=isZLk;0}G9HdK}2yK|} zesvx=jy91I(ZD=(cUNING>BTUhUrP%k?@_`>LCcFXL{yz94i`hC4QreCWpmOdL^~z zEa8rsMpfy(Ukjb29d|peb9e1C48`ks2XQm+HTb50ga#or)ZoG$-8QYZ~7OI|Z-|iIP$AcJWhIfnQ2(44<5bVrNI>f;E9*+g* z1|JF1Fn+vpXiZ_lQ%HZ8yTHfK!Y)REPR)D5>@IvP1xe<;i6;4&tL^oO&a^c_#<~H0 z@k8p49O;MYSxLZaUYAtl3U)0Mi@)Ac-BR@edhR3iGIa5s_M$(D&+Ol zZ#jgfsN^OV6Bi)~3<&S|4J0`%_tY4zp%P9KtZjm<8f7`C<5is4B6_!8oy`rrytB$` zvFaD^#uV=JbqW|=`z^I0AGn`@@lGyjJ}++Pd3BGsxw#4*FQoP>Uw@|pTxg2bSPSMOGGi+o)_5gMt` z*BznO-okW9S8-C|oxk!L#Dx&>KrRQ6oXIY{e2vqq+IZ{U(zS_2tw1%Z`i{-u-a@j1 z3mcE& z@9lk*g!8Sw=@w;)p=GB2Oy(S0LQ*tf@B`Z~G2<)k(2o8MYLcEi@6!v-EUsfi3$r(dj?q1%rsXZr%o|xdKo|AmobrKC`OZ)H+;&i*rOE`|c z(Mh@cL^l2p02pPg68#4NV_(|vPV7Fd5RUx>dXaQF?c#grJF=BU()VOzqv02&X+AZx zW|-!CI~h#Tu0D`_c=>#6-27O7i*x0BdQUPXzS6k^Z?F1I{|CuiPhHuKWb&0i?vf-zxy$*`m>FxN*V zrmkx{fAwL8qzzeY^$xSgF~PW^)hRsSYuZ&xu3WTZ~;|RFKhJux-0LU3FBrBWkD-`VXK91_)}g zY8H%>TwBAx$mHmRmgS`VLfpXgo0ySpOWe4{Qu4N`kJ<(cI%zmtm`Cxlc^paQ+5xf! zG5x|R+Vktk`MrjB?3}Ixawz`*UCVxtdqU;LyT4-d=R6Vz3z&DXH^Llj3Z=fdj1ih# zIBgkyI?i3kl^Ay*^-681Xn)GfSXtrD%F(jtacPYY7u~>HI3$3itnE|=O$d3^g8fsT}i>XEe<<;z5-D?v$( zW^=X6Q|Gp7rbEJWl{<@UwDYDkx|bw4_3eDy1yTI+{-G>6|I9V{fY<8!dT0qg!O)(G z6CaVql5-m1+i_2OChO=|aIC-W0>i_f8mfxIts~L!%EzS9(`>uQa^v?(SbOHBHU< zk0Rn|ptZ`zb-nGq#!SrpiXF8TMHk1lb++tpajv{_I4S$|bw4ci?`>(PSCfZtnD0S| z=6sX_UL8Ax%y%S{kc!rL(|b(W2e2$xg00%VWm_nBw&MsqPZ8Jajx%&R2&s6@uP|+_ z{sSbMH0k#L-u2G4Kjyd~5ca~{5z?`yYz7O{4e-RI8ddU)BrCTnNkkb$DIejNe?8W0 zy4T%NI6tpe6kV$#cQqrmGp(r!q&(G%Od^z91c2aCMvXACC3b92Wod*r^1)txLQ^Nq zze$T8d8OC>%`(Cga zys=m2p2{XW?2hy8oAup^ zw&>=>J1@M@!!Ac>Yw<4aWD0IeVVEkt6%UhF`EZ&yFmgm!@9#uXNRh4L43xh{Fd9r3IB>w zK^=0kgt!WK=8Syc-UyL_G~Ebm6Op1pVnMEb*O<8kao~?7Wp+iVJxJ1^z=*AM;vI+C z^rZ=Dt1CJ2UYiD!%IHUTIzRJ|+5k6{M55sd>8w+08KwJxae%jkeY237jXOK5%bZ46 zbT{RXQn3ox9Anp9Yf{&-bAjKd0_CT|JU6WyA%y#(BrWeiF_c=h`*hH?aC9{`rjydx z{I;+SB27iLUI}2rt~`#4qc+PK*(X?~D@c3xi{LwYYzzsV0Abv+6k=Fz9}Z;+yP)Bw z7JIqIA1HCzf=P6SA;nRX3ebumBSPl{2tIv?r=*&%)S5E4QRp&mn8+2u)oG;%#_i}L ztC&b&EJcY~3z=T78v^zz+;05RClNQTcaMMT{{hZ)k-znYfG-%}Y^>+f# zPeS@9R+_jsLkObJU-!F6i|a9G=hM=hpVlkx#8T_6DsDSEWprlvcc*h-Ge^QCojZ#@ zzst)X+z1=U_J|F*LozHekGK8wZKY6wF_aG*X<{2F-(s5dd;TJ_wS}OjtRE0+ft|!X zuSzVkuDb{(R>*mMm=_Cw5$&_9cSF;9%h=1|Wek;-YVJK6!*^X8Sc^Oj{l4w~ff+v* zX-V;}?_W&~@?)wBy_`^`zd|)-D!*Hi#bVWc@)u`Lh?FI@;oa#8zqLk4EP9U9NKSu$ z*ru7O0P1CX3BCuSz6mbYN%BiYpcm+;bV3jH#Z`Vf?ftye9*sMRl`g{Hrk0s9N1Qtvz1q)mQosyxh+8D0||_h{hB=j1fn> z__os}OK-9qb_oRxS0|}*kv13{ys%MeEVuTftYI*nM8#^ApjPaRIak~mN20k*tV9XDMfWnz}CST8}3x& zS?D%7hMEeqj8<9V?5^b!2j&ljY)lvRwyce^Q?=pkjCHU>{(7&g1nm6OKzl`NV$w;M z+uquW0Bj(Tr=Tn3?up*}2ZGJ@0i~EW0Z~HVU;QqriLd?r4YV2cQ0u{VF(szXGmt{Y zsfvWABHRo^N5fbiq}zeTHAh0|zW5^gNz(x8Y*|JLFV|M@>4+j$Uz{Un>2RHfgZ?L@ zV4LOfv!#n|KQ3Y8#NA2osUgVzC9ATv`HfksKD({b{VO8%&)J`2lp7cI?#`T^BA-sm zp@`yYIARJ|a0yG=CqLFw|0z?gMbE6QLM*ccOyR{!)YX#)YS&}0IzHHj#dz+$$!;|~ zRzHCZmvs0Rf#p1M*fRe5$iBIX%`aLB0Ggt~{|MrgT}i6zv?gC*%_)gfGO^sl1sfVMn^|>V#@u zumwKcmx2kct|PuCdjFtdNF8&tvV8dT&7rdEiM{GBb2ts;mZjTv&Jv>cLH|EewFcg~ z*4VDobIlrA6*{~Luv>97YP2gXqTtG3nd=ww)ad(wNxcV>NiDnr=2!xZq6T1AsJThN z>2l$z&GE88J?giMTAka85B6oFj4W-TD#}VanL5S2+QulyO(!5q^u1CUnLqUV*DU(X zmDGK0tLrttA`s;M!E#>nNt~L(bI%rv(uI+LmB7ly2fU!4!uuwT`0Bfq8@b3|liDFW zGmR)fomqr1O{7*M;p6O*!A>bzYe>f&7+)OdME0wi!X-U$KmEh(da*KQ<&6H59eRSD z;l}J5BNGm>XX8SGu$Q4_#1|QioEfR{{ko`gE1kNL#!&)A4x~BZ%cdSOr`h^ysu zbZB8^?4BmJ^N@1t?=jA0PS`R=QnX!bhXhnEowazw4u*05WkVBk$H~Sie{nsrWm8sV zX90q>FCD3lUI(^wVD03=0kM1WvdM*9@$ZN2xc>lEjmYP9fjtzAk49J_lF?@tNVK2> zR_$&vn2vQ&UCTM=!#_a3(ny#3eqMYZ!;b~wpa8JvA0F>E__=cN#*1IlBwFp#kbGeV zvR*5=a7;bxFl_m5IV^2|Fh%j^Et^C2?{k<`M)^osL@#W-!o*mt_meF`f&51uReeR? z+pgI8I}o*h`(Tdd;@>oq#TWBY`K37cAa7r>?W_Z)87m#<@K9 zn8_wEJ_7@8%N2?sf2K=Q?me~X>`~_6G*ewOJ71LXTMo4GG7YO#t@EWB@-glGbBI$3d{~$*Y}TDwEfKqgWUI@YLX@xSjoFz5Ry6(UBJUk|9-E zfHC5))ZvlvX%8*)3GSD2e`Tp-|Hp!khWcxT`n-bo6Yz)XrF*7b#+=_%$IBL&mU3~s zac$_}jkFY@k7pa>94h*+-G#PEubif|ZmN%6zsVZ1lYY@YOZI&2D5|a9 zw(M&9vS73yORQJS2!UT3&(ssr61^P9ogtc_U)FejWxRs=zQgHDjWSQonse}#SDIl9 zqjXCva%to_Or}e|Me%;#LbK9n4V5H9|7-K@tB&RZ&%2c!LM3KMI zIAVkwIc3h(NWDSVCHF7!eeIn^$6)Z4A#$a&X6-^$w;k6YT7peYdWm}C1R^(L=wOO7 z;?odWvG3Dvbp_Ii^(FjKcEhO&kE;6)MUvCZfuj!eUuk~Ne^J^;w~vn-azQXfjK7+X zG}H7rTkK%_Kxx)e4$fSEQ$)nR(eS9gQv}T)f-+bEliS#1!|l`O{{b$zvv`WtF!~eC z(+E867IbwD;qYsJEWGpJ+&u-e=mP=6;74a&Iq_F_nF>GYo@JWj`yT8@jFW5&Tl)Bw zpdOsXciAlI=J6%PA>tzq1sk_9Gxt7oZxbUETSU#!bA9TdSv_ zAs2Ph>karP)3lJqljzWLlQ-cDw)8iqC9=YHn}6(N1TR7PJ_R3nSvv(#-_zIomrDW5 z@xt0geQVT4*+?!@H+L-VTKKAEI~nCsIQ8h*p_J(qo(pLui6s6BaH>F+6so!)zx1BW zCB4S?vz3!Hf;=CGH>WPNKCO)XC?yJQRJ3FCPQyBq5KJPqFXtDH*Fqov(%E!)E>Os; zlGfO2q{Wm@{Mnf9j~dCHqKupoE!g8kV?Z4gBaJg+(PtzR5|`5_iwO&p%6ao!HuBRI#W6lo^azr zU%cj<>xicY-x2enz3Eco(2!Si`GBVB$Cb0c$eRO?Y%ia60@eE5+93_!rvxAlNR;`^7evKzu;yz6n zaAFTh85_Oy`A6G86abIp4<>rwhm08-wV%Va?Zl(!D(Kh{sG5inrn^bbq~6+ROBv&$dDRP$B6y^ogWLUW zMrKT>j=eO}9n4P9m%sFH*@oUxI&Y+jUUnie|O%gJ1Qt80v+Ct@ue}DlUkuQ!vZVYmHz?8l1 zyldgH=;KKDmI>pWONUenBivl*nQOEbt=v~*`JilEY0W30ys^u#D(@^J!LZ@0)*(7B zj!|EYy*j2pVN(l#?$goebB(#l{Fz3$CpoylgAF6vuQ`#Rm1lYX7|@dO*i&;s$50F3w1}`KUZs*mW-xS)J~SIuXBp;0ob3plrAJSYx{3 zJ#$7=TS@MWo^u*3HU}MQfdl<> zP8Q`z-{+;|KRlmbCpJBDdt;gf?vZW!mXok6X+EZE9R(tWX8!#lodVJ{vic8=s!Bvy z(vdpL51h7ETYd2Ej>#E;SIS2vO&>qQMC;u-;Bpb1@_DB+>`EkG=ODQIEh({3_rBbE#Prz?0BK^rv`o``Seq8cdr=fVXjMO z;n$%ac{$W@t88r7Sk>~j$cj)lZJOZF;@_fC9v%d#Y6jf zTfB?bYe-F{9NH0PN?EHZyUNc(%0lPV3fXPJsX7&7GSWqv=d=0wd=onIhG%f!CF$^T z(hPm=vX(WiO?fstBEJRIQ9R;VHQBt^&3rDIZl9Pn*XenI>c_HCs}#@z2(*^Fe`^`S z0@6$>Ukn9c?!t4k?F?=VC@F0|%*PyFu24w`Y1W1(rF-Qh1GR%RjNP1Ma!9#x5oYh1 z06TRiT))J$9lC}3s7COT&g3<$kpubUKR2fnm5#s3Y)2_j;>R*SEbNPRoA!%$kwn3T5o!G0MWvPE%?H zY9|#PRRZ|Q(ik}W)0)|Bn!LhqwOE4f18A5VT7tQ_+Dkl{^(H^z&TvD9&)#_nc@0*I zQ}7^pT+6;Eo#M|abJ+a|Eu+w(&^+ZEcf_(mbDKsveHaev%2=$I!`Z+1ag%ygIV8B0*51%1ct)Rll|58&e<44<~B38~^puwvf3zK!}M6Ke(V{ zd&g>v)N_rl%3v&4xxSlTmCJinS$4ejg=9H_DPa1^N};xL7x>6Kqhl`s#-PZw{32BsA*a#qid!*_OsaOPa@1%BUA4p4Q59vDIX0YF#N=D#35 z-_pYqo&VxGqA@R%ZM!hBl>@6Vnv0JsjBR>EP)U4ijZcMs%;JyD*c|-Fc($Rt3lfRG zGFs~wYB(C|>8Hmv@ymo|)MOb*K`0wIe&~>ZX&Y?Pv6EIW3!weK|#GQmHp2p zry8d5-m}`C)@)NY{_!eVJa%afHQb)}q_xnXUlk>^>|N+dh=?s86{&_tNlGqK927_mK(#QOq<$>N_% zlSOVu%F73}dYcDx%-RgKxIe0xoPZ=b6r~!5pF=9;)&DAVmw)F-W|);&3VW(ZJFez6 z0#%DJCoj=YHr=yYR1P75iOjJnd{GrxxX#CvffQ0kEVL-lHHT2*)tGeZ1vOzy8um*( zf5vl*)_1xqhs;TOwkYRBqVwINC*mT%Hh~rA4h750F|_S~fdqa-njcce1S7fy?Z}ey z_bHxgHi&0QtA(s9TXM0h*8z*{P#{OzN>f5_L9jzVxishX?C}gJe-by5y8%irCW*0SIAc*x3JWW zum9rKT_jcn|H;aJHEi?a#dW03hUe#p)_fY${n_}57`KTsjODW#+Jd!ehmCb$C)0Zl zTp;P~s{MLa%Sc+}xQZ3E?6m?=CC*_e5#^F5;>*%?#07ruHHL4xYoKTidE)ts9sm2M zk3d+njBCJX90~gdVK;sUb9^TfW8|bh)7wFWrp$mR=Hbw{!S`cu^C0;cVsQVpYjbzR zw67g@8~-?JEE+sruVTQ+IeCbO;dI5Rp`aK2Se-N}Ov3DD=4x`TatKmjk(#>aHS?T( zAh}2T2tK6v8VLfmBiruGIiljUOH!)Y{wGC$&*Gk(QB1=X z(<*Vkh(POd`B5Yihjg+cylIhh=%-D_-3xnW(9V{AbE)tT9(9MlJ#nDsy*71gPz8&E zGJ?vfna=acQzf&1WqP_;e^+2DpE0ngY(}8#W1G*tN4<>?_2{9A#Fe474e1ppiD$ZC zZ&hDPSs0SOxa9y&d5fw3qY?dk!{Z0WX$!-DfaH8qx>h60_5GG>4(_%!bmi?`#H&&~ z{g$=_xN5;5SO4bU2TPV?YN4d>SM13tJ{{fB+cgGae>}5W54_Sg2%Yyo}#Qul&Yw)~c?$$qR z+Ak^oQfo6`@itQhX8AEj;J3ibBADh2|88Dm3{>xf`zpA-F3P9k?Jl~7t@ zV$1sWk#6H=7WBsNe*_H1VMSC??8#?KDh~3>TPTHd2fAZ8&T^&tAZVQ9up&vTh$QFn zW{xgot|;`LCYnL~sG3_PeAijt#{smff~!m$Lh%uxAqQC$iKMC{Nc&Jo(K(#@(giKR;XXrr|Nzj6bh&#Eb`90F+6I(AF|;x>m^HIP3@m>s|*24LJ0z zSej7SH>H5eD|dg_7ACB=gJwM5f(7l)1Ck9E7PNQ2adfEk`*BF+ zxht~Rr0B$wVB;z3OGM+>V`DmRx{~p>j$lFLEgA^oz>8M`TN{OqOJx-O~x#p1| z${HWb+;>J@b9q5M)-#D9{OF*C^FUM{(mJCdIEen1qq?G{&9`Qza{Kd-0-T_HeiAhP zs5T=^&UJWc8`IfIAcb~C(#@QrRer7%*eD1s=8(qxGGDdpA3!2ERZT3{!A7UP^h8YC zxM{b@^+e~J-*OCJQNZ?;6qkekPrHql$7++KLr%`(Slgqu!yEUF2&+Fc*?qdritcp6g~+ut_`kVG zF|%GywHrIX1o~@M>a>jft$yr9!fWCTnINmat>51~=oBJ1-{j13qESm(|IneD3;2Y4M@76dM0LVq-C4Jpo)BM+bD@@ld#Uj>Xz9bQ#(7H>{ZMp6MSTp=94o2%bOebg~)Q7(&UkNUNR$kDG&Tq0ZW&V-&wj zc&gMK^h&#)rl4<%Nv&iwb1hOEqqBt_ReT8=J~tTKsc`&8^AZ+JLRy0=b6ZqfQ#zz7 zZwqJM6Fw6>X|fq{QX&`O&ZM-tJJi=wN#H4$$^@>cQi?=sR!&2?n{hsmJc*9hnyl{` zV>!gs&{JdGY^>OG7gHkc_8XKjlPgu2sg1PCIoUu>c!Nbc&@#@zit1 z-DQC{BB&2e{5d45wg~Q%H|)S7LRiRULb-P1wD*luOqETLCz@#MTE7c~nJJheoo{wj ziaK?}?vEXBZ=45c_dD=n(H2f)cUULBAF^Cx#JKCaQFRsYSlop`a3+%sZA;3PE_tvoOPRVWetdDzit70!LMIQEN!87MIh0TY8X z4lAOWfmOJJORYq8SyE^7LSJb=1aJTu$RkqlO(j*I@hRTLpl0|CHs#kuj`1hu6I)n3OfSzYq!r4Y-DDZo5__VO#pQTFW#Qw97y0S;a$4ddfuHEpt;_No+eQt~ zcU@keNjEYRKgL`M+a-KhvqiG@d_^*(DMNhz0bbQSPXvuZG=?X7HY;mhXyBTcic z0kG;SKd$}nAkv{^{;g{{r>5$_&J($uh|iS4J~}E~q1B$}7C;T#3yEF{?Xh?GaHK&O z8B5=y$EAVYUg?~lhjL3t?6~dxK>xK2JJ<0L-v1@{PecXr%NPv(gCrCc!e(WBH0*FN z1Ky!f2$G&NIcis`z$Mv9N?3|OwjM}jxCd!Tx8P4XNYg)nw+z}_LC9fI=vn|qJO#o3 z1_rhNn;{&Ru<>HoRix^W*@7vih;RihOY}<={y%dD48MHhRD=m(iF8D#Bl}SCD|pl^ zWmA}vdlg9-yIqLGC+=qskS+UV#bbS*R+G+b>z8y6@XuUIJQ#AYc-=^9Uh4tpNO~O| zllLPJ9**RGq`o^(?k_fODfQbwP@%qSPE{rP7(rH5PJT$KTv`}tNKmf4xdV0@dKQPw z8~eksx2p3R$T5cwqy?7ugc56H(H>SCIZ&K~KW1|(;2(>{@vXJr(<%DNsvK+o1GKks z8$L@s9|lD)#g|w7;edV1l%;emg&Au`l?|FzojH6>Nh^gqB&*rJ`fk*Qs?03lmcZHX zE_>Qa>D}N+Vzvz9nW~srP1z&_&-Ub6lVh^? zww2uzw|+lWrbKg{bB#SVcJ*WZE{XZE*3#CKmlVx%L${V)p}L_+Wzi zxLMvXUNA}cF8L;5?g6^4-Q1*y>w2mJ-!YN*@7VV~MO>Ynl&Q3LRnizluR@NM>WV1F zX6Si2YYk*aEdX9P{pMq%9|jVJRd8luM2(fGR9TcfZ!hPu&ruki2T%I3C4@cnkt;1b zP)Lc;$lE)WY%8za9ZSQ~HOJy3aBOc;;5VJj@5=lCW6tmDeca&HH{~AP93p?l(tXvrWaOXhr)5K}Lv`&Iyb&@p4r= zQ-G^8|tn>y($P*L0-A>U+Z zUQtfp!hXx;1YS{$?!3)yJ0f+Go%@vv4S)Z(Vd$JObq%zt9evMO^-`vz6X=&MHoYK+ zWFT;Xp5i_{u_$W?r$(eX>;#-3 zK%mctnx`-9L_bVn)XO|hhsegH^ukCLqIqkyI|_?gy6tKw$QSe!7^`3= z3j1HCPtK-2OmlcE9IA&4fV+A4v{bGpZ)+?lzA37H;49(xM<#@O6)dsBGZzb*S4{4Z z*u&uk-HXA!&5+RN-}j6K6O@RWB6o{(MUW(J?w{7!k4Exs&U$4>`^H0uRB`uwk~SV=-u}jJkQm=5gY=)>=dN=LFEw&Pbw+Yf_uAQ`*RBho*H86|{LblK#|}e_TgYztXUi zPBLG{(kvnP-MWKat|W$w zymkTtUj`$F~_ES8(lVA@t9+g`IRNvhRV;ZhaFQ@^E)0UzJkdHAL5vw zA!EGo&W&7oP#IL8j=p_8&2n5LS(lxRjl5WTqFVh+u4PKm46sw1M)T%g_9MaP^vwzS zbd6N2E_kE4Dt4&IzC97M*&V1TNGKwpXTq198nP$h6nP*@Zkb&?VyIsJX49d{+B1nc z{Up16fbh2D9TutqFi(=n@fWcN-~R3_Xl?f#d1#_BjJ{an0?+cgkhpyG%A(e)Z$9iW+f8TsUG49m7Fo{-7?1UkKyFiTNolt1~*4Q$)%RR z6!M<4v9g|*bo#lQNYQ>JC8IIf0enSf-yyb_Ra{0kFKKVirw8W8-q=z}H}|JCLsm#( z@AWyXu!y_znmt+`=61uLO2}TAk2MwO$;`e)zUUw|&hv!(5j6$ePoGC+$ zzr+@ixBWcu;y~)gBohxYIKK$7eovs1k7I(~P?Zy>(DQ8=(6_5qwH2tTlvZI-k%as8 z@HGzL*V++6^L!;Nnp~H`j{u;NqjRNOfIYuaiKfrR$;716{S?vRLxBhuL~A}CHxhlZ z^X6$$v`6QgKcYi*uheN-qE&S%41U3wyGjBhRfYYfzF;$Tfg-lCRaL-;MP4#0&bf_v$&qVyDMzfN~Kq6Sejlrl&#DXxp zoFaar3`P0vStX$`*m~RXqk(F}xR!$tvm0&Sw!xO)%dN7SH85n)M|L_t8vAMSV$C`D zd;bfLIl+>;*o(y@V@HlLU*VeH{XlMsre6e^qPne=vcO4Yw5Q6QztPT^@+NX%kx}ry zU$CzQb;F9aV*GH}+WPQZNd@CV+dk_v(3}*4W3MD5rqC6BT&OE84CX$RLOuP?peZn4 zAR;Jd11W7A2-2-16p@r31^p4GCtZ`+&QgGW7h}(-?L1WJ&1}c@_)90pGkR`o4w2*<|hP>=El!Exu6F}(+CUv4| zuZn;cvkqprmJ&a;#`--gAOSh7eE-#E8$IY5`!`@7bQ?r+BZGtDX&7U0FwZyRXz4Lo zshuH;X*WW)Wp$nuaXF~y>f`pg;D$1tFZ#Nl6h!eRe=KEr*wR+BJvNTKky$KzFw#fT z=22ggYY)1nULK~7+x_$b>T*J*#GyW8IG~Rd9+S<9E(_Btsk<7ZQ4+UV{(E%Mt1jw9 zIaNHSD#rq<)E8 z_e(omU3qW#o&xcq_ib4n9Xcb%gHKcC0up=+@(GT>&fW9&6?S{bT$Z35!fZ?GOGOF$ zNU4v#?@&53;jEi?Y4TcrFCc{O>-e0&?_x6Okf49im*SxC^^;xW69%La_misNZLet9 zUgal8T6JyT0e)c#@qyx1J>`QYhw|j&H4j4qew8SgaZwE>iVZRVw8cYA_5wxrGGjTw zl9lKSu$At6=aAGzVb}mz#o$WEaISiuwOB7!!rah^v-dUnn=84IZ3q!!R}OCH`1db9 zvLzZz@s*~UBO0{sqcw@q*OH2o=Cy9%#vNcnFH|8azgGIo8yVX?6*jcLOck%xw(M@1 zC!c_D+0N|J5uSTF+2c9w8fKO6MpUh}IIW7NWkaHjs+KvggJX#lPdJiufsRjQb&_hb z5<8M1d{qx2k z?PfE#;G5p}3sxgzr;u>rVsi1pZ^GCl%G;HCnq-8Nim?>PiY^=L+H93hIsAS4gV^pZ zM(XyId5d>sId7Fnvty^{=&v|F7jlc46|%M*;HeYcwR)GxcjrsVNJw(^uF2;lP)M;QMF?gc8C@b$ zbxu=I)Agw(i*a0UMZni&pbfHdonAaV9?;EicYpLOTlpp*a;R~y{vkA`^|uOvrk205 zUQ_GOi?MU9x>P!KWktP8&$LRxg`^i3Y#jR(dpti*roNVOXO~VrEKo5AJ_5AnbnX?N z++KZkOgaJBX3%EsO=>G+AAvly_J{|S80M4{%D;HhQ0}^HD;k=Agcm!sB`LD03S%?S zCp=?8rK1L>tybo)MdQf&YIGNUa|cLz^d0b3j0fBZKz`uBZXY`#f#ro zv2L1g_L3K%OTu|9_bq8>f3y_idB=fyNEoLTI%VH6hACPAcyh3DP$~m@>XNbwzyn7N%bgkANd_ z91^&ec+#9;{dHIY`R-m+3$*&*L}oIB7D^m`PipbJBvXS4d=ndiv#RNWS8R_Gvc>WG zz`Zd7SyR2X>ZI-5+Ps1-iw8Sramm}|PJAPfOF?nf{U9_lv|vr%9oO6ZH~qIRWUqu@ zVIzR`nr5a*^W_^g^L?ZCqYugF`K*CP_ByzD6;s`2Hk+?Xcd?Q6UUh#GAOhBR5iHDU zi@FDt5Pk}tRZ!&ae~_R&n|d=Cg@8Z(?i7hT=Y?YP?|IgR5#X79?Ox<#%P}_PgHhoB zNlvJ6r|nUvdiMwj@>A-)M&7KrhS2lV?kY`uUH_)JX4et@pZaB|p(rFu@!Y%zxp#=w zDB=gizvou`8RXaZ2xwNbEjv3@x>3uo-s2qJWxSewavh+{YUe8()dT+h>szbeeHq(e z;Ah7?*p!+xiO^2qSKGpfDr=7!^6lTvf{r%A3+lN-AHDcWDw_{k%Zl93Ce||w>l}|6 zSp9&Wt|Luk9X{ui{R^({Ul2U$&>lz z7T>RMdyOgly<8g3O)t+^TND-+ydOHDmAY~&(uC!eFEsNEt_|fCEyOmb>r7ntV~17T z7OlF_oc4;WMNSZ?1N-@FX0ubRZ;tX0JUODXqz;A@(+ezil>HjaN<|N$2cFkDCO6)7 z=B@C;1-_SU`>Ski+o`w|_e zEj!~=T7uSzG&9t!M zX2s@oi^mrS#MXZ@5d1`dO~xVrVTr9%w`{3^JFMNVlryE>vEz6`xUxB)| z`7C=2Ar2?Ss->^}Cw2aBm(_kT_^ijqKK{uQ(4bML&0=W8(^}0gyGD*oeTe^}R||_N zi(#$@!oUT$bWd1<@Z83Ny_~S0IUo24a6iDVx!}p|jhQF-fzI8oq<7k7ePFWl;ygq0 zbMZ0AFS!!Uv1#*@Y%)WNOG)K^NP^!$DMxl&)8(2C8{9YBl({aH-Nwc+LowyL@RDj9 z>#I}=I)PF*xpF5WE62|ta>6%MJ~gCI%&Q?E0dRY~74Yn}2aqy2-q?G(3af19?b;iNsD;pED>n8GX>5BG)SI1qVU|xJonk3ON*yVuTg9g5D77wFsS>ek#f4 zMwId4u>G$*fv6p(kcZNTJzFO9rIb;81h$N%yJ9{yPZuK$T$Ra)pqZj}Vg9w4se6v8 z;=)o;D4O|uHm)t!I9{kYs+0HC5b}SWB8>6u7L8NrG0+k~n7RJHK?47?7{sv+5zMhO z+8JkYS-D_PL*#SqLbzGv61OI`X;VCscpAiPlh0HzrvpF*XB0w@-NHZQXAF^RVFM0n z!fug>D=EZ|E=ENqk+m=N^n2nTuWeD|a952Ce%4Z>pUY9hw7@9{QIM7qda#bsE{w#) zYZF^&cCVOojCj515b5!*0A&)Jd~!@}p6$b+`fxA;QOt!K{>^RgdRf;hJu7Tf2FZxT5uc(KdLMx;zs_@j;X zrK0cohs7QpK?99n9njqKFs*@%Riz=_y<0LyFWVBQAN#?@!8LJBuZx9ZfqU;i=+?W z5@sj+N#dii@6L(#%zcr)#8BGdZlh6(w~Pl@MHQK9e)UeV2@EzC2TVFw45sS~O1#y~ zt;?qr`7K@Gi3JfRO*c(+$r1xJ1)o9sE%`V|=CGk)-m)tf|LD)WPMoFF1LqIteyF=^*=$!Vdm zf#U}kK@DZ{pTV(;z}(ix*_XN;-j^J(cA?i<6L^FTqU@o{(~p1;V!!rl8y9(Jo|E{r z(zV^%!t>j?K`EY3uI-wxv9QYI(m3$bU`7=s!lKk0N;Lg0_6I&w9YD2jE}@~ET1Vu# zyDZz~9*cr{Q{|G9ZK=oef1A%YCsafiq*80iND>xCKAyO@#Cs69q-c!2DkCQjtscJzqMo z^3gCD`hK(M`@P0IGkb1siw_4}=@6g&=@4xj(m6riNZ+s+!KdUNGI4_5T{^cNW23&Z z1$;xPFWSX63}~o2J2mspT;S;=zb<`BEvao#$sidNh&9;~#~*D-I*Z(0n}>gt86b~} zwrJ#SWeT)F3m*;S*2LG>8OwPZP5O~0G-K2yauh|5T<8NRxLx^`9ph+UVMSKZdvrEL z*h&niN*kBtD0)MEn4j1jes+m<}6Ao zF3XxI5g~~x06k&7Ayjfhg0lB;c(xiPoO=L52#{lBWc0pQ zk!?aaCNFF@l_rSlhlQ#ch* zCHjTfTWt8CbH1f{YTe@2V$GQp1TJy3Yj@kdCO@(*4M7dO!mvc1ym|g^HFGEG}e41V59>47*QFJ@UNd|90OjwXoQF$-iw=z&uC2O|V94 z?3sRTTeuT8r{Zp$=UG(TU;X+kQ4H%PkN=o^v%e-0`xAP10cIC3=NZGSf^B^(_(Rm$ z+to(^Lz8C@5l?rMTj&Y(Y%XDBubRj#-`CPykn;3hxU^2S7__)jAyK25b*?;OWBDIQ z?_n@}enA-(^F#$7EenT?D9NMFxSoM5`bX!wMFptpJ@rKYDwh1Y%9*O`?q!T(Be<9S zm^Xu&gzjO|Ecu2l5;~6!c^gsbYFmeyxQtDlaqK5Hp@PYLqAw_ElQ(^mQ;q2ue-hfC z_+Oq9$Zuv$5=5M8&OgKJd&&r%{|+ceYhKK+4g~^r?TuE+OeY%(zDi%~>{jJR-%nbv zy>eyvXF~V>5wLf({*Kg4AZkP90g5vqH8IP2bjhc0Q^1|9w5xw|^vpPlap^4l5x^@` zWs~v%*Wvhf7uR)ygh{Kq%a9XCEd!WZ7fYAMg@=+o2`&Z=z zn!~ORKMt}o4JUu`SlV9M-vuO=(`YVy%68cES?D&*#8{5QI41*7K}s#)()K&=^`hkK zs?YHkEI9@cXiaWN}&`&nF6epV*DViZu?XuRko_It5@@Evd~BU|)! zgU4LuhSSnL8SZ&EeK76p)_d+5c$AG=iA-I^;G4v2#uIspi{B62cvTg=G_isiEWC?8 zlVAm!p`y28Dsv)4lW+>J{wqfuM;L-u_)n zxPLpZG{qkF6=SlW^v9wmC80mxQ*q&o6DN(qCsy42pkkbFa$SPBmB6Go;!EAO>}&CU zH3MZn-O>!Z$Pd+2O%|$V9$i|4AA*sc}Gyr=^=m`40OwJoT91iU0dhD%-e(|(y*C(`-W|Hte1 zsP6!V`5-v1v*W6<=+%Ze9-9hq&o;&12;(xS{~;cuMWN(>^}KVD6L`Jr&Ap)CG{?gX zK9}$nE_(zR0fYz`D9xG>_{(B98Z7aut;^dB^-Ye-)ZH0n-#nJMLIZkqj>#57Ij&{) zk)I>de`rJ=Hm8dOoWGtG&d)|}Ww|eg;d1}JZLRj?&>a#<7RN#083OL?5L92TX~zpB ze6uwq!P#}!%)@P;do!f5*G+2qOd#G~qsL;0E#5P>nRs0^R`93{D-Q_d?%TLwEBL^&TP`$GurHC zvpQqqu@L{&A&wrC)?Xc8`cKtnF5+IF5JY>veQxiA>Q5j~?c}Zb#mpl&wQ^MEn1dST zJ$RmRsq&T6y z>z(~ehI$q>MPg>kNYg{WhuiIrg9+|T;-Ks6%@GTui8EWW$r4Ipk4@I3N%&+oF*)Em z=zI1?9$5pj5%xY6x-9b`>6GHFBtW$p|D>!BK{gF1(uUe!4y5 zm9woY|H}1d@=LS3h>3!lZ^=aWehng7%{Dv>`>c!$H@vDM%fY^&q_ydAEyj(C(~5q$ zZzfi8Xeow+^T)T{c9l?umn=<9L&YHoKG8Z=psci{>WX4+Bnx#Oi0Y+$PxVXb+HyLR zikvw+m$s-Cee>5BW2l3J%o+c~&tBG=<00{lcWR9L1Kh1X>#?Lg($FY=kPk_q_97tv zY0E!cja^KiDx^)0dTgvXR&`?w_GUVyjatDyk>_u*xUKm?bagRBPI0{fx4ytXN5^1k z?#~MYxoKvxr|Q%6ip9-Dz4*w)`KcOhI;^Vt@b#ade9-BOk-omqQ@S0hBX945dD~RF z%!kqIYOZK|zh4{DZ_(c<09vO3_7nRdgR6mR_PAxXJ)k3 z=*faWaz9>iZ+ZP7*%Vop`E64*?gcBQcC>}@GxxkIV33ot3_F`8A~mSAS+qRK(-3?fxAmAsKSZ0P@g+vK%ejzGwG1oX)9?>K_b*vsZ00eqDzKeoCP_(dg#>1U z=%8d~O>2{d^G`vUiJCIFH70TNYGt_P+71^tmbpCip^1~=6bh}1u;CruS}QsJ@cSjNZ}1-{LN{)r3LQVdG;k2YQabi zP?skK4HGwl2ZTd*eOPX+M&m(sc#3e@TyuVzqluT%C{Guow1R?BQ27N*s>(QXAaPh~}V;vyBcf&czA_xqZ%)Sr70L z?c#gC;scSeSRuuVl3u$u<^x2jX`^6h`NaWwC}IS2c@r4O2l@zz zU%E;XZZ)jTAk&1nsA8De&tbmJ4A98aLWG0Z^K$z`A*sAQwh_KWh9Vvw-dcOvqmqvI zLkYPCt~`}JdxKP2w4l?1CjVyjvZEp=^>%n*zeNb?L?*?Q^IXrO9Q+^lamJ_@Y7o~Z ziq#4OS?{aEE^8Xt;M2;C$76<})t?|ZU=2(m4Krn0RgN@m3BcsRmS2&43H%91giRUn zM?hNHC7C=h;onp!ewc5!h~fPNJq%WXHrC>65@8C4#73NPrG{e_==@l+9>_%0`Me0l zzU1RNtbC>ebV3(KS-07Sc5TqcC$3rc2=;TSg`xO3r#MbAWEMEwq-#GU@y8d6yg*cJ z>c!mqhoxpbMY1nx$=Tw`3N$lZbNK4w7CGytcV$?gTtk0F=RHwM*a(?~G;1VwAX#Ui z`XFO5{Pv5fDUvtS6M=+Ug(b<`&#=cn+oSYvI~Uix>2QKG_&Su#_WPVXP5d@o_?GxS z+-eEPmdYTFKtA-WdHLPC_zYGI=Gz~)#q8!xmet>_M=R{vZ{c&F9Cz`9|6xg`e09;~ z6qRU4b2XVBk+D8cVCg+3^hD?y-lW>b<5r110;=6yb{e1*94yUYBP&y$?!6m7p8iK) z=}w6lqt$+cX824dXVC^2=30Dt&0-+zV)MNMrS7vmj?EdEJzJy-$61UhPFh2Aatr0Ny+XW2T+gR*q(RJpCDm#q;-l%%%)M&ssN0<2$n z&mRFLTT|`$#iICWdg4z#?u08FX?bCzy7Q@~4rvy-y;@SiCkrLT!j(R@I@Zc>Ci%Dg z&>uF|UtnSczB@hwB=9=EU8p9s-00}5+3�O(|>5jSn0{ z_nLVXRWeWNS5HbMp&tP;uAJ5847uVJubhZ(J+Dw&u`HV=h2ig7DV?H5-#ChdgAUC7 zOO_OEOBPjU^X1Kjd8C6QZ=OyF>(L7&sefxFem2nJHu(0hzt8%)Tj>j99jo?+u}|D) zLxGO~K4H0XzmURmXr;;w>rYG8-X%Z2kxIs*YPr*FE42AfQ^R=H(sZ0*i6R=RIb;Mg zo2G<

    <#1-ovR-9w2xx4k}ENhreX9QOYJ`8GTkOQ}cqK56kOw6@y`DpaZX z($nn(=EITlGB8rY_vcluT68zlP+$Y>uFn7 zo`rP&KXYehS$6~Wy}d$)ywl@re=PC#FXTQG1q(A7{{ZNvhDBC~2n&E}`z04Vf%^FX0|)YeiJNKJter@w1Vwm-sI!+hK7HUM*@QA^6Hr?@9TNqg|Fr zw*}^5B?|i0$FmGqn)vq9$T)n|PqyAzOO7BAr7+=5RE5*j9Yv1hhf!5zEwrdyt@3k@ zrr0u=+=Y)Yw&t+3E%zG--1k~ndKNoBvQW}Ywhi#gr7;Y3powM^Dr4+ zUVcCDp&;u4BrH&$=KOUxPU<__;&e}#9DSHcx7zQ~F>8~y3&Pgbs`iLdT#F^5P%=Q$;2e8ZH!L%l%k~n{-iDuwOPvOE6%$H~C(@Y?gt<VA@Fbz0Ujr*ndJ&U18T zr1nUeo-uX8vKg4BNde0>{t=_LsR{O^4Xa+oZb!^%fpHVhl!B!;DVQqek@qAAE%T8J z`-qCO1Uc-PJ)|Su?eFaF=~{11Gb2bUY*Pm%>|gs^SF3B+ha5$v5+nZrbWb)GVXl=$)FHD;!*K{Y!ma8C_R%KIpRk*mrLcykN;Z3g#T9?Ul}y z#lJh?yVg~Im@O9eEzi2-sdhV?s%jJBNJtB9AS=6SiV=w6*!#9zIL_L58h2ZcYc4zK z*xNVP%~C2(ls-Qid=~^$9)uIHI_W|5T+z-)QiP_x;-hW!@2I1_tERjq#eZvCmxy_f zC3y|ymT`yqYibu$i1_)9*E! zZ>QpXKNZldxb_wJ(=*=hR0E}Z_NJQtJ$k4Mg}g@Q-y%52m^ovH@e!J1JPQsj!<8V$ zlGpbr5aorC`!N+qBsSP>N?AtW4XSq5w)dP_+1$3?W3|udPfxt8rZ|o5xg+a zf!FRG^w;{8Set3Jny~66HPsRuQdWfzh*C)eYgrj<#X`tV=SFtBLJ_o~Kwg+buLBOkCPFQ9^nqnQ~$^0;+kyGyVyIisiPhMc`*L+^%~i z7#?eG$R^tV0JF(bdwt0f6ISIU8W0cRs_*qzEs#C0@{XZO{{Ulb_7=V&yRj7?!8aF* zI{m4u#kwQKuczHwn^8fnKp{X>p{A#&#Yy@902A`(4qa1@^3NPy zxaT2TLDuZ|cuqu+;|kn3fW4H1=%k%Djm}$Lf?{wWCi6H8+Xn{B!qR?U$uJjnLRx6P zl(Zfn(km1yr}}G3{#usV8t#ND>7sjoQ0k2}C8t4Xf|aNq)hd7r^roc~r;3PVp+z4; z)&dY};Xgn~^%`qwPXd}b1UrjJGDz)`!3r)^g(tN40tnm?cLZtqDB(h^EMK+X6or1y zFT9fdy4%RNrQDopv_iLJQ&zX1irSPClJoABEoo21M9^!Ra?Uc@80i=_UWoSdow^q` z8BIrtT>$VyDY^K&%~zZYl4S*Bl9ds1*=>IGX%SrsS|!F%*+^OyP)EaGJw4&UhFmsM zIvrQle`WHPKs-e$_Q7Lf;2} ztv%p@@z$RagU8+aW&!~;s^UDiIg+(}&7Qtm^ELd2Ga-oeZ8wp zC{5cEo#Uk~7e{w))leYncNK@xh_tKDLouD<%7kP0Ci5IS`xC40yjc{dU=@C)wxCC7 z_|sRDQ*xsX+Kyp)BOQ}4+=_{bAdwMEs4ctG{7#9^qF%<3nb{4NU4rEM3vP`C64Lz& z38z4SsxRrZXJSgv|-g;OkXR1#>m+1(+<49jq*l3 z`x7j;_)nIl!kEwLbq*(Rp;~;wAnmE}k=<0>HP3dMF01iJmK>Lv@Cvfr=@xE7m7U&h z0`F?Nz-CjIDS_HOCQ7N?RIfu_X|Vh{*7$CAg>Y|h{o~2t?!+-rig!^TAY+^ll|#2L z9P!#89c1mHU*9h9uJYF!W6CuvOhu$2vG4z-_g853(F%fcULf}MjkQ8gV8j)|qvCS0>1di%0-?;gyq@k36dV!)vv-3wFTOW;W zuxIlk+mRuuf7_gvu+n7*JKB(2Qh*8yp-RZu=&pFiG5%+;RaoOW_GO+%O6qnW6+O?& ze&PA%ZGz$RZ&|KQesXdy++mMPlF~%^QQt!*Wwg&*O~33d#DcK06ZltIa?Uwy$2c?w zj~H;~$!BIue#R5VoC&JU4~E*U$+rzCFa{{Tg8xsS|GAUsjX zFh3vjE-%L|3*yUqnRAtLoN65WmnDaCg4<}BEV7~&ppsHM6gp8sJvD;E@W?NmZbQvU z>DhHIPwsW*Hdfd^AOHhiq=Gu>{ZnJdA2+;Y@t+LKvYZ91qSep#@hNJ}E)rKG9Gb<& z(&L8PY;Eqw-fR$*rD;j0^AXY9zY4s%xUA$D+M;2-(8pQZZL(vVaPJz>>#mj4@?8bO zvpmm;JRjs~KXB-9{FZk&y)QDvZ5U)ISA}C(?AO^-2*1sagoQfV)T+3;1fq%DB&hOO z@nU>~n`%sCbgqlJUS=V)!nOCTY_%12DD8Xn15f(ZQNCH?o(A&Mm+`0kM7Q6t{MO>k zWnFl23^v9$XSVjX(3x*x)UfIUWoZv2eIyR#gIepX;dveYCfU!t^S7~HZ{<8Y70EBz z4lQql{$1dn??Y^9o7<&Ls<#IUz zavSQFJ;sDI^eZ_7ow-8yW3}Ks^Ukp@_KYSfBjYdI6yoR2gj-6PVb|aWl%ymC*W9Xl z*1PJr2a2$ku8KXi>s2q1=d%&*hUP-x1wjc`@8zE?@*Rf(#67#;EOIUh-p&P)*@|t; zxmn66<+9-#5NJ$DK_qvikatK2Qth~WHZwX}`CZzmPwqA5P6j|Udo(7Rk?{liDt@f* zBgL|aIrak{RsojSE^=M7L2S6pt3=n3W4;1hNcz5_0H>)3TRCyI_g3Cu9L3G8uO_v< zG$oP^YChkJwEjMQ!+GO_^Qf0@Z?iY#HXo12p4F?3vsUCY8Y9;x=p@BLn(az}{VFY} zP?{a}&o6j2rQ1Ki;LWG9>{!2aayZF{tskmf-5UPY@6i3c6XwPD7UOK0Co|-)9$e%< zCUHk+D}pbs%!dmXWEHUxUIOfYYlYUb9V$vSw?6*3Y!(4fW`!NclrS=(R z1en{UF_&KTRPE_eal@P@^+Rabv~3mruRDruus{C*zxkeB+fIYS?iBAt(#pAO#Wb;t zafQ>yTz$OK6w!|P7w0r9QqhFNyf)gJTbT5AL#?HyyF?+=+u2%>cRFg%1Cxf}%j=Rm zHouqsDx+lbSV?XD2HQ28stf5~hTuS_U;C(g^ilQiz>H6eux@L;^4BIoMZv=5!^L*( z3jOuhG-f>n?ux{e)cxtsmJ%=#tnX7=_i@Aq~5zG^wXoW;zWHM?@Emz>J7y4-u6#gu}WVGH)s zQk4XcXo^sQtzJ6nBGXbF4@Hg1+0DJJYh4?MTKDiuD&>qm=LZkAVq6dJ%f?uei|K9W zpBI!Wov*xvq>x*!ejDkFXiHv6aU;Q6KFzNH9j$Qd)Stga726kTb`9~R+iq4_{^H)Y z+#8IwM~Z?;Ax;$jq@9IzXfGlM(`oY*+_<^O^@!ZT_$PNFa^^X9y4&uSJXijUj|$lm z4aPeTd!k&J?e;k_Wk4SHD^sbKb^(p^n$`;) zWNTlCfb5`uHMz3w#1_=xc-7UYIjvZcc9{`KS|F}R^@L8?$cxnyrF2SB}J zz_D`83tu3iaOvQwKQ?(0edI1Q!{Xk;QF66MN^a^v+C%-lq0OmEo$0Q5n!hg4F4@NL zA}Kd2KZsbaIc`O7fp3ht@~(1QW-N4$6h;_xl zXc{i6xnsll*3`US<#YCMFCUmi(~vJM1ds3etd)O4D`0{a%zxAP>yWbQdEOiS&OLqw zch-6~macpB3>puT=4O8?W&0w%XYwztU0ulJxhYSfear82q6q3cMF98#uJvhW9lQ-= zdj;hy%hz38x#pyk-9)z=gqh1q9CWU`07;-4Lx@j78Sbv95VaeHIA12@S;Q(lR7+qqnIeQ&xxunX^rn#aC|545pCwlUoVm|YDk}~jq@J|Ubtsn`D3`9vL>R5Z zVAHq``c!YIEp+7iJRWL?Ts1P5y&SDH;Tz&NfpbhC?Q=+$l(nuN;SGR43GUBZB4z-O~ z;@c45D2#xWlS-8lzxeBSQ3N5QvgGB4=5q7hL|H~=`upQD=>%*k>8#EEM1w`O#NlbF zQESh-`-*YJcNO*38{S>jY1qKgK!75&`Dv#%sdZZn{o^2Y=7xN1@Y>xTvA% zQ&ED~Zf{VmzE>}nJfe2*%J&H~IWv&(+e8Tbl>I~ZXRUUq{{Xd*a#~66cpql2_b=dDIZllK!54c~(+hYF!X?@EZMf*2twNXocQhU;u5)DWd+PyV_VK+0E9?O3u zLfX(r-Eu1Q;EM(MX=e~)1r~y!fiUeSnD}Ul14H|A!bL1=AAyzCMgCuvRsZ;3$ zfxp6`=(d~#iZc_8WDV*Dzdq>?3dQa@js69e-rsZ2tb49{A*Al5 z6Yv92Z9LAWV6|7%h}rQf{kkULy{pDp!}%S-B9P;?6B z#v^ZUS9Nlv#ZJzdB4g?=y&+|8I(n$6l27r`M+w}Xuap$5%VMb%)Yv1{R$NV@`+@%e zwco)!Udw{#zHxFqAe_TFFJrtT8Ps-|b_nhd-c3h(YE>=t?67ECNOol3z)> zhOL(pvyG#Bv?uN05ApFGSJida#Pny}eiP)PyMSsbu5AU)O$ygtT2sssNOv;YgEJ9B z(xg(lwlVG%3Z<7}rj7~1&|&0-u&`8m3h$+ck=LqA;%m)Pl5K2B_Qb|hDS&>q$P^>g zZ>dn<%Lww46>RuOr9NhY5$<-9tb>IzEH?*q+U}P)N{Y}=)9j&W?I2U)4z9QkLO615 zmUCJf>a+gqSxtAx$kK?TKp{9$`_-;tq8`VWuD-splO8|E5(|s~w3wvdiBf4PZ81o+6e!@s_ zkLvUl)G6)38ZBlcFzTJ%6Dcm-?JEK+t866hn(g_GK{1A{JDCN*^8iqDE*6WkWxR&W zhnzo2lF~v~Q?+$jEu>O@pp|cFac*mGAp^klOsl6DS}q|^khqG+)`#jT2entl&GiTHIux=OA*v9{xgzV-8abKn_ZG) zue~Z#6g_|VTB|X=rmFANV}%oj7D+xRN?bncdsG@8nw1s7uByIlTyNB9Q?@1c%Wwgy zQNvCvWV_G<^VEv5#)_@>Geyv&axuGn=E~V|D?*Q@G{da}x+d9>nlq{`X~z|0xkkB0 z6Qe3Z7^U4=b&r8XXePMcv$D!V5C0Ft3m z*-<6Lkl{yqRa&N%6yI5^izb&9)va!Pqg6WbOs-H`L-@?+-V7_WFjl1|y?>kItnKD9 zCw5bYdRR_}MPgUY7mFjN&B@r!7MceZropOd>rt%r#yUoyFGZ%ua#*Ake^gFR8^9c% z+Ls8T(>?9SVuBQv9nD7H9WvKW&Z41OzTVzVK~RQe;(kL2J2w)Ri?lS z$u#v<@?zv~O+v0$Tth6yq^Y}|wfL?!ewBo%ijRVZtywdB9aVhr(?S!+5#>y)7m$G( zz|mUnAP1EO!}DNZtmBF(N5xdjH1i(-oS4UnQy+N}vb(#FxD8xsDm3_=&iYK*FZ)yR;{hIG zrr5Zr4DpKydTOGu`*g2*(o{-%_1{q3`Q-_5h$1xZil@qcKZO4P_N+=MoccbN)Y=DN zCvLhQMrl_CG~a-HsypOvROLIZz4Q`kU{usUu6t^g3aDfAsVJcM~qj}r(9F66TNHxdYdyq9aMM&j*94U zcP3+2TM{F?V{&c44f`3bPyS_7So_uu)vl{uVFg=CD7%nx#b-Cw-Q^%yU=jZS4N6aO z1A5SEd^smJJ)=~acmt@kxX5f!9R(|?YX*6Nyb%Ys_ATjt#4wF=j6a%{IqOKdiUElMZk zkWQt>4SFVIoHe>Gi+Jbu=H)(Ol^)}k{{Zv}jjyp??trOIgZ`9%F5ag|xywZl#HwRq zcX0YQLE^gofOrqZJUbDkM71|`xdBHml9C*CXXEt>4OF|5K2znwB>5m;EfTcSC2B$|e-%6DZ;>HM@=2E%m6Jci@U#hk0L-|e}kJ#tHJwD3s?55{?E1t9g<>aP0@ za_wSu7OxY_?a2QCBS}6!AD{A34sGL)5dQ$tG4n1w@*drLnBvwY@%_RZkZ#N)xb~8+ z?#B%&Uet{hyffEqf{NAwb z5VW79kUeN3?0+uiulCJQvP*uMfyPSF+vk4Bi+m@(v4jc)ZHOn?KZ! zwJBf_uSA+tRrVIMw79+12cn6U$y+)`dTcCrf44=*Ta0gNW9X+v5I#E0T{rxME~$=` zIv`QI9MwgT0Vagfo}22nRt0Zv@(Plk9^xa+Z}m4rx5KWg zR>#s(eNqOKp;k-^wsK5&XFg>(-(%6w(yCIZuG)4#1sZo9)l)|ZUE<|9wc`i8QS?62 zki$pNl%CQ7_-m=~o3`CsKH2@cFFAXaWa|u*FQXb)RY@~kar|2%xmo2Tw&Z745J;s{ z-Lcms%vl?G+{5C%FUE4sytHJkJwj|lbjOm~>$P|MHAq6$#no+fV6^`LdPQ4v>OuJ( zBxs(Mszbuoi`Am*jy=1MAiD;>jwgM;5vbD*uByDScKG&{8p~mE-*JV4l{TW4kHFJO z2CA-RZFLii5WPELy+WMQsqCzGeADVQ)W)h!jGJBXSCfkJc5p6KwoOv0is#LSodfg| z^%MAPO*IbnzN~fdTI^irDJj{?De|`fIJRQ^$juRMoTV;1l=NTJH`L5b!OWppu2p1X zxZFAwQPQ!~;p?jcvy0yJ{B%ex?oY=?f(Jx`_xyAcnhKw9=F7 zG!R=MYC)(v0Rj`&&2z70XOs#SK&rpXPB5>`zPBuPjST<`aHaU=Op!%ew46Os!^ zQmRdKN$tTqF)ONV*8>(-_WO!`sc1bYt%0Y==rjtWW~eFA7i_smiof_^{{T%}nxyp5 zPP&vtr8c2Q1wltbKaQP{R8>c6TvNZ^Ep5sF0O#01CFBmJJpt4<8Wkz++|=D!29!0Y zT}f5sAiLA?(rSVUTac>Ky+()>6}IrZ$a{y5JmHOIDqrrgTawMUIUij|h+3#=Jl0(k0NZ1z2 z_8$+!QSmo*@}8kex^LiB8HVyb#1eZ}Ht(k39=e`gRRyuo=(>gBXD!+95U$)Go61Z0 z_DtHEw+B_TE~s|Y1M#l8hdRXVBIZBTI!Ef>I zMB-bP%9coq`qkPUNLIif=GBaZhb+8MiCeU;^XJy9W^H16OeG>yHwHtHy)RSN&B5cdV+(F7AVHofaeV9aGLII#n!V{oano5ga5r>xS^pW9Gk{NhM>7oizrh zw!RDMtA;+#KW~2$GmbyXtX5}pJo4y64dWL}r|xbSS)o1Nc* z&8OP2em%OyjSAVvZ92p&Y(`Lmw;W${kH~Sl`_2=$ap*PZdz%f7uVv=!US>CsjZgg) zT9(9j11^Atw@;_zs>esBs`fd`Uh%y5!|ov(@ZK`WNATrG^Isr{{XwwN{|&0 zyHs6aBB@uY*yu4h?y7p?-w^zh=lnl3&NyF%ul%Kp@yb-Z+GUnHC9VPz(2z-IygK0s zJ>9J(N~b_|Q$HcHf?rBUfaBR{?0EI$(3xUsa|cuWsn)-8kbEHW^jxbAi z8F89&(VlVb%iUe8BgdsYkv82jKYM8olqFuntKFwP3QE$deq+LI^LAyV(9=RY6+eb^ z3yek0LGlyk9yG7@{;TU#%nu9vGy8}59XHPp0JxSE%(~ladHKA1X!dN*!IKGH_gJ!_ zG7{LE6SYB2k@ZtfRAOq%kb8oHw*-8(?6%Gvz*mjrkO$BtrM6O{6|%i_mpZ9I%Wth8o{2VfN=+J( zL%*)1g%!zBuzoeCT`q}|j>lL*1grl5eXfEhjZ^y5lwWP}+=#`+$8C~FWQ%c9izO#x zRP?CR1_n5t6E3-Wu($6mc`Gu-Fnlj7R2z&AE(|A)t_(qs65(wz)|DWcECj96I{^h- zf-73;mAqcxaXEN_RMFJ@>01FW34B- zh>xw3lF}SMNO0^AT}OT}V;Q;@ZU?fmW=s9c!!D%S;4~z6@a&s6o>|R!F7uLcu2Y6% z8JiFa;XQA-M2&WiT1u*y7=1KSLWk6ar2ID1a$#F`1cAklJ_-b!uHwzc0@l615!5D@ zOUoP`l-TixA@IBWca_AL6_($v*EP$BHVFYetVWYS)1#=~g!R;jF}XW>?UQ$fQ&VPf zHy{T?vEAwRAHg}ZX#OrGHwUGdxd`zTI4_f>u0XoJ)!rg47`}4X?V65-smATEpbupzaox&L zevp0|=e7{K5wz-8cD+cgzT&J*qEO_WOdW#v?Lz5bbe}*ci+erllUAt6$Lb~01Z_D7C*|9kP0FVu9 z-{toFdaO^oKHJ_~T|VM49TT+_?ep;b`fEkW%WR8Wl&MhVCH5=*Cq+^e0od>H8tWUw zn^3r1kd~SywMdSHkQP1~G=SQqc@vpwDg}QafB0G=LTl4X*v*}(?yl)`U$JV8dz9hZ zx|`5fNMlV?PH<{L%<|dUWX7x(L@5@S>+Fpc6#+-3O*YfS*F`C<9ZH^XF=payco=M= z?Va6{eaRw<{i+ry5}ms5ry^$4s)Hl98l>s?{JW?(jH+7w*$OIdDg8>RuS23T(LB~y zqU=|N91nivjuEp>v05K`Gj|E$HuUUL5U`(c5$JmDu8GBY9ksh@A)@k+y<8gp{`Y(h zE?4mVxjB;egC0vm&@nDQfgr@XWtVA)jTNatga(CrsHoDOr&~vRy<*7(O$EhrMtjc>(OYuAl@-(rt-xgI;Y-SL}Ll*L|I5;~uN`-?P3w6t!ZuR;d3QFH8L$Zx0v zz9n<(ziPPzahX1y<)gS2{{WQ|%(q-D^Ix^yZPG0_7>1Iq@|kgmkbVUYgs6WFY+<;H zX4W?c4}#~H)>AXqUrvR#nL>kvk{ZrRPy(z6qa4k3c+lXq( zkI%kHS^8On4ot;Tw5=nddsOv0fpwF&gZ}^{+TWR?x5n|?%ewk8x%BV^{{RIo0e`rov-RO2cYovbBeb(4rEs5%iNotlYfs7Tcdg)EoRh z+AYAg~+ ztu&HFaTp9Ws=)mOkW1$Uq;?~}#49DkH$Exm&VRN)s<+J_O$Zmx0m1l#K1yEnDCD-8BzGWhLdhiZ@JtRIy;kH9<|Z_9i~ z;|?vv`TqcpEa^qZ1@aQ)lEz9R+@Af^s7jL^0WTK!X)3R8Z3O`u!EuZo{qs%B4sPjb7;kF7;Ut}$2$En4X0X6@ZiRg>kMs^=ppxPnhiwtxYl2j}+g ztcMG^+I(Yfwi|MqS|6+#a`N%-MpLnA224G9X=KNtKp)j7`G1L+_S@1OJ;X@*WVX0PoqiQcjd4(GG`>9G%@3l_XQRsS9 zZ>_cmltGkXE$!8Fu1UczaB?-1Q*r6wehD9nd8hvXV{$Hxt0KMJc-N3vLtM4TFr8(V zZ_(qWU5v>Op=tK_0bw9_Cu32m8JtC&lzlwjJ=ISf&fmpYG!YZivHt+7zv{z$gyalD z)#c2_B5?N;Vp6^QYnm~+OfF4=*rL-g<2ex_l4Y?pMpjeWcYr^`hm;@5!}Tc<%*DU|sD}l7jORZ!-x)0l8=)w^{8aE8DqZglhQz05TWT zdU2fs!E--sP8K0N;-&q&4~=R+55lUk@K2Ia_-%+5emvuwP2Ug8XNK~NUmymj!wIl&$@AC z&>cEd{K7w%a4t=N8y;!oz8Q5#jZzZ)j6X4HsBL9#*dA#jGk>>ne&TylK}wdI4yvZ2 zN6L-ci)}O`sUt<8y!Pl=HM3;=%p=MgYH8c;_vo`0;4Cvf#P7Vt;e`W)G z&Mr|Aldf!5)%%ZSn2W0*6!xXl=xN$PuA|G#ZdoX_k%jgC?*%2k-s2w=vKI1)_WuBB z?wND#GQ5k*IOmIRSX`bX@;)POEB^p#=HGRgGt|Z%$haYUHJ}t24?joMmY93yIz2c|0Wz*j$y# zQRcC3x=XW8pn~%d`&j`kgo-G4cYH>xIP6T>Bkg20cVpP0%qEg4B4$D=wStAf^>5uo z)a02zYD?BXMAC$_R@D%PDmwAQItmJxJ~K)s#8hy`8OJ9``%JQ{LhD{zm@*Y;%ZXZh*K_pDOI8IE}UnoG4TO0J!%7OtgZUB<`Xr0Nm*^ z-$6TDHPvEuQX#Xsw%u!D8*thkMFoEoPr*TW3@#7j+!+|`j#9sI=QrFIm>ikFS6gb{ zF0F~dY%^?`xM{VgVh7bjL$TGB)veUh5*j@x4!;i-QEQU4g41c-W{l8-T35oo6}#e_ z?~wVAGG1|v2gbJWC-M74XKmPx!u|VvQTr=jLPf?rh0?E`!jg{Uf%$7KnvWH25e_7{ z_XAa+<8tS&E}JAx4Grqjbo+hE?|BK1@CCb$c;_$V{t>&{XIq|aj!edJMw+`?UbMPa zvZiE3r?>*jX;26o4PkM4%eFYj6uU_2RoQt)4#sHNaWospLqSzu=lFYqO_6GaE07=H z7n=u~ceXor#oU)P zLrtsp(OY@=lbr#5>rh==>_*0*^scpde|+ubnbEz3Q1w-Le_(F0(Kg|dwdKR~^gqw| z1&tp%#h&kV_Z8f=6aHG{nF(|x zomZ;({xmUyf{HQB?zC1tz`+rGT`c>2L)Qcy7#Cq$I@@&IyZiFp51?XNQ#?7_AuI{A;$y~37 z*=yUMpc18_T?&Bp`0HjK;^oVO0SUvzTzM~(^Lu7C+n=#&A-kPrDcTz(IS@zXyc%_6>JnvSg?<>Hb zJ9yLQzbx_3in%mQkA~%2?3bG>aHg|zi)EW}j^bq78^IfShjPp`3L^1hNu4S@IEeQjlBBl7W7SF_Ab%Y; z#yR8$q}*IGCKtOv6z^)s7{ORumjabv+({3&vGzxx2ji`MzCD&~+;wqW!tXTvjFf00 zgY!&0izRj9v?aqubQS*q6HQZOZ;Oi5V=-ZXR4D1kR`SZ$d#mboo-QQnwBeuyrh*GJ z5LXP?xx)|MJb3iqq>{Ynh9X7J(2Oz>W^~W2<@)*u89#VZ4xD#2qPK@ zCG1}-%CJ0!)0trT9IRI=u(+44H>hs=ZZ@>3Dk)k`QsYbQ0mhaJ7B)~LPCD2~)cjX3 zjMOf%c>9*|x0_u1h+QszDr3Aonc?xx*Os|I6O}p@iumMWrvvZU7g*7xzU5Z8TT$*H z*V9#3Vb<{$gVk#=&^Gd5;xr1Vd27ngb5A|w*QGojv{>C#8$k*M0yH98Pi;L32AVuv zATc$WxZNQGpj77XB~qrky3_;Ok@6iiYV}G{1b24?f!D5_bEpxqrOu+5)DAC$G}!)} z?1^2@4vGw?p#1BqDC6~|uY$X}lj-1~?xQ9fQ;t{Sf6rPLGfD-Il3Jx%RAGXEYE}A& z!&9S~_W2sFKNjPYu?!)%W3W}4b8-IBh_ghOBR~iKVp4rmwLm(uyr1c9 z^y*X#2o2@Vzom%>pRz_1F3HDMcRQ^3JaojMBels1{{V>L?dnO^V%9TCo~39ne+F79+mu*aLLfYH!>hhjeC)Kz|{_AUmnc>RE39CgK3^mk;fW^M<$aXWRS9aiozNnlgw={TK$HQ-Q&CDqwQRYH^fRdCSq>Z(8 z1;kGZhT_6#&WUyn^=wh83GW~3_0p~_LxzdCas+5ok%@3+q#p2Af0XsrL!vR2W*o^I zMIUGUVUA|7TxRD1xhZiaBq<=$qMoDYt#=&fcUCeDrj%Th+#hQ3`Irl2U^QG=@_+62 zhm$_*iNy_-cPYr}0EO3au<1>EBY}J01;Ye$omb1Rc7JI1_}QIvkLlKngWSpEW#bu} zE+x*=xH6jx`jms%pglpQKP`8AXItH~d_)ku{{W8VH~6{z3$~Uxc>e$;L2~{)l?la_ zF+kmV4M3XAh3FnT%p;U_>Y!`IfyS1yw#@;eYC7t@@oKuZJ1iVDDmA=VLXy3^6}=Qk z$5E~=K?`e+%TTEL;f~8@FqQ2UMsSdq2gGWb)STKhc2ZhqSNlTpZyvrgKfL#eKy z7|On7!(G)WF?*jGA&Fr#$v5lNN|D` zRX|&=pr^{@#bh(zdW83naIoH6?JMkX{*;|cZR1=OL#$+s)Fiu> zAk=_V&uY+ro7em_)R8pwQsK%uy;PUNSu?zAdiQ6xAGR%NQjtJtdw{O`qrI~370td* zIeMwPn{T*2Yiyl+f`;VDaiV(-JA#1gLDGj*?zc46TUx?54W)A>>C!DvIJh#`9rUYW z3X*AEwGLZQR_V3>%{uB8iYNu^Hu zlO61ljZ);WicXzW8I^eRm{?S|?SJ#}+coY3dJn@>p}%QV)8Ss;$|pbdCOIIrDm&0t z(dt5lH8Ha)iE+l7siO<$yY-Ds#<(clVU3oDH#F(%r{TM&RY!A-+^0v~>wxxL-p)x5VDw=Xn8cxI}IO9kzLQ$(u07#+fT}QdK+A0<0jppqNi9zd4 zi0i0eRZnqWMV7?z{gK4WzD`Br>T0JPbwN~Bcc41Q+yVxHY@l-oT~pQ0T*Phm@{4mx zJ)`MD5&$2=Rj!1SQm&YH4F-w;%K5t}xyW`6un5stRG!U=9~zzYIhz})OdEM$py;a( zbBIg46<;n_SEN3p+`jdaDe?ntPqVsXjVPI$6!r>|n0R_E_FK~r8hcHs5=o_Kb!~GK zT+J1H3@v)K_r6fGs>S`j6h%*vw1AG(R3w4%)>y*EIb91`WzYp#FP8hJLIAcvNx%2G zJqfo*Nh%50nrdS*`#(skbn*W0(IF|UFCIc)kWo40RQ8gU9k-=4^lhGFu8D4z*4$UE z6ISiZs%0WAf(jxeR0n$ir_XIzGmdLl6`jB4G!%>|(&?6DHR)xZKnHaK}|Zw3MPq0(1NYXY7eKw zM-)V+C2$E|+mBuDZy}p)y~%4?`cQy_K>nHD;sYm(wa z>Q`xJw5xxpABL)Lb09``bqdcNk9m{MG$ko{(cp79ZZlxyOaeE&i9xY>vNuH(1rPXu z3iS2UoGX^NxW^$?-r;aK+Q!QYjL8ey zC{-EG?U$Qv?sCj`9${qrZ*E08`fF8a=9Vlq)Gm%vRDT|A!XjLC;8N0R0+bKq{4|Hr zJsghGsD!-!TM8|t5ABq90YyvcO$Ni=GWp0x8Gh*E!h+OD{ZuDTG96WXx06GQDE9m8C12pj1uA?JBqO(F1Kre*TI!DL83XkNPO;<{5okqKmRBL$a!W)>R@N0I zo0}!Z#Zo?%3L1|>J{qMKqB$zkv@ObgM1FRvv&cL>j^&H(3Cs<~x8DqpRUKX?@MsPCkzIrgYX2YOSkl^x_y8l6?U&BPQSXsi9ph9$PL z(?w{Ml4>0KM%rWPL%XS#{Ehv>Z`=6SC1ObpFt>Yu;v;$y>rF$J0>h%0E@n55iWSVP zvMm=`EJAtdAD|$MsrSZGbBuSg~0t!b?x}<#;^u3G-f56iI)zOZ~}trV5y<{s46Eji_}pr)s` zku;~ER@;-USGhQF=CV>ZxGX1cXS6( zeG;S8ZK#oWDLFWQxOY+VK9vRfqtj9z0_OP*f?7P=e*Xa0v))gsI(^ilV45R>I#QGL{u)CD zsMzF5OPO zg&6^El&f^od%vQ;rQ~`PodW<;Baj8{+Bu6MNkAUco3^pgr0K9vMBCabk;?l|)O4ok zkXuza@DGjL>*X$Hu08Sp0FEbeMp}sLl`aH7X}04=L7{c`q^Y*sO?DMkN$3u!v)rc3 z7{Kd?27~$izXhh_+$#Gff56(&METYsmT``0kzje#T!SC>Q>HdO?ffG@4$H}k;*)F(%pdKs1-IcQg#&;tu=?W znoK?Y9sdB0#daLTEaX^V2D30OJ;{kS$8%FMT2yD_tfkh@#1xtkN9C^8;>nl`dKKl_ zp^^!U9ia=gz6yC!ChKQ~!+QScAA|3BXO>USFpEYVnsKZ(+Bw49yF)VPT*Ij;M1UJ@ZaRq}dk>D?b&%x{ z$q+I`;z96AvBf%~Hv!jLuB7ni6L7rFEx~SFZ;vMr$BPWl#~?1=h;m~x;(MD)*>MRd zJ)~?!b(+UtOd|!)HCNY`Fg5!F1qXuUAKdGnE#48!w(`=XO3%6O#<%71ebXp~BH@gf zbCdg3+gWKp^6f2Y>q;onqop<2eYxZEWlNY}wQ>)3IeTyx?Up$AliU7PQo;WAwp@eB z4hG}=ONt}O;u)~T>&aV_EC!|9ZWAM^A&7C%LR5r4QhP^GeQB+ZXWIy^Eeo9BTow}z z@3fcMCdSB!k3dh{b^afZz^=qy{8n?xE_LD$Gc5O{*rj3tQh385eoK+vVicBxYm6f* zfQ6=@g%9%AIXNfA5r_dr*dK_smgHO$M|S(yM>C5JpCH6?Ja;FCo3*C@00sD$*3^Wl zPO$qWm6X(eswmQ$$w8Hp33m$hTDRlbJY}b$6kj2B-}`uZKMeBk6Ut#;?R?4NeocNd zE|6q8^c(e_%Hq`VVo!Ba`4tH7K}n}tp1r3t@0G;Z;R_o_&}zK(i2b_R+2nm!J9K*K z^ZqL9Tz~E7@n+TJ&n9RQBH`1e~2A7t?@ozfBI>^cGRUpGEmeWiSbh0FOozZm>(%4PWf020D?_7HP?FK-sD7;{jg$2N70fxux1U)2dn=aZ)I?Wf(AMgZq)TOJ0JuVjYPsGo>h2%Hz|L|eqgxq^nBVo zv2z@4DQaDs?1y5iAAFcnQl`}EP^ux+fU!U-K&G_$jwfM#Aatx7hkol7gXS05;6zV5 z4^jUBRax3pmfAxfp`Gcz-yXk?wBC-K%9p5<-3*k($#I=*r{&h^x_qF;Fhz#E#z(^%`TjE${2L~7df%1D7@nL7DG~) zQqmNffK-(wK-DTrib&{nX^+UqaTt3w1y|&4q_&vFAftjpO_u7me{?N!T!sk(Y8Y`$ zg(T2|w4|qR$3gS9y;Mg>CtbQB8@kA1GNQHgp+t9$t5A2+!6~Bjd2v`=2=@jqDUka6 z{{VFX2~c`JYP&~uMHCb_+fBCOhKUnGc7i*jfw!E7%g;kGll-m_QXKiwUxuGPWm7ym zLN{#46=;QVw|{Dnb7JVWl_hSr7W#doP#bifA9-7h$(`l(M`09q#%5WuC!1{0902K z)X=O(Ns_SQl$)a51GpgtMM)#!zL6wMf9e3KFkZcb$l^Zq4c26wqSxtcL zuPOLpMF=%+Lq?{bigaiO)p#;#Dnc2*+syQ`DBQrV*OB@9ohC6v+^tFqoo&1@+7ojv zEwe_F`bbzG0)2F(->L9P>a^L|3p4J%_}NKefJ%r5rA~v1BSf&!hfVDWWl-%=KeoqS z&MXhDew8ZJeWg6o08frJg4M?*gB>jiCB>(@yA9MgIxCr53`NJXm5PARIAHN#dEaEN z7x9lL!}5+9@+x(S$y}Rj4dB>y#!4BJCUT8d`$0-i^^$;+(5-9JTptM(H}}%bD6Kj# zLh_7+*sBO_;4~!bPV0vLe_K3<@K2VxD%->ympb*w4c;y`9EKX(X5nL<9yEs=Z8r;J z;l|QjZ0@iUYy8@{;t*qRCwLk;rA6miEZ#oaH|kjN_bv`!BVkzt$#)32oNOjkrIoo3 z(tVgoT9s9HFuH^$y%IJW?Y5AJ+d-=HmHq3be&RJLohuo&r9>j^@c4HD{$o;~8KSNd zgiKqkHsTzaAyZ3qDM3&P`0q_2k^E7a2}xI5#lrsp*>1PAS&EI2%Y8pOXuzOEM~Zgh z_?qo4E@R6?M^=+r3!zd)JqO26vA_h=%bZh;Ff)7QoZ{DIj+L$3v+c1RC>03`JJ1h+sndI?6x(=!3+dCv{{Xlz5O|TtmTnrI z!7I$%!%|ysCt_=CJ1CRf;c+f>J=GsQU)y_oCG>mC_SJDdP4AO2SI&dd-?zSs#ASbc z&NjQ_sKM=iD`UITMnu@bcRLfU5fGF?%^9PLdE+WzX(I)Q@k|iXqS6 z(E}MyGaJNY+~zRb}QOL3f(jm{4}=v3%T2$E2@6pDir?!x!bnIvO9Ks ziT?ojRV?z$?sVUk`0c*abMe<2!E$?g(pt_TEpE&y0-c0=P3=y#CtW9laC?k1+~!Q@ zhYp9g=D6p%-r#@i&@r=HM#nVQaO?a6OS|%yfxL+W0JK>!Y^#B?dwfXjXSw3t{{3sX zKGH#T#KURTIF-1n(Lo>qN{w6E$H?K{OT?Aw@KsmV{4(ss3@?N<4b(kp@D=OwUUSDc zc!hmx)V=`WOP&n%nj)axX3m%UmM-#=LQ^%i-&2cgyi}}@#*S2w?{I7dP>Q7PXzp-!Y?ZQ1 z(%D`#CqO=HagXKK^IA;XLL|0`l3E2^>T6N|08LWPOV7RxdBcS9Hw_*;i{qatH7$!{ z{Nr(A9AM0Je^x+C%q=N(=fVi>bdVN%zD2A~A3uPR(uex7yHG3HQ)f9>6XtNoZ!~{R zD|FpT5Pp3XZsiXNc_+qP{?Ualqx!s+=tWK`jND>cqfk?ef9qU zqcg*u*wadZU+SBA)-B?Vj>E)JKk6rVN63dI+%b zC5F(45WdnJtpTp4SY$7NquZeX{wfzI!Qy>DZQem?EkAX2r;o^xt7{y!VvmLK{yl!- z{C52_8oZZh^0|)uOYQE^nMF=VkWjD}M3R&&pF!}|xB6LM8;x~v)8MjkXDx)yVPGOM z38uTh=lCwbT6~D${{R^?IXuzD`7MjfJ~Dp`fojIQZId`gorPQ^z9T8iV}9RhanF0P zLZ0FPs8OIA<#`@Id2qoL$8OVF_=Vmv{LdFH5H=ixUX?TVK({U>N37kM(*_1gU8?pbzC{`#EIPF%(nhb;|z}- zu=xGTcFT@e4~lVQ&ACm%aO)EY2@_(p*$qE6L;4A>%TW{|G&_7>F!U`1cL&c}Yu$64 zkG65$U74)eNa$Tr&|4Y+`5Qb*;+|I4Gs&*ED>vjzlH)y*+u%k@ zqW8fIj-)n~Ac~<%0IH-^>5T6Weng3CT1Q1|#=XpL39f=&uVK*8YCc+P_~@d)FSrvK z;M>)Ym2vJ#gPqLf`SNlMPly@soqn;f`u_lYh1?Y%xep~SD1{WD0DhWNeOvLqMrVM- zD|UgUO3m{w6w6!2&1~@U^I8tw{Cg!OWxS7lv9ZrQ>&Q6XWhN|0?c!V#<;CXi*TlF& z(;YVW$#A8WPm66E-u^~bvwh{KT8R(;yZo?Ff80xg?Wv4 z&vWZ5n3sqdE+EopD~abxml8eLw3?T#%a|EUMMM-50=}-l9V7jlZA&7I2`k^hO*~{@ zK1-P%AvDpn{a?bG{11x1JX7T>{NUyj;(kBIfBPrMAY0VN7sxNjL-#kN2k)c16^b8xa9b$9eDCz8R{0h7tAfW4ZCs84T@o)b!dLy6%umhF%t-JJY)9* z$mLqOme7Wg#aS(`WO4`5Xvg)cr zDw2?X8lPhPtap2)0nv}iWHOfc17>4@xs#rQQV1R=U+TBzp8&AjL9ZX@;oeN+9FEs8 z+2mYjNM#B{TWrQ7rdP1zh+BYrfnA92q3#~F+GY=!KI41mi(^`U6z?M7PO>bH-e$^V zZFJI~nNVDJxu?bZ&~77hxOz?4CUkk|Qe!(dJ15$0;1Wqv4?q+*^%~p0%EKcVacC`E z!fEo@*+X}@OW+f$3g!Ma%VTDDPD{gN@?VfyO~x#o51yZw`$LL58?d&t$7CfB6sr)rOmtnPii z#oSB2l=&(vFLt>Ft+0eYaPPQR@KU{p?s>|Wel&r@+4Pu*v_LXY$04;HE?winf4Y%d zLeix6q#n>l^rxtcY60x8rT5o5aiZ1D4?p*JA8(p{@sHgTCAM%_;wd+^ zE@h(Z`&hNE>kWx^Xm)qh7UZtw(Mgbgufn@)E0Xq7Np4HyH25u+6W{C0YZgIt&Gha* zJ(THk<*owqk0arm-V1V&zIMW*PLeoc)p?&23$oem3>kL|thbh=A*48l=%kJ9iVAN z$&(;rc6+0em{S+UxTcdG)Tu5svS_a4tO5#Yy?u1^?*!Vnp;wSv7>>sZ5T2es@r2Gu z%2{Q^sk?YkdiiuCxiR8})oT9$%z7kzb%W*klWPInsa-RIai?Bg0__?XLGsJ>j&@y$ zV3xdwHWiOpLlB$2&#(5(cHCFk<zl%2`^k9kFj(_87jnb79~hb>+wRc{z*@o-rKzWRPo6k^3Vw)w5q-XR_>OnnS*M)5$%cK?i@@hQHwl@ z$*rOLt!S66_F?rh%?{yClT~S`o90kwaeA#b$WpIYKcyvZEnC-cMv2X)sSIj+OWge{-~+I1x^HlPYrJ_H?z`DwQhMG9PCu+$V0n3N;fj>;m(MSZx< zAuY5CH8e^90;jHoVXf#A=D2i%hq6dmFE~Xir5>7+p!7zYZP==5_~f+nL=fO4C?AL; zKs>WhkoE_Sq*N5KBO#F{BENGXPQ6p!;*?aSek7eqIVZW4B~&vG$lp*X6FK)5`-N*& zYIRuml2yA!1rT~d3p5Z~=}ELVfBU*5@?O8m6< zq>hW-G>Hh`u7_k0=|{SKq56ofr7%?`b{mSCPl|pG<-CI{{!5d&{b_@aoznDsV`x!I zz$0KmCZpr5z3s=ZbF_2|qT?bKwp-6vE?LT2;alWRk@d^Cn)`&+U9_*S&sPwz0Y#Fy zn7P$a^y`s?B(x#%<=jgw5ja$VNGdB+t4$p;T4BsVOAwQbxMBvs`3f-}>?o@x|sp z(Qt3vS2=bs5A^XL^UdW0zNx?DCVb|5$ak6Tuf7Cj24FS~QifV3DWKI~xSWC642zsU zNYH)B{{U#Xn{ilp<3FtGeq~2^6@+AYRg%`F2H2e;w&gr)L^WEiUy#;j!UI~B(Ql<8 zlpR&wuw2-+xt#L!vFutn^}qUeikKjTDbkXMA3!@ApIuO8u1wR0_6tLfw-(m5{^F@5 zxpFS|X?H0rEHKt#y+T)ut5)oSSNX6~Uw`A;2}LzfF8J@(d0 zorWpg2VJ$K!t!@B%JqiNaLDAVnbk)2`B!^0^@`$C3U+z?_*S6c1-HH^Q)x(8LA7ULDkSvcDMsA+7ihYEo2Z0;BF8j)=< zJ$k0^ZGd%K(E!w+ZFNB)r&4Ewa8To#g*oD1l(hCcS_%r+S=*aB>J_vXW1&LLg%ak< zn_3d`SJtAsa@y`_50t2REMzw#gj6bf&t5NK+>>UU#H%}hxpgY`-%3&(Njn|szPnGi ze(l~^N(0^HUR3v+_T!Dsz+4$e_91-v`7z*rLxq{MAX}GmgtDU1NUEDhrGG7ZTZ#LP zVU~;nLtMUUb3bZx{7Qa>@Q;SHT#U~+Zs7rP5cDG2(oV%B5l_ooTZ~~Esa#{+NZ&zK zSaJ*<$i$__sHBszAC{|Rwq3Uo$^-QUMR&UcgJqzp2oyqTwLcA3PacbVi_15+RT$jt zK}7!mN4Mg2OC`NZ+t}r4-83ay*2z#(KA(=NWw%-=FT<04=%ct2D1Qw{eY&@beGu`d zTrJLrA<9xg>fL^y;C9nYk<2G2a@&xEhA!$+%uU%964+exQu`F^83SPG5 zHzi#WZM30X2JrgpV722*YT+Rb z&S0r4j;_$M0W?2X$5@QDtiGn574syHM(9*s1>V1{J zl7&EfT-ORJBV@kinAEhrYPmYlDiv%f_K*+E=-*-;qPJ#E&FP}ISO+m>5*bT(i(8Xp zpa8ePCu&nv(&9@^LcFx(Wa#xXDV{venf9l}ZK*3A6o8cie-WrJGRChXZM?ltRUKb^ zZ?jGJU$Z_?PU#5)snIt4j;cJ^COc4}JEs(8cwB=r+_y)vY6@%6f1a%^uU3BZ5l+fQ z#x5;qHn(xCnm1+DBeka-9i?WUJ!m4hYmF9C>cz}83a@fUAJ4=t&LM2}MslT<(`r{y z?kvbP3HvL;M%Q&$uJk3_q%GIxt>x7B9^>)(YbS3rUOFzVC7FoopV%%#Zd@6kyHWS) z^mRSPnu&LBQ&nkeFxyI1HKkppy>xKJXq1H?Y$YV=N78E(u)`~#(F=2DA-hHhkf9NZ zQ?#E=Ko_iGs8<6TTqx}mDOQUGn3rxd!;3X4Y|!-iYC|t3kWd8NklH|XH7hQhmuj?H z!xm`4$q5uo#UpzD8eBMO9_qPO-tx#IZ5h>9``&Gl-5ybLN|4%*=@kHq{06nqVWD6e zEDl4FTuo9+NMtThD|ynTnw|A+94rH(%Smweok~QMpxd;d?e)^g)gT%gpqnMdq$F&5 z4!UwSRH)L-67(x9*-7vAXx~z^u%}O5OpXr|MO8iRGy0c!iK;totgK z-L2s%gqNw>kf1jryN^vxfI`NX5z%Ebc}urSBd=B1T(iTj4)Jfoczmyx$96w6W2HjC|*b1V@Lwuvqz5L*VHhLMkp%}{kxZ{1eycA;_S$^I;}-R_bv zIPJ9&yO55+f05yu;!8s0_Dg1q5`3uh+aJ;-DYYi2_#JeM zTdnP&T#E9;YxPkj*2#`Z_hTv|6&+{lKmkAHsf4&H-${c+)wtjoeAtMJ+#wxk6ho;S zpT|Hs$qkf*+KQ1IGCMCFYf|B^7#;dP2=N7Itz%EuRUR8cW%CkF= zT^qB(b;eQO?1>JcX%4TV5=Yc^Q=jAv$3^N$4&Q>X!ST7Wa{5s?2mDPxkwm#8#*Sp* zY|iC?^Di4s!}3dIKXD2|5?fME;l^HQsSY7Sbg3kRsho3|*kq#)g~0y+6$6lQY_2Lo z$>SsA)P5~;FUGg-3)yqO0*c31idxPWH4zGqz-ZxR^i!Lng zre^jux}9j5s>O0hQG|=WJ2C@xr+e|-WR9bBxR6IrhNMS-Zt+&Q6$)6coBEjH^9Wl! ztHukF;#`4_vKqT$HyBr2Q?Ig@F-*-Q_S^CpQ2LPf^$J3%P^zTR(`|5n{{S7sd$hW`uTsDN09U!j?+pxT8fds8&m(Y$ z3(Q@UGvmv)ZE>^A2)fO6hggR(hSmvFOCcbIj=jA#)n&uv?xP!BwQx4_d`30&d}Z48 zr&T6nK2rFp&iCBL#gf7Q0NA`J0fu<8)Wb`97zH6N2i~rE?qYxI0ynfdkTWSP_4%*jmj-SW!c^G!Q5LCQ} z5`n{fE)E|B&mKnNEaL&V{_5HV(hEx2O()z{E4@+%wov3)@zr7Oaqooc6*Te_4|ZHu zvR0T3xS#~YcJ`?%0*0eZU&0>Bg?Gm?W;tj_RZD~>ONwds)v|!^KRR!y5&%UOf)HF2!3`>OIV_LrILBk5zMaWV z>J|Cvu*G5pTA?0UhXPvTUhFpIL2b08KFKwr$66EVsjX=(RMI?d+Iy*gk0&$9yjym{ zE-oc=BD73p8dMaDr?1alZv!)OIMV~aPz%ibx(DF%th zJ07(MeF{*i4sw+(4-B6Tn`&~3ooGmU8qZRJ)D1;=+WM=g93_IC!i-jcl4;a_zsE;a zNJk}R&p6d!9LcXTe=u&qYP;EkK?4nGV-*Q8v z(@Jt}u16!R+#o6`Ajpm?*Wv0k)3im7Tks`#MjojP_4?LJ)w&Zju(9l#Xw!XUZkM36wa71@31h5JkcJKMu@?OLJv*s<` z6qb(tb*Qg}DW~E2qhI$B_;+izXZBwzIO^CG=1xq0PGUU|bBIM6mb9dG9lfOd4*fKb z+qXaU8f_&y_Nt%zk@oDqCSO3_gU#~$e>L-e#5|pnaOWao{Dq3kTsyuwB@Z;Ds)58- zjy9p{l_sF;*4K9Jm5apD2r(_2kdcqAHy)BYx(%b3nJh;v)` z72?f?+*A>5$)wxlw`#hugWBpEeMtyiMQOhNmDfDvR@V2S(SkidyH5?uU06A?k^VE) zF#NgVi%iiEny?7t{*cSNRd9&dsf959w4UC8n(2>kbA`3EoYC#^OZQWZ2>`#g6^;F9 zsooQOa@Ti;%hJ~gu<{If&7RfAbSM-oC{XM`uDT9R$VkIg$FVnt8H1|6fqWJ7cOc>p zQpU0zrtqtO?aX_2c;Zx-8o64XUuqvHa48OUY^o4xzOhlq=~b>3V(HOHFB?&#{{V{m zQRkogfo_8D3)uXUhCg)?#*n7nQbPNb)oZc&Yiow# z7^~Qf!Q!K+TxXT?oW+Fxn`6+PrkdBlbw7nZD8_%metcz_K6hjDb1Q@7_uD7P>F-^# z`@OmvY?;R!Ta!r;r9)a4K#YKZUsGf_n$o)pr(Yu8SSL!%q}F5m5agP?dJ?`(C*6PbA9{A*!bQ%bL0!7Q|%bwsUJXHbUKf3jYiqX#)HhIY@@+S9> z-B&k@Eq3_JGQYIWP$*$;)P=UnjRKDJ*4HV)UiP)r+7DW;ZO6UXj<1MS8W1J6EBY7`Sr>iX+9 z%00Y&q@j!zD__Dr$NvCfY-E$lDt`rfxTnpZ9=TH)&!^zGSs4}uX2_kzZaGkEvdFu` z%P`!A)Te0KSGiOa4Z+*4x%K`s;?v+)K(WkQ-8QW?3+CJJ@xs0>V)(`z#9UXvw>y88 z7k}I2c1yjs*tV_ktPVa}lHAEi31v!Is7pkW57kYpuJ!DB$U0)@zO3*of_w8VgJNS@ zANecP55~W4v|l5aJa+3~ob9sn9K)X(U9w@Sg-BPx(2=iBzTav9V&Y1UrnMX+>j|%x}s8qrn~NR&!U+Z1g9D=Fd4AF zW|wDbWR*OZA>9cnd7ZmdkxhzE!0EZ0su47kris&-TCjHAme%FYk9DL)6YXqk(+(Fwp;J^Wwyj+pZT_niYawUCx63L*q$#1lwOoX z?H?64KguM(n9CzZ=L!?~{1tJuT&|Wk*m^A2?XuWXTw+@fIK$1UJB1;}N>r2c1ZzPh zyl-tD0ShAAm&WbUk`KW=Zuk7FJfAB60F`93@|)ABLM7hrv*lbSEw+bh5~&khal#Td zNGjNL)Qc;4+S)Jx6?OIOu6F4ObG%Nd&4WJ9u=tX#*!EE-*KfEXXq5%EA#6B>q@_Ti z)TnP$QU!FmEZF1b2~lLEXrMY2oqfq&zyAPiQ?__^=dIDx9z=+YsijLP+&cK~Ah>!| zP<*tu#1adENX$xG3h!mY+WLYIsM3rLO_vnDGMVnM`x71$7?6O2-UUzruh2y)zLV$> zn>9w*OldGn&B{c1k(8ds6}p^ChfscAx=dljqjuUOQSY~SB>u|os7@k)pixOy)TI?s zr>LO3saJ@2OFZXnrY0xHti`=%7i2o+w;oisB0VUwp7n-|f|L_Yz^kI)NAO&qBZEPaCfhc<(@TEFFBEO;m%@- zb#gCf?Vm&MMOW!|JLLPQr>Ig%{PpDg=SQ)ME)aSJ!N_sdH_LM4`e+?0gl;d7cMBzqV zk4{Jvt;uOhsal8e^wZ!Fo%L(kI3X_=l+j#b(Q|&(xkRXZ4*EChQnrLjW+=HqZbJ6m zJDMd)>S?65fTA(zlN!P@DNeS_Z3OokDN1n&KnLbdiR2EbGTfDa{3_rxmvRg^QJ5KJ z?i#;wE)+rTF%mmJQk-?lcJ_nR>z8|#$iAA;v}sk{J)z-jxVjHI(S2v*z8Xoh_)BE+ zvhgm^!#8}c*?GF{sw25atS=u#l^_j*DmAstb{)wzqVQ&iV&r`+J2tzq!a6|9R_XKjz_IS z9#^m3$D`!DCrTM(1$=k%mmJQ*?{6YB^?>15bbjynWh$ZmI`+ms<+8>c-r;=H$zvUD zGn($8l@%p7b*QCMl|ptpkVy)W@q}@!D^4Ey(v+Y-SgwtMCDrVf(RYDpPq%G~l(=i% zB!Ev%NCig>HBFiM9O+6>E~&szUV}zO0+`a1T8#9il_^RfpU1A+jb7-L`|%##9}Js% zBn+#dQAj@Pm1v;xcqMLWS=8g=La6Zl;KAo=6|&P^H^=#Q$x78rfstx z_QHCV`)#YVZPe3R)0MMf8=f`zg@KC;kd%F2j|ZQV%%zvHd3g6cDV%EIG91#3-dsP9^LQO`Wh;nT`_ zaNtXo&f~-BneJdxFUeo;#4_4kOKE*p5wU#Q}FGSKs{YuMISS7Z}rlBI3gl^ zqrE@sqz)wVV=VCE`;T)+j+w!>b4EpMlWXL=T&q)3@%JsyI2>)Jl(L1XK%^_VBvAae z)fRh>L44ZEHLBNe&QEiSl(yZ~VFYW}Ud2Z9d0t~2Q5P1Gj0lR%FZWERCCwaQzN>vC^uyK^V>?Q-=;SPes?;h65dw*C^Ao|qnLdYX?L2wjV zxNojDmN1&0z9}X(;@b``QOCTe9oyWys@<7|+#))|lcHW-T3YXt@|0HNB_e?Y(|Y_g znc4%qq)`ib_e6ZBaM1ke`KE2s!+5eh=0A3BR5inWEIAeC)|Sxv+V>LLONkUwNv%D4 z>Q$~n83h{Zl~Z+RJT5PMbUrj7uG)RGIMcyyeBn+|@xPNKK)^hto+jKa+@mPD3{9~% z3$gvVE~qkNsCPa^&hmq=yI5cTT0N&fxA+5KN0*s z@4a(-k)7mUAGsdg2a;{sJ~e=6(p|OA&hCEe&gS^Fu6OL6lNtAxe|I0Wcg7@w0+T^Q zzf#2H?Jn)dq#6J#K-9k;I{Xj)R~O_wPbp<|=3KqcW2>c3t**WuyfhW+mao1_cztK_ z=a4ufhWG;EI{yHeWsY2O&Tq>djW+Q!8)i_IZ-WTOS9C?TYLnUkQa@ExqI5AB`=M~@ zXLVcHD;`BXyKEzd)te-(OS|dzr{mpUPYt}1>IZn)TAzG&gpn>rSV|I=nD%Jm>Cb%J{Y?8Cp5RJmx!m zlM67)C&)$0%-xnU<;a;9N|Nzhg53e{-9!qjRhirW0HO_e29>6Y{{Roh{{YyRMNz($ zHR*M~aw#C-}v2 z^BawM*DJFz%NN_#_dXX`k63MSFc|7}Q7w#GpGyTYm)z5x-KV~)r9q~%ykC~z&wc1T zd0VG~x#wI96={k3rnncll54GN*i)~=uX=S{`0=~R-Zb&TeB6(TIAvVB&RKJfv5L>R z$v@k}39226>j(wHvPW@Se}|^sCy`JV%W*8S>`mpRqkOPZVdjFS>?NXyZw-&y(RIhJh!ZVpzej;2ZdID+*e?vwPS4K*>cxWE0<`>s^oHQ5!$5jlskkZ9RVaeE$FrshD|l0=Du3Gp<;%l`mNUhmhtDlgUx-OW~^k>ttd^04u za@FIVUd8V(#j#u<#<@<#APYQ>)|~ewr`ql%KSFzIQ}G(nS$!GuJ)y26Xigt6DTN;s*TwH0Jh1xVULuJ6jDPgAlrovDtln#QY zsnxzZ*|zQ4#gWTTHT3?h#)g#XPq9WaToaA$PjKVcd-dxUx94S~a%}n5#UNwJE`+%v zDoRuPhUiL?2J{-1Ztk2MK+r2IJ2>N$03hUV&y76s#yGE!miw2G{D(L9JV)g- zpR#slFAdms0_&2d7@ToLmD`86B>P(|C=2=S1$%(N{{XRm(BR*ilCK^$@;?CMO3JwsHL%<7D_c0_;@NU$;~H5&C+S;h zNd}wJnuU$!w%G$n>!*lX9OnkO#>ctj5PDHmTg0<6;HN${C-EffNYDbny0z6NEP#_! zN{uX}6s2T{m)&vqk8Ef|+7uFCg5OerM1gyvbY&Y*0O<~GQixtHeYEwikrE&knDH}(rgN`1 zVVFBLMZM`Le)}eev8YTaIO>$34K@_~^mmhe0N+3$zdQxXb{@psTn2Vl&+ZS&mh8is z8{@1=NsokaQw`efEHrA_Q!RwLt*Q-3+gXexWV6&gO5Acwk-O7xK)J<`&JrK?K_#|; z2_Bnj_#HG@XZ20MJpkPF)4Pa7&`DCxIzQ>$UgdXP8<%qdh*cdC)kjsdGDxo*Fpf1E z1$&Dmij^AI*H=sc1)P=DHBNY_OHo$o*n!j6RxqFRfz@9TlYq!|T`7*10eH=t}Aew&-dY%Tik8zf?=`7lxlJJew zCPql=KlQG! zC%NLUntE$Bi;R|+o~2%9CI&#!&?=1uovKIRetNZyn~JHG^cpG8fcQ%@@-LV;$AY-_ zJCZXVS-ius*wov8>5u!h{{UW6hx%hOmAtC@CZ4*Bmb(_vIsO4ewuH|xVCqlXxcrS* z>i5I`J!2j&a5ormzYfDquEE82m{#j-LH_`*z6;oy~RT*|P1La$ajv zyL+*3iV!306t7_93uWcgXuYHRUD+Er!-KY@ZB&!gY58$vH+wY+R{5rrvP}wm2=ZZ% zb{f6rRk-!o>bS%lHPEYeziy*YoA<1G&5@(HH~qHkkJBMPNK(Jv9=dwVB_sxiqN&Ma zBDfj~6%$=C$+nl$-O?>vN&bb&vGn|Uj+)q6a|#Er;Hwvk+T$-m$E3*>l+kkf;L;nE1)f~_{hM`+T_DCpGM=|5;E>i{Q(&8cX zI0zttxYm!3a;fa(1R7Cd`DXyN%0OE7gH_0XCOihVUtO7X$D<}Bm4zyaKr=%F=dPNfe>^ zX}USOw9zg`GC@ZQs|Uy2TgV){X1vYB?aX8{vBp;>>23E}ZcvWWdz7%1kW?w~(MZ#9-`a}%8b;91I2U}HB(9r6J;>ppiRklJYMulWcw5aO}_my8wKo4*# zQ{ib&D7j>Wiab5U`c|DZZJ1M3!|aD*oz~0yX>dq^DNx_^>{UAc8ceo~cu0atA$3ta zwB%Rzn{jV`=}n)ZWmP}obx$@nofXxtX`uO&4%2LXH+bo~v|D4mcb?Ht^7YhXZd#{0 z@;7u{55NccLZ?aykISgYO>dG2? z4bZAk{s&d^S~@eLxQ6ME>Y%lNx9lr`*#>$cnJcXYBw5}ZugBqlvKxKYayld^M8=B4Sl<>Kz*T#$mxYu!pwrpK{D8#eDxZ6C!6%EJ7{fS9%7I(enPU@7NpvurPUqK zuqZdv_IN2|{bf~UIi0)$k*H$x$e9JzN(poJjUBsD@Yb%^kHW!Is^_=-+Id~3fia}6 zOxFvK$y=peN{RH<)K>Agpjr7c62DrlA0RW?+ql%1Qi>FEIJ;EZd6!hGr3BN{*G=zJ z+#B63T5OkT>h@TOzSVV6O$YHhooy4IdMXTtQV4tMm9DsCJ6mE^+Te)l>RBos{XQC% zHM1iXaaFKiO>pn=gvfIDX@cQJwlD4$lR)bFS5@aA*8Y&K*@cncte~F0-DRKyAhLnc zqz=)jj=Ouxw}TrC1!$CPT#3k7YTa`Z%&wTy<^3|hVLntpbw36B3r~0FsaBGZpK$!l_PE=P>{6<0 zvE%Y_-N(7T*D03HTUVAps8G~#9zvUM-vhZ|Zhj}{sgc5&o@%LHH!ENG?kuYvgv1O< zA5_BH9d@+pl@YdsLkA6;?@c>LCnpwuq82P%r`gH;nI+honM zCSWGA+9+63P^xyQ3U8?}-uit)Vq0-1Lb(3`<~BIq3zkd$(``j$ca};@QVM&h3D;WU zcTSa^7Zv8Qwi!;ku4~KWi!!A-9y_hdlFd}>K}o7odJ-#IeD%2A(bVd&QMJcfC0l-f zjz}G=P;N%Uet`T1bdbVMicpEPN-(p$Ug3SW*=`e9vc#P8Y(IWq_@mvpf5Dc_ohH_G`)GU;U!sxyWTWu3uqwHwaSF^%UCD z>r_)+LGB`n>s_^mkKH^y)J|lklpP3LNqw8m#`e9UpwfgNwHMXgPZl!|D7-hru^%^h zs@Gx8u$wD(`wjBTb24Y5I}sAwQyr90j>UGQ7aOTfYC+eS?Q)H=$eAojrFt)9+;Q*r zIFS|oH&d_ts&~Qu0B|3M*X}vY?YUUZ`9}A14Py7(q_(5u@C?ENTb@v&(whl2Dj@u~ z*4rb=a=AgZ?H~n!#PDoJ5C<{9lkrw-%dZ`yy3Blv-^2W2%J-g5ShD{B?GhC9#EWgY zOiGbM4z49(BB!}4ugD!%TVLJTx~_2)R#{v{oynkUNq5Opu1ot)V3*uO9ke;cjzhiJ z;31pc-pWdtaVc;0sY~r8Ro~=Q#5#^()*>NMupSA`20b35> z7o}a$;Zl`plO5I^K^yi5QVLA~_-nXv&voIuXZdtqXXT#AUD(V20LMFr#dOEUOtX>g z-WA8-oX^BdKO$jR!;%`C$ZaL)7rO;e7yH#n_R3svbp$LQjS6N>6i^-(GS4P$woowIF>&J?29`+a?M+qT=nckk0}f2!+m zAo#1D+b?rjn~Te^INE}4Hi-!?ITg}7iX82z3LgMYxb256w0WakyXF4?3B75lZB;qu zZxOBeMmKzC`#RdXw;jS9OAFmeO?$TfI@RO397VyUz}055JX%=VU$lxZn=V1br{TG6 z;>SIb)7JOa+Uf`gwDlF#`|DA3ZSEwx z%Stp_O17d0ui>l9HwS2_@9nntP|q4<4`A7%5aBJO-1cOk)NS2ETI)~7vL8!!JPwPT zbNmR%W61vDs9Sk6g)?r*(URNIOKtrD#JC627DZ1@LiZ;J=}+pkd0dt3e;tu(;aoy^1%Q*}t>ip7y9>%Kofn4uvZEw3v>GtGKqgHyCguwDlsI zdCcsYjr1zb*fJ?j;+7ne6Qtg_@>5PB!Me{g48 zXKpdKY`BEc%|D3l56yj&#^<~@&(9SjWS+`y2);yAOrqN81;~@yM`Ng0S`ez!;yP`w z3uL(L+;cI!3O1|hHyY&@yrT$XG$gfrk@*e7w+*IJ-onzjH-qfc(b2 zwZt>bzqD(stryPTd&DKhUphApE26L-7W1W(6}(N%Gi=O4xwc6_W~zS^ePMTV}h?d3CfpzN=Y+pGO0HZzNA%|*4YHDrJ-FE zK>ALVj=B;yhp10pTkJI*3bC9?=XWz&cvc6CId)zB>wgeio!g3YDI(>@{DnbhxhR4n z%Xkolj_r~OBELOhd1f+a#@pyc9^R|4aGcyNbFuTC(LbT^R2Q1Kn(@Ust=Am5BfX{+ zr!CVH`XokLs)eMEyLHz{;u*;A?E|Z=HC#`g@kwzP&S4%?s44TuStOjF8dbG2J2KoQ zvh}d~&$j|S)`%Tej-yV^Y#Sz{LZI1SIn5N%h4p3jLGt$9o><@U-!`*B&GIfcfj=hU zd@g6Q+>7dM%^4DG%{=HdbOGMT{{Ub*=2>X2ao0QJBt-73u38IR#hKm*s==qtRL{7_ z-Pg+h04z~5(yl(pJX_*>+scKHVaDYAOPZ~XHduBH=GiEqTGFDlo}Z^)q2Rn`&M3sR z58OJh5_^lua&Mf%=WS~V&{TgA;Ea$coc*t=DBM>&_cVGwKN zm~}L+zrj{s6Y@Vh@sE@TBk|3m6|*nG6svv1486r#RQpI+4@Q#HfH;%%(Nz@(T@ve! zN0qnL)YG!#SWahgg1Oxj0imG;E~2^RABp~C`19mvlU`us_ACd@N%QU2ei5)jM1@n=ON9yw&Ttez%0hF;me%E|Ie zT%mDpUD3vVvXdgNg0HDcHCa0G$0y3j>xT&%bv_H*oJ#=5TQo%RMtBu&yyV~+o?~M$ zc)KUFU3kMB$eZlWIaP5ZIXW@|L20;MiYf=ctw(PCHBpAl@Y{T>KAgH~ zO&7sWo?bigCyt&@=bS6VoQHMfi#`mtquuYcb*DklQ}IzA0l=5{@=O-_opJU}d*O@xlz(~4 zF^EdJy3{`4+s?Q9VBqd&ySYeE1b}H>PR(VV3~uzu-a1u8!LgFa(p)JQa_kQ? zeVA?E^duf@@mndn=X@n|w>LW3f4)ZzT;#h$?y}nrw$(}$3TQ=o>zHwWdl~gc+VP>T zC#vh*+t}NdSc~j?%i|)uPhZN5h*>5Pak1R*7P(fbFeX^4Z!qLA<4!P?>`IcbCxyQ?AAdTb_MRYa(iU3db2KyvxQ=L@VbrF`> zDFCPPI&;%azGQo6ikpz)k`$7ZzJLmlNT47Z0!E|H33EstQ!z{!YQC8`OBDNBeaSc@ z9PzV_xF!dMUV6l}qCN?lFkDVQl-=RSPq?|VSWx}LawtoPeF4;xRE@wo^ZoVKcaPor z@)I-!>MQo5^ev0{?DebSdf9}wYb^bliEHZ4YF4T5CR}|#@7bA$9Wb7m#?iDT9 zroYuiZg~01kK5R};`qzi%ttM;?$TYdHs!>}haojVTWzE{U5`Ycm36AP%SRXm#08eH zwwC3^tOScWHgKu_c}N_Zn~|X+Mb5*7q!sR2rup8`#>A zKUJ*h;@jN%<#F z?Ft*(NYNg8{qLzgv080l1hf(<+6ThACbZEpBo##;yskr~$u1ST(n5OdkToO#!BSIZ z)YC;RaE>0F^E9(YD(;5FhFzYT)h@(sVaI?Yp(&^bSk8T(j?WnRE2$IQQ&Xzsy z>Q}sSZjK4IG7nMsMA7~O9ZZ!eY9+x;h%GTn~Qjg?K065HE|5( zlHnwGw{J*MS#@5BAQAH>n&bZNO=#O-*VA(JFR|^sZNZVC>>XFo@7>Pvd*NRmI1_}d z-19EUXW|AH=0kwPT9B*aE(K|Er@ggyfR!vbq>);I>8^$C?-POib*^_>XuP%WKf4o0 z%^w_NN~uG`pE|hjlzbD1#gc3?E*Vz_TAz)O=2w=`T7CqAvdSA!Bo4}i(zF!Ukp1h& z&1I2{5C|^nzLEacv-bJCY_he*yIMxm;=W3JgL%n0Zc{O`RKqhQdq@P7`(?x0N6MP@ zw-0y!00D=@FBAKME0mVHd5Wy5pVapqy6vqx!Whw4=dHm`@|x({bw(pZRJFLUJL*0Z z)P#a+jYp1^DuFfb_0TB^-($5c*H9Fm)Pf28Jv9mS?3@nT6~LW#YZv4EbB-%g`WqGq)#s`8$JQbfEnvXHK0uP8^YMA#P;G8ppX0*t)~h9!55Y;JMe%A8Nh>@=uO& zs}2FdHr^|hjsEt$muit0_qPk1uBlJB{{XSGtu4Z({YJGlpxayCCGN$}B0?%&-;S%5 z^Zx*4alD2hxuQ3>p>Br1cVE47GnM}U?fdde7*(WkDy*D@+pM|${Li_n*y}P7s-!hm zl$8Z1@ZVjh7yFRM$SEvkr}189)vk$a$uZXh{8F@&+N+x0W3pDHiLKn<6GK7TwIYfcd9fja!wL&&qI+3M zuX2vUl=RfUsAIY&V}8eOcT?Rg$agil;TG9$!*T3O3zVyK4tJ$S6Y~$gS}7;EC1g}> zO6iBXplP5|8w?_2=Bu77@IUgWe;9d_kvI#MaeiOHu!gxRe^?2jRPqXi1_q*hWW=(+>7(M8U%Y>2LLOLFURJ?xXK-WVc^0U9mOXRlD zNpjhsaNi-R73fL-0F-WKEy`StVDXEGGyF3LEmo;1 zQi987iYuiKso!(yG4g;&sy zlvq8R$?g{5ChiEdV)y9nNVvzf+#;$uVk7bbl3s6bY`sz2^wd_m#AbcGYnpzQ#y2?B z)1uk&zjv-9dqwjE(YMM06*T$x{wfOPD{me7cjZc<_$A`)?~W|l=xw_P0!DqifkddN zQsl{P)|6U|!i5wc{8gUR459Bvs#J+!8sdvy4f&1YZ5?pNEhP?GL9jY*#%+l$Lw)k22j$}J^I z8@cVIfKO9Rba)&CXjdh;WT1xt38zkoA#%w0_W32eQ!bGLL}e&NzuD%XhlFYsQgzS_*3oOyB&U25ikYjTr#QT^*p zMT;@DhgMVd1voni*pt0G2O#Izn<5~t=RdgZ*?I4Vdr!!6P@9kg8xgG?)R9A{!~UKs zuK63vxoNbTiVc}$x=yx8nANrSZSeiITx5pSrkP8MC~w?VD4LN$ zUZ-10m^Q3^rP~=NQB+Dy)has)a&wDyq=fgWMyTx_p(!VN0i;bkOGc^Kp!1WoQ7dBk zMavSq+j2`S&C6y>D^6Q0j+!M;r%}qWO>W(xym=YlNiPw7SDm% ztkGgwUA@0-OOmoYYNUo_ppWRKO{~=j-9Cp^xPNHHtYp39zo_}E4omOl&O%0uc!#7k zJ|qtvH2lyj&#<2%xlfL84-Dlu!?!Sqw>zW@s?D>H;oYs_1RyfBufN|?NhwevLbajP zd1t!ETe;WGGSi_7zIbgj7tu2oL>=7MXg)o(Qr3NeXMB4#i1-FOi1}{!E#6>7=d8jM zc4J$t7vx<1#aivhzKL~eD(zY}006F|P)8)Txs1x^7@P$FD#qUvb7OPpv7&ak)y@DN ze;?|j{{SR7cL>S3vct=KkHq|!AI4rTl?ho6H2g$1%Pqxt{{A3YDN&B1J6kFE^`@?I zSqqDcQyddj{wd46HKm2;)LdJU8?+knsS z?36Kja!3Zidjs(Q0HXPH@xP6{f6Xj>{pD|+IQ~zOa8uClnC}Q*B0V{$vVLQ@B~v3m zDd?^t*JH<8N}h35XS|TINTAPUaxWJQwzfSrKX12X+%Oz{k=eTDZu$?O^QYn0!E%Q5 zUia99)2VV>NNu}=jn~^#=}1D#HXlh)+ih&UG9#;&F|h+B2CJdICh+jHI};(hgg2C5 z(ndOmHbe-?1#ZfFYJ*DtTI5`(Ea^|C#oqW<0k-k_l66cUAo(sfL2>@$YI(Kd_&k-t z#v*rbaheoMvzC%KNh$hI{yNohJWz%oH&U=%=R9t%HnjOdr0?8h-K9#n&vd^sTFT#j zM3Pja*MEmWtw5^8i4((*u*j#sl`+IQjkf~Dgdf!-4%uA zT+-EKfneesCQyG3z*OhEDLa2~yD#aHB~(6R6jVB-%v+N9=G|jNdxe^B z_kxq|Iab_Z)%p+(3PAX39F~$AhX7ht#ii6yYTAb@1>+_I$evU3Ry)PGHV1X(TU05r zX5P5+*C8mDZEBK{FvSkI+C!emSW1BuJDLq;^Kr+Gh`KiUkB8VT#tPzWi?+&Yz?`3T zP73ie#{U2+=W(Zp*{Uf~pt$21 z3GOZTNHtmBU1tOn(l5Ppbsh3tN)U>vL4IRlT`T|LA!q?82!H=?Ur>m{DKGh7IVK)N;p2HRViNYsrNDX$k- zWx&Ttwlvd{K?ji-XPWng^<6xGDu2{q_5mslm~EE_-b2OMFlBY z;5l)h9wTG9PN)I?-8VacYL{yrN_ys#y9`@2W!5dV9fq9>$LczdWf=gcFXhsSp8`L) zq>`TP`h31RogGS}k@QEd4|`N?4pb*)AJ0~hc|xsl+-jum3&CCFE=k8M{iRWnArIUX zJs4>BtN7DfFK+nrvN>iq`$4H(>)qa1+%^hHKe&nynb0Y>%Dn9vMs6%N<R*NN-%caIVM5n*rQd84? z&bfuG6zaQ_*Ax^TS4}vgL=18G8T9mj^e@B~<;#-=N%|LYC{t|$2E^BKs(ii4nfpgY ze}}df(QocoJ96BT}=dE4b)-DF@nlw_>SM0<*pShHI_j--*X2iJW)XBF+Mx>c!H*Ldr; zXlUY@ak)P!;|Wbf%cH9Iimdj=$XZEKnop;=l-8QjHydw!+PBL800on7e(xIUJZ_K0 zSZseG#PiE~E;cKRQVW8pTWZ{Vi1a$iolo)rC%;*L6dNiJvDJkKU;kXN*?{>x8e+qw=nF)H?t-JA2u5ooW2?gfeWDw#=r|{L3 zZfVpi8KHb-CmD)mVMrt&hMJz(I4JR6xurX;SqKzu;Zgph*L_kxz=E_^`9MyoW?V~c z*^=7Oms@ZijF%V-R`Of41n8Y!ZI9c#9?_gL`ZP_z77pZ@@B z&Z?^9m>h`CI!h#k)~1x{`0G72p=$2fZdS*Es)g@(@k>s3HN8&M>8k0i^wo5%W?UBv z6%|jhFW*8^djiw~lzJo+O8#1+p48QEZ8C#yslR-+w`;h&0l8_O+XQw>Y!HVMdMH;= zrH^T%y0E+TW8X?uI=G{Tc^jQGXk2r}IHUQ7VQ^{r5$zHC0{w{1`>wd6;YbNc>Pgmj zFO-JWpvY(mT5L`>;_>CN4a5rl!@6S&woiawC%0&yBE@-#r{P?b%d2U?P_oOdEVe~J zDIkpp0N@=(9gqn$eL^VCSt3%xZ!wW{X-2OFCW}A&_LA9LlKg91t4#_v{0weqa_!R` zw!C-!v%gvmehT!`!^L&cG~0<=`h`+loOR%pCTt(yM>~m4QE4Fj)2Q;Xjyi=RJb`KQ zoxcRspT>;bl2-tR*Gi?M{#vWJ!nQ$HtLr{y=#3tuYt9vXv$#_HC4{4)2{a?EMzhx# zr$*(tu;h004GyWWS1KB*x(GG(8ie+}s^N}n`X!i|1;LHmc$9&%R6ji!aiVTp7CWkV z+H)9%NN(v3E+W6Gf}`iA!y0<1?y`};q^Zvxz1iPsLJhztxj*m;>0PO)R~Y7#QKC#& z?*@feu3|;$Q;Oq<7Yx*w2KD*vs1w^cqeZ30T}1r~jB(7DBC*LstT^HkU#T1Ks-X+F zYObBJzoe@VjPneJ+-(;SLdg|#*KeML*4@&fzBTHm@A{e!PXu;J_Y|R0l548N329b; zSY!Yu#jP^zS7b;)QscU+Iq;!Ax+3~&jm&8jO-zTFuh&lYVxQW!Hb-gP4JHmY#_QEv z-EzrfG_4e5-QwXY;>*({DZ$(ds%yC4Rt>QYZh=s{B=qMXSX~AUosB+~akEu_1oMkP< zrR$Tah}|kuRrOO(MU0^HC{uE#){GS>wZdN?VaQUX$VJLpfAo_=Yx3)-T`ja<5|a}z z?+&^J5#&6_I$2y?emZTjO-FD@?x)7I)m_$A$42NC{y&Mv-DK3Lnq9)~=mFA3YpZW|U^ zB$#eCcW?rIdhMrzH$H(a*;o4+jJnd#xD*IdcJ;5OtRR%C3+T(+MbwKgFl0Ps#MX=K ztes9ynx@UIfheukevn90X0#O5O~g@5?WL-U87H?jX;RID?spFw#-ydkWGg93DHNi; zt2F9VI%<;Rg+ljLYvJVAnenT-8h37;Axkb3gZk(=OkGzjM^Zxa7*tjtx9%#X!qR}I|VCE zHpRZS{_1qDYvA;V&?wRs?rZ-5QE^o!sx|dGvVq#@ve!1O3C9)bGG1kt+$vLqCA3%d zDMo|!ZM}7CK;>H_Lqht{`!f0LjB}6Rc+ccVCc&{UY0%L?_t!apa}y)8muNj`XjcyT zL&YB{BzSjf@+JOAZuR4g$51X4(U>k`qbRx|p(}(GS5i$oL8$m^wb^H}H**-Fk88(> zTq5I(ax8tYxVV>mE~N(IdiaC?05wuZ3+K;@TrrODqug17WpHyY2W^RXwjTA#)#3B< zA>64G=R|Sj`@QQ~Q2J}#zfr8s{{Rb?>PI$E-LCj*wU(UWwox(m;oI5Tx@%sdy8eDE z>AUS?%z2-g+%uE;v*dR?{{Yh5M~UAqF{R_Hl^WM!F57mRnV#=pOH8R#jk_ zczvW37@-w#UX9>B*d$=PE7%DH3rkO6QS6KyPoN zR7mBe^raA@PMnk5%!j5P#!xFLz0kwDe@X5dQ&XVV;&fNqEKeRh-tfXh+$Vo#=fvE> zZ%oT2?#{36>UGgay_dogm9((15b_oRhT!W2a&NP`9-yIVTWhOZe4UZ>$Sip5z8AcV z@W;!JJmqYIjPedVmm`rm4rHj9ULR!coF`gBlp1vpG7u5kDf+3ty+?@U_{-ZcUPgme z^Y@VZX`8gkxsZb5=NeG%Ki4~a20t>PtsCN$W>Wc5Io_E-$Ioqurv{h_)Z|-VVw{Aip>Wv8gWBOHD-Z1-{ z{kHNPevxemx`9A~YMI)%eZ%21`2+t{fzAbM*nk@lA}Z9^HT3!r1Y_2c)A zb0!;Heg%BMecAZ0j$`v=ZcyOM^BIp3a%3@Zu-_sfK@B}#=NeYRgaS6#R^sA$EcQ$& z_O4j`jg&bj8+TSW>OY#}(?GQHn^wdfN4SoUTkCm)nN;AnK3I7w8&go_W{T#@S`{cl zlpSH0l4_*Vqg0Qs>J_H7a4k=J%$W&?Y`;SK6vt?Ij{p?&Jmkz*kK>m?Sp@Pi5?DE2hZ6M{txa zOAp6>6Q9f~fPDzl3eY(tsSAquQ4}aY;ia{pr~B_>6kPdjA`+sg3snioXwtC`%7 ziAuvV%cY)6MWn}dLAs~6V6RPH!y{hJ%a~tU7$qzzk~)4meWVbeFH^CtX`!(Ku$rE= z`RTISlmJ@j(X7xxW`YYe5Luvt_dyt1X-VaSL`n(}SXI z=qK0QvIjs<_-Q2uqMql=Nl&HRe(G)a2G+4w938Ku`lRTbXpOE*H>h9RXNMXe@{*{w zmIlNtM$@8G#X?A$$XY$b3ZIspCq&RIYg6fT7;n){xzfM;-4jqDH1C$hMSWenw^P`m z@Y8ZtDk&CG>%y-YgERfod;jUZmZxI;_ z65uNKe+AtAm*)6&8jJS(%)DXUO4Q6hmb*#I`=~j681k5qJs9d|aTJDxf^DUQ1Zd>j)=T^-4E{?|5;DC)rJ0*uL!n&{@e{<3MDr%Ym_3H_&7#|X<93@`uv?mznX(&FN}61C z1&^a*JE&-O*E{C?+6+sZ6pTA$@7Z==ZT-2s&au+NC=QRebztv2XEUETrzGQRv{4(# z{s=2fi)xBWu9mh1740m3wImPYu5)jFCJOvaKl~P6p9-$Qe}rct#yZ{q0R9;VUyT5hb$LMZ0PrlX6ny!7jL;mbKLDoNgqKq3QzMSl%0;NGB}3az+-Z=#OEJv zIFY4w@67Cf74pxDv2VEd6WM1betU9m$K|dZ%*P*rh39R>X{O~Ng09vf-4K+mwFIOU zC=)`XrTa<9q_Mblg~_EqMSF^>-+Xv?*ITzCXetQv0sMcFT)TcO`(43S+oT8JI@uoc z54KxUkatSJR`esG_1C6gnWTa>3&>Y?ZjGcJ9J(dpm)r-Dye7`1XB=9^y(aYqyJlGi zL{u#kA-1I{3R03Pl_5(eqfq&87`MsWfLueH(KEz(HTELrtN^vz8uh2c@k~!4vTixK zXE*+3IWn))a_h5qtn$qj3QT#7PLP=sn27lxAz>ZCD_5`KsF-niZ)#d}+7tOWY$wPy z%_ZBNs2>$=xmUzI^C0E>9zWvGjQNKi;aRpLXUVY)8zh#^!y%Od;Mw;l1s9h_9A3x- zf)8G_)0RB@8)+GI?*+r)q~(3F$lgz~+ANb=*IIR`JsIVTAmLYob} znM=N0-9wJPiOEo9l%a*Hiuy@WJvwUz$-VE#hf)!_ZaBZ~-uh@y)SZK{UrBy5coEDV zGDYCld_GPw$|W2g5OL{+8WmlFA@O*<(=QO2&h zoL5dJQCjr)F7J-uUq;5Z+sa2;e{t!rkMZfM==TUe&6Z2gCtA5x_|t3%ygM(AZz{SA zwb}c7=}MN=##^ddQ%(XA+p$eOb&b#5+RH-_qTAr@u9#1(Y6nG4{OkK#;J#*Hc}>e6 zv|6!#P`xF|_|{^WsD&-dfn=qjHRTTe%G0=fwW{F$=icI;8Y)}hus-AaMUTxzR}HW6 zOnCmilUR7jEAZ@tgi1ptsMM0Z!9I`sbl2>0gH#3H+H*LaTq05#*@efx60hV0y9 z-@ZTlPhsHS56yh#ayN{e?Zqc9b2{S7wjpl8?g_Qz7df&0n8Is|DQyinwRXMVO8)>z zs(^xNuF;IIOd!0mR z$@d*|xBV$j=UP*6z%^M>9^Tb7La}OR1Kk)CaK996w+>R_i8htv3;xA)vgH=`X2rXkasw%v%HKke=eQCIsnJ!} zi+MIGTt)c|d8K@{_|E60JP(YlPF*{uo@Q z(~e!(h5}U@Gj0_A*^OOaB9Qcu4~RcnzgoyOWP1zJu5-b z{3-tcMe>aw%6!)S$DEo(!37WiyO zx5*qjnFevC^p&I?!5Y?WG=PQ2bU8C&4bE zclg>~(cq$W;JJ0Q$~QY((Au}Cw=zShQd>i)0F)Kl>td7o@jv~zBB%sP3>nl?(1q76kQlU|}J#`Z$ZYPMaI+ae~Cw08-n{9Tk zfM@sn$Dd7L)Bf*-=cCvXuL(+)(;!g7cgsEbPQpBoo}P zUwcK#9esXgEDa!bTr%dkOS|9a&QSNAQkGmF5>0iV$2@vNMbdJy)?-y(ZR)sK?$M{k z?n2_IEe<3af)C3`c@mKS(?5 zbl_@crl~PjDNd2v*KsC*cGpTo98tz_TZF%*O(nj7D3PXLFbd8KV&x~`{0SQVJ%#5oCNKL%{?Oa1X_PSHRoI@?pb|QRucka}j7w>X zWPLzt_({w;q<5L;$mWpp;w$j2IXlSHd}lpD%3@%4wn`TXknl~lJ<)OHd ziD40%bp()*4{)aSHP@5*o*Fyr*%>!c(R~c|$BOKYj8f12Qj-AC-jk8n0= zez@?fvS(l_b!v-ilH%WfG89U?3yU>cQ_(fvgH^m2n7YDaXCbRcLb{hW;VsWvw<8ln zWFoEu#eCOiTn#rEl38$m+@9c|wDyvunslJ1y$xlMWTQ^&%-6WNA#ns!iW8TK2riVp zDJrMJn^RUX$3m(cXmvn0YK+wamI*cZX~`1-Dn)68E42s&f2x(y7n&$%^(U2;X=TJd z>tRV~g&`EBN_Q2}v6Vo$gzvLGBK46+azcyJB(G|kngP>O3#mYp8^J53zigbs*M<27 zCo{|=F71!vX$pU0{Yh=hM`TQ|=oi^dqwi7R zem{mcit^MOl&odukc(nv!&U9K51}cvW|5_RC#}J7$`>B3HyOR z!Bk=Q_3=}L`G#g2!W?CLPmYr9Ql>WfTg^z3mAv4h8H+JT+GS()8i8L;ZE>E?+&F)j z(5y560CN^hkl>o6VMt|p88U!!2Sxd@h{nb$3A^ym+f2; z>rmkTc}s~fQ7^JUolkjElAM2UFA%WyLNzuDR9xQ3Rv z?ojoisG_eT{lMGx*r^-)W4URgWh8uQq46F*1Rox{P|yx``!RWSXB&c`0LSPot6XC|?AV_=do(uZHq zr{bvPwVqY+^D&0sBw&(6YbomlPaAkZ2SJxpkqx+ob<)o3o9!P+tn#c z_E5U^sR`JM3ThiT^{=X400j6e>3LP-?ZbCFXdTHqef3d%tji>7-rcV4aha3E{^26$ z{f<-89EvB(2j6+LDO5t7?mguoS60zN*#0GCox0`K-LWhzSBXxXS$?c0^y zYNa0Fk=?ieXhj=HI?|n$i(VK-eE$Fu*;`*3{D$Xm4f162{{W9U_57qXvAG)pb5K`T`SMSW?l9Y80+V%yMHsR_Y{;uqZGEyeJhWtQgT z8HC1QSmV6=QCWIig@iVRJt0YL0YuZeN&`(#eH;wjbqZybv#uoA-_(_|948~iZkbh* z?`3tF_(aDo`E;o0RG60etvpyQJt2^`l1kD-NmUi;T}y)Y2UAf~s)+k3#avo;w9{Xm z5-q(?g0jMtlHz+@2_za<<*Nf4s=E#T?#b(pU+-AG^2L9$UFBGBINQGUn&}bMvmz^o zp-OE-bd^-s@zhyuqLGE9XjBO99}|n?{YSscr{KExhJDp@VaHd?MYABiVE$WX@*rEK zc}~lWISDxQ2TJ4Howckjj^b9Nm%Xp2xTvdKnqO@(?7DQ%1h5SP?RLKNW?y34W{}Ao zKqP5VUk;xHbDF$%;tw4>WY75%?n#_nZGLO{0`11-<1MPoEvdGftEy?IlW@8@ZbF(0 zTdV%Jx}e$-K$@LH#PW;DFT*Xgwr+;2OMmV5Ivh*nxCQsFX>g(blpRm~6;jN&mc?lC z+lce5?=@qE?;No@J&qzf-I-*_*++b)?-JI91jT8h5&+y9*1Fk_Q*!&l%2k8E#d8?F zxV086ov!n^)}3_!0H<^;4iU#=+N1H8A9()&bn<)2jCr}za(u4IeYPh7k&aJUZrw5) zj!0~&_DCM$Dc@3dB8O2f`8=>qx=+w_8h2J!d?A+*7@>6^Qd~){pAP!==rlU0^7Z20 zIVx9;{B4Qz6}tW92Qc0Zla%-|NKAC$6W{M!9{i2}0HT1N)vZ;jp-;n7aJ<@ml0J42 z*7T=vr{sOOiKaO1WDk3WwbNZHJL~wTUN7Q}gN-?E_l5a^k}r(7e&06ZWx4ZRlp^gF zH;Z)?mLe!2zoLpoV?uIgK9TC_8~#V@CLgFRd*-?5Os(rw{^&0B&`t$1`g;7SY|-7Ul+TAUksb zJ5R&6{G@R7$-R$AkmJrE0F^C8WDkI;>8mzBcBNL9M;?s;1nsqCmwRWrm~-E^mw+!h z9mg77QTk0#K_8ZpJWY7o;ZRje?LcsIO$g|u4n6W)mGEnow#UcWoV;c{d8T}H`=Q3u zasq;1W1(HnfI6D|^;0($$KNQzp;b~_m8?aR;WcXjG(i>A7EhL0n3qC4e>og>J80rA_4fA)IU9pqdaZFb9di)i*^ zVz)@BN>dtS7)_|S#HD*+zupMkgp=1;-Z#Sjj_yf!SAM##iOBh}y|ippp&kmhc;~~e z=GV#DwnCLB3B!sNb$rvhT=zv-ir80DPt>n~{B=K-=YF0V1FB=&{y1*#D&eBJoN{Z4GU4W%^(>*E*HE>IZfNJFQWUzh9^2s&>j`eFs3DbT00wTHjOdDb*UNo~Eb#{dF`(8=`DBhyswRS521^ zj0Gg|cP;Xd2xd0ULEBiaBkKO*ums#a1EZE{hWtT!!0)a}>Xr!nS>q>w_SC%o( z+Bj8MzF?Ql+hGDp5U2 z(~!U;Zr11&D~qSf{m$x@t*p0hi0#@)s+-yqU3=Unf9&U*%n1 z6kvok1~>sIYCA)R8K^qaJ4yh^E8HCAX&0zlk zMPKo2{DcbAPfpJzLVC(TT|?P7F85{3aWR`+BgfeZuEhLx3MjVfoxi*<6deLa)b&WS zKp?f$a*YgC*KgBJ5)0M-gZXG6w;B{mYV`>MzM6U@WgCjhT`DT*K6KN;A;kuQMq#x-FaXqubqLTs9;D-LP9 zWLcN#2_Mi(kr^k|Y7__Z)ECvxRxtknjfs&t^~ib0oIT50iBbG@YSBQckEcyP98?Mg^YqanPEuWTs14M54@&9DzfOuhxvy1v_!bjJkLD~L64lSC-!@0_$Tqeo8&yzXz zcRMX*ODCx$>xoY*?j|oi%J0^ethak%zrFF4?C z-)>PfTdt$3)T7hz(lS=>45%tbBJMdSp$P|^a|f}WIEjTY_-=N4Al zcDB-1w`N>rA~Smr;_=oKsDe9sJTQol{_1u%y`-&{L{_@9&E zqSK4xY=>fJ(STbwJCxJ!OnN{-sBuoIRVQ3yH(=b8jIp|#PafOiuDJgOrX*G>Kf>Nf5 z>0gM}y2m*8j}?N+aadaYDiC8H$g#+N#Pp*muuC6I#E$hO9-(PfYo}i{Qaf`zuSCpz z&@*ei>``QnIk?&`3d(q@?!12L+P&HM;)xL?kZMu^uVTLqSVNrN-%j9Ec&z2e8FX&- ztsGkK1wCLK$0p?BF|5|!y9P$k?S3o5h<9()+PV>}=2wrff084j*75#c`ubNQj#Au+ z@ueSfGo6Z{B8RTCW~@4`v)IW6T9Kr&vYDL6x{pVO=Tah*O$x1+-(5lsLsApo%fGm6 z16I!D4Lj;rszV_Fq`5ZO^P3;j?Ra%ZPw>-_T@a&Bk7L4R-0TZNi3v6I71iW7fy*h7 z!4~QQZ>bK&P(#4bb^J9bHKkBFkBp@VS0^J8+OMbmHRv@{HKlb|7I(yGQ@%X5wLgs^_%ERmQ<1YgE_C@S9di8Rq0eZSQapQc?)16xA2pp*r|^(VO2^|^r)Ts4SN&Y-+!YlY*T8!Yx~FhN12kk=Eul- z)3IDV=06%T96DUsw_8e>2q(4P)7rXx4!t#HeQlACd@coe>G=$n7UV;C($Wnp*-;G3 zjND-_B*r7H03N@Nqfci=*ZAi)4Tr8Mz-o%()~mMR`6FHisw2DDRup@h zpdX~3x~iJo1<-N%QBJ8#jyVsIIA19luNdW1aGbTM@?o=og0S3##+ZQH&4mzX76lHo zc`P&+APE~#7QY#vxwdxbU9^!y^6*h5!q*~Ol4QK4Jhzs&9(hVDTGrx<)TJoyNhFd_ zu9x&fZ3jgr9HzRRQ*c02OZ`2&>byZs6{))p2;yyzI@^kE1f`)>5$Fz<=nWKiUbUoG zx{`Q(i!@yC5;=>{CkI8!60-QrB}#eAT4bQgLqKot7Q4G?y=kgk&N4%6???61M7Zzm zTXM%vJe3>1UH2%i-Is{{Zf{2L1K~kKC~G z$bG)bl(^Hi0Q99d<(w;)WF0f1xd%@4U0a>-{6)R9XS8s3>J7E~f6Cw^m)niC$L+Zl zxJy)0Q6aUY1tf#LYCp$aS{#EPp$m{c+Na29Qe4bek8;El8dvstu5@X12rBs#sFK*l zCq-jxn!dE_s0u5{_+I^M?ta`w#wn=|HbMUYEmco}dvyz8i}wx(=_Z9la_#dVI#DJv z*-+T`1HbXr3Ab|zs@_`O#W1T#C^*xkL|R;7t4j*B(?m+ z^*@QyK+#9~dUQ&5$1&+k{{WauTMOztM%s+{1$0Cuc_~)?wTH_rk2JRC^JqNHB&vO1LIj%J7wza%FV>M$nXB=h_!E!xv`2(2lW(vwRd#{ zYjt6UvU6(rHK#?%Z@GqF)-lHimby5dKM(m#tE780S<+gvDd`sW%_GQoB`zsH6$ETF z*|%d|DbNWv1_f*4r`27YNrF?y5AHP9fORl1N)?ZC&9>BcTLoLFFn?3u>hP zQpY(H7RtS@O=P?-+dY?>`uqK z*Z67pj;br7At}ar2%>@dhWeizfmhB6DQa!bskB$7J z$QNrxz8*ZRb`AD3^DS9T=&I9Yek@Vk^NmiFl(OhRuFaJlx@lgsT4*|~z5YNof!&Hj z%j{MRQtv9yYsw+z<`VlAeYce=3#23(vbBVjrn^>}pBur|RK+B(>Ig~L#%p)Pvb=LF zwA>$sb;hSeoo}4dhrG+tBql-@p722-NCt!udJsmDbqJ7cJqn!!MTc{AaOv|_v&b(l zWcfvu<-DoOuV1+jmGUNHLV=Uqi+`arbt)>5GUXO3RG?rfRP(Jx7YlC=KSP5^s?HYai`TJzpR z?cOsdF88*C_$`+Z=J||0v1D=w3e*Er_;jb{ts_5hOz+EHI^_GmD0xcfk36=!v?yF$ zn;S01maAK7M5I3a@})W`@Ka~7r)`Zv)J%5(x3vEN9BivRN<8N|xw&tw_wAFwR1cL# zzkvy@#9YP0tzUDlTIU}wc{hQ%uNIiwIgTsGY{9y~&fK@5DuZ=yMcKI_FQ}w@psZ8W zQ&FfC(63uy#kN-z z8*uMYrMmPZ# zg)|16Yt-0paINFAYwiIKUL48$QFkU{NUZ4i!P8&M?nCe|+Kn>1-#^q!P@t~lni06Jn9O^pX9GNqFDIso_mBG_k-K>^xI`PF z71#PMm-3g{zlv?16y%t`7syldZe2vL?ikJe$?!%t`9gb_9wBW?s5RVxO-}mdcAn+R z{D=7%LtUpu*zUcTwYS9Yx0bUvT9eoAUl+WGhum;{(;>yL5ezH7JuT>$NXK%A*ja63 zZ2@iE~Gl2<-akh+u8NOrBsH$uZ=@ztrN~)dd)8(x# zi}x_JtX@t(PEh2~;QX;z=1|OOu8|5!Ek50U)%4Pu)o_e}hUa6yNzrppbZ|dcW9l$@ zo2X2le8I*inoDvsMK-&?{KO||^q%ul2jF#$=eU2RyfpnMP`6&u@?n3BZsX@}hwe}l zDlE!rK=uGde+ugfMb`nSN*B1PsF2zE9Ro{hmd72bYXZ3@HNne#lUTPTmYxLcJB0z% z3yPIR=nc~{=wzo=LS&Y2eiWJ&{l74A5 z^wSbFl=ZaRUan>VoE$@ucZbQ2i z_Vb^PVhXewbLuVY zrwfrR(l(=BX(%kvL1uyrG!R=PRDg64TPXsB`smWyXJw4^sNq?18}099E2K$_NDe-`;2(ss){{RMFZPTq;cerv( zEw(}=GZJG|9m)2|2?`y4I^X*nl)sYA(cH9+y(+oq?n?`dgCNy0phs_fMz$o zJj(3(J&tPa*Goi(-iav$g(YpQ5PuIsJ{s+Ja0z1}dAd1E9vnJ_(GMgr9COGHA#2O7 zGLD2_?TTtIKH^c^qOI}*dl7L7PyUmi&S|$=?X2cD>DJc*89E+|M|)&0ZT)Eaj;f0B zUnJ)oLzl(Oa0}#&dmw}q+jFD1TUlwrMIluENk0R&th4w#Y>YvYii*`27v-0jnY~MQ z7PzU$73aL;8|4_>y$a%|^3+K(CQ5Nf{$Wl~x`mO|4hn@0QD*psz2%S79RTU@T3$2m z4c(Sg>nm3ty7um@SCrgui*nv456QkAw}!13Wdw3IF3LxBuIN$<*F^sS>8zUn0FJPn z2QM{+!LYhCBlMHqbuV=|)xK8LWh{kSe^4EdbxH9hHFmdLFVXPGro}=> z{$aMd`4sa7=NJGLX&S5T_U7|`cZl4pa)p~&f9sA8<%mnhJdNU+2@l1*{Gh82(aD1Z# z&c`G8*UoU*xcL77&QqU3i0!T6-YxRqWFPsMh#(NJsc9YCkg9Jzu=gT6yKhE)CTCIn z*B|#^+X=02OwnrunuFo+`+wEf;}3<|#xu^GQI+RhVTt)cj#J6<}0R1`Z=}f_UJPYj()tTI*1JLizgP?;1Xl+M=obv8)-|eYec>ShKHq@4*%#+%r0uZLOY;aHqts z*L(YgYpnOOCph?l-9r3r_e=i(*}sp?O>>EEnU|5bTjk1MBxCHzb{&Q-rc|dLVaU!N z(8DD}tkSd<>8!3xfyWKeiNpXF?*;C&uJONA&oMes zc2a&*&E8{jca78U{87Tt{DHIbg`eGOl3e1*v0!tYJDPqP6yW<;a{mA$x5YpFjs8U0=JC%p@ViYnBN)i9X61Fc z*<^P)7J6h!b=5kW0)?x*(MdJcXS(>fA9`o88=`lB(yDKWf79(=<9LC0aHY_r4vEa>z~=+Z)YGegs#7l<@ozX zlO%uv@l{Jb`-k|(#xlk04CT8;&DGsw+vQzW;jJQ6#K5e3vkKdXt*3zYR zElKUDr8RNK@lio8$=T-}6kbJ^b=9zz)&4`*nhyTpL-s2 zzrB~9b%1@O6{qN0h!xvbcXHc0k;p->im$VZ?ZDhYS}^KIZ;!w$UBW!u&u(32z~ioK zlaBcl%`Z&0=IdfBGH;n&Cs+xE1#)DeK}ufeZ7r0LeihYC?Ul4(J@lYjEOq_d?lH#W zx&3}8_$$};{`1CPi8su19r82GjK>m%fn;|WT*cyh_{!y6Ev&S)I+UV9+C%I(;+7Po zDMzU7pzDF=yd9Y~RsaoquAA-u063SAmW#09&1fh{>UHj}j}U$OZ;*W8<)%4e+^4)R z%+~DO64<7{cX-BZ(cq*d)T$yJTh2*!1xsm1`hDGY)fB$Uuv>#3k^xm|ZTpEWymP14 z2bvFuhMnrH?>O+b1NUX|9|YxJ2eD`zRd3C*%l(e!bFsy46r6%H4oF+c$a`CCg16fD zKB7uV-(p6yd~=sigS~SuD(2FiR->8lNVzSPa@uWS2E8@=`YJO&!d^1LtTxtu;W#YY z)B+L%0vCZELNwPZI{t0n@6%&Bhq(GV5xGZqOgXzi?2CoGIkaB*z+G zq1dk&)$5w<%gDH|NDcoYBYs!gzjWM9{a#KZ3luC~30SZZ|B!HpN>h?Pr zCuf)ORZ(SbV3dQWh@zBl{&e##&jYz|wC+W}UbtrXqTWq{F!O)0mcr^(g!d!4w3_TY zzFjp1yk<5(bt+ZFyvptIn8T>TH5x6U`E|%LEcl&@)09ESIQtD+8OLo5#AYq+w735N z2@XhPg%l-K-kmnCp-;vw+~egnC|h$I<}?CD8_;|cmp1b&E562N%a`Cb3)U?aO*p#@ zc1VLE@I_yFNF_zb2kWAaTlJ@Q+d*f}h*=AHh@% zp8bqtHhi8=0h7zW+9SAx#FsHDW&Z%#xaBr%pQHeH{{Ss#`PG!t!rxP>-FsbsZw<@+ zFVa;X&Spq`Ph4WsOD^s}>OFOVVc6O($8je2R46h|ZrB1mhzTCuK~W!8+iDpVM9L74 z#pEkM|Z3)7qAz=JK-{qz3DRr*2MA4Rk^)_fn&;I~< zr)Y_er(`Qv+49~%_tXLSDuetr3Qmf`*qW|@_z`0N0IEEN;=4qqzj&*S-YO$UT|!H% zbaoPc0FJuh{_)+GSdcrfUH;tcTmHaEf3z>OzbRFH<9{4^t0J}CRSM%_$5$1fY?PHa zw);zI?de+euDKuW`yM9EG*h~~{nO?g+!U`JXudI5m1TK+x%OMOF=V%6u{+fQ42PU? z^r>Vi!qMs!cRKo-$XwkIN-v35{*UW#>nC6Oud$!7v|NW7@Ov_{ak3^xe$kL!rLz_H z-IZ6SxMip|R8&GqSJFK?Yv;GUk=-*ZE8wd7q5E=|A2tdGU4csc2U1fz$PY8@{EH7R zJe zX=$MnQoH+77q@p(K&k7mPVh*kY-4d=H}^8u5pe2tT)(_B+NC-P1cSW~t##O9lSSwI zxctc&M@3CmDe0%PFhS%IjKs57Ic^*mVpIlpaP{~-h zKG%Ct1B*&_>)Sv#`RPR)4yxw(x02%a47)hLW;0*5WAkm-25wHp0dkV5E%r8BO13{; zE3F?Kv2>ShBcjan3-}<%JWbGwCZiV_4Xi|qHOV&iR#hL~B$Gq3s7a+qro9s#vnl25 z7m9AJ+Tm9XehTSuwkhLBD_(KNe1YaG#}wO?%A1*dgJoh{5auO7A!|}nsxp05N{>=C z&iPH&QYLPz1T@Q0?tV zvnAK9tVL9pBruR_p46mNF16p(>y%0Eo_(8)mh=tzuEAyOCl``dHXFj@CcRtvXs(_e z{{U~dju`f3pXV9>05!=N-HA=79mUY(MPOF~$q2M+_#}_#_i>^xkB^uW%6El%|DQe=} zd4PRv$3d&eO*T?C+MRXK?fu2Jxoue5t|^uFw(`z@O~-3b!&P&*Jw7{TShjbI=N1T_ zLgw61mM@npzEKh-wm1Dh3+h0&EvC>4iD;zveOvFWFCnzZO)%K)vA@A}UJm>uP&?FS z`J&fR{4`xp;~%=;iizXgMopq6!;JB{l3BMHinap{r8JOTVYSAp0?>r*wMZ4$8O(b} zef1t^ZmRS?6W{zqwoPrN z>xGaJAqpif)OV>XPTrbVBKDf{*3q&=HIu@Ja|7T@7OHL-UY zzQ+njA$t!>70qGhc+9CUo-rJa(C!1OY{vU=_@B>?a)*#txmLTbRprH*rtpkTBaz_Q zo11Np64TKL1f{~XB}-81Hr|?^eZkAxhO`1hR6DP0@K^h!bDBLt{{Tfz9Qz~j+_;z9 zG|a`9a(lg)w%$E03qq|q1ZR@tY{r_>fKyK~i@qVtek~@#_aWOM*m66Z_Jz_0Jp4C<4BVs6C|Z(AiE&gl09Son zjE(Kl7ZJ^`g;WciPCCXvrk2``c7@c?Bl&;Z{{Y0e)|_(@;|@%xdfyd!3Oi3o|K zFNt(Tdz6;~no5~Nw5S!@lp1c!o@F8~GK7ooZSdC~khU}>SIn1>UgOkCLFY;XRczzYDSJANG(D!j+Vc&|Fon9Y1p!(?MzO*T7jw~9W= zk8rX~9m{eumP%6PJ4r!BO##YRyAcsN>&@!o@sImEi>ef5 zgegc!cex+~QmwTUJC@1V29>EoPUqv^2T8KOl~B4Z9NTp8j9#>I;rm~`(bi+ zH_jYWz_*S^$LyKMD?)MSj@?w@`J7C&-6Qwz?c99T?)l>D*eV>{{Wr%t;%wE!;x`JW1MZ!=UT1zjPEa%GcK{>_I0|HnKsoZx|J}5sH`ES zBodOHw<60wj*jxnb+47*1x>^Ave;ydch20$9>BsivrIq?atz?zaPH29t@aLTuEBw%8VLP9E?;Fw3PiP z)b-Zt(mQLW)y(}qE1u>>?To;Q2y=yMHLuU--lM;C+{JT?20dl5+4$DkcDDIc_CYQd zHO-2Y#G7U21n0enBM~SsxnL*&>8T&8qo$(IZeI5^A1Lgs?eda{Sm4vTzG4o#=sNsJ z{s}9QxSySPixMZ9ZZomoJG{@63e9tN=9`_aUGX2)T4SO!?*i(Ue^+QFT0AAiql1fR z(%m|DP_ot;9n1xebxAa?l?<-ud6~b)1W5yVSUL4DDe;{rW9{&KWvP;{tRL(%d zpqPlX+rRYa7M|nU6}PQxr)?#jj&tOA*m@$$-CIh=MFR+ty1Jdd2f6$b13YIMCIfWJ zZyBsCcOQnRc`RGmv?&Sp`!3-5E5o@b8RtA|G`>dWh$(8|#uJLT`=0g2GtaC3 zUO+-s*>4p=!!ON)b&XC zzA=5zGRrnEV1~K(i_4zfz9<#_Be`llD3pLnKMg?0-?9ixoM^1E7|AZD3}&@mX^(fu z{C?ugRlr(PdV3!Cqv81KfIr+aH(Bjex6&)#hD}XQ!(1?Wxoho>K2UXDnBQ-Mur6@( z(MC82+$)qkH^5}$90!{~$azncX1RLuHurS1#k*LpFTTo~V%!*-D8Jc)05t6fps!BL z9uzVl#`!>AXS~YEb8=>r%vCLw<_9#$z1;F7+$~mXz1im@{TsJ6lSXNpWQquc;a?vVFPyr}7(-@osrXIC2#3L*aV`vNTL1jwUm1L~GW6K0-yZ z+ZWnlwRfz5r6DGtroDBX%Ccf83DB!tZLPcy&m3^+j8N3~NWPuVeS0WpQE_Bh;N5ZbDJJQoB6p5Z1-J`0A z3E2Hr{Is&G(RFk>i&Xe^QpJi{Zc5`h^+u6Ytrgm*VYaNXvjXDMqg{dq;nXCF_U9o@ zd9|Rn5`KV3rjF(l8?{M_aOTw#bE)+I0O4u2zNHE+2Eb`)vb@w{jb?%iGy(|N)T$uV zAG|-)pq!!+Dm}Cc+eV$D^k@`^uXIVZqRjyMYEMlBU?PPi`tAN&n-~bn?l*+^mAl4Y z75N{^oUSq>Wm%MUTlnxgk=|NZ>`IaA^(fZ5!19};ady}n&~;rqi&{3;)zdqxmniZZ z^xK^4&Pj8SsaH$0?>hX25>~ezSyGfq{B@?Zwrp{Rg_6k1WR$p?sHxXG-75V=^*=Io zHOv4irxlE_xe^;?u~n>ql>E-xL(KwAEF`HbD|Hs@eZI#UgV;h7lRad}ymS7@-s zLX@fRAuAwPwKb}V{Izk%u};WeG7;RY?=R0si^fSik3g+2k=Z0mLM%LGVt5OFGQwMw zglv^N+AB~#8tcC1lwBAXoxPgE$#7)JPCW9OiufpFiZFYRC1$ovM%6?~xjX}p zul^fMr*|Az$s=5&85wi>bX~&z`y2CDs_Tu}Y*}hC8+Fk)tbZ3|Id-_D1TwQ7H7Wcn zx8hjDCDL?`H#d<_BWHDCU=NS(ejR;vM(WW#I4 z9D{x>!o;U0S!*I9&172%s%cI8LFj)CE7WAYbSb$VBsz^+kfRG8M5i9MC|fS*sFiO~ z?Im8MB+|MofhrZq zJ1dhjhg4DOygzqreJFHp5)Q}YMwcAa(aJ2tyHIiNtjcjL%VffuHcI~hl>TQ&Bsx{7 z;Jy{3hNx+2Li<{l*-!c;r=k3H3LCdIS;-R~+9v$_3LZ+pBA?3O-nzAmE3Fj`=O1gu zLSjv|$$;tFsPr`S{I#OCostb!BKH>foQkzA)*RnqQD4hK;?=GiDDd4mniT1Uc?tow znhmI~nxdY}gQC2*x@&?~nHC+*l_6*f?i=h4YxwDQx2}oWCUO{(wgYws1;a=$7*t~g;)#mSXeTK*7;c#;WY1MGA$*%`4SnJa#V^GyD z87oqwcABJh+M}kuvBEviw8_RBAq{%@f9^lA`EE0DzT6kX;orGbNS+0#^pAH>UPF0yr1puELC&uB=><& zr|Al)0Mkgiu!=@dGyDVhM> z1uOAC58>iEu0V6I1t#s7(P1+*HuluD8~# zbD{)_6_IvICd^|b5Ign&9YEs0Dkih$87?}qhTbl<(M&n@B9-~{)6A9a)3Q6PMXko8 zLY@Zy0D|TSF>+f-w@h$Q&S-rd-x4XPa+st(V4%lmd1vN{)d`z_;N_{6+;OQXxQb_5ld2FO%=(qU%^}ORk z6KID}vHF`M*HC4MtJb}xE5OJucIBl^KCZ<704O94w8J7vb-PNEA?FdNGKOw!g)LhZ z?bA&@08}#xX{y~gm9p?$l>$#;bNK<=<4S4^p)D%PEQx4R^bRI!-0GEhnuH;vQUL?I z*RHIY0~$3{kH|>#M(U5;GcG>EQ$|#W>b~_gnTsu?=t6T-4=q&hQl2T^(qOvWLJCBrQ3}6Bq>MqsYZkH)jzKH zR;6p8vx+fBgqRMa0N&}~;sWoukPi=vPquP?4{F9_T2BI@5%U4yXl27AZN^7W^ zd|?_(gnp!o{B+|UK}^X?BW=Dx6gz40q0v^z!BnKD(i$lSrC+FeYRR)`tKQ0#hCkvG zR8Ic7M151l=&YxV-Yj!pmU*zie>bG_y;DBl%s;qcFu-b`@xAjS3sE(+mDk{66KmE?Bo6{g!A zO7|XW-qgCd;`a*e3F*-4CB|;f-c7XJ=PB4+71Z&1k98O|1pTY`u5;uTMOkiAAR_A$ z@|emgROn6@qo}8;LdXF^y$IOqTymEhfkfnQ?2{l#Vl2q=CB#hT7U4$5@$50!YUmw6Gqn}&RRwD`$|HEFiNJX>Ot zxpwdFF;eI5r=`amXobmA!BTsArnRX$V-0Bgc5QQu{v}W3Sud=c)h(gX>kNwBsHgW{{SPn zx02wx0kx;F>Ck9C-BomLN@+l+U^T>pBt!mpfv%(v;B{Pv$$XFHOZ1EG zKaA%WYGoa|`*2 zCGKNMCV|9I6IyBE)3%GA9KmI~BKs!ecx~_Q6V_dn^n#^2>eabYl%>UBxTF{U?k zqUt`|=Hr$vt#woz+uOB$eKm)eF6RMG8zh3usRaJNT~XY%U0FVbKdlqhZo9>iU+FnO z;!i>kT~>ibqY>zoZ)shJC~iYk{k)FVD!+=DSPb?8IWvf2U!suZ+ZjrI#QvpwU{L(}YoYRM zHsrAaR%+%AR|(;_uEKIBTf6@N9KAkjgv>`{souN#bl0CxMeGPql`XPSKHRn_3yg}O z`t81w!UB^UyC%a=bo8Rr?P3 ztK2K|5_4SN;{_a7y^-SV7G)-tP%Mg7U&B<7esb9l=Ns2uaa&<}ki#4+AY_gzH@})Gl zbWKFcl@mMZ`RXE_wul`X6sL{ApMH(Wpo-D4NDtdI%+y`U|-h5=q0pyDObZsCY z6MV80R1^g#`H)3M-87=so~hywVy7M~$k>wn+;aG^^H+3sM}5*%Qs7Ams+CQ zi-^2C<6Ny0K8KK`8iKSWl_PGyj;b?Q&t5u@)H*AyRzTa!PwjV9cgju_webdH0{;Mk z$~Vp%W zekSLWajg6HnNJ&KXfg5_k)0Cl_ZV&t(DOw<%#dsG*EQvsX60e^q8nvytF&;8``?bKo*Um1W6@pDgJ17$T>3z7lY&QEurP60@f2AcS>h7mb?xS5U*oK{_ zy5d(cIE~HhyNUL^^YaL{;x}&#ysl*#CyZQ&BtMK@q$N*Ez;ZX-`fgCxvaZt!p;k=GJ%Tr->Leexo3#_@1gj{ok@gI{MLyh>kZ}LOT z-yp5o7>zWBQF^z(DZtAHCwI8=5_^XA3Ita{h2#)q8eaEpe%e#wxnC{e zw^;ex31EA70vrIXE8F6_8Rl1em%gPQxXb&B-jk=nS5)5EFj-jz~Ug}DcZ zzFbkp88**4z0pO8GMk7M82aZ{C{Bu%%jYvA{$O4NO@3XKg_@iiQl!#85! z9W@#{4}#KhnDIFl`ksg{{_Q)E5Agh#MZ90}>xD3`E9GkhPZN>j-H({AiVifr)9}wW) zD&l-|G(1tn*tM?bmo$(1iq{GZXz}hUX(cU*B`G0bSNVa{vu7_Q1AD4dQQ)j0VG%|e zK1tDoUh~Rw$w_wMXlt6_IhKr2HJqTDzO;2bQJ8CvN9WBMfSk|90 z>W=Fn6}`KjTn5lhK1=iO`;vL@m-$iUc2UdNqciy19k)ep!HdO(J^1fC5)-Qnkhde2 z$@i2`(^5M?`0MCD4d9m8+Q{T30y|?1N^Mx-iK{P@FX}{|9*EqiAGh5u4ofe)e zsA2d8jrF*iJAfVCz3ce@03~Db4=H)a#9K^zCBMRMLd^Jii&WNc_DucTYAtc4zLcoU zx277Tf`kxAHTr?B+Mv4Qws-UTt&j%~hy52uvgTPVMYp73+}cuv>OTsP&2yuQ@~<0W zW!Wq~LB8K}{z&H*r$OWE3?A4x?m2S2OH+~2Irp3ns59P?-hgRNhPu>bV!M@$rjJM zK>N_=1S$CLg&(bymlBlyBATV@@!nKy0?&YyOK*vGmrpP^V6~j$iH!7I)r; z@vngeF!Am|ym=4hP6KlC1BEc@*X}pFp^qhQ)1$QtZzx)mEvtL)UBc3mWXDTg;YmG5 zZ%S)ho}KvoF?Kw zi-Pe!Li5YGvm(}scMC1HT2(15#99=T$Yc@{p5sstQU#ivIv% zs04%4;8nLay#P!g1KPAo1Px1>G=a^CXYVValz_3`dr#r~_^NZj# zyLFn%`p9<~x;i^wiz?*rxZiS8l!BtOzQ=KC#;)ywX!DXt6ye z!)oA+Cyp{`mpo|0j#AwD7pV^-FS&9me5oo*3REa7P<6WCyoP8TAvGh#bDwkgm9+7? z;j|AuHT!s#z@ApT|gE_($m`Y6VMVnmbvxH% z@urx2{Im(!A>3;4oRC6qFI=(2U8795WYC_AHd`$O23=ZDwmzT_(yDLs)<>4xdX~O! zp>%I=u7p?Nt^FlZ19!V&ce|D!n8sUDU^rWK#{#KAuu$&m4^VZ&ohSy2)Iu`RR8sSd z+3y|FV<>xy-32tEu7F7`1SrOK8uW5XQ?N9pYx(V_ob%Zs(YPdMY%d{I4ej#Owz{Y_ zq<2Qyw!SMwtET#9Sp`&x<26njWOrL3MHNt|)O7+^f)&M`xp51oo)V(hg=RL+SIqK6 z;#l?`;v^<|i1!?D4U*DL2{q|WaX)eGB)ey(28-6-%}Z~M{{Sqmr+{Bpxwnycwt1;u)=EEL(q})*Nc7Yclz9@mPXb&KPB`F z*ekYXaE2&dNh9suM<0LB8>C=7t!wg6j9UKy=r52NCnveX`q_(gWC8s`ihv$Ttxu@< z>z3m!?U{UB+H89DS--uw!v;R0NVNEnzCXE3GEPe}U+$)=JfKJkuU|^korb*+iM5O{ zP%ks`EWDDqhaEz#Q(`=#N^Svl=xRPcj;|XP7AeoKRD|SdV9_!`sWjZ`TVhe%@DPZ# zzCbTY04UVZ*G`QQB_{1~5(3_QxU~^hThiDyQ6E(>;Z|7^7TRLQEHuCPl@(Hlv=}|LnR$^+)^Hcnbl6OK42m^g|k8fZv z-6b?!58m9oZJIk~nz(9y9v^R-iK8g?_9mPBb=n%*1?Tyjty0`ujOJq)ZN7bl*SSBA z`cpxotDep$KY1FZyA|r`XSu+++on5fxZ2m>ZMjEMnSF(PP~s@09r}Z&oogJL5GnFZ zO483W2?yX+@8D0kUz5BIA?r>N$Uh9Qs>>$|6mi1elSGs&-EpQ~3f%K-S^U6Lb z_=C#X61hRF-UbmGBkV39v&xRd8O~OUj`M9Adv2N~Nu>^|A>|ocW_oUEBrP5148h$^ z^$c(o@cw9`{@1)!5qTAc&5Rd{m1>dO@9xmXy6Zmp*(SueaC*?3|MkzxVq%;A2_o0`-|^! z&F>piWkt0B>~iUhzx0;XN)Tvuqt5>T>)ef}(|BY2dKJq!)ZEvFJXSYJ2X^%o_;(+e z_$!%z;#;TOPSY=$_<_5Zjl92ddJ|lS#b3OV{wI4v!Ad1I*jjzE*##%L0I1TVrm=Xg zPZ*=Q;GdtkE@O#cSL^z(xQ)a2O!4K< z9J)zr+i9A~_fXu1l9VnncBG`WQBZ1Ywz^fPKfy^B^3V@`7aq;~H_Pp=L`{*;0BAv= z(w(}F{y#MT0P$PNuee{1s^rUFXNFz*FN^V+a;$TvWRXnBZCxTPmx3lZ6!MZ1;ZSgr zI&V=;P;y>9$MQj@H-@9i-R zvwyejWCr5J;msO=sx!GKEkDN?e=_repT)9HUAy0GvpA~a>fzu20O`DTQlK?4msnL2 z($>mW*b1oxSJzsbZ=t!AvJF5TRvP)aD<}l_CWYrw;1U*XdB)s}hTronlFyOH5#3%t zV2>r_-tIA$R*?zmOaM#l(3FEvleKr%mNGKOQNXw2sB=?B_{*9@wCJIX*NXEDUj^cu z-y+~pGJX!1B1T7nV(~zam135^acU;uns++V)+m=Zpfo#ISJ?clGfZ|mlr*h$(Pgrj z%Zp2P`iu?)R+Q9LbCw?pVO;xm<>}Vxu{c8K4Y@bmCdg3hw!7p+NG+I%S8_e4)3UmD z{0GCT-12!K^x7M}r>XEr%7S~W^p-b~z}bj%TU`&wq17~Qyn&hJ ztt0#XZ*#j!h!U7nCQE)pp)C>Ek9EbBQ9hgP>8Y?{Y_=A)pbwBtPnWzB2RZI{{{WOp zA01S$Bgtj8y%;OG$O^Zdd793ubj1Yt-)idg0IE9hxa zN3${HtYj{TZf!IQw-YWV_9nw{c04+yp9Lu2d<5c*o<}KOJWuDA`BR7dZL-@xxvKdk zDVdq&bC%GTdm&1-{eTQdLo2w9Z;{>$^Ryx`>MPTb!Q|gC!h~w3Ho`1F7S;6eINn>)GUq0#;`@_YPRFHEPT=%s%D`a6Jk6{BC`1|U6C&Cch3Z=Gk(6v#9 z+a#=z5|9c=>8SGG!)(`FOGdDEPYg8I#g7(@)cwB2a^5_7R~e7quC5ociuv8gkx7a9K0lt{YNrsdxh- z$z*nmLusk2awGx2$m@^ia#H0c^r9LC-?+vy+XHLs&V1*wPTb2CgLTEOUS0B*Z)6-4 z^_pF)gE72*?9AKBvLaH;5az&3ee9?xEV@#Ig25=Ui1+r7!*j zD5%}(FB*zA(tf3UdUZNF9*I*<);6VBO!t!_+gan@IO99B@i49ym}g?w#tCLb+=xCO zcJlSdSuv>y#Y+SZK~n<~+-RRt+@G^J}wY4p%_P}(GG#Gc>{ zI{IiJoCdagadBV#c|M-O^+R0^e;r3gn+Y{4S=^F;@zkXmN*E*vcF5OE((vDWD;hg( zk;vKU>mNYh(LGL~mei-?G!;?J#^ECAE=9sx%d#b{OL1yD)amy!iUZ{&q{8BjMJe;q zy~2p-fh3*In4(-|3c|Y_O43MBBYias!yQtek)X9rH=9Ij-Zu{L5|-tLlSBTh4afLv zsb5$}J)316Xt~Y4MoW968PrumP9w2vuKlKz{{RhOm?%oy15k_T+$x(QtIBCUnpIYc zBWfM%pyqfbG%r$WE2OX*rEOl+{B-hAUT7eMYxU^SIT;!&;^O=tNl0K%{E31mz;E;5yxr7iGp2}V_*v&X^iCtRzjdC@j+P*#U z!v%3I;@oX+jEjRUQ7KfQj^C&!p-$xM563Ob?GH4!<26om9)4ME=8|HXVNM`e?D^to84UXb{H1)XES6sV9LgOULeQHvPAkjY!w$$le zqD&o?%<-=ri-==cM=joy+TFROHDwANT`4tCC-K*K_O}?@D<|Y69R`cazWL`LlE_7g zhx?6P@>P#2y-CRO8=c}(``nuHmbDXK)O|I~vX}0=yL5-CE3@EnjIy(2E$GojDse^r zZW8Rv#jzw^C$^}u%?&?&z=eor6qe0F9!v6W}a zE>T8~F$T3H)Ys}d!?IsmV&%SbOi=~}QX?={vbB(FUH&Il{+(4bq#-!%FlE0fi16aM zHe7|FxOVoF-0X*(P;IuG9;;fE5|Qdi)X2bNNj;P~M*jdP>R0Ls_MhPB-*4XsICF?E zR+OBhB93xSRG!$X{{ZR>-A4t{Z7*TCWm-^;)jQX)S8keYzNygaXp3C!Hp}`_ z)cF0f8~*@-IR~}WABZDX)|m;R@1n1|#zQy2ru2p0amLBIB;4%?G-l}1YT{G>0M$}I z%xcU{9OAtSrk>77bn4XEy<+yPt+d^3Q;oLj*VPK-!l}62cLCStHDPOhV$wlYs!Oa` zo6Q%M$~=FOulQU40NZ&g(5Y6}Ph?!4GiqBK?N=%KPNzQ)4cOlUsC*SPT$bX*?7C%J z;-1#|mg(%bbjPkpemldtDMEsQ1Xpl1OEgkMcE~>UrM0_)X77mo$=_|fviT9^BrzcE zQ~Fh?64~icRn4|Y6GsGUR1(_%0CR6pY3`~({R`AM-U*|V@>}bjCmt! z)`Xvt)8sjyB-3Jz{d%pNRQpg2ypQe>2%u3lT1TZcuPgp(*x4i}#3SrJ(~0kfeb%e> zFnWN09YuT;Dtvb!qVkxHC%LDS*iY#vuAYM7(d*eWcW)fyQL1CzarLoRnon|ir{$}g zY=}A)ipb#0H5#UJ)Z-_$M5LhY>8lw4aM5Pi*G6?q_S@Q;C`+cP{wGi+vuh8C(^hCOsM^cep^`yaanXTVYXhOmA&IfIJ z!W`Oyxo70Bgcf|tJj;ca-k295+*)z42Y0CM2^;Iy9^L!m<^dhUstWnz?;q`SOCg@f zYbrK^z7C(E2CjKXmA>+KJSbc*5XvfI?(F6NCN5@{N~^_P5f$o?~n30 znQl0?;omJ`@d87vGbObC;f2OsT74=)zJ-t~HtW|^5?Ohwwh(1YfE7_J>qMtonEO%G z_p}P+z7R-FRn=XiU`~}g0|M0AUo;R2qFv(tBhI;kR%HVRjUIfg3c+#7vKY_4i2nfm z3AH6Ekfw{23UCDqC)ZhgzFn5vL%+D_w;0SEw~c$FAD~q0MH#s#wWPY$N*qW~SD^=e zSh7zoRWl*0sCtB!Uzsg!#$bDn$Z@urNl-}dbzuqI>^C~9$>c;(xf#6DzN#+YU<*$icnN>yT+f!S5xkYK+8@cBv!yJ^$C!wQ1eCx^5|UJ+jf=h2?(_p~ zc=F5l0d+1_v4;5^>QnDt#ku{p*bH0T_a9ZQCbiPPFR@=LR}3ccE(iDc%(S?VZr~0f zB`WJmDxV)sdelW$hYf1HiDi*%e5Sf6(oA(BN=m<`P)H;2)EQ$dM-0G(Zp)NkG08}g zYM!hTE_mNTWjU(gcph7FI}^nZhw&F5zCncwhT!zCD^0` zPi9GwNMFXN+f`9rN*s3$5V^p$j1P{WpSeW$uOIQ;t+%J~{n?wERZ@syY4;L+b{cW) zf=<*Zt@Bs5-A3lB)s^vfM~caIJm@QFP1F>rBptm)bze3tw@_BE8_S`X(b^RmzZv!v zq$wpNbf_a#(njF5&|ER$7`I;TR;PlQeN+_Mr%}XJ8mTUUr&MD7$(dF}>Xjzowot$Q zBq)JRWM;XM{YunX;~|N(0Ws9$j{Q!pPAK(KiPI(`PO9mxpjbwv5k%WxL^FSL2i|LAIRwHf)~_jPwJusDosb>wwsLVqe^vK4?+%~YigNrL;K#f zHTddVWFbQ$g$&B~tvc!ObxA2fr*;7EO-XZtir}Q~F-HRM`7JEp*ZV|vZ)=d zJ=&jn2_;Dj?H$x40!4alsja9qP^YHq4v9&pQ{PpOpi{W%w#Y$HUfP5)kQAdpKnLkR zElv&zg3=Jkcc%5H@zc#>Vn$UcrDmHS%T@tZJW&RrP5ReUV@>r=dq*H=7}b*h0FC3B zeagX)WvOi$U6vHb;>Niln)V`67*a#2O-&N2jUXU659hr-S7q3wlHr(36r^9Jp(%nGZr7HBV$5WehNKH2w;B;A;QlD%PH6Kdj|)uV-}>6#j$(e}69j#^3AxR+ zG>7Lj_pqYJVjLh4UF`}!gG%)r2bZcT?ZFf&&&9lYj#%V-4;+l@!N-5!oq4x&yozvx zVS?LDDen0aD6Zb_(3A>pqfJh@oQSG*DytojGCqeuqD+oDESLM$jy>8v>8m`+F%x-) zzjhqI+Pc*<)aUv_w$pOIZ^cA>8_f?yr*gbv z>$%?}*)5JeW?bnf9l1(MTbh^!)doG}G)rO6Zk^#f1y-ABR_$>$Dz@s?!}OgJo&l00 z49#(z#w0lRrQ=iMP8K<>2cXYPXVU7T%4qDiT?r_0KB*_yN0i2WO}nE8l?d%`pKU8- zARVL{AAws{sHGPQAxsnp*5ush5-2O|C%<V0;kLRDCqm9G<|kyV?iL1oih7%R=w`FDJMFlldl-795rp!P{bS zyNh>d;YwYo!jvT?C>_L>y}s}O+?r~2rfLJLTnPE9yPPe{owdw0@1jNJYbD1t!V!}@ zDm&}65W?9GBlknK6$MBiQB6OaPv@8?j;X!{k!Cj`=>R}^`;`!s-|gR9YWiq;9=hb0 zw&jI;4;OePXe)Tm`)&$qj%HP9U^H@hq2lumxPw(QO8kC0qqqbWsQ`+o*z2b4F zHhZMn)!63SgisM*;i;BW(5U$cfoh9+7T&Cb41s!Rch0a9)U|s1B<=YFtIL|cHI=jf z034)vrJo)$ri`f=y~SbJw*)LY(vHL^Q~Wis_VXgw7h6bla^&9dVI08QxW}13&DYeXiN-l?*^)999H zAhaffOeFxd@A+t|k81X{{Ip>>(6dI3W`Z@kKnhNY2L%1aLfw)YLaE(Ke7}ySTNPg| zP5^ zwZ$Bh5q8;y0>8C?cHG#4lhEy{QIMFFYgAHX5`rB0LXpBaNuDF9Z(79X96C zB%tIrh5bT<<)W%EfoV#N;$3BZt4XRz>ImtkgVc?oNpt5lm)e?gLc8wJk^`=FQ>g=~ z`RES1rXQk_+Y4uQf~dFl@ol_5qftg7!6jAMl=S zO9qWNss)al?@)D?$=f~``qBOCON^XOJs0%ss%Iu~zE8xNt^WW4%i;)^s}sv<^w9fH zwOY3OMC_*BO%#$nHtG(qEH5LSn}VgivwSb1Nl-Go>{g>ha_0PqanJm`MYyFl*dB>W zi2!~Ym~7)3)2b$3N=+(g7f1L9Kg9W02f1)xBFjLs^|aqy|-FX z{+yQ7b|ZR%K4V=+5uT4DZrNL-yM@8|PZXaOeCT8zTIhU2g^Dkj7DTw|k^cYzUD0JE z)ep5=q>oAwtzER@Ms0Gz-&?u~H#L1A{j)ykFW|PD4-<1G_FV2QwZ50|%%uxcmwX=P zx*;zx=IZ^Suhx=i2g_l|IS(3^&VQ(cY2don6O%)IZRpR|lm&Jw{o+OMFS2=!`;0<){HP7M^GB*DJjQ;?2 zG+uh<9@0!-B+mZ;AU~$M{l6t{{@0#1{lfSr7Y<#s&O^pHUxcx=OO<%>>k+WWjcmN8 z=E#y2a9iO{2@SfkJCJHfB=y!ioO6p?y0kaV<5jQr`+?-_UA^z}9^cge0MU1q7n6FO zA)D;CKYa4N$m?uT9g6)ZNk0v7iP}lrO73t#?tmmK?-pSB+_qp|@~gyoj3sWjV~urw z7jg*gD^epAD5vI4H7SxwK}OXvbsGojG*t7+pEr1u?fr<`Zxi_r*O_Bh2`GQt|A(tz$Y)p&=s8C16oR4w>9MB0S;2T8 z`uHhw&`9q4Mgh07T2x7mqlv9WNKcD303*2zNU$47H6vYx_}wDAQ( zfL5BM<#zU%<^~r4M}oNFISiI!<Px3zi?nzc9=+f_696UZ#LobNcl1adX}LwCkG zmk`n|Yr7%JwA5x|l>XXMi(yG_s;N;F*y@^pb0U%^#|R~@{YzP8?F`UJ`L0M3NPMTL z1v~MN1-Yjf<-B_)@|Q2mah_G>rXxkWWL8UJ(}qZ93!q1Ve|j)N7Q=yOwM8PG#@Yjy;zc2XT4w1q)t{3AxfUNSwj9S|D z!rpz=tJ;xi5zwlYD2jqAD_T$;bbc|+$N|` zc}>+a`5F#yk~KRd!KHgh6CQ;QeGAcD3l6Wdv0EO_?NRcnNr{%wPFxz=X5 zI|079jJUh!AJ9z~prYis=Ee`G0+<=ixVq(Uw_Ks*QIl_!-EGp`QDnS>L*AcsC%GrM z9`2j<)l1nc1xPej&|OCIJl$8xkKbPb@ebRZHIw3;muRv1D{ViypU2i_ly29xT!{AM zOMM5~P#uK#t8u+P8udT27uQ2ax5?tXZ~K$R<6P#st{%AU-iP@wpKMP)oodZUW>!zhy zZwk48kT|l*$vjDea3)ij@^>s{HjDJkrxUn8Z^%<^wK|1Ov(=u*5fW2vske$0pVUFt z1DE6Nz~1Qq)2GFDZ*MZ!&S-^^7c^5*p{0I0c=ao?JpIQW5b;!8TI-s4UzFzjMa$M( zwncB@Olsjh>(>)yc9nO_pLA&w8vTnBOQ|YxBkCfwJ;uDB2|Uxwwkbm$Uml@%Jo_!} zBHb(-+TqX&(Ce?m=BNjbcsCUCu5W$Ce4yalb``{VbZ4`BUoGMeF)t>KiCfw!Qqq+z zRX{>g7E$#DEkq`y9dEO@SCE#7-BY5$aZ6lm&2*O$fXY-J^z5l-XXA!7%2tkSFcLlF(77^%MG%sv36+boAFmwtHn}>gZP} zm9CCL!_*;VWVg{~7`4iKrpGElmY7OPLcQfDx49}C`8IZiU(HO(Y z?aJ*%!5~rAy7sT)eg|HCbZ#;qg7u|Cm@ob%&T4-9nrf0b%wv>_V1%!VU z^M%ZA;Fs)@b=wtTb{NvsrJEw$_Y)txe#9(*N`4jBr1;cxvfMT1tH?a|dr7Yhkd3wM ztJI5I0;Mu2E3mI$;jKelx$$60#t2E-@<+zHb8$>PsQWSF06pia^6u1tQlv{!CqRTj zWuI|>>g)b`IKc^^r!Fkc)nv&m_t~(KE;Wv1NsETF*oPX5`X5>fy*rYUE25sIv^poD zkhR(_;`x~ai8BsWy=Qr55s~oxjNa92oo=_>mYEk9oPG)mYeEvWkK3qr_R!D|p1R)# zzLGmS4=@YH{^?vb*{6DyW4{rndMYW&+;PQtHM<*o1m`$>w|Tv@?q95s?AIG=?5=i$ z?L6etQt1Y%sy5oaHPJD-Crk}nLKZ(QZrF=`HwQG0Px4U&5VbF{Nj3ifVAbngRZ(?7 zDwnM{1Y2$_yCT|~wZGnE%(c5zqDyFTDqBTq`j8d@Nl@HZRd?+^nQKi6Ohy^KuXejl zS7Z2J?d_iVfp@?;r^h}jvEtkj$TQV%yG3Gq?;J*4U-@ZieGgT_l&J+kSAOlb*C@#Q zpJ#?)ic`8q^j&ie?3P!OO53QY+j%KbUX;-3LE+cB`nm9v?SbR>h}e!q#n%?H@=jHl z2F3d%pKWSQT5K9J(k6Sot?#%8UPtCW>)&787d6xs(R+`B`zwpYT6$rClhmH+tIpqO zUkdr9=WckW0=^2$-Fe5+lHu!3xh}|blBK^1K~deQH9g3#thoOGy%}3@8uxE#@l|}Y z_UD1TtJxTiL;cOr{#}0ht}*l9+LO(WAe(>1Fg`{{CGn0=21^n0hETXut?V`m!e3zv zG;n$;v%gvmcKjc^7(4I$(T2TwZ6lsNuHBEe?b*akE;UlyN^(Pq32i|%NLk-o@o9scRJ0I&3zS>ma%iO39Ok$ldpzrN3|w+KZB9gc=`~xVCHAJNr7m-PIoj=q^3`h{fMV*zX&#{Ca*XsP-;x;9E8^k8;lvVpfh( zyk@IbKaS)!yQB+aFHT?eTy4d@+x;~56SyY5H`WGR9pj!`I3sV0>vo)z9UBAfh#>eS zw)uL+oj}TCVHqvI5#>BEzY^sRV`0N)xOOQ%_p-jlIM`7YC^S_%>Ks_Nz;`*N%qq%G zM+N0r*M>+3bqILpo@A)EW}IE*4D5RMkxY`Qj^A;n(I(Z9MU>}b#$-ZVQ$!>sBy=Ns z*H+nU5Xjply+Hm7hl}P6u6#Eot{|SB!k`~;pAo!i#&B$}lXx?Y?yKdzo^(4^w+zSD z=0y$T4$7C36tzF^0I11IQP-i`n&_V9V{muS zhwdBCN_Si{*o?m?#NE0Q*BBer5On!`{{RKoDc)dsr^^@1j~;mmhpz*&VN(UK9pt;3 z3-)PJRx0s>C6GO!Tv6E29sd9waR%^f+c`Jc$RFH}>(>tVD;<3hk0lqT(rDKngs5xC zPYp9H9>UKjc$ajyawY?Oi2SIFVj3~1KN29&mprnvULWbk#tu_yw!z6+W#_e#@ zrD8d6wigh@Zn3W1-vo)`r!DZVEstV%zH_kO?>Vm!+9lX>3zk4#cF%ie=-@<%@)Csi zHipTgVuT}ezWRZm#9cA2`a@XUYABb*xg~?kez~XB05p;m>*PP%Uy5_8=P~!3#RGfta-n&nEbmws?pSUU>6|g z@wX*m*DHsNzDdFh;5awg8KsMZp(cuhai1wtj8SwW+;z3pN+5qay83Ib3yAL^Gg-fE zj$9@SJG@21Z1NG8+pyR3ul;)0<)U|Tr-r^V`89pca$Xd=S$tDCwm!hI@om*D#%kqv zgxZ{WB}GH(bP$k52_mMwH6IJlGMK{1E*t99{-raJa6Be!)Vi~fK<14Z&>y*ThVkH6 zkaqqcmCMH)H>^(F^NF~%2~N87bTqXzU)cc)Y$-G#Dzu@dx`n5>`1z;woa#ApOpmsi zi%Vas*;aft{z|Xj_}?tE+idrV*qz1|vi|_?wQRY+3HBqt`vGYf0(>*2)~^ ztcHwmKYGSZYjp!drD%+M8B9GijO}=Z!ugTSttzCaq(td+j~`y;yrY`Dt;4PIcw_Ds^}yWhoSSuV%k7?4IJKdfHY;Ni7F&((GB!YL z0IDcdgRJGZ0x*FaomiT+r`)&C8=M$L!_9O)TI;PT)K^kKsL&q>ddtGzMR|RX@*g+E zvE%dkR~U7&t|-04U9&2>#krvpQ*(~XsX=-%3Rkj1dsLvIU9~Cs*fG)w_W2J(;G$XZ zne#9{Ss(sJ^#e-jTJ`(2?xPG7kD_FJLz3EgV*VeaI6!d!kIaow!q0@0k(f; z>Frb%xUc{eKa|iE@t{Wb`tk=sIHW!$Dyvh ziiP}?z=}_}i(4xTG<$l;aR=pHa6G<#Z0z*| zY)$F$UeDqD1`ac5*2s6x;n&097a&-TCiN|MD}wt_r5?rBfhj(pI@hkI#@5E&p;%2J zmDMRe`*U(rh_YHmLv>80r_cjR{PhsX7}8HgCODldgIlRrGlO{3ALBeS-_D$kVQV6v za&*VxPA`25REzD{5!p4y(h2>$BzGpG=kV7! zkpem$Tj@X#EKs>YU1$XdK(YcuN)&c1DO2S{fdU(oZWCQYFd?*__a1r01f?kh)RUx* zN<^;>6(*EcUnE>U8^z@D-akDOb1Z?R9#F`cr}TfATNqzn#C33kx&HtOR|E#obyW6j zeZ&1niZ`%O<~3$|{c;R7^fZsIWU75EGB;9=s*IH@K;ySShrg}@C78D4_d|(PM|A9^ zf&9jvr%O#a>9|l<=XXFf3MRU!PW1eB87(Q=<0aP&i9bYG-&3mj^#Bb7lO;4oXUtu~ zN}p1`k+W#{By<{-0$~}nTBKd>QLXV2-0YQvf;uF6sQiH6PW1_4iQAuEV7L9h6R|(; z*{nmgPWnG2kLo$|-(g)QIt3nCI|Ukd1g3*?q!iF71J{3DB)W;f;Jj5p(H8=oY1M3n zP=$UvVezHN{S?Zm_X}npZ+JOYW~8sMnou8xw(v;!?Sp$2l$nvfYWGna>Qa=Y6w<%# z)^2B1t5yU_NB|N)hJ_QTJtYJKgcWVmMxP7Rs3kNz=v5NmK^n~j7q!!#q6l_+D@aKw zQ>P0k5wT?}r-C^ix_|cT#G_&IGw&qwh@e@x<_pbP99jtrmb6rq?g1izYbDCA_m;xW z{Z{ve*#-wp;nSk2Je{4)&T~w+JGw8vZT3^|ui;l0RDt*kYReyN*y9aVd5g0wgs2JE z3(hHO+>%NBbz&7G`Yl^Rmf}(Vq9`;^c8H=HsS4mfvRI?qP=Dqn(b-=b9)nSBT1iKF zEd>)D4XtYfA3qS>|!(k3jEiV{iDXeDUzEjFxAG5`6`!;W7nLn{{UykaobFr_CbA++-oJ8 z8%vQcWS0#R(@iuAl%wbX*A?}d7}T#w%udorWpQ}7if1P*m0-%Cs?Mxful}ZsAteq( zekmcw7C)6?Nue5vlD(kXH3d0@wA|wJqJ{aJI+4gcgFb3qP=N@D*2ZW%TVbE2AOX^} z)orV3qPDptnx;x4hTG{+hpwkaO;j25U8?&P_*y)-^K%Zo@o~G`jYtfS}XT zgKco@XSAP+-`?6dj}V!byW93z=2fOyx6H5gp*Vk_{{YK=hgV|SDVw$b0E*03TUj?m zY4B6dEu34jMF;?Y-Kg|m0N8zlNLkA>Bb9HLlVYw?VM#t#%?lb=YWavbu z$z?}(fa$0p1)yx(dZpw%hnVkSX^_K%>Q2NIoroh%3b52G5a!6#isnB6tjV+iX_li` z83Xzbn);-4C*n0a8`_PoRr6z8UCq#>sWGEZOV<00bUas7$c$1J-937ahd{{a%~)wg z5^GrH8(slWo!5ocYM8svA6(as%dc~mrIk4e-RehRKTUpG&{=YD;j&;4#bI(BJ7c%o z64T&_HI`g$kAH2>tjo1YNuxJp0;tbZp-RT4KN}o_bWZEpGycwaXeMvoq$_SnS@!*@ zNu3dUg(X!W9aCK4-`0ymVVh@)bqN;R(;&9!L&NluvVk9ts-M0u)zM!zSmhp5s!vWv zt5W{}X?j<<#c0%$I{qi+r`u>`)v7Vf`cB&-Q88J0KfScd1u7LCgb+dBRnpG(nmAEf zFh?M(3NqX2eF2GS_TBVz;h-Jhni?7u6OXPsi&8$OmS~0b2TRcO>WS|IL(w2zY;oqK zxYZJ)xu^c~RyKF8VWX8r_9c?Gtx?RV(xAGeJlP3Pugh9^q+{DvjJmRXZd*gR7RU%u z9;E4+EcXo)cK1c6RPTj%ZT{BWT@7j6bwP7xL8{RILV&bG?qR%EIFI z0#^z=lkjGCI_1XeVu+_9pp)zb3W9eur^8;&;Qr}Bkzi}|a`~I?SF#ySIWwfUtD_yf z7bP>kE0<_~LsFqBmzydCDKsLQ>S6|+d{z&S^Ab+iXh&sQuUM*(w@GM%2>{csqB_@A z*s+=5K-EV#7&i-KTZC8=9E#1!qRquApboTm?NA`?s54lCw>aFJUf@o&=%MGDxLE|m zTjsw2%4tkXQw})Fon?C|wHC@rLcLOUI;6d_jnv#aE|reUJesu9sii2=+J=;$X_&=I zD*zG&H|eMnibhX@w1Ziz)RiG}e*|)02*l%wyoF?D+ivCzdM;6bOlDgX8=GdBsv*Kz zC$V7Dxd(AktmZ!>4esWFpwQK8viOu(A6=hh@*K)&N1O=u!K}oHnKxNw^Wz{vUeg zb>cGb{{Xz0ncuTSWP+Hz%A%4s?MNEi+^|xEvgcNJA|p+`PaG9y)D2g=CAL+MU@q5|pM&K{T&- ztu(~8&u{4}q1f1Fbx74G$>M%L<#ykhfL__NT+}<8$7QG?TlytRJL(*pf4C*UZtV?N zcQ?bjt^AY1x!)Q4Co9L~+M~+e6vrAq%gO6hI4YB0$5ry%0xA~Y6Umu3mBNnjRl%+M zwaptc>jep^2++&fL{s7ZT4cRQqTLE4(z^kr2Lg!+r69fZ&_~&Mpow}mApA5CLnT$- zoiq|2%B|Ao=ezRLu?1T8Arh*M{dFy*CZ4_0R9AIFG`^z0mZf%0yj2`+&G!7pf@GsV zI%-2*RS<9ck;h z)QE{8L6xZc0FhEUYJ4uDgEoj|0%=k4)YhF80#RiOetHSOys6uN$4)&B7pZk$3R6Va z(Gg-1P8E9e{{S60A5@UkX)chOA6~j3?G$%2Rk9xK6wyZFn~{}9JHx5=)}Hhps8Fas z$5O&$#P4)IJ3u(EC3jHZ; z0Fgn`wV~8!$Lb%7u9~f>sN3MW7vi5T@-(hl#yHo;+cX|5yyVhR7A|%|)pd;8$O>J; zZ+B^qPwOfarrOJLY)zoDbdv(=Ehi7k zB1wQLe<)bf*uG3ysM_LwB*QIeD$IwY;>k>9%S-VL zMF9T*x$e;`txZK~N^3(8w;`}K&^(otlie4#@<^{EXah>pgU93iR0F`A3*&!~`4(}S zJ2X5aA1;I}>e1CVxUUST_7?Rw&NgQ|8~ zrFRc)W~o2JRaY?7E9(iURELMnOLAn|-`+LB3PA^@NT52ci^}S&*NTLnY=#7@#w$HE zVtQ8D1RkUiI_pzn@aE90Zb}`ls8zJ`lA`)nR);M{wYERA=} zfWj9!DcwmRQ9(!i^;>N3Msm`${D#zB*$G?JE?ZEg%6uvgv}9>j$%C|8=_xC1K8a|e zMKr=#YS5iJ;!)9NjRmns&`(-vgcx;2iu&lls(k9V(IY~RgGxv$M#7#-XSNmkO*hor zeu^}q8{`&Jnaw($iXY{rO+=VVjFH+IH0?qF zr1G#xG&CJ_+EVbS5TQ=JG$?{S0!xsjCDbm4yVGr}q`N?fqjw~=Enm4|J*r;oYv-Vl z*7it@$a)l%Jb+N*4QdT2ep+#nhqQy;NDeLO)pu*g9~NKxu=w|v!i{-GE97sC@YrU{ zo=8A?9rn`k?`GX_-6{wF0ExR&q@Rc+98ArZ9do&Za~;)bqqw%YgKUP63zpoo;g6L( z8-0Gk$($*Bu)fo(YBcuP65+@JWhrr&qLqaqV|3I5L%0WRX`{|txN21y$4g%*s#d|g zY4V0NH}h7wTPCyO7NE7y%jR32g#idcJ;HtI9e~h(rn;-hay)#8)7@J3TC7hS$U=Q^ z2m@N_{Z+EITw{4p#q#jEuCphWxVAE##O}qw9KG98KDg$hrPk^6{Cew{+%?!6@b>Er zpOWqNrao7UF*<_cE}s=lE_n0TyF7~=#uSW?8w)X;T6(10M-WNvT5GyiJwf;#XdNqLj)i37W1^CyN-v}@ zwMX5~>Ee~n*h6OSk#yPlOd#@?&#$Hv{IDZYoF!#x2adfqnAO$ za=6+(ImJQlzVKsmHJ8c`BF3_g6XZ*6n)&;5TrsP{15)kxR+Ptb5JFYAcEFCH>y?=O zJAft{RcUgzmnPV{)!npQZRLCK-bV|Rdr+g)>x;`p;RnBLPv-2VX4b523Yxx1!$osSKB zbPa7O_tnGmT%yaeEZ>r!oH9`@yq&ildiQv^397m}9wkaH2}8Llt!O9!*Qjwfwi4Qg zTfMDl@F97M`x$v_bY;0~-t(!bKbnl~@G<*Dx0}c^C+9HQ6sDn}q^Q24nx(Z8q1uv0 z=TaMH)`AP`ROq^r;*MltzZ9{|-cfR|J28UUc?NV8qAthZ8PP#Z`q0v*{M#o35P`bh^+sQa#U#vMh@z zoqm4bYNoM{=aHP3qkvrklUHPaMk9A;;)`9_dxPj%@an2m+Rl5~j zP{-_&)yXb6rs><-uXSMdDIrwan#bimzQ)CFe3cznM#TG#ow&JZGJ)821MlnjtMwbh z4oc>m{2VVgB%1TPo+qVelB(@f>p``T!gKc8l%44+wkibPW z`)IfS0QwxcCzCzHO?CNxYn$2s00QIOMdm{mJky$Omb@PnakOL{yNJHi@!7sN2)353 zi9@JaUv(0tR1yi^y6F$d-P~denA$tTy5_c+D@=BJlO54`_W3UApESPPUJ&rc0n8pW zVHSQw&O<{U@*e}XPF2G@GN!;UH7VESJ{B8lRTT1qs8XU6xZjII#Ikuy&9f34@m`ys zo8Yk)KBc&8oO=p=tMgP>fqw4ytgDc?iu;yfxULC_a!|@K9&X1ithplp0FlO&llvnn z`A#|5y^!B`Vw6FsBc`<1KG~aS>l=x1D-j>Ot-F*qW(Ks5+WfWqYMe8;pN-{ky^kfu z{D{Oq>iOxtD~-5b1jMrCrXy}Ca%|ZQhullghgk|u5|oc|AOLG!RgA^MZap~|err;2 zR&!bUkvtRO{F9Fl;68Y<;h6;Q6s2RoZaz=s8&p(A7(;k!lz%hs=niFUvnF!^NBHT5Nj>UXIrFPZ8r(*NsoHBL5!5haro`K z+I@t!YHBJs{57l>(H}5yqUTXs&IZ>;Yx3#%sHY#t+>Xn4K+|t3YFk?GiF3IrY$&9J zr5`a*;i&m}Sm@1q(O>b)c^NZLVpV15Yx^hhR<*bcz?Q~p|m2Qs!jc*?&1{~ zr2Nj3j8>^aHL7X))P(l>@9C!GtrUo()jTYbqs4ufW`sC~$WcW_HZ|2{<6PiE(_yA; zat9GwE|GZc$Q-|wU95OdA7s+8OAGF{i?Z#B5lL&fcXzG25=c7=>x**yZM1A9+PQjX zgXeFrDApaU3$0&pyvsDT z8U>z*UWc|1iMndMS9^FaWCZkD@x_ae;&O`dm2zB&5HGGc?mw8W&os z4l0QHNF-OLvsu4V@*>T@1-awb(OFzE=AZ-BcEgo9f5dx78`!U$na7uYQSmRDp#H_& z!b4yD6}RO!fJ)cEL{XfS>*Ai0RD5b16ba+xyH~x*_~72r&PCy1xRYYQU3rfB>RTKDg_-G}D7 zA;7>xZ^bQEd%S0)U1Gf2XUDqZ&aEAi5!5yvr{a_Au4T$S%9iTU!YI!5 z(s<9bvDoCBCB)4vADw>n^`+xo=Z1Lqi+{1;XtdeeirUvL+jC7ti#08(ijts{LTOIo zO?Bjq{k{F%iZ?_3!}6Wuvf)pIPdN{JDij4o=Bjm(OjGx%2%^?3-Yo? zFA`mhrCb}FwxgvH!R@LPm3FG1#Os;f@p+(qC_!DH8_giQWkZ1qne+UP8_XnR&9{cL z7-f-A#z~r6*ad+pr6@GE+(1c6&`#w${B_cJwAlzLM}5nkb0~3_4R9<1y~5^A;}PI% zb8X_BlbKt1?+|7jq&dlPZH`>!Gk75>hV1z33tW^XNvfO#&}+5hrOG^wgR0;eSa5K) z-n*y((5_8$UoG(lXN|_T**M+XXDi~*zXt2dHtEkN9?0QGeQ9n}R;NJys@zBl+taD8 zyEh%h-Lw-as3C9;QGJiNr=rcT287qB@lci_&YX*fTrAUUH>I%kfz-@_8D(ZCzS*i& zSp+1WyVAN&TZ>uUG_}nYJ=APAcK011k|&x2)2LllxA~vMoY%&19OaH>$uR!_BzT2~ zu3UG>HVYJ?8tDN6Z@Xu?vjGbWZ9Nk5_uLX_4!LtUO~st$jA(Qx$Fl1=4rOVPfK4Nx z9m`KqU4DNLwNo<1=4F%qrNVg|iJ{%v&LANYCg7`D{k!34FW+g$7*V4xB}9ZB!SvOv zye2tf^rdLpd=_-`iJ-al;3w2?VtV}gu7Y?Y_YUQ3t25+VrzvH)ZwL6>#qFlmnG(VF9(pPQl(c}q z_@0Mb9y`ePUT=&Ngmn10>4U4tFQ@=K;|#*!tx&BqnI zF!VULn60Rz_i)*fLfRA$b7XD4<6VOlt3#`5x~D72pBOx?7_SnXN-r96E>#4oZy;X~(wnwP zVJWL|WOpB_ORs5H!}8Z9$?;sa9l}UWSUUyW@jUwj$Qod{ZpjGhd{tKEAGeLYi8pyz z7DC67uTN2aLwd zO!`^0jlDq%!DD=@;`c1$mJDw$!6&NSW=dWWWQJ8|k$U|OxY4B=O8V_jp0fA+XDbWK zqJ1j0_)b%UzKAyBI_*|_DuKlptYXo4vvO8Fjc|;p55>Q6B z=GMpsgsm8WWwKHeQXD7g!h*e|&K-bIS_Y*Z_hRTRm$NKWZc+jWS0p;F7LQj%6l?(SD{Dk`ZYoyj}u z@+jLA16r3p>5k|1N!Oc~v=e2uwif%NwP;BH0ET;hT7D{0rAG`dGyzyIA9$M{{k7|s zc`<5>#~2^B>%foeWEX8#Tt13_(^!U@`tPds4I%z!!`gdKYgmH-4 Y067Mnd zhDC~ArskJcwnY{V@uk-6v6b47Ag8 z)Hajw9=dhS@IkGri>gT^nw>gSb_Juqr}NMW(7lZv$J|O%p5y88(4uY9Q7Bxb2HR@i z9bZFRy07&s@%d=eRY{*!I`8q(prlxGjvJ4=FcgdJ@dXS6`s2qvRq`s+o6$)zqK#Z| zs;gNlQT<2PeKJK;0T5D%JvQ{wBHB<#O~{BZD^Hztmb!`dxem!CX%*Yy_~}e>B`QJX zQ=VNhE#p>bAa<4+NZY4PZDv2<74TTdBi%BD-6FLouCr%Vp<*bvMJZmrMv_W&FrsUF zxmEPkRPc=l-}2C^Ue&jM%Tg+nwQLPVbZCkh9=ZdfH!bO+Kn)kD{X5XoOBK7Wa>_PXDx$+`KQ(-MjbZW4Gu=8zE}@iov$JeX>bW_RNCVi6k^b`fbw7r+ zVAHC?y~(Ggwbf0ho*isvWzY8#K@lOPPCBlG=@lFN&YO@_(K!r64N)u2!B;8rA5frD zk7z!CYyi~wBMoY+oz-&k*%&R)b-_lO-2BMViq@(;heTHGFg)^=UYjL4eeR=FcpFl! z*NfP!J46hNl`rY+w_~lV`*~OMyGn{z=dR`LJ{&;D-^eE5xp!F$l=0tiJDfBggIxaM?UGYYfpj}K zOmGVIE1&juRN~xyb*hOZ`tSJa#f{NGmBy)fhl(s_TBNqPlf(5;Wh`_Og>1=S!VgkcP78$E_~+0sAITBz$;C&Ckna4G$JYy6vr#=22_+3E0O(gxDX11WImDYtTD+$}#=auhEkP~tTOVwHco?hcg_cRe zDY^oB71AV^SW0gAtpFvD+)MTsJK( z)F-(pa8vZFp+1_KfV_p5A+wJuQF&dh%$9}L#*YI<%bYLnAARGj?&HN?NaAi-FP6tc zQm+h+l1jTml!HK|9-&FI1sli6-5s$FeJaWx{eAZ`fP54*6t$4FZ6K?~J*oc=p64(MDS(i0l_#L{3b zcMLk>sB0Ul*0mKfzgj2ZtE&!5(l)$RR%G~jK4?t}WH*-HaoK5V`E7C}utHu%D3uEJ z2DJ3moVd7do8>yJycxT!e6)}nDJ_Y%yz1Jh@#TARK%$a*rlZqYv1=`Z^%VYI$o; zMNf)OawMqz&%;;lBT}ks`G%&(TOidyk~gr`y%Ji?TY?sapm(3tHU__e(&G<_)g2RW zd~-IA8m2|d8<5hJ>|^xx38u7mx9xDD>av%(GSN_w*?rjVgt~%)4#b1;)YA(bJXJuJ zNZmr77W=x=Kqj^IuCv!xAoN%ExAh6w)RyKlQ?iNKLvEU-dtDKzRuTa#XjFBW@%^s- z5%wL5hdaTlRFHb@>OOkb;5k&5(gRh2<(vi!*vKudS0}ui@R9~d%!#&W0huj=TT&7Q zNfp@oi(F&xND-}r!jc*kl|Mc;@oX(_RvT~pn?*A9kuDK zc3unC?u^xV+IL^!=Jc+CZ^yEw`9=(;R_c9|)a^sIq&^C+iGt-Jz-p`hNr$`rLL1-k z)C;R>uD6BcP198k-Yo9JD?m+0sHaU+&1*%{G1(T$s%Bj$T_m*td5BN-o<6L>L!CgZo2o2}oXAck=&vn@Q@UJ2l$8VWAc|@f zgcUj{g0Ob~04j3nSbJCxAVzCG#v+hlN1Tx3%mL9TaaA)7MTwQzfW$xv>EC=}k_ls;_fVwj31|6#Vp{n;|+IwB1)}a`=mMhb+-uOA>DB zX~jlq^^WAWrvU8=P}~A*sf}{2k?q~Lx(S7QfOhZk8d!A{Dck~Hu|$z|y2rEI*)yj} zG(<+oDvsLCZOoe5IV0voM|=H z0yIN{KIEAyd$4*wY0aZ?!w9?s^R^MvxRL7ENouoQ$TN)G`XH2dA!--iZzY zD>N!P{J_+R>Z{&Tf)(`qK3a4T|Y zQrh7~4pBkySAT^#f5$j(-=+CQ&9ZShaqP@oaIQVXq5lAUyid5e^4nY~?iT%+PBapg zDGOCdRYBN|XYVhs9N^d+uX?FAme|(DUvlyXZNJRW(K4>`Y*|Z_b2#zs7VHO$r$c3| z4pi^iUoY{bKhYt8qNCcM>nR3^uG+GZlexP|r8WvguMM*W=QpK2kw%IlQX}7*u?sr3` zzlCTQN_>;a`Fekdo;JOB;9Psg%XbXq?qV1O8wOO|yyj+56y#|V<+Ttc=Dp_mb~2PPQCsL&#|jw4e%T zl9Y{)!(4;f+uNy`ELQ|jbPKrqw-vpe^cNPu(26+x2?T7z9vOv)9hh5Ln5(;=__ z00Hu-{{Z~(5{IeKIGwj6R(7`p0IKfbt!kkTVY3BDw&c;rYj*^M$tfTDgQaQsYF*=Z zwxLntr`qDermh#eHJV`e+#=xM?~vNk!i_c{R1gnMbuJ+GWU-I5lTy*5;av9F_?*ml zvFIzW-l+~{VwGaOPPRyFw(KWXg#)qJ(!ZX#4gHaVQq$P4R^YNNxVLP8)Hp3MSuvZC z{Aq;#-b+hK1N~InuA-K!;Pl*@!D2$w)3oe#%K=2!YNb0`k&|K5Ee@}`WvF}beMwLA z0j4I=%K8@vMxo@@3sO`mO4L)PoC2z*n(CxJKfEH}d2NY`CfjmJjd5?#w&&Ad%TaEc zK@JMS&Go2PbH&@^Q0{rJ24u1Vtyg5N%OALYlH0ZYHT;gNGL1S&7T1c*932Dus!Pk* zG~PVq9D^E^(QOdlTB8y_Q%XI+kK?P0IsHh%T<qB~Eg^Zx*5l;+hi1rkGv?NR(rnR95S zg1v8{(f~$>PfZv}a&}{qJ&CNYv=tQ^mk6Pvk1)|YbA*23wk4&8s9+#ahHzbo}1|;B#?o1w6D1K(x2m97$^dtxL+yqcNn7JjyL3M z-UQ9;v~RKP7a1@SJJ~gCJ{VF%oxYXnrXB(6Dx463)h}JVjOUB;k}rI_kzYGVQ(|S> zWRQ{o1GGG~0ZMED8gSdf2V)=l=7Z@=6lbNmWv(lmvS4ddDo|qyGz$u zW-sj-Yh)IJ!c+q4`C{P)L3t zNat)lfQ36_oj?qwt|xRw#@y#`@=c0C zQ%^Qe)P*OZsQwzWWh%3Bv#RZX+gt9#nfO(cM6=GFFD~)!_bm?OIDc~7xMPp0K&n-= z8uui1Qya4E_Nho*e&m!t+GtT!kVd$y7BSe}7Trcme+8}Nth&9j-(e_W`BZ)t zAH_O2LxsNQHY=`lzaYG@!qtSyEq`fgyhXc&=AP^caVS%cwgW`;3aRV9r{(jQaL^)Q zY457T@%}r=w;!pMt_P=={z`Y?Tm#H^j!(~W{&2c+MEJ{3Xdi?_afiA{{Zd8f>av;NdlWzwAZ%1w`KXo zHP?o+AK<)E&L_vaH2(miAK(BFyjG|9ehZs6`K>NPXp#wS6be#JYy5Svp`y>+Tb7E2 z(nk4D!hCPVybcCEZU2&o%t?pIsg6C1UsRY`GedtezPCYz`_>-Ikd z+%L4(+y@l#`r{;XW?^#1xbGyxuM!cRWfw^|>6h%CmyzrtE=pXP30dw?Z*-&$kEJp zMuj2d;y99II_jkRQP>Twh^cX;8YyHN6%$=z^SmD7*5XIVz!}l2cgH#VG8Qn19OC}3 z(?EKWUx!5Xh4-A`jt%BaYY4)#i|;RR^_oT2H#Tx4w_aN+l!gA?mu<*-9^(C%6IT$C z+C@ToQ%qWWFME4r>2%%LB;-GEnEM=?ZD{SC98ZtS`YTm``?GKd8**=qoIhpeGstoL zm=_s&UnRznk!i6B)fU*UAO3f7*A$jh+CNd~3D$4_0I~b=Waq!i_MVjLw}1VzV}?PI zMBw&3b*Rwx(zP}CFO@&K?rF-|ADO(dapb=q-{2eERKed$uAHE@dx7o)!b4Lz?u9eXA?@x&Q*DizVS8D50 z2%^r!{xsI;xRSAZ0MQvz(x9;LBq$K7^r`$bI`u>=*>ut@D8dGlE+hxS0xTON3l@m%(+wtsm-0m_smYV86qKU~fc2=X)XlvU+ zO##&PsqLqIYPhkI_rCLD-B-b=G3MUvJoTQYv?n)Fp zYpA!lmRz=-6|O4!OClW2S6}guyiLQB`?2pQIBwC(wyVS`OyxH?4>@O1xHiYP25dGO zCAQSb0Mfs!>IS1+-tXIuwJ|$htEAd{oafQAw@-)L`L470ljUYNe#rb*;y)Wa__td04DOTVT#?yw&xBzaHH8REIwl-EGgwJ2nrw+g(Q-ru4|RVT*10Yqac%|7gWOL zZ7xGu1fjqUYxdBlMn`7ijPmi%oKMRa4ZSLBvKtJL+WXu|`jS;ZXhy6rdClEw;a$q72kqsXsoQVzTpj0F zIJS9vxJt3F_A=v@cCxYpy8-%Y4yzk1j`JOAwencR&nWDv^xS&&Y;{e#+R~JhMb!3@ zsqJv0t1!N_Nu^g5*ZQhR^%XjB5SuJ;S{XMWYF{J|nbFiCsu{PZFK>N7ema9GPs3WJ z`$KO@nH8S(JmQoB8y=+kQ&snJwXu>`p9yODzqDIHu>iB7M_cmzC&_H z6laon>m~8ak0EePyNzwmL%%u}E)p;E9#Rw}zNbEg`L7$RFfiz&VPRh;kW?RHwSNQ#%_&o~k5v!G*NXU4zG7cr^5%01Z zSMFmWMv3C5kp$~=}vW1QPC%Q)w_nM-@;7{M*CMS1Qgwb(3$t66hKy!0wJ=ieDe zj2=5>ZbG!!xmP7>-n+=o;>4}m-xah3i1Ad>QjX%AbsFsabDSK7=ycvtI~C{tbM31z zc!LeC9lN3K6$Zz0{$t@j6T&Z9rd28iC*&obZ2K=1sxyuR#ChP$i;~;mEtT#F3iRJy zUd}r_gr)2qG+a*pOqLSdK{JUDsULU8?@(-BN6vE$cwjh?ZHxL)7_sFtq?RSF+EiUJ znsMVogb}hn9W{LqAGV3afHhb-c~#BCLN}8@e05ioJ@WqmGmYSNn~m<$VevLucDf5f zQ)s#>MgC=~0Sa-~Qqc=xr<9K1Yw;D#h}X31H+L*xsF9cYzd!0z{$h=2ArsZACX*+2;{Q3iv&l0G`lWjLf4 z9$t;DbXv?lers0xt!VW&(_Lxyr;3}nf5^GgrAw23%Ac|uLX7t8l0D_dl;f^_eWf%B zLTma|dUVw$SA$#07z_fTNWt^l%V|rV^F!0YUyd^J?$wxPST_$z#~tK6k361;mRvT* zw1#FbMNhWDc}YTC9i-Jt>Fcg%n&47e12uMlyGI)LLAf`!+MAqy-Bn=nD(jZjB>0p?fZmAx|ZcjVgv4e<(AZ_t8uph6t8JN1HE<7IKDm3V`Plf70vmcR7G&> zd71;8uT@3A_Hm!`i`=!CVix8y8)gL%-!oLkdNT`!ZFSWkxZHOZmAC;(DoOz$dUP7v z&691RYh`sRxz0BMHS1j@4X)it`+p@|t}IGn@XW}sK$0m)(iwI$pe%O^q2#4Pqj6EI z2<{`4RfG_wKF(Wr7D&<@I)az*T)!&gET$f1hh5F0`vr+n+mKL)0^`nQQQvJza497# zLWngVhWZy4TeU!4lG4$od_D`-atnpA=x^igs?{nO?`7zPe)1LDxleB5gH6qB3qb6u zt?iEe#m2uCBFN^Y=G<5R0J6S7adR!W+_x?ml%^SFxh_bQ3M8Uyx^QLYWoJ+PR$*B0JlO?Iy5?a#aLF|D?}8yW>S*T2PelaV+oUSZ>WzE8{f z9&zu^%%eB`;`Kd~IUxV@+mB?02)B$zc!fmJE0_K zy%C$@qbX_&CEVVi16r;-c^l4LIfC<*_Z8(FD*4R(9dx$Z;$yb!s*wvX#N@>bWm4fj zub{NzLQk}y0i{iPYtj6B4|8>7$)SA@!=YYAMv2de)018{MBLcv&n0( zi5%qScZFp#6~;Lnl(9C)CE%vyTP3^@9gx`iOU$-Uaiw-N)b4x3LeleLe-S%0vNd3fUvR{0<^k-BL za7yY=VHE(8wxWA^6xW#FLg0DXr^Q(NyLk>XGTXZ&Sfmv`75@N~)yF)6dw$UY(3hp(g<9WZU(nit2X1SLbb~&|$^=rHp6uo8{em5~USx9PnqY|Fb z;r5bEK%!I7itC|UW9=o-fmOtE8B6z+jP3Egg){8?AMmJ1T?s+ zDQHPi-6K@lu0I@Nu?o3ean4&Tr)|MIS4tro3*QwqcE8F zEA*)JL&q~;?(Aya#x;~g42a2J}duMkv z(8Z5nm9I1|@cFJ+=lrvbcpHq~@Ln{$N6I{|$}R4|@8baT`!59eDBPcUpV`;>GUgCi8Zcb-PJa>xIG9 zIVIN#Tdq2_cPYmTi6p4+zNd&80|QzV;^)sHbBZk`$tEk1Z7B+oBvEfG=|f+E`019O zw;cPz;lvSP(Z?oxrFs)!#*)@wDM+BPuixqBFmx>%`GtspXOVB%SxKuGL&~0V}01lc7^N z=Prw2g5N+iR0?GKt?GP>t=@9(Hwmj#lBK$?<+!yPlB2GPBqBNi(Q&8AywvqVkEV!7 z`u!=VBV|-~Jx+XQhuW>Sw|QGXiSOc;r@fqWA1erQ^Kwdif9g-q&QFlta@*YNwblOsdQ7F2DL_#vPx7Gv^h!p+4OHCR?sC4dw-GQkJ?zXz z!fejo=dx9iT@n8Pz*y#hw#n-1f#^^2=;CN0IU%7@v$CF;^RgdVan}heQQVSr;1oz` zqSHYd@eG9;Qm54D(5hHkk!pq9C?&oqkbMO!T^fv#8%ma?Is4Z=spx>I_hG6tlJ<=y+l_och^g`c4VqEa>k@)|e3sxt*rPj}NzH@ifd zAYAm)QJ{{Skr-KBCwV~|w;(QK4Utf#q#xs@+q9WNIo;$XNV$aG^fes{?hK_NC*%gS zx3bZo1w~}jypDVQ-ij!YYmZrwgQ*uxu@(_aip2BF)a=oEikecmNpw`r;!B`LhsNYIs0!TDl z007&joNH*F+!v$~*GfnZs_5_AZ-!xKT(dutGC}R(Tu~9*o!(M_4g07`Qc_B`tp#;M zo4FyIM|HL0wgJvEJD^b?kzBPWn7MN^&Lq57Z;>HrvsFa)Lyk*r3Ll6S{Pdi4yJKg~ zN6uZDW!JK((&ny2g{Ym0G}Wr3B?1&{w&h-$2SA!*NiSSW@i$KTCDP~ z_jp#gjr)>jx`x_F+>)hJ)A7^z>o?upHYW8YR%S9W*;=|+ngA-U=Qd7<#{7AUaW$O# zQqaO*zR>QcS3^ThdKZ~q$BoBFV;+PJSCaj+wUafNn=3Z0tr_wPn;{BYh(I+eAbw|D zW;mQp0`z;F0IKOn4chQapB34Eb81{G#xEsrUao7RkurDVHpHt z&hDji-WSEEyW}%nO8UgwZEu3CeEFC;mF3^EY1&1%Q3z>2r70ei`D=~ZiZoZc?Dw(? zkUA*R13uGq0)URXw`!>BS5tn`UN0XbJhI0xxdbyOj`%yXBg&joYRj`*Q5N{dqJ))O-bYr~7qbZv$&pn!l0l9|aR-c~G~rDRNv$WdM~rHzRL> z*2@iGW{^-WY02|~+9wws0_P7PWo+GVPs?X!)}jFGUrO)s*LcTU2;mHJ@g`b-uDf)j zp;qt4FSsu#a0o?~RHri>H@O|w36CjiX$1XV%ZsTSbV=J>yWB5jG1-=HXbh9!7odHi z{mXJbB_L+wJ-_{~I)U&f_#ef6192yloH@_9{0Y|Ul5NtaHkB&bAcmc7(kt4P9<)6* z;r>nS#!m}54g|G*VfNqOtOq5u5;5VD5vV$IDj_m>rG;P z5che73hj|!J|GA?npI-*zs24=^7j%??p{VC$~K19oNqX3uohL-G36yRE;LW(lfJs= zw!Ynj_h8AALQH#&bJT=J_rF(1pUgW&6pW1bG$gdhYCtC$OsfN4@(+&93SN zw6D^jN5gUJuUKAU@I1EHdnhEL`K=Cbn|o!D^yDsdApA%ncGtzfIj~N0jRwJcklrty z8X;E4oTD;VPOU9esXq;QC!c#sd5Vy^gL6I$==ZVz0J-c}C5T5Oy*a!Ob$4#odrkF) z$GOEr^#mnU`h|W6T+Y(+NDo!%iL9;en@0dbhv#vW%Vf1o#pnLr#;8*jRr-fo0yTcz z@RV(oEN#~pbT8d*Xj7&o&HI4<)sjK%lmH3}NbFeYu{wbG10B91tYW!*jP&PiQx_rP zxb95;`H4$5d*dW2OeB=H?Kh`gwP$6>oPWnQp-^QwsEqoy#*R*WSCXbdbruZr8G$vh z65PJhTX2tF?wV^U$0L=#qzNpCf@LIrO<#k3Iu$$jG8GJ zXyHY=Y_Z5UGf<02wZplUEu;G>qjBgIN62Z|?%v+Ig$_$LJXB~>Bcfad`&Sl7NAkP} z(s$L=Ws-`t>Z+b3i1~#=BQDhr;L&xkl(?>xrldu1_|B<`ZQ_}_4O0$e8J4FHdP3tWzMzWNQ|&TxKt`&UD}|2bq!6knG8{nmz1Eh^?FHH|F95SZOuu`l>=mX~2YCfo1bmM0+Wmw`nI;{za&3h}Lds_b$%LFcb-`M{mni^F>v(g6t&W z8j3$HLNQTJZ0XIEW;YAYEr#Mw+aSS(gqel~+B+SSrWvk#gi3b5SVu&BwMia0MDp&n zSHmv(q6~Ls6|O>%XtY$fvOwvIha%Qt7}(VdJf-E@zdg==?yhCEk!LoIrjdDEVbON z$^KNPr}%1P;sG&g_ti5d!&^nftb1;xkfx17g+3tkI%I&ZA(bII(#i^m?N3V6*HGTt z6=NIqP1n$(zO?+cUiMX7M*S33u2~risv)$Jq%HLoB?u6oj)IgBdwlc|TLVG}^wZ#= zw+>Lk$`Le<(o&qrMo5)Dzt$WGly$4^G;rX+5N<7yjkUx3w{9aRy{ORU_kh=S#< z7bn`aNN_y;s5-G7$51;`wICkSpf&`G>FB{Ks(6@9X{vW&e(1bL{j-r{$OpZrrH~j( zoBIBut*N(9T}qBR>EfubqNrUp{}5#d#M>bnYd`uoh$PnaG>~VparAq z>#GOHMOMOq2E8_?T_mSM_een;v|6q=%dM)%YK*DZYvj1ltuRsA7W}!7qBAWdtfe5W z#e|BGH{8=kjf^ie34bfZa%>MLzhPOOk^FNl#iXttM~}%Z7vaiooy{z=w-8h+57MGO z2S@;g8fI-hbn)>?9p6Ya{Iw~LQ0c1JAE`e*8(maXMh5%z`07MuGEugX-P&tSN^5AN z$m%7gnji`dC>2{H(Wab0a3H96lR%E7pM^d;Qy$*xJd8A|zkV_$EIl4VE8;sW8k;!@*Gx}>RP3r})XJ4x7$^*SqxbqXWy zSVaL=#(#|1_5hqWCCt2?z+SXoqeYz}4k>!~|l)`6z0IW({x?`a^4>DHuu>!d$!4-WY= zo4k^G(}_)sj9E5%?UGD{hkxl;q9bkRM<&ap|zA*m)JBMYq_KSsH zW-?JY#zax-NgYjV{FmDgD{xO1c`e8JR~6^k1)CwtF`RLmE$1O=rmc~Cl1F+ zeaZOF++ptyLqSBg>nhw7sU&)x_Sda0i*8^OH~WQGk$8;mv~#UoYUs||_|w1N)m6w+ zXi!1gy>*dvWI1&PJwL7QrN-z@Na?7Pv;}`@B?5or>u%?1x>z5nbFjZ|uKxhkM^HZ< zHDcf$LZ7;2TU6AU4ZHq3cCtQ``<4M*FnWccbOTyOkXuEIorRKa3NPaPf!s?jil>Qf zgs-+jKNC$$<5@4DzH7T4s)L_l#zP$7KUZY<@>dm4?;vJS848DfZ~CqQK$id=eq?F? z0CRC3jli9w$snqQ?2dfPZ0l(co+D4WR3;oOJP0(Q+g!YAy$Ezlmp0P#Ey_my->6gk z^z4KqLgJTo+Ak5Lf9VBL;pkAUh}C?o+^5*jwc7MXb9+iHmzP{*f67e(=*k^CRx6}L z?%oN@=1|wgG^4ywu|q-+QKY<8H4~^*`o=*fJS;nst&8ru;%~PS?ovovoBl-_dJ(pv z25w<#SCHJ8r4lr&Ddl!A3z2x&59dsFF?AudN5~Y7$nK}sufUB!#!6#(VOkzrWG=0n zaNeqkVfan=F?v9^GM25-Xseys<$H11N%*MsKL9m(Bk9p#Eb)e3x&kwk4WmeAuJKWQ;_G$4O>%wGV&uqKJRclP1q24fSAr%}{?y zQM86v9p$vK+E8gtR5`SAoYw*nI#M<6f(yzIhJqDY+fpgDbahG&q7g0bH_?`<3_7PB zlKVc#Blj>I2ykseDv}Sx9TwFo&fF1WaD8*#n&RlU)>i680(bam<8F!x335tXogFT> zLP*<3xOGey8m6+)q${+JopiB8`qxB_7;B-@N=ZWKB%mEm{lkBbj3ok#1ku4=!QPrN zp-X6~Q`wR|{HQwd@x#HuB`hC9U))lu?wzC$}%N4`RnrK$y z@*8`rNW&fjhhkDPcrTEnx$PmVbmLkFqTQf8+5Z5_d(*ehnzcFT$54L-O3o`Y&V|2< zwVpq4AD*0Zm0Ppk8{&DH5+)&WZkBJ}*<{=AHs=p(@ntf(v{P;-qj6OAA3TR4ml=G` zvYH;pxGSY`EPitm?e^q7r>klxM}DL!C&_ORvfe0<8@#;%X8$J9MJse8JI6=vRlf~C zmYxcW&$6YpcXrjK*V=Q@LY;ptb-QpR6$N>^=H1aawc>>}{{XkY8D;ouD0v&zH5E1K zt2mrSik5hESM$K1xVI^IOURMsTQ2h7!7egdb1TFW^M#P$SR!O}R0O(;ZWB)L$5pwk zGr*_X*QYDb|9 z>rT4hd7d6%l!pb_?Xr!YJ@Fd+7fA9xKek)Y(rof*+?4FKFTc4e8!0LqQl7fbNs25# z1z{EaTjop{mb^b1wq(#U9NmiqTf<6j>F&#E3vDkpTPr>0gJbG6n@FYg+U5$7P`g!YPZ}X+$7)<5fUqpDU{2xIG9nHpdGHbeb7h+MxvbOCCQ<) zvV4vI0LVem1N>J-!f=bb+r~vD3?PCDBENQ^{sBAw<{lsT_xAGV4l%OCnJQfTvMR3E zUIR&T;TkL$&!wQYE)N2b6txggwH>-@Z?*i=>n9O24O`J?{pa@1+ZAxfKzy#$*W>xB z)8Za$=6(MF$+Ys9h}hHzxt}+bt2Qq@{>;5=woG-Vl+3u}v?+u%(_ZSr57L@}Q>`C5 z%-9Pe+rU8WK&w5i!rx1KOmn>Bs3%Q()BY1f?mv3xIbL4!tWI~q@k|eytV)4z>~eJsP-I2*+`X-!D` zQ{ngqeY1c$8vg*rIFCQ(oX?7Jt`mN_n+F%jxZheu^7)cLa1=qe+u{X0+lW``b#&Zp z38B=R&Rb|EF-xEw&qWIpz^aJ`_>&80p&}FJbinp{+^r z?^E&Drm2r1m*7fr6sYTqDU~TN$8`#A#4RR-DJuY^kAWJ!WeCwzZ6sA}-0%{#R3#&2 z6Th#|QMWX8C>Re^nQ^)|9W6Kt!>oRpO(kSBRC{UMosd;ZQnfm6OoSB;Bvo7E@w2_9 z%?E04HruE~lr+PPDWw(3XR$~vl~DIMC<<#|T`iNt(Wk1kb-aPzVW`mo9JK7Dvo25U zorL}aDNk@)?iXwVv zy!2&$)GfH+_An3X2hg8dYS2hSMO7BjQU^q+)Al=q55-w}*6XdPq@>j=SpGmp>8_FM zl(^z=Wc0)3TQR@dNx!u^JMtZE%W9ZxsJ$I3r*lpf0EGn(w5asdJBP(1X;7xy+Q`Z~ zP`XpW9$fRaiutMyn_*uHbzA3x!BJoOpN4s_bYUlO589>qEyCSC9trnf>Z0NoP~j!Iylj2c%(00pG0>-BsX?_V09SpE+V*Zf(a*AttgA=?e3{K< zjkZcx%SW-!C#qv%oY}zqXTf=NodDRV64=1m7=!};bq2m>R5rRPv|(0>}K6US~?ed{+U@a_%G%DdTln$sb> z&m3jYlW@jtY&G8co{AetTYZ5=0*7%`5fY@Z;|;9^N<;m%ZC(9#*FN`}HXJL1+g8)6>>sxY z-5(%eiS%`PclczcL38WOehL@&c;;A0+3LU zt*P7Tt%tT4>u9AdAXKhT`-Qr@n$@x}pD%*wmN(^pHsaXl1aUtYTA}Y$M92@ zYvq>_a`nrC>^@iGGA-9U22@LKealc70t~cT5`Eas#y~=lhdX;}2hV*}#m4V!nB!>e z+m+=0;Nx;rM;=9+Ok$lhs(VNA2gL6oZd|{UV)>lhHa=n;f5nVSbd*Gm48uZMeNsgd z=WC8{+M*~3It=FdJ2q^Um8VjjliNHVMpsD`n@hS5lu159_y>5#JZZtW%ZRv(5#>%= z;|R{=e9z4mE5jEn__w?XR0o}QfiaM!)ue@2=`|J895bFiJd32cBsKj4ozH!<6XF>K zkZE*Ux!v@y^OJ0H z1v@E{#lF+gia$6s-SGQq`KDc-?U``(@JK z+r29M^=#=aZWv3B;NGhe;dx;wYe8e*(DXF;6)|(4jQrDKgN5aJjsEe-Hax1!9`9_u z+}xQa;~ETwSF&63fl`zexFtkTjrEe_{MQ{Tj8U3xG+XXp?e1>gmoftF!ldeGsp??8 zie%94b`rOog5z=+cez0-Q)^4e?kFHDwP^;1pwMlt6yxOhM`@OLh$cREc%BrPm71mzUhBDes z(73dCc3mdp+?j7H)ffzi)*UVSAC|JdP*B*tFvLCNa#`IFk{YTvb2&ou2;y zAS$t3ZKb3tDec*5Pt*AI(-t=QcHEy>heg}qq-1Zfmo_aBHPOyG*P;0RsJzB_?Tm8) z&btKekU;C+_H>O$Bx`lCo zwr#hlB@VA{{{YAND~G|KY-v7GaMvpG?-}!MD~DfjB|7vDD!$wzu_q$`0F@I|i*x1l z6eRZ+(50bOP$1K;u^GN}`wOufqb^}RM`h7C$F<`nk2AJ*i5fpi`Dw3*+`0Re;CYt~ z-0VNzj9Am8vLvw{BuyZCARt9mBx_P0hn1?dFx&A_{^!~C)kQR zTlRn@Nm!*i6Rsne#^m^*-)$!AaDPed_V_O6$3`EV);svN-fjJ3PM;Cm;D0n`^Y#<+ z$*j*A#$UczvK&U`W}SI@=G3ok`+S>tK~yKBfTeqm`++GS&~H!(*18{gtfaRNEu+s> zPY3OjCi{~cWND3~LtTDU{(q9H8M{$&9u>EEd71dEUgkMdB;4>WcC@7;;@7t+Yg9{{ zMIFi>*isa0K%UdKq~K#LyZYuo{7&jVYL}L_xUq$MKhGYa-z`qRk@@NH=DUBquw0*Q zXi^r+l-!tvkSdi>gHM6iY5}icJ(rnXxsM>|K~hDdKK;9GF%on3sqvCh$xF^vL3mDHRW)4Ac#->iN})7BW+Hx# z-$!?$+;!FmF>CZMC4+NMW!3on$2D#}L$U4t1uA|cRCFh_)j%SE`s)vQoP-ZioZ;|sv>3$Kj+7hk2fiN|(CeEXcF`-q#6$f<3?YhlCx04*N1 zNO2%3LvVd{#$d%<x~TdIC_#Y#~HMPMkJ zl_;vDsGaq?1(33UDvvy`+~ZoLTNSnYgQ=A@#j^k`2{Q^!3Xk#w@j9xO;49l}Hy*te ziQ`Tny>ezl0@-A>s$yPJTen-@@+xu1)l1WMr>QK~9HOJjE{dv3eQa!`xt%<)! zY5GQlu4_oFxemy5_qg#YT8Xby^VamVFo52Rh8Y~~PgRIONTNpRsVCqDKbu~ zB5HT*pqr}A5(~-=CC#c{^1RV{X<>=Wjxkv!^(Lrn6TeFI_0^U>D!!@>`{>#)ZxcQD zS4C6~zm}xkoyLi~R!}B;i>i$_rlB}ct12%NmmCUGQc6+!j^JofB#;)GN>bNMB`;8t zpgTr|5G6RH$Xp9c%`~Eir;sG6HQluC2jN{wr~x)3FEt&JSkhQ2c~7Vzwo;|Mz0DAi zNjq2JqjarnYEt91e2)RDz;lO-9!2=v_70wP;v2UYtCKA)Uod9wDUPq-+TImQfW>v- z+Q1;GrIf88ilNF15ZeY3Jf zr?nCpwXK8(4z&xN)wMlA4ka9vv(P05zPf4dsiJZw?G-D0mguXc+4sStNhK{K z;Dqc3+LaSF1p@L=;()g`xp6~b)8V3RNl{&(THGWRE3|D-O;{2G6me4Ot54$YBM~<{ z$0lBo6J*09t=876cCJrv(mtC4T61+O~t+4MIhBDq5O3qu!T_9hjc~Xj?|=fERYlk3LctrJA%j6 zH+hZ|GKzzF8$R`ox~pAO+&O@)x4@uY{VVa;f8cT3&ljlPp?GuNH(uLbi+5Bp%7a{P zst&1ZQTf+h4d%TUIkT`EXcAxdnvhD(2dDy8CXZ)_#?Es1ITmr8i+?sJ{tjbl;Y_OrZM&;`YpR8cw; zb)gj%?x)XOGm-w@?C%_38QOaW=D=Z5_0;Ld4%Ht2Z( z!!i~9F=EdlggmN^lBM>KU2v{R?A}ut-PGRCg7$|3{nGK={Jw~Pr8k4Ls<3&6HGuNX zq252`s|y$FEyPw7wmGF z5B#o7^RIwBi@~!c=X_H0xyAtx`tc=1QU2*{2|d6%+BaH2{&efo99!L+T$Emg?E}u5 zuLt|1`&!B1u0PmeiF9q&fa(>%@e|DN`l2LGKIGYfyZjWPt9|*;rA~pjW4+(a>0NSP za(%G$Klv8=wCkeuKkfJT6fW2-awBZb(xr6n3(ZVI9c5w8;0td9P^6zwb;0(Q8aPme z?Pw=#zGj+&jlTvoz!{ptR8#)|3E}#I^xSD;cQoix7+CJ17J3U!1R+GFNZq%=^{1|f z)DDFctvnJv(;v3qV~AJS$y!za019O{2hfA*rQ75sfz@HrLAk}nJ9fNknZg!N$ArHL zYsfMBpK-c(Jj!7l#E9n3qJq!gGmu?vHt*Z6d3HAs=}*893YZ6b(}kV2VIqJin_ zsng@17n(gnsI<&SbL5jknzvgMk%@XSB`GyVtZj0E!h|R{_=3|`>XYKlahC!Y^r&?m zG@I*2yi_XqFW(-8Jz!muDO1;3yPR;qrAWadj5Cfg zvE{KPGbST)5coaqsVO7;wY=k8+T$M}+TyvFHTKreD+7d#2CI&qPWUkndz0>X&9nCM z6bbea4HZFM{{S6(KikiGW1XTqgVlVx_p|oVb&r|X76O#**>OXYxL)guTwjB9wXKN^ z5=rees!c&j9}0SF(QvvOsKcW*1$l2B$Sy6VF-{8}G@<)+ReP8%*(W021s4MVucHk~zHpNjIYL7}ZaUjYLNxeAqWL+9^p|?2;XpZO)a77w$}M3zeLl!H6mlKOMS4}aYU&o zPiiaj165D9D@A8=*60&)=*U}Y?jK*E)Mv7a;wytxn&V1O(4WgqO&wIm$yKy#gu7$0 z3m!q2m0c22uiG@@ z{{Z5<+@NSx@CQ{ccn_ghP_f0khd`Q^)^WF)zJJs&PHFVSTJez)ESA+gCd@I%yT6)H0G zuBS#oOiLQg^opi-BWYDM>yC*9s6@DZclqf}by5-LqqtgI=t9=ZbPhNYqLh=+5;hgk z>Qe=79O_m=P@0u1pa6VH)q@6#B*7YkA(^d-5ne=wNupkDL1-iz9n(>%utQyHpcpKC66LEYEt5)${0%fCO}GM-<0)!V zQaY&6e97C_Q=@fGiUq?!6-V#(d(Jy?U8?^8YmsHSHk3HHQkij=dmJ6eS8ISC)eXVu zeKhmqAf_#x$G3A$LQB$8mS`Z1$M0!Lb+?O4Nd>nOqLn0#!30o(JqXi{X(~B!syXgI zDdkC(CCPGMJI#+DGnqWl+`)e0 zZ^yVxAL4~#?7qy2RxjNbuI18&CE2QwqE_R5q$NZBM!Jt_31KzXss=zg)HSUp{TF6gm>5K2}lHO(?PgHn(Bja3-O~aQV&fqZZZpeHBDHAuI{2aWM(P5+Po|`LkP0@SQB>Rs7z^0i z*S~MYJVaQ5$nm7yRLjn|5V=n+N^K470*O2JjZI{%9Tt7E$swo$irHX(3v$mO@ulON zF?_cNw{g}!W&ZB%cFCgKrb%2a-9&h5LY{G73V;>pbxCoSY;y>}sVhwu9nY|I?{E$9 zuDX8(6K9R%nDx8%cun!^ybHT&Gg^8$U^eJNdy?7Ql$BHi>NTL2zJYNI2G+wZz}iUz zx%+lqiSX0KU$*9FidpR()6bWjubFOE#;07cJ3KezS?p9zUH9b%!6`*RQBtY!16&W3 zXZZ=6)3MvfYyh)rAsr|LC3>pzJ0Ui$iRHRI?y(5)9K{mo+Wu{tbdd#UB98dY&m zD*J$De2f18wkcV5XL)mwGW&~Z#v8Imc-z>srNP|NkkLpg9Rid-dh7OFI~!>5)_~v_ zoU&Z&H;}h7dX8h9Yt#jwaE}(D@~4N~Kjr=xX0_TUSnbj6oUdeyc|_}?l;Xoo%asjf zPOz%}q0u9*sBrggWyF@UHh>egb*jQGV84aii@*;Z!SVcbKk&Y6ooUEZR~gV!Qg#PS zUfb<;7f#3AiDLvOb*e=QMQf@_;HtfXYA2nla;>)r@{wQZs3?9Oqg8KZ9aW);+k#ZT zG;H_Q=deJFa!OxCI(=WLYWB){g-3gDs)e(=YOPrSpBW)B?lgWX{RhNt@YB*(yOmTF zEiFA2jkLDcDe)c3$fbqdY%LdI)KyhLtpz%hO?3jnnZcO<0KPD|JEb@dOxX^RN@!O; zZm`^iE#xeA`${1~od`77o{UALgV}o$)^^Ci;oTXxIvz`rRB9a?emZ5uf}U%;BzMwS zc}X=TwNvEqOiUCgSTNS@w zWIHO|45l0;mCBgEp`@iErmI~C<))n_dMVbnE;^LTytdM&JNtEoqt!#9BTfKms*fXf zl#bacAOY*8`kw^Lx+9AfsQC>O`lPV{g{cY(NueDMiNb|G04ea0)MLb>9Yk4W5Osz(uoVC=z(2Z@RkP~$)H5Q3zRG#5lQks8YPPVIH zNa>^{E%v68o=0FzRCV?!{{TsSen&ur!3gBW0{;N0f)!osqGBqfvDTVeG;>KvS)hX5 zDX;1j(t3npZdva~N>iut(vYeo^9`sFq$Ok25DkBhoMA)=swM4+;*ruCKgv_|)A(ox z0%%iZ$EvxW7Cc|hvpHEE?&PZmF^t`3#xF`}+3 zsC#*KTb>?6qAOxyne3R=JEen_@QGII_T zE~RbC?cM3NRj7id>$;dDxO#l~{e}J36~Y_0o)Z51R^p|MSI1N)V* z`jHbOaTTR&{{RcncK-mho5?L5Bjsye1wbSC>ZwPH-dyvif*h***z4?G?NQ%cPDA4P zl2q-I_{vXWfN=pur&CVmpFpTIs3Ts7Cf_M&*9|H@_2qkrF0hxbjo;dJr$zSz;+Ng( zH#~4;d#@JcQ*&%j2}BpU6Nb-c{?PEqZMX}|NJ5mU_VN;>g|rk?O^Sz3OPs(<0WInZ z-nQy1$U~#h_Y1ZR@kbE(Yr%P5MaMkC)H_#@IO~xjRa!wl1>VV{^15bss9Ph@ZKi*<+tR-`Iqnb8{=U^G^;* zxVIYplR%8^x=n#o91O)W*pmCD#Ju8yJKN}@Dky3_we6?7cG9Uu7mLW`US&~33&}$5Adh>%^ZBw}E6Q#;bBHR>T+<7g<1@G!O(`Y3jN&xnab53<+4V#Q{ zlhmKVb*>3@GiHgC$jJI_8V3+goxA*dY8Trtl0Rv_3-gZ{!`tWVvgHpd!!1wR@Z5&Z zACcO>`MD1VQz2dH1cH#CmrZa^56v$z^Bt9;I*KnqdxP!XKP?MgH|uE7*Wvc- zqWFdJ=F#O(5_yj&3GuaG^tY2dbCTXL>}{5(TVv0;E)~JZFQBgK;v@D*L~i;hfkG*` z8uf;9Yb(4^n(ei2(eqv(z*yvQdEZALAurr@=ssHbYhTGoKXtAR@@s)SiEBIZ!wH4P z*-w(OT)1LCwYnnei>vnd@#4%=&LwUo5&=?4Z7U*_Dce;Z-tsJbw#Fztfw};GHChjI zxQ0$U&#IsNh5ly|*a{EBr^8juyiW0S$8vmIC5I9<%6=huw%+YAXEFPz$hg|2w=IWM z%E$-mX(03@ZCz}yu+d&XVsO#lYK4x+X5`1r*`jf4o2aLMf$<(8eH?Kw-0zF|=L+*? zH<0*IM~V zUlw~X@4jDR?nB9O*Rl)Ssr{LC^0%FAI)|HZDQzhsO6^%26>2g*=lapGS*S1C4&4a%oiU0x=2M^pj82&>8OzlmpE?i z`KdQgwFSmDkzRx1e$`RaH{2%Gh~?Pb`vi7(A>MO(! zMKSnWlldBKckLJ^D~C>XyLGVy-I|P+iCfKkV!g;sbWD<&;jDZN1LB+NG=9u}!>5aoO&$>H0#}vaR=}E3bUA&@rF@D84Da?a%#n zuXLdv`;g>TxR^w%*LAWp!8Qvoe`^ulab2q*uV7W`bz&hDs44`8KU5a9)hyw8R)6D{ zCfoBYBIO!BU5z{)Zr?G4Oom9?vG$f|<7x(q9X=YWzqW>U(7A3fIjfsXfwXEof1*?Y>R7>w^QFL0}PUsI5tv;^Br-|b}U11Qn7}* z73sfXF6EPyLo9-Xky>aIYwpE_=XmEQJCWmVOT6Jy$T)4xf)?yWq%XC@LG=y#@2@F) zA(4o}=uc2e_Q&ow2RklXblqCu7ZC0_-Q>8YE-_YK6{#r|+g$0eobS;YrEY0ZpN^%*(iBa}e%l2tluo`pM{e!K2kKV+KRr7l zdLX2$Ea?|FnRnR`t?|fASi`2?yZm($QBJDP&b3#!#*D`i!ueMP$a9MNxjJbWnBR5%Ua$0@2<4R2&3=prXiW`C{(_8!eJ>BfhCH_IzRp#7RC$Pp_ zzL9^K-88KZx@lgWYtcql4;}4X>2SGnUm$Y?&Mvs}3`N~=Op5PoYyOy@lFeEd3M_u& zlG@~{UrC^)Ps>u@%3B29gkqtiRa=kt_v_*pvpZ{^K?6-m_?g`{G8%FA#Bth|o=0lZ6VTA=`yR`C*h|(glwDrqZ#KAT@KLGG zGY=iI?3Ce`8Busw$%}kziqVZ+9d&3B9CV)Y;|ftrh}yq~o*Zh^BG$OJg^44S zU*ZeIPyYZM)vw;GGx(|3G${U;RyLVwLX{h$_kP?Klzt*Lw z^*jDrRC!Y8ROkXLtV#4}!@1=rMdFchy~wm>`DLSxWW!Qhe&cj-)wkUt32oNd{WPxR z>;+DzTxAU^1wy6A;=q2%9k<}8(~)w{JDg%x4phro<;+I_jH`Yv-qUv*N-eJT&_h#b z9qlBFiq~1&+yC*pQE{GLa_yoS%`LWc_< zYVB)nR!DYDZ2M&8vzzj+|St z=ArdAO3f1SHKNi#l~mtU5<=Fxv|8xHWRGY%BQdh+7KzQtw<*XfW4rdsX~iXMmDZsI z8l)QfY8=q9$D&&?uOK^d+&lF9cS|-b=GO+@a<)5%L%YwM@4nsb^}J-X0%%YINJ$5C zUbWP7E1T0wB~6bZnxBHRxyOmT;l_53J9l>Yp*7$D@W6lFO?qS`)dTRdyQ3-r*&`KBS&z5A9o99!U-~ zHY;JaXAx6gqv!FdA7H00BIU`p9(LzmV(~xB4672e@IMk;a;!>zcR0_R$dhhTUN<<- z!nim)n*t&zTSvGQwDcPq3GkNAnUF~U^LGRKDx3!k`((U2Vf_Au*@#s%}l>(#V z(I8}eyTiUmSoxEdGEXh0c<;&iqzFmE;yjq*4d&z6S1#K6gKLn`E~tB9l_Hh?Zkn}a zmBu~IX24+`{1r3q@%gBqFR0;mM^7KaU%_+hBH^A$$L%)^hnQ@mi0|>tLmLywl;yVj zd17NJb*a%IK91)YQff<`!*F)jM8xM|xD=02KLx@$2NSxmHMk%+iqQV8R3h!Jc#8eS zxT<#B8D84%NlEs~)OJW#uAMM*s+x3O1mq$C^Vf$s(l&LZ;u^VtHDoRyaDzu>W*FVQ`TZ{XX^+K#&MU>3RJ4%EU4E}Y^4BxvKG9ym zIg-Y->bpM#{lVg{ZQBM=v_6z1_^9vC&Ia&9C$eL_#lrl}Z^W^LxTkqG_}q`)S)()$ zxI`4Dsa%zj+S_R-s3%YH{{V5aIJg}It=%U;hbQ(+8_IGlxq;rCBT`3o^J(U9+$K(C zk3GRW9K-l$2V25geMFXT*-Ooi-`w4vWnJFVhy@G-+6P~Nptr=$cR9Mc)#sq_UBc7o zvDU)Q&AV99LVK0STlMNwX#L&E(5Ws1`*wCA6+LOwS_eoQ)1rdmUz(D*SH`YK=bUSj zITFU>c1#0{<9oTLV_5 zt38)uIJc90g^EJ)bBdqiJ~)eEwMD#QGJkHLZtnpue{i}!^J`L4mx_|2N%Z)gWg*JC z#us|$bL*g6h_FBHA)Q5L6O3 z2j{5Ri)ou4_w_2%FxZ_aXrZF3oMGS}kQ{~MHz4_|V#H%yymI0>?n30Ooioszj0Lh% z9CP+XvZBIDfK#2@ZfQYXK$DTi$^9ZVR|Aa5VD&Vm6+g~$Sy#KENNh)g0DCYZAQYhu zr=K>_zl~%h_nsp={tKAxMsN5#K#r;VLK&eu`151pB8f|(Y z?sEi{A%;yDZ)ES+=b&6dAQ43W07!KDm#|3r=^>>Ayc8^ON{9yMFm9lCLub=Yv9t;# zkKAaRB}S@RjY{{X`ep8_6kZimVb|lNq6j@D7+Ii#%>-*^(5F-oky1{T7$l{dG$)H- zK!Q=xN*!sw<5zI`7^+`ay)-4i-bp&ImQ8A{BaVO~3^L@Gki#Rr#|Qv*>!|DoM}QNP zNZ8s*OBZA{Z)v-GYVLJE{BWWEtxvh50BWqVd!;+iPRRnguH03k%@Q;!PiRRWo|hR- zomWG?-WjKXR<1+jY+KLl3gMFAJdI%%UO&&EDZ+58)2=N>=HeM~3+*W>0mOxN5xAiz zTzB1@K11j>;}m?+)VaU+4JZKbO?2;FHC?;fNw98tx5yT>K9nvU4z<^}ZBBxku8{a) z_NwG=A^VSbvy$aF9A6}SD9mq<$;J7)5oT46d}blPE*YN73tzD%r( z+nbi+Bg$FG;W0I*TG0Mlb~W#>K(*Ze0GwlBzc{;?!HuJ-r+(*7pB+tmQmf~$%&rdc z2aD~tyz9kJE7<2ub1S{v;9D>`F;N!(0Bf~DQ=zHS(vX5u?_dd0KnkcMuDxCD=6fqM zOD|cpj)s(8T=yRViHx>e2ks6!j|7R}R|dJw9Ls2xbWgX5;XSu(o#K#F?M6{`C_USK zMLxRS=5OfTqp4YLH;fLGd%6TKnLNs#Y3F>J=M{hb3}dJJb~hHJp}3KLkwNL)MQAk} z6%N+x)lbgchdzU%qL)cxWVNolS8u>{)tb(bi#Jsig!NZVA?~10#LB5zhQI0DM3s)o z_riB0aM1q%$4uMCTxy)VYeRab6G$si^-|9Ip7UK5IZNwT?V|uGzf;pog^DcXo{2IO z4_q!0qA-H%(p*X!Y*dvgXS5AVg51w2jCCbIy18r+I!77?f>qn(>1=Ul6Jxw*1OU81 zL%)PwuILi7Cj|ojx)6g+dMA<0%w8pyjQwXRp?0^8sC!Whl-awUmoL`rN(8Uxi{O~UuBzv&4n0oR1JFq#Q|T(PIAll zSlIe3(eM5WyNhA}0NCY(@75TGpA+3yeqYKRxJ+DRxKtl;OHv8k0%$9&C567$g1R13 z=*c1Q3OBQ`prn#Ermg~{*{Ym4caCLpZzN-Q!z`DfmjsK#fT(UOCZ@mnjRv$lR}^nA z8p>(ia{mB(IlDOi21(1P_X;=WSJgL*o=D`}0Ni|Wr`JCTC~dhGRjo=42GS4|$TjWQ zbQ^21^N($9GIqmY+V=D-=8v@=`rh%(gP!tV1g4~TSE&lnWL&1MNWBZ4lB&0a1-Ou; z_cz>Yy$-p(pB?`ITOX${Ro!yj2CI(P*bcLDL%getiDd%vP$@^ z_Zi80(Nn1j(zW)Kf-6sktK8ZxOmCu$xr>cqa&93y*o1^$Sx>oTlhd*E`X7?I9cs9T zD0V*MYrjEU!`;3D+|`!&$N5>PTuaRC*GzutKF@cExHlVnif&wWiWI3o?D!pecGl&R z$GN7UuN+xK=-ycIM-2$@QNpIFvaaGb+pdyc0?-RkQcSCo`}SF-emZ8Ps|YNG5Vp*k z;ck!}izu|eT5c8igQm;KH3_@iBr`IO6;hN8@l#7*xgso(5emMF2o^i2*i*h{q8OI$2G}8gad$XC7u##?@sJzM=p=Dvz}3!L$+vcYe?<9P})tXAUMOA^w1 z*2-2Yq&kCIQ=zWBZOL&L7LySpOG|sNsoV#Z*<|emkUTw(9TI;pcu&QAvg^5)3gU1a zeMBwv<{H#!lW|;| zKH-V>+$)je{^~PGOKsoMRJiV_4yakDUxC!y>swr#3*n%078wkd3E~@oT^R1I{et^$ zzr$(xZ>7(1AwU8RK^5AX=9vt1w$Ige%wA?2#s@I!mlDV$*>qJ3{PkR4Laz%nMI2&9 zyrm(rE(7FO)Sw+nbbyKl3^IsNk8Bdj_gGi!Wcm-oOfjXdsL-hfDz=AERC$tMj)zV` zh&mwLkyS1qMxH%Lpy*Dun9fAf-%fE+;<5RjFz|hAb4L!#G44TXbB4>2HT}Bo&(R_? zqqe@473-zn@%Mr1!K+bXd23#M8-YhB6qHMZv`n=~Dri+nYPC&0MyMF}Om@6c%Jyg< z7oIa#q#5vS*%H5S!oRd$&}-fZNLs#Q=c@>BX0#!$^D$OkV{PvI&GN{R*t<)&N*0`f zzh&C?w<488g1tf2w6=`6Zum~Bn%5&Gtb#elo-5nEou`Mr7i#a^I-2Uf+W3RD-Bp%H z6xjy$bVw~Y{6&O2y(`+Uj;x#-;IlM*PD{})OZs=!B=)M_E4>q%H0sb-RWm|IWn#;U zgqMA@#ksKHTPbunD6~)=TjMgaLO}+US*}5YhV7cRwd$yMJ@~PUavio>O}#NDFSw#Y zQUbx=n)E-m-tpX7JT>O6ULyCa_QhkBW1ky#$T|?XJv<~ut;HzY zdeADG_a_zQc6oTWFpb^|=65E-<#@H-eJAL_1M-#cADy{9rF=)_b-&PQi4fJD0+M~m!8lTq0<=h^1Mec1COt`vs<0OGRZRF$Tpqh$|Ks;2-|zPWuX8~)qMdIuhLzDX9A>GY8~PnK@T}vKg%EMKVwYP4r{7O&!nCS>2T|mTihCsv z)mMFS9T$E^!AY$RbegBAw9%$k`i!Insnd~^+>38bAazc((RtTa6$&cCDWZQwbk%#; zP&XAPZCAdUd?YPX`i7KLcXUQh<8AchX{lROA}P@I^#1@YJG>Nu6#_buK}z)0q|1#G zgI9U+VZUx_Qqcx#f~7+T=y6FDNh8#cO)(5-dS+LE)!lEl$J%cz@($Tz^1l0!W3ha7 z;)(EY9FYD&C3faYEmB*!I}(r44;7`p(u&bQ(Clbq=QD8IFv8C?t{oL764vJF-m|>1 zw3_^BqP@O;Wq)UHIJuhPo5b<~z7WaoaBbF0dzQ>`Q7tyK!X>0jlG+lKy-wrZPvxhe zuA%Yha(2=G0F}BGW5_Qt*G~?Iww2b4jr^td`@Gv1$k|tqoX3oLE5p2Z?FpQdY?$4$ zRD$DSXR_G(oOGoG(5WgTTd1U0+L76DMRqnyOC0> zI&~d7>1=fnR@=l56yOeRzJ^MY>xTbP}aBLR*5$L(HM4SKZte z=W=M0kSakH*^8T)nhY%7NmdvuD6VEKcqW3nS6{>a6it`o7=^Dp&GGCrkRjzbjyo!P zF&(vvzYpw4Jv<%HGy%M&h7b?)Be52 zu_1%OMaE64{dPsNUE!a5+gy7}R@&1dPy(T{QaW$Gm+Cn5PBw$DPv)E1+VBthn&JNd zvgKY^gO6xgT*w3@-PA)V_lxPb)sQ{T?!c4!X0CkL};&t2`(ZSdN?#$3iN&d8-E$88I4l3J(q)JX!pG;%~;N+@n= zuTlrWHU09SDzc&g`j1n#r!j;LR8LBX{7072{+p_7k`K6&QlL_5C`V27hv*bp;{{ur zADf;|S>b0EiytE~XpPC1=(ddFTzq>RZe;6pHp?wH`;XZBjgW;9R@#!IT9aM$hGSzU zv{7H=XT!s?n7CIRT6&ro?6zb&@~k;3N@-|Q3I zfA-yRa?v!G4P#%e2Bx2i>l9zL4~zT>!;qupnRZXgS(VGM&d4Lm7HGkOpJ`1I87U!( zgI?lEPpQ`=9_?mt?svr64+<{j{{Yyh9mQDfncImSYfs@t&n$!OzP@kGa*TV8GOjV> zdp0TUGaBO-@Fuw-st3Y7u%sxwgc>-aM*DTwSw8T-&JZSz#F47=WzX&A+bc7jXERs< z`)N?DM;raSa@cvx6~L2NLootYBcRq{S86Z7dP)l z^Zx+GVfg$PLE~Sx$41KC$~EaqYPv&g`*e7($GGNKV9hNuCvtx(W9qcz4M>=_TX8Pc zDRE79rL?6hSPJ(&YDZmU+s?9=S520Nfb?Bf)7snxjii>|Q0I=T^XBv4#w@eNZy@;( zigK$utrIa>s{N4nL&$yu{{YpD>K#tY3v7+Ytw*N4C3Cjo)w48cLg73@@%HmR)ZbV8ji&!R(35qCdTG}(G| z9TEFv=U_^0_SM>?9sUG*jU|quNw|bc$|rUD-rdF%ciVDmm5?R0EA%K1zlNn;MjYCO zS7R}V>~dzX=}LtT;O;eEBq3Y5qwY_8MZ`FTMs)oS3IVC`&K;t!xrB{EZQWoPuc=7? z0EWHLbS@c0a7|E>KP^V}3YEB_y0`xTyW^wYQ=8CK8htb#YO{SsQVfXAG^F0*vV%bl z#cxjWQMEKGokya`Q$yKvbf3_E8e2sX3Kine1A#;Ioe@2PAcO+7`DqkMB^YhGr?0Mp zPV`|6)k2=Wx)ep~A=JqUCc6(!9TRm)uQ$CvhJZ>4ird?*Y3r$xph~wMFu!WC;{HHi zW;l^A{+Ruxv9de6oRcH4)8KX+>m8pvt|M=1t?v|wGRkQ?(LtZFxLlEj z+YCSrQsRGdrraaq0th2wDXhKjTN!lI6Nqs=md6u<%wygrX6946zr07x7|$x^9DB}i zETaeVCmIG8Yk9{~S&x2-6YUwUM@16UrJw+^kZbYSQvHoO8fm9>&OS=AskdWgmYJx#SZ;tNa}X*0REm^1 zk}8#t@z+~njiQVcTuSUZO1Z|hP7W32?AM4htZS1vvoE+{IqoX5yCz9&dA8;;(5bZr zrAT>|^jl6PNmr<-6pwKeCShy4s>@krx07=_FQXq7yxPb7u;q+9Ft+3o{B83y8)YtC z@q29dtr!upM?66i5ZoI6gxnNW;rB($~LAY9figs^>-RY?N)rNDAJ1Pamw4Rjh z?OhMde?P@^S0u3g-ahT#ALKSTwtGvs+j%|4&&fO)CPZ6B`oSX4F)M_ds$D@*N`q;2 zr8Y`kK@KF85vxu!J`)RaXM_{DeidBjmtc8DV%d<&_5#OOxUCM9(AV<&x+({B{{Xhj z?v37Ls|3T-9WjfjSN&K6igtpGgeDrOv*<~u&s`V)0Ji*7pF$icvX=hLWtkqOwXaIo z?0yQd^Do)|0L=ak@#mPXyu-p7G^X*X)<>~emRFd(T4bV3gBpC7BSTYtgmfWm%=LNMwml}L!hfufsfLL`40E&T6&s`m!jMn66K?|46%af7ydxKM6s(s{t zxd)kCzl-9y&QIhHNfO^+33f;JyOfJ$sgjVM!0ee2RMU5>ytK4)IF{(AlE?4ON3%`q?v00AelHK%HJJ=Y98ko!huo;~ny5b}O2%2N5alX=G~xxmIaPRVFg z=dHMUT%7z1q^U5HwKha{8WLT2C9iRPs36cDt;D^|^2kd!E};Xn=o zD5M>>HP=A6o-Bp4+tfG_RnIISy~j+)?b_xMP=4KiHPml7{`1~Id85RbcLzh^3}462 zI!u9a#(!sgOLaTO_VMvSB~&uUg0qKQ~dRoVzvABKB&Fz+)TF+Y;(eAnE@ z=AiT&Rxnduhe~)K=5$(No(uVp&wee~`LX=Y%Z*~QvFnBF_jkJknB|e%1*6;z#u*Bf zxbmhfAxaFT2~Zmnq7IjF&p^B}JE$p`J+W-K_RkTfojiUZ)5Eno_gs88Pwy#i?BdPk z)uFQrK>Cy$>;tszU547|w(gD0t3u|}MJr#iGL8e%T$a(1S%4_NhdQSA9W`6xK9|%_^=X%^4*UP38lj z)A}f?W_rc8!+*kB$E^)m?i1f~+dbyJ+l9P2Ah?!d%PL7J1L*{ck*Uzx0N`BiiJ_2t z+Cv&VdUQfv^PH0|w&6JDM|Dfq907$!v0Kv;D^52F=flsWx`ZW8El2dJ!bscGMBzAaZH0eZF5OWx+5Z5U?^`8FC@xpC zewvSoZOCW&wJF@pus%xL=+zu4334%UzR;0Gmijme)sFU_>byYUREx)$22IEUWxEnF z?-$7Zg{)mtnr3vi)n9RJA>Poi+C^1W3T@W9sJzHB5vtT;F>jb?k}O=d)O z68ZAoXhBrBw#4~@Y+7<#j2bex2}t;!l+=dC z95q#JxpNA1H=D}|p+HeRYw**bM63ntmaEJsIFg3d?UV2nKgUTnv_x-Qp4?xK-+2=^ z$MN_LJ1$+yN)X#ABmq;plzkEgQl(RH^& z!}#XVsGN&_xrDCOCNln)+Hoq`Y5GVbTtgch-rFwtelN@|vj~-FgD0$zPeVZ?K z`vjKw=J5+4UBv=Yu=p*SwA+A4B%`*Cr~^;@!{M(}+S~Fn-K>2YzGdXI+)4Cwu>#;v zLH!p$JgLb17vwv{EB^q6UNL|mML zrjq08R?a9Ig68%49bi4j!m{5FE8g=!3hqA2@(J%b3^1}V2_sOizn?x*_`x>ABZ4_^ z7r8>ot+B!X0IF>vr3e#30SFAJg%s{SRM&<*vA|xw;M_!fwK^}Qp7-)ReWVSuoK?Z2 zRr1@&Oybe*QVu?;H|tk=y_rgn?Ju^bgrt1B>(~A&*n-BPcej+H<7br?Odjw` z1JT|~t&%-Qaz=zKJHZ`PjI%@S+z(1Gpzb>IQ-WdLZn+0EL!W=+td7dIxi60AGZJeL zWCe!P24r7wcca=@ajx4D)|%90@!3h3#>Rg2=Zs!+31azOaw>JvR>)kr6UfY-M-gVc z(9Z|(UI!UhE3Ni&5h^p0p*T!r!j6y!?^G4q*&c(}Ta#dM&{eG8Bf9?pHOn2F8LOH; zUqS}ff}>gyPxwsS+V`4sgHa)1)9*ZuGTj~j0PM17B~9Hf_UM#sKD3cn53-KppG4~a z02ugfqYiM|3e0Cdqa$!*h?xs|c%R^-jx(`k*`&+&7*EL~an;h(4aaOqy(w`m?Z4cW z7|8EO(INVPgSAgxO}fg%Y&}NOp9M>dz+PTRV0~IgKvzL7BEl{lU7tj> zruShN*hq5QdNKQb#$vw}R1PTzydh$=>0M>-ec81R3~sBY*?R+i_`OGY2p>BB1r5X4 zSBzJ#VojHxxvK99$91(mXvbvOO6{{@WUx=|)QOI6K7}Ps_NY>+`n@!H%#KOcqFZKe z9Z$(X~wLy5QMh&hMDp=#`gRRO2p4xZ6|R-6k`k zU@z%G`c=JluBayWCTnUj>>!nF-)7^!Yo>}oKt3wcVqb2V_#P3%x94td-FQDfyT_Am zxEp`}00kh%OA1hJX+6iW#eh41+pNU?=9fI0?JIL-?7)gP0ynVr@mKT4PaE)P*dH!% zj$MzNryJ&Z44bYgChZT~EJt;O1-95%)Pe5>C{yFC2Q}q4Ur@`2pD+P+KW}(Nm-_eE z$l9>DnjY0mJh$ynicFScan@NU1oxVGp&bcR(bSp~RDx7m>~q0BVDakJ$hjS_CPlL@j$~V{P+F9ge|ELFXs1&h)Skr>O;1{FT5F7qxLkVBm)-rE+&xyN z3cD1e@gllY8@Hy4Jod56`o&iEyM!jzx9)LFtAZ32ZOD$DH6kZ7`-Mt_dut&Z zV>DsvJbr3U!}&htg1;K=U?IGU+d3`6!*+EINK6Sa9uJ|`+I=fWcv=F1Ac~HfJG*$_ z5oy#pQVd(g%VlZWS<;?8muPqk2g!cvad`*GPb>NAJ~eOPC=Cu-=8N0Vzq%&M+e?tl zXb_qVAw?aMRWrbfeW6+^zobUarM{{VX&msi&mEYn&Ffm(bttvV`U zoN=ye#&gqYn~dHpSQd93`=1#ck_(oJ*Xs|yw&C1FDLt;TrxF6oYXM0ngbi;nd3$T- z2eH7O>yC0>H+hPf#WP3_sc(9Y#WZ;9o;eSNErXj{206 zNg=qzKwTKlWkev3v*TjSjr(9xr*QIDJvwy$x}cUc!0#H~DMAC-A7@h=17*BmIbd2Z*Bho#HL*1-0nkG!;q*t}Hk+LhXR;w;N_jnfjqxg3f zMvj+qSUB0y@k~_G)>+Qj0&?fv{{UmV zU_7D29y(ra_)V%kt;x4c&O!a9+be1!s{A;w>>Ddj{4{J;=}x*lXA*m)6Hlq|PU5W z98mNf%Oucgm&>_Ig4{4@o6R_+6eqa%6H5Fy(~gwUJOZ`&M=g0XiSY(`iD4NYV{YIK zki=`7Eq2B(3&f{;gd!_9ms@3VkX#@HrNyMIP#)nytbOgZypqtR1hi7#{y(^LC zw^^%Zv*_Eq#4VQ+w!_@-H%5-_tra=)lLF#iFz5bJ1oLkZVfR1TVBEal?ZbHt zRL5=A%5V0GLq!4Asj#F99@2HXMTLZT+chNvdMORAn z{{X{R6j_vE*Si`mp!Sp3=b(wHF0=x>cK-m5g=(cGSWSj2aFzf{NBp}a^*)DA#H;Qf z*dh{SIVt5jwI8dhbl&ot0Rg7aUT7e*K?G2fQ@)gz0@xZ5bQ7u1XoHtea@$k$H4vFI(lo` zPko<@Ew;V$Kr{!tM~BPcx|R1k@jJzTHaMRp`IF>UPo zZY(nIYw_kbngzB&3a3i%u3PMl{I{yI4UnuRuRGF=|!+qghp(z7;D2gO{`t;Yh;7Oh&?By>F4!M)LD_9FghOS#^(%=t3C=Yg^WS1nHb5&Ql=$(Pnjcr__J@gA%?)R)D9g;O+ zAOWhrB1u+D#yl21<0pSGy*3qK!g?5}t?9d0ie z$VX5$`S(fKU7dHlg4=Hndtvuc-r=ADAwsp&HWtHhd%6X0mXx+C4yQr$D*9)WB3iNd zkXcJ@siu&*>qpX5gbF1lpN6@HZuR%~##7OBUO$hI9gl1+>)fgtcapo!<@eMm6tb4b zS|+<{Y$B8jzIke#QP~jK3GMW%*YKdz?l_xll+;4IUPXzwNJzLX z1#JRI1HPRH&s+Pu=ffW6w{r6KI};tm!LN8@Pja7AnZ95X9e*CpV$WNZN}PoFA;eeJ zE2!LRrSn0MZ~3Djj}>g_=EX=ww7`SZw5!X&bJcdoi}NL^Hxg?lE83un3LSB7Z*Im< zwSz_L-)FDiEr@i7%zCNiFQuYdQ8Z0HqI&vk5w&&WZ~;mUyb@b=pQ*m>^<8kyRh$#< z(N@1CJa@P9ovG}Oz=T1}t?d)u012FwrlAY@{0_Q52Ig+JaEeo+;U4LGW$U=z+X_5& z9xI7?U7F=#xXicQA+8j-Yg2M#tt2T6NudO5zp#h5aMUgpkB}cJ>J)u3<)ojhPaca& zZ5SyvxwKHJr_WH>RL9$BoVNRdRIoehN3M;As+RK1fTcSn@y1$9D_3(06zfu-!%L8w zYtdmcc(%FK9aXsCZhqwc1iVGcusg$ww78%yHpQr}8|VjgxP!HR2h&{pmvCDgtVOa9 zH)6KF-20oC<5xmkcO8SPPet^j;rE=_4>wzww|nWuvhbAt`oi*(8gZH9 z?LFQ$Qd_H6)&Brxe{q=3K@bi*BwWKBV4N9 z?rT{zG@|sLM;&dEj)sP+wcK!Q(}$&**!-t&c#n8e;)<#0$R@ovBn@RNE%j%DoG{$>)0V?*i9wlE`k7S{;3I9p-&u^_4bVQV)vIrSWRd zJ$`b{ASU32J#KwnsggGcug03HWFh@DD)vdtPWTk!31Q~jkr}|_YZV0~dg>gEE$Xcz zk*^vehNxKVG)d|{8cd1TI;oqPVAhPeks;c}xp21%b5AU*aY7 zt^ss@Dpg8tm*~vTw8>AnKvEt_8z0MATaQu$>C7};3e}KZH`RdZiOeXPz_#C6?+O0^ z<9S16B-5xmg%!h~cIdhzxwaP!^yC`&rn==%#oP_cAHK7w(V2sO(0vt7n$fY5-m^^{ zvgFN=l8)9iz7YcLAK!}LUwExHtv(dh9VNGQ9noBmLhd@VszG)fh>O@B!Rf0=?ppAu z=EpuVgkot`DYtJ?(@e<-AvD?C-4fJk#b6HY&s|Z;YFcWn;ke!?hm#gO=KEbAT|Sek zHuIRxLWz5gY=G1#GmviD*23#LEius}Lx9zs~2PwG6 zRI{$%=(xSf+%qn~^vLq9s)->L?rB1VQ*CzHpB$~+%|Tq70n2BKUrJzF;n=EclHiY^ ztd@c8B+`^Rs+!wH-!QzXhq{LD*u4syEfgsysO#5Mw@lKDqvP|v>Y6#4Zg<)0xm_d~ zvsT+}xg|j%MJ%8;kb6KI00yd^2nV9q<1&@%RAl1i+GVvOBko0$9QQLJwG=H1_=P5f z9ahg9Pf)e?lkOD-GOoebpLO?Er{0jFx86}DN?MIcC)Ax*&DTYvw3eYa{iOSe4ief& zRitlG(@|d9g=in9i5^lYw-lv!e~CJ;a8@w7p;qm1YOh40Qm)$VTIhjJqaK=4j;SH9=1 zKgUXIsGBgOA;JdRYFk@X5$zRI$Beuse)1Qac;k#LOus5-QFezD*^biPT7%C4+7f_C zC;SCZKy^8?HYm)F=oMA-Un9LXb#{yNr2CD1r8B-t`&e<`hu$sac;^{kaJjZihFF{0 z)o!=sD2~tk+YiW(+eN8oxOV~%Z3x#O#ATmvlXc6Ry0y`7@;E^)h=#jpzF)YD{@c!0 zyyVj1bKjMCPZ9qBYg$7yA`yBL8&M;j;dwJBF{s5B!) zU3HK;w_rl$mRQ*8)4lx{&-Q(QW<1x+d5SpK3U1_QNtbX$D_nM=#$l(NQ^#jkl(qmg zp;xBb=$4j`FoD*Aaw&4Q_OcTrskro2Vq{qs+0tOiNp4g(7SyKd4{MF6p+o7Y*o{Ow z6{w1jZAV06(Q$Isbhq5l(qD4hCOnoJaRJ7bxbG$8rNkf9g%r_A^%WX)zfhMmD%vUM zllg}E&Rn0C@}1uuvtjp4xiN*qAIIQ}ZKZ_AiqtXe#|l_SaBL}bJE)VZyLrON-Pvp% z&Z@|N@&WQZp8@Y*aHcb?eOzT^%UHynNULl_U*sDuecis4=fLBv$&({?*3g+ z$(pU-qqpx`xbOhuexrAqy*?i*R=*nm098Poa*OQ;rmy{76(eHN2n{_nplXojp%Wyv zx5w8&OGMMH>b)uslC!x!x>oQ}^b|sO4(94B+w!mZ>Bt?bXKFUm)=x^)ugg!>MvwrN z*p!CbS`2iktk>N&?oY^SM&(;91e(ERli09Qw0@+WjedHMw!KtGNDiSsaU3fs%dZyOPB&zgd7X2R)L1d&Fyp@Dm(&p3 zVM!gKN+)1Q)O(DKPzt!xiZ`bDMHt$WXuA``eh&H5z!G?uFmPUcR+G-xwy_*Hk`;Gm zFTHS=^)keU;kxorl@!$l(br)^u78^5n46{+>@tz?TC5+px$AqD$Yc;T$H%YEpVf4u zG5d9Whfwb7`iVm-~km5h(AfTu4$BqrSPEeD@P? zIHUv~oflrnds~>hvvVc64sC1tyLc$S9Q!tX%H-d1McTa0y+Gh>8Bh~z1n;>)}JHe z)Ohy^kGJ0^c@rq)e6h;e-P0$4S&ePTo|x6L($%qs!$p_kM;(q80abN7_18IPJ7u)Q z#Tci_btwI_H!y$lB{im+sw3q0fDrr@tdO{UY!>n_Zx$=jbdGMXz(TLujamfavwhYuazw>UAc<* zwLt7l7F?5G*%nPeWeZ96yHCS^p1l(Xx0t9~UraAA9(~9zfByib-yuqW1$MkrAtDnG*_0=l_X)8CL-Nhk1nDW#1W_>(hN&+0U&Oe*VzyC0DD= z85qs_Llq&&PL!ozAgY7;jY^rtj}9G_b~Up^T6?Q8l);b6oGTve!4KatA=>Q$D=d+} zO?3WKY|O3=w=LF4`mQ6zuOEWsuD5oP&8YdVd`n=*khCZeQg#PzdAt)^QF`jyO~Rop z+noK}SE4v-X;z2KX~NwT?QKdyK~#5w_e#)ee=Ro{+!V@|xD_d62pJ|RZoZ*4n`;4y z{{Rw^`c!pVD%mKmraq)$-PBkBO)59^^wh9Qr%&4p-`y;iTzyOW`wWY4K8_$%f%uIi zG*wJ%$!h3Vk9iaxW#F8Pj4$sccN;4umb=f_+D#IwtKqEm*mN^gcU?ONGcN4*9Y|C9 ztek)2H;=a~M;k;FIY#*f$1>d2Xh-e}w;`sLN~H)JZT@1z z4bE))gy}ObGF2{Yw-)5ND>YKwQK=-Jrn85n2bG0QBI#p9-EF(mL2zV|n znUUY^*`=jQuR;<*BTHa4NR(ByN;j!dC0!-Rx3%mtwdMFtfHx0w6MDmLRmN~&q z{cvSbab6t?Rk+i)mgT0SqJJH6y|g`2TvwLb(x7NT-$;las%x4P$**eV7us0*skh@z zS~ibrq?MZ=P?<7gE-T7yWAHkubS0%)G_h=Ah@c1KjEQ88PWice#nIGo%B@+uUAxE=Do2(n&{Gl$w;pXnh%N6nbkfL zd;P35>&yQDhSF5E04iva+-awsqe(;}^8wv&FSW^sgOCdkqF}aT)Z4#I5c9lm@chy1kj-$4;$>%scwvQ89YDZiWS$$XcTJ8j7PR^ef}J}RY#vkoB1uNX#q zU{d0&mhZU4f)>Pk<~x>eXLZZ(tS7XTvQg1Da$Gr^dmr^y zAuQbSH`m%9ul&phi0<}epnj4+m3&+(Bc+?6o(L@RH-BoeRX2vR#57yn9=|uK;6H`R2miEzXg8reAB}>D_xtFIbQe1 z_X5;wR9`Id_8FfpJ-LMsVT`KHBfXoPcF7<}N<+=0tGBdwDCDm2%_nnz1)z&FzKxN6 zo_K-xTE#Fw9dbPD?g7jeJ7x0sHeCJ?%5V$`r06$`1!C zK%{o4rVmU_RavsEb><}(T_^D zHIrl)2$9_P6mBT`nL<>Q)z;hY2o?UNH`jl~`z?*Oi+j(WpKqG;o>$(yt<<5h(8W>@ zZbU{tH<(<7_X*^?UpnVlMlFhG5iD`wxy|-JmDO>{{U!wTbRqhveiPHY(`aYC~9+1r=T?@ zwwzK)Kye@fQ$woDt7u+4;-OE5{^~z0M1$}>SJy}Vzr0K3)uEjCl`cHVX3zX-=6G#F zyW^}}(~w>vT`grwLfu^86%~Y~ZX!!aDg{L(0DFn@{{Xw0s|~^^+d>a+i`O2?aqOMD zJxSaPTszdAekbqo`L1&P_AoADy?HNl=9_mIw}A0~Bf)DHcF}T&Z4=&+immp=(I2)+ zTQQ|J_$|8ONgx1eu8-{Ib~jl1TY>AL{!5yB?Z*r`52|w=KnAU?1vS)kr^n}B%ja2Y zl^WvHNIuJzDee^=r5>LyopstbS5FnjyPh_ z<#{e8ki>&@#N*^pk^750IW4%?z9Ks?2UOA=N;{M3U2?v0$6CnD*74R#>qXjl4=Ec& z6G=vjP*b*~n*IZ=Dcic0Je=^0mifELJnNY4T)T{UbC>cKJ9N(S-ZwhdMWTx}OgU^w54iG+bbA z-H}JKM!&miRjC&Orb$|YUI3<~`)c~qM@_(|O?FFp*)x9S$z-+k%|WX{>+(WhFS!Ow zZm`?$HxY7$i$g>U8VhYSsIc25NoW?5lA%&}Q77l9?X+uk2phtBr7l0@Jfn+mS3A}r z2Hi4!=G!LIIh&omn~MS*&fj#Qx2O={>>aKR#-QZ!p0x-xqPfN7OeCKXL_FrW)N$S- zjC|f-MMikFKe(?eaXk zCBDJ`087G&aJ4?;i^*F;gxM`Gqc0lvl%W2S1$8-$a}K@Ja>)~z0BtGxr$#ZF+w2() zj_B_m+j532sBx>FDLropcR=eZXf5Tod(xy81%i{*0n<@twJ#bPDHhj(uhq2)#g1Kn zYTP>w>em_g(BFPlx!A3HS0>$jD1-?qJ;k(Efj)!RRTtI`4KF?mO^CgF+#?!ln6_z% zg_jx1AJKYBf{EOOsE{g8T5E=88XYmB^llPAP7&flW@i!Iwd9l5#al|;D?+{@N4OD9 zF$;%8xtc+$F%Ke*bBJMF(iUhE6sizM=cAGm4MIdy0yRv#wheE6YjO+00Y;!wzvZJh z4r*pg2;I>(7lg}g!V>CKgZ$cYw^}BjkJTQFhb<~kxbPI}PyY8!o1##U9?*4G(}O19 zxP-?Yx@C>n6(zMIbmqZSk8XmgM{fPcpr)~WhTdsfKwYncU)fmDyQ=L66mx&?JaXko zTup>C{{TwjcCU9LpD9H$=cYmSgU%!o+@y`dYphQI!_z*-$m)`GT_4`u({4Cq(y_E_ zLgcmMlXD!}Ll7lht`{|10co}yLR$>76;PkkQU}cI=mTOcZrXHT7(e*zCB*eevKz>w zGHwy>*9&CFm+hq;?*8F)+yEP){Imnx9Fec2YJl`9w0WK``H77}6(KcR5 zQXUZ7&qz$Cfk0O2uG&n!6{WL4Q#=U;Qh-YPdk8y!8tJQj9uv3TXq*Rvo!_}+83g+^ zwl+I8)!mSUu?h7!vpvLt^)uWDa()!oq45_nw%i|r@E;|ze3!2u-CY&H@lV8lMCZG; z^BBf6n79WmTpN*Wm~7~HqmjBY9@&!*=x>M(U6fJo{!01^Cq6%lhco|jHc#V_~= zA#g@0-;t8l4XY+ajz&y5lCKe5{kiUHu6HeHpzN;ar>3nT;7l~N)t39~Q%2$ovqs4)5wrAD>HZ6?5L^PPGbxo`##Ze%sM3F(Z zsi(s6ySLGg^A81MYs`7BHag1F>76{oAtJe0o1KL ze(isXgz%ple&Tq#E;!x=#reKDBGw&^F>qcC7f_W|OogHJ`+;7G6$e+D4td7zfG&Ga zg+k}{{@Qap7m*ZlG#;Q32l42x?~lGjeb2mg!Xeld#95|ej$$&zn|B}IEYC`X9Vt=n zN>|!kNIk@XxChf*_bZ0uSI^ueYf1237mo72d5Di;h0-*5{j2#E)K3EZtjsx!l6eOq z=lmxn<6G85qbmVT&fOu412CCjCFnq{qy)N`5J*4IQBIoYeCv%{v5jmm4r=c{(sL{8 znVjp$iyZHQwYlGzShFunv)WpoyMmPIj~Rv)*zaK|P!vfZkTw+7Qpbih;;(RB4=>ML z$Ck#zqC9`eo-m(vlryX{^K`bp!i#d{M|{hRO7{Ul`aeBcNrh}}0w;h~4ZkFoc~7eu zAvmHy@#n||-LRZtn|yWUk=E^ntIQQgarfFuDs2xBq*Q#ytBykR9Pse{6@%~?{G4Vt zN7vw}kCK0GEXxbUqw*IS$X&wtnF1?(epl{1mQ7NpS$vu)DW^l}^VeSCUh!K>{{T+p zQ{ue$?kDZqdw7ia=T!dy+PHhmTy1iNbh+BI>@?bEA<9i$h7{7rz%3cF#qds>uaLRuS1p>ZIoCwc=>sPfslx~+Ydei?KJRNHzBtsku_1a$;zBjcjB zj_I!Gi}usdjYO`g-NWcZS)D>j$YnykD|+k>w(crr(<2_R ztPb$Iv#n4)Av+&oCV=(ZeRBRs?u6MI!D*yj%Jg@)e!^Q~2)f95t~)od{;QN(=O%K0 zlG)7U#CA?Yb(h>+j*5uzV>V3?!(jF@%11^L3Yiujh`mn32_Zg3~s-pW`(2{XdxHrMW3R>~gPQc?)7U1<20DhJNe`l_0H`GT z(@nYLCg`m$1?I;#ai5LeL3o>tNwl5~@^(eCXDL{v+XS13?=1;w3_S*5bqbD3SZymE zsss(FNjj}K=-)7q^$PkdtgMIAu8N<(HE+1;$?Xdy_Km;GR{}#yDzXVRL=?3tX(ZBv zZ8n>8P~GYkBhzD}DY_uaPUQM}=ure+k{o>ZinN2j%;+^HM^G)~NL%zt*|hXX`Dlqz zHUt_|-CgNTB^uX31Q)md_Kv9PvEG9~BM2=Tc^`ola$N}n=mx4fk!=q5Ca07&sn0d^ zM_@LCLrQ-QT1@H+1yclDom9|yWRh!I*HkZgSIuW+;KnS>zPE1oBiU-U{Z7}btEEze z{yMeASW>b-pj0{hlbcAR)$aU~W%B6l+G~)*a4`wmj*}IvYCyw5VD=p z9-^u1!e}d?fCwbjO9%XP6TOj*J0C>Tg7qS!N@qlpR>8E?c8VQ1$Ai@;a#V{VbRTqE z`FZ65x>K@se!xIk zilR-XfPD06_Jt$a8~(>(m2`4}`+)Z1ALa@5I$*M{s+GJor8Egsi&KYkqpJo+sU?=B zI1seQh?CRh>s?N4U}~!;lBS6+GY(6b+YP6^$5sLp>Pb5dC6O5=4IR@nPS~L@I#4Um zm90EM!Zw^rISk}kjU?Wbot7CtP)Vo8y7Wc(7m*pnF7e zx5KJW(^`}BI=zj6YN`n?G*cHFVeE+?8O1TkVV_UzuqluSY7O zSp%s}YhQ-DAGbWc+rFAzdD_@qcfi{^9)o<;E+;t!*t}evw*!8tn2zs=#Bf zgMwtbzimYaVm>=+F=$$N+fh}kGhJ!KsU%XrR+?#`Mxq)s zAaPeTtwHky=nnnzws~6o6q*k zem%(~zT|$?DssQ#qK2D;zPz{3J)dLTWP_sj{{XXJxf!F4u-|p*Qo9v~@~$nLKw?$C z;M~?70QOZR(L>a0&G`%s#e}+oD80FY<@R|OL|peuCK;56f>4jQQKvvUnvmz+|=Nl{PNPpE1WVydxg_M)swZP1|8p-Qgk zI~4DzvfH_VOp5J~)4DHmfd=mmMZW0~6C%h3FEq4;17FA2M`Mtd+54Jkn8@NGyN_&w zpsM#j9mx2;YnN0p5!JNmsy)W%5|r?4wm8df$8`bL6c6;& zJAp~q>Lhn$J99LmlRi91&5sH@egyn}Pw%YNR8?+Zs-~W#50htlI4yi%z$emEMn}e7$t_ zo$zqfD7hRx4MHH=*4n5+6!~dJj#XkmOr?0&1cDIZrFtKpqfcfjP^H0lT?$w$OQ;E0 z3JUeqJ>{KLgKWnphCzzJxv=AJGM5`rpfs&3wym+5$!*>XPeqT<@n|ksTIT3iJv@x@ z8poT#nIhM34a=g67LL8aRpuhJ5KPb& zVb`74q>zbOs2y}$LRfJ_cPrzg1rZcpwyGWW9=b4y6gW^dq3P?Rs#q0Xcc|z)}l5sr=VHcsNt_)!}u+&nXX#~bJAkob1)t^s{B(uEQfs-KO0boi=a z&I_a7E#+K&cENx4CI0})OUb-NaL%qqUSY*gnuU7fnzW%r?{A6wSKHI_64C|aslk92u_xW^P$nmqs>DPW(oV=6HEaAC! z8$5>dXx3|N8x&j6exxM4h7^)nsi~)TN*Zekd&{42?XkirdT6??KZc8TZklsZ*IERf zlk#p0$F^zKZ1P9C>Na57h@Bol2Y>CjT+T(1MW@a25HT?1{acXoIKNuZF1zr##`dHC%on-bCT<& zhUAt-LZ>aop*2^^oA0kea2!RRDlN|~^&8Z$BzvLCa#`E9v_#uJ6fJh8KT_A-+5jY-Zm)g7#zk)F z9V18{2dc#4{l3j!T#IPoa~vtDX#fwOL-$oonZF<_?Wc-)3*y@NlN`PIBgxTRvSwV! zn}hN$xI_#Tn<#1=G6&V_9Kz0Pfs*0LWndVG{&ZkAa+p!|sVYn?H}qU=P|RP>23 zlG}+tkL9K$ZB+XyY7^%4Q2RF9%E(e~4SR1Yzoqu}1ohnND<&;^nLI)5QO6^}{q?>E zO45DBNCGtF4@a3#UA`k)DI6aVA!BXHj^MPVzaFy|toah`7gsB1zof>L*&qSmusi-b z+WT>p5zcMpK1l@^Joj^tx@%E$5B^Aw;rA$Go$v`59(8xODXz$?aJvO#aHd@Pl0h4E zI_7-a2<(1E@t&Q&_0|2b$mI#i7;4$4s=g!AL;gDyQP$2DK_mv+laAL&EF3zqG264NzMX2z0^ttb=tdj9~1o^VTz-IV3US$g=pm0?W}DC5~M z2$pL^lt0m1f25sNWFYk;D?`Wc-EBQP6=k_cmadSN<|#e4oQ}#>`+JE00EqNwMMZnM z?0+3hZ!TNm)5B0sUCA;Vg#mc5MB?$xP54)D3Yzn+gA6Cqn2MEHCsZN4aAV~ z?PapEep-sg7Zol^TG3LEi*qr-REK+m zaU(XlR9wX9pSURYWLNFdo{1&0+pMUhgYrFf3Rp`Dwt{f>)f#~{QQQXn#}<{yLcO94e~X{icbOq?D+niV^9m;x$u2s`9#)0&b;yT@eXem!K24 z^wR;(PF9G;G~sPiKBCzhD1Lfn0_ucmzgW(&wDI^`wL8$1TEdr5ot3U77+i4Xh zgpZ%YP7aEe_aGVFN{wLBnrSY6-1ZKn4^QE!?~_GV+|W8zB3&`!!8J#12HHbSRh&25 zhKY%Fj+Ux`YDS(7R(7u?HB`x}>OLAZX2(@%5|C;D)6Nx21zGH;%%320Zx*<>!%Vw0 zPDQ}nD}0xK;~N~PsdD9Nmo1l=QwW|_?kXY#hds7b2u(l-v;ZrH4%}RN!}TpRe4gv+ zj0E{er`=lr02NviqVVok{{SspHPl(wRP|f-ZBF0)`%2ypg*r4(Y=f1#Lo>p#UNOsa ziTN)d@q(oP0PNovn&Mt!#)*QDH4#crxQ3NMM*E5rLD~kZV#Yxmi<&6o)lRs}Pdi%I znuhfFCVO^7JZj18bPbc#E8;ZVj0Dl$6HrFrcQnw2J>wa^H^u?pwkazxj(woONKW z_yv96w0M$Ay8ud#^aiBBzOZwhy@rko#>*nsx6_nWg?77&_=672aK1_MH|=+Kw&Wb& zeZ?<${{SKQ*OC6v<#wxO=_z$t%aqxY%!4)?>6-z_bAq0Vp(UiHvgvFOak08p@}WD9 z8OT|Yi*P!H&3W$_ekO4n!P;p<PCH1G&+hN+J6lX$K%p#U)(F5qUAm+ zpOS6s8uA28prG*i_tW9tyw-;Y?0lgPYRI+1R$;PO>xlXtzd z$C2!&Bo4QUxO9MXEKH$yyfwWC5hpW{Q}@?DMa{{Zd-!5mNG*Aw{bXW|RDiLd54 z6f0ksUSe@vs7+>h9i^l{{{S!SKGVwyeNLn}q7SlywIGAOxUOf3UNO(OX|A;m9w(~p zmK@H_?`^z|BTZc6uG$)FrBCraE73aqsqn7G-TMIGUj}%y7w4w3Xt(UPMptgWi|-*s zvaqCfwSBri@=%1dw|&5hlCo$v2U56~Cz2L!x~oakRS%ePmP;nvKns6Xl>QnI`Ytic ziH=8+J~V8$J=yyyQZ8|$wDQ*5tuNbow-SVspJ;*=Odsv` z*Ik~>)B=^s2&E&G9*1tych*yrUwSZ8#cp_Ir>}4gGzvM94QIMQ9i*P)_?^#9acSia zE50^50Ep#-`=!1-;X?Z;2zYwcJ8C;Rr>?bS6i~3CUeiUVajBuyRGkn8eDZ$5$ zq97&5JBkS1?pu#^kaXE%Tc&jpC#BpZ5#0K|LwabofDvwa=%2i4hrN?z_UmzQsmU$` zp;WHwJNjyyJ&eCaGyS1FA*YPiM=Uzg*jv;2$uSjF>W^4NGbrDtKqD<#N>pf^Ld&( zEBox`FX_ZKZ1m-KbkDwckvk{7vN+?*92?2p_cUp*djPXt(`dNHb;PCa*IJykFpvqS zQNN~}vgV$U^TOtV-iteZ?f(GOQ)Rl?%Im*f{joe6k(nQKzx3Riyiwd5g^+ zEORz>#j)T)&3KnCik5D8bq;(u(o(4FosI)c8Ud)LjMW3|j} zTJRUkCm>wynML~RbBS#hrYF3&T7;Dq2ED};BH4(I- zCW%Pt(@n_T6|_$TsV5O^OIxqHXo0Y=um1oKOfb>GOfB*iJ6p3j@b9(@^j9Ce%EcK< zBirPy#I+UFfI^w4kwD0e2_esf9Yq=syR2T1rElD3Dbu!C>TF~`1Dt+g<`4<788$|&M0KN0S zC}yj+Te2Lc<8Wdmmt3?!OXZ*2KPhCmrb{$kRZ=%m)~UFActEJ=xy0A^QlHmSd=xQ-nUyWH!?klEI56%I9dr< zS#F)lT6YxNQSE)L##_I9Z6K#^edWx}51}i8ujT&$MIdo^++UM89`|dvf90g$hPweA>8%YE! zJ1>5sc_voqja+{4c%C&TB>DV6^yssaWLshfT3cw0r1y6es(>gH>7qW-T@XCwGbg|H z7Vjm@{1j`JedzIZOHIPb!;l~C?0y+}j>#1p6xBOYpFK~9*^8%tc?z2$`;mK{{S5A5bcgMluKK{tr?OXje6Fg$!hY&;u6Ow(+OK~W{mT4N z&#|@%%^1d9LHg`BW)#>9PwFkY0;yk?xwc!|42_|!30>{X||+ISC= zafp)gObXGE!n-!y_^m%0JD<8~NvL%kuB`!SH?a{7Q5!(P7y%}d@m<4SR<5YX$5?1cf>dRJ|9%a}vDE?r}l*Fr*Xf7AB2 zZPQwQ3H|eBDvdD}2uM%rN|U{7{u-^CCQ_ZkyJluayin*={{X_CJ8>r}-K}`t>zL#4 z{l&;n$-QMc3nTk8a#2rhhStX8zO)KgO=YtgYl~e(Wx;K*%#R`e0N+G({{Xmu$Wu+L zz>X!l=h+t)pDnS`l?-)vyej_y`&WiNc9_QQTN5;^wGsk%-_vo>>b05cmxjC3A*Zjw zU;hB7hB~#?Q{rg0~P_Hm>gD zsjA_f*EhWK4nUig@x>S_lGC?TDZ-auWWJTZM-Tp@(cMKKQ@)XOJt&JI(9vJxN2xnx zpHK?8oWnVQ|>_j0fQ0N7ZUI?bYjUgZ%y^#e zzz@%<)#dI{kurej6;>k!B=S^(Nmf#36KcflQTZpDh{v(^+5Z6OM~=#m?qsDuQWBQQ z1pw2nH`WdLTa1jpjkwVDE2}>ZV}-T)i=sDnYNm443$)lx;sJ&9VXenJZ*3m?e)d!Q@Y$_ExIYpnz$V--ZQm%f2FCP*$VX0;xgtCmtqw1yzi zfwtS9yUc1Fbp1}9)x7|&=DJm^21n5&A(3UgMa4RDi=zxPA04$X{zl#?`=Af%x;9e3 z4OrxF1wA%&3pa(aadDbY)bGhK)T-0vs%5BXqSTrVK8+AktFY;)rAcc5M$o$bBvOM< zyl9X@7fn?q2#;Mm8biW5+G|fyO)REd6N<}ge(7+B0fcuwA!$kl3F>uojz@;q~E$7Bw`q_*fK$11JsVWA&ZDYt+q)Oa z(|IWC(Q%^ba&`4docha;NF{B@tTihu>PyOffct`eox`s!sUq4!GGSZ-YI z5n>@5t!YU=0qS)Y>D~%$yG2v4E1ubXyJTTl@CRZD0)&CN{yLRy9RgzhKzb(p6nl$6 z>*{qbC#q?mK#ttx6%fiu2~OQLJ9Pqs5iT`TcaGRfGOX2`jmaIhOmKeg=;DXdM*g6h z{Poa1vA_$v?@~|vaq3qe_pg;33}J>p{IdB+j;lF`H+MYD?sI~V?3hqy-qm(XO?@L) z9K$egZwVbceaa`dTw%DZYZ?8=Q}Zc_V1^{!A=N(OTX7310-~rPLcf^Rb}|n=Pe8SK zNy}ky9Z09*uk4Jz@sSLdlrnXo->(fY!?F;7+Cvdq46=lz4S)ltx&Hts$bDV^0KhFr z55xZeuEqwA`l?ru++Dt2B*;JTl(X8BNdkv@S4w0+H9stsD9mR4lmpXFo`^*q3h9T1 zoN$=7%bz07W3o-rUd^Z|P=--ZR4eg1^bfT75ZFxa8lH>iSMJl?FRZ_9glEYgUz)zb zOZtszO?7Ds@xcKZLh0%0rA8ADYFEbXjO16?)_143X^GmmuGcq+N*IjD?pYrd5wb_A z)lN!xM$agzR@l6AWp!TQ){Rm8zmOW1nE38HGDCQ6l}b&D*5dw((u#Pe{Y&a~SBtV_ zWW0|t>ZW9$Ezu2eKAS&C@c#gmn}puxv3<8nD)$z{$y*6mLa=_2dTm`<-9jadwcn(w zJH|TLi6PPLB8#*_(>8%OpfNXjMi^(mATvd767Fb;}+iVHv}+r`Xb*PVVJOon&`L zbxQS9exEX(blfkv?}Ch&RCul{oA$zl$!hJV{?pNN)0lj3&-l($?q00!D$*20d*dlC zM09inEj>R4Q(apfmbbcY=-pbZV=k^N-|5^mwD|V?RcBmhV=T1lN>23@)T-#TO_{G1 z33D3BvOGTbZOO4a^vq)-vbY_z-ypuY(xbUbRSRr`LN+6z8xTgTa#+}|quC&+QY|ke zw~$F0qk`%G00iY&$CXxhaSXxOmQNvV6?(^$_6D2Cq99bE(2x`Rg!-Ra>yl(2bAh<4 zYHi)rcrBb5+df4W$pCHJdOQB_pWEZ`T`=K}bG2bjL*jmH#Mo&tDH$_MDWE_-psW$o z;6}MGBI2%l&DEpLPPD4;+#B2&B5QH^IDn>=qOo_s7`V?mNy)G|&DmXgi)F~_3u$%S zbUSM?XV0%}kEbxI*=D_%!{ou1(1w%XisBXyU*U=Gs7H2fiM3T129?}49Y`LUw7S2> zI&;>|Dec7w7%Aogo9<9j3hx=dMx6xL9E?hgILWKfA7! zwi}%t@mCy35+k=d6M^I{Ayln!Szb(QT_*A0~g7ooYy5St#%EgLvSFcI z>pR5VUsl}F!6e$VWj5ip-w65PyNh=-qO^)o;i?Wzy}1Eh$OS6s4*MMyrNg3iR7;x%OxhJz zIitsHe~_({TYlVwAx)5%I}^Dz`D?Ur4|J|^?*n(0d9&TWVXSj-x~vtV;D3?cA7mID z>C)rET)SG7q7)JT01S2X8;yIjh;!RKv><{vFO{C)dta5}w+G5hU%_)9FXPg|eJW?Q zz)b}=+vTkd)uBxB?p?Z?s^yX4>ukC`pF`=YX<;FDOgAD9rm70RW90#9_GmqJ^%}08 z5W1cxFI{S)TfLP-zJiB(YPMKcK*!{PQS`VAZA$t-6RMX+YPI(FV1-MmuQJto*J}4f zjcweoj*T5qoxsra`Dl^2)YWaH2$tHFT@qHhic+eE3jY9q{50%n6`%0_b<=3j`j8M= zqd=gvw8A`r-P%4h(poB0YEqBK`BXeY42~(@F`c+fJGWKBX7r2{-9QrOtul!q^44el^TLi);j4T zRNRa`krcU1Sdc%^uAtL5oKMI9|E+;*Pfu-{CnEfhmE zuF91PsD65z7|}+N$U;lCaZ09~Yf}Z(TE~m-x|FHT!Rn*aMh2+lnt^Vy*sjXd3ef6H z4N7F|s`Vg)+;#)gPk@_pe9gl$O9NI}c}_hx#Tx$r8M)R!-N$w?xIkI@UyfY^y`^*u zO-7c;_Z?MK*KMU3YL+hZF!-6>s4 zYZo3h<^!3xvU@AuXBT*u`?x0eM^;Y|6n;(LSaVVncp9^50Z*21e z<4X@C@~&saT)`@1&2mmvCKOp1lz7ToksQSgHn$R*6sg6z(ud+TkjrGV&^^zEjgf<* ze-~|vl(mz+(U%=ghxs4PeJ^;!{{U^rkC$7o9&@u^PRQm?Ai6aU>yAXV$A%$ZBd7-w zRW3Oz_LuNU3Z}&Cjpx1W%LbT)VmI{rRdyU7vN+4FuXLtJx{XB$_|y3)8}8foZ{waS zm7jSJ#TMn+ux4#B+qCfAngo|Su8h?SmG7iG+1>~$NdY8^lfJdy*?XZZ$I`vk-ZfZH zc0H+ef%UQ8Rt}&F{$J29lvm)r9%JyOFsSk)sHND;LXhGOY4s&w5w7`fa><2QLHRBz zA#(9W#b&o>&3ZQEuz-mU4diyJF~Id_E{U;RO{S`^OREC zvQ)686eKBB`nnBeVc~YK1J@hK=~be==NHTe>0Ri9si%*9X|F-s|-3;?ru1+W}UV7*Yy7rKyz~0ux25qP=6c+w61hM1{Uu zbSb;fa0$8m z!Cowt!8z9)#m}?uLCvxG5C<%|b#V26%Yoo59Kw9^5lu-y#XCHPV>PyM{!zhMz7K`?eZq~!i<98iX|vIJ$+BF9mxIt6 zg(-nSBlip>p4SSu1Zp-PAHBuf%bfnI_dj;XVGS*7TFqLkCzC!`X4wp$I`UU6k;&dH z@vo1#;w;?Hh+{0An;^Som$FSvORW1Ocf9k7Pt&(>1nX0c;&zrTF-iVMJ5kkP z?|G(EEb%ly;SB^4R*ITiGc({PT?)?>Bb|>0S z%eH@Mq4_8R=>^+O%1l>MP`K+Ua36>?*BQy#x*3a4W#4fZ&A7K~#$6kADXmwt{W4nanMJC%8l6#jMaDG~G!ijBl9FihFr*76YM2d zykHcb>G=A>+5Z4nu&w_9!0I!`lv6{gLKLHxOnW%u>kd%R{{Y~oE&l)|cO&&FujEms zdTOS|LmZ?l%*=mPY$WhemOT1zG0|$7^^x^7PUKVJt-Y`hG%J=~yvI@!jp?Y?M$Bl9 z_9%hezNWo(D(W^y%{r?4M?Pm&#YXZSD_K1|wM6nsT6mZm5fv+PDf#K7E2{HEl)TVD ztVtu~qe%e_J#^fO(2Ox5*v_Talv1=Br9;qzp$mv9E?{t>QZ)3pms-EM$83Lhf|^cg zKl+IRo!|KC+D8c+@lb8SiR2!NJ__ujX+k=WU00DsH>zK?PxhDCwRa(V9ZR-m*Xr_MCoyhZFhr8rj$P|I~!^jD&#qG(pB}f zaoiPI19KY)ceGVRY)v$i7j@5}&AiX>RYR1q(P@z-4K+lH(2tg@_MPjraogb|RZ+XY z{beJ!p{Au#Z>?FPK~jMf14Tt(izse%y|oA;nr5XMG}$Pi2dA#2HPu9n`{(^%;iXYY zqL;W^f;msfOx>Lp`xguXn)A*xgH#YSork{V7R%Cay$lSTawN7deGwI? z=()Y@bkW)nn@ugte1ewU2Yr+e(zN`>rWaLJunH>^#azM2oKbzXW;}0@%7JsSDK12M zJck&Y328mu?6}bc50^vGjYP=UMJs6GsaEzk@L-TZ8Jbdya z4)&48A8;HFL-xFa&n=`W4S#%(6n4KJiXmatxXOil(kraA552{b8cU%)N8A3MD(dN& z?6>|#hDv+d4v9@0caG0s1v{lDdMFLn6NL9w^hX_6)l95tuU?(THe za+`p;;r{>`IqL%DY4`*VSc!mLzjJxxE34~j%YA4v>ps)YA*YB+RIuU|?WlJoZB2Ae zO=JGT*u_#S+%9j!uNz@_*~@Ag4u`Max-HG#S#Z~!{F}iT{t4sNlg8Jt_ZI9K$_>%k zP$NXTl?Uz~@nw$jAGuB>5-U#4fvBV7oLXF6x$~E+9#_g)n!IgTB4{5m{FPR`L$%oX zZ;|+|-eW#aTksp3Sl1YH9l^()wd4>ITS{ZK9Jut0uC zk#ofCa~(qyFHK2fwl&J$`U~N0w6&p55NZLTrmNpOE&(xUMvFylWpQT8N(gJIS`$iZ z<#E%mhjl4)f0R=BGl6)2#_SHs0}tXZI}tAV4U7A78@gPxpof~7brVaDtqi({Y3(F{ zqqzX}V-16rDW4>+sdAH_@yd3auFMjx_oK(g07@b zCp<*U_yfj#6Ewpd;*8VBd%e<6FmffP*4@_i$(BouIW5R(D?=#=lO{vxb;9Y`q<~2J zwTWgcA!~ByG25d(mgX#`GWyYxO3kx3Ky~rzN&aN`^%YUhDS~j0H6h5{vxD7vpMdVx ztBzH~xpx@Lgsh7Vg*H zGc#+FNqxo}2~#O3S4P)Gl#)Fur3R-XlBmpR-?&V39fi3mY8w&U2%)VKH`Ga|5%pJi z>vv0~6Y=QoSgk6vPlyfX=BD0My3t6C2F; z=rvG>RiR-7)1lOLB^zciYzmRv>ONXYPee6VQ^)>Sg~RzuGRU~ZT#qD*-~RwZk4#6q z9vqTt9`?E{k528ikL0|3-LwK|0_r>?nEwE*c`>%Ha_h$x>{aQraBizwSFGc>> z?T`M(){5#W{{ThRfBSzK{FK#N~D+r1_lR;r$oKtk!^az&GtAEAshk)(}a#cy{7Ft{|jGMJPTU zbs#9Av{=f!Rrvbo$~sjjviL|F(Dl(2ZH&<=MR{@^LIEW{yHiB^2C0_KB&!98#0|p) zpVU61PnM&e%H)bPsfJf1%3mL~$2LqZ6|JY;O7c9kGF-S_9YVjRkl_F!C&r?TaR#2utkif6~O zz9x%@L$hQYJej#%D6b)m+BM>QNeiA`_n4HVAe2?3O@Q>(S+SZ&YVRpm4anO@z(YLa zp{jH8ACg&jIA>VyW6H4Yw_CP1Xpd)Y)*+>&%4Q(#Wl|AU6)aYy{Czjmmb`h83Pxz7 zW^>YBN+y+{k*0#5%{A}2RLstL?grHwB()Sae`z1~_VyB@8%a`94{!U&Rt0D zXwp99<@GHUj@zXMEf!SEj2^bkX!oXKT%a9}Cu*ImS`A2b*6zx` z=|c&lmez|_OOZp%ZBcnPEsd)rRLZ$x(!-f85k74InHDe&@3ei94WUAcv%zZC?Ml@u*W?*sx!18ryS zJ=3+0e@OKzZHKWrTZjzJrr!1H6^-G~x5hJ)*`!Xoa`4I#ZfPcJakxuyi0_&c8BVL| zD@}f?8rNL=k$b0b+4e*Q7f<6J!rMOEboC`nc8>x$^`khr%(AvB20 zfLu_e0VCz81KgS9(oiV0jrJlC&DOw!^G@pqJIokU4PNZ|Ma%x5gB2{>tj)UQhENhV zY>xA==|N2*>o1PEZ5+{6X^-RCylDhI#4dvI*OPhl3~twsU-2N8^_(*3KONs?;iGSKs z70{G@B!vxA<9NijS0ZR+r3!yJ%pkwCb-lz}n&C}C`EK%WgYxb<%yJ3sVUO4_x$nB} zBE!T!>LM)nC8rz!RY@HQ*Sq*{C~f7dLx?W}Rr2qjaH+952OXqB=Mp{&lE0Hmfb59z z?TsR%v-avoa@2Zl+wu78QpW35TLv+bG!pN9(aP zLg}5gflu69!7>HfJ-%KuBPq(4^+Ctuf$hc>s9d+VOK1nJX{<*e<<|D?)ztWRE3R=L zZKk+zbHxXV=%n69Z3#$m7XE)R8C z7x7x|LCdi`elrvll4IOuLJDo1;P65nv24iwhzapJn; zY+RWK*^rbgv^d{&4{ExK`fF7c)1_!9MUZ{FSmFV#5!*x?k%uN?kgwYS!$3U0#;0?e z&{tMv<;_)+5FJ6GNs4s~U$~#Br)rW(-|^K`q}5Dh1ZuLRnw|94sc7_c751bKyJ*^h za#%=a6Gd(w<*T6hQ%l-W0aS@1Vx%}!Hcvn_s(_zsD2j@XhMz4XVNL;6sdAOvAC{B3 z0OhZ6KaaynL0}z-1p4UGQDwW|M5Ai;(4uCDT7;&9*RGA)>Y=&FJrZeXrI0`$$3sN$ zY6V*!x&aVgXwsHwAhS_|bvmq8;H3#KC+I=gYUaWaH7c(5=9&eUiJ?v@w*LT4b{{P& z;(F+pX%3Z5v)t=}2&#(B5|Qybu44<00-on=ZBJzUbhn7v{e(CT^6HAb0o|rZr*7V- zSFdXzk7)fxWLh14ak&2g<8M!jjuc9iR83TRolv`~*0rDtFEo^-UDZ?yX(XjIAQA~x z0I5EjRB8I?hBf>M(V`}(cE3;OqDUy?Nh<9~HTixzakVL_G!-fGaLkqzHfP){GT=je z1v4qu9YbqL=t7j58vZ)GeiG@74|@n{??so7lvqn4jyW3V4xkW7QlAX7Zikp#X5G}3 zM`gr>71Eu=&~?_Yh_#UyJ^g~nb6I4XHpd@wDyKg(d39N(^A*9ZPL$*LbXL(8GEdT9 zbw66DJ_B5;<-Vosepb2kXzGct5wmgV72jGL;tmOo0YEjCaxo4* zujg(rY5eyZ^b8QU4}lS#C6`~hc|6GONy{BCSS_?31lR~CaXtxtzLGjgwMtL!cQw)`||)^+)* zN0~3|S!{MpJ_>?X(?~L$Njt0x?@qesIOyEsUV4?#GSddpjp!9{f}Z69Q{19=Cv9j* zRIH^_+Z?k<$?w*Tb{b0Ex;}=@Ka~~wkK{Ek6KHNO8o}LA^IZIR%v_T=(15#}hQPOC zn5B-x2_c5yFowVb)gy9$6R%<7ts8A@)(*jZZsd1!U*E|)^)7KL+F-sna^E6iBR9ot zP_7mQ4&=J4q$Xb}`1?*QEBNaHmiJQ`Wp-efbw_Y27S9FjmS-boE@3R#dlyirec!_G zBdu5){Eq$#i8$<4IvtM^bZK+@86C@5j>?LZkO%VA{Iv?h-pmx&e^0oz-Xu_dD&Hsj zb>x!H9u5)9FA--`$ zO5V5RNbXlbxS`W=F!xff+SW5g)-M}8AiLb6*?A+G;ya9DPpO$Uw>{p*)W{y4h8>m8aD~F0^?U}V{T+Z@S!uT!0 zm74J^RhGh3?cWYHacw01MNv&^K>q+i{Jt9Od{3KwKoQDl<;VFi7?T`Ckk!#1-V2q} zEz)AR>rS+$oM}ZaAxR*GB+!BnLN(Ev>Z-Udn&fX9zTR<*#lH1vp6#;bZ*>mAxH7Wa zth$3xq^NY%Yx@Z9A~C;Kl_<*QI6g93_UZ^-=;1DNz&xGA;o3P4A|Ub&sid_w{>e#s z-qG5Z3Pmg3>#sEOJQdy=<8(y2Gud~(Gs!G@96pWE4Y+9QnwC}2`m@RT~>Hu=lSYPD0?EX?+8o!)ZFYx>12CLj8RYH6x?rrdUwb9BPpLUP)!Fz+mTu?O{A+4igTHhu&hJv1(Aa?<6G z6z-G?RG+0rpnB>vn^ho$+sHBNb@tmX4Zkw}5@dODx z@%X2lE49Av!&gfzw8~|4RT8SGSALqJys(zs(ZfZtvdhJE-O-&<3+0N@l^`ZlDUEPX z{2AyJSU*3GrC8+Uw$yonP;!`fZ**|l?-d-l&OwB}maYg?%UX>SaX_j-{{VOnn$be| zFInK_hmz9g8vSZmoy=UI-C6|;O2WV*hyMT#S50FiC*ZE_vQb<%ozW+d{6kQZ<3^wl zhMS44cg;nWg})>l6Npe#OYB(PL*u6(1xG~0cLWsex89p{1Ud95(v{Uc&7mC?wYB41 zbxXN@q*2FzHeNdS1)RT$?Sb70IZvZ2m^i2>A<{YeLMDeJAj6Xvtn$_rgU zxu-n#=F2N?St9`?E+u(c;Owh&vhR4!zO7YCEg>IC+O*hf+uq&#t!;f6WUkeGruUEb z>wkru&t}uNsSAvJgT`~MQe;G(1yQ8O?(RyGNBNEQ+1w+QyP2q5Ckx1Ah5IDY!?9H_ zCc~7H=q#Fw`fEQmu?2S=CnE-$s2d>0)Y6tct%ZF(bzMEW>e!6PaO$D^t&Ks#I}hcm z$s+8!C5BK@b9F1NYe0Q9YDLV729=arzngJ0`H*FUSN-fi;M-8Ijk9HenNh(ud(y9QGKRr!s zMWa<#>#$uwrRiQl@?_2#YsdGmsb3Vvy8i%ri;b30OII5A7ZT^{0FzQaI++FI{XLzP zGqDgdb2CSnsAt{pKUvu5+UhAZLM34I&@ZZO;HBtxNzyF$Yv@}o+wBTr4~HLZ4=qv@ zQjpV1J<9^0Q3G#XJxYZM?xCVd4*vkV`RMAD?NQi)OMSM`Dy5JU^C~nQe;qp;wNYNx zek^_{ar`)ZQEcTNe6`{{naa5>w$mSv;IeNiv{`O1A{uU&DR7jw*hqcEq@bOUO39!k zSkF8#LLEZq9>G;(Ot)7hjvE*{6W{rKyniKYzULoksXuMjIL7Ax0F>C5JH_Ow4lxrS zvO{te(%jo|jHOC-Dw!xa!8AZv3EYp4pkgyEnnKXcperoq9hPvkwfo$9_WZx)a_1A{ zOU`YWSa91d&f5-tO!u*Gxm>2TZ-CQCImir&a!fbx_ZX*Jmh zqDRA0+0|FfX{8m1@&0#-(C%4l-cebzyoS`PL+$bO3?5ao8;#$IC8w?~T$G;Zsi68> zH732lics$TLI7!^D_6Ck?rl%o^GG-^B-yapN*4|@#(4(cEgg^A?~vVjv?$W9wCr~q zS{5m>sRF$;s?<=bYi3N!l2d*_w%zYiT#n3aa^ES{xV9SaahRznNZ<<nDQE;rk- zw2u)=uJgh9Y#3{Ovq&5~0j)o$Z7KX$(VvW;a9x>;i_!aW~_uBFk z#2fsagMs1r!F6NF?lfu$vGOg7(DYj#J|7LcC7Yzwb|Ob}C+I*56(H<-cOPi%ZsBjO zzjSWs7Yp}?Cyu*ug^g<@h=Aiz4zx60yG_rGu6gEpZfB2kDRBl< zc(6%s^K`OWoOQHGl-j0a8^ylYSv}j4FzatG@@>~YclHuIcZ(Mu zL(y_p7a_LiMV^LXIH=CDp5h$(5S8@Q`0ZXt$|uXuEu7~%6Z@zTA! zP!AQ|&jfrS@x#eGgtmA;#_N2}1-AzxT6y1+a(6OJ#>bjl4||Ye<3c-8{{U(h-FwvV zQd9~{WEzfL$~iVh*p^I9egj3=cy9p2+<%(cts}(#s`u;Dr*Hbo_n#gzZdpcO&KbSQ zj&l}lif$d0<`eHSIO71b*=Ze31hkxsmc&ox<fj6F3E8Ok8PUOZGbx0MFagg1Cy8WiD4}&B;|a6 z`(@oTSWo<&(Fu_mU>a3hk@$nH9rFXm;Io(+=nD@}rau}o_8)%nfZiXin=FjvSuC$~ z2Na*6@6&y5eZ9%HM!wPi0HxsmE0TN5f^>T?;veLRz}LMys+Gu?q^vtFyx5;dYnJH_ zHNUDs`cLLH#q+r1X>{n%VppK}hIyGA229hVfGG~mAt`&=aF1=^QbwwG=(UlJQ3=f! zBshYa3Yt^lq?IW2QVt&aZr015H2LllP+f)z=<{NtmHBChiW;jcM{Z>lc;jxe>x{TN zR@#bGJ_H(QgOzm6+EbSmoS0Wegpd9-k_%GP@oi#<_-HxwQf-}G6tBl+n{G{gz-}x5 z0N`F!%GhYD^oI(2n^6A%$5k07Z6V=laE}k&3%jVTmZW1DET}S2T7CoSY1AEv?N+q- z`=I86u$fGG4=pNI$Z!n4?_xPTENbB{&eMM3yEPI+k-IvQR)my_dXja0HLInIa9L}6 z*dd$KbExkHUz{YGlTqk(nsaDYpb7+7`soEWv)4o#j2bQUX-mp(G<8Y^Z`=o`T_w$` z168$tS|J)bCEF$C`$Wd1uus$a+w@XS#1ZSKqkEcCCSY(&8H{^6W=5Z8kR6Xbu;a11 z7F|E72mP8kz$u#&?LkdOob4ckLOP8luAxtmo))>NlG%vys8ISz_0^;^wCJm+gpERN z&5WqeEhqGo^qoYS8Wi+$R3dr}eNYI}KSFd83K1O-8AslnQO@C zq^o92XuZTupEPN5n@9+Ser|?rN(v3n{6u_Gt^Nm1Ndf&$D$WbGkw=834V>qPFP2R6 z49OmhhE^QMv#V382uiD7#VAxEb^T2^D6WJNsF#*;+{Rfb)m!7TR~8b9AabeKv{{Y-S%5MjyDLh%0M$LGh{^F6GaDGVAuGn?< z3*SrZsyl^78;&NRI>?|TsYqGYcOl22lg!$+-H863{@Se$Ka|aHM!U@40~dvs@zZcwkb*IY`Z`_1=;)RdZ6e%e&$^7c25lXD%Q9XnUGR+|fwvJszLQ&Cz{ zzja2R9aH<8-{PBJDowC>)f#?#g>nNHXqKrLn`>&h9M0ehZWg;)gwsr_Ih5)fh5#y3 zNCcW!PRE$~TXOl_0cwpl4jbv3%U#5i{{T*(eRQQLQ2goR&yhYKqzk3N@O6mfOIO}TwSwm#I1c#g%4dSE8;7!Z_2VBGoNGj{35}Mj3N=g zS#HwqNaR3N-P!ok+6{fTz@GW7}f@lno7+5*%=S- zopDbYZMNbaP@ySOMK&7Z*RUAmHC}_q%p`;qLDcs`Na^#`A{2)mdR}X!DeJD5=1?6&%Qxi!3<>_B^ct+kaJe!M)n0`axKHrrW#rtvO#V#g6 zDrDEUsig@3SMt}@=E@5J@m~|2L;N%g>Y=sEd5C3w%5AXQ ziX-Npx@x5na=Kq`80;s^ZVmnHR_8Fhj^5O`t{^G5TXkgxBpry~Tyx(i?c?%ST^FN& zY{lREByJs*{`1$%E@1Fi%6}sGBJBugzCH4EIW9rttD+M8c>e(18Y*r?ML3dC?I@Aj zlTC+Bcw2$+n{0$_k&fE-x~u3nxnAMiSj$4)%yU1A`HQ%?i85Y!qf1|m69Z3G;*5;qSxbj>jI`2}B~|OGAxByOPeENN8o}M^wusRfX-=CD^;b@o=3lr3v# ze!=ij^w(|Uk(n(i@LnqCu$z3bcs)wEoY9fucI&m1F3;?EuP0>JOkON3cO1r@Tzhq` z!~i|{XacK$dY_5()s7z8?n6qJv>(Mq^zQaecF)Qy1 z{<3-$DMRYKm!EVgP`Pry{wwUWz0ZAM?Iz7$P zMR2%t!hT@oY$G@1ymsq~W7a%2Gm^499p593YQMi3wgiIgI4`z+X=}!!YaJ;rC#dlIf6-dpS1kK^ zGY>4bbB_vKJSD5)3C!^;(+? zt`!?dnYHku=noWeFWX-K0Ey#Q9Ah7o8UAp7OB`t$<1O2j-q`yHudyOCK)D}kp7qf_ zwbwb!=Q#`xKCvwg@LfX##`vYoP5MwAWatl3`1D;D$NYbH1jn(vW-H_G9XW0GOA!Lz zZOg*YvPpR>LROz~WA#&^*DJl{EPRF1O7+@oxObxiV`OZil@oX89zKaG$zt(C5PKAD zwDv=`&r36B7gPO2KSTm*dZ?>^hO6I_$I5H!oyy+d9h0|VYhrHS1c7zWyp~Qbj29K~ zuOCJX=?{O?R;kU~+-=noqujLi)j*$!srA&WbBlZTx?`fPy*ZH0tc9Q^JWT%p%;u-J zWWIP?kD|dHuq{ZLbZNClZh%osYQFFL)jFRz!@E%_s;9S~JsH+E(K)PnFEC>FBwX%3 zJ79P`XOC^#Qts_L5k33&rImh~SN{NcsAZO8^tr`ZLwkOITUr#^2QtO(%n3PPCvo;V z>HeA%83|cz4ax$|0UwU5}!@wkOsoZxtC_)K#~917(pF@2Fd5%Korkog78{y<(a4h6RN%Y%%* z>ky{4-9r78yy|_&38er7yM1-nw& zd8hY{!EpNx-^X9Jry0lKeixV?jgjXp0<6O?3ae^%T<*~GC|DKkO1q7BBUtv@WHRp) zU&ww{UCzT7jKIPmv5W#cfVd%Gz&R(E?v5rrNlytbDQ-mp&cW^yr76`Y)7GU*I?|h% zEnBs;(F=TB_fOvX(Ml2UJP(V*(B{01r#pjGrqLNb^mZNeTHHvd`05F`mUnBQR?M}A z4FIc>_5s5Di{xHf-H#l$&&e-ENhZl@j2<FU?JJct z^^&2~1NrNu@egZag~D62>Pz`9E6#oDy1WJ(U-8A$PQQwwi;UX)%1IlNNhH*hT9SI5 zb)e=nZ32skyL+`Jr6;p(i~@ziz&G0vb?V>{yv zvyJ$F1IaJ26KsLXiE>sx6i>S%BLM#ZR*D~_`cqCa>w+$foIl8cVB4YB;re=d`y5NIVKN|fX43q4J0Jv5gUC5)mdR8ESi z$}bM*La6AoM1uFY{B&rj5my{hG*072nsC)>gmpF3QYMU*LRZ`Q*G;J4Rd`fKuKxfX z0^*TH;aaxeuAXbElnoY&>!pN2d80}}=@n8EoxURF$wO(aJxw(x-Yw2iCzLg*;W|nn zMg6-}3IdhYl&%bI0;Y;0_f;_-bhzbvjZaNdNNThZH)u}_97ZjW)sW8v=-S@FAFaXb zL+CYcWYHT--S;f^PIp{8(P!S8R|${Ll=Qg^_Z@vjMOve4LpAH95l`Y9W`z0f|g#vy=Dxm3h->4DPCN2$FT6=}k-a+zO(Uo}P zjB%j0w$@7-NdE0|_R;}mkJ3S*`jyb@(macwTHW9yw%R>W)UOlpUuSOdoVt7M=g@yf z*Se(G+8nsu+(=Ps-D+)8<~`5^wkicj$6O}<@Ufd)-FoK364@WP>X03n?b?09`zd8* zYEf-U>)JXG;jDWiJC@o~0{{t@`=$5R;XTzPjalrlZ=}G-@(HAbP0vJ84#IJyZAc=mKs>ZBDv`fm)G3HDm!Qq__Z;FE6E_ z)E13HKzARWo~QvsV`^%p?f}45o#%Gg&o)1{+t2Rqt{#bHpRaO$G}hbOY!CYkm@fSd z)cwnq`^U=$9}emt@;}PonNCi2&J~#F6TfV#+ARICZA}l*mYPUL!{&8=&a+=ven37( z=sx9`_N$8wb}hTV@;}V)nNati_~4Q$^8lu01G=mDa)$HcNu< za+U5`@MvNcsR{$Jme2~QEAc&bmF2hJQ*hWvMyswPj`~|?NPkn!Rb~QVu$hQqNb<5*;#H=jeDQ1C~?FB2gD?b z=;gAvd#YFoQW)tN-K{hQK5;%%xvf&-1`%WoMs-Vz? zB!la!3%)Ciou#gcy8Jc#RmMM*>MI|^SgvQp({N<%UE7BhL~Fa z0F|_HTmJxZd5x>@+%4T!H$alO-;h^$xJHXr$d2;EQTs`%mEAx#)kh@9HW-VJiuc=I zO)bsKBcU}bqgmW*HVur~;br7Cc)hmDt&G%Fqy&Rs@z)&2M>Ov*Z|Mu!Jc-tpP_ikh zy~(n?5^LDP@=IbGlMN!hs3A*APpKabKFi|SXmmAH_c#Ic+rOMR6Hx#X(2wy^j(`X!hzCdOUXWPRGdBCI!pbQ_=uM_R_zt zDRd6n(Om)R4!qsWIBdB2Opx>|(%#(roo|qj(7OOQc&V=S#}MK%FCyc)W+bWNvY`c) zA%3(-uE$hXS^eMRoGtE0hs7HV_Dm_G%TjHBV&Hi z_zh6R-By-Ev{Y+UjHO$yZ9Kx7Zc8pDNSkhPKTC*K#3?GM-{+}u2_blU4G2)DWLFF^ z$STv>a;M1e2eR8{K_iuXYq>7bba~(0UN8XpVU?=A#cnf_u3Hu!~%X z+&TkA`g3SfgxG<@?#EpeeMhdNM=P7DW_QJN!ezBn7O(<=F2j zDeM;0rA{`wsE(aANkH8ej6?{kLc3X@UgEnJa|v_YPinh|rnDHGtd@@jri(9~<51s3 z<~^X*b1TXJ01E9Hj4Ag^96-$K6*THn1y5bT8`oZ{_S@bg7;JKyh4ZK1Z`%lSopEe7 zxND;M{Noby5iE6U2~7}n$uNXG7MEsxWDOkfPNaHn%EWC@fqOZ)p2KDM_*Idnv9f; zS5Ep#iY>ItC7K8=H7zZBT?7!`_xrjPN!qqb`@JcoNKm2^OYD-o)MTooi8LwHrlLI& z^+{8vswHG?+)Yh2A@Wf|^yq^QRDgEU>X{4`Lm^~S{0@UkW=PPfUi%-*QtF~SXp~)P z#yzm}Ds5x}kf1@LzaNg20U(I9|68g;FrW@wsz z!Al%XWWX{UiOX*%=1<_*(&E0Q zK^r8ebdWmfO?^~pA#gPXWriJ!<9oG3zFn7&*=5OP7kkQBT(v`rw*YbEu;0|_Z8cCp zHCI8S0;oH18fsI6H?d+i45ssG!80+)Sfn=+EZK+l_w7b&WdU90B&8|Fh$N(iA4=*= zSwNt&N1xOvQ&GIKp$$C1<))WVIG~|6#Poh$3)u8twWD`yG zE2fIFR;47}5LlA|D+-oEN|J@qX-bb(1e%KaY4#Ms%37SVqG-dSzdmg}u4`bcF8 zLV}k;UsFS>98VD#>vJ4VKLu@@<>MoD>)b}tem}=THTmh{3Z8tW&VJW6dH5~&#>)gq za5>{3#uem74PdiB9wr%yAuWG-Oed!!-2L-_D3;JSSto5$*hQUbbwD6iivIvBkic9u zfR|UwPhZofjquQR=#??IXxxIREb6a6IXO{ z>hP4#W2cSP(>7;%F##aCb!va9k;5< zrhcuU>ax6lmE`_~_V&%IO-ZY2bw3S$AKZF)DxZS$=aBw5@{FN{XIY00=9tvwxpabN zPq*9|xUC(oE>u}3wNDhR)VkR_>s;%5E4GHYpo%Q4FTSyl)MLwETIo55k`^ zyyWu9+<(MtwgqYO`-?D`_spLt!7u4z1jO3Z_q!_RE@N)8nJR|40d2OR3YAUjE379W z;#kaUeREK2ri)>K<{6BXg4gxvep*tb*S5bBnjUV%^1F`}Xvg^zBWf2NazrC=@v|gu z&egt6=MoYd3R91Sdx%m4feA~??*4xuMje9`B!U}Pkl^##ob9_ z7yi!H(Qe-yfRsx8>eP^@d%m8-L%y$Yry}^r)zY-=hIT9I*oH4p?NC0NJZs(tKADW%d+jC-UU}P_E!1! zrd)ga#&r1=Q5vEg6xf5VJy5+C$5kv&Y&jXZ19cNZJw-Ie>Xj@SB9@7-Tdq+hwn|G5 zB|?X&2XUi!ME$eKdUNM+(8c6Ti~x&vZ88}nr_?w903AnfRVw|u)il{qLeijjAMDch z6$U9NRE=|!Ds`^Wl-ue3%N<*3mD^tRb}P9A{I;f|O3*8`3onCY6`K>`qqQ6u9` z3Z}O$LWngUx(Q>VIUVITw78wSO@9h#+GtR7K@?915Z%@nxR5>e#`?1^4{ya((6RLF znhHXjS89gisMJ21D+cP1w;3;^`YWRSQ>Q4SNl?5~4ltyp#M9t@BS`{@Ble zEK}b7LW#0$@pDUvQ^i2_72Bqxm+3{U#NY&Os)O$rX-jar)5SIP2h;J?X(OvwQN}yd z6kb~L7e{IyB-H&x?_F0(9Xc%>Q|UsEUC|CxB20AlxIf|n>*=Jnz0%8?na*_|o|c*@ zEd)zY;HaqEM^tNNG#mt^{yG5!oab{(BA-bVQcprYdI=iUSPVat?6=EgJR_Ewe({g6 zpK8gIHTB&rrCTLUDOxh`g`{8jQ8$7_2(q``Ej!G zrIVX2cNm$U4dyp1mA@6eL3`ZD?c&_(k`sEiw2tVmDG9H7YTCCWv=B7zH<0c-B^z8i z)h9Q=8|fLXNP7=JI;yz#Gc+(~T3i<>R0T_GcTx`HxgB>Nqgs|-G2bQ6JFdW)6rE;Zua#Fz?;M=q)|fME4eE|H&ZHFRos%Ds*(t( zK8I1JVkJW%R}0KAGhGIeb>cb9DL~F zUQ3Pki}DuFB=XGLc0tPi2y$GZ6E?9=wHEz{ahr_tWT6cu0Rg4^ttlx$X>Es?Smh%1EL;SDbm{jWfz*6Lx3$wGa`5|w*EwN$FK?jY;G+vF`Wwx8p0Adb{tXgp^74}5(_k+9UA zK%mx@{AhpWeKOv0_P*nO3+32%Em*kM!FTwI+AmmUJC7yn=3$X!G2$DNrroa-V?+|HTl4sL&9ndoMSAq=N5K47%I)`$@SATG z!sJAA1+_VU?qkH4!ZKjYX82I~UwpPC6?+b_&uT-P165O~Ao@1qM+XB<3i92AVp!cu zmjl=Pe}_}sRR|(dP_U2GI+T;sOK}7#UWp>8bV{n0P_;4MD3zybE1>D9o2rY_r^Q%4 z0`gCrJaf5Y)A*A;ICGfhLk}mnr}h@Bjisctp6A5CWBe5TM&30FmzUe>y6+Sg%#Sba$7QQ~_JGJSnuL^RuFO6LC z3#IQON`Z}!#)`^aTn1(C+l%i~0xC%xsA27Rlw5jYG)_Bw3h484%br1N=*b&P!0X_8 zSMDTt`K~^B7vqO4c)Q6f;tqVqKj;ooYTpj$bL01AN?r-dpqq<+~>HVc6YDeYP4 zb<;5UxGx$^X0%+Y{{RhlV(qgqw4b+LpMrweTy#lN3e>MbsIDPSB2&Cwl6U(A8?}CaBRqWu;T`+hp}9u0^@UFzM)f4(Qhw z<(5V%iR`@_#IJoza66&_@$3}Wrm{_SR=`I@+_aBXj;bO;PO#Ab?bpO^02yoy9oF>Xd^`kK`XMv`N+q6t`u&zXDTDuAtOg!pUHC4>Gt@%aJP&0FD1FVk*wIwhbg+v#%vfX zPMH_!mnb3BE!R?|cC?VDh*En=6$YcGzNuolYp)#E#@?d8CGj6O!6CAZ(oG-~(w)f) ztMWbv!xm0E33(wFDRAsc8Xi)Q_lY zeWp?exPWRxgFYteCW9kL2coneZx`6Hd1u5E?vee*8ofnI!i4}**VvE#YMSMq`$OvB zBm=>E1NO~w*m)BJx*Zo-N|X775wMnSr%VTcTsIdW&P)3>w=eSU*o$Bs%_18BTY&!CP> zo6FBqmGT^F;bO^QTkKf9!C6e?36ZUjMqbgB3Y%C+JNFIhb-T5?YnfM2E_Zc_dnN&u z@$pS;i5c^gl)UOK)Lbng1^aa5$cj*veMq7flvEw4KsD6sSz;{m>Ozlx+m^bI1Xsi< zG9MGle)A6Pbd=sxWXNG9f8DIIqoG!*kAP5_Xn(eW6#Bo8qD__zVRO4FY{0$6k%A3M znsN>r<4y#=WB5;zac113ONhCrEyFHQt`9r=x-Kh(J@gc|2}`IO)|9TQt+F>4QHRCT zz^gmE3mY4=Z*&W^ty+JotXCYNhhIyOB+Hi?*(KKPj>%etg~1MgPP8;iyFl(p+Pc!k zYTclgimVO5f2u=VcJ|kzUa{s@NYCB>0Dl&DwQ@^EsqxDKWO5JIfL^LdBj=>e>Y8oQ zFv~~-Xz@~~UM9VG_>M%zmB|+Dq6ZPi_uR;`2nuB6CB?Q<$Z59-X=p2_eGO?^YOgIm zKtw$n5~bqt_@g)f03f-or(@slQr8=DhxWY4Pr@>Wu?Fn#%uS;&oZ_2Xu$)rR!zyef zgt)edO8q;kMyhgHp>YrsH>lV7r!hH+Ehi=iQiRjxT|W9N0KX;tfw4`ScAJ)6MgBNS zph*4d`x$_ysvT?%w-h68nxdcEoHUG~j4eD~O*Gx_wAg!g?hOKpUMJox<>Z08t6v4s?ib@eYUd;)Dn+_wgBHJA%dKU1 z3yPXHdxIyn!!|6J0RSF@w|#Tl^I>dg)&oV^vCY4{kIv!=R?@@bwbsvVw?V#gq&|8w zhdep&oOug;&cblbns^ zSDzVHL6dX0B$yUUO4Rg4-6f{kYPQ6!QruW|ru`}bQT00BW33^yHKyk(>#1Kb+B~fu z3!HN8J}z;T$<4iSlgL>+K|lNsgu)RSL~YydxlgCyQ}NZ6#J3WU0GDqavwkj07~Bsa ze2Kj`$6Pzd6P;q@nnl7fSdO_Qu&t7*QV-wIKHCnflnW{ssZvN&xqU$7X89JeT7(f#@A@87Q7P( zb)fu4-Axc~cZPqc^OW6T2V<+wFYN8sMnQ3W;%Til`+o(R=N|t6Lt`1YuwbuB=($~g zz&UrCc)yc;h>sKd&K1W`w4Ow|rblzu=VbunMZ`Et%1Bjvs!y%}s6bc0y%~;*(!!9& z+7P_KnY+Ho6Fjai1yhu~mW5QmtK;}F05$mOKAMjAEta9%KzGubR8Hs*QKh3oX(O37}F0He0qs_Z73t8Ff#Us%)xnwx&glqq?Y984b;#>X+^lyU-b9a0NR+)tu7Ns;1uG%~GAxV;ozL;HAUu z-ZPb{#|0>+;k0N0_4=!vS2sSdlDnGif_D~pv*2;70aC~c|uvDotIW>SJ zjZP{cPW*m2io~0ffPX+JHl4pw)oikEa0=OjV=9oIb#h*H)$UemK0E5}F-C<&G`pRu zQn#Tj_exmq_Q44sJvh*!$m)cQr78I;TE3#C6aG4F-99MT16r?k4u-gwf^Io8$J)tr z@>#cn2wQQxNU!EK-?;U-!r)AY{{SSv!Fd;!%>GBq8*Vj*+f(r>pt%14yz1Oq`;_kL zQTXeYdZ3a@^gANj+78NK!mvE5+SRW$pJb5=#c@N6$8L`_w%MQ9uWe<8tN`Fi*pX49 zp722ta6y*eQ6ObBuBv6vE{Vc8@(q=jeXEX4p(bT1^r2$3r(I&Po2Hn^R<8ZR2*hnw zVk#8tiC0>h*H^_+Q9Z2|>6fXoZLu0JKOt#tlmXO}O-aYvI%$IjwNP`}SnRCbGtB@C zwwwupwSnUnNJ#)X%24Ek@o<2n{yOz{8N@Q))G+rNuaUmz^C#qTPMCH&eaA&&`GxEK z{o7taIfvZ0$%=+8vlUf0+Rs!4ZYmF@ba?A)dv=(j?0PKo@;4V#eMJ8N-AW%Ce??p0 zZN}82T2rcZXCPP6JP?CRs~t@#*G(n4fgEY_DOFO3Na$2~B1;XtN=L-#>Q!^%)n?^= zYDcb|0S?K*Ykai(eY7qPfkL9XONjuGsxPA4!(NNC^E@fxyKjikc}0kF)6*Pg-qW($ zlkFCfa#aH2B`NJGSyAm3-(Gdadykj73-9U2dZW$9j7?6)1X;KMa*Go2|K0!QjS zsiw8ja?ay^Sa76IQEAattg3*6zplIO$EF!Y<6CPdyHYh>i~C&RxIZJr%I+MAY*MWl z$#r&|ku+_lUTvW9?pmk~2l;Ew-tXibBxErfmv#%^KeXO46~(02S52c~K35+4RaT!R zIelE8%U85x8Iy8sHYK?Z&n0hm0)`wYDn~&=M_p`q=Nn~~JObY{g5^Hr^O-Xlx0XJP zVIjKsCyyHWKbK%O<=U=mP0@r$_84!yD@w=mg;u)G<#;nEJI9r4_@BD>Hcj-U^VdMF z4WX-+N5{6FPQKXHXS2_7CPaw9t%M#%HvMv0$Jd>_Du1-w4Rfn zwoehI7c;rP!MyK?os)93&yC1ffF12!ZORTl(iQa&b(9iz6snWA%Uzd?`-27Zc06o< zfn0BzaBJ*Q+`=V`{^tJxFr#i0#_smKhCSlba$GXYsZ7YA&@CYTl>@6@NZ8wMPg<^j zgvpt&n$x8_uD@~qS#hy4BdVF=iu+PD8&x+&uJ|%aLmZTl3Ead0&+bxZE5aKU3#V7v&zAU7k z(M^hg`D;yzZJg3=cXf#6x0xGTb0Y@k_^79e{PE@v61f8>}G_5&kmD{lSm@LrU`e(W*#;+L_yH+X?x;JTmV*P6IiT@nO%x$Is` z#-)IWH~8)#Tp{`f_ph>8xG|GRU~{BtNa%% z$>AiXrJsuD9HW5Yc{Dy?8!wM~swXY}TJZBLV$(ir*)AI>sZlPd1hQMNMUBD~Jx03} zZ?NQ$Z*3bQ^TVXf@SE>V?`_i15ZR@3c7gD%e$&NVYXc2{?>WfB15NT6i=B0~I$!_yl7%l~AuZTNk~mVQoRAzZdSR6 zP_{+wc+jC-LlT=*c>p+t4eP$AS>HM{R+=h&mM%HiTJ2Rd@(;xvOOv2WkRc>Vp;Jpr zKu|QU#8X}4+uwNZEaVM)s@3CvdOvK|8Hmh{Z5}!hxEs#?CS>?D>8NtrsSuJ?*zih? z6H`joy7t!|<`#K4foTfd(vJXt8CnC7PDOazioNzmoF)d13 zhl<_j*e_W;c2aEi1;<6!L3xy^waQm(K`p6aYwkbkNvEwfA&_ouYta+#z>{SKe9N6ook_>3KCZNa_VhI)!z9TPTi)RBw(m0g}3dN+!ks0F-jO_Zne5 zKXQ>7cuoO6<8sCtO}-;jT9I`%kR6uN67$F;)FH(XlhdH2sF>!Op6Z#7Bovni9d7va z8x|98xMg_+mP3-_wH0o4APm1Uj=au}y`(Ak~3i-txN5{T6auz3Lm0-v*+XdmA;&w-AxIT#tyMLOV z(&M#gE;^7BQ&j`{jrhUtw)RRgbO?3n8Y0-4=?D>v(ZEfZJ%N^x!F!L@D=DtC*$;Y_P z=Z!=b*f)D^&N5Xx*zBoMWcIq^(3%3Im4Q?piPmS_ON*czM(%4?yV{#<&QdP zr`uoTzNz`U!`PgM;}4HKNzd1dhcaI{e%z*DV3^gp4@k*j#gE%;g){c(TaX=EP})@a za5hvAaiKKfSlsQ+q=dy*)p{Q@!tC$Z(Q!!{&lSMikK2cbk>v&<$(;WH!q@K`yav0W z+fH?iD(KAXovS;PvcnCkRlWHEh}EEStNIC`6t24CeayLiqhW}bOghvqD1D&Ew;?3+ zw`IAY1R4SOec#bjZ!r9K`&4+}$398m*gurnKPq`aiDV4P@!{KhDss#%FR;tYx6E87 zziw25rx3SXAxWZ0>#ZIWGnu+`Os(3$sB@SOIcskATf2OujdePF`d9N@9KYW#m$_?~ zce~5IN$pE$nKo1M-VLKV46OO0`^MQh`T{Gjdwcw-fE=$m{VOH?@`#O&bNaKKEi&t$(u5>ORU>!uClZIfsB zDyfCsy3qPZPKWVTi;*C9+|_Ym9gN&Rs0BfxCx7wQcaYkDlX&jBhX}}%2c+YxbWBLn zgG&IP!&w)wbzKy6Qm!#IGb251e{@PgK_7^wqDsn?sHD+5c>c>i^>(>pO+ainDinsA zX{3Y|xY|mRrF=>F=@zdna)*Bg`2HbN>$+XH-K4g(QONEo3qkZ!6jDL{8k0Ris#ij%MXsXsTo2sJqwKg8%8LSTZL^xscaRVQvSauMXY1@l;!6qcSzJ8#hc039~) zr&~#-3ZCpX<>g#4$T-sf0I6}~NetX=TW%pps$3xYfI-zOru45`2S+WVBNv#rf851h zEm399l9*81G9Pq=w#pJzr6}z{2VH8eE*i$vu6t);EY8}|Qm%>))`Ahl7T zCqXH_R^qk!Xu=I!r>N4ArQ2NPMtw9@P@_c;N@>VsAg1PwJS9u*ye5*E`jD^GN6$}P zw9pWmfz;5Z>rO2S9g6KEp&EkM5ur{wwM)!TW%Pz-r@EU5Jw}}lQKf|S3mt~GL2;k! zvU(_bYCxb0D^9wsVnSQ$XsYfGk984B%BNHd@Qe@w`$}oh>FDL6qPxcLDBP^I)g?(w zgzZhVw@g$^d=LdRUs8_!Kg&#q8msGj1~e@jkQ69)udanukjf(O6}AeHXb;0t zUI8jhGF5KGw@{Ft`lg)@`ZE{-PYZP|DnT{ebkTN8qAXNTQ=}lg&_umMi$aM(-87)k zQxV|P6G0m_Bl!vFtpJ*lox(|y!?Bmx;z(M05%AK1O@~!W9C6H7JS7iVE|%E&W)F5y z7yLePCOhdTb+VyS%fqTxpI*9;aR?%+!rJ}K(DYK3+lVZglqq?40o~uqtj!?5b>qp3 zK*_AhCbTW5{{W8J8-VI6q6YoOr%;27daFlTHc?ip{;#2GZSTh(NFZ^CuA;<%(Ol?Ae-sYxkN+z>$o5%JQSu7wm&b9D+#XEC$oNNJ44 zg4NQ24P`M!^~CYH#K));ONa*J1Mgz$arU- z8p`vZ+JEe0o-U5LaJs>_w3hJ8UOMFdUj|4M?(N=wT{uTvvy4POI8dkm?janFgcQnz*(G zM*5H%)2Lb8z9F!kleU%ap}Y4V-|PrP_}+$r}(+E0+mXVMC;Y6`yzFu*w)wd+XZ*uXB zj>xk0mi-1?h!+@75!zK2Tt&9)N+6I*6e>15YAmruD|WfH1uD|!SY8~{LKkSb-|qyT zR>3S>756XUD4t|-g`uMdALPc@7R$+a`z|Cm?#H?{?50A}or22RSfDjgsn$krF?nM1 zEsTYv_O739ntp3&IUSYz8x}$!@H^9|YX1Nl{uEzIo)U8AFPm|N{x90!%dBgi9*>R2 zvf4a5=Zj=peocvw$wo>z(n_WXrM$EXYDzswJ}_mbW%@*X{x^5+m2`qUa)yG1FmJ8RVZSDfSWQ8Ys!E*+2juN-o|1(U;>4Dr@CYq_+hpAbJj z{wbNu-Z=Q>mGKr`(75*qcX$@NSA_S7?p!$$h^4e9eeF`Cx?48nES01BYN9Ju z_k9B5*82F}`cgKn>_^+}@k|R`d-3g^k_NuIgHFSyo&NwHqO@G{W?dGZ^Gba+8-Y(# ztrLl=p?o?ev^pJ4P(r(QSsxuf!&L#zqI%$5m&hDRk;lzA&nc6HkTzOqq3A}U-@wTkwc?55PwDQUON0C;^ z%v;6U`EQeY$}Wvan%WfXtIUU1w_`?SN7`-5_Mi|qq1Q3WTjXqHrK4)Xx>hpl6_AX5 zW9cu)tt+S9_ByKz%3o@1KKq(n^4}l162r=#IA%ENp?LRh!tZvu+>4cGkzu9(0NTB7 zxt7{)E2J;nd)%lA6iBT@mfqn#fHqc8^gqo101wR^zSvt5WP(!dGy|dV{(qOnaMK{d zvR*#B-!V+j9GQ(}xWuq>)fZp#%^}AnSbv)N@C@hM{Ab zXEYLQkZo}4m2r37x+>*uSnO1$ON=x>N4UtY-wuaZ4tHx-Zws({e}73mGLqd|nI#|m z&bfmE>~abeR!;Gk^B?Te2&!JCIoAEZcM2p_WLT1zVwsv zNu??4uPxm1d9t~-vLUE-SGuivC8jTqI$M$hHlDvFRXq3eYcTTjCAOyX8rNsAV$M7L zirZ+8V#g|T>2>!KYgM{MPr&QyHw5Euv9kf+s)2kp_eYXPf022Q^lmg%+Rqz3#NkA~ zqS-DMKX*v>FK$G1KzSX(D+!RM{k0UI)B>so@V3Wzz_gwhe%@ahMNC&@f#dnVY z#(4fO246V;0PYFQ6FAP}k6Yo~JXNz=n2(TNWm+XPBus6Dv|meYm82mFRXq(Ut}l_E zR$o@xr*C!J@hod>c3!w{=-qo!L|Mm(SFas8zlQUSPAcwvLz{nQ%XrrLaJa>fl)q|9 znw0WxHs<>S4xrYE6{z$FP&rOf*Z7%ZaO_sk+x#OxNYxY}Zi)yE@Y20San zf?QRaq=ZOiD+%A@uNM1I_crnDJ&O7X?}fP-*yQ*H^ItW^y)n|N5>$8BsYL(_#TOu# z6iL?2MF#$-P6Wq9TAETiApO7hv`A4ntYWK9-7BcFomIuGYExpODM?n|zMhrTc;8nA zZRq(5y8biqrQHhvzM7k=XGQzpm#~l*s~dyOTJrbw!DmX3X&AMx5OUvsAZwvq7YOK0N#T@+*{iZ zF`+5gak5-HL0Tt;qlbB7^JTGL?c8&8osMKTsF>Z8B1C#Ymyl0%FvYzutcJvexS)`N zM*A9SHQyn$i$^+?6-l<^w{!WUZE9)vrw(hvxz`Kw7yi!q!ylE$c?G@LYRd7)T#CDl zI1)l!FQF{B>J)vlRFU1kO*sxtw=fwY)F_!OOqQ1(mB89T`Tqb4r+wY-Ik#CeX6!jE ztlCyBDX;yUy!ulHJ=V$*+@(C10b5U??b%KG>6k^y8@o|4aBRQv>sr#Z?4(8_`00#h zSUtvco0mKBjqb|T(k`#cq5VKO* zDf&G}!&Ys$iQZyXeB*8-Jv1FuosHqRrQ?eqMn!6$YM+>1qKi$~1lGp`-wA!z04LoB zr|CU9>bmPS9kY#G6~(3+Tg{=Z@7Z-{#V@yaILOLv)3~FLE;$<(XCq1DzHXYsR@K8w zYT-X`tzPomRwvsvIbRpmlwk$w*k@`sso*U z>-<~dn6j`x3pjb~rO2yxJRE{sM&D{dNk8z5p-!v7{I<-zlgW6=Sq0mdFU;JG z8y)ThXWwG^5yF>ZNQoXQ2ncnzQhP#vlTb|sbpTx3Lmggx!C1QuaV(T&cCOCw{{Y#y zkN5)>$N8J?RmsiqZMx=^zi8mAOURR&UR@)*_R9^I10i3`8!AxsQgzF@f4XtPJt(zt z=}y0b>)a#TH{7^5>IZY6A2a#|M{d>meZK|8&VT2g zOTRyjz>8wJaUUDx=u3@FSGX?sr(PtNX5196=N6$tq!sk*uSsz~ZEbNdfx zpp{S_?v>Wk2`1e`pj_W6VQ$uf!&13TY~@}?v0?dl9$50}wwxP1_J8YqRfVMx?%%in z0E7FKl#;H`N)cX$qis%eSaqdJg9MVaZLWn!N4)H6Y3Zj!suOOGg*faOP3IzTsfG(t z8{PQ}`cQy3uJqBeDk#(6F@xfti#XOAg4L%3rJHCX zG(}ViI~r&sTWFA8Ir;{TB?MdPzJg)$EvTTM_q7>CbE<*RNnUiOq&3w>y($AoL_w

    6VWF~l&K2#$I{X|P<7OO)ha_G0WI1h0DpAg z9fNUQPqK&&N`-ede4%C9;9mZkPQdL?g*5fm#*|67f|{oP0O|dE{6F^%SC>^zTBLQ_ zpPh7)jR)?0wHBz?fm`<1Z^KWu^+JnxJv6$aK&aExNhKHvEg>y#5C|Kc^sp0!$ir*j zvYkD2Dw{@7uG3Mm^wQf}qAjXhjdFN5`*kbaO217{>HIZk1Wn^r8Z?a&Gc z?J0(P7NJjyf!1;e&d_{TvR@&mf`u-U%(Qxss%twvJNjs0}X{XIuPbD&l z?%yrAb+v!|3&fa2WFbGKtRMv)z6Wh}Uvjeze9eKMp@NHqdntl@ZC`mO>zIPgNd+=5(?e8eDrSxuft> z#%;yfuN+Lb#LR4qw65W;4gvc`eMiV!r46lam=B zrQXGKR>16cJ#_%#t^HFtXy4Mk(VV@{qAhoyO10ZvEAmzY!YuE{d@q~rm%k zit7=J>={ua&2hwbKKmdVqyR-}w_Rd+_9p)3$hOtnUbrUvt6<{xD=NXr9{8WW!GXn?@-%{YM$6X>N{(& z-`;v6wA{R-j={rp>PYb!@jVrq;*5TG67#MBWaWHqe1U}DL3yZlrHKi+$68Oc>|%dd z3IeGb$a3q3&xv%5cE<(My|A#7%acb1!wjrFC@Ki?3$*f_L_ZhOI<$eT7=*h5H#Hv^`Nk{&=@ zxY`Y~2)kl!Tx^-$;wnyci%kgs0N`Af(g8vifNJQbv&)n2 z75@Oic$xyQ$MV})jQ$YRybg=8@LpyM1X4PoPtilrBgVSfpHzF2ibR;{Kd4jn3Tp`R z$5cIb_**12d?(|gmoffUW@#2Ix*ALsr?pQM*SMd`nxwqJxr2NqZE-yPvNzDXZk{RA zZ^G;?TW$_o7Tw1ixhX~KYosYH_jLdo_0=!?dzQEzL2q??CmOVTBsPy@{{SV;?d;mKHQJs*Tz=7jqE=j^Cd96>Z@8uQRylWww5^+jISxm36sn@7vO9;!)M~FI zinh3B*%}BdO<{K=58T39d`63|z9@a%`9F&$MY8hTF`VPdaS4-qW{lh$MMYGk3L)Q} zD1Jv?c=reP^Jje1CAXFS3(@}D{^l~cN47hf%MWwh59j$Wrd|s2lZ5<{#-ZMDJgP11 zN^L*9wUU&|VM-M!b(Q@|AoT=m#vF@?<#3ah$kG7rzKie=bnu*t+&mwjvfC1sNH>jBTwLYYZ)MH?FFjKlyH|#33ao!zu>42 zL(BY?$5*A|9z@~Dy4fXnw$=2sm9Zr~2|~#2sUDqm+2G>#*U|de_tE28FE?KMiFt^a z+h(EN5z}&rd4t7RSq2vOGx)`my!`m7T&dV7a0gP99_1M72DMN659O@~8t0FSacPrf zI|8sC^t49!m7t?<|E%7rZ@5*b=mLq#YBS8j?L z>RF#>EN@};VIy5vnt0dl%P)w!`g_(NDWL~K73sb5cMy3+ggtHwLN4-KKetIVkjubq zLXXc}iqDSZFqb=pc7nYrbJYhulEKV)S1w$4l_J>mcM`jG*|MaiX$q=T z6q*CmeR>YU;?pl}wcrxGEuXW=OHXv`Q7Kz)B|^1M{$`%* zINLZbU0BY^2V{YJwmT`enDz@(*{`_~TPV|WO7+#gI`N^vgQB9&;-ry;Jb8kK(Q=n$ zon&-Itnb0ol z$GI?^163tKy;Oi*3O<8sQ&>5yT*9}oT`~Hl==U@_3P}_@cAe{}^mBSCBWr?TXS`{T z=XSXeHe`be!4UBUTlcg1<_)r5kEfG%6leXOv{!r*(#7Ym*@4y}D_ z4W$N~`WkE6{1@IUd-y{nIcabf}Gnlr>Ggp&3W&8hJ-XQTmgk1rbuM$B?4jFdQEPg@oGdE5l!(B7R{N z4u5vOJ3Y?uXSlQ4q_Cf%ppYmvHL`}bily-Rs!dZ3avgC>QD1RBr2hcprH2&AWjd`A zdx{H$5RKALr^kI5CaCBFe#*hKI2iS}E}x4{gNxoAEn08xwb>R!iAhmQY9^%6SEW80 zuw}ccRZ4PDIom^lBeF*TRirqg0!Z&ocL}ca`O~J9MG-mmX(Os}=4|1S@@t%aJ-A%1 z*n35o5-W=XPZR)<0ZZzFWZ6A;DrBB1B&ZVmh$uil{5Qs^J_Ej{oYH@9t zke4Cnc4?)#^rZ+Y2^*RURO_&kB5v0jkhvI}bLzpTRF+u(g)9}v^0Q5ps269khqEf2jloAa;-7ekfyE9$8tD7 zxKCn@J3ggXYKjfbbuQm+b)t&tr1^vrphGUTEB5FzJ4j|pNG^8PvFbn z>x%N%C}6gHW;Pvoa}-`HbAIEGxhW{~E_c0&%yp7X(n zVM$aeFJG45^^de5pkJ&TmETNJLuSNQ2JXhEccrS@{i@Z8?df49(<*3y)p zMI~QJ1b%vs8Uw<9=1Q?wE=*uIm|xf1?TJ!ceJK>!zM}C0pZT>i?uhX(I}@LX?y6?$ z{k6qaiR`Vn5nYW&)z*`UT~X#U+$?vvb|LTyM^>7slP87y3+2WyZ*R2oouG7~R@&$J zyMIpM!{WMEAG-Ce81I5aWkC)PUX&WCAzgxsNFUVi1Fu~qw?zCePYX=6J&#?j?tY`# z<7xG(Hq~)%s^S?ug)`389eUSFRc=0I>KrIiPfB|F=~SXvD%<$iIX7(5b~y6F+s~|5 zOJvs0{m!=>y4UBiq16^@FVf-E6~5t<1L`pJb5I^0U96bzKiI9=9400rTI5?|e&q>U zP0sH2+ffRj2e~8ybt5-!=`E@gd>ZAJ`7Ms_tv4y(uQy2?hiA#zTQWqk8q%e1q?VRa z0#Qnl(v;R?7S=VJk7cIh(%f86uxJ!vo^Wp`@T?pcly8ttf$OnE+Z)SqDU{mSA=cEk zl6%ky75aeH%sCcaGidCyS2(#Wq!KwrUQ%PWi`~}6&A5zkn;#`Gm$Ft7PS{u0(i0^H zojt-khs4)bxS5SMyQo>-OJW-FD|$Qls*Fu&E3oOSnH#Fj1+JM(UQzKrhNj!T zx)lQAGt?&h*sF^pwL|IjI*ltRYMqWi;D~9I;y)7P^kYPlP=8V@vC^38rsaa6EA~RU zBq_BBvQO$oMG5?MC5@_stA0r;OnEUK33MoWrBvAIjkHgPHOs)Njg3Lnl+g<`XqL%W zZh{Ekx(FlV^Uy*uq9(GW5~07#NN`i*61_Q`s@V^T_NlQDG*#eheE8pZc5|MUTqM0eiCs^#lRBBF505E z-umf8i=;Nd4=3W|bXVc3=i5F#Lb!@}?mVy1rXGF8IaeRI9_c9z-tjAtOO{O9+UjLm z<~FlL(bQD7v+CP`5Ox*orz5%!H13kFkV17A9!*EmWn;ZPhJ6xu?JcE|Tuse_Dc=Ucp#upsMhMSU?JVQwR z3WX!NN@%C5QYb!J%1elE>$i5tE!Br5muAa`hquKRe~&Rli#7LxIVs04l9=wlU>9}_ zDN$tsMKs&0t52a_2G#lL__HRPF5}n!;FRnOUH)4+I{yG^P{uPf* zluTuXM`h3n{-I@GO2WU?gTAcoTO)k%yL)^UE=7Qimr?9@jT0X0h|hx0?;yP0>ehEe z=*PSb{{WposVC#9mid599yXPhz2oJhbZ2u?3R2s^HtS%4w`t#fT1PhFRi1kbO$bZ) z<>LK{UTwGhZs#J^cCsuvX1m3G$nxR4{uB&n0>2hv6AZ^c`pB+cr<*Jd$eWl}^j@+9@?RnQt4SakF`RJ&x zn7{VZ`76ct6=(h#WYIju#pw`z;3gC$?$HwtKf7@%J3Y;|pddd&DKrxLx@g8k}yl)zWaaKP2e*RReKanB2DAb?p6HhsXIUp0XLye$;CTeBQ#!z=qT_~T z*5^!F*4fDv_t|kE?m;P7AcZ89s1Qif<+F<6w0Njhrde&@Se#mP=zKaZl3uyr%3r$I zGhkS!FXi}qx&4k5wrzRrsBrNV+*w&|#~i!LV66|lpKJsuAv;%Yr$XKcEF+@^g-gG= zlKMuzLq{}t_577jz2mp87v}jGT*bw|+;Q8xFm6WIB^M}iLf%74{kyHuI)w%OBx#JP zZ1%1n1!aS~FkUsor9yLg0mirP8|AFejxrc(gLKB_Ar8e~>PvECw>6{rw(@ntqj}s% zg7k@#b3stlm+KK}>UAO)RW$a{DE?l~EAi1N&215CQ%p*w)~Lc%2&H!&bmDR@sJJ_w zGb3n{Q>PP(^t-$!(^YpYeEoGpm6S3PwS007_XFm-GnD68t}}t-{C$+jn#&hNCQZeg za*!pK(m$pEUfrbi)~nb)D#>J$@$J{L&;INAc%68;rF?6T-=thsjk_J9^h=_On~;=9-9aTp)fMfo`f-+w5_*HFRj$fhm<7AP zLr$L!bwFVeusDvJwpqzn3|{AIU?RZ$Fm4lBQ%0y^CH~~0y82R-CYpnC+8P0&SGKN= zu~fdU9~FJLkH`#n%6?G4@@JlGUFTt#&6+KvjPo2WBX;|*_X}C}*=563G&|a*RYARe zI?83ROnhcINZs9S_}?kGWR05S9iZq(Pl8zcm2w@ombi}!zFDC?8rryRw%ZSu*7!tV zTWq?f_U|blRXug#oDLz_?m;sgDdt~FxlUjGw-kmzYohtbeLb=rNgIW0(5&dZtuXT- z7O&=~<)tQ(Z3HN@Z52?Wc32bFZ8JYbcVN>*{{U%fL?xj^xilq7{06G`?QKU@q7l?k z_g__BDf34Q_{H|=;%haYYk&Jy&FySmAI2lZ?Y!NtD|E|QQFXFQ2d8=o6ch(slFtE? z&gDxVQ5|~tCwUhl$8p?ok+d=%-OA*bm%POn$tkY$wcm~2#dGZP@YRm%DkRtDhl~cj zhT}$oRVfPGordZsUew^YreW>92RxS3_^+S-;qqIKM|9hf9VGsh(K31C1LbBU;r9!N z1LJ&S4mFjfvlA3oy5xw!r2<9Ou=B;KJ;I6#p{Y9FVB~CMkm5nEbm;u$D;#Y9ZH8Ib|)4utLk!cW!t>rm)}j^eT{c-yt4kbt(`ZWdWB+^ttR z7MYDGsx;`YIUU5Pf}NDv1Z-)h?kysIt^lc#+)B+FNb%JkUR#xaVn#V1ExEOiw=p+1 z>`0$25L0q(e&S`Ngg6@#60O6acLVFFGh*$Z)u8>#UA|K0Goy$V@hf|+=KVS6ubFMm zZQ}DVA;ny75+0Q)_QF6(Q<4;=s)J!u(^eLYjuub~D!ZxOcQ0MtiTI@EZuU3I5R##A zRcbf(%1*|+>_(+R_|AbhEmam-Et#h-uJwyqN?x?k5Gqy(WsA(mB7L?nvp9k&aKo@w1*g4chwV@B9o zN%36ir%f%dx3)Bm zx~VO+xSv@G0U2w^yh9A}uI0m+RzC(-MgIU+F70CE=i{MQMB3htSyW{T5k;t#4~ONc zg5~l!6QE8_hYe*KDpxf!ybqaqF8LbI7U?ek02=J-zh`SuTgz~%Vu)?Ff{KkgP-@EO zJz;AW>D~ZPEqInf=0GE1U6SBWvj;75g|^qro>=DW@Mj+~%WJvcvO9~CvO9wL-XwIZ8ql-$3NWlqMhupaB8!3Gl^<0S760Dh6v z?>+0`p!u$2^Zx*nn4QYYlHEBcl5Jcolw^ve%(FC{_3to|B_p`1EMDa;rU*L#l%#F~ zb0q`f9JYm83&&eFGDYR!iPs}Q1%s-HA z^Sj)X^Nk14)2up5&{mq1kVy12*P*y(Ru@6Btk~eZ+0AS&&Cja$?`xMlc}L5RY2_|B z#rTgc${M<07)-M>6t;*=wI(~IlEDj-kEz7=oq-#5-&z@^S*AxtSDah27N;CFKA$~7 zcB;j#qCW=9KIZaRhaGP?t&`fHj*Yq`OCxBcSU81?8ELthEU_YOJCkcc1e4Z;>B>-5 zS4lhHqf$NhIGH^L6b6O1+TBA@S{`ZW>j#VfOF zR=NoWI-m}T&8kgThO{*6>81)&#P@$Ti8AUT+F{34vVNacYD*euh`NaC-HilO^}2o( z+igBbYCvnAkzbJG>Z+Bupwu}5DqL&;BX;PfONkWv6S|MbPTR-1HFGY|mF`m#(_%AZ z06rR=`8J9ucG2!NDY`*bYOT(pJ@r;}S}&l|Q)|#WMKlmt2S5g+qe{|WgsXcDe$+KF zg%YU)s?w?*{{Rn7Ni?F35O(VJNItrXr6AT%aUTsnQ_X0i(?py@GE}9Zq@cL9kPv~n zB$`u2B!P{oNc2nG0P0ss{Aut5A?Mp(Lu83{RkxMyHg|5^SA~w!<>gU+wkOA-UaZnm zQ6eOk71-lW0Lef$za-FwB#m`TIhi~{MjZ;_w{{X+$YYRE!>UC}lnAA2I%-r!H0bKC z7l}Cw7EGHo$tPPIoN+q@`7vayZ5~677Mow=*HE*v7qzSkW){t?d!O)Cq|bJj8-v8V zB3jm>;p$f6ipl=~9X!VQK=4VLN|IA`vPi1B`f5{6ReRf1b{Y$km`9;d?jI4RZt5DJ zgY_kQyOop>4N{74qO}Hy@8433#~`Sh>@_V`c0o$2j-&&kZG%;1{AIUCi*m`cIba5H zcxzmBD1Yjx5(xY?)cvcq0rFfsqXvtDd+~nhV}{9{WpJ;_LK!{j`|bAYa*(p)lO9^s zqyF->i_6|Rdy?bgx>gGq+gnCP(Lm4wkU0MUj`AFq4D0M$Q_IJ!-~IQ!hR^Nes+9>^ z`T~6hgi|$*B|i2UAG9f^-##`S#+O^uYNgKX;^SKvzRiA;ch*hRo_hE#V_Ed}+k2H* zkhMsEy=JAuQhjPob)ku?Hz27`>m&u-&+!T321N78f|^7Zk`^x<_qjsXYSvGVzDPaeDBZop8MG z0dCpLyrFA$ZY4T&cGWU7v0E?14Jhu7(vz{=_1B8|x4cqE%VL#|{{Z8Iw9}#SUq$`2 z{kt|#ZzqOmK)!G<1d5T}zlNO(`MtZ_ZWfD#+hxWw;aqL6J09T$#P+z$>@2q877s#| z0uH^W4a+BqvNKTOQTMN(vO?&jF}zXDBoW|7gnh|0TiKpchT0|NY!Z=GD4m4>_0r!; zRG{0J##gTKo@XVg(k=Lv&S16xtCAaoB}-u*idsS{2hN(LIm~@P^;=(WOjRirQ3^?_f~&Ur^S(#k9PO-LjDnI5rF$od ze%v^oPEV?d<l^s8M)5m zB*<}hg8((}cebRYwzmQeOM4%lsGIKXjM)#RX{NNQ-d%p$GdB2BeJB;BXb!agziO^j zJ2bs8Bp@IHC_-spjcKm8QhF~wM{Tw>*#0N`WMo{$!hCJc7=I;1y4r7%oH>Rd+(0JV zJ*23R72HDi6d-}N_16&2d$j9pi>)M%lr#sj^j{YJvVXI~N0E+twawh-_Xd@vYun_m z&x2gIhaSJt}&G zuS#3l+*>>VYQx!l^}EVhWFUX4DK}%{qxczYm>AT|ao3a)F@5i}Q&8C^r?$N->#KKI zFh6({D+tW2jfc@Dt*Ufm98-GaD^jj_x(~G&C`f(6RNs0xBk9~%(^<>@RUM?eCDXxe zu)f#aWo=ExwzTRKCgo+#F$rsjaD?k&rPa9&f~M%5yMY?Kx5(Puvr-LKLi>$mEF`IG znnJMLKgn0Q?taQ+i<|o|(C2MMeFCZYYXiz~R>8a5p)0g|Tg&og+a;-J@m4`MIQR5E zfB4p9Y7*Ywn!>UZ6MR>A7tvk$ZDW?xQc6^w&ph zt?6Ft&K~f4K>S?4S7Hm`cTflE{{V%`3xf_a;)0S#!)>+Si!`d@)^fGMAqfgX=~4*w z)IFxE)WQf-ry27OYsI+)T#E|Hrejxli%OfD5#_W`Y3Q`1_N;uA4x#3-c6adlbFerR zEH1Bc^9k)9V|egAets+HC&jP4^rCH;zdKtH<76eHnZz{M04noa{K=-gfzN+#40h>k z&)mI_?WgZL>e#m5D$P1?9X|f!^ZBmUvypJGm7I3uX!D?E*rn0Bsm5GFq(@mBC;)q# zT6O1(X>z=B?ydv?SF>cFj`KWsl(pf*#4b2P zF*vix8(T^*O+#M9E8>znid`eLcPO2dcL!RW1bGfOA!LNfZtkB2&N-e(j{A)Ok`UW_ z<_&cHN5i#MG`GmkIBzRnIgrDVk6*10goLP&2uikE7B*TH=tjE))|Od2ebJERe@1VZ=P)IV%g2uMY~5uEs}~s0d%0>{{VZgKX;9r6DIhDLF&Ch zVUw}t7XFm(0pMt>mUG3KpBPVYO@`##0$2Y4C3Rr;Ad2m>IvQ)B;`vRr({`N?a914U zKHgtrr6dAb*o9&IZ}Q_T;!A{BPh1?mVvj8*MZLnMDUnbK1fHKQVL8{fR=FsQkqSL^ zk7xbw$zU!7QniXXEpc7G z=M{0CZB#EwjO4P_j6fF5@BKQGPrFUG5~oLnU5^@;Y@Td)}QI3w;D@G!R*!f*G!d z@zE69*SeIFvq1&Ep^;QbR(c=DQvwb{Xwp}qKma=E(`5)2pN^l1CS?d@8twi%Z@O!3 zQiCpTDmD}V>T?}b)6EjU?Hy_|Wi7QOw+#@w8Xv&v@1uU&(?wKn<8$_dswNakN6xxu zXT?Z!s+DbW-GMqR)(8*7aSpJekf0XYg3@TJgShB*Dj}+^os|h{=aFA;QXP|ZyU3cS z7Nw>--f+iksFHoQd%d)E{#1=k6Bm3G52s-02jTqD6g-o;-DlM4ZGCk>vs9}JYzCW@ zCO}lTTtQ4#2Nc+C%8#gkKA}%dNse+yMHc4K#OP>)WqtnuP!tM1DbrHz&YGxFvYI7O z(@RQ4dzb2cH8LP7p>82|gY5D5KgPb-nU6MR`5yx-ndEY%o&~nWHLH>!NVLheyz}YV z)Re84i{I3x6rRya*KGGI;?m^W-HzHtRlgnOf9q#)4GyUOAN5XuH~#fJ-RAYx`#yC7 z*NyRQ!M2qbjZp=u(Ct$a&_dp6_nS-0TBr|kr4K=*aQ+DfB4aTn(w_eS@TqynC&=dQ z%vv#qhMy7Of8i-+{{V3R00HB8r7Mki&%-;N>z*;_%8A0)th!`r_|`1@t|94H5&LI@ z@4qS3mlhP752XXOH9C3RdhzqRSsz;VPgq=8MQ+G_ItGwQ(4M-|z5adsr7ltB&$8A> z!BC&Yu^vdDR=Ws$B1a{JzgNnA#w&!l(A|EAb zm>4%%?(~nWdxQDXs+QqCa<7gD7Ra%2g_h-&@U7tz@r=TBWV&xQaMIXv+jV47FC|G? zqm}Llef@8*us*pYxVEb$kGql@Xb2y{OqgHXKY-ZH`6~0tz7k{E77v|Al_ML&ZS#GH zG2EI%YeBfjee?pRlvD|0V44yuS_s#83HG;ShM`$w@=(U-?v1oMkDuVGXCHZm&b(1= z&+Oc9m^Y7EIab~Mt3R{;!W5YEp?4|H#HM3z7N}NerpPosb{e_=09PA;-BLo%TTeVC z+f6rh>HR;27m)n5<*5EVUp!k%Z4!8r{dXksmv@VEicnMgY2iU=Xk(EvndYzDs9r#>G5A z?G@L@uWEEoo^JcRd0Eal?c>Mn7XI%S`1@_SPj8Q`*0$ekGJg{7aky4{#o9COE?ZIB zRR~BO1yrgPDeMOWwt`<)UptfB&!1jhL$(rH!`K>kH1O?B4|@2~RZkJE{v+gkxh7wk zna$&gxHA)isZgKA?XFxe_q)??r}psMLrlHc*2Qitr+NWeS5`ct=5rk(Z8cf$CyulA zKlx8Cs5KpipKp?oe6sS3I_54{x1ZFdT7~(Qj}YoU3_PA$8=ztRyx+9)^dv!76eWpWMTcV?H6{9AF&aD83qD z?_ykkamQ{CqGaXG4GGs7E_OHlX+FwIl0v9zYEG45#clUIzsu{_xz6QlV0x`9scC?=KlbGec3n5+@}=uxsskyTaP^I$x_y}8mUCo>qQR8hSmxI zvgNOFtBEJ#YMRS%tLaE8S@Zy%hhCZ|PgOCjqDxwowI0OOe03PfQ=*>G0M#eH!pg?q zP}5Is3YQe3h_i?RNkCU|Jx-8Ys(Fsms?zw=;~o3Ryq4i@{{X9C@jew~gECF?m#^$e zMmG*zc7u#Tc2YZA5~Pzxn=1s+ni^`p_aN9_7&US1oVK?080j6O8}{n9$nZWm#<~Ybzl-s-2dZ}KXgHt&Cg_l7?<**0YIVSQ5PyJ7DS`9;rB#)k}xmLGit3Ddz^eqK3^_yCy zqRzk{T{QDRoNJN$W5tsFH||1`N8`4bQ=26MOIPYw;&hibsV-i1&~s{(3Y$E6xIF`y za93JUO22lbf)7Bp7K8qJt;$D830ht+F&97%>5-8cRxUuj%!u)gHraQPA}iyn9cpTx zLbj(u0oApuM7H!k3bOsqx4R^D2CJzj$SL^tIfHp^XvZ!0%$n|rHUQ1SD{}4duMD&8 zt>s5m0dRzsB}Z5&EE;Ap9)WcG+^H6=xM=efbAtHSKl1KwRXZ-oglm1xr7tSvE7R*l z3wrlbdp*Jslf42F&}pm7sBE1^3Y6|66FyCa^BOnep*yD@D~|4&=Z`ss>OMh>z%TW#&~=K(GK%uB_}Yy-Gi&R(COrY)vr}QP{3`Uab?XH*1ZS=E-7pvDGsn zvJdJ(rA2)YQLT7>=GAj~t;-!kk=Oc;n@C#dAyv0}v=J|R{b)22sH8kUjdGmOIVuIU z#;oyonRr@AVX!wpS3-3P>n#*#Z*`-_@tcX8>U3|4F_J~LLv>cWBR{%KEu>3IYE(en z?dhlOTwE$jiIItoG;XPqQ%<6XPnAr9O$Ad=)9av(7-7{k;uH-idg)DZ3C9YA@K5w>hP*0X=AE7CWFnXmlWED={jR>s==nBs688*L;N3k)l-LPl~JxAbl`IE&$Ho~s1ScON07HiRJ3YpO|c4l=Ua zt(MpDI+WK@IAf?LW5Dh<3OO^h_c%??r+zsbs4c$KF8^* z#5$dldXA$@EdV1GLw8qk(@c6{E{I=r{05w&EvrJZs#nuVb1IIA){QUKMkC64GOF47 zPwQ;%6h3+h4RTXdNOwf{L_fBdPpUDc3I70e=)p=YAs{^&`0f_nK|_e@DMO_=)FcC{ zk}wWh%eeK~5$&(HKFK%~-LZMC30BF7Udj%zy{bX|Nus`bu9nrV`KxP8qnV-9DMkD< zjk#%$-*JqQgJz2re~c%-lMYjYq?=^72&GR(oaj1JQQJJx8aPxdC&6!UD$Ngqfv%RT zzB_T1XTIBBwA|pUwpG4EN@>=}ByN>@8f-?#(CT9+Yq}~+i$}9Ufo=ZfCWlTJBnb;s zn$?outfRKwa&|Hf>SZVBQU3sWCZPWS4MvvHjy?)~rTmQfDI&|sR#{Il#$Au4(i8)i zxPQ@-l#{tAs7jBormOC;LBmyVZJ!JyPKwG|Z_;xcWSmyZnM2R)*Dk=T72&idZ|&Qm zN?TOD)7#RCSEjO)eQ^HhpxEz ze!r>R!oof&N!#$$cXAon@A8eoSv!bYNH@gowdvvbCWZe1fwMLuOS!KfV?4*9tzrD@ z6MYg~2uJ?2uvAj-X#%tWnriCLKO4V6rF#{b$?&Js%r_4aqJ>{^}LG#O7ww>Vo>3RMnIxY~x_PzeX;tevh)AAC2k z`fvCv?n&*<<)veD1kCt~tLBfr20hLg)BJPi{{V?NhmshyhJS5vmSQz;y3uItS7@3R zl&*%Lp~QY#-C}R@&^S8UQtEgb6~pt~A0An@R(f^pMMvA^MOX8$27b&wOrfdXHu+U> z@Xs^2ANhVV{Wxw zd^Fd<{{SP??kcErFW5uOTwc{LGvf?y)8t+fgayRSxcdDT_S3ATJ>CBRvR`Z95NkyD z8f&NFz2UI9G?uJC70hz}%g=ADPTUtHvpyB%-Y~npk7js1(-^(PaDi#Lz?p8DL9qdu z%`~M_oj@a9R~N`^?ivhl2k#4x=J>V!tdx<250Yg_{^e-*kuu(-rPP3?q$BAeLZFbb zN^Pr_$k&Yu$Xv$i(bWkdtIgux{AXdQP#kZxFzZ9J+aqz>eKj&BG`56jgf3{+r=qx= zwUk|R_AqWi$S-eXc|K8Ph;m!hmrL?rlMO$!tBY3UNkS5zzzuO6?V}^QaBhp!oMP6AlD_v7LBFQwSzP2sKo~okEzb;NSuv3GXs_U>jLRst+;Z8Ab_`pe?TCvJ z4p5X9zow8)MFBl^>@E+-8I8m-p9+=XZ+5wv&gW#1ewLC4+=S(B8RqUJUN4>uXN2 z+eO_i%*AiAB9z*ts=dfmSuLq;sE>eGaQJHS(=TH=A~#fytC!@kxAv?Lk-J)Y9S`tG zb}LMby2+5`xx>;ecE;FtRhsUYjs(x17xg6%IFN;aPu5bRN3Nv(dn>i15~DvECe81t z2f6S4SFpHx@qFT|=Ptv^tj6DFm?FK48R<>&YAFOfh@@3a*KXi-R?L%~HpewjI4Id@ zCJzMocTxrw!#~$uk z{VybJgFx^X(XU{x1^)mLbVTkfC2?0Jw!3J(Tp>NPv7TBMHy@#3cTaa1}Q?cTK?4MinMVm^|{PeZ8lngylDM^z?& zZUur|MN&l;HX9K`s?n)hcfc7LcY?mw8^%Ih#^2-*AX&*E@%@%1zaGdfF%YEU_T{n* zP#is@^a4TIRXXbUS)zfXV=3ZSjJemTdwSs`>qA5hm&dMjIR+({adITb%r7(DJXIOM zTA6Tpx|KVNv-4_lc?n9#jIR2#rNd$ua+0A zhcV*|HSTS{kuhvm?E4qXt>z=DZNVWC+)IgRr;$|w`hcgV@^e>{%K#SMUYewFj9X#4 zak-<*swhYr{K)`*DO-_vi^ewN@)kzUe{i?v_m&lxV0>$3jP>@_V8k78I+b1LLPOv< zw5TL2xhP7j-Ay}bp1f?0Y=8!VuDw)Dw;Zw-&wM+J$P(n{}MMxV{RX1FPmVh{sPWr7iw-LCzbDIz@ zAZTcH{FSfa@3tqE+|3$q9^qUmlIIqQ%1y(L`2y(@MWu@??yN3EH(hxML#((cJ$p!~ z)n(tgcx!f6Nh*E{=HJ?`L3cLjpb?r-o`BQ6J0FM?T_@rX`(kNWoW_C}x0GPC~x`-Jb9_BkVue%byF+xVix1c;W* zj`I>DPZs|GNOhPte{;A}ga=%&#A_vWV&=cpyqr_QtF7X(Y}vBf5>LxTMDJMt0Npq4 zNj6LVOLE3bhT>18xWm^~uN=jgR_gY@Y{6@}DyG3{R;h0}wkFX30ENDxTD*S*$lSJ) zc><|9M~l8WaKsogGwxzWb(P!Sd7Fj-VQr$!cUw^&dW8NHP ztBYgvyX3c)KHX8fu1IM4uAJcCWt=O6ajVBEd4c35uaEHzS~Hgnn%8be?j|BTBfWJ~ zfm;9#N?SVRxnFd%Sto{CvuIk#u-KacaBG?6TxwI@ATki=eN1lbM9;ICG?SR4y0$WQxDsR${!N>>lgcj_}#}AhkSO*c@u|j zH#`f-PD7tzIa8S=Mr9&HR!5t0#u;S;+j+DlLC~X8kwIRfEpB0u2Z2w5%EW!uvd-W9O#|2CsKt$Gs0x47 z$mY?;peEZEiBlKtw9#700Vx2INz>u=QJno$m2BhoAjcE(c_P;wP=-}caaHsH=`Dl< zR8E+aK)kwUkgY7P+lj?YHb|qBhy~SCZN3^k?MiKvoND5t>C7-hR5v4Ir2?W9S<_k{jda(jiNO~uO$`qInq$>S3^iK!{Isg2?OQg5 ztkD9b=$H)=xxp$T7-=Eb)ByAq)aErobe;&LtB6fnjFOa(=BASSpMrWQ4^ol}l%rK{ zqw&#h1*%X0AsRx~;!@gslt=&#MQ9}GlHvePwR?8Rp+EY{A5r{uZo(QBc_v4E6Iu65 zi>V}&)aonb+!XlO0A72aEkH^^t$P7VZ`VSp6kdVcpb~0+8kJO(Xpd-Dp&dqzCbdOW z;aZQaG*wI4K!YUxUn)~XQPBpXRZ9A3l!k?_f>M$=*3lMzLe<9L*;T5^{{S;^6xm5e z;WhY@HD8hi&DuMwoGfi*f$r!RNjzxhdH6pnS-gRMbl3*~lT1c@PpRvCu+eD*4gQdY zs1Z%;u4Bvi%dai|nVo!BbK!i_I~m2)wJhmRf||V6;?5!RH-WiZ&wO7mU86U}@0QzU zb&b6NanX}pu4-wp>~}tZ9c>OJOQ1Un5zvhHw|s)~%KrfUY}(LuS`T;l&MP5|IjeKE z&LC6AS}q=~-q1Uf`hQ5*pj>FYo;6lqhh?VI#T@BB^gncg5*i%!r?{n4{B>1&&C@ko z**1{4s`B&5QwVJ;-6Z!X)8VUe(N#2*O|`31gNkIbr6^mKNhl=#o|=z4Hw7jF8ithb zbf;E#uT481fmhs`lr(?Tbm2%n5K;;4G(NgeIw>s*&SEnLJ;E6i=vTKQ_OgPYI;A3j zS6zo1@oj=}8W$Dj{z-0cc~oHPBURE1FI*-fGYRf}^`$K*(5XtG{{S6xdvZ)-c;Ce`6t#exhbi6?1#UNqL{Yl$R5j9{hN5I`%Nj*gd>q@KxxrVy zU$~|(_AFV0MlErhE}$KQi%2I>Fwpr)JywS|Ij9|!O9iuD>TOME*&SADM*zAx_Tl2Z z7d`SH6Tb4?wj=RR9k`qRZx%|s5pZG)fo;J_+NI?U$EXB)>fx9>+lyEIHj-0A;JD{L z=2+_ouwLsMjR^4_*W3@1+zIye<=-Co+m<-{0*8vozPk~dEX-msiz(zwQd#VMsGtWs zD_W>+*Iajpa?W3iyOukb)C$vbUo7_5g68cZmeM`D>8mua&Ye;muLwL}@f$d>%DyCA zTe{m(a>nl0H@p40O{2YNs?NlYgIg&%&mhcO`eVkLDhYj~;yhyYvr2Rtg5y(PzoKeH z`@7=SPGgO0osY(%`$c5zJjQpi($*%cph)*%`L}!~t&fPI>{{T*UB9I&jUvF!o zl&us!Ytp@un(nSte`2tj4uu&?5;~>=m%vZCqTZa?E8SiA%r@$3<&L?MJ;bTgJ<4`qQ9s zRUYnpI@zutb&LS_EFZ)!8sv5>zB_S|YP#Frwq0Q?qEsiFQ%<&^Xi`$G>#tZg{tKAF zVcZB_UyK&GvEqbv2dMe3<$bKYm}Q1f6lBVA%bl2|%N(Mrmm)0omZ*C6{{WV}tM6wJ zE_j`vdiD?PUz#=-y2Zyz)xRU#?p;vwtH+xUE@QE8R@amxI@*>{2XfhYG$4xBlseOPiUYqwbgh!Ky)o!jGJ_-ckE>PvG?WZE(oVPe~4dt6`t9B`r$pg74_6sQY zDo1}`g>|L8wzr1lT`+g4Sd1oW{{SIjCO&si?I2Zr$MY8yW;_+h86E+ep(x0)`73Sd z38`jNYEdYV(Ia#?Hy;j~_7@+_#d&Dg#%c&(KDh=4Y<=tPCT$BphiWT(;g6F3P~ms0 zXAf{)&HJbQcOhtF4)#OoQ_eI(l-V@)>IUSRYbVZZF)V1fxdN^3wVvB@j!g{4!y4k| z$^E@R@jnmXtk(c|m*#8_Bo`Xh$5QbQTf}B6?A_Ap%2jNPnM+#K@o@!6aSHD{B-ejU zWI4CG7)Rnnd$$9i0c|~__8*tW$c@pD(=pY=o`$`8BA#FKzmWWo$QNuwn(j=-V~^Z& zBerNrD24mB&tjGhNmXf7*U;-p?Uo+G3nt4D)$5)9;Q7XHC7E@#$?`*>1EAw~0M2No@;4o?3`XfvrtVG^w1x5Q@~7iu5IPpU1mX z*cFCsrms!OwOoDTm&M z+wHjNORG{-LY1C^yA4sn4DAAhiYx4m{iKg2%?<@a8C=U|8Gd$lH*fu&z2cwVFal_5 z2ISM@t#&rvTbQWrXt}O;Ee(v{;Q#`cF}%jpacN;F5T_M_r@`4(J6BcLxTzSgaJ-7z za4Dfq=~16~uidfQ@F1zwr9yyezAKtKlvnq*uG_pCq!i6lN=Y?AL8w1HS_Xosh2aHE zoORB(%mvA8ONyL^&n*SF;5kC=#E=LLKHzbeYuA#uWl#KAQ@mzbK zaT{pg^|mL-<5x4o<^2~fu9oNGxUjUA{jx%kK?D$JK?kANYaU@y(PAv_gS76Ojukeq zU&l;2)zMrq0usCngOSt}m5@a=iYoi7K=G=zJRRh}C-Bwfo8AeONwLXjg7Ymdj66OL zH~L7{Pm*xU44h@NFdvU})&r8|c>MmYgr%M%vHO>Gct`G;jq+W(CL7CEh9KUix9vYQ z#HGu8!lBzmrF*4ci2OC>{z2?y_7i&cP9TkFy??_$cCh54`YgOaR8w*2e*XaKlKBI~ zn>QjmBR=9;?6nT=wK#|{-|SOLS5nzY1W@#*u5e!HOACIQ`L(BYjOD+#duz!)yAvR3 zcC-aX{{ZwWw@8*=B!g#XhGLKd69ydb=5cYz@tWsynq*yN3O1 zMn5pZ9neYt04gNpFCF&WrL_K{%PyR9ziA~GmLT!WZf;&sD|V^pK(bC=^;TwReX!iU+SQT7cPrwngK2fZT{cvdsSB?3N5K5GNsZ?dTRYO6 zed}MzJ*2n3kJqwr*IFhY5%Q*U!x>Y`VoR3U(43{l#P@J*^nw8$r`P5+XUH&?c~~_9 zrDM1+F3ItW$^t+KqU?_y^4A@C1z=1k3vFJ?2fYb>q2^re^!V@7USrL1>)bT@RIz;t z;+*3b$}L8qOHa)#L4%cXWT(pJId`zEkr@P4Z2mtjG~1hN`1KTWvu%yuCG}}8YLeGG z7vw8DQEu#_Ji?kWAbzBhI{Iq8kGaP`a}8Rwvd?Ljk8FaDqObWTD|*AGc95wy3z69mSbC)TdumI0zyzbk6*)3@%YEha2OJ^d8XP~x#fOE zI`2tKG-xc)K?sA?S7D+qq9i3WLBR^G+v}zFNqQehO)StsX{l*TG!R*!jSe3F0C)U! zDwDMdrKT67PQ>aWaH=rh^8Wzt_0n6_G2J;WGuyYyZcC8SH3$S4t`DJA6yH-}J55v^ zj2@$Q)`@a%O8oc{ZE8vxgAqzpc*zEUude5PTFZXtv^7%M;+YeRx{{Dmw3^e?Rc~Rc zo&^x$&!=NWYC~N>n2;gLi0E|a0Y!F*R1>kT`jSFr<3w?wO+G&zO>0w96(w=?uLG5N zZ~p*j_v?j|8A=vkk!pRKa=42 z8p6$#tJl5QCwqgqre6lTxkwx_X^TY_&n?(r8c5Kmw3Pi{8LUA$yG* zYMU=ZD3vF2HtDHmq$Vg3O}195tMeK|G$=_W6TZ`rw>AaFdhZbJ&D)Zde%eF++=(5> zqYcpak?W^rXril{?ZD6iN)lc_cD5~}akjq!G7=WSeWkVD+PWz{z*HR*V@w;ixOGyl z6~g(~B3x%$IKI^HCEe|;HzDgak*8+QS{7(SaZsOi&fn%KNdRtWG>ec_aVb=m)-3k8hgRAO+5{D$a9|U zW3A*YZ=8<+yGI=MTPbqZzJfN9uH&sdX|LdDu8)#m_VND!@-<@IX~;P@o0!d)k2A}a zzTJDh-!18a_CNc$;pMpRY_8XWR0;?IJ*0~K>g;{ex|k6hCC9S8%X>{{Z1Cx44R3$? z)l+_5@c#hqb;O*I_CCM4g1KJ-Ri$fY`H5N>yH9TT$yjnT#v3j&hSYm|4J+2CrmXRt zt3NX+Aq^z=(@(`$Ww@^zv~X~h?r9YpMSp-gE@^SbOTZYnC5Jgb4!rV@5wfXCnQntS z`z_{5QWTiWL&za0sqIsVG}~(Hb$;v92n{rUbKZvBEF=vakxRV%5tTIs$y1x;*b)Rxk}e@r)9Hrk%qa(v`1#=!!dhXR@Xp$ z67M|nhY0dp_{^>p;HJELx7n?%!^rH7US!_I+T_>?T=3+yAx#z1r3u=n<*1ljNug_m zk1p_2vsZ1NcDb>J>8%g^1cRRP_6L*k4;69ElYnu|2Q%aU0DJzb=Bs1UP4h7KTT%!2 zHrzr`l7c}6Dpx=S2HKBni;^kJ%gck6wnHfu&ATIOHkPQ4P3s?f4vAn!DARc(GuJ9c zar>zTL$MU!YGQue{{ToOD*C&})#|qYC<=HuHJi*C!elp3pS`Jwyo+-?( zaIaY&5CbM0yS2F~OleE*L0@}e37`rCVYZNQ>~w+Td4gM{cFmyE{(7bixh^}o6x5eV zLZVy18lvt%f|NM|VtUC%3cg_L7s(>?2L;eLUD+YbJ&KPQKV5zr$#+|M4v6?mj;%ek zs)!#hw9>;#QOuwu%;GZ;?g)<^;QCU8_c)%F?c9DxNW2?VtTW4jB^AO&Z&6e_Cwl(? zj-=oPXG!G;{{VRO22yK0M(KDPy3aj5oMH{_bc9aI9ZDuZ*=O-VqU zVjeN=OQ)eDZ6S@-IOk}ID{^#`)jB#X8gYo~rVk(3T&`om7(u7IIU=7C?WmnqW=$X~ zPs9ap+7S*_XgA1tf?2rAF za$AXhEVdpjJ#4?l1NP50b2YxD{m{ln&Va#BXjR9 z9N5(AzmMY@_Ii?PN}tN}^N9_-Lu&?s zG;tm-}5#8K`FY4{o`e=f)qq_G3(KW)|TzQjJdgYblno43taNbk04UGFls6ipFt&0K&xW7hB#; zgk0o4(}-p@0)g(X^g6A&&bW{Ui$jazQO6N36;x9{u^To=agHOHjTaQA8Xy24K;XZM zAgI%0t)4bdQZP?d$+H%ooWxRrFjln#Uyof(qN*)G@A1$|0WI3+Dl@1g?4GIkjX22& zDY;_~30~_JFE#ttHwzzF{B--{+$t4(PugfxTTz3kq^~rT7rHj}L2QQ>%EOH)pnqGB-im*781!*F(0ugRXbMz~ z1SAD1Ni-vIvDA&hMh#l~iXOUBae)q%C0>=%9Mq-i+DgZAew7bhCDDS7t|0~7v=EgJ zoe1^QMYK+dK|`RDDtFSF?NXNY)0@#zZX6&|ojPf{Aw)7)`g_LShe=Wpp~VG-B=)H% z=~t-Jd-Y07E!>%sfW^(fbt7ivq<>L9oBVXJ;G2AY2q`v|u;0BE!9FU*L+Bf%f4n_3 zwvkl92{8(CJBf9LmX?wTp(Aj1I!vtvE;*eZp-y&=VYlC)#m2dk!T$hV+2VlRvm!&k zx4&Bxumy=ih17~}+oXf>)idl%e9c!-<(nwmAVw}cD<Rz<{DYN1|<15P&UT02T965;nYv6`rGJY$dfhLzmfl!t)@D?-wHRFx%E zDNs8QcN&n{TJDOTOLxbkf^b@Fk7W~M+U(tmxykMkR@F zjVi1!75I72T&crcyU&mDhNTBInn$6^S z1WlMiL8Tu zwACWRl-xk-SIVq+7jnKEd(JUoK1w*t`-| zB-ZTS(YxiiP#5FNHqI=6uoQnqsU$g${4b9=N0>x1wHO6(Uin~6Vj613jY9SF|?_-feIf}N>~Kz zo8|qK#N^mrXD-)x59i>z_Z|Jo@(fIYx3O<;iT+2S`;?L7=iFPzo_ob3!o@fl9A%rd z(tGdkEfRrHNbXB(V4%>N(5z}72==2fh`g)h-5Knrdy((n8$DsMk?fnty63!3Vo4zm z%6W$9JB>Tq0E+ak`tRchxcPu9%T46+blg!4!tif7aUg{^ z`wl_Ip(5;CDpBoC+RD-ZsM#dNBvXFltmK?A@yWgymK9?cErvn1Pzd~K_Z2te+>Q2A z@@@)V=RX+p{{RGXd8zGdC&s2fnBji%k9kvZ!XI_E(rI6IBV*7CYRca^oUkH}FCUNm zs8_sq3w#firoVCiY0W$Ag~VQ0+kfkC6Tag7P0ki|msz(N_ej|F(S;_7_N7OP6a$Ia z5$&XphPF7Lb}yp@PTKxU4Vm_?z>o0VFLt5z!b9F1@|m4_fFVLZv_uYq{&F@(optKrV`S z8GvGWCnUXl#U&{28|w>`l4&pEh9l-K z{FhY5!xf$yd#Q(%G}G@1Q!&82Nyhk_HRQ~1$)S^Hmw7HT$NT|s$7VB;yB+xe{_1q0 z*ux>Tl@mnJk+G=*Uc?u-#zsRyunm6&^Q!*G94#f8g!-;=)552}-LJ_*?Q&*Unel8K zCq3kw)=|fp)1&(a89wVN_%_EJ+x}S(EQBQ1lu4lU)$ABpS~oNSUbI+0_g7E<02G9f zI#o?L+Z^HN6#OHH@N4uJEnIsQyM7;qO_y)6TP=3x5)!Ee$4U@(HE~~3j+v6$40B9? z5=yp~?(Z!tVLL!*(z+_OpIGpoJLFsbCx&umzmw+TD{=+;`)f**8L7>wO-Nz2l0wpj z0+6t1tx2%His8GPO7#B#g;$ru+}TO8Kz8*8gah#()goQ_1^ebLXTx$k+`G&yY`ier zW2#AN+53as=s~A;1~!*3ib=5J_lND*Svp~t=9{Rt~W^*ww##ioht}o z3n3zk-)f$k@=xwU+0e-hl-8@>-?OWMEK@^O%)cTmajcw=e2o!d#(mOKPF zeX~!&Cz#rLSI!?519q>tjlT_O7FA+UqeEZMQNcv!)o<!?&Inp5)C~y+^uppu-o~F=)5JC#9hB8NNN2_ z`2PU%5BXorPvLjIH^1`Ef5}<4IUeS@5G*$fEuR;BvFE8{=1mzB9S%ZNDzj}ok0OP5=*XDs~2 zi|$SR!WK(=b$zP4zZIt5KI;DfQ>9;0>OngLs*CS-tQ)vS4y9a=Wv?aK>NpLf?_Ck# zPq5!5arNsj%XtHx@jfbRk!;c|neY88k0G=v*I9etbbVR@ zr8+HEBiXBXN&evOpeMcG#GkTXiY(sW@(0OixxCR45#`(OnV=zvZITkh5N!)UZ50&k z1L3L*?{u;bqgqKHg4)>oMU0DIOf>vWR5f+`<$a+%ZM4P3yj}6(-c6m4q_t;yC6%}_ zw%8O;x}j=NKNQxwrJdPp)4D=H_?3NcVU4mBX&QED{%Wmde}7N7r;;}p@edQcZflS2 z$?a_`i))v7oXal~ge2xlncA%w703G++Rp0u9 zOSfLlyYE^C=Gw;6);fL4KCp4;-5<{-GbrW` zK1Z_WalhNc$Cp~Oc&Flh{{YC`M>&Qz%0b8GJAC|ecTOj$L{z(g~@%w)jv$VuYTS7!l_zizQ=&gq+ z{{Zq!+N};=0#?oq1&5|cD9-tcO)jHZKoi?LR>tirA2JE zz!x;{BfV?(`;=kFoS))H3i3W1ni=ssLxy$ z>Ju0btu;zgDl6B1qmSeBTJ3u%3dLUDi@oAP1a14jI_vm+*TZj+_%yy2=Z;L{+hcA) zmB)D{^3wgjGsz{Xs;607|Kx z$6!sB)11LT;(y^;-{(HA-K}$~Kp&5Fz+2U=nISC5iq*lU))vzbwue%cZUVbj2n1B? zU}V!(mtflNsWF>enJ=%kqP4h5SOT>tOjRVhib%5YMZxyJxZh+WAGQtu0C^+mB{Z!P zgSk2~;8Z%4V&RkGIGZbribDS9G$w}h_}jRORS7@s(}aK%&51&{o6W}R^52~$aumPy z6G6YA>8OsGN{ZYB*?Cp`y6Qbtff@zRO%hrNis%B8=GAMdNyJ-5koMITHTdY$Wwb?# zly}1M2akqN0o`V`n< zazc7j(g|9Q+GxmBro;jW_nxEtwA-Cj;s@7KdnG3=vKm=v&3pBM^aHo~>BwROQmXDH zB+w+O(p3AZp{V%jQa@EP7`B9;rqqS+ang2#R9FpmIs_tcL$;38_xh8j%W6{&HPHn7 zq^R%s>18_;P)hwLK=e+Is3O0Lza1v2Xdd-^bV$*ZC%H)!t#qK$h;>s1ihMXWXtt(Q zQTvs9w>O5xUj+QX)pU`X)m_0eLqJaWnY61D<^_?(h)xtPR0Iy-{X#oSB8Q=; z!&uG}$3OWpUrIdhG#>!A-s|#+*#ua~KSW>`d`jjrfFJ|Dx>yzJxO+;oSUf6t`^ocA zs*@rrYfhatTH#B8uWi5l6xCK9?@vuyDsk0&Q}P-*qAj934E614pLwbpVD+k{~8 zD}-4^zmmPjvW6_mEjp*#*~A*9b?ALHfwJgVQ@zwsr&cOomy|_Y$}W3r_a0gJCwl2O zjnh=+h-+y?Ya_Y4cD>D%>dKVj`jcIT+J|f?iuPhjLqMyA4FOWJ03Rc(yH=NxMM-xs z4XItb@Jq#BL(6c?V>z{PUEegq@*B%A?pHX`-~F4bO8X3O`$Q0<(9>Xb)oeK(hC0lL zJT>RVc;}tsGTGeD*kW^`W|bW)^ZtwI&%vy#mbjlY&A6fdBe<#DdhCIZ;Wvm+dr+d3 zg#|R@$?Y`+eMvRe3*6g$ZT+~NlC%@niuc${u0e{(KFQYZEe4}-GywP$qP>1ZcoF0W zlboqy$?iEDcI*oE92e|PysvuB>d{4pf==LUXnzfJ+z+`Hn0P~4+L7IMA8@~Kc`iqO zW_+7taQ!|Dnf#&lTk_Wz%pqHGdyR)KuuqUeZ_1MFR5H~;3w^brX0$y4*HW_m!C6fW zXhB|K%Kpr`6{W|f$2IIf-C7!asDFy+AB*2){09chvA$2{zD0i`y5pq}yNg?zX}e>u zqKgT6)sJy)gRxISI_r^hPj_xFBP@lxYu=i#Lwi~KOti$z%Zu3y%XQjK8()|8DSOSY zwT=PKcT1ljc*fPU49q(~xMHjpo3&1Hs`sfVYi8DZcMo2pQ+>1d#`?j+3(A=L6WYT1yFkQE6imSW4hY5Jg%w)Uc2k)nX?B9wW zZQ6M!*u-)%m)IS}0r=NR&HGwZ+(R)wYPkbQEQW*^#t(I%R5iXa8 z@+&&OYcNsBlUo!d9qK8pRyRMoEskBH{{V=CFqSn{{MGQ67UvE-{{X4w+uv`H@z{m9 znGA4{`r1JXDOf+KZLm7%{@(kEC9Ix}e7%<*_lMc&uVEK$uc+R%QNN1!PZ9h*!f=e% z{fEu6-|h{2cHT}~Zp}mU(o1f(JU$!z?OUXi1RDz_E3Ts_)4EGGl#Nio?>GNKf z;-2K;W{AutZki1gQT2 zEq*%mh2DHX(LqpGi}1V=_n?;Hldebo}ki_o9#C2p9wz76mrnC9HBVmlcuv&g6%pY?w zgR6=~bD2VaC=wT?6U_4aZ#$Fvd+0Ays>gJ;kiyj?$^Gj^uAPwo235ngFE_ zL#g>24HXX`&A30RvKG9Yw{C4^bN~U}-THw8`0D|2iFuPiy#qpI~Sj#Ksye7Jo;|EQv zrnEREn{~*y+M;@Xp`tIMt-P|Tmu>9a8iQ=RJr(%2`>0vATol^U#Gh#Ps5a$0R8n?O z2BTH-Tt;2A^;nFJp>1k3`&C>{3i2N+!YAA8g8ut<=iWeJYJFfZvSIB5iS(_`F`T8~0C=lqwp z_tuXx+B%?iUe3Y$@rlXCEwXLe@B5V9h`C_FEb^n_*JkoeBPRjXzz%!ttqRQgkO z38zD@gTy_;xwUDE<5u=7k9(2qeTGQ=Rh1hd)P+;=tG0E;my*K|KMXP4nReH^fTA}) ztMJz5_QJ;^&BB!{hGKF#d={iNa?uh4y=FKTIAx`@v6o< zj^%ju$Ey)LLF~FO!rochGU$zCdFew}F2LGmEKv?rQl`GTr$+|3G|Z-2aOmsEpt#RPX;n{1X?@V z70z3v?kHZB15XF0`mc1$T~d%*mYab% zTZPq8h=Ajzgh?cJmr|bckSkuB^wgNmbx~#-pqp0xwDr^N>Y|jY-(Sf80G6$y>Y&Px zGT|Vmr2MraHYkz=uEJh%At*&kwKg7#rfHfEsa1$>D7J;TmWtF8dx;dUsrV0$r7}9| zp~*1qs(Z=BadEk}0X+){p}Fx-+GQpdqtY3=%GqdAV=JQqN?=k4e@!-DU;<-pv%YUL^R zdxXf7{Bw%Y9FqncE-W)W+k0zLU5=zADMhsO+#gL<{{ZZ+WcwsxYe#<{$tLlXVDwJu z3H?70x5w_PVRQcgf;@-ghmPD6Rmu7XjjIvl% zmdEW@9`11f5Kx}quDesN+P=6g4Xe5;HwO@QuE}_3fbu43gL8K)a({uir<8nK$;k`H zIsX7R$)w_V)(d%vr#;rAI~|z`aZ(Dv?LsJmj@s@(!164UnLA^R#;36UYf;9n40fgE zwC%g$tw{M$3V%f3v2aI|mmeAVGwwIz1Z*pZ>|AYoyW^5EtScmP9$?!srT+kJel$3e zSpn9Hkd*>l1=J9mAgk zMxohqOoQYsWPVEZDOqkYiM9dqN=%g0G-=nk*RM*|R$e#r8^_-oGTwJ@h7$;Pj994|A?lxj}fad^v&`=TK-2Q8inRh92CSQUvv?626LtvE?^nd{QYi(y|ZvgT%54mUVu4lY+ceuI3r;q(pIhpOqVV7ep zILoODVb;_N6qC48N_HUXZ9!0Ev@CfJ(2liQuishNl24^{_>QRCurCye0994rO}(Tb z9Kv))5!?Y#>Ydbd$aM;7)IqIlQnwpv>J+xL0KH0-g?5ordJQLXCjdgS{xo>G%)VLY z?eTvJV{Bx(tFYx<8@5TgK()2|Sbd)A{p*XZBA~4xBpRCRI_aF2Ry#QNzoAj>p>@RG zlznMbQ=B|l=MNw9V}|nH5cVgOM~vLrcAwltmRpMI)Y@E9;X3L&Nl5^OYF(4taTt3# z6;9mr5nLE#^1OCG%9RSD@;m*`TjL-8cW<}z8*4qv+yxrCF9?d8o#Iqp*Ze`OH+9fo z(J2?ZTO@5k*H&@6MG?)_GA=Kz=T=l|eM#Kvvf@IoEf@ex7@+oEl_#KK1lQ+HwKEQB zS1R290GXk_B{b)``-v~ScO;&_jkP0ZSx@JW!p2JsN$rqTYw_`RUez5=mAY#Vca2T;+(6DvNNJG1Krwc&kyb=0>Qq;+Fpa zcw0^d7en32u!We-%j#XY{8LT07ai8!ZE9_4 zAxL#5ib9fV6bR@++#O1|V3DL13ff~6!-r713wy|10R34I@ zOO^|~n;p(A#wv(hbp>o`PzX9^Xhe-6NV2;y$Q-(dfb)DgSdf`2FrKwCP}_B(L!G{( zewyZ)99x9H1>7Kiliv)GoMhlm$y}IP=!|&fDamwz#h;nF{{^O3<=}4gLgcp~pCUd&h9O zG`1!=%uey5FOg$&G06-yB91dDw7C*WYuMry>JP4>*yI~6d_t9*!bxc04xscYyo8hv zwLNt%x~%yr#b}-?W4XI-ZA0_hQ=%b8o+^Y==>#z8Q)BvTsRz?cTt*K>)x5O{A92uE zQNolKB-eUq!9_)}(w^#|v)4%ofvQl0beC^)xrYyL^C0e%_>CkTsdY|Y{sLc;0?4<` zS5x-t?;IgDq3)ADGjGAsd!=og^_j5SnQzW`YRNUv|`{ntw$gAiY5*x@9;TQRq^i zic+>+EuZEdgnY)3!chR;sb;zz?&%Y!Xsk8t-AcB#|lwN+b*PKM>}u^k4U z^uhqSOBgpYXILazR@{3cem#!lq^k9et;H$~;EjNJphtGwb=5XQjM`jk_UqWKejl0N zSq-drl=Tau{yO=y!1&e;e&XI=;rQ2!4b8caV>4ESzKB!aYv^fc3Gen)y6;_vxQ2V% ze5_d!acw19PN8}a1@9&rY)h>!Vxy^{P``*?FmXSaoSn%$VW0B!4iC$(jsEAy+47lf z{C@3uT$f6%GM4&DKn?bLB`QkJ^e43IZ_fFh&L-M9eLd|OJH&L??>c|gbIu3B?K1C@ zHot4Zt9AIDDe%)@ig5A-&n)~<GI&>8}R>_sKXe@BJXUtZg&+}PZ zkM!54Y=PF&kN*HRzsL`{GIBo=Il|9i8Fm{E+0EElN4nNuf!yQY(1ts^YWx<8W;DN2 zk7-o+p1QWR=2p%-B?dQmI8*KUDy)aMcdcoXA|+t|0JPUnmtVM5R&M7%bt)s>%-kR; zNSh7BsfkQ2B&4OroC#V8K8hOB$23wltssSfHuA*s*MbklDZ$FJ`yH$*HK7&lYN;bwcJ%2T@#$Uci0^w3S_Z}2KHP4K?{{RB_e!E<450|VGWlCN)wJlDq zR>LKKvnyK3Dp#@D+@gM?Q|qT~TShAxWjoD!sQTqJ2KxaZpZql?N0LgWORt)%de=`F z!h+Wtts8QuxC&EEDGhZ}q8;u%-P@l{AxQG2n=Dr+PmK|jm96!qAvCQ?Jv9RIcCnQ` z0?={$BQ?Ni6{%le-)zseMoHu!gWUUvSvelj!+BRQPq9Z{MRaIB~JxDEYv#4R}lQRe=k&mZIIRop$&$4M3mwlT)yB^r<`Pn{G)%)j=|d2aolP2Y@j z+(VSIC@wsQt-|1|P;sQ1p(%DibOk7S9-8#e7x$vpbN)gp;unUwC$e+5%@8ed>_V&7 zPy2KJ>g>jI`@aWR-nY0#E$!wu)hz&>ihEK4r&24fkq5gNu-&Ae!~Rz@y!L-4Use{> zc$$AyiIacZz4tM(<(4dZe+$OBS?>)ga%I8DVY(xNLQlA+(^USihfO~(xfqB3w;jJP z`AfF;k2UG;0fpWm)P7_6C%-uTo&N9H`}oDXGT;kU=G}ro{@0C2u$yEwn2n_gPrOiu zea8rF0;8)dZ*lP3C>=raP%izY%uqPCuKDTrD)2tT-*l^$mhE(p;*9Y9xB(U;w>wH7 zkhN`Azveds*0j>(_ZJW~57jqm?RIQGR?)x7bbG;nvd^B}J7DF0PUhYlYXY!G#b-9$ z7w*v>yUKl_nG_MDDRl`-6hSKApw|KS_mEp5#iv#3FWXiUY%WRfxNrgb01wZo zqWQt{*|u5boATm`;%+7^t;7X&rNLt=KCrT$Qu); zq)T&Xy!Sp@Z6qC3mB;S^bBu)_{Y3u&_F98_(dm`0z)${QyC=oiP8oYn13xrr$rw$p z?P!_EQKKX-Op63r?7ebnkdadBk&@yN2K~ftI_r|(&n$A)rkbx#+gQgfgNYjLH2Kp- z-OE4TyX>vPmNlcrzqi%jAf0Px zPllk+gq}(VI5k){duaMq$MXJ4hG)NjZ@U*Pu0(t%kZo8J?kiJB$MIZ2a;F6p*R`1v zoR&!d)}G#-gAs2Z>U~q<;;r(>Q}GIwW|?Q*E6WEVKgpb#%KVIh?xB`v+$Lj|_LN7| zg|id;YYIpI09-kZziG&^FCe4@J=_!qp!B(I{SKV z~;HhS>5ODrLnuR8;DFz##V@!%(hnbF#`mv4vg3i@}J?TjqF6{LlAN@|Vc0 zKa9AV#S$MBTyYqt-Ep#U+y^7d$Qvz)?SA3L5VwxW3hhNGqIc1KCN|yMr+9O`3V{uk z`>NPR)|1!m(J{F9?Q!=Y@{;Qw^Zx*`TkVb&)V73WnXSbzQ1o8#N{_y` zn@p*{)KvifL=AI$?{Ttn5*Rj*x7)JXVemGO17jT_@j4IB{#B{qJZtUQ!&A{0#E%kg zUPfo}UQ~FThmhN6JsmHmi7qB};l9F(?^eY=8o^|9_s(@>cV88^XBlgo=6GNoo2mT& z0HVG*Y#%xD7%-d3`3lLyxDCxnZ8i)xF=pU}JSyB|IO5BWI(kvGiqlX%S)M8hDeD`2 zOb_$7dxvvV{8b0!o=5v}_@R@q{&8x`IsX8eFDp~CQsdL^;hD)k*=AzyE_+NbynRTe zN5gGsaC|QR04M-%fmCvSUmicq2y1>{Z|13#Z@Z_H8TXU79yH>PGz{=}9NaC>WIQ2> zTEg>dy@})mz57X})HcU&cvo^!y(kE+M^uyA%RFVcl1jPDo!WbaCH=u*(0Kj7lIG9c zzszoYhverj@e`b#lwbIw({O_5;~cHV9$S{xmhMqu+~XxXh6yd!wOU8jQ(boMAH}hG z3lZc1g~s`hBFW3#{S7?l%~tE zV++d3r1j&pXJt5D3?SzBg z0x7b2!q^Hmi ze=p~w2|*KDtG7tB1PYf>ML$2oNhJgh>ZtuEL=jbZ5EDbxjRY3!S5;H; z(UfS4l~Q-<(@pFM_aci?I|2E1((kokgCL5B@BC(Q>=+4D3VQ0bt;rng7bY6HI+Xd3Ir0Jz9+uuQqxA0 zx%{;v7F9-`N(6f2Bn10N*bRoAk-!Ql@Uo_<0D9^h`l;^dyd&2{gH2Y6HT2N}mPHV# zL0O;&Zn|4_O+EBNzVcg01A1wvQ6@GMswCP<$x-~qfKq}D)e=bb8X{4&XoIC_0Q^ps z>6A-!C;7jIk7z+y&l)%e^W=9Tjytm7qT>@?w@;fI?KZx`-Gtb65%weNcBCZwb=3za z1%wYs(?HBdltwGhWI~+mH=FTIf9o)vl|PkeoXLb*m15ckIdN0$$VA_Ll@zHPl-J@lHvZUjR2cl3lmkjA_0=hI-Cv^A zNk1}m2h*aue?;rYqPrsD7Be8J=?VyGBXU-wu-2yyZe1V)y2@uR1aOxgfqs+!0A#*j z;yX`?93|sc8wP#PJ;Y+P?t6|;wJtkxCrfb2ee^!!Qk5gzQ7I)`>(74c@cfOoP|WAm zhPqd>-upAp@fjR=iY9?0e^Dc;@A;|U?n}vDR`GUijAgt@iCAu2eH~3Ra-5={->Pbs z18G@LL>`r`HGA#8108z~$>br$WPRx7xy)VDWwVX0jh$2z#-DcEYz8h+`F_CCc{a?l z!`yphl&L6uAt6Q2eC=P$@z%RJ?3>-KzM*seGyAr-^yzlmOW-x9GE>nBQFA55}L3ne4e~6kU5e&Doo^iS`mU zuBCP8nH1# zgs`@x9YVo!4E_DR*|oZsjV-7ks3*pLamKMMvpIJP$_^}Av`Uq2V<()c0_{De9`(aR zq)@7r?f4oETXG&E`I^T$lxgcllH#7=!D41)Qh$^U0X<5o+^r*#{H&9Fk)R z7s?p^4afN@x8eExE=Z2VG&^^K%WF%@VP8v)mXwgPv>m!_uBVL5Pj?TgBX%C&73VHR zfQH88#{<1a_Z`BmnM7L|m?Yyf7b?*y0m%L1wQbazprEcO|-mqAO+2FPIKl2P(#$TnSNDmlq19 zDIhAQgW;%|tR#1K<&G6wPCJ*L&S5lWjvWv3UrwG1d24UxOj0e9=QS?fV>X|qWP6)^ zBvASaYr_8H@VPN_7qodw`hV<4y0PZ1gmRilJ1&*Xw8y#I9Bj4=H2Q~7cC9J!*EO-b za~>WSr02&nc$~w~g~z^A!z|o6p5&MYUuXXSsXTk;Ne;-!vExFkRHwm8r9~>QVeU0h z6yI^K{{U^2mf{B7LTN02n({_h32$R{C56!4Z8d-VnyxYVN#mYV<4#E?R29FG@kUrm zu$<+BuE%1!JChnxnx){lexp^l;a|Ait~|$SkfgR_M`^7;6JN{k=&OH}S$_vj9gin!WClu=4gO6#HUd}ZubA-9^yKJ~{v&&lKNvhIE`7TY`KHSVeOsnT9? zOLfxX=|wD>nhu?Huh(@JJ_}|vXp1*FLX}l1>8DfAwF*q*>+P}zL1&wJQq8gp$#Pq0 zsZl_!K&M?$@;poKtX~^HQ5q-s?c{j;q*oFOv98zTr@nK}aGa9+ZF>=vT!yHr{lZSv zT?Ik;?X0J^*c+QWQrZ6iRaT4Mjzwvf$q8)rj5qU8oLTNRuu`%SU52^~pbyZlLmQnL zsOp_@^9jv+EUjd)*Xc!f-`7>w7`819uOpqLROqFgPU8L1*zHGnG|RoFL+d0VuuoEb zHGs@o80u)cpBcy-w}DzKTW!ZMST;uFb{D)Fc(ow$0rrK*U4b&4e($&iU0F1?qxwVvB zJ8Mr$D$(R;2>4+ii!p13vysianQ!i+_tV>TZnWy%1xi$gNbN!a{%V8s)~kYY9!l${ z3x@vyge)Jqc)ks5X5XZFZOtn|s6R2#eAgdy3`%Z0a+z$Dq)0H_QW}`it52XGrD#5> zS5dD-!zhqddG{ZYYtD&`5d39*P1PsUMnb#X*N&?9c$LohHY;h#cu$<{j%Az;$ZjRc z9@2og;@5Q!#9Zy`AgLp9?w}ePR-J3jd42-yy|*@|k~8ol!D#R~`!=!GckJB@{{XwY zPmO<%+P1Sg`*3p3C16J&;um%~3y!9z_ZDoH5GC}&cj{%=P}v|J+IJN6I>}=_=dygC zPdM2i(A8dNe{313?Zwrjn=A)VM_MV50r8_I=Ux@NLa{OXKOlIXhHaa_*^QFfNF})q zoq&*v6p>BH_#IbV`-2URR58v52mVVy?_sCRaib#{w(=3v{@|c~Cg?PHLl+T%EdQ9z}ADeLjp>p8|Pa*&xKrAVkFy5Jlu-29Ipy83&NcC^rg*>!5s zoZE9udf$`a*ASVnG?mMW+Url|B7p5$Yk^6NlG;-g!$IA9Ue`YkM$}Je?R$iUmM0(N z2n|KQThP0p`ej`ZJ7hE~xgc(#sg{1Nv#S}aN{5nlu0fk8Z5$O}zVWTX$tm|>hh)c& zNsn-hl~B;_+z08_wj9)Qw`0QO*&Yuqv&@ln?L_v*xt>!{BVn9{6klvuCf~ z(Oq?0lH(06^&vDXPsjQD%`I<6=JcsrX1rpXQ*CA3eLMTzf%+_^lUHJbJIY*0+*8o$ zyDZWF0L3`fOV03c9rZS5t@tS`i2RDkaX;GHE^e-Mu&4)EcBM`FZCdL&$as`jAih4U zPs2USy2rdlHm&%p&uhHn+4Kb6EHaXbY5=K@J+cwDd>0Ci9G8_*LgNmo2z4?XjGGN>o7Y zN)S{}O%%l`mV{{V;lu1zrEr3u{h*FRo`ct`XTYcv5t8Lpk& zQ6Uk~=#WBaBUzw=8Nbu{=!$9mP?{u#rlqAXXnq%x5aXM?L}yzw;ojxrxp-_x2lz})mF`PSCVKD zFCi>C<0>I)XpmH$(nvJek4*==RAQw96i)hbBAnWV8ZwxAP;F`>x48BV!fI(>jWs3~ z)kKkqX8!(2EBEfez(l{0z*l4%nwtEv6Y&AEiOaR?~w?;z>mQVwWZB7zT*V;Pj6^16l z-Tqx)@m3jpo6}@qn4rZC3m^Z8|EaZz7!sVV}@p0C!>cp=Q;f1knct=5TVnna$ zchbF|SfJlb9F_ZxTOOYj^iX4Mj>kW76%_&6_#HnV;-k(Xw0JXVwDRqz$Wff+ceiXF z=LBciirJ4DjF^&3cPJASp7`WY0%`{2)9E@!XZ@SY3?KpCsIVCfeI;eZO-Gi1e?E%8 zCn&Pxx9m$Iz-%dtaKop;yIY*RCA*Ryp9SY_;GBa6T(Nn|=U#k=r8C2fe-BZTAe zmuj|vx3XywLT<8TEdV$cx|$&>s6hB?NM_Qgtl0VJ_qhB%N;a^_yFWH0dt8YzB`w4T zU5e|?K9#9msR%L3RoqFS0MzI`@wva`=oOvAL`Qhl-L$XqU8C}^fb!oByeP*Hkis5A zw?#f@PAW5^MUkk$I_F#*hL5f2&DLVkt_hnr1~bEnPICM#FQcBqcSdvCs-kYjr3Q zO3-|C<4sdxv{s|VG>bjclJnOpmT^3GS{Zks_oshJBEKJ}S*-Eg_C`Eb{{V%v(T-V< zX3%}=yUbeMlKYC^xkQurooQhd6kPt{`=liGMU;x9f1aNSfK{FGT5Xdf%AGyY}|0l}h4ESRcxj5*4?@S$zKXf0crztDx}tqn5VP8a3+rjz!^51RbBn z{yj{4lKG>tad|_Vt`Wm<+jxB;n?p4{t!_wJ{K-1XoNdNP?~_LOE%4=5I41fK4bDAD zRJWesIg+l=$g(f(-*a?cOV`9uzhY@%Su%B?9H1w|6R6EtN-- z`zjl@qCdx4m$tnZJm`z*q_yBz)k<{GL`7DC1ReJp2-f5sskV$EMPWE2Dz(7KV_Gs7 zgH~B921zy81HC^TXY&*W298*B_igh%~1i$g*^Y9uZ;`iJ?0i`+BNiC}P zy#D~F{B(qEWfth&I(jfDjD_eV8jX&cMhaSmkk5fe`~CRmWwN~hB|zH0#Xmh6Db~tS zY4T%BSapUyg)8->bgq)oL~nIUB3DwAPt&Q1G()!ND)?woB!p{3i38Yv8VDeC!jiY| zKMe#CttCDdXd_#tR2%Q2q6k2JLDL8$(WgHii9rRk)w8%i$4Np9qMy|rP>ZOSvQ$CVku zzk5$IQnV}Pk~Im@hiH4JqN@#BNGjb#^9u2qz_;SwPGC?xjK*OlEyc*EIIBKm04KE} zQLmQdrNwrnAL+c3712tiYg+6#rgubkpUd!9?9WK{?34#L^Fu}8ejnxc*d90X<1un+ zz246cas$yUccnW*uWAL!kYUT|M2(bvRib8AcyyJu9w86)7SoEt`a8g?{hx8Zf5i8} zjB++5aKf*uQA;o`QIyDd3RK5^JtaM8`Jj1VZn}N{0BRKC<=#}h+Ov$Wi+r1F4)W^VgUz36hn>u{izY_nQDl}B z^jpo4HuHVKHKNk9U+3FSGZ66@lG(+C_O73SP2KW#IsT2zVn92Qug~p86kqrg#+PBlCyXn+x-+7 zIL<2QfAQb3-Un3-=bjV!7l7Pwu)J0eAli8z;Re%zWVn6a21{+dU2botEJ$pw8q(q& zdBvqD_ghM8M*6$61_vVy#|0rQbJj6ErDN(v;@?h&ZSX9bcwR_ z!?{OBkk{2{him@;kN*G*M3$6euCz~3X-z_NLYE(9&>so7Mt3Q4HJ!F%mspPKJJeC! z0@k1h%9`aI`#de+EC*7(Gwk0J^dJ(TYb1o|^*uZFN{`#)LgXXsJ=3X2@Y<-txS>fELkgc{3 zCds7FwBuHnW3E-!*?xZ5`>F=2?tUw+udsXp-(0VbU@}$&2D#Mvm7?VQn-zN39jzzi zt8d+}$Ggu8_>q$Gr;ol&XB=Ug@az@bn6tvo?vh2yIsgko*qHm}Mw^NOO?r3R+ll76 z5y?`#sn1&v(LPwl*A9#1Pl^8J-*U;>Qv%!O7a`oPuR50E%vH@Y-cki9fR#p2P#+G{ zuS8^U_R+z*i-cgZceBhJyHB}xg8=e_@3(Wa#h;GzzI%O*B`I4gxLU11kgdo3SZy7@ z4RWco_E5f$Cq>!W!!D+1Edr_2BhD|s<|lJZY5dDGwf)x2iyk<#+J8vhO{SWCoX)`h znw1)U2X;D!{z}F17n5IoK3u!n@r;Mb>`N8Ju1~hkS1SJi3mjo8x4N#`4T8dZ`_$P| zK}{%4bsql!nLzl+V+pEz6~g^HhQ3nJ2Za~SPuw$(=U{y2$vErnHu1*pvRX5lJ?uzI z+^BJtf}p0=U!_2hMz#Dd@H!rvs{a6U@x`y)H4B~SB9tR^_18dZ;;l3^C{|b84QMG{ zGJ^4H1M2k9K`!Fcj7yUWE))>4MSgmXbjq->ro zKbE-uZXJ##ddG}iiJ%Pe3Qh9aJ|}QaXvYz;z!%%MB-}B(7DsK)PE*@tQZLLIE&l+@ z#iE=^TPjIO71-~sKONsSzU|;H8_BVP!tunnbx(qf@$VsFOS3N>r1xX-l9encxjO6o~0Tg#N~Cc7(DSKjO3EkM~BaZ88Wyu|OXs0YZ^OV$suA0F7* zlX>z>&f8Wx$g8sK_K@4Iv{XA*JMR)*1fN2dhN(}x&zJuI!Qk8TS_@xoNHmnW=jTu2 zRMCrg<@Q1G(j-VZHcxc&M(=J)J?mW8~cYmrAgX!vNg z7~TrlKR^vWJ%6Y1R@VaabBe4>i-&kU;B~eSzw|-8yKReVWvV0&_Y}(tS^og#^^mx} z->k3EYAtwc2kLnz;iud2Ms8^PmGfoNd`$P7Y<@psS7jD$@p1W$v2t}vpbRh;g5s6| z*w()-R=+WCAL&{a;q4-2OEn$;0OeGC*5BY?iP`Odc`f9AOUoROYA#KmjNE3-zsz`$ zRGaPKk=>$EEh|ByN>2Try(_MRoA!2ATQ$Vh{tKE|`qC|Nww91TX5RD1V^;H#!jC;L4eZZ<$wQ5euKc1sU zn40JGiUlHkOg4mvkV=hjT*~VK*qA+`ylN)t5@?rXsC2J-!KdY^0tX+|byUXrqx6Lb z+^#cjPcBU9YnJ-WRO^dU)KB8ANp6Y^k{2CP1MaK3gG1D!CpM)3Ro(KZrj}(=15$g7 zsXsqWE#IX_X*-c8zNC^mQksffDxWBjPL)*PVgz+MNlx}&=+PpsezX4oyVFVur*%NB z0)<|>klhjiKvQ}REhz#UC#miEX*5MuvV{>(hpvu@0Dw_6udb4f7h3!1l!_75>N*HU zpuFiNN$dxDoj4;+meCzcF`^_F2vVuFbogx)XT1=Ub0(;7r=ik!Q4wh&Bsl8UYO_*$ z=%|#&3Lv-8piXG815vh(0#~Q_=>@e(YTdSri%Ju@Cr&h1L<{szHFiUe8VBqCb!u49 zD$?TjChN`@7Eh<9qL)=!#@YlB?mH)Qq9mmhW?W^&4VA1f+@TcosL*gNOvNa1(GqWR zbl6fJjiCEUCq^_;94b*2sJ!JmfzW8)>Zz?|0@+Sc8x6*jR2sxo=^-u4cKviw{8CCW zRD}E~)aqhDWlHMrjh;DPEZi@2=1N}IuTG$-RYj3%8 zYuKg8-)c0fdULJb$%nwl_%9EU``dmV({FMG?FNas;VNkt{N;gNtLp}*~xqDR8J>!ulfOh~6?%Kbl2 zGEa(*ZEvlo0+H1xSeP9`w-+n~DJL0m=jF&(X;@QB?gZD;y1TWHdGT3`{jpM>DZ3#( zlAQ!l*L^Z$+U|;DWvf)T!|+jZ&v7V@)U_sjbh;8guWUF9sb5kC+SPHW%jE!dT>IP$ zp%yk*9)YEO#d!Po58(KG1I~P7ndMV41~Cf@a^>1%p^Ihi<|Cjbwr$dw>{)WCp`rEP zuClq`Z8F!Hdc4*?{{RJn;lAzSEU<;KwvwG2+Na>9z9RdN_`8ev$%^t1I9ReP&T-D~ z%d+4cDJl6^i4EGL$o->t2@klUk>A`6YqfUOJ@>WOt-alDHioUHE8ZM@c-KXB{ztl? z`~_$YO5~rAzB2NU3T2spKX4WYKHtFh7f|KXG?5{hO0!P5AL>RZam6~MC*27up#UF- z`t6)AmqPF@A5T}kSC{#}wpWm`_w8vm<-=_$pgL>+07Xh2=S+!K2^PVb-Z3k>q%K0; zw9JzgscTg&p1CW_4tmS6 z&H}p5enpY4H?l6>j%4O*lGl(FEG5ZPkm{5a=hNC!p{k5`3$wLp^xLD2R+~4?Z|`Mc zmO@_`r`yFoF^lEPn(*u2C$W_bdpf|KY3zZsB`I1EMNs?*$u$Y7`nKDBR^Q%7XD^{G z3~o%0DK=6wc?mweMy8$vx}5l9%*=}i@@s=R*F448<=o4QVoZkvEs`B&5}-_D_T$}c zuv$`^2BB03uBmdII??v#INh^$s%I7VQzvVD+o_rBdU|yH&&hNLg!vbaIae;`Oz$HY zZX?eZo1FXa2=j!9DHe!K0O&v$zsRE3W1AW6l#ptWp-R@f9doSxo!yL0YhAiFj}_W@ zMr#w5bdE7Kw=XpYo;{LRl^<`uT7i#6=I`y0E|POk`K#5Q(8^c{b>xMWvco{6Hm<3m zJ#}sEce_@mLj=Z@@m%-!9s6v_MH6xsY~JIk1G?n|u071qtH@p>bFGO)T}Xi>h}&#k zAp|5EY);41UWFWr!PNf%BWivjc*JoW!&_5tB?n(4wEVv{HBaHoW^3WPc^VciDi11g zW%^SW`@Z3eo;555G@{i(3t?SVPJ`#JXAdTlK1S%A@MCxs9~J0_aJJ9E*?idC*&%rj z)2n=b?;iksRc7($o-h0l$aajP(=ARyW%Qej1gQ2YuHoO))N1>jafoturKAH;vfL|` z-C;5ex{9=KQQ|+U`zP`DmSA%J2V=@?vJm@Mh638`P*5tS#qUjv1TkAaT%`RT@+Ey*iE;Sar>xKU$|T_<8mDB0m!a{MQ!l->rckB54Cu&=_?)1 zxb#^FT;_hDs;3%bc;0WvImeAWwE$l2S7i3sq^Se3F@j0&ZNpU|*38;xCM@)Kik z8GCMM4_YjT7=qt7i<1?6H#iR|Jy*|nmb?>l@sE>cVXVIFt2)#BjAG{MN*68?jVn+n zRFkr)^7!l6{2Q9Zm9cD%K5pT7C){6cjZiU?`B5mcnSJBo+Y5@30o#Xa6IW6xIxm$7TqfEUEB3ws|2OEB#+5N%-n*~)A1wj0?&8H@@mTygu zX>jdbQZBe&R?-nPu)VP!j-lK5^!t=(^Qvsrb@}hB=>V-ltBx>Gj9r{*UY`vQSteX~ za7(dneZ*AhWx|T8dnze{HU-Ed1-Ej0*vzP$Yjlw8OKx!DQ~MX?$5BI!p+J+~k|;p~ zSy*{Rthc7suKxfa>J*%Z2FOE=ZTq7>gNXp?sjWVJehFQSF7GP>m&k%cO0>Y1h;mv= z!F?-PqE*{p(oU6emtec}DXl7_9vd*ki~5a)+iBmyYiYl(EM-QGC299k1FeJG-Cn(P zb6N-LLawp9bV7jAss8|i+aJkpDz-|zBFPb7SNAv6N*X;z-nz`@FHNL%V`^6Oi(r4) zO}1$^gWXxqIpO?z^O2()xm`cM%Z@$kocN?Awqf^I+#y@kjdQH`DY}j-(v@BBhI?UQ zEx~J@xHr1Jm_|LuI3v+klEU3>#sT_~*R&J1+iRU)UENH5QvU$C zdKVLlva=sdBdcB9r7X{dIDQqnOPO~IM83zfAr7@2wvglNO?$%Bn*Bp+>4^&^+T8imeW)4;2ib3Z=2c;6v68#WfoWX-}avf7DwP~Y4{ zMLR^i6F@*w1pIZ^aGvaXPi!|s%zKWDlVp9h^_cnWomMydPLBJ$e%h{cUMyE!X6Y*3 zaf1Dx=LLO}t+EiNooz$VrDOOWhh1vcR!+^$IxbZv>Xt|~3%7r2Z##1QI^drkvggy1 zVpn81FqWm1sKi(`av&c>b~>we9Oq;=MKOC%boe>A%)Y zcS7&eUf%)m?gwpqP%osM6+Cl!_inh{I8ZOFr?z|sgJ_b?PT@&S4Jvl7yq%S_x{08^ zrM-&!0mpK-HzJW!cW|m7mp^Z;E69cdDHR0Lfn#y4 z_qX2eT;k5jJj`4x-v0pGHaC;7%Lw%69l+|j7lGm)L3p#BqaNfu3T37O9_(Gw4XRbD zOH}|Zr;rGt>%9oqa>#NlUS0zBIBufxryJmWmx|wdGe&(ddW}I{G+z)5(#V{n6?EJ3 zHL@Q=6#k2;JE=Q*opRVS{{T%fjoP((j^l%zg{Q3;q?A?3o2}Nxc$;FsLpIRiDVz-l zeReb&(_BRy(eIC{<}qH$X>;gYjS?;UgO#3N6CVMPhvYdOZ_13}2q;QFQj=b~mubpf zY|2T|IeG28Stl1lbyd>OlIDCvk=&xN&7|md1wtk%ZVz#|zoo@( z(_>9^?gNGbKUKq3%=vZPxeS|aLH__7O;5;usJ=2CaK7r^b9JneuZh<|nv`2OquAOG ziV|zNwUjUEDg8rS){Cq=UWrXA?yl6Pf@N)>5Jq(33p5Zz=pqYhX?qZ=6W#v+cTJ>h zLg%FO4s#r(q1d;}v^^ZjZ>Dms9!0L!2(Dl$SszGR8hbRE1l*u$r5Vj6~yQf;} zqDByU>BQ5;QKpa70X=mslu=}L5zmgEjX@~;D@Y`hvFWFop-?3WN;`lbt9|y>lu{^C zGN{;adIAsQs~BHJ9I*__z^NvhZ+ItF9!3;kF1;0{Mwogu+k<_oPfa-#sympJ8CXh! zlnF`&NYu*HRSHJcT`p_z@0+}A;ZGj<&y!N?A-QCMI~&8W-dUH* zICLm5=wRNE?%c6GNG6{1txdrxYVK&4KXJ^Wd-7JSBI1rQG zpgOB<$kd0zz}|_gY?O|sr%eW{Xl@$OB&jJC14jgdqD{c@Pn>%P%`=EuRIEP}pOePH zt>WvMKMFdeCe>>$dtbYfQD1GI?Yqv}M7b6<;xqb+5!t-3%l(6?R&o!Ce0vkdd`HO~ zzsd|#A9F9f&9yhfZ6WK1Q3bdta2Zlqj_-0%wV(v2v-GGdx|?f1W0km;?a?|S7~k%% z@>iGm*qe@J#8PgN=9?*~)}2*pv*bJw*AQ|orzgX2_nvls>jmB;%%aX_K0gZ1l_X1Q z;|#1V$Ci+zStAj{h z`(;Hbrr*pyKAKOai+uvOwtFX#JdcdnvirLiTb?PxjYDr8n{c-KR>2fVOO3>3^2=?# zRiITp%0ClLH6BI{jNI9fTN5 z7QW;M?gO_&@vTAY*H4n+G|n`}vTk5^i+GPC86~dibz`0o3U`E`+biRAAqb4|uejbcJu_Wn1sl@iPg95t4hOF)4@?{Bzv1k`*r z-!M2v<1xXX+yTi< z*L}{a8qLnC+CFWqM(+|*rr3;+(9i(>IxLLUXbGHL1QO%jHi+EnoSjwZMV2~>{HvxM z+9@m=tpKO{PMC6hBI@XL!A_2-6W2vWA-xxeMCR2kT*z@##{Mp+_XFC$a^=I_X}VEH z-4=qTrnDq~jz7izW9C9KiW1@4e)h&rf7TvHCA>suXGlxqmEd?zn7i(Wk2JJR0WG zJ4d|G$Oei##Qx*Ad`p)k@+IoQb+94NcHe%(@oSK_z_{6z6&%EVGt_9Sx2maH`hm8b z$oqcFn`Vg39oDM16YjOmmEm-)t9%t8<{R%6WO?J+N00ce-vY+mkY(HPtF)$N%Y^s4 z?i8Wc6tUS~(gE0Q^47w`87nOP8II#hRbX&Sp z{>CEf5czm8!i#WRcmy#m!kvN~GzDEgJr1gP<;k;~=_EDo3fJ>{X63D0l869w4+F== zRHcr+zm~RS>bXNr5r~r1!-yeWG`QSBMe+D>9i)$l(+)Z(wjioUsTx=$BaiaM@w{{Uv9mXMXS zRVU1}%ZiY<5$?3F(&~PspUh|wYL!He?0)q2sj#NiO17X1O$a-Kwt;CW4FoGA$39$h zXBt?f;ZkmKIY*5rpf^8@^Eime?5k^G+R&Oy@%B#M%c)4IJ*4Wod`xdOZimEZtzpc_ z-43I`>ZB&+_;1XpD~Ueo;x=g@3R@-vzIX3YNp_Km?Axxup4+h^J* zPW_~5tje}r(v-+y)}KIC*>KAYMpm&oGgh)9JcTw=!)^U*S47Y$Ny~E_(6NTC>Uw;B zOKHHko3a<4W1#Es`S>p5cuB|pJb0mm4=gb}7AzdzE>{x6}i$^Z|IcGL^03tCTt zy2k?f1AJH1FWKt>voPZqi7mF|xaCSU*wiMXmFvE|o9-Rk-+BjXub>{?*|HopM_Mit zc{9lzjbQS#$SjkOvisIMalGYsadDexpFR`M2=bK~2wRXAg09fm0;gVyz}r~=0Iz4R z1u5Nldz!xg0R60n0&8B%D{IIPE}oQ@V`uz_FJZX4ym zau4ycN_@xjNI7qhKXQVqd9Yz`MF3nozeh2y{ z*}^%@rjL$K{{T3DB7?;~+dp-lQ%d5$!+8*kG!&D1$>K88fm>JUnE4d^H5zYlIMi(* zIj7_mowu}{zGsc>dwy>Ii=w_<__@n`cj3Q}R{sDYu_kfeW8>Qs*#1#;YGZc<-57@H zTuCaBgY~c501|htb&%s6mflQ*#6O|OLEyWWFXFc?z{YL|`B*hS9_#1j)g`G8AzOBm z6HiTc$mkoY@@2$(T()G8RqI33@zJJGQddNQ@j3`CV#(~1()ti|%*`dVQ!Nc0RnV^* zb4ES?0NP1-Hs5GVyN&(=n&-TZ$NXfkKzn(T4v-43syl7p?U{-Ab>mMrd1d1rlbbx2 z&SFinVJi03BH=pQk4<&9KX=^VLRoDqNEJyP%AgfGCX;i3` zN`ijxavo;7oip*XS<>TR$s#3YgcQsp|bKY`Si+TozqN|Kq8v%B;( zQ4Jfzk2>>B+7U?NB1}Kh7ls)ZIX+F1(MgYpd0s7?vj!LG!)g(xc=N)Vm!tF z03_EC=9A@O^!CsZLb=K2jvL2P{G;Xzzc^>|@S9|el4R(XSP_4@UQuhfC`fFym3Fq2 zlBIUk5$UeO?ToR%5Z7CPT}@XM_Y-?^P7~Po@!d+AZ*vXiYTNV{{S8Jkj?QM3zvsXMF+)pTrVWa z+eNk*&c6j5#);%NG~Hiv%WcSWDYBNgB`dOYlH$~=_pM9WN~!%slcX}|kK-Fa=8uv^ z<};IIO$g+A?6w2$|C>p_mO z&FJ#>;nNpb2AK zP(&2JLHu9F9@*si@DE^~BdW{5REy56*&~^D&K@#z3NJ$`X zpp6&Ol8{Zd9$0Nk?FXUjq@pQR5n;tiQ;Y9V-+r2m0(sP;N}F~~D9GuxM%!ufD5Aep zK#(1E=c(RF&o!-q<4IMbP1QHkFwiDo1t00f+1CczvG~kRwV3{*+v*GN)8M&h6)LFFHz9dt>#PLxGuNL-gY ziTYFeN6SfZN{T&!df4ctEASctAaN{b#X(0x)2BRcGjAQk-2jrBz2;- z!P<$gn$yy&ygpQ9myaEIhlp?vI?2n}oz^(G;Gt2An;llZ>owP4Q3;49YVE|nuTg`>`xE>0NI|JJazR)#0ti z(pNao-@Hk-nkZWA5{Zt}WfgDuYAxQ6S2iv#j43CGt_ij0ShdAUp@%KCpwxj{)6?GpnP@T9Omu> zol5aFrIS4>5#?`T)~asddtPJ8Q}P>B&TX*q{kMtjGOspySqt9NFS^3&m~a+pN0y=V zf&31u@A59Sk*#ZY2c1f~$8lRrxE&*o$23#|dZi3Q%MN44Z5OUF{{Ud9lm|O@?(L;%t%PNS)4I<3BrsG^qwp_s(w6PtxloR! zfXWoJ?Y5<;CcBye@j7+$>}rE?jAPE~YP(tCuNnDe;kPB&`3C#Jn6ntpWXm>;hcV5i z!hae5*LEpayr-IR)Tgm0x}bE}{J0-9hMSkFP#cE4{p_HO}tE+t@Xw4ef9 zMJRkIPQNwMsUAApxF^W&I?B#NigKm5nr4=GXN;{j)Ln4-cQ@Eujzo4_0HF*KP?EAJ zY3o6(1l+P*jf10oV|03aO6!)M*};d)wsMQMp4Jo(G_;HE3_}&w>(E{iBSIlPX2^z3zoIWHq&sBy@hl>Idg=P{KEJf6yW#) zRec-1%y!QKi;GOZZ??9E?N)Bo_C{?0B`tslM2&~bT#1e$dt9yV`r!&I($`Cy{(I!Vss;4g5{@~ zeCHp*{K6f&$;4KB_9w{D-(9X#>8wHR&^*GMN=kdapm*1M_Tz%Ll-T;_XEH`~E61Mq z`_TRckrpu8vS=m*k*8{W{8tNnzvfN>=eW3*XT|&io)P2=V@|c7O1HYt_G_-hrO?Y= zy{-dKsH7TguSW2WBY%xyWyGdY+P#;H{mAzd6PvhWtu2MSkLm)NtC{wF@@M{-%V+U6 zLw8!OcN7^o;|n!P9`F950ItB+y5oPiP6#Bl7cmV%)p`T=sqQ23xfkQ+KjVI_`S%~s z?<=J|@8o=59_L3b7st@v8J@&##@!>dA?o%hn*JiX=A2iEoTf9>XX;|?yzkv8uvy`4 zHjI~|W8~2nj^x&Ct}EtD3Cl#=d&y9!X~ACFj-aII;egF<=w3ByXCrXSEB0K*e6Ley zF!;g8K3(x#OiwTkvbk-g>^nATw5n~gLeomNzftI<9geUZSDoGQ>(U$gE6}dZ%>Av$ z^4wE*X(cKgI)c7te5CMuoBT+bdW)8cGI-wq03-IXa>x%ln{Cl8E4ie}X$l}LdIgTX z4!x(r`PM5q0cjqkI(J_@J<0atJH=n=%n5+G|$nRM!~ymoba8 zeXEF;7lJmwT2vmMADGiXsq2$ieN^oc`;>c7ozj3!LPd9==rz%xlWRaLiTyj8YK>d$ z7Yp6a4W7*%cWbT9z}3D32`hEy6}U5UR8OB%eH9O7m;Wxs-ctpHIQ~uPsds(TeH^Ov60Cut-Uhi zuKs*6y7l}t>1wN%)DDWnVsERFta0%yPSS|EU9gvATomtMkl9~#H>TsD z)(b5oZDcNx`3mW{OGz@2h+uBnT0a$HxT}|Ld{dX&aXhyjcHw-utqn5{{WTV;-vH^gz41LbRCXr;ViNx+)t7sff~tI&4d2U4E7b!9xL%~3% zr3>yfsNSCqRKD5U=vYBovnvcjMoJHgT*BgG5iO$s0A^LKrgDY{_xpS6X-&BD39E?& z(JHU4H3sV1>vpX_1v(eldOMo->(nUAm+=T!40c9qjoG5U%$Edvv}WZcm(`GjpA46{7?yFnG{{U_Im&5flJJWM$Kpj-;eC1rI;HR?k{>voC zTg|p+Xy%inhs*eX%I^;w@?vwWI>&r^E0C^IVzBMrB9q#tpHhCbAN;1e^0sRdEvF!K zs`sBA&zq}ex&HvFfj#c5S?^Zk8hlo+Oj4?h;7f=;yM=-@&85V#pDCi7kgZwTkJI zwvJdgZZ zb#ZLqsFVg^&R!6f(AhtV@82=ZWChh5uQz6^Iy49^QPo0paiGnpA}Bw-d@Xb z2~pJ4cygRwy}~FK!qrrf_-h3;eZt(~?)^yUdW8zMNffPg(G*s12vMY@j)-RZ9Vw{L zno0 z>BbFHZs?Y+<%GOHbd|Wt2e=@BYfZfmu9U){%Tl+c383lIQsYs|86T)^r<&TP3O>(q z=zl#}Mhz56S`v~8Bc_(pReR8(O8C=LqaiR`R68W8TOQIWO{=E&YocyQ^iS-;oFXnm zA}=1@ZWp{+4M#CIg=k8fpJAE~yl6h0kN8!m6$2T#-T z)3M4^Z*Gaq-29UwR2y2Q&b~KoX-KtOR_wWr#AJO+M0S>?hLN>OPynv<)QBD%T_~Yk z5R~Mgl^s+FF9cU1$57G<`<1%#_m`RTGmtXBBy8R`TKE?ZzNXs(8`}Ew`05qT|3r;>=k;ucFA~UG#d0K-rOj{Td{4o9&ey&k%{hBM%6K1+ zCUG|sncXZx&Aj$6@ z_=Olsj(G7IW=WDt!?^0!j`RzKs?91(Z%&Fwxa?^y#9B+N(KSYID1lF4U(qT z38_N&0E4G6+1qQzxzW;rOU&5a+iYZ@itDdR{54E`8;JP_mEOFkjqw?|a&h*(>$mKg zE}@X4M5Rs-peWa&HPFcA?BH!)@Di6dsgD&(Qk5iQ81lS#XZ)$M}0QmQ7p4C-8XYiiFZwXyY@&ZQe^%u#%(xE8^rEM zhyMUuI{yG=(~m@VQz9gCDwj*-lq$dJ15+cpmCoBr4;4{7)Mn-oehaOh9DU01d{*ng zel>IEGaD@MkBVbG8SEZRc=BF$^*Pj_U1UlrORGagN6zj?uWWZf@wsy325kKWw!n zzTrSrp1SAUQdctjV~sUk3*9ELVvD0?Y8T9RhQDt-r^yaqj8~aK#Bd9%66NN+m3CoK zEs$W7-lC-UAa^JlfRz9~G}mwBe9@K>A1?BHFE#OBZ|*a@B6GxXH(Jxf?+It_E6I57 z7Z(xntA#v;yz;*v@g#Yc6)@pSY-nPMO<*ilWwT09k}1&YuYls8a^rO@dkeJ-bIq<9 zX%lRsm(*hg2>1ZFE6TTIL`p3z5$`|sNC~9?8w&h2q~w?5^aSS$#}^+|^c@rkf!v~c zs=YOZajNPi+d@~55Vq2ua8M4F)RsX(QmUu9-0h(?ZZeePilP3PI}KLdNGtGHmQV(d zibMaaK7pPs6{Koz-VbV)KDTGmRA`hzRWoU9;V0TVj|q$xwH1Jr5^iS~u+>Af}s zu9DRyx&%yvTKp@f%1|r8)cW+$Mx?GMPy4qasb04ot1J?^-p=mJKn zviUzwA^_B_PZ-7Mj*CT9ZUt$eI-?4~_}R#K{wJI-8P1ZYc;Awm zO`kIZ{{WrR`iPGyAo{f8fhW)seKnfowr}+PR%%+cJaX&_Jva1nN#?F4ym1dCrgsv{X$Z3TV?8pQ^gwUGQAp@!?RzgZyLX^Fhv9)y$lukXhLLK`0=?-O6tu$Jxw#|N; zS55;&lnSFQBBP-6(}09Ug#c2a)NQ9Kn_i@WTIe8$>!dlAO9cdm>0aSqhL)B}eOfG# z+_tV%_W}z0i(B*^G|YsgIr>m0qSBD11Ra4iKPu`&WHdk_<4chhVNEY@=0V&V=&GFC zqEs&0?kMe)-|U|srSxu+PsC`51qW0j60DPAr`~LdaZc!Yzo=94>82dS>~~|KNvU6H z>{3Y5Txyh8{G$|RG;>0JG!8X$+AcZ?}((_7!;$8p5;^Ko}*3{ zB1o%4~9=K}QOyFkDLst4Rvdqs&};kOJfRJ>%9h%(CScQzw({O&m8lZ?RNT z&BDwQ#D!3k+O|*$>S$@I$6+5InirP*4xw8?$YXz&;xOUjG%0R368NFYR9^{sGb4>M zNl7Lb8gjk07UZqobfui^gyMJ_y6I^h2R zicO9^oHY4Y{;8wLULbi(#c>sQ$#`<>dBkW|#&?dkoTkLK_BA{3usELjEmw7dOOf{6C_u%ukIgy$r1m6m0Ld>Q~yM{-AA;V-ko6~5#PMBx51n2)4Q-a^(*S_BnomKLG8SgxXBaz z-UnZ|!^87lh~R$r+gOIOcxp#*2k<^0Zv|)m^kZ-(>F#!vap+EfUfm@y?NPK?nQ0KV9HYxB%S*WYOidbpFMf6oZy@#5+0Ra z)ApZ}wrg_<@IS#+>n+=l?fj+5ckGvw?3g{?W#)o~rLL&b&@6*1JuQ&TJu&8~&xOc7_^%yo!{EYh8}RT*~K+ z=bKU>ci>lChnH~z0Cdg#LN0lOj~sKTSNzk6#}#68Uhe=xfw51yZyH+i$d(LOlyWTU zT!%wYQ3AkFmnsU0PrE{`sz?K%KMiT_y_w1FE_|Pw$6`JE!Wly&grAsQ!tt}3$13sm zNyrZQ6L7ZNai>3SyarYme0eKar`zS+>3d?G{5Y#ru${VZxd2N8F@iJrBzn@ zBnawVxTcrW;)K}-I`bSNT@!?YJm+xoQeakz^$`G%m3ERs}vI zTYPp1<1(y>R;HhCyQj@nWOVl#hzlD(&>gZOEhCnM^mt~bghWA`TAVPq_k z+^W-R>a#6sCWwW$;@MYQObYuQ`z(1V_blDv1-IDy&%Q(6Lb)G}V>*s|1H+q45p6WwRB99CGbT|3-pNvFwQuRpBcW$tR^ z3%Y!}rDSZ-8HrO+v8O)6V|yS#!b0TizK+ulK$ z$_=+M_3;ZO1h!b_5oaJDdbXUe{{V4M+n*4?a$83VK)PQp>4N-Q9zL9T%XAqIy3(3L zTrQ;vE!5QfwI3htrRb13fJUlrd0o6?bg%UfRme}a_n$r~JDNK`%WU@Ff@2t%UBcaN zRAnMH%I53+&7mk@0;DpUkO&*;k8!=Jzsb3Ut}X+rwBvs0L2nDAV|JHxSF7#`=NJ9D zUQlCjA#-KY`+dprq#-!>BXRAP^BW^&uA!gZ+K^Q1HK9{Z)d%^7E2p@R4a#CD*azHt zEY43T%hy(S0zW^weAeH&C+#bR@}lxCAGC7EC2`a?!fke3#Sq|3ee{A>rwb@$MY3ty z6ao0_bDhTLZcQf06yk!x-BIxCgP(R!&I!u6bvi#(L3?Uv1n;k2pI~ zY{qSjL0CYq(q2$nk6)IzSg&X?7NVpVpNU*=J@0OM_0(&GZTJtj@Kg&f&vShC<^9_= z%I+Cmz>-YcjJKt%RQh|xKz=}Lp;+T>twkbWZ^$lflgeG*Ii)BJT6Ni9Vd@Z5B-T^&%O0c%fT zCu8u@o~k)z5n|!=+euO?iMZmZsvQ#ms#em7IBVQbT`2WLw9*snO-fOaDQ!zXN^A4b z1rY%lODRw49mmk=oe_%B0O#t{$xd}widS_ur8E*zMX$Pol%|vdPzWCMf3OrGgkk+EJzTXW+bE>Db zQ5cULx=NHNfz?_7RW&S>=!2OcpXjtGfw4aw4b&-6Gr2^@nom6nq7ncdbU8V6&#t0+Vn^-ltRj1%XQ9vTdb~0_06)E{AWlI^N zGI;HF!pdW{CAS4gDkuQ&sQA~_n)XiRNBOJbtDSyYpS?i{WPXvPBT9%@^uVU2D6i_Z zZ~1>60GcjG5_&8AG-W4xaM>mP+2Rht&{DDS2Ub`MuTA?;LZ~?nxRHR4$s*>#k14>B zM3YpVOuLGYDK;|ij3+iRe0l@H9rQ}TsQDkqQt@}24HZ^v9NAcv?`iLM-%5U!?o>W{ zwYraSs;tI8XxdSXe&DDxp~xLraViz*vVqdAB@xOTo6K7Ju(`e=c4LaORK>GfpRk5s zmy$5dXIy!|b++4iK`Y#Tk-b$x}Ta0pZI}XJ;*m4 zN=89#%Gz=~S*+ZxPJ6JA?wStvrliqGCWl3f$o)hC<+Q55y0>+#<{jIMbg#iNphvi8 zsy6=sb)$8&LwWQ~knnHbjU53+uvPk&2k9M7ra8t#PT^E~Lej4vq@72_J+9onkllEj z58;(rZE^8jHe{LbZfwA9tE`C<`@77UX+6#)cOKA2+i9C?J(3pkVd4ZSF$ zRQruQ7N4i!p1V_4Pj0EBM>d6Z)50$*rgAPjX#0A37cIP}#(qqfTO^53DVv>|>eu|# zXzk5X{)mfl1ORtQGzNqL!o9)a#>Rj47HuA6gR4pY(lr|W z+IV)-l9y4s7wS#yHsP&TuutLgQf>6vpO7hj4tkC8y#{1HH7mpnIrxHQQW+ zm$tVt$#5yWYfTq*_U?aI8!Q$s-RP1HxVL4* zXGMM#%dO$i64-eP9nCEYO(?K6cWLZoQrfUqwLNO7a-X`3r(R0`07=a)1P0Su{Cqz3 z!d`gu6PP*bQ@{28RQ=xF3QULb>q7erR^NR`=xsqDs07e&uSD^lH)o22(t*207nJ!G z&GvHh>ThH+J6^p2J%)i+5BkdPh8>Cf_5RuF`>j{?ZR@MiJGxay{{Yu5%ft4K!ATq= z%QE=Ok#Vff41&aq6sBHMf&EP-V68N$>YD1CH)S2&tKoR4a8ly+>~3SZhxJC_f3M`P zkK6;u{I4*{ER!Y(vn+SZbSsuIHJ;Ph%jxbRDoH<>)*pazi1BuXfNDuoeZtF0$#Vhn zJ4Q59@BaXzn>--$I#(vy(ShW5^~t!}n;+S~aJ4$1%VW3Os)-;cSs!qC^l*W31A3iS z!+~-emdJ7UZYOA~3GP4Pc5}vzlKRB!whMBcrCOsu`h>kg+)7XWZT02fbMVbH zm4oKJ(d<_)d)RGcI)kFOyrst&w=rNhoD)8^w|U3!@z%tst|S_FB!KD({VpKxlkwL( z;aq}zP2iP>O0MDV9}2b4W1Vdd`^#@%7c~ z+Ct4TFjAeS>pI^u6~+w5VNQVJ9fdWQ6w|FZgH){xK?EosglRI`vPSK8pr+o^!&c~5 zK(H@6IR%S$9gOmAj@QbV{vkL{w#m4}Yx}E8H45#LkSahYw2@zl*DuNYM`-=V+?d?% zv@3Cr_irO^V;2ojUgjoM#iLi{I%Kfxu|YML0)Xl;IA>RqlH>}6w-4In#2)vim%%)<4F5_;ZmF|)k;$|z7NRs~m8uApI1i2;WFWA2l9)5Q*8y<6|rNSwsnD@!ja4;E#2w(`_yX+n12Q*s>s${|-n zQmL>$#bvHRXuCUp1zCJs!YO!?Vlk5Q+_o1kfJSHN?h2;K?`-7C(EQzh12jq zwzk_E++94^#H%rjaTf(~wd%`qyyaIMg7a(zxJ_IgSM1zU(?i^Z(Iim%>lK#HAe0y8)-< zxy|Lbj{Gf_TW(p`AH4$}n*JZEaaHX@DFfS@s&@930ta^59_8?9i~VSa zl&HSSd#4NI7WXcr#*3f~a(p#qFfEVT+_T%SX;FmW9nB<)R8;yMaodYPBc*)7dTI=} zp1;~q*E^M-Xt_X=gxzh`EzOE(N3Fqw_F4&eLP^X>x~ zZYZzW?mMQZps!6%nSt%EbyaBp076O*g-Lls*z@HuFG*_S4)6eNr-~MpD2>Gx@zs5( zxQuUYbXJ$9)?=~sHB~0_;^!An&l|}gr8dUgLH*7&m8C6Ls8AKyYkh0ZF4+dv%PzgG z#y;9z;cdAB#ea7Yd$4z<><-`qAzDCL8f**Wdqy@sK21Md0H%i%sQR~Or7C9JbA?vC;S zqwL2>{-HnRu7}6SG5U+0Q~78sV+!;ZmnfSWRgzxCsUmAO6xYz2DX6$ zR(cQPr*|g+U8eiHa62<%S~+^d54lm#W!2Wxx|&|(t61nCxwKj=dGw|D6~G3GNg(w} z=zbb*M$v2CsH!@G3Tk%KLA6co){D&&bs0&jLDH(2A#M!~bZ8;85iRtT>eot5tkQ!j zimNmn+9178>KZ2XK%uoEHjJb6jTlTGsCN44#&uDo>Xalu8F8Yr#9v|dtNWQf#YM`U zw&*FR#;PNmsxE{m)BN-RDWXxNw!eWtJxGs0lIIGe453N<`fb-zLlj9IlvxD_>H#1T zRT2QH>ABRQ9Zre^qzOSSufD2JZF&t&WIF1iK2c2(fXay^3W3z=(!{}fdEkk}f9h)* zGsk8#h%Q58BzoS2IXN)v^_CL7iuzKzn~f3rqtyL9)h=u5eG~Nb^*dLhJ(fL&Cn383 z0MQN3Ryrg*{eav>!H)Sbod*VW3_SDB8q`6vm zZB0#y)ihNz$3sM%%#PJpv>JaYr4F9ln0#tMzwCcQdDu`CpLXUg7@$*S!3Z2a#{Dz=-otM}28Q zOAI`PwH#83t7!(GJ@ojEn|z~H4eyzQXm^xWnhyT}8f*8WeA&bNy}*QFUC9*arsz9SnAxtdxz%^LmR z%eY*i^NX0cpUD3JB)MBK$2iX(;0sS5saD7so;;w!Zlc*CYw^@nig(hwz0^$@7w1aw`1HjZHFHk z@*@s}h&TAlwG@8gmuQp+lH*CEou-77-L7?;;thquDCRz6(5|O|<_>c`dEo>zxEc@V zugNEK)Ekf9E0-?Z$;bFRIL<3%Rif99p>dyKE!(ZsQ|-D{+LbNtitXvB?7oi!35mv~ z$(x}!C%J9F1T~s}Roq@Y@rkT=9hy%bA`O#I|eWK`#qxR)PmeSiQREX(} z`rktURH(F+_W%au(_yRAwd~Z26ouiQO7yst#}FI{$a)0zlvFNFt5>nYkB2)7b|0ycO@rgVz$~O_ocNG0Rg%wYVQ{z z%igrG=-#V6nYU&K66QIFr&aOu%Q>1Y@~#u+xX`*>oLh;A(4rge+yswAr=S||*Y~Zq zgf+SvFF$ddOxLp)yP;Hm^K+4RocyQN-nUg!qH9fa8|$+7l2@Sd)=h#S(aI1YY#i3|GBARvG6Tt>R zM&o@jq%jgwiRwCP6RSlbYoiD+S&a0DTVaG3U37%D+C@nxUOLAlNd|=#aUh5{ongD2#aiXLd%Xd8NbljQnx7{l{$+ZyCeM~iBeAaKT)b{drm&0MMx&4 zTlLhWkl+!dMN43J3ZIG5fSHnI=w-V8oeimjF&fC{W4GKVTQm+nYHarVH#xEIuI-JW2Vp~Tm#n!Ih-NGle z#Gyy@s)nQRrmHhpxn{Q_H&M#l1(S;2hr`|Hg`Jl!g#W<+zsFjzR0zA zIN3XpcReepymr>=I4LoyQ#oALQ%$it1A9UW+ zYQ?UoRnLsI$pT|V3sDB3dTJc4Qet&cc4a7Rv!*JRT9ne_6f0lLM^uC`>7prZS=yiU zjS8hv7gS`fr6PydPkM=m0)Y0`fP1QR;#oj@q;*b?i0F;}{zHw16yfYo=}mUgP#&ov zp>Lu=W`YY^4M$FIN>OFXhI`_?TPLbIr2a=iCtxW+$hKT4E)joeApWV14T<=a>Pj$3 zNsrlOwu1CW1o-`Z#`h`wM^U8gQX`xcOXPMVsOWVaNMX@xodQB(i+vV})tWRGXdtsh zP(vj|j-A?yq$3Q-dOE_`bxkaEQb`}>sf4J>T{o1-NN(p5GZX0`Q4WvHb<#=;KWhd+ ztSNrd)wk`QpgZ~n4YeIBvPG3|ahm(g6d%H)ub~=IdxAq_X-Ue8q2KMVv9$selTOkP z#=4TjQ&ij7r4Asp`GX)dCI4$O*ShR*_&_tjckMAq5MDN@=iY2 z33e+Joe}tokG%f?&xdsDYwYLb=OI}oWnL_DXBPRbinQr&Jf1txZJ*uNPt*|Qrv3Xj z8gv2*n=X<+9khn?cw=_i+UU>3e?Rz9<@WkX8@>m&LGU{MAM&X$F8E947Y9nXx6A%2 z@-H5>Yu-)H;fP*bwty4dN+-Z#L{$Jfl)7o_O=v9n6_nqh!2SOK)`d~OjNZg)vDNqj ze}z;lgxn;|nivy=$SnSV+uq|TRV&ddAS$1pte>!*6=WtxnjVTU^*TvFgpD#WHSHAm9UxGoxFk-+Aw(%vKBGq7i9@PVg&|EW1pFyo9V(>9Fa>lU z!28VB`NIc~6(&@d=CYQfRMB0=gjev_9``;1EQtc9p?VkC`_OU}CW3`UerM&F3ir$!3SMv@Dd9@dsTGg+(M-_`|#x0{0qdX3fmzblz1s z_C4RKouHb~bolF5Zc50uAw^_7Px6PB{4eJ`FUXD=pBWj(6q!+luIC*x?N?h}#R+O{ zzH3U{viee=sXL`XDir{v*WX!s%q>%Kw`K`9bqlsPe|HQkhYzmIwiTz~LK+yP3X=$)M%mHphdIz_Y%_tuOqxR-4rHVVY=*Qm{Wth+)|2^ zKT!v5RovrmUe{U%gIzZ#EH3REcH6@t1-j958^&L}pNkxIfq4tXUQqaHE6JWD@=rF6 zmSs4sXCM^g>20{|mLaz-Y%RdRXWe9?iVjq42%y9JLmiZRTtPk~L$Aiacgl-L$bIp+ zySiky<@w1w8elhW;OR~IX;j7!I@!N~n*ePrut}&AVB+*T<>r_Ucw<_uYA9yL68L*RN{)&y9PgPm&id zD(8L-<*zP0G>J2ac$YoBh`v$yzS&DC!ZSOBbhww*`*IN?+i^c!cL425%>^skoVGI? zJ&sgGb>^?>C%3?SKQa6|T)dJi>4SzS~{#rsp4o!PccUtHmk~9!bjD6IxDTTN@k`ycWS4`XhSXHJtI7}DO*M!`; z8^tdbxsv;LZQCCsc|`&xn~g5)$Y8k#6#+tg-o+}dxZ!b0Yi_hBZF!$RZ!P8ZSyBS^ zEw$_xxc0T@QWQDEcMlFYSCR2N{@KRwTU!zClhr0G?t$CxxROl@c0D@lMRjMI zo)1N?qiISk-G$Z_lX{PBO?4}=d3W7^fcdJ^jbq%SfwO*zimTnUKu>S5!>LEN-Vn7@ zWb8LOea+gn`IxfS6_a_Np!sy$nPd+*OmvH7Wm~rL50mg-ub1xRgVB2DbRP41s zDGdneZ6~;u_fzxJF7|!+wuxqx6H(n!WqresD#tQ7$P_xSm*2RjGdazEb9raU#wx9D z<(aL?Cf=X}k%gtUY@w1<*SGeEdh6O;KGa2zi~*$=i8F65ynTFCCO1hyf5s_YC=?Ag z)}hm$imA>Bish`7lCn|>pgYj&KB?auW$-L0X1f=ZUR(Euj)lj zG}TE;k?A2|Q2a)gilJ%$0J!LeiaA<)sQBs8Aw@(Qo+tE*eDxrjr8(7FdyD@7R-1fv zNvZ(4Pft&dn~e$}mim-7H9B<=0T(M$g$SV1AJG61WmN9lr8LS?0E+0rNpot8DMd!4 zQks$|n|Rd*=jcC{qPwbeBI)a(Q;h1gDLvh(6*^55sMByrPkXARKa)!eF6FU84{}nT z#ENQ7LRexZ=TFZ`NkrHBwH+GsQV}A{M{NyjT__ZwQ8jfsNk>#o;!Qv_3fo4ayy}W7 zod$^{Al8oRZ%Sw-1XSqK)@aaKpn?s4nnRMBhC&LB2FFSX1f`&)FwhjDj_N-$H791( z20&a=Bkz?HzvrmWbxNBX1reAq)gXr)CANm-{B)MKskx=5hT7Oe%>xR=RK~@+?=-vQ@Qo=yd{)Q0=?W6ZjoAl0r1o{i_5k^V5uYYrzUU z%&SOB(-xU=Qs~`nY_E+qq?3WTxPqkA8HDTa|3ihgs9W;<=R-pNtP?xPt!hY#(i*db+4WeW1w{pFy zl}JoR6sjrG+6XHN>OkK{>B}2Xqp^YJgjA=l9mbbj^OLQdYmLnlrQakz)sFG)IELji z%WHWrA+>!}S`kp5yVF(ED9vOGJHBrt`)Gm%J9&+Zy)9D~*931`_z1uVL>tzw&?9d$d#zRuE+bYSSY#f_)e+>>-@ z_nlp!{CW?ziN(*HBbE5=ov)9w{Eo+%%XSn?m7gre=ceIkxm%|sxgGYLqEv+`BkOHK zsXJ~cO@;?7ZtcU|2r4Hgn3$3;rPe^g}z-!uyl_^UIM_$kmLGsgPN{T8xb)zDfpAx;{o?5+eA0Sz> zSOs3(C(vg{NuhOupd3itZc|-UU)y@I77?KfReO#P4V8wEZln5r>*|Y#Idao^je+uh zBRltYTasFF(1rezq1v>ry$XFsysgYIvNXOYjXN&!?XM+dcM;Cdy>>Rmdj9ikjQc<- znN2q1WFFvBw1ctNnQp9%GM2BPcMR1Sga1Y4P>$m$q?mO_;K9r`hIeCNkBkah}+5J_h0^%{J$Km&{k_^+0GHgd7 zxZ}6$JNA3UsnxY`w@7VGxP`W&l1FeAPTLW&^w+$gy@Kh1@i-Qh^5OTATD-+@(#N+# z4^+?o&2eq1G81ok*zo{Vxd>@%Ln$VKPAMRSf_+YymCa$HG*cj#)(391>WZ@B(7lOG zetw#IDH3F0AxSvg*FYte_P)pkC)cm0r0iqHp<{CJ0N{Hm>k{Qly`h9dnzUan%#&MO zpQ!{Zs0Y(o`|dLoz?ja*c!fV3%k`!~1TKbmLwk@nz1#YsaiV*sR^&>(ZZfMP+I=`oY?VSe8WF z_(^x_3$x37aTB$z6l%UW4o!@59CojlJA7-5>o`J1q_~uXIk5y1K%q3(GRj%qTfb-& z3%c?ArItc4_c#*K(LtI20G51rV#%-AW?RQ*Rl?5aY~+O@MJYRi+JK=H_14!Lz%rK* z2_bMRmGTdF@RoP3g5okM-8ncXEwo*7zxu0$bJp9BB$QXh=#Z-45t>hYlCHO=*s7BlwWRKy44(K-Y(?|x;oE%sd!|Cgh*1&T@FTc zozKYn>#A`70C=L1eLeGj3xIRK+q0VScZ+!r>qQ#y58QU~h+J(PJLPSd zjH3~}qT_G4r7p82g^j@#0)U>n^XeA{)AKc+w%|&8@i7 zT`k6OV5kp)s2Z`2o~ZJw*+2Nd`8q1mQrSbg6tm@eta61FE^8LCV<4T=P~pT}dS^89&# zJJRTjjq=IEnrH9TTz2mcCHTxpnwc@BAQ;bVYJ<4xu5&emg+&)?hV&9BQ!|)(X|5{5 zE_o`hDYrw19oHmCb7Z6;B}dc`O-&H4T0rPs*+03b6A zsaF-wvqzHbdl)LJ0|;6AhU9%m{59$`3vJ>I9hV2<7`JV7@@n^*{mPLgWSVLlO;*Up z5S1StE{T#C)otthFOi-*t+$dV{{V4-q^guZSGP*jSdFGPmaE5ksNlX%xjzlK@%D3l z#%HpoUAI2sP!+XX?)p#WH0+B@kC$^3Ev0=mJCmkb>M4bYLUe?dwySi0p``>1Q2J6z z8NXd6IJVN0Ycvp9pn}Z=7HE(NWUF8gUyhG)O}&%|cK-lZ<*6-ENuoB_L#LxmN!19D zeihTo6Z)lQ#<2-=QJA8NVjZpx!bF|LC{zEbkp%%JjpNYWZ5o9 zku_wEV2ysCHgtS`5&|K>Zd{efOlASxG{|3K2VT%MEw7*h!B)FNrhy`=Q7x8P9@}d_ zSljBRob^IUWerLeDjNMyui8KdwN$CE!$@HS>WV^7L<-&ST}IyxI+y5>gZB-3nrWuI z)fr+ih1QfiX~tDBT8=d8Y7b~CAI(8sEu@sgnW9x}ii2HEW~D)qsOl9@#OgqGdZ>;G zMmm(zs*_J$I~!C9nYlV!m2th!Xvlg^=I1l9rq=4-mm$)W(I`)OTGc=(0QBjr21?fa z6;#n4-;x$wN8z-!Ji^&=K$g&?sj{uVm$(X#O+MKNP@pi?;1xH#an2jQakc*d1D@7f zevJ;2j~-ABK1l+muxcCUYm`b_&^@mCt< z5xLipe1zkSOD4Pd8y&RERjeWwNo!M)y^`cW&;f)IMFgc?MSXSW{{V0@j%Cn$1p`X& zzS-l$>|Gp=-MoGSz<(dTRi0D(738paDKofpmf__bb#r~ywC+HKH+Z+DN4O>5W9WK@ zqO?i)*H;)Xcr619Ufn=ho8i34PUA}mK%L1@^BqFY-NenYlu!f$0zG( zDy?NdZvOxkvTWYx%aAzfix(_NwobIb%pnrPti!ms?Y@s`G;KhSMG97$*<17cjk^PP zR0Wj0;28Uz-db+z2TJQhPlZiBWmSazyzgEwV)yP{eF<1sR-3SO;$(hy5tNWj^5F zbDZnR3$4QxiJM%*J!_k~yj=_n8 zc^-E!Kk{6y)^_7zcld{8!`!0va=*`U*Bn#IbefY>s5u+Q%=!hiz~kYyhr)=(mlfSVr;_F}W)qJn2w_{R!hi<)f z5Brp;+KUlYP-!wLE$F;ER=sr!PA`1OQ1=f>J7_8UQ^> z)F-$%s$6U?Gz!>pCL3ep%U}N1`4OG1`{LFm09Ptd-6|zvn%<}urcamJPYuoCPHss=zs;H2J*Xc?@=m6MjXN|OMF>M1?fXZZ@b1896 zfD}zBzNH4$SG%I{^wJy>S=AQPE^QQ*oi{o?g&RUHw+F_Q(K)E7wT&H87gZ0SXqdxP zx4ZzB&=P^#2TD->8Y)p&xRvQ4N$vE})gaXrruqoN_dz(dDg&;N=TR67QlOzvj+5#H zU{L~h(?BU?pcIr;ZMt;*LP#YZjXBumG*K;%sL_fgRW5$zw_5K)sDswNhfVA$w-ims zg?FH*O%}q5x+0D?m5sXk9T8%96qPZz6-OK;XJ853?V#pRr#Dd10>jE-V1*IAX{Q5| z&R{82x*cr#*RS!=FA6z2q{mzA{5>@tI06kTQhFU42}PM>xOZsBL_xZx1iF>ewDr=X z>X$?=kcx!~+>`vhbfDsjiwtI0xgDf*)9i905_;f@_cyo8Nit}V)uBU)*+?VcbYRsP zZ5H9q+8S5T=}9YnA!mrMrrK%I7L#F?(?(21rr{T=oNU!eA2jtEuQ3vI zPX~&mUg4xsI$cG+pGLDns4UT-vqXa33lyis=+Od&&_;$zd%bB*AwG{slOY=+%BQDd zw@p7iEI0)oh$qZdk^6=V3SaXh^zHJ~LsE=^L{ZqtBl;^2r26zaYN<_*s1Xz?&=!iP z*1DD-K%8VXIYwy+v&LEqxN2V=zWlJVzUu+rh6=~LI_xm#wsNwv+l-(fuMs`neU z#xAiPdK}p9cJJ-Uh^C!KQZ`WAUA}lCm9%gLdHT}oSe>E%OQBpT_h|cx`0x{N;}0%# zFZy%21^g=^xg5&K^JqJ=sM%6yDGe-rDoZXSZj?LgF?Yo5Wi{9V_-Xt!{Fa8-m)=87 zuIb~i=5_qkPtTrXau1bR>@540F+4iMlrtPflXAqdnApl@q%5z#TTxtb4!8+4R^4?j z#~8>38ch@n{M@%vZjzPwCMl4I+Ctk@Rtt)(en7Du-0Ow7%QnbZw5i5b zn`2n5_Vo%Y{iU_GxmM_M8bg63)g@IQ0jhiJoO}F}Ilp-W_^X_JnHW6Khil*o{t6At zux?PtrAfKvJZY5J)gfwjOq-@PeuN6vqO~n+nGqY3YDqenGd68t0qvmpkLsu;!CpE- z?T#e;5A;lk`9v0vwJt+3R*kz|aSSW2{{Tz9r&8N2Zyr|y6nmUy#2P)Nr{kybOD&e} zo5l)xrH`d;kffi@HPh{QZnaT9o4nF1J!Xn%7bznT{1rI@#UU(SjXyWMPUT`qEQpzLcfdDUCc=^g2f+)4t~{-Byv>sn^Pya^v*YZUFZ+{{Vy|z+r5B*Aunh3iUv*oIb_8I#g^)BQ0I7|_SoaRDr^OXz{;{qq>0y--ar8^6-9#eNb9r1R(P-5%KVqeJg1N{#C!h$Cf_c(20+AMq(*La-fNTI zODQCf*jt1od^O$eY#D6`ji*2;xV6>u=ajtlDTMU&0b16ZXgQQbKq!lsAg|Nu)D1mU zhQeHkzcp@DCZf6(2}0GwM3(c5VbW!>cdMJ*%S&bQTHQLh&{*pb#jlIM3 z)Qte5L2;rg;???`{{SsBAkiE~ffaQ?kxx##XeTOBWuAjZi3XquKP^Z^fzcp=ccOG* z08(?G&!bGfL`6y^N7K_uO%i}tQ=&@?T?A;E>U5x%7HH5}pn||EMu?!FE5nx;2aBD( zc7|G+Imi+5u@lC4=C5V0bz zLi^U9)|0<+_0pLgQ*%rjD2_{%Qk2GL=}JcAX++U66m`_3lA2WMwvANKR)l)!rRt14 zF9k^0S5WAQ!5K?*O>3!OCJA0?NRf>M7TQX2W`YZ9ZCwOaUQmDT8VKr8sYiNMO}nYK zr38({FI3WyXsVBebe*Zz5PBdqX~nfZjT4(xp6h)rC2o|eoiv8@NPkse{BY#Be085k zMfHFE2xRCs?3s;&9$;@t}`#DDsUC@I*|w9>KnUZiwCs3!9EhAA}YRe2+ShOAXfN*$$(YQk)p2`ffdOjXEo(hhr!$Dju{tpNzV65&}CajP@og$V(h53hBNLo7?lu zak(gTe&vJ@+{drfg>Q%3xxA-A(zEttxc>fA>a2^N)e>b5p-p~Dqy~BG6(&?1#>z7 z0Lcq}P0tvYH}R#aE*E&knXyc@TVh82yf`+Je{{Xc4_XS;*hC18utn-A1dlr_^JCuaF z4MK%?HQ!ZPekUBZE{N!RDY!mZ=(8-jWgIo!X->U<)j@fb#~d$^n9O*?m|x|4S1LkA zOMbVwwK&czbGkaW-m?T4FTQx~ZA1cHP3edLuUI@#WlL!?{W^Ezi2z zCClw94K`E)mQp(Qjp?TY0Xn2i^2Qf1d6E0|vfFIB%ZFyUruA%PYfM{+ZA}N828mZ< z$s(iB=~J<_pi%7ZUn45 zg{H$~8zVNJtD~Q9-zb~GZ|dUAzEDOzkh@zfht>TiCMtlVbM%GKQ|I{Wn{$pg^>J@# z^Mz@PzjiL?)EdU>SKxep-_3no`0L9ydzGE542S6KIwlxWhUicPYsI|N0{Esbr5C@w zs^+m=+Xh#*iqc7ONeOic?vC|I9cipq$26|S;@3A`g;#E8@lT1l&Jy3r@p!UteTqzL z&}}J@fO?MLE3NMt_ctkvqgeB`aNlq}gu!R(`63>tk9E!ld@H_q`JPD2@Q97v@aa+9 zfs*5rnf=Q%V7@z6+o@O}{6Qmab$qY6=Pho~UA5z-7oD(P#6OMSm$jaxF44g2qJTM% z?Wv4%W9!L$eYi%!?=Li`*MiI{Rk}ljSK4LJNo7?V0t$XwwBtVW+*wj9%9j5C+^YG9 z?b<8g>9IsL-Af76?(q9)xm}U)_bG8sRJ>$0 zu2aXgAz@TKq?%N8)?1qM^No-OMqb|JEmyVv%s)RJC744YG-z^%In#i7f5zOcc=smb zPFu@_s?U)yOV-PSJwuE>osa%gL#vK0&8)J}?u_f)cONxb&%LS2aod9x(q!QSynBClW9gG_;kYU9G{~%3Rwr`E zGC~nL1R}-3mXlxkqzWZz8xcdS&LY|N_W%xsYdL+~S9UIRpQs8ZZ;$uPM%81*Z`X-) za_pLY!7St$lMX(}Tm_!mklE^yN*!JDc1rfTpb8BZKieCNcpSAgXYI@~fF68Tiusqa z9FK@y{Z*@r;cmKh3#A?xd8cL0V7ZIrS5aomQ(vLc+SJ62fvUZJTHwCr`(rhXzPviH zuTlFS?}Rx>MZL$CQK72pTauz(EnfLbX4_{rQ9c@bR2C_{2KwUcFKemMdVXz>Z-`#n zZFa{k33k~yA(+bSjKMW6B|G+=GkHzl6zz?;4bfLGC46b(4s*pMTl4&0HOXzHulwQ} zLsHsOQ(pACf!qkzr-ySKjf}L6k~|g<+<$Iy8Jknsy}X@!hz~$ho6~JvrEP?xGGWmwL&${UhDf6-fL~uKkqS=AXq`TUIB1a}8z5GFNWD-Z+H{|7k`Hk-I~8=- zV&eEVTSMx*sa!`Vke4rg>3?H7uaZ~fszb|pYWkK=uWn`Cs_1aQ7}^Ekbt8VdYPuwt z%GfL_pC5*n=}735V_od~cJjjI*e3DK-cNj}3R+Nws8mq)QioZrv^z~xxYvgQ6)F3i z@XMIqY{q+UiS5C=A8t`fXaN-!ha^!=`v z?m8VycVxjU8(WH5uEL|ZX~sN+hqbg$1{O*vPfmuNHDuRRDw!KXggt&bQb1a`=IKcU zZPQVzK}uw6%8!nfQc;GW{o}W8!`Dp!Qp*A`kB0iOjEX44BVZoi>5zvM4`PU@2AdKE zHqsl_HPHMMy89~@e&E`%3vAn6r7`0r$oB~?N|LE*YzNjHVF*vQrPQG`s2d#yR;nD$ z)hD*IQ@*F{P~~cqFzgofm$h)25=;waveLtk&biN@9ur|L3X~(=JDy0wr+>&)J)&LX<;&m!DB^`~ETT;!Xo71wc zHoGIut=8I23vmO1P9X%;0!4d%dYJe5Xcap#(mHC;4c{KCxqMrmGH!lPEHxFjD`^k97j^})-3`OBAIyK%Uj94e3nwV!7OdA6FB>i49q`ioDq-Q;svhJo zanoGGEsOd`Y&x!|iOe?kF)MThO}447HUL+xHA^T3T{c})$|SI&4K_O+Ry(Si$AsXA zBXR!srlLAD@RwRARk;qWX$j~^(MZ|_G7UYCmp_)RYRhuUGS0iZG-rFh3b%CEDxMb0I$^aJ7l z06!kuX{zIE?p(5Y-qR>k%5h$mJx`{tB$i93?P{G9VX=y?G@^Vf6|&)0<8#OgT%NSn zG#!bqtwu5%7H^YR9?LE>*Eu=8D}5QW^fn8y;7w@@}od8{1 z4~UA=FB9CR+~p=KeVtsU<51;x)~mZPUh4Kt_uVV0I?P;6S1mMAfRkJ!VWLx55GJIJ zLnBo9YVZ#2x>HFany1>-RpL`hX{8~9L{~vP&a2RpvJjC~FJzC2J83U?XsVeUZgZ#< z`+;DVyA&gu=L6Zd#oisf{0SJzTwq96D=txajRFlp;6Urt%%YpmL7qvO@2CK zaZZF1y7kkIZ3+jXChMp4^d5&%$xk$`Sz*W4Dn62Qpu&ov1hR(xuv6)_g%npht9=B8 zfasE3+O4!nlvhep5KHT*@9LJ-=V{V)N4a4X&_EOlmqK>wq}4dpp;;o7(5hQd@t-Z8 z#UG)29T-a4-2pAf7;#8f>Hbyv5vQx7OBx)2MAK1IP&CU{jXI22&+0%_AqqFY@;h`P~5Psd41Zm1F{ei|eK*(+L2 zQld0y(e~yjY@=G~Z%U$2L&;DZ{(3?LXc4p;*18o<(HKYRItbR?YuthP(C8yX7Zbb{ zp!SsdY357fk?EpDsVX5}oko?^BrV4K={rg_=-WzYq9wO^00Y0zN=`7LLC{9#)ftN} zOgEDBXOzm*@H-#GXeEFFQWS#P)3Hi{2lyRErmAZQqVrNyD`1kM0P3xP z{B%`PKA+;9Qn3lP#k7f2Soc{nxr=S`z2r4KVsTVHd+}DqwVs{*-1bFdt zcuBcqZ$_BR#JLT00J-ef+g|&jTa6)?4KAXQHJjMbdetpEgIF5j8|Hs`Tj|K8-l5wt zX3+6WcTkr7`O0wv_4XhTpMldal$X#cLd-Tgo~j3Es*0gI9klx&MG>NjL^`oUUA`K5 zsHAzIO7_f#>1kY*Tjo0h_sx3~!wxN>A&S(t=s^K#^`WTJUleXNAXOZ9lCV$`0zAZ< zW6A#jn?l=CzfzJx(U%Z(N@xeFKD%9|;uRL?p6Y&;B%^=hq#06#W1`0KE1J-yG1(zK zewRW20B)9p;70AUuJ$K=sjVo(QmC|nQjbcFGV0Yj#?Yw&B1DMI$%f!{wTgl2K1WWc zVjzT=_0B|MQ0#K5^gHMTB}s4IA3GvHXgv)yY6(f|lPTbKKR^B!l+&(v( z?Dq_N#;+x^o*2h&GFLX?j9O&GRki7^iY@iUpB_tP#n5*e3e$a4-tpTTmsVC(_^Svr zS5OVCbppJ2r~StN0CkLZ190*VTE;n5ETJeer&tOj{})}9pK(tb;>5<>$v;Y)X1ozB|hA~+Wd*tWS-tb zUnATUwtJ0nN;!a)VW4?~<3}xc!^r+{@=w?!hEEIe?oDgN?|IFl_~o))!y&sk(ooY8 z&8k1XhReV@u(}}qJJ8a)Tt&=Mh+>@h_-IPJn0#{B%FXa2Lx?Vd6e$ z=KPyG@%w@yc*Vq5i0~T6cuoX_N{wrFJ0%hw4!87`NN|;qI({|RLBv|g6m)sqd`jl| z%gE+`k-I?c{{Tf>8w)41`BG{JrmX{29@RSVCTnlw4o}1R8u5nP`3@!%HM!>pyQAH- z-3k!2h9x40xfM`VDbxeKHA%`a5@f910C8sS;cB>VC>X2e&RQM^W!np1-ZRFh3S7?q z*!*X7p5DnYn}j<058T_6uQ-4Gy=B_+W4m=e_38(E{fL>qXh8di^h^xT?t$Sg{{Z*X z?LTV%KCoo2H1l>`p@}TUnF(&Rrh~~;NND!%pzYu>umWmmyxDnp*=2Kp>QLR41M1U+ zRYY^DliVroRaHPfdL{yplVnST%@pgYPp42$q%o+7P02T~mn#j@g*Kp2@FWzeCZLLZ z4ufE8$D5)@)jB`Z)h1*Y+qP3jGOP6Lra&nvk#?Id=-};7VC`J@9wLKnwEQqz#rjy$ z@&O{|`r=VXxSzbF=%{WwLL;O!gpZ*Or6^DENE)2$fO@LxJi9=Z;3{Lw2~2k-E~B@# z3Q;HFu+pD&z&fP;EUq-6QeHoX=C@@d`*taClvY3rL?_CB`EJlPSvG3fqs;YbX~(Xl z{Vh`z9s}i>>?Lkk(P?Oaed&+290Bw1H5#Rpl-kySllLuMueVpP6*M0*loi44T3RmV zJd=-D`#<6`iQl+%fA6hR^d0%}}h@O6kRt1*|8zKx4e*e$lA zKi+U^RByhM`)Cvgvk?H$mlVOc=GbFODX`)WEN&AbRFx?HJ*gDb_^hRO>MDZkD{ub* z-rAYca(U8zlt*SVO=y}iMZVIjUvylOlP*Kb1=ott!(pj`jaBZ|c>LJ$6o#bE zZI@gXLrZZ9Q5B^?Q3Q=C!hs|;!5l$Sz){>2*HO06YMytSPUxF5nUtl7?uSciQWg?w z0+X>7`DmiKZD=)3i%0b$h*dd4N9pw6ZS(<3b4Xc3{*Uw0l2#OuPR4^l1+Ib!zKH}E zwN(V9)cgkeU^b-!LOO^KIohW`pn<(rpb}jAAp&7W{WW#oy~m|>B#}ghzf@h<*6llu z0O$szCy79(rlce@qA2Q=qCh{SnrP^hizphJ_4w$JK`*E0qCo|qf-bM7wLLv_Xo*Q~ ztcv^wjHu+IiXFg;_4Uwms-u@8is(j?Q>{9n^hwp42qNx!SM_@6!Z3rO_-N{sg>99r z>^9IEq&eUtX=yBgrS4DX(@GJQ9LQXBMAqSXUqGt5Q}fasHBws`0Ul-$O&pZ2)Su+j z@wZJ>XHQ)M@>KMI8DMsxbvlmsQ5;YbYEp1k`|tD880x77i&fO`T70ykmed_InxfuN z-~cxK^sp07eOJ0Pm6|jVjr!=2LTJ!i1ENa>W{DcpQqq>mT983Vug5_owy9+8Dd*ifkzt#H-uO+EKs@NzLnvR_`O=^lptKQ<85xq3a-LQg&+NTT$8l2DU-=bAd>8$+qr^j6~ zQLyN!?;~_h-qepOGuHVfwE~XQu|Maiw~@Lx(u%?fn=>jCzQ=IxNRIB|RNU&<8F^De zv)M~J9l<)Rz>HLIFDhX{)DNxVoWG44h!?Ctn2Q z!1Md21$$;gDl2oJyZ#D?mGLMt)}^PfW8has@l3MOo7!2EZFxUf z3Y8cU+=73{U3VFfWb71PIL~qWj9tNOgXB|z%7&%dsDEcBi(5br;H30WI*{g)Y1ve< z+KE57A{hfQ?f8Y76n@E*)dMAfj~PSiY(3D zoT?0nS++|t5-iZWaTlBlkk`0{3P)Kxp#*e3hfqx0#T>1l1E7BRng}P%JETlvMWw8c z%GO+FIf#iZoRNqHp)pY_M?He~(e6{HcG^sp4bfOSD9dY2dnc}0jX|-y3g>3IwR6TY zVC74};XaX0;n4LOdKN!mE>%xD)41xI;hp!`d~~3gGNP$$5Tb>5 zc6IpgsH6_YsrNpeQP_6nO_bbZKHPP>KtkMCY5_kIdTBH}sH4wzBWB_Wr-D;*+9F6X zBshMpY82UVNA$CqPi&WO)ND~{ad1b=B7Qz5O&`w6Ck># z%2pQI!6{NmHK-$@8aZNVZ3@)caxp75y>=VK{{Sy?D;=)ZtkPfZ+p(#p1+PXE zv|TnT0B(JC$2qUJG2X)I7#5p^d#LP1@QOo5)Kl+YO`Ji=?Pd6-s$s9~?kqOsWjFv| zwzL8X2VNl0V;^rhZ-V-N#dEE@wQGs&r3PGbG{pN(g|@T0KOIvTK?_e5=BeE8_N%lf zBT9~t?Ff6g0u%_Q_0u-R(Y~OmS61;&9UQs6%l7B^hbMaNjySYJF8k&-%YIJcQai(viB2X{xN5#O`Uz`0X4cpH6o;vPW~=utS&xi43f4g2a+L z#VOOfTFVPd2yZ^7*z5O+oO z<+1>EqI;57+V)ZuI%}{xNN?b!Y6$GOHadHpRnDVqjqF0Qmabjl?rpwDnaX@)4;R0( z+EE()Vp^i2OQ|2N=C(;S^w&DO!e8UY>uEPPz^=7p%5XV{dU*&$Z>ElKf1gk0tlt`V z*2+YD^9jyE8IH2+Fjrr7K%dd{8c>a6vRviw{{S7sXhP`N4`^bAqUOq4wDwjm$;TNK znN5qtI!C^~mZjUfc2Zo}s2@YD<;Ng_joc4KzsGRvnMq^CK~^uve-!VyZgr7dE+|ZU z8&0+#@`?zP25{cWIY@dsVYLn4|)_xBTBH5*0KjT zLKS`en{Oj6I<%?>jrf6zV!WSt$XkHg?YS~YOcb_KB1jvK!a54;sNH*&b3M3?=AbS+ zhxTg5((Thm-CV9NJ9l3nNw-+#zVkN607EZ03epwORbyDY$m6;jo7HsNV0~6s~OR=e3fs5Jy!E@+aJD9^=m& zvip8)>xjxR_-aH)!U82OLK08_ttu-`TU&5O{a30EiVXhfI(rYNAo`sb!jC?422IVp ziIL{{EaxW9yGe1*=NH}Df}`4zUqCk2*5UhEv~&vVi(!SdRd%j+r9wz;Zjh9usYhUu zsBJneM1(g~@Hk#Y`!vk{*s#G*a9?TMb=0Ro;uR^&!@iYGCj*VGNLcJrO$vT`Vb!X+ zWe8Ub$8JcSfbxa1`|kTpO^`o!iqRx3RXuczs6*rUs@q5Ha_sk%*bE+W;K`CAAu=Sn z6u%A7DngQu?@z;7n2y&Lm2O=}2Lf3p<=u)bB=~9rjuuJMP zs3aPvea@4(Q7Em(jhzrfH~Iy7=#!2@Xplp9r{kc4+d&1I2rYD^+RX$Jq@)4>*k~4& z91u~c%!d?_@zF5VB1cduGaKUU?=6h<>|+kP-(W;qQ8IiRbjgi0wf>Z?QQKGq`U9;s zo-R_|&S?$tSuC$6vx-klyl(KQs`VI^-aYN?c1$(1dxo_)HrTW+x7l$N?n{lV1tm$Y z;DAQ0Ahi{zbwPcfjgfk;(W>I#{@Olyr?X4Qn@ziahpT3BdLH%3GPWv zNcdKozLwC9R2VWbJkg{SkmBf4Dc7!|mKv)H)FE34sL%%7bb}7{{Y`zak8)I zxFj%?rL_tyZlmcBry*zsJ{Bmp5hBd*9@!)njevlj}ocq?C7*j^kC< zFEOGc#_V1wpW|K~#+mmaTYb!nmCOdDUA>72N?v7OES0q?E;yUa;STDwDJ4(5%gP~a zzM)VEqK32lo0`iL+{Ow8FOBioZ=-C|)VP7y;n8=O9k#O)+i#A;Xl;dT*rvh1DQf;k@8!-{l#5Y#14b`pSS1Jhw!3YvT5mQq^eIxw|4m>TtNPr5=K(&}*1vvXb1$YFB!|V4<*pHKK_X#HQC2y!P)b9>n_T z8(S20JyoQ2xRQ7Wzg zU`;nW=n+7qG*uUMdr)bR<3%NrqAE34U9^PrR?W7zR+i9`r7K{N2`0aWkoSNTm|=6x z0-f;O9^YetGOW0UqPCvqA}eJnT~asoI?h~NI#ac)X=P~JIQPG*VdiYEL$Ru z$Pk9fN=CzBwRLM@X4uoJzeK6lr$YBYn?>`tv5>Z&&sE|8M7Py`e3&_;|30 z9Cc4dDiJqa+JNoqbm@>MTGgX0spSv!`sfD&luZ)sW*cb&gMU+}KvAS5lrM5oM6E$8 zS3&A@Ng;-)9Y!dYi>(DT5fHqm{{U3cB>l)e8d}W+7HE)JsPs;v=dOdssOV9BA-zyu zQo5N$`(BN+A+mzZw*A((r^J2-MMRekbU?df_knWTAV^RD01+mO74+Xt><6NCw-o(J z5!T;~=eFx*rw8(oMw+D@pKF2Wy>3#{t<6B{G;VTPV4_iGOG;1GvX7>Xgb<<00ql*W zj@l&crEN%XAfeQdp+Yo-(_|qhI>}8)-+r17Z4|aXsJR4ebd9-0g0)-r?DFVCgppmqpWDv_Fl~G5cbWt+8rAYk~ zc|ej@PgAK41w{+i>t4T>ji@1XXf1SU^_mDF8}-mZ37~=`<=Qb%x1L$~s!iW2Gm$s>FBrhDtZ5I+(W4i<>?jXtf7Cu2kW2od% zsd2a@SvG5xt;DKKa*_>DX;hjYLN+>4B8lkaOIdx)-Y;%k5(qCg*Lr$ThQL4NrlSeD zNl9&9ZY@HSYfPo01jRO~Oje;~vsBmc)3g*#IFm$HLc&5q4*vijhL3iMPU@W&`=blT zoPi(~r2K-2($ViL)!i;MDH2pu^ELFQj3RUBgo%>hU~Lv!o2O4v z{0^17MA?vpum1qF8~LJS24AOD6i}bxq!5j>IcPJ7)S{`Fg2#Q^p=Q^iq>HUmP#3wpGY?%M{0eFS9NKOKAq1 z(M_j)W@MKq8?*tzZJ6u~qJ_n!X)lX-S;VLNH#jiy8y|iWoB8k4OLkK94Op6X8xT*H zT7=zuhw%kCRR@?YZ&IGwQdEIo{#w7`rQ2pMIysb3ZZP&yx~zJ>*%z2u9gpp&8|~k8 z$!s|eH5-uI&TLm(TW@~-ECrd1iz&i;77T?IAxChU4@&Aawjciho9lo^n*J14SuBKK zMd=q+)fD*-pKlMva_o?&3G`B{{yNvRu5Bd=N7{OVhhw4=1lOc%Mc97>KG!_W=068= zk2_-qHJ=T`8l4v$oi+AYMauI5vg6FvX04&Vg?*-{Z%z4sCHE!Pmtqz)o|XI8Wa8e} zvhvKz=L*wB&RgFIa$Fmn)IED3fLXxtTP?d^}wa~1&10{%PYPrSt8Ja0b z(u3lfkMY)2$trpyEd=!nlFffQDCy&r9Tju&P8y-3TkaN)#oXLvM?a-Wc?u))2TAq! zbP3qtb9$uN@+B=t_Ll|&N;@e|BpO%Yr$l8i=ZYk^AUgeh>)Rru>H%6&h=PMi0T33; zQdXqNy8;o?l{L~pAS8+sjkeY#!5-|DynRAJ{{U{5;^U%mp`%oH`_>&04{pf0iyai= z=nwGHT*nmiW?%^09EWa#X#)|0+J{FLg0u4Lrj3a`(?cTG^a%5O#g@kHHp3RwN9ioZ zXc764bVm@=)Ttltq#vXqUFS|&lGm{2b2+w>C>v>MWKjBlO(5uo7T6!tRMYt_;S}9K zn-lY#!tLAr&m^5V9)a0UEPn!`HESL|0nwl;%Z#1Ny1f1=zPH_Q_c-BkRjo-UPPnuU z0*2=~-AO0_;(j_Pbra4f0h0^@tMlh3$ZI5IpFMfop8IK(%Vq`5yrrxZ@{fY` z2Ltyv6NkeaS>g_PuB3RbN^(=!^~U{^-Lb1*_6%nspE>IVwX;J4B$w8<*-20T024xj zMLrtumKgb2X4u?(z*4Kt*=(Z?*^#1+CbU*3$iFCJ`4Iz$Se1oaaTM_WH5VGQ$G1mq zq&~v|B~LnALOYyVk`DE!=5?9k+&%c>;$B_2t#>fz?aH;3j4-fMMI7Q8_7#rV&+Tiu_Ja3t+Bj;p zDWI)Bdf_-LK3LqLbjSQP{FkP3^6esO|M6?msPe;=Ib~2JI1~ z#)gj7I_Ysoq7%3A(UYQErC25ONk0MF!Wd%TWSD^e(h*YFLR<#{mHsYj* zUGIDS1E8)TW>aQNw4lzpqt{{WVgxgOQ9 zAn8HKh+gQ?EYYB|M1sobAyg}4^=a7pB^RLx(}>#Yp%9{jQK;=SL`71{P~66XRY2l@ zN!5sGa8UO;Gp9tJq^Xsuw*dt`564lRJJ64a)nu^AZ2eWla>sQZ=IXj>;r{^KHz@I|hI!|NA#v6{n%KG88H+CO zc#rPMwkgQ}0JU1$`W4;^cS=bVR{Gy@%-mF?#aAij94Yq_wpKj0oj@IbJry>-*%(km z(OXgifG8xBQBRMqhEp2f)ZXe=D<<*faYA6Z{pSX{A?WWB9H>G6=ntzvP|8IEC!_?BDb#(mPyYZZ{Hn?P zuE{VgvoFG|Sbh5p$D!lWq*!A9&8wquzeJf0^bXVEu&LEI8spL9qjMPI;Nx9ZhnDiW za}x<=Yg*vYG!*Eo8Ws|0ztVkub)j!nkaKD@M-O!pe^INv+%0x_3cT8BQzRclLk^wyxgcUX8MQQ5?kV2$q)G`tY!A@3;s~aVjui)LHrkJFdj;F(Q%7sD@s)XC_`Wn5>kK@ zup13|cQK4IRmWPdd*U-O!~+|k3h?7OJZz2_aj9lK#j*%RMGoH`b4$xtG1Of@ACM1k zRH=E%--j5!l1zkv9-;b30+riHiqvii$?^^js8Ci`V&>^~1<8+jE&>wwS`sTymbRGr z-EM2yVlwuPy=@#5e+t9hz~r*HG0H6~Rb@6kE7XlYlD}l^_^8+nJFJ~g?NL{eoOtF> zW|et^7`~)cZUI`UARd5gPr>;MBY2KZi<|qk#3h~=O%8-A0b$Fs{5Z^Iw$Um3OP=kc z_bB&D3Qb7*y*^s)`5Y88im=kY3(7d|QF~%e@mDnW3OeS@djRLme(j!TGM}|`FmWie zPGh*hlNqs3Vb;xQL(uwYarv&XlX^qV60hY^+Gg!tar!Pthk7n>oMreC?(M2FqR6%q zrUFyCj^p@O*IjmNrWRB$KHJ+l?;-jSucwRCub8Em9J~JjbzRomb0pm2hY1m#C-nB3 z(_CMiK^4#tCMS#3178I^;2gP+rr_PCWQALRLek?o zj17}x%@4&*S9tIq37qH)Lx2|)iZ~RXh$G>u20g=0>ti@%gLh=tPFsLmr1xW&H<`Oo zBH`{SL4Xd%pO?Mz1E&L7=O8dY24p!N^7wxK8H@jBjXeaySUi#S0^qhXSbr-+&fLc zy>D-JQl(Y)lz)`hP02AmQ`T_~fh@Z=<&U_AA7}S4Tu`f&sZDgC(M<{u@;^|dTP?~2 zrds{EO%yBHu-yE$StL&mR`&M{aG=pGI@nrjgwO&iK3ZfF3XtZ4pq^E+L!AXnav$|k zQis!8ir1|*vBP9Y(PQ)W4Suv$Qg5)$x>7E!rc7vn`vuSrz)%hKe-!U80i8<7PUzbi z^#_zMj?cXxI^zET^`jhRgE)p|r@8Et+Mo#)J*hpv>Q?mmYtUF1%N?NJYD(aC*k~?t zZjqzN3i;?-l(JT%UhcYfX(|^^U;t1jP74|&?552YZ3K5@HkFlh-9xUMw;-KVESrmB zG+#pa-wAPz(~MYgNnpHUkLrMmcLaR?dS+Xa$7reJvWd~u^;|&mM+>y#xn$Puicev2 zK%pQ2KZc!^;I_TgoFtDAEbN*LEo?rAoBB&+5Pk$59Brvi#(c}X{u**SIsX6~ryaRC z9g`KmWo;{RJ4bK$*IBHDWsh1oo9e}11;NLMcEhR_Kp$m^w}3vCiwz-X%I(tDvj&z6ZPd!;6pXdu1NMvSs5 z0Q_`Grzcf}u@-z;GGhsBvb3$p$}~#*NIj;Whu2oN8(S!G6`@)D)Gw9?F{f!FmGE83 z`$Bnp9>w<^@iIR;ayuMj<2v#O7|XGgu?jXcsRGv}yHUBR=IR>#+K@fMp+%H~vDTxR zG-Ta2;kVPeBsY-_k&XR5>N4`(dKe~HODQ6nEg;G4C%$J;cq?4j&O;rB0?E>L~i4Lhnv?I2P zp{~ZiEjZm14{`4{LRn{eej2eI6->@gtUnjJ<@ei8 zIbwjTmFD#!n3l*$ASu`6IF_4DR6rF{hUa0_?W70nX>}^988DQWLVk1p4ZN9&Et8lXeGmP$uKpO|7F*5R!v6piUd#Ck*>7$8k2>;n_)~7O z9jIjrj?{l~xW3}kP}5-ozLnMwkmB#AsknUyfGcmtIX*Hmvd${JX}QB+wws1CYOHa1wl$)Hlv7=z)EYt%w$9xLT576E zYP7Jz28rOsx1|%lt6cgiZiv%F?6z7~wTdK>>$aJ=xC{wVLgI8$hml@OaR-T9eLp|t zJdK-FSg2R*ZJo(fxsL?ZEi&I#DC)zG@jJY<)M%qTR(q54J6ue9n!m7L4_|Q4x!)^! znU+b0{MWIbDz&_?`fC7X;u#?(iF#LQ0d5d0PJ{8^UasT3YS$46YXx20uRPyg-ex8B zZVd-v;r{?XZ;Iv(ss+;~o|fCIg~Rn4Y&D{WIc^=GNx!>-#wwjCr*k^R#r9t3J0H9_ zlh`);N!F{@hQcrYrOO4a)^h7gh}B%lvYWPFbW@JHw~LA)DBt7o*FC?!mhNgU>4m`C z9LrX3cU2A9XWh-|#nZV_b=q^2o>PiFdL(oM zSe%9Mk=ibSj)q5)dYw>Q%=sNgt&Od`cJc3bRUGB{ocyj>3ni!#>LSD&BmjDlJ#}SgY8W-@vsoOo zrgyooiPkqnp6jBD({Cb)_JmosmhVCI`kgQu zN{~niQHH`c9-g{&jWtnRYK+#O_-W*o)(;anhsFMF@L$Qh21@K3SCqIs=Q3<>B$)F| zOlrdlDnc2GTqC)KDJR@QcPb}XtLd&8d@1@Cy2=Y_EQsj!$++t-Fw=@1s&OGj9-^YO z-&)5VRz^?(q8C9ji|8P;M1svmPOQ;`LV|chfdcJkTQJ)8?$J+j>TB+zm7pITX`;7| zczlA69hP$9_FHNE`mp{4wLmTWCh9NxQ4Psb+IYp26yskEjok>!T8Q zrp>0c28&f%EYpo(%rKoj7<=^B;1o0i8hRzR;zs_hQasBg(%wDCW0f>|Jddd~{6>h! zMIt+nfthayoqV8bP9w zzr_hQN?uw6XXpcQ^V87^1kA47iT=NpH1cdAG!R*!g3SaLx-|OD1Q3n-=pcm9L1uyo z!)hrDrE9e**GU424RK2GG7FSMhJ)qE{{Z|&z!sn2ttHHuMkH)S>(ijGCnds(NW9I?ju25?aj!5WeTj{B-kOR7k#^ zQP-+Rbq#4vCaLra&h+?cNrk3@3p5a0qppGr-ruXbzm|utn=vB@K%iB3_-WHZDw=a> zQKEz+)|JzHl#RuB*V3S_>JigPB`kQO=^*YET4}#cBCFLq(ucVJ0G6EyRY>^nIrBCb zi^;;V4qD0aJUV-;es(2lzum32*WPn=t=Q0{HrmR+o46YK>b~O>YUgfq0e6iRv>D6T znMTM!@KN;Kmj3{8aZgLU%X!BME-6x8S8*n_HCFZ0@xam01z$E!SDKPiLgh<8Qsk{v z=t|UnUmZyn!A1Iqlhqef@2h1xr6l?iPLsXlfGOwUoUwGio#nP!#-h(89Dwd%~pcOPAo~QHFY&(Gm#aZVc(J-cTk%N~62z6EZvH zG^ekooOLRo7ixwssZET76*v!5xb)JR>Y&F}Y3`4Pn4(n{9@9#yoql>z1soq0xl%|!R*kGu2}Shz zQPwG4plw>}Au38NbnGc8vbvwgP84g+5(tC7pPrNyStKbu_0p6gfmc(c9;v{K0isHC zs^wN_l5*c%b$5&?u^md%=`;q=0@twbP7t)X8q*iMuFQ{kl)6 zQ_;YRxgkb_x`W}Q7bhCTENgV>psA)0wO!pi06!flMoJEutVh#tw?tKBx@Eeb{WR>5 ze;p>2O*-Mu=)IA3acTCMKw+QsBmwf#mn60jI;3*7Dz)CdbegF_p?jpJSDFYRM``rZ zzzGJcQ~ckSl2L`QO+9o3VG(lMP9j;Lf)xN7)M`Komz|3EXi)?z6Y2a;fJ92e(?*

    S97UEcla4NxZzFlU> zt*OKB?j}5inprN!l}n1~l~>T~rg5Ca7f|{V=zA5&d7mD+vzzMR6(8K+hlojeMmH|e za+7Vi#w|YC+0Ce-=%kUc2T`)Pr$r%ayDKafW;>Wfk(z_{p+n)Bh3^ErGbS{J27v`g zsR>$oQ0gW-EhU`41ji-gmf2f!*9}$Rvho}p&e+o1k=Y;sU5#DB>DOIa*EGwR#WY@C z$?*wsvyeufDgOYEW|u5}(UDkfYD+BTY@wTzg&?`wg;WHn^sla-V+DRHp61ZG!N;IS zv214tx>~Yla;&aKJCZ_eD#W5DICd=8MN5c%dlYR>(miy;i^PC-Q-#w+M2@}*0~?a{ zuHwwrC4SaKMTN&wke2{ZH2o^sTY4Q@?JjKrqJ?=ULIqWw%DKyp?HRi{ClGNgC>Gm> zJeyW$X~`I7;KcijsiX5*X|fW^>$(bhYLed4a5lWxRJ;EGPH8tVZ5@YNc&gm;8lKsg z1)_x`{yPGvv*s)5t4abiRZ+RoJ7dqt_@W9lBYJ7FRG<;2Bz7nObsutKSZWfhMGK)o z-1XCMHBcm|>Xln6O+7TY%7X9;Hq5t}J;+%>6rlu)e+@p~iaeI>c7-^iW;a;Wtc5rS zs<-L$>!M_iRP*uZ9%=;&w46ufb>jpvAWwjcm8A=*`j5WYQQV}fuCtlG8*g~RdKIqY zT&Dj35*tOgDFQ9+TTUL8rZkyAz@A>P9=eT^9rj8y}=uRc> z961ltX#U`=W!)HkoK2D?NpjzL}ruObdK(5~!YmwycSArf|DkmRSZ()CBa%c*5P2G~=$d`vqJZQ#Tbhdws;8U&m1G za?glSTi$W_=LoA9&TEK3w;U!&xk0s|Eq`T@RIha-Z`Nu-A00@OB?Q_yP^KA;xwZhX z)OaaYCHwb~n~!;AyKUWQ?s^Gv9q3gzpsh&x>Wb$bGd#@|YSde0VUz~Efb@a;sWFtf zPzqRa3O`DIkfJ{gj+1P_O&pz(<}p^MWs{O^vRq=>er(5@;RpnR0*^}X@zWPJFKMe( zn_Qb7DE_J2C4@$K55Og~)~PA24Srf{bq1-|MN^`4!Mx02DQ)!ot*CCg?gp84xaz0J zA?Q|@HW<3Zd!oCOx)uJ-3rUL{mvaDn3gs`Je6M8S4j|1k+k~MJs1aUUV9-@T1cEo$ zUf~$ib2xG67XkMpDs5u)+xnfN{NuToldk-^$r(;}d3jFEzRP*)DMa?RR(lh_!8F%y zxqT=HMM;6h{WZ7;snq<`U^nW24M!lWq#JIz8RR_DiX3TGNgolup_lD7N3c1xD&OIL zSLN0#0xUA+IO8p&wY7I2MAN37s6AB$)-3>OK$gGb^s4>HS)82O>g4$fs8O!_lcI5` z(O6^cj9Q+lZ%(EaAxIqTJf6JZzg?L)~A`xGxR0 z$vi(RAhtyg-0dj$+bI<)09JyUtF$7NS6hQkLiv+^I^xNBhf+}MDk)kIP(h*6ivU;5 ze&$trg%x$vFB+hP3O^mg|w!m?F z*XbK?P4%_3m&sRkT-r+S095{3*SFlO%db_T4VF_HD`u!=w57BU!6*P}nQk{LwneVu zYEN^iTE|ndmX{mZW1*&@w9uzr7B*9!j}GU$nE?<5TqlGW6vyFP=^*= zT79OHl2WA_l$B8YH85T|Mwbh1BXwx6jAN*QAsI1z|-b59w=1E9lf>*Pg(YDa@ZSRQ0 zFJM2%N4ONKtZ&y%qE0JKsvW_J?n=6~Yg8snk(zNXEXr^lnn!0vABo#e#csy~Q$<~O zcKF-_TU!1|mucFfmCy`NZlW0o>9^IVTG}QHk%WR0G`5{nGHRA%CZv2dY8~YYrgE*H zj9fd&e7ngOJX4IOS}oZv!@1b^TixO%lHCoqhkK7ZB2oHMgo00KC*U#W_*HI8eaFNc1K4$eQ+WH#-lx{=hcsQwnR-LkxL+dkzUOHz9f zmA5b}_eDK}wN7{M+(zcR>&%>wWK$iwimy^}hB{kzmvy4COkg37)hRWvz*AYNFKV{X zSW{4sf70k-V5L2J(@SxpS4}wuPh={W`OoLq4|qd>N}qqY zHF(eNOsrz~V=g!Qm)@c6D;26x4^nlx;eOv-Wu*_3%NXvtk2LoREO7Lo^Gfdy%i!P3 zzcV>|%PvB==DdrNJ8<_FwAt)vQiLmXa--VY4c3*X>Q__#8ubO0jlGN_IE5&)hqfAub7VPSQfxT~~`D!+ES-g6tOoKn>Lj;rWTz-ye#JlAu4*K)DP z+hbs$(uzQ~T5Wh_B;2mOvMwtXW;ygU>#aOdLt@R&)yp4ye?CfIPAwVSsGj|h$-T={ z?_;1N zwB06$^Xa6}sU}8JwM)u|sO+DAb%DA0>A<16^*SVh(?uaQPOo&@C9(eiiT?n*;iUxQ zIwP$SP(C_!$&_hHO21L2kwm8yL0|f(tYdS)hW=1Quu@hS8_i zXds0S?0%lV4GJQn4!=DFYrvuL-%4miw(a@p6;A9x)oOFF0;LnM15x&r=vG&UY}Tut zha$I2LLHYUv~ChgKuI8iMSAKD)oQHmng}4M&J+i17u%7VpJ5i_;O-}4ML^rAKUJCtRR^V}7HA-XQ8hF>Xdtzz_?-k7 zng}e=L15}MNRf>iHJS(^P`!vIl!6rpK|tsvy|h`NkcLXD_~;DO>M|BV*!LaJPsdN#&Lpajn6~t& z6g1DK{{WVzsw8={-9R@!gX5wo0EP#!?rHEEZRG&cf%k^}bkz`E1t@ygO!{g~R<$Ro z(h;u!`03KXjtZ@`wIybP3p5G~eMM42Bzm1n9T6a$^w4r5Xo-klGQtuXcO-WfvC%O32V_Kw z5)PO~z0{7#mbzgY6$$9C@YBrbh@n6yM!;x@l`3(b4Pwc1D^q#J(q=n_zW7D%{SA~q zCNo{6l{VXerAD;a>pRJFkGFuBXy=ZBY53*?>aH5uYSt5}S6|Kl01|v-@vn)vKQrN9 zBd7BQb<0k;_2&qk4ir;pvmz;VGFAIzC0*s-)KNx;za38ia$~t0NN%Q|9klaW96l<(GU%N{(IvJ@7Es{rP}q$|-fFD$AsPm{oDEYhYS&6k zEYL=Qbq*k<)`>%nnx#Wyre=gZCt`Y3C~G8)r8WXCkw^6#6dm9WqPnY$?KCP#(59Nu z<115c0+k@qN%@UD_f;%4dWRMYNc6AgrN)GeOKc=dPia?b)B1HPGpeqYg}S2kafb>O zUx4^%!6~;e5mapiIk@V2jXX_MGTJSeko+f7>kIal*{W0x4M(opSaD;6o1qA%g|fG6 zwFOy=6ZroCG+0{2vPlTHW>C>f4fRwW>GW-9Zo2AtyoI)E-_V+ZQPB9V89h8%`LHO$re~h}v&9TCiU6&7(w949v zp>T6tKESDq7|mPCVlidENvxI<6K}XI)oJ)7lj*JQ8$8>{{V5K;isxC*H0ucr&#VyUqsZ6?z7 z*6-`0b1903_8K@VU4`fIFKspNkld!}R_Z$Zch^S6W*H!_b?5F&#H6)xrmZShL+n1F zwUVZe_4Tf=Uo%~=Wr1`~XrStq@3sq$JjJGTmirmJvLAqZ(xoY~NCbqFPtQthhpJ-E zzS2IEx2}*R3g#lo2AZdSC!b!i zo19yvqI;J4kyHy$A!R)d;EseJfze)C#WQxeR4D%dNhA*vM0U7$3#l9v%D3KHlHcs? zkGpn@*qPFzB}-{a6g5=^0pD?5aQ^%8undhgK}J^Mqf$HB)7N=wOj{{UzY{L zbdIJ&G*eF~cBqp|gZOLB+>-r{bsD{!!Ld`dm6JttSch+u4Gqa~F(gnW)SA?fjgQBEa}GK8A|^E zhD}tD!oLl4889!EfV3_A4K=x_ z(5=QFl0k9hI)a$I%i^tvl8|y-IZ3wN3JadwYV2v+4(d@o52m!7YoASS?@mInyzAM! zd+5yYiWSQo!XvmX%Hxe+p{f&(I9i^W0;B|*RTcC)^iC<_?hyFRSB?1vw0PU21mcKG zO{(0etUqFw(w+nsol5?(Pg-iPC|ufywOrugVIaiyX>eS^`Dbp_!btAYW{LZoB%nH<1brxSZ zkwZdNw8C;3!2&hag!YtnxZtO827n!8@>Fl>3!~y^C7?Y&a{EqPahILr`Ej1Su-~}_ ziA08;ru~Gcr{ktDw{Uv2m!ces&h5}v->Rc|hm@R?#a9Sc9#q3+;R?w@*vGRrSX`@0 zXC!?odDITng&vh7T}Ic4V{X_V1c09f#pmbws|gHOaS%W50-;jgN%Cr5O~|}(-dC8j z$N6lBObEGzX8^WIeQG|_ZX_tvzLcrjyKAlDIKyr1PU!cA#5or;H@PP4m(qAYB{6Y# zo{?o`xqpxO?W$B>O~m9O4c*&dt|W-gQ`&ti6dkJy8}!x#mU~>rx9VYV0P9ZSblwy1 zZN*KM)MUM;o4XZy?Xzx^#j;(+q$XTom9X~T)lX5l*Cv^}31ho2K-x%U0qyRrHb0d^ zL{<@WkQ+!Fl6r6Iej3i+W1RC=Ep4_yGJ)z!hV{1V*kMaUp>!w#LZ?+SHdbgtwPevq zD$5_={G@WH!lYPLpk&u!u1t%>fh_s^{hQ!7O(xMwoSz&m%e2;OrFVyD|Y{Cx^E3GiYo*`Vb^Z zh!!O`*?(W@cYSy!UmyC`1o@m=6os}IXrzGAF7~$tHSQ|0CCW4v=22R|8EUta=-V=B zxIDLca*&Pa@TrgpueD_RD4372?@hl$J@Mgag4#K;a%nu+yP3V1EU;JuK`+g1Ldn!X z-`0^}$2yO57KD-;{ZhVyS)Dd(x2Ee;Qd?79Q?K5d_WMV~huR@UhoAPJ+iQbKBbj}Z zRi_-kZ7Mp36as}WY-E*;jv5x+M;_4Bo^Mo0;2AEFF(yXfDE8#0c|Y z8~;H9ccGEhefe9g&Siihpch_RYbq*^rmuRZw!ixStd=-PG3Mqude>@UqxQjC= zE)W%yaZcz1xU|`YIay~1hoyebSdE#{C5-i;s}+5uG>WrI;_UH6(D3XqKjxWJ9Dfu_ z{7ZC^x>U+Sa%f_n-CLxDyhc^Gio6dqqUynYRQ2cW4Ce7%f*J&&GZRQ4Ano$PbSw67 zki`nvhuebB>>|^nO6{6w;dWEAqdd9}z*Wk7W5Q;cTMag@Krw~-f^Lw({Wh}TkU zjg@YJ-_Aw(jPSp$8r>fjtv?yzI-NO~qpx*HXysGpIQc{^Vfvuy3yzB)0Qb^S`d)?oHTkmq~O_m%R|cVjV1 z{}Cw^F8O%7q}K^b9?9o!vD}Gyg}$LcdH;1pO=_MaGUQEHYFg9IJ^KUS%?!~MufHtt zl{l}zP9r+!q+OoM2L@$Llqcq^dt{})^yzxmUHjvwM*jrz9RyAOz%v{PQLR|Ew%xGQ z`pfpriL|-?H0%C`um63Lxs|pSsOh2R1#~r(`dNG;f1KVM>CM0KdYZNDws^V4t`;L@ zQ5}IsAw?$qp2xS~#?^eo}k`Zf!fPd_Gx9`mN&#H@2{aUE;DIM++H zc_^GF+D9LibI30$kr)G)o}oLLF6O!SAzZGHolRz7HQv%b{m_5caQ+y)BoJ*`uM2PL z(bS)+ky9{&EZ=Xf$I3qu3Y<2T?1j}QW^siRCIV_kN4yE*w1@|QsQ zaMEz4uf4%CnqR|93#Y2-=ToiJpA@z^A$> zBJN%$730=6Oa_P+C92xaea^;{cLx#EA#}GR)jO36#z1aW$VKIN9##;GDJ{=mxbe9& z0KKGOCT@gT+;o};@u!tBW;^B~afoy#auhxLwIi0kSp^a^U&Kz4d`%%bIj=&r-~)t5 zl`)@Q* z>(CVY#J1iR7ZhXQKlZu}<^*EmnCBomd<=#IMsusca#Z_- za5&y^H#gvda7HJp^`S4H&ZZ#SbCa70&oNVr)+A9cp>(r{tUaLlj%Ho-?o2W`ILAk5 zV8g%+DWZ^ENV-xA76`8?_NZdUlGtQMB!Ks=O*cxhH1V{FvHEaUGUf_~X&paksq1rS zc(C{3A{4R+_PS1tt(FE^_!dOvE-`S^Q*i9)h@$_`SjN?$O4j91xS>ihHnZgIUWL+- z22SU1jcDbXBdzdr>Eo*J@5s}fGL-A*uz&H^@#=V1;$u8A@16`yv=%OKl{S>cxzwv$ zI_f`$mX(%Y*mI#ujnWe&TP9y5N`EFhxbMA}Fuc2(j7LB-OCCq|5jcxS1&MKQqn#6K z=X_r5C-I~uTb6>9arQW)!ELhO5TFqS$T?@uJcjVO{x>WkKt8fS8b-cH3GDxy@DN^a z5%{OPQW$>G{Eu*O1&|4nzHztWejMMOjl&UoHG0ksb+8J9%pB3lz=f{U|A;F%kwL%aos#RPsCEzpcs{0hVvo)z3aWvq&BziPoXT#+*+6A zJ6buX{j-Qa6fczKmw1wcjufbS6#mg2`IrHX>azSzt0eq()4uXa!<&mhd7JixyFd;f zntz)7yhsvQ%izdEwT4kSOQOv)s(Nl`I0!TWDcaE|JnYvD0WvLc?{*PxWMeJmiC5YW zM_SXOv19Ma(lKqdtZg2?_&%&NkqjVt6gCrKUQ%4CUUk9SH|oVYv-C1Z`$pu3)|%SP zPv_gwFXQ-OTFA=b>y5okk8d~Bbp6({IaD0M4enVBM0H)uZPy(Nwqp@Jox8cSB%@renK}x%j z$f79emyfEnaxZXq!WqYDz={N}jVD)aCIP#4Y^i=rIUXXK+) zx!mC}a@mdl8>+}J*SN1N%1A+0j>%8Qma8+2*lnn$SXamcIP8dupBU^+%QH8n@@ZXU zW*fzk+_L9Crc7bj&-rO3dZZ2Sq%z|z8Q4``t2oT)r2Nj2bi!=rtp9oR`?5Ul-O zftymOui9cKR+`07x{youY8U9^Uzp(Fc1DQ50HiRp!UTmsH;>M;Eg7iBzeAiAukIGn zl;bOQr^)&cC7AAhHS$x;bWimhv4rv;=LOPk=*!OE1%z8B+ZlH$ zvMc%8*R#_wD&X_uPu8qL?`=cWU>jX{#1=5Aa(=Eu8`0`j$~WmV!#gLK)1e=lm;;G< zsp$RJ3i_x6k)A{0J3sTApbUu|Jk^lG9oZ>iKabGKw9Nf~LWT zEB+rwZA)ULu8{BBV!n7z>j;0-4ADd9zqqnGpNsCFQH<~F;wbyH_E(+r4Bl<&SDjp9 z)q#$T4Ec4TBV;7dO37^l@OUWOeC21wwgB=q?QgVB82MZFF=`Cg9PbrVIFWAY|KsFG zT5`>cMNfx`o6iSjpFb6G(>w!^Z^J18L^W;jjw;{Lxo_1af23>j0mtu)t2UV$08o{mv!7O_@ZmlF~*fa42Id0nTjyHG)hf6f{N1oW!zZeEF;7g8e-^D!9otdHBX%;W(lgA0gGGN zYY53F876)?qmTkGeOnLKp$n_4-#MevrII7%;vNoJ7D7H-G!~OmOD)uqcSron!wx=N zqyV)N4p5mbP!`wfhW$sXYx-{wt}R+o{sl z5>3$sDhm@Y`%Gg=?3qx-L|k-`sgTcY|?${^N! z3$CaJj-+L7krmilFeZkQlBs;MfPBheF)D9yrIB6M|B841oN4;(mlX6-7wLHL|IpiQoY1KGET`{Gd+04 z{KH9?`_%`K@3l+5WKwHX5_RL)$jr)1EASsfH-$}yTEp^3olnHqhJ<6ig(!UQM%ab04?=IyR1!`Ff1>cYi@y{IJ zHyB1H9TIg_R37tmW>0Qb%_>Nfz{cJqwDc^p6|g?0afcxX$Un&b7yi1!PSPavhmQ&X z{(FkA-+Vr|9$cNh!{36eiL1MEUyHZQNa{+S`NDbTQ{7nIiB;uB$DJ1x5X>EaZN~ui z;)&`-mYmD(PG7PbB{h@>71*c>*Pq|zPF@f_Wa&<$`8oYYjta^ep(YsMGS!*avCn~) zws~HH>Fl$*KdLoR9Ywj$VLKSL#kt_0nz~~pOZ4G*r{+!0`Gt=jZDqTMvj0N&)Q`zP zi@~FypE!1Ji+B%yQtn=0V@r=)f-X|Aas<;%DyyB z4BV&GqWC#{XlnuQm%0Q&wAgz-We}t)>vX zm6Z6a>;cT6SICPbLx-%(dwzv1v&^~EwkAH6_c)cWc`ozD zZO()}g%PH!sR;_qz|;7R$w0#Bg`7s#4jY<4y%?+VZK%n&ilwGE>3ljbd?;J2@gNm=g`K3t}4{(GQkk@}ib@PEv!7AQV zu;z|tv-`oKm^`)tIwjA=|A=z^jfK_~5+NIwPd#(ZLFupqG;<)m$oAdpjXU|^anTT} z!S8fAX8=&lL^8QP!z-%M-s)@b@nT&;C0s>mxo|sA&Nl46$SAw?b4@#-v6bE%XP8I= zvE(}nr<&edj5j`_SjEXRyB)rflTVY$L%b;}GZIVbLm1=c!@;$KRN@M!lgoZT7ymr# z%kMe{kGbGiM4eUCA?hzeQ^+ zar`NE%x#jznUeMiF$SiU$LQn!@JDQ09u{J$$(K0y9RjP};-qPlEAw(ewD7`hK#D zxBF%Ae%6N?@lxsccTgz@x_bYjJ3p3R+qp0#Z-iuQM`F(uO-?xaJ1m&wZ6M^DgN@%> zpL@y7U;!cEcT%5M8rkHB+nna|_Ndz6RrM@JBjXl}0+DRjLCcPXyImE+z@W!d;Yk(kSDrr}UoDhJuu9L6ll zv(KfLyU-;(#ATRS4|?R%25(N@Bk>CUyl3<&tWO}DMEFbUQI1cS z27GdH&Z(?AIVw^w@)dm;!4568f1)J>V|}nr%XOh2|C`ayH*~Rc50t}7rZKyHodb88 zi3Dt`&g`l`6oO{i$*^-j3*j7;XmiPOr0YwfX%qq+^Kq9wDe+SA!=;%2CV*nCByT1% z4D_^^Z&^ZOHgFtoWZfhafCorgArHGI3iE99xuQENMmQ7t{s+@ARj({rc4(c&tjs5e zK|NYRJ-zNXnb?fTRN=(BjR)RJY z|3G`mkU&6Wh8d<>X@+&lgmt4Q!LuHr@^AHV%P+O`Z8*Qu zEGMW!^KKvf@dCU=iz z)@)(~tg|q4Xa<_?7JRpWL6C7w6}rVW>V~K<;nZTZTeg;%;BT)uH1D@c$A3FyT|sFA)n zDS-Lu-z(jhHl3J+$_0bv0X@6TY?0+sTjoMjE$#gF{)>-O%A~x#%@&vW^J%)nV$=P} zyY8~OhMh>!{^S+Kvdml~BO>THNwejThXE9|059{7<<#c34)3NHP22tK6L}$vA>Po| z9kFWrZVVLfQ-ijy>3~6fe^G&7cJdsvWnUVQ^`{fU)?>=&JaR~I;nna%K^Bjq@9&zM!455UXIC6hy@eedNExpf) z(?&M@OT9u|U;mVkg{T<6*I$sO7jnV9aOYHjXkWx-0zzp2-2lR2lTJed{m5x0-w zNGk7asx;6ou$R)VEv?TlM)cLd_Z3ZEev@`a+#!bXU=n{@V5BpZ6lC&jS9l}Z%VBuI zD0GcX#jkCBWTA1;-!9ylEF@s7mHJ{q7lJwGWdNXUt0{7H&j(tHjBiR{hrui(IaZ9P z_qMnj&=ph2Lrwe9%=C&6U4zC(0o^Tq(x3}GT%?Go&TvoPyF*X7p1vzC z@OY?pSJv(MV;#t{<8Rx>8>c^)y9odIQR1l{=NU6daqhBoR))U0h1uJMAs&TyI?Ox9 zN4O(3b{56SA%ZDcZj2R%oz#WNTkJH|JXym!cp4vI4QI)UM_Idf#S|mgOYbH*q6kL& z0Z2R%5T!#YhAT!v($ia3jcV3Q^Kr*D#Y3RVDALrE%?QHO#6Mv@_B2cHs&g8Au+%hk zo5Gg7E`oby`n3FL2k}wpuD&C~+ixsqz;^;v0D(emDSeXoTrR|DPA^`Oo=g|_;gkY? zq=r?u7pPN51$%2a=G?8smsX5B3GOxt3GG$%@>n~DsZ!1J+l*LYoL}l7Ek6GZi3>fv z+KzV)3!r@Q#K?DtLlBa^PK906-_e(HPN$-tm%6%6cp^~*F#Sj`3kdB2|C6#MEjo=b zFGwsE7g*C;8TFKCM$Oma^Bzm6xxGXg*FPyyRcI8Kd+77=pqTUwYmLpR&akAo>*{EIxu3Dr`rJBZeU@G! zBF7Uk#o%)L&RBjpk|J|4T9w6Q&X$S`eJvep=;OCj0%6M-FV^~2k73rV^DoSdI=BvW zDGXn9t?)%U2~q8wjB7ECaMbm+n$Dzh7Jz5nrKsdXMFV#EesY_s(xW_(cu5VvfAfWL3c=5QDFw*cFjPv@K5xRiF9iE3Wm^DC6&yQ$ zUpb9_;C2Wd(OQABet(rG#K_iR19-IV{gM?Gm)GHaN0p|G$2x91K1`)3^%rs6UZHuK zhTygkPRCtCudkY>OwYrrLWSS?@=hxd=9oFf2t%yVdK~+GcU_&%eHY4axI(xNgd4<9 zWarIA$2xeh&onoa|9Z`&&LutM>6TF*-^F3Q(PeJM8_aL}n^fPz0L)l~VDS#v9V%E9W0RJFm@YiF;|PeUK;!^&>^{-JI9tR<@xC zt%7#n}pRh?K7asOX7tH7B9#;yAF$I-&JvuMwB-D{zbztW7SoJRJw;?iRcJ z(^mO7Jf0u~oQC$nHKRH!=EGsEwaFF z^&Crp$gJBYxyAI^C*0x>j7rPF{}Hh)A~Dg1BNi6<`lnO3=_GJd#iXeCky`7mAA#@5 zXcydShWuU3enYM+U;0~L0Ze_Y4CWT=nq4%BRsBImd<9mTG(`qdJad`E5C%b$0aXSq z3s+hswp6ZU>6_3Y`4KzV*%27&nuM3q-;M3A9Fm6kGV9=TKF%{b;Z@2SCoT9zJZvcd zBW8DqNLqou;^`+U=-_5{j~DH!xR`&siryTM2-+2+54uH2j%z1O8BszJh>zG=N;eBk zU91B#DyxoWEDVXb#vQTyCvjC=Ft}0tn7Q4s*XGpy=R{Ke2bbiu%OsPQvf;@$A8orE zkjI@g6&JYuDX`$_0EB&4&@CY#Jy@;WfIR$zZ|ug?_+!mk_Ksp@Zzsy%a4yC`Z}&#u zVboyj6VWuBBGeC__ms1x>=na|tPr&N1V4zIG%hkZzx7u}=kyy>#Wt$!pb- zu3V^cO?2^SHC!-~n69Su*&M>=e({5ES%gEGp!dXZoG{h#y&c~jnxU8-`*UapH$Ih_ zLV6~Fhpkf}8a@6|?4TrDVmdBj*1{r?@XVXiaw4~Jb`Dp_a&i3>a*5&72Wj63(9UPW zYgQ7vB2wl9jKR$`?3^rHfirHG@RgP@wpoR%x&H~jblPU!TSRZ|Q%_h%g8l|Z(n%F+ zQphu>;W^c7%={dblz!FC(RbWQ8ZzGy<&ffgo=9cicN|r-FMZeZKcY5 zYl&6g{ZQEG$!A%M_KrLS#?h(CrR78R#oqsjdUZJVfxEDc7~&%J0Xs+E^hoEnk%kxW zetED*-5$pPvzhoF1vmYyY?`0KZ3wG3ay4mY^-neJC?x=h4yH^)jdoi()$aE46!vz3@ z<9PWg#b6z0GUdu{rA3M`{jk#s%j0rY-|<~jXm}~Jt5!;f1v%_);&mJiT9zLZ!(ZLY zKHa=h#@|?27U*;6D$CepO?b#VBm`9mSn#HV5E+GGo12eNGdmQ4eI{5wQ7Ek4jd`W9 z>SJc{Q}hA3lc)i!WK?eQLnqGpa144Db%)#y;~MuIs^9U+JK>0$a(Dh+TPqKaD6H2n)(GS~BT_9?wP_PQZ;8$Zj~EUF_je7nmUwu-soTdy zyKUeX#dCZ$$()>BD(+Umh!h*|Hcm5z5cR!#^9~!3xSZ9rIcWG`DjNkJUWm=?>+JvG zYP$7m;dPZLUS{>So(@8}jKH{&C(i3qzL|$@EolzBS08pb=BuK1=srCHYy7Mdf4$+E zBD3D({dxF5B1q0QD7xCG?`rxkD=v*S9v?Cnh}xTfB`{qdWyeaT*E5#;QF_U5lc0#G9?#$AB&h70&`zt(?hMB-hutmT0I~3&>lMtu zznUT%a#JbFVBq23UO*?9rc35O?t7)l2xUbl;&d)@h0D! zd=l;`YgCGV{3?Hga->MwJFQ-qeJEkjR6J$&$(`0~-$PDO?=H9C{#5d7rl<0O)BC-$ zM>&-qn|sCUc#$XDVja!}G<^#te#F`It4>zPy;?0x`|o$YZMVO1>9?B_pp%0XaOe%b zTFCl)rZ6RAgw`=TB;z9ax?Iz6B_z7eTg30yQXP5F%6)c6PsZAM!$L~Z_50!5;8fzN zj10b(rWA;f`Z&P8_UrvEy<3$#B8WAAC1*h-<-!!QA*nsZLpIR9mMi_$;)D@mBOH>& zW9R+^;Y5n%z^6>K_^r7~VGQOgG^x zJoo zoqeffzSWA-^Utjk(3fAgvq=cw!}9sIn>>Vfy+3t{fDtU}*tCCyQ__I}mb^S0GJ$(+ zAmr>pDK;RibHM>Qmy)U=#X*CNzlvdf1`m$iT|y0E zeTlniR<{Ds6M5K_kjhsor-a@@lY`&W!8)HxyEI#fOxyHkQXpVYmkP9DSUbVHGmgsR z734|{mX={cm8BI=y!qa9d<%S+?#j;NyERGc0x=Rq;3*VkYX=O`;U&Y9*cWQM1AIqz zKiu9FU_xh%ZAt&Z_UDbneocbD=D0Lk`i(ssdY^CX+n{UrwdH38AHAQx@09QRK_52l z`m7)yeY*E0ubqy73|buc@^}&Ki-o|o0j1+9dG8-BW6_=uw;1Y$bldD%_q@hbY#mu3 z&@*DHv|UEsN9ZXVfw5;M(>xFCX+opt%a_oZ3c|OWoF+1illKP`#1@6Nz1C_Tw(JU~ zu=J*jQt1|v3P%<084Rs`$@%O|HDYNo#B|<5FsO2?mFQ?S*W{kj7c-E*DOrjY)K&rC z(TdPAgM7J*w9{a|W?7(-#bLigdp1clr)tsBEK8E1+|_K<5`4UT13g~P?CX~U*o3lF z58kDyWc%P|105INZgznpwcV2g-RHla+SrCTyw?voafn=sP-Zhy& z-EVf+vND$_MOx2#s%v}dER~qM_QPh0`QmcIN7g*5zuHEb9|(mebuKT-d|O|;I~`KL z1Ph9b?`sx%9dE0+Y~-XtGFOCn+6r4Mn)@d&bb0&i?T~Ckan98r+sh6Nt;lGNS$!4o zThn5L0sI~iu>X$|jKDMB#UKXete*czv?c(92P)<~xK*KH6TRvgvS~hK2u^sW0CpTK zjfS-DTdX!kvlERI9@ypc^D(;NetDxXgs^+Z zOPmuV1hERP*gn;>?Rzw?NRFrqa|Hi@Jelz~Ty?(*0 zr8k7mO0+V>_|n**WB)z=u@-X{KD8*?YawN3OpRc0*k6YZeqs%LC=gKL4SWX@(&V4X z)wI?*PQ^DAxa^HF-a~}Y{CKhA)HE6^v+-&~!pxxC zKR!*Av=KnZFRT_GTcH+Rv<$m?kC-=ka_V>fBl$Xvs54gK#or17!tU@{JN~Aq_x)nW z51*&QBlWo^>V6akeGm$#Eh-K|P^ImowG8m=RQ>Kr>sLWzYP6R79}zn{@B1Rfo#L*b zloifTogp5yez_Lnd-h!qJBC_Dx*x{VAl6YJBd8Swi&2}L$}al0yf|kWX6bE_2#y+9 zf7VNMXP4@7q%*hyp4<-ES{4!TBJ~&hbExf6|GbY?C$<&jM875WG-xY6_6d}BJuL0? z&`%05x{%*28tue_sEkz|cxxY<<)wQ!FGy<7n$W{z<9`^8Fnx)HFy_Zw58MDA3KB8^0Cm-afbcf#{2|5FfPZ^2wRR(r+v>>fYvcq%zyQgOtgK0G*~a6N&ST z-DBq|-&UL@WnR`Y=k;Gb5qs+PH_aAemX@rMd!5^`1bbi$|NALfN4Fdgbc*l3(!!R3 zS6#~@`+nl5LFf84_FE*QH}QF%id!rZ3Hk&Mn+Fg+ku*0C|Bjd#RM!`9CIoJ)&V+(q za21VWTKyUy<8N}~>D6MsQLXN_AgL~-&~lll@H&1f?Cf*`@C-8RzM6m8ujGK<*gOn% zvX95)ne_G1{53wpC+PXft2DHJ%J13HtQ=cUmZ+@C&9?9;YI@nMdCl`R`{T!i%F6;P zV=GIHdby@kWCOfXR^Idx(u->Cx$JWt$QSM~ZA?u%;-7b$yu^hI*7Vrm<;)u#DorwX zcx2!_a_&jwWAjw?#L~D391ilkzPc&NC7^2H!D_?$CX+ta*vYHLACB7*33fw8geKNu zF?!9oKELz%?@8euf~9z^rlWr?I;o6jlk1a$XZl)=*m{K^m~ur;$d1FvXd_08nI))e zq;tgLXBDcvT;Ep0jQquJ#?-qR6ALXOiky40r`2bqbkPIQM1QJ3;^3z}A80pCCOFS? zb(mT9Dt>0c#6;e$6cex5zaYzgx84L=R*=Qv7pn12_xR9g~44iTk#+4w2CCRh+)>uFOGgP!!Qspo2Hqdj`q@w${BqX7Tj+igIMwYal(D# z9ZY3cK1Wk1;yZ79mrB4+xi@4EuRtn$7?^mNetP5G11#+-ruA+dG$XMSc$EhVL?zx2 z+s%8~kvi$0#aX zY^q(I?J(^({g7@KoE{A-@nSbU`y-?#&l{?P)b4Ul%L2j1~9HbFU!_MF$zowjQ>h(@Tt>d|`s_z27BhW17*)v& zu$W94RhXvsi0&D4TA^MR>wIc~M@OpATr!YG4hLoOr@{1$b^u)Lr^Y-1s{i9JBV{0@ z90vfV7h)*I&@MYO*!N0=VPHnGrG?YIV()?UlcLNnHTjI~?0qS`M`H)mvE}pbSaF`5 z4_7-xk}5yC&|woD<9dXVo&SU2{uhoTs>OTjWMxY^lA+LZ*>|3^j|13cWh$i@Qwk}! zr8M{>o1Ht!!O*aTFlQMoKd;jvOW}JwBMUr3CfnioVt0|K^TlKLid(wr0KNJab-p{ck- z$m$#n8z;g4c+lFOdm5)#07)pRn>zm&YI$w&4f;2V*l*`iAn&L}d95wrZHLUb1Sz~e z{al;rHKt-*)AY)z;H5z5)QEM=>N6M2H|}JSBO`XdY0CapJ3-YrE7?PkDj=k;G+0IZ zwrram&Ny2e3TMJck`(fh7wc8!GVnF4sopf>vn#a3n`sh1y80@$0jOY-fr<^A}hHT{v$PNo7gpsf?YkGc*eF!{2H;_vQ3Izr4!{oE>Q%(dGlW3MAK2 z=PNbCj~=Q~h*@m*sgcA$kQ&vR&^?3fkQ4@0bHsXj80FvK;Z{qg2gr%JNWS5y`M4Km z{ECtNd-ZXKpd@{*Ky{D`kO!EU;Ko`SM??Dc)D0MXR>WCaH6F0j!0uB+T8?&A7)s0v z?v6}>iJ*KZI7L}{;KFm0GG#rU)FZZI8{32s#itNfKUu~)!DKNkP==6Ar>De*9*OMby)o1i&u_ZXhb4%bX)fB+MkIfX&o`ty&Xzd&C^*3eaP~d zMmU|e27@^R-dL-Y3r{5P1n~hmsqUPvidClLoLU*d)Dt(nhf(mfI~;wf3)fsU}{%?5k3!D5KW8hu|5> z2^PzJ?_)~c87^Fc(T^MCq<%CG08hGs7qS5#PI0yDkH2GiJF)OUljtJ+D;!KTyvXIj znO5@1&_CA-A==7OoL`IA9P=48sS250Fq zUo4N{RzL`lvbyXw8|a{fa*MQJrW;!2>(O1b25Z8kHmx6cu0oIcPVX4|fPXr@RN>;tt$O@1vmF^m`A_x*h?9kNONx0<;8>vfWq z_T8BCiXl~9FrcG=M*d;U4oddHz=M}CU-HoW^d6M6$sr~D+ZUR^7*o8m5q`53_IU)X z(NdZku2gF!Ci}R6{eDR+i`F+Epz*xXXMl-Xo(xX<%hx<(#`{LbZl%epciS8bTQkX{ zzm9*5ZxcM^dGfvz)QZg;P$=ldzQte`D>JL^ozJ?V4s=?YKK@74=c+I~)~ZsEhbW;rkq@H;5Av>+BS zw=T=-HS^--Za7=X!C`g1O!vzeuZO}E#%=0L=pyHx&~ZKDqgm)#m+wd_;|82XiutXeZWUw zl++}@Fu+;5v^I8l^L$FAg5Y4SVdL03lAwr*JD&Klxy_uPhiBiubRDsS119}PvOR=E z`4*&|j2pAnJ-XgECqIhi1|-R-AeRbD3!>08jwHQerYSL9j_h&(MyBc37bxQwwe<=` zb88m###We}gkBMyx~g}iXOsj(^PdC31Er{JgWXD#y3t8tm1tJu1M#g1R;R^36YGUk zWQ#vi&@KE5@riB>_g_pgTHC)ymKlPr)T(KyBBM8r1`iU)E70hDA0#bTVYn}$VNnzB z{b)Mwqlf`#LWp@>3NJC(O_k%qbfb`2wmY#|IwVh8&`5BskgHF{Mv@yre4+~pO zU^nAnFve{l+`sx>kjf>fJqiA)*3HwuOJ`S~I~=01YV`N^*v2z~UbQ@+f>Ft0*V2S0 z7UTP}D)Ps%Qqqx?+R76S^6ozlHL`rdtOgfN+btP_1+Iy!iA&+xD+<}?7V!s_QWdCQ( zg^RErN_4t)3U@}h+KtU`IYOC`%z0CW1+UBCZrr;}dLM!^L*C0x>51}HYck~ctMG&V zaRr+{?25=_kTUlJw+OV1=H7n`%oBt?IPsM+?pG*#HCE-jJ}__$UVilJWB5dY)&?IW zgKH1**FnK=DoTjPk}bF6FlvoD1ODT6LO_Ctwbh3V=J*lOudM>Do9tv%zd6OSTStys z;dvNEnBT)KlZ##nFbFw}g%>0lL=L`GdC}&~Og1&)=p3#YnDPfWyp-bg;LYhfE3JO) zIaAYngUv_7-wgZSdu43)WQ+bsbb4-dZ1iSO`*i*(DubES8#vf36lzDUyD&MfjD78i zfA2QLTo-TN(D}6BuXpXmRLhRx!Qp83IHxAvAADe~!8Pzyc;LaqOQfEqpH#WR3kxy_ zWtV5Iub0mCQ!{(NY=b(Qa;xS0WJq^Ouc)I`ccxXUk1pfgtIm9PHzbyi=E*OnQ#^!P zx>E;1x#WAc-^UY|3I?F(ezaNjIBRol4wSxg;jGDfnb{;&w$}uIYwI$$7s{0>V*Krt$Uv4+KtjqaO=nm z-&Lty{0^VD?&Z$khu~}@;^_R5U@3r)&Ap`nf8zg$l)R>NgVi_YHIdUi=FuJy5Qiel zXc?d2lTe-TA)?Uik>uM|oFt|dQkY_p&8u1AW74A*#z$-#3lq`TrY+JFwMM>K=?qIx zSFHe$?oRe46P;b|e6chhuY2)f*xRI>xI(az5AUq558?BzT>71v7%PuI>G3bQ+&BhW zTJcnsib^ATafWhkR6-Tf1_dJTx%V-1W4tSMU4h)98DpQu$@K0h_8$CU|7oM;2ZM`e zIl!qZl{hZPlKr2LPANRUbZ>|;+o<34cqU{Hy04cm$`73d5g7D4mkAz8KF`N{*Ydvp zN0glO^vvpH-kRxl;;8WU!(<3@iQ&kU*kdccWt#D|47JI##Mxgl)i3YcW_GvD9hm2m z!a!Eey3-LyC)86Im*@hcRA4T@XSI=AO#M|+W{#{GC9QRCUGRdOUvWB7P>#YAkCSz2$uCTBa5s|dls|kZj z<@A<&w$e801@4y0PrAiteMLH=wvsTdYOu~V%0{i$u7$+a9s|+~KF$Om)4?wcyg!5% z9p2R|@1O&KksNV_)pV7YaC%&7vGU#Ojzfe)SS%{U-6XkjA*c@oPoMSr0oy(W}N%j z@-?>5a=tc-{-!x;EhftnTKpmEvVRh1Qtp7-KtF#)?CjMLCQz!iB|p3y7qj-7=fPLDMV3JaD$@5d_{dfRGa(|=T2ra1 z`b@Qe9tnjSv-nqyieCPteyQPpQ`emkKeRo0sbeVa(utj;O z?*CL*->N$mQmlV(yv}(0-Z)<(h>3b*--|Qxhs&6tN&-CRi=5hKZ*$X%$CQ~E8S~%9 zA|tLO0>Lu6OROyY>F1i+y-(#kfM~-yliKLped~AcS9)px_%V6i%Q2zfExq91%+3%^ z5$;^I2+{Tjg;YnEBkD~R)TYdOKl*<#*L%3Q6V}uDN!e{LKh@taCEEo2h}PaG((NZF z^wSg`e^P8q6(J>(^70!x3Ak{)#SOoisi=W=Wc)Rflwmm_;uVm1&=4JRE-scki-7vs0zX6h(s`~aqOyjuH}w4P*-Nh*h*4&<$E z-15=RYrgHiW=iHFyt=qA>YF(nSS<6Ijp8H7yaWA+(6@j49`b;NYVN?-=PU$uen!~2 z5%nSV7!}iZu>37;GP$b+q?J#RMpMXP?eu~qhC%pxA|g}WrTeAx^FVDQ5#ESj;vO@c zXG$;B1>uSZGPPL-otUzgyfGps&g0QmDQ$R5dqq9LwS^s*Sz3}y}i;Zg8JPorRw%9zg)`>5_ z&G_c@DwryoNjUpA5ZcJl;XPeZ-iYA}&ObQ)Dg|29pvp8yIdzt_fbUPT2rEZ=!-RDx zw5HHtT~oN?o$@P382>2QeX8HeOw%AYBw`ypMLwZSLqyNVTjEJR0Kt3PQ|#BA)r6}G z+Q|@I<_VIHLc;mD3+2;^u6E9i1UJeA2R|?9#ATO2`JnE5JB5zY2G7doc&2jp{SUdt z1Z7s_NkEPzpP94fS=09z)Y5K?wgU)z5xKyMGp^#=dHHCNBM`_Rkd3&he4S!0fcSDc z874&2Kei*!fQZqe5}j$d!MNZk9mRTYq&65P9h#JYQ5>4wPK622MY9aLqe>5F3#oUt zkMJ%0(qOwLT0>G&B&D$komBM@r=rpp?JiR>*LJd4=G{%kQ=1}u!u+jqMZE=7$m54G z|E0T@chiY|y%JA$IDx!+RZQKmRfyAY(WK%`8q-*Vgr8`H;}b|vtYDkz0cE27331OY zd@HUUXHJRfX~GEWtt_63Z3rdljft-|OI4x$v3m44+o zQPG6^KB>!Xt7B9&54ORAJ~=lM5+sOdng2mjVmN5J(T0Xp4GbJncM_-ep&&g5tfKyJwysJ ze};^;FV#K^Z{EoN0P|&k4JTAv-Qa*SCjS8O1%Hl=w{T7Q)_&%73R1xx--*$EIv#XT zxpOAJw@|OkOcv0l-q(|=)Bga1b)jgcy(_0gX)kGSR2q{!R4ZYiV@L-@=tI>#+y4L^ zDb1=46kAb$>C%#r;F32K(V&mC^C=V~f5YLTX$jEamEuf-kNAKJ{Hdgos(#*91FCRE zas!10wIZj|qgBt{RdXEC6yRwH1Gx&JJ!o{~WPz%l=Q5fhdmK;)yK(9?)0~>33Dmz$ zoFMvX_B}H3M2#)EZPb;jhM!G!C81V4nxqBCmZcijius*56@0|i0%A!ZlG&isjBhwsV!j$Q!6;BDDWg$Vz!Ocj_~;^DXdsM$ zPeEN0I<3+c59y%w(S#}zsJGmg33JnKeA}BL4&1$$e{Po!;R~}r45TVr@{{Rgs?a>@kfa<9hK48RXXkZ|?s(nM?by95! zH7ectR)$a}ZN_@;5?1=hXg{St0Cj5^pjr3a7aF8;YDpVcPEi-EyzcpEZBF`&!&9J| zG6ITIQ3@|nWi*gd+G>lda6s#&Cu)HjTxg2Hp&A7i(kM_t_6nW!=cE(9p;{Ev{hC0H zIvli8LWKaK`045(9jY$-eW>fADQzgfT_Me?bQBViP3xm3fjS(qF_iMcTSsz_T4@c9 z5s(h4!4k~cI~M-{)~`ejO^Vv3SIv3goQ$L;6zm8Cr3Xz&V<@WRbDUCS>yu1^mXC7% zY3ZVxdMWnyzA2{b?z*z29=$aX%B~q014IUzPOkz<&`lDIp`Pu6baY5HM$p0QO8oTe z8RD2P0dMRdhxlmyFpJef2GlwyO{LT!LI+xDMv0e{VX%cDpMv*E5{o{i4`?JhNCzh$!wB{CZa|(4lak(=)zyB?#RlM3OFSsD118CQinu>97=A> zQda3wMFHuk?;w>egG3Me{{Z3s8cJH+C@Qq{&_uNN4uUjsEACFpsQ&;h08$zf-KyeQ zQki^ruJ9=s4D2>LFrqq1`soW34N^6Z& zc^g!w^I>2*-oVhPYFH+U6WK(ikHI&at-jJEw+UAlkjR$uyIO0#KzFG60qdpP9nPUp zFC_HXT}sg%$?O8hcAsfX=59s;%W9h)UF^6=Qi44-BJv*u)t$!nSEW;a*?6Ci++y6V z&nh&ii%QvX3MnB;AdZ@9&h}er1K4OnEBtI%aG2y3YlRiaah!X}>{%bwV$;iZMkw3V z2u;Z;=#?K;d=9&`PqyUN+R?WxAByv9WW3<_{R{E(AO8R|dPswPhuXTP0S2A^3TX#_>p$iKM^wVlvoG6u8s(zq%8j#Vrs1oa< zep_?t&|)=jme7|GrFR6Y^;6VT=%lg)>aw@IPAOzEFzsQf*g8ebhq* z)6-E(XtfT9R6bSo(I%Z3M_O>6iqiY2(lHxE*AcM(I+n?FQQd3`a$9mqkd%PKk> zAQQ13x{%8BDw^{R9GZ}?J_qI{_C6}SKP$I2d}#^ofprJ6LJw^c7wDihk+|VD&$$xx zAu8{YWMZ>6`r-cRr%Egs6ZCHP*@~+9TUV$K-|VeQT<;yes8S zRmIQ6LyrFT0tictIaQLM&(~bzm3w_@e9tWdmrdbb>0e@G+V3+LVz`&d+z*)|N3_9t z37sXc>f02ruenJ8q;7TQjQ;=(yu-?50-$tW&Eg*ASm$H(tL7u3rJh#s?s3C8EM7sw zRd}ZZsSB2QxjsN~ zE;)@zU^9Bg+d&~jcD(u%+1`*pnw@m6J;x3v<<1|a%A=g}M)Ev+1HW5U3~pS@EyN}2 zw<<9yaHgV!ZSvn;eUD_!r@?vZC|_$gg0HG@sYo80Jd@*e;57A5;cg{uf%O}!d#HadIJIDT^QR^R~G*3{%E_5V^48@D{$?)-%z%v zDGMFQWSSGV%UcW%I6&RUs>|KX{FTn%?F;4A*HtCwA8|!)I7w28tw}nvI-OSLD1#ar zJ1s(h+Pmqlr6{#}qDM^t;*tPZ2;Q4%C=j(Kx-b%eSbAxI2q*xFzOV@&4Kpi(odiUw zvsTMAygGIy)Qep_no6-{E;`brD5R#F=-bsaL07BCzF>;$sKl}*CHS)kV`^}1zSPtV zlof7?`x~hSF3iQ@L742hX zuydsHnl6oz_ac|Uk?K3CH}08(E6HC0?;Oh}AQzZW*u2|Hh+B#50pb#AS{e{{)bC|x zlEW%sJqcOgd92%ej1#xEtK!*&5|pSP)By+a71y{~5dnDcxK$oP2hx5%nrqWWA5^f* zCV*0)>QIeEe4?p-6|u*t0tqM7bQGqi+T)(%(NNxG8rsyZzx}PPoH=3T30E#w<73I{ zoZC#>E#QOwy=8SW5}E*2O{x4eo>gIa6`f(ws$4!w&Rf9Q`Y*X#1}o!F4`aAKLoVfQ z#<7f5;T-*?LI`gTICwV5(QdgmyE+Wz}bfSTIW0|e|r+$}X@Fo29J(gp$WLqx@xdosUS=XvT>d-&M_Td0q2~iR0m_ z`S!ivt~o8vRo3SEq)e2ew_GQxiK##P&1-zD6jTw#O-f8}aDN>Z@Sa=H=x+#AEz*T6 zpwWX~iKmc^w91J}Xh~54AXJh`paPvqu9X<7jWob&bfSC9#b;xR2){yh1mXp4G8=J; z2<{!jy|+@8CcgqTZ4H*uQmE$4dXC!8-O|jA$2_^%qvKGgE)9hSiMK-^w@Zir0IDgf zDRutnFOSa8F%cwAZR_l4yjCw8n9#7acNE($KdfERcs% zg#{EUo6$f}(@HvuC%TmBbSBOblMYhmi=Wq|m&5@=0;dsyc-n zTSlr&gNwIrF8QW6YCtNd;iZQHn5|cgvh2su#CFS$I6y)Gsjia9-0q2TOxWAG+5o1z zHXUrlV6%S95*IPy-2%~Ct!A4Q0nlpvGAl-Je_sX9uQK;JIXxR)8{Oa02#A-Q;(BFI zQH^Y9(TJ(p2}tMyMKzGfWZ!V@dM&;SjYDT0DOVn)Ot|hp7BFfgHb77SDL@LJO7zr5 zxD{-6#tutyqb@%rVg>C9kV< zU!I|#@=9D7NE;D1q$wL{-)1DG}|9esK`&7Y{<6|lETAM2>O~Q z)OT-j&rSyb5D~2GudOtf8l;xd4insI(@v38+k)^XyK2&! zA}WCh%3G&I(EN15eQ(&WhL36pWVi4X`REW)EyFu*XnqLeFe}sQ&<&FkLnsx6kvOSOGHaq+$CfnxQo^#$CFyl@( z<7*_PNhBJOLD-1X*he^{yD4_J&V<#fq+}PFc|J#xWS0cA-29s1KId{n4ThOai;Tx6 zx*Kd0*nqc4N@{2*Ds>^Sh6)84^Y3*^MD~;a0EU#-q%=|0TBWa3>7=E(v|AJb{JLbc zq&ObPqSjNjKRrIi9_RpztGH5vzbYL{b0~r^+(|A~e!_x%4^1SnRTD#r&?n{%#X0`~ zCR|#zU|3b(HJRaf?D8?*phi=*t>ZyL-0; z;bSX!c88h=<4;{R^vZj0&vdrVM-5a_Y=TDqw9{UTs-OC{l%U!w-+ePZTvJHt14-HMuOEF`%6y&OZYw zHqi^28PPf|BX?6p4c(!+4s@8VqqXSnDGs3ZN|LG(t7}O&HmfI)ZpkPTQjtmk4RtGN zRm;GEUysW{G`*s~)&3d?BLN>D;h-9$l7&Nb2AkJTh|-LUTAJx8y6T^cbRg^ zoJSuya81?xYuaEmyNXlVmx{tY!ay4i>U^|^PELv(c<9?PlzyU^dRS7>fg6qfdMc*N z(T>Fp!KRL=_l4>|R*5!BA4H?o1`-C1KDVxkD|kSuG~5rzMv{v={JLgTVFsR`=ccHp zv!VlPAC8|)9;mjBI11HB?lrA65u(Sb(vsJj2rmfkru3$X7&Ka`G^X{gi7lq67rG}z zo(Q=|arLf=C={U*)cfQqW3j(dbtV`IHB3u#8l3DvZGqatcRf|>r-VVh6>PTz^+tWD z(Hm3JSJ(#h^(RU$+7N|>>UPZR(aCRl3*5M{+3{{V)9Kq9GF zSgvN$_2n^?ZLF*|?2|wO&!&~M(J*i0^}Razk|vU-QEQ_}LnTD*r8%@f6goQTAkitw z+iISIg+rP3$6rJKlkwGsv+5{S4dt~C;nfwL3-)iTwSDZY{RD5Z->##{9@l84THJ@X zR4g@;NIMTt;i(ZFR9m2%*JS|m=}*H^?vAR`$(~e$U#^t(>!`I;>7r5XDFHx`bt0N3 zSanmM9OW^mG`v#&q^dNr*%kGFDvfsE@zpKM2sKug(g$dn8SW7;8Mv>tvbb&@=3O7A zhfOj2f{!~39HYI*;x#;UM3f;-Q$z)tr1~8xkD`erbxMw!A9<~T>rG2(qJr_*pjeck zs#2ehor%Y~i6m58NkMTv$rZ1t)X5p$6mQp5EsFbd#;$B#Es|FjTtx*ov@5jK8v1Eg zSqUv<5-`w&##<8w>;@@7Eg@XTTE={x#+q$zvt{LrQ$WU%uuZ`z8zE%VeRhcEw&a$e z8aCmh@m^_Um%JYIkPMjjok#tZ?iD|i7W;(WWZG`=oVVPq&J;?D;2?qzQSvnFu1O`l z_EQ$TP=)B2XSK=O31T74aL|Qh@hra{$}HyJc6as{c;FZ_hv``Z^sikf4VQ-I5XYK1 zac)7!ZtQM-NQUUoQT)P9yV|=#k8g4R0OEt8(O%ksRLp(-Q(7XWK#+&nTA5oa1T8fi zkHb}PIn8ptxNLH>YqbShPCexKw?s8lqT4lUVKc-IF(FWCWyW8U%eksK_5|Ft~L&srAzfF=qb-CXlgXhe+LEZ1&yC8fvMWVU32As?$l$pY;_7MbLallWdtj5gQqKy54VS2$eM}NJ5W5 z4)yidE6HPIwYc5dtGZ#i6}|PJrfE_X4VqxA*(Mmzv{{y&(rZ){o`h?0j>!i`3c}>D zevH2bAF*e2NJ;G`hhy^8#@tq^i0qA2bI-h&1Bf`%{{VlREfCCD&M7V_KpyVWHq@+L zx2XNaN6cONae8B^Ul&~U%C}x!sXx1@!fm-cdJsONr_943JNo-Wt{52trs7<-_ zTR$AeDN2vlbl{GO4VJ4|S9x~XY)gpgW#$wVg{P>Yucn)Gf$Eg_>bqa!PD77*34_bR za&;xyZMImrHly_ru|Tu<>zs2eTVEdFHC+pYEyK);sjR#$|M~CLn++P@4Ct=9*CWBt*3_#s+<*2zzl!8$LIibw{pLVz#R7YRwkLhH&p9U$H&;ex zx|Ll!i*lyfTZl_)xXJet^8^0?lgrG0V)CV|1+FcH$*;ry-Ga2^PcAH5d_=@;a$RHu zIVI4Z=TwnSi52bcH+XwfYXD_Q(M8N%i-}990IzQU0BR~+9M}qU_0+*3?y5E|!<&z% zQoU52iE`U)xLp+LZSb9#rc>Pi0JPfN_S5lEKp`XMbgO%LCjBiXS6*IQ+y4OMcpeq* zr|dVzUSP>?R%i>vBv^my7;Cs$X5CZ$x;&Uj75AE4ig%#;5!YHQb^>DRI8)}Zd5>}| zAZ=Rh+S5T@QLZ?=WPAzB_?~r-#<$(G9yYo+aIrTrgwMAwTymrU^9t<_BXhAM;yn(e zGTo7ycL|%UiM+QU)HUN@#1%J1$L9GR&zHVN@^b$Gb>rAPvoclA`JXpuc`$^xP!!T* zp~n5O9*GD~X*2)|lfI=mFtn0-D6YMd7BP_kf1tNW5sXu zw2suKi61>y&m~Pu#?9a5%kN8pwFqKH$3Zc?4pK;D3Sb!8t# z0_=(_ZOXi1ZsMpI3rNoGnWU`R#SZlROJa(}piHeIN?B2>$pWgSKMgr$aOc*ormGQi z2@(PgYBf+YqeXbQP)S#y17Yc_R>0b7RFN~=O0O*VW?PqC|m=s~!U`BRS zmwQKX?QN8)M_@^y)pU@$x+$OSE#hY9xB>A^8>}hTdGjrIIF3NMKyggZgz^;I%({a` zA!t|yDhFZ?n0cbQx0SLmIl6+2MUN(PsE-*1#~dLkNEIZX!$^h~J=6%NnUObhv;j}{ z3mvbI;3DU9X*2RkKo~aurJ{N%L-F+1iO!DFI-Oj(E^#Y$QVDD~ane{8DVsVjK#2UZ=3QK`+3R7|7G#hLRAP>h;nNU&9b3&52 z3zBU-VQO0pDKc^g(L^*2dp7ER6w~e79;d2{ZDl3f=sGGMAyelWEJB%)3I^ny;x_K6=aU0#V|v5tfFEF-P1GZR(X zMhdTYi`~Z|n73B7NOjU_Q(b0nE}tpfO6mCAOm@r-Xi{zq$G^I9sn?VHc!;8pbvFln zP+VOG=oXI48f13860a)8akvaMW?-%w1qnyM9=mF`3%Z?E<>o1^18R#f>`qkaAH0^y zZZl++6$(^W(z=mxbO6x{I}%M(&mz7t4X@cm1s#B-^xB(hv67S7T1`L{Q2Xjzoyt9Q z>w2oUBIspuokI+QEhk+`To z;jGlRu8`0zm7YD8?A+xUa!(^l$)zq8xn9MpF$rmu?NA3&b#G^BK~+JK$rtDpNS6Dz zrss3(zNNLaR0dS96^_*R9X%ga7Fh{fIKt(U(<8YWgqETb|%G8Lo6j9vr^YBS!DpHD-)ag`CFG4;#6hUjGDCj3WG-=4v ze{o^%^qOf8&?&U5P^-Dfcm#!Zqz!=6Ehq>;K!==FDoIzVI&qy5?Fhpw2|rQmrs$OB zryL}OkSkJp4OmA)6bZEGmVKf=j@{{|=%S<>#Bxe2u=N@zDKMG@kVndzN_-$z*fgSR zO%gPzYP!y}NDoGEr3Yu2tx&Z3g%44sRS`?mv4|N{3%X++u`bPmsa7iNv(%}h{>?{x zC$f4O>7h?92*7!pl`|>)d&S&qd*s}*(*kwY>5SQ+u4Krz%VR;=Qk`uy>}jB(+@%q@ z0924P#CO6YMu2H?AHr@<{L62I+s%7LXtu12(5S4iKI9RDgy}}dZSL;QmUTX zgV1TWgiQn{z0(+KQ+>^#1@FG0ke-S{qWguV=uRmvAf(ia=|zfJa48&u4G!9E64ry^ zppK{_f;w-aAwJhsEe4v6I8bSM##R*4iWZO3uxLl{(^b_QTdI((c}o&KsJBtrp+K2~ z{i8_rC@Pjpky?s-Kd2KTBbDrQP^5#WM4`bk=_Y;q1NTtmyE}FMXCnjg_-VJ>cL`zC zQ8V%0jN+Sl1qw-_Nfb32h*_e2uLK~Fp5soG>!&K0S|NR*{-dI*O;m@)_Pjpr$T)?+ zkf%Lu!R60oyrNux`0d0|Aw>t!lR;lWzN_+*O4vz{s-2657tRESMP&TR;XV}NJi8o^ z!g&+-jCTJ349Zj~5>qY6{X0*(6r=Ulv^uZjv)stj>k1Wb%55#8ll(_TPCFN^T^35H z!bZRW(?;V!meuVEr3Q)bN(%xOM#F6{XiU`%md$=Tjv_@=A{iUhQ}NTzZ4p%phC+=i z(@|5A^+poPXnT??>8Gd@f~Y@fG_P*jj*fu>wt(qUbn6*Vn!i`8el*eu_@ySxM-B_*zr8pY94*qc4F|H;$YGmU$)Q9}M{8 zh`8OiFy^d+4Ti%$N>sLEDp4zPLx@RAmX@1BmQo0<0I02X3ng{*w(gflQA)?yzG;I9 zpe22eMgIV`d&8b2wJPuAN1GF&F>XySQ?4^C82!9Ffmc1cC6_`+z^t8e6VCGY5^at; zE&H%I_!tY_RaUeA02x2+dhxJG9D(I==8L7Z!qe7_R^@ugq$%Vt+bB|OYIOjGY-`ij znzpx-KXIdu(fBBmO^T0DDyr^D_bU4fa2&S>jNfbtT-T9Xhkc(i%-e+uBAgXSr|(^U zLTl8YZ`WEFIQ{H{{IVan%~HFNz|ws-0rvPPJCJ|wRPvn`^N;xP!2DI?9xjHQL|B%) z3zvI?9l!uulJcT93TgiU#7%$BWE?uxhOcoYLVqTm+BLKauRE0e?mYdH;uel+LL^v*DP+r>u-ewr5?bx$8i8{LbXs*zM3~*;%zDBX#4)f5VOc!c0X^)MqK{@_J#X^ zc%{J8Jd@|o9?u$aRjN!Fx7mxj+GNgx<;F^3#xf(ob=3r|#iUob(!1&VUSDB#Hpq|O zif%J^Z5H`83!P|j2ED22tJg5rioUC5vZMM_PmYMXs2ZUCwP`7JM_XrO^#1^kiAVt& zWuZ~@Q$a>=RHoBo;qpI@f{*}8kz*~wXrPhsIySTtqys>Y2oED=AxGe7bWUgpHPuGi zWi$am)NT0b6;lz1S!`7U=~mxPjYULO*+*Rsp#)f!8g}jS>!h?%5p7XNH~Kmg>!;%C zqRUW=y2ASf5>L;rl;&!X9k?W?kffSYiXBI;l-S5jNc3N|UjwImD&h@R-KzfpNz_Uy z2yVoDG-y!++OLL_L`%Zm0U+1qrO`A(497pqx%g;^MYU=p_S4e3ZZz(|l$nSL>D{Tp zHY%;pkDdkjo#idHH~#<~{8G;Oe;Hboi1~%0!ys87S{+jgOBV^|EN9e}poM!*E4S90 zYMSFQZo@}9irU{BGlf7=?>KOOFnGPnS3VN*4~*`7L(2A6BFVd8_k^&SjK-SRyu+l# zb@s|!PU-Dg`RZMpnJ}wOQe@p3p-;{_@sr73RK(Ywac2nR8RiWY_oB+nt+85q3M9u@ zYpr*!)D<8h3yX<*D{^!!WFF4*4jrxB$^LKfhkBPNC?JEtgs~LcRnao~Aa>T~kutCRFJU z-6qMOEnU(itxJ7$+^I&S>dpwmLaLHt<5W#yB#}ywhJ_S43J=;TDX6V_X?~K3Nn6+D zq3Ndss$kkuh2AekVocOKhGg z`;W`x*!YJfHI~_~*9_`ROL91JbO+jM*#>Zwvpw6ZYjFy3wK3Y3LK3Pfq=DB}@>%+4SwA|DU6Uxg>@s-RVGT5YfqTc3sz|XC3{UT(?wP0n?`_egGz7l(WSM> zyMv~X=G8iaSLr%Y2~0YmG@2W~Fh>((7jziKvz-bPg4vJ;Hi8{G5yBpRAsh)T2Kij zqwO*B{51PP?xMZvLAc(O)1j(4l@0rC{WN}=Me3EgjK6`<3@NDz3&k=Mxzhb4uc%rn z17!N0AhwAtaVWdYev%H}x?7l}2h|3IYzLtA(?r!Gs3*|cw;e~OkmjdYPgG_KlC^tI z{#sZGGB}Qi)XPH)1;h$h&~(vA#-53|?%h<_l^Ld2Kx$F*(?T$;Sn^WjthL2qh{W4q zB?x@Hw|+JBI#cDNXse;c0d!_5fJFg64MbP_mbK8LJMbs?mtOKa60Q|>4PsMRUxJ{nqzA^_-)Ls$!`-%F3GX5p1h%e8-W;RCcDnvHY% zm9>xQNo}UDB|498r4yoGaj)qmKzu8y4+JP6>Weh$<2>7~sG)7B6sUYb(LONXo@ugsS~({kdl$brl-*;a{z6G3xy`;O;jHECE`{5NMWV5LE+g9S)T@?fh+MHNtMF#B zQk0+Rxk3R?p$Aqr(>3j?sJ1bKHQPwXWM#9MNg1PpidkB55ZDv zox1ggUgg>@(OrW+63QQ8bp%k-qP~Wj<5w`vV=-fa1QqGKE6Z$^vt@{fF~XpQHgV=< z4&+y9yeKIzBql4WJr&ZM>v_jBQO*827c%A?Zb*O24{oXBd9X@=>wffAw8Wv`6~!a~ zPe8E0fZ{*Yl6s9>-dZ`1_$=l-D#=@+sU3a^tn5dkEfOQBworS5#>7)qG;q~k$8gyO z*J`YH6mtts$d8WXuBJD6Ls0IES876(Xgkd|A3bVtx%&Ae{8t+1zT7x8&ulB)xB4!L zW3BS91Le3+D%s*n#&Qb+%Q)TO4w0lc?{u*ewtGKVXcS1D^D~S!h z&=oZA9Y@be4Kzr7%i3zvl;6Rkyc#fN2NvL}5l*@w#R@J{BWt`+1lpU5T;ZMD@9i`o zdTBc-uC251N!>d!YdcZfoVk#ZCC8dl3r?n?J;SK4L-W%Os5*s1oXI)PXk(}W?3=ed zI&?M!)}xajxQdF`Aolw^3a_A5|P&IlfmTR=CqH6QbK~v6nL8F==ijM$%Nb98sW3N2xkTsu4~AZBImA zbY(4|21AnBdds$IMddqEo2E7-Cpqb(q;l=M(0yLxH( zW-7BQhik&VJ|%OV-;eWnCMDR%hjVcVksWS*9mLQcyKT0x{M(4fleQwBV3or8ZO0zS z2LgR3XnYrczxKh#*-e7qV#($v_SM>my24sewvkcS>g)LH%k1Nq*!c^Z2%`3Nmo~=b zA`E#4a9sQ5d>;P*!WlyI&N;LR9DQ*$Xs{3T6yfeET|OR~>DWv5WMMGLqt_JV_{s2B zg6a=6N39X-t>c9`ve%8Y`;B4w`z$!ClkIl)Nmxwhz^7rhCbGGc+K~`Ewwq-u?r?-1wi>^SCMGl?Wgme0X72`}H=oH9d5njX2t<~W zp7{+`J=^>Tug6juYIX2X?=RSB28CE|F3GNOHKu#!MbgaJ1;b9_-6{dE!(8TTPeIjhMcJXh`i9Z}0I6uqbirlm zFN!6wN}N#?6cya+b-iSD2Xyfag@wnm`Jwx=`AXJJdT)y{sf0#^Yj{&?pi+jx2A{`U zs~ceqTJpQByJ@paNt618tt)~Xb zZ;X5PyJM1@PgNun>0Y|kG*m8p`8K4fc8T`a)fV9yab$lpYe@%Bv?%gQ2utlQHMTEu zs8mHj>DN|_A)!`1&T*nk4) zEGa|YjE|{FJ*RO^VDOfVis+I$t@Z8u<$wZuFPC9ODu?Q)>Q}C}`$bhNUQ?qP#cugc z${n)H0o#n(i%V=U(iEi?0r8|AWLMiapLzd>Gfs~Co!?29IcKL@?iT9DkxDUjHS~ET=vRmQjExg!_+bC%P zX(W9r2s`b+OnyA&wcJf8K%v%^oO;e8T){^b9{&I#kdc9|bw`Ci(t7KDLQ+64hUblfqrH&(SNcDU(H3(Dt*_cahomPz$Gervj`ucj2yA4Uz{+9cr;WvPdE^v`{W>oZvS1mw!{&vIhrbxcPZDolW;_5maSY3Ov!ucvUMSTWZC z(Y-ziYcTtaV3$kIA%ToB%$I|3^@xpFX8R&z)|9WfGL--+vDnm5Y0GIMZjLpoc5Xd) zP3_RA7nQuz!<);pM);q{3@;huo9ss7qZgHJ{qJ{Z-cPvU3PYrtkSaFRIp%Cm(@>?@ zapz3XWHcdj?rp$aCP2ZX=3MP|S$9Zw)P-Co#)|xgqXW@TEu<+ekD7?p&8&^yDze$< z7Pg%!@lU4V0}@EO0~2zzElHsy>p)!1x|%FJmjpZ*^;7Q)$l`7)yje4DYvY+bql!MS zag0SOegjil0G{C^=t_lp(2=UyWQ(I+QlmNd7ZY?XX#CR;IPzlYY~&l}OTkvL!#2mJ zyCWNi0bz43>@7WrXh={bl%$bgo}T)`)=(?!9wP*Ybi;SUvaL%uyuSG=bl2Xy-fvH> z>C)Cqj=uZL?poBNxl)O#I)=y$u3D*PhSna$m+PQy^XwZFb04q zq!>^aHP)GBG_KSgH443w?k=>bO+lO1?WTLyk*wLdjdIuWCnAsLX-)5BM#8#oRVA&~;Kx!u4h2D|+p=wI*ft z#E|@;LG0Vm{B<_w>N{xmxAiIradO|uzE9KOtLxIP`Blu-#lHK>-jyWO*QT>m-MQwj zE2CqvEs3G53VOzP^52Zxh5rC0=#kvp2Wp>9XRa?vG-}pb;zL_ksGl_C$u}9_va=Hw zBP#7|C)9MNlGv-MM*jdQ@~JlipEB!e3|u3ZkU#Ym)32>{QFOq^=(HHH=?9s6m)+vp zqb?*w25KmjfCUNsH7+*ks*!m1FleADlGh<>TGUi)T|KsoJ0zQtCYyFssA;kFsL&6` zPnlZQ5|wRkWOjH?G}NsN^p?x|YNl$hu(r>>nY0WKJ7fXAlx&qsOg3Re0MKV4B|VgCRgifWrG12GWg z2XcnQnx2}le60awV2VxZDV*x0zOoO|Q`_>?3i+0vEdnZr$ve|aAxLo)Sm6OA5l{W* zoM=sglH7DBYW_NCiX}EQRZoCEp1LI=tVC3@qJDZwDexAF1gHVmQQOri$3&ppDgwQ0 zsbDB&$WY4702`izL_!2-`&pM{X$Hz2wSyYf`D)Xa}GgeXh|l zG-Od_-YQ&d_U=*B^3zg{5yc@6qGT)eAC{7=g7ZNd%>)tn56Fi5Z^m%6>tqzIB!Um+ zrG(Rp?JwJp?$OEr0B@-roq5H$Pa5R;yrYas#qU?kikEJ=#*FD&+Icdfu z00{NfCEpxv)PdC^iP$+St=d2ps^fLC<0BfD8HFiQ8EH!XqW+%pr4=a9bg3s-@xt~E zN{r$p6;tLjmO#EG3^<`4T|eQd^*-W?>7|iGy@-)(w9>jH zA_x_ys&q8RBp z=%ojPqMmG*2p5)zl-EsL!W^`pC0R0bB=}`Q2T=>W-alWAtWhf*PKOIO0fjHi%aD@Ccl*@$? z#}@O+N4oNy?MO;{iB%Fk1$t@cXpl)OP})GD75VAfqN?>>+jjnX2$tF;7U@lE-BIw; z^g#;RDP8tGPMq}u2wv3sn*Mri#FSyB1ni_X57kO^(TKqaobnA7%Hi(-e9Y(m4A1<#kBU+5EMutQq7r#k_HpzDq|vW zX)2Jnlv$GOV(hg3)z=yQ%F;nv8dAVo!Yfizl230_N^0Cj*EguI%|#KvcA&1-aUYF8 zWWGSOS!3kCcHFm(^Es@gcWZsuiKjJej`qb!62EG+Ql@~HE9!(ZU63jWsLYa7F5+*9wR#f_z3@Y8#>y9IwLq{(K7^VBBfvMuVsrncI^>Px=g za!*IvC%`v5R(|`QKbF*Lqykdu2haswhzIc+b-65w(_km$mTmF=F8h7T$#H+#i|rwE zekc8H_n^WuaODHAtam#kFnhPzZkH4X;&jE`#$mdM>GvrXZo;|L-&6BlI^xf;zu3x4 zF>Xi8t3Mu8uj?)OCV%$*y$9j{09DIQ zO#7Qk=BBUKzqZHPs}b=!{Q=~q?pwgD7i_0IfgOGR-?2K|3A90V&f3?tRH5y!U1%+^ zmiEX00Fft(# zn~iYc=nM~lDUp`6K>q;Wb&>x7(x5J*r2E#G@<#?#08fY8Mav($_ayzuJgD%entX7? ze$ITMk3i(8aT|Lxxa7Qla(>Z2o= zn(9AKY5c#c_yXi!6y`n}xi@O&P9w^=hUQ#T3Abf9?b^cX-bG154atU*vqX{tNvIX+ zuBmN*42Q}=SlO*!&Y*20OEt^*F~aLSBuHoQ%2`nM(UV`+RYa*#pS$82G!9`7?rWJR6bi_U54S=0ACuv`YT<0Dj&}$TFWna7a=M zsZ}UJ);9VJ*yc2Ul&CMfA=IK~c@M|`0QTwNk2!LUY{s2MzUc+4rZ;H4I@79vw5`X_ zvkE|Q!h2|tI#W*DM|S5>zNvpxAn8!PVf>`X{B2*7e6;t8vej?#o%wNG;tO@gM1e9i zGjCu>Y0vI%E${@8;YYY?5jjZ{w^)}^JFTX_#Ov0r^FN|8^* zZK(tfib#5P@%J9`2QTCZ#yEEZzH;6~@|!|zIK8HI`cPA=>_>REwCHFk){1dS8xvXs zs26!iE!kNd2hUIX{mIxZ4J+4SYwMzrWZ4xcgKy1tVgXA}ZQuzGzfLQcOPzkl2p9r$~oa4YwbJbVy-yX9<8|dP7vi=HOAk4x7{CMQk#qY;vnr)E~bIE6vLD?s?8`m4ZpZo z)(;Thuvf0;k*Mj=Y59d)lH%5GXQ=~-)`U164~K~H*1kO#1GyuGc{hqVUoPO>?-G$` z<$NCY?mLoUzzGkvc4^eI8ECUm=ySa)X+!lTNjp}W>I~1?8$YQo0f1NTSk`fC$*z4E z+quN{)lPhc_V@DRim@2E$0%{Mem}9vN)*c%EA5##5|SAwh z3~(1doxs!j{!4k0_G2w)ZY8)aWkqODoABxRs%Y$ISX!HKcP$2sBXPF3?2tHAE>$(L zVWJ2&4?$m+jJO0s*~g}+<4u(A;z%Bq1P;1ovo^G%v zbDSR%&g_=HokVDmScvcD&OjCy(Md4h<0& z`z$M=dNa@V59&Pel-g9cTvyb!s-%gj%Q1^NB`v=y?FpAtUHg#WQdEOo!3Mf!-`T!zi+1=)qEPn_0v)pB z%1>jWq@11q0B@(;qn)5jzxcuCC8Lcd%mvui<>e7C_T#re)YprMPUG|_YiW(ma|>V@ za{zc9`=_oi@G_dmB`UeKlm7tP`Q-X95*oq$u;Of{+uc~QKXWaImXAWD{^h_A=dDy+ z#_#*B+&=#R_)uRyH?)B;?|=5jdG%_F?7MDz#`zK({XW(8$l&_yR+k3)pARo{^f5Sn z{{Zl-=DM?JuJBc1&3?l_aNHf2=3OytI_QX^sze;d64p~sM83mA{B?C{o^+0;%6;mF z?~qzEUCWppmBL(y#2F;~M?c3EV#qD)D3cI8%%pAVmtw8ND1HZ8RTz@=(GEfIkDBny?`PR6?(Mi!2$mfL{5ss~kSL4c(C%VFEG2d_=E z4Y{HoxLMkLH~#<)L3=2a*VTEU=@jDIP9&{$9Vrzm>!tc-9T0x!%dVXC!f~hv2E$K! zX>q!!JvT)qE5}oQ*xrN>sO_aHOSqm~lVzzcDybVJY;@zmqMd5O(W)Q^!%$yRr>_$l zM3s~>(Ro9D#EPHr(~N|aHvo!sQ3MpIY(dc&jcAqyQ;r3tml{lmSKZyWxA4@s?LpN- zp6s;HCB&r&?QNk?PSNYBOruJI=_)l|KO!^~)?0ups$2E`8Yesi{5I}-W!!Jh%LSO= zvdU}Q@jsrR%{5ib7Ga%Jc_~us+^(9hb4gQL`XSPe+V5KF?@1!4K$5PXo`ab{h`NZ& zZ6Al_q%@)qsx;hUrT#ilhPtMtmVqj@4A-T88hk{f8Iprgm9VDTct%qOT9l~NKbE9M z;Y7yrheRb%eL5oArs}<1e!x{gY1dL+(E%YT2IDP=Xj0rFgQ99GbrsV9rNe6Hl+AMU zE^GeXy|R7_r%hEEoL!=|ZKD`9P9`5mww9kXe2$u#9Lfu#?h#j>_SAhx$5S3hWEje# zDR`+{j=gmhpE7&W(H?DNC=#k^mvv0MB?*@Wq963m{{Rj2r*D3Nh5#9Oo#R<(g@WOd6hJx z4^NJU&=if~!7AA=GOY6$nGwR%vD6fTDm5BtL)$`=6t0c?g1S*5*ty3IEvvPhmI6Ms z){0Vsdg~=PeYBc%TP6nG!|_zRC&q3##nMf(;=-DFg%P+kH27;t1g(2&vXeyU-9npM zT1ruaFWOLTfAG&!rLfSb^T)eTkH}btkeVWeWb1sE zdt7yb(Q|ZEQlm+l-kPPsaMPehag2iHjol;MZL*gROjV^xbp(U4uTi7NYH-YNpnl6#bwDf+%z>)0&(H#XuaPndLGbLM;! z=ECsY+E-)ETs#zGeNeO= zUSqBr?^@a0Uf<1B=5ZF6v3gew4q^PhBjP{luU?#a1NPy&@ztI;F>np_Jg3B2D=43j z+@UEix%)R$-jyY7k`^dNv|LZ!h;AGpxgMOO$GY>cxL(a(+e7PGpIQ#yAb&N_aNt{V z*_6pc=qf|)xRRBjY1oAYZU{b_>~Y++;;q)J&2!s6DFEm~6EEQ#>`WQ1NVZ63`;F1F zagh|2DjMDBmy^_dE3E$j)-=A0QHaeQ;f@rpomFdf3hoITQ&!KBZsTyGS+~me2>$?? z?J55NhAZo(wS&5?yMuRR>MlwRH7(?KGbi-8ckUyu`eCuS8mb+P+*mqZR%wr^ATWnhv35Vm`j5|5t&T%ddo9Nw55r1pxhM-bWg395VG5eP}~%$JA?4k?G;p4F&M~en^00Kmv?TG zo_2htzbZ*0r9cMO7r}8>`h>8!t(|QJ_N6=#B)cy|f*Q9S)ohAf{^B1yioo zc77|Zy2I*iNv7b^glWQPY83_jgI>}c6{)~EzazDXvEH~jAuV=$POWMsG&^A<%5m=E>_^xnKGSBZ?I0b>X-&1E z#&auY#v3TrW%HiQ-^VeM)RNx1B+M$?e6k`{nfK^n@K+{aL@&8+Q}wF$K)+E;z6sJ9Y= zuHcVElHoP$vD7*%I7W-;>&5IZ5u3+W`O7S+)ixCMN;fq1)S2aw9Tpr$3aWl#TX4%P zwSQ|Z3!pzpPj7V!tLdxkYxLDqUch$>bX@*^Q;(!6TYQGBq&g>|iM2%z!(cUExTnKH zDCK^w7UGluMN;zEbuPA|m9#>TN1+?(4iyPY=zqgLP=n2UDCQr0S|rVh6>#RTv)$aD z&cRTATE*YT=}l0!7|DHD=zA_2c^|=2_=AvtmR)g5NF6Iqn%7y( z9>q_2bYp!2>mP&I#|Ywl6*BPqG47f11?e)~N(;;=4&;RTfwq~tiSBg=MO^tKEiQJY zS?)~xzC^(|u{Ioiwr!l5Yj5_^9EyMq5&bZw6#Pc|WO3$h-2G9-o49m)A{6+qpPq5@ zQ3oajI(f~JycO=-5POVq&{E~$jmA?8;kcxlaWGfX-ZJf{)L zkS$!L#@8s#O1EF_PZE&I(zK|d#3@xm)AIxxib*h;sL*JuGC`3-=g=1cr}O)%_KJvs*%T`kElzhgOSTX9zz^nhFJSnVg|b#H%#GDES(nl4j` z$i~+R*+MEwAIV)Wx^JBr*OIu`7g_P1D|3e|xZSb1=Z_>~@okC^s*6;nr7)C-8&Mxh zKr{z)>8kEO$NrF=&fHbLPd417%=Fu|H}U=}hL&t6UG&_8>4AQ0D~OJDfvTdo8R+B&SHR`Xv!e7ZDr z2wOF=#~l6Kr5B_)d%eTCB%0KnP)Z#AO4dQLCeghVpKZ3gAygotBXul_0Z+$Jp50Vg zF2GE#Oy!$a1r5t(twH5%rpf^03uz(YLiXrq+VoO z5Zc8a!x1gR_sP*fwwq1oF1jJMnQ}^2eP(C7e2&`Hu8P5sHm>TMcgu`ULyYqgrvBZwtL-$CK}u8OzLXCIqp=5XQfcrS zsuJ%tTE9WDGK#9?*A>p3>xBIY{RX$_V4Se3#*dnZED$LvCW0M-kDsR$kMWDYPMUC?X1yihFMO* zK|56R{Ix#L!)OHcS?tba$m%zuhfu{8xT*Ekxn&B$<$X#RdCkOBXAV9ol3x^I(&4v->Pb02u^iPee|U& zB!rDmtyOD7K|!Py9rphKJqjf~!idUS`a!RzjWg9D%#si(@%7S6l$fK@BB|9lLQ*PE zLG#jH@>GVf1UvTmj3u?a%8-`@Qb^e8J4y|eq@o_*`?@M6dVvbvTTmxVv=nYN3CjsA zx|$8osMVZA6;n3gQrcj)iaM13gHWH1H0PuWj%Fzm;TiTH@ZukQ%@E2r{TxhN6GGI@r?NsaO0>~ zTu$c&*)jNZ$^EW=?VI^8F`&8=M%UG*)CA6gxC86u&?3A69u!;_(0lu~HU%_(C%_}u^ zVRdOH$BjPUa-lJrSwnIoxD0Fx7{gO_SuueY+Ar~dE(#?|6g8zP4xEZw=u~&ZMF8g9Uq2af zb8U!J+e(c>z^a=c15E=RHBk4_Mp_8vLZuY|o`+H?qGfnfPVM?rM1sVpxK^~#K@FgS z*a{!3r>=z%LAd%Guhp>h+x@yw2q`Cocu$%9n9pJHj{@=K$C2|Hou&J97XJXD36&B( z>9SnyYCA%Q>TyHl3ENTc@-W-Bt|{2q>i7lqHR9*}xO`_}z~|*2XlIi1-2UMS#X{qJ zUTlkHiqPSX2ustO?U6CmtQx3xBAa%4>!08A#}l?OqPAu_hO6ph#+)Y-a>vAG{yoKj(jul>y0f2 z#m8PTVAxf$#Vty=+p()$c1qkNsP>_rq@6^%UACtYkw1{*Vdi7s}aX-h_6BLT(sx!`&a(n zux+z1xVMluKOe2^C2l?a;`J6xW?KTKU;G(b)Ij;`b%ysxCc{GzeST^c-xjrgqDoWc z{{U6Mu4DUceWG|C;W@m+?%{6EQE7MCnUCR^>}-naTn>9>wEqBaY2^0MvYP;Qx@+7pWmrZ8V0xj)L=#0LW4-}5YrE){5yLaG6e zk0lTLW7H|Co`$-YGx8Sz83-RG5Kj{{Z}t0q>6dc*l3g)JWkvDJ#BLz*-V?E$ip}mG zB*sOP1UPD$76I7Q<$1Z(lIYv#%86Dd(%_gStliu79UGCqt zi5R};XhljQG|~pZ4U#ot$Qw!&*QguQqDNvHXcB=>mZ!EI6j#nt$}J6q1$OSQPs36j z5G>I}`lD6be-MA}3-i;!eh9@pb@9g!;=FH<@c{3)ag~NX{p#lX?QylPjvqP-1qm@kfz&DALC58;(}RazA4B`l;Jc9 zAM-?$^3$#3*xt@alJ?|(Zl7|AZXfqz{pDs{o3O^oaI1CUPyWGUvmz*)cz%#x^&O}hiE&W$NmvN zkhq?M^Zu%s`Puiv^N;RX#4$Ka$y-k!H-y2vL%hY4j$<<|_crE1B(@PMZNgbX72QRE znvu12Wsl>~+dp#&PF!W=k()>qT#f$H)OO1M0A`gW3YwaoT#6{pbrM?MheJpyAD)0w z=(I9ofkAIUKkCp&R1fYMt$$>L@Tx)7WD&6H75ZWz)BVw)6qSx3tW;al0IBX2{{X{M zQ*Li{+ApoCYJ@vou%6%6|55~8*0mh^IFei7kq_P#z zP}fx%3~-W;bGD}-pRjFLN-rPxWTo|(vcA*2aSBTXNs`L%eWs^k3vOfJr|>#}+z@w1 zlAagAnilG#jF0~3+%GG%$+TWR&)h+Ik1YyBgj{8_HMONfw1SgjMQT2JbYM+?QB9nZ z47On#XN<+idsUP*ZD}5iLb+(A^JcKOI;{i+CPq z5}-+Xdn;4LpZ6+##dt2nF}@?Q@fROjlSI23)qq2WrG)K8t(l2Zx_auG_l8+qy00Yt z>dqY0%oGDpl9(X>0Nt1OY@q_@#*$P&nRsjEU;a+y2B0_FOj$Yu_@`gVE(c(m8UFzN z&OdVwQ?uuak1dS~A?y`#&wa1bb{;u5zB1 z`)z|>)R)%2(Z59Pt1dalZ85NVP_Q(A6;sMNz3y6XQfC()-w%>*w|_`s#*rKnzt*K{ z*bfDpebH~l2XDy}W1NYN#!A#TEU0$^G;gw~I;U+f-5K4ssm*cb$K^nN16bTY>!nFk zs&1t^!<(h0NzRG!|Gq_~pcVe16OfoF8`EL^?YimraV(`r`4h~3C;tHDx9$%A0Q6Rq{_&q; zyUQc{r`rdRr6g=)xtq4(ApAS8g(vemLwJTxwe><`NwGxGzzF$&$MQ^7{n3Bz>UGy= zP5VA&msgYAOH!}anL*G003CNCr{;9)_?~F=n!kE--S~*e41_kn3V$R~e|7%=`*hpg z@3MwK*QHZ498s_T0N!+mU^8Q^q<#MY@~)U{9nr1){{a0IivIxJBmUXzl7wsT*{3kJ z`=s|sWY`@D2l=Yq@g~23)JI}73_tlu{C|~IoNa(GnA`X|pT>(t`P2UZYW3kMFMYRp z3msQz?n@4>jq6$;+Fe(szK1Rk{$hV0=%Tl4hngpS?;rk>VB{V`{{Y&-l)RxI#Xe}l z)UAZeenWq02?zfGsW&-8Z^KpsY|}>2?LH&^Dx(W1t6xv?EkDGl+C}sJ*!!xSFqh95 zuJmP2Ie3@=J6EfnH7%Je>qRQl{{ZCw0Kzxe#DBDZ`w6=JgZ|q&ZYVX+yxD0=x&_(p z?ga1ZOhp|`hbNhKlyBgnI~YmPx_mYuAL0-Db}pzXw|Vc&7^a1Tr^G;ZY%68HZOP^vKc#M>3r>_I`V{~Uir_LTZ!}Lt$H*!MpArY* zT37B!;!A23NTEuUhZ`WJNm>tTlBB3rDoNOaDi2*sF$z>_n|(>A+y0ByE54qT(+N#( ziw2x$M6!w=r}5LtYt1$3pn>V7JEC!qURO*2m$`a?RskZKbJIrTZHQq?3Q_r;2F0q1 z>64Q0C|i+jV%*4tr6DVE;3!=x^{?ft2?o~aquY)3szJ%3RxC@~2q7{QQ8VBj8~*@~ zp-mNARW{N7V#12p?oEJVKlC(D{7;6OhH52UT2$32-EZ*X10L7?S4ZlUNB9cqW`6`x zSa_xz?!6(!y7J13z9zb8$W@zLL89|WN?WBWS3#np1?XVXQ9`}R{!zY-Ad-x<(Q!h8 zfOOIWfiz2VfJ)56J5y3bK}4ErB}O6An`%sK)mF`LQkzj~Km&hG2Lw!ho+V*RFil^UmSm3162ol3V7HM%t$a#TnSh?dsEfB2*1Zlh01=?{>0 zNo+;Nr6Ps67v5D>R<*8}9aN2?iEX!kaM2&mkSnRd`YQ6;ITjtsG^UsU6#5sx<)t`P zHt>wJu<-Vm`cI+Kz^Y)kX{t+`YgD6-Vk+O7&xzXPF`d))d3>lv1#I)NQNiqz@aLs>w>%Lj9+zK_Z%c1f>F4X9NOrWAIJpQVkCybmY>k)!%lkpv{nBA zLRjjA*LpoDrqAc3IyE`3OBmiqbH?tGt~U7!jV3CBn{_0DD1KUZACi{RVIvI)RT;c2 zH?SDw6$M+XvJWw)Das?ryuS#bsAA?>4@ zMuk{jILmAoNj94%PPI1zQ|-4ldJpwmX-WY~74-nuTg*0EIVi{X?zt~4CqsQbtejWaOeuzA| z@KcWYuE`gYxPC-CeeQfl=G$(NOKZ1YV!n{;ZdI*~L8w-#IFGoOSgC%R=+(WKI`<#h zJ1pF0=FEmoPsipL&-QJK=e%=pyJ0uf%g0@iuWIXV?^+{BLKU$o2em`iy7cxY`qJrv z@zYh}47L({ZOM+|lzV?azq+ouJ~=kh;FPxeqrYEm#QvIU&f-0UEglau6~NjKp+_$| zw$zm;u9_=l9AJ%7#5|TuEv1E$S_gK{<4O`0TXThDow#*ROJ&YH$}6-75I;!he;psD z96l>Gdt&(hN_oyOyEZ+9;dxcE`x@--O&h-JZedkiH%FqmEU?O$t$O+wTrXa;R5S9-~k19h5iLlyFZ=JjAr?K}Lz%x>+KN zGm*9~X`<}+gS_X0KWDY^k0LP~F>6}?0I*$GL@fKN?(8mBv`Id?;@;u$Tc=6&7W&ty zT}RpeX=M|QmE{}%0BQb8{{V6)HQRVs2$rl-_OB1#BvL%YDJ~eK9^pj;xD?he6M%K& z{{YA~kh=FF=WWEr{vsaNE^S@88F{WpF7Gu}n{ad7nD_}zTvw?8Yo`o9qa6a^@p5r_ za$Iz(cG+KXXk%(h+3R{fSnYfX?cl{G~D?A0Jx^iD4HlMpx;z)Bo(f; z+WJ%}ddd52KHYM@+$H-FBOt62c1TJ}j_0RMR{bHB^jcFmw1m65`EL7*c~>)?xjt2v z)j3PWafcSnCW5||xP^A9W}SLaYer#g5$p=bida5peMFu5E^JbtI{yH-Rc`9K5ndBb zB^U@LQl^?=-BQ930YSEsjSU&cV?2jgaJJ$|DOlQ?dKXaWks||}L(xx|e>2^!jiwdc znXwW(P_llZ>AiJz21(4&thPswHO8$>Zc525`2>LcGT>5xkV$dJ6iMGwMF7z-7<;6#MF2HrV%(wWsb*414v1^JC!ZOZVilO%Rp@5&8037`wF&*Q3Wy^Tdw z+Zmk4(05nY?o-5rEy67vtz=|7F36Pr^~9%jMppGCY90>mhJ{*WZ9yBUs^n%5%-6gg z*>Qz#T~{eINj{qqN_zFy#7e@mhQo1CUGnWZ`*)LbxhSqwr4=nlZ(X$(;!;AowS}>^ zff7o>K?hag#Y$Z2zmjs?YX8JTORaDfE zO8V+$o;pGSCqyJ&inv$wKkyk8Ycc{`FWxwYA84=K1wRT+*6p%vJXtE*UB z7_W7kxSi6!UW$F;E;*9BdT#eHP=^Qf!KmsrNt3$OYA{&Zij^(i+>382WH}U%)eCUNTl1WZtSy$^J00kM@YKj7qRnKkjdW0ihZg&ts0B(_!{@22bpoO-FLs?( zgW}FKW7#=QP5ldGQQn^MK(YmWbxnD8=Ra#!#~F!^CU1Ica+f5%l0E&N)J!`hFQuka zuyeO+k*i#NfNcp`O!i^ZKWNYe410Clnw?(;fU}M$RDx6}Dt;O#H@b`pUx@@qj=9rE zV?cjR2h@%9pH_)Qg~c%E&2nqYp&=led;rv9Dpc558W3=zd#?C~;P*EHQ$> zpzES7G*bihMC3(M1yU+Bhk^=vDM3IpCD_YJ1d1A+M`Mjsj%aXB=ofT8LLOH8Z~hvy zi{`85v@PkX676Y_(i1E)eTR4VC~5q3sRcq@FCjP3KC$uB3R9%fE<=!{j>U@n4vDT% zByCDQLROL}0H)*PsinQteL+d?!vd-ap!EE7-3bZvFGS?Pc=t5rOwPl{d~u7-&9khf z)iI7@cIJ}w>CYjhKBO?(l%C}TAxa{>0n!~tG`ZaqkTl5sp?z5Wf_~MW0Qm)ZnVI=P z&WOKxKgQPyUA`L6_HC?MB}0%}Vf&L0gugXtb*h&C07_H{DeeG_H!jOuTL5GI2fEqu ztZa7#hYvXI-9jE(^S9iO?UVN%%DD%TGV6{#l8c+}@lHz>Pd$d@07O8$znF7w6BZ0+ zO&U`TF+@EcfyUQW4Tw>;uy#KwXaf(Ln)uaFUW|q|_?LsXw}n36YA*HgcQW{Y_mZ=> z$($FOqr)G$)+Y^a-0jg+-|ccDlq9WFV6KeaZO)&iN>Y8+yQ#6Rx#jFvc&Q78cCMF- zy0yziwcr!^g~g9Gd=BG2R^{uz6>?@DXYecTC3N>>AdZU2bYWPjA0p>-n#p-gMxeTJgJ)?)*{DTz7Kftm4X_ za#fb<$Fke;JA3~Cbq+wQGNUMN9c4;Sp`oZ90^aTkbqAuuO=mM5sL-#iAGY7vyT$|d zrshvAzU22uyl>5UT@o&Q8Oc|BoAx<)7B>6Gz4oG zcb0ReA(c9h`LKHZfgn-rzRHbPF@F11M?5Jsgj z!NQ?MY-Of}G_F|dQ=4+A2BMVs>QE_WG88>~!(~QVoQyU}-5&v@9;wDiDMFa;2O_wp zq#&hT2qKz8jd~{tSw_@~dXHT+kcvAPni_g&oM@2NkWe2YDWn5cIgX+(wumKYS89go zP}qVEdW|LR1SbQ{Dy$D2a-<2Y$HjBEgCf0_jEIk^h4ca1bx+StwS)ySpoKTjlkwI;>b6swV1R={YF)K$-k9cBpQWMx3*jIT=tvRo9S76R1LDHYyZ88kh9?ChE7-Xe=A_(Tz}B zeLcVLXdshh+!EaYsZ!Js+7gnbDN5J`fl^6306OXR0M?f~L|e68y6_L|Z_eIPyI|y> zAVl5_;TdJQ5v^-$wTJsvhNzaRN$WxcPfZ5E5RQDayL@il8K|M_i{PnwI6n zlCGe$4At6|Hc4((!D)s84XU+B8*VARH35!sItrB{Yle>MHs+tWAB0{M<4iZ?50Log zD|&|%yw(jCQZE?{j@km2ZPv2u$~83IB9+tjoO1f@{#MX@Q-dy8G!~Av9D37o zxLEu;@uo%f410~kFII{?&(n=0D5r_$ukL?k2UT z$#EZ={{267&Tx75=lLxoi|i7RQ1&y1TOiouQ2K|KnKFmN{x-FidL4TO@yFZQo^gbVYeEz3Nxn_re2x{{ZgyS4w5%8@2)}J0>TBP&W~x zANI&XR-bGd{{S|hk;*tolA9gdkE`RT`;*Np>;vsb_D=B!${z)IyPZ6X!tb(qFOnwS zrpLKJ$nI9=#gU7Pkl-XS5$|LLHrKh`+uUouxMwNm*LL^rjw9tC1<^5B3wOW^`mTUw zpI{%g7a3t$XC-EMc2t~a1GUe)zGFv0Lzeqn(vYt1!9(1UzPTS6^`^aRcN$e~Gr~67oi$$!KlZx# ztN#GWE%y}3{EFl#4CPq&j#%{EmghQP4tv>_Qu~l?>RV+V+Q>qsx@vY%NdtP0-R^!t zwz&4Ox;emqRgdAe##@q4My`~9-Pg!=tX~504}meb?OIM}b-(3#GjQ5WVI`X+c5Unj z%ON3Y4$cZnRs6}VHmITYy4p!2bgonRw2j^x-pk?<5!SjCtnbts>#^<&owr13FqEPw zx}Qx628i12Q|8YBEyF{qA5y{Y6#TUqfkiowJ<^ll1|AgtQ~p|x*3l@wsytun{{Y?b z(^7R$wbfebw590kE7R9O1n7#$ET9Adw)&LN5r-lDkEfvgJ$mX&W$x^{L-v8RM7aII zxPlzG?Z|3HyCm*y#397}HuR8tRtVM;+&#?45)W{+d})B(0q{S`M1ONr7Src%KU!_k zS&0(eJ{y0xX`~??#@^GU(I9SnhNkej3CFUkpOZWb$0|zP$sMW%&5qQd>8*2{RYSca zU7v9=CL}0#mRSG}RMIf3J@jC53+TFeO)p4&+=CNGX(XmVeSIz5ewi_q5 zhv7|i6@d<_`aJ&twnN9RD(BxVe1eI~d~ast+b%1|sdCq4zCe$0h{UPamYFW0xhYao z=VAy70<_mQ_X8s>yiIdWHCt{WY{$ISb8qhn!?-5_{mHzZ;{Gtkq2bs?mUXS`J}qux zF`&aT?D3>JmRdy;kfo@ofCl91*Ne{SY?}8Pm0O)?Y~%q&&E}K>t4^EKT1P&rm93%? zWpy2*l|4p@y;DGvRra^>MlmnITL&*L!yo>xw`TM0*9-Y<2`@r*#WLF}S=^x}lpdPK z44j8BV6K!0@?CfRBR}<)+|$jtgZR-^w6SGKK~x5Uzxvuvwaz4TT*x#4kFwR0qy9mv z)Bga8r%(Eczf2(JTI|xBTVg9sm4X72fDYq9(KkN7@q zV9B|2oAMrB#4)*IP~&Aoforzh)PhPIoVe*OxXoy#wv_;?QB_GG^NIb&x}GhpXlXsv z>wFwx)hoLep9%aWfgUBY7l|Bl)F^GX%MJek(l%q-gd`vemMV4~YpjHsOWId?S8-dz z(t&*7f84FYyfx$37Wl1v;9eNTxRhkM4gMxsF2`bl0doU(kK8cZg@)5qf|V+t8Xb0D zX>ykm#_jH}&*V??SUzKow|vyw&}mPYAI*Gy8|BhLDl&UYw*e^$>%XAaUv!SD$NvDY zg}sp>e7IU>#JT{~)!UwGPwZr+PDYR;x|hADP?aR9wf6R{H5CMnF?Tb>W`>8a;HO&J zMCVYBx_&w@)F+9)+FxqEYVntjJcY-+2;&yx@&+YuzQ=~%HytQT@-4}ENdO=9)PIh= z(I+#%iy~nz^4Fs2w%m3pEd4;|R-P;KW9FOa#`&+9Y*woky2op?WqA}kPCp(CO+|wi zHQDxAj>-u7P}^xrK>2IbR`>4oT2E3u7a+L7#^;k$ugAZC%|>!FS&C253K~f0QxTtE z#A!cVQw+f~#QrO{zhT@{#xEqikK}#;@_&S}-c*&8g) zEvycNg(8P<$6Q^U!ysHL@r8>6vKrA0%~9aUte zdm%1oP!sK;`Tf6=`W^eNeUP|^#BUt0en18@V*AH1`7rLk^*| zEhU95D>;da~d7k{{SDi^# z3-&$#X-F)rDEBm^nhu(c&gk~C;x}mJ zUBs=uHy==FYt(nHqvSOH9R*9X?qBw?;LkZ8yz;LfM~T9IMXj`!^4JkuMWXmyA4@lA zjD+{pY?skT6Sn0w1`itM5<+%Os8h1IkPTx3gw&7e;A!`Cs5A@XugQN6yq56_?wEY( z#?o=?G==+i-0~S!!yK5ZfCE$7LQsVgc0<7mK7i}3!*g$O7<(KwR85SNv}^F_e+_H* zSK~!k#F9x*LOm&_%x-R!NT6GOdUw=8W}QK1f(uU1o|0Qst;pIN6$Gl3()Q6uGLp4% z(AlWlNk-WBA!l8GeGU@~O&o#>TUYyvJ=D;W;E_ctD_y3j2X0P1WtD4L1(%#di3Dzv zfla@TlxGF0xZ&F^Q&ml=CvEiwTJH!dXd~PZe&5eRiAYqClUi5hpos_+qM#Bq5<{wu zG|^XRJ#1aKl9z@~0eV#2OUssZp|rRa)r@DPa)ytt(IQ)6PwY(I~SJ z&?pVfG?CFGa#m(9aUV|laf4K*nx_OvMx*J}hYeC&(K#bq)Zc2`9jig^PvSS!fGeVC zfT1kb-C%`ra`MPYKNO$^ei10~y`NyQ~K z1aGL4KScFvgk=8!P!gKyF}$YaZBm=hHnz~8QP)zF5rb5eqJ*gJ)7M0%MhQinlm~SN zn0eJn5iKEIJ*SFY9nCDzkB84fIjB-DEGjAysgcZOi7b6gxwxmMpDm%NO+x*{X-8T1 z=Eg0qoxo}6-W(`YM}r&cIwtGQsh3pQ{$7Vr~G~-T*hmBFcK~A2U zQe}EJ(p=i2K}cJrx&@kiE2!itkdO*(IHjRPS5dBM1?gZ zbxSK}-A}xP{VV8y9Y_sP!@D3uDs4M#G-*YZQm+(pWZV}203A*|rCT0Q5}E1(Pg6sv zclKi4g@8OEw!7Obq~oA)Y?gGALcrnZq380 zt-F}V&{nAqTvamike$j21dm!BB51TyBa)#nVtGx|6S~K_TO_JHi0eu22W^g};j;4C zOdi8UNysn|WG&2cih>ogzgR3>{{W3ICC8a+7a-ait=8O=+9o9luX27TU8?fh-#=^W z#X&Phqs4ji6_&c zH5@2H^kpKsh&iUMa8l+som^+#EhbFm1;Z&!N0fWJegj(DUPS4OLym!9a@=MpB6(wa z>QtAP#<+g$+$AHq=`>TXBz*-(*YnnjS01Y&iJgQN_$DNxzQW^sD1M+le;r2ki0rfw z0a_K^Pqb&a)+LlW3(sMbULhb<)R+z^{Yn1-(Bc&$>8WbW`>jYwsrvTq8g-AMRhcN7<|!&%wWc#s<2a{T zNbYdgaB00q%Tl)r^jNES0eN$@DWfG|EcYJv*IoYr@YHFjdMyks)8dk*LW?>&nUVSw zD3$diP#qZ|Pqw*yPpeczd&cG4mR?GI$fW%ajp?XTL{~$`W}}!>qmyZSQ{$lJbkuS@ zl7a!=nl`kk^FdU-26t+@_0wjcs++4?gz1eif|3Od90V~HytyU2FwWtu=Sopi6-HLX$?U+~o> z{w5dd-5jhUf_Pa^)XxL_l)Y!YB2;sBu-%%R+KDTQl>qShly%!xgccpI=&BnDty!V) zQq~2?vc1HyG=_^x`of7xBj;UbvKZz(`AXe!oXn9rj|%K!(rmYJ9q5lG*P5Xu9g=7c zu@_ddG1Q{%*xQ)pZsDTm-`umyB=N=)o;JlUDH#?*-(=@oPzsE}*;^#_Rd&?-TR{|i zoz+c(C*&?fWAqNCe9cIb?Ab5MeMN4*qNTWZB$HmcuegwsyA&|CCI*gyYv?r{EUeH( zgD51_bkuhpQiFmSDHR^SEhec2#S7C;G4wzhtSjvmtv*_9?ncmRlchaT*MEkq5BWLbIg| z2^|iN6&Hk(YI^7(vqX#{&ko>yG!%%As4GpvMV|2noGg);2Xs2TjP$!irNg zHD5`*7n0h1iSgCzEyYQjF>sX~_Eex%-Wns;wBJ~Q(;TZt*9XU{`Pt2H(}pN1j|XsJY# zLTT{SFr}Rez)7u6flF%55;g9EHK@^)B5hZiG|^e0g3SaLXdt$tfT>BL_0yiBC92dx zPpx!L6-_iP^tz-ai;b%1Wn6Sz;+SqrX)iF^yGbo8^s8g*rM5z&2}6kpf>aMpKFts@ zrv-}BalcQAXtTpz!sj_@ZX70ubB?&9K$MSCbuJb@Sefqq=q9v8;m% zxXH0)7PqXIn3Vm+wfZI2>MAM{Pf@E$Aaq8K>Z&aAP~EqDXLEc}oJPfPyH5Snt1<0t z9_M75(1Gc`tZ~pfS5%D^c5fvVOxQJXQr*V(?ADsIS<9L|UWU^9ziv%MeJP;)ySAi`Jkutf6z6fdMz&i8x)MVzG=PwiumB2rYN_SY zI;&R{EQUHN>6&Hj#6zq!zUP-(n(jK+R#vvvHCgPH^#ua0w+U{{eM{X%ewwtXZi_Dc z&NT{W;{G)L( zML=2UU8$*<>{vcxsxx`APS&dZPU@*&QoTvl>rFIS7iwxyK|m^iR=oz6(Nx2e&dF$P z4I8!4`!L$6Y!UjY`02OEK%#MxRO*^}9sSF!lG6jSGgJ+MumEk-QJG%qa3CahBUFVE z*V9tAri;?64W+V4*y=!sN~n*0N>MHfR2?!2L{W7*RjEYP2*pY+TuMRSzbz)IWK^U{ zvUe#8?$d61ksBxxcoEBv2UofhNz$VgMI-1am84yl0YJi8X~9jwr}=0+(KQ@(>XOCQ zWx?>NZ>i;1*G)qvspmu}ml#rZ`aj1~{Y4GNh-Z5gnh-Y{MJk(ag&@g_Kq^k-;i%>^ zr^h08@un9$G|A!H8~%($tSV%KuuEXn@1lEjs(iRnS?*tjjM{C{Y&LZV*BAu2uv673 z-(jg1QP--kxM_Dls-y6b%e~5~K^+J`;i>G19*QZEz|f;jHiaghzvWH!6v0A;P;AML z@Fj7D?t08Rn`*zmzd} zGNr_y3N+*fk#vrzDnrX$HGO5YC@7?!+Nc0w=dErHxiFtHpN6=%Wd8u==vUK5Y4$}& z6=pm;_YwOubChm+#_)TzOuG}}Th!*{$#x@YQ=ZMf?9EJu+-YT%wD>OlMvK~qm}9w- zm9DnC1Up@RD|dayHoddi`G%GF4G;48D<$_8@e1eWjuDT2(R>ufZhRZ(UIQ7oJecv} zpKjfCpDx_U5*%SIl!0+&2q{X^N@TPG6;hNbFgX7JA!n#M#_}tCr~d#Rs{kuH1`%3c-lag@khUYVjS-CYQgS@4% ztgu^kN(+wE)y4raWgU{FC>CEE07AXJY_Oc7%Qp|DB?J3{vALc%d4-tI7On9g&3zyJ zy*}bzApP_Bba*ZI6~b*ck0Px0?{>j?apXW_Bz7xZin>^)El7GD#iRADfU1tEDjVw= znBmh{O}Zb_R_6!I8G8*R{X^~c`vuQ0G`#%4{@i@aXYi*wuwh?zTx-W$g{t?*mmWfC zE?w|>@Xx&x-n;88%u|wEY$mB8K%|cP1HIli6>%h6wGLEyeAE%ku!@7>_Wo2;50YLr zc{%?8arYiuJd)raxYieyvxKnRix1moUAA^99|F2J84NudL#A8CM7pIxsn5jiDOdE$ zP_5&^pJkk(v$l>)zri+m_d;sg4~-AU!>+Zb&2Y=bA13_Z`xN;{n&bI@8_&bG;(wItGb>x+II~_DCtx8g(Q%y;1 zpGiMLo$E(;qiMK!eZPw1QfIiCPr{JXkz~Mm*w+ht&A7~n(im{G>n}9&QnuV}X(?J3 z0+OIX0F7!Ok*@%b>ayZDFwpi(NQWM3ezw7_K4(fSEh;D#QRYF2MStAxjw`=vx*bMw zP6rU8XK`sUUs6$55&WRmplRRjQb^GX-HK7Eae{=Mi6i{9!8Av-Am!t~M|e+afa#)Z zs-8{Ppsli?MLr`%8g4||xJ5{!f$dn`Bi6liMZhT$f=w6KSN*YnYU@{(ckGYMKPItJ zm-y?&lUs+v_$AE&`(8(s#CN_#*V%PFuE)12DGj8OlJO*pp(k9!o9CR`M#D#Ur||px zt=}E78wox9e~;Y0v3})WY(EG7;@F*z)y%d5f#(=QF0+F2?lz_^7n@5fZ?R0NXh?1N z+a#7=N|989N`X#)j^#I&vQ?VZTwLMdduZ`h`Q&}mzTCW7_Vvs$>kl4zw~Bc>_DeD5 zWpVYnrkKj{xXEE|A^Vady0%u*mf37Llq2gjrrPe@b2%;RD(2C1jJ=F-6SUQGfvE<9 zy-u`FjDo0x(93+LqttZTx=@O$QId#AFt0cJow~zdwMxF&B+i=qhqWy* z%OI`Al6DCi4|H)2&N>9qcJ7PqSM3A#Drbi+826FBSz{5e+~ap?GVuOAkd(@Wh{bs! zNGnOJZqHIk?{b7Ux)Zfju1U$co1RQ@9Td1MbgQ&)hxPuQ7jb--@+Zch3$X-b{zP)c zi;C?^X==7x=SxAl&i0!t_Tby2g~@$%Nup4Snrky*guJ^uh|u>sC7bw}q(Y>4FPV=d zzwUFxEpz7Bd`{q*Ih(!eZZ>sysDbUd9@_{y^MI2=mlWCgYiVb}Er6`2r``Vm zKawX2-fLPeCV3b4M*Ep_+<-dywWkU|J+DGJ=q_l*@TdFraBvDO5EY#(RgI z{{UT%bDDg_Xtg|F8g3^Z0euhu0Nq>47`79~{vUX|gyZnB%Vo}W+by_TZLwm*VVE{Z z$+N<3#4wZ{+?G_GsCzd%Ynyv9j*;(u!_8~s{Fa-YIgx=ISH>|=BEQF7@%YcmDeGUWKC-458u0) zWbqfL+aPtvN7lP+B?~M9I?)PlH@K1h^?IFoyWESWDuT2!g+W*GBf{0Tqz1 zb(G}N@0_ohn7s$#t-G62vfHSQus*T*X>Sw=nbk=#+6}~v3U=)T{(I`U6=$0j8YVDF_ba9lHFrB({MmvIH`BQUEnI8j9Yili3yQN8l@~ z5uzpTl-{Z;>9^UD1z&M~BT3x3YRQ2A0QnRhVLj9T0Qy<^g{$H}{uDk%SHHQ^yZ+$5 zOoDf6_Z0+uJ8cnv@zuW!hyMV@aXZ8no8)Kz0G$5-pYjTnqoMqDdhV*cCgthX1|F{c z&A18w0N)OSv}zW^%)eKU2dwJ0c(cGT)Q@lWxQ&P@Bh&u?f$g96>&Th552!yfyRI@r z=!Nm|{{VE9kbJw~Mk~7*g~PhP1>9L9{I%LWv2AT#_beYUaWe(;Lesk)tJ8gTsw^=J z>u>(i7-*5?f0}bUd(1p18$jD|UA)UxEB^q#Ym)n{+CahaKgn*mXX`7IKYwzSecF7e z2_L2F3KjVO0AyoY9vEwlVO6|{*66N8Fxq|_itAG&0?!@QMqKpL*xDt9)=Xs$&ihDS z9zts6yO0ov4LTKE!U6so<k$ zns1}3AjG`_A{=!+$nD?q(sibaTU|wL{44ST)#6v0el~O6@zWa;#_zw|^LYTF9@U!i z$+t;)MPvY$J;b`BU($o9S$tEj@y}otBT@GzuBG(lY4Q7gzAN;o7dY2AP-ffTOsF&3 zZRm1Pdywlcpg4u3j-g2)jd|+X#y1v`s9GTK;aCAQ-F!p$ z74o|c5r1b98B~e(_Wcyi$(H5!55;O$y*q`uGFq|Ktw8=1{!5k+(@;OOkbfHg05wAU zO3`1ArxzNiwV_=S`$2MwxP$IX;>JMqyr0H`tEBLMnO^@|&i!9Lm$z zvT*SK0I%{^cJu5N`F|RJHTrS}kMzMC6sUd_+g@u7AbghQ&Oqj`if{X)_yN3gl)QTK zF54L?XEwJ@#xhwz5T#1Dtj~xPeSfB1rn~RAm*z__%G8gy^8D5k{tcJ!1EYXHhMhh& z{FlmPb6R{gwU()gLiN|)in9DiR~|!Db~Ej%NO8AXA;c*vO$Z@C5JsDv0?H-ZbroNz zUIS;wzTp1=Y$z`p`CDHECNuEqm$87-Xpt zw|(~+Rce2M)EK}{$P0e-i;`Kf%+DXW;<>3kI%2oeD6<*a;ZB33OMnCRkJ{yeEo>%iFAes-II%y4#rAcrN2-5bVRsR6O z(;H}00TfqLr8%@t1P^cfpW~#XT7pwrXdtxQ=qKopvWlJkbhb9CE5cNc#{U2w~BuEZns)M$`PF=2zXG!RZ`us=xy zMuJ>J0W>6TI_Ro`sa%_Ak#5Oktw4x#KdBi&5(RpVQ(Ux%EhahbEm5zxy+4=ZsFhOJ zsc-)Pg`=Vbtw^B*w2e9Gl9WwT_l`K%GvyqL4l{+=q+c%6+)Hf6Wd(F9G^(l4e9n$% zh}&0ai_{Kw(bZZ103bX_&pcbPW0xHE^8!T7kPhoJ3sHU|%1XOj2d=|Nu)S;a9TW?z zR|;3EsO`Meg&GiR)ap>8XJeYQc`0ZTHXj`=<3zSpq^G2AXstqV)ktd+d(uy70Ppb9 z9O?x+Hl+6)Q3HJybWXId0!bhar%Wfqs+`uBic+Yu=v!E$Yd^|ssjyeNsk>&iYVf7o ze3~DrBVmj(Dd2Wi$ z&D@k)S9fovE2r8jnvrhV>!pbkbX)w7uANPUn(eo}M6`F*O{#NlJW_I@xZ0}XdqDYZ zpkpb|Q$+N#e%4%PZDnDnofVAn{{T?*It8xmnqb#dnx;w=;|`>y)=3T|^#mGeE_HPY zZ*J6=DYDx8ld&3dz0+;fD?Q_qx1CCy~&dZ z2B`Fy?nEWVRrC?Ehrs?CmdDeggFID1#tC^VD;05nq@DF9H-f9=YnpxuI#efF`pQiY zL!`+Ebt#hrqH^Kcb=MBOM7vt#Dm&5C4MK^$+0xs_r_Y9@7UWGeG!N75yLH(CJo*Id4{wd6LQyaJOBp>WjCM#i!=sZuT&V~-xdrpoH2!>Qc^^m-27nQZSKgCP|< zJakFQ*i98?xA4`zKN0cxckQl+rru!Txjnw-c@q)2f7V=ioHm)3Y_b!okj$v>eNB`p z1kh0Q*FpC8Ck4D-guYoD;=Bd!zYM!MBXZihC-`>xu2to>MY_y~Bt~0Ls;VJqQWOv7 zHPBu|g+k6??~GCg*P?265PbK7c zDY}XUB=rPo5j@pb-%H9DgUoU3^fBd@rozsvfPuAtZDt03ajP^#H%CGxo=T$pi& zo4UmXR;iWt!Q8axs#X5YCGq#{Ri&%iL$P!iCUHv!frpQwr4#1URNP@U> zAJy$E!axM+fcmOCi)6nZxuA`_Obx(7Q4%NC9RBffVI*rX%ym<|c;P}jcb!lro}* z5V|cY?WhE5f_ziDxGh#ck}=edKBav6bMGkLxl@pDnH}a@q&XqwdqAj=mVrH~Ps*Cd zWT&gP_6ySdLlPjj1pcC-w9ZZjGsV&tLUHvy2jMpnz*bzNP?xmpD(@Z<6G&D&HVRasx=`cTI z)0Z)BYLYE8!^jCHrkiV5Ydu28-rCToLu&37{u-F8sJ)>*1$3m^^rViUjR16cn z>5mnaKI?{wZ6k6{fgp8GLXd+plg3SaLgZCm?cQCCZUPXf)HxLztFrH+lAXtXyq#T!A(!g5ynP zMGm&GY7~_X!5WbT!=rUVg?VPlm|F2rrK<&^^_xwRkmWZ==Cq0#cUev;*B$==-<%|q zt%f@u>yPqCbPJwlGO<|?MgTV+!AJ|F(2HHEpJLp}pd(_hnbnljwWDh~DqBlOSJabI zQ^vuMA+)xAv0qYj+zhH>?s8N0DT!-gQl${fOoF8>>?k!M^0lI>v9|7!z%^EGQsuc9 z7Y&ymaFC^Tg`oU)^wu)}03u~H3#ak!HrR&usPe~+MaP(4N0Yc6l&`8hWg3!5sOze> zMYqg)trXLl$YV5WVdS}0!z7ea6j2o>j7Jsi1M<`hiHR#mV`3OSDe1;sQF+ZSjW*nJ zBc(vdl1I{%gQ^SL_Qut)#$sI|sTwQoaGQ$oMi}q*xT%$TOM8@nqFgEUr$MaDw*px; zTUeoOyn1z9!_RZ;=6QURHs>WyDaD^{qDe|cdJS9K+clwQZ*H9-)jK%D709kvDDFZI zGSU*`Cw{uM2Wra=(z#7MUBxo)&LZ=8 zXZH5iexg(;6;1yDn^jjb5_DQ?3}|C_IVkd%BgT%`{WXp#FvCAX?jvAn=qc%^tSpUp zIZ5`<{Pm@vShC`JtsDU$l4v~!lp;tbsmY7u#(S%|&jqV| zQ&khNSR0B~|I9xrwK~sA4OpSn4L~z0zoc@a?{cgh@H&cA%e*lp93g3B0v?i%|3$f)1*9 z6bR4mLoI}-VgVX??f@wMl}b*dvRnvlCC3(Ml#ZmGMgWqZG-#>lrRzTu-#_WuSuMmE zahXqZEs^yqoBHZ|Z79mBc{1#C_O&ycG)WZoG&)NGQk)Q`sT4haI(>&kg36$Yruu0q z-4V!HHO|Of7U?RASF?&iqKN!Ph?J<}E?r3bdU!LJd80Dpn>})ESS}|tM#xHC_a>?E z0?U1r`T0*+rQeo%OeBSTBiTVdmV7^1hWx}!aA>)7hM&XpZ*=px} zb~EnUI~1Pb{lYz5dfvUi^$MHm{YsEE)i{s1))_eIU9av6z;b>McVhiK&GAv6gMV`0 zD!c>8*q_4Ls_r-(nF2;j!{_aiXM~60u*#7`O zl&W7NaT^M16~8qsrmK+&UqU0@Xf`B-JU|L5G+J&rH@TuGeKp_dI%=u;ueZkGp1rn? z9t)j6Z{NJWNyM1l`@v7THUT4>ykhKg8_6ML_NQj$c(Q03F)nc?B)cv;60g-ybt$18 z;FarP$o;)!-~77=Q1#Ql%kBLZUj@!&ws5q*6#!DF`TRcLDl_+$@cY3(7BBok%6Q6fxv?^K7Y(L=cOFz}u!xY`CBhqR539Wd(Ig~ORy;>Be=7`kP1JWy za+?b%WbFmi*%JOYEdOhQ6!ztw&nC# zsaOfGbvN)QKl2BTT%C+_9|&htIQNjTRU`XuK@G*ExWI8PE-aKGNe{+wri*ENdWt9l zDziL(-ysoe!nMP4d#rqwVbOiC;~qwMeg6P*AB=Wy7`dfUIBU+l8rrA$_mT{DADeBw zHAHvDQu`M!+boJa$8aSigV?YL;Zipt$L_&*nlifMK8H1I?V3Ns{s@U@g({*nN+~tVZc|#B(;#^0B<8ef`60hc4MQ#QSct+YLP8+poCVl#sACD%|qk zQ4P(2EhBQl;5eL?_h)vXk^KApx-N$O$Uf1WVfP(lGv^NznzM0_l32Q5es#%Bm{u&& zNo=@cT~fkGYC^kzr70I+DTqg~4LEdyE{=TUKsXe2Hz zXsr@fWrt)qebfG(&T)&58IUD4Rfg>jMY?iE;-#5sLcsacQf=;Fm7AL2Ql7`P)Gakr zH;z6ZShMEW0xm$ICah70q z*6LN%cdNCZrS$9}RIlo;+UEHla~q^Fz&AX8YoKHJxD5}RKv(E_z!nXRu+V?JKS+;{YzSbQj|?e_17S|xsq8)lC@g-A!8Xv zbX@N8Gl;YQ05q;AjArt!=ZttID+*8yF%Kd%{ ze#89a{{Xjp?SzMBc+-a7e52;uXSN1qYqh3x�@jxi@>-n_&n|Dnn|mHv@54;{lV% z5$n2*_bvZ4w4SG9GivEdfhrnIT}>VK#4P%bV#Z`6OM+wfmET$RbZk<2-5 z+mLxTFV3&Z zu2aDIYss!``~du^ ze0Gd$-B$p)23!ZqNE8YaetBGF07XVn zGWRRzyZ+%nB3I9UbM9b=262^*aBM4H3y&!dml!IxGG|R%5D5uUlPzdbrB7h5O?EB^ zi*2ooSUeUJkzBfI%~uxFB&7nU^49lg0cG~Cp?#J-X`ST{+*^Y!k6MMp3gVfH&AA2l z65(l>b_zcgCWumdoSIokN{FcLK9trEon*uoF4lzTv^ZOO$`|QVBCfoZky51+zr{wn z^Ak^X(pz`1tidQ`RlL%v5rRChjzZrfnu>jcHw)xNMaS913-I^Oql9pu-ZGQ zDy6IesJCwsL|x#H);-SwezKk=aV!rXJYV1r54Jb#$Kw1(#b|IEl3$Ghu=12oRTm1X zPgAcXWu-G*x#ziEmc#}`09*(E0J=LY$mV#XzA?bLt+w=ozy zG1E<|x0a%H%Xt)r=OAC$m9@r^9j!hK^TGT4a@!oI_fqmT#^koBe0D6Dya&C+QDWO{ zZbP)G+qLOSLHUEPO5m;7%T|hkpj@XVBcCBXbt{kzTp^`Bzqx9^65>Dc)#Emk)mI3e z;8K)1u-3FE_f4%zPyl;~tx2FAbnJ}MsM^!urZga>rcy;Hk+h?X^hlSwrUaz{ z)jJI|_R%Lr8EL8%!Uf;u(%uS?j6+Xzs7czY8gYW95`LkYB_g|lse1~H7o`9KYtuzU zOQv6DD*piZNcb(3nwgNYMuL7n{0<8L0O@tlz0)85D?c!_oJ9Ws@<+(3^Y=nYi}w!l zwmB3nOgs-tr`#fU_oX_Xvx&(;WrWBfyP`mf5hLJ{!4emn@2nDTx0v{ zsLT7a`F7%}QZ6=cXc14OSmSF+!iWC=irA|806$V!D;s1K_fzuLjJ%Sl=!?E8s#2io zY$vKJcU@KD*~yjmwdOln-MAUF2O1uMYJImU_?>dH;@(mJ0ObDwCFu)czk;}r`@fp! z%w&Y6J>7mf>Pyck?t%$XhZK+qJ+$@G98`!Ks&D3Woo|lX+V5#AAZ`c~dzi;~ z8hyt@`7g~zJL&gojnblMen(!7kX6eQG-im@mDGO?I1Y+DEnlO5{j5Bf8z%c|!NS^_ zWJ==v$0@a3SSs#QX_+WqSx)sd?35@z8uK@~wrA_NeYDYZ&Np%kOFr6wSI$5E(VQ-h zH~WFvITAc?+rj2cd-n-g6ibpV5)#XY@d83?uDihUKBhUW@UQa!0F{xywK3dCJ5(RW zwf+Ho;>Ua`0X+}I>s;QdnpRM!KOSZ(4`PQzVO(VR)`n3E=EZj^74+S3#&#QsXxvBoXpnyu*DFY|d%% zTFY5~#0Xq=`~L7r=FhhmDqkSQL-*biau#83c}VV4ajma`H@Bf3(blo~>uc@x;f8lM zx)J{XDx*0FGDjsl)A7^!ug-Eh!dFz9g>IAa2VKpSAy!PqU*n?rc>DQy+b73wxZfyTp~6&rN1WJ+m}4&n(uO_hZHTj)K~GYLn(tS`zg-iI z-}?5AJ^tVDvsaeok8=<9pUCPzI<7f3zi|M3wXtXvRId~&_5HCtfp^A#Jv?mW)k;)L zBC;5^OthweD%X%LHpfcpN7CBdZNvWnmsC8W$+s46VenI}URrl}8vY;fO8p2v zzeTdmx7C5K9Y^y)Vi_HWmH7Jxn2WdkDQZ@*62l>mhEk=c8LLMAqkS2sc_xf zbi4G_fEG^L-`Zv1xot;PoSx0mntor?$N2m^s&JVCzdt~9^Q+*a0EhRLqDU;N1UB@C zLrkQ&rBEdUa!=)`?gX6^p~1SR2(eqBAUlxE4Y);9Yh_*UA5q+)P@3uSZ%(NqXSn%N ze7oXHE04h_7}iC1D_iB}q_9ff{nk`I5GwJ2pM>9eOIn zr3DHPT`ldYOf58^f*B}CBX7?}jJTuhb*K9@^leN$=$sImE>S5>Nz?wFI;!a)elB5P)d9Z4>(xRk|$OB7)>K8w4bxhA?KF##e zIv}(b2GmjX)VB>(IGie%W5yzahw;*BRUs?05P~W{55qwOyu@A8L7;+ju^a@Zf`*&v z&EY68lx1kIKsM{9(iCx@w;WTlGe?qp+34qvg@K zx&|k}VQi zd6gBiqum+tT2t{Jx@3wuJRi9{oI%KK=IaClc7l=n)&(l!K>bwFZ}3U2bSbt_)aFW> zG+JsLLFx{Y@!*_eY1KDDafd1iN|1d%15Cu~6sN~fu2+wo`1U;S{V9~Y`-=>^C6%m| z28zCs*VBDK%i~1NsOlA_7bxf-wa3a*myldF9Z6Pgh_D^E%O2#EHUUVk_4<11n;UxI z{g2X$R$~+?>c!DgH(7B2f|?LWKZw?nMrop=WP_q8l0mP-QCOT#%Vo(gwqi)@N;adZ z4RTAsK{|&aJOQXejZ$?kDHJKAs7_++N%qiyk?){y(z=-ofdx=HH!@FxX1>X7M0BQ- z@D8fwgbIxmIAtVF1Aaw^*aXG2%B@#wyMMKDV-M&iP#VDvM z$L5%&mzy!yvrIjf;$|>gNxxYe0Dj_|mk>TW>NzZJzIMpm!9ge*g~hnudgqK@3p>J1 zyN{8_x4^1--zwiJO*)lEkiw{>5KB;#XA!izXR5#&;o!K*$+)ZwE#5?EWGNt@rj*=u8tctydqEx7EW};N*SU`V z(xNsXl)X_@g(`|aj-C*$Ot)2PviE)`#;^PW&nMxRC*)-qoZs1UHaxxCZad%nZzZAy zeo6;a11@8}Fh9be_^Ec-z}q2m4Sdva@bFbb`>eQ4zXj!&puTG32lUtC2gGYZ9K;HC zPkjp|A>&65={2>M)*NX4D%}I&q`kVSk8MQ?)>Er-NpUKn{$%vfn^dSH08pg&Tw2gu zSJV&kokeqMrN+PwLPuO)!lBvR()#?g=#GI|HhTQiefVo(#S!{XL#R&CTSagl7B=e9 zL84)z4~!ILQkUDY(_W!Why_t~zNHEivC?W&t%w4HQ=cD>+A>nZ5bZp)xQ^39q(|Do<@#f8Syo~~;QhmqWTeK@GB$L$Z zlg@WJdP)j0v_XY-&=5fyj-0Jf=wD5}G!h&To!>1L5;m_1u8ag=FH!omPHj;;qVe8? zOsANVURL`Z&Z;L?ur`Q-_XF|N0a~jDHi$atx0K};t|Sp!^cp}Z(6%6aG}MSE?i~%= zjF#DK8iPUr)wP54RX1@8sfOi^zi4f@oF7AgP@c8bKdDv7 zHA~p;D|^bNGZNPk$qI2qDPbg}4w`Cd+Q6D?30)xIFBn^h>}!?wFS`1QPioUpkB?nY z-ee6Z7K0yY=Bl$ELGV^Ta^q`|ixnnB7F4!e00KKxQ_yLQeaneYU)q3lTqv+#XW+S< z3#=zga%}Q~!jecNHs<5}HG6Xik&2=R?R8huyVh$jmwqM7wdK?sEOK3sDV3z9UCpe3Xm{Lit#oH|K}N^! z2`wt92D%RFltSOQ=_xx_p1KM8=)KTQFE_4&aMFuvpNalD37w&Npp>&g1?eR9&_zY& zf;1UjNF!=>(zS9yYWLj((@izBNi9L8R3qW)bl|EY)M=&n1d3_qnx@{`3aOU}fDtmyyt7C=oziU-o_4eU$B*#-ig(B{fBN-kZ}|3;dIwXctGrarQvn@6jOI zY*x%l8n3wmlH}q6ZaM4S*Vj;*(7{vHS()E*?XCSGMfdyl$0?qt6jkgb(UyTU1EAC? zrzLB#_6>|sbyBAr@np;VhTVA{+%f+E8PW{^{vCCnyvYXiTdY5v9sn<+^Pz4>~+`Efq5*Gt1Qc03XD|e@HKRrjevXU`Y zr>t*ck<=*~{{X_6{o43082Zp5zM!R{2BL?0ZKcDIkT!))mkS(@t9mKw!Cz zKykEhPeM2K)GL`^Apu|6+Qn|#s^iHVm;1+`jRnn#P*TD4>8bXXwCbqxxk~62a+NK~ zZ<@GNw@&>|tO44y1Ugr0r~V=0DLFi(T`s6B+Swsp>K&ldbGg-g_cb(E))=7QF*+wl zGIFFVBMxD>qp1mLJ(6U%u%}82YTC-ool36HXLQ=dPo8zz4i{Wkde(O&>e#Z23F;DR zP>mzDg=3|3U?{RkoLJ^Q<@QN+(pk60Ki7Qzk^FR-C!(>mx812mGd!WN+H5vzHxYmm z;*+;^Ham?*6>(EvAOe9$QKQs?M*U8u!o^=B>Xw>MZBwZPCd;V3Kcyd@f@yeqnwrx= z1>rQ`K?Ggr0!0qg{{S5=hg7zNgEC5%uEY{_nO)gTYd}QB5Rgw(s1!*gmAn`dE>Z&w zHddt(@%YnAWG;FVO4bcki^MSN7Ba$a*QKS&4G!Six8JQPt0-+L)n+d)-#IM_D>vlH zxe5iE-~RwBXpgsIcJ~PQ(?=`EK$l~T5&4f*8KJU0EAr9{g12+DLX-~EQNP1Yq&4X^ z&()-q;|{4RN_+M`np0cdNe)(D#D6n6!@_P(g~=Q{el5j$)`n$+$x&UV`wa)`W=muq z)liNwC?pfjo+%px8ZN+BCz&Ym9!c~=V zP0>xYJqrmP%ZR0rnGBL@mA7Akq1#@5~NRrvA?#oi+4{yn{M zS2S_`yOFS4>PVYrxW_}m3mc*4R!4JApn#+;1Suo}HIv0>ZmeXh4L%Cy=GNZrv~a5U zfBT00j=Y8NlP4ZScwcqMIU9^I*-qTrO|j=jYlIepo=dH+DMPZOwg+H!pj`riX;>;F z&^%Y4T4ba2ZdWhNamj4u-xpdu>ZeD#Gp;aOM#6H0Mf6Dl?-cxjJCzfW@JOy& zWwzG8Z@1Vll6#f2G2=YaRj%m<>&Kjlfz0;h!FWov-t4y~4|mc~rru%H)D*2GRBj0b z9d~G^w!enDIi+$L8FOVV4M7X(`|ZL10CHY=#u9PPCG+Yd+=<5VkeIg4f3`3VLuf;N ziEI0;80Z3PV0U-E%^VaJZCk5c8=Lz<9Io|kzgL3oTtnR#GzjPE_WOTS-TV0YyY{~F zekHyyjd;Q3FD#-i++FgE9|(t!wPs6*dE0ft-sabGaJ1sXY3xvCWRj$wvpu%MWh@C2 zy0Q3GGwu#!8Ft?*eEvPx%d?i299yn884Nn{?saKOQdD;}J)l(nI_c8Tnt?*ObTU6^ z6{ravaa794B9!#hHxi_<)d~d?2Bk*yB%*d;S>9vC`HnTnI0j8W4b3qLZB55At6YV} zp9)JV3rgB?74;udR0>o!P$O+7=N}E!Wsd-Yj?(&DX#?0uEAA`p<^KS3egn+>e`N9J z?jL*{ca_~4O|mDE>@)3S(}L63lXXG~{gZ@&+@&9BM1$Q^LFfKi?WOEBk^)@c4+Ypb z{{T3(m&-4e-^b#t&)l2K8#jX-UxIka!q?aGjK|C9vu@U|P`5kpLjK1U)|h+tuf{4! zb$)I*-73o8IS5D4=40o^r;;6Jdze&rg2>cq1M0qluvM{#9U2r!vVJGOm(T z-~8*0NLPDo1s|-b!fHoSO?2-dnb&iMu$`et!rHdp-*bnUuDjbjTi~v2#a-l|B)Fd+ zypN53!|nH<;rOmRcsb_gF~--9 zNaOD=@elq=KNWm{a7pG|PX6NVx27*Ed`Lz~YMmgh=4POhw|%t&y_AY5QCj@I8PKpg z9;3l!aoL7%**8L`k0`#}Sbq$14g<=5G4mu|Sn=zSi#P5s%Ac7P%e9x4^z2R1yxWa)Y;}ecuaB%JEFe5#x^NiBxa#~e{f$UW8ZQww{7PF<;$Ba@)epB zxX&iO(vr)J&w|iIdy^j7stbwic_bZ=t#!h#t>wq$x3^JM(D%0P%8t;gP4=1K&$@RR z=6tKf{PMu$KN4(DN`u7wlKj-J`f?N~IbNT#nCMzJZ>PGwRfWj#TvIydl0n8LjM5i!j|rGS|P^L`-i-?rEgaaYHO_dqZPDlB&iFV zd8>;2P2s$|nt7KkjfY`4R7K0YT_r6j{V6?-HkA-o;?BuP+?^gi#rKm2v}l)kk!7q^ zcUAe${mVZ7K393IncnO?ZJS*9D}Z4*rg1zP$02a8GvcL1B1O_tN4wtO+%nlnN2w=a z=-xNPEi9n(09<<2ah$GF;_^z9M(f}3>EZLCuR}_%X85n*@0dSveov8$c=yG(?rO;! zP#IovG1&{VS`*%Z-EzEKO4?}u08_X(+@};OQiDUGmd)ew?5pYUAJJIj@%J-m-3P!^ z`Yz4$NBzB+Pl9|nj$QkV`Q482)QM4nv~hkfZ~pOjxm*h+&)Hl50C=F}m3=Du!vIhd zKnAkb9Ny~wN$HbqtJM>>qziaRNezROx zlQAB6^GMrb!NVmramDybBfq|KMAisfF5KVkOJOZ6cZQG+NCd0XT*s1g*2^EK7vi** zScx}P!$tNd9rU5L*^ZFY4S*1VK$N8-fB-sBYnI3hjeD&`Yz=J!hu*e-?LWyZ3m%qz z+&_V>>dbODwI!$WJBpmPSlMuNP-USiM``Gw+O=gQzfWdK>QeF1dXsztsh@H0IsV<& zY@#=VzHajDtUDXd>`mku-aQT5q$aMm#mq6ITcyplr@6LXQ)_X?`;+dWJ*KJd2CEas zZ=WC3L8Gd!?sG6P#yK?W(3MzZ z#S>aB>>O?X0BZ!>+)H)7>xA8=-5zQ@C?fBDd2c-O!%d;4`}b0mKmZEYL)YaYq`IIF zf;P7lO8S+-unr*c_lxm-Cy(s5{w(6m3fkK^^`XAlHv3h%ru)Gq(gFbPs+6PD?sd!O zeeKQFe^ctVk@^tBPK{hd|t52>f+t4=m)4(ZlizANuTHkLJ1!;tzvf5_q2nA<6tgz^t}ba|Xm4=3#5OF7)iy zW4Z2o3!1l|B`8^`Kvu_88N9YadNOlLc1niI*{M7Jvg04NjuYKcQ_$;GioVU; zf8hp-uI08b9&W0dxAran0Jr7!0&cM_;awx1W4Y7ePparnu1788q|$_a%CG%)IY@kE z_CxzDV43b8pYYwP7GIy?R|xl9N(#9menJe}d#RG!zp-}U>Rc@;0ehg*mGsi?d46ls zB3jU)9yZ9k%hh%5N+r(b>H+i~y2P1kuVasD_1iU~}>RLRWkjWJYE6vgXIR z9_m%vb{mZdZlrJNJyTDCrP_q#L2#h9pXt~6zxY~h?5OaI2%OT7> zTflN_{q^@G-!IcKn^cLDB)WT+>+d+xTZ>WZr5fsQ{=sFGMYya|)=As{0Iljj{?&c|0C`h}8rS~Ns}Gu=Za=pV40wU$_cdAk9pe}| zFB{<59ooxzXBM_IA&F5fZmFfEVHHV9=tlnl4O!rMRrL3bjJTGPqTk_dSqUHtug$XF zA8srrWjbGZnCvC@lzWu763|MNNvR~A>#sSI8e%|$dx!YxmR;ZVc%`_j1n@+J_QV)$$nDHxwAXM@6kCMK6#lLRnFa#-JwT$`0*Nqm8LQs_L2QIQV2># zO-bC7t~H3~_tvlom~?8@-Er6@e&RZ+_K*8szTI7awPlM(`c;+O$N4%nFngtuuTo@dWuhSMN)P>4%$rK<=?%f zt|L_8;&#n7YVNg;)SEmgww4(U#*3fobX#0_U zk@(y0L(AA-FXnucf5hkE_M>TL!)!2-8dEBJ-Yao$vf{$4Ptu{S79*TXVF_aZ)keAF zvE93KiP2SDSN_%C6C$BM-xrW%-Jn%&WHY1p)BgbBdjR~jx&7G5gXN+Ta641gb^Gnd z_M_l`w$#2^yl_@CaI{`|ZXLQNSt@1T;dBnMX>vo+(vdZ52%(a)N@-tSgINwi&Tn!d zqF&11AIEL2YqP0epYPs^OSkX3*O}5xaF5tuZIKE`TC-q=qEF7Nr{%9oaNRMnpAfm0 zdB6O(4+YFe5|jcdN__RGsH)6Jbws-mO-}y+9rUn~LagQ=&#&ViK4rWB-GQgAd5z(V z#M}CvSH6c>?d?E79i)8&ePm?gJMy1a9om#z`yO=m;Z8emmbI#inSG}SDLWpY4QWLx zv71Rpn^LNjqw21~AXJ?$M*uqLiLko51u^mO2J)vRPmdQD!)Co1fV>kcs)DL$pg zKm-q!bwzoZiV@;f^{yIM>(D9B%U>RC{0qfAK@0Z=sCY*=7jT@}mA1J2kBDVmfBE?^!F7*=w|z+f%>d@b46U;R!fXzgm2W)59dMsKM#ui4`f%kk<@H| z5w9>uqUx=o36IO1dmEd0OO^OuYppX2l`+f3f=jO_y_Ej|bb{nSrKEL8Qh`4$S>p0y z%H^`sv`t>x8<h8;3srOw~;v%U|wDz3;06lcpd%p+( zZRnvpj5*y;Uy2ET_NDpTLq`x8j-q0M#De{{Wx- zFQQMfC)<0UzSwv_G+-RRn`7CPzmczTFP1m#E=*EbeY<*Kyd4soN9qkAidW~XH!9?C zWJqunAzEVX9|QI2M2q*!`&IIv+~*16C-@rkk68I*i>{KK_cB>Bp+T2vpBT3LB19*gV^>G{TkM zl!8e@O?@@WGdGbnP$!{GwnAxu7IZ2%;@Wm zps;$6s2wwnzw{;#8XqJ@Nz8ByB0JX#JdS}jN!j3Y$%CmtreD67+P9Uy;QVdG*UkowW_OqPn&&66H+cpU zW!Ua4c1ezjAr5ws>KkwZ2tB`^t9VZ)j?@-NbgGw^!#+pvZ*i*mfLHyxKXj;yQ)%Ly zc0dGxw(_Wo8-Mni>!SYv_J@vdQnT;kmrm?|5BWq@{{U{c-6%ZUt?|8r1eR2l#*#9I zP`2DcyON}-41x+ss3T3w&av-pH+{bZt%`@9zn9PDHEc*E;|A%(PU?Wnq<4y{iJLBELOZ? z(j-QGmj`N)<8FeL0uQLtBc4cIR+<#2!pR#)1;yVizR9>ocDeb-iC2i+ZIU>Zy(fw1 zm8%|>@6@V7eLc=WDyaHsw#TAJU28Esx}I4F+xaT1%x;y`{{SEP`~LtCzIS@)7;_`QjYF`sEYb&>Gm>!npIZD<0HCg z6LTNIc_TA=>Hh#o;CI~H`!bXL@pgMn#_Rhs?R!6PV6y0Js+$E+ol{?B?X6$7M}U4L zEme*_Sy@=xN^9NXU2*%2%SHazD#3Jspe{D4(VvGYili)ijLAY4nuAeMrCUhZ8@;Bj zJ=1bqy^h-9s`_&K5dFKHmajK2Biw3x9`xM#OSek#-Ei8!lW^Djc{H z1MEv%3q!0I5E9}WT1{#Ksx$WvgzY{1X{worWH$}m(^B60{{S$c{{XzF+!F!vyT`u` z{B+K&*9@o5&j{{0jt}J5I>?J1xL37U<~xp`+sSN$Bu#n?a}5*Pj)saf((u^A(@+gQ z+I&;j_nYb+PSpOt549K1%1NSj^cuZ;4@E(^BLO5NS9rz3`L6xw_bVGQ)D3|{#sj$V-+s2pQ|F;gtGp9ee%0-U~}-fowx_wLh2J=lCzAFSKtveXqXr9t`rY+j-v-?LIr> zyddkJ^sfrBH}{uTY;z4v1A3nQ>vJij=T|kzLM%YLb{YoTd{^Eoyvrq|mI81yAFt zfr*mGbyh#b@3rTcUvONdrg$5Q*4``brMt%|UX;6LKMe$q;vHHFunrZl{Re6Id0^-eU4izmxKeRj=J#Z`aWt-HF+j z*b7pl#C=34ic~GdtzQG_u2Yle*H;H+uw5S;#9&}9oay2He$?;*QQx>1{l$DO#THDP zgTCC@d<>@h6}8*Pka8n7&5{BDEvZ(95CWwv5%jEtw?j~)N@@H*54gE-mg)Zha&sZb zB88?acGKhb>Gq&2PmSx!Z$2x(B4PMlGBQ|=J9L}5UZ2Mn}10)?XS;D^%^>;&Xvy5 zy%QH8{o75nkJ&EKZ|y&{j^b2@98Gpn3jqFlL#{MQWOP3i$Ln2Ixz$tJ^asmM=#f)Z zt7%Vg1Z}3x0a5|cE4CBcu++F|RTQHs6sfOWNF!9iTh~O46{l*FuH+ulKTe~T(L16( z+644bJ3c>BeO0Bf)kUt-oiI5603n?TQW}b-Guz#`sUT`(RxTo{G?@l9$}gVF*4b{x zkb={#k`#kcN*!0vki{s#bz`2jfwucI-NIQ=0AL;+z=` zx+`oonAkyb%WzQs=^Z^|ymugh&{w8}jHaO+ZY~>2Ys_J}EhbEVtt;MxeKa-bl*6|| zksL0vs-me--)*Qz;iL6vfnX}@c>B(AmcYO>S4-B(pfeI%gb;v7Z*KmY&*iKHgL`^~ zq`~D(F7)54N@;lq!n3f+>d7pT%lOUEmBxJ=r5)SvPfhgBD=oNsZ~Z|rox>QP>N8dq zREeQbKg+9{s(9XkX=SyARQ0ITWck!5Tv=$iw3xCe7N7V_=}(5HHQ*>^lqiyY*iWer z5H8gl89g9`6?CZjhvVO9jU3 zM66eEq43q09OM(@XWql7b(`f}R@*6TV~}-I&V7d2IcmbZ{{RYU5%L)A`;W&0Bp%zv~@BA|w zYn1Gg+CWZ}UG8`zxQd-xRQfrB^^w_8X1h8ixP>RSdg!NuPyhqC?@etj`2m%nIdcg( zG&ZvtBhVh*lI6DI#nvLDNfalgcCOmHhCoKDp7zzyzjIV~k=b9iNlWY+C#p>~S#fUQ zY8I~zl5EV{<x%?Ja#Os?u=IZA7{71*f$LH3>^4foOcy{9kVlH;+w0hlfXoB0#Ej0=R zZPQ&+2=>A$s9rY5yKTdo{T*FYBHWcWJaD+b5W| ze9@FgzC-Owkn$TOq|rdpLcK=%YI|7ZkUhi9Iw;aIW<*%S<#$o?QMJy)Cg6u?)al~#a}s29^ZF;S{^DC6qmqL0+sB$SY<5)Wza zNg&f>>OOi6aC2&aa@;bRDkulCfYki-8uL_>Az-Qn+jEhccu9;_A0N!K{ z+mASVP~B>(>Px_DYEWx;I04kE!Jc2gy4=>v7gQvar+-S&Ynf%QeOV~GrxV2eJQX8V zF7TfV$j-x(WJgenor3yTZ%swEvmHu}oy*7_wS^Joj~=hsYmZEY7`p&>9g;}{P25=5 zR;RH1jo+qWs;sWI>{k@is4b}`iV6e~{B=t97L~b*M!Zxk?jz@>PgJTQ58?3BNSGJ3 zJv2ycMICeJPllpM>M5|si9V{TgQlQN?I^4wVj!basE+8SH>%f2OI|tp59Oc}jY4RU zPu@MhN3z}-T%uCals_*@>Rq&yDlOD?R#Py@?Uy?ue`>UsM`=w6uS$M8xsA$-<&Nl| zd>i9VSIUktagxiB?8QQkc9L8NeZTS0xXA4)EO_$u){ZvuvmC-L?4=MA9&8G%q!pdM zI?vqX2`c*E6wuvNhND~mfav;X{k^$69P0}JXfhg<944lqz2oN>fAu-veZ>i@z$I^qKi7fyFd1z z=O69&>6}%3lieO+_bsVfwxL&T06z_7bJnGUcvslD!8=Ne{_fr>NaI}oFEZnn@BW8# zAO8KVq)`pXQL0vZcHEk2cuatNbUi|P;l7l#>{l4raH#G#8xgCr>Zcr0ttBC5jRn7F z&q9eik=QZWeo_-9&tZeRi|Rlcn;5Ezb8Mf& zQ3t)-T8>#+6E{521!S;}HHBr0-SaG=ePAjLx1q1jy68B(11+2CN@{!;66AcoYw0}; zT^rn|^rzptsR>#=n62#|I(AAy`3)V`Me-UBiCFwwEt%r(?n4`@gB6>meFCbv?;_l@8;AC*M{*#gMF+OOR{d%< zl)cEhQ?;w0;_z`=vwQlY*63D@E&}d%ARoA_e@&yg2lLZWF|i7ssmH!csQ?`mReHT# zGD+%j^%W$m`fb;s^Z;tQX?Jy7I~x%g0CiIz8gYcH>*$dsg)srD7o0l+K3d7-a2U0dyl0ut&nI zEhgnHPd>_P-lbP?E9SuF&~#PmTaHP0V^ys0{{XmZX*3FmvCnlCgW>)flakD4<8*0F+Za9WDMFpVX^EyC z0=~7e+BX#<^A{~zu)A;l4~3LBDWnujklj9!xFVz1Q!MQ2YE?dKE&$rqMW227udVmi zU2)S@tnO>8&_EOm5M579{4|)I& z`f^7!1tQkn#MGx2W06L-L`FMf`ci>O4yUO-HzQSSZA!M0;i7>>Y{x25TFpwoQjc9Y z8AS!s6sDHDQ&j3;O$Sh|kzVnmM(0&xk8!U}5<>UtPs+LoEfk;z=dOYQBg%ZFc7RH> zMChbO-B1j~DQ!|7O)I|pX))CGOOj5(6G9A$2|^RHm9X{SPBoyqh@5t|s#dqT9>EkT zEsxwC%Dx9v+vN*4Bb_V02?pzZxLnqxIQo>)=#H9;>Nu5S6b+0dW)OSHDf|wVMCmDA zw9y0+ra(+N=)2`Dim1^esn8;b+6m~B;#?rfoYYsA8)?Hr>FMy$acBx)wk;J?YB)yk zdbXy0h<@}bH3>}(LEq5nqaY~O&F0kf5RyP~#1NH!1nhMojsSS6 zFkT0mcSP{K$1CHkuOH6vT#jxZnc~t>FC58j@3|UeIW6>CZZ<&`1cEvcNFakvCQE4L zWgIkC43*6~FR~xDcm2#rnAShSzi`%@V5Ka?vUwj3p2nS8mKsm@n5?Fa9nwsMeH}`I z0N;=D?`_*HWP?NbEj}xgJ}NcZkIj8&S+Ce{BI8!v&jiS!Ww@pJguQFNKz+GUWU%UL z3RcllR0unbaSMw(n^|b!T0-a$WT3fbnuYU;_dELyahH;>Upb4##%=sB&u{zkF4(7F zjf>?^uC5aywiEvVD=BLK0Oy9&Ltm{b+MR!edy^y-igQqWRx>@tgoL?)eCzm~=bxQF zX0BzE#2Mx*b%B9HoVPRY9C8BVaTeRFDt29xO;+1#Q9Z%NRHl||+yakq=^5S~kIcos zA_M8C{C&JSDokc>?;h>0CZBG`8#5V>@O}vR55?G zNN%c4yC8ww+NH${C$)WON}5=$b;~i?JeM4oWVJEZMYF!Jv&=(Q&_0Xg56WKimF3gQ9g-Uj>v3xII?Jzf$C2{G~`4xsy8vt=oPr}U&S6|@}nYd--TOR#<=P; ziAH107j!9%dfLLAQwmsG%YV91Qc8WcNj3dNAcLs69#b8=e4}yjQf)Z5r1{nN9pV1~yQeI2rz5pL$ISl#6mZ$Ft|OGQcf>9nbkpl< z8cPLYEyfMS0J!Uk2~t*jeuo>@`;X!+u57?ztGfJFM?03dwz;dp+L`ivZ1H#QQ3mUs zcrSmp`0wSi8ceQ4@Jk?;+$?_j`;Jyc#|^oWM4?J0N{pn?R05XNNzn}5);3L&w3z&S z6sO{>vTXGtaBPRSML;;ahrE!sc^P8y6E4GYJS&p#mr)Wg7hTzx5%}^-+zOJ^b)Lns z+RE4OG)rnoDhfKg%w{ZwiJL`7kHlX#>_e>==ymq``*mY{2gi1sG>fgoTh=4UwucZ96w6hp?X5zovDCYE7>IMmikH@8#I=W1vB5ldWZ*s^g>SrFoOxDg&rZqm zYmAcghvasuOqo)jc>PK{lB1>{F}s^%JF5>(wy^GT=)XE&zQ^6$Ap3)JjNUNfd-Lsh zuf+46XAp+Vlw&5l##v>{v%Q-*%19zIO;i5>slU=kqPSlbj?Nv<{cDhN9J{XO)X}~l zZy(|Fpl1D=KFU0=_dw)yWZ5&bxZA}HI#5p}^6FUadgl8}1S{To(($@3-$7XFJ+i)MnkW4 z`UL&EJPqN$12{eo8-ZGpbj|VxSbvfEDnn`h<9VDL9o1gt3?S4t&@=<*t__~dOL-|L zqSsqkvL=H~RriMal=Axp0qS)ICIu_iDZtawp+=E5;`$=MhT7hlDQXErOtgaH)}nn9 zkWQpqz+WH%Hzc$LF#KP3CBKF8u6)b8ZLf1p!snY^CcIXI8ysn`+u9JY?(K%{aavN7 zu2VzSN2MRy~xoSO5yS^sCL2lLnx!wKF_mZjNp@P{_6+aDM{X0ulaGce5 zL;l>q5-grmea^VO{JzSowh80Z8GMJ3Fu399k4&;Y!#1`aQqocirap&K4(Hn>R0FIB zxH(4I*cxa~i%-VwJ$r#(fqhPY-3#V&I1j?V{ztridQr%}TuOG;At`aN<2b#sXqk3* z+dGuHqTGGDdz$7x(_zfBI(G%8=9i(AjC5a|i!g%fN)kQ?w145RM?qx~>bazsBp_r( z_L8Cynxqp>(daAi(>E4uXe4${MQ~hK>W}uU;dZza?Y-g$9=T!|<>44@y7QG_*dY&6*hK(09l5z@6a@E+1lp??RQ~v;lr2Q*C36|cHSJL18t=ue} zzs-JE<=GK~khpgVdNgM}x+R;2OEoc@SWp`*GZoa2l8(7&xt9%&+(s#>THY}qsVz@s z_rkQHgehx1sZBeV=#-A8zkt^p$69)m2KcRf?%Nr6g86X!czxFV;PX@N$H<;8BO%5k z_`}CntnOvW$av}%aeo=dC%tU9Ar}JiA#b|26s7k4NeTdOQPg=R4$8{JxDPjPn$6=S zmTBqPcT4W4bGl}Ea)}##jU^XI_!<$mIwP*Am!%qOL6}I z?l}9h$8jzWX0kfHtKEvgApSI1&_3T|zwP^eDI+^SibmxB0KX61ugM-MQs-WT7P<$JrlR% zr=2}hj)I9++i-emcil}2DN1zGqjgG-@Rj|wd^XgwRCFR4D&J8(KodL4vK}7t^j{kM ztnv5AJDdma7M>l+qF(P7c)fyDsEn}l4z$U1r9J9Pqz>W-$5lLs8TGcVglW6_t9(t! zhG65_eM#~U{n>UmU6wEp4zFl!)hqU9?SHhkpqlO8Av*O9J{slr{?KhY$QHl<0BqAn zs^4}00J`s;h+6FaBUl?qB#|p9niL16eW+^{)tr9P+y4M`pMr+|PboSC+E4x3UPZxM zk6e60#pgysNpdoBo01hkDos^yQ2zi87ykfc)~y8ZNgSqc1@@J}nWz5O__G4!v_nn1 z{<*VTZq7hL%ZXuAqM|`bR}25;7#;8@4dgW=xS0EOr)MQBr?wRHLw>g)6-^*gtRo0N6w7ZlbKdj@nz3~&z)!z#k)C_v+=rHN zhlI9>SnnE1jSBlV;hK8^*|OOE!dhyOluv3XR7jwx)<=+V5oG3vNm5kHEb+Rln$jq~ zEI#F&vp)Iv4f4M<t9bzS=-o`D$C5v7-$03>$?_q1HanQ zE4a};>su+hl_)IXiT5TYD1f{(+aW?KcwJc;OAKX%tyw%Das>2q(N6jW>>{`~R z?Fg8r)$BU76e&se(4MK-*IG;!oDiBc0J9vLX=G?FB!q|k&h3j`Z8=?q(;oYZONn){ zy}eO3+a(j2I}mnpr1aLZ?$Ngv&WwuGEY2$9WW%Li1MYjq+~3R`b#CPg8*upBE68jxd-15H!p)-W`~($} z;HrvR9ma!wcS}AWWh_D4N8qSU^q^`^sesr0$v<^QD75mPKCqpDRxc2b!k+GtCke1W z*gpiwzM8b&@5Fya*dMc3-#^Q5F+8)6kQ&@FS5cekmRQqt6y zTuX>sN?D}^3Vmy~dy?@>crDA=DMdEU<=e$cE9rmC%+r~;*TpV% zIWrf8+kh!7HHtkpHcq+9d*`ls~79E54-+WeB@{^wu2TSvLh zR>q|P)wgjdWhe2Y!$=pu>NSUc@-+lBbYd% z;ctE;wrgp%GcO*x$1Sw$jTc=?okW%$N|cfZX-!9|=Dgbn9i0qffT>vNoMqZcAgZ@kakgTUV{=W!1i4M{Sc{33nOXz$)zQr`!%ax@zrq!;Qyx~@@jO}d_nUa_O;#b_~Hsx8QegV(3J|nrX z)@t52I_H4g9c%Yd6)`XsE;5$bBfSS|rn~EmVQk|W4^8@lXtmiHC0bnRr1dr`4t+;AC!3Im@}+b z3p_V1%v>!KTw5xuF1+zBGU7g>4Jbyi9P5k6ZExyCC_ATdc^8YVuQd+o{KwnHTds48 zjtl#N_~Fbf$6GkZlXlC85%N3eY<8ya0l7J5FD><{KqU$G=S3S5rM!XDOTo+a_;Szg zjXQYHM)tj&uRQoRd;2O z-Loe^emaK0JfEe(x38|YmNz@uMMZN4$7-W1bfZN4X{mEDCN>oPwOrPKf|Y(%I<1wU z^gs#}mCF1%#GXds7Y;e*Ogb)Qja?H&vmrE*5z+cyiux$yE}-s}p+JMDtuhl@xE3CX ziLB$3u7z?>gM2Q>JaFdzSMZaR@2DI(=asd{J}dJanEl4ro7jTqM73RA-6(y~l!g?0 zQtxU0V@+xExAR^#lf>$6>X*jsqKkYo4PE$s-XCf%lKsCv;T{}Y@6h~D;ZGUoaArY? zUWJR2u}N0QijZ3+Sd|3Nv z0FXN>%Q<14Z)+?AV^iTO3o z^)@qiK^g-xgNXMO+rh_V$f&8OYKbHlDdtVajjbOgGY^1$*PNYx-)_`Ne{;{W&p7?R z?iXC+5V$+c{x`>X2DuTo0xtGu*0ltt@fJD)17l(PmgtI~sVM`vpBLs3|?*UeY0iA-97*=Z z@)Mstx8@HQxGL7xZx4hr8>O3(`PSpzXTfp!R+)dde&u+&J60nhAQS8*YIQ^ql#mL8 zw;5<4WqP|sVP%Y#dUk2A@ue49ljnEa-|VMixM6>7_*TC!M} z!QVe~%B}5zWw_q|0D`)Q2P~u+e63d(AN!1bqCU(rZJr-eyFfwcDRK~Y_3G2AjJ z3rTS$rBGzEPJCvgLKir=?~k+0s>u>oJ&9azv6(^|L(!v2m{gRcBviJQ6&<%a=+C=| zbu=rK+xn8YfIFqxc1xX+_h8=ba;&r1+2~T^y&2^1N|Zj7ntb&9GDh)M8mA8C8W4~s z-6O?2xB=fx7dRszkd`u@OqvWVUdDn7l&Mo39j+}O{6(JUHx&s6qN6p)p|fGHRcVUj?znKTPq*7& zxqZa=b^icu{{VviBJq{eEb?WRJgw&W*5_4m%W@`LMm1uPV}PM)wi2W^5VZFPT`PC} z>QHNhWj)Hgl5ioY73{kOrwO)X1+VBwb;s*Z-*OMyo;NOd(TCZso>E=$yPL`5X;9?H zfoHR@@G1;WlNl;nl*u$GN>qJ19knNq-ej8D9_kL^W~^`F$CpOYx}6X7Up1a^ea*bf z`-bJxuH63s$?|Skp?z-Hc#hUxfr{IbR)wJ`goU4dXeb3PK31S=Q`=f=VT0qA_Kl-O zPjy>=mVCOo)J;w+@OzN*`y75j8vQ?>l1fQN+E^V%q%v9xCfpJ9tS{xMz=a-&L=NNf_~<0K(Fj}l=`L+jn);!l zIJlAI zR=zR{9V@QK^3kGbq8C9Y)LCC$1U)q1j$>Y;i(JYfDg(~CADd&Nfs-_ zj5g~1uJIH1b{mNHu8>vHO{;F?{KaZD3~1^eigu3`9afnQqyCee%g~f%;@6L^(KH(N zg>TrS@fu8#?9_BiVf6?908mZoEEK1|t$lQo5s7M(5aW^GOU)>@<7TNN;xyY`trLzF z3!@wZ%@#c7z`iTwl9??`L2BJK(w(X(1y$2ojK(q5^%nfU4W<)V!vR*+A&_-aqkfM~zNa>CX z0EW6(BEiF!gX*n%_Y0Qr&Rcbcy8i%OfBJv1$M`Ci%km*STU}dQW{UXjwz)_yiPSxp zf08Cj+KpU@^&ZjM#9cVIhv50t1*|C z1N5s@S1c_x3!vFvwq{YvbU3bv(H>8)28GqCyrsNFh`N4}s8vR=KqcN5}K!&-7$E(?uIwt9ST!zRCOUc(@nY6G*uTwzzc(Fd=ef0 z3vgU=DO+-i4L@+%suU|gMSeO-VFra*V(v_XcBAH)@*~h1{{Rg~kZ82=Op{A0_|s7w z+Nq*IfFVTojqBG*+!KI>8*&azi(D1KNg_jo(DWM2atyF!dsf$oVGQ!`>=kyjM_tnQ z8J8Dd+&G%o=5@~FX>-?SzIUZ=Y1l5*@O6q7JBUGLhSF4|0;PaA+gdqA8%4|@e)kt1 zsz<%q;9jFR#HJGJRw$sMTIw*lqIknoRYQ4~#+cKDtDS+5YEJ7d0YukHlr~Y(S>vze zws%^E&%B2myyG*RhjWa!6oWuWAb*at_ZH7#_%4HEkcxKc)gq=r_0$F$6ID1w5T(LD znwkprG1|lT)8vg(tSCVS1nkW_u15?x2MIsB&!={irBVH*z$)#zd zPJ)C_>KY_PZn9DVPR6}Pj1*(4H*dU3jXEk_kfp~cY&@O26{{WpB{y1jX@X{`~QWV&b(FL}TP0#)s&_QM!N~%exbJr+yE=kLI66o#I zB)PdIZYY#TsSO|>K%SbkYXDW-6JgHLLD5R)9c%I#8H zQ|U?lO%)FD{4|@`TQdkuvE_~gFRyE+ABg$Q;*7f*iY^acEuZplf|V($0k5Eg)EeuS zM=Pyv{tH~@iJ-4_@q^2JX_D~ABjr4EmBL&*#y2U(R+Xfu+-xZJI6g`n>!#bt*;`Pn zVtFH8p)nd)c?OzDWeRxoM$(?+O-xLs&+HdJ*dpp6x-H5*shUur*s03CE|99%f~_d8dW!}9t2tjxP2t70FTU2sgg$^F!~3yNRq8_^=Y0M&f= zvP$Zeachl=)-cwm3|n~=NKJkkQyE0v#HNW}T=*FLMNFLFZKg$NcJ4Iv)6!gs!ijHb zB+sB#b1Tg68RgATErCtj8&wlZii-N{FL!kLT|l}Gy^L0o)!(Ua-D$#LcPUAB;Np61 zP=7rjG(_KuVX@0~C3`4(?RuSlmZu+ZtSJ8gO#;8=s4~x8O4wOgHZedoQ~v-J@f^Ft z*ty(UEyYC?N`)#q`s+D=kfGWZ{{R}A(Y1jd$&XoA-vMuo)R)x?q;)DeKvDqycMX%mz2trVrhbRJx_4Lvl_e$@Avx?d{ z2gO0z_J1no>(Qe~KetIEW3lwEq{VMVPmsPcXgeqw!sMqr5mhLifa*@BGet(gT&$lA z@O8s0l*QiS$FpaQLY;MDm8SIHP;OTn(9i+#)ACQZME#AKaj8!0_1Y#UZ5C@Z{?*1d8(5%;*<%y zdz>6vn{53lsHTobP$j_AO;IZf(u<5oA2ekvN}bz!0tp(86>67y+OmOMeRi9-BMDr8uS{9=aR1Bgft{?s_I8$vzBfkKpu8mLpQx%O){HKX)axQtA5Hl=b)&+oK_`^Wpe%O&Ag^IT1l9r%ws8e0pEA(a%o!-*+c zRGo@clR^mi9c}^9RkgtC7f1X7`>A>erEQb=~(DW0oCoIO5|f zi&Q7csqbiZl1Wk&cNAHkH*I|#2%%P%ISC?tL0@}52mR3eR{M?O%{j(75B(*}R;G}0 z{usQL*u27DU{ai5SGH3GpQYA`9)^SvJpG;FGucW=)vJGt&oWQ|4MFn08tojUZ!Y{8 z@&5qKoHfi7pSb=$RT;FESn>wIk9oB|5<*^uF{(Jyg-*cGf$2W2Yn z5gov(8n2KqDSppBaQ(A#)cGypM<8DLD+|S(F1dZPv+ZTJXq}{yawIDJi;QFVt@m2v3v)Jm!Vwy58-|jS%0U#C*zNxS;)z8& zj?0g7)?5!xQMN^m%<-Q`&k?In+wJxXkjPpcN?bzH;v4{>X&|J6DhMN}I@Soas>~fU zJy(t|+oFc&O7zfH>&DLke3A1@3FTb3#`_c=K)>+Zi&8S4EKJl!$mPPj8YXN8p6zMo znWDYWrKJ5Qwxm$hWV3Nv8a!yN@m4Om(Wa;E)qQMzu)f0HDEP_2c~6qRbgU-RpJr^f zsmirZoPi`hUv$jId;Q)sD%J_?6?B0XX|H*yXIoI+V6W z$>0?tJ5CDsC>q!OBV4hKd!+SVx?~Urv_#cu{D$ScFTsBj^UU@2HxXEcrNeHH0#dnG z2O218BAb;Jn*4O;KF(X~puwoBR(kF`3qt;ei{K|Uw#MXq%I)P3Im~wJfWf;a`IJg< zESvoGyYIDXZinjkoC$3}bVWz5yMx$m+AY|)b)=?ZZXM#eC(Hb$m-4qKWqgfyp13!g z_1$+kv7)@|NVXPSPwg6trqq=+`(!qRwxkr0Q`{(ZsE*1rZW!UE6Aj2q>lcGUx|NCj z#W2q-c#9YDTke*dk11ie!$vm{d3_0%q;fU{^3bFD{ZK*z#+8*gXrOul>r5c@5wPksdHHVCC#WLb;cMt}Zh(jeMrSjJY<) z%8HiCoHq3?O}a=41xZou1b`eTVRZ*K$KFI)aYi_G2mVw`t|@tH{qWVz@eA(fk3DgA3pNi z#!qxv3GxrNp9R;jvA!En=|52mp?`4BU*>Kb@k=(ic*B9a$Xvq2B(sd-j24pkVM+;k z=92Qzl`Sc%f=Y!>m&K#D7cFe2jVmq4rlDR_h5#*vxvkqZNZE@<8+=bj0eV|1TwiZDI4QRcr;^t?W z-_R;vZI{|M53%k2d^Do@i~B--s&j9-hdUe^C_aJjav^2=+7ebPGMlFmJhb=XCpZUrql2cz?W?5dE!qTG{s7`)tLv z@+(j6q}sUF+VcTDpeMZ#USC1)N*7;lp~oxqrFd>F#-_WFTwHZNJ(imz zkz_6O!1rO6eZ4Ia(``4wyqW7MV1T+jz`M*yC_30ayDWa0JBltAw?>wOAY@3 ziQm^;Pd%J<)S7;kr?<28z^_%_4Vz(@&47@A51qnKKlt2X% zc`&&*JPO6c7H87dN#q^k79jxlsS9q|H@a2kLR5{?ShmNIpsz(!lMwt^`(^U2vm2U=E>+BJL*t%2j)#nQQy%Kyw>3rGy1W*PK1^J*uX&@$ov;Zc)fNVABhK)2_s%bSxq!5&L^gf?G8(&1r z2CMJ;{?mUKV{zYze81(L+A>?dc^Qy7XvXIxsYm{s!<=#*08ye!?hZ(;2H_&T1w7^M z=1k9W*hg>Xy3Y@{5kO=ieE$G{+`Htex$o{Q*N`erA}st_MeCAv84B{kklijw){PYlWrx7v%>5aAY_t zxDHUuY<7#BbP|#+&(CdhoTwntxTtkzhO`@MtCM6z7B7E*R`{5A$O8Rn*BT9`6}rW1 zZ87b(hFFPjj*JXVs!VxJ_SYsT)Ad&xaY>x>&(0^z^?#N61fC;MsVn9LiBE%&Z% zyV>z5DOn%ZjVe%^IS3U5lNvhubnDYyBil#(Q}u&h{V){w5^X#Php@@hgJ7e{{U-G4LOMTZ_B*PaAg8lF=o;u+1~@|Rj(U{ z)Bp)T{_}*^*UuOt*UlBq|^?Cb=y)xO%!dDxJ;Ps`T$A%PMMRc zb^$<}l=6ki|#rE-LK_Qe(V(zw)5z4|N)x zkW{bM*TEReXT=@@X~Xp+5U_Og{{Y}?MxW!aA!g7204d)!*Ks5KW8}UHzwVHdxqozo zMO&&{a5&=^Z4HuWl5Q6@nh&3F*M0W%R~!$-{{SVJP6E&8&j6YPzK$_P?8D%01b715bys07q{j8Rx6N?{!_kx$Szqnq6bs9nd8UI`-WuV2Sn zebrsDv^(1Bl6oYAPr&OUA3_g;(KLz``HOwuxb-||_b&4X8fFTh#g&z+>kTEcggA@r z2=`?z6#x)iePEI52=v#cxa{?`X|G@Cu+J19kMUgVOh8a5HG3c$C}v$%)%#}h(crH> zJX+)@q%J%bGPpLor)eN6J@Bf~d#U`PQWB&1Yd@T|^{t#f3Tq@iyHNeU>aW#??`MIU zapm6}n3Tx3J~hpCxS(7U9Jw+_6lt*3y!J8tjD5h~uV}qFszoo%*j`hue$t=!|Em`+piNrefb@ z)&Bsve-HSo_|b@fw2F=Wb=F$_7HW4;uVq95BpPK*oI@)@rz zzT12Sl-?$bzDbMPm5S%R^!F8TvG-dYE+^C6N&<((5w9-t_DDhU5n7HYF$U*P!ut z4VnhLcL4pq`mT3z8Doo2Z9fnARP07dlTp^1w`D4z83Qehg=`qyZ-<}{npPk z57BHhmj0v-ly;(eaMvs2y#D~g{8i34ql$MN<(mbv{c0*nLeiUrL_VY~D%~ndiAsU! zMKr99qpfb7!`P#R-lJ-r;jou!O7Fg#^$Il%`4xjJFXKt_qBHMZadm$B8bGR)(ghw! zr~AA9TF~L=A=D}*^d-S_me#)Xg#p%uKc2O&I#oc8v`+X6r6?(?=XFgUsr77lg|=%$ zT`?y_R8jAhJPZE-$uGb;mmfg5s~^tb25;O+m@=Z=i!NPba9k~u8d?ZaN?vRwR3fP) zYHlX{K>ermxit9PNO0;B*9-GEh#WED702(5E5_^3J$ZQ^Bs&L@+;3=GEDV2>Y=mPs zOQguokd|5wG8Fuf+UVF00YhC+n;kT=2JWLzMPc1FHDd^PBDq3qPTS4=i{(B3)r)ek5Mgj6 zOvkW$=j~kml-c(e+@j`p6tduLDJ8De6abp*mh&inzSq?PfG&xHo9V6uK|`<#F2yxWyWazlWn<{eFYR4NX+7CKpN@*Tsh&AH-NIow$nqSA^aVp$MqH5G-F(vcpe8(&nY1f8j`L0Vz>W_k5~n&&cdbeKGR;?yl%L1l5hmiE%zoxhl=fz zTxP*+aj;5TYTnwty}EZOZ>qM3NNVL^_QwdLMM~!fJ^9(me5#2Uz9O^7d~Vtc7VK8u zeT074eLb#wbm}_OQ^NyBiZ<6c)T%}T)}rI8?@`>7^VEP5p9EwIP-&#uTj(ZgjC)A} zx{*X)p;~|vH5Xt?+_iDw(Ek9#MwS~?NXT_Yp)fJnity3n-z3co`PQ$ z3gRx|E{6XA-qf{T9Yh~Z1mjwq7Cer9k#4sg;+XPMQ2Ty`CvWi6t#g{0bV1stP2K~o zKB*yI`!5=YI-U35P0Y<|tgU4QwC<4ov-*Ld)qCSpfl5&16GSNeKthbdt**&p(cf2>pVohWP7G6`X*MRCiM z4W>>Pm87cCHAO5ws@5Z?V6^-Ndg?#G^ixj0X7x#PtrD?&n(_O}g5#`rTPLUVJc53b z4x;QMPRY_k#^KkxZ_bRSTswUphvT5}LsTPuLIwo);7V#lcMC4Epr+hOAc75jG%5~> zQZ)AJx=Y2}k!9w*QFs<+TXOW%MHcx>?Imdmpxo2y)=ME|Vw2QwMXJZ-MA3S0>Zw;Q z@m1T7Z?CZHDqXqN6B1S z(FSW&9NMQk7Tv*spnGK`Z}ZfM-$h#RLD!;7M28x5wDBMVLt6TNI(Kq$(TY?h6}Fe# z_Zy`LQKJN?t~4n*X;OVOr|6t%PtH5!OTGnoM7VBu#Bzz54E~1@PT>Ap(s7vAT9ewQBMReN39nGQpcCqQv4Vnb?EZZ@0JMc@Wpa(%l%0!k$tV8fC5p-!a^$3>IQm*m5?9&s=$6@0 za?xI^j^6R#_3t}HV?UBcnIHEItK6YlYur5!kua(j(rtE$j{Hw$z*{|p8iFb{R`$@X zG*=sjs@HI*HMtkq#PavO`*Ny?Q&vS=sGtO&O>KDJA~Czukoi{w_cPleC2p++AKVV= z)4yzKwKF0cq3CaYNqr`%`1Km-akv^0(Qt^bTtREa96KdA65WX@hoicton=6w(Xk2# zZAxRisCQ5Yu!1_PLiOVw$oRh`my=0z{<2TDE$I4vw_4df-!r!QqH|@AdV%FDF$>{o z4Iiu#LaDsg6ADLo9-yIL1J_oMdgwN?nOQ`Oc6Hg3J@=%UE;t9)cUHoH9al(6>a6kc zwl-}7fF|NB*|b3P({GJI~*0Suft9;>Km-~ z>JfLMJhqIKeQg6mq3QC|E(VoPM{v;Xr>bu}f|7RB7lTD_8>*9TFSBa5y*cs+YENZ; zG}JrU#7wH2XA`HGF{+|u*N3kc2jn=iq^zFW{yOEkTcacDy%U1Qw%QQTn~~%@4ntCz zJ+82OkVnKC>amiRnybm85-_y%E24Y}Kp{Y!x573| zzRfZ3+ayg1Qjqs(1Z`hIrc9gkDB~`L!NGD9GsdN2ce!?%AFYKDlkpnQU&XcBE{lt~ z^_fzdi&;<_ta&6WMzV+_{yJ#%QlsdBUtKigMED3mDv|hasL5niXrc`O&?!xBi&3Pd z0E}&*hKn>1D5`+i(!1!wHF3Bb$_%HAt#WOwJhz$c9>@m0HXgdSwTZ73Dw6fiVbKue zk0QAzk==Ev*M}p)a=-GesH73|Jx-;10jj@oJyaHyDM~AFl_;8(9myw8vWVPKlqd@6 zVbw@lxZvAuDI|hcd-UF*)6+_CRD&AtFWJw_rg03YaL!k94MVyps>mK)5U@>DfKRBd z+Q;%NVQ}0O{8hngww{XW{og*`E%O!^{{YDk#MYmGEV0E~_n5KKQ#LS9c@$4l0NdxN zJaZ}-{{T(pS>&wDW7KG0BWjH*43}cbVN5>Cl!sVaN>+uSkU<1g5JtK)fTFDvwZf^# zZiESK*Jx2+hSkt(YN5?JBSkg6$)-0QuaD6YqX}*YS0I2~DjU;Qru+y*VPUN}tA<3} zlh?AZvItt#q3Tch>WW6xu8!RiLO0SHHBJOgO6~OPrm9kwzURwFxdot6O=;_-)di(h z@j5!G6i*yK1-#}{Q63W|F+m>eq6kVwcRIDjU?jSD{{XdSxhE^P$49BFycMXnTkITn z1l#Or{oUnb{&M1=4HPz@*FbT%$FpQwp5<{zw)ZhNsXvv2pg_WD`-Gz^Qj3l`Q`~x! z`Rh3(ubK67+Cc<%?Ap+tP_60|B|Cg{pqQD}UfwRY?0yrn=DFA#1ccXeboIr{Slv@L zwh~V7f~?oe-Sd#=mYOCtrK7h008KTVmf`a|PK#R`BC=+>xdlG!Xv3kZBoLQp(uG?Z zACB5NUf6{PROOwlmrmaG3`(0e^DwRDsrMW|N;f@qO(ea_+uGU&iUn#oUxXxG-eUQF zXtf28sR&S|eR>|c%;d6xf`M%C_+RdDQ#U7Nk610Lu;Gyc)P(je*1_9qdTNo51k{zN znWPFeQah!Z?PvA{$84>bcM!?XBUyG7|ye|Zf+_g3N6M>Kmh()nG`xI z>$}?Mp$A+mT8T9z><;>qK}Ay5mlb6AGlVAQvQc@uGN{&Q>IyED&@PlaS5R&zu7O_K zS_b)wm3iNsEgXGoI1UD+H!UbF$$6?#p-$btdYflrdX-g|&b5PMS2rchlXH~pxsIvl zpHVJ2qDds0>p&tD(PEzR?Mha+TY`vn!Sp0nhiwR9P$rN7lo~|4%(m^8)`{*RD_U#Q zrlrd`Yg2ZXrS1f$wbo=D64Pe=mDqdEB^o&p_Y?md-RELRL)Jal~ z=T;HMtDh+)9zXz%w$r=9j`LJ7>!wODvqq9vpE1YAG513!OWBl_(V@rplB+N_w47Yk&!+u(+m0*5z4p zACP&x=YTvnYvb-ReGT4XM-13)2{rd*Qi^%dIuD7}EZBxzzjOK~Ev}?zm4!9Dcl)y0 zxw>p@L&);REt5@jHwz*aRkl*rswcf}f5KMuq5QR&%5hgS&kt_O>MPO(AE;Ij$Zxj) z01o*jXnQKcpO1{?(%UXV$e<;)w;_GK$S$P@i$?zd9Lep{9E&y_gaSL;b+QPkB`OA?NhA4_y{Z*+f2 zYX1O9Av)xIr-8PnSV!Cz#|@ryXxR>n>3f~I*MNLx;al&JT+7KeyjO{=F_8QDQr!~~ zC`h7NYAaCv$0a)HP)lvJl__L^0XpXR?k#;hv|!s#>sdb_wrCYKUp+r@AG3FjA8>v- zv-xk~9Q#%c$=sK0Zue!q!5-EtnKdB=_WuBS#CoPQg_1##gm(k8iB%{yynmftVy-@( z@AULlc}xr!k$SN8@Lx525$3m_zi4h*zwpLM0%Qz&8oM*U8*;Q6IPKOG>ma=kxHyzH z=t{y~j!SAJ_avwqbxCY2vLH(pE8-RvvQKJD4^N7{Px#^B$K6@0hJE0kS8MS*%c+Ta zZfD{4Q7iVA42DH2lMmZqj~B?hpRouL5#U7qBLLljO9c7@wZmf2{@bS#g_MR%9i~K~zYfsTJG` z{B^Lmkutj*@6znjs7oujJ0938k*lIxCEJJ}X%EGxZhN zzYTn4vv9^G322C9t}lD8OnpUf%aY;uU6AV4x|F37N%-ov>gK*sv`|JE0q(dB_q*oY zub+8~1o69%ns$B_Xj{v_hEl%CcXv;)%PLX<1S505tu@eiP1H6JK6yHYgXR`9%-eM@NoYOBKz?~3|T{{XhDk9@M_ zeq7`vGo7x*;5H2z`CR3R-Xpa(%XPcD*X|?4w>K;h#E<=H4z@yjYuu8hcPG!j?%Y_u zk+gr9k<-KD_WuB$;q9YtSSs)Qbo+lb_kG0f;`hjI=J4jSoJokM`vYkKmcL}q$yyL; zMAKyLxYiF5f-R=*s`ojaX$GB_B)nF+dArDdartK}nQe?l@uuN%ZO$Qt`jELWVMT2W zvLh*gYL~!L)D%es>i3daLknBCLb3c!*Aq)4pQ{PcbqAlhj}h_ThjQ*h%BG^#XvD3O z?h~C+2?=H#a_Op+MJw6{v-o^-?=FKpR?jJT+gox<{L5w8xx?<;=bhT;8B!+Y{P~eJ z`x$Pf!rhY`VZh4(B=>eAG-2J_(72=VI`uWo_V`?Pfdlhif3%Klo$WhU?fG~wrti4V znOsx$9PrB!@B_n{5beHOX4X=v*Vauu#pCx_&0Jck@{Z&~C2_W+R zHHe=pc5Wx>+N-AIuVb-?(u=Qk@P&=zoP*6AiEhSm+kMNCc{7pjibN=3Zq2%0F4NsE zxa+SxRHdzIQk-?y6}IY8peJ2Y^5Q#4jdTHGgIy!MO8eRSUVX9fueYZT%H%#rixVXB zLoA4wvs|qMy_+O;r?^`nDbNT)59@M(ty`K30m8GqiT9IjZ|Pe5TzG|>oz-+-ggxv1 z*87&pK_W|vOkUs#Ng|KNOk-V(TvT~5_C*z%*VwPDSRUFE)B(Zk8sUV6I8X6)p6e~qI;&!2}u2KI0 zPhunf=Oy6d-#K~c7s;3X)t*M2tDii3@ru!OTCIFbV3m_)*5riyD!XK_2=NNdEABNK zGWPTiZ`5n2dv}TSt+!KBsd-i2`F4)Wpei|zGB({Jn)ARlY@ zCbh2A-n(+Ib#J{ZKTEro%Z=KXKE4a@*stA~kX6&Jyyp}RT?8K$@&5q-0ClG+J??mY z!fgFfTvkVwT*r6mb%Zl$k5v8=7en~#yZd2dNSyxb1IXnjLOW4>heslbBh#+Bvtl(^ z=M+-oC);7d2Y0rWD1v&>R+^9*Mv{93B4u0x{YbvoTo|t#e&1dzaXiafEjRmqCvLfA zu^LZmMdte0yUizLlA`10q>7%WUQFcq4_f)|?N!?`QI=Z)-B6$Vj=7P1WA@w5F-X@` z+b*2bd$8kBK~h4Vj%Ev_*3uQ!nDLYiJt}=Qy7u17bi0pF>!P6K^BMv6(NEv@?eMBN zW9@ap7a38~)#sRErf!$nPhBMfdx+hnFlkEluDb8HwtkI(@m%kb zT-I{FdZK^s{uyPzU>T4N}|DBiOpjKGU=yMH`K@ifqeQWG6!;i)S?21RDG0gJpk#X z><9vkzDH#MruvHZRZNORp68~c5TqimueAr)Kp>fU8oRCcUr+gG{{Z`vwC%^}M;X`d zU#-7{0-i1KNc8^zq-;O`0E}l|KFg2(Q@(4iV}J35@C*L{bG(mt?yGxBl~Wr8#oPQp z<|i-z0H)WV{l5PI`e6B$&N)B-0ECsmM?c`}VXC5Ed*AkxL-xEN@^!#TUB{FZs-A!& z8-UmU086eJ?o6cKuy_i8KjybwakPk^74uE^_>KEtx%V{H9fnLRL%k_lw(4vA2D;x2 z{{Zj+vDv@;$L6@^6cRVqtQAPaAEug-KHiE-#|iy|ThgTJ;~&vv@~!hNvUJwgwNj-E zPX7Ro+L3lVsd#g$jdey<`u9Ur0$s)&E#6`Foynoqo;jm(g_mt%DrKD6EtMVUKRs?B zB8!@nN3W^>0Q+J&AqbD<&keq=)iE<3%I)pfVkX61ozg!82{qT7z3H;_=47M4Rn>TP z$#X+{QlEWE{sb?qXs>&6dq%~6hxqG{F~o(?7`_X??q%aiSdZKjAI}jPEZTWr1+zzX(NH`e~^=Ra?ah@-5bO zJ9)KaHm5y{m4Z}x*7tY%UxxBzAnou{28!V6@b08S-OVlW6#Q4~(c?yG8&m=>+`)e8b}Oi0oF=msJ(uS}_x9n%@aNqNpOVvM zn`bAtX3MP&kOF%yP*V}^(g3gZqNtyl*QB_OR53gQQyR$J)=6Dspvzv5n zaPFu--RpvX`6GVZ{!C#pZOgi37Aq3+OiB|!@t1VR98J}PAk{mpvQT%W4*P3S!!ErG zzzO|9F`Z~JwcZ7OWsn|CLWrPP1takl*P>%^Az^NqQjC`Bg&v?#>9(lRyH)lz{{U&P zkR`c3QTVxWmXrw}+p}CzhaRa}T7R`qLugk(pJ}BUdaL;H4|nl9p=`ivI{dqTHPJY2 z@nrieqlfVS0E#Nf{{VF-jIwZlCis`h+e}pp2QgViiz9tKp|oCXB~G+WH!5~Jr+4Jljl> z)u~Y)Oomh|@LB=T0CWI1K*+!GOCUL<9Y5uMs9p;5&8x}(01$X*mo1MbSjy~BUz&wc zN=&BP49aO^dK9{aB|~0T`J>1 zG1{;|BYn=YQ7acPB^YUKXmPiY9BI%}<7aTBC{T_5I-MNlj}%FKV6IAX79P9{kg`l= zNG(K*Ax_%QqM}Itqhud|2A`I3mGSog zDM1yeuB+rar(#qmfjJJ_;@{nOH}Qj!i!6AX_c69NEBj)?5gH#MrrhKoc^%23M3h8J zPJ(`=mi<&~wy~Yl+5$gQRA+f)r((GiX8Sc}ypQ)V^4o!K7P z)A6FK$YzHB0H?I?-Bb=l;EUhe&nLmJxs}q^=b2e#M)@Y`fLdoaH~xbNxFX?fX7eD? zOP?uisZXQ>Ppo$+4&s~7PVuFT6pbQCrMBB6jbHscFS%I!onYoW7Z-DUR@f0a8yT|Q z;apML66f~_ZN5=d>}a(Rk*_=7TlzB={8wVNpVV&e5R7IVQ2zin!M<>X{?41WDV|y+ z{{Yr8Tu5!!;6FxMdODOU9z%)>K?G8S>V6wM>r1A+y_HU949m!i9y+cZ#me(K^472VqvY6w!R^pC4ulRu1ulG?U*Xj#++ z%s9s*pNw3(+D${hOTVi5X8W3W8Oxp@P2=3tp80nV%Xvea9=J@KfY>q>9I|a)oSx6rrTI$Wf zl*8O=j#0fdn$;Ik43&LV2W`F@cspuY^9A-5_TRAOK23e8a+usfbq(`R9kFu9U_aiK z8+_=gD~>=(G%IMOXb0joj+Q1zeAhdnRrkh06Qgfl-zCW(6~55kUHPxd_a;$|&f^|E zwOk%gCG&n+5@S(>!1+V%#BI=p&y-dTdk?lcmHz-K+HxLKX=NAygV9Cf7@NC@=;oH5 z-UH|O{LZzdYQEVxJI7zPC)rbgrDpT;D<%VU{kuat=RB3h9n5lRN!!_A0qz7TWczX0 zKuE5`eR2z{tL)UYj8Ua@NSg(ap*x&94!@H5;QNOE0Nn}6e3Z^X#y$&UZt(6AB@RV% zn=Z|z+@^95aunnkOG$1bR47p6-OKp4HMGEcb!`COFPY!Nr~LJ%qqtCVJeKBg8r=tn z{Ey|^z^|BlE=3>W8};Gp8i39l_C3t-p2`7yE0_8C^FEYASpp0Y?S(I zSuMm6iUms8oJZU2PsIJ@Tx@HAIDdeyFyQB9pCq~EU>ICEV0g%}TVzx8+@&@?vXXZc zr~A6CxXqF%G*62sIO-}DsmgG>mEyq*F!M)}Vd6Y#YeH3zaAlp;Tdl1nKT~b0I~}Td zU}0YUYr`07Pcn1yL+NKOzJ>%>+v{5L+c`LXxy7rD!!uhQtw|f>XK0MneEdWC3=F559r1aX%fsbogj3 zPDdukd=c9v!X{y9Mir4O*|*XqC3r^wDG*=o-DKFS>jTUD` zB`8t;ap<5=$4zxKDaQ)=f@{iQ$j>PbCY1d@Jx7pHN@Mp5rJDvrKErT{akt1+c@b8X zwzQgVG?TUMp-lim_{&29NA}MY&@DcPR`ybdwzX835IQGNrWxByb83{wp_q`k z?j=YxIwEM2<^q-^Hr2s^>nFBI{{V`gj-@lqpaNQGdLl68syf0Q=nV)XN)=DNfTefP z)2;jH_5M$Wjm^t%xA) z(^?KK$tK252sL5q7B8G|i)_WQYlob4J;73^C~?J`F|;p|xZSYicgMMXvLw}U+#;l< zBh%tF(K#O< zF72Bud_h+G$7_BK$(fvNGd|1{ghjeUll`+KbsK6e*i(FFN`!JS}5bSai;P`?zKRq(%?rl@nWW#f? z^eQ`9J&UhV(^acr-l;VUY2)p>D1Y0fvGwYvxvp-Z5`H4tSy2@q%6=nEycH%ZfZ(=m z)dTR;@su9GjV9ppZ7rm#jj#11KSVZ}*&d?3bwzz~=3AT{p=4?^uBi;VtC}L_xAA*OFkTvUOqtOPS5%5nB%QS0!VMIX`s$Nb zp-K%K^az5~sF6dVxyZw6iz#&-H`0W}c1ooZ&NY-?u-o;wBgf_{1=iB)9)~3)f|a26 z4uoudbO{nRgd`C|QPq93{jWUh!hDDlfc$l! z;@rMFkRy{v1xK32KGo&T6>9mldQ6B*RCfVsNZCmugc0a<&>r6f0stH;5_6M1#Tu{k z>!{8IDXnD?3UwrH{B+4B6MCZRSyq-4prXoHK9r`A_Ek96t#uh1@B&BxRG&>K3aJFq zOt>!eTKC7jz8)h@_v=gIZr)oB*O=qMLHX#OME}<*wkSb z0w|LB6mdjt)N4sTR56~MS^%&%m>DLQ*10>0?1{s+NOi=#;O{EmdYxy+=Ngr+nnp38 zH9xKPl(5lS_V{XaQ0-MyU9E396~7;bjl();Z+e7PDpk^FYD%4X)ocdmbE;>_T}s=52YIY>Om&Gy_*+^l;VDMW-PuoO^sk?^&>< z(-JPsGYUNDn z^KTQLn=!>Y^o0_&DAd%N^*@fZ0lk(ZbxlfIwp!Nm(&-(nBXtu>e~z4ur7>|h0*L#I ze&;Q_RjobFDE5+{U*V||NY;fG(%sTKRVt`kxX#8(`ya?>BXoZ=Wct(7uBsm5^YB`j z*w|U|P&~O0+@&sDrI#IfMM@<6chtW`0IKAixazk`KpSnRcajq{LLgSP9W+Wi#TMJ0 zleIS^K{~hYDd;~v1PDOXo~J;k8iH|P0zZk-CmIu%7{^y~c{61qkXzgX{MvFh)Tq}t z0ivDJ{Hr66Z)^4tm)cWMY3WTA(gx^}YiRcxl@2{6*-wO|l_|w+JxL~-g6TY^zE+o)}qqX81-Dr{{S-MapmZ$RzquQ-F1}~GQ+Jj zq7bptmDO#;U^;1=Hi4>c*NdAa+i_Ux3$t}3fJrPqb7Z(zQBmzPKG%8UY0MfRm z=9geynuBiR-v>B_bM8*JH#)mCJ){Z;d7PIl4}D` z8|2wc7BUPH=F3J~KH^GNm9*%o8|BuNoEd1a59OQWG zcRob%EUO^4AUPs;6iHbUk~eVv!RHTc2H-ZcPLv{=*75Fe$>_;+a*1_^bYpNe3+HP6 zX1ZfnTa||*wLfINS>9ZGwW3=}W%%zP_Nfjbzz8Gfu8iqpEU#ru*1D?b-TYAdhTc42 zv~!MA#r%VK@ly{8a~`@}WX*ohY+5~@KDngxVIuN7%bVm(@5_^Jo)kTh0(X$W; zB}|qFmQ`GTwC~;*l78O#5_sn|W?v?Fsgy}m*Umop|OvX@obp7Khbk5Gl5 zsWe$V)Lg@aTjb-fM|M;2`ME7zzRNimh`bQ{ym;qg%Aumw%oq*rmAi;$FsT~-V31Ko zp)OGpA<1b~ON_V$4N{@kTn0OwmmRsZJ3RNSwJ`i(L`b?^$$kMb8n{gQO}uHpUjJo&Zpei0|a&KJ8P7FC#97?;Wyt80M^ zw^-!@S%k@8f`=r8?@oyPMyq&75;3^XFzMo_a*j*9dJ!E@f~EfeZf+Qf;)e)ce7)w_ zDgOZ41;W~xyitofkmRUr$OqU!LFn{_5wP3}4^2emnI~RJwYoK_cqX6**0w=cb2;p8nPVMRpLD!QV{ODH~@ssg&lLeJJU zUj^Uy3v;Bdbo+#IPA?tycexNbFfEspb1p;`Fy zMl(uOF2eXp;Ln1;bX%SCjqsl!?p_;tMcUf&-YLkxw_Y(z@%>IYYIVZQm6AyXkn&WD z1KC4UljK>u++;<>Zfe!yaTd4l)5`C+W%ilkZ-sb|g`PffAKRY`T)U*)oA@`<$b1r)!L!>NHahB}ITUY-8zPWsC@@FaY2bcapI3LZ)1PGJ=t^HPxe7}OU4U+keNV`Zyi^MC@PJ^%s5IYxR*$8875 zwYC`MzyKOS<-XdTO6f6~A?L9_vXxQ{6#YZ%XQ_!pt{Pm3<*1iotQF zG}bMP+U@B`OW^@h6a^~b8`n%<%~M2H(NrldfJ=@LQ-#yEHwlsF&!-`f}?H!D=p6=03;-WRP#rv&k zYY1Us)8MNwIE0fwaB$Ug&hh^MY4_b$-22AX_Zs9-l7HpjYb%`{)h3_J+xFd$%ed;^ z-`+fP$Cx_5FZoq-aC?E!kHuS$w$J^xerE7bnw&4qzF66@2>eOJHx*xGTHxNr;!OLr zCRD3(62juN0$Y$!@m*>P1qZ0FQFDKKe>MubN4#sJWUIP&NR2u#v9Z-*@p&9mm|Ta&Vi($Cv*A zZU^19hf4>6?JqKG+c6n!o7-36-lVJiHDeFDSl4ta6S#fLPet_|_Cot~@~_*&$DU5K z^WCQ3XXZXw$prh%C=*$@rZuA94H02SG9#oUkfP$qsi9HVDCb;?JdMCCHE3EKC6le% zcXeH(fa08zRuXIR)*-qTp^sJaNB7YCV12_p)AI5MDsWE`wa6S*isF{L9xmE1)~J$K zDYxWJnCI>0y3t8moRC(vKXEjKj*EvM{sWJ@q*|rl2=WCV zLfz*RcmDvn-43@c_X`>S0C@cW0HUIki`@7ri^uPRzD=?Pr z(4yPeKul;2A(j9X32mmsNE`eTk*_s8m%O;HoYw7S7tA01$$Ts1=aRTzjQm&Q{{RJK z{J#d-ce~#*o0X2-BPe;P7Rx`kWQhAtvju2dPB#!O zwIxIpkag)?HbT*st#7D0E?bebxqQTi5~*6eY4TBz(c_OH8ukAG=o=~&{xCH$*^58_ z0GQv!Li!7@`wBmA@sr8f?t5{3Xyj?{sL{BrV3}KsQoR$j3RM0&FZT8{sgb{mQ*rl2 zzy=i~b5{%IK00M~{wd_E8`!@g!L-|MIZiVu1j>NBqb5sDx`0n=!YNVdsFoM3ox9nq zN`B_q(H*C%gk-PvC~9FsK8hi_NG@FF9)K!?{IsFsmdyey`;GePG)54i(@-g;Y}G1> z?Pag+RH|%Mx5uWeAO?yB`AcXuDLPnH&DASLA4vR0g;T;bYDfGdq^S}0P05!iP5 zJ&6xUWz;s)PNiy6mV%G~Ni`(sO)P$y)f90*cg=pgJ{(7S(c?b_M3&iU_AxsOA3}ji z3KrIOAnVAP!VS}i@m*@lp@1*XkN)P#jk)($&6ZqESi~_@C;}^0CR3mNHoNx*hi*Nh zz19bl>vQ)m0jlEIM{jyn{{X)YY{?5UBP;Lk{?gfz)!_G#s7t9+hmzm#Kp^%gWAI!6 z)1^Xz*AMpwFaB#!!EP`g_)7W4{{XmpKkfeLJeTwYd)6x}f}IupNhz=RYp3wP{{R6C zE1LfR%zkT$htpcHsTj37L(_ps_{zJWCWG`6HFu71qReKa)~NOMvb#*G>=FU~2D(k* z(KU!Pa*9I!TB>hVtMtO(xL4GCwJwROYL%=;msn?@?;HMla>wN@>uK&+S$5j){yW zgWpEo4{-kg$6aC5#a^LO8)x_w45!h{MRkX?(W}oHV7xLU3t%)Snayq(i|x(WHsOywKHtbH zjPoq&$U#u^s8$ z9R^PwH6!r}*e%`tsKeHc`?0B>H zA@SEZxJ_;@E12Auhh}e(R0Y2%?SO?@7+lfRInjVtJ*jZtR@Fsiuni7WYFS zcY^zU`}BCizbF3O+`x-GR%d4%JnVeO40N;}aGNPnnA0Yyp?WEC0M?t-)N6WNB0Ou{{UCFDGE}l&@5|+d!LLFuu2A? ztv(8OMRP;Z!}ANX-a@6$9P?|$?rUf|#x2c^-*|_S)Wa$Ee|a^7mE)4-xv>ak=n|r~ zPTkT#>U9GRAiTpm5`59>Kbp0y@I7k-fnrN$ew8Zr+j7e10c%o7`Fss_h0%a&JQ1PsOX1u%DEal z(XDp1+%84Nlx*+3j_$=I6$cSh+DcDM!^Z?yhF)VpRXBYnfO_a2m5%Ki*0N3o0uczE(520!VgTGyLyqlSF-%tBiJ08bd zIKTUsLp&q}@JsrHz^a zDXM5d%H_EorGpy~75+-+7lgUKbEWY6wfhPm>bm{mH{Dmk?;AXN;Z6hcJ0ZJB&AfP@ zak4XQg=U*Qn6{gI*P6F8E80mvxQLoGv?u~pn$UW3e7_H#{&Ow9pz5|Z9D+P_A(Mkq z@F)2xTc3aK5%G3?`xH+J;&}#c_1b$D+=}=I_7f7wr$Je5x5v7-Dz4xt2HkY+kG3IxTkp{(!-fI3vvvEsJhU0I#z}pdY@Ac_k<8n zTKbJw@vPy+WIy^z)FGeajLBF~{;{Yl<73Y+8ZWsnHI-eiRuN|Bj~^YA8KZ)9&+YC^k0qqS35XU3kbbs%1WVQmXY+8Ds9Q( z?IM9-`)KZPj)@~tZZbCTGOln^83T)`2EU2_0FS(ng;(Ae+5_!-3+;n}@`dM=In5lA zk@9?aPUl#|DNVl&vAb?diuYM^N{g<$*uKXdDgi+w=^E!5O#816*jxAgzvWx9&M2Mg zOG<(E_8r1|HQ{82r?HF`c zP}&aqt96Gjk$Dpbk&W>tITym85FntgY?j?=8D;h`RTjC|2P&dnNUFO{cl}zeG6E?= zT%<*9Lsw|4UVr8v33&HwmB-#EVvXHype+}ENt%G+b@dvl#I0kw!bz<`r_WEb97zSq zQ(e?intWF)JRjyJ&l5TAp_}1{06O?3s^J?5k32+a#OuM zbQ5HuLu1tF=!p*6claMoFakr)h|pzbo|V+1)gROdRR*Jvtk9p&NMvLruotBFt_SqA zC_mMsB~4@ibwqpCCwQ%wnBL)S)|1$m%}8D;*;p<}(J?4+G7uIy2gdnEgvs0RJ` z{{UM@_>D)JDjgKdYprNgWv5GS4)3SqsO^()RbuF>6NQN#=@iqn?x0B+9Z~H(qAby% zywFdz)mt$o$WNk63Zk1k{Kvya9YlgEguUC5HI!r)k?gwB3YOAM3u_*tnp@-CDwl6Q z*KIu$!YPf(wlNjLnE9oV6^!trOqS9=N%)<$5XKxnDq!*-fl>L6tBN0iEu$Xi59x6V z6g3-ZrZ;s$0G1k#g*6;u$d9eJQ}rP3I%+n;(xj76pSTWnb;0k@uXeYT)3H{P;~>yD zkZM8n)3U_b*+QF*#y9Q@q}OXcBjk@Brsr$+k$P#d?Dc>OJ_}R1 zmr?vzF7q5#?}c9FSuX4;x1%jdO2{B614@eO{@!*-DyUx*m}pa9<}~B_rMIFz4pN#m zO1~`_BeIDsHAm$!`UpY>!#l27&N5if)=XnO%V!HIw?Pl`7Y;x-O(Y1`}XM+P09-QL#@_?5!0r${7;fi zkD9$$dWC}LJSGgay$GYnLOOz^PEF&>Rzq!9B=PD!>tw2~@oGlvjH=X-l=^FH$Z!!{ zv+Aw&2dEvEBZPAMi%GxNWHftpYmfIT?GkIT)}GZAEe4>ELN$PbQxmD(b{mUUynGb3 zjO8ho5ejODdbUHSb5r>1VP%mLQKfVI-U!Bbv!Y+QTAgOEBvl7+zfJWbYjVL=Q)PbF zc~gFb8q$Pn7 zZlIb~Cbjg?0VEUM6IpQ6(@bxwlF$=xC&(MH+Z=gE`p(oelkwFyQuoneS2#RFuICNx z6;8TMc5J7lx~}50U*o>Hy~V+Ei7U`{7Ojdf(1?|dioJSj6WDsG6g5Q;Q>JL8h3=&{ zq9v~0qPmRksbHfqq)3SCZ%1`Zx{?YUNhu@q8eB3EQ*tq`8mHC6lrt$7McH1vxa36+ z)7+w?+fJ1_Zt_w>X4@SYbyP6nQr4v{UCJkQe07+6>S(o$0j*kh5IX4()jC(2V3hK~ z*G2_UTWJZ!;m}DTZi-Tbbkem*G)6t2_rJqRqHA&t9Uz4JT~r&N%b*Y_W?l`tg``lV zk3p#++ek@~m2DN9#d0XJEXhNQ`kPMaJ#~8o>o0c!s$yi47;;j*x^*Y=)6bA@QKPD9 z_k|s+^VE0ARV;AkyCid^9CZ_YRaJ+$YTJ;lg3 z3v-E7i0M%cNo*a^;2lK{-wg>Z`%Iy33qeku-Qi@l%Zx72tqkKTUqr<^=u%VpvG z*T-}4dxU2c+Y=vYN`oefrC5@Y?W`X`P<~qMHklYM8@e|F7IlQzYpNaKdj9~=+@OIW zDxd)E@%7Y)w!IVkR@!8QYg4wO?I}SHhF0>B(L1oyjh`Y6qI z$u~ORGT9!}2>O|frBmn7>HvVd&nHE9E$do+CMUH7rI#emPhGQx7OC+Xcgh%&IDrI+gT<}M26Ge zmRuAYZ|khqM=F0yR;wF@6x{Un;yExt{c6o{py0)pZNDM3xCs+&lQomZmGA;5eIbL4xY7n|Bu3mS-^rwYLy?GHgw zsLqt`pjMWaKn=xOK6K|SbBFQKh2be}nT@yYHp_JWodqhQpI!Ab3sOx=hm^{O4Xs?4 zyxy+4CC)|8;~>8#Qi`2%TbR0^IG`5!0rfcuoME*v>Tl;pE7C zc0oa@QP`3XO*Fw%M8(C^V+rGxUt`%KCIl)cwIone;i+#Sf{F#aW>?i$xssli^4)Ni ztz`cIj;mgG6{(6np;(U}IH&%tzBPTkv{7cZC^%A}KygVmpsw3$#}Q+=)U2l?%Z9gO zRhj2bSB-D9HHukOmSV7;#Gwaauo}F*m8@t1Ww7}2_wI6`&c3H!Q>s11X1n^=R5Fvf zZUjIIAt}GhEBzS9TyL-gEOM-Y^#Im_plALYK#!CTCg_P7d zq<11aBAcj^I%$pjjTKwubdFq-=I&?7`PVs{mgEmDxb-Nq+PlA}7Cm-I9V$m%KF}2S zqYMfsZLpAZ;A$s1Zbd1-T?CL(2?#a$Y9XqS!k+v|lyYt#$vBnUhi-Fie2l5F*u~dx zUu*A5eblzwJ*VkfR-yq)zMqbx-Ine1bW)fq7w8#g@($0$d??QjxYD|k;r`s~ntmF6L zc8BA%?WM-c4qKGk6&S}U?JfIs?Chr9w52tQGf3GtZq;Z+5xJBL=1=Z#_7=r?aAbVz z;U&5z70HqdlX9LED-o=h*J<41?WUcuX4P1P@&|}VJ4_wY2Tz> z;w-XpQNp!Z=HAb2>a^4C_WuBd_Fdqg-)qG$EgG_M?=`sJj=a}!NMXxu-aGKlJtO^phbDzZI{gw^XBFeUg$*38)%J821wD3B*?|sQ9Pz+(tQI zFMx`w1NPDTXmf8gb2eGXenHB*UU>V6ZcW0vUhi*7og*c^!XYVXx8*HsayuP~s)-|D zk+36O8a|mo5Tstgh5aZK{OQ-BtYVrIc;pFF(6S3IAO5slyWwjFM%V1)DZV=-+D(K-MC}DgOvqQb$$V2CvSu?y1e3$>+Z)a%U=Ise1Q zkHKfNmx618@Tz>q*kjyFr7iY_6{yev2ISY_tq`Km>f@sNeftajtu0wjOUAxx^VP0% zmsjqod&G_zTPltH?nGwPT*&8@(P@PrySg~oD1g1{kT@^89HFx?+A@S5zmN6Rc0Lh> zEgq|nPK)kGk*}6MA-A`ZB}${t5D<_m5x%kfe!^H78wjZ? z*E4d*6Y3fOsgH+#D)_Bq%QB3s$3A$y+?dENld>$+kE7)XzT0HnZV#>55}s}Eb;Ol8 zqf!(FcF@ejbsMWVp+|;&v2XazP@i+3GBIxnI8Nb{$e7btoALdQ8v$zxFI*m?5!#y6 z(zRE>YR`(`oqaaqGzj?yZaX*#?!Gy=pO3h^kGTp@o*#2;{vHpA-7b&c_``uDJd2)d z$X5h7msps)d__Ft7W5w4A;H|CPT+tjcKm}Oc|3nhXw3$M{l4GN35Le8vi(KFhPs6< z^Y`5c?YrftJ=yu!$Q%bK=Zl4s-qoW9uwG*!tX~VaG~j!-+h!yrEowqkAQz-1=5~1o#Bs@JP6k_aE~10vKWIX+A;gf zR#qY0?RQkThP{$QNm>-yP*s11yx}DI`z}JuJDCY5vh^eEuy-74OUrgPfE|8Is*yt& zt|t+fRvK;$R?_>O1yI-o>(O)DiiP54-3Ppos{a5P!F!VVKIxWCY`tu>IoS@sq=c%8 zBEQE{9^o4j+IP8L=9Gq#8>J`!PN<(HUaaDZfhxxOmX!JrO*bH@s#rA9SI%W^?pEfm z4B#l^-^VfcRml9d+Itq-pkvY!#gZHJJ<0ve&1roeiV>7L1FZ#=e@|*;JxYfYeQCfQ zln3{U@`e8Z7(4~%T(ZlN$;LCw^|)DZE5s$K6PUc-ToBlKm861|G*n#f8;V!cP4R4K zxw_s(KvOffMka;@@EvCRpZ&$Gh-DVvGr0|*!cvCH@;R@owF#i9kfoFOlU;T1{c6?2 zRu8}|naRjMxTyjM-b?P8WGPe+H*=|?{Zr092KS(P_432vNcT`K4ISSFj zTrtYr$$pa5TQ3)5x156EB0_^~gj(P^C?#qncBM){Nk2VXVX>m>aKq6?zP>Puh4EJ( z{_DRpBghX$;4VEvI<%E0@TH^^+yPg;x%BC-vfpiNh*YPM%oCC?{o5XBw4^F;0ALb; z{Xq_187K4aR-FF;?8?w4*~sRq=`Y&<0PpwYf800A9B;>LkH`E=6vyXU?$?aNEs1rz zIWimzV+l%3c3xzmYg_L$iYZ9}B%0I!bG)aC+u2%~8`W83vl2@v1${P1fakVcSf~P| z{sUaWtu8bRQYlSW6ElyzmJ@`1$1Oi@Tz7{qxxXV!z1nQoJl(sLNS3IEeX$p(%9>gh z>eU`PyNDYSDNuFNVBq_045i(wvx7fbwgIJAXm0nxY_!b0DE74l4@ps|6ijy3@!aex z5!YB;ekrruZSh)*yN^yi-V5aU@B6rL70UPE_<4Pjz^H8xWblwk?&?AsG_P7z>!vf? zH#lhWvrpy#AJVp0{oOe7c5A|UjwL+;@|eT?qn#P{q35g5+wV>P0Pf8H0JTk?H~#?L zKF>DuhZAtcr@{B(U3ps|wb&tCla)tWVyfjW3@SpBsH3?opsB)C+g`ys{VTD+%_P~2DB{Z zYD&yX3rCSSP(^o1H1F6i&6pY5nF0{{WdL zzkt%HkJKo_FZSes4Y?+sgbgY-cTPG|6Pn#AxpDpWSrO2>{{T#EHvBZ~BUN21iArq} z4_=y{6)zaxQ{{YKT;o~<4=0}w^Z^dXP?zY?>!zxR3CYx=xxc>ks_PXySdg)m?ji#Dc zz%HGH=djybh|FQfT~}f8&-;=7(#b;~x$v(2jg)k3;uwpagKtRBtbYTn%l(+YW}ds9 z{{ZKIMIqeoCz|?TehR>1fA=SSoZpolyN?xKFtmR?#m+sqKkWcM5EA0QF{Tf-7x9Yv zr2Z(+Fp1)uKwm|8R$Gtq4j+u?SW=0_Gb?N>o;%#hVM>EGI%Kd*R2_t&LaFrWu6=Q4 z_}*CKQWgEZ*mHLU#IHEK-1|83zd2>Rcjg7R4!iSqCHp3_rmnbBuXh&E>{Li$CP(cY zBnF%W4(+S0_9u^I-v}&m4Oz>qt+c#3=0G=1yC$;o1G8iZ`)R1 z`o>iCar{yaR@45_yAAgG=l=i=xxae~VQ zYxbN+*W0+J`k=~VXMy9^B$O>K#Gjy0FN-hxjbP*UzqpL4*BEa*KJSX!)VXMI1gJGC zN@!9?rFJ9ZuGzuMeuP)L!tw~@^~h;lA!c4HLsp;qy$J@I)cTESR3<4_dvTI&t|?5ba#c##*QTv)Hq%8*m3iGlm0nDDVmz5>Dvj8E;Gfk` zm2{_oMyeDKJGAPW3S$9P19IlF2U@n3Z}QV2u`8ca@4zh+2L@PBH(7{3`e3w*>02YJ zKU(UY0x<#x9*Hfx{pLbkX+WVK$e})+G;ua9$~DB7C4^8>Nw3jJ9<&07^46vyK(NlP zLgcW$V@gIRB+SEsat^|K|5EiHDO_8b#o@zNeAYo8;Ic* zaH`}x-*dnFbpwv3W+RAPKvC~^{{Uu>FTB^N?tpcs{{YmpKmPzT^FU^;yR-5_qwZz@ z0B$SP5qRSVOQUp`c+|8u_?5sKkT|Ym{{ZHGX}4uG+fK`Ta$jtyjjV%iN1UBFCN@E zgC@4OCLQ-7!x+0<=iB2rrVyhkF`O<0C=`HIQLEfeJ1;DM;*f^?)Kht4*B$JsRVD}A zFaFo8SS2txOOH^dLG3X(xD?cynoZFNBkNkuL7sHVhksZ8RTQ=yadMEt#-JgleOyK%xC@xVT1a9%OX z+&L|dMB$E5i7C5#YGGhJ(p_={m~t4B60|J}N}6%E03g&Ib3B$l;?DFi{<`&7_P4Ri z0jSY@$?_*If7r(y90rg$*_Sw~$x$ z{%Jo~VgCSWR=dTYH2(nY^5Wh?i2neN&~T0)ehBgn&;?M!m&mWR8*@!4vMRdpA|l&$w?HUHNOo-c@4pNd{z&u%1cyGoRJkCQRpaN;gC=1fLv zZaB{;Sgn`KEVT4fcL<1)P3S>Nlu~QzHBpeXb+yBzDNQ?=923xNu9V-Vn_;LyHTgpe z{{Y$-8|9dwr|&Kh-Bqd5E7>uT?=XM)bb+fJe8sO6QQsEkmB`FGYm(zKWHwrmj^s#W z>`>!mp-Cg+6bRO`<)pL~o^FR#FxlHGakM9OD1Z;*K-E-CtEt^RMx7PP@R!NY2mb)+ z3$H5VSKD4kFCV(xte2VBsS#qsvRrLa+=TNeW@$oHr75znaX^wkPTKPaHTP2b!!-MM z!sj)eC_00!SE+G6$2#v79M!)0TemLo8ls+}Hd;ADYMKeV)u&Rm5&!@HPB_s`(I| z$NX{j+4H~2U$$?RebWBhUvfJG>c+BnT_|SDCS#woi#>{N+?WonG2eMA9iQ^*AE`w? zvf~_uxvXn$qcwbV1#nx-VUM~y^lYDnerkqWKFQdhi*{~KU{%c%ZzC+HQW4LgosGt9yAKsOWlDK^;G5**{Tqwzn}G$>k;dQfckX-t0AIO$hIza9XY&)!yQA2SMSflz#&<_n&6|TUM969r z=x~&!sctd^i-M?k3IQkm8t67y`+GJ}0Z+Hvx5lcc({zvC;z=FP^yohN_=*(U;r{@( z&m~@Q_Bk`jOp-5*974!TskvVglY5@WVqByfXg$D?NCnCeN9ZF=O`RX6F;b=rU^P7t z+vcs$74j}IhfI5n{@5G~{l_T{dl`?IIh1tDxdfDiE!b^I0l3H;kN`~$2tgXFd`^_s zQ>t~ZZ79_j=J$Udymr4`bDnABSw3NTSuI`D;yUuBTVO~^i=B>0aFjNLDOC-F{{WO~ zUA?R3(24}M%GM7RKzY;3Ze8d3%1&E&7Q9`R5oy6~!BOpzn+NGnVo&u59ZI(O4PC_p zSSS=3CM8!g-r&=xO)2THP=T-j^!aJJB12I8G#YA(tI(S05mZE@j7NFbnU5Wn$de&K z3`cDXamN&*07_7RRHAFJ0=ipcpiGxnMbe)h{@8v;^73uDK68yo;r=jr9{abevtgU8 z$mvL3B0_K#q^wjWPJu(!CrnFm+K3BHEB^pbx7w?n{FiHE9ONw58ix`17W{taG9A7& zlieUO8RF6w;h^1McYbIB^0lFqJ9|5+ECj#(w8Wl!`?)s@wPmY2J+(*|H)U5>n z0K7gUMIVsWH9TSMqEfiRL#>I?_e!@~Mx@OUBB}+f<)>qk2{rVkHC$DI3j)3e^3vW? zsEx&#sICQrxAQ0BG`2fws$xiY1o36mNo_8)YLyS+t1xP*rj4rkr@O~q)g7?CEe`>6@7u++P4Q$X0{z&a$T}qV&Cj;7THpRj5wXUR5zj0=8n3x3t4J5yQ+TPqeaeb3CwZi268)a z2K{YERfP3gN5Ja4X{}U^)X?-y84O0hME%=8)3`7{=2`V!HtDCGLMoEi79WCSuer4; zJrt8leg{z+NGfYovBW&DdE(rfCDs$Fa^B*CZm=qQ9%{So@g9dvUf2nluT-{3<{Gth zACoxK8S)PUV=}}ZZOfwC?q$(VAqQm^)ELS1x9F{Jj8N#D~(3WYp%N1uwBAU@Fk zuk(TGH04uFmu`g#9z<(&b-d#~{PiEAzojYC19hoRZrz|ooKh#Gy4pfrJGh_r>3Wcn zLc>IyLXb!!uR*4h&2Unu9db3x2)GW_`}WC1CUhlk+R%D}4z=80luKgzbz$lj2byrW za&Y)ObUndRKOpg4yCSu@$UJld@E#uJ+Q^C($6B`~Aa6};v%GdYs6M9GPn<$o>*G(UZATH#N3C8$C}NXkzp=f zhDw4{H3~lDa-%}8p1Kzr&f6DycOtAk{1+eh1Bc`v5t|<1*+*m0{1ora+(~1wt}aQ( z65YPxQlqxwZYok1Lewfj?CJO&X?aH}H5 z(CZRcY>C>XY*ReF;gg${Y?h$af2|cq=uq|BPFUq%1M1SNbKFwbB@t|m*Rk%d_6^Ed z=4BNOt>Fsk+CAvYAa^U}u9uC@MR3{^LcGJucqQgGO~7tvViTU_Z*?Rn3Z$Kts~ID5 zq_VqdO)HFdQD1@52sBy?Ihs>+g{6NzHVIb%f)VBxsw7sXx_PQBtqDQ&w;C(cB&*wH zph;2NrlZ_|z)e&*iF?BvsVb&t87sG|lYuLwR*4NFzJpxXFOe^I!Fmgd;TvrTuj(p2 zTeTFf`k<=Vv@dB&VB z8>&p0=^e?{9n^$5T3bg{fU;<#UtLv|b48_keShJiP7s9DdM6fBOZ3Vrt+di{1RSXY zx2}$e0=LjjrJlt6G_=Ajqy=^B=yVZ+imfyyHRj0%^XZD^TKsDNn;hbk!&n^1!(RE#pjr4oin#pR(O8 z?j^<~u;P%Qq?*%X)8(hkOqIJ_Dim2-NvJd{>0`m)zdlcfrqhb^#3v`)*$uL9@&2RQ zC_9#pyL`2T&wFJJ)m$6A>+HL|fucNs% z*f=|oq468*kk5k6;pYDU9cZ=fy@eFG`l2apiVC0VHQXuq>uF}_C;bivsvfg2I?)~3eJ2nO!1T<1H;<-b0gJhAujDfu56xLh9+Cpe`Q zAE+LKQZ56eI;yrw94(_$)(xT*1pPrLuzf(#4Thg&ZD>sk*F5NwZn=fN%n!Dp;=tLE zDoOMk4Mdu11uEXf(mYhC-sN-3@{+&XM{?b5P@t5$wwit?RY8-=dT6u;=F-)=(3IGd zal|mjl=P`7yIqIsMMA(>+zS3$7Xk}mQ>jwzBfq~sQMHN$%y}B=ki4`POEHB)8b-hm zhO-yBqcNb+1<`Q4VmonDsZH!(61-)T*^2@q?mTRW4RIRP8nUK+{`EmaM z5n3=ydw1N4C}C`l_nvl?6cyUK&s^mlC0f;}u*5PUxymKwZy{t{99@$WbjP2e@HjP}O&ysL7Z$%#CT(LSW5jGNLp#HAvRQ~`FbnKRZ^eM}Ht4Hby-u>?% z%+|{v+P$etG}u;^+u^G7xt-OE9~|dErW+l$*N0mbEVPCfN`Poi-!rHs&v~I%%#Ju& zRSIWZgD&*ynnTJFn7-$ zo4wL1nXxi^lIcnQr>@o17hHp&R`A&t(ZyQ6X69^vhcR}s3|yfXP~T%w+bOGb6eQF9 zbw1M4rlmy7GtBdXRrF2C)(46?rH0{;T z+{r=xtw&MUN`?~Qs)>1g7$_6s`Eq8<5JQAJJ5oXbJM1)XXc{WpaAb8#g6?ydHN`2_ zmlgb?zdb^Hl{&38ycP7yEA> zV+gcbR2o_kP>>JQPj2UJTeJwUB>ng~e`dAPj91 zdQ-RnegjRX>Ws)?<{~ah*#eXvyXZNj1s#tLbrTWwscTZ!NhJE}8=GBK)NP=m3rwZ0 zLvB2zG|Osr*9Zu0AeAXZ&;SQgPK<=GXuCDyCyv$+kv81UjA)smQsWqWm zy6@0wUYH7WP;3?MtD_9if&MyMNJiq08YlUB`f1{sK&>?O>7*2GN$H{iSV`oar2j-SVgMaZ(>Tg${f^odr^WrRdGsQNc!B}ERoeFp#56KWH0?` zeI_ZCqifkw4>P>j&%CqDWi+{uS-7s$`i3!yC2B$+N)`0Tls4~h(x4ra`0HVe#8TS(B7Yqw7wWumB>fHHhe@^|bAl0`~zUXl!C*h}WG4aYM zLR@zQ74_Hl&im_PoWnOB;O{OUi-Gb*(dJ=e9GDgC*)I>c0vm2YS)@nSucQSMg%T)g zdv(b<9|bHMq3PhXHaSw-2XtRaG9)e{Q2b|#*?1*QttO#Bj^Lz`)E=Um=aJjFuA{nz zeI?K=t5pK?f9>(($DX)?t^AdZw_@b$Y8)7MFWVbK7pN|&1tBi2Ab#P3Pt?=B!(e)V zb*07fY3y7LYS5|ftl2Qrs`!WVKke7#zk`_+n0%><*;NKO02c!G4UGoU3mKe^VgpI(a*3vXTrHERrkj{dfw#j zRdXOiw>GhhDni>rZSvMjsCr9=6JdHyFJ)^DXAw0vsr>wen#SKRZL zt~2A@cN*gxTGMA~NV^!aTKv2(s_=|}Cy_FTj{0~>+n51px(hj{mepxAGDTGQ(Vbp>GifK>|+U1vcyNFFB z)!o-Xvbm0)T>|5O-Ghlf?LJ3y&U43@j{w>^7sA;LM7LsC^{*p<<-%L7M+NzpHXd6& zrB1AbBB!(<8j`9fwQxR9hO&;b{{Z9zO*^hz&G^nn`TqdoK2ml0E5-JL`*^kXz4323 zymA0%{{U}1-*=II&)3|K+F32lG#Z6#Y0zj>9h3z;;r&i`9fbhrobQsqEq&$awR~%j z40WHF>H_%c`@#LeZ{B8kBcJ~O)7M|$@fQ%yUOmN=(5~c4l*_9A=Q*kx9_IjU3iJsi z8Y^A5iQ=1QU^LXOb(Xkw)zxdF;^c-^a-A~oaedp3;^0A_c9iR~rM=paf!_1#TGc?1 z049I{2HKVY-BmHoJAO$HOeDrqmcox|+>ef|p)^&K)Rpz6_5zOPOX3D=i#rsA!Q~fa zIVymqO$bcNSouN-I5~f zI`l(qwhz=ow$}Rel4`u;W@%L*@a7ucA&Oh$sj!Jpwj7eLQlT|V*U$l7PHSr5JXB{%Kvi?`U^$w-%Dza< zWc!@ocvE<5=dhGvIKgt$Xi656YRr`f=BB^=U(o7jBguZ48=xIjj1(cYdikpV0J=Mz zz4knG@#YN0m0V?9GW+~gxDu2z5wGnqg3=90TBJIp*J`G|gIybf*oMQq#b-0~6EXNN zk!aC29m%i9T{BP?PDxRDQQdj=qeYIVT zDp%_p_T}M*{670|_~XV`*zDSF*bWmGiAn(IQWV!1}U4I7wQa@B@M7vw1ZCW z=yBWzohw~*jsXCD!786J>Hxnv7?HxsSsR_m*LsP8V&{<@dLlv;K;Ki0Iw;JT)qQIJ z0NRDZ@~3idlzjI(+=FYdTWt8QKXXt8U%Z<7?o&{jjk{VyHT-qNz0Rh$I6|cXOWJSy1ukB$f<>hbRvz>Y7 z=)k+%ar=iLWD#z`HEpQv&KT{m0&7JW^a=QYb>F#6pG}IO(xd)Akys2-Y?m#sjeno> zUtykZ=h3*A!~XyzxfS&(nD|Bol}<0DD5(oYoz~k>?c9*1iVbou9UkkkRnT+hmh19( zw?;ynWcL36DoGzQH`k_Zngd74av7d?6ux4kv=G?asUEs`OH}~4)pXh;VbDXN*lD;C(rIy#();a=hm9cb4ULG@Ll4sIO|WZRQ3C&&o>Xa-@H2zcmTEC5ZU=dS){2!Np>;?x;dpbI-hRU0_Un;XoVVq{{XzyrnaS5R*6ZcK3=~a z0EiTqlsnsRBnpB+(Y>@%8fv2FkO@Cc2l3No5qc)Ls8wnKuAkl#L2yWOqZW|jsj*6_ z`6TI!_^TNzWwe!W3-%uHwikDaemN{Bbzpd^sCtT7BkRW*WB&llf6T7Yjko^*gOK_ELm*F{zShpf5}T7wEmj*{Iw!@C=@GC))XX3 zHQ!YQZ0fyA=nZO4lnPM`c3e_jLaWf}aT+-yRszzjOm+K^V=w(pp#K1dw>G3~T(0f3 zl%|dK>9E*xb-DK;++cRew@GP5E;k}FgY7K@ZWNWE5l>w%{yo<2094UhVcb1oUWCy6 zj)(GJsOOIym2Bo;A9z=mt&XAS9EXY6Zt>r3UC3FNZ)s{EkN2yu4`wZlH&1WHcFO`^ z6MhON`+{+;!bCnD3= z!G2e)g5ga~PQ;&{yZyM{sUMLByrcgBO8K|T++6w>`+icv zU6zGaA-~;YmnNNWxD&bTn7V#?=bqqMR@|QjV{I)Q)A3(kKXvXWx$|d$K2UM(mJjy} z_3sS4J&!^HrOSi}#(N0>1JzAZqo%pv47u5LLUbzQG(W~2;8*249Q%|}+?{$3+2PHp zS?RP&KHk&-@2NXfJ?p=}_SEuY_($A@oE_O}MYox7td7d4K!BCZh3sCKS*WQfOjkhv z0L*pHy~VQz*LQVoVJHQDfU-bKjgh%l;O(vvZhWC?+Tud_6aN6Zr->HkA>xNQG2R=r zT=EQFa#Gmr{{Y~eW^Sz&s0!`-B?RwH&bs#oy$+1G!Dp|bEs)o`_}6QCY<1Hk0aiSr zR*C{fmgdnm*RQ`X`%pQJd9R1O?ZPwVCCir!<^p|+GHMG}oR%77?h20PTTf7c1wM7x zpS{$z^zK_A>b9J2T7usd^Kt(GaEFgKZU+0C^DWWXYiAed7OmNqCn+o~rW7YeOeu|} zXX#qeDl3Xu2Vk_+ZY$Avc1P+gnttU?l(+QdA#quzl2cN4*3vc=M)?g^?Y%DFB0T6X zt}Mt)L(t@)(QUfQgNRa3#1w)wnc@y%6ibQqEA@Zy@04x+Lim@#{G|~-?0oYRvs`4L zih`+D^n2}^{1gZyUU18UXOZ9WTRdEbTYajeeZ3^7j@hWsnzJhgfm4f)te@Z4{{rN!%$U5O)VjyyCdY+p+O9 z*WeVaZ@8IgA753(eAm#f70%q1_f-3Eb1xh6Y^O6nGQQf7ndH6$dd+%Fn=-@={~@@tN9JAOybD#qD^$eh)4D7$1W-e85T`$H4wsl~F= z9|aAt-)a+Hnu(dWpZ$@E$H?QS$z|-rBeD-@qkt9puazG+{{Zd{^Shg}?nUAJ7E^vB zVwjEg^<=$Stchabl}KHtOm`TW^e=Ce_g_VvHR3)u<1dVuk1cR7Gpn3o z@y11PMtjPvC7UEmHSQDYY4##RQk3uQlJO`7Yf^}zs)4St@a3nwl*cn@5$5plSpg(2 z^99(iA9JoO^2ZX7o(cBocZeu4Idc?wO{zTP$zqNz#+^N;Jcf#b!30<4HIcZoeT~q3 zek(_go-B=>CF@1N&P?-v8T$lt$1?c&V8_wqr-_*(=-)Hx{*6cn{CWMTWcWDrFY*p&nIBa@~O zX({@ZXfZtEp9 zN*xUl+-CP3!5~l_$J(&e?45<&*LPa!HP9qVMB6NnK#AQ#)c!aXb5>&Ml=mJ3`{DzejZjswt z)d^hI4bD0#g7w80d=ATRy>cr{#s2`jH}6^Oj|yC5pzas!DvCmWY6DkNV#TvTlR~r2 z%(~)Dkcy8U$~A$W-tpU{YqlXJSp55X`>ppz`xPm*4vGp;ARo(Bl3eZ-8Y{?g;2OVD zuh)-%bYCF&*8at>mUCN&EODj8w$0MtaB(irfZufs(H;o|l!LkzP<<)u5$vGXQTeIw zjJdjXTYbeiy40+L8S*!eR*TOZ^4kqOFR@BoWJptWPkC=-D37Fr>NJnW`+@2h z&sEJv*WK5s&s2J}3Nl@4wIUT@u ziWKn?s7>p|opBbY+W!EAt;qOlCB?BGV65zKgT)F-g(QU(sC3kL-BU$2c!F}FUCFI_ z>e4=nvYM|=bl^%w)%sQVX{i&4o2UG=Mns^FS|_#I^|Dd#jU%By%CCs7mkh#*6GQ#M z6oGxXVOR7_hOXeskGRqO4z{g|NcoL5E4Gz7CW%;`87x@t7IS<>wSQ`yul@4Cc>nL2aOjo2|W}DP$UpV4SzjpNv^4oD7;Cb2jQZs zTa8leNkt@mT{?|A9H7k*O48!~>4HK>cpD#%)zp^?g$gM)xiwI?BEP=MLELIPT&&&$ zf>={c%4^PDZY?xP#1zyK)7MB0AgK+ZQfC|TUEc(`q4r!aKy-mB8j0*$ zdJsoWn$~f?MJU0 zt@ZvQ-zd*k9Ut5-VS(j$*f;$(^<5ayJHzIx#md`0%`w^sD7&?l>{l^|#zTSb6z#)2?{(wJtlMt!{{ZStHbQ{AW3MWyakJO3uk@?ot#=*e z&$seCZei?KGxr1A8+@xhNdExGIOyU0=(@3wSw7#nt^+41WtZH$OF$curKv>c4}uDO10k(~CJ;#jrsrF%S0s1oeyfP`?``9$mfJ(%tYlj$zeWAstqPFJ zfFOZdgTJP<>~mkMR~w%v^pb3sLYh|_47kz@PDfK}K9o9j@D-r5%SQH88m-`19r~Yo zsAU_eN8zN1>UBxG%Qy75s)RXjHe`cjwYUpS5|^Aw>PO+MPbA5T>}g#?+uknf`qGU` ztj@TlsY)ibI_HuSU8?P^U;>B|Yo!2|vqpm7+gTkng3yRL865_kNm~l~bkq9K3(*-%RZ1QY-O00;mNcKTRoca;dz@(KW>01E&c0001PWp`h8 zVQgt+VRUJ4ZZByvc3&_sFf}e}aA)kfWmsIxwl3Vb1PSi$(m0I;2=4Aq(=^aP(|FL3 z5F~*R91;i+Ah^2)2reuBNJnDgX@~06;_i1Kh3vKq>(+CjdZ42fzye0B`|lXd(bqH-H@VqecAy zB0?f!(n1o_A|PgAF=;VLX;C4R-d(VZudk=HprD730N4@X02P2B;DP~QPeEY;Awj@B z`2bHa#0~1p>;QF!dC0PUZ|h`bhB?Zznu+TO>3AwZU0~`#-cX|;U1La)8$`;HRbGzy zUVwA}+!GG<1v3Z0-93Dy17ul$8<$4$U(JH7%)do^-DFu6eswUL>*zBpA-ti?5(2_N zh>(yFvxJm@gOHP?15}Ei8RfgMpsAM zEbE`Mdhp z=3n<<2ZW!mEGz3@bAWs5=={U*e;F<~T<|YxA72%J-~V3P$2iavDrgAxLHKz?pep`Q z4_~&w4ffwF|4mT&f8`bY4^ibn4=@}C(SbU`z<={k83zAX{6mRBJ$z6x@%e{yj*x#S zdir_0|BkLBL=fr@MTz?Qpp=DwkAsSrw1$U|FW3VD)limYMYRaPV2;uvqD~Ty;^LA( zQ3(+cP)t-v94ILP5&=R*pir==B&yX>^e=s71jO%`)nEEf4&o9}5CjSom2!{*LY$yN zC_RV+P~5>mSm-xE!hh-MdBafo3U>dy{;%;JQR71#p&}3=ClFA?QBnvfCg~^!1Urd9 zfgni-aflPx0SpC6{d642K&2AuTTO$5j8L01WExsORkn^_FGT_eMB- zLw$T;{?Pv*qYn#!x-0m2LLt5=Q!s=qE2^aal0Yqzx34j3sf{3D_us?(&plwcr#sXL z7KG|X={Z8Aoe--p%7wq4Ir`pEI1K9jiy(eIVnhUln7MVp5SWKA!pDW@FI|WW*uxo$ zdgKYR3jQC@<*%F5^fagdWFRO~yy&eB*LcMZ-2lcO) zy&Utu@o)Sa|Hi-ZZ~Pno|HD5&7eGBw8?gth@^ZI(0DgTJ#IKzi!joB4KnNhIsG)<& z@#~WW;O+#&^O<-+E#}rEyqCM@iVtsB3CuN=loYS0{?9-H@$~iu0MPVNT+|V@4?^(>6nB5%>-nqw0L952+*kp z;b9h*l9FUrhdywJ`uYO(QA-H+c4SsUz&*hpfdIfCbN&s|U$JFI1sNnP1wx&m1%v_r z#DClQFXGqQ{=t42{9ZFO%8&nS`>*5>c>sXq2`V-T|7>%31pqWf0sz#r|7>G_0{{@C z)~)f&-{wR4D_{Qhu?Pl*2>c4@Kk&a5_!scE@dSRE`$u?~m7z{xKX+f|UvYyV+!21> z%s!|Jf-(dDa}xhwEB+0C^Ml_I>V!H{p-yznrl>4~c{rn%+rts|`}hL$_+KsJ|E1c0 z!XJ5!0@>Re0J)t2;O-Z40LI=&04CWT0ETrost4^K>!yuo3eZRWHDmhrN8bMi+W%}7 zJ^t6l2j=`+Psz}j8RF;d|3}}ivIJlO@BzdCasUm05x@rE1^@vf011E$;66YFpb5|e z7y~Q-wg3pg1po(l10DcE0FMDtfLK5RAQg}S$N{_slmIFLwSXqTJ3tqpAMgP%0+{!EeS0HEf=i>tp=?HtqW}kZ4_+=Z3S%$?Fj7>9Rr;JogAG3ofBOUT@qaZT?^e9 z-3Hwm-3vVkJp%m&`b+dY^m6n@^e*%d=#%Kn=sV~q7yyhr7?c<+7(fh33?&Rb3`-0r z3~!8w7%>=W7er7`qr3nAn(Pn9P_!Oc_jdOcP89rWfWz%;%UHn8lcl zn7x?em@AkEm^WC2SoBzYSkhP;Sms#HSP!tGu+p#!u^O=YuqLrKuuieDu_>{+uqCk7 zu`RG&v4gRnV`pJkVZX;7#a_cc!NI|y!QsP^#nHn7XpCr|7>}5P zSb^A@$Ox`yPS8G?t2?-gAFo_-soa8x4DajDYCMgan zC#edlBWVO_K4}l>8W{!|8<`RrgzO2~Te3c~4RUO9E^-ZWSMpf$3i8k72Na|fVicwn z0Th`O?G($D7?hlp8kFvo@szccQ|oqtB4d(cf-$8qbuw);Q!pzqyEDIJ?qxn;p=VKN z@ngwl`OI>~%EfBJ8qQk9y1<6dCduZ+mcrJ}cEHZWuEQR}UdBGlfyW`q;lh#5F~D)g z$;D~L8O_%yDK`-Kmk z55x!K%isj#MSxNxiRkqEB{L?lyWLX=QcRWwYrS@ckhPYf!SEjBGqCaxnME#58u6C?rh z233N#B-kbFC08j3MqOkHYlDc zNh^gay;sIiR#T2q{;Wc(Vxp3vvZ%_c>a1F(dY~q*7OeJO9ZOwHJy9L0!Jq-rDA733 zl+X;-?A0RBGSbS>TGi&&_SSCILD$jLN!FRy<EE$DOUd+E0sU>oQg zWEy-m6gCVo955m?vNI|*IyF`_jyIk&;WqIz={6-cwKgp=Ju_1_OEOzF7c>tw|6oCD z0kdef#IZEBEVMkaQn5<4+OQV4jJ|BenzE)!Ad&o7tDzUxW3) zdEgTV4Tskb2M{GlI%LQ3zGI5x7E~6R1pVeD>y+fQ=`8D-S3sZt+zz$v2 zU2|N|-1OXv+-}{?+^gWYaC>-*2dRgfM=ydA5r7!=#QJc?M0REsQ&B8u{k zLPpC(=RC!H3VZtbndq}u&u(HIV}@geV$));pF^Gx#|g*1jJti|^x|_oC_Xy@JHaDi zDp5YMIEf@FC}}NOC%G|&IVC3LIMpt7C`~LaCmlcCFMau?_RHp1?5`4DU1zvtOlB%( zR=uWw{p|HwmSfgf_WkV29EP0OoXa<`H`BRlxs7?8d1?9B`TqG|-8AQlVVYTnVhqt)i@otp-&4Rd3hW*G$!F z)pplO)>YPX)W2>ZYlvw?YYc2WXmV*fd<{r&s&BrXitnIA(tY2?L zd?oyv_KoRV$)?EWyDinNk!`E(jUCUOtKFzQlD*ga-23(4?|=VzV1BT1=zVy5^!x|S zkD_C-D5`xIn8|FwU(odZOmju(Fc{nrm2{rBY$V4!25fVyD*0tVJ^;QRvaKY;d!78V8u77i9B z*57*mMclpzkl<9;A68g7(( zsPLeSW1(T;V4$HB15ma}FtJ#KNfpSj4amW)A{01YDBFsZ2Q^)zhHSViqEv?t-bzO7 z;d!;)W7J7v9O9gy+gShs25JT*7$g9Bz2SY94k1)@OtfqOyM4&LUTP4Y@;5f_0lYOXG1;sRiFZY#d3V>#y)VuFHfz8{PB0$ zi-%0oBV#mEpJ+?0;053s!X?JJfRhSBT=~n6VS^+C2nV`qc%&33DbG`G8}`7F%r43D zn)c$3O@#aGNtMt`VMX`!SgN@j;{If6Qg-tlOVINvLKfA;HotK%t>A2yGR2$pb3Qvy zs=mw;&_UCj;+e)3M1pSXAn<&oR($U{zTdPMV^|#bOsD(d9d%6N4?pXfJl&=tY5j%i zXWAxr7W2P~?q*cT?5((d@tl3@u%hVKQJC)E%g+~pRe`->OTpyJRQm~dW*VGp=Hrm* zr?+BxhW(8mKew$I3-&O2K`Z#}q;esq?9yEcISLzSc~Aza(k&p3+cLGPHF`hlG;(Y> zu5!t4GALqilg9N0C~gi|_1*AhMc(Cuizb!NA=#{mEp}k1{NPs)_jO_u$l%Zy;LZ^qHz&U~%zPFd?k#jnVRwHkHvxmSYB26ka z&UtdDUxSV~k_b8P-(0VrSD*wR(1A(t6Y(zM8B4g|Z{Xae=W4djKTL~c2?jkhI3G*P z>rRidZnoX{=Jk{bDBj<}@D_{C1Tym2`6^-vU8d}{0LwSd3Oz34Dz9cXJ2I`CIlYmk zeGn1-cL^?~$EUSlMAjfGER$HF%M)QPg|<;hK1VH#$e$Kxg3;q00l6MzGVdX*g<$(iJ zw*a<%Qv<1xdUAze{U|yM7Ura|#wxbM`nJ^cAD`YOax)pxzu-?gcrc-JL;jsjQ+`*7 z{O7D=gS-Lh5wCHy1!KtwU2>@GNo(`;`iBdRq|fuAmOhMhLu84u4{}r4WwDek?CJc@ zOg#?q`=W-nyuZlC*Db_cj;_dd^}ThncEd=DN|-O+e;0w(QqQ}|C;eos8D7cgYOcP$ zwVuy?LcUKmMMU`QL!Vc?u9o|g?T*|4;@# zfqZZfpBkF_Drd`?;q#%_{4Jn%k-d$c4#+M8OpUj2a` ztjBhkQ8i}r$*=1(eOsK)nw0b5*obOh7}l4u`f0i+b~@KS*;Qc{7GxW@faH*%pa%if zxO$pfwfe!3saZViwF-A+qcofeyQJY-aIc`&xlwvraI6eE55j>rAU=v8RH(H)i2z~k zXPP9|^!EjtxVOczg{D&3c@+th88VB7T1z|`OF_|MVdLy&h^3Lda1m-w5NUTOfIDt za}ywfAg!a^;NtXH`XJgRI4J^+BG9>A@W7zGOU=v(k$wxnz1j{`4y|L1t`0rnr#pRq z-F$TIV!Mf~58n8h6X%SwiP&NiRzBj|t13><%lMebINDjTe^uIO^HJYPmTk8~J4c|Y;}>^3L}MNMJ1o*T>V)L6A~UNvlN*Wp!S^PJ&;}RL^cj=4rgUcvP^etT_pYI;$} zfsi+XE&~OKYxz0ti#)k;(HdK04BOGpx2036vNiJBOGo+(Q30X|<(uzoYG>M~AA{t+ z^rxN~a2ScO-2$S{ia#Sppk~Mt{C&C25J5g5kO$0DQ$0fAf>()HzZbz~8N2_vY%K%v zsXioa-$SJ(cn*8JeP~P5>&p@AcPB#nxm0GJpq+=Q!FJ|(1;?z0IhZL{xf6@8<5eD8 zNmrBYCmBC5U%YZT%+|-zr55h-)XQ>D!fvnJhG~$x(6DtsaZJ+*s3y;j;p)$+>?NEU zWeB+efBShXHRQ-=1E{s0R6;a18NY8m!Vld+10&#M&HVPXYi%(dd`nlQUIC7lgMGS- zNXiDiMVviC{70BM_>Ai3HLdB5v$n@wg6|O@E8Ubj2pdOPX~n4# zzd7UCRzY zPfUu@{dUG)#e)MMkS>Ol+K1*A-U1G$0`FXL#+dHF+)Oh$aP6n+wWqd!F7;RUI89W= zu3W=>z%TcJ{s#@PMd5H2Qjz5P-H8hm<^A3$u&RN4&st2{GB&(0%q0P-#WhbLBSVM9UnQ~MPr@4nEcvM#bL6=5zYXt zgPPATR=5T5Sq{;p5Y1VA&W=b^-i=ojWhM!#i!4^`8;Ktp6&ua~^n1vWxn(uwp`CuZ zaS0r^pg8TLOYyq}#P8+@=XH$M#bP7JE5yIk?8VXhNn2Thl&#IX2EMz&T;-b|1-rYi zMa<i_YS$^f#CsE>m|+?l40bZi4;34Gc1O8mso3DjWqf?3(mdB5 z6^{FgplD`Xc*K`aP>~`2wS0`y1?k5XZVnp!3E{m@ET&1lk~SdTgw*3+J4S}oCf!M^ zx?{L<+X&4`?%3VQC>0xISmgIB*fcs#5Z76$(;RI_CVQ-_o~09sZFsWzeLt>M35~+F z>58On8Ccc9`9!rFTA_u8Nyp`2g1&MFEx-Q#5kGmTu+=7y!*$=Y{uY9LQ8`Ws4RA0J zH-h(G;fu+)S3sjUTXowHv(TlXj`Ft2VK0*EKqAAffo=^g9(7!sr%k$O58<=C@| z7I|8iwDD8FS_&_RiC(1UaKBZ+F8xhCT zsamsg5|QBgArNWa@Lq#C0lo%N6;i1fngt!7j&nwTI-jDx3(jxTQBlXdilkGlOm^*N zJI$9irf7VBOp(p`OjnNYZG80biNN5j1wX%ljL%aXrNS&%SKGo@6AL9Pl^f1NDWC%E z6oI$r=1T>pdvg(*cS;r$WP+9k*(v#RFCH_3&dj9zyN6s7jUNy<=*f_LbLcTdR=oS! zfnIUkf?E{TCx{G?{F2~xuTnrUnUgj{Q3Fl&TdthRm`OX;bbc0G?hSVCAeM6DeK}vP zJ?)rSE^2GG{Zg4KNop|mj=>Ryl}u3NbG&`EM6cIk%!Ot@zJug|g2l-G#705nn`6$9 zSt9OjLc74)I`mw-v~OSh;Pc(V4$nNNxwF(FIb>eCjE$@i?^0rKmjss!)(X5b#)09N zZc|1laGT?gSn0s1Kr;|1`&UY@!o1>WoSq5088r)%)0i{SOpzSVRMg`v==sLu%TiWS z4vHs-47%ZE7L?n#C~7DW-9rM;SftvoESIKfGbRIPN~{~8b=$+z;(9h7Z7juqu-e3ztcHQC=?Ye2ok4zj&^J;WJ7=Wi9~Bp>os{dAX`Band%ZLEa|sX1a!orE}o0g4AnprCKvO72D$d zI-kO|W2(rqx-^|_S;fQLomftOWl;t1Db86Fin$p6mV7hr;OlI?uQLZP>g(3^ZvoSB zZ+na!PHS>Q$L+H-uo4^Wv$M%9_t|(odF{r3_*M)Dg0Mon%l1GG+a~u-z26JEW#`J& z|7b8T7$J^z_-H%S8M#A~O~5;JuG$XQa^z}^jys>4iK!=fvAFZZQ&2tlC8W9p`=y)l zD961B&n~u@g^D#zi}@giCtf2fJS;YXp*$cG&YscmIi3TtDh*#6I)EZuY!t2QXE@Zla`AKOskPvpk}R!R#`V*y7~LGc7W&YThz9!CVhY|7oy*1o zg1ePIiIK@aIgkuNHteC#FHJ71mu0)V`&X*i6+Q|d4D^>3c4I%OFjh50Zn2yGpckur zc;daY?dxW)d$+@aQx(L<>a$D*iimVY1eH&(Z%9O4Qs>s$w!TK%m2^O;Q-ON}vNCO#~m~k8GE^Hp~lZc$jwdlaFfQha9HkcD#EX3_tjM-S@98K83`_=lc_h z*o4yb&Fl*&t_5;={g~nL!d~gd#b>1aQB@hh7?ic0^5#ocOK1E{8AO{Xg8vo}JKRd} zdMB2VnYU;4n#V`0`Jz6SE)FK8(;FnL?HvI?1YhxldB!T1DCoEE=>#T}3Sy+ZIYyso zGWdwU%jndQX1~e0de*={@w$}=Y;TTVYt&61X`?+`miA%rd%Yya3OQ_UWUSl*xY&Ps z#lsXB8L5h)9H-l1!d1<`wJku))Mgp?ing-GPHpboK<+CxFfbG=RA#H`Ah)d`A=Z{F z>5GoEZ@VBoV$IIL6Pcq~Y&s+|Gdj+ioe|pL$LAd}L9#sd^Jdt_`Y|}uUCx*SOvd=^ zhL1!Rh z$<$K8$447Lu%m<9jBbcHn9VC%o2PmLp^%(D4gRiNLxp{T#*x**4)4kcx|;GrZUOjKEV7O8tt%LUlYtGbQ`C}H{P;^2 z)dJ35_Hp_;cMB#@jhu1uP}Z}fa>}ka)RjhfD>w|_nH_b8NN+l*&^VT$oznS9elZEM z)e`iOS7=Rc>z^tMJ9-K!`L4rgVp^A-pDiQ?oW65%RK#Upp8I6+<&QTNPaclTZW1r% zUnHCo2R)=6$;!gxt}(98o$3uVarOS9*iUgxk1c>by4x-zb8Tvlzj>4>_PKYhx=mro z_h1`m^M*HqshHoL{9$G3j9R&g~(LN^kfrd{x^Zd9VYd5GhO7FbHa{u6{ zM76*Biu=XzW*C&VZ4qn3R(&EL9(sK3jAYH8p-tPOiz_MP7rFbM;@iErs&oC+Lg|;X zlP3p1XXncUSyv|Pn=~KQ7T>tzhELt|lQWU`jj!i=_wf6g#dq4%k))KwOL!1N<4;_q zEjFKNH&#_pgg|I`F`=y-V^Bs$vbb~-mbA&iwXN@mY|QpILGxEQsR-7^kxzYmigL4L zh`~_9*Q$JeJPoN+m2A_cN!wyx`;F3p=TjfmN0IQRl^=%%;nPtM$_HtBI3=!SI`JJy zhlnMPNiF7e(;DElTBW2i(aKAE<%7hID#4WknZ_LaG`p`95v5VlM_1cK{czI}T~RDe z^=)EH(>YJtm}KQz0#;4mw;Z)ngbsc&eI-y#*45S35|8P9%TZ0>zEuBwpo?5u)RefH z$Ku^R>1bU~Icd^04i|Y6jNQ_>h29Tv9e4h$o?eGKGR8=9_xYT=4~O5pPI+s|eUGS? zc;AAqTul=blICIXe&H2@u*u8rA`Y~($!T?ukNT^Ix_}FLnD|cS{GvW|Dk?MGD9_#t zyVrDH%M8=a%X;r!k?l7XMu|6>^MM+({>SZ`Cr8?m89EpnD*O_q0R5X`);8I`Z{;Ji zq?IeGYu@lRrOa(jyvU7#)GdIOVQcJMcT{6uTilEia|^B^>0Zi{TueGzG55Gt7!9p6g>z3G3OVP}`kd5KAZ)U$*15 z;+k%1Xj$-(K`3r8D)iG;a zYemm~Ho&cqjHd5%)sh#$zExO{!VLU0YT1Odnl#GH7e;hv4V7&TrMLF;C*1~9Q=yKr z8mgtB9aDU`NCk4@xd!|`$@p+N*h!fK`!VsG___iKn@9cWC09}Q@2}O!eGN z?d|t?B3b1|W{`W0$ZMV@UyBL$)yoj^9N+g>-0m8(coo4v)n`lz;ij%CZf`t?$=wn5 z;th;uf(8L2(;hN5I5|p|rMt z8^VL_DjUYVuJ#}ts-J5S-0NG`bmXtYS>Iw|xV0>2W^AUjB(;x>YChof4GHOq=xw{k zZIx@AP7*uFH?ztdB&Lj!wpAJ2w$v(6ml(;Mj95;wmoeF8UNlW$9ql z8Js;-RIb>#mupTwP|5vp*^b=p2R>>;W9;ovZ1jy1mw8_-_T|-wOzkm)O(1g z+|gumg0e-$X~TwxW(N7@r$GR9$(U@@mSQcu^6@(nW(@=OliUle%G?5wz*QXbq9i0! zyd2^Ht>`~%*i!B0b{tL)y^rjFOoc2)ZoE%@w^ar_#Gqb2Ddwd)A>4Zwr1&!opL|?( z&CCej(XV~aBr7D=Zw{TMrm!#vjd7JvEoaGmwD|)%%-yY-$*I07QQqC$4Qw#@rJgQg zj54?2I~>N4X>Gn=VUWoCjDnJqO5DJs3u0S+XyB2Mopv~uoR#zR2#M24z!ZK~aD59P z4Rd{8>($$G;b!MAdRk7pFZnhnB3A$5O2(-Wv9Sq2LFd_)idkQjZ+*l=6JtHEyMCg5 z37JW8_qKT4{qS~$GoR=KPmOIhml}KLo&8H_FQ$HubPSVRC|q?Oo=pW89-h-ay#=H# zK@kh0d!sq3y-Rky1Bj87Vy4C+P;;)8cb8<3?325e92%Rl4PTb(_|r2kx6(jZ^+Cmp zp=qaMbXn0Z`{r8?5_xYxNor{gDy)+|>=J_bz0X`}$CHUX-DcXB$)2_t(oc~&n)2-mEPB^!HFgk-MfLcmKs))e*b%xPJ5Kc-oC@S;YTd~SeR zXbG%t1!^8P6c1$#o|N!Wl}pTr^hTodhqxrC8C2cveK18_x1f;I}nC(1C3PbjST?wP8QhrD8OV_}69oGtJfcIrJM z=VBMNrU~&clNEfvPW@q($(sL(vU@_30__!F)_R3rZrH%+i=wIcqR)GpUK+`%gS@sX zu=9yLJ)?r3;mx5f#Tw~z?}9qYA-I=l14obqA@*DujZL`8<>|0tfTWzBiQBy((!s<9 z)%DgSv>iRSz!WPgl@pcVd0md^b&@&dsugvQo7V|whKQB@%5Q6SylrtclAXi&A^Z|M zUKuyf*X30D-X6|W3at>;O{PaH5{6nF#;2NeYQj$L$*jxM)oIR$rNrh+?{96`&_k0` zYID1d+GY&hBisTRF|jbd0!>8H4QGYS84G0YAC?RwlSsR%6b%ufu;%${=Xyz8E}PcC zvHb}e!@hQT#L9iW;-yA`6x-A424Nn4boCkTABP8}@mz`T?&M}6+=5|4dbs3#A1IF1 zz8hz5!j+1f9j~Gz4RpPS9Lk1rv;!yec3C7(7&&UyQjI>(Yd2Sx_fCjEEHYX#bzO|~ zOYD(2j&Is89=5ldzU&L1^@rO&%jd%4YhqQzJ8qP{vxSC!dSZdPHaP6T8 z3TU?nQ9GZA(i}dh%*C%=n8p!VPY!Lz>fbr0z+G75&-5CmkE$pJ&_76U1LZp`6 z@-_ECw)!v)=vZw(hB%p>V=I`DYM!#IsxY+E53wlPYcq^yjRX23A zibvGV0`p}U6gei|{*B8!!J8TlAwwUIyz0W_UdiUKT#H=}UGg(=h-5W}L$!vELXw21 z&nQ}&vF=Nr?>HhMH@UU?y?5xE)sgoG3xfN&NO~bX{VNWve4=ZqJlqDn6Z521q8nzn z0NOKPW=c$0f(32vYGNPR2KwcM9BbWSz_uIGFPPD+`UkU2Jf9v@W2TRK1m7^pNorxl z&xTG<`i(>^QbP^7Z5>PLH-vsaDh4+4&Fn##3GG{Q!}s7UlIzcheN623%5aww?iN4S z^f3zSDA><iCuIO;K$*XO@7!!%=+`FPf)jm8DoT8DRwh1; z>5fj-7fm?$fpHO?(V8jFNwvehbn*EO#1 zsC?NF=M7bGvpMdZ*)V#31hj!bzVGN_96+eZF8nIi;~Uwm28CHCZviGQ&+`-8?b%pk zEea*fjFg6=3V68A+8a2)xP6z|Vp1Qoac~{Y2|FqHI2}zb*avzR>mOx4o|sLT>BMzv zy-br6BsZ%a?9_3MS*TK+Ee$#bzQbYSwX$cAP4qFL+NH8=9b%yAlN`~@_y7JduNVY+ z(z+)KU&gacfhaQ!);2d-+0(5XinroOjQyj_(%WB9oais)?aopNF?_Fi*11 zm(a8>_yy}&vf(-XMtVgsP6*Tq1_~U5Wn7L&C*39_@=5d)!>Zh{!$aItBH>-zE zT&jxUXa-C(uFI!V19;2Zxh0fsXq=vy2!rEF*@>#lH05*>`X^>R9p+15JO;4_Q@cUU zfix?FBI;9oh-a~3xF)=*zXg8f?Mr*u#dV{c6w868I61i2^^GlYHhf><$2<2Fplx>E z8Ay!ULwvrbLh4~S$H|FQ{sc-N2wN$2Sfkk32g^U6V z#m(m`%5sr)B{m`W4PTuDY&ewisn}jkPcg93z{%*Mdo;DcI~?m7;a*gzo-*C1V&hvzZ8>Ta8pvk&5`8(q z&eA{ga$R?LN=TJMmG#>}SrFI8L}7>IrdmOtggmij%@M{Pz%DL$%hiF5D)s(A{_)s^ z38#W-e{RFW@2fnsUsbx|+U4Q8B7;1~q;m)Roii$i4*sHgwUb~xN0nUG!Jpf#qBBlb zj;Yq?Mw*J0$VZt3bTQ66dA!waa5T7dz?eqB39-YVPD*jawC(G-mVMrBuRE&atJl0? zo{E0!plWQ?6N+Hy*V^EZ<2O z!Q4OC(lK%k?e(WuRpBWP#Bl1KwJM4S<7eBcOprmX-p>@fW(_)h zcc#R>Qw>6_G@Z}wf-6B2{>zJan(ruH=)I7q8H=nn)YGed@%8cJh4=5i&Ph~7mKvm~ z4@;5jVGL@+v>bSB7lN4fD=yynxBxo1#EHmxrITtdl08zwd~$Kit0Nvt?>OR+8jZb= z5f{~de)LS2553BOo%0p|Rn%fF0jGKJ#kO7=IX%TWd?Qm$>ypS7A+29TIy7F)noCE< z+%@>VbvV7Hjc!vnrI#bw#Lq^9lZ$+nUa{M+S-ea`a``g%_-*YzcTnYm%gCFPgG#c@ zpDG=1Ef%Z{BdqT%wse1@Rku(S+r-fvd-_3Ypq2Ek`imVnJ2PE){sdAX-%zW>taj!D z&E_|x9^nJ`|&Bi0O3j@;(Nybg;7&L=orA-03jpO~br=DC*R9>T&7 zR1otVt8PE|3g|OwH(g5bYdX?%ztj|$-^<`$T%x!cp5f$NMIP*!JLuF%8$Xy8$LoyS zeyD9VN1^4)yx&=;NtHo=I{N+od135+)M=b)*LCl7z?>ycbGUSzEZN>wsj_ShlaA(3 zi3T92CSE?C!y@2n^WhGS36bHjU#*JX!jbr1jVOKK!`WDcALAH)LLO>%;DVR$C;Q(4 zPicV>^xhK3pFhmm$u~xZJAFF;neuXs-7`$ucU*_J$fpMJwl!%gx*PSdv9>T4Fbl>C zO^JM7T~!ky-f;`S>X@ZeBHsE!ckJ0d2dl$~Op& zOL-hT*z#p5JhRTmKBkTSMZO+yR=T``q{)QKj%u%E5f6VviY=xr#wziGQigU?EZK$8 zRsLb_Rc>fKWMd$GuYi0}Rkj2#2xsohh|*HA)gnh}=8Dg!>5U+)P{x60{LAN(^4Y+B z3pKk<@@M4xjJ_(8%^Oc#Jp=g5iNps|B6Ash8w}u9;XgFrRTeydZaJrtCQa1vWrn}@ zMp%R7<6BWEtSF1JMTj8cLnM@;UCr{L$lZ>H#HUeVJ_lj}Uzn@==pus`m?HItUah)+ z6^gZxyF|`MoJKvNSKDeJ+gA^EpA=3>b>-FU9nB_qIF1Q(MYf_&8$w@ncpkWk&VdUJ z#=E0s=%}N0yPoc4iJci*(5WZ%>LdvOzq<1KSC8YsC$K*8PJ>r(0W1|Xk7KjR13s{G z(Z^9;y12ElB;5i;N@r~ntpwJr5=UmAvkRolb7nsjk0LhrUdPDAZb~vBQt`~ReklHm zYp>SrnU{hKV9Afgjj2>Fa;g6@onXaYh0*@BwULIqgN0WgGv*}46^yX^MM2r4uImp& zisRM1y=2A@zBivr6NW1LhLNlqSWcTcq)cZ$T;l!O^k8Nj=8|7}Wqa-J0ZYY^1x+X_ z23cpKS$Fj98xHyGby6mtWHz?k3)X+BN!5IpI6Cf*7ML?IY`FQ#N&v|Og>%5qX9spG zV;6_M>6dDnLuEb}QI5Q2y{g1x#C~2dajf@z$GME@dE4?F8LwB=dQ0XHOtRR7sAR7>%hH2$i3KpDbR6rmf8Z> z-YY^^aq;Y}20Mc=ZL111jd$szFo{b_kI#^sBCNzZO zyv_hqu5tsV4UAa6cJB?;Dl~OGqa$Ts7N7?BU2|93U>ReDlB~P7bmTw8T@XIOZlan% zHuBRTE5Ac&KD{hcRkDneyL8Q542rd|?8;HT*}U4k*`_j%xwKoo=Cj@Ev_C7j5$vv8 zkl4lDIW-L~Mpgs2h!@4USQ%&H)voO#owQFsqGZdrCHB}<*bquVBeXkY4O+$WE`3f) z_9sr-UE3dDJ+ae}^JVhoYW($@*Ay9&Ad%OxbtnY%x$tC_9+ivYG%_uS>G{Stx~6dF ze9+W%EPVnxj!!7Qz|^RD5665&<{iQl=-&5$I!-Ep08%ZeO{C#j$WMJH0_A^{@<21sXT0VIN&~9ch5ga{LxPMgE8i>)6u+iT6+WhAiY)HxC7Nt%o=`375R2(bj`cEELi5Fr!Pute04hoN}KxPM1r@uHDlBlRJZY z!sG(iq0}dq~sUM9Jqz;dKt1kpE{E>8d?v1M$Yeg=ygk*HF^bkmCBN=%{Pv}ZcX{IUiyJS zFVeOwL=(rYxPtnfi9HdD&?vgNbZSCGIV?>^JEh#BSczau$T+xG7GR_zh>Zh=BxvsWz6T z5xDAFT_F*g67YrHhMKggBVxTzHVwX2HFGu<=Wz4cC*MzO%(9gf=!XKl$RhqBn+gW;U%${m*#T$+>&@@7+^4SdgZ3v%dM*nsz*9 zHO=p76n8g^*s^#wjl{V^Q@cIjD}{WI>0-9{mnE2wj(cl$ToA4zaAeXfCyC+o+EO~d zCSdTw!-TK}m?|==q5neyX=TRuWogZnDF11$uG(zWo5OGC%)LTKqEsVZ*MfG>;|j0drC!IW$qTa4e6xlNa=8+< z#=>menP-%^*EflVA+J8HTPpF^%xC7HS?3IcLW@tNAJW+kUYb4Y8i+Xj=#~Q-va0t)2v{Jb$Y-cvn+4It7r{72hClllq}*o zty*7=#Cvs?CBR1t6~dRnkG)3eiQ#kg$zzjhElFQR4W2o#3BkOojl~WtAIy0>i?$Do z3xv}=l$@Q@*8~=}8qU$~3Nn0bV0$e!fNk{5%SEMe@$1yvl2#MZk>k^yX{~KfFGF2m%v3QOITLI{uQ5Kk#Cr1rZ z70n6d&AW*uFAHL4n4IIJ2)`I;?1&wxtV7hB=k#{=)-|^e)Yc)V597kVnTB~rWZg$P z9FHA;@{ehaBI>h^xMQ19`5t7-4#`eypaqqO`>`p}kH6TBnImC--??$kJ=(@xEp?%_ z4RoF0dKuo@kxPB3E-Uy=XH0F5sUX|~aOEnR`JS>h;^niZ9?J>#1C8>?wH|}9Mbq-6 z0AUqU>QSl-e}mjhg@BdFe1`?!xp8E7hW~jk7eORb53iVGh3aR3zhf74T9t=P!cj%DoFG28-OMJK=470b*K zykjJ$aXeU2Vb9;2lxkg8W!dvvf4KNb^mRf*6Ah31odOvT*QvP0EFGJw?1Ipg-OK~_vhnx> z<{W&k#W#j!Mf%ayt2M$$9|Pk!@HRcfPERVXj@;fPRh8g>4S~>qHVaV=A8R1Z12DVk znDB^4xqV=Gn*5GuXv-=Qf_|4MO^#8lmuvOea3G$SEuMh#*O`k@^81{~rbv%qJQizc zR%(xoNNNv!0AgLHU+0!lT<1pMVGMf4Y1NpR3tBat#=P@LHKiP6l~|yQe<#5UJ2Rm^ zYD_-#zHiwu$op}-7l8n}h$CorEJ*+h+H{Y)p*2T6n695~<#1OEj#j&gkH^UT_y|Vx}wyO zjgiU69pi-777MbzSwU$+zmBeidn(sL#@LO}SUzKOJn5Y!^#Qa3pg!|mbuHl(`25%@ zX_74BwA9}8%XuSY$eJBHM&)beRS}bKsO@$15?qDuHab?^vJy23`p5NZDzZ{}En@|P zUsm2a(VVb_%=*gnt$U&Uq%0jLK(E5LD{w>FXNH|ACu3ouUsJ>1MveJ0y3C3cWs!)( z0=}oRq-3L$cG7gVx2%=SH?o;4#p8^XhSHm;kS*4cvdkfxX(nml@4C`flw2KpQgYiJ zuU%)TCU*{&!c}I!E~dcViC)fF8Ea#)6MGQrO0$iSe6^!{nFy=sGE_x3r=1bsf>5g> zA1(eG)=m3n{*t`E8dPJ!9#dWeTGXY2Dj8x9M+4$CKX8|{33zHfm#p^;_J%gCd&YZa z5Z#UXlT~&}?2 zf%IgADJ6PU$K9WL|E0W8Gf$ixy9u~h=ip>-&I?3l#4nbqOgg~sc~IxC7W0c zDwW1r9#TYFb3Ze12A)6gD9%jNqv%Fl#wldkH#hv$*SgS`+Ps>KKJ)OTgGX1JR59=J zH6q^y{LL-*ABfl6vqy1?DgOX2#B`%VN8#+w#sYWtvWw_gf8 z8)(z+H1!eZP8E_}Pa%8nrL9|Ni$lf@$k(M0+(CV?%vBx3*O_SeqULCEgL5y5q* zN2&FHvv6dwi(}?epyHG9-ujXUoltSs$R8$1J90_n z7n|czvW(eD8-dkdWnR*jGCjugQ=(shC_r(Hda;0&1>AI_)#U|*$KKMNzTrl5PTU5W za%4n*9T!mmkB9-To^|aX2l)T36$)or!5n;(6B0SQJ+ zDDgXJ!@@T$TlIbwtblRA@HOYsgJjUeV8>~0#1oY6uKGo0sTLXSjNSHdoVjoTIwzBphdRL__1G2HWwcH zgXoB;wk(!HYys=}jZQPkCmba@S1T()RupsZSxInlc(smHm|n#Sn26^uNRS(7f=%;hF$_)JV%Bimaa6 zuigA8UO`k1{U7kuGBxe9!JqJoJS@l$XxGsnx@$epsh$z~n(R-N0kT74-tBBirSi!d z7O(oK$djH-DWFEbvV|*KCvQUn8Wm$|&m`ASYa^6JN_uK)3RqnNczs6+Wc4Ed0Cj8L z2&2$nVtl`CHZCX|xEc#oQ%u`p!Hq{#?4|M-+Z0P65Nri)85m)k`ak0QDT7`H#hdx* z?5s^DN=qpBrDJn*?5$4K=nvawjx-IdD3Fd#N;os=e~a*_FYX`O~$os&{^MP}$g{>L zx}yy@R`F#NsOf78M{nc%H%Hw|pvJ^#x<1=@k=GdBb&Bo;6uE$N)@-KhHnOc;hSQRk zA3<8Ur>Xhm@{UmYfbEC-gB2~<@!S$p4%JwYph+gWABuAeTJ)b{=a8Xo>*067@nfCw zd6>++$ZA4@no$H4pY5-wc+OfDRg`#7EyT{N8fA7d4tjuWI(S;1TZ;u}*s<8eC|H#K z6gh2057(v8LR=uzO581Rtt}nAv8Sk_isLVK%D~VLm3yrECROZ11Rg`jUy7{!*;?a( zL-H-94q3^(;27iOK%H+>OA90$%NVUD;FEG_%FINJmQXeJNjeJ(Wn2T>V+SLhZZxg? z#&|Guk^1RkO=4zBI2gE&sp;*h3OKQ?X-ScBV+TAbteY7;m0zvF=|fg5hVVlbepORx z{!+cJqc?mgvbzLO$;}n^3e|RWGRnG$Edu+`LDW+b1>u1jCDOqW)#-0=js*6n%9)I? zErU-_-$C~c_^~pJ9Y?z2rzx6^Mx_9aJ|A^03q#vpP=IU`qV9YJsP0__V-BK;9S`AA z_X1!q1+R>JUL*EWR$YMAcPu<=%V8Eaw~bZoPp(UvqKh|+&RKm3SO;H)RaLWNz{L-# z)VyD7ky}Yu#6`ibUuop%scj;_j^ZN$ay$tpfe7Lx+so=-ouTKrt~mS8N?HrnCA*%2 zz<%22ISo14EMVz0He*`OO9z(yQ%~cwn0pnJ(UO%r4&)_C8rw=&Tj4Ult}q*G*e;c3W8hBXCy%8uIsrFHyQ!>iU^@^bs%CzZXH3aUSFG74EKe1hS+_3K>+ z?QmU+waCf7r%iTC>M1-kD;cbvzFTlBdcf`QzgplvS@DVNLHPGO9uu5}PZBiAB1c#)zIwa%4_E*MUItXnlGBT2^e7}>b;YS#>yw|4sAXgd&HitXg*8pQ;7Ce} zZU{PqrC&>mn}C$};F&0a&Y~!!w%}4vL+tx$9gZcMuW**d<}hTbEe8hWX@3j*>ZfOj zn)C}s+&r)%{%M6t2|pTXAKUCK7Q{kvC$QtlSWUuu(%Txi99MZP7S0vqNMSn@ZKAo` zsD4K$$yVbop@&$x)ZBZi8)+-6w25Z0Ovr6lY+ql-sy4M!M?=Q)l7zdQ!Q2iB9roQQ zWEEJQz8d?gqB|0)U_RjA{Fhhi-izD={Z+*`_@ zrcWha%1DLFZsh6GueI^es=99&13Jm__f@Z!3fmB*W`bNowu57+^P)|(yP8s2>RM3| z#zcRSjQTR(F1aG5%klI!>xn`xl#&6{TE5rXcqw)?{{S{f z9PAnR$VQcAue8ehH3)RPLy1zZEGpW)+=$tzM&!WSY!PdC)75RXCp}<8u=TaCZ)I6W zWr@y&H2Fda_-bnQg?`-7!B4bRyk9{pV#NMUU%7fcY+c7JeB2$@x_WgqtaIcsV(33& z60dtztwnPm2sQ-Tv9>2LlxR!SO$B6U=wp{mk|+f0l+uDLae?AxETn*b+G#-=Y%3A{>hag5Nl6N|)RaMV zG@Frni<)5lwoI|P_hM}bbfe7pP~{XK!Lnr+xlNGu~;0GJXF5kCKk0e7I%hjYv+K>8&vyJsS5>P)z5qNquTT zAve)l-(Ix+G(~q4osEp!4+y66b_X07c(Gb-DnKL)+=4n)eC9T! zX`SOSa?LEpDJ%U}@dwI~jn5%0f@LSkyI7+c=TgDn*LXQKdfG zlG##j3T$;fb*3-dULqsMeS;w-%xo%!3AABDhc3{6$t=p1?xc}7n3_j?$)p$3|KHp zxUuunh3>O{EF&I1>jIPKds5Sz$kIgnjwG=92}bkh?xe3-L$y3JsJMGjC$^SdKqVk` z>8(`TXNYpccecWFE;__wpfmsvKW(T_Z6(lS#Hcy1YL$;+PWmYdw}ze<@}}w9ZS(^t zgPZbLu^HtwMeU$AG~^|oJ_ z@YPkUv!WeIBQ4mIf3m1lPHdyn4ilCbevy4O5Jw23-whzXH(U-;JsH*cd25vhiSu<{*7sAYzBstYa$Nff{~&I?;%_J<6maL@-Yfc&clCo z32!jpYbEFjdmKSJbn&L1*N`<|;5A$?G47z%cTl#|lsjV_NdO>|yk zsDo?Xsy264ONzAK2~;dPz(E%tcBomqlomP-61HHh04;j1dqb;9i>Dl=^+(xDJ(M|C ztH82xDJN8GLVI`#_SqI|43FIDQJ&sFt#Ffg#;gfQrJloVZ`$Y&pN&dVNKS&AuJ$E5 zh+NovO01rpOP1rW7h(d>L>tl; zY~Zklx?yHuF4t0RLHnS~P>}z>>q=}%WI5xQ6#TkNMIvQ^zv$g4uv){l@*vBO0{M?pc9*@~ncgbF~pu|73l zDPXjzbSL<8Wj1eedeS=+${N?J1=GgW@is49?%VEM>i0SyiM;S}ppJ&ut~YLdPPbC5 zqoJ|b^}2*F)43phwaaYo;o#}@wH7i?@mQ%vZJ{VX5dy$zNNn$7IW1$W)bGB)a22Ht z&X&khlt4wtp1w8b{EpMM&%k?T)vrru(M$kqA03n-At4|RgTqR<)3e1R#<^!JrrRtC z+=e_>M&;c@!_K-beYB2B{vviup#ChJj#v*x9-0@>`2$z_Dt2}g*ruI+{a8YDaf5_j zlHhejTzTH!+PA-2RmZP!Jxjjo&lvPMtCRN)Z#?{V8y7p0NjC!ec~@!i9tw9!Sw4v9 zdCM!&r@?iTDF>H#R^_#Dl1G(&HK#m5A3AQP=KlbwvtZdeQ*DXHFC)g#tLx*^t6ch) z@PZtMiM7tYRNuNP>gqd6EN_2xMpkFDGAZNxM?tQHnx=NVmeGp9;Iw@I0170#g2NX} zXP_3Nrj2+raNnJ5)&1rJXId{@RRoG7{PnHVxb1+iEhy z0o8OKhEs3L?xK0&5|k1~63DmZrTxZ@x{$I_Vh+oDXb#hKC**DN{{Z3qXwZF0x(t*k zToItGmHkY0biuBkR7ZOS?HD$(xVZ75&ewx+rz~9&l&e@;{U{UU)KF;Y$-E4C4sVQc zJb&{JMV4wRY)D`br%k+)4OQLpG@cQAU&q{*UqhEUFVyZb9FIqhVv_R+Ch}w}RoL4= zo|L3^ZJY9JadGw5urS2^StLpaDaND7VL)y|6r`vhFl$oUx9V;>6td^zUr~Lp&Bt{) zmY=~)ALQ9St7^ZrSdX@Xxtk%}BjIBE6PG)s+Ncl}kaIqn#HQ>-Ks(C1*pCVe<|QO* zt_{WIkd8av30Z8y%qAN7600)FNGh_AU23;t>t9mln&P?^?rK~~@nXJ&fcmv?r$N%X zKacIt9ZpZ=dazO>wywH2WaKg>>3@oY?uYipJn5>%D>tAMFetX0XR9M@k(e{+kKL{N z@*2UtiTyXAIh#np1nF;hpxrp7)NV1$599DMvI{~QZ}S@yVn7wg?tF^4WxoA)`kaW4~vz(w?5P*s{+swGQ0Y0!#4$Ky9AIj-aW&)e%N{Ysy;jANdb z^tleD))+sOD?oJAnyL9+w&xK#4hvR2*oV{?d@=ZVr;lKyAEahfd^o!}o>NAGp zW5mT~9{yV`NlSayY!rHGe5>cbn0zN{7dck56V!5;=cAHg}k0IwYi0pC7Fl^^sz`O3*H}+R;RlO0(Y*EyON@)CATCFOxC3NZNM`fV+ z_;^w2o?8WE0UOdZy-&-g1PzkWcTugnR`T#q3j*d5{n{jFk^(M4H~h7?ByS88CNNIx zCZp~nSOGf6=D47%zYzWi9PD%DuZ24tu}MH;>nE+g+G;Yuw5hdU7K}0_>HBDAj|5v#e+tZ+!MajO19#zAY$(e>6q|JGLY<2N zYlF!xgQy9~F6mRR*=nZ}ymA)pg|MXHN&q_A0Q$1*yziZr&Px4au94~ySi#?B6o z5XJ)e+^S-*r6pSEJ-4Uyb>lRJwze8&Z6}aRUf|S~HG6`PP3>JJ9BqBdTeoJ`0Y@Pr z)BHzD_K(a-Cao1%5V}pYd6}-QSG}#JL5snMVNG#M7d>fBmX-%;ZzR?MDkwXE^Q87? zg2t%jmk$h@&N6gaZ{5b3Ae7$2O5``;)z z5O3Ctd?8s1Wjh`YgW+&yy8$mtaYU%u3Gf!w^{O4Mg`S;@s;a@eiVz` z{nZ+AHI^lAV)`3F$mW`ml)T|K(MIRbr4st8aqMLZ*DmGt#7gD1km~*;*0^3_CD`q> zEovu1arkYs87kaKBgVMfBbglMFS6x45<&*c{y%LebvY?h>I9nm4O}>}Ra;R_buBe& zl)DwSW+Z8)&8iA2({Gh?TU55r(~a1jSjk0l?%ze6!^&Gf83y`z_*PYH;LhV$la0L@ z!hOQv4#0Wk`Zh+OSX#Q?FObVXw>6VTA@ZI+;42}!0ek6c^i3Vi;Ny3x$3lkBV)g}# zXlta=woIYgiosT8V3MWjU2e4A3JYMeL8aq~Dzb0vtEyzmo2gk~WkFCR6w&LqsI2RF zHAF=xps^LHT->{xg(;!>8F#I%ZkD6i1y|Hn*s6BLTAvbYe(5RH@&u8LO1BM{J``no zNM2O7L}y_)gYb3msZp}>Ep=@WsrUkgTq5Mv?RJ&(tOqL%w*LSHs@A)j>LG0@AVav% zO#WNnud<(36_&1}H!;N9Pdc-vc^7|}Nf^ROzP?n}ky{L(VpTXCKMTER69Z3zl&?WDRC26rmCPdWTc zf^T~3btv#i=I_7AgS_>$Jv{2iJp>wKCB2~(B_>2e!>u(NvO%nw^}oa_1n1P;JS;T5 zNj3|~3!&>=-t}OhuqP(i@wW>6bg0HT2E#EyR+c7<=8&YBB#si=Ma`AxQ;n^Glo}ri zTT(^qLEAn2#>1`Xe%m4LF}m^#AfznV({NAJ3zGB^GK#Hn)1@f3ZmA=F)6kkyTTuh;w9ekd zaECAun_9w)t&fCjYunDNzT?V69?wEMGnnM@@=Iamq@zG@(u*Bv%efCW9OIqc&0}&5 zI{M6rzpNc}`)NzK`1%b-{+&*tjVNDGDu-iT#q=Dm-h|37HXvAe zrok%K^ch&%z`{s8=lLp6iiL$H`Z7lx_kRzb$z&G;7Q>5Vf?i&>u@=(R&}rO?-Iz7h z-9S_?^jrk!c4{MNJ_j4w%=W9 z0mlKc=RknUS!1*_aF7yB`cP#*sA;AajgBhRCf;KJgK>Vk{k729)360M z(1)#%Y)6e{HaRY#Wdw%HX(!$*E_fn_MWBss*R2ISpo!pQ>qAm{o`!)pgH7dvq%xvT zooWj5ajz(3sN1|qx3ZQ~%MF3k5r*J4nqO+%7W$Q}60CeQI8XrB#;LEr^7St%r>QAp z3i{GihRNw`3Z%PnBH9{mNPNyQ)c$1cx3JO+Z7||hqdWx2N}i{ zfoiMSy9I@Mt+2}+cX<4_`>Lkh=8JuO1%QG$*5PvB*;Lcz8hy*U_=M5tc$zJvdx-W{ zfq<2`(|kf*imUsE~tiD}T<7E=7cfqe}xPFst~ATD_}Z zcleO|Cy->!Spfe43$L=W8`&9_nHZ6c{MJQS)3K7WTGCIb&-q^E1nt+xwzm*pwn3=4 zBD)=@-AP@^VT@JB=c*+z@OtWwz7&SW*wuP$mPNzuLVgQ=G^^x#4^yVtRXCG-1Z!36 zed97`l))3wYD?+NIP_Zoe0TF*6WXxTQA))x}pNiVG;nGySyU?gBMe*;)5J zNqwG4P8*5Aj7zx}gK0>*Q*bZ$(=*zXmq4}YDD`7Jix3)+L{W4jKy7LQQfsKauMA*7 zzr)kW(oW1v1&%*1O?KRa<>6M|eG1nerGv-$BwSQ=AjS)EcDl4ZJ@s2HlNB5+fHPvlIl5a9d)hy=CB)v(F71TRW8ll zOByxSGEMOaPw>r+uRylskli#qmd74ay;i~%eMk0JGs#?C=vCC}js*-prcLa%dH7eH za=Vg>ZJqxB6{{IDws;>1mzQZ4Z0$B65Kf-WE6uq+ei4=vJB?l`HTjN)Y zlzvZ)zWm_ZE8Le2!2{3Ey%&r5 zQmVD;%HCV!lGvAigDkHJ$z$0Mr6uP708vQ2tI+sAm)C5t`W|`B{5EyRHT5FMN#AQ& zpxD=;tNi+-&s}aZ@ngpS099cX20m>aE;`b3yU=Mtq1U9H02=8~s+cE|c4eW$5x2(o zr={2g78vXz#2fUgDL0bPQ(D)PFkJ&c2sfr{lfW6SqKRye)1^J`ykLZ2vMcG2Z{b<) zlJI6WE6Ri$2fZ&H#+LFx)MP8^ug~^Y1@_RWNjec~)qYtEHkuXMt6ORNs(xKCKvgKX z*H2|5Y!hP;vj|hrzPkO?4|yknheDFcucUpybub|);fs1|{pw%d8Oe`LULv6>V8d@q z%-Gbdw9mFYTklv_Xc}LzMo5- ze5iJ87^)olYBUdXA(Tu6D*B6iYid|EgUa|#y4HQci5{SPqgXa-AyZ3`;=Lvt7j@NW zl!4}H9iBx&4%5fGA40UxcNY_jb!=jmiIQ5;YEc_H^&WL!XD4K%(73OJt_!JJat`Y> z#x88h`68E_8)HXfPmKGjlFMwH>RVB%R98ca`L8g@@>wp-dG{PZAHxeI+ULrwwJVBQ z#zl90y$Wu0hrLA-P0xj0s}W5&u^5uw3LCBk^wxto7Kw1@R3#gM-R>c;y z|Z^g!DaK(U6{+Nj~+FESH-E&*ic@=Zw6OHqO#80#=>=o zOrhoPbPwVa=Upcq2*xKP%eOiI071v2{u5WQeF_I#J+L3rD6o+a44L%D?$q*UvS(@i z+K0G}VH<2dUxi2A4eg!{sQ6l=?lL|MThlfd>*Z0D@-pFrW>{0INCN66!8En^C`T+k zI{9b2k(I#qi-pMM9HGxuK_J9{4&BREsTcbz%YH%fY1x0V4(#myImANQH~v`nK3MW^ z>LJ~D5;D;+c)VmL-VP%k@1nIXI97o|>Hv^D==y&Ol$xEBdHNSSd%DTtN1`|fAPiL|G# zNm;NO(t4jAg?)y0`Ik4;u9@ynCdRO~vAK6*A=~c=Rfz<7)rG$v)|*n2%K;90`s0YQ z87DU?W2=5tg*^M3t<|e?m47m7+Knpfjv|WN_pGtzN0^+ef-k30OWRp0rX7osaV{lI ztf4~MjDsLd=g1;u>KeoBvQ-`8a%Zb!+{LRUvqu^xv_?zxU4;9#s54TZI}$G(=j?u4gU-z$%d(+?KX zl_zeb4wYFu-b)Qy&N=SRNN-$5&=Q0VRrptP@vV73%*u{#af-q@T6zkUwJOA@n)FIm z5ysinhstMgCup$orYf|qzNSUyFm8Stv8usaP!&;)&%<-)M^SR>Dea5Kh@z`n$HKPr z5r1ru7`sR1y{IXKz|H3Nn~^~L-ebNi04Yr;Q@2`wB29H8&&Rnsd+IT1k&{Fc%a zdl6%;QE%*IxzwW)I|mzqekL)ui+1=$$sWp}dogms&`(|X&2}ZFHVkD3o%~5{DZv7`iq zjSvm4eZs0_+IR}!`{W;rbu3Fhqk`m-t!~-KkG48_@Fj&dE(xaM@`*y+7N-!*2#zJU zQDmrUuC>YY6lbw{rLoftwqtUM2$0h)n=~!)U+}eZ+d3{I^U$hm@h0x_MwyBTb8?8+ z`)aFUze1Y67E+&LqUGJe$HFTPNdeo~BKlwNq3iI^W3oDpK0Xw~EUa4YiWqUlC0{fZ z`4e%c-A~`}_Lf6En&$j)Svbx^1CnE0d9o1dK=n{-kKf{FQ+W?nP7_W&iWj*UIFpd- zcO@zSsN9Wo_fT{o<1U9qb^id9sq@ddf6FmzkcBBBLgg1Ct=% zC3Oso$ic4UZP(l?(zz8ER10?s=k97c)y+jcsQ2=93w#t{AtrG|5%7EH`t7h=3j@VO zLjha#9(B;sPD(5GFh(*`lF=z_>DKjqt*%k+-_xKGb60&0y_IOS(kgG-Y;W^A-*&$0 znymrzk&osIMuB8o+fZ~w(~}21ms*pzeuLXg*9;3zO@QB;rQhP|Oh#P*IJEK&Ti3BB z{@RL>2H4oTe}0|MK}X!zaoSW)n(2BMm*pkl&`h{|1s5bu(7o5v)~1Ce+#te#P5#=$ zNH-EPjg9(NqjN&!iGY@pG`$f=9Y{5k%a^xA>*q{Y4uGbT8<~pM>K}a(!H^IV`g~WQ z;zleAwG z-B^b3asL2$yV(B#4QWCy3)S386)`{r*o$db+Ir2hs{0bj^Y4`MxZI1@$hz;#KKW9e zBEW7w8dpt4P9hSHdYs|Nr0sFgm%T(Dru58K4LZv5TcC|i$rMP*gJf1){{Wia-x{5U zfDfy}-F#?_(;|MUJ{PL8*CL}^R*9rPN=D&GSo)5t(B-BtslEIK4t8OUR|U|1mTjf4 zO1We#qOvMw%nT@6NAS%lE7;sMu!$5NKH}5|rfuMDL!Qzj4OZD^VEFk%jUP_>i;^f8?W_h` zZ0LMsZ;{0pT5OV(+W!D&g&|VQ8u0Nz2|U9uiB|UJEg)Q_y~e`lrK;AAC_cC=ubvTF z^NxQ%Hpy;Vm$1~^@2d@ZAs5gba@o!~GKbw#k5X?|GpyXBhk`uf5x1ta1v~?9BPTp6 zbL<@rmXTsl+A0cIFl?rheV%n)J9>@`oM{AVbn>O;EU}8BH&X~FO+AzgA5oaWWcR1( zs1QEdt=k^0;5h-sHMKz?kaeo7qM9vr?r6BUu3i!f8l2VJBo$XN$m_{)zN7NAHL7p`H-en&;&2Ofk?ymFFsMuMd~uu8pBLypCbHzw!Ks&=-zj_T^B zyAud_)Fh;Bxj%JKY;6~s+C+(*N^}7qg>!dobvim}!Lh?1Px5Qy_t7D?XWG2vZuSu? zvWrx*+-Z${r21oqhDf(c25Z63Wme29@f6%-N&V|qkU+w*~=8OTL39>?YrUk=~h~|cmSqKrKbf?vs zV)Kpx%WU7Lyj7)dp-*;Pj})Q3Hs5sy7i43_BrG^-i>bu~jW2R*%KlNlp$C)FxGg-% zGwokxduy@`7o?zEq!Qh=*1RtJ4ns+29!F&>U(oO4K|6^7d8czO0hT z=F|CIt>Lk{_?zfaLr5t>>0wrs!KH>V9)1mF_Y32=yX_s$wp3I!Bvr<|S}V6X$jh@M z9FLF4apbmz6r`);9u})@cZp=X;rVDNWsD=>kr_>?BFS%|_f?D7SsJ&K^H?VMd91X3 zC)5y~I@PYV>PIuk>q=c>6MjPl*4<%X?I4gms>-c~BULi-YDD3gnE4QWH58Ilst&bz zrMU6X<*wAVS@;x3Da1Dta@N4w%Y>)_{w-_hPloeq+7g#^d1uTxe{SD6tR_djA?Ns4 zZSR~$=42pi%`Q0 zr8{q}?^tS3$gahCe#*dp+ZOcGOZl+bHw~zsmiN&eoe4#C7fU4dx%X8|wplFIN4*pK zrkwW?Uh0-j{SRQQR-5+60h3{L-1*cUk~iRmQr}D14-rFh=o%4u6!gIGq@~{@@oNXZ zU4C@#MJOx*Uo4;9{0%|fA#Vh2^1tEzv^R4bj21;b00xO7kh5Y2gz_kBEn2Ie0Y`dEtH z#D_wxS8sK5ntKx2u`95R~=*mbA4)i%_dDfvq?(Fb3M5J1pZpz3U5Ck3%bQL5OokAV9rigq$1ZCDXY zE&lC43mO!KXxGplL++!-c{Hg;zK6T+p=1k-F|l4$$qWl6v-(HAwrwUjOEb;K7?*Vs@_VqJ_e^Qh>YWuFjLNHYY)bYkx9oUo~N?4l4Hgs zTn(nivVDV87mDr37wh3wuekCj_&cHIj?3ybs_n^T7TQax-2*j^$8aoq!3dSJrN{31 zn)02h+q(yKRM&<|rT%*Ej56-hqBXr9S`Ei6HCCr0#B)6#9m50D(ibvq)D}p%7P$K> zpN$RKg7>6jlZLszL-~a1E`fWf**}eXrx!|u5ySHKImoBywwoV@#2rOndGsl*i4&B8 zW!zg{m6SFuV(3Stv%gKXaT(zi_QxRq0FvBqYno=edI0vvtVBvWqUP7A*=jS|7jOZ# z)SN8pZ`%S&=}oC*grWmbzTxkw1GBI-ZE3Ef&Wrv~34)z(Q>VI!MW9=ZgKKC!Xt0-2 zX)c2|7&_co(?0A;`)nBVb{&1yl}o5AD+E3^o}TK~V9#eG0h zKQaFRj|#*oC}e20ZMK)a8n6is@9m-BkAUvfPjrx?I{lO)b$1o4CG8nVxdXt~5R`Dm zlG(Vh`)f`~5lMwvh3XJ(YOk`CV=1vi#N-roSl`(zza2Xz2LOUZ-)(;>l}|wfsMcY5cthO6BU>{O3z$SVu8( z%n51dh->m4bke(yJ7=-TaupP;=!Lge@bop;>e|`JO?3x2J_Tt{q6kH>cZ*TuPzlEY zRvQ=V<5kj+QQD=39*B|$OWM}+soIIgStmHMwFIOO&Es6g?0id&c(VB7IHp|m%YY;V z-D<8okekWVY1f0TI36dAGL(G!nt|BiBmj_U!1bavrx)xuq3smV%5 zFUb75N;pRxa*lP7%{=qcBzmP}n-Y9$(e*eA>0J@VU(4A(<#QrwaIWea_*(RLrn{L9 zw)z9qg1dDeWeAmT$lWlvi-Fd%$t1c3ibmrcRFUxNYMxeg9j$pA3_<j%j=5bacnw?$Z*|pcbr!3gJ$w~=J48n(H2g^l()w3Jt6xK%+Od#% zPVQ2V=GU!Ts9>a&n`1Xz+7vBLMp+ZGV8t%Pk1raL7|$Tr`=gYe-b5Tzp4@vK&K=mO|=3!hD^ZPU)0f=q;v(b+)QU$3^Cj6sZ7 zEkd_lt*~=gLkDILI&m6uM$Nug>?6XV?g9$N1xf^-v>2swj zGEDIicP$F@H#7?y$aNKir8{wrkf)h3?i{$awDIz!HZ~LPN~+H3;291}3HI1=N^!N3 z*5^-Wg;rk}`V)$0TEi-FhRV}r5HvpOrFkSi*q;$WbLvUUCOrgsQGA)K3WTLS5(P&& z81YO^#aNWv?V%}kF+{IBg_?H>gnjs{2zVsz9ct2usdp8ZW99zQxu9x$!myWB%rP%6m1j$YWkX$=YQCBW20`YGIBOxG?qDk9PtGLAb5t(ma@(Yt; zN9+nO05{oiS!D-H-Ch*jr;{Zd5jk#D4R~sBqM< z2yFW*k7s2r$3d;F}d(@wm0^ECcn#6e@E=7`|NAjLx!*kc-S-#w$z=x zgZc-rNik*c*AxcUw*%6^(lX?*F%CW?V=2I1P*S#7c#9g9e5ncX{{Vpio5Ix5{{Sk( z&ed&4DpyZOs5EhPq?76N_EhJ|<%h3|${oeR6kFg1{#7e?aBht*q>dgSl_TJ_s9n6u(2+q(43Y)BoIWWjxeYC}QA$das1vWyrns?q6@e?hB<5E@-lrk&w$^GM7_f309N6`4~ZG7+StoKaz zk+;kH>v5h)`$nHAkKL_%rh6uzD6cQVkME;i$*N(weMh^c0o+P8bsweyI#pNPKO*ni zUL)pl{{Rp+_fr>bVgl9C)H@OZ8kE&K+sN!{NS0%w?Ak(aN^EUrBO1}iQqALyB~G9g z{{S_BHO_K-3M(b|A4`$-C+R!2FXtX(FCZ4B)a|ranOkJClDF-SN^E-h3)QU&qp;S|kB(WpJeFHA zy)eq0b$VP}ntB)b_W^GiT&0rR;}B=L5xElg4d4_Doix2#QM1>f!*g48Dfq-glro@% zSZ}Xs_$KsINb@h4>wA=CA_|t3aI1Zl?J<&Q@%v)X;5KeG{3~MkX5*;$*3Aw_TAOAr zM!YE^on1}vg7ijV9_`DZ&lK7COE~O^be8ys>NH- zu5|S``E7qTzUqgQCXu(xk1(#&Ikw@*K&0gHaHr zIyYN!w?l1BdG_xiJ-gH*dQTCf4SF2q*!WN04)A1ok0G44rLXyLT7w9={Gi*;y-R`O z6%bhf3_$b0?r?nn0448F^7bf^l~UKZlBIik*I5X!5{sv(MH3xaNl5AKpjlv4i!+Kw zmo`*pnNi%b3923GsN}TNp~<7@him&{mfB)V?0W9HfwY0*ebwi@ryfvTvxa#{3fe zrL`9u9xzG}fRvxA0wl@-zZPNZUi}^eTLC@lxj-Z!NQfZob zxX-ZD<1i^37~}7ymWRZmE#*<(<<)V^8>2}@Hp@xq%;7O!B6M7^tj0N9bmQOlc6=y>helmic5I+ z611AwN|k=5uj8a2$_i9GYAuaII3#%ZCWlg3EampdHdaFvcD(qbVi~Po(PXcx(I7EHI_WNjan?(Fk zl{M*tEz}x{x9Vex20Xunrr&KIr;&L>07))*^tVAreMrZ<0z7ZH({NZq*F2$cxQc+6)*#l3qovAMoOEQ=eK=@Usk6Md=oSU!mfn{|c z@!?I#SPLWa3MGAoCEO7p+}p_MN2<>zZuq1NSLQXj_Y^TvHitD9%}N;;X``VUk~&h& zlN_bD3D8^HS~Y><5JydK?CDvNoCYGoDj<<@NLykUaY(`oD#JJglCL6+aL#lSjRtZbK(Dxn-OpC#qduy|im zI{i0LlYP1orkdWVwfNU zKYNj!o-%X9*>9|@mEPq2mD1L*x+02FasasE%1zaC=Ua5A+{nu!ZJ?JeyIlqS^sT5Z zR`m@>jHM72t?HX(xTrYJKgb^nN^T?VfDj-aWO6<*p3wS|5qO}FF*{C7f*xs5w(r6q)8?!bWgi;pXr?LjC@sEBhs%rY>#`z$2lfL9+)f^!sVRQjSA087tb8(zh9O7nN^0B+%Pwgd~sv@x4Vy z`_17Y%d@tFw!jYxBx1nG6zu)ixxKnmH7s`nG}t}qgxcO6Dt_^&1%Z*2r5|N5n3IcU zh5LT5!m%?`h5~Vo)qioRdznO)eKJTRtc{>+<|{zL$Rzwde5+!i_&E8G{6V5of6ORe z=yh3Q;o&$7V4#b2(9|VxPZo8*XYsE{og5a5nZ1cfJw;G%c$RT7i%P<(6+%tY$&GE0Mf(4u+|2(xXT6&RCg$nD+wa3 z?iK;;)-pm8`DbcK+z9da)r}V59Fg)ry`i2xHbyW=^&Z@PKoxR7|ugXg(UBey9LTkSgwedRBRRNT+H*WjA zmCs3JzK$>wVFh4YtwF()NRdKp8+rNCcTb@8F~Kc`94f}%@$IVRHkj&)ktU6f3)}GM zXgANMLdRdJCWC?w1YIQGrB};fa=J;rKZh5KcR@S+dQubK!DCjIL(1*!*Ahf{#cNQu z(3_E^FZ-4|u+NWSd@pG^ESYRHjC+MHkdWz0fEV+vE@Cb$BIBhVr_xT({Xcw`J}Z+V zhg~5mDk&hOolSFFJezVf_2uexry*yCcJmkW8ToPIrIzny6ziq*9Ze~(3vHi5I}yS- z2b=ff5kEHf@+Q!z45<8lFKsVfZoeBOx|Nc*2Q7CWo{ZC~k?R7K>VyNJBCqvz{1Lf4 zoY$7*a{WbZUBa)>+SK}&QBM^6kq2{fNm)|Py7|%Tk}+c)0a9{KyKT!Rvm{h-GEB9r z^OH+9F<7{hf67Sntpv#MI!=J}r6k@);{^L2v8A#T#hA9Vqh!=g+H3QCUR0}PooFK5 zVt+26^`&P#jZ++kScXlkE$3AmGyF^KZE|CXfTtZoLUkwKT;|l*a@SUd+0uMFg$b=T zt=j2v<)v}Fp3_~9^NZA8D<#v4?V_AgR;I~7*4EOwd-qw;*QAj+91*r#769lz9k;cr zlI@uml-83LZ;2oqhPCn1t8LC1X|FH9Bsj?y2}M`d+s;SZUWPxAI1ft!&|0hPgG{i4 zO?4Zb6PkYu_EDz5Sy?2-ag{i7`lIxX`uQ%N>eaDTi&5Z%;#hm1RxN zOjgKvqB_Pi)FCNA>!rn5+iyW=(T9?AhT|?tQh$eXJ(XFtxUtlBy1bcWM&L_vD0M8_ z!(9zf%aTc!t4k;{*9}u(*}H8WElF9(rV_0_qY1dxGJ=wxotEb(ib`plFm`Bc60KGa`5XTLELZMUef$l6+|9-@#eSZZi;#(nj*zeNxBmciR=)lLKFVJ~RALsZ+a;2^c>`1ojkG`1FqR3g*9FNF;H!l{~Q>5`cnU|v_Ouf5z zfUes|VWYz&PYMXru-}4y_bh)sqr)$Y<>VUFZ8d*PWWM=XT zP)Ofmys60DkRBLfo}ahyrM=`n*FjXYdS1iInN~3m46|U^9d)2e^oSenPnAQU$+xBc zAI7txydLy_!}!*Wz^9}B0Z|z;TEC4hnHfhWJs*@%CpGN zXiw4~y;>#hnr++plY8{7WY@G|Xc}Ky)=*ptEg)LM-%ynBW(`o%mOx5%Jt=FplvhyH z>N!4xu>?ksCVavX8WU=9@}sm_19AP8Q*SDtM8~!Cmy*w^^LvqfPG<1@zayVZ>UR#c zmg8Z;+Jv%$V07zyRc^+My?5?*wa%L0x*RR_1N9A_?v7RF+ftz6*(EXJBWGJ{>kt|0}Ty% zCilDmBIBR~$k&o?Y`Yu7=yaR=Ud4D_&Xs2n+iAL61NTL$juubP9)}@kAt^rMGGqES zS4!$Mpc0$BhN%j0pCebaL_C^8=xM^B8$n(ATCF2uiWIg`7KNza+SC#hSvE7^1(OJtS?W+|7Y8qhx zr$sD_s`m4)Ph)Rl&r$mHB-cQLd8TeQ53<^RG!?HW-I1ReFHzU?G^_Iq4*^*y;1Afe z3YNBHtDw2-M6UTo^&O_S71S{n3>-`&k&$3flCxDj-#Fs1ipy@D*86bvIG?+n{Fa>R zB4H#a<>9ON+V$FPYge1!3vQEdvG0Ep_$@SR{{Y=tlm7q!qg~43J=4NBg37(}88=cA zr$8F6-UQVp(sjS17hfCO9EyX0TaGcJwQYZ|9Y~qIA zzIEJHZ$p`~wURpKo9Ol2#bpd0Wv%ALHv4I{enC|Xk_d6FV{6i`;>A{pgb2pS_;spn zi+YUHQ6*yRln^X*vb?IY+4L=`k5XUAv8sOujL3y+WhAyvy?X2Js*118xMhvB;st8* ziV{_89}!kniA7-mq)IyL@1w?8FdHFT&eWS}tr9a#l%s@3UB#j6r=e@DsUdE=|-P$O9(-02L1xF4-AZ!Mh__7;PvpL`JjFb z7W&j};pQt|)Da;60LR%!jIs;Nk4palkHU{owV-G2qvvlL zYPn!q$uWtb*uV}NMaoV3)Xk(jODRzU%c=9EoxMZ*VFs8!@_Z{LW(n0;NrJ*O%l~|`rPRqK5!7G7}Dp1-fS>1#hnqEsc3CGmwZtZAo;jMF<8%)2VgQIwd z8)=}{_Q6mB`c($z)UaM^(a`98E(SNt1tl7Rs%=#h*vlLHXN;2U7>^EGQcx3O2^0;L zECI)DV}ZyL4ZcciAU zDah+Z#N*ymhWU5h0VD*H5-shoS>pDa#!oZkHI!Q_$&KwC|g5%$*)zoS1chf|@s z4<4d99ELw9HFBebvXg&JHwMG-phB5PxJk}3>|z^YONn6z)LAyTw~4lt3+(XfHLYKl zLHZ}k<9P;?A~{c@ZZGfatwL2R;n0h<(K6SM&EZJL`si^VG~J3(;tf=;J@#dS+UHvql-JSi>i4dWwPyyR2EI%k4su{?}3CM&7hZN058d-#gt`C4ZV zhgqSq39O@|U^c)&2p0q!4-TF+$K2j}msBup#}gmmSvDuHjYr!Xk5aYd-kagiasK*$ zSO(cZW~H%?b=4}?B#@!uS9&T>p+~VCbvd)TJj^OHk=TF2);=ffu9uAC@hp{F`+t`Q zBhB9Ol;!^bF(mk%YqO@jBbJ_9A@Z0vKMgYobON9cL{~NVcGiK8n2P8k>AlDar0!^9 zpP?|v!Y5%`e7q~4+MHQ?O>CLQ$0U^5I#oTBE};TR2Mh~?;n3AR))o}RwYZ-b`H4)n z8}&DkQ*cdjyw>td@j7lhLoDg2-KX_pw6BymQhp`v)9|he?rEuGCtFt(n#d0)+S1Zg zp$w>t+QZ72tDnL*`(8?qkZ|9YLdk2>^ufA_wRK!aB`aS-gOb!xIg6iUV&Q_&fF65Y zqsF~kfY$SAvw-F=P>VS)JuT^y6(Qwr3MXoPmFawbvVupKavKhEBMX;eH)uQsbZQwX zea1LrlDh|TQj{!dU_sK<+Scd~?(oef@+T&@x6|~(yg;y~>D6hA-MF!WyUIx@D!R98 zSF|pNIkxi1Wa)YtPNzzYV6sN_*zz1Y-NWqGq}Z_Wgxy#0{{Vwpa==e*hhu0*+50M1 zSZT)uf+02=d}xZs0>n-D_Bk_Uw%U%{k@nOlYe)~UU%1{T8f7a>Teox`%2(XLNkas? z4aFaqC8kVe)YtnXK&@YQ;(LTy{;yxM!QY5kq;3THP!v5N#y8MNVaCHMMr$mp0 z0i}g3=7L5!AGU%>7PzIGB$J)81N=ShO_ZS zWV*7xMg~qv#+qTZI;D}I9tYh_+p?5FMx33E*{;%HaRoX4Na_or0Zq1TwLT``*FC+m zzm>ofKjgilCjoH3u{jgh-)jZ+$0V*)YetJ{@o8 zN!+>(>-Q|IA`tqJlWY0=s-D{=g36_r)V;XJ6#4MlkjC{Z!~@|}ckZaXGUlCi*&P+b zyHztH^KA&kZUCf!d@7H7lZsu-YV{7y?eAdPbqO;d4eo!`P}iXI70B)L5`BhLRC}FC z#yb^+OG!+InrTe|WmzKUO>b55-nMKctd0nWe0jzfu-s}yrjUtpk`JR4D^*!LdM4G< zaa!`Ztdy7BlRgvod%m2Px_pX6`SRS45F1B?hZb82zlbK)>U?h>8sqqsTNXJoqjNg@ zDOH~QeU)V>qzeREc2UPJ7QMvK;vd7I5tR1eqt^XuZ=)AczzNqn-0R~-uV5dvFj}@- z#`T`*zi7v)?4+eEjzSn5q053!quo-R@QHZjAkr0W4ui&#o&5%3S5fz*s!t<}SlBW= zDOSnHTq4ABvJSs>C2h|=3rJG%Nnk@Hi>BtQuGmAmw7()v6BJZOz;yOkKecm5Lr%Fc z#^YRJ)F>pP*4EWjrls6vMaD(VfaFr~$v|x&4L2)ONNn-YnR%_r$|pkc9%>Yp(ID+n z(5m`tT;}%-@zCfsd5*Z3V%&+5VoudIph35la=RK5OR>?>yBF$68H|@KZEr0FQ(LX| zELOuo1Y>#~pM@*zp~ZV1rwCpjkhLK?d(zBx5i`j=)O0^D>ZCB18%ze-Y>aUG=?lBan4Qrk;qutJ58vh=O|bmB8t zAh1;Yx3mW&)2nGDU!q2!{j|L`HQaaJQb#Uwj?d*FI<)+$P#qK#da;e_B9<(&y-xs~ z)93~wfCk@``>BVxuq)u(!Fuq%-zjC67ZmT(;EVLGb9-sLmo(xi=*(|z+Eiqc&cZ;@ ziso#xNU>HXhPj^H`ca5&f~D(mYY*8=JF(m(W%MZg-v^MdP@*-!z5Ww>)uy)6^&;cs zuvGbu1v$V%Bb9qCS5TxIY(}?1y)3L6!@LL@~J*<4QD2y#K>7avYdLbvE}fMX(i7`CjS6vkkVgmpW9;` z`JE}~^U1E{r^6($Mx=J_@UQ*0-(`(E4sWM%Iq?zt4U^lsB|^+-JawV2Y3Hj4`!Y+S zH$A(Mq42S^_?sGO^V#(T_?DMJgpLCtw2jS>2Z&FV9RWKClU;L`ev-`x6$dks7m4jM9>CvKLos=ce5E%a%^EuKE|(X#Fv^d#%{ zRUY3}g2JTEmhHS*d3j7S+7zqUZUJ2lb9-`(OYJF3)b4&0!*Wnn8tYgafj0+B`vI!T@jEbiWnE9!RV5yi-l9kTAW0?GR-&YD_TM~M7`dJ~($Vx_W2m8tw3e5NGgRNeHw zGENOMM;i|`QlW3grA!t{LVhynMI_;vGC{vnuazIeseeZ$KLWtT;~6oMlqTA0M~zwO z)6nF1w`J$lAgnrWFRlIb-Bdh@$B}q4oN61r)6>SSYK9q)b+Rv)X+SuDi`*LMI4#*i z^@7R?PNs^j=;f!D*-oiFZC^@gY^g&zJd1x?P@jmJS_i6q)OR+@{{U=o8UFwX`)K}1 z&lWjttxt_b*n-Bf=wjOK*WXL2{RUxQQPXV_HPm?0F5aWkqbPjaB=~)l+qYuzBA z^W0RlDOqeG$x|jN=$`;6s`LZcVxE!x;rQ15n2dG$n}jFJ-f9ZQyMvN`g1(Zxhu>QZ zgBun5qwl4R!#4EQS{T9Yit-hpxQMpI`BMIDG~*T`5>G)}^I*IYt-jhUXOceI?GJ?t zSxhOKhD{AhR6V}!YF=!a?@fMHqj+ZinyE)7G>{LFay~i4A)KEk%QcFJ;HM6x^sp6M zvB@YG8oWgA?1~e*-&A}vi186Gfy`r(a?Fa7h>=x)NsvyfbD`4O3Zjgdd3MxDiFx*-l*zZBX2DaqeOo%$JqaT-)7x?6vR2QOnAx%veS=u;FD6@w4X{bmTOO5Jv&wx(HS}`(7WW_FDrHTr#z{(TyWzE0OOq|?(dvB( z`fKf@ipe*ZdN>e>0T&)3)#m>IBjvL%6m|MK{?%?w`mce;Ga5TdNM98|Q9A}xVz#supUgT{)|SZDwen8@*ie3D!?}K&X*!y`p4*BY+S>46 zR!Lb9p*BKx{5~XgrD1gD%1b@V84#N@){y4GcFpW-spII9foQ7njy*w3QMnh;)rD|i zWSSI;;AkH+_|n{tzqx?=jWyK6ge_aINrX{v$;Z+wz(7hdIJ!zdBeu zl_6?7$?>_WkKNLa52dx7B_A(A?gN9)!>jqMf>4;(Ufp~J#Z=k0td>5YXM7Z~kT*K| zfmA#k+$+b_DM>;BQLy)dD*KP%r)1O5LyX;qy;5?D{LJ}f&hsthCe4zu@j9g&eZOUU ze-o|k!C>>AO5ZtkD13lQl;IlM)-}~tXGC+`5&8fePNhGMT6={|mr-c!?%S`3rLDgY zxN2y4B-rTth|^k9%d&>+rKVVzsE8;fdg-NAZC>(Q(NgRwC4hw}UqDB`llT0V11j*v zvN@>|phU;+2xchK-BshQ4w|DEpHTC?zJxYn^aPvVr7>NPByNf$uQ{(xDdjGh6-k2~ zaDYO3nw%|6oa%7`N?%=d@}_(MN;xCa7B%yxYI#H}h!yqy-Zd(L$qZKM*5=@JG<=In zI2k(V(Dgo52}u~D*e&DKZmCyjA7~im^|8IW3M4Ny>H)rOsrJ>49mFk*0X-th>C>R6 zC)`&9WytpXt6JtUxsXp)UYfwR25-7e?NEnfbM6RzV0hozTk+|F068vo{6!wIa6{_r zeFulzSzkmF&cG)wc9HF-{n04HCi_JXAic5k5sK85f$?|>gzQmSWT8is!X)rG)cFbZ z6B$WDx)M6o72LH+<0YkBT=AR|6miE|0ZV9$erxGkj`GiPe`fG5F{AfNZT8a<+sQv@ zCl45>7L=hIuj5MHu%gIStmIjk9yp~f3tM^%Y457Nz0}KXeSZ?`!(q_Jr*wE%J$}Mv ztx?HsU>n#HQ`A@*uC?VBwdEIo6l}?P5z?K?Q1ArmO;z3bdXH;ACQH@Eoo-l2Z{ojS zZ(2%MH`rsoN?HQb!up&fprxmgRi^_^pB2hD_=%%T=taf?sAq*8yrw6 zvDAb1*Qjg3xSn&i@hkw8;MF8s+g2h+yuZh1} z>-2Qg4sUZ|wsV$o*Cnk*#qPPePqy{bRbCfDhWR!{l?_X7$<))i74cxlaf%1xKps>F zdq&-C!Z7>Pm2KVr9cWhUldDy?*p}kBi<1(!5Z#i`iShBOOZV4e>Wv1vk`^!<2nlaN z(@LnG{9kg~y=IXjiHKW!ehc2Jb~d_}HBe3QID|OkA!*nyElxV za}7_U=ti@gJ^~tE=-}?Y@3yjAel1|M?0X`e4{bv2l|cOQb^QSu5lX*6Fifas#8ZCX5qZj zmQ;&u&<_({r^no<6nWmoZW3e)a+D3fFS5Eyik*NUQCrAKIurT4hmBa*V#!-PisiB` zFas=VLPn#`nu7A|FE)rKLSu>>iNCh3_3FYZwzHBUE=rURwyPMy6*82t0DZ*-{%*Z$ z%CFJ}eNZ9!I_dY+r5u3qTW>5raEL;r|qm{J+v5#)s@%X zLaZYsFyk$12G$0)q{vFLQ3@Ki*o)NN*&*#RdB-5T>!CMW#dZ4I=li!LLaSDVa7D9| zzD^^Kok~F&^FA3OWyntviDGW39?B$AYi5&`yJx_t^!S2-M0is6!Z9$iUo z3fZ_7tH37u{uK4wDm{QkB*!_6#qegr8Mn#rG;FQZb`W{X9@VcAr`W-S2-zV)qCj5MAS%ho~sM{ zKj2e{R>8tQFs@l07)Cws`zlsT3^hwAKM}$<+|VrS5fXS#5Jsd`dmVylmL?8Yq~cEP zr6d}xwmr)VW%-Wo?2D1|G9j}e#DQ_Xl%tILpIf_M#dy?cktIB~ z&;n9HQ5Us=HRrolSt($ z1dLax!Nrn&l^DwburY8FRceIhMlk9MTRm(#_|~IyH5G&oaSH_8{j`5{*S2rz`)DCh zN-Ik`YJHT2wsak?w~}d%#g|enp0!D{v*L?QYBGY3VM;=V(hUJ^jV*SBTEeZ>Z9*?n zp5jPioo=D%TygQY;jq7vs!fgLS=Fk^<|mH5xfgG6@dR3_c6M4t#bAa7WOiIfl04r5n!r+$!iquEkZGuL z2(FhL;_wlRf}IJ#r6ovE=qzf%>_$jlTJlJ+FtcYyP)S;bqW8I~yK0NbT6U!Z@Z%F4 zn3mgL=eYQ_u4`{iHf%NAzUEOh$KX^pg(CXfr73Q& zmtgVUXt42|av(x?v=*=4B9(kxm`L)CdXtD))|g34Xcivo6nLh>{_T%Yvr)oSEC46Q zjXozU0b|TI`-LN2cO9K(*9($6AZ6Va{PzL^W&)dSZ`Xc7OOFpAnyh|OC zMV)0TC#bNcFXAM_$CiwHk@a?m6!P~s6+aQV8$}_r$4WkfmGW_6>{n~pfn_0)aCH`{ zU9AR(s^sF!3m3%0U?FW?J_gpRE3_EocUA%uxH~csHZ`g1prx(j^XO$4%>dpd)&7GoX{dqVdIH?A zbHy&FpX_SN5~ao}2VD>-u;eX9^AbHHu&pgSlL)Cqo2aKlA|3Ux_)x#UV}U($!zJ*m9%1yBeH)F&L=X zY1L{_8Xslrh2(tlT30+B>Bh3SggD!h9!hKgNjCS2r-E^LWk)YfvybIECljB=n5~wc zK}EXU3cJ_Z=y@jp02P0qRC;GF$H!O*bSnL9bfZSeW1+o&k6ks@0AJ_$)tz2OpW+nK zm5i~k;}#%1?&Ra4Bo`eA*pqXwx|-M5u`$ZNF5jrb{{Vz2GnAi{cYB>cwL4ywjzbmv zeL;173mcAeW)jnAk)qT<3Dc?4u3=Q`?sB}x$6I-iV$x1!mm3a}0#)IqH}2!AIEitx zQo)sE*>HR+0{v;oTKLd`QU)x@j$NZL6 zMI=>kakbd;0(Xr`ZDz?n{{Z_dxufM(G_uMIct@Utae|Z;Wgwb*9Q`k*M5V#ZSNTk& zs>)=M=W3efs*~u$E*eQ05yDhLfz!f?9nUVnUayC7(qfy7jVf`TLC&^1OF$Fh?G-6r zz^L^du+G1h{@SnDx%l)fb)osXghDoudY=JJQ9K6Ng21S3Yp>x$g`GfG6_X_zb*Wir zLUWHp!L;e6ZP@TCpz?3AAdLsUsxZ?n)Z#q!lWi%O+Vg>jTEK^-Z`P!w%IQl zOv=Q_Mzg}+RJ1J!;Ed_zME4BvN3^Ybh|CzUsQZl9f;~_qY5*MtY&U{Er-cr)UfMMD z$C;uT?IRwSww1}XS~KX6I@U&gvt4Una5e29W^XT#QzAPh*zB)ehvL&;ZFBs;B$M$)j;|jMq|#}L8A84PQ#I-(mOUdcYsb$7JvJYeO0jCLkG zC$~@M-$$VwtdpPs)qf$%`)fHY^z=Nv3;P$^rQ#iA$%s&!3${g#yepcwnpQ51yQhu8 zKZ(Jaf75N2wy9RWn%~NR_nSjCX@NnWdfgQzLubAmvXCp5wTW3JEV>}Og5e_T{atPbWq+hdEYO_sLu$Lt1#+mB@Dumoz>q5FYM58H7Arx zqvDQ2DfJK(l_Y=-ysL}l_|-i0D6QZ2XV9&=Hw)vKrp&p?LR@eX27>0i>wk<;J5Cei zyPFTf8NxZ&xEY*eY&6CpHa{r{PypV*icgQ!l%~Fir*V3B;m&_UnVIUX%k@`NU2Sk{vCCa2o}?~H0@6t9da{h#!Vr}?1%^(# z)q=O`SI1f8aUGHfx^%rIWU|XzX;>wA^jS$a>ETPt%mq~&S^IspqEcN(FMeB}5vi#u;Kdfe5^Ogc{gl0SrpBeOgaDB6N=WmjCf-meq<~L8 zZ>NP?R>W=s%aBdY=&+VR-0}u}-PavS9VyCe$rT(hd&>kXTYGA8mH|t+DaC7VDx0v( z4V*gtm9KEj10K1xYt4XljNSb-pwx(YO&1>71NE!_ycyu9ft$UXs2tF86Dg%TMDV(JNbW8x>Tl{pyYfdsp`N}*27 zBPUzHhKItZ7T6V7vkY!g8-D7Vx-nT`p-h|Tb%)dMV6&ae$s$+srGy}tpP7t6QTG{yY*3^~!hB`J_ zSEQ2Yi%T2EQqyB|Rte}W<5YL<{RXMk!s$(=A-?5}As%woHP$RWp^`VzO{twBzC=#g>e=v?NOGKQ?@6n%=8uNCR^0WS3iwIz z@~^2i=KN9OHs{=VBM(>uxZJ0u8V%%v!$T!YN^P%RnpKdqGMSo>Dp{g2VOAyGAe&$A zpiN1>l--?;`qATu5a^6&pM_)f>22`i$5-s4BalS%f zWZ}wLHetY{?Es64r{#2fs%aK{ZORrV&$GRp%5fgz@wiUn)E#;|X-o0QP10?zoqY4n zYu~S9&tuqly^5@dxB&I`b+vN)o6?kU&WB%KIVaPkv5Cd0)VL5( zlczzTw}n+guQ?C4qKTu-VNVwquEL*7utY1<$s+ z9awc(MK!klN-K~`+*U8B>#bi`7RsBYlCPNRRN}9vTT_vq1?3El8#_vFE*0VKse7b7 zq2Pt;6mAwLrn=C&G(aTvzn$yNzCyq5^ep)|;j5Fe+>Xp- zZ`=86n4OP35yHahP2i!squnky#GpL6j_-F++bPtNDcwycbdp`p--hBWiu0SoK1g@V zxDff)VcCr{=r)v;pdSI|E8l(}r7EL|cPTzixTg9X#UgIV??-jb?KqQW)O4>$s)Xin zH(2mcJh=!3-%i#yAwyj*YrEp_@xn(H$yATIi9_3)w2 zL1P?u8WykP(urN5ET}{5qkDk-k5K@c`nxo}Zvsu>iLK=o0F$qY>7^Q_&>Wsb)U{t# zQBVo@HMt{NW~t!=lF5KN))9+AACFK1>GxE8!on>RTz(;LDX^3hPqKq-(hcCt*M;!4%XNh) z0Go^4A0Kd}t=RDcWqmSq>|P@D9CAg5(bC}QVM30_lLY9%V+Y3{n&3`}2|tu+L$+Wc zQ#&8F(HD-39fJP=seAkvx`)|R_GcpswwZS&18K0vJaRb--zS`M^WzSrAV_yscR8nnVbTwnEGzR2+?Cu^Od((q+m$1IO zDF>(r%DQSSN<9i|D{YA*`4X_#<`lW_ zDGrskg&P}_YUuUi+A8G|4(@H@NE%zE7uY8mARMRMc$F(cdWx1CB;=67hGy*6%9;GJsIUtvh9*{9VKPBFlw zL3OaUPk{#B0*ho5KQdncvODC zKiIG@N-^WRyf+?YH(-9_JdP9);Z-{^v$bG3C1rN4E5Y_?*Wf;mJlF znI(uBUANlaby>B*Wp){_nMwBwp)8_$B5 z$5(-j$!zPlQ{!8!;jUpL&qXfa)00CW>^2waPgVSKH$fG<4i)USx^)K=H=v=%!50D&j-u@VZca80^Y=lpk4=!r+?SwU#!lWpw| zoocq<o0_GU@%)qMhwAyIO>}1L;^16vWj8w9ic-&kNjx-I zf2<)zu>($( zV-JHxc0la;a*(5rF}G&UqsoUrf%zga`9x4H*tpk;4zQIUaISZM`0c3Vy5s(0vw#*u zhvM=nTS#FcD)0#!*9X7xt7*?p$8o3f3g(wy72nq_65;7VvH2z?Jw2qe>*{E`nU5mw~5I?m!5DOZRfpX{qj z_mGR$PoV;z!rV>NS|hK8Uu*BI3*DqdU*hU-aZ)rM!~NB9UhG$>kJ$oMfmQ55_gap( z5UbWb{@w@l6Gx5t2kkS>@%3*9)9LJ~d+5hYCHPz`p~UWI;TE;@rR}{n+$9-I=uRN} z`1CZCT`K8*mNvCh>{6xGjr*87D~}mc($kA|XJ)D-pR%_dH2na3y$d=vQ!dj9X}8vt zCq#?iN@k8GKS2w&VwjSr^%LndbIxlFqY3G@zIm!8E`GNT>K{D z!=iH~Ssh<1w>{yLwUsP1F>3mlF7cjJczu;HKY=<0UiJfX-)bCuG^&g5nPOjDI~OjE!63HYMQK*=w0g7Jbq#sF%7#S z?I0u`wb$r5D6EL>!!jHC3-J|g7Xh24ftG`c~?i40e=@XeDwaTCOJcmM zMvVGu@)WwYj4A~^G*_53f_fbD=+vU^eQTIlDp#>v#wY8~V zc;B>RYQJ`(?HKliB|jb`F3EjCwpkZhK0>NDH`Q@@8mm>?H5J_9KKpkC8Q|m?&X(ZD zD%jXoiX%&#R}(8|b-8_yP2=IumeB{wnJj}$H;78kh&=kTlgIevwD;*PWWci~EhlPi-D zDGDGZ1v|9^PQ5zUHO=x~O9S-ls^g!b*8G!#V&8R#+>DT|w%ctl)~IQ^QDL<9qi#z5 zd{wAY((6$zW1;f4w12v2vQb#iX zwJzY@ZLyT4&r2#XR9bLqmqxa=FZ+a29j8UILB;u0Y>)o{Bd4fG$~SNU&^d1r@2ykG z#(e>D9AxXm!8ib(g7s@#$wxV0&!)dG*-vC&CfhSuXABt7s`Lnf*Nh>~Ggs>SL+O&0|&*FGTkH- z_|=16So{R6eLxPrb=Jemiv-aa^6-7$10~{t&YcIoorJOmQpnB2Vtv#L zALj`Uz^Bx8^VHRxdli=gTu$qbmaW{)L$KG-AG`R{r1C15>sbH}z9RH?@#<-;_BxBQ zJ%-J5!0uuAif;`Y{;?mOLtQJ*epB+8)SP1ao!`fPIPAok_P4Uw-W|icX5JPOn~j)A zS`@-a4yOMA4bGL|yq3k5+2_>sr9@z{*yYaeaMo~gm7jAiiOb5mq^2u}bEpGb>)~F9 z@#~$CIeuOdOrs-XBg=EbQk4zIrRh*M-h&#i*1p9pi<9fOVnl*CW&r40ER%` z-K}jYX>75`QnA!{>{imE02*|t(wieFeFi2i$q7xt>S^e_0F)P~N(2HHl1D>E*tAYH z^*VpE%fjPkrzyxJ2snIgo|P>`0+50fK=;=j&FWosZWXMybo_kbTFjmfrRAU0gST8q z47_gz;*xnDMTB~BXFO9LGDsHPt<*(Pa6iMl*H6mFh?N|dF|Ft1q@|x*6z*OHIo7 z5_fGse}Jx;@U+f2aVe|k?~CZmX+Ksy#8w@6MxSB~MKV}fZKQ&>)GTj*8t0+#O-ilv zg2OK5^Bd*=fNU0<_}2~2 zaJB5Nz0S9U<)>3e);`?#&jZb{@npFAV`Ue$3S^Pjp|z{VxxbECMBh$_sAzJqvMaAp zpK@mJwZ@)R=a+KQ^UhAETS<1oT+HCb3t0{6eRQSYGB)`C z0HG9_#?;!Df{-n)y=lFEQMPJaVrXd2As(t52pjGN>6!0z$(3oWo!8kMTX>9WKxrvk z_EAu=y|n9GX7=N`s9X8jGc7p<7(=)x1I{Sb&}{u750myB8ylv%7*Rmshcm}vEZ{=2M&Vo1^I4^7I zTCU8nQ;5uhdJSmRnh-79#8+w`3negNZZh<1Z!1ya#?28DHs0$9N_KY!itcaB!Zu^l$OJg ztfACjIh;dkF%WJWz0EzX9)PTd2*x{;zmJr+nm*xrYAeuN)V8<}bQK#u)0sB|QIsqa zO~>JC2Hj0WE0;sA@%_Q$^P>qVl$4Z>QE~3IQQ2GWz`QnPeZ%AJxPj}1C10o!Lvhbx zmjuZS9!HIykMxd(pD!AZw*5^;DVw=C_1$uMRK2jG21-aK_7pZ@3gvT{gN~CfMUvA< zBNOWbC>7@qUyDn4Yq8D$I%zuZ3kwav?RCt$S)%yIc-kKibi{uAf!rM~V2 zR=mUP@jR0Ds75yN7B`_=xfc2`v(uZv{TyO*U=qR7{uBkbpHd4;0VtTcO{q6v+;tTh z>||oPvD}|w;lA3gwA$VtG$&$`z|w{b1;<@XR@=F?x@$?u*u~f9INF&G64|i)DTqN6 zwizI~jB}(a+7fj%cqtjYlZ6q-bT#6@)$to5>_Ry!ef-Q4) zxU7{ZdfVOd(@L+{+`=8!)>%3S zf$;wTn^P>Utq+Z_Tz@0F&aX>Ux@7i!`Hhc3lmyB`g3k@(^{soVu>qWc$yGqZTyiD~EK zp+!j=*lTgtl9Q1LwBJy?&d6Yxh@_$`diBtCrR;?MrG<7yq8A6WIQY2&;v%IeN$>zr z^C-T(L&iwAU}2$RV+N9x>1(G?3T~P*y9#V<4JJU2@KS&QH_&-g)zI=ID_#hkzB?87 z3W!K2UYDo!=#mn*V<6MUVzc=h8f$x1lC9)MR^y=XL~Wvwkzk)?YE|T7H!Kbfm2=Y8 zrj>C4DYho35Rc4tHNUfozj=*R;7z=5Q1+1zxnk*&pf;v`q(|QtZbSMJNxFES`qpRef@?Em1$*r>7U!-^c7q4$%QDZ^IZM380bZM4$pd6?jod%8x4N? zV!e2-$V%O|(T`93RblOZ0zUEJ*uCv~AF_r1=cuS6R_ z5i(=!?qsD2Al!uzZLJcDr9Q%&SH>vm%oh_>W)hV)1%|qHHFvF{F6`&8K;H zr%@Ys2~=E9q*4H|vACvVN?l6%-JXF~;S~63ew5vHu{$hS2(cM=wW+00r`!e^kG97D z0A)#3vJc%Pc*O?YMa_Z!%7)&jlFB1H6Id)bc}CenN|kLX9bGwj6gwI&Ow6qEmGGq8 z9|2a-(aXaJS05PnBT{C_bynYCdI5hLxvxSI$3cZ`7h#^#uWhQvH7`J3^E7R~y7^PJ zenAVQ*s2?`JFRc+s4E|#DLkQ2*azVkK3ddmvkNP^s$jAz8uOq>W?2m56F!!`YTZc) zIQ1MQ7Y6!Np**5;Jd^-+)|0+%MBQ=A9r{PktK(F%S}PV0C9b=)r{iV?r3B9whn*UB ziu-2Xo%~I1H==H~E$I*3q`lG41}F0B>v2u1NPA{W7S!H>@1s4WV`4nDq(0_(8TMjl z(@*YNnT@z1?XzIbzfa#=vth{6rYP5Muzl4Wv5X;LkMUM{pe_j8(jIga@=wf2qytJy zNfS)fU|X#T&7?{!p}Uoq$;oNlZaoTZgn}M&$5HlInsa;eobl*-Z^gLE)O_Us05j}z zK68sW_*h$IOM6Y^U;hB6sOxWdBc;1nB^Ay)dhR+3)TY)H5>CK09z95_olZ^3^eVTs zx^y|n%JY0tSXBM&*I9+rlR?`D*w z-owU)89yxGviRPV=U!P6g`37@w=OH`btteQ2IpFn+w0Wmr>!Wg6{m7}-ZzUSrkg2T zmo0Yhaz4>Uqb-|-Ev}>+h+PnTm3?wf-AK+jXzrdTn#N`z zGcm;$mikBV);5rB(#D_G<)e=0oAVlXRXjy>Boa*^-;<5&MTqb@mvF%UTX|Pi}HyamPg666-A+MQQ`T1 z@$arOJl4j=zA-MjUM8(d{PJ7)-ZKfe+mY!cJkbR!w!eLPcN3=Kvd=!>*owzhLKo>}iid44qX>>(^O8x!SvENo30RhTF?{=rq2Ss=7H+Cmv z+R5}k!M+t*p-O8kulVP3n~9WUNPH6E+ES$p`}L%yIi=L?rdWCBDt zR-#HR?W?Nxj&{vY%nEiY+dmgddacZZvr(aojg;@#%8&ObNzlz(KN@tb_d|PlB=ArP z2G+e)-?23!&Zs7dD-8gXack1iza(K;)Js#T_$8xX<8ZuV{JDTvDe z0nRL_+ij@Ow+v3nHX`TgsS*>?vnCoAy-QeL)s|Yrs{nSPQa2HytNLCFZHVNBNo?I-UUhR(RcI;TQ*wF}r4di2 zK*k`~wA@FHGYMr8vOPhr^{M1qeTE80O~c(%7dLJ)ltDczK4_n|%Qv??8C<713Ot8I zOlllSpETSh4Gl^BE1UTTBPdshcP)5rB`M}zkEL+?!Q0;3<706iBaT*1By?GGN5GXV zkH=c_D?TT)wP(3E(Dcaj^y@28swE5HiM!?^aER0amRc{n8))Y zk=^-Kl4NdYG|Xn7ZQPzg$*N9``iduQ`r5CxxTOGUIKJbA{ExC8E}Q68bCFdNDWHytU&jeCZOxmA)i zrm!g~WRPQ+Rnn_MuLS_vkdPkA%?7@=r$ zzff<`bQZVluT9j4f;gE@P4#3BRGr_%C!so3jYz4q6sad&Y3kqtalpjLxi&WY>vL0c$)ZW1x68Zd7SeRTfH{+N6`T+r(EH%+VVwc{*M`#~Gc7o@FZ}hD6i{6qdqLzY*c? zuQ__E4D4Yx*H8{dLXhZMe1_piU3-y^QppL+yyLr+T=ojeu@+?J_$uZWSU^RG6t z?xo`C*ox<2Hl5Z1DOXBTQ*3^orA3fR1dNPH7Guy4Wn61~ zoj+2eYvYlAg;B`6Wi6nx7_suV*+*5<2{DQ@8;nG})79l0f&k?gfy)5yX0DR1MXy$G!ABja)O zZEl)Xy*sNt%Ds&jj>I}Law*i?QAJxlFbY&;W~i1o+f%OZ#02TX8MzdVLVEitpKw#fjU0}AtSf%$ z305|8Db!m^G-vbz>uhc#s3h2Xs$TO<45{=n^2=Gd9_l2vn(S$6iK;r1Y)@L9TNxL+ zj2is9yeZFkQPlkh0hQZPQTIuB4iLfeqeASE_JXlvn~goyzjW8MPH_;*%0|(8Tip7Q z_OM=DPmH}JC0>vy8j=MqZT$vS2VZvYj>5*lRF~2c=_1#)tOe?qX30t63_`S#VL8g# zg0&@|&@IzbN-7>qlSp*$DqP0FZCeQ%f!6k>aH6^ZaKj<9Q1i-KmXZ`tTOS&7i~0)P z1`2vFqS;c{Wq2BTH{@0nutFqV!b0^OP3ViN>MG%o#C+7q`%v7ap(pbs5-<4Dmcc&c zRaqp_v$EbxOtR{hz26ABkH4WkXem_(mcrGmWS$Z_yR=x&bC^>}#_^ex5W57r8Fy`o z>vaKK{{SuW4O-rEU2E{$4Phg*u|16LCRHrCCKFiHI7R(e1;9RFS0T0Y&1f#2S$nVX zJEhf@wiD@QL+V4Benb*4{zFLX=c);0PlH%$_%G+^Y?DK(ySoj`+%D0K&8G9o%|`7b}N_-_s)v^-pY}s*koNkDL3(~FvqMv zcC!YseubF&M8zqPepmu6Z-Cc=u6jqTy=bv1KT9wnBq&8n_V`#{vGcNKmb?*L<~u0f zEp8w|3r)_z0R(-%G}RoN^(L#1I+fl-wgeR=5bd{BXnzW{uWvq}9WcNtuGP$4y&QVZ zjnE6UXClpm~i6{0_wsKfO z>!r3_+*`0RrXed_W2gk~PP+JXs!fh%zQwFG%Thnnild|{QFS8s2?zVCpZA$Qg|v0B zgdOY?X;%LLm$)C{7yil{ z9csyGDSBX1JDx1EVCO_cq#$0yrRhm<*+bFg6kbK%<)E~r?AoE?dQhYB=GaK{=!U<^ z_BgVUs);7v+OVg9!3WMTTC=7?{wFRqxTh-N)E8iO@|o^GYa_WkQ*Ed78q?y|O#Q($ zEFVjrJt@V;Avd_bqHZ~0p>CGejr(`tD7OLct;JkhuSy!w{0)8U7%9b+{5L1vLwbyA zJIG}3%#wB_>(>1%UuwgB;uw)5arSks%8-!QO^zG&6jOAP6Vin$a7t6i<)klNEpC*5 zY#4qL-1u*&odjrH~?HU02W34wHAgC zdMB-gE#k5V&5C+wz~9(tZo!B^Uugh*HK|q_b4bmVFE3AV^*AefkqEx@a7zQp;PR3{aKb>$Vb9$zF)4YhmRS$-8&yt0eV zK5xk#v)iZ4=uy65H&cmD?|H8h=XH{kM`Osh8dLQqd9EYs0=Akz8MxEK?5-ncf^zHJ zQ;hizIyOP1aY?(BLt9%%nYHSsd=tsk)6dn8{YZ9Ms4zmBc#m86K)o|h#pgbyt$eOJ zwnP-DlGGB0vm$|I>z>`wwyrp1;>r5%TI%vWlfy&EqosbjRmxP8&*2nx_v$#F)23rmo`iP z0F$8!vQuuBtL31VVy;`6Fq~D@Vw;0oT1j>U_PP&Q!zWLM^u-#zMXlYI0<7T(x5I0D zD$=?|9?)T|a7g^3M!oGf0R$|TA20$``?MMi~bwdK7vls%46I6Xo&-JV(l_~ zKjlo|SILA@)`Hd-@$sU@EXm6f2(+stCi?y~TB4H=JAWy*zIA4zitllodRP5adhE!X zeMPjlwuncmkbtve;Y;|8Sh@;&U*e|WEDPbAp$EWyv`;Z~IZIdTsq(C@DWI)1IP}h) zD!R!v)OrcXQ)Zoh+Kn48B&ATQTQ(YL;Y~{HkxLzq2Jh7C?5T>pih6(GtjU-*^vCYh zaxsH#hWZ;)E>9u6jO{eTn+9p={{V>9dz<=&^F6ctMzt35XOO1B=TC!jF}K@G67oG^ z^%+|7K&5dg&WY~*R*DRXI*+?FJYPS1>MnA)t~7($s2~ zUWFCWpQ)}@{Cr5ul_hCu4J4`6k^le;ldW#cKQ}^JHQPD($Eow%x}RCF@p5t)UIUF2 zWU!?)ysOs@1t&sMk=LbP=<(9G4fZ`3Q{ynLCu__491of9k16CiX&G$ZQQ4CpCgiAu zq+a$39s;@wv@3$f3(s{$VRQ=dzV~vTD&p4yXQ-8*@|?Pdx`lre3m#Ai+}KGR=0`8f zB2IQ^rc|No5~2{0q6x7yp3bYSbRE`tSUS6i_aC=BIxJ|>;TQ0tU3Id)UJyod8@1^} z%3aR&;~YyJ#<9_%#g37+Bh+%*k_E4N?RY*S6h$_=o`>U{p zYAmL9Ha|iMzLjrM>M1D-`f45>XwvKt%?YI!zLbZXOq-FoZ25ik*O?bEglOB&nhHy9 z&lu!V>##QDuBDt);Rf4MNx!nZo8+6hO(5JX9JUrup8djk=A_GzLV-~}nufL=MR{#| z3D2qZm&YWdLP+L}zE$)-;{8Pem2M7ed)4d}b|UiGbOTDoWVxINRHxT5QBXkKO16(> zYMX1Kf0Uxx%jk1Xdx1@EQ;AbYLP<8%gI(tz%&!xd*wW019Nr+P%_&YNWv)o8J$_To z^eb=E@GDHTLKdW?5C)Zdt!u!owR9rxMpJ3jYI>X1x4Klytg$HUtf*ts1KZ?60m!^ZRwV)5fjqV$A_wwbj?JT47v;CWa1+ZK5j(D$v!>4J#d> zoJK3vWZLvO+`@ZC18O$XiSH3rypjxVKh<#xBil+(V=S=tQi(Ku<}vZhX;AeDzlBw- z$(ds4R=I3}T9>ZARnTfvgj8Po75wLYLVRsq1QNkdKICFbbtafo>Q~MHIRM(6u6Y+| z6zXnB_THF`@Qj*m10DT8Z3;|XqhP|9a%l0ei?1Olu!`1%?dob4+|uYq5bMfhKH4RfrioVYPB0mdL1_V7Y;+zPRYv6*U5lMPj}qg?a#FOQZ?HO7KYpQY zR@YKa%)uclC0`d?{xwdk#EWYz%ufez;Zk`A7m&0V>vqlr++N#gme0mHC0u-&)qTJ0 z#O^`HvN_U?i47>h7P(S@@EvQ6+vlY0Nf!%FDmgg6y1a8GhnXfki0TSK{vaa8!_K{X z{i*>1oNa79o9dl zn1rw_4R!k)ZoRsiIt6P7t=rn>{_1NWgp|A5*B*7F1j>rKo7$H)A{MVYG-Z+zmP~q$ z`F`4wBVSwmRIwrBPG0{24Z2iVXuOXZB&)5#s_BP<$s~ZH4OU+8SVAR*h*Iv56Kixe zQ@v_vvewEj{v01`V#W2a`|Fa~w+W-5)~Cx1>aH5OGec~sl_?+r<6MUP)D}*sN{2Gd z{{U%BleWyMMR&cDqJIkItmdH~Q=pF%kr`dH&b;2_KyzUH%Gf~H?4`fdb^5b!g;?kS zd$tost75@DJP+GZm&g($?~cpzlY;l2gT5h=aMH0Ua&Y zk^Ah7&tC`){Fw=S6fky6;7&gmPq#X!yqw1;@+e30#J1wV zb}LYCU#99Wx1cl+ZzE3m!DSGxRl^}X zpHMO^Z*k}MP@JU$py9dMK9V`(kZ>puty>u+j<@SiQ^`&~r)HEV9F;F6?YnV(6{0Wj z-2Ga0@vAL#1@+)gX|svb}qT|OPAp?7~Hba0CcXuPo0F0PjiQqSpk!QeX>T~M_Y=xuab*J zMVd_k-o`kag*We~A+r`MEoL!ZEbi%{@un5&AvtU1h&hcmKMs^g>||P6{vgL-p9KAs z)Gx>@Dag^-M{xLcG+69m3rks{vY{)bt$NeEjOxNP`5nXpY$>ShEQ(I2lVb;^{v+p7 zR-zK8J;j>GHbEz^kf}X~k};tqZ~V1Cx%(-_uqNUtAr6H;`jWM>F1!RGz}%#yCvl+r znP9v+7iS6K&dhbC?6_5l)1@tb!jC|)f^R2i@jMJ1ZZM(tM*gW$NYdYBRlSEMJsMI& zcd`ogR~>`tq!I?(*41Kjjz*!k!pU07ijR-BiIN`KKjPSDCQXUCe*%<}q2X)L?z0gj zppMkQc8dp>fXt<|ms~dWO|A(Q$=%CMPX(lOV>DJh$n7loPB)LiW0KO8cBOkj7w9V9 zj&}>Fy}VycWJij99A|QAL)nf@)C8`@O(7RhxW7|M?tIl((Jr91zBr}bod?Cfi~DlR ztht@bO33a105Pzzgs6jiXx5qhg(a| zXm6=lHOOysTX#OM1@#UG zV0RIh=goI7F*9?rRJ6LCKZS1(b$e&TIK>*)5y5lYxykYGmvT_z-3d_%B|v+xUa75F z!`UqLD16RKa+UG!^Q&0J15O16Mj{IEr?fVBdz8(*oV z9pr1NGaFU=--T=L0)4s^`E&cCi|(=>2>qnDj%g{_5)-H9u&cP9B^Ll)>zL?jFJ zAXVj@8o*Jw8;p*^`^im;Yrdo7HM>8Qs@w~___02)%wiL!9A zD6sMT8yk&mY>Y0SW}15{vBph~tCL|9f{w1S(m?+J4F}s}zD!A6J}iN7z)3wm5m_qQ zWL3`q$y^D$8!`?2K-RXa$8m*u7m>gccb`K;<8S#=${1r1ydI9;AgrH63w7|RJdD?R zB)F~=%u+&{NFD~>G`*F1cMTn+Phn%eTpSiJW# zi5mz}KbFJK-A`2ysatjJyrK5Zq!Fjv=S}z`A9FFZhL)r#0(JZ8xYn{kUnByPjd8^V zi>BQ`HLnz&O~+`(3`f%JAAMfVL4PPG|9F2td*!>lD?KbDK& z!F$YK({s+V49^y5w$yr$v{raC70kehuigA84u)iJW&~?q>Ah>%=rs$Usj^@LsZOSp zdc#WgW8?>dK&}0;ufCDE$NvC?R9?S9mAuF7!c%*G9Sy3kRWvhJ!_EV-k`in|9WY>@6V!Al+o#8%B?A=a#I|wt}iRxp?{)+FWacI+umSa~y^O{{V)Ow`jMOdDkSI z z!cKBoMc)}jsr=I@$+%QWbxx>maeAp;ZfmNikBk-)Ev?;@y-iraA-{`YJAIVz+Q;Z; z+&`%xg29jZMw(qUS+&MSK`7sn?i&kQg^$DFC=%l#^a=NDZm5BGDi^=HfnOIK_BQs} z=J8Ej{5I3dtfQ+0EcYdhii|rz+$N(PNZ=FLmPLdRKO!ydrlDF$UV9HC)Iqqp0^W4= zBA6$!Bq)q}Hv?#a57)9547p7hb>PR0N|!Qxv=YPGXp#5U%p4?b0Clx) zFATxcNhFU7Vm32Mys^&2`F*rsaLXuoF|c$4U%s(f)E(5~BZN1gCrw6_uQ2&P%H`2k&imoqOtzeq;`?+ej#>2=g~Q`s zW*QYPe%l6639`C%@Tyw*>uvm}U&ntI+U!%c;wKemd&iv0D_wav(%+Gt;bc# z<({jr$5TucBDx^%7rcC~6mLm~o7lVA#R67!_E0XNdNFIb8gG_lhY9WvCCEx`*%=b0 z56pz!Bk-z=rsDpkg&Y+rVJsrR!g7q$i9#btapf%jL2_^Q(C5i0f!pDAQp}Psd?da| zBBkd?%cKFKj-*>tdek{<8^S!#lcP!5;<}uh^&{QP&ONqpIE>EqNlS>yC9`sr+TDG( ztKKFPx@o^dqT;x^wd(Tm#F_JQWB|xOH@}sK92VS3B&eN9HR+mC@mK@1EV4N3>nbD- z6X#4RwkZ&LBq>W!R;G)V3J5V7PAc1O2SZZ3st->_OtD`$r8L?%1cPvW)k$Shi^*o3 zq_AC_Z;xfC7Se<$ppkK6YLjnHwPLcnRSyqdz|NIw`N~tO@bAr{>tfLMh<() z50KE-;;bUN3d&-^zil~PfZWd%+S=ZU?iiS&kVUMXwHGo9LhMH_TjA?f_erD*+q^?+ zj;QRFjVqt#HXLGBE0pDex8~BFkBF50jgpleo_bf0^Vhi}v-r&sp2CU6&~n(GWhQnq zNlQ{=sEvoIze@A;o=>E=IQzKz<2dz;L?e}Uw5VMcOG-cHbvn~E?al)_T+LU042|L# z$8faEf~NgvB?1Div=0w&oe|Dbc`WRs%_f0w@YeHRF)b-h&1@_$()DFl~tw^K*xV69@=)R05k@DJAzH^YH}ULr5pj>LbjcJZ&LSqfk}hb*#uit zvGtZAg z!^Gt_?7FS(r7w>`x~0^xVCK-+^!IA3YgE(M%jk<(Y;+{CvIud;!B(Cole(6N_3kX} zA{Z?uON!XSJaoAy&Z_ocpaCqXi~+ zA8!7dpyS=AvXpk~;h;6=`~Ltdzh|L$EedtkdL4C(?cW>6v=A|E5P$+yPzg5i zxvqN4_9xY{uHF9t`b6_wZwRIqz=GHZszOJGy+uRH#Inb z^{46Ew-_y>Tu3HIg2!zxEwDQI3V-(*5VKw_6{c^CEh@4_#<$XqPUAvXYWWdC$3j=C zHS+gUvfIydRO{ge;K5WwaNdNCJZiG^r6Ky(7E2ccE!4V@ukw#drsOqzqtLR`+LlYt z3iLmgX$sZHEx2{oxIKzwo(r32DR%TPJ~_r922%KFD}P-DuS3<>`kr^p^OpH|-Txj(!_bo67?)ZsT!TE`^kHgLE{P2TPACshqlzl-e6Rjc5|2StQU} z4-MIRgC;{;(bkm^Yz~wOLnwD&Wk!7pgEqr))wrM~BKK8O+~1ZDMB^@VUCKL`@~nF; zsgRJrPWBh^2GwDw$I8yd+^67_Xk2F64f8M+p7O6cSb8rC93GY+q86`9ZP`ly)+g{1sde8o#D(bY(@g zrpVjwBTb^+b=It*JeBf}2HVrFzWSa>V#-jdJlD=J$rv$+- zNMSepX}HPYLfEW>+?>)4lWK0Jn(6{oA%mGOi1f7{eT1y;Wx2&G8rW2%!IFqi2?+_> z2{olB5j#4(4;2ic+@kxbxX&akK|E^-ZNQ?7p1);hj#xIZbXNw>u+rLXX$c@(!q=*u zx}}h{SdN{?t$AE|TWZ)60V(O!QnfD@8phfMq9aT`OM>d^Jg-grpnb7bs5tsU1+Qz+ z@<6fpq_&`|)OBD1s+4?&wy4Qv8o|-LXS|JL8cP7(w1ICAeNx@WDLjr-wc<}9cWMT5 zd~~FQ1rR+=FXiLXkd~7jg~vqkT)cd%jj+SLE%9GMe#+&!Dm-6e*IlCK$KuM9xDC}! zDB!S+vP|J(S!Ds&jqR$h;Za2LRejX&w+!tLJyY_k4wnA_BM2jVitJZ*8=L#;_t`5YEX0A%kfkWx-k7c9xCDsX z=_(~ed&NP?c!%&n^|Tdo00z}r3nbYoMPO=MK2-FU07c;6(jI?>5Y|O229iH%h!?$h zEYZ=qU~<|=#h{`P_Yt?GkKL`A?js?j9d?Z>GKgrQWVQ|Lk)=)&Ofr)*=#SkMl8y+* zSrqjD0L4+kEFMF5(CL2)8fmgwVxKOax?ZP*VV{>x!9EqHmJQ{e`DV4#tpf&Sj*%T2 z4ZrwlgA}lPK40=jyX2IhuJ~FVy|ZC#<+r=4XJyeN*e)$!b6E8U<7qN zXjo&)abgDw%YjxvHtSJwUZIrGJdwW;`^UToPA z>_5xBYrz}yW5yz=F5x{YQG&@AH-bRJU<)Za=}f3Bi+QtuK{WTa5lbdZBF9_%Dj5PN z^5geU-&@I}MMXxNK|XZByctXotN2=%UCm_OEZBbUvYV2@i3=sL2D;EI9aF+J^soA< z>}8O=Ght;arCeM>&k{k# zln#LTRwlsN*mU2I*;ZwLsJKZa6Zub#B$LQ#O(EJ*)5uXH&|co6$pq15+(}Oz2UZ?b zQh65iy}yNM-V7GM#rRZYvStmvA^WC+Zvue|@J$ZR!6Dqv)p<}tsIgGkxYW>XO^;XH zoAK;ySxdX)Tj+0VS0BvEaeX@-mk%1+{KViu!NEWdttwFP(EiHiZR4!5mmhTs`*Z@% zoe@6-MnMOu>7_HP@%Jy(Eq_1b+ql^oNdm}82)9B7S;K**J=R61&PrW}U(B9#ZBo>3 zbQjW%Uky|rL@h2}QZ-x7n_ukcXy0I$p%2_9Xi@E?XGRVC%-X}t@u+(*(BHhpn@HfUWI~VY#O=km- zM3MQcMtL!Cp8zXH^2I$r@l=fp1YbebJ~S!K81ltKfB2|Aer%hL5V%S6so>0tgy=4H zte0SZMr;SRh2+f1vOziu#>lpByV*pG$s8H<&z(cuW_g}1`BZXZnV(E~)@0Y2?@W2t zCTEzP&y_Lnk~6hlRhcu%7_2sHKTDV%EymFi0=OY zHJgNpqXoj19*3r#4S4s-d23}XCVH>&-^Oa%v-6{rjP6U$rF@}vN@yFYS2h6nR~suN z=ZDpv__cJnsCnb4rbn{#9BKE%`?Td%s$ ztl3f6Q>VJEwYhw*rM9-+HvX5?knyhA=jR6Z#4R!`ZhgA>Qky)Ia>6HXCruxzmIJ&E zSb%`+Qne4)r%_Fj6E*5+xq^s-mP@p)K|nGD1vUg@~Fq}r6FlDnSV*PMJ?CA^IJ9Siw&EgN^F8-Pc$)Ngeop5ge-FMU4e%^u~&mmYJelBFr*s>;bE-1}>~;4Mvb zI*wWq$3lXb(u#_=THZC-Rb~!Gb>)KG7aMJ<^6F{W+=06AD#kHSC@XD;jcR*J3nc5I zveDulzsqM|NsSRoi~!vAtegA%e#)fT=M;JtI=p*tsnnc5vkDSek0qrAF52zs&bE0C36OtYQ3G$@2EN`MJ_jZ&c)N!9_QP3N`tTh$La~3qP2Z^yw z+VtyQUCK{6J!9fLOyzo#^f;>##g+c(RQ8fvQCU{tNVq4& zc-J3&NiK)de2*^&rlR|#bUf@I&BJAumK<3CkA103bv%<-l=kdx^hGPmI8*-sG@n*E zQ;H*^Zf*Bbr6!r^yiY$|>+jGj3y(LA#H>7$%A85uR5}}5#=n2<`0d#z4{yb{NyG5Z`#ZB%% zlGgUrRf4V}Drk=nzO&sk!HmjMIu{dp7_)K*PWP02i$Hg8OH^OL$hj<6h7n zT#~K#B-^Dk2zrUy)(E_ZNe1MTMRt{r7y&w>R7Zz}S!y&ZYIp{_VmkEjqs8?A;gD*< z1cCnm0-l8MDC!z4An&mIX@_IU0K)TF46`jl^=Dpj+ao zIN^nlVdF>%7u#)Vk!%JSSp_!nsbo~2;j&F*`os6XZCQJ02e>_EN|2yVo>Z-{UV*BfJCZ0_ClH`_Tze=sR7y4y zhBM!km0NFxDCvJexY{0z$r8rb3)_hNsl5X2M<;=r_WTJ-JC8NiT8Nc~mOtnP1) zH^rElX63j8>%I2_q0m~Yb+{jIsvgXc%fZoyLQkx|hU`ucR05j9h^foRz42yN88ym>_J0(`yH54l@ zZ4;_EB{=9$K2QR6{k2E6yW(1E)#S@h2gjBa(w3X*2{!Pm4V~6p!&M`*zMZ)A9H%XV z9#cVWg`^Om0J%C|{uSk4INh=}JRX_x9e8;ke0zVxVqT-bUnTjjTGYtG_{8j}JKpTk*$I3d$MEKBk#< zFDz+$gKyhN?rYma>W1|8VAwU<_1QGY=euEDa6>ZZy9+4+QI zO8}dj3P#AdgsWYG-^emS1lW(dmy}1U(~(_r50sXLp)LSa50_I_dl7B+J02(8gUd%9 z_gjy;%P&6}Wl9=_{ua~1x}FDNH{x`=Ex6?Q?arK*(~o+WNx1}HrF%yURD4mx@;#Z) z8Ht2?fm)qSc6z$~PD5hV*Ckh(nte6v_SbXAu7@YIRk=s*RG@15iqa~si+xBm$wunF zw9}?p=vVnH5ovNxr*}$vry(0+fHEhj`1SCsjauaUm0JxkLgy?K@mjhSTS%vx>M~sA z8}2#*r5Z5k17TqdS1hD;wJ?PIMv9waZhG|VQCjU!i@>C;NE<64 zP5v&FEX@Q;GFbyZJ6}4m){|?gTeSrq!2bZ73)JXOTCjwA72e>OaIWU`)kt3HHY}Bd z*m-!-S^<)0$KOLT+b-&x>+Pt|^qDL+U1)_1Ai#N)?kXRbON$Fql}Y5)msuZ?ozSnBnv~hZ7+&@xm)<_=bu;dRnXJ zW@I>%W)})%g@h%g60U~F#*OZzx`TA&u=vkyGWi$SQ>~WUSq9`DpLJL4a*^~It551q zc^_+};^KV7a8i_uxnXY_j+Z5u)ZDdt6IHNjja=_o8^QJh?9rv1Ze1d`y*do`rk6L3|pD7t+@Q;L_Q9R?4^8QOH z{DbQ;nIy(!-)d}p1$tixuUsSeoTl{KXCuv(G3D(5qpqYH>ecC$4h|(R$nz3}hmmj< zasr1_YPi>H#74QJ9j4ZM))P8Fz=_UTV0fD8`szr564g5c${MXoPe z_X9=bWTtP^Pjzd_eF>U+KEwA4&v3yaEKan$i!jBFYsLLdpR|;w`dYwrHRA~Aqu5Y3 zj~>4IR#NIu400Q|2X$*Cd`t%HP5vQTa_UYsF>)%xq9{=*y~+D1379QXG?{sTA1ZWT{nQ0cj)&2wiZ>NkKg)cc8mT#>pwsx>*2gQcWI6Qt%h+VT z>#*4Xiy=?yZ{%%UFs*sm&h!_EGxT#&Vom0^d1Ryrnv=50I_;qm->8BLR8!^wx9qChs#5M68xxD@$IU56ZuM@t@u00}FF?`U zo9Ni?-c_#z`x5tLD&6(1`$l`R9yX8wBdrI)3@aGz1gKxFB|66Hnibl1?!VbW_5%5% zZJ?xi(w0>6ZCHmWLEYD-YIXHD7CUTePnSwUQR+DsJqQP_Yr%z57DtUR;=u}5uwd;y z1ux>vgs^QzjIvMN7fQkSK((k|ER&KqxbhW-c_Y`XG~|yYT%;P@dxK(|73EeLBY|32 zkfz0XQy4TZgm``Cri}`EBhHxf6&?Cx&a)`Y3O3mIwXNjL3Wog6VX;cXV~w8L%mqyK zfTZV)7$|^Au%-3oFCr3R`+c=*7m!{?=Cxs?!6fvdnrzr1La^787p=xv1bU)j>cbCK z7<#b7T3p%d)P2Tp2ELf{rj7ZUp_d(4LI&b#J)MM+xmu#K$pm=lj*X;zN3C;L>^I!0 zy5}}Fl9Hf&JiXM5x={^7z`?MfNJ0(Ih!r<{nj4-yM~ZdEn^7Xh-ZfpdWTO#fT9l4u z?ra%6fnqBSlXY1}+H0lj$oZE!*@Sy{!Zx9(yrjatyx?u%;@QT6*1bxI)|%qDxp$FY z^8WypOKN@k7J0_B0ZC{S_|?stFvnwvTe9zxy5*RN8xoSzN{P`n70z#JMlYe*@Enb~ z&O=;xjzw-F6o$djT-aWiR=Qjcm0@QL+UzZ_@)^N* z2{FbYMPwAmN>Y)nfTAuXDCYuJ^Oo5z?4bLVkcW;BAt?#pac{D?{rZxdJ!9comR*L= zo4wQj0OY1dOQAb=enD?5^{f64bBuZ(k;u}L!Ot>r4)0joai+f8*IQQ1y-t2~A!r<7 z`EL!al>u&^6j*Ka2WGJ{iN%~l@mNbGZ%ACx6mDpkXMvUrW&(J_*zGXhWDw{Mmm?< zb${8zXG>rl-HmP82ncYCn(MgvB@@OuX#W7-YCVHknF*aG(As_mw7qniZ_KRZDRn_- zyCz6!HUi%BSBW#6m5f+62(`8IsnsUmP)=s)Xn1SvtRn0cOBU;`GhL(|qQnFXb@HXG zx>*Feh=5i{nvD%f_X_$J77e2vu;Fj0(D~Hj@NxX5if;sWlDfBFBhw)&t{a(7Ou6A; zqaNiI%s7F{YVF*h53$jehx0#?-(2H~GF9V*(pvl6a!{$YxnR^z1$-ZrUYSA0Fj^*?kcv=3PL zN4}b_;8Lh;vIkN}x|ocz4t>UtJ9PeU!lZy$z>-y=B>j})6tMuMU;aMIJxs5hUO~=+ z4abj4eOfmtbOpGR)5IM`EeW>7Y>GoCdM2+TQ@-ORCf+@1n9C$542)fF zc~eSV1iU(r*-OveNKMCy6$f?=`)8C?-?Q0UUM!PMBWC9??7t5L*&#de%v0LhHCR7XbCx@EM1ZAbxB4ii?1H<(}l%3(4Ncp=}`UOSasfwyQQY z)Vq#VcaLDm<*t`YYTXFTkpn&R7y>qhRV)# z(pIf{-nvLj2&B1Xk;xB@!1bxt$ln}{R`4WPU(TNp&xnH6y4qUQI|<~|S|6J;1C$kO zyX>Pzc{YsRf#k`VxT8jSBhX#7TmT2<>#Z$hu#8d5C0IBIAtAP#8zTkfv*R*fN{Z4D{YcID6$B}ZPO}q^?r5L#Yy4hglIJ~TO5D z{VSo_;Az^cuX3+jnw=BJp>x0ArlvxO(-^N$AheHKK~>X#c9qIm@Hd{1q0`s%2w`Wr zGT`0GUP4oFr{mJ_aYPq^xgI`M&2zsNl`-gDX!FX}UwQ1-6CVaM(KfjAq<;-M*xKgx z$?9=)oRVFKmgJ$ICPm-M%AF=WnBM?4u)l!!S5qDxVF=sE=cY@;iDuU&u+!uvNg8Xd zP;wkgi|kVF!ZTT2%Ow151AR}jsufs80_h-Y$Vk`1t8az}Ym))VN!qJuMeJx0MWr(8 zLGlvaXs+O_3*OY6>Qc$nYHP+tSIGGz(Hm0Iy`)^&_}Ef#(@&w+>grbomQlGbe)<%@ zN%EpqszLku`|0JxMaQXU8gJrX@AA=s{<^ZhR}F*Gt5rwe=!XWN2(@+90qae3=$~@=4cPxP&lP zHVs?m=iB2=Mma(jO9Lf8f7O3>l;k6U8P6kU-a4Dy`BXd`W}Te@Z%I%d+OevnR#|Hp z{KEWA>cTO=zq&Db``=58RDRts65MN`!D|Wx_}ZGLq>#Ov4qe1h1%dISrGaB9St-3Y zfIj0LAd-=Dtpm-2L1-^4dJXkYN`z2JzNP)Z8-FI_QBUV89}k$dLhs8EF<+SJ7dR`B zSYku11H7b>UzU=yq z#=gBpxvw0BD-@{F*&*eH0uqpSd+K`oPA?>U7T??L=r^o5+e15;ak^$*x~(lXg5N zSW~Q{*txN+FBu%Lg=|RnoAmb8W3Y1sZ;{1DQH8Ym4h4b$R=yNi-GFu@iu;Y)JX6oj zl=@L{6l|Rc1bB+G(>CCw79DQ-b@6!db>fn7l+qIVYk5Za^I^U7m+7pLSB;q?0)3 zJ;FHyVdrE*SW?L;Rk{l4boqE^xm#|N`V~f3k56bRN{+F&sI^>2kgfJA?9(B#puCZ* zN(auF`^f!8-mH=XJr!v>qwT4Cpj8bqnwnC&qor%z45?#9u(NF~S?-(mhUJAH(|@$k zBP^LNhJICl`efd=PsNU##y;ij*H5;AeaJi$euwO}BEWkq$z-xKRb!CaYzYM4#;c~V z%S4t12AX+MBJwW90xki+S|$jJjQ|xam~0IWQOIaZ&{Q{ZeaBkbCy`O01In<_s71gx z>sVm2Tk%S5?WRmO^ws52$~WT8n*;&&(h_|_MGqSQBp*Jt6(*Q;hY1_6;c9o>HGx_g zu}1uB3>OCUIY%QkT4zzv+M(@*_l>u&uH!=SM%9__WV+$%vTJw@o(~sRPg>kpQ+`bK zSOYQ;Xi;Vx?Do+-k%oOgeP^0AgFctA)_J20Z1&S0;g~fItwz97(wa<;Wt<|s?+K2RstneNvy^SunNq_BN_ z`gHo5;@#WANX5LPisV=Z8dUf$t5vqYggBGqsIl^{8r0~Qg!*8k`5(n+8T-`!Xvz9| z6bIFRe+O)NE)96Rqimxf%AAoKa!r07eGHYus+sHEU zu2~{NN)q9@wZQ;UC1)v1Sa~Zy(wY~;5t~o(!!JH5C$s(3ZSUlBUE=wLl3T~Ppti{a zpcgtHVW;)Jd$C0PF6eVS=fLctc|-HV>c8ZBJ=*R;gJ4R1ggp^o<-XFlq>pWYuKSAl ze!c#j@V!4$ z=tYkZ-o)xHdJ!4+9aiuO4-%c$9?FYkQe(E$VgT@_T#!nK8&s}?K~(d(r`VT9E`-=> zbu}fh!h1;E3y>|=qNUV_wml5#@f#Yk1LSS!k26@&uOo1RdJndcb-a@DV##k!dihb5 z@E$VA?JcMgtt6-97In16(uEKK)6COY1t#z=+75tp@HDeXoh^?mLnD-aQ9-mVeTAFx zp@khbpUtfc!3FRfwmQ=CjzYv;AY2~<)~lLjgc`u;ASd$mG$#zQZQyX70zvz0e%d1& z8x{2b0L4v883HYFTK0`#&)-^g7&`{qimz}nG-0{R9|q^y9?0)7`+S4o5AbWd9yHQUnB%t+9z*a(b$v@f|ZJBeU=YjEnKbUw(NFt%0W)eqWf8k=;Z)qzRa&7`yxi)m8Xt}=X}ztC!fP&Szg%&$C_;H^;|JX4-8(0P@nV^(l;3W0%~y z&9)o`1M?6(s^NH*Z7^O=LR)2Cn!r&SResczVbFU@mgUsq9`qS3wt27e zSGi&IpTR7OtD@_Jf-+CNkbTfj@V%ElUXQ5BwlSv&Sa~C_Q~#Fl}tq|(`Q2C zq17=Uhp=Lx> za>@b^hMrW+Vw`%MPQss;P<~b$PbDVTw?kC>`Z18+hRmYKOzdb{NCVwmw$~h*>~z|@ zxMUA4f>VfEK-R{@R~yK2Z0h*_RVS04c`pcY*r`JJ|+lh;mt&J)VFcT}!~_3^Jks&5!7XACeBl2UKf z8sEA_p%)co6H%3humX_bDIj$56x}w9-~%B)61D3`R-&F}VAg10dvbb6o@ ztzAar+-OmgKrCHBZ!&cjrlD^@SOY%CTZ%Py<(iVp0=*vO`+Dh5ErgZ?)mdQYV`@nI z>mzf>XHP(DjHHr2<4mj2x3_>p%-dd?^rP$8n*)3>4|hO+8h*FbM!;L@xH|j2)ZKl> zY+$FZLAU&AOx*tfAUM1O6xmk2Ez+T@H{@->KA_jpVPG%iR!-j)ku9iaq6auSSW`5- zqNd8;a4>Ne_ZoUL$SFeu2S?$hHCtE}acP4c)cvNMuF-i%A_P-)Tms6?LTgd8ApNmV zQ`t@d7T@HmggXJBb17LWND9B55dgEWN+Ju6gp<^ZQRP1skQK$YblxY6V`A{OvR;x^ zA=9ppVWt(dUe~n?+zJ|w{_A*Kr7t+-Vlv3>$%t(>>1s|3 zsA)?mi)1nzw^>?vRqZKYuDFTIlvHih_*Il&LY{A^F-~7qtFKBWX6+N)kKUu@iu-2v zsY-b?s}^s@h7k9u?5r?Ii`FJ;QOS+I+LB~_u$)E|uq-ZUI~Ocb(pB!hwZ!vn%BGRk@d+yvk@bs;@($k2;_c#c(%DN2Xl-dk0#paKyh`0$Yq!Ys zf4Htt!Owi}fnXWmljH8G*o#rJQ}F?RvbydwmXrLlDE72dj!{W1q{mY2rqV#u%DSy* zlb70$he4;zgs3Xw;EtM8E~OzF?Qv(-f9cD~G6x~ec^f`X@1(Y43l<~>3UNiJ24k);H>TYOTH$xJ z;~8`|>r(0}^otaLI{0;}d#YFoRU>y_)V|Vr&_r9{3fhOTajtkljg@Z z+PQLo(IAum02Z%!;bDj`spXub{KXHKU6iqIo;BQQqmobG`zxTTyml$? zn;{bxqkH(?^<@~MwY>Kt(n&YDrgh@Pr`V3a{id&?Qp>nC-`DS^9IzuOQ}|Djab7OEAL%FyVOY~MxU)Bu2NzSlYY8eS}_*-1IPlVpHWu?AWr>BwZ7V9 z0iYiebge0%Rt_)WZ?=fz(2T6gHfsCD`cc8K%w&YDb+?5|DG{8oUOl^#tQ%>wsj?jEUPd!foU%~Zup-)l=T%ElcP5LQw>adY$g{aDP>#i+?#pyy}T-yZ&Fe0I;MAT9LV6aF`JbE#JrV@rCMq# zg8L;IVU5a36V_S*yM$;3B{$S6FovN#=}V*%G+ob1&o&Y!tHa||LYe_(BI3kU)$%}P zg9YqHJR{XgLi7b@+Fx4UT+DL8D1j~u?2{d7I%us8E9|x0$k>@ya;{S5In}m-xB@}3 z9chZ!LsH3AWP3|M|4?nuP&0W^kN^5nZ zR5?x^>D>Buw}o6)$bKbs=pr&WyQnHDY?PjZU+$+?;xW<E9D8~ZHKq&;?m?e=)T)|C>@}><7 zBme*uE37A4=xt(7o;@n1X&3)vua7xAWI+AHL;&C)JF=~}?j>@Ed`szw{6ZXk5M zVYIR=8UWmz`zu)ppeUajVUesQmi zAP`Lt+(w5!n!S||Hc#4Oo}a&3Dv(8jWrXeLC*&wDz{V|2BmSbk3yWNly4M1^&Ci!= z(%Nr0FLk~*vtmuGBBT0^=6L@Are4@(dH#5_H;^LC@t!nRH%XSU@RbkTPgQ#bLxkm* zDX^=ns`X>r+$?mf_iXR!<({P4w3F$8x$#XJy6hN(~*Yhx(JxaZDgyGi-dC5K!JpCe9@#Hnq}jYs~o%hE=s@ zWV;@t#{9C)dhwozJ@T&ca*k<`UPqDTT*^#e!|S-U5)_tA<&*ZMv`HbeVn*X_J(U~ukZSj*^(@{a%V#1=8~Kaxu-Z8f3ON-#spN0&{`uXt=p?76m+MJ6vuO^HjlMl<5tGPI0rPF>b*I`u(6~ZJh3c|VV`At)AQ7M*6crbf zL&PT5H|i?t9fc>{Q_=qb593=i0-l;_W={gZ0nl2M8AFQ$rN0W&iX`OOwJ{lFh~Ygz z=sc(llowHCr*-w|Q1U|w4rx(=Wc)Yry>H($1EOeN;i}(3!$W$!tx%M*GY64$=tsV* z?Cd!ngYQqd*qQ@VGK>^LLV>+zLCRP*^v9iHuQPgBiR!}-Rv2qbl094UF!gRy$)i%6 z^wOZoCLDyZ=>gJ{wm3yz%zrVX*4F<375Y#dQzcj>Y6)M$ElAlESVlQQsgtvzxIIMy z&7g9lBF;!A+EnhlAmK>eOSjugPFW11FmhW+1gL>;Z6z&*>NgE}KE!V$n}~*$y}`3v zoBsfPQf=->1=!!lC`XagsQ^iYwoH=u(Ok`vw*fx!X7`(qr9}Y5n)92 zI#K@j0@ZwGaaow@Jd`zWUEj_HfHjcaXanJVze)h|K1Ad=<>uWAN`=pzBfYZmlGlde z)syjPS5H!@#rEqfm3T;n2&K0QKsGv? z_*KPsN?j0LtE;c9o(|9K?`Y*=cFV{#H#uckTTL{p?kk0yrR6TilzhkK^*Fdz`khMN zJ+{CFv1Ea5E4Zst3kQ$SZco&6>7^k2HXapV;IISDvOhK=Us&AU;0?f`y?-WK>ufl# zj9HipaDb-vqsK~;)=BmUX4;QtNaN$9v=gmig~eWI5S|9 zO}bXiIT`eaodOcUA8_`-I?>U;BLO$-T5Bdb3b$mQy=p`!)H@nsLPA2cPfCQ-WmWpw zc?l|dRtDvDpw3O9IO;SuT9B}BNzRU)Lq#+RlNk#a+pSRU=_XiHb3|Vwi($kn=ozw5H#G#V&PkK>jIQI(}#3Venk^Dt|Ks?2Hk0DA@!R)^f-j%H<*yg;iG*%V28>v@#%jW>G;Yc1=MQ!uOAdp(_0E9N=e&u zYP{669R)Ze;6)B%z{AfEtec3y$19zib)55BT8z{KmvP%MpYr5V0} zr96QgPsLKIY#Ro=jZMd1wEqCQ3bYB3+5&HC9Bh_CPPCRlkE5@R{{XVBtG}p&uOOF^ zsncHyYBw?ptUQ9BY~RbuomPuVw!!YUJhbwnCXLvEF0=vEU)kkb_b>uf;Q(Yesj#Q& zeM-w>!0)H+zO>vf$i0W0StD)9J|o*gR(Chz$#y4?Ns>a+(v+Pppb7TX zeY?sp85`8qY%L=mxSB1WOr!#<)}e2b4Q)Okryomtr{N-+j&KHVrU3FMXh!3I8ayf# zc3_Lmlu5BVRgDN%%N9Y4a3~Tle>$@AULvon61@-HGX`jaW&+fk+MB_=3VOXi`C(eY ztQ&g2;Wexpz@_MZ;g}pUoxs%F!ZwLMGB_(wkFu(^r6uADP*xCXH~T!XK_6gbwAlu_wi}^78P&5soI^I$YL{isQBrtLf<M+hlUS6;r_ z;CY%|R%IP#gp{L#H{O2PKN@0ZU@_#d;tHK@v2c<#Cqd&@95*aO6CYhg6*|j}U8}<+Q86qb<12 zxz@0DKML;-PHuiYjGk3qql*V|AOK1HYmVIDpSP}V8jRtOHEU{ytT}2I` zfI@tQRNGvn#iE;F&zVV8%CCNvU3RlQuH*~u71eP%3b|(;O5Od|)(%wV zO(ZGe0oLaFn)QAqrjh6Tgq}(RG?0By3lm+39|X>OZrmcU$owRddg?0vs1%pBh%DWA zD)nO;@zkiYL{d_$P3q2~vQuab_v!m;!3m**om~5ArPx>=_3kOJK^#9Id#&8u^);%< zT{I1O1tR0&B-Mp(MSSpvYDrD5Z+fw=#3g9mK>2u@oSFlHucz&%1;=sXm8m0XQi;%1 z9MUGB9sdArWEw(UTvesDScMdtQEtIJ1D2y}CZEgHf;S6Okd{!Rsir=S1eFasc+*jq zNmy)i(F8AUN924EaaJS?n3@Ny4q4whyMUDy{J-% zSx*Ilgv}P^A>=0N3LZUaD^^X_V`tH>%6Cf}9~jI>{{V!hz%DpOhx@CK-_p#oUR5;G zFUNb)i9_y3ZPbtjkgm5iT=XU10i1}!`@YbUzflEAUs99M{j|^7NRN{kKi;Q?b^r9f5nS(+$iyUNu3u(^u&z{6L%3 zZ-fkTYq2PQEk0Xi$uAcia{GxigROTqu98G`$yp$kCAE-0Nfju(5c6a&Hq6)3r&P5S z`1Pqmcw}23Z$OxLD4|4nooVlGEraq%t~0V{B}E8HNEh?Dx4N2!q#tuV_KLG0#P`a= zw<~wF{{U?l?VymnY;EO`%!UeMiZ^LT*hZA(7mCJB*cYMoZ$3C=kJ zIl>$)$-?(f8Ha_Y{V{7pXT_@-%DF*u5)DI+chW#}1s`+;&F5mW4aKo;`)w5=A zQ&Tm8K%0PX*0=5%fkB`Gv)nTR=s*C$Kt8{)P#XEvr7nj3p;6;l*}9{HB9ybR-7(7? zgoNq^jTPA#qDn2ZWa^<&b2se{0zfwcnZQ35ER_>$VZpjYYm>PFjLd^))*;R zV68x4^s~{mWoQO;SiDTAr1sLct|t$W}HQ`mA(2Awr)Qq8m^G|)(B+=VliTahF#Ilhb%RTDjkP?NWI@njT{CDPF+VQdU0J!LK73C~VcPgdBb&|BL8%lZ_^(r|SShAmUkl5pG zrL`s4Yw54bo=mIpX8xYPnwkYT*I!Y{8$tHces3eOucJ3@Is}bq)}c4hbK4a3hncB@$lxO4_oagS zL#EpJM!$tC|TYg68WGF?T`i|9NmDdgLT5+adrDu(+3MGlG<&Z~)TCgsG+QkOk?YpI(=cN6KXIGD z<;Mc7N$W?*2txqL3rbQ{NCv64rlPu!Qx@46k;yTUii45>Qb`0~%DL;gTH?#P zcx&g`4w=lgEx%QzZbyNrM}9Nx2F^BByguY-FUrJc0}O(<4vD4g-NWczar_6Ie5Kfm zX0rJuFj9-}P}1J&tl7S87yNG@N}gY0un~C{UE;&esVGW?`i(DC(xJ(9JC}Z*ynK@C zNoMfXw!%`Bo#cEytWO<&EUknp*U-}6Ya#wan1t)ddz9kjM#i-* zkV4q8-Rz>vS;jp|t_yk-xxw~jSvMg&B_Gg8B}yS6b+(mXD%`pq-nOMwPsoT(?dC%v zE+SlPrMDE4rCR4-4|PFlNxsFi`7J89*Rj^zE4T2mu%e~N!6hjcO^5c^d%|#@D=hGT zl6>T<+-mJQ0*~Ze3CF zwY}8_$g0u1a#eA2)`MvY(1)I|DAud2$IxHf>IR?wQqTN!sxC?i!InCosO28$=A$t4 zsFJ$`?kheT4`q0NFJkwbJ@4TBblfu&=ifi%Xq@uU!Pt}&VmwlOXqmc z5$9V|c{gl-CIr{UYv=v6 zO62^A`+Y$Vje7q8#j5U`@DAF+aucqHr}ondK)g^b)SX4Itua%06}c>o96&|Q?{m_R z8Fd5P7a&*@?-eVs&1K*bA+Wm+m)47|W*9ploInqwQ*X2EtD59h+YY$|INiUn)s$s{ zDMtebd^{^v+Ala@VB$r}e&a?~Ayp2eClO}Je~wSv5{#0p{Jc5p5UKRv1uXBoqxKYOkPmHirU%<>2JQb9N>#s z5w-QEE0e$(#3sWfZXgdI3X+>b^Xy#Q69|;cVq~iXAcLM6bP?QbqMmvyi*-Q> zxIP_e_}k)&$6IW%c#jI1lqo5UrEdQK4^!;DRPA!mitbv|#!gJ{F2{Cq4t1QCDqK*R zUXfc$aR6BD^8&o*m**OmQ#&mlE-{BvkMEakhYpzgDL_(Fa0nLYYoz$sB`aczE^kpp z%phTc^6G zv)El&U%U%6;-oR?K4Pf=S$Zb=*CV;D8Aa!*dB^O}%^yNLamb?4)I1zO|u+v}?2{x>7 zGIPi(eA%XIqg5x8vfD66B4no-sc}w|uf^2pO-gm72i%io!N%DWR?2}Z zQccJ|e}y8Vx{p^UlePOxkzP4TZNzNcZTAY`x&A&=Zb<8RjrCtcts>;wah472rHxWL z*PPkKN-}*8t~=3d`jVDk^qUK4-^7dQN>4^!gD}}oxCbGdbrJvz+M8O#N@rJv^NvL} zIS&Wi95U8AFJC&za4*pXk24kOKK^(%*-96xtN0l=*fhC_F2p;K=AU&6O9ZHm4YauX zD(6$;NvyKE@02CjrE;=zNszY=?VehjQk;jyQ-|eUOHM!KxtHbzX7VC#HEg_{p(!>t z*7mLj{w`HZ4DU607}&CoChiK$TM z7L?B>P(yA75xDfO&x(&ygLURPIr}z+DNcA^yWLL5DX{t#c1@yp`1Q43)KlC+CN=Rd zO={Am)ULgCBT}QAohsU-R9Xggq6sBBQ{r+=7UC7U;C+jky(+c z&ER76T}_j(jXhiuTV%7;VB;i>yE^nVSae{g*l4_SnrLZ-ED6QrZSqcaDL;*G$$}G1 zQcuJ^)N1C)xS)(SRlO=m_<`%?Q<};a^6FnaUm}?p^}w9Sd=a%PP`MseLA689QDsHB zk5W01<92mxOF$t?J5g?$)ux@V$EcOD%Mppj-6|VX*T$UpSf{myPw@D6;sC2}_VhG4 zO*U`s-*QK>eKyhk!cmR`Fybj?HV55Wl#+P^HRxCvXiu=c%1Hv_OI2K_I<=oLDi;97j@8riZ%1jP78_ME(fG zg1xPEI#i>u&^FKweYR(9y&$h6r8u_aPsTU|O6{`>O2)x+;bBBuk_54U6P>DP{9cIN z`gEcF@i?K8?K;*@<}L?I3eVgbFBZky?JmUX#qHs=)Fr&KF%5D50RI35mQdRiTlcoo zk928kj@#{mm2I)Q{{V#2pL`STm|iDpmvMNu`zc4r1+P^406L1j@@FQ{?wAKSWKkcO zR3&yzMi>^LJO@v_vnITi)I&ysp zK06)q@>vqpkUSEv8+cbQ%Y2PhTk-C7Tt5dVYI5vwcl8$UUVDM?j5iD8b61UbmIuF> z&2Ms6SVC-2lAkhlt1sejk5RX>@3na}%P*XA_G{~Vb@a*k>zN@>y0WJWiX@dRlX6M9 zHT2htT@m4s%lyD+-$FnWTMb`fcQx-X6v^4#~QlDK558ov&Xt zeB(-2S(WI03Gt1ns(c@+KE-zPxmYK0jTA~p#0JnvBwwZL&vka)O~(fz@^6aYZJtqC zMZo*G{!@=ErFTlLWn5f%+*R#cPQ}sS;OegJ3$j6Z4+2zpo~$&NlAq2xk$*qBx?VS* z+17m*zjgYHBLOJ+b3SxK^}iteBv0WcUH4FD4Vk4uBRKQHdU<&7Q0I8l z06sv$zY3wy08PHy6r_T|;BnGRpcaj&nKXGikSe-fN%t|15&!^GkZZ&P?s}J3 zESPOhniNR4wxI2bWE(T~R3&0&7_As@N;X9GW=s|u3sJq$Z1wAY>6kNm-8`?UPkg`Z?>~6-cM~Fb+0C7L}@k<@KGK#tfenQcp_YlnbO!P zit%U8)z0#foYoUM4jS64mSnP5k@(D~WL@;B(bZ}-&DW(lKD|!9?ryll^7JMxVPvrz z(P_eg;clX>R*aJ9bCcxZDdnRL@n+4G;&&)&N$YyZ@pAPyK6Um()bX|yq^U@_9tT=1 zc*T)t%*iYv`#3fkH$zGLsOyY~rM#r2-Hu+yp}}I0c9d11yI>7_tqdB#x4(AHd_`;a znB;DJv&IFc;8>o)j_ZE`($=Eg#i^_U)#YbY{FCZMas9o85&r;DDFs)({J#25uZ^V3 z?Z21XWN^uvc-&B1U!a?}`E;u|aPW)H4sKk{omP8?n`9sa+i3!>Xr-d2c4w&Mx1hF$ znzBEX3YoiX+?$%nJQ1CO!2v$nK5z}oCgC9UO-{L{N65%PzvZdT^djUhv)fREz%L`) zRpmxeH{^wF?ERGBJejcats2RVp!iVW`kTp*c&8MTHfiafIsq>;RU>bW>kX-7LC%Bi zsK_h^-yi&1v>&0LO!@v5qh4d}!3|wT;;Z&8j9}Xp#%0i@`-9v_xHyuV^(?l5zuoMw z6!{0qJfz_h(!M3|`Rv%Aq4SHl-NKojlK#gETUEucTtzf_OENx<(c>YjT7*t;%1T?y zS>3No*G)o-T?RbfNQ9JMa3}4l=b@(@fL>@NT`%ToJGLay)paDmvlDBC^q^NM`36x~ zBa-yKm#%^v3G^%dtP#mSoKrQdA$dHoEP-kgmO^`Aclwd-qr%Jz`#^`!U$%>Fod_fi zaA*&eLLCvgR>4k-*mUbm(=W_`*L!p=T6gRV}WZceZ+_gNg7p*ATn3yYOUlWIO794(vAQ;+kmMu z9(pu#+;>UzLVRv~Y0W*{bZk>H=6RJ|FpC~Hk4^PH zs`l6EiY$C46>ejao$41^Pm%Mrc@F;o$Y&`$9nQY2an&7Vli++ew>(w(ev3@ZOJ5Y5 zBpce~Ua0EXvimSixWOL^exv!I zJS)=b{U;N5Rz@9Y5J4AFr>e9@+J=8A?4rAYdq(0R()~TvzjV)NQMhDYe4NqEx?dSKI-)PXmN?b z-6Yr@$uBTe2-&8Wy*payS5G(?qI;=%DpA~kbu`2%Y+6i~E0sfx*-8%Q02cNeP`=j; zl&63M&Pw#{fH}1YJvv_W-Bn|&2bS2uxvFu`&w7hsVOe8wOggfUq>y|qNp5YVurWrv zk&gWKWAPq_l%E?G74l4Axc@2B$ql9<9kDx8xF9 zZLoriB-{a9uQS3^D7TKMZNhW6MdLjVe&<|v6t*3ceZ(Y{0!F%CxD9SS!pt4w z`TqdiJb}i|d4^Z;B->7%ZC;DS@rk5!`iQymFMIrK)kUCNFBqhVaXIX&#x^$6l2;V#Y;OTV}H`Pc_xEZ*ij4 zWB_C@EYPexJAp~FdU%^t4#W@I>RQ}$2(l8Oj}I*= z8}37OSJ0TR0ON@XE$iTIN>_2YbT^I0<{{JswRD4~lux_BqolIpaEUTvwBlJHk*<{= zyjku}RkSr<2bd4SgL~L~CXXH7-9>y!zZ~uK_Z~@cq7t3A)5ewB-Cm4p8sAdnZ?^If zggfY|YhAtqo}YzRefB*`dpIUI?%&Kp5*laY7Wi$g=u_UusMoUDC}+3%b_jT|qq@M~ z{#2d2srMO$F9n4MxA`YoZAxU{ZkO>SQvU#b7uXaklYL1}1BYcHxToJv#0>zqg(bDS z=rXl_q^ZnkxhpnTh3Qz`k`ku?hZ6~xRKt%CQvEMutw}|TI(z7Nd)be1Z zcDiYZK8EC@lVsytCh9=9wwaeyVuW%!YZNYbLQZAFAy!?YD(&;!RxLUVZ>NubnB z6nB&x2?;9eao56<*tNxEa*n;pbY9;f?S;mYpu248)9k8EjzUG^ z!kLV+X!eyO3=L@+TZTDRLy$Bc>d$m9wr16y>7Ll>JqOuFS7a4+k1y=4`+NB^YA7schNY5bW3<@=^>3?0fkX z!Harc-@=RTf^1sVuW;InVyJ0OC?x}NG_AKNEV0_OWjD~3W4LxobahGy4X~XG)u)B) zncDen6-x(Qr|_HZj$c7^z-AM{rxYAtv+k{bsiP&7XZmVTmy7f!Smz?ci0h9uouFJR zOZ}D3ZSr!P$$O*5#u4mtM}NEL{{X?y6`jaoEs--G`U4RbvHW!Ju>cKz+PC3;Jh!EZ zdYa^U1*sgLDf{>JKZftjXwbRe88Dz>lYCo|od!}Cv1O2=0I|19^>2-QV$M3x$oeyd z<~i8yM-2JH$v>1cJ;8wwm@4?2`I3_8zk%2QuT!bEx||i&wD3k$wJ4BIm8mQ|kggw7 zOA60)FSd=+t+&$mp#&Wz&6hW|D`N2;uDp&DFKw)%s31D3k zi0XW)NxTkMc35eN7a)$O!l>?oi!Gr%k{FJwO?4g=D;^?0Lv7%;xRXz%)Gf3XEkCEF zavZIhOzC*)u=0+-;dzN|rZGE^v2CllxFWodn9@>tJvWNmaY*UDG0gccIn+HCYp4JM z5x9h!si%?DYAo6uPtNXVc$xa+!u>HJEzaHl*Cz_vzHv3Ad>Ar-j&0M|1gXgJHz`k*s$PC3$AIW4ZqiscNYvk7 zDq2&>SuTR9>rri*it2A+QOgVBk5;m825T~HA!Lq%l}w+HTpMln);4WGcAkUWC>IRY zE$VwIXv{VTY1iWOq8No(H#YJ$7BhkCci&2}xWgmlG`BtwlA?bqMad`Zr0>~;S5_TE z$GAmMCq_zKkpsYLoO)+GP-`Q5Q%P1U~?Vj3>4S5^Z$eFck zD8WjO2p;s$oe}d6D!_|f!tY#Y0jhj7VFNi1HN2w9R{lP}hL_sx{PVQ#0uir%$rA;Ej2kA-=j_%+c$w1Z6RB-8k#WCo^QS z3XYx>3RB6aXF*`qyeRkv=4=oJMoAI!G7+uKIfG6{-ZYzmqbkt!t*(ZVzoxp}RI+Q$ z3T+i?kUt|EO=b=G9@+jX*`7vriq;Kz8WaTU*0iI7YFRy7FvguL3=vXECc{Hp3YeH@ z{57l+_Xq98E@%b3ZfJJAAz9ymc5%ir_}ATWdkPXw&2V2S-&NG``i^mMo!2CfnVs3> zuFc6!VYdiCSpi@lh}VHiN>=3i8{(W|qgF512Pfv#RHdk_l1{#7TDhrV7gFlTOr{j8 zn|-uTbkdJdGpu#e{>dke7WZ&CXF6<#oE9wJ~(Y>r~;%Qh|@*Yvj2R`3W2BNI%X^2@`z%kZTqib~cR8@;0 zJ)mo>kdy2^DVt@K9=ISo?2D6XjZI|*ltAyauhcAj4O>EaD=n;G<5wo%bvqe|`)C&> zzQ2j4>a-1%gPv!t==9u(Dsl>agQ)w>8{ABXvI~AW*YT&RSr(l*1U%N>iMTy#!V|$? zZ9^BDwe+>H2ZglsGau;AmaA<7fJ#_m#DVWb9vVh4=*lYJzp(m4WD*)$O z{5l%9tFcXdI)l`mr>*Zy*C&zy=Nq-j>O3mO6ZH|Ca_}>A^y$`(35(7-LPUEhn%e+A zj>rlZJ!qaxiEZ)jNi!FPdlL%Gw=n+zsCa&IJg?O6fa5yCkA%bcH5t&@*^#VqV3coSCD?8Owb*OQPnShvl#r2A;K?FF8@ z>?|;jo{e<2v@5pr*o^g9r}C#i^v0gbkKVAJz`IyyR&BT4QF~@BaoOWCRsR4)9co(x*!>HXKs*fn-4djCuq=hFZJGdx`kYhKU$R zUdf^d_!izRBONezK+#A@?sh&!$`r0k{D_xVN#i-G3 zqM1piNLgCINlorNC^sR5&4zX=ax7xpa?)Q;r7ZkDL|p?O8*LBy>B{e7CyjVQ28PQf<0yRA}w}mZV6&z&g zmg7;u%e>ncIrORbn<`V9xS|cg6?dZr7lB(gTUcj2eryg?q`gZ+AKDZZ-RR_x4LX_^-=MEq z6YO_m+FIFpQLK@wk>RGdpsr2eQE4{vSdcqm9hT6~q5uQpO|+$!?3)WMet|~2OO9A1 zHD#wzc8gPQefkOgtV8(j+{K8k#gynlAP%$_xY`+w2@|M1ekw{wqMfO=#f2!Ycs9F} zNcc=+Y70zo2=K7+s4E#cNra9%5wgokx_mZ@lT6Qb;6>H8(o}}h2kzK(t=C=%xw0{k zlQaU9ETnrcMXnKj%~>~sGNt4QP%Ba0)KuXFLNa(E`A$8O$Vd`fe6&-i*+;5L8fe}R z56LXI^OM&5V1uyhdmkIrXE=hDnCw|`aqDdjwmKkKTHaKhox%Eb9sd9uqGujsz#eU)ZD{)yf+ZS&ht@@2TtwM#wzbHYxdE6|6 zSggp*W}iXxFjN5eb?~TI&?TIOqs3Y}r>|O4)>ze9EP~{=HWrGI2V0xc7ivOlsN8Lw zTERMjpPL;B+)}?HMzvM5!mNH)Tvxu=a$6gfFcJ)iV4o5VR{qq>Kip&LQB~DOhg;iT?mCe(_6Wmx~-G!~!)QbUSW%8m_LU!mBHrb1hwK#B@dJ^ z9Cn>Vi7C5jJ~r0450UeYuNbq^elx%%)TW7~zFQHdOgP|L#BB!KMS0S5oU+eBwZf(O zNP8~Bac@d}VK+OQxzt<0RqZyo!vziWsmmoF-45~Lu?&AJk0>(eR@D$dJA8W8j~B(= zbI($H!u-6>J`c0x#` zHzJl_3Qwumq0_B)^~tlCwh0x6VT3T^fp9FImc3h5CYDN@-%>1IZv8q@{m^qlMo92i z<3bA+kq=P*{{Z7yVnV5S=n32h-%GhB#%igKL%Yb<-pW>MEVHe*&~z1hSkks2^^cla z7B~0RMX>5wRD?_E(Q{c3Bx<^qw}zxb`f+$EU2=vyqICL);v>l$D0u6vWwm5V0VBLx^vo>iLBwCf*+GWKQXbMBqzwmwwGvn4 zopr2^HL0crp>UnIAwya;rGyp1v%$-FQtg_G!H-d1LX>v(8c{|PHxsEArk=vU1ckSQ z(yN@hk5(wsfmpZOQmR;FRxmidZ`~t-!gmq-w)<)mmO|DnfJqh?9&{N`0}j5U#35=r z4xMRVc0Qw#rz5uEI=#CPpme^5mD%NMOSpQqm}Q)FfnZxA{>rMin1(x#t#(g1%HZOb zsS;3^Bwyhw@UAa=m}-4{3357CPChXvj`Jv)1V0VN(BN1pxk$H_A!jnCuu{Y0c2v~U zWLh+HDMm3M0I!GL;43QKvpvhptyQlgY{wGAI>p^^wz1a7#=0GTN#nW6-^^a8(Gh9f ze6Z~M&DHi%6^w-Y3*NFbY!Kv8Z34wp`9(%u!OOXiwv&=m z*)8gCQ>o>el+E@5M4w_FOx-ED@~ndDMoiylwu^#3M!`1ly<#-vD^?JeL7OEzui;a| zH06VDOnK6{Bj$TxPuo$HS)N%uo7`TtA0up1!^()rWX_-QYA?8r7@+wWW~Q6ThAPC_ z>cbCK7-rs7tSsti!5V8vjPh+TRGA8C_L@RBsl%_ZdD6F9)bNL?sY)%dvRfq~UMIqw zPoZAt)L9J;z#V++VC6o9k~;}YSKHyKsoNBw@>5%Dayk3DQzfo6i1EovEJBZ;vc7lu zX7r~w6W2Zqty-FtyPr6D(<>4kDi&B#C+)p?)MWj`>pnhSR4f!OQIcHH3m+P}h2NBC z+F%J0I)hqv6IB3TV#K7UT3^oyEMb;Mqu25rICvwnj_Mq zle_j+?LdX8fv@V2z|Q; zyA41Kk9{*t-bJs2K-Zy?NVTZ9-{RH99P~r2hLlQgVI4rJ5YD<)2h%7`azJnUE&_L>^}xg(*Y zLU=+})s`W#L&BM^!FNdMl#yX;-^SEmbl0>=G3#1FcOt^0fn}7*&M75IY!s3e(D-@O z8^R?hzUM{uqXOPvH;aWN`bK8AE18B{VevA z3yg)8FcX}R<7ragkV2DkkgWjHyia4Aw%NZY&^Il+Eopc`9X%h2a4b|O#w!aJJG)3b z8$h?)N=s@JUx`Ux{`0YAw2zseQ_f=8l_oAR32s3u-r8-U^3uA0i`Qd$eU9^bZPK&c z`MaCpvNFZz5gfST>+ty3sq61TsAZg1#KQe}AO3{RkCCyCvK$exAs#yKP`=(q;`0GETmFd9tI*fptJ<-y~V2ju2(3p zo{~gzPagD;Y@3VqwK?~mLXx8%fWd?%X-}uu!kLx%7o9mK7#((+jmF>L-SWdttm*Q*hwNCE##@iB-+EJ zUsBraQC`z{BkOdfO509ktFiSQwJmMz__Q7svq9!EZWN^@eKi#}kSR&vJsxp8cN_H9 zvB4x{`w~oEU5K(0`-$}eKPdLl-F{03p=(TiFFfW!;5eRfv}T98u^Pa zel^Rb7);~cbA67AJ$C!+nB3G)sdr6{9a-BxZpI~~$6JL>ub@%gW9;@-Es^kJJ38uU z>%0z5K2kdlu)C>Z(hqwJ_NH+lIRIy(w<; zZ=sRcWU1e#dGXd;`A<DTY8&3)G><04j8kTWB|lG4*? zUa}LQ1auT?t`WADFB6HOXb4PCPWmYz0dKmdrR2JhRb!~mXKWzI1iHamluh*h>fJYZ zl4X69?3^dv3i(Yhsn=S2@1&4~>&d$vpM@BI4l)w23nX|{W4kFXrko#SOL2TrtZ7Z6 z0H8Eh+Fnn{oGZXxyb_yvE+vi0@J`y?r;~meC2`~WZKM_W>&B_Qw2untp~}Po9Z`u5}Zi|JBaC^rWJ@Au2~0vB|EomI`~%OJcm$d zS*#&*O}#pb*Sdg8w!-mn8h%B5T{NLBh=coieFIGKG3SE7X_S&GiU1#+Y4;jo-?{`>GSDqs=Qu5@hUZXML zZGMBK@YIp;L~y9}PQqzN%vQZdYvAq2V`T6-iu|ygAb9LGwad}~{uhT78JOo~I2FGY;S`I_|Ld~AESj`#j z3RKMYhY3y6HKm&~+7#Bc=KiBT$hWENrd#N1+BZtK3xQQm*O1;LhTH)l5v?sb<(9he z%tZo{q2y16WpDEnvI#`pE)=HQZ|$cSaU2dm3S!F$N*NYwKxj_T{?M)JKHU#Sixb8Fn!F!WhOl96A=4L zUe?X-Plld#n;cz~#d#xNeyo>A(4X?2MTo_5$unAz7by&>#HA+p_-+6P$LyqZb^Xg! zLtlT5ZJ(d6^LNKJ%Q;4Ae>}Kxkn+;YBEdi)lM3R2m>|;_#H9NE1h)?8hET|<$oQAu|oVGl=5t$hkJ-JFZQSrT4Dd7pp z0T(S^CvERUjIs`K$)RxTZl}hH8Q__ezOn73E|bXhD+qL#NWI71OG!308DMsoeV3t4 zv6}mjTVCtwYg)1-s_qwMK$QD6wN_tp$!!&vl1~Xe1-jKl`m)js1osk>3M!*#v0(!5 z;tyHIl64_NO6PZugI)DU(cZ~p&SWNW@o2y%|@ioxx#P>OIlr& zg?vX-2JBS7Bhz&@80kF8rce5eigvKU*53yElTg~>w{Nd8=*x|LdK1K%eRj|4 z{{ZRhpTqSeXPM&JR3sJRCM+PR^*czoHRb%J+@7_bGfZyI7Hy|fQcwE=G5)Q4YP|dS ztYkNG5Sm`YlR{DHkPWVzo00Za2F}*vkCdX)I=VgyZM7z*l)i^V;dx#ImSYA}2P4*L zUzsFXNjB(3Q~vPZsm*Ti+d6jmT7)FVkhCdiaP2qTFVcZMsmXO2rsyYzg)t1c7pk`? zp0%2G<@F)RSg=l+D~g1?qqg;XEToysS&JS88Opxnfj+4M^h&Lf3v?ovVrLR-Ei-ql zS8vRGYUNvn)aJG|r73hF*#$i6+%2Nw=7&wv&qJfrjAE8Zdz-_G+zKz@Yu8oAZ{j#* z;;l8@QMWWVO>}BWkVBga3~nJfWc7!gOWY&v9@|lr@@v~1AqrXeK|bmmng-qnk(fzU z+&>8L^QE251HRXehR&retw9>n)4AgYp(fW;2~~g==~9HUjmZks!HcC2v)6Wld}*R1yp!LGhkuexl_7{k@97<#s0n;$B}Sut2_ z-(_cuBjhNXbfHQgLJZo}Hn2p(B4ZEW!0R#JNiCp)8#JmE+6x z7Iqr}rS#CyzUAQUqSGOBaO-JGvt%fAsYhTcuOwDfC6Y>!uTGS2cP5oG$r_sxUZMCk z@}}WqK_v_Uq`_Z6-(MP>EKJ{v$O=|9&3+wey6p`p-~%Tb@KFhDs^X>h3Hp$;TOgNl zj}lL$MZJ`GZ((3jYd;~QxM$RF=@EY$6Ik}p%h$n!#$_x1i6D-)HIw8Leau?uYX1Nd zmVOknPnn`E_4NexP#NAQEh;H2C>wRPGcHO1qpgrDykbx!mWplqNU7JqAE*_rl0Pd1 z>Mywj0o3g^X?;Ar>R3r(tG#u^DSL36Tj& z9{#myU9dv7Mo19+7d6tJh4~bfZGqp4v44c?)YA~yV7;It(Nqe)6YjCA3ieh?+bZfY zdJ@SxQ+5j!Aa^`Rh8pv zu#VDRpdPalUmlP`e}tk4^6{xIN+e#pUV~>i+{^qCmGJmj_|s6_NDaF3^fyNg$h*7K zh_w}T02(XaiuD$ji(POSXSNwx3QgjvJO2PLUbP?H;~!EI(#s`Ldoz`Bk5Dj49c~Iw zl>z*MAU|zs594;Odjyvn+qdrbQx)^+p=oiLWUCXxQ4z$4Yyl$n*QG5kN>l!TzSTs# zc%%r}=UQ7z2q`0J9>adLhQjwE(=N3o;LoZbO?}DAWwMyAB{wLvLU?The@}#v2akn$ zAIZ4d=&?1?GpM>yma~=4#yB5rSb_3kr{_M?Q3dvB3aWPcx?gwoqunguDHyepXvszNX)*cOotPPzyFHhMDAL7KX+jcLZ5s@jwyLnfzRrPDcsG4k#NM7|22BXHF zr=u&}UO_~XPlYi|^C5ZGk}<6^2gG{X_E?X$j;4j$499(XT-u>2=YIM0283Jwa1X4+hV|dc)XV1ABykr*W;yg+ZLkwnpgG?bMqPlWjW)Fo;wH}(GjDb2i8dO`d5rsGLu$7*gfN6zJNqg_m3hy6)qF3_C-PZePE;2zp*wk^TAnp;;71s70 zYHn`^WPC8}lYc}L>`1i=38YWlH~vi(TKYN<)wF9u%NO?zzm!M)Eernu-KfbnN;1fz z;3mZ(KW(VK*w;{V*hDPc0K-5$zYBW>XSj`m-Q(fK08?OSMhGvAGOLr*!kSHk_Q8*F zHkOhDcO-SSGez&PDFI{g7LasBh3sj^lqBC=23q2bsO|K{&X*$ey0xQHrv_Z@*nd|`_x1WP{{Xkh$nAzvc2CNd?a+a9 zS^Jzq{@llr`&p46nF=HR>7l+_E3pXh@?jRcSuI2jhff`~sdCC*sG8xL^bN-CRINv* zK=ZJn{_ul-%Km{=cB~;k9^vMzQq$*JWaGkg9wV}O>5RDUGAWYXu9YN>E$^XE<AIc|wMU)yRL`9MF} zdXfxhWg^HzOo>-g{8D;-^rfFEvGzmo;UXAwwLC8pvT|95qU;|Q_ghcxG&p>NVsG-G zi;1E=!`L1X+ez*QVP&Jta2r`KB%A$)s<(Nq*!9CfYtq+ju+iE6D(Fc|X>lGu7N3Et zh0pBw%PhZdrLM(Q-RvJ4?YACpGYN*>c}q)8M)OjQx)bfLlY-9i!*9WC3l z-It!#OR!C>W-UZ)hS1YO5Sj$`EXPpZ+z|b|L^WUiG5`X}J4H$s+WkOI5FRyygrwLZ z2}u0Pu%g9TBTmZFF!US@Bwk#$B81$jZaUHc`^{U|sdSg9liaE-f@S-I*}MkvmggAg z>H_O*KHA6t+oCT{SHo>f>L+J0uNUZ6lm4eZopB+;jzqkIkTpXQTwTaMWC2b?;+vBa zx8yp}B9DDx_BB^bXjunRKk9s_PqV~>PRrq&e^YDZ`o@iYTlQ8)-&xtPP0g?M;xVJpvroGc%+Y%EQE{4m4fA09u**_PN+DK5i=}5`HA*jV*b#HB2LU}@t zu`=+#<}?6~49jl|gV1@^ZLbWprF6irt^OfSS1~!k%D0vTUQ1V|J*1dH{MWN1HU#Vxx`n~2Z7jd$0l!T;|5~X@)T{?|xl$XIZ zl)R(Sm+R%%W5t|}&8I=h*@`BC_dpg1jH}qZ+az`5cZ7Jqa2#{&4q5!n#J36 zfPA1^}MiK1Hq`>*b#EwFOB z$@U!To7EoFlP+~2(RgskkH02Aq=KNeCf4dJk>zjkMgE;RX!{xT)4}G)#z)CYO@fjS zr{F@Z_g9#0TCKF~?VF8V4&!Dr5R{<<;ZH;3S9MVp70~0hY?FP$Z8c%!4Hgke3AWfx zw*D;+w8d-(R>!GHKW2uAn#n609Hk*%%T=sbLcH749(BAJE2ou6!xgNf2O+eZX+V|f zE~Kfis8$3gB{&k26V~LL06LOtukU=G5oWd5sY3D}t1qUGC!CST^Ignq=iseP&T)v( zzRPYP*-74kVnNW_tk(y~N;!;r2u|&E@Z?c_<^H4ngW#NIdF`C1yMxW0LXn7T<=l@6 zhZLP1d2WHZDLYS)SQUhUQ=V!{#K-0aleL(KUcjK)S{)Uwjn zuR*=UovpWhsYvK8UfaiT8v5|Uipl4@oVB)3zd{icnPi$+^rRGv-)QM@X{+^NIO7P0 zzSWVRMtv^&F#5Zh!np(<8OdnfciqykNygWpA zq{#_T{{StN6`#OTmTk*8$8uF_te>9$0IDym4oTgtw73n)R30%A#eXfuB#=EYZSmWx zkBxeV3BcB>Qj+QiZ;?=Vk3)zToRs3qgQXWbi`Z52tw*9N)v}bb5rN2OFyb5!lBAo9 zb^I!`k=BIc)gseJZpiwV{{U}0xt7P}pt`AREi<$&yODiu(!A^BJS-(!!aX13TD29hb*FCBVK^-GEWYUT9PTN& z^0>fL>=J%uw2jKJ*Gpc8xpybiv9E*tcUJb}v(fqmJ6X=MCZK=9hT_dh|tUm^PytJy>AsqAq&2Vd}#T2)DMj znIz5YL`-cbOHeCP@;8xPqK|?eN4ARI4DvTZHR)=Ljz~&f4AVv#+I*{iEZ340ErH`( z@gSyb6Ken|SCTE8+Km_y$m?RfskHcRT+84$;`F!NFtt7pVG%lOq_@O=H7(Bv?Bno1tFN zE3qE%vrY_$zunU z{L|xb(0qw5gp^-PRd&wQ+*%ct zG0s2}v5J1`Ndn@$-<07eHrg$>2lH>XT?%_S z*$GK%>^172FfZ56s4w`Y-D|PvoM-ZNZeN+;t0(pwKOuHZYi>SW2c1<}!tW_##O&(+ zOwz|zFmvgPFs93r9yV9$N$>oX}MsF&iZf4AgCo|pVPS%x7qFEv2m^Z zkEYN8&!)`jC1FWX{XY-<=>Gs~x9En~@*bJHUrV`}N!+WYU%C-`JazV;ptZlqdNK7f z=hALta%|xM_4o&kP3UcX2(RSbFR1w+NRkz_I_k%rl0Uk#JWltj6_@gkp(n=uB4dCC zQCps4K}V0r_UtRRf0im;5+_hTm3ZR>7a^rq{o{IRd{X&$WfkAb@6asg(#IZo3JgU+ zQu_QoY7_Xi@ma{4{#a@LWJEGQNHCC02GzHKFoVE$6xtsnRLU+T~7K( z#>;g{$K+x>@CM`5MaPFquT^bDmz94bMk_OflKzEP%07-cLoKOT=m?w>du-ZApN%(G z=ENn{78?HmKGCBY%S1dlPQ{ZJiWNZjRX2rxnqi6i@|%K140ThuWJzK{8$`b6p1;UmJF z*X5)|?9fNv`aI^gSyNj@?X)38)3N4eg(oV0pw|A6d2-zTD+h0n!k|B8POfrEi`gol zV546}{E_w9QGg$)*<{m8o|+W$tah)Wyy_H{6p|0-8&8MXPFK#)9ZgC#KEW)1(RM+_ z+15!H*H7=QE_z_yRmGDSUqg;QuoQxlo|}_;+W8H*2K}Y|38pjX84i>Mgs7j>VM4E) zzFmsz_I2akqR_{GPrNe`jEN~g3F-<+7NK9xZNfS1imN%@PcIbm$R{ZJU*OzLNDIqD zh*zRRg(-lEH%%4DI}n{1^mgV8py_gA57aQTi|&D+RgT> zX>q%gW3-J9zRITMelrcY__Etins8Vr9oqVi_{4u?=)Tm z;xN_i)E>_+qhE}AB8ZX77U5F8Tbt@UYphyndK}lkmyr~5nF&h3aMoeS$*;%j8OQ~q65|)$EDd-2?Rhs_*ANJ7q?g^E|x&Ht<>5%I8ItXD@D3jAADgeGIaX*QNhT(*AVn)02V;$zgDGPS104}>s`MYcjf4E+?7wvr6)4- z7C=eV^{$SaaEdz?2;{iyBl?3E&e<82&x=g>6B%>g!PV5(gUP zF2IH1Bpp(oy=l6xKx0Jyf*;AHAJBgaVmCzqdXtf|aI%%A-k$I5qdmxAiqM9EDl}Ecnap0_%p)qH*J;Mz8r-#o8S3~G3p;yVZbBvpp$8N`9WBg~3;??QR z#7T36^stzaqdAYYjLqJKBZe!Hnny8DmLRZwA{Ffdd=TLtvQ+;leEt59Eww?H(iosW^q zT}HL|xTaqCKc~DTWU?0VAOY$moobfuW^AytN+s{a`%lMkk{X!Xg-dGIy-pmo%GiEXrp!6N&B_8M-EPE8c^~D%OjSaA{>jsRPQG$g_yu zNxi%+Qj^K72nfY}2HSP0BWT~#>MeUz;S@4x7$3P=1=Pb&KwJDibUYgp`B1WK1q0h; ztCvyBaSiQmzUmmjW)>i7lv+`mIZS z&3{n7$b(pkHy1*PK6S6Sht$PHy-mg?(yQBFD$jVA+E{tqE7ulov)wUjOhwFD*e;R} zjWVu8e%50z|&~79b0@4aEYDVUXLbi!4Xvd&y zaZin{NZqjf!5aSnEQ=fs5);&uO;t9*K7f3O^3=Ay?xD2{*xU*Pktn&5iuF5?65!q$5yKW?^SafI$CSd7L;-~ zw~UAKaXH7!{NFs}zdjfj^xSl+*4LrXQ1z&*KawP`+L~FDIo~(+7smJ&Rl|~=x}vG`t`DJT_FL-9JJru9$U2 zj~UNSa_)Tuo8Dec9x+rbQ$g=duSuXA215oR+0 ze(Dc)(UqQCxn0i4g^66oQX5{I8&GfG`9)t*!D_+wD{bWVCF)f< zhqE~qDCBc8<(uBikm|Lyyz0&x)W@MphtxPZ$9|Ew(NRwY|c%?jmuNh>Yy-M5S9r&qGo1Z@5@mca?LjwHHeI z?mp^K7^9J!nOu8IEhJqrF;F%n9&1tcq4pSL*o4+>eX zHY9;0KKLcmd#kM-+ZGnux*}%Mlee!+R=v~*+h(pkw$>G%>NDD72c`aPYdyiAZJH(W zqitvmlB{@_4$-A6lRcrzHyiDv#==pC0u8ow@l^}W2JmCVGj5=wpgSEOrm zwOYC}j@jXChoBXtmei1;vfVGGGN&AO6dBsk*Np9poWqm znMn>HtXjZyCt7+DNF38%gceJxQneSqT5g$T30WSX7dkKPqM#FwM!Rp*THZ+b4$y@g zdRK>FyQ3G(Amy2TjFVw%8IA?4kSdCp~6@OtP@8 zQa~QcN_O=p3g2P-X(2;HZ*?zZvN=^@MiN4=w%*!Rz-lpMwgA50ZK|sB+M>q7#9NAN z>?^>C|w0k#*hRm39B(xj}AnYI>gc{&E>rT^+f%aRo>zeK9m_78moWBSY5z04UO*t#k({ zwj=HjcGTm8PkJ?Rprk=cN zf>=D$Zu!|J@qasDraS1$OuFjUZcyiWOTN-(z)62^kL-}u;lJ! zlOy#P-Kpu8(s|ZOG%3xNr9?!hgKjocw53A!AdM=bTpbE|!D~k$V?`e>OOF!cgpyB< z9W@?{u{mZ>ED~JTp{+YcR9L%D*U7q;7k`QSTYP;BzmjW0dfx08i!Dz3mBr)hiwF}cLYqrS7wLPG_X?JkDM%N< z{H&Xk>{OkiiIK(UAmx)AYs>?=%Ny&Vu8+^hvD5sUS{BgbF3kEVfaLAYir(R|^ntx6 zO>3p%-I!zH3;O%%|&H}4lJ^dRrTx^zRfR5PmppDTi#C*A4ka{w1lJd^c267Xjbhu z-dOkfqLrW?`Z5;#1ct9pO46bQG2;-j-u&cV|gjvQN4+RSXPPMk=jA+<+ z(I?#+GM<$tX`bI|1@gi(#91oceJxwpmSPR83njvv>3VHg4Z|Xrq}-S>P^;g<)vSbVEm0#v?Hh6Q+FguMY9ddit4>I$U$*ETM7kQdnqdUI%Os31jL@3{}4xcOd3(w0TxwjzuYgBHof~UfKojF<(f2 z?H}49?g{LrXCA!?c^=zQ>Tk&08Z2d!0)ZlkN61+Su+VktYgh#-8|)wOD+u`t(;&}Q z7;CL2XT=t>hKy>KPXm{{)AEAQAJyg$?>SyFCz9S zW1!47O08`wYXo}aaJEf`-9E~jcnKMkZYjlb+=^pNyxze>HJ06CqYf-daGzVuxZeoS455g%~SRRd;Gu z)-W2muv?K>UcQhw^8%Fpvag`@FPill9CT5qqMvS+58CQpg9~!csHlv<=-3wNprZYi zUqEj8eN9yvtv(U=R^FXoLQ%RxVF(@qm9tVlh?|oesEsU&3yS&$NNPfg$WFCab)GC? zHG+!o4|`?y<;Jmi*vVY?TPqJZE|h?6()G;p9HhLwS98)n3H*CUDHm>bDMol z_aBktreWk{Qp`yuNh@U~wb4ADMkle}&9>7CHAMMas#J z`o)XEGfpC}`>`$69*T=V7eJcSL%%sX_pvH2Y%{`W$Bs1#+(#z&cax8aXTdz3%R^g1ov zbP}ulx@>w23s*r-q&-pRkleQ&*x2H6+W!EiKqtp&rdF&H>=|+weOTRC3}{&<{t(Jj z_Ex-L5%M>`Qx0GtIjR{+_W@CyQs{0RwXyEa5rc}Kgfx-%3WvCfOO&ztgd4!cX3H&v z-`#JzrkodXIr6qkrb5u!$HP9V!bnF^;Z3emnqncdW!7{>3*T=2;CLS&n%I(*N>UVv z(S?eXvMfH&I#R8Rc{{zT*X8v(ua10U+iLr}F2B_HQ_XvYpX7N-a`G8RA1hkc&DNj* zQPWHLSGe%M6V#%g9~3>vbMv;|orgHS?3lg1=Das5!TUu+WSp+n)IZwbN6RC0Z4gz+d;0izh|fyWPyJ)v@H0K zd8So*K9z6<`i<#;)cTk$wGYZU~K%Z46FvL|cF3 zSqi#=Q^Gg?bZz;Ewu>r*#@>P>Fsm$Y>S%r@+}k{I+rKieM({*aVy)!}z1N}e(v`M7 zFf|!qa@Iho{3`M5dPUQak!_NgGxbOY{x|m3UGT?o%4S=9XD}F26ixaY8s|A0Jfnj5 zTI@2{S20|`(@w8zEGOYW1Pv>XpA_@Z=^?q$h4M8(%K`qMas~(@EBLJZ8!d6+d)to+r!3{+4+rhJWrtt_$Mx; z>Icz}aVL?xjl9*x&Cq&%wO?oQH5ur}Bf(ed$;Kz>Q<}V_A^bw4?mChH(4QO9FZm7& zvl^?4q6@1<&&PkKQIWA(GM1D2kzh{0VHBeI?d|?jQjUdv@L9P30O`ZQPy(lJQ-kwc zLHjBe{L5;6gy*-SyCP8=`hE7QDM4}&b7lVkF&h2AReb(ixh|PC9nD{%A|FLPtBiqc zOFPm28{1EP3;jv7m+I;;wy2kAjw5|9cDD`&+!S?@s3oNJ@uVNgcI5k(71dPxM3}+t z&jrJTi1>)i?9+C{3m${Qp)O+Cda%)a|q~AP=7*yE$Xrwa&s&!*jVTo zXPhVD0qqBsU44oxz3C6r&D!0#J)rO;QI@ZWw5O5me&l1~n@XJ2cKlpYt<&E``)Prf zjsV$jsGbLmZE1fvhh1*qG}zur9#*RjZX($g7TA#iW9oB?UvuBV-0%|TH>4N8H3zRE>mb=m6db=e%emkWdpfd2RgxBnQ3m?_BOTs)Gxh@=r;{Hrf(W_$tP@lde%A%l!4RRS@%`0 z?9_E^$YwM=+o~I*wHzLsmM{2HmU4Bjh%|I$OtE+k`vlC34Gk^=wb$QOTU@JsQYVLZ35v5dcZa5|#cK!es>vR3pzus;m;Ssc+0fw;(WPCkU{{Urf9L3q{5A9(40^H#o zc>t-z1AiWr^?ar5$!oz4)y4SA1L<)6{#8HOwi_h*TF|4t!sBR7 z(|={sMMBOp{B`PXJcVp%{{VFO>pG!9O7Pk*{gf5M*^IMNkf zO~Zw=8DMPXV1^F&KQiin((&gU*Fr|nFZNSkwEbA5xr73l%z4zE-!BF{7T5Vo)-Ihr z^wc=IW6d>Rcbbiy6ocK`!y`D$xgFaYw3DvS`ZdJpj%T7(ImJ zD;7A^C=QpbR@?Ocs8LA0PRp$I0~%rudjX17{U|Mu5|EUE_zE?1 zm(-g`gXVG=PCnpvM0W0yE&@QefWDO770yVDX%P(f)NTulu(v)3iwL-An=4H}vY`$J z>t;$@^}!!H``h(3$$8|d5%KbWmtmAm*=&%dj{;7FSEX>D7mZ6YIetf(a_n+Txi{n} zsmBV5Ad*QpKW%ls#A6&Hf)}>(%TKi3;?MSzlrhP$EjKa5f{76w{H`{w6g0h$k*ZE_ zhmOoJ-%{6&RULzq;8FJ>_lcpZ99dAo;7(3@{0K{LMveqqYU&^u@ zDLjYPsDp7_jyD37HJZiDW=m$qYppHBtHSrzgS9^{xgJ&IxZ&oZOp4ryPI}C$`xKOs zdwU2Jo^VOh*gq@FpyC=+lA3nl9jOhSPri#n^9dSGaOC-{pEH;cmmO`CtN08ckStBL z1FdwLe$_F|yMwl=dKTYrIGpTbZ7ugyrmgUyN1?DaU$eIvw}6n25p|`Vp(j#&ysER@ zV5ba^eKmPdywSe~HFPMShsUi*U=HdOEpTn;MfZY6x07_QWtOAY!=)1@HMOKrIQWqdL4|MW08#fbX=TQVa#PO z#&}uQRxXk~)fG1aX(o!@eXQc~mRyIAZaCEXX+l8PUkV(yq{Bj3DPHFGj~ka|$Z?Ai3C6fM5)+C~OFTD!|0XFv9tzq&S+Ri}Tq}g2Z zPRMs7jyUpX@p&pkdU~y(ttZ+FYVT8@qD#41Zx=Pxh-3Rh+)T5GL>?(I#Vpuem4SQv zMG^B|Z6}?s{2f z0$fPe+JmQ`g*~gwDf)t*pNjxuI5fG^TV)It6{#ZDCfD~-cDHaIo&bruB&g;8z9u_vP8)KE(_quj3>eGjhVyfibwBdNkqBJOhwvm62^8>A^ z&3;DTQWk5X6O`?)DpM*P*piZ0;6Hr-J(99kiV5%(jMAt*))3uiae! z*5BnW^K-l}I8b87ic*xe zkTwfxr2gP#&NX6Q^3l`h&nl zrq^WY()7&j6)};Ujl)4+M=?E|WF~G@^X?(AqifkAL=tr9DNk~8=!WXnaw?8V^#|I1 zJHUB2H)EUQWq&DwDeQhc$Ut@ZEg<@B1Rsj(RfVa}Ggj@5g%1)NF;qjzv*3qte!o7X zxlI26n2XJi7iD|Wm>9%{P=?pl1nN$nB-gm{p9j>fDK(aP-si}z@@X8$54)V>3o3Nn zZzPok{Gy7oDQ(pyFO?m{ug_CmCEOOYIbil&qtg;wqK5{v4t0O1XUgd=w{q@vqqNdARkrY(5p11eY za*jX=savh3OCwk?m2JMAYcfJ`$YwIGMw-%g+CN2M=wOnO?dwz*+xd%qdRZc2rD;o9 zd23x`kiu=ZajL7WEw!r?c_APJsqi&LWb(^+pi|b|Ez`oI=JGM(jS{O7t%r>%Z%Dd+@%Y7SDo`S@q;}lg@@)x+fSsPD#K8BHwM*{j!59hRb*j< zO$&qVy+|7sN^gC7Qcl2ai=jKO?5N80yMmLz@d+SmEl^u4&{|AY0_5vOSF8||H?cm; zRAqNUIW{N+T}la0i9ITBW+Z&N^f|}Aoz{`V;Y7r7iOnM|KXHC)N?cTS(lxNxQ|+!# zo9A3#gdbLVcfmd&w&d--KNZrcccj4S!(C~0y?k8^dQYVgn|nN~H2K@8 zn#0ODUyyuXPj9T2CG|!9$NAXL5YdFF&VVX02tvdm5TYc2j$%55U<#ZMe{f*5lBzwBUM+U z>|G@j%Jp#UDtwk{aAaMViC8A$_pUp0k(6bazNdYq#w%gf68=5OTgf(toJm8Vbv`wH z!t?&>@t1R+<@i@a%Ir)pdh8XaJAuck<8GBG*ayJTr^1LNd+6}qP2&WoRssWFn$yvm zVvnJcgW@Vil#F^`@ud;cXSin8)5@ys?q)>KTHHyG5&1(_k)d<*Xc7$4nt-$Tm%A19$u}SQ#9T(beL5}07aS|4ml(W37 zfvFYK{CAX%GsKS?`S%ZY{}Scw$0Zpyd#zQ$9o?;aP6A@{0JA&y`%0hwZ;3;}zw_4Rxv3rZ?Jxrfrlx)5- zC~!%ZhLW9DqM!hz>fVlej9;O2`o%MZ`zpFOV)OL<#Qk?`B+)7D4+jJDXJ|(p%<0X=Z9}dw+ zxi8l|7}$f4!D3vIJfhA`c-E3d_ILanw1whVSi1YC^;G@@$*CuZ1M{b=x$Y=w`Y z#dF9iviebg=OZU_3RhKhp{Vr**CQ2VNF?7%EM6HgC1lhI__U@FIrcRfGIR=|PCle7 zeNPOciP2j7=xqmMrt(WwuB|*P6mk}uFvD@Cl>$nvDQQ8VzFkQy2}G$jQL1gEJwi;l zc%)2@T1ycjFt^`$gtpQFDpBd670h!x_3OvVZ=urhnsT$UFR9(A?j{l|Gjg0(Y)Ed4 ziArT#c9DH;dshh`m}%QAMn_1v)|cW%*MgRUH-mo~w5N76?8!@3Q9`ZZq=J*TTHLM& z9do7o6U;ex63ep27H6jtO4IX^R-NsvUsG=b?XE}UI*_(LUZ{Ikg=~B!*`GpD^#IN> z#$wLKA<_U26sKti?X`KtX`Xs^1AoD*pH(J{PY2zu7n}`+DKQ;()=KO(wHdz^^k*@> z@cr6)#rY!0X-rIIFF=$Q+Q2C(Bca#AgAM7&q0CsNT(LFOWiA#-rtLjr;nt5&FOH0B zUuK;3L}pwp**^-3fbqRM`>K-YUF+=Xkz9irwC}PPrlnLRys_NA)VEpv3X40E%r>

    m$66TZSCjKo6xy6FEL+0%tEwCuH63(TIbL5X1J_dSNzpf}y-Iw$ z60a=r{dli&0JTX&UyHeaZaX7Hw!Bv3ApM8 z>uc<&)z0g#)XMgw0QmSm;S(XL$}GX;gfki2W#7Hx#;r zC)^D6WV;8M?(Zs&3vS6n2uWyyo(OUWcV8T*9~;h-*(o~>)T2_}T{Gik>;wfv;HC&~Mh1>21 zWdH=px>KMfMyl=LDSeRA=4OTe0Qg4ag&P`_6c1YqkwTZpsZ59N)`OMH`7`JElhz0B zgHo3bu9spF7|wyiEcgoFO!Y=nMla)I9=M*TH5pvDzJn|E5qPkKOI z(|hw1nMt$m|rp5o77ap!q$#3e~bmec`6 z`v(0$*}3=wkldyLgHUbn>My_co^sFL?&Ic{u# z4y8lH^|cMx6lu+lQr3Mtvu7s zAxU+!@GKGvPrOxq{@m6W-7ZM$&e(TbxZGwcAu%w?gh2{z@Kw|Sp*oE#m*jYQWb%uR zO_`*0JALeuW4H6s18x!VIk*r{+f^T7j>C_4(2QjJnEddu74QksKr5x_k=2x6P;I$& zA?xM2R4h8FicZsTcWF-hXjjF9^1aBNC6gWehz;~t#o#pYtw&M%Ac}ZpcQ-i^%b%D_ zidT}aOs#n$K1htclx(Zvd|O-G`TMBqEESCU)9S1DYs?*w2VAQYC=2Q@YOj@xBhZJ? z+vS^ZZBhb~dV^Y(!6OU^sso6($*n98-P@FVegjTEuk#+-0>1G_jnZDGnnR~JHyx1JW$b%uN^}H)Kz2<& z=6Ip}E)B+Frp@A-NKLvSH~Z~IoZIyxp&y`0>|6(0F`HyIf;OO}08%}a4Sy`;-@3ks zKl2}@-Y?ALJu@Yfce)Cp4i@jDsIX7f`nZ>Zua+7%5&En83{E>BSznB!=Jhc*OXQ;c=pL3bq z#VOa8m9~9UpNTz4*T$~?<$4Row%<_XTzWAjGG;_2lc~7=`rm`G8h3ay=HNZW;*k@! zW({vFox8yw2=MAEzjKG0i)eC=QZ3<~YsDmBCPHsB7E>`|5DNW8gp>oR{KR?J6Sc?R z=Y)=et=jx@OJ#jd@qXP8mV^d!TQ>zQECeLl_at?r>-JomQPOba}Rk#n4O8WNC~40mL=^BYzHAHoN5>*Zclth#c<7EaO>Lm!#o zKft@6*^Y6Kmz3k2tY)J?QAC$e+l|<#t+#=HvZ*(@OLgt=pGH|%z&jFg?Ee50nLeU> z!Pu@(5+pejV}& z;V!4BF?jd45PJJ*hjPb31*+uDqcbKcB?UmH#1V0AX-VuSA-c7iLkyS97fRi_)W6QM zb*;j}Hfy|r3zq#f*$H$Up=Ekr#PqE;O(qjzt;+)2uR(Vu+-fk%EXQLXT9T02AzzhS zYntBVR3CGr(c`4oVsRUdO+#(15!6b$kdkYdzn!hVtmv!Z?83q%IPNzw*F$9&QnU%S zw?6u>x1WchWls-0^&RCnrcV}v>+BSxt;izupYJi7=sLJ*$EgCE6qQ@c@TQg74E6c} z7FTiQ;Ar>F#fGLG47LG?B<$BqHvl@EjE2*`g`tfA{@+C1_}z6wV|~=`9|+C{gvRH zuAJ+;FVyrNM_wB`jeQaIIyzG9MpU7y(g_BxwJS*@=X;xtjTjBK**^#a&aPGwj%}D* zmR6_Xs|p|HHyCs^3L0C&t|*Zvjl-83Rg^4vBH(NnVWsa_o=Etf-|V7Fc{9ZEH?P)tsDD>WfRhBfsLM809~wQd)e`w<@#niWW}?T{b~_7(bB;OAA;_D6nk6*DifzHr zf;@f4!k??1Qh3G>Fz0w3oe8MgyOIo?w&B2-^OF$`NJ70gmOu$0Ui#GT=Cc-}A*HV? zEs9Zc>)82|+@AdMCo-8lWKL4!(Arwt4m3hgunn%Ay44z2+ulv4eLKZ`V?$oJ$ySuH z{YnxaaArr!e=>jn0Fny{P*NVc!nX@w;=L~o9~NEPVr!>DHzz0a@>co&65LTxK>VWm z*i*8np{TYKfU<3J0I@($zivF}QcD9BqT1rFK9?IIeM)?NwM(%C*xb7vS;X2iJrp{h zg;p20_R`U#$@E8^^Y(jrXz5NriI0tGwKSc@KoUsTrCw|5C?t5VFUU^mG{>2hs!MBW zrt~^#QQmHS5*FiPp)9sSV(^&~-QF44o9d!3bP7=vYbUJ;HlR~)TJxkvP!G{@` z>Q2zu0^aKNZZ0knNuE{8TkcKCdWN4$Uuj55)Qexrt>+IYB_C$#dIEzWn}U9d>Qt4Rqa_p8b_ zCF)Z;(?g(Cu8K5hUo3#aK{)IkH0f8`@DaMu${EyYPGGH-g5Hq*-^R9+2kss$pDJcu zjQ0W0djEI1&deRs9WRf=5HLvmu9sb{se^TCjbbYTiFYF|a zJls_>BrG-_AR_0)_*YYRB^sJi>5`rmyA$#2fS$|rAXTg_&tV@U-k9^K`-XX*^u52b zf$kaRXVV^Lmh&~{IAxNeEG#Ni;z5FZ9 ze~>50>oq7mR(gl=ZkG|ebFTcmeHWp@xe2mzGM!-Y(90rd}GMZq@&?WNwZ#PSu6wp0A)!`8@B<>el>3>S-m_BMctmoUgqkG z?htktpsQQ#qEU30aZ2{Qk*YyS0BPk`)Y{Frkkl1T3;q(-uUlT!>b0*@w`w&t)VlaI zJgk@70o61I=2g#$t3EwlZHdL;^B-}RJ{WiQUYf#7!>YEf?| z&K$K$R<{*_^tCBw$E8^8$+j)86oz{}w4|YtpCPNwplnm|;Ctq%~ zj-)ze_h-~qQ5tDbk3@>XYYSPo;Z~JA3&Xe@DU9*()mN8FwxVw^57_#tjN(l?urOCI2 zSjU{Hp{f4>9dAY+Sy(K$HWpIwPzX@CQ8pGL{xxd(1%r!*e7t=|5oSf1xiRFS(u;)< zp`iNCZTVeOC?t0PfzQOS?WA4&}aoBVzs)uQEPogY*|1l67PGOHM43U zYOOW{en~2|^r_W%P!?6x9P7tlFABD*aTT3UAZOhwBn5P-*Kbas?WuyFl17Ka!kTuh ziOQA>a#^{t>sc#N2-fm5a#1%Iu^lUMo=v*Y#cwXelhEr#$scS-*Z0)pVUDvOGCa@U zMjd4E!e~=%*Nu6<2E%{PXSj~e3rP&3`=iT zF(Xg}-XlBIb)v(~5)|3ck9}Du9g6b00JU*u*74G+ z<5f2H?lZH*Yi!3YCzGK&W!{My%!_!O!cbjNNNL29N{Q3PmA{Jd>*#r>$^IWzaC5Jr z(45;K-y7o@9ze@s+_~w47dF4%BuO zg)O&IZ((|$Bdhl59+S;{TCG~tt8XZma#)aAM2FR|DYTn7t8}h%t@iCoB)ENx8%xmQ zPF38lJpx>){*qpCODQTxHw~9Z{<*~ zUMXZG^*m#l9i`dac7L{*+?ES5lxB7>)%79mO^ZrU0a}}ANUlF~l6G0-kcT0}LiC(_ zmK3(K60%KCju3gB7GvN5%R_b1z6a7LHWV(^5_us8u z=yNiqqf|QFFv>=6V9@J}5!O+u+3@NwU3FhIlu!@b>U~=QO8S4zFgM3Xt`bL3I(gN# zKQ2;PYy`N<8|cut6o;Gbu3b`_UwNiQBG61?fCt#qo@yflxqN zoQ$LG3M7qutF_@ib+c5`lgc^kD2(1DkDR>wwV8ekmy?Ie2Hk!>p~jpGO4EG~o^|b< zXUJ91Qf#f=;ru%ypp=K0x_vT(bFbScaigd`kxn#PD*|K2}PRyxp*_qwl1i{hgVMS|s-i z4eUgOH5uPgDOR4-e`O9^4r{1bYDOsQt}hY8j*jHSA!=GucW6l#@zS|X%WU#vu&rR0 zw%#df{w#EX<6s3Uwyj-_x);zY-aC)4ZuWvohMJIn3eSG;simz}dZBZL;~Tc$c9$o{ zrnzh4N$6Ab9486HQa?Jr)6?*y{^@)517{yk(9w9lC_gD6j}2%`l)vgn>}mQ4I}?m; z8yit0?51nvF8Y}C>G}a*7_fiRN3jQAbrP;ZdFsI1#>X87zcSeW0HS^#l*8mR>LvSY z`VFMzgjUqyV#BJ9yr^$&4b{gsHshJd{JS#N2NbX;x|J*?S|Z8R>+GujXOsJCl-Sn4 z5bSMs4)R_vmBb*fLXx)5fl0Z!16ogt`6MDsL*xjBYvL!_GFauj2Tp zPZ3JlV{-BJ4_>>+kx=sg0H}W3ad^RqEIpRX?EWK)Qc%-s>!|BaXmK~$Jif-RzPKZj zd56_sE1KfQDq=Blfi`KYT%`CCMRnTW8hPr9oB0%%LaBeh`N>j*nHdLMeIJ?P;)TBI zw6o)|#xNruN#wLx`qjzt{9+^dz;rwT18Ce4&J2x ztKOKDa7Hrm9>z-6v14Kev>DuM^%fr-$H!Bxe9}tLNVqoAsrN6JpxqTDUQt=WL}|5^ zp$Sm7+l&6nr*=(`MyR4&kCnz_28xY}&B~2{9@Bb8Ug&=~B2P1%@f-|79+=ZADJ8U| zf=#vasY8#t#szG`DCd4v-X`X$ZDO2TZH}(=Cto91n%pW+sD$@A^deAur8*OV->gyB^tVc`d>2w{kjWFu_b($bf1hGJ1zGFy2o0N! zkrnp~D#EuR^%8oE>GspBjI0bc#-AMf-rCV>M?_V=y_5(pk z+8VLqQE)bZ2aQaGdyAnKvA(0im1FVjIzoCX{{UrgESf{`t-cq%5@Jp{7owXpS~r2p zO+nZa2;XMziN5c~r74C`X&ZV=(2;K{oTrm8C+Ah}!ga2u$X3&PUZH#Zp~6hGrCob$ zl#7*}03Igw2)SnqsgD3j{KvQWbtO5z;NdXto86c4aL~JtG?y<-Lyy^#%7ekWtb&(x zNFck9%135)B6AsRe4)BFN;8Oghe!BS*&x+@{&0Ab!x(8X+Tu9SyNKEkkA(;B{{RDC z(i$(qnS%mpY7}M{!1XeiA*-zF4(`2lqq*RTi&Tl89cyMj!>VXZ-@=v{H%{$$eU#9- zxBXxE@~Dgob@LQk84qn4LYAihI@|lHBVN#9fL5Q4zrX%X|iL$?((bHeVayJ=Dl?ZVSd4 zz^Xe4b0sA|mDgP*S3-YImF#~D(3RBrN#y3a+x@46jv{|Qm7Z-XQk+f6*?SF5`d6Ss zLMY@`<70@K03tx$zqt^_+dC{Yr#{;~34P1R?ap73CfRuA1NakoTJf>=*&2yu`B8nM~#hhoW~OOqvM<-pyKzVN9M;+$13g?dz9tf z&EzL7a$O{Zl@bz?e=0#b0YKw^IYzv2k{{jXuDjH=yK(hl+-}zK%+DO-DTt`Kl@+R6 z%@w}Gg&Qt~U+uN3qn7wCmfgz9uBf!*{$I64ehBBDL(B6#mTalnX{(hcTFT#XM4PEZ zl53^Y(5i^yD+epNzh%?%lOsjd72k2UpV?NZR>(OEB}+CI+J9|D*o{V}&XDXcV`KMr zWW`U*{{Sh*W0aJ>Kn2$nzumt;JWY9j&3bR5J+ODC8|d4%7`$#t z-%)vq#gzz6I-{e6Xs?G#@~eNBFLLdJ(W?GArZK@xa?hc2Hx7)BIn}o=U->~dETrDn zCa?JK=0vgNRBZnM7e%ki&ST`?L;dN)CPVOW$jU}w6$La-@{YO!H8tJne=X=(u-+NQ zZ+{wY!Sw2L?oYJcz`(qu<#@!nVIP^rGQFbS+gDSs^9?Ik<(esH@hz&}97DK150Ebb zCNj{mYo$pe+eq$n@N4q1%|DM$68y;M4mHA>X)U_!UsR*?kSmMixoKIHj_XH85KAcP z4grL~zN87zlF%R|?m)GG_Scv4dlz~4JAGKxps<2jegOeIfr(hCsarvN%2J<)L+q@p zkdz^Y%}$s}_8>jnixIf)hZ;+73)u;`TXH&ZFiY475Kn^Y>MD-TwfWq8!rG%j(N336Y15N|TLb z6{|%)un9k9a(jBxpH_6bw#FTc2Mv~fVww*)NhASn2VW}ZHnrgt+d3^hiOsAZNWPG< zsmgMwZ^&xm6^lx?ntMF!!~RjQz3d(BvT)OreeivyZbBM{(7nh&(*DW~J27bRdDP`& z{-j23cFq=%Y`gTK%aFsz(CBrzugjO%LT4aEIO!@omc2mJr`uLbfAXNZ{6n&RI|MNq zhO&$o5&4t((|gtShQgZuf_#r1DkX+UQon=~S2f&JNjqf5(28Cps^fk9?L*BnvMjw`zlhi0X$7Zn)SUwTy4PD*Y^GtPq<%Ge$f`>oX-Wt*$(CBCOKc^X7^gX zz*Q#y04oZ=sdL3}I~#BQ+UfNM=ew-G!38^Q+{`vU9HeqwikVnD{bvf66{esab*iXWB(T_@$-XshvQCtWTfaY0 zP|=+6pnFNgCUQ)>aYyhOYQ^`Io9TZKeO27No)J&-UQual z`Ffv2`2PS3#^IS|Ee}~~ADKh@>c>-4#wX4Ex5;nWlg~sSl_i{d2ITE0qDPG~@R3tR zGP#?=el(jo;eA1(>o~XA*3DCgLV1r7mVlP_`~B73XeydHO}8}i#}91^BG$3+rssqP zyx`pm1Zkx+2ALT}i)|mh_){L(Jcy~4IDQeK>q^Ru_6V#N=Q+6Pb#&iiCU<*CLFh-esh26W3l!9{S=;d) zIa1=53nM4oHF6lS2c%-W(t%MLf<<2QA1$8UaahAg7p3s!sbsq4GST7O(JzK$Wf~+{ zBs>8>X|B`a-!a2bv1H*sOYzTdGp&~*U^ePP(Wq63uV<@SSq~-ZdCS|BUKy|Qm8as{ zddJ5|m+lFgn8;7?&9^F%xz`trqe}TczJxE8^XxYYEVq$Ll(#iNKnI|%FP{0$e0bu0s*uO#^|&#vE(w~R#k2jD-)HSx1o zPpzNsInU+pb@A(5R8M60hXOslfOV#`fG*+EBN9a=IduUm$81CJwd-m7{O2i9@}!A5uo|709WS$y{{?)%uQ<>Wt$RCcBjN^VF$$r_@cZGD%M4A8qOX z0DGZ3IkHU;5gHf;#O@;6T88gY>ynRSke!{24~E9dPRTPH9N9=xwIiZa=4#vHG;i+h zD(UR~PB-Mc6je)=hgV|oa-uMVxSxoDxL>KYeN)AJQ)6BW%d0#ScapAh=owx;)i?P% zRrS0bvW{?9M{+6m06Z4bib&~PW%o)F@oNRWZAlEBQ8xr=dQx+<&}O)yf`B&%r6}W; zG?>Z3dr=!2gArHeRVO!qk?b^3{AlV~7)(aj7oo~o36TW>aw<8%RLoY5nDqCkc@DLW zq`H~sgl0>@sID}HfKBU@=Jyh<#dk8A{H#rtE?IASj*>SHw>9H?u$<=Kq39YBjuFbe z%#$i?g|X?CHk;qBm!%zWjJh84@Tx1W8P8LlXX2eMtd>oQvPZt5JG@U#c1BB});AOR zfTcP^cl@KRW922;Av~l9$>HL_K9W`)S^U=Uy*;ewG-H=S{{ZadjwpzUhPdsumuQmX ziU6r#p8{!Jy5^1pi~OfY888zOUa3MG+iS7Ds6Uj&Po%OcV&!J zsZcR+erYV-CD2CFdU(~mH9WC|dPf)V_d3+&<3L9?yGuq z6X|3r=U-B| z`Vf?qS5Ad%J0&5&v+5NUYvWeh@Qr?jRkD+LBW}qmQ7SeGwt}rx`gAK=$T9gE=ryKk zv<#~lE#L8V@v9YRyQu>tye;jbIb@PZ3iIUN+7A;zovhSrNEZ8-3K!GHiAKZ7E2&KZ zA02i3D%QCbPKA&&>)aHOFGZc}z%4r2U}ra6kGhqsVfmmoYa}snuUp&-xu;>YDLN1} z%fBHEgtzJYza5cHM%zl~?SN7y&?ljU3YjP`J0vDZr1yv+83@td`e;aKhHZ`vd= z#+6??hU=p&kVSJ~0@tK(mPXV(OD_eKlE&0F;*gVKKsq0?xxLDhWzLnO)8nf*vs=WgXBB+ zH6JyM95df;AtN8lWtFg|6DX-{s>f;ENwCvjvb{Uw8_G5KwslobJ2Xa7>(Hbb;FDfdiEm! z04rx*@m@*Vp71U-e703M&P8B^pHwxxooouM4!#xVxBP!^Q>#w~?B%c5S+Daip?6_> z0S^h4ftSbTl;R>RIq8s+Rby~B3QimLwTTpazdHA;aFj61Z=Oe)p5v2nEF_^)+EP7R zZUX7Ig*RQk9Z}2tYE|p(t7Q8e*@NPl4&B9-EletGq=F5A+*9ng?4a-Md;b8a^{tKl zefFZCLz6qN-7bADd-2~yj8-x2$wXw`JBPH}TIv2h#Oh-*>d9NH?86nKnk2s}bBb|W zl(iC6rCg}jeeYEm;S@gH+siCIFw!H(E>COMXr{y-sTD)K+j(6J9eH%>i-(Kx+%(1& zCQPKU5M4lpmg!2#ji|xL zsY~}ezwSwIBZT5Ab-2=#vD})KXD#EfcyzAQg!tN*SrxlmY8{JL>6_{eSvO;F&UKMW zq^(|_A12`eN+!TIvU>X|Zfk&b=27f&{GDFva*FDA^wx1vBDpSWsEorvsc$HNkdQBZ zYnIm1l>4KV*Sh)7#ywfh`QNEt+~6iV2NR5y$fY#BuE;_{7|__+Yx4J1?zaZ!u#S!# z2Gxu6O9YFz-Tc6F{KJ?Z_WJQp>{8oxYeLd?+V|V

    ljZ`mxWT4Zn@qReo`vap+nT zxOM|3^K+)dZbzr_Sn2K7-W5o;=GGBk4qE8104qGVv+I-_sjm%jr)c5Gcy~=IS(7cGZE};XH^~!T`p*}0jknLw~cx~ z74T_JwJ8^o+N!KJzcEE&IcEYn)0F3v6`N1yEjCMvC#8?}Ro4Fi4YLaI45Tvo3a)td zEN)eS>@IT_Gmo<77mrS4sx!Wd(B|+>)CGw)wR2on{{STm93cFfH?yy=I`?P$ z50=6iyidFAv5`ttqQuaUG}mb#2^Ox$TjU(1C;8y|6!PMAZ0zt6FVxAnZ7)($AC{n{JHkQnvaTr) zwwS`cA(k`IRz4;Dw4AAyM^%U zk2`IBj+=_&VdLb`TaI?Sj`5CQ{{Sz?g|{i_!H<^&`e`KHN(0Q*2Pe;6vjqu0gAmq) zQ}JEH8Tnt$RWlgZ@r-2;T|Oz?E-g#&`_iV=F}cO7HOgZ(gkPwuGonqDKkct%(&y@w zJDyL;aCWKo=vVnHLf8s<>oU?lCAhUE2_EvR3hDHD6>muOC~e~<9OtnRT>d%gEwA?` zHK&G!wN8z4e5_a2uW6>&=*mlB9>`r-_Yjmlv(#bF^501cLcPRX8icm|Wrk^Jv|$Z8 zwUnV7d<{Z7h?CWy&^Zf_%$h8OfpR*VRet9=3Q+d90cC-~!5vyk{$UAF(QWEU_f-|Y zEPbMjdVVd3oSYN&8_#mQZ!?hLjE_b(aMz_6M*KoX{lnrsm5R-nUj~V$3O1~A5L(eRdd{4Jp z++qrLECOCbmr{~~FXPsY?%>+3;6WJql1TK}Qm>(~HMg@B-GGj~nJdNcOs+x1rKqbU zfqRQspM589F9?OJMYc*&+v$)>M5cBq9=F%%Nqyxh$CU9<=u}7_eLoKvp?1EbHO_H56W7MK7m^dZ0t(42_}bnTG6PpKD*f+p ztsBASvLkoKbg4P8egg00VeAItmrdj$9hV`ZvZOEn00^rJ><2hx{ZxZ-dsoV5vEW&* zhLNE~Yk5I~W9oF7y zW6UYCLHp{LWzMN$OALAyrn(Lz!g0%lpsbFg{>l~G5pf3!o==H?UmsY!rcXZ4c1yRJ zx3D~0i3VfxNzsXFO!?&=qo^(T^yLRV z7nHAsL10}VljmylzmV9g{L9(-sgOb>P$MiG z95?wBOp!ONFjkrt6bAJcgGO044Jp&LCda~qbHXN$Yx;uvkiqs>5tPBfSbMpWW1-9h zHxL|*fVJCu+Kr$Aa6D_I__xNa+kQ`^O5NTI?zPa(d!##-M-NAt$8!IG`+dPUm zDQL*HiCT5q4}rZq@=t|XL|oz69*-{>KIhQrOUB4y6Fy}k8j@5cU-)(&H?J_0QM1MO zIvVGm$1}_Nv+R?3Y`!s`WZXuu?k$&cauBwean_Vjq_$Po#8uwU#msXN6BrYARtNx!GpTkmJc&&|gzw<6h7Ff5vRvb&~o5JIQ?zp&OO$$EhucEw&6J~g6w8;5lK+SUs+$MKv|xF(|^j1ouys^Ak# zxg?%PAzNjYf#N);IMb3a_($h49VJh`lpTo!skyFibL3jI%fZX3b*b^~n_=Xl@C|jR zqp3~F9-E-*+4t3zdr_Qnn6;X-u2_8ri+0wstwfBn58D%<5bR z)wKYXD%(XNx4x^6SnjTql(3Q8Ur$*Ucr<*3{F^QU`(Q>uHo5aRuLt?gH>_Sgk?h|P zq3hQ3(CKa=-&qi{lNP4J*_H66m8#$y_+O=Y{>Q}{hEBq+PA{Rg@{iWg= z*iR|^Q!<+(JHq6oJ)VR=0(E zR|C!LxOI6ecetvP$xT4wI9q_zp|Sg(q;rYVIhPLz|ias1U1PrA{+s66h||ruzMqJDyO*GiZ>&U&ftn?W(K#rdaDumsEXR`evTT9!pN8AuD_fELzI3ub;k6LzTHlsKODuU&Z|n z{{UA%Loo}|Bm|`ix`p}yT+C%W^f?)PU3qjzHS(_YKNB#inCS)7GO|5C(o_2@x8rz! zayfn3YW1qFzZ`TWc)zGl7oBD_UP|Jyk}dp6BE>p;wY6_&<2S+Obt!34u1(he0I`-Q z5bkdRYc!T%a(3m%lmJ4?tTjG2sOsu$(?86*8|AfUv#*|>Na={L#+1rkj{070!E`JT zK?mDcu-$1MYjtyku+|v5TNb!Ghq9+%Dwl9hW)fRA+^ZU8^0CKra>(d06sG1@AKMEYV9yg(_`y1{W5wZ6|Ub6#D^w-_VllJB;g&|rc!S>pOwTD9UfDeTpyWTpQZ8OCyBG#gN=95AWA$t|K;U~uR zsb26J5?LtTLE1m}Ea&ItGcPz@Ct@rJNWOqvRUO=WwYJ&lnjbTcwMw`_bON{Z`GHB? za#B`w(3IF)?4@PJBo+SvAY1>=rgaQX0z0Z+42l)dTm`<;S#6ubsbG{{V>? zcj;3G@{6NCAr3F|{0-Qjx``Sjfg&2VCJ#2oM4hH z@{qLwr$U_#6Zdb+*sz!K7PMfTqSAkZBY8ihp73KBdSY-%n9CyMGV&Hk^G}@?zDFyz z3G06#akfjx&kd0VdlTFfAq}sKOHnooCA$D?CA`;UUA}8eznU$MKlI(pq$LZLjZ`o# zqL2w6bynT6Zh9sucg!>^=%kr&;r^R_7Ml|a-Eiu=r^M7H@5vO`{{SFsr};@5ex2f_ zX>pLGDL1$Mlov(fi}@C{=vll^wu<8N1MDx-Rqml~zin7krzrJjInI2>JCO0mLhxnB zcAyR+OesowKZev zSMKfF-f}-?D=_qBYDgV5=~i`97%t+Ix>N2dTv=81BhH$H^2qokh0>)P>uP7of|5h# z)P5fyGf!7M3QZ5&y}Ie;Oh(izG5ZTpIv$kLw+K?$rW0yO0>b8{9hR7llm$@nP!s+7A=Qya>{p1%RkAW9Bi16J71$OlPx3`6Id%E#g;t!$KY3ol9C#8t%t8HSp4@NA&E)!$uZVHdjy<$N^O?M^X~O14V&ynGDQYB&qrYy; zrJ205sN(p|ePxQ`k|GW^39^rk4ifY_C`{QA0X=UrKJ z2cYY7Fr0ATP$)9|ep&dCSn;;aJTe}n*+d%hMBYx0FQCz2SesN;jR%Vv ztvVA@oV^BO#;zy+biIq9crluXQTJ91#fTAO@1@+E3YgJE+}@#_2S`bMbfPN~VPn{7 zAxrd@l(UE$b)W;lj2VR3R=1gpnRwNJIfsH~l59r7#d!izbVo^hsN zNhSj&NecLq2T!`W>$Q@WFnf2!IjLI@`;4e3Dh26_KIg+75fR1=_cge8u`_PW+5o#j>uAhBHd}~q|gf? zlm&TrNj^0E8o;ZspdIH)BHle{se(}$KB7UfI(XAcKpi6&8#;^qwAEIEl^6y+rtcn< zUv!VM!A{*b*5+x&}JP*UZ(EzbHk}(80+60GN#wd?<;dPNv2V#yaWNnT7Zi zvr(XOq9}M?{uF53fc?7@Q$;r13DZi*>^vHp>)37RAKatEX9Cixvr@r7lv>?ue{i2- zPQ06wS7xi}N2X*p!-wW3|@fr3_&r_rUhQhG&oy#Gm;!H}QH!MMlJFp*QfZ zb#m9^^Okq=Uc_@X(7CXkoag*;hVoo*khEO{E6{Z-0Z9i@a!989<7_*1IQ{RJ@z%Fo z;`;iRwrhvsIfoZd`?3R1PH6xs^neOiw_T)b)}yZH8FV-uZc^P{zZza({ST7AP+aam z7nLW7;2dUCOetZzqSFZe5hLy2Xk=*lK$BaJ{iJi&HaZWqysY-C`ueQBL zI*Ka@;%B<^oP{xY*;waWl*8*wtdOLvD1r#BZE>)&9x_=Q*W_^fpF{qdeyn**x$Jif z&%6B79UunTebaXv@D^FOpGxun0F?Yn6h+P}pzQSZ_hQ@WBz-QG%kgX$5hc$-$Hl@< zgx}v@Uv9gJNlyWD%Ux0$O?w?N1#3cGLi&-dD>^0JjcZqRZwsRo%4L-lGKK9`$-TXR*Fw2pD0MCV(kW!Iu^q8tn946awFPQHBo97P zmI(WJDqi5?k`&}j5s9aL=bsX_0Nb)=^60_{Oa8$vR3L1bjnkbR*eZ{@2K1cjQZ< zycF>~UW!=6#srJ<2ly-Of+PGLf>F?5d(k zhM=}qH5VQR{Y82oA@OSOsl}s-)cGvbu-`-I&k_3C;*M|`(m6grHOgbec^@K#*gXFL zq(TaAbOe!JEno45)HkBi%rn%z-PzNXesOq`^gcl6d0s~&K1}yyA6iSRb#7eWdeykt z{k8OUJQXUX3?6&A&0FQ1hwxn8gt8;#!kjBN^#@Ww7Sx(r?hnQOQ&jUFU#OAsPGNmit>`i&ZslCR=`VN)p zlGlq^!Nn`udC4^5nT*aFdlK=v7obQ&!9(pSNH+%e_nqK1?>UKg>W0w~vK% zx*W~0^(!_tVJ||W%66|O21AO+J$UN;fkbFN!Celo$W*#Wt-FiISb%@p+1TYsS~qR+ z9yMkAjWR+{R?f$h4=Ee`##9fD4ci7nIQ4ev&DV2s8`ANFx_NAsfCT zf3lK#iHCz|FxMu0bWsQPP^F+7-D1O*&TT}8bXJ1(3GwL9xMQaI)Yj~kIFfwozI=W{ z)whNhjob?q`riOQs0vP8TOW?pNi1Go)1kE?-QidEnx@;+b*z^fmnIJ67#MlHhacu} za?&wb+&qTbLru6t5*b^7DJ~%BPPYcyS1q-@1qT+6>y4qfrglz2xmkETw0n{6OhmXCHy? z%57(%*URKO$|?Gt57=L+?g!rVxr)cKzB$R_pt~mw^NCjWl5AGmz#E63h_65Scf_`} zW}E7cuZHI-wRC0W$aC^>F`hW|mmdisE*(HsjG#I*tE6hBZwBlcCDd-X2ts;qy-5^*rlw&eB(fGQ@u^yqYFqtO+Zg?-DFc<;1mf1Vpy3xG;B1fK5-vTYRF^QP z`k158aYOtkZ27`+P0M|T;A7HvDYIg=YtV`%{GBOu?rs_q$uyt9y<%K>I`t_Ybyr_3 zklgf>A^BboN*rOdk-tOtdnuT5xnO6YijyhXwjL^fVQuB^xIS<2sa| zF72~;cGRF0r#0Gv<_EH`G_l;ynspPBu#pI13928d#*?O6Y0Yi$xup(#R3! zUQNkC9$HhrXkXfp*}!ztw#eawT?7H8wKb@-B@6?W@wL21TU5y4YU_$o0^|=X)Zy67 z49luQ)u}hBL$R)>=+7zji`i^;b~Yu4XJxQnsBV8UjKDBix!b%4N>;Q3)P(zM=UIG1 zYBhPTr>*|!U!D%HgBm_wEL@frN-}uKh(obwG~!l=8(Mb-B&3}!TrJYJ67g|m?BN+j zG{{dc?r(4Vc@fz>o0G?CQ$a--XzCJXxKpOovdz_d`Pft3e;;+S?9i;7Blw&j+)u0i z?s6IH$9BVsr-yO1ST~rMgviM~t@mvgZL#duyGl5{dSm(evM$NX^z<*D)O|xYhVA5k z{usFt%X0SJbJA`!eb2bTH%9cf0zn-smE7Z0tZu%;n(=u2#i_+UqWf#frJ@^)nTA`W zC70zfwW6b-x{y`3-f0c)KFXq`I~`QlL!sSim@(Bhbf)3MLU%4(jv=;!NWIFGKmx1l z_h$b9B5VSTV)~r7+%K))rmoz*BOb19o?oAet&ZXF?Ji}OI)e3sLf$_Wb^K?+xAwg} zQBSka>(kMYE-(E^djptyVB}crpAc>~uwQj7$`XB>VG-Ag!v@C@feF+-kVLeiThg{DD6~{9u?n?`PP)}@kMS*D+g3{~YVVjd@{#ku z>La@t?)Y~{G2IyaE++)Y@^+Y3dl3QExf$urBhzw7E&xKD09<=3(7rjty&65QETnS$ zuKU=ZlPTz6opx$HwQ)IQUby9x9BaF5O11K;U8al8covr#lo*{A!0)59r)uwQwl}?U zyqpy4Y4tj;GRGSc_oL|l0O|_~4~fCxT(^=ZJj8INM3l3=)>7%dz)2*5sD$H_@O_g3tDU)+^vKDV-jS`XM z0MG$nNpZY>cr@P0g%H1Y^2aU_BWH_(b|{did)87_orvQX-e zp&w4)UUIk$7WlVwnUl$Q_BjXnTkVj!AQOFxD>fs*SC4$d_|NXFQla?gx!Q4`B#dK; z9Pjl<-rghLe&T<*_!kfyCjcU$r!n6ORU!0}RPxf_!W66cS8MSd!he~sQjeGA zed{v29MmL>Bnt}bt;QA#xIUtak!x)?^Q9~dCbQye&H?7P1 ziz+&cCdxW0k#`ne!z{iQ^k!zZxjz=u@T7o8<6N&hDW_KSml;?Gv11Z5gUWZpF^rzT_Hs3YU15Rj-_ooPpNKk{^QNXDW}_z zNoa+7cj`c{cYlP0ljvV*@=`}+_P4&W^D`lTFqVjDNl06ZSVB?&1NCXGd3QhYw^!+1 z&fkyRjMr1?cNOQHN038n4Ugopvbl9MRJhDMl`H@(NeR8Jdh_qKoj=!jw zIu&x$X_nU!?eyj!L42tw=#ojd{OX@$aZ4?FnHuDoJkIit2gE&Wc6TDgDJ2&To7{t^ z*;Lf=`xZ+vCgVm8y~WouFx0~4g5;#I#E5taa}{Wer|@)>y~*F> zVl&G1K740hmjfdzE6O{gtf1?q!RcPZrL!9=>~a@ZIL{*(Jc3+ON*+@rAB8}lKV?SU zI8Aj!QQ4eV;9ne5zMR_^i+d9vI`7MA>4jXB>wOLMs@>iWzeF{!vl+a!bw_J{sIo9b ze0L>C$VN5_mQV2#55KK)Tih(5vbzD^;HlYJT_ks(7tQhBZH;dsgUBKM$pz*?8<4Le zDvBD@i#+?0ru{kMYp0_fO~X8pN(JrV_Em**>(rjNB^TU|xZ9{UQVx{tQuHn%II%Ij z-$Fd-_4g+5I&aiZ+eh)p3E_{>w*|V``BpeKDJY@iZ@37$wDYEYmEtWrkniT4@u_%t zfc6pM<;O~sheJvSg8h}KR&T`cKEXlu&0x;q(2G{5Uqdi#6psNw_KjhYo?^VKoL?}U z1do);p{?;A9YI0dK=+DEew8RP*Mo&iZXW8)TRDOc)OLT2NnwnvROcgsX+cM0nrLnA zu4(H$Wb}Sh;2O980PX%q_!JIbxEZDL48~_0laUN%pIgXmI=i%$8|?s>Kxn@fJqNOc zwIhSFj)hR7H8h$@BIfPi)JHAE`cP+P6_JhzN?Lf|L#BqeIm)ziJiov+HuEnOrTBC^ zpNaGQK5hbCo>3M{miW4hpJ=AI-bN3kx--N6LGe9}TX7DtMD!)M5*CsRY8OJh2(Fe8 zi#bmwo;^g=+6t{;ZR2Vit$hipycqNW=$@X+*SGXWd#urMuVb@!mX)3|^+e-wvKa>d z00k9SFQXwVm(+a32UM-WvQy!q)~q%3=Pt>MTe%dwk_fnYML!Hx{l>4gzCE=i(hR7- zlCM3``5bR1tCYtepUb_Js4EwS zI-;LeLR-1%Oe?d3LXZMWR|Z$si5aEkoBQ5weO z-ftk>m)U!t#{{RUMY5TMO!SLs0*WlijWUD|WyuP7EaVybVEm4(t z5wnm%hB1oL=;`55b|@^ey4}=Fc>e$n$>!vuJQ%7_C*oNLXde1E7sBsyR`^jDkEzV_ zKP0DCw<$iSW^kU~K)^s~IM8-V(mh3x4z~AnudhFke}}d7t5UgnN&Qcoeo^xawynzS zOl9!+5>RZ|2iyU!hn)T!(7UQwIo*GeSr^pOHy3~-nCodO>NOwVMB{&sH0z_w?PA9Ft z%9@r&qalLNN=}yix{bvMtD&YREQE;NeiRU~8kX<+syi{rbvy_gSm{Y8fXHB^6Mu$~ zwsB)omH{&1A^Y519}1OWw8B5DD7t%_=oc5$@AI?r6W5s8T`Lv>H?qH zjxzcbl|e~Tx+q?!8)qK58>i+LXDt=(N9o6pO5Bu%HM^#y)3oyTE`_9iUk38gUuC|M zqRHH-c>=?L-2|rh9#*I8=C6wnAC=JiqPfZa9p+WL(8?idJxK*14-S=Lx#=%LYn|4S z50~_9%J=U1_Ereg4J}T8sO9tt9$#INxxYqyzyeV{s~tzeO%|V%HQf^Xx*ui(bLiig zT;6MB_-JXh@@|BE%7HKa9&=#32~C^$TCzVVjtF_cV85gedHK?&{w|{T=}j+_#S!y> z4p-7QKS?D%nfw0$vbMfc6EO(^9{N@1jqXpRg^|>NXtsR13(%k1LIuWiVeEd zqw_bG5y?i6M6n6Y^1CI^%B<;z<|%q1(X)3?Tp50t_|KW*udG>@mw zXx+kbidnF}np0GKro4lnACv}rupHS$C;2;y-Q<%_zmmcB1N&;m7(LIY-gHO_z(`Qn zUrf}O$sr$7KepgO->1HKtp)}3k1dA(0Enr7tDtIJZ$oyzmphFm1@`d{Hqg~YK->>- z6c_U@CA{{YCkc*1#Rqw&eNf;_Ita~7OaZ(~%$&3Gto~Kg^YDwyB_EBcNk<}({+qdS@@T}m z>}c~;ve`o11+csAw)E-NnY-ulj69NMjD8U$`<+F`{{W`%b}|srl$(%&0y3<26)Ag@ z*8czn>(93T0GV2sNR_sIg;sk~mr;ygqzslHltRE}GO^*d;U&qJ_;w{;0Y_44xc>kn z+jT9*EiYQCuobkCdn5zY zTy?JJ;=dbP5c8VodH(<(@v60|R$cj~{{XohFOd4E?-O|0n1A<4Tv3tfL&2uEs8j7T0yH?x?u1%6LSZ1D=_KpBpKQkf>P*YeM5mMXrUVR|iqx zX$}1t-(QOz(%NvD_A}{=pJqFw#wE|@*@$`E!wv@GG?viY=|WNlT{}8wJzw>n8rpg4BN~kHse7dO4C4eJ`{{=a*{X1Z96P^>~l_QxZFpKA;^$WtblVq4sv`SI-k01KZF70J|`oX~*GOKk|UhUDqfPLw_y;}^!?&YME}SL@Xb z#~C>@58W8tCo>v!#!giTH@naYP^t8wfa|27+~gH!O|xeFY#Xe zDZdmer=W|s{T|b6vIYOAxKUw^^lv5 zw%5w4tVs{XZI&J)I?mgT*YOoqvto$dFm0U&!25MSkuvm{h$Xm#d*;PNr`!SQT&FG0 z`%ehH)6(-h^RRd(aa?YEa6@Zbfp=1Xzgu;;TJsKec5KfpsnJ5UTzZ!@*4d1qu3pY-800jN<)4?j=YGHnslhEmkGm9kqI)i?5q0Ce}V4HDgrrge0=a z^u#jqjr!GvYoHm+&;u$nWc&aCuC&ZkU4NI%?Aa-j<0Htv|E8tqJYj2Wh%J}v55?>US zqyfJ{7?mX|RlSsIxkSLvj$nl`HKIX5)5uZfw;3}i(~`&&BhR34 zxsln@3@ zb-Hi)jdJs{YPQur8MLv2%rL#orB85O7pndvRrYrNm==h zJHp}_=iI$%$u?K~k<}_%jna1|X5B?}7w^Uz_>ZY=WnFSzywi$C%V3|45GFhAB>c0t z)ixeAQO4fYogNKX{NmF`%D(GlV&rn^hnC?mB&!~x%#46+Hv9I|RU`}7y6_7B0A)B$ zrg?`o3gc^1f@p_9?6+id?Yiz(F2z0x`6RxiDGDFEU0ok8+m#j?J$LGPi$bs4loTcNz5y*xcVCcL|h`)+Q3k4CWq3 z`gLig&xPx^+4#==Rrw>Iub086y&i~5<7s@Y1dxjyX$D;eRMlyWnW!zu~fk?$ZHcggw?b%JFfpiR;W zO-0!y02Dy$zuc4|f-@>0T26x0>apOK2$ZZ^inLAjp)HIe60fKxXC707RQ_S)4MsbU z&B(mD-2?>tCXen zvPievdfmSWDdFV6$0BPhm?m`z~*Gebuup|3@qA|Io zrxK=@Ao_j@zLfjbPoR}MkISnhkUOW$F$-zTS1ZWlJt1bqY4Vs(+gond+ydUqQaA9L z@_k~zxNG-oVEQseg6{re66HGU-!U#?_rERDR^rJz?orgq_Q%E9mkk+)clejQ6%|X3bl{HtFa|F&|m#sCHhJ$ zxE=iNcR!HZoQIiYCPHZ?3}QsN2#SXM5*kYOkOqR_*G0zsXIEGB#T?%EIepKoDOa-Z zD5!3><9p~W?yjD!V4=ElZz2fExRMX4@h7GAt;aYaRlnXlPEqyW)Nrfq(xlQiDdfUu zl5SO{D>yeHn6JugG7GhW{q-4i>=L>37ye@6j;pY>L);0GL`q7%wFsckuvRmm*xtse z_a@P6r&45s1B9(7cUrI6v!P+F7m|AhPSV}HE1lYXPLEbdw0Q3z$zxXAnE7jrq#NA? zrC0Zg<#{bwO?EnMEz2uu#o+JnrygsA<+=Q!N9e~x5N`dN**rG^5JRD_LF_Sc)y%gX7cCxZU~ zf8!ZnkhA%MyMHanxgOl*Bu~c6MddRgwWU$q{m`X>a7VtoPCsHElF8@1j}pd_acgYc5K4#|*LlHzA8YaV z`;qlI-TwfQPU!gH z=u2qQ(x0GWC#u52iQDX@Z<3NND1@R&8JM%FSBHtJ*+M0ab~W50%%u&g->IP8S_@R! zC50t!EjpX~DyL>k#I@CyWOScvr9j6{g_4Xq8s8uMS0-xqnX2b6ljX)SS!9dEfGr-~je(T|S30>5ecbZoYb)#hv;> z%=aM84YE>Dh?$ZTzUk09OA zT?KiDGc}aJPl=9@V?LxS(o$4XNw>0%$805HOqFtmdUt2d4ny0%QgTy5v2Q&kqJ3ng z1w<36)La_&t}mX~P)R5%yP0Unfgh$|)tOJmUeZ zZUZ#v3yF_$^!2(BwGY4=M`M@QYYszoY03oG35(3hnI0-)t+!oJN?Ra;R86f_Hu5y% zxofQnuBXyY!*=hI;qq)tnU_{Hp#yYOY8InmwO6k^?l z67D3tT-Akw%7OqlKFX=vs#swoy;Ak+eCqmu;_PugSWywgdKnaY;gqW7r71?^?dw!E zMfN?*;{3d}wN#qwx_?uf*D+g(D>fe5BqM_Pw%NQ~g4l5@LiVcS#{DQ85lsz3ZYM%^ z&$_bllcK}QIqPHnRE4bAlqTa{de=WC?bw>^CC+hB+M%?+HV>pxxok52DTwa}BF=3J zWl1`1HPY40K}j#E^L4);u|jfdtXXzqpW;rk(0NZd?XI(+AAzq`;&=F2MR{ej%(v_0 zBQBUoK3w9dx2F|Lw~1ABe*XXpzf4M>Z+-V>{{YPJ4T4(6f~)8cjc@n9E)OxR*V%2$ zyo{%jLb`2>Xn7lS{3ze*-FMowY4G;{0M^Hd>e$41 z%GaTmveMF0qUk!;qsv^HX|q;w^}j&6bF#{I?zQ}C-~C0ijAX8SRe8wG*>6bv>(|bz zE!&;1^D&zB;||6?+7!B*G6^0vsH_?kZ^n%YV1&XA-um^X)-%vjVRp~_s@|@}bkGx( z@<`lk4Jv!&7XnY9&A=*Mw6*G^z)fvtx8pK=#df_Nsg$GOLhI7X<8%HzkHo>UCAyhfMsU7 z*kTgarQ2PG!u0;H9euLIVp4DXw_d3JX(OX}2LR;I9*l=rEHFqe5_ALGUEjxk8@laH z^hb;Qzvdct#rXASS2K-_nC-Tvf~=bpUrTFndg_%KFGIoY-J6?fjMc<252*UM)|6G% zTLsCa*TQ()A6P7gUm@Cp=_WRJw|rgjpyo9n7qZv;jaS+HVa89Vis~~Dm~Tb&lOlNz z$ni;`UYlx+qB^A955B0j{{Vux+xvc2UuypVC-2wYZ&qZsEUPm^OA67~aQ(ztG0fn}13rmHEVINVtoK~0a}sv)R!XV=yYHFO%)H<)U|N@(hniVGn9(Gt-3xY$<-uiL8UC+c{m;K zj#;a7t(8YD@17e4HOy>i=5)5Sc}YJ450O4~6&BV`zlrz-w!_YE36PA})GjfMGmpe* zuPD74jI9!tTd5<$wy5$g_5KHodcVq0)4S`=W?%mRl7B05NJ}V6K);8yQ?k;6M_ot6 zwITjooe$Y=<-5u8b7JyI=s7HD++?^y3ymI!TMa#xO@5=#LxuTA$K6v-sPuk_kMlnE zNMkUFJXA9#G;{u{QpNWWt64T9Pcuzx=wW4++*iXBr^?>!zIdK)MzCo(Sz#~}c*T&Rm2lO0UmiXK~*j`Y{4nc{d= z>+%hY`0W%=7wALpWMv#j6L3yFv+dX@kd(39$#1dxc+NGkWW#MB_-#!_*Dt6a+$6-$ z7sQ+13ugU(5khi!?r-npMfuF!KMTPb!yek6aQbl?*q@0+bkdQY8~jU;kJCM?@th^x zt9wz)v2JpwrSfLuM27knSW=RZlqoj0q*8p16H!>|T>P_2*H4W zp#=!`NVGc-&-o@3n_{6gkzexsio|!>Q7TTlg?wIK%B}e(tu@u;x-y@Y)`rDRO=kyY zeL#0cHx}a~8#fgGR%UIN8;Yc(_(=gesY>==rE=U3r@Lc0KZ)?^Tka=>pJRgfmw0l# zJ0gi_gqdjtA#l_!xdX#@TJ85fIq#*9L$TbsNjSd6SKYo<8<=v;uOY_bjutLYVv_(X zeUuYr5`Cvy^xur@N{SR2$Z`$i8OcFp(x6m&pAlV!CoQnLkx4(B{yeJIuEG}g2YSa} zi}0%|%K1klaQ@S~Luv(`iKY>aGz0MsG}4p6#Zo#mvE9nYcH@z=$mKlhoM9)?jM|7w zltsQF(@WPI^4<~c*W$aK2OqN@((HB5Df-_`%scThkc%@`jjc)FZN6sSe{FDlck%tv zw=;P~r5|fk@=K}1JdeD5wti(9hBMM* zclXn$*BorCaX626WhI$u+SD#upc0n}&c0WB|mS0as zeFuESl^qjeyqOFKX*0 z9$xygs&+`Ol@S{zW(g`ma#N%es=xxn-D;nWhLee$q`oq?!NyB@Qgq(ifNc8+@uKT{ zs$F{n*;Qlmfdr+UZzP2LAf`1B2Y-cHxMm&mZKpH??Nl$Wws*WOqx0K8SKMDCF2? z&6SSG4^ZvtVDIK1jb|tDElrNGSY$N*S#^CRcJB`Q!0pdD#{|fL?;OoG2h2usX;W${ z{lyOteQ^7~2ESSK>W+$!kk@Q~66K4PPFg#2ph!dUpBtK56}VXYMO0m{?LQaXW;Z!3 ziK$NlqqyAlp}{5CUoDMX-ma%psUG?e*PxfCdS6;-L0HosH4ghhzO|3A#cL#wAB1gW zDb;`z;c!1~b35BI&qC&!Rst?#BinDMZ#5fLcic#@_E0)bK0VIn!mZ{nMEmLb6ZVlG ze;18a-1$Tey>zuT9m_M^*R>OrJK@TBER-u7y|=RQPR~##<+~o2HQMk?Vh_o7DR~Ih zK8&Gw>(KE_Z#4rVV^57|VasfkskNt2cWGS?4I1}E?0?#lFKOm;P#eZ6~(=s#pH~$9_oy;M zBJ9i=OJw_ew;5En=C+o{Q*nQ~y4`+CO`MJVO2J&qfer$IdGF#N->1T>cxhb%kG&i} zQk6n9&nwWqCw*;wL8A{J0|%IlRz4#Q$ByK9?IqCLX$T(~1oRbKN5OPd{JeUNTk=hX z94FA`PDRf-Un!cY{F9N)&PrJ%xh6xd>P^jp$pX4PP9loGOi@RFC2ae$KJ?|f6c)Y7 z7P4y7*#kq#rwJYC5g1rZ79|zuQYSlMu~l-E-7=QxTDv z892%rNl_Ok#)l5(U~lRAg6cZ>QgVGsMmaS&(y&PJ>S}9a9RoUiflu+oySS-3e%d`- zet@S2QXKn{an2}J9Z3FRG>s!8O~3b*;ax8uqbr4{vF)Rp`5$z$E+n&#gx)Q^e~1qE z2)CK(U52LxT3t#T`6l}UI8W3UFyKz)3to<4nfCRVk`ABuN_tfHel1nsgA=EgvJ8>( zZ>ns^BZTY$fMXn)K&DHmQkJ5Vp|0Ms9d=h#E&e5p*8SX#b{ z!Q#me6D58sI&>5{ESNn^!U6b=0R)WjE{X0VqKZMF#o#<_csKd=?Y8rODl~Ax#%y4z zE~t`KdeF+62E6$fv8;>0KOw4~-D{@SqSk71$kk7aerh~Gx1PpCy{6aFmsTW6bz+{m z{rk~F)-2nuTy?bz$qYetA8%Kk2sq`HYj4Js8bNrfJHWn|s@vm|%86i>LRd-EbgF%+ zG+b-N68JBlg8m*gMRe0e-nzjz#mU;Fo7>K*?88OPQ^|I4%){_zu_7bIms?ONaVKd6 z8|X!HoVJ{7PAuqnowro-kFFn1{r><~WMjC`I4jD*Dnb}>)o*g@(%pJj&VM)ftD!q9 zXSn=#mG|L>eUHH6duHYuBxf*jiV6}^g&-wbi;r)Oc-cF$eyTd(%=FA|1Og})f7tmh1?@|z_V&+?P5UMTrw?DrPER!Ju6*5u#cUb7fNF~n95LE9)Fy7A zf?x3I_|$Se7CvpzKm_W1Z%KJ1I^&+f1Uj1wAAJG5hC}9D2_X1hk+-nT9mA1ir6S`) zN!q_C>S5+4lVP~0N~XH5rL{B43r3z>DZ82<3Q`g3KU?S&k1;N|lCFS@nqKI(hUmAi zQh3Hk2+VKdD6PN-}~VS5;W`5mu-_zs8%wVdc`Kjs;#-iv#zxwQ4bEc#^ePk zo`b@Y+T|k%^<&j=@}FYp;r*lJ`PR`U6eX>j0+$L-)9k%)e4ov=pqg)~)Ny|jvGr*B zJM`W3{PcM5WO;@xGL;mFgq!Y7#=85f#ePfj_k7xc=vq8-*N0Sh{Dq7&5Fud#T498Q z>`s~waW%%m&na~!T}o-WpzJx*q_7hG)DEV`t?7bDtl6p&c}P_-O0yB1I&MQ7BgU-phx&N5=h)*BFQ%$DssP|MCIHP{{U;T4y`UT6$&S! zNEf)I-s3|t%Vu+(u5xBANkk#(w3BTGt#553X4JgPDr@y3y}#|Gd_Gk#t_6Zlo0HSW z#;JLI=F7c!qp7j+*=@9Jj2a0ax-2V^msgfYYR8;pAudE%jIKn7Qp+u*scTh|00Yd_ zC0l)j>V%~5j%)9)d$|7q1mexh#Al*P%FSP=9xHC4)u&BVLJ6=Q+UPzp@td~pSy)At zGF0Wyx;ZbkA60O$e2#kgQ)f;-#J=ysl8tBuCV zhlqKVY}(>mY&dsq(3-L2IK9eZr##>vBgX7YtR7dr|Y(jAM|se56K}QDJ=vDYmw^TJLl|FsiA0 zompXBTDt4TsHAPjJG;x%F_6Y`Ue?G6{yOx&ohV$74N8V^SmAC$hrbdcAW!yQ(!HB8sjy#F>8)`9%=l6@#t&8Sxh{0-v>=OgU2~?6cf9pboBLS zlS7ob3>@Q;1^)nwFX2``59&7#X3wD5@z&4%Z^Thj4g16T3LZ4bBJu)nOh#UWS2Zu7 z)=n-X2FY-#L#e3F=)^rc)=#<{Bq5>{oo(Q0f4#tF+`EJup28A8fED&pCCOv56P~1- z?l1h-GC?0K54MFaS!1w2u$%QTjPet38A!MGQWEBx^fFvaey1&W>%C55Vm*Pu&G~$z z$^I&kY*3S@-&9*0_iQ|8q3b^c_*8T6TC%+|eC^HVaye-(Ovy`fY?TWs_Y_p3J?5CM zr4@tP9<(cpP*_A6=M<%AqHR_X0VuOE!nbHNJu16nskp_G>{^R^O6})Vx5T@mJuq~q z9`5Y8v#`id`FPgbWeFsmz-VfddJ|GjdK7zq6xX_`uUU0Fx3@gGu2mf`kM#S1+LA}a zdTCcYHuPMvCxhn?m+&u2SngZt4j_R zdK$8aa1ulGD&K84Q^2f{uqORzuz3W~wM-bgI#8hc0+8Td!i|tDT`IPR@2FP#0+Jjy zv29OYlt43x=9mI1j~Z?yBoIeaM}iZOMXss+v`VaKLdDdeySa?bWwLN%!-(vdfd}#n zq$x+n)#e{J)W2r*qO%j$J_E?>YSSrA;#mRlj?K)MXR_$|QTu|$TGcll@H?&RK8UvF zzmg#im31x*S890XIYDZV^A0A~DmNc>cRYW=wJUvAPdeZE=HT#O&FUu2=+T<>==1s85P}&G1!%DZVDlDyYY*0R*0aui5;SqH)WWKK|b{jrG zok-cWsYG1(*6Gf2YH>(<2?X2itKD?PB^-(+3LX`GtK175c-7{zVzzf4p{t~LUao7V zO@MM8p`et24Lc2dE`a+98{E}Z+43^3=8J475p8H26~(TjF`fZWFoG>>-n{pFje{Sq z)-o{Ksu}Th#9?g6Zsb&=h&P104LuhhXeG4b{}r)5ZftZodkyVWKv3!|kkLpfWV4sL0rhf+>LM zjNKYv%BpUEm}UbvHML3X3_xh%0GriZrPN{wif4I7KM@^e=Pu~z+Ce7&0A*2f9GZeJ z_^uA^iS%6#DeeY-c5fu)q@hU{A+xRPgx<1NOD&&Fd`F3^R+Sc3GbU#wG??$Y>X}dF zNh-J9N%*{XJB@8^SWC(&WB|5g2RA7yC)+~DQrb2{2Z~(qO1|SwLU;u%F{1efa}3*w z$H;OjRtQ$oKs&V5bfzNXoPfJSUf9Q7TLRP*m0(j7fiNvjC#N5=u(a8bem zOo2EgJgCkFmVOl!owgrkFCExt87k!{XQyyqq_nJsjkf(HTCA0+_5{;Imfm$bA;l*8 z9i)vqcv0ix&?f}hJniN&8E#1e6uTxWRk}eY*GhG@>0UxoQRpwo-zZSrPVkw@aQGZx zDtRrj9Rq3wfjdV*YC@a(9z9KQSk*^$tRxj8Q3uj}>ud+ZL4PW0tr=vn*WgrN z=6N}5miHf(@|Vi8S@)M6D!WkHTA6J>VlDX9wK~#w0?K>D8Lv-7Y_rlRAp%W*$76}Hs1dVLL) zqA4xDAx6B!Nql~UyJekIkL38iLdctokR-CO32`^-MeSUbvWz8$Bla?_VT=62?{6yC zzaOYyWeRVN{nVtU z$jQyDrPxQ4aJ+9R$FmzF6W48m6oid61IE;~_a}gScQrkEygug)_m%`44p}%nUI+Ob zEg-Gu6rxMYI&L0APYUX|r3o&lr}5Xjwfvj%$;g?x&oJ}5%ErBSXC&&ELUHJdncC5E_E)=eyJ5(gmbosg1uJ`L%x8cij z8H)VD{pi~d8HW4Q-G)1h&1YqC5*!#7n{BLPE{&@jMaqcqB+>pk@u=(T_|0`4(%AND zP>U(_Onys)){wUVy@HmdTitSM&b=tZvL`F#*inV7s1CF%poS`&JO2RbbAaHCRdS5? zrtGH{6NzDDT{hQskBxZ`$@gXMwdjh^lWgA#ylY+im#294b-PVu%hn#vYlt&C05jFt$9uK z8Fw$~gSlJ}9_Cv!hXw$*`leh)fq=G!E>`pHLmmc)WkCkLASp_Rv8~*?>DbyW&x>2V} z!3f;)E&Sco3ngTNEI^=3PA28EB+P#{by|9cw)Hv;$7!?4oJavn*WW{(Mx`vV>e`hh zv}?BZ>!n`s5L3A(b#QT1pC)-s(B%0tUnDJRB;VR==`JdT8qz$!n6)Y85SICz3^HF) z!L^c;{{TtHingQMwLMLATKb-H!)Gp%J zsmZB2lT9e0SFs^a=G(k&ZnV#eHSfq~=2WQJ2SNF&SWFMOBLgB*2gj)CMVuA@dygfB zz&ZX)gl92ahYsjxCy9Nz5frUcy|sl4k1FK8MZ?2#mkAvo3(3n~%%it*{{UQ^yOw4n zW;2W>p%t_sM2e*$vQw!#oAvOoE|2kragR=?piAbF=&^!Ge!3MP8CPw~d&Z-erbL?7c?L3mKcQD505}mFV75l2|T>ZMTt@S!etc;LT=2olhP+EM>wS#yYJtJZi zeK4)xq|}q@LdazwB|f1O9~sTFG2=A2%X!W2D#`$rYp1fk$KwvWC|Eq-mXzUPN5Id| zX(59lVYGA?(yyhf2=x}PVa?&44cp8VtlUqY5({WaC8vebLWQkjJazD|HTgFo2PX!O z^W*yRkLEisf%o&0VdKZ*e7TS34aM5r9QQ|5*!Vzl-AMDV9_Icv31-M1nW4zxV4d>J zwo+7Mk%x~z%ikND2+>-28+5L3kEStreao9{lUXT@T^IVlQw zAS3rv;cNLPK6Kps z3dUVbq3{oZ_6kzO$yUQyeD+fbDHE{y7x6P&(Gw%Ord$B^8<0VOsebm{2<-2lEThJWYri~o%s2Uro zqNBJA0r`)WX}6w)kkQ{kLt>suUxEPy{uB!o;JXavF`{E_Qb-%9KH)3{kkXQrkTe}> zM`IdD#F*VOEc7{pk+}0KT2=6<4SxFWHIkA?FSiLquGIR8Rn>EPwbw;kVrYFySUw=$)M-N? zr!J+*+MeKL_gD~$O*m3Bd|Q=s9tCZvy`Nygz-1sRzU zRMI9SI+PLd4Z`*}@vj`I#xwC}d0bqP`TS-il584Ly|#^}zO%PtDV`e*X$4usWS3&x zCsq%LZAYiLQZJOLKI1=SV_gtMo;#j+?Q0PGiar6Q{{VFt_bM0pjE@y_^d&gH(RVX6 zl{t;!^CclanPC78KkpG$SA3UJ53Gw?{yVbQL`jULmk-JY}G@ts@ zLG)4%zAd-k1U^q}JCPPskR))Nk|eflw$l~WSo_ilr{nT%Kl+f8;C2+c#2YuYy~t%D zZt%RDZT|U&{j~QcqYsklxBAdqI31JQwc)3tMY(tRX^ zZ{lx{c!10~yQ~;dK0zwK?51VO>*MtyIC1x9vlUJ|D&mraKzT)70BOVf1!{bzwtrF> zhZ%EO@nHt>lal7gW4ML>;X;A`0Of$Gt>`t;%UMYEJtbWrbA-B8w>dD!%@xv)7Ip zCW@;R>uXmvb$w2XxOo!nbA)Z_dR}qB7 zhNfW}$x2SuH7JXzvNgE9ct7$TGf~cZ9<%XFaU6P|T_k4~;|N<$DHllibq3b=*Pe0O z5NoO!GKL$Jx1f!uWI9| zj$1`-yVgiVPM+W(ys$LTsGr+S)2jv5bQQMgHPlfGmQb}94U?%Spa4~Z@@osZU33!5 zJ_UVua(+F{`?BtTj<3~BNUg!iK2x+h{8f|{1L8HXHS|xwE3%DTPrE#mpGtOrLxPLt zCvEjU73oUToXcVhkSs2)wHeM#*S3Vjl?#A$q@gCs`#2O53AN1^CX`ZRgvucG`zeWI z9gW919;QLC0Mbyg2bE8_K3`WX&J-RZ4X*}>0{T% zfX0wjx5bB-omN_(kox^024&egnpXKd;FVD6)Go~H3t!zyTQ<>cOp+e8Hsl-9gNj`O zjbupEubH9Bj)9fEMNUQ1t8XIT*;T&NPbGu1p|WLPs;+2_C)|W(hAt!?m$#9jy8K2X zEQDh(a)r$)CN*4Fp)bY&RM{F4Z9MCr+?EqM4OND8e+SNDaLZ-}Tqx)X|V=w0|;b;fdy7BO?+JeC~g(0NuV4ExrO6D-`fzn87=Rn?LZ0R)@-uTJhpNYGz>9X}6ZNSzinXj;;k*GnpwZg&TfdE-I^H#JdKj zhmvuK%4AfL5#~Kmq6NsmTB+FE@EV<1>Pf$qHiPPh!c?tPEGnMsTX`b4eF|&6y~vB& zt{X2ryo6Y?Z|TEfNw~Ig9yQES6-=qvc$PLXfMdo0}gmXsO+=8x|NUc?Vxqbk`sHxbGedhz=1GJeM#k zWU$&?Z7O-Dlr|KhXI_=U?ED8-cX_4Mw5Ol-X*qp;O69I|F+sKb!jQBZr)sQ02d!D{ ze156d;`Eov>D+(AdgD3ldrciz6Jzp6)SlXrSOYCM4$~pv1>7W>C*Co4RDOahTGXqX-iIsOVIvfguHS7#slOo?TRTC&N+psD zT>6a{a&$Ko2GGQCMY(QeJbwp2Cbez(WGJNc*4kB7h`iu<{{Xt#jek?+=2l))mFMKR zmfW2_YSysQZnf&KA>nE-yH#=O&!p}4b*W3TM!CM-^1M7`Ov+-Nh|B3JO57UT+?$d1 z(%-t&%H3Vs_9WCAcmd(Nd&#*2icT9AIZ65Px^1NlJpY+WMrL)f}aEevrC$)pzK58bwP;u?FYDwJBt_ukwbWHl;j; z0iaO#_12Kwl5r1HX!;4;EE7DJx5pIR*D>-RMcJOh%WkrUD zla{9>tZ~ZvKFt?LXr=SW`24pV=gk=saBc+RbLPkAW2*Kfj}hZukMRpOBU_E#TI|m> z`OQ3Z?v46=BQ@4PMq9$V+H7YI$4!o>?yDHfASTBMwaqhAU}QOOOVc&YrUqlA9<^&5 z1>}WCQAKQn9P4VRxxXMH>HMOat^QCl?kbH<6-lBJ9X1HP#j0v-1lZ*sG{OTR%0Doz z!MuQ%VcUIAwyohmL3xjxQAz1jg@UtL95&l$szE=!K(4&Yuo=yd86aIL78Pe)h3q%q z?4qj|(2t0%781xYRHUAu`|0}82F22WeFnZX{^N(!{;YJQqrSe)gN*hj~<{{U@t+VJQCa~`2PY;Dt^tD0-T z2{Ax#O)@c-Oekn+Y15Kvjs1qHFRKhN(O?pME1cceSq}y>bZSL&Tam!kY-F)xYUega zqA^-WUmB{|tz>2lXy@tsYOcf;3>;5iz*4TU=rDW7rBU77Zn2}L9d$ zAtr{?5U7P4EP|u#rL2>EgGF>3R0E8ubs>?nqTu@|H-4bd89HrhM*GH~fp0xzkbHDC z&Ft;8Jym^3jO<()N?P4vE-79oTBg~WXG5#KY!(y25t+mA&k~~*I`q(0CGb)@4S$hr z$JC8W?cN<^tI`_u7NjlKcrNw+W3s(KGP`Y!bQ?B53Ryy4rL7+{x;~(h+gM7eOIQLfdQ zY~`rowxMoZyZW5Bn!!Bm`7b6hj@*T*NMr->j{&WAx_8A#2X_S&v5+vr%0c*Jtw{Y! z+H5Mv?jmWZjzcnWiA%`d!qRRO0+N3EkGl!13C$*f)A?Ra;t=$=9NTb~%9KT|<9#Yx z^g%wwe~Mm&Dt2B&%J|$i9TTrgau7%irPPl|$x18L<$ErAim|G=U|QKW!A?kU>%B(a z2L5z(&9A5uW5{=Tl^onneHDQ_^|da zEBdXKjoSI7_*Fjd8C9RuUV2fV>PJfU>%I6%3iSD8M7F0?65NE`r6ivbT%O+(D*E>* zxvn1B$Ntm7(j%7erEOaXOorAjl_ycJO0M^0x*UvnK1NHv1SNBhGYy9wZU?Eis%_WZ zQK~b25PH5byJWECg0{}_9diEwnnh00=)B(Jv=81EH3>!ybSSs}DWQAn z(q~1;!EwI(amJN00dxxqLQnV`F0V7arD7~kZ=z0CS7jT!j=gy{lx9?{Mo4`3d z+#L6Vst0o~0d38zRO#pEUH<^bc4cbII$e#L?}D-&nmOs)1uESyr@Fh{f6{Z-_Zo)@ zS+TwPiX`7aQONZRBmj}QN2#DZ3`ST%SgZJfC&OB=op~LFjfZ2r0R$^U(^?<0N;b6n zmNymW+3qnpi8%T0p)C=yo$4tbb{#5@lKA$${aP&R+;Wa3)gMhe2i;89Kf}#lX|~oN zl(vPSYM@2#ZC+W-@m1^DgmOP4;pwd@*U_=pIqpwvcy}#kK6?Vk^JF#aCoZVzaczNc zlemILwC@$g+jM^+nmj(uy54@~7toviXTXA-WI1lQK{x5+R#28jCkzY>mD{cU+SQeia`7?q zMLy5AwS#`>vL<+w_R{z_?gSZ;(4_Rflo?b{TIP<3>_0f!}?pTz>TEu}Hks-c5MO_~G5I6_MguiO}c7DD+mK z+k{(i2SP8@SJFQk=3{kfuID|>@$ygbIkO_d911~Aly83;?&#c6IP0_L6JKAU1*T(N6QRX=gsfbd0wQ5P2hIS{fZwRr>3^OzLhh*!Jgh{mo+1e@%ZZn zEo~{0B>OjhQC@rVU*vdI9}2pim&SekHcHMaC#B%a$=m=V z#Cg+oe1dUhb^Jn$NwRAeaCP{u`re7R$=)086F5U9^ z^V91omSW!Oc&Hr-@D$G1%;~N@JB(`lR>#qngdPv_%58Gik#n!{ z=8ZGcAaNGb?oTp^QPv4bbnU&m(YXD$wEqAqBfG4tT}t^q@9o(timph)C3H$ut0eo4 zUEjiOc;z8Vb}E;4a<|mZGYiUat#bS>UM#uLESZfo>I&Rsp0)?5uD{_M`!r9&$h6Y&QCp-b)}9R8p?BV^n$- zciKkXmM@|A)rAS7zSofjPAReKX`Fxr&B(hUMN1$Nrh?jilo&@Wz)GF9$W_Uv<2)79 zju?_3*4MpD-6D|X5Bqv}^q|RwhhlrdX5!l9IOj8VIMlcdgm@@>ZB<;wbCoS3&y1S$ zKFRP-eZ{g^ymmVoF|+Bml(5htAOJ03E5SGPt*&n<^nH8dCETC5>q#ixCA9jQrt3(* zxD!BC&$}fko>zbea6K*1{k5ipfb+bE6~(d7|8xn>m)HW~02HL?{I*4Q=oTrnTfBJlM9oI(BEEd~044j(twQNPmHMHYS=g5a=CDV%g~O|=`Y`s?qisqNVMx020PmqJXOdq^NO z&_WVAQgV}TAskXew&Jvu+%k}Vdcx=MsLI~9 zLJW=PFgyY*Wy<7Hv2fZ*y5E$T$kUwEtg8NZGDkzD%fgj>KBcmNcU1VS@bO{ zs5KoaE9-iyw$b=h9*i~n?e|g^vc{-*v`TUR0G%G{thBO<>RA|stwp`M>(Z(>8FNDD zbf;~zS*|&hg_FcjcO5OFl9wA*k5Fx>u?NW4G0E}NY{M2Toi7`=Wm5dF2i7jk_lG&o zvGU>aIH~!}U`TH$*a=I?SAvt~E6cZdYUs1_b}u$|{s5Px&T=CEQ81wfS`XDT?eBXoB~9o44EU+VZYO*Z%RC%Wes^!FkcKH~Z@EfYw&* z#W6lgelG#u)&VZv@V{cQgluE!eKBxG98d6e_Zxk+v^%p;V zY05_98031F>_v^JP_Sxi1BT=gyHbAYZ=kb7#5_nO8&Z;()pcPHQz2U1+J|W*g`1G8 zkBIxJOQ(bun0YXhQWOBzl$4%JSgwQg%JC-E3)>q|L$+m=9Sv69TE;?9{H4@>%)%@R zuWuTUw8K`%&CVj$Q8gSBZ>a6LYEd>fs_S9bC5$JO6=ltCO|MlK#$C%zc|~^#G^3}D z=~p(xVJUPD#psjcdIY6{#)Xti@n>R^t6cfjM&Dx3RpFK%HTND`6MI|ku4iR*FZE@X zbYB_eFXQm3$G0WK?<3$!h_;^omB8=u(UsX~@A&=6D)EwCfae)3Y^GddLesTit5P>> zWjaCO;UG1Pv(PEnEcYqmY;#%Cf!g*>S;z(P>zBU5X1uP@|yIndih^?M4_tFbP( zA+#+D3Jd5(P^n5U45;0Qizci^bg-?hk_hWoy4N&wcU>PYtmJO@b2<1&l?5wyFH>z& z2=;mPuTA)$g|DwsUFbgy;O5>x-3+ELFB>5iN{g|U7LcNBb*}G_<0_pm9)$+I((=PT zoAFHZoQICU%SvS|JuS4U#?6kTUjG1Xc$XvjCfCrvQ0vfm_sSJ+BwyK8{{VM^xKk(S zH$tH<=piQF>ObGFs8?LRf~aa5Z)5@INy~9J+=H!>40~`&lecM5KNYQdLuYN3yKcma z*S?MNxjZK#o(0R}aXv*H2P4Unq#+KaAlewJ1XwD|vfuZy72hjL4-&{W_F< zMUVGaRV!hRhcPXA{Y1Z(Wd8v8vWM7czUm?NQ~HmX#ATg71Qn={VYlN@mhEsvqop?R z9*M=W>pI5t^uFbgpW8{#myerlIN* zZxac%1TJV*yizU&W7)Il61c2{L|9Cw?8q*!z-j%Ii?&a?AHwnI8-Ts*X#>XBKKest zG@C3ny3=$?9Kyxn~(Q(u)Pkrik9Ry)2LS9MaSD#dh=L3D)!*oVRH0%XuIx) z$C)5gPh~)__UB{#t0OFzC&1w`Qu8?+lQCdINmI|Q#DDRM^!kU%Tx_tKYhN*HXe;eI`4?~;#6MFcY(*NxzD!zGu=wE{hXNm&g<`I?oR5=TJ(5541+fhhsXH}HWN{4rU>mf810rOE167823^QLrI%)|%WwjE zB%Xj(WxI>im(1_vG?~)fJ*u$Cf9UD zG5NkP!tepMy*5(9HMtv!1Fy82srF}N{-xf}6}P*w*RjXl(dNqL8C|UlW8fe3X+9-g zN%z-f$7wpD*VOc$ABv8xSY7=}-YN=|G_b2=`W8^^OYu;geZ)4}HypL_afUf`2F9RIS2&PM>MuE!13VUG|;0?0B7(gi!FY_^SS! z#N}A&xB7UBomV`7okP+4YoMoLdW#Ox;qIdg$XFU`y8*N=-#~n75VB2zpK;<;4XvfA zn8n~(p~%nxD@!=&1aOcAt@qaA-f#u6)i3~bkIE?Q1iOo)#+!&K50EWyvWs7FSq_C+ zSKI>}-q!ZfV+;Vm;VA>{qB!A#lNTF&DW!lfB8eA1&pMntk!)~|fK=e#K*)BKb+E3R zQni73NPL7^!~Xy^TvbvPl!qW`?5heXc>>twC;(gyYT}xjSphk*+harR>0Jdc0@7l` z$Jti;@{zT+1$WE6r-kdBzUPv{y1@@VfuTCrJGruyuvk}92e{y|J!-PcvNs3<-Em%3UfUk72ybGe z){59{;ED1+U5DdMZj!HlfEdV z{3=$VLf&GRjS-iAh1UN7z;~-_+x+J-_dA$3$+HVoGEOyvwmALbKUHqJWBtdk{7-^< zI>}G^!L*D)t<*S^BG#T zoU87>hf+-8_@{>NlS0t1B+`~Q9>&ZN`*ErfHs9s&!UgDkOapSn`_>N4N zf6E)b54Nl%#;hy7;NAp` zHtcV0Pxeth;fk~;^2`x!gV;&-)_d&4Zqz1q)A6G{mQP-$I7Ak&@F?HkQTw5@=1*$=vYZkG@uJEhMckTBS0t{8_}0QZ+eKXts|+FEbLl^lu&vDR2wkzmQl=d9NjUoc*$U!xxi=3N2cp*LO*RrxXL|| zUj9=|eMXAx>kil~DYAbt8k0nJ&*%aC#vXA zzkM_IlWdRrFO{}=l=1zdd^@>4l;(NijmF>@Mos&c9(F1KSpw&%>!oG-?l{UN%J7_~ ztzJ&dufV-se~93jTd2cC3)`SW4U3=rv?y%CwsJJ;?`+Q=a{lrGhVFJQFC{Ji04_{i zN`k#tQh=3i0>F?wD=XWC<%QVm`7eZDW|n-stp2CWud8lTjpjYXlPd|mQ6Zo-6ttA@ zT1nhe4~La{hsElOuc2qFsb;>cYoB%8hueDXDzPHIQgRheH_=_? z$VztgX(sAFc9n|En{mgfzo^1b@UE1N4e4|i!P9RG5}OKHjn~+8)4GeDd~0|F9f0GG zI@@ibPAx<1c?z<5v2W*$UYnLqUp?pjio+9|q1wbHyx zo7uN(XY#IsoUMN3ygQtO-_CItl5&~y*^q}++4Ng+wF7h@`GRZI{wKyxs*`=jb{eLo zTzeyzOR_> zRy!FvJ{4nx|yo=*pSa8BI8?OrjCQOV={ z#zrbb!M)aPKL{Z{(_S~p@|JJg;UCg>9Cnl`hm^a6xv$jUHOpq==5RBe!ZQE_M~um` z0#l#_9;585Za3ljj;G|hvwOV6qOlUe;{O0xebdM(taxplv*}11L~YJ{Rs28hu4|L{ zo|Q7oX%BJx9^%W0&z9gh=X$dRFMd!iC$D>&|+naD4h9*n#<;8Y1#x5OY zz(SH!p|3>YenVZc%zGR+E5XY-DBPY3YD%o#!0L6b!%vrvdb60nhMGh&bQw|9eMA%? zT?rj)q1WVK*K?WL<86!W&;sI?3DBP*SJZ69qMprpA)4M+qiF)0b|FbYjn?&t<<$7m zYPkZ#8G#RkhB zH97D_mToG#>kt?w+0)-n-O9;uu49gHKDS^^S{s=aCihB}w)Q?(uQ>Ui9Zj`mbZGBB zFSk1E@qL;1QxN1#rYZG#bk24YfH7;0VB*-{mFep zD56)5m5sqfRH@P%g%z+*sI{da0jag=AGNai_3AY%YpmxUW%XN$URAy$#Wt}e_d`!+ z9dK<4DzPd1E1;v|c1EXTT?GFCcZZ$}dl%hqHN){tyU6)mRDuC_bc88HpCP!{Eci{S z$@sl?HfScy2<%H$ar#hHM>=&NaUbo;8;N8x+6%Q9Fd>B}xL; zuqx?JY5xEcYu5al$PM3~HylLfzGdIebIY9y+cVo}kHmq5IvZkG8~(DTT#Wy5Jo7NhQz>=O}vRKA{K2 zSJtMqEBXrl=Qrvl{_)7hQe2NM$nFk{Le!EEyi}JDOyA~6gXM0AXMj}RZJ2zlO3A+{ z5wED~*L(i}-X@Ey!ciH1SfMSRYhuNRCINqvyi!7(xfjrmm#cTGqPrCHzE8U&aa;F5a#dTvgeN1w$Cy#R*mxwcwNCG3TEnc&B7S&qZd+M`mh@8x_xWW}h0OlajqBo7l~U^S{PLsp3O zk}Wjul=>m|I#)W4%yH#S%5#w>Lw=h}EA=i^FLFFB)~GoTwsmyqJ*i#Mu> z$HNHYElob4JdYrHY*j{B3In3FCYho*X_1_>92CA?N=vK1x2>zI*LlItZXz94lh1AK`BxBXGpj<>X2)yk3Xp>bdoA;;r4&&BQ0^xOEO2H3j;D+zAU0U%22nM z3DTi2kZM9bW`(1}?7#A{>EPV$7gG3+MVocs5#*1>G$qe!KE7ynt_Np5ga;Vpja@F` zr8YtYdNj zf?zrahw#G19ly5(t>Qf{IEa zEq5B(D*o!NpCwj%j;$*`gyRp#a_=c2IJpZd=mRbr{lcs7yeh--7wA z7eOgJGtPNGj#(KtmQl(X%m*}KzxBL!Y*x@e>PB@SczC9}O@3ouPxT^>&Mw?z1#J9z zK%2)>RGkzEu>^Y!Tt|_o>Qv7{-$XJ}WD2d4XBgQ);*{-+>B_KmT!EFy^S_#}#6ca3`tRk+fEf%MHKE6`}r(+IT=VZXp{?xB6u zMq{rbaSmRVNub%g5%}szcOE>TP1{zU%0|towbY$jmSl2>vbPW65%`A8%SlTPUe2`K=dD)G&kVr;PFt8W-x$xO~}aZD**ik(2vD0KWP zRP5)XE9>~;wdJ#pd;b8=9OO__8E{g{Sx{1BTI$>6O{>#B8Apwo&v1OUir1?PsmHP8 z?2)-74RrFg>)4x4yfe;|#hE(8iFd0jgTl2150ZwG18Drox4hSya=a{MV`opPu$f@- z-t1*FGn!A$?=KJ(%7I9=b6lSgqXyA+uQ=ljw>e)dJV%Smqt*rVjN6}>?Pl;YPuWaFekLyV}^*J5SkVX#? zMFT79Tf122Ul?9-HYdyx>bM--O~3C7>p%GKkDJWmiXE?%*)E9{KO^pcIpr@Y{z=NG zW!9}#>b~I@KFivzwLTr8uD>J^Rd+jdpK>HK^3z}*DjxHH#;q#jB=_hvXz7<(I zi;5=SsB$Eog#;^KDzDg&OklL8ZVa{Xz0Bn}*p0!9mU*a+jSv)cu19l(rd`p0Pbp-3 z6MT1dc|(uKkp5A}6ve$VRpe@>&yP}*Six~eF*!A!$I*Vz{aJAJU}NL?y#D~@SqRAi zFR3b8T}nl+N=Amf{{VC2+Z508eq_#fn&6hi{Of6Cfp@FwuN$3??iYtT;@L{Ba-;ZS z-7JyTta#6lN>4K^xNa)FCFRk{oK=zLSi%An)Q?daHZtAy)@cXNt+)7D}RWibJxcsSjrm!+|Pf5o{ zP*V&zt=^eWKnd2~%A$N#bM!1LaIN`8B-qO%&v^!Ewp+|)p2!HpYYA~=gpQT~(-Gq# z4#rmEyCOX1UR8)ogby1UZED4g`3%P<%C_;_HNcH5*l|{{@TBeIpr2v-=Y`O;xSx1= zPaBW&@MfqqtA!+}kz?Oqc%v@WY;+XC*!Atay@b`)Z)q} zud79C3Md1T=2P^Z+&ud^RjJrN0wlUlp zgfp}TK)R2GgKZJk-&R!HSJbSs%aHvL<0{);wfrwn(Xkfs zkx6XLdJjf1TYrY=Rl0ks-n(+TF=x8HQLuvWK6a-oa%P^?MFeL@>84|D2s@%>Ejlmo zpT?lCk*Q82w4rtYq~4EYpU_>&4-kzt9_kz{X^YB3rqkkn%5pNuyx8q1C$5y(2QlhL zLHE?+Ehalce7^d(sSCSBUTO!8;rE4Hq;j!?hpQafF zH`H+1>!n@SQeb9>jRA9i3bxj#4+64dgnnaJluPgjF2mFWlWS>R1r^7jm6K?C+7oV_ zD(bqIhzUuHbQEYu41v3Um{s99Hn*9oS@CknRbbJ^PNvrO)lT0RJdHxZs@uO^Embzs;vO>x zPIahISLO76(`t2p z((p)}O@O9stPCVFU*KEtohnKiB%fie zy=!LrAd1MxyEXnzka0I4H|2X)tl@SXa&2-|CPQv8EvzfUzdm{>B+KrY}gpbyhX+l<6YBcXj98mLueoRp>5>bUQL(-|i`vXsr{vyCcnhMevx(dzEy5 zLd55j^D?oa-aOfQJLvrA_=yB-(v;V-DtKj2#kIAow-@x1IO8kAc`VtBT5zu|^_z>f zp}do>q*|{d$x&LuXRS|_)lumsg1^sUXW*qO9z~CqeHy8&f{LJa1Ek{Xt3yV7JL4=EuT~7m^qmDd2SJ=S}1vwuYtE4SL#%81)C?k-}EB0Ik}1 zij?M%XvGC%A!@hBYf_%!CXToIc~@8&K?&tGlD2&DtU#%bcMQvSH zdNCPs+bilUb6B2%0NclBT4~u^^#-`jfi-7wol?Ok{^+Rt^|DKgztxe9pDtmR+)^VH zkPVKNS7z*AWL{}}dU9(l`Kk36$_8_mqvhL56w^TtfS{|Abm{J=IR5}H_&vjd(feI> z4^x`MQxv=YWgDHK5^CBV4u%pspXs}T=VkKfJd-H___#PBN^SL|pgQ5{w5r+)rR4YE z-;Pd0<{R-;IH&oJ?By9=Cyyx)Pg9x$w2BmN88pcf#p!+PX#K>QNk3#B_xqRJ9Y zyh$ED+VyTKrCddJW8)tZgP8P(R6^ZR2cmik=pemH*AAd}5(3Tj^Q$OHU{X>;n9M*@ zR3DJEC;_wt-i0{i$UKR;@@+a2+LX3{{KrE{D#tvqJ4Co?2|kG+d8(RqWQ1=^s9Fwe zMo@A(gD(O$EOkj9BJ_>g3QG^L!q{GcseH$gPjBVl@8^?g(rK-lMD6Yb@QkxlnFU-gbQ*jnjpUiFLR#aUQx_Zqb4?b(n zxTcqIq?>KGk4m!FSt%`EFQ_$cOOOuK5IwZy8cbio$qko{aY`an;i2L*KH4;`UVtI3 z*Qkka9~^|R$x_aR)oKMTDM^QV_y)*JZVjt99$hL`$)L8h&^)&r{{Td3?xsA%T!fVK zDA2Z$eZsL?4pG66n~%2J1^nzN*<^4n4`>cjW=)1G}6*PN>Tu*y%|Tby0-HA zox$|U+z!*=VaUei+Jl47N^l6d7h6W>!nE1yD)!dAH|BpG-gddgw-dSJcPf+;T^Zd` z1Qa?Z>}jUesvH&og#lys*Ot0%PY=-OEzgb-DN5tZWm`6_1P_Q1K?m9>(XM$aaVRs6 zJMZ-Eisb7?dlexuSmV%k6vts~b_tKM6tcCj=%mPr{`l;Cr;L>N~c{ zepwvln$@q=vK?c!ovP&xC)I*+lLz^UO3G|cm%5w%{{WDoba_ISaT=EQSPZxHm%t|m z_GF~8r$8y%u(a!~#f1(304HtL(p|)5@QryqhZy=5;Ioq19HwJsTB$oxq5Er4{!_j6 zXceCWqtG)S`hIY!15RTxAb)|XBmP~rf0QTx03X-&2)F$@_!+X7%;%_hgtp$5AO2t1 z{{Spa{{Zpze^Kgxr=MvW{{Y-_jH{$|H)S_JV698}V|q`qH~fFBy0#gG^cUE8yH_Fn zjNmL)zEMr>?xZ$9lQ-tOg|Fc{v42u?j`~0Pdd8G399>gXi&#A+#e?=$eV;krpI;!* z_?D!73*UowgS0$x=9hxt*!Yqg)tAdDabm+wRt-~be3NSKjj{cHDw_Ku`EMH;$iWRZ zlqIC-kOERYgTmEM3v&Me5! zbUDrLYBVF}XLUZIIS(t6^2TCIangPmfz>6lFXSy=!NYtaikD-FqTHNV)X`Vm~!+3py^ z_X{P9$->C*5B!?Kn`vU{HuD##?Qk{9rx>gZYjZP=icRFWxhH;F$zk1#6Byii@)2V4 zMT#$_h3k#uz8__K9&&vSgMZ~Ql|CtTXFNa1B|KE82_HLq1F!%_EB3tt2$EU}`pY9Ffp>fg2E4mV(6tdj12JB#$D zG0+;D$e;5939__3Xi?5^X-SBjsW%(?_12m8o&kCJ4OKJq5UZM>wl5r^le0~gt*=If z4g8fQ zkhD6=MyV;bwMh7g#pM=pN)MrR@owql#Q512r{*Rj;dRB2iAg#gMR0u2$Ks*T>UiE(S?YBI8QD2D+LFS} zo=R13D~p=*dDiN)q1M9RIF)Zb;0xr6GT@Z6DMR7~KMD4mS53wIid8H~uH50&$PO2> zSr(o7%*^H*#z4&iqXL==0`X8au*ZEWzidpOjM+8P?}1Dl9vJS zBE1XZ_iawR+OTo__pu3o+wM{%+?wuXESzlRk!B_st~TgxFNVk^rC(~T;B8B6(&ZO} zblKx9m!30_cBd;5mof^U#B*gJOIt|~#8#bCW4I4xaa$aA{as-4nbgy>sWtifA7;Ho z{`GwI)b%V5(7o5v)~$@-B-yvT+SQ{HW2WBth~3lKO0kqWD;^rsq|XNh`c^Vg?ktxI zQ@+<9yHObO>4CeUrxHmPzfnucG|gliA`=T)3P36QDAidcW&m<9IDLIga$i!x3sFi) z)};3=$96Xur|5f*!+QfY#^CYTQQ*t?rOou)4_tJxvHNPjdil;cZI0_z9QGxXW5ARY zABM}jjs7N(f3m8p_ZRt@Wa*Eh0KLUH%fzhWBWu+9E!>~FnvTu!?IuN0TU~( z0l~&T_8b1eNB!Gl{-$f~Yx)R1!1#)Vh7uyb!{{UXsfhAh7|uw`Uc7cgLEHW!<-LAA z7?1vVq=ARfNH6jgHo&`FZzhM?~7wJ$j~9wGQaBm(M1{nVZFmx%tn zQ6y$FZYe1VzV3rcuD<6LlG>V6eGwrXYa*t@t>Ru=V1Jq#SWn+-=d9%`&OEx8km7E} ze0m7CgPJe?iC~RM*nhNErO2n*hmRXI=y^DaBFE(v0qJxakKX0}Khdo_yutq3 zMqI^eJ;$TO-rqz@q4wV*t*!jJw;Kefp(OP1q;@_>1c!C_D|6Ja<9$7M2)F7Zjmc3^ z+>~wjf^B<{ZCvf&G}ccUFJbXZRC32~`ytKRQod&l^9V;(sWt+ae=ni>oBse$T3K8} zv6&pHad{~R5&-`IOtPK9Y90g*f~dE?Q55=iE_AqjcPs8w$LDc;tc*;-X%S7g0brnd z5D%Sox*H1GyowFzI^-dWQ^{I&u<@p6S5qt5c}3;Jxiq{KmX_0jN%2a$Y2o(QC&}@u z_l9&@d)_BW@z1F&zZqCl@l!fEBmFMok*7^-iroG?h080WE|ip=&>hm&EnjcU4gCRjZI`O=b#)!BFzjc59X*v> za!j(SZH8jcOJD+p5#w%^TQ%f67l5-_G_P*k+scE}=r*kgH8c}#-D);r2d#N}j)b4f zWA3P#DBvj?Z>FGj?mhzbS#7JZ{dDK4G90R6lw zk^vg^_R>-h8DuiVqotK$_v*;wW#rOA+uooB>RtJzt`O3kh|=vJ(h zaTyXMytG?hzBO$PoD}k@Fq1P}1b-BOY1;PXkt=nV&^pAzKpU#xTXtVkuHAm26g*q9 zmYw(L1z1CVar{K(UV93oDH`A{t-jONo7edi7fB#;(VwFpwK@}?1MaJud98ViPgc|@ z7af0;=|qm*GJOa*&388iq1K4)ffl4H+M3qYAH0A$L5iX6xA>?L-oXi1kfc?5+4--1t{%Uqnrp|9}c0^!Ut{F_g8BViN`%;RlTD}*+V@PFX2^uzNK2d6tc-xYmkCI zGw_R51sfi30cFs2=?^-!REuPE8}`+$G~t4C6+%eT+SUHN0m+ZRxdYCv^`q2PvA|8u zynHFj7v=)k;Rq?z_3*Beh-0`3$uNiM6@5UjsfuR?3ZwH1vZK%x#p%Kas|-C_W(^6o zQf<73j7<1a&T%#3HB!%ss@rED=rIV5%c&j}S7#$sL#Q#Mk{Q?Ws;!PI$KLXHu zJjF2o0PQFJ-GAVIHu+?wJCJ@zDkE}=_15kio8Q`NsE#)(- zpX3mQz)!lLe>T;Z^*M^Ti$*6i4M*SMQeP<7V=IlZ2|1I9PKxPSFOX{!j)j74<)R^1 zRg@|!Cw%#MG+V!F7= z&PNMmd_gJ-wY}9-;VAb!-kt-Hv1i4SY+USiVl#8GZ;4Ty)eeDRYISvGUfniRFBz;b z$`fl6DWuqhq&Wcq6Mw>sFb%Vu`}NNe=(;ha@A z$sPg`b}DR>Y>LTd>L8AlXHBFnuPg%d zc3IY~QAuQ9kio|`0s;7qY%W>{szS-Pm%fW!P!~BG84D)eJ++?S=EziNB&`SGSG-z? z+DH_n%n6STcM;`7^T`aebme5W0t>20PeFRxt0vUic^wW>k!2+)lAtYaz7$151wD?K z0K1S=jxEz-r!^+tCZ4VMqPobo`wZTirFCE3VNy@=BaWScPD$xvFGP$0xZ#4|lB?6O zv!NSX&IXH;Rnb~hRMQ12ng_b+9Zs}*aQcf|TTcQDSkmhXro30w7lRuYKo_>OG9rGm zsgnf}C23GMu&gzK$ey5dQ6FtAZ4TPVNLq?Fou<||=zIwFieGAlWo|!Fty|nCncca6 zsO)BIwwyrM0pWAm70*Qh8xI9cX2170QgrY%>Mu$ zSt(+qUTFNLw6iYYZfW%y!*ajOVnt=i5FP#-Va9+9NC#bSZnVc2{Cl-mevDUp<&`#b zE4{>l57s#BV?{2KP?Z2czp}gSFO6FTi8G$r=WCZ#h(w-A5^R?rTtve!d`Hu`v8%fH zrxlLpKR!jt<#Z3=yQP@oi+NbL(=0xl7T-u%N0o)?YyL4!&#ET5xxjF)`*W9_D>EBt z5=l#F0@tZS;M&W%5A`jql<}_r0P@sJVlv$l*0lhXlVC|BttZL+Dy1LFyOun6$lRVJ zc8)K*yj2ilx1Py03nKDCeW0+Qojsj8*OqU5cKW8$U5>k7m99&)AkYq|K>6IRbjBbnzp zNmU%9mNFk%ebP)xIP_&2{xV8Lgfs%c>7}dC>G&R^l9mn{-;`T}k2!BW%<>ts@)>FC zmoiFR2Esp6tey+AjsxYCN5U6MNVi)uVPDX1&} zvkYzXOBd6pUv25!2_E^=`?ZGDnSm{LO1BUyQmr7d+Q^Ht*WvLb^fii~#E-o2H&lle zausi9ooft^SkZD-;;WB3-T=;B1U+@BLPOg<7H8E`g_|b+7CO-Ex*t+d z)|-8U(4k}5FC7j~`V308=`Li~T?0Fsf=?sg7hEO^TE7r3(8ztIYWi zm|t#IFpk$thpl$%Ti?p!#zSmHVJx=TvQUwwhLy?7ZMo%_@TEmyOv3~8>F%ww zlm4T5Ec`;@VPHveJXt7Gr4-#MQn~?fYtcR%r%dOTa(^P&oM7;9jMcdElvHf1l77=( z+pkZlslXJ=>6~6QTEt>6MyCh z&ZF(KW(2UXQ+;SIw~>q^RXm+;KH5&nJd8ytGey3S54cd=m{>!hktmaIA1V@-Mr-P0 z=Y5CpsbH2^*>cFZ>1tHS$W2lO{vB`at;G&XX`wHgSk~3Jyb1X;B3ktw2amF(q|ymo zjnz4(8hTWxlEV?k=?^VIC_+b4N${e&Wpt8?>K0c<5z8xlkJJxnaL$Hha~2+WD5YtR zmmbyf#bt$9Fq>%hl=e{!boj{{Z|@sJ7_;0K(<3_EJ0i z7S6l;(G5o@RlS*IN0!;vrlFAIQ_0Q!!DSTubK`&R?T3I8L%XtZHPAOq1KqX2# z00&cgJRVuM9FfL(zZZT%j-EphIy6#C$re8Ir9B=l-uMAMh%ZtT&HK@S;U$LSB|L^> zz>>o*r(yup;#c-n_aE{5tbKbG*YdE5UE@Bcq~tPe$C1WajgEYTDM8g25q&Rnr9Hdx zSwan_FGn<|EF^RSCATq_B(y(-`a;|3s3P^mT?ZLqmywo6l3m+9-MIECOp#&ps{E7cQoepFSY>V8&hGIR9dG$ec+=i^AD9IRDfqhF zk#cpXIG>Gdsb$H4znZHL2^>wzvk@~H2c)$HsQd1Apa4AU)byb$;f8Wsdk~zmkp?>o z{EwAA8Dy`Na%Ht4yW3m)>RC4!;CkGG2-cNrAtBL+AY@e?=ATNwCX@*KDz@!UDB!uH zXTo_pPmTBHY#2#_IfkY%2HmPce7f|m6La9OoZPbVce)=uopJGNp{&m5$DHbx+el?A z>VSc24L=r@YxQJe-9E93X(graZN#MMrqOCjd<15?u=R57b|i}D@}z)Xd-#bL4gdzU2=Fjn16j^Z z_B3aoPeSauZYynyRepp~AIEEJs>vVTY+Z{Q57gIj@Yy&MqH+f$vl$_|Fx>%3b+qZV zSN3$OUC-m)TcFZdW19Z}D!LO*^+#p!{{X5^+w%@OwQ_eG;<<>)DRL``DsAFL?v*4T z#=O68`15yO-^v;ZGK;J=|;O1uwBx!3qT zl1s=!{{WKL=yQJ{=2H1?PDWcK>RpokfGGd~l;|y9gQLfL6{wt@+ur1|BN{_V=zMxp zFs0;zy9A~b)|0hGR<)^Hs2Z0J%bbN{JnM=HZse=VvhNhK&D7-H z3vZ8cxzC{vyKon6qsFN=xfsP48rrfg95)kjYD;gc;XPO7T(0*wC}$l@9ey+FT-YAo zVU(m7UpGAgx1Dm^oYmcnsx)AhOOZXO;rT3RjVWtYg}p$omC8PP_)DQ=^;D4s$$c~S z>HY4y>&sT8?N}BT^A%xF%XZVk1!oak?ob@FwB4S-FVSZt{Hd2A#sS`x{i3?gJDJ~{ zawC5ks)R6|t?Y(H##ogYrc3q!E!6?M_&# z0D7eL@z$wd&Ur7ZEMLZSy$~BY*{;a(W&_i4A@>~r04c5NQ9q4^SZI8clC}3@m+-of zS;8v`m@e41vbcOvpG0_)Nvry|x8M1NzTa2YC|Rxz+xg7;@c8o~1M`cU->*ti&U1dJ zhaz+1HDBpSi1H3A#Bu4}8w`(9+Be~Pu;aO1t2@X`Yf_}UBC(vrc3miA2G-xwYo?{n zORS2EToddu-ukr|#!>$OgIZ;`^6uexnGY^UeA;4c@51`NmE;T z_!$g#A4nriu%s8NR){}Vd9yOqwPAN(R9e?FWN%a`;55+DJEPmP@mSvhyWTGe}4{cV; zV}1;h!%+)J2_Sy;C+{%PSa>ZQ49A%^O3DX;x$9N7$I10Ibv|pUXJYb_8Co9Sxk<4e zG%qC-b9>qllEHZ6`_q-h;GM^oL((?wk06eo`m>{!lwkAJ^8L@_O?&p0nZor&UxVeD zJZ5a=((0U$^7lMRKON1@={j$hmVHj=l;9}8TiV7FM`>{GWsKyI8Y*~4o&fbDsOnF$ zyDd$nBz)8IKaJm|bLp6UNvZV_ZEh;fO`OWZ2(d1}dn(GC`U%^v7mbp!vb7Y(wV|=vP~#20B&b*;|P76kB!v;rUJf0EoI? z1GZ2Qp>uyTdVh@3_T-jwFJ!WU>1bRL`)j?9qHm#G=Ec1qZCXQ6hqx>SJxnmcKBK17 zv}@y6)KY-Siy^Rfs`fPHTI;wUbD+2c=qk%zw~!KJzX4a3cLpg|2@4A(SOHdWSVqj$ zvSI4khpSA%2P&U%oJKCWU^MfpO^e22x{Ma8<@a_+qJfXCRz4Q-t`mD^Nj5uFRzXfI z?zQeKliJ*tdym3B1yF))H+AVrE1UHPDI9I}wZVJA&wE&S?`vR;zsvSIy>&ozKWtJM3KR^59w)$x+{+Rk&(`uF`X zG2GV&54pT|kZ?S&9>o(1TujbF8}QkA^>1xWG#^Szh)tA8uBVK0o>m%1&Hn(*TY8pR zT&kLr>)862KWkbLJ{m2JDPZz_T*wxwfD7Kjocn?3>t&1=H5d3-)2%BlmNgw_4SOuw z3HbG(J;pm<9@!t8$dH7ej2~b$wcLxrZ>__&$c(O9rV5mW0(=GPqFj`dIvPGcrQEHv zIb~#|Ez&hTYBJh$mQK@+7`fMbIE6ADoYQrr0neX+EZ$9l%D~o z?4{>pA*;pJ31kB`ojT=@+p=ud9 z2(IAkdSW{`>=)cX*D-0mys962f@=duA+(hpKQCI;HkrHv9SxwU^d45G)s6oEVYtaA z29W*2t<%b^DsQ7BY_fn4x}_7T8dLD>E#uQ5c@L~y6&q1w+{C{q5<lSfU_VO zO1SolVg%U0t8azrnx%pgj!g@lej05G_+f?cn1}My8II5ZI;$0*3R8b+)dF5U3pbKG zKaXPB!NI~&Pf$UBSZb~wJ zN1~vqD5+a-n!wKy#zx(R33g`#_gA}fd0rXZ47jKXE(bnqUCAl%7pD0BI}*-MPt)P+ z)|ZqHT6x2}t?Rwj?ndVPhRa@{Uf*b|4D|#)$7b?4IZV#S%3Mh-s4175QA(1abktU@ zq87sp9f+KA4)XKvTLwNeJ)d?|82uR=P!(>QwgG>(kksR8spTO^)m3@%icOG3%W>cd zJH7RF1%PvT2R`~m^tYYq#Kh7g@|FsT9Y;f3i~16I$P!QG-D?rjnuW5!DOQ*jU=N#$q(?l4K`|EGUwFzMm za6?yDdyCxo)T3@>0@}ukR^2}ua9m*Xn)TDm&a)t_?lN(AKHmZ5M~w0YP;6-8x{uXF z_pn3T86_lBHYYBGnHASu%&0bnAaR(~*t^ zvTOWf=dEnUn6A>_=-f#2t(tLS(c~$W1tj`w7uv57eU%*f8m}=Nw>i*BQp<~1M+qjQ zWie~23mfcRnJ$6Ixg#-=5+-U)cxK^n(CdM<1J>!is*2qWeCNlhaaPr>V=S_JeaqhB z*;b}Hg-C+v+cq*)VxWIlyj2%1#YWh4T}uru*B<9vV|m9A$b@{ZZKSPWA4$WpNhDsz z^}y_Kw^sWdO*`?DDh}~>>ml5AM|KY(VYVTphGXvqH&E1@8ol5-Yc)Hfvy;${#Cx2v zkosTc_a;78E2vG*w+CA9>iO(_VsZ9-J^C^$GwC;;ND6g{@YZelN|XC2HY>1gEG^}({6)Pe`Luj={iW!D8op9%4O#uEOW zIiqVrm@LUkw7OPwKXpc5KB+#a+W1W0V`P0c^QkJb5YuFMmFf5jkNWc0IOfP?Vk$9$x^1?!rxd9bz!9qGn?z=0XcM^%wv)q6V}( zetmtNA~7F^(OSzRQ+S7NFxiIVN8^l1jLO1RhXGdNI^3%C{5eg#>+aJRdOFojEOa{y zw|%(mrf(G7)p49;b)*sywp-jRI(${;{J!rgU6}H0?R2%YoO;C^_utMV+pIoGl;E*& zlO9+%jMGR3JA6&Lb*|^)yIayQJZ~~ z!DZc3F|%>-%l$4%Q3zMC8l1jUb!)avkH_ert_Y56w6X9G6up&=nXw={9d`ixZEAl{ zown861xDu$O>dzVUloAaJD3X7_p0u#`J__5J^=OLV1xevw@Du3Os&}vdT|?nB!rH! z#=gqm-bjbq#Cp~T8FZ=GiCFTb?cSaS;jIGF;vKK!oRGY=ONWQ_04r3LZ3?-yfFR!0 z%krGg#5)(7oOZm!SuO*|_|%j~X}Eb;2;J+p65>Gj+JQ$SeX;A2x#-bfVeSc@IL1+^ z*&KX!5` z@ZL#saP}WX+^(kO=1$sWN>qR^v?)Y^?gqMgbm0!jq`z`cra?gKr7;Q)XoQDTpW&aY8UH~F`Nf#%#( zPNdxqB}5Q?gj%xAST)sT3~p$-cyH7scr$CQ!Qp|{rpTS}Ko%7hFG}E$U@ATw> zy2j-pL0(=o{=Utca#HjYl^*(Q2O?)an7f?bLG~bG*_gr!Eyzo$NKhwBUcD5gz`0+hKILL&MvWzgB_Z{MdhEGyf>aZ8xC(kd$y#%U zP`v&Jw4M*Cf_1C`z3am)Ngkiz@`ckdl~~*gtoHhiSstUrN_DYi1tR?~<42ruh^%@J zlRbADl02z$V#0c^ENxoJA0D|Krc%|?oWY$11cxpzZRc}g_|nR$x6ur?uycobyTw0` zO^wI6zC{elN=un3trV?jCq$qfedfEa1LJDQ`B;u`kmS{G5h%EQ>Fxs#1t*cd>V@uI zAzz57rH0)5 zoUyKE{tITc)fZKARLx~FOz3>H9oh;N(O`uSyj3*loL^KmTa3Dl3*>55w0P@GLYYbS z3FN(4DsL>~n9Dbj&&7)J&5+WaPU25X3U%=pxaBT_zjX%jkZyaJ!{e|HPcHHxV+u-} zd1@P$0JzvXo7+m)8O2+5o+$y_+J#ZeXV3obch5J@B|nMAKBzolHwcP?q%J`4Q=i>l z+wt#;)2|;UzcJ^$w>>*0i1sKnC&xj32(G#;=I0!RAWV82=zcSbWz=ei5=bDAA}AK? z!rqcuC$F(Mc4gVeyZhYC11OIVJg1p#SqOPPt$^wItC`vOex&EvzSsE`(OAfSZ`22L zvhFtPyqVa`O{|FL(oOvAn#J*5SVtHSKayJ|+_tz!)h92*rKs8F2PI%wZ&&c%9)A$f z*C)yRGPc`?LeoR!a#F<6)foQpVR=3YPO$~(rfX|L;y3{ysW$38^~6t%_Acwx>!G#o zL!rsJyar*ZZl)YJlzo!E;r<5$gJuwg;Iv$gz#eXoNocN%Vq&wY2;ylW$5@ zPpM(AH8ex!aonTNI_tCAWiKD|6tvt6_};9cx|DE`J1W6dcSn%naGW+)39djSWK^QR zm285g5_P@2D*J=dvddJl(5~e6VO%h9PD)Idl(iL=6%timvsHMB=a*XLA{jNuLXO~6 zDY!)^)CW>L`qoJRb?R$wT|vqXv8C2L{{R|xGf0LQm~}enX>}xn#zU668jVv4DP(o( z9=7~w#Bwgwg{x^d1IChk7Ldu#PRyKny%vHvwl(K;J>@b^i)#hw`Y9DSF?-<}uJ_qqNxkCYK*zz_r6>rUY zoMviQF)x~P%>Cww;tg2BQS$DgH{NvoqV}rmC`0w%CWDak4+51H+Wtn=DeK1r72eY( zP;*OAPsUB@M_L(M2$#ml5`@`xTYXs{s%;>v6V##rKKe#fv}syTINFPn+AB5y3w!TX z`#p<&PUZKF&bZFdqq}GUHqyDBtyLypQD!=mD4ey_BqX18^&+ZnN{s+j5AG4M7L7;TaG1a^#Rb|+j{jaR|N%wnc9rJfsMFg ztr7y%9YF*W=95#h5R$_OQj}$ozC*avocyb@kSwPGHz(~iUta?Z=L7GOmM}tce2#um zNnE$f${Gukr-eVO#m274Lt}DM=*A4X^O_Z#DBG`FQ*w$(RW`}KDu2l`kd;^gufC{u z>x|xCa?+W&pgiQu%}Vs6G+upovnUrQ#-&W7Uvn%@WH&F3n~QiNbagPJauT6y+sd!> z_f(Nnbx#14Ewz(vFVgj4801|nFqBE3Dzc!^S@8s18`5_{mNmGhN+4wMa!Om92JM@L ztXL6xsn}DC=*J-_Fm6?d0#g3~mZ3r|hTYw7nL7`=C9x zY8q(&0N7~0=^E)~=xqUAFXc-+Lwh6k&Aq|Q_H3%_uD5t|pT2km5xU_BRulJ23 z?y=u!a@2(j{gsxRsWIzM5Sv*0Xbqy$ypNJxU9}`q^O)j@^dZk8eYdq&O4ZnI5C}5M zu90nRZ3R=yW-a$DBUVWaOmw$4mYOT@Hc2!Y?!hjsBq}5^uvteG0%ILIiyQtmQ7&Gr z;~`_H$t+GeJ3v`C^0Dhezm$UP!N#`|dxzk;c`YGminWEFpkBb$P5k5&=n`%#yTLSz z!Dq4pU5vZw`cL;#PnU}c_ceV{CdY6|j*)olzn0Velr_l3`jMY2MQf`>_Y2!x>+Yep zqm`$qB_vkTHCJXy7urm!(V+yV zpy~T)uC|I;{{SG+m^5~)I)#Dh4GxOEZaG7*=en(T4#VL&{3sF-vV|ap0+X(u>TWj5 zG09c2@_kL0;?9rb5~fYXvgEKs$rd;2ZR1@(9e24Lo-xhQU01ap#EWa5Rb^eJMq)pJ z({4Bfhl@!4;iH}UsX)e*`fIq6K&g`aVD zx|+421CX^b7`%`-K&z^e362F?RtcG+yclasn>|``Mh)B`^fg_x`bopMJl7At-bau% z<)sNb&e7nbTz@CE8sgmgA=={G=u~|Bx;$IA*thdMnHh9 z_)qEe~ zf4gtSjN(1p`X$`rcRsMYufqA~Y52c8Hh$$4KwDSeLKnC4&8rK zSx?|NE*@7VdLh>J9Z0=eR!fZA;%P}m0#IQ`>nF-m_;Fy z(syV+9~vXHYA>MQZlyg>l_xB=8z+K7ui#H2nqt!wR3qc z>Tdv-h`yaXsIsw-)PZg)+qKrAMCIx~2!fw<*eS*Hht5Ye{6=0LpXQ zR@HU~H--dt-F2tyo)I_!BrkEWHulkDJb}Bo8RMazL*r8S~RuoQ;e_-HHw_}I#Vw-{4$aV@##a_uiTCe^&}jdkVMNR zvdcSGkUeD+saK7u4Q@NZ{1- z=^NHq%}YJbq562n%#oXwES=0lP4)1&@UC~3OC?7$B&$12%1|H=%xZ=+OZuE|u#M(h zAqqrCh1-yVg$}(%8aI7G(;sfdCFp$p?w<;i$8wLh0bF-3rXD(lB%d**>#5_fi&#Aq zUijNmXrQMhLEC;oU2dmwIj*4Euo7dIqDp*UZ=}fo0IIDA*-I-h$XPR0H3^bSjuogM z(@eX8BxTtxpb;Ed2f+2C`-Wf&`=;a*sXEmC!E6jw`IHYzcDdkDR`L&Zy~$R#qgKtd z2YaFSQP*-3>ukwVHgoceQ!ucKuLDOD)czGZ<_^jb(ako~Gq(~DRAGr&c+tJ!ai<~* zC{eK%BgUui3CgFF)^7S*{`!x)XS6v<*c1+)AbHl+nP#P45uN^G!_JA2VnC?){@P+P z$i5k$ly%R@l1ck(PSg|Fkf6oNBjU4T#G2i9uIB#Lv|zz_8!gl%d+Jfxz@)VsW%VyrHHbG$*Ix7AKv+l*#s>v_Faj4nWWJwvQ*ux)RmU(+r<-d0IU$M#A z3ow^L)*5xn0L-H}%yCd}ASFCBETkI`56w>1v*d(sp&~G+#)-D1{uN!j-bBC*QbQZniA2VCS-l021Ndt%)|j+elrdUOE<3ZcEDy zzi{h_jC&F#gueE`rKs52;`P98Q#WP~p1Y$MlqYigoZdEhg^t>n8XpqC2HNz~O6@pb zi@TwgbDXz4sbp22_(N7bac9ITl%(wnNV-&dT+tTmA=B zq8qt32U_W=;o61@3;8tVl3Ro2NC{L~D+y0l?_sZ%Q*ZDy#!+FZ%BIOO`EEBE8E-zY z>d3lMf(`1fjuPp~78deQf-Ag-b0oMg`efwJ>-R2eb0lIT zEhr6sXD)YY3GlAtf#SVc%yQh4iors0NOAc;A!A55;adycA zUoMr-aU2uWn|aPl-TqN*R+c*z7*^~1E2=r8aPN=bLWgr2EPm2r@HxKZaQsg!$RiiW z=S^ujJcEl%ZMN9>z9lHw0zen8cjptfIuiE(09Foz<0`kaA3n#?U#gC2*epBU`glnCdevvy)CCo@c#gUaJ;;(bL$nH^*t-({FaKZ9UnvGviQPV^-NNdJd!#t*6_&QQY+Zml3>14r(&p~?WzSkf!4>yto1iN z%rsYQQ7Alj177#5A8@R|+WyMdxEtIsO0==C9s;xV7_BlLQ)DeYQ*Ccc?IkEQ(0zT0 zMp;L4@Q(3t&MDgH(-R@NJgOr_58_GK^Q#KA?r)*59p3oP#g{dY%6cBBH3(Lps2TqN zt1zhQ5xCCDBE>2%e+&;JVR|KB$FY6Pe=lkDEKb_@>JL1~e<_uYRy!uNsHfuG+kqnb z9V~s7&-1?zdaEn3f5UUo*zb&%=*#SRZCImeJCr?@#64rB_wYHJIS;axG7_hW7dl)I zFimaMD0fEI!k8vzMjjdyxjeL_WAW^QT#({|LAQc@&$_yeUOL^0epFE1vkF#O&z!sJ z4W`M7!r<>HBsrLqs6Y1`AGFt?@ZSwl>t|DzKSun(cVMgF(JZDZbORgP?iYyW z^(7%%k~K9|vbfLxDkk!v`m#MzzqmR#+I%Nb8hL`gpSioQnLb}MBk77}Zi@@5+h zL*z3UV18CpCE$^Mwi~E1<7l6FqEg?w9i$-dEaeWAhj|Rwlc*?Emvsbt>mkHz!RN&g zd-u!K4ylt;vijdrmwWC~l%hVJJi;3~51+Tya<^%|3t!bPvgyJg7ot8K-x@+~V;36vKx90Gp9EQZ2n|U6FCFfLge{zF%hN`E!G)r;Mf;&!KT+ z&J{>XVY zPdZEPKN4$QmA7`Xs^hp=(I9B{OsOuqE(#Dj+uD5VsNi+gvg%WFGnGrB$ucs}DN5_S zj}voU)jH8gB*`f?s%5)#qYbVR8s@<+?HLT>b)#04|2~1z7tlZlvOJ+KcM3vnB0;%lz ztx@h=Rq+`$lt>!-Rms1xZe2(XXBWcq|r^opPZvN;+eHa{}f zivT*B)W48Z=@rzP_(D&s=rCW?ZxxKSHs^2~d<{1tle8aw>u=^eGU}pDd{0^{th$6^ z{VU+|r_;uF9xHMcZH>Ox&`I#U=u`Rj&3$JMhU4&xC)!;`89$@`d5%!@GBENDDEtqh zWF(Ka^_71n>-KSJk(cq+(d{B*hWZ=k*-UCCwaqax=ByHoQ@*MeY%gv90F+Xb{!iCa z$BP-K_~yqP`CzNEeS_wh3DPmskr>H}y^GlTRGa$`jdZ&I04}b``CzKE;!>_y&PCbm zk2rj`n9v%Oqo_-7AxH5dp)Zx|$38)0@okwbA$L0^+=%H@hl)yAM^cn->Hh$B^n`w0 zr29-_Ul84o`9Ux69!&!2orsYQWB1xn_t=q5N95YFUzS+`FOBR+x?&9QzDoO{F>vx9 z-T6vcaNPUtTC?PuYonU}@-=*Ga!;aUmmk_L;bdG(aX6&69be^7q@d z^P<;N;XBqp(oLcLFZY^aJ!Ek=TWw@*-lQhr`^{EM`7@4P6pKp#0FJluoD8zPfbJI_ zQC4RXU^Ji1>XZsPUqTH=A3f8z^s$RR3%6fZx&|;kkmNZQQzb<$vY-f2>toQ`gR}E` zlzJwNtKpM zgk$*Bq{lgSX1bogE=frDnr^N}cJ*rpqsCWXsOm4vwxl+cmp6W^*6x&&>P`?zmi`Bb zVvJTMWhI2R=meoEx|3WlC(7QW{84wOr3rU09y!2xyV0AM5uq`MFSXSl`zw{&=C&o# z^g0T(D_x5U7Bd}&Y^NT=S>4va0&VA27GF7cV`?dO6yGKCq=$$}{{W8~6lT-NWgL)M z9j3rdlH{oly0;xsQqJ!ZZ{t_B{DyVI2uga9lgQd%WjLJ1i(zjtvHt*3DhAi6Rq|Vu zjY~>6Aya;u;5rmxawVW7-3>a*Rb$sulUDV7x@)=%P;X|~p5f>eF#RjRjp<=sCj9jlIt?oSu==f<6A zrHtH`mU<|Jk?yJw^*xQ}120lIHO6)dUmcd5rWY>Mm)Qyp`pxU8w^2;n&$%RSy;O?d zmv%>;@=0F2@f|x#f_5mMaTcphFOyeXMXb{WAqpZUyP8pr(52SSB(t13QeOX$Vt z!?A-sl;_)mP7S~h>LoPfzFDvJ1N%IQ&I{@1luKHFm?;ni{vd9m2ia7c-!`QzAj zW9YrI%Sc)oLyh`|;X3%xF8NJq8eBAyF^ly5&1D3s4K@P05^A2`n$m`&#Ueoy>9d%q zr`7aP*Hd~+{a;sn1^YNJQbQlP9$jQxbDL5$`>9!Rm*=^!D{6VRU8n!g)?NFEO}sqBhA@tmps=K~edSHu*ONcD+tIw2R#+=`l8z=G( zGMa13i7MoiWxlT}vPy0D8DTl|P#N@HY`T(3 zO}s059M6YOL7q?Ia=KYh1)qn`L(Q@3$0Z9IIN9nAb(*{Ix3R0^*I5t#;eoX_aBAM0U_18Y-3;yx3Wgbf+qp)I!D) zX7^9Ntu^NIC16RjrCieTgys}zB%OZBv)7MN3p0SsbOk65g1ep%hj+5N>0t+1W;#*^ z@Kiqf^t~Q0LJOuFMNI6TMsxD5Moz|v-_p4+l>Fuz-;$hUXIDZ~d0-av4;KM0jSix` z#-Ew)yOZ)QWyePyiG&&L%G=oKvtRqH94)AQGMJi;oedc}FMwmHdCb zI@Pwc<0U1O)5>{W&rsk$j=o>V&Q?)WUSi|Xr_ro3>qpgBV;d-C-gOQr78h|v0C*3~ z1$XcHW8)V;`&$uz+Ho`f2Ki1Lmb{Oybf)MZ*lv1 z+Gt(K?N@afJfQontZ!O^bx<9AE6#Y|l5iF5!q2={ zI{PfI_a55y{ztpV*{HN7Qn#y@^nHTG;B6o9h}3=h)c}8>*#U5ia!h1CusuJ)${)VCDqp6o3;L=x_?8R z{yq2~C&uWldlGFo+5KKmpub9gP%{3WKAkwXX}N>(d9O0Ty)dS99noW$uRK=W{csNC zgJOJZfY|vijnjCOz|SxIMYFfL?>B{x$6fI6s{YvIm{8^1hAsmnlzNUdrwL?VU7=?} zMLEaYMbGlGgO~m+R;+m}e_R%od=r|MtZDY~;C}E znlyso+WgXGtUr%=NYtX@lm5T@1UOgMhtr33IoBTT*8P^o#5XAZ!p#qajhmdgY6f}Tc-{7o7XSPu{@?cV17X_{pm1W5wqGHq}zYO;&^ z0r2VxeT42LpR&~oZv4S#*c9wUs{RJ7<9<>?(vCrI#wiB+6aN4nH3@Avyc%^NQ_#r- zdkM8W9fy!AAZK8$L=>jy!8W}$9j_kdx`dI$GViwA$%dwzS@;&`zz^S3eBEoNNVm`{ z8@9ZHNVdguqQku(*}zy@LxkJ>(H>c-4X)BgaexjTHk=+gNWE4u?eN|-*R zq9@b;05XUnkkNTVqKUAht1^MOrPyxt*HQ++ z8@KM!BTC0{XJbI;B|f{Atdm4ltO2swU>@UylBH@-OVd8wP#0vFE$I#&3XQGO^yPK0 z5j$rrja4npcBtBg;POSrnnCZznq{7@ie@p_PpBrDs zsy3;UO-H!x5{m9y&g6S++MU)8H5BcbPbo2sXw#I!001RDcKFqXzsKlv%Uw#%o?hh~BBP&`n^Xi?R)nF#M$uqSv?WbzvKV&F_XTeR?A`wW9cbAT@oj|ha-5ASGwmmO(7S~H z0A(k<(}T%jtfd(&7o!}A?OIMv+MSkW^!kee#j1X!4xXJTL>wPb! zuTs}_S0=4B`Pl2n2u|)J-k(t%k7hDFJ_Fg`=4gFPAeFUkzYkqK)nlj5Lt=g{WF4Fv zra4UJK1Y>%BKw7IsMwvLS9zsGRvJeUzjMn?mGq^kUh2QHuV9lo%Uc4t?TyK`HB>qZ z$>oile^IaDMweqF+-_eZTc5sx6vDh3mksVk?^{uMF|?4`)m*PjTGe79VjI@eV5XK0 zfkCoWp{V=BJ*H^1AlqBoiwNX}l?ZJ|@BS4pa1XRLQ(+*H`zUz~gzIL_lD)kvY2Xcr z!Cs&!-^kNamQ7T6EF|cC^#gN{(7|+g5Nbkljt#wFe^te4hgmlZ^Vi#2!Hu#-0V~5Y z{!wm*R9h*Eoz_y6KsNvz=}2r=?CFBSt2|ap@%Q~zy5ou~TFELWsU+%6Kd)DUNUXAc zSz~Wg{50l3U`UWU)`+;|O-ck2uC*i6@|oEDTfF%^&l{gIOf<>K zW0)dX0>x_EZPpdz8~*?nhQ8J_(DeOIW>;(eB_SiZJdxKuWMp1xWZa}Ef#=iNUG-cR zyNZ4&R(bo_=@VX7EvS(k$H7@wqJXp#r6D~oPg6knZ8f?)mr>7= zT|ajP7TlEU@fz(TEF9Z;781gBO}_d9S?*+B2Lm%d<%A^7bumHQV{cGzaq-)GU+kcL zjcxfg)O+si#xiW)Gbbt|xdx)G_)RDY5ZAn|y z6sYJbhRCule|;0($qMVk7DH+TY16<^ENnlD2(`H&ZCM7Fj1;?#SJSX+HcD%D9l?@$HMMXFcTj7b(O@dNx}O?m6h{DRC|*Pk1A(cbZ=y*SO*l{l7Dfgmf;`yL8}<@)F<4n{igTm4gUadXPo?)DTmqe&wuu`Pu_Ju z>r4Ac{?D_Y(#@B``Er0#ION@@^8Wy)cdy!-U;RO={{UJgE)Q@1kd$r-%T>1687WtQ zQ)C~;p!v!l`H`i8BXLQDpA|B+0dM=(r)>Fdgn!P2Bk?`V^@x5i)3QVVRoVLnd zT^KdIi>N;XyFJ<(JhdSEtb>lsmuG_F;62Mm4KA_BJxq={AtkoL?CMB1(5m&ubN>Jv zbC;EU4vUHBy|sywo>|{{Sx8^XxV|-9ei;^aPpTI3(mkTqr1-ChC_g(ov8~^dIV?w^ zNJz@clJR-h)biJ=)}<0jKGR)_)Qn?=*r9upl)HpsfGRNJKr@F7GUJ=BG|5t4Zv zr;)NRDV-O>IjrFwA2REYD@3-7l1V>paGZ}BR3!VI#~;X9G>)v{JW3xd`FY%;DqVyP z<3e7f_>I-&y!S5Z{E*4raoW(98^P3(FuYnkM6}i#BzD^XBqf%`fYYt7UVm=w)0SIj zPX$`^KpA+99C<#pc<60+_*4KE`=~zcpH?yNuS67x8A@(a2-3pd6i)`agjOS&KC5`P z8xqZ6E;dvuiA(wG&331@bj+P|Ih#FqC@s1)+2Sb3>A7VoV2?;yCQ$kmTLxWwxa%DIajBV|=67+;)6H?RMXcq}@;h zo?DIj)L$iP>5?yKeAfWuOf4=mjY1ZZ4e2TF*(42ZGGz)IEGJ@6038LWb=KcjEq(2U zgKdtf6ki4)=FJOfIsgRPoyps7|h;sy0~r z0JA)nK0X_+Hz6lzJ5mm}>1rK1&B(CR;+& zU0GVxZb>J`fjb{| z8*AZ2j(mluAj1*Ku>#-E76^~tcVX!JbZwLuLIXTppqBN$Ff6ZfaT)!{!{!-|=>P|5%wD`)C%$`9lWio6X z!E3*^^+l=6^3=;KC54(=^62m^nRA{|T9B>BsIjR_V|vMD#jtYMsw<#Hq{>q7=%`-a z2Ca1d;mhe-=yM#8i|kJjZl8U7tv{t4w3kuS zT@Svls(ymjj+;6w-&XZikPQlTBATWTBu908mp})Btwvce^-RN6Cv~;8sCGvt4fW|% zve)Vy*42&~ST;9u8&2r7g?*J-wZc(j{P0gdR2n-nk6=u$GlY|@jB8IH@u4dHwajuK z0phNIo~Xa<9H*Sxt$tax{TU3+c6Ya&fwec1;xHdV$HYulcWdnl2DqME_$TAracasS z^Y|mB6XzTn$ zgq06{c-Is0hw+PDJkFQ9QvU!tkC#ux>^Z;W-b0<5yGLo)XZ;Q1`-y8GPQOH5(`G|1 zcKeTIU0Oi^gR@S|1;+#M6XD9jfqg-uXnDKf400WWa{{WGGf5$9N+dMwK z*Z1{5EFWKgPkc9peOLF(wLPnGp5ON08H&cri-bn!V+j%>haPFaRYZcaMu6$%UM=#T zLtBm9pW81u#!uJNrVp_H0E>BTo^J z;(j%L#ND%wGw8b`-EIDDw~p0Xy3NbS{L<>*;$3`S=tt>;g}5KV_IncCl#6`R(UT>^ z?Ir!%)!e={2tJWGov-EoYWJC1%LsM{v$)h1MeVK~f(PbKIw(K>PO5L<67&*}&iP9p zTV?&{J@;^)7KOP7JdVGOCeY<_BeIdH_E2Q9T+tu{=^)*KPL*X{f`GRhTbDQZg%{lk?Sod)J_Hd_uIAY<1TuA! zp{TL&sY|Sl%NQ9dIq^@rtsy0WcrK%uFS(7B+_sxZb-(h|q=kS_jg1Qb0L>FFieU-a zY56sz&UH?!^xNSA-oZkvS`q3Bn$jCaQp15;*YNZ=J!q~m2`zPlue)nGGC0dFOn5ru zS4iA%U=OyrjjG;HF8LKQSK)e(V{-H_E~rHbOl{T<-q*IBDmCgX<*lGpR~C3#6q z`c0{&f>t$1wMul!6go6Z$>gg!r!26Q-yGo;fzDrp^N*75SbfvxZD9D}LGPJ3;a#M zK8S`f*sHR5#jt%#g(T>&eRP^v;Q>!1Y34v(k<|E8@s>p$VA%-j2ac3z-H=vP4si#j zm+`5>YXl^^4XkLMXcvPJbJWxRRMr6)DZjqCa z)nYtoit&G8jTao7YtzD;`+;|^(8TODjJD_?TLx<+m{a!tOmf^u2{Exy@0p9L`t-w5)HUCk*xryH2*3T;dN$WkT(jR@w<3&`rfs zOXvD4qL9RRd!L~c$#9I`3P;N3an3+_KcQ(l*xN(U)#i^oPQ-H-E3wDUzUUs93uxFQ zK~~D_l~b#ri?Lh;++P0xg)>#iR#Y@IaxwAJo>quiP{~dH@_p5i%k>wO8XG$l{6u>x z%Iru5m9YbkdC&6ND<`F>)@~v()EK+R=PgHZA zIY*QeGKA`O4tV=jKKeV4%ow)9gdg1mz z1KWO66DGCf(KnamQwBCYrDIBxyEK;+_SFz9N=I7g`91^L)A#(=U#GA9zv5AGeB3rS zeT5f=-THsb?{#6ZN%&NwV00tKxIezI?H+|AcQGxZ>yE3U{YkwwR?}j4=tXug+-C)o z(%p2YBVuA|i6%X6$kTAYiLJ?Hn41?$+_)(_R5jB|*NV;tQ)cf^b?=4e3e{_FJ!q9E zq%C538d&+$IT7(li~KCI%oK55(0bUC&BbJE@P8EHUtuzh4U?~h7B@#d2slgC87+a;8q*%^1>G}VgIXY? zLQZ&A8W4066ZEPSeYMzF+-pW&=bi!~1qhfcL9cN8}D2bE}R$k53E{!?1L z8IuwuDovEKR#Gf3PfFZZQ%|ZHZB4%|{j|?PNJZdkeV=7(BKT&c8{H|_z}}+YL7_4U z01Xd`sQZRACF!rG?S0jr@xQiao{OjSnwPqWIwWqGZY+<6-ujEQm#{cYCjr!6mUfbg z>U38CVJr0e%p$H%mH>s~W?MSm^1sV=Dm`_apz^W3i;&O?k_b+`eE=t#H@ zp~7vamFj;J)OuAoNs#9+TUIGW6oG$fDEy>;WS+Ixd8gTw60N1x3ZOjg$`jYdji1UU zp_V#CDFtZ-Bm?8+OUlso!6($cc>e$?;u_GP0rse zomRf(D!gMLDf4)|hSnot)|ZI77ja?lqw8zV9oZXggqK8!i;=^nMJDaIl?? z#h#zUa#M^}QE$uc+&HW$PGjVHh!#Rpkf2p>G2vY0d;?89ofP?G;!s&<<#!*Du-;gz z3@4NlgzX?9H@&W*)YoNPo~0j^^*PB+Up}j$b4Xta?KT0n@z5H6OXK)@CC435>ZKI} z)7f10QQ6wF^GT#xzhbELNQ!a(Q_1;+)uua4u^B(+H|ZrHB%Zx5Tjcov0A+W18PVES zKHUhvb@^srh6W)+mQf;NN#2A7029{QX>(S88RMgLzwR*~k!{YX<_P5(+}n`Nc}sIB z=|0OOdHGkT)x~Q@I9*RMmoK#`bxj&Y<(ZZhxpSRT#;Cf7U$&T96a)RoXV)a>72SSI z4U{+Y0njc8QUZ>?+Ni&W*Oa@KwDR{Q*ZskGox|iA7lKRbZAv;Nv;m>_RX)!RO6<$+ zZcfR6l(ZVmavY3C*=d&)f}0ntpa{2(Qc9{TmsVTF-#C6$Nc(+>XCFG?tZhL3UQ}Gl zSio;*9avs82=Luy>DdOFj!5+`vAV{GWlhY~B9u;d=K0Rz#pM~3h|W0v5r=JvQ%Fcp zqhO(X4NY`fn!2t(nRPksx$JphsLe0H~s5xE|x?`BWrKX1~JD=uQB>cadwZ?Id z&(%&Jg|q`>yp1~eQoJV<@6Qaf-{ciFR_# zJnwIxoSOh1tZW_{PK^ zV&1-Pxgf+2R~t6yg&E{}uU#!}Af((7ZahV2?RHfp30CmW%$L!JZ+SEy>nrtF zjLvg(<2g?sPJT?mh-DF>%ZWXldf8w$;QXj)q%43gp?-$G#`(_tZTPmoiu|rU-+ZjE z@yYVn!n9{!$TlMu|aYN%twbN}ZY-`3i3FV{Jxu`UbbCWCh zfb=@teYM)Qn>ie`VY!ySN?_lc7mXn9I@*C{NR<{gN-=ZYjOrap&=Go9V?|CLC5E-~ zi^+UoySI>n!*Lnf0tJhOI@b%k@fgb|OR3Iv!DnInzT?SlsZz@-u-u>pju&%EZnIou(bu- zj-DhMY)$tVjnlF6#s+s>tv@KZP+>Srm5k8nK>Rwz`N1-$Yd<+}gAv-bqRp2d7$$ z;}Q{XBlhJydO8vU2!a2JloN!A;e z#CfJn$)GrP?jHK*c@8d7i^3a(mP-aamSnV-S#&ys`A9X*&bJPXdI`Tl<4&sP=dVFP zv}Uo(Zdm@&Nf95RVBcDVqmZ(WMUwhbEz*LRtlv?8nQgR^ZbifG5VcE=trPZ!K!=22HVi8vy3SCm>Yfj)zK4{zgou+W8jEd44?! z%DY;IV*2r9sl{605#dXF(vvyZ{OQVmGDKB)DUk}2rcKanO{;!C+FwVNuFk2OryTei zpqqHm-AIhLAUSF?m3_vC3S|8R zEZwbol6+1AbE-|6Q6}C7nU0j&DEIklqPv$j9^%e08(Znvbljgh=yWt3ag)utj&)Be zC7#OxDGBnfnwr_e+iqC6X*U&asz?)?9eQK_1rq=~$4sX|Zw`WqTIvGH51HHgyz0gY zWLC_EBJCEj{A%+`ek`rOA-1e`>GoGiONm)A+CU^+Rh>32*Z{}KWdsX=uVr*v^hW}+ zv7y3ZG;Mal9#*HJMSe^2fqN4AizW*P{*b?wg;`a_Q7`hKa@wENblfIE{{Sp~)b^(x zO8)>V7oOC**;hgoIlgnNicCo=83d_0QeT=|%OL4yc>-6dK#x(yVG~U5FX` zC@)py!Ml;!c{^QfEhj>f0lidjaa#8*Srsqp5;saODV&$}8}yOFyXnQHV&dfG_V1B8 zw7DOe;Z&IO+1Rb*T_VZ{+g~pK04@Ij;|B}MN*h!!X4pLDJu~<-+dqr{0Gzol17k8u z>YB%d`mg;D$$!+R(uXhOJ>>6~6(?K6f7kk-q#qRdtsC5Sy_Qz0p2Tw)F5l4o0sT9D8%fRZt|!l4`nc{t zJK?;a9|nIGz?_>H*fF8J!W}|DgwtzMZKaC3SF?O~__EGhkFLkDWNXK0%1J-&fBJq0 z#a}l5UUctSO4Ft`DKy`TZ~ez)w%-!rxj0ZHB)pd&Y9kp3)VJSp)!7LpZW2w;jWIqy zjN-N<3l?PF6Mb|2=MO1w@6jtG(+kO^(O|hBiq6nlN>ZH$gF~fyil#n25$CmEEcF?# z17#!>gZ5Bg9-nkG9jB74&+{A>FD%{#NK^R<2FYnDxk-S2@o@n>Di)*C(fZ51)GK+U$KL z2AB2dIFpA{idNBY8+$8(maouio<$|p4E_!B55P~^Nm->L%C1rba&8IW55rZrYqX1< zZc-o@@Xm|f7qusJ8!c+&q5zYKUZe_2b{6#V2nPNoWL&4Gg)MBqDQQ(>t9z&^++u}>+uyBQt*)ch?w~W+^MA`yotW%c*R+^D z?0Sv<8e%&Q23Ey`+{cmO@2xBPhfXXMymQLC{{?YB?TDY|aKiD#hL z#8<^((wdF=AqvD56~m!L{>mz}6RntI9_rmnY*o9Ut;=pP?q`xqESbgNS!oGEmmN(u zyI9*_x|Fkj9Z;nCdLh#&v+!JZC5ULS%gZe<^CQDrub$>)bDO+{7Inu_S@;DgFK@u5 z{%cK*D(1O+)#E3(QQ8|Jt_M6I^U^3II)A#-fI4%MWNSe@-)?V z#!sNFi>Sd$EnE?_(^auaLjcK-g|fEEx{d8k(>AaYQOOa0BbOwk`|01i2kio1h`-R1 zPl&bY+HFim+L$@WjuK6@9cYo+3aJ_{Gl2e6e;VC;E7Zq8m!jv0rz5ixFI|oPUCUUD zoA`=4+KND-LpKl{b6a%wQqOG+EoXs~(AL_nQK$FT*4_cFVXB!sh0(8_3+~5$+(pRO z3OCU77O5@9E%xB$v1|M`_S3c6MpWQtW1lX)Dr&}v`(qWddhDXcc?AkaPDeJqzS~nZ zTG^}V=n0V-L!cs>jiM8+qz(Bue9blQL`%hqD;n`628qA0)SVUsg9Wj4B}=i;ljA~_l6?btB*!fl zl0idZBpr0}sKTi}hDO9&oxSdcV}eORf?HH4R1B61MbrWCBF6Q~^M4eM(#~E-YvdFc zV!`Eo=AXzUhp{uC$Hq{xj!3@Ysk!p1Js*Kn1=qND%RfP8dx40|;hv4mVj^)uKwXYxbgjJP%4YbShGgocKYpKsXfs8VyDai2UXut;n%#B)$j2g07eM)t~N_`mq}{ruyIpV!D&Al_ki- z>MizGeFeXj%yk9E*ilFtkO?1!IUZ7_>MpqJU!b!Hae|PvZMU-8{k12tE&6IoQj~hnCB5phZK=On_C8v@oN$iqv33eV(%F!^6RLr&8V@uW0Hw!D^w9X;wxxnHieQ)HEea~sBxHcmA7gzeMczOb}XPw zxKiObS(L(=bag=l8+q$iTGNcWqN8V8QGG^?zvw)bj;JEuU2AtNfrPNjTaWh|b`%Y) zqCzwlQbD!7^~mzy7h4Iuv#jGe&uwD`rtfoj_D2Zw5~8wSl9C;0q-mwJ@-@lT@r|8% z<}W4exymh$8pAo}WVXI(#WV+`R7*q1UcDDuxhO20p3TY0fG>`)HgL1Ix`c4 zWXoJxD0!C(7TMCof)Aa=d)=Cq+Z;4`a@)44^d?y@>~yRu;YlD7ufC;T+}~W1vsxG;Gdq9BN7Sgdi>dk%_j_T+S8%LTW6bI*#vJ|rJ=tA88m#sOue7{b#VDM8y`e-?F!?UV zJA$7FPJ08M?B5l7kscXIR>m&Ivs85ukD{{R_a2J`Dl9&Lk0kIyXiM*jfPRj*MY z^x&nG`ka8Icv-M(Z|8GMuo(O*7J82n^unhA^!YW^PM2!HQr3SaN*!<#KgY0d86KzU zN?V|8U&ZS^WJntN zNW?afWb(NV9;#YfN$@lEuq2MFG{B5sQPD*`>kBoNL51Yxk3T)RIZLLz{b|BeSu>^S6U(fui z{d)Y8$<1&-6xZ0X#!m&k{{Yb62h;W8y5xq&t%WOX!5%~!;g|Ad>!VmZY5xEh>$Uk< z9)D&r7*!lJs#a)KKGOqzz!5?)P`GwY+ zC-1QrWUlGbwxDOc+4P?Y8@7CQD|q~CFoP`;pyR4bicvmgS2gpl%H-_p)c0zB%D+U% zSp;^cJj2XopyOOhg^QuIA$s-rkO84<%ayK6s|_jW&(R#A%Xn5AK4wyQxk_;$gB@yK zy6QmpSFZ6KmCmHB?s@+JC!trk{5ug(_ROpQ0Gf~d!md8_q*_ZR6~|?zS6WlJew%}O zhu)5Tn;pmFSqIdgNu-q>3U{Py_R`mHU}lXL!f)}MtONzkfTo%wM>Hev6r<$plg38> z0BN!@^1vg|Zq_9sL-dnP{o3?pThyL=4&y>)OSw(RwZ~CPS-XeaZmma-#M=jtXui@B z7uHcflVKvq?5dtack*W?=8mKC>WlXggl;MueGpK=02L%Qo8RuQJKNrzo(|HTILEFD zq*t8Ulp&y$j(`9^Wg(pU4nhGpJz$F|AScR}%R>W2CiWovsmWdBEA8M(#*pFv08l+| zBCTu>zKEJjq8RW`xU@NE8l|ckEECm9_;2@8cGRry7hZnYVXW$01j~rr!>I~+A3ZxYIH`zTSJ@a-ls<6OJN4%*}V4MytX5Q>jdTOZp? z*}byg>S1furT+kx1zN#yHntRTSX+mOq(8EN_jX_TnB+8Q&*?#io8ruA8wDCMHoDts zDI?0%ySDfKW-_&?PofD#n}=l~DUKVrjUy;ezDB(3T@ne5%Pb(6QJW=q+rOusN7`%_ zVhw*g$sIzG7G!7*p1WDxtKQx{l=ubk(clZ7!umlv&n{uh6aQ^*z0%l9RhLJt^q!Q!yA5!1{Xcl}?fHQcR# z7uru43PbEqah)KXFljzZc>>>!EpN|BLrS_9eTJpu0&8Yj3^U6zMdSeLfdp8K>Gsm^TBf?8 zwYg$pB=^2I1(Mw9`Hz%&lXGjhunF=WJ~hg6-yQpp%LRj=)A<^&^>sclcHip5f%Xgf zj{Qe{OLHy}mF2Oy!!2QOih$%)KLP1c;icIQ363@t+ku3QH3q%wd*J&!{{S7MQn_Qp zFZ%sI-p>@&`NcY1<+n+6exKj|-`IVa?f(GOXKcQtc=sjc97`&`H<9BmrJTnL%eG?7 zTHLuwjOZn&?lmE`5~Mg#$D}Cui}qa(fMw#El-_&KL%xNy66i-Wx#qW)XXe` z?!|4?lYIvb;<68f`Y_h9(2$aUWn{T}m#GT)>t*N?ZwHvt&>ZX6YQMU*XE{gIGU4s| z1sK9*f-mxpC;tEfzq*GWT92tY@%LjG3&g}`03bzO^s(Bn?x9)8()#2j8aA%vmL?-H za7<;Jcx_cwUB6BXiZ!N7u~D?y9?PkD%OW{6Y^% zQy~1lIZE0(_);opC$)wImB_PgC25Zkw|9*hE^=ONfLvH?SMMTCoPBpUR?SWsB3YbnS1jsVW>8^|-5x&kR$99;hoCt(j)Tk}A6_m(9bAu5Ne9nc zo9j#Oaxt3dfw^a)KSP^(9crpQMI{WI%W_Jix$?mxK^ARF{GgvZRQdx2mg=OWPu!dudCtEM-Xo-6$D3`x6|6APW(C z*cO8}pzLjy8oac1 z*KDzf?c7xxnS%Z|6+Dc;n9YBb%D5b6u1n}NH06^Q%I7aeF9(jX22)`#1z%7{g{ZRX z3u~B_wY;MD#ig&7LLFE-57}8=OVskM=Px{=&+_a`=IV}xdFUys>x%>Tn-TS5{{YCa z>mL*}dn_r)>Oc?fyDR+6-5hTXeIg`Yi}sLYWzB5=0Mrpj9K$37@`A$c`u$;M_ zm;Pbut~IuvQY-9&47Bct;lE1h=-ld7TM&vbZ+WhktFzcr zL-x&x_lmgIzo?yq(4UANn)I%b#}A;peFq|#tP34C&{ejW362~5*7K{4c;;CJKEsrq zQ}3(Icy!1I*mjieJ#A{brUJfL9RXo#u~`JqRH59#P2;7i_Aj6jQAeo8W{ha?H3-Wl z9;ujmw9$aKv1~Z$bljDjs9#FrIZuXBWuAHJ`;OJSD6Ao?xRx9sDK^+aNvg|!4MduM zK-KTaD%)fv0!7B3b#~gCsxV6;6S3q{$fo_e{-C^>$)8aj+T}f?<$bNp$BLOfxS1+b zJ;Hr9Q}Np@63dB5Hy|&rmGS5PLjM3G?|fsJ)!9$WN=>UV>C?aW^gY-3o8Vgi03g)Z zrF7a!I`i56P}Lt>e@urOXH4$qClehfYo=T2V79B;%HL~P)lR?uR(wx;UBWlTO|H%1 zhS>iA$4{5lW)`fPUaJj%eox8Q)bBmXWc%gyB|m|46}`kep_G#yQsO)ee6A^JNe!ER zc8ag>9W*uZFEHVD#n)2*04${Bn)UTRNd67Q#*QymJ1}1LP)iTGct1(}C%!$Y?0?e- zY)0!1^kVt!+c`BY9*Ugaror;TJPf?TblVz zwn~{Qxx7C+)~{txQTH*-77!g!`CTs*t-GBKKa_^CvA(VK87$CjW^#k5>`n)&yN z`By8>^A#)BAf?^(=zSCN58|45J_g$seB56})#vDarum#bkP(t$Y3LLkLV*_2);0I- zTtygPo-e1={QOjG^~@jlF)1_ftfZ;jjC3RoN}WLcrj35CrD?Y{{oyIq*^}uBKkR27 zX1R7H{`D)0p_W`_sDC;#=~_*RP(enX``62S=FX$#~MB5nH-TNvY>5M@1&7)RWiUi=bHV{YQdqp>yS0)SXIre zM_LwMM{9(T9px}@(v3~CENhIx%W;FHg)>#M^&N~P3V+c(Ek}cGbsnq868^)|-r8gF zL-5W4Wi1~qDDjp_2rZ_`0O{pSMtKSCo=Om>8%5GL5EFA@dd1|jMt0#7JZzpV$JQiD zmM6{9%{DhB9`8C?>)Af%HMI?mhvOCEi;s?SY&RfCW;L62BUV*lsubw3;kjNW{cp_r zp*-&^#9@}0?i8)T`OuOsDs*VggyT3n_7|6AK_BjJAS~IghThs|>?}G+d{V@}P|;n_ z#FCrGR^JtAT3l_Um&PUk08!_ArGrTLnI~N|*3@sommv>>ZjhO6ztl<4? zwY~Nf_P+%z^$8D-JpO4BC3lM`m4YF(rqg?%j=zmHU&hjT$XCQx6ph^cGwD;=9C>NO z`4E5=f3~9jJ!h6tJ2YHYBG>~h%DA>kMSsao*z)$ql+fUiHdP+ zzf~w|29!lj`Rc&?@%jx!c!~=wW43M8DX~yBAGm9~826XZlJU5lR#N_7knwjGrRs?Y zQi4XlBTB8?+M9hCx?M>oUxM&CN%XOJX$?N1@VTi@zS2!6UgfL18uTJ>_Ie|O_%aJu z8U+A#^1WKq&B`c*;6*2PV=jf+nGCHb@{@5!Un#=s57-+s*=(|u1euI9lhr^iRX_D9 zpKgP!_E!^c^>Uwd_=N3Be+$qh@=JXQsu85F+3-j}qDmYpPK0`f<4(0Wa~l%u$j+$X ziq7LbyM~osGw-N$8Ce+tt=6SJTp$tvBb)6S>ugNtlnq2)Q)|m(>K zAa3i|#*x^YP;A{1>(sxoJQIyOkBH=)VqbWm7oJf%tIpNIZSq#B^kvn0QD~4eg2iK4 zb`*FBZWc%3md8}64K*Ihub%y`S;*XVql^kc;(T8g7GmHdt;KYCXx^j$0Gjm8T&>U4 zC+(G`kQXD9jKf7z-;RW(%Ye#Ik<;8N&qmAco-!k|8sNJe-^gU*<;q);z-QDq*eoqx ziN`AKF!1jMFG{=BFYn zR?*j1l%wBu5Gq$j#?~U(dab5EsiWnO(+f+T^|keRG1WCGjAMAFV9#kuD06<`s}M*a z1lqlKo3Z}aO2=2-eGe|-qZi0n)9l&wy(T&e_Z#i#MR7jcI`=j85;DM5(2}5kC<3P* zqN>afSeE9BS-q=D2x4}I&LNvd$3=g*oV5 zEy+*`Do8(hy;||xzSnU+Ls#8h3Zo&0%0U6w;;9W2e^TEG`)jM#cinDIt=vU|q4)0ild zVVEhBhmPP*gd1C}a@@DaR>ZTC_eYpejxagScLHv5JZP(6GQ@9*C`cE-zO}}Db{hH1 z$qmj@Q^p2N_ZyJo@UuAdNq(L>a4CJ2BripEwd-^E*1Nmh^#=}O#T*rMop0iNO?7o( z=e8{J{?IepZKs^FFKpTwE8(||>k=39EDjCJB;aN=%EtV2%KkeC!`eNy&U}l6g{v{{ zbbLo5>y|wZn#W|Z8A$&CF%{sjKRb8$lkXMfd$mfJhIcyhRxF|N8;ZN^P+1<@s*UZT z*Io*Tp5z{TC?MRo7BUic+d`w`UH<^bDvO3$#&Y(`GQk2&$D^w4y>DRB)z&RYv~%Bd z%qux5*Eb1kO7zqowyjyjRgS?Szcl-k-aE>bCFEmfG?xm&+asYC9W}070M$S$zkiKY zuOxK5cH*(&lR$skVGwS@C&V=;v8d4T=DkeP^(GBF- zRPpJ~W92%8zTJwfph3TgtG+tfTC$N*d-nU0a??(HL0=D;ru=0d=w2;3Wem#&B$Vs7 zuiHk+p{;Den(hXgdC)vrv(3r;-wjLKLCh~H=r*ORPi1P#@@^L>1>j^QC8RvGS*L7L ztN2we#=65S_4Y|*e4=)4E^Swjx~QkGEc7fD;g%+}>Oo7u>(biNuUvLF4S5DnCm#y` z0P@oP0ll=ZZQ>TJ zV4qB$L&{mS?0xV=F#L}p$0z*U9j(661xQkN1$zzZ^6Dwshl?rc*uPd@Us7IQr1EybVU^MoedZy5go_S9(q04_ED zLqGV!KlK%2ChK!=X|G21&J9}7vgfH_5U+>#YAT@q`!K1BS-$(5OIn%7D!o8mGrk4+DMt7pNL{7E2q zSk)%Rt}}z9*V^)3ODYHQH(T}P;Ul4b2-uQu_g5ir+$nyhbWoK(KcySFoa0bbQZTJH zy$G@rLARahinTAFRN0f*qCIeeP&w8Kl2C{VkJnV80)=lAv-M`-%Fw$Y%Y54ay&RG0 z-ui7GKI_tNfxKaJG%z-tK3Lcy_H=4KH?d3F~PK6Y|?^+ zxO#!rD%0_;PlRYBId$q2W0vAx3H4C6JOJt{y51sD8LyL!gV0@Kazl`$CDgB?ZZGH3 zs_(9GcxbrRoL^MOO-GWI5=kJDuez3-e?XAv9ARq!rNLl2kxAV{ETLj(e1)N2+N@Md zAyu7wnOm=gStu4A4f@pOFTmQK4L)UJ!DDFzb)c${$J{0_{J10#khKMuRvjL?hwz)~Nv5>;*-Cg|ZQR6i zMq+bJ%LOIr36XR0w_udo{uZmPe$_J*e$13&d`A-{Dk z&z4Bj8_rUxuCFAZgh|m(CiX?TX+YlKtbL8q$swmUF>Lsqs8y zqb$hHo&2H=jN?25D1AKCXP_^}TPwPM@{nrpV|R9^ir+vG)z+H)>!{}`+Wy!_ZEis3 zFPBlO8B+yUJ#ElX=;pVazcM3d3#*QVHJ&Ajkm+Pi^cpI&au%Cuex}uLE>h(b+83vb zrdTN>5e__X<#aepl;iBf_9kSwcnr9`1lH;fcM^;b5M^HzYu0xaOFHgo@PLofJ zsV^v(d>C=C@qn2r2uothbwmvXa(2;W(*C8&zBR|G9ewQKcD9f`Re-HlvOi~{6B`9Fc(fqb(S&||_Zj_0^81CYazGZ@yMODI~y=FPaBsVgKX zYtU8y029ew?kJ+8k=3C=$*#x7Ot;pD(@G`GPC3p)j*k*dh~-kTw+b=dQzWO+Qi(|@ zHa65(yKDSgZctCT;rm}Q(v7Wlz}7d{@6zrrL5`e;Un@D(fYSpO`)Nwe^-`Py*7nmC zd?#{KPtgLm=h`x@tYwv_(kIjhWnbW&=M(iE-8jqTYUP>Ch9@62zTogljvNO`o_ zq$Gj1{{RWnsK1bYE6YoIGSiDyVP~hRU4O*C_%F%0xM@jk$vNf!0C%VO9HZFyKTTOr zu5R|_JE@w+^3SLEc+9cGM3|qRjg)dzQCnm-(m^h|m2L?GrFXw3*yL#7p%}~CwR-k{ z*l>Ifrj@+il5z8cXIb;dI7UypUrkMRkG9>iFK|=+_ z1OlrqS{vHD`-R<6pk{krX!!_u(w<#HuA9Yt^L&3VQ*$5hVk(Zs%QiM0CZF(wPrhv`w>IbLyU($6eikO z*i!!hb7Stw#^H>`zd#9iTg}=h8wZDrG42AdrVi* zY}-I<9kK?aglqEcpVB_+Tiut`-Vo`C$Ts*zhqANWk-QtGBP!6OU)(4Xy{^HPXvoCE zS4NY69R*GA&OW5!O32|jnA+L2pRiWfDQDD&w1peSGT8^xNh7DinoiC_ZS`jTu7Oy} z^iq_eJ5rwjI#Ra&eylZb0aoTKa2rjjOKa#I1R0ob*4CtbwrqZxfY|F{;Z1vNSbB+n z;;OmF^Nhv33MN&^>s#<7h0!TQjYzd;fv;d9nL^!lrKQk>s2w6eHoAbUNr06jv`j!h zn)dLfl+e7jm_eC=O89zs(zEI(X0{Mjr@&3htOr^bk`SF;M3t06Tp%bY3dH(^S}mb; zWWqpDI^Rkoibxc+TM8>Gzej6-nP0M$vQ0!mXfH0JDa>Scgdk`?D7_6^mKE{T=PcaG z&O8!Oj<@rIBlGkep zpT1+8Lw*FZzZ3*giH|a)$c_TWt+9w$ED{2d>B%d4j)un3( zFSencgo%`tkBM84oi`j5_B6+Bp1RYBUTApP>*GwMEi^l6C#|(K*FY0v#5SX}4wT{+ zl+Z=E>NY-9Xja=3oya1F2?F-Fom&00n&>AhQ0);MBT=OmzB0jiY$4-NTsncTl{Zff zbA1J;w1A0rh(qDQ_2& zCbZ&g*x@}Y#vOnmN^j87fkLd?HnlO}M<=aV;ATqy0Fh$(ytORg^ReJGe=BU(yPgK`@NGwgMHLu0bTTN0Xs*zORo#%e}jH^ zZd|!0m*9@)OXc4ktG=g~^<^f$CEZN^R(2PTeNu?od`pXGC5SMY^;>m1k+n(($J<^# zAB6MP-1XtjN*2Ym*X92Jiaid$k?@-J07pQ$zbkFx)S)Gm^4k9Z12X#!^)n^4AwN?O zsF9%<_Rw$dN~$}*!9U1Thveq$fBtMg{m|OK%AXIeU3j>Ed;b8LTxR}~yYZafo&uHb9EOdXK{rEF?Rf9l8l4u0q`K;Q6}E=18@_~<06Z(keow=2ynWAJz1uc+JtLI=0Nj^P>URGC4gOK&wk?hJ z>QzR*dd;kK{4%<~=yE3&`ab&c04~?R=3m>N%6c7 zC510<#r`j^`+sTb9QX65#OjB(v1j0yL#A=RMSoprTQb}FlEGmz3?phJe3nLHN;ZIZ zC2pl8`)ysOzsLC}9DnAnQQAjJg!t>}{I-0NI zCyZ3f>x_G2iWu7$oxg-vvVvK7*(FPl1DYRIIy z2VH&iqivV^^pQC-NLS`PYjEsz6jRU(B2m`Ls7fzA3mRwzJ0)IqIMXNGe!37b`D)i% zVie<`^;+l-?9!XvU3%5ETKx+cYUCE=@-8=6)L~Ci_-YLmkRWP(^z0#oAMH@Z$;6Xm z){k9LB&$PZNj5fJWMGZD#2t~KvW@BJ#UW={Ims>&sl7QI0Z|%y-{G(8#2hL5kc{nx6`J zM*!f#&d69_<{%Hcrh=(pVC03Uk$#>vVO076v`^Jh8fYtUV3Ww%d#`=}00oT_J z5DK@JlvttzMW7{GY7erHQh7AU_EQOpoa)|_8*|PLTuO>hmxh&NUCkP4G8p%5Ffz*j z0MtltcBBP`XnY05Jx4J(16t{k>7A9ssVZ?fCqqd+RD@}NZCTgydy?!5dT57E?N1$w zwKncqXTU8XHZ^Cj@(biw%T6z-0!MB5q7!9g98nzzDW?Abcf8-)L>lJ>;$RJnadzwR z0e`}`S1YidK|NZSJ$xgJCB=WIaH!u>y0Top!+8VyeM2?zjy%b-A)5}jQ8djwt<7U1 zmT6aks<4=J8>SZEI;5!47b9=f$EgL`63#os`1V1yhae?Rr0A=t8&y8%CuC*Ogr`z1 zWR$zx%o5S?LI`wUX-PJ{RY#e(JDdSmxk*z%WKetsH`PPC&=EfINRk0SKmsS!O2|jx9WnopTy+;ZIoR? zGMq?K0l!59YYOkU{#(oPoR;SmB=U2T`;ppQ1T2Z{g1Gt>?gt0$ zmn-hZN1JyE=xwGPXk02(R665%sS)J7k6GQIsVOK4SSR<7m0Wz|Q)cKv)fM>t3NL~3 zI+RLka(VPSjlZZ2L!7@Km5bfOg1|eoY~ub8HN6DKS@7WhCT zP&SZy-jce!4_4fZpNH`b^X;Q09x8$nt8Q)yC&xk7xz0m!zS%lm4Qm%%6FFb+M@GRB z*IQ15Z^hE9Y1-HFEbprC9)wdPk;}n8oXGA)cl3pV58P_{s_L4`DDA%3JTZXfQc}~A zrZSn4)+||2(#Q5z$^H8i)9WYiii{@wCC9u1kw%B5=K+;xuUyjo1ykp0t{0VVRG;T-kH{HGOGvtkRbkNDyKjxl zViQ?i8OHO~Qz-QAR2(UJCbB7nOR^$JzV#i&2|fnqyM9qu;g#IxbYndJ1-QIxC(;NloqR3jM|a?ZjzNyZQbwpd$AzhS?#mAP2s9OwMc?5&udfT}|Sz|uOxg0L!kAh*OB{MO6jS2}#KPvsxU@xY% zMa*ocB%oYzx_%4u1^)n+;Z-ofkCaJ}FJ{Wdzp|?C_u(A1&~h~E$>B?=OZ~Z#j8e26 z&8U1T2TwXiJd8Uvfa^|Ph7pIDy}@n-gZY8IIUYhDhMt>b2bQpS`yV>a6}jC(b=2J{ zJ#`-1>o`ff+D=$Go?ja2%f}}&a{TPhKGTg#s+x4+vam|TbqoV6-564^P-+gUC>Se( zG|(H6i(S3JT6hpar*(M>>{&=s{VO2w!3$;PTu$_x6LNh0)YR;U^b?hHL->>5XcF6= zp1K>K3JmIum((OSY>#e)o*yO2vnhwyB}L{)C|OnX6;HdwS8Q<6ajVN&3H4_|;rok> z%!tfi`u}*GHd)m|5ybhIu|Niszn@(XH-+-_Dx1`NIBm zzuGb%dVK6lrr;ExeSJ488b^ZH4EGoK&AQgy@_{7N91HnW21Su@dXY;8Ne>aCMf%o> zV1Clczrbi0zJV)2ShJmEptq%tx=`#&NIiE+5(M&T$@q2)bQcDy&6{`PWjKf8+y(Iefl2?Bx9bSW0YS{R2wa_ODM2vdU^ktMd zk>E`&e!M+KX>uz`ke?8=CJ52ke_0z3tS~Q|> za$>P_ndBe+=G6JvpW8#8g*fPqSGg}P*FR?)7RzYvv_AfSA;MPb z?oUp-P~*i!XYWlHoGKS0+V#C-;$n>Z(X+gP)ZHKrho$RK(9Dhf;=n2V%(sCav28!N z38(APUP6{{eq0PyB}@30;Zg7==AVwLT|w8d+(ms+f7|t ztqWMC4Mh=ciVtF~<}X@kVK#e0=jFP8diA3`n5Xc^PwlK|#U?szf-QY-pw^Q1GBn!= zHk4dn?zKw@+LOs*@$PRsjlg;YR+@FUmANWd)e5z)EmV8F6fKrtLhDwDEnz#LWqAYfFOQI;&M8{{UvS!EbzWQEj8E)#TG_B$*tS0!S#z zHbLq>A@*u2kM?U(Yx{%<-zewOu)&tFIQBD6$hlmFyrdzJEo~AF>gPv>P5xjPZ{>6= z8pFh8=o5{t$${x)rc-qYq2P6l=l~sGWIp}V{p*srEN=;->mcM zWyF*(U^_xd1lagcWvy1T@{j663U=(T8Ge>fc%SLL^ke8UW_EY^oQAP#D4Cv6AMY z$*ub7T!xealq^~bxg$=u6=CtunsZlJ^J8Ui$bJ#R>r}B?O`j?|_1eFut|!AeZzqr1 zsk~phJbyZzK6ABsJglbEj*R*Ph)_c=INFeuD{xQ*+}h&3W8%Lf_(&J7 z$N3J;SaIHaIdNU^abL&q*`Fbee(&mjpE22=t}X_gJbr(V?Y9`kc?Xs4x-0%!P3~;4Lb?6XSj^dD`a+0*CaURRnM%O>Na>bHu78=}6y;95~k!uVq z%Rikq@~S1U@-XY_Y&hAzj~~DORCwCh$F?dkZXo{voNv>psVfD2u~CYA{643+)_Jj? zY*{$jC?d<$bgXt8_KlSjAd&$D-j-G`Q$4VQ5o7=gfl)OJA3)LSIZH2_{{RohswVoA z@Mh`U0=@(7sk=HE7??pK{{RU-%3WG+Fv#>cyN`g6vWw=!Mmb`sbypWh+v7p78mw3@ z!uBBE_p03fVA8C9+RwsLH8mp#%{Y*h>A3mSA47Aje%jEo2}l5G(wFke3GIYhag^-% zLV>?fDO+UC`(Xrps(}6)e&I-YFr$PjKkg4^l*5z3i3{zHIwV`&Rqk1k$5BKkIsF3u z>rtENYFQq(hJZf+{3;&cys{{iG;Q$eI?&v5Man~ssP=u;yaMuO;kK`TA8?>qYD8S* zKV|eb07^!c49fzRRM_nq6VRm~9-`jLQc{va^3ygBM?auk(+Y0{TqZ1qGka|scv6b# zXSg{=L-y9mDX{m`9!yqokol*Y7O>Wa-$6;&CKX~~O~^Wm63B(R*&jEUTV~rqwP{VJ zEj~Fgn|bx^8(Ztvr{*I*C1jC2v$ z)sNJWMw@q>4Y1e;OMV?G`Hr3C#a1706CD3 zhwL@A%v}#sKeoY++Rsesvgh4RL*#p2fGpyx9-}=xC-R&4X-+SZ?h4C_t04yS)4x(r zvr1unuaI4sr1+YGjB**>3Uv2Xo~O+@81(KVZx2v*HhuB{al4QEwbE*Q*Kt40vQ=Hg z!_kG>%kHrBy89~Xzn9qiz^|Vlk5TJ#{X_XS-kPVG_aEuNwDtT)iC>Ycy03i&r4h?( zR=U-_7PaaEaYL5gI(%Jf-&bmXP&2F`hXEt4bo!V12uOZ9*GH=%ScF>fuB%h9wS%O7 zYUpe1D#iNK)mwrGtw#5Z$>B^#xv|)*nD!SvQf$Klhgpox6-Oo*`)GbI>S2&7aDy=N zwHCEy$;vo1iqdy)_?ISkBDt8Bo5`^uevVm?8kE|~QlO-|-INZs;@r3J&k4)<%5hP* zFY_*qKk9XwpDEtd@llg-$7j_w{R-+|Cgr`Vo$|ccFF(ShFKiBG(`364cqVF7pdaHJ zdD^^3e~{-s8GpCBo!W1;iDj?;^8Wyd*VDx4b5(h2;=P6553~OO?*9PDwz2tm8F;MB z$IEVHX$cBx=GGRH2)|U5UWLZ|mrs$cPN^v;^k)^dp>Dj~Y)v)w1alZGpT#)@Mw7kC zFyyv?JW85Su2=a(j(2cZMk_DHmOmWZRd3Zd*-kI~PsJy3W4PZj%0g03{Ct@VskIAj zQ!lLw1p5kYUzqci9OwDE4U|bw8yaXJI|ZavkuLg#6}1*Tg~~ z{$?0(ZX{dxhK9YH_!HqvbCQ<*%P^1SU)axwfAWjx4pWC8AM*7+$LIN0CyL?bWwH)` zr-0c~(1evN*mMAR*Vvyf{#4^WJ;uuW9#j`)-1z?hiQu+(_G49RteM77=6l)o4VY!D zBg^f7Y9qV<0LfXGNy_BdzNl`;_(1rcwy%q|eq-{lc7t=pYTun{{W%% z8{;?+ic|jpaV?$Jx_{9Z{{SEDEF8aW`2PR_{!H%zgw7i)CTwAmmZS&dO^y~lR4v8b zM5tflzLn8@m*PA($E~KlT9UR>S$*67tmSonTf54}Yi>?ZQFvXFf77vfIzNOgpNRFZ zK;MQSo-xq#s{a5`EzR~iX;e$N{4hl(Dkf0MOBQkX3)tc7Wo^B_<5m2cuEmAbqWfFU zjCppE44sa3X;$8~$ZY7gx*aaAP6@c$v(qT`L%wCX76h^6#9}tzvj}n8>HC9e>=xyB)-+-kbso;6sH4597c~5m$$B6woN60J*E)2) zt95Tp78UZ-Xk_*d8c8Pp!KJC%cN^{&8%_EFpT)E{5RRTS%r+!5g!=-Wg|gry-AzMf zmagYj0IX->j;Cn)4%wEASHVIa|~un5h@)(-FukC%lrUA;x7x`W;X-owXQZo3vh z&mhGlpM>dZJSCENF||iPtNSfP$(Rh{xb8CY(u%p<8d1oSdwUtR zS#lKMJ~fY=m|tZN8{rtX&&HK1{sietzGl;Y$`tUfVYH0{RPDqGGL@Z%>_Jzidfveqp;x!#%OGjn!au(wju$ zx(mOL#uT8cX)Xp~9NEHdN1lvbK&Xmr(bmo_s`4N0@La)cvc*Oeqq8d*203h*YqUy-V7cMg*vUrRpc%K z{3(~sPv|f9NKrfs0*{EHYrjO5e{~Lg)Y=$Yi4NoO>|7NS6@b#T__evDrM)TjW-Vka z;t-7o!W0eYu$DmC)%GGY<3mYvbf;5(sSc{wrYn#r#~lciw%Z9HOL^OVOSrv18g470 z7P^tbKz7RBLGdeDwL1lc*n@oq12J5&Q+FhMK$Am+*U&7MMAeLjw1k!tzj(R)Xp*%C zjB+$z7{$y5FJoG9wP>)W!3Lon`N8xv?c*+tqM`nI2hGB91#~mg-+-^oh z2Aqid@tqDOsg0)Iqb^%w$6FhXYop?RRef^9mqL@0_}y0ZQCS~1^xgEu%yFN}Cw5q6ulxHmN?_fS$)O$W?EgEN(y5hSlNqCttTHEdKyP zIR609U(^$Xp|SiB^7c$_v46qI3{G*D+ie4&N>gE9Y3%Y=OOC2p>m+=Gr^Zzt zO2pUYi$6eJp!!MrZtjO@yJOtc_PQj7c`iN_oS9K@rPr3wONaAnH`mS^zd7V6-j!05 zSe6fRmg)uLtDf1(ek4quMuY;r!>!pKSEJfAU-YjT}QOjevrjJ9?tTm&py z^<~cd+kU&hB)T+;?g!zzlBM~@Wj1u*4*CfCOX9gkvpBBC;NxW(w%O{9>N{ke!{Tt0 zp%<=KZ|7W>3WkEO^krph1t#Kz*gqMH;$Jp}QJ6!PiEqKIQ$nA79^Mwf)AqTSy#um$kinm@A-9o2W+QP8B3{6{G$qx zEu&~Q*26)ib{^J89KRXK^Y%7b>G^>9};s9j&~sz=0!R7QO!fO;{?I zsaNhX*YhWS8yj1uv?WzsUFiOV8YGlFk#G#$%(u zkIHOim4zruwa7N;ZA$XGH%@9%565m-6^Gofr}9bh=EyK-IF>@x7*SHcASTAfx-Bj` z60K!*D>pZ!r_eQq?gYkUwleGa*o1(TkOGIanp2eEeCy0RljHeRrTL3R`H9QLQm|~H z*PTHOzN>9Qt>Oi6($}3c{OIeUWGs$i%IWUhW~L}FJINH}ph?)$LQsHw0KHp7pRNlD z6<2UpPF=v6jz5{=s!Q>*?mZpU{&H-gZv$$((c9PAU1C*kYV2x0kDxWKSD$4rFuBTG z4t{kWy*v+EwvPv|C)llrH?#F3&$}hL3Y5atxy5ev0VCox>@_O!@WQ~Q_uJ*=o}|ME z+RiZ_{C8UCz z{k6mFe;aHmepz%n`aXF_k5uvpY9&t!kT=QWWLB zU9WvkDRnukx$3~~nnEb#$@N1rN zOt|q7$mW%gWhIc6x=x_ev}i`h)DF(vt6dIsZcBC_>6`X^BGu|xXPig48i!87l6c9xZX;1@kM@U-euJjOa!jKZ&(dzHpTi6)A~oqk<(3l=pNjHil#=I z3lBPu8I9m_+m|GTYvb;r&bDQ8$iDJ)D$w<%hK~@R1zh=78%rW;7X((~@WOjSL8YF8 z^iOopY+1I4#C5Q&_f4wJZoO*(imvdv@2+io;;FGQ7aeB{iBB=yhP4(&JN_&I22sF=#I`pkOLi=cRn1qeWHXbxD zxi|J`e%?8wYn$JtF1OnZxcH&!F>D_aE#Xpoq$9>;e$GB9xA4KS>uRmHkdW9x(f=gviT22k>hN~HtAq|X?G{;7P>{+VUkJE?(ncRsLuiFrvqL=5!Nu1UB*a6 zS^ogT^cF%&h22O!M~J;fK2BXwSydbcIKDO%j!VmU?1>MeP6^m@ArR_?;?Is9LD#6> zm5;K6$=56+G-R7BNbOFAEpa|%dP!ICo+nZ~jZ@hyE~WKR%Q@%j*B!_*d_Rac2b*O$ zBTLA?^W3`*mK8cXNr39g5@aF7gs*O=Y3W_p@wW9f?BaMhKjeNY+jW)*r_BNS3C2q4SO`2v-F)0 zz;~~&aXR~-vChYGF7Rd<$M)CiU$NMXj~iioyIb72d6qNkQ|&aT!+8`*OK!IDKt7wP z3JS3bv9|Asx$Y~6jGXn`Q(e|S-2H#R`(uaN z>0qBB;aqMq%SV^o(Z?Q(8Tm<$TvT+ab_xKHgq0Dlnt@hY5c8Uc)K1rg*;nzN^Wr(z z7vucXhRRM)j&gXd%EZnCMKrsOe2Pj)BfyG&{t{JO>z_u1Y2S?D?n;e?UMr)ly0KWFz+)6|iFPA#s`@ z3o?Zt9Ss7_Md%;gW0Yq%bzv6w8myM8Y;WD@J5E_7=*j`1M_UY(Y{=<3KZJh?I{WHV zqoF2;2+1e|{4a;YT1C+EVu}}QDnY-e($yK#Zy^+AmY|SYpj)Vh5=?g1OwjQn~jI=q-RbA z@zi$Q@~t+Xww2BhS3-S9e=;sU9cf8R@CWP*`pWXAa>08@*)J;RN*k}K8CHisFs%SZ zN?1UBlx~?SNw5dsOUkJCBPvrKCSe*AQ*WTN*A{AGf$MWfVS}?>5U$~8c@8~}9h_x5b1$4M+ zj|O$80Nbr?TJS9RsUssO4uM^39gNe_7evLYrO&pSuH1r_vOxykkgv+xz4oaou}Reh z>J*Nd%D2O|{RydgH`qeiRD-di&9X||+D}ofF&WDxUDws7@bQPOp#!`dR?58PlU3-h#*k(1n zMP2J)thg%JM!MR%s_}st_wb4B<8D1=yaZZ{Ru=x;?nqITENCflh$-Cd53^calb$ARS6M^^M3?k?+M$&xhI z)p=g>z!_82@?R?Ors~@A?qjTa+WOPAOkQynt3<(@(+gpXif{H)SvQekPgX^ks2dV> z{B`PceUZ3D%1M1n&QExFBvq}s8IM6sl9vM8SRjs|+*gY1f8@8u zHRj=EjxBvr*-`v=%3l=W45#>^um1oJU9&g;0PvZiZn`h~sbBv9icg5Xp86br#hl;& z0E%TcexNu<6XhJoBPTf{F^A5s8@QN_GNba&GRL6s7q*HiO1<~56SMySEBH<$cIijX z+UD~dCh&Jr{u<@E4QimGp(tfMzsD#SJL@mC&!yPXak&R#y>u^hUC2^;8>LE}BmjI|-fIiKQF zVz~A#;pW>xL(wGl+o!U=N%=SP2OCcE+2AadK2I`)qWoX zh|Vo}FYWauSnujrjK^c!W1n{ZQ7S@_J1LBHr zYM1$zGO*Y8cPKf3{57%9-O|`iCzPa`f8M|3etJ8Jmy4U`ytk1?LZidWWim42M_Oz{ z5~Vou5~TPL6<$@xRF!Qm&A0vSv+GTIT~$UB?-ZB!Bj_j72lWznv7M^Ic8?#yK4|0K znI-N*m(ohpCFMQ0(jZuk*1hYbGuYZ!4?DTT+}Iv&KKC zu1m^a^%welPpC}1lW0J>E62W#J;gWoS9iPo!J&IfF_u4}$5H}=TZi}m0O$;N z`iT8L%7XHs%*o4m58Rj!MQm+e?!xGul!}? zHf^gGK|w(&ehqiQ-dsnJvCKGs8|6&4Bd0$NiAstVmHJC{0ZK;X=yshhE55H~PN>fx zsUI+M{B-oUCq%o!v^{po2Kv+|6@oSOV8?a>bOg{EL(s^@!iyaTwvn?K8iIKSJeHJi zv~}{NX9U>oZb%g&DB7@7tyJyR`I!Auc?UYvM1E09SwhI|c1Qy!9@^BRGzXxhEYMG> zYewXf0>6j9LrZ?%S=Gpbp28^J$NMRH=`=rICV>xe^{AWuw4AiPOA7gPi1h9vhe5Hi zG@skymioUTaUyd#E6PpNuaK$EqoCLMOjGJc-{AQL+#wnclsHAET-EZ5S8!YW8!W5; z01N@;O)IcJSITwQs4?!}Wh_UgJ%-f8c2Ywt_YA@HNH5ueQ76vO}>s;$F_xvvz#^1OR1j~h814;XF$9bsgUrrZMd zT1c@K9o;`-=NH`ospc6ZhjO}&wYc^S=EjS{!F}IR#s{bcx8K9>qDJhStnzsNH7nj3 z0b})R=YKjpZ^^YB20}I^KNZ2fBVa>sw9yS@*J6CDW`tCdEpb{f-PZ02y}D^lCSqb6 zQhJKnsY4?-8Yar~^P@rZ7aJ`L7QVlA7Ba!Hqsi;-(A2%tSR1>dN4}c(4Iz_>PMTJE znSVozN5Y8-WQ?PcJD)n35x(Vl{>sG^h$H%63N1|A zL~=$BJB2DQI0Ao7#j17L)B}JzM<0qnKC;H1bZ@Cd` zks-$#X#5WqU-X(6-M8pub&!&APBuWb$?>KB;V$D2mVj_Gf&N#?_U8 z-N5>Z`mg$C;=1&w}suK2X2pdD@+k zSn_A+8|dG^nM2r)3&*1IS!?2aMk5g+h_Ky~pO6jH0)4QuBA0Gs@Nh;O+m zW^MI8jQD3aeDu@D<-yUvP95WgFSwsl_%1%A=J7CjTsb&dSM<{%67Ud~`D`l0?0G9n zw9tc9Jns+rVKnmOzq=g^E<0(a^1GdvjP69avP4HPU&~gK0+%7ckZyXU_=@GF$M`;_ z<$E%hVpe%ZLOR!I?J2U6wMlaJCZhGD=o8A%p;K}w*(p;Ul7tdpB}>|MxvPCy!ZGSr zY*#NVko976?l05yoLWwos|xm@kd2zsW~Yy2kVTqVBg`5sH(`)+)0wcqLQg`|LB_J{KNET$m5Y(7 zb_x4r+UhlXiDd{I+a`E+ApOg zuT*P)lw#>QbZCqJ03$&p?M=tBt>d$Tt?I8-d-)S~H%Q!nRWA8Hr2hbHMh_fG*LT@X zu3khxW{8>Nh&@-QpHW*}%#+9^!X|6})n&_CNJ%~x)`t1{XR$Bz&d5vc;}Zm+5a3F$y!aEKsBt_}*6i|2 zk>q{%+XOjHJGiJS!1b=K5?zjO4Kbs!ih(NB-0Mx&@Qk`S8J1M9(|b`>f)lSHoMvY$ zhgSHK3J30;N8L(p>Qi)Z(EhI7l)R!=&u}tY{KE~%QR=dr8s(?)TBiCIm3*S!%>uH# zj$UE1hmeG|`VcRt-&YzuBz4dY&R%yeq*Y&)Rd-oQ?hmoE(*+MU{A9~zXK-m&(DcyT zN*wh3pGHY+xcUJnt+_8a>n!}TNxE6S>koZdQBz9=eX~<}K+R~Jb zNlQ?oq$Q@1kevXwwyGI$RJnNWUWTH^;XH&}SO9CN=}I!fJ%_1q0xWD#g>N8-NmlK8TCkEgxzk>h zsKQKNdVK37u*u2wH?0~rON0SorU%3hqc)Fvp31^r=(1cttb4Vn$#gdx zI*&eNB%}Z+LD#;dXgZ3e@qR_$xV%l%Fzyyf_tDxFgX%dFl&ud#LiUhhcI5|VQbK_7Jx_%~&cskb z3)_7!dRRScp!KC#+WzB6xDCcx9+juTRjWxOCGst0+JPwK$1Dq_;*fRMz*K#(k)92f z@hI92iSZpp5_K#Sgz_+Lr+3^)bZ-9u=7yF*Qy{(8d%UrDRJ?~};^MhKJjcHo=saUS z^O3Rhnr+ah8&rl;wKl!FpzR{|2Tg0E;a-fd#?DketNvXIPHFB&oa^KFY)yHUuqysMi1B;t6V^9SHzRg<3c?qg(0OU`_&Y)FWu7gphUNkVpjd=2!kQquTG z$M>WE0B>VGGux_5zH`(3S>?N5H2Dsk{{TL}T;sn-pQbbZDFLN_SYNA#UI@;8FX7?L zYy8wY$0ub;SC9*ZdZXC>E#T(zwm2LcEL;8a5cmDth znEp%4Ghae;_*}V6m1nujD3rr+-AO0cCFYv9_^t)~Yq9vB7ptvmj5d6~`#EP5^A1yP z{dr1y{weyLPuMPhjqR^>xUX_&@us=HBZzl%Bx#b@fn(7H|)j@c8K2{L|}M z`E|!rd!u8GtQ@|HCrbItd-$J)s#&(|KVIMSK9bh{N9Lylqf`Fk(Zc@#P_OC_&N2;T zxZlwi8EpyxuBXwJ3Snx}=xIJ|k9C?~`lk75@P57c=KRQ#dJCRDZR# z{^ZN^4fRX?RC3tqGFkdD?ey$>4Xk7zf11dQ&}^WkMMdbzNYLCH(>zxT@jrDqS!|@c zentNP@0EM}zseyOn(JBr0PVm30C1tZ1N8~@Q`=q_jO?Ee`Z9^#JZ}q3*J9>)rzLV! z7FCeQ$eg(DWO7YLh@#TmD6>v1qyP<#JMtfoaJKAw)4##X{-yr_1(L&r-{hl8uDbl1 zUH<^}^*)+*mz8%DnDAUqf4Y6HJ)Gt;*C8{wBC>Jv>x|MIDQRkv6a*6DY^|d9^RGGP zY1Xdy8YSli>-9T+KFghJ-i{^oEcVx7ai}%VzNMsIN6e_-?V+afVqtbw@Qri_T3>28 z8mIJxm{95e02l;s;Km z^v{SipU`xL+WSvlw6x=Z^~(%hDk|&u)l?JnWA^qPAwgOX3Lk?~7Gu^;w)=GaXd4my zxf<5)5p0dUYOOXnIB5J`dQ{+^O=S)nRgJIiqgzwnN3FSgYo~#%0gi+Ap40$s*GjIGx`g)f5oth?xa(V=au~xMBE?7dYMWud zE}e->u=NEt*R3$$VQD5fM&H%f!mtdoJWFZP*0m)$Jc~+fb?GLU0q!x%7)^zh?xoTL zD_Ez=rCRN=teR;M`<91HK^M7CvegQj^#N|t^NMFO??b;@@;h% z!7`XkGV;{?a6M4s7r0#fBy_IBlh@b%sy5c2m#NrmTT0UMid|#X{{X!DV%HzYx&qLh z$+oK3`>W3D-%Q{q%hMi=c#bPDo2ZnZ0%@nc6fG`#yaih0F76k4-gJ2Hruqok&Bq;r z?D3C;UO_%|)84}DS6R+=>=%#5Py$D;eYFT}Hdo!vzo2G69BCvH@iyu&O-E@Ww(e$6 z&^*o>B6T{SE7MS(S5#G7$ymcILyVgpqfZM^fp;rORZjeQ_2dxEIz|I54-xn6s&aCL!!WNxD7T)Rj8g{LLIo3K0 zwaT>~AMhx@Z~@0XhFGpciOJ5%<6%of^YG=dl8TdcGwTh=-R}b8k>UPFueCWwx0ylV zh27(=su7fy6uW*Tv9}NRitKfEo=Qo;euura(9^UjD#tA-Xi58N`fOlqi2b6S$=6>R zx}x5KmM4I#YE8Q9RV|6ZGM&9>+*kylPeXA&-9WmBDP6!dVGXFX0l?p_F;>cbg{-X2 z{OOu@vJuDVE9!eG=--kqI2*cBMTzTFT%?vi0JX;qtT~5ov6+-AYj7Hr_J!!HNC|a5 zAfE&6ub+RCe~GlVTH1w5Pt{|&+;AT**w_4?O)sy|tK@bQx4fM$ssqya?9}M|6e%NO zhhK$Y+m%=G3 zP99STONuGh$zMTnr%J4U`1i?taZ2hUwk!VtrJl z{{WgTwf_L2G*@Z6cV&P0CjmhD#cKNh0O79dYxtLdd2pr}{)nK5&$)m9039{|0OvEE zyRGzL^xMp!W-}SC5}amF&~;zUV9JvOp-F6zt+CdG-|W`Bf9F5LzZ|Ddcl&A+OSf=-?_ z@1MlK5aRhhHg@g7NybmB`X+o?{NMbE&2fCft5VrQE-Rw?ou`BPEB>B&$QfMT2ev$O zDURdLKOZrXW?YrG71;j(=AqT4`y`^;gVMTsI3I~^Q7bBpB?z~dg@umipKgC8{N{^e zRhHQq&lp~>J+Nzm`YQSbn&kfhcL!8~v5x zKQ{Tl#PqKAHECB{^o)F;_#@E1DariflS=8{>#P2$H5LB=ul5v^A4nXdxw2C$hxTU| z+5R}SrBS&Seb?pBEo*YrM7XtI%X*Vld_TrHj#vKYvmI*vGlwVriS+*e0PH;$f6V$k zEPQ2rwys`_q*wOfzj(j&=VVUM{r{Z=4Z>JFi8(PV091y*~_m*qWHGTt4 zam|g*uk8N-apzpG{z~&(5^*GpF zNNzO@Tzt?Q8-+s`9Nl~BYNFXZjKUZiI>MNA{b->x=?YdMo z2T@h9v$$&BMouL*8dTlBLr{|&dT1d0BC{d<+F~V^{8B#Z)=B$}Y`E?D54xi`(UYwBTdzuHje(l%2d+Q1jPDzbz+OWs z%nlPXolaF4RN-R3DS1~>>N6ZL(Q|u=@j$k|%C_KQuH1xE?!~vZVyTvXhtHp)j`_*w z{2!A^<^9vabNtl)NtSyZ{F*1qx9Lunr;?N*BE=w^B##R0{{WCUg|D~U9T)q${{V?y z@vXmqU&!w_5QN)#*UHtvS7~QueMG;7k?ky z)%I12VlEFOHK*b0<5I~r4uTc3163QU_UcBI)F>HtIhbZWZ(ffva?#MuX0YH1dJLbQmqtNc;sC z!yxi61{e$s7-!`r-Pt;+&(9_k|xq8JDo~F_17^mMpxaRIW4d z<-H{jg~BhWvr^e2+V#W!?x}xa#%+9?ldjKu+UfKp7_aIH z^{tx6MbAO{9LD4@c_jY;qsrsCyAxYY#nMj34X^E__r3}7ohOSnB&GPpulTXO50vv; zQEs?r`%wkT{d9e8;4|_cXXzIYfSZ;|6ml6ZO5&rw5G;^Y79xRA2T*NUsqoKUG@@2mm;pUzBj34u;fBn#T@~|yM_HjIi52S zobBId;T&wH(C#}wo6Et626IaRNN^z`#e^t-hW4tvG6^jwTV9zeJAZk zSG&1x6$#GyaXc3k?gLIZnQ-{@_p(^^vJH~2QTA9+S^Y@~u<$j&bCv4G@BaWNZNu?c zf8Ce<^R&>F&ZX}DqQ^)=Rb?Zuh^kd4IwY{jJuT!dM})FMQS}r45z}q2eU&)>08?8t8`-1(00yP+FhWa8ZE$Wt(Dbe5Us0AacEl)OQ{hvL*{lLzjFtQ# z4Lp7HcwW;KaZC#KHGicy6#RDLDQ>%L7a|J6Pw?zKtxZdB=)irmz#Va1ut-U_{Cg{3 ze8K&My++POCqR?$wKTUD0ezQIv55DEwK&@ksR`>dPm__eq4&`}*!@KnGRTR5=DHE& zZ$n!V7?z}jK3U+SLD1BYwi)G#8vg(lWc$q$w*<_7SOq@G-KnrWDf;e06RqSP>m( z8Y!JG&{WlgT|!Han;#QxD9Y~;7F07VkBE_|rX#l4o1u*ZKjHjnzj6wynXzFOAocRK zF&%*RQ`Bm>=S>gYOhV)(4OU@sh_>B4>K^eaQGvS9Q)_%zqE69y(l!krIyHMD*3iTp zX!wu(LW;IP`zD)P>9qaSKY5w$2zqUK{@TxU-?m|GH-Cq(?5tLrb%~brI^Wq$M%;j! z=rUVZ)O7unRPumVAIxiDCLld(S_MAl;p4egx2Ia{jQjfvl`GKgKKkfxZ7zjW zFm8pBd+SNipH>$mu;C*^rC4NCadAp|jSi|tg_5$O0nk?EV@PAi9gV9*#VI_Q;>Q&^ zOCf9=p?kWET7e{(ZxMe}*+C6b9wkJPe;ON#9Yf@(9)`qt&|DfYblT5fzO|ETEL97# zu63&Eexds3j)ui{KM=M0c+*nuCpYY-oM_g!G`<7e=fJ{ z*0Tok&AmhA_|k(pW3;(((%#zCq>>8(vj{kK0=Oz!&+Wl!`J#psaEUhYgA3u*m#w0@U^KyiiN|z z8)#Mqz_kR>r>X3$FjBT`-L#TP+BBtD$Vxanwdi_M_RS1duybG5L@pnZ?FKQ9W6YrN z)^R+aie)(dTPG>Fk&%$ZX5-`#->CMV8e334%IW?aUd2|;(!9K(D7F1lMIXv6rpQOn zr;L;S)OE)bV;hEe=I>1l$6CaRc-ibCU+-*fJB`&kMHwx-2&ACoLXIkk( z%*CV9IKE1&s!k~tPt?24@Q%%L-d)7;N&bC+Oj!}6wdteKWfGeYI-&shP=ZOlOd_DTe`kH@}b~7i$@G}6-XPsg{A;&c8N=!wr>v0M;BoHn3 z*S~xZ;oon%=OppyeD(Z^^5<@svlo{8pC)@#70f3s;u$AeOOxZ~agH>HKvM*wb4m$X zNU=e+lsrZ2**;S@_7lhbFa5`jd@c!Z$3Cq6@It_GZX3h+{%zhpiip2X8?vP{5;S&M z-qJG}NcN z3QgKn#lGS^6;tItS6_#>s|5)CeP8u|^1}QB=zMZ6es;;p{wGf)dQnRZM?_{&grT_Y zB`vmujRI1X000A^0=UO3Y?qXtPM5;EmJ-4s^FrH`>;|m$tYdIHTdV_AX!xBMO zj0M7sOblx{3ssSVe4Hq^Pw|oWsll(ufz6I z_NcE>h_=}Ey%n#GSNT67_t6dztx`vgGhmRtLkQkj>i3{pgGN~hwQXv?U4lSx*Hu+F zi}EaU&~VZG+SP}O5OQOXt-cqbSTcvLo}=4HXDls)mj3`Tr5H%}9<;w24C9m+vmUhJ z1Rnv^Qj&r}!c94S-wgxXGqCNaEH7_$25YFh2(+VcLD$BID8$E>Ic39Z6Rq_%2}cB6 za>prICc~vK;IL{@2qD{eb?ZQ~u{l6;9qFwnnE`MdrP2u2^|vMhRgg~PcQRLQF>!K{ zyLX@N3vQKs!x#vxOL{s`o!gvk*P2z2hR~|E(bJ9Z4oW}No~InHcyduHE*15u+n!xL zSb@j0Tw^SNZ;8xAPC5Ih^CxTT9}x1rHL>x>1mBWqxPJq>ODahD)!a_#L38?)@Gj(W zTmzh@bS!f)5yqQ+rsh82g(N90`l?HNf=$Q8Z7aNw8C19x7V+06S;o$-cdswUuk}9F z;k)*yu#=}f+m1mRRyHE!$i@gsd21+atTxh^Qqtm-MSusbd2Xl2X?Cc&q>nh;_?PC( znkN0F?haqMUAyCa-;i)DpKJ2@_Ll6v37PqS=1?}erqYMLRc<433EiTOhLHItm0G(F z70pg7)pln&x1;S^;GRB!-^R`I2N?+gs+7j8+t?@q^Yf}I2A<#e(4Q&nvW&Vlfey4O~eSH71;C|GY$KRs*UF{w zAQk+^BsS){us?_CI_YE!`P>~V(D?rVlUHWcsr8OO#NlPa&1$Rv0Lt56+)I%5&@FF2 zZFd^G9ST`jk=L*ODs^qJ9?dz|o_#=0k>hebx_j%Q)a6q`)1-Kr8g-|t+{{SK*+}_jh z?(XAbsLFE{m1fRi`BViZ6Btm`6s!Yr<_?CE{!soR_~*{`m6T7}xpj?@c#?nD{tu_r zz4(XExA+UNUm$#f z&*X38`#d9_Lry*A+G&5p_g*uP_;<=`@>2UfsehNeTP-YKd;4YHE)$)`LC!Y^BJ3x| zM*}TiD=D&CP$O%$y^fk!U90@-@&5pqaMsls_+1tGp)Qh7>4(?3#qIw981r8q9dLe( zKcwpsSM-^fgt;ND`TWZ-xuw!p6R+HB=%3=AHWu(VS2=hoN^ASuJ_`Q;$Sb72MO>23 zBNzS0M{vwHYWo%2>7BFU*ul&uW*wO1{9I;G^SK!k)S}^eEsdvj?4qjR>*x5Nk7&L- zN)KYkm4(+o)Trd)%JX~ccK7VUIO_UiA5X-A;=5_xy!1iGWf&ci?VBn807b?yVZWYO zC!>l&Qik;E1OOwYw3^d{`0Q0<(w9j{o11T_J8Z2>0i-y z{{VS*aDLf#`?m4IFgTXnoU51fGC$NzaYWpyPP@5RC)-}5@sEY@{4G8j)vqu5pH}|> zt7o6{{{SiExk={PH6Nnrzqp>SE>)+1>0LFu?VS90`~Y)#r7;Edr2haBNhv~n)U5|k zeN%E9fA;cPaolotFQNfNTX7)8j*l&?l>Q*@Y4wHL^bW>fK>11+9op|Z5zJX@JUkb)6bQ9Lptnc~_ z#<4Q?(V@Mle&HovL`%b{H4EjCs8y=wO zL8`lL>ffQ%{5y@Y@)52}9!dWIb{S$vb=hzt%vk>b-8S2U{{R|7zqW;3n@zHk*hUt( zFGkSK!?i=kqg{hg>V$7bjIt90a&J?}WQNW=)8eI@4GPAKb3>(MUr~s|sL{eN`TROs zqS(X`IpreOrCr?^fZDLAf4cc3!F^|Y- zGZH0ak^Hu6@zIZ1+YqHt*>NfO1&Gk!rFviUZ^HKWIau8pC0~kN((8qv@i@PLe1@O3 zbwxa;eQ=%i1I98@n9cC+C&{_b9G@sdiIB!|`L-(yEx6^T`U(x8jY&?UQF`-gJ}jXJ z=VVh}nLB66dE@fG?k<0bp$iEhI0AxBgj`<4coAIHixRP+Cugs(B+QWN6{(TO;j_OUF^Rn!p?l zR-!H~)K;Wm-Nk8dwwA6>BO(qe!uG$NS;k08QOL}$#EV|12&{o@YF!%mnuL2BpK&E9 ztEjcBctl&s$;U0J>)^`?CCjU)|ycOWOHoiJd{ zUzE|SeFfTh8ZW|_tcwK6ZMBk>Bwad#(D_plj!kC+XBls2r)#Ak=yeADZVypYhkoL6 zS?F@UL+Nt?iRLqVq1jGG=Vm*DJqj7b#bGkI#%+3(oSq>?EiGG!-18;3ZKnHaU6#Mf zl<8D$L0JXr`>*Kx{{UxWpN|13+2=%@CG_+HLFKXX+{2AxJF(j{#5uM)Lybp*!>!Nb zT%g#09~Ut}9+RXC{JfN*dJvt!ZZun89o9?BQh%0So~Y=wxtpTYZ;w^#OMCa;f2J-c zfy8F}+lyDO3tZHqR@R=2$#jHn2&8e$u5aiuS9&__ZvubA)nhTJCMW>nL|O!`At zqPmFy#;5$Xx>|^|!Gs|<>TyASBS!~E_82scb zC6SilQWIvF+^b)}Bm!+r{BM@3cFuOcAIkClanXNkBgpX2+l(T;R!-}}K^PofI|mT# zg@}&Kc&JT+Lu|Gi76Yb&xUH$VSmgaqiklQ*qmIOoEZA7-S&}n-O}F&+b*(HS0(FKS ztT6RrFiX<^02MjtW<*zQsm#-C}_4Axw~m2-BDIF%{ZWIthaRl*ayRKWb9Kdy&YITN>LdGm8Y>V%P$G@@K>o<`aYMyD z1#NA0Gwm0sm&gy@>Emjgw^%o-hRcSNrM=a-ZBHO&L`Fset@&?nwyYM=i`E;MNB1gU zcK)Wm)IrDu+u)*m;6BV|sgMZm7}WI5k~xL7}%m#oki(C6dDs^ z$X46mrv`V-6 zf&!{~J~E<1wajGDoKTuT0L{;QZ!?Fyy#1eBd_ zZ&06Ms6L~sk$7`i##eBP zR0zGmHBl?)p2fr{kBIpSu)Vt4^woAbcL8l`J%=;`H3}ofhhTImbLT@d0{;NS{{V|r z1`y@F+E|b;LP#QtW48Q$$`b@XW5eyBUmT3WSq5_+1HpTf$KT@}#NoK!bH`*UE;)kA z#A;+>;y)_(ItU6L27{~2ZEDZXsW{1cpqJ>;dc~2-9&nBu+pf^@MPWE+8pHO}hU1Hm zxsHO<7|eSLaNTYYHl!6>QhL>X?#}NebDXm|zh3NrT3WhLNn$ZU>?UKi{oBQ7c+?(A zhVCW~ba$fi3`9B6XSU>L&{=i2Qo__iR1J;kJGkxsK5EAo9DW0p@;ZB~!ZK1mJ9pdq zK|KxrOUQd8-FVP=!$^KyUfuFY(qDEu8f8wH@v;$|xj+f{Jyz6w3a!)MekuIT=NQ*) zKQ~UR{y+L)@lTik01-p;TU4>0xM#)g*>Qekj#=8ST_cBL@_2mqawy^0{y{Lo!sRAJ zl!B(FJshb?j+KGF!(M{-uWIIbO?hj-EV$$IVP)gj3#Na-@ZKlm`w-d0aHOA1ye#^4 z!^ihC_5sJfp1Ch|BVoB8)t^0UyGsG75IZ%5VeCI`aqk3@fUIw<`ucuY$lk{z$VsM`ao2oW#%gy>PPs8i|=TS_-W804w zGGZed%rvE_EHZ?$(YFlycr395MwhFd^u7g^!)KOL?F2dU_s5cI$5oLa&d*u@!+}H8*_;gNT+gO=t zlY4ED#^o4XhOw=>`)WkRNf&BEZC$rRWL((l2(L-wIL~RZE|XzU zlyV9}`;P4ngRLh%;BnA=mjD3yiqc40P!tXVO}_dJ`Vep(B$eAz0i-;hdH|BaP+H}53C@(S;ir6skC)jN==5gtDZMbdksU)Ed5$b z``Ie_jr{o2EI-8fbKE}T_9KpeQqIPAC%1fwwRUTdxd}XnFY@zuGa+y##d~~t4To-m z;sN+>KFaoOs$L-VHB;}rkade8@;WNtZ8u)PDO5dbSs&d_ z*S42&Y3q^hN5`ge8MvH`UPm_s?i18gZADrGPx^oY9YcvwP!`g;epk-xQ;eK(^d94P ztJaH*pr)nuC|=M{uPc z09!&FF0`WzW*tAZ^)DWE)#m16F)i@{cJv zuCnQ$@jR!J;_qBlRy-=?<&UpUjJlbavtUhoc9z>`17@;A;Q8~bMQarHhwR8&Y3eU% zQp!^wB0(NB>f2({q}@{jZcjnhhq1{hEWX56zDhT{9NrEtM+;1UP3f-X7(!5>3HXlE zMR{k>zFVm~==PFxQb(oy8{>A}UG7;?P-UNj&Ckn2lIzIyl7$qvach7&{q@%HKPKDS zgp}4WaeSW}RTd>4q-V1e^KzncHRK`)QJBllZW=}YG$q?^vGc6{BD-q3?DslH+?E&i zveWqnnia56d4Ak~++VH8Rgbd1h11{Z(DM1xhlqaNYX1OJd|FiwQ!2+@DcX5QkTRfM zGKl?Ebq-OlP)>o{a{O=Zt37UPUV7r_ljLgNo?Hy62sbOHwb9edbQGSr{ka5@u9a&? zB%A0fI!D3KP)D;vS5w~drdj$5I{JXylc`DYHRc+e&rE#_IZ!$EQ*R$-IJt%D3)^B3 zhGZbN<6&u5iU!16#(}%Lud!2wu~JX!7N;mYrcwN`@;(^q^4uYVlxc!p$#Jd2<7{f8 z&H5|Z(AUi0EZ5%PHa_QxZc+aLHGlFwCxnitArEVB^0(1X{$s8Q-p&!+Pf`SV$%mX^ zmhv2WRhpF2^tx2q^*w6y;or|3cP`yG;uiLBmw)bJ-zNSwj*`A5pO9aLKy`+VxB`3wI59)35=zrKxXyP~t@9-rTX zqxdiLKR3j<)TifLYx2+YCD+sFenWZGRKw*wgC5aR(-$K-gs6~KM0rbVaX#I(;2c(q zjY<#G{s-uMwv~}m`lMZbuKK3#=V>?|W43%pJTaVGH7Uum*lo0j{$bQ53bVSMq$Fu> zwC3KvnGTPDtn?r;Uq=GI1wO9OFi#Sy;YCcW8w)?Y?{!sobtK@qTvNk%fv4z|E;QL|P zUiIO-_1gRgb9nSeAmelXxDehcFEZqJl)})xgo}kF{gvC(%ujM2bLh{Jxxb1&Cw^Ma zUS2-GQevlwwdu;F!A7yVojZ=U_EFWLqYi*s?|j6@SiRe8_>Os8Xg?aIy(sJ-uApPD>P6@_SUp`sHNHIPZo!DuAYJxpO3uKl zv4%<4gc%<$>1pzl=&r$AR7YDMD(CrbL)ovO-jpNmuO*f0bMI?R3|?cJH7lMTS)IsF zOSuI&mKrRO%21Rcwm<*?J@v@(e55RsZdrLfL*^RSSw%8by6aKG_5T18?|JcHLAX6@ zpq|%Eo_SqBw?ZEa_Hy@}9Yprozj6kY~l zrI{2bdH9qZ7iD=|-apLSVcfu6`B?Xq+pMljt_7{27Hlr9E!Ol(oMh5ZKY`r*ADXt_ zVllFe_~j?~U^&?j_v(1`i5$bYO!pCq-iw%-Oyk;uA~op?wfq&VIj`zE)UM=XDLAFr zuKArBk=eyn`)@DTIs8j$=RO{lTCB@Cy5ZDm>!zB>$F#R-~_SXH;nCp$zE8A)mrqT?k z5LAowHItq~s)oywd@30Y8bsG1dDC49#*w*@1@);Y^$e*Ka!Pd8wHT6xENZz!QF@=a zh9f}1QhMC=@TRgfsT#cRO=$6!Mk}nPdzCYh#pM08nTeeAUM^*18RrbK9KNSpToo!M z%2K5yddUE&biH((U7Fu-KXdhZ4r|8MG?!zMeziEqX{-_+&%=pROcL@8ws4s zvLw45)izsYESL9eR^?qgY-_0gHsv;B%EA@Nq~E8gKbGr8dUJ0LBz(^Pj(ybP9kp+} zcs^szv3LR8Jl+|M1}qYKw8MJ#qbV zdAISF-o0A#grt<4{`2%mw)Q1yN~}pBfuPir;q9-O<9w6q`kuQiHTVPeI7uBq=~fZg z$QaUVtsmYu?K9nJn(Q?wc%HQtT0TAFN( zhK?wUTTAt(ksky#TD1eqt!+uL(Q7v#eYJ{DBrt}KEZ+YBby-KTT`uAZRkbwH5sKXS zh_<)uNXB27X9HIjEo0%-)w6v7H;#;EwQeTgV5KTjY(CC0&}v9gRkgJ=rpo* z_`eFX_Z9>dZLzL3|W3Q9$R>*cLKIe09wYG3Cfe1iqYpT*=8 z$)YAk8`DzeZc3Dt1P@bx!Y-1VTqtTO9UE=4VMY%bEOuiU(%hDCQ(RNLotW<5Y&rWq zz0X4LR~F-Hla1u?32QAKa#~~5F0f!jmQ$(5M(ZX1RoA=DKi>ZU`r!C$Tj2W}oUE&Y!f*SJ)Th(O^z!4J z3A|q``i15%ce9;BW->7x7klyeFC7v7BsU8eyrK1=+xV}8sJc>~mGc+LzmX-km{Ao7 z=d1q!GybpX_$)8#`kzhwci|Oj#d-c_k>BPVF*TVCMGZLHev>iTKtpLjBEw<;01pdS zpGtOF_??`6s@KrYK)%^R913Wv`uUG_2B#=IGg4klK7&T@W2IRngi~XtfHyuPr9;{> za>%u-UeOGgwQJimCMZ}oda)QFweGf-wK?c*Asq_r zOUZ*8Eob+&^6N&8SvDdTcLH=3GBu|clSh@H`2PS3WeV6eJx`CerG@}O)f?>|6v`qa zuZ`P?hbZK3nBZA1?zM)VRlb2I4yba4ZML2?xiQ-- zxV5}ZWw;Gfco%KqU@fneQS3qa4pP!s9ic%$dHGa&jYAk0O6_59wz4tmPa}Zv^89J( zLm(q88zp1T*Q~Lx5(AG@*+35&WLzw67S`Q<+6~cZ2lC4lLcjo9rBk%>Jr~ef3;3_) z;Zufk9b?2*E?q!vx3-vKN#TyYZ}9#!#sYhWeNXVxp>iv@TGs&hQ0-roW}f27a@Gf+ z@$sZ9?l$J|c)XM0Y9)wHR-2Cs8wnock5t?8(!M2&T?G)Meb)OZ(s&f{c^#qAu9X=> zA-%!NPJ?|YEgM#KAE8@srSC%>OmK%a(@*fw?Hz}4!W`F4J(Pig7SxRuYx`2Dfcpz3)YrPmLKPjxE8z;cH^{{Rs|urgzs88K9TVM>f4#@}s6A$_sx6}MYn z^`-!P)8J{>+eq0K#?ubNb*FUex2UCMRIo8jkB7h33%Oag`A2AbM;3D8@nbi%)Tjk?AR6yKlKAM~$H`+jM<4uS z&D}=)R(dvm!F$D+<6XV&cXN4eQRa(WZ;$2VsSh}WC0>jt4X&>f&{xXe4dbJ3>|(yB z)W0_0_H-pb!2CnzyWjre?YC-sX08mpOrG9Imn~d?%W1?(`S%qgU~MVfl_5$1>PDJ% zud!dJ3~n^#@>q#q z?Tl7tF4mqZlBF~zgr&1!(yUdXDL1#yS|7r&vzU{!tvSz5kN!CS02AnLYx$K?#HCI> z%wsRF_+@`~QOdj<`m}eZ26MU{&gHzFj_1whoMtl^&cb|$^6W+nKM}7*bD>K}amLxb zl%x{9>Q{gQy+e}zB;#%N;G^T?-LCJq_H_RMiRD@!%bT%`=Q%f((S1Ah`t|;}>Ct!1 z72A*N5A`YA_VTFYoM#8zs5z%xmHytyu5@K$UOpKP&ZZ*gvTrNiUZ|;N8vbj~~tDUgCa&8yS8( zIxC5C6Ec#txTb+l!les>6Sx3DK7GmjLOf>uYy7i#&wubeSK~i2T&)d0lTWwQ`^UhL z;yZi5((^@^Tf*YP+dWc5CWXGr@jgP_>`(syy#D~+dM>K}0J9(HiBn7Vb){sULqU%a zboUyJ&4gJuw!KER2MnN!NEn}wCpNm>b5D685@(q%mb*jrE{DPs|Yc~X& z(#+lh2sgIA^GVJgcw)WQaTL}*k?@OEc8i5vv$u|Pt6d1msWQE(dr0Pe73BI7F zE)P#CS1b_eJ4vwnXcpR#yU6XEl?^`{PP-JPkEjbfA3B--qLSqCZtiyiAJWe{J-qD~ zC7Z?a{GATr7VNojWQdD-m`IoN9*J6k3MjRRHSC;+@dQ$!#a zSgs|Ng1l}>w5|sn{Oa2h)t(A3w5`RGYk3&Squ*n@wFLQ`9-Bz?Y9S& zV%Qg)2hF}OYl<(rg=bLlr_Q?5e(D#UL$=-lL&}u%c^9312Z8czFR1BOaK9qXq0(RW zdx)hS;2lQoyL;(DjoBkQ#iY+-1UWegEA-<-Lfnw&f>43DpRiLg%$^WBb!Bae5$`93 z{YtVQ)c0qbVfD++xYI4B{-?^lVxE`2-x{RbgyUve*zA7|bJAYW1L)d;*{AtVbd_JzajA1(C ziDmx)adzN7s(TvOH_qnVW>z~TpN|!ZQ`Z#gn5A1ntS_NarfK9UhU2wI8B5dXaQv_E zC&_-ox|58f2HHvePO9zqa`-oMSjb4>xyJIx5~L?2!a+lfmj%EcBl(lb z)%-j7$DXCiHvU*nEk3{8HH-Cc3AamWz$mu8!2m5Fk1A_V=eE-({Bx0;{{Zp!*Btwc zKhSZ9`j(K?Y4KuV6B#ZoZZ_$2qL=Y%J^ocbGx-Mkp(v>1EDs_4DF-;pR%Zz;VC$aQ z^4yms;#lmqS%Ds8oy37~6o71#W9MH(d^3}}-9MUIKS-?qEm zJ+^W()f}c)>)iCKimwAIFgQ$&I@2#}vd9-mt8xe?=9;H##DwV_B2J@Pv8Q{e8Bh>z zQP2bKu9H`IQgj4ak<(pj{{UBasFjq4TaZ);_I}#ywD-K=(Oo)lN7W;1?x33xdV39c z#_rm$a#4R$+BIe=jy-#$^Hq&|acig_Yp9B(ZNJ4!&ut*Frz9nBxBUH*KwD0HD@VTd z=KA|S9G_mP`W+Lc{iotqeb)MU;XT1{=Gk^f6Xie;LzLqj`FTM@^6s$OyMKrpsQya+ z4B+|o@BV6UU8e`){{X=3z6t!V$#F6EUM{EpiL9^v3T|`{tFG7LUQP>=VY|o1;k3V2 zCR5bL<&eq}bwlk1wz3C-NWYzVp0@$>o)(u>HY2mE{{Y%pnqSp)c>aq&U$1rdzX|4c zV&_ctEp2{>25fr0y@~L)ltRN{j~jA z_MP>{q@uedutQfMB$8Khdh1sH+bi9OX8f;%{N9xpSKu1E57M{v+i@f4*NI44u0-t8 z)O;)_uKxhChsOT^{WF*1{{Y^6Yy6>?@)<9|9jYA;Ar7}?HnfET4Z#Fc>GaAu4-7mr z3R;p0HmPOclwz3}i1>9cUa_2)!VJMd>+M!&gW#$3gI= zB{A4QV5drtO(|K}+Z&B|8lA@aP^9_?STq;he0`NycIzbj9LL?Csm>$WoTBj@*B3f7 za*@6UMKPA_cgN*WN|Fe;@~t4EmProc{81 zUQ?FIzlGwnTVwfgOh>mWQcuK65|W|!Qk;Jj;bZgsu|4;l_+Kv`3cYG^J7dxH^g9m# zR6(o0iG4|StDf`6IFAR<%FDR)6D10EGt9D*!3kPYr;?q1A*FPdUtBstx6ttpOZeZE z($H7z!A1FFqt8!5+mn$uBpXum4X>XPbO3x$=& zW%A6LtO*aBh>NgExy5--kFxY78jTHX?w@(ULbJDqU$))N`e(_pFFo9T zYk*OWii;4uh#xe6fa}gEZ9yd@q;)pZz5Do&;5&BgLv6npMdz=n!~DFZDXm|#MkG?DO;yu!S_u1M_u9xx15Gm8ADyCUWdYkT-bGT5L{BL zT#-#JfKDliDWx4OMf!PDwd@%3gjR(n{#7r0CI-a{2KV@UYij(mekmHM8vs}3AAL0& zfkk-@+(EDhq45+&YV;uFkei7n*6{00ErwTDDj3O5Cdc;Eu6TU~+8Hgx1E{e-s*qI4%RYop(O zx>94u%udIM<{ueM@}xpD)bi4;@)VL#lqCEhl1700UlaU0y|wY%Ic%j}exF{cbpDxH zJd>0DRMghy;m2XSC+fQ|Ucc18d)eMD57@3fBPGo_UN-}np3KDYEJV3YGX73nb{6Pq zhRM3rcGny5{{V>Za#p7ZHx&A_srgsQb$I$4oRXG1A2@hN^%3n~3c}*^IULXXi$CAI zpB`_w9jI;RRW~1z$}NbQ2H#t%SY)Num6R222H_<1uVUu^0EzZ?6O>jX1oHm?r>W)K zFY+$EcO~WJNm#D9kQExnk{bc%U9Z$Iro0sK|L&``=Y%sh=Qzo${BGROk`btR`BU53mdOtDo z%|*euc;%?{JpTZL`6klmTTk-2bw6Iwc^(>Lh+Le5aaeh3E10EOrkY4AQ;R9Rl9Yl? ze3_}j&dZf)b!_^(ca*BC9g_4}KQF&rztso2Jf`=El=h&8V-T z*B{aQ>hHWg-p}_(vEAY3bGwrtjbb744&QQ2nwozj4YkT#IdI*O+EQI=`xf5tRFYMx z76Z=z0Ga+NsjXFe6v08Z(@XHjU-3N;_+#bUTT-%{o0pCnZ}HDg--qJ*pL34ukd)kb zn)&(2_up`E53-}~ne7RBr^>V4Gukogk17!l7B}9XI%v$8^}W=qMEnF)yYKef#wT2Q2>F`&-{JggFlU(E$8YcO6{=yAQkFy%{jFivkL?h^#JqgYwGRSDi(Z<1 zK4z7&0~s2r&b|ZAr|p;`w>KVwXN;eqltYD2zrOe zee}d4&_8rV*Q}xN-%mOw0SOm@2jQ&2m>QFI)Ovs0MyU!h4SZ>h;RnncS{j|rlBE{)Zed-7r`2M8aV0jKl817WMr~8Q|sDk*mf10=rJ-4 z#<#U&hFAf+kDoe}E9ypFgnfRuHxy~tghHv|n)<0G!iSP}S=2zj2;1Z1rAxts^5``6 zkDuXL;KBI_m(Y3(R(LZ^sgcDb?g!>uuJv2x(Bf61-|bl+{As_&i|z`mKQO=hW&Z&F zGL!Ziugh$)ci`Q=HzV@=!YImFLQ7-Cj}ylv5&@Qxe;u^FDft#M*@uad#-#y&nr&ZU z6wD$c9q5ZA0U=tMDAV6Tz~r{z#edt*fAOUK#*11Z_i!@a4bO}HN`B))@d@|P93uqg z;b{r{Ygi}uDfS!;G^>lek9|i23u-2RlfVA}#uN8e{0RN@4U>+2Bw6&IKD8faKSEXR zuc*KB$shb_KaB?n$GXgnWB&mBX+LqO_=b+)Nf zz9yF51wKh%`oE0|$c2XXeFrGVlXL40DFp_@N0Er-yn3zycnkaMJQ!$iMZ6A%KElFE zZS53EQ;_i6NgI5F-~3@V_fRJcf?It}(T)dCnCtAMon1>GyiE%u8E_BG`~Lvq)Id?# z$5CO40>kFN3WTGCF;?V!jlY%-zc4+OjpxwR_bY5sIN$&txcq5HpP)s2KB9?+QK!r# z`Dkcf3~h2S!Lnlzf6Phy=#r-*>yv=D%KrfKRFA%v_Lz9@KvgZM8~W+|XnxxWxd@K` z0E~bX9zmmhwtAC%gmQR1`2PT^*gj_TkJ`MB*S`V7avs!@(ECsKP`=HDe7r@IEz!|! zYwx0cgEjkv2d(5@LP-7*9(OmjBJ|kW&`46_}YE1Cis#dF3|qj<=9P*XbozNe!tWpBna` z;w_~JPQS%7#=n;}+~opobs<60PhX49*RK}%9*(_x>kw-{Q`^3C$n0CkwB(P@L#XtS($GqsNxrr8 zg*W8+nWvJ;XOMEK-L%DV`8|_b0@-`={n-JqRhurQyPR8US zyN{6C+i(=EDGvuWkZw{uZCszuxXEqkN-_O->G*Ze{-4@= z{!fSJSZ)pUnaMEt&LfOOR;F`o<^jyV%sjLP%ydU_=>D5bk^xz{I_ajqeeplyy;$kH zcT<$Q^iLn~XL<79@)sziV^r#f5+%H_H2EEVJN z&rVxGZmEdMb?;3Zjey>ISBrA|U4L;UR9s<~g!;UHUYY*m*La>eeOLWezu8VN+(^#q!${hC9cyFhZ!Ajc3awR=sfHt<$7$0_F_p2PI#@y9^5>+Gdt zz)aKU;gQohxCu}`s=d~s&*SsR&3yDS5pOpWU*n$&9>>S#Gai3mBlP9sq#H<1zS^ai z1!-q4a-0e`mx`lrT_VS=>wmD?BKf*<5N|0NcH1F7&1q@ycQL5*wV~1{GH?8J{q!%` z?FhM=$F0j?gpm4g@1e(luq3&traWdsFaC0L_SAo7n(je!5M$P+M?d`XpNmRfd^E8pKM%J~gH zK|w73ms4-mTK@n{OwaWdUW4|grsMd@0FOFSUlO}L$^QVTtH|jyH-GcWboS8y0Ma*o zO#c8-)ILPi>HK8h#9vBD{4;0jHoiemsP(zyL+dBqdRzS&we>gq{QpQ}wUYR5*U*dk4K0pSnTnfkbR>KH=^K6>x#)qea+TeVhakrz z^Sxu`Rln%%=K7*j!TJbf~i_~fRcY@O3#FD`XF-Uwcn{w{Yv-NcV&K_d%4}- z8--(-a&F$?WaRN&ZS^|aGM668DO#EZKyA6xTDtg;@#@^)jXN*G5_r$hQSvoPNk!!O z355Q+QX*rPIakxeax;5Jrsgw{DNu^+3BS!nLzeQ=yIr6Bc!Re!+Wd3VJsY=(IwuZOp1I0Hq~F8;+--@-AceyI*#kJ2L$}PM4D8 zr?8Y|n5F(F)-R|30P5}A4)k|JiFThS+N|Fh&2yPbg@eekCN4rdQbS632_Zh3kdl-e z5D&Jz*OdMjhP^I%>{CjGC;1=upHojJ#-`h2yrZw~i+(l0{{W$%)M0VMJfP-@ta9{3C0H1MU?c^vwRE(&B9&qd5qRg&X*3tyOICvV#?u7ihop zsGs8eDiYF`0e;zGxATTQxAM}t=~Xny{@pVf8Ikiitd1>8*qQ9O%GK;~1wY$N*S4V} zdV2HLzBaG&wEqCO__5qqd7p2yISw(Ogr+7$Q;BK4wr%*~M`Nv@6saJ4j+ErJovW$$ zKLgI|Zfi!`+IdB1itdzLR)!l-FLva(O_nY#x1}wA5c{I?dl8brs=JqtpO9tK0SH@; zB?@xnn=L?SEw5S}xTh7@)M7l0YN@$Ci62gWoO4{mhx29dWyX)iroO{7a?QTlR;jEk zT^3SE*57qC!dtVgv*7wBe8K#^smAhJKX$V|tt@&q(VtPEen;BmQhq~iJT?qY* zY=>UwQg)~67a@Q!@cC8?_@9LV(W079orhZV50Cur%^4`!)*tRXE9bw&6z%z4Q^lX} zJM)Zl3{NM=HI&3mOvsBneMI;ubqCrj=q?|gt7f%0JjI_Rb36^&cRK7?k|k-E*1B4^ zbM+|RxIu(I=}yp3mtPvR)UMffOj6lKA%Ox>AC%WcM;Ws|(PeYmfbpzk^=$qXhP87? z#dqe{L2EavUZjF15>o6`oua)1hxiWdudhT@>~e5KiHsJ@6)Sb?(s!Uk;7BqO`AAp+NwBqfAIrauVz2iUv6A0cmVQ1h4Gt}& zr{jFz$E9+A=(t67kka0heGoQ81Z_X+6q>2q_|lL0$0^%+>{pvgGf~n_?yj}tRl&oQ z!+ARQJnJ%1$Yw4aSe6&{7L?!LUWM{~*=x}s#H8Xi)s{=}Lk>z$`#(Cizd3~Y2G=^W zqg?MTeV?|?r|31prTdL({{Ys%-hLzbGs}&e$keoMPsi)H*90wq+u=Oq%1a=65Lg`dBgUO^CI=X(XR2UoD~P z_)U+XDb7)fPe<>65Dg8choQk<{RCIbV4xGVO9QC``{`*(;q(h1kJ`%DmUs7paK{qH zT3XR1!SJ#gQB}RbR}al{zS_b^X~2IIa=es$s!PRs(66t-}QdS#npx8-P;!V0z z(cyNcpO2!+OLxIJIAzKhiuKAl-|KgQ@J?mh9#7AVRGwMH8eqrBg%mhhFQp}=B--Ha zIuX{Yz9YT0`;UiJ72NCo55Kvn)e`>z+mdZB$NHZNXQ`HyRKJA)-6PA{UqDfoj>6ZO z0i_sgOO6Lpf&uu+09x1aqr=KC0Wyx4`V;P_4&WX6#v_t(OouXeFw)xNY-1TRq{Eqe znFWABS^^2VAD68|oY0)<^InHF@@i+XPq$RkZ2cHxJB!9M_?9!6*`Bs3^0c=q+f%u< z7S;{?QlLTh*Ob9I%c12m_|DCCcz2M_PdCG*zf}q^qrjrX9WU0Or-_{9gg;lt_C0zL zIUUi#w{lx}p9Jl}9T;bDq(?6zcdC@!W&gg7q4&ytwgTF=|lr^#}J;asE7Sy8$l<`i;kUp=lw`J}`H)%%`)VKjf3xTp?_rMPNuB=f#1tvn zuSts?kXIr~m<0$zM&eDCc~F z9z!9KauoUOw%*jrb|<2uge|~!jn~_2jceYxul#k_o~pifVXo(y?Ee5M_-uPnj!%u> zQQuJfgSoxQna<^x;q$E2c2Ub^y4YzuklK9~7;S0XhL}m(kgZQssjqwZuf!|X)Kx|1 z3$f>aA^9}yy~(ULbDR#~^K5L%v+>-jZZ2fz%y~H&kFGR@Fst1jqgGHKZFZlsQ!4yg zIm$VUYoC@+qJ6Rct^WW}ouuJBJGZ#5HOILJ9^m-cn7mAhtxFReVk1GMt>;kT-r)l3 zHq_UNa=-ZZ$M0-P*-6F8=LvK>`ahMnX@ZUuCVx#|)XVCVxj$K*y2$Hum1o`UIhODg=hZ&QR~a*Q2Gu>)rK8O z__)Hs9RQMp{{Yz5pYY7m{Xfg7~_?9Z#;G?xFtx@bvv85q!$CfBuRL z1H`29=acSvAKgZO#P+XJKkC}W57gEH%OzZW}S^ogVcYRF%09e=b7`^o+hP{)3S@RyUFZ(F5{w=k7f`2Z8eF4t>NMY7~3Oqq`VH6h4+x!b);a{3xCzj*U&Vg3IA#s2`6 zIsX91Q~H|!09Sqns_(8fL|YiK=ln$f0Ch?~$F^g+IQ+U8>}mf1!+tGd>&LjCZuC;$ z{C{$NSb2QHvq9dB64W=vbp!W>jc#8Gwj$5u)wkSGcYlnz`Gc*K;z0h&PyHXVnPCr+ z(AQJPy1Z=Zz0>bNAGFhz{99lP=c_hnxtw^Bz4fIh{5G{le-hb!fG?0!o}`l>&#*IA zhN8!ll6B=IHx-6lam8zLe})xndDT|u34KW>ve{!tB~<$MN5?;|-=$xsPG9vs!2YSS z{{UK;9>ep=68P)6{@&%xe2|i?43R27rra3`kjtyu5a1+@x3Ipo^vA${dCNzGmm4i- zD)9RC>7JQ?g8u-2so)o{H>t-XylS5wUI(cN1W(QZ4Qu#@v3kuN;*)YF%ITJX85Q zmDt$iTb_D!{==F(aUTbk=DoOQxH*jc9w&j$W9CYg##?RvMA2?q91F(>?vo@c#h^j> zyeqHeT-6frOR8(H;*L|})2@3lQeLb48Ek!VPR2<3o$ls01DnoexZ4u(;}<8Asgs^l zPU7P|{=7#CY(2R~;UEAlTlkG$IT!QT*3ys(SD1DSv{A>Hh%2wK>;vj(c_g027Be zep`+s>SKw(;*Cm=ihfMZ*^*)v5wjp+u zp`MsO8h=s$08h#67bxWXM<3kIC(AJWHcQZPuH&+1RJjqDSMXJ_w5WQXnMhrgZTGc> zeTm2KJCc)K`kx$fynR(z;}()#539e?PqiGIk4fkM08!ZtmL_)_pJ1I7uY2-==e) z@ILan%_Jv!zDy7g>bMo*n!GJf)b`8ySwdZe2XI~j%Zk^rI{yIMQ~v->)%5}Xr+-65 zcM(A&{TpQX08LB%GhftE{Gt5@O70VYZZVJ*b97%yD)_t!{{X4d86M)h)WdB)%ai+Q zuk^G9ztx{qXnTUI-{nw0@m8P3V97(}uRVP{t+Di z0OGCx0Oq0p0Hv!V{;GdLCp(DXvH4d20P`Bp^sEg3098Mr&CcSuE&iRf{ipltOX9Hl zi+`$L&}zQpEhBMd0^JSGG5(u)!LRj0k5FQ}p0H2wQLneiP~q`2Pv%}ef(!0~jfrJF zHP-ao_@Y9~=1xzr-1kHceFTg6YeJ92(D(YpMc(NrZ}}Hz}DBrQK@{%`V9wl7NDW&q#prlaz7J@l)hgfn!Bi^ z{$wOols+efR==0F#_R67?PMi1yZE*Uub0Sg{{X_wkIWK)uPwB7@q`8Z#(si-{3*P? ztt8Vw=~&>B`E`xElD>1;E9>eg&Kj0y-+6Jz?VOn;`x{{X7Y{{ZlS%D*a8r?Aj})675mv5n6@ z3c6I-x0t4XU<3ZCX5*Z+dZZ)qy$a8cW3j6KP}%SI!$0BssDH2qUnOR$9m0O~Qhj=eA8Q+y#m^#U-iNgQYe&%U?& zH1H4oLWbYZqxzkH;?&3OGc)&v^%DMd0ZBWY)cAu+$A_7x&Om0o%pz_Mp8o*(Q;UX{ zK)!MeNaf%i3XZ-vsolfN9R=4l0}9OJlVBV=c;1zKOvaZn0U?*i+f|Z$P3u2unV-DC zMOl~$x5jn$)W2)6*Y7YHt1FKrpIGbd6|dSR`jD3~9E?6}6_l39@x64PZgQ%57DTHZ z?j-H~DHWcLFllAt%K7QX|p*CN4kq zKHX`MO(SO<*ap=lL|XdkOJKtR)oXNL%GH=K$$2U2oBj2-78l$zxnG*!50ykIpHtab zY*9~ocDi?8rD3CQRVuepZlcw%Zw4EO+rFcxmFV6G%!>#2AIob}in4K@4U+l_O^(t! z3fHo2TG6n1Dk8^0P*BLqu-HFj*oz9>qk<8T;7Dw3{VLXh?S-t_G6lugr=GPn5YUzx zxRluI)8A0CfRVL}CRjGG)6CE-sAt?g^Q!h5T6G4EjKIj0{8q3XO-tA^8yI5$06bN$ z?R=;*qXRRvRWlRtd|q`AY&2z)KR#2Um9!7aPmOQcBJM&?Puu|~^7OSOIvJ@?Cx%d5 zYee>m`=)+(ebf{|HLqwNlNz}_Y0)?L{{Y!i_D1S`4ONqI3bv$qYf+VbNlrp%D!g~o7EmeX=`83_%>j@sW?ZLp^uAOfWj2eykH3oUqx z$*FHwTKvxg+=p>^h8qg@dS4-h{Jo}D`){U81uwRS%a>^G_^t+)q(5lbb4w4a9hWb! z3cS>^eE#E~)rYqk4td0Qrc1UtOgA6pvk?q(XC_2(c=GD~Hr-J$`?z^nIc_25;zf2QJ}Tpd?Qtkl z0YcMr*Iu>el=0WX9W7RBV4neailxpI7a3czDjjbLSmjG{MJG zI96$Aamc`m*d>RJ$uBaViq~>&eL=4o`JcxA-~Q}-9>?(Bb?igI_ea$)T$PX#KpK>v zeQ*lI&g=6Vw=L7~gYFloG@BzSG=j}YkzuJ72*qTIn`74H$yM%7p6c63XEOSa5i!sO z)|DSqk&kY7Q?H}wd!Bzg2cesmW~U|`-tNRhv24qtCUP;{Y>afKRu z=tBN4Dz2Uo*;>a(cOM;kRHqQHv4-Aw>FOJvwJc}QSXjE? z0`FXpZ5r63HP*$Nbd?*vQ|-U^(4G+~N1#C~R+e`|)i@CaB`5=HzCHQFs4dKH}O!ay^DMrlJ1NB zG~+CNPsRTLFXOU!hcV>bR!z|V02rS;9`a74Dio#0*Z%+|duJPaw^}m%3JyUyT+;*q zk~#sq@U;>xiv;-QSr~ptfXFNo+&S=Gc_UI#)TAw6fHa>e2Q7JH=rP7gJJc@cc9$6{qI9@-RjQ$TS{{Zm9@z1QqQ|}*$5QI2)TD>E0k(Vcz*Mq1N>q?Guis30BOFI<-(VnTelyKa47q>CYJrTU;czMAL89vKh12P_kYOe-s=5b^PcfP zj9zmMjD!52)l3JYMI=otHmj3`Ze74GBJj6^^ z^B5^)60JIw8`2N*^gAW8So~8E)X5FFsXHpF z3Q2mT&!Iir`Z-p66rBG6Y`I2D`O_%8oDNcJvLee+PyIELN(u1r_E$m6e;-jedDAE5 z^lv#lOW>dR3q4ESJM}}`Z=|CfHT_qq^L|up&mYLh!{^{Tipt|0ElYyv+T%{7>Xl!p zJ_L0Y=3i+#WaA}sNJbsOq8P~(*(B%11Fnt2#THSN%*xE^Xg4g?ca%Ebr9U# zjWP0@B-8MdF;14{i#V$kkf24r$^_iLfb{t~_9K|~6^h7L<|kUxo_mqL%yuLUvaP#P z2`8qP)~Dr?qT3d7bXQ||ZU>QO5ceuRmM#e%q%qy7gqZk6xq5Jp}KNC?_J!mjA~KE81nic5~Q z_aC*EZCWI2R(jtelX!@woO@l#1X(Ly)3-rg=MlTLCzsI`8x*8SV!wM28ufk`lI*1V zl=spZO?UdTMRbf_qVM!Kf;Gq_t~6lcH5b;rTlr6Z-Fg(G=v{FXDbEbAC1+f@vivLN z{oj?}C()gS99kpta}pKN!Y^F*$33#vRIppq$3c1#{{Wv-W;XMR)du9(9cL!tu&kG% zZ1oeZq;W6Sr`DstPi}DtQMk%;{{YWDX9Cpkr|Fo$9dtJey$j`^HrU;iYOP}(Ph$Qc z{v+4q>ayFW;+IZ`&u&NkVmY*!Qeb0yZv?X;ml|3el}!%YU6M^*%ajU{md`+{{U`>jDI)m%C0spEv&y! z>W;wvpx;}))%s=c+dKocn0x~`zvNKnOqo9y^G|%lm)!?pX~sf8N(P4gYN333Hf=Vg zgpv^cGWcGnC&Nblrztr}{{WgxqImkBI&=R3)ywM%7~GL-kl|SLP)um&4uaO5Q)Id< zE$yqD{y+Z!VW#uY?qU34@#@}HPs{7%&(O~u$c{tCc=sVt307+#4qJ&919X_^byl~v zRmoqjp<_t$2HYm&^kq4dT$lL1u1>+3+bRkPMKV+d+PrX<&2_!^$k3++&|)|-6?B76dy83{_fLDHrBW55Tgc6$%mb~^U# zT1Y(zAkx#ON%j@8xvt`?OG*|BA7w4}()B>%Dmj#tMrSp_M!@lQ77hLh3s zG~jR{UcyGT&F}EOcPZns*XaKMB45Sv*3%#JukJcKxgC=2e{eX^XP9uf8RTKwam-M0 zgr!U^EfI7SZ6Mo9xY*-pvaYdD2=PCSeCw6c+r>7@Dsoxtob~*2eqQ^_CAd>nWRj}K2PhVu%9Wjhr_7$5Hmxmk&(s)SS}m(SLq_zdUH<@ID9dte1_pmG;8}p%vZG@hFTEYa4O@0P2vU#;xFIKAwNqaKEo(i; z9Ou-e{yhFn;dZp)uQ{k&9-Vq;`ZMOo8|*?kgg$GO&Ex0eu$&e$p5)WRPBi2P4fcnF zi5o_Tc9inu_90^MT@LG;x{C0lC7E{X+Z_;GKiDeOsevivK$*-TWYSBS{+2#b>^ z;#z$*C21-FE!ye$JZrh)<27oVN^9tFe4l_hQ5Vqo8ojGl-P?0Gr4{L)`C#ODKZ9y; z_IdlWwZHnFQe_K}{-wO~KLX=@xZ+$21`8X;pvH+Q=N@(UraK)Q7V%I@LVZJZC=gA_ z)YX2M@swj`T)KS@vaU~Fn_a7{-_%=xe^MUtIgN|LM{Y5>SyG-$SgD^gArB=^ELk=Q zAnJPgRr3Bgq7w5{{=&a+ffGECt2t!R)}N`rbU@2>B&jZM1azRzhmwA1Oypd#02(cI zqH1{>IW|V{ZgkXE@%RK$W{b!u6LY%#D=UTrxQ!ghIClYiQ5-Oc0v9>U6Rw^$x}cgN z5rflSa1z>x^P>2ma$t4IkZx46ZSSR=QLH!EsJr8v<(N*+_cNDp=ve&5XNKZrWF5t1 zWVscY3^Ii+ZHF2PP+F1>-8$9(0L1I2?m`ffcx<5g9^9(al#}X@iC;>5cfCJU-%y>U z?jIEGP8+@)cY-2LXSp4z%;f(7l`_P8lFAv7^K0JgYO{=j3Q0|rQ+>neK7Rf;T8V1i zWT7c6MaTWmw?=rU2VT^+e&#V!OS#GY(e7SDx*gBwyxx3ajFX(o&UJW{q##2|WG$pe zAX`>afY~HXOTkAuF6n03Ql*ugoCsY+$)7SC835@kD*b zj*t2~$gq#84glPgsX?~#cG@=-tK^nJHt0MrRQ~`Vhy9*~&&T@RlkQToZjg|N2sYN$ z=MshwQ;hl>H<5WkC)73Zw;kWWCvaPk%mnB=2yaXG${DM&o>tn9V5a6>IgVdj{6j zhNn2`{YOd+s7Af!Br@tZvikI`dqJzYh<`hG)coJ^YR_mFyw4oL7x?XZ&uGtip8j&9 z`str&++~vkD)={BzX1HY(EWmDKbyPID?!$W;K+N3Px-RX>etzNAv~V`cIY4}X4D5) zQc;+K^SUeepBI8}(wX-E002_Go2NMKzz!8DUzUUIB*Mbifyl_oSO`*-H*~SR6WT}z zxhBd}>xaA?_pyILedy(U7da!1^8Rsr?O6DK{w`@J$GF5XSU;})h>HY5he$P`Be znHEacO}-?9@RL_GcMBaQ!LpEU$Nd zpu2h6))oAlHJRlwaK*<|SX?P>ODlMibwb(mgeb1cQ^d8ji*>i1 zR(}ut+P-6mTPb~iz@PH>#U;mbp2~gkgQ>rzMtgvLKScVY?hn)_HjCRY=PhD*XCB4B zBW_`=kPl69Peg3`t)Lgt@hDqEdk(yx^A8=n$Gj9_j@$T)k=5fNa=oPF zvHi!+jIMQzKX`cq@iHlTFZ1=h?;&Ak>SZ|(SLRtDToOJso})`wrg7CxXsEUMv4hRI zE8w{YD8I{->Tfpsaz7L(%p=WkfZ-xcZ~xuQ1^Kv5P%%_*ZgIBEq_*3~`l+n;LU%D3xA9mMo=Q zn~(+i*RtwzlX(1?$3w&I;vk>uZ5{FbFMH$LPo~c8cQ+Z_j#m$h@E+x1vFLA>Om$J= zv6jS2d1*s|LqhX%tQGzX9}4q-<(?M)Qktbqb2vAZ^-mw}!Rmj-eC=Fiwno`v5=$S| zXZ`&b_AjB^pHRQm%ZqUC>hbOy*v=h{?)LzN9Si3?;py+iM41jRRxyiZ3rf8-07{lN zsYcfpBy*n}{whyZ%IP@S=)dM%{EzuX!*N$qPQO$#Y#0ikoN>mc`gu; z;N9g$oehf0(+49zX9*wXd#LJ}0gbBw$9WT>RwvzhG%5TD%Wtj$ z?&p@UR8?zr)|^_)(1f0{RRZ8)Kvr4^+&c0q%sB7h#jiwySg%NjE zr(I~@Xw3J&)cwa zxJ>dU#m7K8RF=CmtwS~PcJ8q}*V~Z$i3quxc|?y^^)Fyq>_|Xm-M>rq-PC%yQZjKh{AajE^dCvJU2f1Jo9{6#kKwM*Ij1)E<}3(GBFSlL?m)cuA* z$|46c{-_@nx|`D;$Sj^lWX!M@?+xxe$-X|Ug5dJt%@MzF=m-Yh{b`3+K%VI2WS>YM!?^wJ z1*jg{Gu&dy%mZ!83G=A?M*Y!$=5dYfBnk)HF|Ty;voO$AzE{GZ&7srqt*v%vx-8yO zE2v7^eFY2XO~*=;p&IuXO_|AAB`zaQnu-i6$ZU=bhn7ARr*P?GdX(eP++Ga>@1KEf zg!R{2u9N~frf!ApS=_LoeYDOD_Y7{i(cQM_J@v3c-lEfFhb~WrE#k(69b2d+K<-hs zz@{)^lO&{s7L-lCD|l2~dKe@nsYKZp+fnw>f`VJy)I?$j?-DIT_P;T_o0-bp@h9OO zKpNC*tR;h{W-zOl5V8Be4QZgJqae~$QX6w`f{JK<+BTTFvTV0ysymAVzybZWju{!9 zhKDAXHiV~U?ujDAd*~Ka9uvuvDo{H@QnVislUneJ4{?m0k&;4>ryMxZwa$S4G{b^u zlIVH2C8(96=~?|7sZVJWRt_4MFQcJILVqgBO^t19U@UEDP~;?~Hde<|)d#J;^$Ak5 z&?>RyMCBwnK=?)a0uG;XsQXCUc{DjmP24H8kUErtO)u@6OwoCnIt4hCr}XJ+W7!U` z6aHxOh0DJem>O37v~3BmCiyp8pr0U3XSR%Oh&z!}iU6NrG(T+2nwdl<;nbV^YaKle zdxKA#T1WYbAaxYCw}x&q$f?OI!>RVsoH85TVt+pG?|vq=;EeZ7{QIKUQ6Fijdq#VL z-#SW#ojUc_gc>o)l+02JN2$~CsLDAsfn2A!+#9zT_*vfKb8L4C!pwi=q2w|$+KSWC z6qPyYp-zwrwxpfCZA@wLcWlY3)>;L*zgDtNZwUF<^|SRk*skgJPc6i|Yu+4odzC=Q zz?6{$bLbsvqz0`%RW50m(IzJ z0wj#Il|sj?xdJeK?@fY5kED&(KsVGT)#%XEt#wHAn>Ot2mOOr2uB^P{s&Vo(aO!X8 ztwF(d^e6AW{-UyTe2RN*Mak!85n3KHN z1)@sQMx8I=K&z63DT;EzE#4~IFKs4RKTmM6*e><(T#joa9yTfr#j13rI*=Jzj^Y|5 zC~J3Ak@nX=^3C{Lrr{YQqxg3dZCgrhFlW@}2Qsez0P<0DCeYPu<_e_df^PEH7|5el9eq z9C57XMtn$d&OrgEq#-d|btTf2?MjrNhg%&@?7lMax5^tyf0AHzB@s3`4 zn;ubJf5WNzSM1k%r1y)rc%F5b;SBLShCy$pW+F4rN{E%R>2B=4^KBOpZE&r%^RFSH z9ebQ2@{#Yl+p@L_Snf{T&*S$E*!9OZ>JkY1!L55?M@q|M5CfjK!{uX3Sr?G;jt6Yz z@y@7hlmdKilS63I>Co8au^{bSm1t}>q}uhEWXEa9VwO?aVS=WFd zX|9zF5si#>@~yJ1q40DBC=fxtB{`(Sy4wgshPRfarO>4zH`+BnZ?dB2_!6WU&^dHkK!02Ggj!(^w%{zOq$#gEaL>5tUaF7XvF@OiXvCmH`22_bGn?e4|uWpj7Mdk5qp4PB1EU-N>cqaw9_r4d&(-%dSP1W z)~+M*9z}ThLmpjLf95Lr9dz-MS%><;`eKWYlbOi(mlcM`mGGr}nza5E9qC;g( zEg?XZC>sDzg(Jv(Q+jdnQR+3R%TeXr{HXW4lVSN^dpoKh<9 zf=CzWplMyTnkD@Pc)5SykM%!Aox8=(dx&wlFF4v(^d=oK)NHp9<0(+r@voP88MSLz z^*!@mQSezlaQ%76jc4AIj2OF7ZBs(fT4~?|%qM0lt|+<4szu zqD`zMx}H_@k0o?)$xmlKtg(D+v7D>02QQl7Jo}X8#&av4#>{ane=lSWkRi-%&rzU& zfRZ%O8+EO2{CWN3x#7p?1529!0JV%@`djpk{YraZ!}~haUi_Y(TY>`yj{#Ab1>NaW`EqFi$p z!Du2wEj9VoC6y>GkUz!+a(f>h+N-)O;dCe2m!AgiHwf-hGcq^_CyUA>@%bheVd%w6 zOA>-Y%Xc;e*xyR3vNMFR$jxK9V1&tRjmk~O;aMvM@|qnYQ(-sSeh#)BYD&;@q2y6H zn1gBp6}f#r6Z>mYyC9x~^V}yH=3Y=Czz2==txmLsF0N5si2ndQ?I!~6=Nxy6^Sl!| zyjsazVs4ocT_VBO98Hvkj{*%LI(m!I&1`DV&bNpD5Bi@KzNtQpId{|I5zR1joaQ~N znD}K`l9Zx$CC5(GH8Ba%Nl`XSsTRJ34x~`6`1N@`Q{%rkr;(SpCF5dUzN_kvJ8do$ zg*Ka>gi>zKr(7I}%z|#D5TA7;WYI4!r0)leGRjhIbdhpLohxnY15lDhw}#?ml$+Rj z?Kk^0s*Q{AO-#CY_Zw7Hgcki#rk)`A(kfDYiG9LFsgJ_sU?i08Z4PPy>F2HJzQq23 zz0~?L_TU}bkHXUxyprR+4kEX=4d50+{0d3EjWew)VI=VIjyvRk4u)|GtKNhiK$};zJD3~He4o?rpr9ma=!)iBHT_^ZaNm+7WS6_ z1SBY^gWxNX+R^fPNbR=o)t~BXp=@DzZ#2)i+GFA6KC}M-QD{&f`JAdKX~>GV9ypZ;LJNylEl?PpA2xSs^$EH@qtEwsA}sYH@U z@vlhZxPCiYI7pInlJPmb-9 zhAw>ctLnFEZ=^~|YEC`aRi?{@Ds7-GZ7RyTWn=t^uCjhB1cDqVih9KOur$pm&)dy`&+#&Pq?Y^b`#7e?STuSn8Tgm6~a zDx2%O?55Ng)PTb+88V?f$rma~+wZPJ`2&cK*BvPO;BP|rU8$5S%`bb1U&5r42)4EG zRMfr48FXm%dgid22Cg#Ay8YC&sh6PiZ$nqHjy-Ln=gz4)4l+{g9b<+&LVvQjs_K+C8h(iS-}qA1E+%qc_5T3D9~C6OvCy(@=ld%1l)X<> zvfPpx@G`P-GV?HTugk^Z8*N#c?Xv3FhL?|~g*>z*rLd)F3Wx;LRLVZ0bMcI@-&ffB zUHuiA5B*g4UG)-I4}`}k9v{ZcSpNX@lm_5~bk{`V zeK+=dk;{_Yc3tAQXJtuR!VrL*dtpUNQ2<)qO?lSUHd}k~N6!0I z%kSk}Q+-H`sc~kw=1&nRsqm*2IK;LbORYGPdZkU057|I*oL(Bi>Y6|MYBKagHrSagaLUM-)~d|6?ycj+mYQ`lOnPmdQjN3e-$T1B%Nz{ETdKtvK5y?5SF(j zjQ|QF+WDH7jDCbATzzmidn=oazx4B5Km>32u0G4wVjn|N)2;VND-*ap%M!G|7E?uI zp>dtb0C|u_Zh95Wr{4JHwaX0zW&Kj~Tze82oyVc)V-%2~9py6M1Z_W@5Pj5@+!Fr) zDb4=?@s^*Llw{)3@vFN1mgPLBk>#_PInkSDbI(5;5hY7$#{E^7HU$7udfJ1j!$zp! z_PTlZ;G+Ez%vsLN_fs1AZSF7gOpVy9E0%gH@?>p^d?O^1> z%;Oo3uUVCRyXlAOD!|;M;kgyfb6*zsKQw!jqm!<0^B+xqhjP;UqxDN4olD;H{1<~{ zBRMHa0V#hkmSEdXpA1t}+-8lMu$&~n6O8_9+@?G&bwU2AW&Z$S_CGBl$J=%~R0@;` zNe87X$m~xTK4|0VQEEpXeNAyz@gGjz@=RusKbqijUpTa(DN~!{s^vu^;`)kl62ZM^(-aff~ICc{S+k8rSPJbAj=R3%@+mK{Bf#~9rw1PuvQPsnJRC;(- zZrzfUu#s5y8PkedWTo%t83z`d-7SR_fIJ0}YtYy9H|A*(t~~{BD7@X>04si{+%H6M zl)EEJ-li3h!?Hal9Bm`0xgH+VQFKT&X3d()K~TTVqfhZ#^_A+r=KaOkT{#xkL2gP9 z{vm4L+ejz6hb_r<(0w-&cKB`-v!Z5us8i=(Y}?@;?Pz|)lk7=I@SOUWeSua0DL~f0 z>S*4}^hCUv)!D!)LvXzh+sU_|sL)J#-k@0Mb*OtNGLjiRBhpgR^NC3oxKh#%zQb7M z(2tnz9B-U0p@o8{4T5euU*AIWXq4xKeC_(KJOP6wIxHmlFn6BG#Nb!>;h*MgV z$Ppq+Zl`4jf}3dwRe?wZ+gkLF2AvtkFtWGqKkmW9e1hGD7VDl7@*nD}z1+@YoD&ZJ z0DUEMx3{h*I)mu^z8#p-3Rb0mqV&X{MJgm*oh#Ko6T?+so17G6y2t(>-g&pkHmtqH zLQYxkb|3WR`i1&m`Zwg=&B*ghMw1V zza^f3kzg_ijR%wQPE2JlCo&YM479?L6qnQ)T961JChE8&)3^=&xmE4#*WoxMm(i?a zKkn_(_c*?Kiah-4l4#@@m_ARCKwL}|m)l?YYD;$ptfzcp`kei6l)D?dh2(5VY7_Xy z#F*;P+bz9j!Ewa|rsU`a&ZP9IZS6)CIpOpVucIkgqS^K*Cxz^n(=(l&?bNJn5B?Q6 z79upR=l25^mfm+Sl*`5rIN5OksYq-r1f-n>tSia1UhdqbbIOBn3Hl?^Hg+|&++eYs z9S4Nxcn(Xsc$uEKNzhKlFhYI{8*T ze^9>ob*W)UxUnL~!^WWynH`CU1p0=+el)wVC!05JV{2Ni*Ytr!L?LHG4orr@Dv*UJ zs`dcd^&cV%z+&=EEc__ip9-D=`LKHDq-cN$_x}KWZ`vV|1CS){2G;Ve$`~*}&)P-& zdK$-D4CjFloj<=)_K5q29GR*7D?PAzG(?4Ib=rq*W}!A@&)@K+TK*Im!RM%dBxoi& z6h19~NbspbzM(Q~sWQ=EhZ0hHgIOnnQkF#;pD2qsl&B81G=)9?07FH{4=5EO0Yk(J z)s>8=h7{{Y@yFDOnuU;Q~>`kr6%KbFJfTzA>lyk{A#emHsm02~azra#c1 zc+vIe^sc_*_U}07Yoz5J<#KuaSQ1;tK}-x>x8pMGryJOpOpwr03B8Vkt*ie4mp_vD ztRNk%F;>hmPtKMW_={{TW?$3Ftz(ce~E2Z-iL`gHXEy??;{YkyXLDVD;flm^uidy*^cPv-BFXl?v^ZV{Y(s*;E5U3&G$ z_Ake$^gOTlgX27hAI#JL0B%^a4ZNnje@jYVNjIMO_36=OTYY_;1zR* z);~Lc*uNj_J}iAfHQX!*I^y}R>hQQ+Yq(tVjbZs5jGGTF$MTRe?5Hy>v7HeljD@{QsCF-^ndN> zbsru1MVeNp<@tV}U;TgE`gXr^f2^M^OpIdtOW7zmxGe8Ehj~1opId;C5|)NFXi7lw zQj|}!xxO>_w^q2~$>sk5x74}h{{WMAr6uh{AJ_i?L+CfsAJlg}`jhP&-N)xV4j%=4 zWS@tHR0kbgJfub%K{8tpoyh^hv{>kEaCNRP`w>|_R3b$XrsQse#&pnNtb+w~$NI^fO8iQCi4qI&Kq#df(=H7KB7B4R2 zRkV#rlXK;!lw_LpA_rPZSL!A~+%&Q1X~^Y^4V{p?6s(1Nd~~AwV%fJ-erwZB! zYZO5~#k8lz0U@R=|EBA_yNQpS$Kpz(I zdui>hm}Iz;kP@wgR>9fW(Mwo6N|Uck4QY@fE0F7Cl%3>ttTC7qRBl$-vFX=e3XG$X zVHYP2-wHPQr`u8X%n}bJO4H%LQc1VtM8TOi^~zF%>JJ-=#<;1Vhx*`d1S5(TERk~-XqlBoc)(FZ4?ADbk~ukAOceWXv_2d-ibmHfp3 zYw+oE{QD&9wLpEA3H^19ej*NPp3|3EnFq{^Ut~jQ)=p74hueVcLu`fnz3~-5a{ZOay&n7B0Q^;Br zKHzR>a;qIf#(5qPgSFH+W1#ElIM>DJsimhDjIEAad1^b~bp;6kAt`L@@6w~<#|2R& zpx?kd zYJL=;^rPSi^K`W&+CtkS@}*WZt(4^wjh#&prxl~J8$kW>p*H(#N|BW|V*AMne5&3@ zt!h%iq3TfjQF2z4o{B20%QT-s>f^eE1*$^qe63RPVZFh!R^U1;k>hYFMikRY3u$4J}gnKA>Hm87R^N8tiu0eUVgZV>O_EzNt$f>k!jz-&# zDm+9!?%&R}wgCIhMeDT5`>c4G=zOGln=O6GQoyxNEv15SmPAB`98p7aw(aosQZMYK z+H24(E$Alnkk~>jm(;H(Z(-(b(xgtgCM4VL6q9h21NraPprC`L(A84OALecHRYp^gp_h_7jg>0H|RxS92pC1TWrk9>PBOsZVU>JES5VTzq!Kd8 zg3?byO31&mu$Yp<+E5OV*gX$jC|=o_7Hqe4?O52JriS9s+)V?_?jbBGOC223Z|$rV z&r%!Q-Ywg{b-{C(p6qc$%J@VUU&u+vAw{_%*i0$(sym4(ExGX6C>qM1)pjppUNU_$ z9@@2~4Ba0nuDJe(&)(AKf2Aip#OAo>SBXvRu6H|t%zq-}ywSzvBf!WA3vx1%J=fkt zPkOcuTG#2;uWI5xSzhOqtyWi_sy#Y?OfUBw{oMD*XQGt(Z1u-~=zNvM z7r&N4`Lt}A=^)HThR$sl{G^Vx>o;_tPREWXFY|XCt;F3n(2CJD&`Z`nRiK`1)Vg%A zwE%_Rqmq-zgNs(fZGDxpB)_*=NO)dA+u1%n50gcbl{fslX5!LH?0rG7-*C2` z`m5&kt+ubq{7aq#cA9Pc@$x&rw_VWf&k=cZ;`R>(k;olemg?SG+EKQs3rc_@!q(|t zW61FPS3$;BJRYIO{DWGVc`BAm`R}W!GMH*4KF{JYtTHy28jO!w!{fbvj=uWjXYeUE z*5=Noe9wnaOko#?ofg6#urgAdCDsd=S(_m9+0Q+xNydUx9I(rJ$WS$Dnd*>GJ zQ0s6>G6(df+Kvg$JJX48zTLuy+J4mHB1w^$%}AQWM;?}tlt*E;ybX$!t;E~H-Zj?z zmv>?`HA2fczmDo^`w@y2=2WV_Y}C#Sq-+@ z(xhpbycoCdS229dl1+=M>Y z_)emlAZh7KPVYt3gpbwK)*J3RWfBlj;caUg?>83vDiVSv6vI%Iw|awrB>*j2lyeaE z&1a}xgg7mCK}qli_Nw{P*ut6)M_FK`r8iHCjVj}S{@`huOb?CMEq@PfDPlJV)He=O z1p#YO`22vlpiU!I1e%or1n&#iLR6Z93O;F23HV(2Nz&RHWwB7I8f>F;ZmP$qs@cn^ z>|vB99`yAp7Y5fiqCs?I8a7TxJ~>+yRY3)oKiMMd8a<@{{U>UEatn3&0uhh8_Eb{DUy_k?J3TsDD?>3e88w{ z@t58DBak=pRaEgegXfRc=hkmxa-F4|zQ_GtV_^4VhR=tUJoo%F`N!Jd*|SBP4b3$ zJ8~L%Ysy4aVemo|O1G{ely@nm0tp^kS7%2Lt6cJ{O*H=i$JhHk4o*C^%lyFn*VV6m zvwX9b^BF%-Syu8q{fN=?%(ErLMnf?gEg&?)PR3S2JC=YHuM?oNFG7VBlomgy{6qfd zYxN>u{5y96>}Lw=zGUCa(afPL1-3r~PrfcJ?i_Bw&3+^ZX08rB* z&g10&0Fa!QDwjQ6O@SARxKOniF@Op5+T2M(YybdswGy3A2ZV_`8~*?>DnBLS%*gqL z%#^ntJLK&_PpZ4zZWjRRFRzU;DktayP7U>D()k}6lO{~nQvL6_k1|sK6n;|QEa}79zEH;*i+@ZK0 zwaa{`mRx;aQ&}%K9S^#*^1jDKeOLG6y?;hr$AI?Lu3HD6zJ5#w8K`$9r=Gv zUYm+4u&}>csrZM*bh&yZelgb{-aYc4knZt!#=PY+;o*EUi~UCMejD8W8e)>|T9P<#WAcJH@%#PBX1 z3J`NA4m#4bslXa}N7erTFx=b?YTx4XvtsA&Hp;J&Lb__qRto2~U9IAM$m48fG8{E2 zm>(#yz+%lO4qX6kRA@g#1JrvF7osNWpP9Wlc@b0dlzli{L!Y|+LE;Sp+H;grlHUxuOchW{L{;65Zo=dWS0O2>c54&D<@lF zsiye|E8P~CB^lwEET)}_%OrsS&DHoaDCd~)a5^o7d91+#~n0hDeyi8Be&AzM&J3=AAt8;CpU7k@1P&h%S|>3@gl%j z(4|c)vH>nZuTtK@b`ziEqkpZ+O>=dMg4|Hk-RDa?BA-Z=w|trqeO?R8iv22Lr6I59 z5~L`BxlMv~Q0aT#^f_wVH}pB~U-<*uUze$N#QJjLj6zH`;yp&wsd;}Bhfm)A$};1w zXyqUBP093ONPkXz4iejZV_82Z-)N>@~bNJBV*_#XCY;MA3(Zr zD=I-OAcPAA5)H3bAGb%<71HHaJF-t35L-$;GytF0QUzL5r6kfxV57T`P4x<=91v2b z(sl6))9$N3vY(+}E!Nji#8{6c*$yc;_WuBVSZne74J=Cqeftq>swEK4MYM#W#H-#b zq|uKlS4c}JqS;ixLQxJ(@BmOrP_^u9yW=~5pDSc-P}DM!Wj2a=xTj^m-{Od$FZ zGEzNcMJfSBH`+)Z`uKbKA?j*#Ql>8xeQr}cN9$0O(5eBv^RTdK#03vu`a(;eKt! zY;9Xi=xI9Tm!I+e++=C=H}!4VpQRq~cTc%|s}+!*o^y$hhvWFnb8M(bX^D!KOP-?A zEn)y5)va1v5sSjkSNzH^KR`iJ9RQh!k#uezL8G^kv2 zD#kiCHWb9AxebU>eI*qH6#x{XNY=VOJ62JOa$RsY@s2KBZiIFGyu5u*IWwAVM1^h( zxH{jhSgmsN?n}p-eO`ClmiB`I*zAuqQysVBv08_S!^J?1>fGL&=(tL_so~dFN86*BmsYjSr|r z@9Z@Q(gj%ZRGH|Wlf|&%Q(X<*3rJU%wP{@_--d6pbtQ5bK|O?bSZUFxANG$m*N+H>(lBd0VFH^)Xrs}1|-0apg{is6I%^M zd^1w-)QanPzCRsnU)j)&b5H7X-kwFrc^3ra91{hVz;ak}AY~-&meho@`$e@W8&*?$ z9WE(I{{S27Ch+>Qzf((3Q*IYjns9mU&M}+y0g-XWUw@4xvmK-}5q#q+)D=U={9EpQ0c*U`GBF$Zeo-zu z9Tz$PR6GH`ppKNc`g^Zp3w9q--ZQ#Tn_EkFGa>IuK&dMr?jBw}YE$5nU5I|+^(8KM zP8@|ew&Hr^fNra4?zc*=d_;}aw6l?R`>heh6sr`{w6R$ix8qTl2PTJOy%cV)>P2LF zD1jWWTPy=@eEQUv4%HLVeHn2Eh(qihulSvV)D#fuz=jxn@YOfL{w~|SjGa?bV3g2v90Y>IX4g!p#^ZSrJjhKYzsHac)T#flX59kbf|iZOPh#EI+JUg z4z(LKtc)D7*fP0fLz#UoeN*JoB1~NO70aS)ihm6yr==y44co1dH@1Etf<7zQ*PPw_ zi=kDXHYTuLSH@XtP7NH{+)mp4OZH<1-zDcbyj!>hNoEt-G-jPsA<2vMkd+{%y`Ym~ zD&vazUmwU?tEVbP_c+VyugT$`FQ8^1G2Co-72tiQkIUB=Au4oUHH?6#pUl3e7a_*q zN>a5t*jla>e<=Ga*fzD}T@klp-yC6O`Y%Lz)GW5^TpIw!s zwLQt?`KL3TsGZ4hkH>+BaS^QSH3^Lm;H6~zKb2tHO)8t`x?7yDwPzn^m2iqv@qPNX zo5yaQKd`y@opkGYS9M);)22!v`hfROv%Kq>WBY^j1SDJ8MeG#m$4dhTJVT1EwNJ&~A`FG{A@f0p?VBB6)?w&D~la|;1JKD0ZB|iRz0{;MG`_Y5UPse1PoyO+k-*P@%4>C&Q$&Dqm zWj5n?J5&vHbo{$Y_$f!KWhA~MyIh=G!1E{Co^dWSG2YEbZC~{rep1%5NU=Q)HBZR( zu=27R9~#Tq#;pYk6CI%V0A7yNhA@|%Rx)(@6}|Q@xmjq z#Mxh8EVJleYrd*@=Q_c554GLXo{kfT;(4Z|_g9wzb?x?j;B+RkAjT73u=51q>e@0?mNra?{sb;mq~33Qmt)Il~wF<+o{EM>)-G*V`{wqN6O1KlKauTZJjVm% z9A6(Q22^(#;+DCxn4cAB5Rglkx?~I7fHt2CQ&8b{R3#MpkdGx_N0U9<`m5~w-%g#& zD6NCuqaYZ(m~eX^fVU_sO+bavWd0S;R60u6%cK-Ze(g~tLf!Uc=RHz>S^->WzN1t z-msoVtD2;cl&1dLmqpxJqd`Au1y?B@Y&D=XhE_t!lE@cF#DWF%^Y_+53kIjy(Qh+bvXT-Z2thW`jD>QEk!lIb!II2cfp(bKJDjrxEFMAIDSdO#{$bl&185K z=9yj$@`1JKSZCAu;TlW+%7avbg z=zfv=1^rGXci*;ri=1}Hv%GnRIEN+w0B=a+5&2rZC8)t_Y9D3c03@J?LP**T!LO74 zd;D<@4{kNw-Xyc0y}F-A{3D$G+f&_!KAwRrpVv2kM=ax+ng0Mw!dx6dl+?J`&Swd4 zzZy~iR@SA)VYMu577I`soqAU#uf=f|KmGR!e@FiS1E!Xq)DvC_qFo>QjAQUoorRzJ zPv*E7WGFC(mBuEemQ-$}ATcvUk}qN6X^TENOrhfqn8`o>_xw?j)V4D7@y0%x{{YbV zA^l_izP_10u=`!d`y5K(mXK2`Py z{yqNy7j5v?ee7o$!o=1SPrq#A>E8#;AI{%A*4nA)&N5PqPc1xsai7Pt!~Xyi;J0-9 zuiPH{XL!dd?kvV9W%zd=#qoS8jUUiR#No@oE|sJeZgcp`Dpr?10=~B5INJ3q_TlEWeb4i3Gr~+Wp?j z-?VSsVxgq?f-hS3*d=3T`$cEAZ_Shb)^HF06S?mj;qH9fnCz4H%q9vwS&4?y3y!IT zSngN@L2BhbWy{Ns(sw16yB(jyIGA!9%DMJFruH}KH|b{u&a)g>lH$MI+}?X4e+A>5 zwPg~HJ~&8lq6@*axZ+o-QBCY^;a*|0@*Y=fQC$|}9?h{-Ouu28B2=+ z<}wLif|}c{{pPj_$)k4+C?VkA}68sSB%eY8LLhMbqVewK&76wPQ%fs=k&Z%dV79XvSnX;%=bn zRs88?2a++F?_EvbOOG#y)ZJRcvHm!xiLG#d1CWVCoXkO2Yp1Ra9W0nUY#=m@h z^@O6bB&S&)8cl%x-D@yJ)pIUDO0RyVw!4~Q3Y;z*3sCm-)|nSw1eEip+X+zdx!3Wn z6ZZy=m)H7>V0cq7T?FMeHd8MY{{Zf6KX+wejdm{SP|xIq5^4P&g< zPJv9>Olv>5WJJA}W>B_?L>o!e!+g9;4oi8-FU+Y;ElcQMC)fB3*u?M{`nl z23M3|G&CweQnm8vYF-G>c0;zG`IMEXBou*QHm7MH`KmIO3CFphWCw1;(j=z;02lE$ z0&mkwQ`{MS@H;r!@(%Sh__g+htwT@z)vs{gS+iKj+PHB}SoKG$O{+c9Ud#HMFCNQD zN?OUd+ln_STZLa|TGf;C2^iocg7X0>Qp>5ew{8d7d(<3oOmI;Z(o(5Tw|~?WdK9 ziEoX*191JdgySrpQwCB?i4vSZHr%D!-s;gK0s&6nN^9*S^?-ty z0hAIRQ!PS}TS>BATU%1Ac-;6_D6Elk%RHd*D)i&*3+Z~m#(fa_N*_Z*_lWCi(+>`) z(7?v6DQT>8MQHy3t#A$Rd(uxMUfM=Spxa*qDc&^LdUdJvH=GZly9Lw$rx{Xh@d7~F zLHj5Usbn->5QNvEBj7`fk<@ApsCa&+d4rX^(+d+rJAWw9ioz^jJFKZmZO2fh4OFcS z>qNcN&Ic8^B%}}1iEe-6q>9weizJ}DWCS7@A>l;Dw zp_<9>Q`1NxV*Vd}L)$?;!NX2D{{W&F?Wr4BHw$*;fDob7Rs_kg^_Egxu?MHJuphcK zJCd~kk!2es+*a^nuptsVl3ny!Nn5A%4!%_u6&mu46t{S$ej8uLRH#!BW84y=SWw&V zK2&}+rE+iTLv=aRB)j1JrOyvy9`yMRK1Km(SxTCl9z-<)gJlazN;+D-)~jorSA$Ld z4L|oD5pKP;Hs+dH?)nlK@53x1##we#PgsW=K~O0=gYKne2H6KJ8m%l|<~!&LKL{w+ z-(@T9vpv%&Tyivs={`RJ{@R?QkkOV8UM{}_qTPN4RInB=CZRF>oc{pIf4Z{-Yd26~ zR`^`7{{R90>ed1UxT8!fkjEzABr9rFa0%N;@7Vl8=vV&$bE(%IsLSCo9ITAo#$#ip zFr){c0ZpOJ!o-^!d+UniwXL>4ksaR~wM}+nU5>r^p5C&`WFwQ{oGJ%lZgfi%Z7eTb z+v2xAjOzaYbI@JdA{?8zJQod&$f>SHjgviy&Z{30rMBwakPvpJ$iBCtalB67yZN($ z4pW)a_L_@XISyZNIYh=&K18G6eRU zI5Sx_WWJhl-JPG#WUgN_DPQ^=g4t|z}kM>fQ1`Fe(y2|$#oKaq@Y0!c` z%L)vVXfzTe>4zE1DQ;Ou^Cc%}rEoI3bO<*EhlsSbg(lw#P#3jp1U!SWqM?E>{6pub z-%*rFb`3H^K_D;USLL9Q%N`{OAh{tW0QjU0YeOP#MhaKJLQ(tH^R39m(8F_gZVxBIZ(zQf9aI}0N^Ld@KtRr%endX`pNn! z?nly2W(3U4#B)yEawmN*Dcq>6DmxCyQcBE8tgTBi1@Z`Pu9!`F z95*Y$&tIxvl;Jnf;Qk;c$F9HgtN3;3bLwnGwX6a+5>H=k6+$LEWIhh4jR^zFvBL(Z zVBrf|tV)f%t5ID-D;poAsW;G@)G!yxj!J$c4!5=IG7ZK3Nv9R<{{SxLt~|#W7~)BIJ@tA7xc<^3>M+Tk1Qh$5}7U=uWWQ@q9iM>>vA9K4sUGJq)9-t*2qS z-k%6;5TFl%q@%{WU!F#%y6gP7E3n_L99!QK;9klI5?jA z$Cn%`Je1<{4J9OkhXTSBKpJbMd9U(}Jhi$mhoXNT%k!ND_08BmY4suAPSj>FF2j$- zFqmm@`9^X}sY{XET(;awP~C}Iwu5g0=UmsqepOE&#m*AXxa68%p1sbio}%Dqc)WV1StTJt!tp*IY{bRmQr(CPhtr#LV4MK*sHdjGCv!X z^AlyI=cG9MK|v(Et=n$BO@~uK=6MgfyU3RxrR>*&)6L==JWG_#`N0M*D;GXwnE>rt zL}<%);X}hp2Oj?b@5O#2dsXHPng0MxAEM~I54BuVy_|{3QTw0Vrp{_MG3Qj%c$j$s z>IQ>1x2&es@vQ5wl|$z|thP2CQR^7=&(|Km+AYTE#;oA`I^J&+3^5jVK(ZOIO5AiE zQY~R)dh^A{JeHnG0P{V~yNT^{bI0W^oG$@k z3JY%-Jtcls0xxf}n5K+$!O8FZp9>ycN!vVCn8`>(Ws;t$-EZ4W{kr-b#hmn}`n-~O zEM?*Zsi=sM+HG{)T#B%+7XJX#l8c;zwdoKCLEUl^j$uX6GYrK8Tm263P;tkpqDBO|Mr`<7-DfE<#Q9mI5(x z!0zEltBsdLb*fey=0Op*Xc0xS$i@NUU6YlkNh|l$T({M!gN*qRNjzp9->~ zp9O-qVt!qMY&mPCcLUlg!iMA3h}#dKQakOXRtIsX>D)<)TU44PaS*2hei9Fu>0MtP zT(w0J}(c^p7Z=oz;ZmLS7_3+*dzbRe?q+O*DRZVtai`UxEf-W8xc>mTVQTsta>MeSrU}Q-Iz;T60eAUsx&$a%Rf+HQ;t9J z1GEbPasi^p0A$-bN9kYcCo%N1+}Zua#c`&2dl{@#*nGYP_m;wbUD+*34z{!fS;Q%= zE(ozTUC8LiZa8FXhI)2Bs`0<--`iZf^CELj8aXdqxS2c$65{>Z#%1}>A!s{};%naB?}5y|B6 zSf>^1tuT_LlG#xK7ALR8O~q%iXH7F}EtQ5>oZ$60dKKUZ zC#No#3^avqx>UBc0bMEA3=wuB8xR?xh!lt^>jHUgEtL^6e@ z@RJL{kruaxh~zXwIgmU>-khHZ>O@Y z=(mZ1LM)2a-&qYBmxr}}k$DNpTmo?qTh<#4IoPq-O%JBP$^+@lRP z{0Q;iE0Fsow;cr%7j3EsREzl6d;F{OWwfJ3Q&X?LR(s?A1N;{a3R9GOv+1@=4}i+Z zVpdNH3n?Zh;TBAotUlvmeL_^~6hJ;gyjqR@nwN}b(tnB6Ep+;`X#G4 zxtG)OF?f#P<3Pc3%yY%KO~}CHBsLtB+L8iG(Ck)}8*WX&*46Z<@MrS9JSv=b8(yrq`Zv-2JN4}R-{+qamm}fh`l8l)_NTYJBeuLgD|p^5 zz;Tc+uVe%ZMjZaZ`HQTO0*JK$NPWuKPlf?e2*Q- z{-gWPmW&dXRWjz`74r-Mpe-{e=fr~wy>OnR0b9Q07<7xQg7_(SF+>imw2Lz9L4LR zW3=;5e5I|?%W^Hazs^x5)NVe10Tf@`b>Y=T0R7hQsFL{~Gk7Sy$?`8auBue&KmENy z(Ng~aD*oe3YWa3&MRrH&L%W^u138l9*$yqiWF4#I5~fOV`AEOSA~v<^0MIye2c_$n z=6+ADLh*!Cf6(i=KaFf$)aNFV_bUa*+rnXSI3^q_g@%m8SgR)LQW$M5f}`ebb6!_{ z-g3$7H0vnEFQB{7fZT-@T{qkB6cld*f0uEwbf60gV8k;a)SDK!4-zODHqnCNASDf7 z+C?%bBA1quQS$e{j+KVYscws;H=yWmN{pKCbN>KNu*1#d8{81JZFgI>Vl?;KhwY+% zX8!;@Te#h8YJBTGv~S#Fq)BP-i`EJ9Q3Mahra)B^F~wF)gsI?uC0t&Uec8N*;wgb; zm%_sAG$4UMpK``Mh?jOdJ;%G-*wrMw9f6WVk=QMQKnb_z78kw1 zuIu1jmfpuN=N^fA93RiP?QLEs!LF~=b^Rkb?*rg}PyP7J^Nv>si(qiv=L;e1l=P_) zm}T4~c95qWBFG9Uu)mMKxF7tD_|D%a$m_zG=1#W1x6Vg#{{WA_Ag4!+x^_Ry=D+<| zpH@%QFVlx|Ae46V>IZ`6^D-Vs$io@v>;UvY3%{NFO_BUXC3hr(EH$r~cmDv1zCov# zWfDn#5B^8c)&Bq>@cSt!TO7Z{?GD^Q;vCP5;9SjM45^K?>E$Lgu;f>sX+;jLhniB* zq;4L5`tu9*wrI8aCmbD)ro~p)oEm4x{{ZW!+fLbVuEzG)x;UOA{{Z1KIV^lDx%kOR z^x#W|^QE_qhr+eCkOlhMzQ+Fm#BOtH{rf4MpOr^A&(QgU{{SXw<1!1U%CQtWXqq?6>6O~xoivw!B1 zzx&eY_oRP)6WRmr34bt*Zaz)GJb#D(06NpHfcH;)>;rqoaDU)69|mvUJSF1Q)upG| z>6=vJTgi^_rVE)!;hBg$lZs|$@xDcjUhHOK8iF2WWhEq_tP#-ba77P)kE;6oY)QxQ z9nj>h*Poqo#Ebjsvzqf>3Qgjn=TC=^uxwrd062y2b^!7D!r)V*2Z2TR`7K4m3hRQk3QT zT!UC>9`nAK1JNX3QY}QUxV*cKS2c#pw~HrEsi{^(67108uF4A0;Gw6p#-(0B%0ma8 zm~Z?&w)63)7#Vg4EptCpV&3YQyBN5)9Iz?5PuXKdtQDgdV@_4p7kl&-D&j(GsN3b| zqSpAmo)qL`fi#*CS(&Odu<@xF*q5z1q=oG%9S)+LP$JodJty?Gl6-ZosO1281};;l z;ZtJg^$Y20jzBmV4y-w;Bp+|Gj~Qf)rIQXc(zS43_mng>knBKf=|=}*9$HkR*8c#u zreVLJ?eFtir%_Xb!3jqqVYd$AB?TyW8i*_!f2jnL;%(wAf4-*b$)gA7T>WSk_8;%4 z{kk(I)|7$*iX8Gbx9y_)Xv~Q?9wezv{%*FZm?awQl9ZBwZa^bX#Ehqwq{LCRDopN{{V>ftRX1m$ICdLnh(aXkG3{@9=8BVmf-{UbnEs} z)uM8vBNKuuQQ1B1JU0t|%6=BC1D>n;jTeM*DoVyh;zjJ6f)C+9cVU{1ITBnqHtuVrptP0W_R#!UhUk{t>McfLwsz{ZDm}>e zX+!a06vxb}4*vj3RD=*Zr75x0c>!9wCh8Sjyc=Ml3tBDtU2P7PdwL22i@;Mq%$!?l z`6bD5An5wgy%X*tu-B3mV=UU#6D9DqlVf%qNk46>9>+l5=tW86?Ua|?XgIJ*LY4tP z`G}yj(=*&3vKdl$Fwz#nIuwwh_+Gasl5v(zgP8X(xoHbVm$iplP$CvvkOC}~HtJ92 z_;(R+buf`B%O;Gt)P(;44HwyaC^ff1?*g#Y7f}}x>m^$mSzCJi18F1etqjAtqEZMO z+H@dmv@82BOBy%X6r}o0sE^gJ{Ob)FL?#{v@zT!Ds@yMczOqXUW-p+p@|f!yJ7N#- z8&pl1IM^VR%2E~whT*OK^|J+o5#tT9*4u0+L?*zJdiV;%25rm=Qi6aA9THp8!j@*o zKuLKgYS4uE9lDCp)K*0U^S{Y5ETf`M+ls?Rs*gJ3{c?x1XtnD`(&}oM^3;AOlx_LO z!TppkaGYRe+&8GSxSxdH_PHO02sq$E*pjl>r~tOx+u2%UBNfJm+K6%}VMkRY3zPR& z7-218UkYqP+`If+kVj8#ENs=r-~nx*`8RD1Xgs5nbbl&JY#R+L_WOSVg|KPBPhN>gvLvS@;1_JnARA*hLTBz~<_e*Uy6M*~QG3K~6D zV4FWlfo=D;h7fZBNmy#wkG2knH|EC^Kv6F@>Y^TBlw9uYnR#NEQPBL z_d2?C>$11xv(WsyccU8@6W)&O;-R(j@p1f@G>FlXHYKF|`>F^D761#_3i@w{&6<#t z?}O#OdZi7yCEF!(gaR}gp0!|+N^G`39}2Hp@g?NV1Dk(MFwlDJ5eCNhwz`^I)YX2W zRvtpUkncwzlhA3KqkP76DqDgH_x&H4CQFT}OF-5_G{yzJx`jxQ&i_zEkFZlX)GPI6Cl?{B!;P0P;SR<31C|>eio@TQAOS*LZ+N~iKB*`glw6h=7C(}yN@cp$PxC#Iy zK-<5t{RLUVosdXT|#O()ed)dUfF97ZyH0JonC6Ds?3|x={B!EkAv$S2e+EEKa z!MGxr(B!Pvx5^zD#>S*Chl@WR{{UA%N}SK>GrM_S+wwxZzZ;SlC%3%j$@DW=&EX)D zfD$A!NJHozHc%E-hraNf9g0VrKh=sF&;U-$3$>Zl;iwAHOHYBq0Gq;xbxd^wx-w6gSOWMR~x-l zEZFxSbai@Blxp$Easc;f^YQ=*S{+i~8d5=S~aM?%A#FtBekX5*oWA@jc+o6{5j=nn?U0Gl?kVeE^z>r41R;u{u zPd$be$#-E-=eYJ8DSIHqK#}pS$6HRFd`%uaeXpuO<-JPv0c3lz+x#{;|#O@NR= zDRSrCddu}IU4yG+SeTq%XDcPB_?%R1$$TWB{!Y4!-%3&3t2c&DAMGY(dsQbH{Jqx| zy2w&faC+Lu&Z({CI*mcEK-V4Z=Q)Wj#sqO95{-gfP&*Unb4%ID*1}tQOR3OU4%f<* zhQ4DR5;D_dt^L+?-^Zn?3GexKDK@#Ps$W)FZFXzZN?vOs+sa5tQWk)#Bzrvlv^ngQ zi{~*<#l??@Z|6iIY*~AMHLq{5cnV+5KS00g%BHaT7>R_<2MbU7Pd1WnEvWLPd{Z@l zC$EgGqC*!PhFn4)j@rkHZLLZbMKY@W$0+74hmjxU2dbIM$28ulH@ZOa>rcAizXGDm zz0Qt4Onnb8kI5-4oV;nm_E-wk+y3nyKHKS$E461@%8NvaCI&SHLJOwj^R~4tAF}ks zTC1`u+-iU2uYpxsrO`l9I=e+j=LxYik_@EQK-e)HvH2 zCqhb|SHaV6n$yc(>3WruwwU%qwoyDQXR6=_^%12WI@kI(FJ$U#I6+|z@RTp+R2OfE z3u{MOHu^}fdlAkt&{8x>1$X(S-ujK8)0+Hs%lyP{*oHeILc>Hh5M*vy{HstOuv)Km z^_uCDn%gq-fYd0--De0SbvCtL>FCs@`IbuEo1QunSgy3if=D;;uE$Ganpfp1Z-k^9 zVsdm&?~B5^9d1je5m#=wQI)@I_4ij(q4yoq$c>SRZab=20E-Vg^j<4&#<3(;TLhTr zr?#$U+W-v7V&Y_(Q&NJ86ci5C6!jMHu5aZ301eXQVAZ{!tv(i2t)+Ys{LTDv{++d5RQ<}{a~VAU z0CQ*7pTl3xKWk%#%rddj#bf)=l({eIq4h(A#m||J?oSrw4`SGQLNUpUk%H@qWwkg^ zl@&fNZDFN+|31-pyxz5f8Zuc+fA6j1H%`4Kss zsnvylr(MsE+P0#Yrd3<9k{>&fJ$3GezwsWGX+O;*6j{zvVx+u4|?x zeD7d4xmZHLEGtXgY&|H8kI7#xmWF@t28GFJ>*2K1OF?}|M|tP4&0N+XQ7YVko~qhy zS^IZh5#zThtj5u0xDD~te!cwG=Zh`Eod%jVnrtv9d=NR?>0GIfU%UxSvbeY<}L?6?; z>UV(sOm_3Q{o>;(;@mHZCnDsWk}{FzPHIcgl*wA^lCxpssWlwPWABqeCitc&7#7S% zeI+CkTE5fmG#S|@@J_idbN zZirsrQF+QGZ;_LZmFjHA%9Me9tMs}H{q=E8G7z@N{Q(Zb#5o^pvD|>O{!vwsP5t!K zl;XwYqDAI$pDmG!Gi?`dZuZoK*t%#I7vLPDd6>LdK1bw71CM$TDN zv!>AopO1Kzw1}!w5B)^{0C}s*??rp1_9H9C%Z#OMeNCNC)A0fK(=fU(pOLY$&(hUG;`ml;~=eHNemRrCJ<^KTJdnrhgrw9eb(_DWfc z^gjGP($#ac?HvuYuaIZ0YI(y)V^q>2v)EIsJCsh|go;wkr;ererK%{5NyoYgDRc|| zlcg^wmy+_Gxcvqy`N;aEJCpCDMs6r&dvgYJ-r-{VXWIU8+^$D480=>e$4HPKaX~3E ziajXJi}h2FDJe+tCc968@ehA~5o@MSSD&#{2jkrQY;yeGMrKzlkjl6o%4A=9Mor+Y z;K_Z$>z^0>$$80iN7;rXHII(e7!eU3G*qRy@f$|O zHrfCQN!*nKR?PPVKbsXPew#m=@`KXUwnJb|GTzv);iTK46wgBxO-Wjk zRD)upsq&?p2koIC%cWQKh(Ei1D_%%v*<-SY+9K9f{ODfkp4UT9Qdm{ehRE=`K{Q-~ zz?$VsMZt*Df%8r15?Bu$vm+*^TlCByfm&Q9{{Xh3l6X9W8gsjTR3XhSl^`bL+iG52 z%|Q|vcxcNSY~A~yo6u)=ODIDCK5o2E6XTP!nORGTjEU))Z*EXRLfs94q46e_j=ZdW zJpfxc*ZP3H@%kq1Cwjh~`QIYX@+Wup@@^W6;;CT)sy7IWpcu)TT zK8G0#fb+82FP4*c9Z)TR0*2$(z5C$*041}L(pFs;Q+@uYg8aLJS{#SBOqGi(A49P+ zJ?TA}=NwPBzfONu^0;3X&9Q}li{ZnHoevQsAj?XW){VcD_%&_myk! z$~M(w{J-t>^*Lkulzle+E_NG}c4^<@G4c%l;HH`Z@3UX!P5%JtkHUznM*Z=V*v7wi zPjIGPWF>LZ-$)&X?dN!`WB6kmrab)7TJ|&l##^q$rlKLZ} zLOnI8CHEANqLXXd_2S$A04UwruflT5J+DjRnmX2b!Z>G6PURR^l(!hnu&C^d?n2T< z{DrHYhNPcz{(G@Y1R_^)@DZHa^rfVK(@V#nNy6V zUz9k~(SQ8FQ9YastHsxnVf9%hNl@_GYYFJNCGh41tJ%HqB+w)HJJb)8@UGcZNnTp9AzfpS=w1n@e3dv+m zchMkNtHZ{$NK56F`R(>vu+Y&+vGP~!rbSrU=8F7JzP4b+ZKrYEf~(jI5mEy_<~=|0 zRO{|QYak-&KAn~3G`*@$2-0E#M&Yy4Q640D(_YxTyPj?LNhGA91I$)?W`&Zs99P0k z)6`IjF5}--1u?dk+>fDf(4f!MSiU-UINm9G+ufpxR*pX^HBZ77fT*%6`#9?dn-Y-Y?`U^Wo>7M&a zNn2$k^%e>i`{*^ZUiL>n&Dd{hoU%7)E-o%=Xx)Nq$^*G#I$ZsQjNmpBc&au>Q-32?4jD=aqOG)ZJ5O4ORnv`PEleZ%mN`X zio;59Dftpvw?`8Dz+c=IYOD7b(CB<*cted8fW~TDKxuu-R)^nFQ}hPwX@ru5iw3e1 zHaBaze~6yC+)`*|EFu2iSRmd){UQEnK)>v$xnLw?kzY`4*}dx>3Q9mfvbF9K_RlGo zfwCt&+DNjHk`A3{N%b}CA@!r3goj1?s`t5~I|;6a-ysRxtx-bMFJiQUH1oKi4(MYcODj5d@yl>jtB zuqLpO-NqZ$NdRyB);iW0G*TN+sDQo0_xjuLtT4-vVM=WclBEJHt*cUhVHKGR?Sq`- z_;~g!c$E&VslskP(^zM=Jc|MeP;oLuQj_pD6ivsn=Clxe>b*7ZNL+*>-8CQfQf_!= z%srBvQV|;vWGBFtr8fJoM3k~O8DPiG%ScE#;vECxM&aYFYHldnauh__>Po-RIF|v@ zcDNL}ntj4JNeHfAAwjcHl)c>)RFQA8nDU93nB}>gfNUzq7ghFY_|omf+EX8T*=Gjl zG3B(RXsyPBEDUwQkLC(OZss}1m3?*~5;#84veK0?5Mp4O*T>-0F_XX_^5-gHZ0UrBQ#gl1?IMkH@hUOs`H(CmB_6nc6 z#(lIh7`MxWsomE-ZKk!T_cWt{Q;mIVZ_{;6g#KVG0it=JOmuPWHUi&uOoa7!z@M_U z?iu3E6Am(#m9;f#9wWfeMsO&&k?uv-=^Y6MvEakZvVx#JC7@s8x_%nN0_lSdV(V7T zH3Vt+RvQ_&9iZp>c#2ljVxkR=X~`I(!+xGcNJ}jN6}LL=Zp4p$sd>vihN4BeZ*)6j z*(`K^L){Kcyi*h1En=~)vi!M|97=a9(PlbSq7pSG)D3O}V{PIFwt4++C7H1f1@-Cl zlb@Xe9FB1B0&nIJnOQzf238&-8%mS$m!C$!lYXR%t>u4?`8ZgdW99t|PAmBjjj|0% z;P?yQ={TI{f4ke2;`vDYgO6l+MqT_4dK;;JEZC09E;jQ{CqYH;&bQl{80B;Tc} zb+DgCGaXra4sqO9FPg|Vg~Y=CW14j?Ev#x3pH*pT1=W#q2f~z+b*#47L1l%jN^*S$ z@VL%XBDJC_5|2$KPB^t$01s`ADK1*I&~A=CbI;&d9i_uN7nm-7QLxlpTvuN8)IwWH zut+4GJ=M&7pJm%pj6F`1<8>-#-2DlEb32QO&f`ZO$B&sxTtkxINBLzdO1U~Z8CrcHg)5_)A9Zz#I5|%wL9=RH$G#PtA87#7Zdfa)mc|&k z&c$_3HtN_~lVIu;q%U0v+>#ArlI+)VNj23UUjG25#$&mhyPWq6CCNUaxy~bwaE?Bf z@tAnr%aF9ECgk5!4Y-TUAtjWwcGz28dJ6GBpjk*D>0Ny~ z)%}v3QjDb>Jap;(7u4kL336(aLz-4W-D((nLQDf z;+B#w42uO?*Ci*!SA_GQ8`+JYl06S<;eJC;xB1^wp}6OM{+7Fy&m?xkg!fMa%6Yp{ z6*fLTG?Xo;qqc76r6E8xwKg?cLaqf-?r=PnHTk%=AF3}j`TZ%&9!RYDx7MH1*KE1R z8|{{3nKv^z$ubOL*nb%YMn(i#k{uD7M5$4brrNsH5_cUhbEWC+Z-w$Z8uGR)^sM3Z zc>F%1mVB#9y_HLSWc!q7^$Yrl?uXK>e%dR7;_-2n+zHZ}$YnAx-pKk5MPxjbwA6Q5 z7I0tVDmL<~Pvfo%8`VwSoUiN_`G+vH&U){+^(^8zS9?Q=?Sw>fS0+7{; zqHUzes;}ys0v~QThEwS)D+iDFLGmT@Ob%)i@~py+7$n6{qNKK`RPBLY=ztZrGx z(wbEYwl*2{V~A-ux13&H`+lhyyI$l33l3VMxH(f zqa8>^n|2&6+`e1&EgEa@;5*%ni|Ok>a77%XPO9lc_WtxQk^hFHy?5ilDla{9kf- zL_0dQf}~&aqT>GmWmRACt(MD;Z<6bHhuiO$5RRflPdt5%*lAFI5xdlj`IRfC2TS^h z!8QlVVjge-qK?$x+gh*T);{Fl%xhhZQ}scSj*^&MZ`SbMrKsud6!d-*2i34L`Nld$ z3%T_#+?NwW{{UNUzR1}f^6!>KO(s0mM!oiiV~d;pm(rG> z#`yGN`9AD}Ex+IHMYq(-WnB+`Z%xG^y+@6$Y7^JdsbwTi8=Ka??6r93(^q_XNMJbJ zr=zb`Ns%eV0ex;p;%%)|>~nf_u8~ExTxQLyEwin=as4~88Mh*TxVcLh^1Bt5n^Kp! z_ zDJ;JUlG|tl(rBQj!@}AFRdZ64>dI}NLfZWagSciIZ7ewuyxKn_hvK)o^t`o~U@+t* zCzSBO(lK&Sqx`Y$pp8 z8G9a{UKathlp7K4CbR7<$3`W{Q^(3ZQY|JP<&MP&+=B9l#G))e3Ty49j2vCH@$sH| z;C~%Cw5Ry-(zPe^5#v~@`~@zSvwxL}vnjNzbewrg(sesDH5FP1X1iTlxHn zQlZOA2kw+rC6d0+((qqv)u~VGkNAV4z~t$0u1lXVlpBDazlBvTJ+|`tj$gN;)*`S= z@bdFZcqLee64Ep-EL&Q!(OZ6hr=iaBG;1jIn$O@`5Z_B|q&m?`ymYSXPHji?BZt_j zxpZRYd|{zU0Q4rD``XYYsWS&9j#kvSAQyZY79`U1uLDv^4v*NPSfYaM*vDf+a4dvA({el+!a>(|aLh<>W zHLWeTi;`8+-javoYP!_^N~yd?iGOilZ^xv&%@!6DkwBK-tS&ejFM3F^`)k_%Df7xM zij1pk`lFKM_?s$y(Hh$W@T|H?~p^ z(s~hG$H+MOTT6@{8yeP@8Ssz#p>l7kUc~X%`-R4Pl(q0o#znYz_;KPOOUJ(JYEygH zwP8p*2)%`g9c%K-`Iq>cpV#G8+nZ9JwB`9@^giwX0LPvG0E_QM(>-qFQDLj2X(PmKv7^RveZkpZ(9C{g%lM)ao;S;K zm;|$|W+CLK#BB;Xl>|q52?Y3%C<}+yzImK-0_~aC*`H*;r6V6ze80JlYi4kBylxD*U`QS2OA+GEkJrkf#=?Q3Diq3zvWBz~e^!2jT zy6cual4swXysT8zAsD%GAQTk|Ew+`Um66lp9u?1fP{%-VO z%1V^$=vMoW9Yu)K!kJa%a0u@yiyuHp0yd(7(SyZ=c*w&8JwW0NtV;{W2ZpM;|n6d4M?Zyjcc(r zFdAgYY$?fc*oJ_wiPc>Tw_OKHJX&uElU)IMoSq1}gtYt03IlS{s#2?UqeiEYDd2|7 zk&JW{naXj$7SunyQIr}nys!kEbXQbV%$5ryuf#4d?ydDi02BE0YiR|w2OD4Jc|R48 zzrv>*S|s|Bxm+SF#+)v`UMd|^@Y>#VTDE2MCoM?HcPq@F6U9M7(za5CBKBI{O}f>+ zJ5p=0S7xG5xeUoMjA^Adtjuh9PP+oZC+?>!vd^Hmgg_%F!D3!MTa+cXjlEZzN|dwr z4wP3$#MWYg`_!j~!4tV}Vlew2S;%r(q>$JsT2d|LrGTu5AIM4mZivb5>P2=TpZq28 zg*_z2@#Bs?118e2G8EMx>P>$A5&jtA%MU^dC+}fxj*JKl<0p7jIVFh&uyVk#@t2!0Oi9Vx$TH9$(96P>S5o^9ClaAyFrBH z4FhN8MS656efvmxHOuY)00C%O)~gz0Rs7EAeyA268~U^M@F6X8j@0IGE~fsY@Slr0 zn}gtzTU}6X{{Z90+y4ODnrL>fCNVpP=Ur`bxThk- zzNqd$1?0Zc-9bW-OKv17KNf@oRs3J^e?P};<5}Ws=>Gt-{l%re&<-VO?f1nqO2wsP*Kua}yb!1UUq z>Jn}4uH)l6ef=l(;2qmpJFdl`UZ3661OMa08Gi%lZ>3#^iT z6XQMNjf>5--j8I!5>PZ0aNw^^DE0f;&0!Kgjzh z%9DxYs%>oLeP_7)tlXv8$#TQ4w@4rw_V0&tu;g{TmvnfS%=l?>`xt#wEv}!50CBK_g5IWypCc!sBp`W;;yBXkO=SqQ2B3)M%g(z1mQkd>Vk@i*ccC~4mFZ} zrlfJa+!vhJ)N&r=4-T!|S1)J9)L%&*wztZB`Z$&k`|}l*PWE4YkTeZC!;SU#)in6I zLS4&c$VIG^@BK_51HRT)Vmg3kLm;4xKML4XYt8Vo@Lb|kK+*PU4CPC zDNBp6+JI~c1m9Xte*?BsSbYa!{Ebv#`gJO-2lXPzNXbe(q{{Tln3tt44%$SQESr(@%Bpwdrg|n1> zyXhjuB|e?H>!otld^&3X00-OZbu; z6r`%xY+q6>(wp7*FSjhYbO)*P{{T2)N2n7i_0OD-$8dQld;y4aGL9&&9H|cige6uf z(E#qZTZ;oh*7#Ir-{v!8=W>K_k&q;O1Ua57wv9K$mryyCd4vRh6?GFWV> zOcuc^3f!9~OCGhzL*pBNb>WVOWWH6aEnh!e7QfR6Baihx*&HW%h7nu_7F#2f$32F5 z)Vk|0V&*O>xjRVTw%`#}A1ln0G&f z_K$`pXEfzJ#N^DZmM!Ee1}U6;7iOv1PZCm;kk;KUK_kwuH#p0w)O!-~9T`Oji8eVD`MaxosxL~|eAtaHsI!<`kK zsf;7Iej#w5;#;WD`PJvezm4Oqw)QN>bCr)mkL91pj{RuWTkDRe&sL-=GTady^pwBM z>U2>aMO|%PuV#gFT~a)wUz55YrL;I>BBq~iLx@YovI;_mhTiI0S~12KK&z9JmSxme zHOZUF&2J+s2N9`gP%3$bgGF`#0E{*4 zVLu&?%yPMF$n&Nn9FlMfeiM>nN_HZYfa=ht0D*lj6na{|V*Xa<^*%RN#dvAo9@uZo zs>z~6Or_tZW+&uZERr7sleGjO=)HC`;{c!zHcN2|w1CHc4zc`7-@gE_V%ZV;v za#?r7p0}(bIKz!0U|Asn%O1DZ!^Zyrh5rDVIBo+@%H<0br7W(QU)$AvPeJ}w_^0pI zLAj+U#|OpU*nKYYj@bQ4@ZQgTR{cKBXN_eg&0{Ho&pRs%^WQB6=h5<6C@U?vr6~hy zP~jxl5G(4=U*@vqIKQ~#Zv64X>HaxSs{Zclq2N3Z!ztsrFKc?gI^>=*jxgua_>>-R z$s=&iJuixJ12#b8yl!--XG((^E))h`lJ~~pNeALqz6Skk-u@xGvu2bfd(7HRv%-F7 zsjUs^sum)jU^{9;wv>>dJVCv7e&$Xy#{-Yy`hWKM(;{OPr+2tsrIJj(zL&eHhU~9$ zvRwO+ar}=b#6`el;=6{$lnF|C=c72htxxL^&#=R^i-hBNKWN?4=|@1SxD+PUc6)E z9}n5%tMijheh0DeUozF?rsD+DI(PlG38U#nhStN?DQz|%ZL5rjDJ)@~ckMY@HADtt z9m|N>3DV`Uaqa0_TW$J)mmQBoU?M)H1F)3ni(5~1F!HX(!xF0NirsAhqt7d*{ zdD1e-9z#kYhJz#<8$MN9_}aEAHFGM`P1eJSDg8k;p^=)o6HN=Lm4KtK!+OZUq8{*y zvoIYu^$Hi%k_fdmfG=aOVFl%4C~OXbRnoB<6e=JUw%*{8t+lBFvGo`#Q9G1ULP6I` zG++#Hm*JhOP$2lG-(^n*CLp!L7E^1ofz)2JCNyZWlVTJU4^L%mRA{-9kamP3-X~hx zX4AmBWH^moY%E*!oj)2YeF-aGM*}Xv{LzT_iq&YyUy)YwkyerdgG6-%QE&_2HdKZe zW9lKx_J$dVB#$1nFL0RDw1*ZHy{IJUfOVY6>XHGqGHW7AiIyXCGE=v}7VrJF3?M$+8zFgewI$Y?aVa|3C=>f?QI<(KG00%7 zsX?I9(`)=%8~Y7yMX-J;iWp9nrN9ue;*E8!llIIhgsSRu9;ZTXJ_TCW@zRZ0HZ)r? zHA8GjXrCZxW(~ww9KOjx0DeTc3ab+$*Fhk}wvNR+8Gb;d`~1xwGRP>$A`iVe7aL=3 zKKj^eEH1SsV6qvAmeP)?04Sez?^Px6`Keh3P?p*$LGa$l{{WRnSY{h4UQ_it(Lbuq z1l#d6KNa;bAkoM;lmts-dFgUfOQw{GG0BSZtXAq5Z_EKUC-I>$@$4h$f^6eS3ieis z1QWX8CANwEDLQC0tp_x6ME%4V2=w*Zv>nIpwJwn}bRoqHD;>RKPluQ=9z>AhooR&s0Nw8hG250oM{SESj0(l^tKak#;1G3CLKelxZ_zAC@F4*pWjU^=E-|wzvVB* zS`zGphR;$}U_JM#I|ic+8C&pMK|z@uxSz^PgG2Y$y}*gDQS3JeN>Ck)vaiaZ;9iGb zgHc2BkjF+oatxkIbG12hf`MK%uSTyQc|zT0qIazneCxfLr_lZ06q1uxHs&KWaN0B)6U`Ip$dO34Yt+^ZN89kJn4*b*!L2wPH4W?A1@Cu{7@AlELjZ5TWKc1klK_-PP7~2WqoF_ zYhL>7=@*tG%BN#EJ_dg#<}MsA2N;m4lGcTiK{f%jfJ)R(fg@8}(0=ubD3;=M*5x9J z!F^i&KV))kV%IY>jEy10GMv=>JecuAt2RhdtfG)kmm<}Bwej0)j&gJQy!}>72VF(O zG`5=bUb`El;oN2A*hHaXj0rzN=4PDQ%g(wgrzYlnGA+5 zT5T=_N}?d>Q{V~eZC7;hkxf5TR9neeBnJ7%8gFV5Yv>5LrX~4$6~0oJRyG1d>L0~A z_0)|hZU|}}L-DXGmXPnvp&>m%J`|;!SdnK|rh1Rtz(uz7%nDt#YzkUigHY2)Qg#Qt zd}FdPxl`+jHz%6pEi}k!KAK}R+a*LS?i-4MQR$^|e<9&M_Sni_9r=!N#9^yZ^TRzAf)@wSMtScRYnq33naft6Wq;JrasVNo~>U^j+>(MV& z&}O*apKj#oSpHh;MQ-u;pvYT)=+_}lHiq5Mg@UDd{nWbIqqlS#TDK>c+(C$4&dWBY zd5E~o2=F@|dS$f)hSa2}S!5oHrYYnlIW&RU#zq*(2RE37I~$LU zir{i&PRFFBob>rgX~LVEklS4}HtShe<(F67%jNk}Nc$oEDSop#-yh{XZ|P=#8O6=x zyo33S&OMY%Pd7d+q$lQFLY6L<8(2Oz+DB7;JeTAwN_Ew05*Iv=6A zJjana9ip5BHe_3B_bln)boSSZSEiG}>~p=pLN4bp*!KfipItYxi2=C6n6Pu#NL~)UcXa5;#eb}@9y*?c*`i_QQoifWW z&Eq(UC08ki$sCUYUSlHLMNtGbA!0hf9}|R+ zi6Hm`dUsRf@SN6Irq?4VN1^c(x_?m}<>fN7{CM(RoJTBnJA;D_Fq-sl{#%U`xdYnfg&Fom*dn23=Jwxr1ul_$(4O5F1Fw*HYF{aYwx7)=i<7dsy;O)i+cm~o9iLJS|iI(>m?u) z?YCO2zvWD{*Z8;8wQ*lZc)N)*VwV`UjfCyne>>i*t@&J#+YW!LUMsXjbo+X&ZwUmnyy?}tGk`SUT;!=Ju!ns|)pU$~q^$%T2FX&l^6_I(w%4;0NbT|2<{D>=0iTt9fN%5oOm+m4SV=`o4) zkXiK5Po3@4-A?-oU3`dHvlQ@=Jmb8lkYyAb$-~&F#UbK^f$tipt!mnQdZ&;MYDY_P zgNcyPl$?9)qyfCGBWWOOs1;c9-dLT}mOm>jNV4I`P)jYLdn`98y`+8Or2d6XwM{Iy z{vnp;Lu`QZ(iA+0_fQm5eM@JT|2yatA|?Vq7uxr?Iui5@t@NiC|%^8^ag)nxwwd7JJe z?jCX#x-_zncnTkNF7@WS1cxJ2Uz#S0$7jyfH$Aq<(`u2S_trgoJElwTH9cb3QH*|7 zKnW^o>+~rWtd4}65VLZH`f`Uy1cltV+7ai{vGM6XpjRr_X3bRB9>bw7%n3(|l9Uy6 z^V$t+TJ+cYjjJ~H9v}pn+~G%5K_~(`-uXq<*l`RF@n4Q0LZnh&y#=E1o-wlJBUCI;nN|( zYb`}8S+~BM;&iq(y0~SC^832m52V^eyyml-Uykkrs(uswwbH(GX)pbgo?Ldf{{W0M zPJ1%jgZ}`*W%@+ek+j?KsIQXl#q^`0C&g-`EVHE&@wX7Q;Zw7is_n=f51 zuks#;NXMX*xa)mG4ag28EeZ-r=tXy0e4n`-{X`{R8JcHl{!%eG6Z(kcaIXD6@}4y~ zCE598w&VuXfUuvJbuKAi5U{u3UlsoVWMZVPNefde^9dHx z)qTZF-j7j#xsbu9SE|F-tOlPt3@T5E4)7>!|ixn)a|SWwP`H zJ(0*Hl=?~Yz0FP)pc1xCk@;CHnnIKiJ%aSgp&FI)NQ_LCn##zFiHRILk;}fsOmih+ zKCGEeraMRqIt|NM7d~}5tc@wjtl&~^bLf7kzKK4Wd1ul803(s~`^f&K;quAcdlwrq zpX~cGol>F9&hDD)?8SL&^jb=essrOc7MHIY`Hscg7CRJi7ue~3IXb6WYrkLcKC418 z(HmnE(DT4`QjmJr4Ju?_S)VT52*i__FDOV$65=2d+lf+2kJ+x2^emDatrg!T<8ZP~ ztuna0X$;7>s-?wCi8j=3u%{z*ur*Unk!&1{w+0dw$9Qfvi0JL!4J2l1j z2K<=@$zXG>5|lYH_SL4)qpe#}vtQ7zoMu61@yx3CdYHGCd>mfwJ?8ZOzV$w+PR(ms z5tG2DO0t&?B&ch9=x^++70K(R)400Qwe(lxLCBju6t(C@41&)B=Za1LvkVy3c!PI;7tGc#r zmvWP9S`d8z{{S%#BX{%y-30GSb-iShnI_ah^7%{{=t>oibTidU44nz~iX3>P-*9o< z@$?&>X~!Q;*bO$cb@dVvufFv<_?x?|j4no*KBBAS7{$R-!G!zd9f>MQO^>#R9y{(>OdCq6>$wYCD@~~)a8`S45M*NX$xt}PMXwv6 zu#RJ5P|ics`Txbxt;JEt<9jP}--Mn^TRHk3 zueB(n=Z`qzv5fgnV$`t+F6=TB$#iT?Gs)qSSjoQLx*;wsSbHg$!&R=xNv)Lou*5!3+#Gt+X+(AiZsl8M{wT^AOwaUVl(2y!{cuM0F6{_ zd?SpT>k{u*<-C+X%v$&_uaBwBI4z=f8!DwAiBj<)OS%656HcS{RYuS8j~QalFkb8b z03mX5>5`l8M|FFp^(U9(nSSC%f%807m!o16j)zLuVwoYr1CBjFBDy9{FUe4=bzB+fkxmmvtu;1agPg-2+A4GM9) z+i*V>y4Si_#LJ>@vGb()xb^Yrk@svk&KI~?4&w56`GjsUFNZc~;QM^@4oRPuiZysN z@)Qt-H>Qdj)HzEh3nK_eKZtD&TCAp1o5T7YN%X_?E%g=kVT0s-%j};S;~9QC+vgVM zXN_e!BY4cDgrSoE0PxsbD=OXx!*T&Sduxp5KaeyuHa&}+)SuEhU*DDf?{45Ss>kNf8m5A!&IT_YaqU&f& zjj6v;X$jm2C(YRNnpW)nv?ZHxpHA3oD-Ze;8!j5;$L}6HCyIV7e@vF&ZN^+$A8kl*Y*_S;)m(HJs!hLMZ66RXG}oZpyHRoqfi-ar zt!kfxl8auMS^n(j{0@I}h}?1aJYER#$gQ^v(XsAKZJ>oaDK-uSTwLDWd_9Jq@Pd8% ziAUV<%Uucl4(3kBJteL)oy&3jGwKeph%+2uwCG6OvW658r4!?8()(OIE_sqojA-)~ zRljKY?lj+&nG8Tm-o2g4lU9Nm|pu^+TuE`@kCyx+ircnR z*P6@p$;zGR`bg#-r0rJ;?`}b;%Hw^&$<4&$`0El8D=~|c1uIgF=Hu>4Q0Jyf6b-Fw z^{-|4zt8I4zEysoA6L^q(ZE@*QQso7ufQ5uO;0M zD*)k%Es)?M_a>O~z%>HR?LU1xqu89I`HtE54?Do8$sg(@;j(hE#l-*sE}?}4J76pV z!LE1Y9}naCC5!8fzpBa7@P8`X(T*yUNcvgYuj`5R^TKf{#lrf7Ikh(Macy!;9zQX^ zKnUIhWgFOE;;$p*Kk@s4vA5ykpQ3pG0E0aPOZih}Jk4eQ0Jqm9i@M*{Z@s@zm@Yqo z?*9NAUkrW=TjP7Pn`7}fR~w8MV6G5y12foOTnVMq&QpMu&Y3;UrOj~x%f98 z=QmwxSd_aRGtc{1!Cu5KxPMN(yEWXN(V(I}S>8z&9zT;{vR;~2xbvgKLrpl+w|@!t zk+oetrlXDdETez3eiWZ?p>N3iZZf0Z*M3o5{;21bzR37}Pl$dWZFSB^7^#wdN%w2{ z?l*yF^Ep}EN@trBZXcZ)y=q__(~C z9|g%hWi^cXED~PR_Z9~c=D5ypBaI393_!Ft!^&+<5;m=Es9bL%<55ZTJRj~l&Ya=0 z&w%EiAF!|S%16*Rzg@E>a}+Nf9qv2AK25g&0Fq2?tb^;ETm?%GF|dKRKrVdi&AA`q zb+1o+$n@PWf9g6wKpP`da~AY8-jJ!IOHS^+I$L9Nj-0&>sk4i#ihV%KS>SmU+|AP5yh}=yu%MP2eJ+!3r$7nzX;ZSr zk~LPymnY<~xRXCK$XiY-JE>BbLW)%Dn>vIxi>XK01z2%5`vUpV!*yU!Y1`p=%)7 z2`5Dp&{EjIYafs;_8f2$+J%%gv7=Wk0`|a5hcP?;1!>Z(e1zN#2IMxdrx2B+%f^ov zkZmzH1I{?0+Co#}lUW$xSY5K>E#ggV(3`0%DG0Jj>J4oJu-!3TAfajs_W6DE&^$4t zuu_Gn6eV})4z@Lun~LaZ#WAY!ic$)aQjonADoBL;0vC!1I<}s_2_mo#0#Tfu3wNf3 z6fWD+HPCodBvGsLl3h~VjHIZILQ*vM-nSNS1Am)}_rvI|pn5p8**|?@gGZqy<$~El zPnGKkIOIjle2|MYwD}O7O=d{@X1^mn7U-*Vy{iaK2soMHM6IA~07^mdro_a_lHs?? zSQ^6*S+pfckr;Z1@{)8Pbz!_TCfbTm@Y%PKNHvCo10bes$&RqE)T;y6-9h#+iOS=D zo`&6u))G%u6cB4Dh7&hF<`bBPkP>7t2>tV8_?kD1Hm0yC^Q^B>;}_}(aDRO-h6BjL zcRRY%(vjqsQ)*`feZ*+%X5 z0Hh-Il8#JG3BLJSZSu{9`0a5t9~Mqh$i>GJPUXf*R6IhGe`Ph|!Q~8``CA1%*&u_w zXFpwEk78^lkUstwZdi^(3#K z&~^OrG3>b*cOtcM>N>ZOLHh+O_ABZ#w`KG;V?Pbluh5E{i|w*+m74vQMZOOVXlcTcigZX-k{lc{=WH75o)V1U#Y5m>C;?)BJ4YPEp zU1=86T39?~ks`Lng?jt-HKJk`Z3_xpMMP?wRv2N)iMjNe1z)5qb&X+wwPYl=q_(Vk z&TcjjHt&bD8yi$u%6UYJ9F(tVd2Td=p|Xd<2fmj!7-38{tO!e8H_b zwuvct_YE9_Y?L-wP%9Qfm9-=Aq2^1_ayG{T?Z|?Uiy*&DD&y4k2_yTca%Lsv)X0+& z)hQV1&ONXakT$4vzh1ShGdvg6Xo)!@y`MO}I;7fxVqv1tK2(oM3i+j#r9hvJLe}vn z{V0@cTWi8ZY~HS=mdX>i{lwxu$>!YOh2t=8M9J`lG`SPh;vSDFFDRnz6knkl-k#-n zd-bB6mvX<1X;8zMZ$?+v+*-VsZICn|M+sEpy)AO+j&v zY{J@Fz8N044Xll96f`uN;dW!)78?A{yuCy@Cm`d|c&2}n;ki6F=3ybpl^iZY9BFG| zqT*7dp+M?Is#@G-j@1dLh7)ggxYOkpvT|qAXVnio@6KD0@!oaD@tDl)CS%FMMZ_)T zhuU7?;uaq2q!2Y4Q{OQ7{4~^M2Jpl_EAk0#Hm0!91HQjl9P73{%;jCQ!Ej-Me~Y&n zPi6B_oexX-mP4V~?cFIYT-hqVq#r7{;66D`)G5jLDE>wA3+THLM@ju3_ctf?5yP{x za#A54Z^PbFc@{mTA}UuI+qlez+I4r@Q=+YK6e@$A;ipa5g3L?q2b_iV{&)F`E7blOG#Q(QU;ppYQvA<)s+5cg12{^vb~Hh z#9}|!zilLQtcMk60OI(>Csv5u#LteV-b-OBQ7s!0q0?GhW8*FUmYHuy$y0b};g72R z<94f_{Xy|A<#ziv*Dt}w#>9!3%13X1pyYWa#WvfW6jGI)M?>dbrwzu+y;n^7KIaY2 z@)DNZXBCuMA5)z71n&od1+L0wxF0K0;JL&(W@jkGgFhMS10lpP$^QVTjXVl&FVfYV zM;QoQo2(+PG)<8@;q)M6^$P-^jteJvC`EZI*#(u zf7DqCwd@oWM@r@O_axzB?n`oY<#tB_@-F)KPc6u&@_dgq&Q0a{eTOBL8LWam zHKN|0#IWj7WPmi=Z)J7$@zlx~QAr$+?suv6XIJ-&>UWX%Z?N5`k;pM^&dOoeGabt& zFtZ>Tz+eX9d?K%h_c~x7gzE_l_y(jI$#Q67*LJ*C{GW zlz0%plsW4ppt#PD<`X*Wr}{| zj>wN-d6Xbf@i-e#MI%`ptY4;}$n-^j! zV(__1DqgE3>wj(QO9=#Wa%QE4BcQ$Oah^!wv9c}~W8qSJC}-J!L0^=SB|#C{TZ=!W zB%A%zw6-f=p{dfFBsi`wk&_{|-b-)`qV2mSvB(8CFLPBI@ zc`PUpkgblX3UT!UMZvRa)KwJu4>!>lH0Y$dya(9BaCi}!lQ)=6nwcbeLN+|M6i<RHd`{J?KgUGj*)$ALn8Die#Dj&z}0ooy5k}IY>qVG zZ}~rMaV(Xi)l8{teq);nz3%Y<(i4@vr8YT4D{rJw<&3^i&0)5cio!8_phd?pg(lrB zr2=YStLhTklwMXk5b4~Ln9j~OD=WyJTvk`H#BL34ZuaPap8-}i(Fn)4Qnt&b(C{xN?l0gd)E{**#=@v)$PXn6*q4u>Uyo#F<1nw|;9y<|mglP_)u^_j zu!~p~_)?ous#DHMY0B8c1HL(%Wk zWZhG-%eWscImahua@h`Iq_2@Yxj81;Qr4GN;)<1EZ(*&idYpE8c=(xSvQIukS9yLW zN|4q=HI>VJqEd*BDnp5FC`hmgN|$8)f{Pk#U6uelVf6{6+*IxznB`Hi&$8|!TzG}I zBshIvT|2icW{0PZE$pRti|)qMpS9+@0=T3uPtN%s3xn}!W>#a9%5E+X5SKQg$xiee zlx#E?C&*J<_t&jco9w%bar)M6+^dgveMs%M^nLo9D2MBwBt>3?^N%bVgwpv0l+~qN8EfyyxT}HQL{gi|HKa2V=qfLtjbdJhQ$S!s8jc z?yct%J`{yGs{(cLu4B!e9OHw}L4dMKB`pa?g$PxSltKWTsyA*R`;b#H}=?ydJZu} za8~(F*>abkc0@L@%&pLNi0V*nBgaK7*4F%TXWNm7dsCCh#(f#QdlL|#J#)NmRfeFZ z{VPwBet`bSuVwu=_O4&9_IHUyX)7NUz7Q-dbxK94>UoRf*^&LWx7`8Z{Vw(?1-BXE z@!mH(6kK29dOZ1i+Umi+DwXID3p~ zCX@JQXj3TTVD_>U6rB+HN|&#g03B*E_q+KMcR_UU9^(69gP8M?;9&9SZr{NRi*2K? zxYk*@I4-g$UZf}J75@O}c9W1$dL(umk_fX&3T+JbCQk3p=&ddns;@3c=C|;BB&~Nn_9m}#^9BYt$V@?dSm+V$95OE~B=*}T!DkrG; z6M9D1Kr$KQSJMv#ZDJwf_K|WBmICkU|_wN@WWCi&6?lUmM!1Z}G_D z(b%ikAqO$ItgAM8gi3DK8hk_CMzlQT3O$=Ee?T%g1~Dp8$4QflC#R<&%|6AI3WR3! z&~Nwq62y{_MUE@U;EQ7UW9}!aQ6M4Rdi74zO*`ct%u-ywr2hc-D?!$O_(5SPyV2$> zequpI{k=_XY)y(;`%JwW4noOt&wx#U(n;!4yWjRyWqpgeReqj_`DDZBmpSIgs?cqE z^dhlutQB(CsxPDH2b{i0wB7^38t9Y%04lGyk-s01vek}~fAV>8DE7gvz%Fy5Xe@E0msJ3fw zk-c{413C@DMNs9w$Ggk=bhQ%YdbvoMu7& z)|GSjnylK~ z#>0%uj-cu`k*KlGU0?&xseUPGae0k*o>$a(W>o~eVM6}FdVIR0V9TmsLsNg~>$pTdN`W$~Q!ltrH%y1f{?oTCpB3iRGc zN^S7?c~iWn^KSv6Tm0*xsBjw=KBy4~xqNbbm*cTCIJn>E56f@At#SNM{#E>Ma&m{d zzbO9S!gnfyl%tIH0P_hwv*#Fz+S%3lQMsg+jS z2R}^Uvf#A&o<8Mvx{_<@pYcB$e^WTvJ+|*Ra=1=2CYiWiHtk|G zr2%_M_E&)FS6*Ceeo#-N{14DxD%-YnVReyll%ktVuDDlp)5qi`<;zmg*s|}^!^8`U zvs%)WM698AIcw^hyO{ptb1ZDAi`BakCVcCbBboGYMTB1o-r= zSEXWPC%+cKF3)Aab|?}$T8;MeA@?W}sjVog>j7HU)~vxTm5^+yk(olAB`O{!;+usN z>@Q`-TgbVt6BPVsq{~!AGUIMDDU~46+K8M^7 z=@E;Kn(hyH{-B`pY|nf7eAscZT&uZx32#fttuI;T<7}3);Q=X4m8bv!EHtk?{zBQ5 zAsQtg2*;}F{S*HHayq|?DB1aw?tRsPo6pQ#ggJ+M7}5>POGd{OJ)V7auO(84)bD=X z1`)^}!6d&g12NR1&agbR{)!xxi|`#5}jyiBBm znDQNAJ1wrGZ8J@MOkIv|&rar<$tsa6lr84gcL=fCDI^}2BK6SHppgrnWkh}gj@^EsURfH*XSs7OHr6|Y$}OY(^({zBZ|^-VOjlO3?!ZoV=b{Kf zg-3FTE>@)(Ggn5hF$y0>wKXty!O3pv% z!AEuagi*>GmVyffDj<+G2H$N>@$*F1>S$)>UPGlKGbt!0>bNSksLM&OskO8?knf_v zl!(!uS5>EKy7lm_EjeenZ*Z?Oe2bDsY{`ud8EvEfQf`$Gw%XOczNA|2A#L(qik~;# zyyvO%-zcD@5FPY)1mB^x>!H@+DxX$UZF4sI5PXL^$-1>H{L3Vd-&MZ@R0sW6n6;uQT3+z+Q-hf_i1_yIwdFEaSso) ze--n)rR}f3S`@cZ?1y2mp5R>OHzFEwsFK!|tPq{H@g6o69et$bmP*<)9)~yPc&22! z>g1vS08vW1AGrYd9+lMUarDwfU7kbvFXsn+hfD&WPD+fm# zT2JkwLaJ)P`KH4-dk`}W@ruzfk;aImw^pd1? zzqW|?rx8WQ!S)X*;9xM>G1>C@TBLrF5_|1U9w0baXrKVC;|JrX=+97&*&X!N9q-0j;b@Bpm9W_p-Hx% zWg}+GYq0HFo-I6pJE7W6H{QM;t_{qxSqy$5F10E&tbC6~LsD2Iwy;zb2n6&Z#-YY? zcIn;jMI&6F8D8~%6YPB2<$u#FI?wZYyg#^cc}vM{X~E+&5tSZ8t0u|yq{bm6r_W_~ z^#1@QDPztU{-64ue{b<*JhK^;cXU3MD{^0 z;{7z`pyV0Aq`oLc_m^Zo>CS3K^|CGFe2nTg%p?EW)_mi&%I#6Ex2#4@<$ zUaN_0f(i9Wxk({iYWqh=MiY(kuMSJ2{=JTKk=ozBGNM*z$GKWR-VT189v8N3^BFka z6%lIm`1^SYQK3oPHCxd=P{N5K`-(4pX}dU0W#wX7lOf0M@+~FT#eZ?ybAPA@czCzc z_Cx4jxAHiiPqJLSE{B_c5>v?|J1$4fNlSSKq&AQXpp>Y$bc=v`Ta?xKKD_pp)tPoD zoad+Em5=!U0PERq{KD^H)|YjLA=zGA***{S-OGDN-ChNY=DfeSx$~Fd{oZFS9lVzj zFyW#)@$WR0DZ;~!BXHMhg3ZOn=szLjHt*zXzK_bHu-DhGbAKJ@bZX;gP02R<99_rY zV6!}N>GL@vG5o0+iBaBw`)e_NK`n(g>K#bxR7kP#uIp~=>alpNtRv6$we_s6w(!GP zUfl8yQq0?3(?Qzjp+!zpO)uy(#(aP7Zt#Sc z_MJV?zJ~kT+g{&s{$HHqEkb5YH{Z@dGT}V-J@lwqvZ&5Fmp7;4_)Ud*M;ZLB$L()Q zgqvyMJUlh^>;C`~*g0R~{q0>fIL$cYFR9Ck?Ee6CC14@*NL)4dc(Tyr@UvvJ8M8;? zSLWO;;ik3T)$<-L-sqH61hlTiAAjE?3u%(?hwPjL{NN~e6Qs(G@M zn{O#z;H|zk2Gx3(805Pq&VR?1#xmrPA3c-hv(6(g%dl*SvKreKb8$pZo?sEVVNIkV zYqpz!rnR=Il$>gpRsI5-+PB9J$YUZ<{{Yw$6(r?`M~?$@ijaVbXxrrCNJ@!1Sf@&} zj?Ml(9&lD$sY;d_z^*ffxyaX|VMI~n7+$W_mM<&wh zoL-Hj#siaQnZ=ImasD?P zq%ZltN%t6xtw8j`h|y;HUrHw5$o6TixYp#u==^s|+ZUafM`iL}5r^#8W%7K7wcPW! z-2LV;&p__3IUJ16$R+tI47T<*62Hz_zv%#`+E+iq^VVN~mc?!_{lB>AxsQ)gO-?%Y zrkPU6`gp+bKGkF#?l)-rlP|p4*sR zTTS-=04E8CoyX$xt-hyLsjY=2Z!+=<5R#oxZf|3zS7o%?rn;b(&J(BGYZ5y%`eS`R z_A9=f@9y^(`lZayfyTIp7>$?W5OH}|apq*TPg^gL{ADFh7Mln@nosJ~8uBmauat6n zI@R5)(YEvbFtPst6WKq5zAdM_eXzMFh^D`_y#D}#MP>Dohu|H$`ldW5u+rqr?H?Rx z7c-5@$U5VbMp4zRQkoJT2wL_8qiI({PN%{j3FarraH@M9Y-#8GG5-J(v*%n-*mFJ= zEJ0%j)mcZ7@{gyy?{fQjg5{HOkg&5~iOU(o%W@=1F0a8doK3%vf`hM{N5)feF%3?n|(}YIX@r%Qtn5U@VsnU&$=f!>f~CI;@kcZlXk+c zlhF8z?Y?=X4GCA`x=G`w`+g@G@Lon-g*G|9u}4&PCpDbz4ojZkGuV%k$nZRb*nE;3 z`bx)^0ay-gxW3V;PM#x5#^d7Wp*U%jhklK%i9MX}yaL&|srZt7sLSX5x6 zOp_6TACNSO?Jbv<%1YQ$(&)b5D)gE@Emjm|my^ve`DNZs#|x>4-rn5Tsxe9$oZT1QjGc}V~O|goAFswW#*Z95U?tXd7~17To~x}V6tHTh9&Vm__-JMOg?lyS+pmizz%Q-jA%*b(tHw8g0;R?s5 zmkTM=)3^mn>qvZ)hlai+yB1#?yze(b3UdyR}toUz9)s^v07IenDjiT$!{(#Z7Rh>(jvh4SEF&?7Ov`)o5MWk zmia}r+Txc&#O)7x*WD4BjXMh78zY2i4=rphPBimG*r;v*iyEr=2N(8jEcY(BK2>d} z)3GPX_m{T#IY^0<&h8<5A9^|q&9<%ENK%o0o;4bHYh_oFE4kWXXN&Gzk>L!Kc++vJ zG9k)BTw+Rk6fABA{?SzPO*|KB3Tr4sD*iW%5Q94VhX|aC*RcoIh zDeXNe-TF|T-sCF%M0*j*_`fBH{O&QHV>uDEY;r_(h3rw(0yHB2(OI>vZ7=g6dfQdq z`b~qWQ{FGhgE1BDt*R~VJ`te*0J56#WLJr&&O}t8P%*QU~p=Mp+>^4tfY+l4P|1YzKx-_#^1_SZ|$W=60wa~FqLQ2A>i9c)xB zZ?=W!A!k6`Vz8pbAzI&P^%rm3Qu~8Zie1K!alAvMxDt`OrQKM!<3;yD`$%N2Cx;5& zNOgMc*!uiF+Rt#>ts`dfQJVlZAM*e_q7U0!jlDuyTWh(pvGM-^%rho5=SwBkTd%&D zm8<9j+iTQt5QfH?H&M5!Qo`^50GJa?`-m~n34pY(dge12coeoAKY2CFt0j}Cb;s&C zXtGc7b75&tiO~pEysBO@m(&umM0%g(Osx(DDd*Msx9w7O_ls8iC+LS1>&J28vUn)S z^;Ya?q;*ogBy{_zQseD?vSfCuOQ^}tb;Q{~YDXZ3kfUK|8|!}?UbOhz63aCW%A3Lu zM#=;lPUz8ILuc}pptpZ`U5zE}%OoJ;hf}jrVMcmO%_cfyHn$wPP1KgsRBiWDbkgj2 zH)YX`a!@^$`laK2xzr)WAUK_b{3;2*pPeIzZ5nB`G5*1?yhE$9xCt%ad{*X@TS1gMwmV zaqQHTM|UF{M4y>pymp}3Y1*N30n~ZdTgZGSF{QOEv~qf%FqM)?tQ}e2-stj98^NSx zc#_T&EV+zFZcEIz@>1J}Qk1Wu@-;{C?kYB0iFQXt&vFu~mE+vzt~KA@>?b1?+?O;= z##BI-Br?KUvFZUzMXWj+?6-b3uHgJvQ;yX69qMbz^g7xHbMUzs?W$iSVDCUHP|7;4l@Klt zw(%7ne~7LR$`Lb!FwzJ-eFn!e?}Mzx~JDE-d2Y@@ZVRDFmU+7aUs+A+ck1AK|2G2Z^p*hvs2T zoneG^oW~hC(A;O1aLgVv&$%W|-Z_WByYuQL5}68Rq6k*oDNZdSy$$o4iV5WN`u;ny zIcKr-SF?Sn%g5nRv7N>FjyUcOA(r98afxe~lA=n9ZA-A|E#+K>*U9x%->KE>{BvnX z1qtV#%pq#m8y~8+$vDima4aEDwRFUs(%tYKb9Ax!0+#dyd_w zUbPgNeZ9s`+2yoz!=1FB$K?M2F?XEf@l#t$+ibU$bq}E`@GFc`qBPYc*wy8H<#SkY zY@n9~I7=#N6D|qd4oTX);p7~9mvKC|BXN|+M7)+Y8H5Hxg6vTs3*7ZPuR^vpUDw5> z_y(pDhj}JXGXDTl9Qz^48RofHI4Ap(J1S}}Lm$Pl@RF3Q-AB<_btId3`zT*!PBDpp zc*naW@|r46{`arC-TNF~Pbtmf=0C%mc^q~m2oNyrpt=*dtU^L}6R|pweS)m@b!w(B z$31_vtK=`Tb4|6-zxnGOi|0M0pMb#k>xZ?Lz{87@!^`D47vOS~e>0QNf zcukx;V$I|wC*eYcje?2lNui`>Sn0PUxgnsrpDvda0^S;wruHNc8xE8$irzX8Yh07b z*SXI0b|;TWA-H#~~hI0AHoFwqL(A#;5`7h>k za<^ZuTufmZEu|#w+q7zW(icJ==a5G$B@B83+T&RV)B<6;rMuNBZEF31(cbJL`^$GJW_CXiNq4gTh?MEC-s9}!c zMasUD{7BHFI_c?qZcze*XEQ(gMwbU#^#U(}@mz6I^7FCk73QVUP17crx;YudMLAHbCvfdlQ2&1nRl!2vD+{0vZn7%I- zEIeb)GdfLz{WU+UWCd&Csj0?`6YANy-(R93^SQ3r6QW6+8*xi#&}oMD?ndvAR1YCV zj)a)2-b=6ua+U+mramkAnoC73mo5riQPD#F1FbV1KAlM|=E1UZIX7|t0Lr9#Eyhy) zMYyZ_gp<0&>!7UrNH6LMZA&7s=P3;$G^b?b8$tg7NYg+NfqRjCEGc>FFTi4*rr$uf z^FNrnU*ijsPx*_I`#wKt}cqARe6zp*KIJhk#xz>v0mR49ZlTi+6e z^u36!DOSdPxvt~c*}U%ESdqR*YI^WaKBBlNS?*s6 zb6?L$Ty(EunJy>;roWcdrzYX8EJYyvCam^gkQcIQ*FVP}SqOpio{%?nDn+f<;^b*jmlrRo zI`>fD{q+&`2U&{zMwDznFbX5DLqPi}6PETDVU(WfVKL8-n3W+f4HS~noo?P*3sW`m zE%#!NWv83~vON0-EiFAA2_=12+;R@4`sqS?N(^Eb`jPgzo-O4%z{*X9Ehphh*>SeI z`G9n(ty;|ch7tLd^(F+oXS+T&&3QR zk_GQ_6fAzqv8#x4{VY#wb-)zIe>-f4oM}@XT93k7O9JZmUYLIxzZn7h>`-zycspX3 z1CUCj6<8D~?N+`PRaf2d>z8-_BXskSZ?Q6u%zJ+|A#-1I8dqY1+7P81`+c;8^yLTK zemshPgrgAu02iAi#DO|$T-mvWt+ee>>-mrDr0$}}(GF9y6iW{-;@D#ygPoFsf<0v| zv$J*jTwGeI?&2pdchejUWMbu`QBF!hFFTXmdNVDo?6qV zhNh_Hr;(=w%cC3Ie4A-$@8#*1`u_kUeLDM1>vEw)6`JX+6KLGeCzL~fJ%Rasx`zqV z@TKD)F5sib#=hAkuruv~mYj|QglMY}2fpIAs~5LZ$@e7ePmJmaT&9qtujaA0+G)Dr zpQ&AK5{vA|O+M1JYGYb-7Y6?TjZH8!t@@@Ci6rjn4cSQ@D6+I!f|sL@+f!5_)@UP}Z_!> z_zAb&=UcOmwfZp2;`9FiGen;e+{}J(59PAd_)r9_AnmcGY{VaaqSpLp!DZl@wIXxK z4hJ^{4}{cbvWjadsxKG-WanquO2cth<-Z{cwe%0=`As<1{gr^8Wy#mNWd}r3DGthb zhla+Qap>@OmRR;+z15zWCw=}v05h)*3pjQRug3H3&E)7b6*04l{`@t8cnuQLgh zZc8!crE6Jr*58j{FLPi)6 z*d-&xUYphVj{y29AlrY({{SMdtg4m^CmYE)KOty;CeFj2Q~^L~=GSn2#;+^n_=}b! zIOphdn_N#Ns{a7)!byH8btjE6Asdj#s*`&s&WCr$p=5IymU95WiP`bv-$O~d4yN@7?0Tw6A|Mfwq8ofzVBVp zd;67E8vg+Jb@6JyKZjTNn!ou&k+vQ!tbaly%)Xtt2PlU!JjA}zr68eBw;{lw2=KWh z#=KwoFaH1vz9F?-r57kq>ZHz-<3G#%u7#0s!%OOq&ENGA{+^uMwNdij)9vKB9JhqD zR;6XW{3UkIo)n*T{eJ`QZ-Rd==zOxDV(6;2 z(&(3Ie^1BoKRFz~kn-$)LR41r&qXY%%1Au{_E*-N8mnTZHHuBDR~KM zQ}bpjORjZUI;#G4(nk^Kj%IPsayaCDq^WCBzM3kZZz65@gQP-b&+~7zboh?6SliST z=}Z>nNNG*OMQ6w2G}4&Aw1)0OjG%43vN{0as7K4_Yrz5@VNE4TZTCSSi-!)MeHyl( zpc^Y|)DwxsuPMoJIC+_8vH0Ai$js%kDOTqgisQ*k&ZHd*kb+1aCbN**b!)lBbwIUs zyzfb$VmM#X&(gm&;kn=TRy&q*X?Ru-$mC~DnB`!_=n-Sd@-%4E58O_+tJC4q9EUsQ|H;`jljPImm zAu?P;Pd2ptXdsY5>v2{bpUw6-?VtO0t09E)yLId9j=x-$JG><}FKy%d51@ZQ{{TYV zL+K}ub8bnG$8tt_A2Y~_H_1*3F|o|F62xzofEtquu-wiT%w`vVLHC|56Xko_vGJ8I%yDi{9e^J+=4tdQJa!sWzX)xI zQvHqfuoOkEqu7eGBy%*!D2o38%fg3cwr#)A+yLlF+tQ00pr24Nu#ECK+}|4wEycsT zm6GTERPAUWodGu_k>{ma>PCwmSUY7rbUF7g%lq5wxDmw|OD&D5Pp+b}Z{wh~>!Q}< z^n12e-LthG!06nEz3ez7$>FeHf^1i|O3R=VURSGBn()e=`Ga@nnX%-%e4 zZP**3!tU=Lno_WZ5$wnOt;bYDGG93RoAP3GVv>C|G8TmN>0oN&sZ!9TlalTQkIJOt zoKhpH_fU-jWI6_?LG3kV4mK7dzFX6SAUv17+^QUv$BQ5|u7J0-Lf7)E&0mXGKCGnO z`6T1n^&{_hi# z*|Qt!9NY}O)F%+ZKABN+eG_@0$RX~lzGumq87?k<84n$$z7g?qpN}x^u!e_ z=AT+X{N=H6_)#NPOtG~403XU=*xsL;3+ZoJT{YKBQvfVL)4w(AOVD0haa|abS!@)N z5L=Gwd{hXzqfb)kuH@<3rM|-QF?gk=YV=Z;j;K&dZEIh`q2+(nD)#<_Zy|()8B*hI zme>72hW;UO=hCMQyH`dARaKG;k>gm$u36s1fqzjdLR38TwOZA)VifyonGu!8Vfuj% zK8F+qk_rNJ_tQ^$(MPFK9Le_o03R@*Gc71uwO7Y&f4Z87DKtWSb#fpXKHf@%6s9I1 zk$uVB2tRFEYw~TbrB%E#&qi1C;ANmKz=fsMj;6#PvaR*!85Mgq*&c*3`Lj|~UqKyu ze|1?`UP>EmoI#Ie9Xk3(-3qDd;ELJVJd71Nz(tLS>M8YF0q!xonMVb_8+&P$u7#cd z08oQDm8R$z*07>9)u`bs^>#&em4@B*+S)z5RyUEiW^AZ+%iWWAm~s7ParEWjs3$7 z#-A|s7f9->>q4Dt&@?tO8;?F^^l@K(t3KHidn_UR)@lgYqM_q+N=r~YcG$8rQ_h0^ z`chhmdRuHD-d0foMMrXapBfaj(m~_7)H8|AJ#x7m*0e^h>d+sAe`ybd(SEltZ8FMM#!Y`kjQ;?<{PuG4 z&uM$5+Du%@{I)dDSbU4x~43&bmS^lF^?IVCUeS^n;)i1V#$ z?^xHXdH(b5FZ9Oav-^wmwcZ25cy=G2_X~h>1}AZDDV2c@=W>imhKC=F%!vZ%b9hie zCfC!Ye4YIDwDrD4uL~@dbxu86ZwsqgX?_?!vHlvWi}*SC!A`~XT}ad%2L|q!We%E*Nv{@Ijc`PifOEB_KV$4DLbsc8M{*KB^Ac%O29mV5sI^#P4_@AtQ2vl!3k zIFu=76Wntk!)tNnq$J&KDQYRVf_$sic`usmX!H8|GV2TDmC=5zwS@lw93#r~{sXPK z@%wip`s;m;Q%){iB&5P+$t)>q9g&t*CyWj=RRBW9UdE7?lzt)%8pXcSDbYI z!>RaJ!}qycoN4a5h51Zg^>FtX;5j^&b}MKXUIXXlNntBWi)z{u;?QhGh!x9md)y}6 zUc?kJ^}=8BJ8Sr^dWrj4l$Z6$bC7p`>L&}pFkQXPh_1%TY0HVlyxIa?7$GW(F z+;LuGj>ONy_O26eh^%ZKpo@dCU5i z8~*?!*|u@_q1Hcu$Gn~A!1r=rZ-gEYI8fqHSjA6nIc$k+Hnga{bOB|64T1Bad}rhP z9Ay?q-1Bnl{lD$pbL1Z()ZezbO1~cxVf7g+>C={Fxb&{b=MEQ(nfTmucnp*W+M5C| zq|;B#b?43B2w75&fZN8U@k;qRVG-0Qm~hvBlHTJSYAC0PM>{xIL!g z{5!H7(=WuZSngec;@Rlalhsn6veJu?qv*b*j!gm$vQS7t&o@ zSZOcl{d#p}uP?%C{i0QMWl_yLGsrL?KRxC6(wK zSWm#A?DMP6FXr{nYRVK|Ykgm%{)auwe;(f&p(s4L`VpRI+HULio0U1iJB`8QaSm?m zIFp;=QVdt;ITIihVy!5K;<(fn6nkCe^aPb}~*Kv+4Nv$77JY;@fez+VT9w zWtvRPRJCJsc&S*Nxc>k%8zwW6BqZ1yDVLI!kF<2F-ELB;L(MeT^*QaX4*KkHYx5De zS)NvR!F`dg)Txz%e!?rHp~i8_Sxn}(6yY})(SsgBgVEevRR^MysOwWCDYNF1MaBJ; zPqm9(yNWxf7t!`4xZdLPJT=pd<=H%PT)y_%pE0)1*%?QDI*@w7pLB z`ZxOO;JuR0#^asi;n4YS9+UjSNBiR;0a>|i%z1(sV78A6ViJZs24Vf;~U z?(}wOvBM2!E3-$ld_(zaonAg#*Vd%^;o~RFjMiT%kj&>Y1ST`N420=^gk>K=p(<07 z0voly1clim+6wmVN- z9$rRcp)58_Np%3J!({GLU{{#`05x$N8~jJNLd>#{TK*6DHoqUs`QGsyGqk>}d%wj} z&NI1PspXhyT$ddAi#WG{5X-?9mD1l%H zfEC9703>Q{)y%`~>TU}iQQ!Xn8f@s@!A)OkQ^Q?O5Bg*7JNyqn+_IRHa5swNEPi7R zC2<~M4L-Kg`)X>$r9mR;8d%=FGvx3|)hWlxVPx_zjDA?%6!6<=bv}&$08G9o-N$>A z-p2AXU94AdHL^W`7Z|To)d{+9*xZKla-U@Jp3!rRJMpMh#rb>bfgfi z{$tu|r-tJ8Am--sn&@;K&nr!lgk#+UJG++cxAVBpcHcC_&Ng7gek#`Urc*N3*5}!9 zJFes}N}i6aqtS`)Ul)Xl-_wt7csGA0^Gt6mb0p47i^hvL7@SnY8BR9qt~B9ZhWmE( zt_S%O*xY=W6P6pE2U2!}1<9w}y*Hihocj^YKb!o8e~1%qa-n zn8TDMW#B$H3S_7yB;NZ|rRvLP;+17M^ubME%C5sqr)2W}+jBlamdMEE)1(~Jih4#* zG|)2^;)TxTFf60}m3Iw!){>88Q`@&29$#~#K9b>a_~4GVA4 zLrb}Fe=tIN8s+|3a{QieqGwz1${SN~^*L$Gd<;Zp=DW_Z&CObr>BcRo1z}2AR^7l5 zYqZz4Go>W6(Bk%M@xRJnQ|LDd`oHXt7~)ubJp81*gv?xa*pQYXI#9JGHYFuq6~^E3 z3#V>ZM0Rz2!kAT#?BY)RcgfE6b1^ZtT*mPH0Vv~`RT7X-i|(Ks6|2Ji1$Ue;3ms^6 z?r=QkH!YcdUcf%z_f;La%^bsW9Q0)%HIRycOKvt*e~1qh^#+g3aj@8jZlrjwT68t^ zEUxW$);BNWXB)@E@zIo045xrn73n1I+orZxO6NG97Bo*S7d*c^D`A8(`f%>22uDIzly{i$w?hePle(gQu3cue8)7dkBmAp&-!D2Y3(*5O6`zKzOs5ew5e)e(b!OMJynVysripk5|i|U&_lM7r7 z6nsRd2rftkZr;bqKv$q?{1%w5uP2^v{O@7o$DwR?L%o^aS05`ElgUi4lUht!p#e5Y zzrc2TtD5HiGhK7U4wK`*C9=roeNK{Nxg2;8P5JGzu7xWFKeSgY_*-+)Wa^(WU(}Rh z^HFmtF2R!2_BGY3EAQLVtM_$XPYi2H-+DPrc3nEBDMWr9-99g>-zU#NBoI#r7z;kD04%@RG((t1@DxZG}R4)!S8~z+1ymb*>sRNbW3t zrFF~s)c!x2;h1bjDVNvEGHHu4M5n_-JY=+|RN_I@pM(HB%~|o>e66lhUWHEgD++9k zex5@L!n8t+sLdo*b zb*y_G2`bRdaApMEE+m`XKqlH$E0RQ~6fs*X_`O7}N3HGs)MYM!RIq{Sf)#b4Ur=zuauuDg^Qo%;016sPNcZhQ zrTyf+3DK(lp+`7lkFZlWJvGN29}I;{aii0&--e?tiLS^p(vEtuq2OF%Gi6gUrNyX% z<8I2{SLQ6TLA?X+Es#g4QbYW74&LLGovyT^e;_TPdrz}s4MOE}?)nm;Qx;fpGx#>p z69w0ng#0T@aa|)pr{qN;D$x9HY}!vMOhC|&hsn>}Y?QMJTSmSm!kU+l)XB-@c^@O5 zea5{GEU!(=ZgxzzS=)a!q%78WA`2~nIoi98H;iY0mL~^=G2uy2ea7TVa>!*$up?l( z0-EEmf|AHCMwv>tkD>50x4qZS_Op{>dCm=`DtmAG&cs&lN#@p-_c8BwSC{eVlCjvS*!V06(l({^KNL6=pr(l%Rdkfwr&{lL z8j{xVms5o1_M6!AT@Iq`hu3CFzBLPt^Q`z zbKI}RqbYerxY7A5QN)YBN7a)vf>jd^lq0Q{y^FoPH&rV5+x0D_&*!ln{{ZN$`G<=P zV~ka^a;KfUh$pA$BJ_IxIUJFfKO(O0FLk8%YlF|?IRhC%ms^>Oc`T_LBrta->QOeh zxIX&2<9Od@Std9AsgN$UgW*^(jPlSGfNG-K8JNw00trvN?Z>U+*2O;=8oB zCkb@?7|W1%U!En&IJ~?0ECxju5@9}>kYu9b;4P^e7MtJ13MG0~ORDSsKkhYV-LcX9Wf*QX=L)#|b(E=J_?o(B;*mo3F8FCWQf$6J4uXq5OM zf99c2gw>yTqEY6yW7$18GEQV+(NO}8kGEG(yjh(B#3XT>!o*s-tW zdok$CMmKQzr!$S|W;s-l^q{1M(ooVt+EgqWN@}j(kL%MVgjmzg?Ap4soTTSxs$_&t zQlD@vg{-YasD)bRaq+H}ty;WR5zk+{XP;IYi7j#zglxEQSqT;-li^a7BD;}|jl2hQ_aH9)71^%z`FkcQ5aMn0Te6O z%}Si3ZK-9Hw4c3qH7Rm7KR`=DmE7ID_W{Q^mm_hNWEMX+%*k4ZPu)d@0o!v8t?ew(zVk#3r;Mz!r&`>?-x2y{q8{{V%In+e+vEJ#}2CAiHCec;i5 zZ_joP&QqsEk2&4`RO$#hR{%XWY-|s)uqeM_1P}K~b_coK?o^|=@(C$Umq>ZQ54;0f zhKw>V`;DJaQ{3YxAfK3;>(}eK)vNZCO#a;j`xE>5{Xh@$M-g%yC*M-`QuG7w&+2qf z($5a$J>TaOuy_c9YfxHk?7NOij}5r(EoCWE+>o?^eWupA(zxH5ZqHJL;Ik*#>;4_B zUe)-q;Ulj8sQUrNJ7wA~NtQ=&`0U<9mfCL{$J)&>=0#4p;7KTD3MANSd@G3fe=)yy z<}IzpA5*>gUlE~Jqi~vX{12BHJcJxkb?mnyYb4=pp{S|#AKUG@&=Fp%f1JA`%4>{L z?n!XzmCKbTKOIl3>uE}-g5hO?l1K$eIv$#7O5G;~`G+;CQ+PqPyHS^Q-&TD1(L(KZ znn`qvdsS*$T(Vt}H^O?hA7{2WRtPeuKO@Z{No*hdf|j$Eusk@r&}oht#v=a!m5xJ> z4vJYTKYFi7%YN7DSS=dy*exH9!$Mq3QPS8}fhkDv6c4zu_hT{Dg6xWux(QxA&OZ_{ zEQc21e2of#wW{BX>lWEf$o^ar@h?Ptb{4l^Eh@@XWL0b$L0E(6Q5yzSPxZ<9a!r&aNYxKoB3wP^NHj4C8TIpWK+(HrWYF$!ZjdWlQtA0#jj9)~4unNQTw*Thk}gzoY)o z_QN0B=*@B|QMrCEGm`#s8g%G^q-fA(k{NS(1OcV>*1Xf?`!-p#4@s>OZ3Hy-FCct$fmRX%~}Ae^4Z>m-pOL|Xw%h^De&@bT{U)}QJ8WUS>6Sijo~o!t+M>? z2Z%!M3ob`1Lx;~psH%%M=LOP5oixU8GDXLP?Vk^6D3w39s9M_=^;%g_R+@FLXK!}q z{{T_YSEU8$T-;ZNcCk&i&-)or6!wMUHA(B#nx@&>xvr8dwX3Qt9wo_x?Vo9-wi!-A z+;WJrQn0l3@=(&Le{v8Qx_dm zeXs@}Mr9Xv7bj5y{*m<4U_7nk_S1jrN&27@<00rejr6A~1&p3o>oAY=AeiA>hra&+ zvX!%+`~lBNeMvlS&_=~dX&*lxd{(R^W6BoVzQa}4a%xOwI`~}!(aEhlNgOnkD}po%{q2ROD&7-Y!#h4c#4bIfwd-Bc$|K5lrmnEv0w!@fCo=5 zm0xwy^d_Y6Tv#ctK}r)ZB2NCP*a-*UV@N2xo1B+ocFH2jJH|wv-lb1(=x`U}+KeiUI!s#kExB6g>4I#_n)vG>J8O%5 zQ5JXWOX{`8!l8C!7M9kl1M51o=08xa*~02)^$JTyE&h;s`k?yH%gI3h0BK?P+XUUm zeK#H{NbCBIYRyhQzg)1o{Cu0oioqhpPVbj+)W7#Xeb>U1y8eVHVJaJq>=d!msddS$GpJi#|&{=T% z5$ZIX{H9Y*0NW(2d?0|WXmf-E>#c}Ou1b@T_NAUswFG3wHI)JjOrxn+nFfIzzQ!d40?~MH@_Ir#;VrgbhMoS6{X~{DjQc|S!_)=0R zi&l(&Zl^oF{ENxnnn@-74vc3n#5jI2^+?PJk(o)=OFK{pwBDy@Bdt=yk~k|k-R;YB z7-amHe^tnwCnfa{+>C}FlD8ux77i+Dh|1FM&$lh5p>61ODcjI%t$OdoH~i}H?-Sg3 z{dTkkSk4*BGV0kEC&wqpRCLC5*M1*f zHOg$GbtcBRpOXAebaD~CmC)w5G^g(?A@x~4TXFu=!oUoN6@!&+jCKlinAlw5kQ+jq zjUi}`^CwcB$w(lNI_}|3R@A-4=yjVH*^IAKsc^l%=bg{w*iKp9enrH2isJhj>#GbD24B;v`LQl{+337YkSEwFbZdDvsVWN>3^{$<{*4 z+;IArmuUW=r(<~*CoIoy&&sV$sobEL{6#Bju1MUV1?~R;0;o53IITMsl6Y}qT$cqs zsvJJ3`<>hGtv=sp@v#itW4M=gAbLe%?QQ0Y^}Y}P04jKGY*edqYv{6km+~+CdF3n2 z)RSM_bT$*b+d?^ky?^qF~#G2+~#5bBuE@zuG#QJqe`NC#1_qPZV| ze>rS?lUi?XKGoH>r5~eT*8+dYf5zXi?Wk2hcA2ckUsOuDnmGP{1DC~SJt>&ktvJR) zI?7an+lV1SL0$w`8S}npLwcD<%{g`{zB~7NyHK*MsV15~p0CvR4##os_;+s;$6rjo zJ&EL5NVpf0;#}PsiAzEgbpGv#`zzFcig(<&s^Z>!RzJA=x8%C^w;L>2oZ_2bOrw+M zA;`;dw~|nA;x(^brcCquQ%nk{7yzkjvV2MCD%KL{DB1@|bq3|NU#7J>;@cO<5=)8J z>f7f`*Hb{JG>{$HVX~vR>C&wus@f{0vxWwhl|p_rl1ROiSq*oSQ`${rn)Y&U5tH0L zKQEiZoihUw^eO027*vT4s3>h7nsritcnalyb)$Uv`9`UD$@-VS64|ZpCnb~i)7U;; z+nyQOi205i$Ca4ln7C}cDcnRfB`l}X1z-hjQkH@Fk_GGG`)s#G$={jy`lHpkjv~$N zm_ibbeqED&&XMBWvyAi1OId7UjF>Wz6x*vwPT)p@$IHgL4kM7(+NnwpDV)D8!|n0* zcuT+F5-!4srLxR$w5$=RxiX)&;Wuje;PBB z@;Ztwa)&@FGZ^+i(Bq&56_a4JC0IJJ>_ z=T)iVOQ8;ff#n#2>X_`~F_zWLcM&a={iOH}I#aaoS0pJqI`nxXa`PS7OVU|AwEkX) zPS)OTYi=MW-ELAXMyBc?auTL~gi*{daUAs|J;c_cuZc)AK?eHhow`%>Y-{~NMmDGQ zBY8%-QwFcPqf$pR@(M!$#)}kt@;&I-dV{Ydg_aWhN%MKG1Rq-f8_?P zDAuLEjH{l-%g~55-A-V%IJw+0g^2noq{>iHBgHpJ2c=ssH(854?SzOXQ@z=FZod`& z7Sk*ng~(IoDoFCwb*CudYHv9ZvzwZDaVg$i%$muzQ3DXm50A|8aY|MF-7BNxc=*D3 z^eA`vDNCYQDLG1*;+G|Hf|ueft#9RO>!W>r7I1SiuF1T#Y?d;qWe=ET*2bzzLcF|m zqCLuUjEgNRd*Bf}l4S9zP>%t`p+A`$Rc@ZzW~D^^O-k$Q?1n{2q{K(2s7rZXB?k1xdvWv>vtE6fR%I|wsY^?^=a~b> z#M?@{)!Ri~y7JBl1kODSD5LnPzv@yI_V`uizR_2Hs&A?R&Epth9mOXoq`t0R2fY$XE?W5Z_#4{vbtJ(_N;FgQj@~VkuIP z$OSgGqyuWyxa#B?@>al=<2#wjAe3&$H8BVD@7S-#mVX|p`t=~Wt8>_}ah>C3D+!&C zb}H4P%1Ty|r=FCZya&*GU*uVRQD$NLk;k$sODtuY^%Za7DC|}3ujaK^UBk{Lm3*yp z>7JziE@3WwLD01LEh+?m^0%tjMAvj$*EG;+yrMKKP#$!=pvN+YsL3&MP@+lvw563X zy5EVDl~QkODA0YNZo?7z3%K{hxbB2H$+~>54aZ6j&l?+H;@5L@@)E8|uS4jsZ~nad zM}*_}S$zA5$$`%JlP;b=n1_gaGFVAx(U7>xhKC+(Eebo(0Kc?X%|9~!CFOj-{GQLq z+UVay-2N^6jioM^LlVbx7)lAN$w*a&?bv{{W_!H~LrlaqsKAd%D>!N6td=d4AMS#U$fo zG=>&FU?8yP@k_|ENxt1Q0N2l-EdFNI=lO~!v#zUzQ;fW_)6cKrk55(gXSsYs`0_kP zjH}nR&B?C44RgOq{{UEhjPGYE?*?vazN>p@-i|Sk5jB)fz-~G^V8dw7h(n*GiIpBn4dEB{u-t z^8Qu%4>I1S^z4O|#(9O1!t2qr@RLnHs$Fx8jeJv#(}g!`Mh-G*pi2&$qeUJdNpfB=5E!~`hWF4l>PzXH@XVvJ?PD1 zqd5BT+%gm#TqQix4<6;ef%uE_?dA;Q?q~fgIfHrE zA$I$@n7&+;k{gMTVeD*xvNTGHJyh%NuUg>0@|#M?rF}AkeyQbubI1G7Hs!zZzG*jA zss8{|k~@|3OZ5Z5F|6iY;Dp{W=3}wr%WX! z#KTmjp$XdHM%4qU8u(T0wRrZNwJl0bb;D1i^}_4@rFB;FnzB;bi%CA2=yr>pK3 ztG++=GtYPrBySlaWCY+)zc*+kiEB>XI-G5K7rVi)hw(wh9 zlH0akD;-npaKF@X?VoPD(aboXW;?Rqd%INuQJmrZ%xy#cYZ4=+)imzZHa%7qbS#8` zZE@l&>2KrR&D%K0J-Yq7ePi_g^W~r9ud@Bxe`zHuo62jkRmJ5@zLESykb2&`$RuzB zkDiX=4UpQBuA^u_wuyrTTwDdD0~w_w#1tp{sA?!9mQa~Y1zY_yL=OUl{{VF?mJR!3 zcj4irC24ma!-zU51+>_YZN(BYz$Szu;<)^EZ)yV#`J^_H_={HK+|uYNCj-T1lrB~| zrwR__6Z%ivLQz@CKR1ke8;H0zolC8k*iv*+Y4xE*d3+>kM4qj3T~cP2)Z+gDD`!Dt z_$PHZUk2@e)E^S(d0q*}dyxKOF9ONp^3q{sRQ?8GfjLTTl_UneL9`M`Ux@SO=fUXi zSt_iqFURoz0B`x7HorT_aqx|hn_fs8&dz(0ajdr_<#~Vi7a)q+by>*~VPy^%9r7n4mc3&bdY+(O!c)7c7Oy+`*TiMY zMkzdao~iI}nMaC~&!{WvOS)Ko^K*O;DaA1J2*qI_Vy8O#LKPxJML4B80!gqwr8)4P z23&5lsrBkq{M+P9kl9u*p+q*4-Sxe^J@wjqX9w;vWEY51DO{=hF=Ng(xC zEcEJqFZw_F$NGBuNA5!zj%V8bNzZZIzl_Pn^L7{zSf7ZUDJW7zcBeYZUt@43M5ri( z@Q(`f@0R>B?pMV^UO0I@58^*Gql{8gkCS~rp~v}-`f!Y_?317EXHe79_k`T-g7OqPsXI-F+G;ZJUZmq3n+|EhWP+l@iYdRu2;4vy+q8Jpu8-tz=!5D> z`-(qSc30FlWUyVm$#!kND}DOZGfKayBewIqH-Q?~LCDZ8VUX`w*5`K5hubGLE3vnQD* zZV6m!44i6z_E|SV{d6k!+|l;iTi0d8{Cn+i@^Rdc$=1$OMQ0n0y&IBo2ykI0RIIzL z7)fblt*a?3T9!Nj7d2_i%|l(}uHtF;HY0=RzbMVX;e2N$$Z}EySlDRA=Nwrlc|w0E z9wkRXUMr!YE4DEn$FOg0#+oxH)cE<_PVsWS<@3lq&n%|#{{YIC-|{SMd!-SWX<=X! zb9E};5wB*`;;O4%*B@2f@%_G5%WqaS+tB)<#XH9*D~iX=dx)+%meXoVQu+u#4^T+) zHRQXTRAV=Wdd{~gCl!=VBXjl?yDCJl|Z4X$p#O}lmcDNDGw=r{X{Bysr!Jd0@& z(n6L#7TiLcP$%C?t!ED5VYH+%eQ#W$QS45>r>HCl@~J|m!8TyYoPo87q2&!oD$r@? zTXlzhgbe(G5_hu7Z`D4bCiOoYiw#C!C>OCXS$HR<)HEGBbIo)-GY2bT3;W z!EimA*4DYTcs2ai%f9_yk!^Mb$s}jq6`=D6$`Gl0eor3rP4v z#kzk9DL;UvC9yU$9ax~mMxJaX#&@)zGJpa1&|HTjTF68~mhnoOm1LW4XxIQxUkX)N z!uUYlQCnqYG93fSSFj`PqR-9tK-8fEo9?H2_-}4-IXlZ@c3`mY$YQ<~p~p%}Kv^o$ zI_p<_Uj;j4o9I(-^A)JHx}Tpc)Uq>joqN)CZam}bOm!qQlJd)>AP$KIRsy|BsQAYp zL(KLindj)|=!M?J16%7{ zuRF!gc6mlvI{yHVZ>Yl(gOIn?k1P5_?$bO^Iocc!bAxj1F829JWHRm_Arz*~?QQ22 zP~r&DKnslxFUGx!S4k!_%Swx%>G3%Oj!5m#8_dSaW>cQdM`V&9z;+BJuHdG{ZiRtY zA9Hk;GEu^cVDiy$FT35F7G5`jXXEK&CGr^SRmcATQexCMP5%H<=<+l!jw2@f*V@)q z{()NF{0puYJXY0?H$QD-xcllS=zc$G z`#RUupKX4xJ7x7l+I41Q;kh*a8zxkIz8fVprA!GJ>5jgws@{^a6gC14h}N@=`G&>z zdp1!jrH}J^{{Vsm$owZ-x_$Uv8)KjHulMz2lj)c17wB`fJ-CU+yXo|&lpf8=(2A1Cqp_0%t` z%1hMaZ>ipS*pB3P9ll5O%f@+cZFnX|AXaaLa-5=atBaQ2cew^iWXfO5Ehk3Qv9yaM z0bTD2J^JWS+M1l#s_Fj#BZKGa(Vf0p>BcY7_5T2$Ps9Di0*q9`yRK@AO}8D&zuZkZ z_Yz_?Q4q^$$+sLqR%Szx>_TFhay=A5pAb}t4Xo0naOY~BrNkT$; z`|V7v*+;kqtrm+P$ZWKvNnxiOE4B9^ z?$JOUdKxd1aT>g?zq5MjmD{;#@%~PHX2-n_tm{r+{Y^fu`93|^SZ-yE?eTNrRJ;Zb z8xoL@-9yv9Xryc4FYT@~ivA_x_3cvea>t`@AK`lC@{+oxV@vPvH{R})Fpg#+$}mS zbu8R*%W>4U{zo447)U!FmiiXn#VI6P;u;#O++OEzLg!Y`1^qy2;P59%ThDXMemzna zOHr20hi+7NerCtEi5=$@mNS7z$EnH7A?^i{!gkBL+#i>6Y+8BN1At}W&AJmvR|^!nmM>ztM=2PD;uIb%=gt+p3Geqsa4kd%zZf(kYZIyc^#Cdeg1M>hz zMToDhq)b8%VVc?nYg04u>_cZ0dz!S2Tb!^q~@v6IAh zu}(F`V>*`+-dl9O?3d!ayPv|6T#jEszuWN=C!EFL`18&{FpS7+N)-DJI-(u1 z&@2rI1bB*s?wp=|QIt}uefkn)Fe^U1iE+=Y}&StzVXs^g){-v0n?T}bBREEo4=+gHx!)HJhAT>k*#HO=#H2^X9{ zsnNTC!GE^7g7}GMCQSZ$79%n31(r*a3c2yVn+4 zGW>m*(Vmp2nQ?7;O{sd7fCs_|`|GFTFNcH6T@HJerpZg9WLq1-`QAwh^zlC`!5`t##;wn0^fA6EBU+WqFJ!?_%FW$%=%T1!`>QG&ZGnIeA%I zijQ&aZYH{$mTdlEGmbVRJ^2@mvpKslEM^I({;t{2&xJ-c4$sX;J=>cy||Ci z3(D|VPBn6qujgQBt z7a3>aiy4F<3){rirTR4|`JPI>efyt9(6FL#K24g(Ejfwu0N_9$zfi)+A{ynaAReUb zO1AzKnzFd|EIq4azccO^h1c-N(x4TQmP?#8OMpK@;$ssF(hT=uI7Pj}uxb}fo z75pyiySxa$n$=%KvR6F6`jT_$F-$HE#Bsb$O^}u+N>cJKpp8Ev+ME6K_P4;PgZ%F$ ze&@|Car~NR*RN>}AY9h{cU z-DM_InMiUoGS@GYa<^xx<8`|H_oX@T`xd2DjBJE@MkqX5%p8v6*nQta}cUZvFtyS0T$gojZ!DJ&1 zg^qV5HvZdLwQU!+zEx7Ml!kDUB$0FYSPcfzkBZWUidje?ef6hI+sAVqu#a%d9mV6Z zk?F0-Fak&UsZ&U|-fCa7+WRI-&PJ8$XrA)+{y9H2+zez=gp#KId4v!LTY{=0`1Z`Z zMT&m&L|v2A2N-M(sEHOkX3{`iHT9SszVvlpV$Q9t`J=lH+Z*r!0DSCX?Iv ze$6q<7^j`C$I!a??|3_D%97i>e=NpB#nh#R3*29-lU()OY~ZsdSXZ>9`xb^ibV(@( z`DpaW{tSSDwBJEht@u8s*~S6+JUcJ$xI>Cg-~sal3Ywex3tMC#u0OOwRBS35 zQ@qnFi{ieRx?XluaezqH`&?N3Y1-+&#+*|XvyoNDqH_NLZ>dved#Ci*hmh~@%TP-yK-zb@($uYJI%OXJ*wU0wqH?LR`o<6Xe) z-hEBC3L=}{Y!Fbi3yXR6s{6QI%AQ*p`-sXt3+oJy9W;{yJ81NUj`Vyab?B`sn`K

    4PRU9|gZF}*KAs=h-s^6>uvuo3>iYki%0_Ydxe z7i&?y`1+#9{ZjGKBuMf6MhhE%({Bm$CjS79YBEJe-^eYOsC_uRYd@AMcPGcKFE)`> z_pX?}{Cm8nxLzx4XWHPXL z$1a7Om4+xzQYS%l5--b z<1ju9Z>p4ffNS}ep!?`l@f%ZlZ=q-Yuj9NtBM;2|a6x%jeEVO)3=9uD5wSTO(p*b0 zU2&(><7m*Rgl^KUU&dv2GKY2kExWP*0Cu_b{?vRM`qAwl8Sfu^a-GZJ&*P%;lNKaM z3?)snwJJMm-saK)>VE3=pN`utY7`>79o0O1<3v@jGE1q+* z?GIJOf!9zy^?h#jkGjaWjYYd7wVkNrFdJp}ve{THf)J!K63dqJxan%=cJ_92m~<>5 z8fDz)E=Sydryd>1AH_4kWlb@_6HC$yjP4@;0F^4L+w^%WK0p7Q?yt)1sZSBzLeki*` zRG^i<9z&Z#i1>gVN4mJ(zmiTV<}A97vd2!R*w3bKr@qEV#HD?}enn)(BW`Oop^5f$xM7bW-}C$aHgopSH)+U6!Z*T>Mu{@~B~dLYHIz037~VW?#1C{{X4cyeqpL8@SMukm1~dE_Nb?!>l@m zfpL9oYuNq;{P)9rlm7teWSU+700YZDL-1}#inGIN4iE7Pa{hhA_$~@}Amq7LahTW* zt=TdiQc{$m0_4@#&i??IcyEvB%I&Gg33gthpC9QVGJQR;^xfgD;7>-x< z3gkBMxSmfRb2+&;WIK$b%0E zf2KsI8(B$89N1KRK(V)4{QCI+04SHpwzeH@QMD(JOnPS@+R@$ahUqc=ksi#7A-YIW9tn5i?Wjs63X^9svls3LSc0p4R>V z<2d_gwPwtv=Pann^jYhS1^6c;_gayuYscKKKdCIfp1JMUz7NY+*557TuXjfw$GCTG zxTg}s@#*p%fy{WM_wp@g*mwn@=?+L5uUp#Y#M<@rSHXC7O>4zrC6B|WY<>q_zrAyU z_oU@7#~ELTPKN|??20o_{NlDY=|~QtARt@|bWM8BjWW+uh}^Y4#bcCoDU#4wLV?vD zHPKa3&fRxWo;FBAon>wU93KVxTv05e3nt;Rn+4ECsahrm_D zUJBP>Iq|%xTTN_e(2uCCpHrZIklBFme>k*^nsQ0Q;?VrOo0y0$WT&MppltNxw1*U+ zrH}~;Hm@=MU9Q=2-W9w_v%B~OwbiifpJb1|UFQC*TvO?#W3>1_%w_pED-a{e%0HD` zScr@&TYH*I?G*aOTWz~)0IwI|KZ5VxrB3TQNa;R7^Q~`x%01)Q`Kb5xdiu3Q#~I=| z3>Oi`DaRdW8xIoGttBf}!(_=SBo93+)A+C8E+;~cndoynU&vSQT9Zlt0AczV`eEmv zQGM6!KWqD`%DDsvIX5!IwLd$GZxywa2iC7lx2~5!2E2ol-Fa-LnxCd`Qc03F3oP6XXSV{{YOKL^dq1UYI1(^j&}T?bH7NQ_ubnw_??E zwpr_woPMPL0IlbHd0*5%V?Ffm$|G=(r@q}jrg(lac*ygf=4{7u@6O1oS_+zvBnwAF zB`QiRvZ7VBH=JMa-jzHiZHxDuTY2<;i|f#--1+|0xiwW!G0s24{YzkszEyz0@)l#I zE$5&@oYN^KK_53650s6)K;K^?ZqV!4lown)A4Y3z#lbmr$zSr0Tf#6iTEt~J<|7lI zoWDaGh<_y!sgB54vRgw7a4kzGqCnQI_5MA*?R%~bMS75YuD7}1q_R`F-b zyX|MwtoAWkz9E(FA0&N-<;mmNM8$4ISgx;PR^p~Q(k`Q@Ks$)k3iYpwd>?Cmhi-gh z^=F^_ljk&P>_(Qlv!M9yCkM~4I2`6PhRY3d7>v%31@s_3%TKl(33uYKP`8>=2{+12X1^!YOkl@|5$hhOn!$x=3>YxY2 zd~KksTDg6=bWB3dGg42iO5ZQqe%QRfgBrv($@qd)MgGug;*XGT#rG&L;B;jAq79Vn zZ*AUfW)%MbMa@1RnMtyU)2(0Y{I7ZRWkuf=(PdAS>|X|SJlxJw%yqVG<1ReeR=!5n z(QEvhX-BbLXT-E$`G_Rnr!Lvac}*T{+;t}86iP+)^1olQpQq)U$$pkp+3?OizOqQf zAEy2e+Sl@2g;7BgW#@TeI^0r~0cO*7%N;e@&QWtHwKHD~-naOg}%A**Y-l z974iX?F%Z_sW0VJpXRH%c~1!r9knw%la#b{>lrof~P#4siQoUHDTCKS-d%Utj(C^E^)LVLjy#1At z-ebHIyqaka)a4gl1Q9xcsy!ZOd___1(7MefrZ8_~h4ALHvrQx{+CY&kZ38NAnaE zpVm@u`zlm$*Qd~0-g8;5tcyFyc=GPUbDDLjgfV& z2~v8pq!f#f{4~W8u|A>~dvfZCHb)y7YTVK`pDXxRLtT#rE!D-5r*X?dj=&VJzNJmC zBGRQXoA6y;jv~j-orR4H+gf79_X>6!2u6CWVC+EE_LK1SJ!zOvB9_w7DQ9YlLw-7}f)C+N zc%~?_hgj}Ip2WHrjmSFMdX)Nvr|u@yy!h(n-4ll;Y#(sz{q^N|Yl&Wzv*Eb{=j~0X zR(xs_>V{$R$=A_^p?y;0!V-+HA9PrVcG0v*r!IG6XYySX>)~ZYXH+(%ZT+#BqdHR+-JpM9rZxb6PWSi<0a3rc^!Z`Vi)fqmH@s zwe?@j`z*#TJ6^YKGO|_5u}wVMw}r6gL2G#fa329#Kmb`FTv2#m=I$}p;mPUOetlM8 z{{a0LkMPgpF}M6_vaVCtx=>5}s~CUbpYCN@-EQ%JRi4FhwdMI=9^qZ^;FwlhL)qL; zHgtTdlWnPW*sQwh{v%+mfnIjCS#e(<{ypbq;Rj~DvVElDT&-7CFAu}JR`LV z(n-c%GU?f2^=U4P^5^lwDDB?(&nJ6NHQ6TH{;%=skEXvx-HO8YTN&JWTyyG+h<9tZ zox|hr;`@EfixW83A`VJZ%Plb>c_O{Ll;(ga^tF8H{Gam2%5wD7wI>Uyep5~PHoAQh z{{YnXpNH^uahzqiwJ6w{i+wxx@BZI`z#YK;o?lp9iieNo*uEV%xpT_Q{FgZ4&$r33 zi**VPZA(!Cq!LQL8#mM2Uh7x#FAvGTV#V!8vW(Y@$JafUJ#x|Hdj9|c+|Zn_rfN;R z=RfWK^X5-{ah=b}bB;ls=KNPL#pmSRF3SFMjF*d2oN0l-;Fil)w$e2o749Dy-_eCJ zs=FaJm-%F$^z6?W`9BZ8Wzx1#f7I;X=`hmszf|3kK}r&&LFY0dOR#aJI>r%YKMzZQ zO?h|zNl8`l``7;fYP8q&%l`m@>0kK#ocSn8c4|*6vHEd=9!#7BMYy#|lH68Qbg@#) zK$CB6epo$)=za4DXSvG!^XeyvaNhC9?2dDTa=gzfk%ePF8zF+g%*{q_6YEpy^J5L9`eW;k`8<1@`RzJ9lh&H_&PxlAWO&aZ zUL%w7nF-2cGMn6uG_%H(UN6*_ZhNo9Pb$t}ark~k#W{Lbq-5ZanK<{_Ly7$+$X>Su-u3d|{z-D%+#lO+ z>P^8m<0a_vhM)N3`wysp@zYaRmRlM-P*RdgQGUI0vHh6O_WlPs_jlH_eZmk zyO^#Mo#Kx}k;XDSfwJx|AqOJCc?ossi4HR0a_I`%Q(@=R)4nJCmvy{NTXsjvGwEjRU&j9cC~+4)vT^>a{#iJlOZcajp?+AYufg^I02~xYDC1q|?fH$h#XbN7_u8#7(5k>u9J7*mvWscaB!1d~g6EXyfcTjL{WPpF!g*FmYN+24>ILk6 z+Rsx>i;hc^wf!pr;nuZ;Y;3MakOUhLe+tN1&@D7(SU#9{AGvd;{W|>;c6L7>K9^|t zzG_x;BODn%yzW<+ZzeaXyM(Ei2Dz)tB7HjOR^pUW>2wD$Z9h4h57` zq_!MwwEAimP*RjMx1AZ;mLp!YtA|saB4zSWUS&|EEt?QR5n>gz-%vrTXuPbI&c36` z{H|7FM2Y!V5`-lsPc024B!r7}MTdu#Li;=;k&SI{Iiy&8%YwDRKhowh=$Dv8ICG`r zn_+2f3`=Y}k?JVwKswsFogQL)Qhq%WI$lGBj;dKAPh(-qdK??77DFJEIgV{xRLiZb zTphstKW#TF>`w?m>s0!VpmAtYlPwZ+EkKnS;=-9&D%_-kZV1xaQRBY*U<|Y&-z3Yl zx!hN3dy&Pt$0gy+cP|^or9C1~3w^~i464UpMEL`IR&A*dt>o>xh1) zKpue^FDyKzG~%_tP)YHt4==>3vWFgmjlzPvADB?J{>tB*CGE36%h_2QB%6JsT9hpO zNHjXdMZ(3+;AHY99LQiHWFWtshS4F%;WI6_WHy@-s@qDEHSwhRnO$b2;a5qxw7<1~ z%((dV`t-q${TYmE3G1AiZ+s=4NCyE@yuJ}F?>REc)6ni%xVN&lwpud)Nu0hs zb*V9=xad&$6dhf^ZBD)02slOb7{unwW#uYNrKuo(OaB0AGOJm8#fxZE?Fao2dqDq&JEuFZO0HBO}pQV39arG%>KH}bDMuv&X8B4+?2i}9LSR^~OSrknZ(f8i z(y<=f)R)P0=DJOPc)h zxUWK3Qju+SS_QPNZmR8rP4_3KIni>G^b37JbN*A^c=+xG!f_7IhL?l+6Y3?mBsh^| zCd*IE`!#dpR^(7pI*8srOR9G6k9N5G0Ek)$<)6ohf|?SL$+7<)Mt#6io)(N zWpmze7|+Ne{{RvkjeGk+P`yS;HPj4zvxFKEvdcg#N))5{TrH_2Tij?~C@+k$)I{U+ zD?5=X3sC5Qk^cbMP{PVMJcya*m$k@yg1<7*w1e-p^`>jAg9c|#%GF)>;$HIoVeXr|kFWc|sh zEp*J6AtmTTW_Ra{!hEpqP2%`KTdg7!?ks(zTAq&#rPxM%t4w``@1!n6Em+8LGV&c7 zwy%+HGj_L&vXt5d{K=@xhVkl39IunWTOr)oxU7=#`EFx^;z&qAdR|8~!_POrONJo@ z18*--UXh->)9D5~9f&^i1n2qR(`@B5-!=ND&t_sEf2iYJUIaX258B|RQeI2W{i$$P zrB5T}`5}e<2}b=KetBp6O6mUM{{T%qE6%!(!q4Y&7o*ep25Lq`JU_(ThnAguf^9~h z7t721*f(D###XYUmPdJKcYmjcF2)QR{*d_WJaTWh`9K(aib>F^6Cf6yN%tBYcn@;q$USmd7e&+4P;53!tBU^_F|{6BC0VM<)ghFy`!GLNrPJxP<4&4;aIt^MmT^zv%BLIBz2t9Tk>wtcIejo? zuc!y}GS;t_j+50+Yz06MV53e>D1;na>L1Q<-Z_V3)r;LYT*^r42N{jZByK)$NI*dz z(M$d1pY)AJntr(=W(4BORMZI%te=Bx4yfDz018bX+mm9lwj*eqGnHi?TB32eVNL>{ zThLoTfG=WH5(pmJ*RcMBQREgd3Pv9$!-_=K7ZvvI7Siu~gRh+m_RuT8Amuj^mJ`$o0VaIDSCAq~qQ| zQT)XvBk}1&ozSkx$<%`(HWz) z)ugRKD(VIFq}L$_*kU|mea!|67Azm;4@q)Dk`~)rQrZ-Q;x}r2^{H-6_CY7B2%#LL zHDVcoG5k}_#L6RnG`N3~hJi_J2^8B<3 zIr&gBPCPzFY-BI^k;uA{xfZsgK|#Is2Iy>vb11plah!{aYC^HiC)IpDs~6r)_NeYQ z=}&1+FSxbzIIiGM8v=Z2l4NC89LOa^#%W{_+DO*jb*F4;b`sUGzTr%YM2JcSupUZD zE(=_dwg(<(vY<#>Z71tjujQg^EJqXs& zHL#{*EP*gk-ND)~QG~K)1rmfFpaDa2W@J&n8X9ey9HoAkHa$h}Qf~;{d!3iYcuyST z5FyHg5u{~gFw*7aR5!e}sR2X+yX_Y}6Qy&TJC;gVIxR{dqnr&gl2jCTIlIrLMz}l^ zoUz!5%XfJyy@2bjF+^|%bIHw|+H*&c$z#+7#?p_2a37S%iR?W zqpH`tuazE}@hpo<*5iSjPJk>=#r@RNM$7B6Z~p+R&uhBPwSTrFlIu0Au=wS86 z<0So$DuYPI@^u2;HzLln|V zlH<3(%c%@1$NuS96wElCi#VK>Bd+`Sxnrc)QI;O$vl7Mp>B1@ zpKH_&-+sZ@r-eVnW8CUga!zuipEa+nzSL$VN`$Z{)Br5z!(j)Y$S0DTs{birA){Qzaj za#WEbQiwfBC*i9I>ZXc`ZOO9Z;r+}gx zaDB&4Opl#~L>Ep0Dqs=IRJ$F-=X^}&4McnnvYQs6{H z_U~c*HroU3I*O*)f{|iJfW0aA^BVg&g%!movRVt` z{{YLQCg92?r!+s|Pf8?rQhmrmuAw-bPvy#8`IR)SOF<2gPmX~!3GL)*YV^q6_c@DB zDB>_DA`%Y8I^ar;k5l7I{{Y*Q%V1xc>@xLx(~+}rF&Ox6eo$RYiy)qdOA3BJY>{5< zyCA6?59&5+=^yVrW;F)>rq~K31FnTi*3~t=qaX71CG2d;cM+5H*WKP$*=r--a~UoV z;|tqzJNM}xY>)J&39(MDkTq$>ZjG@=zcBqkJt<|>6wJ>W-;aM zNJZLZ#QAuGtyA6do$PgO7Ip9%iS{k1Ur(64dRD$W!*JNW;E;ijWlO!Z)FcY8zn;2( zskT4%bwl)_8C(;+IJiq^2$&EHTE=CVDG4BSxlOI^q!#@$mLxAZPaO_&?pG=L?7>N9 zJ_*0&c=p?|lNJJ8QoL{DYQ4~-jhE_WWwE|pj&;m;YwI&8R|k>v4C_p{a9WDW;-qP| z_iZ5lHPPx*#l>U;*1V zx0X&guhX`5E-=y%rW<;}O(C;?y;Wa-$|!>LV=&?7`<+k1zLVujQz}!kTXC{9wZfD3 z3ZUHiv+?|+RB&p*cmBvg9Y~6KV`}FL7{-9d)QAB|WLN*dyu11Qe{0I^2qvK|eXZ z;)r$#zr;6qUev;zLCU*CTw6|s2CP-=wIOATJp#fqk8(XJLMkLkl9hT4A#OgTf~5pD zlCxksn+}u;{H>ph^(|o}*F%o`H}%8x!}SHq_|FUCoYNu4IVUbe=^RrXpTgvDU(OiC z$q{5Z1>B2Gw;?G}1Ndq+cgg<%AMaCIu`I!?yk<>78l`Va4SfQS?dm$Uc?6qyGTkZ#yR)+s70!9QPK}Zn*T!H?Qfn zDaJ4HzQ`fKdiC?JljT1FuI*1M((;P*^(t-LTT^w~v-Ce(A5y*h5k8PP{{VQqL(6ge zo@e`fQb{RN(n#|M!oGaswdEbJYJQ~n4l-45ChO?B=hLUD`EJks zcyf+iA}d^jw{W85F-h8-%49zw^(AL{)nm@R_e0_|s3y8(^f`RK?6|K$UMK1&lk&-J z{{SxH?aYSS&#fv#9B*n($3%hm>rPAKwbqLHe1_>fofn3c%yV#Izs6?axbmJK`^TfL z^o_&GD(WhOWmie^!`-ryCR8770N4b1F zvW|mJiFV2J54in|&+)vh$7V*6Au|e``IUaMbtLJb_EX$nGnIwdV`q-T$x1J`l4L9> zw6t~Flds=#R+8kSo}%#3oP#*-*5#d3UYvJaDNyv%mhW?WfmNHoAf^iq;Ih@=CnwnJ zKH2sMn&e!G>tN*;P)DY&Y zmDuw7dK^E+-H_)bYmH@Bbg4>h&C1%8y4fIwZaQ1U(|pehM75iO>w@Y#SX~+VKKg0r zy{_$d(mqQs$FdP4@?PKLpP9=xkX~EcRN--2?kP|o2|8(BkMTZJTZs6jlc9L*#bveK zv*G^$PG&VxaTGM#?Kz%^KL}s?$g9{DB+9u83)>4?@Q9~ zXdldcdUUOSq;x?f|*RF=b7^T$>Fq{sTV zW%-{N`g6hXz5|Vs1B-HLUn9#ha2ZmJI1DrvhCpb133R7nJuRn=ePRCqlrg_ki>FUc zAO23>C54D|V3i$Gk3g=Sx-&NTApMIMCiAKlyT0XV?SNDJFJG?!=ceLD>>7+l( zXW}7C#=Q~@*;!^)5yt&z)>PesNw_+Y0kw74@;je%7++;CSsD zC2cz^4Ze;2Uu2KW2XP&C7b42HbSKkwVaA)$C~+z&Qf;M*NU zCmUO33zeF+Eo%;&-`!oNzN?Q@ik-4eky%SpzA&q)RQ+`o_Sumrg<;;dP`TEjWnEDU zZooc!#ZE8cy;@a3t+sUro}GHr71h)Ylgc#>Zb7%j*5bC>06W9y`7aNW_H&AJ3H%2$ z9hXqgCdRt9C9vAk3iheH5|Rd^#*^eJrN=f!OC6IBuI+CScseWU+Y|h=yD~9}=d*jH z(Zcc9M{qJhDVviraSX^)iwOV_3d%tPTY27z#9tZLn5-=JDfViySp55(%FfsiHt7LK z0>vjzx@lI|Q{@LAsSPcMO^RL5)F;y~^>X@l>@U%WWH{d|z;a$|+>RlJZx71wkRUdg z>^%ibLuxZ7t+tff(YI}hAaw$~Chy|=C$lLwl5gsc-rj57{Mo|q*^!*z_0PuTTuirA+!bs9in@CCiKZC~*08vO66xP0ajUOho8{7m@UH}vXB1&#MJgo6`v-mPc5_Kr`2~h?x)pXclk$n`9>|QOx{RXOE?B!amcVC zL~J(Hc;)S+q!Lwf1<5t+UlZY}<2C)d*I%jUe$gYM_krvb*u z#;?CqVjlBy=WLgxVKL2=w}^?h#Vov{OXS~hQh@7mYfr`FcKmj1S!=Off5~UBsOf=7 zcAvG`9)HjHpAestYacV3 z>C^hG{abUNrp^JuI|89ju0sAjL9CQ49yx?XN+ZcloMgyQ+EkOe*RdT( z%^$?y0k3yrTQ{t%^gY}8KjqI(nAc0|gXDMg*5lkeoc&pFo>R_U#>>lart(>sJd2P_ zZ{}RcDmtU1Y`lt;rMgS%Rf_Iydi9^?nrx$u_I0Ducug;&>UsC^cFb?z_ivYOy-(9r z%(oebx>WvAi1Y%2OoilNgcTF}%5v56Ek7RQJu83QeKj6;L4K*P?t0>(zu`Zz+ zNk+7z7`!%?78g#-*VX-5?lf|ge0zh8u6dJLS#9-weRBT*v(vKZRQ~`(zgoXg{{T~+ z>do-3W5+n&KZY&@oT^&IM3d^_n{8-mmQ^M9ZvNt;3d$35FJaQUf8>vcXz<#Rb+_hn zzsG+v+U9m~O zYR?#TXRz`xqgtmsUJ+lqoGwNz9HuESv)IhT34a%nVQoDYLkk)>ha0+7li)>LTXmFJ z;XaKN@S-+BNcs;AW1S8qRt^S_hmMtNPd_-avd$7LukwAe;;4?c zmxeN=s^qMXh+JQ#bUNQ4we-nVe+OP4PNf0M`&EQnYHg@$j^kwi01y+??ykE-o03N_ z$Z(O8>Cm9F-MCXIU&%;ypGM_HD_TN`^0BU;SCoYR09FoS{u#w|Y^w4+W0hmpABmEb zqoJ^Q!C()uRdrU@yp|kMN42G~57OASJD&G%j!E@matGdQSUDNxDdx(2th?CK)^QpZ zuCZAsPa(fc%9AJXexN&q!gkBXLz+&7!qSnp{x?agOFki{+0oPT-Ne5UW`3dhgA!EU zLjxV*{{XJqmf0!r7StLIzYC)um52WTTy19(M3)ow35k%RoSz$%!pZ(4jj1a~+ZYS0yM(+ou zC6z1{R#UQ)BP^yaRmFmCln^hW^EG`QedqHchJO@{dd24?Y0eSIv3uBVO56jYyattQ zxjJUMbrY8tePAbG@-~TWZ6!rIZn?cPRH>2_vb~7+Lo(iRH{QxZy_f#l+-t4A;N zo{m=5b%C!_)X^~ys6Q({f@+BQByA%8I$ntxngEnyVB}SNBjDS8Srbegs(WjbNEugHQgb)p=(B^Y`Ty>#L%U&r*i6%y7=w-rTKDC@g3yuR~||ck&vm;ZSf{UM5qt@ z+8Uv^htspM&};IWw5&;F>R+eNuN=>1IqG=}tdwxf;v0?Jjxv;&o0}N~2IRH|sddI_av@|Rx{$C+lkuUz4VIH%mj3`J_@(@A%c|J8xV#^re;nrN-mBe&vY)5Wk>-%h zw%apgHq#Omg_4DYw{Ua`B#*keXv-4l`kf5Ue)@p=Z|uj^9vL{^>Gtyz$c>U1%PKk| zWe`We6wJt59757MC@Q@h;=jrq&k16dwXJ>~)BGBJ8vGf_^M45A`2~xhnew~2AJIFs z+^!^syM5iv_im;}-~6^VQHC>c8BzF%E~K?1VZZO*zK{6F{#@==V*9EiH2byrU-*7s z{{TeszI*=w9rP@Zl@oE2>}Tr(>Cd?sPwDTE`g!6UeO<@p7`L#^E-nr?u8AMY-$Smx zmK3evDJi{;&xW->;veJ<8T{6Kg|hbHXO=SiWj|EE;BWaS;hQ%6WolO+DM!=3f4uzd z@gDH~M*S}G*s`Exxqof)P~BIfhj%5&a0p703QAjPF0$U9K_pk+n;aJp&C}D{v3JZ= zB>w=bYx6JfJ|mwm%yAbys}p@cGnPN1&*k*=KU$yDN0a?S{apPV@V@5rF7zDVb!2ls zNe!H@CBk9z(Bxtv50yk&4JtcJP!_RLRup_qes=!=KXQ8C9ry5T`b&tD#A;vZ+1q{A~F>F!H9ab}c9QaW0K?>;qf*qlH%ZTR#bXAK-8|*0&>T z>O<Ql{ZZbZNSN*qi27{da`>d_ zlkrZ!ka_&BQcIHOs#51U>nZmgB!wxhvmDU!LRE@1RmWTyF9=;Xz*Zv-1EvR~|f4upN z{{So&9;^E2{ml4C+FC+%_=&O7zSN#ijpDFfJ(2pD?#I&4H;deU&hj7R9E>nxWf)sX zkq;@4Td8qzCA6>3+KNF05Tx}<7dPgut}(sqp))sk>dJ z`h#Im2Tee)HPQIpd?yU}^*=vuO{d!_rH}U>tG~+K%<{{TP;pa9HT1@R-;?TxC+(g; zDcSaUBrHdzDd;T7$2iLtS{4uu?%H>B74!C=$ZPwjT=9&a-@n01TB>keaB*uL)$X?f zZA~%FGOR&tDJp|0Zb*z!1<3SrDRsMB{59NBT~_{?b^fOnR@L#6jH8wP3%lyy={grE z`bM4J*&KTjAGq9W85feX$?^E+G4kKU1PKxtk@CR_a2A{-1rLQn<63X{kLP=E=hl>_ z)XOTwFMg>vy{C)hr%U6+Cb__ud`2$Os)cl=+O998}VXGfJW^v zdc#Jkf>G3UtT1%O6*8P{qM+Dx*r@naWYkd3y>3jEMdLY)YhIo^l}>`m7Ywr{_#D&+_zgcj^tGC zXsbfzoB?W9>*Ce}>(b)HI|qxv|K9B;*p*eU7+A+;?()QhZ7A)`FmuC+8+B zg;sz4PtTW?5thkH3Q63f@YoSqbQRAn(6r(qscVk?uFQ74 z`g-T6;k@s-c&zRNKf^m`7Zod-WT9o-Td}{KovbpJ6o9843em);^3oCcHGb-?r{w!| z>E9j4>G;I#so1gNyY=h+-lcNS5#|$<8~Jt>?2~gFF30qe6cpNZCik$nwymShR=T%@ zS5JhNaJqxC98Zzr-BTGH49G5}0mZiLSK22@ zcwVLM$LcBe)A}*HsK~YoRTb5Qj=L3a*+g?h?4zeZO2m=Xm81Uv2^A>I0zT6Py5pDl z$rkBZoRh{7$=854192T`nD!%M$z;I#hVMofi(+^#JGfaExMl!8FuY-FL$Ko}LW_7Ip7_H!WqL3u^I6Odb>RcJKp`lHpWOxzbt!P;Uh57&m zbkj<((BkUVSb&z_CuRQtuc=R`CcVj8Pp0AsC~+jENhvo36xf11>eJq6t!Xo(I49L# za(JYiTy8;=bGRH*GmZZMG~)Ruj_GlcqM7-`0{S-%H{C>5ud>S{gzyfndgCm$lkM-* zKYzZO`K}4Bd&N5+#yDm{wW+7@F3tJe%o&XIeM^4Zfg~vlBc+M4(!B4Or*<;KlW6n~ zE$T`g5=^miT!#h89mx(+Hv`OhB&iOoDJDh=8dmCyirTWYn_p=Fkae}~RW(la>dO~R zPw2zU@xBfmQ?i;uN`;$h7WUH69RgCDfIXI?t7hM>O+6T|R1KBKc$*;X&dZLHeg%{j zpK&{HPpykktJjC1J)Bw@eM^nXU6~zUk%wade()1o`>8W>gi^p%#z;UR*Iz%V1O%R% z^)$q$+5re8Y=4(FEc9m*p>3f_N{`vL^dELe)MBzWa!6s+7NJaAD?gpFbR=7;9#tl! z!^S8x6vSFmpXbHb7oly|wv?X$ZUrjkG;hrqlZKA!FY9k9I_&s*d#e=eeugIxsTP-v zh`?8;E#_5!gnFjr{q)6l+n&U3j1uU;PeGKQ%Q5xu)E`jZoWPlD`i-p{T7~Kp`h~Uo zs*O7uIz*yu+budaNyWI@59TGigZR`}xEeLYL3qwI_rvmqD+K%_;1~ALU%Bc-!Xss2 zv1lp?aSXTM3W5Ah9X5p;&qISgxbuwgz7xl?e0w717e6l|mILfQSu$h#WlBQ!kO&7* zFI^AE>~73Uu|>{Sp$wzti`;C*~aHU$ik7h5jM-uFkX0W+> zPykdm(tmghAHuZjK%C!lQxlX%aHj2r9X=9C1MjIPgGsQr^SC8q>?K$5up*-C8}eem z&9T?F$|lFC9Vu^U-;*)(I5!+BIMfrSq^J^o;L%bu$%a3jV{{+O+EMdXrnE4bxXwfW z0OBnKAJRa#>;N1=Gq*){D9Za6V@QdG4lTSaxMo=zT7!Yp{%uROD_VDetwVnyNMU^1)T;xj5B`^c_e z_Pm`>?rC-^ZuN5*xy)`Qjx5K^I>WAf-=NSyT1qqoqPHF`E~Z(_IU|}Q{{TFGN&|^` zovKQT)@+}I-ro@v9gRge;2j(8x#X~OT%R40Ty(JstU9AOhYFP;E+Km?b<;pd)Kx`% zFN5gI%el`u_P_^_^Hk>CK*;2vM2OHSIQVT8pxmE`?bM%b>c%`&S5zS`QM5M8^QalN zam{7&Ysm~-VVG%dDpH`*MS@hnhWdHdo~wx{?4}RM^VpNx$Htc)US2j8ze^0vmw`lf z{{VHcKPat3N`FuvcM*&H>TAvi1}%#OdT8|u+_HQHv>$~V?3NA7l9qZ1v&{2oZPugu zFxz)krM6NofAL zOIa8DO-WT1*o>^3?jC_S$7l=O4{4ywxO)0^4KbOB+S*&hDWs?u(tlDyfg@i!jdk@w zt(H;sekKQw#Kl5hGYu9TRmWw<8%%bRem27FESkHeuOpwf4WoiP*Mw&a&0n0uDPg?J zNO?$X1SB|{rCIqoaykAfG2@|!m+&^)7J!mP(ttc_j?FH)E#b3; zf)keRCON(3mYjKD8(e@5{{R}?H68`t%6$lA4q=+)xn?mKs7OhZa`WmcC~YYRRU2v2 z)YPhsA_e(OSB&?yJaaCdH{9;qVR_FBit5{u5?(zcArvXkiPoQQ;!#ed1F7&dEBI$R zyy=^a{{XK~>G%$D{H$u9jj>KAUUFZj&!_HX#pCBVie+)6Wg2cyA1{xVUWyXpLWS9p zy(K;d#2VstzC*vSbB&A1en0J#xtAHGbxFgcXC3CdGn~R^9mhg-9HJXmQyHcRWu*c- zDJ~nNj}foFyE=K5I`fPief)A(bnHtw=L+O_cBBY%S->t>A2T7lKJt>DfNmB7lDBiu zzfsLye;&k{JYBWh`MzFmVcYy^b>$_a{{V%%3QgIL$Rwuo)r8~YFz`2eIQAGHzEYA; zyq#)3b7VSTtNH!_@@bHMS;V%)6sQshioAfUtvjV!D|U6%#++k^!NJdul9shhdM>TC zv;=PO^XXZ&xjDxOj;^F@my<1D_Z#c~04amQ@~J3C0Hth`7iqc6Is%Pt(zRVg zovuN)U`bjCQBgj0Unrw-i(}TfwpwD4+(<4=bV3>!Dn)h-&|iO^PR8%00$f$&mZtDB)>eeUyy#4{+u|6 zrige~W=muMp)zByZ&zJzd}^6|uavpr^hJyK#~oK#tg>B;!gkY*NetrnvsJmgn(w;+L5^R1O4?2>Z+vtV7qCxqWclb|ipr_9pNj}Q9*7)`N9hYz$9K{sJ^D~EgpZ!dGSBrvG+CJXQ?gn{o1TI|U$i>01 zAZ`U{+@a-RU2hruXD*Vp^e8sIcU)hMYbPgi->=WBZdvI60Jj^*aGZAUg{*ufNqqv^ zZ(H}c`zx{6_;(#b{L3dLwer2&pXFEw;Qq0`v=-yI_i}5_N1JBVA$Y_z;5)j4l!1SJ zX5YtnwI00#Yja$KIv0p{55Ih-3LtW9Y>5mwzvBfH@r7!BB!ES69G0$?Z}KyvLNkLv{v4rY)K!5Hyw*tOo~2EIqV;W z+g?2l!lXcH%k%o9 z(xdiMpJ1Qq+h^Q}z4a&CXzrF(pF@tM*-fAodwi+C*zU$zi{iF5-3$@+Puiu)G2YJx z)=jUmR=<^E;;bV6En@7+#D4H`Y=dpNikfAxvAHWsRboETNN%TJLn-*RkGnzGJhlc| zK()OW=tv+{Y;W>-BYy{FQS@ie@2St~M~V7q?}roZayVpeSA=9kce;6{WQ2Lx*tsQH zg+h(7LG>UdOK>Se;pweksj^ld#RQB^zYAPA?BKY71~Z^ zIqt}{kad`N{2V;XF{Hm45{}Z&_Z=!^UdbfsUuyh!Uc0#3)YE^JXZrsD^ub5S*r8%^ z>6MrL?CL(({{U3J?qT6~^XW(Gr;kW~2__8AH^yYjb){snk|Z#u5@kq9QeJ1`3PXVI zwX2flz5y!sRl405Mr&Srme{8y<0$_CiSlQZa2{6|Er^AW$~_VJ`3Sh|R#9QZG^r1~ zmYOC?lecLkT%QiL+)=$6>BXz67TnjBmj3|Tctp3oeT44cX7fRg=Rw3Tk~q>{OYrhh zW;**SNl0*pEHCz6t$4m#t1HGDLOa?PL8)bAXWFiP-iJ7*SGG8gd62+xK0zf4&ETAn z>yTL~P*7SKxhcK1+ozpWavvGcor>iZ=2_@-yAD3vI+u^&KAvznXfN|TmmuaC=TMa8 z9l&AUO2WcyQ+1%+UrUOGn)aoLw2;EHu6whwF@3`NN#Hy&=eP%Bd3Js2sasCDE)2!I z$X=V&3OWJOs4wC#wf_L_7nE;7FVY#neO~s%hjI3J`#q{KAdSVhT!tKyLjM3t%a%8! zr|`Qb`p-EHU&s_c$^pmyQ+CIY@^&Jbvv`Ue3Q}BjAq?39`&2AOy#}>eyYQPUUsA(c zXY@w1_ zYxGi)ynsqTU5T$8<@p_%+VXT&?82D!=#I+8c9h0xF|gcm??yOCQBvEog}lM8XKI^R zIee;$zf^+ma0%{$i5PJm#;>`RY$s1>HBog^4_C^l##FMI&AtA}at_;e=ace`E;UjS zjcp=o83hhD!;1~6Y5xGbd(xasjk_umI=xO(xx?}FJ}b!Xo9=RWdE9h&45$w_>_*h0 zvTUo}UMAmIptx%M%|t=P)LHyOlqR^hmY3c z__3K(q{5IE4fiwxx0@XZy>VT$A~N;x;VzBP^$t(?6*s{{R^O0E&N9k3RV?@&5o3 zSH>aAX+^@slYL+JoOS(Hew%SUu(?e+K-@?q`pdzQgjII%n0d#Oq$qeMo&_cW3DYb4+I# z#$~xmBPR{Pc?>pPa#mfq%Kl#}OYN_CO2I)SB|sGq3c1<-LHS23#VPC67mv!CU2wWT z_Q}f2;GY)benahS*!(l((|r@}kH|;V=Vpyphsjq+6g058wSnCyx8$I!N>vh<~? z^#@SD-YP1%(BAdxzs9`WwkTE2V#xH8ee!yL%jHd&ww9$^?AOrY+=Qel!aPOyTD_X6 zo>hIhU}qK-2vy|wChE(qy+UgI#cv~Ud1f8+o%=u7{W?buBfwqqyC^C`hxBT6Pji) za4>)EPCJC+c$oOsmdanp@zxa02e0(4t@} zB*&gZiBsuLs5S>rY4EE4U&fgwlG~2*x2jzZj_glyC3csa<9RGd5VH`{g|(EyWLf!$ zGb1`St+!h@>N*~xyr1VBHpcZo_NL(_+Xt+CKYpESacoqhF6Y!v8QNc?52$t}=eT1- zvfTDdMN+tm-BkP$bojE;%1cQnuG?DuE6KPo=0011yV&I7=LOndf!lLm!}=VoJ>3Z< zC+KqDcKZe1{>`%HUy@*A;COg02pppvz{z5qXd5M8GSDFkBVDVpucy8l^PVf3f|WB_ zex9e!pDFmiCB$0cbY2&!$axNIW4vR?U&WoFdq%*EN{2o;#UZ)~UteqGQ~yNy|I;+$p^Cxm@oJ$T4?6+?Du_@0k&% zk}Oo($v5@@HKIa4Rx2d?BAlJZrp1){pI^+C1L~(?H@<{g-c>g^G~(Hn)n#&1$70d$ zr_?9ZFMP4xwd38%CCOK(M}=nxy#e!}#tx z+1X5R=hdATJM$gxO1Rf-xEZQ(+CQaEL)?tdcCmShE%N9%__#?}h#x$(7_YqR zsCBfEw540pQbqb3+e-6q+X+YqPL<_c6jdz;dqf zt}`xNNF2&MxUV-k5ne4R{{TRMQi4)SbtH{^Yq+cUX6S0GJi4`<9DdL91wVA^iptJ1 zb}{7~+bfHMJsrE7A0LuonUJ1LmP?9{C9vyi_FXF0y#8xmxwt;-q3Be-iy)?#ajQJn zCfjVsnZ~lsxODhXT(JkKBf^@djc4j3b=wK`LQ=U_9wMAv$g<4U0d|~1l708JJzj-U zdGZ52`(m-;RJq*bNv|!%IHaLO-31*7mrCfhX|5}|OR;sbeTW>WnEZl&hS@hH5E4Nr z?E!DR@r0GktQd3e^} zJL0-Bb?}M4sCkU-UNI{n)Fh~%jj^r$*0omO&e{Z;3Gtlk)wF+Zp{sh?|{{ZC`f2TOj^(grYr(fwP4oeem| zBsC!>>3z3W6od93I_Nai7os@bB)aQ$A9rz&HBp&rGom3-Mlun z{aWe^Q=Ht^Sni7#74-#$o9Z{oaIa$CM@OT8^JI0sm0~Ko--%B-gXmsseBLtYV!>j< zgfAJCY2`m)!dxMxlm7s9UNv2B8?{r;Iv17lx|<853d!SMsspXD>Nc{jNa`=tc+?ioNWD&m{{YIApg{x?*3?++WQ4Wa6J8lhRksnK z)M-wwlz@9|YB5P=WiBYVeZ-oCb~=DnJef=sjT=b?UzbDuv{zye_URi8bGwNxl6*b1 zIL`!ZsC3LmzzJM$i1>gy`)Cuf=b<{Oz`9~3g%y@V+rLe(LzV3g*I(d15~4=39>XEj zD?p%*ntQ6gd$LbCAB`FGBl!qia!uDK6w|@Wm8EQhVfR$wy=QeGGuEl}aUdBk*mmDA z7NtOD8;k1WXd=ViY9x8xh_3l7FX1(&`(W#Tr5;tq*0uggukSQ(8O-r`X-J$Clye^H zzG_2BDO1c?TXIe8tI`nNt}I9n^9f3l#I}7#oI;JPq3MuR zbhULHcGM#!f}>`KhtS||cyf*^ZZ9hIeq)C!D9d5rN!d{qd9&^uV)isI1HTa@=*naTmyA{XJ67Ba5kHz>ySs7UzZyCkB*DLK z%NM)j6(ujjqQ~}rC+GhF@_+HaA%6Vrf_^Q$V7LDOqRx9C+OMrHVT8-$Og>YX?bj#7 z!!l#8Jcj13R0q~eZrJYh+BW!g0MZ}%H+}mb6jxC56>MKepGgb<00(Mo>+_Ph?ym2Ze5 z+MAJIHT*mIJ8|npZR$s6xOC1jB)|L1{4$@>A479r2%%WjR=TGD03L*Qg%9c>+Xmp` zu-u1&cfXBcioQj+8Ig>aD{`-}$ZRbdl1<7@SnhJa2IM+fUApsctLY@a-2VW?tkwAE z$+dGTryCOO{{X4Nzgjq-t?#M68NfSt&Hj<6ab)(2c3N*7%wQBUqapm&Yf)`t=t>Zh ztrQNn7XrP*_=}YIcgGywzT%&~-AOdpuj8Ng?s)$I@~6jsQO`m#zhD0VXY{zd`E={u zEPr{!UAO+6-(4NYoZDqO#T4^C<>G9!1>bCM@M+7gk}opk68dp{-o*YRp0 zm$e4|i^;eBI(PomcxI3B7sze164#s;)#D%A*FWIS^^g55J2~{0#y^|?08-znu0O(Y zE~|Dsu{;+e{^I31Ur%b3+TM)XR?r0PU!vZh`hvLK&*e@}nEwE`!fVO5{{Z1ENkxA} zmcO=5ufXm2@8fPej)%F**uVRbZNASdJsWuHlu~K0QnUJv{-Pg7kvnn8I~nwQ^euwm z++t)GpyT`3nphmS)V9x@X)UGYrORDGPQAFEpjD5=KZ&_%Y<<@&wJU7z;nl@{A5ZR0 z0{I{F29*$6{B0@}uDQlD>e^WE-#N!$>U~xJ0O-f;r(n2WV|e#=`0jP3^6npt$-AG! z!)qwiC){iy#@}HI4;{o^B?(uJ?O!+Pe--X>*IQ=1(!|&dp6Myb3zAg2A^dH>9%y_-t<8kK7AuQlIBwyt6$>W%E+|T)9d|0{{SIdsjJ&&Fgt%y$@N-M|MDl@GLE z`)l-(*w4=SQx+IjRyjX*v|NJwf5dLAgCr17x`M1|KemRaB;C!v%0JuywxdxwR{(Xb zHWfga;o6k;*PeAjuhN^HZA!l3yqPURP>zHejAMa;tf=fuLPM>jsRrQNTlwChVr-0~ zk<*#ZQrcTc(ebV~1?i(CqiBFG$gM?$( z=H0=1BW^W`in3Cl;j|IzzVQS$-N4YOkOhwy^G})6#Z#C1pJny`0Mq@yr*H6&hFi*C z)1*=LFX}V;WBn|4GwCP3z0i5yRnI%M!}!GM{AZKob56vF&Pk*IhyF(#8eu^s+%}|; zd~epg%i@2@o4ki98YGg0pVR(5{{Y1F-uF#V}{jwt!@)A$4QR}_Vk2`40aM+J+{{S&jR^)O|oxUMaTx(1#Z*jp+oX zR_ZP^@U2H#cX3%I9o+U|xKi3;JJE2QzBrENiH@0Gozi>&HY4nzzCJTuP&dBQ=)*az zk3Zy5*olzk^YXa%m_%k9T<=*Zij#22B$7}MfixPpy5O*oOSxMn*HT}V?gw>qUB2S| zq0VyI)B9t>N>{}AQrnC2uw6(XIF}H}eMjOynZh;DQtO9GZb@~0*=sIhP6{pbFMh>x zpQhe1^zZeH#5>EwnB=|a?hXy_nIHpuTDX31 zhvk2HqIEX4*Xv35>;C{O9q$*-af^nwsip5f(*B1AOOGg|q>>7JI#;Kp&nL=6m$v7R zv(I=t+{*(Wl<}5wuz#LlC0sE!BIG5f8gQn>Cu*;u@SyUtvJ^Q-qY0rHO5~)vGw(Y7 zh<=SZBED^1k%5k7<8dA&$GKYv$ zE&l*@G4`=F_tP?{*d^Sr4H;J-`Y8M~pgm|mK#|#)nxOI98{ndA((E_a?ydcV2>tBM zQwNTl@4&S9|~H1oOMW*e3hzQ8zpf&)z6_O(fQU%q@z@$=G-Iy0GGW( zV}{pjvL0OJ(SNCvDBo2b(S#Bc%_PX%ZK1%+gHQYwTK+q)j;LjPk7LWYd5^5#>>;w# zx%Oo?bRpP`m0QRZgwWTld%w(b&|`?AAVowj|JNjUn1=Z+MsiTuh-P)|oi0rFXl`C?s_UlEect(2} zVHPrw*g#M770X}gQrD;-b!o>eCUA?noL)f}za>cAbhBU&+f;vX524U|5(uI$MZ0?W z^5cF0r~Y)m-7iCa#E%FGc#Aw%4^6sMWwnur!vnwe_A_Zy*!JA@2;8MypTy8Sn^`yQ zMx!<#P~5k%{|lQ1_Tx^-L?6g&iUj_K)tZ{o^+cGhg>{Kb9H4w2IHYWd6+> ztCg){!YD}gpZQcLy6|b~uTY*hn`E*3zD_e~TJ%$ds`mRSDeTFvqjhP;K7-@jZc?&B zWVXmq*K2n+_o$kKx1l#iJw~qbu1hI99LzoGtEv*MqyGSv0)s0_sEv^9m$h~Ex<`tZ zqx(%IWtkbS88_OVgnkrs37hOnzbY+mw7QcthY3#G>(-c*vNA5$>ZqHD;3QmBau>l4 zHxd#ppz*DPz`0+*RcaTyM*y=Vp-Vk#US`cccytEUzZP%E^Rm*ktqV6w#fZO9XgCY* zogA;JPVMBGgAFs{z=aW}+GNLyl)15{!(-x6R<;y>cO8KEra^K!?7nV7rRDN2q!rB&lfhDe*nH3c8QZo|byQnIzlj7Gd3C~-q#5~UNM7OhFV5{wy?@v|># z=1laO@nt%#L(2UHNA6ld7P5WGo*57@k%U}yV9E&|qWZ0W_m7QN*=(lzkHWPT&~M0i z)+3Vt01{8|Wo_D*(go~151ksF`NS2P(2*+90Tv4PgHeSsT@cjlZ48&2N_7j=_|ZM6f%jO!%&e!i zO_Ba9)W0v#3r&p!i&}_z(!JhiO31#Xd|nOEmfO%%Y=mWj(_2md0Oe5eWaBi#@fl^y zbRJ$*1qRtCB#}JEVk0Rix_4ix_ts9SAk?_Eob@F!aFGEC2^#gJMx$DgMn4{A;>-p6>nC;)M@lC+P9E4w<+#A~#k%X%`FPa^&gxwYEp9ue zOBWFlEJ=&<(8ya#1r9bf{3?pu@^lqyVIt*o64-T@tfL}Q0-b$(iislj)`F)N#uYMT z!xxgxVdA#aQ%WNv@+WZy-*~K^v?lu#TUJ-<5R=QKttncj0O~ptt;)aJO-34NdK$Bl zll3)kxOtY^EEyrdGzuURKH56cUqLx-NsW`-44id1wxrog*LkqkrnDm^hKW_VICV_0 zxTn_#EbY>og`GqWOM`0LqRn9{UeTq3(l=hIxBmc+DB9~6;>(>+p4&%BES+E5-|7kU zJIbAm-*IqLcn)>TB;}zrDiw$;JzSG%b!T?UTRPjgphIj%{C=5c;i z$K902B*iT(AZ`m~0c%aQBwiqCSH&+^Q45~9S}$}Q@sqA?2=Jq6S{ z*FmeOrP-Bs?Kt`rUTeE+l5W zYhU3N&usJ8+3H%=#?=zdh1xzZ3AWQ2grv3x%MP9%HA!aW()AsVwQ=l;ydEcmjFhEK zqv00-fpB!B{G|Jg_@JN3n70sRY+8sn+7C~CLG}TRGe>ioY-$3X$|GWbGE~n00REPZ z4RqM15S)H@0o~usG6@Mm(e0~1(?g-A=f!zPp}64^*gTtxN4HXya3tv-V=gM!0WKuyea4o0 zxB7@hNqP-kZEE;Z_^{{!29}zfdNIo8mvjnwxejhfc%u4m2F90OOStqU1?X2f{@RQG}$rGY_}q&Y7ievKUbzn8eXh^N${QiJ_=W-hwI%Op7+Ua zvF4_lUqk4d9ks?OYn6d9ck7g!kNHk+!1Mf`c!zWOb3Y{=wpsb<1#Y^gn@DX1 z$5u*|ppZ2rn(B6RgT0!dNQ_C7UZrnVcyT|sS1gp*I9ABn~IhEb$B2LqIyJgDz^P8BvS zt;q1C`5W%3er7es>(y`REvyd(i4Y}ffZHUeUCsb(e%sfYa$JjVq3N7o8j|XC7iV}Q zbMrwM+>S38OTjC0WTYSk+fUAVdrf)AJIGEw@^&0{oFcN1s(rZnZ0|Ub?8WyN3g#Ut z-qSGTDP$to>fX^_QMU^&Z!IBBznN=mw45c^@PG0;M!rDL*th;o_ZX&CH$GCChJhdzZ61y_4N_h52s07dj_eKc_%Q=&5sK?k74x5 z{#u9B5?*{^!Zi6qEw1HX$2k6Dl$)KngHu zjfXxHiA(5m8~YZp+JRDnLQa5cYv{-2FTwo$}^!Jb#6ERdS?pY}<0;xSX4K=L2%o(=nDxLNx;5 z1K~*W{PNVIQe7G>>%KCx3#(nv)gSbT`gp;8HE<4d-N|_7u-G2wITel>kbiaKVX^31 zmZiAqJ5c+Cs9gD1uYcq2uEv&?oVM&z_kT-&PPqQ_=0E3dRdSn=)S>6w?w8c!U#Xtu z^G~LKtnLZlzWb;Bi^ivN+@B@ww+DuVWG7|u(c2jHt!s)%L3NjsPV;2m+E<2d*UIYr zsq&UVW>RvB{5vmB?SH+V$MNkBH4t zF!(&nnM?;Nn(|jTrO1xNQ$CYGr77C!IudRxvE_a#yqC57*~+A@^o)29LleEb5ION54$i1Ix(TSIk(Sbg>C`dT`2cvDQ}c6oi6<@kegJTo7Tbxlae zI>U(|mfT&H_JLhKzsIYWUg+g)`5pPkVH9IccWYPr{AEkfr=K%>^$RzR z{$ao5Fzq@T0{;LC1;@Y6n)_-~#tNBoH|O;+7yUR$U!@rdQb)vql<8sZ6vc6ZU4XzA7Y2j8=r$$XzHRt6e)v`3JV+?NdSUQ1;_ zpHL(VTf)4P=9^nmN-Ej`n&MFhxt#_S1^a$j(d;_b?yft)h~}sc|?+yl(1fgqJK}sf#qFi#lP|o zlj1H^a<#UXPcQvW2j!o@+IzISxalsvaD1=tAJfltI~kk)L)}gxFN#ZbMKGke*R2Xt z0kCn2OzvsKtHR2^m=*RX#D9?Z&RQN+O*Z`xmVQa_9z%|vEQ?F(Qp(1|WmiT?Y=rBu z{2&_iMAM1E%{1_C#24IF^(HL#yb$WrFLPnk($dMeq{)~2VVB^1!(1|1*ayr^$Clqh zKo+@3P(Ot$G_o-Yo9r-yFSbx#K+jFiB}+GTF<51_B-^3&ylW*HXQ0eHBCx|U`B!FR zG<^&&BFr)m!X!zK$Uf7se#+OgV>j+ryMhtlUNy(tG5F7Dac;P?b1x(un(w@70IA1| z@_B3w`3SC&MQ@+)Z#T=S(GD~$GIa=MGzJiq-~RGcY5Ms34i_B+vyv48A znjG~_$k>E|_5dwXldC_JuW`2|SyDM0Y8zZ_ernsV?5$-I@UUXn^H$ckU5s2#rDwn% zx1!l%P;clPT8AmFhu_Xan*RXkO8%NWGbq;lH*&k9g!x!4B}3GWGm<)|om*-k+_c)a zA9Z|{@$E0(K2v4A>w`YH=l7Qp@rcx)=aN1I;b1*Bem?<^isnh|fo>c)ZJ@gJmmVRc zw1l7%r7Wlb2Bez$Ta&u2eOWs?B#)UmN^IEMiicS?ez<;-XMIG=?A`+r^$WvsTw6KD zS&s*gC7DWIT2?kYDQ%VHBc&w_ya2HYS-q}6I#_V|jss`(XmQi!a! zU@1XD405k8L=b-QPu003`-nJ2KEnW(*~lBc*{j7Y4DH<+z7qM_fwy{+MZavxO%>$L&3QG zbg4*w(i;b92^xO|Xel3*tNvAct+3p>6nE2w*rc=Ark`6 zJAfGOn|k{fblb!x;`N&U02z)un%Vhuv-(9C?}r+5(`z-3nT1ietG$w>3-z&0>N5EJ zll-iT`7D3TuuNg|j^5-$XpPDkVQUvlPyYY}_5T1Jt8(HtSD@TEt!aATlt+67Cu&&8 zM3$#DZB4x0&HOsj)6}^x!}afLKFtJ1Ew$JLqTUuMH>IHLPpV{Op)SoHUmx`eKtKd| zk^cZHa&|Nhbo~QZp5D*Py30`^IVEn88%s@b@b4pufDl0{{ZBwBJl*w@Ys4IpY#6!O!?F;#I>=cjT>`M*6Nc|R2d3SoC@|k%ovE%Jy@_9!V?4&mK<`z^hZK@g@`Bl9i z!h1CIjPh4s{KrMBE-1KrW8c2SI_^IO%=rAyH;|l!vb^G%6EVqh!6dl0$y-T1Ng~z6 ze3$rxo2o9#{{S*aQNaHIC-HY;GU(2OliSV@%7@Qz%-jsq3M#VUNo~YgJ_H3^b-j57 zvE;R-<7^r|GP!A848lvP5qGsl8|!j9m~ChlxO-~J8lQ&utL;ug=#<-fQU0iftG0MI za}c>AqsNWNaPJ3TWtwRMYYVYS0o|ciP$Ut15NoZYum!`kiWSISIMPRd?%IEBHON<{TPN z^B&dne1{KpKy1Zs60!D1pcW*BfIsfquNOI0{WdF%v+LRa0Dt_-$#K%Ftx|Dihl~2h z?>}x_OitwX(jRcKtlQMh;oVj`P)|al!`qut@wVYe{{Zpd7r669j=4Wa*Zfys@Han^ z?dz}mi^(IYcu(~L`fBBon#Xc1#GV^16g*Ht9`Z0a{mCtoSj{3 zUU>RE{{Vq$#{A=tvRJdD{7!%GPxSKspTAYC$jx!CHwS?7oPt+=dBA706;3*Avbivn zr(-@z)kR3T^RE8@;$P2SG2k3(xBl%>!tt8VMd-a(_B3&yCjJ-rzdYq+f9=~Z^F`tI z>7V+s)1%7#50f0{`Y@xmyw?GjoAnWZ%67gkMVE8z&w(Qyo=!ctLyK(

    DGB%2TyU zPS7uVSJAw8{#I4LsOpZq3Sz9q$3Zxh54qDlD(W79&cdTt<_TDvdfo<7Y@G^O58FZ)ho z=bVH#_P;M^tQ?J%#3jwaY^CI?`;edrKJaVS>exKTVy73-k7Hu55c1WGQ`oe;3S_et z=c6|IR4h{JSW;57k<=&*`cny>d+{JbeJ!E(4Frtxm>p;~r4x2Ih(C zE}M&em32k(Nan7p5@mgn;#`LgR#wN($4CDFJhBSKz1F=y8?sj0tMp`N)m6w)C@Jca z2>rF93Z{>I$Diz@DG(SOZikrB^;P5J{ndN zG_4pSwE{MezK025!>xXczO{L8(SJGQSy((#uHbh60Jto}kIy(_r2LGd@-D4x%Y`JN zX-n;g6bc+0TowgMwd;}jr@=M(iqdb!OSAs~pZkA#+WbG~dmKfWOUe(r{{WZ%zv1ik zOX_#}nEfXGK>ZkZ^M>(WTan^j(d8U@*m!Q+XYo%)nEUcxLKM>yWBRe3DFED|!acUH zFZhq~j_(`G#@W9r;`^`m>HVjp{KNS}Pm|)MR1=MoSi}DSZok-mX&Sm4Mf3ym*U*cx z^Oc>=%B*ctAT)8yBhT6P*7D8ZPQ%KzPXH=J5)xz;$3>V4nba&}}m{{SAykgKG(8dOj<=mH*4wY)2s zv*B7+OD!EgIU0+hA}J`GS-E z)wRgLi{oZwa|}SY#_!siAK%0e?Y1LVxt1{PverEQAMT?5;{dYIn4kN%7;JrYf8{l& z$yO~6KE!AJ-Hbo=iT?n?Yk#>wC++hatCeGozm89r!m5w%2;c3D!@RBtLG;#CuW=PQ zaxuY+j0HK%2mLh>KzxEUxaMyMHImCpYafY=BPo!4*_bifazo2M;cb+uNA9h@Mzz(4 zj~yoZBJtwiPhI@wp$f`i|M-94`UHBDDhniI0SY z*a0bFlp^*P7t-YWYn-=c$+p`%dbF#G=t`9gl>)Z|iC(ta4FOv;7s4h>E+^ph(eKa? z!i^^IZAA>#jY;(!IQE<>{eGYwXmQA_-#}!Ojf_1v@b^*w0MkfXv=rPKCxSaBW&yM* zD}eiM9{|GDZ~U}hBuB*^R5CcZMC_dxaQvW!Ykw`GxhDr6=T>T6Nd6;Ait>_v6#oDc z--w|Zfzn@`q3XP*7&fs&8&LxO+Ao?7;?pz_DZ)1wBzORp$P^PihmEoqWSC9`^|&4t zhWy1t43(%d!j|ajZU6*UjpT6JrQ8*?q>xRDjq7nt$t1?}W5dfuehyj_c?gdJm!dq< z9%)HF1rCBLP}s5$C{lX0wbbL@>HP-%Ja;vTa$#}b_7@>^?nZ9{q(&Rcw=30(pr%)I z)a?hz)#i`LE{nvvqK|Whs!yjze3|cu(Ek8=doZb4d|VUUZWPAic@{C184Ms1{{Xm# z3f)DnZlrEag~=7x>vA>NzKrKKINK}j)f|ByN)!di*T$TbMPXwJA=G|QXe|V;!E0WG z(8;<$H@}5Jvtxl^EGSH23b*51FjJ*y*s3~IBw+qBMTxb^pv{qoQTkig%=u8{vP^W^ z2)E-<_Q_;%L^L@-_(q=k2l<0V^fsZB7i;pLeQBGBrC~92ESv5=>Wd)7dg)k<0L_gl z3*O2U;tBY+f=InP86-ualW=lQuEH@&eAQuZzO?O*3{ z8zk6*dREbcfV7aLl!pPe!PHjNv)I9OAps)E0{#@LL1NP#1`*f$1!9&08vLpk@UFxk zc&$!fl$1M)`c0zSM@nC)!Kd*2f3~7VQOKfGZ_0N0(B~Y7tbUWY0lQJq&;t0O&&B)o zp`0N#SwmMWD-D|LPw)N}ypWV~I0#a-LD$ByMYC;hKf|KzqY6Qa!lIf8$`+- znE@bJdC>MX);a{Ec0op89dB!R)gDwMy~yfD!3V1x)siI>4TWUd(?}ATWL_- zleI&qwwkBrly=EkyYQ`kxgK;0Z$p%lm2Lny!AZ6J z3AeI^$#VL8F!7pA5Yyo|^(UCNeTe#c`rq&88pk68-W+N>keKR2a5A|hA*qOHrq(Y< zPw_2n2^Hh~-@|n7pOsGssPO*)BHDy8V$KfWW9qB2{FR7}@-7nYGb<>i0WJD+Ql*_V z2(pO#D~s6pmgO%omqV@A`7JhdaV~5>72;gYrT+jZ;VGO-LD}g9EF_EHYV1W;S$@v5 z#F=STbl9_jIloW%c`pS!!)IfBl7{yni+!S*ujJEREi~afZj3uYo89+ zOdvw-;zrtHpO*SL0GA(E&_4_5>S9}3AhwwaUtRWWpZqoyXU;}nh)Q&zf{b<#FOGL~ zcmPSg?v;X_J(uvQu67?&Z%KL%)7u_E%E!(`b+-}L$tp>=w$-U~Rh#=M6D(g9l?=9$ zmlJ(eU~0a5@RwkfYh$@Hi3&V*1Uk2f>AK1ytEYNI;T)1gvg-}+gu>+$(IDHy&XBr! zEMXiB+-I4{-SU{EwAkoY>ACx;K2@4<8+tv(X~gY<#?fuf`iG~!v)-u_M1~{0lIc|3 zTSzCWkm%FftwPscIvXS`Em|q*EyTCx3sw9)=_ay6u0t$D$4gG(muRl`_yAY!tc?6X zNk5?4PZPxCE}2q`=mO_xN`GLfSG^>{c>Vy@cFT#lovtLQ8vg(cp1w57-k;QqkKi$P zwVY);QrbNvlVpV4AGXxmu zpQlIj-M{2tFPiOhj^(6r_mg%fLQ5qQ5|TPNloF(PTvwof8*@?A-%3k4Wp_N*?yHP? zG>^$o($-rWgzwL4GIINz_XCT?c_uDuIY3{gT5S^$9v-ScA{K=yDWy(6ODFl#;X%qXRFZ|->}ru@$3G`{9FBXe^M^(eP%U<;hog& zR~g~hUL7smhaZ)Rete9CX-`K~WwPVaf&$wh+<=>03i~VJzXYR?pScIz^FNXNl}bL; zS3`ri-*d7&3ypI;1STTnFrCVPG2EgY=;)HD=xOmGxe*jdEWDKs7M*RO@UGuyikG?N zlbF=yRAb`(Pqg1hU(uhxJ>ub9gS`yP!HgGqI8yQa6>d*uThlXZh+5L# zv+`Z9yHJdODmxoB^kb_0pOmD(xcQsk{{W_5>Gn6geABd$J9D1MWxH_u5IKCf_}OzY z{{YNa6{$-3SrMBkb(=aitRFCJK?BnG{%=~yr6#i+;!wIKNuRh-=#yA?R84{$p>8us;F545~fk@x=qw!Bf9 zu^x$tv^Lb|8<&!W9mZX;Qnv<_ecBrE{#WB#wpr@g>{=Y_@Xtg-dB1WwPjWGxyveKT zO9R@($dT#yXNv_%pE@Qyx)Qk&WCD+h*E?0Ggd zN=F*x`0DZvt0joc@!8I#4XwDhS#2dsw)YfLEC-IY(^-V!mzC4 zv7hYjb&`~jxzjO=l=y&Bo7qk+u#aFJYP(-zyKcv4{Yzc#S=U0xR92m*?%#bScQcY> z@SJ)-Bf#<)jm2^TVmb2h2q151NJz4Uy7<+1&Nq1aHPf(WNJE+ItudrxW?;q)h^;3=QmWTQKBkSZymjJgR%RQ^^M#A0H!~ue8y?0k1Bp| z82g>L!pPiTkv+!Lh~-95yzmnut*-9{L%O0!MAD66W zIOaUxIgHM7OtaZ6{$^>J$}-BiT$i4I)K$llNzi#!&n2k}HJV>T=nWdmHTG`{R93WJJr6GA_9V7iB9o)e&f)%4ke4==<2a#Xy?z(=(6>1iTw7=@ z6uGnL1{>-3mvH{(_9ug3vNQ8I>=p+bj)s@g;!xDA!7^V;7~HL>-0xcet6##rZ{?ek z<$Sdwu{$KX{W|wL7}r*ooVKjTicI}GeO!0biSSRR$z7b|IYy$l+Wc-B4|Q@(A4!-9 zF;G(twIC2)bbvOFm)BEX}^x!dD^vOnSNF1{-HE$g|l_%GK$MzL!|OQP5%I0 zJewce_&j5@^YR=sxsD=V8_cnT#XJ=qB!3xivO8F5}+Mo~MB#aWWlq~qr}#{0=aTtHHXkS6sn-(&{94llVO$#PnTR4fB=~{f3eYGq}*5aiH5$n2NQJTn7PKk@|(too#ayYY2zz zHK&ktyp|e&s{SG~keV8n-B$Mx+M9XMzVdSmX1r_cT-~B7tu531 zLx1kBN0#6=FPG$&4!4K-F1--Mp_0u10O*13a>|>`@PQGc4c&;edW88M!=-X}d@qsC zRgCGV`NtnF@JQ&-r{AWY+kIehKIwA4Da?`Dynk$$j{bL&%_+BWg?U#N(>}3fX>p_# zCs%Azlhlgh{zuJnJay2n#pRz0+{Xp6uP-LDnGI5+D3Z05DMrB;wZ(LB#G{jy zvADc4Z^rVt7)x)}`Gs)?TXEPNA&sCAhgUVlczIQ(y;gUV zDYiBef0Y#!`Dl4WrvVxuy^@q&K}4JWqk6TgUNKoKCAj6;0G^wVY=XRZq&4*vTHI~y zwMDEl3f44tF^`Ur>RL|2(F;z{ZS0`T;9n(D9#hPvYTT_XNf)nzC%LYou_cB^ z^El2gkWv+l$A`-G^^)e~EFD4WMUS$i>aw@liN6UhjHo$R5_<;Q*(G69@;{-_>uMw0 zVntf$Ut^XUE4HhIS8{{ObLUE{;;&vCH#ar}xj=48;xGPX?j*yRAXtF*Xqw&2pR|l~A;GNv1DPc5o z&S$zs5(?Uy$WQ1at-1=o*Wu;k)T6hQj(QPy`^hn+SvdlblVv3-*573{R}c6KzF`g3 z-N#l7h|8(hwD=UBnpRuEnq|C2>DF)UaIdm z2>ZoZRTmFZuKD?T5l7=JF7LuqA$pVq+Lsj>;wbeBYqnW+ynbp{LqY zn*1kj;!jGPcWYgM+s9k#hK_KF(h?nZ0-aKI3Q|A=*`*uzn&aJz%Z9Rjk)%%DXXHsp zhaF*eHVNF4e7+&lnq1zxS!{$jEv)oJ<|hK?n6#!#&&+_*blj;6N^RrRnpWOVT79rL zUlX+jmy&ym=8*WSNqZs9CBEWQZao> zI*i%;k8P31mIoLf_Q%QunE9`nTg2~CdgPz9ijLpKYlMGD#~<>E*#7`g&|F)(uCUu* z{FQmIELtJb2-D0TI^el4jaMboC!z7*A+js1j+5gZ@0%5-3z-j?ND16l<@D)u?XNcE z{yB+sM|H<@&83!G5&PuaruMiSc|HXu<67qB@yGN!DD$yn*L#D9$grfi6S$3TpsRUU zQt;z!gC2(`Q!9Q&$l|)uM07F_K%zCPElMZqSMKgZvN`9M=7dP^t=WOGsVD>wI_tRj zNt~|jv1sHTW6g1+)=N{$w-a%FcMpAfFBzwldB-ig;>r^x&6o}?O1hGp*;|3VbUOTF z!OiUQY=zF|tT?}c9R`=H2=RqI+?=ulUnyv~Hz*)`{gnRz?Vo{N`;>*z%Hx}axDp5U z)}_U!y9_Q-uUrpdb4hR(mE$rl!f`4pE->QSR^zAWBDmgH;W~RCt_htMhs<`UYr<@| za9>ef%fp6S{(79WW`%_2F`AYEN3MN+%5;(+=FgQ#1>ndW4t9v4X?=C z*99FA3_B)1(C9rcP*K)Bv4qkC&}O!53)j=a>P1fTQ+ z$nslv*W={cId?zAbC};*_apq%AntYM{vs3Rd~4TuetL$KbA!tHUIx{dg0-61vWkHo z%Mtr)xYU}nsyKbkcVFrwF2_J{8tQeTaXWJAXz=)Mm00;{R`BdnZLd+VacH>kG~r%H z@)&GJLvp4-Lyma3J{5WOO){{JNw!AY;ut8-rbBG4WhUuu#qK~p?;6Oiq zveCG8wWy+U>@r$(Ad16d2T|lJ3>Z4l9v1VgMhNXbR`!qi));$f9v7@I*4}6PYYaVM z=U8Ei!wgt?RvPg~tURkSXNnL|1EJQorIG}@g>Wus+gX%1h_PFSr(_>y(1OE12C z*?mnE!E_C-dn-3Xi7FqxbAP&)bXafB12?j+fTc2^q56-eN^kwOHx`NI3b8jL@=>-w zs*lv+Nl)A>X`~HAD2C*zCCw4+CsUOFTaph^koJ(Nj4ga)oKhm?Zj zOKEB7Laq1FvXGpimS|!rCv!>)9sx&MSMCNqp@f+(wl*}ZDL4NB%4pGKG#jH`J2b$3x2G z5mZvkZm{5Us*A+}{#Uh5s&EoFWJKiW$6u!080Ce3^T3d$kUZ=~WU5G6TH77Sk(-*w z%fsX`F$TbT!YF-7K7Yhr78XT!L?=w!?n55oeCk_)O75eY8-=0-cE{W=dKd06U*#kF zRNrX?p>Wm^Boz%ysfNYJ+$&Sqi+vF~^&0NUD?CzhN)`UFyXlP z90OeeRdqVsm0rIuf1%23>Dfd4-HH*H!nT2-@f2qgG1>eFt@zL+G>0Dt{9d$RrD)jY z3~m~#-(^L!MphRXB}J7ul6(dK0A&ev7163#3>9Lo?h$ z$mnZC&Ff|k8t8q7qRddpvJtxa+SIceNLd2O6{2{-wP@B*zp6iWwS!m{tVBl%=qn5y z)XY6vXxd|-ZZArV3n4)y-k32l$EdGo-AfvjniNX6T=cbILD#A5{Awcxq2DoE!4Ggz zD&2i8YS2N$dV(rZK)ukcb{sl{l00Z$%O<3ZsS*~wscA%y3s${}H_-)GCEO^SYpQ4{ zulT3(xBmb-pV;&?w~*WAWep5vZvD_N`zs~K%%9#sY+z)GB{5u3>-lJZwNL0Ba#j6I z41QI6_QP=P{{RhcJUx%l+_^fJsg=gfumaPKb$%Axuj5f%9na7d`HEr@Y9?5v2jtcY zz`g7(_tdN4u8=wNw4v&8d1bbL)1@cG6^$vC@u{`dg8u+hRJ{TW;HIf5Q_dj(>885= z6xDwk`msAcMzNGWYmY~`Qr}yL$6F7=t*PLvfmuFo%L8%XHr~{sHUrPCT1JRPb}J>f zFv1(KHUm!zjMPng&9H-hngF$joh;r%dy;6$$;6I7i274&{-OP}IlPWix($yah=mk~ z0*L(})os=+7F5#mG+!`fACU!C^Zwyg7o^Kt=HhIgNc-fL+aMnrR?5(Nrym^UbGuw91lhl$uq{o_SFr@gaiovP3Qprl z&uXa_ajNy$vM_4q_>fF>IHagqOr6#q;Z=4=UA&LN?SzM~G0CPY)R4lQ-LBFeNLm{7!n$s_KZS05jWOo;=%Krz(yc1wr6tyQD!#(FzL7320FK~bCpVsF zwprP;+R;VHYit2~*cA{6q<5;K-i}!urO8LJ+gw-bBbP%lnC0E3e-)1#L)k^oilUVG zk~b52=ikR7{{TzL8{y=#-VTQ2edFyvEt1p>Ze0T+??_Li1rH9QE}f9kkwqI zA4WuSFJv*6ZO%O7$xl@#%j>18SF#qh(zD4Ue3T-Mm`jY zmLCe5k6?YCb#JZk`{e%sH(^O`*1bUr`rG40YDP1F)ht}GL01gPV^XuBTWQnCoqAFaP5qQFLDL- zwdxb$97#uN1gjU_ZZj#gy%tNZi~eOJYJT-vt8e41PY9pA7D`jzJ_`{FGV;x|;%)pS z{7NV6s`bNOz*$0lnW^u`3Suj>43|{G1^)oc0bg-6-G3guR$8!rq)p$dei9abT{2d} zZ)X&M6nr`#DsCSVzXTSoey7X7>W%f~$@p*6hE2}J^IV^Axy0R-MYeno=h=~Qkr znar|>T!oY>N?JmsG=}~Me<;1cHPe49e;GzJyET?E-vtlx@64*$v%_N<{7=zbS2-tg zcos*{Y5Dlf>}e6)X~F(vc?}lMiX-zSS{nIliH_dB%AOnXXS{4aGBWXaUP$~8eN=rs zWj>y}>)ig-WSd?xUmVCtcO4PnujAuqtx{t+jonhDFK?B>uhD;kaM5&eg!+vp=5j)W&)aT02e;jl`i! zDOhh^)*9PNrqbB5jh|*orRZDoQLkc;H2R+@xi)=>QX37n7RySxSUo`YSESXfoN~`N zvgaExkF~$ktLY=_6Y3MPSf8j4%Xfzb#JC41pOwYourvAOtOVwyVCFfFkf99;Qd%CP zYTBSdP}js)ll-<$J1)4_*9qr8Rh9k>^dF9G)$RF8S^m$X$J6Wn*gMB3^gs18#b8KJ zFZ!6mU~;)NFZqj+`GRJuLxhW$Tya3G+r;Z!J+rnl=?9lXpsO8uZBBhFNE|Gq$MZ>?NY^Foe4?#{IPDV;` zo4<~fM>e(k`J2_k> z7)P2y+>+{BLPDJ&3#ncPymx>2pOMv%os#^h?SJ_WHplWuC(+I=*mnN_-4Enq!gbtV z8fg+5^&GR0%G8GZbRIRr$B&b<^7X+<&iw8^%wZ>qPp9GiNdEwPJHy(ZcZkV$K4v8o zJCV+DlVD1n3(;D*>KJWHQ_>#Xl8{S!BwUkiEnNlMK2-a1kLHrc^bq*J#P{gijp}Vx z7o2zU%KrciWcVfD{^IgJ^=2_yEYt2}alAVk))S4z*h6hI5bZ0l8o079r5f$HUv9W2 z`<|Cy8yZt^Sn_#))s=T__|*CQf&q_>alC6TBYx!VA+Vu!8;-Rfk?+>%X)*l`IVokI zb$GubNvIFc@#v#{IF*RKZG#5R1I>5|(03CpZsW8nV)a(;<>>4g;K-$owW$X1yiCabCLJabK*4ing2h-ng!B=a8DjUUEH85VhCmN7?=h+BbVW zgXLZN?zC9Bj(dp4!Nd0Xgf(_0N^OL&(}@Pxa9C?UoCH?1> z^0zK$lCO`U{{YyIWn=m!)#v@ketk3c2aECE&gSNKlLZHm<$I4kIqpS|%QD=6?6MN- z6tunwDhfzae2uHueg*K#IG>Gu&nnZ|xAQ#r%l`n=qW*`0{{SfbzTK{ESH`(Us+WrK z{{R#4;q`{jPb2j$-^~927vpeotx4tCcVc08A(s6H3$BYI_k)N@*wb>7V(XZP*X$zuZ2|cK-mH_V<}` zOs^`#<}v3V!y9TtEetDbLXf{fTQ{VDM%xo>S0%UnWxZtauP+$m9gS~s95uGa+aQ$U z^Pj8yPB;3>`nc^UeK2dCgs0-l}&ME8DHj zdKd9$@x}a2?sJ=#MSexf*JVyHc}ut9_#Zp?uW7jtK3+s|UO6*8BO1Yzl=)U%aS@pp z_?D!C5)HJ!TK1l2=9>I_O4(y4C!z6KY}e%!KsiqZs%<-AHriOvp5o9ujD_dDk zp&<0|tBx<_6mCU0Jd`}2$F0|WO4aD$3b9SReCwoF9RC2@U~2EDWd8upn2d5pP{E>< z(ygV4rlTwYlrnVR)2C|iox*#SJ)cv%FzrV$m}$b;QFUqx-M6lStN9JNQ`HRVaq*k{ z#H+rYu;Kg}EJeQ)l zQoH{E6{!p)Hm?om&b;sEoJ=dP?owH=5BoBf7x(`Ff$CoX+jgl?-Wx1$qYtb;^Kc0H zkEK5Kan`%*+MY3!$#JFe{OU}FL30f;+-s$}vHElep*q?5GR0tZhv{z?8R*V@MJ zq}4XKsdVkvul!EK;XlaR6k9!gs5wI|e__+z&H6FoJ;HhiBKlCvs&m|lFFfC7vhMt@ zN;23%apXaMLD>2R-4LZ;$eQurkNo?4LeIC}k8krHrO5nmM|Q7wRpI>)nwk85UybG_ z;IsIt7(CW7WxSpqTPu$yJD}L5ZMXp++u>f{qrXnXJnLzr%QyAxs(v$AUMkp^&xd=NoCHemV=_zhUgv@X}y0BdPj?2L1nkM+UO_m%|M^f6A@HjoG zBztSsxSnrYb{=)fGCxZ4(*2*=6xEiu|$jP`FIEos%IHo*aXNE5TP&4k;XqQ^ycVOoP(WYT1zC^EyZz5wY)5COWXH;h-QVVo?9k(j^KS2;T@I+@rGk-@DEYL)7ommYdEa8lAmL0zeHTUp2+aN z;bJ-dQ@CB+$98IO8i?DxGYIZ6{GpWz;A{3rwVzyI0EP$ea_fZ*haQixa^=&P?oO6P?@tV2l%R7Z& zuz8gxc5{bg^LWw^s$;rgEg+3l6q0RKTYSyQVRU7Vj|D@PUsQE35#e7)(HW01z6qK< zv8x#=doJ71gUC=<@cuh(2E=K4=C?V1N>FghX`NPw1;gsCj4ZP<0OKXp#NF1+;+yO^=| z03Kg2;xE48BGyizRg&XiqtOd~?`C?AB4=`01q8-$ET7P&{{Vdgdaz5d-CT_1@sP?@ z$nHnVg)hWvN8v!kS8=6%tnwbtu;D|HfGX8VC_O1w;QI_EaCSS-sHf&}sUqJO#kcO( zlbUJ%QTTOTMV5$C90+-prGxrGxdaai6NHI5L^B396+a6Eut!klg8tnpM{S=#waNph zmlcwbi3xJuY-ns6oyXq)^ZCvVIhlYx+ zon+TWSMwDqOD!162|XMMxT}|fox%Ez3~fr?D?j|s+I`iLo52}WlQt3Yq>vyjOFF9L zgw(vB)E*=BCD@!~chHZO#gwdo5;qkRYMW<}j2;||JzAwV(j}3G;);?Q5}_%U-LNb4 zitYB>N};ozjAhbA{5VOiQrTXmvq&CUL1Yj?#*^7kkl{nqW3Gv+p(cavpX-(7B< zRrENm$A40l`lWA~sQPG=p?DqNQo}(l_hTGS61sir;eQ#COM%V_HHdMfIFQ zY)Vv86bGGh+rJjBZz-LwpUUN2aM3--{YlJ-+Ef{FE`jkUaj-pfsvCY2lF}bEcqGt1 zs;4i1tr^DDZM584-(^+Z@JtrE{{S^)k*}#HUrT9vN|H(ViTxxC>w2fN;WAx|$b8N% zbpmEP&xy!1q&XoDrOWbYDeS&RFO*B@Qj)EwUma@hm}L~VT9YI1Wjft= z@)Yzc%IsE7{-E_|9Bp3>?c-xb`)J@7+|70uXOlREf~8n{>pyOYzunjwW0kJ|0Oly8 z+1B*8?U4+*8xS`wozA${{9a{n9T(ecDJK44(=_~Us~=!HKPI}h(Ul%!Ki}vnb({}y zmZlxKOm(!90l$GD)s#LNr7rO+rSl!i*G5)v@-M0=B(@Z9>?xvup}5!A zzz6#Gl27#yAonNgvl`or-sI(x(Zx2RKkn^NpZ7eZS)SKgwQe{{SmN_eA#<-mWi|Qi>;` zz2ICcKvm>Hu6F+bfbQ$Oq;#5p$hvzSBt|)ud{%vzGhA`j*8E^h=;$KL@woQY-^Xv- z)v;G<$|`<9wRT|asznm+NeWen+kS`buUyov<3Cf!cP{+KL;Hp9EmtsjMLm+plFovN zrI8T@{!>#(g!=xW?S%WY)bK}Jh>3LKXY7ri*a{$yAuYV;pC?y^L$&! z@DQy!{6usEtyb)E5r;+Gds9MEeS05A_}or2z%Xp_z7>dTj^bkrCLA>UZYtI)bz|an zN5x7c)itZ!mRV~Wy|vKNUVd|EQ-TAT5~1+6rES#dOjEB1(1n{*9AfinmvJK$>=a(k zxRRr9Z@bQkW}k}a%~xcfaI2YUOa&pVlko@YbupH)_mF8^Fy3qGYa5UPqP`OUnz`*sm7sBbm)*bx2L-1hLvrCP_+XjavbYp_?%|zPwHjBufW=i_AvU9 z`@2`6f479qzCw((Nb(;76ZaaU_TXOT!A0@O3Q-cJxY#{IZvq9s@YdbL)e>y|-{{U>ZzzwI7x)3^&>QCcR^J4!1YaglM$73kmaunSo z^O8ru^3;9F2YOLPUdI@R)QbBuowbX}b!ol2TpbN(g99kV5v=Os=5ZN5Im)>FeC{JA z%A@45uB8%U%V}e3lH+cI2q5^=x^a{x<(yzj%uRT;(D=voPxLeX*G5OUy^MzZ&OFLh zBRkve%6>#7fpxA4AgRWasV$qRew&L5?l}HZGgzDIa(s^qD*pg9S3~CPSqXF`gk~J7H%N~bpyl7v`AX8!+KPSvQ@VFUiF0G zJPO}ZKI+0=>l8}J>GxKdp6eGwiX`i53zHaxuq)~-$WZVxC}cEcky7WfqhlnE(&tMbvW9F_ICQ$KD_9Ah zhD?n#_?6&4?5(J_LntD{5+r_B$iM0%N@BfPGc2|!EXFBhB~BM!Ljq`Xx`Qg_e?jEX(|M$d+)6l zDmf!4$&cF0HEkU6srkeUQXl^SIc(}xtxgui3$DWhG}1u3p(DVPY9w|bYwc|QqlIVS zf(qrL{{Z)g_SXLDuwP?g)*8hGr!!*P(!$w>}6tDgaJO3!jn)U&6JUqHq`36`I= zFO>u?$9W1Mf^Fa_JNX`+%PoEAkEhNv6t;tmWoi~Hbwg5ox>6Q=mTi_W_}yuCEgVm1u(1HM zZHIgs9S`A3UCYW{LQ}%R9)#LIYhV;1QQARQ>p``Xf3lK%ypdxLDnCH8U7Qm;O+hGq0k`BW(%S%p_D+ z%$x3FsROe-X{48xj)y5tom*3l6(L8=`zpqpUV5@Jtzf2eNapc(vl2)0#E?XHS=^g@ z_X@kGUZ>oywYAu(vbk>YJ{KNGJD8a0zQz!>B_v<`KiOSwlGTdrQ(G$^M96ELYq*&g z7Nb4cfRlB-In9bu9ZE?w-CU-;KguONJ7?-z{j$OGUMVEJ{(mJc)q%b)J};;V9tCPi z)7@3u{H<5?X(TbP#!A2HcfSYk2XG-{Ta6om4G&|K4f#v`W) z_98Q7a;$@exnCGQ8xwDEy)$0SQW%Y7K4ul)RGCOyNc~IG@UW+0wBKQAN2nF-6K`Ba zoazAl$_i3``%&Gkg79Do-MhD6k*j$!DOZnbZ z@)|cK3Ux~)eWr;nUOm!A-jn)~=l-2|yK5?QG`X&;t4F~86i9RL=#2XukzAkC)z%k} zfF5BZ_-au-dec?&_!E~F+XS=O43u{q-3Q=LwuFfR#34rew{^dtjUxFDrk05$kK5J2Pt3gTr%(t7Um#8C zd$(MMBi0?kI3LI)#<@)k$%W0kew;kZygiL1)C zXe-?|f+X<%W8mE$9fbFhFE|l$zklIo`P3qb44!60W zOLYeJ5DCGICYxw$W7n^0OB0Qjob(z?Hd>{z$s^f_;x z)14QS@-N5v@?-I=pA*Z)3U+(UJlm>KO5-}xu67jl3a_ZG(~Xs3^-TF@z1h`nS4NMy zeS!X?KU27#$IO3=_wEN6#&NgfVU>f*&xqz(UZN6^>`1bKTZu@u)#3J6o9}-bao%oT zJmZaoBoZIVvk;IGEunR^7(N z-75RzI&^A(+N+`~eoeP_rCtOYXfwxoKQ3vqR6-+&soS@~OCa0rty#@& z`b2N`ialOIXR;hWiMZ^c+4dS`Dg~tEWi(wHe!ZmX2U?HrT!heA@me*mt0SzWPSniC zs~!BCi||P@G?4r#t~n>+(%a6JEq$Ix)2a_DIQXm-vz?>uyl0+}o^sr_o>3$!I*A(J z#;+;l_Tsw4qS)azWh}-<(EB2cc{dQ2J71L#xHmATLrvdb4a)ZTS1rl^04Qn3Kd9*R{{W7ylk9b0Z2thIPjwN9T0%4*O>Ev%=BTxzO3!Ee~qfw#O!!J;&zw%dG^b)cs@U# z&gK=!c`Hg~ILL&z6{I?aT}p3Y3Oj9mPlb3F$^KKZxgX6eC+KwCcP$@wRa0J6S5{u# z+xn5?9kkDI{@3x2Ld3Vbj?|dBUO<&8@8pHHh)au7G%G96B%Aox6zPV$%vx$){{RBV zh4`(T(s%5^#n{;%x%A3rl4yTfMr&+?o$W>g`Ovr}?yB&L2yFI6dI>$PX4bK4w` zis*ZECZk$%=o96gmVw1xPBZ0J!y6JBE;5U#E2+>A8bW-HzPUlPHY&Y-R-Ov;8QZ3z zOIq7-xRQPbkd&l>_f+iJl$TanK_%#0xU3w+mYHHXc}W@pZ9FYi_)IoS8aE%F%tp}q zj5HlpaxbTl6gn#Q{{V(?k85o? z{)OD}-{rrSEj@1v{{Z)8*uQwrE+5o?eDORAOF?`yvkwfGS%d{4r(~qrhL>#(j=Jk! zXDbzPG7v~JV<_0PNVrKz z=tX;1#`*g8Af+D6wTCPI6AlL$H(DbZFp6)Vh!29*xj?ux+<2!T99pe%C zFl{--GSdrGgtdLCCDbcHeu9$a{v)Nx%}V8*8r1m?#~MB78QNRaGIKn6-UIXQ;Rn~Isz-Qx-)$d!c}Y{v(W3` zrv87~4%hSUAI~y9smEnAJS!`V$>H+rQj;nJ@TH;eS(O5H2@1cTPa5Dpd&TX`LT)mC zIO@NrU-%vO!FRQzwHVyOaM(kNv)aq&Z zzMQD?srby}Zav@rHQziFoM-B1i%RBFn0Smgd@+TH{F@4w4ZSz665Q4=uEmSnrSz+g zci}g2+e*zhi1f#$zl4Oe<4&Wv8cX;ZVo_0n28N5W9*&HyW$i)MOa-&oEj>%_M9d^ zpOP7GizOvfh_=%44j&WL>j1<+JHJV4>d#%T2ZGk!+U;1HL@HJqGGFhMvu2NRbC*)c~=(B@pa z%7D~Z+N+-f?Z0zUZV%`@xz{!A?LkUEQJ*6EO5i=&fUZmQ=gw99!C;wPu+KP_<*=jr zW6ByhpDn`v+UdA|3YR|@TWnTXP8Op{Z)Q0j$wF(73{VKwD z$I+GRkDBg*s)phT3Ta8VUBHj*pu15Yw~{gGqfIEVo=rM(K5Qu`;8x!+bw*Xi_Zs)* z70`Iss!1hUQnIcj)wX>@&@#D9Vu^`0#uIH)l@JfS1Fa*m#?@o$i#<+K>YsAS!1p&L z9fd@m!X!4Y{&)jpgY6w^gKOi>KgiCbN97rKnl0`f&$B+N7~@?2W(Gpk3s6-hL8Tks z!46#qUuAOKhNP)~o;gQl#_ZR#))Mh&Q}G|EY_}DCIG*zG*B{2%1oK>5Bs`QK*6efe zugsJi`zxB+6aN6N5N-AYMx*3a$#fQ< z2QO3uceZGebUfK-ZSJ5orX$Z}hwQ0$8OZF@4z^U=zZm?8HU!vHk>{FV-vJiLysptc z%J(%V({S}lNd(*7O~;&uK|hYaKx4c=lVb+v1Fj@!r6ojL-)e4LopbI7_Pc8ssjgPS zz;UATMu&E%_EM9wm+Bo=e0>AR$V!OXL)gaTs@774!M(gG1w>ou%OALuSLQ%TQ`EKk z%|KjrpVPYFQ=Tj822;qz?5y&ewu7awdfKiedVt^L8C{D108M44;HzmDrxjI-%KHHd zUqcJz)AT_em>-+r*YTqJ zK2M#-r(bAT%>ebe6;IWgmPT)`{Ue$jeIfNggF7=Ej&fo&~L<*EO}QwM-8Z_ zg)Fk>-g9w7E;vPZE4XHSt4?snkX7+*DjR%%O?0*KF1YkX1Ll>o{KKgEcuNX#85BM( zXAra!bvNp39<JU93 zqD6(S1-e|-wRg=3%BVHf5m&gi(xoC-rWb$Vo9k~oQ&8QMSzkRQ&=OxAb*n8Z-Er4j z({=4gQr$(KfG6<}fK%xeYx%8z8f9&7u~Qq8 zKPtcMqDyh1Z?ObPz`hr`4G?^-Os&=`^jI}`+=DB*Ps6Q`m)%j8#!daTh-|2DB$c>L zz|;%cv&|XfGz^r>)*&|uAD69eF^rfSJiQ2<`14khlscr?n+wyG(H13lo&YmI%)dL3 z8SQ7AP%S;VBKJI6!jN&4q3^DR7hRX{{T8QR0EXo z$qZcXEl5&ceJt(OYFC9d6rfhvyngvNA}Z*54LQV@wY27+Xq`K)J^U7RFmpstWEy__UzQD zxuJ=zn^-&PtJ&jBWeK>EIlZy%W>IC=a4bi8X6tpOdT0Ltlp=tQc(4oIhV9eqE}ygi9E zDM2$(+WL_x_}P&P!>tkR>M2>()Opx|o0UTHX2jI)?zgz8%2_red-bT8dbD7gmE?5s zt(YabKfFHr!+2s2ABfn~DAEx13BK;NnFsEnB`lu49u+9dsJrS5S-I(JR)x${1Wjld zHZ-P%hV_9f?s~9;?s~A$-mt+Z83g-$YYZDa(6|GoVS^A9MV@3?t zf}MW)#;{~Vab5`n@$dsvSl$td zWuH#_1N5^SgnAsWn&X%+>7eH_>z~XxGEbM`MgX zCz0tX#^Z(@Ag^+YmX!494OGqx>RrYY!Y5ET^gWFSCbUVmN{*2zBQjat!{ zaI&~eM+Wwkl(ldi(xa$6f6TvP@*7v57-~|oxvoyH6&nJuqR#1sM$L|6C z^hLiTU$xdYcZV{y`p!ylC~80n+eD82h=jPx#6Q{)R^fB`u}g_MHY}6)Qxf0LpcSEy zP^(z4mlsw|Ytu%_PQQf*-IV*A`*i>YLp6=X6_;kXTDwAw(Pd>Mur1&@0}L)I&_8!9k;Y6XlpJzosRGvZ6Y7MYxKL*BKNy6@KLY7Xj=bsEEH`h{ z?gEyTt$GKPQ|c=o2Ok?kq&7;=!jOehDu+ZI;^kn0!9D$|dVgj#8 zg(U4i5ZB`NQxUe*Zd%a6@ek{_>E;Iu&i?@Lx54pg#^7_(Lww5;(n=eTIn{hJ+ebv9 z;%u-wFJBW~{{RWeyCLB>)gEWhX)5gUg6Pf$jIHV2_tm`C3dldo61U$_BspwqkbEmd zEF9JwsND()z3LO}6wj&bsTeJ|+Q+3rRs9S+HKEEFG-l)x<_&Aa(|8>8tdwvw31m~% zCrvb{N~X<1=7rMbv`U7)6uXNG=FO6=ru}Lp>rW%pxNq?tDhxYD!QV0NwHMo1HG?8R z8Us=AX8fXfDbg;MlvChrb-hRJXY?oc5JK!3k`=Z*M;%Z-c91RaG{ki*et@3j18ica zys_&P#7(d95q*8vqfy35_Z7dBNN|igM&h|=cZd|sI8^(wRmw5iAVWd;oKvGqf_!MJ zjfbd5$u>Q0aM}EgBz@GxHEUgkrLj2<#znN;AngajpzDl!5skBAE65!_J!%<2d?ai0 ziLm(f{p!pl5k_W?94m&pUhuZ!W2LqDQA1g+NB;ZI`BK4BX2=_R9&o`Ye>~L za0087=W}`tuaXF`y$rU)dYy0YH4k--Vn0E^LjuHsymbfuRh~^ekis4WGOOHl=~MD# z;}pkjsgtxOI<7tX`>PXpHG*M=!MN!8h2mE&r$E?{c9Z6P!j!XZCOuxfn(Rv<@Y5in z-K&iFI&O-**218j^$qf49nz+vIw5Ep>Rty$f@X zaVOt-mYZ$E8aT5}804S))*kxj?&PrhofRBo$70~Ya6>6eeK5TzV~^sjNDV5-z=PD) zecRbBb@bX;EWSV6%rv*soa_*&%Qm=LoKg1aOI^Iwa)%+O7`$NVP9MOy-yV+Iu|Bg6 zJ7OGC2}*}ucN1#n_BUOf*=a_jhoNsli5)HOVmmJ0-Um4naJU2z+%?RFYse zT{b8Q0R5U$wrmH5IVLf9O_Tgq6w(g6Qha>0q;A|%?mJ$aCK#>`&?!wQi(`_* zEGyEJE&e&dn|VPid(qwgQ>s8VKJ#4m&oO3SUWMHpdN1BJz+tR5+lxcXK-T3V;C1O- zzSk>cv(&z;QV6q_4&*Jh z4X3A73RCXvBZ#&&bB1vO=dK)Oiywhrs2_#t$lT;kX-BZ<`8GN-lkz#%M(_GzrfDdI2IWI$g88GGwe5V3WA&}5X1r3*UAoU$+zjGz%YuWrl z3gWmWfZj<0TdEsuR8_m(KzyaqGuVj&%y1q*j#}S;5R|ZPq@`{h#P#vDw5y#yTF1~8 zx{$%o9GS@Q-*MPKFNTcCB{7n}Rn?U1`9ZG7QVMIUIs2~heF$zNynWNcN>Z@+b>(89 z*@)?2i;oV2tvfy&TE&@50irscqvq z9$hFxGf%{E;croFrAY+)HyY-y*URYo4p&lr2M_XgpJ_^J8?TXJxwKqf_Nb`-CQ2CS zMlyNXhzD;QlKRQD!bX7k)unF$m1ITcvo9#E7#EpM?xSI){KY>Vm4I@jBO4zvU^wbs zVEJ6yk)6nHHHDkwa_1e(b!|4A{7F!_`{_w+VAr*lH~#=>w$rC!Q_lhUR7pSbsM)=P zajOh97ZI0IkkTNfErFr9RrK-$#-lz#BXVew!sB^WB|fesyj}kQrDTt{TF7!eeMO~Z zE4-11xMwM5N2=EqLfKd3S&)Hm9YwlRf4F1RtdAkC-$rZx&gBv$Hn-tMZa-Jh2~xel z*imcb{GUM2cCpg9R3xQM=-MMyBI30jt3{oW7GH0m&0U*{lqDJhp<;d1{5Gs>xH;Phk6?DI5?gtP zqi$*Bl)5(&y8c$RsF2*SO6|BsxLVnEY{;3sirB}q=_IVL zdfY{b14|p4V^5IfWpa+LT)zUw`*dZko3N4ieEP?m^ql8?a}RwO!^_6(%5m_UZOqG( z9)0ZM7U-PpMNLwi5moR8uV8FSMToDs{s~6%(~sT$yf60tr^cT@+ti-t{hpEh4qU`c z^|=l`ZibMy2vaY-o1rB?7Pjy<^RC);Mo8iHXDsDI67k7m zOuTO>nI=1u(%rpL9zv3oq_5&BwX2W2gz&e`O9x|LFDcXEhHzS>%2HOi;a0uSHr(yM zm0Uw+I4C2jCz96e$84ewmlTpcOqQaeS>*(HS%M)%i?OmFm~ z@?^TZ9iegFQ3qd$r>N3}e!Ug;k*Y4f&b}m@kNslt+{?+EnB$!0RHIH@cCO%y+W_y%3*Fe0n+Lq*qYq;V-?aMXQ?T7kqSm3?2z>`J;RF-omN_m z3ck>>d+SSA&sh%9C;L<<`lLU{cGHPWc`i%tl$mG;a*&9id_;rN$E8;CA0gTC%86&A z!s|J8lRLAr-LUN@H{@sk0Q5sX)Ax22rJ2dQaO2qdG_`ZwFI zOWS@mi*MxA&TrC4EVzdRh5O5wAn9Bp=o-pCcn5EI%oXhoR{>zd@BFYSfF8FKhJ_qphZT z1zD0=8jOq4Tq9cBo<7WvOodPS$0wz)AK1uz;&x$Uz+<4Kii7@ z^6mA@?07H4emO34mzLJU{{Xii?!xZQxcYy>#`SuSa+l$sL+xG%k5y9iVH{IRQD2Wvtmn_A59wROJJa5*mk{I}>UqHNXwmXK znj>)=i7m!lb7~m%0Z>l+8;~#MUYqcLmU4e3+j@HI#U+;`%|Dj;dj37B8`oJ+qCZib zTj-PNzX95wKiX*hw}t-E;_44zqbw~_B1uS45*vJ1tQ`4d-?yMXyMyk4Vr(N;12zmKo`&$7M~%WQMpBHP?bGm~jG>yKZ+ z_*nk{()-zrj68oG?>7w1&&j-&G4PqnEG_q(SVB;Z?bwwl*jK$TpZj%rPPY`7QnLR5 ziAKrcSeWmXeFS&kjLCW?Q~4xmOf5~j5m|N;@?1ye3PIEne;Pj>{I98W6Ht~vpoYi5 zZi~W_D%^+B{{VSVt%`>P>TI;nX+Yb`)p;M}op1VO{l#PW9sdCJA`hDST<-2FJMtgG zw)1QyOLeM-HLhhG@B+URp~i#V*TLy(AP!;K-cJ}O*N zJ!V^U7h4NXsos!NxOBbizPDc6b(^oGn&@#c*o8X_%d##$`trZ0ZcmcJNbCn{a$Ty& zvk)VZ;j$UodE+8x;C)uzmP(MDTx}hC)j!2}{#vyD@=9M!f3hsTMa*f~qwK7GOS9*_4ooR{gNyK9n}U;H`tgNS7(x`GwIDTohZB`m7vQiQS(y10Mit(OgX()+?~ zf3iIb`4Y74uHX9O{^N~VkmF3Xu~!NgHm^l>sV=9R#@R*mI%DbUE5S2-#~Z`@r`%Nb zr-|j4wa$F>CnrOK?%O2FZIvNOX-7pldnc*VyrcOCkKKm5vsONG`W~C`PDfgI`+E-w z_Gi|v;(tszkvn6^yOG4Zt&iq7j9w}Ht|KEIw7C-~!z>rUWo$U0H-L54-Zk>)4gAly z`?NInYw(nwJ0APVd=pjZO8TYblF9JF{L3Twg6(bq`e&+d;A`vpbQ`TacRp;|-B7Wi zqWK#-Bs=$-$Hcb9-!P&izDN({a{hHF@n2Ao=Dv(*yvSX`))l+J*wiJX;`$m}>cxh~ zxPDESP zmBZc*CTcD>S(Pfua1SG`T=3t>vZ`Lz%EMlzZ#D23-4AzH5AZ&2@~oBvjb|m`d5;WK ze1r!*Igp%S$9b{|1StyukG8#YPa9SF*DRhxvpH(d#@ONi06;?U{gma8rW#oP0B*k^ zSu+!G0rOV2(vNT5RI<9DmF($V`Cbz%{%P49uM%2jHzZ{6j{gAdGR%;+6cx}ZNxhH0 zg|EuC@g;^!e}RrOTgocAI6RyrdaGc(z&0vEOzeK2l zMZ7DBp`i=di{{TR2ye@GB`tTe}cD<5rBX99| zE(7Vdm4ncM(ulU_>J8Cfq6NLfqH0p!z84?dDV4PTpkA{21K;3asF570HUw0ozn9z> z?fF0nd>3|~EN|d;iheuah`Z5$Kn$J;q=hL8u^mxs+K)#m^%kETvDgJCh69rDx^894Hk*OQp89_0=udQuJ!k2b8*w z+jaKG4fFyd6B!Fjk411SXtksgYRB)}=v2dxS;$@-d;`PLLY$7}^>CuVpChFLTN3U& zuUubD2b|&MXiMx~B7m#Ft~-hziEt?VSJYGAe#IV=8au~b7WCOr z3e*FC@1rfog}%wKOq?8zge|?Jz-`u^hWZzuA1o26c&8(vl$%?s2-obU>bbECwOmMx z?WHOya1<)~8)$09Si~l<#$N(%l&5y%r--S_OpiesL(~aDYS-}t%A@W`+@OK0#7K2T zNQzbmT?rmlrDTy06a>g)8~A~X=${kedanl?5bfKv2+-I@1;2B|T=lk-6Bw zrClux4!#uaH+2!Vk~J?76cl#twzj9e{0mBjf+ZsiQlh2&K=n0gCX(|~zE~0Qpr`^pl#a(YWpctrmbVW@ump3;505?LPkl`T?kt>-nbKsB$(0nx&b6+c4M?ZHJV*|GXD%#Yi)~%qM z+Ty$_4n6(4oN&fnruSmtN+7nkb$Q2~~+LA}MhPIIpvK+I{rV-ezQNXcdYlO=k_b9l`rq9}jhF!HlIRv|IvgWv>W1Q1UwU_txbE&Bjtss4e&qX5PpR6XG##TC5R~j`nj9GC4%G$UBLX zLVuic9lM)`lVW2;+yP1f{LAXI?64faA<0R+F2`BMadF03)I*FxlDigE3H<5`w@)io z3)+8Epjr?KGsI#Q{{Rig5Jrl+y8ZQ^e{@MLj)g#4lv5pVA_bYs-RfD}{!z4wBqfk?jz(`p$RvV@t2XdRxu-m%>J8IBpb_{I zPJf|$L*-A zfx3jAgX@AMV_76<93d({h^_s@U4crJ`+*}VKNB3K8A&U3Lsfc9 zcm(~Tqp{7W`XYv?;1qvcnPp;l9}C2mZ;VRN-GEbyeZ>>!w%XpQ_BjhB=!-h^Du-g# z!5mZ-A-EyN8%g*=BClw^x|IV}HYvB3J+COP$g^>|T$oU4sp<5U`9hfpAQPqT1t&F; z`lael*oP`oQm5r1z^~Ad08iaWJDPn92+@)1aA-mGTvACM76B8%-pYw)2SyHuvlIQ+o8yZvwkp5Ux2mPrJPLiSpcLV~>cT#Qzz zi#dyuk#yO9!BCc~F>T~Tvmq7=WkiFiwMo;^EN&EKm)4=Rk}uUnX;S+@Z)F5mjRuaba_57yi*lM25c86gk%%w^l2U>!sBz?vx3d!19JE)SOi}9RLEi_qC!C z(UW~yq0Tx%C{2Lr;Yh$^g2pUhH|V_7R>71Uy_InDGf z&M(;RO`C?!QMhf!g1_>dkm3-dt%=y84Zh0fw?0cvPYbEh>HKEf7F)q)Gr_p89%gi2 zK$zT)Q-}@2QeAPN!UUXdSu(2Wi7bhRog?^>)QK`uK0Tp2C)x*5N!hm1V@9lH*quVgq6{Y1?6&^^n7Ltg zhV8yT5I*W%+^5iU*!H^Qm*Mk#oMRTEp|BOqZ`nM&Bdp`pFua-3{C&K{% zpk*YTt-;7PrZ=dY4XPjm_tf@y?U*j82EQY#H-!3^Y(5vrqIxC9QdH)tagr9Bcpb>U zopU#D#t#yBF5#ysEXS#6;CQsK<7!}3QSKUeX9eVf$vzW(1aZO|AsulFyS9I9;RO^FaTfygZ za}uPwsm;l4H@Q-52=mZUV_7);O_Pzw%Rn-4@tro&WT10#jxi=V@V=%-Cx zd1v+$R@8EkT_lAjpP{;;WpJ&9IVCdXTWd}1Q9ohus9AM$eGpi=5sb9V?9hH<7pS5D zA;aK3l`5eO^d+#L)QG)~d~SVwdJF46mDL~>1NMr&jMf)i6)r2(Bag^TjkF0dv1#g- z(9ZHT>v9ET_59k#Sa@3%_z6$$0w4g>{J~7RgLAc%4aWTjhTSQ+@Uv^85ZK>>?!(-B zxfnNA-_CM*D?Lh7l9b=>6e)2t{{W?v`>Ou{>L(L>#l-RHWeqKv>muH|(v;{mI)g#| zns3n5_Tswak4*Ze;+O;!MaF#FpQ)(sgSd~rou}dxwPa4tS}BmspVw03{22b;{J|Hx z-INjqhn25ZH2(k|N?+y@x8$lIHom@cjj2Aj298@-R>S2=A7G-t}vPd zd63`_cK-l#3OcDPUW9vvBR_)S^<#1Q3-nA`&#jcnWFr;B11{VGl`YR7vVi^@Qtu(P z&dv6ThrX)&r6mcE#W;*7^6u?R34OQL3YDO~+i9YMuT zck8zNr#aeg&T&2|j^vyz)j{RF{{SN%R#BI-EokZ@LqpNh5Q1zsb!;p75#BPs7_LAZsj|%3Le#kI*e`-6gE|5UfUL4ARjHp zyQ@EexKyze{{Xz=s{UTua{S3-`XlNm)8F->?q50LEi=pey~g<8F=6De#wRU{NL0bH zPNc$j^((#9GoW! z;*hiabCqHc^KMI)=0R}eFz9hbARr_WZ#v@t0F*zC>EJahvNCQ{Z>RK0ogeZ405c9| z>BV$WXVrMz&VEsnQ2{?P2JcB}KK)3zuM(%6Qh34Z7kqj&^~qcE{_$rxx|&W$gvoX~ z&4h@lFKq+IYDM*{7;ew+A)HZ;elWit=W7stcL;-yJj{#y!Dw2$!vVOG^>TRw@DHfUk> zcsP5!pVj{WCje~ipA?0~VyJ=q+dm{dF+OBLjpJ_xD!NS{j$7VYYz(EX2>x!T#4oC@ z{{VJx>hme{zfs)RpLY^`MUx8yjhNmq0UYiM1*l20WC8&SUg8f!UFXMb<~X+AvX5U@ zm;8F1?=g1^y%k-Jq`rqHLhL`&CPl<60mnJSrrcNXi}RxBp1KCnb6uPH_dg%jq^z3}K1mdC?UtL0q&+wrdp@d0ng zxE)@zT|EjRZ}zv8@|e71>FcpsT*SF+2#ppx%1=*wR@wNZ-{LuTp3Gt8oE*!O?iV>+hhbyX{H3HHRqgH{SSRxmu9vQ^ojN|J zF)gi>@{wkC>lMqph|Dp@^NN2fDMc#42LXAdC;tFYYCyk2YLl7db#*^eIu0jCXJ;5p z>#hg%v)&048($#EC594HxuqIUSe7{+1=BZL;QX zVugasZ4yt~piS$7-2O$vXMdsmcNV&xzuOO>&-VwQ8iPALQ?!JoC~by(L2+IK-fPPF zkL5|G*N#`z=_~lwmZ$5IO!`g1{)YILCyvL@@=hq8S&_miB`qZ-=*=ej1#P{+>U!6k zavwC>+=FmW0}r0js^wNvZ?=Er9L#sKfN?34xMv)}v66F&m6w8*zNAJn9TQG*X+A z+X`3bRj9MA#dYe=$4^?VSmG=GY>jZYF!H;Jje`jTCyd9kq_opx{Nm0UjM<0yjg)|rGAQmptLRmd+@QZ0&ezmf)+%_}p#mA{e&gNO1oQZN%B`4>9 z_a#k^wzirJY0|n_)!-I2B$8f$Pv+AbQNG>E>tK8-j3Jn@)JgIsNoBqJ3XRm+IuYYQ zvI#Jfc#CJz?#g1YoxR~scPleD(wG=<%!J}nRG6v+B%q}Jtq7-es;;%<<)6U$_vZUM z9H#2iu00=T_@c1vpB|s}SM^DTz&|ZnUf=fZ#Bx4eDMVVq>p_RNP)e5BCj9`eulR@I z{{ZSglGf+>sn=>x@qU5-0F}N`ABuclRaX@o?_MkLUZ+BTP~QAuXXo)FWOgwf_Kt@&mbj;mu^z+e~CwX@)q`6t>z~JGU**gcS>&0Pz*`{{ZdEbw(bE zA5qt#8YL-5yq{CAzLk6R%=>rEc^@|8*i>>1?=ukb*(jl+zf?4Ckb;X?f~#Ner#?OM z4eyKYea5C7=Y-MapUU40(B`z%c4XqK>acyV?myH6owe<=oV$}D5LROYB?-E=%6T6G z5`2CmYWhd!fB7fLalA5eU-v9n>GfyE9~1s8ua)I}ih88~0R0u+pOJ5`TWQ4CJA(vz)L1AF{ zf!hXOPjUDJmf8(H1Ce1IX$ZMDMp1$fO8Ted7Dx8E=hiR%$CL2d@9**Wof-9an_ItB zUH+H3Ot|kQkZu;CU?Q-a4Rq;NKgL{b7V!1I71*fxj{TJF$tL@p?C&U=HtotjkU9YS zgZ3KqRl!mJ080Mjj{WlQs~9Vg$dT%|D<>^cp3*`SCDiU6E%~X~@Y?D0iWQ$RW9Zu> zuJO*?6&Dr8mib7~u2Y>)UXa>8@y(u&(}Bf{co3eHI=Km5 zAIaHnkKDID-2fKT%=9=UEi+Z_Jc7WE5)+zvX)c|qA5hQzc4|) zSMuK;*4&O7!}^yTPsnyO<&^p#L{;4{rw^@c9DE;mF)86a(P;&|PdAI{wD?_z<|9WF zf6%=vgQHuL;cW7yxmW6T_H1#S-F`PE8x8^Qf6_N|J1H^zgDD)lhb)!A<&YAk$5UF7 z>Y+P}Y9pvRdu!IdIrF+V5{&-LDYa-X(&Jj|uG6a5lyW-T+Y~<;7H%dV7VZRxCvXQ_;(4}G96UAY2`sz_ zJH&OnV6Xd!Un;KK(Xt&;d8@w*dPozG?P@uebv$9W;qgvV@|GewkaOgtIPdB#l&pej%YxjY4TL$h4Q@VI}6>W<+<3 zZ!)0bSGh@2R+G6ZN`|38>Il>tue(yaBI4IZJFm56a#D-KN7oOe-|BIl;7wzA2Y)cJ zxvv>(YdD_b7|C8|7}yp{WB@H8uQh&fJPxM3ALie~%Eax~Z9k#!-wgg#j8dy*e`)rA z1>JmaHo#-~cL$e+%<;^i3Wb=8+8$v-*COLkM~FI#`QvMe*`mh_dNh3jrO4~u1v>hs z4UygCW0vZ29Jd)ANxhjd0!@d2HrA(C33S&?gP%7{9wdmozV7jCz*;48_(@P2gfx`7 za6GN8SW)q9i1o`Bv*eUMjUpEJaJaMgX*`v=ZQR@V01w$#6!BXzV$XJ&{RrH?O~pYf z`MB~>Zc58GNcI}Fj-KDtt=YR<=s~i4-;Cmlo%EV` zrMVZ>-w4Gxzn9}Ho>OqA6tdVR{zAJAFNED^Un7>>`DHcdpY0sBfg!p z$vOa-Vhd#FH>fsKyzphSwxFk`qW`L#506g{m2ZXYMXp( z(@|OO$Un0Ix5>_Jd&Gv@N%r*>y5_;Cq!4Y)qb==e*4AyJLN8nLiX@TdoSy3J{1iHo zt;2d$EK!oiNlpzs;3UMGe+><$GQ3;0sy}MJrk&FI3pm?}+~=l(PNLuQqB=0Un^yh+ zS<@$gl>CO_uEnRq=f7Y5tPlp8 z)II#LUuw|J$k{fEgmtYuv;-Y3_EzB}$j-i`dn1j=tXWrCJyGNMQ&6=Ky1NL>$PyU` zb3XfZrek_5r%i|osOw2z@JfLFHtS5n<$x2{m#QOkOf>HG8(U)M^z{4cYQ}}<2A5O> z^&mPu7L<~re=3c?ZDf3~ZAu-O%3m3CCa*B z{G}&7sevUf*-AZsZFSXIlyUnpaq351GMPuGat5}xqgraH^eXI}zJLVDEG^cXuO)F}Fzg%VIL(0Nd9sisd_S?Du*N|(?SlR}I}qbvZV@7z?CkfGGm zwX`L4o(3N=A-e+MS-r`pBE(#}bTjK0yni=^KN;i%qmvE?u5N+{fuN<-$wduTyIk+G zg*Dh8fSRSPrCly=w?RXUU)0w~j#%W6v_ZM4#mz8j#lFFYd4q{JD$r9N{E2v0!inYV zuSG9u>v}4^iM>vt2DwW|qv^ku>B!v0-n#k^4vM#wU!ibv&NLJ*bR_ky-wu31|#R)O1kZ}?0Tc*VL)~ucs6_4CZ>_mUPd5a-%(Be<4kH)!WA*(1|E;uZu^gRcK zbNq(pUyAH>TwfpR`<=j%l3Is$9#m%&`bfR>_Ede3qZy9OlOYYKq85$cuuv5Y znzmm7~CwIy`;ym^d`)hw-OYUX#8?Tm51O~C|)2{Jj_))&vx80h1vtEah%rfMK zCManMz3xqK?J9nxe{i$a4jbidC?A>h8dr_gZnPQky*e?$=M zW=)1$7((q%4d9boM4jHuVkC0^U{Xw7Z6%PmX827X2 z7Gp5Pt)e5*WRq=n6q;G)J;OAm)ehB>$_E2cV6Z{3x2y&K01DO{^)>Av+c^nQKsgB! z;-w&$1@{s5+)|RQx6s`(j6rkzjey1^`ep2crr;%n>88C&0+^?WvTTjK#9+D*$$jX^ zD$0QpJ~;=eobIFSMOm+ps4vXKKh!dl#8Ane1UWGn7nEIQB z)$$~#an9Quo9Ht%&w~?twMQhv!UN3X@`}`X#A!{#uToKbT*Ur?G*^l zh?{iY%IlMGOvfThT$akRk_fWBM;Gba+-yGzvDfBluh6W!hPvNUsGHdw#Ma%8))+YQ zZlp(O0km|~n!m5)bmcOx{{W5bdMuwgzOFk*$~YSM&nV0Bhw(Vs?>PZ8T-rjO+Fn9Q zK~T8~RjTt}k*e>~@^hO!Mf85PgOFn_J5Ijpl%;@JVMIL3N$Lq6%3?Mzpcz6>sHgLC z4S^Q$7NY&;0IC}n{%}2?bx-b)RYNvTSSt4*dn-?2Gf}0B44M;dKpu6cvlD8D#?R$a zvTQ7&(t|CohU?Z@>M=PdZPb1gtF@MjsG%g}9`IOCzN6;N`*AnOe3`Foa+#~t7YfPiddE31^Mb**&o9sR(AQEXs-A;y z$HunXVQ`R#^8lR^Z$b9iyrXaCwz=v>6;;s1YBV`@C>sJj*R6XDkGdeNl!XhV5J!zL zsmTv@p83~S5~899od$ZuqjN)~6}kZz{`$z%&@!G-;i&JZkd~Icy4WO>Z?IN)lEY($ z*?r3d#$LbSHH~!!qafTqH<_O2r57Hhts?981LLhj$pnp3WW|o$<fq1gcF{asMA5qJbv9tgHKI?R>@kYJR7tnU*QP$qw2)Gu6k5MW%-cGjA zP#;zj7G%THa&nTlUVOB-#efS@DIOqg=~&cYZ>)f(8_6qS6HeuWxT$J32j5ZmmU|IY zzo^&wWVS$QGlS^_?EwSd)YA%4eTljn7LMk1B@1A<&!uYsYz@7XrP@``xjOeRhhWp0 zPDPMMqPHJN00nBjNa@guMe!Q>&IY5(Z2ef_wzDhr(CdXjex)T=Prt2ddeq~mLs7E% zJjjx<9$``&FE4ogO{DdPgdE?mb=dud^js z__>!A{3V7|s}OB_U)@UIuL!)2Q+2j<$!1|V{G_Q0l%@J-RV*sOM0>TW+irR6U+Y&D z-IkObgb2brU(8>Kmd4iuNL7u!w!P}6$|Wb*u$_r6jF-iqiE-`B&Y0tdz}X=~aP}Q* zKH4ZB?h@^iX&)y#Q%C^HKsCRIHdL2!>mzJcYLE}SQeJAe&~9#4uB4_H4a8-ABBs`o zo~LQ{c!5h@$+)hpX09$WI^(qwbNqd_+QsGNdISqL;@0YI&{#NCTt_oHZcX82>^OaU zYUz`+IR5}Ao)Fq6GKbu-+-+@b?5{c9(2IRK9nQV7OR3R(FE*(qjfsvLAYHZDC(BFM zDY?eUMZA_&?8Z##Tw0q{bS<27FF1PHNw%I^S1Wd;)9%YiMRz5r#m2}q8<~$nRON$! zSzUDMDH&Ui#@YeQ?Y|va3UeNm6!?ug`>Be#IY*$njz-b=eiARi9h8ut%#{m(FMk@x zxhBj|3!@DI1A1ejAsP>a3e+!?=wzzEuyP!G4T--_fLgcY$;P%cesgUQw%OOnSPC>L z$r(ojB+q(DvLo&{>Os<(b>q~?MAAelbCRSOp+epwi3f+=+^izmE-#TGN@fZjPzPaJ ztP*_9S?kGXxUzXF4ok)Rf{|!4;ljAE8x-Y-*{9=Iy4KU4y-J;$<(`EP$oqTUtb>Yf zA2v*7yL+K5B?!6esWsN;!A9y0BCi!jb2OS z(ecD0ig<9&Qu3R91)}l3#^Bg?M>gghhcdKn^w?!Q6?~?g{TM}_HN?3l=WrQP6i$r2y$U}2&{rk7OT>c5Q(Eq^ z0Ofcz*bBNqLQ#7Wa!=kW%D!z&yAs&qoO=jFQxWs=zbijifAEHOXT&1piQ*YI<3W7+`-|(j8EQ1g6)yQup z74(}aqDyDS_Mu7%lZ@KzeRTelUr~Gm>HEIFQ9a1+zEI+L_Aj^F(tEReozbBvR(E1bjPvjJ6dy=!EFz?^>+WMo;a*zK2=iHQT z%ugJnE>7Vb|8-yBRAF_3|bq=zBpSX$m`w^o$}GPc$6*+8UN zU(LUoe~UHv>SJ!CzcYC8>7L!!)nz>N>C?H?{0o}Dk#cExrH@zD_0OV7E}g%o3sdTI z`mJ`5oYw#xK;yq0?DuZrbJy5f+y4M_XJdq83y4?dYC6(Xv8BN~L9agezwyTE^4Z+~053#yv#rj|EGAp(1lw}dRHa<|Nv@i7S4#Y-SLL!jB$4^>Tp5j zFDa1;H`tcaNkAh{I_3UR@?F`-?YNrrKG`k)JMrx}Ke*(l(RIOhXFk3o_U91q0{TPY zFneFbfKm@4L^l5bo4mDXT`o8jprh96CaAtW`~iODi)Wd#R?ogO(7ES-oAv0e)5bwY zE7u1V<}w-nd7P2S`PUDwvVmY$YPsOD&`zA8@NZ4~Ewo{3VA$MADba^Z&-Qnap44)M)ZCf$jAV%D6>NeHFNgh>UzsAd6jBBsV8%vk2 z`!RFqeFotiJLnaNEww+m`GoqQddx|0(j+Ay5xEIAxc1kN@>@S6jGu}CxB#e_=!>iQ8z5`SIs?Zex;ALxK3AFo!b8Z-pfaI#%}QX>;BdQ3=+X2)>8FQ%K6m$%PT}3N z<+&-D{wV}xIWAbjb-Z+T7T{{$y3&*>MMlX1S33C`^L|Fns-)vLkN6Aoe=1rusmHIE zMfGg@{n`1Qk;UO%?Kc(OpUT79-y@os>M2_F1iZUg18a4qb-9f@YxXgm_jo)D9KKNDK*2CD@ZPuUn{{Tbb{{VV<;|*f4khwNeK6jo?Tk?6q$Egj&c1y}7 z2v?z@OJhJTZ&VS|v()LPpLwj0 zaL#=TU$tBsS1rk=d-%1zhQqB7Pg{vwOgxZvXgURgi0M+kIqT?i9Pc*>LD!s0M7!!I zx)|JMAsi!&;2*)`l!lys8e|y@en?nDOe7LXxdTDtr6;pwd~pc%eZ5;{f5bR>Q=N`rS-a363fA2W&8T~u^)ygut{!`ES zo-Ss4ludslosb-P!nqP1E^QaLz>N}-wge(dlpLT?2nw@U-f_5eH9P(|$*s6iyUeFhT$0HrrH&E(NCS8{QPP$yzUWtAfREoBe7x-C!Q9sd9&7+9V}w*LU~ zn^PSrk=Vaa-$JqbDJ6`CZ;>_0r9)q=&e;BnP`#T08k^d>IzNQAVQgoyEU2DI^Izv@ z%T?{w2L#T2{-s>|6NBOSZWyJKnQks*i0L2_BP9BNddROB{!NWr9IbkB?OV(K!_@x( z@yCjqcJ5E<;=kVi0N{Rb`;C=*xO{gp#`20VTgjBASrq(lHrh@0*;WR-juLU!u}UcS z?nx_ShH`9v$)k$$zBS1(IriSiV6qVA>-)>xD{j(xt2OD3h<>T?92^yBz-j zoS#-Ol3R+)j}&hWq?IScfvsv?Rt>`umOxK;dz)k-3|=~T!s604C5Y%s6jC%Blm*Rl z6nryMHu=7+==Q#AxjMWP!Xap3#RaTjbxpP%3(50BSu zRvAb3!WJ_v$Xdl_cYKyfiH)S8K?_UiisP)CkaP=3xbd$-++H6mH@e(lnGn%o2?|cQi zaUHbFr7@xMxWPNpj9m2;w%pXD8?f)^x57ZyxgJM;dY5K#ZU;rBuML{<7x|HPeFuFo z%I6or7z1gx~Jbb>et}={vN+q zWy^8NP0F{Fa{5K?Iw*Kmkw=UU+drDH#KoyrN2!ezlVf_?Y%~^7*$Z|vh<-DwWya0Y zph(y3qsb{OKE_8dd5gi@!nn!DB`aG+^CW)>DYc74fh0Fz}F|q{5m$t z&Zpwe!;ATRt4bEHD3<>KEbKR9exk9`c|T^e3~_E)`h7{fX~Gi9-Xi58CsbVdX-je6 zB;Vtw;dWXqc`u7`+?9S*;Ypu6dEaU|&ucSlg`UK}jFA~+#(V{OYElnWENFe=y<3L) zM!wu}o~Mm+-woQ)n{TjAA9f>V5g!%Y>`CNkWFe@z=E_;hLrQJ=T+lc)=(@7n)r|V0 zzfX~rUyO;KB6l;-$dQfhc+lEzxPQ^SyDuDEJPqB?pCxeNRF7 zukse(A2gfH53IZ=zrCIMgpCncETV9{v<=d@eB~jjA$xu=L0Tom-05w6>*r1Hit-#) zjY&5X=&cWvar~`m#icKyBAdprEy&vHlpSoF0(Pho=S*qx5^?Ho?luZQnNe|&uBKdC zwbyAKDcIkiKr*Al)C&Gz7Nn__DnkjiuAy>o|PN8?yZXnm7Xzr-294_9Il(Iq|;>0U`ia_c->c+j;vbN0z zA^fSvPR~hY3$QvJDVW}|fhlS+MB?0OzzakqbR+v~e)7QuOHUzYn_2~$QDo>9Ym3lb zh-F2v{u?EifZ`V#Lx@P~eM8`UsSEV$n(Bu2cC4-UEZAMa%Q6FR$|Q8&ReisWQ`^Hs z=wH>%SiMO+E^@NngypE7ps4BODH-w0JrQ9~D=$_UjmzA1XeyMe4HPawq-Vw8u!kb# zOmY08OJxO@4&Jvu&wVLx9UNhn5aeH>J%`Tnds54hLyaJ4paCcDs;hYPpF+ltO}^+k z8A6xtHx}t@)&7qm7tx%@A;!)D;f@<*e7U53y=tr{eSQ7ag;CHoTTyAlRSX^R5$Pj<%V+9kz!fRq^~rlQX4Whgt;*A{T5DgCQhh+(#5gXD=N?S_ z_%o&B-j<|DX=OJ9MO&dauFHqEGwONgETsq3s4`4E9A5PmFH%%I&307DW1+@uNw!B1 zsDA*Wdkptkk5t7=Mxyp6Q2x?XaK!u<4Ll|zcL@n3efxV>Yt zL173mQ}IxXf{}COTvkEu!EwD)rQmwc z5mrg0!S2S8$^J77Y9?Ir&4KVj$CWPDD!mpG!MS*$x#WdknCa!E7B3(ZO$fc6 zY1HjlxHY!=8wR;1Gp74f?Y*j57Go6*a>?8U{J^zIFf#5T^6`NiB$mPIE^BxrC4+P$ zZivVfr^fo$CDbH|{3&?S8D)f|*(23jx|8Fn_SNkkJ|#T-vQKddkMhe=N^Y$edsvSu zKY|taB!DyEV+-pj0RW%K4wtR?qiS*m{I)qA2jfflqkinZq8l+cwx4;U`*Hda_b2re zTzq?`$ss;k)Ll_wqk92Mhv*e44sRaKYOdX7k+rkX5~nVmM_OIgh7ntoT0Mo~aQVk| z_|y!m0sJN{YRF~SRrb2vLKcS2zj>aUoD-zS7R7Nj~&jWG&Z}6OtPE$g(lb6!*HtW^cx7B>6nDm_g(33`l3zyAOuT(Rnv zAtO(|iCh{Yz*8|XoZY(^TPo;wfp5gqD%TNbK^=dK#nD@UBl87+*-gEyiaHoATv3HD zawH)QpVAMN6>WY7p0DC+yfaa+vE?XxD}Qjodm{&+M@b=0xQp~omZ+=QCmx`=Fe17N zQ!FJ`=)JeU*-S=k1mV<&$ip?gNLelq-UmZX`Q)#4v$VYzShueOA0J=y8D#Dr@~hV; zIas|8(mK%kn=ULhrEX0~TPE9zNkAjqDoS>nwx}U!%D_eeET%dE7b;mvKm4$$N*6cT zn$1afLd)b_QYF_TEe1G7t`#`g3HOSObuNB}zWSK=V>Ww>+a#!{@^~(lXcU!R*Y>1Q zXQgP&ISN@L$MXIcB`H)a%vo3bsz4|93Qly`n9MJ#U4*hyF?mEKrM_Ml1tFHzsrRT> zt8O6fQ3Nx_aITUJ7gPzk-sjukMGD=!6C({e<=l37#~5uJoWsX;3H?X-iw91ds8QXu zzO0-yE01IqgAy{-mf=SEVvq8QS_#wNLw3tOSmkx3x*(hX04)y6$mG!x2m|3#(wk|n zhL)WcHWoKT9S4wce1nT?7d^bY?H#FYt;^hRW2!~J8eZLLCERL_g*Ho9kj3MW23c`2 zx)K)bhSP5zm!#gD0mXz zci$i2o}=<_xx{>np8Lh%mJ&X3B&oEd1p-ZU&Pe5zY{6LrRobBBN>Rc+xSt!){DEN3 zPpbcf_=o_?x=WV9A-Uh50ZP~M0Kje z;vpv9`ZRUT`^g0(@XWl0Z5vRdZ3?{VRqJHQT9GY}+c!{>=0_xmr6T(f+T2&ON{_V_ z+{Bwin-|&~bmq*4rAmFYsUgIe>TcV;;{O1J3eAWhl<8RelUQAcT$~yb>ei)_+o0&J zPq2OBCwZiJx467 z8r0m8IM*LQhw>b19}(Jf=cYVt652n9ajCMdMOVuE7h$UP>h%pHh_XOwY;2NrLn=_Y zJv^#ExLNcVTzw-y_N=JxDNn^ra+`+t8-)+BRIA^Nh)+T1pbA`Q(OOwJ>bfsro zVWG7gbqqj$Th?#X^cHmh^tDDX!MRT)vMel{i%Mh419hx=Qu0yt9;wa#l1m>6$sXF& zN0ha%MIfm4sJp8A6ZS7HE+xcyGpj>Wq?Rr4p|$NPNb7!@)h^c~Obb0-9f_SA$M_R$ z>?dkuAI!d+zY6E}IWwi!;{K(f2Z=^t1u29&6>n18jlTM#p8iW!#^A8{`#9)|L}bHo zf%$Hev>&>vw$p2)Ewo_b@Lre&$Yqo}5v}(B05JpM*bjlOQ)af=(p4-LD_QojtQS65 zzTvfkl#ml@t&WVO$5zG>7RYzUA&Lcv@of0jCAD23U#-PiRq?lC>beM9%lP&zj@kW3 z@&S;HjFYXq3gdWj{e>W(0dZXa05R~@;@T}Z-gn8Q&!T)cyqIoE8D|!;>kXmt>ZE{e z;10FrJckD=o5|VNYLYF#=c$rXhaE~2uH)^lcFkGmpmh{GWo*fCdJ0O7t*Pp1sISX_ za)|Z3Y?U3ZJeHB~(0%nUZGvl{(o{%nxS+G^tIZ>KPjzZGVTG;@m~JFjZUhpde8Cp` z>PumplIReR$6R=BX|GFPhUcf}NWQOE;+lyMzB%1?dowv2=+OBajP{~^6dt9f&+!Eb*x=-+uRGymYYg(>o zWhkv`K=)PtwAGa$UzM($~jSOmLmF;@-NK6%7IjK9IG*9S6RuHhDdqbu6jSqx_|W{{U^E zN@+XbC23F_udjtsS|~GzP`WXlAS5%(D=7< zsYdB1(+ov2Q?lJv?xva|q{$u&)cJ@C-&c3ONY zjB$gC+~?;vk}R%yGCIPWWiA79w30ylDXDM?l`{D>lj?)Ge&V+!R$MhNCgIx;7||AW zUITKvT+lE0ui`na^0>tiZd07+StpjRU6YT(aE+-~gyX?(l!I#}rizKDba?vf=?G>; zHs1MGC%!mWG1f1k=*^&AVUwuM6laKakO< z6sbwSs0n#I_b8m^Q$3P)O5Z!3E-gzo@!FH1`zq$1B8_;Z2&ICHW0cs~mSp>q>_jh# z4MT6zWRdYJs);xE)b=jNa$3}N$98ld4(-I*P`}(6w^4-SF+o($K_lTnN=~V4AI`5K zTyG)EsJ|Nev#inLTZ)vs0lBArpkwiG@lM~w{#Aq@46Le>kv4UOw=Kc(B{%4irB&0S z?3bN|{IBs|6{C`J8BB&<)wtRmlvGu!5~X!M%D8)4wX8WLAs#Po$CVbrsl(@8PpENn z4gUa9W!(k+simdNYA1y?{pMHk&F1pr$Be_uwzRe*I@vlXlr2BBRAiqbZfQUjAxIbaKqTIiyOF2SrdZX)?EM)bVlHK%7V;fJ7}Ti&M1jzq0ji6xqt&AG zTaCT;$=3e>OaA~!ed7AD=B)9a3zlmOh{Cj~yvv$Qd^L;X*pw2dX3JSpG)PI>fOjb9 zEG|c%{Ga9edJDzJ&QG%X{s()({C4@ZDA*>DHYbBg8YR-(gbO5vYo)8nK2!1iT|I|QP;qMy@p)Ztws^P5TL?-&m)G!L z^L@X)oDbYS=J#Km^Q^x+=Uk8HIbLXbS$EnZax7l|JQ(U?`|q^u5V6mF>RBw2oOld z+tiEq6PrQgyqY?(U<_6f0zNU#fhu$)-Agv}H1?jKw*pl*x`Th6sbR+4bX``t1fHJy z%W52|z=s=QjK*^vk5t6MOs7!Vq`G#tfU|3;o`YJ{ppd@S!>+d@y*W%AuWk4QCHC`{ zW97k!7M@K2y2H_5am4K^NLdQrl0g6(E`7K>^+%WJ-nX{D?$*?^(Y1&D({e? z`gDKS2R;7)k)Oz)vT*B0o|jy))4E4n{d0X%a~OTwXN30elDmw=aJb=|x}zv3<}yzW z>IzWm8%2e@$?~Ot@(=kVXMdi0`m=GWm5!fNmj3|9kK$cO@c#gHyIP#?d0Abf>HYoA zAy?FYcuLdIT^^y-lI-(A8#^8sI+b-`@&x9vW?aF5c@;l4fT{CW|R z>paW*`+tG*zaW)5wWS!>lBAQcZMK!^TK7W-Wpl$KlQHvKLyjjN^7jLD*kAV5%@?QG ztd)L1m>9gXZkM0On5kJG2~US#3)7zLanwiHX`HY1iGul-q3`vA!pq7t;P`rF*|2wvxnq zo-fC1zbwnJJCIFbsRbeME&27(n!Wg@y^r%qqWNCTCnTfj0&OULKC5a9Pl>&HRdGE2 zQ0ghLAJ@{2SQ0;!zsk)&{LOaz5^4hFI|V9BU=-@DP5kXz$~7o95uYqYg6fhk;6W)K zB!PQh!jjysRzbyjqQi^2HCm+mCVfZb9mx7}&;1Dh0PtzYJIRj7cRwYD#>$b~-gPE&d12Jab;^MVH+!+H+pqWbse)4l5&#M&{AKn(;?p zTz3;T1H>sWG+1%Zgzg$D#9vWVA0Ol+d|LVzeEx^CB>tsi>o3{uuCAEiI9x#uk?uSS2b#(4eHLDoF(@1Y8hq4R30S?vIR?P+BdOQc{mbkD*)}>t=s&`!~+M znZBm8l9$B!F!5zp1AIr z=^S^d-*>hAi_+uy8m6LG*oj=yM59JuF?mtNv^7{_JG)TlP2ji!l1b`gHo|@O}FK0K6fY z?2jAn*A?X5-ugsb-ciRmvi(lxeLmzKZbv%dZ90Gua{{X7a4x?L<*W8X%N?+~%-%sdrtZa)fp$;~%q$=Sn2IH;2a=jFjjXSVEHc~;`tV`m<)@! z`An%Q^_p2hx=s8or4MI9t%Zp!h11`*iTOd`?*8BXRdBw4i$@0U-Vv;fR|lleAlY{k zqAZe6RG(CdYD?Uuo{7c$s*SjNcOoC%&!h(xjdPjyn<9=|#^fbVvl#^odA7#3 zN|B<7zfQFO02A|h)cw1<6#VzVVPf;495zt&Wfv`q^+0QDLw=cpmeWE zhUUJ^7hL4>?Tsz1D?XSnJTHIT%EgCAmfAptDvXyTq0)g>0 zReuX&%&KLcmput$HzLT#@Qy9acT4PlaM3q3>PdUI*`*^Hm_t`uU5s zjxU4Df@pTcD@xo5E4ua3Qh&9F$H1@_E$lCLm25#&Y1b())1UmV4p z&gPxk?-N9NW*LO3l;iTq$U6A=Ae59o+3G7(*P}7cO`!Ul5Jzw^^Ikk z5n4vX{2qSa8kwvKGG@eixp3qzE9|R#4>qEQ5TW9gB!UmP&^C3ex;T=`4Xw3pGV+7N zI={C609Tx=0vf`18auql1b~D`%LVPqMM%=&YSGX!#!W)pUKx( zh-`IVQ>){5IOj2Ik}%R^!w zbv%iux(!Hmm*>9Bbf9XR3o8EVvs_P-%u367zaEq5F7G_TP4r5nG}@fe2?O%$;(fKb zXxR?NmnBsDvFf8h$b_wpt}R2Yv^37Wl8(MP->-#l?+raB{Q>WBg|-Pm)a%xcx5XbE%)`P;MXZ8II)V-U`W3qoXl5D{M1nY- z9>5^)9u~b<*(AoJ8zN0{xrbtuxhfLMFJ)OCUiwzrl)XoF?v!3yEL?9b#Eq&qj(k#5 zdi!d&{WiN6v~o%M6BzuLA0<0b`c-b94_o_9DLq$$!mdU+4oAtzzwn$!nwxaBDfap9 zH}_-D=dU$oDWc+!LYg1hB42vU4osC41;-$3aBW%kNFVMj-Q0|MV1>eL`6!E>DC&hC z#B9IGbpXps6=I0i-Oz;i{nW*82tswNfnA2QD6LUfZ#}eX?&vQqRgiRyQ@4o@LtvXVA4T6sGjUfcMd zf}v)k?j9Q{_Zj4Ig@%jEeGas(TMeia_R#2zv((-AU5I4J)&w2}<@Y7K|g&?qZlE`kLrILQ^-u)LJ&BlPbW%c_}Gw*Xe7Yze>c3 zwk9}(4k+30OdrTC*09oDMAgC+egrRnm$tTzIEeoMv7~j582J_V<+SLy{GBc(644vSarqraNu(Cf@%}vK3nqap$ zuOvZAL#ZeB!S_ts5@U4Q&1gR2r+=YBSbTya-<~LF#o~Smh z;McRV`Yvj%w{0l}^R0BvRsc@E=EQzU3~@}t7`h`1(+5asfa+F3%1TS)11PRDtO zPgqRneMMbQkfYaM8e#1aRJ3JTUEdIwsqjDZsLJe{@Ee&su2U-H_+F(e2)g>3{L{%g zIlJ9auGVSRJ7+5MM%pPpAnFZE&0vJ7cpA!<9B{aV`giah1to3wqZU*4i|#FXWfX5lTL%Z}F{oN={Nfsk_H`uHcvv zB7v^+wIH9mxYuo~)t#Ewt@cL$0PXxD(v-z!IK9D08r)QV{?Dr?_K|&!4;PH!C~cN+ z?6@Ccto_1&QcsS^8c!BOALheW(sV5-YwxM8)&X(&0n5ZdVQt$E>&rUtQq#9e+^>sZ zY}Sy@SIovu+2MjyD%WyfX(WAyikf6R8WoK?I@nNihDwg1`cnH^-~Rv=44wgUR<{D0 ziGr5Wfzc|Ig#J6ImCht%TR=H%WuuMf11-c6f8oq+#aL;e+$ivzag33&F9wk^xMaMj z@|u?s^nCa#a^q)|&TgS<3a|vbCBKA^Ts5mrAFFlC7)Ih%Po(w2n z=_@0pJxm0q@W3v;2$BwI4lnl#l1JWYpJ>;R$I*u&WhqlmIG&0+gYLC!9YwryYH-iI zpW&&d?cs80itGzI@<5_;?6gLLWpigGsYl{c&Cl6mS2EX&^eevVfAtHmk+Cmmdy~nL zZ^+VCu#@{Me`viBN4n+e!q8nZ7JVeimAhLdYa{m1tZ+-Oa&h-0NiqCssm}iZtK-fT zby-nC2mb(;s=bZ19>co&ZL-zl+*cIWrj+Abj*gcX@6DI>aqAsRMg|)R z5BQ7(hYg4bZ*7l|uoYKr@@17&Mkp}b@e6^!mUU_x4X9B3X&G9*M=OGBsPHW=T?xgx zwD_q>P5%HI6z7nTre8tJEqZ3(%TXLFP~m>(?gFc>pHkA8VF^iA!Z$j$Q~Rwbi_*Sr z`iJ%)pH@OL^0?Gc?3{y5h2G9TEz}Tw;bxER?KSy&gpBo9SAw17-Q(e$c0Z=aGR0$A z+<$_X*g}E7OCwX(k4F`?H~EtiyOz|7!f51b0Wr(O~3u zqL)_kR8Bg|)TK1ZVK=zkZmoRCrlFrvUp$Qq>N;DmN}q|2ZJPRv2cfOu8a@1P-9L?I zLo#~re(C&c49T!c&At={olk?a%M*jutm@O>rI8Pu(}zy z_L|l~1z`vYLd3YMkm|o74(v!C;0^0)SYaa`fu}Mow!>)0Vq!9+eswJ>c}L&A!nG{3 zQjP-H?oB2#H@%F6>XZQV%yipB)DJ(lr5I(JU9viRkV}}a{k(X|1bi){U>}VZx;;#u z%3TLcDcLGJkUFl^7vu|h=pWCPESXZ={ zpsqGg8;b9&g*7BALA?r7fPLL+A84PbO7}lEVnD2pCI0~8qBEsB58xo~8vf}`FDg3p zV~c4u*p*?}gaB}4rN?Q&L%Y!8RxfV=2DFaNDL&)c_D%XQtFm~LMKQ8D2eL;^!ECKy zfAX6B^t>yRea5A7i|B`9R~&7>PaTJmF$|;@olIW7?bSD@4_OSyc5n2@`~d2AKQYB6 zz8>o-StdJbuUbaTLWsoAVD6}L~(5zgrn+UPg@~oJjrrdUZgE0TKmoD7)^G-!k#+} z##RcP3YEDg#1N+b-zpc{x7mhU3mri-XW$q~GFwuMpXHP^4u`^{9a}TpQ}3akz&R4J zS4oMDdGZamizfd23bFft%iL01Ieie~6OK!jvOX*1Lu$6?7C<()TiT;2D5G^-WV<3q zBMX;wHsaro%0KC)y>H`6%W4Q_%{j{%2*JQid0XAeB{tPa4gsL@27zwEOQJVkn{TTp zc)T_(vWDi*Eg)Ej3AwpGU{z(?R(c})Urw~TmscLZ@n=gyHc7E0+hBEJNl`s4Dx+?D zG3$kf4IZlG`WYf3%|zPTQJx=X1E zdCA>9t?q7L64N24_N0Ne>yWDeG_Nn`cujf9^gS<%yt@KA_r@GqebsrSXI^b{TGAk|+6>IG&K3A?cdZeBbFCx?GSk32{WQ9nT zzWp~Y{{Y)mPNo`AUW0?rvCJpYLfc7A&4#~-sn@S5bit`sk}Hl=-(>OiB{4voB%K;6 z0V%m3n_B5O{{W9p#nlL1d>}sJ-&MFaCP!zIA(mND{wmmB@`3FJjk)lfV;?A$n=r1( zymV({aIWI^*SHKRTw9Vy$UcjD@b>`fE-$K0kKJ76&mB6~Q;*rFX1APWeNiEw4k7}L zx^yC|{lyWjU7HyRoe>IBi)-}!w4+saPJ*enx{w@-Ww52CIUS)!+pKf~t#$0`WvH$) z$de-J8N%9!=1%MW6?H-YK;}r9j=O2iJiA|R&;?t@4nC%Ps~w1jUnXw{$ex4fAH|hDTG+0%!k5Y4r z?v5QQEX!^Es4l1RW{EE`^2$^`oS)b%{WZuh8)euPpkRac=~?LLWNd7J7OJD z8;;FwVU4*bXE{xxc>mFEI-rFcQEg`IiDA3e>8{u!9cjC-idUU-aEG_AwZuR_x}Kce6 z5|z%72a4QKC+@ByS{H3u=WMcflisIVzZ#>036E!5d85F;XxkorDNAoW@o=MB*BFX> z1FZvU_|*?7HG%y-8BSg?T~4@%+D<3go+B5zefM$xJ%^Z+{?y^cJ962n0XvgsT-v7D zzf~v=E6zC&kIT|7lH5fhJdmkJ(x4*W8!Ew>$Hi-V}qW1;h*UAd^% zJdtayqq?7$iEv{(hu#K($}YrbGPwablzn0bv1Halolet zz>u-tKd6Mh9bX`jw#I>K$HghX*v+c0&J&6oT*oW)WPtbEzUTqjt-{H-bltb`ah%*o zoscTN?~d`Z?JR$3W3A3|P=BXC*i3O9^5l({{{Y;5h2nh5GPQI&i+3L3@jt|lyMy*s zMZXZC{{ZdVDE|Oy(&+q)Yd@M9{=@0d(60{chtz*J$8hdf{XO{xROE_LE0XbkPWobF zn_7ZY>&@g`jLJ z;hlU*%l*2){-lGw*~~92%wX8zL9P|rJ_x{x+P>dHVYpE+_|&Uc9BsP=NqqcHudU6z zDx$ll^5vN~>ihoyX?4bN{@rUmW$IBc=Mts*7?7oqE$UxzKGA#YL-xL<)LSyrGR~v4fT~Qn_$G95Nl|j+)03Bkt?UOjxY`xqN)Ckk>yPC$Rk=lsZFnb~ zle!$knn_CY8Hc2$z21a$osA$Xs0cs;)k9YsU44+%& zP|PAqlH#mEBIwtt_f=JkEPAuAI}gP%sCr*B$YkMShBIiz42)N#M1933>j!OvEQ)uyvQDH26n)7R_HO>w9|-t@gn#~`$o~LqZ~h-14y^F9^L%Sz2;av5Blqbnhhec2OatwgEE-%)XDB}c@QYW(|N<&$%lSxM!;#h;^8 z*9~f8R2Cx@gg0s%uROysn)Rj4P--(>N}X|G2}t&mY1z{`VliD?$M==HyQ-??IXonr zKTtnUox6$Jp3iW;B8C#ws8E}8DBu<=l=1_vl6-z8SLv7WUjtti!`EMDjKA$aDSmy< z*~#+uTHaQ0zp&G~T-O`K_>U#znMooxJCDMXE>r3@2}2F3f7xDl{H^j;9a*S9Mq2zs zgr3ISD@&}JU-SO}1M)lEZs0`bJeN72$jU?V^3q0CUE>kwbz&4 zU0M1~ukF=LYllS?kxXPK!rXlaKuGDd-n#ugHUis_9ORmF;U{i}{e zk!f{j%&zZ!TJrwoML0P4nE0MBmjkThc6!b!#9Sp#Ecol;Us8NW_`_F?kCt;I=HHe7 z03zMJFBtYmFizwgzbq8PGn|PWi#Wpv(ofnG*1Ono+Og}?Cj&j*sjPMYNXKN`w&xr! zEAkEMB}DrTTT{DLOAnxgxcjW?ky!FxL!RT3o-2~~|;O+(VVdYF8TNSxj(d9LVn>D?-xaZa7o8C=eV8Bpcef zJ}>8-M%Kr%Rne81?d$$2e}+!Kkp4gAb+z5nt@wRNRBmXA&4BxReN71W*Iwpv9$iO~ zGdwF8fa|!Ir%qa~mejH70y+{CYDewta?W}d5!sAm*nsjJgHxYVjWn3ZT9iWwzR(rn z?yG9z%KI?aLPOdJZA|V*7RI`ew&%(DcAL-op0yQvlFFFTe2L+g!GyQJy+3zlZy~{{VT>avpQ3H`Ip(d^fk* zU1diw%sOm?MIy$lRf1HVJgXYXk)K1MJ4xQYJCDE~&v& z6sseHkiA)5)Ku#~kkt?Z+$3@IK&i`CLnwap!#;&m5eON=$X! zfNiQ)g;5!lNfg|5+eX!ky6S=7Fcr{>+O5Z-lVft|1Iyc6z#$hM>aY0ePDVXV zzKCYEiyL(`brggrEQ}ZVm2D55O~>j1wu_UsUHtAJXyvE*4rRyjE+6%HDGDw|aW~e< zH`-5_svcY8wCyg~XGg<)qhnXI$~zs*uVlWcp(H%ZTuYx6o$=OCha6qM>8n7ne2sAR z)xXBird$sC+cY^^@bIS2Ozuz9FEin8y&@bn=CN&(Qu|3INf+@**H0T>yRIMQwb_Ly zhI#)0HSl|B%Zkc5^y6|cA0XX{4INum?vj(??G@P0`mTrONt|>w?$?(_(=OgU-LBzJ z$HwAWz9u`Zk3c^m23F@XaZ-A!G_iLDt)NxKQdz~*Q}ToHWyZfHb)S~SloWrr8AqV2 znBfrfnAoc*&vA)g!_MK9p}TX;+v8`+)Ejwh3Q93)9OE7#ALb=-Qj6UfgLm+W~4=oBq%HpK=z7^IDz~l(0)6cze>+2GpaCs$>P1v zdA9S-vBUX#_Jlf{HHx0$_7nUk73KMB&NluxevGpRX2(tY&HC$J3M3M z)I#+%4NPp`#-%=Y`5u7#sIrVb7`L3?bTM39;*_MhCA5MsbLB*fkJK}&Z@4Mg*)NC} z8(9aTR=_0pt!N%Y39x*i@@&7FNJ4bl3DDLj_#1XM27TEC z8@@<75m>7=Or=jWi+ftoi820#4vzT7PTZFiAp;C^)(+*DrTWf zyey_YL|@=p@cZd$rxI?&M>5#BPlI$ci|A|x?M=~SqJ%GuX8RCXd_Nkdm*_kO?NpT7 z`qQ=Y6GGGDt7UPT;j`K*{#@!;e!6W|gI#L-QB8zv0@_C6=WiWY&iaW}X;6EC- zD&BQ@ud`L1Ivk$njrBaNkqHJ{cMh-BzKRE3KH7Wjt1CX@)%u17Fo-4iK}25aPL(fa zOS3l%YkkngaN#(wh0jR?P!C;2PBNdUd3$Y*myCvP8;}Q?KlV_ZJWX3wETXFDw883#=c%GKK0Xv-c}F7Ng3YdwDTvD; zr1B;5i$4#EsbGc#KbJk~5wzTXQ)(?lXB>{i7XS${ioo%;7**2pE&Q@M8pS_xt@$%v z2=frwY?PKQeJ^UyCU3z6nT-L$mcaYHO(`@PIQi@uMs?xm)$UwG`ZI5VAlw zITArqf{9Sl*1z3Os?#gomm+^u06-+i;W&jQQBaUCwP0#| zbrfpr!M4#8l^Qya@X>2{`O#$41Ar6It#h{BYkttaMyg(xDjO&u~nz zXzo8il4h!4>7``40&)>C{>qD}ntcxzeU-MdX){uV17xU=X0;y{jrk*6yXJoLS>(*} zLW%WFhV%EOjkmoDTwbl63BFHizM)9pR~^b^AWegfHa56nX}Ig)ttd8pa-TwV ze5UC1eM$Bo>hqG!KyBVL4pBDID_AG&)|0d2QS@bmep@E-f<)hcb5ntN?i$+LQ22^m za9i#agZS)I?lB*b*#7{^376emyvE&LGZ2_kk*IBMIJ6RVwSfdt*9Q##W^R0}{)KYC zs2*(sTBAmQOac{y#273V1pl0nGaC8k>Y`Dqw_cGk#KxP z#S{G{AE~(fvdr~HU;38BJfIrluSXss5&%B(O+p_H_3{lbm`An`E9x&3Eh%b8A9K?- z02dIm`zX-(vx@yff9mSw@Zu4N-mVs+J!~`ab-%!r5r4jym&Y$&xf_SbC~vVRW4m3w z$fY7+kkza&^7?(Wt-L18dZ6|5n)CDsr;>J?AVS3QhkBjNs9VGIib7f&aUQRb)?<`p z`*08Xe8UOyzVUCoR6gHh)HmJh4^ZzMa>f@TjI^utB~_pIet_I+`ke5`AHa*a^N&0{ z_K)nY)5d2Z?Z7J#aahVqTEfVbfonKHH~Z;n!r!QRIeB=suGfY{uN7P zWBrBIR?ozY=JUP5SzCCvF?g~%Jo4U@uem!%<5>@3cScSd4RmD}pMl;iik&k%s9?0= zAV-BAmJkP!Q=56wG+%8S90Y;UH4g@B$XEbH z!ao{;AwZFBQU|i9;BO{{D7m_|@uMg-t*dy_MQ`@H7;7LNZqvw1p5U8?n6gMp=9EFN5z+IQnCwTdbt3@g&t$t(=Q0>w;wB zJ`E2Lw_!G0upbgabRPOo%F@^V*yVG0P8=uK`Boez%2J$cYySX@*5#*T?#RoKrX3Q? z3qa(&YEzB3jCw4YPG}+|6%smM_-QnT#WIiii)~H#$#jWg!p3EsKrP~;x}|j`(wiQ= z3UsP#Ec%x5lYNkOA03}1Nle^Sc*LKDXnWNBHi}YPG98TzyF`IF2HF-`TFZZyzy$uv zT6Vd54n{Cuqj#8nwA~PpbkHc+U&6AwXC!KNMRo(dFCY--%ww8i1ERe|tp3V&I`VzM zJ%mD~W+1YqjD%w9Y);V8vVFH5Xz*`pTyQGEr~ z$O79#Fi-aXM^R3sS{{YMm{!w}(R7jrmkfcOxE1*hyVY>FL^(SyKnKyzR#$y|fgxHr7?d-2{8dWX(HTHc`V^_H@ ztD$yq{O>l#skBavtn45H3}q*A2|h#*3Z&oD)SJv+8PIXt`*rtOMcspoiOj2XoJ-h8 z9JSnZ5R{U70+C$a(zdVDB+=CBT?<#{i%hei@Ha8cB11VdA%_Xtq&ih=`z=(K@{)4I zk!@cc6tcZb1CAxu&&$enm26gp&&^?B<5fErxv}kPiTZJ9mVlU(AqOw<1t}v>9VuMO zCB1Z_Gnv5rvu=X?844tYotLooQyRD3lUo{8;OD+kfrw|gp| zW8&6Jy2*D($Sdj8=&Uc*20PLP?m)s?7To8<)QkHom)`h1+`1#7tK^j|Wrn4FUT{ba zz3b)Ikd%attu!tP^75qh{{W6Jo9%#?{MTBv^3Hta-&^?)S@wATI79~EDa7t4t%$J} zuHTIQFN`qkX&i?4@>7>e;@LxGJMGW8Dx8%fVw-8ha`0_w1Kp)|ReX0wnBzG)tNug1 zbLg;V7v2s?$ME9Bm7G<_t#(!dkKJmG++K_B`UKMDchtLN>0a6WOZO*?WZ=uiaa2Nb zF-n3%O9ez70*(A{To*s_-Zsd*No4Hwd0ojb8AqY_AF%%bQ}`b3@yP7wA|sA+$gPCW z#=4*q?%NIR6VwuVSI>O64ym!>#K4WoaVLS)*-r3ZU(5SmTu+@EO#d zPcd>n5q9(GItSG+)yI8uJ%9K} z&Gxmps+A8i$M<#me~IJ#cgA5ZM#pN*bI_`Oz5bRyntqgfrNkiiO^JC7=OD*FGm`Pl zq@SM{cgvwHxPx**a1&%51+=c0;J+Z{ISMnd@P#?fH%Cs!>W}6=crT3RGg1rkz zEsS$-xJSh5iCNdoP-n4ph9N~z`W}hSpz-3TaWAJ4xNTCI0Sh+hG@{2=QdpK*x$nk5 zrvu0wv!3%zoP1Xc%H=Y-tg5y~#X@pJ@?TZ8N?B&)i+*h_=UKUSuAF?Vl8y!HrtO({ z!DlCC_Lur_7X=h<0Tde;8;jT}R{#tK9>H0VDJo%1B3Trd9Gn8R>t=;FzOm%qHTXon57BWd_EfE zYW_^;eeZHn{HJSQ@z)UP@XzM@oKcqkoq0myB;b47#PjhcEBzpDa?8p|)LkhC#Q9fs zPxGWB{{XvRiN&@^CuhdUAlg<0t+$k*l&f?s) zBs}s#B&DXCw#M5Ru&}PKJnZ<`VyNV*d<-||n_dwq!F@jVQ-Vnnzm$Kz9#6*;=A9mi zRrDn&s?I~@e1_z4`cZ4A@l73PoMnCg(Qg_6b_ zE+gQdMbOp@kyzR4;@JlGvYQuB_TGr&FGfN(M?Hg%W|<5YMmeU?FZ>{sge3WZC?3?` zsb%j&9fY9rsSu9Xuo_#WfF4swaVbwrkg=^-?WG9$Idnt1a!upJ`fK#F^kvFEp)mLk zb=-F`ov-bFPD5>Dd1g(Pn0G3n@RY%2TT8pT1$6SgdA~RFt7&dEKBq#xitF-m%U+24 z$I1Hz-#@4Cr#XBh=u@#cx!lceB~NmU#D-u=TWhd(T0^K=bjGLR_?wb6!N+?=u@PWNuOG;3?X5luY8d%5^AJ!E|5k)TzBZ=3Q%hDV7Olxh zJ8|MKb8>^v3)^aR_EHvPX-g=bVsX#TLM-B#LaY=*jn9s?U$c5d=uDb1ii3^;4_R*5 z5<$OTZBgB0W6R{HmrlW?7eHRdBpg*}d#I^?jeHQ^*m?)049EE_Ifen zs{a7ov%mBX{{T^ssD?S$bVqOTGQs10yt{6ctZjQJ>OYFD4!)X5NcNLdU(BB=pFPaD z#s2`>tD^qCqks7O@P)+jUvXmp0K0cz=^sV^02AiJyqUjDrKv;|+j2^W_g`A`%J{5$ z9_bz2v#oxIc#PieeMj(oB({Eg1IJ6be9F3|2#IAh73?k$EnR28zCHJx?Nwv)$MZkz z?f&0^DZ8Mx@tOVA{E3j15-9H-jrD&y3sy zCHxu2#_md1wfSq0_`cuqKQj)NgNFST&n;OHlEM^Jo7{MHrYhC|>?65v zlgY+*9F!@>Uj;6tDM-3cQFM?+T5DG0EU!Q};!(lNytC)zHc=8SjS_SL0XnnKq^rH z?Lu#LDCh-re<1mNe0HO@=h+k-H^t?-a*P*4wfp1S4%vMk_U*nDh>-c8)IT$b=;ZvS zMW2ty;ye5$F$I-eCB<6aqJ_<`(!#v^;C!z4$hQ6Y<}}6hUsJC6Zmy>dt?flASl3^` z_~py7%ujf?A+aSZI~Jz^Sy*uFu?MKv#=fc2UNBKzkDRZ%PHs!N)STm(cjue&j8_8g z7u0tv?jH%lvdehBA()ojs1Z=oQV@ocP!+WR0Gp_r0aZF5#(Xxt>iVitx4V+hU-AC{ zacQ^wqsh-*(K)vV=*pozzk(YnOrqDIl__ZnO_i}XuJ7#;!%J~Zl{~5OQu^C5ER+(R zPn84h*iX3`$eRqL{{W-huhO8Saa;Bu)Cazgq@UAiuE9#=yuMFyyL{gw?gtlo7DIz( zWWML29hU)VX+t4fJwz-OZXlZ|0bUvWoen~qBP!*HKIhT@0K}iS-%O)8{&aFT)Mf~9 z-uw4A5gm}!M>3Kn!f(JcIbSr8FW@)VsC-uEc7z-6gUG&buNpZS!FJC=O~YC5$2Z~F zUN^`2PHo=UJ22v3!iB|F9hq_z9t@vW%Sl;KRf2%3Zby=*TWbzIexp2YjhE}^%b`i- zVNAkg@;Ho}MT~|i_hZjw{)d@xwxMy>#8+XdFDUtaPJY<Ca46Wx)$g&k zo)sZxF??Fe28&m76D{7(cl)^=lF3bXi07q6!)8)j&)}Mp>n%xbL^p>|F1G4|+5>nxZ*H%dqMF;4a#+T3 zp8o*u>-5Kd$3yYEZdLyP-H&ffcl7@N4&?*&5A^Zg&!nE|XZtzdt`jSUa{MyhUOC5` zSIFlv?Yvq>OBnh}n~@d3Qho&$Hlwk?l?zo>QLfne#q|FGkJJ6T{{VS+u{8-{HJoQ3 z?erElSD{4J^3#$_|>h1vE`gqY&$&!~tLf}6Lz0Xtat<;`+i@6~qe(scwQ1(mLV+5B3U$(mtAv&$lNXJ%s$Nm(&adrP z)z2s6KP7|bY%7-WCO+? zUixEKm*MgwzO{g5A*7+XY^vJ&t&fNrdIn<333XnvBjzllY~TW!6-*iw&{ zm0q6r9{YL2{l&bOAIYWH!hi28&QaT5UE8vZjLcWBf_MJ_XUXD`GZy$K{{UI5r8n|b zja_K`mYe!MlKL~5zgYE;)ewZkYIU6trZR~tND25!J!}U`=%I;rD4b;b_d)(Y z$grGUsnK~4Y_WaEg^}8VW;rG-$YW4f7DRFiRj)z_s{6EM-7(s?(pZwo49c^JSxoE` z%W5*pt%#CuA~?TAEjlcFd}+;D$_o#umFgBeu7eawNe0Q*!sEi3c$Y>fu9ROw?clxF z5m}4JxqEKsFi7}g$=nRcV4XcRtMedur2Uo3a{mAr^8EFkw+Zu4975iwRpMu1;=^iI zHEiawYZq%E+JF`!_F8UAR6LDvmuuTxyjtpd4z}HPL&`6qB8`@ZkW*@j3@Y#bX}GA+|%?P;}gobo?nBH1645v;l1*P)!?3joQmixD5d$B zJugF!s|g83yg}(faToWakeqDO@y*!D0<&$lwZCDdETul^H9K$7hoR;(r__|0B2q!N z&7BGIw_0Cf6-8yT#)(}g^*WohJ-P0WHxcAQz=Mua8>P!Bj@VC9Y`spE=e)-)Rc-h) zw&M8fEKAFxI=+8u_!k6>)>F70%*J9OMM(LWfyRj*X&T=3%zpQ^eEe&RItQVvQp}`^ zqqrT~?KT$`8~*@;ekePD^WjQ&kUFIZ?VD+$hNKbF)p5jd8yDG|o(ir{nWnn&Qp-4l zl=m~4XQy8xpOcI(uat`4q?7`6>8YQ4XmL!-0kU{G3j$I$VwZMrpgE{xY~(Vf!_T>8qAH)xc`FK^OdnH|(VD(|iD!NHX>rwlFUuMRSW0M_e4z_k4rrK21 zrsYlnvSFh8qr3t~=;>3fN%sXhQuQOUpi9&PaelRD9P}!-E66kD<^EfIBTMU6B~x8R z6A9%0jeP02O(JrskK*H>T8Stp*;@1JMpcDlM&6yJ8%fh$dTCOW{)XWkhJTbe2JJQI zYB9SZY^>2Zq`Q4W6rXKlzo@&XBF%C1uKSIOHBi%9oNvhmtSC6!YakfoUaPVD^r*`3 zAh6WSHcuH{h(*u%YCm#k*&DZslFB_NVW_lKqRMW6Wkpzm{k)KqxEK-+$IFZ;=t#H} z#4Jo5k?JarHzF;U5@CIjZ-DE1(%MU*r>Y@36vU%O)S{ot05(4QC2`9mC`$xFS{3RL zxS@p)VI6A5vXXIWfaXAqtEjQPe(Te4kQqb}?jfzJTBGbyqDIVh7nC{yB;wV$sitld zufCw>*u0leB#d*~mBc9u){2?6Tfhz2z#Do>7V2$k+69&kb)ZSyJ;IsD0h5g+{+kn^6%0xn2|Di}e|;@?Cugf0tBZp49{&Kg zqM;(qM?o4PIx63VCikQt?bpz7oD;}Gl&70$^-2OT5Vkdg0IsmgD^ zvP>MFzpF^+E>)H79wBZa{zVb@$kr@mp+tSC1l83#JF{8CMP-|sP|sA(eB|8IwvHvn z;^HPucl5ipK{h@_)0Od?N%dt_+^(;pDNN>Ph)P}KVx=IB3Q5`t)c78iWlojrt9=Ts zjzXDsMPZe3G9GbpU?v=Xi9oqg_tmutn3utuf-rJ01j5%Znrg z5=Zz+qaj~0(jiN8YC|eeC!hqAQ+h%_-q|8&w%PT7 zT_|V&02c4rQFtTb!!f&3>HZ|Vf`R?SAP?Jm)0PRUo&rhjY%n)1Z!IW4#C28z-(anK zodBvNJWp=$fm;(F))vuw^sH8+C8;6T`zIM3B@8gzt{?h)LWrZgk|PQm55eNEMsW&f z8VZwQE|hH*3ic7Vs2+C>m5@T_wi1T_0Lw^FKo<6CL6*x5s7VE*L@lPu9<9LA_qW?q zjjfXQi5wUbh}^_*zwr_KDSJAFt}Z0j4l^X;P`517M^1!W%9Cx zNK>uj+buMLe~cNpBz3qwX{zGWDsO6C2--1>r23B*<*!n=U0}Dbv{Rn@LjDWXS%Hjg zY4sj}?TygV5;uFi>N0KmhOQC%66|qmV!@31Ba!)#yQKhm^wyNJ6C0{f3rL8=J6meneJ36$559wCwp5#6 z->vF$n#l-M-(m}v;#`|3`pj8zu(1|?Cd$2C+VwXAtf}6F7BP~$XF|z(;29US@?$Mh zG8~bIN{!0HY1)0-(C5na=#1CkBaX7jX;>UoG#dc=JYGsHN%kIeaWvx}4bkxgz4|KM% zeAW|cZMn~+sRQC{#wrT8hydQHY|?}MEa<7(lp4y)FB}eTHcBvK8Ig#EuR_Y(DNym! zxoz5~T)w64Hmf=%f?W;*m~{xq<;YZSI&#wrgE5KzT+pkI8Vq4Qsmf zQWSsy`>EY~?vHZ2W;IK(Rps$Ipq~=nwi;FLzRaIOhW&W1xgb+tXA(k^`dkO{ zrq=7$u4~s)T?+Zz;xyVmx+~$%*B9yImCEbB))Bg2Bd~$u*TDf=ViC!DKeZXzV@!ZZ9vFIrY zLSe@NAo<-I(mOmIIOvCUxvO*ByK%krIJ7igK*`E%0=EljY!l)>8);m&=f>Tstl;c* zK3nZ$DCjOD^{dA5X7wI^(fo)Y{D~semjBU0(CxfNe{zg zuj_y4<2w9yIY$!8cIzVDll=9xIVqGb_T9QUBmwOe=RDu>cO_csETZ0@ol$31jGst* zX`A7E%rTVg=X`N|;~MppaZ7chBn1oG)4EnZ(Oz%4!0&3m&lk|OkI1RIIGlgGul~Q_ zbTfF&!rKXy&!+Gn8A${mZB#oSmtKHg_PTkC=teS-UT=GEL@$M06(|x1-Fh`QMBfTbbZoebuO@Q*Q<9xgj^!o`)l1UB#uZ zmQ`8)*I|&AIN3@saulEk+g6&K-1F2GoMdFORMR_hrwWl8dnrgg1BpmAc)4c8CC8jk z_3z)=A42~CPJExcn3P4#^XbcL5!)y)fc!+}XRFg{L+Bey(Hs)y+v{rY{{W7770q%A zx&Datf6*K_&%QzXK8u`xT)Hpnel;@RRQ~`~T(6l*=X`_e51LNpSr`6dUS+;VmV`ZP z=v!h7?6BHLf)Ln9@~^QM$N1>e?JA(7kNQ*p02kN%Pmy2AH|4SKdy;QISN`SG{14mb z^o;tB=N<3*BjY{S?&f(M-=1+ve2Wp2%SiOmB2&0`T@aTjG!hEdgruy1FXdkt{Qm&r zI@ElV+pajSSa|-aJbz*JPsM&wb-XW>o@SSK-}?0WpO+5fe^P(1PIJg}^0|l9=6U>x z$<0zR_{SNW(T&VRY~5-}`^K@Qwcf znDR>Ji9YZB{XgVJF@Ij4Rq34GPnCC%Im*#{I@8kE6~+8Jf6jmzSkv1?zxdaPpJ-r& zKan{|%Q+om{-=1%=?_Zed_OBEDKX9SvrcB8$vm~IRVhlIRI`5~stmpl7)KS$2(yRs z45a@6?nJTiUFq!>Y#|4@4)GV)@RVe+uBoJ7&2dV;`lH{+kiJ-oW>{OsnL0jI7*b z+= z=F{*wSCZU#=?W~xZ7mO4L%)@@f<^k@p{V&E%IemsI?`?Q$K(F|7GEFujh9)QT2Wt# z&>#I1_TvDtm-nntw(D8yr zf3>}AvnI}&?7!3ZAm+K45ToLr%KC~yae$TD{zq-?TxZCBU#YDZoE?7=!CNMuQ|v?d z{>J?pa7di1y5GxiSqUwl<-Adr+LM=_`cZI{HbE#!BU8|IuOQa5cbmJynBouA?RIu4 z(XaD9r^qj<53gS5chJe>oOplmecPt@A)Y%4R(?u+-*f;@o&!qve~SMA8)|X!Q=jIr z^6qQpw`@zsJtA=)F}1L-SSR?4B&eT#^sf5*aIm`^)m8aCvT+L`H~<`e$r>d+G^&fM z>Rm&kB$0A%Y}{P9@at8VM+2~y0*z;$NC`o;?XO!zeE2xblmt((3a2suGG} z;h#eQxaz_iSLgh1St(&;a-C^6(E9=XKmP!yZezS}!S082ajrz|)F8KUST!Xnb291( z+i<`7I%;|!b$E}<{#ov~O-E5pF`@~``b+f--0m~OxL+~M_D{FayWii>r^zFaF`CRi z+%7M{8=n;pHT<&~>K5IY(pHp(Y`>bv<*L7r@O91!+ui3a{X6tty?@&)0@2dgdp=T^ zja?oWcK-ku^zGBX=e(G-%yJKi2O>r|lygo?2o-MpiUCPI*cpMgLClCLTr&mT3|xvzN=NV)bT`f2qQ zwCJ#GHrvEfmg_Ww*RZl!_-PoIfN^S46CdJrU|d^6<6Q2|P4q2caXxRAVOqLb$e=3L zDcVMzJgTZ(eA#CWgoInq;nYU#!PoOx9Vs`&%>-MBm*OUI<%BsRbB(l~s@ruF?Ee6r zQSH+>vD&AWSXi;bg`Y+8% z;y*w?MmTOS5_=f#OY&-s23y1>Q*+bVF1q`KYOSgypCBz?QT(5BzlwZf8r6UA+OhcM z`kw~yI(Pif<&wJ2wMFIq6jSKGxBbWFvoK!HWhP|i^ElJyPjzm}oa4b|$6Q%j)=H0w zCcb0ib!~({GRu8i{8{$x)5FZ zn|;~&$NsK8$~o@%;QME1F6+9oiC5c-m{{XRcN60xUD<E-w+2wM@-NK*$pK{TYB^~I->F#a>6+8zOv`?UQ>qSc=(Rx3KW>w%zFT- z*>dHqAqYZ8<|L2>dzZzwDC6{L+XcVJ59sBfV&IGd!gJE$Cuq% z@V#zLB`OMXDxF9%u~AQ+;ypzV%x-#G-#qeMp5M$lsO!|4=xWKw>`U_iW~lm4 z6spH!!B9HeZ8zv-Cx&v=$3otI<6Y|3pf@RG9#yp}()Xo|Z0Y|1qrB4bz1(gmsj)ej zZ?kTji*|Ho`Gk*KGrpV0Ns;yvFDxi3aeKDu#YA9!%Qfe6~)Cc;K>Yw%N%lvz>t z*o)MEkL+^OQGSB|0Lj|;A&k~?1yr` zoicC9na4Yz;^bvH&ua7XKmDZEY&jtw`fOrEKLqkv!KaURrC? zpyFe3*yUw$anZd(+atj#FBK^J&1qDhb8BWu(XL;b6Vs4&*4>z$w|PvqpmvJ_Q*FP6 zEpCi+&?Gr2_G8SqCC+jp(z4Q9e6euQZHAc%2mb)61mEqT_$AmSmdZqYvZY&b2Hh)Q ze14(cIm!7S4aql_lhI!=)e3QRy5n&LbqFX?%AV>8pqLTN<429?G$d z$jL{k6E4aUrd~>bAS@!+_w}xSL*zSpR$|V`zvOsbP3UC0^yzM?k{TPdR%)Xp^V6l zv+xpQrfxD~4^+Jw>!9DvSo>+s&yCTvvmTQAqPtJ!TY7CNb&uj*ta6{H4&h_hCy{Xs zw`4h3_@2mM8vbE|nBuR0^bgr=T$_;Dx3;Qj;W=t1qg(QVIy-(*!|c}F;o~xz$>VWM zOOnPjG5pUOmD()lSzL9k9YTw0xlQ~<>%7y`*zsOVsloCadhwU#c_Y%WxZS}@l%VQA zQd4pLm17=0ex)S2DE$Y_!N8OP;@w~GBqqlF1s(Cv>IHI3=xzMB2$Q*(55MN(mPV)D zYK(Q7W=m;cRdY7Q5ZH0dOMADtQt?g5&=qUm-lZ|ykE<@~vzo-OBQWN1nJ3y~`9pEgix~mh&UZUl7q(zf!x3@uzzeai=o_KAfp2~%vgEp-y$x%LZq)gK}97uzNFm&c)w zg~fv64dWhR~mPrX~(Oc@cHU_{Ro)@l1Z`9!KO_8n4B#HgonzzBufxlE)9j#^IR6tonHJ zcZ+!&i(Blj*H_~E6s%3T9Mry3RiC0MA%xC)`>d^UvC;A>8ij1xxOE^D`P1;@o_n!d zc`WCmDeSj#TU^DiDPtUljlN8ap1Oc*qOXLLUE4=3v(82@vO(-Ga8oG=TZ}1S^pf7T z`zcMXFT}9Y<`nyr*x6J%zg4MQJwOi9EOqu(M5%G;TURA6#J?Mq$TqYo4;NAUz}0Qq z(UR(mDlU|LNdkF%@(WHSAzli(2TwXn_Q(!Hlb@-)&Q`Rxl`dLTr43skkUrX+IM}vs z?n+*y4>8NJ>1}Iza(zR4*bP4Vwx@_*g%$jt)Ty$Z_Y%=i4=we1jW4fFE2PxnWhK<- zHo4eAkX(;*p~rIW#8;+zI7P)=!{blKqbRNUQj6>kkKH^Qt|dx%HWY1TBkb_4Tk)$R zvPzGZRVgfzmN{-dVuz;c6c#^07uKm~##;y2zSidGo9My|$j90OP*$VV08Qv##FugR zvl%fLn2h^D{{WZk)hDehIb)#LgkMrIGB!FnTctjbU*<*c`zs?hDJr5zrMn#|N|+>* z;5@$Ca;gQcmybk4vQp+-Ye)(TKbc3Ry3@5$)UCGGL+3?YvIDZ4P`~BqL8q$BeKJ*B ziF%P4*k>atPBS3LQtx06;c8;5q~0)BPhKr_WI9~AanGix_fDhoTK@oTSi+pV@hYpAPw zO3R;6G@M$&U#9+t+g6d2Z?Ro;e-V%JiAqo*`)=v4?WfAjbqU46%vyp~Apo9+`cPes z235AnTpm9q#Q+wSSZb^3O3Pv?j@PE-6laXhE3%mCe?Nhued@qP#|6{Iq%;z_3=rRf zwIlGPp|qPDd}F}6AVX^R2tij}*6Y%kg}D))mJA*~mw$-Ms9Jwg0=1&oZ%}jAXl*YU zmW<7#HS;5_#fh<|p=4iiYjvi(5u)~b8^v!kupsJ^Iu5kjuC`Y@Z;R|nvHYqs(f+;? zl#yUuNVp$iploriM=@odpD?qDqD&aWO+sBUlAx@}6j#nqrwuD?MQVHVgvjD*TZhM=pjO)1xF&p-;akP=QFE2T-73>A1N z4Mjgy@6Zjp>8_+{{xuD|`fKaZ6p?D3cS(rLRyphke~FiprC1av^#h@!D_EqpqD7v; zyxCG!9HQM1P<5x{GQI2d0n5ZVzNog}WgS`u%7D-Drk0@LPSSij)Z-qa)>eV?c!Q6v zC6pi!GHpc2DO5LKZsn@#5?M|90#Ei)eb4~QKs3Kz)=UoEOSb1HFrQ(+_g4MVFI|l# z_U9nN3fXy?hS&7apjG&aCP*qjDG`;%C9`n2Yi&dGT|e9_X^X1b0KJP+0R=YKPdbuF z3zNt-a0u4lZASp|#DpSEhPAH}N^DU$_1s)(Qj9v2^5{H|0YzxqH9a)zY8;YGT{#vP z9FdyFyoJRIx0vX4y{a1(!cpo!t9`MTL|w6Y3Nj1^1W1D4#|GRox%alIh_+-|pWMQX9td2V}+s zUIf4-G@<_hkK+4{SKq_x+`23=y)I*NiFJ88 zroKkGy`PFD`eb#Qe=W=W@j4d=-VWL2(%Wi!Tqj)W+?cAkC&K6iRV~~u#5*&hqnOvT zx-2M@cqFe)ac9O-ozV$WLP$So7NliH&wrrQrL6idQ)xI%9EB@qq_Ux@1t<1VBeQDs zAw6mzsw0naMn%%f_P26HwFB^{D%+Q;6SqdPDUsm`beqd6@gE4VK2+2>7lOXdDL34Q z%;FLmPz^ALQ{td0>UZM1ie1Jx)c~EuWSJx@9H0+Hjkf$K>t!)FsNykki47aAml+?w z*44ii^#tO~2{NPne!9>HLQ`@3DW3)PHSbI4D&}?e>{B7R3h+k4l0E$?TQ$i`VU678 zu15hcFqK=U#6|B|wLcJ_xU;3B@$3|RzM@OuepD0_{gqnw8!ln2lkOU2P$O*-@v8u6 zHnc@8W60HS4*`~PGKa#$D@sR!APPgnBha$}NZD6f{{Y-U`LM2`M?pifoQI9s7CB2B zv5rF^bx2N{{ggjpk$V6fhar;MR)*R`Zh&_ZpV_TVRMQ0u%F`p0 zi1w3C#)aGzwq@B6_gm!>99mP7YaZDA%T<=E1JcHf{j>@(wZ7o@*_t#w04G4$zlLPO zokMIkmWQ-l4xeob$wSc-@SA#z{nwNBL$|l!F zbB`(QzDFdre>s~vU?i0Xo>@Tu0KA@&@S$%o8C;Zfx`v+34IS?}cjz}BIuTt6)b2i#-uGA?Zc+d~ z6kmK4sb4}LGam^l{v1%V<8+NFnj6VqZBg_ik|L_)e415mmPo6VQQOgsn`umZN{j-H z>?|!rfA&o)s63jM(idwr95rNxI#t!BIHPgp zYBZ|Js9%wG^YD_Di15N3>FKB&U+eA8NRFDg=C0jxFTA=xYqMW=i9|k7!pm+v%P2_vttR`e?gpT!lVM)mN!%>d86mLHLJ(|7 zT7qdwYc$EmSyw zti$dlHb@P|S-BnqrDc2{lj=(ElEpkEGGn{hJc?Fu+)h-otsCF99e&y>;3 ztNScXsXKIF-$K%kMoaY}^W3-QfNCnFJ8d0oGN!cl6$+2pAkUJ1h>K-;d?=0v=J8LT zMXn^c@>SPMX?%0jqSj~q7frR+dzG`6i>E_^#L9CCyWim+`G*! z#Je4~Q~vx0qq2#~FiV!ZJp&Z)+?ARjK8)a;TPgKyAs z!ecPCJ1YdZEsnC9ij-UpNxHoKlm+E3!!<@RT}3Cmgvhc)8SxVGIwnz5B%YS|NEBGB zS9Hvr>3y0Y(-Yhnjqk-t#<>#ryH9v{eqd>eoMif;E4|3C%E2yjY`#6T9Ogn2mOnd6 zaFe^j;T{z9^c25JE8)K-`q+`?nCF^PKR*pFxHKtsNlI~DF$)JId2hZ}Qoq{zsm3jh{UN%tCI=v6-G zQocgk-$pifmGVQjHaY|4SF>#-7ZumpdS*JB_4nuk&CR=C<;iJqc>UXy@>b>`!-BOY z?@s|Y@f%Wg1K(8~h5CLtPpNmuYwtIfj+DnDTMe=@@?)3u^$^lX7xKDmk=Rwq{!_1^ z*%yqZuvo0u=OOlz?q(sNSnLw3RW>a>Gk{S4$m^fyVF-Yc4Z# zakBBwwyS(?i>O$O>J4(YawEWSD7ON4S{Pwg2pRJCi3VB@$Fc z(om%SHl_Z?-}175a|A~+8EzKnQ5gqc-VTLm>(;F`w00M$wcFIcLbk~N04iUqxF8fJ z_T99opJl50sMxBy4QRQ6*td3i4JeA|P$R))eo>H!S`cJGV;Kf8D*kZV zw|-O;&{QRTl6dhXwX0sCd}IYVB`u>M$_j|uONmJQdQz78HP~i~tIu-Og~l990qJ=b z-a@%&a$0C^fpe7i$X;3D5}=N;kvamh)E{Mb6;hz>T{2d|k1lHBBNaPn)Q z{;w5Rnn|6Z+TPHkA0j4mj}-~b*-T^_kf`scP;@9zSL0r9%kqBW@sj9w{AUpete;u? z7vGNJVbE~A&pOW`gvQjvZdJszplP%@`iH)}i<{tTv9}I|HynKyt8$4XAmsV5V$5~s-c|Dcl(6VhS)`;AZ)dfK}t$XeRTKu1j!z%o@ zZ`ldi4U|U)^uU2((5PgQDa%C0C zyKf}8xgQH7UEZP=n^@Z50xT-;SCFr<8OO-{q<>$YTg-8Mlj-lavNNfj#1@>^T^Mwv zg(hTVw+SvRj;Rg4t+(;7vHt*pXrg+PwmteU_WtALKj$lKTM&+356^5og8mRZ2+-cW z=E~DN5)x^jWd4!9y*pR*;n-(;Uar>ie4jJSN}D^8^SFGBM#sMVl3Q!ifpQS0nz9Xt z#cPvtYsWrA{CW4=%Vtj=J(u+V0B`nKJztCYx3Q`1CEVgJdgoUSvh*UZ;*tEV_cDS8}+&eF13!3>X(iopqRhY{vLBGlGc z1zHR$Mg)z?Z9f<109e+QGy08r*YpIu-W}k5J>zxk4~0%HYrj!R!nO`p9tj~ykW!*N zMZlv&Y5Jf~Qa+EUYY)gffyGoL@lJV^z%7zC7#T^h?PI2lxPkam_G#)`zdREduXkpa zyd=xhj(u$XYw*JzIA5rqP~3R`0OiMiL`;KVUiSY0TOH%Ayy}ks0Euyv>Fuxa{{WeD zt@6G?5A`zc#Qy+Q@2bWgVn1+TaPI6tNVJj2IHVOxbXwN=hzek&`*s@Vc@N^QHt4)% zKkL)`j*nCMFMglIwy+s zbEW-na9-l_9LuU$do`Aqf_SAwC?Te1p=oIe=n^y)*l|A(-{R^}jSewhxG6V&VWGcc znzQ^zFus_3gOhC!am=q7=8U*EG`ue+#Lfy&v-qu(Oxv}5U*@fwhuc`!jL@X-rylU& zw=X5z{E``HT)xu+OK1N86lFLKR@up~^Cp>R9XP(yOo%_jvv{*?mM%;M57P8!+VQvj zM1eq`C0;tQm~rj&6pxB^!V;ek0drB7<#JsZY3fOIz)ieT+Pa7%{yo$`c!#jl>^KqK z{E2MtU4nF|HZrPjvGlX(6XK zuNwK!&3&}!r}cWBc8-s+U%6*v?5EZ48|q6A&uqIf^yR@b@wnN@V`TSFxfpXEIfH?R zCA+|aU3DkaaT~9+5CF03UU|SzTBR#?I?L(x&-{yuHtD)5UP*lsO?@_f8N~W`oAoi? z&#HX;c<#bxxdXhp_)})cOrwv6lJsWgAQFYiQmvv@azGmRbyDW%$U5nw4JfQP{abxM zyykvOxhconq5gN!`G56)^=*>+&+dGFVuU%1?Z**iaC6*Z+ZZ{HHxz#+w0bKjX&UZOAgj)UZt|1r zUstTrJjL*4qqah_L21+A2I@`v53+$R)W;!KN$wW|j%mP`og^I!#=nJBZd;zi71x5m zmW%{ADT#=7TBWYR_uNWDKzQ2P)npf3g^o}Om<%Prr94gb>-%U{$syG?NMNE04y~2+ z+$=S!U53jF5n$n@LvrX$U^dx1LDcwo*Dt*mi>+qS(LJ!u@LXJ(5IH9bS15L1{{ZBC zCfrh8ZNM*blmb*d#c-U(vWy-JJ7Xsg%J5D;qQ0Vd9GnjZ#rC_ljbU=Uemdjw?0kr& zrX)zPZ35=SdICy_y-SAUqd2P{Om`_hHOF7CIlE`)Kjd8W zxvpAQxBmccJtDuSc3*!K{5f#kI`de%&a!{*U)X%7`Q;@*APOOrh7EaM)S4&{TfQ3goznDmAU!mwYxF%B6MirajwB`&sy& z@85H!_g}ny%I*V?-5jGgK15`f0>WaU-MibX~8lcet0QjmJ9c-P&nUe|liVs=Hzh^LR`UcSc4lCFZ|@uwlVpF(e6px#dy z$$}D65=FONKwAFOL|aYvOcbe0)Q-&JnU$r$mT$BhDI_TV2A_t?ipxTl>q=djef=o? zJ>%f=_j~uv;-R&1hF@=i#*hGQr>*>agPx$U*a1oj>8Y+S=N~n@Wk*L_FUzCAUNeQJ zxx20OK4E`QpQ~s4sjID9 zFv_dz@<&bQlj>yTtQ!S5t)tmj_1KPAYf?$|BC{j9mYyII0u`wD8a0wa)N3=8WW0Ag zFVSkZ-$lS@F_!gIB^;ULTRn!$ejaI)B}+Px$iQ@HlILSDvDR`VO#<>{B zF>5(39&y`VQ;qE23zuj6Zubk3#By%oa?{+J!^@YU4ewHh#akG8`pArm1a>UHz2du$AC>L!;#xV`V~ zp#E%{@@9@Y7MlaFl{MlK&nU3FN!~6^*^XrgmGRl7v@&*N;9Q0$M_0nOUP$T)I(bzu zEyKfVw%186#fKTny?J5vi#wy5{WW&W>YI%J0Pvsm_mhjpIjp0w}8(IKY@w+fCJDYlhwfRU@a0bm>8@v*^p^ zmU5YB_?vx|apoy+W6iQv&>F>K6pdn@hnjxc0&R>%Bom2^nCVZT%A6+FN>Wv)*+Pvo zmtt>fxW3`kag5Wy8gcFvpWYhRYzp3-rfavr?t2z>cns6K9ydwz<0mUbw=rGWf z2PQMQXK1zbP!xs#00P#&P0bI}^X-~p;|I_mnEJEtUU7jL{zThRP45{oSyN{EY8*yr9SBm$6Q8FBhJW#xpG}`h$QVYLP{Fku7h_@m8E`6 z!4s1jd#Y*462L>cwA!2`xSqNSH&AXFEx)HeY{z7~``WJNatDh%R}(qd+_B3Qr8umV zM`1`&N9qmwiCOj}*BktU!b_9lXG}j|M!%|S{uxJM{C&*++sQ`BKgrcR{TlxOo(Qb< zao_$BxjMLhZ8#3D0zg*F0$%Uu9p_Bm*MIld(=rJdocs$)_(uD%oM;#> zJ^;^Uq-Gm%?Ob^5>^*!o*D<%heU0Ox(CTrNSNgh>_}#(bJaw4q<9s#X;)o8)l!Nfv z-tudlmX+2&%*(IbDy^?>C6~jyliX<;S7-3fA@a~4LQ^hs<2SWdJrJW}eU(kUr%inv zvRi6w*=Ja+6@PsB=Pu>0&f{6E+?-U%J660@$f-*uDoue3M~SYh;+6YfnMWy%&9^DXl%bJr!b%-%ixKB)_iitjuL*WMTaw_Wq}o{^u0diu#i@~lh zhcf>F6o&c}=WEeuw#wH>lt#*Fx2%>JS_c?&7?J#&BA3{3+^|*gd7iX8c@_R;)IPTt z7{AKNtQNA(Au4j|DjE~0{3@#Ll>G~es*+vFOc~jTTLX##qjHBXYzX&NeAHw!7bucH z=IoXgB3tbtNgAx1e%b<|VJqiVMv@@65gY-lSVrR_$8|^G4{&x2p zeie1CY+`fw>&`%-gGv+@&~XX9_UcFDPRngbQd*4#Vl^%!=`6go1oa*^rFNkSA)Rk_ zEWJ2H*)x=+e8M-jH4dn2r-kc<)5}Id)_cUl7VLt=L@4 zzX14JbYADgnzyf$u3Z`_WyRSUbYpCEId6w!=NMBn0sa%g7ijxQ@Sxi)Jjbv~>UhME zguEnXu2#ZS;r9Vv~_SqJQ!_38Skdl(1 zH6pAljtZL^V3%Msdv4Rf>!mvu%GW^ISk$EN4y$PWHDL?zh2=}hmLe#dZ&k1DqDpx( zTJ3t0Xt15w63JHdjXope?yCE&N%kD8UB6OG0tQI774uFWn?Nd6t?Z@N!uk!ynJ-pM zai=F6dX*$Rl^@Ea+>84>C=RH;tD#|4wssVH8xnUZ=8~=cL#Mi;)&owQh#WpSmi`7FI(GGnliJfwTqz|keQt;M$RV(OR&TdLpq(ko>0eMXvOu>b`t=k)GfGjxx2Fh_mzBH9+R-pUSS-MY9Nm_ZR5~kYtpvczQ;JXIN>_bO zM7+l z*%MrUKbk!RKFTsIqeVt_w4ql20K5fP*`S|c>Uk-}bw$Ut(#(AO2MSfJ(V6 z?)H?6k9|~K#%$Hr4<+uV{!<{YY$w~Y#dZfR9YA>nL)yC>i zv1;SHeTs@gkd+mSYks!&)pzc;H`H>z4!Ew2mbJ%OV_`#kQk_5`5-S$@Dd;ic@sAX3 zK`A~81drKRwv%}w_DzD{!R5$UkCJ(kG||JQ8oiijAS}|7^#U#rGmCVymT~66xhflt zzR^y7%`)^7dbcjjZ02j3vve!bkTm#~-HnI3jHuUZpb6c2co27zWD~VBVFhjKrj5S? zTKgL1+2F;sHQi)ru2F~!2!1n-o}~q`{f3J4EKj2&_mIzH3*?v;TsAfR(x2?C`&&My zea&mo(K7LFl`XbgSwEV|7pO|BKBn^zpp&VJY<^j#m2^=;Yk!~WYsw!&@f^|+3A}`b zEAj-krJw#(P#+y*8C;YXVc` zIOQ;fbqgp-1J1^TqXpbzRoZOtB+6otPD#bBcF~kH7NUKdPPM4hMAD`1o0s9nbkFZh{w~~KCI`=NiIiuM3%GanD+)vmmZ%ufC`{v$3ha;Yvu$Hj% zQM*8;FVja~w9sd-7_Q-KoaPse8#b1}R0JV^`l@iO`;SVjvJlU5%C}}a39cq(!n@|0 zh}pf{ZID&>lR^Igw-LovjE&@CI!O!5O_+5D(m+ZFyQORHYwV5t6Dw@&7M5H`$Vz=+ z0+|jydwqdMo7ak$XMhu)JED%y;j*fAr%F(?;P^6TT;ApYdk8}!qmUd_m`qb`) zumZ1j4;pb*;w3rc7UnsnX>|oJ$cs_cVm3ht928<~;+1a|Txm>3coy~tP4V_M0YliL zMe9!Xq)IdbgjzbDuKYlNRpv>nhyb`LM;W3MVH3fNBQzKI-M#W z$?+rx)URfjVM8i_Bk=7Jax5(-g zK}q-8wv?np-$*I`;f&f<@|f0}HnCVqB-GZg)r~!zp}I1vd^VLM(Qf|$8e2t=-&^~5 zFHt)j*&v4>86%-_+nW^I#+9Fb;cAO43|Aj66bO;h(p6xkEC5H|D0XcRLsYS`yg`Q& z1MQcJt-Zw%kZnrOZH`rW9Hh2Z*_RQH$t7Cjf9X2e}oX?{n`qg zcq^L+{-dE0$=&kcj0GXCK&eC;>^S@E}z&#E*$J~&z z0Qra2>4JRrj<%qZ!etpKbSk{Gd@s?RezI?ji?Da+V-oMX=_lk zoU*c8+k0KQ58CaQG|r_4T3v7?f|lKIB>lZ866EUbgciOpVfB_rOYMI>DG=BRj+AH! ze2{+XOYil5g_`3w+U_(xl;)1Oo$s=htMj;}sm`=$^K`?q0X_#}$JLJ__BvBnb0L&V z?g4U^+E4(G3zKt8&*V__LF)LmpHRep(4|%h%oyoe8;Tp$pCWajPnniG60d_<>d8Jc zv~UpO7P$(RwA>xnx1WWnJGpxr=vh<2*1EFP!ErgbZ{lO4#$}~jeVIvF^07Cn%XVcb z{$|TPZABi*6Zpzf{wYZcaUD04YYL~bwVtKbQ(?IAgr#po?X%Np43Y`<6Iz>$SZM3n zCLKdkg4^}jRMy+*K->3LuCXwXs%!BjnAsUf!KFvX5XjX@J5P^|>aulb*Qn%deyE)> z$XeQuo^Q}k!cCh5r{8Z1PTFk1^;bbYgyR9q@$pty8e@qn=ulMHx%Lt&lW#^Zx-F>W zryk6KNZWO%55fWt08vUR@a`Nbx`c< zO1X48DmI}w_3kmxG{#8;Lyrz`P*Ah5k=H_K+V=G7nG0(CNI&~p8WLQ8jmKpEr6ueA zx*EKzWUZdIK}+JzEyA`E8%XGodQ_*fZW^G;oO(lhf*e+!I??Lf19q-RE?WmNNm@dR zukEWS?e;0J)hTXQ#Q_cvK`2oC>FmVi zw{+(o$cL|p&p`M?iU6IYUthYdBgj(yO1Wv<_>M8XzR%_z4Z>y8K>Jkg9aa-MAmWniu8q?XIL|trb)u~@{kL5k!#iB>06CmWIf8LBXuOsI8eY&NI!LRH)%AU2*{E|%R>~C?g46;{0BOW4CqL5um$shP{ zT!Yt?dNqQRbA`|8>Tmx5!uM~TbJ9j-mmL@QcB@nNiWI4W6psP2f)V+)E%LR($TFzv z)lCoy)KKGR(Tu(hc{Q{{XH+ z>=bV9{Vw+3zI~|U&~kZyr(DFzUBmfyTW)vzV=oBCb@Az>sl-QpEx5Gw3A(&2ex&~Z z#{U48RYvT!ZEybK#D1B|Yy0~iE&S2(O{;X(>#u{${+Mh0JO2Rcemg#wewh31^^M)$ zIoV#oaCiBqHqW|*yy`-m$6}(W{ByEWf>emnn-?W&)mGYor4TFZACdfrQ;ebAu_YL< z?|*_`ul?Sq&Ky4ncI}?rSLR)RqWZu1eSe~Dc0bnd(2sNf0H=q29r}3grhgyIa>EIW z&tmzm9hog+r^U&KP*Bj9b%n6wDnpvKQ`NVPPaV=vbrC+EB@Tck82xrz0*LH@)j_`vtZn+`W}LquE01H z2>|FQO@3o|HUy9EsYkKAj;v2ZKm{n|-7A))Y)Y&N`)HU=gotku4+OUyS0%*B$wnz5 zDUBdUbA7%QT|j~_)KuBa}59rB*~~YV?$#d_~2pC9?8DT9HdAZI|sttZPmBlOo9~0JIx@ zE?WLJ6`u2Nu?Me&36H}v*vRwp4YZi-V}NrZDW&aFH!8rA*#_jAjova{W08ubKdVIV zjQ1HlWnT8gKTO&>0|E7)^>xOA z1TOmKXT(ka0MO2B8If&Q>v1+Cr2X|rwefyBfBp?7%UZrezXj4yp=aa2s7HN6h*v7( zyg!^VWQW?H$0M}kwjlgYK~r6QiK@Myh5jCusAv>WgRi>J{~j@Qdv)reb(t*o!=Op&QPYr>k zBwrVch+sy?9Gqv^ac^4ddGb;0EZ#!g(6{SzB2A3tBe#iO2Fyb^J;@>qoZz z<@)H0+stMsy&Ok{{Xg-TOv%W0x$K%u{Qm$VE8p|--DHHNrp1Fz8-Q-3t*vdL%14i< z+xFVC>-1i|G3)(8@fy0dPEya4m#@)%4moG~diQS@)3LC4%r_@&3mKT)talXgNzeo= zr%xWWTeF)_Sb0-}X^$ywi>WO-(w2fp zmG4(vHFMv1t2teJ3T$$W$!~R~l*$Pbz<7h}f)+Je_mw zTyPNu0S^?kfY1s65J^4k5<%=^q9oM+0551vm2`x#lXWd> z&;ej|y-@NnafVrAMEy^un5Z4g;2oen*vjyZD;8I8AQeH)M{$qmEquaCP~+)XDN}$b zY1EGj^G7>2EyPzuS6R8P?)GKm+`T&E`_Gw*o6bAc+&p|EHQeVX=2O~>4W?R2ThnH^ z5C+tMES=ZspcU6`FAC#C7v`5j;_b@x^`(5eMkxI|_kw;4=nv>e8^HL>sAreVfN@YV z9||3nn3~r4QqIsWMbxjb3iMx|?!$uknaPzl3InYC3?WCt>o*?i zKN-f$dz+?+^^9MW{?AYL`W|Eaz0ZA*4?nE>BlZ6P$=?ZSh-fxit@!C*NdEvJ@@l%{ zL|;?kz7D43=FT+w`0l9eztVqlGO^@*u3s|%;qU}2YldD={US+J+PAFX;qYq0!cm|>f(6nl~qP7(I0o+ zCZa&{ufH4AQ$DCs*J@O*qup23&IMfal9%M!-eBq$++2*vsW-4~=TqUQqb?nMovntA5rVHVVCZ2NxX{x}%Hr~l ze~n)vPjCBj$eqpkugt#ICmzT*N*#ISgclcYg|T&cfGd24&lzmh5%Kb$OnUzSQmbc@ zzLlRYs876qULR9E#N(6uXPD;fWB3Gy+l;8Vm?+1;A{#adZP%JRh#(Etwe<$Q1LB{= zygsGNVMjB6Q_j9e^X>W8>TjpuaNL=N(rigW1)CS>E88lqV+is6i)AO41d${Z*c*Fl z!Vzn@jk4*`8v#x^$+&}P7cw(gW>Sv@7xJ;$+xG9ZX47%jn_c{0_#sLYmdqE{Oa157 z-t*!y7~bo1cq+s(7DhWGCACINNkS5&s>k62$E{WTS7vr-LQUo1zWH9fVYwuh3SU1D z5ODb|u-ikWmr7nrKqAeOI#*9b-0#LBHgS=z@HDZ~ zwm}5={z=SdBjecw9fkzxk6d{*Aqzi%s?KJmJy|cnxZtk#w&5?t`Wy9f+H)PP#pYU0 zdKXc;^MOqkah|H{m?P56L@{6OG6}i{g1zwVj$iVnlf6O8rJu z^8O@^;bO{HQ&fCj^*CK}%f3fe^xT^0R>kvUr^GtfQnn6tp(3@CRnQv7@?oy^dDe^+ zO1EE2TCl;p(wmOb-gxY7y;iRPJnIcM19vTtQ+h~G$IyZZrr{AuGGW@@{PKRua+diQ z70!rlF5k_uuBfUc#)s*37uVQEqN}-H2CcZ_eaAJqsq0NOmVMOhuk|eYbmd%PE>oED zW;lLx7LQLQ1^DJd67UzhE3qUis3WCtGx&w|JSWiXA@bdOqtyrdz4h7jLEkP5k?q$# z%E`}Sa)W4;jC6qPSqM+?WU{i6XB$_st*^Az(+^j-13FGXmq2;P9n49?=oU(n?uvw4hqu zLA5^}NMoRcu9Ni#&6_pmeq$q*LkcF!Lf-)2{_W|gRTCxTMIFiG8dleGX>bmnnpCZC zeRQek0Q_4RyO@N7bb7C(>(D0m_t1Udk4X$wonbfhpo@QrRj09naMw&>CzQ-UvkW%T z=nc129>{OB^#ni3^GY{NqSuf}0@Rb(lQif^TNTLXDaju^{{X6uw?SHpZm%ku*pnQE zF+wxBBp{2ng(dX=0GO+S4fAk!&40EnU#9Nq;c}clPan^Py_w_cN6S58uU0DhT#ppm-rD&W`Dfx1)!p|if0jqrpT!?D zTUv^{7G)Et@%gx~upNUHX=zv3>YFLOc{|lu>d$Mg^t&#hTr|^@%OW;PGq`BwK`rE* zSt&OoPz_RYcSfc8dXI6=cFz(!m$#jb;`yM&*QTo`<7ouCRNmEJ{5@;Rd2f{L!c8U6 z^sOESi6q_&ZzJ389fn9x^3UdKX(sW0ThuB0K(Ve;e6W1%fvlgq0u@v=18XzB%#R_okfF!OnAh=C+&~it2ol?gw`{S1--T z%Vl{grAv4X?gvOt(V!L{LcW#Ycq(=4NyPcjo9FMk#{7sI9nj=Br&|s@+_?9$n;;TF z_Jdc?fTG3IqbcF?E9rQBQ8vc+Ba3E-%Vd}^dMY7sZ3PRgZUY-;f?n*BbLG0q=V$2y1dynKwoKr3(X=eM7phAcT!mTLescRjX zOLrH1Uq_L|OkA|t#k8YtiW(Z-{w!->T5;|WOIejHdJ-K#Z2m*K!`*(gqm^E$$@|`d zu06__32hP-Ql5$+_>XlJSFT6g*&njrP1b>&ls;|LjYThIbtjZD3EcamM^P(w)CSUp zXpy(i+ADOZvsXe{0?5ZtAcZGYk1X7+;3jR-nT6#yM?0ig{4bs zTy%#tKQKkoN3d0X#~mbD>+*t*@>XAPpcZ2m{{SQQAP{=>rCy6;vE-IIjU>r}?$=cM zogGV!sBQcWWt!59>_)A~HQWm;DUaB+g_jkjkZ-SrO~J0<-pm;gmSnJnKI>^^FCXB5 zMxw1~j(e4NED-rprK{9$;ZuG@o74W%^edf!XIgNOTtL`-HKSI%MU=7tpNVbNl9X)< z>TYV7O4IsmHKt=YGGF&saqAfz>}`HcUh8UTR##kt*Ry1O z1JSvJ#BCBFJe4Q&C?J7h)|QVJrPVRm@_6)N)_I&DPo`82uCS!qvRq}Wpv_#pNWyun zQnG?b{n7=fON(#2H2H}A8I^Nn1gBsnANNJ;f3*?)<{v>sbC{_GFS4f*adU6(pjo9g z*@TB8zJjXdICnQKv~5Y~l?pW!$#*mFV9^uE@>K@{p_NAoH*IZ_J@>1c^kag9XOWVY zQ2G~hr$uQ{)g2N&T{oV#_t4)D4*4eze&19PPcMTtO{Q};Nwv@iPjwAw__43QjQfNX zoDAXo32JH6szEzQtZqxthq+jhkC~2@7}8TEv0#L%#F5ss&+r8(v60BiWh-wdk5YE( zhY*rRr(2pmRHhC$NoOX^$%OXbmwRbHrB)P4YRNu?VYHct5`}Kul(>I-v3Y{%Mym_jv(opm-e-BvauYFHw2@R0BDCuPmesi>8c)=#JsMjdVc z0G6^X(#G^fKDhxp@#;53X^B4riZ=1R5;MrS^gEMf7T6rOkBO=D8vMg|vJ=qW*CHz3 z!CS%s?o4Cz&92^~Wdw^TR1{Jt^2gl23<5?Vn)2i;0v7(U_Yl;T`?K0zWgU`d2u2lK5YT=X3d zhPmCn2S3uw>UmEUhPoD>FFV5|w1)BwrIjm1dac-N*HKj)I$JSWMdrT#qgZH~!r=Y8 zCc<2a`c&esA|E2J{7B$ot;-gbv?>@OQ~w&`-75DZM=pjdX;a5|PB&ni82A*{^}F&s|6t{{X@TYCNBhpxfirmRn+I;7|~N z7bWKsdR@zr_S7f8H`vH=u}E;=0mCHkb>~!(uf*b%KI21s)`nbFYq;qcJOl*;9RW)7 z1FoKSq-A}N)HHRfk7OoBDfL-T6oJ%OsQs1{KXQLjyw<%9{{WX@te})GUCs6ssW-g| zC+fl|C;Cv|`7|XHwj*qfO^FxxQuDEfs-B?yhaSgmQlv&MHbvG+u=3K>tMl|seb)rK zkW7mlBM{P@aWLbAYQI(H?mqKbU0ZYM6wsgC>OtmMWmTHy-K}LWk(x^0RsBRpY^;Jj z0Z6r5QNV9W=PXjskZCU1D85PD?hM;cA}$(UP+09!+e&VHKQ&(K{AX*slSMAa$|zkD zOy^AZEQgrB&M?M0V-FIZ za%^C@a~EKO0UmV4Y(~hKIM??O`@0f#7O8oCNX1Gcwvz>oTq-I3BKpwf0G{aexiHX3 zXoois*Wve3k~HiPamI_E1FGG91A6IO#sK)qiu}K;@mbxcs)|SzuOw%-%|uA+B~l$- zhg$+q+$#sGoPW$PeLQu&!EoeBaW~e>0H4Bw>R=s_8>c0Vh!R=i;lO-WNYaV+&92}j zyBNNRbk`=AAI#RNd3ty69Ty$^G^2QO4 zi{E%68% zAn1iBVtW4o-AJW=@mBj}*V)Q_bAmTNw-X;qes?!H^1VRfedBi?nQ%bnL&;kj!2Ekl>dDPDmi@q1+YvR{b$ zYxR}CA(<*#ZFHd)2kkeiyZ%<+=|#q`#3TM@S@>?l@jPmfr}V= zlIa$D-yEj5*tKABj07;IVX%nkMXXQ=_i6=H*~r^xbD^hFsJ@6Y-Ucd&amB2sz$B1B z`)PNvG4Hp8M4iXtqbFiUqrj8n?W|sAm5>I$j9to7{kHiiQ%lR#1g90)5~*<78xeT} z^_?{F@#|4h2P;>ggU=TU5u+40;zD;5?zKcby+Jr;}irhHa`iFeHKgxO+Kg2wCtx^(C>nRr>eGcv-UWQf6uc0o-a*Qmu zN?n&ByAHZrUuEjL&1uJ?EUDS8^<=G?*u@kEBq`+D$#F^hYOVW=xXN&r2?I{!)`XHS zpfoAcmhxkc5%f20{{WR!YG}MkAd(bpJ=)QwL?(#B%Oc4u9GD@mP2(XcH~5mPk@gy| zpBr0!OM3UG6a>g}7t+7<(Q)SVldNFS(c1f(I*_F4`WFQ-6!&RljW! zQw(-M-ID(RsfnvOnSFPIka-FN@~uh)5#>;>HO2KtOWc=f3&qTHbR?Ja4|-`I3Q4{1 zu9W=Nvt5ul*RZ`!Mt^_)-^f74OZ z+iF(bjd;JwB#SEb_9VYD0_I@hAT}MyEw;yBPg1LWl+`;jm+4?FroZzn5p;9z+Ts)m zh^a21n~zTSx4x%7D{4=p?l1eUl}oB5aDJitWeqEB>_mvqtc10)I3W9aQZo47#J^Kv z;qskmc8MJ2-Sc3?b^brbrAb@KC5Osvk)?sFO+SvtFEV9*_sSgO`66;&QeQOX*+=J3 zhKQ}Zdnwfvr9OR4cY1be(HuqPoCe)nTT^ZHM0`u_w-VhqD$BX->Or)2teELoHU{@M zwW*~Yt%uX3L?7DftoM-+&v#Fia#Z}D4WWs}o29tXo7k&#H&7jYwa{qr)j@ert2zDt zcH5pHu!w|iE1!@9k{Kc5GK8igHIw_rh<}EHO%kmshvdkV=k@Z`-Z4sY&q zv#kFBEScARruxqBrv%JEZvmFdXQOTjdmk|hn$wo(3YT&^`PZ0ykNDq00i9l{{YINM;T|(a^0w7B_%(aamfpjgXMXsYQSZiah$Xn z@>(pr5#*FO(}`KtDoG%Uz0%=!>9J0{sV>C^?#oKNqPS zQy9U05=;HZ!Efs$^{3iztxiwc$@~|AW;ov@?J6=j-b2f>NNFu);LR<#(@Z4`Qow1( z!its$km_sbZ{ofZ?lz|zJ4o_Ro=%&g`4?1tPi{UfNhGezAFkk^w!N+^CydS*14%J1 zeK`To-J`-d7Ixqe?55J8wJKUKWjF?j_nMNLUHTFBB`(8&wv@ zGJ`_wsS6jqi0e@Xn?ttze#&E@!09PSHw5k-DL>7V5MDl1v=WmQw*a@qZ+gpmqH%Z~ zIh2k_3WxBXqWvm9(JydB$;3PXc`8EF`9RcGdqkCkCz`^oz_h&L50;~)O(cz2GLw>2 zTztHI2@WNkvYS>x8?F`_JCY6W(A1Y>N?i{^bty_sh*zIlM!>b?Fx<&xko|JQjIXMl9AEBPyFlY=Y(=aa&GwWtmkl3hAltFIJP!aXqf3wp#UU6+rL|s zoPl!A7Rayx9^2QR{NsY=H)(Zh&)bXc{eQFR{Z8BCT)u{^{Owt2=wGal>K}#rrQ_UB zx43@HVtX^dcw53wJs5V{R!T#Gg320kz}rbnD82TJl653iXYp_G6!LOlzaq3YP1;+k-0iPp=W6$8*Aeu+IHOMh&Pw?XH9pjoA(Fl zlPiJkAmx&By~ECqkoT01vvSKjO|WyNRkk zcewf{eu)17qc>|L{a1ZF{dfIGeP|(F%wgfPsz344<0M}tY<%o)9UZ^BJoPV6ro~Lo_g9TO6Bv#{o;=~mFCQVr zHwop&ve1bO7uwpDV58RE1$p1jcj%&wN=Y_a)30TbX*^`U{qAS%YhS3mJ?IJLL!{doI!Pu%O# z;bkwUPUn6907l=Z1I)XFpYOjR#)Wat+Mki)G9GH)wrWE`Z#tdYA3>#UznZJJ_%{@{ zc_)0oMSt)A0B`r6IsEzaH?OH37=Eeu{)9jJv;9LSxgF+rf?#Dh_6N1easquNU6(RX zB@2+gx|9nd_Vcbkv;ZMrN801Yw+$uM&`}kjmbCsKt_2{4U{{YnaFaH1_{zC5Ej-+2m zEWhf{uxoJTIR?w`ZJ&zWZMUUa+iT@{j}J(mI~b}pKNfr}{;eSQn{D0hh z1d}rqll(=Xou^Pix%+7f>yzk@qpg}D^K;yFD^OD#mO34vgKu%8*P(0d#B91Lkj$2M z0_NnS92Bi?umIQGZ%)&%atrUk>Mx?dqCA(@{{V3(V;YQjuWL9%j62+ZV1ymY4xPw@ z5ZHxCbt3*qPsDnQhkKs4TDGG4oCnRmX-6MU63pLZTmJyoi@qJL?XL*n-_z6S*A)+s zaDEhGV18jc4ZdUqL*>`?K}*xZgYIb23btzc>B!xbM|hd*L79v;6v5VZS7hfE zKBpvQ7wcoPfj^~(_TK${GAr3AU!5Lzdc|kWOn_LJ_~~UT-+grv>~U9j81yRl)-pAX z+dr6&snj26te;rm*1fo~A4)rb=lh@O!UXGjzcGl2_+O8x+Edr=uP5PPsN~=EJ>#0O z+r=ij^ge9HLS1!0>W2lc2?t+wdnNQKQuFjYH;)HdDJq=u7qUgp}@pJ?$kR{WPnpHsHnFPLg7Ar2Bg60kZSDz)PDe5s7H-5jSaxhk)eaoLdJ-8{=jd_Fmah@sIb#j-}f z_lc+e&+!tL>uhn5xDquXArEQZuZ>$X_SetCqNR`#cm+C(+>x|jLTfz8jV*l$-*2*&jIuH`TPg7ty-ye+?hW?E18RlO zjc=(xBrtXO$sq}E?e|e77NTwvO$?lpgxrB)`e-p^B^-$Q%s8~6Nl7H!CibZ}DD?=c z^LQk%<4k@Nu;F9UiLwpal(0fmZl7gE?nnNh(&A@~VVAxg@1>X+oHJb4wmk8CorvwH zkv)yeFsyWmNl)<}U@2i~N{*o#TgtrWlm7rv%5T`dr4L0V*B_5V(6u%A1yc7YpO>rZ zXDJ8i7G!5-apyVz01V4AlBg(~9z3}2IctQtLKb$OhSeS)koiRyFLqSU9<4kz{wRkj zq4DdfY7m@f>CurqMZw$yd6bk_NGHA6CMKh+*DZ^d>k zc#I$SA5Z>)I0rDz<~gr0&T)x2t}5h4A>y9MM|Lvg$S)~tQV}63QA$7>+Pp9MYvr|V z;t+jT(D#4h4~gHX-0WQ$^;wPy$Dl$(&OEdLp}mBWu{QCqo-MxSeIFfqS5d+t@s}G) z{dRXSPSiNB3)+)$m#EZituVSdjt`GEschz*hb5g*!W6fDZ@*j0GUi$PZ_V~Q9vbdX|rAb%C;sMsB`;*9;;#f)q zenWFm98 z_UXet8GT0miGE~DSjyrYN><|BX;weOB8KUo)UdB=67GVHO2Rq<(fpHI@%&Idk1H0Bq>tS12`kI`h=!SCLjbf&gyr0Mb z`zVl7T|iDrzK9&-Pr3>OYb{*~@TaS{sHK$410UtGR0>qU-&T)eK7~!1^ZE#XC4JMk z*4UMPVQW$Ax;~=P(VGTJ;%um&Qd@+0?a)&(tX5lZa(lvDXl&COcF zdN?Vp;+te2BgN3rxOr+R@V}KiUfPoASKp!H$kd2=ChzE^>2AMvlvJZ9)LY7HqCod% z=HpnUDl19TON!IggEQ`}U69sZZ;`ypW0Z!_FQ`IpNfj#i`qKqw)$h=RJb6qaw_(e4 zB>Y9aR0r5<+8UHTj|EMa?LqX&*Ztwl;_Xz3XqJ)t$kdN`rFM8NYaUQ)xs9lIU4&-$ z8RtOxtYVqiGzn4C{>l^h)K_Lp{ZTJeArqY9W+6gLuEcbHP^6ttyijf6X1fj3@;ck< z9&0NWN>Zh8h)IV zm!T$MdWsiT)E^pSBC3YFg+59u765kl{KHLjtTV+AQ64t%fTa7u)`#yB`(TnWiE#ly zZu03uR&|ad% zi`2g95c?7F<5;y6m9j#w@h8C6?An-k>Q;x}?5uO8w<54r&Pj}7hy}$VG+Y#E$mFw(i#tM60DKWvwCA1QllsB{xz~htPTyk#qM_a=WNl)t-U2A#6x# z>~o3QG*IdIQugdIs`WYsVPYW3NJ0J>{l(Z2e%e}FvO)g)}QJ_RjsIYWy!|7hs|O% z(>a_oIb@*d`a+voJ$k1~=XUsQX+O&FUFvgNF^}e1E1*+B13+m@c#fUS32}vsZVOe`i@5Gn(RoMgNkt3 zr;%g(Xwy}1Ni1Px+@U25)UZRqzHZ)!>L=S@J#@;rke&83dGL?g6 zb|@~#QT)XY6#mL0*cYCt9e9kSW4<0;PIj3ly4a}m6C zY@MfCH`vV+j@%iQxgJUQRvZV!8{V~v{Q>*YJAj31uT`|Cz-{pR>zYur=yd+%eMT}e z7|4iGlPQ#K>Qtm@_nKlfP4`CL&2b_54&`yk2yn7jImGGdM~=5i_Y|g_ua5Fxm_puL z$Fd`h_jd^+a83g*wJTJC4QRi+bxZb`uTl$_vGzl@`H`Dr7mj7aYtSuCp*H^j%tZov zP>b#!z56BT6Jsirq?d7XTT$vCQ#*eJ(sya5`Z3zCXkjOsW8qyUGm;$|C7A53;t!G9 z0Q_ro@a|rwujS((LI;xWetkFoSHa{f!7^pIi0J#ZH06IDt-sA73qD6~KP*bmFV8!j zEl#CbC|EXs^rJFlJOS{zxcn;GjwePP`jpo4+w=SpUgbt(B;a==pUq}pAb{Ey<4m7; zM$iYonsU{LWQC{qlU-XPtKqpPmcRZ+As06eI^vc{KfQ8m9^G%U0<>&h*%iw1Suu~o z>}FO!J84zO@aa~X`&~1Rf?FDR=-*Nh{uVTK6Oo>!j-e?|&`(=kROD|`0;*B&2yv`; zs7wgE-@jKZyM2PR=Y&biuVJgWmzJWpk?GuOP}xWx?IKTaB()J+M@N;V z{>xKQyk1(*AjIOO5UuvNiiyxsBVuH%qF$pnu{hvT9-ITF`;&Wt_fcbJMP;s#R~f~a zkVs5tpKSDZ(>p%?w4C?UI*n=Z$^QUpW#TwgR>DJVHe6V`Zl8z`wv?AHM_p>Nt9fi&XyMoBLFI{jUh9gA83=N5kPm`>6w3 z5cM1_so1i?~UKb?9X*+Yz1a%wo}8*Vzm2X4*4YhY>?S=X>-S{<$#O`Joe zYA4}AP0!syjPhyJG<`E2QcJ;3dqoVsY{U$pHkAQynO?$ zH^?L{9+YVD8rJ15Slik>>1K!E`i5G?LS-p?BKwZGi=R)_J8nFatqNI_=sR2@gd}rZ z9y8!nbh@Qz2g;CGeKp#v&YL+iv~k&X+wu zwwH72PPj?1>`XC2N|0TUq1BZgs7;OZ29&ne%QaF+AkG$I)3mzOZT&)iGv+#4q3$e_ zjeSSy#ju9Ma+wnV>vY(Y@4X65zN0?c$8+ARae~7bk$(i(0dIXlB0heEe-cNB9E}vsu3Q3{{X4Or|qj1hZ43~Oa%MP#8D;@ONr=!o!7tGD3GDF z2F-?$&CIi>L}3*7KsS6NPaP=BRX*Sz?3ZLFnb`!a79uJ=^#LCj;%jn+Xph`mC)6&| zo|uF4aV0vV`G{B_fTiVfz-U6m$8EOn{R|@3gxhZAl1ILvudnJgn;)S0hQsS7W(Uv% z^0)v|Bzx;_*!@heTF&?m!p=L7SV-e6y2=y%->!nIZsn$#bFIck#g;4n!EWRiP(UC9=0566_n8e# zih>?D!C|1CtszIxrAc$0K*xq{jOFlzveT}rB2?Hv3yD?j_X<1a)9I0g!y+)}8dmUF z-j&}_Z)<(jTKO2R;*;ZS3q<32cP^OC*lDiNuk597X7JI)zS$X=6? z+9%ZB+EzB70dOmmRpUJz&=yLo+@?8xMp8El_^NuuU#vAcQ0mf85-Wv zt^TO=C=9O>;wzLKe-PU;Qmd&6Pr^@-uDe^A+mETuY;f9>>w|ygc$Kh}H-L)MN&SAB zN{H#@RuSZ`ecKRxWj=_mc`Bmg!-{G%ud

    RFtI}eU){tthPyc6}?zUW38Nv%m)3N|L^XM3ehTrCzmcWo=D#39*&S zm8Ew|t@%mTjP>isVTj!?Qe2TN|C_`GllNknFdYYnL5X>mO>3a(h}@(DX-BskgId>TI`o zHcy}NoV;wl7clfW%<6!{#_6&48sj;BKU&z~#OnCHy{hH$9?s8n&On!8!OKsb3 zoQWVM+4q09*_SRd;+Mh*&5aR27azME!(@!1b z1j);G=`z;8^PCC_N{85VG)lfjzh7zK-TVfftMf-CAILwaUuZd6q&%Ed-Z3Tb(;4D~ zh56d#RmES(`}8id&-NVk-^bi_y`zRx89(Vehc6d#JYng}0x`(7bA|N;xi|rv_Zna&z{LO5o z=8x&o3FI-)JEiLT>Y z`A3Jff9}OM>DNE*$M~GC*YTGjR&cU%mm%VuqlZg!2QA}sxXwY6667qDIS?G&d@U^v z-=~Fi+MgiQy?@)9m-J}mwtg|WQ~c3jAn>)z1Sd-_-wC(6tfR9Tb}6T=IQK$ETPnH$ z1ywa+JPg+RZW014O%zyfpwy<-h6|6YkFe3Y@CGd*@8NM=s&i&K26>z9nkKd9Lw?#r{2Rwlg&HRGY*sU zkn#BDmbh!UQbxv515rSEqZ$0Yf*~+z5Dd-)BAFIFY&h>3oLg} zq+*FTb$CB!xnDESavVo~-{+^1y#7t>oSZbAYp8Zx%XBcLNLeZ^f)l#>fn1jz%I<5y zN883abpHS!?>Zh!iq*3A+=w|?n&e0TKw{*wOyPG3fl;WpQCJE09Z&hL~dLGPN#yZqK; zic6mroqcI+C-kIPXe;AxQ{{SZZZq1E|+3q++eGa#aoMe({?hb9;KI#24_OA!r-X|A<;+)7h zkN3Z5@=&9kyV7MiP;BgXcMyy2DYk=3<32sW&yk#|zsgE4q`UU|r}h4*Ym@e-@zrDK z+W!D?@?(Z8nD=YBKP4Te@;rx`05|uatMf6DaSEzHf!$Vb7 z4(|&K`@hHi;~INP6w2!!BNX5FAGluAc26(#v-FFK?ZXzE!#-R6Ek}NWZZi+;*_}(949rO{=O~lS{0Z`+sRu{{YP#YySWw zzq$Va$e3ckqQ84&p|9qB;Ns9-TEJUuw>{Iaoor6eir&g4ZdUY-iD##8{YuOK0Gu=; zx>YuQlDlid@I9FA9}(^D0kF<5hT`Kuj7EiigFyvn{7N8_Yv|hdsp5ECN1{>x0D7-d&1LZ)%I=Ay^?tTo`>Au z3F6ls#MMV>DE0oYt2m+Q33};p4SH*>b+u{WyR!KL-H#P}wD{9cQ4A53vR_|I{{Tw- z&+50jIL_N}sLn@+qx{d86mE}($6w-k!gMQDwziI{SFjUY?>EiHma2#9Whcr0M^_oB z?XmUrUsiq8`m_3;?AP?G`gXy6K6W_~=67;@B@Qo*bJBK0Z#p(Tn#z<~_oqOyu^G zOcVa$l@1118yWHv!>6hodR1o;|mU%2Ic3CcCXY zrx>i99@e&GcF**+;scU@QQxFodfWM!@Hs5ZjK;0H&A{WCH1e=$}w2DK{Q= z72*E?_|x*(t|?b1j~35R{z~ATv4yE;$|(7g>{Xyk$0-5VA8E$Wj~&ZN0Bc`kXmZe$ zRI+^i$#HUv@i^{x(|2`VlHg~Lm2Ogs79K^2C);Yahdoq#v!Ao#_etdp{{Z?KJG^hZ zjjz}}L{$F()HDPC08Q+D1*3g3cVV&qr;Vw#yQ4?cMXi4t+xbSYSbRqFv7m6w_7kv? z_^&65Bjw>^r846{g@A;CKpnn>t&Kch7_H{4Z*y=RTV79oXjGt)C`NCzfTh)1amJPpqvls;d-oD}<|*YWzJz18`xbjB^xq}NQckmg*29m_d)B;^=>I>!*k@(FIt zkHuoCON#)=SE!~LD*)_DiQ9h)=(w#((L%0IEqa{SBet%!&MOI#^{?R?eqZgUoN_GW zHQwi!?5%rba$|a}Yy4hSo(73W2AAmb4l?@9gn98+Pq8m0U)fds?RnRi^elL5v9O3g z)b<&8-u!nJK^q##&-teOw#bg!^!sYh;uotlVIqs>dj1>IUtXmj>6Ct**Q@wrCpD6H z#@kD{Ec-v6t-AF({1(F z@U0j*+Mg<0EM)f7`Bt)EnNN}XD|ul3!*ZQE{uL)MhQ6A5TUyY#(ZC1h)PBm2LVIFu zLXk1lMux)W$aL8e+CrULkFh8O{uLW`tF2~l2qhYJ+blsg{{RBw;(tn?QGD$zKgWAf zlU6!U`B|ArL^z;%sZtb`0sjDoxqd_Dyk5He)^+?2r;Ye0D|6`%pNV+E{+oUDb+)AP zE=9-q<4gYlF{I3LW61UtB@K)FjdMF6%G#3u0NRYDgSFQFBiL^?$s|kTe@-YYq<=r| z`isKb+JgbG3Go2FrBZSJN!pwIwWPlh%Rh^?YfCmi@DD)x3++4`s(ybdo&)#YZI8S% zky^9nyo}#WA<8}t$3=Rwaf`pDTlf}kO!EcIF?l?}i)Lzt$-zMawy2}G~d}ED^ z{RN?f>{n?pYD(aC`xcFs2vLlMsE;eC9V(jsS9V<--}jvjJZ_8+=2^|3kb=%=3r{lk{~mw|z`SZ*~EB7wP5BeE<& zup+}so8vZDc`!?s)Q?!j2+QwG*8mO`jVy$xZqR(}n#b|{^=~YcdF76aAJ|6b_iHMp zC0{h-t*8Ep=&v6GMX%$L^%uM367)kSJF@)L6v>9uPP7SZgj;pT_)R_joUIB&o3M_} z?p)@Y1jG;A8w(IY{Avq8Uw%q$#;8*ncWgZEw1#Eo$Z5-@9jY5RW@L9Y*WA zk>FE%=}P^jXq;}fvPBWe&$^XA2(}fXNKcoYS6|Wh7_Tk%ENpf$l{A$t8%nwYI)hcW zT|D$U8a3nN)Q~gBp)DYUB`O^?(EasQ_cMso(XILq+vAWEWi6C@g|)3$w>*cAm1zsH zgT#f4Mo1&CN-No!Keeno7mPu3;zKAqjr`ByRny$V9EPC%TwMsU3<3vTC_lI{-?2gq z;*BoO7LtN`0ZhkvivH4u5Apcz@A$5?^w#v`H)PnHv?Gv1nC1GGOFDVlpN7;|b}M$T z(G69GlB*V&)c$P^H}^~GB=u$JfUn}E6%e+6?$+VA7FJ!WFgw`CZDN2g<7%>w!(z8< zk-1nI4lJmFx5A!|)-YDjNw5@-Iy_s~!mJkPjPwESar7k$3uFuU)V}roLHi$pA9DcC z<@^>Gse7~fi~X#AfT>t!&~54IYk6r$`-wN)jI|=Y2+Yhg3f*D=1EC%@VP4##g>BV~ z?uZ=DKG`cSGTQB;M%`)X?oIY7Zqcp!5ec{$;bf*VQ{`X9QBSZ9%qV1) zpzR~3qKgZ%3flZuLCQ1YkfvlPs0}qV#A=VwOYSF*jB1>&Riz8QrC91InCr&^^W3*1 z;mYzXq$NlMi)(9MqaHRSr^`zNhPj-r-yBd#)b%tu@$icYUo$B#p;o!6J4=vIn|T`= zlH(xz1^v!JxZJcmV&Cxp0K<9=_=t={lfX?ppLsiqj_$VCr4Q^7Pu&I*^W=cI@>Z1| zslUVbR4LLEw{tI`Jm)8t5E1dn<&y3?IXxHYl+kCZ1O;30bVcDucB3 z+kbr%%`rt(vS?aZxb+k@_SQyZ{kAlGy31-PL2*N+PCZ5JgUB2Cg!CIOA-U9s7%^EP_!>^y;QI{2X2s}F@cf%3mco?7G7+W1l2dCR zD~hkbMltNm8n>U&oMU;0G^{1)t7X)6_%|nCzLB$5Us39|p!#5)L!E?{me+QcuAmfv zs;s+SLzUSal7=Sw2uh=rl$%%!9+bDX##PZDCziI#$IO*94S>**aqg+hS>V)mkY*<( zg(qlj86wA+t19A z_ol5Rg(Nyh*g`EzDL zpij#rlw$t?0}r!e3#X-_Fd^=zCfuh9(O#$+8TD~!|>DBOgApa#&gVjiTq#LJgEU^(L6BOYs!Hkk9!@ySXe4byT zw4LPN+G@JXHRSsi73%CSP{MGTh%SE*CY-wp1i9*q5q~0@m;JZsN>y1G)r_S|Ol1VT zf>uZ6O~}*6i3r02^PQOzMossQ*!a~f57*`Q(W|Dm`-`hB$-2&Ydsf2%C#eUms&e+= z6)Tv#QuFt-vC;;bdHvO|3YzY2F6@V!moXbu;~<030IOA1wW7ijvG+uZ9J%i7{{Tqh z6Qz}H4gK^P$HZziwo4;3DPRS>ZhQ@`T9my8Ar!iWT8Ib%*OW>9Jqi0oB{kG6WUxJB z69q!y)ufNmP5%IGE#%3n6C_MwpvO!GbZiyx*!t$Ney$4QfgkJd_@ zKf0Tb+64>Fe?g@R>|yJ?nm>82Tt#@s+{ zZp;=s`bloYeWaQPzc=+Py^7H3>`pyaTZl3}KhULV_X|{`Dm$3;GrXv=xz zYJ@b9u!DaQYB!r=&Ru#8oqAN1{{R{?A)sof7x}I3MQ$?5FFc5^B*|>WYG0+{>mh@1 zfAI>MM0{}jf?Zo}+%GWfo#(+j2)C7mO;;uZ-26noXCL`hHRUVFteQ93%h2Dw4EHqk zG40)D)1c^0#b<*J_MT0V+~|wt5o zQjou$dFn80`Fnv$f=r1A@^1W*)!^2IW_47B-RdlzkPoo{HUOk!#Il-F^dhE_kjjoL} z`|2OONj{^NaPgApCm)14A;hJXg{<`g)X(ptpRqrvVeZ}L*xZyd;(_XqipTJ!Uq4fQ zM(W|L@nE+&Zf&FdM%2Iu@|A0P6XdFpqLF};fRD>-(z4it^&hWRXb}GZ9&P7$2;P|<0{v)WwZ||vlG}>?YTI$Iv<)i8 zkCE8DN=>hg(~RJ)GaM!q5~9jNl)v0gpR%s>xtYoJIlay%3G_vMF9GSeZqd`BwRE*| zFp))n9@n59>^#vQO@_Z~!W7~=~S07_#p~iT2 zMQRc~TPc61rM32o<+(0dHPGz%t{zb?40a{x+frP+#B{j_r?R;FZjN$vwd=_)gwGuZ zC2-V4sV)*fijsF5`>73?!54aTPX(vPxKB5j@)s4EbCxVr6>xxW;pL@ryoUV|M@6N^ z-<+LS!uUpcK9aMJM8}~>N<>%Hy#8%;Ng!7f$Z~Y9$62MNZ%mh(J2#Sjgh!b!WWOlZu7s27$@d!L z(XlTjb6R!eBHpK}6LWt%ilo@x@-3>=Nfv}$7Y*cyakx*l0%%o(Pc~aly$0_ZLd)9j zgTf#QeY)k?M-;X=>M~Ima@44+4pfyOuykolban#$! z{{RUBjVkDp>l0pb`jgyL?(gx%B;mV}k|=Bgu*p-2UA55;?i91pre1>cX}+YZBivp? zoP;qw!{A(Zxbd9;JkDbU3R0a<_?55|SqkS@TBO(Zi#-ab`jTfW^3S3F0HQAPKb4ir z{*Cb5K77)rB=SBVk&}U!xSqQhDNXA0L+2dUqDnV681_%9{^!t2{{V+^_kOr0*~J~k z{)wMWe3=h9+Rp5AUe?do)5qoF#pJNktLeE(+Wy~i>$2nj0G;^?fA`@Te~0&D3;WJ< zd-#ivxAZ3#aUVMVhd)ucBuJCnuIuqW=Y)Tk3Djgb?1c`xiDpVcw}tDzF3ln3$;@fTJ+;y}3W)igTLGn_1 zgYOm5D--MWIeU29s%=Uyph+?ja4ljhCoHsU#ii6yxAz+YN0l&|eSyLc)SU4C1()V> zki$y(7yyr7CbXTX9ZkH=soh>aQ)Pb6^kQ|7812sh;(>)H4&GL4J9p&eq&nk;Br5w4 z!kv5tbTsiXMWk3Z^uui@b;=WC;nMZqRlTcRaGp`g*`u>LWcp$KB>szef7G|vhhaF6D&xynZ+ON_ z{oUMTzDimCPsU0nwvwT6cgrj%ZNMqNhKGXwMEPdErx{|4#gdDP{@#uM08bpR>d|}0 zQ9WomwVd={)t_ho0I5%SA@|Gbf12duN)tHu8(J{jaJHaZ5b@V+sXCB%xKwZDYWYv% z`qPb0Z>N#;2gdXxtD^`${onOx&(D;M&WUd6SC9Owq?XL`UDsSsA+}UYdjQ+xdr+0L zOeA9r4p?n~Zc2ULRi0C1RdHUT&7660D0x;2HYpZ8ef1mmykn5it1Q2`7a+oR^BIwo zmCNSvisVc#JV)?KWx3vhhSv*j$WG8mHUi?j+m>+K*iXrRUre1wj1_gismcAsdxoA& z>m9w#@+{XJP;918CR;A$esyH}sZ!V~1Ow3C=f=B@C@5F%$_g_478m#xF><=k74<(s z-_W<|5nP{_=Dwx5o*bSz+qYK>g04z#+?>;`T1&_a79u-p2@3F1kBF{gUbVTu?@9jv z+pnwt0DL2}JIqw4Re<1C*VHb~)$9Ni!KPSjvL6Dg_ zL?7dZ!c>v9qBeU~e=4Ws_^bXwwC$AKYL7&|zla~>-{VaUPC7Pi$I9C}we((#{%6cQ zul0ud$(;Ju$1a5kD(7k-Ac5}OqgFJ37VAE;l$-vie-0altzlIk?jVyts$bSdLD1s+ zwR0Ns>QLqTncUp3Bkjj) zaqG$C@k&^3ER2km{Jcau8VKA6#;^~HYm=YGbz#U?3e(D#d-^Y`{B!;X1N^VItEa^{ z)dPGr;`aSNaJ9YBbM-hX5J{1?Tmyw*Glwc1V`i=_hEQ8~II}KI(?7|)R zZint)BZvJFmy`aH{{Ytq;bnQ~&Mn2d;dVvTl{>iIOC+ge?(-zo=MTbrR7M_=SU$$t z~l zhkGO4OqXos@-#NjAd`(m6{=HA-(XkHu=oW#jfen?)YrkaZn;}H=4Ug?>{GQAu8NXf zQfDc52b*$EdC0RItD17D{JWE9T`y)i4o*ttOpvm4D{<0Ih3pb60MGy{(m0+j8uj?a zVHFnatYLITK{e>MQoUjc>N?u3>Z>mWCAZ}=0h5;ytz*?HDXx=BoE*mGq+V2r=W0Uk zZSAgxs=Aby+kje}WLyz@{!?mpF!~kV=TqsI^h(1cEB#A&6t%WokHt8DFv?jX=|iCq zp&>;0f9_Rk@X!37+*JHZUs+id{t|Ef$7}E^e6}ak(J$}G_M7^YcK#P9<~&vB91gmq zI#fUf#03yGl62Zg(!L=6Bl#t|9$r0zi=p}b`p)g&BkZOICnUg2Q!0>~ zde=*X)g&klE)9pazd(P4esKWQMixf|+Z~zi4=?V|ZywBZNes=&sdD2u>rDnxWTcdQ2{!3o zbINH$UwaF(XRB}u- z%O%*E*cbR_Ry_U!yxvv}mfmZbw=wpN794D}8CwYW6cn*=PPfvjIek0*?>I{Zml?>_ z?f6bP2erML ztzfSRPTP5SRkWd!qt6C~DexAlkd(4BTY)y#-gSgU@REHD<}Ln(x7$^ZO%y3kPNwK+ zZ;iDp(PDjG#A^d#b**L#OYxx4Sw-a8d=Aj!7 zunLgtXTwyQ?+z0IemxPDe>RB`#jDqhQzNdUOmT6 z{&f3IZIcOXSCuL^b_7ptFhhXYk|RWibzwE9(U%S0Qh<=+h2OB zi)Z;3h&)#o*?p`0Uv!picshrO{SkIwiZdE#CY>vsio>QFnInG!x?fX6ydnuGSYq&y%r-OT6peICjQ#1maW%3+a~`2 zw7josiXZ*BBsh1HWe~Nktke4mB(fHyquHR+fVgTi);SAjIe()DFEY6$OOujNQN6rL z`{~N`DvxN2&wg@USS{?7azGBa=<9!NHS8tX3w_HQ49rHzK?Es8d@48VLY}D@Z$&V- z^K4T|^%ESrvO4Ilo>!&q(#Z68lU;ywxrE5bKPio|QT`qEBABmI80rSy+ZSX-ay*Nb zK95ksT3c7FRNGP7T0zz0xY+ptLh!y_ zkmNQfiot|T;WCtl+s{W!%N8vZkP@zzH>{nvFGfjMuKiHE*#0<%cKXS2+JX^trnaiP zHsn~xNfWGY0n!qp)}zjlvuw16kt>UivOrrS{5BM)+zu!|psTSRN92#8@z+Y6wxZ4b zvoOvP_rM;hMZ5)SH^BpRNrWPCg`s~6)JI#D5^q|2FfuyNBmmn$B)|6+iV;Fmq$$E| zNgmqgcc&jCq}7AKaafrW3PUU|$H*F~dzG(J(N`~^<&F_GZ+vJ`S5S4nz7+`av0kP= zI&$<3H`^&r5=#WO1@CK$(&zsG5Wlj&p!KfUwuOSWf&Tyw-?p{;?3ty*p|su-#AKBg zwqEu&r|IQoBq?@}hhn_UVB@rsOIu2Guo_j)rsSkiU9A=9Ryhn~dz28Jx9TgU*Sbj@ z_SG>&b?>egBVqSbHO=I%zPzF*D-sUjw&Ja5w6FFu zMkV5zM&P!0U#6WZ#uj}9FV`ZIa43C6TLKe(1@)&PZ$hr!WC(i(+7hJ}_id)@*vb)} zsz4bS<^dnTAx4_6>3Rs+Oabz6N=5dK;Ol!+(Aw$L6!`SJbp=Sot*~y52VE*FU|w37 z1HFzXRIJ@T1lpgb;Y;c1&=&kjO#c88DpGyK{{Uq(=N5|Ts}O=DWFx2mD?rr06uMLo zMBby|nF!xB$B5(;SyEK%DRSCvBI2R;srDF_?htN1e-`zOxN3~DHCh#+_S%6xd9P5_ z?pD`S5n;?EwFI}AF^TAx0inswE}wKEeZ;~o zcoD3RACBJlqoUXS6iRM+>I-XA^5qewW2Yfy$jVbH9W77FN<{*U(+on(=ONiLSrXew z>P5|{4hga9+_Th`!j{zJZ&L;`{1Zz`7-R8W2{cwl;5|~XZKAD9%6VfI){uF)t}Axd zLWY(g(zcAkIb+sgO1LeuP*2^b*+HG8p{(*sv6FK+AQVOkffj4L5|(2^%+`J zNo5Dq$8aG#JrsoBt4P7gawI8;d=EWF!GDbs9r_gwh^k*TW+_$!*~Rm-2LJO{B{`K+~{#>A4gR6ophuT3Y~ zH&V87?jE@t*WdE&i9C)|F##xJ8uX=}geeG5-lITnMK8lr-TMFFUN4t^t21nv$LK^3$?uQMyvz*3vcUpcLnoNp=^_&5J~awk`5}X)1g5 z$&%?>f$M#}D!zW(eajkExa>%o{{V8jl(wv_Y(!+-0Udx;eWb6%P$g|q{{SvVJS=Oz z1Y_~`Be?vGuQ$kM_XBiFklSnl)PR;!fl->BonssmrA8mBSsF>^eX-+rUP~zrhta2^ zkM$0I7d0xli|v0vlH_&kMJdVts^dU?$h<3xq2wds{%J8mw4Hx4T0prT@++dz_(E?$UyXEC@H+MOSvmXuL9=t}@I%_l zF(`lXQ#B$>y?tdkw+_8}lUa6~{{Sj7oyFR1hOzmK6n8^zDe0{E0su(+FGww0{Rd&Y zPI}~(H#-3);shd7`qg1%pD%4GHDn*=ealErt0VbVMgg<)7A`glQTO^&ob!Xa2 zU*=&yaPj{DDI~)3tYqe!OJx}83+oYOE!bY#SlHEl+;yweu-4>ZB3rsC_|&pV@ahdK z7|JiG{{WnQ1=J%tg%;Y>eoLmn)6ut}zQtVtlH7{JiD~8laetI;xHQE{$(44c5sQb^ zu@=**=}fqadxoJ-DD6rI!afyVqU-!kd#}(T8RUf|IKWXI4&nXOCr9EM7lwbDWRdVS zi)^E!9wZ$;>W8wuSns&~4Md(?`pTJYhMVwF1fSbP@v0ybx@H^V`Rsy&vN;R!>`77^ zJ5%-zC{}Gk3!((Ht&{4>b|a5u;4BC8&&+3_3)yj}ovOBb$-c#fe0-OwJ&A=cpn6U? zvMdU%fxvT4DN5c{4@50K2j5FenoF@|9wt(KNpwEkx>A+MDGO1x zi2Qn0Ou3hm+P(`BBn|fEv0#a|r+s?Bg!emv>**Kt!WmeKJeeAZ_KZvb9 zd&v`z$MNbAi^5X`g)+y^zMii>MTtH(K%hQGGI$@aig7|yF}dUwraYz~1E<2lQ6B5s zh^~RFXt6GYVcs{$WIkShkjr_L{{YZwrrS!d=4?$L?;(zeQW|!*)tmnSvvI3+xeJXc z*(0N_$XH1K0Ky2p3eDdxgAmg3467RCW`ZIx(`0l)y#1tWQH8xt%7Q%pJ2>X>{z^M3 z{S7pYGykf67-ZU)oUu#_k5xT4i+ip~YyGJc~q@boL=Mp5Of zTO^P={gi*+Kl>zK9R8v|?2KwsqbgIcE2?hhx_f}V74mP<8jl*6b|j8Dk6>Mtt54LW z6Mw#gExEtyPwk+P)?`>n1x_@T0s2X(saVbU#d?vsnG6;`bcfqP+xcVMIHT@Jxu&bs zvRy^kTZnkp02Wq+@`SI2qH`9oOco8l?P6 zVUy+HPJ*>;t4pE}UcIlbNnCtetRXf(GF8>myDRXj_hbtw(?6(Y6OBd#)M<}`{{R^z z()$#2=pLKYdy-f@$`gBIy(PHrbh=ZehfjS~Y;p5Tp<}7bLA*9uIBr58Wh#!QChQ=c zEU-S`3ZlDCX&1Ee6~4@zLFAB>>t*mFJd^o|N-S^TReL%TlEYap^>^z{9WBQDp9WSG zV)7|gQbQ^K07)z*wGDu<(!V2&CSp_+BW$M)_tT2d$xT;_{@viF2rm3fwd#ELM-(ylJC^CF}$te97eK~ zn+vFueLRhGn|dp`c}~`P2Ajx1l0lIXOH%r*kGICEr3W8T*vc~QhwMhiJ+yi!Q($^^ zrDb%!qg7D{*8x_z{3R(WOC*E(wIY{$t$L3wSmUuFYlq{Xveg+XC-rTyq%7td?8w7F zCmVs|&@ReK3G(nJgFbCO;bWnXK!SLBnYcrW+MfcEYOj|k78PnZnyZ8}EoG@K??U{* z9cxkF@$AfW=g?szvYci0xL?}Z*QiB>f%ns~=cJek=|z?IA^LcR;vH5iCK_I+VIX^S zHF2Zzob)Sg;?_z_JK4!t&FsjS^pXMFwMV7A#dO-7%Wo^8O}WNpV52krhsR{xCQC9J zNB*EH1o&H8>-4@)IIgEHf5)RP{RRl^HWmzYw1g-k-xyIUzusuk`83-E{yAFcS6TkY z?;6_@3^vDNAZLs~7)@l5Wzf+dl`0N*< zSmbzy9y5wg)HsysylLa)YWlunSUIb>XeMb3jp9=!pp=J6B!CDf-KA>YR&8SetK(}} z>{=}G=9r2ejKUf=x|E_cznC>qX3X0!XxD^M9g*@;9ZKZo8A^(-Q(#E&)2$8rl2}HR z>We14f4E$02!gj(tf5VU3L61Q`)yUboKD2oa>HMkrXGaWKhzw^QJiB9h$${9J0FnY zB_JMvC;RH6Ulda7CRs0(w6PRBh4n4UU@U*~3MB34;!}*NWP*I}Q@7!CGIe~i*p_xH z>aRY9Hhx4#N=Wz`8*Do04N`3UDxX5qpO#Kt(a>B&>b^9V65d_M@22%=MZLGKLy`Dg zm+3mKACT82)acG3-fjuV{UYXBmR3i^+=PL1>^80wdxE~V*y#1Qs+#D|k>cE!w@D~*-wK6$_AgOS4xpJ`p5n5QuR|UgT8S&L@cSvqa+acYQi#01 z#p1v}B$exLfUEoJ!aUTm)EBTsXZBks6s=LCrO=&H2BY`CKwth`Oqn|{&{O5feY$k@{uHG%F9Il%lJ zIhd544&;m5&h!aJ6D z)9N1CS8MAO*nheoP2WVl`Nt`)<#vCHauPz1t0Nf}B$A_Ze^InHpl)7?W_K0 z`3-9lpO^JX{{X!l{h!C2V_hJZ_9KS=vOl8#0MZB3w`IGb-wZ!|dA1ea+TtI}L*f|( z;<7O3I??zehgTRsb=)Swcl&G6KaM|`xgJA)I<{7ull@Dl`DWapJUR ze7i+w{$JSJOZ!ho{yO;2-)<ESV+x=5ibrTuHct37pPEv9( z;^1S5Px_7RN)x(3bdWElwd-Fr{6C)7uAJMgKXz@(Qu^oc$N1;e_wSMM)3VG)*r>&0 z2Myr(FENpka~lhm z0Dt!vYfC%z>ps3eng0O8{Bixq#!S9;Ov&=(Kat+!i#J$R_g|{6e9pDe>G1yDF^|z6 zp?1ph>DXfCRFH)U#YH|8U$p!It&yP2ZBmcSs@J&If%bv^@OkUF*}2cce%VCUvW6wq_nOp5#=!J&$>bzQBpN2Nz{)T^Kay>O^IrM*s)bFscpqs z<8NN4(vPaotWGuBzSQS_m@)mA#bYyhoJea0G5A{~VWP_yZT6qCgU$r>xdbb%>&!ol z{v)w^R@*;uXRFBe@^RZ%sWtd#qyEF?2V*;N%e%qc&LP|WGZXoTBj(u#AYg&i7oT%# zon3*l8%@@ubD^mI{{XpfSN`_TmcA{kXUKW^ZQcI> zw+H0^0N}skel58UaxQZrGb1)rk|j-X*Cal*6|KiyQE_WgQ6#BKB$Hl;#_^7dlq169MbC~ ze$oE`qW=J3KlmT|VU5i4&`fSg-FXKmFL<((39;zJPq?6N{{Y2aUGYaPt-CgV%AObW zU;6bvfBt9X-Meat_J{k~^aC#?T&KG@tZQVh7Aan&gm`=g{{Xht<-2yvnE9k2zxL^Q zEA;4EJOrly094PBAJ@yh@i0BQ?LTDVOGwCayo33i)J^3-=rN6kn%%d}=RpJ6Ui-?! z?|FLaS@5Z6`%9nU9S>@|wj{q-v`g6Pf7kdt^1Bx@BaV>eYX0XWfD8T#&M3lj++lAqz9Eg<%qzm zs?E)XtKlvl+bY*?Ec-)*hn9?9FnqfHu-pd&gX{+z7zVMfGIC1}&Bu_Wi!!T){6^Z> z)&BtahsoV8QrABCK79Uqi?fbdSi$)^lNdDV09*st?XT1QSLFoHf-XukXm9Be`9=Kd zUO<{M5TZtuSj!|_GS8)7M)HIFACtjS?x>ScrqFc&Jk7sqYsvmvv9y`(zYOcD;*L4* zE;IR8ad~WHx3xby>w!1d_=kP}02=iVj?K$nEb^a}SM6;{ek1BYfs+wa z8~$L&%w6(ZNo|eWmNnFYr6C=kD^zjZvz-{Byp=*{Ui$de>|vinnT^H_-QQ=jqwN!o z*%MRX^fsar_eSi#TbC8f+iKEHz4QyK@~M&5x5ngjN7gh%y92XattQBuiZImev@2;T zbOL+ML4sJ4?e~Eqbnp~7D`lF1ayXs$Rx8Lx!dN-lx2{Ki~UQ2wFA00=3&U%d} z{{Y4yiyEcm79*15k8QjGB^5@R^gx5_B}zzc+LDzKU@BEhG03&KjPM`k95WCx8lY8krGTxpxbBC0AqqP2n{+l~7^=O_) zisfDVR{`!P8+iP;Ya58y&t%{Pr2OQ05=lzX6n3d7P11VkMS1VezC+1z*qw0AZ`Aak z!s^w)M@L?!_DRC~Ii&uOHa{LFDiC!S5QM z#vDZ}wpn{|j>Yybfgug?Uf1F=3qb?c`6iukpK6>0o7FwM&h_7_axSX){*51^D4xl~ zt+emPvYb+XN?1Z!Nw+`}fCWmudC6tA&C1H-^h5_Ptg1q{mmE?rcNKn~`nRGUD5Kb3 zZ!57|anMUtzJZAIC9y-g@< zN3t3Gs(nZ-{^8-H zF2Vl*RZ0B_(yjHlJ7wIf*yd@EVkIxRYk7a^;sT5HQ9Ao-(mW?iNlqQyP%s6V`V(0grzPkP2gJejlNMIu1Z1XVzhy|4;3JJsM52J zP~;@j2%*$8oy^O+!PN>MvX#4E3nSFq)Of_Sul&}M@K;M|(v)>E+UA&I92|VYlqm(L z3G%r6=yQ~#)FpcAM86-z5aB|bR_)pZ{gkEq?;S?y;}K%wc!E%bg|&4L6Vj>eMR(wa|+^UR&iQh<=UwgQ+*DPkBeX2|X<12(U1nx0fs>F0?{`9}pdVnpZ!_bC`V@ zW2wZq=yW#*?2B_lDSeMqZHkEkxXw@Hv-+JzhY1vPj6TC)8;^4lHzQw$-;Hth{EBV& zIvO~t{)UcrUmVf87#b@>w_AButN8=^j7vrN zh)2gVqNYGlxxd@&tNjhhPF>2ah(;@^&ODulSP%M0us10OUY*~8ryr;k-0;neCg_~$gJu_S@xY;mW6=O7w|P> z7wT497DcAxy->R>l6-0E?pQ1B*Ke^7lZ1Gc1iA43{p?5GP1my&D)nSXrD2>GC1uUJ zb<&%sXC-~wxFgbWE+Iui-s-rf?4qLmXP$r;@#+MdYJ4d1nnIJUk=c0FEy4?3KUal2 z4TM(8rr%+Q$voDjMZwUg%8x0j5Kxp&l^q%f8@RW`E!LLuVbTM?fr{x41dv9lPlXZe zG=N#In4>sY;RP2hIeNFZO zNaB%DwI!(+xY0@)YwfE__v~n>w~VhK$s9fF??RGWJxC*`O3L{d{{Wa$d`t8Ky~ky? zr4=DSBcg!4Hw~)fCp`(pi?W%Jlz`K&Gt=P&>-JP<<0MKEoLM%5o^`^?WxC>2ldFl- z?WAp~dJole^(Bli=C-90)aX{bk6Kqa9#voVD1M4D*ltflC4sps~@=0mhG$1iqWP>i_PMBNNNvaa>;ol=+lIK)a6{BdN`GL z@g5yxEP-Cha!hK-pM@_q;_5XYg+ElGK80N8bUiW)S0{sVU5M;)eN;iU{?SJJHs3}s zJ*;<3%DDt3ShFCHFhzwDI?`Pj8EmW$qGp*=w6gE#sHr+|Zmp#Ig%bQZLv51LBHMFdzhD#|hstrv1r<#Taa9AJZl(S=B3{-Y{D@dQ+5 z3>oge%+)N!c~?!7w$~jmT8x>grIEs7G?ePr`}93+MufT&gffN0ky!vP5k|T=kZ<=; zr5~dqHu2bf7ECS1*FYbQGss1R@C;wrB0 zDpFk=Eo$c=mK;fpE@8)JS+fC5samaD03-0ErK4i?ETg|vmtel`H!y_nBi@~VWiRa5 zOWlC^tZZcMFlDWP4b!n!KZQrmNGkFAhvPezU<9IS)`9tMcN<&VL&9t;UZFl6JMXqR5udhN;y?TUGm)b&+B`O1`-Yrx1e*li<=sHeyvDN0bo~Lq65803(xfeM` zo(S2oJ{!`pSTH<9|WYyPyO(#RAuiz=#_R{BKyEo5g zq+`_OW5FczIb@bWW4ByP02^jhruw=X4!M6l0Tl-;=hrwI@uIdIXS&!%Kx!5mL*Ac9 zo!W$J<$XmB@lQEy#mlbA1mSld`J2gXL~&bC8%h@mJKxbv4&L`J0pLX{TDbiDW-=*rp8ZljwTI`eV$n>;ASLO+B{YtG&s&H?} zj7)`tR`n?eTD_m$Tk^02RglTdM7o4{H%REBM_N2Hj-aR0u^W3DRHeDQ)4BYqI#HoK z0+Yzs`Fx~YqFR9c&jUmEjRoKtu}WP)QV1Y`4Yl*ASqkh}VYRKCdZNI@^0pZ^%foJ6 z#e%kt$vy_D_PB2;W-PkY=9FWJtc8wC9O@MN4RJBd<=7we^|g|(iI3&&M=UWJ2! zGGeyec~3Hin>I$(l_;P5La8?@%(@nOH_3M=%`oeU1*7W#IvoJ}sYND>WhTTWL4HC8 z=GaxbE2@PW77N=>Kn`H!-0a$~-T+gT+$Oa}nZQ0s8!S&t(-EFPJz$%70D_Y9jie{r zODi(N%Uz@vZX9HMH!hz52AX^5Zotv2pzXJwY$Y*qrP8m}a0Z_GjHMUc54U3U3d2ra z(ea`@boiALqCVqGT^J72T;EpkNTQ|a_WnFj1+IdggnMsDPQ1Q}mM-L%bV~8Orer&` z%LtscTWVvpwJ4wbHv*QI9G_Wq8l#YudvzouxO|E$jkY*+*-B9V05P04Y)AN3FZ=djo53E}x?IGp;^H@1?o z$W6UD3X>gMwEfVcX$|cnF?q?6=Uqa_rgP2!bh25u;-O4N4n2kCHo;Fp z#U!ZDks~074?=cy_M}q_Vo_ZX2TGTGk0WDoETJ#OZJ7JNcqME5cTnENj4U;)Ldq%@ zKB02mNC1z*p)7<+L~f_IA5BqItKVwuQUUkYoG@-1J#cQ!7Zg-~nEGlX<7p~Y_4n3K zqU;tc%hW3u5ZcsBt}LW#uZZhQ&c+@zuHC}u$3}HQq&B6M1A8N<+iHWkg@%lML$EP2 z;1c9{DkOLU0+KwczE&pi(9~;7u_K3*d4sY`ez3j6in5h)rApmoHBQC}ZW}I!-}U2V z(~NaC+&5bOJu0JXNm!~lh{`G6)`Oz&T;9Z`g7aQ6Y_ah%u?{H-c?r3(xF2tYb6Z!< zb}cHV47#u!Ukx#|1h>~vuV=csZLUIbeaotK*J66t1c<_SAs~f|-rgO2YNA}Mn;TRk zX1JVotgBdgTUyWD84MnO09r{?ZHEB%UXLBDfwmbJXO9<}Q=cn+mL%I>vZwc8{fvNe zoPIe!azpkA%3toJu1>0(<`T`5+Ngt$9}~<59rdYYuuUzEoiVDjujc?sU|XX z{d+~}>bY4*xSiZ`Wm}%@EKGr0W;#+1yGlX5&yDK$OXQBSlVgQbDGpuq-W@}-A)&Q6 z0aIxJd24Z9ZimeA+~qg^A?4K^*E6zQmJ7}nq52~CE~h|s)7@Q#KPK0*Id7@US@Cy~Lr!ROmwQW|Rwc z6bBrsJz-m1Bd6N&IKyhU% z`vE@6;yDVTEc8cHsbrJxcZX_w$II~1qQ{-f;mQte3W^pEm$1G3Yt4DSJFZkt%a65V z5@%#_-ty#Ru!~HMF89BtK~=QY*Xvwn&Z=IG7j>%I?pTp~nUHWcLJ~I)gbUJD*2q~| zB#qCVaY^;rDOu>D(BIx@lBo-|fVw$=aU)>(J}ZlAe;OByT{0EL4CV4D51_5Yr*Rr+ z2=bDo)U2MKm93Es}*-h8B-*I`=@&=KJ%#4OEHdb7DFy^k)o@HqM8ZCXq+o#)G zYd)c4n8zf^{t?G#EfHOi%!H^Np(#U%AX`#uPhKr{7F+X0KlK~O_!nsVDa`rGu@B_( zxHcPe9OgP(*!PlLc__E2%9C!rX|6kpR^FrN1@h8S%k)1kUtAws&-XvG{G+n7@fo}Z zPF_+Ik(is7UP-rLHu+nLQm(6oY|sOMY# z+EknENLR2|)pR!hl2{tP~l>t`UxQ%Ul)OF;W!8>64pJx96 zq=z5lJaZTIJ@tDs3@$$3)4vp4J_nx2IvK|~%Xvd<$HYRE=mjn^+1+&9*?wZaOa6G| zE!*XFc=$Z4C7CZ@81zm44dG*U$M`rlGKL^Nj5wm^P#N^~R zA=Sv_aeY^qmh~^RI7%P6{xGM6kVjF5ldFzUucj9OXP^oe$ftGutf7 z+-f7e_GvtuBK%m`jw_GHNR=i`*-xQ{8r{0UxV3->ovVcTRBW$O%RQ{(E%y2*-}oI* z$F7o}>U|H_=k+rfpQO*A{{W-^0HaV9#)LTOrFUQI2we>K%_?XO{auLoWCF=2SG zV`BmH84$93LDH+Iry^f)hY~Y*XnQ?0i3@x3tF4HjJ~~We?-4ZNJXeu-KOsLE#Grh{ z+-wOs8Et};2I0d#0=+T%g3bDZZZR)ljuUW2+7%IA};yG7gnW81LCdxiIxkMYd^ z03pP=K2)bM?@nB18bFX(Q-wBd!)}%5e9rd2A&6NQC^zWNqfRcjZ<1M%nWc71X5Jq^rZ?uQ2DgR>s~R=!aMReLutf=a~Nh zHhh&;L^LXYne~_akDvbl)-&q*U(_Ge-badYjL*#W_p^S!8wS%#C70(1tu{wn6TJbj ze3R|2gW)$>;C|!W8{M@$bCvX_KWCE zdTUA}vTevB%o5_#mz@buinO&t+2l1y=&zmBNG&Zxl4RqxJfvUKh=&57 zS+F(%0Gjg;pK}jeS*7}=(SKLbe|LZEE-Gq`dD{N~%9}r+ZW|wp?Wb-a;@}}Marh`r zMuN&f0WGl7T0jG*MAtKQ>xkgopL`!9TI}TcPpgw49g~jsOEI40veH!Ii9;@R8vIB0 z*8%(yp(|~(cHZ*K$=H7-T^gvbUOkcd75=XM-G97aS$)Hc4oYWW`+6hs>>ASUJn|1DJCxGACS>elHwamZDo~pqE{&qmx=?+z&9Z-DXHzJZ&Z~5uUfI!x?e$CV;9+# z{!b421BccNvFHf<>ibT4?o(_>9SD>7$L@VXFX3ZWI`64hbtF?Ojd=+-H|wS9>t1Wi zsZp@7it8rPZA35O+t=S+9;_U?obJT%*%NtMoj))3)x2YZmU7D(zqfDa{Aig5kGY@y zZ)6lX^$+z+lhgel3ghvLx{>o4xvpqOpQrd&$v^ptcaIUTAFSp70P4i{kK@W0w4}S` z{{Z_Z*zWTtCRe@~4mc!4#xUxWi|SoN=kwd|76@<-P?7C7j} zPpe4bZ|fn&u<-tu8_8fy5?aYHiA#XXY_hi0+4#1%r(YWSH~#<-HfLuyB;TRopUpl} z;(WYX{-@)sAjl6W*!4B~(Wc+FkI!w-*2dc|NCbPSoC=vzLTzd#Xk_l6qWpmg?hY|# z_iiKUNa{9~woQ45&LQJC$o1cVS>*>3n);-L{I|LJ2bACNvTQK&PnjcW{gvJPD)%i@ z&%S@J;kxor{8t%~aS%?-=Y~G|lsFMaGwbj5$vkV<2Tha!W0^%HRbT(At-6#B3tNt#Xf_*HMhp zf0*iHz}}mSeUVo%T73c3AA!IWoh{T~#=AK6iOEhlcob79Pmf=1DdNU3HF1Ipjr^-v z7aKZ~lAX%e!&_E9#H;;i?2i7pWPLPt$C9uSB1R%oS##7vTuMXjZ2edAt~28a$9i%4 zov+IzrKJXNJYHRtPr}+*O@@ciY>(MqlY`5nW)=tWd9NU1Qw}uiBhzszQS@8AfgLo` zmfF|M8X42MspJ#g(C#-l>&&RO)!e@rz0 z0Q5Q!g#4rSyPV}8ILo77hCM&G1ztInjvL%rJj=Dy=kYO%$;y?%W~JpOdrvl%LM z6C!(*5 z{6D+7W%7InG2@FaWI5BUw%91Mv6+sQl(ixqmn*_qrE83jTH(2laW_rnu7y_b zHB=T6&%BGcuSqHlx}}TU3hTA_h^5ry_PNrBR7K}s#4#CR%p$qLv>{M~6;-jR{lm~tj9#1xXd`3g$w(A;$& zLSc`DP*b~G9crs(^e=Uy(KulcgGmIRRP`6qt1T~e(EYq(T)70K3Rc;TR|XP~iY_fE zLKZEkQ2zj(2ksIZuUWEhjKHWZ3rJFYR7dvJZpG^bwxtjI3o{K3_R4PABgFlc&TU>? zQLM{b3ms|+1)ae3HmZxRST884qQu6*TFOVDK=IP3XMa(;>UbrLU>tvNN-TwSCYF}s zLlM@FPd)25c`YUg(ElkQl?2^QON!DXa?6l^>yhhed* zWq~L0=?2Ef&q@S*hW*3|nHf6^U;=8_TR9WEsYP>qJCc&=N-h94K(5n^yTzi1az0r& z%a~&viy^}DKZcTP)wr9JfyVAb5-Gg22uniLR0@@H6JcE@t&b%&=`y&P2I@YTHa-<~ z3(5=X$PwHiUh<|@r$eP!QF>x_#gWp%;@K`~LtCdO-CRX;o2OL@bkjfVZQvtAY!J^tJCo_ZcM>j({onnLiTH(QF=< z6xDl}7PC%R=tJaU9!BLj_QHCLRj=JaqPb~L)QrlC<9D)(kUVLqThOewI3m*7RmcOc zjXfJ!h4IJ<_=~?w)6kYgCoBTpWk6e}Uja^)&$t~?m2<6c$k5Hm?xg%}6$8-Pi(3?w z8xu&{ek22W-Iw$y;>S$9%VY%Yx&7hNg(%8hMr)Gw5t$6h4kj*TR6U` z2F+?~qajjaWFi}`8Fg3DX*Wo#nl|Z@`V?2`T)TAzFDMYO+^FgR_4sT>4$VkDxfPDp zk$7l$!f=xvOMf_~9dKXbBmwT~D@v)MrDbwF_6f!}>{rb-{{XHOm4S2q9+kLk`ZIIW z>6AB%gf0@LIFdT2OA%3aFcs>eSBk*o97!=Y<$5|j82Uz=H}aa+hN5E z5H4@_R^Ks1N&d-vkr|cSPDp|i>!kplN=Jv=Ovjz5u9pLLKr?%Lkz{KvX;?oIN)1}m z&Qm_cX2%I^9vBtw7nx~#WJ;2MDy&7X>?G6Cme9gj-~k$kWJ=3Sn5J3+>vMapO^+jkLB(Z{u26k|QN0 z>J&uD42`8>Y6I^y8Al9m7d#F9bjVTl*hTzE7Bw%@#brMylSMNb#UxoNJ_PA+8q=0o zSwkYIhmc^EM|0Fif=;!!)sciImqtEjOQ|-O7WNuhH=)@EyA!I`zfvAI{Gsg>-=?+y z0EGV0OSd#Psir^Zpvb%H8vWCqanh&v(#UUeO z#c3x`-UIER{jGt1=AuZ(QwHi1gDut{)6-D3zSBv|TF*o=4X7qdF`1^>7Wsrs7uB)= zSHDYAwrN=VmKE++*(+m^V;(ycg2IR&5F*C(g!HUWBem^9LzS3rBrUa>9!Mjv-&sN1pwI(qo^paH9`n^SempcbEUIWrT8nuYpvUXFFJ1wh`IrSuZ9C(wc9IL1{f zD#T~afz`6{Ni_9)u}`c@`!!#ri0kb49tl^Yl6l6DP#xcUd+Oicp~o#1R#kLgMi2i0 zar3yhOut=e9xAy9?W22I?PVcbE!bY5=Q)0Kl?;R?WTisKb5>VhF-7}Y{Q!ISzT)un z*cC5y$V-G(tBXR_Wl&{XUV{<0sODPnSq4h5S+^oS28^lIC-$gN)LQg6VvM~@h0Ytw zvrebek0w!30ZVX}kUqm|3*Vu3!1Cg+o9+^8v^loF_jzWqs8{AgY4sQ9*Fjs4Jzvmj zUmmljSY9t|a4^TIk&^yJ!TFY@?Me56P?sk;c0-GVt{vD_ z3sZ-3l(I=D@2eW671IS(wMqJkHeok^S0=Xy?u&u;QCrEnRs~yjB8r0yxZ;)iwLi9k z;POFB4B2obzMq8-ru{{&`zl?6x|EkfA(!KMRVb@_rv5tWKFT#aRmZp+ILlvVLl=g~ zA z;qIg!#Jxx3sRh{tnBy;5KQi-*xcxr@boXmn_W_oh0;XlL@P_F#!1e=s5$>xB=$xNH3wL1>Co`Ym;{)>T zu4=U={{RRF%Id31xOT;F<)TC~9nr`zcS6Uq(`p9FQBu$0PF2Fe^^*{@o0d8tDDEFT znxd$2%WW)KA+||3`)@>+hO9fWXUOc!vL8m1-5k!;i7vuzUzuzEAK)!Qo790S{_bzD zLVJb5auXw*IVzpUEq!{!&MP@9cQ)AjyONm@D{?2S13ReLeyR>FuhnL*Y_OF=fXa^O&}j zk$2;}&Bf-lhanXQR6Zm*u?hy>-nGfT2K4%~uKxg2(KVEbWL|j=QbVFb3yT1T_kyp& zm6shjjaSS?`e0prp}?dmymW~wBj8d8!R{usw4;u_!bf^d^hRFG=bcXGafod<{{X7( zt@bAM1x!An9rIp;HO}Q~+@&fz2?pdKDQ4E_FHQRu{Xl#6uE>VwGCZK&tbRS&4=UkK zENp2f&#matuBF@s)?E7$XYicbT99Y*Ch@58LoMrWw%ko0?qL_v3QJPSex@fD778=+kuq4WBSP4LR?LX4!$H_misv-Huj>|K_@bDW!R;p ztsq}$RgX*Cr&?k(0G0U%oQe=unON6%AIb`nb)&s}16wQTGxf1ICFK}uH~3VdG@&}@ zF2tSD?)nLTG{r|jE#$3j5COgQsmEGmCAVG$Q!+!L20YTBtx~IA-+fKRMAB{c2*-q% z9uG;&dW%1#Cf1&?6|I#j;xXi5!!CiW&{!W zmTht>zDj>U%2E1*Tw)U3Q@=Ljj;mD^EbI4FqZjlcIQ8y2ODj&#O;@M5#wWVQSr5$OSuZO4$^{41#3pbVH96|z2h{LOD1QA)cDx{6+yP+cI#OI~H zgqLG_DoFT(Z*oVzxt+auv#!?MlSQk7%NKnlsYRlC{{XV0{*B`xJON#P~t`=pd5Q_^EHLiNdVdDcamPblicj7@7Jw*k> z1caS7NwK$@e5uO&8XKc1sK-^7!0+8xOYMIU*(5^WpB2|O6#N6i{eJ4Wu9+`EIoW`FnD}!~@Pd=( zkw&t>E*TeR0Kg|jU&@2`0o7=9jy#nh*}k67ZDQUx?1Cm>V zBaE1!qBbP|0Mzi*^a7fXAkzUT<#r$(leNN7aZD^B7SSqC=KN~gj^yN@Qom-U^f~vE z_Qq62IHb6Ol^&r45I)N6H2IQ~V?#YkBars`UiUxMFXH^E0H)&pcC~c+oYHHl%O9e1}dSqImU8)yr-y#l@3(dEmzbcnC) zDlaS$>>eSDN||#Y#g3O#son0P*LCvrBC4Vft2JV835q@$2y=D9_FY7eE|s@l#JeDo zw^luJNnmn5474Q?vCCVHeqZ4b2_D;3ZMt{os~@L(UBAkSBo5?6in4~W4DM>hykFTU}Y){5$F+wp^wIPCGGC zoOK|&!kZ^%o(mP`o4*{{rL~U7RjtXtb0623;X`m<7{k-p(_NL{$$yt|(A31G(^6Khtsj{PAN_D|c(N%*LPuwK zyg08N%ws-kd#i1g>XfZskOYCD_Sf0b{{Y9WEOeGnm+k&=zsu?P6R5xaa6cr35M=gm z428HytYgfQo7i+4(V+KLb^ieP_e=dU!N2}&m#WDb?=SVl$aj;pT<5hop3^nQr*n9w zB)(j{ngHKGAiw4+D{COD&}nHFSCPJy?+g4DUah|5rINpr{$7>P@QUtyMt_p4FOqrb zb6k+?swz_5Q8(Ow#a`3Kaj>E(Bgi>ULU&KfN3$+4!(|Q12vU%CJE7X5G&a7rHDuFb z8Ao=^ulA65wEC`E!wF^8RNpH6m_OW@;NuverU6QyEG!T=amkQf;C^ zHl;36qNgVD_`gs2FQ{z}K6{%Hg6sGlt@Tp_hy5=908ihgZ=)_%e(%)05L$$ zzmfFk%yw57$JeB>mX>~k_jXsr0qYeUDr1i^b5aZKb0Ou z8eu1veNO(1UrYVD`kMZs{fy$w$D5kN_U2l-w=iwg1jlYa99wV13s;imIM&hQwE0(( ze>;4Z?l*yxgT$@;pO5Cwas7)A@Ml@^s_TVzP5LgG`u_m8PwhUTe_DR|ue-lneA69@ z$ceZf)kTTNF^nNwn?vj;=4I*U>N&LgtHythIE7xCwf_K^^&Td_w$Y7mzjl0{VTH&9 zIF;=1__|kfcR;aCtc#}}*(3n14UO+=M`FRcvQ2TAgf|?h$4bB#D^h%FrhCm6)l^+Q zt&80}B+2}%wwO$mr{l(QHcC_oj=mKZO_JkF2tt8M#)Np+mVCpR<8P9x=_N1fcDyec zXOEIPi@Wf9zc1i+=1{(GIk?o`f8%=s57%FI-Z zry-XnenHh8#J@51NOH%oRduhYekJjFQKbkz_{;vUsqGx6As9~!vLP??mQd1^3X09Z zJG_3%?4iMBoc)~937yTJDp2*7pnt|qtD@E5V3$-=?DF$(xtJV*>9U$>N>FV?2N!dD zc$#MZCJ=^F71!jZ=iHS{3?U_KNmPWY`luBC+Mqs*Wxt6C$RcP9^xWE&_}o>E z6g-O0Vf{!BQ|FKs>qs^qlmmO$L9YlbbSmz$ax3}oKV@F(-QsJYhl}h`-6?v2_BwBU zKW!Nm@{2&}M5U!^t&duM5y-NAPrrZtZ)PEX?%%6ER)7+t1B_#o?zRCBO_gsi`j@YT z{{ZtNdQz^6K8{lU{4e+)LjM36)tY+!dVg>K07L5sa0XYB_u?KK2aLw6C{cH2PAl|7 zE!jla17#kaE943#2=VVI`mPl!wIclzIYaubaP0ISqh4{5%Qp!8v+;c|<^HK*8&03* z>P`G>-hc5oZAauvE7cw?{K>ToxM?(>PpSCBl9j1yxdz1PU$2z@lzwqt-Twekd1#KH zrlv^8r|s!RtXn5reHzbO9RC23;>X4O!xG|>JTA+rSL{))7w44uR~ddMsQ&;P*^8vV z0oT?xG>oSg%_J3XY1r<0T?7$N`~vLO(3kRdt5&I;RT6xRWav}jOL7G0F1GwB%&kql z%gn4eg|?)SQlzK?2sZ}3Cu3T>VB;l}&m`B|vZ?t;qYSUOS@D~hmRBf{=4EBaGC4KM zKQX!V5Vbh6Zc4(9%DUG-XB(>(g^VMkr+;e6Wj5Oe!+t!3HuDal`0l3dI@*HJN3f?# zw@qM9#MHWqIOp*}xBOb8U^vSoRX%jx_EN3{{Dv<$vH>F2qF~)`u6~DSauVMo`XkCL zr*qtTOn1D<^xpwpeV48$k6zy{*VOEJHs{A@=zQmnwgZ2b62`#p-$*Q_k zX1WY(yfS0nRz&Ux+m1++f0|{;a?}S$BdF+p%BRs-n?t;d%u_j^;Qs)N)uSp0(cvQ@ z3UX0zS{iqw{6L+4>QZ)i$qqs(#h*z30H&YS&vHJO`-d-uv6@~xwSC%grnw|HQWVoU zR3l_rEd?7PYc_3U4~T8@uRH!r_^o`7sv-*_RKND8-|&4p z{{U+5{fB4&08;+-^1f@q_!{G9pK3NYEa1reyUSqXqD=de<2sMbj+8$r zeMTbeUV%21y(iR4k?lAB+7+F{$FMqU#yKa~+<~e<9VjR^8ucLAtl)_q$30~gCiZAp zdw$A2I(|~@A%5qI?sF$C=eb#mb~JegEPX&oB%idJ>-4zlp%n0P8@#o$>DZ+wm)=^I zx0Iz5a1#hV@Z4cUP1cFt2c-Az~ zNF>9tR>-nS{vkrTpw#R|^d%4$%B)tfLHxdShSU(|vnN>SG2ST&UGLCU9@V}lL$3tM z+zSUTmUMb?m-tdX#EQp5!V>}c5c1132vi19Fip_TxST>pr$dC>62Trt46eQat(p*v0c=*$r z(CP+qU5M1U0$?Fs7Bw|}ttOdWYU)tD_qRyMw&un?MQTz&KaF<0S2OMOWghmDk;_>w z*hy+q?#T#AHUu9U?5O7<moglmQAa1=dca&86#N^-py=i6WEu3&bqCf;_-T9X{%{qo2ry{A&6U8NIz#tV)4BO{x8Ue5JR9c_P!h zaT>+NrEU;4y|nwP&s&(2L?y&UG9M?4Mvh2CeWZBp)}DtTuwNlnjs>Oqzx+G2i09xkAHosbWrXg%t(8enytXz0hz2ryQV^^agAgQ$8-=z4|{p^`4 zFeUQssT)d2QSqZidW%i9(*_1Tp>(TgD(I@yuxTXFE$l#Eh#)BS*4llv%q~I})W$2o zy4t=62{zYSBDh!Wr;vM#nHfq)rdAVu2qYU(98|iPgH z^kNdWKjuK0OXKPa55ig%Z}S@)Y){>KW7@ti>V$2&hv)|`!f87oKI>^-iv>p3sii-_ zSKN=GqY+tkYEyX!BfHfrNx$Dx_h{rW>9Z9LoW2dl&8+*4q$wM3p&lZ+J^uiV!OQY9tI_#okyxBdBN^O2 zAw5k7)TEPpp~jSv=dJ3B-;UoOQ>3fpR8P<|CzkN_5ECeRi|A64Y-nqPySQb~>#m4R z+^#XeR@>>3dlW1Y<6EmzWT&}bbPkmx9{X!-w{Wqmg2h%oRMi#EEJo|0Btfzmd~n?3 zPB^rVsqqG{HTGP3V4~TrNQFtqHlj)!>G??-)!kP-6gKOxLL-ue8Pur`GB>C4yVU!t z$4=Zbmuf2*WF6+09l}G%QU~b1wQ&jgB8K+j&3Crp)q7Tas%j)%sf!_?N9Vt`u*Ebg zT6MmlEe72U&22mc@JEYm8xq&WQBrv^Kpwcr1xiQ7=R}`zMr*OKekQk_H8mJKkd*K^ zjV?rW5|9d0eW0Jhi8(Cf)GlZ~#!@nyCR;afbRcPJUNDz&=-(MGq6?i7Wkn4H4>MCh zESz6qlCub5VJVK4r1|?j)f+2su&gghj+*4S*LI=kOJVMT#G8UG?WE^JL-6FQYpMx1 zJ{dx+lB9SNnuKVQ?iM$xb~X4FV#x?2^wj;ZQj1~^xg$$0UuXQOuWT1li;|}{Y=x}b z-YXR-Rk;YW$nr6kRIsD7^<2{v(1Rmh#fU7|a&j@jAx7fbABbu1pnlT^WVx#%6mPuw z#z(+}xT|O{pdV#L@Nj;C*U9DtG@zp5e8FbX#vo$%JL53_*HzP z;`4&R)TFvFG^{cfNN!r%1ZV-!Qr znT{A4bUuP-dz*~Mr9xg(JamEaA+dE^r$h_a3$4T1CU-j=<#Fs&xz7A!SZ&5}u^-4f zG)wAFri=do;_52qlckWYo_1!rD`h7(4P;pOAs32|lg4v!>VBvI?M zQNzS*If?G3*x8}E1M;<^Nk90hh)y}`Ejnc)0XHMu93zFtW=du_Hr@bQe*kHu=$A$< z<8(hs=w0H@VpsmN(#kXql!safQ2R$iS!WYeG8Kwoi|=QP%w{vcS92zv>l-+t;77xe%rjlYNHQ zKXnHFUG|C@*5f0e%t|6~&Nq+R?1y))8rYC(vRhYAu>5uDk6wWVWij!}+Qj1Blj;6l zs5aBvQTuHEwn6RVq);|aOO;MYLs;cONzgd7sYlImn13KE)(%xRtN3Ut8C{jZ=lV59cg}U zr0W;ktOFq%FsM%5cQ~~vUS2ef+_uEJ7L;*xN2xz&1%bx47S<{xS5Pk7&Wn&ol~u~? zWzc#R)gGu+@=iaD3HiL7c3c-B2`TI0VL*=`DR&DlPPN!q9o*Cz31b)~WYA60+GDIW zfBVV+Q10Q)S-4G+M{Y^Q)B9otmR*;BIv>K?8?h%&Mrv*0Ml*IN4nI{-d_&LS?bYZ%fR>l z0E135um#3cm8(!n?y>h#e|WvFsDj}sT~k3;IDAZWHzUEtIEb&0<0sW58w*_OZA#B= z$@T77M~{qS&=AKHodwOTAl0akU-bQld<(1s4X6T?B zrIwx}qK1ZnI^ft{c}pEfYRP3IF6m&nj{=ZL`z=Fsz+>(t9>(XUKAo;ZQ))=il$%o9 z6)X+v>`G6nCiv`xqEbFl2c`T86-jczaYWfZ#y=qXYGyyCPk}nuH@_Dqbox`lYw`KA z$WN@0Jjt$Ka;1zNK9tvT)WYQpOG*I>w?qE`ZE~B`MV&6B*>`b#!BIuc&XvdV8s|}` zVbL6Ne5JCLw$V}m{J;_`hTV))Ix1#bSe(HPrMEr>cpHOMH&UNbn!gfvkKH9nDOo?V zs&9+H#E|nN>c?!T4azk=V^&2ZR=JG4ws)oW{{V)8`Gu>a(%14UtdLAR>Q99Z-HOua zO1fm?ERFYP(47iXz8y^#4Nk?hK zH-=4};#jAO3$PO5ADZ{IZoW!c1@`22cN2y^K^7WN6W~R~{gmw+@9Jc=l0*+7#B(xS zQW9?&BUD<~KWM8+ZoG=iRTb`Mw>ei5r6I7SD1*~oJ@wG*@_r~n-8_{403_{ej-kOa zHNn(vR?-jHD)U#JDsAG*qE}{O&I(H3R5tJND6LzMy5g?k@+hHeIJN3UCw)C}c?E5~ zE!RQ+05BEkf4Z!x`8|pBLU(YL%ex_xK9~DWsRBcoB*>VL%HrH4Q`P*xYACI@uR&JY z+;s7oNUOh*j&T){-W`d2iOrF)vKmVC3n&Rq@8P@OQTJ%8t9fLSzRye|aK~oVHDpZ6 z2uU^}YDhL0*4ol`e1WI=VioX8eb{Nv*Ks^kWiqpinEEa0p`>^pG6gFxZ(@JYb{str zUx=eK;NeoXaQ^@~C21c5P)Y}1ASxaT&(@G7Y^&)2n|XfcWaG0R9=UL%6z~<$5r3s%wyMd$^A_I^ zJ>}y605HZQxDw^AUDVunf@OlU(%ZqVcXLNpYYsijYOc0ErMsQ_hVGvq;hdK_%UppY z8^RzzGbN0VAJKM7;YTidWy5=JBF5b>T(=+bYIe7yO8)>e!DGM5y?rTO*Xc(ieHnjJ z*Z%;h1_!)+Ka+N=AKPg>X8`ob7|7WrW zXq|F$O{~Ai@ccj92a)ifJ^k}u*hymv^gj>zygG55i;`m4SowGv8SOBUa1z^6+-)h> z$O~8AJa)sC5Pw*&_aox2QY~!H{{X!I0NxH-r!1M0s5YJ{loFlKE_g+H5^w%2`xlm&H6~8GdDF>)Q72{veA0)R|i2GVU z_x4}69CyRVoBsg$dVl%ff$P7-yd78V{mqXMtMfm{x?khh`l8DEtVeM^r+eYf`_syE zEjH&c$Uh?)6~_X8OBNPW3MxP(Bs9oKJ_T1gUb)|hLcSx8uUas9itGJ-f5hm%Q5kX@ zPT5Ig)A|(FE9stALOZWH$)YqGKN{BaVHQ0__jMI}{-c+c6j~|cz(@Y1eyINdpf_W9 zpVC)i{VoR}xA-ql_WGX-`M)mB^Uit7xrSm=rsR1}LS9N^$`)6v zHT6hRO}vWjUZch7s&%6bc1NIYNqd-Gur-c}Nw)MT4+>uR1Fl5d4<;iZZ-AGSZMdxH z4N+{-CR$M_vTC`J-(|7sC8Qk^lnnv)*FUYsJ}Qz)z5tUHNo8CJ-g+It_{^=RDsj z%cL~5DhqSwJo8Mc%@yf1rEEt{c7a}r#{73nO0WA+QD5Wzdl_weNV0&t}8zCxrTTJy`D3{{X``)M9&4#4|rJ!TTtOajrDWptC17;=z{Z!_+hY zY z4S&GPHwSFxWUTEs1avhU_2P{!$s|hfsK{kzz{bbBujCvxeT5V5WOR>&UxYVMu5{SJQ3c@foO*BwPJsyYuv0c@~M^SGo zdc8>|0(+5h0WM5MUu%QdDTS{*h`VA(@{TeYQ4R%r1wTh|X?I8IBmV%c@5UjH*?m`X zHq-G;2C=Dix5Vhz-#3#`y~w|kEY%JI*Z5Y)70hP>d_k%$I;($T*qWc z`7;yOh}?xZ*g~EOxj|BauWtVU#J~Rl>R0qPl}^UjnxmIGrc*9W86AA2&2EC1ma? zJrBOUmG(RnJxix4-WT8D9h<}A_y-@wXS*pqQ{Z!kp(S|?%ZZ+xgDA=Z5+os&q$Md) zAxY5J2XDqyY@C#z=l=i$zSa3UbixWLczXRt-O%n%YrLJo17NRF&PC(Xec%b0QI@6em`B&O0T{O?nh_ev(K?!=~e}QL4v_aPUPRqTFDEp z<7NIrYeNTXcCC@f{*^nuf>Mas%;mRjNKsYO0URYkyMvYASUn^f|C&xcYTOISW1^N=?5CcP!_-1tGTzCH_+mkfI1G zT9v5w`O^OYZshunt1yC+GI=Q|jF%AHbVO2C!9r}LDZ3Pqe8B>Y_0w2=n7lTn38uXZ zmzeXeL(RKuicVHZd599i9VJ&Pc?c=ouW24N&G8&eb#`Ycu97;QbCH(Tv|}gL6k=OK zdMfwoZPL2fOD7d5U~rwcB|SjanvAjypLf2FaMDxsi;0eL-kW@zEiAt^#QX|rQUgUs z{Xj_Y@UN7=k`(98N?3oDA6b7I#u4FT9IwoD21AT+p2PC)BiZpGAHi{K z0l6yH=S?V!jJ+|{%WL3*=W|>>TWXOHBElUyG}ol7`vo~xm8tawsD%a`N6xUvx$}0SyCYLXBcT-xDRdPCe1zAz0TvuYQ?WwnfSD9I>eI+iw zy{E3Gtf5j&td)(Tv&mwyFF2y}i#I+5-%7KMs~9V6gj?!ECE?;&`Vv;Ko9#k{&*4qc zaw~4sP4oq~jbT9E?W}kkie`3tibq_^Fri1`T7Xg4*;)4}iq>QHF(sFN75a!8sV7_e zC^qgX4nswz8EXREi+V$1$rjLn4Jj?`3`dUg4MpQ}w*;!|N`Eff(mPwxMW(+WGI5H> zN`_Ll+a#veCi)7X+1O0Gqf&j#1_aE40#I2N@zbrUp6DZ@)|rHQJ9Uy^#_$nP*-31CUcp%{xrO!?26DKx z6{LkUm1(Kk4}~!uxp{X5J$R4|uL#Dkc}rk;jRo)St7^F!#8z4`YpGRdI73bPie-eU zH#(7Pebs5L$hIwZgnorxJB2#qib`w}tMmrCEpA<;Ra?fal`kyg?BW8PQVP#Yf#NHp zGz)!T;CgR@8b~ZeVSp)%);aZfK>AHvJ z)|{SjCZ0tjA!=AIB|e^;$sOp);mC?#MJeU&&XyMeOmexpdt zV~~=4RW8;kDsUQS%Fm!AtBdXzW*#;`3vNPCr1;+d0A(!foJNv_kb?0|mjc9vg)2ai z1&t5x-_VcQr|KZ~D7Hybiqtw>3)5cn1NMY^ZZ+9JwdkQhbqY<6zS?4)xAhjkSMVb8 zc)L!o({c*YeyRkmH>(Qw-_)zOK|eqgjw>Cqpc*5<4~P>@!+Dk4JYdbfHV{Zj*?lih zSG451io=hLMFu&>Kgv6{)D-eh?c7a-jDu_1QMm)JU+tka07_ENP@_2oZfq!qRxUSy zEw5S(p^^1yT^ZIPp+vry){uTJgYL1V{h7=3oA>Jf05F?;;zMNttXgFOeWfIWZ!!Qh zPun7Ga`Ru%bdKTr2XxFznRCHc+DF($NA^gXk2=%pIA@)}n4*Cr(hhapl69d^jGxeL zb2ZD-OQvRfM%q)oNB@Go1ZR*+^ab*?UY}4o_B1u8HSWrKw%7~gqEqM;x zVapS^f;>9ZXKzDli4KzxsTWVgd_@D@8TKS-mR1vGN4WB_)`{eRSwm-(*;!WDEB~9_qG|SzqrRMG7sXPZ8@4vT}gx~Sm(eKVtNbkQbqp&WhXdEc4O4t7u^S$#Ns6l%&i3ZY5la9orrxZ z*yYYA0p6qR7QJKHn(Kltir+wM+!{0Bf_8;;u%oU{Nn{-O__Ri(cG;7;Nxs|IE8Fd< z*UDt9pAvu)I24$~lp!t;X0>HsAlm9z-Ns?`0lmd$p+E)-M~%FHZ8!Ul0ZWg`vwU?* zDgFsU{6~SPOKp0Cj~dhHSU3r}912TOOQ0m&4azC$E6mp;SB0}D03ogkL(u3B@_hk3z&$0U z)RXy=LA`1+oO&=)QJ-KW{wmYE_N1VZ_)`mG1YU+w4VT0vq}itk9v3E^SN4Lww+OZyTpJPYwL`%QITsiq5OZ>7w_pQrHkAtx0uGcb93}V#hq({LlEix*CBOC9 zs52IAycR68OSLGk%Eu{6U1Wld;<$;jnc?Vl``^?`cBYk<#f#K$*{dCc?PB8#2z+f3 zQ>FeTlAo|rc3l_Uj#pHdWE_Wr%@BtfgwseLmcW8E_Vl1xy9BT^PPLD@AdiEbR23=j zO+!Tl>7^|_#MeQJwA=0zP$S8eO$(pguM)p7V%{_+dZg4LhY zU7k0Ki*KBh>XEIyziFY*ZrOI|F&!$S->ChGnAoXPc-(s5C_k7d{!}SKPxUaclvikv zn#pFS1tvZSx{>)DbOa0g4w_KD-Z<*ajndEMK?K0$5)vGKJXi`NOQ~xj{>0jfr%Xbx zc1(QM2Pw1onPiU@1U59cj-=4O?kV($Pqlus9DJK`h)dZg*saFSks&QMqEDER&Qjeh@2uribrXz{*CKvOHXZefHXqQ42r?){! z-*oegj%#SwjNq|iF$t#HjU9*DL9jqVRFr{l9+ZTeZ@Fzu5?zOC#T2NPTPFVinQBp1 zhu=f@*nhP{cR1U!Hmzw&?a?JS6(6}6`!vNy1;i2(w&QOWP(lK4_S&N@mJ&LMV>1-P z0Yx%cZb23gzDke}umUKa`d!f(YB%atIo>bL=c&X&<}r|i`5BH2Y4={{to6AXq5e{y z?mpJ~NUxpk2PAdGuOpXf#EWiIi&}}-+g35>s_ddxf2XW(E<~;mZDTjGG0r^O{R(3! zrt0>T>q4`ZPqG|bN{a4Dp!WJ!A%47Uthc1QeT#lmmPz*=KMF$Kr!PRXYxsJS*p17E z+M0hfUYNUl4zB1P`&xtG@;zLct>lt7&W@=cr)o-&HS^t3@nQYsIeqn|q_-wSRPE79 zDkAst)}aXX2Rkw}Ll~#jLy2X@s>k?SkK6Fpo>L(Pn(Q*lBO9=$EID!=8X&izzqa(n zQp+K1RyqLTqc+>h^!Bnfu}f)^#-&H?cx3+v0B_8oLk7ZrjuY_JgNJo;BNvGOV_ zpNyr0tJnD5_E+T?_7!s%kYOot3?;GPg(k&DgSSePuPRpliMX+!tE8xO|09rYEH zr_q#|bzt7#u`eTH#f`!HtBT*P;~)F)PsXTdOk3@qG`*SWlxp9FG zQTPc~(4VkrM25?KWXQ{39)Q8y{M@Iv{M$@6KnXy)h^Fi1;Fbm6gqshs+PQB4+pmcP zrsZ1*zr51Y=OUOz#<$!(iR~LP2yysH0FqBZVQzxjnqGYNS8=#;kV~;W#&-7_^3cPO z9SJwup>&IVf~aler|82%nEeOyJ9CD{K7*6Wxe=6<{63o0EOqN|vY~eEjv?vSf_+JY znGOTNxb8zQ1xlX&9y70@gc7G5T2pkSA89p9v2D^_%c|p2MLFNdu15M;?p*hx`cA#Z z@{;nT*+naQGM!K>9c)b(`1NY3mz+mEacG;;Zdu)JrQ zaPD1*&d!qOqY(jEpPh=Q7NQaq9cIYCOP@OZ1pZ0jzW0uVVzW5TEq)8Gf8qWI#GeD^ z-j^X~lf)$%XZrNV{7;$rM}|)^l%Ay7*|Ma(^1--T-F<6PzS3`Ai^Ejotpxsy{l}d0 zmMQGXuFbI}G7{Q!y1e`;=UXdTZhbqa7}tG!K)mi|KOVv}$nj3)bJG+N4jU{;OXb(p ztpYv^6YZ`a`6F8C@K>srl-wFi@qItzpX@vT0OLz3ua~LY(kb;jWBQr^@`-& z2e|k?<;>0Foc{oD=I}CvxQv+31V?GbB#;~_Qi%h~yodh)9(Zm?Q;T(NLBkTtO?`X6 z@JFP7Jo1`bdy0DUQdsJI({u}<5)zdv>L%6$#`W}Sc!}WEYB@AVCeO%T(U{0M;LzF0E`$<&)%n}- z>)*&eOMdK@5|eA_y7XVu9S?WVuiJC6cKs{;@BP06^NR;2>ql|1QR+y&eIfRqM1Jjh zgjdLaNzahU1cOe80H( zXEWSjxTQCMZSO^L%VVif=$rNPuMqit_HDOK4-e{kj|E@0ysTLyUsHYL?aw{NVK64CPlBEs1Lu$5xAnRH1e~4^qeru)X%;q^bPx8fJ=RU@EtCjb&yF3rI zye1l^@;+yijgH131rXbk9I~BVf&Lpv+~UuG2Cx2O%vYtSJ0;mJ-(SP~UqF0Eg;ACB z{V-W)?*9PlC%C*T`d|M5Pd6Ci=xwPy6A>Ga_Wl9_QmNU=sPsdmbx~8QQ_dfGua~%9 zH)E05_VbzBEVuY)E&6smPm71|T+_bA`6KzBev7jH->3CI09md}Ka*zi+3c*xXV1$> zc1-DxlBXShL#lbjsGI6kqHFB_FNs?8#<=uJ9mgrZ?pc&7)S;jn4z&BBh%18eWnEU= zq5VXEZB|=Q^k?MHqCeA%vg_}E)VB)8$%LsmUO4+%EV0^33S+D(DN*t!)$^b8r#U*b z7PWt_*W>+~SJVeYRq z=Qkt8@etQtB_wzs2gF&+a`Lq%;}@1fyJw%9o5~pf06HFG17$@hKL+I;E=N;T9~r*O zX0zEH7tMI;b*t#HDSvtS7yiB7{K#|ftML!_uF!~Bg>EH(YGsA^TBzcNz2`AW1wli6lbQjt@eCo!wkzZtG zkZa}L#afZBl+$UEQI-KNA?qnc2iZlGq5)(a{lFxm%JCF>9%3bBeL#-7b=Tf$=*I$6 zlNc#V+I2ND9!wD-3(ncpI(0z>q%p5BRSrFVFf|w;lozu zW5{)Gr7A91lf2&AbiI7@%-GhXRxo`J8*uPYf7Nnd7m4&|-3&$~#ztX3CLL|GfwiVI& z01Web6KzEvvl%q(?A2pCqr#?d^w5lh30XZX;Vu6FZB+b%w=GE`^Wpb6?<}7~zM?Th zDcjy$*iPk!{dN=u?54gd^Y4e*?d>Dges{0#=riP6{{SiW+P&}4`Ix%9Xuiog!aJFS z?z|36&oh|ZC%Z|?`1yk5IC2_V#HMnw=B+AQE+nU7nhH=i&}dH&@*Pd}vl}RD!_^(% z#rL!$OrdmtJM76fC4cFm9gN~k@E$?UWc@)y&Pi*XVl#N$V;o(4c}+Ou5^>UCZgb`X zn-HemP&O(%KmgIWy?w{9=WG0rvK!=gv>v~N7w2B392C1>Xt#xQPP2||Xyb|`d|fC^ z&^}9s#^UGC?X4qgt2=+`O4&XI+}=L3xiXrNJwE_mh*MmTJiK;d(b;h+OIk?zM&3Gf zCtir8EPSHY(I(?o)KUGF*!g7hXO%n-i&KHoD^T#z>sU;`J5lQ7crST)+<>IHBNZNe zG@UN)K1Tfk@!D!KjtdGUCl*y~wt|%D`Kg$8DdgTqdr~|qcr(eEbz^DVu9b#{jb;>x zk|8DI^2=YSwr^yWfwy(M>?kTY{?P8``V5mbv2TvTih;>CEHX@mr*YFuT8m$BSm;?8 z9^!MXJUIB=CpF7sF)`Fv7Z%1wQX;mZZd8_1y?~R~O?+r`;k55<*%aFu*XB0$@S!%8 zeGjPJiu$iT=f%M7zjn(o`R+BlaqdjpVWu*Sx1dXiKMEwJJwVZ8QEfo^{{ZG+#_x;R z`<+$!CEa}wq&^M&nE7fx*J}zrnUG?mG==;M8E#azbo>ZEfwK zYg8o{7D9%f@G7d?GCd7!dXjii96=UIAaypX+osEUA%bO(WhwxAdid2vv6_9$x?;RZ zlniI8p>P9wTS~0hwn>&$QoEKOBL>kax-ZjPRVK#KcU362`1U?4E;qTP?oUhq0M5Bx zy@@x_=yfSYwT?tI5Iryti1Mn7orRTf!zS>(6s0z>x8=9|s{>DDo#;Lu^6nd^k)`TZJj)6cVJ}<64)wV!L*&eGzmu}iJmiZ)Qg+n!j_X* z7bi%F(QGTChvpQ7JBG154Ig153wffP;6)mDq2bucY>kQUn)H?7jtA2CzwM)L23yjrnO;KqnDty zlOUN6M#%~U3w!$2japAV083#cLFV!pNDdSP1NIx$)f$t*TW#wl1I^_~aj7x_5~hXD z-_2lauj1q5jFh{JCoSi5EpKrJK`TB3OAFUwMLtI>v1f}bEY4x2Hnb&IDjL=PzNC>) zwQ_NJD9%9mQpr=AYhztbr5z3vdT%9t^Ncd37FHBVAwc>4wQCJIKBZLl)W!_+EI^5vsIKogwU^M6ZEyL}HMZv$i3nY%3H68*`FzzX z!Xl6XAe&XKdvPmguVQ@>pXIVyDO2ojO7#10P?g9J+soA+dlLwhl&mFVs!rkI?xQP$ zPhY4mW)~QtxGB$5*mNeR3#Fcd*6PK9R}w^J6+mdATJ2g@b5;&n6S9=)mP?nme3QiJKTu1x0Bg z2n#^n)Y`_XEp|k>qBTheqjA`e6Zux7q3J=E&5?aTtxDuM{{UdOC~fr$2Zn%a`YfMhKPC1AgtehSs ziG)O>Ae8zm1M24sRP7tg!RhjMITdpoO>8+**XItubG6)&Y~9$TD|0 zz}|WRdUM*0-#~J}53khoNpzYr?jLDr{cl3t`^vi(sbg`2^ZEi>5kuVM%n*X*kK z>dE?zReV$GNFqzXJgbE_w0>dvPvb+DwTNyv*2l#>S>h1nk3)Whn^=9+A1#RXH|4D^ zjMhg1#7(lXnepmc=>=&6Uky4`{{VEceyD?dO)~v5BkyoNb&6N7i+IXPe=3=F{@#?u zJf6JQ=8gp=#9t55EQlwPbBr6FmRbVjr9YKUAzEMBD|4q>&(guGcPaf#B%U8T#LL=@ zgayNORti!AxBpnRlN1Cqrnu8SsA$GbGbk*-z3~?QOAf+ zaAL7B)SpT!8bZ1ipNhhY<&MNA@__E)vXa3C^(D3SLRDdE+PR@mwaKG=Mk|Xcen|%Q z3ApR`RBhgt4X!x`*zMe9r7CVRoL_>hP3dpl9D9yKicNMTv3qptNeli8N99JB8hMHp zoU9@qz7|;xv)h!oq$oD@fdct zH(p01=?TZ+?vMRts3%Wwtvyx#W@6a?0HqGuYm~S_RFI$b=Q!Ck)3I=y`X}x%lFoKvOdumn+>%nm!uS%{#5Cw zl`~A*c@we(Vp%LGh_TaSYdXC^s|p`duGE)OGcGHVp1jFrb|8c{jiXPptmw2(RJk*D zc(f@uQn9&`ME(@dxN20Ma~lRY z^&D-WMxh48>F=UO+#nWJ7L8-gjJqL-w?S7_wnA<_w6lx#H3=j=^yf*lZL%0{;>E>n z9_=U!we=ZRrRX)410b}iEKY7R>_ShZ7q9*!O-AUh=!B}2x+Xy~94DIO7r7jr&4S?w zNDC`uE^MPdB>y@ZnPn8TK?52(BCtk(-GZ%1Eyyp z!^C#?i{V)gB0Fj$Li@_~6jkSQRkm$eH`KD$)utU0CuT%ef|DD2>Q0GEErp;Tcp6eZ z63|&lQA)B2h?ZN3$eGJl%@1SeU=LiecK@VypmX%?CgakJnM1+7x*FW zH}}JE?sUWi3hQ zRV_*v_Zm;nsNb|BIyHyOJtgQZ6QF1%3qH`ai_t5bn*nqZ?m{QC!Lg}*Ssac<{l@M* z>+d=cIubrDKI*cryPs?c*<~^Bk%~EQZlJKUh?=~iIxj~YBKi-{O;@RO{{T}!t>3CY zq#W-gZevSKCS8Vu`CTof6XWAd!%;`MTP@co3kF4j{JA!{8az!vL?-QqvxN>1VWDOn_# zQ3eE-mh!{9P0MN@4aT-Ns-1-87GG;M23=Ur{O0(}DuaT;#fFEIwdJR%y>HNz2BjXfS+1_P8Cy;j|A}ML<2JW(r5=qpMItu7Dc=c2~V*3>vT#c7m z=Ohhdmdgigag?MUpz3c&vrF*=w;Sw&ka=!dPoQnsgz0NvN=s)(33nRRU3Im%jjKvebqi2Qrjp}kB#~oK3{@kN87&`0zn%e^7L*acZ$T@yt`QaU{6~+>J4NkCbGX$xBNbr`kH&jql(~OXR+C>)5LKEL`*`PQ4F% z<*}6o={I8QH}Ja2$(!0OF89Akr(Ats9*wdXl_fW^!|N6ALTo z2^O)nWck=NfmI}#HpF&QkV9BnvaEsCE!lei0CiPc$VN{X4foJQ{s#=qb_TX=tb^D_70p=h)52wN|H2C8dN>j30aYhs3_aCqwuGzv@sW#0SIwLDJWm* zvC@kTfcVM>9K^_1ZPrsDSo~c&d+5rx*uRTFnY^|lSX$(tl?nY;>1s4}UVDqlyb!#v zb1)4$#BlQcL-`Z8MSZn(9x_fK)9Od&_bU#at2mDTRFG2I1DqCq# zlG0oK#Se&mrlB5INT1mu1~@xpg{|9x)gdGHikI%M&{=8)+Tb`uB*=Cm+;{Y?UzA(e zYA=?T)M-)F67+07EoxGkkGUlDNIH+cfSuMp=I1EH2`iOxwp6B+$qpaPklleDbT+5z z(VY8>S-BXEak(reLE!v&&tBvN6qO-thL@v0cN+VWMVxrmcOHCNOIWw009+)Wge<3MC#`p!AI7J<#oM)7elU(s zdvD}4>+i!}PxCf?BKoeE$E5up_ob*FiV%(|mMKUn7atY_JJ0?n zUGwU3!`c4;-GA;soQ@wQk%y0f;}0#DWX5eTxSpj8Y;FhN<6o<`LbcP=Ed0le?33*C zfzHO!Whs80`d-8K(^bwm=+{A4t!sa!ih9wkQeR4K;#QyUsjL31By??kqRg#}l_1&H z)%nwKp|84u_c`s<$?DDzs%X`iOf$4HHiYQd00B~V3^%v`C&W9-)?El7><#~8+91I4~_vTOgsi4e1ry4 zo|Y{mf4xSzFXNw&zB`PJ7wVVhm-T&1ubT6b+ueWk$D%op`JWj099$qSejret0QXmE ziyQ88-c_GMf29{;p?zEZcXl_ojN-jcHN&!O^6qk?lBV0k@wqKcN>y%!**=N@JwaEM zc^~uF$=?z2+a}&6V(?$qNvHl9?b|5sJ`o5?$xj`T^q>0H_xaxL{bq6=H;bB?JY%!) z*yH$?5^jXbPx_gJZ8rSsN`A`mzu^y${jUb{gque)OIxFv^#ObavZCd@%}prg)RKdwj(2IN!lG)Nm1*d8k1Zn&pFuX z=t_C&k=A@~ig(eLjQp!(( z-(NHT04?)>cefol{+QxY{=SR*y7c<~1vl~!EcEo&tUJrBzoE@P*XQdFM|D1$2p&aBuR;DK_~d=ByKAfI9QrR`?|<$qJ~yni zIP5?D%X+`*=)c?4_yZMgSsObM99w!qhJ=p+UVrkQ zTkiMUYbkU^AH#Up$LM9t!cywXPm52(@P02cY`7@JM}pI`vM;A-0k#`1DM};ACc1Bq zY{ImnmL$(T`3}9CyL}~`^#1et&;3$&RqpuxMfa1tD}U5)oboKJ_^Q+c7v_}2QDfog zHj7v2SHZZCwBqZ7?~MNdd1K7onfNZzNg{<*nF&Dy;YXeAe|>dn^(dIn=j9HLsC}l1 z9JXdmE@SFe;jXf(~_orwUY6Ki;rYts?~ zN=@PVpRm8^v4xd`{+~QuXW?w zqXqrYkNi)x{sF41(yu+be~EGSu6|A4>2sWqGR8T_WxDcxHXUziQg;$9vi^N*%05A> zHgk>>hH2I2rLC6?@0E3=kkg}ZE-5J*{x$SZ{wMHiuaHL6 z`zCnr^Q7ju2}WJd$&x=Uey`B7vVL&i`qdEyl6V zW^=jMkrxe#U?bI&A;Dy}fU*V6yiHbf8@20F?4M%)0D;h!)#|wPY_y>LK6fH-b9pCe z@|TjlRdLCPR31p>D@QSy>*Uf{m030@Xtwk;B=ItMoRP_nmBhDgOZF z5~;t;qTlyp{$&>*Ak?Oi^9OrQq$#BW2q_}qgXC+fXA39!nh^Cb;Yyna$#bZ`zP6Ev zVSO1)baFmfjBUYZ<20}cxIYm={k6~hlAJid#J%Bee%zPfaA!5;Y{KT8vlzS(WmxQ# z<`=O<1s0{ajn+PPuA5#tf@dXnNwf%Wn&Jw7j|J9`he8DuT}-;WGstlHzF5W$hsz7j zMVSn+-~)PIj;sD6lD-cs1=g*r#g)OqW!ic6OjT}SYC$_ryGc)%vaM^@lSKuVMKNUY z?5mcvVoCAnD9c-*yRmvRF7V8|<7edE)~EJN6t;eYW%kb`EVMmi`jghQ^oV`i5l3vY z{-7#%;T?20wQXrMOYcLGdv9frRVrGO_kFb8T8NL`HcR61CZX*LY$OjGi&pE_NdldX zu;L;$k1^%4@5;%Hw5~kF9g9NHcO^iLK^Ca&*Hx9cO=U2;n^sj~oH{gpJov7C;Ze;pr{xhVsnU5F#h8u_#2e;9guFURyg$JM_R`BZoH7JpJYVXWH9 z5$eRT{aW?*)j!+d>Em!+nP`xN`U?Ya9|w);e{GE`1fP0S2G&Zi(@M!h1bx8;$$4l} zo{TJ!qObjw%p;N$+66lyrS1hARqm<#O%Zp*O*qK%$O}f*9*VsUvT&(OxQqNTPNbHA zT2rY_=znv9FH$lDIm4uaLSq{aqzl&m^aA6uAUIRXPz41(X|<2+sQvmHTw4f6;A&Ca zl5{$z^sKo&2Zs?Hk_h}IOxQS!U&iPADz<#GEU4nxZZ8&w+4z(I^A`sH0Buv>$u`S6 zc*Tz7mNy?|7YR`Y==M$a7al;E5$y@Hf})tIcj3@F?!(UP!9>d0K)!Z%h;Z zXIi?S4ICA#l9Q0UoUosy_*DE zt#&+aRB+MD@-`Pil)0O@M5jc`k*3hCW9 z`m!X{sPa?`)0C2bD{H!GX*RnfOU3tX4%HNVN)7x5)>?HGO184Phg!|PgJrUmTXM)2 zH%&#$AgrSvqYpvl5+bN9o}P5V5%!zxFnNrs55nOB_BK@?@_=>S=qK|z7RSdOOG*89 zCZ+j4rklC?9$`yyX;Y0GUg}RmMQS!h5Uz>(ih2OJ3n+0VL|oppF*6hVNq!n4LH__$ z+Ldq1uTkx!_GR=fG+?~CqF~l91h9oAl9!3qtL?b<-l`>Sx%>;iX0_BHmm@(SdS5OE zfPzkyxh$eF{GnJ`rj+NQp$q(k3;pKwm)f=0Bayj2gn(m^{k?Dz5Su&Z)xqQ6=z)Q8N$wAS@E zoJ(pw5J?18#d~dcMOB)~_K+cBV^{R$A@!s3joK^jrsT3EW0HLg4E9mQw8Y3sVHzjZ zNd)L^LY})dUh{Is2}D`SiDJ|0acUatprA?}OM2(f9X_5WQd(I9cD5vw zYNgxKaq7C4n%jzKvazphT2&h%U_dEh%|ZL>j`h0p6aq>;2#!#2rP`OyK(EF;k$;u3Mc+3$yg|PWr(;r1#B!6|O znt2zH7IC>ndzI~EXUdc#6u5e?U+u1oUz~9Mhc~ssMqSFYH`;uZS62Q#!qWc$LFxCA zS6cl2a{Wau+yvwLSzgS6&oF?YEIRvX_^-Fr01Z5<&bz6fsD-rM1&jdP<+-?|rgS0{ zlWQ#nH=^0A3}rDHZ&;sH6N${9Pv?RZgKLY5$mZbpdsvQ(DTH9;LT_g0mXV4N4!)rnZvRq4)z zO}{gV@%GSNQ}kr3@Lj^~D}k(~Pvl&Lw=H&v&|l1o+^D#mET`0wMI1531|#MW<7uz~ z#RQ~#E^SL$R>BQbXpQ299Ej^o;>O)Z1sq{?Las2ZOCKUIh zq-vG6>WAO0L+@*n{K&_~S)9d-e>mk>yP@Zs%OrkezStz`?C`9r8uR=O&RaG2>MF-F z;+e+bkK^L)SCS=cq1N8ALfrz5}qF7lnzTSmP!8rrW6T2J~e#z z{)ngh9QPiMHH40lx_hz?wn}vTYku_F$a!hOAxQjuNLlon46%RAYwv%8DVvmy%YtS{vGv?ox=R ztKjCl`iaY*g6s#G+>DFiA~qZ%-BRPak1Z2=cVC5!SLR?X=A`=~G`>O0GEe+bW5Q$| z3EZM@Pg$)*A||i8Zy~tJ%B%wZMT8Z9(n_k-=Z7>MZ_cC~oWRH=wJZV2-Goy)$VEZP0)^jR37i zEs??j&C1P=O4wz|WDkJ}T5NvBTA8Mti`{EPFC)x3d~y>Tk11(U(PhLEpLna9HMOz% znNMck!he+rGs!vQw)Glr8hWW;2TrX?K1c`7!@&&lLBXunN9nhw9=HK(a2``{#SVgpceb7 zYk6e_gt9ZQ#7b{1lW6=S1B*y{erX$%Vxok!*s%LARnpzMeUmODqeNF$hjTd}4#gm) z89m8JKad5R^!*^zEJh9bmK-!gf1dCJA*QEjbo20uB z7;}@%btIv}NwHB@(SNd+zhb!d4MkVWxngn5UnJa@T$)q1_HTNPbGv(S@-FoB>`&CQ z`ocagxW)>8qVmE1FRQ9UNopoaFOJK;z#6eo} zVeb`a3F*^XtGcxP3z{>InLqEaB5E9+ek``78Kq&59Qx27S<3G(LiZx6_JYU z8oMUhGshQ}grZ}KD+5DrzWSuUp?y=xb(|zAZl&ncl?lX=xvjSgy;ohgG>aPbpjp_y zH4Wr6UD~oVTYv$(@UC-jk+m}a082ur+_Q05Oj{wpPB;q|xwtjUZEa3S>NT}sy$d50 z!T>;woz^;=TS}zayQW!lM0)f`M|QIXl+MpgOmizQiywhIzS4Xvg!vBTP>xyB@w7>w zNIQMQGkkG|U&_u)8xjx8q@BbOt9G$V8-VT~82((<~vDp3=)aWc+s>MWzx>I=v ztCT$rYLJ%_xa5O-^u10xZH1Ky^ktES?gAN9WiCQnL9kM^*H3K{&1RZq9{0y#Y+ea; zyrxD<{aA0Ra|;(mjr5_}31p|%(WVO%UM$-l(;1EaqKsLM{4O-DE`8N1mRHe{+p>Dy zy2XO?JOdvwO8KmNFRWbpyAyxD)seQyzK;PldF0R?z7@yflm$XdPNSm00R9w5vnH_E z=k7C?=?Shh22c{E1tq-(;bd-(1o5Hb2sO5TdYpBUwoww9W zFDKJZo?41^Mog4An);a@FaSFB4>?ESXu6JEW zs67QuU4mZI8Oer)s_1N3j=%O);XE^OZ?O%@V~|S5&3x@mtJq8s9Jd@YlAxI3(XQm1 zfmWK^zfuket#TYDTk8l^0E?f@lw8~Ft5|ZFLQ<(QOn+@03QH}vvW1di=1fdmNW6x9GDXr*l+gXwE$yNkl4}Pbp(uqV#8`Y=l#ma) z^we#CQcAoL$ymHO1t~(AI@qZIjdkl*v~m(%0NbpXEB(I~Lz|GKgxgD?5Pu4G9F?C@ zN2vkiC<=<=6$)f@6dr-J+#hWze#E5un~uB}Vn~SmrSJ6znnZ}&hzUYkX-Wd#Ni>x8 z@-+)^b1&8Z0MxV}qVC7?->5!c&1UmfINNQ-!Ei{aM0Dh``33x(Hk9ITwH~U{R7vPH z(z`$7&x6ODc=D#fcpN*aVzMs{fihkqwuezU7o#$if6S{noK6|;! zk&lxKY8q{ru+@x|B}0tEUJNp=04D0xo~FM+^nNEso`>1gv0fVf^@siE#an+Z*uQ`7 zT(dm%JJY`3)f4LvyPc=c_J=jXIA$sbFNOTOG0HH}q{GPku}7w@iCgX=C1a_*d9TL5 zgLsZVd+wv3Eaw>LdLPI?mpLunC$HHxj_1cXJTIH^smYrc4z||DjHo5Ik+;gm;=TIa znk`st@QCW)Tgq82d?$FjhsFvQBZOk}iDXzAm}cBI_oND;yM)%awAw5+`OUgzha8KV728f38B_T`Hp#@s;bIML{HH=UE6E5KTF-((HOhRy zmamV}owNNhO~3a1Ur+5it^%~%vVW#MPv2)e<~_Oomj3|L+D1wfZMTo@4gsuZ7=%hg zxn4sp9*adsLZ=^+;abnU*T`QQ*4gDZRJ+-1vi|_5uVv9Bd6vt3uPWs~(7!YP0Pe3( z?f(Fo{O@^ZJj`<*SIGH}a(eRF?oBpMLpP9hMM{2b$CV-XE`NDzHm|!lJ{~%{@vSb2 zB)_n+<+(pQTV05=nfVq_O3F`&1lLLS2E$;o=RKMqBiUgU4IO;q!Ohp>OekT+T=B7Z-iSsbLf+^(*e< z-GcfE?_Oycr-vluSk}1b9LS%A&!5h?Ata5qwUD(PJgeM4jQ%+t-b%Q4S<5`Ssm5D= zcu7Yu{s-Xco$o1bCBd}WBHlyaU!e6tR}`0Ia+|if&&YD7x`!2E;ZmuJF|vbbKjwq> z(@}U88xYDqBJfxEe5wl}JXQn?vB5fhwCo!~Hehbz(ru{Q=T0n&j2@E4)c~({m1A5R zEA43jW#kz@l-l|lp0!y4u_*cx*)!VI4x+B3PD=StAcy88o0V9Omhz>QE;2P`z_~L3 zZA5qj`zY5(@{y)($sehoqrP3vJ9YG}+D-tP7;7V5n1O?6)#1^5>K00izoHRN5)l?G2WF>-Pup?Ms%gtV)nwQ3Rq zY?ESbTxUPUQ(nJrr&-7B=6ET;>amf3C-iI2Qnu9daAjkR32v~aRN+*li-NIx5%yQ3 zf8up6KPHRx!RMd(3gi0=UsLjQ6=u_>sr=s4U#xLhia$1P{crk=9a?nOv;l$Se{C~g zU>=~Qtw~Z=4c3wf`wi%mgK%r=N~yJCe?#k+9hLn1w%8fy(9OrTN_0BwQiH=Lu)B@iJXa)v%kg~Q zDF>C~+K|LPN|~>(Acke2w)tQh=|63l+CZrf>(=y7Z3FiXnfgS^mV3D# z0-W@k$;Ejm+FSU*s!x$u{{U$}qb)c)zwR~o9JStT6zCn`?lWn|KI=ctr6@d>98xUe z{Z6S#AF8c*zs*u9c^;Os4f0N*AUTS6Y3NlQ&;Hh|J+Wd*!FdE;E6VWMlLvp!n8P6{#BD+zS#bnf)^p?j+P~8MCqKwm{lG$;jEyc2ovlar zMB}X2sG9N+oBc_>^}Q4BQMFi+%0X-rwWT-q{{YUHo$&gD`+5Ba3gsBa!oQ4_0bBdX z7_?>&f0)7l0Ht*i*UF{yf9TDR(5KzFk}BLV&YK=#_q= zO|;bcN6$HmrzqDKnMXPQ00Z2AjlMs9Y1<`;zTfTi{{VUP;fQV_XVh&4w0Wku3hX^% zT?4W5n5aY*$4XY$LX?h_H(c0w?86;I-^}3UxE(h`EJ_yxmFzz!9xv=Ol00i}g1~AQb|Gy4nwA3R1{OuMzWDxjU4W+}ai4 zOSmDp!L$-)4CF04-6S6aLA``oM4gCnfJe;4LczDi;%bKO<+V~3{!rVt5t13dg@r9U z2B6dQ9<#?C0SjB?+}_rtr?53uc?z+`lBE-GQvPDAE!~M@Mw@Ia7mF_T^i9#ys;9cM z+`C$2iYFd;6s1hvBg|E0+=5=AE7X5NZHvOXlAFfZJndAs?WgKnSE|X(TuJ~*0><4c zrh7e0A8Vl?i5zH^qfZSgw#=}p66g?`QAj64L!6pe8AOp7##OYZ!>Fab=1T+gVp>&* zPsPymtd8DIPhDVg-YlZlP3=dG`33Eg99}KuEjCTkeEqb(#pv83%TEi3ak7OJ_3)}~ z%SKdLVn=a^4UNhdBd>@f!m2%~HZahV^5m4+T2ry1inC#pZNZ|xBScH6xIOu!uJ3g^ zsvrcFEh+?qZ*4bKO|TZ&uthR2pGA8Pw$N4G74{OcStZ+YbwXan{>zMZCxx8%*@gnR`q}_(zit&Ku-6An|XS}f#(q6*sa$9pT@f$ zGVEa-$0OA2@);Nqn%IVx;*br>K|0&5P{xGildPS%k3xy$QWWymrr|ihRFi##+OKu3 zQRq(4Z z^r0a>2H$r|ZoP~mzI3LUvJO7VkE zy4e28pV=>{PrrXb>s-nr94k~Mp#I<>k1A4n4?n0hc?SDLDIb#KrYL~Yqqv(L*Czh! z)~BM>e^DrH%1^2#5c0v$gcyq;kP_mNajjQB9QPkno9>BRTzr`cNDZa2rJn#JPr8-2 zX+`6?V^*xC)$|Xu31ko?I7)4BPL!mr%MTh_9EMm3SImG@&=NH@6rM*Zg2N(tj8-m2 z?Tt6L70Z>zG`p+mW;2kKT?JPdS_uT^f_puN7`E1=if&vT!@N+^QlSS)XCwW%9! zQg7C+^{h{$D7IkNU=V(5M@NeP0Pho}Hq-?CriTqo$Xo=h?(k4QZ4FZfW7(PxMafQD zgHqoKw?{3Z`>9*Bm!V^?xTV~2Ct)`Wx=8q!dXlIuvWx5qdDI~*N>ldJKGr&wbDmfM z>52+a7fI=HPgIKgZD<(c-cpU$N%JRK+-ZoM?RmiFJc%3bR&8#T7}jbRv`FGkWQO;( zv1#hyKH665iwf>x@0?zqlKo#H;Zgf^AK%YmM)J|nZ0t~%6VQWdQc*YeSo9?L&P>y5 z0ctiMmgmBgzemaTEHya~9^{Dfre15%Q-f!MPPCijTuT{pkdR{q%Q6_(Khflr%VYAk zRdH{1FK-oG{Q=j?PCliVjP9momj})?jFSnl_vSzh`-bqWBAe+aF#`MEF;ZVF64vxzZ#m-K$6RjE20TVBmOT-$4bbI$8M>5j2(-K zSSf4MQdBfvim{@vdM~Cx%i4X&rbmn5<^>1hwC?Nau|M5bFMjBxAo%(v=vNtT)p6-N zoMoWG^&qChTWQjs*XJkdA%6*8IuI${u68jcmjaWmlA*8RRxsw)3X6CwzKn^?;u9k% zg)~-?`jR}V*1k?MLO1axNlISHpdB9-cSwU@XhQaEhH5DjH z3(hiNxjcPRQdA$_M~P6d{3*Ck0=81x=td@CrLYd&>p zm!qh8ET{HS-n=Bbq7m4#9Z+ruba_54LuO^wgo|xjw-3gZyM?NLgH&_3L3CuDUHQo^ zq_+GtKH*K2=^btErN7gar_hwXOJtr5vE0l)O(*8Z%RaN9BjCT5m9@o5b_}^lwbh+h z8;Il{2{{EZ6>6{~B)H}N(bl>7@5U!v4JR()MmSV9)BdJgkMHUa!an*_@8{?i8c<89 zS%{o71+`Nhj>^H&PPmiy{8y-ZS4`5CPzxuSam4`6E#ypfv2fExI{4c9P-Ukq{$THN z33L%&QP zqWciHM(KBBVcRAja4RJ>`I>x7Q-@#MYRPia^h7^nS-P*Mk_6q*DCH$9OI2G z5poI3WCns8VJiqW=t$62uD2Owk1w%Vxyn@q)44*OhGH)k%zjapdl9Y!(ape<woE)eMtWRsVhO~9m>bZP%KS# z(tj&UXoIi1k-vqq3pgI5o~~?=JI$m5gsTMfhSzeLDA}}+wCPGmLAEn4LL(Y##7ojOxegRinkjS7Lt~+ZexbnDFFJ~g3>4<%P9j>#HJ;h-7<{b%9PV^*vZb_Wt zlx?r;SKn2e61vXFyrbEx9YZ6zovy&B{LH8=y4d)e3l0!{fL@Z13$ZobjDGb*AE2lI z01vz^)T{Y4M;}-p2d2%FexI1>Lzl*E^|tX%xgNiN^~)C4C_BCAvLyH=j~0aaVKcFqaT8?qeh zDx^ItFq|t&&>0UGULcG9ROx!Bx8$@gkww_Et zbZKv8O;#C7Pq+$97A6vutpE^je=0J%aqLD^#rGk3E-c)er1~jE{{T8hzp||9~ZQ(u^c_TW7JZXg78=6+B`nmZmw2X>LETKuVLhV1so+djH?cs3KQiY)M0TL^d&g% z8;nYlU5b?{KbT&_@T9EYa_+|K;}YwFaT5!QWhD(jO9TK5pc8K@r?ztP>6Tm3trW7& zCO3(I->oiKWi9xU2Zi*uw0LXVZ(~+%Q(Ed)*-ql|_>ZledoD^@(6}2`+l>!eX1*n_ z)XK}b#GWFudmYT1%{rEtg007+iAf-luC^5&iXjhC-EEsDx*}%-BEw1upAGg>bO0l* ztDm{7od&Gsj;Nhsk&l?5rp$)Z`-%qNaTP(eEP9uEaL;nZiH~e-0gy@1Eb3~Gdk-f| zMGGX#BuMS2__j{ba7B-WR&8uD(^|fy%L$80l8_|0w3Bd>2+-9g=EfFIgI2Gpbn)&n z>UvJ2CX)Ie}t!Sw|I&ZpsPM)o^Sn&v%{|^5XLnf?91P zfI4mh`rfWJJ|ihSFjn&MiYk~tQw=#W5#`496w1e-+7v)mw(&kSXSeWZsAnK3`3`4; z=r?3LqbHa-9k?E(CL=Zp-jZ9m@VT!l<~07&#Gk2czZ$Nk%1E;Lg#J$sYP0x!bCX|( zxlcf8R`t?HL4O}AriRU4StrKPt?YYpV~#K1$nz4IoR5!Bz)Mchv`J*`1n7PsH5Bx# zQdvbWA@J0mYmzM{Gn{8WuSpZx4lc?UT2f8=c+&HxNT!yC-m=acU*2ny<1u8iq@mwv zT27ybT24}HxJ^!GaeYw)%W%ivh_>OcbEfws*qtegHqp>i%|XA-(4}*DO!fsOZwFdR zm1LBpsXqJFoG**@F6!8-Yoj#8@;T79nO7+X;Up8+trplR8gyisCzL>WNj|NVtpNAX zzSAV0pN~+Se9TqfV}Oz_0nq+5J2luhSWTRgZWN!1Dgwg&DH&5FCpy?oZ$w&9r^T=N zdI}U(C?%vwuQ9AV_5R5=VR zJswx5Ti7-(5VbOH{|dlK%ku zXzlagQGLx`E12a|#ydsAp}U{&w{aat*w`K^0_%`V!odzHKUzGeyB)yH&&KeKG+y1q zixUyr&JQ;phttOR#w7+k7>xj=r4E#QZ;%bvqB;OdA07C8S~T>3o_%EgtZSb66!t8_ z>lM(UneQJhVQP9N4+{*g+6-ev05q_<(z*!o3SOlL?mvL)4=C>6cKI(G?amq;8QK1S zk%NQc_~xky0G|q5lY0DitnabE2t~>N07SC;`u#r>px`5R z*2H3$_-FhV`2Gj&SNft&z1sM`@4zAsOXMjob zDzDC;;^jY(e4g$~&bgD8e-_Vpwl(We;}wXdgueu_(EO;kEqj!uom?dN-p5|G_FJVK z61rI(7u-LPn;x3br0`A;GQroqhwP7NGdT9S?|*yWis3v(vVL2HLwHH#GlEn?foUW7 zDK^lXlkBgZ{{WHxf6n`lSB=+{t?lIh080M=pZj+`qvAgq<5X4K7A$=ZitmSXzo$f= z$MWtS*{<4TFx}S2h|Ta$TH`W3SxOG4)?AlXwGEpuqO~WbabL&(02TS2T#voUTBTF; zUZ2=s{I{9l=da_eu`l-gPnad+Vr|!`JxzTZZEFXP>q)sErSJZ?`{_Vtxj!rVujTMJ zWB9>d54H`h2~c++@BaE)Z-4TWW|Dl$tK+}t&y{=IFww8BKQzDnmHpj+wS5l%0G9s% z@;*6Kzg+si=vF`daUa%Gy!*R6Pw790Wj#(C+0RcS&qFQ}i9D8L2hwRuL9&Eq7jh5k zSn{t#{sZ`WuBmO>uWmG<@` z9A}MkFPdUrLnz8yZpv|{o7zsgT$&9^OAVL2*E6l+;|Ra)KRw_5aL;}h9CknH;}?+i z3ezi<^4LLF)g=QyGmvq% zP)d!u+rqxLvKU!?jyv9LV(%wy+bxNSeFH@)3rf-mHX`Z1wYb)eT{%O)LRZ5$r*V6s zpNYl$ZNYOK`;kmg4B;@i>3Jz65&4QDA$w9hRI35!RlJuusiPMwq@t3~U-s+$7gJPe z+p+o(4?U0M99uJ)gjKGd#4oBdk65*o zBV^@N+Go%opug$E-Y>8DJgv^%^L{Ja+&2z&xVaqPC7B_z{I{A*$!a`!sVi98p}AXt z7q+KL;l4@ygHIp2wcJ~6XZ`4E?NtqBYs`~=onQE5;;v8Io?C$LA1Ulmj^1CLah^#p zimEgh67v(XadHaB50!CyLfds?;8C`RTUTYV@(QWY`ul?I#fLSA6g|I6NPi&Wnp->`r9lWx}5^r99J8*)iD&DhVR!Qh+*pX7~G5di!3#%>lcDpk%T8nD5PSAX{{Yng z0N#F(@wyn(hCYk^#51>8*5Y^GR86aOtb0}s?DZ5~O2UFjIuK1qXDp0c z^!G|zfj0113Ksk8+5SIop1MAI`8|Fcl3$5idaALp8^20?>h=INK+3=6m^M=BsQIk3 zr#q|0dkx3%otWet2e{1gLCW37x7^Mk+TL>|#}K8qiBCGB2sR2)N;>I%MR1xM8~*@r zWUN?{&yV7IMRRcAm8xZZtf@(UgPE zT&XteS4kYODo;gym9^Z**t$NM=cgau-W1}MWIVUpR<^&W$+zsJdHVdQ8{%h+K|fYG zO!pW1gXWO&8Cf{Ub3RT|A|b*MORa1tZLO}#*9#m{khdu{?=rB*_XC@<*7R}*h&m}` zAc8zExhAb-vH}d(Biue~A|s5P@2c98N2X)O(*10GJL~VDV2|8&55mplavVbuk`ff` zmK3=81s~pQ{3P3@#i;e{N+5P?QUb3lkY1(pr%h{aQ~H7MfXP$lIs#Vmh#ogJC|>i_ zii$&LA9*^Jl7H_Sjgxsc`wa`LvAOrq@&r*b&{1vSC(QdQEqzJGsW;Q9Xz(t~_b0gu zY1}p$So|{71DV;on2rJJQ@CzjzrsG+rQ|v7ZGXj`ZxQiLy_eey@jJt|KS%wu<5F?? zZg92p+?xdpR40+Q8i!JE{zyL<2lww>2PyJBo0Cxdsh*|5{5MZq4u6_Q&{$cR=xwH0 zLL)N518lI`0vm0#iv*=9&;UFIa8u*0a`I=R)3a0-5OzLNGp&@8cJ%4{YA%_ezjqcp zZ~1$OKN<=B@<;5W$H?2R80rU@s4>;6K|q}fQ`hdPxlN0@>s^e;b%Xx^w``-{E&FON zhup|m5DqB{{xg6P;d;|SVJw8>N@WE%T2u+tgZ(Wgsr(dkL;@Yhn5qdeq>WK*lggxEg+41yF3;BI1hDCa^HgxGtuE*H3LyS+vV~ zA(CAWkH;V%O(X28i*}^TDA5#1WAXU=tWcV*_oO@R#C9<3pzZYTJo-|rVoi)rv^xGs zmHtM^HLtip{6Z~Y-?D54ucZ_2)(HD6=t!8vK9G%0<#>42MYpVsLcgeaym?QjcFIpv z)~5?oH0nX<8D%Le5B_{F?nK>SkwHNcfGp$?6lXyiF z$mb*0a0OhG(z|XxhLOr`G8XnsG!4$n4?eW>p}#{N$$F7&%;zRHpxe6wLAdj{tgl-P zUU!t1W;@DLp>G8wbojJ2d#mD+Q*&*VwnGPueSV_i-C+DcC|Hke>!7P@coVZ$ImpqJ z#f;jSg&nxd{VK4zrCjFN)l|VOkUYjxMm=u&msw5z4K)|59c^DOxe(ae@nvg~!_YUuh{P$XfpZZ5v*>L_=cbE`@KIg45|KS^&4@ zAXwGyTP)|cXe+t6hJhe9vab*YF*um%cSUC3BCQ)6>W>N7>2cNbwhV})*;la zw~f_(3K9*xJ@iQOmPO&bie1N5CD8%g?fiH4w!vQKJQka6zm=-ZbJ2`?l@j>-n;R>% z*<~wo9VM~(zAl6Bss8||>T9Y4FNods7iYEaFiCX{yhn=AeEo*C`}`R?el=u6&2aZ( zpoqALu8;=x6km0zS@IRh{-#;Ojk2}UC~`5<5Rip&=!jVALMU#^{{ZS_BSpXTpvw?v zxXB+Z+%#Ivr3ttvz zRN7^Sw)9nizLXemLQkS0_BSQm4?P*k?JaWHKA7lJsXt(=DlCNLD|9V{dX-K?H#N5$ zOH4S)B!E;&SGTQoI@%CxCnvkVVt65QGx=tmB=0G6%v^pIXI_k1Lw@a<20zG7%efdh z;QZQAqo4*{gI{ zqJ4nSqU?u8iwRLponpq1u=I5)4#g@d{H_aIT4FZ23R|z>HY!_*)E{*gGr|;;Vhd;< zqV*Rh#Zqh@>|_yRrTl2}mIyVGtC;m&A+x&uD|Oq4)D4=3@$quhKye|^7yH=;@u3DFl6KE9-<(Iy1nT0_tBrLP zEE>dpM#{$HmXcbN>8tdBM@o`S8g2l$qpgK2Yk_GRTpBE-q`M2c@VlOE^`NCd8;+)g z>qrZ+k_b7;k;+BB6W0AHX9hbPG2E3!u(_c0CKi0 zd;a~)+V?M~{_pov>q4xO{m#KBJCTuPBh-QOH~kX@?StCw^%S}?f{ zSxB(mXaf~@OcPDK>)gg)Ekp(db`&_D|K2R z`A80^xPrpK@*T%wp9TGF`FPrWEWN20vGFxmv&l}+QsSOE74|2n$4rLWeqjwX-&Cgeu=^^z&QG}PD$isJ zb;(Wwhz+N4)74$T9?|TnsX=!GC{NHD{{RroWVRfJf6{G6>S2IVKt31iO#a~o*#P~% z$#-L@ox9?3lpUDTvQGY`KBYJ=HRw$xX8p726fsvDRC=-ASCnwh9L1b^JRp((0JUW) zQ-hM}yKy8~n7*cbcFThYMR9jFoa$IHI6%{E`833DF$pBU5UvEC>Pma-Q(RYzw++2Ra_20q zdiaLXp!@5j*ZAuH05j}!yFVwJc#0|fw{W|Uo^fV(BYmjuXky0!hVA9>YxvdGdK9SO zqqCE~Qp;n!A3Wq(Qj|9(Z?d1w9q-~K`P{2t#-X>yRU{IgR@l6WMc)6p5% zLSx_(u%r1B6jCY5r-qrFKQ0zKxjt%$rB;t1}dp z87h(iB#%{Bx%+xmOu0vr(i~UfN+5RQ<-iT8452^cQi-LfxTgCDTw_n83&_MLkHvaS ztk27^fz?|=2A|@s(}~F>jbBu%UQLu2An$f781kZH9T`VkBo&0ID8K#U1?#G(fvv2z zj$3P-wlcy@t)=%rm}gW`0V`!IzSJ<&gr8@_dSPnoL%Tx1d9`Ecn4C^2++IrReLN(| z1@r#^xR9lx0{;O0RYHS*C#_dug~jdK`j;0L`fe#Wjg-vZhJJFdekH?}Rov$GY^zFlKHs3-<}q=1BsL0$o0HJc zmduRyQ5=^q7}fs(h?F1kStNb*{ZzRMS$t3p%!U$4P%93TZK)SOvaCMcz*ZJ19{ETL z>Y&0DH0nCijmfaSgBc&n;`~aImjLLLf=Q<)dZ}l0N!P@-oj(gHqxa7J7%^ zajbk{w8d#@zr>P|e)0t=euD8J?DWA{}S-s61>olP{Q z(6gaNg=UjE4HpmqzQL&W>0ITvSl>o;^y|ab5+@tSDFL{MLuSKJk$S6~rr%atsw>At zX$KWG$2z1m4vH!O{nR_(h)QFUdye9*yyHqT3W3z7_o_Y3l)Vd$U2K{?L$LdGbojJz%#2AY}n7xP=+LVoJgXDIsl6?-xQm91hZZ+H( zoM#Y(q@_PD*$u5)D3pyo!nhl_OD(f9ES}_J9)~z{?(*^+oXJ7_kNR7mO{5zDli_jY zUV+2>W~CJI)TP48q%-nIcsZtbzuho4=<6R_pNT<;iGvxBRm5x)DHlnv&$SG?V z+Q#Ba$ndLrxTq!hipzH--#~^JnBe5Z`8=aCqN;Eqxd#4;7V0Qh>v&Hw2@AZJ=6E5f z>&rRG(41fDH<@A<%a|#L9~xGU zMMjDEO@~gN%HcU5tvhM;L(Ug5tbE+7FSjvGq$Ww+u%m4OI_rA# z4rky|tv?f^t75zT4wP5arxS;pa|6h+?Z|pEk{2Z*DgdYhYY=)@4`;`AD_KmkvyXWE zj|GJzly={lz-~h@)HS$yq!yh>9y^n*s8w5aea%y^v0Li$iLV#TPRAt-#mkJYC1?0? zq#xp-TR;_m?5N#mk^{fSRcgyvm(>>xSEI^~97n+67c7&l{B214EhcO{JV3i)l? z-%h1Z%X>tu9?Z5HXY)8Nn~3K{G(EzuDCDrZ^)L9Jl6&%Sn#7S`v5kDZrL3$a7u)%V zPjIK_yIb`-IZa?H*Ud&LU&PtAmlnADYCh#)l#u<5?A?Svj&aftzm$N_Yi?+ucn*S>YbNH@MruPX5*+E_byTC|h!Q~}lvA&=hZrpPBITmUMsN~)-T#vU!GNi3nC-+9Q z#dSr#;2UHHUvMK#D`{AX@e6t{_$p4lbUIbOzJ_|_M#~aJYa`7$Mm6C-2QF0TN)~Sz zrWd>$TdB2LYw1_nh)(&g#;|!mJt3vGxmiDuxd&bbO%WC6s(3`rH1B9hvVYUn|dkn{sUSW1Z!9Nu9^qid^E^ zDGEE5wMS^Bahy@=rid3eP&M?g@&5q+Ky2v6RlENHb_>_lFTwpk2mP2lKjuHe8hgpv ztBUM?nCRc)zv8g|xj!`h`1()!iuzp3y^HThZf0?~C|OKST*xypa?%yFDY=Y|sS5B* zp$hg_)&Bq-`Og{4!AoAm+w|=(?*9OmPnf<({Cml9vTEAi7pm%tQKclM1d>9!9=_V^ zALi_FGOq*s-#~xwCZi}|k-Lgl!jz$JtPm3&w3WEEk`ja=Db-3*AS4Y*APSY%#;AEd zi3iuBS4~BCJO2RcLDv42dn3pGjWIIYbGe+w>`yoPxtha>akg@zY^dH2U6`7Ou2H2fB04E;S{_Ppc2s?S5wIqj(EYOQM8@@aW@=<)vF@;TSD zedz6{Z?So}TNKL^c^*%K3F~DqHa-DWKub_t! z@*1hH#!yVJJAuu3w=3jMKOBF7W`9|~ zskL|e>tmY1!emVD71{JBj9@k>`YV$gYqQ-z>G3>53G+pApTYkC68jD!Gm^xo)V=vO z_ls38L*>kDm6A7kY12yfNo$;>Hx1m$Ap+i@xagbFR<1&=tPSEErbJl zM`g#{+B7E3r9=VZYL}DV)zyw}l8^OxF8JL|-KYNmX*c}?EXR`(duKGJpXllq3RDR2 zAnHEat*c_IqR{Z7i1=HhzlYxCFRnp=M)=X{@8SuDT7e`jQu z_!nHq{{WQTm)kl1#s2^^@Bz;`S1ImiHRT-VlH^_F9E+7?B;<2hRTVg2n>oV!#1cuc zQmm2S03H?kO~mQzar&^SVk#*fearjXHn|~;UIEFbfwhL?CB*JSkGPi8W9>B3`7W|} zi^*I274lhq1|x;#7@nAN+-NKUxdjW{5YkSnBzRV~`6VjzA5n@pw;8Oy$4c?;HOf2C zuHSG>bbtQ<$Njuoy%p?D)VT`Yz2s`LwREp8<#|Z)I@ez({{V7%XQgo(65hHI$K{Fp zk^Y_ClE-!n>En;WR6mo;=MtIuK%|5$dX0}wvv1u#A5nopUG$fbxu#Wu`hlC>!aHtXOtu6fkwYmz+&Pg7e@N-&!A$!p*sW?K02 zIRyq6`L)^-fz+RHrz_Nynvbh^IWAvwS+DzX>W{&9^$qXi{{T^+SN-np0}{;MJrgeW z4~Hn+r8t}sIQI7VS#BZ6J-e&)-}tBE{{Xk}^;XyBYcKnHo+a|$OE>r~(K$1S@C zCBWyhJYF9$$KhnPCQlW}6^Jv#N(=NK^!4=CMU)3orFs`9!o8|pac(*FM=e(-tCmYQ zzMoQDm%lup{mqr_pA_xuO-61UJ~_s5zas^h?0^ss~!(Q2sduK{{Y_p%{e*F(|02&+c)xxL7zJhBjn_{>Hbh%K;2<}m$fy@d=rn{ znwG?TuqRi@_9$43=vTS^OUXGtQzf0`S!o&E?rHaBW;1f$T!~WTzP;``>#ki(T8X(N z>s>CV5lt{qg8Znb*yZf9d40hQ=lLEvqRuyvPsKE)1+5V#%7_%C^e5$Ri6yu%HN4 z?|QPcgV(mNZc$DjK_A^M_dK(PPkE=*H-7txh-U@d9`^G7;b0h)XGg&2c_w2mk7{nu zPuFfV`3|&)Hs}ll=ffqhpIRCn5;Q+}tEa3zud_CgmqQr7hQs!Dr$Gn_iz>d{XEyK2mMH2uCT(@+fGJ z#j?wIxBQ|?R#d0LN9VXUwQ-!!#&<8>uIjAuj61KX)$yMx)~ix=*$m2EpU@NA_~SF& z4g-wa>IvD*D@thopzcwI%6xi)X`h*|YmBx}{)vD3tY6_v-sNjwNh9`mjpZ_U{Amqi z+4Y-Cev{4-w6>HFhQuEWSLHs&g0{ye>d(??+u>46=u^Fp%5oWf`s5jWJNVY*?Nf4B zlsjXBtxB$Ag#A1a9!%Yxq{G+o|?sWvgiqKlMJ!W*vo?+0)YclThK;2zMPlP9Z&Phg$~z>w{iZ^4h$1bM-y1;Tsv%NaJs+2!G2vv6Quz ziOjJ0Sn(x0PwcNv_>I}sN0I#U9y`zRIru$d0k*Z(*DoY-P58c|Pdc5V>f8plq0SLE z898<|5`9IHy7sq??MwKmgi@OCk;kk(pM4L=bY$YRD!18ML}%EU_QNEcUvaozLi>FT zgnRpzt%vr~yYX(O8$wvI7O(XH#eAQ7@L6OFBavZZttR)nm~}`x`2KN2`1?)_2P+&{ za>QurRt@1){+JlPBqPr)ey z=p(~kl(xl978=q_(Z&|bvKVnCE;OQ*0tm4N#*YasbpvE@#I`D-f+8<~oyOL;jp%k5 z<1bF7p};#E+-~JM+VFT)!o;aSJDir4F&Qb-RPM1~{t@=o7VkYp9W$17d`H7}c6z_c zzZ1N>cl5K{?g#SO9N_-|ck)KYLTWD#gfmQ2|cEP`YEO)Qj6s@1RcfyN*VQzQn&1f|&VAmg{iaRlXM%EW(29M@92Ow50ShWn@z7ON?UNgY zNitax)`rr7{{VDVP3tJU*Um6Xq02xZ^(U=!+cmrvP?u6?0(FXS`AtylP+HM%Qga33 zK-S*N)ou0SIW3~HPjL8&Q`D6;w3QGx-nl)!uLsbvuAf#|k$5!KhRekbfz*;JmGd8f z$Fjw8I2JLX4Y08C*VK(bwFSSKtJzrqNaak6p)UH1SGKEd(P{P>h1oziBz*C@ z&d_d2y{stktuDrUkJOFkS;fI&*HtHAAn#u9EoxM$Nrb*iF(}N&M;7vy^h}GT)^B9) zDM_w_TUJ;otl5cHa}H9;Z4ENDsz^xnzlcr9_t!NWa~dzITy^pu0yoVkz`+8tQG zOOglMT_@XCjy3LIk>WD^jq8OeMWpmft}HrMuhpzPcXl!nW?YAKeWW>K-$@8%L)1tBdi{Z!JdN%mBJ*CLBz zQS==|ezXc@v6+0aA>{dL7 zl|r7M;YJZ%8kaW`Z(Tm8A2`0}FSp6YT~Z-G&QwmR8T>_gj`m65cVXxrW}hWCdvav zMNN;;t#tNY^a}=Ffpq@>5i0W5p&8_psjkB$ zIq8z6BwalLre^Y1esuGIvl)i{5`@5N;M^>yUn&pn?Y!h_T$=I50n;+>TUOF9`=Zpv zT3D7cAw9+gb0oB$q+i0C_E4wZ%+XU7qQiws9{SR!>P76yntwHL_?>;U-?I^Kxs&>d zCUff4D`_20y4IW`y$wRi3;gt>rn>Yd)~I4XK%VS`1KB;>n_0J8duUOu6H<^ttfp-# z1JVi}L}^&*GtOg^naF{?aV=rtLwc#O@Ys?FMCEeXP*2Ty$p-8F%l_S$5w4?F-~ zQzpa5Q+iAG#Je&0^1oCVM=HX~)mBMAmrI);bu6_lNZ;Q<_YTLMj&ZVBbtd-sNdW!E zlytE1lvq)xp&`FfAvYaJwQt*zn1dUtRw*QxqoK=wYk{xdO!&X39}4|OZ!ZSz(juY1 zXbHV-DI$K|XdUQAZO~(s>C`6GskbBwP+90XMn+q1BnE=o76e;g!h+*~_3Q>&l^K5w zsOnDd%CIf~rR17@!!PJ8s5oqbY?6gv^qXCD8rtM8mttSHQy#2N{8t}f&Ney}k)Ya=N84jYg?CH45}nk+;Vg_S3Otgg=#GVUYaUE=_&E)Z&Oh@jnQq-S*h}E`e1w!>eRNKiob)h)HG}iCd}s zky)`(K1x@GJ4Xv`y~MWpy6Rt)q`5C~xh`$DA~7@hGzA+LnpqAke~Ddw>gsg(nyFn@ zSjZQmk)xKYXd`ZGYcRY4VAb;n@e`ldj^JyTme~7#Nygn{v*X8`DsP> zLHj{?P%%hw8BQ(4Bn{pdrLK$5LAvE1R!ZV!a4{5?+sE7|{;Bwl=s#~Z9c;mVB#E9+ zinuC-f)mj|4SvB(*{)L`&D#ta93dS}nCcDr8qS{8DBnpJMy!T+N-Rt>K zliY`!%1IGoHIHS@31}_XJ;18Mp!BPcc5Ci$YoZ0eh0Em6uP==~mv{lOQl21m*Wm`% zsku`g(lz6B`>c(d#$_V}t-b?}WliXp7TOe{_MK`os<}M*vUR&*+6c>!b7xPX0|vxL z59CaEL16oofkyDx$60hI8@5}*h+-cb?*2<-OM+uEuCg3hX2Aadg*wu&XG*;Kgj##^ z{0kce+J06R)STBdMmjkE06IHOy7Q!8pix!73aH!XX-g1^Z>_*xoXSP9h~d~M1rDxs z%(Kn@N2EBgl=SF;K>RD3w|@D)iw8|l7gcNXGQp2JIz9wPL2WuM#WsOpJjoSa;e6lX zTqmyu=temwcX($FLQ_6sWWThO#fXsd)JKQGp!;gePvhG*da}P`p4YGF%AH?SxMfO8 z+wJ_fSwEIa-DQOo^cP6K?W9w4cKkFE&^od|W`jX(;1f{7M`B;@$T}ugdANhqP zvGE(%V&_}tG{4jsJ;>l$gdLNSC8g>R7qs8N5qd#zcURpGe)~+{L7TarHtH0Z?iRrZ zY6Zy^pSQ>pIb^4-m+2Z@+BT^EQPvJ^E;7gAraPU!7NAU^Ut;%itX*)JcN;NPZXB~_{yXincADH!|ovu%z zX$oT==Zw4zb6|k5Z*#Wy{3&@-<<}(Ru^9BmO%a|#jiA62u3P>1rC=P^+b@x)zs-!br_k9TzU-tsSRMZi!n~AsfRb~avKBDAB>w;o`_O>(mg* z>@hpqU0FkNJnLvvh;2p3K{rt~+40fDqN7IYU6g@xuNXnMS1P~^0R$brP02}bzc+V zujbceeB|yoeKUCnS;Ipq#lpk?017lzBq3=6;>tP_Yu`RO@haVFPp(f6=KfJA&BZ)m zsnN|cOCjkpNm}($1mEwjvfdJ?tR;h&tD3S;EVj5$aPml5X_u7#;sa^wO~tMO@~$tM z;h`9|cDz3&DM=mS+Bah|`g5FyQvPNud*HN%5PfBSTi>mCU&u7SH$&KXjmLyp%x{~O z{%GXIy(w$;6tZrjuC>VBty@@V>h<9oVhHFOYw1@?^yrV~<* z4qkGynROaZ!#M0WW3cj&7L#pnNk(3Q^?4N=6r_q`bZ%9MaG}g&bD=kI9Yw;32gDj< z%hLyWueGmo=*iRS`wUxMc=}2OQo;*GfR^Lsl5XSI6H$H-CRi3s(O30@w#Vi~+eXXY^XA>Uhhw$d6Z3Kr|2s~RTIy|Ksz*OFZdhnjJf6zIyC+>$=wPA!hO zG1r0_k>p%*GMsZKo0Q>f5a*=ph!*yX)s!HtU=nMoT}|%?4}hcc<>^hJkXGqZhSBBM z%B)uun(2T~ZO6Ddp77xEODcr>O#~aMdqFAp?jAJ>aBUm?!8R6Wx$MYV7anLSMym8m zRMv;YCtZEiC!ylHnvU9Upr0Yj;dw?5ek+yn=$wa+Wg94+225njjNn+?wPGRaZsE#pI8Zo$3CP{{Tll&N6d-zJDp$PE99z)%=ldOT)PCsy?h4fy5vZ zbu9#&c-OalTlwRf(_S~FY=0jA0N>yK2a^1Y_-BdTZgu9lzo+;1eLv!Sk?%+Jo%*Zo zql?LK2oU@8#m}gyvpDj@86=?}`p{=Moo&}+huvR6{A>Bsh}^_({{Xcw$G7+%6Y@Xd z9!pa)rTdZp03Y`L;yNUI2-o7BFIKR^*#6aNuCo12du1nonO`qi$rLy`7 z^q`aTl2v23YiFP$yH0nKuaJx>*Opa_EvMQ(ull?-j{Vj;oOczWe;X-Tu@@GW7FVn7 zkHhQTemxn)T&I@uKHzfROU-$Pao$PF`E2<)E=|ZD(aDqMI$d?bJO#~=k)l!pMu1mC zucZnrP>RH2u$TLPkK$5oY`d&;msb1#0JmTE=rIgis3czcb+u&HL3pv#OoSvn=?${j zQr%I$h)BOd_tMu%MmhB__`O+KVv6-Ybsy7;r|X_Ia6 zrrz~(Fp~q1KnYL^3ri)n6ra|m9;UxIzmWO6K3Vfcs}IdZFU3DjkE>tR<*)2Ml;gPA z=uo{e)#K}hjQ;>dgnmTw8N9AvBaqAHXD=z1%E*3JTOi>jZ%UHtpLxUWTGUCevh;Z9 z(Nct0M6i!2Mdi=?PxRf#_|(kr0Q%Cv_h%~+ zYfC&@HVY$;lP$EqT1$;B_Y|G0NZvH)YnbEtu3pRiyLJ7w^*SBiH;aXSRo&$Nfxg0h zT<5-;Vsb|NXNJY(B=Y36-fJP1hKY`$ECLcEsaDty<9|Blet-NwtMWVJW%HDLJ0x9v zPx&f*L>y~Lya8{$(qg@`z9%!TPm$X98pD4>^jZA|K9bxoav!VCS&Bn(9!t1R&LL?fPF(EBsX`%&X*eN#?f*U^7s>;1ULL)th^M@E8gy!!tDXZwG|<-hCu_1hn~ zJ=?^6C2%aS%6vtRevV;~^50>@$roD^*03E-JBcrAFmM_m@ z>HR*x+uQpYzp7R8}^O+t+^RbfC ztvYUokkj|uv`06 zX*irF0#|@khNV9If}&R9*1*~afB>57J{$aKxa#~ZpSb?N{Z3=#{{TFd^Zx+0u#SC& z-x=^Gr)0QQv|NsTc))1D(J*i`hp*pl2x^O3QoOu%RWk=*|P7E3(kYwyl54pHEQ zGKlI@R~sj89DCrL^mIm zvZs<`&0&NFkcF}m5Rej(E`)=pS{*tSFF+IIt@@Sk3c*9=oSPZOvRuCvz@lVJpE1jG zZ>lyf7-^+AOU@)HjoWslc&LF{w{xjwEQQia*7EQ*egp(gmku6oHc6Pf_GFqT(0wR2 z3T&tXLb|8fTc&wgXf*Ji4yO8D$|b41ml{@7iEz@U+fBmOQ00xj`rv+Nt(K&{4@vkY z))RqJeO+Tie4meGl-X%=;uI?X0Q}8Y7yAugd@{__gPs0Mp+@y1l7)BGo@F^?nsM$& z#qhX%q)Geq##5Xk#shmW{VtO#I;gFWo3R2Lwm$Ue{Z%_if zuhdsh?UVx)3l!;UKaLF;aW67{VV6q}GNi4SllhXQoxuJSwUHWx%X&mZaX=G}#~@d8$pHkjeC|Waza18RD~Suypqk?QdpyDMYyV<#L>~eN&Lgp(-23 zmrGXKKFX%P&ApEA2x0L0n8?^DeA! z_qj=aUWnbS)=oy%87V%&)|sBHQs{_7a(V6LZ3gCT_#%9wJZmk z$;o^IWT~@#6Ro;Z7Hf?|WHAnFs#p4l_n>$YrG-{q1^uO_y^>^sIhYy`lSxn-ugau2wa`byv9*YK)c&B!EL)2i}a zTyKxfWlgw{q=RC0HN$dyQF7>7(Mv_1g+9|>n}|}1Kml6SB%5WFp@4eSg%iBo`$aoU zutA=S8p{PK@wqfyv1u$ae~gm*NCCFNBd1%^e%fSU*y|#^hE*v?q@WwM(vhaL>}-rk z=O}-N;F&3kL#k_QNA(+zeujl_C4g!uyzpFd9ws<-)Fp2KHMdHwdkws^X=RwoFK!ladGI-0kmQL+xwzRU6uf_(?9q2zzMuIl6S+(PS~N}G~$ zqh%LX7PSMfyX~v~JCDW$F4}9 zf`xw(7P?@bw>4%-ZX>z4{Dg!<4lORcLKER~bRBDs-{F0zxgBnYFsx}lzB^s z$0v(nSpeX>aZsb`0q#CN%I38>oqFs_UCZs>9@)b&IM0%vS%+GpWgVSY`ki~ zlLqKdO^N6_);=;b?7+vDoO`1Ub2!q*kE%MnP*ZQ)S^Hy4>J~iAS}?rhuI@`R6J-Nq z#y z;wxoM+`Xw0c^tzr9nIdWiC2*~7WY+zI7+fq+438a8+$72^8R&scbJgot7Kdt)wDS1 z!E{NLmhy75)rO+;tkf&AnWjr0xTO*Z>DHCMiK^dB4At@*>uau1VZL`2{{V(gDJXek zRe~&SNK@u^F!So}Uw7V#uJYm?#LaV;td-0p!%7b!nwLGZRgW=yk(H%*-q3yeIu zWiLkc7RvtsBYR1=wDqOqd?5O^4_o zLlYUw9NGNAOg2WOp#?tuZfRL=RG7sYSFYhF7?RNipjU6rZ#o>3WaqD9>3RtY|k~b$Ck|9N0jrSQn5EP-~PDW71Fjn17iRU6_LzvX=OU~qN zuco3^)54GKDSih2=2iYA2bA(WrbU+_wT(k8FGAo#y+MB?R$Nw$AulYP{{St>+iZMs zf}@ciBG9*}>aW)6RdzA5wz?dws<8pw$H^mXl&pjLkA)T!kE0TtCcOmPf?D{RxFe}L z(XuZj-=Op|^Q0EYK44a!5u}*6%4E`a1zkP>R3_ttajwjc8#9|Wc>F#ZP<`A6lqJ}G zb@lZP{HqP&jxMv({{VGp%mgDT*43%|2-33#)y#Yti7v@~A)>C105tn4e6`eOt0OLq zX)grh2@ar_8akyz%ABWVNnld#Jat5U=#~Ut=gV4hsbQ5@#pE9Bbf)CDMV5LCG%;F~ z%7Rkew|;7C3mrv7&PAq@khVv{PPP;k+?t<3H00rh0vos3Ygjk#kI|KeELNn1j|2Vm zD8;sI-yC!l9D^D^P%1r06yv5wmC2#%Gf^G4r0Q?`y(wo@#(nsjoVo{qKJ9B>)Xe*Y zXZfQp5B;dWfv7^6H`Ls=mO3FM<(f*sTrgAPtwZhE54at*oW-@Ov73}S8~*^xqV>%C zfnmt;>iC2w7+C3LX@6~E)g-o%<&IggN8vwxYFdPr$o)Kq8}|-esQy(0)9k3aFoG+x z%}Mo15$0H*1sc`~dE{oMveDF5)RST2l^Vqe{RlTT*_*M563I8h**>y(B`~`?#Nnw9 z$ZzIdY~4G&KKtM8s`>MK7Ica%E8ysdSu#pmz~RTsMQjHXq8McbYSY+|b*NVhWd8s$ zH|;~DLDD5=WhqU-;xc-&Yjd~Va`k48iHH3nsu1s&7d9m>=#e6sa0K3wYvufG)4fd5(A(Q4X zu*@weN*hWv^xCqG$+tvnQ`Y0BFVqIz*e=L|&+Z0Qe=p_gtK6@;lK$Nu2IjaySq5Gw5Zi9V!lzB4XJ31<^2@^5vi z3LOWDG{fJjD;XA#A7uRkG5d9&c%+PKSO;I?ta$m-lH_6aCmtSSw>xu|LX)*ktIugV z9}!YqiW&W$UZ825CR-s!Zb7y}d@gCYax#69U1u2>_hfy{UJ5x#e=ogS-3hS2hg(yM z$+g%^*sDFpUXCTrsEG~9OY;Eh_faczOQ>4q4yg)`JnW?1xJiyW-DHbUeaDb?!#;v1 zi2yM`&cDED@12tf^J%a=yr_#|iv5mRAjELEnI#pn+s?#p!A~hAuzkeR+dQLPJxBF; zgxNTcisD>(DO2i)8DsMY)Rz`X)1ft2H+|i+g60sSmDdBveDen?LZV`VWvVw+A+m&h z_5<#$mltCFK$k6EaIh4U$+GegP{z!9PeqHZR{OxER-6;d8UocFl{Z%^}qlV=z(GFuv$w{3?y-`HsJCdC#^^KcI)6;D!zlOC} zWF-3!MyRi#aksEB(mrKB#0KhH#UUDd7HeJ>zS$U!)Fp-?E4TpoKnK74D-INi8JKZY zoBsd}yDfI|0lU_}W!bapZS!d74iqjz$J_v;Rl{r6^4N`>*2I<)Aai?#+}!fi7m=RZ zsvrQ{DP>) zX!t(H%<2&2CXf#?XVW5n9_$q2j)V?keMsneuQd&3KzE~N6QFNjIK5G z{3XW`;*_L%JvBDT3?>zAyY_+kk6KdLvc`miv5^Q7^AeI0p8`c!H6qnI z{Yg6#a1g>4xhq?p_`s+R8%hrqqLTV{WuR*kr=4VJ?AK z{H=M$2cYWXA!7DA^!8~%hYtwLsv4IeDX!#eDElp|({mu(t{#98PPJo3qg{}V()zGk zc}j52sc{|J>w5ab!qnY-C%oKc>MZ&A>nyJ5h4R0xe&uoeaKiD}ky=b-DWg5X>bS6^ zl$BWN<3!>=g|yzsy(ZiG2y%bR8y?@bqK*omoO?MsR!bz}RWfUec1wT(bu8{W0byR< ztEU}$)MMEmFSWGov&p{V%skVIODS1VIs!atZHm?gwXDt#kL`O9BTb9^6-b)D4uK#J zy4L~nJC}n;q&?dmpPoaO#|gwzSJQi*Bw6E^KMU?@pwm7 zs|32UqHukpnDSd@X;V%$V26RZu3Kl6$jr8(1(wU>3&B&1VJ)roJG6XCS5LaB+}n9& zW&HknMG{nT*sbl_p>fcUI-0Q+(&lVrmKMvY^@<@BDVTIB#Zd_Yf8)lh3+q)7Ec~&N+l@?J|e=2E92-{Q_IO9L)>mMw<*TP z?=S~S(z&w2zGiE&syOsi^kF9bZUtw(KdHHL)B24b<4_4cq(s>D78=q0>jJ}+pQ8iG zB*qa=q5qPz2Z+824Lp(4o;k@GD+@3kt6CBeG@WBPnIaH>eFX z)9|R*M?jN~Ku>ZRO&{W+D%<&z0jCzEU#YJifR{Ofib5QYLcECn>(lhEnCt=J%9pxSHstDox#Ie zI-yAd*88Zj<*q{SPA&C4k?em3lO10#go@;0s#^_!Nj-XNtxa<`$JE=>t@;45U7g_E zb=Kd;@pfZQM@1wZy6MyV=&zZ!YpXW2>s^p(JZBHR)}=r#vi7bZHk2fvW|bQG2Fo%y4Dn<4Bz6 zA!=os$FUib>g20%0?nWjK|0mH8GZZL#)vMm=v4DH!&hEb$>AgMas5F4sIebb9pS=b zF0JK!733expE6u82X*P^-CSw}M++9&%ZXdcW*HP~ZJ=Uw89de}VlveIj<_3oc$akh=FJ=CL3}dG;e> zIQwXajgG%TD%e!|!qN?{8#VH${xk4Y;@$h?yz_;YvzPw>3@81q9+UGOt6fSEPcqM= z?7tjiyX@X^+p|9_4d8ir>%{V^{{SEI*|z)rI{M{uy5IFL?>vHBKFj|ABNB6u^2L=8 z_O?Yg>I?~E_P0!a?XCX+R6)KEXa1HU{@mn^C~xC)l6|n`i+}Q!C(LU#`lJ2gKeBDE z(PD!U$N}2s7E@!%QQsPW^BjB{8^mB zNmnD}1(l_YJ`OFNpEboSZ3PXTt%ItZQbnzMa6ihw$QqnC4`#w@Qi|xlvVE8I&wkw# z>zrq__!k3hn`zpy71f`+j^pCF2h%Up8$Gi6OW`o(a{f1mV~uuW4Jp>*<1ucAK3y3J z$~GmYHF;qmscO)5y?$H#Gj2SOH}|}Ul$59CC+pSq@9KR&UX7a(!sx%-r~7yQ)AE0n z{)>I_&1LdAp5u4I7Hg5_rA)|j43cB5*JWg9$W*B>sl^|OPB^lop!Qf;SKN>J?^dX& zQ&)^u5?@4?67=*rK12AP&5JyxF8=_C%>MvHUQXdDk=@V|dMUVV-|yo zVjui15BkR~{{Tbh(gGjvOpSjw+zV+;5xf7X5Q{+d5bJQwJ5v(mVu8TWaACFC>R zoyfU7gf`05iv%*t5TDB1X%+*_lV2x)FZ_z{k@AaY$HhfqCH~&M+3OrP#6HVGQ5V?k zuH^SUd3W*5Vvrm}Whqipy)Aofx1D+RuMux{j+2DrtJaI_)sA@AIAEcaOFH4+Yt-$97FQR7HQfS;zC)TzifsCD zq3eM^x3}oQ&SY)~4kR1+s8dN+1KGPhm@V9kK}sJmoj=4c`)eYrUZ`H#&$0)-J)?U% zHTH^WIQlUc8GRVj`Jy*pc(;|s6V4x0ETcc8GQ5LhUtK*k8--3DKCEtZO#__EAy&J< z5BtK8m6E>5$v&uTth;v6-*C9BgtBLo`ZDSITaoj>Qyq+oh3-?ljwN&<&1ms3V#pcRwSk|L_#ZvAM><+`6yNARtmp$>oPqrW>{TDx&J#M}ir8~*_2Q$OlW zSLz4-A+z)tGxXKplC3*+GRMF<8^he?FaD#|`hx!eOl}fQkf_zY@cT90q?B%7aT_HdG&3fPLqDr@dPRQU{5oe(a0wcc=u}7w_?xFazUhGDSW@j@MpdSK{{W76vY`k|K&P;9;+0atbQ#cGGA@)%}d`u*MJ%le0wQ|FkIRFv*QEhKhpFE$TD!31; zB#d^}O9%>jT6g%S*B0iSBvjBd63Eu}3la4Whemp%|s z{s?J9#HzRK$lKeB2ffInM)-bVDQtd}_?pkMo9-2IRiqV#%rLJ6g}kJbVPy*nsoUf1 z$#<*BRgSEfM9pGe{Had8Zg2M7RVDhiBJ!P@+77dp$DzwMge_L{7OG35Z)^_LWFM(6 zgOZNm0Hqp%;xAOzDM!%gHM|^2bR@F+lA8dlYvFpMy4hzhv2jcb7me}h5|#$vP$>B6 zYUeo~RY732)Z^QIPKM)L3^>|Uw!!`2f9$V6+xZL2M`f+Yb}UGo4gpG32E~ul;av3j zxn!~XKfpf^2@f9UrnCjzd}<<;yLMJ}4_zuDwsLWxyr9zwAvV-|sY|uxKA9MV)LAHn z%UNgu(|-Z$)9|-Ss;!qr`k+~48DZrbPaekf^+HtxLT~2>;=AH_{?ncYblql$L@m~FF zp<0bGKYcgU6_Lh6$wzU-&qHrmls0|dH>(Y2<0K_|@;S4W7xD6)j2sRG=oSuySEKPw zNh6EoCnY9|7m`DIPW7qcH;o$}8drJ7q;i{yzf+aDgcL@7$B`tdM4buNyNy=-dK`xI zaov?euQc0=-yrIK>bQjrpz}g1b(c_7(}8NC*;_-N3HpJYVfR5ok^)6nxiQ$*(J0{f z=D(f9TZq2Zq=j?I%(x!ICA-ox%EBw ziLx_>8X~>r>^~LmmNB<7Z>kPY92BV2Bkc-4ARl2hZ@_%A{b^zL6z6_9M$|iXK5O#6 z(`0zmr{lWWaj426mzQI|T@A;wy~m091a=^vdY&Q5{Bm?n=dn!WxYMnY@IzZc_>!dp zZS1S+IX}&VoV!6JSD9QMI2Robw%m;K+nf+F`Prq_<%yc z16>7GDSvIU_Xdga?pO=V4*{Yd>m_e3|>SzJo~&&YsYA2=$53p(+<1@kG{- zgZ#q#FU@oz^H-CI?cIjz!aqp_{xx%4>wS)X{Y$%`Kl6mP;4-77z1S8={UH%3(wTLa=kP;?{QE1lT*+M7B`eopI- z5P81(Kt+tU41}XXl%Ywm9ycP8pTxe5ey^F$E~wf!d@<4#rK23;*axCdm%h|j@h_t? zKQ~`N;yas;$t@oshk#N3BWh@i6YZ^8!Fc+HujI0yWGgR}hf%d`?N^G&N+lWs`y89d zC;94jl%X5b=T7~<;3D^#gk+Yx`YmrBIulU)aG!INSrZWIcD&BTcqERsIQb(^QGLkt z*Q07(0VdW++9|rA*%5OCC-y5Lzna#LR#TiB%?KnB`U^5lG&XmL$-2sML}QWUbQkISe0t50S>P?FJ_ z9VZgoMJ7o*b<~U2zUCx;(fvEq6fl&?qvsyQ}^jf%CX5Qc|^`d*_w zh913WN#J&IY$WP|dugSGL*J1%p$rTa4l7P<3bOn<(+c)hPgXHwgf_Gi(Ir888~#yS zQ}sk6JPqXW;`?Rjss{a2){729#q|YlhnmVFvN2LyDhgnN4_#{FoycQ3DJ|2UK#yWy zB}9U6_t6&85wl)M5dI|VxfI-Q$QaRpL-@B652lWt2Dhw*$Q_!p0KFDM2}-Z0QDaTU z_+(O5P9t5ZYfEZ<^mIqeNiL{gYL1k?4SS-kkv&O+m*hui8 z$~ZS0a)R+5ZQBJbf_mSrN6_427Hzoa;R@7w(l#-Oj?;#IN5tAj+kWt^d&vj;cQ$Bv z{H~=XBiU1XkZ2d#OxyV!r2ar|JPD!u@L_)5X^O56L5*%rx@v#2pxS+mvNMR;$+O!U zr9db1^{ua=7i++X%gnc?rb58!ey0BVDvMDS_ptIJ`_bMC4o6c9{Ui(6{gt(8IasE; zbQ>a8Qyiqoi0Y8nP01u{?G#4{Pq?*q+gr*lPAA=5oF%gwb)>SBV(#FT0ru5y=MJX2 z7W$m*qW=If)4Xq!<8w@<=#o(4m204>JGCBF$L(p(x78hnw$*knjG7#3Bp*e3aof7qZxe$FEZ=R{{X1Q#m9^MAS>1& zeV|lT`1Cg|ew|o3=kSoloF(TG)hQMpeG^5EH(AvI_i3-VIqq8xkXwx%nCeI+E4v{u|>9>Xw;~&J;d4#FLB0loX zu6;#WB8M$+#9{2E=pyU9f$*P|n;n#|^jN81c{Qa(Bh(k%PF;brFgdwkCn)Pu8&~E# z)R1rN6vx~~K7DN6fWqio4vl^gTu^%S1x<9iRxT(`qOJ>DRe^e z($98yaU3Q!r8^-ll6BnSDmABk<=E78D^IA%&d0_NvBw{ep)n!Fr6wn_r;vE(;*S%dw%dmp}N!*L6eO4uYdCipj@P zz^mL-kmTC4+@09XtE&;fX+MUvq_=6$Y=(}7jee{HRoiHd5T_EC-TiMZTaEnnqpnS* z(F2zXpII|W99^~$+i85pDg3TB3J#th4XaCXZ@3@ZBA9{1%T2E$OM}AXs2{?CY@;s2 z5!IOKj0D*RCX59V9aR!7<|O4+|B*HeDyB+XkbZbwsFM*23KBkh-7YYRE^6ZhPiM=Ie^_c3* zN_64$EX-#ah(c}}ilJlaHlI|Fw!l?Yx6S&Nl<3FkOWDHX*g-DGOTd1mgeKp02hQ1g zjx9P+Hg6&0cyy?O({3i?(L#fY`vAQjol7O%gO=5B>_O-ED-_qLq~m42hQ^Ca1cT>b zZB`$*N%YBEXCWB+5ta92jWkN8;bpRoN36EMA9d-U+h*5BEgn`G>k?Is=G=C3X;v}B zzPAO^k$>*0%e5R|R#{WG8TG*-XD1ourI#OdfO;t?HuqeMQonA#rJXxpfPOTD*#p&6 z!5Rk?EKij!WoT>N2&QQu1iGR7*S!u=$%;7?^~%04R^Wb`hqf5iD|OO#xPU#R3XitN z6q6h_eH8@&P;>|O5JoW{{WC)-?F#u z<%Rcfg%F4?9buKLz^Gi??V(v2CDD_Giy?*`W+DCqNd)!KUXzoYyM~0Y8I6)~kf73< z{nBY$Xu)qy$%h(!#Q=pYr6;9V&b*Ij9fys}j>#tb)#u^mLwam7wF`XoHpuZ%@g}p| zkuPxze>sAov8PH&@bUXAPN^IB5SPxoxdDgH?`iR^dr#h;O^ad^Kq^9z<0H(0K$NBB(GF8w9D15%a{Ox?qVgd(CC2{%Dnh(PGq0;;-{1oM zitAbW72aE(PnNJ+bL1HU$zi{S-*sMT@K9Y}Qj24on|%O{ayKL0^5Ii}btdQCS3kBv zyd)s`J29MqbDY{-xa*C(V{01N*q?nhtHi<&A{%^!tVIo;b;sT-bD+~+mKD(J#|Y*2 ztjvfL<)plJxpUJ_lxJ5#4=l3F?bZrTb1K9p8dI)3w522hdYgFF56Cy8MGc)t$228< z$opy7&!J95$?=e~txJy@4#cS$5WBAWZWgzNe0}o&0Oa)>d{+VIDYm4-+F4+0~({HD9w4;lrI7`r;Y&O4X2!ZAl$@TGBlLB|R(UV`9ko>1~AVzgfy(KL)o~tSrI#rZ7Tj$vg*P%>1u^8RS z$G#9FNefBpphdrJGya~w3oQg%w8@q>4fqFUm&)RWrxR)9FfCSrIn%^_gn1KN;IXCP6Nf`BD4NWjwmC{-nEb*mtC11 z-SPE62lNyBDU`|O;E&44DH0T^xksB@oe-A35%+0KVKHjNmC8|%_>n!;Ayx?iBTM*Sv;$I9ut3TDo#uC?%&V_h?g-dCMYJSQD%El$DPd$6 zGq?^h7$uxivaUDrIF%_0NgrwUQy*zG5o{y#@BM0js6Iy(#=rUvJADlp%&$Cx%+C4p z1{_+X#KTBZlExXWp9)nkX;b8#8y2_32>U1a1Ng!Xn8?eNgp;>=omx?H zFJLR^t$tp1?@wK&`C^`bu0Q5@j@JhYw4;1q>UjRtQ!0K#$%_d_(DCKN38efjwd*T- z!V)wam8z}O>s=rqt?kk)EKQ-q0#!dmw7q7{$~FGAH`u8F^KU|jY~f=8)h<8 z6J@w>sZli7#=r6QjY>VV*$uztwx!W5e?q%!`ib{OGcB~NHVyXDEoI}GB>keUEdCAF zf9IF?g8u;KDX;RU{ema@i}wV4YvMQuZ`D5{f&KJ9{tu)6xBct`{$-#2EBlA-{{T^L zTu3kQ91DlW(GzdBv;P2uYoBSf5q~g0SffWjsGe;_MSZj5PV6d>{3UCvFIQDoie}i zw~1Y%1%Ekj`muul0IH|dnF=E*jYqI!nm@HvU;hAyz9D~<&;E1d{-D>ts&CZLLG-iO z^!ZyJ-TwR0WB&k;oIXE6wfynQF47180RGp$tJqzdY``QM!_xBygX=in(9K zP+g|P{{ZWWT&y{{WF_ z{{W&QN9!vXyYK9e3m#zNnb+{B{{ZoYzy2~t{{Z<0zyAOi5MsWtFlr<#v79iU5}ZRo z{=r!P0F19)t&y+w&42VnXuqp2BH=2N*nS}4@))M;YxvV={{Y6w$MgMf{{R;dd;b7c zd_hCu>@N=k$YPl_V!w{IfBa(={{UZWfB3kAT>7KpE~JnC0Qhh$D5?fhK0UUdXpw87Nz@&B zijDsO0gch|v9^aazJB%Pr~Hq&{Xy^jM)UbwR%Eagk#F+B{j|-01JzHk zt)Iv{nC>f&`jzfhDGs78Nxw!(1=e;Aw;m%}VqXc<{{W<}x99tr?m7Pe=$!6B+y4N` z1O*Swy}F;g(Ek9@8ejC9{{Ua_eNE5Q=W({}Gut=u^uYb3)3y8~QF;-7H{JS(fAmWC z52|tvp!lCjB%ij8{)pB33xBP){aJ(cIouN3k^ICZDi-)s0j+ z7pjySKo|D`Rts7yp-k?WlDRHCSRxu*9S6dn?xw!l0?z9O!#|G~{{T)k>tu^oeY6bs ztUdht2{-vB$o~MinrH2xYu>O)S)6%MP>z(}*az&T7P1NMNJ%-EL1DnqN|Vt*B!6W~ z*hYJ@Bi-ia61kUdA@R8nrO5aRXqgBesUan`1l!Cg-l{gV;U25F?Hd!C@sl~PiScOp zt`pC>P<_T9;SzB%(-5P2B%Gt=k@4xYqSyA-YHU1~7&yb)f~EdyWL0+sEG;TYY2`{Mw5Tb!~A%&zoL=HFFsiH7r5}ieZexlWT z+=H!9-CA-w3N{)hu`_d#>7`8V0FN6RTB)~nV00S$b0@ftNg)eLi;7O$ZP)LvTW?t; zbQ+s-`j8KslK1LBwe_rq zl%o2QR;ELy<+Bi;AV*r87ud6V5$AfjhO`)kmPryWHJyz)Xl?gBCze3;{v)aPQrnb9 zIKe%6H{6;#ly?~63;2qEFdEf#YEm7P+)*dv*;%;~0$C-qsL+FIuiUG|-E&3x$FVDT zFtwfNC*mi=#<|PwYp~;G!G+CX@XAXi;M$Yb%Y$QTrMj8P=LBUXC4yG}05}d5M{UHr z>Htu?j~aFwlg3!BdJ#`3jJ`4%8L9bex>aJKW2U$8qO};usPtu8?nzgYhsUB3ka0I4 z6)2FKd+BFHrvCsa8O1l8^egUF&Y&Y7EW&v%4Ti)KapQWt;`m!!lVmn06%&a0PHF7y zmlVp>^1(MqCry2o>wIlWy3%JA$SF#F5Z{k*E@PispM#GgD?}AOwQL96Ro1Nj?xoxU z(Xvs_2#+I1QwflM6qgGxuvC?Sa*=IDzBO^DU2|C~Hsd<_6&_W&83_(4O7&EABd6`I zt5zy09M;RrD9&Uagv-vYWNp9j?{BiYt}1>_BA<|oj9X_CbN)uZnR&hT0{Y*=y_<;B zUMG+9d+kf7W1O-atg#^7(p0PUHPI~vgOYrv7>r(Ugdc^=8~j?ON@RF?!bGsZY;PQ@=o@NiZD8zGU7{>y=M6F8H5UX0%Q@g=V*x~;QMCk#cB$UjiLiUfs9$!D9)vnTSzLXw3f zfn@a3s&@VlMU~<`8ZG$mn_XPLJIVudF6*O~HL>h3$tg(H#3?rWK@@%y<2LH`P2d+V z@;Gd)JY?rwtQMsey2{dHtbSFgH}>?dzKOWM%#JSllUYS$oWNurj<;e!VatAHmjMRH z#156w>TFYvz0P-YR^0R=(YVqw3QJS*?ZS0`3LFaub+w}3zTqy9pbEIvHPs4D?c_-R z0K=Y;+v1Ytt;g)Q{BTk zFF--z5*i6nj>3~+xdPjge;Tq}(!h*$@0p|84$50U-?)A-DOqeeJEfP^^SX$V==5RJo60x9RssV>6* z0O`kD%6VbPn3E%Pc@DQQ^i`VMaqkse`6cK(R}(3BM2-hAJCb8_3dhucgmVk`ER1X0Fv4=D<1gs$C6lWQ7_+k*8O z{i@HQB#FnTHe30a?V-Mx2U@JVYb<`P2Nl%>6^vzS{`>SsA@$*%7R-AjUA-(1L{}1N4ik{uNh~!U?9G0@sLk{#W zSgAL?OT(w-V)DHw`H;K3a~qRnzXC#3wRAwYTH4#PFY_9nrB*J4I(HtGjn-y3sVV%< z4~Sdrs~Y!|yE2|!bfEIVKPiQIR=SyRn*c}m)+;CIX((r@qufo20PRv%6@9B3o6%#q z}#FlaJIw-X;1XEh~yW9bXXIdY!~^(}86fqte`f*#F4^ zmIW==i!&NNQxdTLDtgirYgmv$qE5J39;gbG>-{Xbc(-{nxT4fdVJ zq}$Vm+r*p5{KVhUsLmK zZsZf=eMLfb>O;_+XSFrlmE-ur#;JIK{Jon3vs!AM zXfEY7w$IC<9e*h$OW53}WxuEmwvU9Lx|p)qE`Vk3NO07A+?0jM3x?ITDqXF8I#TM$ z4%p(v*Nd8d1tAmQhk`tHA9JO9Yj=Q|~li%FTH{ zQ8!CqZgyx|k5m;8*-=j(g7TJ7LL??+*fhym1N5fA)LnyBRThaXem5aWN>oc|AE7qA zA#MJnkkZL0hs80wZtG6GM!Qs^kWadmmfOI^S~19WCISN%wlS22f9hQ6RhC&iV704W z0pv5rprk4C$`9*E1NI7Ct0X!90B$209Jh_aRik3pJ>(zZgrFfK?KLm6n+Epd76gB| zS;v8D7BReP4gEFSdh9;oOg$8rRxbDC`yeZ-&Skg1naRBSO6tg0q}jiPk_8^CVHMB} z>#}bxibR0hpToz(^#|{z7r)!*P?qB&spu3&3Q1S-qpg(qB#IB-$UoaJpjEChgDNI$ zHl{1dKoR#*qqSq~8R^xYqx8SWaE$)e54&A`f$QhIbQHDoA28uR z3ur+qKVTFu+U1VqBg;YbNPQQSi7T{DKPgE+mBlAu)89g~LMzm=tC4Tek)iyIgoQ2u zj#c2b0zf|MR>k6mVVMLNg5$#OH~{ep08-Mq;C8)b>c^uMjZK5-B}b^fpKU1bn9`yG z$&CS`LoAYn^a%jkmXmBRwrlf6%*(dTE)-Z&RblY&Bl*c&m2TOQ;)wRI>As@(JqBd`uOqSwS@0sjEMOS=YSd$Ekiu)k3aw(2@~sN3+SDfs;E?a)tW zJUd$f+jIi-A%$tPzklXs^Xu8Hy*M%>%MTK`cooH`ijn%LfnFsp*x7auOo!Q(tjP!SbQ>qF=Ma77=wUN_t{(+;sq_RdbIlHMv3)-In zdVZZ%kt=rcM_+zz%pW}Y)tjx;uDX3srM?&O2x`SyW9og6;r;63*oeQ%`2HcRE*=VY#%TpQB{~94 z&FkjAN8$S}PEV=p-?>#Kga_t7P)+(POi5mwHqq3bPuY5(8}Ql7xw-DbKBX%sy$G60 z6(?vvnO7QvSJU`>x^x8js18@(IQYnch|(Tt|D`)||VT@_y#gccGipED^kO~$*WkdGob zeqxr;P~Mx^*c;MY{1jYzp!%Gn$=v<4??B5>W;<{CV5K0W>J)y$YsLOi@uwcB>h&%6 zx-+Mwa+PEnY_vmYMT*Vzw}}Sv!arr`H$aAyE>E{lAUts z8I#8n>t*Q3OUh2EZCVP-?o6$xnQ?fRK^J~!85p>@0Yr2O7yimJ*vMM^FP;>LG~Qqm zqA%$b6LgVolvg&_Ms*th03PY}Bd_!5$+gVro=806*VTj2KAKf;B13iEKF91Bp>KUi*Et#Ztq;3CjdZT=m5z zay})foufln5Qwt`Ii6uBdgFTGM?{d3w0Y=h`lxTPt7CQYoLWFiQkd?a&G}SoKFVff zNOH&svc%BEzg@+JOZMsD)<+8G*rtMgOw(#3^4|BOt}bAmrc=$Z>i#TwrdIx~jcxeQ zWua<)fVP-HzGaWtODY?HwT_m(Y1K8|6GUq(mU?=e`FX9gxC?@SuezA34krBimCE4q zsZTr+on6<3vz}R)BR^&c`QX%s@ zY2AFGQl$3*fXeUu2I41h*8V!t7a;}cM#?0~#}xW@ZZygM2jP>rkU4LAJL~mR+{x1+m>Awe$Y*8pv*aQ>jzocy zcNG<=$W_a$OEFWpC#T_Gry4s;M^9deMYkx$e|pRR0D1ZKvq#?T>|HjNB`@%8FZUe} z^zX(vhje~~vmLVT7dpo96><*c$jtFc*%gGjhIO1T%9)Nb2w%%AjjFY;xYrBvZJt9< zs35TGSB?~0Ni?S z@h9>n95k$r-=;~Va~>0(F_p-2w^K0?<7*Qqy&<5j->4yCX5ahCGdYm2^0Kj(JV&ej^>-LybSdRLvCzyqg#+`^$vy$l zYhP&1c^)}w-=wsldRf%orS{;J$kgU%9*w8o98dStKWuN_HU4WmTd+U-qJO@D>@rcY zu?1wN0Dpo_`dZ)LL-v3;jE-`Er>Wm_fW#8r`;ulgig zr8KJtW09(-Og|5#b?AN6TJ=&Ggx_Jgn~_KTBCGVhZ|zNcqjQm%day-p!vtlp-=LV4 zkQe?3UugdNm$b$F{{R6KE^-LogZ5T?OwVv|XUeewwQ4*?XSU2%$Vkqbk?}C4d0bQ- z4Tg7M(Nppym6*yF(%MnsTNn3VsiB#WAgIP7-a1qjEsXo33Y(E1^a=M+{8>M74HYVW z?}?LojFwOtTJmVAbIP=6g;wPj(Wi6BnR_Hv{z{Yy*HJ|F*e5=sp1jvT=%`!RX!s(o zVyIlRf8Z;xm1vXMm(WldUMw&F04;4A@@#UKBrE>_o!6)MX_c-=-DW*WJ`5;*hMK^$ zZ1<@vQS#~|%GD2K3$WXCy+_L`-c_Yhj(VBAfd zc4dGGJ4aD*YS)V}%AS}~0#HaDMwHi!7RcW6Pv|=PMNi0zPd$tenZ3dd{o;d`N;&!% z{{U0an|m)zU^-@ZKhU^NIV(b9V7N)BW^kN;U4bCbYMqOy)=L0_sIZb>*6yk)l-^(KVP2{XyD!!yrTQQ49_575<7|!Qo`2PTFc?V?U<6||5T z4F;pfxzIF{H&}T4&2rL%OzI;Tv_izJ9JY&W%>Fk(k_fq`1#GAst~90HVroEEplvq* z`D;^f2Pr>7N{nt1zCP9=>}iuVryOu*3#tnE$g*~Hu(fVAndHuw`f?Dluz4&;IS2A& zDNQ*NDY#3{5oM8Xga9sUnE3?~^G+!3J~4b%ZAswiGm-dC&QKo-u??kUAHKQ&010{{ zs`sJwFwSQX+6wX#UYBdxDR#vDg0|?iViwewSCCV@uLi=%jgD~N_nkNE?W-7Zwm68- zmaZL;nCFpON>JV?AP>snAx4*<6)#pAzFrTpvN^n|3;qE_bq^8%wMrar0!x-}u)6sy z_tUxgUZFR?x|Moq8e4Pvn2(%Cs*!8!sV1X#u?n{yNrY^>iAYm&cHh7s?4)j|479G^ zfoNGr-UTa_ypJ*c^rNdDgOyJq=)V{Xb^WK@JU!H%nI5hS>PoS=@0qgNKC+MHMf#eg z*|A0S>~vb1o@=8f*#0w`mXUlikiXNofB>qEjq0MimpYtHt#&1`_*Oxpg*_#xs2}Eq zEL>F`yu3XZQukYhm#zsEel?GbeMvc%m1|sugRQ)cQD3_`zQyf2mBn`nH7gMjKp{zz z5vd^~U%spDjM!*fR6S2GIgMXNloEQN0YdvZjD7S182scI#Xm0Fjj2ac4JWst!_&A~ zSYC0GXZUd+Q)zH$ug$MY!83^a^7D&g%l^_X-WfMEozBzb~$4O+TW@%%Vb7Rd$4$1t874b)c}ps zG$~EZT)&4~IOhc=zbdLT!exbm?=CklHZw0}Q?^pTyO!zfsy)AoU0QDsp=DReX`U8N z$K%-yVTQ_KWtcKl5?W~_5=rp6t^<2g$jhnFQ?nUIC7TU=iN?^8F`R5LLY$Vh>2LVf zlGdUwcxarb(#pXQO^D$B?aLvFe>u(8)#?)blnLe-LkXPb3Puq%)E z?kz0?H z^%vq@*T=TPkvt+g!hu7i1r-jLzp}M|#wU)3=;vdS>%(^s{lJ~^xf-J5Az4N$ES>)Q zYg~T+025_zuS1}teluA{{GTNUl;hr4q|k8*{v3-WC;`&vtv$y|Y2)Zu@(@y4IeR4A zn1>t>xRru_6?uyE4Hqdy;x=R9BcFSZArs@gwzh$9h>sgrX~RLrNaeYu=LUSmds2{?&EgGb{pm62xPopIGp6Fy48(jtRY!afQxi<1OL$$%u`mT(-)#vQa z8_8ur?p*muLM6WP&_~6o&|A)^rNbohj*_l!O?3%3&l4Tu>oQj1Z~(Nr0ZP8XK%W%n z(*qUqinpBiAGy!5$=vdzCB4H^f~y~TX&1su40&@^v;}(r;Xoe0xOn9$yW-Iy1FF+> zl+#k+`gbco)Dcq70)Os4IhbWAbLLXZy_{U?eGuh5y;k(eyO0#>Z37^Q zN&s1x>=Z}7w^hD9(8TNW^%_XS=2ViToryLL?N#-oy^OxX_US*U-8c-g0W1ly1=OvnF~YYS?k$V`rjHw0sZ$S7Yq%T|7JU|+E_k>=x7;bJ_86tq zN6^unh94427V;QS{M(QX{_#Y5@{;aGt+>|up!`l%h>(OBPqLzaJ&x5HLf$rS)U$^% z2)@IC&2hmh^+Lag`hspQHSnP>D&^Q?_loc}JjWL$B`wNGO}eQa3FITqIg|cA9|ZV0i*b4u59I*xLw+% z04!7IPs3*-PLCeQbsI4Sg@XG^X5MM`^{u^ICE1XT@Q-vAS0=(XtGO@ILF<2p)i=jp zPxlH9Wpds)fC}a%Xg1nxNc<^f#%yF`_bqTD`Ck-SQc;jW3kLSMwK%x#qP_B(#0x7g zi)4z@iTusf#I>yGnFyU0I}EAFnE>iiO`9qOVy?8e8+ei__cS4l z#zZm6H&T)kTp;RIuiaL@9WrzClT21QQ?G4uGFwlBrq&{ta$&1UzTj;8Sm4;$ZvFa; z6#f-qt!$Tavyp^-2r-724(;a0_EoIyDD133%xNTgS5+6y33xEZFDabDI)`zy9Eh-g zPfa|*(w5lOuGNM&R@K$^Wy^%_DvxW2m4BMIlIvHV zgx8I+EY|Q$%7}BEA0~|K56<%~5FTt^%a5+u^Z}mV z<*}*~AuPJvCjOeW>8&*$N)uROAGKRs%IqDD>B-v6_(iuxfn9<{s57gsQyhiw)f-IU zxV)w8*hxQ?V%HQp_w0;~*{NL7U*>yQ>U~AWv!oNXI$qvYb4N7$GNS$oW1(Bk;j$v! zNQS2TwCQ8+tBN@|KE*w}MB=*u7sj3j%^_r-fDi7b9_nICM#RM%P%D%|PCWtIGpDym|?*lB|@4MM(B*YTD7s6}&Wakj7ex z*iZ=61JM1oHK@QnwUNK`lGKuJvVtckzQ$ZGuvDQ^D&bxxSjyCY3sAUe1=G?{;x%|5g%^8)(8 z`#?6LecemYkJ;S)O=I?f=?eyI%kR}kss8}wt3P^6vNs+Xzttd+?YA7ql={*ddBmT} zR*il4t*%CL^d}x7l)AC^$zq`+)5k}KhlYV=A$s|X3XG>G>SL-A?2SHSJnfPE)FqUQ z6g1FEe*jG@IA!IT8+|Ykjf?rcs+!8C{`n*+ zDhKTpKYH&%!|m#8st7Lw!4gsFM~EH*!s%?(ecS$^KF*e7W&$FJZEUg!M6}7j+yxcM z{Y+HB4P?nuflMrXyo!`;0#F+R-M-ZfKdC2Y(Fw=JW@W7@UOd6S=k=aRHuCr{M1}Bz z1?jMe6SOwTjwdDp>@x?=I_foIYn2uSrr%$_BwV9)8Lbl6}loU{P4?tb?*D(9#vrWD-Z&Ou}{gfwNN9 zyN+4nGuEN=`4v5=(H1~7o%^v*qB5O1_F?#3V;LP=LqTqhLexr++j>%a)_1{YMuZc? zC6RGO0X~+A9?K8HvfFbJn(9%JBW;zogrvBJ?t0r=t&E@0dCLQjMGcbSpRqspfT;Mh zGvVqEl%`!N+WOufyp78JlvzU{)s6Nr&N5891|glK{{Wcb0H3f>?2=tVc+L6;qwz=A zx8@exSN#d=K(}%F1&*8b2)T#AAMl{{Y0Uwb0YS-6hoLc^{Ec2hkM%OTO5giqsAT##&wOhTXEQ;jLX$ z!%7I}?)d_n7{}+FdRa+MA>^sRE~S*H{kJBKJ~Cd61b4ijQ0pA)8p@I}xNYkPsX|7+ z@F^+rvVWWNl6C7no8=Il#~7fA}Wz2MbV)kDy>)!3TuDJLnAMT^fB zC(SFlv8ekMl9t582Pcxn(#n+Wx{F->^*5;o%#R~xI|5{LETLbf+(%STO)PbyLywDH zS*dasdLb7xJdN;B)JGB*`jw^r`hud0*^##SArIyJf>J-!Cumu@8^`uhKJta=N9^ry zt2K|qR$LCq$R!Fr52<}g@x3i)FrxY)h;Vu8*j>B~$mjT0mJ|6^)g)HGcw-BU7|oR{ z8Wg9tZe-RCx4Jf=C*566hXwDfoX;!r zk6~gcqq9Bm<(UPz8Vy2e!DiY@Zb!J9>Gi%fL{2Yf ze|<47J6(PP)pGkyhq1>klug{+k$aMhjegp#zN+K;S#PgwDfd)Hm24dMxh!ckV)SbPdh_D50B;;+{Shxn~%jfm%dX%99m7%4g4#R z`EMOPoo{Y02TAd+Lb`ABYbVjX_tbu0xRY_}lK%k9!N;IC78+Hx32i?LMTLOUyt`B2 zb?e8kSM@zc&z#EMk?4yL0o>UsjE_=rZ2(%^w6#@j{9+mGSZe%^%y$6tzVR)zdTdB5 zPwO{7h3J|e7L0uZZG4Vv)T~9vNAh2Wo1oMV=ywilC*hX_x=r64O^H3q5f(%Gs0WOCf6!TcAUT}vM^#<=$- zhPRLs3yC{jZD1SP`di;id+*e!(&e`GC*v;PiOamdi^syeq%oB$BKBp(k)ZGj@vAL8 zzbAvy^*5c6&M;K(-Nh?(g{N)*04>ex+6^F+j!Z&Klqe`Vq#$$!UUX!aprwrp%bS*A zvWQX@AvPw#SL>xmUa0vRn#@Y;C&)7m6(VDM0k~~i{6Dt3EnR50)Jjt5L1yKrDJ5)V z2w#dXYj{=NT8OT)T}ayU8Fj3vEl(_5Zzo&y(^_^qv5+Nmh}J~;YiN4Wiox+D>S(`i z5<38Jv`RZw6GWX96mAVUQi|>+Ib=Nzr=tWGWUT>u*a9s>wZ*V9Y@9*MNlQo`lBEOs z!({92wN~E+BoY!~R7#$s@I^`#4Z>EfqJ``tNrr!z<{4y=mH@ZLfUr9L+D=uhjwz$j z^KwwMt<;1gO-7=%;EDO5x^mK_-BIMGD*7hhwxjK%Jj&C(+_Rr>?#cIKl3;m4Zveg4 zY?K9c(%u!d6K>O2BPGBBm8ECfWRJWEmWJ`^OPh#FkHmF4(5D$B5))006Pb>i{{SEG zrRu%BjC)8`8N_a|bm^$pnD?0#P_lD}#zFr8%xiAYaHv#r5p!v0UW0O~G1`-TLVHG^ z?nk%(0Paov_aE=6`^LSdZT{kwdwlwU{{X&k`)Ufw6P0EUCXjS9TtE2F_tgIF67fP` z?nz(#b$|KQ{q(oolKXK3$t7R^0D9%W@1O6Z`^No*`<@(K3zrM5HB&c4d zgOI;_nTyl^0GccN>wfbwP`ypR+_JR1$1Fd_f4;Hq{sz9pi{v{UYT{lzE$0FJN!06M?Eq4%>V_Hj4=0CLIy0OO8-_e1^lpL;WZX%k=f zEAD^u;>EwlukWn=&O)nBB1e)){{Z8w{{YUf@1_3uJheqX+z#I6V$*JxQPz*|i~BuH z4EmGSLeKs+e|-n;N&8G!{mk-3laX+Js{Z;ccCtm+gTs}-;gE2D;y>R~`-5Hr=atDw z8*)zH%hXkAUxaQkkEoyfu225}eBA#4&cE-b*3^LFCWn{g1JRp0*TukWq>^vwHMh^v##vHt+%o0tCp>WcpQ z*X|ND^`dM3=bAtLpy>yw-mmYXee3#y_EYo{{{V8oum1ofT)+PSR8>K5$kU+-{{XpV zo9>wR{{VJh_tDn!1@@@PnmwIANzs?WQh)Q^YYCV2=KASj>v>i+uQ-@mADwTPI{X>_$~{{W3u8FCE~ z`&4K7`je?@;D3o#8ShBi)<)^%3qnI-2~a*KTYdFE+@I8v)<>0^bfhFUJ%d};zjToa z(3hdRdB+ODDN0D`r%%4M9zmOW>4!0MRgf<>;z2?M!o28mx=c;hvJrD16JjpP4UU(w zKaEX6KH(LNP(^7Y;YvsT5nJ#^PS(9g8@f|H_jN{#LZT5;N#vZ77(GK6IJd_&;!O#)KLMF}p!c*RT6)3jQ^ zc^Z_LK+suXb|E>rC8u}ap}DG=-jnJ%RV{}Z$3Ec!4eEbYZc|tuu7=-2PX`B-aX{`3 z0r^x{YmaqS+jJ8wsMeb$@h}pdBh-=_3DqZXx3-b9d3qw&8Z&*_DTRoPn<@F_(4Wv2 zD!$FfCyx=@bYP$AB#|;0jBS>LC@Ctqa^#X}Es7^KhKpKng?*TL9O_NV(CWecB&g}( z=T(+)aedJIdD*3e!V&Yc<=H}b0mJbEyBe#vii-3d*X5@t(S*bD3A*JpKY3+Wy;R>+ zBj{aF+`UJBv^~3C%B8KpnJ7>?RdY@DMS}49j^oPXsT${2qI#eUohTBb@A9G5J$2oV zk;rhYTVw7%JP5EoX$fiBF6f``6`-76eG&>+@`zYj=%tZs(lg;DXLLDVHwZ3+JfA+x zy%ob3Ahs6g=CQH+HKu6z^+~LuEw7MUj*Nt-oX++C0N3Oc`T;(w3Y19wwRH|6n6Ju; zNb>f{{$_0VDr#aJ3`i}%VpDQJrfJep?t$I6^aJ8~FDp(#3QMXeO03!tNxl57RyXmq zu<>;t(88CBWEP(vRmwX;mxAW>%-Z1Q5ydS^uV?kpm*D>^`W0I#9 zxeHR=^$5L&`|Dh{Dg1FepNfxTtJ3+UUf);LjCTj>0CHSA-llmO7cvfSkjjEWl2U&* z?z+{F1MpjQUNBbjpDC(q^0Sum9mZihn=gj|JBxSML+r0h)cDMr&JHVMm2|wIqBjHQN0;&YwPnPs zkMaky`Z9IKKB%KK9)%)#Ug2i<3*7k)z*Sby@zmkkCFZyCJZ$LMC!5_!yn+~zmw?k$ zB%N+5t~?XhZ86Dn5xHeDoaEfig)-|6yh*tWO|=!!ahz^mN-ge3rV+_~#E}|uUyv4( zwSqGRdK`<6Evlww+CS2Clj0+t+yXDuu}0Nei$(6=mXz zM$Cn-;>xxBYEzC(7etZ`_4XPAf(-|RI}_5RsCpq?J}U|ru!*gKAtArah{Pxh=ryY8 z*M?3uNx#avgd*iKZ4tE#vU&v%3R-Zj`j<3Wu)PaA4>J=kIR0RVBR-%k?%mC(`1`A# z+;%V0@^yNWgOBE2NmFcMH#VPDr@#YYy@?*{Rb;NQckVuwUV-Mgt}Sf`iP+k-0UK3+ zvhu1?yZpvz{^^!0fj!?E!lWFA>{4^!xk_jFf&_C~g(!l$*Kl3l|1HT(T z9cmC=a$e07cL4wmY1%g`?P3(WR>^gQjZSltLJ0Yo4iW&n+FS$fHIdfWx2##ee`1x@ zWPcMoDqtaO`i>)0Wde0HoxF7G>Ta%96X?QTi_;P;g*Hd&O{!Ghqugmx<}`N&DMd;J z&xfXDovKQ(#hZ8pT#vSm z_POlAJLbR22^lzTxKg>Qj4!ANDYw3uj)eQ66}uea^aw}HMQkXw$qU=RDijE{y|%5r zNqV99Z^15-3$w}&z=J8d3m=pZ&0*(D*`bG=2d|J%rbJ)JGd&k^#I&ROZE9EZ*7^)5 z3l~sKjC57BRN}p1Yu3bs0njc-2@*3Y->(fKvuGExuC|pg`bRzX>q95rxw~4`&>)7MG(~3JBO+5wQW3` zb-0Nx17s2D=&eb$6BRXweJCXd3GG07<#6^0oU*d1Z-WID!P7>%LvqO(yds(Ie zl-$IY7LKY=)5@}1{{TR7+sAZl^o(?*?I=UQ_>HHitb`_+DcK1S{FXin0^W@syV}74 zI@7h=Hu{O%TvuYa%j6}(A#Ef!l;3rSO19OHn(9^TSnGL3Z!KHjKJFjbD#W5a7 z#O!2+tDv>NWeGONTw=z{!bT*A!D$BDN_DNm%mY-Ri=1m1;JZ?aPwtV{xA!a@^&$_S z#v`GRU8%v+Kj;BtM2+pCd$d=NLImk?g*cTqK~I?Qy*pp#Ir-M$%~I-|u^<6ORhqKQ zF;HJ3?JT2jLJg^vvYq-LhyXtd&$vL9DGN1(IS};QN zf*$U#3y9*81CFjmzay$`3DgVt>r#gkYxNhmnWxbSfAFs!RlZvc+*J{xm8l>3K&dVr z$GZV8SIeS77~HcbkzUlvX`TN7mufux??RmkGj(4n5?GEcw#ueu45F{pfCawlUAD^z zH;$v!@yssdTj*}(@Rz@nIVT# zNP38=AH;dk+Cluc6fbg26RjF=!?sGx!Ad?F6Mtny+-7L3(kx@K6>B7DP18(533mtP z*v)ad7D4MGTX7*p^?_ILHHa+Eu{}imivx)Z#M+>=tV0&MM-@J ztK;SBP9b6&VW6_vP&T;It0%I{TJ@3&g)+iy+;wmABEq5O*v#aR6C;>P2|;}fs@Aa7 z`Sht*Ta)T1v2shH7+zly6MWf>DrAGI7VX?pZPRG-qkWX-@VWteP_8+BSq0=h6^{+-b*2GOrGq;kIL5d6r>;B7qxV? zXwzS0b9QgrW9rEKUNOifI8wG&loATBVW*u~%1~W|eTmKWB(QsXBrPj;Z_AGs(l?*K z+ezKE6fv)%*Qsss&JT%7T12QXxZ--sQtXp|B=r`mi}vRK07=kl>cK9g;i1H^BI`>m zIE(6jHC%ahHD6=CT^VCs_2JvNqInz7Jd|NE5+z?jB)93dFR$tYPi-$PIOFq3)OY6P zh(t8R9#N9BnF;BF+0Zh82?N?`C%HsABw0saV#U{@3I71M*<=MhDcP2_!j?jB?i8!q z%jk!ps@HZacuY2ww-oO2Hd3!kPC4}(!bnlMPAB1JFU8{j>1{bQQEZ>y+`d~+9~wjn5UKE0kEa#wA<`C4H#p% zO`I-0rnh<__bxsY<5l)-%h0oiop=^otB>%;#_ezQ1dIODP*>0BP0)aI!z&6)@ppxb zEhReG^`q5n^N}rzW7`E!knNraCa0WCClTsg?XXuC44x7*gTh;Qr-$IK20E%lToC4bI{t7Fl47QihlVv(Bt-P;Bmo;p?MAr>e zdlFVS+$P&fVKkMh_5j#l-$0i9n#kmIA<+k7@qCF+t)&V5&_$^Wxf0h~jUBs$%g1F! zTT;o;6JkfQ)~GGW=eae<%h3=nLB{009ZsR8snQMXl5c%H1z2ixFpI-wUgrfVETmDH z1CYhMLy1#C2Fked>0MU6(VTi5rp}D(eR>M9$hMS{uYKvYRz*Dgdj?U;03+k+?deea zHWC|eBB|vKDPcv;Z)2@z*|L9lTAXJpxd}?xIvWD9{{Skd-lL~d>yO;)(7iF4rAI?* z^#EOfO}s0bzfvsdwf90Z^}%AyPhdpf|q|QUaTvlAin3PQSLu3l{g(LAZe{m?O#Jw?RCju$K^I@1S}oB z6n^Tcmlu-iJdP2m<@Qm#q#rP8Xz;&*ljfh)mf(4qFEph%Q(`PhzO;_#3;A%z^tq*+ zoqNE$(-!3Tx3IGw<*~}}IBstRA_S2d zb-#l6NYMLzl&>GR?aQf0qjmanYQ>atr*OEpF5uBuFvL{oi2T_G)43KJ{#5H-j*lTl zuMEAfkgmm;vy(FY$rn_ApE~)zW0d>tFi(T!P9SyYmEy`;^ z7^e9v0q{RG=LxwfakZwzdFkgymh7Ut5O7W8 zArlA2OP-9(9Fz-LTUES%6<01!%#xITfj2R{34)X{jCBh^__w4B5$ADPEsJCpG^9I+ z7mZ9&OlZxuu#vsO3HOaED{9om{iTs#<#8LkOzdF_HdD>0B-s19(Py%D7YMcWDFMDn9hv8TQc|dxT*_$&kXNIHPh0uCw@Fo%ewFjnfMf!-2UBNeXD# zI7w1SG&x<6iWm-)k3(UsZZ%%1P0H3(5-skf+b1riA@w&iltKDx@0ZG=GYdx;%&DcW zp^nG@0Fy|s*4r10!TEmk zl5UU6DkdHZxg*Mp7Le zmM?L8`P}~iZDOR4zau}&+x_Kz;+L~n0-j8ND{lIe_tAdXm)w{*HVyt0bK`&cR{qVC zTa3c+Y-!a$ZENjtG52gs`7=McQ2TNo`S0Y+{^e)en){JIb@<7xEldWE#!UYJDNp{PseP#rxIO%(Kl-M# z?M(ZFAIcIx>Kf0sGwuwWI!>WPgX2N=dK#9)Z=s*%3jO0mj4%(~JaCS$tA#e{Q~Pm$ zbkN|^e_Dl0*(c;<`5^t}eiff?58W01M__KUulV*>dnWzVB5(?^KB*sNN*b>wz37_% z03rVX=BoRszh-ORiQ$6(0OqE&^++pEeqKH8h;UAWozvTyG=imB!055WoNL(-oc;b$u}DND4xkRZcV?DxBmdD ze(IE~`U1(X@`gY475G#-qXz>GA4;FL&Bnn&h==-{_t=_@>gX5TM*jfHVtE>uv}@c) zzm>)v5-D}8@*Ceq;~1SRn_tG7l^7PD)=klkj7hpcw^{<%c_Z(|h2X?y8CRwb<@G-5 zI;@1|f+a-tH>2PIip&{<)r`yzwbSga7+XE5$SV_jjDM0+n>3Sn+15L=$^+{|HmjaKF0GixoA5spvdJQA;2N@*~ zM^Feh1bO|GOEe=spdq_B`Vt74_r4!VN){gib<&l3Fzh*BBW8OAMqetWUvkuu(A?IZ zoCa3)fluZcTZM@vBz13bKaDT!*YphbZuDU$bMqS=NGHS^UFp_Nz5BkSmiZi%fEKfH zsqv^!QaBm=`Lu#9a+QJsC!iOlZqP%t^11GU5wmV7(6P9weGOOLs}#D9(YG%ygr*i* z=Lg0VSKys%m)g@uNv+68I~pXJi$>PhwDnOWYgCrmR6CZ`>)m7M$(#-yCFaujaG^)@ z0#D;r_j0iGIyyL+`jS{&F~q4Q_Y_b(I&0U)sqN$&=@rznr;dtwY!^R}_VbUvwlev2 zE%x+VEfTT_@c2QsH>tz#?0%Jj4bPBqy4~Jb21j%|eJQ~k9gCGU)t})hN!p!1ZC=&z z{iv+YM>VzcO4!esvdisCz28L2L0qe_Uqmx^6ho}oG`&eV?1 zmi4RhBF|5AN~ignMTo9d*{nodLlT-+QeaX{-s)O&vLTTC*d9`G@j~+J}X22<8`gA35^|0w) z$*;k|MDgo+**OX`+((;9Z{`5KijMCQrAl8Os$PL9`8}1s=C1c5L9q^doFIQXU80$1|0lg>eLc{_#lt#1_D+t$>s%^sLHsmM#u zxzM~bmW0JCdm+58;5HT0r(2rxzI%(Dr_mn6!txM}f2*h?JZTimn2B8|X!M zdez|`Z@seRB0iZ!k-Mjz6~PKAKT-H|<0p2W8v83w2yu26X=v8!_fT3j2herAn^@3n z2$)|RK72KgO|+>U2q+4@{ovJh*0Qm8!DpqjD&7*wHN|CSDa0a7cARU*?WCILc6jK% z(mI`9N=v*+>>RElND-Zc(`yF6?FsAFx%;TPFQYnocWIi#SYdZK#3$PMh(VdT~f{p?=Q@=Dhgu| zhw;U$?QPZii@&hk*F~qrQoFpIuOrTEN#ZL7OPiC;bEJPZlP@5I{zjCgl&0Q5X9XJ&>Uo|zyhFrYLj8QS5@ST-DI!ppB7Ibn} zGu^IO&QdIlXDE{TLTB-pB1o|Hl+(#t0)X(`Me37h zj*5L5(`$0J$~|HcPIZ^1DjQ8AI4Rxg6@qPl5#VSL){}k0t(D!P8_Ic`9Bix9WBHzh zp)EF}e?35}eJ&f%yo%0KoRurFN@aV?HP;GY;G1L{gqK2!g}&v9RyKZ0!9txNkIGFe zeuQ(DJ4Y)w+#s2}h4h0Et<)o^PN(iYXg0=}uA{1V*t!YEp`zhbg^b>KZKCBawa}E+hB#qq$ zqTtio^xu-ZZvA!8t}-wgmg>WD_b3~M5-+d1x~*BsM-1b4;~74T7S|rhF6(AcRgdpc z{rKmqD%Px@T!#?OMj}Eb+2oBk055N{hYPxlEmswjWXwuQ+zV+*@u^AV9GgStWW#Wj zsDMH1rK>`|;tVw(4J&QGgugIF0&YmKg04taI3|*JH78w8>-5*zT36*^i&?9O`9d%= zd8`77k2z{=8!3_rN`{^WgFQ${Eqivu@g@*^i;9@C#!?ERx=8h8g#0Jltt)nu$3e}0 zP7kD!XJN7!*Hqh>`RREo18M@SN7+>u!g214eP>(i7Ft-e%W zEmdAf_Co~Bv@H^1ZMFV8YxY#@+>&_(ey>arX?!R)qGCu?YmFPZI@PUwlE+f6-Z`hx ziA%xep)JH28`?f&%A2od3|UiTUtYvgZZR@JW>X|My$gUJI%b<(djq<)KIC5`+q{B8 zoME4tNADzy+s@SGe8-P+wtP32VhH~L9hDh=l9??mcqmn+e1v3*+2~3>gE?(Y#~~|m zA*M%54yWxEG}OI7ML6`p#jK~Oq@+9XlcI0Zr|dI+=tWOSS^!gG)1;g75nA<&lz)GR1o-ujc?sd@sAg^#%Tt}iKsp|nI)g%1YMr2|DlutX@r5QQSl#kwOMa3 z<=C^QhHJSrg}_*3B)254-ofv3*ly04?vm6+r=&l1$k-N194O zj$K-X&BaAjlkP@0%h3cGl{MqAwzL%vsnWpf<584S4<9y%XpI;UsSK^<4!)y@_EOH~ z8W6x$gfX~;p)DuCX=6i_!p5|RrOb0^SE_-ybRcRz>IAHjC`C3M#wf@~tqs(++B7O! zOLzYO%mo5jWAVZZzsWH$QUuwpy5mDekP6j%bofPOT9RRG+>A*3y}@zyDXeS<4Zka^ zK}vq>(V@n_Q8&q;6WLBplX+gE++nk(#C$0L`?aQ_2t*UIb`N4=Cx8%*sam?JS5f!e z(C?Z=tJnITor9Qcm87j!{J?2jqhQ{~u@!rR$azY2=!%eo`W97gzCx{P7^+yPt0rfr4)E^ zyu*tJ69zswAIkKLxUdI~(P3Kq_^;Fk%?V5N^cQo2<|XX@XPR4w^?D=HNgiJfFK;mz z_Cr;0^2b&m#&BFVJO#4}3UH6&Jm3x}{i?lITgX{2QQCO=wtMnm9!k6~Ld7v?t>J*_F$6Rdm3Q@fs zNp`@qwt46`ZOp?fFU4?xPOZ6faphW_Fla0;x-TS}K6*`(T`jgxUyE9)ma0X?Tg{;J zS$O8(#<}rGBGn1J4by&2o0k#gkXTE=YA$F43c!Lg7(#X}ff%Rtev|gnmO^;wHC(EG zqwnQ>cPQ1LT2|5hJB7a*6291m`%0ziM&|anj?M(bnewfxs#0_xeINZpICQ|e`29J~ zM$vmsjfxUel~VeIED!)bdiha*)Y5*2*TpK6&P+<;O~Lfm{{SUrF+!}Q0+OHy+gf|G zv#tsL-5OppOE7Ns_9{R5E67-IHZBssh~MnBR&3X`WBi5Yj?SbOBsJz8!R^F2evURd z$-IDlFvC^dO6gQQN?^rC?TEL;N1exwdglt2 zDP^8b&Bxwv#T@xJ7cG?@ppPo+A-I{t-^a-UmYJzKZnxV-`;;U0(>&X=8YOBfBy>)s zljJFBZ6r=QmNtD%`c(RF&GHUAADeO*j+m3uwMWO4sXm%O(MkrqTjpOlQM1Af`Ul~E z1OEWBrDE#FUDe}5hvr<4%EF_SD4-6h0O|;*@s@GU&=UDtuBRS7nAyG*i~ct zO?6S#$;s8R;=)q78eU-}I#RD1EOm{`X`(c&=`Q~Onf8-TSEhyR-C;<4jG&OWbWXPL zqg$cmfJ1$$1V7zsCc(|1^d58{+rs|n$4qAm)EDX%E5Kfiv{41OC+bS^9D>S0QudH( z9iA~bybkK}ElFgz`1dI-t-a41cxzlwA*1*mj)x)#46hC`3eoRgW@iF zn(=Rva@1){54q}GOebtp$@CM9{W4l+qSd=9S0@pRp$5-BYk!J>?x&q_(?fxqRT3+qCT!T=z;}Etvk8nG1uGr#8>TWOI9xu)Dk${xX`jmW89#TrItt?YqpCiL; z(JqUj$j^iJ+msno_fP4&jB_~dWH`dv!sQlJvH<=QhpFgvt8NqImut%tB~HeFEsG4` z9#Pq@ZN}$25>_Q!+qh~<2IMRc6q@z^C*^fn9^m%$dgT?|?jgv61W9ZO2Z)@a5=z@GU(FuEFe3?zSV-^s+LY z3}~b}^4vCF7GD(p&bvp{#38+(0-)U?DY@C=jm~! z5sKIreiF4>=ckFF{fvI5)$9|L?nZ z05MwmyBDC5}SFV_C7Sn`YNWsP$%*m6Y7fJy&dYy_Y=1K z@3k{xoHLyJ5jz;pL^h}XTjrT9Z#zB`1^nvs;yeXf8}Pk6__DX;o6_6SoiF^$2jy3} zKSn+J?7T+gN#iba4l9pF)5-BE5&}@tZN2E>Mb^Or`UMYded)sg03_*e`uNM}{{ZHE zFV6n}8}b^So>BY?0TKZC7jWW93qjDNj-Z3k*LPOLX0p2+EcK@chPsP~l0ZEv)nFe6 zDz~gX^)}2%XtGL;&$_m48rB;!mx@ItR!u* zUR8!02G!+8jX5=wT9Kn)NBlpHFN0=wAKk1l)6*Vim?MK5SW_dG8iKE*ue}}JKD`pM-FKsWASQ9BJxV7!&TQ`cc(ME44`Q-FnfdF_Y43BO)CkchpAFUVq_;1KB|A>)xRJ{ zeN_Jd#j9^^rtt}ym)61$zjs` z+3h452?;}6uVH(VZPKeY_LDkZFO6%tVzh*)4^6hveD%Fk=N(RxHKD?>=#ZUVD0K8y z>?=Le4^ckM68;82-L`lAC5UjZy7nYB~DBVY<^Nag>T(V6g8&3N5Pjy zsBkQJq?#v$O?Wi>ny9jkRM<jRHGc7PZ@VP%Tv( z7B*T_P99Mxio8sFTrEWdQU;{eCCSP3It_WoE2<1Xr6zca;H8BP*E5W;Dr@?&2FELYNz>_zSqb$C_S5(oZRxuouJ3oDIc`>AJ*=8iZg0?<2kZ!eJ{vP_Y{qDNgCuC%_%a-_wDIfu;wdqH>Zo~YEmJ-pUQD#kKbt{*`V2DxQF2rDbEsG1OBTEXg z)A+pX>yD*H*U4iSi1r1Jc(xE0)5%E&={jDoO;H8(zWZ4~&+zC&fG3$XXpOaZ9jDV(d1FDeeDm`_pMa4({lp$v|FQ~yh!TXY4CvewO;Y_YK80ahe zr2SahMszuIzj0NHh3UxarTsupbIAdo&1yU^<5RC&Jwz7sT^NzfZW=35Qk6-11^2v{ zb5G|s>a+LO=!f7%>@m9Yib*$L*?Kb8$Zxs^JDw^8m0p7#d;p%zfO2x>M*F_ng4gP4 zU(g)>V5eQyG`zL)N;kAMdz)lofR-hxuP~EHU!h#RI+nEaR#y9iUG5G|VMFGM@8C@e z96eE8gFbrM5saTHnUY9t#aSow5mAPmA=nkXjO^}2UP4w*x3`TkKguN~k(rLV)Mb%* zM<&Vjb-HhA)T5D_XMyW~6|Qx?s!il9BAOl{i8lqbl_dC$O;zk!;>N0vLTiY}yR{`~ zQi(TER<%vFuymR_()C20nB=+|4HlPsg%PDu&t$**Miv-3M1|QO7wQVumX^{OTy(t! z9LMGHjwR-1FKw=d(xqudc2_Rw2eWiNL|o+gc#2A0U`Z(=?jEY~*0)}rQtE-vlD0iD zkPPgqCrIArH_&+N>ut5~R(jgY8OdF>x#aQUActnG0NZL%Y~8R2pZ%29A|;9a2@EX1 zE|S=a)*%IyepR^~D);f}<5l-pR64m2YVGVko+E}h04YG$zeaa3;+&fah)t96EiHhd zLm^|WTpDH6jEYqfg`C+!q?%ESgxrFxMpvlyny^l}KuC3+z~kp(vv zJ!#!r=AS~FdZ^727r5pr_58ze8d*2ol_ti=#8tH%m67#jHM~u7d1ON^!{9iTt}Z*$ zQPD~twz?fYX3TYEF3$~H&JkDSc>LH;kcM0-7qXM8ZC5mMDoo{exan8u0{K(|q{(4Q zBdYbL{^u-7*8NcC4nGo9l}VDcB>q&}_L`NKPaO%-O?m1_(+`iOH)djzwDcy#U+t>r zeayIxTK<7nTU=x!OBn<{MYgH6*<;zEq1l@j8XSX-eVFGZCA_*&UV_@50?Lq^dYXCE z<5a6_9f{v`E?D&;a%1FJQj2aVHlNAYr@pK!wh~>Ae`1?p`V5nm%|lo4-&VhtaR#f&9l?^!89*v>7GIT7r?L;-&=&3UWJ&wTSUQeG2*~AJRQm z+-&_qs=!BmA>=-#ARC=;dseCBWV$qJ&N3403wsY8DO;?qNKhK2pKUu0*W9eXj!EIpSoQ4mAH~gP<0sQxbiAMOH?QIjR|vZvt?e{(NXZczckJt*pEo04uwtv8QTR#nhZm@iv<2_SoGUf>$)VQ8+;jI@B- zkB~h-3WVU2HvrQ>EWcm7<=3$&iI?^Erc`9vCtrugk(HQZ?m6fZQy7_|%3yCkAXt7C zpK{TssF;g50@Bj#Im=+D@`Uxc*0lE&ms3&I@qHO!;JH^P!V~;t&TSSu9UHfMDNFS$ znjNcd%9&|F?%XMeNX%nggrF=|gcYRQ_EiSY3pD=#DQQnOpGhK97c+;;*?9bePbmKY z(TW}fZzsoX>TsNwQ*y2{>j($vqn7wSXp9DM;f4m$2Zi!{S&mW!WF#MlrG&a}ifykBv9 zb=yQ5e;x`!ZzGHWI)xP?;@)DLuWu_k5T3uIMQxPeST|e>ilsw*i|1!Y zA2KakjPeuPFkn8?cfQA{-M$hndMsoKp#zVNAbc(CK1PhKLRQt54R;J%UgnatxH?+o z8ZW2rBP+P4@|3vPwB4b-1N6@C<*+|-Aw;Gmu|O4#8|u#lxK zq>0B7Q|{J^=~a z{f45pY$rW<8-F9kkBycaQii2Z0<)*HN(}9sgVz{G(DRaGV^XC2`(pn9m8Osqe!;D2 zS0~+)m67xqJ(zXU6BYy0Ryq=FKH4IxE2|g#rk)ZtVa&^=PCCn2;F=C*{z-bYs)TAWqw=A&AB zEYvkVqt=riIxK(^l5MmLbkdMz0WPQUvrPIDcie!G~?W0=5N?k_9=qS3kp;qg--my^og;yjd zx736e8H8RRq$1WoeQ851AHC2V?D+(SMq)tUDAlpEj|h< z($ogRuP;;-i;i*POKj~1*DDt3Sfsr}Do4W$>Jq2c0P{`7LGLB#P8y!0v|u5R%IekZ zQjJ6Ie2vFf0-wwAsQ6YtDb-gut+~qt+#rS8dL(qHMhQj#05bZk@uA{b>}018K{d#Q zAv+&+$8LTm!8ZIWS!kMuM^T_b2l!E}f_edSufBu#5=3LKA5uXc1SS6fm=xZc*i!P| znj4QF0m#NgYz0OtSyxbR`zv2{AfIO#u7a)Nq`CQ2q?4`1OWyJ|_J4@+IAY)Za+CU$ zNjITR&p^@F!0omhVC@g6tW+||`(ke)wUAwXNuO@HX5DICt@ThD0sOSy_8!J+Q zP1HPe=|Q4CAxt-?mbwE3MV-)g>~YS|L?-CDvKgfHT?bfMW_kEuH4m(VQ&4Ad+>;s)Oa?RrMd zg%(*;sU~`4dB^;g&#@{53JkWQyRo_J%N+>phBA$f&&5iLza_ON?5W21G$;1oP>n~9+e{1MTASaT@veNS7g%R*6BxpMLQuh6;?y@(p_fOQAMuC<< z56a3cp#$P=8-~A)R$lee=!?3Q*y_o4BPEYgOli`V3I717tAnncYnrwxJjEB<*C!v$ z!ph-%e<)PsEV5Sfx-F$CNA~oke|DWuu!r61!ZpVv5hB-nVKyTHzjqWI) zX3(7W@#=1z!Z##(nj7Aor%+mZ`Slsdj7IzttY0Y&DLdXb6-~FqXFUsz zE=R}Ik}ID48$*P61%J+{Uk#VozK=AL?n^ixO^!jIhZBu| z>opbB?!Fzl3vXUeq4fI!+&nJ{!Q&ARlsS*b!rESs9b2{Pei5lQuaWtGjNP$o@{4EE z+T5izK_t=FE%%k4ZQC?R!*VCv$NWB{&NQ?PS9>+&q#Ex| z7aNg7Sk6vzFDAlVP@nSv@Yhd$UfJ>0?d2>PUh~O$L1zctUNh_C?;k3+0?F%guOWRa zjk+_YOu2-!f+Ll|a;`A8oQU)BR}#ux(DXm2-8}t-QhLvUQ4+olkT#0cN^|(UQq%d*?xQwU-+ zjZZ1jDo6mU?$Vt5+jHE9iVm^F*v6%W=CIJ+jOCEt;qiqNZkE=Fxf?Uwhqk_hT-R#^ zg&|O(B{82r3Q!K09zM!y9K4s*$wDlGPVG|hsw)0gnXNCOD$se0U(T#5Z?zz~ zJVr7il^xs*jUl}pQ)$px>(Z=Yzb3i}&X}eSXAi`tpt4bFRRs@Z zUuP>U{%GYKx85$#eL44=86zTzEb&figZ}`(g&akimp2sckl6Sh7Sxh2tT6RqhpP=_Q`GiW z8#dlN4NJ|M!R_PDv(1<*Or&VVYsDCPv&9%`>5n?k69lRz8(BZS_*NJzwLVom8Zoga z^wwaEp_AJY`?Z99qiW0nJz0YfRv2csX!T)+D@Mfjr^r)EC|LHE_$;K4e>4K^prvj!X17$xcZYCa~7$iV72}dRfdS2QIuMsNQ5nthd#p_1; z3sw`s>d}X*3_Vz3>ca->Wx}-|S85MWeF{_RPBpe=n5?B~E0kOS{9a#$3ik54gpDNA z6*C>xRoMbZ`X9^fo19->C$y*55?nao)UT!oJ#aYOrb8atC===r(t>@7}6 z9%?tg-%HC{3#fm)oS1SxQd?mLLyX95jXp7P_fUPfdIpBXhYFM*R_0#~1JMKibrhn4 z$+j4Ui)u=JN9L#EAD4Ebp(gzUMwO1eMO@>rDPQPMst&t>zO^N_^+NAQJ%p17F4yps z$wIBFNb9Xn-;3%FdRCyETz*M+^$=1;hSD^>D=mb^tJJYDvoZ1wEF~$>eU-V*4MLk2 zaMV=K7qUgihP1Tjk?QMY#~T`J-k++VgK?=>N~qXxxpS!uzNLu|6^Dq*9-lldYy7cN zpl|M~8)Y{89TXv2U0Ci^+&p&2qmBdL)e5~h2NfMn$gO~TK^(xvv`p3hKQxn*3Fs68uDuL92e zvAXgr_->=EGNLi>ny(}IK&kkGl=5KY$UT1AQERr%O7@V0*&l5m?N|?VV7#G0w*LTi zWaxRpDz3s489)ALYa|| zA!ESluS}9}skfakd~!lE$YHjU;R4)gDI6; z(mQ+zt$VQ`>6-K(MiUUAHmDA?l)o_LP?9J3Jgf1D4=`QV;Z?d-2L89=bzENLv4&4w z%rb@&^SJbtr>n^~x4c(99X6TNzC~bU)8J$h7K4j`8En2qQoZ^c1_fMV1hy&3*wm_^2hah}LZN)D!sBrO7;iTx5QO#6?VGhD(kz2SA%?L3g+6 z6=+3u3O58e2ooRLu9it7?IM$vBK_;dBCw^kCbWr4L@>(2h(B{szM`}qjDmErIKR^`fW!)nx{yX?8ENN8O? z=&YY5uO7^3?=0dQY@1m45D(*0TrJ3l@0#riwU)%pQjAn2p*sFyYCLr%9_Ug#v6p6R zA@G3r~=^A5!;$Lk${$zC*~oQAd7{aFZa|w;=FV$7Nm_>PWQghnJW=^g*&Gx~SaCLxP2}*k1JFG{I5*&h;KyQTc>R}k>pdhIWac(48ZSK&I zZRuJM#54dms9uvpmluo5g^OK7mVP%VG? zRW8JD^g2yPte4^?HQWhFL$OlYFZ?$WplmNk*0%ou#0JvW`l2Z3DYL0F$Hc{D_8=?Kd3GB7I{W>#O|Y(m{0om7XV{XXh&)gFYxMlr z(ECP|KfNZnruz${i)C24nop^7@q^>#YgXmA9FYCK%y!A;WAYG<V|m^E|n|Rg(uT>I<)c>P(kZNjp?qaUhPbF zW+L<6cf*zv{>;uf8V^{KK=+MnZ;z_Q)3^w4-B03+BaZj$ip?l%JsnuL*P@&QdOpi^ zs_n0jT^_5cZ=>>hWbq>8#%8#;AZZWcva#hMeH#lK$^QVlb*_5aE{AiVj`dc4keiq& zj(kZ-+k>kW(x9@A>2tS2l-O3YO zYsjlSdG>$>B_w`Qbq29+Ad=IP6jlJNS#2dl{_Pd*%h8$k2T-$@!8~@x%0+Nq!)?|7 z0MgeZZE{C+wxKS;Hrsi{q{fK3h$G-zXemj*{rgbe0Ts{aJ1piKK`Frm-=dv-Ctqj) z)Dwk$@$^U*l_QX}7Y~ z3GB`Gh)C(iKjsz(iR0C_R!7%xTE%ablW%dY#x_g2ryAF0JNe8TsY!>Aao~R`OGJJu zwFXeb!)Dx+y$303Yss@vLm=oJT5Nv`gtk;4e2sm)rn_LmurVG;GgPui=h&^O{j{^) zk)L3a8m}G2n6OsG6QTHtRsPG=qjis~45+UdFqoJnp+XdAUfogciR+1mdxbpIOC7 zkfYT%Hv4*3d%i}Nv{!a$xJoJgDI3#kSku;NZFBdkdUB4 zA)P;Xfojia(z_t_y^~of+=O4KKqTAkqDu)N3NkejT2Zv6KH^VOohx>1U>qyxHC`eD zoVZM)4?t9_54yIw40;3mHs5f(W>}B3rIiI~Wi0FV-jkb@w&f{G@d+cPZaO(Y*5(3CX4^K}r2=|bQuEZn+&05)&Rc_OWE*&R ze#%XbR>xkUb5EGR+pS^<+8HRkw+dA72(^g-->;o8?Z-?DzT=NYt8G)IVEK_sO|bbB zZ5Z~Cz}N)!HzvpJsXY1%j+>N&`BoYpCZd)GQ%1{>(dytQ?5&t#Gb5xG7SOAC+dwKl z4BBhdNtw9$>ewW=xM*A4l4(m6;nZ%4*87B8!eOKUr_z@%a;>Cm_fT%wo(F5ys$EBo zF^7%*TvpJO^s;rNz1R4Sdo}(5jEI2hlvf8rKR1tBHF2;`?qDr^Cv7ShLG z8uJcM=9cP96HN4uJK~k?B;p^*_9Wn2aZEiCN=UkpzfVh>THR|kK4~q80lg=&ERR#HljunoDpO4Z=mvhxuwOGMm9k%3yW*C(rr5kQvl7K!QB#M7t>@vS*tfQBEfX-;| zZ4s2H;E=GCTgJT)fws5TC!TYfv-COBFO86$rM6TZH{3igdhaNbJmY4kk_$H(k|j${ z_Er1IBoX7G6=g4KYYi0hjq!Plz$GcnB`FVNWJn17jkxO66Yd1*TC0!O&twU5TRpPE z5wh8qeiWVUr^8KY=c`&>0Z(@9k6h%Eib$3GHK)g|7L8uL2yAiggSw?_Q(8wTFdD-_x73U3HtAGcj~5FURg&wA8RWTNAtML-e~fP^i;6R26R zuavI%_Vo7qHs~$1e+%<4ZN?{e$A2SnvtOv{ zz8l_-z;}K-zb7vVCQIhbEvFl8)siiuZ+qUn({JM3&YS%3csji^yDUM4O}t+=!-pUa`R6;HJdhaRbxYN#DphNEV1&Euw+vs-X&eF-UFrsm$F z)khtP#$8agl|-zcdz5zl5+KKp*CNL_qT?;0v-1ofllchh1zD}Q&EQ#4fL>F$^KzIp z`qCtEsWBE*#Pb+BwCQ2+uB<4oDfI(nSGW+q%Z#=IMQk4r0e5tohNpwJzrp|)IWTbOO z;73M1L>BKNba`Hu=T4R=I@{S*Z^jHYmCG3-oW~i)IM`qNlQAm^DFCIiz*5%m@chE|?GEL=mIo?A0qOm7~lrx!JMm?7E8Q%S5unaxE%*bmVSbW~$76{xl_j~KN zuKk&>7}ty(mc<(B<=8rQPaKiQVdeNnQ;hS8G8Bd7V$8|9%duM)3Uw%R)CymTKo+Cc z_7m}Oc^9%8(vDP-0OUO5vY7V~8!g=qEsJ^7DQbf%JMm>Vb7C%qCiXY6@wHy-QNJkk zizzR^PQ3X^btupG=hK@xbU>Hh%x-TTAgDa-EMYQZD3foB;5p@86!iHitoWS$d)#z$ z*SJMFU#5xK%v3gemojukl^Kc0`WWY#OM0T|c%RN}4g4u(9!4*$LVeF1{YqQAA5Pt> z?;G!BBH(cAQ;2oQT!$N#ix)D}Xt)8?sH81qi;zJcRnYOjH05-M9#`$cpx+k^EeC0yg(3x76?{{XA_kME{E_<~i))A`Il{cpyz--$nB zN0XL>tJ(Aqd8qx=0)4EF#zr!_JcH-0ZEQp8Y3oQ-OJ?8aB;TjRDt_!mzSW-jw!ig} z_lnePOfRy3Q!jKty*$Mc(YGbd44sXNY55$)E-r@Boo?Z%2-XjZ&GtW{8_cf zoo~$<;>^~((U@k*0Yj~=S>%lIW`BqO02irr2RLNVka`PNWCx2kCsSABVOAzTg`2`Z~%Rxv)x2qwTx}+vHt+5KKc=k49@od0QE=SD>7@vni-7``lJ5< zFsP}Zzilr;FXj=vT?6eE4|x*y8|-c;GNZLF-CkF%eZ)xpyoVn;-L~IfEowh=0IOC; z7b59N3fX?7iVd5&>^)wZz|~%r$@R*O`dHGg?2>ih&fpf5ofG?La+_gr7G(}9#g_sS zdV)vx(TyH5$`!PzFK?E$fV|MVb3w8|Hu3h>IpCj|{kd+Wq@vW17ZK_ObT+-IC#r_2-L*c%m5xu9QL>b^ z4>Nj)+eEVc!hb-A^IW#zS#d<41A1HSk~8isC73*xS+KQBLvA4Il`MdM%hDSf%Q;N7 z)7qbp<|K*AwBw$UmWXA-3YFJU?xcR*`j1CrsCq1B9Q2(2e3y3{meP7qtgR)E@rs{ks3i)$sq_MMr~HFflU=Z>KBo_ zys7C)2xXM8k0ICZt6g3NK@{6RAagfjYLYH>D(hGO0B)dB&i2h@%~PXkIwz*JB}l)z z1Ca8C{x5AB5WyK;kW;T6z=L~r6+8=BH<7!KYE`@JsC#JFzG%04>#u;UlfgG*Lo(ve zkGhTS1@}Z$@lCvM?xKYTlA714fsNc$l z*|f4{tJx!c{>oW|jeSXOI~a*+3QUxx!gOrtZAfii;9hEK`G*?7yMKint>vvM`6M*B zd1Nxy4nlggq>pjdt8CnW6DQE*aE#MSOCy5QAy+?+C6Yw+`zP=Mo-RzjB9Bg0Bo zT(o|rqT(g=3NyyHD{7e9I^L9@D*YJsTwZ+#>|jevUepDpBj6upA$HuRF;5*8+;Y|( zhlyO6>s72i9Rb#pmeWHp(~Isp{{U%3DlRg!8D7JwsLPXcE75rJQDE{rm7CP)f*#+tgk)2dRrANp5?E$XsPrjb3j&G|LpD7jD0G{Rf zU=oHRY*oQd%bh;KPFKajJmjvsmbbD%r}BKFPsYdoGG}8L|5!v^YO8)>UmChBE zq#sc9XcJ-Yt9omt9L~bIFh9=cN!u-urqlUtsWo7wTkMMe02>5`VV2yymKKs(bVY(|27$;D5>hg%WzPUjk_YzCm2$^maB*+A z^{G*sZ7n&P-c$OLq5ZU~u1)qC_u>BlP^u*{wAxBuZ1h=B-(&8nN{bLxI^U?9`3Q{* zrIfzV>#dFc$~9C>8@?WdYELA@MsT_EHVM;jpWjb@*(r6%jl85QeNn1;);x`)7$b## zWeX~$`!wIE7xF5XbTwZmWl3wXqxEPQ5?PsQ%iB@6tpQuBtn=UF$}1u^QP8O z6qL1O*yUS~9Y2Zj$Uv0}sPNJ5q4wOUsh$b4$eez93cYsP$nyShuGIt@{gHK5{lsR~Wfut7-` z%}U9~(CR&yHPDSr<1nRfD@szLH22Xf+LU_$mWqAKtB`iG1oSP!k4^8;;(8r?K&x$T zc5&`i-^157Q(2zeWpMl4`87?7KP#=KEJ# z{Re_0Kl%gMZPuCllRdTc3^6A-0645{Xaf3Kqow`iP%PJ}JhmRYt@K7ro_!+h{{S>{ zJ7@m@NeNH(=|=FQxw&0`p_K97Q6p(_=Vp`DZ%I{;vcS}x`so9Xw!`%g#N(N;LRRAt z^dAsb<$facjQ61x(Fd!IvR&CMjltNGknGux%Thqv*>hrV@#tv|<#LWTr1-fYBkUlNPDXahI7kPLP|jHwm(9C# zh#@ZJA)uo~6)Y^D4Gy&BdY8!vOL~OHL%G~qUex2~qF;hrj^5*LgFqK0z3K}_X@>pIwg<>^*l__uCPzZW!jKO2U+kk*$JoU-c?#0# z$X-L6WL*U@)S!!7)eRrnty$~w$~|PtOSw7AvH&9|$Y<`@sV^x+?JBYn_EYe6#99)v zLzsB1^QYBypj3Y@_fDGm15lN<Y;z=0A@)r_>Ddr(<=uircz4W!ab+1EE)}N^& zX9Y10gC{EFfWD(aNzZOCvMi&l#hS&#Wm=WGlljVzL-tVKz~a|7Q)y^j8&VU{n$$C% z=Dbib=iS^?le+ZIKuQ=AO6D*01nuxD2IK6kA}Z69uW*C`=YF4M2g@Y97(?dUU-_~& zD1LH)0ida6he%DQ09jK(*(1eDB+zdwzmPMH^6KEQOb0WvE`lP&(Xc zVN$@js%9vg86g+xptV9!$au>pi%XBJC{9TpR_R(#9)x8Km|R&F1rMUb^87a@neu29 zsvI>6SqLvi3s-}J-6|2dnx~*rK0RGaWjFEiq-?Q;g#iS|TRI?aQSnirN}i)}SV9jc zHkTVzK*4B#nY~3nb*Y*x!xfu&Jh?NF053EFI*o1j)ZBWQ=OGVLLz`6N=|~+=4TTNR zg2I1Mn%-!Fl`m->SH7mGo_i3DvBy)!TkJLv%*Kmj{-TY|ys95)(>AP2vo%Dh%2JdhJNk)}>V7*HQHu`gRpYGFyjor@7Q^ zB>7Tq&ZFuKbc`&bvZdtQSCTF*MEj6mZaQlomUj~(=f>l&jV&wNVXM~``54RoBZ#p* z00Pw?xno|!H89g+8jPhjol1qR?xp9l@-qq+I}M7V#Iuj1(@*L_Adzo7R=v4M8gYZj zZw22}@Oemg88s*;xlS$)m$J;uOhq#_1i828j%##ZAq$E7px5iJlr6@v6 zp_S}f-VzHY*}a$1I!ZPi{?cdcugQ@u3~i52EbQk`1vp1n1$hL{|^_jMRYZPh6%O0I9y?WSWK zfknB-urjhCpin?xkn2_Z8!)S>f5&mMx1nUc+vn3C)dO-8J|vrUr8T)&%6Kg|zB?-6 z>W{=7)tMt4Xk=#tsPz+?AKv1%-qHp(_tp5N+>Mp7!pZfQ0{{Wea zLCT5uOvwsT-PDyRolm~>md_Nb=@c`q%DXzYZz2vQ8@8`?{kN+B0A_hC7Sd>rIMY$w zQko{@>3)>VwKBT_r*Xnv8ESU&aE=k{&y}G}K0?&#DY^Ipd~46XLBiK{Z93Cu zq3|5^=$eKP=%+;oV!Zaf-;Yzy%p( zH@FpFqL4*)nmhT92-`tU=Q(R!IBs{h(N_HC&!$~k0a#JL!aNOhI-2g^Qm10)7ucsV zKTupx3zTMSS@`@~Xi;h0VjkXEUA4GMw-0@)`k#)@Yp-D$@<<2F{wYlQhz+;7D7r7zDc75jX}(OHB}G39?;{qO!}xo3!AV7(9GQ$i!2 zvunPGMIG0!?^od5beCBiR?o_L2)uZae7Dud)TeP;Y7ynlaR@FpM9HzEgtpevdng^p zQPQpT{vF0sEdHXl&VP}b@8VG$n<>xvdyZrnpC`G`V@}udQDrJCtsRj53+n)%;brXIAnR#eFEd z&Rc661GjjIpryw@+zprU@(_fA0U=r@-rl}cg`dot^t>rNjz{>a*+;4ZG4xcN!*cP^ z`L7njOo&wC9i2V>?1U$2K`BnECsD0f{{ZtHUx7_?euqv;4YTNN+7i z2aU}oAumS+fS*~vh#vq|d)L2kKh73cVy|us@oT^Qzub72JNz$1;ODI-zL`F6a_$w( zI0kBO9_5&~adY6Tq0RcCVJ6#)-g^W{ujFl}jm}4asnO8g@8XyAe-U{73Pp z%B-_s`u_m+58i5BO!78U?`ix(KGRY1VcDpZ?Zurxd9C>~Sr+-}U;4`b0L*HB@t8t` zCpZs=LV)+ym4RJo**Qu3zxQj`+vQvLq-V5+znx^i_|m@fS??~JO2RgRqnlKzakz8&-2`u{{S3S zXWT!jzZ56@`zikb#}!-m-_%>eCjS8LEU*6n8du+1`-k;4;EliBnScH~ufnDG2K*vy zo=cV&ypn#wTKAbK#Tfqpb!R{L(ffrT?${q`3JzVE{Zc*J5ALjcGc(&p{{ZhSzl*o5 z`TqeSxTxxJqQ?4rHNpnlW^f8E)i{cpmf_mUX);%GTeSag5ipsf4JnCgQxUnJuyIYMhLsKh03LwAS77`jKffZ=RF4{PkbUajkpuHl7U(*xtoP(=#T$IXy-LaURDH%v#RcHwax-qdg)OJ`s^hOpUVBM* z3tpRjNtP!LB9kUUy+tF!`V0A9gJ#!3h|rTI4r3SrsR6)3we-fS*Ur=lTE`=(gna~B z-kGS^>IK#~Lkm-bVK(u#Sb@OpxQhh) zD`LZ>vid+NABAC{qbht@7O!h~c-E067c_D5`Vuv9*&&Znwv_2W{vv#>O2cz3X3Z=S zhh2>w3iCpQIF78 zTTO=|wl^o?u<@o+Anm|(1RxKJ*Q~%Va81Pxx*gP7(H}58m6WL5>1``kcOV2Ver zu+YWHskMTEK2@V}eMU}B3Gu99<1B-npL2WbO8KFc0lAz0DoRTus21#{ef=wDWWr>l z#C81Ct3j(BHv1~l-8`r>i8!ndn3L!$b89J3{Hmobt}S&Qt7pWVVKb5vu$LTChMyNx zRJ)o#m!W^7$;->HV))|nV`P#exWe4MkHl|dTrVK3u19&sP8Jps!?hP>+t}i|bk?aJ z^Yv#<9Z0UoD;5kjZ1tJ4QPEyzt1Y@s^em{1S9BT}NRnz=!1q1G;ix_HEF2}#x zY~TF9M4x`OrOC*T?1({pNo}Wc7B=`;b*R2gHZA)*LJQ(^JwH*ej@@+VDE|PuK-cZE z2Ct6HX3KT2Y6oB0QTxa?D`uR{UdLp}CKyU|=&)=l*zMKy7r##YEEq-$H6q_NJRvI3 zEebz~6nO8!bw~=(n|)+!Cx^2eYtv_sd2-kL>QLOBouUrzKDd6V3?w^(#-{6((!l9= zLFwU5s`eY~BR!31`l25c+2%Lp{nHerr78AOoG+ zpDo2W<~^oVoe}`Qy!EEP({YHTzExzqjUVS39FZ;{G?jU50D(<@&5p!B)G7BsdOY3By9!3n zH1QwARB}d_Hy>^3S?Di9#-3TexJ0I!goe-mjOnM+A+OrRyp?j<0R?xDwz zYDDMBwuq#eN8%`XR#!yy0<7UmOs2YR^#qrlO^3v?d}_l8ukw(re5Ckl4o8yYMGZpR zQ9;w(XmZuAeHtfT&9%J6lI(wT`8*UB)0c8cSoqTD3m)xiqW=JmQ9igWb-q`%4vs;y z-5i9b3o_uxkd+H5!hwBsxTLH2)ME1%(B;1^l)6b|i#?b`l?A1wwxx9gO6Ya8To-bW zW0F=r*cd6oqNC87J_W3>O~br`g=t&B9J~pXxnMW@kDY8!A~%|XZ{yOLGsU6&S&ufd zmy$-h8cM~`V^;s=w<>M~W4n3LVewYfDCALSxbWpdvRy}w(j9{JnmSd+rYctk7Ao<$v4#yT!dE6;;8BS6X#ZS&Pc6(KBNmN4j;rUq#qqWx~(B&Xs)?LipY!d z?gdA10-aF3EhKh%?$H&eL>Pp*b_q z;1+Qu3h1Cg2kjL{IhG5sa4_z~;A!5m3J!z$72M z3VyB0u9+*W*R8&&jH|b-L~uC*Nhu%v?&hicEGfuxQtZH4#X?gpg6DQIEA1j z{1RHS#k}pMGWHaZvecA&o@tzXt-mQXDp!L_HdoxM8pOyr;5`m(*@n_FE<;L)zU3h# z{nZPoa9tREeUB%M5?HYkvXmveU#SL=cSyRXgg-_WN{9|Uu^tvWkGhn5QowlYBoSke zvfkCa;@k&Pk5G!&xh6~6iVw3A6G%#w&`LZ3aI5yvp1hkH_hN!WEr^ZqEV2OWs!6x^ z(sR?3B^|gSv)rc&)`7)sIs%r|LHsITYRxV}CzY4R%|d+uB0$~039mL(G;s;BA9$_8 zwHMUev&*p`lf!tvSwT|aDo*2aB*9i1}TdcL`?;K@gk3@zNYBb}Ne8 zp$d?)E$$VInY+|e_IiQI;27rBv6g}g^$BeNsQc7uuiWPQuy2Wnq6sI2xP$}gh-p8R zD#cc*{^gQuh)H%7Z;Zu`N*qyi^-{Fb^dG*Fi;jvMh)|j~Ed?NS0E7D|>uIuH#7!2% z3jB%NZ#plJkiWJkv1dzZMfQmMg9lXGX;!H>I+ATtjBpKtU5^q#yU9^e=x%LOG&3=y zHl3s_k^U_zQ0{GwiW*gs=@g^R^`yF(EZcEgNh*729wd);x29}^U4cQxzNW(=h;MRK zq!ojyxxe8^`*O&2ZFn3bJp?Cgbsc;{debV2Gd}VMmBchk6Fd!g!WSs zR!U5RX3&Iw=rt()1^l0(rp&XV+Y*3$lTlMZ-IEq>G~lF%Ci+SJKqOdwrm@-<6*t|5 zqq4i)YkodCZ&$Ukc3yMB#gIS8(WqNk>Cnyx54F>G3bX&ZzPaCKjJ9+K)q+U zMG0)mp;mQ^EB^K9sf3?V75@M>2Sfl5u+|W9V96NFoXyEl^wO^zn~E)aF^_QzwP!xS z*V*2*>Y}9+_ui4aYI5u|R=siRNLbF| z@$GC&t+vP(wbC^AP+pAYI=L$2(*o^tTq}zVB70lUhTuPKCq5R%)PBBVrn(YTUBCHV3Iusyr8x_*is{m)-3Bn+E;&<2&}}WR zK(A1h4KMw)o!hK$ON?1q9B2Dcoph#d4>cjl44v&n`Gp<7sBPB6-C3>7Ys>P@5AJYx zuCSL-HY2;;*zN1;gEa>z)MK@9CNx%(2A>LrO706wVm?XXit2N_`jhdOR9cwds2nFI zX|u^VjF{31w1UH`Cpr)BK%CVE$&dTz&$SoqH|*qdxAH)5L{m zVR0PEQ;-|^SsfBC6Ym6RSDF&OG5pUZcKj%pREm2wf|3KRMuwH78zEl}t>gX&mPN00`@YcsbveW6Ntkiq6}c8(T})rg1V&XD69+ z8s03b@y>-*LN@pRJFl{fCHV^q}()aVL9bP&Mt^jUxGqGW^F(u-o@Sn~3z14B;r5=SB zxhEicTXvJ%**b%qDoi|8Kx#C+}n1vB_4*|xEEpbyejp%!vHI=fAb@) z{{Uqx8aj=68vM}n{7ni}U?p1yqC(bn@aaLe2rV;gkmKEOEhxVe%*6kCyVBI%!Bwbl##eMjY`&}q&n)h zg&n%-ewD%ev*Wh?oSfgG-~3`8{zvOifM&R!8!z2BP#Qd^1Mb& zhS1uD(V7(vWABDHRrsCJ;-ZX;_{2Vem`E-nBk!NzfOIp?d~E*K^T{Fh>C9J36&+s zqqd+ev~KC_q-^ImEqU@Aua1s7BCW|k^xpH)+n?I?PDs*LMVKg4?45LWz~0BL796hQ z?KTtaYV9bZ1>6s(-oj-X9E%YdTxBePRgsR8<0?a`BlwY$byvJ;T~8PCzCy3_WU1Zw z_O+gvIinHSF4OP`@>-S49ON%@iA!EHAj3{Fwvl_;z`-PR@wHuU{HJpG=doE|1Fb^( zmj3`O<2|h7(C{;{J)55`$??k>ETUy8#uP$txS>l1>mvH}u6=SB+5T)Y&W$?b)gr#e zG26^uP3}{X=6pwtM1vhliNq(kn9>=3*Gd+qmUk+{TWMDH!+Q9rc*%%Mdr~jDMS6Sb z+N`cW<}kTDCll^m7hPD$C32?0$V1pvq_ol46t63R_SKDF4%(dm05oOe{@ukT@||d` zMo6nNztkIqahw+-#p1B>G90(iU&&?W!BjZTHMH*7*f%FbV0EsGjQ$YWy2Uw5^0JqA z`96x5jC!1K=k*7`H~g+vL$){$P^i)a@+Gq!O*ZA!R8h9y7q;fTHjm(&6tJe^m!tmx zZ}=Ql-^-Ctu|oHUKP$Ujnold^UEIZ$@?vG#vV5#}5ap`wA18*=K4Mlg)r$!$ zSLRCARIT3IY7Jd{Qm%86f|b$mss8}fvi|_Etoc^|02Rmds$NrHLziXur#h8J>(i+CHl$N{%%667MZY}RmC3i!9b+5oyAv0U!sFlUHzKqM8O23w2I7BHp_7=U1$h4P~ z^kt=fl3cyF0pq5kyB;p>mma4Z$!h9l_?1>m6O3v~n#X0+ExZ2!4NC>HT^)AM9Z|{7 zs3~+{r=Uw{o3d7v^>H5(e(^+%lLaV@H!IGH(lHph^Q>kIP|cpKF!g3kHQcW{F(!;$ z`PK_%n5;FHX}natO&;DWJWMgX)VvXfHyYA`hL-l4mPmLRN=eWHYilMpk-A%O=o=>9 z!%|4vVs|&O6)X@_HmHO#VfWPVMWiUN%9Ma%|+=Ho)1~Zc%SLJE6scJuB zMGF0j>sgb+8lz6zULdkeQ`_E9+agT(DuUInpyk5A>-lP_{D)n?7`(FLKN;Fl%$5z> ziqrs~PCeWQS5>Jbd0w~gx%!a%We%7KB2<+HC-R|RWlI1Wc?^ZZkBnT284vY~Hf{(8 z;CZO@qbl-lb66L|W}1#jk@2Bd0QEZi>A$xZTe22+B!^wQ0Qy6~(LJE8vPw!9W9jXbp^eNR;DY#%W+kWH_BKt*t<@NsepyqGZNv8I5ovJ~^xYr%G-` zBlc5osPuQMk7P=k=QoZSYC~)t3cdlYyeMmmW52ycE`aCxgu)bt!fnRp%I*wK!0B5=mE`KL}x9p`G4RG;NlXw{zPq`cI89DCC=$LDt4jjk#lQZE0kW zk+Kr7ETxqL)541k=cp9k4T1}r0!i@kqe1i+vJWDx!;+;Ptu4dhVg1!F46`fj7mt9+ zyaJOELA3s58;f3>_FET}l4O!T7536M-6GoSO+!^64sDJOIbXX?t$u>iy8^AmSU@(Fk`dql0nfm3CUobBJvD-D5I|GL&=8* zJwD>)RzCe`PF+SJESM7(p>U(EMa6{iW5!C|+La!^WkOdZH&so9T8yRa8+5*=mv*6* zQ|uLE9jUcfPSLff!z^Q%O}#~cJa2#Pp-*F=&)Q|Ih1zo*#?afl932Q31dj?{$2p?P znO6?vw*j4U6qG)#?<5dQVS9QT=~P>qO2zn=dOWYUETf@t9l+z08ACIhG2GB1_fQ1; zYtH#z3dqaKC#3P;BC2UA7L0sU9Hen49`JFsfKsF_I(^m7SvkYHI@;)})%jRfJ|=4| z>}F9(PN^!fxcg}t*_V-;blg`)5=)bh<^k2GLY=*QJg8EdL+MO#%H2^YdJ2-Q2qR4m z1IZaBALSjq)`zH+)B8yR;+1sajQz@xp<)xC5?`{%M(tr*u5!;{o=5{FzfEKW-^kNd zqN?ZA86o-TzQhf_T2m28^aEs`0WLiu(08K5^rhpX@!e{v0gTylQ6hMlW&WqL@%^u zFt(_Ce;2l~n^dMfY9d@D9YFH6W<$olfe2&eNKp#(?{%lGz^duR9fgeEJvwLEbs4Xc zy0wSYwQya~$7J>~lwnZh>AC9-g!HPr_~}Qm^*pMda=}$>WiuW`rOSwm0?O5K zYNguv{Z@8G=BLRmhepe{58n(#&4`+ZBP;c-I)UX}&d0_l9>+;zJN)YIt^=}mgb-5G8>iv@BsQR1WZUBILLrl%b%W#}s{`r+M! z9`3gcgs_8~T+n~%w#irHv{aYJ?UTu<^VG+uag&|y;tX#2f14!hZ%idWjT!uEUr>MQ z7=M)tK<{olBIlFCcwByx;gV?CRI{S$VK>W*a zpStF>_2(zhJgkI!gWTqrh6<30V4k}hMf4RH$IX!Y`uzb<^CZhy5%KaK)Y;-fQ*XAI zj~&n(pYcco&`-pu;oAIk%%gDLtchvs?Fg39e z#@Kt2Z%uwjOO0rle?w}>gjqu?x!wnoG}fTx!+ufAZ$)KEL6XC2BSfufxxa;|GFCpw zADlDXKNBA7n-tS`-KtgwlpiPbEff%a5G5dg5lKPtq#e&AKGaMthR6Bi)C$m~Yf<}> ze$d^VCLpiZC@rhR0!Oo@LR^K9uvMd2UOX%uB^9vJQoG-Iz3=zXXF0z?$kw11Rz@9# z-bgCwk=IIF+n>;ct%$lZEh|7uv>Q#lYdj_$Ua%tWNyItnm@cpPR1gTGLK46U-PFt3 z2`EC8);Z}NG)i^*t6szIX>5K1EUt9Tstg#Z{{ZHK$LOkoHJ{uc~adDz3s`d#;>qq^#bTa98$-?F_S}Sr1;>YlR9kJngEduKLW_Y5Vp}|+ zLvMxBK>JPUXDi$6GODS1o;a|(7&*p5!54ih2~qcgZBleK-2{!1^kbyCDYb~wGtD}S zC)5fc^!DFcuAaZ<>^)Y@P(j$3sYwU;3vh#XQlLC4HhD*&%ut^eMcm^6JX-pJZ5mC(1{DIb;5#Oxafh+wZHMA}={*M>f=>gmQ-^#1Bm0+u4FJX>RAx8T3_S2Q2XwM=p$Ub(WU^lo7c2~Bf zkhh!2$>}z=l~SA%2+@?u*G(_uL%|{QPi@+7La=2HzGxjkx`bjk4*pwT*`;B%k>q5n z)CFIZ>q=g;*n3_rcMW{nkzvxSeYQNTLywsn)TL1<-Xy66dxw<`)Dc?6Q1f6N%DFxs z6tks_OJkrCRno2G+!LZ}N~ZgO_ww*`2V=3kaz7!(@-AiY;NiN3h7)4yoJj>GBgh)^ zFXSm~+gz!wH=c*Gd#MdI+1M^4$sx#Z3501WkC}ByOK&KnU`vgT zi5BtFz9ac>@LwZC_h6gPTpw2aZ~0R~Yul8ULz(`rJ9pUL{AJ(en4VNIyv{o+{!r?t zsaT^G7b$Df3fh&69YEH*FXGL;?jpriwPioRr1|X{9HmZm*@+~4SIY7C@%dNQ)5>C8 zRTkQPwcGM2f3lwUrL?v?iU~BIJ|BrkVT0hov2fpC)C_2301*Mg-WpY z9}4-C`EU5UR=Qu{{R|%;{A&h9Qz+@xRz&@^95(}4$8st&SI1x#K}XO^HAJt zYHo!E8xVY};vP?0x^3}Eqv}m8=F@Oo zOKAeY+V(ZnasL1ep2Txo?pAI5jZ*P4oWyq@vEAPOXU4e3US=mdlSiqH$~`r4pG(Nq z2h((fs9(b7p4s@`=O2$a8K6R{WMoNh;N8E!j!FnhkAE@n0k4UGa_ab*}vv{L21EhvU^P($j4D_sY=5 z9x2Al;t?a^Vz>g~q+M^4tE*##?zso4uW92Qx34(Z{Jy_K%eI{q&lW{scbVdSu$m&s z?VN|@E%YF#1(K4}Z#LzI^dU{y5D!hp^a^=x_(`p$h_CY`YTh$W*X6A)zd|X?`+MFe zu`hBF#wB8Swlc_eJ1SC`R?)Wm)<)y6gD;aMWjy%1m-$HifP$o`k9}HKW>-S4{bwHLj?9zkt)`YXD%-`i&|0(GS?Uknek6U* zRZbPC+^ealYe^!K)hvjV9V}{jA%Rw9ONJdh!&1lBW0VAbtpeKnZt#1Yy+^;&;3^lEoNw}^7jzf`D85tB~QV>*!SJ6Zs zmn2c@uPlpX>PKr5gg9g8S$L%?Dg3rOMf?pCRNxY@s9ZvuQj$ifBpl$C^oWm~7)TZ_X7DH6uxJO<=?Nk|6j+K@i_XUimGCRMH>CDjq2B-nklu2@MV znW|BL0i|IAX@}z&tb`;Lf)wZ^bB(m7Gm&-kdJd+EL#U~!D zeoZsPpubrOAo^t|OWV$t+t!R9Mi!f#yYwQ87ABNL&1+^EBCw2QlNE%GdYXv?#MU!y zn5`N&t+Y&(@eUEn`Sww)KNQ2em74ERnrWoIeG#yJw9{ zEb5rwN{sNlLwWA!=7Z1MbC?`|#FP&w9wT3{3iDb20406p&aGAbul>K~dYwPUQPQPb zw9{|V7B>py9kb$X?8M<3ir>TN$=Don=dR@ud)F<^Yw{P%GLvUn!tHU@MMeoCcijef ztZW2X?@WyRoQVoCT2N69kPXxaPPJ3Ot)F+)z2)Azr#%ia&g0@sFDnlp)Ju2#ruInJ zrF!QbeB*^P$2m%Co?J@7Vb+OG^S8kH*J)LG>TuU4n>AWivvR7kQ7SBKSpciX)~Oh> zNU;s2$99uoLAsBHMWKjEg;+~ykm?qq5RySvFFfRCHX+KsSqt3T)@P~{xNmkclb>dICaN;X7EO5WHcZU0Pvw%uu3R}%FPYR zDOJt-Qj@ul!kmI0%dCq7NxK^ffs&^@;(|r>p!=rcx{jI)T7go!^r_|ghgGiO$gQ0d zZBLJtMUADBO$r(mP<*TGRE*>fMJ2;JGA5HZk@LDNsRwe58Y z?=WlbWLC8;#?-W*%jHsT2wvFQ-c6Z`{EJz&ZbwZj7Hls-wbLiv66{t3Cic>RY)+`S z1XV89+Jh`~bXjNMxK#I)lD*A^TTe1;ncn6gyQ8(x;O8Rbz;KN01f|%L*BuGfNcA?} z{>#@VySG&R4#P)6>ZQ?>r~Q(M?nc*ZjM#R;GiMcXnA8zd{5Xb zKEkQ4hJNXEIxw6Lb3MgumyJUy)B;cJy&*my)iI4;UTxu^^L&Lb?Rg4k)U}eGz$qkD z7M!E?9ejis;aTvjhZG1sNVdOqN9|c3yoiy_Hig+ul_SI6eCmGFGw&j5=3S($l(Y@U z>ei(i99XW##AVozPMVj3lDWkV+})H5eVSAC)t;qomt_%$n{C$#Y9ifIt^8Hm-(~5_ zTTH68CPe4p9gyW zv-L=!*`^TvG08~g_@^h%xxFzL8Zt_~Cci2{_5ym>S);p6E4jvU9A3`E`*b0)`*p}# zxLjot_qbV);<>>|)b_1#B;OiM9Iss%gjS4XLj| z%37YFEfHeJT|6sZT60L3wDH*6`CjpRTHY17*^z%`nM@_B z{{RK8QpvArnc<19L!>#wtm75mHv0*p&cHO15dXF?7gr zSF){r4~00Suq7!-(Jekr>`5JTpc8YD^)S}7{7DG&HWfKbph!alkL8Hc_Fd{3C+uWs z?lHYKr}kE43a18Ih^EP~(DkFMK>KJU+bLe%x_Q!W(6Fo^n58EE6hP9BMG%LQKm_a7 zw}5WpyV2wo5|tFI%dKG>BF4elET{{RPgCJgafl@;1tQE@vR13xt?F4L89as3m6C^8 zLXy6yDY&+t$^_J8x}oUGQRS zk?Ag|moY1Z@<7v3Tyu})-2BON;$W-Te5nWb6h5mcvsP+sFQXb}c!qx)@wkC5I={<( zqrEB}Z}8K!^3Z+y2ySq*U1QY{r{cT_7E?-3UZYyFs_4lSHfz}lbwWZmRVXeqX$T*g z2HU>x8Zw-4YulJUMl%(XCAoODMUtRy=oTBidenXY00-R3QJ(0EPT-4Z7o#kxJ{P)a zy15}eu0I`6*B>`E$JgnSw<3e9cR#dI)GM>Es4DE79t#s0m}ew}{Jv}DB#yBp8zB3M z_SJRTuRRNT?pW>}^tn=64<-t7EFb!fDGm#H{3ff?@$NAgPD=!Sen>K_WUy;#wP?Ge zZNA!Wi&VWutgcb+fD3nu$X1D5WfQISr_!8%5mzX7z|>)&xT2^t(5?5n6<~iFD%+SJ zwym+Tz~xy1O}3(o=u@cI%G5=;pKxDh^U(5N%s^1Ooo{M}-lFt5%DJw_c-ddno8Y^d z$uPhnxkX3>T`L#-X$_t(n-^}QntaW*zreUK-Ro|0kj7PNX=GRh3!}@ebN2jsN2)p+ zzDwuPmO42^DGj##M2o9Z>K9;s>aBcik!rW2F1ae1T;>>3n?^PiCcja!TE81tTOAeZ zbJFCafEWAjA_(+bN>&e0$ng6p)8j$)`hr); zW)!GLkRye3NJ#$5I;9J}*1?bEWEBNT$gOHR4QxLOBG!u__NDa|i#BYfh9JduONu&e zY@6TOXm%>lO5r(^C_HyREG2WoG0~JYQo?Ux{{VMTX)TT9mqN>5PwGh^;`pXDd*!nB zYDgd;Z96ac3Z}mMWV)8MRZ=X>ME1}Un3$$9ss8|`Rug}>ZBtuRru&waTLsw?_U61o zK4&Ms_Pb5kBn#idl9wAM@>s2uyR%tYY-};PPXzg`OC;7+$4}@=?fUWD0V6w%letAn zb8WRCsD2dk(n-CV>Gqm(y*hwf$lZ`MzEPBZ=#e=K1M_ce zT+>n2g6xIt-<-R!I=MTIr9p+Zqx!x9{gi)a9)KRin(i@HOi57n{{V@8fB-*bAH{tD zNjA*Kla>qgq@$^@)0!@EBwL}gJgT9!EwIU`>C&Gkyi~|h|HHi)XjJmevs;b`-KVr06>Zj z`ieEda8h6SqanuK7AkZolCQp&`8{{VF_uE2D+Q$v-% zR5Gc4k|hvaL~_~;`LZ{3D|DOwG=-2&=L2;&^(BnFg(bAM(w2XDZY!_8s_m<(X$w6A zEENRV)PxTdi}d?xX-5N*tX*hff02V>)S4t^kmEZc9TEZ5?YE6b-4?R3M%C?i8<&eJE^_j|2NRIQv%#@IS8jD|0so-*#Qlu%*QNrNp z7C;xQoHAv00P+%fN?RrZ;iL|Ub!VrKN@|)R>cwof%=83*xE*O|kCBM@aOk(%K~q=z zJSmUcqPnJI+qP$NjaC^chhWL2cybA4ZYN}f$DG(h*R*Co!NmpOK^>CEEO4j201;3EXl!Bj_ zO4fn#5QTO7sX6j#F{Pm(EWRl&P1_+w!=lCQuiI*3t;+G7ir>drL~>odl)sPpg5eQ= zRFxZ3)bcgA(*ic})*=y*(IpoYRwKq0PD<9>pdx=5t>r;;puKT1J@9am^ zi%jkhDJyxoxo4eJn~zW{ZSU-+sN(8U=!G9EYgmy~X6HX8VNTC*?mi~fd#yxPN*%q2 zDG}E*6Jh{}(p0ZBsNZ${^!nC|>wqh1rHqm3*^I=6q&SaNpe~S`bw2Y={j>$8u{Y`h zn>{Zyq`b?BT6Ekj0JUlf`3mewcrqD~b9K<8be^Pg|= z{{X2r{{ZS>ITOeFZG|^KCKDyjs~tn8O-;}yE`cX#okjcA|!i(YlRggjZaGDx$XOF zZn?v!^e=e5XjuHCE~iH@&T9pY#ka>e$17JDeWK?gl|6%y$0BjItd@!R6m-(OyJGEW z&$>;2Vc*fKa)R|mW0}Y~8$JH)&Tv>!xa)wmkrJkqO3-`?Wr9Ei+TeIqy&Pp$)Z+>M zVN15GudJ6sG~XinG0BcW%OXnd4i_-uT#l(*1jbmLlv?c-?kY&WfFB-}dHZK8T7^>Z z$8^6c>G=Nu>(s7$`jSoIoTJM#y|rA1usL2Yoq1U_{LFr0+S@Ly8-)6eAIpCode>>j z=x(Tcv7A;v%Krf1a+_Cb%2CuORKmC)F?XpDc#xZ zIA+$H{0!CBrOWbX0iHqh>Bo7D_Lmcuq846yit;$rxbw+~R&)mc0F-;rN{1W#gMOWO zaLiX{jAYj%@JcowRJxS+CHj2heYc*tytkGeiYP560+jsEON zP+9zY99^Fdqe|b*+FyYK=bYn}XR&bj{0x>-9|PgZ^fCC?Ws~ZVZR)xK1w~zKZ(Sz~ z$I(;rl8dr^dv?#F`V}1SCu@!%+KT@G6PPo389y*wTh1xnZ0d=*9v<5C-aB5u9?0@u zS9awrpfr9@oPf2v3Q0oLExe85-XhBftys^DqRxz>k1w|gbOh#d^0RLx?2~P&kgWF&dtCw0y`|39~8$A zz{+jBC-;@?t=C)h4LiQ1Z)d227}mRt0Ug_itLxY~{8$@vX$Q3vE^q2fr*&z(=nm}_xEZJNp()|nv_ z)YQ?V6|)UZX_00sCXCz9O5P3Na>QrksD_bnKQhU(K=zYbY?n}j60lhE43^iHkUF%J zv5kcAOob?_COH^b}GSxc!NU*;3rZ_BY*w>Dlo zd`N){Z7U?Gj}Kw4x9n8AqnC!)k5oSyW}b8-E+2X;$qC$rYT34$DDlTzaDM(f|L8ZlTl*s?BCD?OqLZG;-y zzMXUev&@+wiyBE#3A(O2_)$f06>c^`J5zNmAJcC-#9(4H43mf9h_U5;FZa~@nHb5E zIQVI6uv1_G=4i$=9fyHnttfrO*qul{FQsgswoI|S0wzJg)OQM24a&D(`gEwfWH%j1 zcVwh?!x0q$Q>M7mfYhXqFJYkn0NrHTsUgNciQyvv>scRJET8&{)ZF-jO%X=S_A9^b zr<{b`B(5y5MT+5OIzZwyS ziIxwE9@@f6JqJHHkKU}vUj%4pAaz|TNhD}rLeJ|(r!;3Mj;fLzw+fPQG1F}bz?%p z85(ZsO-UeCi?E+e*c+&I6%dRn!`~3r+EVj(H*-%>v>>bQs#|%4-%iI(slhb5l1Tg- zQ_I@iXFWdxrqq#deW@4BLNBWivGB1o_@!LRWp}VosDeP#*;G5#*Jo5-aq8Hz zcHVio%=&J{RN1!vBaQCauYd}!dRjhDqdH3WoO_{}<*7=R*YbA|uxtPdKX#Q5wU4U~ zxha3rA^gsK*O9ghsr5Af04&`%()2W=4^nP(o9q{Bj?8o7^$>uus#mG|jp#3w@aTfA z2}$J$9uquY<>w|koYadOc~N1?MfU@56=E8Hvbk7VRU42YyBmw7sD3rS-YQEN$T@Jg zzS0gw!g5%G(=tuW+y>&5kL>9|mmwGEHC}|`y1t{iJbyIuEf*llZ~4%q6LI~NSnb0G zr=d1CV~6G(_@2roO6p2b0siXS-H)LN@bX>Q?XDoQvdgmE+(+lXOZ~Nrm09dh_Ce#k zS02)KIO&i8{HyW%MQis7KSEY<5niKc+;$=rb{a|rjg!=LtXp;gd~JTBPhc@*p%Ni_ zrsm4EwdqVP={|sCsR;E$k;bu(v$ggRV~(g*ih_RaXiIW&exW|n67GmTUw}d`y0{2H zZOv4hbRJd}b*;?H3neb^2%G3v()b)Sm8EHEYS&G+x7$~B^74MCJ7)_Vbwplg3dS_8 zW2Ip~m?U-gTAq(FXVgjXbb;5yaTijO(7#ZEDadlxh|7l82RX$MpnWLz+o3kn)c&^o z-$pBc4N_S^XL!;Qc9dHEY5xG+y7ww<;+WiVM^O%1!b5?|>N0dQQl;N zrq!p7XS$OI_R=&{g?f=?j;5syGFWIwb0iSuicgrWc;HtfHKsUK(4vFo;Z0`%-Wa<% zIS|y9H2AQ!(|_zWDN>mjTZT#E@@#vF*^yupdok@^TH5~r6i#Du zjxmx)rJTp5my|VdAOUrIMN{3zUtj1hYvpLQ;%H|l5yc*L4r3?$y3|JBi8jB0){C!g z@n4*d%@j@kMjHPBZ^U#osmN+rXbY@Z8+cHxot@DquUktaOotW3hL5IJgt&hwu?fGQ zT0J|{#u@{67_rl-0LkzazE|?(6nF!rKds0W*YRnrm6lhx(i(8uz>sw}+kdjGp&+TT>hE9EVZNi33huJ-$sUAdTrVW;)=TKEueNLw9>pGTBh(`LQPs>3|zYFm9p z%uY~jR;{JOVh6ldMo;pw2>ctB+>H4O>*$Ci-bazhJnG((wj;Q8 zLJ7J1HLE)K$V1a5Rh!mH{sAbQ*BgrfdNMLDi*G7bh3Gc%ifBE2r;mQ7(mS`3uuLpF z3|sQ8TZ+EklqJGKCQF)|SV#_e{{SMKRHyPrItS%LLusuo7$P6JTOlNJ45WusvBzZ} z&{kBTe{Y2o*oH7l7FZcqCY=?uqIA`=Rj;JRho_A~I+67aX5;7#M`wt+pLp(UyHVyM95B9ZG&)My6b&{pGYq`L$5@!g6iCxC>c z?RfyE{Q*_~01DdHlj_65=tLwrW)-%QPz%e~P!EMhT5`{D>v|rD7Uwlf&U#E^Tw977 zF3Pv;sVxu0Qd`o`L}X?&(gloT88+%~prc1#2A1WL^bu3pRs3HtR)2Qpwc`VRHnCK> zoZJGT7ZR--Sh=^~OH09a2^Vi>8jl8Qk-ha6PwtHgp*?}0WrZUV4DLf-qkvecTHcqO zvNKy^?s6GxNoX{dkdyH!UbW#5p}%@wqH5$5S+Hm+?R8QcKifx?6YhZd%jy(|$)_n= zM_N^Wr6f?k)f1;}htte*?N~70^qXp=3k!AWK(mRAiwJVEis(CQon_ruq-|r=B_jU- zeOFtqJrZuDL%y{-j>+m(0KC_B+z*zIhR_B+v%c!z3RIDHgGH|VGKWKRY(qgQC%e3&OF^jc8I8NdH-CriqrL_U z)P#BY$#y*#opNVi0q9J~O~6q3f6cF$6dCc=N2W$<-nLTDi9C(Tva(wUYmT8eBVBFN z-BefUS0D3axvOs3FVY!=jQi_J5aPy9Qlb=8FG@ccp_nNn|gYC_f2F1}m9c075}ekD)6x}@BSuW1(QGmAum474chePkr{Re=>v zvj$yjU-b?N7y`64B65S8d;vQ;pBoCPb=l}!M(UV)JXjB*Un?!539;O*XfLe7f-6F}_0WW7r+CoSV{wY~NFGA&{h z-lyB8bJC_sma*82LeeGi{EJ@)r6bkQ1t;0~R#xX8sgtr1k|NVMW+G@_$ZYK+_<;Wa zWmwb7-COQ0E+V#Y#l zO}6gc?b7tjepft!m&KQ%2+sXJm88mvIJ9s6Sp@Z~y>FREqGeUz49e(K8P3Xbd~kXA z5(B|K6LJqr-qqFVe6Fc~Vr@S|o8;=UJs2Y! z$bMaArGi6ZX;Tsi^c@K+;tOF&DUCwUk@tCMfL>j%gD{yg;yR_ zJ1giD-5NP7rl;?%V9#({=7*X4%`t*L;IGX1U+mJg;!E8V`HzeIR4GFSy}{!y6Jy{! zYEzG?DE1@`I-r!4sQX0=0~JprpIrH$!m|MUg#L8?>HH~N2ZXXG{K3yLYRnQ$e$Gs{ zs!rQWh6whb4_6zw-97`DH zcQAKMgo(tIJmT?^MJaXDtAsla(<1y230KY&$zlCNdPa%(MY-uy2 z4Wnqi($j!lVCWB1SNob%nqF@RuC4e#%7tIgLu(?gO_anSO_1<&Ft+Z?ZwrH}S{e)0 zb((UVeYG#>KVIL9&Inf`?iX&dld_XCd?`;qlUMqA5te;)n`}yw933>$uDH$*dbp(v zFqhY`uJm;=n&r41E9=ZtOy&+j+Xo{$w5mU>8d{QEMbuTd%{I^<3iRGL<&l!^c?Tu&390l% zGb!8E;$x-U!zUl>jnP>q>ry-vKbDebNv8V>H1n1uhgRBx z&`MM~5nFLg5}QmW+?FK`kB>?3(&+rB-Y9M@494XyjMdg0XY-9b72s;TmKP&C1_vTKue zm`BS+AC;7zzSSQSS%7kzV6tym*&LIu6`ROJm;8;P>%q;bD1J4sWpoN2N6wSk(T$Bi zR9WfoP7BA;muK?FlF(?QL|adFN$T`iB^$EA@>+2!Dpmc0i5e0N?a0MWI5#Acu9h`F z0dF=kEUkAXy?jl&)cv)PS6Ienl!8>WXfJAW0xH(TN)vNoS{XI~o`bDsOvtcl!T?FJ zJ!&$BL8;{1(_OZt4c^+wMWRL#9n*cpnvt-CXzm3j+K(Ii>o8$B#oadC55HQT7_)sz z4)aY>k#A9bOu88wHswa42*GQT7_iYQLcv-j}GfELxpXQe-+` zU;PuI6ngbm0m|}Y8;eP6X3e0g+FahOtJX;sv)q-qpQ4hYD zo9^T$sR74|f;3g{t+er`jMM$38*=;R|jgJef5+)^!CB$y%SWZdo4RvHDEL=*7e+glfG%HXeIu)ngi z1k~9cn;mQ@n|13^V>R_7awOSAl=SFo9^wd~*FlHPtJ_)bB7P%vP1O8V4|KtslYB3r z>splZV2PWqohTENMjCwa{{V|nvNeNKKHsX}b#2X=9-W2`Wl}ByAALh8#)Lnf04;la z%??so?iw^a4dikr+X_igKW!~5QW#aTD>Etl#bBsy1M<=`*jO5XicxI_x23((e%c5$ z(Fw5|jc!?e%|j6#i0?R3l&7wpJnBx!F6^4BQ|y9p_@+);ujv$|{#6_6_tj;*jd1Q- zYjGCI_9YNIZ7tOVyyngNeqBe~NMG_LAJf#mqvLJA%QeT+MYK;g_*pH%;ua2jy4w#3TDVi_!n?!okt zZSlXVo>u|3qVk?CkK{;M__A&8iyZxu>&V>P+gjceO125=7Q3*tZ6Z2Gj?`@Bw`~)djZAFH-Up#r6uYykZC{ zLtD>|(N(U=uc-e3u)P9j)7%#pB5tA8Hnjmsz{8y0QCamM?LH>-CB(<-1NZ5F z(g$;#NlU6LU^o;k5O1g5PeY4Ku#NoWoVyIqFp}Mt#C@tdrpIynsPWJTE=$M}FP}Fa z>dNH{3T)jmtewJ|qkxrIZRtHZaD39S#U*;nZpUz$5)JeX7mdZF%oC+LE z#0HkpaYFv{(xYd`>#x+5enoA*fh6PkF^;!ZmeOn#lwQOGP=yhMgZt8yO6x5HH!?!Y zuDum!sYp9bweYLTCts=0?AK)bG8>cQyp|^E5HgQAlXA9_wOkSGH?D%Mjd}EG6gxWm zG3uKE$K;%&9VskNhN-d1u?_=!U%-lu`gGX8D>A-EVh{5ymJAL}hI9{E=dtcs9lw|l zb*hriKG@5n$}Xwpsd4*TG7r1(l^vyu;y?Zbn6@gxN|`jSt?D%($;eVRF)uZ{j}r_hW! z#9xbYQ@zw{w)Yie_SKkZN_=;<&>X1Qc=m0i`e2)nhoP;;~Si6Q!DJID-Q)w0Nv=~n; zTZ#TQ+S1ny_Xd))3w5=&+sYQQn_bVse)^nsVVLh+9 zw)ncc;MeXJuTp1??*?)bQrw8>p2_^-LdN69^o86SlIU91@+n8TKF0S)371CM&Hn%{ zkZuCMP0vcCyNI1^xucnc&{|1yTt!L7NKA&h=t8M#=$c_%^VFE22ry0u7;F$EB^qN z6uh@wUs0;`p(GYY1Crww7KFH{?ethXl5NmwP=>`*4E1b>W)Y2?%0*V!5H0$WQf@Ej zL%&|P+qo!g+Xd7(oUB>R?0LKH>2%WdrvTEiO?D#LZZQ^R*BGU>9|AwRto8Q3rFGg# z_A9x(a&#pu7Po`Yi=Wv|LfFWxw@t*2IT`j;N5+fwNdEwxZP~^M55Lh2?Hu22JrtO83g0b*u9zl5h9c zd!+rOg3a;#TPY>^0X`(}0Dd$n$+B*kBxEm+!zm$YcWLW#qD@7n(YVSeQCaL^{{Y#? zQCP6LdihZP$$GJcx%4uLIT=Y;%VSoAn-5V$>VDeEI-(sbZ0v$DxRlB2^)gO2%OvWA zD&+m5lec;?k3hBYu21taRU3?9A~$5iZNhpfM{zzBirt&_ANv@uLF+g;P5f=+h11Z4 z8&GV%P3I(|s-x&V`F4p3^g}l3U)KO_t5eq8WyBuaH9tXr7>- z?4mf;^(3sljuQrBLh{xXq3BOq3d1K{CF(Jknrah84ME=T@9jN(^mtUeGXxm*5uH-%ds`ORFng*5?TWjIKu^XJg6(t*#O^_fetXfl5|4*drOPLg@IX zr3pS8&Go3=Y!c`i5tO@#+R7L&ORet+R2^Mr{C*77VF9nH8Ha{(L zNw)QuKR1xpsxR5HU6FgMt6J{KYcbH`Qt&OA3t00ZI(`%#pOE9Qc>HM+_+6`lq7^1m zY%QkC8w#txAi%r}57f zoP?+4O}t<|1G^|~#^f$HrlI-T+!T@xQr*OtTHOBtQozIaUmcryG2X@c8%X%K?Y+Ej z`zxN`;wK-{bewm{A!L`5Iy;E+UeL;j+yj-jQ0JyrBLw%g=moALUoHx79wI%cXHf6MwySVFXEGJ(I;^p$LQ(6B2+^;C; z>-f(bwFT`@4UxRB(-u}MflTw5@`FeCAcZzfjm5%Ay*01=ja<4)7+`1oeK>WD@&b3Y z8Teb-oV09ZB&J(xQi_kcT8g@>_~w5dA>h%GHL{`$KB-8t{{XW} z==J$ZEL~1^{Y&e82C=jJn+}gf7&p@$M*H4TRjuT1O=7Bvyr2-jVRXSG$M-sNRkJ3` ziAd7Hoxpvj)UMAFT)M@UG;hv5BuyIbWJb2BO|Ht-Ahr>I#;d>6?05Gtn;p|~VtqEE zmK2=>rAa8a*l080@cOZca~VNl&162&L)jD%dVs5b%hVRCgAe!l5k%IdEl1`97eqNfuSfAd24O3P2HM zg{+>0i<^5XKW1Y}$S`D-#&HWvVCm0os=pg6_1bv6bx_vh#*reLVI+R}~#X^vmZ0TY9ABkq;Parg|&x^8coL46Z zC6sf|Ck(=IO{k@Iy_`V!wD(tOrA)iCjN00Z>4KZ&Q)1-C*Aus3vQh<)vbv2KPAKE~ zCkd3+XCBCLVYKW?B;V}z*H5cCCDh~gHR9d~zw;TG)r&1lY-zIh@T;mXDJ|9UVJMub zw+Ks$^w-kILHN^j+#zLMdJ=Z>NpJ-vV2@#@)R72ec8R9rUcG8IBTB%NQ;wH6xS|Ys zvRjFon*bH|Qn>0lM7k0sPEv^RO}f%{N2y^6bQ!%lDF(+;QBlFENEZH6V6a}bwIE*L zfXIcY0IN~u4uqBI^fU8>`+wu#LcyNs{h36AbGo!lb@LzfKlxh2couDD*S}w7VW5L6 zP*4MUqCLT zSR@cfhyuKa2cI9~ym?@`ud7jJXanr{8cnK`6=}t9;ZS)V6g*n7K6s1~~ zl1;@InE3wWXZaJ8UqIYn$#nd4WM~PP)qsYL>y>y-7L(Jg;3n z{9~Lk^*N1xel<@y31@qkmCa;6Yaq5{A-26HV0tZ~rGPeqQjj&bjRRBT5v_F3e)%`Q z^95%{kxx%+=L@gA!Wmg7V`%t5*V$dI6}U$kI9;3LWV3=G`c#!z5&)-SRnBZ`;Jv&l zn6MIA8zDN4deK_~gk6A_McW%ze&btKGj62N8OGGoiX0nMo}!MPMJBP)jgE$hD%KcA zE=5e)x6mJsdN!{QbwW_jVDYv3_6&TKdCiA$&U|00F+UKX#!Yl%9?Fu+1U$>EFJq}G zP@^uQ&NvdHnm0+XwMh_^u*WG@+v$7M0g6_^o~$s<5Ye@0A+2l~ky>QpVzxlS`c@!< zG$Ur8AN*RS(9oSo!SrYnB_DwsQR3n0SkOMX7kT(Nr0r(XW&WoK(I?qaS!VK*Ba$UQ zDP?Y~sGmBT0o9v8gC`{|h)~)uNsz6rwmf`lE#Q=6mOswn9l+1|7v!}qds4FFB$SQb z2H;qa8XY$Itdp|87F=Db;!0>Mk>pt^E+=j+2SNeryMD^Bsst8&3%BLaeljjche|A< z6Ck;8TPeQ4LF=VKz}`rtmSVD{T_BOr*02%3$^>=$-3UP+x@}U9JwazE5?XoH5E1bB zR?^74QpiP&L(3o(oqC#@t(fK1I*vFL2gA3_(f-wo`;08s2`CmR3a!GBzDREV88^BU zZy{>e+mZK?v^*LOfwrcGwdIgL;U400}r4-oeSr{&O0Q`5U zJOlQGmyt*s8q>k0DFa{16a^5g(#}Ch^sYs-cDA>+l(T4J5b6M4dBu~lcPGM@meNRL z+HIRRHcRHkWhm3c(5G|HLv=$4GRzlbDJlnWwza8DX1ZL71YAOtQcGo2tn5p;-8xO8 zR|AU8lCR-36c0jlu%xZzBNE1rHL`7p;0P{XBC?cq*eOdn7W*;1UKqyg2V4*!vC)I^pDl4KO^0{_Z z{{T_A4KLH^90I@!ZmMfcs=IFHSNXT{@7Xe4^mkt9NEaZV8&#bWNpvdqBPK%L#cw{K zJBU9JCrYq{@>JPaJ^1No^8|uFDeF_n2|*#UlJ(B%zwXfG zr(C>+W zL;Gu)-TpC*{{TZf9y|F)QD2>8yExa?2XC`&Hi=G1z&PLli7oC(^Z3w6x3;+b{{Z8y zx;m$mu+aWX)3xFBELJ)94$LH+e1@7IhT-qe|RjNW!GY zd=+1Mx(%k_eb=q3EQ;!h&uY1LVEHe?X+aO2s#dAJ!29W_MY1+d2?8X@i~uAJs?zrr zXG|nj-{MCnWbyGfmtPUndvDWEeOc2){lPsifcIgC)P|Po-c{VHYH0fMrpej7PkPs z>m{){Aa7c@cM4;raNmiz?nIEVegkx?U$(1Xy887UqBLjFmE&YgeM%5u2I3^xJlbq8 zZweKjQZ49Fk5Sf022y@zW39Wi?f8fm8uXyYS++TC_+1LCA>0TUwUj*SpKu*p8r#C= z^>eSo**rf(n6sL$uKhu*cN2@u30u<@9q@i8mYaSw%)UQ%dy$XG^}Rw%&)_DoeLqS| zWAmt6$KOzVEztUrmpQFJr%)V+`1(%f<0p93n-A@$Y0$p{7VK+-*OTMQQ4X*YG#=_V z+S-9F%2?%$-ZC1K^4^);EL&O+yNR4moCp!IK433Ld2=NHKh)A7iLzx`GhxA3a@ zQFMBu&b`_t(n1ZuFp8w{tfHjbQq{^<2U~k^4o?4~sJ2>BLt zTFBZ-KFUhUJYP=5g?A~(^FE`7F&Q<5e4L=Bze|B)-*rW7SB|Ekt847g&HP^=ib!?O zS+l6OP$`IAN1%?*rpkTt4>4srN>te@J_o6$<1~?7I7!P4YN#?BDh{9)@1gPOrDDG? zp&3xnB5YQQ0zDut<7#rNgsr85(FQt=o=;5y9Vnjcg&b4vHGhw`_0+?k5Y!4U-86x{ zWcvc1#>rt@OKKiOjVYS;A|Z8FCN)}QG^CK>N$M@^refhP;?nmJPBvR58C#o_qM^_d zZBVj^v^!qx;U46bu2f|p$H+rN;OJFgNED3qan%+Jlk(`$DqaH{IF}&1vBhc_Jq4)x z_sKjkHqi&zj=#pFHc3-v!>JU{-6FPHtYn-L?5l%u`)QB4Ko+krM|rhUM-F*Eou#fD|hNj z?6FB)=DVTll~1!K5Fo1;{A^BILYY_Qj^fJDeV-PTgltLn7>zF(Ox{%Yoz>`VNGAJ2 zlVU#6YBVF|>PE)m`l2@{EX*)U@tZ(8lpQVSPc2saF&XYfcS3Qwe;pQTQ!hkNIyu!h z^U{YcNuqGyuQ*|*G8jC7-4`g^sy$Fn)qTc>)vId~PY2 zaz2e3mV{qXM)>>0B(4&!2UF)vPjdwR#Shx!s!*|M2qavSsi3{5=n@=!MGqK@sCXet z*6P|XtxfGsuH+t!GuXm7Zyrc0Qvj~yuEy5=lpow}`jU?yU(hKR5r|=F5pl>WJ|R2q zZ|0ctpLR`n`XSrcXpw+cR(o;D{Kal0 zUf#-9akfFBD$w0a<4Fams9sN{%Scj%{e%lvm2zF#lG%&wk0TBI#XB40Pd3WG>N=p` zUt!jz8x&u1J|0~#Y~O6)y0oe0epzS1D=Mwq%g#N6)5JngsO5dO#{g0vb({F7ZNd9R zNB5gElPAMhk}p5D3qexiWZYWMK;m}+{{WVWPc`)`?ciH|85eW4Syz@zvXJ5Z;*}n> z{{Zih1N#JrQM)>oDL%aCT_f`}hPM7Dm!Cep5NLdH=cy`#+K5opm9q#fZQAZQtK8el z)P=mmex;p!EF#$vX*--78U8CH*s>-N&yX{_kp!%-u)2|+t9(@9*5Rk zdOl;I@TIAM&9qjBxx(zp-#m(mh`V~ zST3#}j@)t<@w)XTm=O>k!_TBSbnf+{N;aF1Fco79Ct_5(JV66mELY~jfBc#;pMW1hK{{yV2@`ZJ)9FS%4R9J72s)%;f7 zW*;%&?PNnX56O(bmV0QhzSaCUpzVJhd92kkTK@pZ4!)MaxpAG@`gP^@RJ^g{QeXIl zNf$sKe-OAe&Rg-$S6Wz0$s+QvlJT1z-WYA{XKJUQtBz-rnQ_7JG#x=`f;_>}rAHyR z4wfdcHnsGvS%XZAN#L=i>fR-otb%U(UrNTF%T<*;+`Nj*_-g$K``x7DjyMbRWT>mF zO{a24vq4s0>Lx0_HlJLCV|H%|87Wj~kL*~`x@>Ls((9XSH|#M(zy1qx7CT!&b6uz_ z@gC}j?$6W@?2FI|FN>oZwF3=lS_X+}TeSCzI^-iHMtU=Y=tS~aSMaTuQ3dB)Pe4L} zzuQ(+#x_2JH{Uv!Rgx%}>{9xT$DNO=W0TKuk{b1-;3G^$ zzlQo!1#NIPrW5ySrQEK$2PVKtF9xy5i0Z++VOwl{^>?YGY*A6S$Wq=iQXGrZaV{;n z8v?acWZ&^M)$uh=KBpbYI6lQ4oCsTs53&kUPR9Z3U7oGDXCb{5%9lROR<{<~0F$8= z(`opehTeR;6$WsdtIx{18qQ+=?5}5_sFebDd#UP~Q*?4i-(lA;@MG(!@~4vZ8X78SumV@)=`22+Y25hn}+JhC!-MbepHe|R)UoaY2iith_VY+C)|-j zq3%#fh&+rGWT2d)+vR^{g;i? ztq|B{q6LqSTUHt$2}k#8I2eSoJ!N;${8sBr6HLmI(EP&p=xYpSK2kD}T=h1a{#wk2 zjBo>Wve7!|PL+V)aiW!d29~TeB9&Z`Yg)4gvLL^OKws^sxa8JG%Xps~pDGOCvTr#w z^=-Kz?dh#$ut8~TT0T-{J;cd{C2?Ly<#n|bs95=mqU5-mwqxTYnl3asJ9XY-@WP4i zH!+SM{{ZCc(h~SmTtkO=QdQIttBTy^BB0!E8M~0)0kiIMT0wg=A~?P^{vqmvlWxFLYON+g#I- zW1~)r#Kk(Cib7mlgsn)iz0g(pPqb3f_};Yr0 zBRxB|N^k;2hUIFRPpY(#r0MLn zYE|Ifc8N(y+JAU;6|`;1vNt8I`dZ$~Dj>j)vagDb77d{g(uEc*9HWujFO*mo^d8FF zL=pq$TMJSo1n8|5r5j#eg8MM_1s$8Za(gm&2Z}9)CHC% zl3s&YtT`#tgn~j+RJC{jJnD0JK&wtt9XB3ib%$dTR1SqVp*te`hm96TswZqT3|luT9;JfrZM!fB1DWK5B9YQ#*D<1j%%xf2OWZ*;Y8pmd}A zSvME;7tYKu+4N#Hw69NuXa!~gxe>fi9hI0=xl&&k*btjjvgbMp>Cq`j)k;1>hUAe2 zQ(y(O<0=VS6o8?*t-{2h^gnG#g9zd>QgIVeJ|ew24Fv=)!04ounuU?d2~)!+d^3#7 z043=G1%%BA}~C$;LmXW=(FK(3Awt{=F zAjC#+tR=*P2S6wuz!!K;IMfh=6|rNlzKW|L?0E@CjHd0l-^#JnZOMg`abgW(s1$6# z>8Cu+twzBa%PcV&<8XYuX_SphSq`%)I%`Al2RP&+nYs&j_|ZTQ+MCo|Yee?hza}iH zK-*1xO=q@5)8@wAyMEecP{6UeWOx&Qaif|x_5FwMt>D4WsqC#7YwM!324L`zI&S=H zFk$bRPoLvk!G^+J`r4r=9Zjc^qv>JPs9$wO*2y{Gthp8ir_kf5 zN5-GZ29%x9=r=@;cg@dw3+^=F+q98u`)Z#1a9crnbt2DaS8vWh9XilswS+S&Gu&#d z)9W1%v{K)9CK4LRZ0=G*&BATxP?p1CR=P+YvgC`E-`Xfn(_Yx(JVCe{YvW5+V~GwoWhcOR(EZkf!i|$(iN5E?fGHm1OQ~cea?QSB z^zJ06q;v;boMnR?8#PqL0!orWtn$MNRGLGG`6<@emX_)@_&^|O?Wn%5sWmAM+0M#f z-7S_}zvmhdXj6n5>}nTV2b9Ua7r5N37?NRf2{F_^Dg%lj+rq~6TYnL)V(Sv)U*vmL z_LD3aUHj%OAuY~vtcp@TB{+e*?-fU}@eOwREEjseIoVy=EQjBu%4{_|2kH11T6CvQ zx&u|0{4Oi>vin=-l56uKPY1YsZ!DEvYEP@8kl<0b+pR9RJ8M|L%sJ{Ud2B2*ym^g_ zL+L5;PM4tm+jD(GK2n99|)#>h2jxN8^CtbF}GpvgVK!NeuFn1@~i zqSmpkGe-qC-H@L@Q|yKk`d3U7YKDnMKN`k41OEU`9euYn>eEu`f~{>hyr47Pz~pw= z##!j#jZ%e??eMKGE)n!2K4#!~=6SW;%AV|!bWu}_H^02mmW&sm2g_RhM4rzkX+mT- zV5^T)sTC^e!@vtKa(zJ)7(!Kc-3v;p!V+l;+gDMVL+z|Bmx@o2H7trsEeBf6%}P?0 z-Djcp)=CMEM`l9nn&bO)DLyo`tLPnUb-<%}2bd&WTHQ$LMv_d6yxAE=sb}Kr(uC^z z8vBU^A?Bt>czRL_b-lqN#CaO8vr-cD9;cDEFB#}a9?h;>Xj9FlasauIG*{b5Ej&DW z7ZKXbe-WE}i!BAzt|ZkZDewnFQ~kE7_d+)EH&)Y&CYV0z&49Ms%0^B>pAbgA{{Vee z?R;@#^1K%sA1IV%iL91fep|%zsZx@EmA<_O;6McVRYdgfp{Dwyp_d`Au)AS_Cgx<| z-$R^JP93s~=O74qdi(SQp8D`14N2aQcvB<(*4?8MEWbZ7g=0)#7jc*|#emxIF zS!&A}Ow@OyoHR}`kuh?Vg+`E7F$ zydE9k8j~0E3n_bSb@tNH-keb6H6taA0p)h<6(;J3C(|OsVzmeDrz_@K?j?5+h-O!| z&P6ID`rEJZBx!22qm)u9Z{gf~5Db?SdN%F1QU$GXtyopCq#*R=5*sHP^D837B_`IU zW7JYmz}<)D!gVe*q6q3elv`-?i5SZdsVj?}OUb;pEw{vJNWE7Pg=TsV7m$Yk0QpM@ zDg7k*R+40$u>>zH{AUWAQj>4WQld5cDW768?uA|Rk5t2We&=PeO9@)OZG@3_TqeYQ zl!ng_T)PhHbCyTHQslsQGmB=nGn8NQsS40;x|_ee9V?yL;;pNfPFDz$Yi6=0y` z+e^5)Eo7NkKNCtywEHTz?(MDONM>4f*s_R*PlB}W;I@loeqFvFc&i$@7A3LAKSH{l z!SeCc^GT6oT;ixGx@^45U2}*-$KVofBowiH2K$RS|<%3aeW5nB=K8G^5gC?|J9tqo(OJ$8?gkXr9>7jF$sYsvirdx+fd{{Sq- zBE`1SFVF-gmwQu0)~rXZLV^%d+-3T{3blm+?O6MaMu_?$=*K+Ow*hS|2bD+3g!YYo zb-3VzWZR$`ohmN5NR+0d)NJ;7@5>Ssl3fF!Ngs_f4QTp+mebBKXvEG(FrDy*F$1fP zDJ4hj6}vUkH4TQ?WiBxpo%5ed>iyEchwiHNT(gV}w+O7ZAbCbI8bXf5So}Ud%3?Z` z`Vd>tBiRmjiD)a;o3`B$iUQx~w6_DEBJ1 zd09>VR916SBP{xtE<2JE4ysvKQ&zObs^U^8-E55_@#F^z{#*g$(w6(VF2ix-{X(h4 zBqaX;05*?c6$fOWsW{gZ`wAlk4nvKkB&~NFQaa!4rFOPeaB`FESZZo3WRzD@Z6~t1 zag9z@)>RFct+fkij=eQDu0#E2OMWnUf7I;1(RL`QPB0&Wz8 z_EDG0V%8*u-^5y%lskxWIDNo{i$)_Nk(i1QepX@?@iMO=x2-R$Zk(<1L3XO$jXn>m zD914TxwR~|)P*=dGJ#cz`%QIUd99B_jFz6}P1u#qvFk(0jUh@w(P=9u@HH7NSw|Aa z8MwX3OEJz)mF<6cyT6Q`10I%SPsc533xx6t$w{&_Q=Kj0UU%|;jB%2SvJVnHGvdD} z<*J&DmRIyU!hbqa@=L^J$NP09 zQawID+!*-xh`UY&sjYkki2}8;$VU&(u$ufoU;B1o=*%B)d!)o@udkTL%`|`ll%3jX zt@a+i+gAy(%IY~hM+a@K#O=Z>^LZA2FB6@|DU?Z=x8x|KxDch-U#+*DDF*7FW`>vC zN6@$+J-w7e$#Qlv>i+ky+tQranU2LbcxKmgVJJOvJ`sh zV?tEjHPH_{>r1%|k&xr^SLn0I8iOT2q`ZPU^w(W#+KoMgx+Yf3euvX5AgMT!lxf6_ zgHlN(2?!`q8hG0D#dE$7vlpFIO?7Cm@*HLq2UM(#R%Nui1U`V0N8MJ{@7pEZt(KK3 z_Q*H=xq^{zLM_QXN=3i6nfu#*q(5(~=zy6X-@_|f{#RgDx6BNmY22--z=r-e#*Gg<)Gz)t7D0=BB0FSy(PNVLL$=ZR|J9pz5J`G)3+xf zxkHQURe5Y&HLXP(wx_FVwS{!r{{WY%%k2K5y2;_;JGiZoN`EeZ8obxOaabvKsDy|# z-aQp6Kf||?y;xIwWh&E-SsRgrijCsmmr+OeMJ2Bk#s?P|olf69MKkV^_97uG9}To$ z#Ur2s=7OZyys3|J9Q0q~dWi)(^sT|P#HUwWn>Nzy2H$-j-54Axr4eM3Z|C7c{Lir| zw8V;E7Sxg9sjV#fkWonY%{rR}D1%c>TqhGGw3MYsr;Sb%!L{6$!I|n>#^h6QmP5DD zl1F*Npg$Lt5QJrr1YU+)T#0Rj}j0fv~I%xoGYQ3q~b?#^n zxg3L}NVT;0){QnX^#SUybxzozb+&KoZ6^AYK%8 zQ*Vz_Z&1fmUPz9(&5y(PQzER;!cxVzSQ}GHITCZgauhb&)D3M$jYPfIkb{gR#M@}n zvNbjdSiq9;p+KyBC(465gE`=f%Ed2Il@UUeu*MVU1AYVO`#frKlEJirYiZP-Q-89F z9553KT*M^rR?J*M?I!y?VJ9V#lhBK}ldT4FVAN%i_Vszzzz$J1#3Y?Z zQA{S8gXZOn-ul+^W=D;_t!{nSt>E4bx2yN7Fk?rAqq@gHJZecXLF;nwe~eQuI5chZ zKjQtB9GGL(_L|;I053q$zc5^}(_d|9!vaUQ@vOYv zIVS!sdJTQ`nKngonREm>@z{?G(o^g+u5%YKl+3)6y&78JEh*Autq|g=1h_t7C-bJl_Nvdjc`W0nj^LSC>{_lPK`lA$P4D*9 z-STg#mup4-lnwqyT_HpOJOvxxV*dba%nS=+)@Uq`XIjs=W-dLOoGR~TquEOQs@tzm zbw<6+_X{pL_d`|jeCpN|&*e${w+XfBkGVpz(vU(Y8Is-CA!!TK;3~bTYj(zH)5mbA zh#5)&RnE3m^tB4RTL|kzIT82VZQKd+y>2qcsjqE6Kr6Ut>n7|ZZt6g_s>VA7WtyyL zc;r=^A)$X0YM0*-`y)khW>B&NJC9N=QI8_uaPi{OW);TP(cHuol^%;yLBGC>EuX}P z*sq}8Rv(hcZ4uaI4YPYJqycbz>Q8S72ii&c3JwzLSxJ4bQNjQur>Xwx1zd_xsKerN zgbxOql6?rf{kQ(gKkjh>`z(zA0I_lef?2UWQ?%(@{n8-m;F``wM-6$MrO6#UEz*j& z6uTOHZVilhxYc@oqLdT)PfME9v9n&K;1tpX-+UkJ={{RTBCa?m?^$D_-D!}SMpth43gZ#qEZE8;2 z5o_sclCYJpsi};#bV)a<##t1yrf!y(uDfmSsisfc6hp3s4L@aX0Am~rdg?!>^sYi- zn^wj!cxXWth9HYhEF>tZ_UfNHTG7}(qpwjaNGM4Q9R;*Bjzf*xai<*E>U{-Xz>}rw ze&mrDMlJOuapOJd?`3p4X4<{{@3~q&!^-u<^?iW90=qp|dUAl$$WY=`VTVdzKw#Vj5!M&*~xcD{Dex6Qo?oYAZ#fd2?Q|%aw2VIsa zKHD0rw~3Z|7L{`mlj=$0@(4KqvfPv?UsS0;6G?rokmx`6HArbT90^xD#mMkF8nC9@ zAXeE#Ci{@tyjho0w4oux*L8*Ts~R~Oc?jFZuc-!`#3Z8XN-uNLt?T4tl9v81UW8U& zDnv_#G;9W<#8DlpeT7!E;`)I}YUE#O9cyNLm=apsJNGL7LW@xePa`EV(}zJ~PKNb4 z!Q>bA4NN5RA3&}zH^}f0CPo`5>=!}5z@u-wr6sYeT)V7{)4g?lnQq}b z?a1N=+T;;Ym}F?F0U-{7?;3s;O|ij6^@(w<@+%kXC$0ArjAt7Pi6u}SBjaVz5~6*C zX;tfwwjB#c%2Q`ijJ(r`Stv?oHg!(qg^PRW{>`8zb!_QWH?&-D0Di6M{_5ww#?Ht} zROs-UQ?ed$C;*iyPIVs6l-+vvPqHHRAqAE~CU-9l4MDihi4UWxFKO491D zvwoyje>a|>rejDAC;tFdO1jPe0A)R1gcqS*ZzC_M6<%gcrCLMo$hTCc=JaV%71;@U z@{sX~tt5rBj9Q%Wam5=SvW4uY+%mQLWHQ$)&cil{%Td6#-+&u`=BRz0EM^;)qks3` zHgJ`uZq$yYDHr{ezu8oWp7q{?$h@Dpl6Hpj4y8)`q-ZTrbW)L!+1T_fEIZuw#3jku zhYB>XHYfH~PRwdui(PMb{KWPY%$q<5U3NmhQj}~jRP4B22?Tt}00-Rpa<03a02&0U zlYItZWO39eGpw>j*S#lGVhsvf?9<#v=a|54A!UA>DE#Xr3(#Z5PEXW4If$s>fMvVS z#-Odnn{-QcJvIyeH1&TUn_?6B3W*GNzG2Z{ghwY1>ZkE@`E4lu6@TgveOK-Z>pOrKY280aB#^7&s+?d+*$hn0IhXq-5at% zG~HY*G1UT>$S3s}r@L83l3Gg%abu&7Cg=9iR|N&w2k-VUQ~ko^GgMUMrsFcN(1Zk^ zyweflYL9VCxvN;aJl8svC*3JnrP5t(zpA}qQ3m9`&$#x_$d0tF{8*TDe{$`|&b?JIW-8iATcPwZkjJ{S#1f^&P z!2T7e(^@4PEo?gh+_Q=vu1H60TIjGO>E}w>2zn1f*|*f0Mw;9~cd}lnO^)IR-BlLW z`ZXb$%<|=W|)@0{6wx{zB?gO*WJcidL%p zYF-G>CdHMM%b_+{vPXhQ08+7|EHtfXV(v<7*G|&1Ezq4k^hm-hqhj&8P4*bO9(=16 zH?Vl_y)vwV>}%R`?hBERLT;(jm9!s)v7&z(VjB<90sCmK><2nu7l;q7A*48eHHa#` z9=flW;8)*$5F@T~zB0msTRy7EutHOFf3ld4ov*tA9!90nAhb-1GFz70Unsz~^*N@N z>uO%zW`^tBHg_?HfRYfCJ;R|OfRXL3$68n<_R>H}+^O}aV`*WAdTkcn_|&1JId&1Z zay^Z4dF4ft)W#(xxlAc5Xpn-Nr0xWR?Wk|?e)Uc=?8kWCW7wLUd!xHJx7E%AkZ}{j zyOG48;p{9F9U9s3HqGau5)mNN|#Ji!aY0$c*} z+i(Qj_!OH}C6?JlzNO_AReXB`WOmWL?lP67whE2Hldb*Yin+;p4EBFnNIx1$}ejGr)1g>+gM-FeS%(MGRq0{H&`bU4~yX;QF;Kw(!Y(Fw4(h}2$* z$oy(|L6PGAQ&m$@N4MEOvsZpjOAbk5uLe$=!QdR8mt^&W0@*0xFUqiOP z z%y<_jr&NEg4@&r_IqfH0rQhmLUHXB@@%~Mb;Qhek7|B^EirtUT!$~k^u%qxehb>{D z)L%;H>-eQM;G5}c^jtAYVtog< z>TUUkohzQZ;CCnaSr()kJ@+-YB!V`!RdcON?jVd@e9cwIvT1N#lW)VN1MX43+vFtf8gnFtwk&ieitb*3<)tJH za~$GM;+vGz<90iMmaxUh?rsXPO=M+}cS}KBY^6he3RlE|ZNiBT{SDx?ag$-bl$OXO z*pO}IRw>~TQY7e*fB*zmupTp5H~F#9D)#WLV3(S}ugss_t>Djbj}sv_x`@AywV


    &+-VHusC##q9t;#t7oMf?X8&b zF;uIPI{PRx?r3J?mm~pyZASzgupkAstdz1$)74Ln&$iVZ8yDQ0eqBBFhK1YstgkN$ z9OA=Ggq5ldx}Vugdq_I9pcJO}0u2J%{()mIgl<$@n+*XJm72XwS28gs5{B)!*RGTo zs=Y=fU>(>_DP1jUjJ5DaQ^@fUNJs@YqgPWGvO~l&5C@$+tHSCFCTu_S{{R})ZNam} zq@K4wWd*U3lhBYsDnKL7gDO)c8DIq~6s3g_oezZqRWyUSAbK#RN*6&o_=?msgHgdD zgNH*YxkjhiSkq{mk{wBag|xPuNGMn!k6{+6q>wJ9dk2d}#Z8?xYW39N{{Tg%jil;+ z+MSmVnd1(Wc(-x_((oJ+B{$l$dEp*>)Hj=<>yj;2ql&v1;`a4 zgs^QTX$-91!rdye`SmO+jz-v~mVqKr(QkYxSBOpMHKy2kO=F-@+%T>Wgkzv&oSqb?mAiCy)IJvV@bjkWrDU(2&7$JR#wywf>ETv57xzW7 z7^hxgX=CAYTU*AmRb-2`qP3W;o;l>ELu}iNP&YW@4uC6!3j!CeHp^7qW7ah zSOF<8+AnRE(D>2tN6JPmQIv8_aM~^o({tlRRhl%aF4Xh&f z>sZl^4pAVJ`MP)-(V&uFNe6bGRV2;iMfZ{}pha&iSH%sRlmW0;PPM-_$=HX2EQ@V+WFS9W=({kNEho{GXsRF3)0&8)*BL#kek@v_M*ml7-Vi-qRMw})D38E zPX?g65Dbql5z3NON08}yMtc|>j=oCsFUls)3u%QG+9uwm>`3vd`}QAU?R|U^6)>f! zsdrM3RUK--pzpSYQ5;;|2ic%ByI2T2LnVXuO~Ygs8|V*|z_?&?B)lqtS| z;})2DE;S0-cY=JWX-I0i^2t;jQ%gt%+mqpX6_e;$S6U>oumB{fJDmrjMJV<>M&qs+ zYmT(iLf&Yemh;k-mflIdLDC~ZWlG?2j!-uHs)QAxKj#s9(ia zCrWA?A5at5&tN;eUoRk#9w&CX1q~`N1x($5&wY9Mq>S<}nc-$ue{JRz6ZPK=`$UkAG7K+?P3QNjUrr!`E=Jd6ZZJKky z?QUZMONfr3=ZUtTbwzoMs}Y5&JC-|A**6;BtxYK!@{eFbuu76jQ2JJ*;GIwGqQ_yt z1hiEBL75H`hf)gL0bqW6Yv)YIk`~zE`VmF&e@k+`V{Zgl_){_DlM|l}B!&v(iWdZ0 z{RODsb|ARMHvU4S{$p$O6xZFbAGR~f;}D!G^(7WuKbx%ve3SG+-xD8G4e?B0#8oW0 zZkk&D>L2b>HD4D56l;iK6o7qFO10QX)9j&s>oofh!-%gP%)P>~);mcDPP!l2Tm9}v z{{UpK&TGhJ`@Gcoop)7n|;ET?QaGMjmvQ% z^s@7XC^{9|_STwo$}6BJd~?*Z<6{_=9jQ#LJj0^sSRmYel!Mrkmr}+wMZ=)hMirs~ zY*>D?OxLz4JwQUC#brehs~bY^YR?G;@`eW@K0S6GuG8(T z@sWyAeFvY&!jP4;y~!s}bt`7mk3p)mBQH|}10-8xYEb>)bQ;i}+Fi-Zjcc)%@lt`e zqQ7O5XfJXEp`l~$H**+VK}y+BaCAF-MNi*=PqUQ00qwgw3!zP|Z7DwR1*qz%NeNBa z2?ye5khJW`vva5M(ky=JLd{txlr^okyG$&&kg^*LSt(gxoIwf5Jr>eHOAqzC8Tl@zmPpSe+tWKMTy4K>c*h+FzcoS0OM<6(hk3N*d zXOU|mBxMe|FKdza)Cj>znyfIg3xem}Q}+y&Mv7>Hl`yi8-qhAbN7RqzOQD}mQMI2? zBcQph{y<(<58|sPg$+uP<8gf|7PR{Yg|qcRPfg{7^-7P0ty`3`6UCaY1ds_RxL1#| zr|zbKIv;RLuohe+XWQjl_dw3JG5I-6tdyqdK05p8&cwwOB^EknTZ|BupruEux>O|u z!z(6(aq`k{W~C{$qoFGP`d`8WvAReijzgDV-+!!;)DSFw(LsDGbu= zmX~m|r%w??#8@9Ek=in#SuZE;pw7OeywE|n;mA7@ze!#n@M}%U1&@m`xX(55HT*=A zrqfYLOSne|9>a+Fyo^v#)9P$r;zp7QX>sxbgw`1(1HspK$DM zE|$Sl&8goauM<-DM%5icjAB`giy;X?@K=o`E8arJq>Ox%uAQtUOFwWv>eQJp5WIdm z-i}R@mQp&SLaiwo+}A`k4OQKb+D{yXw7Bx5uHxwky}iItEV_6Z{EN3zMGb3^l#E_I z#WLsWQUXnfmFl+3q{}UT9QF;IoQN%5=NXBzc5DxRm@&g(wXG@TQ?; zBiLQjC){kDMrpyhsKssgT!H&&{%Bd!EApB6`+XK$X-CCsO0W0SCmaQeXi3Pq=WZXI z+h*UD0Y7$>H8>LRvGzBQ#_|gwxiF|Jt?n!;Q`w5_61{gW;g)#pc8>R)FS&04P>$eN zuE))B(zSdTmmVOCF4Vo7`C+ToWQIQxCdKA<57eJ3SMH=g_OS?mxAAO%R>~H;+pYfq z3YXkS3)P2Uo;v{4w^Ad*N^Rm&2koF)v*NmsQ>WLVFOSE4A$6&=B=j0y;*z_1QtUlW zq*qoRVkR;&1<8HQJ`{ufl$(^K#_NP}*i|Lq>V8uYv47UCpN$4@9mCa>@qy8Yl2|25 zM0EJ2BIeZvC6Sp|CTiqpSRNLO^Atp;p*>+xAc=ZXZFq zYIw^Frxfxwscu~OSkm768T&wtcrQAB6)67zhLn2@e!WrO%Qn$JGLWw=#*Uk4i>(YK z%4#!e`Eu6pD;pY&tNMbnSnNYGQuz# z-*Bt*T-yHtW$Ehn;JXRSR&rff$$T#xw{!X>I;jNQ)SoRZ^h7^rg8fX6&QBJ=?sXB2 z$w~aG3DWe%R@pW$bv0MfA_JG_xfK$KkrdKA2HvBmvaG7qS>{kQ)X_6?ch%M1Jq67PL11bZOeq(T z<{8M3hsfK0ei13ysO=4_7qGZd9u?<&-^41MoZ-?; z^xu#CvdZG2Z&;G4-RH$%vuF7v`3_) z_k$mlgkOg)OpQJ`;c5y>i0Rj*Q0)9_bWajTNvra>>_ahq$YxiXJ& zE-Vw)#GbVG9jv3+spPiY*#wOAg84&@`Xc7$#@}UMOS32_&yi;FsA`jD;>>4#fm15F zNWZk+xm}5VW3th!$|QnJf0kN;-2N&*NHzwP_Yh|WhVim6TX+{#xWQKn+J7A?PQSoB zHynGCRx!sa(qDm@Bhx>qd}Lq8@2cypis;bgF?MxKlt<@W(~F^qF1*~>pjoEn#!Jnq z9#>F0Rb=#NpKgPHd2}W99;>+WLR%0N-x-*qONKjpn>Q?M(PXid3u27Y!Nrqsc z8>RJ~peBzy>+Jf1j;)DgM<gDa@{rHv9Y4b?6srFK5!!8P&lmMG-);3n#9}3>B(&Jm^54dXJ%-02uhXy+L>!5^ z`BL4FOB4N+c-p;0y_qKZ3?IZ~mXM{eKvz-!0BtuNsF9r$`T*U+xJe6VZM~JRx7xUA~}f)mZ2XG3ByBLX?7&acZ-T*(;^4 z(6TbQSmY@WsYN|+u;%~+C1KAE+i-)vh}iGaK{HU+HJ12ggx^oG}1u7k6P9Se&q-`oo`Q$ zumZax3C$tZ1P2A`X#;lzlCZscR4lM0W0NJ#gF$=LWgMH7vThEPuw&X*8_Al{gO(d! zo=2dj%xofr6QJMjns&ZT8ls44aIyW6&qrY6c08K3sm_t+*S& zlZwn7{Hy;6m=nF6lrd>-At;r2q^(TiwiHHs|u|}eU*_Mi~|+Y zv=8AgUv)Alc`vA0%w2RpIo|PpmneC%1LdA&aYGfp^#iIfhYis9OhD0#f_*3xiG+4nH z!U$`1hR+I_5!zcCU+%1M$)=MNR*FA`Zv-;Tczb`cv=B-4ZD~4x#p`HX8eh=dYv)@x zmNQG*-}Y%*$*(356A}^DsgZ$VP*)*+|=Z`j6{t;dLs^hFs5*T(R1J zAib)V%W}(jYv5UzO3`8}GFdekju5X4bv7a8*FH?jXCTSZyFYV zAi3}<_EbImAirps;BmZj$x(V5PVdZZ)`u=eQC-bd##Z{YXwM77IJKXVNl58&O3#?J z`j1t^RNr9iZY_;z03tg`ZM^^~7d2mRC1!h;lyOw0*dpEmn-7*LZAAEuDGN8|%L;TM z5VJV=?Ij`UJBn^qU;=0^b7PT&bJQ%$c!gQA>Ni*rcXbpW`=M2pCDo6T#~59LoN2`y z05_)R?ydV*{vrP3Th{@$xaJYo!_m~*t}K)#cRt#xmd(+AqsvcSti)NY945+C+IJ}) z!)sR8BFtQTO=Ll1#wZ{SNfjlrA|GiHlab;rAxVye1P_HLOVb|opQsnDAE6w{IEHSP zaR^oa05X8r-*HwI&Q13yw$hJeLuE~!>W@bhP*QaXLWm>VdbE}?+^+MR^d3f)nU!>_ z5~Q{?2X4}S`qQo{cQP`y`Vx4MUwt6I2I>RgxEHEBtdw6yTkGnl+$_ftYGwDr3@G&^ z`S}5Q)Oi`iW;{%0VLiCs>O)Lej;cLBWf6Rb>Vf|Nrc)VB?bcV~`Do#G{JMV1G(JsQ zeMK$!jQs#;?UsGlh<%0KJpw_}oqr^+1$2HPc6zb;xMcidzl9x3cn^=^*WW;V-E!{6 z*8yZi)yH$w77wCS1?&n){>s|qsrs8-JTwvVsK-)~V{xLtaRkCG*}9a5TgE+WTn!Jrj+uLqNw+);#bj%wxHjpk*1 zvK}cuBK{YyuU4mnh}qcu*~wbx`8F*OR{}IQB+rq5R&tGkN$>)$c>PJInH2k*ka5IH z$JJR?%DVNdqn(`cSUf|U*~DCUismeZm9wvcs_1lJ`TSN(H*rX~1lre7rm5r-cw(CzESkyyxyKy} z+?I+II$T<;VaOt{_NNwMSV?Hwu!SFOjT7$TMRC$YsO`#ScO;~afYQ@SOlmGrM3x3q zVD#U)AaxZ{cCKaIz0})EJwhpz^2(PMQc65fpaG%1c7uJ&lIS#hEs<>h0F;%F6$Fb` ztJlw{Iqk^-pK!_l05yc0-uijd%2?=DPF`;~204vw-NmuD*l6`$4bqwzE)1rYeLcw{ z)>2RRQKdYtqHdQ5&@{$UX+mRU7!p}ctd$K#hn+0wOR61?-JPT)xRISD=uWA!Kj|K# z^);3!D5up0BeuCwy*EHBXGEV9;aM5rENjXee3F49VFY#m0D(vM0m_UI8OF5m+j}^! zl?Qei2veWx6l0DpR@^KHl?Hn#GShkN5o?c;@mTFoPbypPiOWzT&M304;x*;8+e#$% zLqk~((~e12!kZ62#=Be`-5L-gdD~?7CmM{f$>N+zqkkXqF>9I za^VOlP$UqcMvl;}x??F^d>!AxO2_FVsP@yfY=N^-0C0&(P+i+TCbZ?ApnlxkVO>@J zR#Hd<{Iwpxc&)2Ck(D?=`A~9E7I()Ihg*YvI@3zPGO6GciR*;uS{imEgZE8`<64)D zx(g{vB+nPkmYuCmN?aWSn@G^mZsXGIIZq=mQe4}ZeBw6z7g)Fk*Xiw~)l-A$Hw$aA zJdj-918GH0Mn=G|Ayzc*G`f`g8VSCIT|bRWh@uqMiO?SdR@Gdjis@@gByIe1kgFE; z@isJM!Mf5K7@@VG{!voPV4p|OFCI@#Y)6GFC}3yY9jUnP+j2$6h&^jbv`?cteV#Fz zbd`v$W7BK-xo=%4^ajIf z(!d?Z?G&`R3FLS7==zF|C4@qfwqhtB8{Ch^w6^3A_UiIAn+iM;Whn?vzbZzenX6&6 z3jI{O5%I z8(u#%9y2|lpM~WmCqjLnWhZ4Hn=E3jkc)4_J8rGlz0Xo_Svuk|8(2~-W~ATIM&bv= zb^gjma!?Hzxr+Y)E0CjN(YKHUUZeL2n$~8jz&f*ZG+HX?v;P1(XYNs-Xu#7rCQfpc zKbV-of2Aw3ef6ctq=o0jW#raBY};rLn#z@@OPiG%BzZr}(9hWt^$s`1@R;(I2?63X zDo`izG@Y9*h5J~O$soBD>0d0m6?xXiZ*EX8IuylhoNvuZHu zk(g-7gbxhQx$(D6q}D^%Vts>+ zONF!m3Wc==CbKvX7TNDx)C4yWxHT3c-DoyGq3c;gZJ8vLh8YYJb^OOl)>n2yYD!%K zX#Q^DOJPb%M^Ruk9{L}WY;HBY50IH@aBg`?KEi2NBoS3%tI-}>QW8|*CgntvuiIFr zjQe?vOc~&mp$wb#DhB@mg>COgCQC|U?P2FwElSyc(vLs7fcF=m#5nGqNgNK|=Q2n~ z%Rp1>k^Wx0ZhqRR*!dN8KE8#fug0vmq)cSHH_s7dxqxw`uRyW^($pIMccl+e%Ri5| zB>Ez+%l4a;Ky_k99cv2l4U<;1eEU)Ql=plCb|2;c00O?tcCRFaDNOL#snb&1 zX8!=fLP;WzE_2JUFZAT2+-Bw*4FyTN4oGkl`MwZpe|Y8~F^v!EC666Zv-73T8CL3(i)s6ZqF$Qk_x?*0b>)Nux$d zAsb^iECg{!GY#~+4N5|ipi^Ofo?@zb6$!@;7hGkRRV>7~AkN5T6v=uj8;~p%;2TQM zo`TiLU9Af9gSM+`%Noih4dQbpz~W*J2q8)uRa?3rQtlN?=t^PB$;46wyi!7JL463Z zxYpWLO|Vb1Ej8yTk0ePs0l2nC)8lYx5v`z?Z21En$4GRACA|loIbE3+b;`Q*ai*2Z?CVBS?}Mh) zvdBlTQ+eO*tb^%$lW~>tx7}Cp-GXQ?>F|XQ~#vj;)aQcsq)5UV?X$O_jJ`tz1Kq61R*%sT?yd!i~2#;)QO%;Z8%9 zt@aY16=Hoy5HJgfL$jDmt4HM`_b1zKv|G0n&^ojvnIjPWP}px82|Db#w!X@)jJ~XK z#eEG`hLnGZ)O5#C(L-~d@e-d zMBJmRPPRZ%1ML-SugOCl5-K)0srtTzWnHw2kV$mCu7rXvFIF$)HZGVeztd3e#%F-V zXUX%EypX(M31?wBO&@{YZY@RDA~SV`DUb%1S~!c7a4& zHvLRgRJ{T67=*LXTvVgy^&@;>2y#0YBp?(IWieokrGbj!H1h0}?Gz$jDBU=nFxeF!FF3=@5gB?TTQsi)}6^(l7g zkD&mahfyZU>sNIg&9o8e7;lKGU2jnIhwZKhE6q{A*oJbs5ObygFRpp}kAU8%!4dc2cxbh#o!_H)c^KlqC~9 z^HSR0=pY?!rEksP&r;~aWf3uyE7b<$RDHVIcJB(zDw&k)NOsRG(!6+St}a!vJ(W zdeMEhFXqJnNU3-uQnRVzfUmN#){S|J2Ks0;=}5|~3|DIr+64_%h&i!G09D29trbC& zc7bH4uAgmcRdpb(131cW<`v;;EMbB&mO-zuLT-9%Y7LbVsgXHC#bf?D)jhF(MUsD% z9VxLPwYElwr?83=cw?23$j&M|>Wt{IN%(!E!kc}1nvDGegQ=ocS6$pm-8S1!w9P`Z zi(^;Y4>nM(Z%skGiq0s9N{*N%m2gzhSlOJVE=1fxT-4qb!-M!6Vg(!R&Td zDb%vi+@+m;f~5$yX_NLDZUl5P*txgHe9b-Kg^*H~M3?!E1c9w!O1q6_J`yxK4uhpv zY%=}mHC{CJGXB?W&N6L+~0tZ?etiV*bzyhlubr%Nwm@zB>tpNw!La?8> z5^0W>(58xr`LL@?~JE@p!B^# zyt7eV4P2HIR6+NOEaiee*&oPqn9z`~QO&l}*8^G$*+?qZD_qYx5A91XSSsT~<)tTf z&PQsbh_{_WT`nm`9_OBu*mf#YmB%&!OZGWVw zi+#eZYv$m)l>1*Fa_mJH+CD@D;y9Nt{$kX!f_z0;)$)q5hKk?xZLQ@o++G9CanB)2 znp$jYu@0!~eJB@m8rEl^YPemRpK@G?{E3p2G36~yivX3HseaU-Lef>=1hdp+sT`5D zt7=x`YS(IsP`6H^h@C+FyM$Gf%L^rO+XwtaiXw$z{mTv8m64E0TMVMzETR4NsaDsi z1!jE%v5_&vq!z906tl9=sAXFE1zL#mSFCj^{Y|};C$neNcAZ>~hCWKx3vI2CpStuV zx0itO(i{wDCpRr*5*s>%D%-yMP$Rt#%#?Wf=P8?}CoxvR4oM{g<4UYS_SF9FqAybK zLGV1?DgF$^wzT+^q}cndLicKY&}m0yb|iSrr!YuTr=~mVIs>$z+s5{!?p40Q_3Xd2 zgEz@4_KO*5Son&9ul7|_&Uy|fCi;Y#j>=BwY=u~8k)=*n$acD-7XDJq&8sU-lWPSV zP=4Y_E7nXS7afgZMaR<1sz}iUkJxEDEt^C1YsE197qNi)aiJw{f;3BZPnM?DUnovP zuyw_CX%S9Q%<#A$m{wL?SO`x*PyZJ3URe(#ST`-YWX88kqVVrpG^g{dyJrzg_nWjyP80 zwZ7wjyw$x`M;(e=wr0U@Mpz`M5IoMbbdnYFV22}ZTDN%gt&zdxpr-a$Ub9s@y@AIW>)r{9s?QQwjES(pD?@sobV?ByW)^t?H^e5U=R9jp- z&SG`iKO;?INSv|Hcy3X|wB&;l{dXqX`|VY-(6T78k2gf0P@S>UkX4PE+=j-1K|@;~ zDo@79k4kk-bOXzQu}9Qwk>H>-t2%G!Ra>i;2>Bp_RFk-$3dv2rz?CwHqFHB9??|NG!Q-habGQ)SV_z3Z-d4cw@d*v0j;2opcSt}3~OEM#= zCsBLR6{IIf^W|;%lX2sE&v-Pb!KjB8`h&aApsbDENGee3E+#X|N2epxBlHpl^{H87 zNZnfSlkQC!!J*W)rGQo7tJuFS}sYiq1)d69O*7 zk$@aXADuma?WG*sT?XQmS5h^Z%i`H_ZN!4M5|Vb3ZV2$EsMl;qXJH~5{&`7J+AX)D zdiAS{b#l6u7HuRkbCazfom)zJTgKF9r}QB$)VqxT0N>%EVA|Jigr>vx(U%t31OmIQ-AhVSkDzGT88Io?fckgDQ3X9MdS;8hq%Wb#bRuud z%wtxhINzv%M%60kPkmU$DWb9Y!Sw~NkfqPz%!~*>nC<&0i(kjE8Ey3HhOSA*J_n}K znkU5G!@`Tg(I)LUZ*-` z5LMiyWzUs}g}SCyv<|MR1zkLiMl@I%-4)ypy!IVy^um^>{24+DZhhcdnRL(81h--6 z4Vy0_?Sw>nDL<&+ce|w|F#h>Cn zprU#t8b7*_vesi)BRw0cR-&7LrGI4-OCW5o#A1|BlD(0db;3F%0!QCNR>d%J*0G0S z@r=W&N?&a;l0Z^avwaSJSwoL+xhsRjp`jqnd2ji)2GsrLm%Cze4PKmL7EEkh z5qQuNrv{0?#7Uqyyme!6QIBEB$joR2w-7d;17Iy$`*^Z&+mh@(kD8u76u}Pz`fYC? zI#&Hy`V7~#CD#Gy($z0#%waZ<-t{a1`)M~@UZZvFIeG^M59X4U!ia{He>U|C3;AAx z=HfK!@{EwNnZSTqS1sy1Qk|>zW zYbi?Eias8n4ThAY_GC9+v4H6rONF8jOQu3{_nMVM_kWgKRs+*k>5@?ZFAr{${ zECM}X?#pVjG`&ohix|H@91hj8^6mQ2+K=oNh!mmt8EQOf$p#w_1N+O|k^cb83fqca zrb3A}J#Pfc^^z7z{HY4l?V$a{f~^d7$o@706ARvB1%fOBluzxgJ(&zssF?V%9yq&g zwB7oo3sUhxoIFNv<|9K$ON!L%@SvI^t;mbm@j!R^#AxazDJk3g$+zq@?0EkG`h*`W zljtU%;J%~$B{s?M+?)Q|g!t$pYnhb21sddY=8&_SR`7llrLpJi6s6knLF&HAVWe7| zafjqbyYc=qE{P8tZubFPp8fvZx*dj{K5-6zDVwAk`7s?Xft(6jx$3ptL( zp$c1)0#)Xvr6cY&pK~|a8}=df2QMRziDZebERV{Rs2wU^^`Am&y8T#78=AzK48I~v z&W79m3K*&tfZ z^*%jo$)6J!cZ{A7Td$PGeo;x481I7SyKILOZ{^l?4LM~d`hKC-uPOM-{{Sq%#G#+d zy5dj=@{V@PiApgXRwSh<0cmaK =n)La_2(BUjk^GgCdoaJqPSmdHI{Kc%0c@J== zG4k2Js2fT6y!5X7M?=MQk->5tyOg@YBC*R#ZRDxc@x-mJheDNK&ceEVI$G}|kKWpp z{{YHY(lJ=c>iF@_G_llzbUy0oYS~fsIa_r6-f#?ajKo#e)Msv;O~v|qss8}JfnWP` z6BtZu$S9V^&lId&5pR7GI|N-nfZ`Vu<8vMizm!Hbmk0(+iBa+Y00^YH9ou0a$g(%b zXluECewi*@M`}A>hMO+;HW#Xab)BSYYlYkA`3ZlJ^bI}}jf(wfl(pOL*vD1a?o37a z>7VD4W*ZI(R@xBbaTae>(xRWJX$X@-sPhBZBcFUGrSz`$4`+h!1 zq_cA}I2V-}iEZQ{pYq*Bln=buQKPRL)>^|kJ^QCbu)3lg{{SeIT)(7%4^2F3+xCch z6pxgWeT7@)*#_=@N7O*@1ds2aUBdOsVOPi+?t|ElcJW`OBN9Vo-*7!VJVjT}im?_H zb1{=IPA8RfC-EVd64HEA`#fuq+=GO=owlXkaVC9`=M3w@QT@oEe5 zv5NUu{$v+998{DQGO@Jyl-T|vkpLY);=hC^7t}qs!cpi7km_7gSYT>;kOisla6zvi zT((_go2genq$c%YRoOyzYo*i<>_=P*E-z^CRjqn*so)f;!4;ngq1Dd52T*?Mw}q<( zb(;P}d-+gO%7Sb>sEct3y*4NOpeTM@3fJBs{{Uuc);xNo2Jh_qDQB=2I_x(N8v)Pa zG)I`Wl%%X0gg*jV6}_oX%diS>@p)1bu(zoSJuSwQw6UP^wJTU~M2RVq2rZ~2bq7i+ zT|ql~74}~+i0&zt$u28f5q&D+iV92inOfvudBrZtQMotk{k3;W^f}9{l(IynA;Y^% z)r@6=siq5ZGQcG3eza&yAZ+Zb0fUTFy?$Gf>#2XMZowVa`tcP8-2DZPSCsZj0o9{SeO z<4KyJAlpZn5cV(}-_*jn?2c+I{s2L+EN#`Uz`9Xn^%PqjAvrRuFg_ zF8xAuPq(cnIAoo6VAi%WXupQ)8hbj{c(WS@z51$tELpH)nqT}?7J#XPo=w3Q)1_@L zq~mW=EjfY-Dd;JEs&m+|s@$Lw$UrGQe5!_Zv|H0Pf;W^wQbXt)R^MNB9<`2yoboz@ zAuk~&_fQtCw82{9;x3|3Q+j%l44SH$uehb1fw?IE05I0=ptz2Q*+hh!VsozqOo9mq ztu}z3)}`Rg$?G1z6$5BkCqr?sg)SVI;h}t^Uo^cPbbKlDO+8J0G6r^az)yDTl4z|0 zjIv^uWX(#_I+}x$Ly2UL%8|Wx2jLw(wO4Gi#;KM9@ETiD!^+cf_*3UZriA(t+n43; zS{!N3?4YRMsI_#O(eXzuvn;a3$1C`NTEJDULKYKvsR{|VjUDx1hk>ixOL++fn8C(Fpsd+uP_yqpe^MJh7{e zkg!1i0E^Z$VJ05cKNYZfa(E4t6?xN&Bk}c@Gf6)PGU6P7M>l^{5W1Lg1PY zm_HzsIITzbR--KgV&|Z@7XG zq@zo#psJfTtjz^9B2h3vI8aW(~HCRwy{D*7P{0E002{yYWN6NyK`?j?_C zfk-DxG_8~MHmgg}-MqXOfKT#-C?1Lky$9TX^&5^oP`b0x8rq~{04%9T8_0_Czdx_!0mGI@%Falv=;~H1QVrV zp__K0xy{9J`c0sGJ#Ik$+Gp)pFSfj*K_QKDj z4O^-{sFFp^;%{RqB#x;nPJ)$HOt6N{UX2A&I~rAgS+tYkyR8mY$uNre847@+B(_ku zKP}XuQE*_@(8xtIW&~Izw$M5&UY}(H+K|V)52(T1t4!`w(o!~c)OecDx3ka#*oetI zi3wRzN>-oKsJ*(;e{OR12KVB-3{N(T9FXG+R;JeJ){px$`ij0)eGMjeIu0=f$04}z zP`{*$AI6QZz)o+tJ&%yWzd{F+`iRHC-k2B(5?v$VDQ1tbyh`=iQu`TvYOD5LjJ;(H$vo zL*!B~_|?w0$F!?|r4*ZgDBiVolPUbVviV1g)1)n2+9V?-7rk~!U{ zu`cptK6X9!TqsjTKy}d5vDl1?nd?n;c>_5OOTF6&w^C`i&!C63p5qA^@_<#l-UaF^ zZ3WLjGTBb^t*u3jvMV^6JGWs??P_ANX~!Tb3Dn!iPr8VKOl0G4Cs3UY8p0Sn$#E7| zw+4jFT^TCIx3)Ro7Amxy{JseB%w9YCe!gf;DD^kGQiTva2} zamESPa8iY>LX}m>YBt#p#K#-SvoV4Y>!xcd>XrEYw57a!eG57{X?+Q!87x*w2rek& z-Bq_9s6Ta8Ivg2wUfgE7^a}p~ES{GAXkinftMe@+5-sfOL6+BC8;+}MC`G5%!Aip+ zOGoBHhM$cO#j`6-2OE)%kk0uKLLc!c04Lu|+MEXKaedHAJX~RAUX$o>tMd;dZ3QnG z%Q^HIgdv;}jHH;*N`r~oQ*MHsrcO++-AW`?S@k8tT5ocEFQr?@Td7Gs_Lo6)@+%-I zY$bQ<1vB=d%aM{CH*SWPv?eZOR zEd@W{0^-d4L#nT*Pbz}rvJvv*_`Kd5)=ZT8D(kpvKI&4=Hc90Kr>yAauv=RXx~1oXIv!3nE`Cw8c^ZeFOpQ54zsjFz-uAaz zikkHyI$G>Jhc_XQRoQi2d_lIJ`qZg}mr_*OII_pX^DW6kGf)syYdSi4T#8cN40T23 zzarAZB`c8&B9Nw*B`3#0p!XWCt;#P0E^wO5BN+@7H_Emo$5H&F+e}lk*Ks@aVAn!1 zD~(HNZe#@@okv|N<{Ob{rTxB9WMxq#Mp*S5QW7uVZAVTr7n~&)E{Q6*8mG>+DP-PW z0*yT#)dJx3kBJ(ZjAPR#)ck|6osk)7RC#K$bsG75DLcECHD)BaoZ6mjCR+jD@enUc zxUlNO%881Tt-UM$kaeK>Si2I%Z@Hns!_#A_QCI2cMN94`XWGDc#t|tSq(f{Vb#Y4s zoBIKzKH^@8y!%D!!fi*!M=9Rdss0-zip*GNg(!NAv?fVRbjF58o7J~)y$ySj7=^U8 z^fg{;5kiC6@X8N^1401y1FcWkvP4z%zQ8Gbn~cLH!Sgv62Sfy3{{U$;%xJJq&AMaw zfuwgm?}@x`8(Pqf0bPLu?xRCh&36F#H6F+m_i=eTZWDuG1%9CEmlZ9*nK0JcMTe;3!PM!}gzKIO`>@G7YcY*ONla$a5p6?Yd#|M<3S{3>&N~tD z=pw1gR;)A-*=GeDbr5oN z71*j)^?s!9uom`I+>(n6yx2?!Pd?CW9`dK{xH45`FH&PDO;W&^KWeuNLdS`yZj zpHoxWgiz^>idV78an~728aA*r_u7f=Ydr$K58s&Y3gt6$V) zzD0){mi9rm&yY|ZDT;Nh2KOuVV72ppHp=X0p-vAEi2gLhIQg-8a&;ktoYxrmE;S?7 zNa_NIUv=sWN@@cN98Dt1-=P7;VSi2w{`3R)K>k8 zeGN9Bs2y2oYb8FKo>=iDQxUFAQpqfhKbQo*3!alLB;8jbN(1bvPK*Ysz}!AhjEJD6 zDd*HW0ki@4T9UUyZQy>b>fo{%9mI7dDT^h@$?yexhPwFD*6FNx%ap-D zc1qkqKg9&=_|TumU!p<3)jx|Cf1lyd=gvxsr!C-CZhUQFdR_c-#quvA(Ry{I(19qAfqT+2^nFOfeBD{I(i_<$8I1W|iHQ*L<&I zv|>$rVc+iH@LaKw$ET9VNfw=na453sLA|_nHOYLJ;CkB-@{dU!KgIrgw@sLz>1S<1 z<~^qFUNsqX!W>(Yx&eO-mF-I0BIKgh2E2D;hu+ejI=G|V^!Xk?dJ8yGWNR?2HZa@N zlG@Nmrs9$}d_LN?)w;_uh9|YJT`vs~N%`DT<)zfAd(bsr)ZCiCq2C_IIkk4adl*JT z7a{)uRX{2G9jvJ!1pqv4R?2$?J&#>_0dkovS8W8+Fj?^PtL_icSV>drRL)K2PaeTamPE~m!i ztSKW}Vmq>4g>Bjqm#D>Dd1)hZ$Vk-fHoYJ2KLQcc{7ei%2WeNsZ_Ikse&BEH{{Rss z;vSFj!d}(>pnPviJ*a051N(Wd!&SeRXcRb&_r1_>V^LD8&_A8k_XH?RQCF!AI&|A{ zZ+BXhtVj=&m!P4?WS!{Kr~{x)^{D;mRj()5Y>Xam>QhR1AJUV5?5Mq%eMv_`#7MCx zn_^tE6C8?_k+~suy{fNft!p|oTWe2lyfoQuV)-t0#t|PTvo6WeWbR+zT-~~r#r{*L z*4(mD{M}e;K1OSVEv=|*bW5nh4nwrDQTBABHA0P0_cw6JR{2BL%MK|^UrM*?+M-A1RDZ@U4r!*u9~ zto&v{yLxbaa6fT2x4x|?y192{O}ci)^gtJeNrI#!9#G%{>mZu2t`$qvM%dh!VVk*J zm&Dqm6}md4BHE2Q)s0kE*G45ftdB;Bepis?7;VPWjd4p)g^h(+QLkI|E4KEo$6^Ki z&pM?UPh*m%28c<}`)b+h(~jjWdza^`9v(v+M-D#AjkuSZJpHP3f$LPoLdBz>aTXCr=a{mCC1vVhk*KxLvQOs59f~T5b+d@+8 zkdnT>!Pmm8-yd+|tAv+yIqpIBV`Vvxea}mJqTlkZdIMVb88)ES0dsM7D{Y{}fS`7`Zhsn*-jehM zcNtk0dkheQm8At|%fg?dkY!DrVH%Ug8-2s^rLc7*{q<N6cU-VPod0Z zXaxr3jSrP#T?P{$Hu%@$tTt|^jqQJj%vNN>2z*?DsXiTR3^jRs5qi%OGnNn`a_nps zQo$I>vnh}in}ezL*6>HN$?sGAHM}z&zd7eem8&eQEq09-u#1~sn!*#6uOPRU72#^bO2Auyo}Om(jgc9e z2dx6Y6G8?slE7H7`vptK%b_UG1bSXQL~ls3@uaTSEH$<;$o!W61-9V)!mIpRWuz`l zhqB_L$t_>tC*h;xBwOf6_uV^z*T$z@u*GBDh;L8tQ(ovO$0l^Wk3&aAeW;92XVG|p z(OQezrl!FRg-R^LdJkNT58%gCnJ7X304!?Z@)UZj$h4<|UgE-tN`ESJ0<0w*A!hO> zpBhbstlXW({XLbYnhG_WBhze?rGcgI>>h@fQaKf;r^;=n5>&E!Yv)3)oXE*tgj^Vm z3SDWmOhG$s@cXLvib~72GRdrbOL6vy$tX&HPNuE>qLx$1EP~m(MXh?2RtS?gDIqBb z!^Wgz4aaZc>wo)eV!>dASZPfWGNKA>5-xlz3>i28A8%SRT@4Kl22senSrx1q6R8V= zCoqNGXsuuxn2QGCAd3-E91Ct53*X9{LdTs~L(`>WG{n7MA+2jBWLIjH`Aw^4%%v_P zUk?N0SYqfqL!CAIYY4_!2=CqFdsIe-b5II8?j98xM<&r)@@%SLu=#Od5R+l;wF+Gk zw2oiMW+cd#wWUQVB--Eds>?0OV_hjAVw!SAims&-)54Q^V+#myF_QNSBm|CvhlQS| zNuk47nPtG63wYMdWP&yfSZ>u`CZT6xilP;36537n*!T(*+6<8Oym$uWTmIV78ABts z;VlDA(a-nRFTp)HHc8?Ztc3EOQ_ zE-J3g9;K~3i++TGu0`tGYbsIlwW&#KdKOUL&!B0X+K3xrtdZ8jfpshDKUKkaxv41y zX@ud~;Z>GvC+a&^%lZ;%`T2W7)X&7Ohyc}o#VC5Kr;fqMxwlT=RuYBJ=CSGLtykMa zex;o^%RPc9$_EJl07{^f*Upx7uc*(xFQ7@J$bRLfw_B@ZYpp9OVT`CYWh|(zZMeb` zTO!~A{k0Ew@i*+hp}30?Cu)@1cX(RHmX(Z>sU9sxX#W5U(@`Bs6$LruC$|%J=dl?@ z#jU1o;w*1Xct5CBC@$ve&Y?r#ke14bvFUN`rBxDPKJE*!#axyu94T`kh1>Hc^}n)4 z*CE%a-d1%Elqn6Vf4QO5A9WGw;M3e#0|m~I@Kb1MDY>=9hzG`v7+_YpN%l8hEpspG zsHrIY$^?x)ohoYnxF+>yft=h6C{Ls4HsyuFomK6pU1;^nUqZI!4^~BGV&J-;Mf}@X z+>C&BtD^mXe=vPaLy}sw0;Mw#e_>ZY7>Q}!}trPb;K8AkNvGohW;xp8Z zvL>yiC#sZHuC%xAHNMC-TurO>Ftkn_Nd7)ZlzxSaq@NlM`^rnCMk(XE=g663Fi{zb z8Ivr_P0r2a?gPs8O5B_5UQ^>0hVm3fHZ*%EK5yWglci6`@G;k)QFd{lIHH+u-Fofc zp!W(NnqGv|Smfg(IFPqVbPvo)>sjuRbTPZI;HN;z1H)S1yesT5JMf|wM~Y!)mt+3WnH@Q$5clp zV{yjAK%eHj_ZV6Y1t5bJGu5w4e~ zstFb zuriw2 z6rY+yE6*YPGI5J}EzD@83K|va2BWQ3a+8vVV)~anWGj^XW*X0M^hbH8aWX@ZC8Q}X zB-|w1p|_P*>T)uFmP32|YF%Pf+=@JiJJ4PAz$bo})9tRqi zuocuooE(hi@+jMlI-*6}2gCl#m1&Z(rp4Bkwl^g19&}{DxHP&6u{q=$Em!irZd5Gv z2OF6A5)kF?cBu3;2Ux^qk*b_@E3Lm9 zeK}*+PwG{*rR~(mV$Pla0H{?nac}fq!oHh^lrPN@r8?w>u0v(h@Pw#&jelhlHoSwD zyjUUJQ$&&ycOJe}#IC_#EiR^?%S(3(7akQab+AsE3I^J261K{%)gprOZ^s9|TNe2N zWeomT=oy>;S0h(r zM141AN*wn1)T?udyyFQG`IuvJQrIKRQ#I{+3Qt4m3tltPKahoZQ+3;rGOAECD19Jp zQ6B2dre~C*D2aIlXi`Y<3ar77avrspkkys8#C1(2bwuMfJ5MKAuv01VzlCetB<99( zByy~hhoHl*q?;ElWd1a}ol)#us;+OTAWjZ;(B@k>)C3>QRN_s(z-lh``$S?|$#;;K z&T*`Ii+|3}QDkbCTNeB6QEGC15ID*aLq~Z8RUb|UDQ?@MoFOScbrDrRP;S`$1|(&s zD&bM#AlpN#zhxa;j^G}%W`hNgeC$?D*9!Wfq>EaXo$Wjz(bY|L54Xi(#ArE`mZn-w ztQ=p)e!=Hb_vImo(**Ly@-sNBVD#`W8H~G% za#Xdc3bmu8ZAkW5Q;WtcsGD=2V4NN}`^6A%i~FY@0#Cy%{M?W)`*h zrqrd|mBr^H)oWPSVoMv0=B2ohP#9~eT8IPg)~ame*P&@w6(`(~CyU}BSXvPR=@A7Et zP;)X8qv8Oy{neP9x`UPzH$(VfE?@P}f|PtcfK)o988j->_+=Zq%V8tLf;6c0F(ieP zlf$r}T90G^MUs+#8rQJ?q}8j?e%491KlaY88;8PuI}uS&0pk?RJjBB9U_Rpwgu4uia7)fvusXM>4(zfwc%c#YC#gQH{%pDghVKyT~ z9X6Vu*+&Fp%F8)CzEe0Xsg}S=>XBpa>r;-rQ3>wJvq8ynYucv;hlr^CvA?`yO?N_{XjQBIfgp-P4@_rC$tJd$#M$!P=pTLJ0srRSiZ zaP{x8{BB#BZ~U|&L&ZA>o&2d=c-W=fYK~56^hDbi%JVL4QX+aP{VpIBkbLwt&TjG5 z$$n&xhf|ilW5=Ot;rp4$;I^33~+3Vvl#fxsHEOx z-dv50BMka9QW13}ck$MtR~Z=g31=@WI@ELdi!jmyKO4l?tNt4+ZKX-9iC zCMLADUuBakZz~w?dAEJtKLbUc%q7^JdiJH&3!(QN_*_DfzGuDQvoQOX)Wk^U%_>vV8(UK?|=N z6Hy7oppk2@uD5?YC=z2qQ><8w*|8LwpH!?hgEdI0%vhB@<>#6*4V%9 z4S!`^SHs%9N{fC$Pog98`-h7qC}E_PCsLx27Nhr7lHs;KrEBKu`T}NiTrR}=&3Xz} zyY>$+3ss)C3$T}QYq`C5-G6}zpUd!$9jy;hO3=Avkye#)H~O-Y{#u6mA|*OhS=-j9 z5QHc4_+I+eopd;rR_#V$6}K2@+;(14ZbFH*>5R4NDB`i`Lng5!*Dk+>Y1VbJ8zWKf zt=A^IF$vnU)*x3L!xfO{98^`TSd<$Qf3ll(Ti?i=zO4R{n8IRaC0A+m3-vbB?Wue6 ziIa?E`wko8c9d=4wYQN7T2uaAv_3Z0w}^cM z`?LCvBIh2FwYLz}!5fr(MJs-~U56{&{{X0ck(~r>SFGArI-=Rl1%1ix;!Cle1B zF6?&TsSdvO?dU})e*Ph*w`oSreiFK&aR+mW2_ZjH@C8+qz}P@ zUuj0WIR^LnLpft5@JQya4QsbMp;ZL%q=E%5{ zoF6y&R0ifLT8kBdeNVobqTi{F6Udyp3!o7H0AD(5F=Uf$GLAtBKp$JLK~TY-`W{Ek zMqlMvx%P@-JcRcV>A6-tBDAPkBl7MB^_f8DK7={mK^J&V{i^c4>v=#V-*Q0*AltYfhUuDP`$Hfty%4vk;_QBPQPVmwn;M@(9IaIz5Hrk49uFk zy%lq-ab z)a#4M3HAj_Zp5eZ-^Q8t@(vTjBa`u^#+*`DwPYJon;Od*9HwtL$yiN};ufM$#)IYU zs_d3flW2PiL0fhv${lJgbtO_96{d??U}cnKi_=I>OCv0cexz{m{LS^LX8;-jX{NTU zOv7X!ij5bO2}UI$rWD~9Tb&QGnW{Yn?0s-LB;1>IqOuI#3kki&?@F{9v}e_~>8VNN zGembH@p;t8Zse789@<+C%LHQZi+hS;5e6G-4FtaU1=k4IUkz&Fni55J#jTbENZ>0$ zsp>qc!n`I|xiV73W4F(BGm&Q{kfSlIQmwybVIbsWBq#!Hts1_-C6vRg>2G~PBm%U- zgLhVtZ{=8MVB_I;@CmILHV!0<-lvgsRWGK~@2xgqiq;Z0DAXa?+Yd=5qy8csIOR1oQM}f8NO!*^zEFgUw zYtZ{?e3}GHakj}JTR^GV+`)(QrW3e&+9TUTY-TO6RNh$$fPGhNJT;)Z(h90&& zE@972mn?+!P(6Ip!uYSVHU^j2h=~yy}T$3uc@~B1e%KC{{T8R(WalN zy4X>yGY$Y0n;$!y)B~s~1mp4cE|LMT{WmnOS{jWpVc6IvQV9b4yyy=3g`$`SV&NFv zn=L8knd5zjys`hr8E-BQGJCcZ)d_zPj*N|=biB(novTrU9svZTW*|-8YqGcq(P3~s#!u(okpM=QhwdCWtw%xWsIZuCyp@p zqNwg4PvLN-9c|;SF!(#ut=oe$=ltb}k zdM|yk1?8puVx^&9k;MEe8vAO>?}J>Dc`IY{+FY@Kyl&=W@(zzpY`|$>l)G@F?4)h@ z=FB|{3cgpZKTzlUiw5Z)w21Olbwf?81Al2It0%_oMgEe<_f2^<`Hg;MmAqd}RF;jl z0^lOY*;01u@B`1sQ>suKSIE(E%+(#YB`B<2{`n|~8EaT@FL!D(?I zk9}nLy?Z)1k&i30TgqhO2X(oIz1eBY5 zjaNBpr`YHtR^*pPVn%P9;auAD7f`j*rox52^k~zoKT&Hh+^e|9{{Vzc^KM=Eh}Z}# zSV>NwWn}njq`EO!{DSz)st*q4rCT>ZUG4d=p{n=szr?U#BXUN`?n_HjQiw}J!8Z!_ zR^{=|gro9D^a&%lF_x00LRvfx!f9D>l0y*r0${hhfrvkthR=$8e+pt8RJ}mo&Orem z=9ojd0pjN2@TSA|=}*LB_hP8%eH)PYS`qhol^3hjD)e*Wz2?yyGL$R#)2^3D9s>Hh$2eF}JNOX?9! z=G@zN%6;aeD!L9sWw+GXauRwtE#a*NRM(-WG&FJMQ~^?gtxfncE=XqbqOR5n_WuCE zsVK2#qBAj&NwFTvm1xxCk*}bEe^7tj(u^tXntZ7J+RT~l2(6~lKqBE?50zwX>MteY z#_LOEN8-?QqIntQg#=-34!!_T^VF6NlFWuIw$@R%p&D4xRAT#~nB{h1NSvcB1<;=xo%5^lIs(&<9Q z+DwUYu94xRk`jB@}wJ&X-PgO zr5-nR0rs+wWH?EAl8{nXlh&r>nHc#*EgiQKlC~aDN$M{^lm;n~O^I(0h32=X%v5C@ z4Z>L+?`6ZS&_5c1EC?9LS+G^g`hAp0PXg9dH+vARN`btc6X zwXrxNZ&DU6psAg{kl0hOB{h*{T7Z>&Rsx#U6SB(Wcbs0-N7~vR3o!#*4zl~?y zGkIy|q&+!-b zqy%-&SuL>Hobnp61+)@?0sjEH1y8~Y(0$oty+I?7WSqL6CBd)xidLlf>qT=(bVAEs zZS`a3xVUX;aalIuk@=CK_SAj+_Cceq^}5bGd~6Y><`K;UL&vt?1)Z5!l^+gdT2ZDE-3F zqt@5ftYfVRlsD|oU5Mn3?*d&rR&?73bmw%X~wzmg{yCs3U;K4$6bXH6Vgh4Aggt?g$h<>3_^y2 zOiN*=lAo7C3UpHK@f~Wm`sCko!kMU&Jbqgof}c^~!js~3s*CjI*>_JPRDB729zn;8 zsbXW>lCS=FN5>#G7VWwAGMz6D;k9FysOoYKZ{{P56-Ppn zl?!_h_EN6L*`fQXSjuCLF`LS9yv%IA4s*!J##m5WDo~IVgMZW1RcAHE)wffHi?~iB zlen6z{YlqMGu)KJ`6x?DmFUQAV^q2E zdwPnj*;142T5$8jzE7aEiSr&oL71Sw0NTl=p;C(+S2+>Kq;d~aYY#~pTht{( zPPFt~P`g$!XtVi*B?Sz$k+)x$Q6yL&Z5Hb2oN>?`7Q`QRTlfy)^61RAWud99d>{uv zO6&;eL9T0eg05er>9je^AipSWXL$P;PI~V9n zTjWuMQeTq8?Wk!8#_>v1C!=iy)6;v7pVAD9uTzmV~(U1?4zM`pU` z)QK-8$6XFEr;w#2*-BemY(0jq^`&-t6uS_mEIbiO@-UGVXXgf)aQ^@W%h+13>8{!K zM?bMGdf!na2Nf-V>`Cp&Sk);C1yt^Bk8~?JQ6#eoJ=YawtGe;j zs=HA$MDavA*$p@ldYg+HG`E^S)|zM>=NM65(EWfqfI$X{4ZMq4qUG2S_Z#$F-d)>k zkb+LM&%d7N1hp_Re;n!Eu}akIYB!Pt_V4IsJ}piwVu}>B^(UyMU8r`wf54D8g1S?1 z+WZZ?FG){ib~=b|=N4&_Tv7^>qoPtj3sV08a*X>HFkA+ZqF9KgmNdB*znwGpF$DFb zyBaqOa*~%EQwmwpL|6(|Rwf=5v~4c{`%VX4Z|52U>3t&OU$UaF@(E_MlL5(%kNA+L z9FDX908u>wqgQC9wKeDfelTUl1gyS;ZMVJk_tSB-t!bP3k1(6DAxzuAD1ooKnDSph z?ag*!+)S=gkflJJ%j~S#NmA0K05#~LrB>fe9J>!{?Aacvgk2A3?#J)Re#1IckW+72l#TqpA`#Mecy%qDX?oHUc@h;+O&iBUjo zk~-W|u-uG_iD)!NW@25khoW!bZB18nUr`zAyb&o87J@+*Dd|=bv0$sSS*9>8+?0{h zpar3#n*!I<-B@UD>k3zfqoJ%YXyj#UYm-ro*(DLVOYK;+vV(5}L{ny(Hg06}{3Z1O zI*Xc@yC6;(W47{>o3_aczrL~(c_y%VS-gM+9=X%UUmBaWqXA2wGj{&~RYd#haj~OP zhC>+cNjfu#KB?N1bM2`sdgPC^TsSUQ4 zlNXe*d(`%RHKTb5$NZ=Jef1V>d+GlGilHcEJSUNif*Vc1vGc9G;1{^a{F$E;zAtqR z$&vSgs+11ld-)0!qmvCc*i@PiT*iuXrH{U%!c#1XBwb0rLtALZ!bKZVe*ikvVE}V| z0gp7NRBk>I`)XhYPcgj~wd^V~T>@0_%Da=aVaQ5-5>V*Tei!Lf_tApttMEh;;^R63 zzb*Z~Gzg+nlR=w;H(x_pT%Js_IXo5O4@zuERn%`nl6nwp5gQ=Lx5SmHD$u0qQUI8< zEtecZ5;aj3xUQx$UYn&M7SIb-lI$t!VJh8yEo#C?k@-bNsN^kU337u?5>2R2pj9Mo z#Bpxr@`6j6Fu}R@o9HS!)n(*gV(cTKB1R3Q05!E=K_peqd1R6kKNZq1MS^(%=CDc| zXoT2+Ez*twG%SRapF7rxHaqfTA8E_BS3iv}k2IPQb4-TQH2O zX;X^VLEtM82NsVEf$8{GbToL)w}rmy%o@PPPDnO|DM#V+y&tkO1U>G10z87#&jgfQ z5lelNH@Iya}{t8@eY+TYwGJQ*L!cNK3mv@p)9{HiBWP_tzuN*e956!#>_$w}Dnqz{O_ zR!?qBdKDx9arsWVwK)2mzvd*GgOid7YTBG`+U`DdDXyjtf0#Ow2G9WMr9jLZrzWfX zX$ReJdw(0_`}Ik*F6PC?OoWh1dyR(YO=)MXMsx4+autvM!OR ztvnf6Nw(K+olR&m3$W9g8+le3gN!ErJ%6&3W7URYk@-q=@%GZJawzacFH`c3UdOU05_IS98O9)#{tY3Hx039z+k z?pc7<>cc&UjNvivFVrM7PPa(%`>9X6n`0lh!FG5Ze7f=FN{b~+2HSu-ebl_ta?T0j zOS2lUlFf~DJBVU!`WG7p-)%V!buUmqb>rv&$mar+>-sD~Py+3?i=W3zcCBQ}t9PHG zA=7ySt`d-^l#|n4x{q~QYU)6)yLQIN@}&^Hziu@NA{Fhgs$@%xs!DS7KXw4 zd@oJPNsC5J4gPL(>IwBp+bDAo=*!eX)$YBF>j6 z_ho|%lrfJDCPLqyS|Fq#kem7GRQ723yfj_w@5()yH)p%j8*H%>3K4soxGtbQhO3{( z;hv@9`Ao5aqrKA2Ist4H57YnxqyC+b)LMTeoAeq;=P7ITkR%epIRv!9f*;*_?p{vxXiw69(XbvfJiY;V6nTZRjZ0of}m^S770(@GIwz0SC7 z)ia_nqL!UnfYV(E-A9cGjJ8I{GN#nA6xk=Ky@09v2v_fC=qnFBIi#t?%Pl3XN{^4T zT8h$A=t>*Xk5)_Z9n8hSP+Q4&=D_%*C*noy2B|egvA|8sCqb+7cqM?mu3L=di3nC| zI#+LnhX7^3fA1O+(zdoV_i4R%z`^`v_8UZ)pBk<3I?C_Y+JjC*VlSkTS!R^e@{7SD z!1phl;8cJzi*dt!pNirKNlDZrUL+&@W`fj+l-B#;GW2sYbAmc8A@%b)2&;<5VDMbEV5^wfe zmb*pz8DA}zh96nX1P7m3G28=mC2kTKu7XXsRc)Fz+tHTV+xFAdGR?$ydn*hSWLXmigK+^Qebqy?@ud2u zUTb`^Y2r&IfsgKn046}$zY2fw*S-^ufpriF{3@IQYo=p?^T4wp}b5R_gG(INH}ml;V) z$B3v;RxNcht(irV%zkPdosDD~2wBtC1F81fs&*=p>Wg|eV)`(%n5Nu7Q%T+zu7kpm zuXpNLS*E=P?c zQ9_NOij)(g6}))Z(_RL=k+LN#PzC&jCv8kDCy{@ch`)u#cBu6v3fJacjFpM{5aIk# zP?bD=maO}ee{Ump@NdWh&Av~GHx{D3vujd$3CH0Q9sKB0f$F1rjQJQO7CcPb4My$y z&`0q1dFW|p$)q^Arb%J&rrO$9X2|N02DFvjqik-zF~pI^;}P20#q~1hd^SpJXD|FC!q32x$a$C;MwX+;=w(cG&fOn={gt%_$V%RZ$ekHlxXQzQO~#!n6QcoRc1@R_D7b(aoOUdQd!(^Sq;sV`&O*{8=CD8~+PU%ZBW~|}h#}-rXPk88`&13h_ zEWJOOA@>xAu;+yo?Q6?H29z$hu8z7h`N;WrU}r_4J!+x$CFc3y#X@WAf9 z6Ks(^7Guqq3PWhM#tZ-?h408+_H zZDgb!e{EPUW|#w~g1&1#hKu0YRh_FyXo5jfhyZ=2wE5b-2wU;%15rCr%!ZKctg8b{ z?Ft*b>05cG`vuqd+*|56f3}#`$P7>1G}mc8D;GB*hw=D&AvE^pEtI7^R-!xswdxY* zAEF~KkEDhR;Ol8>OK4iP_)V&RyRl`%VU6VQ`M`8rXt(@$Q+oW4dy%(_aa{82MkB3lu4EpbhY#c@2KC%$o`<8=E?HwKXZ^ud`TUtTSfe z(sy_Z)Vav>ayte zr1bZS<@vq?Uu)A0>bOsmY-zu$F4p_2#xiUr8LTSeJE3&Pb9-t(_f>0qtIursb@ji< z`W};4%=K(b&OJ>Po#6n}KrFxWNJJ-FjFWNe6)ClOjSiIyIE|=xLywu!pGcZw<1}Zc zMjs{@DWw9HCBT&f;bMBy7TIKfQW@2*7Q97=IR-N$nVlM5PEwI23EG#@@P2PSpkLoZ z)Z~0vP5vcVf%5n-GKPZR_a5Vf8o7bQO(!c^eC+O?aQj_M&x683YY%?$u2CZ z3TIU<4ebWj)}CCYGVLO@@cR*erzVnk{$gqqvld~3G(zKpW9NHTmgOF@$v@k+zv%w} z0xc(i^L090f`pVK^Vn^x7;^Mu0(0Ot`=e$!mvEaqoQ{y+?`nJ~f4S?hm2o?1bq+)0 zoQcJt%ZyWE0dY4^?W*mrLa(Mv4PGwkbY-cA#pWQk7RuYH@)6S@+Y-0K{{t%mwpp$%C6)2TU!9*(~Qjk-vWVc#f z7!|D+`o#`E*tqaoel8)UF5qoS#s2^r6Xd1aA)}!e*BwGp@etvZwJ!_mwoapI_-uOg zr7hf(?8jlOy-?yuALBC6P%++{iysAsy8G%Ye#Po->BIh#w958|X?0RC*$2P`skX`% zQdN743+O7-SLD^F`N|Ex0&nyJo$cQ+#6?W(o9L4nChX&zZqBby9<+G#I(>y!;Q%W;mM zh38v7DA-g0KG8sW5{TS+ILn~(`J*pcLC06@*?m0vR-q{_;pH`+g3;LzUF1n`YP8t1 zV|vKSOpIbK(-~IQLnw_ScjUj9*-Lq>^%;_Fnz17|hZLtViQ1pcq}bZt%hpOzk*d7Y z48@Z@3Q;T}dfg#f+LWCT`j%>Q^$kXyk1h}9QPSV3@b5x;T-9d8CFoe|csGVhTjTt5 zCV(TYC)CimuV;l;!<^i2 z@}Mt+)ltNX_dUh?c@Z9$9_5e2UsVTKQqG!yN&u^_Kats+{K%l?G<55ZAo`T9S~Ky9 zTXWSUsGYk4h4rvDJu9lOY5L&ftmB*Ql0I;mndmOI(&VJ8<4D{{KH6jVS|R$HCHqbP z0Od@3h=rDiB~i)>6a|#o9RuDc9WumyY8ZFl4&)n9^StN*mj-dk(a>=1-|(RBM-hfaT()oIThQ4 zt-=(dDax%Cy0nPYpO`|{m9*E0b8>Df!n<%&Y|__sR~exNvP�eaT*fVbcEq4PNT) zOVsB!s4EsORZ2Q#%^84Z%!6tY}My1zHFt}45sNo{#x2@!%xAqP%} z*++!iVzaCq<3QU(r&=(zl?axQd@C{wz-sF#Q3BSr6|R7q895N^Yi%7W5?Bl3@*uWs zM?>wXU?V~$pYZ*)wq^$FYT)Q}tWP52Wi{qMzKIUzn9)s??jT>t)EWuqYTDdFiAso2 z@x7>#n?TyhFUIp6o;)Q5UbADs^%O|#G-^sD-xbf$^XP6sE$`3|3Vw^z11J`@KQ9_m zL2cxfSRJ>cCYg=2NYe6@HPt>QnealMY!i@m0SX~jSCvLlNf|pj6~0F#mfR`5jqOR> zAP{&NMwh+CZdJ#H1;J;a`2EQiP077Up>>ikna`pELBGaF!hz#bh8J*<&PO;7O&cL( z6#GNWG9Y87MCrkhx?QdFG+ex*TgHh~XJSz$@uv$uz1F8VLd~Ry%UZjy%b)HPJSZNmnr5^wZBqoxC zrAJHj6wEscvH5)?r4U%pu8Prv*4C12dK2MHLRm(Kt-H__Y2qqIAwN?gKj}?F{5vi@@ z!MPM0wpdn|KvSYAv1F0IOL@|=n;WlRsVIqwLHLcd{XR8R+_T($RFF^P!Uw=Fe+r0P8+?S) z32h*xo`U@=8w#r)H;t6NrO~|u@{bR?lJa>RRJZCrTPT9psI>%Jz}A!l%9NK@L^3%{ zO9)(GbR<`*PW6w@zKqTQ&&)PTzFR48we0xD#svt+t|DIV^$+Tz<~bot*Nz$EuG zFFHYO1q`&ZcBBrM_KymAaS1Geub-UW5jVtUGhYhiEixHhwk0Q9dupd^Rt46c-H)kd zM0NzUp)y-rN?m|muAZLD)f}yqI~@&vGVT&QOBFPgdY2${7QgJRLw3Sf@pZ6Np5MSa zdWr;{0&RPJ)biw}Eci$OmD_AsB%FbcfvbvDfFF$(Jgt&=NJd;GQR>(-Ru_olzl5h% zkW(k)3Dhh5srBy5)Iwix9^F7IQ&MCttVS@h)$~ytjkG>Z^nA=~4ToC(|TlttfY3cVt0h z{K<+z{Yp*E>9|to)K#?}<5lNz(XD6PeE5r2=cMv(dy$C2&Rq?o{#wd<+^VG>E#L?4 zsSO?G`2LA)Yd%L!Xt3hYy_~E@{{ZhuAFcr-&id3o!2|uqEDtl2M|_`^h*>uUS5Z1s zP}7PPw~%sSJVe%}$ZR^0)KRRbi0F|}zgy@Ut(3ZsviT@eR*{a6^6^kCgd37=rO&>H zuTP5W+@9XbvEdPy8s4BsRduG*Xsb}m2dXyEqpt4?nu7H+!N;>4K+I-ws=O)g$0@a} zqi?&VD=liDpwu}lW78!2AKbjZriQaIqvhL9i&C2iSqIvhUVKB&VpwbZf#VWcBu#QY z9WfidDLBW6R>2Bx{?8gxI`>Do?VFmmN9e^G>>x%s;NftkSWOQZfLGl&g6aM7K zsd~dI2|6uA>v}9T9z|umhmB_Ow+ccM(!X}%r}peB+=4@d=b$BN3we7<76PI6pHyPx z5g}%K64Hc1o9I5fZQg7Mzgn)hUB}R|t8>fLIDg!YGTVk*E-;Ii7N-HY_G?Q;rtr|u zxg@iaKCV>-$sUZR0`{F&o}NcaxUY|vGMQa2SvQF;g0f-5n#!a?btt~Jy>32mcw+k% zS7OXr1eHE?Ey-I+Skw|hBk-$wAsM2E$x69)0y)SoP1qXs7PYCxT19o6jlRQ2veDcY z*|47)BCYxg&t-ay*CO{QSGZOp_@8}B6&d7{HAZdL6}3REw2#aG06NEv z_Ay=1a*U2XLab0&D)XpHxhK#hXBH&%OOm!7J5NF9TUo&;5hOgc65E=m#V5j}FHuU1)@;YC0;^RMr6wSdzZCwY+K*!#1SQS-DZ&AH`0- zBpON9)95(|91f$FQ*S}y-}rCmtpUM=%D{F!@{*rfNm(5&)`p_ly5%2G>uQA~^4Omf zrDL&&@Qk|~{F+cnZ2OHv%kVeg{-V*}lM%q{{2!!_<;Q%3qlzaQ%bqMlQuE+ROcJI?uYtUz= zo519ES{yH)IN94Foyh6Gs2hu=kaG5qTdJg7L? zWosjQi6XYCu?wpqCN-6gb!hb>PfF5%DtB78tzM+3UrvNwXEzPYgegFNux^uTv#rKR ztdwk05FDyoC;dw|zfBK^xKLe2zUFJt=~h3J4WXDK=b#Aj`>T+1m(=tq#yRX6U?h-+ zaqCf6RRn4H&}FfXjfIVJeUTuIjF{>Wp$JC<^8;&|Q{Hc(VNDMmP;rb%NB-dnKH>$b zYc7%RqgE*&02ObdpGNcKn#w2>&e-T z#4SlzDNoNB$aL(iu zU3(D9Us<}Hx`n?Q8{C5EV&kUAtZ^n1jnDr81xD5=BoDJ%6}yRz!;49EVc76vv9JkB zJ9G&uB7QL2t!FuIYDHb*Mdwgod0~aEhnl zv1kc;RCbYY0zz)Dc9gZAZ4cLVaOh6)ID9@%A|@t7PPCzPj+d%Uor_}dvR>*_RMvWA zHT<#+WTi3G)}1vPee{>RKT_M+f?j}$*xX!zvniCkq7SRI=xLp6n(9~VYeAqYaQLC{ zu?N#ajlxMJ(<^GkE%xyG8?PACH}s(mJVvzA+js=%5cJ$5ZG%YhB{jI*+XB`G5){)V zTgf2#0ai*;^%HSmH@;cL0KMzo(Yj_AV6L_;^aec=I+PZOsz~#-z@jS~GFokl{{St< zDYBU{QsNxYEL|l&+B7y=2R#_1dNeFdH7n&`2ug_BR`PG<(3*{sn(Kl!S6c4LBxc|i z4YF)4MCgxICcyo)2Xy|0y>-*$+(rC+RUj8;HlwdajcZF{Z_tC&t@?vM+tCl@k|ZGN zM&NDZSq<4P=vLhd+UUkF@yJ_>+8&ulQa!ZoT#f2qp4ca!QT zcNpQ(0vzK{sGSDy-oN~)ly4zt#Y^fBUmD0Hi>J}+tv0VhFOHHLFOSO$)${k8R(tzF30|k}p~_Xyx*n<*SJ6JiixJB?Yz3tLbD zx=HsdrBPkMO@5YiH2i`e=R~$YF~?*+7T+b658)7-f)7nCRWFUcJ}mzNl>6ab=8kVIZU) z9HhD)N*2ZR^zCs=*~wsPo;~^^cZcFq<}5IlQ1Y$$98K@;(yK4#loneZ*W;z0sGk1- z@J|r3Usfd!ln;q1O^xkHf2m79*}<#f4l;!Z_~kHeJ`zpG-&*NRLjyM{nYt z1Ul+JDu^XOdn_$jw^MmWmXC_+Kr_5=A0#bnfZl+Vl^`UID)Up2ntjUcoj7)AqBH%w z#Ls=_ol`M)4uwEmdpg%u$Me@m)ts*H2ThNvDon2g<8fpbUdA^$pllpK3L?ASGoG?h z>Tvv@$LWmm5-}^cd~syC6BV&3{{W;_ui33!(acZOq?ZL>pvZ6t1OkjW0Cc**x8GK4 zm145tpt=oT;_+fy(9#v)jlhBTQP%!NWyQulh5@`}eaKXFAn9U9L*-IZY+dW54R?{E z3EIR|uWiEYfNVX+rlXN-EQ~u0W^);jfTkm?s0|8I6Q|ilt6DkB1uePbpb;7AvXl}~ z>s~sGe#$zkrQMp1_sh`GNX;czjN_>n@ZD?u^#xwD^+de3e-NB}r!Tq#PB@a2sj(!| z)7I7vRl7m4E1157rbJ~}epf&#QTvT2;ja%;$NvDzq>X-amd7^q)Iis18>>Cq83*?$ zY}Yr+vhiu;IMRVPN&= z(`)>O)f^&Z$8(rB%y=p~nptkogtqlF{AXQ;}^EB*SWWLUyHz zDfLoCt!>JxDSUFOWtg)5A0ocRhtv%rjJ)REmyO3l#jV0i?WMu#kzx8&X^3n}zMaJG zp--s@%W>=z2omyn@0ZA`exOLCEaIo1%*#mg zE64uYMLErTfVmGUTwXjGeVc~}YT69|B{u6_Cm+No57crS{Pfh{+VfeU!xY67@hAB`H3Lk)m7) z%r`PdQRuQ#egth&k^u7qK}cI%;n$-q>Tt8ws z+SXyXx48Qf*CrUP=!&$MP{smm?JHMuLvj ztpEe|*KwuH#!J3WJmfeD)7=nh5$$oa#;)kUCpUd~0=o>QSKICtV6H2z*TV-cF$Umk zPVL%WvD<*osbFO5R|fucUvyI)2Fn2!^6{-o zSw=t7H(6~-Cg7{WppgNZz_B*h&alC=o)v}(Sb0_$Xi?=@VT9ajG`H&Vt*n?Ai=D}1 z{{YH(q#mOF6nNgzp2}px2XZGc*h9Y--lUVQJ00Gb6nf^3YR#0ru>Q z&B(}_;c?Xy}-v#zEv5*J|eD{_tP-GEf8^Nlr@r#B_T|{mq&$^JZN15f~zlreNkgniowvh(hio|X?zir!z5AO4HZaNnq@mAkuTn%At}?+ZJ~~>Q*XRalC|yd3@TQ?9>JD*crQ>6%AW-s80Qr(V zG$`J}gKUkV*AB)nDVF)SQdY!GP5KphRJAH{mO<=K#yC1QB0><51+D$nbdgpbX-EVR zef2ot+@#oLsja0T1?ZY!QK#rMe>D-LtxI!m-lUOHazNCd&=}Vui1L;Y@HM4kF~BDi zIKsc`Sd9rZ#AJZAf;5$dk-rod zkLZ9dn|a>VD9Zq&91;l5Fn|uxr@Dq%*47COd5bnpteq@rX+^H%RalUDm&mra_VlG1 zW7WVd9mbnGw=HTu8@y@~hnG=^bYVuajU#Arl03?epKzsROKfG@k(arY2#Hf(l9mTi z&{pHACNC}6qAx$6WJoE;$4q38)U8P#$|YJX4ZNNRCTEyQS`??IE$6Ldwv%Lo;iXyC z$D*q4O|{&t@)=Bw)gM*Dq<)lK>3V(|@kJ%O(qOjZzV*ObP(7xYZD0y{c|0a#@gYl> zFc%L*qpkkR4C|bcI_!;aB8+p$5pA*~&Z7SSo7CER_*#~`i*i^Wr<-tjM9&l5tebA2 zG=SVeU>i{?RlTOGZQxc5jc=1lzNbO)&f;OGwvU=gOoZCyE7`7xr{~ znWn{=KBVAsXl1gf%_-*=EQOGw2ir(Zc)T*!Elp-5YJ5o9W|X(uaY}RZ5LegAtSQ)O z^eb%Dc7CMD?2HyK7Z@#2%e@ zNBbE139YI3V=v~o=`Iw_LeOohk*NEs5vp;03H#8WaD2GvRhJq@^mqfJsIYB_?IoU! z7W5@uUmuk=g`HsMV=V;j_^;BN!Yk(qCTN(?D4&N<-leck6+DjPz;VN^fIpb)(zMYy zN2u}nAq=(-}FYkw4YI{ zk=kt7-OKo<{uMTa^M)Xno z*r2Ej>+h*$15L5gSbfI5MiLxA9}+L|3X;P^J}epoi@vsSbV2H9si999E`~jMO%N0| z+s2rLVETfT>_eE@rx2nXQh-;Fg$`6E5*srl@vfEup=&ZXQ*p{E0|E z+ieIQH5UYKGiW^2_E4QEae>AvNkrVD|~e zQ*5+ExZUhsYp=tpVEP$wnS1DFH30skab@mPf?XgJ}1I zTC!3L1caW)VxL;Kr6or~q^n9Sb|gjWtc;%H%&dQ;eP})an~PMTdthg*vC)h_l4TkS zD0L48{{Sde!KXgqU|rLYG`QI&{ZE_4wJEXsQUN4>H04_ok6}w{>(vpZ!No~X3&yz( zX7&~&UY&--vQ}BG8F~X=<4+WNk(%C5hgyEUuc)2+Xoco+*|_bjJC7y1bxJ|$?iFK3 z#938tg^RIWy7@}Z`2PS<%T5nqvHR*C5=PzeebL=< z@N6`j*)6BXrk{Oz+u4!oaMtQ9?96^SAH|qH9;ryVw_gfV5}4PVVu<|iagL5H7CX|^ zG_XyF+fHlhH{447>~kfr`zppjXenH2p73zeLibVrJ$B>trmkwlE3y+O(rOR^arCpJaZ zEonQgxD}~G?5vk12=+2OET!F#+2XL;*dI%3De_JK0Bu)W%Sun6{5Y5;*dUlNI7N05bxR#0oec-dX8ov)UXJ^u5aMG4_MbX5W07gYsG4H5RcR8- zmgp^}v|KVt4uY}qn*|6LPNz$nikQQ|7Edf?wFcv~~E4u>veozg#vX3g^i)>s7X?NSp~!5ok^uV>>_HPsNK@9h_=Akn|x(J8~dp6 zxE!j$26jc}P_IcrOHV|kgZI;w>!}gG*+q09^Ja1|-1OcH41w{LLRR-4>vxIbd{Jfe{CRaLlEdZ2bs=CjuXr?^xY!q}fPA%b@6aD7mf=D^JbTX_NV?KIJ(T4fOxpo>Ed)Yxea3+p zl|;gotRIC-UH2`?Ne!tGkk&jAZ-Ez3{{YQh2B!}Qth1lk z=dPdNKwZvqNlvSL*Vb3p;x@n8PsduAuEj;0doq5a?L*|Rq`74|=2S>OjX3PrORzY{ zWxV7+6Bj+CrN^FD{{Ym4TK;`aRM_fov29gXDE&!1V=dOhD!WJr;Ut^;=nYn%L|R66 zW4Na2QB`p@TkoLUt5&|=r|2&Bu8*kk8HwztN6r1JRVVg!woC7>hhg(v@6#ccNb0~{YC~Zk6!rx^=99+`wF)m&V-F&HXM$pZXeGiV632Wht z{pMd|RAiobpY+!9x$97traeX;=a-?nP1KGWDDS9Ro zLnmdae8|QfWuA_#zr)^saQh5`JT6G6%kU{$eGWh+^^CDI9Tzc%xN$v&- z3rvVS_0W(-qkOq3KEihEL6-+}?uNg1*I%YW$nQ5Bj&{w-2}v3S7@(@-L(~dc{C8pY z4gRIpk8pxJ>%-$LI~4*=-eQ$7}I@cA< ze;U^}`DAvSKk_BjugtP^uNL~Kz~$a)hH$E4Vui~E#3YlZiBYhxJLNx%_7q+^qrB4o zLf4aCOANiZ=hQC|3sQ4&XJQ>l+v+J{*3?J+(reE4eh;}@?9R(u<@(kl)x&oOi1Ng! zV)IO7LjM3NQnaPee735_=gO&X;`b?CQ^9Yo$WeD@V<(P)rj+Uy6TiVqu@|MNC~h7FCC1M;gOVev^oC(OaQz4h!xuLyp&tX&Tig6eDSl*1{{Z?GG0~b1F_ui5 zf=J$wFIDgPGE#kd7BBebE6^PGVAlZ!$dE(rwn=eE;ual#$~XG@Gxfo@@EKPeqym@0 zFmaMwdEm>5{{S*DN_4sMI@OIl{{WilBC=dhgTsj$$Z=?Grtp*=O|(}+J(^XFIcYyq zs@^6)LLVm#wh1oEaypf7NgGO&q1KqMc~7{r)9~~&wdlzXE#UK71Em-4SOp%vQIC_v zbqVjybjZZ?YkhDrcZaako})2$+}v%kHMPl4>PPmd7)}f=($>dg zs2ggVR;}a>Eh%~gqw%RXvLz|C+*#e;C(e|&Wk|Kx(U83U-trgQO2dXjy%HO8{{RWr z=9K%&lj<@1P15W~Ti+;8@AqgY3+bb|&7>GITJQ~CR zyo@_b2mO1fLYmWCk}qzaHCeIqTi-=2mpZ=-qd90A=V$wD^w-$K%*Dn`a!@@-YzlX6 z2E=ut>inZ`JzHpM?~G~oNo9~Mw`TEpX>y%sKaP2)LWE}8ZEqx~d{!e{d+Vmv`5m*z zlR15_jKZw=;gqgZ3CJNls8LY{QkqxAA}HAB-TP~))#PiOe6o&C{VBp&!(%8I&NnDM zACYA8xU7KuuCfE|DR<~a!L=)8^Yp}!YPDnZXjW_vTN*daaXed@#Il& z;kRmTebGftNxAqyH#!RRZC0_+@}1~OcLtXCtR-Y6D*at8N1lBJ;~a@fffxYdB_Yy! zpm#sq?jAhMSL_H6G+yi{Gie zN_fE-vsz3b(PG5;9V-kHwfc&~FeFi?hW^UK4J!;iSYhiAD#Hy(y}ug63=wbdtr2W< z9&D(_>^`Qw#EO@gMXJQXiu}+=3G7Hj`ivh0SftsR(xi zD-4tX<8lOHtC@}VZ@@1s40kR?o;D_q8L{G^pFODb>l3-lMgDQ?4%dO7^ z!nQ6bo!agDYeotsE)oxst&@*qW1+X>SVzQfxk%9XRtCVf?cW!jY{T1bm1}EO7;AUI z_(jKntToVl3Gp8Kl%ddwi;CJ+bc=Y@3`WRW_TOcR@vJwI+7eU=D%(Pnc?z&79gdM@ zI6e2GDD)BsIE!XZ4S8;=_zgoOzT+u|;Nwuk1vhImivD`^CRO0V|RFiH}V2uF5O zY!dqGJVN`dkVKb|V%uL;zs0Xw9f^93(`ye#$xi@oaGP~C7ebKjd#y0TIZ0tYpmOW- zrB$_S*zWd)3@jr6AwmdTYTUNnFqs^EJ&HDsd;JuUQm@ zZdN|Psj(-r;u8Edk5xBHye&*uZE+zjJ0Ea6kY_LAX`qxJ)M|3EG7DLtL)dJITCE@p zbq4-gh~1%Mm7G3n>kB5>JwQT%CrvA`<2jq-`ja?b zXM@#g_%u-YPA`=87O1ZW#Y@OJ@4qTATMoWvwXp8+@aq%vyygv!c$7|LHt6X3G*XH4tWkI+?jCa; z7D57>d?`nwN!E!~z9!`rGzNPSDm~PuN}C_5)Z}kWURu364XmUqLQ`tq5Uv_w1Y}(a zvxjg4$68u-rp7C^;6X))l2n7WO8p1IwxNPDu`z?e#>puS?#b9Q;q`Qt2l3`X!0E79Cw7P=}_Yr*ropK_H+5%6V>4?=Kdv1t5L?vGW?gvpv zlrV*>n#q9jfwTHdLl2H|a@7#G3@K*-=?RV+Wj$vn{N3tLOBV+WMBdxO)*yu@EodY0}O7<*x9Vkh>U|~%$7npIq z6Y(Jb0NGlv@dqW;8tMol=^}``5pj73IFb}@Ev-z78*E>dM*`--OK#d)-Zg_GhNP6} zqoMJvHg1U>b-$45S%VWj>$(rZvY#s&--(%Hwz5;1AfiI7JTFjjLHwi`Ym_=HnN0-) zrNOl!I*}^&AE-$a!=ODC&B-5`zZ%qX(BvktK8>E26=BHksRcbyPM%d6L+S_a05e;X)1#l0M*Q(YM?XFBi+NGWwb1EDovWHIPH83q3Und9Uj zA=QSRBjQOKee|?ckiwE+$RVApGaWA`V^c*+b35g*y@nO^*3e$aR zGUI-XD&)Tbyt$kwI^OJtI7M{+Wu#d{{{SscHC+eMn)|veSd>UsLxXX-VaG7}&xrJ8 zz$`88s~v7e)%uZJv&T~>`B?|ZaQX2_2`G6dP02T_sBLuWQ`@D619J_Lj;jzrtu2U) z+pj3Dh6H(yiz!M_Pf!WHPu_?hZfDf@+JxZvh*DGS09TG=`WK)t zZMN6;Qgh$8&}zOmuTu_UARBzcD@K~#L|?H=RaaZVHkl9hBKkM@#Xz%E5gu=T8?@(9=h$o#vM1*k7UnH{Fs{j8jnT`PtZ zqCYV3sJ?;q=Kg_%M0Qw8gp$jPu?gA$6uj)p8;ml-&EwG4#@6Uo@6Dhd-wIjXAnJrV zL~AI9>{5R%`gHbGT;y-s6OkEcKgE)W=p9ChJv_}t!cVck6_O=2>?;l}Pfk>|4uCA! zA1ySeVNCvlR?Z2LsrhVsR!cF_9RO;wQb{JC*VmKkD|XCMBdf|_$O(}(3*0{`HmRy@ zVvBCHHU4?V0=6|NwC&K`>w0V1xX-=U`kOtJ#4Q(8#3UYrOH$t5ro8F;p{uVlme9Bo zLi|=PDTP$uMu@pXwnbRvEu@AfM@bz$QA4v{W=Cc{gY3VHd=iImYZ38up}K5~9E9t^ ztG_O*uh-t6`@*)RKcELF0cB;IU?~jk)1|u8+WODCWnRVUFbl3^CMcFK@pK(3+8BVf zlX4h{3PJw>So%^`sYp81{Kfv=Hh8&=VOLrM!9FBj^k_-HQ*%*_yAF|%jP27F7z4nB ziWX(+OWu#rV>W!|2})8ywbBNawNjUM2Fu!5z%{-}l4{9vG1sBB5$nJVcWhpPCOIqb z5T8Vk4;mcx(tQT1-*37KH9hq!YS0vwc#+bTabsiD6uEus6&}Map~7Q8QT`%Qqvv{M zT_7^54UQ7lF&dDQYg*QzxkzK#hoR-pgw>GR0S5Yt6rqrEmImh_?!JABTrr#b2}1)1af?_xk`hma5m=0tt81v#<5bwT&G!4~lemZ5i4H%Kl;)AkXgwZK zHWij_CEXfYjlSVFu}wrENXj&o56*zAkUrWQ(6<(Ucs>OdfC z_RwbRmt}$K=St|<(cI?}iftAFbbZn!-j}&SalEooNaK$7s_2`ZCmxM5DDP`j2DfpLK1v6s0(`V2}o;lC>mQ zLg_MOnb`(Xl)iw2YY?psSI!wN;eHGpyu>ILN>EZkN`j5ZpJkP8X((y!Ro!$fKKEMQ!v|nxTvD2uh7LAu>AiH8!0;fXnra)(_vpY| zL|<$(b~v^r8c9QKx}(#5$GZAeoOdPl7W-KJ0GVDq%SB%#$!#O@JcaJp+hazJ@|ST* z>!iSME0AIW6#%;>7XB^UN`Tj?()JX`wOqS`Q@KCN8imZKqy**o>zfg(a#7Xks9Vh> zibCEBtmZ7U*5&PT#Dy2!H+TAxxf}5k*Z%-GOJOcFN020cZ4O*5Sn3vg_jf?Id(oMS zu(QSBWh0aE$Vn(C>^Bz`rSVFq+C&$~_ACDYDAGsNenu^rdxQqilcJeDXpTIpoD$%}P zGlnu(nc;!Q&v_9h02V;$za_X1jnPey#1*LMJUU*wjxx!*@h;~%%BtgQ$LSz*;)x@4 zY?*NhKjo^%*YeW!aHk|t*$B*w$;Hb;y;!r9rK95X6pMv>EmkntY)0KT=rj{m9i$AL z2FUS{iy_PT=|!@?Hkx8}$;YISS_7?34JQeaRHroC5kl?*<C>fi8}it5{I8+V zY0i&SJ)g-qrV>MkABhWOtKiu3m2vh`D??&SE=7aist=+cV!M*dZxxIKu*$#o&Lz&O zJq;zh!^&OQ*0(d)qAVz!`zrHpK8{-MJ!&936=5!x$4x?obJO9{^y*(jm2bNSYXkj4Zr|j^T)u;u=baTWvP2SoJA*lv^iS*|mOIBn6oV zxZG+`m)!0R0zVpTpITHUHu}LGRkb!g6nf)L-6_sR-B(cj?Q6uNxj)nwYa+~$-yUXU z_gW>h2hd9au#@?Q?za`t>HJT2QTk-(xBhdd8F=F$xLxY)!l63zjR^(uwNYG-6D0)d zPUQRgijBVz+=~51q4M1ZyCBvNyIrih6DNwg`wpGIG?bMc$~G6h%_*_L?^hm@9Mt(A z9VvB*D~*rs7cR)Y+A|Mvnav>lx*R38&hCH|9c|-W{rg;ovrVrgbUM1+X4HB=7C^g= z+kERd^4bFZ??nexCY@_zNenvt70^;|m zV8)TX5P*V`LFsW(Xv0&~)G%!x+C2Wsz=EkA4ehT=!wGC=mGaG(5p*~Jzp(qNQNU*U z5p2W-Lv%iY{3i~F%Cyr2q%V$>>I9&x;^|Va2HPLZ)+?Si>S&|}kw@@SeD7*4U4Tkh zV}d{wLP}Xc=~8xCK0Z|&EH!}d4aSL6{{Wdu7iu!UhLhs2x5|mDTh3U%)cRo;8fGTy zAni};_YZwsNus|5Un$E_(v=<|D^MR3x6m42-a%tqeM+ix#rKx#mR6;bR7t&7&E=25 zU}hEPLN?Y-fwisoRQ!oxQuPlsX^M`WXn>8y{OO9|MT!M(`zmslNMi$(0r2Z?m57T& z)C8apI*_w8_0#3=tz;*(EYyUL3fg9+SoJTmMTz*lMzy?<_SwJ8HPi8|$t*Jg4X<%3 z@vJwK!L)b^)=lJJ()RupvsoM=wIl;;f#+K!pl(u?4Ro!V@Mb+U9}C~chUSw9kb|4- z02ceJB=AL63v~)RzBK{jJScLWP)NrEWJg88Sh(=khLJpySdl;pvThaATkBGiNX677 z%Lz`dn^B=Gi%u90F%hKz2Dj;B(xk$q5pD{Tpc{$srH!N$Y8FgbA|~XY%cvD8$zW|) zAgd8F$sno#De?HP`zStr4SQ1xu@y=ioR6=FJ{2D@u!l~ggg{9*4O-MsBT}mv9;O!S z{1&xFQOUJf{fv8r`~%-mjhIBC3l;H5tssk7hDNGJg>@Dw{K5nXR#c02`%aLz;-t+%F1wj+lAW?fdurq8q(iqTI`0awYH?5a!CedcK-m}l>yLPnp5l{oR$xioAn#Hmy$)mOip^7$)}r&X^}eGN@f5r4U~8z8#zD_V`cl*Lwbnvqj(`jCywH( zaF9JMP4yKZmLByT-LxendDem(e8E2fNc%qe!v#|ELHxG!tY8$dJZ;v2zfFDq6tiK_ zxqT+ulYa^vp^(1lgR*xJJpi%hYg5PxTak;5HoM!ev!xUSmPSg5Z((~5l*gI}D7Ij- z;uCYFsy+xjV3HFXfw<}@hJtbCxRltM#ae8~dtCypyq6LZw$kN+)SXX_0#$VnUA}^? zM2_mWB1)pIUzG=?{>oZX469GnE@9>&Bzo!fm_L!kkff3AwJzoK3ZU1h#Y~~~EI5Fb z4uY7fZ`2i$O#__A6&^F~cbUT#qvw~@jq5ah;G!aB#MgNc(JVPSa%qE$34qC}7+6&alwgaX}ll+T*QlESRDohL(Zwn~&YCVBBKYa|?{ar0;A3 zd}~TZlIjdpM_6-nDmDaK-DvTWV7dfjOzlAQ-%8pfIiEE3zJVNTU}nu>Wb4V{X#5>hUoI@zYO zHnH+GqiJCBM&obBwvn+1elVR3WPorG{9V_=C6 zEea}S!U5~8O~n9}kI3DO%;v0>EUhWFp6c4EGZm~q3qHb$RE84be^9k)>yo1EGEEuo zJ2_v%d*s!n!5|CXm6M!(2je(N{S3*(&PY?Z`CmBL=$qd4s_*jX`UA6FzNn2p>ez6i zTV=N|~Xl%D5fuo38yhY6u8SD+o9h(w*U)rCUku0lHoq;JQ?H8{( zwxo|*r(yb%sErhaI<+P^S5Qd5;YrH2%P3AUSst9mCJGAQnvzp~rib>DW;bKgx|=p`vRQr z4r*Ey4k5&#^dTgg+<17PtDlqf1I_0-M}k!23yu!E)Di_2t`&L#vzuv=xqQcN;%1=^ zey=)ut{|+RH`lopmCj}blOHG?Ke{>(x~${FK&`i&m(&ohW5^!S5{bWn8qt4ktW)<* zfuYNCc}ue7xo@-q_ffvezoDhOdws&JH8`j$Zb+uqHuVjwP8F_+1&^C~Y?4Nqj6hGX zEpC+gERjlGs^4L%wi52e-{t&w9;Dq%B|ba4el)yzxiOh@b;F>_M|m+&%0zTH{q(s1 z0GXqIrX?>>Pv$X?pvV6Jho^_a);UMWm49^-KNyBOf}hN!-$DBw`c@EzMGb9Z*@w) zj>az?#ccU>n(9Wf9n_yCHjf<)jc!P|`zq4D1|FqtpCz2T5r_HQ=Rx(}xjhIP8g{-m zwE7`A^43XpV_)|&r4)sd4@(}E8}wj-SIM{Ba&pL3$kWY3#+Lg;AMTA)oZfvW)}Yw^ zLY0=H8ivyjureN)dK}BPtw9EraEdLe^J_UA7c+Oce5>C@Za+YHm#YXxnUiY3thS|f z*R2X_43uYTL6l0KY!@QJ;^_vX;{l&`(bzIjHzluBpVUdHsH6!gjuRs!N2VJsg$+sT z_*DE;MByfG(jK`|$EtiuHJM3vHw7$?gmCqXjHLi{RRhlf{f{o@%4Rx8@MVbMT5T4+ zPsy`AxWr}huAyN;N|SHYqe06+{kF`xE%xjLRDzzm3TG_F(i*ukL!~2P5=TNn)9$G% zn)M*$hU%9j+^Rqq^6OcaN6cX4=gNorm3aI&qI)qG^OvI(Z4%x#Zy>aJr*@SWG`j^# ze^8rIvLO6&%D=)1sQudd2UJhhg;{OnsXnTA5$&K#$c@ufkX0!v1!1z6Av6h&*~!n&a?|Ij8{R3YD!%RTwW`cNcH5Y479C6Eq~f6TQ=4U zYB;|h;kLNUauQZl;(@uffHtc6@+}Y1p#a2QFukjgh6HBPeifd*DNo&(V#D?}*FZeT z&bGB(^+*Hri`)bD(-FTtMJ(0Bz%ke}9BjP8Rq3bmk*O80xfHWazPTGQ8TBlo^(iR* z^H|tAsA!Z$lL8_x!W6ZD0bZRdzSLUPVE#8JLJriSNpOCmpAWv5x}w)tVUn0!tvi}S z%_;PfH+pxtwR8KLDqTzcZS7n@?0a(iD^XDTVQ^JC+|nG5Id_Pp44qAE>;{H5PMxHY|YR zY@h;>Z!&t-R@cBH`qw-b`rpVF$MV?>k_dhM?X-|1<+zN9k1PJF9p03mF>(!5S@8}+ zztKFGHT>H`&Xa~RNe*+iJh_FgHzpXT#;8l`2v|1%02M7N`5IqX^Xv z=do-hynbztONzh1u%_LzPyO5Z(d)h+=3Xp{)|Yt6NdlzfpDudvr#X&z=(A#`&)Mfq z)4N+Mc6IDSw*F=-$#PW1zUI&`;rl95x)1b)j3p6okdHk1sl``9zn^%f?5=kok4Ev8tXz zLXWz`@v0BEo_{HO?$!QcAZKw@thif<+5siNo1tHZm0=xw-=SN6&Z_S4HD4g(9qo`ftxEy43sfdzYazOqQY_+|ln_sGKG_OCF{M*B4pvR7_uD}Dq zag*I{T)e}Wg(XA@FAE_*VAh3yB9g{4kh=UvA9jQ?JV-%dz7q^9JBWaq3$ROYFtnf1= zmJU%(fZK@m02WS_r4FS0x)LNrV=WcPj@c`5Kf_hJDyL;n0b|&!oN4T2*JHdkt)&#G zd#E@e7m)7o^i`-`K!c~*dOx9pQ2zk-==0ZFC0&JVG?eOE7xAJ>SVZE6Fk!osmX?wE zdbqzN($&Se5}edgV90$qsA)uDw4_-n@~f#o7?q!@L}~2SMbzw6f|M($018XqkjkwA z)}) zO-GICNj$S+N7N)6*mzS0gqBULqjGwhSl&iTemYbTw>H|e+9l$`3}ljpHijf!0>BgH zdd#B~87_{~b2i<@5J%=6DqdJN;-p9+;u%VST+)y_4LZ=?!}JXeVrK=0+CaUXwuxU; zr7q;y#dV~~RCORBAobGK9FEF3AD{zzUnUNHB&(`6pW9iJJXfgk5t5XALtiRmCcU#M zjF3Uwe`RHMgOqYRXd8f(?enavcr}K_KMIWFk*G%lgsEs7lx}cP2ASn zC)-l?%}T^Xa&zBt3QI4kf#OHzrfILBmG-o{5Giaa)hX9yxboGl$v@jpQm`v2H`Eui zVX!2D*5_J3Lc0>k-PaYUMaBA>grkttwVElHm2s)K>qD)@cL>vCHzCS9Yzh15>420Q z6F8|0Ka`)|sJSCbsbp|)ZaP!&*mOP=DI~o{y@n``FuhbP4~;Kjl+%IJTzhK5mPxmd zv+SwIu@z6}4@AZxDgwl$bQhu7FG0H3sV0YxE)<5-r;Q`-sG<6GoS80x#NL{a+e%6t zaCACVb=<0tOc%89eoG;@J83PZy;V3IPk|bb*+7piDRd1!5hc};jD|KWEzOb&)8VID zywq}+R&yJ0Rk9a4Mede?zmJ7kC^Dk!6C$!2eZWeIH@$_anAX0fbf+P&^2Zz(PmfXV zt;Jx3lVWSdxSO3ux_MDmq(0n0$CTQQhT-NkqF`GROh;?TRlqj!KKhJaL)j)fX|B5K zr-d%bHal&H4!e5wtO;6Zbck&Q5~c0-+Ln{ZWMRmFSVensllo4#`%PjaCxIkLak~eT zxOl7n%G0j^(O7X+vcsl9Gb0Ob@kR99R5 z6)5z01meU#S2pR$4{SvImL*I@W^u zG4x5zx(jmH0Y~z=#+qV~|eKp74x>Z!*g8+jwb=A5eh_Z7EVEp#Jj#$++>6cAE6YAy{O#|xoe)R1sv zTawajzd`Y9>7uD$d9LN9aEzb}myUUb5Vp4@eyTNhDG6P!*I**y+@DPBy)zEtZ3(HT zlc}*DRTmQ&qfeh7W=Dak0uIG3AOy|Mv^r7apm|L!+13fB!Wn_t47A^NEfoKuRfKtK{jTg(3WK+ zDMRu(<3n|z%4SrOn-Qu_?5YL=4BQQ96t8t+FdX7bWU00ML!hmT1B}<3R?>F))-!Q^ zOe$iQK`z{n8qmS-nRovHR{q=TT5(K|grZAuJijxDSS(IjStMPuN`;58(zohYFG9kN z+akM{Rs*>b-dk&CU@mF8JAe&+l}oY3FGAy6n4Diy&xg$L86V+gaq3~^5EkNWMf~)t zYqYG2$}el=sh>#(6UFeErk3t2Cey`V^Z^_Esd~39k8!u5O{?;Y=UAhUShUJ_KG!j^=St*YWXwZ4g!PeyT%Q~@b{vY_K<+AT%$MQpk} zF)vhse^28yZ*bgR1l%AS?(~m^y3yj+I+*_eFJWf0GvFi$(_BYRiX?gXnvAvU?|^aI zn)L~eS1snfEQ?8;oBm}a6@B%kL|&`vkghAa1jzGVD9Vrj03}2A zIsi$%J+H=3=vM4<@m`=5_X`4|On6F?vCyahFZk1+wphJIAThuZ&TBL0QTRiL z2=@+**+LSCaTKDj%ByQgx?38K&(PFO%{+c4%Sku6I#Y1h$**yZ-kB05{(5JsdY03v z_juG6r(VLd-SFrJmB|Ia&^DAdpVXp3@~2^}yRjQKn|%Qa%RzAe0IDbA{M$jS_QE^X zI*iqq!8EcN#;QWCqLfV$RNvq$E2H$uyLrSte@_&K!L^7_fbqR*RO7ig+`fmMkmKN+ z1SNf214@ihxz8Zf5LAnLcjZ54j(38qms0gt3P{2#Mr=T40 zw%_fd)*38(77K*@DJNYh7s%au&AgJ_ZyA#_&9z(MC!xJoU9l9pmwKFhq{1(8OqM*C zB*QCYk!JwhpTevE0C26m7XJXV+aA7yxB$aZL2X``8XylEPU++|HOf87TTK_hC}~3e zdRWqulSP~=@*N@zto$z|jou#0$m}c`_L9ipNA!vmQSf=zM$CrxTtyz8=mZv22>n$& zl9UDTX~SFzOA#*RAzSM%xs zC@2pr9cjOH2>UZbCCAimVL?M(z?w6{7jc)+NV@YwMW`fe)Y#VBOvtAn=q{X#E+Xn| zrAMXu-|nHt7$@(qpo@ja^h!#6>SzqI#EvI^)9Mu4z@D|9ctpO}EF#u0q+8{EYxGs= zUvxBeFco7E^Q!@Iy+3HR2HTWa)p|^mF@cbx2xzDQu9SrL3u<&2ZZ0GxAgVc8zd{M5 z<+#3~>d;y0f=T1;zIPOqdj^!B-wiH4FVKE?bSF@f7Nsx54M_YcRln4K_Q(Qc_UaIU9BwQ;o2o`)QX6ayBq1YmK|V*V zQgU8+{HW^qcvja|OP7w}&>jni$Q+R34b>d5KtA$KQ_Wo^j25tlzn5}Th~`+x4JtEH z#5Yz(=TxEs&r8)M+;zFhbFIkL6@bXj;_?@1`H|m3bO}<4BK}0u@YS}yxE)&+%hcJ7 zcN}=FONQFYPOr2bN@1xmit2O*bX`72+pIxAV(_m0gzh4T6i+-k;g6=ePA%%35rPA6#xCX^F z6&azHs9-7ZaQOhH+jXZL15!nlN0yY;TXrEEb*i6aS6M7B5rTbIJ~W+n>VF!$*4Xmy zbGw{;QpzK##6opz`A2pr-p#wGkDXZirBAiw{)M00UMU3b4O|t2svixH$#C1TIW=V2cIs{uVJ`)L z;{&IKaaZ!wqF9;M!;Px-`5*X|W>2+qoR+2++l5MTB!cK^#RQI@ruAp9^4j9F) ztZyD7j&^SYifh!$`1 zkvk(K*-1)bJOM!IV3BQUc>I>zo-z_28?L^%4PxYR7Tb0FZau`tNFj(%6*3CERDDZW z8rQe8WU@IMb?R6k`JP1;$KErr7?oio1znU$^&N43Rs;4^`r0l2r-HWLK}uOj=Kkj8 zIR_+VsC;veMZrQJx}B;h9X=y!^d1JPA>HKjuanz-tMaTI&6dSxFpEm^vzRKs{@E@S zDOc9t4SHW4?an7;dS~IJprTVGJa2a!76Z!U({k6fc3@W3FZH4|nT3tr zx*OVU)g<3uru6=<%;Wlst^GQiZUuKKq=gZ)Uju5suEiskwy7?Upc&}J190i(PQqOP zN*D*Z`Mtwhm@$)56zz5KX>+4WX~jN zyngj)fU*~B=~5+=a)d9ZhrXCJY3UyX)MXr)c(SAVY98A)lV6r!O|_oe1?Fq=uU^45 zKLb36BOc%&5KZc82E4-&ab-&h*{ZyJs}*R+aW{vCTYi}XaXk$%sz$x$M!EMKS%}_g zhW`MH$=fj?AIadO?&(PFQq#y?do$3lu`+-wj^?3YfDfIiKPEOkY!da>64iD6XZ)jpMB1p`o;*?~!+%wf7TO)N4jDD=oFj zk(}gpAgn1S-=|6y$lWr*4J@wYkVlnPC_LbEpr599TD5~D&C=2EP3lUvM<$+{{{VMS zva`XO%001FKWxEL4N6hTyqT#QHVG%$SZT9**%2!rD$zWfq!D}6v4Sm|>|dYktr?L- zwoUFUcq5Ym4kZ3s-VDgHLA}7et4PKvLyZo;%i8b4HqgSV5GP-@uqx{w3r4~T`0O)0e9$V6S*$L zqfTB~A8~HHt;#kk@TFU9X#`>8{ZI?emeP6w)YjtwQl2{p;m%GxZEbHl6W0AS^1VsN zAfud;L~>u4NJ1j5E7Rj6t!Y3uS|35>GvK-sNmG48WhT^nV1TP-XX;X(E#fvD_6lQAj_z+GTlp}huiXl+U|3^dGV zK-lOzPN?yysbt8*BL4s^niOY)6*v)6kk!A3a(rm>uoR_(3i!W9>YQVfad|M7Twbcu z^)GOcg0X?y*(Y9#Hoa@l1f#6qP)wXM!BAQ)e;Pe^bOCm!Uf?&dt(^jFYF~EPtEB^6 zI z1mD~2q7j6$C{Wj(>1N4wH1$X)Vtw?7$En8x*5{L?PI zp#61cxhZEM$FWF5j;T&KkI!oje%h~|t&?~x>fNTK79GyR!)W}bFsc0lT~Qx?f`tA# zCi2<4epZ)Af+cq&kz`O>M6|$RSK5ZsH+|-;wfIWnRP6Ki&(w%LEpE70^Xhp-pNN~) zwR-9$HM>%uak|_W2X6A7rllxg7r10NpUde1f7NT+v_V~wbVo0d>8EM&@TkgI3Zj{@ zf}{&QYZ}^M+(s+MgS%-4=Usd&WLgteB%7%S7w{ewFMslbROOl@80C~bKZ>HZ5Jj%f zE)(s6>JW?c28jt|$=1*&RE8Pc1HsSmj{{Q&ECJk{7JnfYt>6|+39GmpwDPTBl6W7s z@{QEe?5_k;#M8*_xiVC2wn(>#8c`F=>P1C{qOI3ex6o3Lj+8eQ^$$#5dlDq$LesfO z*a-bpjIL?)ER_lR4bD0^1c85LZ{GneMhtrx;uUlQW93GBDG0h62Cf75-V&p4A9ZAJ z$**OO+hRby(tY-#Lt-S=8402}SN$+ghLs7~^bZKN(3E2(NQ{uv=`Tkgr0NH{s|2N`K9L(w!fj6boPgz)bT@yEMv|335y5`GpOq0^ zfO6H!BeHN$vJKm~Jq;E%i%VUuz=v^)wy7ZaQ%-pj_VL_h@-DPgq>=~j`)X0cBH;7T ze;z~a1l=V24P&PvVPH)!!jheiwpv*q5hBz%ClKmNN0o>R_}N&o=qPc&53#3KMk|2h zN;gICk3CjUiE}VN_E7AZUk0|G|{ijox~HZ>tu2^ zQ{! zm(@t668y%bn?n_cxAZ@6vW+NUSV_jx_rmK$E5Tyb6pU{p6~b|~Zfx!qDRm1~i8Dm) zSHQ1q#4wC9z7=B;D`8rb_)SP6sbPLr!R=KrSS*3kXsnk`yuWWJ9 zf0gaVGa}J?8rzfV3xbPZ?y1$tSssjD?k1`AD*T^q6w8GuYi*96%CxJNO%!_^43}aH zk>OHW1g(<2zBOxB{8?Fd7W34KNaL?INU_`CJZi=pQNV?|5{NBW$C&uWgTXevOF0%% zaqegsa1pt(ljlURlx5&bo{g5s1tH9n>j$Y*t`F^{{{Sb@xn6%zTijh~PUo@(yL8l( z_mfkNi|7i<+g--1!orL$8r1qh89OCvy4U_JE3QEhb>OT-xD z%;IcLL8#3u{+@)2-Hj%1G9AXv2)6l&YnGwcY1>%gIRu6AfJE}x?c39erQfdQllv_| z`)LOI_>L3JBLWZdv@J>MRda83ZaP?bGJEN)4v#S|ShWR|+oh4Nv`=OrzU$~=dF+zL z<-C>?;Dn_hk@rxgS5jX5kP>EkbL6%hAsq#}pSF))vdB9&WCC=`e1m=7)BgZEWAge5 zNlPPTB0>n>-tFI(vk2B$irRfg;~N3$1xLvz(8U=u$!BKO6x!7+llI2~Td5WVAb40% z)5tY9^fB`e6VR<*BU+m`2|uBfI^xx}2rer9HLPr$B*T$$xhWR2MX}?3FG|~bk8!H# zl)^1>r`}QDaeH`B9@KC#UW3Uag~ZXJTPjuhi_&x49fpJY66joTC)4!e747H`Q+le{ znEt;ZY(H00TaV>2Cj_QrDum(FVuqbO z>YCjtr_i~pl#G{D6DeWRR+R-K^r&k_R<+!hwx`%&>rs(qTkq*i$6-nI6tb5UhHS(r zvR0IZC9*mmf~PH0<&&0?#RAF zlrB#1mR7``fD0RYO2(taJn++&6CKp%#d!j z$iK_#DyITJMsQNBf*K{_&;h>v( zWb3ZFQWMfp>@i-oD*^sm9(X$w}0_~ZB#PZ!!9V+kYWZErkvR3r62 zNv}ZszAulGjn6dG%1F3SB`Vgn9kzy-7Mkiqr*`QxiocY&h8)|t6|5*+4`)iWqnh!5 zQm12pjMq+uUoVgH3=F@RbxVsWg^UMvnlpFbLso* z124r?*A4E=l6e;vlma7V&+#ThQ5#1=btmx^K3UDl+iexwo&{>t!KZtrQ^;XI?+eXU)n`Nb}!Loczs zdn%V)$8U}uBoeT+pRjmWK}XB{7ha`4$Hw6|(1?$x+?FlZ97B#tY~4xN_uxSvvYr0` zTvc!2D!wCSa_^BR_@;l2uqC=)O9`-265jP~^0@%irOefLW=r5I%aEx0q+ z4Zju~3M;u6@wG-DFO(nVStT!q#!K?_3~_GTcXPJSr~RKH$uXm;B?&JQn|TzA=}GQ# z+8dv(U^cf4y{-QMw%L8a@3()r^kE$wN<4=bVIk)ssbKhMY;Ro6ye}V|#z#pGUw#X$ zNbYOu6O~0nsmO6?han){`KZUOK`R5GO|>A?RD5QM{{Wu?+aDp>U**YK^KYn5?#zJ0 z{{S@_1|TDAY_W{~Bfb|YQb`>vu;V^I$JgFnf2qfET;Cx);w2X5U!G;*9LFeWw{VWH zYCdEADz=?%rH-|I2y1y_wU%@8vt|Bk9uX0r-0mU9;~ak;;qhI|DOMBQeNF_E?AN7P z>wI(Hd3`V|xALm!Uz4jK(s?H_&B`+Q0%AhKf$$+#1f=Q!3D>1ue%n^OF%_8UmRH%yd}?+)x%{*= zTR#bnwIM^ygXdj09=2k-vy$Xxt7lal5Ns}Ai9uVV`A4geC02R$l_giUn|f8GwDv4t zsHvlp)~lkAP;yl5OH|YPI7v=TCQ%r<2%?`B+G)=EQ!^7RXhN zw6}-4x)n8)a<_0CSBS3!Oz~a%&hswfepkL@Y3TJ7eYHOji(c zjzNn{+TTxYOjb>yu}8uJg%&R$MyhJ&_aoU-Fh|i<^gnH3gQnK%Yzerc#zq^s;*G2- zc>p)ue2r+sV6uEZ+AL<-jMvmv2--%wy7>84k(eAMSFMpBx2~Njq%OzkY-la55h6Js zGaU{MPmYy_vJm+ulkow2^yyiFfdbMLehoJD6`^d4w_4 zH|=1g^|!oL71emM&K9PD%!!$Im)q+dN7UG%;J8n`u`p|j=Nt%Tqew)@9X6W`-o58Vff~C-p zQxAS)8a=%qyZdWAk*qO5;!oLI$(SA`)RF;6vGK61 zHR2`wKlr_7OcX9Qp1&3T%Ertv)aIKe?e6<2Um*T0aA!wSf6Df0Yt}xdoLU+#OFC@_ z+giw9Aal;4!fd5n{+iQdQXBdnq4+PaiLE%2$%Mi`4u-YjO$rFxENy$x5;uYSD|xj7 zlc#{DmO!rLml-xc<&fGeqp2WP1j#LJag>A>(3b~^vVQ6VY}|T}ki&T(-{OS2ND)8V@2)jwYH*=VMp>QVA%cD4`yFO74Vm+NVx62W*rh(Jr;wz!k2P+!x)W8_BKS8ZAYj|(!zrLWM9y#^9hwNI5F3bUz|Gp%_H7YZ>-ilk_Z zAALeOY>TaT8vLwd`BqK;04X$bF^&b;h{8&XpB4HK!n8CzMm8m`LAQ-$mJziDfNa(OFPcBX%4+g zi<1O|FI%hdrsF(|LIbvt;WjGw3KE+{lE8jD30hJVJT^hj=MlSjHaQdRDefcKfu5ZbacP(>c){Ndo<$<5abGz)UL`{=@LUq4GSYY}(T+u+-_|}=SPX=nE z=;G2+dW8$nEQ@SC66@EoAdkwi(MH!9DmE8!AOnB4mzJ|*@Yt;(gZ!R4)R2}PBs*N6)7ia_a2o6xe2^vjhuCm^bNpn1k!$Q;0pYz z8Vj0}a$Sj6TvO^v8^dGSK`2)7Z-qD3tEFw3ETctD8ho+>6aqp#>cWfIgQ6sUOG#*} z;opA>G;d;8PzHTb4a2R34?*L`4 zh6~0iS`cXd>J;__%sFw?7TVOCcwCW2ZZe4!q?q}+(f|pP$WTbN{gjWg9nFLgh!PW_ zC|UhB{`$tGy9JDsLq}jG7YSYM<WqdNgJm+eRE=)|4!Qpn9IQ1z{^I5Ot)jH*psB zQHZn+ayo8GKmd4Fz%*)@&AEN&*Z|V7(l%BpDhF!5&pK3S9QG;jE)jH(zL^yUJsC;S zNjE)oHm#dXX4!Ftg|xB?de-L@f>&lq6dAGPfR~aI=YqWHWcr7~z(sh{zlkXL4@D0e zhEf42WsMb%R-*PG=qht$6>LH5X7D}@wWiIc&?!v08Nw1u5;|L@Nkubx`h_Abmw-!y zV}rzanqE=IVD-ggBc&t~q^UhR8U&-0XObUmrO3+{YC3KwLJbMkp5xWc_sMH<{(CYe#YdE(w1IGE=X@6!iU5~lCghas#)=EOQCmPGZyf}Cz0Z(H*(rrR+mbLiT76h znZP`@Yp@wM4mxkfgdqeCPyLi?u4qomRJswz@Hp~p9rVf9pxSl&DXO<*#B9(~7=N{r z;+5$-+uHflvE7i2xaX3Oo7@+Pv`EN8dWSllYVy4@qFes}LOCmwPm(OD3L#wpI$obt zAWw0ySdlcFi3?a?=Se1`8j!2Lc^o(ME-SBNZ|XA{ts7T1XbR#AsF9oRf`f(Amhl)IDQs zc~<2dhpHzcmDL1}3R)P=1 z;kd##2*+svq@KTx4t%WV(IKel!YimqVff}XKBLYdr3?H30;~PB2<=;(2AnEOs0Lpl z#G<1!v?*AzRYRN&LYBTOCDYb%DolDvYXQG#vFv zS5j1U^XWnRY`hJ_eF3I(NuHuZP8+1?;#7Kd^PpX!7aTMiu2OKCXzOpMe(*!&nFcsV zD~}zSGY-sD^^~@ir~o!Blhs65&wt6k6T4Y^)VJg6eHr{?^7?iq?B9&~9hb`ZzXZiK z=+GwCQtVMfZTh0f;j*~ieQ!X_$}lD< z50O$+xhqxr6XEQxcb@m@ij+F9Q?K~+^>2O_JU>EpH-K_D1J`B}B*jqv5`xC!I_fH) zTbT0wea@2p0mrNe+^#*5N*D9cTxrqKNKN(kRit@Jy$XA{n_{}6DC`V_3ffXs03Sf$ zzl5C>tqr}j^fzIGip>e9*g^Ont&094Ishq(wowW3^1-B7`8aF;07kMArr@A#e)^oJ zQ(Zt;3u~7`UjWF+AC{WhP_t_&UaBmbgUT-{gzQ*h*65{-l3eCFmKqZwu*{e*T>QXrY4*~3JgvD= zl6eoi#M6%`zJ(Rao5D!-Pt77Ur46CqTx}n5uH#1QV$+g1?n*VNVMyifK?^;PeQ(9j zH1JZ1Z7n_*aqh1F08e5QjvR1#zT78U%w>=iUQ@_nZA5~nhgBN_N4E93@$t@3q&Z7h zMoh8X{{Y~-jg7t0u z7wCIyw z64c=7w0Ts%$skhXB*4z*a*{`;g6dLwo2Y~L(Y>gZ?_$OaU-M$31FA;tZb>xEU1nb;Wk5)~=4 zCjJRJ3sEDrDXxI7(uGf|8MT#;r6n!JLRGHNMzsZ8zNVG&Pq86~m`g5;vIsWbEv2a& zXp${8cOs-sF;C*;xw*A$TJK~mjkh&bb!{T!UU8G^OQYfOa?qmlC=PYrf@v4D*K*C# zT)Pfg;%OIKQf$hueKief3pU>s6-n? zQWBrbajj$IG7<5h)#0J8#U)nhw4&d(n0HGVL>*sIi;%(sn23hjT6D3oAp59N*=FOe zutr~xEuU1r@50|(Cf{`$Gj?<)nsEL4-m9(K*o(tVvaefOYcXiqLS#|799f)#&{9i?keltlg^rZG z+~wW1khMp{^sdE?$1|yLp)KMJ1)vX^jN-wu=zZdpI?v62b`V@QIwAWOyMkn%IvVv17Nsz*N zs5`q)wyopGG;^17S?&mHk>ePBnG&Hmw4q?Ap=k;u{{Rp)ccErGiAR#IGRsF?_Fw9& zhH$)-aXBV7PD29tN?r9ft@elb6oLTjKmbJa4m~;KL#rYc!rI!Q{lW#&vNO3Cg z(4Lf?Jk`}cjCxu%Si1E9p4LrmxiL~vF@>mx_s2@Cp8^U+9sHi$^Kwb5pq0FQV#`|0@6C)^CL0>tL> z5K7>~U_PR6J#tEIe-MG8p?#?Zzc6-Bxf5J;rbjN2?1USlJSf3nfI1REwKHEIm4=Af z$mIhZ?>7^~E7#58=j0`_0YeTYr0-Xm1EpElz+bV_GQm%G$f>KmN=v#wQw-;zJ%{5w zRX9TMDGvbJ-Q0Q*K+?NUjeHf;Hz~=pjrkYOJnQhTIA9=D73w;=bis5<9$B}yZIAOd@8R7`?hY4ra9n9|9({A&9~bzcu9)aSQjcD&qV$mx77MQW5%rRq~_oBQe2 zQesls&vAqD0FXd$za^t{Z9M0qp<=CVtsZqnb^$AA>I-)t@uf(0M&{~p8<9nYi2#AmF(%M><%c@q>rnbM|PemN{^~enSiyMQY>-|QW zNK6pxqSqwrOh+UfQwJ^Us9XNpViLeVaBS%W4}CKm7!MT2P85q9^{7T!AmA`s>_`cs zq%SEA><56O+7RkNHtX3i}4GJMl8(ClC;x*Mcx+hV%;rcRFT zy>0tQ_Rs^B_G2QY1x9IGiN5uFD-1*>){+mEx|&slOlFwy+)-8TN9IX3sNoXwV^?KF zZ3P~N;-AZ7psh)y5-?z&RQ2dQuSw)9iSJY&8qAu(xLt*`t>o4O)~}1Lt8;xxBg#E2 zYB@0>GT|K!#b!<5$Ecz0tz^s?^@u%m@}etD@jR&-d?v=V?-EcG`G=35wFy|+Cj=;$ z9#QcZ=xRMpD52^W<*j3X6Gw%ZL?sTRtG+<8Z)%sg&A6mvWQ+9HgYFvk#XeR_h$SZ8 zTG4#j9)`$@`i|0@c^cHZo5=Y%x2>vu)uj?XMt_wMM)GgxS>kWV*%-ExZayUsZEpzN zTVwd~7z?pQ@8l~)oCRNPmfWyL-!7B}$>3%qw30_q37jU?-2WDNwM5>B?Zv)QdH9P(&T~m zP_2|CH&qjlRLOgI=`RppT2ikSBz2_}kz?(P{{Se+B$cwXJe%sB0ZnFdZZ(b}M3;Lz z6rn)TmY_kD!aV{y`-waZwZy=9ad-Dp1&^KI9)Cd+CBRk>b;#p}7(xIFv}~)QV=U+NAKA zSGU96TFXEM;UpX;8`Z?!F|*R{MW6BV&6^cO^;+JOq`QUd3O6>B5`NZ;43YXe^RL`+H4J6ge?aBiuJ z(`v8yQ0qaXaH4Y0HlPZ31fG;RP`jIV;f0#XHsSsvEG>JDHTx(kpz80aE>=~hfekHc zKf8S@e#HwTO{)!HaMVR2$pb@JY<3t()Egf<%n``Z^#G${R7GMofiEe==m|}40^Uq&<4&aY zBvie&8Q};YRCufLtx7loEZmnGB&B61#zA{~jCKHA3??ZRV8% zDLt+xoBA=`PFG@wlv~igq;sl>B4@bPH#$a%m|655znLhwHAQE_VVuTTIvRd+YFKeb z4}KxM@=-g7#VnCzt(&B$&i56y@ho*x4!&Zi-XeJZ{{ZGOkG~N(=-DhB#a-NVq?bK3 z{{Vq%)P0qgd}2P2sS18yB=FWy>US!On`PXd33iS}=HqSyj40}K9Y>L^>*&t6hHCyo zH+6^&j%RX~)ccL>@G95#)79#y+^T!iOf=UjQmm$?sH?#w>-f;+snn0|3^N~-GVuO2 zlAtvwp|+J7(S$nS8W2`q&hjS2LHCE$`r z<)OxZP`=bmIgTg%vJ{`vZ>>#SY0|JaRxPkKM)e;NMOO>DhOT2a{ov+ZIgr+#4PNBz=?*Lp`x~qegu@kWz2eAk?Rjm?N@q?TzY6NKv5KCxQ}y4NzWX zL3=tLqNIUJ#)DYwqpT3ygzf3Cl}*DToVo`Rrlli$OW42NJ~XnI)I0PWFLHHI>ET5n z(8<8s#g+gEsja3#pmO3m!o5`dXdYS`!an8~K%{jdLqIIq&jbGeZ3cZ=jo}-um9lIG zo)j3%B;zcaI2&4jDBVpblxkAIE!aN@S`==4uR&4BNXr6j%*x}JzB8&R(Cs%MQ(n;p zZW%Gg^O-D^CR+sC^C%ihbdIZXPjTGYi77%EmFcg=E7Mc&s_APfF^Fzs)R@BMk46AI zp#8|bjlmyf26}0}gB9{qUCAR{%NZ@dN>}i1JZvvPvyYPOJq}6|T}Lge7X8&8Xl*(h zh4sJMC=XV|5nWsLIT5&06}M^#9wOD<94sqgMlf!;L0YYED#bh@s_1Qp7{Mtv+6u3! z)7wkR!KhNhP2!e;{$8iZR=vgzRL9NZCM@WUp*=~{`zdcWGwraHemd*pRe(oQO-;-+ zb>2tA3YIQNQ?}?X0R5C7afWpyGmzs>G)N zkm8b87?N$&*izPQ!4B2Own`%5r$^^D83kd5Ui~7d2CQfvX>=laa!JazvwYJk9DJSm5R9ZU`x{1W+&3uG-vYvY2|Wol{eb!#>FUDt$!` z%#bXvzP=^?O!!RrjmLEv@Rwicea-x3^LqJx zB~8hB#bf%GuP^Nn739b>@Dtm{${MF@g{K0D=%AY$*E7WYvdY#bvUFUh#i3?gIy0ZS zCv0=k8u@&qEb=(XB_U2(%WL4Ht$MEw@@S--S?0XI$0V|(ypDU!_G^qf`qZpkoOGp= zcF?k<1J}c#uRzoJJ16W<9!Q&(`B~3|KY~4S^Am7+Pz3RhbamT@6!ujY=nj4iReVa26tCR^o%?}hsS!rtOJF6~v71pHE;XU~|aATYk-n z{!%S3C7pjs1q2O2wTP>TQ&us8n%$ecyMbh9c{7Zv7|qB;Q~8(Hs-rL9-jUmCWmisHH=rrnFC`m<&*F=MN9nF8a7RSTO6Cz4B| zHmH5u9XP0wN%XHED^gXcC#7q~MB0EFT*uuzQ56g%AB3eoRLn7fTPH7|#C+<%D%790 zv%)_^JP2;h34k~euQX%R zs!_O%nXPdf*7F5!8(1d|; ze;QKWA}nUEa#1W^J$)ZfO}u5*&%6jo8vaop0g37rLto+GftnbC`I z$OJy3lA9fFb*?97GxEYmcdsh^6Ui_~KH>Q0&>oW1a_JjMX+g3^pPT-4w7A<}QRT^3 z^)`2zaM^Ap#O2wNNVsJV1nF-AXitu{6aCVM(2Hfc7ZjJhpwx#67HvS-U&m2ua*ca& z(689qr^t*$kd|hj^3q{3GN*Y^F z$6Ghk{X%bh)~w{?(l)4rapKv0%>tzh~XGv5hy--Q4 z!_LnQu%Qle2q|r4NEZJ9#YD1*PCX4nX2mYV(o~0C$2k)xi%gV|2z@qQzVy{TCseZ% z?ofrb{$j1mKumcldlZB>>I>{zY^#Hy*UGvLO}N^zG;=%Jj+dIoak4WOg99;(5+bks zw+R7AH|hwcWlUo%#9liyn#6`#3@REbbh@(H7e>7WGfmIbS7%U;Dfo_`K~AU<(O3TZ zbrhCIsFRr4mfXTl&jpj=ME6l2vX`LK`A$&;xP_G@=%j*u^`6vUa}{L4d$$V!=Vl7Gt>UiIgZ&1^W+0&de>rcHLS!r)IC5}-M zWN!Jkf?l_coNQblZFx^U{5{8Pugm#a==>-0#`eES^+##;-|O3sN_`CGU{2#Q-8XUO zo)RH~q2iU0K>I7i`M=>Cs>_4P>|7`E&b`ek_dBnG{XlV!RROg?%CVnP{{U2X5*yXLc|U{f>&dAn(Dc0@l55`-;UvpRj#r13y)5L58@e|oXcp6;B-cA<9ef{R?Oz-S zzZM2isLD)Cf=5KMw%#XC6oY!*aK5*en4OZUex)zXcxtnVe;J5kL4MC24}iU^(KR``r5=YDx5M2T^cl^; z<FM%}J4yo0;C^rEW^pcOQ$W_-|cz56?OmitKVc*8sMq=KHd? zo!ZFo8dQmS1vIM#GRj7$%J;Q}baD9)k;=b`cs{S7F`RZY5gE}TOOfj!`l(t$NWX|Q z{{Y@yZZXi))25{ILr*E<*wXZ25pxu~fa?Ac<)vKJ$KN%alzTj_5O_$AP2@O+>>|J< ztEne;wPOuW7uA$^a%uKiBa3y(M|#*&LDZxIZTAX&TNwI@s_d7b8g?#V)UdGCue!%i z!ilf72K$O_X-nuuUtl1U*41dFR5)XxEzsDv{OO2GAf+6QsHAwT^7tXxKX?8ixQj){UDb#h~3PAk>taG{Uj7Qlr>dIG~>(q=dU6{$0x?R0$Z4pl=-L%FS!wC34H{lb4)y9 zq`5U0_8XtVw=I;tNyeEn6_oA-RqkdQ6ys;B$4z~L)U>wa=n-m2hn`9h*P){Eam9#`pLXF;p1MT+_w+OJarWrqecOfWeGTP_ssl=}FQ?dUD{Ue^ zQbT89vYTyu8~Ic^FqvaQ(k^Vh)}&;*hcwvT7X&hUV}4bJx{8L;eG~WA{86OLrL>b^ zky(Qx#l&B;+oL>9<{cs%o+>Ldk1iDvk|gbc<~jBS9A3jF1A9oj&@^ z7_vDM;*F?FiBgYIrB2x*J|p(g9N6skjcNqrRtF`(ouw%oO=u*ek(-X9>IzR=TUy&8 z?FcvNYk%#fSV+eM){@CkU#YX%YQti(p%>dq*c+GEt!UU6Vw$*83Kl%;3>9o?dtcjI zF!Eo0vItK+keHvRfh3(KfwgS5j6y4k2VR&}FqLqRgcWFy(-u@DM*C>p+wq2cw1`YaVW{~Q*JWH0 z;BQd&Uv@KH%N`O5`-X1w=Op;a&$3S)JJZisxhMkL{cg1{$74&^c0Pa5-NKt-q4_{V-E;{=! zT>O4HwwZpuO{&*Xn){E&UQj4gi&_Ss2pWC#+Tb=mqTlL0f1t_k;o_vL<=tm=WofZM z8frfs*oRatFO$({MmkKcEiII(GLav4peSpt9Z|A@yY*xVl;0d|T@#S!n!1m|oUZGa zMun$MKTs@}@J%1#J?g7Vn_AUjU!SR4KWJ#FSocBL;7RegwMKqymy|FEn7HUq#PV0B zfE$`(uP5A2`9}nG*@@mlKP@=p)InG?Td7z&YG{cHPS2>x)FF2_vWHs1dp|HK`0LcA zJE@3v6Sg3Awz0SnMZ9Q$6ja2?d#)MYF2dzyBzq| zCJt2OO+iTm;l1l*2CgKyN(ya%8~ZB;Mhj_4C*da5DM6wh=+VVEEn#c>X^2Y%9I|VA z54b}D{)qA>iIWX|KW%8x_aBp;ij;{^lv`N*$?#C}H49JrmWxWqQ3m}#u!X9)^boEF@ZOW9LHQL!=Ec01akLg(`8 zEn3IsB=GtSmX`tC6}S%%8ZwY6R?wJrYw@+KSRsMKQk31UZU<9Yf^n7y@p2J@fLe1%t9NyjJ!m#DFd@~;&TQV8)iA7~8k6IAlEaE+utQDQQ+~A!o1AbBb~zTf zx7uq6;ff(kY*|SP2f)!IJR?JOw-tMortRWDsXGydt+dj@eHY3UdL;WQE=?O!z`EOz z5;vycbx_vkw`9zD89Nq5iqba&(KHCpsTsp4W9Xz39B0#4@6`N(LcB3nQ|SwZ9Xu;c zJ_4yxaBW_r%A+L6wUZ7C2cJTADJiNtwxz-EUrMe84O)cPcioO0avv`$j3Oq7~3Sw#&N{i zP2`lc`CqT!QnI+VPOGJm)Om3hjgADVN&Lse(zC6i!V-%@r{rAK0S)_nYB`ap)Y*pO z$9UN)MxGX}J6i4y>*bHy#zky}Eu;(eze`YMWSG@@u#a$xX5io?CCAZlIwd#PuZ36p zcFB&$=tXoxH@SQk?9@0;N?NqsQ)JT>=z^B+A3!XgMaUgChR>kdg@Ced1ujJDu1m`O*5*hv% z_{bHG7+D1!mDpm5l4m)%yySv^LHwipD7_Ns220DSAw!JBLR@vgg@5UheQOjPyN9kx zKBGA~xTsdD^b_=8chpwZaLB;%aZCC9RnbJ$Fy_`W$X1sHi{uxZ9k>(NswplqNLKd@Hc6qjI2oR_F~9n5jEf(*Q1^-b)X z+wk$FC&o=np=YnlS0$D|dntihlBcn*#vMbuHw)gh=(;|mj`}`^6(^6$N|bU*XK1>? zMYI}t(@GYVV7D>^+M8mbm>pkQ=05l&I+g1FR#=U)Pta<(l3Y#F=(mN)8dC}33rY!t9EclG3y>A&1y3X_s|aMjNuW}% zi1-?ln_}_87^(CfHh>3Cw5nk}-@w>395x#HRyM*Wj~rx&zsn(HbQBV69@g)RO@cRXh;d{!odvg2`FxJp|><4<4YkNJ?Yb?a>^wijOo z+_JX1F7zRJd_-8=#HdSzj-IF*A855*Q@t!KvX5hphI%3!BLR~tfS*DL&A=pl6OdFn<~iLI;E3o;f?y>+X2 zcw@3E?5fWBVvJ_G4GLMnS|WEm})xHc1K^{q?wV&pn9S!Y9~^$lx=ZaZ6MU!5^D% zQEwWyua;6OHa?#ewpPMb%MgLzDzV))$dJ?P~)?>&zSY+?!)#J}QcZETW!Ym?-t zg^Lr1f57SVY_g}C=5)Ua@6RBZQwy9%kDOvU08V+M={S&{rNpHCN6$+0PG8}a<+}T` z()hoZ+Zd&1QryRKj8`jKQNpKVU&#Y@-HViVG~}E0^txZzYmndIt=8)}<$pt^)#a$z z{Sw0@LoMI@6o~TH?2OhPDoA=3MWFaqg@RJzY-t!%#?5u;$K@U9Kg#lfSuXx&aVHlw^zk=ti!6*27e*jss4KU5oSA!+KyK2e57vS=ePz%OG}eD z{{YJ=Xje|8q^Dk%s~fa;Xpf4Dn);RNlGL^3<}7pE&g90xJp(5bwOQ`r!9o+dV=f^{ zQ0c#7Hy>`a*AvCru^$=EHNVIGnLWNj3#?=0iJxfu%k=Bboh}JSy5uD3R5h^vC*$aUD&XK57ZJy3*Rbq?`Q18(eG}{H1E= z6NernlMHoX;Fatd5|Fam#)K4|ZhY-lE0RM00JXK;f^u%(VKXdkPbm^+Z7M?AxKT(Q zENm;F<9SaBqn_nBcO#ZFym@dYN`mWBl!*5!TjT^UZiid+HR&97^P0*$%azgg-Wgov zVsRjqywn#}Js%5^l%eR{bt2lDzoN~g@th-`zP(I#>O>^JhDwrG0T6bPpjl8O?LIX3 zjnCAqR_iCx6ZkTBMQ%g!6}k-R^6)C%vVuVo;sY4 z#5(ixJqC_Y#EA)f+b58vS?4#HHOa>pAWXM z!&0N;tzi~o^@iIqdfp9r7_1_DTH0pKfmecvt)|SzOzNZ7^}LzljrO13tTQInuM!n{ zg>Evz2_aO;QhpT;ZDMM1w+NSk>lKS7#U}Nh;k=|GS(9seq~G>X{99pqBi|^q)oQNi2%G zB(gWH0cic}-}Y2Oxg;a=$hM?|SO*5eax~ZNtTO|+xst-PUBK|`Tgo&@6Fx3PAg;-G zr@Z@W5_k_8WOCavnXJ0nMQ+yL;t*_p9}mK`**M`IarPVdfB@RGvTqe$wT9~Vmt60|DQhi4jgoCL`vGb+^dt{a~s7}c3xOSsJqHbwmq3H3NQ;Aa2 z7rWc2tr)tZMBXi;Lep#MrAN>*r8996O{j$YHt4IHn%pc4j0w2Yx_EW*sXm~5->9<= z%Z_fB54!qRU=o{QG-aG!gM_4~twM0f)pidCQ7$Bn$w$MDF<0;Y|53k*sFxji*AL*YT-%rskElnv7H7b;bV>Hh!* zr;%@OsIq4ImI`r5LVPc4{nbARv8AxksvQU$n|P=b<3RReXWYoy$)(TKMH|?NUnTVt za%ZW3;t}@Ndl5IE#KSAyr?0pRUZkqPwBCJ1)#$F5VX#T_{6#`>!3xZl z)#v*v1mzr>EEFc{u|5W)?hD%tA+^w}?nu+7lvMtL%umsXzMv1VQ$q5sHY8xjHwkb8 zD@4>|fil{X4%HLwtUwY%MfZ@P0l4wfwoXd|jm+6#PV4nG4+Lu?mhwQIq+i=nb&^l0 zq9m2x%B}lopSopkHf$Dyan_^lk(9C^w;3QME$tl&twm&JMl{2GBx@)}{dWq3o&&IIU1`A}1Twltgg9a`~ab)O7^XXCn;>`qCJ+;3e zu%X~=V=9_+$*EI;tML>LiLG=33R^2jQCcujf}~w4`!7q7faN1~^|h&pPb7`g9Ba4j zsSEWs5!RI@VA)CNbg11{S&6WTedz!qW!)q4xov4FRMQ&B-N>p)xFKV&wxis zzve3+ocpU@+9&QYf1gcj+X$XdIPh2mFKHB7>*_5zF|l?-cT;W&(re%9lK_uqlOg443Mpm1=N7+Dn*h7%HN#TmQw%2W8#?Oz9WMI%+ z1}jP&wau;UG}xM18Yw$%<9gcx?He}Q7LqSz(#$d9*Y2D7(LK{x5hAx?9N^I}Eu(uA zdWDEBbHMSL$xu%J0EfziFszI2I8a09cS;l`gL9TfE;giN;vm?SK{Wj}d(+P34D&V5@F6>QiB2dZvRU`;*C)$8E&epd0=5 zo?Q$FD;zhUOvVIZ(@U12aQ5uKJKQI;^6}fgL9^=+ritz-f zp+0(POL;Um9^tsrQmw8=mVO_~pln46*@5L0Tf-KVr9ysb45#$~unN39O&PZP40}m> zjie4G2mb(YxfvxQ_uFth{I#r9%3h-G^#ParzmLXH82%)eSN{M_=lbdPQxMl_Ww3MF zPqG_3&G`(!h75MG?W;t#+j@_*-kAG!^%qyR4EerCk=8_~oEm}xibwWV@R1)WF=+UE zkYu_Wxz$Tm$)V0m&|(nCbt;a)+E##{06cxvgr$+#Lk5b=u*brPZLAK8J$~C%?T6(v z&kVzSoS`-t*4#+FmA3Rs>p)i2kntJ$FQ?#&hEwcKh@eW9>K*LKnQ~fPDO7@8N$Q)} zP^Cr*{nk5-cZkf-g6?Fqu}!kV=#<@#qKPt!^4@T6~5GgzkNo zkVuY{#=k!s0%&ybVsMr7tPQ%Uuut;^i%O{ks;1NUUc%`o!Rl-#LA>UBDr z*N;G6y+h36($i>mbE$2!&|d04WkpsRGQt*nI)+p7@EHwlHZI#!LydiMN_Oq*P^V4@D~lbp;@BxEH?g^;9Rt+Z*nTG{kj~UNn}9lt z(B-XP(6pnFm((*Cl_PRh_);%Mx9BptX_9uiG*=1~>w6Wa#FBbav*F7sxvxeS9D#%{ z{WeWWD%7>F-AOJh9mf|YnBjbd)uTzooeESmx7|#~j*9d)`8-FXGM-_aZrgH>DWUmW zD!ND5X>ZwL-&8_e{k^hjGnC4li_4x$$3TDqBx$ac-CQgp`v9|;n^#4C@HatP&Ho}}CN#Ac}YII;;J9&LV(C6{@ffIdXpWaU0qwl0H*1yoP*W1?hLFRFs za!%6DL(=svv@JF@R`%_ObX`MJUTZ0aWARuHfwLmppZ8P`Pjy#Y%uRIh9;1Y*^~icB z9)whu<4a23PvuI5A%8D!I~N-Kb$M81wjUK-grup)(&CMhfTV(Nr;T%W^AgWftJdM5 z*K%I>3eRJij=XyZAfyYbC;+VAT`yF2bC8s}^e=zY^McHJi=zuQz}xQOoMl81^DdB* z2-C;PgFa15)Q{NDIlw+#NG$-AMvmKuO@ubB$KIW4!&=$;u}f)=I{{m7j&1^8QHI}e zpY(+mBkiUmJLUR%p*1~a(cuN6#pBMYF`=OdkWIvdxR3{jzJp}8evDy5ThX9|xm+&* z;|D38k1a?a$B6FNI{9@vQtF+%Uqr+G-h>zCk|H?{d)q}g;KRphFB^p{BFR_UD(72= z-0t#L-Oll=q;OUIu4XE5109wTmhrpMlDmQBV^>-<-yZ1YxjS{_zbh(ypBzeCREA&v zVzvHMUhBWMx@s@_oPOq-eR4xrjeFHssDw78{&o$oS5?2tQ_7zf3!4qMegrkJr0P$Q zqSt7>u*lWC6H6qKVdJ49p!QjdMJ5)Hhh>{7r#2{iC^EmvVU|_X7u499kI>Is=OsR|NO6wAmznFQGD?Y`KUG21(4LFaS4l0)$rVQ;p({{Rx!PQE&6 zO+$;VytR_HT-C73ivmLB`6!Y^q|7m0LWbmNxQo{9;OLwmL~m1`*o>3QIuD9>gBH}C z*{o2@w&1XRYYA}|7CI-W_E(tlp9!CMGt@Yrox$|67QP>ycK&41ki&88Y+0$c_4=z} z`;LVw+$>M9*BQIO?M67CSe=%yH?KFCJRLW|d&A7($5dFH+D96M_$7o$Lgz&a9RS=G zHS@iB-zmUrxiu*DJKi&#zsE6<{{S!b1DcGcqjE0bLYpFgz_{zjEd{}QTi`$*9u-Zm z@Xs{d6G;}fe7beN8R6v7{^oY~B@t|L4ier~4kv5U;Yjqh+o1%cZBX(w-FznfO?b8e zbGlY%$skt{?zbM|Sw@$~^Eoan213%1)CYdG!5= z$49_Q#Ee!kZ7HNnN~9d36}LqfK{{#Dhj$;lIr$o|k=KN9S6MFN_VzwbbPg+pkB}@Z z6lb!|Ef2T}>91Q=-nRv7Nyw|W*#aGGVk=9 zL(J70U8Z1g(u6xy6qo|sr6FBINkpMmN-8b9HNlqKkaUFjaE_IhV+TW*KNZEb6w(#Q%YU+oj)W}iBC&CQDlo8-U%*AWR?Nu3WOVVSP_ynf zQvAAk)Z)m=2&q=J>M5FRK}rg@QEHGQ2HqVghK+7K>kKsBYkn-zjpx}~HhX#YRv2KY zn^qWW*093|q0+F-3G6kzGCoGOK2@W6J+iRZL)(3oK8AEXu-3X7H6-*kjKQ=!#WxGk3(2t>nqN%%$^9WMzUn0{x+;en>VcSFwIQSiq;v1Y-=#g$(^e5t#vaf z_S0K6bTPP(hpiRCGfSwr ziMb#VYt$uILP=qYCmy9)Bl4O8ts-7V#~7;+l>us$qmYr7I+#oe1Qa;&C_Ww)xYH9Q zYZF#Ai+};w#^$wxF3gBKjZJ}UcxzCUa!#oYeoaWZ-KeO0Oy8P9h-Kseww%ybdu*SY z7Zc|tK^t%WD^c8VN68s=iEpV6eA0!_<+-T%neN~XE$o|5{{RrJ#z*DqMd?PG3aw2B zv6%!uLsunW{#$!1agY}bvPhp%RsfNIvYc2XEX*gKU&E!ko7%L^l1y`MF<|K2q2qhj znl%ifUlKvOM(?thoG@9}X()^ucFEbjKwk8`V}aW6*Hc#^l=MySM9CW}$kkC6kSv?u zrl;wxjI>_eW`#2(dvY&V_<+sFKtb(x>eKl$#Mh3EOXmjTe+J zDD@Nd2pWsld7?6lMuEivuf=-bk`{2mgLfDCe%t#iVUGq0=mDu@0iq*|oh?{z#CjT1 ze=djLTFDC{$7oBQE$-5=EF#kt5t(yUTzHT?1r5!#N>Dd2V0Z$0TKDjwyJQtn6J>=J&{$w4ElDb9p(?5U#1V^+n`X{n zQi~vqsPz`C`=7v))Q_o?$Cj~X8U;i1dnh(;Wd&MG&<`pMcw4=7BrQEs5AC8=WSZ(L zZ;Tloeb|5 zMnx+@8twC`R%0J9=_hK2rl;kOE5UZHkpMYB#=n$$3zYg` z1)Y4wXx}cC{a(CzLm2Tk0EBJHuaMs zJ5}JZP;dzbnX;SRa^0Ok@xHYgCK?F{dTSj;yr`rl9FE#cmKQ*`od$8iB;U~8aZ;;T zYeJI9D90jCLUzRZcTevM+mSU`&*c^k_FM0)jFSf{@W+v%mkMsJzRC@+sA|yX-+&o0NGjfv zJg-_zMa7}>QBdcG(kPx}QZeYf&V%AlovS=^)Y}4fYs47GzuQ3Q0-S zf7?Wjk(XhAW>M}UBoIm*NGUyQUI>Owmeff=gLJFF)Z4&n@X$GMTtOa^w}a4l)T4Gy zI)=(zhSz71tvX+&sIi_wSxYr_P+d)yl7)DXJnJImlW_4!2VE+%vfk{EP<6_guvHX2mp91goizx*~)2$LIR{zDG}LRgAEx=ZMA|% zgbP~q)GL&_i%#f~iJ8GK;bFqs2U}XV3NZSCmE=ZiGD?cjuUR)g74B&9ibkxlup_Z0 zDMXfuxX{tzFF^;EZq6xJKB)quHPj?pW5%GyY}|t3^Q?V(l8<3$W(GGUq@}&R2paiT zIWiKLP+N{WA!HI%JPD}a+%xJVh~?F)>K^|9D$^xYaywhB#Q-D+R(c+wRHF==l&}Yt zCD)gAXb+8PtOAs>H22nxWs0OU4y(W2TQWVx6X~foPuo#pA=p{A)nEk|+BB_W#D|;e ztKX)yEDT1*hiD2n-S$$}))J=2Eif%~8(XNL`$2rz`3iA6M*H=tj(ek&`mS_0t$3nJ z<~vHIBEsrFuu|SA(y~k-(j_c>Ya`iO@k0`Fz_V~96>3e7wxyIuEH2h91Si#Y#Z?D1 zg2st4>afAFZKNP-6hQ)nu5X}44<6%JoreuuUUem7pubv@u2LrKj8Hwy?1w%SI1q&N z6+dYpKI>x#vTJaVrS$gJyb+F48MlxT%9NM2SLPR{eW?8fosvU{+?qru0cp$mj=tIj z+Hn}KdQc>B`9ujxw3Zawyj|)wq-@fiXtR#|EPX<(E(_!MG7>_%0-y~h4VP7yb^Odft+@UPF{kp~|MsKtCjX0c(?c8ctOy_eG3u zoEJuD##=1u^v+dK-&Tl{4bnaJ3$V<#al3JNYAS zj*{w$ybq{3&$i)}l|fVFC`XjnsFDwYRD;vsT=n0_l8VHCyy^A-0Ft34nG)!|+U?}T zGP%%>E?4C;nNIdxRkip|qN{EAXCWT3c`c~?vw`!M{ksm{EQ5ud_MG1VZchpuBwX=J3NpJ$DFFCD#2vCCag zHs&;LqIiqwMn(f9;#_&m@Jr}s#3U&$@E8HfS$8JxfW?cSQ z5ohGB`4|5HFmyNf)+!k}+ZCq?dtO*fz z53>Y(mmtQVw+2XkwDjn2Ov1QNxTB5L_IU;?FO+PlGUFv~ofq(eN8e4=q`bOz7qfa@ zq`HE=WJ+|U<+j2Gq>*D*e{VSSMQ_|z-%#T-pfbXlZX-^38A<%Z+eu0@QtCMyYilTE zu-hdsE<#*KwCw}-R;LEZ*Ekw~GQ}+JenVkRcp&&x6*GQ@d(|&f4)W|YMH3!%%18NW zN$YQIP4UvlV}HC;2hoh;bLb`T9g^rrv=eW}gDx6f@M@2d#mfb)hRwubA3r5RY$aH! zcDO0MssPSFF~5gvNz1o$q|@GvR$pRqA2-@;zxsAEM-4IG&Kx#uugdjDW~Tis%Na$q zHhAmaGlnep3(27t#Bx~k<30k~2zdnB-ayo~`F|&g#h|xvF-!7I5t+d018pGuCm6%h@> zWAvpd0amLzc=siiA+^j#9T5eY&Xp?YTZnb%FJ}n7u5YK66=A3WRxRfIIRkatTEz*UH^CIG{>nmAIGmq}aA;Ww)%*>d`3f*n$Qf@kY zB-IAb#dYm@aZI$<`IYtbQWDJfyPxFVGA3r%J-{s6t4cQ@dn^DHO>c=kpL&6Q7dLs(*}>x}cT+0NzhZVqY58`U%zYou1hoN9LI~RJA>ob)^BQO4Vf_ zI$GWataOSTtDUnwSz+<6$Tb{s!HeUHXD zI^zd7l4Y7|6!)8J=1>(PFl*I)wcGTqHzxiy<~zS{R&aW5oNk&(l}7DvFv)E}f~nEJ z=NIV4FIdo z2bR{{v&JnpQ}cXw&b5A0T@E?q{n?w7f6|YXTH`hdVV4Qon~P{X>(}(YD+L_2)bX9~ zmRRM8ydpV{=UL)~MUj#Dym%-70FlbE3n$4|xT`PNtBdSbQ_jv=>PK@qDG88P_79-i86D6OAii-Bk)9dAuU zTx_Ge!q%fWnj2Jw8XBo;Nje*QYH=fxvyNZi;YN!EF{$h4T1*=fQVAzpT8IzCL0VLi zuLDb;9cxLGHNCGy1bY6@+gXzhhTCd=^@baN3(xSZFe44P?=NbN)%sFs^u;i9L)lC+ z)sj*YPfPgG--#SBa?;OGp>HiJ4Mf_D8+wd%w!Z2YiZTzn|V|eO=N7g98X%z zGG0PERp(Ji%%0eByMme*8l}}$EbxQ<%k9@Lx6-%|$&m&^OPM5X3YI0o- zWXGb|>S}AThJ7ab-ldSKih7JTej)~%&|Er^@X3)TE%Z>L$ZlN!{aXR@yW+Qwn=P z=HD$VM3CI$o;yO9bMCApg^>wq8fq&Do=aYsRolHrQOIuuAnIIIi&nRlK|v8wLa1d< zr)$XxBgWv~vlx90!W_Vamft>;N%-yTrW%d{(U9nENGS_%uZ^iQQiejzBnu>+w&*E? zek116q}-`jJ{6&y1lmraSNkYdkf*sW$JmtVufCJD7nEbjlg0EMI~1^mD_vR@J=1QhNaXz`vv%8Uv5~|_2O;VMa(vBL`=Y+ni#9LiV}PB7#E;cudP&n3!@Y^5 zK5kw`)c*hx1xOtMzmLAN2;baH+(mqy<${~})IvrriVS1U5=hs|r3s{CqD<9Tgn_wM z*R2!XM1IagMLv zQ5EPDpvR_At1I&ORK3$*Y{CseGBs>m+V&Qu?6U(kRi1;CVc6V5%PAwOZ9;c+CswRf zx{uh$Vp;*oE|h_wzQ1mitfyp`LHg;YJ;bHu@$rybjM;Tb=u!Zo$6DEUCx0beWBFH7 zFuqNYO4sS32x#>L5=YxjM^Y&jPm-H&Bz9g#Tk7={G0XKJ8xKDUW-w7gQnF3wAbB~b z*#rrS_=cl#{B2EDL|W`EJ=w?92C2(%lA=xel4!mt3$^1TaN$~y#ixL+gZCJMmb4S` z5#?Lx4Ae+I?|o?*gKwBVCgaM~&J7BMu=ue~)q81Rvyx~I&EzLo%;FKf+>U; zbvv$-P)|*KDjq#XV{QWR*B#(Ol08okZE8rBH)W2(8XA6E^aG^^U562;$(I4xKotyw z_RSX#Qa@Pt(#HLvql$4SYpB#6O)(S-%En>zwy;KsADXo|WQw~Fu;^uykz#L3&WsMm z{D`}mK)Gz~Ut9ZYb<@;dds*lt$1#2)w#Y$}$u*~~<=BtixOWPpFURvG2|+O*UWF%6k}i+S?5U)?f_Ahl5gFn9X2Oq&BGd-3!qYS&?g})d?quyhyIc2V77BE!_5@7h z52NBh*Vrkj?5uS(^;iY`Y^JW2q?6&HG+%KMRmdCs_HX{7`|8sIKI0$6o0XJyuvSNj z)DOmoZV+1L$qamSvu7pGODQ9+?REB0@MJD&1)E~-~+X}799;cB( zWCX;75EIi)PlmOTsU%~uwrH+FE#=v^NFSvkCe^=e+n!OWdo=Z&Y$=ras^j+5lu{v? zA-a>3bq9R!L=Own_$B$Ds5RzQf8$Ri`D$sTh@^%}ZZa-hL3ejS;ZclODM<(|NkVSh zTLOA`Q2>q1rKoKhA2C`tlXZs;tiF|*7!X^`AYD#wSBhxA<`>QoC36(3rKZUC)}zqT z#1iyVB5g6oj?riA{1skKGqnuwFq7(xq7BFR6QM&z2>Mx59&PT%qm ze!t&aBPnETtE$&cEX*`h@ z(|X1lhL>^MGO^3iw+c^%&rwTBF!1pUM?&3D*x#rL02}G{-m#`lG)vmSx`^gnnNi`e zk*Rg$B>jT=(q4IZL(N4lq6aC)A(QLm)h%i2+bFpI0L*lwEn6A_?XDo%9OoLzKg#iT zQ0VK#EASO1$G6>@vwx>TD?5|qas-!T6f~}_Cq&haTFND4w`Cw9l9XLbUG3pSjcE~f zx7CR`ltOLkldoD7VUV_3GhRj4MT&_PB~nBc?VF%F;WodvqR=HLAcIUko~YbB4MRNw z$0M{h;%~Vcc+`B6G&u#tgqFS+JUr`nc>M{+azoa#li34n%06I{b-htcVljHy5+u|GkN z&IBk25VPlcCrkj9!q;W=-K7$v;d&>x(A;F1yG700cM_rCXliS*uLc)n$!k=Fe~CU6 zot=iGT-T^_Jd*2WwX~$V0Qkt#-pWeUnQ29`NGldYqu?tbE1)D=tL0V)sk|}>nx|dI)Lz+n5!o2TK6kSDP4zdmSXHq56_)EJ`ga+nvmbjm zR@C&+>S&HB^&s1iKsH6W4(OEcH*(~#*LY4nS@u#!Brq~)@18gfso2r5Qy95rLunxsNk=3yqn^t-ONEZ}$U zZH#e{Px^!#sH<`N>F*f*1-zteoTgqS%y*W;hviUGldqnJw=Ei@&*P&1hb(e4`rsOS$%RHIV|Wzonn{FKrFLdikUj{+&FMIPf=q~ov>ZlXjgBdHV`Bbm8v@JeN|)q)ER9- zyku|e=9X~6QE)j~L>RdDpCpTfwxjz6BX1{6dNzw1c&(X#l(x9`6OrK4A0(z(h}&xT zoPE%cFV{=orE)x$-52IPS=sR#SL@5pM?r9Y@8LMKl?cmq*Vd$>S`gq36#EM9LH5_2 z^4}5I*j+m_(s-|z(Odk_syd1X*R*~V%y`lG{xW_V7%EOGUilIfy}f-zk=O06A^!jl z-)rdFI=erWwW>=iB@6Wl^~cZq)5j$7%p4e;cLkGdqI5Q!21!M%P!@Fn`zz8u2mDd5 z##`=9`Ahr{I{ClyeY@3rb@WHgLga?lu(T-)2E-6}B_x4!<*%JDwG0x~ zmSvPko#Yt|V384s;vA#!Jng5UHN@-q3?PdUWIZDcQlW!YQVKE6&)J-_|6b^*j!ie^gHne2S zFBKs{MYdGnK)T2T>E>%u+lzP#xZ4uWIv^{6Ae8h_I@5`WK7tFi>u6oEEuPxnk%A{e zN0neR)I&$B3_Vz2ymhG$AxJh?fjufwlSY~|HP>8yMdj`y)&r%0@U1r6GAL?Du(w*? z0vI>8ho^;UkuXY4(Q{XFkH->xR z@~9cq*z7$`O9WZ4Est$T-&1hoW1##hQI-ufZkNzJFI)3jG~jRZgTWWAt4RA}#v~!_ z3EIEiqr&Bo7FN(fErHZiNDF3m8q}kcGAJ9qpmeNNqCQM^+h7}>qpcW->^aln(y-PA z3yA*!)3K~I;$wLK0MkEpVV)y*9AD^a^$GST7@?6=rRld%DiebX;}IpteZlc8i+Bpl zAZ{{8krBXCy~S%oNs@9Lm<^p&tNH3^jE9-3ASEgv+gnxr4SS>Jqb9`K!l&&bE^Kq@ zulHy~jXHY`VS=8zzk0N9#BiA>;LuxJ*3Ec}hZ;M6F13Vk5kgMUVR8ujst&|&1NP#V zI*S9;)P2-97&bmk5TqTwQ`Ys4!fKY#(Z&*kWhTJ**4>B`LM9tl9|g+2mAK6?Y63zc zQf?7z`P7K}Y+VeCfHd}s$s%!GLoGg%$wSEtT699i!Jx_m3Vj6<1@{hz!qo#k!*7^A z2i>JLfJ4?_S}m{q=yHxi)*Xj9p=nx4MZ9QJihTz4ZR?W5$1@Wy%W7%DiA?IT;Z(bj zb^icEspIR7vCpVoIP8-~$xp7|l65qS-h&VJGs%e(JpxecN^QZ^l-l2Y6073-8oOFc zx*Ig?C97)4O8)>#jWh3lC2zLV>?*jdl?5Q&_<_>0oAMso4a8)jTW)pnqkEt|v1BsZ zwKf*(rAk*kbRenfKSW6GSyl&4N0kfTka%myxs+q#tK!>n9)71nBa_ju<$jW@IMA@*!0Rmg~dzFneDOjVFu*3ZR6)#_i%$KiUO6LW8cV& z(sG|sms@L~b~A9*WH<_M`m9JFg+kqgki{BpzNj>u1X)Pm7GU4|iWWDdqqwu&a?UA9 z0^h_+V30t0BgG(}*;Y~7jJ-;0ILXFeP-5edcDm89_8lnh_=`#t&KeYD4IqM&P_T4R zHmJ_bqHvBLMPGGJ-_&&6exqAPpIHFqA;p`Ah)=eS7LXai%eO#Fq}<(iw}q`l2IC-n zjF+Z`AqcrYsDXNziz6~-Tq!90#`iwTWQ!xtf<3pQ!dMf!SsIp=$AkZqsku zNAxsh3vdIuyV9(ac^3(7V&lTGFrMNYFJT}B-|VI#Jb@5WNWRf!cvOY5@@2PNLB0OV z(xW{`?8-u{hjj8aqZ0}kzM?m`{7YNn9W|*ah4%r^Mq4BWB#&*YXdL3k!uPB+K1jdm z-B31+rH#k(`>SRS69C*F4-;5U5owOo7$V+cw8IZt(*FQuVJOQ1IPzr2KML+VjRn$z z*K0wx@(k?;TqLL;5!SWf(A;+y(A7+1M|=Q;yRxi1A(6F$VI$~P5wW1LjzEo9Vauo$yf^eC~>WfEu0IgTMr9IY0YH>{wNeyL3X?85AC)q`em^(4(4G*~;M5Sc}b?~D_bQ6S{WA?I6Z1|ks zEubc%^XNMZe<;=5gk^%2ApO-J6bs!d#-W>q5o=Z9QW!(BTS8kx%2J=xPacExa>K>obNuFU zAbca-l!N;#J+w*KZoHg@rt4X-@X%Dfv}@c2Ny#K66u3$E(bYs{Jb~QGJm{T9;h!o z3A%~$)Yklw1yq=c#kdj)Nf-81x)GEb8-Ffef=M3g%$o6HsKh!*_zjP;qvnbGG9P~> zbdj|kDTku`T#o%WmT*rPDER>BxMwJh3G8S7g zB((dgvUR^&m$i})$%JEK**5|}l$jRi_ z$f#(t|8V243Yf<+Nyrw*55>_sj(PQkacct8%Ym@X7krgUV=c5%T%mZp# z%cJOO+F#IWwxBg&h~xv|Td!rBRVCPV!WmjZfGJSW3ZJ>;G;HkWn7^hsg!Iz2OmZyV z7@~PBz~0wlAweflQ@2mES{<5Emtuc?ID{hdEPJX-WH6=y9}1JGA7uu5^FzA)oIlKo zqzslJ#Dnt*P_RD{0D)B-8n!<}qSD)^9Z3ksVKT@jGl^C0b#59eswZ{zzNokRZCm>^ z0h@(pD%9I%%C@He01Z7>%aEn|Y8{B&mfT~g-A+?sT{^r$i)lq$nSawH>Vwj(u!@G> zOa|tYbvl*#8`RAUdJmEdt)!nZtp(v4#z!HB>k4Gl?G^#<;XT>$GzdvOha+A2gOGY=qVmZ+cd$tb z`G?i8_@dVu-jcezQs{I#+ZxZh>KI=pz+;k@F*u_ooTQD0+QWV^F z(%B$*k~*4JzlzftW%U@@`Hk*4k3bZDT;!xmW^7c+62+ZxlW=^+&sv?FDvM4%FlM*N zoZ{mzQZ)1|r>x?kx#~tb+!Js-Z*MBHjg=_%?p8}$_bkV-vRYc0DSZL`rs-MLL&K@4 zCX?tARmv-<)yI^Al8=C=@{lY=s=}z0*Iy+13dxk*>p47n?jTq!Bn5r8rehYEgSj*S zmebE!0kTv2O{&7H3jL4FM_h>|E-K)qAm}Ymd$9HHRNY+sx*8?7P^75!PNTrqoS?y} zSrS0Rl{{?YWoiReYjS8RxUR#Hn{TM(2L1(>i30bypbZ)=TdtDYQi0Ic7&KE!(`tvd zq{WSq4P!*O0*{Y{D&##%46+p4U^)OO{atmWo69Ys7l2H#r(9BsuYQBT0ZrFt5WA*Q z>@h|yZxt<0gd*1jn-lJ=cl`z4u>LcU@q3EniDl%Jq^RhTe!5cg^O)6ej5?A0zb2BX ztH#1<5aXzw?jbf>fzXTfwLPiFB|kRu#CZ;0UM>;nnFJsca&PUcvzzfo#@08fCImvH zV4BB|*-}i?LN%fWXoing7<#b7)rJ_b@~kr_fC957@+6=^K2?T|YYaVDVd}#VRv3D) z!_|f!tT5KL4Vk@W33(Z<5jFtZbxeK$$6yi-m__^=YGp^dldoqQV!0h z{kb0bmI{FNQ}>FTW{rDt9kk+ZHmDufzO_Fvk%PhINc4`T$tKsOay5Zv`b7s#ZPvGu zPy>rv6hc*{M0Xtwrb~%V+uz96c{5lu^DbW3wP%wv#TNen1!Xg$Jr!{iDNkIq;pzzR zy;g48;`%c0jMj@?R!OEOihsD%z*W~lpruzJYSS_xFO451iH+m9+LY1gp_3|}3rkP)Eh95w1X~gK8C=U}%N1z#1 zhHdE+uSFlzMOKiMGH6nveL8s5qhe$oazcm-1Q0$G)}=1QJ@&HL5#@1i;{<$Adl9c0xCON>d4`b@%)Ub zk$Z7$#t$}88WfWgD6Wk;U^iP$x6s!E$#K?I*U4a;?^cY!H1AgS=P=ABzrWVJ8y?|k+|2dXJ>%B zIEgwX$5?eI^&!B2Z4dV^!HzyQzeymAI2j<7wl^StWOXC%t-eYK$Z=0S0&T=?q5Niz z&s`~5?=s500oxHV5*23lJUmjGDfC=PPgIlAs@}ts>{`Z!0pr`_pW+m`>_=L~21%p?pDP;rN?b_*-s0k{ zD7wVNHlthaS9yq-wIx9+N<2lswMKOL0#YfcZEPjg71wKNN%ZJ4RwES3e}g@_3Ghh;Mw+XB-&9r4LtZbV0MGe2 zUlL=X;;$-tw>QNQsN7P5%ojy;TuN{b5KKYHF(VGA-=N{+n%uR=;nya=LXUg{bfIT=FM6`2?IguYyl z55l*TUf7b`T1m2NGDcAwdJFedvPTTZpy%$^{EQV-ItzR?9Gbw2j*;~njZ%bQV zvj*}wVdt1t(ycsG=UPaFQo$ia#@|Rw$O7FgHMMC5O=BXegu~WTLvNnC8o;h8ihPF4 z6sGp;_SO;h#Zwh1w%@w2mx>&vw5P)Lgz#@zY}aCZs|{e>B_QO(!%;y%{=6I(~f*MxE?j8vKBeWQ4iRS-7TdqSdo+a(aLP7S_eZanK#p{0Ip5WbWg2##61a~2KU zJBO7u;EiNMZNKUM0m#&FAM*$r#@t5dh2O6X&x;tR+4uKITCVRJ)_@=A1}WbwG~ zkQ#0I3mrj7Nc?J9Csiu}CV0GN30%mHvNR~Vwdv$2Gy3G5bk;|WV2prN(P<4NSOANw z@1aWWqfyoT1l;1R>*_0d&yJoZrncG!^uf+?%zJlleb;?Poqf7defDeZ5rc$7Xetc? zl=UaVw(mFWgJj1_ZUFTBh^_g4Ab!{u<}x#yt>-rd(gcsI)Nfy$N zPG4X$aYJQIXabnAlE_HVfC<;zTa9l$L3_VM3w1BNNd-$+g~d)t8jEahVaPw3yIa{= zlWwDAVw_prwO_)+{#A&Nw#VNu0unCa+_d-#pPZWsm?SWJCR{sMYj1_>YP>K`OdO)> zwIrMQQBvq)iKnEq;X{(il7XWNEi;*g>N_(AdO zK$YDGs@3XAV&KaDB)SxY^*8?jvZ}1PvfE#e!6Su=abFWG??}@3s-2y`7IYeV3PgpR zH1s3XICZ>_-w50-=cQ8H$s}DQ)wbb2=dP#I7(C~a?AfT4==NkIIOXxcZ{{XxymhMMp zp6ey{w;QFg>*CoVbGCUH2t(7@Te$a#`Fw+RQ%VXtY7H$Pl&8}TGRi*?Q0ttb@_Sgz zi){>L*$+FYVQK*TwLNH0QnO{0NAUn7ExU<8955{oMz7S*sV1l z%Iaz5E0<%4yNk9x*HM_p)!yDX5R*2|8XR!4dvCCAu| z{{SYO)zk)qs)7FMm)=0nw6tB6&S61KElia7-rClr>yjrzwgc%IT%m8$TgKwnP}YN8 zFn!R+-r4nHG4u1|ZcMoC&1BeJK!t?+#Sv>Jx&@<qLzx1G-W}1i2+g zLZuRVRG*3?RdZyg60DJN1(sCejkbW2LHnw|Zpg(KoP9Hs>Wgm~j>^bF6E6gI5%DaL zp1#v+cUAj>2XvmpSw6wW$gYnt0-l( zikCSPjo{ob^EKG-nv@NHu@cRA53@PMHRe-)g?^gsq4!gooZS{IrdDr! zb7F~MphFnx4;3Mn6bLpXi&N`vyk)YvXkDBnAodn=6=K07!C(>%ZoS%{si($U^as0- zl!0>ElWwBl-$zzgF3b${Y>%MX$g&&#Bm+>CWAy{!CFnbZyo^Uu^9mxwbx2U_(1l!5 zchwwq9M+wRT?ORh=SfnX4UnZ=oua_ssIaBsbXP*k-5X@`kR-*P!q7Df8id&S(Oa#H zLW`io@nz;AI@8SWO54>%DI_0wu@zT!HAudq>)`T{kF9-?gsVJMk8TN znTalcrU#Vd7BK<~3z=l`=~F3Xm)i;(i;bm9Ab4EW z+_<<%q^q2qdNe*0p3E6}2W2$Nh#;6r8!j#SQjy_GPL)?d`fqmi9?N_G0FkY@wEWAC zT|EKYprtBboU*b@+bhXU?oq$#H>>KVa~m~+$}MkgG^AHJiC*7rPaveSd1xsCT>`p$ zYDtiB=r>vLsl^ZyBgUo;n5;1MVTY>>Jy>Agu))BrFw~?guZ3ZTzMr}awHdq+bs(&1tvh6>0JG+4 zxLBAO)X|Br28EDy@TKpw#GNq66wiHH71$n>ufE1rNKM5%qKkyz+e3CpG-9wd-c9~8 zKH3kHC4y;L5lJx98??=mj56<$S~H|Q@_ z=;}{}WUMr*6=ECnQtH_t`e`aq@vyC_vwkRJwP~psUAkkoVP=*S`-_8b3WTGQLfuFN8j+#!U*=7^TUype4E)2aI+t5;prqXjNg#qfqJ=tR zC5O~KH_8tvlC{fn&oT0|XD(zl4~4f*;t!Z7QC0r{wbvJvSx1$&Yx6vbnQ3_hrpZbB z=+dn&#GI6n$#Mb}VXeGsP+bJyMq~M^k}Q%__L1zU`$Uzd>}1|-q@~iEljB>JF@4b} zZCJfR&2mhXb?y^Zl=~J%sXKJ)&tc`rT%Sh65V>Ya^^_OaAtPPHlcfe))#D|l9JS5i z<&q>x%H)zhS*KR75pp!Di~YDiLdKcfZ)}S(iRG>vHWZbEsy7{JZ*qaBskYy6)OGV% zJ5aBfWDc6EZ=a3nD}L3o(unRasCAsjD2|)DR^eFjNhi<7llE$TnA)>tbzsTaY?Okn z&6OUiK}iID6wNhP5gm)Ikso!ugrw-=hwip&+MA&glFDq81v!DqD4oeAKWV2c-hhPo z$@+s{MOOl^g!+j4*l7<+SkH~D{ z2HUCB0_Hhk)Q^YpsUjH*H0aQT>F=#HGhLx5*;nDLID^wmcpy=t?9=bLJcxDZ*`kZnaG%anw~ES$7x6@u(5o`Q#{ksQqDJ zjcRmk+2w|XxA?ka4=A#)$w!Imx~9@jr(2VybXwhxM(TQz{{Trlf`!GrDW0N=9;T}? z-B1p-U;y}l*X^mn;M0&7Xq;leJPZ60*Wo<)6zm^pCNZG9+!r5p{{9F1&E&2IwIi^-^uaq(%Urm!fTLYSp?~RzBQqkbXXrY`rEKdtURkkf~?Fc(3^_G z2Pj%kLv3^wvq>XxNKMt(#-bdu!6$8ix|;LbYEX?w7ln$ zrwE~&n70ylHtzfMsjE}&0qe`U7&y#^O^R+jDoX&T2_!T1TnAD|vbG6Hiu}R-+QL~K zw!PjK0<_R*%Oms_*Kh{4SuW$KMaGtuoysL9m_Y0iRCPIFzV@kPSK2l(mXJ!2bn&do zwFD*Tdp{Fd_YBWz3WgF$u-DyFb_a|wc}RogMZDLE4wI9YASk7> zPk=osh)^cv*vn+~jm06m_}+@5kS```ASi+ANecFBQuf!>8i=Tg9RvjuwRwu$ktY%J zOvf6fg(!8Y$lF0i8b#6*8szvJQ2phCY7MZHG3JF?mUfZTTT<>=WTPoAh%UpKMr@_H zQMXN-jY+gO8B8Y5Y3G~_mKM9|zQ*5uB&t~@8yjKPqfC&i{Mr~obsCG0zL#k(f#V12 zKSWp!A!?nSQ6l@cEQ9BzN=d%MH7KEIxo$rPa+r?(RX`G-U1W>L20n*Yc2USoO^J*l=XraD^uud-Ym}!}k$O z{{U#`pH9P+kAbcq<%##l^P@|%K-LS z8JBrPlE`CIbf-W8s|Ztqw%(wDh=dim-2VWE^*K-IZaV!9Q;m#8$_N1HpE{Sk%|eCf zJoy$-Hwz7;$W%SXH0ekj{FrSeNdZo{Ity4)V+Jd2hyx~kr>=w#NwfK_)YRi&)K%E9 z33L*mRdpzM^{phq#yMuYllz|WSn^8`Jx$WF(MZ3Mqr!PLDByI&*r91k;#~RoP&$P2 z2{Shwg^;+VwI2bkLUWNS)KEd0Fmx@jvOn(7eYqA3$LJVAkn(L3J<@IQ5kQk!8OvRT z0}|A1>*1n5DsR*9sLD?v9ABvEtQ@DvD4O5+iC7k)&to2gP}OZ@T^RDk!~K#@Vkd`FM0UfS#c>@UfWOX@RdMQv=5xTb<#nYWE&yTjMcoQr(Q zXC%tK-&{qSWH)csK`mAG`>(NQZB}`zOCvgu0lWo1iOpi}lVt@}Ft8-shaV1gKN4Al zv4#SUEdQ=HWB6(`=Xl~^B}9eiOKecL9dG{BBX4V{^~(y4#~*pGNvv>0oc~vCRaYRsDtu(*cBo^IQB0|vLlo*=3 z8m~rOZCRJ>iE5PZMT9OL5fbuZK!#RTF+vve~h5A zzX!pTlxW%A-6|urjHfNpE{QFb&UYkBl|h8kMsgU*8!ciuPK&U261Dmv%FENk_khdC zrJ@&vgKG{mYw-(4+Mb^?{KF6vlPOk{(bCv zyEn9uVF}SR(zjGcU$}Z;p=OFv$Vc56JaF(v3xbpQDZA!(`_@yP)8B$UA?RN-7#jZ} z_?`}5BD(4Y^7dZTQMD%jAp7yd%T&knz}o%$~X#)xxA_SzWR0OCBf3H&+RqTqecN*$l0nzPG=xrxLG*y?oNgEEX8 z66sG&uzBYSb*3}yJ?2gidw)woiH`ytI#`?Vr`gQ%gl?pnc(|n64?bZVcJ^LK3_ar{ z{M4|SUbta-^CQE3ZAaS`)Ja2QT8b<=Us!D*6yvJNNKmg2+H@q(XJwZ7hp-WA{nhR3 zibUg$;sD+I-lYkJg$)~(T(1s2SGD6CpWjU5@Pnt~P~jEB`w9GWk8cul5lQP>pBGf> zHC5>Usvdlh_WYfx;jA*9eW}Fhs5QFOQ0l2zX{cnR2&Cr$m*C7Hz$RM$%5*S(jo?

    -(nbeTcsMe%s_j?5YR3XO=c?~}Z+^}IKnDU1!R~(d518vq86Eytv>z)%D zKrF>InyeTlAKb+9;Sip9tldweK#KVCb7Qx%UC%*rf}s{O0O#gguGORUr*Rp*__paW zz-g;Ka^|VaqvcrN)lGf`=Ha=B0Om;QS>rD zeNNPSBe)*VsdX})kmBKfB5dT5xkzw#Q!uVeu@kKo-D49SX~&5l7|yKo2wcCZj^mKI zP2HLuk}U{;IK0WInEiOwyZpI&#Z%w+3dc};KZLmCcarr)daw@Rzt&pbLd+mK!PGJ1 z2C4)rUHp=Zn55-dPP?U)4K6=J8A5@HAYR?i|J|9uClsv9E_Z~}r|zcBzk`l;gfJ3Z zJZHDZdEQS(#~rr_x5n(8s^S_p zjWkvVN+FU8CQFq~ZMUq4+094&j|j`&WcGgu05Yvm$|^3cy_uGfAJvUGTJZuhwbgi} zB$Bb7sOLp}anUUoeWV##bi0g75U6#kz^+pnSR7TAnZKu`D3XOf)fW!M8q1Q52A3c~ zn1X^CixlWo98DWt{GB-WbW9YfKE1}5UzvlUCoE`=ieEtJf0zd9*Ta>G+P~s|JtDVH z!cmxN)J4~?D$PWZSI*kfE*KaZab9wf1|4^gL?+CAY`ee^^*)wbyNkYdz4B6>X&AM6m3N9c~~<=bXKtL}~ahp;_n3NYT)^OGj{W3$c6& z5D1>{b9DE87p1nQn`SYR%3nBiA@Dj_*3&@pb6AajL|h%_w_ttFhv831i*fzf?OXBf&ocLa`Ph1ISDwOf|b$kvyf1Zh)U zVu4HO?`)SvSPN?d=+f7A^JJAxv+Fhv{)Pv6>S;IBX(>t+*k=Mz4YP1ah1Vq;7Hu3~ zyYKJTRZt$E< zqv*6U=T|}hnkmsr>cfDc0Q_$+Z)jyr&_mPQ(J|$QI(<#+kCvdZcfl+N5bR@~u zGKH@ns^kS&hxw94DPi=|;Q7v$I_jEBklvoR3UY_+X7Mh&4Vy?)dUO7XZ_lx|!|lq#T85s~igL z(}^;@(c5v|>mEvPMv)q=7Z&M_@kki#TQWuzgO%Mx?`tNy$ABd*T!(mrKm%i9o|TGW z?Os-uPZR_JshB$rTT^$br9dWtLUjRHc+m>{@spH!O7U#%vzvVBgf z*(qX}PQz4&YbB{5Tx8mVNg%=?X6j8py9eDF#r9dpN4dFROZ>&o|`uwDm4q-iC;m0?*}G^1qZx!>*{f?YkX$jwH$S(rNEi? z*JT{W;)A`>JSWZXDVBi^c;9WcpDd$yFSCIS!LE$`dQ7jEeIsdm^0)(Aj1zItZEJ3? zD_?D6=&7W>RqxfoGF|-a(V{LWvI?_}Mi;{&5|T z0Xod;76afT9JYd+d-9-D$Keo2d*NQ=-2#*PL~iA0sk;k_X>u{@FlWG6$5}YDpdjQ^0m^3)+s|}*t{&)VQ3VqDjTVY@9cgh z51n!FLN>^bI7+hr%zPoa`Hmd`ZQ}O2!Uj?N*t_*zT z((MUKEEA8N(rVDp#eqBGwcS`^G_@1wp5jC4amOKlY5lS7ma=ry2IIf;cJI-Cg2q6; zK{L&1?+>X-m<4Wl2l6C3*5JEx4R`H^sh=Zp0Djd_xlNid(8Q%iiZhScfjXId|D6DZ z#@8-ebF9DGk+Cl2KHeDXF&OCBx$5>wNW4|F^F>T-tTzF7t>m9cDT=A_EMW9Lc|}Eb zoaIgecMa&D%o*p@6dsHfs_lz@L*(=7P71-##>3TiMb(5r=n*9oNZ7GPkAdO-Y zHLLkK`<>LJK#G9rklPDM%U@@^nbJD88f|1p^w1*BC(S^J`*{(i^D%!A|5R`QqgP(! zZqaFf-rhTTH4l9KR~GU88=3tgIvVkHAmzIqUoWs0oDtFWt}1!aO9k>744@zR#ZjJI zP{i{2IQltJZ&cBDT#AMMmY-Lsr*z2(wS2g1Wma!GY%Z^9F{PHA=2Sm+uCs>78#xjD ziC32UEIE!u7m3%C;yrxMA_{*JLr!|{AA*Ww*EF{4IE#63qv_$LfuhApz{qaP2-o0M zd~5KBwnYF{t~3H$Kxt~ul>i3M&BqTWaJ#3kS;SC8uWai4??8p=p26#I(5Zu4mmU|K z1m6-yic;%Uw(Tqx54}UTgp4-e3Mr^#PkG_tEr^|R^!Wj*FegT~xtKEx(G}Bf*dxlR zZZ8;EXp4Raq$C$FCv4>tWq-z?(HlLZ-OnN~V;p#~pL2?SstS!wg(vX#v^;`PuywxC z1HcjUVeqytr{3-RrN6t}i?i+)KQ|4lBqjdJ_Gz(hd}zwdZnJ{-R@~Lv)9dRkZls-~ zpZt|bl3)ji^YvE$U1qtbD*vR4nLMG>#Od>ts79)q&f*ss9i_X+?o8@?0hP%xUbwix8Vjh4PftwEIL$K5IUSR3 zXeschkf?HZcoCqKP#(Xb`48c{F()aZmMlR(c-&(RvX;LfcA)ow1@w^vpH3vU(v2hu z?GKYjQ)vR}A@n})RBhjRjgvmxDVKm>?9~lFs5_`K*f%%506h4M!k6?g0H7)rb$hKW z2E9Q(4&DS+CmUjpVnDS2dAOm1OWZ9Tm>`PQcIjbH|fa<2e z5o)RoiRdmION4G&QsVZ(o8W>Mk|kQ+y?UsxlLG0Y55}lLY;OfFneIxe|3MM?SSVdm zjzgN<1naX#$(wdPaa11Irwjo!x|}Hj`0t-yVAzUrm1cseGPR$kjVsw!))%{sPv3FU z1!aFl!1{^?ta)D&wsLon{SN^NsI+qBJ4*DLEkH$m{z8D5GMdUTC%ju@-~k?Ss-fXafuy;ypS z>isJ9c62jJo|WNk;o)x=u;XP$?dtM)k}>)w*>PgPS#2)Cx%ws64VD+4dx{DO3kojr zpk8?!=v58s)IS!B82hY}498yy*{uO5bjf@=*Rj9J7E=vdKi_B4zipfh|FyJ!pp#!W z=lshy_53rX+oQ@Iu{GXhM}cmQBzS+JcakevJIM9SVC5geFx@ra*gpO8JL|L{Je5I+d*wd7yImVV!FL1QyPnqj``-GdyVF>0N!fI~ zq_BIj2P7_r;guSYYtOj{kQXu79?F;0#@BYe!H`s^Tb9a)Fi;cx7TqXssJ2_3Yp=MN zq*_Xr`;{~EEs{JL%$4>O(V?&I=e59sJV{A6G=h03zmsI)BJup z%MdSUv8sUr8P51*r+Ch=0&i2?)5xZ>6p-UNn6LlxSc@acEr5c{XqfymaCM^fK53%c~_Z)O$0pT zXhQsamb>kTiXc7W5dN1+c$4u=!X;*Ty)ndv%}EY^uho4^vX*?pMk5RpegdT=54)NR zDM7`9<$3uw&77%83chLpvBL)8S{O^3y5F*oY2*DHoXrRVl`6j^&$n zCXHQlk;E7z<&H9M;caggtTk8VdQ?!%#-Ax0>707{igCefD6tOkZ8j@JmB2J3`iZ;3 z2u16QV_};3yE(=O?8k?ix%#J0XOC4K$lu~{MEic6Oo}!!MBm{}5Q(Imea(oN}XYPJkpc9Q~x|ST3LtN6RF8X}Sq&!ra zY8zpgBdHEJ$k;lDGX3#6)4X0(mKnO=<`Fhe>q@a;PAO2jM0>ZT*hAa$T#$0aruc52 z=A`0jCkrn}DR0$>XQ9PU&#wg#U&?a zVNl^%Nk?6r|MHZ*{g5!^fcQCC_2)9Pv($x%FsNbIef5~9ed;UqdAd3cmU2X}5eR8! zl~@b4wAq{DD*$+Yx+a~WF{TU}k3K;0Xs7fP5eSgMicFiyOmh0z{UJ!lBWrk5O;&lXT@n4|1!bA$^PfhzM!JHzYLDxTv@ z`1edP^w92P<{ZSSZK2Be-;|f;`qVxOCKXNVGwXY8Lf)+whTZ|TRPEx4lKYKS>1Ve;DQ|No@He?cAZ zW=EI(wN2r6_Rw&;hsx75%y;kuWz4FM`9T_!7iOzZtdvN%Qe5`E>#8ZC7}{#iEk(Esb^RX6 z;;TD4rrpg*f9*fe`v>yMSpdcpaU&xJrV`O@6_nWA>oEBVSvZLQn-CRiNMF@$uN3GTE zPwmi6ayqKwbrEZ$d?a7HDsVATcZsbT1Bvt0)JUCrn#k5Vj|_rFxh$Njxk$ zE(V72-rxm{w@vKwAY!E*N5J=7^sk|LUAs5B1J5N98@uxdL_(yB&iLqPiZQ}>Fu8!? zU$H%+pMAtn1WDHE1t->3Pc_o)|J$}gcX+L`iXC*nc}g`(S91G2_)EjUH4Rsa?@ zf%8BrTj2BRChm*4L6>`VQUuewv_3L>VF|LH;WkpO^OIWf*O-x(kW*kPhf}ITUDb^b z-`<>4Nw@2Bmt7z;5>Z0hxHJfYbs(pET9Qo~ej48yS!U&O?(hck=(ZAVUEM8SX{wOV z-T+5N%bukciS7u~MvHl?+n=B#T8&oMLhbuD)=ZDx7`O;d#;7SfvOpR?eFgT}Hdi4{ z)rz16VT0{uDnbFnTvS?%VK!eooxh9jsy9_;qDLNsxZC^v7#iQQr|E6Tu-P#EY84DM zDPbt3e!4RXUv+(kTk?%Sn{F!ttM>~|9eZnS;Z>=x1LLuV!0;jt-?$)4_D{dUJ(xC~ z={`)XgVtODAc;pO;n=zgC&Qt8Pc31*2%2vQTEdk1mcr`LIlH>W&MNkcBCRV_GK?3yOEeJ0wsZMS8BN}Sb#s^|ja^Hw=!VZ{G5IaEn9Y)G4NLO42_a%d z25I`-Rv5kj_fptZRjsqsk)A_nl!UXy^)i!jMnRp~M>SwRkQcvP?VH$c1 zFNe6xFK=e@9Vwnv&IuT-?^4^FQ&3BJn@SuWXJ!2C@KP$-Pq~|Mk)dhE-vHXWixaKM z927G6aOzD;NMwcf+ zuZ)f)d}6dh8DbL000KiEZmS+t<bhKMr7M+2p#VD@yB`1#$`AIJOiR)A4DW_T0)MHx zkN37be*j16@w+l0dsDEu21p27B^WpeIhK{F>7iQa#AfP6?%;e@s{-S$I|^@vrYh%A zdJHBijW@CX;qX`!J;kHqr!`b-`K31|ZSCuS=EF~?W7_bY3~tP=!!Ej;Acog~f3r-O zmuR%xJSS`s$u#YNIbxBX7 z_^#EitBVy8{k4w-smY$c->ofH;f^?pbJfn{g}okat~5BvI5kq;$T)Ulr+3@p-w-_gn8E+pfzS6vPA}Fr1!NX2D|1I*eYK$e zlnO;~uy-yP;vtOw&xX)|cqD?l=IURL-QvtW^@=x>zhs7|_a#Q=Fs@2Y_gZSNs~)rt z-;=$L)|T4|JFl$1ZN!@fi~g)rcZAZM9FPdyuFLi&$h!6m-C+;jPKcY4#VWkc_Eh7n z0lu3#7wpr8lZylOMJq@xnC=`A>9%}_i9f-2`zH1?*JdI=IUgDS*T&g7z^uYyxE^`H zZXb&tMG2f)PVupK_^7&qQ|}wow$=~IjH&A?B1VDd2r4ogz($jCd3E-QXmHt6yHSGl z1`bHMwR$v3e24xD+Yh3^B8JxXm%m$Kt`jNm#mr`A+|P5XLCzRssa%)tQ#%Uxi!}lX z;h4Z5QC{}!uiPPj?Z{K8RVc*eU6r1hucY$V?bI$jNdML|3d_TS}m9pCXpDBdR9H?)39q~ZhaGXKyE2P!7k8Qgluu*`K%(_q|;U*h2nOe z)(U@c^5HMUYfPEx)W=*^KZi-q6qKo^IBbcX^id*g$5zRwg^g_uv*8nc&^5%Wg<=a- zrD`ZWqk-i-B{;ZGD#vW)655F%SQrAa*vTMCUmKfQGkUGgJ7U3r-dn}8zJCboQh75{ z+O;%v2|i+Z!f6Lk>n=&(U2Hj^Z}D%Uyj|3&+xJ~s5JyM;R;eSkoDKfnD=y>f+EW2b zEZ~_-0NOxn3Vp+NXW+(~3b(~>PD&;11XUS~mx+_I-@G=jn56h?e?u0HRP`-i8SmIQ z-`p}An}@>v@T=F1HV#73G+48`;a zJ;!~)m0W6o7_2cEd5SSmtFO3{=2W!=qLnN9B@Ic+<_FkmGIpju93Ar*=q~1L>#FDd zI(pF1jKTG7NV}t;Lp*5#K4hSyE>fVjgt4Z-H?3He7}U|`Kva-<_4h1%8RJ<77S<`fd3(|U>#I*E{&bnFu>U&k09Axgq;vXGn_50<{SO%ASkzw>!xMy z+D8`-cuxR-yBVl)4p*!r^B;oh4d!da_wyX}pmNW|iy%TTkdApW@8t~!VF8=O!m0?? zF^PZaZO7=lj(-S*i`lOYZ1Q4CJUp?iBS7h)w|pf955$8j3YO5F{E>{O`8l;$hu?{& z^m2dlmA+(8FS>g7#IRypid_a7YwydCX zuPvoAR+(?*?iWPRLK)8f#zo5vJ)9KPkEjYLs$tm|!%v*y9__&WPL(7x7|tAGA{z!2 z?)uQ2%*Ry_feX*eblEu4&w{Kf&QW%ApGN9p^TJ~(i&8^Yeuj12>RkNtLM}A6NLhER)pB(aXh5m0(zK|cch_ce-vw`=lUm zn0UJpaHs2;GAO0D;pR4O%02eDO_DenzquK|Et3Y&xKG?k11|8ceSMvF|E)a9lrL4_ zAI=gv$qyQpiVQ? zQZ|=XFTnpg+ULD&OmlZj2|0Aw(-XXr8v2Y<4aBN&x|*$Cm=Urj?-NQu9@n|2@2lNPEl+@Q z(Ecas{iB`|C_@QAq9_R75Ufk%{$Kt2Mw|zcEGS-0Zzu;0dfa-*+`J20& zdTPN&q#rlpNpGYn6lkO=n@u!C4jO9|fzp;5P6eQaWiW zSK*lAL8+e+b&a6mIKRW2x4qYzQb9h@>GF~lo0c%lRqe>8q-3nuvLd4enTt_Kv*`jv zluQdtPq)7jTP`>3YVo2Abdw>Z^;`7stK(s{JIQet@ulvj?XwV+^Ey(DPn#V{3<2+{*kKDN>Rg>P++`CB^%Dr3iRT<@Qte#0p-MkPQ zmCP+TI|%{B}Hc~7+JIslqk z)m&smuuXmI<@!~Qv4bLD^B&~@;IVKZ@08CN$Irb!g?njzn$e@Z70f!#6Z+#$G76E5 z(()rf2F$R&&q3>X_$PLGEOqM40$*=L#el$o;SVLJoZaVQb#!W{R-dGtG)`&sfxG5daH5R7;$p$8|4_O`TR)k5acO&eR59wQnO zH6)`Vltz;?sA2UlXYUIs-S~SaZZKvrJOiP@7Gr1@xi|O=5triEM3%}&$yK*qTJqsj zGd%BNr53_sg$>H8b<{MD)?jPvH57f;DQ1+Q)TA)iYhfzKFGkJLSv#HI_e0I7}7o3c`2yQJRsx)+}=F%g62AD5Uu5X3iFn^090$U9Yo#Q8-% zWAJk)>r&%lNV;n;3$;2@1aQEoN^cwX8#IT*>fH6aeAO@F2W&}X*&j+!m`f5%Gdv61 z)&#$;WGetEaCnvODO(Fvw7%tm_$7-(9ij$^YodiUwERA+#o7$~B+K?U@lai9{nSO~ zyT{khvV>LgIB#B;9~fCyH8(Y+C*OfcsTr#jd}Vu&$5ygQe4oEvBxw}PUP}=^72rgq z&RsY_;fnF-%XGU!YTkd`4RabjFO;0ci@xJ3PpC3>D!!d(vIQ)rp@;Hyc>RTZoTc8P z3yD)}myBtZko<0ntarY%Pe{3+ zM)XV&K6#gr+yfMDO`b9%_9#9~DVPoxAXC_j-Vo@1FD&9_!d4Oo zjAMXO^u>86$Pjg5ls9jZopwd&Y|(pE3w?~3=UchMpnqKAN>FHC^NsI_2#pxFhs(;Y zru-a3(VJK)z6ifK#Y;+qd@^;lfEA(9m)w<4WHynK@kpmraR!(Oq?!`odbBHnQM*9e z*B74O>Q*;rW#!j`P?8(6Jjax?4Ot#j15b@dY}`cgDT@hvrv@)3CB)ECjoVfLcf7~Z zTWczfZw@@k!jvVc>IAe zr74-1Y3*XQY+?+`_HsMVI}1}h2Yjfn8G|7dzo5;BP#X%WPkTcjT%ztInWq9L=vAJ+jZA{5%?plwjl+;oOouUuNXz`VFN3 zX9Xl<-C^DIF)npvkh(KMBWm=oHPK&)&`Q4QRSxKsQiWky7I@XxUKCkhzpg zig40Wh^*B@h$^Y1GXWCZ1%CG9Mm60x zvv5lx#Wr@`an~d$rh^rjzq|zNA3}hg{V^4p>3?`BC?IQZc-OCjc{Oq5VucJe4rVZNy5ynq`vysf6I00%u_yYpLA%K8fFR#nLSk z(8#4vUO!TAli2uu&dKPXkF_Z@QU^El1{*E@Xdn%*6a$qBtqavZs{z`D$NBjA6b`Ta z+JRG@?WoSZYl4}td7o3-ov|~VWv+0`bb9hB;dlnlL^thDy>?4`rpNwA!}faxHI)R_ zkc}g+QQEXWYZ#TZ6pg&l28J$#A&8d8o96StG#-Xnw>Pf2 z+v#jP%LCUnLbNA!kk+b4|Z0ib8-cgKPgao?g8y9SR z%TcfyPAA-j^J6rFQS|I594#RkvLb58xv`c`g5{t1!hTM?CZ%-i4V>p(jug*|OX}~t z)aZb6Bu+|y+B}8@_z|6E@2%&(Ii`YwA?V8#5?`=-F{0{G)}s>cdye@iYi-ZjFmUJnFrJb1&s~g2O&%(nOV+YT(J=q7&4FAl%YHbHhN`_qx!?@l&f|FscZi(xF||g*d}%}Q3igHSyHy^I)W3ckyg$A5)hWMT_e)( z-kwBeO17G!&Jnmf68@xSIt&++(*8p@xK#6}dnB`T)1HExV-z5U==epvw{swW`PUM| zkDxBt#u0s@xp_Sz2 zWVO)f3&z_4zuk%tOu$Rs&X(?WX8@Ad>|zvV_aGYHL#Bi7P-w3ngF$+MI%;T_{0MnKwSL(OMK% zd(}ueX5)z1r*A!xn=ke#k4o*a1RC_e#rWcertC{}F!t0XX zuC+OrQ`jsr1j+uY zO{w{lsQYyhl95!XGv!MI>gJDD3GTl5U6aaf&FVA?5`_v9^JBGir)gm;qNpp$`$q|Y z`JV}!sHO@7P}o_vdE$VzvB#tM>Pa&2V0o7~!v!{?^Fq!2&a`Ijyl^SnLmlSd+LebX z6>K7*Rn)&IkITCCcEsP+>~FsFuzWvyKtmchVbzI$t_`m>KkYv=9KXXUk*{CK3~yj^ zxC-aUUT5ijtT53~r!v1@xKSzPwTS5Yg@{Iv7!<(N-cD-1Kg!#7Vl4LYfw8gEbKtZuAUy2CH&5+{cVGdHjMlLF z{i>}Yiu~@-HhK%WF2b^yRk&`3#~5etiJp!wM4Lw47pCZ{)S&GbD*pbbkEYgN0Yj>Z zEB)&~t3`_AmbnrmfdxF7YvN{)m!Ijiy|~G(5Y(Mp8lRWQP+drB`SwL=j;z7^3wI(a ziBB!2zX*dj|A4>i3f|M^7$^%e5!KSXr^|~wLEaRinR7Nj_@%!4_*eKKDkG`l5-@o~ zhF{&xaqubnVw80MYxoVe?qr;fH=>G_2w?^}M}j2jodV0ls($Ou7e14UQmP;KLL%6x z`c|u74%R~csDtpz0d z?)zs8<@i|BB^#7nvTQ&pTD228j5;qUMJqZv%5vQNA?eR4(vY=a;X6LdwCq?PO4*P< z5nV{n=wte`gldOSLb~ygQcoH5X?_1+&Vsl7kCJIrD~@oMh2B>UHqY|$Y)Q>x>;C~T z0%xekLwSFFLFjA@!NqUM1(^fGB5p^%5l$k zIgft`xIW$nTED1Mn0&rU+g9%$#;QTUuj;r z54*dE8mgn`6{sVS-Z$sFzV9q0xj)PB44v3r^eI;RkOSP#Kt#2VMd;dKU((scIBdt{ zD`rzH7VcrpJtFV7Bu>QlpUT=wV#GA6Z+!+6Q5$Ws|1x-+8CJU!o|Zpfe=ZI+cw-eo zZu&7t40j4vE#e?d6%LzQmO3arp3H887p0rec_xiWrg6zShQjPg8RR!49`K|(Gr)9d zzMBqAY$gp?TeU&_r^VH!rX4ArE1=;8PWu$7duKl1{R>;JQPiNRbznsDtchv!NA&fv z8C(xn2nw@~v0AnJY7qVUy7YlNvko-MB(ikMp-6`{1`G?Ny%Bq7>;vKU7B z+SAGu6f)P6H0X&xIEXZMk&onEQiw0DUaP?jhX**z8S}xPY+nF5|*tfS(6MQ+bZr~>AMrwzf4?~{pIrz>;xV+X0%Sbjp1;pk-YFQ* z)}L}OOfK)llA6V|Q>@HQz5bi?^u1Zr&s)e5A|MdoLVLni0=(nNl)@-4F#;3a zqHb}shAu3A=|dp@mlU9<;?bgJ_dkR`6?%bR>g(CsJvrE)IC`oBSwzN|xDIp-Ud0~= z8up@9u5c$Gdb^nJwKWH?$@ChG%Jf+HS@8w4GjH12lzr*ETTKMq_yw_U7anf- z&c9mX+A~G{F-WL)G^w=g+8ofTgaHNG1key;mJOqd!aT zpyqP{Qe&DrN5Kthr}VdVd}3{}m-xQw0VyeXHcXFNVwE$o_o}VyiXu@cEvaiPGBGEn zdI=ZBph6lH4wlFT2mG(iRa#tuOS0IdcmT>mGzw=^rOpOc;h`L7^8B%b@w1v{=8bV- z+>rFXFZi+GQZXmVu6Y@m0zK`Pt? z)aZ&|nT$SVan1X7-M2p6hDhdnw-PNGkVLCr^bdgq7;Q6&1WRu=Iz2g9DZHQ!{26B;Uz785~X;ABPcA3DekjG%$7APl;a#!puN4o#35k!w~q|2|U?4FwvfzvD#-D{!;46hoyS3FNbhyx-# zoU1C9RF}JSFAnn|pO!fFleqQNCEhlq{~_S@H>Uk+Pk91OqA4GOt1)BlF$&j@&ca`f z-7ELr0G4%s41+Ia^%>;#2nj<$O(}MPWb+wjjpm79%Sh8^;$~ZVdJjqu`Crc=s;<~_ zW3pHqvg{mi%_;*jzX+t`yGwyu_rS0y9$Z65(Qw|ne6oKCGZ0+~i!_sVW}IYE&z8|( zCcSOn{jKHNa!lH_*gycyLbk4gsHdqG>7b0)sB$@B{QeKwz()=rtQd4era%U&BXaI3Xgls1>iyoh6qsl560PV&Pu$lsCr^ZaolG7jBpB13 z8NzJ1@>kJ2vAJB+!jE(fUb7?lMF!KD^hJi6_+msNLVY4aX0x^agI#uN`^la z&l>E#_M~c!ni5-O12%88?DQ8$yeNQcsV?hdiIx=R+!m1Cmf+%?bdT<7E>ste>yrysc4iZHxV{=We8C=A!s8`RZ|1ggQe$u-ePwfZXEYF_BH;pl3-DjiThplsXW z2AF#o2Q5a}!R|dRKND9dJvN*E%GcbZQP7%w2k>U+u^{?)d5_l}cPSuy>rN4usLW+- zJu*5}Uzbq{cIUxX6=W&y2`Tw02}zWqpWV{1D9S^Ry=3mPM^S6jSrnCPAlEVO6(vh} zs@~7;si}1Y>FOxlN7ZnJC;{VYgV`e*Wsu7X&NPIfx7L*O8mm!Jb|A~#9sIUkL@2&@ zE5wt3#<%VwDv6sS46*P#C>0*3UX{NzZW`?O^5`P|v(7xB`anO~LY<#L%UH{3X-Wx? zjVnmeDp);fXh)KsZE*0o2EAEQ8}$aHBwcFr&``fT*zBcLI|AS4KB-$cn8Ab*tfw_L_6j|EIO@c0 z}(qB*$yB#9j}*cXb1xJ8O?yqm>tHRQ6<;6 z+r(U6w#lPbV<3&rNVSdj*6`U1(Df*Qo0Y7sL-MAU>yVEa7G^82EUmc84V?ytw55Qt zt+&^)FM!K1)4d_KT{iwI^*8rxOHIwP(z(Jh41ClrgvxnxoC4$nWc#Qp0lmeJqOIkf z3EK`b8@`EGQfo>tBR=}aU`y?CDQi#@J`wp*eV|^Li;C`7%}tHQBO||RRX};%hFKl%vrhUybcjTT4BP=<7-LVh0;0 zK;DVB)chu-x8n|sWpUO`WNxNsH?;=-%Y*&2Ww!<`K0d@K(=M&hhi)4`nMoHT?4WF3 z@z7$uT+0NiKNsWjR@-p%3tP^9hZ`-O!^7gPe(qNIJmBheI6YeyGt_$T13%Sjb8`Yk4?wA+4$O8 z7CZ<)-A`A)2iUCH;;e^Ofrc!r+tXIx0cv~hky-Y#8!ay*Qk(siFF44P*P0L#9ZAu@ zu->&~SD>UD$fwLnMUSQ|dJ|fVa2UYJJe3{5Cfz*hUMK>(W`CJTZAo>7tuBM2f`}IT z=oabMXLLDU{nfsVGRHZ~GD;qNxRcZ!O^3Fttl}WM7Ib`$)h?u-4=rM0Niw#qkMdVx zZ?=%Pjg(8xer6UjQx_qYbOo^I@Q*?)t9_M6I5p9iQL`)Zl86tKjQl-1pA;c*c_9shMGsky`Rlba3{{TB| zuHBTQm?Vnn)#e*go)H*YuW;k4y^pXOzSp%jayu$%_38zioX!o&NsiLahhhM=T34>K zmc=J*vC#k>cQGW~ZZoPuyjP0obTH8LYk5R{R z52Z?wwwor`vWm$2jVSjc=rpuv>U-o-5x^wKTf0ZX5`$}d%@Q_ygxyNFmc^drEyZn1 z^u7jkLR1no6(z^m$=}FT2;{0cKQf&$#~Ws6%OND~umF#4m3gPeB)XhWDb7i}N1`j& zWX+VKwd54~mC!5VHx*+YD8&@h-JI8Q{OwK>epQ!0m}1j18_G%%I@c|OrNTfb!o1Vv z+uqW6&qLOJ6{GJZ<&LG*m->5Ze)sU%biC@v!rEJ1!S;(@xSda)I{H6DyWIF4Hms-A zipBa%=W^sGR#z_)1}Uw|CCwaA14{!}8$ZeQ_+lyK=XL)8jI8%_C(yL;pHDm&6%O-C zaxVdQ<*7tk-T-?Iaa&(B+`3s=!QSY6H$td?+{Q}tlJ@T*8zvLN-fBcus?EkzcOu>d z*nTyC#(beDV&&KBQ11L?IlpRHLkO;OJ~EeOX7TZwX|j=JG!5_I0tHWI-94Fi>M^IU zxvaXZqSL}>b9u&Hal8XBGQ)RrM0ZOmDJiwdRljX)AQx3!I&R*p|0ZR1}vU&t86Z>nf;l6D3+OEaW<+-nf$7MSzeM*^#ZSiTz!}9i| z;$prwviV6@r+bapBT-$l-Hy~e>BBhc`1xu-3ziV1+zxLHesQUTBYa^AV}aq2yUtT6!#zdsU)>U5cCbWiI4fCiG}(B`udwty5uP z_TH}bAm)yDa%vB-cN;g0PGK!L3ld6A@2Cns+E-?qV9RPPqHzF?Kytss?l;Q7ei|H+ z8K#vHwB1We!LS}Wnl7H){IJEgwBa7B1!C_cVM-xU0RyGUu7WyktemvB5@UwBQ%FV5 z*3v)d?F5_qX?f|&L2BiadBKPIwq9C22AoR_TWCU1fP99wqQgtX)ttnw>4G%8HfnOz z;$pb!j;c1)-^W^Vy@*GnD{j!TeNZpcTT3mhd}uaOe63D+CR9f-cFH;qJ)`1($iHp>0B-NiU*ZeyM{+xT%%5@IJ;7peq#&KMW36qi&+!)e4|N;j z-yGi4hAo?rKbCy2j@_1Pf{9W>iH*rAWm4U3X}_khl183AYpJ&DEA%-nO=_-vUJS2| zrRS{HK~E%VRk2qm%G3$+%0H=KJa}(8Vk}*r-SU~q_2J_jki&N?vJ?$~zJS=!Ve%>$ zb{5Zu?8~ZM4Y0Ue^BIVYN5=XRS#Sj{)Rc_~CghHqRQWzbUhQ_iq`003V_#`WdKb?* z5!+RP+Tboj!Lg!zP(2Z-M3*;Ed@8cw^iSgzY2`K@GRPXT~ zw_O%N(z9P+ccX8`0-zD!8Jq}jF92U}`%uRzg+Cb1r;mvWMo zPpHlxHj97;UmkVbW>0Aen1g3Rt!)VCD=y-F$BA|qqnq~npaZ?Y&d5}UvNPS3{GB8^ z>x*@39`zq>9y|prvIBR?t#7z9A;C`a8K^8wJ#)H~F@Ii0FJNDz@P} zyd&2g%S(9cDW@{&lD$~I?`5j(Bc5Xw-sBoQ7@J)L?o)efe+t>9EE@#_ZzJJQaR{bp zq+H*_#-^V_5?Ko3vTBI29r9HbXH@xL#)oZrSwU&Jr-DyA5G5zoib9)nRFVGxI#R3f z@IN{eL_$P=NlHiVntC!TEshx1X&yfMoLX&Ec zCVH^Z>ca$7t=7`ktT6JGy57pe4QmWNSYhhJ4^|j@u*21c9;`6+VTY>>Jy>Dt!wlHg z8^FC`p}k>)#+(ToPv28g2FE0&T}|tLNY4?u+B~ZpGYnBSP$S3Awwa+PG}Fh*qS_@9 zI)O1Z{A&yptT1m_Vd}#PLOBbvpyP2l1gGV3prRw_LPLo{)ww{QA7w9PZ@L?*)cP>T zG~&!+8^z}^?y657&Y+kND1zc#K(mNe-)~A5rOrzax)|NoP{UoB1|2$D{b|y$*l0oW ztS~nkYidWL`(e{>fk&vUFqGgkVk(sl{{V_Q?XgE&c-EL$SWinSlHaI=?oNkIDp743 zl=_0DViLcJDZT#yPg*QzlWx3`DR}bI^#wb8P3V~xltdEIN`Ofz2Ksr_C)g1BK1y_5 zH;~=MkabD--k~~^7=@CLQhkBtYFBnT+ zd0o*no!r^EX|eJ=jfBaR(_yEaj?;1dRNBC+U;vTlYUeBAy}5o@(7&I_9gzI->M`P7 z#m;ctVlyxZ5D7Y%woWsCbufyCK{h zR18KtmSb}g5G@lGGyN2Xuhl;tylaZ&bUCdDmloI5@AP?Y4{|9fB-tvJ+wLgCk)%5v z_#r3a^Z|1HEwyPT`c(zrDcZ&#sbdd|>v`p1m!ppM?p$!pd9KfxwAuKIyDse~ZNioZ zPb<=MNmrQ~c+s-o6+p(OAWJM$q=_%D&7yH4rBDvcj`oxU*h)Hyp_>3j+ z{(BKBmBsVWxeEXr$CTJB9u~i!wyh}UXqW0;M5n^sx~2igr?fyUH7 zil3J^RkXe7MLfQYlC!2T2h7LdnY7qpoo&|bZH*}fUS{e7tTi_8rg@9VrPOO*97$k? zWjk%eGN@RGopT)^kAb&A{MNOv(z;4MW3f}}j&p0_`qnOw1x?HQBg~qN)W{ha*|xy` z7(z-MJ^*NSwRSvr@>Z<>04A3U)phdTpuGuu;Tg$U}SQ#+KSZW1uZ}&g{8%z!mHZE8&X^Fs!u=eEVScp z5_u(`$$Nx^g|!k}tdaZ&1a&>3@X;jwwNoB%LOmRoOE+05(%2GdRpSjTPg01lcjwq9EiVtSvg)`|Z-K;qnzvR*H+hKldFI8Z+6x zeJzwufyYrk6W@{M7x5A15=HG+wEXK$FU;`D>3<&WwfR<1Hb-i?qX=5sN2&@zYb*$~b&n6qb*cX0StX-t>`XE9C&EL4*r{=^ z)D}J~TiHt6TqiDtwHMB{`H<;+i;sFFJCE5Y9S>1T{k@s)G`Tw0bVf3{Ha7nNqfiB3 zqzj6)*HT^1cY63Ylt5*qMu=DTQ8{X zxye}GbUr8Ws9<`#TQljj=_*8mf_mT2?5+FY9ZXP1drhx{`?`wXzY{&D^&PYxxYFlZ zgPeUCxmMA-(liTBz7?+|N5TC8@3g&k8X{mNOumC9wxXgGfnYlM(c^OgI>F@)F8lr! z3T6|?ysKh=F5l=`_Ul3=lU24*O0T;99y(Tu`(j!vrNu^5OSmZOw)zj+KyU=9Ul&ph zuUMv-;@6aWq0$i9Uf%)ZOJvM3;0z0F7xOiP0uRDVmkC)T+ed`5Mo`GjSmkc>9yKB! z*pP-&K(V>;rHINH9wHu~+TyU3Drh$nTanS|mcNFp?&*4qNsd)EjTFlPexhAk71Wdx zl78w@)|hD7B=Uo)U*UVp9!p;R;%;i?#UPsZA7u8`&Hva(2L6(j?4#Sd^y0K(tT0(b- z&8!}P3)FtpH{P$H+0I7tijd0s$Llf4;|L4V5BHqqRMF`(1 zNrIOTA`|Kc)?4Tt1dwY{wPadUNv`0z0d0f5Rz5d0U(_pQPwEF;ot-rY@1yu+MXU(? z(Z5f5qDG{JMO5fjzA`OD31T+&_78&M{b)W6k6?^^mel}F#+rCgrwom2+1IFP%rMBM zE=6+W4O8tjq^@vyWsSP3)szI2gO?nXy2-fm1ksR%%Z@0FN;)F(f5zk>jZS zlz6J-Yr<%C2bFRhYOc)Z5)J+p5~2S9%Tn{!t&Br*$oFK0Jo6y=3CKr7sUTg%fRlRP z*(`Jo_tKNg9kIyq(-pM3xT{G3Zq$49q~)(9N;gH0U3J%NsMC<|k-fgnO%ucFPsnO> zxr}m>K=l#`H@4yUP$5oOIol}q3&-voIwE#Aa>*A^7SJ7Q9=GBj?;bj#lzhXgvOZ03 z9~}<0av|;_SB`|-^rR{xnQ)JYANkf3Rrz%!>EbO|DbGzQKfJAKD`AtCsz!g2Vi_A< zXVyQe#^>;?mHiBj7-2a0oKO~P7Nslql&cRv8UvFUgcd@LV+P;hLS=#b#NO1elP4w6 zLHHP^t{+Hr{-mJOAZ08A(Hk1!*%K5LPM)IFpfC5(9NCarVFt0zixC;7w|LlIvD!6V z1BK(rBiDH?j}QnS*;;bQNk;$-o>L8q3Qo}b$^4s0xwqX#`)sf~8!?FipUiS{3fYR% zG3dRt)SnKt-Fi_)ZM=hh5gl$j@)jIY%!dgit!Kph>V9c<1?46YZVE<{T(LTY5BX5N z!gWU|Q{lh*XmfD zRah*=yGZV3SSTpDBA z8T*5Cmz$wcSF^2ZP{0`MNsfP-B|ry?iMF96TC!Di4fh>H{YcAK!rGP(iC6d1ttHR{ zmRPD*!U-ldfbb^bvQo*UJZmT;pm+HymQ2G6M3B=zY+ z@|)}ENl{dL_aib~l={PLB&CplEr7K-70^<~$%YLP8*K@Fk5TPCMH8l#ProOeq+Riq ztYNZl$MUw4T|r8%(YsX?rzhO$>!$(FoyN&vrc@@IZ3Fmg7v4|XNaA_u$NlQ@`H>Hj z<}f%+M64_wLA9y)>{}k?ZM4+-p~E@2&7d~;J5k~ioxuA#+Le^#HAWB%A(NLbVtom3 zM{PDJ8;+l4H45u%suI}Rwi5iW1&zgddkJJ?IT~7hwt@8WdIyp%U=&zQHNW2<`)`DsNM5 zNWH0-$ZR%O!%tJO9Lr(&r=S+xh5XugZ;!Ma_}=46v)ARV&KXh2ag;UCuAXies-!mw zV#dTMYJS9u={2_-UCuK7XvN_~Xk(%4?Tn_=;0QGO*jVl$^`zfmd)UNkLOvw*9Xu-| zw87b?kcr2vfLkDe`c9O@cN;)uu7y*NjI<~?(BJxq^QE5u0A9gXioh|>KOt^jFwh@{ z$ES@FTWhurE;;v1HII*%ge;k>Z9v$ka=O)S)L_Ka&@(-6^bLQSv?xJ?wlYc7pDn2({V}yC7{{Seq$!0<dKA@vX^V+F@alT zyOEa2r(#qipx;F5NcNLg8r(XG%WQm-c6K5!X6C>CY_}Zq1w@Lq`)?FhS+hG|QRX)Z zCp{@B`Gm!3b_MmD0e?EA-{d`{*=M7~`_^@R3xSTuqD&uO5z5$k_L2*!T8JQ@b#rUx zNxU?6tKgAqF_34A@c!U&c}bm($$CvNZlWIe6fOI{KXp`cJG5+mrQEa7<2E&An7+lH zzo;Hf#@Sq1lD&Bwc7-;U!6Y7_fu~C2@A#b-%Q1L5%D!D~OAf}I08hPjm`5Uyo{iJQ z-Uy#1@;lZ-ScB*~;~nYb{7Op; zp0@c6RNP97F)497e0qab{GY{j@BKX=;5ozm!)IDc$$28ecq!E6%sy!|XYgywNOX_s{eEzQ2SJASgXFtgJyPm9U`;!<-fyZRqTFOc< z`jT|%;ayVW^>!Cn;zv6UV`EpOUScxJcajcnJiKQt#thS81uk;w%H7CU+9S^)!&d(JI_4PUU^7E=c zSd*MLC+)_0=i_C!@%*%rq=tt0LnikJ_kieYqT&8W34U4hXC?9ldmYC;R?@dWg?&KK z(4_hd*9GKGpyVBCNR0v&>@KHuDjwSowa#xPgRbMOpZQC_ws^^TOq@8fC8h!-xC645 zfI30<&`x4n;rTkCJRE~PH^m2j6rA(`Yb;Ck)H#WeKNEV*s0 z*){|V1Adilr^e25%j{Eb^AN5WUCwRf{D(gpX-v$ksA)rUEg@woPqN9icieW3E=F9G2|td2ETQNh>4HtlSFR zih7_Z7ZOx0puG#k&m%4rs2@!mLXS@&TShUNBnxf0Jq;c97fYx{5QLUxrfOXEoNxUa z5uhtQH($b+osT^W3aID~#l&_-PBcZnBQmw5KJhvsyZlG&6+N4=Q^5@iaeDIoA>QNi z#d5Apw_BRzyiO^mq2Yq;kd_!gMZiv)o0=C7q3qN6zTr8pN8h>i=tCG~53LDNvIzj6 zDt5`ZmDOvh2A;#=*NmR-_n62=AjAX$+o`^{JwdlhUah(LFQXj0jEb$Yy;g+z)#7~) zPHU;^fg#Fn1&H@nq{8Ig_Ey7+L(P`fEJ{yWlpaV;G{bmoryn<90 z9nQ8=4vO~CdyA!pXyW~4mltiyI-fIJ!Hr~1X#i*~L^RNqVFT5M2`{Z-hLwgMtT6Rq zhWm8WSbm$W`~)uk2dJWdlw@f)7R-d7S&_QhC=Qgn zvjiP^B64spHg}~3Koe_GGHcobI>=ho*=&us8tKxR0t{1XWT@@ zLXWbB=t$L6&JZ8A7cYA3p^DKs;^k|2>RJ>Gr?(r6@7br<#p(P6|mQ;(6g8e}E z4}!SE4d*0g@oiW+ik9k}L2lb@l`nBlohy&#{s*JE)x|~r2W`Uq_j^ygvc7|^L*Kql zm7g{IJn7hoQx$b%%#|oMN_7cWTVA!P@SSR6lS$y8BlG>c<&^P|4lBLg#mBs)Czne? zcLgLV!oHvPuTN}zGeYQL!4+En065>SdGg73@qerCUyVv)=ZZ5-kAzCmr#jnRmFHZ? z@g}vh%}>RigT(&;FkMu9Y*|Ioi|_vcZFv+luOXGm;ODFm>Jcg60=isoRjsXZ8y^7P z)_<21NbYrikTf=9)ws3^%y%6y*;4p#bth%e6=dU$zYbdDRK~(2nWGnjXmP7 zs`$+t6}33xMGSsTeHQ*#lwQ1V>Qe~82QXZYr%5Pvr#(iNPnrsJ`zw&&{x{0}jO=*- z0OWPrOQ~~VKB4#~Vnc8xdQ7_a!j-I`w{JCW>FusNW8nMrerUJQ>NP%Zt9!)JKU`kh za#)ICIJh&^ej@Z`x4bI-Y%E11Ue^5`GRJJy(AZs;M;J*T>}g1QkLBzYuku+grD@sZ zHxg6!8s8(0*nV`=`i$sF={&`zzJ;ZY=Da(TwBs3;T4F<&OsMY!?fVuI0ljk%kK5Om zl~QL*4og>NIJF>NYqLF=-ADAUPs*y0gG`_VKtDRRsL%CyYT_- z3pcC%J~r9@Y~ZTDmxMYcl8@yRF#MgH#=~Xs&p1hHdoGeS)D;4wYhJtF7nZYA^-C!8 zu4_SUc8(ceJHz>OSkAoPDl$tWe0Z{w3X})s>D#VVrK)#CEApN?AlM zS2)h%y(TBbX$ntAQrP&Fex}veQwUhoOye%tx~`K%XCuyJIHfpHl$GmbfCXOBs~qH} zyO5LUMy2w=w5?w*kWWwrs>-cpB9CKlByrwE=yK>*hoPe?gniMAxyQQg1wJI}Q-m-E z%T3$LZ%*Yjl22Nm0ega|l!cuevS`@BIZpvLo@8x8n?A)~&WAKJ8*+lsGayp%;0?T7}Nk>^Q!a{88j@(i~(3i1O|;A%(bPJ~gi!l_5q0;fgBkNuHa zyhvAt=#{qEt@`T#ry>u8dViZwzMhQy3h7gTd-&$>`7C@Ye{e_I1HF$>B}h@yn6BQK z6m<0)DhtUV0!S5;v}5htLCh(K4s=ObEMhXzSy)rp<*iMnPiq|!7@3sFN^rR)(xs~Z01&X(+$q|s&p}tP*xeFbY@{rayTazC zspw(Q8AAhf?FEr8Gl>a)z>bPG7Ol><*x%g89Y!)GHWW)veigYa z3rfuOK&SHZ&_F|~Bf&R6+ggQrV%J*I7#xGIJbh&Pq>Vuo&*(ymZL@cxw(=6D6i}np z3hDQXoGkKYt%gt`)V#{}xZS_S;Y=meMqLDbMS0Y)L&!;QDElo@$(c4pl_Xe;+s3n_ zH06M!8L3g!g`s66TT$a#ZlaLQJMJXYAI;tP8j^&)r&2Fe_VKULmUQwg|l7EvY6v2XCee8AcoPX@uyYIi`h~@ zg~J4$s0ZxQqwWg$vAK?;?6-v;DEJmq=o0K;z!&HK^FxF z#XeM7Bt;6hE#_5pD#Ua;)EqER&4uIVo^0*>_UbA)H|_+{md48)Ww4Ye{-o(b_E2YL zPyg9aO9KQH0000805o>`SZfYAbq}Tt0J*aa02=@R0B&V>Uv^<^X=Gt^X>V>XX)<@QF$Yh|uv1NeGEc2#O=* z@S*lzUTzY+ye^(RR!~=K7>|uBoY&9FjhCN?j~9@V^>eebae{f#S;OocT%;L(boVmQ zIY6ZujD@xMwA>V7_6}-~Jz)BewGC_@JK2as8DwSXr2HiO;BIi3mld5K+}Xub!cUsv zw{i&tz8mIcp!?0@Xw*KuecS(bWS+C(6UmZNtaMM<*)IW6ftPW(^bPqC@!3 z&&w~s%O}RoFD}8)FCid6_a`wR%y~dZchV$HyAGj=IQF~VFOe4fw_1w{>|9`Ec`b{rT@+=_#doF z{w`K<2OBLI)WPa+{wX=Y{}ulbY%mv3L`*#Y;T+WFAA)Y)9?rj`3$@{eIl~aFUY-bH z{@->G@sdz?@$|BCv4N>8Ni!fucpMy{65?VoD-lsK7`LsBHI!RONI;O=T1-Hg+ZF~B z6}J}Sw-w_P_)A{N)yDfy>z%x?pooy5t&JGBAWTFAkqG?a+~P0+8*VFGs5swm65;<# zPRGLmk*`+Hf0w_r4@KA)6cQE^7ZerY7O}F1A?(Afxy7M;R@~M?g2F<4P-{V`2=uoe z2`xmfLan^4l&rj9(hPSM#LXwlEg)j>R|RqMiAnJB{mJYKb+GmSmsk}&U>JaJBP*d3rkd!2Sb|u7e-U zS>DqPX5)oW<=`sKfT*g!I1u~f;bnl>ZG9Un=ig@j_Zcg=n=?$`;W1(wAqTaQuyyr- zTOsmXLswb#u8#f)zM6xlm#c^WpLL}VbN>@LJJ9{E3kfTmyDF1r@U-%QAwvDfpu^u% zMMPL!9L5iYA+|^mktc%ILWtdgig4RPp;mt^UJUk^oUc91<$sL$A8ekkwqCwg9x!=3 zL^}L++tKxa!5v^8cfj@TE+fFhN5`gRW#i!DJzW+iUT&n?6+4CNMs35jsSY+?KY{DJ~P;=)4zUt80^R?7b`tO@>S<^En9 z??2O!_djRof3C5+?56vDca-o&WVyJ&Z^!N~?GcxJV*j}Gc5y(s=XO_8zm5E%2(@4I3V5fb@lq5V-`{Je+?`={VPip<6p3j34O3vsFPXN3s}|24_*w^^e< z^z-pa@Cix?iAW&4{ijMR=l@FKes5fS7{17BchQR|x=rmO6eh z31~vz4#CLUy-*0c-N1gJuBd3K3(-+gzpo5{KmdTPcHhCp6$u{z;4WSs5LE@b2PUR; zXcGVufB~Qa`~YBO(l+2M{>M#~X&g;QI(1<>LVJMc`QkW^(q1J0S4Ioh%$?4u=U@t7tGJ=Zc&P^ZvKci+1`te&4!(hUtC;_P7UVk4D<5h z)C(jf2{d85#Gg?4iRJ#esM$}F2KVN{FDC2$$ydV_Vy2StoM7* zK&0dU9Q&{MT#Erf>=Y53#JNB|&T`sWx^ApqbZ_N{I5Z|fnw%a^}>FLHp{ z@Z1ITAN2po@GsEc?D5>G`$u@_lwh`2-p*chcX6|Eb$0dkpz}mj5R8udKPU13wcy|A zZ+>t=5S^z74AIfi86mRF!Nm@--7Zjv-|dTo%YV0t|CeI_iT=oI1QC_~0*Ebn0Q^Z} z0K7L2AQNE$V6%M04Co*Gb|1qC&_(R5gwt(-z0dNLffj}TM5Dth4bPq%e zVg_-71VCaS8IUsQJ_rIb0X+iQfm}e|pa4)f=qV@_lnp8ZRe>5oouD_M5zu?kB4{17 z4LSn-L;@pWBM~D}Be5XyB8ef%BWWTTAekfCA-N+xMv6d6M9M-cMtXtNhV%w$3~3H& z4e2}5DHs4_fl0yiU~aG&SP`rPHU-;)J-|WWIB+Jo7+eSL0uO;_z-!=L@Fg-TG7&Nz zGB>g$vKq1>vJJ93au9MnayD`$avSmh@(l7i@&WQM6dV*P6iyTg6m^sbD0V2mC{ZYx zDCH=vD1#_-C|f9JsHmusA{MtsE(*lP~%baQ0q}&qrOMoL_I}AL%WB@i6)Jv zgJy;1g%*YO9PI^KFWNNP7TN_m7CJ4u0J;jgDY^@K72r^L0y6T}B3gd{>FMkIbDIV3$KYouUO7E*OmXVN6nCejb2Kgp=ccF!DI^dh$8)pZDnQDc^ItmvXQD-YNw$1qX#5g)c=u#URBlB{8KWr43~q zWfSER6%rK(6@=;uRSDHN)hRU%wJNn6^>gZe>RlRA8aWzAnsl01G~2X9v@)~~wCS|H zv^#Vpbn?bA-@KH7=IW4kpPE)jX;jTtRRk{ieRu{hv1%a`)so+&SfyZ6rBa*H4ANH8Mbc|BbTW@*3T4)0>1C~Ci)A@zE%P&sVc=OO(+v88!Bfje^g;mu~Vs0IZzc= zeWLnW4Mj~;Ek$i!om$;Sy-NK+LsTPBV?YyIQ(rS%bK^e8eUJNHT1Z+NT4`D<+RWN6 z+U+`kj=D~o&Z;h}uDfox9;%+MUXI=u2tOnMGNezWZ>e9ce`cUykYuoI$Y$tm*k^=i zWM))lbZ)F_ zuyD5MvBa~qvTS~Y^2qp6^`mPm9jjuiQ)_kWTC zsbMGK#^GHN)Da;On~|!KHBq=x9#QkrlFONEnpRp{I$e5P`bmam#z>}6X2CPe zXWq}&vhHVfJZE~I{QNrGK6@@lA*ViToiCSPS3q6xwBToUj{ex&+X{E7Wj z{hG*H&$`n3_=f(*@}|w^_s^c6ueTz;;C#vaO8d3yo4~i1+bY{rI}dlZzPo+D+Kt*H z*vs8#+i&?H_hbCv!NJy{$KmZ!{PDfxiW8xezEj=Pk7w|+tMj;vdl!|LVwXcdjemZ< z^1Vj8&bnd0>H4MlYvmSxdwaVN;Qs!c3_xGsy+7P80}+V!;x9tFJCKlmpZ*{)5*R^< z6Y?E_QGOHJ9ijh&Kz~S~fWateD99*(oB0>%_BB9&jx2^20tOKPNCY4-0qAxB*hV;r z3I>7x`H}-hMnOeG2O(i#0$3mbg!0?xzb27ENMICH01X`j6CVJ9kx)P=D5yxNDCl4i zJz@%(00p2YL`CD5*JB_;w{jOC#t2O*QeY%$guHogEyzTA;1Q-sww_wtq%Xvbd1&Jq zt`tEo%+i-u(rnVdpL=Xvj!N00;*FArXL) z=~4IziBJ({?u?)vgr+c(6g9qizpfw%VG=?=B(?TnRum=+OVuy#n^xMeA-`P$u)!e2 zy1@j1EYSK<&s0B8DE@F?SgMwmn_@eUJ@LMh%RTjaf8oiF&1l9S%=c_D_6E%5^MnGo z3a{yCu6=dWGP3>ce_3wEItuDqvYT&G>*dDJjL42%tUbz4#&A#{Pt4-h2wCNm7JFK) zu1}txRBD@rK)RMQw}8e50h>!od5MNChk84;DaQ33UP)-shnd`cp`YZhRLvPbMKH@u8R}`w)QN5Gy1$Kc&ejteqOwxkE`4%1bWa(_ zo;^`)Rf{V=`moO+SS$Ndv6ouE^Ij6$ogERUEt+eA3J*d0CNzrMPs4$xrqRp08HJ2wlZaYg5Gh=5cEAscWHl5lC+3K9xG9Ms4H?$4#C^f6 zdeToWFQMYzcM^)}bSXDPh!mn;_&vfUESwpcqM(`3``&x?@G^;-JluQZZ<%1gPQ5K% z&a9UrxNXYFhy0XYB7FF6uVTLbuzUIdZcg2s`lM<^BDp5 zP)s|uhJrTDs$+o@RWsq$(4_Ke@5(alM0i*N9cl~Ud|{m$jMu>yJ*kA834 z&nLbx3c8Mf9u&4(JTsF$a#4C7pBBz@sz~Os%^Ph7I9AS;rrQHioZ6k?Ml(8{`;WTX zb_&N_EFzI--&>gDKA5vORfsgBrq;1s#+pm2pw;u$^bng2G|l=X`VFgdWYha))_LKI zR~xgKwHRSjl);WdCW;{S#o%Y_(UnskE=zr-rWS{K#XU3vqVSRpa>ZnLZ-Uro>1awF zvxA*vuQx(h?sJmmE+}f$7fxPGDC}w&^d=kX>WR8`OBvS%iD)|*imuJnvl1aA6oIWn z-neQKB<8Y9_3JT6)u2Ses3mtYgJ-2KQM1*6fLLTh%+eGIzG0=GHp69v=vL?}Wz{2s zJ-YRI0qqBGqKd_YbvJmk$hO`rLF3rff7DTqSE^I;glx-eD^%c5Xp~Nct4So0NOzcM zl>BrSi`=d=tRsIfV3ql{izee&ypXpt%iFXKh~5&%xguHeRaCPRd;h= z&t7SI!jB*4tTCIC;Uiasv))C`?%v2>_2xf1@>97tySPuOZ*7=9c4Qp+>^8;TVzTIr z`7O`^MRC6zyLCBt>D`X;%0BH$Eh^5k-=})v``^2k9Lya%s8-W$>ha1C1?H@Z%6tnSYK3QVuzh90?n{#65jY1jT zmsJ1h&AsQd_oe!Trfov3tvD=j_ywjRx;nAUwc)3I&J~6hE-s(FF2lxUO6R?Y{_>`} z3OWh%&vaG0k)$VC_S3ES`iI&F^extwjCv@H`mJO$(wS1FOXIP)%yP2%N#QZl$db{l zrZ!=pA8=-8>#^&h*LO2!GDQuE)AP9Mni9ObO2b(X$03b5kf*DfEw6(Vd*itR+`84+us^#VF6LWNj!`$7tByl)+FLL`80_1 zX8DQ+5+U7Hk)PZ3?U)KA?lX@+DJZ}@oA_;`<8T&gULV(FUeLPYJlXKs5Qh+?ki6rN zn(Vat6#b!xAt%gV`8_SKH++65%QQ7X+&>${x{HQ&G;_#u8G+=w>|>5!LXS?HRh-4T zl~uqO_B5ltBzp+=RT@N{q)6sCG{`){oBp!{jnT){vdc_k#r}0qse`9E3Z;H-c8@hwHE)y_I@2L z_0sHntYucR5(2ZlAlK3j0|%vK4w=JnZjIRwA&{X?8V1hunk7spE(2o;mX4e<=eRTr zI;<7r#|e-(`^4moWkj#6gKVR{R#VeVkmB|MYr|M1(P!>;^s*nz#k|=n^lFv)MHN0J zi&}imvO5=7+L_JWp53584ZKnrbYsMjxr~`)m$I{>t&N0Dl~O`!Mly z`5Z1odc>Eqqo#DET38?nksO zHm@u!nCh7^Fjl+li%4pv6EdfX=JAY%c{QRYToxy|Ibdu_+Mzh5#+Z9d5ynglU4zRW zE0BnyAx{$R2+q|7K5lE8J@u!JKIO-q>`djVnZ4tsFMMvYoPWr>F}Ege8%bHYd+*IM z^)fY}I?WoI^(@dBD}7TP_f*=HWk+%9h{QWjB-Gb=)e-)DOgDjntHcT3fo-I98nto@ z_$4=JTPBjcRpL00L>|F&9*(Wwr{ycX1z2AG+GR$Qe8KqZ(Xzt_hCZ|Oit3uu`J0kN z#u{|VE^=q2cP}z%Bb<sKSr4bgD6U14@yc0zmkDv$JM_+$b!ww@ zcEtnooX@b&yzgX%c$B*L)Q2JKE1TTh*K!>ZDVBl!$cSHWB~*wXczk6>JjSt@M7>r-rU@odKIJ{Ud^pFtuM-XF>>RKF45Gxe$7Om8I0wg4XE6^Xm{p$D z9miF&2d&a8tjTG{#B5ymp00;4&nUHLxrTxpMP+N!@WT@hC^TOLyvZZRV(Hr zUgvlF4OJ{b(Fszv5A?=5@cF@VvzQtSIb|nBg8Q-AcuIAKgwp8%EOrM!Ymn@S?gvhYjfz;YrH^T@oP(TTpE8#fw&4sJ$M%G06!7~kXm(1e?#&=&m(X7gz9XV}c#ie`oNSh@g372B zwt&&sXYi=%J;CYH4~)j2++@Kmj+|jXG%7Rs3;PS6WZgi}9Y}`3Gg9*Jj@9C7LfpZGNFDk!yNi{d_9kgDUGG zrr4)3BXO`t1Gi8u4*PsN6(Mqk+&k7!%Zc3y^I zl(8ITC~h&&Dg2%G2lDi*QMJh`v8QBOZvocLXm91&08l`$ztLyq&y5i6;rJ5bM5Z?3 zmsz?0ybEH$*Fscor`nFrt47h0R;j3Hj`hn-vQmC@vW553eO0mZ0fX*iHdw??e7YmX z7VQ>olw`Hm4BF9?(0;ma!(SbyJ`MkRBeP3AN4JgCPM+9`eA?$dRXv7s>8@)pu>HLM zXN-0ZvSy$+o)6_5SvhVLhHl}Ix3jLQ+!)BHyzmpYpf5QUTB zheID_sh->d0#az@?@oC)DxP*@3hJ93h*d1C80$A&=HA|Mr;KJiM2R z5MIXE)N9N1oj#9`pNh+&OuB>Am|CHTM2=Zb?;GfvFYa67tVzW%TadZMIN32j-8UdB zk@bk;%o?|tY-^l7Bgkty>)T3|VADL24CJceXZ&{a#!|GcxL_FtlBhXL&&3x8xh#46 zP^ng5*pDF`Gz|l%Jhz#omnqjB`Uz8U$!Z51SV_9|V0v)7lo0tRDsG(V>@fLq+HL%| z1ul$LtoKPHwr4><`rk5P6iF|*vRSvmBPB~6QBmqjefDx5Wq(tj2C$#FaV8I&Sg^bS z$)canQ|nc;Q*Ks^T~X^NH4af+I$qSA{UX;-5URO!7ke4MtRdOJD?sX5ADYCw9E~Gb z_{4pPa_ZbBxy5d^!^E8c^<-vU=tsZ5`gn?&I8M5wjoOdCV~iP8dtEl)^;WWStV9}2 z9EdjsQ6iOd<_V(tMVft9?pm>&Sotz)iRk(kh?1!A>L*uFM6~cHxVye)iel^XOV^*a z7h`H;-blI=+0l)eO0^nq?DnCAwaqJgQD<%NWI0i3;5{EA^y+6*Np{M6oH(wu&!8XB zxT?gA)2F5K0MQH}Z3rTkqx&|)2P#<==|#fguyqUATVXKcG0Hsn<=Ri>t2Wi9DxbXj zy8rS9(Q?Zb&6i_TeoS8al=Bvk^6=x$XU-hVXD$mLV&_d#Ahnq8e2YBxn7gy~oO}Jw zL-`vlx$H#4pO_vOe0;L${*mV_ufE;y}Au=XhC3x#P>MU*oSe# zkXRv-w{xa(q0fhVn1(w%ZUMR>X1#NHxH)cCL#pTSi5*S^1l;NY&s+bgeH^s-IbYy& zqQoj`jDY{pBePq;yUjIHE-oj2ufkIf#uYG1sc{SR%S6%@_kz@!Qr(ri-gFwje%V)I za41n;5M%v zxsIAcM0xmqS`{7URwAQ{a7{0j8wZW2E=ixRu7MLLwbtX!XtqWlDhv-bb~QztnmBs8 zMG>JD zY6CbMZ(_-9N8E+9$5uRgA!@FAJH+4WXxbm*uO%kFOA)QXW=yPjSz4ukFHTasxa8uI z|2a;_fztcE%N|M+(*+~8pN1Z<*!$2DFxYhtUX%IOOb4Y?yF!~?gqDvFE;;u*Eb$#wd~b9_s%KCIInQCk*z#En5JN61gwSU^wp8b*~GvYe${lc_Z^|!+&6hN$|_IhE5|xR%3g35=viW;L6y%SYs~>%9r(xK-c>oJshoRmfmhX zPed-Ja5kZuk@6`G=Yp}gLrtS@oa<@|y%jh&b~G|d-|a^p@36~nK3SQsKCN;$mRIU3 zEv%6l%@z1^X)T4sz3W?ch=q5UxAc*UhtJ+qUL#q3?*}=FYg=Eia_Sn1O;TMFJ^|?M zNG+sD&S^E4<`6x+AC`kB`Zgy_;5~zs+qC|i>2%t3PgDug>PNb3wAxJdZ-da?40U!g zx|wQXiWP4T-Q7JgyB*gz$cj!g`mBi}Ib#F`*|W;IV-9|pvo~2Z4{LYq!|B^hEa)TM zc}5d;mt37Id4&moYN36KvA6|Nex;1rH*UOnHb@neA(C`l<4m$uUHEnHb#FEF6#?^& z#86h{IhFfMw%5EtOfT?$;A6-J^38Q@$?h@UNYcDi`tBSg70LG|_xo`%fzk>jtO%hK ze>=8J>iJLtYJFx>$Ty#Ng9Opzs;Rq?FZ@iVK(|0m+_|sd_;W?dVV4q@!_v+WwIC%W z>F02@St83*e2;~s4nbw6-;L67l`(rl79U%hwtXO*^PoDap<`NYbdj9aeQJvLxFQd;!;R~Gk zEH~Dlk|(FPzKF0?q^yMTYt$d)qmSfHef%zdNqi7<1oN;YdteQ_mfE&<7Q+WBbRO8amh!qzjs7=W6mN4BH{&rR z&WsHTQ|lLHT2ogT6_!3UIZBPq_)8JUB3SRQw|wZC@CM>l_9tJfqF)r!F_7#iF=GaB z8xRIf^YfZ2c$It>qZ5W^ZBTa7fA_Sb`w4jl!3)Ywq|ewctjp+fYide!>(qIMl#Ef6wr!!tfm6$M!k77Op`*K=-dU9yokq9fqeFr9#e($b9Px&*dqm`3}lBA?cP(t4Y*AH)!EpW?0yY4ZF>n~;N{wi zhlV)!td?fFY6=HvWo7Aa0s1|>r#utRQFoI1;4oX6TNsqH{jbApj(*G z(FsD<(~&B%gcI_QdOnURei==Lv*;2R{fx}1FN%V;tkOTy@;Hj#;T2ale?^Fc)Uyt$ zcW?$OpP7RQAyi9S#;q`F74{N;53Zke1pWhTP|CPMiSrrsEjaazvq+>zd<$DgEW5@L zr)lai$u01p0^ggiPeMWSy(kq^np=;+sIj?TUe@B|N(S4;1I=OUutS@`%MbS7I8wT7 z%2m7-wYm=L8$u%!+9o=EFupO0cX8twV1*wODhsvr>B^k06=bAmQI5syruVG$nZ1yF z{M4p6&mtI(%KXsj806@Y39X1OZ zVTi2=?!T|;GFY~z%9YcEDeRLDX`7+%i!7j&t5;`Tz-8KAI->b;|3NIrT4B`7NK(!W z!pAoq^a{wrX=EfcM?Wbwk|BKpicS{kq4RXw_a%(<}DYG4JTQa^ekHA2J0clW04+YI&Tkadzsi36QRW-!gyHP?TlRRj>5M zU}>Q)v>Fpwk`G8lN`v-PmGU3ec)zZ`3}J(vtrv*m@tYSs(vu8!W3%ToW0&qdvbM~R ze!j{M;&gB!loy#N+5H~7nL(;Njm>JeABV>>yQW86CO7t@Z%krVtqX$9KXg@gqPsL| z^u)W6>eUL`&)O!#nDL7EcKzpNQPYwT3pGXw>)^RUg@%VDwrre*3}-aF^ZUs_4nNy*D1XOM!Bba25HLze5v zAIFoTxCN&B`n;)U z0EADEZtP$@!V{%ig=<%IOguv3lcO|Cc7-G`jZVxz1i&p z@|pd{l+-Wmqe&5V7c)fRI3~X7>_RYMjJ-NLEM5014vm#Li-^yHV?=KaY4#EpwX<`7 z?TYPZMOA;-V`=R(@UoFhdm~RzH;)A|ffP}2UiM-$qnBZZaF?P`)EoD5ajTrXHiM*1 z=`Tjmj^L-RvR9%F1Y>(pC2P6z3jGo@dOIqNwO$&?xa&@W*HEnchGfVcvlnb-xLa!M5T86%Y^AX!-@1E>d|*E>y~oW=ZMnzC|Ebfx~VCn zCB5<5R&h|F>!Vxh{sal=$SI36I6K1x%ihRS@+9Sc*`-zBZ|c`2V8b-8xXYyCq&zKQ zzm}OjM5~dA=mXx0xu4zrJNV7`uhC!?3h8@j>S*0yqG$>W)2{p#;fVeacF)C_Iy}(< z>l{CwvtTN>k-l=N^bYM5`Mo}kV>5Ltmx@npiLd!?fvn`ZX<-9=hZG~0%TE03#`9s0 zXs&|-0rjI$Z)ML*Ihl#{fmNv7t{{c^ult(gF&5(Kta{%yti{~Pi{za7n~2<;q6F+m z3#)WAPWOoW)0Mx^E2#yR@IPaZeYxyp~%c$ zpFvvk$TdH5Nv*nn3)0cFqry7j6Ng~r%1ntrVqt<|xK%82RKrf9lcHCMs{4;V7VKn)meWZvqFu)`ldO~p32oBi;3qH4 z+DxWb-y^5ZixA3^z$eqeAUR)Om4~3*4{BAXO+?*6VGF-b`l)LrQCU!G_j$S?#(O)H zxiXKBSp}o|@%#$kD(-efo~u^yvu=W_CYn@5-c%8k=1jl&;6vEHP1|>4dXulFtY(WK z?V2{-h<9=e3FZpjq1Wl>&`-{?8fB zo9auTMuFwE-yA30;IQ2sPE@A2&at#^%b8p)(An`amHr-Kx+xEdp6v4n%uVQTqhUb_ zxv<__V8i12i>k=(f)w=2vd=rGkG!p)`@76wI<^YWqs-}sHMyIOspQzyb+wCY zo(6DM29Z<@oKP%540SnhITa#Ba!Ay#`7(-Ga=oU62=jNDU=RS#@ykqW785+#4L(Kn zN)}C*&B5N`KjkkOoaz4*xg(&RgE|0JU07=-r*d=iBDW!bmvV^h{u9TIoJ5bg!)0>7CX&rv3K1K(I>6?b~<&t583C$F zWP5J*RkhFKMq}==l=KqM*G}jtynQ$3b5WgW;Ie`GG{AP9URsD%;>iRFeC{GoxwA{} zr!L<|&nY1eg;i;<+0cte_xAKE6+YUnQK)?Ftd+>ovp+*B*4d2iX<8yX<%|a{*Dk4= zU+p-0|;5^fBj&YM$0WjPRmmjoTydw9yuPDM8_B%SWbKyQcV3g2cAowg-hbP|9 z9GL4q3ZSGC*(>Q_>0Dnq$OZq5l5(oXjlwy*;NPN+t^W@1q$n{>4`!^} z5vSeVGDGow5^0}iRRhg2gd0F~s2*nPiUyAMbJ9}6k-r2@yCb(hDOg4&pw7hFNJXrN z$Ni>N-k5UcoS5nBz0x9v9%VIkG3A11?s_WGTxL!>#(MI47a8ef*-v$8sRXhjE;wjFUs0h9V--OG1Fz1)+%>JNDK8oxr)r8 z2KL=>d}sW^Dio=XTIrRxF8$ajOSyefG2imb?|#8f1MhrBvedLEvr-d7 z>t%AFR0He&LLI$*5vN`lk7tWE8XozQuFlML&Yt>ghw$_uJF252m)3w9{_^ znDEt`0j%I2z2-MfAxw|mo8QXoiw;eFGOSgCO1BOq1$2Lw_$XXaScNw8>w`Xu_R41s zmTWV)69Y`jGXiVJa_S8O>=2L!CfX;n`|S^3so69-4Rd_OD;JEt zG#_p?-t^l_-k%9G`sz6xy41eLXyL>Bz6$fE%d=exr2Ntxq%IMX$l7l0wm*7DoWMzqcg&V+ zy4Y;%^t!HB^3aCG;$YxjO>P0bMUwbGlpJzZgbF3a!0_IqHML&l1N#;GDJ>_yidPdqjI)kd6}9?m`lQ*vE?x zsP4N-E4gwMnu(;khM(l~_#lZ&`StQ~H!;51inb2G*V`ch~>@czS-!>y?~xe>SsA52UU# zEKi-4TrcOPBnqra@9?4J?$OVMZCuUJDwEp2XmnY}?WpU)m`jYAZeGwk$^_r1l~@_B zjw`FGJHvtHkur0SXlLO(n5Cb%Wl1?J9xX9M=1lx-f2}9OvTT{(cn&+adF4MAp~~eI z5^iShh^3UU&avN|nmS;L*HDA=wd;Z|QPXL7=6YuEK%SuO`g_x)^2;Mr)E6bdo8Y{fv;}`N-Fi13Bbri7uLH*7RlEJafpIWro4#i@Id}cxkRdrNok|}2D&p__J%N% z=(FeFB1!e!QdP>tKc*<4tw&@m(33p($7}ADPMZw2AFVQDsZX!@K&4g|r<+FEb~>W44@bLLevzSOo1D5Y_vbXR57g-91OmU|1K~BL_kH=}I{pIKs;bAGS_1W>P7Rv^par z9UD0a7&WPDRzp1Z#=v84FBi6=9WWdBmrWUjl$UTwSbx1|(-pb}Eb(%Bm;?%*y$Ao(w#kkeMl~J}9PJ-f~8>k2YH_42o^5U-k@k zpN2=@0*(!`;8;*{V1jB0if^hFvc+RAx4gCid8HHn8K%Ad%72 zEB0a;IPryQcG1j=TSI~>tCH~76<;N{!re|&6vrgt;OQ3u;l)MvNmBZT!te% z{~Ay4Whfd|ke zlpns=SJH_uhFJ#`gSWwf>hxBee4J zke#!WfePI#DsQ|ngU3wS)-uHsQNK*P-5T?hw`_mu{&HQ9;qYbS>%&dAx1qZK2EQpc zilA8SJ{7ebm7-ffp4K5f0}66}i_9*r7o`xrMMzn`f+Q^V)}<$amEl{p-B6Cz7tlPRv(qjX%^4<^gwY@6)ykkCc zBHcIEH!bhArKyYArFF18%HHtxODc6LQ89OlDRIX&N;!)R<{_(AX454X$=~3|6J#wG zDPmoW{6fB7)rOz#R+PXMU8BU2<4&Di|e>(LsT$!j_t+JA-W?umaOMQgN|$h^BTB$ z(&1pVlkj1ejEblG17sA?_t7bx@;~aph2}h)A^+$4o{)4LZAPS}G$*$_2*V*N2U~4*1%4Es(}k3idIY$z z!US|0EyXUKiT?nT43v@wjj2BY&up|^E#kd)e4m`HPAICUxO`dQ z*kPo&0Nl=Ub%cIj*i6 z4}@*C>U9QG+Y1N=ErZUdYpSWQejstXE-aIq9dJPGZmAJPV%nlbPhlxdK|cIOuqx`< z*ychj(uEK6?&HuzX=rIcY4X}aYCqf#b}=D39hF|N>AFgU}fx0T%Xq@>l{UJbfq&lOg4X=DORIjzYL#p+e_0HI^M%4 ztfVZl#FmCuOs(V=qm3ZzlI_I0HNAmBB%T}*^MYT?Z!mXKQPdNm$+ez zddYqP5!$S$#4>h=9>+qQeT6(l0oJ;JdUjzYotZIZgN%cbEgW|nLxMae(yYQ<0cZMg z1G$v=iwI(&QFD0WJ4#SY4M{Yr8U+2+t|4}^TH-zA;|WK7Z5wDmDQ^-KPl0J;EWXS| z`c3)j z3+dh#*iPqr!l}9zI+LqExM8i@rie~ko?`0|w^3{cE^Q563W*f^@dH-Ja?8X_nYVXr zmX^$mn{rumC9*XDe$j~|7!Y*i6=!B|2ATo4UfCst!}N9pUM`of>Vgs27=4ko6Vu*rBU5Ve=%`npSMuN5S6;> zhbXa~b=usTlH-g8d2NGFCFGE4+zGBNtz~1bC?4)D-!dq?xxHvwoT(}Z$!)ri>KL-O zcF*?c1ngh&)MvXARD}Ms2&#IGDNPUEgJXG9h{uU9AepqZoYvNk^#Dyj3^G}SS1`h& zD@jm6k_WT`Nc%A*yP&ehrvOCTS=Lk@1-{ZksrL%tr6=MxHkXL49^Yehc~4zY6C;fYr^|ScyozEeUaKa(PEgNipE?q!62=HyE< z59gIFrj!TzfRli(Zm?PrT{^WEJFgn|YRFu1UwLs6+ff6!XPya^@|xp8@w1KLsjvCRZopYht6)Q8Pr~En$JO7R zPI&KB6mXc zkYw@o$zD0r7`F;$x1Vhk?de+K($BZxh}IKaSXx%iu)`XsZ%Aq*{mTJws-rHbpu<)~ zrQty(Vhf89RL0h1C<$_IMJJ*fODR8Vgy85qmEb`qX$h;j+F4qW8pd>vi+RGY`m`7+ z*l&v)8>tEI-=rFr?eSOx;96P*L$mW&1S=3;BvQKA+?jK^xS&%bBVPhg6Hob|Vp&QE z0!~6#_~+DhDQd4_Clki{K)j8ZRkl-wK3sJ;vS1pVY;w>LXcTCkgYRoOlRnT|c&u7RzDGLi8WD1Sx)x!e{|RCXJ}^lgE3N_4+4 zHlEhd38}zS?n`axgKca#DX6N}&$Yw}`T4FYof4E#*YU(JcSP~pV;vZx(>qQP0Wh7A zr1dHbp~G%&Gmz+D#S3w-7M2247S7EMT9%S)_E6%#=4SY?kCl&*Glrm;ux8 zIFU$nvLyTg#>%mQNjH@f>;Qf>!F@yw?p*RWM$_5tC7 zIuJU0@I+;Ck$ktbD(@geXn%#UvXS{oN^<*#9-QBZ{{WWviEi>l{Z(o^5x^hzYA5(% zj~Y+Jo!E*(Tik=V+$!>2O24v}lyaszHYHKKkP?@?_iM^DkfMra14LoZ7Cc8J1^#B< z4-W*AQ`%H%PvM5f!tg>mAAaq;R-J2_+h_)gO5CA1a6QHkPGNDq!E*{ySh9I2BxwPpQ|_h)cS03}X(r{l zUd@;#q@JLmIsQ0ul|me1gjq6oaZv@OBQC^&+3YxQ*5Dw@8`VlQlo8&Q6r_EIryMhy z17hIj@Msd`^w4$(LF?|vB1fpnnJWIhMcaTmxM&4hy6OjY?RQP;d{{ZN4 zl^w{>v{T|bd%JPN3}Az*YlsB~OP$D|`*7}da4i>Kh#WVl&Z)gUxPMq$pHth8>m5_- zdvV=iDh`c#>iaR>K@IDU>Il)4E23%k;&5=%Ri!lRh?CHkP(ZqqT30+gg~p5-vTA#M zxZ+7gAZ&2J&Kz;}I;S6Fjs!}Lb3X5WICOyus&mZahM#NvmHRQr5enK)T2H?$eH47< zLr*W;_+dq%l_G;IO5`Ugr7{K)Q{jdU-&5DBTSI71lfoNSMXWnX4E zR2!h~z=Pq$&>#a)am3yrfRWJShXy|kAfWB6lbO%65j$j-!wGZn$4Ud!6k2dp}K=xuMRB^orb~~Az29T9k!_yti&Y_~? zw%`>QkzHdYZ7`#jqi?F;Nc+nE);R7{P}%LsW2!%*!89Yg9Z{4t&}yu*{J2`3 zLtv0V)||!>-Jq!f=}+_i2OUwg2B7X3N{%(qn%4?HrhTC`tv<|g-6n73{{UkMB%wn9 z0nk$%k{#1F2tA3X;e{hL(lON=(>DN*%5f)9RH%FsquGeXHuC+rN*rzF3U#gqOp}oK zAtSQ|5k*lONFDg&fzXX7uUrVi;I%fxV=9Pkf@+4Fr5$w-b|4&X1u_F1bpcR< zK|KiQF&wTAiqzc*(ra4bvyx~<7T91Sy}9>dE?}-k44NmP>x3lc2MttLSJ{HR2J#yr zAvvC>1C3ySfIpz}Y0LSs8Nis~P-VW{G@KL)kUQ{#q$o5gQceVFtal&_to$@MDx<-fY^h1?9n`~I zs4Hilf34bsJcYn)pj4{KdRbTg{{TlH(~Ta=XLpsYPAFMk^5!>O)$sxDHO7l4;AJM> zzcBX)Y(aN#?WGjduH69rh9hSf88oKNrKO;Cro34S4#IGp&V)_^lWd&5y|{`KsU;Ys z+eCI|F*}x{Ty;Lsp6o;-Ml=uQ{y2}Q7H{N#XYs)uP--(@C)jY+Dh%W?<&SO}PtpU@ zuJ6YY^$zHo`-T&!cSO`A4ucWYNJgQQ##zsx9dO5S62`??YeQ{-bkwWa{a78`XrHA+ zwLNQz^h6Hqm}WJiQq~WYpbzFX7=`6Qc#9law^3;p zCx?kd06m}Ah@M(&K+ltFQ$@p#7!v8J8Pt*Z3`VCW#CFmNszTRBoG;(uV_gH(lB0${T;mN9G9>!5^i>uerM2d;k+VY}F@9R_fGq$x6cXEx<)~ z?AM5GyG`};R$(|2uk4ZhX^y#>X);*h-F^YYY`xI=1#!b^{&hM9KI-BL?$(l`2(hzW zZm&S#*^`@2xmPIYDHT$4T|b3zD&8XAilMREd3dvUc1vS!kKFGg$`qslkePS*y-X5wtb1LfQ0T=aA+aHJowQvtP*{Af=L7^!fmA-HYGz9u%Kd30OEt3S55 zZ%X`@vN`42hKeEPH=EH>sthP_l14!+wp3~F2c{!?Y@fvc0BGg8)4%}lUC$+I@@|fx z{{Yys(IkJ&P9C<~3yb}uK7R9hAjEmw{^dm^T%;i65|&*+Bi^8#FUw*{r;q~+pTf}7dsZKAb_hJ{iSby3R`fblJ z;&|TR=q3BiabJmARzdsKg#BJCgh*qx&Hn&#t9)kCTm{XFvR?QzVVU97{amq~lyN;B6N%I3~!1CU*W$>Yk zZL=SVj)?mwj#1@=GQ@=r#X3|;KkZ<5otNZDv8)b6ZH=7`2`RQgWeNCo7U};0Na2x| zT(#7CwP9!>T&^2J10@W}gu>E%AU2hM0fKDW$&=QiT)Sc`N)7m%($JR*1I#$mYf_dK zQULF!CR=^xyhy%s(5lxX**w%XrW9GB%%@NB!hV@}N1tq1i@BS*0_Bk?QRt==v#z~6 zaTUxp2T24%>ypqgiQBnr21ed-O^ndAiVFMn3@gRAL7jJ63%1l;R_3WTua^ffpEkMs zurE$4fhv`Filys{9MJ8sgecJ~LctUrrnq95icx$Sqq8J5piu$Ep+6M}C+xyQfcgd1 zcR@njdLfR}f8IRmv?O+C*AJkHP;bfK+gXG-6?tI!V1+i?ik02e3>6Gn%Yfdj7l|L- zIa_2$O4P;PV%`}BQYe${s_{Loo^pDTOEM^q?!3O;N^SAqTSU{$TfrypPAsi#Yl|!H zZf>In>&TahZWkI_T5Bo=c-7O}gJZVE15a_427}wS=OIT@(?eRRErIePTbX)vHo##{YBUNE{abOM~#g8u-Tq2H5W;uXJ2x+ci~07YIdfu=T|&Ww_YyOqfb=%E>c zE1r3}anE$UE(m|fT2F|iiw4L;WZNzB?lL-`K1pdOxY4|^-#3vji$oByvDXDw1dGMm z%C#obY>R3gstlp!kUzBUtSqc4abWYxl5|K0M(NyFB$Uaz%2`nO5~U#@U_}NbwTlV^ z&n|C(hSi?$a8iRY-H@_+lsLcO4kT*a2F2Zy98oyic1&x`RlCS+{{X&prKEx0b&-Jn zcG=4g?6`^}kKn-t+qUP29ZmdE(4{)8ztqF!pH-p?wSgG%VOLJUghER)?+y-%6k)o7 z+nsR=xqZJREzXozmPrmyP(Yo#H$>uBBIPB}PIOw5kbTLfEUe+#^3;NPcO}4V%6fE= zN|Lvhu}rlr*X*tdinO%Qfy(3Y6ryZh+bTP9Vp!$LVd@k*BfU)i%tt(%RiYTJP4OLY zcz=D%NO4w`#BceZT3H`xQLZ6-gc8tvFx;59-wR7`#dX{^`*KM5S0EE#a!v!5?EDcV zZ~@O9Y%6gex$mgi48tf>r1oS5aSPlpOMqg+w?G#x{7G+5>B^mcT6%_=4TX1RqMSq4 zzTc4?ajnyUlrOs`DuONh6>bqqDnqFsfYpai7ip}CW7CiJgB|xWC{k^^ZruqZ;iyuU zKF|gOmMwL1WS=wOkx4yc{Y33vjbdy$yWfm%TjBmec4Jm{EixlVWu-%HE-TJdwE_VO z%SbpoZrD~+X-HhX=2-BnNT+*a>v=bRu5()((*FRQ?YSvj#wP9tk)z)m} z*_Pq)Op3>GPU5PFuKxhl`;^(#rFv?y$I^3cTb5ZP+2MzXzx8V#7Y_Yq@A^YX`n~Tq znnU;ROLn<&S3*}4b-EQ#bCEjYPyJ5M{{Xxv{buL-32%Z+b)Sp*W7Fp=?RXoQ*rrm8x3T{h+6Ex>7VL3GN9_35@2zWo9V?BJX&3YG^pH20=cH;+CwV$RoQjFo{ZpZzj?d8?auUHCqL+I;l$^(TBn=gC(D7c!tK& zn((8t8*B<^Qh%!x+k&p3w|5A5BNF!Hcxg(d%}P=EXbn%}g1IgEi5#~#phc&4$~9_f z_jF{Tr2Y7kDg=+H5tDk_!9G%LP8{eBbo)E;)ki2#%tn=U+lm3SS&&>AIjLEb?IRsg zIc6d`ceJp-Evr)~N^?;08h;!jVI4Vw9mdir^7f<@b}Te$Pxgi-oQ`pqh?_rh-P|Y; z?E^x(IiOS9)|izt7X~3byZiN8Vg^uhRaTUWj@(HHQbh?wO_LsKTP`_pX@sleXe&|s zC~&ZnL=7Ps)}y-6Ns_r5PE8DjaVW(m_yqix0=%@_3dMW`s+r*`NyyFB72?mBi(_pb z<5EcX;k{)+>dcrVTYT4u0y;AlN_CJ&+gwN2i8L)rf{HA!A!$>sr0F$MHJW|n5d6W; z$mWkWR5sAl48>)zJ5m&?`*l|lT)nRmd02f{Tf?aVl3dF#ErLhkffdRV%GzOdN!$+R zSdz6TA*Bg#{?(YUHNrw_!0HNcBGXb-5q+J4Jr874jn;} zGF_t$Z6QQ5;*thoT8aLgaHwlbzd8czJxAGG@y88Wx0NcS$y)v7 z;z&zfL>jZMTD26cn)Sq|P;WgmStOd1{Bh0DN+B`XRPC-J>iY!}+?C?{@!dnHCc60~ z(A1nvod)tCyN)SU6d-ruoal1`>-J$`F_Mi!>Pev#$oAl_qGw7qKt5Nu1JrL#1(l~Q zUuG0{Nd%lzX(p+vu-cu{VKvm&f`=_Z_hG~!sbniU zPn3t-8`fM$T$a-k|NWnD|ksoOo>Yd1slczmj zW)zBmjP+I$M+Pv{jEv1GUuHPtaN0r32^rOl>7ZOmQRmoGfsycxamLs~fl`4_5cgs6 zl?F;RH^WDpCqEDKcH^obU{}H@a;7>mq0|oHl@^A{NL6#DCszcakb_*tSZrqMa57t` zMAo>Is)o2AV9JhUQ@aVvtw#}8mOJ|UG4yXTN6va0Ew1eQ>4;XRIvMiG{Jnc|G9Adk zysu%v;u!Lew;fg?_m%8Fb`a_smZ|UVBMVWE*{_y+hB~5jk@uE+hB~Kok(~;{9n&=^ zReE7+Ijunuk_j}R%N#b^XcHVt3WQVzSM0)6N`s^&CbYvsLDH>N%9O*CI2{VhO+MU1 zbBQcCA2z(wntixz_>iFBbxEZ@`kXR^{Zt9_Zhxh4*+Qbibp%7^fRUem7pD>7guH;0 z*DN!naqy6mXa#WYO%8N1=4%4EW1LaU4-r%x)xk3>ME}t`K{ZM!s3^!1|3g@|S`tOUi(bBA*f<$Ci=>Mq|4X)FB%ARqAlq%A;iy zIsw6LkR%QeujQ^HcTi+)v+XCl5e;bH%l6}rGoWLHG3EaNYaH3h8S+PQ#|Yz4Qi3{X zjvSB{k^H0Y!{o-OpJ){XVTVdJGeL!@-kLW7RPU!#h390vjN8j!cksezVn_M6b($OMq?Ao?G*>RbVu41-GRnX zFaZa}PrC@&EK^Js38<;**@)#LRE_i_yZGUqMh*xPO)~4-j^>fTzCowNFzQG%lS54_ z?i9y!uqrx(_UDGS-w=*ShLQ*X9-|F7`$)pnZz2pfGOvGJGE$*Ewr@{vBkGdWI+bbc zt{Ev*R1by@Hb=xU$l%Tf)}bcYAVXXX*x=k|)LcoYZWRc5;`-vCBauBm9W>WvzJ(t8 zPKeT z)^9E88j5IWl#dM$Xv60(A^^ugIKMcdRi=$PXNam7Kzt`N2B0UFJO2O-FEcn3$UWFp zkuON!<^KS%F#f6CGzPY*Ps@br3aua^9VGP65!C40qXIFMPD340&H$){uu8J_;A)Ll z&^TcT3Gn#4aL%PiMhPD%0zJK$=(?RZpi-EcQJ~}qhL+;OQi4jlWN_KZCUhY9A;%AG z%XEb}pD7ttM=D@Txv@o+jlN7=MW)*i#PZ9^PgMR|j0Y?CCBQN^E!*N{%bRVD5!nr( zbfzGNEncH;XD(VKCc7>72qksZ(H`s>9C*YYc~X#KEv{VUfgPfIR8LHFuf_--81tNm zQyWo9qNJ-)G}MvP74zIYi@Q5!nv2M`K!=*bQ$bw%j6m;PkaX=@f(uEy!(?b_8rR+i zBKc`}fjqeI5uA}WT9xGmA+Fr1Tu%s4WbW62qAe;}3U;ekdCr(A-&6b6MWk%~FfN8^cVbR7Xw)2^$+jY>Un+6!*A7Gc=rI-sXi8bSL`2fdNYz)bwJ!LlQZrf-w2t4m0Z%6LdMXn~7TQ#9qKac|SfOE- zmodtCRHs!#+ujBdEQjnRh1*a*P4jE8Mvbo1iRt=_Dm{B^gqAhe{lY8OpZ0(`e}@^+ z?Y_?mlmbed^7Zx4w-9`sm&G8jnO+eUTHhB{r~DKc?5Jc^+Y|%eL5TiB+v6&W^Vd9s z-{(#6;sP31luu0zFG(KyVLwjA5#PVyU|)Ch!$Q{csj2Y*P?~+9VQyn%!^n|e$bMml zle=XATu~MHl3XDE7=>N1j#0=IMWb;#l%d&(Y#j8JpTvwe=PdaIEL+mz=n9>?G@ysB zOrhHRI@C|`z~4C!^pY$KpXxGp{mlbNxctISL^k0_{7xjEWETjK{G`9s0NtKzP6e&h zCC1N$mx(!l95LQaQi7_*@iRI-$k3uf(4>zFnpT|sxR%5hiV{t}3_zC|=~P72zbQm` z%AM=?9I$n}kV`~Y+_fojVOaZN`75jje1|``A!dUOc~M+ZBgcMX%pNe@*B2`F(Uwv( z1x~o5PQ3*&9o^b6(IQ{c+_wbO_C0i%7aohN#Y(;Pf zuhMQR6xM{RNGGAF7!LmNb@))8*4-Gf+TPCDkdgxJUoZ#eQi`j99&PD(g?y^=6j)WB z8PRI8qB`3i2L%BiX~SESdYx@@io*W@xdRnu%w9;K3iASnS$i<*vJRYTB}?4bMv_qL zD7MY)O6f!P*AjHc47r=fbF%l2KwEtemX{Pih9-_Zv9PxM2KC2!?n;j-=?0@9mRFy% zrU-?rUL?_!k{VO5JErc+*-UwIkbk~sLh0R|P9(PNMT1Gp$ZCI*_gu546`5?Md8KP1 zAbSZXEEM+}5kP+{a!eJzAKWr%o0RBKNzX}m9^YWY7XJVUtq{MZw)J6_cy)$UrtUkk zD@k8fHVW7LK)~|uON~)PGCM`OYQyJ`IO^09`<~^KGfz;`5S;$wOf~Nrb6P4A$8N@6 zH4m;g5L;NJ8yu^n`4U#@(^L1;1J^E3i1;jc_ytwCHk)2jt=m}QfpA);(L=S2H|*x0VDg(Pgb8`7;ow^@ZvRNF-03wVZdyr%{ z+m9#BDeNB+!ov>Z;>5X^81O=5-b36)2~25pt4a4$VYItSjmVp!zb9XQ-*a6{p3 z=Ict7^D*MKk>d}_ZM?tP)l5M1Vu)3z!LWGzRbpTluUqZ~x$psYBMLH&JAv@$_F@mz zWcVUkSxed*i)G@AOOH1y>2bd)IReuuVZOcfsl+b**T9k%sHkeY&c(L6MWbVx$*1YN zE?bKVKkDHX%id(2ID!sIy2l<>qi)-+1bhl^DhULCt%#O8dbZA_SAY||k;)Siw*LTg zaV@N7hGT?O9J0`yjwX$?#&jWC&`Vrh{dyrPYU6N2lr9B%QEu zzEu{hUz9r4=TM)&I^s^OBj7bl+ATH1mjHyW<9mSsS2UHC8h#iX=OV{yD#qh2p995n z-l=d7tu)#@J+)E9e>B<3Gw?#Ovhd_D`@oTSUt;wwBmRj_7v^e&b}v698Dnzs)vOfS zwo?U289eesiaz~yz|zUkbauOveyJXEzP?+Poz)l9lZ@J1wl{B=dI1PXNIKYPR)7xd zXuCe;Zb%P(z)-N2YpKNk5sb`vH?i{5WPHPIYHk52boh=+rgYC2{lyoh9SHVrSgIC= zHr{pi8g-qOcaWj3k{)?rASbkg*BKSf=3I3mybbLec4O_j^j(@jlnGnU&!^yPf@5v_ zMn@qbgSjKmg&og2#~KHap{V@GaY_MHu37ZOrrReOg5+{I)bvI4GJeat_-CA~_ZW@* z*4o`s-f7BdSCeqHwB-S(O--v5+wMGF+Oh|gcHCDl26s&s4Y0~lR!}L}4KYs}c99Fr z%9GF$G4`ZmUzcjW;BhR~p&HDQx~}_qjVWp~Vx0zoi4>r?2)68*ac0WhkWvql(ozxb z7;?K`0jp-0iweEFkC#@olCFsruiuVRl|+u)1oac%-7*r8P}Sz&_kx*C0#%oRyv=*DC9whua{C}DD)am~B!Q$;w+`n*8m7m1 zkOf>*X;*8GP=6eEGCF`xZKjw}L-!_|QobM+sY3}FRh>Y9x5bP|OxGk(_)Bfjej@~R zNcush;H}^S6!H&gRXSi-b&Jv#V_Ms7DNE885`HAsqwd6Wx|mY5!v+Pll@sO2c{vc2 zk^#hXyP-5}&^p+(p|X_OURuxQTGc{6!-#4>q+z*kLnU!-v7-WNLWh{I`du)U7oxyi zzg|j|p*b%rBTCLWA`VsOA?+@?*^Za2%-h$ zLw;j7L#VpXmF-%RJ2Alu5%L(fFEs{^O=$r=KsvCo&})iXp@%+WNY;LAx$ge}mlE{` zJ#`xMF0J)QE3a=%CscP$4Oy3psDv^r-CoQuR9>1J6K*R~ln{`dvp7zv-841iN_M8S zKV}tOJEkJPA=Mh^Q|!wVe8fidgts16dcMp+?s*RBppw?5*E&}n)jOnu?jYoKRCZ!{ zw0B4ZG*Q&`#8I6@=@=m%DxdYR>YdXIxa&izv%*4kA$HOUIVB);sXaR4XK|g#7caO$ zr4!sn8Ps74+6t6cs)@pNnBI}8YuoR_b%b`<2A}Ppy9w17ri>H=)RT#A+|Xp;kEw_C zNV;Zp`*8Y+$ZLfeLr~f8!%`)n5ki6Kht!3}hH%=Fh5Wy7;fIhkedYUq3~-bJ!#aps z25|8xeU3O4DX6K);oU)s(hMP^*gx7hRdJotM*dQ|Wr?ah+%i3<0!nlkt%8Pqntt3$ zt}%HLpDg!b-BG=t!ZeJqqL8PO zuHlcN>U{Ue{EYd2+$|0_%L&eo0Z`LS>s&XP9DJ1Ou5_jrGN{SIrnL6qMCqU(D0ktV zQF>_2KlNXZ6R0OTHPWu^cTkdsr37c!*@+=G4Apcspu;+({UC5Zu}nldhH2~=T7sK< zF#e&&aUf7CboO=a!)Ek2-kDavW+4<6r2q&x>WI)j6k+x_Q+-{Zm%AKwO^~x%9P7~G z2PO-S3`fTtb3Pajaj6}s)HZVjVMm&j0WM?`3GT-VZQbm{x!9CGKHM)mJC>#b zK`vMihClxRl`bQYk< zNhXCp>_aJ&Mj?>V_hE`jl5#UXSGNw(PDU6)HCCpS?84MX25h8P;Y~a7#Bq_ZZ9Tc- zPN_dk6UsiyVjgq}$m$Fl?>L93!>Vi&27NH9Y=@BFrW+|#k};vWQch%eaE?q~R1B1A z=ziQbrzjxE_(!`9Y>>)6A&4qCG?SJd?h-+0XAxA8`v`EY1pus6>cWI0&p+2Oojdc> znuz}L&$wycK$jaDt(lpP>1uF^hFfX%IORe^ZUud)BNurO(9G64eM8;G+l*V;n=uIo z%l)3zQC`pR4P(zUJ~Pv;`~sp|HPSlwWr+D#5vV@XeFZb`?8D?JX)KK+v%Fyrstjp8 z*x@K|<$FoTN`7Z(XUY}ma5pxh(BH`Re;g^vA9*9RW%l6?N5ml{X`%LEYIQ)JQmC4C zR~)55@l^|Gadb*SQT7Z#=M*!NOj3_wpCE&y%R&6=?8FnyOMpeZDL1ISA8kX9l$lN! zmD-+(t_PlEp90Rt-x%>>-C1@j ziM8#~KvTc2@_MOV0sC=Zb1rh=Sy;e=T8EbW7;V!e6m&vIU%L#BJ;`w-c^2ZRgqQB_ ztbc_@l=P_|*Mo7e)OdqWapurWOEM&=>seVne%w!XDIiO7n|g||HtLo^bQFP}c&@vb z7?u{c{$aw+r3j@*fOg;omsT4LIv+b4GefOUV8Yf+hIB$&n)3k&MFHq=Y5-cZD76(+ zQ<}A62X|1_Ye8l^OtJz(0H@}|WhOmapli?kp!>4JD#y^>Evd?DSp&59;9^HQhvP}VvOqMzzHQd8V2j|X{`BErG9`S3$Z(GPrdh-a> zIJe#h;_+s28n8pxs7Z^3p$byW2UJICSi;K|erH*@yhUjDt3`Dp)+#Be6OsF{E5W!6HKJGYSNxjBOs>;#Fs2aTo5yymbCy}g%TC0 z>+4-=KhuO(A=#~|mXs;`e1~$)N*NEh zj8b2{u7TDpy6T6dD>c1d7H!V7MoLi%O;5a>R=cugg0T^1JQo;U7-Ld@w4gtZ7p6@m zK|r|VL9NE4pbnsvsQsNW5=S`+PG2_r4lK&M5btDkwgo=a=5ad5lVH-wNGAU2vIMwe z$da_4pdBeS%d}!h;xX?j2!H2Iy2_9EMY!cD<`+l@?l_rk^9Ffd!J9vHd9qaOdz6^b z>)~aksOTNWq}K*~qf1lcvERn=*tPvM7tooC1!uP_n1H)7g#3zXxHIbAPRMRcYmjs`q+WS=v2@CrtWxvmKY z5?+u^YgH?^{cI)c*wI?hTXu3-rw?IosY(iQrrB)t^B<@5;<9_SrNIZ=+pfMNo;McB z1S;pa*@~h~TS!xf?-&ky-FPB+ZS>;B);2E9kEiD^PRejs%tL>qO+Ld+a@?-I2$SUP z_=lT`#`fi>MX7Su-$xyMs2cZ-u#&xy#f^(dRz~^Tw&bt&>njLTSpbxRa`w{=`Fqmh z$v-U#uX5$lYb{&t?WFZUp(pN6EFpy&YQbfYmwC5G(8;VIhlR zDb$JIm2^NUwQ?op%BD2Z;(?;vaY!GD7>B803pa6!EOCb-$zG|cZ4Id+s&-F^A8@W5 zuG)zYQLuBw)8hq5y6%{e&?~7L^;0hMOcfmXt_q}U5zC8VDEnh!LnPeyug@(f^&4dr z9pnR^A-$Dz!46n3Tv%ZeIF17K{Iu(pE8<#)pMG@2F~5@E0o^NNxBOPymc|hh$+cN^ zhDU_F5=LkHDXtCa-Io?aHw3g1rgJ9beP{O8V_I3yN*iG${p!Tf+kDt8Z+iyQ``3D1 zR8sZcOS-?@hX@&tNUJ^fPTq15k0%tL^~cUaCURUIT%?Dz+eh<2!``NuWMnoPg}9BN05L5Usc19dBy! zov4J#n&jsiP$ebtOtS;SmJ#Gj!3AuEBJwjJ5S7VgcpJm zS9;#}W$A28kXKq12M9IKuwr#(c#l+G0k$?*AY4dMw>mwptT)&Faxmv^35@xOuMt_k zFWtGB)1kv1?nzExa5zSzQYV$Jl@waz%WQCybl$L-Kg!Tkr2XVzX5uT!xb)=~7_srB z6(mW!&AGXiLqRRJe*^q*$xhN*om{5$qCDySduk8s7Q6Cx)3R6yk#$f?sNTBi8o?Df((ewD;Z|2| zitGB3)wk9qkLOp_UZ3@Xs^CZVAJ@fz!3EbA}b6y8vUFEX8PejPG$F7rcbs=0_1%adh zh^SY6RHI+ClYsLjky>b&OtO_E9VscO#Bh-etxv>l%-qV0sV)?@;(jLbA9Zm0 zg~t_mjhXw3-y_E4h3g+0LzMm)bYUjRdTDSP@^?L-#NYSJN$~j6P9C7pjsPs*Es~Su zu%L(4*swsU=}fT>qQ*1=a($4kapv5dN~uDI(4q(1(;ZegC-XaPrO}kXEh!l(L?*qy z{5mpdY@<_UUf_g}lz`gxMi(-=DQrwc#R0ROclTO?vPC>RT@D)Jh^%6chX-Ku}0q${lG_Ty;kT%l3!024K3kDUb*!_~WY16CH9?a-}nBLm(WK z=|VF0gN}l2jGVL(=U$&eSLe-(6-TNPQmRu0s5C869Nl*lfse1W*l(JmbMU8q}L;vRdA;bS;eZHz``6frj%6phFz7y1dVA+ zYPpe4oqAycM)a91q^SVaM+1Y1(j+&S3F}_m5D`HI*FP~GaNs#aPjj>F!ZJXj-b!jU z)mTALUD*`N;-(aq90H4V#Se?8+x&3^rr7!bm9Bv5_`QR(5{MS-*9i^;48Z`39+-Vb zO=to^A7^e5aqtA@1{uzUp{W%V!^X8!8WY`)7JwgSCh8(O0_i=&3DrBMYEzJ)&$kn+ zwIZRRNgZ;-YCDj{J-Arhq1=WkiF#u6%#Z~#>+HmFl?KUB(MbxYWicmEXDN>-`&f#j z`e157O*5@lw4xy5o8fI{{I&nx4MnOIJ zaUDY`YcMD&jyN5-;)JN?KFo7uHkPayHi6lW8$j4fP(RX`o^L`pMW${Dr&^o|&KW~r zJSQ%+?ZHaoAW&@6<`vz9>W=A<@vSoJkD%&!0g4g@diLRJHc_dKCmm6ojMSxd%Q4-K zsoaeF%Ol}5@5fZ`Mr5H!sjd)|56V^+qA$bIE29aFky!J*o4okVv`$CiJW z_+beQmg|J)LO9gNlzZ^{k#aLS6WxUxeW5+rQNa>W)@{gmX|j{n&kCGm)G59myDrsQei2k-8XObqDSk zV;>k$7v)#ne(Vg`^Kl7d@{#souOJ~=s5i^aV;MG7+>N%sBNZ3qp#fLM;yY?hR&_EQqZH4qRtLOcH4C0PELI$G~IZAWy$G$;8+5~P(r z(2-Nu--!B%bA=I~vmMkML+`U4)H;CaMv+?9y_kVYq^=YO#}gz7gn}~_$2l{o3xpKK zaUW3X1AqrXg;x%sv1)O&ypZ6aBx*gw-Hw_%E{B*utc};2FFv96!yEqqVA?ju%G;2x z3~2WNvR{`qwMT3){{WbfJfT_+(~GU9a*LuXH$}}ukxjREazYtRt~-l* ziDe3upcb_qDM_L`Ndp%XzWH@3tk|ULdza$dhVH%bcOP7L=EP4LZgXh2w{+Q!-C4b& zDw&FgflS5_+FT0>3iv@4p~g<;tk&9Ik>->d_(7#}I1;3SgGoJmP7p-*jFc)D z5nXVUr8>UMM>~orjyBt3#9h3SLy{J0*nx`6neMbfkic~pQ4TC%JeE>|I-aNT#17=8 z#IcNH1P1J;o2?G4Iat?Gk0;1RlKhvA8wV_i_u%PUj8GcAIRiwoTqaZWkG%f?%YkQ+ z2%QXTlN}ItjC2sWiz_{J1DD}|F77YDxX0U4;yM|NZ?~uP(3a9VlS-ez5z1F4$&{^I zUeJuY!)+*rPO@uH{TPjwtrnanJ%%5E0a#zm6+@X5@<~ z|?VXG2yAeKy50S;ZzxM6{l_nZm2JofMvovaV(yPRQt@)QBY}y3Cw`Y z>Iv{GKH@RNT~ku($ON2llDVO(RHA59NIjLu9Sg3eDwS9A{y1!9QH%}9?W%)U#h<$b zE2LdBLlqiz0)5!-s82`>MxSMI-BY??ernPAt6VQoj2*j!$jDIRxofEeBKO6*3$>{w#B|72puA&4*%# zTv#v3(W<(Ni6a!3;=Bo=%=nHpGYDy{%|U%aMQMTD$e$D3EP#^T`nzYAI7)| zVr4h1g?Tn3{eqi&#w;R4UAmMMcBHqN@62?=Ufpr=km+T?@Q7&gRplwHTOPEIib|5B z_W^=qwZ$)pNoLo;&~Lr#Eon?Wza`HzB10+z?>S?hBrW)evLz;J?XQ$?sZ!gD<+()o zRO{g>0B4vOSmoM}w2tRAg===g$>NC`m17dHvlLN{WfbiJ3dtu;)Qrc;1? zPP}qHF4ZL$asX(Qk& zr7*P+SdTU9Z4{TT}f6eKFK2pl?Ao;76&;~j;b;uI*c@8vb;&Ejo{{WT| zhP7@2(qfz~u_bN;HscoDliK8jbpHTixNpp+#Ev^1u{P$>Z3$J!e8(To(wniW*Lq|1Rw@c@?FNv0xMGkPRQv7tcfckdS3!1;Z&sf8o-)Z)U4 z_veIiy8K6SK_f?b^3e;yYT8kw5lT6rAH)HM^rhxEmLfE-P&=A%0CGT$zfxc=F@+E5 zp|s^pvZ%vWI`Lxip%`!8NWRq!Jtj(A8F_7*{orBswsKPy;qC5By!n1}53tLfR_mod zfxzx?TnEx@ecE`M;PX|zUVMTEqT6ZAXPrsIo=TF?nl`D#KASfb+4ZL5e_Xd(BshTL zNhD-PfD?$7`6H)`Ao_P#7F#2!_#1rA4Gfl~>L=`Q;Z{t%gm^OZK^OV111R$CyK$IE zPeQb$`=}Bz&U0jM4uUfIk8;W-F@R|bsY)7cAc6N`$;zBbZDYs91^)oeTkhS6!~%psMR5pBU5OKA#nP*W?{Z6nsv^cchrM;gQ@d?(87{x}qt36_o&CHfdU{UVepAJ1 zysBrPrk)bW1W;wv<7wFxZM0N(46bw%mlK719`(0jza#v?G($m1^6J{$)IqH%oN70H zzV+XLr`vZ6R#&K-<8qfCLWazCpIe2JN&+LOb^EcLeYKMKm!JDSB>|Y`Euj&;5n7b? zh53L6z#5K5l{k9sJb+*QlNoRlZv4M(pKYIPi)VPMcsiswq=K5CWpQPP+Ovezr1%xt z{mU7{l_Rd7LtWVOT>k*d);RB_m0tF%PL@nayry= zA7&pJR9XbvO`)2sSdx%VOL26s@x$WXbPz8h;}x?=R>mWN+mSdSH{v2d%T?>>zj4is3qF?q+=0WhzKIohPWN%kIR2 z3az>sA#6khr}@dJ-H4?+ii54*E=!^+hLV~M2tWi6eK8xL{%HfCTx10l`O80*Wa$FE z*c6yohRoz8U@0suADp~1KH8iiBvL`2m?;3txirZ?l`ASZfm>{QxQq*(nVPCa%}yj_ zl7{N4d(J2MSqIHWyhHD+&X!=XWU1yNl>Xg`K0>8~O+azk2H;gt5G z?3KCRNjOCs9nDd-GVrsjI*#7_O~^^g#>AVcCr*WDN@594f+#T_Oy?Y^N>8^Dbwlmj z5T!ct2ETSYs7cd9YEs=+RU*FH(+MdyEMk)zlACam`hXtl=ZM;5qjF<(1!;)-jBg?YqPwt9P*5Ei9ZAe}!2LnMWJ z9C5fxiRn*nCXA}Y2l9Wj_~J?-C~B6gU(Jq4<7_U2B^sOPJ=js5BTOL`8LoI0vD7}# z2y!>_*ZijoP@rEVe=)}#7zj`uFozrYf7<*8I8$v2?#Bpyqz{L}drmmuO(did*S8!I z(?H=RMu1e~h$s)CN%&1`*QOGo20|54rhxl!oal#Ih&q59R(iLGgoTffUNV9_&Ea#To`Yzi1ed+#SjpL~5)0@SRds zXgZj62^|mNi05-au8?Eoll-Ibz&&O~&<0Ed>G*0fG_KJKvHhUJ3Qtti+lannz<0}n z2&n8MEH&S0j#eUlt3^rGyhsMO1b*;TM+{0#vwB`J91J8h6y`pc#JdLjK&DW6H zPj6Fz9^$+ZWOk;OtPT5IDC0RrYwiM{*MqEON=OqY1`zj%t~P|4C#7j$W+!uOujEE_ z1MnIihueti677L4vz0}Au&7Q?GjQ6eQ%`moIZ*CUJTsk~4A*WE&5D6=!ROfOoDK-c z1CBV)O!3DbV~zyu!$J_T0V(qWi3H>da8qK!5vk|z>v)!q{{Zy*gaDPdW4=^-=STjW zTkT$DR`DJO5;n_v_decjS)o&*2R+79b;aG^g_L%FGu^79p{rf6BP9H^PzSTfPBC$v zTQ%@WL`_O{8Vs=E%sSuZ!015QJ|BK0=VAtsfM}}w#uu3cE&z?AqJJDh$e3v;r$BSV zLQptteq;7xKA@EZOLWX)8igbrLiAFJ%pSOkO&lo+YVB^HZW!$)sW)3dC8@`|Xr7s2 z5}?;|rtLyvy3|Sb9PruAp<`txEXQIaX;0~(XI%;RuJ@l*2-aty9;#1G_DV zvQ$_L*GHThS@8D{W+k_c6gK8uniIn^q^mtkJu)VkH8@5fT(Xd$&4W<}L%Ij@iW7mS zm=|jfy0OC&+iZ$FEd?vFO3uGwz*n*)-hsIGZLT5nHHHl;5-H&WyAVdhmk`I2qrgml z_B>u+i1(hjmKafdO8eW3Tm&~KKJu!!6hS$R6Vr@J-KnVleft;Hxl1R|Lj-_~5)g0!4zG1HXFzSJq5>0gsEdd#? zn3L65>M^I@Vn3T5L#PGk`v>2LI8b#AdH(D+f>6nNzYa&Y3qeu@^Au@bm}p2KQhYge z{Bg$yl2B7kI;qf{b7IgH3^1zIAOX8P@IBTgPAP>g@a^2LhZq}6+nYr!8qm(8)s(s9hg+U;DsfboJ_u@C^ETd*F_BN=M zz%G&T6*zPDb;3^$^6@+7Yzwh>4TvlTcJn^PD3knf+nasaJ!DB7+bcweTNlK>8x32d zribFtll*Wy#itfX@+-j(Xg7I{r9M@509+C!!8%Y|=~J%5*4ln3F7t-}2NCL+CR8YNLN z?Zm@KIxL|Hu1Qv-Psa|ofmv}Po^xIROx|P6C8gPc)I0?VQ9sj(ZsRB_)NC%fJy5q6 z&?O+a%u)%-Oeq8^opP=Lo?K$+7Yt`5z&^`u!y!Mj0HKnWMBt7;p=Qd%Q8%1T(9Qf^49@{$7M>WZ0aZNt}ZdGK_b5Fb=SGy;e3z|Q+02qPwCpHW)kyCGfbb4Vxsk%>BugvJpH zs`8dXgh)*=k=`|$f3;jPqq)!nxBH8!2~48eN5Y1`Pe1lR!bv7W)i7>XSK3Q%%yvq8 zr&`p1rvW%1j&=&uR~4sANK(_;M3av6p&v69;n**doT3+nMwfG$0t-b$ zw56{#S^Kb8{)Lk+GDsHZcjG5Tsa$tY{$P%5lw4zvxEE_|=E*)xnKEYF9s{t@A(Y_U zU#F4&`2t<0I!YQrxO#BVC@L5*F<|wrZXiK>~EikRg z=$#B8pRkdL+}KnWqHCM;aZ9R2(bjeU0J>|ylm7rW2&6WEZ4JkBI-71}>h_N@Rr~OY zLP=CtXK3!%d`h#t-Fm6Twt@EQRwH*RHCq;-?3vWpalApdBvPm@rE59Tlu5wy2!+=Z z&tKPfe7`QQOIFnDS#MSsIQ?^C|Dgr*;jdpv;$aU*?CE4?T`A zNC9f=)j0InMur0*6%RI_%nf^LF!l<=>`Lr8fzV=Ds_`8b#YD_XN(xV2qY=t9C6sD) ztLW)@nIF}%VcVppJe$7Ty0qC)$mmlpk@kU%A9&lPw9T(?HOoWQT{kFRcLWk>NgnTR zJnq?{q7|9>^`MCo#<5LGDmmfjQ3l#;LUk%q2YpRz_~LkQJ3!K1wzQC4aj>EB6rdA` zNIBSHi91xMRH+JS&*n-^GWQHk&c=btowY`g+Mkd|M1oS2?ZPR#kxFcAJoG}P{JprX zB%LK|C%4;zBFKXNbbxg;8K&#pD^Gq{mz_{Ubo}$mtB#b4cyu?}_hE|a38A{J4!A)| zU2$ix7lbSQj`u0uy& zLZQ$c@SM!xqMt@%O@D?vo<{{7bP_v5bsz+c`e8bv^v}Gn?PI#9bijPC?EW~I zi1;(#o;X8lALaZo?qiR%E4vLkfO>GXJD_WYAYhnjI39!i{{V&-qdI2#72S!_NEfD! zaNKF3tF}K2Yz%RFb9&EBQYha$_YLnyGrJl_~AV525f%j$mc)#|Ung^w&Dq z3sOFrwx4b~r*atqM9^c7^#)SFG$$~B40Q=PK?5DY94Ly~1^IR3sy~sMmVh0#!{a&v z+)u#M%RMMD9A#0irix{1reISObw_-$zm&2-0;Kk0JGpF@7M)MNP1gE@o(GPjr;Q|G zEmzrzBg{k5&=boa=y2^vD^7rbQwlv=RH3$m-Hs%ozm_}k!(|GXEuQ>yl!tOe#)W$^ z#C;?i@|8}wN|Mos5P(4b7)0ny;)5S7liqvqH04%4-MtL?DI%4~_EQaa5U$REVUO&L zIkE`bFs48mjJxoa7B=-h3<5hz>^SO#^vry{Gd%~p9aFkso>)oJ1#pAXp@0oC7>}qx zpxJwz5GB;-Ma0iIs$ESnW1D6`OIh&k9&LDVFgnq^E#aAl^z zqO0~`dz?vCx}bXEF?wgzC$gssDvVjMVQLvqF(cAtJaqIAO_w}MSgEA7P2 z;-KD}2SvU=Rhp6ZVzJyMglH(IH6hgXYaW5ngj_t{y%T`&hW>Q6Z*QO($ zOcw$1#+8*^VPn zH3XiA98bs(S9^!yj?irb4YXXK>b|tXF;MBV+njmSq#&r%pahfmVm_)RjkiFUi)6;cukG%SER;rz^hNrN_$kVbX> ztUBhTs4{_6_hK$|M(u`L6bC#*Rz4A+c`2y?cHy-o4J9EXFSiY;H%=hIv)hEK6(oU@ zlA}TP;u%V!f@vT2e~u#(puiK*Vd4TM6#&+lM#m%zjuhMlchHPBYe8zmO`#Izb}>w!&&EGPIx`!cY2i#9Xh;mJLw&%LAeOFi%Vw-JlBOIHCe;L)(XFnALTK zYJ2eUoXpuVbyu>O7Nngp+8Iay5lnSO>6ugECW5u>IO1A?Dc2R6as#e7 zamRBrS^~+fg4LV({@=qLL#oY!)_UdlVhGBFkdp&amO7tyCM5&buXovj zg+A7<{3*sYue?(UQ-i2+dFvVWVFCftr3&RtA;H#(%A>9Z@d%FrO_vfu1vMF0JSEN{ z7XA?Eb-DnQX&t@2n6J3qNsq zTT;8|ll>S+dMIC%Z-650E(HPQsWj*`nq#AqmjTNkK)UU62^vyB9kj^c#dR_%;~7n@ zz$mpS6Zv`phP;A5mj@P-bfr!xp23z9S7LI;6k|k8V^dWTN&|u2)H*oLI*)5^y{rYQ5vpD%eR=$Afecg3Up9fs!!jD zx@car0cQ0nC(2>B!aG|lQmUVAaI%78b(#%3J!Bm*!(m?%oFiA+ObOFO+|7b8+=+-^ z$*C@sin_v2j-Kk`exzx@DK^Y1*Dds`%wVk)(os;K8NgC@d8wn1*?>@FrNWrlBV-b{Ohg(yqvqKovlt2kyf4uCzKB z*1ST=x?AFZ-Q^TC4GU>VsrM2NAyBy@PDn&9t;Gm9N?wo_J`z==cjQJ1`BrySBQtp@ zu&Aao9xKSmRTiKbG;O+ zP=#r;N3AnlBTH%{rF4<}aMW6XjxCpIwyQ%G-5D1x$#S|JQ@CC(`-$I~zg^~DU0=kxlH|)#Bea<-S*|x0t=p!F z9({8v?R8-ihEqhPKxrQc0-z7G67t1xLtWWEA(o0Nni8UWbL;kFsz)f)QcBuu^&J5q z4Aw{=#|Z94aXDEW8+&ix_gsWV+S_j9x#chWwr#K_&QY&G1XkC8=egs-(o#q+J8ju5 zBH1wBeKqs$)!4JSGk9?B;MyAs+UpEW-1k@ufgLZ(UWQSoQA32Ri_+`YALlFJKyZnnW& z&ZS;jNuqTWf@m}C$FX(-rX%;5-tD8BIR5}}p9Kq2RP_oC2*;vBfq=inPW{#o<$tGU zf6;}KsQ@c?$f;IN8hfxh9pE2taKs7(Ekur^O0)biJh%x7(COdOTXlxLYVoU3m*mL0 z$GXB~xHP1x=w)NC`k2UfisrCvyKVZ9VTKmqLXxMXwwjF64@36j$&@uVtkguBqD#`! zE7a;E6CgS84XPk1shFIyprQUaCOR>fcpCXJ(7fd-#M3cKVTxpKG+?#2C{aOb_Zr}a z(g+7aZN`BtA9{`lyw-wphnjJO(@Qm=6hKW!OgX&0S7L)v5cyL`Yca&2+Om-EJxKlq$ax^K#B_Y;I(lAxKIUgoF}C_@;FIJ z1x5+@8ki2OB+!65p3Fezhjh?U^rfn6&)J4`Ms$S7KQf0->`BbU>4ewVnsn{NuHZ_x z5pt%XC#PHoJA(Ax0uLGr15i4Xfz`BnTbtOlb?PaE5z`$Hf2dj1gUh@-fA-rO%y zlJtc47PWv$HOmd>JEj+LRjyi<_hKh-NWy>x1oTk@yAsCZp#F_ORGJbih~;-vsZ9-m zNCK&;@2(rwI1^S?(VuoD8_JATAI*iR&Y7J_@BA?|XH&W?1eE^(E+dqxcSP4LH13g{ z5<-2BIQtxNw}(&mvBL7RTyTamI8J5YbrGgmeMk*_uW#Xp^+$A#^aUge(+-U4FHFaj zKbsQ9bQ7jv3JPJI#AfE1Vs~(FDlwzO)5C~|0u+lcyvHxZPwO=>>u6V=d-CK_#$ zJ3h=OQH#jK0U-9*+%eqjURDg-XQyXr!gI5^7$9%iPi`1bKwvs{ff$jE2beU`IBXv;@)ZKXx6}JCqwi{$JsVx~NS}G@s@CFh5jv8J{5k z059W*i9_!v`y&%|5g$k}8Lvc~cUbP3r;>Zl7p!+lk&cA-W9e$2J5UU?do#m1(cLx% zTBlUg9n^@tjKO4)QINvuCnEtZDEH%zVyG$A2b~?8NAXbEs4@M@0R-aU!?|qT4nRR1HQS z)g`YC-m`&bV_^hrk zZPcoSj-wE~fS!Ssw9;unJ-A3ovMIuVYe$R(idUu`%*l-TPJf}oRO%S=SM2-o)fCT^ zzh~cxqdJL0e<%A`hnV9YTD$P>qafsF-b#JAWm!45%O3OiVaFKxW8a3am=x*|zk9maIRhbqx7H^hG|*EN4-Zg-nLj zJ24AmqZ$K*c7gWer0O^@l&BDChR~io11TE18dDRgCU9UHDI?p8@P_pa>sNjU9vVtY zfzE@s3vucy45R=68PgFnft&5e90`u8#&sU^j;X*kTDwW65aZy(gwwOzi6cb8)kDnN zR7y(Ef|ys6uK<@}F=4B8CcrKz9X&9~WjCNzyl5q;ak-Hrqf2cjqv13)z_Pa~B1Li# z3CO*;a!No?c@!5Mkdk*)mX+ysL7_^Tkz6z9A>eYrmxvRV=9#aJO9Q{A8ytx@sXg3i#3H0LlTL(n z?ZE^f3s6-&u}XFS07H&P8dXa8MJd+`bpf-c1sg!1IG!@7<9$^4a;_e5u>vCBAOzJ$ zfg`CT_W^|BanAn$SD#aUAoRC!xbgPRvL~5&qTC8@mKLS_wT{N|Mx-}#nA%)Yoy-+5 z$SO!9QB6h+vA51{z5w^HsH27arS&THmCJ8TV&TjZc3$0*RpsqHsKP7|P}+&2ORVdb zvO83#^EuFT#k@AVT2)l5o^{U#RM+>$^1D7{UN;>3)V1Wf)8@*!KB3ly$<8{mGw;C= z#*8#b0`2L~;sgEKe4s<+4yPhgw$QA>t#eONf+uN7QH+Cebvl#6pI83?QoMNmw$IAe zt4}#>_^4%%GDAqR?iORVH2$2}Xe>HH00>L0r>1M|#V$q?j{?gda%s`KsT(wJ&u&@h_N4K&2Exj|4OQlnG&VktdI;gVJW+6aAmZ4;mTE zt#Q(mWU2-=BfMjrn-`E3FVxVCGK13t^Ax_y(+VR5N_AH{QwSh@C|)g<|3V8R`QeVAUTzb?GY{E1h?K~A~i8<&pxP;@}0awG1v80pqJNQGhPp2!=HnCsi)LYU~<*jX{B+!zePu_78nqu^fe5>ePADRzsQsqCV3)B~+feR4y7YmDd z#PnC3qx=pcCFNqAN#0VMTsYcZ0*ZF76;u9PM{-JpmRG1d^A{J@sQKDPVJHd|`%N%A zr2=COYJKu@BsrT;DDlH7NXRw+01Ke#&PJDg#MJwa1;4vb z$nTJkne)7L8s5oUvqzGl&;-_>ZUp)1$a2A6AdoowZ8%AlcuQ&g#IK?ERAN58#lT;e zjCi2?+b51A=Oc3moC2gGip$(9j_XgrYF@z#eY=dKBp4*O*3NzOsJth~aMZo~%W33&3n#uCU!~{Ja?O)IJb|sYg5&V#d0$TV4og z%x>`dNMY5y$@p}(K>P+QBPe-;xX43Z*}KW{tr}8)GSn0Q09fI@L^q)6Q7y{?nlzCu zpsv)VdXM2;5!FH{1Wl14z#DN-gZZgIF&w8-Swex7SP`bKNJ}JkQl|sE$q=h;v2G2f z00;%;yrGtP0)4c^O8%jc#7!Q0zO}nmE$^-)zNH~8$hS<44t4N6m4W!<)Vnt&xJei; z>%>Zo=ZB-;MZ441hUwosV`uNXKEPKWTjAVpGj7bvlk+W1khsANH5EuCprWcI)21x9 zzU%n}y)`H{uGo%St8_bY^7|w$X=oD7cztXCN+l8#8j`?mCi4#KKf%Fll4CP zRD@Bjwe)TG zSZtZdk})`=p{ zZM~xQ+*D{=cF}LLT2LrhhZa0Aflk27X}}S^dQ#P}wQkDrM>+npn}xe)q$>u~dQ#k% zyf=lYC6z5oD{9=s)09nn0YJ+eUemK%+{P!k)OmC~ur|uj`!q>bl?=Vdracca{!21D zYW(DT&*L66wJVHx=6v128+L7u)bq`hq$MSATF7Tw40!HK{MLHKrRYPUTVG>BC3811 zP*3}|K~k&kz><*?N6C}N5!9b8xtAvfLo$*05rvIVl2t)hcQ(>tOH5s`l%9caII5HP z;zt<`>qrQ_>Wr1MWyTj&abE8HspQ z@*(w;wOwrfvVUs2T-v-yM%uS6G8$5pmfLaXh(XoRiX3|%VE+Kae)A9AF1HcLdG@B< zkm9_dpSQOg_`M88@i{i63VxMI?WQCLGA+NTYTZBdU}}mOFj#8x4J@JM-MoYZ{6xJO*1#3-t}?yZt~sXd>hqq}Q5yVIr7gAig-BAf&mLD7RC*MZ z1C7DD<*fh{*htF?Hi6VzrBbfRLw)J3P8AKDbPW81VxQvzyYbZrlqwr$JY-U<^C{Hy z#BSi>MWn?56fhJ=Q;zDKDl()Dyp*JO=Y*ZgkG8c8Yfzz5fJpX_=))r_5ZpkbQ3FCk zNFBYnUTtbT*hmV<)~BG$3}+fUk`HbqR}sw9$_hx%g!W;2hfprID_ped`*2TCN(EIF zumLHpI@beA=+2RY8IjeoiF%@Rk*CWiJuQts;g0Hy(?V>vTnScwT2HeM>pwDXn;TEZ zt!QiAhP%gf*oyOXQ2`XvUD3)F8uRZ> zGL<2!05M%V&zLSYnnNm8`NOF`VE}Ze{J4?KkEVv~yQ_<@i0SPW@538o)v*ofk1t&l zPq9j0mTCl3+S<+QTJf&hTVm_I#l%M3nRFL|f&-DB8CsC5}vjrgb zR}ShTJpe#aBmvW|6|qxo^w8f{ED-k`NU z>y;vq> zqc+3@p#ERO1jgk~g;%LgJN)!!-yMPC~w+SiGYKS6QReVXP?!<23?pAN*_apGcQJq8u^1F?3KT(j) z0|s*VYlP}DS~Fxv>a2&e4(bu~fkOxRN$)2M)e*vm{zvvk9T`;akT=Vp^rjtA+=fFG z>z)Rw&e%AQUc>hcNz@aj4w%&)G0tOEw#~j!cYYX7pzfKc%a7$hal^UDsAtIbAGqN; z#Gw~1ARP{P`fIAspZW+aSfKc6_kUI!&fgI_Q~^^YcK-lQ9FV}cr*0LI#UoWSm^Gzv z*HL*5l8~C`TH#5$K)zbNm~BV$AkrH~L;j8isP04<4Yo#oaJsS7M&3euvC1+y$B>cS zKFkx<8_11Q6>8`Xcur&uJ2GrM-MT{SL>gnKb~Pt>^QQq-iz|dAuBjc^3VBGH))b!+ zMXNki)k|Gz`D=l`Ww6zSZ9PF9`B?SxkP5m2DTQ3m#Cx5)Tm<)$>*l1AgW3N8qZSZK zC=&Om(Kg>SqySZ3+;z*8L{_m|VZmcRnuFhfBa=|&hN2dF-rR6#L0@S2c4*< zB^GFY9=IaizzM>JpDt9bB7mOGxLMxNM`^J0cBQVFl-89bVXq*X)*OSysPRr{*0I_# z8cp3EmR3F({YS9jxv z(UH*`d0*Q6FsB(Uj=_giiQ6}#d%p}NN%V)*YhJ1nE zKJ0f?i>8mfgaO`t-7vjTI!MZ!>7;w`ol$yd`^p{o?y220ZA=8_$8^z@r^G$@MA_Xm z8N!YXV}%E2^I@z|#u_2^IN(py{{TwkjyDS<;LpGP4iw-(;>Sf}s&T)b>C0DMgB?+7 zY?MfFr63Yj@Z*Ug2{BQNG{=vKhSGmAIgh&n+|A|D1V;tRl@7OLwl)EoD7aJkQW-AwoxRu^q`au-*VL2M_0!WQ8(Vn$jHf0yH}b@3BGq#VN|(2+sXjy35EKf2 zVZ|RRFe4+WbRjgwrr&AJ`vJ03d=D=0_Fcx}>$e-V;RX3?Q<5Kj#~yXZTv>5zTGUjf zDM}?uQ8grzO>uok6+{gkxzAQRV(e=>bS~a%Px7*=edORtUYZNHvBist*?V~)6Fu^= z+LqJ(IGg4s5Vi<2akA`dPln|CWOkO4e)?e^T#(sz)LY^OBahfyM8mtZwU2?PQJ|jQ zy)fUKsSW4d;CjFGJ?ieo>eG!>o5I<*a{kP1v~SBJHA_+wzYBBYp;?BVAnH%# zT~pnR)>VfJ(}8(yX{m2rjuTKtG@Mt$^+!8s!*$d#Bxh5(S`DE+6<@Ouj}AA`;jNRP z%Y60Mo}pC)9dKpGq0HK&zYvK-Z!KNJ3Mr?N?EG-GI-ubI8jK}G zslE{84uPjC^};>@V1l?Bqf!kaB+wqc_(15y6)o1a`!U1$SZOV&nrR2^#4@=d-6;76 zT4Az5CrxWy2H7iA6dPtK$waTerY3v?EunjCTN){uf8Qc^*wAm@g=EPJ>ae{yZa zqr`%v+dwf4VI)h|LR*`<&V+#y65Uzo0MGFlRS5z+kqN7JNm8jA%HozX2c{-S?ys~B zSKH&2Kea6+_oxr)!lWlr1q7|cw3Ul?g{Ys2(cACCl1qr3=uccNy_96q*%wE2} z?wBSsg76yT6nAf4UT^$v>1H*4B9zxpvph9ii|`*9wc;0Y&9>q*rgr6#F=PA0Hb52a z$X5#+ufYxJFEKq+_8cIjwHj2Vr{Pl6p;PV5VT#qpJDwtW-gd?Yh_+@!gCJHy>Gn|J z>l$7xiApsMMBQ`V05NJ{6&fHRf}vCFpu^puuApJtj_;7*F|BF|ufm3O5BgNZQnO~9 zbp=zEcnb^-+aVykZgHS8Db&((@9JK}fIrG{Yf}CO@q}x9*NgQCu4|hsiZTTZ;$r#LC1M7DLvX3k!Dkl(ng=N+Y!8 zd<%M}gx9v1=AjN6gvL^QMCm&^@uO79*rKqlR`vq2X+knqN4LZd9UOIDEax}mGUjd_&ns9m6qGoB zyJf8chy6e>V}uh0N>9LRJ8QW}Bsj~9^FAezp<4U!&Y=5gv<8i*AtbnlCnj809$wsKWO1usEn30yK-rs(@!lb$Dwv4-!zP;cUW%PXrtdqEz3H` z>iQ1WVAOIQj^}M#Ri~KjRHAD`{)(|@-&SjBNUiM2YSTSfCuwfVklW%oH5DWiPV9Li zXQc*Fv#$=(5@)QG^&pTj+~^YHVzgbGIW!_)l%dg7)J`2zN>vKc_Jo&K1GlGz4oOL% z!gHb+BZ5C%YxB@$a2Fn;agdgqy51xpkWEQMCe&%{QKfO|J)Z^2LQ_RC|Quoq+~F9qc7sH`1PQR*P!PN}tjShS|}XKMzcK4dwZa z^yL@InCxEO+H5G7_Y?95Y8x5;7>bCP5tepTQhcUnOd$1A%0c{a^$=c>Lss#*K6G@|M#w-Wm;BnxedJ3&1X7%TgU7-CX5bw#{y=_pELuw#Sp=w&Dr`>?e~ z3e4NL?6ql|aa#d>bw~-z+?+*3#TC)B+HtiTmhKHHLXIRlrJ5eOm0(M=kBD>>ztVW` z`no{2ZO>a)1M?C80BE1QW4TC=wV^_)bP9WM#PN*_JLux@n>VSB5T1uZ z1bxkF*5E%1S0ycPqy)h8z+Ep?8^JQSEl@xn0+LQEyNxZ^Vl2EjR>==)zFGL*OE%eN- zU)jiDo}nK}h1Yw8-eMw_d-Y&fa-vGcf^?hlQT-MJ2|o#~O)(>y84G#|E4YW%N06tA zyF!Q<72FIBw{W+#5*KJwlg=Btf%vqI1s>PbigR3`y5@x)nz%Pi;;H`I!&tA+-DMI0}@aPi}aJoRQdQ zOLS%re^ZEMdW7932Cpku@x$%j5>P%+X*8l~_E!?Sq;a;;Np7d&K+D^P^^i)63oMhE zqDdVvy;cV8prYAYs#R0$#BNQnn7Gn_W<5J8g;Qv2ONbKMreq3sVd~)b3a}3+*l{O1 z8PhT4q#Wz+!zFTp^wEOYrbj$8u8!%TwuJW#FHw!;W^|{!5=K=LQK7Dae>DeoBj;lB zGag@huxhx5$}K7%d0*PXJEU+_N8VTVzYHf-d60q9m}Rhh9T=nPMNNcZXMJjp}JcY$X5h)2g)jd-cPp+)gkDc z2GiY%ZQUB}gNAAWt~#TYvmR3Yn47FX;u`YLaKODojncw$ps%+QCXS$G5Xm{_2WBUC zQOekOLs1n1m~UNA5cI(k#d0E^(}$-K$x#A)rFvmq1q2b5P-#(t zstq;Z8uCZ-Qw)r%Ef^wVD1KUw%um%7MX+Q+Qh_>1?#l}sqVh0w!9UA{>J8;)=zy<( zeim1RXfTKaI@j5OI)PLN5fRX%{j5r^&>g{>2&tz{J;Ma6=^;58g2t}OQ@;>%wsJA@ zwfv_OMs*n|RNLifa!JFg#|#&3h$f*uKFoa$^Cyb@sGjYp_0yT@iKCPaEFnG1XwNzk z&|-I!LMMa}ToqQSZ&!G)Oe`{!2bLWGRSC*er#`_jiDZdhDNQIu7wWMhpeWMNO#E#}u@IgvE;%TV( zu#k!ea+B4DBii41)F7I5ClX63Bdy~~Ok9s>(o^Rghe3fZX4TOaFu|u0VMvV~x&)@V z(Kr%UB`*-}SjABxCHV^hS`()%jtH>|up8GY5x(G4g<7glf8m4R=vPZtXqdW`60%7h zl0kLUB(79879zX=B)GB)5GV#%eYh&|8G2f1Bdxa;PYF5a zgLI$|PNwLLr%eJ5S#&sSnnaPZ!-fKqkf$;u5G;zZUK{fZC^njQ*WZT5RT(EhAd-3t z;nS5zA7PYd8)#Q| zrsmXTtba8KA9=)*lp-u~r66T(>GTVIZO2Lc%35pp3^isVM~K*6R;znI-%RtB`I3a1 zSKe^{0G(`W#fk$CLu`*O8dRk8T8Te?9V@Kl<4ys%&bE6`2`l&Hxe z3WgrEg{2|4!Ai3!@4_jsA@_0Ugr_1u7OEWw+72R*s14YQ;!0khd5-z%)0eh~D{Yl5 zF39*z`p|_C-Nw}*=$942(1oMKp5>$*L6v1V0Nl2h1J{kR^<~}m8y4={l0@CVx*u_Hjcrk?8CZ)Xc)tzDx4icb%WkI zVQySHDy$Izrnz;%>TGL4kb-DVLlR1LLa2h`F~G?hM_S=!Du|J~Q(6ItI*j07D0ks{ zr&JXU)1ddO9n=w<=~s3Ws3ieHK+6);>Y1SK!W~l>98+Y1qd|xD8Oa!-?l_02g;5MP zno(W-n479dt%K-S09Qb$zjh(1B>~XN)7e_#LeOY1zR(ZbhF+CN#Z=uakzR@W&L2_S zfs`~-2_y^+H>o_ML<*fcd+XN&IM5_A*#wlOwH14g8B!?3S81o52`WKNDb!(S#3!aH zveNpffCo%4kTc~U=1<>&#SEbdIhqdaLDURTsA08$LIACE`!O40TZW;aq7XsOy}xc6 z-3M|R4ka|NZ&O@DDA4B`0)>?y0tqVWkz926j*J8CMwFxd*l8quXQmkE&EQII5*bnR zWj30BYNzlxi=@zzqCWk`Gk^QgRs7TdXN8TKRA*EL%j*dTLuJZ$npf_@KO%(d1pffc znp9SnMqNQFIPa1Z=NYPZi|nhz5w#@s1QmZAD#Ap6GR==5T$4pMhMK2DhQ?UwU_?cb znV<9J>0}nhaR;;nVyG)>e1GIm&4cl^Heh5l0HfCYV^^~0$@GDWPu-pfc)DuBRmJ2IR_ESQkZUdSYtb#G zzvF@4G)TIvCS`7C{{X*|^-0XF%6|ih-H4I(S*ZQPtJA_qd3ckF)Fb34QJQ(}A$p`- z5~XxKRs3-H+KUxj47Yc*pb)3QQhJ(y$%k}N-#BG^otBQ6t_G4Rm>p?`{Hk0dNU0Rf$g#@TqRpVLnsZKjv5?7ig&j zro|Bn>?1BXDSuHrb)L+}+8aDxJwd6vJAeLP1x> z-Sh;yw4=10AZBopl0xz^JX#$}3ZzYO3146bui1s_qnc@nu2~|bWkJ<1`G85UTtyq2 z61lLy?Kb3<0+$_7%=Tx8N?Hi*iZNiY-jK{Aq;(|LxL&Be6Nmo*Tlk81V{n`1I7XYC z_JJPQ!C3ST$&%(~f4Ur%gs5<*(yt>ghdobluOlJC+NuCF6S{E? zH?l5IRmoSWwHcBMTNUm)Mj@1)cns!& z0tp?UoO=&vLzfb*YR^9IJCT*@9Jqo10K-VxR*yCR09`n`++8fPuWEJc* zcLRcqv{6%5($h!(0JXyNNOf)ahg-C738f@OltPboflln{iPRYnR?_0dX#9V< zQT-UUCS6DsE-5-`T)S~BuPSJY_~PI3kleWK`y92C78!`I$`YfbefWw~sC}4CabsQ0 zmg!X+lqf&5*Bx*&G72MQOk1gK4rZf3x?zZGy+D-BvivzeFCpZ%;OeCs3U}pPBXlK& z#U)S`TOhVH3y5)fRY*-zJM$+zMb#t+MXu0NRQ1J5Mp-2vhIo*VorGT-VlYygOR`=S z(iE*J{4lf`9wTl=!KT8zw$j()amfuEQmeXzKvil9>0f37mHk4YjF82Zaecy~rwW1To*OHb z2#jgqAv+<(_*GhVU}tzr1QfLjWvB7Nb%}Z*Hw_6rd&WDgcT6ftCZ`7j#+xsU=AuVZA|}6F@3FW`uOl59*>mlLf9js&`3&EEQ8%hMhZb`is*GsTHPL zvDFpZ3+PXFAJ!YwMtqO#{y2xKFGvf@_TjxocTE8)tn*EMqZ3r_kRO&i&J|T1(mqmu zlzZ_0p(Oy+no_t&ZCB7#ZrQoThm?zOH2Gms*n0+J4(c)vn+Ebd zoqf1>RBtK=OD2QEpLQndh4TW4s--~8^*C*-q!YJLWJc}NLx;xwMT}?+nBC+0aP@TQ z#-V`0Bmk0Y-GaK2AZmi6R@FEK5rO%M`4G7t+QI?z0IfSQ56lVD1XNUm;A_G#LcTYo z!p1JHs5xR)bdKetDS)N*PrC^65(iLXlXi7@Xy0Ck4tGcN!>w3dp{r)Sa4%TpXjqN4 zW=m2-QHrb6uw$o{p*Ka;QFCcM3=<(}K_3vPCk>VIIN1PIwV{#{RqYssUL|p$AIPKm zPx^hBFRC_xf43nygI@887Q961rf6aV(FgrL+%}|B$1(CqACTchacKBLN$85<8=!{& z04Raj2i=GBv$>y;Dw$;>s--*n;!dJNmAwEAnB$=*?7iw4 zh+SbB(AV3E#0V+0gHn)d+m5NhR~kXgARlco^xEJG&`h>F1x6juA}B=wEu^ck4{j#s zV;TYZWcH77!u1%}1>zU-0)7+k!c=UPKfvcQe}>XhFOl*l`Az2K<|>q#899m074r9C%>~0N>yRC zir|9o1%(o~=mun02}BDmJU~-5B~)40-9d-O6%tp?A9Om9K+*1V?d7 z03heo;z?daF|xzxf>Tnnrkw^I)olbT6lNAiNazo47>PpbY!gbc;F z#88z+$~8AiE7uMkLvJj5am4Y30Q|KAdQkPkf;j4HgsAXjwi8-cJa;-1q-~BA&Nx~h zV}w4(9DR-^=^tZ=sC}Vb*c@6L2IxVrOmWl%#~nb@5u&-)xNBtHFiK&hv;{?e!P$ip zfS5uF@dWbE^I&=tDI#ENW*zznCQ7NRj4;t^HO}F-lYWB4RV4fg{ib0 z7I+AunLf1YlxK)8WIqH;Zyqm+_x}JBv56^RQWxH`K>RQar*V@moIsquHs6LcCfFT> zBv&tK#LFI1T8wv>f)k`)C%uq$NlEM<@?q-`hpxo;A8}_j6?-e!1uF_8T*9es)pb^y zcbr8jP=+(9f>7F(S4DEmzYX+{7h-8#M^pg|VYgfq>)~li?e}6>uTuA%+dM+<8;Q7X zoBYMA^`+0aI`WQXOI+7hqHFKQ{=$7URYFUdfj4oF9lcLFhR11A(x2mlE!vQ)GBEKp zR`#&QBspQ8&LenoVWX!O1G z%-(YMccn``o@?rP&N{l>Y|+%CT`_egWONT=5cLK!x#=kt6ST|a<(~WjCsUy+gQX;% zm}v0pNuq1~*ha)`G}>qldK@ck`TyUoJ7>QgV>6?TT?!lydjKD&adQ;nhIP7{pAi^RXv(3aJrq-03UZwRRtI+2Qko36GBNj%4z0GE3Z&8{uqK$ph$hWR4-R+J9F02qVi(6YZT00mrRxN5gbhhVA0m86DsS)rD- z9L0OEVv0ib8!%o_$ge)!MLr{E#4M*5*;RM;VWU7#nod~{ekJON6?0k%@z$7Oz&e$V zsI)#I)bz{0{RR=SXuwtG3RQi3ju2=PNCu@LN85pi0WMR4R8Ek4&O1TsG-ex@sbsE3 znja8NGyCxqwk@#wZq~3;9&N`GE8)pFjiOOuyX*ka3|bIJ<#lT$*9~bgK|joIvXEMZ z5uwy9(4WT=b0i>1{n(9X>d;GG=#-*$TvoLE=rFw{0b3=86s5`Te(IWbju1f>4-cDz|*?IT8k8sF5K83FK;xQCv z*x5xhbvt#MG_h$~Pej$S0QY5%xpAD`0>^J(-#NxhME1~h`$i$>WZgt;HY~R&WGb4z zEeI7Iy{8KtfjWpe8;{}Z0q0*(9RMLez+tnz&gvX5`tI$nR$cDw ziFlBGw(B*=87AtPIl6)98#f~NuAgkz*t5L7S9@2Q!yc<&GZR-#^NsWla=iORU8 z-aq5Wy4Zi@AE8GY?cJ>B9P#_|;kf(ivs(*(AeZvx{Psk2gQ2zi?mm+R*MwVm8M`@_9;fHkYA&V;4v?lZ$`=8!i64!7B zpTiPKPr-!RyK1}wF4||T5hn7g9+Xk*h2)ds$^K>wf;5k|p#lCPX_QTMRYy*K^N224 zUMDQscq7Eu*0R+K)9dGOM}V~atF~)vgd~Cj%L=HC_{ZthPR%taejrxb7Lngo|xQEELBQaHTMin2T%*QIXEiRYEOB#-EoAx zUTr7B;7LeTJ@|0+)dVH3v`ryY zfmu9+^p&i)6{v|H07xp!?ZYR3o7hWZ1~JjL#>~Cha8i_8l0|Ef$bFQ+yuoVHOl!)$ zI_{$5g~ttbD5#(5#|`4dh1}c}@tPjAG{*>*p#|L>@hi+e>>y`FcnPmheOif80}kr4 z4v5yBd0~VoyCe#BS3EN2N{Zr740hiEjd0!UfTXcipUa+D0p|uRF_AxT%#sKT^3r<- z1G$+m0&6&$4d)c>gHqwktxg#$&G8I|EkZ6uQi&>60=gRKh`P(flcK^d#cevgU5B9oS;XWRkJ~boUQ_9I^*1 zKx#lHpwkWOv1Ms6FC_l}FXMt$Xks)DJoejI?oakvdQ)tmVq{3lhC^py;GC%EBy zq*Oi(p5k%cL1{x_7z!FwykS(+7~`q1kC^a`Nl|`WHW(hyyB*Aga7YmhRX|hl!0EKb zaSe$1_xmvZs}rV!Ey776q@MAKI*1ddEJuR15E9<3Mbu>7HZC(}ny`?D72@rtJE+MO zhJ}{)yoH37fQt5EOH$k7+e5x&6MKs5U?7NRzW&zPrN*5jpXS5j+-^4;sZE1okL=$)A(khjLd%9s`Dv3%7%aY1zq<^Q`iDZ%+InXHNpef?~aLCWd zmMJi^lGK_}M?4kI#F9Z3$R?SPMjJqGMig6%D)_o$SaBL@tzGz*q7h%szx?O#$I_IK zk@N3bKrYIA!}^B~tb7Dlq+rW<^;ySsP`zmy{Oh03i8^TRy8`*83iem79<2)QpM27n z;iub{cx0?lS9AqZ7nY@au-=*@b7r41d;b6th3OD0j4$&FS5;~Q+l1+o(EH3igAs}d zB`kIvEePW#jEPQraM}~b=y5Y$6ay-IaK~ve%IGk!xz$_*I(E{FPX*vg}48Xpj|`387!kXo5gG!#!xy)cX**e0mb za_{@_sBHKd2?^*ajv#5EZ9mxjal>eQ5D$3A9Bu^#F~t2OXB;n08)#p4I-}|bo(BhY zKF0||;0=Bs!x41_NDu;g3^+5YVxMjZjOsxs)Y^*vQ-}x9`$vaWaW7P4qf<;s*#bbR zNFzFffSkf;37~gj=^SsRUD#8o3FUiml^x0rp!Z<~p}23n=Z-iR%T@*FLevor(z=Jc z9N5;uf=K+oWiiop9<8=+hKj3^%h`hJh$9LcEd)}DrEn`GHu4pYp}^^huj24i5xaz{ z(XwF2LGhA7=rEl@-6SYBm`rJ-0cWzK5zgZ}5j9=W*3Om?mg`CooH5(;C+ju90aGSD zO-d@SLH_`w1ay(y(lhds-8nWu8R}I){BS#HbtY=;K#e|hfY?2IP8rlAVO0o;H5%la z^#db`I;cY-PdG?s5$?=kRr9EBn{~nJ5>p~UG2zYUXV62S`a!F%*8|bxH}C?5ViAA}nrPsoGG>ZqHJ(q7t_c z;0`6Zl^B(hgy!liwYPU;^ztlHLJw-v8X1cJ0K6&0UpWF;VM*`?{{SX#zs+hRym`zY z{yzo$+z921Bw*~_+?3Teb-AQ?w7AJ20bYSs!hG$&GCz@Y5oOu$*5T$#wMmPpbqt2W4MFRO zE)zlt10fYTjD{B2`NAQfflTtkPl$*Xhr0|E86!_a*RCDR#_}V25t!`l!YQ%xNibBQ zBnqaa_Tm{{@V>T?$SsM3>VHv^3(zPTI z4SVo}@!%*1LR(aaolJHRD&y`bApmvFN+4iXpODD32SlohZN$2T3ZcZBr9zdcBLO+< zK)Ax7t)wUtMR4Xx$br0+Y1L1&2~jyvT(E?KXG=r{LZ5j9YdOkBpUW-hmC5XGZ?Gme1Y0^978HpP$aU-O+i!i?KQ_nNdvUa9Cfi@ z@aj!{n2Iu}KuPIK6$oa%;E(xmZqu;B&i zqhU4f#S#9V%tY>zM$G}+n7Y@^p%@>iJEp~$_gM%~1@lh9sNt=ul0iNLk~ZNjgpW8^ z@2()Y**{Eo+HW!B8a;6NQ(V?5o(Cki3KAq6g@`tlKKqi0BC6G&-GSvA1`cSOhRV{1 zlWg+<{K_@{ID&EU9T`wrYkoM+pCS@dPlkz6{sRNb6&jsK1U>V1eIT~MC0zh)kK=|) z8XWD$Oi&w=@g(V3E$Zl<2DQM>EHPt24hTxSJ`?7tK_;2h3`C+p+)(C^8DmG7@`>#= z!`+&Af!(^i5y4-t%p|9SxMD&MrGJ@TNz57p2}k-d=sk=6a+7?UmMc7;%0Mn8^-&q) z-V~i>R8(yjhUpl(q`OnPyBQh=5Trp;QbM{@y1QfOZfTU18oIkfk(7A9^ZlED!>q+R z=Xv(N@9VnkS2-j|X@Fx?)X0g8840K4x8^NguJq*Dn9|5W!NN0Peb{9iXTd55bURRI zWI}~65{fKexU@4?mK>lgD?e-QvIWYmTL227aLHy#ex#is8jO*;43(e)UwAlC0O{;C zV>KC}|3UE2zK8KqslbM}i7-BT_lR9Es>T4fiv;{T+!GrvZ3e_>f8w`sl9zHF6tWz?GcWtdfuNxazP!@nZdKUU4`ypzjVHgJ?y4q>q~?p z)$CW+RD&qnq?Q2>4~{v3P^K|*Ip+}C)KO3*%2={RjFS(Qa9}gK+*_yf8G5-BF8Tsr zmGyGHM9b-aKt(fm`TO63zrX@iBzV%-U++A=D?w?(IXmum8eUNcKK*@WMflSJ4E+2OXK);&b4?s(fdycbf@x)?su#(J$A_# zQPFrWT;q*Jb*9$qdg=wbQ+ljl9u5m7?6U^A4g_QIdIB3QbbWs#@6ftP@Uog&kkKy( z9rVcMB>f|>LLhmf(u%Ml{WB1%VzPX9_BoYyuk?q)ZBy1CKDH-A#yEohTA9rCBC!^ z9Med>@RCrEKkj{)E~QL$ zVF+RwhFN;dgj;FvlF*u&*H7}@OuU)xN8OQZW&e(I{H+UQ)NRGoRum+*hjGq$@(HeK zuqR$S^*lmWd5x$if+#fIPv_&|-%O+?0xuYx9oRz}Qh3WHDhVczlL?v~NF|1GfzTTa zlQDL7sW1}V0M|@~yuA1cur~=+%ec?|c2wsb^Q;${9J8)tptdNoQ$B^^3o_8knDWRm zGPJ)F(^8Nbih&WY^wx{&kS44Le3|EtrSpW>3(6@`F`R)CQV)X|@}>2>G7iyppxT+;IzPcAw zx<{A)cR%K|x=bj*&0y8n( zLSAr`!bweGk^Vq>%LtH-c}Z4yF!xYSPgnJcQXP7tm4AedT*ZKK^Oc!2?r7-RpfvSJ^?@^Q>Qch6vb-$W4n|RR3|JR{G6&82#Q5x61tU9k0Zbpg=xpG38;b_ zPJhmsMyA)3ve(5HCI3x6m<=zL?ZE+C45_doQGD7XI_kPNz<~bYT%aL~V)OI+EzNDL zXNCZN9vE8KRphW|)A~(Fb8C1p0AwEL_D9cBn*=+E{@opxt#POu>-Fv$3aQU6-*KiH zJ2!2_-QcGtvEn8*OSN5&R$kzxZ|E_-EdP09!ua~VIG`-np4fjz&Q6Z211l%-H;<7l zGV2A2r~h_8@E{cf8<4paWj zwHOQ5tAE^n-NHWBqGD8nQJM2kGR#-5hEy_}!q=oXrM(sqs+LfOF={WYr5Lx4t2o`_ zIw+{K@eWO2(h#{&eqo~;m(2c%vfmUyeKDDS#-8MCoXTah^Q5F(KBADKGon*{yk`%@ zi>VyDHtQbbJV|uWdW{4)MQ-bndVr_(Fe_ov^B-zHM(lEAy-FLBgP&pTn__&|Ltl90 zRAdl&jCLJZEYPO#C591}LijPuyb2pFME7PXSr<(wEdInRY7n8yc_>c>befiuTyndv zP-@f)L4W8Q7II3g1dD){z#IBy-PR1H^9(+S6mEQ-P95a(cM z0g{WPYJG1%B#4Khqqt?zx(75w#~jZMB!m0jiOOGRnuzV{j+;a3&gu- zu3!i^&c{tqq2t^V|2rt|F6({G?UY|~NC+8OA9=Syrl5Hm1?tBHH!?lauy-}Z=syH4 z63-sZ%M~cG_k1s-HPYFPgt>MKHnZ`1a+Xu(KMF3*c&UYcEN_kd(eu_u@evtEVAPm3 zU{e#zRQ|knECV`WV8LQLU3d;P3c7Iof^srt>gc0Bya@=Q{NL#Od*t2!fAuf~b;AOc#q~oZQ zN=0H{lH)~~N0c4ryup$gLiNsMd`92*ZDmsP3CF54`Sr47tr|OxJNGn^tM?Dz9IeZI z6jfW0&}ua0AR^o|4~6qWYKfCPfWF?1OpIf(KV)v$4X=8y#??Ln@dPbbm@P)o7pZ(^ z7OlJ-c$qGlWjN56E=ghYHLtItR!7%S(F{T7muMYUu2*?N2kvJ6EOdQSidPsDBX{Mq z=(CPj1DkTegu|1h<%*pFDxb*0H^0$9o_ula&l8}31eVJ36LB)-I7U4bQPu`s&#FxqRZK4(S23{D>*D zKt7MiIsSo)zj8C-3{*#gnMP1`Nak5V$_nwfl9rEzt(5!y+2r+I9v}j_w*r5z2FYHe zJCCernqKx0&;4xII~{FxaJetu#Xwm)Wg}N{H!@2Ss~Wu=%#$)%h%5yTE5!89^M#vL zc=uaSfQmW;V=y0BF`zTjAx>E|K{!)2_BIPfX{hy5K)oMreTzwz@wD6lrzI^r@6RRL z$a`-aY`k(fVy3Wmm4ujSV_D8zWaRiCoGR9_buUXU`GO`loBrva|KQji79W{9-|Yhi zTF4v)_}_L`b0>Q|gIvzRPX1Wvu8w|MMe-8w;`M0BtKffdVtlWw73{WIs3qk;((yE% zC9W#tZ|ZH}52*Il-l05(J)u5Dik6W3AU-uM3O|oaYq1%CVQ4E*O{AxAFYGYVsccqA z&yNUXM<(xQmJ8m}u={QlmWPjp$@D7CYzGX1-(Fm*`UF>KtO;z||AV`ux1thqhU5x* zXVxV!n<*21{D;FNl#^i! zW#hzIzo1;y>?t>-&Ffwcf$i~sZd40>GX2QPk1lYbO!&+#Ah$r0`nO)Zm-DA?+dWwz zBJQ1Rw^bxj-_dV2EXHt#uPN#{yCKe7HATsLiIn|@IIz2MKjo>CITzIb;F8&?4sZ7X zYuA5pKF9e-|G|k+3_5C*dt2Fi3>6B8+c;`oT9U=0JRDYbfD#1kMQid|!&4bsM!E{} zYkzID*I#^W8L{O&*2QZ}J73z=OSf}7l9-dyl~8TVtf*|>qIPrd^!`@(nF#f>{yf@c znRVjHXc8xN=dyL3(r`>kLU(C*DQeGE4rU%3;ZzPIQlXbvH8PCb$orP)(yf@-7vEQ7RdQP5xU}WlDH@ zq8V3Ht2fOR1Q%Aa{*f|22dM@upSRh;~q%ApAe2(;N!s5n-9d{v#9E5xI?v1RKN zTre9e*kLwlOgy{N_l+t}Vq_5PRR;_c_|Lspf%C5fMjHyJr6|19MgSy{;T-F<#VqAM zkmQ{YFZ9j{t^@NiMv?w0HI~hWZ5kS<>kGIc)$)U2DtLD!@aiIb4acNGhk9W?(}M zjk;ZJxLmA432Q+Ennt-87WBZsw}iEpx2hb2ri;gHnGF{|oc|OvDqTMazG`XB5M( zhuKCnbt4>>Um>thb7s}8Bxg+U*E!6XgqL2FZ(dp0x?T#89&X*$R1cZ;a-`Qn0zC>2KXlWXJ`m?{MJyxcswo#BGEgiZDtwS|Q9D=5v z*G>tkGzXpJ7cJI?9!eeQpbUG&5x{_n{NWo^EDc_xyjh1h`Kkg9z){S~=p>2e>kX5O z@)VI(unG{KC+OPcmW9R^)V40;d!h}UrIj?NQ#-kiO^!vnl_GF0g@be(oYCq_7>mdX zE>BIvK>5h=Eap90GE7mc5y#efMW&y`JOfEmp#z6FG(<-wet&pL7O`AQ?UcxfHWQ&U z`6vEKWy{0Mh_Q}9nrYV#y-wnBr}WbP9MD-2DyGU6h*py7duRqZ3-6E>yl~2}SfRPq z7rv{0&Dv@4ccqPU8(rC{a+MrKD6~odqEVRmsIqRO2$kyoW;Vi*Pi!#LwfzL?VteQe zx{OZ<8$e`q8;(1B+YFp?8z?Y;Gv?3c$GEqG3Qr5~U(!EC(jM88`%|s$wh?Z4P!kqhbcF7KZ|$k+uO8gx)dl>z0PAWYW@^5XjLzA2V! zj`fMRXN?tpS_RUiQeY1m*P!iANWRh7oyqIZ!Y3-BD3* zBDBNI_(zZ~#;wLQ|E_3y7sa^oJqTcfkSSy@_T zi%ulbf|h2c|E&K6F6*pqDB(Eg`m!!Y=(OplK|LuVeC1KG(s~8tOzede>`XJQ$Lb;! z)H5C**;wv5NE@;Mb+%$9{?Iv80)eahgx`*_!BYAyIkKlxiz8hU9a+Kiv1Qa#!*0iW z(cpA)%dB`^8w(mbS2lN&d2IU4>$MSF;V>;q*_ z|5~{37ufp!v(GbMZlMeKpey;Kbtt;s0*4lc`y0C+6UWH_n%Eai6h*kXRGoDXtXc1Rsufp}fN*Y88 z<{Q>K82vdZh~-JvZOJsc-ckI=p)eOMa--cPD%m3#L==4pN`-8q7A?O0;y!R?laWB} z2UHkD$L^hlZ|F{mgztam11c=%u2ab4&f(iMV_db8FeLlUxG;i$AOUElORrKA#B|49 zNkdw{ETmolL#JxA?a6YM)J(Pa+W9c0H5|7QN=2oKsX3ETbk=YNOfn*r&V;hOkc?SK zSt#%2(BH;{AcfYrmY`&s8UT$uCuJ;5U{rGc!S-&l5j8Y+ZnvQ(*|=uCm~XqvDdXg}7O4xR3b-&PHdX{{pUzS_OA&MI4h_ zYji*=4S^qwSM|l^+SfgDBr5*QB*u5X27qh5N~HcN)PB7K!ixpz$ft5p4oFzKnCnCy ziCkTHuwH?1fQ;6;$k`unt^M$-X6qjk!G(v#1GPIa2m6hb2=-1S0yQP}7zvfSQE6?w z!(j5@{rp1o%Keb^ljK=suj~o^VmSB}h~fvo?^#V@2)?5X2>LLmCedz@HGGp;UwgfPnE&8X(QfE)<_Sz<{}?NaFZ})D-&7$;?=Bo2_wAB>CbY<7tivX_8~PO}-mqx*;bCaR#`iDE!$? zLf#chmZN;p5M<1#(yb_|p8gf8wGJ6t0H)Zp6#oLbGW2vW`pRB zdx*P^EnUUtrlTTq&<6Cd&eVx_#Om<(oA<P_^liBg)b!=F+@tt=W4%>-A{7J2gYM3Fg?BmbtbV=n5L z1F=AN+5h00)m`6qv5-+=ycI??~pro(LlnbD{nGROFJcT>@tO$@6cj; zpua4)cLWn{m0-D1K0A%@{(@F~J^MHE{gdzd_i-tLjBkO}K^JK}*V~%^!M$wt9%p;c zJk3oV;mOvX%e|7Ufc3Z+gglABE#-dP6BMBzc7lh&vN%7?fa#;SYUSzRq}4|QFD^%m zahy&9qTv-e6Q0A(b>`_AtxL#;rsZ4qb<@E=kKGu4xoNSE9f|=d45Oe@D)v-Q#m^fX z%?IQ5=P*w;o}_rmDV|T=VK3#vG1ph&p&)Xw-w6t%VVvW-yuDbjN)=dX169+D@~>LD zB}3#z-P(D$7E;K1O+*Xj->(!_$h81f%GBe$2Dj21n)lID#({~dN& zLH$?FR9A{?+j}?4e;7YFP&a5Y(tkjng9D!r!EaXl16GK+IKXS9rehh5|Z zb%IuoxL;7ZvUds_CVnPDmWK5c^vn|+{;+X!!ZV#dGk>3TpVC8{CSy+%oHr^b6GQZ1 z%3#86y$XE6DXiUw^G={p@3Nygg>B&gsc)=|Xtc)G+aKvYH6tVw7JVhMFERQrXG!<&#yncB z{2iy90CNL4kGIle5uyCh*66v3T02abhM4NCa8#UNk={Por*BHES}-zX<`U>;pG+wt zP?WnjtVapb#bZV=w*rBcD0h0%St&v6Hjl)zfGUYcH%hn^dov>LXlZr(>!F1`A)Pi| zHeG51YVnXA6~z*;faLHf>Hmun|7XVE15#k%TCg8yRn2-YEO+6lz?XpgOu?&F!&eTR z0j3g8O9Mc%EkqTB8aYU#Sf_kv*e|a6OqeU@0FXGn#e9V_{b5(M(j)#fO3{-z=?SZF zf_K=rq{h7O!}W+BLy@q^E-YW`kyAaop1z7bUah=WZ#r!h;zC0s1BZRVsdzheItnq( z?jx6#bM4O2#f7-COa=Q=sFHWb+wmD$yy_s^=ax%h2fd@Q;K|3DUm>b$=;06;b2L9y z(X#`d!OFOT$;;xd2T8&*67EA~Zi$?Vtraxhs4NSsEQCY=I!k1BA?}oNCD;>@%B43>E;G%=ot>GI3q@9?_~LRDTUi)Iw3um-tmTA<@G;hQ z@_vsgrdx4`{iz>Ql($#_3xf-KP>r_^!|j2d3{?agDuy^#-RCju;ha+YcPW|&bb5T! zP47mg07ui6d0WIM8OsP1%P@0*S@hJY&YsrLD&&eKb-hM3cyhG?q=VWelu*i|UkecYR>|m$UY4NY0me4^z|VA8Swh(P5Tk zEjU@P5u0|xi-09J2Co>*R{CyT@Y-wh5Ojjm?bOucXv*@JH$D|Xb-LrYUNBX~S5ublG>EZq zT0d6?6ZbK9-<`Ym$W&B!Zh$wD7GB5#vi`0=|2S{Zcj9o$sN=-5g0zV`lsho<)>q=W z;|mu-gf{Q?Bq*h8JzK#Xh25})0DnE=mt=9L zia}Za`=!oLv zi6mA$TxP#CXW>i)*-O4?K2%#5*^i`ql{8vFU9-_*nWLxHm-)>DaD z1{M#p&*8m=zPkJg#oAHd$8VzreZucuOvvGMrEB}yI-E`FzC?%|K!c(nlVKL=b<7f? z5$N}OH?WuPbc5~cC^95aXS6?)%w?YiSiqD z?>t5!n-BM$qT)jc;orWgu+n#i6mUK}{k2XH7)GY>EuCD9#g4BxyD*|)m1rd*x%Ka= zm*Z(SbWpyJ_-SQE?A9w9IA}NXADlngXqxHc_V-&7e-?Ki^yTG_dh#M+I#OiIXtpaj z$Szxyb`+BB#ArH^>N5{9B(;J|De_338|HAy2J~e^&#M`WiX`m|L0nkeVmfJw=L3B9 z35r9?Q)0eG{fo-fp4_9lDUxy-6Zi))IE5D4BidCQ4snr}8pu9gnH%(fSy5vo z-UyALFY>1$ci72lYT~B=!&z_Z9|zGX&#CHQy4G0<1&5$WhHoT0;mk01Lxd`&0D#KP zgkRPeOQ7EDkQj3CtRMwU3Uy57Q7%(-rZX8C>04)WEQL*p)@AtGmw5R?HSOrK;{mnYzPjqi?I#LJJ4F5EXV+o{Qj!??lB3`d-y#)dO6#3%L=Cg4y{rBB`SzUj?E{F)5|@ zFv>-IS5G4PLF;_h#Ik$fCvp)$HHv;@SO~A6%aY0_I8bCd2W0Q9}AWxGRtZ(XO zv+U9T_Pf6rXZ zEwEvuk{v;kDw6JH1)+IB=0nSxEbfLpRS0LW(&{qNGF35%<&by@Ou1^B+pbmv`=&G3 zm4(SDuPu=9{Y30f_rHKQFRcga7aO1Wv|oncstk!On7H$c1M`z?dgb~zI1pJmo4|DI z^-yRhjTx)duLS6&G&8-LK|+p}?fqeJb7heU)KR7lt2@V|+)dp}Ve^}1t8NKI4vf?p z_Y7@{?OlH3<7zKUzn$^h&?r~k3lvDElTBOCV7HJ(h#`EEw;F1e8uLw`rr&HLn1W+~ zdfx-0qF@&qZKteoty*4GJyysFcx*8+SUK6w0<2o@4t7WX92;;i0 zeOmkQ+?bCfH#MZWalnu=L}noo1BT~+$k|D^WGSe1p6pcdW9%!}FT&Qu&oZMn#8S1lJ{=`^)ov%Q1WcMNx-p@?osEqzMy(=`9!BO zUZ%A-{N^e*31r*-4~`Fk$i|aCyO~@k#-ce5--9M^^!r}-m4Ygf6V@pXTWMp#6gqo+ zD!W6d!_uip*yVYor*gNsJT&fYN>xPutKKKQVo9zSboU*|S27m1e8P6XE%(5f< zXyz~4meuA&*jct(sb&5;J104>od7wPPb;FJ3KsIoz2tW4$#Ejzb<0Ap(Ym=!KQ!DT zZ!7>OPKbAM$c=JxoR{d`;G4EjJZVDb)pwOA*()VjN^^rTLqh`s%g}e}w0XaC4dAj- zyvcxK1a{Cc-?87CO~EY{oZt2YqFY>NfTwfuPALt?dx#6G9OIzZRh zX&ywDilQe9(iKSQXIT0xw{0IW7f--pz_h5kbqa=G6Q8I>)zrS(R?TKSbjhcqRWQY0 z;QmhPF+ZB0$K|g+T*;CO5s5WbnYtbG`1j?3DX&rnFB&**d1hJGfzSPn?AlA!VV5Ly zyohcVs5VLtMM6h-YuF~-K>nlze1*eOqdW^(N>wr9n^Ja|I?umVRSEM3-=SwrRBM{P z2%Nm<1$7#IUQ{wz4|!T8Wq(sSWNpvWy&X4QIpHN>9A;HEaS9&wHiQ`}|a7qPUPE_0si){!nA zX+u5@Lpqyi9oA*eufy_h*>F8;rjl1A`8V=31zd277Pr-yD#<|)%OJPB|KD_iA$1TS z{zGAVCpDjS7K+{xP#L3W-~ znYUF}_CSu_X5<&zHt84zWM#lGCCg>5#inTZusfdUQDngiGd?#~p9(nvrQyj2Ug&Q) zcOxkv!k&fG+d1J=_yI*P*0JR*9xI#3EoJAoTCwC%d+muVCHJxg!FjPJVll#P$qTXU zJfJ)ggsGrg{RvIvjOv1+QictUz%=aluxPZLF<5wPlwuG;Wy)LzpO;)u!PhHxCxV|7 zen=6sRY0%Lin~AX3}n;SZ~brF$gD zOe3g6XrIvk?>okppqUkbjyUKs$5oSRs1yTxLnTh+(Af>b1q0E0bCv1h`@NM?vTe@4 zilxx9H+L4vzag7DP{Ysq3JD7Ep0~VWF{I`O9vQ*|LyO4821jR76M2f)V`7ITosG<# zvku7mjxce}zZ@}pG$of83-;ji4HU5>5}^qiMnl4;*R|f;kyqbuH?>TM#YURpuPcdl z&~d{+*)JK33@{bK%L!-8|UtGE3Zcdun zq+mT`vWCu&4M|v2dhYg=9NXJU=oEUlukF6C8&u^sruCU0xwd-Og>{%Vf)}gkfa-Jx z9_7|G^_! z^EhF3x1}`AKXSV4Pn+vetXeyNmFiK?2-ZEZ%lS_i_MAN@cn?fuW;c=fk6f?Z@@mQ9 zqta5(#0^zZCK1NdMv`N zP6f8abQXVsKwy%THR4QEG<)P&*n;V6=PKsRmA<&X8nQOasZzAZz@k%K=ewua%5X*l z(VXGGz5L<7uc%4>D{4LMr*G=j@V<2hAJkkWp^bXiSa$ZT=-0f`xKr=om&g^!7%o#U z9)rPMZd_Z@?A?^d$m8`=%|Pr+09NVn#g*@2_WE7!+u0#`-_u6(!r7`Xri5i6tM#VepT)uM^{A;YcZcaU^OdzTO5UhfZyAq**GyJ`UKoQMdpw)= zut7up7y&Sw+(KCiFOngI zh&ng?(ED8vRD=b;-|4vM;4>)-+Mgu#rI8z?PI>SCEk*PP?%MRxd^4F-q1@if{(0Q? zl}~8H7|-g$KHwIE9{cJXry?8GY0LQHPC!uf zWw=1R+E`Ogqga2J(gb}VMCrX`qmGpGZ8<*ML`v?`VXM;bxav9M9O|d6MX%5x?(N2s zEB!v>zx#G^#jPbENCr3_n5y08mk|v$!S!gF*HmQdvskn@-J}5nW}0v6=m{6XpanTv#1>JD*o(~Qv9pnfc7TiR*sAh_w5 zv`qccgKS?23Rv_V9p%UA=IEp)s$@vaKIHu-+WUOJp#}D8jl#EID5u}v6F#VTuPy&1gwOGRn0rB?DCRc&G{|FE*Y6gc)6MC1DMft7D6hL2^%DzMiAQKKmviNh zuAx{vp?hHrXE__UaMDOm+*%XeX?wZFV>Lkl5qMtPov2K*7O_)%46bim?( zSmBEgvDo{kM6VapP70TB;cU10h-H_xbEtlqg7Rqe`}l(L%n?83u6{-b-Gp$1Qu{HS zo_B(K5SUhzk78jJtc61b1Oba?ESN@9%WaVxYT75Fyp|?F>2l=fSzKf~x1B zNQ&_)pxazrX|#tTCvCH?MKu5A-l8r=5xacmc63TGfxX!U>YcHn^T6)ZfweTC1yUAR z)Xh+$3fk~fnEb1g7;-|6eN3)>D8cB9)iv7l&tU)5t?(&wxZ~Zr-WPQFQ5Kx;&ff`p ziA=gmHJ2?%Z8{solqx0Z>Jnm59xYtBD%r>cB0s*N6VW&gqdc!FX`N{gWfkTM`foL4 zw36$)(yVRty$pDd6gYyj!YV80MhTD#y1Z&UVl76+55E(lI{GnFptl24^jnIe#VC=W z%|~r8z=e+X#L0Bl!ABSa`>6xO+d5Fm0H#+kzlj^tjqI+YG~K{N_8xW%HH_!LpuJ$q z+b{+u2{Ay2XO)~QAYUS`_{6gm>ItHr)Q$w79Db&jRSe)}OMR!$g5;1e?-s1$`|O}XLRX=-cEn#x$uogb^g?( z^B9w}B8@E2JX_A~bXOcYsUE&ZJcw0p`(jWO|8@whhtK=yhG@kCU%4KArUFAS^3$}n zVT}JNds#}T^q0B^C;peLuQHbX0`D2On762@Aq_*kV;7MrtbD?OldNc!UVC7s6Ew-SObsrHzXTH0_YiCqjlR; ztlNASlXpx*np7(K3_a9pOzE|7H|BDaoMmA&sk)-7p79K)xG>Y;pDwH2(u z7L`sgrTg%f3~1C6MlSfJi?+n^LOAnC(POp?=TjXJ7cE-rNz{${sq^{s>`_dcg(8{B z!hYcd9HpBO%St2c4-9cA(!Y#4vbWO>6Unw78)PqlSahmJOOtVNoTiTD*D{ivTeW~* zx4gW5Jra8okNj>vy1u0TI*_5mHDrC3DygcEu(ax+~l@)*2dG4_0^4?)WfkOiP*Ryo5#;k(W!QfG0vseUk9BT1d_d1 zo>N)Wv=P24LMMcdLNbCqBvw4Nao8n`uSWGKC#Lq(|FEC~I3Ud_z|l_B?prvD_yyUW z#zUaMrvUKPmin4U+oaE6ESbXf(pWb`Qk07?fF^(P&!Wpl-E>D|yag+hGI=tomc;sM zf7rwT&k#8cWIlbA$FkZn0e5>+pY|-pW6I6|h)H^@ZBM){1F1Nm;V@h9A`1>E^3L$_27PSAfe0eC@uZ(SLB6_==wR z+E7ugBod2mau1Rst=oc?dHo^fe=w<4%NkeXQFr8bQ5hx z4=%vt7XVFZN*!IxMRf(G7VOVX0z#_3Ur;*F-Ah5fp(y81+*hTfV{uQ`9_rlrir#cQ zhxi&E!yloe1m=sP#|hP9$+<|_gD{}6TpGlclKG#ibrF#1GL0_94qEDeB zxAetH5mZGX+mT&)d&BS|e%-s@*HWN%_EE`*&64k1u&fNbr8T4hd-D#YXaVvmS~NO{ z9<_qFTB;&`z7Db6!L?StOHSCH+pXP*v_}tqDJ#~gRoy9Sjus34CV77D?Mt>WsX{n= z8g`8P<}Vjq*57eayV1vlE1KFl;i)<$dZxXu%XqPBx)$HhXPI02(WUEcxQ1u|T|m%! zaQ$HT$D}e-H-Y)c0jdkp@J;F5<|V}xU71S~53N;=l8@8rcdQDEavZ88TE|{c#k5Q` z5JK6TaT5)JZwx5n8s< zEc)d?xZixE>zr>j*@w`3tA)egrPFn8<_0U5Jy`Hodon5#gDUitUawC4qHEy2sx!xI zXXXC?;0{-s#asSr46gts;cLX=;mfQUwob__!&9HN%E{TIk z;r=qU@{n!f550V^e`4G?QZ9{wv~6XNfV}C;E6JF*=fO_AlFLU4{J!ug4MR-xh1Jqt z)Tz(&iXN2Q^`E~bN9Ll6Gi&D+VI#BEw_Xf8NQ?+>M*|S*T5E2S&$cXID_sG+x z*C>cNhWsljvdE?!#hcKhW)mcpC9H-MAXpFCTv99g;kCCY8znBc%&afx5Us0;D>)Gd zzJ8A<%KKa3CUZSnwB**hGuJ0*^~Lpem#4}C40usfyB1+%CmXvfsfF0Z9A>J zY6|H`d!=lea?y0-DBXzB(H@aXB5mIHvUv90kl)#Y3mnVqEe*z`1yEQ;$GeZ3f`7T1 zV>D3mWovSbrNL4CREJS!nY%kaM607Uqa*jaC+S(Sw_&FTGHFu_%#Q1QpMFv~9hBcO zRM~5P&S7OL3dXg7GczAAVaUPW!baTdP{uiaP1 zYc^h4%$MM9)n!RHGwxCaT1n4;e9Z z7#mWMLgU;#(QtRak8U0C@I6lT6ah33tP9<)PW5v`VRdY%l82L-F$NEX{9mFtI%f~A zG@kL*dKm4 zIy39)`hFLikldFy`0w{MuM_HF1mhh^3Wd(Oo$p#1bYn^N#cW#2FK#1yJzgCtGU`?B zgD1wGi2r!7#_d=FI(6LHy(lcy{w^sZ#gEF!q@rvZO?)RpoWo+~kb=Hv#}Y#L*?+Z? zNfRWPV{knL$c-&pSIuGn zP^TyS5aTnrwUm&rY&A|KP)3TBzAQR_*i)$xl&#@eM`%;((I9K3^(IYgkFY(e+>}XJ zxGIZ(sW{iArFmxIp=;C5n|hv*Ij0q%M1!;HmY1-xJs|t?Uc9=u6gd9U(|VJc$Fwz> zg+sk!KZO_Rj9H{QM5+hMXP)dyIL^K?-LSIe!l#3GUmO&)N;_&ouCcS=4=eXN6(J)km($N>cv+!`%S znIOKY_isimWpLALPVzS5%WXkR40{F)gn1|L^s2DFer#Rey(<<)Cw*2VxoV)Z`>Umq zMB*~k^P`Wa!X67j>Gy#1tpRSj2-3n`d0`-Bvu|UIQ~wB5XrssD$+LHb2L_6pj8%%mPI-*M|3Jxhu06X zVdty2BevwX{9<=OKfD^d%x`G8Ea#=AdYKTWZx#r*(Z3HMo(%x*v-b}11_kLz?3+`+ z6aT}ghPdG5UCZLtDkG8P*+P(7&V%{6fd@O+M?F;4`d|cU)OEQS+b`O&WRW>Zdtc_$ zl3_#K^+wq5vNt+12#T|P;YhVv8-5Sm(YOSIY(9zy!uD8xrSFY9n9 z`KJch)gtQZK!PZE?Nf%O7lh7UHCLo=_fRcsGkEKxQi5tNKzGTUCTMLVNPg?1ZC(Ns z7O*v+2k&YVwIdF9!Z0N;>r!avYSrxMm*B37R-6=xm5ef_1?u}sO6__k-1(zpgOQ4y&g`G zv48&-%=K6DH95Npk{N1+FriG<^PjBvvyu}`b)9Coim>{xrd7WnbHpOrl&Vdpx*BDR z>?lzc$O*=&vsTT)2gYV;d-+Mb0f&VIQT0I0u|eD76HRm3aakGrYV@&hG}C6h?0HI( zeMS1k)kuI7TBVg1A4AP9AwAp-iw8#|_kIBQ0OIE0(sI9ZiwK2TW$eGs3FT3I!Io4Ds@2?!Zs7!tq#db zkJe=ttKeY8qZ&qtKUyr;M9rD`-?LUGZU4g*&#kZ>`yT+DL1Vs0`V4gr>3}#HNuo1X z@cZ$~X$*>}O~q7#lx6Ph#1g+j>ex_)5=}7Wc<9?RN>Ec1^M+6?P@sMiJ@|i69)Wdg zSLLNJom06$6vh)AbQF6(b;M=)#HF(SG;DuDk0B=U*Sih+f9krOlfW6MoLR6X^= zLh%HpQ{ibO)k>#-LylLFyyos(n*afr_Ydg7U1C7G;l9B$P6v<)()laK&nWZ20}S5DQR;ZyDT2 zzm5@GlVmj%B5j4fu$ax4&uB_P7%QPOv>p+*Y<5zW++S`oM`2cU_Y4^f2Gv_2mDDJ= z6KX@KNL05wfS|A2g010OkBJoK+6EI_X=y|oN;IC1r~{`Cf{%g*t2TKqyuDA#4I`;T zbWVR}9-$E`5>)6A&XgcH=kuKZ0FEBz6eUEdTaKWFkli{S+;p#_rDlk3&$}u~sdTEI z@F{_1agOAsf*RZ;fv??%#x*z)EyZ;49CEwr;1l2-jS5SHT{I)LN+19*OjSW?MhfAy z!!&i@nOaZxN$K~DEM(CX>LIBd(l>m6hm01IhhTd1V@WNDm8TXa#!cUPTwt<6cCX^%FhPfem5Fl=!5Ux2*5y}9`X!hhV=E-981o98J?!r}$s32^giC*ln z$Yi5XT?+nx!wzg`OxMaz1?F`b%FV-F$m`#U`O+0Zvp!M&Q-(Q&a+-O5!-2Yj&>bzS zx|p6bXhm&;gm;1X;%9O+AIqVrZoP!yUZNNaLHXqN=j>yHTGf-bS(oOU$+yx(epc|!Nm_#PV56K zi^M7rsr4)Hn)l(cokXA@Kp)qF+~YzXn>V5EI24%Q%l6}r1jh(&hV>;x3K2?Ug$43c z{KpaXQPeUR2cbAv&ZirB3GO&oBycD}_h4RaMK{oU#tW#2+C8{uQ0fDO6W1?h806TX zo=rb46PW4(C7iQ5eVAm^XKHL3{?;Un=p{^fUf;mswK|~5MpQ_zeiEW;ZkCT)Y5xFE zW);e!&=bl}dBbWuro%z7KtFyWoao2&jlNx7{{X`a)HzvT;Rm-YLQJ9<4Uj$&mLYPG zv8RwS5lV{n#8H((Qq(wrkzDDAIuj^R-5Skw{kTUa_>T{^)6Ni-EN9Sk{BVM#8AZ0@ zqKb+UT2)x?x*gMCYPH;fCYgH|j;a^sq!n@`)MTU+-JUx^{H#t~Ik{?WUycxpj0fdp z4-rYI?Z;H=GYUkKDMb&z3Ctw`%vZ@KgjW#rv#3;Gs1ja{=&RHc)^c5)2?N?@G%+sO z{{SH!o&E-S5Mc$?{5o{w*5~k^b!&?cO^NdnGGtvN#=6XK~z zr{9M>sD#yPUuLhL}Xb$&VAJc)pY9f53#T0FM*$1Y^K_3t|BD950b znD!jXRcnsG{&yVIK=&MU?=k%p8h+PmKt5~D1KO6+T6?j|^pVrI7PTAmZT5f(id+s? zB4`tXY~GiNUhPFa2CS55aX~){dK^lnP#MK1pbiNmKF1sc;?+@~W(O4bs)UZc*yD_N z)ZhS;a>oS=klCr$;H5t{A(%<$3+p4zQ+LNR*tijIH9unUbQpIhEk!6>V{lwhwJG)1 zwQ;5bP@m-rJ^1!+%fRW{=4*-OJ=L4dbCq};bn;+0Jij>&Eg%gfcMNJ}6;l8l6aZ7HU$L0!dO9kO)1rt};E(wZi3w($v^? z=H)obmC*I{<~zSJk|IA z032|&1`ZskuXZ@&3XpQs_T!GA4zv8Y;V8}6__54vqf>@|SZ2e!yNXbus+KFNfcHyp z8(!4l%=Z1k94v7qYSI%?+l}?Rx)@vWsi0vRsJ6vUK+`(m(v?7!x*MV?3P}QnxK3tC zL1x_uzd9oc6jRQ&jED0m3=fkxK-?u+7Y1}sBHURCsvB!aKER4&C7C^0y2Q6cdbZ+F zp~QS*f~?VCwE{o)%^35G-y1eCUX(qHi3Ez^Fj>Xa$&jRcCn4L0a@2cZ!fS*S>(Vft zG<8kWa*%r3& zB!TS90!raJ+f=m_8{RSkM}OIgdB*;lpAb>HCpsvFgYD^oRryqZ>6YoJWf!ZXfF-wx zC%V7Wh-H3tq8s{srQi&g@2fTQ6+_VFi79#UsOM_J@IsqNyDoui)lpiKpXwv9w#ju2!WP}6%&*WCr(!6hkG081%K z0Q>8Kx>o$hE0XX>DSonheA+!P?Wa>Y*<|i`VM=jDMv7tDV`teKWHPgLf0EBI8b81! zjQzicJ);%2d5%wr-}0&3_+rxADtc$hMHEQRxJH=+QK&i*+D3hY3?xWTx+nEoNX+*A zxU0)y_7QtWhu~Gm89$N4G2$JEdHqQ*g?FIZ*!qEio@tpQdh700bv7@pt2$ z!8%5WuApg_1?q__bTUSol&E{VaotjI6+Ey=9WmWeXbB1Bd8j@A0MUf%9Ye|Y))gLS ztSf@S_$`nJ-pDxh{>F92Z`xryy?W8in{pAY%8t(u-;KeAiUw0)sL=!N7)p$~XqISx z1n2I;)QBAnpb_mOuwfZBq3g2tmB)K-J9gQ$tt`v95)%6ILIQIn(;2gl zvEI2vdRh8zTh7ek?bXN5+!1n-w(m=FL#X%yrLvXyYuQncm%Ga=pDoMG_ie8RHmsl|z4{{U+X*IokD+e*Ki@WOTdhXHwN zz>=LrlxkzkS9TxP9An8}^Pj&B>d1F1H}X?jlUnwXhI27_8KUCTNf~?b$%`dGB}P4h zryWs-hAPx58zA@MK6#2{LUalMeYMXMb!Fxepj}X2%fYAbz};DSokKwYkx~s$W*n{q zpDNx7Bk-mP>Ien^h} zw0BI;@9!i8u;-B4f8C&Iuib@?Q{W@Cz!h|D1YJRM!$)yf#*<%@UUn#7VDA zIx^5=kANy-l^T&!J8*5bDu%j-L@wPq4$gxTR=SGes9jH`is@2NN9Cn(Jx+`$#HqI> zC?!5|)7_mhCAm<2*d`#kFeax}6fj3KKuQ#07iKMW@z>diBRN!ijY7(GO6oHmxN@2u z(F+x7@Y0p-!7A|EmVhaR>k;(B1+MdnE!`!#X;9b7{?-e(G{G)gbTQ=)`~>dmP18ON zs}S`OBqG`}mK1AW^!xEMx`@@VK^Xh+-msl8VI68v$N`0SSs*Tgj>=aK>zzPw8$F)< zcUbO#C`v_5KI}iL$ogo)S5AlDi8_dmfsH-OCj~-vBv#S6NTwr$)MVhOL>^i1$Jul~ zTL>{xj}rh z-TW~RRPI&?8chaS5Gu6+l7Bj>2s777|P>tAj>4fJ((AY9YU;*~x8ja*e!36co67>+Vw!A@nw0|xK zjOsYqDX9ZV{uoDb5%mx!mtNXnX-1=>!89kk5_7T10(olg!x;XXG8_J3?!rnn5>cVH zg!f^?O|i!tr5dYV%ymWOVDuP^oW=A| z@<;Pyxrt4~bx>7v!c?22gE|r2hYQm|kjSQIJR@ZNGjz6`fKGUg-5#I}hSCjG1wYff z5fR%=9W0=LK%wl<3{ykp6wkba+%UQis6Umf;mVj9okMRY`#+92>IRU6kIkQbF(*)- zFi0?uFbbvBCcfN5QQ)X?%^-P)?>Iq066=Zl#QV-Tlc;H}RO^K%3c8hLMRl!m$0i6P zyqPIk2}!{9Vui!fH33BmQlf_(aZSFX&%8Yo^PaJx3b)4zJu6wv;?HRR0FZ%m{{XIaiSN0=41M~0brZYjniy&t6;e%M|oq}N_`PiR9evV`>TR~}~PD)BcQs5FOM z8F}bOewM{?-174ik|5P-Svfi79I6tXBMo&O%7H+&r7G1K=ahP>OI0z|6xpYsBUNoE zbEOniqljXgBe*CxHJv~r&lC)^QVBoD5|Ro{+>(L{E%6gh(85hV{6Q$tUQqK_nQ5tL zGS_yP=&1ZK!R}$`*DIWobj(shy6I^ml>0FRqeHpd4z47}PN^415AvLb8j(8U7J4Jrv^$PXYJu_Jc8F8ZG z%;zn{!%r>ADN@<6TV$m{GSq-5Aqa|4&=H$c)Z@8T#K#NLd&nd`-&=3)}>}iCmzRY5w{f;!1BDS(z!amB2#do z7bSeez}*1QjRfcHrVHkY40r;?X>k;Z5tLG{(vm@^*?}%4HAXtc*9lb~gMOBNwISZN z;(a!9{_AEf?{AcjAG^+V4SeOhPm0{9T_6`(P^(onC^XEc9zor`y>is~?W(4Fw#mFg z?X?+kJ3}%G+6hnmBhfO}*I-^t#{3`a- z3N+KpJ^10Y1ds8YMRenapI3UF`^CtmX<)g3(w3CtciNm9GMoPZf;fxv+8$nDf^zmy zW6&WFNshi`#!W$`Y1@XiKsOLTz@f_x3m#i0+4GyC&eD3N;I$69BRmDUPnIFuErltv z)?8zR#eYzubSkI2BaDtzDw{TVuhx2*<3>vyWydU$PcFx61a+tQHu2@RWck^+hr)+S z+a*T1cVS~OB^rQ$uxmq!BI*Zn2C7=?Kn9-NDIY5XT6I%8Qyd}ztDOZ-JDCnq1xs=U zVJiEMA^C{-lDiBQG3Aqz58Z*Kkrm=TIJ07lHt8z(kf0B4Ao=KoM-8C*U{m98*SEHq zcg-WdQ&7jqGK`e98v8NNIZ!EM8~n2<%RyB-0!1+dvy}+`SRJ+ks2gse^&M%1S*ers zJ%WlY1$q>troEIoR}T5L<}XEg1{<*gK{9lK>^OpXs=Pv;?Tr z5YH_?5!|YyiamBI_{~&2?eM*^-0*0Ci=9*rT2$jpQuo{6ittq*d8Wx9k9aUJ+dxLvptA zQtqyM5R8Gu-Dlb_mbUeeVAN5~=uOmH4j~JEe2jV99`xKf}81I#jIw1mXR7 z`3kI0%^wHjQmtCleVTLpaMzRMj_FIqn+ZzzQ)*fO?aGJl!()=)fT|SnHZo+21i__y z$QWOkWNt8TRu}W+o{YBGS7B9Y_hBnGOrZ)NB)+PtQyOe`riA79;JbCH%|tHh(Ed0*JxT_v#2hw9MD-nTj%;iof!l0U0(0*eA>5)S zQKJ&BC;{}>szK@6PAG57Qo|i;7eWfCMg^OpZ*D4qjUc#yp@26=-ZZ_%rq-){Bis+C z6}FB5;k7u>a2>sDARf=~$r$=kLyl+SQb7YMbU1RzV?JCkF0ztcP>jmf=T)hsk}}Sio;O`ic4~w-!;ZM6tvXAG zs1z89+|-wrf>emcqL}2yt6_4&f$LG-hxG>XF-8=1y5OT(eb^n`CyY~PlV7X~RKHj^ zKu5!Ggp=7TD`3D{&3FP=xh43}Ab@bg@=%J5e`(L(q4imC*9+ zBAp8Jt zJIs=Z(QI`kH9q707<#r*zFl4TkE~15KKXTb;X1<%?^p!!4f5dodNe=vIxi4*vkd9o9G(<$xZ{OYUiOY!NgErm7ix zjwR|7awFw+9hAhgx(&;y8BciEgi|KEX`x-%UUo&Puq#GHQn)5ENS-dH{#jKsDn0mg zE-Gq3ZitInHAw^R#HvVVD+m3*rlo=S;nA97!vY%#H7h2aMha3*2yIAB5TMq+(kY2y za)V_Wnyzz51f>)`gDgq&2#MNaYObW}0TuRB5yto|p+KVMy5Te@KW+$DSug{hYOCTo z^aPwe-jxoZMZoD4)ux?rJZ?IQ7cDIQa<1Wt6zI-1^4I={5_LqOst~;OpK!wUL8x!# zB;~G$0`-_x3>2n=#h+=!u1%266f#>Bt!wTWB#waWwv{LXbdJm=xjGGT)Nw;isg$M3 zDO0K)QP5!cT=b1_1($iYgcQbx6Qp+G{Zav&F6gCds^~wIWrTMav4*u94^Pxdr@uTR zx-rEGjyma49S3#|h0R3)^2s`h&$|XGRSf0S0~M|6NNweRXYs_bl}@3qg!f^>8+kw3 z!ks`uli8je@)6vp$w(j*k;9{ZK{-$-mtOo$);pBK6F_R3{y2%VBbpvQ^quYh*2|&Oe5_`KbBD_Z^83`l;J8$<i-atLrgP7n?mG&G-)%G3Hen!GQO$tc%A< ztozD6`0k=Rm4b%A_=S6p8_tROO{jaZ5f?}hG;5il@55>*Ocz6a!yHO(l%B-UVFz+E zZ!KNp2z22?Iu1Bt_m(}l99kP`8g%FH#~nlDm1nE$#Jxc%9Ivwr)g3@|pr^LA!(}?1 z^5B|q%9C`EHXA``Qn?PeK`063`*Fe<^2fIfl*cHrz;C}SGpNEU3Izo*7=~zj zvBJ>UGJ4nS$5d*zZ{>e$2~ue#;BSG>q4rqgfdUU`G{d7Y{WBU>{J3{eh!h|K2(5dt z$2yMXV3fluNz)7f6Oh7lA?_yFc%Y%d+m$iL2E5N^1sVw3nVhhKrfh{Ltt;)v9B<|x z>~Sw6p^)JBVF+Nn&HU8UBc3{eW`1d3$S|}xz<8y95BeM>4xzAgn(9`b?C^rPv+p#2 zJ_n)NfqCn#d?(m&hg8ZRX--h20&>S3QU3s`m;V6CCpA!JV!;VM`->wMUBCYTDj>Uu z_!;DpiUp@SQD4)KTDV{c@0gaT`+6kG75uAt_(I?6KXbu{uQb6(%GyzbS!R^zY26q}I z>4mPBICeNMTyJ;S_FH^mSLVu+)K(BXNn6TFN8f>D?n+dQTMJz%s>zi90D1jH{S0g? zqU>9*)i%{`?$WeBZ-;bl;NvRpScB&2`*D45ws$L$QjNDyaZf(v+hHzQO;mmD&j4XS zp1)N-mbcx#ySCEx6|*kzmNf+p#cgR~zs!f7N`dxM0#9bow<Jw)C;*OCVZX0TqUhJJXcj%eF=wdC~4e**JYaa308?}T<+FP#Kxf)(*b`0p! zfgk&V)qrj}+jNv#`+{vs-PP%7isn9KpO3q@9I}xM#$k1*K{XT|n3d!&GI&~Mnobwy z5#1I9N!LJf?!KpRljYmq>J86h-U1Hd>mlK(uuYRYtPfI)U z>%=zB61#5Q^8zGEjo;-@g#yS~niQldC9?E|kPvD-v7c@?oxj+d zJo0nrJns)vT*J8egL_4dZM*I!;xLO$J%1|_EEas0T5P%EAPSOCQcgWfPV}OPQagPr7J>w zZIrFGXW>#!K@{x=JUz~+BE*{DbobCltBzOO{Hw-04o>-*bJ;tx>cyV*wB@*Fu;S$_ z<=RswNm|-iUYclCJH{|w$+X$&ImJ<%t&efJD~c_io99|3MO%%%8EmZtg@q)H!J!%B z$rE*}qRqIr{uP1SY zSj_6tVVFF^Ku9Z?6e>@<8qV3f%4YYwe8&@HLV1}lGNjeSlTrnL9C|go?Zp@@yA)wX zFuJC#kzTn|59%OiX{AG8+KEW0rA}SBV2R3t+lxz5U*X$N&hCdKeoK1`T8Zmb9U~N5 z<@^l)`G={Up%tjxGib)HaqU+0Qk~Ho z81ihapIbLJ?RrqKwi;3RwEH^YV}C)np;JQ@gZYU2F$c`DE{XGYnbknqge}Kf@d!rqc z8BXC#Kxh$@?nZyZ9o4uo<44+MJE~WRA=c0bXh0v)iKBi%{{Sf#1g*&PqsKW_{=#u8 zn{SCMF=6Bb`-;`pbN(*s;x{%+^6>HoL+N&b#W=5NQ9sj$#^XmTNI2S{=c&MH-Yfhu zVQ&0ErEY70b*CXz1iHMxvz8_K=ndq!8r2zKB>~bq)0J?)Gt5Tp$THh)iYv%b_LUFo z#E`g%oEqRBON@YZt4%+(iGE=b^yK&&J2o-~P9z_HTu1XUTv;NQ>SwjtuLqL_@oy zp=Pb2XTLzeZ*tc{#ci#x6X&;;vrqlawJ3EY3?tnHj?C-CN;~@8mjH4I3GXQsz`v=o zOPe&QprsFfS`@T^&YF==2m`JeJ=HZz1AdD>H939tkh?;?dR4X6&H(J2^t|9Mqpqm%B79I0E$UEXe)|+$NoHv z&8dETPi{V$qrETF&kZdr8iwAsWPkb@IQhQy{{YTt_6yzw^wokxz-xI3QOw|#dPQZ) z&{=D5vWC; z^?ChYSa+njg4ht@6JDqEMm?|Tt@k`yvlHF+mpY-%2!JKW8%U_{$ERCqo_TUKH+iaN zKFm*FNDS@j0DG|dlRH`hM*j7o-fvZQ&ori-UX1qJk!c+%Oh%L9 zcQKxvW&UPBe1-}bMt)6c&{l&HO8Y{_%K=l#Z!|E0?-j&9H0nsL0J(O!l$O%+gJ<#z z0sJx2$YLuL6V}s;QkHy5R%G{+hRRe4jhX-{3m}Sh!yW1oN(rz!ulX?#RFvPySAHAS z2uKghbNLUv;wzV*fj3PVNFucW*AeyPM?`D_@)~-7r*;SHOj;m7g_`Bl3)LG?P}M!Z zW*gT?ky@%VOLfkJJo@4Bn;SsWO+ijh zG*){EJ-8;_-3Ce;)DaM(Ea@bqp5J~S)HyIu+v6ZrAg+|CKj?AF=;P!v+jR*|Dse4A z7%r74cu!Ae6RA_E7WqeWB#&vs?c5x~u9TrF1y!e98wNFrj;e?R9~W*J%+5gt4$^(Y z6Gl}yETyPbAX1p0szuWR*5`Nd$8}EWnknZM&>v@9H|B)t1)MLE2vrAm8P*aosqyoU zRm1v>o`6T@RhFs_%&@V#L(>5%kg8G5IDb(I(q??82a8|ZiE2j8NYk3es`iXJH+2}| z&C9eP1D9vB1*?PHXk+JAkw7c#!mF(ea7-xV#gay@ow#0hLqM~V9Z=F0L4u)hWR`+z zl2!3n--LHYD`7Iw1cO|#L?uE7nXi;O)Zy^Bk`xCKe8clPjLwC394}wkEf28Tgr6Mo zBDykBsk&QUbQQw8;=%@ew<}FJYq&arq(pN`)F@BGhr;y-at09;n%5leuK|=Z@~Xr6 z(H}_|F%NP3se(Dsy)yq3+t=ilos_>E6 zan|n;yC{s5ja1H*$J$)-N6eK1Q!2|)uXY}%LJAo2Px6d9GN?)Sk^PbP;sd$>!ICWG^!`44>uu@Vv-ljZFC;*ccpLPrQ%p3~}0lc|hh8aT2)daqw+D zDThXN8zoadNbWdzwPrkw`*4EP&ys(T;q?dQX8uq11{zMAW_*wAjCDn$3QW{G3AVL+@OIp=#z(Yu_y=-qASxK z&cx+LJhk^?IL@Nf6UiO;T8UHdBzNPgbxipl!;Yx;77w8J;k86&{$IBV%!hKbV3F9G z;k>~~0wubd3_7E_M&4icvE0ZF#h-b8+$TCYL7yeFUl7Z?2(BQ8e5TDtk(N8CjxirY zfkm~YU@1shNea@WZ z9KgbJqq$l4mb^hzwKnpH^M4F=M(SiXKbp8gNE?6vr7-?coTfu*r%K^De1a%^TTgJu zNlMjyJ9!)M8NGDk-mqwZe56ysR~b)STHV)m-UJaK~)| zB|1%OTyUKCUV7=!T$g$5bhu0 zXOcF5yidRSaqBbSk2hkRNRg-RZKllDko-^F?Y?7c6-i0BF;ITNif!vmSwCSu0GiTX_`Cz>qsJ#|IGXM9>9?GU$4KI2c+7S&)?f0CdYO{{U@7 zR}#Wa@enX&IINc~mWOyq{){q<%u|1wWLM6+sek&m(M&KP++86cP&&8AVL1Ep(%#aQaXUz0R9+WSos~zCBT#RG~52m zx-mOw?hA(2l$8RBR@u_6>jy5_jyHg_cKa9ONyR_7KLE{joPrYDq3$Wu| zH>-TNwPTU1Z0v#Hlg?U!rrJJ3zzOrpPTwKWPzo*}- z@1~w)Ff3ex>--hHWvLB(t>wf!W{~>GNDN$A@Ft|>9tb${jq9^BuH3dk-bMrXw5WiRaQEnqN6NR z+uw&Pkz-|#Bq~RUrGgZir1ke=MRts1wN!8i)(_Q7)K>%go8*2O zQ+%m(<*rYMZMW{*j7U%K(Br#$Wo?*vAGzzd)sZpyU1ym7+We) zQ(i;C!~n8L2{kzkG7eNDx|K(c*W#;1?T8XCcXh*5m)6|olKgZQ+7!~HhYwP4E!>-t zbV{S+2zatYZ-BO{uMNO&a!a2H@C#0`xT{(TG@_~D%dfityxWzf3HmTgMnc<@Xe({r zZnkBqf8ZZ=wj6MvscoTTC0+`Q#w}xdvDAtVgllJ1sEd7UJh)Pz$Xen}W!19tYD)E? zXj^3}N|JL52BWScy%^7qs3yhPMQB+LHT8Asi;r%Q+}hmtx?2%cg$Q%+5^nL_N)!r^ zq+Sf6q@+-RL)>vopS5H3r1@27T>?y^njdkeMAUFyeSy{d{;;tV-UAW#**9S*39 zdsUANa#52LU+8N~)2kv}f+bH4xzc0M#p)cP8EE z3og>KNsOqL>kLI)BRdtBl!s+H;>8xQR6HPbIP^~1+nnulGa8?kNn!T2CUNgn9P$BSey%x3x@3Kh`UG^%PxQCvuLmV(46NN^PpkUNQ~#}Fm?s3vc z8kBn~O%4&_sXhSxP%qshr0Fe_+)BRQ%s}vhdW_HSV!1Ruo-8NIA-zUycLb^(DhVBc z4wz4pi9Bw$7ghR6_@tEgBpP9UN(3E76PxCM{?f6}5-Ae(kX+rSC{a$R{{Xab z&XOHaAvQvdJj3^#Crx-AQ$IA~g*8+j+%j=E8L0`+^)M|CLB`~xKNsVK9D{5;kwZ;b zbSE0>Nz!7oZWCG)P(I8$S5Vh&hS_xF3cLF~n2)H%R@iZxjU`F=tAY90y+HC7yp2EF z$8)o~K!Hz_lZ1N)JDOf&6%Cf$qMV#6_nKj4DK8QBmT%r(>56f1E98z_4%=TeY{7y7aiA5@ zjCJ#_%BMU2t4Jf$146zO->vok0GhmRzMua9%_uvGf3|0ulppZx2~T0pxcV--qs6jN z)>2jG6Ya&CbSaoPNhdDKRA70%Mj-1g`#;qfPG%Okjmc;~IOeXP<%;Xm)UvZ_)c7_k zDrA^TAGo7>)A8l@4Bg*v`8m#NaSKw8r0F2_#XkFGS)U#z zleeE1Ew_N00B`U9;6Coz%eaC&89;u2bQ6P+kd8gcZ0?_2)>SYivT zp2=uEG{be?U$ZvLdfj?E;k4Hy;RNH$exO~9aUER`RqQ)WIH)N z6#oFxh3c^lyo2q-dZL2-x4-eke9Y<}e802s#CnaCYGdVa?r9G)BalG?qqhp~$(#@5 zkLAPqgJ@`D(>fo+5wy^tK1vtc(+#TzKKXR^9Pui=bpU*^?ZmxdC}+wY=N;Dxs2-Jn zE+&n^K*9%oa79D|P}5B7Tt?)@1HjzO6wn}wW-`R+$ zl>kX|?KH#cf@10yc3g$z(`iCYN3`N67g4SiVpEY{T}fYD$i%$#Ba|o?R0EMyf}OQE zRdYeKUI9h2$Un_ahY8nW@<;%svJ|ZyxGFNK+^h#eaTigI0a{v3d^M+98sXhkNz-6D z@;VtF&bS%f9n%BQvrtn?^cbDqNKTsrQ)=xruUdOC94F$2+<+R@p)~8>aU^c&HK3~2 z6-=puVSc1?jY6kMCsET*>=PTqB?=%V=@ixWVe0cEL3*4oRf|JH+PeitCRZ8UfJ9wNTP6FCWNHejD>hiJ;mo2lI^|V8cFSRSiQT&Y#ZeMKc&@%{q?h1Ag5N1IFb@ z>4|#s7o>b6Lq=%@RO$^Wj_XKHk*}8*`3K#E>qzd92@wxgf%`wJ3)YvJ9ROTY6gpTW z_F^S>QM`!xEj-};IJ$LosRO1Z>I#ExLzJQei$g$+{{YZoj%*?+P&kM&0)c*JzVxJL zU%v~^$OQyBW@IHLg}z5x<$_aV=ITI&vQ#LVpUaQBPnAOmg5!W?O7;=c5vzzJq`ZhX znt2O#@y*|b>>ERA!`(MtSKV=}b5%NPu$s5qQwjO<E4XQ3xOcO$ivcz_AOk zl%(lI0hf_0b4e=Ho7WpPP*iqw#WS9vvx=oa!({ap$4XTq2l56JsL8nz_mDB&Mt34J z<)7srh8Q`cUnKtkE*}}tkEUuPtG^C}{0`}u`E_^VI-SUU^6Kx$RPLFuXZtwnjD@p* zDE|N;{4hmAx+b1U?)-2ZF~V}H)Z8YYm+`I0M0WlM52__h&z3)z@i-o&e2CjZKbHs-(?H%) z_It3kWL&KK%3re)bwUliq2GyUG1)fqlkfX*BV&|kXUa$N{{S3wO&!RM!Zj!gRO6Fm zo}VH*<5hXkcan*%H!bibU+T;${){b1us*fwjQOb01c zM2&-f+({W!?oiNcEb_whB2Gqq<$G|XyvW9ZhI50^$CkeD;fGY2TR_?q+Az*$a<+i{ zs{U*Ut~ALybvN?U-HExGA59zKDiZCg8dKSY1fjKY4^W>&bh3ML?!?t2=`hY+*oE8+ zNXM3=ohkQXHzBUrIvb!l;&{%ZdS-si2<~bxNZ25rgWriNCrlh9C#P;DxOYt2gA!Ek znlW4qcTB}`A5fNtg>?S_N?|#@r*flDH~U|P1?oRAnjjxAcVc+XgB_!0*Hr;ZdvVU| z7arQwq+e51s+HRXwx@m>+S(cX8zf#f`?gt=&_65#K*s{{T?WOJ1Y>68eJTZI!tv z+c)OnwptUkcHP?9CL`A;<6L7t;u=~4+p0rk(MlO-j~UrJD=!anS01scy4-t{9{w*Z zc(Z^uWVqe7G8|15kSmX6jHqFv>UN*#xBmd;-;#ZsT-W~q#tq{W-!IaQ?`ixeqS=LB zeqUxh;ukbE*H)ki^D-JvpLQSA8AfF-I;!dfQ>Iwwbn5|>H1Rn$M|N0mRFOuY%4x+@ zsHd>uvYkcgf%y)5e&dKbqq=6=Ro#W^ozo1kf~pmY0nlPOPNcC~oFDq@LsafB;CQG9 zQQXsweS*&M{+xP$(vR~`-TY5J`i{RYCn|Ucg?KXI#}?b)BJVA}&gGu$qUEAg;LNz~ zCF0v*#>&S~okK2%9+RJT{{U6;tq(B9$G1&MnS4L#5sH`hKHW6SD~MKf_9);^e4i#t z?A!Ar4g0WtEL-;8;9l3c#kg)(qd05~L6rNYa%n^isyg+rW)H@d_VuVN?Z$;mi?yyE zYXuSNd?cV0lV0j#<=q3d4M$`4OS1?_k!yXVq-R#uL5j&_Mxi|?idk1A&h@ido7Qr_EkRXA0dB-`4l zC%+#xX3m$I_MY93@FC)=SEd$s2GG?ll`BX{r)DLU$`N$+7NA02p+xn>H!m*$o{{Cp z>RWwk^Xm_lBNwVHBG&+?u_cRpYa{P-9@*O#pUOn?uHd=(?Gz!TvK;ditkcumjXWho zJZk%`jY~>W3cE*2959hUPNQSTP(8R(HN>*qGrD#@-rJXEuluqcnsoUy5ZqK4Z6 zD(OH8ILB{zmu_9GR*2i|*|sg)^*u?F)5v^XGT{m;`6A5Eock$3RH0I$>PC<$)YmL$ zb1|>R({4IY(qLxTl=PKbJy62%TWx>}ghq}BxH6Gkc;(4@-@MwT=|YPx9+k{=!4j2D zLW^=1kxI}E^$xgb*+QV-IlK(VZ%>lqOxRWQ0 z6$M6JsY+U9Zdc*U9V^PCx+3IeB_9%+2ubZKNbJNj$kdKjn0O_&fG*2zJ){K!aTm-n zI#OYTOo%80-e6C&`mi_56D(;58o0+!Nlvrv!+vsuC50v&ZsrwF(E^`u@WZ9JI*>~W zo+Ed2@9nX)lWQB6>fNPxxe1GDZFSWAh4exPX~44FmkBa_O{(qMxN4UXa4^fs4!h$* zs8WO7*ApxLBvX^E1Fbo4VHqVkWN|vlj8HV8aj`1k{3#VBiubIH@zcpohT~hDc(I!< z5?(4xX*zp5@H3k%Qq(ML4lD!ZsDp`>NqUhBy<~ZWlk)0OG@!$jnALqTId=B}&MJxR z>VJk7H;IDEfKqa0RqPq#p?KhSAh=YLTOgi?6I_uOG0xAb1L`PC0}j2 ze)SA)exJYhr5|zs0LEvU7LloyC)jcH8oGG>`!ySGCXTF*l{sMeN~Im^Pna;vVu_&7 z4;Z09*g|s4KHNuf=n)HTF}C_bq`Yh5B-iZ4Q<-;U`3>egM?TzF-Iu7ahXF<_HIp0~ z5vW)1#ddzSrS2|v)l2a`lzLrfU#CtQCn5U`EWOCb%A2ge+&_8kRrHhJmY;}!v@p)0 z2T&QemXx0bNa!%h-O!GLtW`YqXaLhp&wd1ixrpykAw)LnQ(TsRn+qpEZg{hFs#8Z( zbwa64K-2hPlD#Cf8slzUKtPg8l(Jn?x~int@4!)%o79%D;It3JWo1B=<=u%tx}cdC z6%QzXSEhA(wY(`6{{Z290ICY-^7O~he@mbHNAG55yB9Kt{{U6{j#CEwxh@3?)B#_& z9=T@YJli(brpiLJoUv_g;a<=|%!s0LJx&pbYSI;{B9zYqv#sb`{{TuzDdxSlRFL3I zX+bGH3G!=?ANryH00-|qbLr}RbG!bJE8?0$533}nPAJT+_m}=t6zo#EQ=S%Wp z&syK&-I{xj!K!mKAhTKyq+*&G9>W%~W#$ofTxE(WnhJId@dU3Y;1vvcuMsKu&?F&l zG=&8qRY@Mq4LHV{B70MkmaJ(!l!f%@Ph3?>bY;+0!V0`e3a4jGEN(9mvW-BD1-g|j zw!u$sV1FDVgB+NUV^o}ta0`g^C%o~YxnQw>)t z!Oa|FxM$x?6;g07A!GjlMjy__s1ln1jam0%IZdG|WK|>@XM$Dej&NYYQjtm~lpUBw ze@zCOsza;zoFsQ*Kc1P~fi*s)k>Tr!mQx+mFeDXxDX+gQEHA$jIodui1ONhZI7O1% z;xA1ZE-a*J%eY{u-)vd|FU~0$^}{;Mir`ggbE1x_;Olom;|c{7jK-1gz||cJll>yY#1#^(AVK)YoUi?dH;TRopH&9vu`D4HMVos?S zNYl$!CF+UOG+Sq>%M&TFjwVuyfI6D>!K&pD${U6hssJnAoL5TrqUae-y=O82!4bSH zVW>NHtyHN^dvn7UK+Z%dGiGejMPMJW{{T)ScgMKUJeyo(niYzlXu@@vfq`v^)?|eN z`GKw+t}zdTF;P&I6;q+Tf<$5h!7q$;H-p&j^Ca4KNPRX->_LBS#)7(^V^sPsPUaFqr5M$&vN)E@fbP5?31 zkUR+kvkp|KGeEyC@b-PUBBvVsyNgzu6;q)Yvk3jLnZ<~XOFF=Lk>T?>moN=Z_PrAg{> zn#huJW``Ly0+#xU83+E z_G4MJ;{O1#l+4C%M80uGg}QaBRS8M<bq+rHas1fo9DVZR`LWbE`{h^NgsH~=04|Si6n7&s7)Ti!VFgeEQwqyX z2Yw;vWRzgVwRYi-RV0*XXGgaW>I|W;k^P^;3)LIbM%q2NbX`Xu1Ru8y>I*|}Aom}? z45+8xLGC|(JD5}NFW7M>INBR|2fO&<7b0GhHVjg^0fi%#qj1qZ58;KN-kJpYbKHLn zaNd$OO9q`j{C0$jhTdAgmmDsJ{!jM540kYh8@AtwR8ukhaO4Qcmxy2at4{4zDWi1mB$59B zv$-ul!v($-1SU|>t!rOq0+q@W^i8ym;fHk);9o6Y%Y~`Hs79Lh*9gwk6)lx!avhk0 zS1J}5OMP??ejgdsY@<+LD0lss=W!2JV7^)I{4l*lL((_$P8jZv=^J@ObU2TyNUbwn z3dCJeXlKYB`0lWs18*pQHXRw%lcr!G0n)zQMCQoj1LD{nwZju7nl}iiLxpo^bj<1Y z;W>`!faq6t2j*d25Xfy=<%v|PMQ+jdm3LwqvTmuLEq&j^2~j#=iAuB8mv%dt?wP)y zZaGbf(>~Mf!c^{ajX(5HtO(J~OG( zg#gM@X;IUr97#Y0p;}dz8aONEulY&B)OAA9B)F$mg{c82xB>kb^~rEN+%Wa=sL#f=rp_naC50`P zK58=xS`$D>5)^=QJ@|@ml!E9^ZNgI-NZR5yqfaf%ow-UqpYGPILwZ~RV6_&ae9GgG znYV~~g;E2kcQ1NIA@-KhdeH2z(Q1h2UQ{PKZFB_vw*>zHRxmy4^hW!%cr(#c%cG=M zKFnZX^ffm)hFftoKV@*@O#^MMtn$F%u~cHB9A<;CpJo^sZTeaD`+wxZh;N~l(vASG z(Sbs!V8&&B9YYfJ0o4K9gq0e0cj2z)gz$n);dj?oEJ!?Q>D4GDQ*K+5>`;-=Gak^e zwIBAJNXM*wAYYw7ckw*?>Sy?3dA@2ZLaS3+mT6TGE024WsCc4mALUs80J9l;1|6V* z=0aM{vQU351u!#6T8p;%jK!4}L%~jn&LP5t&=u%VQ5p2Zfisj#L*YsB_Hr#EYk(%^S7rU_O^si?=DTjIUu*DHbH^Vh-E>yw`x zuA%;DmJ+Z1oJi@%)V;m`01`gq!FS*JQYrSLx8GV#4z=e`ZZ74(&OeBI+R*EEriy$% zV%DW7jIc$mQ60siK2wrY`I2kwIFu2aB}+X(`!Iq76+2J?PJZ)_I5xt_SxHa_ybQ39 zfaviFTZ+GNxq@j|Di8Z36qgJ8908nDsF>MgQN>guoux@55Zw3~HM^9WJ*0YIpE~d9 zpMkEIXg?!!m$fXapAS~84v1(%g-2eP`J;2W%aJd6?pQJAw3#ou#-$`BId))PwRi;d zYB#t2)=tmZThn$#X%EZWwyD!%wy=D%sTu(@sTlTZt$Pp-XYGodQKqb|O|)NtQ~W+-s1nPbC^+ zy`{la3V~7A1-xwtR$_Vwz;^WzPLCY7=4#M|P@IiBF-LtU%AO_Fh^fS}JX6Gc33=H& zXLj%Z08`e!H{=xELZ@-Gnj;Hv4AP zj|CaVUauqCtQXpT*KB#L*<5JlmGr7Sl+gXy@kMsG8R}AkQAXies)H;{r=aOVpvZQ; zwaca>cSiK+39s3PR9;i#%-F3$*z)g1KNM&#CJ0iRSx+x zW@f_x7p0Wy>;nM&{YfKICBIZq?A0RiJ!4m;DzzFR;Z@~m(EpI7G!P2N?< zm)v=RhyB{U#~(za%`T1CMOJB*39sLa8%>l~xPvwYKHN$|9zqjEdj&~24&w;Lf*K?Z zXGy84sqfDe{Ynka118T&0rLT z_WZI>Z@(V(^tP)Fzy3^*xuxm1{{Yo~mq?Y527e-*M;bhB$8`JKPR!@|| zP@eJIihRA|()dwvi-UD>Vk+*X14~FqZC#*Zs(Z_mq*j5^!CY8~`Aelnxv18d3gzmm zhec2)@h+z4g3!7d{F9ju~Cq#XmP7&6}1CiN>aH_9(gqox!qb*X<7^NcO8XnsSnthaY_``pnpa#WxwOomD)u`t(PfEs^b_FmuZ&ki$pJ% zZ1hS^F_6lYsm-*gjdm<@EyX7)u?2S6PSmvW&};CKfSss51*CH>$BF^}04{mPo^=Ac zk})dAWRG{E;3H13sydE?<|RE;Fg?h)hU4ePYsf=(Dd|E;_x}J+85{HtO$8i;;iW$T zi5=(WC|wrUO=^>t5nOCa7*q!UCrLEy!u63aEds6E-c@RnDcMXdi|FSz8E zHhftD-Aoe;tuYu9<3vcHS*gK}=%^y5+Nl8L{+;;luz@8u*cpr?$e=8tP@?`LhWyA! zNGj*WtND99_?xaI5T4;Wi0C^p46h`K5M5`cCAV;Tl>o^nu>Ke()zTTlp>-&$^3s@_ zs3%PYRjVyuW))lpaR5?ln!7W>wL*aCs060C4p$vTlyo#LY9!E%5!7QkXba0%yAOrL z(H|o=@`rvAT^Es?2Kwt2?!n(TLKhmC^1Z!1m>=et&X6{heo#T|G{e?KSiK{3F!`x2 z5|d1@v%8_(fwV&!NXS_&IN^Q%j=tFe=fr#Sdh@&)xMs|JvLTVs50q;l$4@zzDCZ0{T z;)N!7;{Ni#?ut!mLTE5*wKw1eEc;8v3QTcD>C&Gyu-~`8?!vrjz6cTSwpWPci^EbE zPd)85{!#sSHqPC4TtTJ3*pC6r4~8x1DKD*Vp3y-}7ag8{C2h{+ir|MdZXnyN(_C`l z>T6sV4W;;%_dA|`2yw&XZN9)BUAePET|@)?MhmsIAS=DgbaBPn*46+Ch}Z+Cgb&~` zG&abhzV`KX__0woOK=qOkSXlJ3kKTo1ACqCHhJLnmR)0o-eIvrF70b=N7?7brX`G5FqjD~;w`g;z6yu!OW6QAFV&n||JD^M>Z`#3^A zP*0UtaK}_Lq|N+;?fZru)MPa^@>SpXVS0>{l^=Nu@BA^{Mt3SRjR@=a-9!wl+*XUO;c*jUb|QJC^iar=fJ zQE1G0C%FB?5%o^xX8uv`KXwzSPs(S>Kgw|&r%|$vOnEE*Q}|({dS*POdw%S3-6m{W z1FC8F;x4H_EAla+J=kn-)Q)hdWH;ZQ3hFU6H}d}guzws$8B~ro>R>hg)*|XMSB}lR zk7gOphKrg}Q|Ik)PX#Thv~e4Df?~Zrj{%s05dHPUtBExL#gJ z{unK=Wp7e#^3nXb3@(WK%RkG95YQLN{@3AzsV7V`<@<0jy%Q;Hp33_$hjfDoWSZqq zvkFU~Kg*Bh{BV^zp@gB({x06Wxd%!=VI?e4+fu3Dh0PjCn)95XN;ArhVj(=EJIYO#8_l`0A3> z7ytkj!VjDcDS{Lg0GeiWj6*IYstEf6lACB(b`a_UflkaM9=t@7(B7DyFgW_Y%y(4k z12t}js8*z%$7SDF^3w|5o=$2aHWC2yhu zbzn3kt)!_f6{t}G!V0@9fajA2(ypgjjF*9P<35+;pw_<6vkZ*OXu_vv{)^2K&D@|u zXa?CufBayaVEfYUh~4(8q)$YW2BoMGN@a{ZI2ucwL7Htj=iP^qwi?0>3}cR|^>H6K zTu!@5lTDCPfpzM))JZJ6YA^BTcdw^x4Y~)_a(ctxPBBUdsfVHX^A%o z$(b?yx~!Kb@)6XN@Z38=D-7?ihe2tTQ6Bz01fs{Rzm zb;g8UyB6x7Dd_H>goKg!rkIZ8Q5R93lRklRU!N1yFP^s_Q*Bs?E-a|0#~^l`eC_pe z+F6mOk@P>(r(9=K(PIq+Iw_uFp4{=}jjE&3a^pk8FRp>+de-OstN#GSRmhS50J$_V z^yhCXVnpx_^ZrzdhRQQ~qaw9X{kYUy)f86=3Oh1dv|G^A32^9AmsL`=RdD!5fOn;! zRZU7hFOepl$^#Mt8+kI7pU4#Z0>}KAQ;qzI6$C?2r(YPt3JTn50YI2*6zk#`;bX)v z7$v)u;6X}>LbRW1DT+LY>NFNU5a*UvDWHU@P8AO+DFmpKl>h>y;=1(d1{Y%&!9H@e70lmC!{cS zq|>(o#imcwpv33KLtG2YFVKFT+q)7!_t9e(dqu|bqwhm9;JPw?!@@u)4!%LnCW5`V z`hwN15%cy_^|mg|ZF69%FU%H$zy+uz8VY-HEtzu2tGNzjH6PoRDL}Xt1Q0^B0CM)= z$B`xn@=CQ8gk>fx@mp{J`(?7>O;j}vB;prxnuk0{v^QhywqqfbB5H_a0E(eG`$@y4 zoh9H294ID8mUxMcZ*7LuR+f~Y3HIlT9P5qD&|<-IhQ3lcl^N$+ONA~pvPc!GTnD$^ zip<&mME%9K*uOb@&vSc8!uHGBG6|`E*#SrGirzk7y>s(ly!R+t^e=F=PTJyLdZF&# zhH(KAub964OU}W~f|Pqs8!1$pE~nUFy zkuAGg1QixS>8Z7)L*M-)0j#}4<8C2Olz&MHK@{q$Vk@~QMQIu;Gr$#SQLj@@>@s;3 zh*wP-B0GmNlkTPj>#q?yX8!;t_wt-YE5kGa`6tew2V5h#f@u)Yg#Q5YeYs+>qKj!hB7zC_Vm_$)FC%nX>(CHKVZ!xD z7Z6n1odlXw?XDnqRYrzUZh17ItvU)<4^ybhP*9Bp(ytN^vn(r*EM-y`dr(l?8EJ1XB%l(i_5d+5X+^d%&B|0m zXKLC)LaC>q0KL=WL-Xy?nxxhFI3!1vuE7h*c0 zvfFgXbgp=(lBT8Aw!@7!s*JGt+)WbOgQ&Lw6p+%XT+?51ILz*IT^W0g4%z-hdoc8z z3cWe-^3-}*;R1cl(;qGFU*&%D(6Z&~p=IN~sN@uy)3Y5Jl~=7P@fJnl@IV9smR&GR zawLl1)1nv`wUlWs1t~pO+lD;(C7~1B6>Ct$YwPVfT{@B4k})FOWQcEVP?GkJh}URx z(md9~YV8>eS=h~lxRh7+`oQ##Kz|*V3l+?W5IB&$p$8GSuidhJ5m+lCr3w%*u(+ z!;cC2vuZZ&sU&+JYTT8~NvUdjg_!czdXN>=_T%R%%B;{>w2~5vm#3*T?86C6BrJ^n z%yGcYvb7L&<=cfhu-D5D>^4^FGno732XVtXM{}T5N2Ypl9Rzm}b*2!1C27>)eo$%L zXc)%!VR;!JSl{I`$aL$DP-A-FpC~)N%sZ8h`GaJ@C!#+LFDTh%f`~^dppRuRMF}FvG&sva*|wJ*@bo_NgLD?*8~7b z{uqyxRwDYdPVpD_bbLGHzsya?QlQ=lyKu3L0` z1c0dFT6}>)?8RHSt-b||Q>PFKC}C@(EQXt)uW7?BUr&N1vneI3t2bembySZJ=ERn+ z#c>;U42x~msPBre^y z=+#i!mGccaw$j)m`M~RnYm%d(ab}WLsEp#@QBhFL;x3?EI0^2yy;V|AvlGTO6ePIH z$fq;ghxHDiS#cd7lQYviF-eZ9Xezj_1CmY_Q>o4XK0}?Uq3yvDNsS6T!oUWZ3?yej zr5yzKW2(OmYwW{0m@9}~QIx8FnH2V6>Ch)Y50a32Hiul(W1AK$$t0i9SCs~aKMYZ39<(2QI8`L_1(Peyqe3R_U6UO~c z1!~VHKhi&TBI_KGb-Ogxs06T7Gw3UV9sM*Tlu)-9X5L;kL}CF0Nk8)8wU>b$?m=;5 zjvpM}A5hh$wDyzRiLJuqm<8P1QSlv6xT$JNRVG*o{JhZa{}?km0t4tSh*wwfbMFEL$&*E|_-Yl|vw_YL}ZA;lMES`qMD zQ(nSTi%T06Ux8b9f4%;pVZE~rvJ#kWqz=jq8*gKTd;sOWTz}ldsI^7_0?HFl?2H(; z>uZ23&$;HqXtzT^ua=sg>=z7sekg)jmmi38-0R4NWr7=_RBoDI; zR-(KGPUGTWJsfsZ1g4O^Ihhie9S~1`CXL$o2<%AD?gq3&RZ-|^hpV2$zEjPEq}-nV z?kcJ7r}4obFMe93p$k@H#e)2W-D0I7pn8&^F*4h)HLVcOo5x<$@7q!hB-}zt9|}n) z2^>oa`s8=czwL?VbJp7~H^o*)v>g)n1+BFt)OA@u(T}G)V)RwXH~yAj`-a1CjXcZY z3K>iYWogYUGCsi4X^hRw)sy#{On*#Oe2!l7gy@$asD~cd8c0>b+XC3RhgcdJG3Ch> zN$;S>?+Huv#@CsP$!qm5xak{~=ZkAtY^@3W!^QkDoTQX>G|;&jH$gMyXnIhLGpHHU zOy9`<&*O(wg@Snr{{TUTb-jbR7$1@SpT`fV%Aa{3+5B*)RL{$w<@|BgI;K32?EW}P zol`zn_J14SF;1k@ygpYa@y(;r2$%W z0|UnEsXr}HpXDqniWI^9T?WS9rc%vw$aiIkxrcnXU^Ct+-G=oM3%0@jRbJdDR3nvz z(%BvontOY&y+kh~45hxhN52B~MC4{%WU86;#LDQ|+63~?f8mKn(7|HAmcM2q>JOp! zkUPdDOd$KoC$tPe=Ehq_JfCWR)9k}4cTCTed;b6fj;SRgLG0xT8|N$-3x$+k#SmqYb*+1t$bFph2xqb|93fhb>rSH`1p*>^e89IVV$pD!sUd zGpJnc)HK^_HSpIERD7XOV2vdrmG)ss1D;vK`k~!4W6RI38Uog#zbt{31|#P|6ktIL z70Uzjp^yji72ky<kKh1|!BBs7tyRe}2KG3f0J22cUbIs33B zQ0|)oC?lqLo0=q4X8u_A;b<)k`DpGxjs?^zrh%|~@cJNRnV%?oao*+<3+12tJS&*q ziZ%_L>M4Nv*!5)lLa>!$>*Rgm)BUVHU1({F6Y!e%<%qhk%wAB^QdDRL2NUcK zJdyl^3QQPfswp{i{40hzA}fgJ*VU%1dL88DAI&1jDet+pi~iEN{_pN7yN~`eJdwa8 zwt}3;V8^n{f;`eLs-stX?Q0J0L>4dB#9Sl8bpP&c^lD6hyuO4OoiN{J^PN3hOM zIkwGS;UCI8>Dp3K(@E1@hi|hUtwN6w?hnyrlD=T&b@dJHmUS!rYl?B_J>>O9_qRXB z&qD46Y9^Uu6A4t%xKzqfrz+r;bQ)2pK7xAXgqsy0$Cj)>?ogAaV^6mZsJw>QLFNq) zZWKovhoQGqknGRxz*A)E4Du)SLAEL(6yv>Dpk3#PvMWI`wCUtqIjQs zf6QeLZZe1@H$&Tvbxc&DR%hi`{G;tw7NdGb-zKl+{73cSDtAm6#dEc>_u2hA)a(cAoCOgFoPrM|9hQ_`ilj1hj0s{h!+-~+I4v1Qo;(zIrjwBK#{^F`7 zz3yKF;c58j1bv_kO=sdco|wP#_om?R^$;daYGQs)#H6jJ$^i*}NEH>#PJ>Ldt{W}~M~K{gm9==1E2OOrAHa-e@>i(Q;QS$J zj?7`!o{u21`%4RH)a0O`av+X`97j89QKR5bW^Oa7?04ziySFDH`Y7PN&AaYS-ghIG z=@Bj$M~z*0K$`SM)U_U}C_t(9W9F{pgkzT^xK#QFYPjW&s$53ngsQZ!vlVb^D6r{~ z�!9k51gsw#8NcTDs64^A%W^WPk4??;V%^3458~m&N8fPhT|s!kGHX&-l^vepmTN zsUM=*Pm3X|zor{+hzx*BNjX#F{4q915vSfr9i#5TCfJoX&^t0c{cw)TVvCmagu_;O z72=mONf#K(kpqxa*g}$;q#D+=?Z6p7;Fq|u!v6p)edoCTKLyS_*mcbQ&1}{G0Mc#a zLF<*ZYOMxUOQEEJGzi|{rBA@#;fIl2lTpfoVH%Q_sxqn5gm%#~ zyCFTu)|UPT3V^OeP}d6>Fokx!LJkP757UHOQ0TgUUK=N%@F3C4NYmUftd;<+t4(~y zi3g=HIU%}I(vD`Ieh?p(iY>7nAdiZ@a6L(SLDYtuJ{GC%%LF-4kCB3h5D5Igb~)vu z1!_ciwKY4IB5QRijcB!Mab|!ePsqrhu^6PidNRmW(7bl%3>_E5#!m6|U&VW7beny; zL;J==Qc~lV#HH2)faxgn6rh@dD0{G%x2tuc)Q=CfGHyjVdTK!vJ#OAFGpE|NX2yhx z@BlKAK4SH>mDfR1T1Ric5iQP7!$RiQXj+fQHbNVV)(@#f-Y;;XuEA!Fx0*wjDD2zh zNqhBM^F<4ee<|+45!_ib?feh34*E&w-f-n^d$Aqcy=6+;6Ro#3J+>DfEOs1PG!~r* zLIS}}4{vTHSLCiPG+>+71}rfff>k>L9Vq}MA7(C9jg8%ROEBZwm?aZ0E(RE< zP!GhfuvY=+WqLUm7F+CiRk-d;;fA858EspmF%_oDP{KkGk($ijQ1_ zryK6Xz5eiH$7;-~d1Hvx0MEZ4Le#+CbrEK?eABBU3H!R>`%^_0L^UwjRzol8!30P& zu|^*~p(d5U*X}B4Pi8ue*4wC6O*6$k$(IpAgGg0ISsvU2)C*G3U$(VLOm>QnzWh{f zi}Ihiwc0A~svgVzA4g06nz(gDpB0iP{Y-qlp1<5bdAQs!ILxyRTGD(a%{|26nA%LO zxt!O4tj5`z{%j;TI#&+2wO^a`0A1egL*mdjt)X#i`@kAKmBCl`Jcut*1bc?exM~(V zc4|HqroVW=F9ziamh)+XH(;J9RIx$Um`H8+Dl&Vup=KquNCY^twGMz_=;KJO$jwiI zq>FU*vcD~b04viv;5>^L8sIw4Wu+}y;&bW+AyPe8^DAy3wCZlop~ln*AfdDl-u}4u zzQEH3FCzZ{x@5YEuOv~~jg-eLJ|fbPq;}IfW76$O7-<2NDOyxbDmr73*+ozYqgwt9 z$7g0H>L74d+NFFHt{c=kg1iXMzUmO$eqz=@2m++n-GHxOgjHq124iB*a-up_^z+)= zi*Nd%;~S0M$1=|nL$cbBB1~79L#618NGL9)I)V0M=kKdKwVT$X7&_{EPik#fZDLMR z@;yna-L94F$Aa{#hpfsPe%PH}7XA2(kYAn9$hKEMEDvP{6Od1vK&5(hi)zc&?)Iej+h5EV`<7h#zo|{1&J0iR2T76@}8Jn2-{7unHT2O z52rBFoGL>=4$McFYBF_4V%?Hju##BMR+=OV(*nmG@d|f93))qXr8?n0Va4c5tky1GyK>Op4m4v zjG{wKok$)o?tw|*( zw1bZ8&&*dwwE!t{rAowgY?VoHFuc-qnq!!;;sOG@&R>ogNmK@}GNe+JJ#pO3dgTMD z@1`9=`U2}w>VEt>N=T2D8YQ(X)h*XoZV8Oo_cXjiNwzKA%yNCWD%=<}vm(4g?pRix zM5jRSIG3i!M{tV>J8fwhE${&D7=heKa=AG@1oxU?B%vzR+$o49evB?Ml_)2=gdI6$ zf)&JcqS#5s8Z|1+_6#Dpi7FFcmIwxl?-a*%5P?Ol0IH4ZIa3|h6wyy8DCtE9+k!g5 z6g2Y{{{Ut`94}bv7$Ik>0q(>~{El#-ACjehRQ<>L@LVz^$lbH#vO7k2cU6#rc^}#Q z1{bK#2W^Cqe@pPeDq;o^9m0GJntpE$aW%VOtXgKI{Irl_zrFR+n8hg;j7 zNN2qv!w+oBQj_Jhkxt=*ENs`C(G{1ss3of%@%EL$S}N9Osirj+dsix45kq^pEUyu5 zjiD?L)mmEvwB?H%J)L@$lSr()+&26*6w_w6#Qy$XXQ&uH8x4prRDho4dgsFvaJy4E zHqNuyqk`icSVoBqalbAs5M0_uO|+DO?Eb7eIf*YyLmm{L7HRvFi|wF2n3B$9n;-H~ zMnt6((*#EIVmk=WnEL({6Yc4U6`41{PDmijY{_&Qkc9hB^D^3sCM^UyXT0J^+%_V;G1O1fvL(fuoyMMxM?-YzX@S4?DLx4yR?PfcVO8!a&JqHs zliij8xxZd+l?aZ{Y9U43pL60;mF=m((#xCT$>WCv7II3+B&d&Va6G3c;!g_*^G)ju zfAg)oi{Nfe;l-9$_CDUcL7O7?Y`e;mxbh>rR+A-{W43&ysDu)NPJKIZ_YZ8~Sif<` zja7Lb5xs9$VFXfL-St}aHMm=T4UMh2u_G*LX>cu$N`15el^mSv#YH;k#_JpD-V}Az z%;b00OuZ{YgYS+*-yDy-E?fTqdhYr6?dNy$#Qnmz%MuiZQROy-e5aHfN)wVo02Ic* zA8fN`DiMx`O8)0|g+V^&cWQ3Lqeg9O;h@QguC`(Jbk&XXA;)p^q=yhfsvu z$VMAddH^ksnSI!IGX|nQ`E%|2u$;^!*U0zaUUoO8jC{Yp?!r{=nlt2&)+MB@bzQBdpDE$@@?;qH+}TmcV^$00>;dFX_ao2nCULD3Aa#u@ZAbl0qNU}pLBM- zY}iiH)~2(z`-R=Q<31EULR{_cG@!M%QWu&}VWFl}>(d@z&nUV`^h;_jRSmnG(`vO` zt_eVKaF}`2C0z=eX-gIMRA8G9GPxMO4Uk@xlp9=8=K4?7GwC5Q*qQh+OY2CP@)hc=^kshjA zn?^*{KkA&p`$jyL^Koq+n_^06DdnoYDc{oz)fR!zE8B_r*xp2E$v)q^3)E+FBX29% zaNc%0hQ30&*DrQEor%hgG*{s@@9e-1M(wq455{U zAw!}_&+WskN%Ns@EeIdev?mW8NGcv9xHm_bJ-@%o#fDAA%nfQaMDy}W6d@-8PWFTC z+f^F-^L}yH6m8i%x0H9avtaGGbo1|5No_=DGfD#C1xMHc#5Qr~cCB~?TuM3)YgCIi z;<4JdKG(a(+*@|~$|PBDu%B*JNlZGBq`1?pl_g0dwzvnX;ONI(g;HoTlqo+Nbe`-t zoaJaR8~)ZMjB0eDKz5Ll2t5W6k--L%$@c?<7_FOzN$PX%$5dXJFP5+L80w4DHS&|* ze*7g#C~M@8=KKaYPE>8>r@Z5;cPWo6`&jCWKtC;C%Y|%bA)hGsj15uzpk=)CPuqlN z8u>^0F%MEhdT8IvSF;GF(C$NTDElx+INm@k@Gmgx0%%`$9o1vG7LR4qp5x%%UZZ>ITGBIZY_byF5RtNbQ`Y z`i=cTxo6Rq{{Z71fyVrGx3?o)g<-kfW^Rk))6}SsTj$4y8EPyrgt!!!^%W_>LMcOB zwtcCwTh5D=-6+hja_+ZrDag2^mJ+_B-nKV*?Cd2PBw{1onHALQ=cMnd2OaWqZCeM_LL%{xBR;0%+qdEmmA0=gEa*sZ zuD>GkjRuvH+KI_snDgEDv+m>8cJ+zA+4uW-2`8gEsVdE-w&g}}KN;kOF!Bg-wWQ`0 zm8PJB*hVmOyyEHNXl0Eiswbi%$sqle#L>8#ImcQ8H5JbmUxp*=Aik0Jk?aHxrw)!q z20JweW-4_g(zPS_@;za-+W6AKt8Z2YCb$*WHM&S(MT8Jk z!_Wrn+dCU{q21YPyKGS_ZAnkd%~*|cj_d1fwR~wxTTvu*$DM5*zma1x%1c9Gm$+M< zswQy`(5keB0r7TygC2sTLW*(dR@{N+9`5`@dw@zulE11rN%hOi^!P(oQTB^+Kd&EA_9FiP@aUcg z-TwgdBv4|Gzzs2~G85u`<$q`K$53hamHnT`9Yc(0{>R~jmjkFrm)4jQ9dS{ohXAL| z#8IMh?gUpIXoBL@LTXZ)qEWhK$0PDgi{>ZXPLqN!El8=TQc#mHDJZ*IW|7#m1{}*j zW;)Xx&r3h~d}NeO^$QA5mYF8iV|BE$YJiWNl8(;WIqtPDZSfxO5CSvR+0QM^(Sn9#EIfN_x!09MW$+2 zMKsl2dvnIE#b{*i@g;c)NNO@@BxK|3unZFTml-KM`OABYlnjyYh%67yB$GI zijX=N585%p1`wqG05h~6(M)jV#BN-3ySSmyvV&i=;*&q(G?;JFB+o+JK|*U)iX@J; zISg0L(hNS-eQ5TX=sx+gcPFDyPt!Ine_*$5_}1qTTk?u$+Z3o%0nmt6%lK4QClw8z&tkMhy;PE-8$y{vnH3vGjzLZ`209UdUYzE6(7^svUnp^}9U zZ?hbvcPI{kI|e7}qpB}k+6siSM@1U_!xY(H8F7#BpG-*do9$2uq3p$OZ}?^IEw;t^ zsP`Ag=?UYlh!0U`w$%Rs@0)n|`*M@(zj^dFKft}in?bE(S&!)fR|(FDI!MhAb{o|tii{w%azsDCo%UNLei-<%QfOU+ubU02urZr zQ5{uEVXi5FtVExQsk<=w&I(grhPWR=@w9GULe)_j2`BSs{Wxbj9~(>!;836L(kq=n zt{+6n;;0f4)Up6*s=YG6lqGC9&fSvv@HJ0q%MOK>*#xuU9xOOEOxGxC5)Wtk93-&h z$e#OZJOSfv%QRUH!@WyfH=i_$at*kDlq;sMW;(__y3%fhcejh~HMFXiA2Hz{XYHV! zNiHm>uBG++sf$=`m~CoOUG5Cp_zH;I`(dC7kFVNPaP@(Q5IJty(tJS`W7`z3$s_GQ z^5dGr@*-P~ZFsV;$BAs6%N(KdO@zmV9@PzW6$w5|j;TL$gC23Z%G0OQenbLs9j zRy=OW>d1F)^vSU;Z7H%sNh{{S6UJ-Kzc7R#^0w<&+!d>K6Afv2=%du!?b z3?oLWWAFF2UB%=#uRfuDNN$Ur+Ua!W8-p+IAkuG4!)`oAqy|tMKu~G1N+eSwi@aXc z+o07TRLgzJpZ6Ul`aXQ@pH^{Z-EXq)lg8e>pe9tOo(?I`DN0gOwGarVR2ag2M8maj zl|dw5?YCzuGIDL6s?M>QKpxzA)tI!$Di!|UyBplRg}CH4(OtJ^k#CCqK|=-@^_-92Bpek$fvdiLUFkh}`Tg+MQyR-#g~Q-tfXPnJ{% zZbHI=D>)wALmSSdGqR{qzp3-TG3h$`(zE{n#Vv>KbmP^2p5JdVB9}VCR-}23jD=8m zY1Y4XK9;vC)bg9O<`CwzL&SkVdogWm;VH5fr^!GW(*!xH0;r?u0+F7mtw&5%-E|Ge zf~bbuX(F9Rb_JKhS;88JnMF@p)2C(zo1!`^QOj;ZL(G+ADl3Xj+T|?2aiH0uTg%>i zEc9t@xV`Czh1SbuNj6Bj7BU1-)4#JHH015q?#;?8#M19McA z96lQsVM#658k8JXwr(+BL0W@J1ZGcMT4H15T9T`wPn~kj$myu%x-ujsGGts*DVC6- zmM=5xE32Awp-Y2vk@ChPWm4^N)C0F2RLFs!@Wxf&u@8z>F4jHyts)5a=G*bW8kXtr z8DaT5-Xs^NRx;knmp>#Yo%d;Gp@@2^(MrX8?z>Up5XIWL68NNGaxq#KhN2`?5EV|9 z7#inz%1(+WKFvwh$niz>3XrM(uKAkgu|q2G{-8V3+7VyF9{Jf5=D|()hj#_8oy7&g zB9Xk>R~AZDk<(QB#(3MtW@>O$3pCj&O%7D)_F&#{K2{IpXRG>f*L4fiVWyi%Cb@&x z5XR<=nh?~PI8NIh#8FZn?$XU~FTzpTZ@8d`0;KuQ4NYCzrO-xXUV@{RjX85@e>?YXFR)cTIEpaBTOP=kFm|1 zXhN%(Od#bz6*QuH_34IOS`w+CFCk~!fD~B%sx&c)28lKIVp%8TJzfGaR(~Zai25_~ z9u3bil!J^1gFk;^>* zD%S5}N&72@e5A>id_dJ}T25y!-k6S9vO|`7VPg#`>gT@^D=8DKmi!F-vq$>r*9*(2 zooN{PMh|^^u{w&VzcDa1vl$6LrL9B163S7a_}rU(Kn$NR`=$a&?WP#+P?1@$78hLk z$0WQ`p2Xrmn7P3+MTcxpdDq-NCkm$5n+@3F?OBL8T9EYn~RYjY$BQOD3TpeYoV-v)|J z0q*|*(}|+A1b5;eYi=x*mlRHYDNo^vBAOB3LgPDekOric6w?zyHVZtYEJm?L)CglS z&>Zk|VNhGylpGQ4=l~@wfli(r7jFr&y6%6pTaRd{AC!BsZI8FIw)WsIMzyv&(!IH2 z%GL@h?`N@Hm{F2OwE#OYZD#`Ci!1JBP4E_DNVY{DARRcn#FizeuBBz`H|~1_O5D*I z1Th^X_GN?Daa)3<5+GY8$$U&C=IoHLe6SiF*8ib$C1oT# z_*zD^6k49-~ut?#n5#J4h^8j>tk;DqPy(Hi*WAvCRgK;o|EY*&a) zyeYo{CGFR0YPzN*)7#ep{PaA+W42BB3C?fZJ0yOgUwt}Yce+I~WwP98BKIpDc6*F+xSq40VL@ZKfI;h#&m)hXtxAPLP#V7h&qg&!6OBO-TZKsB9v+i@{9if zL4>H@L=M9hDFjtV5yo{SIa~FRVbSSA%-U6_&fS)v6a3m*bzgwb8(zY-{_#IykMDc+ z!hZA1%A=iKjPPC8+cs}lbAqIn zzVl()y~#UQlrB2Oo{+a4${p8h&YNkFn~k>RZ*|yn*-&wpS$Z;-g`sF95J3kX6N7H& z%*9dhJ+C6=zJro}2aCltQ+2wsq>@>cAq7XRQu>qq82XnHQqo7v*{}0oy^Dy3%gE5{ zVQD`YJ))SFaEn^-AhS6sRUIw#FY5mQ)K=H#&Kcp!yZ%P`;;rX+ZqIPAZXI_?Fj$hq zniU3fius1h7L<^e7PQi!0hK)GySsM6wC4pzdRJ_3*Im^aI;MJAb|^y10O{M?j~I&7 z^)}hJm+i%mfaHNZuZpLoG8nFtVM!SBkGBa{bq#!__kSPi$0;+YVNu3Z8hiTS$8;a2 z&w~mkn2VVX>9IQbxHf*%j_keLyDghm8#ME!T&Ki<+H2maP9qbnJM&(c)P>~Phs5Ys z&E53^^cTm-U-_%mUeIA|D2Jg!xC^9!QffeM-W?>5W(&aW-NJIJgP;}d_iu+8s+^13 zzg(Ud<27zwfn@acy!jU20{OUQZN5g2BE@aA-YxU4@6L?-Y+NLwTSaX;nxWQ`K?fT5 z*!S*g-3+zP@vx-QTn6v|0H^13NpEvVkLijG7TI)vm*LCYPJF{mlu#K0Jawf?u6_ef z%HXB+@&5p*5B~spkNQqvbBC^uL-h~5ncf_M1RIMwQEnc2TsUn93X&r;paf+~_tP_r zoNm)rV@0S1&hG0>>Hh$sPpY4%k5All6Nxw8(cY{@YHZ2#w|qA3w|i4-ny})$h*4fl zrqN##l9tutsRKIW6}fg6P0Nl@YhSq3?cL{(6rm%!Tr=v+($A>Yn{S8ty2rUMMYOI~ z(t1d?-k8*y>2eLn%6KchX~sKo*|PB^>x-wEuf)&CzaK=CWrKd$^iq_q2C5YSK!Z;F z0W8M+0mZpfGIGqox`y4QQ_8g{u(X%T^iGL;7s{ z%T^Ppj_I>+EBjxD8`T}tHCsT_w+Yl^oKcS_`y&ajncRri${)%6aN4o-N8V8WOm$d> zo=?B-#9dK3Xy3{A{n%$%Bc_c!qx{3}$8}hpIGGJ)Q;9i>3N6Xz7($(p)Rieo zZ7Bs=5_6#Gk7vW}Of_aQajR(ZoyWN^#^X_RA-jL9e^?eR;*Ire^((sD+-|gn<@xj5 zmv4CunuMy~EvF)lRm`EZtJ{Mw_SPir^75@jvD`T^nwK*#-y8jCIoofhJZ@LxZN15{ zk`g9e;4S^Xa8L&_5&j}Nlt=o580_z+_T-zlDnDtb#P78i`H`=<=)VztSv@9l3Y&|% z-n;PK(ae^M$xTtz%6H|+N}R5z zH@2$N`z^SWr7C3y{*FB~^EwU(8bg6vXhOjXsMH7|hM?gZsXQwkQ{Z0TOTSQ#dGyB( zovO?1@_!gKN0YE`PdrQ{*D(TWLfnoi{_KS#xF;B`^Rlt>mo$AcHTzF@-L<6kM?p8A z)^~0q#|}>x`f|8kw=$cp?gjq!65j$dl+7)@0l98EJC1?v7{yuoZGNwsR7SJ^0CUIV zLKe1tX*rTymJv78meY#D*GCfG<-9pT+#FTCBkiUom(!QwAy<0aeg|VcDD%r6sW^SE zOJ31C9_t$6F4=gn2|{a0ej!Qo8h9bHrq&b)szDvN!0wnICy?#&p{&EmSL~@T62&4O zm5&eGOb^v2jfRe4{bt&69)mVcU*!!6yT2a?dzO70Q~FEEWgsPoXT z>BM(sRyNb+bQSI2G=!x|ib^|GQ9*#6!uHYG-=l9QZ0(`y6L#!<*LiBia^*W3A?$Z# zC^pQyoGF4yh!(88*IQ97r2JY^ToJtOOX)sW;(BMDX?7~eQV8rKhbkHo zk2r6;kp@5Glbjp-Ws2fX4QqZ6QO z_1BPEu+2VMN1&(Uj_Z2`=rKwpaGZi_Rgpcz5nR2X=-JfrJ@rBd{{Vx7nSZdmN?l|xds_XL;J=f{a_*v>AD+A^ms5(M{ zKl8EVSlm~<^y$G+@Q3QCiGHoQ`SF?_-Z%5B+L>x^`Z4tnZ@>1D_Z|tp{{YBkYi)p% zw5Eh=Re$FkYW$4N-Xxbj3rd@7RRC?UefkDE-vDWM4Y4K~RG|C0AuCo)xf~eTu~fU&LrIxCJFNLw^Qir{9RD z@RnB7=SQ-?r#{w$wLK5|eYacIR>jKBtE1Y#EQ_R!rUjMl0oAclGThR9 zRm5FkdP0*Dl%tuc9jCJ$E6t13Lciexk%%HMGQAoVDGfxe{YZeTP0I5^4`nQbu5d(2kg|$-d+tjfV?PtBD^c zf8O>q#^7$*Dt~XyaV=T5Q4|a#bS<>k`zb^@5a)GdCyT!I+Bn{ReQ0+qjN*4{{To^ zouQ9dJAF4N*39Xao4Xb!Qj(;+`DrRrR7b)z0&DEYko`cdVr=%Uapccbd;0A%$+qUb zZ$MSnSWJ6EU(YG^d2Nrp*B(=mInt5h^ei?@Fu8p>d@DnP`GhIk zjFJE^N_j=sTt3w)MB+H?Dlki%Qr|nf=XT?Kx-K5!)SZgrihz6?^sYKP4C}(URIjr<3vuWbinS5hNGhNi_F!3XEmsix129i% z>5AF9@g=l}CA}q2At3}-zrz(9q}M`T<3+UF{OHj3JLvavKLgVr3b4wk8?nU|9^;Rh z^LKl;t~D2>O&001Lha`!SXmp+;_rveD5r77b^hYXmX#s1+je8E1gEzR!C-%hT~D_a zd0UO={meez&t-TZs;@IvlBGU;T}H3#z%tFbE-jO-w$gk8W@D}>l`l~kWNtnID;g;Q zDd!YUeK4(1d~Ku^Q>8UgJ-snsC#gJPQ2ZAZvQ^YzoM~zcl?0C-9=GIv;p$J6B#evK zn}8$zM|9)s9>@Oxfgf>{?vMEeM-6IAsUsx<4{v5RkVZO4O@=>39_k+KOqe}24g}PM zfr#!7LW4}{u2rotmtKJojSB_}HF9gDUi z^9OwKO#8K|t9)lKkR7+Y+JRH3b>yb16*MBa`8#ubwD+GzB^QNqo=0&*Xi|x$U6>Qa zpFO#>kCMU@(+YFC*rgxk5DAcyuHZ7kn7KA%@c`ZGKZG!~KqQvXLVJ%~SYFAp{uD(R z-SGeqvOX4w`*14CkC87*USNrsP&_#tC(pIOW5%d3nDC(r#EUXyZyq3txjvKe6Nw|8 zKQWTRiwvgf`cw(=5!i7$-L8;42()f)k1ME1ud@w%rN%X2#Q-@i#eq;g^u!a%oVb%i z9w;WItyQSNlat~QqL2d8qf$m)xDIKyOBo~wquGJvsK@gkc{;Mw*9!>Moz$V9lUMTo z7?+_TK4AtvNdEvX7v-=DvR@z1NlgZ zHPiM}3a}wsvEMOqpJ9``|xVC+c{oARYzDq0jGWhoESp7r~y&_OtB zw8>UnP#Lv7-AqT*sP&%{Lu+=Fr3NPHx=?b?h*@^S? zmS}CLP!t)AcPZaA1}&(lC$rgtc~LXWX5tfR2f_{^iXrQ|frz0C$pHPhokfw#at)!8 zQAYFa!AdMgb%z*)&q({5!x5`YK4JQ}ergS^QwcG!x|2W}e+aE^piyEz6@5Hm`&;NGI} zA#Ipcu#A#^&bXdAX$rCG1gYvDnA5Of-!j@C97Gpf3);JC6#*tsqUNqcl!qn``K%({Vr zUh8Ra3Xa=yTtrDb@;hdYwN!Qza5VSNAee0L#6*$Eu8M}r5I)}A2KPxzfLwN1AqGz% z&sME6=#$cv6vJnqKQef2P+kac$lp^|PN`}jij?Zh5PaOc$zgz(klmNNpK&z`@eZ}G z^x?C=UL)<|$G}5-a7}S1^{73VXH=Rt1l3)psOIvO+$)Kq=yYR76LM>+rAamSoFx#o z0#oj&jENW{LyeM`ofHTsyAO=$HG(%0{a>gkp7WL+)j;Qo;O)XHgOKhDOPxwxN*-@K z^Gkr`_T%=-R%*3BE1`UnHg4*+%#h8p^vf1Iqo+h`qgs;<^;Ur@4iZ{Yz2sm^xb1K( z?O#K-?`u>kE~Uo2wUbr1PM+^(0iO`wB7K%3ReiK2qEg_S47H!;5}P8QZY#HxrHM5B ziyevo0J@3o0k%|OTlJc+<=EDJzDjZP1Dzz#q^++?3;AcYKf@9!CM4q7w~%|s-GS;p zhz(4dS5Lf*aGZz_$vwv$CnP~jKF`Mlj&Ww5LGCybZh|DuyoG-+jQr(g2`fJ!Q(nwUT-4{^*08$gm$D@5d{{WHq7*6>A0FHT%Eh|lFo`1(4&2x$4 z4eD&%1WH<4BsLFM72yf*B%Z)9!gs)0kMc`xqZVs(*Z`-_Z9!=?%c#TBRCNPYQmH1{ zx2E~Gs@odVzwg#)5mXY@wjJVpc)_xBN0i&48c%4Z2U|`vmp3A>xGnBk3q>bSc*35b zi|ybJzcA@1ys35moEusII*tgHQb46apcrk%p)WHm=cCw)f7QRHsNqx!-Owt3)M@4a z0P(oT_jmkB8t%~l0F;kk5|}D_x^pDdW9N4DK7fw^K?!OV385pP!yVcZLMERsLWZQ$ zzT68b(3UZvC(9)Eei(dj9Vpbt%KreSc$26)fWI$Zsi^niRO)a$5t1BjX-Neppi;Sy zvk}7Ui>chT(DHTltNM6J*gUsz^=oOnFMDy{IbP{{u-%sA={rwwE(2sS49W8nAWQjW ztqB@SZdE=Yp{LkBpLYtm7Y-Etr^u$0vuJ`tET#7>QpLT`v`{GD$_=H}9wta#BQJcFrT zB`mBeq#Wr?IZW;$gzSqO&&g@XMJa9%%`~(vX{z;7op7<8Nu$f;Hf}o#(dEE;9I269 zd1>>Akg=wswV@X&Hl39+Fb8rf3hJY0gWQOUuXZ{!}-8tGVa7QM$+5sq6_% z(OUe26I}|U6(z#aAQFv9pjMNWM_hQs+-p_9^w_6OK?5O69v`#p!{a)Qlxhp)uMFxA zGxFp4Pv3`iP(2xsAom}`5Y#57t7w1@nq!VW(4OpZ#yqflPFRZL6x&C)5mkhnd0xYW zILDNIzjip|DQ1);RQvGvBu1cQw_nSFSt3+5@`rK6ok2NF`F_KW>LEq88KgHuDHrq)ELO=;mU;uVtR^rQ8RT`~wqPr4o39nmb z+n#1sy5hr%Y53A(I38){mWmXu3rJE_IuTq~{jjBvG*(;-LZ9tB_lJ4x`OnjCU?*1 z6?xh_f7Aym#NGS0-D2Ha!)!r;uRqo8DE-Bbc$-J6#GszPr%IG;!8IMTn_=EBxy!xK8P4>I1vfjRiB65 zg6MNpXWm=g$@}o47!AKs@QQm*6c`gkiszvFFxhYwA+2^oo4d4?plK}i9dceX*=~%{ z;cyE+y+JARNeUoq_TV0Bqd{skX!cF?;@mLzXQB=Tn*k*)S}l)SpVUDBNkF#0{cBvu zVmL)R2}MReT{@y;e#rP5Pj^qAjJJN~-XQf3rAg5p@Cp3|T zI1kG`{{V(MrvP~h!cb9i+Jzz+1oc|F;f)7eyM8X3} zSxOG1R=p2O;8!+rfZ_=%Ea?Zbncyy|14fElBx&-G;f|Hc1faU3B-DUM+72Xl&Z7eP zZ2DoHb|nDJRUgY-cg!$q0Q{?n%cXns!#-l69Eit|zvVx9!#dB*bq$ij#e6z&PgLW7 zmsh*q4hHJ8gRR@&DGEzugWF6u<{83*AUj;ra~XJXG_PnNdALskYS|v#vcPS+gM*=< ziq%>6=Z4N~B_PUDoV;IVBUeVruwa(YQU}|IyQpK6K1x^}S6^|&F6v@8Bj+SWY)~ZE zu;K-ABcWz&MY)@O#vGe`$f=bUS`^%v?ja%)~-TIN$QA7Rm$VU zZMWzA&FhO=@U8kgCVhb<dr)+MraV_t!)L&*q$JRsS?)M;g~+;Tp=EO9=KVCHN0&ZidAZ!P$up59$%0kxk}0j@r~?Q3yZQKymf-*C=fHzGXNpMEY1MIhXLUMhBHjySO1nfcRk z`gY~*oyvP^GgGGR*$3w>&eKeoPk@OH0)7&-WEI$PmhQgY`MX}1>PC-h+!P|>_%qZO z4Ep2ae^y+ZvaA~giMHt5UM#D&>yL72G5ncfp&mOaQBg~jQ;JnaSqyynylpuc_w`yM ze}QV;aVFyG<@=6}g zWXX7`2)Nr0p33>u2OI9dxhZAp!DokV-MMnF1I&XAE3G^=$m8iLRW#2%yTn1dFg{>} z<@Bu46^~*XpgF7b4nbsPrASM^7k4&rG9)*Tpp5C73r&o2|xY@U>|rY z82LMO{{SoZo6NQOtgz?}RA=@aRglY=7L5M@Aot^)2)#zSSS0v!9dJ8{?rEU9l9Znf zbLue0O&K-=q^6{fxP~&JFEv2)DE3zo(T?Uqv^cJ5Qo?Feo(Qn$0h4`JhmEgStNFfn zJ2m9nDuAgtZ01^jH2-_dlB5kTb?bp zwDewM?G?j1PN&TB%#B(${klLWt~yxK8}pC>R98}Hp+koBnJT{)1X@_?Fv>CNXvda6 zn*-6E&Ta+?5FH|&y>MTF6JXj8ejtYuE?~l18Rv;|{{T)p zf;)ngRv~Lik`<@9Ibh5+Yn$^p0bjdzo{>e=T7TU%eYM55T(G~kAXep(;=qzE;tb{W=QLiAV~qytXOE1gHHR5kNeh!+uhXc)a(WrI~n)w5495t4ra zhR(Ge$OA#$hESHKfeAfPj-b5E#)cAeM_%kfI7N64=94m(B7SD-d$8<#HvCZ8UAg85 z^7j7#`y+||TE~$7weK*SjYLg!uzk7U3)XC|2qqUUJ}gf$o2k-*1}Yte;HVg{FeO1s zk4je!c-2GJQ$R-A2|W+DJVEm8k!G4I+y`g}43;?Mn8J@o^75@|fo@u&2)BEfPjs3c zDa3BH3xX5YT_>T$Ew;lVq6MYun%13|A~0-!G(byE5_~7$f#qUVB%f1^L2+j7TBf)&Mvn4uTrs4^&py%( zro!pdt!YoTxIW$(JKbWq2b;CVW`%Clj>?P`ZypQ88hO)~1Rrh4jC?Bzr@Irv^dj_h z1e|V2!miPq)mMkG;!o7!9xUaz&G-bQZYb!bT-RRg4|95qSXG^#Z@@@ z%X)}?y^2qWL3_cf*Fv-G!oAfKEslY6ywmwXuVBF6s-jh~BX;*Li zdom9+r4R@%Z)lY*@XwL*a%GuDtlkf;XxZ^fJNh7zwCT54BZ7_uWlc_J59wI2!z2P2jQo^Te+&$WBJUi|n9gg1q|I60D*QiSeG=P(oS(UHFsc zLS3x@KXiN_3!wwB0fxr=sRu|0=N3&=>8=y5#pxr(^N32RQPd3n%zp2mk}7mVc6((g z8EOhRS`hV_ByCfBe;Gz zPIIVzX17?|VlmOsn*3<@c?l1FFbgo~($-bwcT*y&2F zCWf9@zY9Qc^DTJOkTmIr)dU=K`q?bEz}#Th=XZ6ar9I&`=c|qX0MqaJA93g1^uODl zYgU?`;n|OLYgBlwxQJVpK!+c!@V;=-ju4$|^kab$im2f=13`me|sk>mjriD5y~&5>MTT zX3mAO!0^5PZw~WM6!NUis`liG$M<%w@^es9M$F;Ox3fIjls|KwaK1V#n5tP!Ch9w?ae=e+{?eDK zRW^6K=HvKL8Es!*KCgKLlepV$TzM)U+O}TzC6YDH6_U#W5RnPngGhaXxgtvFv=Wj+ zfuYFPJVCs6mNlGrC@oPw&D{AH@>FE>XQWr-BxOUS*GEc`j~P9*2{%w?WE*3GAJ9wC!)LC||Ig*P;Xy5rl16m>d6lv2y?c!#}7!SKSMH_XHc@MZB% zy6cIjw+nE|1qn3+#O5cK4uo$#J_cIgJ;Lc*jBmuH(ty^rLjM4F-SPCmiw&Td zUa1c9zZLHeJGDf=$lR^pAT7@zTicGN6bVg2g0-IR>pKnk=%GjcOZH=~-1es~&sax2HL+bUhQbu;B}DL#vWl@q&>`^~NSL zsx)(r0E24o!+@GM^1ro((3x2_(4XZ$jw0$3(Hi+a+(XWYx<;N-vE5OM+dlG-aL08? zdPY38{{Su)q@1k#%U^aP>WFR`%fx7L6Og953FgrG)xy6cQJCeb zSz23W6OX##0k&I0mbDdR0#cAkB=iG3P1Z|}fZZ1@5TuSa(v*c%c>w`;q&u6E~Oi`ie6+s!!3iTP!gloW>mAcIN@GNIC5osARF&nLPq(`j*>?RjEOyJal}}usOREg{9*`~4r@?j+yY4IOh)8h- z1#J1KWGq*xB?gBdHurM>0C>-!wg>**7q8`rc#@vc_+veKbS@)irz}3Sji>(rfR^V( z=_}k;B-g&OYyBA6c2llj+-JL^{xRgvA`+cIbWJIbY>tsUM(4&u`~LupCGUImIneH3 zA|fRyd7_$?3hET0z!?`@VNvpdV|T|2*?4Nr!xJqgOS3r^!1c=Nxeleop~jv=bZjLf zN=~}d0a(hE-lW#;q=b0jFG;N{+4f+bpjD-)b=LJWq$a-HLL89u*!wmuvg5Yz+txI@ zX4|`eh!++TpyJ`B%?`N80Mw;5rZSz!emyrsTibZ;Ni*B;2kgCvu{ckRHrE(KL_L8T z&k{!1Km->Un9!HztgFPOxLhhxR%D#<;@2kK#(HAa5A}}~23UukrZL;lC~H$(NTmEo zO$WamO~}pCDcf4(j^$vw72SlWMM%?i zugbV0jFpnwO#uR#(+#u-kk>GZ#D-gC`Ld78M2eE9_4f{d94*Vv8IJUpzJ5N70s~dL83tuWn12ZG8C60%(uGR@Mf~&+I@$bP*NOe#>i3@ zk}8!Xf(~Hd9m5V85OJ3R#|I{MB&8ao)=$z~>QQ?22WorM*EwAT?pfND{G zj@cLh<8nF!E;y#80pm&4*0}lGySr{!cX^}m)b{?|-D8t(o-{o4`iNMO>Q9~=Nu@Vl z;wm1~IpTkgJ=?bR!;AJFU%!9Jk|4P4fLa7l8h;#L8M&fZxT=y=kL;&Eat0R0f=)FP za8L~RpJiP07~$*TH*AqytAUx;&GfHcyoa*5LQ%bZY$|*u=*+a>KX^E*%vzV*eJB-7 zRTQG5w<_YYFGgHg_|fcJ>7!@1TD=YMa~8%!B5WyA=Yd)(T$%2;>a*!pXOEWqss8}I zm%R7wpZp8lT^AeDuL$ec6|mqHaEg6Ms5j}~nGGmL-MJ6GF&Hmq{{WW{c-*)7Eqf0K z)BgbP@_+WK{{XL#t#N(?eBHSJ0G*D-r@|@uaXC>S0)jK4!nZ;$LmEyNqH-`VEBjdL zlT=x^qR$8eOeOg-_YW^I1;m9X-<5DOV`UnOj3I@kI_Xo>-GHX`A(gjK_sTV% zkO}O-Qf&|F0jdLZ$+7RmbGYa#x_}M23g`%@Tu1W*x`)Eil|eM_rX%aII_MZ$nhW?%+4_Neg6OmYVga=13^XH-=DlU0zSYmK&l)5v5^I?^c3 z!@eg3(NcE?;oA3m)zbPnALdzbX7V-bj4i5Qxb4=%Zhq$}=Mtf%wKW_Sfz*RcdDnGXvk3D^ zRhxd*%eg7j!1SZ^^0GNP!@D=kK5XASnQM}!?Xhf|uHPcn7AatPrRedcw1+CaR4S~iS46LalhQ91CE`ud; zP@{iRn93fMcUq2MTrG7U`|>g9KTaavdMfg+r*2;3%H+TkFj-z=kRMxc5A%N~6xJIIkRA*kn z*?{hb+Dv#^f%%4r9Vt(;C}B6_1m* z2)?uTnvDFV8_gDht3Fvae%uQjS@{+7+nDO_F`9w zDsVgJlYBr8xDWSd6>ik4z?bJep?Bgaf4U>P3DTF5HLQi^7cDtjc|=p#KdTVRPMisq z$oY!Qy0)T8qctc()SRi&B=y90?lmq2eamWB7QQEn-%^g5oVC1Uk(<{k>Zf+8OC)3K z9?{nd9^?bvYnJ%}u#XmEox>X_%BD)xs>H-L6E*brs1f$!ep>1YE+U#*@pd0(1iK`V zWFq*A5!%l&pJ2pyE)ycsdRmUBIYQQyr!d=;J(+=zGWwT!tOn8Qe@(3OWu1cDR*lDM zZ(}>S7zhUG1NPTbq^HR)jK-gSK48pOBNffXp7m!Y#PRO*_$1ESu-cOnHi23p6#oF5 zi#t~3%6y@AY20=5W~DMSs5qCSKOxD$1h(<~rvh|kgTkt+Y=Y%7@=3 zd%p~HaZdS=RKy~l3fB%+2<4Co@`t+wbcX!EC(8c-F!y35Vbo__8|9V!w86hFN7hE^ zhO%-Rso9QL)fnQjl=xjF9D*HWz)iTJVNJBew%wbSAYq zOInEzKcgUuR=5&bEqE3XwNR4fypizWRg*s;nB&EX&P78Z5lPBiSX>>r_yv~@<61mNc&-DK38RxAAtmSzn)v?O;8{h7+RKQXs7@!gwZcnK zsNYO1t8>awQ6nrz%1iS|ENzFPj$OEtp3+BO^a1%(FN$@ED0BV_H6*h(CitsWcPEhDT?=-`e z#ulXzvY|*#b-oaLK~^1I0hmeVnn+L&71FwoPrDMr-e={L|T>!ZyMLI`s zyAO9;I#y5upHiwdW!;IYK2cN(mrqL6VMrJORz#E2vbdefnhr?>%84YLu^MbcLHfSR zVN{wKW;Hagin_nr#JY^Sc-%t^Q*A4*q@;sSWxqaw5SDI@8IHu~y2Oa5>qStte@V-0ikE?YQu&EpT@!*qWD- z=tE_FZ8kKxR`{Hiz&C3gwpwMWQ*0MlS16%Pwi_x-cOR2-hLxpH+m8_3K9667oUP3| zp2fHQS+yvr&J7l0Vf|D+5byh?w#O@w?mT^ayGV}~3`_lr=Mja;mhxk=*p|y~$0}Qk z2mmjs#yfQE9mg6R(q5Eo@O$S6B1$Rh&WJq$O{$t}*V&8*JFN|Zqd`=$PQpO!rV1gc zzJ-4-3ef7CXjk%#3~`?&Kj<*L%u0dzAKCnHl^sxDD0kugMmDC!u|fiK?7~V~j)JQ` z&^tv=1#)!>1JHBn?80g&FUv0oq{Pts<(J=tsG*3=>Txs_tuw>98OIvFx9k*f-wK*l z4(?ea-bJ;{dS}ze{^9$K57a;S>)v^|WuUr}LO{%(nGAb%bUZpy@&-=CcXl?3vfNY~ zb45bb6Y)DyMLMeVPhv&@mPad~Y;dZ88RKh}oy9#8dS)R#T3SjJM_N==VclqvyA2M@ zWEr}eN+GSsMx9NhYc;~g!?+q8$1Zh^yNQ&-+e`Jleae`T5Rp^t0|Lbj*mTVH}l8? zTkEH9@}y#mx?{2d0k<>f{l$8T5#$soj{IEQTA(St0DPjqWidGnR-%ju z#1WnZWu3cZA?kIao;-_}{`RNvNBp>`$}I6dG)`P&ZA7o8?%PAi)#l#k=}iIfs^C-G z@5hQ|xB7wXm_PA8^S!@J+eBFHZy70NA24h-80JuIA$m}^+|#SAGwF=ac0bHD+bzG# zM^YrPHJWx)7_#lH0{%$;Oe9+eHu4dN9aB0F^L{v!sLrW^+Kxn2W4YIn#yp?w;yMLT z_@I$ZJ#g7@8k-B2{{WQx>w(8m$Cj?@;RR4^$Ckh4$5dXMF{NM1al>e5%U53~1p(zJ$O(}b4acET*t9;!){{WU=3c_@~7wrPh{{S05K8I4?N}dsC#w=@bRaTU{q|-dBj4%HHqx+GcH#Y-UF71m{q0as1$@bm) zC;s1Ooo;E_7YS65jV-tH(w8izpwN(4OtHAdvB?CPoV${)O|{}E*kcu2U{|rE(-uHx z`+*8YxfL}}n$~~qjB&$xxe=l={ORuoA;w8t&#NFPIuCvlKvJCiGHm;7NSh4~I>YKBc$eRM?0aqRgncbA z`wLIcT4hI+q^(I)jjj405-4*A9zDC{IZkNxO^X&hv1hG4gKCFm<86Uuw;;6w(`B|t zhSB_uwF$H&eSl*DdHUJ0P)Q+O@5}b$I)S*EkC zTqPa~#@=~s+;%nI9OWenDf6Nw(1}c%bq+G6q>pYXdxvO_ZaahU)Vb~L z;^sB}ZvtrL%ghbi&s@W~Er0wg-1lpZ+Sipo!`u(eBxT$eOcAo$>ElKZ!BAb+`n!#s>2l{x2`&afl%s-hls1ZVmV1roh?H5WCgu%S4xKf`$BP_+$8E>U>hNk<)KIH zA8sn%h`6%#J&S!h8ZCMx;4o9AOSCwn-fJ!6kC*$W{%pPH&>J`S7r40TZmX&3ib*{S zFYY0~Q)$!b+nI`nU*+4DU;FN?SZxda)E~5mHdM7Qb37i(Px$v$Uak{Qda?C}FTjtR zv%k#8!>xb`>Tp&)$EjV)7ow}Dw-bp&8Wr(=T6DnGI0!fG#PO9z$`utLpqQeflv&Rd z+3!>vWoT(|NBD9SKPmnguG}KJGWQo7RB3)b^V_EvNJ99pj-43=*zHH}g4S60V|V`m zmoIq$EJ4%0^(G(qW9}V>Nz@b^`!N)!LTN^!mgFn7bimS86~!>(COV1)x_!7*khd)d zk$8Co)j>e_;ycky3#1=K_zDF!Gs~qg9m``LG+1dD))Z(_PkErkJv4K{_J+}WiP2Q{ z0iG6AL^HvcfO$7aO7>R+(GYxMaC&EPX4B0Gg~-Dru)(FeIwHNnp(deUg|{qiuFVJkV;;?qHlFLd9vYTq>1GB{{SBSaAk3B z*PCK(ZNMzCp>4)%$z??vv}PRSkyqVZp^62zfy@G_Q6k8DDKwO;nX? zs888UCa5)7R0oYQT7Xh3UYJ<6$Zz8+6o2YY;+5%3c2_M^?PQO1p^roQaQ^_`qO&3? zTbH==KNW&f4Gk)B^poIXFML8p&9wZ$%Lc(zsl6JB+C8{>xH5um;T{O)ol1fCP+~lZ z1z3oH+1*MKS)ZrcEv7VOpi>{*Rq&C&FJ`|f>`qe5@Hn4w@P-?1@$d? z40bM8iT9ZiR21v%#4hc&cO}FGrlY75C=Y%Fy9f_cnhH3}W}OxGU^~{rA$CDeDLAg^ zL?fuoTo9J5_cuieZab={xw3HS<3@sGTyFeVwSM!&A3ZF;$h(=^=BaXt{{U`6&OV6j z-}s4p0=vWf|!0mQ3F9i4IV<=D8!HSs;l##O79Z$=7~PN!s{LRz3fAvLQ_l7HA3-D6_S#UB~u z9YsFOO$=#_>!TtmJ(zm4K^^mdkVo=jcVRyPpEk`+ywaf8h^CrJ3>_2uPc!>-10DmgJAx81JCUS$Cx=PTFEl zjXVftav`9@qoJqSiIu64JFz!09U_{KyASy{<~3wSroXqmd(JuFOkbOc@@-D`p1#qA zTQts=d_mJ?aaGKII3?_p^CEkaMn#olPNAQ(5G|TQJ;??Im~=-HTfO$z@N4+rko?&Zg?S|I*fkU@BNwkvEQcM z@)zf$=4bZDzwFQ4ao?uh@)PHy=58P_F97!8uNkf=Z_T4q0QX_B!jogZXvfK^HSpHB z=Y=7DWiwL|!%AU3QHrt=_wDADDZx?On_Le0v+vvg0JDz$G5HY_)tXkxynIxs5mAPaNB8#3;KY)eOK%IELomHi9F% zT0wu<9RQad0-bRW>E6;`)uZ5o?phga_~e7^7=_}+c#}tPli-Bs;WC_qgx3R3h%(kh zpMaL_zCNmz6@8cq@`gtuOixs1S|rf9-&`bV;1>zD0ON%{8w1;d)$IwRZ3)jvaRg9` zA9gu1R3u1{^Rxv-j_>^#ja?e8p*6s>;sBcV*AMFxLMe$(A;VB=N$tdH>nDUF0=87F z&OqXRam_?1xXbUT9dQ(AQag5le2@)D_hHIXV>(7vRMv$h*@Wwz(h03pIjX(G4TA9*B}Nodx~89QB#YEQ1wGkaUHGv{9N1jF znekH#pxS^b-)#@yhHv)M!76N( zT0n)`{5gI&cTtJbN00rs+*_TF-Ee({AjxS8OG#E~sOef~kKSC1e771$JadPVBKCrZxDA{)0<7y#E?dyUgI+4mW0xS`c z0AR7ELlsRzYwhg9(Hv>=>HbrJs&x;(T|dfk)jEcLT=fEj5IM2YRbYlWADaC|iP^`Qhn^&p)4Bj(XTpCvktL z*%qto`^RercWuh@&A1CVg>`04wV3IY>XICLQz}x@is%&8O+voG^xJHlleoAZTlEKV zP_&}ebvO?bcI*`>e(U>$c9L}vxP?epa8O}OH1=cF?qcL6N*+mTKFvusFD3DtjiHme z*@{pomfT`XSq!P;Y*1pZ?iKGM!a3Goh~Q&#Y)hI$P`9P6#}p`*jHZ&Jd#JK7+jeg! zs?c{<#FmmByKd6amK}>KG_)L&rk-WW*SkuNEL0ya0wm+jLQ@vyp|DE?433};KMX+` zP;-Lt0Js-C3j9uH6SxIpj`DBmH zOix3that1!H5DM!09==VWFkQxsany;R^z6c=ZwGAXq-{y7WkhUEJqi!Pp4lW_6Zk> zfPeVQ9!r=106+0Pj|cuDJKyxshJR+?ZmflAgJ*0Se`UjrUv@2OZ1!*QoiTlcr29`? zWK%8N5DHr~%9QQH-NY?S`CxurMa+%?Q)Z@#%io7occ;j}MSnI1Vt^2?Px2K*ZCs_<||;2e&0vZf#DD2rjg9%77Z2w#EYu1+5?hL)74e3m8?B6cx7AP!a`n3ZHHwxR7_9UmI@!sNfi+3>XK%!ZlWpTijNC~Yx_Cj?{*YO@u@@9(BGRWn_w=> zxUj(Pb#0|za*ca%1=wmy7B*#a4%rKY)ZO08jlf8cZ;GiNVi`+|9uy{~l_#LU@V633 z%TipowiJMVB&-6JeVCLsDuU7(B&2~r4FK%V5#9Jg@mHwn=Hy0>9rTddK-Q~sxq;jB zDaVEGpX8bCJ16+!dXuuCCu;4)AX1k2LoZ}x3}Noc^%0)DQWEB1%R>P#xruF}rSuodO4AfI?kQx5O|o_`;wfTt>}4VHNGq z93){-c3+O*32f_E@`F>?0No{pp(k+pG1m@chZ7m5(m;8{sHaknRRC8FmDfRMiC3V5 z7mBvj>1v+uGg((U;7g^Y!?>kjt1>Q+a@h8iMCCoZB&#HX z?3T?(u}$HAdYaIdJ4^F6R|VWwr3icLZOJ2|e{v`%FYUL8o^tYJpQcOA1|0PN0KXk; zU9J07fnB;rT8#jXR^*ChM{R_OQ3ZO$#TblKmuUs-@ylNIEoD5a33 zA$3V0g(jW2>kfxrw?>3!5JSwM3XoB!-VP3j04cZ3Jog*&7`Q%`qL)*ZSEvul4|WQ- zb3!}f5ScJk2qjhP`iR4w#tq7h{Pge+n|UPv0BtyEv^5g$H6QqKKG3w&`fy5OP+5Rx zJmPgj2=67pe*uBy>N*Qh_0f`lYbs8aYH(?%_5sBPXAD!kt{$NHfc7^`p9`H=rRuA0I2XdSL0K^+Q-$n-vT~-+!yDzK31Ag(y2W_&mC3)%cu%l0;H#|2}3Q2R}H~uw@ZxzT)i`; zg{m|_g)M4II-dxo1#w?{G7)qaq-Zae&yP~{_mYYP9aLG!df;pa?u?^NUrj2wj5z2KRQ|c`lgG%>dl|qIKs~?=_?Z9)L0^&apMu+Im3bSMHKkceBChTqtY4!m8kXI<>$rDY=&e3V$t5kS zoz6-vB;#Xa=>F|@O(d=Eu-I z#m25kpu6=40=!?~I4oaV-%b}75R1ZTpLe zcPJU@?dNHfy~=$bvpF}QKDVpsnSnD<&}lk*-U2g`)hHq{cw^DhJ0qK zwM53_YG{CadLF_%#V{+3lcNZP)QqH>4DhEkK^~wzOYr{yrX96v-5kNVow7o|KICfz zNbzJ%l{P~ydCG03qoqWUoXIpE+#j;`=2qcx#>%Y$ax$T9R!)fKX7T#g@k}*3#~*Oz z{{VEUAo&w+FT}L4rFI$xKHwkh*Bks#qT7Y~JB>vYw>zT>E&7}H5Jl7LP0BJzRgZF-Jz75g!L51)t>2U1fdF`LW?2+RBCMXVu3k`n$Jndq*)vwMw__sZYz>j9cNV8rphGL8X-} zxU!+7Py>yhZ2cm{+GWiNHz(p!=XavH2N=eQl~KK!Vw(GLquNxb-teKMy0WDc@5b7F zzS7Z@Pk&XTLW1~nQkACC{Bn^}38+Z+_KZRLP0uk`aohg@NC*5MeGUA>CL+*!C2y!G z)7VBLco)%OYjDQ6#>U~bWIb!I~RbGi>@-j9C=aI>~&Hd+fm2yh0k{!3PZ9_4HWDN;rj}OICPCkF! za_!!oV;)p#_pEu^xRZ4*Gny(qI=z^#mWZx}O-oG?RI^&)(z#W@RdQZohGGhZ2>N}v zM;GKpb0gYOPpO1`9+P*1ifKvKoj+{EjBdW2#+!&6xy=bLVdi!dT2KOob0A~r<0&UV zjn$e+6azHn_u$w%GPd@ui9Sj07@wU3@-Zgt-F&2xm;{gEfp0L|LKA51KXwIZD%g31 z>M0#Na3uHyLx}FPpck72WJvxvquoElFL8ae{{WdDxBUvVkNyR2A}Qz~uRs0xUOsoa zzd8HEclS&5_J1*u7K)sLm^}{l^^eq)4wM0Aj2kfSUHx758x&CtXk>xfzigY;HH3Z_HazC9N%H zH59O;Pqz@u9imJOh`gA8_Fug50n7IWZadcc`*Gc3uf3@*BqWu2q@I<>&>f>6_J}{b zroLg3f<6(AQZj5wxakkWSrnlmN8gFokq5a4T8EjD9H^&SkAH;Xwog*Z%FtP1=D??v zrH^sIu1yEV(w7oq;x11dTe@IwW_I|zxerhg9dNXjR);c>O)|z)yS7;8<2!O@ueEnc zVO^>^VxL`CPEJnV+n3%efdz!jhN!nDZV#2RQ1S>N3Ty%?l`D@A?oQE;J~DiHS?L>Y z_}@P(_>-yX9?sJ)S?x|xX?8OT%Q50;9BDAD!=)DdkCitIvRM;f{Gaa!`FQ^TnHBEK z1oB$)BkN4l$&R1N*9*yO$ahj~$V6<1mx-0jK0a-0V4~TqKvd8_w>LSm+^cePii*=(!??kgkwK*3lZv&R3~*lA!WvjYo$4Runo)`710Go z^ulU}I|tc-rM3jeB**Qi`EWn!p2NO$(713n9rGspor*E@CtWef0VoA$Gh>NpO$<11{)||@IrN>0JdZ(d?Zr<-m!kV zB3Rsl3K-9*nxN2hz<;M?Od(#13ONPU9MEvt?6+hQ45*-rtj@VzR7!h2xE}uiZq2Q9 z36A9y6L)WgW~Gb+cemGz3Aek`fI)kGvVJ<#t^|7oe8S_qu%nhXYml#g2f4IbOr9I0 zf^OMar$A}b6?eACAe(!Xg!e728c`XHS4#*o8L}{#4I~kxJU`_U73Il*yBR62mdCd& zMJz;#E#7a$AmUak0%&^;Fb&8*AgDAXJk!h1QLX{GX|l%M$e}Ixt|*dEPPz5PUE2+< z1Fc_3Np>QWT)x~^T(Kp@u(yzyp17vDCbjLr(#MrT<9!M4!%;M?Fgu84yp)1Y+Z`*I|4pkL;d z?ZR~qHTi;lzjh_57a}v~s~uCh5FeUON2Ui-Xn!g-)~?l41amQ*huYQOQHAGcavyn= z?K-~11(E5{{WkS(JLc{ zLJ=jeFo4-V5y%6B95fQ6u?8!pI$dSbMqyfV_F@_Yre${4U8ymUwDx#G&+O@jP9vhV z3;zI@h+Jt9)bt5a9c}oMS$X6wre}0%gp}j+Tnd1Id+GYTx2p_H6q#iB>fTE?U}@@6Dt> z`W|=9QKHkNQiD%v@E-QrtYKtw+NxgD-McGyy_=2CcZtzwBY;jqwZ=*lsx>VE`6N^X z_3MY!>N6fk_C`9VYG=qj!vj<(`^Y`R38B?K@(*ywXmvooS$)S6bw+g2k0oB;yAjZR zPrhA$=rAbbVIQH);k?3Ba9QCMnvsw=>-Q2lBg((mf?c*xshxppazQd=?U+o;WGnlx zCgSSosvMM{<#F#nNiRNxtLAwB0MyG%(?gLm_AcL3miBFT36bQytr8m6i4EsU5v*he zc=lzG>UiU}fFyC7DO1`n=tyN{D-T9B%e3iG{upmupOF!E^RqzP_vdUaKOX+{b{Ktg z8fj>37t^RC^#@Vgo)$OVM#98c${JXBDVuANRn-r$>YE^g{gN>H*TEdE(}NyXP3ydv zoete@-S>!pHtGAu7}TIBif=qU%!9`e4-OK5-cWy0$KHE-ml&Z= z3Q(0A{rLG}=f-^_ZVhTDrK)ymt|cFF8>y2~m*0e`EkQhi)}2lpa^n@sp_ZEQq*ArS z4b(%8A6tZ$_ydjd(tpe{KmHc>Jwxfw#>_u@{{V#Z-|6wCD~#fSUj*xl0ryBh*N=SL zWb0GFbKt9?F0L@tYMP$>Ut66Dc|xXsTD3ZU+;lEc2`hCBMUFy&?5$~mZiHGOe^qQG zam}ESL-Wch_WpszCQ2@G_?9b=<0T(Yej{LlguQl=Lqc<%~gcg6d|38hzQ}1)-^Gqrx-$al%jt zwxe2(y>Z7HXdT$$C~2d`)5DHBh7^vPN52D(p`R>!an(A8e52oqIqHIH0p+LpabG6t zE~b2;{GZ1OsFgG2{#t#Qo2o5L`9Ikh?r{&ik^G|(YA7mNs?q_(y+%bYCp~_xnR*oD zrjB9R_5_smtuC!7e++6nO@F#f19<-ck0Z&uL1?uyiqI_zpr%5V$I$qH5#iZC3n3Y? z8EMt2$*LhnqJ&p7jU--%O8WH$RMZcL&vC^oF_9Fdi4SaA)X0y5*bS+Q;#ESdI|ZaYkQEW3Bx$dcq$ zO^(vS++;l1DsNb*)B&ejVEgfMw$gN9_8R6ce=_p-r`}BCZO62BFQvi5+=nHm6Sp^_ zcdgFr9j`6asn0ev#?X_g0E~xIiVS=!@t-r5SCMCS{kcY-Cp>@Q1^sK(tIepDX0P#T z{{ZbVXtJ8{Dy557EkohU@HH2UX{9Pd>OcH$9P;0g%Xo}jV~IBcP!s#bwLtd#!4&@h zqlEdlUr29f@th*T$!6w1T5}%NO zG!%eRP(=q`+_77f*D*308*0n2?|X^`z7F0jDQeWyj%X>=--sF~JPQTtCAY;F8Z0u> z5EL><1KmMPBP?MC*tXncuTNz^qXAst!Xxn`rW>W7{>46$1v}51Jq%8k-#f&MKi5%>YM|h9{f5| zsmU4?D0ReDWSuq#p*`4e$_K6g0N;;C}FaXxnXf&Tz7qfN((RZo+BL)zN2{{WqhjZ6!TP%p~0)i#$^{>nvv9C6W& z4bch1T^2xa5SAs!qeChU(tg*)izD(u*IM>>&PZ?C$5_GA`^0SgO8WHn&11`d(V8^pXEo~Uj~s^RIZdcVx+Jkzf{?JYUZ_^zxbWF zkGILij?w;jd&>Nt4Df|9ul!9}{{XxlpW4UOc%R}&g>Fmpq&|yaAXEnr4m)n&?B4XfBo&{qUbE_su_LJrd5P4N^vg)a zu}i4Zy23)+S#>o^5aH5#d?aEV?1dW=uaRfASaQD6;dI{3ZSIGd^` zOf%&qWD!%?F&{fSqzR>8%5fi1i;R%bx&pNn=hN*t zSw^QPL!5t9JI?C1aDAt4?d`)aZ`=1%vzpvrBKwh@l^M8dP-#vyLiAMBq!157mNuP- zViVk9%&Mvnn^%W+apIBWHrZ)ugriDnLO}P|zaMZ}g}A{eDkJ2)tgDi^+?#B0{Hk)O zt|gSJ4b%r*L8iKUux35d3B|D8w%obXW5ABdepI&-+-4FrEo)K9DFdk|rW^Yp-bLGPs!a5M^l;=|;ktGQD{TGIxo7Sen^JYT{l1y<-ne|r+&79vvX;a<1TPEeqMJc+ov>soNuliFSQfPVfIF0qKXfI~) zE~8YNwcAEn*W{CrW%@@q9GOte_b#~KfAT#0m#^WhK5_KiDEM=MaLC4W{vm?Or ztcwk5g6#wnnox?+5sOGqLqBrssIG>y&+f?Jnvpw=hLFHjGNu~GLgg6bu_>t6n`DQT z(%6S@+@&Jr!+?V`56sv4Y^7Xt5ZqNxoKI*W6pLhRK4e+Z}N4IxPkO{ zWIM5|C?Oh_U(#K^@pI-&L@m9^=;7Opxbje)`r~2LMn;WFK}vxKUYZQC=iTu6F{{T@ zbJ`V^_IkMVF}L?syN&leZQ;#o=X-#=EXUpkG9ot90fZ335ZFkfYf^o<@Qcum#^{Y_ zbIsbEeW{YCBKno|@5IhH#{1=KJ*IO-K~|RJrd-fcXh)M5%w2xWPlwyLuJBbVVQ&1F z3VZY`b=T^B=~CGHv8|6^w>yoRB~dL7Hrl<)mZqzVmD>3nIY~cGy8bC8`$qJSs*eq_ zQ}TRSx#l4JB}szM;HQbi_WNey_>lhq(4_wW{HpvvsZPCSeL=Q&Jn;qNiS0aJc-rzV z@*zoUam!8v-n`94FS7)K@PP+l@Hxq zL2>FA1)ziFroRn4@e>HNC@|ef&o0bX-E<4LA>U?W^#KRBEK}|p+J55mYySW`J!AS8 zn9@Bg@iJ?gU@tHI-FW%Z{rJY~e$vx_u-*MUi*nuBpXC{0klrIwj(dEA3A(a+qp%8? z!vj6~*)QpG^CK>8-cxLRL&s0k6D@!t4in7|uRg!>JDi2&$EL zDWPrK+e4OYE3T)C{#I{vxw@xZx0S)Gt?Kj~YblY1H2fmH_}7^rl;%|98tM*%RDpyQtp&SMBN)glMOE042YpNSQW;$8p8ET3m}_ zaY*@?zp9w>8~_Knln!PDOo`9? zgyN%YzPc;ikDvEESMyW<0POz&c)HuID=N~y++5k5Y6ClRL8P|IfI7hTV#eKzb0L)E zgRR4gbn0+@*t5jh?0<6wo=wjLD>BWksuiRxj{0Luhlzn@)uhV%moLkJpqqpiXj*M2 zyQOh&`u;7o>52q+cV)$eU%8hJ0n^<|Vy`oNcQ|k+w}r}l$@6nBQ`wHO9(`&p#mnF- zA~PnMm#V*RE^Xo1YgPj*@(m^=aC<%YHr@t3dJC_^c2LOBA@n-cb}QEV5wj3srC#=wrRiZ8uF6y1pT#YS3r^5(-TKxuM|M@0b91B zqfi65;>I1lZ^Y?l$Tls}>C|GzI|em1;z08zXZF@TpMD+x0IuWt9rLpX7TDsw1#vR% z++r7-GX~ilbO7Q10HIj&7w4uIHs*VV0=co-@+Mi5Fm23$bb;-_ceXn*S{$<^C%p}{ z)af3>iaXnNwDq86yqPgE*4s%m753tnBHSPw3CW;?iAGqfyK+7OaFYqF(zF_Z#Z}9} zF+zINs1ef@6N+puE`e3X9YV6eldys#cqa68S1#VTtGQw|2)A{KjxdqYCj(rw0N7n& zMY4)lO=;}F7c4*`HyD_<&)MCIyS5jYbY>EHJxx8>25V58Y)^MSgE9EvYm(G5*i53M zG}jPHDuD_?cNu7m(?fx!ZY0pTAcM@aQ{kw^MfDUHZsdYaMPQGI+%a8oF94Wdm{>+? zxizcXfz^fJiJkd*nkkOD7|8X@2<>UWa=Yfj>VokkhV+BkfahjaCXQ5Bh(*W}1yiIA zScTeY1w$?YCvC6vQh@t#kSR4PF54EkUx z-AuHxdhEOgtl2P-RpT0K*8(}}URbqqnvHpzf-6BmBoI1Rw*i2Uz~SUT`DT}_TcEnArvSjyEv&-)t7E0jHp7w%bW^G*PnJc?of-8 zk5I0-BBxL-=U4Mo;oU|}$O5G*y(lZ!5_Jd~0mGvzjWdTFX0c5=(%~da=*shtd!2(VrcoR%ZhRa!4H( z^c46kLbdqN=}EyNa>z1>MYPorCq^AAhm>e~bgl&EUL@(do3@#8$t_8q-ilLNwfk}k zt`nIuPDkQ;sQM@&H&09)pD`+N$A8W%ZA)<>3LzGF#8e7*o|yS->Vy9Pe?#d10H*Z$ z6RPs!QB)(h9(@T^+uaH#ITadf?ZJdK12Mb*0H+Z-A;uLSr82}J6gBdCWsW+AJb(%7 zmrrgR)I%e0Q*SJPFY&{N&>4toae4xE6VP@37#^a~M3=mH_SG@?H=X@rwakd3iEw1z zEv!7!$5gGi9tuev#9)_!dB&+a13cSmRBusU-?_N@<~73KHc3sK(x&dV_XpmGP9l~C z_29{+oobv)r|Q$zR^9aNV2=Zj;KJmNL%KG%lO4IS&AQbU6bg|kO{kTtKLbh3(>e@W z?H#=GAFcA0x%=v~A4;n{h5Di8mwiBUygl7!1AAt@!c(w?fz71O+N`j+m~956Lx}$XR*7l<0H|iZ5oY%Nm$+NJK~aH{912lN(@+4ddf*vukYRTF zFF9+_|%-j#^`z=r7(|(-)0Fp*OzW)Hgk3XKBQ?HF09oHL$5B~gP-mpFs z!gD-HrED&$IeT$o7gDlGAU`DO>EZ6ffkQ2_0>@|_doV=;qO~AzRt%5$d1A|~)1-gy zClpy;CEh7!c0(H_*1B3K<~s|L#U6m4*Yje>LXt) zR=sf<#@<-=MAGS*+Z*pLD4Gm>kKUuJ*J zMPG1#$7hoXVzy?RUZRyWqCh<>Tzwmj@g5(U@UkKKc#C`n;T5kcEm`A2jAXcn5frA0 z(W0j^J1~=tL_@1;Fgk2jn+epy~fZ%4U^`3b#%b*Ac`rE%QF#QisDxC z8*osi!YZRvf#-2Bn8mU|+BSId)Yz1=P+d@XB{_tE2&wMN1K+wAYAkfwtoD0_?k%!J z_TV)s#}VhP1xZ0bDjJBT0RySSZe3I*pjn%A2d>C~)P_r5f0kY92n(fh?Cr;7O(=^O zsP9hMv&GyjTP0jrZ4RkmJdz4U6RRqmQ_5{3#uNbF_K1*S%v3o{&1EH|m9nI)Dx@6r zl~R?!w&^LN$E`@vg=8QmnFVScF>2XP-lb9Idt#)%(~W+Vg(WCT>Ds3#F-Mv41=@>h zrJlKZSK3@jwsuzhg?hMLcNOMJ5iPk6C*{!F(MzNy0V6PbYl{09D(Dw(EekBBwC&Z0 zw8APavf4ly5USIhx9^CE4vUj zNkd$5!I+=lC*CmiEhsmaTAn&ReXS4r#q|#2fGQi0N>8y6Bd7W?_HSUn{{Rbt^M8B) z0L*1dXwcM}j=_zE$DxPmuuXm)RQoVAB~d3D7zxwcTH&__qzS12r%%O|Y5xEwA?8ez zmW;!X6ojiuQB5)){6lrp0odOWD@WTiU7X$BsSavXQp;>;1tBhgNv~XFWsu_NY2|{h zQPdvYvhFN5Z;afR)F3I68@GMR~p}+L_{$?;v+_KIQ=wHXMEGW}futqN}N~q4tGEY$5 z(#;9$gyp*hw->Bc%^;FAigoLQV5otvJw%V#t@?=ly`>8FBO!;32WrIwI@{U+c4)^O zZZ_qyYXsZd%b}q^(}e~VVc6}^))drKHdK0ma>F^$MYzz~_QPd}S`$#22_4;1Mk_ZZ zR9Rs~rV>LSDIgje^&Qxzx$v_73-b}{cj=DMN22~V2^p?M-BhQ2#ms!Q-2VXcCGS1g zZ}=nbFOplKpdGlQg;{3gLH?`(N2V@sT`8w;Tt61EVYAofA@3`%%ZcGyOJny36mvT3 zPqbs|n-it9c!uE`U4HT68aff5V9V^mFlBoHZI9Jt={c<*^@aq`gXl^787|zDyuIV+ zku1AzCpek*s}yTn%tvIcY%-RKTH~itIzY_#;|baKu1dt5opoi`bK8tAq`ZTpaLR<^ z&6FZ!IRK_q2a+6V)R966Jw5ol%-C&TRa#5T2M4&W+4Q|*HS%S=eC z`(gFwC^PaFdXHhnC(h*I%a-bHE5y-94;wXe+R{(JidVNI{^r%X?v-0qTn*0ilc&l; zloF*7e<&H^P0v18aHQenF4KbTWkpX-Pjg&h2Gq+f#4#UVm@0qS!<5@&F4L6e-_;>nIJ;yh=SdeL zJ)`9*{W$bK$8^kt_K;hr$zS&#N#ipjv~9Hz3aOX0jD5j}t+af1pBLAD@l->au8otc zywmvLnv<(&QFbfH4eL!Rkbk2Q+9SDeO!>JmCPkeRLZVB8;#`R(3Mg@>5OoD4jKK#6 zT!NHnz!LQi$2ohD6t%ei@RcySYOgtA+SjkgTvp1&W8e`9b7%n9C|_L*M3Thhx@xlH zU@Pn?r7%37h)i{KJMHv5yt#V4bJ7bzq@o?2OF}AA6dimHzRY~Z^>tSsQBg&@6+q1J8(YV~XzT9UTZ80ovkX7ZWAQH470bH@|7IxgZ zTwKnG#^27zjkiUq&R9}@bs|GaqfafxvsweCN-7x+opI?H_Nf$^=Q#;EHaa3|(c z0HXyW6~J9#sX0)AxToCowJ&jRwnn#O?-`z=5L12G^+ugmmg!uoLxf|`J4!W-{{Z_Q zmD~&9Ucv@Pz%i?4V$o;4A6ur~Ilpf}wzh{QyxZvsQcCqDNTCTx%#+wL=iTz+@o4hhS*tIPN!G#ylSPB)RLo8ap_i|lF+E0Ot=6Gj9tHUvR&pw zHyAJ`$98-GtqOgHol4wjDD>8lMR9Fo8?SA~E{*t<*_quLDLw~9>^`n*bQ2llFsM0xZ4dP zN|dmsmY*;7G{e@xEv6BU*= zn>DFDfw~j!B!5OKE+m<0VQ3v(bR}IYtGBxkx4>AfM&0!YLy^-=w5Zn*+%2u8O$P6l zzVG3H>Pb;agaM+RxLc_*NO916S`q_@9r({~5AxbC@L%R5)PJHAPfPq)o?j^LUEkst zkCS(2`S0Fow;gW%#i~=CH06QgDgh`N^H<-A(>tb(!^-UIhjTH#Bz^M>!)kX)0V(G@ z6i>4V+aDWoG)mqo>T|%$i1!%Kh>)qHQsXv&pb9|M_TW5;YFk*~I9Y3^p{l18cb!XXPNcKN*2vShzSd?# zdf8OQgN=|Vpie?Eitl`<4ZdzVG>xxnW2xmvsOs*_w{AG;-%KuI8{3xWW1`z{47QS# zx}5XVT#h~-+_P_7)lKo>eHT1fMn4aM-`%A*PTAYQuAo7R6ncNSPCff<%zrN*aq(8~ zi~j0={h#j~&CaG;v2kNCs-7sK-NuyhR~GiSDgn<7IGO5E@UI1Y{F~wq@n{@v=hjDz z8uy%7$N;@Za4{{Xs33VA7T zLW@ybsI0J_{8`xBZT|qQ3U}X-rrC}FPtAgHVGImp{{YAZB|jkA9f~QcPfuP`|xM#_+RQoe=Bpy`{$y6+NbXr@73_X)P(tao<{!wIT!xW zei%>HaMuQg^e%ZCeB@vIRN^PQgTiZ$1VE!?~p(S=?iA2yv6wln*QwP^NdEjg7Wbf^z>)<*FT=P zH~#=+yeH{s3-{N%h9G^{aFg_?pDO;~r44Zn&hEG(+EMrUg1-@_2YuZ!e@HNIOu3m@ z1Gfn-<+oi<{W3_$&%sdfIfvd)`J1*78jbUi?C` z_WuADDz|h8R1VdTykXxpw|SCs-85hAlq9%w>IMb5k+(>6Rq&eIv@J(Vrh}=j1iNc( zej!nwLR*C44bRKB)fbQQ}YCO3gWKj&5JMY365poAfwjC z2=GW1?5}Pr@AmW`!yt=!(ypjTlWrs6Ay}=su-x-SjhxRhG23K6cR^aIIX|5@~X*` z!*KElI>Z)5ROmRUyK@@X78h>q*?0+$+--42Y8C9vVy6E9a9OpY;2moY{Md&hbX%F| zv0mK9D(`nR7Xs4G#d<;nz4r~hLt1miRQL2-SnXSkYfR)w$qJ*%6g@Kjyag@I$h^nx z3HYI@H~@-5MvT5r)JjrH>FvTinU;3;3vn6~c?L_tLA1~-qKU3L zJj^;6F^>VQD|1VKAn)6P6k0(EP;WbQebRLjPiQC6r&?Py{8}0jz{?q#tc94A)Uz! z(fe+mlyblHd+{B$p*7UV#wy30pD_V?jcB7mPi-(@me9XMEB^p^pxGht%oAR&;zb~?}#l*QOINlK6mc@mfi+uN)D*^UfA5HnT)4B8ElsuKAk=Iri-F9 zD%PQImp|x6BNE<+%8FG?sZVYZl7Xq3kxZ$9PBG*={J7!9$c(JKId@?MV~*1TiwN0@icL$x%~=iV0TdIPBPcKx-dfYfAsu|kMBoWcAt>2vMa zOu&Eamsg23J?aZ_SS#0#uNI4paPx?6OOZ~65|WXn2DPV3;;$pLxxN`tGRJyO z4_MSdx9=^tyf>YJ+ZB>!keXmL58aTw8TI5#oZEVU{3jovvj=1PU0 zLyX5OO4}xY_)av^l}cWqnQ)SYwgO|RAd#hdf&4HT-9nemxT@8@uQR?un~GB@;9;1#m^ zc$+&PRAm`e!46n_feG1h-;uZ6Xr)4hw~(yC$oQymEzaL`_=~GQR^iR9vPO@{dwwL= z-kA<;M-+_-K3GygSALWD;*S3SY~6erq{8~sjHAmdWS?!_8y@v+xWszs)O(DQg~4%b zsVmew>OGjPv9)E`op_>6%e=OI#@8!JNHZ<#Y}nAC#hlCTLUArWlq^sUI(vKZc^hr& zc{TY(H%Ys3~qXjkBN7mV!FGw7M@47_Ro1n^5wat zk3cWLVfI*^1?J_#nA#qTEu|&K-EG9{LeQiV6p{@=28SL@l%py=8R#*{RPz#37$lUD zLtnog8H(fLY#IkJoIGtqY_q9AQ?G6Z9B}jNzVah*M-?Du;_vc`5^J8DTf_Qs>Yq$k zHOU@*-@oBUpU+PhJ5()&DmB95)F~~@eQiWp@u@2zT7UqWW8JZ?>qEwG+N5e&ry}X1 zh^|BN6~*njdTLQl8iB2wYM;}JY?DKZS#6}-jrt|@`pR2Qi5E!(1K2~VQOA!Z{`y~e>NdaQ#U1?mbp@+v z?{cIKN?pIq1EotVIK_8Y#BBC&@f`|ztwi=HV;ow$fId(5al&(&=udVymViZ?^cD8v zH#bm-d?7N$Dcgu>MI@5~^3HkVs5INl9^7-HCxq%_%RTt&5*qnSz_mK2649xsr?(N@ z;BX#+{J(}JI*8Ag{g1;5Q>cMFrFV=aPN}z({jb9vQ@MeeXa^HY`*7&$$3g0IU+Wk8 zK8bJbriF5^wj=<0P)rIx!yAsxwf^lgpSVBcv&po9OKMV_&VmYl{Cxqp{6~mpd@POI zLQ?Sxl)C=_Do_r8V8($nEpbtX(#Z}WmzI?fD3u@)mwj;sK?f$!i(TcmilnLG^NgAi zK~Spp3^H5-kuCCENe)_WE@;$UlT@@jN1M|uS0_dml|~cIGhmG#?J^av8^lsH61NOHxmktwklGN<3XFh^^ry#^hLV;x(i+RNk~_+fLeGlB|KbItteG zW%)%%TOJTAMF%lcnu|Plq0#>UQE^{|#$0Td5JtI?3YAWppsN%(7oSmtWK)=|v6D)6 z;^I!HMpYJDK_F#8h-UN-#Nu?L{K+D( zp@iwFZn)K4yP=P8pdgK0l#nTbeq?XUf;k+3NJtn?r!eOV4YzJ{nsvs&WFZP6Cq+#G z{BaYxL#PI;?Zqlb_?FO`{!}1or`dreb3mxpq|acTJ@`&3%aiXZY=RUPQUN{0WsJT1 zu~VU^vl;SKET=D_#>+P;K?*;ppE-GtW(MlOiS--t89VOdzt=d#I}|#*UE;@ zdDi32Ry{T^uTEEhhQH1mX2r-69TX^jF@oXGG8|)21pff5g}vKWp0RB(oL=h5g7qZP zCO_({)7J8Fk-B*P<;M3#08PdTZs8Ct8q}e(l~etkM{g4^9o7=&UO{Vg!^zU*d0X}e z(L0W}0;Ema+>LFynMAFtbdq37rjeG36wld>4i4!lX*EMXpS9bgowk}2OMXhqND3pU z1SoXx6~)TBXWUxQ)Fi0TcJvrtsDZRHlvnh@ueS{6V$d8mJ5x_(f8@d@6CNT(>;yz$ z=>#G6zRwjf9^t_S;`~8Wp%p;_ne^{9#(w9CqqMb0t8b-!#^*oL?$U{IvO8&J&2YL$ zwn}kLsWX`}l=q(4LWv2~vYIM}pyf(q<}T@nE@Zu|_M9jAQTH7XLVRG1$;CCf=vYbU zSHD><{I{i!ade!8f?z)NCr&OlXjdX0@CiZbWtFD-Tp6{%-+HMloL7nI z3+XR1IoRr(vFfGCEXLtdi2ph#AlT20mH(h1<6ynA~FZ zJ;P_-6s;%3nz_U373m*=6(-+(%IcU`l+V4zj8tsjO?NET zlpxacWchu=V$yDMNaUw5{cU+Z^r=?<4dSbRAKO(_M%;JjzjnFj(Ly9$5zu|^FCM+y z{{TX{T9!oqJqvXDd9`BT&D*xQlHz7|{`$?R+QsX!%QW zvrEt3Wo}8LfptnB>mJ+_QW4||MZOhkR2oP%&Zh)gRRpC7^Kq!@&?%aRcsHO6QH3G5 zl0i$^t1(Oh<|;I9M7Bs*m`U!bI37``Qau@c1-EUA(S7v@=Hk6Zv$EZ;%;S&BeU=*1 zWXMjUr&OMl701iJR+IVE---yWS?Pr&rO8dN)jG42-WO;3R}DO@?MIG0&< zwPqBBppc}V!ZG8Q8%~ANs#JP2iT?my2x0yW;%OQ|`CHY;O+Y^twL`Zar}VPt`?iW5 z{D63&w^Ps1=41FyrRPzZ#S`(JlZGW^uSxF$Kp!FSl@pEI~MHW_*WP=sFkyO&1 zYl&p&st*ZNUT8D|bvl%cfU1XY+lbx4=~QxCtq8oWmLl9Ep~X6yl|Ik`J-tpM`Lz;t zisXqB#mCpUGcF}+VfeBV1bHpUX+V+=R7$YFsK>}}>C=1%4%*rKosS`v+231=TJlN? zhLUplLFgSu3&kk*BI=WLx((!-pl3!>hEdOG1)Tp+~aF(->aMYEhrO z-uFkRliXJnE~q(tSl7EoUfsgc`YQhbIDha#ZFe>-7A{+wBT{5r;K_XvnRCl;#i_-R z21gh^^@TCliL~u`er*oNda~Rc*C&B?owu0w{3|4vcN?-S%8PaV(M~X^%ONf-r8LuA zhfH~wZ?Dg6+vUOwfy7g|+y4Og#u6G?qG^r2 zyM`W4qO5o(KvQbWBx?TvlMQoYV;Zn0Eb3csgf=`i%M9x;5blJBrK&2EX@Zc76omy2 z+?9&!vRuKCS_TyjOIDJKR|^Z2IZZK}xnnJvabtv)sJ2f|l~pi|GTihK6CK{ZNdx^o zm=q}rG2^PB7OGEgb_8rb&k^NnXr!cQ&)77wpBX<0Y?kK4BO)?m?#f6ysMSeu@ ze`*{IF=7c$r?npJURtw))~u1#dd1`Gr3WB%;wRscAraS)ap@ z_u6l*H4M&Tsi$(Qj4ys`x6j0I*Iy&uh6amxULCJ~rODR#nCS0LlLG+JwEH zpnaJfU0G6GQz<0k43OZUp&r^`)hFc0Tj|`H$a%IoE+3vlD-sclnRsgy;5+=tME| zPIsI?uKdV)2-oJ9{Kp;EV)PNO%==H{j_VHS81s*4t~;ujy)zzShIRWfH$4&Dh}+J? zt!0VRH80b@9P<#W(XY&p^sXV6;Zb@ZW>>cZ%RLe+03S4a{{W5}^3g9q)l#XEUIuyc z6<7oLg!kevn`8P+2+1GKPqPerjJ(M@APbhP=z3ran~e!0j)K2Ba1-4EK3Fc~wPl_J zy_M}HhB^wWW}1+%vjfj2kBCCYs7+TD)cEiX%>MvrL~zgw?hEX2{rIo>;#lFYAhEeG zKOTPkO!JAB8UbC^(U5f1dvO=)5+sfUe2pBux^nXm--WrkHn@(4+aDmJuC5RF$M3~d z_ZGZD76S4MIeBxZnxppOH@ivkWYF7JkPq)J^z+Z(hW@KRGEdWEaCP+At)n;N*t^6kilHA;JHo;?2mbM9-?X2Wmn>XB{R zk!7n^il?~O7|fl@^~Y9jusc55;zyJB-Oc4wQAn>`&tJ0xMiZA4LyfW6hk(?Vx$bK# z{Z#>9ZWrZPY%w7hq7k3HO;{>Y)S`Oz$417whep~$V&zV9wXF^gR+8{LfKf?(RH{x` zVrg(gC32_%#^fHUq3sxIxu`YHA!WAZ4zWazp4?3fh=?v5C`eaa$@}o`q0|CcCp3*p z?L9EPP-9Ihr;EUAg!z(HYz<>7lUifGWL}a3%LuYg=^$lRo)nUMt6UTFC{B_-Z4W39 z(KS8e4vtzv#t0C{cdFA$^-unsHRmKd>aq7YEGqwM`Dx@ zzXCnQp*kbBKGm=^fT!+B^9RNN@B48N>JlPwop>6#Yub%cl692z@YEbRWl4`#<>G?x zf0tMQwmi!BBn(XQu6dm8R3zoX*3tbq)z#3D0Lu!pE)4l#vhFOdzFP=NPfW06az@I< z3^wknlF~&xK*6^1Vz`Xy9u}@V>AN;v$*V*aM4xGh(|$}>+SEWbBx^wjAJKkQl;qFD z+$`&s;=F}-IhH3ND%`}oOLjT2f|9V?%5|s8sA@HUQbq)~Wk9`-=B_1O2YsH$bhtQ7 zNS%vhVY5mev{FaohD(4{c%H&Okm+_$qAj~A*#=whZVkn){{SpB6caEZ zJ{09z4!HSK>LP!rGwE)|jWChXHzVhjMSC%aO`54nfPO*26k|Gwz>1^MHTy9Bn- z#WF>!cp};^>$W@qK$4hcDi0R*Y6&2Wvj+$4`1WpOC3oSe2I-M<-aMQ*(C`=P1C@DO zmbn4j&oXnCYq)-A4(u!$&11vDu^ABHX3p+dno6AcS~+(`;`1|ZV43MgrHLp%c+ zw#G`7sArB79B)e+5Yvc0ZE27Oi8)~WXIT)os9i})WngP*&`Hn24MqieM{We#U-+$Wxtp`&Jj$#*oJe|Z1^CPb00|m0K1y@npgU9 z86PU3tpWGsPJ-Iwl_a{|NTo7R#5oVp#j#~`59_>@HF=z`5G<_dNgXH!abE|aJzaWo*Dh|jNf25d6jiZWB%j3qB=N_9B1MK84b@Ea{{+aJ%`ZZ0;G zh)YmCO)F1*a586Hv@h*zCC)ULOO2_O5(*BVi6^PViaX3Bo@?^8ad@WqO%jLuHw=W)@^tFCuj3?AL z`7u1#@l?j+K)*J+DM>(31OlT#Mpgbe_Siy_spI#ZVqY~!TH`}?RVC&bP|K)OV2oT^ zZ6cQBKumHH2^~8yLPT{F9PhhJe4lVg{=8?V z>S%GT^2oHd20mZW_okMI!nyJm2fI?Mk1CJ+*`Awy{zOju{W_ylV($3}O?=0ogZ4*N zKaMdS*ZxCovaiZVK;2~YITMV5U<9b(4}WeN5^3cB0Bi8Vi_8>LNmV;3gcL5OelE&> z7>?kSH1drqa>Qp)(6j6qWTR7%AWDyB6{2XmftJZ5Dqtz6QbTVm*@-A|uarNR2|*3K zk^Ys(a-3ty_I^0-Q;)o#XXB2c!G@9gsQ^@W;nCu8%zvz|pZ)_E*&SZoR&amWbewu0 zWk2^t7jS>aXOrhx{{X{MG@9t9M>^7&_e^hy@tnVfjW%tUe?QDjcF3p3rvL-&#le=B z6KN1`2~yVd__4t5I*-|o8$oYmM{!%*Y`AWX$Ox3xSqaijDt^o(RVB7cT$Q+OF_NoW z3KtuL%U78lvtKL5YE(xg(jMZ%q+H_^-)|?WleLS`xgBythYTbTqmt76feGi`D(FiW>twLrAUrg zh-HY-n>ndX0zf_I+m4jcuG{hXpc{}-(wiXwQ6QXFN?Ml-5LU2AX;)M-C<@zI`-T=0 zDL1@oTd3|*RpJ%*ClZ*r@d>6IDSys+w7jH|r&3aAL9b{yjX`*l)rv;mj>CC4oxzjBuevCrvZ0amNLVXTR{rRDU}GQYuAyj0;wFftFiOnu_E> z6vD)7JZk#OPOnwopjJFeUg(=o`Hhe0$J+gs{{ZH@e&goe?*9OrBDglsTC42FhEl1M zaH|CJhx!Z%NGaQ``+i8A{^Fedx(qZ0i3}l59<(5Y_F}JamEAggOKrcU#(V)Iy~3a= zXrC_S)|#TfQ~WVn@$=-kmJ2g(i{Vh}cZUsHf?R6C`}!z#e%?)K->BlUdscM#%R+U! zV5g)>I)3G}+cxZr?YnNXEw&<}OlS-J5aLyuLbMFMxDw9aIZKvIwZEGf!7j2ifvP5%J0OZ_4gzG&Z1p-F1rYK)x0(G^7J?_MQct!u@x zXWVzG>Cle%CiLV@%W1mUXIx{8J?XC|STUQmW7I#$NPvY}8=7$s^X!(}A?N=<|yJ6eTmthqvck z#{g15t-sAZ$^QT`#xqX-+M+XZi5= zfUh5M;v67+?0~MJY5;oy$I}+S#=Xai=6{|0$E7IKKkAB~vlClnOxh|^WW_&}IE=mO z7>jg&?WOKyn|Jx5{iBbOTPWr0)pF9zT6+kNF_P>L_+NYd<(GGV$@`Fqzlr>7%&^zp zTbaGHYHo5OB6P_v`7E;OP(pk)Dmrt^6WNb2E?oIh1G~TXFPgWDtnZ5e8&SRo;@n+-6Q^^EYUgcoHh9Ju0O8a z04^i3Yx0-9$C8#j&zN`KuI~JOk+=TtXp@Vu#2v%W4C}mEaa6kcEoV5`YZ{OjpZ^CC(+g^mRSt z?tVhG>(v7Jx1W!5igQb?htNPH?KtsYtJs#_Cp48=?q5dpCG##G6g{Wa*SbpncR_cK5~ z+j_sN9-s8Zf4!lQ-6ze9FSzmfKIwUN)eo5guv7pD0)w!B%a5m@1I+&bxR&*depcj~ zB~SC7_21UbvwGiG_xCN;sLr(9=_D6)d6^1;JF!!@;oGv>f}%7z8FRZ7W6}z&)OOw5aJt<1 z9e?$Sx%e-2@%Ix%^!tCcU+qYha6672rS-hjSJ2uLpD;K{LW!uRFk3bp?Mk;58#fOj zTun!ZfzO5Q>xEnJ4E`)=Rzz4F>`agOJ_L&%-D2S5MStQONNnViqf#qT?Cizf#<}N2 zXZN^r!%`M+klbKUZTPDx>Qr3*yly{Fmzj%sv5VjsJg0DIEU{pKg%ANz4iZP+F*@7i zO&sVi0U^#>+|@o+x&tZfA(a{rwOB`i0Dqm6U}@j`_URdJv3O<^NN}Jw)Kk9 z=*BariEn8+#7C2P-!hJucYL&0u7tFo=))AXCHYnn^EX*^Dc#jE;(RNqRLI>F@NP*;BcLG^{8DfrS1S}+e|Di?rMR9+J*6!l;+zF`Rb-Z{Qo(G-<-2T* z?C1V!Q6H-QojQ$aS`25mYI3@H8ojkTqts8MCW~H~c(f5pLU7aG`tkD~@BVl1Ewf+d zeZ`grn6HNtHjIT(Qx2Wf#BnjNxYrTfxV#1BKGu8ionyI8QnhEE9PbX4P;s?UL%Rf2 z8fvJ5eOO3sk53$wT$-2#*hy`ojXf`ja)G#0z29x+C8TP@%9Fz4^^S4zP^ z`@gFPuIaNS@F0KEZm;fU-@DsXGlq~4y9@f}H~LWD)91}ngQQ}V|64#&|$S#vY32l>LHp8qqhdd z>O&w+SbE4AK*%(ruv)%C&^v0adoeP)MKlPJT&qk=Rx&|=G>TTZBF)VRTdA7jUZK=8 ztzGzyF*#5pdDjxFqb6>wl1>}W#;zb0SGOJ2WVA*rhdZLs*0>w0EdZ8!U}trYiZQ77 zzULEg@BvW06!Pmfn~T*BHlQjuAsj64!r?UgS z)sx2=Ad=^({%X^vI$MiqCOD`pXDAa@99PRcnPQ+LFrMDHZ_lz%mI5YQ8 zxVo3YNhds1W@d91n0DL-M9sX+g_@dIu6UrF5vRu9B?Kb7vZ1fycibz1Eprb<6s3A`!x@NqKb?LMV>RN7Pi*PM8acbws5ir@t&C zx+}Jf2roJDs3d*(XINfD4x4)dq1Eag;NksKxe>CV+t4yKBdtltbr{~7G7MI!MEJWi z#BSt{R?X2V9k~w5VSQ30G+emVsRcxKRAE)GGpcBtuaqduNeVp)8IQjaa-)FUy3AKs zNe8fYVr6LH70Gm;?-Uh@ITcCgzzt6<^jd-K&l0JPxxg#tys|t*)4LtiBA{?Kr3#Q& z@4`Ezip-ciXs)_KQj%0i)9u6)x{=P)Ip@s(09CI|UY~hOesJdH+xw?;Z*Do7-M@3S zY_~`GCgm<8Gjt+Emyj1kKAO;Ea_q;^ovZW*aoq4@lNrTEdapCh_n)Zvcvs!2tx@E+ zk?k@iZdZ&Lj=TaI3i6p!lAOP?4nK3VtV^8yWyij`Xf`eO8?;GGEij}dO{gg<@?05% zU**8DvmsULGRoNZ*64!MUo+;)WsnM^xXO~8g*v1Ih;HrCpA=qU?(>4xk2$=BwzmQr zVs)i<^_2}%8_h{H&#evuxq~Z9P6gIJ=?mVLDnQ$*xiq;}4aU^Oqc<4Vrrk)Tb4fM8 z)6S-i2ge_qfNiLok~zumm`_BgEE@j+&%>iNC7?T04x!tK2~@j#xn->=ZAO*LrV2zj3phMbS#j71$yGn$|ZW9sMzUd$iP$TT+*)$Jp@ z19P!zG&l6U^A+Ovvx9+T3B`z7Snz)Y3LYZ6X%S&#l30L!{t_`!u z{vdnKUIA^VzU&*WL)Nd7xNSYp9U)UTZSEVk`-?`NEdgM(`a#QE4lO?1CH!|yI1jUV zJ|vMA-s0D#DJoH>L$v+4rsUS1C6`($5tJ`gUY24`n`gTEf!Yw~+MaSf_RRh26H?;1 z+KX;B^l2(W(xjA-K~60(c07)v)5NXI-0gJQJQ8}e+WT(F>U-1$ziaYDUP0Q(-IsY= zZ+cr))3D}4xXCvrgK9-n`XT=SDA095$JIL=q_Lo-#UtlE>OY?8z^~4EA)M~YN@{2n z%6ohA#iM0h4L2JrZU)?OGSs%!5o!*G#+81$heOj5V|-xw90B;?d7IwA7Hj(B#8ap8 z9_(AiB*4*kmYp=J)PD2)Frg^YLIT-U9a4cA0uN`u6Dx5Y&dzh))WE-9KTx8QAD|xB zPksTr@dZLPKrKjXSOH3}c>@d*gNn?ws(b{84GYbHG}H48L6Pv2W3N#s&r2$uH4Z*j zwJk?MY$%Vmn68h)A9-qr1(F*DyFxi$~j8M#bv*xO2Uw)cA^Ua%U zpEf54{{Wf(cZj!|uD`i#nN0_og8L9$mfP?gD%TvTXrj=Xpw_gmc=CSx*|DRH?xpHI zSF`s^w=QxvdO3Zc6?FIG!ficIQ@OyzRCuXO@M#+2a8v8|{{ZBNgg^baa{mDMau^43zMs)XhbeGWahrs{bF<>@-Tw@T;jEv+#}$0FYm zWc3-e0mY1E-~~A78g@C-a;YB77a3H7gL+#>=HU*cDyR5i_dmwR*^=i(C+i87f0wxB zv)HL8{(=rO-`i79XX8bwWs?`ukEOWl8M@@9)85?%Jhu^lyqUevUy}pS2h*N1)5e>v z!~X!!Fq7}3TK@n}Fx}byL(q0#@gn;=f=H;!rntx;iE#og)OD}32uh}mDXyKk9)AM1M7_~I=2l9S6i&Kr#&>etdhekC%@`w5i z48#M`{g1;O7L7}3?M*)nMat4Thu&Iyu^^nLzE}3Y3@uLOKJv$YIPOr)bWzl49k^KW z9Tlm|@2sn3UXYq>odtW6*G}JrsPxC6c3=FjxTfw8`0VnPHaPhbU3{jg&$5H;$Gcj_|%0s6JKvkTg6csd96h!Esv-mMA~an>|2Wzr@Z0R7%6SY z_c;5W$m6!S@eqj(Y9T_S#hoz}-5J#$w~eC-*KU)f50sed*6T8Anl7s_>Qp8o!#s~@VHb-2KOk{9-Zz|mC(db+M0DR%(*-(#w zY5j|~*mb3wvrM7V3#oO$odBP9G_l+AG%)LU#Yi`Gv21j~VIy=$N)WYDT2!^9s1B6_ zzZVwmU4&@Soxho8<;tRK;QJQYm1#0qm-~WSmhduKDhXIiV^U~)K&~_Skej$0YEWxI zBmV%+JW7;?nw;{(jt4`GOsHP7sAdOl7C8B@fd^XLcnSg+rpt_&v2E^55?v7fm*8$^e+ zEg&AJ!JaFf=EGvOE_Jnqp_W6Yt^xXr?=-;7wze;!s?)Naa2?3iG!<>qRXUlznJM9q zMd|}3TmJz5L|D8Kx{IK*6Dwja%?BGKZ=cIAf9 zIug(cMK2 z>U*D`uDyGv<3;uON>lw9`xmpn{M&EbeCgg_^BDytrIdmPTwfVkN}zsQo@G0G@Pk9` zsSVpNidVvxk>(0l{oue-bZ9a$O);>-U=kAA*R4rboh^O7%t_Q>YQ$I^pON`ugKd`c ziRz-#LUnzI3D;@Rlc0%8a!U4ulqn@4R@8ul)MZ@#)y8tu{NKFM-oN;(>-_hL10m!=nX8#PGMa_C6HIn-FfjDGQ=V-4YAL+A7hzBQih2Ru>t z9GbDAd9Zq~6Nf9nx31@Ade?G76Y}n%^(nwAbqZ3r%4{D4G@HEnNYX97@Y}kA*_J;o zQk4ZHpJj1(j~`K=yMNNh%Wz(zR8geUF-(WE8tXNonr~2X2|$#T5|hw?a73ip&L|Z& z-CqiNVlk#n1%kh2F~?9RcZ{#w9@3pfC9-G<$two}X8a*ozYtC`n<{M3lc;C*<25gZ zn%%qNN39>HKZnux6`$sAY5V56kD2?A{#3o^uvlK0`-`wUu}KaAY^uJzT`YP==CuQ) zs?em@{b!Idcd>t-KJpu1-R?Y1EfkdM!Ocrme*AqoYy4~6c!ufzckdr5YOS`CatTgf zU^s6~8>VJEf8rx&{_(&1;{)b@#-F*CSPS!4yk6wWBbPS|YZOs#H7YX{U}GoP{{Zmo z?PL4D{z~@^DU(=+hh$293^3Jd;*u)b2gI4oPdvQ#0iu zy!y6~5|i)6#y%^L60}QAQE#u_=~4j zTt-?FTf0);DpNgGN$o;TAh~N(x~8hzumVz_nsI&VpcVVE(Z@%1g2!(`Q>jUP!mqNQ zjwt(r(pWbs{+3^M&C2IP-g}kew1zmhju{>=F2!mU{{Y`6vH6#I{$#!E`pK%uc&Wpv znqjpFP|z1&vlCQjC=ynz=!`+k$GD*52~vuCD~X`N?zE0n{aRI{jd<#cr}(>)e)wY^ zq4fLyL|E%T@b3GMCoQ(~vK>HLnWB_64#BARkf@BeW!3t8|O5UI+tvX^^Dk8p5QKdRM5>i8|QbtGZ z#1{rrQ8Y)?0P~2`(x(v$PTrjFbF4e{#lvtPorL=@g$jb%qR9KkG5y`P!z#}tl~J+n zP0Hpi-lRC4GPL@b^)+SN@HURw^#Qqdp4qZ8(-tdDzC>nQh{GnSLJ46?AbUy2pTn|t z_*j0OO`Xo!jC=*_R1$+#K!6A*7a0El0x7bW zsB-+Vrg7`-rY8pF0{J6R`cxgFnP5pqqNK)@J-ZnQc+RyKq^?5CNg&Xmt0PhD#Wr_3 z_)F{+7x52wc0Gte@>;i5FQ_d*DOCz1{RS$!x6rfLJRblHjk&8TEY|i=rrBwiJhs$oY35>FC(PYRGFK`Iwxt>heVDUgk#!&Z zT)3_7))z9+?eV3u`d4mYDqD*RMw60ztAO%h%^h6=@ucGPEDp@&SbS=&!3qw~k1{;B zP~#9}Eyjy#r4$q!{Me*>3~x(W7Z!KP+Knh3x_X)3QWW8}ZR=H)wTfy>D}8?Yy07TL z_?@F=ab!2UuJtI0KXmV%-MiM5?hDGN$7yA3#L|>Bg>PyLN z0%)Z79Az_YR`I98mo1?){O@H_fm|sF6*^K*(bKdTHXb*r+Qs{{Y{GtEb(H3C=_jyFf3MS9T!kFA@E8VQ|zb*^cT&SmdA;w4{?>gSQLS zM3I_cV=rzNGfa3YRI21X_#>z>q*gy%4=Jxqd*xxjJ5t~Ux89$eZ@UF;v#sd+!@Fxt z&$<5q1IuS5xZJsntqg`Vh3iUt>+PmKmhC9g*;U?8n&}TZjU`64rZ)$l+yGD%smh(W z;z|bGZ%geEn$nmOLAGP5AX|r+!$gYYYH^;*e6lpyim4M19i^3zRX<4wZU<^&>Z4}n$_Qe)#eS# zgH5WH!Ej^~jYS5Qs*LCk3b#2_668>zg$#kgvAEg;ltW2aJ#xX;?`S~c!3Gb6h6YmS zcWxhQ;RAqR8sMbJnhYU8(KOEm+q#HkfksCJDOC(%omgJE1pz{C*wbqoWY1bPm0^5#B_~*j;ZWKHA_XeG|spVK*Q4;;!yM zL@fmzdp^GG1J|HLErb^JYfhsLy^^RE8(~KtC)wYDC2%Gd#MfUSkVn4&-oHS|S`yxp zbN2Pdb0aRUh&tVXir-p?T3|n#enD}xkV#J((BP+`j8=;Ot3LL1WLx>Z+{k`fzf z#SzpJC~!qas}iU|`E8o{ENLCsT8OdOo~Kl*R96#0zY=NNpb?=3N?v0QsGT!HhUiM& zDX(Z5j1nhI)BHHCR<%>EKw(sjebu_LfazR0sV)=wpp!N>@3D2n#wlH^3J^Fp)sSAg{xtDHj ztAh%pi);5-zcATB@g;2~YEUDz5skjv^#1^M$dp>N=_0Quf5o#^DJ0S7$CkdSo~>8P zTj6|N$eVj=ar8Tsxced;J-;4&h1;2ZO*0|MHwPRGQ>hxVZ6H@F^~c;B7tmdSVq2Bl zZQP=+zCVHUj`#ITK6j-XZjnzznH%?UUilw=ZoWn23*z?<^xpFqaNSq+G|Ns)mb4|Z z;X?^Z6X8Nq0zd!^dv?{e+rzONHkBbB2=fiYI_5>!a(r0qjFTPtib|Afw32C z^0<%BG}xkvFO?*nNz>C3a*6W-u#qUx7YzqbW*FdtBYlJ~4gAD*yus!M);t30*DBEA>1k2WTBF)$(qhkXwfYs~rmoCHW^?x|`_85s!;F5V$$Dse z%4kUJIQc8Sq@Lj;>CV*u0Emn7^C!&AS3L2LFAak!?Wf&>1fXRsQneuWU|IyEA8CLT z6-p6=sm&Dom#_QH{!)#!ZhL{x@+{{VOe-r)G{fwbN7Po_#^~}^7BvEr2=B)8X}1?< zti|rKlxIXlyt~cZ`RP+`$mFKB^zT$WAGexIyYp{DWs%8N2m!<9&VZQ6OIOBYwskoi zrMX%XN(<{D1=86paK6vr-&DMt#~gbM+xDj1aw@HM zwE*YKi4hR;lT~#AN;JpOy`_G4u_EQf@gC{37-Y1#6p_geH~sC%w%9o&#!82~7wpXf z?xz8^?PZngLgGA7paAJ5L#7iK;$%0t8gb5*?|r}6uQ=vF{zYKuaRH%q(4{E&RtBG7 zJ-*yb5*&CRlyuZ8iB@2HF(ghou`oCLwb5#T%fP>miYiZ=6hgy!9{{RWhsj^Q@2b$`H$BIWxeFmk0 z>pO(f3c-3!)it2#Eix7Ok&im>#s1ERqjp=R=KY7LsVFt+(;f|M_31jJ5!>+t-Au6| z(Rv&K{{YMmpwANUS7BZ1=4tH~Va+01YFyJ4vDYaO|J)j>P`} zEeKoIFr-dBW3HxU6Zbd$SfR)G4Mo3=7Ru}uAJdWudJ# zHK#$2M;x>I$*8B&b+sT@UgWoeJ+t?^quZ z;yIrS8Ay{b>TWERMnZb|Y18bs+sh!kGDD=3)zyLcVoeyzhb(DJdyd4U zHbR#zl%Yymlq#ix*X_h=+-gBmUEHx}trHRwUks(;L7AlvKw;6MLd7x&-ECYV&D+hr zA=VfBYe&n}QJ4LT4DN24~ReJ&O`!S5om*$$>SiVY-Ly;{XJ0(fFLh}^srNCTC zBC9!N+C8|wx1s^L#Bo~~v^99NDXH;R>QsD?mk?H?LtKS$E=#H(XgA2t+oU4EX53nW zq&Dx89SS-g`dL{w3}`HP}+L*eoOe0Jx%m z^8{lHbK_xkRbcKn;x!egZWTx>6VNK8l%Ho|#Gwh)42-fF;Tjy68;Ay-m}^6#z*L%3 zI@bs|HxUE#b**Xc!z{+LL^qo}S9*_dN%axVPUYj>7ju?dTYg(XUP@B8b6@33s4I%x=juhBh@}TX zsU1kvbV0m@f@lj-l78x+zXH{RLzQYKJ0YS}mmyyDn$^_34Fx@>n4Q~`RYRTlE+oRg z+mW}@?Zt8;+unkonIR$Pn`ommK|oG3OQ`PY#-@UEx6)PMLo0KR$`E7Tt}X?9G>J`F z<%_$YI8@Gfe$#UP%0fZ;yONTgm5v2)#q*vt2!!zcp+((z`78Z-S2`TOxd0D-SR$Ep z@BldVcJ_9U*-DU+8a zuZ}fBTYHAt36O^phAsrQ9q|H63e$|X)~{1dOa0O|YeqwnZF?SCoN6@dTGZx2$hQW9 zs(p!CaifH?6RiqAl*Xov-*W9Z5pA*Gx5<}M;0u`4`4rPmo(1X48axd7%SMVF%Wk(F zVn#AyO^k)Q*5|pQ3Ftm?(@L5iwZ->4p-Iv~&)e^HNoRQ75RxnZ0D0A3-^`PNV|B6p zDseXvH!4@*-xoIOK+;snAB$%T&d%jR&AuM#ojN2Fz3weBA;cl#Q5iu=T7pRa>$u=N z+s#{SCl4LDsuD}EjiUQL(p)d5+V_%&RZU9(1E|x!xXbmc2K>#g9iB_69yT{ z6Dv?qEzYe7G$MPTgG_wk-0S_+z2~P};FqwwH1YkoskOkGx)lTJ4zDlK?=z~sZS4c@ zGMgB*?f(GzabD6JZ;!a~Ube}U>VLMXANHw_sBHfLjeCz5-2VX1{p0+jQd`cHLVt!F zh{AeG7Tbt4Q)DSM=}Ngw&USo4WT*{l&-^9m8^p#3{0n&z&8O zMXv*CZlhIANEO9?+qB0L$0T?a`-bBy`3_Pl-v0oZGEU^Uq1g*uX4V5N5_BiS)DE9+ zHalgw@~WB4>+*7bL8!}6+4l5|!Iu7BMNneQur!SQ` z7Tc-+0BGT`_=BSt#FSoPLo}04(TT1t2!Kj;C0WxD9DLJNeUx^E1B~}{{{X<7_A^o1 z{{Zu$?>)S6$NvD6UOxW-_9d_X0H~YC<{oeSiF?nkE%6IP7uo_ORWkS-Kp zWrI=&N@7Stq!&(8eOntJ@XL-B`IT_E3j01!9;x)*etI1Vf3v&y9!}8Pij?|wC=E-Y zss3Y+udx3B#*dmaSLVNXp!8P)6GBd1__7doxgGxi+8Ck<(%(<&#I~QP8u!3OQrMBL zK%gjsGRyIfWb`G@6GO*;Tap?|b++*6 zMMj)eZi~{3ovY$6Y*w``VM8@nPq-Xn@;}DSeUFVDb^6AsN%TkQ@oMUsHsC+}P@H++ zX*%xJF85VTNgi8T@%t-}cs$Mpn&TlK{Jz6dA|>U8xDe^3X{{SHVtPZ6y>m>?|+pTp@t9xG00K_*fRq9ByXemdv z+kZ3$l%C3mlk|-1@-;EJjY=3dK{J8PH z?T5QRyTnoA3MM%;ue9jV+7`LKI_Z&8Hn8C095h%H}-utaqP; z5^*QZKNNS!!^|>^HH>__3b?XXT+*bpl@O&9O*Qx7vcGZaf^A~jTt#?WcFdVFSxfD% zOK2)vDoDr-0`g`RYZ?NhP@=gS9K`5K)QTm*pM(QUuoc+`+O_LNrLeGsTCL58OVn*N zaE_xHt-@ZGYOo=x=#PXqe&g6ahBof>+S_fN*MHjvG7{a1zUEa z^@{ugt@`ryjFmQ{&9?_dJbI#A$Upp`Vkg*`UO_R*wc!Z<2!2?8q5iKel4JS2k}ilr z8kv`EfZ`MnewNUG90{A)xLrEXBN}!t&W;M7aoul98}FOBM_|R?{J9#(8V=xTr?#-Q zw;`!6G;67H5aN`C)}9cPkDj;5+qZeb4}&7!>|tC?rH@RrdKHb3+F3W9f-K{RDfE4>AA9I5k4&z+dv-cj2+sc=sbdiN0+b@OJ zD1y>f-%P+2IQZLhw9=Qn_xS$+jUAEW6#>V4AyuxcTq+;^!8q1!_dg~+per`@K<&iPo2Ufsh7k5t8Q}YOH6fL-+8hVB6WY2DCV~BD`4q&{{TiOwRMp@VV2zO`^GG-T_KzUX;3+g z>51*!s|RQuwVLY0_AWN{BAi2sP*r`HIZC_%+-L}rYfj8at}-@(aM((t4U(!?EE5S- zMuSzvs^C2g;&nP41)c}$>PTo;-H9hU4pB7La4WjUa-bzHnGU#Q?<77k2`R-_@5O7l z+X;*i5}l`9&vrTH5-0(eeV88RSoHv+-KA=2-HNNZE};>(P@|961Ir+u7J{pLuYYy} zmEefm3#sv)a=_EcCRTz@ygN+?ZY!?eh)gX69FmdSimSVXQ=n8VF*WIwCYZ0kxyd~P zZQ}`!%c<%ySATJpbqX;G+e=rd9JeVh2-$EgOeU^)k8Ms8*FpSupa=mnx(K77S=W>_f!o&(L#PI_vIc(_ zaOs0NqD~ahzi;1$1L_2#u8C0xuwqWBA56jXG97#RV1;=T+zS@hUlOzJ#2rC~v??^t zm@!JILrrS$!0w}ulC3`4;ZtG;Fhx*@-FjggR5-$`r0MI5>2WwKcDf^PZR_^reiQdr zHvBmgU$#XEGNd9iN_9wRI8u_b6F>l$6e|5sIuDB zlg1ncDqD%oFYYUoQ5-_Mi!c)S%i6BE`y01^>cl-aFWvU&csA?xqRqDBZZ+^Yce{5S zZQr@~Zs~7oL$b}@cPV#Ei%si!Uo}1TNOSdFqKE}%gyZVnw`hhnnA_vXsE-idck7vV z9Q?${k<V%4`G-V_C!bK9q`zudbZMRPNd{amq|mTkzdRCG6X zu&v8f$0NITftn}Xy|eBUwb;|%iet{+E4wWItJ=At32_NGy#7t9oSlnP+-^}7C?7l4oreBJj-~2 za+wZ3-i%v_^R1=Dyzx?k7E%sC0Z=ibhRJEcHVJodl5`?n*L{C??!B>onGF#fZc|K_ zn^Ke$R@XyS4%}bk+E(L4P-M%f>PRd~F&3afaiszsXyynal+P2YfLw-$lHaQLr;bnb zG0yhCQ=XWgyKJ5*<+&d*7kF*?3xz9aZxXLL`)q~9(tc8D3XvHvrEIBQV@l0Upq5oJ}7UWiMKb$`SSwp}q3Nzy-*WkZSM7SK$U1t(o^ zso$>wg^KXV4BWdl9}&#csjen=^ac-GSPw;(lAbYe*{a2t0SpDPnV-g^`XK^Vx*>3-9#&{R;fq$W6b;O{xRs?p1(2sk5jK9 zA$6}_+<1I7J#sfFsBFSXU4NqzBd%zVJ>U6-Ks-snx=k1R)!Y97;fgr*zoq{G=4YMv z!u*5Dd~_`+Nja4a@$PgyeishDaLZ~}5o|RSkbiG$F3zgyh#a*CXnxCNCOVbuIG)U^ z$z|}7l!p>;5p(>)xFak4Op6oz#4+nIkgbHD%9t`#pLkPv%41!nw#`kY>qWxetq9yp z>1q`UDQokzdleO;KaMaPF1ZtC{{VIW02~he{Weiv6x{BCQoFJef7ytW{W$YJ?bjun zjjQ}??=P~^L(-KwoM(fo7xKsQpT`|R43?|ePrDq^z+Oto|HHQ>c8Tl+vJQ-Ht~Z(BqCs+Jn0Sgn{{Eza2zD)O7^X z59P-bpLt{Xe~ufV{zhZVAIto3v=|J_RB(85?ZOX>)a5_cT@>`MVofqu<%O4h8FL%{ zz=!*)l-t!f1K7X5_HE0ygf1yW~Smo;@VR@jV|>s;u1(EG3( z+qDjk;h@9hJ^8jH+@QB}jd7g6mP3%-ZRUXToI0smcj58DY0z|Y6OBenc{^${fEwEq zo=qrJxrhY)$-$G_n}|BhNR#&6?#(jI8tR?ATa7JBEwcQDElD8eYLVNDJgYp6wZOSo zQt)u9Ey&uiB|(CijmT7$CMKm6r&g+*`|*d&f0}LVFUl*+cCDKlM3R`c=Nx^-sH94+ zYFQ?^`*Rq$SsE1Yh`nfUY%y`Oy4AJ{WT|TTHxl4d@*XWC0N=RDt*r&^=;fj2?Ff{_ za@a$LI$cQCJb9Fp@5I|++|i|O{kP_m@`{dDl$|;d60JV*Oj_P5Bg9ydO(UT8^uSpK zoOqS*ecuh6cUW$%Ta$SGqD)y!fLtvl(H-F7K~MyW>7{9kZRZE(EK1#JR9fI>yBAcv ziK~$Pb8k*@v)VYG^TM{Ri?`Um#=674M7_$mO00I&fQ2FW%z;|xLUHGt2V&dX?>WXz zwraON=y_1(XnH}9=QP^FXh^SpF|wSByGijC6(7Jdp`}R}uemmh8&FA|Hr`r);5VbC zt1#Rpg&%a0j|<%s`H8=2>^npJqs8kPpN?*p!h-3(EHX7~)QFzJuA0cG)j5VlX76QiPm0 z7B8s1Fn+`5zUuz~nIbh#pTD~o2292{RD&+I z`m7hR9H}Z=S{jf_Rb74fr_Y@xiAe;imWs8HvI+o>@<1rBYrLUm;gy>3i*R)d;ChLb{i;(}NI05Yi1 zO3HSWNR@jNYeiv(T2#db)HS%NJwWR~0K}_uyFdlC+YURkwvz};-b2ndur(!ato7 zWS8RHC^Z~~CAjKSj1&cF^C+tRVc&}xT=ak|Y;t_7;8B#Q15HXeTUQRJDX@tnT1azG zdG}%CQ0cKmQf}7g)asV7>s26-oYXrpQM*s_S}*pveqtVX_=`K=a0QZ}B+*{-gySQP zc91pvznG6rze<~An_s3y`L8K)?ib68@R(ATpwbS6Q6WZv5}MF<{{S*R@*98M?tj4Xh*L>R?o!l0 z>0{~(KjU8G#rHq+zj*Ioz~8(2F$yDKU+6I{!-(qOs_u#Axdxj47@GI^%4?e4WC6V0 z0{;MUHT>WouncK-{{Zoo8J`e-I)e6uB9TBQya)dPBN!I)^|EjO045Q-=XWQ`{IuY>*qU9xpYRgaCiO4|1Wcvy1#BzY9>@eVz()$yc#&@~n z{>JD309I+cQPVO$^m}LHKvl*3cB*8n9jK4C6L|c<-0c2*pS1ew64Z8)TtpI?oN$Ix z<$F3|Y9oMkrBP_^u44{^)EeT@5 ziY1^a#RP&g$eQ)Vzc!R8Wgk^W-Pwf&q)>vP*E|9}aU1DF$Wieq`*FfDXW*1Nc^=A- zJ6D*Off@e*Avmer{sQl8;&cKtKdnG0MNKKwJY_N$=-IP*Tx{{Zv73*FoPERQe{hnSRhX~)nk@{47l<5UA;VL#hme{K!M-~qY(M6(^V z)EZKlj&bn->I&#WYBK)-N@7AtN}=Y_n973zTpWdH?F~s)GN|pq)8G{sswR#)Xnx`p z2UgXW-HgWl{xfIWpX4iFs#xXS^<`*yRHzE7z3*4}U|oaYF87W!D}pR;9F+J-p(T2I zzT9Y(^`T1Ppoiu-(~3-Yb)*^xbezYy;Yn8k@wJQIT+cP)^1L)rNKHMNVoOWaQId5- ziCVJN_F~8YD6|BcpAiErHZhzTm!xnrt97A;A>Ahx?|QuHqlZH0Drk%rE7Cxx&`ky})_NeQaVp{uj= z*8(xoT92qOk^BA7)5qm|J5q61dIi0p%TW?-aHuFc4{wOq08UgatV7gUnj!S)2PenWZX9F)?%edB(_~jNgZ({ z=cx^(=qX>JI{`MAtDC7Bhw~JWbHe<(y=F;?9LZX^nmRLQ`i^qv3vu?!+14GYVVMfY z37D~D%Whe9O}3H&SC;BUec18qpGvG`U2^7VxyZ`1C(Utd?_xZ^tIw$E4XfoW`)b;z z{{T8I(wrmz0Fp=}78bsb*k9htspeoOPFDW*LY&9z&(xPSY&C7&-@0I(p?q<(D$x$w z%w;Lq&iRQxU{aI-AZ3faqv@_TE%h#EAt$)nvmR?O<$h%-c4Wz}6D4bqqf(NhO+DE4 z3pU|tQ5g)Jx!sjy1mYb+4}^ew@cy9Nfo%!y{BbZ4VPJf*s2>$Uz;SeR3aIwmPa!0s zbRf{DKFm?>mg;Hti@It|J9<+szZ@x9HsVUCM?kP~=iAOZmANJ4dKSxiF}RyA2FzWm z^0(uKEUIDvsC|^Vr9jVBv%Pr&)hdG%$_-p9Hf`>}0i?FxBY4z%yKm^mL9ZM~nj2HVJ}JnaS&_kZ7s)xsHD z4TlroiD7Vo!JCDH+kzu_Yl)iS-C-yQm67(~Zo3%bYENAA_u*B^3qxEGre_elgVaKccAnf8?qjZj zaIg?ugPl5Jz2-2u*$OU4w*${Go;HGuk&({;+{#EIHbj@@&=Fs^6xZ|9;8P2yNG?faFXT<{8$d=bx=%kf&e{0!oN<; z$dGG*=bFN;;|Z?HCop?(B(j?gw_r+KqvFp2JM#{WfU=~Xl{iOwMC!;hQe;#H{^8dJ zW93|Oq*)y7!^B)+xu?XXl#ab|dv6-;DI#2mLs z_yDVItg4cWFNWnygTfP^F*4 z5q$e>ddiauy=;IwRM)bj3G<|?b74hBx1yA7N4ux=VgCR$$+>0X0Jhm6kexl*VUWKj zIYOaDA+gYE2ib#d-D*PgzzcK)Y3tJtSK21C8U_53tNXte7v_f0EU4gg8R3w>Qb|IH z1x+r#8qjsi77@OvML@Z2VLuLPz|QEj0a6ZHlkUQG4xy)%s{AL}mLyl7n4G8w$W=(t zYxdxmeG5Ycl?r%DYu|}hLP9hEV>o)B!vy@wBk2NslKP5@^v64hUYU^*(eWD7puo=a zA44IBH1+G58R0t2<5f1K{#yNq6ZKKl5O<>AV_@%h+VHKrMZ~20L`N~A?4SPtI@|vM z?1A^+_%rt&DINsMc}^e|sbtXh6P`a@u_5^p_apQ7a*yY~dAb4A@B=eJ_~A$%fz)KT zyQ@*|rU??FF>b1PN57}D9a2XINO8z6NjkMF%*_7)vyZzesrl?GwW(cudSRDIZ3bHH z#4Mc+jpV&BVuXz)PAKa8F-4Q{8hxbEIM)Wk+m#DL{Bt=^m(AzGyv`1(u zO`Ryz6Ys`y6y|1%b94(gDb%jNI^gfCWRJd>n2ATk73mKG+lEwi6sQna7^yWKDh4I~ z(PFhdvVABQB1fRUD|%1MrO6WSiz$rXl+X&gJ4z?8<(@uG?_c@yK8fv5_^7^apw(H| z-Hf;Pnp0G^1$!|Wz=&$K8J?XmsT@*W!oq)ext{qCAO2Wzy2k$i5;Iq8%p(LJI?5?Z zTqFMg)m{pWszgagw!dy92O_Rz;a#QbD~fm53i1SE{?z0cW*nyxPc|%#L2;$BR1TM; zx)S0Q*hV`GCRT2#M~Vi{#)a!{GDqj_&72>5;yzd8elFY|RNLnq-e=kF6I!54#v8>b zcm#kFU2LUgJ%rP<9{GcD-ms-gRPh}6t5=*?fgReVZXK;tY?QdIoSuqCxb6?_wy;ut z{cziu(nK)3sd@NAoBc6vSet)h`kmNU8#7kG;}(8HvfN}MBxJm>zwt3rbtGy(5<-CP z7|m|Bw+m5ovg&HKtc)o-UPbkX=zEE;&%@+y2IckKY*7%oajo-_8sQ#GDQQb7YFr|M zl~84=CmGG3zcQnetz<%t_?o@Xv~g}@^=!z6R?-8WV`|icS}K&UW<7>Fs-9ORNl@D0 zf}3=;B+`wq@JYm%AygsGHsh`3DQHH$M5yKmZXyP#eGOCn(5_#Y;82A17c8hBy4wek z0+%7G5^MQ}AiXL?v&M9hCL`tl0C;%<7%KCWnw61KRZLe!=pQ~KpKr33u5@Xj!EG*@ zYAXbUl5qx0(0!QIhAo9=BjH0=_=Zh8Nh;3+U-(Hy*x|sdnoFNAYf+&}r3misI3DK#(c(&cP&ID2tJ9>Vx5-z%7_Z8DAlx){BkAJPN7io+xuk1L z*J_Xe_^mPqc=L|={{R8#9jD{>o}`~()Ym$6_T$AUdQ@do=;$g@G^H}c%GRPg&7OIG z^9iJmGxYY!=)2wHSG)F={{T)sTj_*B48hy?r1F*f7=0ob4BB0pML z%-M__yt{m=`}H@BOzYA$*uNQdIZcH%v^_J%N@!c#jW0*OEP7)T+j`1o*LSU8cem-J zom>fhcc^mqq5l9jBV+n;=AGI9041AktNdu_1Ff1;! z;UNmJ%d(=N%@)Z6`^Iz(x~P9a!IQV?EA7?Dv%4Q=VU&t~~z$CtKfnyT#JURAFs4((2u9hMDt=i&7SlX{f5d zel8^>kxy-E$GxLxgdLpo4YHRYy!m_qT>(m5@j06QnGWQ(tBexiwO|9Z?ETKe_LX71^z>Hyy$P zW;iIC4(y0Hu9d|?#c>)^_~Q36dZbMrcA}G{1t=PS>A;e^pvY%7Zp(%M*g z3Uk7`fRj@~Ml0nCGJJScvo^SDZYzBW(kd`4oeP9vOKp0*r3V5{6+#UNz;`axCJv?8WHMTow1MRIx`BKhe}yU#qzr7D42is)9CWM{RmH)xMy3l$PEgdu!c@D{y^n`J1^nW|m2r z$u{;A;J@=P$+@;;3%im)UtOI zw^UR6-z>$p+-F8*iF3p!RM*Wo(h%E=Nuf}o>D+ODZx$^XtGkO&L8m{PZn79#5ZI3o zR$gkVwS(*g(-X%6J|NdONxlO$&Qsm)xLa;`lY{{WX0e66uQa_jLAZ2nB%wtdGM z`3ml~P~XJFq5R-EL}(KNoYq2UN*!?abT1aiRilS5?hbv&<3F<7iS- z=F+`u)|dw$Yqx1qDuJ0B)!U78I)E|BGSr~l`0Tio+8IbB{{XXsRv9A9M6-Fi8P=V= zQ2@gUk5vj#5WK+yvjk$URjD3K)71^B4y;EcB%Fn6aup*6xmG1?C8*jYDP(I95?>DPuq+9Kf zrNQmEyu@!y*+1`O)PgDb6O3FgE=n5<5v|mEi27OX}^UY%WWnb>$?-`56U;SV3JZ336 zr2*KLnxAcP^u@1%9y^}DJ^RO6#S)Xy5UT-1Ne_fS78ZR-25Zb$ga z3F&f$f8DF|FLwb-RnOp&i~M)_Xr{{e#fV@1XF~q~=-0pbn8XtJ*lPa(7>wv0dvLi6 zfC?qPBSMt@_bLRTti9ymTWX9u`$?zBPkss_qB7KV_^^itn`oC4T9HFvxZ^wAEB%en zair~!R$P6I_MgSI{{V$NY!6@9Z9n}&-aj+jxlq(gP1l%H2m{{SZ3QaV!SaHszONsM~u(+~4d2J8O-X>jtuw3I&Ru4QWn z{&C0GIG^K3&UugXSaHM>gn`$;1hO~|H(LhL5K}OU*X+c$z9Kun0Tjwwyv1kQdpq%U zQmAfR1A#!2R8M{eWVZMSgEN@z>5d{~*TDz}k}RL6Lr1f({;cAwbpHSs-d^pW5zyF? z51Fh0f-}ZK_xRg3xN-F(rJq>APedM`M|7AO{{Z{AW6rxRQ+aRLTJGQRBzcR0@O`-V z)66ZFhl~;b0D!bB{_$t=!MMB%ZPVgJw8LtpY2Am$H7k&=+efL&xQ;QPf!OVxUd%%w z@Zv78f=J4~%mi4XWaE_SxFb=ZDOmglGTZm~&6jR}kgh(cfBd0Y4?+8Ss7_y>18fV> zU2h)}EAF(Zr#fm0J-*CdLF!gCG8tZ#xPWOwMFx11P^gi;LG8BAl*trVi77Q8*AK;( zqb2Z;8fwd}acBSntkgiy3J7YD2v>NeFjF1kKdg+E?WK|_N0&qX$i{Cc<7Sf!;2E-6 zQkz?OGO3@x}%p<@^5th!nNlTKjM~ zESTZ6EwkcMO)2){(Pi);>zqVhMrT4c{YGJgc zjwF}F$tgrM_u_npY>F9Gx*xQGiLmDRBv#Hoq{O~d@yD?sd?B=D-{Ba8Z1|X7f5JqJ zMMjr^T8Y$XC@G6Ff;>kwuo~MIkkX3MhgKY@r-=2$HfH*zhD29HLt;|A=Cq3EpWB0_ z8kA6V98#DIWjcik%wfrkI5s5wnme$gI$@y=3S^B0sa2*ScXcI&wAi_~A_YnaDa@et z_F|K9k*3=0R#YT}TG*mnanRUU__dnno_NaVTz&?-2FIv(TCDLH^3>G#B8P$;kwt?G`PTsgK&P(9P=VUO6I4T(^ zhE_w3zx}LB9Dr%s4z~XQOz?g#TLzt=$|Qel78deIWn?yo{kS4GeW3ER9&SfK2(NAp zwUn4F?22r=kj|L4wR)g8Er-zIJ-On-6KMiU0mj@krG5A=)xdegG?mla-GZZXqz)mW z+wQ~28MyYW|hI)V1F#CK-nKYMZ1vA2pQ zu61fFTvg9GhQ}06dSR)d%L81>Y%<P2Ku-fJma`_Z$(vjJKKT?Q9 zHo1hqd~B0O$Kipel7fFrbmD9Kx>fNtPU=$(S!2IWYsA%4Q0L^!zyAP69PTHms~Mn^ zzC)GN-AV1hl~oG2&b&mYBg8tYgYU%rs5%#}1fGctu7igvBo%KW=t*$koV=r!Imraa zBJY@7!iSzm?*9On2%2QeCg2CjME*mL<{j3VugSs{r*0W5j)Rrin|zc3)c*h+5etJl zw>*!&R^D;|97ok1)VOK#cJb}^VlSCHsc;MS>uJeIB=#Iw{LsBifCkMG>S<_=I1yi7 zW1aeO3?|1NN8q6Er~H_Dyv9o|fTF_&hf?z$29byS!3)cmk+1A@7lToMSTpKUNyb1ElVK~rap ze<1hbDBqJE)kdGR#r(tr+mOLL?3b?mhLwHTKt}u?k zF1m^~jMAEl(>!Nk3Q4m?{icXqZprR9{<3mEekCFVZe&*9ja!;XD@Yp9I@j^SJ_Oc& zBzuGUSfS7U0X`qrrq$-I?xd}uM38Q8emMCjy{cCdg}Ff3aSfFtQ9YV~t|!FS>R!?e zmrHH?56|P5Z#$cM^2a7`J?ka8JF|4(E^~Jrq|i3xmnE$vqefalND1}4=ROO1%aNgZW}9M5xCIf+1I{J;^_*R zur~$cYzLB|EvRdCG2cptljgIfO{$>vBEH;GWbIZi$vH(fi)%ZT`%&eKX)?=k-z`qM z$ct^XThl!$x{h6i?=1l>} z=lpn$xfy~57E(1Ly|}IB)uMY}2w@PFP&{7#zsCwlIrhv&CiiCTNRZiDN%r7;pM)nH z;v}1E0PZ%{pqCT#1cWFG5FGa43Q)ElFe7QdNJ3dlKsNc(pJ-JvU6k}Eu^O<9BrP$P zK&H73*YBYEVWtUJ7PW*c@ykMRKDYw_hI(9 z&`X30?0NEU>XUDc=Q7KhgoSun1t+Gi-?I+RxfN=Rj?5aJNK@4^re2k|XIvySUS)@w zYCB0OPKu--8mpasn9olip4&^r&|&0q%{E)z@yk0Wl;QBpn=ajSvNf}84O{N;ZfvyL z)g>&nH8iGHptKN^;>JU1*zV#+eA>3nhF0?&na7vF^gZ;+M!!zCH#&!JvZz1RVWE!` z?zR5qo}ae5TaVs!4aUGA5npCAWs&!l{h!AZMpYX_K3V=>;fGM*1tX@B@4)9Ye3q1| z_G6AnO`Un$cCz5Q?rU|iY??gOnb8E7p+c!s zcUTGs?sp6NHedQ!<+)h@08$U8{kE1WjnQmS?-vM%_&J9DiIe`8N&1D4^#^0zEN-hq zci0as(@Ik|F+cV2zoq2(#f<$<&zN;=QiOPuN`(qgj8~-K=+I)4YePWLtuxODJxJBc zAj1sN&iqHxLc6CWzOzY5^!2gXU&6cnR6T+!zpoqKz+dhR&v44P(mcn{mjw}4hUFI= z0)Qp8WIg0#-!Y-V9ygUFHSV(G3v6+bB0RsGiACyHn2J*_++1AXR8*8^NOq3V^?{#7U{xv| zC~7lz+MGqg*Fw4~8w6%^uB`FZqY9>jFU?l8^`~xU22bGzwZyj@DizIpKGN%O)TZ2E zsV1a?3XL&`&$;=G+hC@mi`m9-u5kF$+a_ZHnFb`4`9$jR0HQPY&|?Re{%^e9Sc_H) zkjEqMxywh)* zg1B>N$>(kQT;qFG!5Z6RZd|6*=16ifIZ>aJqA2j*uYMUNCpzwkqCE)0PbG8#e& zQ%Ue8IeWT$u*Wt^p%g8_;{A~03|m}s8g3DpN}W<$@g#JqCZzQ1h31ndjUp#K#})-V z+(4+Nq-H`=0(w;ChVrq|zL21JZ)MAEX}G@#4GLzMH5O=+v+s0eHQ5l73-c}?CiD||V8xA{9Mop7W!vs8r&)wQ4HIHJC^Usv2IaQ>1h38c1=N?*Cil_Jcm>*=t zoPCAc-|%7XJS)0?=17e^<$u`I?l`i#W@73VdEI#yMv>ON4RI8>632-7xov3oZIQOh z+EP|!JXEIBs~|G2T`@wZ`K-6Qj=2}KCAGFh7Opom2+qETpu6U!)Kvi2Ejou>GX3_r z9cL5;t~|1v1b1GHy>7Rmbw&-B zyC%_MR@|pTSKfR*nGR=1 z$L|=gi5A%%l{EbUetrQ1w6&TiSo7KC^(#+;= z%XY`ah1VON%M2Me2V|{iNlQkWP>?79py)7sB~reDq?be&9a5^W24}4*GNu+vqK=kl z%nhY1y`Tfw2W}lhgKS)`0ciqS(@yFPGH|IpWl?erx$B!B%$)v`v{&_2N@^X{z*~eD zNtSzTzcCUX<#7K1oGvvrA>@*M&8HrJ4}jR&{{SjIQ2jXA)9Fu-m)h#?1pEGNL;5k{ z{lWhLE=KzFsT7Oc$U4Sdx!@~k7UR&RKT>BrpGJJXdsM_R_swq?eYC&cN8X6B-^Kp` z)&Bqk#@lHn_Url8_h*l!RF5soUbZ-&?fWqF19yO2`>?bJ;6#%Y6}zt#HKT3Rf3h%T z_`Z>yQmxvMHtklO!E*&a?=m0kj9zWi^dhS{{$m!PfAyIZcA3uo-dMz({s)8 zH>r&?%PN}x01)CwU2a$YamUoypW{b?<^KSchn9~BMSD8p$coUu^K`XC&+-fxZd#4M z@R3BsxR+hhfUL~{>=nU$D2hn&V|wg>OJpF0Y3qq@!kUjlJ4qo#(^5xa?>K}~Zs3-P zB!dN{6|E-uqsmh6Z&RKlt^`O z7DalW+Y)Mi{A2qmfB3WaFwO1%0JLYCSZGd<2(CTm>E-qrWw^|NWQKr}Tye!|$kLU> zmbeue?za*qsO*u~@Al$C5nZOoXHL_BxtK_<(@CemDmr1Ji!m7l(Mhd(cVHW&_V^OU z#bTEx^2AcK61L&FYmZ8~V^`RG0~=%dx|V_FwTcLAZQncyC9N@zI`> z_Y7FWhycch(Oi_Jv0ZBE?5-yel5A1SK6+H01b}I(wZ}nvi5m2agcDE)W-X({r<%|z zMMW{dpbJKdXIkKzj!cv`qXpfx;O>yW`k2e+{AQC2;2W<+CIiF|_nDkWCqjE@18cLU zblqJ(S@I9LHN!3i4KB1mnM z`|Z_zKSjFziO&@mbS{ulrW7nN%_*x^U+tU?IMklc62if4D2rxFMrn@PsrF+zpC3zR zn{IlZjUzYm-h?5fCrL)qZ%FCYym|IJ_5J01HQd_!a5ylw5LV`#$vK~HJrZ96EN^^b zCnqqVy90Ef5t2x!xBgr|7F9s^p+-lyDEh3{<4^a>!ym@U8qjC*TYup!NgfqxPxN5y zRma=|f8s$dO^5_LB94Eg(rMjPb;NsYsWuV1MzKtZep$2!2wIS^oT>rNowz-e!o9_{ zCu~{`b=B4CJu{{Tv;JU<{{Z4cdK-GAqb($nf|*rsRzIfO%yyf7#y>K2X4?kQWHuic?0e43nRvWd(=z1^*NYN-$52ue;z*(t0jIYf z5#8JFO{|GRzLVGcduy{ierkG=-#$Ha%-CBoL*X{4G#=>4AJLC6yLPfXKXkXtlQr#Q zr&$DHitQeNyr%wcV>5|e`VHg^e8~Q9#}WL&7ev%a%9YQ*5fM; z1s3EGMtHTclj;MVkx{89cvK;=_7EJFtjxU@L|E2+Tk5+OCt%ma0Xz$)R& zxB^U`0weMiGOk^ipvcRU;91(TgvTFMNY54bZ!qXuK}D~%M?3>LrNkmJ2(9#|s;i2d z`AG#v(_&-t^YE+e#&a`0hT6vxUYDO=vlMr8+AJ;Oi4ROCreNZ-_FYk9Zx}~;^{TZr z?!`6ylE&Sng!f)2F4NZ)6TMa!3t<-()jy60Y$_26EmTp}mv$K_)FvxHvfS!v*QN)N zJYXlS-~BiVN!L)gz$~{?382HYCyW?bPbD=qsl-=r#Vt6KM%!UWB#OLzLz}rD09C+_q>t4e`s1M_#z!t`@Dtp&w5rj-JGx;TL2%8I@CmNk z$gZb7)WaJHj%>VwN`Sy5d8gT!{;UY*6&$Jg0jdK-qr`pKM{%wL)?B=elL?Au_Fs-2 z8;z|-yqS3csfdl{rGh=<9acSExWX%b)`80>O6;8dlky~V| z2IRPbf0$#?khJ?Lh3Gav^tKW^ogqg+`*1{T)bEg0pjFM#l@sAK!4R+!9l9t^b8}G8 z1y?GLxJQW*^2SKX+~D;^eYM2v5xyrZRG0#{7l?R29=LbOlKi>^8;A~r3-UvLT?NkM zcBe?7#H$q{ULpy(xZO1$*MhnTPMY!~?oYqI?;LN`{$JUyK%D;o`OVB}yAr>xS^og+ zik{+%SQCE3#kCBw5NGw9@lNU%Za zenSdv){=B2X*8}w+lroAHeC21W?Yz{6jaNk9>AaJ!jCpeBpD3=%L`thNIFQRa4ROU z@gv&*0Mh$;?U??9_-UWq8V}o%bwuVGI=r`ka(QJcDHICNF^`x#yAE$URP#QE?Td;y z$h!7!NZXhD%XSE!dMCP9`fNtJqS4)Y3-!W7)*h^3&Ua7>wDU3VK$A zQxVC!p$cWVlUudV!C&z&=U@G5v8%_&{IaI|^-ymkn$!bW3rm6BtBbd8kUo$pes5W zS06%HQfTno(p3*DYC>qTrY$b5z(%hz#1!Ons1(ZtD98oCf624NY5Oee9?pq%x)lPO zVyN3BXsVR5s)_^OhdF2>g&8$k#_)10OMhzFZSE+4-J6uM0H?8>1nue*3xh+Gc_BPk zWrw)8-JO=KQzQt9RlN_+bq=Wo#FE=g>e50^bfCh@G$o9xH@L@?wg)pxpKIHZ9Bs(# zmnAbk445qzl%m<9rMA*l?gW})D?z<6{WpO&QraP_bSSkHs}GbBpJ77A7Bh={1g#lX zc|T*dvo|6wR@Xe}PRB#cx~B`JD=H~cNj~a~OLB{V=W9xht*C{6n5`j3q&y8hz?=np z;S9!lkLLW(-5XL-sCk7-%cqLHaa>s29`=BZ6j;MV&;)MFW>oGzIQWa64R|bo$ z8b(Q;nLd_WhqYPs^||fagL7N#Qmvbb@Ee;lG*v0LA++wKht`rn%vT>ecXr!9o004L zR&-@*KE!nX9P3@?D|ag;(mmqb@gbXpSDKK*%1sRx(v*`=elb$A;ONa2yQ!eZl}_Ko z5LzXuAV3r&v$F@G4GITQ_SXo|?q+<$L@1p#?oI|=M#<=TvGw(0gs)YcsUet%FUmI6 zKH&nTLIY^(eSl->9?RwV2-O}@+)ee$RPBi=bqGu(65u*~C?_Fa>}=~FBQ%@#jk|W& zcX40r);1Q1DY{LM5!pp4($XJ&Eh)oMN+cSZ;;VDY=~{B~3dDyzk}d}>dKlYWaKh(p zPGg0>A1*c(W-(%iloX(qDJ7SVLcQ4Y?0)CDa=wZhceC4r(p4>8)9H?O<(^I6(a13F zyMEbkYIVWX!Ao)rj%ocS!)s791d>m70A0yCy2TbT+aUZ!8NW?GlbJE3a;t7eLrktB z*9A@F@gNmwcz^{ki@NSF?L?1ghnpDt%g_z0V{9Gayf;qva_p-ngj=9PL(CU^^(CNu z)Voxgt1UPqAGzmwxbmb$ExHkWS=}x@+lbry2W43sD_eG(O};xzaHy=MxTr|fRZ>rR zIPx9F(|4rl;%%`q61I@(ae|TvO+f9%W48Q4qjshc-4t@6Rt{0-)O&gyg8I!Z3E-|e zX?gj#$IhPpFp0+h0I+}L8J^?+0E;8cjlrO(>!O7yC`WZM?zT9i#xoY9F7n__zeb-5 z7TUG@&;~7Sy0WJ#Wq#)xy>Hqlsc|v~+)^>6!A4g)`2aA2)A8#<66y&Z{vnG$0<@K6 zMN$K=IszLbRaQb%kgj#?!#QZEO5st3$QOHTtA(*~r6u6RHd}Q~f>IPKPzb}2>ZFDv zbJ-3Ameob{IaA+)AHqW(Cqy{%T9d|`XXT-$Pv|HWl>wMp8P^_dxZM2KdLG@aw9AXL z=UhL?7Ux`sAh{j`>O*BZfG7$gwdygCy)Vv7+H5TAV5}_ij_}aik63Mpd3_cmP*7#s zdwX$bJY^!LSNT~eZB6OA_br-GnjqVvDoar%1jPz%F`gf&;7?LgDssR#vD|(Z6KYNI zIsstj-Ux|nwp$_YXB5?`rr1lS*(i`&KtZJkZ7YF4O}_oYE3jNg16;$zDPPP&Ooj4W|Es~O=J>@t@YbkgIb?o_$h3^gxnx>t1lT?S0kdWf-(o!fp>YV-f zWWH`c+fnLx@5J6R_$|~r++JsnRQV-(*>SpR>rG?ZfbL$+kMTM}aJA6t55WUMblyK0QZ1}C|GQnR}XSi^5&NjC0)L-UBWiCg#CPaNJ zQ!aU^0V!XymBwQt#raLi#)hX3<*R8$N~b#)5?ppJqH|@ZfJ#a-Bvj?loFXmbMva*W z4W(Ljf5qpl>?3Z`ybt7}I}rC-3}J67v7CqJsEhmLl5r<{3bNU0b5fgfzA2lV6Xp4a~X zgCB9?-P*t1Hb!l};)vCidf@rStjr_;r<7HotHau9j$+5+p)1{Iu>+3wlaE~8wrh~` zK4GMlDM}!k06l{lSpNW-nmfLmNJ6vUt>sdbi`~jngE}2gmS}Y=u;DIUBC{6Iuk)}` z6%DkD#FBtCwuY5TlTN1r^#X{mnRj)|Y`3?OZ?&#FucgmDPNW?|l+5-V8G&g^rGDvt zR@x$aXCrS(O*;zRdX;5UsVjAuvKGo$K~wONf-K%c_9#-(39p+9Yo|PLFtJTqY~x41ynxj zLP9`D$SBjaj8AZaRqze6RjBI3WYLue9i8CZnCe%E1&vAqxmOjtll-PzZ7!EmB>l*u zca6hBLGu*WfBTYgg0t{5a+l_#)xXotppQwsT0(T|YGS_mj$^`i=>Gt^k+i<_UgGYh zS*I?&xE|94k|?*--Ckd#K2-5(^HXj>_o{PX3~4)Qf4z(Lk>kpdpSk}41ILR^>5$rV z)mpm!xcVO71I)7JSqtwrH3LJPbHqzl19N~;+Jw}Kgw&DtV$RK~mG|dJkW`xqoi=5} z-DWWSwW_IY#?SyspdSJ3_TxqJ@stuVQn@v7OmmNIZ#uaWB}$U}xvkZ@3W@fRk%=u@ z#G-SnLrf8NR8=Fl3bGGpmu^d0t+AHM=~RU*j0CT;UW%zI?XAZB-6&JcA%$rq zDK$CiuWmEj+}yFIy^Ps*wW?)B`%iEDR?$35#wWE~e$Z?0fYHb2p7U${#Fw?7Mq9b+ z9i=H9Q;LV_Vbx}(2|bvKYOHdTG7<-75;35Sha;dUq?|s)D=k5L!#b)9%%eqbyqVBf>Jj$k=gNB>MotBzDIWrAoF#De)a5r@JgqYE@$0jbcSp6RG#8 zXqunniwSW>3^=g{=8E}VB4|5(_QA|G}AiQ1IBdn!#HPol6XuhT%r1*cB{V{N-9P!oqUFUIew1~0y zJ2EY^Ahv}yZc7BA$tX%(8q+{fNIm$^c8-g~(vpl17@ZdmO__?#G05X zJurmmT#FplIeYL0(tBJ9b7MwN?veEh(F^_<&TqfsHQNXI3Zd$_`8(#wiqIG|C*0{c z?VJ2;zt8xJ+=avWS5#k6c}Z0fl{$Cg-i23CYTF9FaiotGRYebZ#}16DN!Iq%he#>Y zLHFZ{V=4N=mEsS~YD%E@c0Et;@ zU;Zlo-UkO)9HrOmnp4y7raccK{#q>Wd~8VM)+;W~p1LD82>Wp+FTg_;s~GnPC^jov zh(0%*Bz?Gz{7iEgyqLm%TEp!~%kIG#_W|!hqGu=~ku3=W;ccp){m!^ezvE=PlJpS~ zXcwdaJ`jrb{#;o!QHxh5^=&I(hLn)tuit@Iv#3;+EzoN~YO>j;V5nvGj2AD4rA?pm z5{~Ez5H0i+s3YtmxB|=6w9Af!XtsfKxD(eGFuS2kIO#wJ8Xr0ZG6I-w-UOB-Sr5A- zdTzGg1!|$&?8O#$14V?tFtL{A%~C=>++g#(P2M+wYk1Ys{zSSqQADNJLWh2&WsjbF z^?$ml?H!{kYN%Oay3kYNZ_dSHkq$YmsKH6=hCtBF=xi=z?I zpuu?!Hn&|^Lrnr)QS1XUa8@o#xur;YZcdAfBK@!G4~F-K<%_fS#{G$S+;ddwB3qgd z65^RjTY)Pm(sYEH=Yyes+LB4tnhn>m_iQ|Oe)XptYsRbjlY$ATuUuW)yQ(n~LAJ_= zU$++)ujmgng=SsS1y!GZ3b&BJE<&P<5>=}smM$^!oj|VGR2@*(98X-w+l@vat#K%F zBg(~Aej;&ojDT)%6$4cO*A}YXKsted-8zCs4C%CpoX?cV@2)x9Ib0KD9XV4K*(*@X zT8Ya|YNL>*DsIA&sAO1;Ez(H|Jv!q#n|Zzko;|S<(5ES1%Zjd_F=Ym#5`AG6C+@{= zRplL6RjAZMeqLssoiU%w+?1UWX=@xvef;t?2d*^K-gP!2(A9P;tse> zZ3LG0x2^_r3XPzvk`kJDv+cxRHRewXO^J?5PIMmQfhC-SVPT2ROEo$BuoujRLmVcz z{kRr#35}oyNd#dP^vP9_vZPm`_l!p7$>U@MzwN}GQblMiW_Nw~8>mdNfDFoYt#Im+ ztZ=&KcdP8eJM|`xFbbwqrg%apw#)bI^v=|r32~7gexRZ%KVUWG94V!-VB#L>5-CRu>wkPRu@&Q%i zLXCXG_u@Fol1FZW@IL+0NAsV*1;)jEPWcD{D(@n{-WO|iubPE;koOyGOeQOeu2>)EVl4rGZgBin`|wZBN6h12+gD!PM()1>Mvo+J z?W4qdChN$1_G`A?uVqsyohg{^%X?ghLoI}~z$#T#q0nPh+MThLyk#pEJ|@cD)NW{e&z7^CR>FJh{dj34Wl&;}=yDpDMt=$Cs$L3WT*}6H4{PUAt|CIiuvjcmqbfJ-BZz2oos&ZX+H>?u(jVO7ycVO7FZM7=1 zg)$A%P8TQ7ZEm88THIO*%PiHaJbKl+BgkBhzi}+-4qaig1pq#CV>ABNCFequ2GlYW za*2kZ3|M;=7gm~CrYVG_F)2kiU_%FL9r#$4OIIn|H1FbsZuBFNy)@bjhdLJYfP=``YMGCZ>ROAHM z;sb7rEiOkxjp+-Lf{{^LSKo}RM|HjtExbs3*-JkHWeG%e#dP^5*k4m6396cEH#*5m6r0EW_3 zqxTF;ZVpW^GEQ^NSE(*I<4;k1m3rikL)y*mYs}@TyL_n!rRau(0aMaTXSW!i*50Tt8o2h9z-W4 zwL#z^N^B~J-Z7TU+tt}{E^)J5G&{-kfaR^3ymnVF@%4u4mEHFB!WHUGgUWHAmydL5 zQG=*L(vSkz7}lMm9yi{*V#m2(GtzcU=W+<_UNH#Jnh-m%Sn8VS_TeZym@vYBQ!Ec2 zBsXQD=Ns255hv9jE2&5Ory-;({{UwJv;8>wFSUQ*MDpI@{{WHq6)Fr+?2BMfsV%8X zJ@i32<3!sj)`2~~(@D8FfWj13MHQrF;_4~ehUGs)_Yzr!WkOND*_(;D?J%J$PtQFR zJiNgn$3h6pzPQO{+)}Y8>@P5r>ct+FcFzEC<(APIs2dI&2$aKLGT2;_hczG*sVZ=T zQcq?)npdT(HoThXE*=)*Hko)kv90DZkbYq+Y^fy)6*bgOLlN7_brHnjN2qP{Y zmbk*k{9S^pl96|d9ru1;%8U|(IFvf&5`4sJ6d2#^c#T4`MHb6q)O2}+c*2-8mLV?m1t1*t`sH6@`8OIy+6nQW`eaVyf@MMxt$k9I8i*;$~8 zBH?nLvZ=D1l%;%SYbK!K@wZZ5nisFuWr-}c3OEg=rcD6}rBDxVzY5_}JDLfN*yW_v z#HBRoN|VifE&{L*7{{VZkqcd-{O|*1lW;7GU zFm;(;+VG3v{?eu;G;<^R#|=(#nXBj$yGX! z>lX2ew>EQ|lW|8DXo{R7HbGMLIHpR*O7~L$-F0D5yrUYi0bKL4*vAB!w9J;4Hb_PzA= zLX>+tXUIf2#pMTTmYs2;ni2fj3_JCRH^m+L4aML#?oG&nDfmf2eb!V$l$O_~gSVz9 z{Z`+YE8A}`1~+Fsv^G?>^d(snrkHomTb~LMe4W+iCb4;!8}yi-Uxvj&z$tlvprIsFTTQ(l{l=_ssT*#iQG9{s*R2eT85*!fJ00g4;n>( z=HilTLf;XtbJQ26)Svzbz8@+XixeOFnDM>K{IA|=V*O;qQJ-8wkV=uRgN~r?V?oql zc9`IBng#oh{ZohFFIf|3hK_k<-}vpz?WH8E_xpx{-$^US*1f;~016)C$UC$COCxkc zxSD=}>5!o(rYx>AERYi5P9z_eQ18g%HY(yx6B)b3!Vi z16orVOn3RxG`U~mK`U!J$RctVYnItcP-9XnO@gH=hJ@2RLvf__BDHPPLra_u{*-G( zY0R~&ujwQK-h5TVIpPqZI+2ezY@Op1ZtaW3?dIiMt`3yL3VADaDlO_GNF69T;%&D+ z*E2$Hn`OyGF}t?-8+#>|=QPAeS(eteKg0l_hZB)CPFSzMzgkmhFB`XRxF!kz00H-a zg(@4Y#CA#6vhjFQiOhUS`{{TUt<+lgCfB&_o3a#E+$)S3 zP?n^ifK;EDLUks&RW%r}x1VDnRuW8P_WRa8HA<5u#rNg200!vNSrs6RmF)InPI@z| z%);wzh))^~Ndv`N_KY>UvSCq}UTr^BBAt5XaWzQYv0fwwkV=&BD(wfRBkBv&L$=P$ zBI^p3Ik;`Kv7IV(jPn3?^u<*G5?&~OXdd@6>8&cRKNT37CPBK=$7H%W{ zRC>wu0|^a!N#W`*UzrX<)O(7iJC6$9WB%e^^HHBVNqd*^E+Si^>Gxn8YM^qShdutP zvP5r2o7@vmnYdY6pzYN?CZC2iU9i92M1tSLFLOMLw9>wHDI=n?`!VmZ^E~qJ#4ozh zs!2ZlDvB$v2{RTOX=Pm!m74pLgRz#SZgcdJyhJ5zpwgt~N4(b>J4R0L#3*?+(@v!3 zd&UT$!Ft1Lmp*$CV8U|aGFrkSL1nc%^Uf|Ykl$lk(&bTYydB#E3=~>LKwK)1JToK~$<6*Zx54#Nq zTt^c1e1v^DD@eobgf*cW%R-c=R8yX#_#9YA;YA$dNwEs%g@iX$g!f@Ca5zQ?Qiv3$ z8El?1sW|XUC;atp)GI{0_#+j2pTb@3d`r*sXlYAjHP>F4&s%;0S6fVnxv|8^+AN?q z(nCbK?WxNbc#+hpy4xy>7#Yx4zZX&ip=!9v1v^^ap2A!p{{Xduyb5gZh?JO4qBEv= z?rkUr<3&+jmXA<-a=^1ec11Ystf*(7Zdi&+s32Z~#T1=Jr?UayR3x{=mbOb;Zp@ng zMgIT{X7_LL8x4p2g+cXo{{Yv;lm7sqmHztA!<^M z#k2vaY7`%C8cArFV7H{yq>g}B9ga%Hq@dA9--{?+ZP1QTR1uvSukHRgC#F?gjTjqa zt`S=!j}lke;Xmcq^<`Jp{{T}NyzlZF>>u&eK$PnKRrbm>_{|S~Ah<{;?bW(2PYpSI zyWQP1;t9s2_GLpzZY?ca))hquD4d6B0E{?zW%%K{of0S#=ujVMJ-A757S(!$sLLf& zZU?WL(huB|iYaj~k^+7`EcWm-2O;jQFeT0*UIg3aADr9B_-PrSpSpt?{J(}qgJ}5A zNBfDTGw7x}2Q?#WMX%b*PBA#268DuBm;V5Xzqf(HhZ^v{e)W5GV|$bEBIfVJB63V} zE*9jTzbb+cPa7^UZ z^}@6eDOQ>L$|;EU{{W4WZeIZrynvVAHSu_B?+K=?9nD9Xs#twC*&1k0Dp6QMLw*IM)y!ri$@)RzYuv!L-rB*eKv(ydxtY?nG@a>jEi zT*iXMu302l+j>AK&ZGSXGFdH9+TkL|>=cA-Yq1KYWG5g=3 zhrLvBHL;F4;k(qUjkk76VjLI~+_${DYx3O$A)zI$NlKMU4OGwGF_p>fX~xe{z6Y3h zPiSUF^zs!P9=vrf(&Dc=Cn4F4eN=du2;V_E?tQh>+YFiIY9It3k5!>W)Pa`!e zbSizr=HI$ZY)eyrFiTYmBf=3tDU7E1k9pqoqs;r?wQ?5ZBP#2|IL(l5VYVPg9s&E4VHE zd&!$ib={-5s8(lPa^T!#q^yS+ak3H=K+LPO^uf1sdUNJ$f%Q*+Z8F_9nOL&@PlZm1 zTs>v%)|oC_Hmvzh)4-Qj!$VhQswqa87Uy>*A52|OpYXry*5Nn#mtz&|O^nMIto_S& z-L_jgwnP@J*Ewv*l=zbIeWsmWlC;$T5<9R8@0_)AHH=gn*Ii59=l;E6QqKJ!2CsO;q`2R@&plP+*ZyK2;~~@P_Le;dD3U5t}lzfZQD(a-9F=WjcsDzDfeTr*pSjrP`^Cq>e$smMgX-ItdE=a}8y|V9(f(RRf@J8>;6d^pM7YuzN>p4f zER>YEMw(;GKD2!?+xDyo&l)e9zVkub-N!rfeA*pGRk_fWE6OD)>S>qZkCeK1lGZJK z#>r+zw5V9Ll;x-JrY42bbvD2Zbd?dNzRYxPHcQoXHT}FA_9L)7nnTEid#-cli*Tf1dJ z2e~iXvZE5n@#n8!RZ{o1-h*e{_O|X9HbQ!sSN;{n$y{g+~FHpys9mv%lOVN$2YLx z4gT+bnR>n^^Lm$boAdKT`HsnbYLM`f;#TUlD>AhLqwmMjm>9MlwMeZ|;(NAr&a2h& zAKYw&GMl2%fK=!}Qi?(C?ZCT@m~f#s1y1}ySxvJw+_HSAj4J1B+dB=$+-7Xi7VWwztWh;8L|}3~Ddv6HwZ^waP%Dp8 zuCT%oxJ%ZjAN~G9q&~G3EyXFtRZ1tVaU`8c;~E3SSD5TPtNtQ(T|b=v08Ry<@+##( znGbEm4w_u2*tFpMWmtpVj;N4e>TVQ1-7TPdydwbJex_Q&*oD3GHo$72SqJ`^#1^8O zVUp1u=K3XCp$kttUQ-zNhjUOThF#-Hk1yx?~cf z1~GAs5w1@|Vvq>S{{T)LhN&B=I;rf(4Nic}whG3au(3pUXQAfb){4^q0I8m2c#4Lm zCA5$AAu=)aUu-I1!5&%MHTjaUTM_#9?EV&BC+u$)#bBFaN>zC+p>-p+l{>b*lTwQ@RyL5KsrE62`j1mfD*)HC+IRne7@uaI=4pcc~J7GHN zPdOZ1`qV2|9PrF@l9zP3kbTh8k52k^{{SKTjQ0Eg02%wwHS-3T@&ziY8hwJ8_dIjS zGs@M!eefmdxSjZW9V#%?cC0=zJ^DHQlhK_|Z#B@FnD8>%S*MMi+H_+h~7 zRyEpAX%*|+i7k^79^*wPr7HxKvc^^KX(t_QWTR0Tw4@y&y+KsWpZRe-IFe2jMTX)= zn>N|)sf9eBH6_gp4Uj2Lrx@*o*1|) zTaSb-VdXa*pf`~Kb*>;&Xnb*$)fodLinil#(nj+yfss>jE1yoPRwh|3A{Dk-@f|k) zI@rI<>b2_Nc= zR%K$lk5p>5ozB)W^pa9Jp4sK1Ig@$w8>JWXjn_~Vs3Mde%vQaxUIvr@0B&9)!(7K? zL}JLFdRZq@SMx2aI@dA|c#3;3Gtm-T?;8nGs)b;3}*NG?Vm=k&=HMs+SUTwDf zOt14buI{HEQWBVPz;ZIQ>eT4ap17A1-NxFN0v*v>>8U{zzXV?IyO&{Ju2;#Zn6ybP zM=iD+{Uo3PP&(qfI^QhlRN~3XH7o!~`B=x{suP#lfu*V>wAz|IM0#*alj-+|LH_{t z1P}gYj}Y8{%KhfOKU4d76BQ|hwu$O}iD+^5SVN2-?$_==z~Pu*7^{ilm(*KobDlY0 zOUi7U_0F13pZ)7qarF;x{{X^=xbjZy{{WK6QC6BMYgNho$u#+e^!qV#<3ldcRM}9X zPtC}xj#Yir_h*4y7CcJ>A#)({X3tD{FGQrrn^VoLVa0%gtkjcS@tBYNhG~ENj70ks zi=3wJn&!=X1R$k4+%%%beNH3CpugyPXVVjV=L}pmN3n z-p_JQN=ciAyW5OXTu_tik9I=d_C6K6Hc=+x!xO)9mq1n&wwqZLzL{Q;q+2m3=2p>F&rHiplDQ?oqr z!-Xj0N=Z_v2V8RziqM;B$t;aE8q!n}o3eL85ZG3u`3T1mz} z&%({Lr1_56dK%^aX^h7cYzu!Pe6GQXEtu-s&RSqUsw>Zz7TA)YmqkjBb;}-m+?bYQ z7s|C}&koYRW13u2JIJR008mpdxbyA~g;Zyu%+ITn{zs%ajOx?#+lI=~_R-ZjPx?61 z_HMbU4zVvp{l}E%p{wQeFDik}5Y6B8D2x+m{0R9-WhK$e2)so*7ac=159nQwu+U%hVQ3h?%;!_$ceX4Oo zx49!Tfj2Go7_hGvT3HDx_lO-RS65T*qw^j^viin+@H#&WGGo zXMC2@pj4x$pdI*@cT-Fkm8l}8H41VHLgF}sl~Phc?8~MHx$u%DQo(F?ltV4)Aw?>v z{{Uwht@r*sy~f+ME&l+yUi03U7cf8JFBqdbqiv-$G(p4)1CvtyXX z(ydA~Na{{QyA+Yal=vdaU#byqTbpQEkf(23rrByjekDkq6_6>OeqzQ8wXolx2(g^= z@Jx*^KBWCV*;c0J^2MEQwed2nBNK!-NL%p6ma9}S?UrOs0-fGzF2mI&8uSDux3C( zocv|Ul($_E2RyjNVE_S26&!mPVfOs+z9To>H&wiTooecFO{6U(*EIsFj-5_DaPtT8 zFzUVp{kR}eXgR7QE!5-&d15+Rq)&E>+e%X9xSWzJ#B+oD1`Ds8`5Nl|#ZVg*MZTsV+3=3TecUq^T#N1Y^Ep zB(cVlV}c_<=?6MfJT=X-SXrSZ?35H<8PD=B_+vM|d<@cTU*sw0tB?M@tp5Q02!HvZ z#2YNl9&u3IaF*0G;AyTTRt_s&4wTRbR@ix zrd+CZX;lUWlA;xaY-rbUkwH)J@8#|of_z6|@d~hlltiv)%pb&I;R2-NOPrqX03UF| zOW|aeccS5;;UEHyd_Yh^-I95L@6#HWrfSM3;*q7h#xu%g9~ zq<&MY>^M6U;9#?LXh-BUYKdvqat$&-r}l9Lt~v{nL{B!aIm1q*Q{9U+nkiSrE!^1u z0OC^DUZM5>01QK7UZTS$HHh4RkY==yC`nF#ZVI{ZvcA{EN?W;=2)5BmjV7M@Vy@4| zi%gG%k64MR*A=ItPF>W+jn)Hh*l%vLTipVDMYZMbz`2uLmGdRrt_ zAM~+NEn-{TUbIgZ>XOl=D@q(<@|6vS6KNI~Xh=g_KIFod0-b-tl4)Fl%N{q}n3K7d zq_`v1dv7K&u}>50tI4r$jhDER$#~cSre#J@)>IjJ8a;1xNVdBEcvF2uguZK`|E9RC3HSC;-8k$-pfE$+eH zR$fcmHl69W(EApgeA9-T5zy38``?GW6L|^J- za_&8a#P{97wdb2<-aCK7GN8==FQ zlV;6!Q}Sgk)yQ$hJZnHyvQ(o_X*l$YHYkzw+@_(RG!`@jttUpH2ib?+N$3kJ)nk!{ zf>MxkuVw+Av=FYFDGF1q9-}ZT@5FB0N$g3ANpgiL2R_q^o6Azl)zJGV^#LcK&-i0C zxWm$2;z(U>RR>Y`9Q(0=zr??>CsBWChZDQ@;YNSs8Bskx6$6Y{a^_0is>h0C9m2oZ z&)jr_adMvFwQZSlZ8DIF(VHc`NY|!=?#4rY`nR8B3G`%?(ejSX*qC?oV;iaRx8_xA zcRzDFuW@B7dAp=Kg1BBw1%)SAs!DWLgk{#a^9w&%_xN8|I`Vq((w^=Ix$a*``*-Cf z7}-hFja`3v*UhmpS12&1D1@p*ZLL6bs*(w(-;dMo$;`QxiEiYcttNhY@0iNP*+a>) zO}DfToW(Kgn{2JY>d}eInDw^BGTcKfohL17?l|=h+}j>))d=u1`M3IUZITNXI7vuK znez!5sN>!?-qFQ`XyRn9ZkHR;fXmwvTxm*Vc`E}d3IEMVwo_NdUW@=hm#t@&9Nj@&Pr@NC~OIub%$0U1k zQF8hYg^@AYIg?xqEUg1=*%MxqUbq5xDjROdoaCI1P6Tsdkg^kUPxi4D%b_r`5L}~} z)t6=?bI>j}0_uLbl4yG1HpydX1g%QxhTO=ZCn`po2X-Ni>i9AIzz(FOP}l7g!(`;R z)e^#03M;2tVsdl^hLQ;Pd!4SqYq;Mpv)i@VZ4E9((&XtOrWkBhOHmax0|#x}ecY?c z-L+Q99Q?SD(Dga#wQQ~Aa~-AhBDG6mQ;NuR0Sz2)Ed3qcW&NP*GJom)!va^W-%W88 zv<=5`oKT{WShUI^RQ$tOPFTN>&~4&Mu84fE-1A?F4sSoGC9d6baz7vO!nYNip#_`G z&TEhyT8OH@!?eq!Bz6+2p6q%S#s2_ZEjx9{xcQ%#{zzq!=KlZzSA)G>yC%oF z?JGT-Z{1?gdC3u#EppQ2RzrvhQ>xNI&s9b$d;b9F{sz^zyVm5~kZq>9Yf||-k}H>H zK6vI#E)oiPYQh9_qwg(PTAj{}=}_#!5S>KkK+LwKsVV+$zRVek7j|kZizB%-ky!42 zwmmG_WXyvjltj6Z9(9RPn+q*g(4y!V?~lV8pQ!p@&tj~f#r$D@v$H>0Ao-n^)H@$f+-BI^8D?V(49uMmxKCJ?aCAW69q)*Wg^I zNp9h1dha>tl2RZ@(uWy!#UVnpBBQQ6XX`g?;9A-zC|in>@kt|N*}K*I*PLebq2I?@ zQk)=-H9b4A`PFh>v)S$3Nf0Nhjn%n0*VEr5?@gz3U6*m$<3|0q?1VPrm|N2tXHX7> zB;=wq81;{%yCsbM?iO!Hnhc@e_fD2Pro0iu2YqDvpg~Y?m~I-DqHE*;5<%_0LLGni z9DVT*qZ9Siigy{2;C8p+(;uj=Av#@%gPr9?%M*_L>N<=R3N+r=^& z)I#cWtun{YUr;`nM;kS)dk>S?dUnU&YE~1QPDNeko^<9OHsssh(AgfPCUPFujN1=d zw=`EFEerFT)TY$9?dd8($kQID**{8l%#0Y${iV<2QzhJ;^^

    Ejl6^f7K7DrM8uh zWVtu!^Ao8B22mz*xkM5Qscibg{O4J*v1Qf%lAHf9tLV>wDaJ(^@j$>IO`IPnM^ctMfi|2mHL2Xn!1j zjpLsv<`GPYpg2tlAdqT*uLd_nS4q?)lu)cwqN)xYhKA`zo>|DMnuR+ngg8$L(w9n& zYJ4XOabitiF!F_&s=7nz}^V=OZ3&@v9Jm5E3V(K(Tq zUd&ZE7O35oGE{1mp2L~`oG{?}RD{rJ6zsx21Da*l+ULr!S;xSSA-t>X#T?hEmzJcQ z5aUdT5};XeO*o%{UQ;@fqN*t>IX%-vRHP^YEI2vP%0P$9NZ3YDEP zOP!I@d@2`W!nnAWF59=bhBV0L{{YL8q`}moAX{TdQ`8B_0{S%d_F|Uq>yuAR3wv9A zkM5AUe6sp}jkm9tTMsBtx?U~N-hR=x*9;-)@(|v%Fok(w1uW(LIFk=EGE-d9q8*Wm zjmYTrDhm07Z^V@vCEsq}tu9QUTh3Z!yu&J`Xt!er5gc!-sf+Lf##wG}>D?-*uJ!aEm9 zP-(~lwV$LWu}^u4c3Z`^uT1Nh_4qafzWJ?rOi zJ#6BAyJKl#Cv@JKL+S1Ep|q(vf3y>gS8H2xy;^Vq%bg<5L|(3zs|Bt5#qMi%yKFR- zNtWrNQ|$JW*al}Fr;Bb<;ASt}iaF!Uyxn=^DG%-4z2;cZ& z`+KpZ#KPr`S)a(utD+#=OK4#2LX=S^aVP`~hztIl6Dd%L8K(lDVE+Iu&G%2Y#;ias z3`TQI%X&wM6-{t^wBw~t1eW*_k05JYjk(xJa97^6;%hRY1=!J&A`|7c9UYnwEA|Xm zTyFp7Na1 zl9~3qJ50X(WT;dr!1Ilcot3T*#=vvnM8dF%BVeviF*gOvAlIPNh5{{THtMPcOW zeWVs!KudrU5}{1M$GmKI`gqZa=R4)qJc^K=y`mq4iq@>XU-_7~Dnz}og5~vV?MVRB zrb+|10cZaJjgH6v03jr9lB92obB3qNF$#xoh02Uj*t68cXs~zrG7~1gTi;AvZxAjs z8j`kMX)U!C(rOE60FI;qmOEnGqiQtJCvx2GTz*Kd(XNZUO@CUnMg*y*iK0clO1@Lcv;f6 z)O-%yaF-}S<}Hm2b+H`~ZcVtj*Xv4Es7I9FPzldUg%q0h9C;q&fb89An(nTe9H9Et zlC7n|?03t&EdKzr9i;yNPCWzZ%KY~Ig%17y03G|!HgnajsX0*TG{?CvCzfXLE0-sy zLyV(~YM`GFOkAR-d`2DLN?;0nMQ8CCRH{Os0R@Fs#*jNm zsrQ^qdK3wSsz{r$gZtCI#+U$q?kU4qB_J#>4kt`~$*Z7kvYO?cPBOdX zjc&;roiW=bDB>uo)RMK@U-N=bK(^F6kA6JX{{ZV4uCQLb0s{U3_E;k7g(71bohccDDjhqOC@x{{S{v z1)&w@7HukFDpz}i55o^jluo-eMap7C(7+j3s!Y&f?=y^OU;hA3(^WfQ-yaPnhTN3k zOKBb)nn}iQakoj((&6Pd8kSKNTxeAxKc=7IiYxJath~gMb4RF;PGFx;+&)OB{s97? z{EHqrxLf?k?=+S5zqf%i3`1;j@dAe|NOE90MRTdgNhFDCZ$zaku6`AN40bFzW-_g_ z!%wIrwC6Z-vx+o7<_oC*0PnTq>HUZP96iUAcR&1=MJ*yYH48}u{5^vfF5#Q-4*5-< z&^@#mid+Xri7m>M^ITuI(HdGI2It7u^1vXC1k*nJW}*IeY9Om+4eL$Ha-rTrW`}7U zD@s5}_{A$gFqh*|mL*0fZh5{{menp$MyW-4sN@cs14`mF8#SrFFS>;BcEfc!l{lak zTGovo+*M(JnnjM|{uqzTTWOJGvAncuSIM+1(>*lCcJpfNz2(#uV1f>^LchBQKH-)M zw{@iN10{{Si_ZN$iP zdPoQ!9TCs}04h#0SR5Is$aI=I@#v>Z81~8r0wENFM*x&nS?mZ7p zdK?cFxlP4MCrX@JQC#~q9P#Jejklqnv&}vVryKsR{F}BeoH?|3#@PGs-)?uv*F`#( zmGXup6gb*|ci}43;(R#Oc2+C-zHcg$<=MNBWx0QK4?iqz4QoQzQRW=Ts2KLxvl)`= z$#-pWHq5(^{6>6)tvX7XGb2Z+JxC5GQTL2bZxZ~~V;Z80_Fb4tl9d@DM&M2ucl-Ui zZqXlVk4FFND$#d|7ChaD%C z7wW1VD|no`QJnkb-&6eAz}(Ns8xM}3vu*vYx1WvsJ-M4RDpPGg373ddn!=UhS zC_6EQo9Ri#cpE5Ubz-FaNb2eI;N&<<)neu75)wWX%e6>okUP}F>K}#~^Hme{xp|Ds zkD+f+(5kqX9lv-89X|`lGlTNi^#eWP^cLn&$!=b3{412K3 zO{3hW61LO4Q^red)weeNpr%Du#;!kkMgF6EicDRtu(n(ouR~4h zWAknaj`NHyZA5G#0Y#N0>D0c5BTQ*~KEyfoRV*+n?YVu&As1zcwFexG9(J`Wa;_@1 z?^!1s^=>PIpkv9PsMJ)pD3-!fI%G2Yuu5qTciD*uQW;4}>T^H(an^2X2a|WwRc}$` zCu~MpG(K?^_7orL#*=Pem|*vp=sDV@k2<8}qG|WzQe(l^Mv^;7IO>Wmx14sxYvC2k zu2`1fgHiC^T(9#h_~6KJD>6PIt#<_ZKpul3*+6mET_`%-YANlz6Bj2Oa&^uI zOOEeyOY3l3++Ub{0dJG+!M3rL0j}Pl3imXEfE8&u42gI8@h{5}b=VYL*zl4ZU9qwf zf4eRm>FyYc_9Q3~Ych2vs}3mHbDw+)U%s8iPH z?l4KJmI{Vl{c#n%$~{P~?T?q?B8!ck*ymKhue4tNyi=!si2B$s1;Q~_?kex_Ye-Qo{BHg2ykhEzk{yo^Y zNT$2QA=-Oz>XF;xOFKQFt+^xt{`k}WZZdm{{Q8>BulyohmdaajQ=-maZUtd6Ttb!> z(wm#-p%LE$?HG!DLNQ^WoZ2cPiqftFi3Fyi)ad0`7b};;QP#Ak8$A$DOHx&|Az&9N znwV@8_8eyB{bq{7&`^@Xzh<{4nd+!d`&h2O#FZ^cYC|XYO}kYJ7|i9{oiR(f{{V_c zk7i$*k4KxfqtWUaAO8TZMo9Mt5HXj-{{S3)=6cM3{9Z>59gR;jM@r}a06QBC#m(=G zLgglZ@fe`~Sk3fWcNSb0!UP z-l9(aMy73vr3jA7tNzX*#rVX^;6+Mit68UYF=SRQBIV9E{{RvY3Wz7>9Ht}3Rk%-* zzb>Or+`^2;PqiQq_N#+3{tz5%exg_1jOvMP?AC%$zL=-6@fLXxL5C7J^B?W-8V|Du zUY$j>7~1R=UcgUJDMa?u5N6NNkh^q0HIBFA5Zh`wl&>@HRw=o;RST(G)e{snt4k)8 z@TRBi#!ElrW}hG7M8UUGp=Oo>vP-EtdyPG~^X=~6tk~?Ac~*mmbSY0qgtZS6RDt$% z#eNK_Q%5`08afJIBnnXH_u|qrq(bqp)a-|#E8)Fyb^icwEn3{$S2dK1XXVU5bW`mI z9xvQh>5(Jn&#G&uxGDbtsSkfz%qA}dUPUNfmkB@p@RNaidKI}qpQIPL_h0`2RsQsF z%}pq!#R1{ee;j%gV9%xUo}%o?ah*BDo5nM8(!xN_42;Cu}ASg2>X6fI6sG zw*bjdKG-F6QC(?)Z$_nsmQv&cl{&J=(Y>Sf7dG^@o0IpR zH@o)5n}6C{o+G;^4mw#R5EuvF;^MK9U&xF%RRLi@-L|PSiJ{1@mviq$#QDqNQ0+1ptXeb z@5jnH*^i!x^(U2c6Rl~XK>4!MuQsC8Z%`&38IZVF^Jr==Gmdeu@SQ>Q=Tf4 z4l((eQmJ`j!W+_Ltk$aOiYwRKE$moA8}e1(?!_im^3v6mCCHSl$Ms`9k(BBkX2KF4 zc+=voaaVf#NU^tUNpMg1aaP|<@#I!66fu|)$dTQL zPCwWRpuHhfvrM8-Y0*G2zWQvx{8rz+4ukc5yp9rDEElPz#W8JKkPRpj2T_l` ztlXqgS9bRl7N4|k=2%g+tt1*Ol!5-4V5mvS74H27KiMQSgr&xG6oF7_*@7)a&{vj; z^U|#NHZFSD({B?Rw!lIzO+7j164NO2;VmOlm5`|et~~eZzR`OxYLsm%$RiJMj;QR1 z@4IqmjuzpAFc1jx<;PuqKM!z9{6dG0~unDz5_?U|i5*Q(_dQk`q#%NEP9H;HPkOYkCjG~@gSJ+fWC z_ic%HvrlI0Yn-1l4az%-d54=Onvk>-2+OF&9##d*5^($pa%W+@RkJZlKiiRb+f2Droz;|2C z-jJ01YQ!V~q*A$iad?@Xmk@#)pwd%X^rkEo%7IE$Vv<#IB;@!uzc>`@CIxAFdmtv0D3RjlRodcjIm37tQ&KLc8?bXIF@&1N_KTtB>lteSi zp07FUmp0YcW$yjY8Cy>F8coK>65Sp;T}v*d`e3(iFNz`gsh~C+LJ&f7r(nw* ztwYF9nV1^ePI>sg+%F_Zy0R6m_p7yqFz!}6q^l*-l{l7eaO6D}LIRRfpqCg@6ag6; z9Plnq4V&@hxfLbt568~CVe>)JhHreA$h(HeBWqsw-RAMO+AvP-#3l@V^RuQ9ch zNLs~GbFY2}!|j|Ldz|**qc1c!Gq^IZ!{4)nrBmx#5WZQ;7Y{`QEU|H@Qmx%714gCTP^^Bg||&bC%Z6l5b6imrl#mq z*<3bU4v!M#SPqL-tsRMFy#D~%#cKGC9j}EB95cj-*(nPA+4o{ca;Rr0tjR)IRTZ5w z{){w&pi%T7kHZd!5hPfSEr!&%a}wmHp)jQnHdBE_Bmr8{IpZ_B$#o4r(-z`C9f@mU zz+1ktEfvV2E&_eNATeE7mWff6Q`WQWqx^##%}8m*O;VOkG=P*QxqI=x?RX~!(TeW7 zkDe%?O0(GBb;sYBkXPE39{&J#H1ds1oTXFdEIWL^6=v$Xq2|gFpiq#pLEnfNMmi-P zRrwX(tvcIFo?6(?PJMA5l~FH;-9lHt_A9ORj{3+Q;IgFC?5;X?^(>snm^8FvwWc$y+RvH+ z`Ib~01Fcf%Cjwmww{V*H<2$o!>sAy$fWTS0M6kg4j>CN}qZhqVTb`y-Y}aL1xRf@Z z>Bp0Io6#PNw*LTx^qLw{obIW|n*_>fBb47$t?Odh&hb;5j^*-JcoA;%WU>e@Jn?cn z47SoL3f8X>6zSI zaF4Kb81<}NB?M#d$4kEi3<2h&)tk|nCn8?<@zlSb${@AOS0}e{igo_u_4d5+UHA*!>}g4 z+>+B07Sc-7A+;k>N@_qft~M6?RnFR!Sy6X!g1JhPeXG~E2}ofsT_D~pYgS51;igJb zYeQ2RYc%^X9nRdzOGQyuR2%q)7U{C=P3124zmdP>+W!D|Q6X-NeMT2^IzsMrD<-n zW93RR_wX391wua9Z2tIH)RJ_M_QnR>{{Y4=H~9y?@LH9LR4~V=KS#HJc63QPTaBW00;UR5s>Aep0QgMH!Nk)fAE{R;mQnzh*qv>!tJ2Eh^h9 z;<&C_TtjMZJ5B_IlpBOq3e&tAV#0YPJ=||qM&|nAcih>NRP$+OHJ#*7n5gnteR@!W z*;Nv%(uWXra6RYI;ewYK`|rew)gdo`ec5rY*-m8jGzt+QrI=oKIoqoE< zlUyuRkTkJvu}!W!VYDK*s#o)=0)szc!nsq4C5(E#4u$Y`*ORpN3au9x66~Vuk2<8J zripbSNdz9+V;#7<+-AE4B@|CI)YL4zS`$xhLlsy424C&kO!Xh>(G=hgAQY(mg5RJ1 zA7u%L|NM>yh`%Ysqa$oLOywd*w{iF9BJ5#Njm9PLR zr)E;V$*zHqT3_K_6? zrN!)yC0o=A3`qnIpWUYgc9;1iAKahdiB@gVP?pZ13CtCH@v@f_6A)=F58Gwb)RIC~ z>mphuJo1`^x13WAr3|YiAx4QAc4EIP3KsDz zO-{j`Z&Sd5KBlaP6~s*>P*UaL?C~v;iF-YI+avB@@uYL0+L~%BWPm6KUv@nv7b;on^|@wq(>G9#wpZE{?xQPjtr zopo(%P!vsb0~A}|%HFlh>8W|M%OzZ^tqULOM*AOm^H%WO8*n*xw$EzZ5iy2dbt@=_ zfUMAy(B+DKw&klf6kAFA%Pqp(?%b?XBZe)PMoyQVOf>!zFehT_QNVOFm2KXpo|n00va3#a`ni?KE0! zad5t>dM4tFn-<;+wFV`+8$AUTtiwyyKOBMo|iGM}WVKBFckM%IL; zlCT#iqR>!C^3P$$yKRT9nH|o5mUH9%_#MR87wE;yh@|F^EcbR}f)KpX{U-FLwLq`8 zEKU%HgrY-^{+&EVg1+1niNK@Eexplt7_%gQjf?XwG?Jo>$MC>gmCi`iy4dZ;a@7G} zV8)Ch$%zRKx}r%+iiimZsP^ZGFeTJlg>Kf>t^wV>8Oe6*yqBUjw_@o_Xilz*`|#V! zk^#j|Q56$tlD3rc?I;OJ86+P60KX3ALGiT30cUw?{{Xs*3S?`AsV5@{F4;A=9V|p> z7SuE=rAW^ZWIsvJN~Gho*>T1oyD14}#EKN;a~%#b+1DD2XQB4?G-Y}z`JL3@&qw<= zlQ^qoZkGHPTjWnRQ!X}qq2-+<>K*;q`RjP!^FC$9GNJS=@8DuVMt0Lw;wtXHMO=Ni z-HjG>1|-Rus0IoaDpS|94#w`R*l8&RVeFe`TzR1l8Cf^C1Z^VVEqBarIlB$n)yB?nmhTTt_uj6vbpx)X){9Ica zADSehe!`sp02~Rs&Gc$dvc)US6&ux)g*(o`SNUNqQ{L@xPQ$DicI)w#PG!Z_+QqcR zWVPmpQ7k5pB!NPmU&?!Ns3-y(d9?oZA1;bHeVF5kx-t{ybnG}lkyW;7bIHp+AaIVkWyQIT1$7(Uoa z%-8-Py(<3zbnQ>sh^NGo*A@|FI+G7q#1;KGm5#B*UYgNO!z0+A;ejL{ZBdg2p^a;C z8fZRU1dn*@jNV_yX)*W;(xQ=XfDU?4l__1`D&h;Wpg|9&e`-T21G zD+*ni2lB;5e$a51I0g+GC;Yv)swqXRSMS0D5sxUdm7O%H3C#P=aXudaE1Xb?znNE+ zZK2IEny1-JI?DAA%J@j(_QG3Fhc#(Gh759{Q8LrcZ$M{=u8C4yhaTf4%Y0MOA;v|1!Torp#E+=F zzdiyA5T7w%jO(5(6ev2?$`-%VUoY;SDZ5-?ZL~19_hYdenjcD*5#^Ntq<}>Y6-;^0 zdF_#kccY|$_a4o}-Xr<-^zfl*y-ji*`*iTU_?M8uTBk!&TxBI56uK3dB==(*+BfGr z&b|c~X!f2q-MG2htvPA0+;X_GLKWGWIQ0aNRJ%HhQCqBLj+}`BKKw;_RS<9&m}e%> zQU~LT>#3$qYDQi{e8m3%tA*}zxb0xVqAI?~csJBBh_%~~}WF04M4 zpc>bvJl8TBvsGaR6CovCY1xcs@rPJF3rsoHhEn>0$m79x{i2g>vv!cFZ;+(A6cY>utI(Bv}2uz7ooreV4>g z-82LpIe%6@sO=A|7qPC!<#`~y5#t-rYdNPDt5VNxo%L?L!$SP@w;?$?(IYYkQIF8x z!}`OwZkUmmI#cmShi`qWe;V%(NT+h$5aKqfI27R?6a!pecNf&GJZo)6NyrZiV!3hf z(21{mibUlSwVx~Uqr2|M%U#pm`5D(MDSCX3{@Jra*A62ZUBN(-4SVtDm-F0Rc$%Di zL^b8zQVB|v@5LTY__#D)`>mK)fSdp5uR`g ziuA=r`6`w+;3T}Ta?cgBdX^UA6jD@1Nv~W$^=CXo!S-_T}rj;p6hUj zvNrQj_f)ezv@K{3v+~ZUrCLa+s2O9_ew9ixy(ZFTqqnR@&QOI_jK1@*-@uhFDlmJBcHJbXHhI3-i-!+#Oh*!QJ7}4xgP(=HL-82UeKwPp z?Qh;X#md}YZv&3oS`&=83RZMb)g!WlA9QoJ!17?C^dsw>cU3N4w^p@lRY_XA{f8PX zHmGJYbd%IYOi@vh_h*ZkL&yeK8V^KIsvhP+MrRmaE(%lo`;>Jv6~5YiOM)&|boPK(J zL)Uh>@BSvT)%Q=gkyHMloT0z?)vh(SY>nwRW;cIk?7OnzXi1ahc_>0uqJM^ei7hWs z%iIRMR1Sb=ixkSqI!JWd+0`yM(^{=L)7yl|oo%vwekgKWm`I%dtns-EGJg zLlw0}Q54eZ{{X8J+W5h*Jq%f6s5<_ULK|2I+C~gu){ucAwS*C;I@1Qkti8lWloU__ zIpDIg<6%!HS~Yd2W(Xc=5CRk_+t=*Gl%g5{w$c@-3@{yWG>~g=X*8`JAmxZH@ejGc zL{fCqKDZzNLAbUS#DtUa00-ZL>`PF4@o8#CQ&<8+q;$_JV!)^oTtyPGT7lObaige0 zqccvbre6GU!57=`pDwv|2i=Z1RBGQjFUUZM;*=ASBTw--`tPWGehSnnQCV8#Fs6Lf zWl{mJPQq}-Ix%LbKCEe^RHT~Lz8|*^auv#?YrhZvB@_PuW`FA(Pd8DvP~Wm<`&2X_ zr&5|vZ^ zTAV%uC&05@P})~)eJ;_qYNG0m4nTWxl-_$6# zY3`?$gun3-+gg*_RpL2xzy*kDr)ysh1!pu3=6f)Mr8=3_@d|clj%2Zg09e-gU+yLhy_yKbHwOgd6qQ`mp+bp{f6`iT_^Jy~%gNV!PiBZq zHSh6%$${A|o+D3kv>mx^!PNO?gWkH}uK-BNJCyd4IZmS3KF{Hbsc|%Dk9W!ll^q=@ z$f5rLYYex*27FH9{Ta93+u0j4)HUaH+|g>YTLr2_sSC{MWiM1949PmE0LP7Y#tqx~ ziA5fbvRz9XR!V&3^$g@~$9kD=;>VS+_No%5qssEycI7EGDJxpm6dI`Ll#2UtceHjy zVM$MkSGaH5a>VD=!*tEL$#!ef-F9=zQs0oHLZz&gD4KO7n&W8(@rZ^I+WZ(UR0K$xZIg|VV0PWE%=FHI$J_rQj%mS0jUbY9$YSQi5?Xi_vXgI78rFhmG#jV^ zw?VIGhPaY%QKBB)K}GRBq$rAgxQlcD02u6V@(!KYs_y`4@R<0i#9x&C!?IuIedGTC zmC>!c8oCsT@pyt0i{S#>Tx8nYU)Egr&MhXpkb>46a2~8zot~DE;bHPkmE&>^ET}*pUYNURy^CgVfm=i;m`8ZD)-jWMKSm> z1Go38r`e&L@uK>B@xO5f^1s_R?L6aMl%U(XyFsNr*!Adno@IGaGPm*rhOy3^^(*kt z8cnu!GaI2_Z);nQtePhmNXyPu7?-d^5f%AXr3%+98wiPRw=hr(v&5bm8f!kee&c?%MYqu|1=uX}4V)`HM`7oh~%8Y5^Uo zIT3@fZYRVCCM_|NmtNFrvUAK}nx-<#bvHwf3hFgIpXe}}6Dc$s4mhwBRDS$J)DjO+ z<7!z-Nzd)XEtAwxjPt2bR-!xe{;W1!3Eb#8VM&m+)ViQaqDfIWQPFI(9393t4oScH zvdK=`u#`V{kf2IlT27=#N$*h^_Y7xuEy6L;aWz|31DfW13)ItMmjj?6l1frK0j8ZX zPc2=1%LQkZ&|7+qhy$rDqxcL}-?8as#uvtqQofv%tAKcaPly`@zJ!15vEv!hZu9q= zMJ-?3z?dYiG*A^XOL3BV6|E`$Bw|R~ZEzhMo<^}(0$D#VaAE3yE2vjral}g+UMEiT zUIQPLx6BSQ+Ecd+ts><)B`sR*fyAfIeP)8aPLb4*I$-P_vR5ZTvAW)_sT{=J+>yNb z9d|}|X5o73VeIx}C7>1U!b&Y8&4%k}H@o*pNRD4Wy;YPJq@5={6g9e`x%j^dH-YJBdf4ZBW`%YsmH~#5+%{Tu5+B1dg zXo;3*YeTAOC*3)C_C@{$?mS~5KB;9t%$DY4z>5vGUAiHboS!2~wV#ws6HjNBoM$oq7HKbkksUbT83<;{31_5G=a@%lhPcXZ@bwxzN9bjo z;Fgti=6hs@%|rw+KJN7)x}n_HXu5IM&b>p0Dswh@ zGpub+b+E&#Ng&8sp-@OBnG=h+;9Oq}nka7X5L!wJjW_*IZg!IjL`|KvEv8bEjWPDE zu>l&Y24x4k6D_9qjNjr{ey3``0{jE1^%u#G$b?}@wp-ehB*%HSEOwTX6a{K1$95zA zEB^qS{7HUxK1=Wtob^l2r8b1a-;0LCsd89#rXky!No|!Bgn${#4OneIiT)%{GOOWV zfa#6uL!Dc)-7Zk~#J8uwyj$Y3)2u>q1|*ZEbwOt`I*Q_FimH`a61@D0@$XY!q4s&& z@nr7klVffvkFbK}wmzni(h7kgq@)6A(-B(4jGJCXg1qQ8t^U3F$=g=zyM@~%x3uH2 zB5Mn`f)tnEEQ%DYWGP%r@*dC!n(96;{dH^gLO;C6@_F zL)5BL>7{U&HtT8*RSm?;ZgekY#cw?G!G0nXp*>o zUjz3R7(e7kqpk)8Tsg(n&rOQi{{Ym<$Id%~a>;)3{{SLdoO1P!^jG~uZ>FHM8)hP+ zH0o1LK5`8XWak_rL8c_~S}tm^*^4 ztapsC;Bm&uQE8tVllp1sM|K(#G~P>v*J$BBBiG-8tU480Ul_GuWnog>O3tky8uuJk z?!OHUHo*$BTk}OBgsBB(K~(L_5bX;>efNN`wo>ZsH@6!FvclRG6cp$?aoJ5N0OEWC z#J@0Gl}Ut>Q=ggHTtgS3H&KPc8bC@{E~*DL7dt-uBwb14dZTvc@)(%Wk8lY>lnaW~ zO#thKdX0|~SKE_RsLiOotphM~sr&Ikk!GgDZ_03zk~%rXo~(IGY;3De-Lr2wR=Z7! zvdk!KhSQ}rCYrSBJ$p_(&v)z_7q2;UHB;8GyXp?wjoWHXc(Kyws?J5Z$YNd3bV|9% zZlD*`m1ze&jRiVlq8)*a2U@mS+4_#{`e6i+YU|9`?ezv#w(440LJ+d>LIbL)%B3Qw z7I-@yyB8Bp=vVK3&n&AAHNEB-t%~aZtTm_&2SfbXUqNFa6U7_TTu>PV>a5t6rpxy8i%1HS{wS1lwQyJZs*p zBM_O}uC)F*LTh{lmO#=+W>}sogP4%|NLi_^Ph42rD(>+LxHBH?H7x~PNsD2$9oshy zx=8;3<8hST{{YO5hSmNyBgsV(BB!H8h_cjPqbkssw`~KAkYBA zAa!&4aRaRf+TuATuuQ?!gQkbXG55h^_Vi094|N>7}#4UY8rq ziom9w5t2w9w7@qJIv>*5rN+j36v1QhBNaP>{I<)@x8pq$?(DrM$Y;B4Fah_*PBQpk z68D*RAO8RoZ`yG=2`W-Z>){9OP{yl~mzgWpHMw3n{oR#Qq-OrpOft%%4UfWxQo;R$ zZ-pntsYaFe*Ao$37+j>qQD@4l(2XPBMhLSiDgs6|Vix^lOHh?u(-oqj?bA#RXX0e} ze+e7OV1}4FYA7rB3|o}*jaaL3V!KumIe&>Y_i4lsl^91%Z9!yw!%3xe5CQ)HYX@BT zNNoH>x4Prf6||An13zw&ilxLl;u%nxqjQZsS_mTzx<1ji$z%Tj8YYzeHB<1wTZh6+ zwJ%#9b6NiY1f-0=Smt~D%v4mnn$(jmafw4(M_|vhrZd?O*2}zCL`X1PEJrLm8_#WI zD^d*=UY!mobF6b}U*Y9YaPAkHk!odaB}rNq$y!cbC^4S3X~sp|=TRL4#=N?bOcPB) zZ4w5}oW-{BZl5jS#AuH;x85~6P?dsCRp>F9?w!7Q82qu~S?#$!K5uF)Pb+e6z}>ui zGS9ZRLpN)Sa^pGsdJ^Dw%dJTvX&o_#?VW=DEZ;if4Tl*<{{S=LLZm6MkfYL-sHQyw zrpt+B=qa$!5hIc+6jDd8r*cfKN3%mE=JJ5rrK!aHm2$4UsY z#<;>xE}kl6doUHq)fSfc8VB>z26V4nXLn0n$hzBa(?H2^gPudP+m)(v7`DQ8w&fgbNNh&#aoP7P=S7NGZsi3V1-z{fWJbOA7U>Pu~Bs(49#+KL% zDL|p6Y10`@>`xWM$L^bSWS^O1wr=Ejr4G93LK{C7dj>Rpote^~G@g+>Yq<7k)`@D# zlFh$dxXBUQSwl!8QtR1YW<4`)`iplvGm&WodA{Y^k+TkG#Gkef<9+SJ38r6BW;<@J zZG~mhl|Ozymijr}w{C8{k(1?$;Zw+azh;baTWeOy;a62^)TcW4=Z~(kHx_n8={U-z_BJAGQ4Uqbw*?8I?ZA0E z#i>VLC8dlxA-O5QpmaO-V=0||OM@XfwXP~JWi%gW6h?M=sqt3<-pZ+EZxBy%aZ$}#;9J+K7Yteir{5vlq06|> zaH1p@*7TJmRY47;sZ;~0pu{-V<$8Rpi9LGxMq66NY@ z{fd_Puim^~LjueG)M;UjYAZyB^tROPO5JtE5&hzwSy$Le%LUuSw3R5RGIaj{N-Z|Q z^&X2gvfH=!m9guDcnzVZudEQ{jQ~)2s975H)8YoFyB=lSA5GoKIoW)C3_ju7{Ykmn zbZ^7JCF&D+?H%j0_MDxmdus68H-IKEok@L<@U9XZX<#Wn^N0O4*)lON$z4>a-Iosa znMqt!(S7_%gzOr5hp4KJJ*4H2bjFhAEoR*Y^4U*`Bv6VDp4@1m3Zje?b#PKNq1x^4SU9B*f@ zH@H?+8{t}$B10@KV8Ls}M-&MeQ>SJ=eEOT)d6{`>+Ko|5yQ zJ6Ch;&AzucP1^qe1^A54ji=;TQ|TzHjwsa7XGDX8zMFkA&a;~v_kA&s?FEK3pwy)4 z>^a}Jm{MGEGyxzS{j>n}KnlOd)ODnQZ_2un&Eb#nvVlkyT8fqS<3)(qDV)jpgfEw- zbRwSY6>C5lLZO>`HJ;f6AeC0W(Sj7()No$s9Mt7p@Lj$DcgD-im1^+x#I#^m%>#eo z*OdyAbp6;DlBI>Kv~@Zsuzhsw&OPHjjk4kK?b7Ra+SaJ>?h@UT5s0on%5a8&8@ZS zQoOdpX``li_J5{*incpax6RK}E52vK>?s=g>Qc1ESD2AOOnVKsk&2SkDJg%QtK7EhwjCy@#@I?yiUE}?iz289AwF3;eS-wsfx&X00iq~z z#|30+Y6}h|1r)nbf+~`0+w8|ABQp~Arh=k7_MS5yg+=4@Ck%A&IDb-CzAhok_QTYAX>B^+` z-6TlO5#+Xz%7$6z?7)fBLf@&S5N(FqrcIRBw^eh)2qc9_;*U3#DnPGxc#UaVJoNoIC#S{X6j4N~A!H z0(uVIBqLEvo5I$K60G_o%!6(&rA2Sga5m0KB$A>C7{vDmO1E=E&^8P{YkHf`gYvgE zFuS}Pofb!%31P;F?V^Cs6>!aR;uDdIp)C$-`tMA4H+LN{?xdj>$!Y`&=42p$Mm;aJ zm*um~`^oy^iaJ@fr3wYN%DZZuySs6-F@QF-##QRp+euXnmS_k7DTB#Fx1t zMa}1ILc1JbeVBsMp){jWJ34{*1E!z*oiHWn)Fu(3HKA+Ns+_^VbtDW0ii&mY z$5h}}v=sxd@W9OA-R|W77{+(Q)EX{-y4b7kEWR^^$8#& zAgc|>U%Oj4^4|A;lzM+}d$e@q2JbgFW%l`nw;&}%AzuYpD>N+pozH5<^Yun=lj9wYw+{q`+zmPJ}zz6I( zV~#U4=!XeknYoBpxg|IlP_)dB;z%AZGg5u!Nuk0bTY<;wZtv%%D1+DiW#I`B-MKU8 zUTla^_*oP`?cuO)fVO|ki4-D~&!_a@c`Z#j9q*8Ta$qa+$8YhjA7N+-eIn5MZMfZI z>@DL0**$QWxKE5OETo+;A+}0@P#tJaD6*}=->X`d*f}#+DN+4Ba(;2zq^JUP`*3?0D}I1a?(l{wOC5|$p*@AB~#{ShUwg@h4xO)@G_r@X5jl>Y!Njj{eg z%ensm`tW%7c@;k3Fg2YlHcNB-pLrwlojz8=v)a)F`*e&|ZM;hyzX7MPQCpO(z9vL9K0sAp6h*Ag|=fpK0WIVKmoko-cw-d`iX6nbD@s7+%R^+nS z&Lag$?jUw!vF6zOnj^1YOE;W%ZgSgir6EF9u}M%h$g<$3Jj=Y3>u71V=lMvhUt4F$ zyl^aHgGI%?IjgYIY^VBhq55)~O|hHI{{Y)l+Ii%^uzZ(QOn|8SvFkREJj#tq&A;S?}n>oI%Q->m+Agv40$o!cF^cZIUuMc(zM5x&-|yTS^oeLRV}Su5cCu+yGwCb zqt0@Otq264ek`NFq@`Fi#NUGx8dH|{2z^C@lFdA`@YgS9B9xmF+Mw@qjz<3g+i>jn z$D2adid!ZlMItywN`)!m?;Y5J84eN-`6%6nr2xHHowHqTcGD#?jk)((5}VDpLu)D2 zpJApnHgc;IGk+PzbP~mW(bRWgy;1S8BW9LT8`t4Wga*%fCYXZl)RjitvsqBFwogtR zp?Gd1>VX>F9#RtG9ePuB&_Gb|x2S_dic6dIsJa@Ae%rY^YBBe2%D!wX?5(}H8B0?l zgsBc7l{E8f6$?VBDh>{@x?8%mAj{gWW4%ZJ021+a_meLTHx;G9+pyM=cE2o91HHDZ z@hp$Kw-o!v{{U{%PjshZS#RC`V_ zHm}O$`I)`BjT=y+5I{&0)JIUNdufdgrJ#Mn~e8U^HZIcX+ozKr5^s= z6K*j`XERa={M(V={UxcQqn?!!T6?fO?LqMi#JTDtK4F&g){6*r^|(XspAw1ak8Uis zt8r3vADG=JSNJ;BB25iy+vo0VoN96Sqd9-!XP?c^(O%%btc_}1*=0xn0DcvZL$~lR zapn0R6ZeRyX+oz(3GF#y2LXV(li?NjoD9L@K3>v|ixbUKk_7d915y6#xZ^W>{Or_O zZ=i(tU2R)s#iYoR3#p@N1q(fg;lQ^asF(XjR^nv~+wGTa^1>3nh2;>E1x0FbU6-ib zukn(%0=_@WcN7HBH!Rn*=74(RC){uIQJ~nLb)ueHo$=l=jHd(Ar66|Z5#nJlF(?PuC0PdeoA z@=wE0oLR@Q$NVeYc`e`a5>sObZ4E8W2#|!Ai!O(@(AdIVy5L9kD zyjVeKE!v_h=NJfVxV=dMG$fT0DUiS$oNH}{?KAwsOl1$Nf5{KFrk2{+N}Et1q=XEJ z?8bW?dReK+ezbHqhHgz;w)M%^77)^1R;3^900KbmB=iF_jK1-4E1H*>N>WruO1(I2 z3&z;B+_%o?wmD|p8>j?Ytq%<{B3njjE~oOX--x8<1p&^u!FML;?llF$8$F*7kyVf< z)FKO#w$~TkP=d5YygJLOACx^6L{$6Jiw?}YSu{u7OVElE%1J2&8nuBz_+!;^k>=SB zr!sfH1oH*T?*mUB+>nDh?Bd)kgr~}RH{AFGec#~i zXIV@V^@eNDPyYZCrPU8Mq)<`1)D;X1XK{=s-2)|r-vSzs=zp8`^^1MLTejP(cho4X zzHNH(Jckob(bAYA-t}m&mdkd!3mc=;FEQQuCfB^2Q-+DI_YeS^Z4PiZ6jF`Dr-Nap-#h} z*?_M~uwnS?){gLOZbGqfkEX4ayRXmbiDj_P?#8ht1w!d?8(vr{_rcHKi%s#i|>HgTuf{v#v1PLMhN zq<*rq{Kxp?Op%lE2Y8Ts9 zZmBY>Gl=-v(3_VMVzp@?q+*+Qoe`zjAjea-3v7bfl2r1M(x!sG;flLC4hur={xoLw z24=|#q%E|yp_fQ5pc6$?rABys^BY8eBM@<7wrS8JAf)vKobU?FVILs_Wmw@PR766% z%{qPfCFQhqa3ThaJR+h#RR*29MpzH$SX#k=Wronp$x3n^pbUrIf$mPIp3>Bwb`_{x zEbck;0kYerI!OSWR%T9;FY#gM)R7Ul($E0DkfYE7KaMLd-(l(~b=YIE9hs?9>2+RO zYeEU2R$pcV=F~cH{H8zJPnyykp#ea9dogd1=6U{d^@HrxSCyIJuua#(2_E6%xHoZ0 zfA`fm&*Lvr_nDhN{&IfyaZ$F8l`5Z^k@vVUu||&X#wBu;eRG!K8KtCw?4kkwoJ9be zJP6`cYFMnf_>kJy_SX@R-5DIL)|Tul2Bk@EzlCu%z62sB{9wA6l>Y$dR;YUqOb=s= zJ112_HW!UT=GdnHm>>`+jz--q0}=^=*_ zUfi+u!L=22^36WLlYrwImIF%N5~!RA>e{pJ%L{r$nD+EF*m1RP1OhxhTIw^;18#o; z2-=sekDG*1@=^j(kdUGO07|%_x4^r-@e)0ajchTORHyi2uQTHh$9fNF?CA-w-HL38 zmYCtYk|`5UVOlf$aZc8y#kZhOqrUIz!%GOtf}21HatBbPs{+)OQ3-Ha2t%%?!nKe| z?aLA0*NN7q*r-`9AzHH|rYo3rxC_W`kRs*Mlo$=gR4rX>TeY+bNnb*G_hO?a-l0i^ zudRmP8cCr}B8{q`{CSP1$LWt|2FSKt+Ko_IDJy?2&+5i&a8!wHgYgHOLWrU8gZSeQ zpMAvGVl_cUh{;h3_(!uWd2e)X*A#swqXGhgbfZ4L-nj5>+H<)rOKDY~Fr%+gIb$`K zw@F5;)+tx~v;-D0%#y2Y63JkdDJ{sIBN>LIj${R@iLtl#(Un4ZV=3eH|o9CAj zRNEVcHt0)Q*QG|8_M8{CW=j=1aWH%4&hK2JEN2F8f1cbomrC_1sOSkbCnBtUBmV$h zcP6v0&G{q7eyMCdEK6(6vel~fsXrZZ%fBCFT;FPVI@>71E4t^`75BGP6&wJA*FZr9 zR2Z+hx+(?R!JyrjGVK zslz10V`U8uSzciFvZwODZ4zkXn)q7WS>gl2yXAA#`z-g*qT*d(02*w$LMQQw$iDi4_1rkuqNI!i!U zPf+dkqLKi}pX#n1hYE|3##61(sOsz2?ZvgLNU6Ch3Kp2nA2Gt(ROqD^QYtg=!FH^r z#2V%1m|^y{DP4&b#jG6)>$g~X$krAm6ofz40Bdfy+6C_k-Ry4hMvSmEBz|C%i6Zm|NORib7cJK?QfQ!z{n(v&2{~1a zrW*ZK1D#3FyF5t;pfu8`=(k1*d5{%A6pqUF;aM7$2VK>g&v!o!0sAzJO>coDrB(-V zN)4JHkNxtyJ@}OoWk#WB65F=IlQB$*1)_q6Pv#(x54hk=>rU;r3eurCS7Tyoa#bg2 zT>7^!5U-!`_StDmF;g8j##W-zl#poZs-KHI@#Ggil;m@{E7E@R+OdAF;YQN8(n^bo z2lAHWcX)0m@3JF&lBOjgFhibWZD>GA(~zJfR+#mk)!Su>VL|H881GHu-gZwsRS_5I z@anENXu*J#0MX8-y)d;O2-MTc9$6Z5#|@}TXg?wW83kqU!%5}9M~;9{vPA`QIC)<9 ziMIxb*HqS>xSleogk^JSLR+*M99i3JsJ$o6LAvW=q#tf2j{*?0qx>rZwW&s|zqN>^ zAesf;^9srip`8?=)C@`rJ6_nEo9BGh#%TZ~>eVHV>Qg0S>$iC$649q;VZtk-%FPE2zB1x6hMg%YPgNw;ei)ycq!WIk9^YU5I$C?1PJa9dOKpi3 z#MQ(K=`_h**qY}7*1jS`Xad?%=k|PED~j~!T|(s$ZKQ*w7mv1>VH;t#oJf77`L1T9 zfS{93zT7#>($Kcy>P5@9{{Y!=-xr;Ym3 zwKC`llF_b(pUyD3JPxrMGGOF_6~67^Cp0OA6YS}Mtz7hmvFl^;Z}`3{mYHzV{hSAM zlG!jNV3iJ(Ibc>y9>b8PLi8S$t_Hld7Jp(~T0_+`T?{)@Fg?3_lU zP|Ia{rEV}%b1sSj+(0;~$y8;WTA+HyNtE+r;|)2V-+<^9YV`$MBf6%-H256^{h8uB zgYAXqNdEvEC%I0nkF=Z_Z{Z@n@g_)&qfBkAL8w$IB!BX3Ro(d6VIKls;}7ptf3m#( z(TaQU4#PV6^w4I!>2-d;-B>4zf2`|69$DWX3O!%7<>{f`>t*f66qb-dNIBQ;$C+5U zS|eAJV!ED4y>8rFmFnl4A3@fGxnzKU_uZ6s`?2*OY5xGgiQ%2W{{SXMQDwm5CW=$d znuL$`QH=vBQ#Y32R9o|iPLih(p0)S_Mg}-Jd(_)#MxT%6%1_;dI0kxkJAY3~p`%~B z2u&!|5!V2nIaIA7HJ7lj0}`h|gpgH!vw!S<--{B%fGvm5dV~J}!acP7aa}GZyo>kS zn(j$eC=%0np3VIV9Q&GGtw9Rbl>CuYfgK%H&3#<#DluR!+kC-C0%$ykr z(E`g^0?12g2u&zPRiOTiEVvzSh_!NZn|o{_Cm>~1{{XfzU5)Xu`_IxLd7(`>Qgalk z!Eb;JIDk$Gdcw+e5Db+CQ?JEe=)y+D3A*8nEN~@05_K!(pCj!?I(4R}OF=fo-7c}K zmX!b}K-j;6N3a|>B~}zW3HpVL@e1AYN=+)J>+i_nV80=f&-|ZxC32$c3vY^iXdh-+ zq1%0J`x#@G;5PXE9$m%C=T?`e?!^19kf7XQH*+MpaCmf~1Nv}HrAQ!5pNMR!Ivnd< zK7R`!w<@1GT$iPm^}3Ltg8GzF2_2Zb z?RZ8pZ6HnJ`;$w^<|gE5e3Poxrh=#6(;JHttdW$z!Ie9ZD&iWZU@dv{?8ci6#%^9E zbGW(`>Vknrl5**b!!Cw?;Qs*LKg=|mci@q+l|mLuo3+0z)bm2W?TjM~$0d!~MQ$69 zhfqquJ)IITw(bp-YA3Vg*EW@3VHn}W<-=`&_UTYf3Sl1^G^JJjILdE^uVp$Q+FTq3 zaUo)ev#&r0GOsWGTzRzr0LpqbpYas&hVEJ-1te81HbFfHgk_!%=PBgfXdX8El$lI0 zn8;JI)DqYVt$Qm$h_2j853_1hkv#6ZlUo;K29oGS$@sz?MAI_0r%Kd2d?4bM5$2Jg zl({X_#HY5syf3#~eYFwG?o4qg&pxLzachy3ty==yw*Ix{QtCnDZNFiJz>l}#hgv|^ z>0z~~s1Bs48sir%%d(#qdOgju)FGr%&i+no%iMS8v;-(mQgkILN=9G`B{>XjF{Jtw zXOZS58}(jg{^A|pn~v7BEW4^h4W*e0Dk`Fo04MsfLz9aX_!@j{=IN-r&HE{yM&tEe zps`)kvQoJbbc(J_8UpiP-9HfbU|f5u-2H{7QjPn5)I(mHH|njoL$+oth!P2Lk_e!4 zx35a#qkF8pQ=Bz^YS{|Qx>qmu#~oam0y}{V3lsq)^~SFQvT~NMLqC___;ux4 zk=2Ww_|;ODweAF=rL^;C)RH_-Af!|uZZ4tQnGkiS0;%_QG}S!LRo=LRkQXj}!UeUf z(^4cp<65nQzK)t3YN8^&cTefOZ@XwW)-r8 z*PE8s-IB~yNK)N#HPT33GO4M~m_(~GInS0PZMb8+EM7yXb%KYr7aVkoj=Ac@wwJDi zS$BUZllUqB055J|5EK*@C=x-h2-s%SxwDoNl9Yg4c}Yq@?I#&;oObOtm1u0?iPgN5bVqyrJNj_rUMRQ0 zy>3mj-)-`=Z}D*Pt#Fe3OJKX5 zo#`6adxW*9_RWj^E@Lq~Tk_);T*ETb;L%h)Y*`>)cuG~q71kTh?OTi+gY70{WiQDN zc@7kWERsN8G`+EA<-CO2Wlr;c-OCMCXmZ9bhgv}c!m0uHW7zC1PbtWgM=|%F}roNp(X#D;?P9jcvXJQp&kbB>BdByK}1Lv&-9(dK$PilW{(!|F>Oau+Y>(Pl1=D!owkaY zXDuMM+J|jPKs`<|(psA}E(iuw6k%1&=k{fWNoWyoNh6?q*(2utD{(tTWdb{Pr6mC5 zRF)P7Uv4~~3fftcHt~<7=evV2f15mLF7XEeC>nw6?xqKvTNK;KIfI>X+`80J18^k> zx3Hp`jcfiGvc!g<*%G2Ccd16hyJ{1uKiy0sndhmpaiiJvnT*|)Vo-N7SWP+;pW9q% zOtS@aYGu>;Ksn-0nbjOX{{V+ze=BJ}^gzT-5?gdbMP&s`I%!oJdvn1Sw2CS&y0%I? z0fGB;;!{vuAHx(Iwz4*xBs~=$pDrozQ`=FBqWQ;|87(wa0chv#{;WmSB#opFsL0eb zq}Q}!IZB}&FzZkXkZRZizPJ{1Y?>C)kCJUVlvD+J^uj4IV`hVB&`DZHM5y6!@FRRl z^NwQ2e$^w-W6ue5P@y1@vaKuB|t=%_=migWF6^W$1Ir_7ebdsX0=V#IzDObrsul z^bd!i$3uuj_z035qWX2u1teG8mUorOC|D;88&wt-^&Iip3%N3d zW?WhVe({0lS_R?8vn+<*h*xpCAg3W66(43Z**r~rcnfxj{$#L|m@yUte#3w+4nvJ> zl|i+uO+2(VonFAK2bcaPAGQ2w^eeejUrTQa(z|7k!ZLB4##*m=n6m!>{7L)Y?l`oB z(6*ZY0C={leTi7cp%?i-7)z8HtCHNNVF?xg0ALJB22WxHY_>I9twNc6rGMudW4dQt zVZ0Cn})-030S`^qqp0N96eBxU$>opz_H3aZMRe zy6KNyy5IO%)g2Nsp3VONA!m=kdNzV|=cc_5EAEH6&@*vOW1T6b7LMO;0BAbZ!7WXn zIZCY!PFN24WSFkXN65QDm_2^n4;Yi%bU;wsNo_!iXV(y2jY6Ri=tWYoT50Kk8eukv z0$M+{$5k^*wAbyf1AIf{#96ksu#TB^#eQPcDG3+x`sg3bkwbx!&?^OvXH}^5r9Z0! zv|Pq|fi$2^dQ%zQ)((Z%B#?a$cn60Ve2ubqC0+)m&QB<1m()9Y{y4~Py||ib>S!=? zwGX0-NF%eZJlk;X`*7fmEG{Mpl-88US3G&9{{W1sL@cB%GVx?(oiXP(cGl!qT(``_ z@g5;*r&HGul+9+wae+q0LHL zkfi2N0;&x=alZO{zOAD%&P&rR8G-tV*^}IHRBilG)fdtR;)ijG9()M`Ole69WyG3- z4nC^H-XnV2xffK=lybIvmTovrQb-whGuKy4ODxv>NaKVjy>dC7SWDT#mhg~>_0&|= zSfjt5>RH>s5#`QGhlO33s?5IRv@CJ)S{u*Jr&V>uM>8%hV+n3t)H5o~`*8PtheHVk zT)Gkns5p(>WbnZgq)gN+T+enQmZ6q$s3fXUs=QclDPXJ(o!b8ZnYQnZ*W-%tC*FcF zVX*%I%8M=bOF!`5FUg2cks=K7qzP122h}2Y>bR z%1NO;#GfrDowy2=z8N(Y3NYhW+Nhwpn;hpn3{k!6mKl6Qcps$m^f}L^$eZnGG0cM^vrep z@qGoL4aw*!T2QSoWzeR$k*Wen+4jeptyzSHNXxMP7@FqGxoaUIjL1 z5vb_Xkrl5)i!cB=J{W*jv@{qJQLKU#s)n?N*Of+H__U7#zI;Tvt)G+8PMI7@4j^*i zHB`aYnkS_*#hNQFLr|maEUb>Am@*AlKwFB4DjK5D2+c!0O9fDSk5T7mVEnYvOo$nK ztBIo;j2R8bN*;6+)j>xw@5IpHL34<|2?Q;pTG2^AjutYh6r)kibp=zDq50B`{{Y#; z>7d}H!9VysPwhNw?iIl}mCA|hEYy@qyO_LQRrN zR6#=1J}i}&w-N)1lafABzO*YjVV3wEH=vOmBqd}F(oQ7?5~m$WbkiWJ5!bK&oGf^p zM|W1V$WTb=f2S7l_)5#45Y%}o(E7S~jXvxZ2~ZY*4mGM1D*T~I9{lTwWg3$?P$=pu zvsDpZw8MI1g6}lv%s!MU3>ZaB?mnl1j%!e*6x|1QQwn)-^ zU9=cxR*+$~Rb)N)?H9k>LXU|mX1{&`#8rg{UigQu-qfx_Dkgw1sw>&1xE~@ki8Z&W zG9aBDp_1oroY+YbOY?6z(MxI_9Uz4qokVn{73zqM?NT9Txumh zAc-#}MznEsKWP}UP*fnhbJmG$>TQjx6lwnegt)qYuJWn)Vv6#tnRCvlOq^N@N{wn% zgP+@q+u_)J4!wOfNj%ZGR-TR6WtS7&6#GD9%)8&=N22z}1GW()lu1Ae$gYHaxboX8 z)Q#28gz{nP)!`%5?=m_KPx+qXQhU*-A5wP8rVLL9?l1Gmu8>Vxr%h9ppScyq)cF8Qz5f6|mNlhWjsE~xV}+nr;ZXJHR)ff6@y8s25B=`rpGa+h zeSnjM2r&59xovd#Q50(X@g2yF!&SUKNB3j>J8@k2ni`Q_`Tqdni2nfDxBWOvB`#@f z{V_l+?0dxWa-TKdED=nZX(-71&MPt+ceL;?aN^aaQc!thJfTd1%LO2L$S*V%lqec| zJ-Fd#@gV)><%=HKWE~+QNhjOp3|86rnKk|bTxI@*jOtNJdrl6vaA|G%n{5w%WXw>n zQ}qB;Q#SqEd4F4m$eul4AT}>*WURe@{T` zb@p_?_N&t!Hu{8<%AfZ=x!d3$^+Cl}%f?>v%P+u0aD9O|BXnbNcnSBk z>buKNO0TXYwEZXxC*m9Lwvh70Ivf{!;b1oYG&}e2{{Rgm4K<`l`FnI=Y%T=ZJWCPj z&R@#f?k>51i)Tuo8UySh)h>ttfv=#(Z@8Tsw=LY%X%8$4dobv)Nz;0X&5$XX_tOm$JW1Dwl0zHG>eM0RKn)0IG3fClg*#Yu)^E<!kF5vgi40BY+@Q*Us) zB$QF9?S*0u%5PL#%ay{$ThiUlmuryHpugdYTR3-As?APL;bE;A%6?t{0O984TUDp- zN+9-pV;idv^5}TpfBB!hmbh~K?Ye6))aI6F1kpW#Nv3~}EAB$I4#S%-cGN({rN#0JkB_X+)?Xc!|%#FTK@pW!l}tkc(D@rS!a*&qqjbuqrESKxYk<%c(xhdF}YhSE!9D zoMUdS(5k+oy?<=3F8#x3af8wKM(&Ih)cwq|7jCw?GEhh#-z8Df8v0K8>KADH-%g81y_lR&nFY1gLqQ zZQI!onogu<*66scP6f$AJkR$*)YhMVCYEEafY$M}4x;yVY$T*9WKw}9p#9iu$*Cg7 zY1I$yt;%;hEXE7uxSEYjxm2GIP?8Q^-G3z?NSD4&K?0oxGGvysBGke64OPT8SXgW~@bJera8m{vuuAKP=ey6Ktzm z6D7JIVMbV}?jQ2~#+$a%=?>e9%ICZWKz~*!qiv*KO{$5sQWg%nYB`*97T3gE9k7aU zY&DZjF(>LL+8xFw_x#lTrn*c0v-cb_wS~6c);g)AO*Q`j-IQ_cOZ+R`c-^J>jFI+6 znU@#R5~tik!hxkg0Gi-?`bMReDy+_hb2sU6w+u(sR~2nhLMfR5B{jZ;`TX6RK6OCibx8}bSBC+myFzdEk+^CGE9fEx4UQ=2cW(=oF zyhEUE1e`@IyD3+hek8W`>+gbOqqcY(ocCj`qi*pY;+`Eo7gcM1J7+w+a)W97a24(A z5(=(pn$F9WH>6o=HfPh1)UM_EcW?IVZp4P>pEY+GiF!goInkIw%*Xn}8O*Nv#fFiq zW{V%PvSgE!Na+s;{XBTDk6ed!-jk$qF5!$)v=?g<-r9f_31PBI^Zs=lWVa6Py?N1S zZ8px?w_$6mImz{j;`;@&lK8sw4VLWoSvz8LU$e$+A*WxBF#st^O%tdEfC*oI|pYwxbBl32Arle6z*K>UG6cDmA53T4Ps_mpJ$tOq^J@>c;GD zE93all7~4lkd{Gz!x?EQN{wntyD(Sk6aN591dbd()G6C|uZZpc03{*bR7x8{)chvH z28vR$BpP8pdT;wfmKCM_!5hpq$v*hXmAkP!n&bDy8>kIYD$hFc>6kxs-?i_ z5sM-$XwA7*c}+Hw=oJK|0VLPnF`Bz7vr%SAu#B_*9aTM2bLrWLC%``+;x7*|xavJH zab~RbHEIa%&6Q)yY_Io{HnI3hG*XpQQK1;nTwv5T42lHSxP?F)-K6=Aw$l~UA5Jt>(@O@G8IJr= zb2^vC?Xu+phe7VbR7ArGORBDPr@IId2~-g}&2=um{6{#{p4pcWohGUVgQg%Qpx01J zBs!#yr>9Ika0HhWSgtN1c3A|P5ak62QSHSZ;qe!^oDn9p!c^kuDM5fE#3tB{0V!?G z!kW}2v0r6ynM>A-8(-pNn`L-NmUh@IpX6Q32-a!w$U3nkwZgM zPPix#Gqw{;<0K!*s*ke-D`9}Ao0l~+5Z5i2^Fa|#h{SzDkVwFzXZlK z0*q<}+I`q8WO$Qb9+JBS#Rny&;Y0q;F`LKvk+<4E#1WpF#YyjVU(%2hhZK&K5 z0sjDY?tkoJ3%&t1)`Dvd51K^@J}&QWCxq1r2ZRO`tJE4MfliYItlo3&w$d23taN*X}GOILaG@k)T%cTPGU6b%K z!)PHJR@*1#V`>y~BV5)h324_4U%6QzyHKQ9Ho=Mi0E&Q9o{WdvhGqOC z@gh9#5q!kY$c+xG`tes{0qk;Z#Dzd)k>UV@_GOBE--xt_!iTGERSB*@bj4-mR2^;V zZ8Q@Y5t-JSf6l=Ch%VFAO)V1Ritj!Viu>`XeXF9QKleuxdwO7KL^)mXHHw3wLb+sd z20|vWCt5K2Q$+q4iVMrr+mXd~s6#{*oCr5%qiRR(LJL3KX~>47C_v|U=cs8zMmXGo{?VyiCAVp`%|)HbY=f$zl> zZ5K1T!Pq9XJ!_1f^tCUx`piD8ZxKc4e6{rl3;zJbr2WK@?a+aeh{loSX~f1` zzzeGCtkbTw?ZXyZWXk~wQYQ6Qug3x8UGXd|;|U75sD5s^4c#mwhJp{u4kY;Lh>hafD_mNtZK#IG{Lc}VJG zJ5TwC7TX<`@?sse#+fkpqJ*RSqa&l0&IFYeGMc3S?rP`4#fW8G(jlLz+NW7|3-ws)Ic8#)2_ONlJmsQdAKB zb*(FgTmWxSS5u`+?I2=nBPYN&Xh-;p^fWd5aO?g6&w$Ljw@I(}YF0E>J`$eptVnUa zLZ26L>4Pm5(v3i2wmxgS!lC>zCH)Y-0#)TDDgy^#60QI_+6-+S}HL|`Slz(#!>7>?rgAw)cq2}vU(+A;PQQ27Q)1ICHvp=0Oc)keBQsV587A-Fr@ z6zFP%8XyXc!NX-bk+@NjHuBT^+@IzFAbY8anARggrljgtf&AUyb{jqe?Z&KmwBtbt zrOYG!j0WSaOBhv(2yrSu{5MWrnk$H5#4;R1ai3*U;~_URI(4R~(gjIS?!#rkgM`s? zqAews1!+dxuh@}_8F4gldIm!3s-#e3fyGu_DOYw~eb^u&y$4h$YZcL7i1o#7&xo4M zc}%kDOX%nTJx(T{!gYzb%|9aax5(R@Y4Od5d`r|s$CTMXZ^^5*N=8-aefaYm+a@ji zx=K2py^r5pmM#?`Dke8JA1iMe@+2{9y2pe`Q-v#Oq$HIZlUj8+J0rHRt-2+tUyt<* zb-Nc?9M|>PI(_GqY!faEM7ij!+wJPF->jr^DeS;O3IG32>)s=L4YNkp{ zl>4v?4z?_94}p%dURAls#0c{iNd2KS@P)FP&@RO>>>n z{@rAXDK<#zAMGh;6pvXND8bN~c(DHfgHi5m=l;;Zxf-I^0cv%->7=jz%p1vcG#h=< zUcp&y{{U7zo4z`Udb{4w=MZol?WUcp4U+vV}u4$NxEGUa{mAZ(EM@2h8zC?>yjV;074J? z#|@tWxEkbCcRBHUGR0(#LL$xTDRWYfK-x0I*0>b6m#n}1L1s2iGun`rfS063RJjA8 z*Cdxeq+=^`b-vlAw;EbT?;wZCt#NRkg=G9dWiLvSDO1-88B}|fSYvQ6Ri|xbLK7?h z0GfwvJh1Yhthr>A$EMS@?G0yLHR>MLA?Ytz}`%dRrr z!y4X3k8YCbm3P)7u1w^2gRokN>`NnFVj4{ntxhc0ATfX2s+P*V&H9dfUfBw_NCX3= zCZ@E;%MH>-cOni^(4>3$=M^68g$e`|=l0VYoIi-g?>q|CAgxZWpcl(jjL;+A<}*9@6l*qvpfS*Y;JlX|XUk=FSk8nvp(SjRE42&C99hMt(v`Jw?3yKL)Q6&U{ zObL*}?7@?j0Lwr$zr9cSZJ{yNgOyD(rEyxGrM0^(raY<=nfr{N_j3hLwxbs@_)(V0 z{{SOi(N|?UT7KCfB#$XcRejXLT#F3GRcAvsy4ND!)TJb$wE^)2FR|j< z;6>h|Emob?u_InBmniP;y^iQ~Ms3s=pMvUw9d4D(q>!L7QDWqIs9U^d;Zt8s8|XTT zdwN^mTF?U9ZEEoxs&Y86y<>wSSqhXb!_Ky;N(s%irl_a3m@eAT3C$)f&rwKWbgqU$ zKf;)fFy_!PT;){;^%3;|x@_(Q8R3_f6q0%yirBQQi4iJA= zf+eFLHcO59$kvxwyS{}!R@nVT(uw!sf)56FUh0YaWhVT}RPvg6FCi-g14`E;Tsl6% zjyy|U!Ths7Zf{#OY5H5W-Cjc%1F1k1DO3i8^{y&6+N&+DCBJWS_0oPPTz7v0*trvK zsmny)l#_F4DW{K#8k92Bk<C-f; zpzIjV_bs>9Mvu29m8LyzPidU`yD>tb%bQm<8`G~4p|oFHt?);P5%HhuSe>!ptnwe1=etii=- zOfBtwrosI!w*jNsrW|3@+oKb8?*9O(lSa+7_#sL(D_k%Y7Tm;go|+kLIe)SS8`Zhy z$y&Eg0fO@?U3vr6G{W;RdaocQDXm6{_5+{v;f^(AV9|jpDyTvL_{pFEW=XGhC$$>7 zB3R8N+weS&{Y!mMPv_52*C^XA`sKf7@xtBPyINZ*Z9-XT(q+gY(4bPclSKOyUfg@1 zX>AK189A+4;r-vc%KiF9=#DVoH~#=6+piPiJ$*!^#C2_j7%}Lo6Q6_(YdT2AtFj0+ z$y?!RQ;4{r6!!d^U3;_a_hEWy?yQCFufI2(-NRvtDpPL6er!ZP{uwDHDv*Gx8s*ml zW!hYhC?&6E;8Nr2L+(MPugRWV|nLvPxh4c)*6N)eb!#XFemTno&CFGbLNMPV*m_F2+N zj+BIDl`;na%IZw(cAW%CQRSjs=OO}XhXHITjO*7da=4ltl!eRX^6O{g2(j#!X;ztN zo)+ZRBshl_g|<{tsR{%Nj+h=cp*irVTa?slLXde+maMZx+gBrA;XI0IPtQK;GU-%4 zR+2u<87x~3d?0r7Z^7}a{URbRm8I$#+X*L7u3*f zkX26!IP&G%*>Sq$)`s5Cb7Qqay<7QvVO?biHuq(|@*VmjMM9@V))hTq$6+W3nM zeEWAEgtfGJKaA}%1G|FGyY7{Kl03wvw2yG>jDG8bx!iy4^hTEtD+)T@bR_uxtGIT? z`pR}iu(ZhmEV~@7#R}(Bm^h`i*!{tORKl`*n`XD8V`}~C#xN-HQv<_L@E%i#GIJ|mT^u4=~>u~n+B1~`) zhwWDdvQkfIMxs8-VrxC+zvI7JboQb*+dDP?0EMC>+#Z5#k|UIC{qHsc+D#dcHHKuM zf3(k*ac_zBViRb7GyJinC8P+wIP#Y<30ZXvF+1so9AfG+VdKAsWKV;6{M%)ORefb9i4G~ zdws>qY1@)wU~G9ftCr%RgSt0MyH!@k7`3YIW@`^Em8bMT0)M^D%`0C5RbYY zgq9c#1bGPw)n2q1yN=eI)WhG&q?#C;w(t9;)o$tiGRQ@9prNgCbz>PrL{z)H%!BvD zR3Z!~LaMhMSXDC}D}wBBBfG$gT>xgKSFT;yu|Nzlwa6EuNcdV+2p;TDWzgbNL&4m; zx(rBgs@mw$H0|eN7m8b`!QSv^2F zZ3s~Bv$Cb%Afr-1FC)SM2d!}iN5IQXx$0WIpABld+|)8clAjUw1B}$&8K7dVsu^zM zIpx(UCre=W9IyD5;93OIHa|T(qC^AHEz__;N2ZH{l^-UtVau=3PPJYa{g>-k$*XA zEAM0##hju#vXA$BPzdP6v8rblgUgh+xFtq@qkD@^fbWCWth}$ zZsu0Fwt}OXX}{rthupR@WHdxkLGY{i;G_(FH3aQ-0-ycq&+f#~m1YzDlo71W)=NXD zhyXhKao0Exw27wv(pMHOQ(Y~jeV}5rGpIGN$UMx~88SDS?*=sV1HH%4Ka- zl}j60S_fDqK{e03V<)*`H5v!oy-`5XIswj@$1S&-L(z~%f=5qwEk8@5q0Ks(g;c#z zek0QXWNmGsGIfb=c&cem+e3Aymg@fiuNOOIV{V7fefx~QcD`v6a;DIHUh#sVakCK| zV4|l~J`H!pmnJgD)(v>(e5uqK)Ax`=I+dt}$;e^BZfMvjBIn@2Qw70;0;LWWA^>q&H zOjLrpDxx|CWKKC88n&XcNCPbJG0JK^-wf8k9~0rZZyH=8VHZWZ4IGm9dK?f-9}^~8P>_KhP3VWV&zSgHGP+dmlF>(Dks zV!d&HXw5H-`6(LIYxaBbA{OWruZ2GRDH;tkoCS{c{{Ti1mgfMfl2np9W0E1Lk#cPO zs7jAjWPR8w#}H;DG=%wX)9m-x9dc|zEzo?YNIg4n`1y}t2+g^H;-+Nc!o}`V3Bsc; zOJF6#?KyjJXrF zD2YmHqySWU(>x1y*pZb9j?YC^3Cw@1ib-)avEUv`%`!Q5VWTbwTU`%dal)oXIW6)B zlIkmvIAr6gV`Ar|(8(xJ)0wSl--+p1kjc@eSnbntT+T4su$46tn`y$K;HtAvLPx(C zt5HikLsy;u02M!A)7b7tk302l?D;DSt1cl#sGR+nD>CA8;0J|RqL=!+klMKdz_wiT zP^*0sv;qcBo`BV{{XuCii<7honOT2yN-1J7`u#UP{vgjHiKYp?Y_Nh zs$?M1f6y?@n}HT1Ryp^`uO+MlRs73!`vox+eiU`p>K{9Jly7T@r@<6w-CVFG--d*! zl2ZP3u2@79P+;u_L}!F>L)FJX(j-(XRRJ9^Im<>4%Y@L3`^|B~`hZtgv{~+o=&vPN zb?n0>z#`YgK(N*S0Es8XT$WGRF-v!ec964ZBs7(u%#~-|fJ3bdmK3Di5>9kWnfs7V zI{yF?OFkn(NA*-jS)3C760&NtCu`dQiJpi!QfcthKfevOFxv3o{i|-})e?XCsKi#d)GcrjnNb1! zt6F~piRKC}$28ku0YGJ|{-y(QrNH<R0f>17q(?PDmy-n?xwIGCNm^CG=TAqymUi0SXfj^6_~ zgc+HJ6ZfhD`Bsjt`Bxa|bV!41i2Pt3LROHUJ>5ot z=TnS~JMSV_c~nwG?d^)hE!MR-r_H=;YT?dU$JdE=x36utr7}KQbW55u z(nunsu;Xiwl`D|uGk5T9#pp+ja&Ex3q$A{H+%%dgO*D`R>(xYI&ldE&L)PPu9|7yN z`9i^bnwU1*42Ik(syxDgG8NCM>5jR9acbmA4can(l|o35dwEW{Q)_LIp3-Vh+%PO5 zMjs5M(L#A~X-wV523~Bsg!@W{FJtf{1CqZnzi4Y{x|X9dwd4TH?7-KnN|t+evOSji z3G|ieH*ET8;tpNqt0kLqZ*AjdX76*nZaHkKEHua^)Hc*}!{&UT54}XKvmL&}`ErCvIQaSaG?Q{{S~r4y9Rz0)hcO^~bGk z3#)B&84dn&u`0x#I3t&LFCyF|*lv>hRRV&eEL_hT5n3XHA9$Re zrI@-#-7X3)=n|ACPJqy&lkdQm@uH@fMeWvnr31O`Ntdh6{&Dw9&O@B@O=<-AG=&g7 znAcc`qJ|?TT-Pc%Hb&j~O9OI}-jdNzaIUPUNs zoktI-y)rI|1@zl4D+ns9jf|_|1oq>I>Ph=z+aVUaBAiB$9&n{!Z7_uKl|`4Lz2wAm zK@^m+)4hFh!-H2FtJdKHYAP2U)Ty<1DGR6|;cwehj-*y*<0`Eg&^{3L!*ke@?Y4dD zT)Wt0AppLDeAX&*I#rPawwUvt;O(f~gK*+{Uv76yt31fxEWt z#TsP9btrWxB{ZUvd`VERTzNg)85nY@>9KaQBc`aN-1qeKpJcY)uiP4kFBZ8-E1R&M zQWWwMGbS^trmBA;v-aabh20UCrdkylyBv9aCmXk-*JGb6K&499!0&>1US;r zx+y9Kgb;DC!@9YXTBFbNvD{S|A$oICq@D8>q=Dt4JBmp6sX1T{;<@Thh5rC;FGC)i zk!yq{Oj-zOl#;H>=@_cPhdhgXxhrvOsWP`C7a11^5i?rUS5_2Fbk`s;v9XWAP{Ll! zRcNL=#ZE5oE$WXc4!NhUR5b%EUGP*WB+Yad4kRM%2v=dX;w;AcLS)#EQL{B^g zbzz_L5~LrDrlZ+Iik{)MRE;jpdGj8R?*1_{&ZPN=}}h93r?!DR>&G2P%(tBy4JnG)grc zG&;LZ6PS9oQ;(hBTd@xBSLZ zuLMwcMsLewI&o)>>8_!>q$U)jsZQ!mO-KL}LQhkVqCd8W&k(xg+husY+MSg?>iP(w zebN@A)CHR2IL@avMk6NmDGBgSH;p0bU&9s%??9x{9TWU0@>vd%1;e9Hn;efLx z=`67T9YEs0BS{{CtlWnDSkaw^u(X7rp=P8|q*E+aV__$$c|FagL5^TqOS4^O*xG+_ zb#F{bNFi=GqO}vNyqsSvG)SnFs_GtPFxm49P@7Mlk1geSY4JDHNz0}h?G0=`(%JIR z*U2)r+8SCxX$lEXvn)sQuB4E=K+dFiN}#am=|!@I06x)#c+bRqF6tY82C>^NCPsC9Fq>ksQjLY2!t z+!14IcHLX49sS-n7cVISIkuyPB|(=x)i^R%+AAd}J^-oPk;1|GW%#-(xRiCV`yJV6 zZX0hc(9|@9p^)tfLV9%10pwUx&^Fdfi_nx6y6({J56iV}^fwv{C4NHXxRtx570jVM zw8cx=?aq3Zzo^`dDw$FDKdXLCP%(D(zTdZ0y7_AjJ1x|xW!2>Y_+sZDVX+reKa<>d zWua-dM&jH%lVEJ?w&a>;-loG!?lThFTk~8iCZr;OQ=q0S?BhZzi4`|5&Q7h4e>y@N z0XnEhXSc2kjig1-FEAfD;GH^DLF!Lt2f2?L3Z(qO*?vG)kZG5Mnp5z?ygN^d8S{4+ z2SHi{sqvlj5a|k6=Sd)nXV^w6?(Ep};}jKZb-x}BlGfGVd#2FxUWa>;9Y?EPVOkHc zrFw=j+rMDo%H_hZYq&!SlL zZp&!&?UU-|-WQTB9g6L#8&v?(+_YL$WxJgYadw+t!PHKOu86A!!a za--o^RnB_!3$XX}cdr{fot3$BQ%i<5z<7gDm9S5Q9`u}E?0w~#hb~L1s1J4Rd_36m z!O}+~`6Ge%rxo31TQBYWn~F-Bg*8r5hr#V_V z_%TW)^7Czun31GNXtt$SNh+;+XPzQ>*5$-$KKeVak%L>2Nd&-nk7zTbp6<= z&9QW2gfDCNXAwuk;Ov)tLs-%K56h{8=}f2t@c zkinrM%2ig*U+G*S!3T8e@nPW1E0r?BgDxc7$7Yrp=9T?kpMEmizxhqR$M6}RQiv&0 zO=|kE93?=Ui28x{`jh*JT2d26+%*3HXAETaokHxk(~{fh6clJWW=%1}M}38q{{H|6 zQiiWQ3RACP!1+G|EU^9}*KK7bsaI7IKn9xkWsJq&x+V;1Yvg(+&DW-~v$to441|zp%n&lIz0Heae zPkBKH*bWUqmk{LL2rfgB(CUnuDD?O`VOW+~AHiU#+LA>zMp>`AhYcqie1{C1cHSx! z4YsO(&aNR)sFo$D)7}x%AXt_MR)HCI;j-cxC#c1GF);>R4jMH9&5nP{ZV}Kwm*0f9 za4Lcwft4y-Am!rA1YF=nY?*Jd=*)54flv9P6{O3Ekq#*RuI%Ljq5%8wB;Tzd=Wjsv z#f%NvE{v3rYu$vsCeW5*JFX2YUC}hjO8j)w5Jprd3+?4-inLO+abmveV!u8VX@%Oj zK3Gra<>I9cJF#BTFHwO?AK}6O0M z_e%c&PRv*4qSn^|4|M!dzT8iZYtlg*^kj7?9?y8iZQDAU8&~uL=}cGULZS9138K1l z>xzq*!LIJ85d}y+)#;4xc0F`2v9c0D_MA~lO>vsLw^X>t%G41jTGUAC*^Iv9gGO9p z-HFM#Ke3|6lsk(eF5Fg@HqBdp|9+8W24 z)>Nq!z|zDe%Dw3<_ZY|`%OO29ysB5+FdQ-H&lB%BDP*VYB!$T9RbpP5kcE(+oPD^5 zrW4XN`JT?qPa87v9nlNA_#~dA9I@ME*6nB~t^ChmIDM>rLgQ#Er%CHfu_CcTb!^d2 z)MfVIua#6C7#fW0?8BBgtPHA(BAH@8lta?cD{>?D>BFw3T>k*5a8})Cmi%9|mlb|& zedwbY#%aqn2O^w7S_m#i{LBAxX97_?RfUAa)YlT9RNUbre2-AbF2)K75+nP*%T zX4n+vP-y6*$N*B6t_+N5B&AWn{&1Zg={5VXW-r86X7Csi6GixP)7KhnY|h+`S-fM+cMaecFwjgPA^gY;8Ai~-eB$|_gVs%07 zpZAmgsl-?KgG_2&&`hUmmyKV?7ueqcOL=M}t)QSX>4RZ+{ zQu!6FaApSruOw=|YN3`a*d!%T!^qWd9hEuz@m{_m*FF&qO=Z%Dvlg(M1p#pynqP;Y zIAT>`S42mfS{ZB;@lyxcutj=w5}iWzbjp~rf};hW5P?Fg(-K(XS6xE&q!5=9N5m+8 z+*&_`3ikn)l>W4e_Bg-9;g;?JiqS<@pA7;CsKlK^h3Wn!Bl}2AeVJgW%B0biT>?{) z{{VzXHTYgv@c#gp9dAMmnAM@a>Oi%=4r5r)--XBchu)_kfTo7D8uYF&?Qk;p{xB~r zQOb5>poBm&5E6uu`LQU79kEM#PMXy9!okstd@0uirkEg?com3@v(=Z~hoJFmSPu7c2K&u6t?e|`$J#-cHmIi;gkgwP(R7LipeF2GOHRS9$z zK4(w65r`(OvZAyS1$!{3397fUm}9_%q=Q7Jl=oo{p%w1N)%(ld#HPN?u{Eg*qfrhr4!RbrUfSX8oL*@I!W9il zre#`&1!qN2I@oTY3v$2bCjzj$cLVT4e>oXO0^8E!d_`b^;uvhW5<#wF;zxh<+NM-W zd^N9N#Y*64E9ne}k~D-5IWaBxS)l)WVT&p-fqNDOJ`mli6hKrN*h^EiEcI*_KM&UGu1-d0%w@Kj-VloLQ|;W-Y>Z@X>(0JkxH#J}7X(XTzjwG>LS6 z%XVsHYcqG72KQo}&?4T8k+v2Lpt47(JvhPe5?( zdx`A8Hz4?vLFgQ1#En_|u@WA_SGP~>mm$#kBoDj{Hhcpud`N~Z$NvBnX*B8=L-t~# z>7cj3S+uIL(Uefr+s)h`Go6c-?tw%Hohz&8U3Z)l7$!)f4{!Na8)q zAwwR8PA!_`>)>38ZfJf$JxUZ!den?kV@Wh$-(HeBQ>98#2e&bcYn%#@td;6LE0F%Y zC9{C<&i?@6n`DlfWDj@h3`cGhku-#6rGzPZdSnVuPKWyNK>+87=w8=xAZPQZFa6H| z-Twd}eYNQpcDlduX0rIBNgw>BSeE-BwwRx`kNz_U{cJ^%KS*7kB5l#qtHa9&H9n(X z_V7ag01l*U{K%L&!1;E~om`Tr?UVL4F_YOoH}{si$AFQrbgecc(JFOc(r{)=)PoWs z8|RiUv(R(W@u5Ea7Y~I3Wc*?Tntbu6^P~8TPc8rv;yJkyT6Zr_vEt|d0J&mKyWnK^ zxT4*9f4w5nu!vUScFO$co=Yf6N_@`2LUE7oD{_`fp`*Z>R;Y7})K2HT?p{XUZp(3U z<+G+EFT9{spvr10KpAJ2Hd{5iT1J0zm2MPsajNrgvCsMFtTvK`r3()dtw$heF@1w> zJm!Bc<4|QTzx=~iTg&p@E@)FgFS@Jk#=hLCBxEmJtVmJkitF26-uz!h)h1E6(TFbU z^Oe+R(zWS_kTKV?N&f&eS%#|7^cqvUJVSLZWRW(@*x!2(E)cf>fl0 zs)?u>;ZvZ>x1l<5dwyow-0UG~*CqIk{{YjiF^}J6LwVcQ`HRWRmvn93EV5e}bRo$p zXek{D(w#(O&FxxF>t@?N>y4z5NADh{xyNc0qiyqE^03=m0V%h{+}DN}PMm;B5dM%o zfMIWKwkGS!s#IItuE$ES9f`?l-d9O1@7Z5FcDguS{{S#dTaT(!&StNeaneeZujVJO zTn9XN1?k{lZTTweq@JU*b@7xaQoeH5;+keph(-XAla+Ll+1-a)+$p0c%i6W7Vuu@B z1WWQ1;z$7t0GjpF?8CC|))hiwN7Y7HcOtZgRJ1rvde;?7>kZ^6H!HY>Uv6>H;!$-c z?WeaI7^?kr{7-wnGVu(d^960uQIDcgZvw;5EI zu7>{GHqrP^CYo7BI`AD(-6Z7VW$?#7iuQMZlu_idt7v4_fwn z1{*B8lGs9few?kUOx3d4rO1DaQ76UQ*Bx&p=N2WS)1KXm#Hfmh+EDtETva?-cKdP5 zFp?H|v=!Ll?XhQomV<5ia1x@*Xe&)sufG#VAu@Q_p&#z8pJIt*wn~b^3zA%QG@@!0 z2{rq1J~jHf1LRxfjY{XpR+eK}V>pI_5|dJ)*1GC3V=8hL*4s>u<-B~GQ}v>Bc#mak zjP?6fFEOrAmpIdvc?*fJOy71@)yN8PrBY#qD6slLtrm`-ZZf;h+hY7_E+)@v-Y;XS zt_O2{H+?`hzVvR}w|DEP+uM>}hMRF}KQ8jk6GW+0rG2>aEzh;@T9-66N2_h!!;5;~ zsnMKW^|5V^&p-0=W4=)l%aWzUTT82DFE)oVrc|lNq3n=q!mks}ciAqxrvjH-uUC6& z)?nPR zp0rWwaJFp4E&l13GfZQ>jw;apxoP2ZNDMePwl7g zR(sSC^UKz?C98ujXw*$kf-8(|+sVsTYjN-1<<=A&+{(MZ6!BK|COpAN#N)}U?K2gsF?buMsx}jbpvbQ-}gOsDg+HtkttQO>sy$azA zMvRIIZ&Fk!bD`G2Ib+b{^vg{oV=jF`j2^KH$@<7$+6nVGTGWDjuTpdSJd8yCmAA%Y zfBhvx!Xjz<%WgF5TRbUd3Ox$%GL$RXkYHhv`dX{;1Iy}S{{TrtyDM-=!-pa!CY8oi6OA-Bt!I<{pu-tW1ZsY)bsHsap9 zm9z!^rwO8D_0#6GsI zyvj=4F0&nonilfhI#fUSeR6WDyoZ@NZH6++V(A+!;x3Qz)_71yQ? z!?PYxY@C@?m)PI1)ujBJi?2XRdSc2L=X}c)o8Pb~0fwRPP|Fhhu&XwLjD7uYKoyHn zYhMy^-zp=|6&T<4?Lx6N`*neYc%!48sV!~I-La-x){Cy+kuvIY=?3eNoRw%&60XsaAdCYXE6RGxsFHaQlF@2J zZ|nuhnDeYlZ86kIVU^^hr1Y*Wp?(~s60<6VWO2&UN{oabVpaWkZ(0I$SctAXQc9MD zDcT(K0AW6E4DO7CY1W8v)TUjIkn4ZUq|_1aIGMzhYRF+GWk;05D(%!y+kmGMOH!nX z8e*;ScHGy|dx-mpjIySZx}2LiQ@<7V6L(Q{kxq+#!Me?V^Q|wyM1Xwks#1@ z6eQs~6Y>%{5JnQ_+)yLt3VVr3AJc{CNRO)%2mieyOkVBVBK+@_Ep?>DA=NSJi?(47AK71D_sy(UcDSt?xCBsnqi?Hs3w<~wq( zGJCXAX0v0h8E|%N_anf0u)QHm2Ul-QWHUY^ZHp(%p!Q;ML&3oMk9e+F0#T@ZWl?6= zAud7PPN7*Tqz||p3n+UZA;ukt}GIvt3LdC1;2$Jf06z}45%m?x>HO!j2N7T zl$$(!$q0CV)x@@jWX6#onKhu&a_tx)P^J!Tul^f2dqa3S#}TZN8XyWOCZo2vK2f11 zx{AnA&@;nH0;N?^r$RHsfxJNy2(DDbmlGQzX5-yIvreZ#;UN2lGI`(SHd|l7Yj7b} z=BbfW#MA7+B-eg0?%F~9$O1AgIaBW#K${#5m`YJGB_IM1N_%jlM{eIuZ`S36)n70O z9m4@%;9G6nOE+R(cdYZM&+W!?^3=b(#SaQ6%y{$)LQOr`fvS$UOE!h1GU_Qpq_$9> zW`UjqcF?xn5a!;pyx1FHR+WTsO`)=tNV&HXW2&mpv+l%$Wh##Q(DN;z(>2B#Pq#)m zXw86y2CSs!OlL)Xxnb@MbzKJk03fsf0EF%8=6to~_TmT+v@|!12{z5TMs#RX`f&`E zT|=J{npsJKUbI(F#|S81b7TCX=2;0j;wxt{zqi@fHG~w@1*blpwZS(es5wECUFm>Q zpX{Jif3h$Qo*@D=3Td@)&uc;bbLM z$X68;#%6;a_%Sqd?Z#_!#4d621ISlJQ|!h&Cks;3A1llgH5!3YTmdb$q!zM64;?X- zM?u+&Te#k!%QvWiA-sI%26a+}a=`xpC+sJ`kyw^vja~l$!sUW;OSvCmNX1r7h0f0| z?xUf17z%eSaO&fXz zmhP&a{7Y`mq|vuPsaXoGGr>;6@(5AT`^{H&9IX2eqp)@9n#|WlzHC{w2?!Yr?}(W@gTVoWtBR*nW@m)A{#Kv*e9i!Afc1Y24NAGag?Ltu(lwfAGMLGqy+pwYS{reuOeDzl5sb?GSY zKT{FPKa_j$b-n;>B!qslC_g?P4k7Le{J^GUj-A=yygmgkQmbQ8NIHc_Pl$W5Y{4zI zn4;)Of`Qyka3y^~mt?3m^dA;p>=svoDLi+;h!if+G!nUNR{{UP47@8WyTiQB^4kY}8s8{nOj5gGtIu|405z9cY zKl!-4MsoKMEd#}sdJ6rxI@p}3B`rP_XYmi=faS!9-2nWD`$B{4#Ico50$;*$D1u$x z0L5sN`HctJgDvp|VoXN=08Mlh{$INg&NT|~6W&IjIVAnKg#$68bwE05q78dGVCy_c zFQK15NkaTIr@se-bp$5-cDENwQOf=}QXq&aK5(yyiXUz`;RK;o$vz|5mOjIjs(ftL zq2^KGPw8~boJUirNDx+vC1i90qwK=eS~4Zw=Uv_KGOm+cNoakL@&lr z@TBKnzRV3#P^gw%1v;8jVSNgcpbxl_Lx_6ud5A+q23JgtY6rUu$jPHB z8>PWYsa-ML+6{xN7w@vv?$oz5glbz)kO4VRisGIy{MwdzAL9${iLxWfQds~UJ|aLg zr*&~#xPOp00IvR_-&328*!!dv7-K$yzGAVv?XU3A#`h=r*;)xnCh%y+nq|T zjp$8ih4%L+6qk$JGN7esHH>N0moxkDG^0@4cE>K?&G||H0M$Bw=+gmQ;)(6>WPMt> z;y(>*_u|F93a+25v3vDlJE^AAmy>L^BAwsh&~co^TubZDlqPOF@L3}EY9^S2D~WH3 z;B}+v$NvD=pCotw(8vD(RpZI~=N4|l)w zTl;=WD%JBByUL0G0QZZ-A5HDw;fJ{%3AulkUhyI=JlX|kq~h7dqMw;C<7fW>3XMTR zr=BEz*lS^y?VDT=_)AuQ2_x=a1PL=ds9dE3{{U#n9?diuirNw~sIM@e^9{?go>FW6 z&Lt$AsvnO;ZV8DQgvLOxu&THlBS?+g{{YB3V-cYvx2NBM6!f8|Wc9&t66Mo#T~v*3 zFoqdHBcL@|&l%3)o8?IfgB0f3C*E!eW^~b}s-~2EqYcBh`2yS2j|=1}z_Ci2pRnTE zOG3G=Lh>Ylom#qK<4v!U(5qJ1reQ)!%d(3YhCdq!Vmod!72-n8MN>jM@jBuTJZ+#I zsvM;Wn7_EE0%U)Xx3%yJ+gz01 zT!F-y7%-cwCx%-}ga^7WD!5!GTqH6ac4MkjsXYaIDt*`@5K)Z~Ebeo0A$~}>K}OWO zsrU?}sg)%T>7hwI2skTii+537x?D=jbUFPhL2F2whk4SlgGKQlcPfi#U)}*d3KMIdsKVW*hjTX)w2?FGPzou{P^XUQs2N+ql6} zR1j07kWNCQt#MU5^Q9Sc3sHKaRNQvfBU>i88%k({LI<$-VoMKE*CSZae?Eh^|qJl|?J&NRQNk>(iaZevL8t0XAX>?aAf&X>L6 z+oi5-c5_kJR)D<b&l3ON^hvmB`LfqURa>jLtngMi)yzbPnu{` zPpbYwN<)9TsX%toKhQA0qrmT*VodK;Tgmk6dhE2&<^bzYc02TYXME&@DPE&*Qd&@# zbbM+}5);~cjwD!C{D!>CzGGJ7&J*Rd%SxL2h-}rSa`2o!-ZUcWksn@dQLv@=lOtbt z?09AMt>i29^$K>^F8ob94unenqekn#tw^@SNoix{NdZ+9G#$ectX^#iA)3(t0N5P6 zvaV@V8}q5PzKt!$dBOT_9+{rZWH)`PahEi8HQQeG3yYRo6Z;!p9nv|UYnT@u${6l8 ztf~b?6r507xh`rnX<=eAk1}K@yrs#Va=J&k%3R4zHRZb@YARQO6irE~Bw)&=;6ZW? zy;%o5(hLadY>=|0g(#e=0L0W8)Erw~rrU+MzGZqDF@aL6)Z!WOMBKHD7x+#(ZhQL6 z2*qx8w>&YONKzM0@Stm+2JTkjg(`6*?Um1#s&)SWh9OFUcE3jbzBFi&oM|ianr*!b zQffzqAmxvpa&*1k(YEguQ;oqj@QjB zDMS_N?V-%#=6Lp7;8#a7>Rd{<>a&tL39c=d2F1ChS#CF3QQ3KDQqHK#ubP4sa|Ct9 zleRYdF84JxMtiuoJId87#N~?Rat@G}_cPBC)P~SMh{veLO(V&!&G?!uZzn2UzC1{! zb4C?hBttJmW@ND{F=#CSqNG%j+G$)3ay3B^l^F52-=BcE>u8~p(FHWjNsYeMQn+x!B;5bB*}#?SYf zr3DoG=&rbmSvf1jkz2I&pty!THbri(Dh8qc?Mo^n@x#7i{{Vv>3pS+q33vVL>CbE0 zWKV-_fp(bAQEi9?YI$d%s0yI(G{Y6!w5w-4NT=|VX4cp{Au(d!zh`B)uFW?80LvtR z+abo%y&!@s)|k&_?^n5~nQ@J^!N*iTQMI;eO$D8WAdHfyLPdY)XMz5H``{b;eWT}X zdm4rssZ+f{~CJ5#WY!S??E!MMRfoT=^b*D7HBu}*T-uFkg@EeaN8QAKNW%u>3o zM3R2|Ypwh&q32+M(6zR8bm(v;#Z(uM8ZpPXWd`=W(v6|C{JWK2l%$Xmiu^zGBGWJY zEaqL_Q!18OOTg7tP)>F6V@WrmingfB#kY3~;O))77)a2D;<6HwQ&Hg1b6!(lD(*ou|gLgVV_oLs4?b;pIVT5k?RbTNnr(CfQNS-xe#BUVjpPK;p zVO628BT;q^@@2Xom`aH4C;BkgXqwgPi}860mfS^OPNCMd80Tb8vmVy&Qi{q!^V3B1 z1fS}_j~XMMLt~Y$WRA!{ZQ!xmb)W0Nw{W5mudtoRnwxpI5FRNi87Qi>_hP>8+h$Rt zL72OR*h@xJz4Nl9Zr#OkzOlL6Y0HTUd8ZInLqGro9+=7JZ83xNn*2?{&3c&^%;H_b zRzz#GIY|{2)TH}!t}8BH>F8Km^$0R}yL%uFA~Uq8@V(dY!2bX?njWy`nEwFS8-H_c z_Frb_2(}imq^&9o0KGs$zXVibgK&>JsxP+4${b5Ew{POR%RA z;MLC{pljcNtlMZh<+M&9rX{}r7 z(-s((^&F=}NergF25RiVte)FKfN5v5F4|&Vc5>B$0J@NK{#-K4B9tl$mXu8hrU>O^ zQjG~?WT{QrrlBH)IbZrX$?nRR4Nlho04UyK7OQJ3r3igOpKb!S5XD`+Q*lV9nl|VC zTr%t`6K;MWp^&*Fvq>0?e5Fy>w;L(HLFz}CC~zKM0>fz1+Pvjk+oR>m!DEFvrAbB+>(f_w>YLjb5M^izE=)WTwk_O*)i-eUzne71db=T9cr6VKx3a(_V<~`!OLo zA-vY7CDe5k2i!0mxQ)2Rye4$Xc_3-YB#&XhIc0^1Sn4?#O?6aI8el3CbqvmI2*{^U zvHWn21zKW!fvRJttx_-`T;vSmL5xP4W~jsP~32OgNQBLqM%K?h+#~oUuGFQ zT7^b+1!9Fqwu1oV!LA|Ju6Y``+N)leube8!M&J)&{BRZf6c4d}f%bxu(^XGecH+0B zSVs1ymDcBWll?fU+-xcrn7IMux%{W!jFs$4vi2DO?6{(O_Ka5LViH3mP~_r#fCe+S zF?~hdteT*dM+lb6X^}bogAq#8LnVHti{p34?ekkB;c#d6V?CW}lBtm2Ybhc`Tc3A+ zD*9}si~yA2{{WK(+sCRTP&6(QDNIXl#-o$~it1@x@hh_GKa>ed8Bo{13vSk=i2$DJ zV9t>$GppH!>4`%X_F+0lP#qeG{)|KNTS{3%4klgbKCYN>>MT3fa!4$vy?;pJBlqQD2 z5iS7IVh-R*`I-#;BNJnADla-nYpfGW4}L6&LoOtK+R69V>^Mm;%tTkX2z0GIn6bLF z1-Hc9qx?JBYRf;n5Jgpvxxhq{H9}*vNEv7eWUEdlO$*> zFn*h2pJo&k-M{;Si4-YVq5Cj|(fC4&PLW#I1=N5~yno$M{;>WSn%@E|f)fM4qoYkb zYwW{rkA$iebsgkc@eH}Ds#GaXx&4^2E5vpX4W;6h1m;Qq z0G*B*DAY&jwF0W@>F&o56_noHZDmcya!zOBcTZgNpvTl|{mLIHbfM%pEN)pIxWHu= zrj_P6fKa6=VL+V06%`ncbDLVR*5L&B5}Uh{L>sAxZrfId8*uq*NrV)VkZG2+6JKs2 zvbEK)>A3k7J9(8abKQv2f*w%vP$-1cIsw=*qOl)Ioym2Oh=p_~?WQDWA?>mnc6ROC z5m6m4u0+_$L}d!4!d^(yefS4*PoXLYZM5quoq^|z#>b6jYi`{(x=9_hWz8^w7pD3!6f2 zj^e^mRaHSL{6-jA!+^OR<2UjAvu*ln!;DhO^P*kd= zBlA#jd`%V^}RPB{6*+xN+?dGyI#L; zHGRKTZ6g=lf9B>ZTK6<2Uexx|Va+DKP<_oLcT{6XE@MCQFTYnJS5KsWfr6mS~|$_zqOScX8+79So=>E3tC2 z#%wT&uT)o3l^sBUdntjs5)5067l5m4z1ZM8C2m|)pzAInAhNMVC{SQ$VpXdrj^Lh@ z8EbYMD_X?QH&TX^q=0IAXNfOKa*|`~GQe^Kq>bSD3Iqst>ww%Eis+ByK&+DM`{Fx!hI~Gs$xtRdMttGBPLV}Xs3X5#7dH(>caTT$~(w?o!-xh0i zAB8Z)sy^G)RF@>G`!pQ&I@kx;5TWQHS4rvN*3vD<)|(2vQLE-AIAsLBu8rF6y3UO6bh(SYlf*JK4()Gi>rUK z?e!&LpwWFLXSS;bm>$&EGYpl+q9P*vf_x8!D0HF3{XiAOlPp&aTS-b%wI}CFa_`VE z1?!32FD$jnI<~i%y~ht#LJWCSH11(oi!$#5dniuZAE|gskHW$nQU3s%a8<^+K`f`u zsMwtAG3K`G#OXrnNbB)e33~74F>U_+u9QwQkr5Uc=SzrC2dxR8O^ru zPHi&dHs3O-L$5sLKVjaO+AP=^pB5~Z#9Nq?9EmZsYJJn*59fkwbRt4s^=xE zo$q2i^w8Q+w_{BLh(MqkT>xc{wX-;?CQAE(H|mJv+x*k9uaOk#Vr7OvRSOc3mDfM! zXpw;L?Ke}?P>63fUmXOQcgsZSUQ#cy62jD06s3@IHSHK0<`w>41j{bNd`PR9-iXf9 zs%Nn1mhf#XA=D{Gy|fkV!P^WjE1IAk?ZMjgvXkz6GqN@{J$G;?hh zokruef`U9`05i`?!*XR*ItJ-NOu0owr9o=cDch*wBJ>v6a5b!?d4vP`yG93_q(Uuf z^es;chAkU?`;@W^alU2dU0DP50zy-n@5h^O@^ZP#k3!g&CAyHV<+`qX=~CQqg{ev^ zp{UH0*8*9Jf}uU>)>S^?jjge_p8J}vNxqu~+qFeos8Ui(lBCoGw3-7zb0p%gb-i*i z^l>aUyLRP2UjpBqwdtZ2Qx(NaLw$>7<&}No8OJ=wsill?yigl|@B$;1VgiXpbfFQ} z2jNUFE^RoGf3aQz(!;+v&6};V-M3@81^3nv*(p#;%{xMgIEmq1O0#6p%(ut>i{|%+kUVvOG1)rvzkQ zl%++$3mVK|n`0J6_LIJEaad_fX2{#LWvfC*Nm8dtw8Dr|2e2{jc;L!ew(aOtk9sA=}$-9|4e3cCwb zM1SIptHx@uRi0fl7+$M8l>{At(xafR=*a8(zB(FvhC8UixlmE)tf6TF=G6$HuCPsh z%yPKJ{l`E&PLb zxLzg;q@8X}HCv?8r=}|MEO`gw=1OjieLHnxHKmx={_p!cvB6Xunrl?AvXsnX?pQIq z9$BiQs?K!nP09|8wL5#%+9N$KTb!U#B4UA%@pWLjpqf=eg985m>R4-x5n%hLH~nmP zFV!<{fD_4$uf1kczlAY3{-1;Xk$?8A-}SL7>*^D4-=Mb-adKp7aanjg#AUp-r4FP} zC{$!=iR|{yEEN)&XU-AC{kNm_o=PP+h8E9?S2tMqvlh`q7!02x|4brSn z-xt{2i)f8!uv{f>XQUGJc?wBGNzj^usbB&PaZ?-EShJ$iMuB$b@s(xGLAWmxroA23 zB)r4ojHec5%mqw)~5w~I^coglG+6c zBP{#7urp{;xpTY9w>aBTpHy=3NOi=}CX5MVN?lES%=G_=#DYe9% zHb){oC02Au+cq&8b5AM%0PvnPtvZ50)U|ssvAaHfST-fN$}dAUE=JrkEjFu)t#){~ z7A_99r8RD2O4&<=DN2HY)lt_IW!ZIFor@(#=m7r!Gx98Xa|{_UqN%wKAq_QehMFhr zMi$;SoJcNYI+7*Jk~cD-H}lUfV7WBGeLiN8g((0XAPl{f#e2l8iwR$(YE_NRxVIxh z_VX7fRr2yvXz43K`s+u2T}PHyINzMcHB3SKo_gdDID5sMx%6j>_AYNw!Jp zxJa37UWNH=pdhGcl8MY=v-J|kAwX_lx!Y~rt#d6A?bBWPrk2|)bhebYs8gj*Ljy+J zjOum#{&@10O?z3F*0;{!X%4umhCHRyPjzcdSwj0|G_BB|AYBsNSS^wrOm@(XS?M|j zF{Q`2qXcJi;`zmtUP{OsT1l_riT+`EgGMc|+HUG3g$BY;xBgsm$R?ed10wE}XQ{Fk z(=+{e<$69MCC9HTI9` zICspzSzBnrHwJcOML3ox%)x&(d zG9+op;_bw^(_3x$GErulw%%dn2N2>Elo3S(P;v9#>Gex!wRy%@6;bpC+w`w>)~!^PHE?T5jnR}A)9%7w| zJw`H>RzW79doi|+h}?xwKb6^An^i9d3_`MBc7#i%;VYdgNhNBlo*d{pP-jeMv(#W~ zw%%#MHcOMBZTnrSYAu%X+p>TTCc4E!xfLQVy)B-F6&|cvj+~D?^m8hwa9TT54tk zn^Z0P;bo;!ep<8lV$*1aWkSCu?G;Iq-2vxiHy^=MT}4NyYyxx!m*ijyB)lM}OcG4P6&6-rHO*vZ7{#;FQQ4v9k@tA<8)U8X@l1L||YwW;*2z1z5 zY~LUhsIHDA41mbu43B`d!uW}@(zT&CP?1kD!L0j+C@zOR3MN+lc|0CCL3t9iEw_ zH${D;9B|QtQ69xif_`Bq?5;TB7m$O0v+c4)2@coXRa^@qTtYkE!nS>~2j-|9*$fCA z)kbLxOfz226WicxvzpgEkOp-X_tau0!fo&;Yt%X|BzB}_ z?idi1ZU*$)F7*_yn)c!wj3(8y8z;kXQhN9fc#tHK77D&+Ei|rP+F)Wkf)Y`tYl@{c zHk`f30A1i8U|EhlM*s@AflW>fRGM|f&3F z!}^pafOQzGplZBmomqu_n2vC$U8{kRNsgTQbihv0XBh-_>2o1klZwo{HAL9AokK}; zyCd2$OLGkt%KHqk>b28Wc%aI|4z-dQp6-^AYt#?36ss1JscmN@DGkr@tY_gQ0rz4H z*ug(ASe`RK;O=$&u7UpmM;T4i9*m>iCsv6yYU`hevlQ^NLaHb)t1h|Vh^iuL8dp|z z>)VIHHpg%^Q96%{2Pk_EOan@*@%|XxZGTGJtWy zNKi@vVZr>skHFz@;rWZ+2`PGt6+968rn#kV0Zz&+jC&3YUnou4n>FwqUk)q^i;dJ1 z{nc<7e>{n$4i*$vx(tk=v)uT%{1TeJqJQGL;(5bf~)02~#HwM+`@8L3%P z)G0tQc2_A?g8U^u+&`w-(vaZYL|)Nu?0$ zbnn3|Nj(Is{{a2t{efH?jqsHBz9FrpRp~*CFj!C29DD@Y*%ki)O;{Gv{J@L<00`Gp zWD-x`izza?F+k}b;U7N3m$+g(Z&R*t5f}deb&c&#e~u-yDgs;+0IhI!K$L1HsXj74 z93N;4BsXvPo%RF$7_mi6<58%gSrP~K)%;E;p8#~V4zP5Tl=dIH9T`;N!*TNb#`PTZ z3VqmmI3q9r0EGa5>v1ij2}-O=O+Rjt-%*97WIpoHPaQFDLY46gIq{r13W9U#R$NTVu*u3z_4{;v82T6W=K1Fp+an?B*S?{Z(3xQ|DdAnJMA)DnNwy9f291)~AwQet{+aGwe> zu0WKEl#@a}{4s74@j2rlR#a^+F(HXTGGnbPWz;C@O44c+2<|wJ?r%J3O==vx16NJ; zdAi(FI_-CMY}C+v!>+Av8VtNeSgoWYs!thCwGBDBWXTP zug0^Vdvxr^w=^e|oeCifTVuCKa8o>Gx6E6)Hrlr;GfT8@7IEe3jZ1+m*FUET5tkMI zC@!?(NO~%wGDT@lw8l2iZ|B9;Tr^kvmkMv-Ik(8%mu0Atvi6@?@wi-zxLA^pr(Ja#|@$&2y)D#LG5z zBcme7$5{+ID5(7Ia&gnXL(NB}-8tC>)M7xLbs;!8)0(!)=9$(Fr>u^=3xx zu(f&HQ776~`cpC~)R3ecs7OCnZ{A16}4f4%Wn9BRH|t9 zd|1p!b;w#a&5GNx>)<#c?H)TyS#iCs24ibTAfZI%_8dF)YtO`VHk%K?P`KLo`GAD0 zZCl_1pYI%2kUgTfY&Sd4#ERzEkK~1Td{wrttu*^ktqY9k8ea2&NC&j%Oh-NTb^SyE_$V?Zp;pJ=|lDk@R0#h3#E zyqN{Z9t7RS)~*;}xf^-yMDBR<7M%_)`T_@ z+!sjJ#}^g%0xi+ol3Pn`w19*Rt6YX6$GJOkq1O}~s<;nLt8lhPg$a`(xTU1fM=GgE z=mmY)hH-7xmUeH&Di)7{xw_B9+n(WmyD;tA#{5SbooZks&UMO#G^8nH0Ylf>ifzwo z>(M7l1zFCGuW0Y}3F=#8^EVh>q1kxVG53U75tp}T%w?rWiW_w*Q;AzZkU$uhZP{I! z>nfmJ?bDt*Bb)yKGV`)uapcj}ElPXBQff&jENm^fsG0Z7-vLequJv&IdDSCcDCVZi*n^#T!_rZ+;$_5jRC}Ts%zLW3(m5d zt3q_UL*W+Y%dEEKfA1SEroJT_978-oo`m^{P#{%6W1i<} z62ik~mpQFkHyp&xBox1^P|OiQKnTkfx3@TW1jW!AF7L51GUF;+;i*#tXlupc2}%B% z;A@$j!sBH|6C4wF3u}ajEjMNpBqpV5HPe-PcAO-0V_ib>FFD83%I9+2x5g$sVcsV< zEo&smL+-ZSNv%KxQw6)0r5*-)cdtTRNq!#ncH zHZvTSq1HDYqC9}>Pk57>Ql|-_>(d<2U96;s0(mW3Qp%EtQ`8WmPv3#5Af=Xx9@Wjf zOx>a_r;v7ZsViy1AzY$5=Yc9UB}qvgJMre-*R=0$PI%K)UIyd0x6QBM$cDaS-*xV9 zP0X`zYQV&~PD^08o1L~o-b*W|!s;I*I-Kc@?`>yy9BxwjT&2$M`X$SCwCKwE?;zam z$y9jPH=BZml}BWFLC+-+M_hW%Th#LPlO-w(wk~wtHu()d68%RIH0yNs|AE#-D0)=3>gaKvM-tAb_b&WI^u5#FJJ)Y+O^msCucoTqKz` zWPq;=EECr%G?9!I+ikl0O~junp&D}j$Kjj))V|;OYh&4NmTOE`WL+4RMglQ-WEm@ROQyqDY<%3 zIu){XI-hovjdt7JxVM!XqbM_bV(qO~rDD0-q(n!PGCPnZv}m?nX-$##E{7VZD{@s$ z6b{YBqeMx%meO8nr&m&((mDV^%dR;nt^n=HIx&m7V$4?=UL`!gH7cJSw$w&R$f}hX zW;Fi*+#vj`OXazW)Uc$fC|dP)gYU+XP9{oz0Vf+$$RO7~{IK}RJ_IADX1b`?=n`CW z%S#1n1oRY1@563Ma2%smQBpjAn_I5wsc}U@gS}P6Pe_Xoh)@>q8d4Hf-aBy(?|>rT z#8^3zVOhA!#|P%LNVp+x%xD^^AS*8Jxns<8Wn1j@Of^d=g6=HGQ&dB>QjUM>;jPeY zVOCkrFyei-*4DIKuVgiGzm>fmW|T`k6Iyrm#!n|IPUA~~vBbfOf^UfTZ)VvQK09$7 z)d}LiE%Q@Qj=F1x{KVQO`9=^R zcv<8@f6I{-ZTX26)8Z7NJu8R1&T*m8!-Qs;Bi*m;d2-op)VSeobVv56a1E;xsBG@W zg$LxRCPYDDW>HHDaJeidz9j`#hwZ`kEYPdW$e?3{HtKF$mNa)sEj6B+cGua7GSg70 z7TX0W)S7)!?Sl)8C6-<-rYyK+Ezl5rUAPl%zgg;8?zrhd=bR;^Ak<~sjYVpV&nGF; zzfN1-8=GauwyIB`a@&dwNJ%~*-G5B?^vbx)=EcTFlWMxBp&N2X25znC&8=8=mAn-1 zG#OtyzYKOfq=M;jUE-CX>s(l3?mU(&;(&8@o@|QvF=v;!h7S@yvu)PfllKvJhT`VE zA-^yDLr(ciEs75XPF2PJ$C~fPtjc!|8nGqqAu0B`Z#U*P%2X3gN(Z!b7`;Y!rWIxs zvjXN&#ZHuJO+`nDVMwUL&|ykRnxN2e#W$oj2g6rE%(&IBKwJ0SUq; zEf_)8)Vo>`(`i$o)e=T#hMh6T1uktX%wb7v5Ls*=vm=D4tqIo_OKPYfg`f}zv{w!x z=Md&6Ojf%|R-x{uIg$+}l2)VMP8A9nL!aT*ofBOB_^ip|8;pHyg>$&vQ~O=6Ma5`3 zR9RuyKq~1{+DIR6GdZ`R7fCuAObZg1!m3RSY@O`vM8}b}ZYpfjnqxjxJ97afj8e}w z!v5r3EG&=ov8}jTl?K~%h`W2%cNIoGBgP+$wI1*D-hLct`J0rugoRC}I* z3tU#D++wpKdLyZ5^PCy!MQU>QVhpHO+e?iLEvq6h$c&}IG+Y($%U!k5cE}Ry;P}m&b)PrAEk7l%*7!m6B9znIzY@ z9-$n1MI+^Wi|*vypNMU+vXkIB{kW5)MNs1`Jhw#}C#Tujhn%>Rw&2*iPSdm-yZ-=~ z*=p$AbiDj!I)!Q^RHP@UIIN6h&sB|dij65(=oJ5Mx96eyCQNYCGit*R1K7UGr` z=a}PYSqCwymBfU7Ko2pL>Cq7cdr3cu#E#g#K<%up_f;AsL?!1ksz*v^fG&DM>@H?0 zy>5_J1Jy~T7CVo-75@M+Le1hyv{kVxyWMnA{N>uOxLbTP^ziu%&t=-LMr0(fM zY>6do$`A74T3ZNOWUIs0iBhSHi|}cf{2QK^?hkE=Y&&C&pD#+0a9tKN28uQN@jh1- z?mH9Ve(^R`=*`Z#Jg9Azxgo@b5>0*C;n3g-b^6H;?Rau6ve~6fG#_n(y@AjNDUOag-6kerc{Px#eImWssd=R*c3HC#}ZFSwldsk%L~2t46$;yGV@Kyq-fq!$}zyQ|A=6tvoj8G%e2DAZ-Ug*M-jAG&Va`&~K#Bp`hoCF1 zxeZ0yRK}H;0Sch_s&na%ZiwY>qt@H9?vDfkZ*}!LQ$UuKQ{w4T1u!znjd+B7u-LpJ z@n*V9wY;SVVJM`wl17ky*t55mk|?sUxk&3{y355A{b*d&D*$Kv+*jQZWHGH zqk0lH6D}R4RQIb9JdbJJDK7(mpPsI(UR^rly57^1en4h?^rVk5RXw@lS3Nj`b4$n% z_Zg`pQk1UIk;E$fd`V*|F9RsJ%u<1(lm}%FB~$j2#lH~Rr^s0pX+b^uP9Cy-q^ba>3Tz39XM=CRt z(KFDF*!qv{+eXvc7KmF?YmV71u%W!;>OcxqXvkoE3djsEcw zwtZvT9f+o#Xi7@@kVi((*s@`4MWc7cxcQr%x12&E$%93ZBdH;33M8cFp3FsK{=aT9 zr6j6>ZhcVN<-!z2g4+C_ zI`LwX@xI2I(!5Q8(nh}At3!%hjhZR&H2B-}+S5@IPgDJP<5jRU3`hoNPye>c9Vn~hiwrAr+b;Kd( zl_h6NR6r)5ZY;0lWWiRZd##G}YeK>8?ls{_^90Kl<8hWwl&<21DaN%cIt^6BxIMv_ z32>%?{0IATl$zKGMb8AC;zxr*3zOSQqo}ATntWN;6w$_#EakdIu(W4=8DlqE>S$fW$DF9FB~5uv zDIq~9C0+HyLI-hBZb}>Iq(`kihYAcQ-z6*dYXgoJP}5p=_1=O;hf} zkpVQY+FslC$5Wq>-lh8i7$FU)z-3K9g}3C)l-ywAq^P#y(uGi{(9uL-VQQZPUDF!V zezDlui@Pn+9)P9%(p`{%tt-q>3gCHCqpdi_i8pPdW!yHqBxd*JsfR5=UUJmwA$7`t z0ojI7kzPy@jvll~W65ultq5=$lC`WS?8_1KhpWj8jc^ywSsbxM-?nF-ec=02i|a~K zDVQdo(}0{QI+DjWhAjS`Z`OJ2$s3~2Ex79iNNQjNfPb_xEaI9BvAC}yMZ9tR_g(Xs zbGM-ZI!Xk#R%`d*3E9v#IZ+||{jFA(ZkDyY%kox`KxxD+G$xg*Ngs|XE>D(4ovW%y zM%nP+X8PL>Cqs@ltq482W22E4s!F3&NMS9d$?00gwzUNc_h*3K?P%|o5k#14l7|xV zS61kwQPdm_J;DtITU^4U4#hf_T^VGd{)Yp6)Cqb)DeuNxBg=FvN|UOY^fkntNU~)S zG5d^kx)hLB6p}I(?XD4Ai2k9X?m=;gN_0?Wlh-`4-01G0LOlAmXxOMdeVC4RYIVEg zmfvt`O{5X0rhjGyxo&~Ba=nA9O~Nnjv6xDRy=181NkV}iF+gZHIE}WXS28-*xMoF{ zB43yi_Z-ObT8i6Ql#*57-G*7j<>EPAs~FNopfrkREB0bY*`YnlrdW1__F}gt(P52n z1#;Qc;zFuPt}17bsbLGzpaP_zCV=YiHN;b@2(@$&OGnG40MPznfaeN@b?OpZ-A#J_ zYz5H>jzW(R!%qbam#Vm|zh$SuF4k&%9Ye8QqLcW8^;&ON-P)X;JJLsfHaw zJR0(Xtu z4K1>4(^*+0`Ni7wrc}kI-`jJK#Ey;ho}wV$lN-z;JJk4yBx2HCwH80-VhR3t6$^GE zH2!3z&+8Z){{U4(esDGZcDLvbILeXi3JSw+c7@4sWUDSA)b9&El&{St3mpIkp8O3i z(7wD*`KX^Odu_Htl=S}qD=LsuYnQtWeWj6I5!1?o6r*nawDJSVyCE%+P*s*2{{X4w zC%}W|AubIIEbdLV;xr4>1xlzwe8Eykr7+m+84!|faAXsmBvKjimWO1iNjW(3lAhXm zs>h+=Me>flc0AeznORd~vV#`(;ZSSW10Ee8+QIS1mx?Q!)CtVcLBX7&xdO9O!ICNL?u ziL7NHRD=Me^~Cno0UH^K)^(_$`>BH?DuW6(<$$L(8X63+;(nqBSrt_m{>~QNvc7EI z5`reOvfuz$Gl{O_Xh+mTe&*<0nF>mVjb|3NR)t;1#76T5oizJ#JqSACbfBf?jWP$W z2nKZ%uWTt!>-?(#1Ma~%*E9mVYs79XvXD)F%o>;{CZH2P?hXBzb-#>`{{X@_ywjM| zTH?c*g2q$@Nu)^!*nfr~iqv(+Bq<-@V<;TIvbUIjA>yyuLl zcT*G8Z30tVCA1=$ci}2BQK%y5E!0u3LOs4JVQM#~B&liAeZ6qxszJqsE(;$imth!u z8$!*{Ai+LZS4>Kd6IK1>kxrmf?5-Z3hYc%`d=pfVJMzI6t^nQOGAB@%YT`jcPDFv8 z2)B@kG!mhfTeeO_i-S!UO0-pJG!(?rx~Op4Tb8yoh29%dhr|VYaEmEKjoS&q->^{H z5k273Q-JbjB}pkY>C>Ucx%Z|=i##$oHlMgxx9&(W?oG^bX%V5FNPHbZd3JlS+4tqm z5U|>9x5ZFmUamGfjmhU@%ukx75|u81D5&b}_u|h7za*U`3z@RolF^Kq7nt%V{@CeI zl=I$OXhlftN|W7-td94$7<0B(SlR17R80`J%l7p=meM4);wa+No`qB4rYYg|HpIB2 zLH_`#NJ^1tEoyYTp#r68BcK`LSM?6V_(*^ChPaJim9{CkJq$@jY8{W$QRz|a#wvaLob;ijj>n3%3$t?D z*Gr=9?V&8Y8B}!&8c71ZGwFk&-dTLQx&!=ITlY?dUgmRoheaxZ14 zI&?nVO8%f`vK8C7ic~_(+g{d%YE;7jM?`dMbf}7yjH~|uCISBd-!mmXT7|ur77DZy zwvOT1_cI}O=U9mABn1%QLd`SnHN&zy&o&EDxY!tS;4Iv?`kyV^EEg#Xl$}(ogfm*u z_>LQo-!045hu_$ymxdZw28Omu(v_b(p*ZGudri%w76u$o;(cRZP{Cu8zadL zv46NOW!~fSG94f-$5m62#`gz0cA-RPFXKY&y-ZlLSvJ@10p(oUSIQrOx+qk8Ylq$6 zXC%`bkGDlisg8T5^|HoLjHNDMXbm}!gk*Z0RLeHJ3kPM#h`(>-F&5CNmYc#@X(vjf zq=hXfx|F~&+~S=WsZYU?_2+#{mZIgtQl_fE-6)>Z-HD^SLGhp~#JO7PNE;`WrCXrA zZAe1mNCcQ_RW$f%?aLh;%TkqCWO~t5sjIh_ZU}Z#5}z$(_ytHGc*U)|_MZW*T^i3! zY`9AES3z2uXNy~R<3g_ba+O#Y%=l}o!`+Ac!V-uL7X-Gj1A|{~M?4Gj5DHOZqHhI& z4x*0rW-$%xid+tbP^Q-GZaQdR0=;})$J>UhkqM})3y>klrnMC;WtM#~1n+CaCJ#a7 z50c?5B?)mrkSdZpaNfVPa$}m!$#U)2Shomow4^XXmfL|UN{4785#--ox&~Tml^z%F zPPg3cF3q*=2ypJ+Z<#9QLdskfsTz6?`r@J8y7(})b`+~Z1pa+x`yy;nB0C9gAR=sb zoY2#R(yAZmElcFwEei#;oP@Ehb}iN6mIC* zN>eq1A<;k1>p0_@zaXgjQ1o8`R5on*_Zh44XiPYFR4|Y0R1SP|}1p2_b5Fo)cc- zitCrXbV2d4>C}bX-B)BuDSk3RLCDn9XN!dhm9uR!R^y%E)V;&D;mXhh*Gq75#}%O{ zK$h82Qjb6eWZ^HykZ88rCt044dGFRYA901^(#4_V@1+({9!^Ct=KzB97gXIQVBvKrxspD1yb+i=lkN^JLKi#$!S!d0jx z?4-tD6#D$*&Ku$_lf`$fm2|sZwv-m2BH;|i`BgUFNhu3hAYd%5i>Y$8A8T}QRG}Zt z_0_gw?x(kejk|L(BxIXxd5LYr_k1-;%fALgw_vz37rZ%fL^sU7sJ3q-#kmJ|{mb^v zK5e0ggfk{Ig$&djLPChIVu={O!nHeYs$2oD<;HQRDMjuZlVRK*Y1!$%CB)ay(1_(V z_E)Yhp}F0mO0aFYRiR3D;o}RQ);m&5x7KY>ND`h}T8-v;i4+th5llU9w-ou85Q$;i zaOHdm(AfArYXkRf#>f>4QH>dN!3(%@Jb>24jYCD*_*t^qV{u?uizbW^2fE@`qiq83y6-J2fZ5Z-m zB#Va=A0R{CO(m@(&5-rBV|7r-oa;bNC6@mHkku$Y-qVbOSKI#p8sBAGQr7&n+oIZI zp{2H4mJ;zv&15VZDB_k^=I6~#O9r7zAcN7k&O_1OM*g#TLV-vEKuUnmFjLnN_2(t3 zCX7({bCLGEc4Ng?BwNtLRC!U_YCMJMXbMnJ3<(=iPQ1KJSm#ktO+2TxBKa+~$e}_u zqRDOqiYHK^p*ZfE@&|dI(@5O8w4I|dJzp^X-u8ca7>ng!kiR&*!pr2nk154C z=J9>ioa&o#8TIWW2^$k$2w3E%_=)dwHql$^TYH*v!m1RZ5hyCJw48LYr%F(fUyXQ; z$Bz+CtJ7V)N_9k)7Q-kNIri5K=}l-yc2J)Y!L&CI6>Q6zZdT}5mz)Wvg}lf)`vAn3 zvE0=eE+#~6JqITJoM_j;m>Z#9*#qEeD?*J+XM=^(O6L+>_Y(#=~+VNo5OdKq{b}0A@Sz zE=`3rO^K4NI+D%je%Lo#u$=z@J-ug{2&~7I6%H`sb&4F)aSSY|!<4bPxT!j^UwH02 zPb|G)k-4|a4ZF1B3w#F}VOpJSDhe(*E1Jy(P7K1oD<29dw<{3Hb(0#yW^7cmIVj|2 z0bLZ(RA+LhQP-{y{bu)&D%tVr#7VX6t+E-O(BH8Mosv|hT}enu_Se50yN2)jNB)Cj z{XzH}7iq?r?3HiW0hKVe{DrjOCB4DCAAtYxeTl;D&?ft6UV!8pP3x{I`@MTmFa(+nS$;5gs31-vkbCK+qIrl|v($c0RwDdP4I#ENHO!FAd%e1lO{l(}0 z%aptbHqrh4WilLA)Mmc_Q=bek~|+lF+A#kV!Cy-3TT!0zlwdJ-Gf>uY7T z$PT_;`hCS_VE9N7*#(-*4y(dsTlqUts z;$u`&9njZsx*={syW4}Q1eO#)SA=RKEV^QTWGJf=6~Ws}7Zp0%rO8^-sI7uRTS3#R zp!WA*SxGlgk{-J4J>4p8#n<02CsMviv?awRhuNM8Qi=tf8xbsgV}4HJw%pZhTB7Q# z^d*N)QY%wkJ#iH05_@UWQca(-Z(_deW!ossxp{r?h5~ znHZ_uZ>d^*oJ$?H)h?$l1^0i_X3MxD8_ezP%F4rO`KIGr9FT_>)kJCpjClm{H;A^Y zqu_fzoZX@cd5ul$>AA}dsw;edW4}n1sb*wrwVCM6Au1VYa3iTbYl~ji#LBgK%OD54 zcbrU&Wg8XK}dW(ls^Zt>+y=3vPlE zPF3(>W049FyEW8wUna)TzS^;+>2~T?6w*R{c52hB&#D% zm>UM%RJN*IwK4P^e&K<|SsG{{RYWf-UA-^8;#r0s|Hs<-y0LPmbcG zzM{9vBE3Q1r0LHQ&o0t@Nu8;_C~`Z;v(mWoVp``tKnkmGkW;FeR0JFeEZc9yCdNFA z6S42R)*NST`<3m(3f%Gn5dF5xsl*@(Q6bpmW;;#-xhs6Z(Wx8nErAl@x^5G8{@%Yy zv`YZHKt#V%6ee6PGCpm`3VddWZj`SOn|VYAO*{{W{%S$B=YaRj#ZJ>^L%(hA}_kb6IpxP|4{#3nefNG&sE#%-l7J0|ZP zDAEgt3+hQEW-1ae-aDnqEQMa^g^PEzak~w@Tsf$g(rUKNrcy^rjPxG3Uz~noPYU0K03z*p4>IG5VtSI;3eerA zStNFk?#*a$PtJQm{{T&itV>qiYT%gbw_e|l6px!qERxjbf_z0kr$bCS?r*ewcJzl^ z`)h2f{;PeO*?n@-jXEd2Vv>gxRoUf)la>N^=fn?mhwg5B%T8feQ;v1O{%py2Z@d2N&$BkLWJ}>dcwY?hx6P!*`Uw3?s^E`-F5hy&{7p zD=OeQs()}yZIRy~Y<>5+ZxU~Iy^|`>8bvfi`RMK>5GZp~iKYs*aV|0=zlSMsR3O{0 z7Vlc6(QWdGCs$#KX;R;8d6fhjr?#gZWfF7LHs>V#i&A5%BEe}T zL1&<*eQ`g4$9W}ECBJet#`3hXr*yCs6rUE7O)1v{UUgcP897dVC5_^KR(Ee(e{PtT z-(o-tj+HAQujZ}-kEU7&Vr6=0$qqj1a_=%q5BN2X_7$27FK&w(6_aT6$-Xd-c$8u#=`iEe+>=IxKSEd7v0Nzl*C4* zL`XhjVzR<@Ad&!1uFO|G)_y2_BL>N@i}(J^zd@J0w@&f7Sge-^zwmvYl&Wl?geR(< z1u3G9dJJasyQXlTDv5oKwp)yOtHiV$A}0xMq8gFB)fsORuhn%7y`a>Jg^tVyyOv%t z3FVD=gg187wruOn0chN?_bsmDt#Ix!oC+;y{H_X`C=45CbcM*emX;!n?HjC{g~^L^ zPx;I##TWD}DOsw6A=3@BViz?ND>6}97Bp=%DYn#0&et+jv)2Z#zAOXvo2!VR-rP}I zbt&*broCq&-G_da0l%!wEpXYMdu%cDZw6SYVX{F2rnsB*s1yFNA-_9kwQq+eRGq?$ zWF`};RLM$hrkO~lWlE-4>FqH{gY~&oq+F}(IyHr<+aHdut=-FMEW5lu7AiWHQgb~r zQOgw<;}|+FBcA6wnib<~^C~YAn-}TsPPrVDCHK^%Q>}9(3{&5cceh_nQIfRX>1Sba z{JG7%pnhgmpC^XgCuhPNeimb3xuWc*{zhxU}mF%!HL< zz1(4z?8c+J{Nmh(PnR7IysB2ERX>gx?uSbnI#4}en(V2r#`#vOC8r5MR%szUwZ%qb z^o3(Wj6|Kk6Srlw>(SI@LrF<(E68O`$i)TaCmIg9dBu!bT#a{i$#1ggL6KD@zc(>a zT6Mrtz~=E{Pbs0dXXKgD*CD&I2~`i2=vf0ZS{h)NwXZTedGf6XJ-3s!!%IR+*EFu8 ziaIn`q4wY#yJf`mscj6!;!1W-K-$kGmhTr=?eD)tp~SVVq${+jpKdDhE#<@29a1c? z@7j7cP7xw)3xeUrk8?8QNJvvs0Zx^4twM7cto?H-aYgmKEO~f=Ois#VJrT>ZExeU@ z&|IU5p`q*31Iu{J{?bQ_ijdE_ZtgO-Jk)b=c1DAx*O?lVL6v(eo(!{-nO2Azu{;IK)#qrzij~5Bb0*Lz!9uFNH0;ip8s6!a z-5S*9@%$MZO5~lo!|DG3!(@f0C8Vqa*S8ljzm(Rb+}Qph8GN0$o(eJHx>TQru8P;+ z_F{OfmU4K=3MVRUhTHPy+Z-z?Kd6^MqPz3J_obKMMy^?JN{LsFMX|_i3rligOG)_D zu%s5#)L+{ymnyK=OK0{2X!{-ivhgV!^HV+w>jfYCQ>^gfzPP;F=PXZ$eBwuP~H zd=2MgTsGT8hY>Lp^A<;d_GN%2gL1VEx0f1fCBM5ON1F|9T*4v^Mf#-m123}r^;MVJY&<U_~o?CLNzzS~K9QOkIB4Z-AV zWP~NgW<^_#s1<&?G9BGUcx?M}XeafN$i4IyLgPAxrOebr&8ma!IpQenl81#wp5sv4 zJHcuN)mI^r2EW4+ew8DZcqrb*WhpJXQ`%BBdpoc_&6)TGLp=t6&upVgOU^c-`4oeJ zzf6%g&ZZ7I35>#|4`xRR`fP{%+VcZ@dqa6BA2y1f%7OfF^@Aio=RlFU;}jBSH>C6) zU>W`RGTsD%{&k4*ck4FgEE(uhO)8%>clTEkLu!+f2fdp_Ks%?HzFOw%YMh|T-0+=b z1f-tNW;IxQC1PhXx2G9#9ou)68(e4GRm~3sq!bT5J|jxdRN=Qasz@UrcJ%5v;BqVT z{s$6**{ejap-+e&xQaVMe$$@rR4vQTT)Tee3q_r)*UH$flf0?SAtAQijsYYRpNgc` zz4+JL?W|~FRWDZ)2Xnac7H1zxaF@6zhFVi=Qd@B;I*&t@Pi8Z=HtSV2cQ>2PH9~r~ zP01#|GC}ql3?B>|bl_G!=I}Ax*9C;@mPtJZLk|5u*O2*cm*0qCoyBqdveW?XIA4;t zu;qnqco03t^YH^SWV$tc+V!WhxOdCJ5-ak0)EDO5G}R?% zyw?yrQSu)0UyC&P*K+~Icao(QAH3ov8nKb)<504nQK|E5?5V_^Ar;95zGU_&gIb=r zlaS>mVRDER753+e)v^i6pcf^qq^7yxo|wr_qC@f)XcDit630M0w*mr7fmN+~{x~fI zN^B6xU1_L_9E~uvBUgfw9d+6bULMh0Pj7(@=Gvg0`k(1cNDAV@5)%1np-ChT;zkO$ zqal@=jprDAd^Hv6?7_WB0lPlXG)Hk-s+A-GnAJ+*I+7#ln>e;xjpU)SG zp(SB}Yix1|UcQM1zY6<$^m%EiG2i!=csbZ2*;%(=T=$ zvtwNjm59(|yS!|S7g|h!pCIN4>OGj7<|NjDUX=ACy~E1Z6fQzwGT?b2f}!E5rbJX> z*6vAxU%wT=fNp!<_cW}tJ|$HCI8>4onSq;| z<5s1q!}sCU6Pbt(++On+k?MQ#$5d=>>8!(u-K7%Tb`GEe%JC^DybjDskvGKg>k2PV z0zr)YSXQ?hYI?3KQ9q`-rkbgic&+L=%~ZL!i*7M(5YFPhq+GnFn|dOns;Ejx(ho}3 zojY-$ilQhc{bPe??gv}Au;jT&`6SV0f0!sAzPPH-l&2n2!x5;umLAQ^5hFV)1Y{{q zNF5w$BflG4IdHoj3|>|o?L9?^T#VdkaG8C_Bo_caVOFp* z9he^O3!5dil9EH()5wk#1;$)Imk8EOw~S8eu1a%C$q|nd;(7 zvG?t=`2aw=-jw5VS_!IhIgIH{W-nvP7kSr#p|g;u4pJbs*#7{_`<*Yfb9?1pcrB;Z ztHgyGl+(W!IeWBj*rND~ynU_4?R*O+tF4YhNfKT$US%|9y%$`%r=~HpbBn2`hb<^Y zQ?~o+xF_XhsdJ<#t}>S9ru5c;4Dzl$GqtoeE36w{duc?U!5r+_rK&Ob*r8>LuVU^vOmmyWe zem2o|+u}SP+w*TBm)O6dQuGx|NdwAiqNh;@rZX4&^;&f0sx2q6tDI*>CC9qi_C)j` zUt(?>q`)Om2Df3jW1S-({9ei7kFZdc&xID z_6;f?&dgF|W$p@ej;Or1mxQhB#D%Z?#bko#%kBFtSDZ_MEjp@IARNi5&bZNI|^KdNOjb()dG7+pn2}+a#LPkjfzR`f*-?&+Iw<7x&xp8M2 zP6(Cm*H2D2h{^u|E5@As`IST*VGv*3v09{u$2ZyzUX zjdHxhYgo9GY+z%mz{>|h;Y>9 zMIAum!rmrKb?0oY{dU})94J`Lr^eQvTe3#G?2YFh4Amo4t*bpqW?N&w!gEjdsf_k_ zbl$_AE=9HGTjStb$zGR2FXTO^a^m~DHhZn+70ObO9ZI0eOq3)mqEw{h73{^%*}AN_ zb1eWeu=*09#zyUtq@6?q+^krtph(K2=GSw)Gi_$!COX@K^61OtWl%BJ?!s=B3F*!% zz6NIRnRF4=b6OZrPQeV8&hZK{x6%cl{oBX_V|3TA0ax(=ff!x__5R$RzWfY!WR zC0y2@DepbyWZHteJFv+|lvu&BuJyL*LFY1{tcX%gMMt|4^${mUj=iyPzgrwaQRf!P z@gp?@{Bc>A7|X!2$GIrF6Lr6L%XPFiV0miDDN1N*?=>eBuN|%d%O!tsq~o^P>QBxk zgpxB-0YDwO;&{7>XvZtId_(r@m5R$O8&oDAeZV}&9R9nYJXgh}Lg(MT>P4?FfjA8q@{O#_Dyl>N--S z z-A2@{y|j?EI}?1i^9Uc^I?B?j_f%IGHn&U8JOO8iZnR7!ZA`e2n6=JodId>wD$*(q zDMeEg%W<1pqm_txhixrhWloI+X<8eQw4}P)3dpJKpv5+R@n+m=+D!!6#FfWHnPu%8 zBF!xR%G{|k-%0}II{PXOvh6h0G!(~?ZvOyLep>Z-qj9%(!ntqABXjiMbek$Wu(ty! zT%`hrv?_@d$e(@!kJp@7M_7_x`gwX%(TnjtIL)%K^t;5^_m#1y!jb3B{(&?%}cAw<=JK zr|jFxkY079qg@J4sws5#595t~H*nff^*v1W?W%HO3>#A5&MA~M3XoAZBw56s})+v&8k^lpssl;yRVvCTaY*42E*9 zj3L>T1{UJpJ8VjM3rw2EB`$(ef+%|DgJU_q0Mc<>MEhpu*b^Ns&b!Tq5(bf>D?mQ% z@h4L1;B;NFZR9|^#d$V6vS-FM1>y=8l=l#6Lx#sLQQ}PPL&1@)+6Lf^-}rPT@S4zt zCZJ&ZIni%Y4uHawGjN2c2#QhZ@d{wyn1LTK+j>UmLi{j_tKLWT;z`{a;Xyt7zU6CB zRjqVAppWRomEt+A0w(i$RebAQg$}?$r{RU`5U9(*0B#vSwPHoH4(?UcC<#yl*(6Z$KTV$zNs$ zpjR^l{pD_oH*H$0)CSl6Sn3PR!P7Qkz8f>uJx`XQ{{Y70gf|i{D#I9d#=VuMzD!s%OW2WpGpb@o>fPCA7`8VW7# z+p3hBwwB7Wp^~2OegnHY6GF>uer?`0+?$DMjcsi%xWA2M;82e0kz8ZDgS_#!_}1eu zFgt$kL!=3Nd zQ;fLVgn`-sAmY~(>IMb(MJg28J*$~-!$Kxqtq@&#iR)Y!nNdTCSSTq_$c*S~jix?S zE~>@Ur@4&nI!i-WEzFe%BVL1UqjtrA5i;1pfkd7`8DDDEV6 z8Y+K=AnKucn4Y_~=^;VHu z0506>Y12c5e++j<3|_pwrct|Y(fniQKVm)KS*=)$x}R{nC>$^pHOMC&}CL7w0Z{hF|ASM-7nY zBH!FkdZsDQ`t`uyCu_)0oVn&DG0oe`HlTOK;FTJhraJREljCgRRvbJL-!(rLEk*SU zyyNLA-d9f#O2>q+;?6tdqviWaer`gleNAq{POaHM1soQ}7CqUT0K@MH+tn{rQT2<5D#w53o zF%{b$C=KsY$}}T!-PvDgHmfp$`{UvL8KQ1%Zy`PZ08)~i9$R~oRXd!r+JA_Tf+;aM zvAlu@)HTTE`}UxFS7x=J!^p%Omj$jjk&(}r7fnXtZLjS9%WEU>XA;3&BIU*owdx;o zNB;o1Zl)O|W>aOhr2Y(Go_nKbYQ;DQ73v3ex(e66id1P(n^nCaeazu{vmMg2c`ZDK>q-X z7b1?-5fwoH0M2CLva@aY9ruTm>c7+~K?&t4!&g#Rgnx?|U~yWXg|`O<~E z$kHC6s4I(aAbY<<4p5Jflzw1q^*PROtMxevmWO24WI=goxw#H;z`|-vg9rYB|4?!u2nITvVaZ zrR0y`k%_ui{EnxzP)Dc^SR@m_?Gse-q+p#0Eo!z*Dn@WR~vprpD*nMbC`KPQi`W< z3%s2p!RB0Kxu@N!c!Q-6Bd7%5D)KI4{LgYIKM7R%HLZKK;CE&XQi9Xk40%hCR-Y}+ z%W+$Etv-4|75i0)eo`Joqc`Fk9Jk0NC{OvOSxpRHE+b zxUQX_G~&OB!t|l!B^jUismLr4WA3YiC0&2SxDWBibYbK-tv>)!$~*O0jmbQ_{{Uzt zI1k~7I&ty~=eQSNB=5%79sd9@+(UyuGtYBTJ@u|R+mDdi%oF}9@)=j0cSX@k?IuhA z0N0M?kC43A0tb@!>WxO^x+ET{`MCrC0FK9W1gV2RJk4% z4up8J!Ygs|BkRaZnYniqm4~Fyp7V+X6}c((9=+;T5!;86%OYN2vG13X0$-OULWY4- z9_=6NVR<3sbI6gS^5i(!O%}afrN!2@_v4f!4Saa;>mL>8&j>oUfOu%*h_g z;z_8Zo#~L>?);MBTT3ZMuY{2A0H+ElF6-M2dTHLzidL zPm6iBSlzQdab>?bg_SK$>5&~k1UQg7iudC<^5YjnUy8J-&)oZg=Y;14P{aC%mMN!E z>>kWmLP@O(%-fGz5ew3$uBeatt5E?c?aMqehI&(#McW?epB?zkI<-2K5`x`LQVvxE z*<38_Md~`t-AgrKzJ##PpCv$$2v7tb{>)d(_aij6rkc=MR`HhMODT2FB}xQ4tdeMH z--5BP)dxBuGzd`-Ac>{LAb~FgrR}&D>H4!TT390_qOjG4; z>bwgKOy*7&?agybHw(qy@?(6Od@iBnhXP7!p;VmCSz?mzBwVPAbtaWr8a>W8x8_W@ zv2}Ge*VLdt#4k`!WqN0WarV_7AlEkrn6YB!?aj8NGbYDQBnMSLI^w3v^BKWo!WITtH@af&8wvG?VWbeY@=EW5H#d31m`b($v$nXu zFG2YRfDI475&o_(MnoOWzN&c-m70kka^2Zt*Bk;YP*SF1smz9@f=O4hy)hm};VG>G zEn}stD?;wFC#%hhpr;09p;KS86c=ky+d zi3`@M(0Hl%VZ9|0lagSjYs{4etSKGG{#+BvL}<+U4JfS+DI|9E{uq;^QF_e=o4!u! z>#9-w;8aLM8+B6wu9JZ!t-_))b}Iz4&h`CXCo^6BMd=fTzWt1-WQ3T@9vJQ&LB6 zXAty)y38NusoVIRFGxjsnOpq`QTxs$pjS`}&uT->$8Z-s(4YY(oiIfaA5nzews|Q3 z01qR|Pz0wvLm{1Oi7iDoPUk{4m2{X^Rpysds)nEh<$}5?gL5#N?s5tV4K+jBQ(R5* zkt6FmiHe-16@v;oiTH+s0sF9=u?^;7HzS9`OZg-<=Moz!TdA!NW|A=1FSW{{U#&#Z~OvFm-$j3s{np(0IkzzNlq}7iXU`;KRNz zGDW5_NduwOLO$$g^1I(@vsF3-+u3p@z|P{^9;o(BoBYeVzhQ@F)DRaoF>S)5*pxE0 z6Vt9H*th+=a+NHgUC!0)cySxH{lUvudt&abZLPm{hLAv#)3Vr61Nlak<4JdI#)Hh=_E#5*~>hk zA^SeryV$O&Etexpof>N@9Y9fTgbH@k7n^3^M@rQcIoq;&&`Gy%8~iwFOWSJGEVodi zviok5M_TAOwwG+$ldF+i{{XXGilT^SKOtRQL!fMEjU@zgPJOMqNG><{N~o zI&3k#xv}6W4K&T!B~K+O6rx>dP~>_XWOpr&7G=)(vrV_}Gn!FFgPl6rG&Z(^VugyW zNi{$x?Zq@Ma+cRpk+`zW`EeijX_rK>j3Qu)(_J!HYf0%383$@7s6+P5K}Mx6g0- z@nW{?-{}SYR>%5{(|WMElXu99C!q7&AH?B*PQCt~?89hBE+Pzx3Iqh2Q>eia zl|k-^ufDpU)XTRL0JbB@QKfZPy;v%M&9ON|s1)?CZX$;ec_5NjrWRMLG4S_cIa@;; zMjx6LRjoj(p6X&4*>wqwlS4{u_kztTP;q4&S_57SF>rJ`g{5UQIrd_BDwXk_2SP8B zRD|erN>i0d$YY}hiTUU-_df2BL$Dc;e6{}odZeo&dq!9{9>%o>zkR(C6iS%;l~$wd zB=!68Yi`zsRI0|zsyIn>(3~9@(27y0o$_f~3YuO>uB7(&;u?+Vh|jjVod&e;@5J$y zNTF;fhMy@qH0$lI9)X$>ohL?))bGBd$KGAvBVcBW)5b+{)=sFUUl{kdVhyzfKD zYNBQ~@d&E4RTTz>RUmut&y>_#d2J2-!!BjQGxBVKDp^o2Hlst^Q(O~f>rR7mCCwc; zk@P#P#um2Z$qcv+6g3b%*s+!@S}P%XaePT{Ywmw7gBm-3H1MepS20|>F`3*jwJ)~( zqDr>z9l<3?j)W8?1eB<#BomCrMiMkOGTnTI`^CZgy}eN{Fe5mmCXGJgl1M(nJ8=dy zCuV%eCnoZ{8afqka=hh&aaV<=NIq2;tPx(!dOp`%(5(zk;U#KqR4pF$b+{JYx3;GU z&Y>zK|qC}JUQg5iw(xRkV8NC#30O?cB;Ldpq{wGZ?-O6m0b`)tQBwXO^JN)TV8R z_;o6d6?Lb0%Ld~1^}2e{CEfPyz7edxTHWz3EBQD6Xp#oC(4{A|3gYJ1Z)2rO71wtg z)vZSL!;-EuraLm_d36g-Ly^)+O?sS$LX^dYy^_MD6_mB-i|YDn%~UKD+D~6G(XxPfI+}n zS0iCmKE=MPq9toz8Yb==cKN(uSS`yUh(gNN0&^p@1B~8pd&(L{6)$mnLNirzC3u`w z#frB6hU{|ZK0>J#>NY4wRZdi`aY_FGyye&6FTeGSWWFKMbl4ll;ER2(%kt}bIB&_i zIPw`$(M*>@bpkr#%GZ6y9~#t#eYyUZR3q;WF}XK&^QLU}C%)53c0@~5#RNLwNUDlS zt#Zr^Y&MSVvy-K0XZH<;xK<}**r+mP?Z&Kb6pek4#& z5ykKc8-(Q)Erp>8SHV=(RQqzok+~MCm^+T6mu+rq{jTnrOTd++muy!+-`IKn0|OmR)IxHrytzR-yU3 zf_CUc60SaTiz-S+mjR2;#ZfMmvwPD01%$2`JvKu^v^stYO7L6+>&CP zUEZeF619}ms5#da_iZJpTryv&~HllGCBJw#lF1vn?;yjy+VQwhZ zrHTq-DQK#LRO!S>cK2>$QYrx=r8!f(4!3oYtBZh{w>NVZ&CeOPRIl2ntu5Umq?6_{ zrBIZC+AE6f+Um7o+?8Jlp|1VuDyFR z81d{+tk}`W=}l2riRcsTE0eX)7pYk;R^97j-4OPhwi&Ue&2=e4JcX!Gni7?u6p`(& zJxcF*ZSNbXrl~_4`X*d%@^Z7Kd~vr%)7fy_mnElUFbk3%kQ3&mXWLH9e3#wb%YQd1 zxIanl`$peu9?DW$ieK>*G?&_8EIa|CoKBHncW!vc^l!S=)U#Pumw`n<f0i|3itcz zi)5=pwpZE{-QI5o{{Xxs_N<&4YVt=r>~n2KFPo*8WyHF(2@NQr33#1uFSrEP2|KZh z2(sMJ<(*;Nwt&*^S0qJk+?$c3QV-vOE#Fe$5j$=oKbLm6EiA;nFzdY%lp?ySJ(w@5 zklvM8Z0)Vz8aaAQ=9oz|NmE^L*yJa`lIAO1Kt;QBsZ~|l7*F#QjK7Ww>%@O#PUT^%!{)OBtn84cIQP ztR;JmG8U>00Yqkg>^eD-Vq09s;=-`;X;;FPHz(&jJMT2)im7$hapeD5l47!(39j+Ttds+5t*TKd&Ld_Q__d- zz_)ic5e#~;^}B1^C%fhT>f=F?T6c={#&3DsDu9i8RJc!tGn&3lIMiNN~n+rx4h!=TQ$f%8v(bwsqsOZ&7+F>P9!+{ zhAS}Nc$K*+f)gw)UFAJ~++6Lw-?(kk^tQlrc6@BL>f%9rhth?&&RN=f05k7j=53XiFYEtV=XwOtHaiTSkqvAU)%3V zFEXO%Vr4~Ch+gBJ(%Pl76||QksHJT#wMj|O?#H0o?wo1TkBOYSv|Wc3B{#CSp{Z;` zbwjFr+8P1dTv&dsY)_Js0!Gi?YH%UgQH4zsQteOOiT?mmwgmCzQ(2oMcCzRyfQ2Pv zClb8B8e%`yEd>0$fZW;}r7We@Tf!1LXIv1sTSt7Xf{WVs{{SpHV|-lzQciyCN&1D6 zK3+!Xux=2L4Y^JO+(E}J!q7*{$PP`O;>tBiv@E4r6$B6H#J@Ff!I5;Ikd&KDH4;|^ zCa;!US*Ui6#udymh zviF>K&QdxH(uawn_O*rDc{`Pb0xAJkpW}hvTWDUK0DsB0)fE(9qb{G6yc3_l3|TDh zov?ARBfbZfagOCW>6Cy;_g5UVRRrk4h7raNNHuNLDOmtMX}|v43-j?vV#kB=4F3SZ zYon&9sMYY0@=yN&9fRxh4RX^}$|>z$r%);X080dZrvgvXW99_D6nV0b zGUV6_u1b)K<%s_PSGVNF{VG0TW$^_k*v;q8a2kO%DV*Zf4!N(fy!bh8OPLc6d^pEMmAF9j9ll0Z* zSsn!1JP)?_RkLvKE9JH$mdDz8g-LZK0Je2hikjEkik#e=wO3y#Ju#xg{HUcM zTiml{RQ~`c+`Auyuk~iIfB5Vl^N}LyBSh@$$NvCwpZ)r0`W1xv$cxei{{SgoKbAND z08F$00NZ1}aw2q*ulYvkx>a$L{zEwiy#x?3g=>EJ*)f1%_zbnH?`iVB2-Vc-|E71v3d~{+uFi_Ds&_tYrtV0`48ztIL_7`6LE#@SDFaKPbigR zz&M?)5gsRR5YtcXll(BBG{xvda_yal?b1^-VYwP?2&$xtg(p!Wlmv|N1m@Y+t;Vz@ zF|Zt3l$2Vb3UnoIbpHUGfoU<_2%G-^kR^jvK({1&R^a~t<8VzRCqgER*iy(kVOw(l z0M{${5s3PX<&P5sVNIn3OJLK~`KF)sanZR^?+hKSi*QngmMw&&W|H=w;+!+AC&~k_ zVcilx?w0beW5Cz`ju`S$%MEz~v9oT0dVg$!qMqkR^sEu#i^CvupK3%p}=+#Y0paVbQhuq%b0oJi2F?sExi0BHC z;{!k>sMP-e3<>`Lu%;HU?W7kwa?;~Ucur|+Z~Mkjg%uC84*hKnwSjH;iY!>{>4uqp zL+fF`3#-tO0YAe6&wk}}L#MY}kBJEK6`?l}sCJ;>Z6vI^wQ5Z%&{L#8krvdKMjn2^d*6H5yppP;9+ z07mP9ac#x%4BOp8WVQQ~fVP9F1ZUGc4LteRL|H}<8oQ%$zNG;GX+j(Tk=mNqsP68? zy|{D3QllpQC!nowi63~waiq*lC&~a^k>E;HocvwbqkD|;uKA++);+w7e$l0M+w&}I zQ%%8D#go!-s*-zhrZpIQt*eTy(4o)T=Wbq=R4VP84c~O!5f0TEW?B+TqagWeAP&N; z3%Kt#tw)VQcFnGHdDa92vr%~|IgGPNlxRa(uE0lBv zW@-==w$wo#2Sb)3B{1Fb;^+mETsapzX^8r0m#QtMVw9wmuf#J67#eY?O}0q8ZQJYX zjBUwU$utB~p5DDNS&?yYEb(I69a8U(+9oNv3fAIC4kDnWS3Jn-4gs7hiw?7h;BS$f zc_!ByW3XV;Ta7fblrDoh3iUXz`9;lE6&=o0q8o1RV!23l2wQq&`pDdCFT}T4~SlzLCxp|dBCx?5N zo4`o6?n1Pd+fk~r_)-ZT^NTrJjJPmmeWA&3aNS)~Yjq61nt6U=$f&Mf>DpnbSVhi;qZO~NwX7gLcNkTW>0^3sg_Nj`KxqKizPVz%FE$ocWaxvfU^dGc$G({NuKh>uF~WBJfhi?d$%tAI ztiRnhD5d-I9^81RcYSW#uq5r<{L7w5ytTJMHD3~7yusqW5wTA<&ec8D3=|V4ddM z>XU7C>HA1`kz~4=b7M6bO~QUkkk>%Af{vu>uYGdG=GA7X$8Q&0W1D#A7URDLVs@uO>l~U;ooxKdwPuUqTU(Sb+}}>F@^IC^y8I+Bwxa(4 zxrG(NHnu9EdV0N5D3gPK_D}vOZ~m_jHXupjm~2&FzP^B(^hh{z?mGVfY6F*T#dxu! zYTI_3{OLCPPU(HA5Q>uVS_(q6ohL~oS3Dgp(ot1#PwoJ*wcDJiq}SdUx$OAXsaCsm z{nG2M-Q+hJms<)U3UyjRRaMfsC2kVw^C%)Ww82l6&5~lhJ1~$dyrr#v@9Q|Bec3Af z2(9dyX3a3i9?Dycf z=GcGQR@~a}ukK(9+dJ%Z+(|z45&TXW^ENBMkM!J+^#D0xf>(s^d6Dki&G; zrs_>A+6cvt6|76IL|5()D%^EuEpPIL>fXiDJa{G~p{<~`EGU91It)7|IFOYnE+wqF z!l{O~<<|XmcIO)R0jMuPSV9!i)TF4=r>GdN&9!bn0A@Vq{w^bh$d4M0L;YL;iRBuE z3>ULLXdcWH6+{O(i87HPvV226%q<9cM*O78pdePA4`IY!Cu)$2@R6#DNHpkkt`DaG zXtvmh0ArW>P0M{7`O)h!`k;+TQdD}=yAWBIBdOxI z6Oi6#Z+ydUIKcafWkwn{cim{Rxq8r{MG~W^d$9t`gYX&h_3q+?CC?>xFBQ)g{y>e%0fas7oZL$|r}j--z{M06d(nNVQNsC9UQ zVr08|AjBswx2dUHQxMmbL6FLqr%HQB0|MN$3s9p7wJVn;R7)1!(>C25FjFBj(4LA> z)t5tz)7#@-ZUw*2%ACFf7kb0E%#ORWI?|feOtwx*r*<_PPI^>}MGkfoYngE%$M-X^ zWCEG1X=1cLn;P|tZuC(>8?&^v!e)|JOSg4LaUnOD+)gszRfgGdUTC7+3ZSh$n3ZA8aUpjQ*c*}LB*jD% z@ftX-UAUQIuNDv0enM=Uoy0j-5v3z4156bgOU!Dz5M9>hr2@AoM@pO((TL7w7)8$L z;L_TLp281Y57I&pTLNw^XmrjIij*|~s;JkmOjl)DkBSS&QcsC68@}dz#Eor9FSSfbvUHt%(KyAM)xo}ak z%WuaTR9BCkUV~DQMl*ILD{sHhx`+{XWs_>lP_^5gxZj&riC0&W)5=geSM?G{ zPW)Q))mH{#rkq_L6iB-lce?^wSdl8Ql$tur zzo?26+^(2O!9;zl?WzuIn=;&a*|FGLu^c5xc?l{-6lw)P#IVh_l^Yg#mpG&qVkN>! z9#l7+D#Tk(>_x5ERM7k&k$YRvEdrI z62or2CrT2Az~gHSo1^~#@hLLxwv?qwZMfwvyj?#IYv=|rUB}%MQ2zjJ>Sm*6?f578 z+91ay>^ol2ooI7uPF2@#-4&s79B~EcizH<3lhBUZU`>O%?p(L|(dB5Hc-bvmYo#it zN*2FNmo3Axt~c|!lWm-zEZ8&zy&6)FR4rhFlkUY%XVjZ^Z#XEngYkPdS8eliB*-_D z_`{I6deaK=xgLok&pAm$a#?)pL$oJCkgV&MUYOKu9n)iQbb_NrF6FRY!(OLakVxd& z)bf}{ja^6-Jv;HORvAd7J~+DfFMDrGZrqT!o^8cyl3Gbyh-e(nRi-k1*Sjn%XEnsW z*)aZQ_bcFEo!jYR+i`~;UckQjS2EaB(Qa!2wVGC;X%$U7jyy+e`nxrC#`JVO7ca1_ zO4V)4QBQ=piUz_nw+|sM4bJ-oN=#T$$|N*U4^T*^e$k3P_xgotd)rKsb zu5l;ne4%G}rY)Wq%R3^7M0|n@x(ZU6H;c=vKYXHPsQg-j?SlPkFoh_fsKi^CE@Sut~02;E3Q#D^x}E zs$2rmuA6;R8JOcvp~#BVh{$n39RC0??WPC0Zb*_T0`Cp(C&ppaPFyCVim96TeSo?nxPk09Y`U~b z9oiF(3dPF1u13qfLYo71J8_9IYE$;^l%cH&Ia3~su9};|#-SL#p<++9 zHx^EmEeB7RmXc3OfrD>$*GhG0ipKTMsp32Kemr zG0}%(-McrTFWxO1Z+dNo*!dkTKP&@BRzPuw59UEV-ME)#?ktVA?>(=DmYKU%yt{n2 zN-es*C-MNe$o~K$nA@h)rAtGfFxxI69mFpqkD$AM>mC*BXBw*SAoTQ~nL**?x1wj-w#Mbx?Xl+PLZC*D;4_X{`ra>ssmNq)% zBF<%7id-?>swcKdpk zdG|CRkKQLGOGA>?sV1ZhzS?6(tS=&!GDg_jvOGD>qGXkMRj#!k4Nram%CM>vi+K~} z+C{$MXi`~i3PXUMY0EQ;nZuRpT2`p9>5BYRzbEF36Xy`vra?8!JQ*4RC@`e7KAEk% z9!lL?nY!2}ia%#eNYYUo2)0cS9-A7>lp+vQt~N^Fr4$cLfzK2YoLtLAZb~B=(|^`v zDq4{pWR*rBj)s|xSXz{6pd{fsh2-535Q8F6Z9q{7r(9Rxm0LkjVuszHY*obpZ>hwC zRgcPOdSe&dw%n{UN3&h!xJfrn>X>a7?awFT0om)P!Y=u$s(cHoH6OF*T?sYPm0?x_G{&-a^ia-?Ev>|a#kO}3s%w!~7LwFF)p zb!kg(JfWFSXvR|~XPZ@Ofy;5DrAGztkp;?ROL(5mmgkbBgp`s8v%3|RwrRuEsu>N= ztbEuW*+_CxMTx7_r<*@LHi@d#MtOIf4U?UpWv3anLDtqi$4N&~g>m8?ow}27ehkm@ zh0)NZ#A{j{VP>_VN=;MUjfTbbS2oK^?x?NZyI&sitQ{FyH-{H;F4kpN1Tve2zBC>8%}FUzUPqi}_Wk^ISE?B=8}bzmGUm4(qMbpts+8@OJSDb&r%*yqS6jRhY{GjCoN8$)lCK)PTp7ZvCGBpD3H^hZhgZR zA27jaFT_e=SzMDLOI(ztLB?04f=D#$i6ecds|n>IpP1Zk%0oq_T!|sXq$zErk*I-| z4ZW19sRgtqZ_VLp*;g@Zdv8U$t+c1+RHTHer8Pfx9cuiVA(NvSjq=vZe1R)tw1N4S z+J-~FrU|z_YAwr%mF)I?owy*!x<;_4ah+^<_g-67cj0JTb^YSIEGyqw3| ziuW2Zj`H&ZkGnQH0)w_|i^W|N`;)qV91$2Qi1M0TNpE6UIG2&6s@mI^3k~N zOqGguQ`>b0GOE%Rz3)BqkdaTw2h+ydDFzu%kCJRN%?@h$oUIUxl-2+ zNkaBLrR^q*D`2lLykn)QG&3~NwL2De+1)lqu7_n=(iEx`Hy8?g{kTO6hTFZM^={L% z^^Y@TTfM0aAM3-qM87b_Mt07|nEFFoHs0EZsG~pDa6c!sUzlT4kzeDkKeVDujj0A@ z(p^Qm*p`wCh#9F_)Fd1qViX3*%c3gm-cGtW?x?#y&k>W+u?;Gx+guwMBvstp<=_-g zC(muf{MOmEyw~SKV`x2nhZ6MT1<0gZ zSiGkik68OJY`OjAt)}bKLt=vkt5GHvo8!w0)BYX{RP3v1IAq|F$YgRFY#e!DZsz5- z9J!L6@}R?Upi0OUDgwO+ZUf~^j`iMDS98pFF-0}Et+7f?bcm@V3&|fKFPwgCv3w!O zi{2L%Tdq+c#`4liQq*K6Yfu>q=aw4bA0opWDYru0=wu-XLQ;g%sYplweb|C&7V(Wt zDWv7#)_YDO2+-TfZ$Hxq*-Q#80?))PvSlrlw#hWsMFo4az&TlPgli{4SlME0axGGv z^24ZeQKb`4=*Dt=#b&gqMVoRD3RD98&*Qc3Ai z(*e&h&*3R^bE9oj#ADm}KI=ctl8H!)!+FPBilVbU2`4_7itH8cB}{ial^ENTeO|Xa zp%Y{V;zlGP)RI&|>C@ed92`hmL|3xsF`DM^n*@mMOL6yG6lujM)DLN{Eh`xUyR+~U z*Sz;6V3nrgr6tlq2DHySt6X)v5bz^;7en!R6LVW#N@KVrd7uZ<5)XvtNw01q%dojL z;t`FPax&C(-MLmoh;6})ml%wsgEKI7s+i7vw5w9e?&B#c5d7WuCJV2sB(*E&29=fcyYztZY7eRFlaOexCU53u*+=^ZMk!Y za**5yUaN8l^Oj9g-3>Jn?8Rn%_hvO*QDub~+?J)BKA9|=uWcshV&qm8Jf~74t&oN) z24E_q7^^=~F59`wJ8p)${{U%OxfJ6xK)bJsZ~T1HCC$7+xU_}naWP)=7K7MIa-cZe zZ5#Y^qg1G6Hw~g#(&JP%M&fnNErk=NfDk+Jck;FH0**>Ty0;XTSW{ssC%g6^IQc6q@l_X0%_g<046246B$%nVr+Mt zOAdsZd_4wq>s${gRF*TL@Uum20pHvMh5oHnX>6YS2;}QRcvy_JHdfZ$k(!Ti{f=6# z61A9@KOygzdr!nr=A9%C+8nSu$mCUg16JJM0DDD~a9`)ex7n@5)wsPLa=Z;TYf7zW zATSPQ{f}lG&5~K!?oLXGw$ME+BlbAgXE{dJi^q_+Cs@3f&X*FMURoTf3sSo>raaAk zQH#mxI8k|;<=K?A%%xZ4SIM^u}F(OrHe`D7Z?rg+GA ze{bbw*yXi8N9JgDZPLxFm6TIOx1YbbBI#+jUEA6};AAmCf64xVe;Q{D%}zP|z%L8JS_RN@nSDKxX2zBsp*LmXeauWquys zr#x6gv@WFODON*mcMMZWEe7=6k+b(z&Lo|?eQLm$+e^22h%1zqWGO_fczO)-7_-LT z@8Z~{$hOeT%qv%}Rt|}d_PDQVL7f~# zdryJ~{SW^Dk_3SlZpSDR_nQnz?Px}QUA;ldxF0idxlc@L5o2w$7p%$N*~Q0j!d%U- zx#DgF#%(W5VU>Arp`(>l+Vkz~#!n-;=W1NCg$)g@rqfkYsFQKqE!d?YiEliRK%!n{ zZ50{;m3_D~8+cb^RtmC(CT@bpSRIpFB%D3*lgB{6_ zP{oxU#YbLeHF!lc#Vy|6%eMajD&wJJ9o`aElt6@jGQ2qv5$-c~i;4s08kX3Q*e5E7 z5Jp4Yj+SQWj;iC#kX$k`<)tV%;&DZ>64P*hatqa^upEx;7K@%}9YT_G8H{GPe*V80 z@^Uz~O&mz^T7_>Lj?W50;5QxC*3|=X)Q^`b10Z}j7G0L?fy=F_ogLU24 z0!o8US=mEOpYOBt$n83Yc(VTh)MmX&k@E&+&Vl7cM2A?A#DGLLl%ieIeW#%qp2j9s zr&zb4HN8;WO}$8-DHXug738=_P)B&jpki(jcGou$%T^zNLTL=5f-8rpQpRH!7B9uB z)S6b>6Jj*sZJ?IcgjH44JUx4Ruzk#wHZX0_88*4n0v>5C29ii9)&i(=IUGc?IF1Vr+rh|irf2*r5d-Q4#(SWNz#S4;y+4T+mx+V+r^R?wp-Fx zmu~YCw&N72gI3c`OHk~fraT|K{Y5r{#AYy)&9FWT50kf~_?jay7=2XLX7& zUB_jsjg_9;E!*Xzd%$73$c)1`i%kjCkjc{MN$v2Q@p*ocxS2_XE_-r7yLRsF;ZPx3 z_UWHCQxtLaxScL6Csw3W;h@FRjS)ucM({n~By^uIf^h#b!?4pS(X0Hfj2vgP?Ebq~z;C%_FK+(EBk{`g2aDs@x+$(_5vt zmXxL=t{Qk$C~+LIWKHw13QU^0Evm5HZ3K9ZnsxW#*mfLcR!5P2P83CYpSCv=E|cDg z{uUzKDvq$Ag%EQcF|JwGl?-h5%c-`Q0I_bBPP5688B&;yEr(oQw2}~q1yz~s0=OmQ zT6Wq)?BZQhPEMt_PU?x39XeBxQ6Q6y4EE`BnkANjzkfrShbl<{Cs6CxrWROIL;h+Y zhU`~Fhd~FR1Xu9G{$4~~b>?AL7c$c4zdRApVLfCgJ1#*wmOMR4sOf@ZYM`^Zs8w^y zzcvVj0p&20vh!6F_TqJ95#AHslu13F%u21WSAD)LPgB8vmcGmrZp#O`bkoc~$SH|> zOyCEX&?oK71$0;~L{J(xaj7R#8ThG|YOW>a2U65Y-Z0pmYm&>9TWU$vp1414jV=Q2 zO0WU5sy)8L4mkh-*wBIQz?oXj`@(Tppf(^ ziB6E!Pi|n~$jX3njYK(?n92l!mqT1jZIMdY+kVd(c_is4tuZ`fPUfMSfKeU^sf1*-E7- zm4BYJACpEy`Lu-nrkHBNg>^g=<}MAH8;0Q)vLz)gDL)Q{3VU)Gvi&|XM||5$+GvgN z#H8q!u;?EOj(Or2wI!j^&(Ib==!O77Kz#~1B||T8!7T@Wk@}On+ zU@N$&DP)T5&zjitQty(X`FkpHUh(a|B>w<2F=8gI`67B>a z0N7?v%v);e^tTW$6BP7SW>8L9L``en9$U3!wK70ayw%dRvH+qkOAXmu^fZ0>8d!qT*^ z1Il))I#Q4_9@=8#2Nuo9(W09#CiG>a5?kb3(<~HAQ;xPrjk0nXdvRd)J9W^C-t%*V z8aF%5zV4vnf?~Q2N=Y5Kvc<>gXhV^Z?hKo(d5tSncLLCJ09Kf{>H^!eH`REV94kWf ztaYXcw~U(9YOO)z?z^i+(>s`GYLLS0(v+oz@&>_swOumbKEWr7RJ z�~(t;G#@LY1Df6ynKI>Io+a$dK7h4y$@!OK1UFlzu7`UjEE1qf|OFsPnge;#x~> zwrJVQu>Sx?DsRG}wv|a*!*;wzLO@c|>S>`wf(F-gas>II*#vdDdwj1EFj>Od_}u{_PE;>7GojD!N(CjnmD?agYnEG=fZ zy$Pn(%T2uv#JupgIIu$~P$fi>?(fEO_iZ}1xEFS}cR|x>-%%zq7Vtues$CxnoTqI^ zeg<1>j)4~P;U!VpjmFEi*-pUQzH3qZQWo=tPSd?3-O~bI#iZ#_i&+$01qPLK-1g|L zASsycfFiO6NzRnWp4@VUt8&zl&g7$@!~L@L9z^&L$6IJZ%3o> z*AOdquBWRbP35q5L_2ibwS#auF6$I)muLtDDJL=lk}H-q+Xryp=25u4CVzC=t#Y|X zi5G0^RQ=C2foP1U-Hf#BN>Ei&l~#lf{A)KY=EN=fq>)9p?RRG{N~vzQCi>m`I@=C1 z_WE|bSJ**nGUHVWEvA}@Sn5tO_)@WcewkF&K z5!1U)HA?~!Mq%zVkYZOM?r1Wt&qlmSk{dNBE6h;%@8G6okKab@qt` z#!A#dp{NHuYc6f}+M-Cve9Y9VQc-uB_mlGM=(b(uvWRV=3AD2E{FN;xprxdqzh)}u zuwtb7qLoW*ZNl@Gjh2eWtH-Ksx;Ayi$-7$O+QC)sWM#P8oNQzUs&{9M4czNnRp%&b zy@tZgiRMC*;zb+N(Z4$Ei>ydiuM<~+1V^3po_jlV(-JBh}p59%H*$R_Jj zuQ#-B+jU$SU^q){S-4si_W5zSMVGp-EG37Knk>dj4x*GS_0Jx0-MftUDN;H_&}`Nr zi>u;DBbx3GJo}Jt3Ne*bypk#h*ei@I`)x(ihJriod`2zDmE}0=6qCE+?sr{& zT<6@GGN@ar_>_Z{F;Ra8RzvLN^%#wRyF%D#X_lMv{%^)y)=K?^;hTM_Uz6tW47++Z zqoCZ2$_#{=EL@JJ&ZV_NO+C1_{{Yu{yqF*BG~zZlzcIHqD``Wev=REM83IRc48ZNE zr@#j%eD;jZwaQDkP5G71)XRVY_I6<0&4oDVh_SbSR}x%jk!;4&F0&;q`H~cVU6j_K z3ia&88ra=<7E;~cnAtp@lR3FC-YqC4!$nC23JT9UcVLW*^SKUM z5L&jB>RkPe$Sm8V!dpph8&s-Lid58g(;ip7ZPJv@p5L1oM^alx=(KL+E(Gg(TyjcC z7Xd)D6Oc9gaZL%_wQ+eC^UPOZR*l7d-P~n%meYHQ+m(eRB?w6Ip(-_@KFoS9*|u2B zw4W^>iI>UTZrPP9p|0)byA}TcYCmSbG-X=eKt$rSrAltJ{YLvi7}f0CHQX!8aFa-@ z%ibe;4l+nR#=L1}zU|2>w!;!(%o^d{h&5AYw55ASG@GA$-p87jtgOY@?anO~n~?b8 ze%IV`;?9zlz)Q7>At&QXyA3;gG3&j*zr!-ZlHz1HExJ}NP>e3Ip*FOz%SF9LmDNyt z`u5{X7e`XAN~myc+oLvH9Hh&*hlWJ~ zh@`Wi7pijAP^;prIX@(&BHnZmPwV zw|3i*bZAg-XUj|N2IHMI9r*#Cb-`b$K!M;+wI1>}%UfZzz?8yd2*XNKka!`(1u=)MI6#;PkwldCJm|t z*vO(K3vVI7S|1KHx7v%=Kpq^j#&07AYP4mXb16d#-@AV2BlVi2WKby30x?ZIe2aRf z6t^34(V;qAJ*#j5#@tFAbKz@Ne`YCjcU+qh)RN9EyU#%64YO~z1j{Xz04f{Rl{@i| z&h9Ih{U(zWY>mA@XUK$5rKRLh*P_NNdXf(2h`^Aea732ktax=%MC5&$VBBk}4y+1k z$U|1mmuy^1rO~7Y9`Plzs*;}5Oc^cDs<}`s@VOmFP0M*hR0gBQbI(`-0TiGel%^}c zN0g;n6Mm=58VPoeNq*jB#gQ1rL?o3mfNE4h%9X({w_|c8YTnwGri-X-7|jn6md zfMwV_TCHFF%2m*g##vrrIJLbZRj-H&XHlUz&`u1Pu$RyTI$x50@942e_L*-~0c zLfskmiVO*U<~%+okZqXWnxafuu{Fr|79>ti+PW>4R6?9oSrn}^Ij5!uj&>LNm*N@n zSLyJvdL@&6GSXzvvd_06^?!$3k={?3Pj*;YZg`#(Uy0cGcmDv=$s$h9-ih0G#vQWF zer4fm5t?nKscO$Ef-9aYZts||)yWp?Y`WJ54YIgPk>^|`B5j&XHNrxlj>G72Axlq0 z0-@JDS7rB(1wJG;#?5xMX_b4Cwl46CxhKoMZHehZ)S~O_jPXrDq0A>1dv^Z-i*gm` zUx@}r-FZKRi5sy>n{kfVY8@OWO3;6uE-zY?5kZ-t9Wi*WAQ@TIhj|Bmy=-CY zbQdU8NK1+C?XDA?3nK26D_}FPS0>?G;k$CVOH~<+(hA&KyONaFxEnVz&ON?+lA-{y zk8^GZo7G6htvRj+PDVA>7qp@n|tD|A*Q zG#gy>tsp7oqe^Q|%tbpjA-kDP3SD~Mx3ykm&Q;y_73P&kNm{g}B-8*2r7#W5NP&FZ z&}wfY&$%*|q`J8UR14Lfef@C-yhxyjohGOejkui?7;&H)b5QXoBVL$~D#ZfqTw#~z z&XnnG3JOxzNgb!}#A*mJ$J#!vcg9<{D%$gDOUXeF14P zmh>mPEW2@IYk{4~jV2XpV`@@-0=>DM@N!xWR|_Y^JcA0x*tbuA<>I=a6I_#395o`- z9{pCOu8q2ED?xnAZ^;6s;-O5%aUABNwJjj|iGOG8jo-MD67dGgsJnhx3yXVv1V?$Z z+Jzpz{CQ_{eN)?TC&?#Q>@P8PTbC7A0zBSclDRK(U#>SevSZ&}kfkaFADGgn(N790 ztiJ43ZGT)gtIbDLGb^+1@l2|h?kD6sJJOh$wOXh(v*qaK^K_rBkd zlZ?@u%Gj>k)uJGhOHVk%@yidiNO7enN`dx+o;0>HV|7+9LY{WqT^Q)to5EhszOgY1 zCfff1g5w&rNkQ$Wc)`q<9`lPXE1S;6785rIu1z94;U%XEhlnXUyDTWC_;^O2D$70R{1{KfUFihxu1?$Ly#i8sa&Pr#6%N$u>!dM^?EQ&zb{ zYRMj5vgFjal18+o5}-TjimlftlGBw-9DB%!IVYPVrNI>s(iB0HKxs;poQV|7*B)Pf z?cA2zdJfLdjCd8neAw#bX@v4^&LX9%1sd105dM^kH@r#k6=3q~EiE(d6FjG6bw~wO zP|GpgP6U?L*8rP)njDO8`1eOryS3S9X=1HGW5TkZZ$K~=+$q0!eZ2I9jl<19urj!o z(&H?VM_SaP;qD~Yw-VuOGq~!=a(BfT(G|w)$Rh7DRJpA#YqGuv7NVal`zejKHp|Nz zBPEreDY~)SvTq+fAuZ3AmSGA8+jB5$-g_yE%-eId@U25|Zo*I0SZfsRhQghdc$kUp z$4*e2BsQ~3P?YD_8J))VYmcW!XfakiUb>>ram~lm#1t`kowOpqI$}Wb-*7D;oXG)r z^R3VIbp0}ByAD_nj76RQ0JnWVZuZ5==#AUd!B@*filBkw{!rPcW&^<8x0@{{*k(=^ z-z`c{$`;-}+RIFhy&`AJl2WfRRVi2XW7GC6+cN4(?-L)o*~f)k3b}XOYSy)|@`GUH z3Cx4;rZ;wR>SHcnpxa%@zM$Y=g;{`3d0^RNQ4mKmu7Zpm%HkFnXqJ>nE+;P%dUe2; zv1!0MH`<`&{{Sx7w^Q-a8tiP+-61Zx?xz?FJvwLa#SF9MVp^)8*kP-yjTs$os}1*P zPkIH>C~<@z(3;Y$l40jS|$7hgNJ{T%OL3Ctl<@;R!;}@LW-oC&x)ah$fH$@$ABB*f=ZSgS%XS6XvmQ!n z6w#TJuGNU*xQ47kH<1$o1B?N+;Y5GYSd0y0Cw_XaGDN!g-LU07}qRTsZ z4WL1GS{JD@2mqei0rp^d;lQ5d`EecA?a1R1mgkyOp-n3a%_G0;#a>q52Ey^PZWEUs zZ_UE5mqb;%RNAQ2rrRquu5~mc7_HxIwOvh4+nIL0lElZ7WVJ3{>fMZ{Tj)|vt;p(I z`VYgT1eDk9#%~hOM(tD48(24T-6cwu;`wc26~*S*L!pqf<-Fr%vZ3y!G3pq3aJvOY zWioM{x2iQ^MaNoYi0gUR4wle_sY)dK04E1YsOrUpWhzlrm#iFLysrsciL)vMR=>WH zDQJ$AidLu1Ak*A2PjhpEbXv078(ie7^b&46R@Y~F#K_-p7b-a^UA>l{c~$KJNdY*P zIWv;dENjEJ9x_53?%P^yxAVU?rRD~m2o?5|TqUTw>LjNa@Zfq6^qIPLy~~I=saw)q zS1r3ymfDMAah6vV)(F(5-$KxJYUp#vrDE*(^Ted0ol&2^pLJcLWhdwR?SYN?x$azb z#}M{)p5<_mVp(Fhsv;1cu8kxF1(Hsh<&U1bo3<=`%ZhYJ-)$SU)ol$Ooyx4hl8C#S z#Tv$vQ>IcIabe-ur0O|$!uF;+|*70{t7tIFrG;c3&%Urh-C@(YcWzT6&ow+tsPF%J+m#HaIwaI`8 zu7`lkq;@aw$Fp{yc1I!{mwhul!@4_Dp!$||i4pBL3I)9Y<02*C3om-RH(5qV1)l4OwhusYY_I4KTxgKPsX5E+^QORDL+B z+@m^CAnZjSiM|Aw-OfDNp|}>O9P?HJlH;mCs32-0x#e7E<&APGwJsJ^B}mY-8#$e) zx0zv>7T|@<4JLi>%_Rs@yk851WDY#rbe;Rn)Y9R?7RQzC*O)}PZtg}SwvrPK_EMAP zgI@>;Jv;Cw+i|%l${|;&#XUwG9r?RrTvZYa&&X|pHArnJ>DM9JF{ZoQ__t!)RA9Du zE3kZOJ>A{5yRC_DNNFg%p~Bp)x~57MCACxa5UO?W#TG5QnAd3j5XEvgV_yDD=AX6G*AguO=t?U6g~LbW7wSIRKO#d6)H_=D%&G$sjj9|*L4M|rE%S6x&buF zN$Y{GWaQg))|)21TWL)@qC<+#?@_ z)Hx4uU77OWwYe8E-fN;X^p_OHdjWE%?!@uHf?6RKOp`M<<7{#qqjZ8U%z>r##s#Mk zqtRIomx&e&<=R_O z)XaqZ=tU2jwCnC2NvIee?ldGxYbr>1w|%mu#S5G%NLxt?X$+|}^DCC1vC#W44cvVT zD|gd+f<(XEnpm3_>bP17A-IkTAQ9RtTKg%A%)O?~=u+fRWxR<&)+|}Oa`ef}z=eLd zx4IgL0#fvZ24$r+rcSmJPqMhqF3z)Vy(h{QYkr++HB=H*>m15Tx64!)B+*~C$r_f` zdpycej5-+ER=qD$(Pdb;so(d0~&U-JYoyA7c7GD$`U{)K)vC(3; zH)>K7;c@ham}MGhG$9}r5!^Dzq;{wMYRtW3uaoGS^E{Iy>K-D=rN_>_oPQEJxNU8N zu+c8-%zO2_unAV1E~KHoeMgqU2{rbUjqc6#>us?vOo??G@J6ry0BhWHVxzX%=`Z!a z;@|E?A>~h37JCdUhTY<8D_e3I)&3Y5dEf(J5<|O zb1pl#Jf8*k9Mr$x@B8BECPcXep?a4t2--pW{X=r8gvOQd@RzJ8Oixq2vH#O0ZE%|8z z6KcqkN$LGKrr$S6^75u?c5SWINtbJF*+d(BsgD$)<+iOM%IQmb2~h*@$C%}2IZB51 z-L6>Hg*WCc(`ZKD*|+#l2kw^F=Ruzkp<3CB^1QUEb?P(wvAON&N)h!QaoktqDQJpq zviDuPZu5s3LziUtJ_Oe2a#}LZ8k{GlwOiCy2!?i@YL57U@`Bw<~aB;Yiy-$ ztHs*GC6dDSteFtq4T2nbEj+Lm3Cg7;Vc7EOK{1(8aJ$OMY2Cvagl(DT-t@?gK~rf% zk0mZV!;4U>gbG%^{7Z|ERa8Thk;+vK#)P@N*i%A{IV4e$(IH16jWs_k&2>4hp%ZdP ziw^m8ZcVu}k{lWsD^fy9KovCWg~wiz+1kt`^y^ztlG-d0QnB!^t`(=**FKncNDrzw zw*A%Q+Z6q>O08n0r~IUlLaC?#PqQ7-O`Y=&D01GOxdYD-Pnh62`G7LQ>>R+ zDj*ef8DMvBmUKB}K3b8LyL3g8@=}=z)<^-pH6V4tj@n0B%WaZO3Cz$I*0)c18Dg&{ z-Aik84<_v~4-i`_3HTM6%dQ~gUPSKb>a=cY%#T4q1NF4&SAwnif@Fg9{vFM?Aw{-{p02y*+8r{{Rds z#ThKX2X_PMI;lb0tg$?Cnp7cqAb-9-s;E(Z(Sj9%8<|08F778)6H4_N;d(`T$us-2 z++q($>$Q@2@YDpEA-Jyb_$b|i!lGrA(m+%x=0QX6#& z_>e)S3sw?pfOj&V0lNO`xKT+q*OtVVfe3iW3Z`|;VI_?G2!3TYH&Z8VI>9y{>(lXsD|9dV}3@@ozXV8l0or) znydH>OADxicv5@@oyTZgSXQG2*3V$ls-MFK$00B~yM4%rpXJMnmX?!mc&$c>2}+HB z3`*`qYQdY}a+)^jNNSF}g*QqBgPwWzV&+(qZ-Mh_VZ~iw&rXF$Q=|;^k^MLwS-7|b z397ZheblKTaj1G$fPL6{y?2Lz&V(4T8a=iyma2!G%8o9T6wbeHBDp)Si0HyAfZ;{z z-xaj9`>84HQbkX*JV5M8p?dC(YdOV=m9da0w z-a33otE~Zz)iX|&%JUIggycGVuvf`j0(o<;qV=pXe7@7}#20qRNU^n_ zCB$-z{2P)UeTbmFPYP73)9e_ojthkN=pOT%ZO=a7X)2@C>gpyCY6d$QvgVE?5t@{LbxXn(bZpVC} z8lNYwUD;)Wt?$**q8VecxBf|;4Sp16lh@sX{{T^phtOQ(R^ER!6fHncIT4iLtR9o}}?^ z;=6dXMqIcJxiM?f`#@D%Yf9I$xW!)I?#6mO4IRCr6x1ubI0}1gD(kZ%ytTG9yJ|Vt zxAf0kuvQ;+#mbUmSQS#fH}XPs%+0^j8EVq5Ay1lzM?OSumrc4saDu~n@Vsn4Z-n9Oc@*6l7R(BR>}Xc=j+IoAo?9+cl^j_%*furuZ&_HlUmW=cUh$$U$)eHJkgR5J zZkF#{<+f;S?Gm$VnnOZYCCQCGK!)-X!&Pt1sYqISQ#@S5Waf=&sZ%WZDNwRo2Xsq% z%Ja5X*hIMq6_8XEQBKtNV;Q<_@`Q~}-IdQ0Gvo)iJ1bSu%aWuryq4<81S{ADD~qk3 z+*(yDHzhjg$gR7~#-g|wmX@VDs=BQ;DjwSPt~HAq6wuGk`-`U%&GBa{TK4|gn*6~A z(>5hYZK%jhhZb@RtuqzR7(MT0xpq#DsMl?Ks)AL}mzv+(Gk@7y8;WW-5Ly5Vc?oT% z%DRlj1~V3Lb8#hGZKP=~_T@z)jk*parI;1!K|%~6s)vtw>I?6)hOxzF06#%)S%mvYzJKq-vWce@8RBelJB`Lj9eXNPNgBTM!FH2a;`SGc4IgxC83gp(gW3_+GunEwFT_si=u{KdE%VWk2EMq~XBGTY7sWVBkBdu~HSjX!e1y2wJW4E*w0 z$t6mtBeV==tZlNl@c5SxHZO?Ryj=F&wpRkxZV=yJ#-y4OF@1w(b_}so z9s4Tl;h}5#PCOSKNcV8xcGPQk%ETrsu}Mo!brd~KeVEWtp**Jkqc36k>XmN#;8n*+PB*so%@k>Qoc-8=2eoi6|OT7&$D{x zA#rye<+xO0v%cMKQ8HUX98PIL6g2kY^5)E^4Kx9c={s-Cnr$4H3RDJAUTz!BX{tt5 z9Wu|i87;@RU3eGPcXZJCwDJYgIY-IseF{Dj6spwr<&1V_%LRJ&pl)5&gJKGeGbxF*WX@D+mQL5WITMAp)P0?}FA26k6IhDVsUW!7 zHK4C(7_W`|Z0mf`E#Z%Q9nxF}?&aE1xr=XY@7S;LZ5MR5rzg&Qke3~%K+~oZVD5?S zvZT{-7jfJEY6_21v~JD{uL0?1-87XJ;$a!4#q@f8m&qg!KX$Xle=NVhTJ&HI>$rO_SYBpvLS92DlJA+WwY4Z4{b-WA*-}8 zB3Pl3Dzqe#T_U~M^Igw>$afzj(7wdNgk!2SXJpu#l`%-IwmXD|O6gK7)RWjTJ}-3M z<)hSe@S$Be5Z?N@CdPwjwnAX7ip-#W%$B!Kd2^0WVGt=9Nw(Y(VdT7Ge=l{wl)DS=iyj}jTGjFa=|bS*0`RYIw$%k05U*xOYljlUXs zjxKGu8j#IFj!sG*%w)5&;I2^`TuV~qt2Me>ZnFWB!>$<$Rdj+#NW(1W!L3G58cB{z zEIH5}jM{Exr_=_eC?5&yo_K>Iagw1mw&IIdu{pVoh?gNhx412l5!ADw z8s|V$mUy(m$=q!;s^~6Eac3P0Np;>9Xmg&o!j53H7IeqzngiKWjphbMPLivsm&myo zx*@4Le5vtg%Yh{-OF-$4GE!^Z-;Yw*d#+E>bBZ%L78P_Wq}cZj!+eixjU95XGh7X} z%4$lKqe4I(-Epqmcgr}|UCD5vQ;mZwmne>caQ^@lTez|su5j39cJ7wg`J3Cw3i(p6 z_ic~LeYp9XyE~sFa=O)NZFnA$wQX>*8q-q1r+!~;HqhRbyiDBhTrIfRwn5w*GS|7BLj1uc$c-Y4>rmho&)bW=yK~&}lTe!k zzm1P^)=3K-t;M@rmw3u0i;K3kr)lpfwjL|4XD7OhUrxm+RI z?aisl($*V@%2K5gv+PAl`!ELaoUyI*coSm7nWSEO*5BieowKZO#ND!Y9^fq`?psQj z@*9r!nw0p(Xb)~YQhnW>e)EkzCgDQWyH)WGLiLZkA@R5QQqgX1dN$*zV%Y&sA1$T= zx({cw87z;cxcBcnw{>jmJ;`zlmLSb~>g0QG?Q*2ew_0E{kJh%9l;o7u_N_EDr?V3O z0QxJ!{?$TX{igSe4()$j78zH!es(*K>(r-iEGNXJE5vG*WJPkoc|T4kV^~R{7jr2( zHBc@^V$;jk)$Z-K%r)KGPPvS*18%7R6=spt<7?Sn#di+kv3=EfkZ0jW^m(9EY3xae zwJ{;0%gE}IC~@zWuSRO9$x03`j3xV&#YaqcT~gU-_!3Dp_g4}f)R0ZNWp1SiSH@|i zHgLbaIY9CAvEzG%ru%0(n?!5%D72W_i4W`Z?raEF#)2UQa%(W zo#f)O{l`+zAApN>pL&@wG@GbxX>fFyB=e|?(XEdR|7+XhszyE!@l#_TX)* zX`-Wcvf|42W`v;&doL?Jbtk907fT&9W;H?9Z>-B93IK-bmwqIkJq3j5DqOcDn8^+$ zX-t}UT>_L%C^8s>9|AI#p^qV@puv9rUvrQWvz41_vd}XY7cNSXMDu3h{%3YG zQ4wNOa=HB%5@w4#D-MZO4DVfF;Jm_(M9A<#oChnw>j z0IAZN)cu$O?Bd-jiyle&G1mfdfUxQsQ{@L%gIZUzw8d@gXir8JX7r-;0o)8ZxFS0t z=_WGTN%Gh#>Q8lXHs5QdqTqIHoz+q_gKJtI0#u%{`X`-`b5 z?9NBITf~UZG4oG2Q}U8C*4PG_)2PJ;e#r^=7x+80kuF+`&(X0n-AG%r?bP$3oQ zkUMdhzp-+?G&Ht%E^lcp+_tTgm^QQa=isYpUzr2s_axw2jphXWDp?t)1L0@S4t`+H)V>vG$tE)l2A!*SUSEF6IcN^6f)!ri*?PKTf7ZBEqa*hbk7 z%@x$V^yjVxtlMR_!VY<;kh8tRQ6~C zEClUemyHRha>{xk$rE(hp~Op3cDz&-W~bd!-ckPmo@s#YZPz8nL6FV1l{rV#&?WcZeowdH=Ik}>!)-<*$9r)`RR47!aV<))dWaG=8(`g%h zyC%)m$#Yf#xMSM(<jhQ}FHq$cg zd7QQw+)22|l7)fiKA=unf#!8R!xpf$2)d+KJCnG$jEk%ZwwK(CEuSgHRER%r0u~Wt-lF3MYx+<$Zg3Q3eea>NfjLj zPPp_vy5spqiq{I=L~8dU?;B3{+&4$sYj9Ycn+ZTbn|uqBgjEPPa_&jD-N!a;_~OMC_dmhg8`b2;M?jS8&VrZ<-ajhPTbHEXqq4~s zGp(h@Z)u+DC{o)?(6kVGk1(X*Oq)(hjRoV~@l2XF{`$LovvC9&m2D*wCWe&b5?Jj^ zi8&G9jCM{Uw{=PyGg*&%}?+r_|z`DJZ(6WZ(EnN~)l!6cmtwuC9 z_pGQ}s&%1P9kUtS*G9 zTf=H^TH8G}1ZR=OE<%f!D^#IlYA@nMskir}X=z(G8#|WN1nO;UtISbY=~TJcF^Syv z2VQ787uXwHU{^-o>H_1bQR$a`@ki82^iRuSu+ls3@b?98H)UtPDDESctxwngK z)FBSN8Q|q>sX$OGA`NpG*+Si^LtPB(w{=TKIw0O|KRFTSt_}R#D-JYYBmb%4ki!a@ zZJ8oF=^&uf<fsU z5D86c0mf^0y>1$^{{U&XE(43yCl_z-9^I^4w*$^ycI4*KFJ^BhnxsplHK$NgR+^77 zAkk^9LH1#rCT*+HX?PFa>`DzZGVI%lPCC-pH4?&*5F>|6R6p9UTo;vyt5w7g9{aAM z{ODGBeoL1Y122AJgtp^ru0W`^La|?NcjELdB^y%WBr#&Sggx>Lg)uD?rV<@W9Uylh zUM`p}7oBxX3Ra`wAx(uga#XiMS#cyK=;&Ar8hQl^;Gw~G9JNGgDwVC71kfO=gb+U* z4No(w`~otre=rmv$@#^#nwwBH1P;Qgbi|6xNA*}<4f5jaigGQX`A)atlG+kVfTm`L z5#F?*s>AVR&gh3Nsv~!Fs78*c(^f)Iq%Nx^W|^c{F^s-uWvH!M{7pU<1=%_z%WuS< zpSe4D+svk$60DHc8(WWPY^_P5rBnebHOOPkJBPlotS8Fq{fD9L8v{zW8PJ|9^c?=@ zZUlh)jNO#5U5>Nmr2|($N5xuaOnClJc<&pv&W;pm?IYhimu}jvw>49>JT>T7j5fuZ z6Pt=Z?^jko`3yq?Cq`{Nk%bi%=3lyLw?SeBb29K1XgX; z&RYYG?~>kyb{m=pmZYvTDPasS;#EZ}%>ZU;(42YAj^^9;Y`I4U9a!+;rG4kMY`;u* zUE3K->gc~YJ}cZ&9%lN9?pwkoSauX&+oFo0R|J4nPwcGZN4Q|i`5V^k>wa98--6+4+h{6B%sE#=A=z0WR0%gy1-l-VlrsI5vlc4ym*y{}=%#$JSLXPZBU zKA8C-ivrPf+>X5Sw1sVs3g$~v^d+X!Dpa)`fB<6JjyiIr1ID&*M%k zeE!mfd3TM*iianx9VHEspO;flZ@U{Uqh#sFsdMl#d$#Q7E=yp16T%9+tM~i+0jMxX zn4+Ontx5d3_hOfR-cpN%OMR1Ka%sru1&YRr&qHmt(;g&Y`V0($QULoZdYpOQPD^K} z;o>Nyg`M$nFf1|^1%~E7AqFj$-lchddZ<+4sLqs0&X^Aa9p1i?+qM3ky$(zB#~@oT zEp9ih?5;Ge(Iy-_1ZI3O+lv6v^|z#ER;-D~scksErr!jQBi?egr;95uA~u#c#@o2Y zYL#8KL}V2dDLzw(@psm^*I`LZ0!BYJ+m3WnEr%&szi;Lp>ctFp?jfbA4j>Q=m?(E5 zD@v%G@txW3H)y!uEdKzx`-X+mB9pgvlhNE;s7=08ZoRD}A;zBga_Vt?w{f9IxVjbE z<0WqHm})0AI@*W6xYb?%011+_tD-A~*ulci2h?c0cpq`+G=Lq1yb4r@&f zdocSJ+@_AolI13THCLwjynV^LdzGWz)VJXCI84*fgyv#=# zCRW+L=yRuZPPFmu&1_ufHuQARuI$)Sm;kFtD$~>3-GN<~L1TU};yL#I?`q{3%-i>d zXGC-V0D2=nmjI_>p(J2Cu#)0mVPwX92RvQNELY~{Da5DZJ)^d`g(X61>N#zjlWs{} zUzXxsO*FLNQc``s8sIC0cCM(hVY9$`@9fvNMZz2{_Y*9tRb6^@PF5^*-a6=X8AoDl3a!bcud%+l)!4pR8pAlKPEJ#m< zVU>0!xne$6i55yX49ic6pdCQ!QlW<@A)L`*{(9L+Qo@jZzT8eSqqNu6!8}`4Zh!2Gx+1a|!m4FfGL>tqJ2eH>(t{ z0^0^cTDRU2cWuE{I+UptJ=9hkZML-{yF~O8Q7C0oQgC3(f+|wF54WZr%EAQap43WH zO!|yE6-M>VZ%JOW2l*V&0|bW_E$cR}}W;V2#S0MuYSjGK{O zl~HZu+nlJ~2}0wR+Z%b?H-Zjoh{UuvBzru!w3CY4JKXuHIMA@WvPN3MNs(pK zY3?WvC{46Vf~0jTN~!i~sKvdl!qlG&Z-CE|tE6JE%dQ}dlj2aJ#l^e!X~h-y zF|WmN8?W9sLm_mW;uZ}-%lfe*P;7;Dk{h$H`-$p_5?xneSt98J|8?7AiOGNhVWA4W^ARIYe>#FOv` ze!;e_)>}wJ4~Hx90!gQTZYOxlg$65f4oBVIU=0-MCoMw@@+f|&R7UGWdNkwJO1o)a z;aotr*2(#psczgbA5ue=P*KR{0K_uEoKQVb{)pwIB{CF*IIt>|6%6rTb{9MX?B0r6 zib&qj?Mnd`bRNLG%89`DcF9gPsU&vAQnTT-!|F`oK{`B(LA7+;e=8s*_S{`# zL8v?F4`;@d}#dQHq_~H)R*fS?zO#C$6Nnt1X(> zIo-o%w5jJ7pU`Yn=qd)Jq-EQREKS01(Q%jA7|Y^<)H4dhS*)amk{*r`5`E)OKr!90ct~F z8vUKPNMO3^GX8djd*s{h_1sV(7;q%WhT@c;H3?dZMJZEC<9pkd>_T3qW4nUo`?1r3 z3+=8y+&hAFsEbLB61tsz)1(Ch?Z)FXxNTUDnwYF@qUFb{(6jqzY}@uVxZA&Q{lfVD zR+dttlnRQ3=T4pY!)0!NF>t>Vn{tGjGXC-asWKBO*tz;;nBOa(@yH{U0hTR4i?tJKwvCWGh{@Yqvm< z3g$NAs1iD4EA7Qr;lJV8hx(Xq9@UcN z>5n$a&xEJV6-t}TTxOw3JxEKPA>0$@I*r=7ahRjf8B)rYp1r9#W6?WDI^6v<)W-J~ z9nXoq3iDu&?%_fO!eNlY6cVO&60@Fpfn02}GF7!iWN~eE86i)%!e%gL#$0F3(wT{L zP$9JDXnxF6!!mPvmhTo2S`jAwzsb0_9vpqE<*{i_gx?T#Da89}jTRK4%j$ZSIkRoj zD?&4l`kX?Gl0!;A2vtT7f~-pF2JJ6goSv3oKDiQ9Muyv}iKlSD8Ceju8pxi-nN86b zTZ`?t3s&EG{lLhBbk>&{RFn#(12IiiraZrY%#HTr9tP`c#BfQ-m!WU=9&On2-4fe# z!mylx%9RPr7(3f!q3T8d0L{g^5KjH$Y5SI19NLezg*c*QG`awkli7`4%iKRN3QVKi zwuOFGh?f_HZWaip<8=*8b)izwQ0Y%@4wrOwT_6Vyn~`cqTY}=;M^_dje=1Y)ni1C` zD~mj9a+<7wx{l$6X&NXZ*Op-s$&NE$O~Ml z4GXfKZl^736t7{=7uZ`g6e~i%BXg{{jYc;bB}i}P73Z*(0?^-AG#0M_N}NZ@XbmS{qCYu!^fJ zKG)rLi7&M=4YJ&(Mzc)N6Xqmtz_v zie;D>thw7a<;Au>-uZ4iAv2RVUlnr|fy-U_hU8tMjZp3_fEL@DY6w33U2GPz@b0e^ z0l8&ZzZ0Fifm_RG+}w?Bmg~`&RT)c5B$ShtK<%bIrwiqBAz5)S**C6Rm#2W}v+~C7 zMu?tMuN0)2Dqf-!T(c*pACa?T@l0556Q-nlzAYC`%_>FO93@nOr&~ZLr&1^}=$*H} zTDNPbC&16;;>vR7L_rSCbD2Gu?y~D)Yf4Daty!MzZ0zJhbfYUJiqUVjpBYcFLTcqO zxEPXxkR)R1bszNIuM3XZHiWr9kcAD`3~1@Wri?sS0(MWLAs>l%yYV!8?7Z-Xpm#R55Ox zi*vq2UAQpfs((qugQ|p3XN?v%4T~$HQkv44 z1KEpRmvK|#sKGZ~yOYGM4LdR--bAD+3Kh>f*0|YnrnEB`6&a&bW)}<1RF=+3?LU46 zxnVU%ExEKTTqnqx1tHO`@!?uB$WsDY#z~W_jzdK+NsP0twRjF`>6Q(u5nExq8&1fU zr4mwwRD|YEc&o{}lMJ_v+8;423GoqaELAO8ZO+BpPUX+TsU(cvr9kY?xUZHhya}OlJ|m^2!%Iq#>#2G7M!zzn z-%LRad+t;^Sr({8+rs-{Man2Pu`RHgzO>V>Jp*mPl7gv@&b+IjnPuHiF6Oakl&chV zr%SDZN*xCxlTQ3vWbM%9RaFGDc3xpQ+oJCc;v`+4Vb)btvKBz4MGv^(cx?8cfzq7Q z;yG>Hzqz4>`5UH8a`hBvNj6KD`{L(YAglKW4bi&%%+pa)eVy3At;+0W!835SyKXv= zhV;1JS#2&ZQtrqUAtM}qdOw&w(ZM-X@fYaRT71}eQs;b$z}}Fxe}_BCb~k2J)oK#Wj{}| zYgfRydAA$oLHQdqmnFXXF1u!R##H0_Zz7rlYRD+&GNuN(ylvLv-!iEzFtVpE4urX8 z;4R~Mwm@anm~A(sQquTsNFiP4*^K5#a^t61I+xhH9mc&yirbuPW7=y)z5UA~Jf@Nl z&jqbbwxuSAO){y*X8!jny3(w>x3)$qjH%rF>C0!{EjzP#ZJUgnmhXT(*5ERr<5Ci( zc+^lTYfO7@)4!@0VasOzk?=4b&DoHXcP;TbeRqWIt;ZnAQA5q-Sx_K?I)m-U)EK!@ zy9FrtoU|G z&{cD#FnncFq>?z&TUI;Mm$=s?+b-}0Z8G%gQXC+HXnmbAj_z1oxei|f+YXCSZ*%T} zdAJ4cH=VrBe3I;zR1h50YIP+&_{G`6m8~KdJCScu5;enVM`=o8#PmlHPNixlzTa*n z>D?w=h(kMe9^x*|lQ>3>6{RjXJYK;=*@I)9dJ2sK@>ZuA7+X;7lhbpU%?g}s z1fja*byGgL9`5rfs>x^k65>(MnAl+ARAf%HpJ5UR~07Y^pN@!rB}nf~T}$ z)43@NZ4DsPGH0QdmI@zONKZ`Gpa=25_pfx~Qt?ZvH1g%t}j~n^JZMP ze5PJQQX03$Tj-8lRR@~oG&Vh|BO0yWyD;!=b6uKV&k~~xYPo)NiaPDVUr(L62HkpB zIPVD1uk)?yMViBImW8SWrxTK-C}gsePYBB&G52TJ42O9vSUyDjPfpuAKGY-mjZ}rX zC(>r;^!aUPer_wo4dJyWKl1P{R`9T<+^tYwLhGa&b*(Fnk8b@=wY;vKIc8OEeZSC` zGS($ye|%eH&3UP<6taS%uPUWZcXs1bcW$Q>J1oZ+pxnOl#>s%&w%2wvXPi)=ebQX> zJF8sA2D7wJo6^W~w?w1xm9qPBvs{+@?AjT&qCr-M7$-_WJ<9FIhZ_!2;_7B{Gt|5R zHm#Y->_2WVXpR%`AkqG_zIxH)4v|?j{ zrJ8#)#{U2pakYEkbu-f2ZbRbGeDUqt($y-+7hf^xZn}AgbOXN`H{elJ##_j1x#Dgbxgt(D>?di4( zN}_FO5f-e9gGy1S-;0YIN{ixC-Q1MC3xDY&ndb4d$z}nlF_o^mBZ}v7W;f*~D z9%_@Ip4@uBXni=w+#%0(zj2t)?wMmxmMFUX$?6AiU${?{w!=FEW>{ixOKZ&CZZ_?) z1~it^Q&P58nyIylcvO>-#g}(|D#5dbCkE4Ki)-&Yle&{g;*5Slw|u|JM*UVO1Hr%) zRZjZnkDDKF)BBCgcd0+^4z;_i4W$0#x6wTm1+<40fkJz+Bz9Rh>6mP{MHTCZ>=Pz# zO`|G8Twc#@#;QtN5?pU8Pzrsw5IK?#mqUGO0?Sh~C1bc}Inv-G>Y~>a7>Gy0K8& zU2JLggG>VOGJIr%E!@KY081QJZOyf9lRj<)3%ogt@HaKpHtLaF>Jml*p4WPBR+=YS zHX!R1>PmYrk?wNddf6{=E@Wke7NO5J_*VB!<2;t3hJu4_bx{HhF>q?(_z3dz$I zn`g?R67^a%AtJZ}>JG6=Xgsa2Nd>PZN=gS|B!le5Rz^KpYmIf%gYMgPb{mbpR;CF_ON5nu77Eo_TP`K!^zO~V@oEj*pM0T&5aqoU^47ICT);h;-|ej| zXYK;EZ}SSjy7v|8)w%CB=4Ck)!j$R@L1bneB#L9nvhibf>u=1uoMEZRr&4{Gf_S{Dh>_gp;wxFp3_ROSr{)}KAgS`8l2i{|2X@Aql3JFe)3rB6 z;>j_WoW4wh38ZBWl~i@<;>wuK=HNm|)a}_5@gyzNcZIwyEW2q+--{V5GLo{NJr9*o z)EanITnmGWqUbw4Ick9RHNyV@c!1{4a_{?PsVy($U3qC~5!OH+5~E!Eu$g!Y+||gf zN8sj8P~?0H>AG#sZTr;Qf9*o|A|t2Iy0qfTDc7K=CmWrveD>dPGDmiOw(g z1eom9Y4U&y2ixqX4V)&UpawnF&8aL|7_mGe#DRNy%^@O?(3Po3DjATnkA}4F#%ntp zmDTEMu`em;hj^aO(3=7Ki;PX3Jt|aLj63GIjX;2qRsaXxirHVY8tXudb}q|SiB=x( zvREOe?BoE%`E04Xd14tc#-!*viin^Cr7Ma{xc4v3IK2yqX2Q&AC8-#`yJg+io_^u3 zbLOo|2mvWqP!y5{J>wU!S8bp^y{npp?D8gBuPjA!bCC+prsSo=p~|Y1G&xrj%F?-R zw5uJBecPQI(C-fgarJ}M=cUQ~KPKwh_XgC~_S$9V(Cmv;_Q6wYZWcDhzNW@(Qu zjBjIQw+$6dR!&Wxm1J>ukT~*R8SeeXviI~SY&(l?KDhT7An74sBNKnlrbr|%# zJkG2P)0-v0o0La3zn z)}s(~6ty|rluJwL{{Vt{W7Kq;vN+>wuX7^Iv?AkhDrtzZ*5K3-x870{sEp}SaZj6& zt;>~hE^*+a9(lSV_gOxZeEV}{6Zc)qZEdY&6|nWePdbs*Q8Cb@{y3t)mw9NKC&%7! zT={7y?o64ie@G-shRb(ud$wC2Uy0VETa@Vpb|=ko9k`9|c@bL9BX93EAty@{Ys{MX zyXlMR%Z~RQpZ*|oRIT5BpCuyS6e2aPnE?k`Leh}6EKq0S>57Zl62CPYmy)Rq-FFpg zS26M0QCxo2I+qjCuci?aI~Fa6V}P~4F0KC1Pq(lru$bH%mKryF)&ol3uX=C*#U zZ}`)0Rr}+o?j(Jq>0ga4awEdG?Q}I~jCHJ~%_$`3T?rYU`r+F@{de0kGBs6)ysO^6 zx9;xK{K`rD34Z6s9DlVfuiYc5owlgRD{4yA5|ofTN{&54Kj}Wfv*EeLt5Qpeguw5O z-*CRI$vd_1@hU#y^^b4aj7qWX*s0wY2>OCAH7JUl=qhVfQ`wIrUsrOmA<)BI@4{XO z&|6Rabh)@Sez(NQ&Kc9^B7=el!tCp}BR)ds((TX!gll zt9M*k+nZL@g*!gyAz`GY2^H$PRY$N4@g1vjl&G};$-fvXv!6bpeN;%_<<2?cmL0L| zB&fGGLOe3=)HKwxJ{HeVoNu;`k`|-SJz0j_H*1$=%N-D>>EF}bo6Lwe%iLW*Ff~VM zX~`{hDx$reF-P2;#~8QrV~&*At!mTe0Ydu7q4;|;`*G$I z$duh=&q=k57^)%rZHA^lz_&)$Tk~%6jRGS@163+I6#-mS=47>^E^SAbQmS(P_gwNJ z?Zx?rIA|xV561~v6AfVE>iI-FIZQ1PfzxiUzeP@?(TKvM>hD8HJb_= zd&y-w^)&&Y!-SL2CWjSU4a!-o@d)3c&3>!Ehpp1%NZ5ASY?mmtOn z?e_lwFq1A+g+iD6+}PVUaImIY@hOKQ6pAS{US~1a2WMLJRHwwqQblHY)KBvhDIRlg z1qVXts@J}~aY+g$Pj*U3jV5tDt+{KyZ2PpGn|N1@v<-DV86Erdnt2h?bkH8rz*jrPQrSQV)JchYp2-*z&B4CZnxE_Tm}Bgz-s#vs7q=y(e(m`P(vVtlKH4Y)iyKyA z_%f#cM5CmTU1;0#5&r;wkpW8T770kNw-2St@CS>vCBz`R3r>6t zhj9ttIQ)Fp-k#t=uj7Ws%5Oq92}g(u?v6ZiQAK;JgZgBBN(CI2`l+T;llJ4e!a9Hp zOy!V%byYpe1Nh*$Mj)4Ym|byZsblH!}{yURc24b4R*OS^A|9tl{!MdqX|f& z6^AF{Y8qNIOr=y(zWgUpmN=AkH&c`7poOX0eGH$HOM&v-acE0dh-!HUCg!w{ z)N8O*c7ck!_{loJvBtWlxo!m~S)|5jN{X>M8=nl6bx*NFu>0`^yq!HNPuf@~(wX({q{{Tl0{TkH> z*7tt8kIN6Jt4+Ov-S=s?eVBnnyHvE59`#TYitC#koV_wF>~|`P^(wKsb>@h7wZ4}u zv0z1X_#?8M(hp@$E%Eltmi%g0oY!Z9>u;m>N zR+9o$=N3s()T7rje~uoaOVLNjn55FZm`+4>ZR^&K%HiXeFCG|bhhT9ird zMh6mh=q*N?uu=m2rFZ@~WysY5N+YVC}pHQz|^n|Y5Z45?{D-YR*?8IXSL1f9ac z+>eS3#M_sTH5Xe#nTZ%tMoN?cIcA+b`0Ke)apjWKeY{!4^R5Jmw|BMC+n|Qy%96t% z2U3z!ih>3}<2##)7{;i##JwpqJGX2|y6-D?hDswSKyfr*E!DWu)7kN|XWf6PBF%;5$~PTM(%^r$(H+@49_j4spttx9@IK?&+Ncm z8AZ@3Nj*dG?qpU!h33+dUn(I;%h+)g?aW@fA??r357{O;ZMVTh2G}(+lSC~+C#q;M zr`vm3-;HG1Q**>=K-7_yK&A%C-LG8Hi)#kVb%@B{ z_d8X(+AfHY(ndo->~kBM8kwFOcXhrca$*$XYiTg%8o6UDR4t^;P2g{I+4L1o%X)U zX|2h5Z7yynnguSnb)|x#S_GV{kCr>fzwqG0Qp#i{M#B= zP2n{Ush7gT?ew7|+1Y}Y-4?hIPc_#;Ha`2BlIuQVnx{mA_SX{6X@r$1GTwBP9-D8M zu5e@}1>Bn=m8qqB$4pS?_T8x}rKx3)yiI9R#_c{_c*%=4d#Y2ZPz`DDjPlP;FdMeP zShkU?+jn}(jp#^Q?{LHUW^a&D?B&EP~9?cFVf zu4uT?C|01*SRJ*dG_%;_CBUHlS=3fMgk-*CxUVtvkO8f79We}c7PukbsnlJI%Pqr< z>?h$z4ZGE8pmK*gSKEmrv(0fk^-o~zuhg;mI8Gz{QA$b{b!VMMHr=hh+qI?E6+C!OHKBSr6^u}K%%sLTT06Vhlk0yp3 zrdoXE2&wJpr=JN^!89He^}+Dl+9CPme0O5eO)CpN{2~RZtG;Vd=0HAN9j=J2=?G?Y@7COdKonNB#5G1#-QBZZq`(mYM|O{ zT79_md@PrCHGE18j#xbn*FIIiq)L+Erb$k!P-CTO5q!>r#>D)1Z=u&8C6o-ttn$Pc zans|dRlJwrJm+s2k2XG9a^7iyMp6_-1v3Je>l=N)Ajg+FaTqsE#d^7abh$31?z12P9=p(x5qUyN`g%WABn!$zvHML zPRTob2={bfBikQSEn85aGD7nyueP|*+1ufEHK|2=GIrf4BwM$kVQC+;A!~5Ztts&u z=Yewen`%5maW@;4;4j`c6Kyn|O@`{6&=sHbV;MYG&*ooCGt5i$$|JLr!U^I5IZdR3cVYLsCCvyR8OP^*HJh z6RRZTI&{PSUQzH0>BI!t_PPQz&!AF(EvqqHLy-w`Iw7&M8kQe#zqU5*ou%2*+sb(W zNRUNUUb$BuAHUeYa`&k8y|G%i1eXvQJFYyJ17xMNDEN;-iciyKS$y&v*FDJ@rxJiu zA&>|Lv^4v%VQX)dDvHau;@Fb5Z5OqrX^xh^nuj_MZY7<|*4MD;t5Wa~?^3OnsE@Rl zHTiV%lAgob4hzAxJFct?GW1}mh%W7Nc2bw*A1ne&iO;FV>kk{1BO{f0wkJTG`W>lJ z1azV6f+t}xJ6BqIlP1c&OL9vsd4)katP_so?7{{XWZxONq&M9ojVFHP_b+4=rjWLz&6YdL{7 zVsyR~6tFVSBb6~S?C2^r#3TOzXt^lSkS8QwcDC|;cNtbRGkRJ702cF2c|mnPB@HT zI0b8-5(=uPU$;q6u{;~#lbRFcqkCMMRn?s%>KO$gADpWA~sKKruQEH$XhJ5skQvPPV(yonor zX5!tqE5(Y=u z!#5Q;rd~qc1!)GU3SCV>&mLvF>=DJhr4ws|oz{=3kiR&3XT8sq{oeC)v(Kh-Tpy61)%hr7rQ4K5`H|iOB3e;2 zRy_|Ry?G<;GuYb$mal-dy?6^|?aiRA&kFT2-z8^6TWrvcZn@kvgO^-Mv-gGUo1Cp^ zLz}Z(wzMi-*(!|Vj387b(5QBSk4uLG8+AI+L|w@01gl90A=`xi>&V;hI!cyTVG)_YoJk_%K>I`` z*HkD>%f9Ttt_(Xyz_-GW9Hg#NW1%m)q>Vl$9?X27n>=)!Q>pAYai2166T}_mWMgiw z+sL~H**0S=`EB_~Lvmvt&mvsbX%PN3vk|78lC9ckQRA5(#H*>}!{p z@-C9>M3d#ldOx0lUV~B%X^&09&%BaSN19~eV9rg)L0Tsnzcndx$$z!o!T4KDv@q|u zq55Zo;hPo2i}YwcV!>YHZT|oS2UB8vGLQF-XnbYdS}Me!H*Q)3J+5hpQe|JD0+~yG zJA<*r6l5~(0}Cs5$_cQh@uHq5t4 ztrbOhY2B6!vuaTWca((5PL^dy4wbAp4x>~3I6e}p0J$Gg-4CVK{7#eaWo z2*_HaS?tU(o7yBOX%2a%HB)poP_8!nRy6M8G>6-1T-4>Csh&Hz@!vo4U4~PQyvV!D zegkcVRhZHoFSN?_f#Yoo30Jom3)qg`#-EucbJa2}W;FRoPs_RA>2e=@5Klz{QV-pMC1UiIB_at< z%w_39w5`E1JsgD=-xw(&x<-!LXgW}w$p;X_ACxg(tKb{9?fXvQL}y&KrrSM6i<9$6 zOAa`56$>>@a6I$fhrqFh3@+;UiMP17O`_t1wsi+rsR?zo`OWC1E2%XfjtsMrZtWX% z0La6gx1!K7w=Pj3DXGu3A&DT--ziA)=zD0PTo0Rr&!S@DW^z4IGHw28jkS~_gs$NQ zRVY4h+v0yB?90rnMaRiD6s7+F^0`AGh5?~d z1vDxGxsJ>YV`g}_67UVVogAoX#I4b%&hvjttWL$ek4q0>c`yUOE8wA@u^#889ppvw~AV%me|Q$=&PH4ar4G`0t&4sPBvBHMP(%%3VekP6ggq0n$2h1k%dZJdhjG2T>&YK z6m#ybC}-Vx@N`b4tTt^b&cAw0`fc)F%i8bQ?EI~4uv(|R^K)QBG7hN(09K@ef^r#G z6|b~z@7F7C>VaLK^-B|Ck0h+Qzu0sdAE20ye82gFl4j0%6!TWWc^bM`<65zCYyQ2< zNlyEJd7R1r0JK*rOIXof@xT4Nv@!m3gyy zvwzwR!C2|LQR+V>$LkdG{Zt7NZ!y9$J3!A9niW+S^(Qw`@B~0vJw~=S)(KG~@})llGhwd+R;=bhi6{ zdo4D9`pJ=gU2VqaD)NcWZe;bZblr=2H@oNWC50Up=2lT1q3%v!elr=kd!FeoY}q&L z@IAw~yFX*tWB%4Fo6YL~01}JtzI?u4)V88NsF3ppJ2jyP`&~{guy$)$>XB3|ayMr; zT9HyFUT=%?qlL4qio2ey`*EVTX>nyO?6${cn_?-~qqOsDopXGE)JDgcwJNz49?UHg zD=u7eVZKV=JX7h))QhJlToE&LxPq%kA3<6jn)2!4FBGbcBigEA_?R=lFG|p?+&499 zJasuK&3>ghGt_nMyL_bx8*(#*S$2FXw+1o>K~8DS#rLUL-C}K)an*cGc20fU^QCkq z4h7&hAAT*`^AF~V)R4nvH4iuqLTGyjX~j10ymCdPUTpg_xg^eyrZZNEBW~K3lQBsD z0LN|#plyT@D9RK%<<_3;d1hpt$@4aPel3LvsG)WfBF@dZi1Gen@ph|QdeE%PfKVyt zX_!K5RUXcraaWabrKb|Y(xLY5pKO6=pSGI07205^yT11uWL5b-5%TRRIjD+&QaTzZ zzYt+#JCgNrB+0wW2Sc2j>v7!Om3-Znn8;&zZwV^eJRi4PQcIDN=>(!C8CLj+$7^xxRLhDvAMs#!bf+>jl#`8i*?}I6eT5F^l=;|Z3}q{h-MB<|L?Zb^aBHb-C10qN*R)YtV&e`J zze7KqsbZus*ecytG!M?8EB@9t7ykgl%(fzZVTH`AKj(?fX*Eh4sHHk2U|p;HXoq&I zyvWS3x0y;BPNS$DJq0nKD`6@!)$!qWKNreWhLo=&YN;F$H2|8`B>Qmu5AslxIsQu; z`=VS+hS^5q9qbf1g8O76efU_!qwcL<>g2zA7GwDidKj7uw2MZP}gDf3HI&~heW6pIx9?%W-FbGbC!%g7w*Ao zv?A;7*S9P!i>x(7Lzy)pDJM#+)}z~jZbJQ+>D3Om(YvG3ku3Z6`{F6l+>LWp-)hZW zTS}^?kF!68SZ7AoLZp=>HzOk}1*cZvSRK30FG;ed_Z=&_K#NbGj?;^7OnT*&2&k?D zmfIV1<&kG=c&>xjf6zBG1 zr8=T^sj3>!UDpZ4C-*%GO+W$CMqbQEL!*$;fhzRVNDOwdUrC*dCSWnai`6R_8+is5~E=Fy~+Wvaw_~ExZlO8UW z2K^5u@G!X7wh&fgtzjKVRZqLFEUa$Wmp`~3duCKT1W6aku}wZvN>J(zXfbP#a?Q>R z7b4~BUI8WUiM2G2gWX!!?ZGm~i&`)xx=X|vt;cf*CR;^lQJ*;rL6x;aPaKI{<-)hlHhh*IK^O%+ zw!zY(GV6ll#AK2a#l>zD*1e?Th#RTJsW%`irV9eQKXI!y5x+wuuHc7{kEMSXv zrU%h+Ah`keZjt3_JyJU2w)|=gESvkyxDwK+l@68Zfxb~bBjIwK1L(M!j42Y1w5y;# z-)3oPF~0B{=J8 zPNhXs&z29cVuNwD$oLkxS2>pwRJF;sz9h&?YV(^(O8mb60Btdx%A00~5>#9VIwyu~ zPRAulE`p>wK?+DcD~j$-P-NxlAUplt$jVwAUZfh>W#NY|=}&Tuk_m!frtMhlZ58O(;)s#VqY|PNjpr z&kA^r_O?FpcH-n`QLVCSnI&EoC$HIym~WOX#*EEhz`Z$Ce_Hm<)wj!c2ZVT$#Tx-c z9+ep%b^^PfZs)=mV$H}eQN7M}yKcaiONxC@R5a~Rg+zAXTx)Wj!dv%g5}EI*?`{pl z1}(L<$$itMY^4OMr!LO?dal`dTCrJaLk*X1S1!>K4&CC-!ETptyGxAZQxK(npd@t_ z@4$IG#y!f$p_VpVw%<7=+c^<(?~d4rc67&-8xjLY!jX{~4u`)PE#qjn$mE?Fdx5#l zOOi`Mz|G^^HScLZ4&ux9&m}t8oY^3ip2D19@b|0O)1q}oy56pHpV0jyKH+AMPDyuFYmIi_2{o#roXVwiWp;k}xRaIV$}Y0f zg$OjS!{MV?<<5=swr?1fkc|+PM>}yySGMK_>8rYP9fN{?iaaD zM`aGmd1v&JgsFM|0J1O*+m`KoS|k#oDX&R8+Bi>tZZ;uDQ~@2COHP*@1r#39OtF&O zd&d_agR3mDwk!8MC`rwFF}mEXzT6dQa*;VFz=AYGVwE<9jF;Thhcc8rr zrKS!Iw*5tdv~iCWOpe=;;<&j<6(MgBPigJO7dL#|RH$ifZP=2Cr2g003_8?EkGUOe zs-`*@QYd<6a6Gp>roBj;<+0NA@h3|z$FOcqvo6f^#~Us-3_?}o3#VSpVlp>L*~nAy zsiL!ju36q*AvgTJ+|&Nop=$f zi|n=+<|0y(DUwA~pLR4*u?cW1tx1Fi=8~iR7dW~jNa;bXaFcd; z#Y++^9n_H1id9NaaqEL@UynDb5xF$9WqaNmn&A>;yL6|##jsC|9KE>N;b6jVL{#3) zkr+9DQqH70d|8kVc)O0<6tx6-Zlc5u%Cw3pYEeBBO>i~LD56N@HawwS-E^dae0@_| zVzuF1t9Q3TsuM-)W9;!qax)9e`xyYVMa*giK}7h$1sR%-e}6gmY8 z*WHA-Xf0kdq8l&y?z*rS@bsllJ1YB7CdakvsQ0r;w#0Bsmo0uD?wZteiuTi{Jl}QP zWj95@*x>R-Rf@B(w$&lY4ctSHqMlP@ymZ9^w<$20<+L`@y5DiPnv#6R)^kp-quq-P zYi`@B(tx*eIkaLcELtu~RCU_L7NSt;If70up}byRC2Y3XOTc``*!DYY7bi%Dga8ph z?!UZKR@ITE2V7TN-f}0PQ`=!}R)u2ZCnB*e^BL0O72>CNQ;jwpN_>elLayA-qv9f% zwS|J6-_#(xsi_`u3iq5z(L1g=_RFrypBa+=q^y9lqm>V4BE1+z)RxnfxR=XdaX!!7 zmUK&Y(w{XnONps9Cpw({nAhxfZC!@EN$%NbeYwG#Rg#p8SXOz1kf*;IOL%YscIqHq z_`hP=8*ygPkhNDvb*br)IDBH4fXODA8P3}_B$rxl*4t^%FpzsOPrFVw;_4lbWzCEU zkzS3h5fHZ6Yapr~gB}-~degYkt+!@sCeYzdp2LqvLX_J{K4Qv9&!#IUmu)%(!o=i= zs`CBL9L0}?y7)?~L!K_NCmBaVhblKCA;D+4OCh4ClEP>zDm+8%!*cP{L0ETPH8Hvz zOUx}gz+Eb?^%IEp+d#XGSq`sST`3K!mFjV>w6rr5i{=Qrr~K2P6prjo4G+v z5EPKn^9tw`nh}cpyIjb##<@gHHqFeoV*#i#k}BZ_iaF<=Gg%vJZ$w+;WzkiFJAFiy z`Ap4PP_DFDNvCytF&oClj;+&m*;#GK?52)8CE%cI-2KaohZRC@wQbKxZb=Ke+BY)YdV)X` zBZLa|DHZ9DqAYzCyKQ`E^P8RbjVJJpu0+}%mY9Wfx9yp`k|LKKOSUdb;y#5)(?Er3 z71Di}p7+yRnCiUM8vCll;S!wo($aPf_ijI&89n{8GKP^Ew211>;IMR_WbBif)y<&u>n9Q%LxfiGC ziDM4Zw{AMJ5ufSvf!mT~O59U!@!VZi7|2UvwKdNw{rHyu0Df++K9mmjd_?$@zSHBL z7~A&$0GQf)cG@JRHB9+yOs5iQ-sNeGR)2GDm#6uSq+2bo8Zh*g^PW*Q(PUhUQrvxy z5q;K3@*2$0BquM!8ot(yF3PjwW;e+4Mv*zAdEfI?=_XsMbqWVjQJ~Jh(T{D!+HJVf zhn`%?^$&Ya>%8tON+V8B=%CfPRW&rNah%TEp=N%SLh}~$n$?lr{>3quos|jjsN*wL zRbb;qg_{KcJeWF-+iZ)Z^DZJ=F0u#eECNX*yDS{??Z?CdHe}eb2W{Qnh-Wnrt`a^V zO%y5qwgtJL8qqNIWzclnKW~pREZ5pq7@hn~E;4N)_N!~16(A*h|OkzlAH}IapP%tLxw&U_@PlJ8CEKn0&15=|R^u9gp~z~}mMXT~3`^AI6F)BG2x9lkw^?pd6V5vsBEB+3 zRo|91c6)l)fu6g%O%0WAhb|8`+>Y!-$BAm3 znhFJ|cb?OX?d_2#sx&tD2|p21?Yd?QYj4ZD7GA29*-MASqIz|}R(7PNzd9Wq>!gw*HUx#;?m~Yxi|pY%#)uI((z97n95vhZL)87dx1}x8F_gi8j=)p ztv$H&zTv`!rB-Wp?6)UE)!ZW7@AiZ-db!B9TxBOp5W|WkMAO5UFVsiF7@}B`;YH=?r z-R=?O%CWA`M=7P}lNEYYbEvIpik<7dcJ~@Z*W62o+nEX0lrLWzaSgwXyp6$kOLCr< z<_p6uzO|sCSx^dQdyYJBeBXNV^eqoU+qS1}-mGDAP1BF|@>IjN`;Fsm?gctujUo3` z(pB7)tEX->eYIj)2_>UNF6n=#sS$0x@~F2sHeISvi)>=0TP+e?2w^B0fv9z+H$T=ZN8jrTn}%1l*m`SH_hh~AeicC{^QLyP>%Bw~G@>c)?zI)vugR~=g+ zsJu+dD=zu~P@UMw2A_qjANu zNPPz;R1;5TB<~OS&)i9|f1MEYlAu%a@)xB^r~yO|!kGC>BjSGZ*CO;peQ>%+j`>m) zrG%O*S=K5rMT<27%gL2ZS-H)*wz%!2HnH%OR1@#Tlq8}J%_qQ0^FBsGzi>jEDW-#3 z0&sn+kdpHqIoyI5;BM7Ml;6qClMEu7RLrNE&)gISIoMR-ExgHBi7&OeH@v8*ZM&E5 zk&-+pkjqlCBc)c7dwcQHj9`m9bDV-Ku_9!oGdkMRRN*?yag>5Gulu~P$94q0JB)C( zw4+FQq{6mN*0lho2GUdQAgGK%cOnT;mewn8fcd}e7}m%Zc@lRND@_~H@0sQ~BivHP zs&wr*2Fg-;1mCd)^<#1I!B%=m+*_}4-sRIMR(Md@VOdoEq~#O%V5J$-lp>w%WVqCZ zuGhON?r*)fBgu_xDy%l8Ew#uACoN^56JK^CyJ^s?SxQgRWj9GB3Dl)gU4!rJ#16^` zSxhdQs)L$*#iW|&nCXP+Bfe>h%@<@HXt}wR+R%=UHOJ9H}>xV*21zUyi91JY%B*w71^ZJdyXlxEnADC zEo{o{qp7U-BQX^YDW+#U!cWXeC@hd@Yf67kBUwC(tB91z^O?NIGp|_+aueG%&z2@{{WL!f3S-p#toJE(xdhrC*!}N zkEkmeTiq93&9yDtot7PH9@uJB`B2hbY^0^vLud-mv?mog8{U3Lr;%y*gyQd;HLea` z`Z7MgeGk17jpAQPf?~zXjh2mi#X@*3OYdq!*#JI(CSQG7wJ*y_q)$6r z(zedDwdJBJ83vlX2~v-}O6!8L`vJ@1lQWtB092;5gb^*xY&7h5Wr5KOUX+S^ z5(Y2vzMtW*imc1!{Y%f62BY^6OMet?2E%AgiG+bq7K)r|aXTXq{VIlQFTQh~UpokH z&NW@G3B7FPy$a!P1%D{Yw1-lh$kIBWW;0#U*gr3A-@|SM`eB?)-H z0vNP+GQ( zZ*N_$M`gzCFj;`I4woEXn6G#Om;*QJPT`RK3zXLO_x)Y8nw9yszXFc(U#$(HkS`u^cm7m8K8&}gye-+x)H*oLE7JU{Xl0{YAeAm8r6OTD2?Br-}=3z8wN)LL+ zZa3H+zl~vfBC23F9_z@w^^+U0u6LebaM~u(o7WB~x!QmJsUD zKPnP)Dm}wIBw<&|wPY8vD604z`{!?{b-!&wxotmB@I|SY!rGm`<%Fu}_^k+vg=!5X z_9*A<#)kv8Za-Dc5tiJ$k_*!wCpRFqy}45NBJpvPA@Ol=xjN-3#~yvv9LXwKB$L_M zi}^;?i*$)ZobS%8zWgh~R^+ysFGUT&V?mPgzr0$i02+^4o|w#MZY_A4?Yn1kXucL` z{{Xe2t9_lcv2s&?;w1n)s4?>%AMKoZ=H-<+z9*w?*fF&gL^<7+YT7nuuQzq6i)39c zHqxqIkm|BZ0L-b!6_6OFQfxlEN ztu9+{)WNW}{%O{H5dv~oCD{3Anz-ABc9P^n;?0EWHpbR&s&O*z54a=EyvRY##4rlg ztEmZBel}YMJHy9!n*;dFTgVGS1&pwda8O?FS)FwS=C+5VyE|=dS+>ZQ z=Ap)h(J`4w(@*9-EB=c~T=d>{%@q-(^J(wrQy<283fZRZ?m+z*6xGrmZ8p?81prQa!+AOzf@fvpSjq%!;NXVtxILPDirjrqC*SpQ)&je909s)srsUg zF3rDA3XSLy-O{$-CGE@mO1`lpkv6l1i)8Xr%l9^#RSR+wDOW)FRojfh-6w@}mgmB2 zZ|t`BOHNj3ro0Lzc17KZ4>s!7(}*Q6G@zuZDzCR1NMD7;%@}IMR2zFe9KSRO7Za~wf(`i=&CL(=A^W9 zC?`t3%HsPMx-LB?h_39`q^k#@qTHCx{wl$v7Z&Rw@2C0y|* zUIS8eCnGOP6lwgY--{b}V9p+p}DyFJoRF!H)FpeY?hn!XMF-h| zp_S^T1*a($%_(8!r49#@lR)Um99TuF2U?FZG-_(poYX}ywPWRB3lJo>ljB(+^#>8o zH3Yi{QCUs2^J;aeK@=Gzn%B1-YHWp739h)hfK^aBP~ohRAnY>p%Vh{2H6IN~PfuyY z64eZnNSbUG?R?yJ0T zi74EoH+b1AmZN&-z2rX~^dVpb6XVK;f}IWvw}Dv$UEJtb{l|WBMcr~j^jAKa<6F1l zKO&bWAv8v+zAaA=5hA$JLe`uLXLC`>2X(wsh|HxRBv5)M_vI&+7-=v<7#X&X+} zb8+|W=!p0SOq5cf^&N0^g^_V`*FbETTekU+pmG~8&r>rJry(IkBB~lxItt=@yG_VO zsS(=UqTL?>VY}_HTztD!7DArQZnhGYj?SY4WaGKR)Ywg|$f+igD!aYZ{k52~KShYi zNLds*i0NOu89D8#OG8s@aI8q*a;ja=d=`)gGbqq)z6V{YfVZIR>6Wza}`{<6|DRxNSx6qbmt zySRC=fw=h&GUCJX9b0I(P%yEkpr0DS*mO zi2zelN3#_hhiF-{`H~L8?i;rwL3ZuP*9dD<>y+Zsyu+3Fi0#V}Sna!V;)dSdXBlxC zw;o-&wiN7llsrBqQ>mw6#)AvCtU=YHk2iR|J`Atdo>jfLHg?N# z;8XnE{6jW9!7p;h4M};%+H$2~H5zf*6t%!ur*I>6*w()@&53hyOF6GvwGYP+vAI`D z9dgn{U2~r%-zC=6@|N2~r3)mEpKdQ~LK4=1Gfgc9jl*-vam3ZbPKpT~d%JK|yjZ3x zl%xq`vEMJZSgB=&HyPz5yD375N-LT6;ymoDHZ8YZNiA6BFJ~LTx9kSP@T4~_N>7bW zgx0wdYOHw<`lXdEG&Fb;R!LX>ElpI)u_%y%;Yj#F9^s75?%je_Yf{l}@_Hdn{%*T4 z&{~$@a64#|g)G9rtcH&y93%iTa zo$qQ#QlRbx-Q=l0D9CPxpmYYmb|S(0pN)D+HAFKjwC-C~POPlkf<6BLWlT-7?Qe3~ zC8Ll~BV7+rdSlTsb1|}ESUQwfZQQi!B|%Ebz9h6lgNja`iwf#{uqEBPE!KmrWHLvG zzt@RY9uy*B5Tu1m|PlZthtT zq_WrdV4C@rr7=&PhNm4%3@h#~q^{Qcb*I^F>1%bfO$+Nx@tS)od=YhTaF>}UT6fuQ zVnnyfsNG|v4#SlQt|@Nplzal#Sq<_I?u8}i+hPZkB}Dns3bRl>;+WIzm{edHc?ppm zdyNZBd&^}MDbC)r^Tfcov^AZHJ`3vt-wTTPrPQX@W+hY)&=Nj2}rM|N0zE?$qs zGwvJtz~NxKuU1BfA&)-&FuwW9F$r6`NLV5A6I0vl!ozP%z?0_NLgZcERG{OJYEp72 z>JQt5orUB~vL6xoW#0Z{t+rz&w!s9r4SHgt<+hk)FVvbLUo96)YlX+$E|EnzO+Ny9 zU?n!0VLQCh)UAAlVt(ZF%Cs*^TLft|s*OqOjb7cCUL_`7lD1tDakt7_GLuCkHHu}8 zb}p#RUyU^gHkCTPVArjCFl7J~s>jaNA-9m)sF6|B_Se4@S^j9f!O*_kPrcJ8AwXPks?%g@0%S*bKG0UZ`afG8eg#{x}8q?C4^h-HvN~L_)DFkoL$7qt4 zBgB%44u=nz0)e6SbiucBRAEpWIZlBabO@g$r@|8Zk}}XoL5~RT-sp3>;3G$H-X zwW7^s#X8QG)M^|VQ{Rn7*Sb#HGuwvQ8L}vH%>vLuBP_V}d?8B6efZEvHQXs461Ma= zF+X)en{f$g{{YHLhfbI-&G!~E?WoE$8Haa6naiJigL(kPyr2`l~ z?fke^8qJCIU#PYIowvUd-#4ppxo>t|w%*KjkQupJAR=Uj-0(UgXY(%DiHMr2bnjGgQU8&rHx zM8>^ox-*ut`P$DO^&&bh?kd}Dd1<$)RUTs~Q3xkbK%fCQ@jdgj-PYuzs|@{0+#5-e ze9p4@waPtuBca^w*F=OnXpbpR-o zv*!3m*<5bnu*E7+$lu&%*tKru%gw#tEsMI54bw#?z8`XE=S=XV|lq3s~^Xh zQ8wEq-8Ny97^g{TNg32OWdk}cWLjJ3fFGY1cByh zJ!#v4@o`@UHtqV)LAJ)?W+-)b>ajvjogOOm&lgbJV&0-lDpVTtx4p^7)ZCd)2F7S) zWDm0}9cOE_ksR~nF711|q^-$sL^6=bs#sNNUc-no@RewSF>iRVmgU{joR~@yh=?OJ zHrhu{wI>Bx+Z8oBd5)pIp}bXZoZOF}TS*E9+6sz)%Zi=TXXaWVF?T*$6AtF)D6^pa zgjflBG=%AvU;GIq+D za!5!<%~Q`7*tj(EDpRc_|J3RKW-->esfNi6)T+I``vhR8Y(T7DR?!^*POyhyLuxFA?I_3A{`CTl+? z+e)L0QuO8S#Mznl;|&oGHZ0tW1-F#`(s43j-NhCNTXEJk=h{fd7e3OxtdVbKuzE6H zvhtX)!l>TYO6~51(!mtj|M-enq?Vt!-eN&*L zKA7I^J66i$>ErD)c{!Qsw=Mx~kXOKAdyEi3z)mku;YTTJT+2dM`wHFs|O)~>X52W~BD@f_> z2frEH%Iqbg(6z?B7Py3CTr5KCa)$I*BU`RWNI)b}(3%o*=uQ&#>A2Mfb5)Mb$0KhY zt0on(*o)yqz%R^cUtv<(z!v6V3Em(;vT?BmKIwDKj|99Nx7vBYK-r7wgwT_E<< zry=dbtwIvw#$CI1FGM!aE3C*-borGh^-$ufQfZwASas}MG^!x6mwTImBnxKh-7(V^ z#j|dgC*`FtH6%wUYz3y3DtkyE*S`Ww|PlarO4ao5s1V^$y+SJaWf?@pW&ISu2gnW6*v~<=*8;C zGrnBgNbx=L53toMPwym#)A-`DTZNSMW!;^RDof!cmVGz! zFpgZm+pOCaX~}55ZVOB&0U44Kmz*T~u>|*Pd=*;`x7*WS6%n*al{XEy7)jc8y+-Xv(#OMZQ$!ufoa{>{odj}CZj=(Yhgqh)GASV zWA4Dt_Dt6b7FO)xTGF)~qj~ZDwWYmd^Ed2W=(a0voZ~SYg!zm-m4#DE;_r+w84m+Y|L0&*4-7xv|MRkge^` z{L+vKOV>MsSZtB(b;0~FbCEYb{-3a{7q?q(;eL}3<`Db^xLQ-CmIX(IWRh!* z?%z4v+xgn1MTLu2<>qrI)s>fnpn8bhmhVp-jQ1#U3z49`Yu&H4^6OmpTUX67qe^mV zK_D7-)3X>jUT$upXNZAZSI=~u zz6DVK0MqTkdYTa5+`AWUqsdL&T$F%R64H`Dsq5kQVmo%S?!FWv`K)U`H4z6i+SqkX zS#2tP&&U)~s{M(n$6VS!*D5h>Zcb0p3Xh$$Zsz_=EhlVjx!)}EhKJgUq_ziI>w8G2 zw-tG}aBS%2n=kNoZObA0V$wRh=sScre-!dXzmfTWfTQy~m;f(p*k(*^xNzhsf+V`v zlS*jw#8&dY_y;@^rp!@2!adV;$$*xNDeTwIg@8iOL) zCgh|JngSbffG9E)A7&{308)%eaXy}4!Ze$5_O@-c`JdLM!NdzT;kRz*Gt`E5RKy~` z>-_3K`|w6K=YCJ}<5%tj&FyW1+ojh}{{UF9+i#M6eYDTbTzD$WxfyKuS~FK@d18^^0n))W=}`JKXYJIcU7u1a3cEn>yT7-J;xN zloLl5um{|q;flN6`$b4cLwUCS3!J~rbuV%t9oh8(+-#K#Bxuor=!K_I9i60C6nDGR zKI5clk+#74C9}i)l5%G{`CHW8ntk7KZVS}7wt149a%9+XD3vU>nyt{nNk#bp`;pv?I#-D zzp|~{)=156-%{?+NIDhMa^0_+j#HB_TZ&EL$IzD}wBmwTqO+n~NCWzDq_cr$+R+$$ z+xA80SvJ7nThvq%(gTU0C|Jm6{WyYgqhL|I*&^AtRmR}*(FPH@hCo}g6X9XBlTQoH|dbHs$ zP7z*@3Ff!gBm|F_vFimf)M-Tki$HY0zOhuG4!TAM#o97*WkjhEeZzjSVmxZm#tvZh zQ_Fs#cLS0xcUPQ<7E=b$-BSiIQmIDUPDJPN9Pzcq*=*xOUx>oxZufF$MZi(ICZzc-)CL8ylHhVgddx;Cllt z01XNJ$1Gv8_ZI4Iw(X*n(6|-|53QJOv2SiK%cuVU2(q?(Eed4o%NmFE6m;D zV<_+>_S>-R+as5)#QFpXggLC&RQYu(4WJX42d7}|#S*tW)1obJT~^kkCe-cAdt4E} z@6Zf@mpr~d*#y!*pHW$EDOph|u4H67W=0>9qiIq+NTF%Yl41SDeYaWmWy0%nl1|)W zL3eiU$)dyLsD?<;R$ZVl{q1 z{{S%F&{orSEwF^B=`O5MlO+L3Ni->MQvM|ZEcY$KIQA&GgIULSHDSM5Jp9JV+fsJ_ z03J50JUzh`t+~g|G5S(tSfVtB-;|D;OVj}hrF=;Tp~Zh`ZL-Y2)w!fm+x}X$1qdxz z+w_s?+G7fPk|#?}+uT<8%sqPC00!9GHWm<6v^1vyQXg?4LbTLDD$1z^-QC`DwalRO zM0+=Fs&mU$$xGEf!;Ny=J8llrw=8m}H@u5K651qrq4*Yv>JCo6JU~~I<+ae9wW%m4 zQN>SU?sslVSMEPVQ~Z2CX&-Ry(3QSO@hP_^aF)*5Jb%X*2j2exGi`0LdUnHW+c8;o zT>e4b1;F3w7KqZrYu1&ym;e%JlaEQ;`-c8glr8B+$L;ugUWOwxx1nh%$hmtm$Hx45 zbDyxd`8ro*ZYuY*Cf-|+?F536vetik;oWZ$7D1CZd zuZ+_$dwtl9lxAGocv^4T8EX`L^hfB=uzdRtK4}H z>2x9X?K2~0;^!#V^kpiYB2;QRNj`e22`a8;qZo9WZl&v#Ss%9{ z(f|#qM>?e`sULP8^5Y&l1KPrs$LUoRSao$tRCNM6f)Dj!K7P;okz%%tmHn((j8(h#DFQP{qT5>Z=h$nVa6B%>Ed;-XJA-p+ zaK-sk;i}c~=n2|`ot z%M{k_K4#woMRN39R96$9SKd!y%l`l%Zn(eP+`qxuGx0iAc6h1Cu zw%nN3>5NUZ=Sy_nElvfOB8y400W^52r}wu|ITc)1Dp#VNH*X|q(Y zY64MT<^!%f3mcX~<>X4@t0qmabn;w;^Ujv^5TZ|JShKrmW;NuBW4(86&c(<(f_1WL z(6ml@*zP4pfkk!uu|vA;wz6&V$rczr$$Jj}05l1AZ}Kg#mf1z7^W|7~$`PV1zz~?z z!bVA5X{CL~9%qw@leFEceAb`rJ!ckC?!{B5j^~&@O2CSqu^!Lh{TS zc77PtU}r}BIitYyP1|a_hjWtRppE;BDsQl^Wa5K&566n`r9Q|!Wt z)X)KPT38_~O%>C|FqAqsDj-W~Dq6~2NeA+U46yX*3nZHZwOnOcC&(o}Z=b7C7Xy1q>#9Re7;qJ_Qp}+K=T`I(rXpE3tB}N7Ftb z8C!fDI*{NJZc-Icm3_UKu#Bov>W#?7 z4qogNYWqOfWgj5|MZN>Al!%&-Uod4%Sli2zT7%rN2DlBE>}@(|il82y_&4U;0ahq- z_i(r#X~ttDtw0g142VA3<5hzbF?kZ?kR2BWBwxpi4c%nsZ~$V{#1YxAI}B}|1U zyx}e?F?xCf`SI3|6G1)02%8(q>Telr6;sQqiOVc6J0ZB3?vC?_Y!=+?6eAEyAZ>xZ zuP`gk)ZjREvyfG$JZaKMPiXDLbR6U?#%ZOz0^BX=%n*9v#C12Da=5xx2j^9VrC zF5j~ZR^wd>WJhzD^4wBN!o_<%xHU&W+Y<;-BmsnR2;zKogW+90m}-p}yvgx0)6NA1 z163-N9i-tKONktySe`WW<;lFD#EDT6A8uRNO<0>fMFI5?NJV zhT`p#x$vz7y}`n~m$2@J*?!xPNJ@SSj8v*a3Q6q>K&bmhAC26&x8qpJY5|vlcHFo! zM6p}q#ERQ;WT_AJafKy8X(KM&3zt4;p%uLluI0iao$}g3kC&AX-HPR9i`4~Y)Z@xT zgijXs>#1a#5-VIXcWa2WJkN{BrO>M4v?b&9h4 z7U?F-lNRG`Td0sDIUOGl!a8xB@ zj96_uY+$tk&)j8mK?dHP!*gN3m29mlJ~uO{#f{t6BIv;E+q)%HEJt19%`v3RlBoVz zq47|f;;!wON!1{$ccl^M7kfx-1PKM?s8L#vL5jKU(UZ{z+s>buohx}f;+Os~wAz0l zJ$~#;~w|DgXwh*B!-j-2B*H5zQ@~= z@F2;`uK~YdiGJO41)9wa&3;j?y3i(rsV2QJxi>A@) zur!A0MNns+Ga1{ZyRMPc(Bfdq3sIfB_Z_R}V_R5hVc6t{Qm=-i+lwu_+i)v$bdbi} zJDpHtG^FVPL{S<+8H`<6!E$A7%e|#=2ZBzHAgFC?cp;K@|2^ z0OQ?+WvE>3XfmVTr|p-t+!ylY!YHoYL$UVGQ;g&X}a1Xw8gS&ZcY@B zjb9IbG5eQm+LNnArY7+usZvz7^YloKrQ6hzDoXmWkSN-_{rJh!# zl6Q94-122lm1q)5suGuoAXInbciX!i>ko@7H!Z^5$68dOd$RKqE!EH3&|_`De|smnJ|k_pGh1>@&Bn=Gi_Wskkp2zw^d41*1qggU&d>KEG_2Lue+{M6XdQU$VReA z`*XyWY@#QU>Lb9rJnN|r9|-7shfds0cM>URiS1<6b~klJQiLjjT}~=)V-_~=4y$GD zF~W4K#%oMbW@1q`%NrLPdKU+WxU+w9OlA`j)Tmas=p4H}`e{mX%^>D+kag)OOy%%@k(9p}WD-B+!Jh_9@ zCot?ua%J4@D7o7;D5B=WsL-L;p*`65KH2Y_OxV&oGtYO<)w_=Qp8~zY+xJg6w5GI? z@grJIGVcc)X=JKsW}$}aJ78_Qf?P;o4OIt8T7Rzq&mpe>=vXEz9lG-&w8n8FGJ&R` zphjJ}98>0HDC&`AXCAs11$T0bB4EjM>WTvOA5Ymv9b#)x3pCb=Tu zqWTF*tyfBeLyO$r<$V^sO59Dg^+}%HaYKwa-<+n{Z1_(?a?2h$ohe+oQ+tm(MuIiM z_WD-heDb!1DelB7(`2%qqF3(@gaU$r#8V#DsT5rWDkKq7qwEJ|xQTSG(-Svu?A@(U9C`*8(GY<2FiFR}&joPQp)SHZa76 zHAY@pa&L&7EYoF4Z{}vSTFGA#H9c`8nv8Tn&`wgJJ#4#eosX9J~1MmTup_Wk#VF} zf=2J!k#8`3*|$Rv%mk8zf}vk$*@O1I%cng+GjJ8i@hT?u+bXg;=gPMH#)&`TRDzIe zmu5YKX5DREjax{;?t4t&^i7dU=12-sLnU-H=uS2EZ^6TfoajwH0z}7d3R%+?K_q-d zGywg$F-c2-9OBc|k+`;HmfZBQxNeJq<`6=gXupu}10L)2lI`fqN(!f&_tb4T#m&&4 z6*puwljQElA40wn@{0ryXeS=(-eX$D9$MZtbdawe7R$L>E`Rv>T(ve7(hDmBqoFwS zF7=i8+x4m=bK5eo+vIg*qsI0vi~GLNvYq-Q8Pb?)Q*mObDIRLYXk7S+2iuPt%*lKb zJx^wfbD+ufWofl>M=9u6Pl?vlw#okhn}XInd=VSn9~uT2uC*DH_V7D7I;tKFDKLE5{n|gv2*sx9tr;Tcg_h(PZ zfZlDl{lO92uKxg}Co0V`g$HNBeXy}aHnc4%QT74`CWYHifIP4=JW!e?9h{eFUuWkDIsm*U#^j3#j6J9+OfsnZpoQzZ6JBFX2o@;&3aTg6N>Iz z7ayh6vH4rDRceCWuaUgh8Cs)9hK9{FAPppr+E*2s`yVo@LAbk%i`0MIFSk4Wo!0G; z+cFR`oOEhVUzWI~wS?`>b8sv#J1l z1yfKo(Or8nlFjWHEn}lDvAcqN7@OL}904YO;~bf5vAl@7S{?$EoFcFN~501H6$zuVooM&lXVTK#Rfgg176k)xz( zZ~`kq(w(OldDs(Lpd2iiJsBf6KWi~MJoP^G#s*1@rFc|nmrj_@PZsLqLqe~>TwU!7 zhcZMPFYS>+R%2?^S5hmI&u6m(<5`=yl|t^yu`4$0xJY(l+8?FTZLpQJ zVzc$QGm!CZVM=fm;{F9(l?m@>+*oofRG}o1Kma{zdj>t{v}Tqnsf69r>atPX_Up_o zvnJ^yZ`>KSwt|CoR2fN%lDv`&#l0O*ekJa_yK=;mO>h%tvo5;PmD#4Z9F!pu?2~1q zDmdI}3e}cmX-;5c3{RgJ-0F7*r_FwfS&s5r%WVnTZ*A9 zHrtYX2R*Ndu7X2`@YHKVZ4GZqojNRJB_LG%ODFKeS92XSEiCO(QPt3WSuC4kDwh@x z@V8!HX;o0SH2ur$tYp&ybtd?ee4G~n$ov_tN@7Dh9(~Q2`c#?nUWaUqRFjy~Az@TL z>=K%OCp>hADcl!W@!qAYXF_IpElO_ZcWz683K@+oBPxBkiQ8V$6Sn+Nh@I(d*|3^s zCC_9K@j8IG za;Ev2R|hEV7;3OdSF8uIgIsFgj!1e-TC?mchr z4bOUW&;IdO3(L&5uX}5FC;pBhmFy*~RY~KInJCgypq!p6TL?>+UDIrgtjPi8NpV%o zf#szC031d7gnzanAEaHSWiiif+FKzKBSg7wS#vzajRF)@HUI*YDw2Pr2=^PW`$_Cz z*izNhbEV!nv?*;y+Y@hYvPw|w%ZHMM5NT3~h)?mrig%^QK$$|sI4Xg?zh8uvHDlS9 zYbsap$oW-DeKhGs$y&4fG1l?h;>ey~vVwe#_q?vmsj0W;%LM!kr7@5_!Gxm^cA|63 zl?HO8ytV3fQl%&ljn@{n8h@@%5!#QD6el@8CeE^Cu(V{FgHG?q5VXQ!R(4yMY%=(_ z>B+>S$@=AmOYs)Q4@V{L&ZZEu}V&iV0X5j-b@ywPc2rr2!Pg6U3aog`6^@9|3G~t%2gCTQ+jnD!Dqf>ibP_H^|9+Oq2CBT_P=` zkhk;k!+T?6c&kRr5#|bvhIJT@<~Ax-xjGj1C7pJP)v;39$3Ch#5!9j`nRd3MS4u{f z&y3T;5``1@;=cEGY}ooHq|5tVgTG`_Q(TcO_ZfXBy+!hy%*fnxra053B}}y)<@?UH zB#*Zh*FK|9g^Nv=SpNV_@FMG#eSRlNc$?@OU{Gdm_iAL7kO^*NuPLGLNGSYqQFFO> zyoBjUFTgr`e`;a9A5pKo>Q4uK9yqsVvXgGw))*m@f%Ae`&B=Af3}yPU5jFEpl#7!Q$xY}0VyBs_h8&#r5O^JHObYPEbsdY z+NG#+qIt9WU)r|BC;U8if@!14j+ABXJ#nMP`ekk})XY9-)~w5qO8z72*}?lV*0Azz zEWaRhea4WrXa2A_ys_9gknl3wht!;l&Bu|60?s>AMn{sR1{{VSi z_m`|5AlhNn#hB&Q_Q9ZG6hZb?Mwrdzcm5i5wwpa4Yx*y{U)J+bUe{Oq3WdJ?eYJ5* ztk|}!a@nX$(odSIc4bO_IHJt%``5TZCGFJo&7bKOJ|?rfCjFWfJ9he<-usL=!%|ZY zBbhl3fEjF8ydVnWDUo{Q&HgUOjbW};ubb77P5%IRT-=KN&gpXuMrbK-NOi#5Xh&L- zqtIY0cypHoT|9_eT9i|jeQt4X%(Y(U+OALU#qGHa$Gsa`eAv-mkgW}+*UyC|N2h)+ z@q1;-RidLSy5Vi|lz))DVDe_=yzOLIU1M%-+r7#o641&^@4Uo?r3EF$^fgvmMl_h+ zyKV}s&gFL<*?cT-`f_@F&sLD4?wh0XQFf7evEAa!aIGFh=n@)273LZO3Q}=nYX%Bc zS`_&kl;y;5?MN}LcDS~QMJ;@$sOejaB})xDq$a(lpjQ!OwG~Q*SZ;dIpX?lEy0>Et zzY5N_Z7qbi`_#kokkC5n3x7#J?1PF4<;6ZGhaYdbHNco`d}XyPs$~+cBYWJDr~Erw zqgz{8G&NE2xkK&;5KiYYVQUMg8psEdxo?d)X3ndRBJPO0eJm$b4WS3i4m?QsP{V#A zPxC9L84bSe$4G+Y?$#`>Cn5RU>Mh7S`Equ*6h(7k*d5f{GZ-Pulju27PG%52iTm-Y z+x9DV=_{I;ZOgbW-h8m&Q?0$Q_Xg~kC+=B}7oJwP8dHi}t`!>9YEe1j*C#p_l@^E- z9|Gm)uBGPUJ`9~P(5o$j<{p56sngsjZX_nodfs``5RbIf_6%b;4boDr4XzH=7$s8K z*zMbAXgb&JXip*T%+l1yfojDC+R!Pc)&k`zC!sV?XvTh5CiQ#`Mm4xrktdCvw=U8p zxwv-(#mt`SPtDyWY5g=%cp5+eBz6EXPn(exsErmKuM)_qduxw2YrFG&$rg>c2o|^t z6q|LBq2Ot>(y7E^6m)xKPM<2Vhew`{Rixr$o+v23$6?xeQj zlhNq}629-V861i7y-UY+zf4MX%R8p$&s+VwHr}=@Y`whfXq#(oGy`p^$DHz`P&4s8 zAb~-d4b++noHrh7++3Kqqf*6kto^m=hlwuST8r8x?ib^#`_J3np`@`L)>1?yf$=Tx zEe*EN&&y7K7AU0)nYa0T32jLwQ=I%C#`}}iA1S@cTY@vO{d2mN?~Zu06Ka618qP zw$k)gyd7VQO?Xn24sO=>o08u37Ml}Bb z*!{*Oar?+7QE2&%)6039p=k%Mlcyekf8u4dTcFdM7HcQw?bj9BK&1XffD@8vJ`j43y(u?pG zZCl5ZR_RBX1TP?!E-GpE(BP{J*t+1n;xa%y%Z3krm5Fmf;>!*BMFG zQ%tC={>)m~?dx!LO@prFyLoDnV(k_VY)o__Q#ORHBmv-bg$}(nV%8Xz>8anST=GPd zYO->^lp?mo5-XA&(hs<+5-dAu5lcF4>Ml%Ye_HJGZuf1;0?f^|1ybK!Pt!_APOB9r znP45l_BJ$YC-((m*}Z;LiqN53@13_c>bWm%+U_pgnt6U$OG%)79E}o+(!ShO*p=*= z>F^d;BWlEd`4hhoaHaP1@^?MVlFFI^Y(Q0L+6b*wYf9rsje{6Qr6uggIcu#+*L-f9 z7Q?bWi&9(G$nv7e01)e^B1f?_#U|mpr#?%3co!JkcO*IHS6#EVWNsUW`G)FT5tiw2 zNpiZ~aYt2sxYF&Ia^+2#%>3wcG>Mfj!H+eD*m-Le0WB#&<mG);%N^!X_ zdwN~^@vSC1zcA=nty8Ehj=0Lo!)%e{#-b>TYmz+3cVx2iNGM%sLWW?1Dbo>O!&hU= zC7^a5e5}n*4viD`)w?su)~5Ep>X_7>&u&3Yj`|2ml+YP8;Th@_$m7bl91FW%M?5vQ z_@mx7e9Ig5Cr;%R=&c`_7~XcA+hoZN1tA_<%3N501k-@!q6)zHA0&8D*2SDI)H7;{`5RXj$ZK zQ@I{^332bO(?+sXoRpdne;iv*(ow1^mP=YRIpWKP>J#Q2BO^}S3`N7KLQ0;VxN) z^{DK9%Jw_-@Er@>+aq@*qUjjy%i$CkDD_>r^&G!Znw2;2PFRM{K2|=l^*}!Cu=8}1 z=ycG62h(9CW6g@~@1D?$p;*1ycGh60`P(%Y!M)rK0@ z9$ne8J6lN1efeN1ZgcV_hB{i*;qBUEuePGpil~g6tt*dYwFH^-JgyNf;7L=OoLZDs z@PpaeOla}xP6uPOAbBpSNK0W(II~9CPGv{940E`k>cdo#HEpgi2#pT!xLzoAWGTB> zN+!RWpTilf{^Gcsj6V=-musTnICnn9v|OU6OgmEEc+yr8d(ZS{_WA4Rrh0Qhn3ZhL*a2rSHvp{8?&Ud9ugHUOQwj9gp#6t!xPUY z7L{bqoT#-OOj&1ITrJ%pxeerGT5%*F#9}KsFuSZ>O1ztudJ1VmN_8PX4$;&2dQA-y>V%ElFu=A2T5Wx)a+{dSbVE++mAro5YqDT#1Kza4QW*ZOyqYPD%+1 zcsij)WuH!%$?g5-=Q>FTJU0bemEGN|a*H`|0B$xiX!XFfo}MFgtprf*}mUeMdA3wDTu2fNJ6@TQ`=7bd6#teE<$b= z_!^z71~OHdcvzO*nlfCA6}Dr9jS;c{A7CBW^Bb1ub=G>CNnDM+B0H@6ip=sAGT`to z6wpTlQga=o;yx#F@; z+tG0dQJQ*%Lue^bFCQ5ss2-SaNH#L5t8!EUiq2%@T=siuh?thmXG9K1Qe`YH1b9>_ zk?Ge2*o_%I&Ll@N-dUDX9WN%mad8>ZP#ROzcrj-W2`f*R47b0sH zQe=|#(nflpyjs_afJG$zNr3K9SQM$hZPsSHwy`C5wUZ& zFx2SpnLcAES__RR48S6|w1vrXl=mSODv}e|G$sgBDoYU%G?b!%ejT{4ordOF^_M8n zYjIoSyr#T`yyDG#LZY}i;YyrzDmtl4fZ$IcNr1>qHbh`@NB~e&W=M-CWEC#B$`s4 zxGpwQ7A~e*u}hZOq4EA-6$Luyh54w*i8o1sou4*RLvP4!mdT=*s1r`{hTGhrkmX`k zCoPQzCeDlILvStUD_K3@_kGyQZdJb7b%2x=No9@bZcrp!WI9-BV1>vOtviM(tZnMt z>Ra5*QnppS(QAy{sVG5IrMgLJKsxEojpg8%*cohUEVMO?-gFq8XhHX+iMF3Z+2zVq0D9k55u$v+etyXlrhM zA>X@Z66MQVFUCS@>YH6oUBj*|aJz>d`=mi`ZL_z;V{G9rRpkt^9J;!X2^wfY&#z`I zbNM=TsSUMB7gFBM-W90?#r+b2sTI_m#$t!F7+FRxbB)&_bms56qm%!H*s%bio?Exg3&{QfX73AeJ;*Ia?lf zUC(w^8*NnA`IIU@98~3EDC#k)76)i=%fuEC+@+<)ffPvqiVtDNYcFNBbKY5F=1mUG z;k_)|n%vVa+Yu5=396sdK&1heBEZ|FW_)UcGA{E+fms*5lNxk}QtH|*sDvpBr%!e? z%Lkb!LXXs)P^%AkDpZsxxU!N#QmdVE&nzVH;FrP^)sVMvZe{tx+U_ffQQ=BfG?CLh z2X5z0)`Y83IUI7+$sT-^6y{2a6+O5+^=ITZR$Cg^DAwJ!Yz>8Es1D zqNp^4GrOG75)Nat6Rp3@Z!;cyQ+YmKX$&BTP<%$D*0`?9+hO7@yYh(t0NipDE-Ix< zv?gQjrifD0n5gVH7XsfTs2y|F8jvN&<=Ju1A*8gTYPNe*jYMNqRkv0F4&<~Q3sn-V zfTw;IVmObxSDFf64ic4iR~30780=SF!Q`REwy+L)R=MCcZ8xeVjOvEehl{FGoJny? z_=!FEu#8efR5f9=y66i9DaifUQ-__l40ixJ(g5j*?g}5A<1i4F6onH?0sjCd7^3QM z6ic%X5EQ!ij_euI5~Jqbb!=%vDyFIeph5P1n6AHVCKGN|9gW=7h`v*GabKBa<~^eo z!`glV3%41_abk|T6n+tc3Hoyq--&d5DNROETB>TSsb78^?!^q1fZJBPlx9JF#@itx zjqA){XOR|9ZqpNHTO>TvokH+Kb;y$cNeqKT&b}OYy{E!fOJ5WIE!yarx?c+Fy zt@h|lx>t0&raNc(b0fb1#b5H&`3(xGzZQ z{{V~gq{2c{l&QXjX&?n^KHlMqciYzBT}OLzMRMosB;C#}mlA!o2HNOzdA$ zr8L<^TR)n$N%z+o=~{5&Uqd5Nz|`2PKcc{lO)8K8z}MqYn>Rc~Q*=WOc?fsJYJJ$L zc+zYw-JsZ=cp8dS?qAJZO=ApCm=dGhUvf%HRjrXqs1x|%_wjTHh2}h3bLNOrSttUZ zsX{`s#T3$mpmU{0y}Ay?BP<{>N`YTkG@P9V&Guqpm{|PW8)=hJ&nFw{7tr4eto8!}3^P z%STBBC9nlgW_Yc;fZ7fD&Q+j(A0}KG(9A}P+^^}6iDXw4_xp0OsWv|G%Jn8&4(qtx z8%y_jsbGObM_5Tv9rVEk?=C3Z&#jyw#D`~I-zWjQg%YMz9O0ubk-D7_3$3#J!qS1CMTaQXol)UN> zgbZ#la_mkuOvh%}y$7fc&`QLO4lFnlNF--NkE1@1+*Or4Pap62OO`G865}Z>EAD)h;*GBW**CzZ$u<9f z%vD;Pl)G)ETU96YLXx6Na|G$HvlP~)UM6=U%vS;*y#AZ22s%eX4n%g~`*QXb7ZVy1 zpIGR&{Hw(!(}Bv%y=?Lw%weH+5(}i<#uJz9^6(+(Q9H& zAz7)j<|V_W%_lUZicY z@Z=km!id|Bf6FofTGHZ|U$I!H%*U}8?JY5~a%JW|FWcL57THYO@I%6$Q!cX6U)oA* zim2hI!+~V8XH9sCQMm(uxhX9~-7+G!06LK=rQ-q&F#4S z)Lieg=eF%ao09vhLV?SokMv;7OLi@MD1#>MMa>%{cpEB0N>w6IU?!?skgU==;>#y+ zQdFT?95-E9?tOBX8JMtTIKmhjR8s1btatV8#ch=iZUHlHltT{qhvTG|r5Bu4Xf*|N z?8TlvuvZ;*7||{g(InSXNlkOESg>|uKy^1!out(mU6{qprUQ8aCs%ruZ|+~GLd;$~#YXp%E|xVW*EbsDb5 z;>b`}O}TfvIt>!*^Aj8aUglGaeoU!R)gr0g#adA6*VE6Fc;9TM*&@%v))RV;`r3B= zc5nAboJm+Y88rx{I|ezfGBm<8L-LP%iyyHshZn5(2&OAnVMTXS(n7iiX-VUimsc%m(m{Fxi zg@a#xaLD7^Yg8X<^rPa#Xx+>~Lxvv5x9d{%q4`O2dw`CI^~u0*8qc`t=H9M|KNom% zebF98s7kiRC*Xk$MMY1wq$l{{>c%v9v7LO0=#>B-*qniK9&FVQcupU&G0j)k3(tnNyzq!VgQ6Fz;N&2KdW5d~B zrxxqFOtO$dgg9hZE|qZ7n5CY5+WJvK5R+3tFyDx5T_sUw?rgKJCF^T1GKHyCZN~zF zs;bmbWGRS4r*fhkr3RFwx8X)$Q!)>BIV}l9(>=zJw0MxxE2(7Z6+Zk)a)XtrNocAd zwRnvD*5N>XyQJqy(E>5~sVXC|G{&`)>;nVb$+Hz+V2c|TTU^Qxp5kxWTqo)aknVO{ zX4Bwp?{Q7x=2&XxHg@A`8(wO{0+jrSZMUQYQgw8~WoFp4m0fG)ukJ7L`+oH*+FyG8 z)jdsmtol#$ha&LDr%ji}H=A|Qt+#H1ceE>vhft`}(-b6>kb_bQ_-HAD``5XnjfDu? zT;5^(M!^h=ja_`S^YI!yQ}h$Q+!~6$N8Q%;UH~Os$CZFdDCVISYP6ahZu5sC=ncQwaVItxDc?YmExqs{jBgkAao zae$2D3B0h=%LF0sDaC2~)t(QA)!b{1!B>0s+y4Oa5AFAbw)g&Dt|+%VuKm0|c{L}> zVlikD-BPoI{!e^s)pY20#=^CZJxLHz=BXIbdyLJ@tW>LVRhPBYcQ0ms z^$Ae#R`#UBl7fj*9ZFUfk|;ufs373T>{lo8@H20?V`f#h-AL75nQWGTw>Zx8a^lH_ zMPb`am5AEOsU|FfwoQ9{ID`KHOq5?WnabR~kN6iBzNN}*mnspxA1N>5g{x;L@+S*O zgKy@^(YI#GxIWBST%8%A71%FGa5T`k#|KD4l%StA83XYEGsWK7zGdP~TgvrDx=Z<# zJFjkS+oT=b1nt~Pt`AiI0Q$0O-8|37`%t=zmHCq)v0s*LL2Efzpzp<|>D~CYXGH4# zhHGZ}d%5N$;|8MqUWH`d{(Qj<>Fhl}Z0rq_4_MnyOU3F_ zoz=^mg7WjqmvwpHH0H9hqxX|sXQz&v*Hc!qVFk+sk|LRx=@J5+dO-+Z#Pf^Niu+2< zF=1;Kyb%S=~H~#>sE*RrDGG}iNMS#j-k{*i@-=2^c&?KTZsuFS)BNn!Hd$(4K zg?2{!X4=)%pxmkTLQS^Z?5-o&3!4<3Enlxr2GpeL&{l}eS0U5|D0{JMjoUIyQiCVF zApR18^5?1EVC2J+)eY%(OLK0%X3UQA6u|P?3aZ;RJu>aV7hStCZLzR~u1O`S zZ#MU)&GiyBx@P>nK;oa+p0`PYo2%j3i+U>Ce5JUs%Z@138R{vm0TX$+$wnI&-->_rYCZK9v;^?6|c~gX}sVQAjl53F56|=i~^E4LlaWUgf1|KPN z?-%i$NA6sa$R1LV3YfLH=C|EY{EWv(Z@5zySX(^jt0=b~Sy)ZKkL@jo^pw4%VWm1Ziw{bvV7p+9Mt&13S8Bb<#Wv0dk$YxAoRV^LXFoU2YTH zE0ZbrR`cuBdrHsY&|>1<^=VNer=fQHt0w*|;_I&JzFT(Q&D%1s*XyYWhi-<#Rfic;0z_p z7VSsW6%3i(dMyceXzZw#I$SR9rd`OIl%*rfXp@jY?N_%H*?CG|4+BMm1#T*(otqPG z@fO{-@6KUmf4xp`&DfkKlcM7{JxP@{2RQM^TCX$ME0!oOV#?%$qn{X+LOk8w>K}K{ zlw{6-RWT;!k6xE{8!&Om4WGKBN&B#PY6H@I|iT?Uy%{ zrC9_kFYKIUYFKk@SzQIUrIzzu&UJGL%Tf}0^~)+|a&J4ctKev|Caon1YQZ_DcANSj z@V^q8TBf?S?rqbjPwtCoQEpU&Mj8mBYnEN0XGFbMJ%y#aYR#@`abb<4ZnAoG;_H7Z zzh`aj^ST!)o)O!!=$R6peWHmM`MQZyY$3XoD2k=30aZ8#2hLNRlvmsB_VNR@kgmz4 z!Et}X?fgz#No#cazvZ}l3va(8b@Ba*;Pk!2et5c;w^jYG`^>3s5B=eaNC=_KfRxgh zcH_9&+Ht@C0A!c!{%`jdco@^jxnJ!^Z#Wx;?J#Xhuzv$MdLvD_?v}A3(}y43?J3W%j$YgQy*Hi_gFL|W z`@^#Bb3I#Lq1A5OE(?9PZ2Kw;ani3oOy!3DVmd0I($LPlc^1rPz5@L4URu8s>TbRa;lEJJUOXE>E&8$#AIp3Dh9h$g6*R~UAtYr(n0ITmWXpB8SO{>Uc z&b2r$7W5U}8ZxdacH47pOzUmP?*Oq%AJZqZ5?NlfR^{CMwnO$PF<-SrSd5P_Gfyyz z8skY%N8Du>AH1NK_Fji~nyBlezH^|0a>>Fo7{g^ok}Tr%5LI4;+r zp(l2s$g-G832*{Gnw|J+9y~4o0JlDMq2SWe2BJswW4W2jiE#L?I$wcob|XdBVu7tn z2AZo=O5nH`_%T#11*Q2@mX@Q(TG!j*HK^;^i7oL9xe`x-Wj4kC0OGTni8Y25Dfk4B zHJGUJDd#&9k=}8i?k?oBx50D#Pl7KtF5J1A`Io}DoUipo>O$+vo3=k2akO=tBX0=t z+GLVty^mR&DmckQsVV8F7Mr`Cm1~zh1#}kfBYmzj*Od48g7G1a-jQh>JzrPXpb;g8=YAW zZY{R`iPhZRmXjhNT7+OhUZjf9Cl%Qf<%xSvWueV2!zup&-fk*=wJ`I~IJJd>lT|FM zw-r&6dfI%rJ*KH_U`(eYQ)H#i)Tow*!M&Z#U!Zz0M-~I^gk)0o;&3~v5V39 zA8si_R^7TouU`(M(*xYut}pIPCLO~Gy<)}q+tY5!NG&%tlCS$jQ~L1}!JIsTe^j>o z7@%+V{EB3iSE3XRX^x&esz|aT`~l4N6atd)N=V3RAHxGe%O!U}jIc(uA2<0>GdP;u zT}Yg&B1xDDAx)?e{gaD3F|MHVV`^NKq@U9@u1cW;@WsWHsYX{_jH{uGX6w4xo>J}7 zi!HrS{4xUBaX;Has+0F(JKJ@s@!}O@biHYN2NEt@UvQL(2>$>o-IZV0Z7N(B6<*ZT zsj_{Q7ztawV}__MQL&Wu^sp+Nk*17Iy8`yj+XBJqde+5VhcN!?02H5RhiO4wTcGZ*v-fJ2q{~`ITu#|Q;I(G)9l4I zc4kIQy-z~Ia~zjj#j9{Nt;P{3xG1Jpnx36O0}FvGnDec41eUx?y}Bbyvs`3~d2yDb z^5BC?9)q(Ulegza>=W@FYSdnXILLNIX;QDs1%24Ea#YkOkwlcXM%#>%-Qxb*WQtXb zYl5lk)kFF*klyp>%gY8wE3vH!-CKejs3?N+8~GY&5cDTTp6UUNrK=a<>X_GhELpYO zEcU1iYR?sj39pH^P?YLE+(Daa?a5YTmhvK=fl<3;OY>S&hJv(FC$q3(Yf!k?I#v2J$OK5pf#mytr`>r)21taS6Y(G zGr)T7y@_YK!~N?0cT_;;Xoo;(JMo{*-LBi0Dpf7*VcMK|gO@8d*^W^b1j6HE;wPf1 z_F=Hgzs5+B<67_$Zfx5v(0umTLyw~*_yu(0+i#uOO0R)iof~nFq_5ka{FF;lS_V2+ zYN7o@8*DgeXPc`Uf4mY%L%FLulZy_D5!6y9;`d-Hwsq80+7_bSRKlsK zI+c7%7>ZcYsnEM2bnX}2Ys+mVv}NNJ#4idJt0i~j6kYCO)EZ@eXzEU691gJI1*8y7CZMRwzJ<{V;sMfo3YlaKGNP^NH**z|k`majXs!Upwd$n>xcwy} z-|t3M`SS`&C06+F#9`nc#^A{$K z(_)|bzC$VtWo1g~o_HVRQ{p->VxwG#KCO9))|BQa@sjVj2T)l3ld9823hwwgi;P?pq^DCV5i-`5qndhr_xHb3_ZvaT`0 znp%^k6k$2$X-egQ@uL-}MaeXX9%Z`XiA!6oIHy*6X;G;^el8_A@~Q(czsOuX#Gz1~ zZoM@jKz<_(=R_!aTH{`s31~riV<43T8h2o6@`ge+7u?(Xf?7dp-r`eHsY;?p+f#;C zBjas=yR&x-3I%>kO`e5sAb&~1b3}JaV9nV}KnWLkjgvs6`I^pt>@i&OLzEwj7H#RX zTi;4%UkrqtiD3sXwBk9=YryBo__5_1Z|wWpl>A$q#531IkdiXUiu>_Zdvm=Pz>!p8g>b>K;JrxC0-D(%u1+&gP+unje4vPVoE9C^)Z zSIXjC0YA@I7S~1+-h<)AFT<&^XZNF6=cPn4#EzG_qmCi!-l zv*pZD4g5zj{4m9032F`VGIAVN4Y9}@)>AQTa9xsJP)K@;C>heV#RloVVqw9#wL;?q z2L1UiR9ncNnfI>Ov_-hxOLxgmHXM4|*);^vf$ztfOX@x)>%6ewZeiNjp$=w28_Lgq zmp}HVEUwsGSqVc$3r?bPrD@xZJ|6dF$C{-*>??{?p!C?@jdx!5Q*PaFZQA1~SIa&p z6bE%pKKxT|pHp$M{V`Pw$L%|pHDuQR0Hrt_brh;q-RZz2d2~q#3hRvZzWk1SsF7m3 zW!Y8mHH`FazR_wdb-bkHg^a)h?ZYy0y03smyNVhPD<1Wp%%+lE31Ydnp_u#5DYEu2 zPK!{>JEb2I*zFDZYkDuq-Xx}Ak_B2BtM0}JaoMshzG&!cFt;emG=3y8&YW|#H&j_t z?QvB5tOBs1s-V-QJMpLOP4fMzB_kVeXgfD~tIK>^*A29smgxhD9xm4pg(p1xwl(kG0(X{m$-LL z##-E4l6;2Jf4T$!ud^0=eg(DQS7zqilHx|Q?h>S{TZ=k>E_KD_tW=5&o2+d=duB7C zCMbCHRYh_*w6TFww(e3(PA0Smds zf=Q=bSLEMKa-f)6YgRjA-$0^J`jqLfjYmJn6xX)JElUeI{DZpRyCg(qDs|K?0QgQv z_+p~=*vm^>jZnP({mD^G=OBj_QK1DC)IGQg&MQ+y*E82C5O3Mi9*!Mu5*uOBMIkA! zp3cBAz3u!}U0ILaa$KsYM;lQ|6O@FCREnPZW9o0D@}$~4d%wTNiEW28=@iV7kE6M? zJZkA8ZE7kd_=QlWDvdk9G$S5e^(7~az2~6z5?st-s^aW_mAEq4ieSoVz(@rn%5W~Z z`?28p5UdYS#JS6QrHS=z>NjKZX5+EO+R~SJjcT_;GOs#xfh;f*>tiACk&27%vSyzFUARf;&8}QuaACbmkhP|VCWfQm`74gKoa?Aew#uyr{{Z|c-SQJ#U8ZED zj*C+&38D6=0gE_pu#XlAb7_~5xn%VH$P?tna&@(~BPl?;=RV3vPOi`wTmhfl)~(xe z@+86S>B?HQL!^9V!Mu53+lXD#-}4gP0C_Dul_X_Cb0aMA;v3I>UY*}Z1JSTH3raK_ zS8quB7WuiCxGwv~UAETYs%&AV-6bI8kfctt`j5$)&o;{^*QmbQ!dis0fSlcRF zdj`vKb96F-VMy~0U0pjzcd+6elf3R+$*t!|i=D8--Ks%VoYA}S4?4ecdAGJb(X_Tz zlA8fE)Or;xWO3~lJBM%O(p5a}_S2T%3IybFCo4v4<*qh_S^JEck(kT8l{j#AHtUNw zRaOZ0+wpqPZoRNK96Gf#b6FyEb+^JScj@!-Xlzc}5F1ke0CVJ< z8wyTpOK-UOs69L>7&gvR@Ph@rhZyma6H#E};^IDQk<#K5O=^j6km;>^2|3}{a&Obq z4My9wkIhEM5w`H8R3y#ax9g}*ep@gZjw{##o~|J3XFmjjxiFWDVQAC%ZZf>++g9^< zaRVUz#Vmq)W*AN-`ApfbRFWZi7*TpfvR>L3&Nr7I=OXb#2`yw%BIza4qMrPQDRQ>_ zlk2ayZ~KWp5w!cc&>OlE^A zAW#xKl#CUFi;Z1E^D{5F)et1T<+^U}*6k`xwL8`Ko^c|hI+C+YYBBcvJhe#_Q_sJ_ zwlCP1CDk~%%|({zBU($k%4$0>E=83Re0$DpciLdzS#swlJccc@yt=9Gz?0UV+zhS7 zy-Nr-wIdt9?hd-{yN{TpldaU`3HxzNwV%3UDR5Kfn*>>dk7U}KFzE=)i z=F!FX*>?CX`On;DM^(BjX+=tkoSEgOf|xe{0C-`_Jg}8n*1LBiFPch-xcwAL#oMsB#iURt~_&bw{qpzC*o@C~B>u>iT4TWmH~KGg$+o+KFOn=b=a_onEazbak!OO4WtK4C&f*qUL# zBW-vSEyCoq64!BCj;OySZYQRtD4OT(#H!2}1UIWM7JS6=+sV*?n*1v%1mYW(=spst z>$B*UDYeObHiAZDC3Nh?P4?7_40&j4dXpu$6yB2R5J+w6)YG%CyAj$F^eQ)!xjL$b z6s=uViAIN6YHHWwNNoqSn&tN6hecJ20w_Nepn^M&2B|k9HK1hV=DJdct#Fk&K@Zz? z&hF){sIe5W38hpaAScJxwd;g>6ZHc^*>G^PCS$>rsFIqflV5N+9Y}S%RSg70juhgZ zQ>sW&Gz6T5as#d-o1)M!8Va;MGx|OH0P!~cAE(C^Ms6=2Z4H&P8ICC1R*k<3I+8twnBbH4;K=Oucq9)Ut8|vGPw}Vj z6S*JOClQEHts4^VCtPH{&{mkrZI;^n-9mF1m3Fh24Fa*PU9m1}j<4}0&4tN4ak=g6 zKF;A`m*1zFmGmp7eLFDBj@O)_Mu%r^^YA6P5MNUP?VoqFzcE>~w{xgHeiMoe?$yYG zt-2-)i@Msl>5!+(>=2Eso7QTZjh9L2L3E5&J)?A9F)bc`z?Z{vqJ({`%5xK7+m0fYDt0i6duAj!%B6k1 z$8oyp3vDmF-pt<0y`}Wn=QVRoRcVm6olAi#?j;0(aQN-`(&4GunOXK|?kZGr#<9R< zKs0~==}}xu3_Ee)io5%r*)2upnQgRy4^GaQAx;TUskw4Zjcv!sJ40!-r|);RB4`Bp zN^v@g9e{PkW>)QD*A`pty|tN^{{Sv7o_rBhx6iFTxd2Fuv|<^uPzY{Yxi?(;4!Ekw z?itN~TxfbO)AX|3T6r`3m-9J2>$}tkC2sx!I!tTDHt1=Sfp+Ah>Erg*BxrPAsuDna$9n$?hn}Lz+9|>P5V_ z9~IblzQx+{_A0j})=jOr_Z>55M{STudrL|U>Q`W?#+MVeExJ_AW_LWI^eAT{bGI^Y zE;HZKH+LgluM!b)fQG3c$88bTwG|*DFP4xM(wNra?KW-bk($omuTP62i$up)#M{AW zj+T)B0Ft)UO$Z@EhwZ^H8VkaJaLJs!**o!nr!G(B4YjuBxn*-}ZIHrOc-|M)3r(#J z6>6w*ND6YJrtr~5swKH;^R9Z~68-LU1r~RCeZ#O%aJMNbl5#%#% zZ}v^Tkdicym~^g7p)~lC5)Lpqz2SEIrMT$NOWXSb7T4y;v36_5?6%uKW7;sG@jl#j zK095CG9o_mMMYkgbv(1})rzl_%DnwgLs0EuW5$p*wQYFHm95aZ(j664mo+-#)J=0z z>P{{3wnnv)NtwRIDn*BRAD=d-rJLr^(5s~4A9y8&Mk2^$Vb4) z=659S)h19ooV~%jt}fpeOr1Wd8tGCzPXaL{`OIy6YjqVzfU! z^u)Eq*U&iWr-Tlsr=}|}Ua579h@+{}eiig(ahbQ`@&_+&NZdolDmu>UxFo$g;uAtz zwZZ=OQpo741P--09(N@rMWJnvD6eU1UFF@sv#jrIZ%f-@{w3PdTQT<+B1v@;H+JD~ zp;PjbDHW4bMp+`2>M7YQ`>%QK6$W5hBDAOGOLyD{g(IqoAOV>) z#&afey`iG(+sB1v;8>mAxwa>zHe|OtWxJKVOg5S#8Wfp(M`S)zQ|2iIeEq?y=8!@V zeZ4@IG+?R4iI0@?v&8999_iQubVHnR_4KK-LtHoQg+_Nj*T|fe2)@V^#Jn% zI+ZIW9Az&nm*vd(8XTOvSa9W!#X`*TCgy=@wMpack2OzQ3Xvaq?#2Zbb$k$&Lxi-Y zAxxyuIJD3ogk%mWH!QTDBzTLo+i7(!a3kHVwKhh}v+q6i$x_XSjJ7%e<^9>EXXac{ zO5$yMJBuK@CQ`I~+M5ai2g?8{RZO_}uFbA_{r>>T6kEROEWCwk&&B%}oBNJga|bJO zudH5Xoo2DL?Ynbtw*FpCvOAjE+FM1Ul(P|{gy}<$q~7pMOKPWJ7OVYYE!9Hlm^iDi+#cZ7E9@^DZo^3l|?s=TU@ zfJvb$aaByxQaq&*==^yZX-ciz z8*ayGW_IPeDMpEEDiWfNf8IPm=~6up>91ov%fB;w;DYEp^ zU*k_#{-@>c`)fvyI`KpW$dSUFjkk8go4;;aXRbG(4i&meaf`1ma%_mCnUcyaM{ z*WFD$z7?<6FFnB7mgydxINNcjeSX7qRnOGlE3$*5dh%3KS|E|8Q2l8{oC z(m-yQ&~_JW^WOQJ%8r_Q65;my&o=sj-A|{{wZnnPmz#akB`pdIQne{Yy*q|IZP9Q% zRVL+Ar=F3n%4=vxOTaRx9e2yjili!?KFk#^2qq03Mc6l85t8EFpj9eQ*< zcRlf$+d}IZr@Y4#5#rJS%xOh)!)#gCQSNm)V{*DjlR5DFdeyAeT91El#e{9N3fY*; z^5NVTiYb>_c!rdt)S%M@V|+l_lKitfXKg3;SdTp|0bWuE%jZy%on1ie#pF0Yah{ZX zj$ZFWCMM*)B5Nv&M7aq1nw19t9TYM>a5K|@G~$q{ zZ;qL6d2O~FP_ARNcm0?ab9EgURaA7?EYCkRNNp;1qy(S>PhWN!&dDsa5*w`%VDnDVbb%TGhqyN4zlMk2C;I2SY8VzN#Y@JC@}P99dqM_Mk1l3R#&^vmfqd8}$dcr|iwQYvRV`7x#wnQ&+xD znvzt%GD%>)vZsPH%pyKy-SB}h?F5I2?J0Ay125Ep;VxXnd+`5?bgV*C@y@A zw}Qiy5Hn7c)KrWClWsHO6ARLh1Cug&ZuE&Oi*$z?^4ls>ORusD)ujIbGIPMsXtw-8 zp6w+nHBdxvosV$;0O5^+`*WwN;=NS=0L@+mxwqU;Mim{>+_<4dgRw4Th+CC#Tvar+ zDXk|z?I#2DZ$*LFV)OhW!yL_=-VBhw&@rBHUw5~jsq$C`TXsUGwp%|{-_WOBw z?%l*%_U`L*11Vj#E*yQOu-w~4j!D|GH}2fFrH$KdMR#q5pp)hhGmz3pe^q10HhtcE zUPsGhd=G;?uedflJJvVN<5Q@YswHYf9n7kss|$fW3(End4|!Kiddy`BDy>sID)p(t zT(62XkqT6FpWN^8ZFH?#Ld(nUIX8|_i4><-#6euGw;4n(M7!gM^B@TASN)<+B9*de z--r>uk)&Ge5!lR8_cc7?M}LbfNR$x`%aoM+V0Ltl^tK=A<3ih-)x@}975|*p@FvT&|Vb|-e5A-Rxf<<(25@@Za} zFQhZ9!e;L4O7X=jPkL*F=!^3b7HyYgE!31L$j;X)sM9{|M>`2{CAE;)?P}$5NvWD$ zPIRhLaTtE1+jdK^9F2|v+<!}W9=A{IdPX2MpxY1XKmF!WO2GZn&)xW+R~*d9}P%7aqUkcDEYur zx9phK_Zzrhr3a~-P*R+U8Tg67mp2K^(;fqr)ygf)yjsIY$zV9*Yd}ZvH+#0?^7wmM98RKZx$ZFpUu8hN>hXywpGy@Q&O%jF*&Ik9<=|KyCT}ej4;R z4(=RPY-+ z(vYbxS{Xnd5kXq?!12bEwIZn0a`>-1!}@KG&vp-Zj9yUWOUvcp>uXXHj-;NrN#RuJ zx-netClHVnkQ5KZp4(!p?!nfjDaSx}N}IbOPBo%B zl;I>~jK&=e8N$Zbb#PsHI+QeBAcqz_UFS1_HybW-&^He1gVc}>)tB`&`o@4MsG6F4 zD~$ELDaJ*V^5V$vbjEC0EJQ?~(FgwxbA^~e$3;%J2HDs9i2 zT$E}ihag93#)`$Y#Hhb1H=vhjg*Nvg<+}d>ki>lRSKuq#OgkcLmqH|+Q)AY3>5n2b zW!=gU$kMc@;s6R~OlIxvvTm&wzc!R~Vk7tVU^e8IrWYASbSd7X`!Q*60ZT>$cRF4K zZE@cB84^h=s2L+Xhr1Wp`+OjZ>Ey|wi7`@cGMstE6%}Xpdof`YtDwapzUX~D= z+lXT7=qr%Nfb!+dD1EXuAB(MVXKJ(utr;!a{HW20OQ~;A&Xg6)0AyQ}kojCqh&LuC zT%2~3wYH3aCpuv<;^Ctj-A07#EVdF!1<<3ChIl<*h>i^oeY}RCvX!YRTZD~7=29z& zZ?0rI4^h*2ml{kkmdj{K8PtmVsfzn{T4S8OBrmvIwm5!KsAzIZhxdsiuef6R!y|)e zXg9EL^AhBSVuY{(I?r||dA93HmQl%68FOGuw?3%yThO%pKHL(WLhapBS^>vIf}mRp zujK-`PEpq+g@YFCTL;K-P5S}FbciaH15qsZsfzNNgyV|K!AcSL;yGWp5nRyXiN`yx zWeRO;Z7LNCQ3EUp^R2HD$@vRSaNhKqKS>M_2f$qb{{T)KJ=WYwZN6p%cvE{Mlo)9w z8U+Q_KYcM5>vlZJlWS+v zx(O&xMW|He(>yiXxYwzT>P9>S+urAKr8gaj2uWukRFm)Nhgz}i37$n&0!;2p#D%of zbTXAUO(#M~8Rgd#&lZZHQM}OlOBYMDB#F|HT|lZEN>Dmy)C^ak8;>XRY)BL4H zU(<(jp*H z6duZ> z`6p-by{&<5wy=b<*aeVM6r};*j~Cs0QztrZt{UtJh!~7ubdaqwycQj*^Rx71xh4QOCdK_5x4GZgOXZo zHsi{zL$p5p@z=1FR-+-5+t8nOl)17lk)?dk%K)F$02-fQ#v637sL`z3FEq;)WWK>M zUyX8n>TsP3SF}={xWda3+m(DxeY^bJL|fgPaLzk-tB?gfJ-9Z-&QdIx*MN;D%8jE@N{T!1Y-cCH1=|~PEk@wf znQWyV1LLoRV%r}3Ob+FUpoOwTID7?#AB-X^bIN z#Lr}I5^jJyQ}rjSF3OLz(m}R3HUHz8sVaIXzWLKg3Z!_*{Hbi;mKlk>4Y z=*7xtD7RVPeaAWw%c@DPT|#Mr?zqM3E5?)HGHx7azNMimMS=2+)bfi(vG=Z+ZM)EM z2mLNp7VMlKx?E(;pCUDaHbu#jUu*!HXYiWfE7?(s(}`hYY<@36#NOQE?X0bLP02fX z&t%ZTv@~01S6tq3^u4&5|s4? zNgdhO7=O@W-l)HZ?$7rXtzc_RW&zDAStQb!uD#p#{E0F5x%rZOJ^LlK8Ddi^ zjUZ%?GMWegS0jphyG&wVWA4sE@^|Hm;!=rEnWTz3;v7KNxopc+)NU5WrMw?>FzD?X3G0tW?VjUSE-2hpMpty%<#sK>>T=?4*tl9H zITq-ZHvCl-tqRl#p{70K10vnn^C}*Dn{Mn~AAnhQ`0^6sLXsUl00dVOw-7H-t7hAp zN!33;U!|d#P+vzViZTWN$ z`$YsCz4(TEqVXh#5+u#Bw`E)Wq)K`;HprvCAW8eNN13?ShM~5x(j-XsEwbC?=PvDq z9Msrss=oYF&o1jM?OC%HJQ!Brj<)MHUQ$2^K3GZx2WO9;x`T}f)uye#qK zm?)N&#!_^xIs%DL--;X97kR5v?;kqfEd*QE$J)EqEq{x7lF2$52nbCTo@C<77j?U0 zG$6aT+qfskBP_lg8H;B0{w7IxD%5mKQWd%H@cfeEZ4nTlf4dEhIU0pa*NbAF>`k(?yJqKd-84W^J;}5)G=jpbId|5 z0s=u|F>MPSejspS$Fpf@wJPsrT%Q(K2)5xBD$)Q0uwX0K9GJM!LbFKL+K+YxoN5zl zI)<2NfR!m^XVWY}sNvBb-rLb#d*+mtoQNI6vmD8-fQac7AXc4+2uVQsWds7Tr?}t? zq}GQmy%x}vtISd#PeQJrW-I4rQe}mxs&N!0PGM=#h69>@71Ok3+ksBZ<4Mf*{ZO1aTJvcW!ya2bx%ac^38 z*5a}~)usH)Z-o7hCl{ET=I3w3smk8rbBd@7e@|`qw;Mm?u6lhMYFm#!-r9j9pel}} z5sQ00)1LUy26=eR@MB{2z`nG0OG}kxxWnH_jp9RaW{A(ICp?>$5)FH+i#T^8y3)A{ zdxu7L9|?XWE`4EokK(K3eX+gxt9ikAIFzsd006knVR?h$ej9GKLHFRhzbSSOl4+B} z6>-s`^z5!!-#npbM6_&O$!=W4SNu!~kW>lo^Aen8GH_`x%bR7rjGH#rsx&^hdUNId zpeih4}v=+hb(g7y|+}WS zcIZEu*AB0`n2l@^jA-v^WM^*Ju8{3eA=gd5GoE{^Uuu+7 zRVhcboN~;^vFS{!mdCqp66d(7@6d-gPpzG=6U}FAS>B%Lua_b3(n0pnRNxz%rP;bc z7ua7WmBElP^&99or`NEI~uu&&KezcEe(1L?n-+mpe&S{i zjwQvAV>YmWdZ$l+ZZtb)*$BF`AGq%~B_@txa$WL#^+HPvaaSn&DpP==U#Oy*q>A-5 zuWmHa#D{@HEV(v7TUHJK0FZdAjP{n(v`W10=(~z*vv%}IuS%Af%WjEvZM2e7lAt_F zpdI+?zlS!&$wm8mNx7)qW8GGxS>QN z$c&=P1w_;r7HE4hgWi6nShp@*c=JCq);15Ld6%g?m&p=*+7Nh~jMBK4^PJJeV1%hOPP`pvhQ8_As3DJdw%G# zK-#7eL?^ioq$Mc>xgcPS4$t*gPOOVJyi7##lMmCacG-n(OM@b=zsc=~|fu(;a9;!2{yl&x0@hMIySLqYSLQ5`5S zeof45emb(Q8Pl4n%kFCWox-%YaNAy&_IR=>K)3$@6EBQ5{nE6ixUk7nZ2tgcxl`Vp zSEqLFt#YELFLR1?j;!Uz;<)lJC*LP;Zb;m7Z+B_FDObCz>vBt=l0r}%IV5%^AS8AS z9TfypT!qVzQxKrRkD;4PB}kW;a#EA6RsP5Vl%@x{DR>qb_z+sM(s0M6eoy8g?b-#a z_T8-r>bmbslZk_Bj*xRIks^UdFNGS8AyDSxSX-20v^o>8_U8?73`o*8Rhhn2-|>#G z^X>K_V`|&vBv6=eo*{CaO)8fh@b-@EN7Gf{Nba${yZ5H!JIioO-y4PR12UfQ zGRg-=W3qshw%F(rgn@}=ZYfkM!qj=~eU#+qo0GU4Ola*5M$VE_rFVIh=#kxuEWGDl zNSZ7h1iex%&l7RR^yVMkq3jgf_MaYXv})u|VY{#8HqC*Aj~AUF>GR}M;f~oWIaf$E zz>5==bKGO{cV!331zC9^<+%Mp?QSi%&Ek(pUp(R53>aJc)C}enjoZ6cqol{TGSDiw zK>k!Bjwohmb2RsF^GcTVL7RTlDc0sjufcf`6{ZXgv37bl%g5e4q7a9~0Q*i1CQ^JMM$(M-JZ=3C_G3i>>@EzOxOz+L!y!7H%ZKVmGRf)K{Lgly< z!yc13Sz37$mXwMq=NYD(MTw;(`O{p<27y7cML}?cgLW?7jxDE`mgyC(KbPD3X8WtV z`j@^(3)OO8X4~*f?X45%M>}(N^!1&;wto-s*3n+p;@z61x5J!P7NE_wkV>As-yH>B zWXA)APOVBhN{UiS)KqYL?{ulRcIT!MdM_{P{{Ug?9i!Os#k8E?HOYKZ{tMW?+SI>( zoxMEUIH{4wTxeIgxeIiZ$mEVikboAafsqryeJ}_XYjN?!OO$@<*oM zy<=?0cPFQpw7mS)%DWfPzY|#D*)0m+e8tET6&H>;<%PD3jrQ)gl;g5rD?g(Zgru$K zmUN*4f~p`LPi*#;+{vnFxU=W0+l~F>E>e|Mwd3LLU&sFd2b=aTR_;l;^4~Z5g52+N zzY(=}h@Vos;?kF2yK+wR5sOxiE!pkI(52g0edMM>TOlr=3e-oNkE^!4dE!}yHKd_& ztv~oh@tmK7Yg)bwRmzk}w>--(6pz5PqT#3U^QV`RLx$svy%XWP_PKN$O8r zdtHk?IC7)IvhtnDRfZ&3?R5cATTlR$(KO8aJ(w!l9Fl%u6y!Sl+h+aXQ8Ym8;TXOPnC9c z^7|`7%fT)U3rPnm4D-tiZt5L!5-PpRYTTHR`?2n+bSXrsPBICp=}jbJ!BL@?AkA5c zakap>NS3yxc`@BkN|KIo?Z9(cXz;d8=6lRgojjzgki)!rGCr{%j8)i!lD8sx-oL-*t z)&NeH0%)B3&JDFC2$t%ybYU(7jS=0&$ljv)A63(D@Js`KmX}fsb1yPfTDM2fB14`n zKEaOw`m4TABK$Dzzlo}z`}EUlSk@(#HNj|7ejM2MuT);3Zo7+igr%$nC&o@qs9pdKp7<| z00fF-)a~2lNb{SQT;$4IyCyfuUZpm6%95hy+SsX+xOWu^C;|gz#07SEO+MUspVoct z!x31A#U^_1(mjb>$#v4|rWrrGdX(FA+mGMJ$2_u9@S`d%t6|xns2$`j-RIyBB@Cwq#Yy()g37 zHSB92A?@aQkBRTk+_tppf19?zDhACfxAeiNZCyd=F^KH`;=7M` zHxIyVDoZhv(vQPTHCvnU3Q8zWa%dWmud@;>V3$}=<{1>B0=<>Oa!eNCh}VQ$CNnM! zvl>eVt-69i2l{>3b?k0+Mv1X=IuopTl2jYLqkFQnHkP4oWPDM|Lv73eW>eS~?Cr&0 zJ=|ADs}k~Zvzn^3kkXJCYDtd#V$-dX7TSdfp`{2oxwU_qHAZtI82Fh+f+11^dkaN;)TC^2bZS6gy3E1A|9GXjD|5nm*h|K;V<_x?Ha{mAk z4as#7VYr4j$>y5RwNQrIvjS%9JbFEXr#c#Mf zN~vR31j)yWWD|(>x87FSZBA3AI#B0|>zMvy4KJIQ%wzI^cZ0a6c}+$)H&j(egR8X zB?k?f?#y+q1tmpP(yWG`zZKb58$nk1lHSOaHR)O=;bK}WbP<-l&Qz6QoPU=O4NIB{9HLrjk%K; ztw^76?(|A^z}qpqS=WhNrx@-{e5u_|YBX0`=lJ7uix!}abnDDG>@TJw9MYuJft4#$ zOb3(9k zV5nE18R9Fua!6yoyhmly^=y*sFxhWgXhxK*9Q&);h+&G-2^$k09X6q0pQbS>yd1)COG8m0IohMa6 zpD9hL(pwZtPlOyF8({7sb2%)nB~=O?DgOX270QE@8w=f}M}3gyp{YPoBd%Ta#L$Z- z$BU@iVdBE;tKvC(u>M-0=|MC*pxi%CG?grTAOo1hPS6G0T7pQEInm*W>XLspg@0Bb zZrVpe2?fyt_~{;0Cq)PHkzca?ymQDmqG((`%B0m>|-)dXi#_P$O;frsYSp7p12+ z6tya!ajqLLQVAULFLKQPHZfmGfQ*=U4L8N3p z+!-8QLe->&n?B;)k4kPmwGK*}^#g3SkW@`*Fx;C~;T~y>Sk1j13wyM@Wyr0N$u#9mN6Nf*^LQuB~sTLXSmf{9Tt8k+H5$)+xGL7CVJIX#brgN1v3J@ zPBU4(#dcao8Wxr|>uZZFy_GzDZnT(@%5LaU4dcluwk7gdWmHHhV^0$TZ|s=30cSLYHp41lvu?=HsDBi&~)5_+!zqvZFOq zH#pJIM7O-JI3d>*5-C*HnT!!}RBZ`H!!;>Rr8wbog z0n;yT97NdMm|a`ybS{ZjU<~LnByH7H9f1Kw1c>Id)n8|vK)9J z>y_xs7ql(*+XO|b9LHrXK>4de00-^HH#a78m%>|Fg}ADqS$mGf<~W%#mh+9PAh!=^ zuw{uX+Pc!oLo3dbHkG6{h79mr|-xLX|zenDflsDau-#J6SMGLQA)A-K7Dfy?lnL ziUbr?(IG>jq3cX$GPWF>kJ8JFJDsR;9@cJZwl{ET#HF@jpLlt9=NEVYF=Dk zrGnor_zJi5Y_3UsiT9Z>H3}iNg%U{_5|A>-VO_|;dW^c^lP?;KVd+Bie2+42)t-x# zbXUk)))GxKp%ktx7t~93rRrxs-k%hT$-g#k?c;f(-?;6@IV7bH083~6p^fIvzgW(V zV$Ak-Ez1s#5reYccgL=@IgDzQCH%#Bc4)9q;Wd< z;@nD!I*N%%H0UvXi??a1OFY`hQ5WXTnZ_LOODgawCW4*k2_3ddbryNbNNSmANv&&K z>+Qwut2%HguU^y~-70iy%l=F~;gPJC1iPb%Az{iB)K~hj&y-7uy>|Epqbgk0g~nxW zlyoUdlz!kb)3t5I>KSG;lhF=mZJUYa8XvaEam5bS%0)iVDTpznX?Tvvw{n~UY1(_L zQWUcZ3<1?|KFdO%?JBsohHbQ{0`P80j$Ny^--$UWoq4ym;p&7)4yX@u20d5lcJ7u- zqGl((cDcJ1sW!&jRu;55Gjf%uA`;AJ6nb~6gC6yNbh!KvAGNndJy@eL&9iH^N*3i# zl_lg7Tydb=YNxo?nDfr`zdM#aUIx>)Ek-kRA?XXBa9L_CxpE4K&=Asz&$BFe=0&v4 z{iP;uHrDe(2bW=a2w%eL(w4<3PtH?{ncYaSR+yb5V_jq=Zd_(VwU)(PTk_gXKjssN zZO_Dfv8py)+dOp~gzg?i0Yj{NWM#@(?a4|s!7-a}K~<}MECIQk17})Z{fBby*ArT_ z^p&PSAuG~7sg-u?d_p)v` z=~@lr$nzT|sP^WNI%(2UKMWc2vHt*40e5cs4?Vqki*RXQyhnB_)IUwe_0qKe0FxA% zRg2M=72IPn#D??Y8H9w2Y|3m8WiC zVnpoIR)_qh-yqvNi*-v%(ABzAs2xCUzz5-gdRX<=g7RMS72(d_a+W-cRIMmJDzv&# zd+CbnxcJTZ6DwEz5h7V6?v}>4J;F#B|wrHsQ9=ua& ziw4UDv^KhV%U_tIOtZ%B)tm3k%gd25cHOyckD&z@1|zt7w#y+@2NTO4@>-6C*fG&A zbQfbGEzFZ7s0~gwl%kn?Fcjw5KUgsdh?5!&{;Q-K(QY2UW&yc=J|dxIcK4fMI->?q zkV03~pT7|+@bfrqx|^?>JcO%A=xLF`5xEu*RyZJh!-c5x+$=V~?+7)eI%QtChax_m zH4U{U)}!9k)vn$Y@~%pKNTFp(MSl!ZT~d~XrG}9s*{{)?Wp24OR7g5NK+CtY6*(}W zZ*K1bU$Sj`ZQ|PH_HDqp&;`br2%^aYrYI-4N;BmxmLD{EivIw-pjzf!q)K@P0$B<| z5JB-M6GKDajds<4nIk=yWvNhzT(F_%mg+!p0YnbHI^$Pv>Ojsc`KweY(pp2Yfz(HT zZX4BLS0c{w@x(S$>C=pw`F|<4E!0njfaIlR_v6sEpMj9xPeY45z{+Ls-NPZ8X%DOv z^y!yo0NfX-Vnk0S-6*!7Edcg)&p%}-`z3oD#}zZaQk~M^P-C^#?$&!?_0}^ z9pTFM>sxtwjoTr=C17_4Bq<610HcilSAXEyuC+Dze@!#5HQ-&oF#168Lrz5!zjx%h z*;Fl00Iu43R@A5QILY7orsU~n+n3yG?foOQYg_kH`_8QJ*V2WkEyEjg)0&~j00d8Z zBQTDfzuwDGUuRrC8}D&ue~#BG_LI9kr-JEn;%#_wIu)`$n3ssjVaaf;yIrIZe3usN z$5IEepp@6`!(rN6=2o|^DE;7Bzf=2vA4?XTsq8IY-Sh#+mhUO;#k{6mG6BncR+xy! zUem3Ajt_^^8!KDaTYmF7m-WXz);280E6>E8Y#yDs3uc75Z`tr)l8`mcnW|f+oxiS5 zc(AeBJ61Ocru^BG%kQoGE&8U?@>&vO61K&$2#;-r8Y@T?ZMGV1w16_`fH82a<3Um} zUx7(1Y|FaNP5aw2yX0Ezg24vyD95UTG$*Yy3^nlY2`Cyv`s5W4Q=RHJ9^!}+|? zBEV5KGT(6gBn5P!#OfZw{Rl)Ezc7mTVi`e^&Vqw&xJdB*xFKVw#13;pP3v^+J*vv2 z?kntDVh}2&I_d#5?gZ4~k0Q;!B>4M&R!8{a+Wm!b-afW?`)(!Z`(`uSmO^q%pm{73 zEBTk1`>`F(;YEI0)W5;{eJh&YmoK=eyT8`2C|})iHkl0V*o2=gsO{@=p4GWuz+fA> zm+tD_4Q3Z>?e;&nnfrAs{qvLe-sf80zV3OqA28Imx0Rq*trC(4;flYPtE*;;)UDf% zArzOf40rAUM^mro+DeJ9zY}!hAWD&N417BWsV7Sx~8dn^pT)r|;d|SCY)D#^Li` zCXI!O&eRneQz6@d@v+>QE`C#nh>mQNH$Nm>_~OII8**Ovm$dBLn%{k$7$Fv@Ke*g= zpuccMu`JZ@9-@B&U z5q9M9*3fyzEy#?c{nYEKz*_2lAkLBR$DC$;N6pKZE*v!TXRz#lNHOu@&zm&8gvoEd zZU~l?ZC)+fw%#V$T4*db2&sY`0f0>!gD1~D>|QMHamB2V_1(YV^&}{F8=_(lG|>GoAyr| z&$vy8Xxe*a>y^%0f+9ta9mm~L)M!2sN&9iH!q{B+sv`xscQQ_u)sufA?LHv%(QIdN z`n%;Q9D3^64_f%%8ozbjGfAdR+u@Zd9eyRy4%|tTk%NY*CY+ywDPfb9efoGDw&y>q z>MSv*?H@>dddD1Dk`~|mM!hp0=eeF83#_zCqeUmECt2?U6tm9B%F+vxAG9N@3ttwp zIUBjR4<&N-?q>PqE!QgHE@4vUZhO?nUXtihB!>Lrr4ihO0G{jtZwA%3Rjcz7=1t3* zR*T4Qu`0)BbIaS-3<;He!jtiuenZ`J!Q&YqdQt z*uuZ@UDG9hFKdK~RF`BqL?(msimTazp%|n-r$jiz)BgYzar9MQ;ab_|i?x($xo(@V ze$lbuxCe_#a*jODTLC#!Sxll?=Ql=p@@=af1hT;$}~3OF4eW|$xp(VcJwZa z)7Z6)HnnEU`J`4H3XS3Rf%aVETmVHpUY2vMkwC$X2!#g1cM$_UO3X=zI?YJzT z`{mA3^a&5sHNN#pRdgn#)|Tg2B3#!rUS3A4Y})N(FHW+2TjKuTZX|IVciDE7N9>$) zelAIMwMX5Yy0jLbbyd>rTQhHpw9%;wkgpQIEi#}t@2EON(pj{;cpVS)r|D8;t+!_6 z&P#^(AMt&{N)xm8!x5D2{{VJQS;;YCtMyz`=t`YX=~IxZupIDIy>sAI=J!I2>04Ts z@0xu_akmxiseE}p#Lc&E*wj@QkJysK^XExG0YADt!h#ZntNqA2N%*TuVEgKER3v=)Asevb^Zx?%gGey znf*!fKdw#lwRmTRHYzyRfwlouP7vXEZix2#LRBaT zbKafV*SXJe^1*PAg7&{R`xl3q>pxDuo!Z;2b4hdFma>!L-;(xuFUMA2t^naXUl>@C zv-TdF)!Of*Z+-rz^gDBuN$@aLQrt$#As`E)Sm=& zF8=_#c-zrW2;8|(d^7X6Sc87nAey14nvlYW=A}+m z!OF$O;8sf_%kdt&SC%<1Z_H`?56Ny=Na}{CG?Jm&K(Y=Dg{IvYHQe4u=`JqW^=UPz zk)Ur4fpoN&#*;cbX`IbYRjWjw!xj%{PAR5Bx5nj0gRk@79!PSly?)+%1g0O4Q{CfF zTsdMmB1vx%@Ih_C%UdR9)xLa=6oAPUnPT`kWxnC_C!bxMUr zJ;%EdU$GPuYaz1d`gz#9!+TGQeY4xjkqvZI^BqcHEN9}-5AelTiJ-$vly!5i;=O+D z{!zbM4Z8Jbkl?t5Eecbh1nDP8_zH7QBhJ6cZNDU2ZJ2#asE|EHT$RLLi}+iNV!877 z?@(^NymzizUH!H@pD%A|&e{=03io5>&e?>wUg5cuFNWJ`d(Wpg`fd9xZ7woOq_OBf zi_x^Wb)C~LC~on{G8~@cq)~q;ljX3Vc9C3tncLW$wo^V#-|{)GIHKTg83N6>Z+BIw zHi!}2b!sMqz>==NW-YRAIL4&~h*i;?$KuV-mN@&>&oIv=so%B>HLhN2(IVjCKmech z$4q>u^%pK#8`LeX2h=@>im~>o++HJB(gxgWZTjTsXUtG#31kmysh8c4aO^8-zj5Qe z_dZ&CPLMsc<~gad(wnHDC+^3n9$6Vw>5a3FMLDQKSgRAMNvd3u*Tb|e1s&Uzu-Tu3a z>NRzUF;wvMzb$9=|oEdAL5*b^qE{`YHezTw`bqqN$@IcgfN3n(um_cOAC1y zJ}FR3m+@erpg-t|l$AJF;+BylY3x?GX?$oNizue`dQbd0{ zcy4>M!FNmKnT@E_w6~X_dy)-53>6$Zao~#OxLt2nO8a6;UX0#A6#+m#{Acql#Yn%! zzbQnrTszBmzAD^}T%i#<%`|yik8iv+#-9~Hg|@VsiqoO&$C>w^XTv_O!JfI; zeeFCOoR>XTpA=1;wZ=Bt`&vIWaS2_|j_zxv_RNv0R^5`U%TJ_KhO6+epvFhD`@U*_ zCzt8F9TR>gpT9dA9VMNFxvfi9mw{PYxu}2(hMm~;UXkJ|<4nVoXn&gm^n$v~PJUC1 ztuiO3CZIFA#Qk!Lt6Yy}I)qhh6i0c&0H9FnPqz+P3&eY~BjiJx%dNcIC-O~@~G!NH7MZA^uP&Me9;z+s!PE}zSlCKt3 z>4_BCH3sv^bS+u|6zVI3VJf1!_Z`;p8)|lGgKNg~+ z{Wz$Lnqg`>5N7YR$YJNahSJMwsY77)4Dq1D%&wIyF2;<2ZwhIboI|M6==TrdjWw@? zeT3fuxWTJ}iluuoAY9gink#5ZLdo}HB?^JbhCXgVT2j_QKMBCnvY4EpFG^aLpz6xi z+71b~;3|lg;@TupGy~g-nk7ksH==@_e%x}49>XmyTG6LI>@rcP*+!$Gr9o{Wa)Oj; zl2S%z0Nt|W^cLL#kt*Lod?#}~9AI*%d1tu<9G@%|8{yAZKF*>8p+q+~k zh9WmlEv|%)%msUEi{edZDvW(Gb%S?n$hkHgMqXQh=^)ab_^6f??MkSzgp}5x?rlq2 z+ip?{LrtAYNmh9p=T5j|$-ONI);ww(yWifCX;cWQ@T6%d0Mw--gQh3O#88rKl6M_?>RkK}l%1Nj_?s zQ9=08mR0V;DWpAGlL)TeHFR5SD3CHv3+F;~rWD|t1~3mfzoqQvO-!4p$&-D1b6&-H;c>(ZDZx}DQZ*qd~BQ1UO0sl|5% z5tn`MC;O`;bST#~0IpK3(bSG zyRQz^CFHbE@-~$8_Y8C3GWHx$c{_sk=l7Q5)khfK{ZQtE%{081FwE3vucDf;xH7ayu($M1s{G2jSCeeK=TmG$x@J| zDP5zp4WvfO>q5jw*ANux=xbA7!w-}yK=ltNbET2x)CF`3Cj!qG)Hz#WsMlBBNL+V< zR&yX6{@f$6L5`>?Hva%|Y19WDQb6qngAM4SSgvPL$7$V?ZqI>fg3V3O< zW=7kV^CI(bTe94)jDj*09`TEd7|JM?+;pgp({D>tQ6jb+ebNF`Ov&uQGPeefOU8_DQlr9^>?8IV&IIRg*Y1i9M>`__S z;N55!cI?vPMmJsiG1pYvuhG2q>B#-#7WOu1#c(SplR0r<){W@YK!+1GZYf_IOIbf) z7?$SQ8F&&}$dr6ZCMO_T?xZ2+klLG|8bJy8U%9v~N*sa>A4IUV)ubxtS~L!V*BmNQKao1nRAFUS7j3w2{Knke?;oM8BZ zsLPmowC3iJ+L~t0@{q2ix1j#Vnm&{xMe-36%@KMXyoJ%W_Yk@rtTW4=Etxs;`x}^L_X|*q>ObwRGK_|03IuM|gVRddDpC4O@waAv0^+MhWt$V=fiMma#8PpRV-Md}7 z{I=CYYE^1ne5@a{1uHH1i^#$b@)L}WHv2p%v_Bt`6o2VrDPmK^dbSrlmAMsPB4~!H zjS6A3H$(OeHDX7^a)Uw0yWewN9ZDwMpO#KRYE>vc%rbcKM_rVch@~;#DWywtLxsg> zr%}w|nt1^8O1wryxvO$QmrW&EieS585Y-FzOM~cfqPJ9r{{W`AgDMM`LOHKdGN*Pt z#j#PLz8X@7T|2wRGv6xt>J+ExBPmz6lJdi{N|aAZ)K}Y!7-P0X9k;bI=DN0$@)U9a z{{U71yB$$3{NQLj7tOiB8NWATUpwXUw1i%Y&+sqNip11#gLT(plMIX4b9l$D2a)<$}-|w zO{L6Aj2m^UYi_#cl&NY1#rPa~-tF4am320ocW_QV1)16#?{eH$g|O$yH2SKR1EnY# z^auV-d6s8tS&MX4Hh8<#=Rj_9+;-`E4&iUP1WcUr2xzGZaZRsIL@PR1zXf)#)jK(9 z$3b^}!ZNiIG#Pp(>>b$I({ou3FQH=G|H`Ry5|Qbu1-a)utHiB2|7~L6Z81 zz@Iw31quEbcQwB=l9!NWHY>%>j>WUO_qhZPo$96`o=vEDgmi98z|vOI+$;SD;UXH8 zDg)0UypOWDjysjLsMUct#A!>~iiqyJo%R%3W#xxUhwe@+tnR%0BFfC2vz67cF>!X} zbyiK`$qA-mhELozoJ_CZd=NCP*<4svZ|xR{Q&FZ(1FBZEwtIh+VAy=zmlTNq0NXAq zrqA9j0kkF+g{DvQt!i~g_zXVXo9+6f#J!VcaEPTJcFkpy7`MS`!9tVr$yRjh;;tVp zzEfJZ4=u$l5<%!2w{fjYap{60YmjB6HD7fG1pPi7IF4EODNqU9hT%M@J9T~Po{Duq zQ{7w>XATiWH*+T>HJw3q<5yJjk5Hu*e@+ay2%;-o?T_9*vCe+@hgh~cumG#+XU#|Dd8s@{LgQJsozhEa z*8R7)%CNYy=d)Uf^HHu4jIZ1plC>d4AE?+sMx6TN!~Uo5w=vXIBUH~_`hBy*Cz;98 zf{!TuE;)B(S!O-8dH(=!NRrOKAwMqTCE$wqQsP`8C0&Grj~~YF1vyf2q3F52yU8AC zJ_i=L4{q`v&%aLFoPy=bHO4BG5MOn`(}}G$wxQutPk8%rebTom);=a`cPAG~Dl#wm zdwa`sSD4`g#NyUfS{hcQ;22xfRX0%7NLG^QZKSy&+W!D_k_~$RpctJZHEN^g2OeMN zQSr7`%tZ?P^poVYky_VNlRat3|w(}DNM~2>v`%tcNHquBTEZf)$cn@mS2?yJZ#!mX~dl7AK0x?7-yzN%o zo2s8Pbh|CmNn5X#Ep<8cQOgoz=S}F4K2|q3i4?eY`zu39knGUe%{qz_e(V{~c76pj zu^9N&k!*YB)diNLv9fLV6(CWv_NNqS-bw38lD$DFkQkHN=j_GGfmDLt1e&26p(A(~e2p$!%xgLrtMN zK|Ok63zu<|_(s{5T>k*L!)JP=mYCc!Bn4+gcyspwhy2Drk{!vGxX2?u@4YOpe~7wD z(4k!=RQxe2wPxo8g!!wm;=o|?ZsTp>BTRK_KOVEGCb@Qu3ijJ#{-ZxKOR;bJ!;iM? zxnz-=fu$?#IHk(kV8ZQY%b8T0Hm@K`MEz4{tq|R%7MC=lou-r-V=uVvjVi7v(%^1X z(T_d*wYKj60I|H^?@JSk31w(?U3AumvY4A=-7aHIHxR7oTfYYua?gD13x?ajJ8yP6 znJP6y$302!6vwvh+pKQeQb(TU?ec}jgFAa{kgZ-~lS*nm{rO@n-0~YZ-ej|Diy9ME zOn|Ka08s>ILy^eiJ9_hwd=f}`Th(r4ml_FDyH%0x_F|^&od-g3V}*A(Pcj-pHB@I) zQ(lL+5n1M{B)N0QKy^A?SVNR;BP9Dt#nL07nzj<_zo__V*14#Au|F~&(w~AJ+izfR zoyziJZJPzlak(J)5ef{M6&ZH4f-zf>e$;rf(;sWdp9IT$$Nq?YRBsnzvuWWQ>v2}3 zs7;p3a2cScRg|XGD^BcC<+lkvaO_teCjV@zFP**3~t0ENrGM=L3Ij)0t2e z>)nf6J?VbRp#$t3Y4AD2zJ83nVR*jW?$&=&8Da6+_@gtnuCrguIt3|hqPU7(?QZJyDnDrw`fE7xnttL~v3)&w7T((lz3(1G-clRM z=rL^;%R3GbdIh|NtOM`EKlTik>ZlWMvaCVtT9ciV(Kn}Cdu^`W>CR=*#y}!|?B0^g zDj?RTG-@Q*v~<8Z8^%-OS>yJOB%Y|eycOtgcW(A0{{X^2O*?iRR2?GexAvNu4H{QF zjmnLz^s0i6EACy~?nm;`Zz~%fBK+2>i}P*zQTm&?#%@)>BHY^PuF~!KhLi_!L}r$e z?yezyyS-jja!5;k^RQ#OK{xjj_Z_sc z-jw3w3v0r&@!8kfs$?^Fjl&~Lc`MwVKbbde-(B8{_VkGRdN%X5ai!VQp^d$FwaD92 zbpuKUu2JI}`?0C9nTKoPf{XVVJiUW($#tE_HU9wFve4OiTG70sCVtJoz}!%rrkOCM zvn3gd(3X^-F*LVHO?p6=Z9>=iDsLj=x?d{EEg=4C{{TS1(z$+Wj5^jP6g{G1qRnuV zCgiYtk}3F{M=h>X_@o;0tjFPs3aZ^0D6Pj7@`v*%pXtJWlM2poP_`mH12NEPY; zrVLonLUm;LnwM+vSZfR*O##boOR9G(%`BaYqxUe;ZJ!2ZqHjg9?*xG z+cvi@vJ+n|=SXRz?d>>k&dvM^g@vxtTy9V9U(Bid>(+lH-~8CN>yj)G+^F*6ri!Qf z=Rdm;T$R~zW$lfp2F*ERubb4W_s#Qt+%LN#=XHK-&YG#m98!rt?Gu5z35G1E!Eg`+ z*Az+DLHuypB$%tH`k6zK+*4|iIoG+l@0xkm)Xmd> za)svn(~BX?aG2~^d4Or;MJ=F=`Y0S*+QqdO=|wfn`;uO!H=l9KYBFs&mj~$`wVbwa z9{1jo(dEC%JDN~}=t}>m+aw*nsi5h<1#GK-z1y*!lbn+(K z*)b-Y{ne{--EG9igs;?;@hBi@Wfe5`s)HUkzkNW!$C|eZb4i}(+h0pJT(#A4af&G4 z18?YXwj`ag6N*#F4nn(K--&m=PjNN~tCb~7{Y!HvM27$(y7KPDE?;qH-~793k$xuY z1GM(LE1vVL*WOCgo8&G)j?@j$dP_DYNcl~vXJ}b0tcm~vW5d!Ekx$Ktw!Yi6q@{WD zziDBa+>o_zmc&$299FG^Y_+n`F~Z9kC9zO|uJAigzZB*5=D-~p;pEP(&#_@Aoi6sg@0ik zzVnB;H|!!&USq3m?QQ4D7uXi)5Ms)r$>&~j6Q7CL=1$xqeb;QuIP5}<@ zgE~3I9*ykQ3zJse?*MYIEO-|i)8&SLmh4*m4qt9*gHx?7L>UccWzCzksjZ}`)SAE@ut~%p8*STvfA$NgI#LPH5rkJZr_J30xU*N z=t44X9F5I)xKJJQ@AKp=3s&8R%2|UH6OdvvT3cxV9L+KrliP}FPH#feiU}e?iM4ii z!yaAmx5zu{I*^oYhFvMKLQ)CTnNkr&NLV6;4Um!0oK4KF$J!HO)10_yKei7Z^0%n1 z_}0zXn|00iy3pEWziEm@D-Pa)s)hM4EQVpkZ~oC|IuMYhWr~cPski1E%Lp>$TX$Cm z`aN}q*y?CB;-`_qRBp2Nnrm`|RUndHNM`Px^1aV)V~#lT#O~DNM9NFH z?Xbej_dDFNL?T7bLI$Z1E3vWQN5YYnaV6V{EkYr2Mbzpyr!Pm_SBI?j{$%wr9wd$M z!);92JWDaeh!#lXYHDrWDXms|w;Xth=n2eG-oF^@AUnAiFH4dOPZl!$PJKjU?df-G zUm0DJz4EB9#@(FFB?xWH6}3t6L9|8%O+raW#8OJ>nu%2oDDKEG4n2L;bP$> zJD#WI?JM^d@Yf$g;%5}T=hiO2h;{jgs3;%h)uU9q$5=|_~61Sd;wNiF23bcIvGfD>By_kVprlU}z? zG>ZFuxSsX&*XgaC3siz_%UMbBllK1rE+4Tjg78NP?XEl9$lF_DI+GH3Y~QytsPg7r z7TF5XQJNU2$vwFFXSjQc?p4N<)cPL_YlWzDHAIejdDT%3qzcx6W^%Ia&5G)yn)OKVPX6xe+lEicVS1a)>k3! z%azUT#mxKZ=K+&^+>%>DLnT6?EX7Jf0unT&1feuaDUV6*PT7;C$#`)0eZOv}kaur( z>(Y+`0J8GLWk_gTwlvs^O+3QWr?F-XvH2(n3TZ*aS zdOEbyo#_N%JGkYItJOpAc5NGW?6XH|-8q|+NNq(3ZN&M66a1<*?C!!@gp(k>NvagT zb#T;(?>-tzl0m3t)ahSdgR>T}l4T}NB$om@w^>})DAQ)kq+n zbvgmT*9cclv39q<)~4Bz+l(lhk*ySqz1FL z_u99?dfxbPmeHk11d3GVO)y3^)yt`0nQ6*e2(8GG^GJ^Qp*1P3dJ17~Nj46x&%nH# z8@{=xZtaz^v8|UUtdP`Qeq=-;EHt`<`l@jzqqnb20dnMGi>zxmB(w`E$Gf>ZlkD-O zTn)=|?_0D;V^Y$!E=0%B%}qK04NqoR^M3l?UOwM)N$W$^I|FKWGUA_^FMUZJv~JH> zQn<5bN;7SHMXj;3tn47DwHZ-jw3!KNnNcfwRi|h;^FN}yb6DxgYdJUXP5%H!dWY2< z*IIZ*9bVhJZr!wY)st_-LSw?RM2{9bK#C+Z&_-Q5Mn1r^X+jAj<&3Mm^JFpVZ*fN3 z{3*L6w)Tgf@))S=rT0P=)7gRd8=SV;CJm(Lsm^#yOi!oxAO6TlSWqFp+xFzFnl&NGa#RUDNkB-)r=C}Bx2akE zLrdmM&e?{i69_)BB<8-vt6Gaw!!*zeu0R0XFVD3&&4kf@_kDpP%Xv76jssi4~>Qch*vbQS6{7^2{4Ej%eCvM2Iqnr2CKDzbLD+x1%f4IT#hZtCQ`s=UV)&cNhY&E4&?lXu(hpV7Fr znMpsD)Qr%f?aLG4X2x1gQbc=>&nP~7Zm=6>J5TtvX(V(ReYi3*p-I!`65oXUa-s=w zAJK{G3-aMa$SG9`(tB~o3ejdIwWJz2kLAF^)Lru6$nhcK(!i-kRIU@|2|7p1hTx`^ zgYLuOexzS47GIu^0+N5$rEx#Z9Y7VKDmkabI(AnQ^+@_hDD6mkRHlP1E!EU&N>WsE zIQBfd*3#m544}!ShGxfvm52|v;!-G;Ab@_%jwv~#IcP;jjiO%eu|Z0$p|3EH8Bhf0 z+;Hod(R@XW+8;IxwBqtrJxz$!u7Ki6Pj5krY^-Fs1;TVjyS6pX?-FD1AS@7r<&*$a zRZOTWmLG+3+%6>{mu|JXIrdazX>RSgM?jlUid;hM+3xEp z({FJqS3p1q@xd~jQO;5G7Lj7TXdYq{94D@~P@n0*a=R8p)IpoIO+s`zJ}XK&=K4B) zmBemDZ6il*NI=&0>Ncv->0YDw3@0=46?=r|A;LzGtGiSg)hSW_j7tfzV<~wbH~V^2 z0k|nh?NVDm(}k7EF9FhopMa>s>}gMqQ|dY9mcag;K`G7n4CG<#lG@s6n|MI{q^z2Ia4hlMiS6ALTHNA7MFMn=hy;=_E3rmN z^9VAH#XTUQ2tp7#=}0Hvi1|}v{$bEblelg#g@WRTQIGoh zi6)pb(j)3Im2e0Ns8MI{!x=QKMuNg5Jo8|W z1v;A3F^13)+DV1}eb!bFnNm98sSj2YLhZKhmtRU`D5 z1w}jXEMr2ZTtrtOA*XeaLPur^XfaPg!wy}gwieRTlr34642}k)dS{Wu6_Tmf+lIkal19?4M5a+9LbV3e;XP=RgEU?u%7}AW?Hi29 zZbiP+CF9X5LrbS#`eG}*jcUl>D@5~YvhSB!WG2q~e831Us3xT6`MP5@k$)Z!%yqtQ z1Cg#b-7;8lcL`A8^+YHQcug85P%(+Z|z%@mf;{J3Wz{+eOWpLRkXxn;b8#*V-vDCl~5QyJXoOMa9j)^aaM7cVclb9J97;NDLteqpd8BN?1;6SAB8t*7=c@ zxaRd`S5xS{s@0r4epFT-OW7MP`*9b`o|Vp8ybT4Ugr>cMxb)qNaLuu6l_x_nyW!&A zmp6$G?){In5o2~lDIR>6KuY2Q3X|DT1v@de+V_0BlT}7*2 zC>+vgp`Ydhr%Za4t>S9c9)=eq0yDem4Q~E2l1tFY#gI;JEmRfv;`Y~e<2Oa99Q$(W)s5q@)n}dZ*L#~RDF%u_ zH9qWE+}*m6^rFAy;|Wo+Yub?ZcvBIbx&~LMXpvm2jJ|yG9cZd(uzCq9vUWGUueXrV ziFbD9>Sz{q1fd?>&M{dySu%R68f;C<#8nXDRK1zJEssRG-Jfl8Xx@hYJtTG#F`R&}R2J^pyr{(2{WWm3XzV){>3O`*cf?{9GSfj_be$>Hi=X|&HOE~D7SUlo6)Tq^ zMwiD^-Dcm%a3RA_{oZ@&T(vHOl_f@={A;#}eAHJwol1P{xmk3K6$pl3AZ~ex)8{n_ z3l&Ro*=@At*fKd|OK)woy`?MNuD3xcdzW!LQ1#*UB=rHpJ-w8{5!$TRfUk1A_^~4A zCRDV1!sIQ*fOA`fp8mM8wTW&nlt6RIxYcUL!_u8vKyW62d|kO>%H4$&19tSJ5?q8Y zNYzT>b#8zyR1!OHy82s(5y?x)fP^V^{{Wf}66Nc0_@t0WEZ+pjv|Wq5?yV*EHrud} zGY`5_%1G&-m_{n@?>Mku6@A5xy{7d1DeoLm_{w@)4_)uEZIu52bQWG;*c--n{{VKy zIzlan+3jZ4T!V7rSuAjyD@2>?gptrP*rBIf&L54Ne&>;ffQFA#Mihh;gA?MLs5CpdY<@v1Yc*a9%8` zTb0`IL%(419`(5HSgkEAwrtj+;j#ZvDRWq}z8QV!0t2AAL{xNh7nwf(|n~ zzSD0aG?Z#x>|2%GY1S`^0GmK$zdG$tRjAopO6cC>=4h>4OLT{0YCG2#3dTpqs)1ho zc^>iD(5sq_5$YIS-+Eq`DxCA?-cY#nUg3#&xrKbKtT>qN2E$szi_}y=%9(V=VdL4g z8V5@@c@nEsAy%KUTc1*&w*1>^AeM^s1p}GRm=~>TJO=VMJ`>7T`%SR1wzj>#!8KWY z+F1^~eY%_n0N=mN^e4A2R7Q8L=ecjr>YsFe;-wmBQq)i@+=S61?!Xy0;JCU3+qrUF z2Zq_cOtM@2q(v%uXHif}wd(sZp2}9PJz6dCuiS)bK+Kzt>%6H*j{(hfs2V~l4|zDN z<*^Z{74vQ&E1pqtpOZi9xNm-)+oA_Z-P;yG_7Y*C%+OqcVzWsvAYz5G52laVJ$Hy z<(TMDgn+6O--vGF+oedpc5`$??$IlAMzc$v&9U2$%bF$Ui6{v^&YhU2>Eqm$8xOZT z*41fJIJj~R#rF$u(X|3~vJwhKPfnP)x3xksq5_`bHtNX>ZtfIFZ3Bi)DUv7(ABL-NiBu z!H^g7sVQ-^bieFO>M`k>TzMU?6N+gnNT3xubrb!)Y++`x8*@@mx$0uM{{Y1I zGek4};?(Ih{BZ=WP51@Ibvj$=GmZF<)F#hP{{S21hpcSLb~~{nI^WFsjL<7kQOH-O zS>neZZ*;3E^Eam#NSz6>@&5n-Y=j2kSlPO*uCK~poT7f*v27J<9u+h4%gCGfnCv_6 zk1do{w{Jmgr~9NLD>{Wcj+2QcC@u(zS-SYig9=i}jwxrt;A6=j^OvU#nzJn$Af3s<{6eyLCm`-U z@j0}o=3H(=Hn!YQ>y$+50Q(L)*4G8Dp%=Tl?6i#urY{P5BlNNIH~Z$pvsvZTi+%d= z9=XbKZIDidOY@XeI^-~rvufO{N|m=Imz5nSLf>(pG;BUu+tM!_Z*dbWcIS*`sW9Zi zKv+mu>83+XYE2KskxXc@@>HXup_aY0yK|)9GxOp-9J#u65aA?ydSPB{kt{Hw!huiS zWd8u#97XdiFXSQQe4C5)uA{5Iw8U=pMEOVvJDX-~u0yxKX5YIy+q~^kQc(T2=>|0B z6qRH~nFwAR7{*#OM)~dh4u>&$ujycR@}K1Y06-jpBhxP`+Uop%KN4lTv2RL2^E`*4 z1(OX+%q7-*DJH5yfTk1l#V?0I`yJOL`EUA{pNOmI3)i0N=Iz1Ao}E2G?RR+F)ZYuN zF}`+u#D<=9BKM5y`zgAB)MV;Tq5Y#lfG%arz_pIR+VwPw)4RQKZYe2TeXnGXYh>1s zyZWxvv!Me5+?~m3P^)`>G&R+-uWoL8r+Xj``rB=#`y?uE4eA;eqJ9UqBqmj1;)MUMUvRD+{<-|ZF zRpq4UMq-(qIc>LJfGqb*HrErB{EzjI-y5dzHs=;tE!!d;!Qmb)##8CIT+X#<2g_`E zNIB)1#m+x(!ZEDXN({c~o0TF)yO!_B8`&o1zqdrq(Q}cha$S_^TS?X8q8lCrf!RTc zmfvnX5DVPzPvT`FA9-sb9O^NGSFJq6kKp5g_2ILz4+4Y zA55cV^UkuDn=zfs^^P}fyHea{V)fIb-WJhY~raswW# zuy*XSBk5mio-w(16l~GXTub4`-kN!9l4CCAVA_AS?RaSmx^F9@(QUOxXQ^7Alu()h zjqWdHW7<`_G>is!dEK9;+H7TVFRDMMf1}pry?EcL^>xbjV&Y!en{rYa+OXvy>C-Ca z=Y`EG$w+Re*@oWT@bM$djUIV@zTjwLWn}uVzDS&B^;h+|^-0e#t{WR*UK`*JERvGi zc>e%osmp5%Xr`KE&(eucp8X{N_JfMPUBfBX^~yuZe?R;W>S1HLLOjiQzvce`laOz2 z4ad49JS56?S#@f0@(I@CiZv=))l#JrdXrO$Y~We5@F0Ah!sY#272`#=SpafkM5)Oi z_f1s*_G%*z=jLGi^jeAtRqJq|5}%WT%?cYx($YZ7`f%t#kF3j?#Ga9_*Q*46LFB8% zP8Q<_7TP0kZN!Dge&*tq5*f8yl}F|}vywEVB$6mq(-S;pO0qwilXQwYId8J@e*kSL z@82WO;4PzYV6HvQ#5C_KZ^lxRG)G}WMXn>Hr+Y{6Dmn~5@Pa<6rwDyE=(`A-Wl{os)E9A6I5Ty>3O+Y<*V3&&(tjVqAHtB3% z+#I22wA+`nIO5&@T(c9ZWx<8&pFPBy51f)Hr6hJX-jo1oLyCKu9IlbzS=htom1Iaf zQ^409`DE01)cB7l`tk>Y}O;Dzb7pU21Bv zFsAq-Slt}?b-nHQUY)%&Y`Hvb#n(wlZS9k1F3i90IqISWo7C3R=ge_H57Hl|kSRh2 zRe2Sf<4A?rwF?(HsaD7QJ|E*=0AfDk^#tsGOy(5?E(fQ`?|F+%qGng{dN+ z;MqRK5;|Ailva?{hP$)6_0bXC>;5!)ujJSNF! z)gd6{RGzue3IkDlBcA6hb!7PV9mUNpBm2%i`ib?&zd0v!-Rv7~;UeQA;_))>rA|mv zpLJohhV%s~ZW@H6B5G)noiJ6Mjmy_R0&I=m)--uz#Il@s!Te9?i)2}SL;CgGBar#Z z_S!EVpS=mLNNx8QTCsfItJI{k5h@|Z;j9vARVBwLhR&Yni;9|RvZjmnru?{R`1`TW z-fs7C!pr2eoh3g%{xM#D-X0zjD~s2kP=8S_UAl7r05@*Ou6D?449MI?lGUco1x^Bs zO_B;|+ij!(l%W+l1vsS~cqf1N=gUR&%bMfx`*2=I(_c*foZ7QuFpN2Euf@MM{8`rC zG2wl&vv#~IKGTHFz6GC2O{A&6o0PR?WJ^+WQjcPK41BA*Z+CKJl^z#eR{;@+g{(}+e=OCy`!#o6Kt7_L`z+TxK%H1 zUBtHzhSMDatsy|^=mA=(kEHu2wP!i0O@3qGyb|(X#r@0MLyLuDV!~^hw`9Fi@o)Kk zz3{E2JZ)s+7(A7}l_Pv`1=j4Fgcy^OsJn~Q?)-}3brL4BztoMhEshGb%Ce9JR21L$ z-s_cqI8o@?T-W$N{vZDUm!JB5vf<@9o1N?TI*szdYhOR;ygXa-@#pt6dWqyNUf;X7 zly}wa-uKMLRT}AXWfYBQW||^fElj7t%Wb6SX$ct*5vqwNTibo7qLj6li#+?dyMi%X zvEVzmZ==pw;qE`<-SxY6ld<+MCct9hVcikc^Kgrgq$zRUd9?nU;GEF96WC68_M8XP zk1vtqIaa!|=JO(9vY|G=`+`-djLJo8_h7k|{JN7K(b!N*8gt~5l`LjJ0A==KJCLA# zxR8gc8)@4vY{^!lt8}CDI^StZLdX;;sX+%Ub*nyQer)9pQ3P3en)w~H!`uxcazX$l zXpkuN2WWJ7hvSA%3&qKNa|%h%9R`s8nCqXtwnNt^?1|jb7`aAXnXn&o z$Z-lDDkv-rU17Q;OS?p#|GDWLvl7NrPl&*>Qj6T$0jMokaOg3V3wt{Bf4e zk2K4Sd2+^rgl8l}WPlQ;YHE@ypJ5cj#>3?#Z5d@(8@#kPZnKHcAb_UZKq{qwwT9e< z5Hw$?xjR#GUv^IvT`w2+(PZ1vq_)KPVn9QoZl^+2<0CZ>76P6Grx&TA!_MW>gGbZH zAzL=v8;-@0#h?lB^_3Hi)MkL9$m>x2Y=;*>=xik!)M zS#5@sb)gsSXfp0UPMyk^Ut5Z^@+4x!1+RbLsyfX!$9Ha0j8B$RYX0xc zWRptQt#RsV;yjKvsi6vU*3sUQxbF1*rsDw@YHk{w z^sz(SJgvl>wZ+q4GIyQ1m2Mq^gpsLFTg7BD*iZAI8Vpn0#)_G9JJP#mha)HN4bc%x zLw9GA0=y}$e*;=#_Ew|ILm3Jw zsNCX^xdQQPLWyH+#f_~00Ct5qkxF-06?bIi#C^D^d?S^c#ANu~v;q8~4yaT9a5$*D zX)x$tnnLtj?UBaGTxa7bSEUv(&XF{7(Jyf)I_^Fvy{>lOl9{%Piad+%-I9%QCq`CE zLx?$6oenTP-Pst~@#l}G;^5Kfou&0Y{sj3Ss?*V?KKUDD?QPd=dz83%6Jm6exwc_C zq}kuEi$Ln8Yg!T0688+W2nQbhO1+jZ*E)0+>`SVU7{D1h>9B8c^#=*Rgnh+ zX6^8}yqRydZx=D&oP3EcJbOm*Y{=U`_CRT)a9f7ChmVx?cXYFx9{ zK4yk^mNO&IYe{(wm!>?+j>nMNnsKsH>q-SG3I>D_a|afd`C6)VGWizg2T3jgCwzx% zl?nG@)CB8^OL1-dDj+AXw+=`N-PAc6=%gz(Q1;=|!*$S_89Hh>V_ktM@z=jR0X%ks zqmaBsG~ARCQbPUt_TYLC>qG(R>0W+XQ|=$~;E_6qJZ(GPV*v?_Q52r4Wromx`eW56 zw);<=o>K<7-)CGxkM45Hsp1B`^}<@lP~bXw4UAcG-dmnGUGorpOInmO_h9Z?p;n=Y z(w4UZRV;#X2D#8-j?8?A5-e+z%7OCjT_28$QA-Y}8q?p3_~Nd8Q2us7o5EGmna(zw z^oi__zpAnUKW;Sm)@`WhO^r0xs6({DyI*B94l9nkA>V--&ZGF@FSx-}>yuIN9#?)c z-Ij=7x!e+#p&;o})V#r^X+wam?l$f^A~kzWP{>`ql;-5Xs&)Zi;fo0ICRbmA0|;Th ziz@CtF#B<-)ojWa4M#~(N$e-L4CQES<1YchY}yy=$`W3WN&Ij^y&3QxqJT@C0!hPYcOY$-(iS}0ke-<0DMvuy zxfawzV<2)g&k;LgI$@D>)<023rkYcTZdzza!&2jLH>3Vk=D5c4g`;WhrwT+Hq_pc) zom4exw2(-l>T%?q=A*@~;uj+N~XrcU0p?e|6Fa(@2+b#l69 zQ^^SwsiFsMap3!h*L<6|y(*DC2FKG|Jes<)wQ}F0ee1Fz&6TyvZ_Kq-A}R$JO7sUj z{rL1Q*ZQ?${T1ySGds`I`?jOaZmgg?lGC~NypNXLHYe=T6s;&r$kC{dv^}`?tR2e5 z`14zmX!HD?zV(@M%?J(35{H!NPrtVp34k?0uesO}+YYdxuzNkYJ09TsTu9|_DN8lT z;>HL)K-n)Ul+{c=f;b?%jx3TZQhPAeNTO=e3e*KD-mc6j`8p0)8@D0~Df*87W!E!? zYf`;RPD6Xlp)~t3B?CD$42fz@D6f7SL`BdJqH)tebHg2kh^1`8(8Qz_IpJ`Ri`$1= zO=v7Ge3YSuvTjD;~PlR3Gttcig` zBTOunN$uGZRGONMFDqe*nt-8(YA9j1)Rd5teV}30Ba}5TBgv4j0}D|-$tU;>H<=IS zQ54}=Z%;^X$hN-WuTX=Qe%v>icoAH^8WQ{sw|_D8xf{x_V%*%H2`eN<~OOQqBgI)#Og8~2}(>mBv`}wl$~esIOk;(#@m;P@;8;nD<#xKj>Af>k{Cg++HltzlBjDz0PnrP z+paD=gfJF!($WX?W4aN1LY6dW4Ih8*w%tuHZ#IHPv?W?aE8H;@@gUZSW5$g%EKb+o zEv6z~pL1Nrl!HY%QWSmI$Y$V8%S1BW&R!uo`@J(IxTLrc*ggd6Ibki}GFaXbFATeN zh=&_+De^uruA{CkE!(Q}-Q9mG24_o9Uqs5y4~u?Iy~qH}9OtmK9g z=a{vDLy+r&dV)$+0ef;HDpcAY^Fpc=(Z~>R2TeCo;~ssUUGC!Crkf~2>8*9H4z+uh zI**L#hCTA!<+gK-l1g);sY>UI8*@@YHm6i21@zCSTuM(s!bt!WjIqNS227IRhQ zj^wDEqO?n@)`FM3eJHA8K97(*?FVB^kmJEEl2Q&s5K7p`6+z_{2T1c((-0*>7AUPS z)Yp!sW3*EWaGN3Ggn3LjYu#K;4gyNuM$7EF>ij5DiTIQUZYFU%IP_epJ8Z>DXkqU4`ws@`|K?$E+vjO3zH_PJRf0c#Iq&D z+heMQYYGIccV~>v{^+>)BGNltR}x#73wPNIZ`sU;^a;(eIR?i;RM)#6!W z*s}+zX!hqJS@y~y$FR(cx7cku0ca#SLVHN>#s@pMuH?$MH7~60cI`*fCEj^9=>6}^ zg}MZW-v0TGN+g5UlsQ)r+wCiMqoh?td+o`-BNuV+p(f}0mm?Vo6wPW@b@t%=ZKgW< zP&M7gRpKgHw;t9*$dPfnyFHgh9$~eCPPt;f{@;+k6+o*you=sBMKsUYvF^=FgIC#f z)y5U2ZV-Dq;uzy*&NO4zPBbM{su)Y(l46s}(EKL=o>y;Ibh z?)2&DjTr#mulw(LHzF;)y$g&DOMV)T>f>97g^gjSDioPnxff$Xr|%TsZTA-#yRfEZ zr1-qjN|i&`9+kGMa)n~FGPzDk>%=c$nB%K$4^0e$M`+7D8Ig;ofR?fcZNl*ERV1|f zrJ*G`jJ0Q;SfsmzTU*H|4X3)}tr1p;ZZ=XA<~&qtJ+wH-?idQJ&|u{yAqVwiTcXI6 zLG1xXi8w80<;z&wYu+_l#)_kzeZfTz7Pl^`lX~2XAN$Ur#70yr-aJ?-!#+z|AU=FJ zy74lt@wA|V<%fyw%sSV>I&<$#UtEYv=nuOL{U-eWJC2@1-tlfKv4TTx`OTZP#Qd~1 zMYFNd4MK?13LotOTv}uH#yvHw`^X<~-h4D(;x}IM-BV?@t008vu#bumX+nH;A^e#k>E>`uWZ!k2Jf^@ssdvUMB$(DvoD-|X+o{WamMK}tp zLI+%EZMv0GjR&V1!-C%Jw<<)HvfHGG6rYF>VC~1D{W;~9thzOt^9_dm^ZD zS1I!xNw1bQBv-q$A68^qj9n4nF_h|P-J@^FbSgEW*#SfvJmR}MabI<5z`4ef(yWX* zV|i`usRl)kEJhf-NJ>LZge0j6IoDAga>tN&-uW9}mb5(+VB6($)k>lQx1G~^N>v)) zCG;$KNNqtTzSEXGCog)(j~03!F57ZTg$Il+kvZ)y-!5^iXamNF90hduBDl?DcMEUw z<5JceELO5%wOaNKr&8UUBXGSzqygl#Pq3PrVi$Wh@BaW4NgRB5dqnpy>_r-^wivKd zk^S<)adiIxYM5BCy?+0RTWw|vzf~mSa_5`SFb-2g?W9ciqPF( zbG2E34RK)?%2!S`5s_|r6!TC}lhZ!9cgzgwkKLVawx)6<%XGM;iZ;MHd|ib5F;TeO zl8q34-)<^emZE*#gzPC+=&M5sQb}>B?dx@E0-5EJ#xl%leyy6hH`CD+8sMfKyS$$z zY_f$YD5)e8s-D4*Rm75Z%q{1uB(0}5)fvW~c`B5I45O2zY8eXj#XfdCrKvV7(j=3+ zT4Yz{62m}zO@UBIr)DXCPHt7HX7dt{0gHIP*=MbOQzdU2m8o5%_R!$`OAZJQchs2L zZ7VCIZPcsbMlHw}5yfqbEoCVn>Fq^n{Bc=+g}tHK65Ox@g$hDjJ~eb02K13NtZ6n} z?@Qi`eNXzs+=<%0oH%m#$j_jGaojCOR7(ZFJq1!Oa>5u$N%)e5DP#F@M>OQ=6x)B( z`Tg`}zZYwyD{-i=x7$jV3WIueIB zmHm^ruP+qSHznP?<&%pj;JIqmY5Ob2fpXj=)f=Mre=Y|?U|Q^3b~|zGTXq$mD|sb| zaafLoftE>SDHyT0i0Qx*SutKBhwlh&M4>53J+#m8#HzybB9+w80`ICM{-?ZG4_0I3 z3)YXA8ZQnk)ICyr%`p4W(B*Y~<{o+^?+m!i$O`^+C13T10+s|H6cKAKae6VSbmOi$ zzNCwu%eKu5$m@v7_Vv;*+q-3kz7YI<>iY2`ySJw99l11G8v@YUKp_pP-x)(cYLi@6 z%Vm#&XAa=K=0Kc{=?{kO`)c!gV~Q;EU$(NRN?L%&n4q4-fST9Yiubg&;2&#yxi?10 zg3am2Xz%@#aBdFJ+prjEk>p5v+YnKI_G=*_Lk{@`dQ)e@23Ob)%x+#p>{>H*ZeYC@!uz#3B z#I38a_J-rw)=Q)XPPp#tqcB*pI_mu5hZ40d-5se&O-^`h@npD;S+!1u&x^$S2HsQh zyLKuPiqR#cudlULIOhN;?pF}uZr`?SjFl`zwla*$)*59a-c38Oy%#MJ3(A`zUPJY7 z>7R+2aeLpHtQJ>UulOT9r(YxviEWal6YLl=#?f+XL<@5vS5{Z%zM}hgdP-fl)90to zLiGi36b(zY*d4T5POO5za@-1s9H+FDsO8yJsw(d3HvbFw9&z)m0D%-Vk3z6n>nLydV1pv&noVXwYYbMw#@W6 z%o*gC5Ze(aOH>>TnC{ zNy-+k5&E6|JAGW(=Qm@K$n&J}w=X4t+C|3HuFNvh(Uf39SOoSdS)r)RCsz zvAR~JZSdm1kK(xdk=Rad^&ba~FD}@kKsRftY&r^@NM#8<1qNiCQ%>Vjp>tzzi%GJQ zZhuvrnYk3=Bi+Acin@eHi~=Y>*6WAci)7N}gWy3oJ=uM|Jg07#i5CSPs&Un;ulmOc z$r0-N5sKE~9xC-1dLf<4vvnnD85-BFA(;)wpw`@*uWRinOP{)T938a*k*V3Ur860@ zk=s{PoJk9Eo1#FsGGPU)Im^s{R)0sFROIV*t9nZ1jl~mMMUQJkT=e;g{Ekari0za8 zrHoqG*zV0#tH0bft3r=hey=>i&)c%^wD^yT;&D}u-oSZVlJ6xqOR|EOpaC6#7)O0e ziuYpLdn_$3HDw%olI6#up-MiYyR(=#rpe=b_Tl9F?AZ#RGBxHSR|zn!@3dGhXS)J{l4H50^r${A8KqQW!pkk^~ zt1RUz6|Ujw+m^YHdm_!ndm?`Al^_K95Tm&GLh4WIEhEcpQ?+Us6L8)!vE$8lN+=_4 z+VV2)s9KAY_8lR)`Wdhe9^ze@>Wh@E+)mEJjZ4nihb5b>;$pRG4WYIwlzVko9uwUk zUG}^gIN5zk`6KBb#{U4VyYA|grw0gmvEyRqKTCd|8;N4{{lVvY?r6hAJ?FTTsjrs; zXcZb8X(2iG05RfQ-_RmtX-XO%;F5HrxAK=yWY$l<+xQJXzZI@xbEEd zJ;BsQj)vQLD>PH20!SVB(BG4k@VZzXMb>dtJ!`j+s=p-W#k?QN;B zEd0^TyR(yb)$SJWxw;&(S!|LoPO|_s8%a~^b(9AvoU_b~V>TV`<(o}fjazTrZ~KRC zvvI}TmWeOiax2w$)pylrs<)iCZ=O@1aoVk@m|G6s1($5F0MZhe5Q==^Py2N**-TCP zZbn1q$dzm1;r92}#GzTXY{!<&Y3=bj=Jc(>xuhaQ*IbsPns)llw)XsOYI%jENc@lydN;u|;+(3a9>) zsHLZg(;g`F_s3jev_~ZD&8-V=*{!Mxjq4|xVmWHt$$157E81H~sS>U(1p(!_=s-|V zDRDzAHts}Vxt_m&*)K0KmNEIINBchC&)i0wo$CW_*>AFC@Vis_XOxl_$n|Z%;=}#U z5*EDM+qMRo`Acgccu=B{RJOb9!()Y)fx@;s)J{@(Q8 z4oY-HDo&=}+;UhXpjSVYY}AaDO=v1F-saI3mTg69#m&35Jwp25-g0hjIK(}vwHZrS zJ=Zfut7YuVOJL|>3QZenv?O?1Y!$1wzXQ(PAgeHsoPP`nTIb9hOX-f&bmQMl&98Is zX|1jv@AW+)pDN)gRMO;oBMYy|#Uzj&1h}!CdvRGij_0K<0&KypW-z!%)88g~jNP!A ziwXJ5n&^Gt0)IhF#rKr@L84N{gAa^s@JrU9VLFP^v^dqyb8y||NK)iDj z;<(=a}FvaZ{L;VoJlLR(-d$DVmch-?q(BTcmIwlQtmtOt|2r+T4^;>5NqzQPjl@a)|L6Ji^`B(Bm7DomuAM;vD|(viR(H4 z00rzR#>U#baes%%u&(#F9rS_2&o?*J{{SzI5oB`i?`_hE+foCG6;-jPe@$&5km0CM zpjNMHhD^5jM%}OZ6XB=98gci(4<)aPW;=SOLw6?Y9dz*f%5Tmb>An3&axAq%b(r}C(O?T3S8g~zZ(E8!Z0SiqsX6ip)VCBWpXE&kJ;MhLLfWQ>jpXiJ_Zi(UMLxW( zGB)#EJ5&gYkHmyIl2ivmFt=kQC8@=t)pJ+V0@=PSPIA|y`?5Dy z-H?*{_}$l=T*g5f)$&iMfZ(n{w>(|M;h6c3uB6v7;`kiMjcB@T`?T%9x@_;*?)JA7 z&BoO!l`|oSQfN}NECLQJzF`pZG6eNCwe9<{ovb3wLsBCp^(B?5DI_UL7<^p;vMs4- z$~kc3OSBi3pwFJrG%G<06#FqNxRFzL9i}<9{?3lQPP(8SVN8ER6Lz(SfyI);*ZCjifhYiboL4?0_wJCH6CTWlri4x~$1NbzYv%;QUi8lxk) zDJ!L};l>4zY}*Vs6qyP^F0Ct21d&mfvjF5LQqLA`L`=Tfw+7;3E&13#<~P>268`|6 zkf0LNhe!m_&|^QFElXQSgL$=^YA!o{h?etXbukL0#;@s>)`TSDu2!3MCza%e_Q+1z z{jK$GcW%tOwJ9(q$7{`sw17S$q1L$1Zabz;r${Q$)a?6KHP!2t3BQVF>>aPV-Mn0^ za`^@VnkkQEG7Y&XdZ{7BsRE@SWlUxFJ>Nd;X$0cUj@7W>LRFjQMS8H-!3d4 zPBthJrKJmeF6AysDr3Yls83oN(*oe*Zqw3p(3?9eZM5karb&NIEzP(U&b*y_TnIaR zZtXrf-cuO0!it0>Nio`OrFn-x4ONdb`pTng8r){voTjM#o;rR9qWvf3o!QPj5+QFx zvDGh>Z`YXs87#o6-9OIrx|R2wdcR~^QJohAXa1!>TlTZF7R$ohJ)T`NpwHirVUgfh ztITu1)$$G9?fy*`w=EFdXf>&l-P)6Ex5lao z(AaS;l~8n{Y}X$qcd~XnlGMB;sry;=Z)e_Dc1Ir}{{ZNY@{4C8@uc;vc~T`vgCSL> zQW;oJ;s!n6vhQ-*RMnLaLwws!}L@;~unJqoj_OomtX zG2ZZ8Ba`5DrDUL>O%6vHj^Wv3l?tsSxHE6sy~}eB=PQ1P#S;$y0OMO`;DXlh*=TG= zxu?tsL90qx6)FD0tJ#e9!Q67ObD@8VHa*kYkjb(0SlZW$MP`!{=>_%2lFFmH*`m-! zVB^r+gqDYnUbi@C&~2njRX;F2hY4~iEe^MGyhX^+BfQuWSEYTa6d0<#eoKL2YSQrz z8%4PSs|!^-RBL;G1W-Wgv)2;2$&yCYQV6>2AP6l0doma&nS;1QN>q;_ z#5*0*)5tYx#FCW)4^T%;EF7MUWG3y(v2-8t_Z63UHi#R+(QCe-1VP=%MN;Jj5}9a7 z@el2c`|vl*+_LKpW8_|2$g!0UTbfJEXkYA?jj<&RT^R+515^u(YV#w=Z?R3SIT4F| zd-1rm;%4_O3FFIOhs;B6TPm)szT6YjWk+2Y*1@e6?xjDc1MW9?KvrY##bdbB50XtBlZSS8 zGEx$aDg)x{{BiG6$I$ReT2&U<+&2Xh6DpefO+M^Ku~Hc0Knc<$$w~Q%!oAsL_hG!0 z;BpWGbURZdgO`syEUhwbBi7DSptz}Ocv6A7idUw168*XYN_1gMoL25G`C=^-QK@4h zPf83%uehREIulPCZEIfNxi>9KCHE3Mq%M^jz4*%S8Q1PABzIHQsW5KqeV+Snek|Fo zAdoz!iLRsU?!_(+P1sLRu64>&{mHX#N}8si+I*PpP&XkYQO|F?9*2XU7adq6FH!0% z+IW8FHX2;GOGH;46=6M3PSe?j-6kcr+w#oEY7;FagtU;5kdQLzjMeKBl&Z<2D5O@dS!eg@Op8=D zn+U0LM?zKH3HM`RvEERP7&PI_Dv$(-GT|>wmh@$j?8d6y)giEwV18;(Z7G78(n6J* z&w1R}7A~N7aL0mfTMV+~`*FDK{o5}FOUTde*tYU2&O&lG4ocaZg<=E7O|jxjWg$w+z5ST_BV+DJ-?FR3 z@~r*2#(EKb$_2H?o`pT{mURQdRA_Oo!_1VWp+|DxBOr^y8I3jOW3S9@v*B~U?#8zr zwyMKyCf1@~nxoioLsbc&YBIodDQT)lZ+;ju;?Pog_FOb=9$I@Tjs%Pv0$4g|m1WzB zI%AX6DdjkbAZBz#_4R_EP`s))Jd`~cjwZ4~&Gx)&?G4aQJE^o}6sL;3D60J@Q~SooJV zUY^P!0OPI5THRq|dfBY2^!DSj20+OZp_MLDU{ z_|puncoN4BellPx-!}~<48VsOC$SOJ{WZfVIc?GNVKcjLE+~;8GEZUdMrKXwP@NrGR5AgMsb(n`{zznNOso}yNxnD?9zxRF_nSk_(2 zLY8Xbr(mug-Aff=P*I0+Q`Bi`1RRR{Miwa$T*pgLc}1pHJhuR+W)@KyZ8>oOMc2tX zQb`n{_hG!Kd&a4&mv0dA)1Xp>_u;ZSIL3joF)Mk;LuhZHQOuk)lDxVECM`Mw66UIY zUd%ZY!3mmk>5id2MQdHMMal5n38?(X5nZ~Xh^|dUm2JCbJ1m91rHvyo*iA8CJE@h@ zgOd=iQ-n9XhDVkF0Qb`lw+6?;(5JWV0Japjsr`!o01OXy#-pUD#0|%EX~&e)RaFTJ zS6||AUNxEzw+e~&RVmpBLJ$;d*Arb-Lfk~r3UiQHVHyD>RI0gyfZe9q3u0yJ{+UtK z&;hkKnwE0ui6M-}FNlrgUEE8>Ma^``3j1(JM44NtQM-~|k_k>#8nMe9bGM-X0Jh&7 zh=id@8Y`IsxCVCGA_d(7(RH*fJ`p8UQ*H$22=`%es-{h5RAesAn(F$BW59X(8h9I8 zzktQ7oL3eY=7*i1W$p)*3asx_wJJeK{BX%++g>2ooSy+oUmI=*T-4Mc%WM3qT15xn zhCJ92!sM3_>l|ryj0em}ipOfTyem%puspNkxDUA|D6_S#aCNF?0~(q-3G@E4Y)lkdcR867F4g}Gb8>kW@ETA{$GsA(+&K~DTbZyCwbqX1MA1fh1vQz_TBv2DTFfIn)Z7Q@!k(no=6!_xXxUcRuxpLOTFKW(t;62Ly zm}r)v7A?uD)ReE%q1rXaX=Ukg#!AZrJ0eWOdmdc0;Vu30Z z6s9oQ+hesVqf?8Q8;XcH5p#zIW487_USr_uTSGBi^R5DegyfWh?m|jB6i<^~zN2tF zXE!2Ll2405)JQ!#1CL_aWno!fq>N_ijeXBV#=W-Hxo2tl8yv^MJwpqp-l_~6e>)yO z+<@`$Z%ObbJ+H>Qvg+~#$&f58B#$x6jJDVSA7xZ=oz32@MS4?Z#jUDU)=3rvXY3ub z3gWhI{HJIA5K5YcrOPWLDkP*N6O3--yxxBevrUPIYAab8zPYeq&vi+?&b~unkfs}D zG4%xGJ}g&QyO%YhNG9a*VEoAokGGb=x7?yTZrMy++gYQZxjLx|%ePNVQSKPI4L)fW zJAP(5b&3=@2X$|oGTh9oHND#({{zmYG2 z(G-4H6mLz%etF5Sxpi?$G?x3WKCcm=D%47`>UKMq7Q`t;k3YY)a ztVP^*)kkUMg+-Q{mYfHN(@`eC`^T|u~Ac_-CSAYsze2`F%opF)OS+{59*6i z-&lF472*lc%NVc7glJ7>=A+g<*>O^ZFzSl33qj19=h=#9hY9M-Xkr41-A64 z_C3W0T=jX3!*rD;8R`O~vmU3|Ta;{O#&TL2KI6xCG2B-Y!pY@JI-5eHHnE?DMIABp zzDIlF#b}QkU8j*Uzbn`fJq9HpkfR^~B}C zN|hGa;vBiVZ)WjzL95q!O$QX3^eG1+Onk4r?>QOw-0n%yp6j>ZTEw=a1;A?GZWl+| zZ6bT9LV*Dc{*z#Ipa`d7z!&z59-W35NofiLDsDkgVGdNOTLqod^` z6+VguEm@|$rxnvuldBdKOf@R#wM0`*yxU1w8p>!1!(ob(36;vCIGXbX)mQZmNj3NX z08TD!`cYYWoWg699Bm;DGo>mt$YyaQrBWkpA;gb0M60xBa15hS)E?VbLgBi=X42cv z6;!_9D$Dp{n?6dMOAIQfz_c+XUESNRqS0Yy*)i0RZnX0V1XJNB8D3aMb#A7Pw;U-H zRlC<8UL{;#c47mLl@%^s0#Z~@N@Hh1{x3XIfUQ#X3~t1)DiKrKxReZj4r}1=$>L7DC*9 zL74FbtI9zoD(lb;C;PeZ3W~*w^ARTRZL(c z%NA>{+TgjDW?ba!-Ap$VX<;gP#+nOFbv#m(8fT=MP}2*MiHS~K8-H)Nw9)NYd6Abh zj)&;4#J;qBJ$i29?X$ytGr$)MCfeCC6AHt(?eSQN4J@*dhSaryH7Qi8iO&zkmKBKk zmpL0wRq55#h_|oQuhay|QwGf8)?%=te3q8#pLb7XbbtZGvE1+*2A=>%~6FrM6=L8tH{X~6VT%SOC{cmlf z)u9HbKAD9K0{)|I_P@0-sQo<5x&6%&ZJXB5syWX#WL#l9gVcPPMSjq%a{8UNMOINl zzomPWm!?datp2I}L(gfoIX>4EEQ*&|Stl;wP9UGu&7MzzAO8TN-Pip{S=|1hePUap z#JOw>9jB3BSpoYCEX!rlm1q~`k@w*HJ@vHR@MT^90H^zvzA|5lq#s?rsCIJ=zsx?6 zw=IetXfGDX?p)XcnUx6jQb)XE_TF~Wf8lL1rsmPPV`}KpX!zsTcNOntM~MBv*e;F% z=Gv#BP~Sq8*ik1+f%`Ef&7Lh*5>i7J0K7m$zvDQ{lT#7Tk)p&{0SgSGi=$Ci+N8JTYc0nPFX25fQ`)V_t(N188F_$z`ZRdway*HA$IH9-mmaNTN1wSR z!Gz(b=8bAG+o!UabZzp#N}&_Ul(@3P@z0~bOMFaQUdZ8%zipV^2)VljnNtZLnNP|N zET3sOWz36%x^0#yNP`KKM~s9=j*tUvG?0WO4&Ze6iUkASFe91?R>I0wpbtu63A$+A2v&JuJDdg^2)~`h1f@09Ye7$L0(WQ;>>4-T zN#nhm+lsyJSnW8-s-mUDt<1l&l5r!+gnglw_N<$WMMdo@rz85}vf5aY2W3D~q!3cw z$xao#cX+?kR_$T6yEkrb*{|DXB}6HiOj+ExvZ{`rIKvjUA3Pu8K{ZGBe-v~l zn>|NNJX5fFa_IAuZaap-ZjmA2ekV)>KC?{g*BPzLAx3Q%SXTM1rWN$($Tg0#xpR6~5$Wlt=~wFelOJo4x3<({g1G)W&B{*e-qX=wOR>Vk#l|AImn1pT zfGG`=k2>xzs+&&RYPT*brNX+Nk=ox!x82(JHtS9clm$HfRU zeMtI(!TUDVh>Jb7C?jGR1Te6gs_oepx zQ2IK?(bzqmWoZdqUgpM~yVLyZlkUjmJ>=!kzIDHEw<$_1QnOH8xd4?K^-@KBxb@D~ z`U7s;=fA7+M~C80M;+nkDt9$T;H~*b3Oh~j!G?Cb%w+XJVwc-Bf?`! zPq9m?y||kPvE_5~GpEllFAulAqoaScLxpy?RPQA-=THRKqmnskn zg>nZPSzonjQVN9xBX;AgokzvHY+In9y(deGZ#Agol>PXwmQ?4!v$tw1h;diebs`;t zBBi`MC1^n)5@|tBxK)h!v!yin7Waa^4s%a8w!PJHYCg)?&_|ti-PhNDZGy`Q_{vNA zONAq`KYj+DOd&2<>c(=)m1yc8r{7FmVQR8T+i*wt2D|?NaUt=K8tlNSvf?t|Av?FL`{w?;DOUZFYJDIai9T%SN91SLp+>ye_X zExuwu3`=2i=B_35y9Q><^sLz2JdElU8*uuAmldaElCkBfs~al{`F87&a)oOAaa(fZ zy6CtTSnyHSL&JbIusTZdRugNIZ`7JfHe@l&Rp z1Cf(z(rIxH=-j&3FAqNfna+N( zE;-xKVz%DVcKha^4O0py$l$%Xd*L5VWxcbuVI&RLzqM(VIH9tYtUQ{X z2Dt>0Tu|oOxsg6ttqVL1NLpPPvS@-u#y~ZO3lN zmgK$1c9#3ET`zN9SXzm7(p0@>rEU|KLG1bW>K}jG+idi#d~d}1yRrKfHyPYiUPpL# z#}3KZdoi1a*t0`!!MBjpA12$e+c4Q!2}&Dop+x7IAIBdf?%BB);Vg;Dy6di|u-M&j z?PSLlrxw$rqo$m7E?*AtHqmki{BY)(P3+z#+L6~X=_*r!girDYvr^>G;KhCv{vOr(V+kUkXEROk<=ZTBOIWnH-% z&HOR`e$V_BJHBJRb;d7G61sYv=R0=)0ACt#X9QTz-t_gu&HmZJ+Lj}%i+=w+ZW>d@%TVG!$IaiPQ*Vm;n>zj7p{mk)=^K*X+DqFjrR2d}F zjg@$kdQH6u~{4}J)QRSu2TnciQg{{YlucktEL z-vU*|S1r3CNJRT%t`dbH&=Q*D2pAh988xA4i|DCC{GmCDSaSPLqzpT{=yC@?nY^rwz9by^PAn{CE({kpU1D<@-^kn2*r1~^$A6ip5|x4XjPPyftqNOr zB+&b|b2ODHAG>Y%vw+B}L2X_C0GANkwoI~19mX5KYTu?ydL5!f2U$V`;=XDsqm=-m z`>^Yt0od0yWF^Vgn{D5D-W_F1WoUG1O3|czH0|l`IJ?5RL}v42%{IxWWGW zg<^L^)xSC$ep`+W03|A+p#rtQbFme`(ptGGabkDV>?9-AB=D^-K;ui$ZNb!el}d-( zOnjOD0Aa2^wlZEb{zuXuMc1O|xQTrW0`>QMOsxY^;iA1MsYK)5`!dhwGbi;L@8&;w z)s`;NZMOS_`AY~fqq??&jzpyeLbdl}*E|v7tm8|C z%CUJbhJ1bW{zugR08Fpf7IyuFXX%pe>vm;LjHC0`36QRl)H>0JlH$P6*^fo-{Eke8 z>@gkNjabQ0^k|76H4+`kl?NG~({HjzYF$8I^yA#E%TIapt=oLEpxWNwyS=kFGG}BBwM95lZbS*bd;X{>`d7a=AUqG4dk2DWDtDAUcHze zT9aD4$m(A1Bw{bBPfy#YfNsvW{{Rv88Xvy!%k6!vX$+;;rU zJ@)c(>*9`1+@Q<0&XWdPDvb1J7Tk#I1u8>`QA(1adJshpD{m=ARAxJXp0uH0CC0%N z%#mD23zAG%MuzhUF5AIqyynNo*9aojGc-F zj5o$byobcBP4fy&=|@9g(Lt>$PiNtX*gR7~gcTt}v%BoXHwpTJly{bCuBW>^Mt4?( z=fxbaD`d=e)p$y-8$Rq(o1MQRS*}6W>(HmE1FkWB)!Q-3k5U><9+lYL>vIx)E1JSY zxxcKo<+&C_g(79L?vFcfZf24KUTH@lx_>WkXvHsX?~~Tt->N33yL)QQ#n!g8WGi*G zxovY!nzD04$;}EKgdA)wW%C|im$ph0&_GLp#1$k^Q^G-Two~N!I(&Zc0atyT7q=4u?!h3L) zJEF(6k9JG8#Z{W=9T%Ov{Y%z&iP8^EO(Ny`ZRn-#%Vq$6vOR|i=^Y%I){q#zaVDkw#chZ z6&}n-D51{Hc%Ld%xAIJLQ_$h62@O`_yhBKXJ@q498A^>c95#1SBIb%7_Qcv?H&>Kj zeFHLjSKKjAdQy|%LfuW(xI!OGkfZm~_Tk_!-!qS3?1Zy?O z9?UZC1Z4ON7bGYD0G@9D05l#Fib82rg%Ak)@jKgXrNDn)W1b%&1vn8Y1;i+L&1->W zx5bLGBuJJ`(Y3COxWtH!y6!|aq21YD>{DjrO5UhBUrfgB*svz;%ay^%t;KK3Ta44F zRVoO?dsg8!c8n%;NTv3;Qm(fY!DW>d>Z0;F3ecQyZ)7M&qi(t*q`WO^umi08Pb$K=WM{HK@$>$D3tfxIGO; ztlIGmi)PB4>vC>8iRO~uR@a#(oyXgXn~N?o^sNIDu-2?-ZYa=MTAsDs-1!Fo0J}(7 z8i4GgaR3@X<-eiAi|P8-tgXwOdA&RYs~WF&c`m6k1Uqbh3S4n?C+sIY4Vh{Sf)f^Q zx|4p?+qU_yu&bux4nsixC$F4B@wC27PY;n11t0bA5XN=`VeTfiRxVuB;wgN#4 zr*0{6H!R3aI<%2xEKFAlG>&TXXQivA>c6{lvfD+YZ?;sR@?1ux4!sGerak`vv^(zA zyDBr*N0{$hM3u_JfnKbBl~I40DIY%~EO;WWt46h`c2HBcnBLv*+c2lJr?I!+d_*1V z>Czt3h~>p8pCaCaLh6!$pncRCV%u--+Zhj)Gq2*_f*^-u?D%e$U57E!kBBKrLCdd9 zYU6c)b4`UFxow7*{ughkf$*uVh% z1M$M7B$&G%;?oUiK+>+l1|V<%yN=}Q24o2UsZ~)uYl#z~B8Mnl$f`?aa1njE+T}CHmQz`(*jeYv4 zL=7u7&okeLpdJ7dpZLZA6DcZ%goF}9@q1_~leY$xSeb=53* zLK35wvV#Rt6LTlT308Z?w53+ps_F36wG95;J~N3lQlRzQyEgdyh+AXheVq&}q&viVT1Tb@fQAC;ESpTd;Ft4v;?$Gq-c-0O;5yPM%Esh}kl{{Wkd2v~(U4~*)@ z;l5nmAg*hq_m>t=;&Cs_#V!QAln=K#`3R%3iBKI1kG}#tDKZja*l_nV}fjZ~wdBOs7+ zr{9KI^obNVVcQ=^grhQLbNqX{xYDly#Ws_|~+Loq}mIJcfO;;k6Dpf1l zT(OVM$y1i2t;S86fwK+5Lhf!fwMSBhhP3vRpT&UgWi=WgyH*h}GSk*5Nkn$h6T&Ex zY3;>dN2>J=QsqQtHRB>&mnPhT_135cx_#KUvw^0CW!bqiO}#4B9&(oSlHgIJeR`Zl zk%u}9WfDsYcINtH+tl;^ZD^E*l6xsZ+koN8YgrZ%wK?z_7u|uqjF(zmACqB3mfwA0 zyu0eG6Akws9@a;DXTL2TgK=DWDhU$qyQMTN_**Jj^tlHx|9)Fz!?m369*LNM;y z!h|kz-EIBjw|&m+t=0)6oqL0KZ&@;3O~&&p%w2rO+gh1VUevdz9_NF$jt4N7%6Qv=+{RXRKYpW89iqU7z# z7s~*U5|yb`>x6ldOGT)$vb;j9*!HtaCHtI-jf}`?2|+*e@W+{vE((N-)vp#PLvv`w zQiFD9<~XY9ThCEHaz+_))=apuEzZncM=iH?vur+VHYjKf{{VBX1!wIG>wzb=%1c(u zNNzEY1ZCa#c6LJDjPhX>lV2&cpbxtX`Yor$RB!6jPfY}Sh5qk#Nkj9Vbt|U2tbk~G zWs6))D^~a-t0yw#<*N;CTSTqNf4141K|eC;5bCR#N|XUPoNjwh78kQ3nke_1(vCct z&^Ns=ZCLS`Ua1Q%Hiao#)&NmRBxR4J?sncINxcs!x4h&=Tu5h;IR44A;xgT#3PiLK z=DfA3UJ=|6emr05cW*-7y1{6A4`yz&l=WnLZ@Nr!v73;Uw?GOGV1jY;u2q_9k6XvT z&BR{1EV}_k=2DWvJSqdOSQfw{+1PXgFWxI*6 zS7=3*W^^>L%ceuaEkcJ#k%=-5X_|EZA+|P4^sy!07K0 zeFZr`b^NrO{nmTSbf;W+Q1rL$9&3R8_{C=ZQpSx{n6}OKAJGXUrI9p_uW;}FULB2X zL@67qY_%>(qMws@l3oCh{YnWK&1C&V+^(uZJhe5r-HF@ylx_30blZ)-kQ^V!vE!9T zg%`>omTmAEXl0!>yI#KRXRq(Ojm25qFKu7i;iao9?=0khOB?~PVXe=XZKor6$$dlO z8U9^379GOv>pPC5^TWNAmrmUmxP>==B=9cGd3NZ?Vq~h2mYV^`6o1t~S8fOY0NxeH zNL7Wd%DcvGBty3g*EsKITxW*(Zsz<+VvO7wvq)MgkOxzVN=II`!|mm6H~#>|26fC0 zs}+82$kYREeKY+}at-_QlTX6Z4lFwDybI2S8bk<|qMNhA!;(#`Td-XUd8z z-RH34szcWOg|FHlN%tb5w>PgcH6KL_knJ|oYu-HBYd`gIpu_rWU2aUy<$X_z{{R)L zd&^<7eFOS?+G$}|72P*`%+Qt0m|JN_{{UwgHZiEd`h9~PU0=M;<$XrnXTp&uTR#tQ zhSMosmyWGb9Ao>pl)!akKDwUNaeIr}xKQB)W^ea4ZRhHn9>2R;?ZLL;MPe&y$t|>o z+ejbmfr}eEMVN4kDz0vqB=v3m;TfC05K5A#5^G9~sb9AR%W903C`%09QW4%(4l>>a z)429UruOe^(jdlh%Jv;vD_^$-W8yn+M#1l2jJTo|lf=Av3B?_~5$(Rb{I=jpd6bWG z0aAYqT-%-6Qnf2(3v1YHo-5^>hz)F=#e9X34b6SlkjU;Ny3wfnF)PGU)sXz;U?)lR zEz8`BRCy=V&ftw~aE~_2YQl3prFN1ZQn3?@87#Rn;BD=b#v2YRQ8u2^fn;HkMw1#lO%MR3JRo5@Ns{#> z#|yibIx=anY5Xw-C`_Q(jU#iqr5`M5?gOC2vS}hkYf(SrZaEf8E#GTC|szqCqp18Ma`KXz=*_``%DkNj)F1_9y zzZ?(xqqw~#wZ7iG)n!Qg%ICYiZCc|c#d z>?)@4^*zZuqs~jWwZ)3Mff!_6z+wH`kB#{)iDK;?W$$Co4R{39MqmG{e&}`v$;`)yA8y{0>o|B|n?U6Wj7j|~q zrk0)k(aV&(NSxf2JXR7D=BX_P!hN;u!LMqM%R8Pte(KPxj^LI;g?PGs^snKk;!K}d zYf2O7=EZt_C+2o`^s*DLzI{v5py zXK-ChNVn03W#EL?}Z>X`oJ6v$kcDst0`m4X6=8 zOvV`|6A4ir*7SBO>AQ^AU+<$WUtxWJ)r)K_k3mC{t9S1?BkC6Q9%nf7B(Um4c$X!h zzEqHb<;HC}sE|7`)0|fpTw2AH(ot2Zf4-UilKn<+c>A|>+qk;t4{z=dln^%^$};Vc z9cis(tuD?D6#!L{LR1LM9AkI>@xaA}XEcp|$JtrgvZc5-bsv)b3VLXhV7FR5U~*{O zP%RRXac_*ayw{_??1UjnN@PrAhja}_p|FhloOmyB{{YymTt?%M>X+mD52QOI{-$o* zohNZP$B&Rs!Oh$Y!4c$bA4a~N>|8}6Zj`xrg==PQ@=!?9m8|J?N>}!lRB_?k-_<_n z-sjV^Sf}r7`oFfn>t|^8?Vh&bD7h~UJ>qojvFZbuo3cb0u28b&+CrpC^3HW` z9AiWZcj@rg~?Xc>%zrM8kkvWCD&`?2ghe#c_R!5SYn?tA`DRI7GPu!q@@#+Mrko(6V# z6(&A6;O;}@#wOk^7KB<|k%jW5cPJDtkOnKYp*it(^1fV(jF3dyU=4X^m7^?MY!4&9iN$ zKti=e0R#`qw+fZ*#8|zBEAr~+P09ZNr^E36b)cDD*KX43;zpj|6Phl<-@KD^7cI?q zmws}kK*(^2%!AZ6fkY=R;aqJ#YabThEvThWBRwm3@*gzYY<*kyrIy5ms!EXTq;*K4 zKqKFZdFEZb{ef_?2rYP;&fN^LR$ww^KAf9n0FqC226&Rz3$CI(uOffo??{~C>N4Pk z!?!NMw?Wr7=PeF39hS$7Q3iz9vkc76vyB=pWJ`^ScJr?Ub*F%REOB1N+;42I4C2W? zt9kzbZv-!L+zQx_vv#V^gXi8ZGM0#}A+)S5NK%W1BB)BSRd!i93cMoAOIB>zdi}lM zYI(8o&D^)aI|4(W@z-?Qy>W8wtz|a;BC@gjM+0p)+FcUVf|Y#5`PR)s)BVX(a|pu1 zY7|ygW#{ep{yrZ15G#^yvh=UpPq({|i9xw@)E}sSSCIAWv{zwHdq$>pQ-D?oR1|A z?LSV|a~=!KG;?it1hQ1zCcm&!UQq7ofiGl4GZP+nCG7JWEIrBoqAzxxowe*~XK!0n zmbTW%wX$Q{EzSHXq7V`kvp@>hRUXP>uH^1qW`f2ws}ZYbRyHT39vt+MwC%i+>eBcB z0FBKhb`CPOxYO60qLSEi%0p^b=(pInm(U!rx1FmHthJFGF5SqNExi#C@|USTTJ>Ld zv2d>#ZE?4F^-?Lact>tV3`Rk%JlU&g1xQ#Kt=Eo`*DPmpGNpC;v__W~ZH1Mit0vnV zUJUdf2{--HxE#0YiI&jhGy;ixV`VtL+9(vvoS*rqJ>9j}GF-nd+SkmshBSG3uDT>q z?c>Z`?dt=P9*o4>hU&lG-11*_K4RQ)qOS-66bL}BNdP2b=V|Sy2hVZUlzX3W!Vu(x zq)xs_FCMnw23in%5|kjjzHFcQ(a4YpwmhNjTG#~E(R`Ji@Nz@ z$_eYM>L+#NJ9N7y(U>+T88nd0Ews{-nfFQ^A=Of)2<@P!c6g?pw{h~9*fx0=GvaaM zj`o*6deG(D2MO@r*pa=ref?!@*@KQX2H$T&fOh40nzWYULt!APZ#mti0ZSnynZn*3 zo_V@2w*}(<&Wli)X7wFD-*=hP4Z+O(J@o6^yPtsl6Yj{|jp}D}M_s4Xx31Qh6pWH- zCvNOuqQ94l+Ny_U0#v0As$EJv`*O^=koo25hl25^x8LKzwZe*q$6@VxU7TfqRmDfc z-!I|#`4PXRPe|A9Mw@Tk+nxi%ZTX&TOInMDRKl`dQ_ii@RINotN>YhZ$*DQl9tHIi z>Xp2B%GI}7A5`{NWAgrO!@rzcw{~9HLmL{>!%*+=Vx`6qw*0WBz5<4c6eSe#4!>qT zK;HKo*%N&4R(j>93oue&3gTZbCgMG@wzh=MYv#Se*3;vA3SE@)WvMNqQ*BhyWoI;~ ziX{UzV_Vo=g>LNQla(q#cpp7|P5QBaCn4?IYlaH|eK`<)-gogCsf1`^k!lJD&TNsK{8%#%W#^ zQzJl;kT~>C*X`Nm~Wy&uZVyxZ8^kSZsW+e#}OxxZ>_FA*KC1E9%6LW zut)o$nNS^a81~F;X-Y~l_)itj%Zw9miz$V`J8_ij@?2DxTvC?f>pGOBtm;yUJxL~~ z8tXQ>6%Rkiy5{P^Ti9Q}G^8@vOYRTTRR|UK(*{Zv0EZGTzU>{sv?dcZl``J1(|I72 zD-gP(WeN}7gUF9s~HrJV(?Y8&u$S^U@Ej&C|z|?UF5bwa4JkAdNK)#Gut{rOi%^sRtm&p7?vZ*&sos_%XfwSUOG-oC{dlUw=jwzBR`vU^U)GLn(CPFV?{0< zXhj1+9>Gq%Fe8Jj4z+nv4y}jGe06QKwP=;PV@A5eap5X6i_)YhtkfFzjAyUGbJV+z zR5^I+GC56Fjp4httGjx0H-Pj9gR5ylbWm^}gV}~o*E1oLOqI4D2qNVYJ<4UFQdvpV zkfzBGG6annktbB3*0AnRg%wPWh0Lc2E z=)b{p{{X2FdJ}ysYk5DfN}p6T`_dDSqx()gx7skDQT{1=&W`TYrTwvfjGC?WLv+WW zB&~W400Xq+*d+zZj}5)WoWe%ky}NsAS+hxD3SAu3!-)YTX*DDaN11Q(*bX%5I+6#V zyW2c<>wAx(-BJ;1-)~+vh2tBi6TA6eA&JDy5yfE*=~2uzbUn4MMIeL4gLrpek}WcB~IKf*1X9-#VY>Vo!%+N zZ5{jI?ZYQ?aTd~&q!*{!8dbjJlTBV^L*;^f=>s0~w_oS9kDNDa9Xrh-{PpFCXl1A7?f(FB$ljY5k?fCN9~y7lafgymnw-4Aq?&3y zxC&FFsHs$?ymP?l!X{Z{qC+mBKmtL{C)lBp_F@d`%92Y|S?xJIlI46E zWqopSEvE5p+Pmvm%wo{Fi)acRV?Pf)wD%?L`X--mM~NF%z@LFt!%9Hc`lYzOSaszp$uCkiuM&>HWw(}h5+qLk_X04@$iM4bf3?JILOUPNefEAyKP?znLPPhY-r$ zLnD(w;$A@psu_E5-l5X9Rv5SL3zddaE*7aui6Of9#RyV+QlN4m_Z%$np?iwdBM)%9 zkE&pjHyyihI;Ar(Y|X%<;c>MoFeIp+&=2Vo?8^t^M&*@GC@So&WV-N>+PLnG>t&E( zT_#J8vMQGg%2RHmu}E4?6c2dCt+~c}nXIT?w)NCVb9LMv8kcZdmE2UL{BZu80QrD| z_YBq7RF!AjhC3|oue9fX&qYlDk5WH2H)=1;^VN?pGwHIL?oq)7K2aU6w8e}z zZ($G4$i!{;F~#}cKI4F#hN6-8R|5WvY4Jj=>3Nb?+vFRBR}l1xOLj3nCK9?4jCSp@ zCnnM@v3E9E-K)qj=fVkQACy=9=~4a%wz%`%$7i_)Z1Fc}$!a}bx>zJcZ4>0^O7f$N z@c#fDR@=wOimXv#E7Tns$gQ>i0KIRZ3Z!(UXHU0mNZFJbYK@Q4*YU-B%!CCPi8tSUQjZnDCVx1dDNk>@{)Ba zRXx~@S}-RcCnSw2?!)RnFfh?UaRtNRuZtXxl624(L*>(<2SZ8tQ-`KD+$qE>nC-3_ z$pu?_b&`|f%Q1$vs6G7vWuYBI|lp;ml*C zf@3P6Eh-(DWd{ZsY%3*oJuu=a&Vz;;<1wYRv=vd}J#ia`pcf-2s1*5$D=1|FEvFzb z=&B-1Hk~yQm8nF~RG!Q{GAtdtWySvh?jgG9?9$qgU$Y6#L7i9<1%qnbUTGcQDug0BwoXnH$Nu#-aC3JUw%+F2cGRVCY|c7=B9!{S8em({lX{WH55yVG^#VeI&4r~a@+D-f0y=fX z4$BdsR`=_!WZjArK@_5kAfILvqWr`RwY{$|QGR1_QUwrF$`Sw>b_^#$vy;?f?i=ms ztx{c_w>uJ`O*;E9RhvzsYig`%vAJ%c%}bWGj^eX}6|@xXASpLFQrs#*Qr54_QgG!2 zh}E==#J1h$xDwoD)kFfK2pRiu*Wn}w+#U|mkWldGc zBHm;o=#e@@(}?? zvI(g!5Hswjb~QK{He(e?l-XIACisyoYlGq6Dd{C(k*HR=eYm%|j|ymt)hT(hZ?HPU z&@@MIm6~Y>u0puUX0KIC%xLI5_N3dbrdcnzfXj-@ElT`HZYZ}OQqdMTZB(CTZyS6o zDM*CunHqi&l@yXkbq+H2cIM~AxxIO)l`D6fHO<#AcUBFh=}Mz<&@w3pxO?-*qwUkZ z6-c_7jF$%ML75)WvgJI4DRM&Ab5$sw@t2vcMWsnB~Ufgmc@W!aLhBb%9>OZWz ze`VSf(%QH#ORoGyN}xG+VxIO!W&Z$)P_VOW8XO*?+c)bav`cOS0xb2^Fzr0hay>w< z9Syotda*BFNmU6?-*-H??H2E?rAbnCt3rZO4nS86a|*;)3XQjuH0sL{w|P!=nhlx^ zJ`dmJmw;$Px!vwBpizNwhzS-Z8}-+tdOZcV3fLOw;Z=a$@k zBj5-~0GxV%WnRU2{W!*wU$n?~RBz5zu7gX7_)(5ZY+P=ei7+B85YiHLI#e>u9?VU> z_ZP}dB#P~;XqD(zq<6`?0!JI}hpaB#lW}EZTt`z%lysq|XvO}|xkoP7I#eRuu;pr0 zY-EPOKvG3iJ27JmWn3iCP&%OX5}GToUuFmY^iJ@UPMhPbPh<5S0?kdOz+8+{>peQL`+FVlgpP;f&V${KyPJmv@gmnBwli*jYb?IY`ebun ziz0o^3=WQYq)P| zV6L%RW$?p_bk$mu6|{mq_^Y|yGVn#p?S;ZmLS*B=q>P8$iu6t+v~8x*q@sMtz_^_9 zsCzz`&t!K+_W1tOM~{JxWm1T|+Z*Yjvo?|=ySld(n$*K27nzF@rk6qewP^zxoy&TZ zzA35~*x4|RI##Rq7RPIFucsY_QAWiU(|it?0HoASFsP<2E*aapp<`>xa!GfEo7IQ zx5o2DGWgV$EAhqtH*aHEYmy0l#&0Kf%e}43vVVW_9k} zU6cVHT9@Uve#YtiaGaCBvU>aO;y)I@O^)+lt&(3W%lw zc`*(7NgpNGo=^!%S?vH<0ZTeDEUfMAhfxAknpCFzpnf8vLB&>1>lIx?ZEd=PE*w^q zszj|6>(FB{lfFTCmliM|_Mx?R-#agCw58ndapMGMRV^W3cTh&LYb$qmnq9fO3N_5r91xKw;f5#RbB^RPAZe+?{A!R?W;u^j9Bb5#6It9udT2xSi zpej^A$N}0U+JnN6Z-N+q{Fdu0Cp1P^6`WTOK44L-368pun7EjZgkB_?bQ0GFb_x)~kpA z0BwI2q3I9mrwzT&=wFvyN_DQ(i%sPq_eGq`x+mT}hRGgxzOpd<6fEA8cLR*uiQED1+>PgjH`d0_V&<47kWcuAC4m9-c@iaf? zdy-Nq*le#W`Y=`}7FTay2iw?i37H9Pkf% z-N(1MVOFLcHz7Xc%;`-b$CUC=hN7q?O950PT`S*=2XTE_+cw)xIaXf7)B8{8rt!R8 zPTU%c^F({MZ9h-HOdF#2ytY3bz0X|1aGfG_zEs^|w_0XP>ncAe%gORtZ8-oNYNS%C zCm%KUC)Rz>aE~rBqWnLv^gh?@fBLD5hXnH^m%WmFuTlQ3eL}~69@B`ux3UdT#I{na zHLWP7K+0Vf@TOF35$qVrWA;}2y~%YYDtXq6`Ths4T<)I9?HJOLoyG2dZ@f*g`fP&p z{{TP8+rCcZxVx!u+HIt{)POJq^JoQu(GazeKMFuV`*ySqj z-6j34>HB|f9lZYfya1^{R=*+_A91u4mo6NWxn;Fxvog{~r714TDO0o^jilED=I{7e zadMoNgR!=pi}U4#YE*u3`h|KK^tBQ8_}Uu%UGiizMsHUz{ezF8$!zAaU-1_dgU}>s zJ)~m|k=~hEv+3f+)6CZ6_Jxd8r;{o_FCW0*SF1m(7pZSmQysVO4c2CDh=iwZ>`Plp zv+V0zb)XULQPxoDO$8iytzESkhZnMXv+89@w){Tc(Q-SUHY3T88hd_$ZlSLeP<*A%%Moy)pJv$ux`aW=T`0#Wj?8bny=@U+ul3XYwfxOJQ-E+o>uri7csUYt3H zo$j&YZOa?+ZSSZDE^juWRFzVQM^l66cGZQbExb#YWOb{HeGhOy16_8tuaVoia+f!5 z^#-<24Q>JYOOFj z!iBd<)>8I8e}~$u9dLcUzEfVUBjmo{)7S$nh^jgA zx4d^6QVbp{v(8&EZR!H8i=_Cqs*}}BF<#{=n6|Nv)6ygoz0JgzwqFu*S2kGRy&_ya zqr=-LQ&P7i&oa}rtZlYDZL)^wBtAP3jc${XBL&T>bu9tlf69p z9pz4cyfb^;t-ofn+?`yRljNyJOQ@|@>#KbDYfp)0xStmSZB~2*CZCD>D|7g zcn{GDJ~Tcn0~!9)hRg>*@Vr`Q1si>j~iy4 z>GRyt6uW0}zcl)>-kVq6uXalecVuE(4`R2`h zWf;en2%5LAsQx_kueL7SVd=`Lyxy4+x5p$&ZNJ~|6$C9Uyn+4}J$@7-gpRmp$y~{u zBB>LeWGqIq_=pqJ*QNe%^^d#SEHeF@W-#a%UClSl5m{|Us?kwb7JC5e+Ht(vwiw!n z1JC!4<##2kJBb44>9gtIvZZZaGltIS=2|ZHVZcvQ-nMW1#`AEQyl(Qh4aE+=yH&ZzfQYm>;i2-|QKCmv-|odN-%`-fVOxc+ zBnRn>(sw?3{{Y_mw&Az#F?L@VY(#wDIeL`bWX0MWBG!id^{Mv?noIJ$u~uQUtwx}% zwt^IEHyryI&{C7v-$(6!#Jv3UUNp70bDBytb$k->_I0?r&AK>S7CoTb zCB~V|CBHNyB1HiS84P^K^$+UHoZXCleA)FkYW7u}D^DW-0KZewCdJ}gXBI?yBle}u z+!mJGMf&FRL92W^q%5%BnpH^zS06X-61zFMO0Al|)}fM;QcZR4Ge!?E^c87hylIMyjjQ1i*~qFlNiI;of+ z3J13yzq0MLwbs4qwzx01+xQ!{Y*_nJIaxz zVXgPLA*0N6CB)G-*d%-@8S7Pg0;Oe*=WOkNHU8%3rqpI5u zFWxtWi5LF1Q~oaF_hP9#&v-UqxXTHV5vKtE00R|AC6Kz&Yab1B?Z><0VaD7PBB+lR z+%ue{B-TRh9h)|1af(ur-db};NoPqbMl{nDt_^J`FiRZhXW1Of&4BR3i zUMJ9=;#4~^bWk=+e1Chk8SOOW*1ShLs8a<9T9s|hlew*{jdJ;SBe;X->Y+tFsvu?U z#8)7gTqodB4o>5~4%>HkR}-1-wqdBPcGi@si)_}ENNk>%1+8=>j3}xnY@S`&yHev4 z9k+ao>XNN3OVH$WliBfsgmGwCw^ObDoxM#vZ8r5H=4#rKQbPPmr*Q|SAe0n|`I+cj z(^sy4n1>3_xh@Hv1=NHz^3G@!qLQlht|7gN;yMzN;3j&M`ek}-^<64QC-FSb_<6rB z8l-L;Ewr%IE6V9oP}7Of)P;XA$ey?bTcZnE9LkLIx6NFkBh!8V0Ce!b8~R+hYz^aS zbxC87;=8KpQW2UgE^Q?)Ojb@}#7r z%mSL7RRE%L9ec21(G*l|lH6^rCf{VdIZxJQM%gHg5+b(qB%+G#v&t)yc{(uk2MI$000T5DYG4AP|RCTfOqLogiqw( zP#Z}`j$ZiY@|K5rZmBf?0Gu)L_x+c&)5p8zKjeKu^je>pA7KFcFfQ5W?>l1A`myAp z3rAegvf1n1Onc8~{{S*x^QGl!Q)pREwS>@nE01lD!hHLC;uB^; zi25G$6Qn5wsUaZLkAF-g)ndpKq|wWr^EF&;&RcVNvP&u2_xnxl4conkt!g-mZl@v^1^~vSE_%KeY%%Xg zCdV&|C?72F4*24o^SU-=-M=<|+?f1lNd*d88(9c&1yd!ahwa01@olNPa56a?bd<_g{L#(?8h7u3SAdf}@%BC&tZ~&Wi2DNWVklIb2>d3sf+9oOpZ`8sauE?{BZvO(PI^>K#z2*pt~D{V6$I3L{Q>S!d(RZ7zOQc z^Ab6(LFMt?g5D~q?LM)UH5H5x@^<4bMmw$WHAdFk&M34kP3i+Q=%?d`+=a0@q&v2* z+_YI`g~Z#fD_(B6r3{B$fyQreitrz9mg#%XgSjHL91*EyG)NW9QvhA= z`*stkWN3txMc^TD9Uz3gmsKwVy}q`%FIDWAs+* zidmq_r>}N1{{Z^^%Zp6{Ui6f`g4-UXqN%{+mUKY!$Al!MB-7h~FLqVgssu-WN%0m3 zs;Po9oO;yMm`s|gL3^TlAG;N=>6_xwWpBE);w(?z`_q%Q+s^FxP@+RyYSlgEp$I}q zt!l61hvIC{3gTQw+}x#pEawL_?T#_F!I?9Db06*-t}mD(?fY6pkQ^d`vbqpGz1a31 z*}I+0l7%2*_os~&SxT~H0JLpQ&$+G+F3)Ru*p8@{pHdVPqP_U_44ulwm8;atS;v&g zAsl%A+)AZhl%JVSX+n)sKF<7C$9Z?%A`$Y699Z6$bAugP60%l;a;T;(E#p-K%3zQ; zVqz;(BCb=k^v}505!}G4IydRyN;_L}ZfoMgB1mzR3IbE%NY0&@$8MN5?Ygqp3o24{ z@GUOexBSEwn!8T(-kXVJfpAkv?m|$X;4rBYM;anax0}S1soRUrj@r1d>%)>B?L){Q-}p0qW>D~twHl%9iX>77mv`E(TQrh(rn zF_!D!-k;B0$v8?3j5W}22hDXMRK(GUQh*YlB%Mb*NR&%APh}l-7MXdBs zzY$!zqK3|C76Qq%>>GYKdScS_q@)*hQzAR7ihI+Q$Dk~mV?%dtxQ+$HfPtr=SR*=n zuvLs|YII=P7P|7CP$5LAU4T~y#UxIZ1zUd6-8Xap0Ebv)$p`)CmQtSXssJBA;J={6 zcQP%HXNc^7WVo&i?wD2#2# zyq&xyPcGu?ixvGqT7CY|KsOPDcU0P6Ic z3vUU~u2GcR%M1p^Q%?T?vjpMU<=}}F+u}g)sU_7m2ByI}bEa6hP;?cca?5gJ+WH*w zwX5=ji1H&;2;(Z$m6N>?sHdhWEEaRaL^9J3pTUku`OMJSQ50VI@WGK`T{4ufZ%&FXs5kb1bFnkKV zGjcY!JeY|~EsqH#c!>Kku7=#{R%a!XCBTv_8;)E%O7%V)X<&j-Kvik)#Wq$8=FmSM zD?zCw;|U?IH=^tcMQQcKv?3K|?{;*(w0!LO`!t;w-sB%paIm zJS8TJy|uXN$zhV2^6$tJq&AXbJLB~l9QIpP1e&U(8!M4 z(!5D1N^4sCv7E-t$%Wd>foppn*Gh<{TkX4s%e=D+LjklY-vY&T6xM)&*B-aF+J;4% zv}9)~Te(CH%C7E>`><{CW45>0g5p$_g%YZ1pa2AmscyjvU4 zw^Ior1H`rIqP@ohcT}TsRVssQv67W)NIZ|n(eG9}h1rESbiXnHg6Uph)MZmn{0H0F z(~qU;5d8dP{pEYxTQSI2D6DQa)Z(NN9zt7Igd~s&sIPuCd)DDgT-M&8co?x$q>*AI z`gYrww&SkDC&`MS6zrANAb0f@t}>Y2%YG`7)tC2nJ5N=i3UZep*>(_R7W>EUFZ>H& zrj*LMfySd`-0oy+B}7nW;Ktk0kfyY{Nh{M#YNHwy&e$V~20VqKL@AUlNTH!5B$50v zI<-+8=&F8)A4Z?2C!m|Zqlg!+@5J^U-?;a^u@dgwiG0xQ)h^18HF*z9{^>26goM(A z4anNv6#1VzG!}9>V@g*;oOX{vjaoR@>Q{W+yXwU+WnCfNw*LU7<(I^*FWX#ll7f-& zj)@8ez8`Kq8t;DRU&L7QdL>HvnWx*fA#vPXS>&s0U1YkM{vHCYYhQW~@5V@wzxoRumAPsuTcy_^Z7@s9Cu# z2zqS|nZEbrgfX%9t;=-Cc!advNOEKXYf1$)f}h6~_cJWRi$;eBw6ia8i!PrXeKUPx zaxa$n;4>Ui49F#WqiLT(!g7m<$whyM| z&eYmuZ%y^I`1OPesSL`IaBuNtSiT{ws2SaX0N>NIsjOzSA=| zR^sK0i&P>ViW9M;5(nl~G!aAXa{|2kQ*?rqy~vtxGL@Gs7qze4izT(aKWxit(!H7K zVIE?0YU#Npu%CyaA?GBo{*EWc#>~3?7T?}hUfgkEt;_!abX9+SNzTp3Jih8fDwVc4 z^4XPY-XSP=zzcaShdFbYi;Xxx+bp>P)4t8eCq$+pRB!I(h0*XZM!lca6rEvq9L^&dX|J9T5}n>&!0Z@`zru_DG} zdEz!Eayl~s%2%d%J~(3~eIczckrb+*G>UW~SduuP)60rt6s`R7#!^Wh{{ZX3)^O-o z-pfzKZ#%p05>^|E9FpUo18s#Y*ZrE{*x|(xOE6rw?Ve2BAQ-kM+F?XeL;nDvkUQ$U z@b!xX1uVHS?Qh^M%P~lIaBg_;n^5sE$WyE9(yG!hsYZlyyC%sB*TUNp*px1R%wHWr zH7>eP*&ph+OhZVHa+v&s18i9kQEIY9ga<_>w3TI-Q&WYKpy;_VNz2N!0nCW+?!!5W zx+q4KbSmh-#~o2srV?D^)UWKrTM6i}qq9_NMbr#OE2#>s$%VtOI--`+T&_x>CYTED zY*!H5I5K_!!re9FS5g>9dejHtF-w~YTN-FO{1uOQPQRMe*X+qk@ z$FBnqPqAB2U2ryppm?2mU;*v%5sdx*WUD?9a4f$lv*iAJAFgF9C~yzt^AM8_NO;IT32M*R;%UYl^+N(ZJuh9 z4|=F6pJpe=z_~S4v;fQAEZEXdUoJ23IY-IAUe>OTPqlh(;5Q|-*jiJ(x1`Y#utKz< z#%=b$Ico~|f?aX5Oo#^tTHDlKuD@@$3Y9b8=P!k6?ehD1(BucHFJB(EdEVW7jlMb7 zeUomgH(#8e0Bbxu&TXw_3a#*e$5qp z{Qm%H?4OQO+W5PTpGHC31(`{q%7)z5e{L}sWk%0X$M_KFVR~G3RILU>5p^n06+^`@ z)kTLbKD+td=8urF^`2{P^dUmn!3Xbe8V!Ox_~}0u*W#D+Gds2A^BVXh{{VmTJu!OW zTCV?avkPRJ^_DvawdziNDhpLgso9;Ad8%r6M%;Qh&15 z!PJaa++Ue5d;R5(UC&cx;cSrdXnqr(Sd*p$8^uNSx9IiXS$2*j+u!HU@*SU#uTrj; z*@%Vn(&V)Dy>P=!C2B$tpjT3*)CzRRWbatm*4w!xs!fTvPGXr z%aYwxUzZ=Z;(cAPf7kBZ*kRVplB|6A5kp)b&YZJ$M&;$L(!IQws-!t_eOEiSm1jny zG_czs`&2*G!Mg|2JnXimd9rMDcLw({gFzUx-9V1DBzN`Vj(Xd+wV=#0o6 z0ZU-ht#C%)^#0uC)X9^Zt!T({`^IrxmLQ^;NLse<9z?l(-NA2Nr7QmcdLAV$p*>EK zKnK_{u%1>nBq<#|#X_-vCUuo{B*r;!J&}Qhd^2*E4_9z;+#8Jcka^9@><; zBR;mvMZx)7X;o8;B+)4V*A$su!rSDt8v9GkEsvFRTz_$TzH;ZcIQlmBxZGIu3F(?+ zZYI;pTawVZT&cX62@2D-cC3U4n^MzV5@n-GNCY&rkV9d&)$?A~RpH@O^E_E*})#TKqo`x3;B9v~h;umkEu_ z%D1z{+eYwQmmb!%e9q{-2Ub?KM}(zoY`CRV$df@_$ThAkqqo|#!RiAn$=!8)2d$%a zOH7qa`|_{CmQD z0TpsRLtY!?u4)NUwR&dTbIX?dOS*wVw@vaOEws86mBEiyoWUv;q!}5}w-%PK%kB2} zh2vt*?zE(S+&>l5>j=8Qn$awJAr1&wdu5VsVlGfPTmVh0ba+Xqr z=|I##IVPjGyWfB%7{iOyviY?q>EJW@$JLR$Mo$jVG|J{{T@k6=U0Jq=Rjgy$?x8#5HR*N=Wa| z9+S51F|!R(=9`}NcJ#FIFDEeipxc~RveUG9Q7|?x#;_nnyKlz=S%n<~z)=wiDO1j# z@mxojmZIqr4177ySsrnHM*6CgD{)zl2T}mbb!qLV?)Q6J^5EYIZ`7}= zE8nQAe$w=l>0gfj0N_WX{fhdN#~z-nzog~m`sa4sZnjCAF~e5~aY{;5bttKHQ1e{f z-CDNqw5J5UzHjbYarKRZx6_?1u4|8szk>2#xAxZ9`cm{|3u-jQ=KO}H?hP96 zSBF3`?dhg%v`EcegpQ{lDtG??)N8qI+qCHu>AuqK7-K<7TC=h|F0MPW?>lzL*|%Bl zZkO5frQB`s{%K4l4V7yKiqJBpe(ZTZF5Pnc)2pd{+}TmFirvm$UncZrq~k6vpRw&X z-d6Psx#YmwZBKgVwe~#6RFyo0Ip!i&z%10ML;{w6{_JQh;KL5xZo0|vM~wFmbG_ps zVeODqN|KkC^hXo^p+8VdpDJF4(A$c27B*JmcGlQ!M@e`cD3It+Z4N`T9--PDnR_Gs z-cP37bx1xI@%C6N82-D`_@&}dt*s`17}=G0Om{aIE%F1OC%53V%B0PcGF1?mkefg% zB$MJJui1^hCfxTc&R0D+w*LTaJc^yicejIToNmaL`4(Ee2-qgs+qlNu{=T_B2h468 z;#S(Fl6o@A-IdA>{{RSWSuJ{k$Jv4U^YaneNRhUS`-^2PF4Z|}ZMtZV zr5X?^mS+-MxYN`pmM1|tOVtFZu0@SLTA@v3f#s;_3N@!`!M5@f(SbO>1Gc`MePmp1 zE4L*#RV{^-`K=X_Dbk?Vvk_ddo2b~!QvU#R2EyV$T{{s*{9cEL__gc=2xL++2Ui7RUDf>e|zg#pc|k9Y`o}X>ql5 zTWcAiVT^FYIFnB zKF7A)-$m#4C*;UW$y6kzPPUYRQ>{ZRTkY9#nKbsvr&Fcztq)3!)M;&+wmKYNN>xWY zR4Fg5EwCWi_N=-9)9aYhjeC5JWB6b@{0Xs$f9X4RO|RD7mdzx9#`4>{iq^C%;Za;v zz6aZ*A0__)+FKG^8o0Q|aeut}JLt9av)X(q`?VNc6A8_~&pgq!mX9e`%e>}F4^)O{ zxIcl%p?x;U=ftsd{ZNnYCHkLd^JH{#TxVlLO509U%;ZnAraqI!pE5pQ%2bmP6FN%J zL2L|&r8~tiPf)FJt2x{1Uu~&TS-mB_eoL;gEP>GrtpWB7dFOHAbEc0&?R>tS$iE_B z^=}SOFM1I4FX~kV$+wMx9_z+Dk3BU~+kV*Z%tBMD{?b@FYy85Uar0knTv%Jp{9Pj% zK4tvB2H)I$3A)ecFuB)&lj&2IWNk?s4|TBMclId_H*rAx(Uh+(Xgz-^u06|S%jsSB zo-Mv&xy7T=#4Ch*qzIQNP6uL7X%9wlW(pimq5N^__Ae(Io>x0^Pf`u#P5|QWcAIRr z@2K+)XYJvCW07|*+F?Mg zQr$#LnmTG3CA1n~OYU(*(6w$-Ia@b{ID?KiR87&deNsB@%F7`y!fD;FHMNHtSWxk_ z=pCI#2Fa7V4Lk#|60vvK%bn|P(;J?+4~>0Z?1m6-cGeo(+|<-`pD{9;q9V;&dlIr~ zsQ2fCF(DiFD!8J3?#!apjy-3)I>`wB9FEP zLY<8o&f*SuQQVB&scEN-oN&M&n9iT{u(TBu2OILL_OfKyFR@X$HZ|k) z@0J(5K%rR`wO@r}s3W%-J^QyMH`cKEZX?q>W4qv&Lnqa3>8PQ<_^qonDEsqa%T@mX zVBF96X-UpRC7k@3DNn*jKzbZ@!QAn`E{;OwxV-#M?q1Az(D{sK&29O32==|zZn@iA z+xyA~&)eGn0JzRcq}a#%4j!O?Dze3fUf*`p>RJ2>JYCY%*G~GX^AfMy2IU4Lx2?A! zJDYDeo3hy-uN-xw%?!mm##pl3yMhvZJD!;pTaRx_F?TI`qDR78O8_Y&#nAr%=*Ed# zjVfi9Oq8igA1Mk@)WX3eR+aYqaV-MW+d@;1Jc2@oojDOqCWj^_KOuFfJvtl}ZWLrP zokVFb5nW?dMtHJmG&16Ks}mO;wfEE;&_a9sG$koW8F{x0DF6w?Ro<71MtB2cT9)<)gAh3 z3onswZ={vTb;ajJE#<4P+kjo`kHqJSgr~RN?IkOZAf-d?{y3MKL+s;6#7~vTd#%C} zE{be)2}lF_F&vX&5W|2;8`pE1gLxUz%iBeUiwveR~N%jm2b=kJ6^dXg!^KkzF9$qa>BJ;@mt#IMEHAk~0 z?M`a`*LE-)#_`Ws!31L3abB(kiBC0cjkeODTUX`=zzr=zrAOX7ae=$r_p&8dz%F}? zVWdXA%hUwWr_~-ul!SsyNj@MBb-|WQosjx@$j|l78--E1dHlf7>Pb%ve(Dz36-ZR^WV=r9fxK4%}G)f0T zr7EBRJ8OyZu(9H);zJv^q{$-DI@z=31nxb`_VC;ZrFOJ8iG<3BUp>NTXnO_$coMp} zQY7)plyt77p5F1?d(pP-GPe@!mbFkPZ$Vnvj)WeXiU=ypzZ7{m*0G&q6xm~9S1xGe z=p2p4doDYX)pL%;$<`H{YC82A*17lNKb^Z}Nm4o@=EmP)>pfWRyU2a3cggMBQZ4K< zk{L@$LIBc|DorpJ1?jizbqmX}6(2$==OgYVFdk~8!hD9-h7|Htd~2-|1_sQz%7<8PMV#6e z>2QM#Y5^)WC_x?Frx;DvN_1Sw%pvewN~3ig`fggsG5D)#ynnTU#!rq-P?41 ztYpkJU z(bVVweWc^swmriR+!R!d-P;s(WE(eNZYXLd=G#`%Pe)#CAGi~ZbT3AH0i~4~gvQu{ z(tpM77|nk&Q&peC4-#2&@QXG5w`#Fa?divqKar|`94Z*E-*S zROrT~l>Cq&yzPd73~kRbL$$|A!<5&NC(B-ZWM#eA3OcIB+=?mil?`9V5%Uqfx^Ik_ zqR${tLH_^=Y=s-z7)c;uWj#jzolh1x8s58tOl2uE5gkYs(Ap4_Pq1OHR1Yg0uh=r~ zyQcp7w65my_bX5E!#PlWoOm^;)_&nJUht2`UgYED{!I5|jT%5^S0;TfcpPrnC2P(BgR04$SUgmlB| zEdvQ#&krnBzdLBA@Fh0f=4HxQW$>`nK@i;e7%7 zyr`Ba>CH-z`(Z$Pl5qvhza(FoHLVT} zYHbgCgT2ZUhLdTvLbe3-OL5!(0ONtfA&*t58?-j(@BQI$T8p=rkdt52ZC+Y)_9B>; zIB*=Tp{Xi#$&1%2OOL4aD_WKOP6$@g;7J&updfbPlpj-IMRFC#HZp|Y#qu1qGd5Zn z3Vq@FE)kfedSgGizHI^K^&S@+&D!eWh#GNjkV=UfQ%ZIm3y)$E8BvEO6~(7imsT2~ z%_M+3uyd4J5uofB{#hvdGC**Ksl*0;PtCaYNaaa8#k~$!eN^-B}u2{hC`(l*qE(V7K zGEt2Xfy!5na@d-l96YAxtSkLxltC6k9Zavtg~_4a@8YJ!i*GG z;56z+F?-K&Nye7+F7Wosy-RM0+iz*XjLzS%rm@HACq|;9N_dZ4W1$)3PO@yfnv5f& z)Q0y>`*~jElABk^6xTsbK?9~()MD+pu!Ua|rg;~fsw`{^runhB?3bduJ->jLQ<6ax zBq-vWaNDfOXy{wuOz}I2}@&2NCt%F1_8BYM^KlL$sLV%l zMD+l*+!U%a8HkFgC<1Mv#<5a-qL`&3MAH;#^-%V+N{=+tM(e*yXmgy ze~O|h#}@qtw<=6`Ia}4X(An&$gD%@?v)r$8#(w~R&O=(gzFsgpr_{SQ6=B5R?Vm}m z!YinB-->>MK8oCGGuxAX@@F*5Mv~-fV`9q$tNSM2!*1#S09j`k`vy0tO(4^*w8E}jFhFTb*YwFZ2%6M!a>BA_BQpp+aSsA+q}HE z=`U+Ur0*SY-G#I_ZR1qjoNRbYt6RjL^QfFU^_y1y3vNYrHSGL}dhe)(TDIEC>tW&O ztvNy)kf;r>KI*MF0cK{+{#~Yn-rbE2Zsb&S!nAUWiU_uC*fw?u15u8{RPOjjEn~EA zm$*eQxC-WG$!YqfDSMk7(Kat5%1V`T-l(+d_?V2!Tr~HPF=vO_JCyagIux0m&$dVW zR;%w1tBGt<)2nfAD~<6~@w8;AhROFZQ;WQBr*`Y>lang1cg@)U0E#h!79s3zFSF=W zZK(xMH}J`Mq(0}`r*#}?aQiC`+xQVlk=;4hINX1@at+O9fQ=E*%w&N?rL-j=Q!mOf zq_MMEw5b@RXD{YloAoxtIa}%?PxrqF$o0hW+t-m2ydX*BCQuLS0o-eeRvQ}bjY-^! z(9h;G{J5QA)o>WHJ)>H8BV^-8-0ifV&ctMupLV5?EB0ZIQ95_3NS3}9<4Z*q zCNsCUN5o%v=&$;zpu{~^xDXH6V!>l*GibBMf|PtEwvY$|+e+ciO^TvvBL3mbAF{Yj zNmZdC+ry+#AAV;XbRfcR+}&5^$aZ0lNHX221eY;NB$G@Jb0JW4F=W^hUv)~-ny8+X z2LasN;}X)=9GG-NnB@thNu@%SitC%zwc;HewT2M|v;bdxtwX9&KrvU7+!S;y^D7Yh>np237l&wr<~TX6L!0Lk;{*hfr6L{#NR7M|UqG z9^Q-in9tO0TUsqj^SC)9bYIq8yI&+suT&|+r$1sy7`3ss z+P1Vncek68;h`SNxT(r&aFR*RxHZKU)4MtGVmq?+_SBBt4&zaVzH+OkhN`ArxFbYz z8#}Xo>~24_$iH$9__XYcG*s$(<8`=2hT2j(ljkixJ86Yu+fg~6%KrdaPelBDC>5I;C7w zN3mJ$&)?a0e5X*#DAnck0G&pvIdl~`$M+9%;oweA?2>hVwB2kyiMj5#lyR-OC*+A6 zeR|K@c5ga29$1~-Qd%S%%t4nG0I4@66?br# zD)K9A?n3o1&jI+BsghW*x@h`s$o-zs+H28rN`jULXbV@n9)D&#-uH8antzAe@jZ7U zeUI8UXq;tndz&7cFVaMp?W$a8#YZw7l<(@VKGX!)9;@5GL}cjl zU);ETzXQWN$Nt>hl6J6QqaO$2S>3z0cHYm2@;h3|7jH%)jv^yTLyl9mZko8G-b%6U z`#)r0;jhbrN6Nj)^*?spV3lSGPz)D0-zODm{SU31(Qd=~}-e zEx2UgjZ&n2r!l=p{cX3Auv|7r3qghH*J9~XTiP29NM>8?5ke>5A3|g}qm@f~J@}-` z%E-Ry9U{w|d^~##ns1r=Pdq%)>rZ&^iPr&dxfDy1rHS`Mxa65nDMvEor_`k_K=mY( zUfdgj+i;JrYfyZ><;hYSdy1cHv2N}E0C7}?n09Ng8pyajwJ)!F-9Ls5%G`0V<3{!J zBE{P;1jf%Z)t#}Ey zjz^OE4Zm(fMXnI=w8KJ*S7u~(;H*yHe-*~t0LtE<2xyPCKN#{)D_xOl+Z%pW>f<2M zEi{5cf3&r#iAeU7jRLc3%xd&SL~mZZBdxq6^xE3{O4^O->Kkw~3OrPYAF(CWwHdM2 zk36CZkFZcp)pgMw3*#kjt?Oa9v*{y_Ejwd}_DVGGUsOA>f936i zW=aT3gEAa!F5>z{NJMa;RFah~2Li)nxhV7GOHU%+dzG7Bn``a-KH3#`dwQtgE(N*0 zf8ux5>gUvSXB1xbRlTCV-rlgv2~%qS04Qy#1hUc*sX?hvYFSMxrv@!C)KcWJ6;D66 zAKX)A<#S29jjv^Yf7|Xw5WPRney6u3-oi}~W~k5$twVS zG2yqQ5lSZqSiwqV&QLrYM@N zc9;@1A|f+1b=^)8*91G#o{`ZEjk;>}engqII0t&}t7ClC>I-?R)87hSok3@l_cIU7 z+}o`|MM`6C*%U;{&J-nDUs_ViYLzFdbrZQicQBGl6E^C>q!`4hN=!lJ`eFl+DbPH&bKiY;+= zu3uEOf3y1ixQ~xc{*QMzC|w~cWHq$_ug;X(lC`{0qpfOL{$tdY8upxaCeb^R=6WJ* z&DIv<%Dy7`Z(mS8o_!eFUDe%* zKpAF)ASlokQ^GJk#Z4lr*!%bS{wJYK?|a9ey>sr^7CSYm{jJ8(-3hVnCKOQ-4Qe1R zO4Fn*bm~r6u-bcmZ)UZV-^;mdCp!`?@!)9o~(Ux?QO5~HrTgSZj?b$YTvh_xKSBzP=dGDLE!EK z6LNhAgkD4M6WXFOKMT5~em z5v{(!=9T5nICp?WX6W3OqP^V* zHfGhOzl*qr$dyOiJ7U-q#}8NG5#`+nX(%3ks{$&ZTVbII!Ps8%BQk=-N`LQ%Z>Kr`iqV& z)hL2cr5?s20`~ybf>!oSyQ`ukt=hra$6|dd!olF^(8hJZmQK)$XlRlU2`V2t$O2UiMG#_ z^X%Nn^J+Y;cV;yp#@fuLzYeEAsAAIA*FQ43Wx*09w&^G&DI0bpWj_r{jtE<9hkl_S zA=3+V?l`ntHn2bY9wX(aVxzubJI?cN3FLBoA zV{Uw{>Q2~aalF@7p8qth)w z04wjqaN%|@Lq&HYvC$TbjQMBOF8AElsPp*A7A?HDK)K&$EHH+dp(Ld_l^ zqbqJgTtu&3GFuxrX`1HTxgk0ADvnc8i@c@js11clQ0GUZ{q~MM^_{>QjL0oTjku#m zoZ&R97Z%qALZ_vC(T|_LzU?nBwlX5PsMUY^tv}qE?O#hXdV8!Z z;kVr;JZrV-a#(La6ITY&B_@NdbsS~>h3=m*+xXM+jXws@P5PsZo?M@>zSl1#G*T#* z(tI?gbse zBkDD&ZTqc^#iVOVQ%yau+M8bFeKFa&P`ixc`0)Fm@hA@r-rlj!onw?#)k8|(Jv@Dz4+8#Mcr;cSU*>tc+4C&c*64g_~SEvA)d-y5_&Sg@Q-=JdPAcs1c`YcmXz!5!2K}{i6AS+H23045%Zv=G!r@>x-$fl279bR!v!`?am*N18fK zaAxzhd&1@%xhLwj_@+u5?~kmPt0SD1#V;F<({nrBoRFzjch#9gsTr2*@UOc7_Wu2P z-OrZE(o2K$G#%60R&QGQtUX}(uOc9_?#rg$eGfvIxQ?SDQgTr4S79E*jXqx6YbAS6 zJ;L3u<2oKJPfSLlBMc+63ZO$EZnQl; zm?U6T#E$;}b{Ql}q-6;erXq1-Ikp&W#UV*bGg0lP8#I!uB9ikj#}93fJ8#9E38X-f z@^hY~)phn%Wn5wRe6;08R@-CV`L}MNpd7iEl~PNtIi%CrF;N-9I?#^fnKp} z)TF6tHLkkX7Z&82(F3b>Vl+^SY5NEUyw&PYc>M%U`;3ZtF~m%SrO~Rh?;mNh7jEkAWX&-+|?m zIp~+(ST3AZ7?pe6w>i)b*x8nbHeTOB0K$cohsR33{{Y0IdvP+vgmspz3hVHX0hevr z>$a6UGH1(IHxd*=jYf4N65hEQQ6O%Ut!hY6_gp(uDUQJkEed&wsVBQLicF2GsOlSQ zFO?3PUiXtMfZD@j`PO=Su~Tg4FQ*aS5VkhFjksUV+yMk*Dn z^PZ)IWo3KF>V59@G1RvaVJ)cCwvj*w*fC_Wp&06b=W?>D7A=zQ@d$D2nq#KEBn>5B zWpM@D5w8HeY@xv?b)c{cWhz^V>aqfVh9FqF=o`=NEAJ*;rre;(irvk~ig_vlX4Cb$G zF~V>(Jq|ezMl}J`9Cs=9p@ch>*)EitjB(C24!3e~x@i<3Qn*SGJXF9Pl%irB{?N<0*wja5Cv5y?=3cYKYdTeN^$ z1e4iJD^z55vNhC3S{B|*1b@%E#6@tEo^<@u8K=bBk!gbf;*93Ba$B-?&(D;O`UPlto+xO+bO61 z=t09=(irc=T4#@ZjISHA*J%m(+A<5S53vIfyM%7{l0G)BJA#lV?wL0=r>k)zP#^Z3 z!t)7PzXMO+`1TQ`9j5)!L!#zNDIfD&t{=-$+>?A*J3O5_1yN<&_S;i`kftzoX&>t^ z`mj8$SHx_wYpY=?+v|7S8&{vWBsSXqCYfK7ntPk37B*rrS&CyB+0q<$_1|&onw2eT zRZsg>#H!sm1gmO;&eL`B87;EsmZbr!F1ca}CfQwj)}c$a##8Cg)5MO7B7puFSXxB( ztuGM)t9#FU45`H)zzIKw2|*m`3bA&z>O~ZYD@%QfRS=*0RKm73-a@Opan{fiGU*Zs zO=^`ol`HN=aTAjxQxgro#>$V(UkivI7nIV8`zmnsJxX2(B)`}jP%V7KgpcMZMSkky zHcjdbH+HqMP^Rpytw`yuYc;}3Bu>=4$wJ+{9)$s!2Hx8Z*YqPcp8ndLQ{J)7A_lb* zr0+YW##XkSC{f7`rz(Bend3%+DA5Gwu1H6pGD%a8XG+xlxJW9IIaDPc=^c*}l5+gV zui1%Rs*`fC^6u@3?nx3=to$ekt|R$fM>rAWxduwfQi&v!O5(yXNG=eeq-7QA9S(=Pgy{ZaTC+kXq<~JmpirZyO7gph-2^k9Ih^LIAhVyeO1a7PONd>jI z8$a&`U&LY7WZK|2q&{|i!rv7@u^~cIj!ptoL+`~tci;1mD%X+r9CO9n2J1r_cB%vx@z={{U2q;*WXFgO4iIBE)s6(RL$Ewb@#6xoj6GKS?P`RRudS=Y2kT zYP2*K<;fl)36q>ebU5tl$tenL0kM(prU|uxyaqhB_>4QY=tOq{9$H+KXc9f7`>{L0 zVE+Iy>>L)2*Lt`{a?YqNLSdxq{mEj0p5sAU;5(S@u(;fyi#BVL7Pj2Fn8l-Cm~AA5 z5Ph97CA?P@IbP-meW-6hT$zum=AOFNvXR~FrVg=d#RJK@5jw6`Xp&u0U}m%Z;Uz#H zx4RZjEyM;(T91f3)-jIDAf;@Ng$P1|zQP6uo)p++yRlqHOs)3;xa_62i^)i#NN%Ke z3aAR4M;(r@R#-fr0PTj`xcBUs(6-*ppL~7gX;PSDOKDQO6txtA_MAzPiH%}4xg?Nw z-?45PlBK9NU+x+DTY8D+M`8Zm#&VXIbogULT(<#Hr556}vYLBTaZ7W)@a!sDi<>uC zpGdNBUvl{8YZ99KqH$=H2edWey$gKfCp$R$?ee1<_-u%zK z@IgU)OPf8Jvg2va?#tM9D~!I4ycJ+WEbb0Px$j3=N>c5v%?>5D-YNM)+d|d{{^^Q2 z+}*Ps9NI6fWbK&TEXIq^?8~XKKAQfUc2b*q)5q5Pdx11m*)0GKK=QwQ?A5B7sJP;l zLxeSYKo$+k}ZqS&;(7Sb#Yr_vv%TWM|Cn<)V0B8I)VA~;hR@4#nI z+027XIv3)Zl8!GvY<1jazQ$@ya4a0#J2FLLoCEJCvp~|e_o$%CiyR5FU_$6 zUm&F*{=IA5aBaLaC(W@6M)!5#OKVbz`>BbR6hwEb6N`1dU7Ye@LV9b;Ki#t0-9PjJ z>xiAWN5q4u$L*_$IZJS;Efy8qSy&&dEyOUNxSd~yA1km!9GNEBJu%L5j&6<8+*F+0 zj}aQz-%W1Nei#wtzXTyE+Bh?cEJY7LZhHGEun2ZzrAesnLNurF!aMSAqEQkYX%;Ip zD355g$F?+&gf!9+N7@B&$4?Som;?KWlBz1b`QU1j%G+Wix3#OH$j>8)=Jg+L)`11w zTu~;yJ8K?&_?uVPj%X<86{X z!qk2h_>hI~m3PkLT8-0mOL-@&LBfXr0Oq2D7IrqP7S@Qaxw_0^yC}SMy!mxq>Li>^ z7$u-2@+AC14ooObN~$|@!8M@TH{vF}IGO>SdI5(6xUiC(b;ChjWOVC@`2})Ii2(CA zsqas|pWzAI!^`$9i{?}@Z<#%xoq0f4Ls6P4b!t6{39cPGHzw$T-d(h{bUBgAKUyw0 zwp(9U1aRf|m7Y?P++D?W18rIZi&m7CB4SL!E7}>)4CqNcM!dLFiB~t5tuI&n!nyXz z9HO2cg7hcle%-X;MvCeX+BKG!HsXm+tafWaLGA$HJFZcBi*EIJ99iAUxbI7&*G=oV zZX4?B<}Ci}d7m!rBD&2=S8#FSk?c(eb}SUz_Gmz)%cbe3iT1VYiuYyZ>l;^l&+g5| zw=KkK@=_uPloACybHR8Ry+>XpHu;k5&MSgrlbAT&-K*&`cH^0AW-R#|;w)z5C8Q;2 zjJ8&kwg?r^wz#>!1sG7Fv6AjxD|e&C6ne|_&O&SFZgnIl+xsN*{TQ3J)iV?FCg0cO zQ_Q*_Sa{+Lk58Hy2yWpgaF)o+UWn|IUv+VF-c8B-iZ0ZAl5f~?>uEY$zBE1%5a2$_ ziemDINdq@)V+o$ z%)SfY_tc#ZJ+rpaak2;~SM4l<2v%Tk*3=A9K6}`*@KhzB8qbV04;D9*54ENwW<_d<;b#Q zF#_X0(H1+h+>aAil+&}80#bw(nkP}}a>tu)zgci`w72Siu=ifl{{XBT&gms9zn2eb z&yQ1l@yOhb#oV2@qcYu>Yh5hU6L#Ctk{)@;C6xyQ@)Rl%N5w#q*<51#d+Ijlx$clq z(h2KNBh&t&{S?`IZ(y9w*RktjUn(cqw7|8*#PQpPr-1g7V+zdvX{ZjUxMc@Ab_M?5>XDHj3CAZ+biH})JZLokF zVW0z1&;naP0)Qnc01vQZ*tXu^vx=z&M10S=Z}}NF9PUZB{J|FJ6w<8o#nZ7xpd50W zVP_{c%2cJSB9x&~1Fjnz1Lb)ZH;jEPb6+SxPUmvO#~SgYBV6uO-I7scvo^>TQrlFj zI`s!l2q6CeFu-{mm76NE8EQ9fK3JyMhseIB{+%9;n0ff~9rdZnFf0BhLyg*Y94RRj zDbh7LFa=})^DFGd9Q%n+{84N@Y&V+x*>bNh6ONv)ezhK=x$XqbuWj9b_Da$WvF zsA@YwEW%|~pa3Hy_F~Hqx20WlKzW_Oo`|icZy|Dx!4V>%`Ek~Bg&hS9H&gk~Z`+GJ zi};vPteUUw`;ZPk9!^}Ut3qS6@n+W9Y$ksEyf-FrNK!WQ2CJk(^5x5B zJhAg;s>a=oi5_XXzaV?pCCXa~wryK_ELO(Glm^lRiU3nGI$*pG*Gbmv=8z6=a!ZA^ zL^rn9!`^&>caH+kYn6JrPjCb_+iP`7Qk74;8p~LA?XM(^V#vrC7tN}SWs=x-P`ZkHwADD-8+ zLy&KgyceC)&yyO{Z__|spK*MKS#1E-MQW)PASuYdAu-&dM%Gx#*m2I9YvY{4zo3`N~fEV(u z7UQ;-z!Z8xQt~=aW-S)ia`WcA{@-ut0dqDm(gb40-Qw;#mfN#hjnBL8B~?X-TiG_} zyETOrJ8@fN^zgM&--G$11e7&o3mR7)XQ1QSTRYSzt4-MTpJrO5Z|63%MBevAwYO*3 zTP_o5)V0i-5FtTA5gtW6Rio zwtCCwC`dz)IfCmCPZ{lb-~|WlHifUvxyWTn9$cA9&<#}C1H+2ky!8n`7I|xC*;3c+=VV7bL#jJciZR zN=P7lM0CY2=iGCehn?9j7J*uVb4^B3Qw>IF63m@jC3R)nLbXF@! zpm9hY3Gm?CRvlxvy^$c-U{2)~ZvdA7b)Pg7JFSP9B}H?3VV`)V-^JZ9IRW zQMWH6dW7}a%+Bu3{{VZNzc-fRc(DNqK}<_@H9Az7kXT27i0z611qM}7Cjw=>O`ji5 zH2h2LvkLy{chadA&cg5?2W_4Jiw4oRY;1BLHP6mMl055mnyCpSuk$!NRo*hk%e~Ls z*Y^wWnfej*r*G{xw(AP3)O8=zSEb%ey4_#idy;comwH}8zDiWqgQ>MfUP@beQ9Woo zY10`?8(sW~(h<;2@49jE;mtW}bTfC%;Lk{#Rh{p?B(;Cvs%=TNI`C~%WJn3kG0iz+ug)-ig$7u)UjuNXPAmw5x3HxBIN zu1LJQxHrc8p0~FpPhy^E7L`IdJ%;hInv4s3F%(U5jf}zB~n)7 zClMLhPLz3-B8eRh2W~UjQ=={Lqv;~K+YaBpH?HWpN>h;|rdpCviUO>FIpWI>%v@1f zlX6OIlwlj<*nD5A06)IDto}IL&E~AeM0CP^HZOyv}Z*6^`forxRD}mq_B805u zNEJAz-!bB?u2Sn}O}|k@Yy~HG*qU`A#5pMeX*t)$Ok=QJZqU5Uhvl|kE1uG^zi5!_ zw#HUVN>Hi?hJv*i*Sm2@$i=qE$`=}KTeMbRa^93U=n5_%Xi%uHRMP`Ic(Q7Xkoc}_ z8l!J+%Y~s10z9x+m0PQBfiL&iX=~~T%xKX10 zeM986W%Yx_Jk4&?`(G>V4X<{KEKs4E@cQL$d-I9NQ58Qnv z^(xb!E#LJ7l=l?^X`VE;>@z(quVGX!Oi7zI=*-Jvt**;#=z^jYQAG3= z_jSN~raf&s5^ebux>TP}9GLH>4_$m)zkJV@vnKZ3O~Tj|OZJQ1ZSqjD(@*N^KGckS z(e-y|c{^RsRar$OEt?S9DN1b`A#yS;$6d!SXA^D2zG=Nt(6T;xHAMOWNYol;T~-`QG_GH zTxZr0=78l*DUdyIMG0csMGf__>B-(lu{F4DJH3k)#@?4{3R8OH$Wp~$&S?fOr$Bfu;e94Dnh{u9$Bc*6nX2JR5t=$Ia9#t{{XJe zIb+m^r~W|OG-sRKLXY51e~#9i+xBfkuiRDuR3w|7#$c0_qCI@+@tj@(OT;;9VdL_9VZCT?yvy`u4Alc5d* z-NM-Cjx@#W(C zNQ_DyWoao=R+B;qG@-6NQxfDZLaEg=$8vG!YHl)3G%Q$o3ir2Cwp(>GZL$yeyEK#s zqC7fLNLNpIp}^O6Yg1K@kIe>RZx^jPH&x_13tK~a{-uIacD?m=uH2Tt@UO4BG=g#` zu?xf&axUZPG}>MzrPx@@_;4u@(w!B8l;zX6S_-A(R$w_DYJ%Ty@_H_4<$`S@-(3+eHmAXa> zsZ@vNW8hkCpXuX`tPe+i_rF@)_ZYw_Y(QyFG8J5bR%0w<^S8W-ze><+xo%upW+7Vk zjg~WJHPHx+;Qa;^pp}D8l>2d+yBpUhR*s|+(Tq*h*4lk8A|$f&&UH~zmIrTTF=vmC z*Ag3~N_I<^M^u;*CtZbVe3llLk^ZvPmMraGN$>)rLtb7CmCAzJc`tR}{Kzlv18%f1og&{6D2UKV$O2oTBkjZ)cy{g3kzJ6e zZ_vvCDPu?dn|>o-?H_I{BN_xI_=JY)M0%n-2};I6-62Y+wBdOfB%Y#FyPS#ZQ4SXh z`sPLmjk=MD;gy9SHT?*$!(7HCi=Z&S7VQoPmsd6Dr?&v)V@g5xnB|SV*$Qm|)gwHT zDMN~T`+T2)BEH8od!$10WxXVhgwa`_zZLheJ%Je61HpWkYY>-jZW2qtcqS-nAk*85 z`+H-lVDoB*HPdxShSDAq3y!Zak>Lb=n6t6Bs?mW@DWo8`$#+%_8+$^D$kwK(1^$Z< z5;dD4Vm9}_-8B5n&02Z@doUgCzAJ!I$aM}cb53lZD5+~bq?%Xk#eDHJ9}BXms}lP# zpuKQ-6@>U(sWj~!a1^mQxFTrZqXb6oiqaiSr&I%<-K8Tji6OVN7aPyahdCU{xx+Fe zOI^Ax2}6n}#ZJF}Tx+&F@J^Hg$zCNdyrnHmaiZ_cC=*wD)Z@~zpQRm-!lF4vbHOwT zOvL~Pw;U}%@Pmw{DCm28aH$CUQ zo-neb{0`8Qd2}BHiIPZ3_oW!+QaHx+_^_OgQhf(apKRN0Eq+v`k1<32rBe<}COE!q zZ^`5>-zY5~yDmFw>f1>}VxM5H8rVX!5wjxgbe^JR&w5(B+;MCF0IYD%R6mg*sR|S% zb??N=BdQ0P=qh`L8sda91F6zHr2=|*PJf0S7Kbrf1&Z-=&Kn{T?Sx|>ie)0+*@@ytpqsS zK%2T$30b$vZ z?#?w5D_=b5AS=s14xao=e{BqWk9R8 zIaY;ry2+HeZYAKP(But&ry5H!k0>P2de?G#5!Mb6-99N5x!>kQr}NJE}|OYw9E4xhK#jPB)>+^B+K9SQPe>mt^- zA!$S89KZsU>5#=kvpKSRbVF(j*A+ac8N8*DU({&lUfO4g9yDTtpxZ)(b;H*pMds$> z-AzLXO0?msGXB&+l^9&^TSM9#fo^H(t!f5B=~MUOcZny&E!;pZT_>q7Jt5|sLPv$P zkdOzo3_9KcA6bm0+?MjxBfg>qML{_nP4fQ3xnG!{COq4bZKma|DOZI_E@{+8X1$mr zq9TJFwhgUvy-JB@w_4o0+}u!0P@=Hx$DAi1klb{rlY#E#LJG2J2V(5kugyj&v!whD z^qloy%kM*<8(^nlSY;6B?;DBKTboN~FheO0I4%7-#gE4RoWFLM#RFGc#AKlr(~{JwgSWp)IBp)*mz8tYoA-lm zUB?_n%L%BjVSnOTcMMpJz_@>cbv7GYZnUpD0A}#cglM z4embCxg(U^StI62_Ug;{;$PB^{YybPe3G-MnN=O6<}mfQjeD+v2RHF@s`<*(GgCo{ zzC;)2D#Nyk+pl#zi2HKKHumbx5+g@-(f6cN@xZNGPl#VLdXs+K^x@3=gr(bsxt+Te z%23UHDR2YZ<+rTm?WQ8Rm2Nx*qbC~&quW~vL%XAVJ1KPq#v(M9)0bdVrAP3^boUP< zoqP$Z!@)dHw*pkngx%yfpUh2hPm)J+NeTQg9n7h)h_*?QV%x2ZhHUnT_QsK24YJd1 z0sjCn3oZc&$sX&ebJ01@Un(NwQyJLgcpD9jdD zQKZ~+FIydVHq;eSUAG@I z@-F2XE?c5Ukgl%$>rZ9voSs=0q{l7aY$uUkGgk6rmZlPgZ}XmA^Gx8u`UE0Iu@f{e&56Pm6C z-IHwzZd$XOYMDS)-gdvQfL z+k8dp3xs)7c6qODjyv25x$Y!JNOD5ri*%UE4W&c^s=P8MrX$)i)ti-NhT)9v#V>F0 zET5_VZ{+^~sh0WJ79UPtpRc!zCl|=NBGE1*Uo_mG+#6{sa$MDUP@{mB@=12JIJ4ff zUGIxex0e#HZN-JT`gKq6{CIy7%WYKAU5xKIL_Y7v1&Dw)6M8=aQ55xCb?C8im&#aY1n?1eGPgqZHvS`v1@00GL0zR zTb1?hJRb{J)9p zxlrw1)wQoATK80uY+jt>!%I>(jpYs$09CiGs>mpIIVC`S$RBP!$Fsld<@?P~GE`nm z?fj3J{-b}}dp6@MTXbpd7`~}3Hhrn-i<;tCEzMex=G?l47L(&}nB)&JW~x_S(~qY% z?!ds>@zS#tsC-@A{{T|AP3H1>6Kz+AfaUsel@CPxFxVs|F{P=nX_uLBeP*56;`40A zPE(Wr08>2oc*$+AadVaeMO3WuYOxVU?zW7^W>oo6;9DTOaG51R5#cb}-hI^`fe36B zDEk1zqM;r-5Xv%q3qfi0mwvQu@xHx5@9RgV4TYqwtB&S*AG=CULES4YRiqkY`s1TrB&t+s$ZbrNKLsP)BgYzubFvD+5&C-!@ojR9k;-tdf#Vg zP-ZHEq$l2Sk(PdTQ{}X&A2UU7WyiDXy)U_cGlssaeyg6UeN@YnJCL{Q>)Ndns0`c` zAzGp&nu8kMA45Y(85IQ+--7Y`ey^HunnK_12wR6Oq@s>YZytTOiB34g**oU$3W>PN zQrn7!tFDCgrF($Jrx#|^f<`+tde@Cv3nphHa*X2^G9S1$;;Ztl0^DZmdq|+5_M8)u zcM}dg(sX~cW)411WT_qm$z$PdskIquc(34ab;z`TzJuW zvsG(l#KTp)D$B@pP2?#o#x`x4Yiny-KnX$^1J7`Ds@MHkTprg|=e=1>A1}Ek>kCwj zL^~GC+srYtrE@15yBP6|G|?H?IZ5lQI(O;Y>9M^x@MibBqpNJ~ zW(ZXKl%N<$MKv0Ep3eMNWNtR?d(m%+gDJmZ(rtwUiMXPCZZ-75#?JF+DL4af-611X z7>X4^aruYz(v+IiR~cOW&R0Fm*JEN^w7sHlG44((;*|^Mtd31Q$G*{}+h?XNnR824 z_)a4*o3+WTkzR^IGa|i41;)g0;aZE4of`2u<;~u_xrdo;%yBNljT?$MacV5>-VNDt zj0g`Uc?3!^zBvc$la7*r-=*Ocz!O_ZYA@XuIY-yv@y8e z(~L0gV^N5n!#cpH-B->XDeJMgs{q}wb+t^wmt2x1-6 z)`sa-_j{=+b)QAs2I#`?k;2;wZN1sJB01$+g0yGUuhv+3h%G^iU^-j=K7`RQ#fu6x41C#2S- zY6wZi9sHY@r$+Sh{$>6a-3w~IK7Vc>zuHVbpS>J!UQ3@ILA93cD59lJU+FU1YBMTO zq5iB|VeJlDz6E}M`3nz~YRh-MdAHN&qzKbC&m3E$^H(v&ZB0DzaRS3rRt*|PU3smn-}3c<(TpuJ>o6Tf+Lb;y%vTZu|9FSJ^R4YDd& zw?zacDW`-}{iPgvLXKc-!86G zB!DwQY7cP1nEP`C%H5Zl&5lXu4rTRCd4F$PTYkZ@q&O1`YQtK9bsDG)ml2xDufx;d zi;EjsnaJGu>U7`H*VDo+trp$Ky)DR@a(hdR!#Oh0$c%`gBexoCJ*qUTMq4X!Z$xzG zlR0mSeJySEMgl$ad3h=^cI!%PCNK%`xcY~}0qpDDhi2{&vbk3gxcE}I>plmX?^)iz zuUqQ%J;`^^-ejh!Wt6y69yJX#GTL&Wr7MiwEnK?mCsN7Zj2A3UV{yjB=D$}Q!MAwN zjCRe(k@*jLNDbIF#6+pZ6zTzPxUw{}9iVFK%c4>`nve=-WLm?t?n^|z)Lz%%zlB(K z20Onc9|OAXtARd?J6=}T+A}Pkp+2VDng^A;`4N>haoXHS)RgKlZH>4;Qg90Kx8qVu}EMK$OwzW)Gm5psZSSw!9HuHs;QDEy=q)2Y-CtcMS&RJt__{Zc543sag2x z)b?Xfm2$6?ro-V=s~(wsJM2pq!Q39AIpS%v`eWi}QtsioS3}$8`p=DpfgeK10I9gyBu-j%_Zx8Czx-0#IC+n{VVp6pBM4I=k*`Ik+!U0SvLK$ zGq(-hxL#0Z1&+xSYLPBOs_>;nAcXd*S#$>%o2Kls;?mYnfG*#ME6%UsL}ltvnRedi zxLfvT7&L9XRk54-J)Z`qvPwJ)oaK70(kd&RF)wTF6PM=P;>vF2+@lZ6x8POXzX5Y) z7hP7Qqdzk`l5yy^qeIWGNxeye1az_zu$rE!7@DMNFNoocwfU&0et0elqsel?K#}6D zb2xRV&=`2@*3qi6NUux~ab9^3pLY(xx+c%Jn`tc+mC0JcDkPeAcVip7TzyI-Nr1fr zHqGlJcG?%M%M1y6lvLqT6q2PATAE_I8;$5_m~z|gOIA#_uc#~WQrlCuL}yT%1u3Ug zf=AjgbT|9RbhdjG3vvgmjy>ePvm)5_qE&+KwFSp@OGAtWBzBSChh^_wSW(oE8*7cZ zRH~aw=G@KcV^c4o#C+3wNL5e8Tvp&obxAX#`b_FXq-wFdpTxn-{fusmSjXD|AGXE^ z;?qJs#{-H;cJ@-LDzwU!!34JW5qBo-rl)Q#oiS)i%A8zx9X}94hOzjJX7j7eOBkl; zMP8z9<7VtT6svP(F26NNanV}o2t7_P8L3Mprd~xj*;FIG3fxXLd4oNeEMAhrgRVt= zz=e}qsHFGh(BnI}*^*ioyM5%EJwtj-+|f6d)tNb%YlU#Pc`eR}vYkx@wiKU&wFfW{ zvlV-7d8ln+bxIXQw9ufXDxVMZ<3nr}k-sLcyKZ`jJ_R>b)4f1f7dvyQUv%T=)Jtc* zbmK^u#4)Wl$5}(J0aN8Toi#P~&|vKQe9&vn3ug48B*>SYF8=`Wcj?jS%bRrk#q)?R z^R^c2ntV(8}pBA)zwGuW7KXz#7V1?daayA|)GzhaX^dRz`QMTEmkK{=dGtFcY)VsTK~O6UzMW$(>f|x^Cum>%svjYDJ*KXfw&xBsgt*$< z6bHC|HO9}ET9~OlO6R}$`Ox!M$O#UHsvo5_Xjk#i5-WVsXh_MXnaCekdsW*$;+#RXHYrwfaxs<<9zKOjZ`o4&-o7@&i z-{f9Nn1QKiegaqW>vZRtTVx9V05(2R`g1vL+vWT_j)}ibe16FH-te^?tQgK7{{Xk| zV^7kwE-c@+&9HYS&bD19w5HpcA;bkOZy<^l<_IR3`uAi@ogX=GP;LzGcH3?fHb@bn zG8u})XbiR#rD;M_Kmk!EgC2yrXnDk|&8V>I(ozk2VnixKdvxu~cJAA^OC8EMel5+F zw7QCss8XW18!|OS*5^aam#VH9M-J~<(O8t{mbX@>TX8EIo7QMbk5h_eVRDa~ag|GN z^yBp}VYE)%oH5AlBJ;<*iwXNi@3Odj5+O?-Sx^f6?bHx`mEu;E zR-T>i-k7nIVeqGkCAw2m9=w4XL%|5v+6mHnf-p8rC|BhqxFMqqrOYo$~8ZQSa~f;vR|@<|JwRg1d*~j_9Mh@-XDou7OCX{uoth9PTC+eG%UoWDi_V z3Al}Xvo^C{@0&jB9{IPfE4SPtkfxt(={X*Tok8swOx?EDWv2^@sIu9&yo?LoOHwSe zcU__C3L+blBkc}XWw4grs#=G5DHZW_(&?sM`12 z{l{RtZL6fKHSzrN!3pJ=a9?Z>EW+pV0dYEX5l<2%mLe+uijby1~iDx{J8rlS>icD3t=ol%XKZ_y?+8HF{@-^?cR1;wC-lZnf&9q(u-jR)w<=uxMabLj-(1$X z`3y&+sR~*aoenl=w2(r9?jZK!!rkR1=2P9qlvtefY|gQ$`*XoPRzM=ifUq?(Nv~!N zvwUQuQM9fcI#{K;i0DL&4J1pff+SHIF?)Y4o`O5?6Dwy<4O^Jf%%Kp*PI zJ>M>TGzEE-8*-7yHug)9wyV=f28(tZ~CaX3KO|HHN8U3v!W)|Z)lJLQ~JvteD30_ZMHz$ z_H>dOwLwZ*PM_@*07>u0gC7D5SExJ4=PUGE1^)9dTAFcYwOgyNLVA*V;??9rF&LVr zgU|fF&7L_-xn*vwRmM_jTvuG0xav2i78h1qA-2Yr##%D#_F@>@)Q~Xk;ilTuC+p};TQUp3NctW=+7AM)Y^5pi&=g1XbZ`!I(JO#8<}{%rORCje$T%J1p=usyaj*a?k)#~`rx2_bDw6@b?v|F&4oO;((G7}c zi6OU8ozI&=qIWBPXs9PXwZTgggL*@*)w~#G#>EQgLCE61_6rg+p(|Ui>bo=yHcC`L z)Jje>xi*|#8Dnl$;3Mv9ocN7-ERu%^0E*W$g0L+_NeykfW=)lI7J3i>?gtOpFe!4h3cBi%QeP<6s%EbIdQ?_~(WpgAQ0>Pu zc%vHf#|z3}l5qNpk&2$U>YQwoT(H!HXca3`dvVE(2$3U@a1~G_3SmgJ1#aDNnm~;e z$qH6LHifJB1BWYZjqBTs)s8EF8gF}+lN)colq>z~sSPJTYlLD~VrXpl8%xHc(kBUC zkOP;C?XJp>sxQrKk>5~?;%MTv!~$Ewd?Lf^*<5L9O)k8b9!kgeh=R)ACY{GTIds<& z7m<@Eq(ZLTcyihTW3KIfQ23TY7)hu4)HsW+0$F2ZN%;wKH`d8u(%Z0FbOYgXTk3G1 zcoi69$X-W7Y`x=Ot<$@S5RgA}HKd z&`*&P!yO|{!E+s_z?2k<_f=SlRC<(+VZ8z862wb8)1|C{(+Jk$lz-Jot{FKfs<%PB zlJRA(<1bFPt;uc|z0?}p*5?MMAr#U{9YsOu?#3%~?j;Fw!%@+*QKP)QH{WAi9vg07 z?rajG66F)6J%27NuzQ*kRFWl0#Flt>fm^UP?ZW*PyRGT(`?G36Q*6^dhp)F3+s|;y zdP7PH6y!>mor!5)re;}KRF^7--Af{g)s|Uen;(0;xH4OoP{z7(9kr_~*5-*DFy;ugAjIdT&y=-095l&31Oei$Db(RC!4121gO_UCq{o`W z{zuFjf+M7Lrg`8k(NznJsbJ?YhOO;NFoY>I1d~CYG4bj~Mv@S=(8Y4u8hg&kamK@h zoo*AP_8z#gjzri!+ZOfIkM8yqU!+Kj7IGT^`ED$oLcZbKi#vIfx2poViy3eqdoN&F zHh4qLH08=`)GiQ`5>HcHR%h-`^Pp}nB3g?(*3os`Fy3lBV{VR8LXv@<4`HWfBf-h( z%Axn|DJmkF?~REUY^2GNFi3RQO9d1not4JdZqAkDXR;&8$gUOLxG(vLij@?ehd!SC zT-?TTTtOAYKW)2#b0jw;#6({#c(jTGooj~tt3uA?dk2}-)EhqJdlIa(#^A(wl+w+b?+TrLQT z&2uVMC=b5`Lc&ghbt}NLUY`9P`l0nOBG2NUI+oVdaY-NNOTr$uTeIy!eIdtkIcQA} z^J5>qcdiZ$xg`0ew?((x zcDEG7jt00k+iE6G6rd$er~$0@nw&vnu`{x@y+63Lz4ZfY#M2tme$w`B-%5Nvw%7*s zCU)+WoYtvw3oI!A0PL8cr?R-!ZJ$nYpzAG6hUfJ&GUI%@CQ9~r8v|vwrgpB{gJnnn z6|lkshy9`ozZ`4vJ8u@x!xI;m+!+_XDI453(oz%)`6r;RE*2Dg5mzg+d|1P{@)$uR zJWp`054D29F5X@PHfH>N;-H5K?dyveVo(v}p#|;7E>%f*q#mZfW+%6eL=7wHgk8F_ z%1MztAa;#Z&jtLjH&%>Ce|lMA8ktIxq11O_@WNtF_M=we%TSpMEl~gi;wL;Ta2sPC z^oZW)%%MSkT%AC42{oG7j_q8p z%u`VdjCMuKcpX|^xW;Qt22XF9DdLt>PTv)Mn60~+VhdNObZuOb(4Ic@1HWak%-&Mu zINNVg1;RVUw!)@JmRq!DR}i7IMbJ)h}!gLVi*?QNg)HOZQP z;&*pdHCl&iTU{GZ*ql<`%egnivxY4hH?wiKTO+0|x-(Yj=oCbD8$(C{4#3z05%Lo# zHfW3Qn6iMXYM=uU+_*@s3y^+Fdyd6K_SHRrl|KkLlIjq$Qk5wpssNBcG{r7PTtikQ z{D?c5eNTFK^zm(a=ggeFVb~Ga6;f^&23l~_xv)x)-HO}$wY9;Q7~2j-$68S5h4bq* z!dtiZ3ty#<7UW-88=aQiHi&y8Y)f6TTggpQkKJwpJR|~X;#;Da9y_-?qTu{|KN800 zZ@Wpky;p{n`~^NF^*QUykoE$1&oFvAXVUG(>x8x;Y@Nlpqg?J!LO>;{4t~-dX}w>` zsF8?sa&D~?4%=*Fwan_hkNP%-PW0zLQBtOqH@~cVLMMyNZVk0lq-T~(mb9lX{WzwM zCQZkzi}!yL3)3A)sqOQAxV%-nd0PJfe|VivArhN|8AtyBXAvpO*<2TIXkXmwSKP~s zew9M|dVjpedd!cKlDBQ|60J@;x0Y3;^-{?=rOBU0R7|i|<#kqtiFV@^olYr9Z7J&F zhy(998e6=PExn_~j0^t&FKnH*m5ZL?VGbkWjL}Q3eaNmMmPPBF5oH~^+<1@%==Eu8 zj0;2koXG)d!#van*`~K^TZ|FbeS3?ZlCdPGWB4+ZdW;x z;WCgjHB^PC3(}~uyBP6jHTj?G7wAig6(ZYY9oG_iKFItBq;y_xT)~=CAy~T*Y=wxEu??maaO8utaUixn((dxPX5*$-?rO zN&f)dmF~o_#)5jf`@gU9D`&b~@KjBB@lV{e{iP|}T>I+J)|6eveCA$qzu#=o=9ba4 zAj&ptG7?j$C*|#aPPB^CONw3z$($nT9RM+IUv9E>sF#1@n;y}!xXr%Y)HFBoHq%K$ zN!6L~fuDXWXLX0w%ZX4o`dVVwc_B1Ylpyww%t4Dbi)Td_kFriL5U_BJrv zq?Tns4k~Rf@1{Fa%~f1=*QEz8H7~`X4L`fKRAMTFC2y?*v&)L%75nj6?S#g4Ag@z5 z%`%#R6qKe+gruPV04hem=*LFZql!gyp3HZYjaBYY>5Fu<0qW6~lhgOA4dJmi-N%hM zgKJCP{EL2)^Nhi?z<%*_U@Zt_DX7tsvM5Lz)HI}#o-HwL%`O^wmD`47Ci!dX@-H6Q z`f+_heO0||xbrO7{5iIpQE|67P1(z}#y+Ze+OgVPETu&`0;kZS+uATy#RRo=`+d9s zRrysUsqf?aKbg~h9QrwWG~x&m_h&1|m(6!eM~-#(dQ4_E-$Ln`O|gQ^MFIJIz_U*C zo-(_)cVXbgyONA)`2PSiVX$@%>$+S~v2IhZvrj*9Wp_WS&rh6NYCCU?Z47S#2lV0D zw(}9~_EvO?CbE3Ap+m6wX;H_3cZdCe$F%t|moxK!Z{~fW^t=9(?zyX}n)-0lPwB*` z+t<}sGw;=^9j5Bu!nMe#ZpUIt5}ZjC(y2}Y`p^1-KJ0w&+&@%zZO%>{&W}nj=zYuD zAN9AjdvemdbCtE6Pz@>p$yK zz4-3k`dPkiv#N_x@o!Jq3*JBWSG6`PPSvR?PCOKN6ZM0Ewl3Obqj^&IeD=UAar&_j zSFwhVh*$a4arA#{f7a|f4Noca%l3XB&Zon_P`~b8^PSCW6)SM|aU`cCHySb^LSeX2 z7yvN|hSJL@6r}$*k%>? z;!*zqTz64y^xe!s!=yiCn9!jWtF4eJo)^9s0%7xTZ~UoE0=aDTimw_%IBs9iiQRw*VQ9{ zKBK;&{Q1mVmf?5h4nmeMF07dkJuTVyO0TdfUKU68QUJxC+1jyMs>%7Xr*!U=y)pEZ zk8^T>hoXI|GOjmS!&~>;gUgiqmO$u*llbFdiMFb1p^(kpr!E6R4&UUyO_C)(m*huL zsGB84wh#_}6snnfh8Hh$#Kl^=za^_5iMM6tQ^6B0hlVze-E9-MWykkvNE($DAfbRq zXL-g~E4VW<9W1U``S_Z=eUlFwHMsetJ7(`y#f&W*UL$sxNfZlV0V^p2oj?@BaXWjR z^WCeEK3@L-$?3GHhU7~P*843e(&UK`AA|z6l`FrlHF($&g7m5*GkYd?9=uEQ>D$mx zC-TJDZ>Gv^HkFi@t}=l`VF1*h7xdy=`Bv=5QhZD~T$Lzw!)Ni&rmsr(C|hdXx7W^` z;V}c`wwE!No;EG#&b7zcNZ);8vI?dFj|T{%X)UtWlrPusmg>ub77LTFoVN072XBPA8&JBBEuW^;6)Y_cI~9M5X9M5Jn5VK+tT55_XEB7T=z>-HKSh(ulbGPx7HujCN)=PJd*C|aq zc97ahD^rryPbn(yZh#D{hF-~=(P$NF%wwz7lGe)f&ARsn&`&Y*#`8y}eiXP8t6t~6 zzf+0+e0g5Ktlm%p@8jMur=Ullj zCHwUKh1M3+8*$fH`##^Z-iSMoxU+EY8?fNvR~0gtv=w^t1@b%D0ZICUwt_eJk;wr;VK7D0+~-#I+S+uRqJK*y81N)nq~B_|@7 z&8C6HS1R-r#E`;K;Ab!Hj&7wX*gQGOexdhjZ};bAUnkvMNq*@eMN3ji=!E`Mfzp`1 z#>CTsoygn{{`z3_Q?>Zc1S^Hk3SGFhrD%@sm9)(UZZ4n!72qfl@ zD?X-uY~J^6+VgGNw`me3+}v5bZYP~CKJu1oKq{}?G8h93Yq-}m#mw9^q)3a>AEYl( ze!aP>2NZC&+E(M-4}?kGyOwEFR$aAf)kWMc(umT_AO(47uD3{{1p<_pZkvsa8!mHn zU$ws_{9gt5C)gNwZrqAmxBC~H{mF5Y(Rj7nj$ZXC>C@>e$2*hKm($+<$0Pcr+$?_I z>Sxxhi0E8|xKN3Mr}?g>vu+sk>$H{HoGPCWdFo^-n6%H1VT zJmQH`lu0D!O>o}j?z>n}<%bkrY#q6GHhoK;C!szM^yOu?ZR=&0-)wfxvzk*rUAeLT zKKS~LHApEHDLsi8`LDP)+nIN?)wAEWeWuner6;0CpwCKPikpcR*SdKc(3r89(z_1I zDFqE608ppLc}gJyuKmZIchA(WSi3eJo-CaGW_rfsuSVQ<>fLpjo929V zjkUJ|lA35u1C9^M+IxQCjlOJLq^x7FQ{(JMmh5{=%#+B;bhN+zBg8+buTz|*>UWWE zGaS0REZw9jb8f|F%T0gXqPYgro`7R{+nt|l7TS|loAG45!`>Gz%cXBiYebP1vnnpu zWBj|anfa~C@&5qFcRGPmSW1)PZGdtF)`a6ncEVa2%xOuwom2XJdMuaCcMFZram^bS zh&E#%y7NaPJbb3vwXzGYHkTJq)^#Z<8Py{`>}N7DS39Aj&ffYqlw+)(iF-=n^&s`h zX!RAlEqqPFkW;fgD)7iQ{vXVT=6Pt$Ylf z?B=4?YTqB_?)!16%nhrvcXv5&-MPlRacEuEb=mOMqTVWo@*~ZYA!4B|pdCwD$PaBX zw9UV78qZQprj9(X+)+r7y;}1o#^Al3aP(!sSH$05ZRXeNAC)5=Kfm2rpf<)95B}#`D|Y@W9K&{FkGzCy)Z>)%J%sjS)8QUfL(Co7 zP1TbQ*@A{4v{afbfm{=BfXVP5Otyps)K<7K9f}M%lsb};_F$}Ws-&g2ovTR{trLQQ zn;pf`_n#&M-Tt`Av2Y!a-7(Lae%A7{sx+N{LHZ zsPWUh;EK?SNw7aZ`+0X)59K>i!>}$AS>F3&L&PWNqlFH_lHdmu*n(3;c{ghYj_Iw0 zxn-|nnV|mw7w5W+qw3>WXj*@+=$HzZfF+WM}j2+QFRVM9E-N#>gl^3}BZ|Q#R^V==lS$t5{ zdyRgXJh+{?$jxnO64Gtk-H#y$SAf0`n#mRIAe?P>X_eYo`M))hRyN{)kF)kcugqPnbz(yIIGhgg*eE}Yr*p~ru?Szh^OYN@QaB{gzj8UOR7tQHa)$Rmk%XF4q z^Ih_j=BH6r6a_KsST}j#d1m2Z#(EQbvC0-ktQk-k&ZZJH)hQuK_I6&o-r-}u+uegc-t&x2gy7KYFEp}+p+NL0Ak<)V+GR}i zl?Hv9!zCy)g=Wq>s&`AQlj;$2}+Xc0)aY`KPl-< z2Q?BUCV-w(4yJ(lD^^Jdp*W37g2{-|T6LqS=AQAMBT{UMx`eB*;#Dcr6UsChDAX>r zbO6`+Q;6EA)c~5*tjDvaAtPXZVNXM-cIDfSZi6D(kl+G}O+oF!HeCkJUIAqlGPFpJ z;c4bbQ*I=Xq>yR|Bc(9s2wsDX)l(m1B6cn*!O^0|*f}?B@?D-T%jL7>U88{AhkvCb z&t#tpQvEYpM{RNDyN<(`wB76By20^@?0aW&VedA28Jb-1kqz<|!`vKmbZ#xxw8*y4 z8tk{`PPmhqBB5<8R9C$xF^%5C+;%gq)-IA} zTNzNvr%<9@lNt7(hWg{b9O_~8DM(rf?MjkC7=l(|FNsv1-TA}ebRSW-__?7mcf2Ed zDHLk>&X%qXihgAP66hVv&)a}!wMq`QpWGjBaaR{D-X3C#t&B}!$<_&}zCu9M>nH^> z+5`U4gW~&BQHMtBttF!&yJ9U?E~sy7k5yCvp^+WO?ZfR=@GB<=7pW0DcSBA24X84n z)r^V7`hq>64sO92tC*$6Xg-1YQwT-->wY7 zwnq}>zJ3vq9@2UN?Kmnjkl9~rLTydAWN}j^QeIBv-9a@{Qc_~aT@6A>_=xQ|iP)XT z#DUzCBdFt$xs!7ylgmgP1E>?Vc^Q= zforUsAH&=f)O+7IZictc`vY8lG_)_wZHEmgnNJS1Jw`(ro13mqd;GP_1X<49Eh;`E zrtivpg~;h@*<@{vfwLPUIl;w)w{Ddq#Du6c24}5sDw-0sKJrw^C|hbP?5OF6^#+=?x2{(uf}D?3B%aJp z@*=p7w|bld4Q(fq@0TIqgV+E+(}8D}p8?MCCv46>j{B{;^MaWT6{)Gq6q}~$C@Lfu zY8>pdR&9+6-w>=H8j>`MQy6RejHfc%-aI0R!+7nbt>=GGHj39-G@!-)7FaFIWH}}E zThg>fb6-%U3gmHjV%nb&SxSU+xaGYfGp=#LxzvZ5EDt!*)uKM7G-3{{U1RJwb!qzZJ!g2z-6KB?`4- zeY6g$aXwJ{+$q5e7Ht9C+$}E=alSUV#~QBpt8JMXYV+lwsCM*jxRyA6Zh&&!({zkm z0$$(PFi1BY@FpM5+$gGZ?kmda@yN)R2-DsN`_&J_ zYcb(gIyN%p#iU_;fwv+P`!2wQ%9H)m@|NV44{a4p{@f$Thk_-J-8b<_FDI6_lc){E zlEtl|@V6Q>Ez|8FoDT9?(b?>$tyt2TyEjF|l}y|c-U{lIA<&=ssUTslObJ zLJE}Qs?@Spv(-kVpT7#{Qgo6xZX~C_x{kL%{#2;{00D>^InFDHeXA|WbRQ_D&wA*O z`qabJSp>47<_avU29jsSjZU?2DE-I>3DX_dUI~~Gtt}JhT$J@Y8f@qP06Lg-QW);s zd=aYLv$RK-8R;I^78Cd+W12{GRNt`BTv|u>HuhVmLP1&o0HkpU?vwL2Rql8nrs7%u z08t4~@EGo9#8x7FZr2x)p~Led>OaI>R)noZL}nAJ zv;g$So@VCWzdvBfHBCcT?nedV6}JK#aGgY)>0Zn`A8AzAi37d>#`lEVC~cJ$zTu+W zQ393j#NVY(BK3zwhjCm>+M>Fnt3NOggbY<>*=f)s6QEyp?>Sc1%XP8Jm%uKqvt7A2?+#H<4uvb+k`=_Ua2!(_Dl!%yq51Q zvpzyvHITG|uxXtMz&LlM8LAJPMRM4?CvQ6X%eKVGb+<}g^VN`{r-Te%W=?X3j)WgG zSE+h-t*>cXEa+u0qd*QOswe|pY3|3K<@MXumiFUqA+>+CvpP#ILXr$x(Fo{k*bX|~ zQq*LlUIKOh0EFzDzS9-k!pm<%s@e_sYCDPd;GBKl?X4nnw5U^lP}vsEywb}FWtFh4 z4W%lgiBEW^XvXJgzdNd|4L(^>75uCUgywOebPGt!eQI$8he#Q3{EwmtjDecPv-;A3H zu`X17y>C|;Yyi~NkkP8Ds#iZ|BejRG1D;FsA%`wEnJ+1)E`|+E# zwcJlsTCBLx0b7NG1;nmmqxpiBs&gl>983Cisx{_OYE+I{x^2&vk@GGY)2e*7!h(QN zIdsOeW3x21MLtGKl(Z_)kd>)L4Fsf|k6db;C~)tyzSReTN93qJs1z;41aNNK7# zej5AxaI$DYQWW>zAT2ctp$bnyfToT^;w0$NSxTiOI^fT+lZHC6vYiF1UHWvHD1~T< zrOJ^+vJjn1T9k51LO~>+;fOM>TD2Z&x*+YpZ^+7@EvUB6q54<)Li*YDa_zosJ*=)P z*)!CY4c~Q$^r^2lR4QY^V5MfGp<0KyjACfRvSY*A;Wa-Zw`}kEd<; zM^DGk@8RHVv2N^JL}_9@TdVt}PqOmR^b4|hCxJMEEJk+5%7Y?hxhYF?q%hLLbP5kG zppb-pgBLhmkBeod<5_!3?a#e)vb{XqvGQ6E%Z~p5ZL}7ea5B&oLk)o11v`?O3~Vj! z@rv}LA$sd>vSfWbkd&QdzEj*vtAp_{TTB{vVM08*@*_#qrzDaEduf9#;A#nTArZCD zlTa3PEa?@_xIV_;>IAuzkj1$2Oot1BF4aGlz179i+Y<02o^)xT(|>$Sd1^j=H1y1N z;G1|!70)fPDedhgwJ4b(Rq3DgVLo0YPV9t9Tl#8}L41mI&$A2i{s8Xy(71268Vc7Z zGgFzU!B(&+L31xrg>~drB`VP%`$nv>X>SIg*D{xx7v4D?&oN`>NUu>^;#+%mpqV+N z3U4AM8nnowlV03g+Q0#x;h@Bu$Pc`wey9}cbFblrw}Yq^%;v^){qLkWIqCHE$9qu&QZbRA&BhTcE&zWzsr6;Lr#iz`WY0xr- z1Ku&5-8cMveHN^;+xENHWv+`dWG%;|a}QCA=ieNENvS4@6vJb( zS&p=;{iJuhize=sr@K$+Ub|V_+d-JteY<6gVYV`qp_mXOGZBO$nItAU5-2czt7&)@ zcP>s^wlKXxI+;9mJ=RGQK#fO|_ zn96V|M3blERWbl~o|qntY^mZ`K4tZd={wWI3s*4n&fVHq1f;DNAD?Pn(a?^exRDEv zsE*JRgR$`^J_rvZaz%hlMUZ(1B$i51f4 znJ$8rAha-g+hqmKCW>3V!x=h3Kfh1kuk2?&xj*WO^%mumAX)c?w}R|0*UGi+i(6kf z8&v^H=UskbN+egrmecO-!(y#23cmjU<^4$Ik&$sx$tCPsufO^n-Q;F>w;{bE^U7P^ z_sKWJ7aaY@{dSvl(&DO5mWggH4UFgl5(joCS0uP;?$i2%+_tCjYwy?p03UG+Y*tc- zE>Q|XLQbU6008OPi1IFIn6VWvG18K=Omrntmkx~j18tY~9{p=aiapH$iVlikFZysE zZA%PtWafCNZp$k|0%(;JPkt-Ots|A(nD91v?^iDm;@}uB8qH>gWIYLvtNoHXso?Sv zQU3tBj0ZLAp!uyTCUCXM2Yab*C?yC=>PZ05)PF`Mx+S99HOsq4Xm0yN+x7A_wj4;( z)R!J`Axd>=4J8Q&GNW1mDsU8GDR5N@jM7?)F+CRg&h?eJt@rJR#L~FejOIM2qVgW} zP_af^C|X+7)Hka&+KQ8D8l-ux2`F|G9ky)?4mHjMOhSAwpjk~a7Km-k+v+dE0lHml9;-MzG zmtPW`b2FL8i+4xYotp|yF_%(n%gp*u>Hh%zP412HMqMXr-ybd0JScBl}Od{-1x*ZI=ZrnbUVC<=|Cm zHtt^Mdz$3mrrmi2wy?J+&UgO+4vwnn1tEGPw@>|AYmYX?x4myyYi&wx`0Gz6_Wg&x z=5Jk@+1T`xw{MkjRC0gBn>LrFxEAu6JKt~#r2hc;64G?Gobn2m>S{$lkpi9g_HNey z0IvDDElVx(uOxi4^$-5>S+JeGt`+vO!|lDLw025J+Piu+kUU zJZ~c=cV}5P*oOMN3e>vy3{N<-t+BZ>LuhffQ>n+FP(*0$rA|8Qis*zPuvDcUzy)y0 zIK@?uT+e}NHcz4+Y27v?Z|+aLT75L}MvpdL^WE+iE(>Ow{{X_;^|s>5lkk^Rk8#CL zZ*oG;k<=p2Ba_tRm(}->y#{g5qy9+aZ%^_soZrru`Bu319(&vp%y<^)Nw~CSqSbF} z%4=KAg4$FX$QqR#Gicmz7&VwpLb5m1zd+?AE4(Z0>wq ze0a9G=91(5eWx*b6VwkT?%6jxoc+l;sW+DttA)-~=~oM^lC0ESmoaWSs@A!yF3f6n zj?~4oE@u@fdD5Q4J->IZPDc+y_J1I*%-u0|p3!>f!)gBjHP2I@D{xrz9|3BiSBj_Y z#QE6rxq6G?L>PCMNjIy1h|RU}{r>=YPI7AXu+)W@Bu{#!YbrG)1DU~hcZ+z?rxphd zmU(wMxFW>uel6R(akgbkZ8N1ZH5qb9R)RWE9EVJ1GWUFo`YjVRczZRA3td_q^7ms% zQHN$^%8)??;3(25-9erb&u>apZ(5M$?}{2nK_NDK)#+i&i>q`WfxmCC3~BMU{z$D1 zo>t*Gy1h#`!+w_czbMX+8r5ZSh^`P!g#r{sK=7mroEbSr-mFVDCwEkixbgR;?gsSL zY_VK6ZQP$#+NhZvEU6SZd(1?1|G|az(~iS=2*qtd&>VssW4JI9BaP zUM6CBmoG}b1OCnX7RhDAZJTG2DQGBi>iYco&Lu5o(IQ`oy!|?QV0I(iB^e&4JwrfNr+NPXbXx;k4{pAaP=Cu?dP23zeIyc@vnl{J zK~j7;&t-1CO(CND{@;P9#Ms@n`Hzo+rGFo`z6gOgJ)j=qc3WON+A8Ss4A(yrE7@db00t-gor-=$g$VVeu8VCn56iNJQy? zHn>ooDW<6}Ad;;oq7#|+V#Zf(*p7&##M*Lm=glWXb8bp`uhn+*{{S>^E5w`Q?kI## zdsA>?q^g=DFp;GpBd}-QahS`>o!f_`ZToGUC>OVF@%Inw8_wy*U zJL|>#DX=(GZYuYDb}h0cv9B`Wb$BJTAt@E@10Xs9nZ>2)!qjR7Y0B)Vj(_^Q_3dqI zZe8K+d$;)~i&#RFaoh`6lX1L&Nb=bNibHCryyGRAm3Ja(LxH5VYR)t&_?}_8{{Yn< zn#~iFXAf;AJ&RE2+{25~|Jnx$I$EZR*zr`>X8z zufvT?jjLn5ktDcR$4A4-SC5r+Twe-4gZjYwig2H&t{Rc(H|ck5bC0+Zo&Nxs%J>%r`*?YKJoxa})8mnAZig%> zE5EUSKexE&ucdEGn~RlqExz};;X3u2B9i22sU-QCf&+n7qIzZ9hh&AB!{k!IV`QTk1-!=w^~*HeeM^vBEF_i)cH^&?kP z=&hq}hApmX(Ik2o^pEK68$l4a=OMyv&Bp3dyET=>g(3n@VPqVGl>4!|?4HGN{M*{M z=6LUZeMIMO&OgKA$z#@EtS&nAb;fgd74lP3_f(eLM%3F0bt!sN!88g`ty0=fYFF8h zQOT3i)s^ac9%r=euOanr>1}?~!T$iNe^i`_>UWUuo6mS(9=JlCAb}VtptzEJ46uRl zwx7ov9fP-4Bsrz5-xf)|cL!bc;iMM|5q{RQPnv@Q81kHO0VaTi$87aTQ0k&FqrG`X zB~C6Yn>tDY-l2cDjP7_Shv=Qr4r9aa19+_Mc`lxw+~o zwrCe6h&t;03kUU8^^36hua98;K0PMQHtsjt?g7l(t|HJSZaazy)fsNL@iY`F=@}f7 zG8mI#!JbsP=Dj{1(w{B6Ua7xZTs)VTzs>&uBhGVzebQ=ekdV1gx}>(GHkB<)TGSAR zn+eTIB!B?SoUy%7gciIE++``cc$Z6@xdzen=HQ?BVYOtmk68TOZ1Z_C?%tKXu(sU9 zRjYbQCq&5!HC;MpPCyJ}?z!%HRHx-f_LsM=+uQNE{{X$5Ah%sYo^|)0GG|FEQWm9Z zP_0Q)%DSl5xb$l(HsGTbM2{`VyDQUjlhkwV*vh!w8UZChC>5L)VqPG33o`-=n?h&` zLO}4-rY$*@2DIOReR3kjNcovf$4Kf{w5Fe7ICIH@@ffX1h*CGE)P(2j-(%Gu5Do%= z3_8~1Z4X%T9o7z1+F>9!a}KJV5|tn5!S=G@J^&XNFA_qx?~7FNNczf`-kcwA9#R3j zn-~s_zrs5Qe9c|P`w`b&kUv>TaE$Vx!Eo)|dFwtUMEi#NbWEsxsl;1y!^roYw`kVg zsw;^MRYgijP#IL$6nVQHg*qf&;P1DbYS|?0uS%OAb+inrvg0XwM}>JB_3P6E+t{tJ z&(`js{{T69Fxh-@$E)6!OT5?RuSTitaM|81sk@6V1%apa5(%%c z(-B(AgqW&`T--55!*BV~{{UG{DW&C|KyYKS*Q6#8OoKTpI&K?BeOZ>2y8X&4Z$xP& zCB#T$#3%u+a>ti%cKLE^>@V@RLGH$e8lopE zoS5_%y%^yisb<4Uo_Qfeh zdhJR#7oVp_m)zU?k4pMMy!^K0?Z1Xq0%kGHRs+=U&Xeh6sQe3v&L}8@FNIp)!v)?P~^I zsY0aHu~nll<`}2nu==)rsIu8Fa$Go_$NaN+^lj?HjycB5t#;j|c#m+q!bYL)!`9r*f3X1`Ub;(LrL+1f|KUy7M6 z(1id&9?^|ACY3W1X=sR3l32JZ%Q{WELySs_f~JdWDOG7-!xOM{^f*iE8`Ot5Z42bP zD;vgaXvrtaY0#Arnw-HS*^I_~q^3hF@{0Bx^xj;#a^8}cBX-AT?I~F5Ckb;6*N69&w*9xJt+)tzw-k`NloY5cqYk%sk<`#_*&5QPNWD^Vj@$J; z$79s@6-R7u7!0;KcWuT17t)jfE)BMy%sM$vqdXszC|vbKvC79(IQuCRpg6YNNlt@6 zs&-maFChDUwhAj6bEJ9Hl@nIV5~m-{q#3g{o2QMXmOR^J3UH$ zd{@bJehk`dJ?6T9FPZDM{{R#e-&|aXc6Yf4j;^P8@uHRz(0jLCaj-|9Y6P{VRLZo$Ne`5&6K+hH zZ?`HV>x%m(t-6I2sApV?XmKY_65^^u)RZ2BnLF8wg3{jRhv$`l_+B=aq>QOmgW+G> zLyGse(^!Qq!>Oc^TLbdna1|v>Nj*(QG)*LV({o5m zi2?ouC;OJP3+yV^n|>=KeIR`-CYXkhn+`-n)L7X;=3m@>(bPI^3xk0 zFybm){Y>0pS>D+EH@2AKU7k`<=g5-f(rQLpzvVXxJXu|v{YPONzAQE>Q8QXBK5Tx(P&pWQ;9X5=7wXTmV#5LWR}1K z?WQOtYO<>q5+fbOe{xQ=NtmY4vyy8}j(94?jRb6BuI|)zXl-R4mhvE{Z)h4#R}`aFMj^)euxyk?1ihwm_mH$$e`TQJ_~tf@Ff?gy5i7 z3PmaXSbB9IDCik-V~rz70Aa}pp;0s~*$$;TiBis4_u#%&LQNux0>QUSYi;=oD@wCc zDbw%8K4wL9Kv(YbG3ww%V@6U^+08`s#d#2!Ar7dQf^@VAs=NKzBtVbv!XTqaqQ_N` zMipak3ScfmmY00OHS{+NJC{( zytarQ1m#b(V>d0blBsg9hoHL(;C)Z3#e#Z< z&-G$eAWOF(yhX%qtD@2iu|!tSRl2k`JO2PN!{`U|QBRFRj_vr3RWr8)DB0$ex#p;S zh{I(C;>n?vC&Dd-{#RwNKg3?&nxWM!$0;9r&N+1aSWiAf;HU}vw#JK3mas{lq4}PF zLQmL=;r(k~M1L)nE;2~mZ?8;wlc+^Jf=B(3)fe(KWJ*_)9?}6gZ5t$Wj6;puE=~m~M^aKcsajG$=^S#mA}5z^c$xjFX%&834Eqa! zk@hCIbGAKSPl7c0YkD;W&enT0Ae#NAm`+&viPW3$Gh13R2zle)NI?Facce;Oen#It z4Z3+R!{i&F57uOqrrd{`&%lG_wvwpa(XEhfKt2NCnJRxSb<9Ji}-hAsB zoalYHeTy<$Bby7cdxRYYh7!>q$yC%}r-wJijSJqcGHwA4x(jj7!~%|gb|j7~Lb(L( zHalEL(#8#VIPM2moDNPEiNFMxKM?P(g>f9hJtZ^P|%&?Uu zq>>U9@l)H2D9f1#T9sCyG2qI0YY6a<%yRxXR*s#-g*bM&&N>9y&BbUD<|?|CPhc2^ zRU>zNiQi?nw;{$VfhltcBCFG&6?bQf?28hZCFSNy(C={KP`^Z#Qii$ZfZiOuhg-Rz zaqZBRNb_RJSC^G4doet0RBgvWHh%Eesh=n#SH|^oG{U{T_z`jqrS6v_x|bPr>rtSl zc$4J3&bJ}Y4|2|MdD5n-2~ZS3s3!x@78T$TZzY>tw;VSc^4be=WO#{JTJ^)8BTA$b zxlxGuT*;uI7Sf^)kgD__(}|Q_2wsEQ(E1`BE;^P``HB0ff$w8gkGC=4o1l1UjVDqE z!<_)Y^2YWBTe+x7`|6o0YD&Yy@}|8q!|!eJOF{1YWJ$Pe3f&SEBS%vrhuTT@;GBM2 zaRqg$7~Xar{{U=KO~NjSZW=bSbE(dU7B=RlSX;;!tUt6ihSe!$phj)esl=BAxx_Vd zhiiGn{E*qLhR|4wYK-+Op&r}~Iww{Y2u@CsRcBN_p8o(!4_LoY zi^Z~E5AoyHJ|kP@yXQad7G8z6BDCE+&z4fcT%PFcO8}sng5V;Ur`&giZF1YzU)Ngs ze){-$o6U!`qjHnTbdTGIpNHE+-2VWeub|hXZ$%cUSN-K^M?Z4{f?k=wuTk49w&itb zi40M;TS@3CQ`$+X#%fz7+^dRLomY|QGTy9U!;>B=9^V#rZH>pZ_Dm!~+ZG6$b~7L) zG2<}PDHQGq0-t6z8-B@;X(t$@%%*<*e*5*wwe2H)!OCt)adnVXqf(#=9dV?$k4^}w zcPXh9U3m;0U#d^HQNb${*f-_~-s{MX4QcZanks8kOjujNR4eahNQ~}#{!7}2pH8%) z&bYI+fRSJGgC=I~dDRf(#-wFcR|eX~f*UNV=pZ|}8YfGS5D(Ze_i7{)$Py&(Xo=ND z`G=x27@9DV8@itXw=Z)K$mm6T3BfSOlN;2a)1SK093M7CN$ssLRB)>J5Id=Oj7y&+ z$6+f#kx~bLOigPBz5rPnkRw~SBnJwqBg5~mEfyEBuX%)VbKl-!RV35x!8S1pyV*{o zB6jpzimLT$DT@eVYrqQTTN)F$f*K1edY!7z1bnkZ3h_F6ee}`lBh_5g{{X^mnB!YN z%`MzHF6dVZ!5c{dVK1pAQWgi~Whynuk&Mpkzu3iEI>@xb+3(+eNbfJFZ>A@wt`TMq zW44uJ|f(Api?V&xHU~89XSlY6x8R>{`PN6+8cD^6C0bIMF>u_y{ zeAFNUavk^r?bS^fTr5ex1Q+TK^%B>{_Z!2nC8J@PYn+9tRHw?^m~iLqO-2UlYFXk~ zla8eI7kpY*4T%ewm6aN#g#%D=ExXm?1#=OmKwP!zM`!WEM7zZ1nJ%a$EGfhSGXkeP zMLgI;#I?lQCvrRp_k8m|`LAfsWrq@COO70D16@f{Di2?7EAukck!QE<{VD6j;m51X z;cC$b$*9)p%PN}U9DX$n&iF`Qg|88%z(imGeYZh)pNN7%rVP4P*_955?fGH5OHCzI zNOKLy~kj|Jj+N~uj`@g6TZts1@u#G+c0Mfs`9EaqesE4he zQ*!3T(1ueHuyG)OKv(mEvreO%8A84Gc1&7P~F#)GX*I? zzv;xWola$B+TW#fx(>Df0PV0u_#jopS-cvw@%9sk+q}FEk$(Q3lTZ0ml#&g6yR2iU zxGEAqrvdJdmY(9zZk227C(dYjAF$6-oB*W`bvfmpDclQd2O&95^%M4=l1!BO%2GA> zk}2ZV+wR52*=@>YR^v#{Wx@wkJ5_Xg#+&y34S-7=yo&1Y-OK>b59 z>|-Uw3e>5Pzjg(dI_IpKU$^b|lS<->qSyZbA7!OZhWdH>8t(^`)#KD3IxlWYlmLHZ z?50sA@SSB^9J<_)5mG1*l*l8oN3ITcY^dXvC*Iq94iRlB< zBTU_`n;yltZ)i%7`QrNT@0*rE_{kD3Md$N5gtm|eWzbFY;TTE4X(=i8jebk@nOC7?86R2}==t zPE>z6Rz7pzd)sr~E>i5|+y2$V-9zpk)A}pg-K~zL8*q;*)>5bOKHD4ty)WiGiH*0k z?QeLL{IEmXRh>~82U-6BXr~&!(fUQa?$h`jBj+#W{{Vv<^$Y&hcD}<08&X$hyd%J| zJ8OaX^K7lTQsOzfZmp0}GT>5JLc7r!uLrwXRz9HZfBN5piAiKjsQkPSfqtX^0Nkzj zcTU}p$%nIvExRl>mf1;9l+YeqC`wyxAan^r00Zn8_uZ>t#>9^;xTvpT@&@(XdE12f z@7$E%wS|=}$37H*2ic3I6u1c~t=K#4jol)=q;Wq}>s0)9DTm9KngX{a=$>`kQ4$c9ObxVz)oIAfxHjvcpdHxG!uCd1uYqvYxE^=?QTEO@m=w@2@oc zuuy~1EAIAUyEl5bX;Bw=9lvt#szmu> zL?*4oY!sja*rVHxh8EKpqZ^UCT)4|8$6OD6-16hYv)SC5>dI25Qi>%N@2)N*VIUb> zl=vN4;cr0pdmV;%B=YsnCd=5{X@fHV0K2Zz8H;U((_SShOL(TDgR;02FC|An>|6;w z3$<;v!nEuqZw-}qjp={W^GQ-4^+CLCuQhhQknV*iYFH(ur{4-nRpm;3A+zC966#HH zkI&Dm<%`$t_WowC3vT6)tL^stQ0K2IeL}dKfzf{3-jX)c@h{gjI>zbsHMzB1`44if z)hWd|hvZa5XsK%Q+dxWNNupAbL5S>Zx9>``BhKD!+0%`rC-UXqRo?l0mPOTd zop_%i`BL6eje5FNwUb<_mNi(rWveQNPbYG_dF#M*ZC*ByW+<8%A^K`n+DJmA6%m@7 zLb6A*slgYo(}4|)TJav1>m}oJZaZGc*ftxE{NLM=X==x`t*kjd=Q+YcbtNbs6eOr? zab+!_X0;?|fikYbHr(zyBJ&dlcN$PdoSVcP4e6=*61`B~$1!s)x64(((w^11AX;Eb z3J!s~@67xlctYXCs)!^j%t^};mE1~E|UZ2mFZo?13n2yM8iv+V0DOb}Y~pfi)spd@~?#cHUkbk>-vwTXJ;tAB5|?=Pi( z#idJXp5Gt0+0;$r?l8J;D~ndEMX}4p;q?F&q?S=rnNo#%V@rp$N>5WKxo@zuA8FRU zEcBPhoEf!K%zmP`1Zxy4azo7`SxOR3GD|G0#J3e%3Jhtnu_0w1f0el8WlxqqC2{AU zUK_8JQzMLhIoSXiK<2;Q*c^WNF{EA6f{V?;l63e=Gi53r-Y<3m&d=9FO|W*8eyQrn zqldjTuIF%stK3(273yNaV#tuSjcZn-T~c#Vu4g}fGt*PMBhWeQ@=GD<1MZe!Tm(u>pP3Lt?Y8ssyEw)CI6*_a}HA)$5XPr9Y zY^}o8mF*(L!@G6Yz~R5Gk6!NSZhrNcl(qt$**sYl#HkFVp9{0|@h*U5MFum<)!dz5 z0`^%J@z*NidFAD9WaKV<+>Xt?%W>8<0}{~D{{Rags01W-)4vyZxRA1{%-&8+@+Cxd zOX>6Us6_{Ovz6xGITC8ziGJU1wBk~WZrNc#DQ(DATQd_T@rh_U)U} zx1}ySW7C(SZa-6z`0Iph$x9C1(3d% z{C)m>I92)#d2!;cT2+(c{{Vvfe-`VfrSC|W{z$*YxZa?-GGu}s4XiALcIt4@;;Xv* z(%vn$lcF`9ui4J?PD-rwpXs;LROo88Z9X~L>)czZ(uBPwERU6lk|?JgTD&O(pvTJF z-tm>W-{pl`J@;?H!`m$L+-Wp=A@sNC>u>QZQTI;palOU9+yTPss$)Z0AMTQ_svR|B z)w?ILql~(jRbs3nLbN3Q2pcQ))%baGxUSUQui+Cn%up^Ew>T2HoI;o&yxO} ziAXgKq~@h#(w)au3u>(Mj2*YA`R+PJ9t?e8`o!O!r}-ZHytlRSb6Jw=n~Mz!DQ&hw zijczy%q1zxr?(r<-RYJfJ}M=e>>pG-cPcb7f#7T@BYFtPmY#%hwO*99d(h!Fv!vgNosB?IkI z$YULUCAVJ#TQv-OT+HgnuHRLBU-a1EljyJ01;MMIGVVyl&ST_;){mNur&`jrIN~cu zoUWFeuj!-PfHvG4n}y#fE-B^8%k zQ7ax|i7QoeDm5mju02vVA!;fyR7l9(y*skzY`GjAH*9)R;MA` zlRHf}7U_+t5^k$)LaK*uAkkDHtP(3k*DJhbO8wPh-;Wl&3)~w~w{6^?1aNnech>&^ z=Pp6rd&_X#C2qanx@Ny{+;@iwThisetx9m$paa5`^h!VjA&qX|hiekbkCBDmGVbNw zs^R2D7aK+EZ%Ij?73fb_Q)OvVb$gF)54DpEiioVai#uyl-f-|m({D90TxP|=wOsUUEB>YNoYW^n$a}~V0gEYcYgq3*Iu7u(nlkz5%NegN?TT*Ii?8TnlCsLa+ z>RVlm;VrdX>q;DY76fY)Wp@Jcaf^W-NUy{}BO1Fls~Q;@$@3Kd0Gb`AEZwV-)`Fv6 z;f!wJ{{S+EqiR)&W;luDn5-VqQgJn)wmv;mIH$N$QbLZ8nTN(xcZWag|}{y zp~KRgC<00Qzhn{TWVgK5nUv7#y@J(>K2@Fh%(X7s^oJd3){4N=LxlF6v&E&`l$8+7E3NS+FI4jG z8!Obs+1qWw*l1;_FEY{`SV0Rxs41-orYZM)Xv#b_E+cuxq&4QoXv>Rp2`NR(O57;$ zfTbFeJE@FC##-1^Js15bq}t={{mW^!?k8@V(wC=OUkR%&{{T8Eoe0Yn*iZ9Kb|u82 zL$9wPM1xNJXi+WJCryqWVaC**T9QXjpo(DZZ5RVNX`+y~frp6Z;ia7i_Y|I`>wM`kR#LS{iaYcyP z^W61)^wDz8fAKD2^anMxBoK!t&*N*kSCm0DS#Ao&4|kQ^aq|A>Y3yCi)!}OY0KSR% z`7dvc6FvSDTb|I8)?dYazT{Po4BryA)zzg!bhPXjQ>8$TiB?}`K9$+8(N%C~gYGd( zPCh55ZUN+YoP)A0QvCF;N?Aa7R-~v39C{VESf*pHDl$slTXo6Ssi}_7p5bjoGzhA) z%AVYGth9jWk1@Ws-MpuKU96W&Y?}+PURTTf#CIDG4w{g)qy-Og#$z^3%TZ^Kq0OoA zCIYlOM@>(SBLc$m@BqDwF|wvE(G;T2MnFAdbJ65S3DhocKzVJT##1!qDUubpn}vp8P=xI*(IgbLvZkzTw&x&DFafqC-VOQp~QVLddvb+T9NsH?jn`JHl))Tb_ty#Phe1%e-gkv>_x#VrqR`PX+guj`o z&Dn7`NcFK@E^%@GMRt;8#C`a4S#lJ(t5Z_px`j6DP9V`qQd9v1cN}_7J=olWlwDIi z+V!iJrsFk6ybV^and$s-GDI?!LN2vh_G5_JB46B`SqV>75C8(F@WIycs`N2^IIbku zvACM)dr$7SSZ;i#khY@&5LMLvqLx5HY1EU8HMhy6<)Q|bLg0x5aqey`+b~f9xoQhUNn;vv)$k%)pE2%f(Ok9dXsmJW-B6;S+me>w zaX{6q-Nf6*W@`eL!_z0t5VjZ)U8IjoEr;i6GqhLCpmwZ zuv=h=Y^x%M@+2O@dvLsj-z62tR!UnzEv_;oI_NFssU-NZQI!o!o63?N*A2+TMt)ub z0^OxG_Zr{@VOLRuJ;<_VEXbJoN_vEoU*ppULJ4p>qk7>bEEOf6)IEyFT8&uvMx%Af zvXo6#QG%tH8y|&>FmS|5?4?ou@|SuK%y}2W`;)K(>2GltyhL zw5aF?8wqnVN^=oC!~n8M8htb|@i88tKpgPw>TF zL=iH!Ip9sd&U7LTu0zP_P=XWpkwJ=k)3lzfe(4>cTH(3bHPeE ze=2%oPq*&(ZM|d#c~{|7DMuit?X@lrHy+6f(-4v)t%kylJ1SL=R@?HUaurUb81`dw zRF!H)FK$cY;Dub>N)O~hiW8sul)=tyv5pO;;3qBlax-z)9By+tFGbS5}AE@JK_7#f$7Rw&@)TV9{QyN`f&8(C+2$%zHSPCySLZ7TZI1r3@vqH)P1-? z=fa(s;T=zobpx(ARAe-;lC376&4oxuBn5Jv`JpIsNJ2@VsU4>gN;C)R3Qu2S!U}Dwyy9}# z*9fs&CyeMIFLd0Uc%g>$_fxyxaIVUa6S>%SF6ofc)AI{fp^+8*F$}Fmimownz115{ zQi{Bh-I+M9xrY`JZsu1Ro%A&d9iv=QHs08UOy8*>08FDnA0LwfPV%gAT zW86|@+-5Enw$NB9J~EUNr5^K^E^S9Zn|!=m8FS!WE#W46Ymer9AxASzP0GS8+=R43 zVirQO6(HNdlrsZ)Ph3i2-Qx<-F!?)&jPZRkRok#T>@ z(uG!?D_k>|S_-Z(xR)tJlCrO1!3{JrPNK9|lqi}dI=bR3p!H(Un6}xXRa*9_;Alrv zt=tggzbd6kT2FZd;MxQNMsib%LYBQjJwQF)>_DmHJATS$J`ZgFkRtcDWO`UR0w zh#HYJUzr$kS($iwhm3*bfIvq<)KHOv@mr3d?IcCfZ0-jFNiM7Mj?5F(8q;Li!G51P zpVW5rkH@?Z#`j)S-%?bRuWwpeF9>a{(Jst=%`8T2(9uqzM@;EA1v;eDMXn5nk+H_+w4jpIEL=B)OAi$NX-E#Z@mQ2 z;uW&w<~k?Jy!?Ns+gck-8=fB98o1WyzuETUcd!2dgSVEVMz`#rPdpJ9XzUvU4aD1a zXm<1nkz=wnB?xV;4S)db#+L`OtXYaql{0yL*O7izLOtW2KBD`bK5Q+46_(r;MMF#q z9^tJ=7Pj_`MeHlC?zu0B)pL0LM_eioaU2`VdDTd{vWoqOrV05>Kqrxt;>4-Elk4`^ z8R=IaNiuG6BBxxLu&!`jN)Vo;>UFcz1=+*Ko1%0hd$s5l-9J+;nQFLAwDMOSZVjU{ z-sh8YkjznWVGaXD3c^!_(DgXFeX`tZ76QArJk*jU9l7g2)2AZ5%zF!ngqF6z|ve<6Nh;6On&CM!F?_XG#987^ZxzS$Dx7l?LwxR4OBw>#ZXq8@z)MVUu z6o!HQLr%~SW(tlm61>pQm%Px~CB@X9gq(D78)S0JONi8)$PA#Ud6YiGgRNjPH=2e$ z*O7vC6u(37#kGtAvgUTAWqIUxmZham9V+eBmMmkp`v7ikHSr^>hWA^V^hmGveV8)N z5?o5V*{-}sOzqzCwPrt zoGr2EZ=J%mBurFvltF@pAMYm1a*ano8P#T_d?4Tr%XhJgy=*fVZ@Yd}iah}OT>3%r zuN|h}zCBLfndRN&kcV5?OzUfFTihN!E+pw(b{?`GG4U9Gy0QY78{=+&WKQd%l>ilvfs&!OzaPt3VU(AeAPwJ6uBzFEEReih`q za}|oO+9oZw(xY0kk2y4_wK$gC%P8Z;A@R3)R`WBAHsq^%^vHJG5#&Cl9-U^8 zcid1K!%kcdPWq+wE5ckMcagjOP4eZs!En2%i#?V^l|3nKHYx)V8P>8zGdgiY(Z7*< z(3>tkcN-4Z<}|D3=v`ha=6$uv{AaTFp2)kmdfD5u*BV~ly0l45h_5mf+J|*VVaA18 zaf(vnXCZw`by}uU4_dR+9?NH1lvAnj4LiyS75lN)Wm$yfYXYBf7a^w0xF9g&grZWC zm64e1gD$NKTMCmq3$<8c}C&E+yl&9&1tbUx$eAa zx2{*Ke&2cJFx0mouwM-+#3e-`RHZ;TpB~Iy(vzYSHg_X!xGKLR$NvBV%fHB?IKr_M};e!JI`>O^HNfijVv&%L3#bY-Xi-nQpBqub6413U+0LyOGG3Ef=^J7;bZ{ z-B-4+_HZx8)%5$YwtFlH)~4jVfloDQ28NxT*sQyI#-@WG79%&BEpTA%yNqbjP{V6w zK%pQ3N*?S-Zu4?Ob8SUH=AOr+cRBta3X?(IP@-|6$oNr{#dMpX@w+aqtkc;{18oFO zVy_1|r=57X^w)4Gr@eLUs9Hm4T%#>YA zq3B0odZpa4H-tVK;2p=lAnylaI~R9|qTgYTgru}wT>F}sV2;z}sb-xLO$Hyh;B%#_ zR9|ib_Wb1;G?Qt+FK4!u`;NhEucW^-@`DY(>MNXJ>`QCnvi45Sj&9c>RqCO+T#|^% z%OV7Idk$C%7;s!wedXs_%i+`Q_D4f_lj-s3htl*|+l`(qv75{j=A)7KgNVJ|%bGMP zZ8>!+OGJWvET9g!q~816Zrh;U>#>URukbY;v-HbE2Z^z7kZy#6uPqx$` z?C3K0yYT{$-a_ANLX(gPQ;8}m?$F&kvGbR7f7uHbn~oj6cjoj**~T`*E0SN`E(B3x+WWVDNOona4ExNLpVzv$rxcp@>yaSSF5eb0_#L~t z?pHr5+^$#0`ktA)_xEge)u$QVg}Q%gr|v}aw|zUHM?>4z@KpPhKet=xs#iZ7?+nLh zg+FdR{{Xaq>qcfpCzUQp`6J`csXzC>6B)|Z+itS|08{=1>tWklV`;P_?b|ChqyPbs z6XwZL0+mzl>RCDWt6juhc=1g5&yQ~(3pTu&%ehpJ5c zHR2-@fM}eG+3b&CawLr&E;=#gK=C-3{Ae}-DySbDIYt1 zWSrQWPU*CLHtgFkrXD_JE+EOZ`3~P?ic?e5+(kl^I_cC;^J1P$BO%dfi;hbcJ}p`1 zchr~E7kcigms$5c?K(oHDE!UNQ?E*&C2Azq1?4E`Uek-cuVkb1WnS&P#u3v)mN$-C zzi<2U+cKVN6Qr%;K&WXxSc07+4= zPRv$V$5pJfMWmeNq%~nHXBO`L-8B>Lsr|kwr;WgMQ@AG*-tG*05#@%glFw@7U!87f ziOR*qcAGL9b?@;{qAE(~-3A;=+%9}38TxU!+~voQ64>PVj%m=4lBF8|0L$Nv633z{;N>QD`_tFccXsZf7JdE#tybjG zZ@Tt|nnP(Ciq^L>0AXB|39RDD$hn(qaPUVP-Y$H5>Az)(4L{~UB#Fk_^v3T+u z@}Iai>(P*YX7??|(uC8oI?WP0fWULCn02^PzQV$7z6rZb+SZO#^*6m;^ryj>pr^osuM^B{Z*=r1-Io+-D$yim_Z|7wxig@g1?c;Ia>d>&QEbJyYZ{(>f`(dcGzwDIa-bO0+S(Ph zR$=qIp01JAmeY|r{{Yh`p*usHLyp>o%)I>HhEq|%U82(`w8?z{g(*MCDo!PNakru? zv3A?}vrbw><_75A9N+58cDcN{LvTGgfV#wFg|%#il70xR0zyCrfaZJ7GnspXZ1s(& zwyjuIXu8}n>GljeEX~C!_}f2YZS;liu1dNR#CA}TQWB%_EcPWC_uxp{mB&_1ox8%y zW{+d)JmOF>aUW@));FUa-rWi_%oJR;nbE;yp>v2B_CUzUB25Q_x4I4@P$DCAtEn zT2ldr03o2C67^>~1m#(=jUal8E^B4K!l4kjhnd(-kcC{4g z2w}5Okbr0a&%EFZ7&5ym$g#-Wqis&3!*pv;=RZa7xr!VrX| z0FqRq5`+LrBmfCGzKkPk25xed>D2T|>7VJZ!QPK}FK&9+`n1M>Xzabh-_H7c^fs2% z#`xr^Euc9a%hMW?xOU~5DRGt(bY~$+NmE$FZ=2-haYDm<$yUA#{{S9-E{S=)XK(FY zX|*J|I5zwjjeY)J3sX6i(3smEI_KPkoBEo4&I+8aLD(o0OY z6`d`m$AexK$Wj81VIe6b9QHmnoM^et)=%;Gc+r0MtME&GZqGO6S#Ouc_`iSh{)B_+ z8_|vXk*{#oZMA&j3Wv^a84Z*&jL9V?od6iXcW-(|)Ly5f_UCG+M$zhn((j>&aqNvk zhCgE2ut@#$cskij^WI6K+m2OArz%us*^dFtzmd9K*GSa$51hx_t#Zd%oz>yKA=^6+ z%GIB0*c-IMP(t035IpH6AP+Is5nB5(>s_XE@=h(PX=DGy=CwJ z08xAT_q?~2@pmhY)k;E|WlIb+*jIsu8zkvMeVLqZHVucN9%<^?n(iI@sQOm)iXna^ z<2#n}eC~b1m0PS)srrN(m8YT*71a0B7dg4=3X6yy&$Y_z>i8WW;JzB(T+zO^?-p@x z+;Z)EygG*BveFghFwH<*hsXy*Xc>}mkiR!KR&FONIJWBeoy_NsX5x>eXQ#(FTZpB_ z-9D>10w15ew{n6OCBS+n#OSxOG10#{1u1YOrI zae`3znPGvBpQ>MPx7);^{7)zkGBC`cZ_a28$V%|uvj@m#fKZItqsr}LsXa0(CO7jr2x*jl5(OMZV9ma+Dm~6HA8+BdxjvkOK^B?u|=Q*OvN#v z+kPU0JBe#HI*yie$W!_;=omi354ikAC8$bQf zV7s8Wbg62+dbAnm2s4sAH!DmP1%IokALD_u6<^#~IZ|YMx2`fJ*&?mArPV`HH8!E1 zFnLr{p?_sM9e4B{>dMc>Y44ZBlg) z#M~5pGCd_)F1BsaX0~n%1lF#WYs)B+Yq+qWw%ujHsVYerx5rGhxptwKV&z(Wm?s^a z$}`P&ML^sa7Z8uom;=qz+wR5oG^&9a#-CbS9alZFyWDu;PHp#-A*TTG7E}sc?&uCC zxT>k#|HOp(rxqG*Add7prqD-<A6@B@% ze7;>`y)xA3zoULva>sH-hE$I&hg6c#CW#eLAC5h8c8?+3ABDO0{447YB!#mwJ8uI}dsYb_W-Icsl+h}>a%Y`>ocu>O z_Y0kt!y{(Apj4Tbr;3;a=1Q1yqpx-|IXizNst&Z&v}Yl?Theau*@k4jsdtOLt?>!- zBOr+L&^@gzja2qxI~hE#fO%Vgkx(Q#GT$k2rp8f#^ZSt+~cUUsiUX z4hKs86?&^1B5T}#zu8UukGC=mTjja+RRx!UY^I8+=yEtG8u#|IU5mMOVL>0(Po?L$ zQ*&E)md0^+-Wud2s%FOA2=S$Ypj=!hl$z1TtCe$7bL=>V{fauS0GZYr)f8Fj(%T9` z$C!$cK@}pt{0OXh6tTT6QyWs&jOv_^Of}<>U1M@WR@GF5>rHbwe66$tEdq#=m6{x> zfgx#(>KeL{n5MD)7*#etqGRzxiBiD{T1H*x4@#aO?xK6EbtaV6-VPq?Q4^yJsZF>j zp`i92xahqFcLe7cbw3EG>!@cz?ZMDXfd^119dQ*;;BhBS5meksM`_GqdT5WTWl|AB z7vioZf^lR`1Vp7rd8sg-Tvy|erk!m?D@uW=DN;xvj`4?N;==SfWU8269(~IbQ-f)u zVsCnE-dkylPb=kFrT?XmYx!MHUCaX|=;H9lpQ`5j^S7Zwg-*w3TFphr=W9itkp zr+cxNA4}K8c%u*8I|fb^=_OeBHNlNPF$1Z^$wky=t>5^UjF6;TPyATjEhpO#c9mDjF)>s3y*o zjVc9HNbL4vYiZD8okm>QY!Y;eAHxM(wij+KjrXJyPMi@y=pdH5nyG&ATrWtPfG)T* z3C_6clSU`ZsHthovk|@obCBtaC1?N>k?Dvf8kILyS-I5m!q!TXPDGrr$wr8EXgh4p zxRN}i6{@*})Str;Tp^RxeI^d!xFiNG&bWX+BAp&XNUaIO+iuK}u57F$l-EjZQVc}CI)i3~QU57j3jFTQQrB<5dumP$+vV zk57wjR5u!NlHw$n->z5+x=az68mX{f;>z7l&eV`CXa8fv37N|IkH zQ{go^fO=rH8WPh%QZ(nn%0q?1q2ec{G0}|yniUz8TO~!@{g~*+he}ly*|;{s7N!27 zR%s;EjIhq0qeK~3XOy#~(Dc>QEKACcNKuD!3>Mx~x~M_ZT(JI*f(q)~QC6Tu6tCtr z%MtXBp`YCsBPPufMq+>;!x3DC9;X^1QFW7mI9m|1qPdbTr>qnWhnCLsl(La z)-jWHW0RoPdvZyh@EXbpJpk*Uvlb5?D3&Rb4Y9}mTT;fbQ_%L*Vyg0^C$@;Ebl&cQ zSIgU)Qw3EX6nKgDQxjU##C@p6@e!kOzWUrL`NtVjg8l_Q;RO4uiGF4g@eGCdE(mE% zuurnwL_4cqL2USsP-)$QEzL`TEUqTB;yfeFZGKyAFIc8W6Q;NoHz37KND9pxke0$y zuC?#>;A#(x481Qv)Yt$HrxG}`r58{J?`<}JQB?24x=c=tg$FK?-_lV2WMrK1=Vgp= zXxT3*Rnpnh*rU@C$}||ALQY(y#$7rqumeF)vka(BxacUryr`4sk^O_$5zaLxxQDf_ zVqTymxnIgX{@e?4(-IOL(IKs57L(+G@mH;W%n0Db<`<*FU!1kOBCSvawa`}yDA1Y` zs2lc?aGeg?%y+IAE0V(&n@m<+l`><>1wp9s*h<`W!$U+NB%Z;CTTCwAJCo4wcc34j zCoc1lwDMQ07*qHoh^8Pt4`DXq2K2q4okbWBsH|H z>q7jMkZJP{gQc-vqp!0EV(n6s;0Jp)DRDTj&R)Lx)1A!QW!H2&w02~LA1h|s98^|x zS0uXgdR%iZxzigw9kSebqcxSACQ>`S`Uq{rsDx_xhktL|izNy&o>v~|biAYL~J@mxz;6oxbvvRio0OJdTcTP0qOP<+V2Cc$sO5K90b}l@PXdw3hF*A+1 zDl|IQUCVDqTrMd3pDp>ZcE37VSb&#QmfGLeywY6@5bGYa;W^PJ?Ldho}kZXbfCN^Z>O-)>(&bjsx zf#}F&(1#x5cD>y#+kb6aWZ$p%mr})^$q9#LOqTkNO|r`gQr4r~0mQfO!qhpY=#e#? zdEJ#Gt2;^QGwAQW_rbRBuU8j29CfxFqp|pU-tq?h?6*>K+oU!J; z!{3co7C#epvi7}SHPg@zz}dW8!23cT#o}$3ZrGbr+@H|3KxHlKSOA|uV80YDJiONH zit44Tl22|t>h0^7-!$87+T-I!&XM3JUU!tqrAYI1H`7gfF&9KHos1W!g{-Mc)TMiI zB2;5jGKmGf?-t>6reUf#X5!53km)pt{zyhq5lE!EBbv@O>=^oyH& zLy(gS&k5(0;vqs_*|t80T1t%&jcZz{J4wcR*T*k^@J6#9sh&dOQ?mT7a)BN>k&@Ps3oXm(zASee~w}K4~IuVJb3Xi<$8lRn4 zEOKV@<~{L!bT8!Rgd)D%(X+qM3C zu3kTH!1hD*IN#E@KcU@^YPiiZq-@Kz>e7u$B%(ZdYITHX&<553#V+-w=KSbg_Ux;U zmz7uG=vZH0vV%JcH0Ja2WwTuAKx#2li6zWmW=*IZ%j0X&k$$$Mol6d*NU1gP4wS{? zQE@0#t2B4-j^jNWXIlfB!CCFV%H>&Mb$(ks09jkr1{v-0n?LjhH+NpF^1F}JT2DzqulUfG)HNyFMExe6 zG_93jOt(Z@VI3!wd_ZdK=}b{hRWGfTi=I`Y%`|k@(z5p?;L9HpE3^Ismzz0yC)`!5 zuv6_c#-}6UMjIJ%9XA!Il?_=Q&$9v|Ym8CinW=%y{9h?b4L04~in@9Vf`UIB6LoqY z+r=G^bJV@(Te&}e?b}VNdS1CB%_&k+*jA&SU6`clwq3$Xr$1wj*~|BLwwDoX>$fLv zX$wnc(PFpBxh<0F%V{qzGE|0BUaE0P13dD<_N+;9(R)E|U5i?3_2KOO-+9olPCr6V zQycY7Zhmjxwyp=XTs*fGx0*=4?oA6NOG27uNsPk4r8PL`Zdqc%xN_;JG;G|H^8Nn+ z%l0$3+!yqZ^kKlU;NNc?c%D(+Hn4{mzqh=U+>nQvSC}4V8=4_7phZT8>L@#gDtA5G z8xkDXZYVE(Kk{mJUeL_XxT9j+u1not=)U60?=MmuFUN74wOel==9lgDLlz4<8W7nD z$RsRzY$vDX2OmH8PyLA5@f(eb-IDU*`0)Lw)%}7008TrPbEL8ovj^r>E${0)k#8$? zotnbweC^!-0A4q(!L@Em%zd1HALxCv*kAPLxAwTH z@gdqK=9;w@ZK(~zMrE~J2 z{{SDi;(XuVU-vg;+grDb9<0}ofnc`R5pnj`X}>1Yo3y6-%$~2AB!f^xWCSPd82T5s zf9tN%h}7?{to^@g9~}4J{k`r@*08faowwk$BxOu(wi{u(G}AdX9Ri3Re21)+W0l*Gfe`AB5%877=tP?*oP7eSae7iaGR~NH zIPRc_D(=^u%w~s~B1+QSSgnHN&n%h*p$Mv!qIv);gP@%_6?d*hGU;(Q)A!VeEkj1< z-4@846UK!B={xIoyYeb5X|A>*5*}7x_MTYw;CBeFIuZQ(;T2Xnw?8a>F8vRs<)PU= zw)ukAs{HJw#zxwMDZYR-Kf@HIU8Fj%ZYr}k%Qm9d77>TEtT;1*wRj&f~8*I|P9%WwjE9y%6EiX#-p~(LL z zSs4ue;BQE1h*M_qZtC6+w6@`y3+l9}2H0x9%%ea6#mY$(PVBB~%Nqx4ao-$A4&NR| z?cB|#qTa%Rgu=&p%UMYs*mpbe3rP;`kG1)O)KR?2*)d|+c;9K){M*Xcm(gh1))tVW z94}0JCtN^D{T4#kQA4J+#%p!%EPOXdte=A7Y&I^}$KaA5GJf7t`H= zpH%$(C>r%Qx-H;st-UMZQnudTk*O|0vI4 zhK4Vxk(Y9w1uFG5^(Niip$S|q>2q$q!#q%(aiz?NvNsL&zkxn`&G?vYLp=yOk7*q- zmCW4bEoyGIUek(dQq`5ejD0>_Cd-R(-CyHw&l2J+AgHd3@oI5Kx%Z4`?&Zo-q)$Z0 z!M9=+XGhM|;@?i&Ru=Z>32n16ZG~+fCWzuwK6nFCONkk_-+GlpOc{@hSE)<6?-Oc} zXE*ut)aNUDn7&4^L}^xCjcs#I)Zd+YVnA*>fYP+Z z1W{_wHT3SqUHsWxbb)t|gKpG$rNr|Y&;G1#msQ>@J4$n|CgAIezix*dS(!1@k)qCF zClljswRM*guRC+_i_oO|D~~ttHShhmvo==RxNn{Hzb7?s+51l09dhn>TZ6f!h|H%o zDuSv}AqYq-T9SZNE~{?TD%}z&Ta@0v`TKpeJvH&i2KrI@7+Gu_$LgZrX!S$&J$@=B z@pq+&b0f~$JX-9A*W_HU&m||#nwry5NudQSpm>yg^L*duk#SnJq#qaV@cy1{S4CC3 z*!y+fN>XkxTz$TD;m6O*g)38zT;0rGpuKGJ-sIB9%ELOz=8%J7IMM|oUZh2PiEfqfBZ{b7JFtAg$t;Ho3;ETuT%h0y#=F5~RcKy8U zEsX(TQdSaL`K1F>t;Gz6S&{C?nRi!q#@cB*v(dIL+NCr-Dsayq@#h!6YvWy`w}$-Q zMYjI{aTUwov;*K%gZ@X>0?>TL$anh3q zZbxs+w_BS7B&8tHZV%>HPRx53Z1%E*Q<6M`x%-ALyqte)61`=5&f`BuJYh3%-=(!b zcTZ{LY|W;J6X(s8lo}O*np;Wum5uG}o?pqdt1}6Ow>$5vdD7bZQ62%kW_r=&k5K%H zeB9f!a5;RxUe-&kC2RR;Xs?Dl39S-@jEy?u(EER4=|;2GOs4J*;_5YS){#Ut!<;uS zb*+2LdAx1=UJ`Z@aS8^cz;=jSBFg=$mm~_=o7#>9$3_-zpaE z^@GYVbzG*XrQeY{E0E1dj>%OLGS;kCNzU~8Hn{k*sx-X%_q4XSFE{*;Cp_28dyAAe zHss%%?)2@(W$pJRT`te9PCY609Yr9Xg1rF940ja~iL!Ys z)W$_r#G8YFNUGBS%1@otrVrQK*s7e8=5C3PlA}Qj+|&XlZtHk;6ZgBOg7C@3M!Bi zgp*Z|iz|}qOGo&9zn7hKxXQ`oicxyM*m6O3rOuY+K_!}bitko580n~)e96{8k6DX- z#+^GcD{TPKb3$csHuPwu>HX2?9#^$3jROTkMN$dPIZ%*y`|d#6doI8gM2Mza=^0gT zE5p;+F;#LZQ!TA7NDxBYHC07>yZYjIssqY(9ZQTX*7P#vKXIIZryK2Q29yT9rw}j6R&?;{`*AI$s3fkj;kWa3H2%-<#)|-|&2=q4$XU?RPqX-AZ-J^YyMt9O?-)K1 zTS&}|&NWzWre@@|Dn~Ywo>-c`!Vy9KwlSNc{Pz8(pKg_HmJ5j#lH4#&C;+dvn3~vF zf7L$Qg6|n3`4&U5E^(zYk`lA!t+g5uQCbo!if!tOC59cMpz!U9b8an}Hy6^dYTDAE znpYINW<72OUb>nu?ULIfG-&8W?n3kdMJT4A{n*yR(OXfA&53dCq)X11{I!GT9EmjT zrUKl*$3UD{>N)=a&a4Oc?`6K?!dpY`gn(#4(M);VYOa>qYtUD&IFiE~-)Xp&WQp;C zss8}iCk9F^3l?>q3%%>nrq{mq9PK()D$azdN4G+U_26B>QcK)eOX47%gtWf%kpi^& zh)F_vRE%J^1k!zxF7>iywbRPn)|TG8*xyRJC1F9}lSjE z78!Q_I}=jy;PiE`wMS2H1JS;h?g`wMH2xVcA2q{5u5r7!EvLLD*f$eq1?B{aR_p8{ z2r=iwkf^d6AoMM|+LA}J7WVQ~ttqYzC-*&3PkPT!B2e;Yrw>4UsJ++6lQwk80n3Kb zdW@hNtL0u5PhP-kF{s*dIqf2oHp`6aT>a&?E!(?nZY`^HKz0rO6qt{KD?s~2uygJw z7MSnU0WJraS@s#5F73H^`s&Yu=Dc3LA9AvX} zKqbajsPoo>QdEQRs-rwqZrE!5rxsgnS<|t4GhPhp_c7et^@HeZfxTbFKQ+tz4AUHG z>L@f4UXYlC3r*GVl2ikTBQx0MeKGQ{Z>j9v!*dQ=TAs1vN=YOXo~IvCZP#0hK7GrFEyj-Q@JAsk^=gQfJLd(ZB-Gc% zOld9rB`wssi$o`>ZDs`z#4zW;ViG5}3g*(INMtDv0+jTsvjCj##ckt$H#wq;LkqC+>tAcg4}RZ1Ok zTMm0t*MJ6Pj&21mOL8regOOdXW5Nl|DK#BE*tD|KRRFbd+hUO=0RsZmmBPeX@M z5x1>LM`H5i?YD1xXzoj6TicKBO04~@HmVI8*Ug6K#U0_)DLHqJ+-7%P+2?&*FOui8 z$KGys4)k!3MtOpw zQ-OBg%lQ%-)gkRb+ab&fK4je6IBnt5#GnBmuDup4+Y$yFodCmIQkGjy-`X= zSQ1#JWdvtR=GD)cWsqtTf~u;A6Gs^$6x2;W%JO71r&|k>p+XYdWd8sP;r%p;qqy5z zR7G=YU7T={4bTb6AvCYwOhr}``kkb>n+=b8Qc9MDGN0yg#3r9+C5^U5_uET_S-Lii zILQm2e~BClf`Gp;{upmaN4n1E!xmvoY{&owUfi1C>W0~%`w7BM)M><3^H&!N0V{BA zhDrH*W6}>|oLW*3*ei&Ylt~S~`{KnIJJvc%TZ>?f)enPMD>eIYE!#?h+`yDoYDzY1 z{n2p*gYxkbM~F7cDg`^uaaVFRU{P9(%kBF5`^s!w-*FW>5|5MGj$pU$4ovk=@(k-D(lskWC|(jU}NJYAS< zEl5RfRU49SkXuR78&y6MbFL(fi7=>GlMx=iIOa$|Z$8Wxv62gp5?n(T>#U&ie^F`! zszJmzv7*rx{8-SJELXP+MTXk}N;#=TSfR?tRjF01So3X3btw|0r9NTtgeISkE8WLU z0u`u_8*1Q34JO{n0aOcjO-Vk}irezI_^`NJNPIUX*4s+lVJ)PALaGu@0d{J`BN`0L z{Kd%&X(~z-Yvb-1y27(bfZZw%?u_Ge-&2wj;!2K+b;qFL%bForQd|Zx>Tp9Xk&Gax z*yE3}#~)!%1=6iK1Bn!f>#3=2U80@%YaE&d1tByhhr1jxrfa1tP)bd4!Ttqkh3938}|&oz#9n zL`P5NQvU!9B@n)&1=J6PpgpHNby3``bM3b#2}yC2<$y(01v10nfHQe$PB5g&QA*VC zN=HJu(fTPqFA9wQKwsZ z1=UN`fn2bf4Kr|4ifu%%Ua)*3m~>z|3amqdbiSP6V>755BQ&dYB=%WY7C zx3%gl_nSj{opG{Bc=R>w7`Vo;;pEnowtf9GVjLL{TI_v`Kxst;IB&bniHVcQq@*bF7aA8WK-oIBU2|YCtiz zg(>c>aEfHkL!#XU_@obkW2vn%3X`ZZRUG0}<`d-yidKFc>z+Dlx{}(o%YEq! z(eFHZz1*(E*v#AZrykkdALkBh+>e-hX4I6cO41fQb0jA+BeR`F6r^Q^wTml{Lh}O} zG1f<|Pe(j+^xyh%!(QXsRw3#e)Lf=~ySF{vVl<~?py}5h(9@{V4T4fKK-6hf5^8aK zg|s-oQmJK?y21;VG|-&+GwPXnzNPqkM{C8fMOh=}TyaiFSUsABC~>C8vZA;d?2X!T zWFcw#nDQ3Sinx1!ZCT=n)K@x6lBGWS>G3UQrC)9=?ZLKI+{kiu4*vl9U%5&JTbZ>j zQ#AAeP9XH}7?K$1XU`as^`+m?m|JaeRi<0&rkB?-^o0F^IpO~RBN5Wdmw{Y)d+OWj z50ouW?aOrNe0O7(pDtQ$AxW6(X+l0>M^Zul%`j#zHMOe%zneFqLQf>H{D~7~OL6&f zRpvhXt`)fA)M}+EN+gq0nK-+QAh;E+$pz(dn=~Q09{L~Z#JtoYsKLE*QE>|Y01;lf zYQttWSE@pH?ew=);#DK4Ctj>RZ4o(YQEX)c@+DTD90HjqTvbeUhE_l@I zZN<@-QssLJTGUsb42nAC;hi%1U0Rp!&jeZ^l(k`YU$BPA+g%Lsf>pC-L`QT zD%M5j&)fHMonDbWK6+I2SLyH3U8!<56^!maN1n)UadOq;%8`1MPsD~uPKb=xqf7*j z@=kd2P0xJE&6||n7m?~)ZpUL9G>WV9ELG!rf~A(t39U2s=YV5vBHWJVUtN%;8l3+C zrw?M#Nb@B=RZ4{a05KVL!)L(grqE>tmei#_@X`iDrg(;Tl>+SIxd6X@w?Y9;uBshQ zU(<>#t;s`ye~q;{4D0VJLbV}EDoOwu0ZdR|+@&X>qs7`|6;?o7pPnR13u+Up2|YCb z0O`OJ>PeQi;cg3o3EcP1&gSu3iz2nmlZ93YE@gQLF5Mq>Ko?2X?ZcInhIJbNRP`ZV zC4aS1f|MvIwQso{ZP8^$qJU>wp5cJpsZvXDtg3NCfjPPbo9Zj-ua0F%X_#D-e3s?4 z()v`VE_RqP+bK#CNi{+g3GYrakpBQXkGJ?Ed68`^5t8!$EbCmQhizAg{{Z~+5=u|| zOesIpi-l^2ed^A6^KI|i$SmmQ*Op}E$^Qfb{)G;vS9L31bOzoBv4 zGklIdPxtHmPRjEx$l2Vhc-ngpeZW=MYv1MsTrKH+=;hMVRPsld!jiF4oal4LR#$ms zsBIx>PCAwwj(8)}_ZY-s8z+ja)+I$0k1b3IQU|oVB&}8U*9N?YKxpGdG1Ggvw&S2M zgtFKa4WS_^0Cxd^@?A?SF-Wf-r98R5pE;@_@fkZ}@_?SXstbbxtz?kwr%P=7`Rp|L z7?kRko3H%QjkWjce)GyCwJg7-^?6*|VoF!1&w+44hqKEnK#HChN&Z^cL^v$ zkBXzerYlk}oTyZ9P#2h{)jbG!YD;QK&o75O9ghk$Rqj(oC;m6>1V3DbKZ37>V0_UWiw# zqSgC8+I{C$_zUQh>u;1MSa(kLZprE5$iu#DcXtexPQD;;$<<-a6)+QAU?<*)QcuM%-MVmmWTTe|dUa-JX{HF<4sJ+gJ8|r4rhW3~X(w z20B!bgwQ2vh5}QNrCe4#hq^wx?7MYRpHgeVYp>hP`V+A~>c4SsRBGQ&-`(eZwc)iq z`Sm2c7XqJd&Qat}Oq3P5cT~&6i$A9g_f&|Bac?lgu`4mN@?aBW8frvKnbfj3F(frbN(Y zefat-V(e|VZGw%9YBcsA9(Om?eb;ul=aF_&Z`uOv(xO1iS5wy-I6|f_<>vT>^(kN3 z{BT7GdVmi}X$dH(BvykH#tjJWhxUfk-@Kc7M%&{(nR4H|fq4s6q9d~0Y*YU#dr;FGjGW!SsW{4Q?(#jDEcq}QQiC3dInv;?gy?goO8he2g-_rN{Tj$1Mf9B)NN6> zZRn`cmfhn!sa5DPES5!~#GM{$mzS0XU|{T=xPW3|CB&HQJ1%X(URv7_qr34mf#W&U zRal_R+>$l8J578ElGU{=n|dN;$}PiXk>YjInx5XL8H@LC-JK$7Y}vJDHIUr9^3}c~ z%nhk*{mxx9nW_|Mc4P-!T;cZR$?G;zWcM^8=>!zDaqj)(SuH<%dDTU5f|$UmEd-qM zO-42NTTQ!qMHwv3+U>{cEyo!8XNreeOLo&XLCgZ#RZp-A;_BAlIXxDIWUnKvTnp$I zb8hD2+%6EmVX`-+QfEnQwvgG(=_;jZ+k>vg&4|`olUT8G@57=l7UAT7Mm~$}@z|QN z$()I#A|}2yYu2Wy0WE+t>*K5g|C$(vkm-gV^G zy$SkwxS*;I&5l}L{M-?H5Y&2?87>L|sXDXhF_OEvPa)C;ysg@{s`WWdyM1=;ZY#E4 zHV>lxh5rBxB3*%Q^~*CtZX4S26irm*m&1!^Q6!wn_h6h()VVox-E}khy~dW2DSeAz z@4idC$i41K(j1p{c)dD&g(=yRmZPalTGg7Ad(Jjk_91OjhEpQ^?mD(xjjP1FD`G%m zX?%l>*QM9LS4aI%%*|v3_D&)_UHE7nf zx07wApe@yup+A{GVz)ak&NaTJ*(k+%{lBSiwqRe%xUJfYl%Lz|_!1nxHR5ki?ZvtJ zn!QEl-dQ9Vo|E>4z^WCo-|rANMeaGF)O6~L99ESI^Anyt-!r;0w;6Kbrx<^&t}EES z-W(5S*}E?nZiiDfr!OU4KXre1g+^BC4e&xqCB-qDJMeWr~d-F3*P)8NcLyJdRvxjkMF6E*D{_gcr+aP)Q$_ zxnLPacOS6QsoBQ2!&A;bEpx8t#h!;Q3Mvvb|a z8T8BPOPhUS^B&jWt^?R_@&_XBWd%2E4hYboK}x9g!sjh%T4KUvRJiJaKa~ku$Vpti z+SP=d^r;3e1g4vMQ_s)Aeg5OVyN{N=FMSv`p5ox&MAwUNs86Vs{sQeh{p)!MW=*eQ zsnqI|WZE&`4yp!W8<}u#}%N-ZxvBkJ9NM4{aat*00Y+qSMg(YqgcQbua3l z2)`d~JQtGuYU@rJ$!l=$?a6fCyp?&EyEi`Nl;q3h-fOFqKIJvT<9buh)k|8k9>4$; z01Q>+G+0RD*08f*?_WWLQ^$$HfWx^Jp)`cEH`FS@`-3V$rS4t%Q zWz*O@@#a0n+-lTXPOY0Qn`&-p(?oUOi!a-=)MVH^Iq6Gm4mRPt0TJ$7=g3nkQ%xNT z4H20t1L7whb7unPO!=q4-d)`7;=|L#?)Rr3O?w-KcGDKyCAAOTB~i=9;Bc2D%za-H zmbK?h^2edN2Q?nSjqD*cNJCc4mL4>2zNN=Vqrfk%-&#DI>MxUb4({DorO3Tq99Ql3*4$E~ z+M*LgvdVf24&KwAJtwz&ZaDGhxT`j1yOX&keqB3yL1>6yf%ql;-kDqDdAzGm%e9{} z<8*kD=d5^xO;k{+T?Q<-9l|h;5kt3alDNx(XoGm<&C$0uX2;uZNsTeYMA&;G6G~I7 zTC&!(j;b`OiRp|^USpg0o|(7cM#ZjacpjBFt7dTr(l^sQ{{T=NukyQ()Q0qkV{NNP zfljp{*6Jcf3(O@QU2=n_B2sEf5Y|ks$iMRv=9ZSxzgR z-;h?N1R((U7-c<)NbrCDi)WjGmOh8jj|amA1a-betPT4dggW6doQQHPG_DjgB^1H3KYOYRQ2 zdSK)2$p-c7qn&M0p7mMEcIP!Y$2J*A^BURq$CH|5yoJ_NpoJQhQ8jHNEo#cixa8~0 z_WVD7zj=9m#=@feeZQf@SW(pDUpBT%BYlcDXvi0PO^uPL!DcjKfz-IN z3XMDJtVMRI5?ZG)KQcI$_iW2^YO7Xen2Op*MEPwdzlp_ty;*HGxo9PAYb1MJjotZv zU>Gg9S=*3SGo2`8NCalvT}ki6H)h-AL{YcPsLGc%MZ#0^E5}-R_ zH7cu0M^W2}ZSf+15b)dI5c@Hqz~ENq@ht|+c?m;x%cXwoZnmjtV>dOabojnPbs=49 zGW)Ty#8fi38C5yuMI5C1`UN4f1~D6ye>MBfR@Xm4Ht|G?w!(tDQsK4Lmlb*3`esY^ioYmN-= zU!a}+Wt(S{W}g8rHn&5KJ5ZIRs+Af0Mj+Z@Sx~yl$x(;A?{>i4Pz|OeXwOs1NtGy(QRBzZe^6_PEUksB@y7AJ#cNOhK{kfj;Gh;R8TyLmu)!R1i zeA^uF$0!Nozg7Ew*~vR~QPZoPD}^ z`%hEv-M$ubqjnr7s#woKe5Bsh7@9#Kz@C zPTfyI5*^fpAI#{|v(ko{V`UXajI)muhJR4*HFIb3-rnSx3Q4xIt%P=OAVrb?0CkIWd1W@4r%|FxUSF50mGLLAW5Is2cD2jaqnBlVWxajt z$HKaC<3+1IgXzBflX%x1V)zcd!N*tMP9CARzYOmrDm)7?q;&BBD}e5|#3DD+Af3a^UY|K?+~un0Iur6{p7FoD?2~c1@0FQk>f4&9U5=n;mP5 zDk8zuGEgZ!Fhi9_S(A&Y*KRA_+c(v}6*s}$i9MTE_O&Fr#QVwJA#rXrr^>venPvKE zJ;xK{#ZS}XU#q=HoYUkjiGAb!$$HuQy3)IC>d5-z++nx~ExOsEq3ucP4`{>_w$myy zY;wqk=EQOuj)Uejeie^ycx6D`5Z`%CI90199^Y;`w1FK&c@3e>O}@}|2lEe$>^OC4 z5IP7?LGn(h>hlr#ka9G{YEY)64s_rVb+@}k0QX`$mc~#gMI%0yz$)0a0a}@OXmL6f zpO(JRa8l+WxdbH3gyTRj`^0yrKeq+Kbs%=olILb6APTI3s-mMTIXWBwq&7mdij&aO z1T2vo5}l6X1w~e$=1yY|i&Y14s{lHFJjcK8#f__kcVj_D!MV2v(7Zyw?TfqCOS~0U zJmwXpRO(Wp(Mdh2P$2e<1G#OMb0Nt!p?9(GxfnC%gZ`uxyPHdOPbt4S#|cw~M3SWw zQb|2Y?Ks!+n9r947pI%2l^wSK0NJnc!u3erBJnr>05V+%!1!zZRDp$8Dm$YPR%LFf zWocBLob()8V5$x-qL|wh;K%3s+^MVk^t{MX&8w{OmEHOt{VNu z=Vq1)e{$UR_#NZU^He^>VR=2H9_9X+HXMs><9I6bE_X#YhoqIxCqZsWro|JIrSjo8oH9lXtU#1GiGiZ)Ws|2 zuCBWB`jD~|f9B$9x&__-vV24A?itJ@$xFmH(DdnuRxMc*#zr5!zNFOvEq|Dv-)NX2Q(hqVtCn#4ULG2ig|1Johm21;ryW-i3VdUNhLgHpuTo>+po)TCn zZ2{*&rD*>N|Qw&zdTx5$g>G5kT|xA`MNB3 zs4jVz7QCWPbtAVtYH+gSFN8Y^>{Fn_Jx&j0!al>!2lCR9RUfk)$obm5g>U^ixa@9fHG1X@%BPh))Q8gW+xa4yJ!VU$Fw4rrgVuqglEKX*GnwlQC z=FTz_wRA~W--#7O1QFitMvrMU!xCaSBX1=?oMukbq=ZU5fkw#ZhI&OJ43ZL z5642h5rVt55^*mwM<}s+*)0zfsVznc>ERju_-)Blj&RUcdTLooDV9_~=nX%HA|i|` z#>}||+LGs9%r!DwcIYFkGJCM*$&6tRHbAO?@57Lcd|H_KX4_I!QdT@e9HPpb@~aoW zbA1C!TqF*Iy9FysgdBn-nW4>k!3sS+y_jbLXxb9glHy6#TIMl3M6FONv7peOeg%|A ztwT`SlPT1-6_rR?=!Al!_+iq@gjS<2M0l#*aJC~d7Fr6l2VAge6UqJMjKNl8&bGS1rX=6et-acH!wz z=ujkDm{gRu-3>k=Tq)H?Qsu#;LZejl7#Ljx%}jBst;K6`Lpr^M zc)~{7L6MC{M2TiYq^#=f9_&vk1xDQmEvDRrgvnZR>`gQKFr4im9E;C@>76>UzFhcF zMF{*b_^Pl7$5EUhq1P#|NUaVERMSs~ z?8LVSI)-9B<+u`%Gwh}Uo2asM9SmI7+EYLiU$|l^+R#NIK@#J-vR+UC46D%M4q@Pp z+}UKPg#e0pKsbq_KA@h7vo3RE!L~A~GNdRaw$PnNKGWO>e@saz2I5+GB}94(`e%I( zxrf!Wmab9d8za^(A&j8o5~m_WJ9W^c2LckHQ0$hCXs<4aS4~7$7FZi>?|2#|V%?Rg z=)-aSFB7#acKx?#<~TMh4VAFnk7`=1Xoqcr5P;ZAY&Nu~AONY)+l~GP#mgQVnZ3(* zNzrgCwfpJEdvOi~^GZvA9~rjLSL|~amUeoovo&*dbt0>m(~o(f3S3TPi6vsb2OvD3 z#V(u`Z)d12&EAAhxjg~%Exn~XYTouc(!j}r zM|Rus8gjRc)~n{IrkT=*rVEAYEkS2)yaX9Lxz_=!pbpcCEl#9zw$RDD?lrHgwZu}D z2V5AFZ*?kBS*Kv>h+BL_<<^55b;WPHp$a*lgd9N_)s33vI^F1>Wysjvb$8siCW{ld zlgM!Zoc>v)>1=j&H8|1YLzV;*YNegH-vKK9N>;tNGBKzX$r|z2%;fHN-*EWbhIT~$ zMc&htkNg(i*jS9x)lWK8%PDF*B8ILyq>QL4a=_V}m8@7+Y)ouxx97_QdOGxZ^k(C} zG#0K!^>;omEpPbBOo)748A?{vwYoe>5(>1g;NnkRU^P*aDl3mI-aE$TO=AyCPfXf7 zMmCo;^=D-6JEjf3EPHjfJ68LA2GY&G0}Z8?SZN8;ls1NfQj~x)08<#Nl5N$SOLn6j z5}_MhFOufDP;0E_bLoN{QbW{Oidu1HWyO(GQ(nw8>M$uXVnazQbQN}r*AA8Bi6f0E z4I7T`EmgSm3HRcr_D7L#jfYMnas9y|X;Y5Bp(8p}j8xseTwAe18Qxrk)KIoqm`cTd z6V&2{`f_EYDFvxs?>n|t!Q~gEscAhETt#lyp%KV+8!lWk1q73sI4*;waMS0`k*I)2 zQP-{`ow^|_iJTXNooJ2v-togUp8wvdvwD5^@6Pyp<%A?Z#_ zLMxdzp~Z;XHlX7uUZP9QiL`qw+2w@{D{=gN+=_5hqzcLX$x_x-wWNRRnJFKR z8+CI)mmXA+G42mjH>=&js|MPO^A{=V@}S!xvh3OKEQ)}JSqoVt4uD`wdvxBZY5M~& zXGSiS*UQK7;#h7a`eu6f^-Q)dvNk%nhh$vB3b$tSeA^bbTv60nx1cUklm>KK8|pK` zR^w{kt=0QKpZ-CoZuQklQ{Vi*oes%*ZTbQFWY{f6y7R8&yL0~J0hb-Kp>EhV`%bj7 zyraZfedHX1gRYQ3>6yg&JFhCMYABNnw`FovigT0j@cciWPO@0p^ug&%Zlb}pVeO70 zwM0QeHr0|8W@R`BQRTrwbwr-Jk6yU*e&YJGvhAt$uBl7OYR`7;ub>^tyfqsZ9AoCz zg?HpHUi)&|WHZM*CF_gFBwy~Nw9jveEHI+Acle0M&;7;!0A{i-Rv0j+wf_Jw{2yTU zNBtJEsdI6;!~0Q5_wT5_Tjdov#p8I&Hs=q5#SXAxCZd{37}8Wf)=|gL`}chA`SNcj z?3#WF(S5!3KEdq|qJ6uvMSQ)O)p)&F#@k$dyZ3AD#JAX&C5H&rB?KXagW6nG2l2&T z$=lhxjOmLBP5IML#Ix?tsk1&fdNR?<==*(|KFB?5}L zI0T6b>@{1U)9j``j_psPdnVSCP*Rfddb8q=`Tqd1y~n>cr*XzF!Du>Fa92=BJu&K5 z@nLCFDv0r0xi>C8X|~_AAmq1}NGc!>qO{8r0IAe0x3rAO9@Cw1!i68XM`v6D0RN}JcbMVwE)-~&z z(u>>tzwlCDP~TjIY)!c@j(srLY`&fLTL@cFEw)IplX6l3$hjaRNKvk20)4m&c^4<~ zXcZl{(wF*~=RcgieeZroxLl^*w*=d~=}K`=PkKx8WVoeu)a%Ms0qz-KIm*;^d`nw# zxxr|TaBq%s+*8ZSH*pm%5>#~+yPa~>;UIm31X`S~Xpjy}BOWZO`;&f|FRj(SF?17_~duuy2Y4U?5&EwG31uehN=lxV-^#3apTP*C<{owxqrj8e$g7tDXOMpMqZZ_V=EV+uehADm`2O0GDlR?P{J2!|(S{ zc(0k|z}Xi)t?9FFx9MAhuBh_22IYJFUm+B;thOpp=<0;!fU_O?6O zw*a<4Nx!-BUprhpm$`kHg%3o?-*C3Pex4##@o(*~-}n`p-sPI#CCGexWb)lwkIilw zDAxMcuB965r&m+nap^mL&lpCD9#^^d1y@93y*>Ia*zE7VWWPtUy`&gW+(40YxVn_oGa4#< zY9xweJ8;aL|#Q)dYwVrd@Z%M1(mJm%$JD!dv9Bt4nVMLLR(-A!cdBMmU>i*V9YJ5 zzNzskcTVbQ@>}3juTVa>F8kIy+-<_pPTAS&1&1GP8fD8zN|M;BLcN&0w`oMhuHUgMBCOE=s(_nR0}wo_ec8QW6)RZf2G3#CI+Xmzn4v(_f-nPSR=v&Rde?u!NaT zEeRnC6rfE;BkB=OTF|=N!<0DR)AgzNcC<&_8_P%i#TtB=#)Jhvv5=?o_TW4DYP3bx z7S8T^r@-^u>qFPGzq;H6yV%{E%muY?Ww!z#TN1OFZLv{QiVpkDNx5|ze zZ!P({IV$ra>wUOACC653>efw0bO3e%+%clFWkDF+oS9vEmbcL-pk8wI#mP_XzAb{r z+u2)DDVNNA?RY|)+Pf;&5*}Og-z#W2Ah+UXDi#*Jii!|*U>(zSvx{lYPwpkb+vRrZ zR60rO3)d$MdPm|d#prYCUeuco)Z9Vn9;5wbOF?6M@+`)Y^3Mn|n+>WY6)R9dXeA*O z4FCvG^6BSa&%4#0kp9)BJpTX>XP&QyzNdqX5C9b=qiyT~T5r7G3swpB`%QkW+*a#2(R+xFQ`T9zAkZ-dAe zYZo5u3mdKNXbz$-&)M-?Ql6Ol)Ef&@P!6@M{n*E4UUwvV9@~QpX*p<*NPdYuEb-T& zPAc5~rhP=six+)apov#YlZ7rdWo6ca+qS_6`^j#6D^h!M9WWHFN?w@p1kBEr`<_ro z0KIJb#NK|dIfnbmo4QJ^8&cd{kFa);SqxgOXf!}6uAe!;kfomdx(sbQdt{B6bI(~b z6TWWC;DgZRF8$w&zPaQw@5saXej&NC3U(EOYaU#U*>quKktuDikl@o`7U102sfOnA=oZBR?F8T-A=1L=E{-dg-E`S7xeo{BTA!wB1N@B|%V> z29haS46$SdQ30JYuCc*S=*L)cK%NxzL@G={TarnHgDrnauksjpZX^&GM zlC`N@{X~T|$tZY?l*@V3uwxay+~l22X47fP8V~L-yu@v|`kv6Eq;$wr71-PDg`;MU zJ1y71koo?6d3;H8(IGA^wX#45Ka3nM3OS5nITN|5pucgbRHqi6SL87+J4ba&aQTSws5D7BhiOhHzh)^_AS)R%Z&RQi ztG5lp&Bgo~xWa^ou1%>o4=@FJm1L{9P?}B%pa-p`Fz#-F z^EUN#QSH<1#y4=pxna@pE-~-hzO>PuLHc^kOiovgwyyTl^}6`O?X8kLO+1hYP_Oi2 z9j7fu?L5P;;?^lM(2b95y>CM%I4Wr%1OZZMpIYO687IUQWAdTp9#q)wP$@|R^7diZ zBB(4YQG8t1^mR2MTF`P|xb|Q<#c>U@Cb*QJnl{&#yOP>PQne{MP@>Al~Ew<`JeIo>5YE3xITA~JqGS?oV6;cEB^q8 zo6%t}l1^!69Hl6M&<4kTJP&Zjdw+5&e~$copTxh5qHo@Nj3*t-VN?GA@|N`beY{9( zg7<`rO~MPvk>$#?INQ-&)B=+0L@6il7~Wc&w{b|tT2+M)R-9eE;&KNb?QPi+eq(o- zOgll;J)kAPM|f3nzr?(!8ku~!@?0QKSsY*g02Vk$bXwvx9lqI`H(k8$l2n;cfpbxlG=p{{&eS-JUg;x_N~@JDD!pd1B~SH&hnEA4a3WgAxd4V#H5uZE1)@2n8@eLr!FM$vgkz6 z_s-~o$%`^uvZH}TH0e^pa;8=6IEKdLo`S{{;x{g_HumNPZlN}tnKeu4{_W3VhAyFg zEdol~lhAKiMXWYw%S99TT9k2dKoV;T6;;JLP3x>BOWc&^YS~B?M?c zl;#c}(Wx!Q7JU&V9RC2qz5K)ASM_3BPnSSWN{Dh;em}>W55VBfCj~}TAfEufI#HsCi55P|SU!5~sL8o%%R~n93uM;}uko*$&fE~xiumSlEhc@WH%H;XwAp6k5X^85E z{H^(MnqJ^W`5NHLlm1@XHme#&O{_;U90UDlPxNASId~!|ZMN%KNx!$=eD>$$>yXv^ zb@j$O6z z$Myx=vBm)ETj^wuAXnogfS`Q41AjA-i7QyIkA}(y)X1jd2a!h{X}!&b1B%%e1yF zx+2+=GLj@35S0pPAmWcNJ7OKJ=+DjQ(AN_hZh(=0)4w=GVBp$xyY z=G-MYHsE}OIBFW|ab&Z3RSl0=m|6h~&#f@w&L+xwgCm|6f-7NlElN3@azx507)j3@a}G!N zg-%#w2fM&|Tb9Zal%*o4sa7j*rrA|_G3{@5LbMb*6G~JAmv?R}?!{29lGJ>{khyJy zKJ`j+%h`&_#*cwIdJbztaAU15I>=MqQnG7bvjSe`kmNt4T*-v5CV{|@jC-&YF%;)k z5gXJhA0x?{?Zsa-;ZtV1O>)bwSlw*)BN*sUhYd)i^*FdK%xVL-4ovDAm6MKRluxxD z>?IDNu+OFvgG{BDN${Ya<4ix5ispgS6~U5-M}1Bm(<65iHu6|c$0EJqBDn5EcQbqe z61XVp%HH>@NI!NKu|hQ_ZmErdMq##(?GdQ{I9{S$5z612093csC|xN7pcxOt2*<37j8y8bCsph1fgA?20Hs=B99hpyr1{hnF>7*0mEtydjrf5}-19C={{Rs2OMl3dq*L97 z^dlYuELsXS`F6!5d8jK`=uHhz+lfX>GCWjz- zF3UPk{5-)~$?Ae%g> z2H6A7c?xt8*g5-fu#H&j*Qm545b{|_0Rn(gAPn-vv?=9QCrfz9q0!T`5IC{WR0$v} zSHss5B1SnNN=pC*9}qoGBtW;IVob&hrC|V`q~}aOMlu#(h(UAI6cpJ5KEa4CThyL5 z0e*ew%~GPHPJ<0lZ07`pSWxeCGmQoL3I1fEL}%>+n19PmhF4+cOx$r;zt4VlT-MuJ!ESoFm7 zO}zJ{J6*lIZK0NJmgp@dsCM}1LoBeQ~lG8tKQ(69G<`iW5UDizm1>cP>9DDPfmcRu#!s_OUWiJ<%dF61;v zH?7X5bppJ*YH3q~?ZaAvWM4^Dm7|ci(@+|D*WdW7<+a~Vy)a#ibSQE%*-E!g?|Sng z^r^xaaM#Mrq?($37S#c0bgbSyLl7a zS{5hMXA4c_yYpL$^vP>z+g29iy^>aiSIZUb@5P?mfjKT<44dk@9b^|ZqxQY4wyjUx zt}|4a)>{M?9g`*ZeiXLLG18^1`w}sD?n2CKEdkxBTxmyEb$8Og(bJuIifylV`jxo5 z!F+VB4-K6iY{=c07R?j_@>?*Fq1RToD0(F07rJ+hBUNvSrrLW-b9~oyUPq*!8saV% z^vSa0>^>0Ku=Yib0Dj=>DGQx&bP5-yN^-8g;*MoYQOBHSXI#HNX*!;dg|*qS8bwfD zyY6?Ssbf(I70$Q@FrjU1WxX{DI@a}8I*R3oRCi4US9xuklyM>s8Q>)Z_MRLVm##mb-&C|Z-NJET6 zQkrJ8Sgkr|_~1Ek3W8inK6JO9b;T*qHEKDeRI5K=7;GZxsTA(jmCKjCOK>LzLKFFW znD+M`A!SLA9^-S2+_zjxb!l!oRHsy|x&-02?MhP6No7LXt9l%&<`1u5r45F;5O~LI z?p|`bq&k+S#)vGIVB=><3XyXuc}XWQTV#SedQ{k9J}*M9{{63|ROEL#eRe%;a%q!q z;^?-2Pj)B5Lu<3e7fZ@Td_d2;CB%S6RYNHwzqsF*x5esIUEK0!=(ITr$vlzDJhgpZ z=jC1h0C?mp`>SSGJJf1Y0Ude?7CxUlylpi6g3iBCh*6$Xys zqmnbs`;It>GO0Qt$xt++6o2TPBRL$!+72$^=r-!YiBNXcy+TpfKiub2@E9uM=CT*J z%l954(6p_Ts2r4#aky%X@*9^_l~LV~plssDJM*kc_$F;wi&A^bHx*O@B&EH_NYy>HBKXnV4{fTS`1S4nTKP0rLmT zT?Izu5tz(&4{kXo#tm*yoUz1wqEy&GZy9Q50h`)$#nqv`*zX)0L0aVl~RDI zlDVs=O8LMZ?%?IRG`%S=d3lT5>QdFW!$qR6-K{;1v#UEViTx=0Qn83zFJ$d5AhtI} zFUX4-HwV-b2BkQpxgpi%@e@;wM{#{b+i)V~e(JB{&sObEqT9ylNMhK0zUm@1-(I>l zHI>&{*n4Vb>ziF7^)W^qKI()Ty1%$%!n>FL$5nFR!|#{!;(b}zfApH|$t#1X%i3ZJTNrKr=a@K-fSN!~VKX83S-nTdT_ZnaPbYIW*pJDcY z&`#RfQdX>~NqEar1xpvJ%jU6{CPT^^oI!Et+)|Hl)S_{QkFMn-SxNYLp0#%`6K}Iw zxZ8sFB6HeaoOhMrH7@I`Z$)qdmCGQzr1pjZu7BDi9+%ty07*B^$*0rggYkS%o%`$l z)%NDjoNZ65ad1n8OEI^&J7aD3<2yNneVo+~>!l%(q1)x7Blg!HMs~mbZrbr$xsv5_ zUQdVMd_CXa_bY$g)UD&v)E}1;RuZGdj)>6^mKkgS+bBp%MLPgZ0H!{K+x8q>Sfyqv zKI7!Q*L}^+o0aLxP5H2*=D4hhsLT>O@ph4)stc)WrD}8Ps`p@4PZ>}Q$ZaBuR;u(B z!qJTh$)1FzwEqCSc|i=ckW{x8qd=6UIjKjm0aJxhmyrbS!4}7f{R{e*<~U962R7SW zJIClja!(-dB`(t(GupOdEPTVi?Ij66&55NnmY{DUu8|~*{{SR?G<^=lOv|kF`ncx1 zQ42R2=}UWN9K;T-MylE&Dj8HbuV}>{Uga0?mKggz>FYPjzTa;Xl|HRLxZJe$R>l0Q9O&2;>(C)*|eyZrv&*c)rRWb5~Zdc z&B%Msfc=AG4x2IokC2u)erAsw6&!uaJLRatU z6;UNC7T2ljR;|a&4?I@Qr2(eSLDF;kv3-Txb$rt^n}>2mDnww#hr0J$#6*tk$aP*f z)llANJkRuE_H)L^TGFK~EoNoCDv>R1z7<-Qhp)sgvh8YAsJPAIcGicd>wh5j#W zMjFpnOp)ySb8E%c>e;u)PgN?3>G+&f!QH;}FQq#+Bd-AlX8uEqR6VI{HwhgZoV1j* zJ1P^@oLXY+Zncq1liU&E19)6>ytk8xk$jZlMQWJYgd`s1WIp_9F}B!4M2y}><2k)q zW412=z<^kOL`2DTBzV-Cnp4@`i+fmY70uM?FQ?x|d-s&CFq+!?ZO34el{++`r6D!U z)j$e+@N<=$Dzj+}vyF1=q*YFo+&IJ1zoLr^JENWVbZL9FkW3Aq8DeDTou)|*wiIf5 zbORY&zT}kjw5YtzRh@zs)n}_A&DZJ!)aR(JzjT|i7`A#j^s#Pi{^86Unc;7fB07E{ z9gt9`4xX9~4}LRQo1CAUWu=NF{1MG9)$0$^kESPOJL$V*fACH7pO~{F)UP8n^N=Dw z2|}H<+MOd^4rx&c1ZSw@u{MkOO>@>M0%Y$tt#H)l&o6VQI(nwv>)rghzAjtl?zJoS z#oQiW$w6&I=|mVwqY=VEpwNJ-btfAvEt1`p=|)pKdY$b&2Oh%UyQSIK(`Cn5O;cCo zRYtu$BiV~+#)zYFk#B7qUuAK`LgdvoD&UH##?(MqpwHc0Hc)IaAtuOqU({~lxoqAF z+d}&7wY59C@9UC?J9BQsK&g>*xxND|B`G?zRJft=p;^;Ey6=`TZPrg;FCV!Y9h@WA3Tjzj1EK-lTcj=3F<8`E}OimMev2VG!F{I?#n52oyqkBmtH$HqC)GN>O=z zynUv3xI4++(P>X19vZyNEUyWwX3IB$c9`d#OK>83XYVjOJe9N}|_l*rj?^XBuU*&R(nc zbnW}RFVZLc$ye^1?&zW;MrTkZsEw%ULPugyDc(uOJ1=yNT^(8KyH9Ji9aRx}y+3+p zE{5Up-KogUIr?|5Mw;4I{DmKg#Y%=r_T&aCY~o$M`Q&P@=h(uBEK66txhI1CAXp^Y zt)@Qfu-70by;BDx*) z;=TjgJa1uUT$UNPMTpQYNO>W;Ap)eSL<(X#Tp4QArt~6CarJLx+16W~yJpI6$!UUx zBICLitqwSx$LIob741E61Lk}UHWmfDZ<TU}P>_o!K2R^ab?T z7xk{o}YH zh?8`0?N7(|YFzC*baEu)8rOoSpO3RoA8wpWCF@t}6|j}Wmj3`m9)<$P3L8i@TZ$nm9ZoDa z447E7--7#nzmP^AEbq>~XQjR$^xMI|OFLz!aqotpdXDw`d1NZdw%PLLZH_)$?e!po z=1WIH;)bGYpd@3)yN7<2$x=K%-*2-$GiBOi#i~d5{l4CA{{Z??^ew&pM{nrfsXby& zk-2UYREb+>Y|1JOqKnoP8Br-hKa>D6_Tx8=Wj9u7Gjro=-z*OLSnO7-OenUiJ+-J7 z*l7rj4U_~ngp7azG{wcJ`6D5dl{c$;kk_ftQ#@JeM~tqU_kLVkW!BK7+H7cAK5l%1 z4wl@O$Jm`WLzVl#g=|zS}rAS+qYHfNF1-rr;@HV?k?hMf9>3Lw-gz++u9^5deU@! z!^CDmBA{a>etg_b<_;e(4hMIBm|lQjZ3`0pNOIv#fY}&a$eV)T5Y&5Y2HH~HeIe=q zvIP=Oeb}$*C@yajEyr@Kx>4Y~N(1#7_0;F8E%N5o*blj3^!Kn`Q#&fmR-l{AlmeG7 zl7)OlWFIIVgFdG%Hp8~l*Cb>2y~$gTBu*Lfe)%#9fd$nfBFF^-m3V_fa_p`(%R|ql zIZj%aC(&0F@t+Ry<-_Vr>fXwmhXQeB!{zt~jUFjXo=V@*mVQ;HS$8GCgz{3QxO^&F zYG{;IS^d*F-&>*MzPO_l~P%SW1iEA96Cj!pWP^%=?@t@+!2^Y<@bRd?Q7=1_J+ zVQVoh@Y?W3x5G)GL}=*np&$yHsEVlK&ld_dEhN6)-?xPd9L)J&xmCbK*;{&4$g=N+ zlkyg@Jm<0r3R$94pIV#^e(L6-(2G&1xRa^X=S@c!RviJ|&SnE}c`Puj=yL#j@hIDz z2J#&dZC6Rh``I*)A37QPmv}!sN^HXTvqM|Yg zO-VSY%&O40!;MC_AnpqdlEo8>_AyJAn`_2S^4vi*cJX%jNv?EZnrUq{&#%6=(rxNl z-IWM=wkilw>xri5!QsSkY@LU?`8#=S?mpPImEN~z>5`bXNC~P^R(wlw)zE}A+G@8J za!N;oOju35fEE9^u(Mv5f{bfCB0VX*r*_8te#}+_K8;0Z^Q&qyz89+ic=w_XnwN zINg3qnyx~*eYo6XE+$r5kPoPISAIrWMSR9ltKE!t{{U%9R5bfePb_MIHWYM%%0_e| zpN2EGwkt2dy1Bevi2UYg(#Giy9sCUs=O~Vd0p`grs>D}Y00&yGKTxDO#q||lG z6xcjV8_PoITcrgu4}bogYHjc;9}$bmj|aB2^eI(Pp-Pqn zS{mF;wiFRsR-gJYg`Bx%Qs(42pix+xZf(AFWxCl3ONpV+A{0HCb=_L zo~$kM(Cl_(u&}lk0-GR$3REg7jn%u0l@iZERb~vW0D#)bp;uaB8Qf@Uf@nAUDjsa1 zwbrArW(VhIbjU-K_S5Tz7E$J)%J^pFY5L;6Y@O|6#9dQapP`Jg7E??mi^6Et?xJvU!^Fvl=pdu9%c1o zWx0C?uV?(0nv(O=iqnRgxBmdd+z-BrdP+ihs}WbzgA~>0(whWe*DIck}7f%dOwG zt@DnhKUeGvMZ(S$byh5t(bOc5cQRqEzBVf0bRBDh-u#T9sdS zSsQ(su;eDKx{}(-?aHZ8_hHmT?6Y4>9$(v=51RLnrVd=0pDSNlln%^zht*A*RpMs#`UL1 zZF}@n2LAxG?l0T!&4iGG>~}PW6aAMHrD^sYZaY7FhGr~h7hN^?m@eqrCy$WRR88sb zI+^L#`8kV3OMK_*meP=rPFZ&6-;MRFjiWDe2)gVr#OTRdN9qCM?&*m}z6DJ66SB86 zR%v&=wCjqK9V%bRgW+8VObc;VAkc27oqth0t#OqiZr|M-e77ODHAkMXLfBGO`BhGb zJYsiya&l#yZ%HD!+b_(`X$?5&TIeX@>V5dl#<62&+>k7&o^UH<(2j$!;>!;$gL1JA zSK0P;DW^~*Ls$2a+ytlFPV8!IWJRJWUvXr+#C)?iVQz~y=h_!VTW&VDlIsm>AxTwg zq>8hq4hJzn{SskdVNaqmAx@-_csJ?VA`yTs?B*)Yx7A$ za}nR_70?rnPEDBKhgKhWK2^R}a=Q6wd6l{4{{VQ&&-+6e$y$&LilrFLb)`K}uo9z? zJqB13N`+n9dWjDu)((ZIF2mV`D;g7?SU&UZ!l7K4oVBDUfd#1g7YvhIp&E-I-6Hibp(C5o-i5$w4qg9bRO=^NFY;I zoatJ2VZh*PZMSGq;8^TE4kwIgB|>X1F;S#~dXB@{g=(TTQUP-HCAx$mroYuduj7q( zF-)-&^ATlSB*|4)3LU)3#YF%@BA=`gy6DupVlRdl3Ry9Pr1k_?Vip&C?>?K^(W0fe~g4XtSztyHf{ zWrp>p*&Dv!h}*i$gCbht!a`^dh|mP2eb`95gW{Xjj=Ljv=txdM!$DPl90_(aZ&C*o zD9gAq$y1H2Aw7U~#9u0Mpo{z9Q7UZeDfpH~5n8Vi+!&oA{ta~$f;&(AxFS%Xd}UCX zys)9-bhzhZY45{%Xhq7*N4m*uDu@lCUG=VbM+yiqcQm!Qrxv4Co}&fhS)&cZjaeHy zwJAj6(IWQTbO0op;mDOFU?iS{3sGttL@5eMSV7V|aK|)3EQp}HjV-f93^_7r%`hw@ z92jYpD1%I~#8bIJQHZ3dRAM@U&giVm$!>{J15b5uJDQ$ld6rwXdF$L%` zNN13?q~6|*mWPn8YwCk>ngkvr^`T1 zb;?ru{3-stJCuJ?Z@|a?9*R-5xlXk-{sRu8JM$U0ogFFg#80r2RDX^cQK}O0W)(T= zX+l!a{>nhauHzi7AgZ5rUlWA?0H?DIa%0pHre?J`N{%(dQxwh|gamV-!n1&|(~q!; z3TF?9htxQ2MWA7(0>}Ur9_%&IA;|_6Zk(_@sH#$Zg1C>Uh2=5(MbIZvg65y*(rJdf z(epXMhT-k&ywwD=1=SOfq6t5SBc5HT@n~%a-J^`RTjeDdXGXt=P^VviOa*&(vmX)D zwCFk;^!>iBZY5vgq9FeOJBFj|0|PDo!WjrMW7=Mww%wV=WbR37y-G%swn5OvX^ynn=*oXih+TaUHD8iR#!C?7?^y_kTVlp>Y!& zxa#skG}fJav7*MCKysA^hSlOJ7ncLKSBzFks#_TnJ*Tq|zq`f(YOQEZ4eYk8Fq0D7 z9kyAe3dh8w?Ksa}+?<_wh_K%lM%Rt7l*mz=azr*<_{D0x9gTgxXhACp45{`VtjfY! znzx^>eV~k5$1TumOn2w;$Ypi2N+R$_o3&XZt zt`3G_Kyp)uqEM1>vcDLQa!ZK(wQ)}FxQ%W^O}(-Co={0YV89o5TXB^Ks87+#!L7De+?e+)rPZY{Qop{tJNSqDy~q9b+aGEGe|lAW|g)W0X> z6JGMP+0cbrHncdZXb^{%lKR2xnQ-CXFqZ^5Voq;XN*ouoIfK-nAlWuv({5y@F0#=P z48UO_Psn>DMPUu6#^SrX1xX_kSaZ1Oi#&a@+%;$DgY=U6M{GYp`ySoO+@)@hd-V~y zVm7zD;E5w|wy+oS?6`m4{{Y%34!L$>?+r+~c<%)`9Ug5-u64$T9@9w8atzW6BCbSjp#jTrlMtc47C%N6e zFCGbnr9!^uMudHNy|l<)r-Dumw{E(X(zsUsLFD^&iHFON2Ul99J;ZdaF=t~2s;tZI z+q^Ak+0MRL^#!$UE=sv>z0(TI6$(0AenU;^)N%nORqXD^UCFU&Ya~|T+`W@3TZ?rm zjsF1GM-4FQ;@K|pH%-Q+G2+|exbbm)G=h+7Yl?r@Y)#TSzh7s*YII&Q=K>LC(BnI zs@)m6JEHup$#@_a0uUIAg?5CXRFUsECKe}MK~^*n$KCPm>_m{?%A?DowWmlm`|z{3 zQDG`GnVi3Kv_QH$@@Y(IX(v)qkUwTI-NBm(K3MTRE4H@DPCU%5cP-4~ipIS2#x>>2 zOxgUhNSQbzg||mB-(tSxcVlgaA3|J`l&HNe!l6cEpaP|-T{0f*X}0a6Q}x`^D4@;F zqvVFN_8zo-9sM4;kCAr$f_C;`dUoO#kl)BRDlv0#&5zgoG$m8Y)D1%4{{WkacW&sj zj~+`}vi|^S?K}77&38n4AN1$w*VBKb_^5Y_wmWWaw^|L|IUeh#eg0VO{{Rr^{Uj67 z4FrLYInUoO-+rcg7T?=;F}@Y4WNsUcU?`=^6Ok3gA#O0PH|+$ePgI)FVosSQv@-4+ zk;ADiu7jmLxQU>vNNqcQak6|`Qbt5}VmV5PB}FzQOuabjR7<4S+lu)~vceFc*q1qO zABzx6QOymxQ=L_FBpiSpaM@l+d>~c6e)T=TUYzVo++6F+_C7L%tx1Z-&T~kaDZql~ zmmVrgpLJy^BqazWfq-QPE(DUxj25nko=;L=`HMJ`!2G*6rj8`Ia)ip1x4kzL&RFdr z6cQX}A`rPw(9^=_m+!=oz^m1j7k9cUMI0{Y533)p{(1SxSFUN3u=d@-0WR%brpjzF zQ`V=>V6IuHG&-LnE|@J^iYup?c5jUru;0?Z6T8fo9mBhCWp|yxTYC2i627V@HUVunW-3p<~*MQ8+i$phUCd*=`q$cy2FcYV2+AHicjJ&1bBx% zPix;r>w}kHr`HrX?q4A@a70M{%NK{zF^%4pG8yVUtiRbo`wp5u=}O2?OjJ2g&`K2x zmkOy>X{WQUA&trn=>yGuEx>(M%R=-20AS(#^+O}X=D!RfS5HOn!5$);e(@O&x~?|@ ze}*|p^w+#5+M@f95QW+eJ;0B~nDnI+$1lrT9r*eK?P-#^Kh!?=TTUk1Vz64R$$vre zoRx8!^PvGxVWMTJ~>;3fPvZiah_TJ z81s$8;ys6Jg=11`Wk!i_uXZbfr5Xp)9Y?etxSrlP8vwfAts8dU^~-SIBUml%in$_N z$YBTFtcv}Z7jf=990gmmNYd?_vh^^wNAcu0@7t z{ISlWcx)i;+x;p-Qzx5+;NYRKCA5?`rF(4R8#5oB)nN#r(ABgc`i=4 zDe02ls-Cz!>fUvA%i>v5jeYpQUAW~c%3ROddyY0fHXXV}MiE|ANRl>}3vd0{+cGWo zX>7!F1M^ao@~lww@jL-NdjZCqXzZQeaCEa})6M9QH140SJ-M?cuHQ~;!8(?2YIEJgcwVzTe3C3%)<@_1QOX2GUEy zOt7{ct+Y0SUQLH@#ekY=FCnm9QKn^1Dyk&=@$?SE^vh@3;<|Rzmz&~zgWZ2w_xcpodDc;hIP6iXygQ%%=q3!MV;X#`U zn}ZBg6@?SoTuaVF)j-=?4&>jviJhadHtoZD?rBO1ZpUec{P_T=&=saSR1li>tbjXC zJEXy9eWr9TV|_LG1_il0+tnu|**!XJ23s#kfVoUh+zy}(NY53>@=^kAyS zL!NKasz&^)^+ft>eLRwF{+;;gJf+FaRk6ue(y4M&D4>LvoR>nBsPqjb3C|%+SI>8h zljZOZhR*7_-xG%ZtiG@ws6A1l;VX3R*Y;l0m8r(I+YgqQ(tZ);H%zJ0q}Lg%*|NCV zq{qUwYBWgV$0c+1_Uc?Ga62_GRKG4micuzn0G#r`)~&gVesokS)XqX=YrS0O7N3f> z80%Y=QSP8PvyR_#R8d-$`5BhsteFsQu4bKbQkk|Rxot1JXWF)&@7{6~Ax*pGIhmUELMiO8Tv=Yv#>P}sudovvYRk-Zo{QQ~TVI52 zaD2C}mAFV5l_-9g`#XNzW^((6^~Xr6BUOjnc=k1xtdr%xwf1~eiBGc_G@sIpDXmn> zfg=J~#l3a#EV47~Sww#?kZ*UleoJXtmfBo8es#f8YnDJ`PmSC0ji*_&Eb>gOp)N>IZKSQ@*?1P`|dQV?9yX(Uf?4s&!`oqF1S zia1Zw{fZ|1^^eHVw&ScIJkG+3^KZ_1N%02CQBzafmNEO6dqF-+3z?d{4$^V;U35c3 zc>3Ao?ofqs*xrwJEx(1hdO93pe=N>QVwU41QEsW{07?xr>P>qFGx5dA$)7dW{lVEg zjxIcTEmPidN0L2PdRyQNtSoC>uv!^7WHe^xDR+EL&H?lxP^p~bdMxfM5S z!kPuK{qw{m*D4ew_KZqJ7&?ddA28eP4#%-=XfDaR&T$M|B1Uc5Q|8NX=}U3gj+HHI zS7K0-eVBs&O{+?>bS*J(?%tOy6}8OZok@JqHJ~ZDUgAL(}~$i__1- zo-^iC3mi@J3 zamYH7iFak7hMGttp(8Vm4n8W0ow;?$?N-_1A5H#?c0xHHn6J!R-et7Wax^(IQ`D$x zD=Dg!r5?nChu+GJIGt|Prs$PUP5Q9AxuP;BGb^(z$Y6IFJ__a0kp!w~fmM{nxl&VF2I+oQvui z012t=_u~}uZ{<(XS{}7$Z?l60W_NY&$u-Lj5nDY-`unn2y_zsXcHDyFr%iDjn(c zspw_}p{rb*g{?J63-VNrAXbKo$W;Bf8ds_p(@|O-YAEAQ2imRNFR~bj>rJFZw;-i2 zy1H~iKc@iPdXBegO`N>-Me9M&mU+vZ<7%;`Dpjv;w&hgfM^7|YI{R?3iE#+V(y8ZD z)K99d&&gMLFd)oSc#Nq?Y7=xVG>~arjS9a3g016$Wo~Wgig?QfOLA=YJjVv7A9-pT zQ$eVxlhU2161gN(a!OOvTAj}H0rb7v`=5^(^}+QH+^_Htq1!@(yIXHQx2@Z=lcXay z*ta#{emoadkaz&Mhry*DHRC&nxZ!5sBsG)$pVj`)e+_2O+qY$Nf}&h}y|3N)x8##! zSLzw{G{1VAXKnCj5p4Ke2k8fhQSv-T>H5YI9@`!~WdyS>c#aU4q|rK(C{luJ;#Z3q z%nS>e52<}R`9I=mr;%qKJeIYWjJ*Bc&$o-M74*~TZ=QW<@}wRr;T^8!`s*f=H$LN# zRL!EpZq?(Ra+E+zO{4>*HPo^QNXU_GnYdZTw%na5`+ncE_A&7=ZeN;y2cX@bcHpn4 zKGmPW-i#h=l08LV*j2lneOSqDE;^^mXyhv*D4@)C)KmaT_U1`A^4;fiyOZOhJu7O# zjkKFw3+3o@(zf3D*7qEBEr!%o#?Y$1@Mv^%F~ zWQamcB0 zadzE-8OMsrS+v2j++E2m>xwYa2xf3^1IW5xmA2u@WgE00 zP2XFY>MkvY-BPuwYgdQPQbiJ;xNhg3(xr79w%~gO-f-rK`YC;=p~XEU;mtlKp8_wMi0og|gXz+(`*4fNoBlV;Y#qG6HtTkhgryT4 zXa$zrQmOiABC1!j6*-wvj-}3Ca<>jh_=~?)->8+Zk(Ddbho@=A+rwK`W-lOa7>fu^ z_DpgS^35p*raO9Qqxp_qF>A5y&hzG)p^)5jI#Bkr&UYa0=AuS+A#i|3k}{wv%p`j; z=n;xah{0q@+?81A^mFNE)#__^@4iLm4bZMP^qJ}Q5x@GGSFIb$RXd7d0Ba1ya z@gy{D{{T%q6%{FJCfnQ5kd{!ln{J3RWP}Agpgbao9TZhI*MS>W>esFDe{T=o{PY3c z^W}2$*0KCP&+SF6YTq33*5%}F&1%|PQrxSB=I-KKhZ=<_N<|9Xaa18FK+Q_*7)V`s#shPSf(%RRql z3QNHNyM*7nZjN#16WDB!-SV#+wQfmR6bo)L2bL9HwNdGe_I{TrX?E$--AcI1Y2u|j z@Dm+7#_qh9TXDs!LJ|pDyMWB$(v3&j)>QK^%*(t_Mc$rM5(z_Isy$6x5cSefQ8M;6 z$9+|9I%}I#ZUUu!2pLr96|IGjo4c;I z>S!t5+lQeYrRGrHPuno(2`#OU1K~(3830vY!Ghy8L9R78E$Z)%t{Zmt*6Oyc+jGq* zLz6M0P_`r`-!?PJSj;Ud6-tLf1_!+e&JHqFPr?50yaL_n0Ej?$zgWJmMe3Po{dP%5 zs2(l!t?Tb?!f7*kX3+M@^#~$`D8%GFnAl(~{UakXB2=`jj}StAnDKtnu{8%xgr>=KQA(3Vo7T1QL9*p^r@!ukB?ZRXht-MgkTaO&F7^yrBwl?F8Ch;b@H7L^S3w%`O(o#5lpp&BDACaQCz*MAo#@wKh| zjc#jQ&t^Idy**a}(WSs?*q*p}Jf~BUTaqdzMntygq>@ z7gq{gRklQ?j+a@G(}BibNg1U`PzndIfq*wG806WNxo~NgyJmc`Zz;#X^kHMw$GLCZY5G4##`RBlH`cn)2M3DCQ{V(8)Q3!k^Ud{IACW!tY#G_=K* zog*zEnpfS5G@Mz^t86kDL^d8(u9F?C&a6TB)JpO2<*} z#*Z5zO%+m%tvw4P>37t$&Mn=+SD2~2?OXdww{Ly4{{V_T%G&)>;ueFJv{zCU)Er*z zS+33MJz~QCWzIT}FI8Ti<8j2>M)=rrj^W~7LTt?0m&ADdl)2+nH&mXEHCh_t({RG& z)o@^Pok|OHgrqHM2q|y#j_hZy+2sSdY0VAS)KEeekhC7o+F%e#!h%Go2_U&0RqZI~ zK{#5F-M2@eit-Yl3e%*6LJn9}AQu)Vy342`O{gdysL0a>!ZjkCngUa)QPSYjuFlLf za8c$^LDT^l?R*Xsl)^OuS^?kx0452JgihdHahLw>K}NX_pT`8Of)Ew!MSSE0`*6I; ziUFl7sY%t|Fh!Ol0&ztlEx3<06*=~gZX9t{jS0xn)LO4Qz z3BLvrp@I~V-G%vKeo?+6RmXAM9c;Fhme6yct|g6#rX6DzBTX`;t#u1gaYsB8CXy^= zY6%a>mkHl2hJ%G5Q&H$}6u6YPV-+AL=Z2L>ohnEl4y-$q*g08mfJt|%r9q_xC%k2r z84FF4EN4*)Oy^uDTFnnk@i$3_Ld69eWx_KVtzx8gQSA0&8`(Cn3jr9E&x+v+42a@0rt zr)b0rNs_ZpsuUIP?Zr*(_uy7s+#3#Dt?<}Nnv9AeBQE2HW89aC1ZLYKo0L?gMMD?Z z&ZWjZ#v)NEJ@}EThE=f3PGvxo?HFLmL^zWvR7x6AuSx@kv<~@Jp~OLwq=l5FvD>LY z94u!-H+muH(VNr~^t7hEi8_zrh^|hgQlF9>ahSF+MP712ACP39#{qN7x{+L~6`8uW z(E*65VL1a_&*O;Y1kOluM7v2tDtN2P2+b;|Trp!QibMOOam>2Pbv|-~HKv-L%w6MQ zLfmxXSlF=@sLdf=xG5mTL*%DbbEPn%o6{Z2Mk(7aaxG8!@sQfr(vBf3A9AqxA}jV( z^kHk1=k8M-og^SU{WXTr5P?BKM5iHylBp%AD5@ZU6*z#H$w&jOamP^C3^@A=nZT++ zP}Dx`Ee>qdp+g-*jWCA-AoU&il^hvEU=Iolt33x?H0pw)CBti&M5Rb4L;*%?FxpC;p?cJdI zjnB52jTY^@J+=t=R<$O%bm%Y|?b$vocU(<-p0^Y4Yi88cbk?9HMAPg8629KG9xRSh zsV!`6!^m5#wJD}j#3iba1tg_GJ^A8~a#;4XuB4HilPnv1T|z2m%eX1kjF%h$kWD)Y z#%*M35bcq*_Rh36w^^LUtr>!qIyE1(oL<<;n^d7&dfFpKZo6j6`lL*_3G)+@nn#S( zXV*MlTC;j;oDd6w3A@SbLL|2a*0QD7$azVkK+JXlhR15=da-TIBu3Xg)48U)ryafs zET4^{bEnyh8#p%C1S891dHdb9Dl3&O+3y^dO0qbbIFft@Hrcd&<7AISrox8@{i2j= zaLM4!z9(`LhTG)3bVkce+QedhCsO8-PQ7pi?TepQ^(Bs5N|X_|ZtmNcvhZHtiu8g2 zGM1vMp8Tnv4#UQ|Z%XKNT$#X#uEUhXeokpgo z+Hp0zDItDdj7!#Q;Xh^_7pX5Pv2kbOHSVq)<}pOVohFpS2dEhitP!M|cVQ|gDV)g~ zmDk&c@{Ot;Hcg4V_ZpHcTV8d|Dl`u@8P6-f`^5mC#~h%@C7Eq;Xpz|9jnM(LN!@pQ zR?CcfiSwE-q<`#~D>e31V3lc8P&w|A@J)u!+u|z|AH`g86{5Bplk0sRn6K-#=Q-t^F_;B|qjaav(#poB8%$w2bwQZWs zk~U#1LXO)3BAo$9uj7c^{Ek~}QrA(9D*1W2txjC-kIr$!QeAax{{XB`19M@E8VP8E z$$2VCE-N$Bwz%fo;wraJ0Y`2rZ7TqTno_uiFsl|es>=hwo}PCX3Bz<5P)?8*!(JU2`eP(H(%^TjZ^8X;)sk z^fp!NBm9Wmn6JY@7=kJ+An`N!&A%{-$z zO}k>YHYKicEywc5&D|Z=bc!m4DN(OPgN#1y^#>yV0HVU>Pw#K;{{YG8`xnwIOCOt; zE~8q%2(Q15KLNSH9*X(zpEr%GcXExo;bGZMJc$>5!G2T8mpPzGEwIDq%q3K%NJs$X zTm!Q9re0>vM=X!cpi`w6h?f0FvgnzH?n2Ta!r!c?uPXtp+qMn;%KUo#xxsu z7YRbiR+?0jO#lP5*Avw)BJ#JxiO%m)JpE(aVr7QMBwR1MXJ?iZOdE>q;|Q>9Kxkm9J6j---u6d3ch8`%@9;?GEzPQ((4 z*l(v_(OcA?t;>zKd|zyiaTfz6XmMNm_RFljbwF3c0wTgz;uY7;RT6tD4RL1OoGoXp zyn!rZNyJ8s4{w5u;{i<8nEREZ9C?>U;|J35+?zj3*tDK%XvJN=l9o-(N& zT=x-4DQS70(Vid3#f7GZJiD8YnyFRQ{dg8ppiCn{9q9`>Q!f7iek8iI7A#0}&MQ5* ztek2c2~aBIY5)q7Rna5A47nthq_&k&&fi!608;+|NPdf<$&JfgogU@BlF?niiR>yx z*Kxe9Dk%$(9F)gOiU{)`QW6hSipk#aT9P=W#p(9&`_C+%u0Qh)dYZP_KgV1i`4lO8Ulf)tj}vMZ9o!xgH%b^Cq1fUf0AJTzao`7hdVLv?R` z<;dHbPU_!#vg5sXMa@I!_Z{ABn~e9fC&c`8R}5i1hi`tB){}JO-RjbOOHJ`%wD;16wT%}R&pFJ>lq8q5=9cV^xZA6A z!*MzsiFCNKq<0qQINd)CVy-GPXOAvup3MG>A=n>J{x@mlDIR^taXQ#grBJ)W#1ZT` z#qTn$_IkH(E?ju8VbbXjl{lgftj}Cfw^HaVi9ISjSmuQRy-m|=lAx*tp?UuRvT*ot zW3EvY3)19SmtRT|q1)rCT*^E-S zZF*wgz5f6&+RsZ~n7uA>ccgwUiN)MkZgeu^pEbHSM6!k4Z&S(w42eoWN@@m3NL{M}Z$|vE}>E z)or&GH5bn>VeGw~{{UY1{mx0sJdl1Ys{7B@9`m~wV{A)18%Exwl&0e%lIN&)5Dgna z`xTEI-~O*>G2XD8_W(q6Z9kOj}_T-wIWvUbN zrnqQgu!6FjzcJ{aw-DiCUdf*=m^r^-9{&K|TV~e|Y^$+_JlSS;ccq(*mXj-aUD&fW zKqbfer1v6p#Avvp4a*lx@R>j@1-Bx_WVzh`}h9j?f7lm!H-g2eg&4=ykW;X z9VlI7V~k|3V6}pRkdj#7zVe&de<%RA|l(Fdk> z#oyJRs8#c>`+`#G;tiPkp;{2)GcCw{rTOkAxe})vN&Z|#^AS22w`8kI6<3`8yZb|i z#zi(%U~2yx%MNm{%qTh6wf2^m#NRZq7SncJGR9Sh72qO)AuoOAUr z>t~%gDfw3(UgVkAtD}fpj==e+6&gKLQh<$A@Z$lMx!=1qybZK*?OTmb5bh2@zVBXD zUgx+?671Fhr8ZtrR+oTMQc?|S2X-~scKI>r{wMp4#rw{8E8vFC^5vOQU68pKFElQp zOIf06(t#rji?z;gfeudR7$UphISu&^ExULwsYfSPDxa|@8jL-XF;$U~%irYPBdA)- zv3Gv-e9Dp)7Bcfxr@=@g`w7apmm_q>##D~1j}v0c%GP=>Z}Kcg)8WensSUp+fjUs3 zFFiv=cZB0HmA+oO^m-dy9g74Vo zHdg0m#CVZ>{zcpyh0zacL2?T#B`J8ME!Ah#48X>_3u3n)i;0WQ?i-R`k?pg%GWUOU zT(=9$Sy8IewQE_gwBp`2W8kP#btQ6IvfAx#E4En>+G0WlU;sz}sQst61-LQsu+eOI zf753p^4qC_-)+?JWrSJGlXOz^-+k_2CZSO9rYd*in7_HmBAs5My9%&mKF4&jF zchZRWb^SyX?66!KZSn__{l&J_3PZByENEpk1eGa33{cwIv-5p-Y!;=8rV^UZvK_o|M2Q;SmgzSDJE+j7wOB|$e!si<>Gg%sN$ zpJB$M17NdZdJ&M^JEHzW^)sA2!|H3)AF3C=O80mhV~^s|Hr_b0*4?+F$IggTZIGwU z2n4uDH26j|`1lCV-O6uKp|rT6r(S5a9am|&e!A28C{XN82WAfF&<;~_EuPQfCK*B{ zPHDG`U@2=@OKda&=quZX%_L5u*yz4*-KDbQmYQ`k=S4{?eMJe5(o>lxw5SxrZd^pR ztvNb)g6%#yqdU%HDZbzsc=7(b0ol3l4_(VFi{ms+S32RCTIV_P( zW;Z_Mg{ksEInT*)H5*@U%IrDh)M3W2hw%OyaAKXCf9*}G<>ue$rpWb<7)z0Nf3Cgg z(+pqVj+0jnsh8XGt&A^7A-2$6BdAKeSis+oMn%Whm36No;m5_UI)4pMOvkl}hiZWSp+ zp3-rF5>G~sJB4fn}*#Fmzb>q;$lm8B&$l=DD^I4kfbr@ok^-FJ(2Q&H8I zTTah*{I$|25I3IZ<}XvbzDE4yt;;6;HR4NiWNJhhO|1U_cNKs_TSy(*p3-sWdE2ea zmGft=Y~8(TDOFJyN7H|#-O0SAMv1enX4`CcNV#t*ER{I4lj1BS{s*Q8#n|rNb-462 zd3)`wNOD?Q`JE!%v%N9;3)<_Kx@9-Gcl3t*$aWMshwk%TBjR$O0&CwuF|o0YZp?XN zv^?u^-R|CP$(&L359=eB{Z3w&Yc4Lsw3OjZr)0uX+m4h9(7vF5QFQBEu+C~al>D+} zf$6u>CHs&raAjPr%X|{r(1kX9tu+Mu@Fm-!P=!xJ-kyCU*)~fsD@#l%RZCrIM2wOKA?cE0D9E_(WLLau;FH1hCz>*3BX zTVGHvZ}qpxZ|1=iHuEjismQ<6H4ND8`8Y%E+22V?lwDa7bgX&aJ@8rICnLjR_)1jVHXX; zpdC<@0d65ssU$2NN)^Ux->7#n4D-apo0hZHGHH0RC9`9iH;9GdY zPb6n^_Tu9oMqGDw-@J#rRdc#J(v!CALr9T2SWZa@uftxa7yDkpJe!IsXa%3Y+Q+u# zBgW^d&r;j>)Nb~?Zav#{DU9=qL@QHDA&Bkj(uSK!$eMLH`UAIiJ2*D0{l|iD`}OSW zw6COOaW5X=>YS+m|$ro*kC>7r*JH^z6TS zgWZs>muNCATxDuoYuy`xNF(N!g#sfqwbSDK#CBqzcg;%Wj{jpg&PyX(}Z>(G8ibtO1-*RtKAmKh6<&Rl>;kI};iK3i80W)JN4X9N9KT*7|$;Ou0Ds zrXB^jIU{#(kw)cmR`@_En|Ye1LIbQe@mG+NH0l9K03kK6+wmNw)t?vpKHs=6a?em) zu;R{Z@mCk`j$Yn&ZI`laZr`^B?aDPB6bNYon%;S5Cfh(YQjhV$dAp6f5UiP9je~OY z;B;qjN!k9Gw(O20;_IUuiy*oVtHb5P52S zGHgAp$N8(&cPm9i&LQA+F!Jn6BUa?yF95q`sic|(jb$#q*anbtCY8IL$<941dP-hC zOU3=XDp=uJjkV7rvGQjiZmwS5`;U-!oYOmSZh0&u^ z)(xmxx5~fs?J06+D~_XD+zMW$DM5)YN~}o2h>>i|qll@Y%D;XuFdqW9B3w%sv)@Lh zbL&iRHva&G#%J+mvDxl;n>EJ5!d*6HZ~R0@&s0_N$g(q1C{NprmiKQ|%?{r`O$_W$ ziJ|QW?!$3-54^{hE~dP1e)b>{immS-K<%}aTaR2y8bX4!sFwc#rxaM!F0Sw`3A?5x zzTWwFMz<1>a;ANm;GC?76f!Wc0zc*3n%axxAw;t%AR#{7v&B|kUbG0twaPH=p-=Gb z{(_;Uw0*-BS9k?RIuyC5mfVK(rjvmh(2n24bTic3f4O=~;h9JUUPR`rPTH`cfGj#) zOt;Pd00JoH&f5-7C_)wFxVm|3r)kB0<+!NJQnwNWxwWm5t$U6)%`{T`OGv7I6##mJ z?Zpm$WM>MZ>jDj3)b)euD)jL&DkVTxl)dkBJ{E1O58zZ-2>?6XuX}N|Dro)|iz{?pBT6xW>Fgb;zHh=@fc7 zJ|RxZ3?hX|9P}%e@6fXRS4O3(pr~~Qxpl;jR1#-5{ZVW-cShT|vf@Bh#gfscN<@~9 zrq%b1J}*!#$ksJP5A@}^9$(uYsJ@*WnsOk^^&x9|{lQYC9Sy+c4Vu@@#cTor0&5KU zN}+7h?pJSx(M zmf^~SJc;i(ektg{)E$RuR?S4#*>VS_LL+jCOxd3tsVfBDj z+^o$8JUi;QY)*F8Rh3JS*Y5j&6W0EnZ|W0vE^8SbShz;JaUUb!wq5$H&9X`?dHLym zqvL7jh{&PT5TJdy(S1Gc80F)sS6?4_LEd{WLi=>0Wy|C3)ZF$PL2{}I8ijfSGOj&N z%#S#`8ZvW*F-h{4+nyey2B%a)D9X7l5!??UE;gV-(xIxO?ZGix5ys+)&fio#b;ldp zB%yPH+*zOy6i^UV_V(htH*ST+sWdXt!Tw?3r?$kYk}SnH3u|goLumudXai6^*umy) zsxovYcDahep&|=`E;y!~N%oJw6%nw|)S49A+)WxaH2~KyG-i@IW-x80;`9=CV)S7t z8+PAta7?v2mSmHvi6cSn`wlJbU-JicR}^$R(gyppZo3ud(&se32E*hfDO*DL?}*s% zh)?M{lCFqZpjWpWt(P)4ZS%rYENb>?nX%!S5Tbl zkh7*zG94aITKh&CphD1gYu5%^k_QW^#F68xXSj?Ia9zQyQ6jy^0~`e6m8V09)}GKn!=mUh$)J<0 zpc3N+NvBkd@O)*Ush}S)DIhF)gS4kCI8v!&Q_4VngrUdSRB&<&s$xPNN@`QkdvKQ` zL6IJ4(2!x?A9*1u1+a1?)4Kw46cff#sBoaV2ytaaT}O5koPp>9K2|+al!CGGR18Gj zMV~Z;iSkgYsCIT>kjoHa6I0x2!cvfP%h`z>Qw^RvYf$LAOLcldrmn$-=_Ez+&`2;M z%X)+*OD7S^;zV6YAx#!3L&oZ82e$((kaw9M0XkeIp;S4aW_ThuYE5e*p(AaQ=5$meJ)h+EYx5w2IT;fi7K~-lKV&2~4@ej22sHDQfH6 z_hM^Sok&zhRYjI?2I)`mf_2KME z#UW-g0zgs+i?;#Iflgjx^eswQ3(MPxE(1?G8R0grUbIv2BjAR5IY0cN3r^f_A`*lU4H8a5m8Mt| z6K0j^`pLd;l&aUBSl;*7oOV_1)|o0LG1~n!k8l{+V{CR{xB_0iCP(sbS4nCm`EI@y z*tZm}-)=5rj|8+p=^2YEL(c%I0yp-^CZBd2(@1NL2Na8}CL@N|adJxgLNXtD&k?H) zv9L^O%($5bjJ4&Xu=ON+a8r(4avyz&(1h27=eCV+=)cEjr5!PgSVX?k5DLQP%WiRO-&MHR|lGYl2)- zED{5)2sOe`b4bsbpIq@coTu84QgR<=8pP!m?G_95>BOedW|ed*GzL6(o)!1Dl?*x- zWcU!>zitawN$@x?lV+t4N!#Mb8F`sk2iS;|j`AE zR70VwEXSrFO&1<=yS=%$BQC(=%L~D~oYd+=mnmtY#3mFdK%uLv9|*{1a-SCypI-s@ zuJqQQ5$!|i3+OJvzBdK8)Gqz7wI7Q3wbZl5n^d$D4sF&k8eHx0o<3z zd{kdbj=0j}voo1|fwv|3rqJV$J{tn0l5t^8Ozr#SaNWG02Hf128S7e zBDA=axp@mWiJ-rAHs*APwEzGf{mpZMR+Cr>RauH>EnU8h@s9t;|oULoXSE4 z=*LW%>PY@+5_PVp-;F*N(Tj~5T8c(%D|X7tf15WidnAchW*RGT$AwZeT9sayBdEYS zNNP;p7S`8B-7=gfCfXX0;fFhnbAc6dKQwI)H?}J8yzeboBdi~linRqvc+#LDwA7M& zsfzrq=GBGjWv&**di(UFm7LS-Q+4tSk}Nw7hqmV4aH$KKs*>a&nrEiE0mc_{?rXQ5 zAlgTw?LDh%(yaB767Z)3^1rB#LA3G56YTqkCvsNgT1gnoz*bS46%f8`lrL7IGQ-kiq zSY#0%+-}dlY8=Scx#Orzv=E+IRY_M~+;FK>C!uN}9J{c|Duq-ELr7^MZmffWTNjDV zk69m8Uq>E^qs)QG{Ep7+mbkmX2%S(c%@9_J*o;rCPC%W9`+$PSsTxB^j;d6D#7dg*3dT99=Uq~k z;?$)QRH8{I4vZ=TcK-k`0lc?L`V58$P@!8Z)ePIvrA%NR)e3$1>y)9^Wch>gYHl+9 zia3RQB(3^^R6t4hW1tl&Q62I6Kt?ZY9n$uVMD8aNf$ogi{+wjHiu9n`UNrs3v;97> zu3+ot{{VYC+sK}x_?Ocb#*MtWO6rFGWRYUpt>|)n;q<3bVmnGcJco#%3Pxnp8P479 zo4J`4UVKRVuefdD+m*KUeg~ZkX6f1&zf+BJR>!p+ld>#&J)Zq1(5PO5I5<;ClOEvmJ)w6ejd6tBtJjyj^<9bo0Jev@ z7wP#5D^{M|R2M;im^JwG?8KJDsF!|{8?juRaF7~l#S{Lj2{>3Gk(ZfR{R%QAdVAR} zB?U=*+}%i}a&qk@V4tw!du|Zo(F84AxYS^qWcj^0Ym2z>y58yC@gYnAk!fLHH8DhX z24m__KKf%5zWqwuuqve_-;zB$wEl_jd&HMc+lqcHsC&!SpPA>vB3jwK*aFoTdJ zP$-_kjFj?Jo|?1KUAtCob4jSB=ba`kx}Huv?+^gy>4@l%SR*oAwyb^xMO9o zb|&w-N|x=ouQ#FNUFrV-V0(LFj<-6J@{Yf>vUWdDJ6_V$FYXJ2m#GLEmv3kMG*Cuh z8db%8;~w?ezxB@4eR;Fzeq0ZZKB9lzt;Z>HU@n}0X)4cvPvs+rVMIRFp)1WKb zqBBwVoP7tdc4pnS+flP^Djyv8SAOoBwa$E-j!pYT)LhiC0H_`G?i9wZQmK+OFO2i3 z!=Uve>KZNWS#+&)uXZCu(w#)-XCw@jT|INcfrfUqw{`Lz@vW`LJ7VF#w=3l-Zo_7X z^!Wg+(0PH7QqZsFaHT%n8tt@!r;`}1OY^aQnS8q@)ZXy*G08SROj{{FIWT0(iE`X4 zW|bf|h5V~4%POtI?Ciu-xft;vw%p!{x}RM=PyIGM5;^|>*qe<%_#yc=5LTW~-A}VS zQ|Sb2j?2jM+8JaSX+Hc3cXeD635LZfKU1O0pH?4Q-f86{aDzXQ9@<-Sg%I{NpZpU_ zK@`fvF%X<4Zo3U)N3Y@VU2*>I{j(-bb5C5fn>ICzg~Q~8U@WE4o2V|uPKT9 zj$?zx<;hqiGK!I?ic|TqgWNmnDqTAfa*?vZ?WZ-(C(9pc1#>UfB<)nQ!G43duyEz3 z;Yha4wYMU-ciwkr99VD6l_{jSw4{s%FDA}%D|IPupMz^>tbw-h88)>zAJPbqZhto$Rt z0$#t$ACF*FYn|03!LrW9}2CvIX}UBlGFJ4wn6W`$ehn2GA=*(4%Lg5t7=3^ zX5AUSi2=0GD1rHuEAGboY1r-BimIzT!*k!KeqKez;O|T~JPt8Q6*^5T*u-WJ-t zru*n=nZ~xpDyHfqCa{#2b* zInd(|x$iQbk>GkB*V}N4td&_;`2PTa?srM?PU4E>%bc|Z#oKYxr8w)VI=@*}G^sTt zobjEVw{e@iOHj2RRSv>%KcgF5SPk!9S23&`e$13eUA@;3%w`)vsVmiz&ROEm4{n0h z>cDpm)zQW}5&rY4`86Js|aHw)yQL?c?#snRME+i0y^b;TtJ8L2>GJlpZJ4rjod3mR7$=+kcP z4@{p!`!sFUxZSb(n);~9hggx)>sK9@!xAMjQ$`S^kD9n4D$=J^HK^1iHiJxgR`I=E z&Yw=jR9BDl{tNoE&~|;AIQHD{x+mf1?DsA{b@wE2{{Wgf2~rvIY6d`3p!eb*l4MiM8=j;+OmW|(jyj*bcZ}pFaA{`GXhBb(Dd2)q zke;+Bw-*~;&pY=yVyaeV=R(c5DKo?0tiM;hhw8VJZ#%be&ndSsR-05GF-VI7HmU?5 z6HQd=aqZg{%CT+gwLE9IH|v>KD83T1ai+|<@B8=5*sW^4O=J;nZ1`iL%Xfy(>d8`^T8XJ7nw)xtt12pp!RO`k6*qET zcLyTx3r^74_PINb{kUeaZ`*rr3QN*17YR;=nrWuTRVhZ5GyqhR5(xxKDm*hKF(+|_d-_d^(ZboK$#OLd5XT5Q9&A5_w)uq%a$lF5IB_t^e0cX!zXme7? z3R-cSwPobp=5Iv!eZJ54Ti?CYUY;LsxBd=3&sH`Tkanc$x@U~X1Fkc#A{sS8Bd1Nd6oNx- zq$$T0OpuasB&d~N0dcMKGI;erzqaE~?XOQ9UnvjqM+b)@Id*7exc5@ECh2%}L*puN zsiutp2@0k}U|d@*dgbvy-Qd376re@>)zNS&X$1)YCqBX7gXJ(TlP&GnhOJuz(Wb-Z zZOI%XdvosXmluv&T~g~?V{XM*l_ViUG`}QiN~uZ_LyAm%=YMf_V)VAP_rKz+M!k%; z+;HP>5l_ZnZ`;JLR&}S$Msyitdy7QUMhg4YmLF)ltIA(MDUHtDWwVQEv!4kKQ}a*( z@5b91Rt8$`lOwKg0n4SONAeQt9ZyGKI8p3HRB;=6 z4&-GLapVaJQ-~mfE0zOssM~)L)2>Wun}?$wEt^ET%zL9vsl%R=!jzO9L&|fogV~I>d>o@vvdg0-oelir9dpET(tAM%6m)dp8E)j1s&;?T z7dbooL~XYwMz^5?8fcI;1fOmIhE(j&*l+gh(YXVx)}7^Poo;YRQ1YH`i&!8jB#el! z;fpo8DJrHIowf zrbj(d#12Tk?(PKVZJW&8c_DVq(YG!!cMjN+l29AFTO=XU%8Cf^JEud5u4z#nrx0gh z!zRqF-1uau{oa3f741YjM&&#_r8xb+d2l|Vn|EtN=Pi%wn~Irk9}jxhvBl(Vp#`6Y z-FcAPEN(k`(vSjF)YXSnlb)xQUYN&hSrh3yHx(sm^1dG0ciOb zvL1oCpZulBmfK{x{!N2?wA5J#Q3ylm1hTH~r%&6BX3LV5&R;VnogQe^?~0|i`)o)i zf%Ir`9Y9xSPTXxw>qjm9dU2!Bt~L%&w?qE`4f3j*njs?ubjq7d?gBKezR;Hx5qqh7{%kKK8 z^sBb_J4kbWT zR;N5^tkI&L?t}S>%*aQUtjfBBo(h808bxX6t~PISSSGEVNO&P-mlAVIwB$0v-Gb#{ zmS*|&JnwTY`*v)7#c;G;lCs{VhSs`}Q{A308?>5}Mdh37Iu(xLb%AwTo^x6pUTQS$ z#%aiw7`MgEcHK#o(^{AM^~*a))VCYjHa7Te5cx-Guv*yr%DKyE z45LU=y4Rp58_O51=37a8Wo6o``5f-MpW3dIEIW=Niy|APrvV-$Ejf1fdvQ;4)gLUh zWDv-p(A+yYMZcYiM_F|`kn%H0_tUokjkLsf*5Z{)eYhY#+CVxF^B_C zO6?;NsL-85L2(X7hRPgK3R&&$#50bx2Bf6M4F^dW+IR+#49%_HQQ5&otlD9)kP??3f3vq(ubum$;1}rstD053O&{Ic6I$xR5js)TenS~09TnUZP(Ur?t)6i4*p30q8$kqQi4FA7$aOL=XNh-+Ox zjv}3#Yrzegh_<;CrK8FvZV}!|%Mr#fh&yVa@oiTp0Sk_%(m&oPP+{_#M_gz$ZE>3? z&vV3(XcL`Jb~#)k=VM{|Ygj^v6*=Idc!ZRC_`ck5^Y1g;juVtU4h*@@B@H0aT@~4d zqZvDqi7sv1J66Vs55(A$t?>G;eZd0U%2QfY znT4}Ya5$UhBHn=#$%5i8tAlM8H8`F5%e#MVEuZfpL~+~1vF!4rXchKX4@iaPw*4_| z4QTZ5x7Ve#ZXQB-0apoT(-5e2p#JuU4gktPHNT5hBIn4LlKXw&?eiwuvn37e-WuEX zml>#P<2?^uq>pZ?8gs#MyG{s>cru^GC+-t&ob}50W>p{j(7RMJd@yb7u?eL1ctZ#T zefa5R%tf|YkA+Cmeb(`KaZlZ@k}gi4iP;ah^1k;Ja3i@V;!LeVJQ4^1&<9lk?gK12 zlIm|(z^ESVHitIPywmx9+_0)N5hLwn<%b>02$Y3&H15YyjFX^YEUKCG?Kt8$FikDe zmFwGwsG*f$!9WhQ?!%N$k_AJ`IV4mLhde0~w?akXt|`9rpCs8h_Q>gsnVO_uu7-s$ zZL#QIeAc;uLQ*?aM`kCpj_%5VNvKDd^d+$NU7p(?W$|Xhf-RO?Xky#9;G;bS*8p-u zr}H?nO4g&9J#nVTz|m8{rk(X#p5XWv&9gY)fH>=pwmMs8)wJz#V>{)LO5}NAZ#b^? zDP)pxed`nw%<31v%34z4StQV!pSJ{KTdNz9B#~Qph2^%33ff6dRl8s}WiFpA&|?bnME*t0-niFZY!WAz1?)vHZqT7a&q_M8`Lz$}zk0TTZJxgtjk zLo^@)GROh;VEDvmIw6~-`PnK_MMZL_b|rSu`)v$si_W17kuGC)_7W=Om)uJz1H6?H zmI27gg|$}6;R#96BB643son+HZpv!sy+6(uc)-@nXIISV;c4vuUxhVXmNFU8Ku`$HP>LXhH5ZKI~?5JDT+Lj)wOG zw5-~ro~)~OJ+aIj_rGCneYjoo2;te{(lP5(zIUFQ;%ZQBj{8|qJKue`EL2wkdbWOezCWEexEjTo>Ph9+e@rm zcZ9?mETUADYmEpVib_1icPt!9Z996>pSAx0FF!wdGK#Ghzq{@4{0~?wB}ZB<)a8IKU5t2>TC(JL!u9X_II%BF+ufFrDE-lAgaH&gLi6p5+l5jJXSu`Y+bkHFvpa7&A*91g@ z?5Z~8EU8{2P;oR-QFZERgd-Jf(O!CdWdxrsD;kwdu$NsTOKmqGS#7B|89?Vnn*I2R zJqhXRHgEz7=m+7BhY|S)dp?qvNcL_a-Ll?LN|U)?rF)YRll*aw?nPUvdyjGYad}(Y z>ipmDWqX&lKyWO0ZZrXO+eXnd=Tvb_u$!q2w@{`de)uXhoohm> z+fz1l_!ir2CvGYjF0L2B^Dr#L~-Q=2Y`u+#GucvO% z+?xy0C#UVNyX=y-&gO?_QEv6bltcfonplP3kEP_w4VwZOA zTns4I?9!osvi7FwxXCLPoclH*}j;*ReFV znYTh$%9oPs%Y9yRtx?62rJv1DW;`o=`iHq@$<}Q}_$?2xyEp#7?3-Lu;I}1Sjn{H*MN7^JU9o+n3yCyO-(?&9p|j7o!*DT}gh~z!ysoBysNC-Wn$0yjk(*di{Ri z_o}OxTw?qa9%kUR29C06S^|1w(JW$Hw5b(C#qu*QU3HsmCj7$VW~V;mi!@bAbQ_+3 z>MRq}v%dup%2gB^eIW<3m==WLX?K`Vnhr>o+bF0=Do{;HJqLNurV&M=HzgEV4k7f7 z>Svz}S$i*xq;2h^BR4qz~EE zF=vs-9*TI5*RX8p3Tde@p+`VgT0@I1qy;P5qNDA_ZdUU*q)RM4ipq7Kr=E{je^l;h z-f~{La>pUPIs2(Uw^}^axca*^wh}-9SFmC{D;IKQSFH(mEbBPQMm{GquRf<#5H*~(<5CLTJeE?z#EAw&!liQ7s;t*Kl@n8* z8*3U@9Vj;^HZetE-TZ+r?(594AwiQ+NIiQosN42s4Ox@U-(Lu^J?-mAvk8EssMM7xk=mkjI4)amJ-IuR*?c2p ze}p{)aqKP!+BXs)z61g_cI*Q{r?pED8$tIDVac#juiiKQ}t4KMe zCp>JhG4XL%E<9@dUx(slcP-Z=A=d1@<36>0~ zO#S<~%95tk_1606E~Kd?Kc>M>iclQacjaI;u}hI_22K4(LjJ2xEJChP_;6 zqiAt;t@d2Mx$>51Y{7DU`r@_Z8;=?&amB3nAtd68_G@gtwS<%^$AA9-0`AY^h<450 z>!W_n+xxEi>nCLTFWJ%JG%;@~vKx{V}=&maj#-3Sm2j z`h}zg{{WcoX-Z_wTe+81=?OK>H5JAVUB`PaT$pif{{XG<`DWDm0k~O$U>MASk#pcb~ z+|%l0H*$W`k9+SA%jwuk5ne)<8*nSqUyCZ{!EyH#3S)%ltF1EjfKJZdUNz;xHs$V$&+mTzQq9{a>=7@wuBmz z>^>HO3zRuN8dlV)j4dj%+9Y)SSJPiHzaTf9-TaBVpSiU2iq$z`O+kdoty_x4N>HD6XSAGScMSPmmzn8XPR60ZP`A7b>6UKb zmm1l!tijJQT4!$8n_85T1rL-6A7%%}vsbBmm2NiHoKBe{)xe&M_P_a)knRY%Zzs{_ zMY4q@`-|`C6dH3W1ooboHc*P-XYw+oQ;>eBy>0SOKFgD2Y16khIBaGj zw18HlPMt9{5np#NQXb+TPMe>SZqVM70dBP=KqZoeXh=N`PFRQn^Qqp?OrC{dSy_c~ zpKT@dB@1}16%DC5<*UC8>#_7MCn9r)8~S*+Cdp}Ko8IaJPwi}kXlYt#WBm%6V0J^{GSvBtYB0tJ$6oP@zHd zj&h?d-=g0`{PpXfmKows1lk|I&y0a1d2YFme4gIhHssMun|7R_Qpx}|Lt?Hu(IT3Z zkoPR>c-Gr)jJMmi$=s5YUZ30c`*Ayc$eyEpGW{qx_i}n=`gz}3+L-J?mMl{{{VEjUiS^f>#gGJA;}jhvRzy6$dKxa*6YqXRHdy)H7Jrv!8jPP zu?|(i5Z${g@~b;@=$q-Cuy$`7wdewMu^Y+p^A z78canw&Xc>P(i(3X;&mUqyyqPgbu(llgNbcO03(@Hr=aqiFCI1-eNn6UzQwn(=sqk zy}pGb++}i98UCStNAcgK-Z{B%eerZ|GTUup(Q4E@`I0F_w%U4{p4>@|fp+ZFVyZI! zbRibx_#QHSW_s4%zNELU+jMUms^VHyvYQUxZNfx$Q}CvhrlLBWdoIPXM;aAtQ_8o# z;Fe6eR|HfJFXFx3&lkrbMr_%(ZLL0S-r%rO(@c!kw^xwJefY7=%ktC{4-?g?J8vER zx3<-yR}%0MR2{o+L2r8ULs>|c5D7m^=Y) z^y|RlE8Meqgd0oE2GqFgS~NL2Nltq6IzjEjHtbnmda9zcKX6F$BY)lZt>3t=`$D`w6!rW4hh`?uO&TN3WTaeKL47*e}!)`o}@sH{O zEw%+!*YU*lpeef}SZ^VBuPLO#-*$3~Wb3D6J#X&rz8f^JVxM%yF~ zE8{)K%{-s5c$a6gr}r--U1C}-WdNwOscX`=h!pU$*i(x3B;XCtbB-o7uTO+ke}Z3o zqAm7ZHGcAo;9d`@*8c$7w;xcp&J5#4wm7HKb%x!rc4I8l$y}>@NLA7Fk<_^&h$QD! z98~rm1mTMB+}3wrGyWgMZ**qDbLm6ji9&56s!mzdQyz_tJxm?w78_|e0c|y>OmBAM zH=D$@`%&6?IeS6-F~7zC02!6Nq_r8m(w$G*-K9rEjDGx!eXf*YU6_GJJE#r?!g>?k zOnY)T*RXd2fAu6Ew+!CD#!a>_T}m%yy`~x%LRD4NRCfVEj1;ot7HQ?cmDPK1aP8Z@ zkpuivS}sPZ6jqVb-F9fVcf9Mtcrk@HZ%KcicQI;SzT4O zbJR*xFD1f!DJMzo9oVXN6Gb!7l(zl&+}T7I+|wzbK2|{@rk#fsH}L7eBHiZ6`;NE$ z_hek-T_HMSON6i0Z9NvA{{X`jITj0yEV){G9Vpv=r{2F=*mf+{Lzc=1%ta=;ibh7A zwK$@UxkZ`L zC&Ky(NdDdtF_Mi=SbD7Eg_2@oA==nth`XhVol zr(q(a4z(#lRp3(IkM^FUJvnlGeo^CVH2(lNIdN@-wQx;*Ns!RePqMi5j7#yk6w#E< zwBsT7%6lRwBx1dUhOk2cRQxuyE0?Ww>UmA7AuL+UZ!JQWf0;|VAW2z^G~R~y7KXQmj3|B4N>SFhrcX) z{X3ro^r3utt_|1IcQRb9QRFIC*l|ru+t(mU+L>yafg&@=Qfd?m6ahWp;%>>_dic?8 z`j(83yY~K#_eG=C7w3Svt_I|V3(J@kl_emiwV|hOJx3PiyiYyIgQ0j}+>42BLkiWa zs%zPc6w0Z_gQMmexW`;s2@6A`N`W87xSH4mr9+rrw0$U}ds^h*w)7$M%mu6jrlgQ7 zpT88lw$s&BM3%D`G3V=b?W?}hy2`cP<0>qO$|-RQ1R8@(dA@Emp)FBsY1IfRc%>yk z>Q8+rZ|6ociL|A2wEGq=rJ`YK6nNL$Y(G*N-U*Y7Y9fv=Y?708G?V!j}*8 z<<}ePLp~L+tpHxO;L5n{%l5>&NV8sIsVzrYOqgpGPj*E198tSBEzQP6aL8ysGTUI? zQH!wDn6gxwmU0>UFdjvi%r;FlL^qwiCfNGfklsL@3#LSL#YRkd7#+(BqfPi_2VR`J zaST`;$SR4id948AD&j{}c8_&1NetCc8YHFKg&LIPQgXxz4U?&}h&hydH4IAT#?UM0 zP!OsuJ;YbphQ{MUrU3L9Z}{5=Gl}Y?{{U79j!uV1h>-kAO$7y{NJnF-%;(-Pb-T~Z zV@Z_egBxauDt&~h$JlZZ6UyqsgY(iq#1P zKxT1Ua!tsllX78aWVR|gllNi#gQIPP{{X!|+QW%>22$Hic-(%9?#mGi%-DrKXey!> zPktx2Y&ROkID}%!Z4QMkN7`{D_!Hc;=sn@w+6dCp3Y2?sS8~ftr@dvSVfhl3;sS|1 zxRa3)!ZE0p?A3~e94fRNttJ4}me8$fSPJ&xU9n|pu|{^`Ps#>bPnhBANWxmj*HM;n z9d+PGl2*04rKnJx=}Kb43WfEgk_r$`bi;}CHv*XqA{y8rNg3gJhBj}SeV9?L8-*(@ z>s)Zh6e>h?YDg;6--xcQ0dB)V)e>`TlA?Ud4~MVeirHJJ1nsd+`QJ3bqe%-R!lE-6 zlG5cH2+@qG?YN~WZjzJ^s>Pk6&dc%|*@y~%_M2OL&p{kVxMv*JTE{KTjCDp6mRhKSAo04N&29I#@>WOm>t zt8htoSDNCAX+)!-#F552aHxwS63HC0*DT% zYEjY;e_Ska(^aUCDIv>a+Si+_fflPItFxb#EJk_cpS1}i@x*hxY9A9La+LTJO_zhY zK2u*b?UkLm5dy9~bCS#;2WbIhtMI^AH#;5+8hk*7v@>Z^$aT~{IYno_^cXs~(m%0+ScV6k6cv9=; zH6~N5PyYZT3JCl#ZI=8I2CRupeU`M8rfQfJ=#&CG@LxUE!3=#$jZ5@PG(@1EH>gZ7yzcNZJY<<2aC`sD1sZrU)OyxZ3h zfa8C<+v0~Mz>q3J$CkFJHF<)%MvE6Z#|r9G-@v~eQf!yFkLww<_}xz&1;sKX0;cv=zA}{{l0Fw+SqTH} z#jJai8Xpd(N%oF(-DGpKbbVCrd#30iT3kF`vs@?V9k<38EAxn_YqAm=K{f0Oz>)4~ z&VH>BW9-YS8hl1Jx3BL_+d|{})}6_Ey{T1x@j5!@N?miVw-rD;bf$yZi)&rihXq2a zU5&ZUULT77W9swuZQ*33h#P-!+|~I(j>BTV`=Y_XQty0du*+Xf^6RVpsD-y5)r!#} zOFMIpC4xl|x$dPXuYDRS&jQ}>wf(5Iwe;40G9XRa^(W<9`z|$Pa^JO(?LwCxI$V$e z>6^rWN4pf+9p8~Y8W*_TgO6{Hq!_)+>K~sjD1~dX?><$zPC8@R?XWIa1TMK$GX=7p zBDs<&?Zsu>`}5Vd%ZnQ}J+(-rj*j|z`Uw3)dD#~G-s9RfpHKTndV-#~?zyA)5Xl)W zHtTPgvQP4@#Gd}_S6RfpFK54B*uR-!*RushtvtTp!0x}M{{Z~89+oy65bl1Zxl3{L z)%~EKnA`T}4%gfIfz^^BD9Ttp2wTRZ?Zw56PFAYxPq}@)^#_()mjsvX{r>>MhjaKt zgZ()AN83+ruTMNTx44ql`PDggi+c=9lAw@{awO2a;)qBDsYIL{lD8T~Q4Wo_IaTV} zI_YsK*bS-$kSk1^>xm(<24pv1jQg=eotUCqSjLfJ>>F~$v*GB5*O?}tuW@pP z=M6Kd>>k02JDC!=Xe?M!LM88Vgz9aDR36+6l|of$M-yhfB}Ge7QjTC@sS5NE9wA4i zRB_1@DWg{4t!fJ*h|B#m!no0i)vFOV&hprNPqgnl?~(Uazh!LgywQ5~xo&b=i4r?W z8j6K%8SN+#=l&N!{Dm4L1 zSHO|jgao8^0gB4XPNcVPcohA(iv9Y1wZr5&8EQgMtcrq9T>JXseoskLD~(83y|?m_ z*=!^gsGVoo*@0niP?+7&XggFPMD#ge(kXBeR0@=Ps%eQT1o(#I#sylJ&~z2TIS;oA zvF~X~O4&ja@e%jp3*pg1W!<9P(sAf@n&FY)HcL@0ZX_h?BR-?I5=Ck?w2q?s59J7Z zONsZ*zC`w*hA(CE^Uk5B(w8E)nNsKg@S5V6bHh`*n>U@eytrYzQ1gGxX4$m3DC{&aO|m7ai(rl7JnU z$?o07YQnASiF?_(+B+`w=9V?4-?1C+{{T}QX}1i9BE9+J z#(RtFe$|MaZ8vsa&L4^Nw`2bR)*b%1y5H(<7Q#6~upRn#TyrArx7 zy4B!G_XCd#?hmZno@;TpOL6vbKCSG(`mwX%r7VW-++TrMZ+8pU=%4$A`sE_|a8W1b zWW2XXC;38&3Ht!Ym*sBPuRd#%ZTnB7cD~HO!h%*|7}Loaw{7dB_x@fSSEoJy0M5)d z95skNvB~*UQf9pR=Yin+4+84mHARCyEqA zmg3rFX-itGsYq5?bk7^!-uhj;?sqw39z?jdc`tnb0NFm?*=%zubtL$5mUn9KM#QqI z4{isSye2n7<4%tv5F@k7LaML!jy{9!PoNl>%}*)w%ge*<_@5B>_x-%yw>II6`B3(8 zCrHzw+n8z%vMW~jO^?!u!)Rr&Jcer`?qvKQR< zGe4U4lgm1dr)%C*9M|A7u290LJ-%KelBPmBH#za$c#niia4eOmk>6Zh+bI}nxnB-> zb{gP0aTFDDkdOkNs(|=MZE%XB3ATnWd#3*Y7i;Y;zdv_wdFo0b?b}p0?h~9UGz2vj zD^N{(sX!!lk%M6hfL^~C9SGU~08Xw$w^^9peyDjq&FOn(IE1|IqGR!HJE8VK)S!s% zxLILT{&L~(PCDf6+4z_}tG91y1yK)k`tbT%dLkt7&cIJJ^1Z)OMQV^dyw zN*k%nC6>}XxU0RrrKA;03p-5Zt;a)^ex|;-o~U`p=WW|wfB8#nJW684U@kKVZ=!q+ zok}$RWmDOT8P4^|RkGF=>{{Te&M)#;IA3>N&Q;rzx5Vq@hdgRv$vSJ=0xLmZb`Dt1 zgmccTEAHv_0*6wV8)gze{=9gQxq>$!CMxYE@N#S`ynJ=26fNcEJrXNBm5&(3Hw#o z?!`7=aJ~>+sr2ABQV@r#&!Wz9yu4exLcIUAYrpn+6Go2Fd>2b$aLY#3`Qq@zPNzWPuWhJ3T z@40a`jc@tv|UZ zB#lWz8$n)$DwGKcBzy6L-aGQ$r^yXlH5-1Br;mr?Lq4Z|wp<73`)5w#pF(_n zZ{mAo5GRx?zja}LM zj#3vM^XgZ4w$1C1?pnx8i-Lt~jcaYqAweyqp7evBE+MpJ`@Fu|6=A)#b=+5p+oO40F)8TiU(RkRZ2-d>dX~b- z3P>HN0bk8gbV2wyb>blGzfatg%$sswJaW$Wfxv#AVj)s)-1)o9jV9q8J|f23wGZ)E zSq@~Dkjg`9BvDHEaYK=nCn_2X7|A5nDd+b4YEC;NlA>%|Ey1rqyJ1`xw3QwByou}E zk=cbOkZ&w`;nk+Z)4HQtp)C!HGQvE?Y85T5H8|blyfLg@>04b(zDt7lvu(3>HS4kJ z-+e27Z{lm%yesYh0QO3=BbfHq!kcwx)3g~9_topo8_U#P{{YOo?t826e}4RT=arUq zt35ze_4eYs6ScFqDf4GZ^4CbM68pZ)KRdf{HrP9OF;(%C)xW=w^6(>z2Ex1UYopgo ztFPWKcNYt@E-$UeUv*^ol(o1?B;(aEcAIu!p%p}rD9qk&-;ob#PN?Jof4XEkW`IS&{p~u8fMMc@St2(CF1n_^Pov9a*{ZH_T(Ms z%Nw3m*7$25T-7>jgktGejP1$drl6sW{RW|jSflfUF{0p^ZS=|-Mp4YrOuxF znv|p2@f^+^IyYB|&Mt8EQ+wWtk+$}MRhw-mQ*X*fq{x<@l`85IeVB?cbs@ifwM0{Q z6t`DXRk*S=j?#KyGkx--d?UD-uau7_e(qn=?9NI!%3P7vgdy>u_w*LUQ)a+faa{cJ0zTa=R0?>M~`i^YPpT+Y% z5B)uMtGo^AJ{VgYwp(DeySL=j(GE|%Tv}LsrSb}jHELCSL=O%;zIb^V_d3^GQL1Tw zKW7a;6Vj)DA8+*8N!(sO3*GJed${isM2Sx@%ytaRl(L4Q$3P^7sX+-s5;G){0zmD^ zV&WFm5l1`Ip0eV4LiEMxQ-Hpod{ei5b^T4FvpaR%TSUK4{*j(gH#@AgHRnfqbZ~W6 zz95ASDH)T3V;XA6u3c?7#De8d^<(v;^%(e;Kkaxsk(p=Y3$^(6>R4{C)fefTljNTHp_CNep+bp)95lTwFB@i+L24vlB(GX%|E-!&SZawugd^R|9gxprw;~OXm+`Ew= zwz=%LP_-?29V(@0PI#ux$c?2}#MNz^bnniQ;DfwN!n>QFxgTqAhYZ?F>6;}ii?AL9 zqy6lM);b_#C6~E9P39&XG2+`jJaT6L0O2pAx20&iA7r-8)0;PZRQ$bkm*-AXbX=#3 zb4v{?;T;YI+wdimI+VMv{VJv!_g-Jg(s zd-}4E^4^raH_Jk;Hfc3VxAyXBz^K-UX;=5nH0V#B03ffy`BV)K>ysw>Yf7378!%Dk z(YO0N{@w>WJzx5zAN{+eab13c6Z3z+kKUvk+RvNl8Bc0s2&uy(kKl8)T*B9 zO}F4HFxQDwos(BZz=FI`1o#r(wpe17ar8p(lkB!U}bY+pB}_8o@7 z2~#dJ)|81=EnQPzP|D8#L@t4a+Cqof1tzdCgwgo0Xf#yf_nj429t+?7Kap*XKUTkQ{EfbC<=eM8a$WC!PmFDr*`8coskem#c8eq=mY~8y zQ&0mA;T3LR%+QRFUByWiZIa)O2N2s^3m{w$2RW-&3Bx z_e3`yH0hPSw<}^hdI<)LWw%j6I%_z=cXX$hm1hP&-1(*MS})??+-&x^ zEKEsXmx|Nv!T$g>a=JwM_luRn?R1)}jn3x#(r%X*4P22bL#ln}RD0I7l^=dRIu_#y zD91#R=eMrAmt`m7IIU~))}HLEgK>D3xA+sL({oaq?CJd&++aQiLpd_kZ3o7-P*qSV zOl~lw+$d)A6R1a+5w10*!PG5T{g|NcD3&`^ggv67ksO22W-t!l658~WM2EeSggmDp1rus;pa}p)m;x&?{3M*-Ep1EDLn(*Gj&F}f8m#e>600B3nr%`1#-u< zZPyjZ)c7ZQ#(3Ed4RB{gIU{RFndmQ(Wx;4@wA?L|+#n8!aUh(%*stB}P@Wf*tobmdf*UK7w)VBqKYxILLM}T} z@Y|+Dk*cUAROo(Ze@JG?>!%ZstL_K=PE>3DU(^9)h$DiuJ}LYt4BW^_Rx9 ze)ada_qtWqhOc_VR_}Ai;gNrR2y-eddw|%bN~OB^5L!uAUT{Ctjo!zT80gHd;SOl% zcH`z7cqR7ErsM#iuAmYtl`*$ZLn5E3#?PhBe|Var9*HO5`QSK zZai`rpQLV%KmJbd4=VoevOR+3^}21!f8B4174V-T-T3!*i$tlVxaD9pl21zImOY0G z;?VNUsPRX4_iw>oVx5-c_AlAOz6o6VB%Mqu> zTv@Co=3Nw6AP`4!!77adQKoZe)pw%}v&j%9U$)~7MZ7kXN&q!>(Nx6_=d(uibmA9p zB~;*dA@Nq_`)kq*lRAh?ohJ3G5@Be>rI z<5q0MxB^~HNT?^N7%JOfceK!KNcifLM1EeFvxKS%8?+=|GvD%d#nwA=96z_Zu$PG@ zt>;RQb}#nKo}I+T+?`gX&E0!`%gNSw?#uGgbu9&3?(j;f^M23YghRK58p+>kC(ZpQ5_$rjDKD;SyyTq z)=X&4ju1*vpyF$ifa)<0W$r!V7=K~(~GVrS)wem9Vxw!gz2MkiOv7||U+6QzFPPvef@FEpQl zl(`)Sp_0G#kM(1esB?^;h$?5TAKf&BeU<(=UUo6#r^FelvR1F}%6)?`;fC=WD-m7* zht6=7RIa^GTqK4zkQEiH#4y3hi|}Nz4WhLTZmaU)BAZ|r?aFfY1teV^bzQ>^=`u)S zBoI``75S@9+)2v9RfHO5;)e5BB==Vgc9S5UD|`VRxU%T*r$1@_tSW^DM;b^ z6fOyc!hlwNJ2AxEx(+LBf(mK&U|X>&@~sXPI!HRzLURKF(5_Jvj8B*aNyD_kG}rx{ zHAvt<6n+%f9ONf5Hw-+kh&b+aN6dkkhNo5%>imQbsyJN+&hkslQhpt^2})zF$5uW! z6+n~j0}x6`f;XnvK~Zw>jJxoP;-NQKz=XQ=$8`|ix;GHjOp#o7Q3=;DZSJ0l$8{0k zGJ%n5T8?U`vkmH@k0?CdnUVogkGmZ15xmAIMuQSMf|ACk6Vjr%Z#!g9CQpd)gDuyS z%pek~F)>%h~S9V09wdXR!i3K zdcFlpdAKYZ9;Gb!zRV9Rx8(KEWK>tQbU@_|w__o_r^TCk(zN^&Bu7=j^qTe5@fZgp z#mRWE*v>103GOas-&Y2TUEE57$rMa$e0CtVf$Z{9595eoh}+cZWLzP8rsxt$Ro>$D z)yFGD$bHAvpMRK64OnZz5c5c9O1$|fA|n9;xlmt=ojur@*-yYB%gubCR$PNxr3UZ)DJq&7C_B&eto zI$~795D^6DTqyL+lS+GW!X$i8L5@0xal#qL9Br$n7NdDXtVlHorBY5~3(nFZow9V< z$N~#3#YkzlGjf0erFAtpsJ(YnvEcZR8>OyHsE#-s0Vx&JPi7*8+A-M{>k_)&8<8Gk zsZvgmax}>O_`J8Os5Pi62h#?@bZ5e%p<7l?Tgx%_=V`IjKNbF3JpAX5L)1Lg3kL)af4N z-}b&T+w=Dw;z)-E*50Wvk}^+98ucCE<1(zt%T6WRl8V&xqwCXV`abn*GUsr+dS2Ue zcZ}v%fy{RBY8}XHpfB?ZvhN{YvX{g7EwVoxA@4mHzQ9`r5o*?=BS?arTzlfdVsPxg{=MS1n|L`MZ0D4UQGZ ztDoSP@u^=d_svMbeeM4MLdb2e{I6e5jjGa7xi`lp@-xUaREf9SeZO63Xj5s3zyrFU zekFTsB>aCD{{WA`@1MV41@|IVv47?L`b}*qXqI>q?YiYhsMU7fJBCXtu3jcYgtmUv zElK%j?pIm;I{{T(Njj4qCF5~-uU~)Z@Iu->-=220>Hg?=(>Y$VNA?r5#{^$6M z96$8a^lies#lA%18z$r8P4g6$L%Y~_J;{j429-z1zNexV=qaGJN@7d8f+Tb$%j3b8 z+m}7b>ts;Oc`b(=BP~h|1g59hFf=3PN};IRmAGV0lHI=2%eO0==8>T=lH-df`zg+z z)0P`@{9v^X$Fk=1CassjF3E9?!N~7<&milMwOd&&p+lj4X{Mw7k(Yic^EXL7B55%; z^p>T7kvcWHGjXG|i0rVChGVvrr3gqF01^Pka!yipdX`ayVZe{B_mwh zjEN5@r~{ow6-0a(LrRvt2^A-(#B$?7t%{K>i(ZExJt_V4o_*dl9WlgI-{Z#YJ*icLX|=g6sinXWo4vWV&KYDZ-<%V zPt`}Ok5az7H+r{cs2$n{(6_u^+8jA+Tk-b2l_7f2e3hEZkbnhbzAI5wCAyJ}{p`6i z?;97AI{5vx(B^+Da~vC5p4)plNs~3IrfufBn{4zcK8fi^cT9%ap1#_+#$;MkjU^}nBu=a>^UV6R=^`67}O}TIPTyf*+ zTwj4kH}9?;<9t)GHXWKi&$p_n7YFpqp0u9YAJc6A0M5tD{mb>&ama2s>05{G`+g_f zU5oz!Quh2*u4X=Yd3Y5kaBnTqyQHpO;=RkgI`2I-%!l+0R7&bjo+bS5S+xf#wG@dXZSg+s zzm}YfX>k$Ma+M%?4FTU*C{Nvu9@*L(ws-igI(fYhKi+<@>^pLN*Hdf35|0^ip4_{^ zyv}EbyAO#Z5Id%0eoCw4~;K(~qKd9?;wHVasmXUfLfTZ~gbW z?spuo-IM#qO}Ro(E^mw5dAuxpegf8W_#BramGAN6TZd^b3WirY{mH5~QSaxJEb3h~ zboS!jGti}NS3^ZlSXaboI$`m&B%@Lf7nbL94f)Mr{?B5H%2n6LB68eNA7TY@MUi!N z)eCiP#=#>ye`Y&sb>~vsC>3$Vs3jnKN~ug)M&(M?2@=-{@b@`-k(_P3AI29=?89mS z{n8pRtSka_>RrZILj(@c*;2ijksy1Q;~pnN81xOod;x7|c6##lEe_k|B`%|hc6*CU zt`MC{Y3a`-$aA`^w%7XDZT!o2)mtM{o$VmL@_>4-`t|yI^kuf>@wcLGD7SF^o==#W z7slhUFLqqX8a(AV;+CWpQl(Cv`3q%{K7*qI1WqjpMoJ z@4eAB*$KTbHdQ(KQlufr6ra>_!$jpur5RznhQTWlQgY^Ri6#eeM&ogec~T>bw>z($Ev@1u z#IBxaHxd=N?iYzv%hk;4QL5JB$*a2gi51s#BNbYa<>!$YMy71+`RnYI;GiDi zjRro^{-ZyYyuJ|{@T}Xfc|GQ(wYt&kCC>v%9?t&&vjtts$Hr6>A8*Rcb?_z~xx|*p zg%8|`xyS$#+JzcP{{U2V`*A^)zfO8bL~Qon(X(x*KT{@)cHc@Hq8dzi$Q~LbB?$xW z6vZ^BDYI9}3sDj7`{!%ztm7a{+#&T06Jn%emj@x zsUo4KViK(-UeHhWaf01@;?1Ya4NWE%$0__M*PDK^9+&+KZ0okv^drW0t*XRE{#su> z?sn5I`!T?hf+Mo%^E_Xcq$k;o^_*?Ta(Y$}Wu`;H!&SWbq`Vgozt8u34hHg%JbKCX zYVFIKc_V(v;QkkRD{}4XKam}A5+t_ZATwsRzN9+wSx^lnq$MZ5w62p2Xu-jBit1hb zOx{la0F{vVa$Ec~DIDKY{6)dC9Bcfy?HFDffI^tJTW3{{sP!N*3 zC2l8O0IE-1Xd|)YWk1e*vG7YoXQjPZ#;-|pN%+1$Z@{aUalUy!a!TJj`*K`29_YGf zaopEgZ_S?lNl{L>%8C)6g$SZk*pM-$!`kgwii%W7p~~EDU6otc?e_8{?W4y|nGB-W zZu6aUtH!ET4|Q>A^6_B3Qr+xs19c@6?u>{~vbbgJh)0JJma!!=2iyMu@!!?P_Flu{-LC%bx zxcLK`ZV_bfJAh5H5K3(uaw}ggZHU@3LK=?U5s>XQKHPc+$=X=hbmWe{Ec1Q)ymGT< z`JES$F!A3G$(*>XLV%|{!kQ=oRBO~@S&xsdrb2U}*ndrag6_ANZ0?Qh(_2SmL_vPz zxF)a2Lv0~CiBgw9GQpQ)2u7{wb)_k~Bx%jxQ63HS&u6)84@p+re&^i`g)ujn=dg#N zu;KwizD2x?>aA$1>Gt+x8@Tr*q`57c?XzI3Qn~mXK-@c*mOVvpd!F#zx9RuGwb^Pn zUDyBT|vl$~D_YmI`Ulw-iO97DhoOvtvqt!P!o?l1Q7GT552z*Id$jJz;~MQ9gxBdNG;&*$Ed#?>N^=Q6$}uc$vWF`=s)stZ z?%&-!jdDy&dk#8>Ry+q*dX?6sAGaLzDf*6V>z}31Lr6qOv58GU@-n;*PjUw{SaypqKR#fYav+xArFC$t zvsHQy{94gatexg_qmp?qc5+q0$L`XL7Pe3LOL7!a(h?3?cA8=q>O*$pA{QxF2vBXN zfm5%j(FjULqDegf9ZA6hVHVEddLsI3>>DQ*#P!YfCEPPOAJOau2X;=)Q)`R7xi)eY zt*CbBuK@EfojDhDg}Oc+DDf*Rymt$>`5?V!<74gjbB!PFOYQdZi+y4bj;Apcq z7+Q}`&xaT0bKh(2JGJuZ8r5TkBKvx|Jkt=Pu-c!KDfgG54Kmt6Qk0@V0A_J>N-7kP zbyYnMaJL?MX!=I+^_!giQtjwG)%60u#d3ECdSFu8k!{&6xY<;Dp5jmoN+fh4BpoKH zKqjPxN;+0ObmxCiTefZ1%2# zP${=sqmokEK%k<3+9cAInfs(;qT*=wZJo-JmWcJ2!kjI~+yk*}+Xs*AjoR%thLYM) zl#Ol>C|p{)sb{Gt8SA{$m07F1b`Xy*kubLEoLHxCPi8t1p;dC{GaG!L%UcV9xcd3Y zT#tBe&9|(LDQ*?02On?>rKXZjYf;Dng0S}cnNy^A6**VqVm#7kj6YqTx;MwFJ^e2I zag^=9Yuk&AY>k+e{)8BcQ3-LtGE>VGB;(ljEshwL*O}z|{_`w}wH^gD;_f<^$afjH zJKW#fHjS8vH!bnQkR5!c>(y%MOj~8$Y_GtumlAuQbiHzv+fNa3d@tB`UA-X+VI;Pa z$}?Q4OHZ?{aY3KBM;aA;O>Wb*%O+|oNTJY+3zsXEn}hgnE4O(Qh z)YK^bp((9gP{n2z9P(~^3hmoHK{Ss%oQUT;{Xn^=a_-3!l3ckhZpyvfANgxtq624A z8cLibB?uV+nv!woIJV)73K$#ic@32>nK+jB!PC=i+&2i9XwuTP+V4=D^CL<_YS2`I zY0%=^A0B(qbt1~5L=|-=G)sN0AZjDck>9Ca{{Vh1)N0C?It3%QnDExU zPAA8Pv?62j*&IcJ>*`2sgcDQl#oR2?05N*APCSSvZUjE)1qg7u=|9Sx&JxTte!^JK zQWE3~(Z6o%{mQC%Ns^$-5(<<;2yH1y8Iouy2W~Riddafq8V-ey<|z*F4+-tRPBBU& zayKC^9_sZ8AsS2ZMu6p_#I>NG3n(GyQV++UMPj1NBX(|FQqg}ux1X@H%hC!yB|V2g z3C^qS#MeQrhdf1ETWwCbY2~z$RV_r(DM%ejIRW2@T_URVLD;rsa^i|QtLx$8>DSYr zq_09&*Hag8SnWrV{X@3W>L1-(m=!{f=9*P8*)9;33W5-F#xt_5tgMUK&3sRmHSmi5 zDN!1(?a1ibhBSN?T|btuUNx_WiOQH-KqL)E^HlW4{<@e)z9M7?9L=pqrvMc|>c31^;W2JVm+py7 zjjwobtz$X=09O_pgll2ce%hjVV%b3v04!=(Y@mZck3U<-vP`Ps?6!_9tB1ig5T`krWpw4`J@_#n$C=w&;1qhebwn8R+y0 zaUT%O;+AqFx-3ER?g;W#;FQ*En$ReY%&;`o1`9UHs)YDUsVOa*DIMA7a3)2bbV&)g zlhvm222@cEN(zSHtYx09uAM8_JW)>1D=#c$DJlv!o9gZJ3l6wGS!x2EitNCuB>ePb zoQ=He@tVF`5>y*CeIRC@-h-|Ll=Ka?W!9+Z&!x@#Ds(hjCOW3wWun~`Ul183Irn12 zYRGk=bqTc;Td7F|lq^&@l6~akQyA2ohcB?(Z|AMjP!}3aK?)U4g=I_?7epb)pH*CT zLbZKLxWR!YlfNhMbKE6YC6m374SZdG!!nG*W*meLt?~ z+S`>WX)2Q^jW-iv+jOOAAzC2G($bW20V_E3%*ec4`MT+IUOXLj@b}Z$)2ZoSGaURB z*Tlbnx9#GJ@xfctU7qD^S#Yx^+Uf`((2|8$`Z0Njnaetv42wEY^vS^7j?VtsR_OuI z^rWF4c4E4K_T$m;uXDntH!jyKv*FlrDyaBxW!y<3UDu zr@m#l`dsw^wD~!F;o-&)%`JSk>!fl-5zgpnF z>&{vp3Tm|x)M%=E4{ki?BN;|jMeE>2_c#PBg{MPjLo3PwVhN`MCWws9} zv=dTvtGM^~;QLnrS}czVaw8E|ou9bfKeo$NEz?QPGsNYeaX8ub{^b<8WyFSPpSN9^ znT$Jay|N@FBC+R;$w?IyJ=oOUn&8%qu9l3do6BIzw5df zr8eYJq^++#kLKz|E$m)RP#>l@o{Gzx}=Ob zT8x!R9OQ5eDseU3Q$9C7v>k;2`w z+vhDVKUB02--VH1m6b(B#9Ef;l#&~0BvUF5nPaR=P#a3GQ3wL-m8tgs0HMOk&^i$YhydZeG&UHt7p&#UYC%;}(EM>*m8O7sX+=r+V4_?~hSldx2S`A518WmH&nAIum#gsA{;2Uo9 z8xq-Vr5y%?y9U~mkbSAPCxuY^F*rp7?7O5_p~Q2E1mjTH2w1AsTtliL)CJYF2kECV z)Zy`{_$DW)*Rq&>0unRq!jCdFR)ZXK6^#(eP|()u@>0m@UhE}k2>8{7gf7x?Cs^l@ z7;Y0I;>@0yM|JoNmPCP3389%4_F!pVkjIe2A{1~BfL9K8Cj3ZVM1fHmisaZH+F`v( z3D=M}%eDmqKt14Lyz(2>W>ka@%=@sFV|kgckb)~r@}?%|Vylc)wr%g+5~&g=o&T)rr-Yh+VkJ z?!%PoGpHE(baxCsr*&8z2X+^gi_UH3f~1t^Oi2j34CW3p!Es@<4Nk0h8_!aYuy*2j z*<>=hNgHp6IXiQNy=2&TDbcdkEpg0dGtc|UWu(*FTu8B)y!Ax2AAxP#ag;yFT&1=) z3P{wuU+x0!b)e5Ekh8D*M-Ppv9Omi;CRe7dytN4OcNZkQniRSkZp{%#{{VEOl&&L{ zx(MVq=5sxY)(`nhi>!NjNXdzk5aq&8aPd4+3B*?>i7i~81clQZo40+aUa#AW{ExMi zl_{xG6wS2?LUTS_XaQt<@n0;c)Rj$;PUCLf7GX`g_U`9#H80OtSw^qymoq;66K3T_ za+BgWM90s8UJ7(0BZEh+K)zt0_}$EOsl-Y(A3*YEr7fvONo_G84vu63t=vn9OZ>fi2A{ULPrA6BWW|zoq6TdDsMT7nuF)ChgG@y7ZM`aU zoLF9M%44W8q%ej+Iixlcue{<*aY<+_Ev_g?1G~2ql7! zxf=^@S}zL`WkyStO-srIf!;_Mti3&IUE|wQR1&#Ai7Xd*avHa?i1K43`D(}&tv=D& zg0VB2bYNLHNotONU#u_LCOH-45t-3ib{ckLN~qCVr2I>v=u^=q>h){MQDe%3CLb1U z$B@12eL!1(F;5VM9l$@lbE3#T6rP}wgYa=3$!oxLuBfdzp6xvh`b*ok2e$U!+x8}H zHY*EFM}=)=pq69DXTp>u4w%(mlwlN>g?>CMCsWZ)n!^KZ*&y4YvKeYJ0!ESCF+qOr zrNx+}hVB0VXxO)W3ARi1wX3zRlF?NMUe3%JYj={;fLC_wY_fJekG8n8ZpGSrPQ@Q@S|K24g&m;S zE2oKSJyfIAlY?#F>N_249*U)sEDo%N%K~7*G{=&Haup_l=`dGcN{1GN~UM9SE)V20K4%|i99SyTnQUwaen*c65PjFJ4b6fPy9|&56!-cjM!k% zfJa3xp5uXXcOh2*3tAnNwl3|7k+%fMqyoa?1^Bo5Jr@-c}GsSaj;91SSYA~L8|xQ@wK2?W!w zCbI@eoddJiW7^hf`EiY z`i*^et~-&-*9rYU?y6z|*B5enlT!(CD%5l-HU^#rE)XmJDQPW=f`Gi~Lz%hPfpd$t z(eq32Jn6&RF0%IE-&^AECi8i`I=Q=-bloL8GJNR^6;rZgNm86~bQA?d&;SJh0~AxZ z+tkoT3M)dAb6-3=c~kpeWxeuNMAa|c8znBhGZX}LNyL_|*NIJ;mez@tJRs0zgDO*{ zP){sVrWsLaJNBmLcFym}%UYKO{E%ytWS?(memSoZZO)F2F}tm3jPfq9MwGmN#H4&6 zt1w5u2^HWz<-Zg)*7aIRBBb_V$q4S~77w8&y$5-q9WgBkInIFka{6t0!Sx;?-8Qcn zyR$Y_tkK9jpsveoQgtL%l)i%d^$H~=wu(&%)D9`LH+*|bS6A#bTXxBpkk>3|yx-cV zc)dUU6MZvrqj2y1{{XqC^X}z@Q1iBQ>#=QyoI|D50>pO=hte`igRbdSJ`f2Q%--&6 zlXSHj8=OANu@6=0b3ZN%?eD45lfSr|i7YYpKN;=&R@Jjx8x2I-dwLXmd?rwYgaohU zQURu2NjMG)?a6aoWy5xC!$`^4&e9t|hvAiAU5``d!D6Az0pZ zOKWOdVzSF7v~B4r`Ikr?JXK9`@qg6K*5+2_E@xF85&EtF09-qM)-J;t#VXHQk*?L- zx2@Z8L=C@ck$aS*MWL3;kc0iV4G^Dth{h8SWXsE!C73xg(>tf?7SFT6%Kg-u@TqP# zr>1*~LLT0CHtkLRdu%e951!Gf)r|@E&>wz1gSS8H=3Yf7l|EJEzn}CzPw#L0hi=+- z@NL)H$=b)Tc4p9M{>x-%<`gKagWsS;`2e@CkI?7G5DdL$4Udxv#W-d3&>)NUD zR^YU1{{W+kn;#KxnUdcfM@y?vTIwnl)Z<5L-iBKB-9#Ol1c}@3@`Z&eWzjK{PR8KF zX`nyn6=BGR<7{&{tt72$$Y(=|ZZas_Y>qddB3m}{q1#yw-LJQG`8I0=CY_r)Tj~c& z7>^Y$I^xYTN)k`A5Z%dYG(om7Um0*L-=-g=zph?nyx%z|f7>nIm^kloqBF8LCiGQq z<`WfARC_!}QsO~M$p%BM5PGMx6_CV}o1C;4H+8b0p$f`H;E}RElwOBvc;7d>NkapX3tg5A0}ZVCzy!c>rpx7%?mzm2D*~hznUSeL#w{2;m=dYCaHN$^jZkL$>a-`0m zAy(v4r4W?z%Slo1N_32RcGU=3jXWv)Oa|)?#m)vOIO)}tYAuf5k5}jt??MQJ^uh9 z)Rt{A{!JYV%JD4whe3;MeWo$iT9nuKXD@^&Y}#ADc07l?hf531Q{N>mf!-^b`vxSv zxMSEuxIPI<>vs)6}n}Pl2Z(S=ISwZ$Ex9!GPH@NOkNa&5O-P*Qn zwB_n#ts2_Ic1Dz;mRkP+Rkn}^;fgv=uom+&GLfw_bT_IRv2rMMpltfHHF}+EtUHEUfKQp9FSahyIq) zHa&tLCHlnV8-J&bfK%o+-Nn||dYY7gvW8+4sU(l)srF+Ty7$GaN|xPOsoQ&2a(bb6 zm42!|lzkIzdyd}piLvdr7aMI%J?aBV)K50=XzsVP zt+Gw|wv*|+DIOv@jo{i7x= z%YEI@=4~SBHHAAK&cmfD3sF&WnWbq~SjFGpXFXxV&G7!eZ4H*)ur3l+eoKzNeqU`s zH=iJHu3+Q1yKd_O&*b~5hjT4=S;e1@f6o?dlRpdm^%&`x;B-QI58 zxS@=k=i=W5?lw}{@p0|e{W)KP{yu;CPNX0=>o%)PngBra=ApIuKD_!V|0_ z!ReFGhTVy{xuWpR^M5$knP%fyWIc3w;3|@|44XxADpdf*c5d%#)1hU9w$Ah6p~o*) zzfqoA^&dJ$)VHKXYI3^~Mt~aJzm^ybZ<<+Y3>m z)T_H6hOYaTa~ECIrA%;;sL+8PQ($L803ADqBjhe|u%y}4t_Ad|dERe9Q!tkT#=%H- zSx8D~diHl=)J(lrlnc4Mxo?Dh8||gqEN#Tx9E4k=G5iWGEanvAg=y}@kXn#rXDF&1 zLFeyPJcZ6XnfER?H?9vYL`H;%36C8;076DxJ8$j6lX~HzVYGz z07*MFH*MD~x2}Nz+iVRKs!WGzkO@vdKs~_rV;!xMXBNe*uGp{5*C!x99wp%vUX7uy>eZP-9 zeOY?M=f7RtpFfg0pL2%bTau=>?%R%~v$wX@tr<5G-xQFPBqRV;r&7;a3M6(G(aSD* zYaw&Do^M6JOjqq8ZZPAXLFb+MwYYnMc3sPOZY|3#4B9Nv+A<|ec|;_*;S>$Fo~3Ic zDIK^z^wNINNJYz2yVoeZOY~^l%G*|Xc7A61o8KU)Tz4{t??b3>- zqC%=W5-EY*YhNF?+u9XMs(5|A-omE(S@ea<-nhADZzgi3?&R*)yU+RU;lA!FTeEL+ zR=rD0ZmyLor3oGsrzC&?0-HZEuSza|K?@TO!G_wpE%vj?HLk490@O zXoQ3&mG+a=oMx{2jf;|sq9ztDpECZMj}<6;zZ^Xj*&4QezIg{`aSq14axVV>y)$In z8+nDjI%BI!Nua1g0X`)tA*hdFwYD z+FWrP8)PMABT7j~l_esTTIo;^OnZjMvD?G7N_vkU-nZ+S6LfkMZ;g1~cOhKoU2ig# zYT7oB@f(|Pxszg3zCT{DEpNOooxPxF^Q+nN#DBmM)#%QHI zERdB$j8X~EgO*)!=2Oa@>GRKl>sXkuvkq8sJskZpJqv9gNW4!c*0-var*7ZBZ*peO z+w+i`+hDlZaW5=6U-1tdlAghVXNxS0i6NP}akUzpm-U0{#!s#ecXIc=?xxqUt8IzW-+@dC3f$8?s$P0_Itqi@f_RX3rljb3p zmJnEupEcLzIZ>!nvcAkz{KeasIbQ@2MM44RFPf-sVY~EE@{X z9=|NiSgv_Xn)-%KvXBd|uef%a1F4gZn{_cRE_XeGr?1aYy93g{3pe!O^uK#W(i1Nx zU#kvYj7Oik_b>r2z)Ps1R7m7$ARRhDKpkmSiDBhja+0DAxvl{(o{x4+;Wvka88qMw;Nm_q{y5!l9QPV%7UHviV;k(TnjzI8x}6l zWjm(ykmc3%RkCUNNu_eH@xy15k_fem>rn3^^2EKsAG2HEQ!o%m%|4Y=%n3gZU6>km zo2X={kpM@e`-+@j{n{WZHR<8Sm5X#$De@+SCtp><>ml_GNd}4c4w$gF7T64$9X6On z*INKoS2Iq`ABAU785{{@?e~4t&%B2aPUHRyj+>)))r17zHzTV50K%Xr%*jggXzG*K zB=#J6=6%O0@<)uncaPiNZSn1L!mO)%qjhc$LEYPjdhV&lht770%v(N`04JSN^sK#HsGUaOqQG@l)kzaow*;V(WS=&}z@2N?8Vw-w- za9+paxbQ3Qs$OrO>Oa&+EpuhS=^TN1l`7F30+h3BXGTnW2t7i!f$ z-k8a$JvBk|y(q?w z=u#c;Bw24VW>}y3e<8;Z;s#1V1cD8I>{VX2Ok`e^E;|fV8FNW zSp;s>Hlkw4OYJJCty)mPuHS-QHJ1N{uxWnoFrw7BfnHy>U-+%Unx!be_0_exluQV%Km|R?Er> zb+vRoou?I7;mBHGWhqLBQ~2j{rarnO6k(5nCy*H`pTX8%vUD=DO2P zM!629aozCLQ~6B}Ah{|^sO!l%w95DBJCrOu9qLW}naRsn{1N91ZO4zfq7sy#*X;N9 z)fz0q21LY?q%0`pl&NPFUCEEswamUK>c5YQ_xSLqh;M3cF@)WzJs;cc_Y5uV$J>K~ zd3$$pw&Ic`-8L=3>ujrn2kFXfmDPz!paV>XREI@76^cB3mpE7L{7Lg*t=(wqhocT* zded@Z!u)_EN}*{)kBs(X&^u>yQhbf-W^?RUQ?ac|M3x$o(W)FsQcu6#jjJf!G@-`d zSX^eEhsmkj@FV#rJ0h!mT1+mj+U%%D|G#0`v{fEtE)gc2@RZ;-zM`-&oPkO^@f%aQYrS$Z(yicglO#7qL)%n3IlKnk2{p*We zQPl0nX}qe;f{=46imw_HJ1LJP%C#-CaOS6?=kGVsxgEy}jr!tOeV@6farW+MXnjl} zF6(7ADH=k*ryiY&a*<5IwaVysbJKq{LfkgkW9E{lRD^3mRY_6yV^@KYq>6mYbagrr zXSuU2lq?-u$GVs*N~J1*S9V*)!p|v|#-}tWApms*V47$vk~ksj3u#G#a+M)5iW?<< zVWFjQnBArwn6EgYm6pMx8fTMwGdY=5)v7i;#f2b>7QmHXjwf3 zS&(U(JV5nUIXy=Ru189eTXUiKRoXCpyn?z*Q&`L)hnw?OQlHB5Y6vyTw840HA27F3 zaP~Ink$SZ=wy%oUcZAthu3=SEC7NWAf0$#?aWXoV(qI>p@C#i_miHFht(RdhC5308 zp2J*DuQ+>vWv3Dg-kUY4302O4Co8kz$?>scEbU^8dx@2m~ zMxts`Y6(>`sqQ#lj-;x{M&T(Xu%Mz<(0#a;&7DWuwW!l|YH>=hOjyNH7I2D41XHwT zFyZF<^UEwgf^veP)6kr;6NuLnC1lo}JFtyVZ2+lWgAQzQuugqJ!VU&V84M$Oi6$Fm zY8el=2|^2r2GXNSs|gYZ0K!v-Igrd9Bca4(Et_FRnFfZqz7={%Yb@syQDjLup9y2FJ9DemI6P6P2;eV%nRtafbf@%ll^GdJg1; z#fKs0>;C|>N_3BTIGI^&NlGh;C`a`F0Kc1$^ShaLt%V4UOOboJ#EPJi)cH*pRgmnU z!EnUZr#tcBwF5g#ge;C|++5JT!14uE3vSt@zg2e$GD&ET%F_kPHkTLp4R-FgRH(hW zeA&GNxZ66z zWaH?L9|~=Dmb~HY^QC#D{n(0GGHQZ6TYOt@?e_5*H_he9o26CUJF@9`O0rW9O?@m< zwf^a)A+R&-P8hC8RX9h5KHl-N-LJ|~P+&Hr)Bp#!vjr_slD|;Wqo`9VemIDKFznge zh5e}}Rn85jEyn2MMYs|rup}lb$*-E)7nZTuPfS-|wah*@XedgVxc79UFL2!7y}s(I zS0&1&C;KVMDIUNE0F|y+p%m_hhTLHUi2*4DW)MIF?ZcsRRUvYy)4agj=nf$-`71yq zD^+MmyB3z*m{c376v&j*PZYNIoP?4B^Uv?WeBMQ*Ew+HuIwF)lVM_q@(@Zgh#@j=} z)c)2}jnXRQud4jjgr>Lx_fe6`G~z8>_cL%C@*!G8M1R^P9Y@=UBZ%dx2PZd(+g|T@ zHc~RLW(;`fky&6YZhNnmw&0|pW~8kksM8TVXf7k=X&^SYUG0UM$X>38P{XT2>v&6n{7Vj2{>zsDfaUS_N>bgTYdYf^g=> zm?--&shz3tL8f>{aj-$`I7*79PMmMd3j7=>^9WwJYMG%N=gW`?&*if_)!yH?0p+8&8FJ(hAbm!77)&cPKzC*sTay z^CZ4bK}z@<8I>sH2ui7|+puSGmDdtlp5Sz4}4f@vU|x-d$)mr|YM-waWlxOG3udE@a32#if+0 zzRv7ZPDOz)ao)l+l-=Uw&WLHvhLkf}hi|(P&gDkNB;7hQNMz9oX|yLQWCaExgQ?QJ zQk?JY$js}kltk0yM*TE7pNyv>aesps7;08!%}RO@P0>MBT9uz}E@Q+BO)eyRavmjM z_|Bzm+j`Vx$_i0mhZ0z96^$v45>9pQ!9dg-7fJTJJ0?}qV{sZZ2rEzpYCQr|)kK_t z0|_gziwL)iISSivWO833t+j3Y0os-)Gdrg4+S=5S{{UeWX>nwZnzW2nlI6|;6*%-(K`Uhv$_(L(!h(BWxR=))j+Z4YP__TrLx5@qF!FiMmqiB~|9poM9t zW>|^3kD%O{JggEGnCqSv96~5IUyw5@I=isgw1F!sCw?>XZ%9Q74oWEO-MSjBPW-h zP8T;*0C?5Lxk-l4hTZuFGphQjip z?e+;tX*Ein2{iE<<}gjT)UC_C%kL@4tL+Q8q0k3X`99 z7N;~7cTSQi?!?ek24dS6x8>Shu{hGCyy-%1;UKAHWk3XgDoM_O409w?(v0cPrH`Z! zs((=p+qV|*wkLtO(pHuK0GYQ7E~e)xk_oI!Qc7Q0%OdZVGpHnDzb|oxwWh4F!rJa$ zur_Y}*DkxhgJcd6t8eIh${Bm9iwM$z951wJDoa6woA=(hUb3CN1HVi!!WHmNPu>49dAx(+>)h6tVnu$!y-%IEtOEPQWJy&r2e5WxS7o!MB$W`4ap0~z z+*|thzxM<4Z*s2cV7s>-kd)inRCW-;XqJ)|phI;bN~U4jqmy+*Xb#2y%uws3F z-f(uv;MjbzisRxqizXM@a^Ay z%g&pX>A5%N!w*j?Pj*a#T6`%g z!e5TSiV&GsC}vXBzKK+~*Cd*Ib>hDfdbKP!OSGi}4c?NLO65o^ zc_8YiO8Y5`XK<#o2Pp+Om;BGVoEs}*ZA+f~-xrCJ<)dm@pvbvRtl0|k)a!AAgD5lu zTZJhdrxC*A@GT{Y2Yo$K+32eBWjChsFv>dCj@2L=v1Mp-f0BJd?YkP?e{$|MUM`vGgvz|< zLb}j|nkVhV);6n;h%QF!7_ACjz5P~h%fg+e3Ywb}vV&=`b!$_c6)vC5Lu3Li1W=fj#1M*Yx zG=K@OT+S^lZPzESfm@Ne+q9_2^5eWN*98`%D8y}Mi9>%G`+DN)&f9JZuTwXFbGdcW zA^Q&=Ze7IUTvH|!4jQUc`hg(!g!MR%{_kSmiPif9u{&!j^Q@H<^Ws=duf$dxFm6P) zzjl}aK6AimNgv9NpMEnL`_0>qk#t7uZ0(B{HEOcz7+C z2GHV|tQNJ!)}RGY6IEx~ki~>yQHm{>TZjEK@2)|*#Ifwnr846_Vv?3($Bwt%ReNZF zNbB2%WMxL$t6|u?bgpGjL24s7rC0B&431 zjCt1c->|f~VXHRVcWXH+@CowQ)i2Zcr0utJuzFYGi%%at9ksUSG;RgQmwk09&(39% zR2xwl)3du7>}-9@c9nHeVfh=T4h(&KQNzAK^m`?SlEH)*t`s_W0&@^Y=a zb_+xuONN;`e8Wt_SwRQ1gN=^U*;u%(O6RIZ8+h-F_tTBPOiDGn`D4?k6p}}fX-_4xg9R>ZZ z#x2ElsS}y`*+2jkpfsfRkO;-BX40T5vE*838-zNs5vRzJ;s+r|lW4eR7 zMX0mwJ(sayq3J=X5+mj%Dw_HOZN=R0Bod`DV6h~oWY>sE z2~k2(QVM**mNGksa8dHXb2DePcJtB6stu++QX_8u@x@8zJucG%bBfltTzS$}DfFDwwG&g_fw3@iH#b>&E%|#s z-UPdb>xZ|+t9r~&TrJJMZw+6!wbk3zz<=FhsGpaPj)4s|@S!8vG3k3=!)qQXK~VBd z!+E`vD#?})kNCEJ;F%IEJ6hZdAR)9MlB(lJZrwCREbbkI^!w-?axCBHzEQfugE;n0+&TY}_%cWOs z$1&ACHc?k;TIxXV10jlMVy?B+(OOG_(H4t}Jw8UaEr^lenO8}gPOTc&Rs{eQ7>}bw zU*h9eQ?9%<=}ynJtj%wJSd!M=v%*Mq5g#dSDo0bOtFr>m^$Sfctw>AM$J83fw5{^E z?}4K=3M_Q4Je|bY2@3qAwNAeLOKQuP5|cY}RH8WNI`_T#QvCZ!efy;Ot`$w1wJB>^ zQLSoggT7TFj#*Nf(yvS2gs0w~g>$;WE|(UUKT)zZ_tyhX|NvS(SkFZj^O6> zDIV(HavybU$9Ye_=1x6QdiM{v5=o-AKA;@AYT)lq8x8BjuDa2@T}V@QdlWUQikWkjKoXFg!fUC05-C*+8BR5z9AQPx zEfMGknfce#chN74?_QAo4qh#s-}MLG@R0}9ud5Rkhwb}43v8u!`(0G6GH9daFqM$l z{YMQ*3aQGKBD~+*?f(Fh#~#eCwI`%}7mvT~{S_(td;K{%AJ$&C zw)ozc#;iMP!ph&d+@z>BgrzB{N=mw@(tr#di;Z&p)MB+O@^3}}s6bc0)LfgP$i3Kl-Ac;9hvlgXVE z(4ZV?#@F6izfN`eE!wQMw$(Fo?nx&^STRs_DM}|Q)8S82adUP}=w@u$a&=?dc5i04 zM2lw_aW#%x`!+n5<5+DmkX)UAxGh1XzP8btiplUK^c`_omzNB9b4b3#$Cb$_qQ~x& z7t&7EUl{GlEZ==q@)TtM0L0DD9Z6d|CetAr(`{j5qvqB>y;5@?%!WLlD|2~Qn|u#n z+IF>K+B*&OaQb4~w-ifftZz~f8)J`l>Rg?$tQSuLZf`O*uHz@o>dSN`75I*Q*pDOB z(uxYm%Z0k7#H{}SSC6UWzfwEy;lq44b8;64YzZDiGFdtqyv$?t)bezk*+CR+V|m*9 zJB)eek2UTc+181iM%tTxZs@uuV3cAp4C|G0D=7^m8q^X-H*>WI9#*t5iz;!IQzni` z_5T3KJ5P=$`h4N}>s_+&Q5yZc{H48jnuVw|Vzr{HP+3-lAZAS|TH`&Lwc3xUyvE*d zT#q8uvis)kYx+%kO_Lm{3HpQjqq8K>bg8!_arV!c6jBmuN+rIP)c2E4iN;d=-P@dq zx^s=IZcOt5c*r6};qbaYB-LNdVjZPX$%p&2~lSZJX&H0pYie%wO`L93{n05- z$?_6wPMD7Tsz(kYo$eg7v~1gI*%flGyOK*xu?ZFP9b3x{p(Ea+1|ZyKp^n?|3ft^T z<>fQfXS^#^kC6ZBS5B=nh${p~P^AYVtbm>G3XlYx-OIsPz!? z1;2dyvb{y(ekYJlmxY!QREy*6l+i+$eZn18B zzSk+~%-t8A6Qkqg>ix$ZSSTt0BTWSmI{+DFOmF6oH(Kg0L{_4EGsJLOlCBJX+0qGa zqoB+0#;$m1Ku*eIkB8S{^#lJjdY5R)R zK3j;S+t$dtR_VO5LDHApT2D_3ludEsJLl!yYub7)*$!w_)5&dySa=Odb0EC4IHTr8 z?YlYyM0~WrEm@^_YPbyJMmuAmugZC`MYly%bp#sX#~UaP?W)M=R^yEyckm6G_XA3= za9c@bKmxF~hG6@^IMGVtmAWC>cZVjiIuNv$ zT!$gX*O5u-gBGX;N{m*4Fg%eN<4@#Qb}O%0Ns`7@7huYA8`@P>AmpHWC>&Q_x8O+^ zMi`pWZN$i69S-Jc*lU5|s??J24Fy|e)yCA?g3h!MNUwGTzm4P*k9DP}`Gs{ZturI9 z%9POxpfoue;0w5X%8Vw}?pIWFAJgAGKN2fexvy#nq7=fEXi{>;46HjdHQ0NPJPTw0 z02I@1am2MOId69`(a%DUT2r$GSjvz?y-yK=b>)Rx-!go_>?e&iP@I%=sP?WP# zD&cO;^GCwTed}_;R)v5ExQqEAcLMnW45fvYV~{pXGaszcLnk@Tf=F? zE07(Mgr=jR6d8G2 z92bpum5@qRI9L+vf?PR_mSixsvn7L)kn3cjNg(!uDUA)O^G2Xj;zK*W)2q)%m*ND_ zcaDbq%8Q5Ib?Z!@J02 z@5ZR_*9`JZJ<)24<1eMcu#n&iu9W~kjy1T`>LQBlIay}el84YW0IH8?Oia9hr=u%Z zJn(&=k}Q(c+++#hD1}f|k~?}}YqnG3My|7g{NL%$>$PrE5ST@$TT--mRFtdl#%FQa z)T<;~SiepsExhg7cGp(SwhB~};Z-Tq+l)q56rjUnbx>~wIGVQVXpVyrMy-#G>J*i4 zDnW6hP=oN==q&`EfTTox6l;NJvF4Aog|z)3Ug|lvJZoXGTI-r9WeouP&}Kb8OeD zefWh<#gOQT?*9N~jeA0_7PJOuij}ZBD+6ZXM)WLUgF?8tueyX`v`INIOAkfP_BN=a~m!Kt1c>leo@m9&LC5WY&-{E%tOjY z(=~QxLWePy9~d1AAyX+;8Ij+Ibjg*VHOSVTI7t0iJd2#EED<8VYudID0-M zpX$O?>H;CPbx{Z&-neTR+BepOj{wwvnBlDnMhE6CSyqF#n2>!a3WU;xoG?ZjN-8?j z5!8lK4YYy?%dR7GL^2mV=xxOEQwTdDS(yqaFK`%SZUaR+@e`X0J#tPSOE!avLCZX_ z&^9O{*>G|>nA}yaA`J|RctOI^k-L!!5V7$ALE3TD9Z^=_V830|5o@!{xv6;%myGM8 zr`p;`#F4Q%a70tP72-*@k59b2ctCZ%sHR435H*s~_`IZ%Q2s<6l5AcJA6n`Q5fVR>-tRLRQ38NBzcQnvD`ml7SV^9Sl22 zG24|jAdYe24fNgQMzQbP#BHMcW8~q*aS)mW)DS*REt*o96N^Y-T9is`m`zHktxbAL zM9C&Z)w?M|ks2y?Xkd!b2}!B#%Mnj%hk)tb(}@~P;9H~a zEvrSbP9ZrZ_eOxPZ}nmaw9#-NUd9I((+=u*YPrde1qZP-QmNmT9JYr+ok>@Su%Ar! z*yt;jcU*#jni7p5{ihH;oz#EKx{dj~M`E!cy0~g_AkZmG%>CGodm}`@H&Q*i?UsO4u{>POeDn|*N#F>WS@;$=rpUh#mgZTz8_@`@mf#8>UFIUZ3y zYDo%DQG`)TiG5|%Y%OY1x=-SXw4TuC zO4glmFV3~ZbZ#)nEMZoB2Ykdrs@hk! zQsS1vN?H|J3rV2FpE(H#?;*b9YeI?@)je=hC5ueJIZ$=S2#>3k2T_hW51u{R3g6qd z#k7XxwQTj@q$WCdCGt|CT49v}Lz16L94)pfLUf!~?mL392R9cgcrGQb)3h`3R04jVQU>Wsl=8rlGUH3kJ5ALKg1tTt|^Nid;b9O6PI^-nz?f4CrcV# zyxu&qlP#Q>ha8fa?2LdKluC+}b(qoVMub;xa|C@{#Bm(UDm-?>QRF3+#zH^?K{|4# z4O7qxYf{~Cz0N(KYeKgb0OC-RM{huTJ@~4=>cVVRCFbJJJ$m}{7yH{qyHY5)k(3BB z4KgObmlZiTi)cqe(kZ4P$H*o-45z#$2uL0M0KjQB#Z4lqa>p)2SIlku1faAErhUZ+ zC3X~`j?VLq4h|i4Wj0<-G(}dEa8v3esFd~^=Zo8R5-T0)2os#^sa@G&u#E_BPJ>$D z>r^C>mUXBD1}2vh8C;X#h@SbqY*3{9gD;z{$!3YRLPDiTB<1ij6i3^FBasw37?NQ2 zemvy;=C3i>&GF@(&;96>6DH=dUa4gVLJ1$vqJ5YS?pRHnNXLmh?9Lb1dqPt;Z;)00 z0CBWDTZ-hbnvfOLlm(3qk75!rSuB>zXvQfL+UBJRI#tuAcw?zDNJf)7wee(oFvkJ5 z)}wMv@=Cvs8_2_iC+4eJC0QP)3CPY9-I#I}$4WFfA?DPQPGh0Kg3va~)u<-AtJ{Y; zRO{!N@2^*;&Fv;f8}kLSRQ=Plq$W&jq8@$M(`1yE^@4;Gr3iHZih=Cy$C-Cza%^NL z{jByL)5OKf#iG(^^LqzzZSAdYard8XuOSL5Q8|!1u$FRE_!H$>>F86=aOW9Yljb17 zmHq_-OD&|(Dw%?5gKfg5YbPo^N@DAZlA^Z_R;rR~-fN3U#Lry0&cM=?r?(u_V6>sC zooyOWOT1jf!m3+j<2e%+eNfr9#p;@r z{FbM`{Wy^T`OO)e^i4~Pq`3|ABJM}Dt4xap2br3%*CmqMf@;6oS z?YgNSFSWLGcx>L3g?^*+9&9|cC#5I|sXh5)JGf#e$(# zoe^a+I_e(mOjAYB)o&cDo)L@G2t2f>u32PjObkefCzp3Zqu8$yZSEkrsLv!VN(o6> zK}{-Yl};s;YD)%_)R}k}(wC{vQ&yduZsEQupSdhB?Y8*Scf8=cM&693*h*BRK4LVw z0jU1~4wnvHFeW}LTD=BmZWfmobVvUH(fR1-($%nTe_o!Z+TNRzFiVKw&6^>}5!_H9 z`A@pil{xS_5(_Ckqq7s_=Ge5j)%gI&uX)lDqVs>t{{RFL%3oR@KH$x}CyhM^@J)|_ zc9JzVo9Zp`T)9XpsqHJgBo=6=(J+}-NqnK_2< zH*@koBTm0aaH(=9+@+U;dXA&z`KLo;`zQcn8(0@7D!Pohn{L{op+I-9sV%2sTf!vu zZfmNE)RvS>Nm2e~p3inJv36HoN}R3Z$g11VpZCV?D`htWX}2s8AjSPKQ~jmd__g%UMr0V&H$NRJz=j}ZP zEV1_6lX~zf^7lJ%@v9gzuh+}tq9jDGO3#8TAw@^uO!0Yd63vxXQcEuWM7dy}+&gSM zQ+m2+e&*-MG`!1S8^`SEF;$z~maXX>O%B`Hckb(P(SFjwZH_Lp+We;)f|R*TK~%?88*1AY`c;?iQb297?MgbFUgK=dy2#7rZ;?)s64AG=m&a1&>SFG$DwQsL%jCrx9f4S+^Rt z8E0>tnb-}b^!VU^P7$Epy;AaIC|i-#rP!QXXj5~qvJ{b0g`u*qC{)s>fOq2yyZ4N3 zDnnLmHqPJXRbv;PKC0Yv>91-}+8iliwfcLtwAn&#QOs+G>zY8)JlB9wXsfw3KHOu~ zy~lI8!KBN~jNBXu*GD99Gkbcg^)2e|?RJ&M5#-CWMO&`!o?pL6S|LS2Q43SkWfGc< z{0P)XaL1!;U4vr6=DVvr+kEe<*WE4Xin)8_J_F(Fhwp2Bey_QOf)=~=;nx}1_Ni$g z)-*s#mQbOoHK?fSF|)H6!Zd=SG50d1Cs}IB-N>B7%)I+>ZbtXrGA~xh7@gu2~Tz#E0QLSl*Z=8<9=lI5WB_4X4)EWWo?Bg{OQWK znQhs2X5VBLd5%rG$sSg;1RAo}nC)p)fOTS4VN?Q$LPu=@<9jMowzZKwNyahN`a5zOfwbW}&2-pg}!XGi@wdMMxgpL2%9D=UH{v?g=S zlpu=MpqgYcVQSP@5GQ%A2TLy(KTH0CtqT3^EOL)9$3-G-n65UaqdbvPWUXElBpitA ziv0cF7W9V_#}8?{an@QUabMM6uRd4x7kY99()?yxS#X~U|lXmRATn$x>OID*r#IO8)!aIY0Pj2EK5w{7x0{OXyK%P_@;iBj=wS&N4OIBg z;est<__H6Kyty`!UAVi3x1TNEnu%i~%UzO#kp&*)d7%mr5DrGvI^7OIj2Ftdh zL%81{reyLGR7!NwN3er|uU-hpw^Oq_Cyq9!9a#<5u}g0F??PrCv=bQ~j(fi7+DzH8kPjlZ8gcskyjv#*Svwe4oyZAgm=vZW+A*~_Wa zvZZ4ie&$ByKPWHSZXm5-La$kQ95ub%XI-Yhxi?J5=SiI7vQX2`xb&-o>sF<^A=gx; zokuk-Myk&mEKR!2i28IhnK}1wPg3go0Qzuq$JBq7-?Ht5BWCeN&{`kmK3$&EjM;YG zup~OLmADeyk_t0JTC(_+l>(s2$y%htu+237ZOg^_daux|g$DsB|Xh835>EBUi&+n$@1oVqsD)Gagb9l(dx8kzU^6gR!xvc2tR7k$yJf+dPC;PGaN868N?XKQf$xdGrJd?TmidUZ` z9)}t@&tmSrcHXZrhueh?8ce;#dwp6WTP?^nTdFD`0FVs_X~w?&)xgVRW7k$Y`Ad}S zX|yxLTug^k$B`u=25K5AxNd8T)Ei5VC9qbcEoO~0E7?p`=4L|0w>fY{wl*uAsUH&J z*tX;^r~b;e?rq5uHQUy|A=|^7PisC9{C+&MPHLYr_^C;_x|MiS`FaehPmpeV)bp-V zKLS06VeRPSN19&~EvM33>9u#_?7Vbmb_QD&C<7^KoTVbh^ zk)bsGMGn0&u-G;Sa$NG@d9L-k^45=2%@*3*7ah|r5z@aR8yU!uatK0utJ{x3f>2a2 zmvSXHs9Tgb{kp?$cW3Pq7>J()={ak8V0ev2c$QbFfuSn8P_*8PxQAeHFCJWeq~51( zqGrM4_twv*{yBUEFL&=Ws*&Vqe0ZZpUx_OCN%3?5;}yK_4szs1w+9_twx5eRCC@zL z$zG~?k96{vCEeG5-doOVkK7;Br8L?#pDBhysimfq;X*(Ix2ZUO*@wx;R?6)B%KL_e z^JYJ{-nO>$bb3QU#|L1qL6#1`7M%+0Sthg{8$QO=iOk55nzbB)g!U3W_=dzo35Ry> z*4kmaJ)8T-k`dtos7d#9!rW@1NQ#0?a;Aav7EkIcyyCJ!5tk?dO5c>sR3Gn1)$AB` zn6~IP^yv_m;*w#fPu-4mzEr$zmb0yKFI@cdY`F)iM zV^SqwrKGgY0qPpntoLMlF`e#Qo@pyf$BCxcZP7Wio)V_ayxgW;u5uMJ;cy*D>eS)}+6AG<@2?ND^= z>5a_QOi~u&9WH7=2*gn0Bj7*miwaY8Jq|Scf5MFZ;Ut+k?b3fsv#0c9O@w8#%OAF! zBgzRBr3uCLwW(8ZACy*3)0RbY2_~M*@ln4&%3(WBw@Py!{ z4%RwxM1f-8?_1nw##GU!x{z|NB(c8AE`(VdTqLVNpC?I;KIYZkq|{uSPPz=Lf$YX_ zewvl)qd~UAmfZ;IBgxRHPn}46HcxT(Vv2TEDp*ENls9Q&zFMjlkA+?N;r93mIO;pB zH#D@d8cITtqd=l*L-yey^#!=udU#uP#+|foyG8Y+zZqU^Mmto84G13WS$zAp)k{n` zLiMK~B9(IWp<8QuTw*lQL}%0;W4emW{x~xtagwSsx2(82G6>u;gZgC*&qs3Z?ZkE` ztpKu@1~x@M-H(1Jm?d=`me`6|{^{<(Pj&>k z8ZuQu4pb-NtwdMCX%duZ{lrJqc z_)lC2qptMn7xZjMu~EsalwLR=y^jqJiws72Bo_eMr06_f(UF zhqbgw+jrSk=_}Orz0!rKW|gA86@ohmp~h-<=;~-x&Yb#M<&{~k(5>#jrI*OlQ<2rz z9*f$Wx^;|-jJr+hj>6k649QgAR&N+I+<0m-#l^40~u{^D{ z$U3B#6OtRAig~D@>8s)&ZY;2-s3t@TXSkyL(<*g20Gcb`gQaGrdb=X0at(=Zq4E($ z3aO=K@eJwq;5)X2=4H+LeexteN83B=(Ej+ChMV3ZX5io%D)_e^$F?3;UKeG@gb zsbCJO3O(5K9^u2ehnBK!@bUhC!^8HTgR^DFHx=2R6harPZO3Eo$#5pJxN;!8w&cc8 zqT6+~lqo6$wIopEXNhr63Hg}Zi*7S($mvg|E@^f%(5>@ifYI=QS{l?5T=A;guC=2o zGGWcB+YO(3V&`gQ=A8~9X#^-5;^sUGdymvTb)mozu8JKx;b=$An{HZ^mZWGt5nins(ym-Od1wGFap7@VIhYZPk9BNVq{HQN)o$L5(H`LY9mY zY1EkSwitIPLC8w;qPjfDiy5i9^+~5p8-B}UO8az=H*d)+HA;G z+hA@+L-CXto(5Tr&vC>#He-4k@g_=x74~IYq$)JV%WF@JLGOsHAY*lWJvpmmOs*2R5ng!B(%d!DVi|3K^(l(dbuD)RWyz zaH$vr+e|361Qj04B}Owf_UDdAP!Y5#MxfAl;nW#9BpK3>J-9~bL(I(y&WX(T;c7V` zRICA?TsN4F#05gNBOHlS0wRaoh7YNNd$9D*NYbETQ1cb88Y!GQf*I*dAc2{h}m1L-_Bjuu%b@-K;7`eyV(vGEd$<{=-Wa1r{Wi9-U%eKRHC&MmKRJXzQm1Tpm zaIMAEW1D1)4{XG2=obeVMMkC~wutS}c6dc_dWF9PHlv0n*s`=kj|4rH$Nrhi z5=MzpA~{;rbjFJpz=k2EJ&lAmGyA(Rv1t)D+0xHIewha;Zx=Ui;DvZEQ;UFKzmTT6iY;O-^0`i^Mk4{{V|yDVCIly2~n0 z^EjXD!{xf#;ZRj4$7H} zt0XqbH3$Q{-+@0<$FIaMB-rvts7^bO(9?Nog;U`q58;DW=ZyaVxOXEGrt|**A2!;{ zcedmzo)*wG6G4f;RL7>QnPXKEW%JY}snk4+QUbM)?^Z=ec0(1{cUx`fHaK!4d0X05 z^!d})wI}kRD1qP82(lAZsQF;DB+k2A*>BBuT3RX{3WvW8xt59JipB-kaj@pF)8Rst zBp&=z{l;59Uv*BY?O&*~RV&+vQ4i%qg}c*BZKSx8(}|;{y#gMFpsL+hWU9Y7ps&fG zsSk~mC6#M6IiAykhth|^HTXsw%p{(HmY6D1LCU@OYmn6;Rk(;sRD~@dEKt^%3Q~0} zG2|6;+JsR}yaIoaQ!Gsz&4W#Wd8L|#Nh$Y#$%&{f2iF;KR`llm<7DH0GuyW<`NS8%Y}Ida}NesIaoouGc)|ZyRB3Z?a{Ty!$IJCSSC7i~#aRo11lf%vGLO;MtIwys6E6(y@| zOIvH``|qjMq;&WiNYCxV-a^lrkIv&QB`z$U)vVP@KJ$pWj5TP7XP!WyPD2c!^-(?9C%lj5 z#CUVG zO-6z6njDT;^UaPly6wwGjqh{M@;Z^!ruCMH z^fBq{(Z8o(OSbRZxOa`>ZTpkS3Apbk>puN9gsQGik`&@aR7gTJq;>!>21Zhhd8Fuz z>^-)xmfUM6! z&m?^p^xOWk@B93e(xg1<48oje1Q2M^4=F^@q{#rC0H%^O*{g@Losem;Igp0I}A0NIky^*4=e}WE1MD z@uz!jYg$#|i2-h?c2(!7KkDP@dwA+7_-Z*0Z&DlDmpgl#qRsPi0B#e}o^`&ZT3$&B zPqfmQTX>9;?o=s4N!-4x<63t{_1oKfkJk0i_jgeuZ23*J%g%&0gi$Pos0vdfipyQQ z`L0#aJO2P;R_$_(!RoV^W?%Nznk-92jx@WJIJYKSj^vW26(J~bA#SNOB{iX@M;2H1 zI9rD`O89pr$;l*+fk(WT-;i8TLK{92o@4FCMp*irn^&tHMQb#aNmI_DL&oBO_W%YD z!iJ!E(ifhX$~5Y54LzN>KbEB{lWa(K^HF}+htaO-!7YZcdb zn^yR0!tLz&nG4qWBl~{eiO>H4RDWB~P~51QTWgPR4e=(+VJd1C-`lA9Wd?o_g~`cu z)0GesF+qDa{rxy1?-vIX6<3VB*~0Ek=V{jmZa2N@x&seQTAiCROqDI?RBB04aY|B& z#CY3m+t*SIz1|5FhkJUZwZluXcC-U_d{iQQilv}?>D2o%qs7^m7L4Xr^CzMzHtgiR z-@K$LSBt}sGx=el=w&0jN+>@3Ut7Y2QAc+oa$_AWsAjW7_VvWR;j?KiM2Y*B-2p2= zbLO8J29PQ~)pW&$j0=^H<-VrfzR3+007bo%sn2qi(0!Pr&F*+5OKa~nTUTM+pQ`WRznNin z*1(4aEw>H|AT*qm00qTm`IM~jl*!y~-Fmaqa5if=@llFMekvuGZNgOBi-?0As*?MP z0>6J;1sfB%=$51I<=VBykxlED*CF-6*-!)L;r-r2bU>{$LNX`q#+M7XsH;9EM{w?# z>aC5)8%p`PCW)7q9lFa_VQ$yUNk7Pekj9eLh}%?3hED!W?z+j;u=`7g?h@4;sesxD z8i7w{54Q@SIm=SW?XDk%1RyFYdDQ_-fvr2RQnZo>*G64`w9ale?lZwYOOY}_+sAGq#2 zgPn+OEOJ~GITAnvwBrZ4cVrfkdYfD>-=eES3)ElK&cnicRa;|fCMTr*nBz+MEs1F` zu3Uk4YKDSK%&FGlllg*mD1R_G$tziz_dN-s(*9;X6e}i~=cktacJpsLU*>GS8QU^! zhVG?STSZq3{l`~_&Xp<3=sD)4s-5IxZMAk(VMldUn7z}x+}l?4MMk?=_?v|;Qg0rk zZ;x~DCF(Duh;7$L6#~<;Tq;6Xb7DysgcR zqi(w6TThPSC<_-B*qRpYzX_z|gqB*Nc!AYU5(&&CV7RKfuyoLsws_;sTW?H4raMql z2s#jZsojFDpqqjG6EiqZr z3vt4zwI08S#5X3#+YIhLMz(FZX4hh5LuCZfNTG3AdvnhW>W_A+L8`y*jq`rDZGEq3 zo4L0x;&?M-+cxOWO}S2WWG6#yFqJIOnyFOMk>XJR6Nn|56~vYp?xdx;cJBlFUTQq= zRJTQP-!mn|sxPCRsd#Oq+g(dSLVwOT=nHT~NhJWohE(E#AeEq+ROD_Bo=eQqVQr4% z<&LuQ{(ryS!4L-|^2ey(S#fR9H#GQsS?SMVEGKAjWuiK4Ty?f1wuH9YV?$?1l=2dT z`mXD6l4wv31|xOucv*b0^-1_HKYy3oiRpVkWiC~6_oYYh{67vpRY?u&HS=iffg;zz zmXF?VX+dh`%=d_bh;WK1+t^(~M{zlxx!^1g*Ua1{&ywj6^rFjk?@TNhIAbc$%x*=r zNSAR{&g&vh?cDNI1e^4gDd&_)tr4i5Def5ceWzu$jUHQShlg(6!GAJ**Bwhg#r~hS zPWYV_v_N&^DL@8N+SC#W_6p-cZyhq7-8(bGzKeM4i1x~PBb)CrHul`NBg|6dsSScg zVF*IaR-$qP5?;!Mv`=Qso1!3(QeRUR_T`d(!Swdpc>e%s6e)%7Zy_S7O~9O90?V;WrUWl8`I-csz@kN zZoQ|!0$H~^&ZIkr;R{+Li2Q}g+sBqK&${HvH>tB0NtU9uy5^%w3g_F4%NVK&m<;X4 z<;GfnPJW0tF8=_V3hlZhsx$#brW(+orHa=+>_;o0u!T=S`~m5AZfqz}ShiY_)2T`V zRFrk44l8e8(?Y@)i+{hlBaA&aZ0EOMB3&7T<0&THx6q{gn)@|$&sl$Ru>5D9Vx$%)*nY*T_ zj!N!sLrR=^sH!h1MCMILzZX$zDwH!0Zc-U>EmQ9dxqQ9N7OOKP`Lo`r3hkp{{72o0 zdAXGdpt(kE^N@UmIc3?*Dja2a7bpT`$myDjdIQ`rst!7ktDMe=w;qkYpPSRwBun2f zZ@t#X2l`jzx0bF|^$&N_+%`)Dmmpda{Sz{Jz7&B}Hm{3O1sJ2;_p5ev;`o}qyJL4K z*>HLD*G}BC@nStFdMMhD@F%5sX>qIUWYBKjMVbecyPfuGDB(+>R5Vpz<|E<8HWn}E zT;`sUHPg$tiyBAbM=Ph@9+NP*;scTsr^;wo%Vv}r4$>)&RjhStG)7`~<#JVLO+KD~ zO-<43F8=^~5L(;qG0TX7nZW>f3Me2c$i*rV{R!#X8ORf4^N0Nw~g{9r6})AgfcIv`$K- zy>&lg{r>=Ox3=>a3G;hj?E8Jaj!&n*($RaC?P%W|sRLXHs!NU8NB2M?ib@nJJ%WR# zDz_Z!szlY|ZLxx@;(D0cTYeVJv_i4jE#D;u3KH84Hh`3+DNc|89?WO;ZmhI-mjUY@ zM8b4=WP)<2J#gw3_inZ=T_;*LrGn$Rw@lXU7MoK_dVJTGkd!uoQgb5`M#QGlTtP0} zQHyP!Q$D00P>Yu=Pr2;vyK-HximcAKh9aTD)3s>0>Cedu$q5~LVy^w@!E;H`BGTQ7H_IL@w_G*H zHXlV?accD?&2}i)e^I-(I*$eLg~1gzD2}A)z0Yjcj{%!WJvw!%$B}QF_1yf9m9K)Q zwD$i1ZbK5sR_DaW$Dkia9I^En^%pYxyC+POv>Te;%ayq*T9v8NTm?~Eb$J&SkWvzp z;7uvni1Be1mCB1;t-<}rWxl9?Q0d>8!e|}>Pbpp3Fq6+@Re!A^0{lW$i2u@vK&^YRHZHTlA@wbRH9E(dgIWo z*;eD}@I2QiA>5rqyVH3=+jfIxvw+%Ak{x{}ic?=e1fFSbKU}vb*~I^{Xq@ zS0dZ2`y$`|PW1D|%zn?o{B*{qqeQkaJfSJZfB-!B8mdJ?O*)f-F>#%FpAq?)X-AjH zY5G^;cXzJfi*uO45TYq60b{8Z>5Y!fwK+?jn9a*Mtr08wso631_Up9AjUUTt=+#XC z@RXW&V154pAClm1%X66(f%&G^(jvo8n=)60wuf5MotV$_P`ZpY4JJ6W$10G&I_)ri zvdO7z9>MO#Y^3T~$ACbrp-Wqb;?9_Lu|rnU6B>QlSq1?^T{AtnvctDo6`7Z`pw_gu zsZw8<>C{O%`$@){5TTyAp(^e8x>M#6*ZT2kX2D-_?GLvs-zqwSIwWR2xNYa4+fK3& z_FOf&Em9ISJfo$NO4UTrVmmLEJl;f-k2I1C`kmZ?hjynITOTG4)M(4Tq#T~P%o>zc z{-O;BzZi?xI+x`?z}DNg?b=Z*Tz0@ha-}dlq{*VWodq@u1xXYmvphn{OU3li;n;lR z>yLsqmEJ0(ZEFqJ{L#Gn2GMHU4V$xtb^N8qno0NLIo=tr#@!~JB`QUI{{YP>Yg3JvQm-O8MuelOjp@UPxL2u7Nk*0?FUgVZ|5gzD?;!=N%_^1<;p zjTYXXhI=!M_YgldJB?v6=q zxH4C%4Wx!tYH9c243{ZtI?7ZNR+R!<1N9mB6f&smhJ~k4@{Y8n&~rUSdufK-(a?4H z=}>sm^>|dEg{PvrbiouWB-rt^Zo8$DLsPBuR~Azg32`bt26XpgN!wQzqcO83YFY*N zzV?~D40$(DlR7GNrR0p%E7bdOC7TWkgYxqg?R-eN_mbjP)0z31AATo_X_VJwni{S) zS$I%UDnHYS<1S*Z^jcx|328u_(mOr)j!`5`ep=1lO;-8e;Q+A%D(i2~8;I;2|Z8a>V;yw;r+SRGQJD@+4l z0Et7yIc2+fYD-QpNvJ7uxsJhsuH*QVS-DYa;dOnt(5DW0A7R8$w?Mg0fp;zrIJCT# zYN+h)#FPZ;2=jrIg1(Lnu`DF&GD-0e8n`Yb(dH6TXp(cvr>MlYEptTcQ*oMTpWEFo z-YhoDzOrsMkeGL6W=oCJ^qg1Om2sKM!c{d`*V{#l;=ShMGNP@SIwUeuQbErE;@#<8 zO%^uYV$(ad>D!Z2x%T4`WJnH4jX*2$5l)!)4YMs=wv7Gs)TlcBL-C_LyT!Mw0#XIu z14^B@WmCk7OoE___b6qHs}tpQA)Bh9!i5(@85#A0o)~2M2 zS$bIv?+aLQ49TKasWEnK$+jl3aj@KwRFy`Z4bbyy z1tc9MNuq~pNgcSO-LNBm!-MffuGhHywdFIco}=5bbLSiJ=d3M(1u?B$rpS%S7Dj;o z0KqqmCj7ba+|!Uts!4GNw=>&}Uu)#MHy%CDia)f}cP};cG`D?YY5GP-K?XRZ^-3nGsIhK`Uvh5+$Wi zIef#%SME97axK!*w%S9A`FC>~g=#B|_I4{z*d334gb#3>%$hYVg4BWVN=-E%e||H! z>_vyG)LIQkracvE54ZCTRMEka`;IH8VJ-v?>wH3POJzYtG;x)FUZpwW!qer@hI6W* zN!X>Q5h@duQV1Z9qXsL|QmO@<5+^=2-OWyQyF|Cs3UaDqIC4viAC&fIjfUBk8ON3J z29$LjTdu_2z({lD{I#e4^%#Lx&v^D6L!XOHM_Qc<(|m<-+mjai0I7s{jRB=AiaA11 z36!WRMz~q`Y+({bBV1EZg&J14id>gf9}!g0(VuLWXpm#b2S6vvd$B{VDX-wTvSt(M z;!oN;v0rk~Ekf!*a-}27U-M!WZJf?_Y4+pODpyq0ier-;nXn*Zs5Xby`}?qzI)I2k z$diW*l!H|PIUIFJDOPAyJ@vv=az;QT<%S(oB{a;MVFan6S6}0a2Pl9Q6Nb>0BNaH| zPqD`t0~{ka%iV<m` z8PrJ6W*Q-`pKcpbd5yBiCLyr<>y8@3EVARP2x+3?)sj$2&^po3c*z|FUe!CHVZtfufl}$v-SnK z#W3xwk0C&05vEnaYMB0gv|d;))-AsULOI==;>#ap*^-(K8J9IS$|?Q=Gf0freLMoc zjx6okqZsF*ATbKn4@%J$v@JCKWesYseWwSl0N7wnBvC3RpNG2->Ifc=)c1(f9H9#T z0C+|cG1QS<0Kh;$zZ|hG&nX(z5|oVAvO++oeQ@^Ty@c_DV`Fpj=M-!-6>bgC!_ry= zstjcZ-zx6X-Em!ec!YS2g_isX6^~YXiv2$?!d>?aNNr^e5=<=eoCj)8=FGcHVmBy7vN-gu6cK zh^1>*kxbV2gaO%yWaKE@ZHC*Dgb&X1ee$OBCj1o@=V6McOBBwl#ef9=mx`yI&KV)~2-TaJ;o1l4j{snRv|xCXCr6Xn|2BZ73``ntEWh zEeTZt)wymggZ?4H2Sn;L!b2u&#FLRRG5x^uh$TvJ_62ABaEko=$yJJp67Q0n_)nMJ zPTKqNED&TZq#?OA^%6?XquYr&Doq$5laDy6)YiRlv5F;(P*{?Bn&MGtV+e8fIUIeC zIMXrg!?=$#Q`5H{K`3vAjU2l|>0q;NYbDQh?X$dg+$jyYdfLgH?X^?mq}5D`OeJe) zSM#J2FdlwA+qPV7OOkJdsak!{Mj3;8)w`-!BhMcdT37E}3D=mpThs3H+wpAtL+N?G z6}hzdc2?s%!tc2sd7)p@(Q#$4kZaT&W_B%ybjQ2Tla%i-E=a+C#%Q;F_X74mO3pgB zPwuT>?N$AyaYszfHc@mjfY?Exp!XbeRU>E+5`t)=W0cr7(%9R-k~eCZ-|neMD?jkt!wSX z$J1qjIo?3zul%!OZDOmsqX~NQ?HjzkgKTxkl*6vA* z!jXMMZN0c81I#rH!I`DfYZMis`yr)c{w+} zrd^(Gs|C5q{$NQ-S_@)|_w>V~XF;Dau7lpyv$ zv+)O8g~{+A2;h!saRdJV8??6KyAPf~`;&-X`HZ{?dUt9zx1Ses*2c0uf4p2Q@7p8k z4k-_~msH~NN)Ux7#7W2uEset-?+S|>S77xcD0An(94cW-BrD=ddJn%G=7XaaY_i<*l&WAexCgDaGGW5ZIWQVJ;OX`Z4J*Z!n<+90e&VDlYu8evRV&+o<*8*0 z&3F`F*T({}>T7~`7TvePVg31IW$Al4Zln~xv@oQ(6-w%m>Q97@ilN3sdc&l8kob*e z&zn}`FB8Ka&h$&_mzFndt*^yg2fBA0=&Erug6%Rb@Zvh#r3!7ymoUU+l#$x*dD5U_ z=NBTj)0Yz!n}IH)AUC=dwu(E;PqL_?OGxyOOiO(YAq7wtcMb?+?$)p z@|y)9rDl!W?UD*l-YlCeLA?I_Wt{ zKyS>IIsH$(vIb>K#5p+-wGMldNqbAoJ-Snpg{ZeLU=Jd5XCCpZWjCw9nRn01utP1q zDM$NY9Vi~v9C)vK{cYOzxVYPWOP)yjQ|XueY2UZYv6aX>cAtwVHzfM7-+Qc0w{7)} zrDuMvhT|Pm7TL(_d>pC|4wDp#GQ>55703^qtfr)th!*!Aw!`Zc<4GE%s@cHiQBvGo)F-)uWn zWp5MnZm+<$8$)1h{yAwQZ`)Gs$flAn(kSe=4Jnd{q5 z)n#fuJ@^f+ODk@%4s2bw5$M}A&4{0ydf*(NI+RzI(}biFJFtDd3d@VsLK{;3vf7>jxQVS;z)KxNUB68maNEfWREU7DO~F2Eh3_kN0EBGPLEkWpBp=kE$r^E zS{|jW98qnlTby~fPetC4 z;ynw|&!@I8TiKANTjky2OZNv8Ox#IdnIe_UBo@cB71G?Kz7$HfnM?eV)5&OZYt+xw zyVOUhSEi$V@(i2Zv1?MJZN?AC+2e!IA~Pf;>F-%l#Xad>y7eycRz5U+2_um_rRplf zXmRtncRFRxBP(2Gd5Wz&hDpJgyHjA9d*-}oQEl_;mvG$F^lh}zvd2&$OC&n+?5#+k z#pX8E%f!s&?sB;FD)rxS-mkJ(BIP~l&$-ptTvE+`++AC-2$ge`n37p0t98mFsZxju z`_3jwz6gcN5kr1<>9#@Ch`-}r2AWe)ijVDoRt>ebPJb4F85_JKq;(qKJGi$*l)!?+ zk{0RkLXMO8k^D);lCx&SYV`M&QI<|z`Cyy(60Y0fNh>YM-10oJ*Y>6krw_4f=smQ> z9)EGgX>aj1TQ_65darQuMD=9ZQD8KNE%6$+#UBzI9yJf`6PZ75G553XT$N<%dQK+Q zV#F6DQlRx*dp_TYX3u?_a|i`aNp(Rvb?wA3wQ}UC6cLk~3u>}-p%q@am2wL0`x4r2 za!phF%gHEZC#6uD0qa&4CeR8opzKiTlLSsAo0zG;M{jKJA1P zP}J3J`wuhqXfE@&)F^RA!%dTnBip?P@StFblVwDQbmfiA(h#(dl7Z$p)xTJxg5L{X zbSO#2GE>XUZ;V&%{y%Oh8WndN5phkPa=CZv8@l%$`tN4+=eDHm+urV3YU{~&A2R;{ zzxNW3q7=Dr7nX|ZB`824AmvV&(QMt6tIa+pYjW<(iBb-qE4bu zZhfZ&!r19*i$}6JhJ3{^+&f@(d47rzYuQTVa8}oF0bz!F4LPlrCKm*!vJIG;H?T-zs!1tTB(Ek98T;aZ>`flK3PnP5h>~*<*+$$;o7bux&3kF?AoL5x*Ip0W7iL2n zzm(-jOzuV94K1|*AVA;02VA*cQQ)>lB}9nO)|8|Q1%ctlb8+7B@uKB&Nxv`Tynk`7 z+PhCLA*7?r9~buF`~Ag!wruV$;`yw#xL!Qf$?92JwtOVIZ4q53QjE!YmzAj1LbN@& z%|d&I#?MN}X8#eC`*JzKet7HI9No|yx5yi0b~5dv(^G~Ju_a%bIw zoo&?xE!PUPq<>@{m~bud&>kq@=N*}9v2Dvj?ssW~C6*d(PAfjD@Pk3#aUNzw zAgbA=+V)9aqdH4&*c>zIUe^C}ZiO3(w;c4HZlxZJxg zTWH(E2G$fgrp|MEq4n#%IZel{F>W8TZ1xU|cG!qROeHB4NF}hHST_S_@tKU=yREm& z1))Q>zR}-&$9rXqd_|b9s){F8nw5KLi`c0Oms*NtB0f%y|B~%W$4t`oe*N)sb_a@;UFHJt*Zv$_EvskgKRg&Z6zTAJKApZbZ zKDYO;s>#;QKG?fRWsP90MZYs-++fpb~lmjT>=GLaoA5;91@=;EkuXAX~X# z)NH4AZy&y<*SB_%QW3Z}LRD3Nf{wo$86bappanA>hY?PE#J1B+=`2oZ)Q=Op!Lm)b1F@H77Lq@Dd8lA}5dq%~w?Gd5dprR{f2-H%L zDnRyNHw^+MkUkin94u%-S9zuzC{TtosHrmjVB7Zj7n|(1?eHQYPDp)aDG5qNNvR_a zw`s}I16-Ppybm=WQqQQ1oBiqheX*`egU9w#>ocB1g+I7Dk)+g$RXxKVzuMiht)+5n zQ_H)PxE$Mr9*3N%3*Nlrz3uy2#~E|v+?@?Vh@OZQ&Bd#tQjwWG0LJe2LW(gqthq>< zITw0ZI5T5U;Qlt2+qd~@(aQVvm5=exCqI*AL!&6I6`pvcy?s&1x+GcJg&K<0vdH>m z;H{VG{{UiH{a^Z?w5I1eYJ=Pb!O{Xmn~bmtX6HGj=9uU-S`bEbsm_3TA9ME<@|&@1 zf$P1i+r1CYx`242i23vCP3n1e#La&BdE62No!zc!d9#8&$^}4RJcNXNH?aJwNd2%jO-%^})_uW>tEoxOsB%E&X z;H|12dy$zby+hY)^lL;V22>KXrnLtl@1`e{bOMdE*vjF~Cg1+3IcsilZp62_alP(o zL8K6pq(xwn=Sh39Ei{r;qDafyMk_P&lww`sW46i9>QCw~U~x9jvwb@KFJ2QTj(FA7 zJI}m-H z&a}phZ^!CQBQKwOmNjQ_{XT8$eS+BrG)U-aZJ;GYeq{;@9>EEE+M^7Jllu{nUxg+F^BF*zGG*x>W+RvWfq3T#&8c^q}5C^O-o2QjATtmDihv& z&QhLvTGmM>zT9;VZi8~+aV6H`wz|0K0-nm@V5-EqRH`^HuYTWRhipgpvNV+^x1~wO zlM2c+S(jZ*T4NL;mim)Q;>HlvuDLprq%;VzGsx1ox`Yx%Rms)RU~U;j_t3&fs);oF za5IvqY$-xm;`kBcNjFKLe2tBVX3HHbDG)I`L8dRjlbBVrVgoQKB_-_<-6+!=z+8al~-qDe2JCl}{Ex97h{<2s*Fy@OS7L4#b`3yZ6`3&Qjz}1racDas_J>~D)0nP#Bl`piYZ4L{glE)dzDAs zgW^V5)$It+=M$dYBrU)6?Z)Q8j>&qKFKe$(fHS2kefT!Q0Twsu*S6*$=Tt90vjA_A z{{U{w4VHAh_ni2BNK5ax@RFqu#N)#<f zqr?F4E5ElD=@QnQ187oTN*+o5M!>3no3fY^aibR8!wOcEB}w>uMmjE`6%yWXAcs*< zE0Dp^UIk_O&`@Gun1i2oC5$Kr*qURRF$~s8*Bk=-V>3gF$t_9X z^a)9pi3}DTASpvscMg6b*<48oc#WLXLG7csksWd^Qw3wm6afBJVV7~*^&!^pS3rBT zMO)?tApVP)wyy8H42!BFZKf_oDY?W9EY?Fqz`p#tV0o%A*sfr!4Z98PE`k!Qh&`B! zQK(Ce1tQHl3hKj#&}>~T-V*u+R)bY1*@+_}7i@EJwbCN5vF&#kyr>u9@^TicXhN2B zwxeE@RWrc3xcXE-3n_donQbo5*?X!*s(CEPx4q@O2%sPu0zHIg4l{X{RZ~UXtd|*4 zomlk4$P2Yy;iu1|5(-MQ$m_O_W?AzsPYbM_n$KCxs-?;QX*xVLG9yIt>JcYKR3P>P>n9m(6G}KsJM?L*ndU%JutaI)WA|+DNl_@TvG*C1rv)XVjRzEyW zQlmWfZ>_aIY+h#CE>X);BdnzhAb~(mVz|O({(dQ}nAIn*z}y(hIK^}!Il1u0ZM#nVg8KGMq)8BkE6p{O;e z#g;Z>gsLYwYql=;i@l;E-8Iy+xZ$8BmsDt(X&;oH`eR#%mkfw|NZV8rcXg2tC2I`P zti!Ak;z8}G?!dS4hfYOqzwP-}$5lSur6i?Vi)kcN-HIF7&Mu^|okeSI<%xKY`)%PN z5|RDes_*HA>Zckos7yn+Kz198DOZ}w_zin2ihPW9T9LUhm~|?uX{XzOWhxCh)F?Z8 z!z^-tY}I)?P91Ev}_rV4wp)j3G`C%+w2s&7CJL*Ip}8&fqW zw+M9%3~|Q5laLwrVZ5z~(CxGAUFUzHv~7vAP=DT~g{8V4-EIV@@x;)?yDkW?zq(^v z_@uvo?jDWsmh6m=H*n3jZHC5c6DKkp1Fyi*UMF}GUlT|UTm6>l3bSSH9x}8K{8xRw zdU+s~F>Hxcd%VZZF(b#uhOSlj3G>-A_+w9bk$vN9Wj|`zuHGL{Np~q~(_a4oDTAcr zzHc5n#ya~(D_Ig=sZ-nzc{6kQWt%KWJ9Ur9h;Z6MhE&N*3OHrW+T&i7kl5|^)~&Y0 z^sjmD_ro%7b8f40wLx`dBe$R-AYeJ&y)G=7bfLi_TiQGr>*c8zxnep_W-CKdzSG%) z?BdC-L8p@BT1}nC_G?;O7W+u5XcZxJDnEWMZD7#=xvG<#I}_|66@;+TX+@|2Fny~^ zoCq#WR8cLVeioLwDo;|JfH;*|CFe{y)uC>Lhf|WXH5EBxTkRvHv>SVueQXVY$#-yH zn2z#AL{^A(^&agBB;X9K($%jTvO9JsC%~-xyVf!rOVM^d+XyK%P0nE?p1?zCClpyd z!8fE;1MThQW!-$y$=k!s7hTUGwpU;9v6QIofO|hogZSd2=4IJ&VUdJU5Xkc!*7J)c z#e7R7l@fc&&j8dSGF}IwAN;(&m4DAX%ey^F?n_t9T6P*2YS_*?*(g=T^o2-y8nPt` zq5_9c5bwp_*JiH10a8u+PPYF5<_qSdu)Q6|;!Atsx!kL9ixOf%qSp>OgYP8v(1e@` zyyQ5^B*CvfM5)B_7SlqdRHl6~NQ&1ps=r9>{N~X0L20)s-4Sn$DJ~~a0G8Abf43Ic zQm%xUGNt5t`Sr!bJ9E@eq>f727sk^hUTm(t0rCM;GUK)9H0n(ci7H5;7-r#XniX78 zu)kqARiB<{3V|+7x%R-50oPcRDgN=73rg$lPBXM-66Lm~s4}Hc{667{t;}nYCh^w_ zaz`<_F}^<9&tJp>6Fy1{Xla$KY5{6l6eN2vW<9A*=sv}nNU#>4rw6H9-Y(7|wp?67?AVb0Yw4A{;5L`{pDyiEaZi zml2t`eJJ{B*(qQ7yVZL#UxL-o1pCj72%dFNid+r2NYHxiW1{?5a(?SWt65c<;Np+DMEXe)y4M@p9% zt@7$OlogVXZYE(Vg%mpFi4ibSLWc^OBw4N;IM#=_Vbm6;weHIZEe-mNvD~9ii2M66 zyr(8;GX9Z-90`VvG6(}Pmu?Y?9FH9@9Gyx>X(tbiYC&>MAw)nx{{Y^W{o1g-G8J|C z7%;**$U>{w6`nh$HC%o{kq{n^C0^r#6_JkMD^QlD0ZQkF&d|qjUT!F7Plqwvg00!{ z2Xm-SY=pKH6s-sV>nETXmgMt<#l&Mo1s2%<0QrUg0H^+pJ3=5p>Z%T` zvBw&?`6STek~sKR@5AFN9YEVrR^o?*l=hzNODRx7F6ORp;*U++66eWW#k`{It*vT8 zh?OBlPs)Ct2=Q2K7ax7aQ7yLEDpH65VU{e;~4Eo^U-u?mt}r09Kp1+xDyF z-_VDlISB}I&`EES_xv(-d9#^70@od8g$xxHl-JDwl7X(oz;-3gE(^m=d_Ag^)l*%3 zDXj)hWL#+-U+iDc_j}QG{R@3N{W$s{u}raWrqN0-JBQ0wenK!AW0)Y#V;42M+ zNe!fgikb`!o0{jv3m8gr=98;CYs6enGHO|tsOAl~{ruI66`tn7txvb6GMk3w($R4) zaJKGuUjph`>{k0!cH+TfNI(^LBBc@9Pi`}pZo7`GyyWBu+}oRO>}w;}-OD*J;X-^eC%y583g}LDgC%XtQD`PKn@0%{! zZPo*Vb=4y%$s&m~%Td60XF!`+RVzm1-M3xIAjr7M)P$4r@x?+AJ%a*H=ZR}-5rQMx zQgkHMUgKO0PNbO7oj9XXb!2+st&_svDA1WKuk_s>Vh`jwj6p;?K9(nk-;mnaZAvV4 zTLDEa&}ULIA8EunxtR8Cxm=P-2jJsmQDy zW|W^lr%`4r~3 zwnH0hVsCBzFgIkg$hvAH=B_j$gcWwU`?O7cfRl`d&)fOCgy~`qO};0f_g~cAi?=NP zeB%l(3;7n;WO`453A9Y#*P$iF5KWpFg#k3pDtTTaf7JsX{o6nF*CzEPmo8soJ|_Bw z{{VNQo3*#YPc~Vty|=b@$c^&*YQrAXicxO1in_e1Rk*6HN8WMthi!c}+V;C?Teh3? zJ}&OxtGnj&b;|YWa=!|J&Ab;igposyinUG$nl{_38c)kxg!um4Nf=O*tq`DbM=*ce z!qAc#6)8U~`fS~M!HSDk0Nax^`N_{Zi$2$~wQq7$ghjR6Vky}U zEyY%eZG;sPqI(7bzmXNhvc_7c(x%3h>W7i7$5Le73S;fYB&2ouRsxWrUx!w+ z-;QccRf%%-co5Gy{ZM(Maogr?jg@$f#rqw!`Ehox+F=%^64>yKPPP)YWCu!MDbG(Z zT^rQm{{Sj_i;rhj#_hegnr&!xOL^(CMP#11g5D&qOMyMis9BE_o%`R` z&dPP!E$r~_>Uo7IB4W;n+JNc`yhh!y5inijZ{e{wZ(#hiqHmAbK(a3v7)0~ z$w`ck(6i$E2UCJ=+mziBBbG#M>e$(vyrVN#?U8)tCnU*opnJkbXYIv}y{@{*tH|Db zA|t`J?%n4j$$=I7ksU%tmQX9&O%gub6>{~Ab*oGFf@5IiWy{i={lv$%_(CgTxf`b9 zUv|IzDvMn{<+?}xQTF3Io80u$+WXnL+ItOgyzA^mgtG0pc7_oS)og0l7=TM{AZk$t zl~THsPW)uDGp<~fWa@fO4%=$PDHM_yrMY90hQzG5ftIg&0etkk9TD4-Mks2&hK*wom@j1?lNW3Of0yDH7pvs*KU(vK{Nc_0lr zP<_j-J4(xoV< z^Z_XqHNsrKZVS}t5e#`r(H(c;{{W_h>pt0Il|4&tNIVP0X>Bqbz26oXmno1drX)<9 zac8ho@5Wbf?yFYQP}P_CJ2alMM47uiYw*vd?WryYrG39|;#(?Elw5sH@7s8K`!%HH z{{Vs6s+H;@x>n|p4GxuKA1j%8kI5PbcN-o)(}2A1^)!?IW4m3rg){dTBko1Gi;t*{ z=2dy!vsUrb<=W%AOK@9J0HvmoJ&CEs_6KL%g6ET~`+cZqv%98q^uvMAecQ7)h00R- zQ`H9l0FkkyUx|J@msL48b>2$+T4GzgP9PDTTnP#OTx)Q#twkjiWpi>aH|k_>b@e4P zX+xE_Jw5FPcq(kc7|OmoNo2`R)ZGAT=N%pmOa8ELy@8DrYvx3fs0P?n$oP zuG1ZraGt$W=SgwN5}sPRsmC2EQkCsd#f_u}$@^#VIu`3mg%8bAs65J5b^9>*TT~q< zRFuZrxr7I&C{2lll8~ zUM!q(X-w;Fk5cz`@|_@CcD;dw)kVvV&G1=jf``gUQ>LPbp{@YT-7VQ%YQr%$yO-XI zh(MdpUYor($U-<`)Sp%!q1baoPG#H{<+aD#dRk7Mb_)}QPEL@8rmf17qLEslkVg~@OeUq{yYJFUfRQ;w$fBK1b z>UOLySOC~_PAj7cN>z}5QlAN8 zxW9l~Z^^js0ja}PCN)W^8H$|091$4MJ0BKP;o@}zgFQ6fH{>&TTG-iKb7xPPc-{8} zrHJW5a#C6frnP0+iu~=`7Tp%C-)z0C-6uChJ2@}aw;K9<+jDkDr&y_7;lw!I`7RmA28{ zo&xm6V%jiZ$Bk=3P{>JfDoS+~4&6hy5Ido^;i=Hu2GF)yAU$%tOKQ(*hJvT$xkMo; zNUdtBbih-(!);rkMtw9P(%Jq>G+9pvG&ogD|p!{Wy?m#aS?FybwWYU#QSLhdALY8j_Sf`JN9{$qjNxiFZ{ z3y8zo+e46fpMF?2cFNnAJ^Q}5q{Ktp+hW?olWtcjj~@$cw2GA;)e}UdRZ1ZskS+Yk z+Etc;*qeN>&WUJxE8D)BJurP1wvB_AePrA1ex&}Pmk7;Wcze=3SrKLJem-^4`WGF> z;IE%0wSu9kBnoM11yxe9c^i$)tB%}1AGg}3*B2VrJ-&COe}B8}`|5H30H6I+dW7}M z%vUa7=dJku=-jef3^yfO;@NE0^pHebTm-3Y#&02#Nm8gasZBKVz3^tsVK4Z(sAq}*Wr71*Yl#D@o@{bZsTqfjn z+VknFWc@t&$7}O9tGf#utDpC}eq)1uB{=IYJpt5!>vwI*psUMmqeUf}8X9~`AP6nq zcMEHlBWY+**gIpH{blo8`}2F=j^f-`RW{s?@EU z+-X28dk*aUk=||yx$GZLyK)ZE+pya%#0eLBxkVn9`alGg0r2+@n8@V0Nt=voiqujj z%a1)G`MPZPCCU*aK`nf$QmOD~`E};tOvn%zRyaKv`_o`4=}yL?#kkc3QkGgrrw8X@^?Go#)FR5WD%1vmR+2 zKlRV`8kN0mv$kf@xh@9WX)A7Ygs)2Ebr})Zap~Q++KSyLbBX4i$K5XNV{b%x=-pe! z{mGkxC7#%aXPK|1t8506rMn^bRiFTr=1C_TyO${%cpiOixk$1T?w0=mOnXA(w6^@V zX7di`ij?nr`(!!$EO%4lgv$FIKb(wH%UA3!BN|4YBIl;Q7}?gXzi{Mk3Rc-wEf zT;si_@&I>s-Vl7mgrRwu=vP&ACm0^+-Wz=@ekY*zzi*`aw>$%Bz4M3E8`O25YV8{; z?|ZX#x`T^!`trwaN7#I;gP?V=KR80WH z85g-#LNRProjm3J{{Un9R(fTZ$D3wrPWtTiCu$$QU0PFaJ+QXKrs%f9hr=P-?ycxbIx{BS+L$pn2AY)SHB(B2w2XM1?7C$Z<+ri(85-Qr4wZsY)s&lbJc--c`XF z>$iE?Yxuq_dmv%wk^_|h46E-48qJEzjD4nice>@Lfzxgky!q;n^|7Qy6d2}&zW@aRKAbdH|yb)F;oJ#hBkN`6-B>erL5+k)uY ziA#=JwxdQQb%dYDRKs-a&bY*P#IJyT#~Qt$&u%Q6cKJHmpy^C)@Zj1; zGcxY1usdPF`a~*}8RFSAR-whZT-RrmgP18aJ=q*pZjJQXEH>MmvpMm1qgQ!t)52D0 ze%yGTXU5N1#rP1OuhMiY@|;ytp-GEvx#!0Rs)l3i#cY)rNF-;AP9rRhF%Kn04=L&u zrj-gcBN0$uxQ``DO2|p2dUlFn`%0(+{J267xW^$EMUtuLw6d#xhV9ZPW zvY`#zbw>#NrxEADl-7rtDnpGZ5Tva+5JASC8p5SS^kiAPZ2H@O!(6Cw8it2CBvkF^ zXK|N4VWOs9VL?fGwJ8g2XjZg1g(ie4Dgc5r2Rv40TUR3D)oK%qkt=r;mx0&YzUCgY zeJfbKL9I!V&3q_f?LSb_KmqBons3Xs-wIQnmzxPghoDwf%N|{|uha7$JfEgo$N3(G zzgtkvObl0g}G6o+a*cTvJw)7 zD=>Of8oVrpIMA!k#-Y;qcLcoCPByJUU=Y7%4^*jFIA|+3D0VBSW($<6MH|#NogOvL zD%gD?)VPwBB#g7fNfM~0apTs{ra#=H%iNY2fKFg3YFbnAc93W>O}XvyoLgK@jHvZE zb9jv|)iy=GYh@0{QA&cU3RDg-nHHQRD!7Cfm#k@3RZQ!O;j(oRB`}bxf&SJOGNU6C zRJ5sHT8@IBfmpJz>Ok(M!|-m?Es~?jjNxibbpx5`)K;C8=MaXggz#hzd}^C@GmS}(3hOrk6EKESLW_3Y71Zv{{Wa$RsR4-1jlc<_~=(% z-m&7eb@vNj@ONw36;18SwtHiVp?2SH4qYUBMHruf~g%dt~XYqy`N@h{x!2~G})YUV%oZC zqDhPg`n1Z9Bd znRWPtLus)e?XSFIIPt>+Z;hP7JBQTx?_Xxj{psKscgY@EobKHPUH%_J?`?UEluAku(*FN)$3szez=XJJO4Y#u*LT$L# z=t*P>0zm88*A|%Aeq?ngD?ZSL4YWwQEe#-NRS*ZN;AxLX4l6v|If3 zj*?syM@m!-71B%vu=^0^+=^E{)jFDrHUS6%m~x6j30uQ_Z5}tYyEgSHEV>eYS{hI5 zIHA}EcpEQrg|tZ(T?!G%ex~<^=UcFL@|Cx3YN}}3fqi4zuFta=jNQ_AUWK)r3yUjs znNugasS_lwOUpG=t|+NQ_3Cj{q(8X>gOr?;T(#By|FYHgui*p(`ZC%qm_*Vu^- z9%QLbA-L%8acT%p;&ISup40O^`h73<&lvhg^j)*pxv%i-vEjs3#`SZ3#+pjsl9M6A zs*zPyM?7im-M41etSLHbd7%2edc=$Br=C>C+?n(H4`(2^x1@ts-E~URwSY40rE!kT zz0|l?Wl2fsax|nRBeh>{CP=O=Z=x%zw*LUA{y)42eqFYt6gs+*;87LniSh16GYQA9 zIz5VVSE|jcwfLRpZZptW`4SiG7bPAHTe$PjHr)YC-c-H3ifIWGTu9U1L{k&|)z30@eqLm0 zb-mxOrDpYemv!?ER%A(a&`HlQwH$Mln2J6kB2ZAHq-pFF6#f`(3FB^x}J*i z6HPDeHqapD-B;VF(IQR*_E#Bnpr#hhDigA4d^m z7}wRnfAbSOOB;o!b zSivGI(`}U7lZ?H=y_kZODOUS<0c-ra&S&t&-ZXS6Z`^5$v04nJDW;UR07%7xK+hou zK?wk7_hL?0N{)=oDb#`h&b^uAlvxKlHf?2DgwXdKG%7NvzXIIjZNJ^KYm-%#K3ecL&^h$vRYJwQ07ns+2cpfZ_7vv;wFXLbVDNDC5v@_LXim zF?pFYuc+!b1+{tKR|xPdP~DT~sAy#kmmOU)%qOKg&H>yn#u0TR$BgGBk=mXd^z{hDL_nc2?{!yR11=z!EOOmNUH%9T? zyXNkz&g8h6(z6lT6+x4rAOkOYsCsR0s1<^5P-jD@xZ* zJ}>NyI!Pi%`GWTct@c=sPrXV+28Y0f5#}$piBDV)JCq6OHj3@Md1_R^xHd3t(G5bM z2Cg_deV8Aupj0dnd(zyHwJpa9QO>6`is?zR#t}$LlPn}<&|{@4jZs5Vbd{@CpN9=3 zvlWQ_uMUXoE!&!3Z2Bw^INjVIPT!Y_&fZG(P0QSlx8cm(YqD6Ms2h6Osw{Gf*Gkrs9A945XWfsPyNkbf zP1a4`xYF`kBk%sn`U~5;1Y^>)oR^CkJaNQ3{{WL^IsRDN?=IApkw0<0SoY*Asnq+Q z86BYkM-=-Ak{{|ULv!u+ z_^raXL}nCrQUh_?LR)PiBAF!=10P9kJ+Xy@1sgC?Nchiu?ybXeo0dh%NxwEAzjbkX zs;_au&KjBj0GIZNZ(JNYs-uSujA$e~A;lF{RKq12gvwL_)|&iT;lxsvPn?+=D?V%Q zrKZvZ%g-$FwqlJ4UMe zFa}0_yKCA{Zw^u~PRjax!rQjr^IxitRE4r(v-*qK_Bk!Mu$1Of{OJGz?G2+Y{7_!p zE??3pvWm59h_Q9`o#~U)D^n~!m-x=xJ8&Tdw|ALq<|R6zP*k}ug}AStfFnwxI=Ej-?h~an9taZBXaN=iQsHd0bt%Zmaye z{Pz@8UXt?Mp*?h@ohkPKF`}_%-MIQy4E_7pE_bY-+tSTgTGc%=XO-J@Y%;GGq=N}zWYW$I7 zxAw+nTdP!FZ}}5m-Qq33ZY{Yv3)~Y$JSznZB=z_bbNfy*xxLSJdPmZaLfd-}9@ew< zKX39Ti*1!?X}HWrOeo{wVYG(PLF`Dze(mW=)w5-18W3KUz+RTSC4wTR->ytsBP0z< zb-)cfDO}0-3_i|1%dU}CJ1a8Hh*oZdNq6Px)6{L-2GCR$P^&)j>Qn!gIMNdShpdRdL?bw7PNU9i{nOA2j z$<(u(FNM;dEq^KHg!hiUF*}6@N>wcu+u~-J4XMb3!UZZaCYY8Hpu)=Ro*}ti*CF%XCWKJJA}v zodDZFB){>0khh^g>H=-hDj8%N(KJZx>y1v^+g9kMGcUStvul0Hp(^HDdPHM%(3rt14Z)jy%^NAGm_= z-ljO?ixnqtdQsV~y>KA2Gt@5aZ4-Ai!=#~Wx4=?-i*gA7rhuOGgPtrbhGW4>IMEO)KFvm`R~TXS+;jz*@G z0YM`?&%X%dkmy0aC5zj>r7knrY7?_jYK)AUwntBwt3a((Q|!le0~8RMe&p-^_`1T{ z8#dcJbMAZ5@~oRu*BQyT8IBbM_*-Zwgo2@|YNApzQ3E`5@?&XMOm&=D+;oW!@}pcS z>H6;>9;!EkoP)^lS`S9_tFf+anGDP+^O|dPZW!fF#7B(<$nk$tl^T|lOlER-YxioR zsUJ7|3#>h+)FhPsQu1HN%kSb>jlsWtOZw8ZL)z+M)x^G;_L{U~upm z-7jNoM^|g$yK*=7=w(_i+`n^gOhyVxN&UH(Dkwjg_3t=?Z|(fu!Kq~WA^w&8MBY2M z4+S`5dZp$ee%%Tk(d*sXYn!)tOj4Bn&Pt1fEoQYsRZx-K2TXc)2GwU4DmIl5CfxU% z`7`FY>PuS(iMM|uTpLM>=wad#5`ao_?ikWp!g5t*JG^y&f_)%c>@eB8-&k%eNZ~7y zIr0+Pdr4B1GR2jRC~xJ*)mm&qkcN&=Pevf0TayA(GrO6IMa z7^vIPA}HDXOMBdsk#@c~;c{xVIQyO@8lP?=@Nw zlh%TCC%j`dpSe!#xnaQce0`H24k_Gfr55!S>nn9~5))}l?d{E*X-RI$wBP{DCL13K zP!!a!X~VHL##RudoF59{fWUDLPP|PBztBIT7j))wby5 zSyI}YB~~q`7$4iV<7)vaNUap=g*6&?;@cZ*Ec3tND*i5X-kmu!%-R~QfE4?Ew-Ne_xAySr>tXXV8neYx{_DGkbNwAPxS-qTbHc-H|hIgKfO17)CaCJol>LO z?3VpyE1!0nLXjFWD03jGWDta?gF$oiV`eSC0NuB4S8l~gG@p-`+raL2>-8t;XXvf8 zFMJv3Ug2o<2lWNL##OhUy=+d3UV*e!>Ir(=wb)s43G&oRQ>AqL?kP&h;^%tybdz3~ zct5}GubTE77l&sJH#b)NJpTZa_IvoZr=Ppk?|AZthCRjF>g^QNj%=mtR|fgk_?(Avd?og%4IHGeYZL-jooF>tpQa&M>(Oxc_l!5a>4 z{NMY+K##XJwY@r&yuw_5LyGXU%Rtq~mO@f8%|#%?ktG#S{9JWPy%OTjNOy?-oBo2_ zcJv3XmVZ&7R6XwksFCERR@>v4&W$TWMv9}!HO!we#ShAXMCRpYcP7^s{{RoS+ud~w zsYl8`wf(-{-#vdZ%>J;srt7~qrNZI9x4!h8x9+#e3vu^WTwOGzD4h);5D|IP*6RuP!WxltR|yYtdg~fXri^n0X=BE?iH{2W?*V{41Mtx9z%J+TRqVV$Q>X zpt3XZoQ*x0Z1CYFqCm?!QF@lL7ph(h+x9o^79KxmY}{?f#AUBq6qJgnjb!JD-puYk z0TA9)Bda;}>TB1A^5tl1vc{0zgJwVq5$#aUl}uPU7Ycf(E|}bG{k?9)QH?%R)$pE6IJposCC)TJ%Os)<4pnvzL09r(JvB^?Z{xk_$~v8>wdo|m@r zC(V$=?s?p?LhWoM>l<1V<2AX|eA35H<;7&DQo>fbMqdwXJ6`Gi@0+=r?z?vm?MK{H z_E)IQ-8U-g5aN{@tQCKHvF`82b9&s=7fu{}&3|s~E`OPG@HO1rmCQb`xm#)MU9n?* z=z+9$ z)sHXk4o~D8lhf{(r`7Tn^0h6P5emOehEvc4kG~#~gNX}Kq)#!-&Xws^f;DT8w=GFq zfhcq%#Aq`#uWxP*(4=AqE80kQ0#MO+Ct z*7rDD+nc^Ffylm`IPuZ?^X?7`B6E!|Foc}as$?p0o!oL9+JY}36$Ponj#1>T@y)!6 zx;ZzIZ?0}V*}G-CD(P@>YF#y~sVz6FqiutwBe-|rZCg=^Ta$aJ#qE{1U43FwnW9<* zjag7rt_jDt;#W7yKcHWlH>Pdu5#_K#Tu|vz-R{PpX=yT#JvQ|^8^PBc;~<>U5`P?P z?Qk>s4_X#8)QyMy!@)a;QZ=~+Ey!=ah&ZI~yH&@jrR|%)okNLN1vhUB)zKj5k0E?d zMVx4e*5NJ5&lH2PcLA1r0=-bytNAq=x$i0I9}|k*3_VReoN6 z!>7MhC*y2aJrjCpw<}yO2;z%V(-)F;Tz4YGm&%eqH~3&rzZj0ojIKuY_xyy=?VNPw}B0UN(g@r(eGmx447`AU<1BN`+fjQGsU?SY4=T zu)(Hbdb_DN(ID91zeXqfn#D z3nrrui51u&kC{~@Ene(7#ENkZHo6hwt_NsvyA|SvqgF^KufGD_jY5+YpIri1OwVp2 zNK_k3skpGHPbdddQw_t|ujMDW;x1lSQDmHkW6e0r?lj;iSUx1vr7>k~ClGnUf#^@J zI;anX_TYQA(b2uV(2Aa;38yIf;?0-9n=_Yh|z;w;!OUKH>I97 z@!Xs(z5q2q%D-fWHZM5CHfe9H(J*g9s

    OD5(mdB4eBL}I+Dch*YZu1q8zO;(o|Y1B--(J3i@vUJbqw{jBITpv@21R^ z3$>+~$b(NdvBqlWYYFgu$D2`{h@))o#5zrj&iY&3*V@82qQb3@?P|aG>M-?qL2s>T z?eZd9D-U}jEVJ-O=$M3?H^=8M$yz!3qVG$?{+Yhmc##$RFAG$ecIPcOpE##V*fuMT z@hS+=aU@2tdfUs}h^2}nThskGNYZDgW!bymHcIPydc=?K6y+9V>MK&KWE(n zwgrb6QIN9U%pRf8iu>+PI+1?@f_X|kOq({`HG{A*Gmgsv#VlEkCYLet$H%IQnEWeS z*Itk!gJpDgPED$1qr28C$fai}+a>1LGCqAzF0KRQD@Y(mjg_)%L#=(?S75)SFh8l{ zC5b<&^Tp+>qykn8PB~R3bYF7@c{;9)YJ3~V#?1j5)9Pp^VC#dl3-<4owc~V)GZW@; z&kf5$H6*pAA)M^M1vb@a35;BzNL_-GrqS87 zHe2N3yH)y&OO8JRpdZIf#lZ8}e<2HL$HT?c9drcLVcR=NoI$Iqy9$LRC4y8QS_Cxr z-7M0-ebeHrmj~VxP73`guVtPp`VXnXyvB*%#UdA+H$r|lh(|!^o%PyQQQP6^Y(?hk zFuk4IG-PGQTxGv75imt*617=L_^eaWLD2L=&&D79Y!8Om4+eGSEg^y!6&ck7At4o7 zs8|vS&}g+OB`ogdT|DdjqETX59ptl>u>-lxGi{?isVGAaNdx(m=)Nk~wRYN%`kdjt z(>UViRiy)Z{*+X#7ssMhvqtjWl3luS>m@2kg#@kQa-T>f<)52hOQt26DbxkAx+wE_ zh*ci(?$c7}?!Z_{Zhn>J)6P_v9%$g~=S4Eo&j_r!q_cpGNr{9y8Gr8O{*DS_r_L+( z@U-;p%NLZmG8q5$>Gz!6aH;4v3C-)h*3^aRH>>2MPDd9e)ct$qu1SvoofBFUVC$w4 zpseZ9GD*=n6Fw~UA)7sSBGpB92$~Ka4kJuI!Vb=9O;pk zq-bYf#bWsly;}}q{HFtl$odUYF*Y_Xc_bg!Lul@whi8VnnMS9SBfmehGTo7Vi8;%O zlohXFQBT$8FN+#3bty!8m6Y$^hOlw;69c3hOz0ohUA{;4qfF?r+_>q0)+MZ)pmU%e|~$M0BHC|%#ri3#l%+%LhtLA$8$sZ;QAMURzi9N*y<31YexvY0g@4jCh1 zLk%b?zp3PSQK4#eDOfi-ng1U0(6^Be_FGp_)C#I{_k;yZiVn5qD_Z8Rcm#Z-@vim% z*pTw$prJ9d>I}hybtn9~?>dE%xv?DD3fWG@jg>v=xs;;y+ZE~25qa|z#hWMbFbpy6 zHkj6@bKBLBvfpyS7Xr4t1lkLOF}npVZ4#1H+1i7~4IrTI0%E^bT{3_HVzJa5LJ{0A zI+2UaUVbfU)2tykUJD{Yr_&tp#>0k6BvgD?3NGcPvAC+~&lb7UqZ zu731>!YNxzBTsL*WIS!Ma`Kr=?+nzXpu5{gitKEnn;o|(c$X|5E+*+!kC>$orY5^6 zLvzumD*2_zg|3$x7}<*bAE2o>U?luEn^+Ssg3&0GT>mF@0*|w-x^T+CSYj6*cDeIJ zI!5?SOQr58ZCPVlCA)f!3eeNBwDs@G$G-U(-8RdGPl}+CuHf2NnN^FFD5$+@CLSBq z-A;YnOPz%CGIUeO4vg2;;J%^}j|`MSFmSprU=IECGL4Leu}PjY>ERdkL^T<#W&fW!*X^C_d9hJ)_IaS0OjfE)7)~bn z|8%ps-4(Gp0kEQTSy*x2e|@{-*rip)KP)1dE|AZf%RXJhY zZC}_+(k$Hf>Y37blA5);tm|m}HS1pj?wO0YlNtF;7qpKHMpybif6d)5OMRh#DBvKdf`O$~b;lNi=Mbr>LkICM{Gt46CU(C@+Y%1jCSNBDtd%Ax zJOU!7!$5S@2DU8cRS)MwO3U%A8PQE8ML1ll@hK!lWto{I_WKnJtRDXqFuYXcYKMvWxsdQlx#!_E9sWWv;(y6twS+i^C7 z-gEKBMf2I8r-Tq@&uH1W54jhdg{=ko#JT&$Hu6#UwvDE5=zmKe)egn7br{brbU%&4wg#?Bg)bDsyzA8ILCKFzWMS=>O7A= z(nv;K^$t}MH?X9KYbYue=$bZgx!S_`_4+!>l-9g|*yKwZ>7LK0zRF!xF4n838N?(7>Ci96nRdfILE}BRO5=2C_l+?dsr?;^&2V6x{Bgf$JZc>Xd6Utt*1|!bX0h%5+)3 zZyL92Rtjm=+fM1EWfWiG5EQS*uB(q$KLUtByI|-~TF#UcrOLFAsr^nJBi@+Ufeeba z*waq##4|eY6*Lx3g*VBr9EDanyHU z{;m18A+GczK;yk*NKtZI*y|>Y#xq_2#1>FyX=uHa=9_UACPO(RB&w&I7Q|{ox~4^v z(#qG(#|0#P<{iN?_5@Gc5UIW9RVJ*Nzap+QGJO!zMa6HSCa~m4tOi$uyPAO7rx0q! zQHI>0$zt6U&O}v74a9a1Dqp-ZCZ!y$OGwr;nq*or_NmdRsPfy<%w@j=C&E7`2eCL) zED1OEpPi%e`8KXzAEsLLvauPGF?yZ4^k*i7^JJf)zSjJdqu=0d>AwzzP(64djbF#r zbjbyh^6yP{{(R|l=9-B;m54s~egvU~lT51(TU%rIy0POgReE8b7`^nWPgO1xl@n{d z9Z3IV!k;saUtZYSn#j@Ib>-+A_!9n&O7y+m=AnA#GUFG;(8!E1$?5x(*fhJ8%pLk$ z7oUP{72g6~g+zX1W~HBYlAs8&&TK2$YV^Yd`v{B&_iDZp3+~dO>uTZ&b(yYhdq*5g z_Zjb|Xf!#G0K?YeU;4=l4w^1dRVHJ*YTm0B7>0sc8OeYuZ+Hac2A43ay!}eK zqWu81`>2vCyar0T`A^RYOlwOelp@D>w3e%zei}%HJbiXDu|5FAv zERolXIECXsxat&)|#cXTp6e9T|mtX zx)|8v7GofHlVyNyg;&oqNmAF#0C^l{MXTw*yi?yi;4{e|bz;7iU&0E>zVhUJ!jt40Mv9|M7gvrm2xWyoRC4N2v8uqUO z21@3_!|5|(n|9j%fc`X)D?3}3J(WJyhcGe@IbX`(xuRCQ`%EnBMX(+L3E0srU1WFM z`FP7xqo0pvD)(U)@S?n+XCsCBFb84^Y?5@Rmd}}DV|VTvy!w+@FTcTW*H4R4q~2P`Gk&1WFR;-4`CS$wmV(KX;{bu$gD>dx1~IMJ z{@7SzA3A?YB!TjU$XPLo4=9tZ;MsW1*(A-CV$(z>4}oUD(MD|X&D~_2`JflU^8{SQ zux;)Iks(<$F!o<`JA>+uu@U+6X&&i=Ex}{`H~eYc0dCD%_zsEr@7~)8jJ!8Lf5)?7 z-`19SEBqX)K44KLwfFlOR2I}Pgk?Y%XUT+ex-wQ=a0zIQDsK#Q-9!F@_n1beLG0Mn z3)-h{K=GoiDc<%Beuv#%Oik!~kB`J1SGP#qR#3#^8~UnI5eXXhch0?wX*A98208e; z9IOgoz{GW|`LEWY43wF{C({x%5im^7Zsg%!3%`6ymfW~#a+9vULSmHaXQGCnhRAUn z?=pr*K&PTl@3K%qQtXzt*v=7R^XVW=Y^|hZ|JH7(c@{RPIoa@i3)lgGSJh_FORP_Z zw)Sk#aqSiD!_s1Fb43OgH|SE8v68D8>l1#X)Add_!?y9%4l+09fU<{tm>oQcyYtxQ zdIZ8a8}@J%hH%5`)pv8HcKNKDoG{OGvVSL>(m!HhmuFmI@>=<66+1_SFs0yU!8*M| zD5+G6wh-;_MOoHZ1cY|DA{tQQ|6!f?$Ls!+Y`T|WDa)q_cFD>#%CH6FWFD|y!BZI( z@%%nR$s)Tc`vZ2z-+L2LjxtRu>&7SyOqJ_nw%k`Pq1T0F6Nj}` z9?75|^9}exBw?Qeg85={PmHTlPD>0+S|U8hEO z{rootm-3LOBkPrRoCwm>KFjpaK|tK7D+W_XIHQL3#jm$hQ~znoIBJ6yIhrnCtx5~T zws27W(wz*+uH1|WAKYcJ@z z7;~%gKQ#(;c5j)1MjSE)LfCC(@tj9I`M-Y&%~@WFA9*X-HLYsJb>#T266coNv|H9p z*WY_%F;yS^i4(a;J>DK01|x-iab-~pGrl1g`)RC8`G0a>uX zW_RiqGZ9oXT*r zM)Keg{Q1n| zucm}&Jk5)R5aazpM~b~AzSbO6U!}+Zi{|IgUE=dvTw3`zcItBNz2IL{9~USO_+IDB zerO(i@5E;1IfM~uj;d1k_w8#n2?DLO>=xM1M?R^UnhFgp*5%@G@FwI;zWS8G@9UXv zk-?U6*63EF86m>#A~~G)x-Bh{^1E(g&N(={ix!Vo7~0&ki5>H{Y3Gm%vvfNcUESaD zw)frSe1Sowo~o+(F73NWcRV5?Jnf@JtR3NzW)6YO>yG50&`dTLQ_>-;erI9(A-X{XI#F2@M zKATOOVLWm_(vtM;V1*U=!wuKLG0Zf)QubB2#=P9zbLWHa9cVk)EADa*!AHQ9Nw{mf zE0N9@?|*J>O~F#@%>L^m)%7T0L?r+~EL#~9b0>T2OHOl;(S1Uk>4tKPz(( znU08E8rN!jUs@0?NN^A#kJ2~_)Hp&-1;0jUXWJIcx%L@NfI)s5j{t^`wgBXeNo3=0*d7hP=Zh?v*uskANcNCj1^N!35F57yef#g6c<;1^O%;BFJ*%n>Y#{l_W z;Vo_KeD`>eckHKldswSRES{L*gp=9yJrGN4vxe9a=}z-Z?ys!IMZK}_M%{rbPTEtQ z#fccH{OQcXB+9MtL1WQbti|}A26b=lW)bm!c-iduBhOW|%ARcrmIy8mOH4c$`71%BQjJgM1C{g6rcLc~8RRGvii?rL{cF#MLq*n-6SVTY z1Hwv;G`RCE&C14=Qnca5nCZ+PcF4gbq%?&+Zps}|dhVN&g+37Gx!hhTEd}BJehFiq zCJJf%$DgP9blNS>jQSSV>^orn4u3i=mCjcD^j20|60iqP9rh@vd@)FzO0;UhMOcdJ`hF)qARG=SxkNVnD~yQYNmR5!sEln?!;w zb;IL;&NgGVd=0rK8MvKJv{XpEk18OD5adZ)8*#e8sP}UFiS`D}ye)1bd8g#KnIoHK zMbK4=cqn=RW!7Bi@vS!hedJQE-;TBsTq9_Gm&?e68zS>yWS9NqN1sWHPuQU(4h6pk zfg0UgZ0qvK#m+&RIKGX4@92+tnylXoC5sGY-A@?TTZ=EqqwsX95kO{^yh_{_SWcAT zxLbMa5;-wDl3_!4az?)b_OvwlNOMRu-r2#PaKprnZOz`%(|^!haD*j8E&f)p7D33H z@`HgeC=K{4AD=}#`!~=PuM_%Q3mYY{d})DO)~PrUb3gjBZ_Y6xPm+1HdRI)@93M%UXbkbF78x-w z>K$VHs^w`kD}Xbp8vy=SyQLZNq4`go*p@HXkl4+^+^e^2wosL47irJg6nb-B+E~@y za28=oR*!_ooxPMye$x}RPcw3uTX_BbIP!GcH$1E^J&hV$twE}x zU+(1YT^y=^zM6JaYMWbRe6!=Lud$VF`hJb-qNR38<{*#AW^-{R>107qAoU{^fqF>X zq4wlFsbPUFCx{1wfe6r8>_EjSy53P$`G-IeDLqSqy?ltV;AjLYoEBc!z{M?#j}J_0 zVu}nBc0*NTzuFhOAEx*Vv23v1zy0Q1!K-(-uk)eykG;_&U{@kDF>HD8gPgxCqIyw$6GOpLr3I9NVy**&Y#QY$MuaXlQ-r zME&o6FqfMCe7^dxo-U|qBvd~yucTgtitdwKVywle)oSYvfD2(ou zT7)!l%A@DWVsk}g$Z zBHnIZX+DbpS)lSe;ksj>e1_qZWWJ#@-TkDTc(_HG*!NQm)sTe!YVm66f-tdSd1ddW z)C(SfSou;h`24^Ry}PfYrBnoI(zM6aDQ3V+;L;={u*~mz4%2>qHB}wiQfhFwA8nWmT2fz7k+{8*A&i=SJm*SnJQaxOCz+E*m!B zjGs#NE^za0-n|^30u9%0c7CUolll&mKbf7;OWn4gqR*2I;h0JCuv}?cryYEqCw87P zmJv(Tfmv?|z(RZoBDs^a=|o9$RtUi52at?MMw*d63!A@6_L;{V+h zkYUPUW=bl_P9|>*T%T_3#_sY=fdMi9rgDmyJHgsM z9tOIP00NR;*l_7wND5~h0{Fyu?-%+IKW*P+UAKI;6Of;pIFBO&l=)A=za*ERT&%S> zEcUpvC3 zy=Mb$mz|r=xDYJo=WP#8|z9>6oNs8LJHum7L`iFtLg+!!a|Xb23_3 zR`%|%o$m0~zVt=go;^aHwoNE}K`N!x?!WQh#aquwK?iX}uKd*yKWsa+MLwb(IoLHV z9C?|GxFA?aq^&gV-anGnt8ae|G%Orw+!@zeQZB{UKi%hFji*wdJOW(Adj__D&L&dP z`()|-#r#ul5hSN~e@GdG%$IL?u(a=3jczG&!!s??^n84{I({~c){8}6@-dBz*T#)Z zrL~=^6!=WSl~dP%wok+nnc@`1^+|5zF+KmJOWEJP4tAD=OASqOx1qz3U9;bxmT_Qo ze(H`=P{>*RO%m_r!>2fW)^HOev}LETR3Bwp*4-mss2)G@KiGP!xG20Y>=%_B1f-Re zZV;urI|qgu2BdT7kX8XH>F%KhW@s2X1d&F`A%_O(MnKB<_dn;oI2Y%3U+&Md*LSUF zuf5mcuKqtPX@f45*OJ1+Po(bs<+*PladdM<>@_@^eb=f~A_;9ksA zEjt|ebhNZoM$?RsWA|r5q5MxcIY2_Fno_tWgiKF?DabE>AUYPNK%@|LHd%s$-5oCf zf4|0}!50u(6#h7^0$@8S^MUD^mdFQ^z@sCST}so+a#;U=Zw_8S1~-c}i!bD9&?+4U z7n5WkE%xvQd^QLd8NYnO=j*^5)I#Ib(XbkVpX`-kyI4~=Vqy09C0GTUbarnTOy5rY zITp^J;~OL1VLW2lW9SkZYPAwVhV3!nI2Ai^DYKTwG87h+9&s^^!px1`2cS|exM&eG zv%sriufu(+7wxUGx?cYC&ihk8^g!0{US-o`I_!sc-$DeaH+^r1kI(H?0Q zS)@OWs`)xFn6^4=uENQqlswYOCo9~i!sOf<=YkI;NGi>g3^zPdhv&DWXdTJ3@=A(Q zJt@sEMuRQ357jjS;<(+fE)DNa(JM$2V5;J@A(^$MSxuzFXtf3BCJNEpY}wNqD%(9$ zWRC59{MI>&>>?feg;OgBygaL7gm>&>4j}aXOoq4+P{m#t0lMw zM2zF!IpzjN1toG*_!*rs%^)l_0MX@;%k1(eNWQL!0vO@f?B$31N}g^@I>h5PTYuBJ zJcvy8D>s&C9PfLEURmE1$UD>L|FCrC6RpV*W@2lYN3;C+sP=I$+V=cz zz)XPC2+yX4XcD@9W%O8~hRt9f9FOF#x?#ysJZ=hEw+Cd{2JWUwN4xfjAkjgYKXSBuD9j zvo?<G72|mhU-@S_Z!|cPjpUEdW2>VcsHR1>=e> zS^}YB?T*jIViwYn;l5l#`m)l#QicVkas}(&JXs<`;5E?wX>OdLg;+1YMPR@y7wy%U zI@*Xhhs0pRgBZp8#GyAWYKOxAu+~5bN1n9qDT<9CKYS+r2GN|X`O-e?S(MnHyO8ch zy+_yIQk%KrvVvPx8@Im|Q@1aS`S%2!^`NW|oK_KJimq>yUM$(zmWHx6VM~H9j4ZN{ z?JL-SX*UF(6xcJo|1j14q25bJ$~8D7qtBkAvYEgqDRZKugtVON$@`#akg3cr=ll!b zgr8@X+2t4`ha;@MEy^8#PWI}_WHbqWO4vvWR5AtBaC#IC5~CkR**$}c;N#a zUl8-a_ZAfe!8U0BNW&58J}OSvMp*dgYHfzJwWfaX14^tz_exZ{-;9#;w~YD7-`r%c z8immsMvOqV)L`j|cj%96Bm(+04c}Ft3(JRQ|HE@hS7AFQHd3oky{W<|t zxHbM}?Oyzc$$o>>olMHhroki2dNK)(Fi#1yX}KB4Vd-DI$-BvaIG_IV=N&b-YNxbNEygs42Ldmt8}*3i(*G_4*%H z)@&yInA@6+YEr#;p{wT{v@}`hp;w7|mHY|?h1$d<#s?G_j89FM4aq` z%BB~LO-JntMpx3(o-4o@eEsStmy~n*eW6D$<;a>mLpA?l?LTHnw4=8{T60H5IfF^( zM{gzto2Q)9DFMkBlS?U8DgG{T%>1}&4JNJSti|kXxhY0M{`#(aw0bth=Y>&e1qFHN(=5 z7VEVyI)9w7+5dg%hV)K{6@ynYV~-n79esQ~b2Py}EZsHXy!NY+MLm_ca%)+S7C|u( zT9-EMzLxb<;-;uF->8Zt<5h-rekQxGJZ~j3Oui8q=)@+7{nF zQmY!{D3i2Wiyl=wS9Dt@*#$rrH-j@W*~A408Njg`vqdW!F~F9Z-%S#I%WF$?xxrM; zt4W!I79*7TRNKP*)tthVx7S6oe3A^Z&XLlS0qls;*wd)j_EO*xOoy|QkX7fKj-Y_> zhO#gAt=s6eQLfnUDVj6v$k(wX38hrus)h8rGz>t`s|6&sB@HE=ir>H=oYS`JOxTuL zncykfIs;!mm(S)z`M!2EN(w*$wyj!4ukYfEng$LNpP)p5s=h&@up8ewfpObWGskwS zXLJnfplJ))aB|9K6=P?x0d(R>fm3#&Mj)u&)EDyQWiSl;=eC;>Uw=q*LGAn3#}ZE< zmTMm_R@T&v-ZILk^Q6;76-tX7FrlRkQ$PoL^1`bS8^)+N15bays#*(*)=$k2>DE$Z z`H4g5?jwu8^bwTzG2LW@4A(_B6lPf)cj~qa-v(F}Uzc_co;DEtr_|USGPT}K` zCp+v!J9;K4tD>5B3u9~-%(_!4OX)etv7RdUvIPwP(8t-h<#t646I+zjg{o>WtSio$ zPc!ix6`W}tpFuR1h#q@>JoI-LS}pr1MvBu|NV_K%ej^eUYmQQ6>aW6SzD>pyCQvMLylX5ab+$b^&SrT`z31KQbo-2`@sc&M)Ky}7r+SPP&GlnmkBD4F)c_{bwWI^^4TSPvAYZrzfk z|FA;1rH6%S_kU}?|Gi(l1Ud9IcI?KOcMQF1Do?*jn22T`_$BjjwaxEc;j$!xHooOH z{f89|bHr_3<=P-x-4|`&n-5i^o0#)6I{Dh>Oy%jMXV-HJ{BO^tWfR)yuY~%TcRfbt z28(Mj6EUR6d-^bYjmI+jRQU;?*%Cr;m83>fO^r57evS>2XI`t+{Fzl3DG$H29+%j) zha?5{^BXZ_i{XMxR$RrsdKA?`mDMVew6;se+PsC^htzL=dhu47vVR{4i|P;FJ+E>o z=Ci^lU@ntO)XhN6XJ!rn@nW&fW^Q$^R*AFiVg!ztTM9wj-`(KCQa|z2yF!Td3{O@SzVv)k2?*ljaX%HhGur zw-@934bvk;d-P4Qs_B?Vxu|i>a8j#!E9}oRJ{6bSlMXq6Jcr?LMy1b2S?c>DG2H9W ze4H}|B~g}PJqo*l*~OoM4Cs&km*`I8vsT9n`}&l29`6AQHgj*;R{>$ecgHU7eP$@{saN@TCk&IIn{Ff}KxI7;M_Ep7vsy6MbsTif0VZnQ4x7Td zRe=o?{rR(2u(dxa*NAb5H?=xi8H)GMsgYPd_p=@1t9~EJv**K5G9BW&*7BOo9P`vM z+gMz?6-COjq+UBWLj;k>trQL60+5V+&3bEbok5ZPtc~@yG$$cv+7v~E8;6&Ymf~E@ zlNw_-<1#nBSviE|3`;fCSr&Om+?uUTK-3w@#8>Ht=6PQ`v=7!c6k%UJCcj$H`H@D3 z$>?Dt0D>AmvUuuG?Dl96Ip=KelN&&|#YveD$V*5A6Gp2B6E-Li+SeS50oYvJ0M$Jc z$9r}Gz&l3pbUD9LZ5=)(!H zoq+Xbnw(+`dsA`0@GqI{n}g4)d&WC%zKDKZ0e6k|GA&?64oe? zdF!fNH;@*pijtX^^oKXe(!URwBNKg2-d`F&!(V;mFMX>{gxd50I=U=qnIH5K_Hlgg zGz0Lr`sqmDyIn0CO^;RX7jabp)Fk5>iMNkBqNe>)M3F%le(XDf2m65HP*}v!2p)~{ z`VQ~YskS!sWKSt%ZZTuMIer9)`@2hYcKX#m_1|`Wn^0@g311L(V&m7AsK!bLtLSCA z#;uo^@NF}DSn^Tl6p|ay5Sga;2Rk0J{GE=-qV>22|NOVs4m3PI1(`v2 z4A;nh{aCu$Ca1>uO+#NHt5Me2lLCP^vtPWphB}fq?8xe+B+PvL-tip^f&NKBEv(gY zEHHd@Qw{&)*(^=pcF4Bt&V|i3JC0O4(1pCQLriKw>z{$1wPB`X6{ZymN_1ICl^6qV z9GOih4KzXnqfO#p#pvyGO6q#RZ}oLl%9bKCKugrl;OZ$Q!w145HZ{hK42Eb_I#HJ) z2%wAJm!S#9dEOlPUnHLShqZhvIm78ZKFf+Os~kmY$}?nX(% z`7dD~8pR01>eCK`%f?oGyl57T{rRgLMG|$WAa>>jn6gFEUaY#N2Pxqjauh<~qAVe+|@9#u!uUv(w z-=b*6UN@v70Z$%7@VZh7 zh)H1{_K)F3?Egur&E`Sv?8w4YJbs);s)6?;lotN}h9H&ig4{@A~^>ZD%Icg43%+J|Wh&%cXTT(iFEc7uv zRq}`l?H^xakMSTe^PRL|Ez%t8H{!Z1?;@%K#xQ|y_K!9Gm~#3E8C$nWfAIc+4w()oPOl30^?U0~^4)2#Ja05AZ0RickJX$Y65T!%cKVe2 z*h7E87%QP;%93r9_|;Pt{-Xz%xFebL#LEi5N_fqAxX=449by~$UT1_NY&O6xc1dLi zL^ddcB;(qH`RcskS-#rba`^BMYnYH1)K2=0*UF>psTz*D%!Wj0V0+K7I{uj7^u5lY+3Meip8J@8hAQNIo~ou3 zRxHKw-770)bBt;OxZWn@KnzH5SgWPufVxFfP+x?%zNZ?J0YW?A~r@FVG0f#~A zc(lMjK|xeD#(ZLmJ`_q*=%BYuW~alJ^E?aDh2 zbY+em^8)jxf58pSsV=Km8Lo6i_FI}ldO56=GEft+@UL7~ndfS=d?o80{T6F%e9Z7am`cSkH6*$jY#tqJ^tLYG} zm>*(wMfbwLmV3oVX_GQTGRoVhC|u+*blCLIrAlQAxVl}Ox-qhkQ{6q@YRLh3u{)}s zGKWFvwjUBX{y1f`8cwZ6=+UL$MJ8&j=byVd0ys$hv9|I~@B?rf-?yqeeHkhahdgdt z7ydOA&T30^8G@L74+AdOo-y_*wR{|9oQbU|FCSdYQG;<; zD0930SsJ%J1LSA1z-P@X0X`AXrF8vN6>Cv(5D%p($gb*ys`M)LvLM{x`}iBj!djAL z1yAWFLY+M!4&(Y2nQZ~(lf~@`@lXVQ#{axyY??G8p_R)z>Krp6 zHpwrGLJ84q&lH4G(hYIr|KHB^x?3kUtj;O@D%_km`T`+7C{3R8Dd&W^pOpGllmF93 znk*5vre0|ZkVtJG4<1{@9SJQ_`fq(*=-%hW6Ff|)1Zz7lYe74{egG`~e+}#eyF$v? z94)%&VkBFOa!FcwRxsW=)yx7PK z1=0uQl&3&xbs-o$Q68Z(kjbC>L3)iCy2W-yzj5Ro%D_V}ucKz5L12RIQ_WN57bY^l zBxlt4Qixh!kRP@-rjDWNED-oRSS@I0Y!W^6A3BmCJovU*BJgCEtP)wF$#()c#Kq z0%QxvdI?^yJh68e*f9aqT3IOQOHFB=%#{`z+4P|CsYJj)sZNZ2!&^tLo}A&@E>P6* zN$3T)Ztb{E3}>;-3d2Y*TgYU@mR(gUMHQ#N77%or$^bM^1kc zt@5<|@3#D7gDFHMl1B3>2n6MIP39!ej_#W=Z7RRcZ=?KOQ}-}JcLGoLEA9JUlm=vV z6Xy0_ZHW^9{jdH877S-R!#tKmcktf_yPW~qW)rhRha znN6}l5#pNBmuzz4i7|hDUcR-lnX`AR-!oeLKxKaaYf~$iNXRJTKHBX98!>RjX62_& zCAB!#`il)xJ;ZkxPipoE6J~x{ku{`cD@+i2(4R$Rm~zj74XX89c6$F5w5QYVJ$(uw z6kwP}BJ+9&uR}%cbo<-Ij%pkxfOO1F9M(^0ofB@U8$pQPvEk(kPz;a3iO4+VHU5#@ zzPbcV76FZaxmdGwGabbO*o@<~D`^JxJ~c#5JX=|-CfN6zt(l6q-l0CvW;4IN@D}uE z+BP%+2iNewqvteu{lK)7=oK&F^UUU-J^#k319IBNqIWGWBMB0LNuHBtu5! z+Nsr)-uWnyl=rLpYN$dVpUz#z0ln7TRM^di*FUU{R;QU~bu(9&pn`1nGF1&zO#GKG zQ-$;t9epotUQsDjHf>w{5>JGUv=6h`;Xal&Kl!6SrTN`l%VVZocqcaiFzxfLgz#rg zR#WkfboP}=N}iG_>4dH|ogDOqB0j^|%CRiAMD#|nJn5s%Qa zg2kkWR8u=U#n$qz)G!@{|G)`hl*D`UPosptgT0ARXOaTyW37*CJNBx~gwRavEIu(* z*+k7py%c2;G~~*#!Cebb83e|%!sryyo`v_vMJ0FE(vm^O?^Nj*yM`B59APie-NTfS zUov$K{|TkM>2=P`ebcY5ksU^Pw|OSv9Br&qNa|uCR7J(c+rzJyp?ss1M7%R~@Gtw{ z_v_s+Bk~*u`Kc7D&F$VNJSFARDqr+lJArbys7W_nuFe^SV$R5|-1o<;#%EIJK+4Ba z;^MT^6lMxPvFK%-)7D>BAG{lKZ9SH@%}!YrxL{tYKMZ}Qyw^gTwlhs5W&c8=dX9>W zTaFU(`l-qtQviLZ-LW(^PkiJt%o2{b@a;(7@y`D=dMx0nkV{lLrkWuATzQ-A?aMj^ zS+xByjvdRcL&xnX1;02@j^f#PcBqQGn{${Nr4e}9zJ_2jt#4Mk|_&wa86e$O;H&rB12#$Su6-8sIn zL+3ndnb#4%F&?WNO*3r@rG)~_o)quJcMU3keN6``OpSv`<@O)wc z)7q{~xJjK?N#C}Pj%KWT$8_ z+g_{g;LUD|`C?I5%V)Kk$Mnf#t|0Dut`B4c6U3Y$5`smou!e&4nlBr1dl~(B&NO>k zA!HCD>ipR0Z+bv2rVn6_ikh$HNh$B~i%XbPeI^1)%X+;GsZDQS=1r<52uGURWQd7s z@KrvbvJj89gEh(%?3z-1uOzv8`#FWUdVO4*anBQwuo(r`;QSqa%???L5d`Oe?&1^) zHLffta*`{TEW*O5igwOS30ED?$enj{Z#dmq&v?EH?4_S<^0Ms!gN%=2XKqt)Zx=SY z&PL_d)d9cT{$VYtJl!R;!n#if{d~l9`uksl04rI?oQw#rCC>|*0~1}$fINys*jzN_ zam@4W%m62{M*V8wGsR|79+1X$B9{KqY+vjIlgl7GTx1@2cV)4gZ-OaChqSSQ>3mNd zFlr$EOLuJx*;U%0jDJ`onS)`_Yrl7RhB*|cs`2kU#m1QQNL&<oKgzma_q25coUsV^paE*2;^1kxAh)oemMN* z5WenV9&;#BCgF0cjr(U#u`uSx)dVlDEarU2FyFAv_NVu#ES1KF$hxA6taqC2cwQ?X z_wOB#@@6(p$}j)bfpgN`S2^hEY!{%hLH}L9NY6+t=gLy}o56aIsS;1P8|9#p(r>%h?a(j~0T?>LrSMJt^|Z--BF&{T zmRXRD&H@-@$@<>O*)a7L)4=C(>*#VXc^LCIdMg3JT&j$5R=P4?Lc+sqT4%xQLSf?{ zz?|Og+-)Z4X$`Rlo8=?ZX37tr5wI|@BGA2MvxX->z_Z+U`NQRtYGnHP3^S|E3o%n* zB##+G8ho+5@q4Bi4c~Uv5tJJ-3r$-y;t}y!(InQ94>GqCj(c2K%g9cj>smh0O_~Gt zZAL%S;9gPAbQ{4LYfa%3r9Je_z+mbZY{irZ_D69IszkrdWPG9@+^&wO*Td;?=kPAj zh_69>6(C=17&X7M;jb1M-giZnF9L3j;KS0T@q2qEFZ4kYKzI$wxEn@$=q8M5sp_h; zU_Ew3srBaQ_^myd{i;ZfZ^%l?pEVMC`kGN&M943CD{;mR&13sl<1a@_!`Rt}{uUzl zH+fq-f-aKEakgw-3h+%4e+_P)o74(ZQ)jBoM6fGtznYP<84iZ>mGA!Hs9D?&sNi<) z7-Y!Er(ABd1@hh8T9?`J4pV}CUmN9;iBQh!ExDY6hwNNJonr#<{L_zR2)p+5>GzJ8 zTHS%djhkhq0YSWs+1DihWqV#N@zxfENzO`~ZBgZ~6#?%MHSM8gB^o{&e*NV@D* zx-@Ih5gN;8?2;-|oA1D=Lz;3}^P|4E&-ByF8G3j0s$B0?2KWHG3lJ3tmG(*^NZL&2 z9M5fi9oGEBam8tFv2;n-1<1xgjLJ`+WLNX%QD8d#Hp*ynKhlrXuu#u$hOpDcj=|2# zDaFS@c`}hu9H>xUGw7LQR959DRu&jaNwY<_i8cGT2ND+h`+5ALu~LBhOX@sVT|4>% z*Pv+29s~2m-aGTAYtkrTAXfn7SFcMu*jglpNLE?R5yqzkhv`+qcg%voFAF(?GQ1v+ zNsj>e=XM*v?=%hJcjn%I+Ul1k3<6zBO;nb{oqH|XdiIh1)B8X)+V{38#I?eHA3jbh zaIY8_Al@6mn54JR>H#feaymBpaz~i9SREbx539MXZl2qhAoVcwg#?VD>-hrPhxB$Q zNZ7OcT^K}3yCYniaX}!Yr|}o=xK!hIXW@XPlq5C&GV70*;1?{RBP@?I?32ujf*l4& zOmhlp&=SeHrEh?pDn@p zPv0+-ZL+CzbD#=)!iY@4T?u>0n)W825CxZ@{Y#8HU1bTbHyY+7g(I&GC=d z&Qs}dgb6@raK?rHgS6FD9Lx;JasfQH`Y>zfnp@y9 z+g*S5Ab0Du3Dtv1+(wGTFKGI3?MUjS6Q3O{sv#KP2n){E@WS?sLf#W2G%*K$8y)=4 zTMQJVZl(#(qw6yI_YX+lz`1Rbrx4m1Kr902NEtVL)1ZF#mt&FXSlP;je(|Tb;Aww~ z0bfG>53KBR(%ceHs_8tBOxsE^B@4QhnCcLczs^3EWd^!vCaLxL(i6xZc45kzV&$xL zPYoWAYKzj7<^rc8r6*in%nIODYo6}tocI@Iy9#ThbTvJ<+6dHH?#;!2jCbU3Bm(CEJjJoz7FF zHsP(;#JOGlWbCPm+w{>nX0B1ifj4BfJn>sw`%+g`3KIRD4X#IC-9m z!KF9vv4ooNIXZ}6f>ysM@QtZwgXsLHIZyx(kj8ukDP7f9r27v`GMF=Y;~V>r)JV(p z%u@m0r2)krx}4QNCCjb`$bFh?^2^K(|1TKIS_OX&9UFJ;K9NfgT}!i>Q{kG`;OMGMMmcx z(E9&(PUQ|fWy_I|qaB|OMpJn&1pCas1CQr&mXlU?npP@MrbGJc8s@~m8O+FuWL80= zAl!9Z30s{6U+hTdR(&^vB2-HevK4hyG#(7_{HEE-FJEoZyKN3-4Ng|lbQFxg8Dz$S z&-qPVm7xo~YUjBho-CH*-?rSGz{R!fYDpL|RjqbtE5U-{pnl9(X2Yd-BS&Pp#BXAc z)eaC@T=)OgkU2AWhCD^b2A7pKb7oX8sBDE`M(Z3++!fL~>7>f6&dX=Q!^TqD%|>f) zp|PBL)uAb&Is469`s6I=xA)0?vIlM)L+_~`_VmF9lS9R66!RQ=k2HZ~gusn}1byZH z6`59Uoc^VtauoS|X=TOhiFk#t6V|njRLz5+5k|ejD*0WxlKb?Fr_5Sk#LliAQeY zwFy7cz_w1Ic~U&b&wTg663oRbKu!M+V$pO}%%~k_h$WeUrEb;?Z^yHFI?4fnrH+G& zAs;lS!XJN}bt+cd<$N|2@5saKZoXxp>ZriX;k(mX9XF@SBKpU}n2Gx4;N59V#BvM_%ZWG>2kG%-|v}8DE?7?WXWHBm`>`)Y7(Qnw22$Zt*4cWWuMc?$K%h{Uncm+Zh2;ydCdt&QLRZv1byVW zCzx(-pwb}eR~=F8CZnFfE4aBZA6&;gUtCOnyB=4}_LD~qpbvc=l>6y19&(O+xjK_!l$rpKAHNsv9q~RsyTj_r;2BPGu0KZp?JNdh!$TCjw*=mELfGp@Whh{VOj| zMA)nBJI!2n6!O3Pyr7^lB~jmf1Jmz$lt{fc;K_Z=t)P*Bdi$x#&J@jAkdu%t=g6fg z6Nt|q_PS5&(92Bc04hDqn30p-1n`Zk*w)F@wJ8M91KTRKR^n<`U3KIhmoqVfEu}&F z$AV}%(EY9?w9?gkzpgXk=^RMlVNvXFS=eN7JZVlF<@kb*4zXwutTRa{FITd> zwXC1cET~36F{xsTc}OAFXk-eRm|TO*ax|K?cXI-4ZRt;DdGex#lD0BT6c3Mp6cdE7 zdpY!0NiZX2BuXZJ9`x>IhosHWp(q@*xW&cGo8;7BuI&r5V_$eWw+)S$J$5>#Ubz0c z5FT*-Y*Q4woe`X@=nQhv02rIK0z3wWyE^`XrYBVC9{zq|AanEN zeJMj>Aw;{wKDLpPjjov1Fq~|UesO~Bed_5d$dg9eu_n9yZ8e?h6!1qHO(b^Cy|5c# z7?nSw(=2{6xBz~^h0tpOQEe)N|AsEohL8c1M1^G&a)v~T|2s5X)fA-2I+W6Wr=@%` zR}ZRak_AH@wFCPotmE#zUEact6aaL5l9u#I+AonO)u!>GsTTSUoYrM=L53SE!Y$E! zPfB6UGZAWRjM^$XSWS}&6|;*WWPjMBwqenc?NgG@LcK~;AN45Ma{_1xRpQ@IQc&k8 z2>(CPG|1p=s!8d8mh^T~?ul z0E3X-W$7MZiDzk`lceE4tl*ufMhbAfz%6rsq^5=SqXxWFdWPDtofSgFYp{z=1mTK<1i1#F8n zc6o}G;^ES`J0ZN-pN;YhrQax6Y{w|11p!mkce%<Xf#>UKJvzPwEB4YbNjOeQIN;%^v-_ly1 z{Sl;DzuBhIjY@7V*7TVu2hVry6t{JPHhgPoyBFO8+GWFtTJ+kOz^rZ}_f6VK9o{9c zd6vNJ^&6G@VV^D@sW!SVm-pXYX4kkye2&+zyy9qrG3OnJ=HoJqA%K}L&HCZSQt0Azv zJ(i!|jFLIB+;F<%L*`h9a@#}wK zj8v&F6e-&&nleqa82!>SdU)h(4Hmh5-%4&sAct=*)C`VtMK-i<-zwK!_8oYT--$X# z#&ndr2H)N#VUVZs(s@MU!qdftF^5`s?TW=s33$v z^KI#^Vh59WBjl?xJxaEI9im40;oeYL zeToJ&1}!l#W=&DhbQ0#SMVAjM85%!iHR~S-2bL~tSlV2xLXL7k3`bE zJuC^r4vnJBo+}*CUTESo|D=eap;8W;<5q53S%wK;O-E^UK(?pK$09<$gnwA+tJN=O zMID1!h8xtAVK%e)!-UB)>k-*xn}`sDb@ZVw_rpv?0|Z@x30bb8Upx=C&i}o0E$C zm?NRPki?(fBcorJ`6EX^Hp?8UP{{g937uzYr2k<9_^`q_6JprE@d4qeM)6@x!dh!n>dP=dM4fs1owGp5n$q z`fg?xz_W6_MH2VaTEY3h1QSWcsPLjNP*3aiv4BQTHyNEBK_X4IfxD#!)J7n7*gjUE zQAfU~fZI*gUzg3)b_Ro6dC1^$0mLP`;bq*rN~hMyAxb=pkme=bKMayHSTDl&UC!r< z{hO-iGz7RO<~EEmj1SGL&Ic+2i6)O5bax53wbx(r7`Uxl*rz}M%od0#h<>cKi;FpWoX&wAC}HLqv~#Qn)!k2YfE!up7LV?A_@;) zlgQ|Z{)6S+momW|&R-c72f9ou`S}u#-{p(-6hG>PxAX!3;JYcZ*$LksSZ1WHEA=K+ z;8AFcOeqd2@8Q%dDPOY*|b$Q<}Y87o>!fcQXascP@-$@?5p> zGz+rkHUNEyEb=YlV6ilxygBJ3rjw+U5h)q{H;o2*v{uW*u^W;{>cRDCmwY2Go zzUk;6)@o%jLUF=rztddB9Is7!HC|{_fQ}rGZqOG$HN&yitzsSY_o=W9Z&i4E6gz;! zmw?}h4I22#Go`NJV?s`AT#{OE5op@ek-?WB-EX5(x}VrzQK73xUe^&_9VO9b-vpX7 zXI|JWu6-32^CABq7RJ*Lg+Qkm)=bu9M_i}uKgB(c%i8J6L$%h#R&B4~f+4y4`tdl6?;WrMNBOAdeh{$ZUEH#f_i$cq9SQtzw+ zo}7m(4W@43EKC0qe3|ZJMv+6*RO%qylH7z%Nh3vn$UlVw{*qtYAqTeth~J|Z4KRxc z0`2LS$Ha<;zA~kSFcxP~#bt{QmyhQ5?mI&r{*N-F(|&Uq`qpNuk9xv}w6D)%UA!3# zKhRgFKb4cexkUd&S9pM0@0U``3D(&b3l$rfQc%f%{CvwdtL!!LEdu#-KX3Cvxk2H| zb@Q*&RcwXe(=#7&>*j(fhHk&qe2dzd1c)F@&y}x^FQ#aL8K{(yObNYw{=NvAcNO50 zpG-l@fU;~I0>1)Gn~$DlvZVvM7)|e|%Yc*Jo21}Wq0KEI&W5>kG z4Ozf}ID@P|C`9ITo0evJ286HQbFMCg2plO7IYWG`pHTTlL>IYOK*CQ&dVs-1IM zh7l$LBR(oIbp8_8=S2kcI(bX=5d|ads_r3pzV`R<)3CgI#l-$O2FvE9jlS(s>*K7F z*}dJINTEeDvn8K0&LdB*0PIit-yqWN-ksddS`6Bf+#lQQBGNALillY&yq!s3sR93C zJ@fqQmC_ztFbns2pk~VTJt%E+x z;f7c1G_}(C=+s=)+7)aWEH|j!^5K*t%-zP_YJR1%!5sT_p?JFM7qh=7(4>HmjA@+B+pDqgrjm8`ey=4uD1A(LY%UdTF?LI#M&g$_LtG#)- zaaMFO(Uuou@}1W&8g&y{*!l|1^w6_}nmF?#K!ncakwwmk*H_-TH&ZMMl+9!hA2T{M z61s7G!$!ZchE8<;?8~NDd3~m_{yp!At(1qc&^4ayx&CYH&j{y;zfN0v20qvH4u@_H zL_sz#Tsr0b+^dPh_};&BXPkDWsIllhT|&^!BEWb&4Zs0H_(yzTRoZ00BcY`EKdfbK z?lKnZ(Dh%|JZ_i2jAE4Mm@6L(QYo6Ba^L{)9)3Qw8(*G4-dyFBo8888)H0l_*bIn2 zm#R!WH*GCYbOS+KMuRhpHQ(na+I&q*BB|K*we)>InA7T9T~)hEI;~ggVjuFM6=JvY z_SLUtx0dob_C3!44RCGuc~^h?Mus|YMx8Z(7)`&zRfKXhi*xOZLmtNleP@BR@vzrh zi3u0oNAV5QJvVi&?*UJ-`XR=lPm~O=N()C#_l_XcAU^>Xk7^*hB{h40P3ChKo<@y2 z3qH5%+*xGwYHFM+yb5l$KB<(=ZrL9GR^_=tb8vEv zF$ghTu$-GzS!J>1hy4aV^AuQ6T+r?`zn9M@KdGAXTUd)f(|vU~n_Y}F*DMWQ9Ils! zP>T2*N;A<*{25uGSshAHmwhbK^5SuMm7yJFi-o*~u!k(3rt$rVrLVx-L`*8^D(icC zmHY)xHKpU|%^AG^5+~~~f*@0xA~7SKHmV=M2aTtpS%xww)Nwmb%Ch#*%HGJ-0`IP_ zO*}fP{0UfONMEghopUXf0yIF;+qvY6J-=ZCYhFISSC}hEbLTVKRE3R zu(hu-1rNEq+IV5|69?1Ic^fTMQ5TcI23pS_VccmsWYR)@-}4QqS@HB$Ye+m!5Zg-X z+bKqdSd0P*B@vs{+TF7&AM7ju0bH@?&y+{MF)n|*p&Whb3pD98VdE_NJrnuB4VG7i z&i$751!WI3^?lf{5}f0IkWTxDbv<|IZgVf1V=6U#mN8#h7}c#N0D?e$ztX?T(5a;6 z#V;MuI=S;+3QPgzNBBC@e?q#L5h?cCiO?q)xYsKfxzQ{99YI;Ry{z6RnwsOf^BJ}M zvKqseNijMm^Sv*=JrW+l#jW-_-bJAP6r*IauD8}G62Mi>LH15zs{7;oc{tYjQa?xO zxsKG&&0#6}hBi|s*J26C>aPuQ3X_5Vm*PP6cqRv&l9c{7FX zLMD7bEbN2!)-BsgW|9;OCtr7plR`_U&u0@>^jVDF?qh!J^RGN~{slijUy3+qY|fPY zFQahnSmao~mmY_UgRB=)p?uR&jb&j{2e(4U7X>gq15>H5y3M&W?#ljOlN&{+!VD%o zOVY(oxHk@W*m~YoDrV5NkY)Y(#?sJ+p`@|(z2cMn)Qnb#uZ15}r=t#Y-mLj>#~yPi zvvEznJv81EyT7amGvfKC^+i`1_6JY@cU&(wUNV|RkX&6_kh*}6)m?K)#T$f6p zSmcr;Z$8;HSLS|+8ekwDli9{hnXq!F!EWNUK)qbQMItbK?qU(>fnK2QdY90p0E5V$er$0a12Q2^R4`2Wr&4ELT^(ubiUs2nGh#%t2ZI+)85Gd`uyhAWJg1L;M! zE;QOLm@Qc_Hak)Q-agTDyKdav1m&}2D(Jrl`Y{1H%aes|w&J`D>3yvC1|1nkB({%I zru=cbA#*r+M-2y%xy8j%r`l>R#meA3OMPn+Kjd?RhvzZwDs4s#WZ$~1$DjgSAVC+=mNc;G@t%omQY6q&A4Vo_HCl>3V$7d!<;`izQbll*W2dJjr!6B z8=IrEqMcwvrziH+#2i04&~|@R^EoV~Xtz^IWGeHon5Zd8rx)h^2zr4{;Z$~~6Z-&8 zSck!dXH@Be$ybt>y0_?Qthx*b2gwN&T6-<0l-2(3t4UP&+qB0AhsU$`di4DTkP+%y zUzmArmm}UIkxTUra%)v}B;+c)2~CWby6zmF^9!qFUq53KgPFekepAN{14uqd z3$uv6NLlrAkAY3qBL*%Z%4f0bV zTRFJ}>>pOVZy{ev#+anH;l<#!ZYb8*jc@CQ&knj#A_nwnCik6B5^G}%JTz0PuWxk7 zAA~ELRR0eEPe8E0rpnt^nCH>!Huj;YT+=W20BXo4#0fv;-9hfqQM!PAFAmPJ~= zl6*?P!sj0nt{8pANs}l0bhr+M5w#jA8y=gDYa{}DYODlc!F9LdzHxFJEUaT8zGIVH zQ&Ox`Bt(4*6jfuoTi@{ly2tlj2x=;dwxOrfB^qS?e=RuEMAMPmf&!XDgQ)edirZk9<5lrnD)6m5RS_4 zTIxAbiCfToG)Ss}=zYJ3^M8(%5|VAzl*)x6l?wZbQ0@FinUb_qE#(`PD80p0JCwFm zJ9`$O2j{9e4GP!7>a|(V5i=(-E?CCohy9kFc`18(R4G9uetLS+S~*f@t+z6(xjt{< zDCCbDvPDH~J0>(#%!9Rlw$bhnL-5tq?A;o|V&^d2gYjQ4ZJMX>)^T;^a(GtaF1WMy zr^r%3EjZcUsUVN?)!mD?FolN9`6sHXSq-&zo6X+Xpip4RaVQ64*c3?r01Z^UZF<++ zOTHj}3+Z$1G0Fb`^*;|ejY*T=Cd9WIT2)d&?xK3v@z%Bn`j7&-Hd@!vym0J)nug&{f{_~$u8rw+Ag@1^+V&?SLO9r^3+_5L!qebthlAer+2jUa^NHh9^26$AIaPP z2UJm-5LZCCWm;8$yEHCX`W$R`)d1JmVXG;dR;-=5*($E`Q|!x76;EmZ0B)%sXyves zhcQ_!UPzl`fUs($q=G*UT}EoqvUek7hN?}lNfhks+G$GaL}scK!l5*IQqq~Wr(8g( z3HoW)w63Jg-OWzpCD`>)ML{V$P>@Em?k1~lMMC$~Xvaj_(|?ADrBP~h0&#&a0Hlg5 z*GcHB+~8NbN;@n#36tb?~fS!X)cT3R+sR)*6AiW7HAOTWz5fYC#5YQEk z_S;F^il}ubwt+vSX}*OZm56QER1ne%)Or9bph-#%DPlE^#Zhvc4J7qP^J&%{5K2MF zR=`r)0++QEKqQ?21rZqsWM%{mk0yLEXtuj3u7l}Hi1>BXSGkSUqGS$nRzr_GNz1aQ zn0oM)Mq%1fppp;7@2Z=Oux^1}TVjijtDqcp;#L=nFw3_HXmR8KRJDLje=(|AvTi4; zyN?*q)nnPVV_4K~$k9;HXaJQ2q--?HCXwA#wlqc+Xy)!*v)C;YP`RzJpp|w2r1u|9 zhOKPv%$h3Oi?9OaE@$L7Bwr!U$1V9>=$OpD;V8PHRFDSjgeY?)ONprn&R;5(@Jv6zVO1!ayWOQ98@tYlw#@sP< z&8|cC^YU=H3tEK=W3(vP{0ZMqvObF!mALh;C^`j2b8F7UZg6Uxl84sREIIW zPRlYm)7`7mr@T_iH|SMFjZ2Z*Eldq;an!4=C-(k2c=o9=(1@AFV2Uv?C$;1)&^jw( zJ5yajjCiV8cU;f#TX+$ka~~k{Vl1~T&SlpKvD>80$nCNems(cArW4$x_X1XGD19{= zP*vVa2;zuPYES)CyY~UllMNPZGT7(0A@rFSCV)){uWChl{PgXZpsJ5(%Jo%C#(y6z zoV2^;%KrfGBGm;=w-mHW3f)j7?g6beYXl;dDh5*d%rt6x^S6$e*AQdE_Qpi#?jLvC zgKc#oMQ7j-$aT<=2DYI_vAlGSp$fTis^?wrYK0NyH1k8f0R=$yrqny>7S1kHZ=(%r zxtPvJ$r!>=xOZ1%u#J-2!Al*r=|%6Wc2ot9xN98cG+Ozl@}?V)da{?owo}O1owxc( zrk!ilcVofmmZN2u#P`Fku$Z#f>QDlQ%Cyt(*g}rmcd(~or6yDd(8GaCbqWa;_@;wQ zOyjVHF4%3561Y*CM1B@$lf{!?d7Ff=y7e`EM!LY=kMb_DVnKvc!6YMaKMf!^MC-~O zu7^a4>vNHi9*-l$%+gf|mH!#9e*M!^o-`s#qWIe00R_r2@OL zw;fc=B$PC=O#ugfpN^LsLYPL1E_s*oE8J^jMA6AkdudmqQa4i1$O@fIZ3t9X(`^V0 z#^)Mc!JBQk1llYp{k63f>Jn)1SYpA5FGr{=GD^0TT z$@kQc*;x{%>~Yow9+p9&-_U9l^HtOebWs2*qHKosGaIvs(OL;oe94h~}17?s*NRbMQ|K?GsRg8eV;HineZ7-)Vcg)tWbYP^6JX z`-87()Ek+M((E6kbyZh#kTwpcpYiv1{1-SSu1(5xnUdzaa+NLTq|CTJvg`7tNOe!V z`^#49an}~5tw*YfI#B{P?Fz;SE3Ioq(%&Dx*&OF@GHJy zZP~=|=4myaVW)^(&%9o)*$v+7lHaZmU9Wek4@$Y*o>5!Ur9QIa+*v!4qIKv?YY6S) z3}B!O%=hE1!^8LKX$YeKKNW?VX2o z!9_^i(T#}ypd+30ywl=mrxkael>R^T`P5NeVQrY>x%vRVOxUn-Ms0Sh9hNkEWtz(w zn3j7(0S&blEOyen5YNdEu|>J*j^s_>1s>DW=cRwIkP6bN@AeU%r6@HawV$R!Ry_z%I%W3Kj>*Pc#_oj=`StbG zV8RuRLfn-P*ZFBmG?A@6!iohCr1UzN$596gA^!k!NdRr}(~Xc-6PyagJVwO-0Ep3% zRLqM5{{RJPbP6x1QGq304#QDEO5O_J+*$RlenU#4TTq?U{!ygSNzrUOPw7oG)0z>0 zv-*E6AEptl{{W}|0L3)YG&&Lz4G(pH4I!Nn3u$7nlrukqH7+n-Qt)n=}J zzo$-$IJxjFN<0KqxD{Wkuzo)cSz7b9lc`Z1!=z|YE%ShzZ7K}JC@J2dBx_e`%AtQ! zt6hVdK7m(!(}yl>hfvIAq#mZ7KP_vqxhr`<=&L81<`Yp>Q*%xYxXFD=L?cG3q^GvM zM^bgOf-6}EWeL32>JzP1e95@R`vTi8D2>|Nq=hJ?)PcPQ+PAjFKzdP0cexUS=2ZUx zE#sNZsqi8cDE&&P`1L!DUd6@XaOj( zrntwSONs74>`D4k6Vq?QRMYTqdIe!?&YE?qknt;W*;{Rv&c=asrr_(S=PC_6+<(nszir7l~n?&E6|;5EUanOVlVINYM^_(C8(3wYy8A&WLCB8 zv(sJGF)k6*fhZNBh(OdPq%Bu3WxPX9TL7_ z%Q?nl4$o}M^BbmF8yaW!*xMH=(x)ch7ykf=MiN|KwBEz%O$Lpa*0-WenF=VHLi+vi+WAc-y97NnIn+FGKJ zK@~M04OZlAT_~XFq+_y1HwDtq61mRms7S~E0J(OheXXdCijmfbTt}5)-2?Xp=x#BR zZOkBdQx`rd)!LkKD*ux{WFFI6rF8guYQ{@( zK&u}h3-p>fYI+iiYXrCfo89 zPhY5iE~LiYDyn9aRG9mVP(Mv|D9F%L1~(pwJcXi#DJSEie6&e(D1q-J_K{DPkdgvY ze%U#=a^*J#=}bdpzdhEPb<{^>p3vi^w2*{U)45b1Jwkp?KT~IQac_%|vv5%r(X@c2 zr2R@Y6!;xjL~5ySBQ18REB^qq)2PvJmrX540d+s^<@SK`$L=|1&9goXn~vi+ZUZm& z7hIzM05R6m{nyw;kqwrjrEUd#Ye_3m0919>cH=VVJjT({X`!}cEDv{e^hx&Ty|s>F z{O9&r`;9<@KG$s^_rq$XT@d-7NgG?pK94$ytXIKssUJ8i73!`~mAjVi7rw`1t$ z9aqP<+_S(=N&A0!8<=pWYZc3_z9QE*0L#+aix;~C?;qO9ZD}2)U{NlPp+leqYj=aq zGFEDJS$wu4POKFQvGCg*rHW)Q?-s0fpoTbJtnVVK&jlM)NMno?-|U84@Kutx(_YD zg3)mp(CVYypBjmhU@6FC%`W72rKP0ANCYW>DN;|vT5Bt}M=r`}x&CUwuD1^d%%E&* zBD~=_9j76(Twb!>?2?(2ZMiU_mt#p`(Bn>_L!y$Tf;FNsGFI(y>ai}|#|HzbU8wV3 zHH*$a7w|v0CKnl&`GXX4J|X5tIT^4I?_;sqZ@+e=eu*dvO4acqy)~V(2HI~-{+`QI zBinZ#_VpcC4JWctRDyeyj$SL;KR4e^J^ds`o#C1rNBlyNf{g3yIr9{QLG+LI&@~tPOe-BM2 zXz1zziwQJ?=Y9xUq=~JsYr1j?H=y*>&k4BvQZ|@MS)hW= z1Quu@vq1%#2q0NHXM~ev+VoW%hTO>oM^L&@n>#l<=PhyBSX2zE+?&On zq7&Qhgbx0}>%O;G*uZ1sJ=Zk8l+}{>E?prKG3=4{72Jndc!D~8O}W$jHG_cqbJOCw zOn}R8UxMnUC39x=joL0us4w>{Ah<{Rf@pQWh4p1uEW5X)geBmWpn>{prh}@F(@*X^ zxtmT`XpvGHanzypd{VPQKgUXYj@l^C9%Xf|Euf+1Lan(T zbgw0qp=EvDqPyv3NC!j$9xElbYU`Z?V{%W!QA3jyQa%fGkB`Z6W=SQRG;n}Wx1C5* zy?;+#OcDTURdeBUssLVc$0+eWSB+1`<6jNJ~kQsA_^KNMTG_gXY6EU6^7QTQE5wUg9@w5GDKdKY3rpgO8MhQVGb z$yr_#EEc29_fW%X{VJ!_ll%^|7LD06n0u|>Q2N%alywRf%Pp$4-fj?;^r}0{08`Vu zR2Px$kgaAee^L-rqmO3xO)Z{_1J_e*9-w9;^3_;K4k@?VeLYY{`m$^2R!&B4X$ke@ zHJIdFUv9rLp66f|+$X6YPils&^ETVy+^hJ!-i_|l(5gd&U6p3Z?DInVQiqb45BsY< zr~LJgu#;q=bZpJIk^&Ut&-Q>#mmq9awSvp5{JWE@e7qmrs5r&PRw~?&)P8!S8kMdg z3$;EPYMQ1;R0`9zbhkE&Oj@qJeXV&ELdY^K3RJGvwcSuX12`X_R4gpV^K|hYOcTw$>g{sBedXR#t$~sp@ z6t}e-XpZ20G^EzlwuFacDFAwEOAnPpH4aU(C~5FIhV?ulN_%^0*G!EHlGCEG{9`;h z2uALzo9I8!T5CBf7Dp=2GMIHCRt4x#*)`uxKdK^wS}LKx>i+;>(m_TF1cFk2eRNd? zKEXv&xfTJ5dU;gY!`=LKO?1jRX>pGwsUHixyJE=gHx^V-sE;S*cq`5W2mL;z(NI+$nc~E}}i&O}T0TFAn6S`jN5J+;iyqDjr2{ z3wI=*cCsefnG1pw+Rf>Z?MFhi{U=T2W2A~t9=Q*So(lbar#U5f&nI6oH>CS0nLJM*tEl7E@)F}!nApQhvCIvKIoz!@!=v9xxpBFrN zY!V9FK}jD2tGGPTtZnNyOrZRl65?E+LWKg9Y5a9hbsY-RW1m(9E%A)6dfMz#t=T&d zk=2!n(Ntw3^HoZg#|(Lt8wFCMQbTCvP6b;;<;kK6)@Mg;cB$Xenvw>J2zJ*hFL#Pz zT;)elr)tk>_=?w5R}7<s=%;f~5M0FJ;e$mqdGl1!z}qPP&ei91(gShJre; zbowM8X!zUab zM4DNkg3SaLXd+(p`PWM5g7@jsXqL3nUC||ZzMF4UXe|V_@B4J+P)Fn2{6 z#~vX&HP$8MUD8j|OJcpd{B==tl<-!NVu|XnmKVa9ejLL}5n61v001Ocudb>kxqFq| zHgT}2P21I)!5zUyqO#DcURP=>sbJjOp4`xHfY1jtb;bfk(7hMK>J7S@ebWZ5$AilXEh zZSy2`Z{99W+#OYWmbF)}%TwZstqQU7xyMw4Y=$MxWtG&SN*>`K4M~NSa*J^`*40cl z`)rJ}A+qBT8}4N^NvA}hAE@oCnBoo~EbYYGBQ8Bu-OLw=c(t0{f!kIY_8Us%qJyFT z0L1FfM+S;4zBbp?khrJ^562(Ovb!ET822vgAqZFi)FQP}+vTdDYUQE6yYyitbgRgF z>v)C%WWz0r_FHk5qx&On)>};p{K(b7c8dvzh|4WXp}6gx+@B}2SlUmxtCWN`x>siF zRUhG~l2vOiPo~9i{EWUv#ITzV4{vw1OF(T%Ng|ClkblJJqhLC!Y-~mHRI1_Q?h>5u zlY*Sl)8_e;7H`bcYZgHMUmj__BRDK^JW=hmP%-*U@bPcaa{rXb@B2rP8z>*jpeV|&d=Uck={_<7RX5{Z2k*1Q2cbngUUWB zoSdGd`$s{i_59a5JcQ5W@((Cvccs7khLrlF!R;kUEw-<1G#@=ph)@MSID;dq>lclf z3wRe4<5uM-x^(8m%anmoOOKBJgnCw*xdY0%)=KYmijaN7?{kxr;CA%KbNh1Am>t~}%03zzK1Lssi}*f^j&*0bo8^(R*nP8hN_w$ey+>Z)qIjPdsC za9k*NmB1kOw<+dUhLv5nB_Itm{=yPzRc$^F^@5?Kg(6#^SZ`4qx?{IiE^!{+5*b)f zrG7-}SS@!&O(~Kws80J=y#@3js-d&FsXw0jP-Q}e^%d1~r^`$q{+;AOlUw7K?D<=1 zbSNbDrb-1I1pH3NSlqq69aYpg?bv)@1rt8@7c^wjR4 zM8I6-V!5Xvg<^wc<4Y<-5OK!BXZCbliUOq{$53q{E`H@I<@6Lx?ginD&&aM_CzwtFcDuXX`re%_qI=CrNs)`0MRROlx`Zebg*b1Vw@nLmz>zOoy z9ZC3)`X(2W{NVopJo0mwi+e`b$GEI?+4!3nNGN96Vv9t#^z;rr+wM4hNdwbbULnW7 z*V(iY=Q^L7%6o&#pO(KbuaUYmd=h^Fa1SSWG0XgS!Tdj9UGI^3F670l5sIWOQJYf1 zaru%q^Z{wNT^R}J<3QoS?r@NEzFMUvDPrUSwmXCPJa&m-n@6=cZ;|`jvR4v zelymV#J)h!5|oMACP^)^`^oZ3dv{vu5RpWoASECG2E6HS${a5YuNBp7vRr%BgVSlH zQ?>s97P$qPrcIxiXS43B`mLyXR=aPmn`7>6nU8DX1#)bkv)Lm3(A*Y}1v27z4=v)% z7FV2$(2y#bvYJN%sRx-;RtrgIyKTA6fhM^sl)F~8VZL`}-hu0yLkxF`< zH5;DIT2zT+YlR%tj^-Z<;Ds*DetsK+bONH9EATYcjlUt0hLSK4s;+q8c&tt!rtGY1 zl`Z{(*?dgoCZu;P3Z#A`uB{o8$G`P9iz{)6&P05VssP>jeU{2yfE-GH^(Yleej1X< zv2RU`mmrAkI;Bf(_di=f>5&47Kkx#6paG%&I)`zLu{r{+ZL(QfRpBhFExfQ&r)^3| zAFhZxhAGy%qa#%ExV7$K?*GI zSI0pLp*`>Sf5$~dB8zeQhvWX;N>%5oF{s5!T74+_S4P&=L|%dqPfZ(}L>8Xs`?_DI zg$nonFZ;c}?bD#7IjeUE&~z$+ZBb#qmAIt(OI0+L9n)O~yo9Gl91$1o%s85RVPu`b z)VObCY0#*yv0(|HbHiQxaH-@2&>S9aW%Xu1_idxMHms8OBYhAfki;Y?JeO5AKe z@Yc%1+*w*RSnP)dicWz;xfhLgyNgR{aT4KGccAsw=NImd*HD#`$op;_JfT(`^TKE| zT6M_L!kt(ke1^Kt8{K%#1B&LEuWs&}QovO>&Ac4DMQP=?0Ijrz1rU-+sre0byid4R zvYxAzW%%}1*wg|-sW;CRG8;^necNjNvQEV%G^gdQou*C+g+U61+@A8=M>Ob~Y;e5T zua*ooj>S}?wA6nMRb;K8pUY6XClBYBEg{YZiScK_Cfa?&CL+?8#T!ZAT&Fp}!;Xv5 zcu#g(0M&WOHrCn47>t&b9c?tnFKE?JoUQ_c&NAaBsHsA? zCWqAOxbW!*_Nzn8;duQ@EH-IZS41dHd++wRw4#QzLPxfG&;mQ#YrTblqN?p$0Bfh)=Lw%&wbWI$547SQ=*U8HKoR70io|8p67i?)83g(3xU}mw%x8!QsQCH zdLm>XwIwY&<6+j6K`3w_j^xxVMyl=#xv^iF5b zdzx4MTQO*DB1KT8El0T}by!JFdq&!+x3Ud&RFg5G+-eWxs2t{je5IDDyat{FB?}N=B{B)NRNg+b)LLBG@tnBwJuwWikCm$OJyMb8o#iM zY(lF$kbPvLYe;EL#}8C2o~ZR2p3~(}UeXGB@uqdhSiFny^KGD92;LX2apRn6`WreS0_%k1A9@}yi z9!eIJzLb3_brewm15f}Jj>pDdxD85_ZeAO->qQaXe5dA1=PO9&9FHuYe9vY`OZP0U zOY3%9@&c+HablZ*1qVO{2HJXjQzVRqCff9|%j&m=vb+QDo#A!AhcoCqmrD|^`$R|UU8We!zG(|sfl%~Gm6zm%fXkEkdHuARN z+796>1+K_)qN{+;STkELU%*mq;9aa-7j&;1n zhoY|8b~^30YwY`EHWg4Q)Duu^tp&8hZoO6x@-Iq4u0PxF%1d95T$^~tc`_0&+zHM0 z*Rd{i$6g3qF4-r7+GZsVeLmzxuiOOsSLLUtwQPc&!ivkc!tmkHom5fgCydvR6?wZR z;;D0$f3Y;wYo8xxNdZysSmntc{26Vynx$$j{Vq_{EFP5_iDPo-d0o^iOpIgoH(IK5 zDJzQjQ9Xp6OPFp7rZ?>>Rf+B9mJi#zz24fIGO zRxmgvfCJ2g)1ax-1SB`gQ+3pKElJV4s#*_8L1uyrG!R*!g3SaFZhB~nG+lqB>M=%( zYD!WG&rJjnN*_wJMYT&}CZ(;Xn02$-v=Do~6gAQ#acqGqR_%)+ucPZC))u;e8AU4wUin;a;vCG4q@sQLGnKjIA1B;(3BA#YRl10K9UxDhxzLp zm$odPzAIyaz4a~VdWB?sXummo#3kF5qU-i|yFy0iwPK@MtVE=*iVnSXJoH)9ayukhnf3vUC_i+{Be3*x7KTu+^^y%~^yvmVqHKlE&?Fqk z&j?9cAY#@DH?^fbsd)`A^-_S+o9ZEq1!l`9ylOxn601gGoAReLO^G$mJdrc@C;nB% zwc3`FuRvE$zPfwWJpfB=EMV2+?nmJ9UogvM2%Gynax%u@N~r^HMEo?i?xj->zF_ew zXa4|WVk>BJ{{Wk=YR~G7$xCS4(AK9>+)6;Y*F_5QIzQB#GZ}liDJ~M?*U;_` z)Y3>Q;=zH(RL90`;~;tI7bjIA#WM5W9?LnI%}l{i{BIZd%(XWSMa((!QlC(6WBX=zFUyrMQ~RJauXO zkNp1t6&Cp)WmYZ9SoU7SUds~RLS?u*cO^h>De4K;c3MA9;5*{4xZS{-&CwpTQ6Dl| zLl?GgO$xDgXgHO65<2RyF&?!Fs!912D_9pavPIe8<`_f0-AHkP^?#PN)6!ddu6btD zZG3zxsv!MCSpn5_N})9RVR)^^kny5+Cs3;!;%;>A2hI3x&)xQ8OngL`qIT_kgo@Ya zs|+R2r#KyosN{CurMW%}qCa&k7~JvhKIHhUf*?3z&32pn5}O4(k51q;{{SJk+a~W7 z1B%>MJ*$fCFKQiAu&rCslpX2u(yEzDaTa?>uE$SDQldz8NpB{PIr!=`$C*oSL|gIF z2UPo9r9L;g7?%XJRrI^J@zvHoanVubW74Y2CFJ@a)$6M|EW1e>Ebbp2Mo*#3r_fVH zL|ajJHmA=)KG$tfA1uKOEK3cLX+;$&?zIfZRS zsa0)#Kk|>`sM#pC$@5x{FE;CHcokas_T~MrEVf8i&A70YetL~=Kz^#nGY0rdv0g#h z#OzkdeQ7<;Tb2XM+P>Snf1a&!G45b;%kh``a+cSskGxpSUBY?G3c4Zh+qEJa-YKJ~mk~%Nl>|7}^!_@~+Gt2Cm%P?6 z?*;L>{p6)KsBJe35T4Za(K}?Q@~jOSt=~xLLt%7Tje#F>`V<&-`!F zH1&jrv_+X`mZ48YU5QkoT^eD}x z0EC}FDbw=PaBi-t$)3@oj&3qb&nzJQHQJh;TBr_os@L#tB+o2jH;W@KB^eem^+VWI zO{q0c<*^4;SM$0z{1wHOyI&dcTUxjSkS`P6i#9*@)=;k1oyjykMx7Z3?N#R+Cgo{( z{{Y3CR3OXKS^dPFyPj}VZP!p{$++}Z(BoR(s@UVaagXCoG3~}|DY^nE0HFA4pVf1^ zt08RGrJMG3jbd?DxpSDevcUS3RFS8lw0n_HaMtux$C3QVx5IJ!ejN#Sc}XQ7yix6B z5w&aUsgPpH>Z_*80I2SL%>0X(EyI7w+>Z&;do8ilTTLs__MKnBWY&#URM*dUMFu-L zB}(?%8=cmz)PFrp01k>N1ocG}#ln71C5aKy?Xml3)9ve;WWhp;Ur9noC<4sbouH<;33L@vO9_C!jGnad^*c!Xp18GgZJ5o{|LQ)A)>{v}I zetJYzEmm0DnAqPog+F+U4xhn1H=XhhS!`XmBusbIyJG3?Hri9#pVMDoo}#!Ag~Xz1 z!$zx|_^vg4=MGY{V-{e!*L>1=Oou`4_L^~h?>J299_yr0Rf z{1?G2;dU^XD{d1OyF*Dmv_cyQT8GPSx|}~!#^c+$^;~%2JTgxw^5!*;r2C95^DgGH z2F%AHX0`MtgHmDuxn1Qa*$V6*Fmq-p#1*( zU|6d>Q|?|>XTkY;$>ml0=WBZbRl3uUp$^HJB)0faQs@#0=zVFW!+O_>Q8{^nlFZaQ zs=4-Cb-Nw4H5wZY!-oaF)){RfDF6?E+fJ~Cs`kcSbt{xy&3L={56@6=8&mEt9@ zz1m4gPj~ef@CVRpvhH%{QEYM1HV8NO?62*cfH-#u#hbUkhd`9vGeu{?X>PNn{RZ@O;#r%JnnK*7c`x|+1w{l^EP|Cil6$ErN`08X2+LtxCk(#T8 zW=0puR`}OEHvGfVy8&|B-juos6+Ot<>c%^&to+t5X0%bqHt@B}IpfRKkj3|I*4EW3 z%&-YQ<72vm@fwYgiLYX?!rmC9Tv>KJh%)5Dl+u`!8EQl9s1T&BCWH`w4Pu_%uA=HJ zaC<=rdVKVCO4_sW&`32{4uVi9$mldmIa#2LDUXY6VfimzF9=$^({y68a}5*SCMwsrp{e?YVUSi zaK~k?){z?6kAqF^U(D~TO#oC;L`zduWP!G$%4($+D{aBHc=%=~edQ~vQi(p>jAM-} zWfhd3<%86GH4=F@H~^+W9UD{CR$JxrMe^-3;`+PYnBs{9llqNyYaCQ<6HnBxu=vXy z_;=x^b>)QT1;3p;lwVjhf*;*>F&KK|azRwz|`#EYK7MeMXyVt1Bh65Se5GgT-HB?Cxi|WKk2j#(`Zv zYq-tvw}S27FvYw6=b3qFaWa1^vQ)n0nN6X_%htSb$WoN0COT9dkUPGmlTr1Za;_nZ zc2@b>356KbMh}ON+wswN4u6)54PeY%2CkqlKNI~`B(*?6F1sP%+-A^RnEI#?%PoSn zDMa)Db=PBanHvk8&@Vp9{oM|$^b7w0x9jbyuNHWad1K9(!*=d?=Id)|!R)dTSy-ML z;rE$n_x_1f_IdgMKm)5n3jPbm_d>MqZ9rXX>!&>eri*}3zWa(-XdwJ_^Ih;z-6dD3TdUvQ01ZiPD3In< zAYD~|SPQ0|^-7Fd)E8?i*tQZ!u87F=2x{C$VIYMzD7}^VX>670A&jqNQC4`XBq0GO ztw;XzO@FX;Q_Qn!)B9V@B&nCE2dDo44^n=rsyvp+CZ1D8MQZl}=stQ#xS~mdsNe3; z5-8CMJyqH?`)&bEKHP+3+=_&hKH5lF>rE-QXT>GfW4K;^($cZqrW;j%l%yZ?(Tm9k zbRCO?_6a^tH;zv=To%DQ1p!a;)r47DTirvG0d9LH`}iH4t!rV|SW)ytgaPOTd4q5Kflo#(OCYic#GuA&K4-}%;_ao!}I!ZbreewRi1XU~4uB0C7k6jv0(1rI?qE1x^ zmjy=k`Dh@8_dZ$((4lqg+vBNheG_hNRsvE@J#@q0R6-@?H04nA!peN~Du<>PQ|F;n zUYgF`9Z>Yqg!cM=dN7meFFmw>r$ULUqJ^&NsXJ4s&|g8pHru8@-i{n6#`o}J3`9q4l9!6J-C`OOBF}tuNLRM8enb`)D`rhAXDOX(Rlv=H?@;PMS#rW zGIq^7Yog}&DtNb-T9mgNwmP==C`wXFh(8(+O?2F3wiEOtRm`uszEafdT@^pRVYw~~ zBub41$df3vd&*4}+M(K@0UracBs@COQC87ZKi?asovt)h!y<(#a#bnD0vu@7G#x#C zbpr2$0njZbJKokvJkC+2%`zWMpLF+yRH~Kq^wxIoffN0!Ut#W?QjHv#$+!2&U_P$M z%UVBjw1P-)ymx`{J+-ILSj@izmCQRr=vY5-;;7u@;^!N83{K>i&yMl0aX8jQSYidf z{YBiHxTT)iV3d_rvgYU{^!&BVZa9716%T6bxSn5YJ4V+itlA!8s#9gPx3bbjKB1~J zjCeGn-a~l6I@KtkLqjpv$SqA_K|x}>l$whEdX005e2v;dmur}8PZTSx-a2wa`9>0~ zb@_j)IBnz=8r33%9dV9FfHup?x~eIN{pqJN=s@f zQqZo7qN+zti5gSkac-SdNo1?4RFlrpynRO`Nq5GprT%Xi&4X@w8+j`so4i|^*#7{Y zj=sczw$SMQn#RN*fUcEj$pG`H*8#5Rp&8dplhMtsS6P4B5bwN;L6sx+iILmdUQBYo zX)Y15SWk5}uBFEX4O1?CMG-~=iCSDHy_7Ykl=y-TaiVoyZ!FxMjHV&C0Wq3GWwz@@ zl#kY`?n=FMhCmaJn3P#AxEl9~1uG>WdXK|K*t;Z>N-mWws09zB`A^}d%tbjH>Yb5h z+9Skg--@Ynma05}N=YkkLxD_+F36iA@1-ka(Tw9RqXMJ{VMQoWKMEf*d+ZI!&q*$m^W zn83&#iV^#yL-$9%Sq=i9a3;OI+gGlR_`Q*m%}Q#{HW>hSMb$1M`(S1sV$aWR-XC7z z-Eizw`?i=_J;A6~i7c%kw1*KDwWK($_n=O)*u2E^N?7qi7Lze!WoHv!O+%L}IV+0p z_)c$sz_NHMwPl!;=@#6I)UpEN%#876*2x_w+h~HaQwA<2q3(<;0r^xUN|s4T8PclS*+;({a>5{0P%sURMdCUuxQJl#|ashUoTthyFE zYBoRkYFep#XoZ5?i+#e{0-Qn=rjoDHrKD5?@~)#^ZuHLHs?QSE`on)k-(NHNm+h5e z@DGA`o5t@SEthX1^E(}~Zh6d4Ft+ZtDa?d^_urJh<-Yr_RVhIF0zLo$Xl+>e2q#J_ z%hrg&p{-ZXWzfAT^*l*R)JO^kT6O}n2CgaioXrz>=Es8EEDXq2H!@V)ef6=~E#KV_ zsi#y2)Rg*HQw*(RerXMjz^hsI_VT|i@Z-rYJLQ`Tjz@uPxiD|AyOa|m-s6EUHnHjo zNjk~$9B7f7o2t}eBpS8Luz&X#c!`U0ZaK#vbPpa{W=|J$G+1#tfe?_7X`92SAGMm{ zZh(7+thrJ8sa;g?+{A2@#__2so==F9NHo-s;J!s-mtak0(n?ms$8~G96;z#d*`Q>R z)2J(!#P>lcJ;7Hd>Z6)qR;=3IlI_;Wlt;XPH=x-oJ+<^2VkMZnw*u zXN?a+t8chlZZUsTA5ghpG8^!n{lZ6TbjlMb^$b?DLPpo1Yb} zqjvId!~CAmR~BC{k^L@hZu2VoOdG`%3*jhvampv=q1`EmAI5046}$ zziN+lgw8_%e@aLH030B1*uPL|q(h^T+={nFX+Gb4zY~l3+S6po9Z14CRfq9h;w!ZF zl@{tfmYiL7{+pz4@F!UR0C6#!x_hYz^id5MYBd+?;;j(SHvtx%^YN>fHDB4~Uuq!p4oK0Rx` ztwEy8x%5f1n)cmM=~KRz@|GP`n&0zju!0jo1)2yf&_QN`2sb@6PDX;#P(;sDr6q(D zo|*_C^bjbs{{YXmbWaC$RF*4;!FOpHFA?UrYfA69V=kiYW*^Lzw0r8G%xhDPmvmI{ zSWM&_C*9bEVmUjGZT#^ckMcJzXtT98*q0xccT=x6kd>q4cGZ>bgJq+N%UNXIZb%4R zUj)2y#lBUm3b^CO{zsd4K4r}HiZi6?ejXWT1dq#(yI1p$+B+` zAJrKy`%Qyd&?N_^rLsU&Z3kte7Cs1$us>ifPq)8uO4dmq5_;)ofTVh^PMdxI02hFO zF?5j@Jw5xcD5b)-R}J%N4$f_QZg6_y5X(=0Cj+>tg@ANE42IxrFAeyXq>ajwkJ`^ zqlfWcSo_6N;yG=Ac66jwXa4{V5rN%P*Lj%T(K;aUbWr@(=j2Vc^9~^9{VLz2--{@H8xUI*ekRj89QV}rgb(+M${IzJG9 zsQlBT%3OUJ1_v(T89nQa8lMqq1ez!Xe~3DNJ8mBi0RwT)l0mSmT`5e78E;ALO|8-Fw;eu%`&7AsP{bMCm7@_ zq7aVQ^N+gF0EE>?alW$IxHe5y(s;GkscZr26sf^C7Vh}1=O1(hsxNK%%R`-lUk$yulPYJzqU;ugZ)+n5iEC0c-V0yi%R zr~KQG`YWg7Mi+f!8;Y*St zkK9ah&eXV0k=(C(J3)UxhPFI`qFZ*nE_Z;G#c*1DSI#;VL86n|p|MHWf;ER}qUkGT zx*G*ltvAz&kjjs4o%LM)S32p;Ud#)w>r^U8w#g$j}k>z zNFM++&7J#P`XpXhJ)k?X=kvglt-AEo@|$6zCZqr>P0pJw3HFq`k4aQpgmu?oph@{@ z%STb^o5lw*T9`7F>^ST03MxqYzFO36@`A9_3OOiy)nSjCj#X8}mfc!T#V1rwZ5FmSRdl=Uxu1Vxy;?~E%A$~wzZZLc=tY8w!y)(`tNM+?gzRlT}|O%ZAC z7uctST!7yraP6hfeJfI%0+p%%0EP+H!aZn5b;KJPX67EnaL4Yu$G`NiJXCQdEU z{FJ51&_8hjsUZTW>VLylnE(jUQDNoU^$TM2KWBQ1v#{;}{$`r0wU&=sTlQ{^S?Ll` z%SnBEAwUqXO=v5k$lEc+e7TMRzAlhCx~+Wg&11H|9G@G0`yy{{V)n z*RIFcOy9Dis(&B2qqeH)sz9~N!_a6HifXmeP+bzA##Sd#q^2rTPpvg4XjKk=iJvQr zt!*Vs{kZKLlha3CQWOuFLATqcsuU*0TK#Ju<*22{brYf0Ons5_u7X!o7LJK)G)UIk zk_t-A1Q&%MnvtM_FF~PHt;Ktwl7LzTK|9w-DL|A_NlC8VbV^V_t*y4v5R4QDwe!(Y z9aeisox+q1v zI<3+WwS6i-fz&(LWs$WiMV#?9)C;k*Ml@M!?+>xYB>=Y8DMck)gI!iY*K{h%aL%eJ z%Q=@4#G6_sU0IH{!R=ux1xIT12UBCRbG22{-L=I;@wwr)K;VM-#~=r7QKXeX*-by)hQc$tj9!`M`&N^O*cP1C_cRt zk}$OqIwvLyRE6BK+;o1oloFKu)v(lvb#jL`fR)kA9@Ly`5P;Is8xffAf96Q;_-joG z09A8)xy+K$Q_euVJ&@${90%S?RoKquRT`_3LR_^rQS6STDcqp7W3gLp;C4=sJ?SeO z5Eam98mRT&`jmwh{gGSN6$1AzFKs(}Bsc)4$el>_2r9-nn0kc#$GM-EBh7_xx8;%@ zFF1rY)18i1s`-JY%!H)eSw%{z#m^{n{8sGcxmcis#$`u#!iD<(HluuroeJa>s9dOSV`C zw3h4KlDeMb`26(a`U))`9@ooKq%XI(AF1?a9EmbGD|Jb?S{+d6mj0&{E7NmN%T8Od zRB~&NFlg+jZz4Q>#CfjuZMolX)2(ytY%V-?M0K**N{CkIaVi9oHD&b8cU4_soR-*7 z4GJHFxSGYoHY@FCb33gbZE$AzL;_@3L3ihO$sYmb{M2`(_szVw}sH~roxdpc#iDlz# zc8o%>FovaskV@PM9mPO=^u>%Moy9wIL86U3Sl|qY%5HVRxK6<#%X3*{>vJf`QBK=F zKLtOzxzAeE6cg;Q2tiP%xTFKNtnOdwvreTod43{@xP@$d?B_cVFY{;iellbGPX+MZ zvL+S4*veJ^051m{vm@9JAqo0Ir8<>5r4Q6QYtvFJ;`E!ZQn}tL66cqbQcu|b0Ln`7 zg9Gs&G`ifao>=o1W5M=UTSxsdpJKLaef5ZvcMAUJ7F!RzS?EDwHS1Cax!)+d&&PD% zq+?$h>t5e2Lhrmw6@rq%-&FF+2TFfBf~Oug;@%?SjQ;?Y`S!)i5P9dv?;c%fajtHN z#tJhWrb7YSb04&Lp5I}`cRV_G1oqGZHHzgK%gmM3c(%7|->dup`+o(|IM*Jr!((L3 zO5or$jCh`($3;SJ*W1o%lV39onq!wc_DwPQ_jzs-wL2PfB+UZSnDO+JBHPN66XEtN#GUAw&0fyylRD9wA|aP259`4eQr&U2E;9;%w^L zu5*89?rgel0W;!R{PVNQ7iHw)eO2|_oc7evPf{oV>#0{)99H3*19qsmyid7Sa(a$< zaPU%u{%`V@G!`JUfV5|_kS9+{3X%xwQ@GYuoI?QD?%$zZ0_)sPYw3o1AQ9ak#XNxK zhLXz$(+{2?y zYPA+V>e*Z~anUQY%J@W=+luZ@5mMYE+ln1Qa2|kA9WyTry^Xtw(OFvik&K<;q2j6N z#mX#^+`|5p?^@Iv>op`2HPkIDVHNBz6%Ghw%PLXrbfgc3Gy@;tQ-*lI?hACvD*k=z z(@POQ!AJF2dJv3$9KAsX{R62Yb_QINb_X{b;&KN@M#IZ^!(wDyo_ z99F}o-nyCfB{pC|y>4Z}Lf9bw8j%&@8r4LWz&o;STro?0#3(8aN-?gCBZSzq%YP|c*{{U~c^MY8~E>m+~+m7hUrAf!?Lc54*Ac_xw z+g_F8e)L(~GzloYea(H4%UHO&8ju$@bKep(>`6|3>e98ex`Xr(KpvE)y3Rk6*xib> zg~(l(%VP~CX#rMC?;2li<;D{MO{I}X6WdS6pw*$qM=;Wg`xox>*nGt#;{0&L%(oPW zkzEEak50M3XCl6-vghor?#V(VJ_2_o4V4fzmE}A_SuEe_ioTjIvBCcUa8Sd28)YOx?lNRy)TmwXAP3Vl>B# zQKWXHf_ANGU0}KQw&m9Qz|=0S?ccf)zTie()mol+u_EsYGofyW5L7AaUH%&L({V;8 zZ9SKvE^;h;cU5ysD~T z1q8C;a1H)6>HIXEsyvcVqHeNX->TM5`b&n?_U$T!LK4zPYn508I(-J3Yify(TLJZh zLTl@(Owc8?qv(q?#&Tb>qZOrut?GR#Q56Ee9W+R)b%h9-s!UbK2_1_dsmg8$_*Y2u z^+e~@2NL4^H^c3vFF8Uew1p&sDbZVPrk@H9nAI!U1W7YvH9kp*!>%@&4}cV=n=1iI zN^7|ZsRPvQsF%%iSyqKQ(crjMPF(v3gmDb<`!|m~gLmYt0z616ixNY;J-FTStFUkT#Mf6M;NuLTrrdSs6zbL3 z*h*7XE6{3LjIdE8Jaz7-9vJd#ns^3%%I(8h{m%iq+?X+L6D`sUMfm6W76?!@^fajS z)_&(2*S~Pkt9b88Z5%2-Dj>|VoQE~ZZ#jkjYu5~d^zx)#ZcnL8dHD{jxo#~+wNj4c z>UpJlci1}Twc*IV8th?SgMADK2o@U6;lcDegzT~d(OcSG7W zGTPCx>IEvIPcxjO2HQuvfU($>niU^GsgeV>p+kWA=z1@wFSL)^gT_BL{C~{33pnH} zg}*$7HOo788+);Vv@Hqu8GTTg-*j!SD2pWPz{S=DH?ZHx?a` z(jg;d8kO_S=Wh=^<5!WKt-+G#$GFeqtGm|a&xo$}UttLkGVZ`oskNl3K8B-RhlJhB zacB$rimX>GY)qE1iU$ByC0o7JbM+PV-+i^PCW{(fRHE7$b~EZ+fXj@4TV&LiE3u*Z zX@Y8=bYrSdgCUhTQ6bXJe?rIlz8WW&{X}nJLPYM@Za(xu@Z!HNXUIX$MtjxKW=Mo4|RozIM1@?RM_F|z{hb_Io zQ-(pJ(%?M@uAgJw(DX`cdxE}?{>q+8;9fxSk0*Vvyq21qhZ*zC8Ikz@M70He%JAbQ zNolg;k7*?##_9aRp1S7Q+aqr`sLcgd&d%UV2t7#t9oNhknf^0oz9o4N%3L3tMRGmQ z0J}wi&c&1QX+ti&`EaiA_LTb14l$qnMe;UPocOSfv z?TaI9$DZ2vO;xf_<5Q|$ZH07<60`L+X}z}OY_*@!lk~Y+E=T1)P2=n?!^TnX55Iuh zB}kEJwWyV~?aK$bA6G=v&}%b|wu<}-ANtz0yoTcIA0duCV}C`@oRju98->P1Ct8rp zs_#C^(6psQ)TH(kxcFcg4#yJULUM37Ip&@<^3Eg6I2*wi{5DvV9y-=g z%*H9%CL7>vS&-3E6b*XTl><|4Ue-I!-BMoLK0#J>Q@@#b4;!4C!_E8<=#|0VL&E<6 zsPRYwidBl}>xDO4JEZ`Gsw|rJj^aQx*e6JW;jVdTI;Za=k&F&->ZkCA!&CR%#n{vO<$X zRHz?9HZ{^GV`PM}-3Ui*GoqM0bp>6YGQ5N1?;>z-9(hJ2C!zAquNr2-%LrR#EKh#e zdn3zfYW;obuH$NJC&n?dT5gQ$+SDz-Fw4h!86f>sp;QTsfrP4vhQF)VuC>JLMS**C zMkKmsq&yof)2KDmfuy8HlAQF?9G*ewg-}K`q)RjqS)hUq&rK7NptOV$Gt}u}EFhMk zXb0z@i3g(r8U=fjbV?D7Ywo04|@Fv~awZ{FVw$q18RpTF;Hcy<6to zXW!QyQ4!v>I`W5br$mEVoh_~;^npYPsFxZa#RH^m232_Ta*p0^IZ>c*%jK>qI zTD@`v4m#t!ODd0HYJ9A**)i&{6YRSjl0gJi>@@6?GBK!5Sz}*v<3rIx5`37>RsR6q z?`8^DTA?TekId=>mtkE;#+pA-QFY^#Gpp;0a(+c6&_7B8g#d3~<))*0#H)C4us5<~ z$+=IKr7ihRJ=}LIT8Tfz=>k_3VHK=x=mMFYk}jMlk4I9<*N$DKC%Dv*nF#j)ui|tc z`bAYU0T)gkil6*v$eo=A@nLlLA|OiQEbc&GQLD^!!Ofz{a+^)2p<1}{+iWQ%6jHaP zI{rGFD$B8ks*hf0qyGSEcRA9KDD%=GmW@eDl7T`g^XaM$#7MU+wFU(5y4pXz;Fp^O zv=HRTkF=7l$off1`5NiV$g0GQCBcenj>S1J+oCxSh4J^Ms6Q>3YPVhzcBAn@Wk7@L zQK=E1UfCx8Kc>iO(z<+)Psvk$KV@+aWrWL37Y0MfLuR$?RZ+Edn6qU!F!oz)sNCr0 z9l%f&_=XJm5mivA&MgQ30DVy!%}Bu4(Qa+z-xw-nz%C%NhDz$BpVM7j*~w6|IcPPi zvRF0H+wx01g09pl)S<)a+OMZpmMe*>aQ)=uA>MD9_j~@(?zJx7S zZsr+c+|o2n{$#X^W@jnp(?)JUI)m{)q|^9mIasLu6pS4E;irO&karc;YZb$gx^Xs& zu83Ga#A-9bl-*Z;e%%?H8MgvmFnOyIB;&5gh^9%Y{ky3pJ$?Y|Z;No6%(V7g*DBwq zzo_oGt|6cx5X3PGW+ z-8R;+^jVFxawIzGl2nj{j-rQxf!rM_hK8uz+KMYFhulo7OJPdgf!o)8Yao++YYCBq zYtxh7KNF^l%8C}j-M|X#q&cJ~+RzZa#yTfb|; zNyeIy=n3+vzQwqi7Yufquv;GvPzPmDd?^B3Om9d z(OF&E0~)Z|R8(K_w_Qf+TO@M&8iPgA znrbSwCu(TPMOCTMAl|F1YIrND)XFLWB4y@xWLj-99PRg8=N%Yid&?vT&onGnl(Gv4a(HhttE7VG8Ue`m^N}>gQ2AL14XrSq* zy;Sfh7k`&bHPuQ8vbL$zq_B&VQ(Y1@=7I<^Yobd5d7zDEf(vk;kFTbVi6zV4(V;|D zL8%oV9TgGP23uFdM2@S^ZC?!$LaRNcXl+8PUd8_ar$t0{T9m7)8VS%>hxHbXH3r=H5n^inWJ3d36wBq#`7?%^gd9 zJ9~|F3|Y0Vs$7{TQi%cEQ?d(_?vs}C$oijs#VGXn9Z76svWWSdN@ra1Xt3M;y}2Q7 zZ`}6XK3acuIwBDh)i9<`lR3iU?iCaKwKV=3CqAl6TSQbO)NQtqmWBx(6Z~{5q7Di9 zf#X8`bV4K#`DREieCfaYH4^4ctyWeL1F1^9qU<&8bmY?b+Jv9N6S(z0Rcw8 z+q*>;dg@Gur9f$_c;Gg}c+6}N6+)LJAxj6~kFV44)Qec^^<9*dG7?qnaAhOZ}e8%2Z0N4@n`Cl-P(7eOWQMx)i&lz-hl+J_FJt4ntMt)N5SZ!5c8 z-+jam+LW>fK#{P~1FCKtb3}mR>L^pW(C($j9TUq2hwQ&T{_;od?5!znpHFo{iR-69 z(K9@qlV)4Xko=iWDy}}IDQDtN=S+1^MA`*4_|eCY@?SGyb_F-?TicY}i=plAAGcva zY6tP@r8lB#>P@ZDclXF#&L0?YZPr9JCOJbM%s>^|Qh=V+fwnQo zAtk_{neJBF5ww7?cXgWLr@G|?PE+J}Ge7Q7a+kol%eb$_R|M1TM^+eB`N za90voL}INqT~fp@3AtP0JeNbjwmsJndy`NFcNNu;dc@5R*C?OLyOXhalOt&fU0-c2 zp-4$TQq%{h#=1*XB!MpKkr=a)N}_X=Z*?3AR^gZOD~1O+Y_^ik^<1rDmMbsR{Y^pMJuhXqP% zug6lG1wueHD)0C~kHN)qY}?69i3w{M;5$saoJU>#F5TT>OvY^$*r<0CO;_DG0YzhN z8F{MZhHI5wGmOI~%kOd*F7K1imvFc~vsD+Ryr1hJtfq^a}dZLaBn)DRG5_TrO@;eZKZ#3ZvoPdVdWw>H@56VyRXf z{aYQobftI`66l97Hz@YTRYLl^JN*)ls#oL&oM7Bj843M3ijH#2u({pVh{S%v){zhNzOt5OKGNgjZ8!i^fR5Fz zJ>5SVYO>~1=Z1xQXuud;RVd7I8CfnxaJou+z1i+9N^p~~so#E^YW^d%4HZ*sxRurJ z8Ep7t;O0xqE;Mbt_2!E%S-AdD1(|l9J&s52o^GeT3YxM{a000*2d1FKAFnlg1(eO) zI%n)10wds0+utzr$`3PKFy2_lxcc?PtU^_X0=y=ZvgvafTd9iyW=kQiz>qu290fS82abvk<$oXfkA}0R_`i|3#`}R~^512M z81>rOA}i9C`>ACLVM|YA$SP3!+ftG~I?>}fwVl$0nCdbC&NkUPwIh8jpZe zeb~%oz;Yf(&arGS541Ub$#Qwn?U>z-u%fw_39lwQEsoGl3Y0sK zM2eHJI^EXX?70J2Wt1_!O8rJWDDjgC@GHemIq~|!%TjN)$jP6{*Y?nqF6)zA z5+0i&boeaxAb%=S0NY+)&Sc$ra9nyWxoK)GVhsUUGjp~`SG+r#D-}{zU-s%fy}X2I zlKaM2RMWq54l{=qU%E{RX?36lDkImWb)&=Z>)X%J7C(@4{B|r>S0+k!|<$5F?%IZ>!@ zMPT3DXC8Gt{Zq=*%N%8tJP~Znp-7mqR+=v*Cu9Ec8mzhESsQxqjOezQuW|6%3VL%2 zr`jaH+Lsust*dt2K?*{LU5M2rTY;m67SfG)+|yC;MBK}hZg<<5%9#iM092&Y^V?IS zwggla0&C5NgdxUGB>-cOeH5>F=rlblCJoh5Gmyk_YHd-6xCY>-Kt_C1JDN_R7JlRI zwv?jYmt*?<1AoU)vCkdKV&UxRv=rUBS}a#))BD>iPBwS#qElbSnw@Wyo*|=(tGmQn zPx_b>KIMR8S65j{j%{qT3JWYAz*0pwrl#BSJBZC|Rh_!wmQ#Y-ZBZ4=$GbaHU729I ze$GlsQx2^vT9UK#uC>s6j9lHXYUlT!%SPEZ#PSoa)rw|t;4>BM(ND>a2&y(czMFMA zs+T`yBdh7rY3(?b^h^!)NkFlP=1G#{kLL1&2|I$5LsRL#qe-2!ri=(Tgyajq@|3k7 z9OQSVvS8(INQR$P?tUAJ>7$ylaG_#0aZHD3xl72Jg^kU*;f>xHd zRVncG)7CgrC!yP1?)MEntIK2*E2`r2w%3sfA~gkX zbIC2%D9W30kRnEG?&^&IsQKxPW-3dU?Qc}(S21s852-1j36l?)G0E2`u%Wu+=4$&H zke10oCvKqYQ+vWDhAq$0VeI|RwYr=#J!+*U-&>oKO2)CSVGE=QNkb)W({7&~MR@L> zX{bh!r+Y@#KOGI|$Uv#7KT$-LB%i>6U&Lv~&p?|oqDn>43MDQf?}b50{{RgWoYIj; zAyFzq5B$Ux1%Igqqw><@j3pUd5s@k#5*DR2rFtk4(|se*_@|k3RH&6D#Dax|Q`8Zp zIi*Yv4qhd~E82GT(h>+md&B)Y6-nBRhX~y6G^UD_D!2DX`c9n5khZDD)5Ly&Jl<*cOkTUacTN-$~P^BpdJ%16dkAe4M z^4RF&xfdq(D;s|sw=h(yS;+qYZ@eQQZS+92B08P!DpFDb-+g)yivHwfU;;wCXU{)p zZE-HQ(5`jzzuTK3vZc|FTf~O`ucNI-=i#qO@gIM2_d{#Q1g|7={{Y)=dm2la3xHh1 z<)0Qa3|;5M>kG_&-@2~*WgvVjL9V@Ln6tfYSnz7OR5{F!9$mK$b7|g%Q+vN3U0^uy z{kbJOlR|xe4LAOx7aq&7;Qij4U;+9hJ58p2w&@ySV4yOvN`N}1&2gCSAaej+D*^8` zwzn*kcS5>nWJ`IMAIDOdtfpEW#EycUN3Og<%eVurFAkbjeK_{p-ivI-fOhR@s3#`j zGT~ihQGyvNQC&yp>!?oRh0M?`dF9cE&=m<=usmNU$SpQJ<0e)UjXv#eSzqm+nfoN6 z3R)IejYYXt04P2+^wmY~0!E5aO|s-Hd%CE`!HZ${T(%vfE6xTHhdn9gXXk6S%DH~c zlqF@il?pPD>w#n>6r|KvzM9R=iI8;*Sj?+P8mQ`gSd83fvu&6C$`cN{)G5KWG9)&K z3Q*{6by5P-0=rJ)v^uOdj%lKv+orf8i-`1Kea7hG$LjcLi7jhR8)osnR zjy_dB37FogGbNR4i+ss#n7zg8rY{di3@E0g*ezsicd<@Qlht@ z)iE{2PKmI$MSD-uR5m;42*NVuy=m#DByx48h_aqi6hD-Y(gvC_Xn>rrS-U$K;O}Bw zT2IncJAoepJ{peuaYZIOvJzAC6#Bs;fUA8n$S9Qix`ghz)$`S5^d6fD3fkJ_NbOhGr-pxH z9t-6k7Hxd3&Uw_NUO6t*i?xR5Ws=$=S|B*zxZ8_csZv}}0;;aVrm?NCo>+dM$+Y$e z?m261?o0wb!F;9pAI5f$Hs!uTYp2_eqegIaFW+bE7c&n{C~R+@uQc*QT{t zo5x8AuT=H*t9R6CRa=C0DEn$jNKI%+r}^sDkD{+I(N8=#<+nR{#O_Ncg?R?ygUZUH zA|35<9w_(PXsDo(LOYODdrwiUwp)bT-%KPYX&q_ZXzckj;frh9AK_lgoM(AX{x+KWTD)wGX9Zp zEL8SXwjg@m35yo#D@|M#L9nXUuA+a^UkEo)wT1g+JTE}s0fwI0ZpVC#| zCB?Sl+so8yKce{*Yxg#rm%4`8ufd3|s4(#6K= zm%-S#5av8*0lQ3_wlNJb(|BRDC9veQB`F0!Ccd;jy1nB#6JX-v`n0TPF3v@eh(y@- zy}k;$@Fx^pt@-a9@}1+1;c|u{iQOYyu2@y!r8{N3w!t-vHM)Im5~`H^K(5-6m*sYi zf2P-s$`&7l-!d;$u+VC|IrmKSem|GqJ#i-sLGk+z;y73Y{{ZgRSMi9yvM?9y`wENL zl%+k21lFB@$4>EFWy!r0om;MpFU<4Se@W<4kEqwei{_sqv%d~Rh9opl^JnTo2VFW= zXt|BO?Nc>{E<5OT3hgK$omM%$m84SWuP=$1HV4TZj{&UnA33BbEUru}Fv(O_E;1OR z(%glh5?Lqpr}>7*ZDTow>9dGypHUdirXY0n4mg5Mg$z+mP_<$4R&i6%Nd}rz_)$X@{3% z&sm;V{4~0X1p(?0uD5n~(N5a1)pPsobk^>p#S<-Ux9zHj@YShM<4vdbT|OFNtpz+6 zp6AO=)di?h&rSaTEeezn+B8cv5J0c>9VN)tno0-= zz|}+2h(*dbn>*I1@RX_1-uD`LDLbSJ5mBcGnkFN2YMVTRYh`e0l{1g;k>oD5 z1-506Wn_ctQgr409S_Mngt+vt_V#z!erk`hrPTdZr_P$s#L`zolAY4rOHJB%DT8l% zQmIgu+gi!mzJpG((Z`~$lI|uQT%2$?_cgdSmt*4d9YN@Qr4FBtq%zILF#N0y{^dR5 zJWrj;;>^0sD<5_h>P^Zdiv(v%QSP#q!dyF6o7b+SHp#`Ns+#LDZY~<;n(43Nf0CED z3y8CvBL4s;<9vwIHmOW0wYC@Bzi^O2B7jpY{pDQtm8ydv-RT!)iz7r)`glAEQm6`(B36kP|&N$aZo zPn5ZzAL6fh6F0*g!>uTO@A#sF`2q+1Bh8J-N`1K5I{|9y0-R3D_(qvw!cW6ye%|1Old`=u#k1&A z@{y9-s}T<5V}>N4QB0^UKZhrzwEU|}q{;z3OLG`6|mu6VkRnQL@ zU>Y3~-zUS5li@aut{M-y7=q~a2w0)%UtK?b(D?T#*m;aIn98j~uCvu!1R_YOsr)pw zK>)PW^y#MFoP@5BeYCj`{+Z?bo*^OL*qLlq77|S;`vQk=nosy^MaJ$Z;?vz@xvi-! zY=g2b_cg(PuyZAwEn+{l6D@;BUL6$lWqSPRqpG~czo$7?+;;3q0_N%5>a;88F0}Y* zoLg7p#%YG$-#}v7DnO3QhhT|BJGbVoROIda;Hw)cbRl!IkZo*Qa{Js^q!_;X)f|qU zsx{kJHuc)7E(e`3SNgPx4sM_iL-}f!=9{!zTwyykO!)6EJcgfXR9i|4LFqybLDU*ht7AgiFQRS+ z%oybF9^=4}p!1Tg>WL);g%0q^L9VryFW(gxD#}}!Zfd@Lvs``bS1`$O=iBy`X_)&- zNBe_PmACY^U`i2jHYW6}}sF`EZu1Gc*b$or;0f%yhx7RNRK&b1IAT zWvoTYjllwlx;7VDQ|eB~N0o1w6z3hDv2E~HU+v$?OTl@r-4#yuGjUQ%QcVF+G(Vj+ zD-S5CYNF=0bq$bq(S0EKQ-sIl&L7CFzSxqR)xFNpKvwH$k*l^e7Lc)GLdUhid~Plj zi%k2SqEZSaT3>aC&__kG;cZp?#BvBa@sv=tezbjoH$}!DfcUVs_fXA0J@)kDv#2% zle$PghgLYr1KmQidDc0NBr8YC98rYBhO4E69glCK0+N=IK~A*W{Pk^nV%F*wcNdhA z`VyFs;WF)&5x@Z`NumJ$QYoW}ps@>ycF9#tGYfIGo;)9Jg$L` zpVWvH*CW?W6*h~_B_(x13kXSCj>#3T<*D_ep=ec~m9jO+k^45+^a_P0zby$UIwZEF zs2H8MI*8{K_!=UGRH@O;`cr@)l%jh2YCK^$1s1w-8uaOJKrb|e7TO}5MUIdX+tqpK zbtT@A)LLqr2{83KlBBOR5LkDw$3Xys(I*z+H2LU&DI~45q?MWoEj#{N6-wH? z?mrz8IxV}tS~{ZIu&SCWpc=F*r&FSFqEKqz-k|H#@X+27Y(`owI(textMJl;o82c+ zlt1a|4y3Jw6Y?WSWCW&us3eINxsgc@u%wSgP}5DiBGRO?R+K}q{4`B8Pi|Nes*SX* z$f^*)5PEdbaR`c3iE)<$cHsV%AMpM?ibd*iIxk6GKVPR`|pj^5yrMVXDE zuc==)w@5NAxg@(arR80WSC-T_+De@FsIJhYgzrP3@M}e)^%x`6uC#dRkKC|+B8YPY zDlNgsUX<#d?n!N>?vQ)FTDG@@7WG(Mi^JqQMv5`a{G`G~!P`?XS9(NZQs7fzO*m3% zP!u=msa~T&qG}9upl(xU3FQ9(5ngPpSu;2(ixxv+x1h>xNJn85ps4;DW$gtSw|jfl z3xDxT$BtNt?cbH%{ZMYl3rHvN8h!hkrEXy#HG2HUpBKlU9WWmqZ1OvDE<`E3=l#7)4S-uils>AU z0Qze;doeFpxXW?|tC7v5sCN@u?xXQ1QOQ=sP@@U`N&H5Xl6oQZ6jio~J5q#fDN8~V zxmg052}~7My9%_X&t#0r1KxCZzG=U5hx;`J?F8twc2R8!a^mu1#&N(tt?9q1)UlvX zwuyq|uk9H9*8ERw%aXrkXjOL!AeWaFv;K?S@(L`tf0Z~GC)7t?GJYWR! zE@?So#b`D(`UX2%)vIx2gcpGAkns~zVLIz#8qbGto@pWE@i4%>MD02$j8 zK-6nBtO&?0q-+x5L0{lTlaGtiUbrxwB>#PS?8u@H+xoEty;+dHV z-KeHEtlE+g3>1Rk|oD+{Tu+#MMpj_atH7nr_7WL!~y%Y2OGD~~i{ zJU<=b#4xfp;me(Gy-sCJG#H*)ZbOMkRX}lTBjc|(@?I-#HgX#~Un4rYm7?{(7UZWH z#OKIf9rE;LXP{k8rDFEr%4$C@08W>T=8AXE0fE)FDnbF!D4?3E-10u&)MuWclMI+Qx6 zTzA~QMnLpi_pP@L!w0|BcL3%<8Nc23`*&Y!7fAT+!6ZoTbgy+f57brHTv{itkv+3q zn~&lac?llN?lcJWSN=MnUE?IVwH*qQ%Tr_|1iF9$Nk1Q5XCb$WzFij9?u+Ng8^zAvW|d{maDI0EIKO-3&y>=^-fRPiT$c+ea`3QA-Qps>r-PW z7m^kFz&{U$l_k85YHlmI9_rSGI+pdm;W;WmOp9Oc{wt#Mf##}u)<6ng>#9K&Mbk-uY)sZgPv+huC* zNc=R$ODy}RR!vHC4}tn7?Tk5i;KB{;NW%-{KCSSY(B>_cKDKW$_lUixq7tjS*kauVb#BPBOcTwZ8SiYFQ2cD4qsZmLic5)hEx2~Vj# zb)06sR_V?}Ub#*}idkt3PSpCSbs{(-aMf2!b09udqgP9;dKnBPh5nL~4F!IBC)hCb zN{b@!e92jxpnTO$_9~l?O;<}~T51)nc?LU5qs=~*_SAP%^3-QCifGjt?osM00AE0$ zzdbZT)I}_7!i6iUYxseoQW3`*-3_G!=}&5o`)qVY#XP`;N)(jrNgkrQazY3QG@79s zAQ4ebA*`rR2Lw57!|VQ9b*!Qfbymx6Q-~p@_NaQ$9WovQVc`aa&SX`_M1krP*Z3Vh zR?#?&HtH8zeJVXtb~;0u;E9r8D=G0CX;ndLP?dZ~@YIJ&M^sK_P)cq>LW@$A z3Fs678g;U?^$0#EQ2?ovSV1mF_fzZGMSp?Q-%A^UgkJWP5-;!qgKw!(lzMxKDXx(& z4niu!RH2e!G$AXsA%}o0x|c!dQbGR!Ek<>Wtk8t}ZlgU46Kz+i8Z6L4h`^{Dnw<)i zbXvB0>3*UrAvCtSsSjmWx>84^ywF3^T9o|sbx}-cvY6jw?fB|InueA!LdaqgXenod=JyFoE zZ_VQ2Y`nJZ#U8{`RF~@CaX?Th1cObrx4Pi5O3lD5wYR=Ot)V)qnaCKN7*gJD3*uvl zaen%ep41vEZH};+pKWC#tqZhpzj`2;0QV?Tb&X89?xh`-BEOHzT+Zi%N(s?-3vP7` zplxy%MWs))(~bpqQ6i-JgXyYScx;69S8e7j0BV)8s}?D4vEu8;=gYWc6JxD;oOui- zrXoo92tpZ9{b}u?B>L*EUNrVp6`{7|Oqsn@RW%;UyLxwe9aKqlbxo+A$V%0n{qNRgZp3Z32Z!L`<*?` z!`<8P)OOvs)Y_+;TC%B!A;mPHm5$nthsFg%x@f^ z&B@%OjUbl=>koF`_+_@PXqJ&JL(oKOOV)%M4$L-{72YoU#i?63Jz8SAw@J z7-@{}>tzK-1UFEkXY@+MS{{V=p?m!(5 zn>Kr*}YXwG$}a>D|Izi-}q_wbWFEWq+UwHu`GVy z$1^bgO^?YJ9JbIWW;RIBWJ7lsxP^opYwLY#53B-I96$*p(@^bZBpRtV5dvrt>kkBS zc4Nm`mn-A=+n0P7jz4ZQ+v6^>|nmqmL2U`+qfiw>PDiA@xN*(+4*XrXA&Aw zDg9|s`p4s|ai~zh)mlC|;{HtK4hiLp&L74&mD3sGR~Wlzyqj{kwF@Y>LoD|uJ-w$? zKXB8%r@bI-dK&GhUX>)GwGam|5}{mP!IJWWSmpzT;Ntz1%bfT2$KXd&?$;%}kES4@ z{-953C`c9SqbF)ac45i6nqs%?7WE?=$*gkmjF!&gnY~!2FyvX0A8gT0ywjj53LgSA z;~X5-4rY38QjzA&>nP*AJBe~RXue>$5+)}Gw5^ugRkiDbh1Z=9wgQ$^qhG4JhU@w|pg<~eG>AS@rD7*l>@Xr%s9KViQaaeZPPFl0Z(GR*ye#~-~ zrPZXB5TirfJ$1t3;*K{Y$_;CwUHcPwBM~oUj4FCjeo8Xg0W7i2VF-OeA|W}g5z2Id#t8t8iW4;Rn0rA zU$dH|9-HoHIvQ#e?KM_0nuRua{hrUlv&?TIg8u+x)>$cWKz};~_N_mfN!DkT;)T_z zYSnQ(skOXom(;cuc7d7WmTZCn5@Fq#ZPpf$4$DY8{0_X6cMI*NEjlk>T1?xBLx#0f z2kwD^#czl08Dmipnbxa=4Y=}BkfPh?wA9n*uDk7iRquYnbX<$wEJIlke|5l=x)hGk zdY|&wdF;3ZeNtSX_^0yJSDjPnzL1RwT&Ne4T-N5Dz5$sn(+9|2vP;J$4)G_B_`91H0@8&Tu52@bsCc$ zj7{)St}-_+>PnOCTx%ZI&~DR@vlt{&sb#v*{#tn3vAQS($aSh*9CG`N_dN|Ug1=Fs zQ}gs1WW*GTzKE;VI~}9BQ)jZukSG>iY=4>3$zVj-p6RK;?oYge zY6rj_Ms(~7Ojc4kL-Tplj~jHhjDfY3oz$J`e=Ri0+N=&bPU#j;GvV@-12+XcujL4B z#a5qy(p@`hjyne?ZR3$GQk6!VlC+xb`)5yg{{S;xGUx6|WvOecRSv+FCfUpQreXU> zVpy-KdeL-!)Gf4?Jdbgbdw~9A(0)3L3~p@T_A5=jvRFB%sMq-)x4~)U9z0Eo%a=w) zC1Y~Y-m%m)@__yT(@^v9?IGR@PYRx)(H`ox@;VcF$1GU`Bp+l1I!F3k6$kt^O_px< z2L+_#PB6~q6%yMd7}D5uEA)_m$5c?YY%beYs}XL0-+hHITW7 z(~^s*0w?caBBG)d6PDPijS(7#csF4M}n((-1XZpP$i*VzT1iJ zKO#kSQxT(;?fg69Ax|9788hsbd$S|CEw6QamYrK&HnD=UJbC+Ob4rA;T#`;7m)PbR@RhhEF&Q_B1^{(th?0sY@zeHB^AMZNMdhkO&!Yw}OK=JYg=?lRv~^SXi8nHfem%Rc z*^t6=ze<*xiqLh^Y-HN>yv!S+E2tM$CijY^X;@lZU=;rV=P1_1w|sS6vef!>(N}DJ z@)Is9)m2AXNw28ssFtU)xW8tVRo9s ziXhYqe?FQdsxFgdS8;5$yx?DHAc0@QRwMwjs+(WuDs=Z=QSR2b??pRNU!JY)A_cWs{I%T(3Cnf2izI>)iZ@#Pep(VTfi-U^ zB?|?1`JguNL8$tH18?xtAuZJi^Vl_(YCFS2zo=N-L(&MFa^ck_FO8s)M4D6QzK_w- zI4(*i?;S?^LPt&d>A$8;ePg^)HSya`g0+*{I`5}M8YwKgC%TMsCZa@=s8Iku-p(v< zzM{{fwSbZm3vG^|YA8}Fhi#LlP^uXb*6P9$^;bi66Y$1dP_H%!)F!%nXh|+BJ9pe|pd(5LM5hJ!A-vS4TrIrHf|^+B zl><<9COs5ssNqE~*{+iE`;=7NT=(RXKC{$;sW!lpsBYQmjTZUj*41|qV}KdABp_4N z(xmOFa|F94`D&SA(^2&5I-9jEW6%xW?G7+WDGLg8{1Bh0>1-7#xlICB%P$SwF3+Wv zRy1GfMgZ;FjjOhdxD==(04CdE$q%%{C@#4nX+vlNq^U-OO}J{Ets1_Od}zdCaX$iM z>^|CCoBJh5xGaDVy6StW{yHZ+N-W*Ww>@!F%Iw}oRi9kh4j0;r;)8TG+kLdKXsxW+ z16M=-+Pq5(8M9n7i`=GT-tX!NW(t&_eW>XIgs0NG>6xod6kDh6Dq{23gt-ft?Ks%u zJMXh9+*TynU6QsV$w?Iil!LfGj+nBmN@hsgQC&h0#jgwa=Zf*W74tH(Cn4pzjrZi& zNe+JEJU3OpwG{*egs1&fsnv_-p!lX|218Db9TN-g1If0G8xVw z7!CSUuT4p}bCfA9nNR|xo+$8qOo?yj*;EkxbgYX*4gx_}s>nNj8kQJFsW(!AQX6H!+f^{T7a-yLq&IZL_mwpT8~^Lw)o-gB^; z^y{ErdnFe!!^{%4G0px|aYUfa?gH|=d}BoRtamt`y+`5dH0`~6sBg7l)uxN6ydB8t z;k1arS{c5Hs46dO5aUP#ap;PQ&sbm2AFq59OO6#qis48N)f)N zK_Tc}5^X)OYcaZB+Aboo;nBxWrridK&Y)85Vcg|tqo=VUAX9FG@Y4i%EhD4bMAOKf zc2lieqMFEgL9_w>r5acA)7?l>?xYQkM)}pzsTWo)5S86+u{4xE`Q%?YVBQTgif-K}FNqwd|R>X!&3{{WF=z-~n6Ud39u zTgypn!+5pxQ0o}AwM$jTb7RoDzVxMIVmo!7fB9~<_G*KWe&PjJJ#na48|CWVXSKv~ z<95ABW-X@L%6r|DCPLEFO(8o~N>YFW0UkU^^Nh0-Z~i%QVxoO`6WIUC2zsFPRS`x{{Yi*TA7+R)4uHf;Uoh}As(Ka zYf0^XE_p-mXC7$|pCyg`z~Tepz;40NT| zW3}v%KC~vLr}*ndZ|$A18kL-z-#hmYD|AQ^`R_22;){qx79ATnPz6q>vx8bRC=B<1 zCP~tjNs{?%FH-xB&^g_(Wgu3kQk%u>niMztla+h4trA-tgPywbl3aQ8sQo1fCt`Y6 zO@FAfZh$1g%rY-_q^I4xILM&d=e2US)>N=RX)R0ooA{pJ!&GzOB75pI0c@}MLF9aB(is%P8mCsn74=hf;cj5`UZ(fR^{zx6wJ(-BTV)S*3-TskIJL128f zNX>hV4UcUB-{Y-Sg~NNShF2K#&?sVRuQvIKF|B2W&}g+RSGv0sU1_3@7mZ55+?%M6 zmAWYUPHm4uxyXr>M4Mx{r~d$r?u>U9*WJ+VtEO?up4!4eS=l*+5j$qY_iDurM^JR^Hm$OlK2|1fWqwKm%W22SDB@g$mhqk~ZnI=~ zB%72=a>Gnqt>4#ZHRwo1Pfb9-!`j|RWMhL?gA1R?W9{1vtlkH3t%g5&x8yeFE>`a4 zu;>Pg1qw+bq$n$JM!D`z(8B5xy4E`Jv#>Y>ybzP$+>uX~s+#(hVoE5u4W+@k?L9`O zHfktdlF>}Mg$1Yjh#gV>oirR$hzNGV!TJ%uu9|Zyjg6vLK_n!&Y5?46r!>_~+}4d& zARsHWkwt!;^jivJ)oCcpabta5aX(MeZNz-m-E zC&k*SnQ*L-^;$8D29%X9;QPU|Qj$QgJUI(tNJs6mO(mgG3Adcqn_K<)&rk3ck*+wL@~ zhHiFiM9OoPu;@~nhjokMtw)?9}cahuvI=Fupna!&RDxKWg-H+PfK~gjc z&~9xd_!1kI!umRe1rPAndV6UiJ6ttd%Uh`BX7-&Dr|r^`R81?;d~})=^JsOd6>PcK zvckZ0f5ND!uU!z3f}EUHx+Hkh@5pgYPqm>a8^G6X?3neLX3pxwT1i zYJl%g$4*oxR(q*GO@B}ObYaL*Yaxiwj@Md4fYg0JAM?}k+`XL&W@|PcsXXJ5)US3r z*A3zNOKT|u^69H6vhS|Cs`p~sT@a{)e2udclOezKI-UOjA~bKfw}0vg*=s9$6r#k5 zZTAUrjZxKTp&d0}abpYr070UW9IpnB2}!xJoTy8OyWm9+`0BDuY>fD+jks+VYBtiB zR4q~P{{RgY3OcRy5L;>VOEhT@Wmae;dP_7A^pJkg>%RIGN9hC_nfPz=)VQWq_co{n z!D-*tfORS>*EgY6(&D3cQB;X=#-LmWYtay~N^UiIVaO(k)D@k{aVu%X3qp&o+%=2b zoGvcfkl-~+!Uo6j*83msHOmIl<3-FbeVnj!_l5veN1yN&y7zC95XK5aiJ{t}l&8a6 zE;H^Et)|=@vYgA?7TrO!YUS@Xcqq9hQ}&nv@~}^|TiTSU71ybFXT5N^hRQ)+A@>XR z{d)m(;HX?*=Fb!ITxM*k3$nbF)j?z;sh~cFpFMh43!SpaL<=Ztysv-AGT1rxN(cc_ zJO2O{%C$7K%#5J+05#}#)Qheo1llp&dKZQJxXD-! z?y4!eV?jN3Q$x1n&nw8An<=m@3^jT$FLCUro)z35|xD@_9vq2)|$%AfGyK% z*;;Zq-a4eiYwoH=Qm>r`sIG7)!COV09w>sb*PJEQ4)($QLuz_zmg^2$72TF(IIgX6 z7bo}ykA1Is&xkX5H>;PJ_w0q+&mY=di#f|=Y3=qyg(5r1AGd9k$4ULbS?*D&^&01Q z74M^>ZV6f?wcE2#e`-pkzYPV+ zEFmRI4THM2^d7ot@zprVl6nD4ibB4UosZ+F)KXaLr#3o?GAYG3l%k{B z00VMQpw&iMyJIC?*j3U1zRZ5r*%#Z(jJ!C?EIvTntk84Y$e!mXvyfZ+=PiXHNMX3b zLw(5}*aacI`;AWe;uwqQ9~_OIu8^HR3s1@Ha*^LK!~Xz=fqZiF^xR%&BWCNvkmER^ zyIf8x%c72=+55*oYL(WfxHa2a>#gyKrDdZ-`l>!e_8GKQj!;=wcXrj*vKG`V+sZk8 zV#yLUwRXAdoa3yacSShYv>A}2Legtq;66UObJP{0XvRQDktaI>VkHE3t*Z8*^r;lm z_Jl^EDBH7)w$l?Jwpy?9?RMO)q{qnMN093*Phku965NK{P)mtQw*f%V>62?7?bOu< z%=%KQ)OeA>94pRwE<1#eBKJFKW4m2UBMQ}Z(UGdb4y6^mj;@KRkzaCb>i2cZ^9+VoO_JYUT$|28Irk+@nIXgVIP z{07=n0H&Q3d1_rl)lOb4<~%dTe9eJp9z<~y{PDyX%$FqQ+&vmY5gfm;Be;g6d$Js9 zzR>EBwIyo_SV>7VI*E51oJ9_b&dOmM4Mx3?eoynC8ghq`+4m^$XAeg4--|Li@!Z3> zo(h^uY>^v^+Z~B96sE$|v?;}VYAFgkA6-bA#gj#~$(XNwDS-ud_sOQ?{nWnG_;-;p zTUF!9F9Q(Ohm%&)OR}zbv&|$TLV`h3$U;)H^9NWQMDDP^EO$ax)_)$$i+Z=dzi+pS z`8i>gS}zW>)d(VvZ~9fPxc3Pdxg=1&7xkqT(_-YFRpTo{Bsw3+a1r}uAN^OMAJIzg zdr79brNyt%ULX4&yPhUr_W1DLvd5tDO`EKCJI`*5{{S1@BrQeDPaxSMcSbSFkJ0<)TK3!n zuW!XiS~wnJln}F55sQcmUDw5O&yp;YtQV`Lp6vy8`^C0Qn3s4i3OOEJN7WtXln<&F zf)2D-4XVw=&80H&Tfg?J^f#O|Z7TuKKtVLr0?;(;RWZ+VXyMs(&x^j{`KOK!N5d}o zV>zxb83=mH;C!~T+=`8q*rnm^s4E+Yux$mq75RTE49tJe;?K=ZVdzePks)p|E}@eP_7d5*%JYL%O7`S0&0zmBsqoXaeAD zxWyq)wvXup+EhL|?5<^345bP|bM>oHoTVW2lon{h2+o#?Xy;GQK%jxr5v^DH6`&AB zG^DbC&_M*}rh*Gi1QAsYkH<)QSb0|0#GHW#AiiMJoFB5u83g{LV zm5Jqcr$?)khW`L(lZiEL%VP(ThRTs{mR1VAzSjwEwD_cI(6?2Pbnb6X>EUX;aO?se zTxiL0qyGR64F?pj;x#T480xF%6T6~YJkNky(x&muCSB3wYzuKx(n+tORW$zqsLsi= zZ9O$bC3ykJYG|Ut#mR_2k=ZXKQ|3ZJ{u(#PqKi)wn$meopRYaCn+F*|=uqE!hvrrO z%_noHpJkk2R;ht@bCGVn>&ZEWN)n<_k!ZP!qLN^3(f36;K7si>OMgc?XitTQAoeRl5yh0k=H1T}$nN zu(s4tc12gdjlUCf;twv+imOq_dDA(zIL*h|ZHXginN28_etOYD_PL;i zhsjyV41{;~;)eLwTI&h&&4 zx>VVyAK*r^mQ<1w)6_1DdOaJ^@zCm_R1$?0kwTNW_)}TO3OQ~8t5p4o$oK7SZ(1Y* zEAZ9q0<2hj))vgH4-U>uOO4XndsI56AzUZsq zkvU$@Z25fTrM|{x_PW{l_X;2K)I6j+Wj+dS7Jcvq8UsRgTCAKF>gYW*w~bLW5K(oK zliZU9A#y-58^e0r3&gzaB!*_r+Y@mwA1Y=8$z&_ zj~{bvi-WHm>uFn4G0O~*E`Dm(@wE|5&G!jq*)SVD1TT0KveJmlhWkMU<0qq zTMT5on7Icfv-GAk=&5(zLk3N!ld;UgJbuL88gYqgNZ6E~^wj?V9ad(dp#^9-Mc&}% zE@oKgDjmiv={>&Ml1ceinyZFQu3HPpw?;yJ=k2CLOw2_O!1|DNHuO|gQDNLe$Wf#T z3XLI$kZ7jV2>gz!t3{@jo7h6>_ZME#dc$L(Y6~fB1No+!+S*H4sJR|Vo}JBTqRCAE z0BFl*LP}KVTHD175BD$OWkyS@r!D~#bJCah0t%KYY2c7Ag%`_OE`J>7=v@z!ad;A0eufL>I?Q zHPuYhRiQuvqppl82tK-2;)sRrl8t793&Ja)h=dVGN)0sHr69F5^hT20aH&Leq0~yJ zKw6#m)PxtB2rSS+W`YYe5Luvt%>)*No`*!10@pzr%>))`AcHFEG)cgWMqs?^5QNmI zfCu7r9QY)YK~--sYicbv$nC4?T9OKn;w!BcoMEjC3z3ky=;lFciE?a%vX4SOdQ{Ph zChX_hXjDBm&2qI#TdV~wuusozSD5G)k~x}mOBUR$vDnm<6m}XbYH5-al0?(dKVsft zO;7aP0Y;j8byE*!h~K!7C_b;ApNs%h64TK~dCpFYbS1`NL_Y4{0yfeno{C+au_ae@ z_sDf_toJEol`G?_cbb)@YiJShp52FCM<^ju(%f2FY*lJIetHCiwli^3-VuelK3fP< zc2x_iKAVr0qqW9IQCb#=+fuC(aC~j(?J+jZ9#3ck-7Tc6;neS~BoKhrmb;j_=_&pi z=}((4wBdhLB=R7kpAIpBnK=0ID6zL{%Pukf0CdH_|;eq={UcvpiV1(Ek8x zy`lQ_Lx>>%01$N`TQ-Taa`cMti&@iZNlMu*SL$-7rkpSU5>3;GRPe;OXa1j%nJ(TC z?398OkOcwxRO(z1hN@fZ$2@dPlDHBT&DSOW0C{jbAzjW85~|lvJ|WdXk0C2NB-i|A zob^S%{1sETdwmDwMvKrMDbbvG>JrV9#@znV8tmn{3|y`f8ZE+Ka*&q$x~QG$raclW zq#USm=$H=pz0F8k)QbWpED}_tNI?Yp)4U|q<}b&u|n;B=4pDF&$J{vTQB#uC4h4$gwbvFjEoa1`3h=HtRrAl+XdF?r5zP z8%T?dO5+z4Vsm+?n4rUXwLJ~l4okT&s7nnix`h1p8ZdQNtdDC@e5aY`{C&>X*;Z%n z81gO8qCK&O)cZ`98bAr6QbJUd@zk(t?}CihG_cpI>lYDoXD(%yEIo_$qC3&2B?evL zRSOOF{;GN%GWh6ftDP^IUpo`QsLPinR6__VQt3^SnvJnDs>@^Dmifh zGCbPlgmEvANW*N-e$y)DB49@-S8Cd}p*51=?I4Pzc7yRFS=_zN1s2BvYtYk0!Pu<< zM2(Uu+BKgGT{DuUC`OmJMIF5njT(*@x+Jx@0D-Ztf>N=Zdu9EFnYm6UwnYB`teR4Y z9W@l1m7%eZ%A|)E>_>IMw%d&$164Whs$qWl5tnqoIJ$prWoU>!OYV-;{{Rh5w~Ugr z*;#9%gjV9#ryW`DTZ$z{y-79IhV@eD=n5a_7#upymJMZKS3leX-HI;56r7=H)O-rId((oMFn6OMfa1l z+%t~xhSugsoZao#TNp!r;)I)&q(u&Piqtd~RUSs5WVLs=bHMSkU(ZZPLN z+c*!7UP$7<740zaoJp3?^jw(|J8QYVkdjmNKlBsvLDURIsAD%CrBOaW_wH0@8*kXS zMwbrgt98Z`jk|4-+Sb#^c?#}I+gb=xRFWzPAdOkv(ahCa*|qL<8{J>d2(y0LR$0xw zn<@K@JOU$^DOV{=ThGm#+MvoDaq6kXDavk1HP-~?^8Iz>c2WNTan_60KHb9nE+R~X z=giUNs-jb`mn_>dyyX{D?_6(k-jgXrl~r#zSK0~Qi39qJ`4zt|%uKjDRU6sBbF-Fp>9g@A} zGn&XBPrV$p$K+?+OpB8<1au0lIJXY}01foz)zye~8$`T*J1e%b2YzXGR4e3_B2u6{ zDg|5mzuBnGniJlESW9DbTJ0%LJdB;FX^S2qLn|E!{{U&z5_Rjo@75Rm$jN_tcis*CH}7)4xG ziwT9!UPv1gX;o-&ZWYUr(qG7OZ`vhlulD%J4j$9-Qns(*u3ebt5jLVGuSW47XYVK- zbpHUkPVjiw7KlfEwR2t+qtC3Ybl-2Duu^h+$t$fFUAEvj_>W4_K5h1Pp$3_Q35bm+ zV3yhd3Vs@=GtD8ZiCSAr2q6?>LU6YdcE|TYAbymD4gUZgwCAn2x|rAxfI})Y;Ls^w zdEdBp(^8|j6zYVqd=W&vsL3jnmr>WbK{ZJE>3c)RRaW+v8qo_D=?UVl+k#vPuX0=- z*#vxRO*Hrj6beIc*gBZ zQ>S9Rm7}UT@zB4rVB|bcXSx!nV5WL0^r2vPC-6GDu;kYe5$4fkvivUA?W8%ORg9;` zc;DRIxnt}*Yq;d<~sH| zq5lBJi$}jvKxtYXU9$5Tdf)q8vmZyuadP>p_60rhS0Y~W2WB@u#NE0o6;Iwal8h+QcL2{qPKB0EZFj+&n%FS6q^$>Q;x=JJebajYY0!g=w(&(2?zBb z!&9Aul(TyUKvb>daEFFH)F;J0n|e#>2~%k*r`1isuj8s2w=+iyEnG7;K~QS7?EX&e z$=rJBy`#lEXuX$afjN~kHY;+PT`Jp+pe zs7Qku+R+Vwl!AR|J$KPMg_r?uy&IQ z#Auy|m|t4ZOx?jq_>OOE%zttHneTPjLeh29jCHvKtyb#+pNjc}x3`Lg zbH6EY92YT}gxuMd_2$@6E@*Bt*4sf~5K4yH)?)p>lH%A)xCMpEd#Mez*@s~yd=%%7 z-mUpvirtV}952ARHXMqw3KRkKP&6QoV{dGYyrqQJi=tz4ExLqA1uB%)GE8pP&8^q_ zjRS}&G#?XPBnau%8Ij=hSvF!Idun-?TtlCyu?2KLI&{#6%sBp}xiVWD(3I4bmcor8 zwqJuOpydJ`_K8pDPMWiB0KZW1sh`OM51EA(Um#+wOAw#979LeQLfcY7ufUO~hZDFW zpn{_R0PdEd)HMlu1@_HwS#A{e-E*lxKUeTNthvKn$J8wiu6EX8<#JLgul}d_YPi5h zRXj=@w`>BSN~``FP-PQteyHLcSf~f-G^UKDqX}1acIl|qD8lrz%C}MlPhEze7f%5; zVg#Uz4I}HSfb^#0MsC{Ss72`&vJ{b8sQjul*QT8Xc}IWuQ$y24hHce@^@@S%sE!0B zglrz}qo$P9xY`2uN=^`i($fnx5Luvt^Fa{^FxUIv;i)moqOvps(yhOj{B-!0Gw1|e zQPQKn{{Rq0MwBjG4vHMsO+8Xn`wS_-hgnIlQbxyZUf5=&k6NtePmV)4rqu*zzB%Ce zv^Qg1?M=8`1Q!B+pgvma9An-4>jE~o=(ry_{kh_Dtv;kFyo-t#fF}#X|7!KK+Wy#~CahT6`BPd9T2pWx=CJj}FqgmYC&AQ>-M^ ziq@oWU3zPb`_+cZv@}0yUQOga$nsu2DSYu60PX_jesRRPMWRHQ@?gCZTSlsFZK))u z@KD!8HZWYba|kJ(KkfsqTpH@I_#Fzbx4c5=&|iF7xmr*TzNV&{&foE7JXVXnai4bG zANOdypTb$ir;Kbo!#WI9;aN@YRff(}Ete;mu-PxTry`2D+f)Pvt73J{F0hfwbYczZ z@Lk6n?%PB`#lv5UiRM@o+nwqiigWcO4u|2YY_AC0&@PXL_hJ`;=17vQZwCTUN)X>v zQ*Ys}bCuzeHl~ZI;l0C#Mv4_5UM&*R>Xgz|*sX}%ZgtJ_TxHBUh10QIk&-f?qzRVU zabcHb#Zn`_3R359(xXp}HI=!?mYr6r+a%ZYDW`35sP4FzP*mU*02*{6qO>4XY!0a| zElZj}SJwAn5K6XQVc+~Q%dzB7}^l`>h%8d`*>n0iz46sA;~ zZWr24=>t$;vMm%U-|B<)RnHV&OK5%B7Bctm-Z`$QQm#kjMg#)>JZ^j zQiW^v4~DH^h$wCcx|2AMl$FFeRE(XxLd%~wD&0QZ<~@Ij`^W(^+4g<(w^2u$>e{^y zqTSnqYL>~I(M1WmlBCyiQ`V<*r@%-qs!JV4l#-4|Qk1l(zqYikdTEJT65}UuQmx|G zmT}dtOAvCXZZVnL+ZQISbJHNM8~*@Vk0-DW)l_|GJv9zCl2we7gwf)>s~^Gn8wbqt zoEi@$SaD0FS@+&IyLW$Szr~Ec=O9e%N{_Pe`c*)G0yZegL|)niF_XGCmjv$Stn(h_ zPF>{=Ou)F-*TY!0X*J3Crx4myKeFSk2(2*OD5?#$R=e9PB&2SmwJE3LgvSLw2%wlt za0|RutMgkI@KXij&lN5{9cPyu*E`EGCBNll8-%&ik!i}1h@Fi#+|UypN+s&F$oBg{ zI~rD-5j3u7+!f|s@r^}36!|KVi`lr2=fYgklx6of8|1us+dDi}U}@JIGS?~MCBCGm zWu>Hb3hC5p)76vJr)8RZS+c#?Og_e5VB7d7%Ih~6@-@LOaBq?9IfZhSx8oxiqQCEIie6*xE98Dv#<4CGv|4G4{S zgt9-FI+D6muXe2rds-E2mjXjxi;oVpwj`GUp$BDk>!?nxRGQVaQtnd}z;3WR_Ur zm)mVCs(aRe+JGCK&barumaewFhV7$3XjgOg>UQ1O&1n9#sQ&ma-lC`)VL0mbvcU#Wp9>j$V+}Uy*x0p~hXq5E$ zX>S6kFw`OmT8e-@56em{6iY*DG7B_yL1uyrG!Q_zvr?Z;IMNY>Sww?CX{V_~I-s`F zJur@_0zs+x=}m1CNkEl=Y4Fr?+M*69I@d;=1v&WpAbS_hEH|dr*r<|QDphH*P)XFQ z7wwRIR6O0ct8RV^ueKNwCgph@ydf@IbL&J~y}dwyg8XsV?`=PurnYgC7b=j@>D5Md z{z}1>$T7Im#fQndh=lKdPPbXDsT<<3mxsgA+6%8O<_ z!8J-Mexa}>dVw9=r8)%mLmtRVm?dyGDP0lo-g8L>m7yy~vQUJnC*eV>M%tHZO%*Yn zt>`M6ta!}y*=310d9z(np;De$?yp~6HehNokRzZ2?oxHbz|4RChrC}VJm|SK71v<3 z-m*f&Wl03}JB=|lklRqKtaCFp<6Rdg@u$~e%&iEyBe}bD=94Xj14=gV3j`Yc4ya%! z)uCMy_BP3ETswvIFU1zDcH?NbzXB6XCRz~VNat zaY^HDaDL|!H?wNaSQDWsDeX_Zf*W*n8}vHFX6-#WsuySBcmB1m)77ab#{89HOV-C1 zR^Am$b?Bfv@2d=D#)_Ym*-_P3g5I<|hvLW~O}dpNr)m_fBoY2P(#!*xur}{4kQEm2 z%cT~qvk8)F{tb1N4W|Au-#@rxub@MKY9=M#qa4o#b)SSqGi7i00DqDE!lkXkRv*kc zZZ`h_(;jo~syatS2T!<~fAYj@CvH6}r!e#@b8_!q!IBQGQ07x^Rih!f!YL_294c#X zK0v~I)BsD4k5sKa!~Au1oM0^u zS#CQ%*OnDn^_yEIww@xWg3)?tiYG$t@7m8kRQv~!IQDbFX~*RzOcqK10OxsVsUL@M zR<>9d?})UnbIaf?lsqnGd4GVbj(Phyx1o~Ny7eVj8*!x)Qz(@z55ZMGp0an5?39JG z!O!a3gW#)LUE00fbsbynUBCNgO@W>8XD@Me-Gs@D4foWzilGZ)a{*PM{{VQAs8fnM zs!e>Wk2%gftUET!u~jx(7CG``w1d<(hfin!05eTlvEa2pEhRNq{Ms^u7qQy+fD;@o7Y)C1KyDn~LpE$682m&2v^F>yy2X zr4x!zgZ79mkKAMm?)9qDDYwf{f23lG{{Xj4Pejq3;$A%wH8~h;*JP>0^!&8h!m&Qy zODRZW#yof2K{wR`wmx>~f{cUyPVEhtkve0!0thec%WF^GJ3YBi%Co zOK>|Ue6JliV#gWBt~o0XsUQ>&+$4(p=yccm$iYpwHFWgRv|`*9irw3|$h*Xl>!#)!sqi=uuSeV4lO4dhuHOa89@H!4TT?`kl{iHQLN#TGl(p1Z0Ny|$ zzaC>_zA*9GbZ_ouh^Hpv*77!(?|RhZPa!~5gx7ul06kr0;jDF7o+o><>3sdRs!)!j zSq0e+p-NJn7d&yQ{Yz;9KRTyYLXM4QqhXHY;G+i390HZ!RnBg!OdX=TUOD7zeiQb$ zSKO%Ndugww4zw7_`!0RTEhz25JNZe22J>pW4cmD3xUuO^!&0t54^88x-3=34C6E2i z&ncpSuX&;q_>rfc+KQ^{SC&mhNu{!;BtF803rkXI_!?_7BWUH+;dbT%i)krkOj#Y# zR~e2h{C~?*q#;x>iMO_zs(VGE`W-UvsL(X|X((v3K^pTx1RuBa&_Q8E0X6BMjTcjt z`JaY}l-}Zl(i4aYZh`cq?t1CgB{U?@T_?JAd#8~s&_QN`3p5Z}pn}Z=7HA-}U#5Xd zYS%=Rvq1%#2rWnlL{Zg0@JlJU+}m+&_cF-}QS{j9R?w2_s_W~3(M4nBDd@3(j7Hx2IYI`RT?LK@{M7T9jOvJ#fY-^1mkIM5|P zH#t@7^~vkC!ROYZQ(vJzfNHW=ZdyxtgF+3Ky9G~EsGF#yxFaYE+x|XcdN9YT_%Xsla5CB+(&vUfAQ3Ix4INf{{SO1sCW+^NrN4I zm#gwi5Zq47KvH*5BjKqcps11x=($qW`t|@Ryva{pgU%23=m`i<2pQm&qUKq2U<+9s zw(0tRuCxAR=|Wv;SU%zjwpXlhUG@`)85itSL_{{S%2`o%UZVs}ucjBm+qRK{&^ zqyR2J(Z9%?&CpI#%Sm;hlh}>FlA0*43XE(cu8LW)`LB>hX|&z2*z*Xdyz5UX1pZ1I zmfM<@3S2mHw>$yQk$Ix?akJVm=~kyKts(o3!D~X}(P|(Bl=|(^=%={>LUs(sYgJu* z;^dcKyu`zzJXDy-tcJnaC(}`4CsM6oU;rrtz{B7+Yl{W7UQ}aSR;2D3Z}kyh zLPnyz)T>}<+MbHbO-=)uT%AqA(GKGCDyMU-p=V=FiiY}?3{GfN7q@Y^&?$Esv_$WS z%owR+G;k3ehSCC3l>CQHDefghXz)gSiR5PZ$B6IKz5f7ed+aTUX1%1k>U#x0j-*AR zkmmxq%i+fxJ0?Zvldnw`y)|o1u}bysRaCA1da{Z!)l}WcMvDAPT9QpaPM%ey=$jI# zuId4xLTRq5D27zM%18s{pwMWOBehQaPSN|h&6D~P3Y=M>2}(stKY-Ak-kWNjvy0Vz z7ZCi%<+nL=n{SWAl3vEJ;B6S=AzjUcMOhCxk?~uAf_?*7d$;d>S4^;j(~gwYR)!D& z{05NLsRW}VsME9w1sT#4C_p=pcTbjrG`o*l+`L{O!a{q4iv`b;Q@Q^DEha}%J8cnj zm9mc=w!=)dvKdH9K==VoFvgCnNgF5;OVpoYI@?IwfAQ3c3h0+`?V_qC&0qb6qSQ8)f*u;$}}jKA-&1E+~(Y+txw6C;@o}Z5=xY{6%tRboD>H{cHkXXNfHg{2^50fg zxQQ94lk-*A9G>0!IvqYps&Win*$58V986cbg4n3^s1(t@3x=H@oBE3xI>wXaOvXdb zdlr=~r5XUF(C?`Xb-*>=p-UBsIxr-Ok!__7sx7d4b=|cjQ2u(H+u4ZJs=4u2$Mm%Y z5(s@sLsq8aeP2$%(|?UO)On^LYEta+Eqj341r*hZL1F`;`3XzizygpGpPu^6KSPX*yUj< zNrz*%Pox_4HU9t|Fd9OgSkH8ykWI+oNAIxXNWQJWJ+-Pr83`xkchc>f6U`oIsj{psX-WpPl^8QpKtJ%EO=>;>jWpRK?yMXh28pqA zzPk&(yVZf_T5g^9Ce!Y;ubn;ZUkJ3eXx943yM-_gphe8tOpx1wvOnQaVRB3dpMd9mG^zilq5 zg?6KA?rBb%n(jQ(-z$|X`F_{Xw55nV>Bni3^i|Dv8sfNHTaGfI2?mt}bsB>HCvf5A zp;s2Z;M%uG0u%nv$^314TG1aSiu{Ji1+MenE9**h)jZsG>NBWXD0y{@#Y2&U$@n~Z zpRjW0+TuS$feI*A^y^A!{{Z#*idp5BwW3mACg7~PmfpU!%c-aI)_X~>PP$3J+&iFB zhb*ya^Ed?NwB`7v-O|K+n-6<{q#!teNIPv)zM#v7yN_L^KOZ8;&S=*J&*V-Z!1)!c zGVbjusaV_UlVqC?LgPHcs!wS(WPqW!Ls6?dKOw)d(v@lJRh8ttZyl6xT0Ol8DwkdI zFDZD#k)zl#DNOS1ic;37w*LTmmgF-L1O*p+jZ&t;-1Iu^F*gl;kM*Ty$1gat^Oa*W zUrPj@tbME4D;FP=`BKexHyo#p3zkW7)B8ooeJWdWDO27PU-H&6;}MFOhQ{hH#cj#+ zcBHyyS45uIlW#FLTNi9mcI3uN*WBvL5_>&6hyz!n+bcO53nT=Ujho*K*k(Sg27x^= z&VRJ!P!Tpwut7|Zsxnz~_X%1mKsy0S(^=flw~n!zRl% z#;3TssZVr>$!TY_Q`c>0UB=~$TCJtGFt)n#fPVz#u+PUYtvuvNhuLLaj(e>Z&v(cy z58^cfN#%&rs`?lqm9-;8sN5!PRQ|(jYC2@9vfphfY?EH5l&-4oFJANJDov%dap=a< zsZ8RS^oQJ-{hmCDa)>fyILa2c2o(B?fvq&_GPeP=4m3t^7Or;U(Nw;~cyXIcaib~hL^cV^!^JRZI^a&l&pLR z2T|jZ;OI&ztmAmpB*|7wB)|l#WOu31meW!B>e>v1?C9d6NrAU;^E4FCpT?Nu&N}=y z*KK*>^*X}J2=v>1wQ$Zyb4obXXXoNJ;nirOKWec^v@qOOt_qhOp+x)NQ=j~>)Csa7 z?&z#-Y+o3lH3>O(Y?-V3bDFz0<(1^pMYTvh=vShWs_m;u@>ZtYH*FlM8H~FZTlaZ< z6Q%FprNkmUbh_XEtA$;?zkv1DRF$m_B^+%E+q`}{B(M8nt0%JuvTfaQi)GQ58JP{aC~#3}QbM*ndg|iOJv1h)aRp~{JWAT> zyI#OY$yEMT@T-WqJ}i-Q#~EU4^(dIy0u&7>H&4S|n}>ejvpA;#yt%8(zU6+>xb|&? zh{GOxh05Mu`$x%)nX@ptX}BYD)Mru#!{9*cuUzpz-F`mijJg|1?iYo5$LxoaV##%c zrR^O7`&Dx5mV7JEun*iZd&HlJgpwPQgeh$r9-@O|uSno|jvFT3&^!`XmpKM6l5yGg z-$dXCdT6MQVZ?J_Ddf8uNK;DO6cB5x*>TK}EiWAR6|bca=Bv%;6`Kv!_Oxi4_Zl6) zj#ts|D`d(bA_xb{&e{ezHrq zT&`veTXMqSKqxCOEzQr=I(#+Hb3WM;2?2J_Iqx(;fafT$C&86#crkB|qTgs)Dn%7l zdIRgON0s37Lhg&b;W_QK!*p;eXYjJ~;!hv*q${_aJavEM9uEDC=+ms2>(4WAV&wN= zVqa@6u(uG<3aS#V$p(g+#AP`3#XNy;F?^OIp(-hBlV$OTHe9(A$Lr+WS{^r$TxW3o z)>~2JTy6HzE#Ytd%48+h*79R1DR3wdO?vg!ea;Je0C!b)4qP=Jf0aK% zi~j&%xh3%x*yXt@Gz|4oC(r@Z=%jdSYL^s6=R|hvi(WC?m-d{N$v+pm(WO0Wx?AU4 zC84(}P~xN98fDOh0aUBDiW`D-86b0NkJG83D27jJYZ<%Eh5hm!;uJ!Fo9su5;J#Ln zs8D=jIIY`h+Uy?yA?g|CW&+0Xm9hvqx7vF>Qmm2g40lbI%eMMduTf(&2Hvu zQ!+%ioeDzMRw+dFJ8E!@Q=*9ZT#_7LY65Ch6c2E(O#vRM1@2y1E|M(QXpwC zAv+L|^28SXh9y1*ddA`0`)T$&#cFv3EuJmI!F z_u_IwM*fE{QgG;^@DIw55T>#H&y%<(yO#WJ zj6!|FkcCIRTUl399{QG~@BaW2y1KP+a}^cjMY4W>Z@1pKVRmG;`>7}>p|5EaAa$Xx zpuMK4=%lWpNnB?8R}@_Fsb;O++bL`0uqvHC=<1N??Q(kfqs@1J6a2Q}Zfu6NMpqVO z@a5fA+)-O&ju=ytY1A0*tkc$-%<`;RyRv&b(@&WDQEm8!WZB=cO6gi3y>#pEb-{N2 zAM)Ezz>vR(mM{lPXdJ(vS*v=(Nxef!1@|jER!d(tqk^i$U%_FhzOl zxAebN$t>0b3o)+I5!%hc0l-G%xQdZc@Yd>9P_BIu(MCa3P}@k{r0+&_+HGcq5Luvt z%>)r8s+HcRyXpA)BS7hBBJTdJC??)@LFm#D9j2Y$nuwgL_pe<^K>)H);ua3zifS>@ z$?-=vg>^rQ^K?wZIBPFk^-e!K`!(&t1yLSS3qUL7-9js_foU%Ifn0kbW@4QD(@x_F z7+;Z=e2}-=Al;h!=}zV(IFy>x(u%H%)uEGvM7x=H+Lo)+_Y}j?k?=-0mPKpa{{W;X zwBTArPq8IF(vRWwYVSQ5bYaUm#b@;9+v2$S#d#WRSu05G{)~^MU-^^J>no1A+Gw|# z+dc8sb$=e@nHP8#c=L~QQ4pH;fCA7306z`2qHD-lAs(tkW5|9p!R9U;+-e(g-%nz0 z5=|FQrk%iO`a($QUYd8)X=-8exDdX3{Eo$y zhw~;*!Wk^Yn&_1bihxr0)M@$aA$;AFF2jJh653O^U3l>0E{l=zb~y4@3)YnW+CV0% zt7@k8){e?e!E)|FWMY(*kM}dcBjmgrjPe9VJ?oYma6%e; z*=lecK4Z?dzcnjWyf@!SAI!&&k=o$Do$5qmsYw1$sycJOT z5S+lJBRt|voQ#uaPF?S#PlwV_DuXjXYFpS1Nt4JRs3~V zV(=WVGVI+NpK4R>JdKo!6f5u`>f||OT-9bGJk9#Tl&p8AOC66V(2|bR{yMvh&k+kP zAu-$nxu22SS7g83?am~m#C^n|^!j_MKf_pA8MaC;zhy6}2tNg8?Y)_Q6P6G4B}HjS z^e4Hlg#KF7If7edh;?o6e)UvVl7%S>*rd?wA~Y+YR-&ub_0!EYNumuvf$`EBl*cZg z{k5@PMz-hIjJ=CFV*ExL`-?CRjH zZeQ7KtrkjDm7w;iBoIgO(@i84vT7)$D7@#&mdH(8?fHbL zaam7mZ8o3)1Mvq|gC!soRj$IoO-M+Sd4rSZm453rp67R8>XiQgkQ!j$%i@i=TVegF zzW#BTO8)@kjGAkw;$%7g!=xLz4|O59=m$oGMR_(vz14h_PsG0G{yHC5CyICI;{O1& zEr04O_x}K^IFI*?cB)>W5$?M+Y)&6HlGe5;Cw(*XDBFZaQ z>Lpe9@1*Y~H~jnJ+qZT>rt1@k>N8?a|ghUo2FXI6qbew7id zOtFwiUBcQ>_`&fB>yAhc!I2$%g#M6yDm4AEsG_RB^1Kwvl-XuesHG^Q{(6q~kVscH z@7&s{cZPBkWy<3>-cr=U5}{vROUKJcsvbjazAE@Dk;+g-%N8#-)f|XY@>VO*Ye{n$ zEULs~ZZ^AWi>-46`&%oodF`Ty_-R(I4r*QN3tv#Gj&BG1enB-oov3kL$^7-4y=K?0 zg@&JeAv4IXqN^GyDded{l^&;UNOr2cB4?@8Vv!>A(?p(Hb`Oq%3&MlxzJd!oK6(hM zv$N-*j;OQMPX7Qc5kM4ENJg)+g zwp?0JozzGGeDuRcs2+;WU{*nFw6ybx0q4b4Uxu_XFb3$d_p+BcJ=RBQ)5bEZwMA6Y z5T#IVbkC=%3~@zVHKy7QWmJ|rtJEU3s%uV~VJ#R>^wOmfK~M70gG6GdMF?^hLEJmXD5num2Kg1fMWfkg};t$oL@rl7d1kd(lxLV5we@YGZYO7{3hTgU)=f)lEqQ=|~gYmN>e-~T; z_HxL{?j0OhN9FZXPSHm#AS3YeOgiMMLB7S1O+J*oivIwfooiH6pid~tYDa1ra4Amy znh-%BA~e0lMsOY}hvdH{jtbyRiy(}!#!;C60Pq_QNuXMveH3-ngGz>xRUgAy+`a95 zp=-F_JpQ7-dl>|Zl2oJmmQ6_~a!=1xdVsd_I3*U(N_09R(xT9zw$p7S^+62RL!~C( zhexmDqfNO^oKI$7WZT=QqD32uvOR0{9Y>G~tIG)Vs+ikwDN9f^p|9}Mk+!PUvWljx z=RbXMY9UwZRa6K1Q@O53oGj`EASlrS^NtS92&?LmvQyeIlB$9Tsi6rq8&V zb=Z8g*%eX)09JP-3FH1C#<3I+ZuOddP%0p#6j5M$hS_u4z5=&nNTEYwR z8)08cT~}YjeLT~r#Yml5+@6A(&B-Oqw;rr*sBVfZH*mNXIQ1k{Yo|*5kMsN2G2?iv$oU<; z{GW-~ZXG+OTtDwe95^-Er?^;xcVH463fZ#LR@XL+JzO3m0E4rT;JUMm-{K=w20KW{!6&~ zH;HZ<)@+30n+BEfRF9Fl3h&Asfs;Ra5OW4{L3vzKKmW+i&G%4J(l3_=L$hV0xKT=J zqL$J}LR2*uGjjoE#~LnAp3B}vDEfA8`+S$zyPT2D(pz~c!aEb_p3q5GPN!G~SYkt} z_o(r5PEodug)>{e^X%Db_i*$_Qno^zC<33yS7n$uz}jk53tOIFJI?sP3QDlr`7Ucm z{-U?WVb@3r4S`gr)~8grxeFE_BIwv$W=cl?0GI^yXA!!B*!K{yn$(&Qe~zl1ortU7 z{c=Qn;Hh||$dl}@qoD@BkQ#w6Ar0xOn-3KSu8Cqc0Ryo}52?_f_=R7`rkOdpNa$6- z;?NGHBMsrW@!ZYrb)Tu`+gMV?JqQM)NEs`x?L`(Qz}Aix2_|)?7M9`|ZdBI72?#By zpqd{|I&Lm$S|PD-bklfYdv$2mv@*gUA7`8G&!cJ zTH}S!n4~`*?zpRIBq0QnleX2;S(S~N7WGP-2v6xLryk9gS`g{FCHvEgS@y(bg#9(z zpz2tf2mBRtZ0=&vYMgMcm#8W3UAAK>JCA$nNcH|&>Lm7%ySkMP-0eDqt8LF8nJ7YR zDKZv&P>|Zq2>9wexYmJMsS)Jf=WU{WHGI6pZ6aJ`%|gvP+fqW4=|UAvF(tSnp;$*f zG%3u?rX?VIhz+Sj>O!ee`RX^QTh&jfaM2S~Bs6>4T8as(fcn47OA{z!6^RV2e@))X zexcBV`RG*#Hi$I~gGvgH>Rw0lC0ifjG`0Xvsxzt*Wyd0B0uT3i4@PN!s!C8@XaUro z%G3C2TuuJ~w5Zn;g4z(9E#tUt{z5NyIjD%c0!}x8B;|5q^us&dTe#2ZX!9gY1Lxo z%*$%(JZOzCJhVQvGW3SfU*Fm!ZC&*o;iioI64}UY9I`q5BNvfmB-{V{Qz!X)( znLL{DV~BBEc2jV;Lz#Y^AjUZLq2^wh-MK1IZ0wZ!*Ig@%duf=-OG8lK6~jHv_rHr^ z?Y9SRldu|}$y3V*-Rp_qU8GsF9vrzhjTZ>FUZc4c_kE%K&Nih2EArP*%KqD@a|4+u z4qOKV`_!}Fwp(&poj5E^#Jta^?Kl+xB^ozlC9`WMlK2 zp5kKNCAbfIT|bpN7;IYW_a{(e| zjXo;0O7m^bKf1Ajb8j5oEcwHVAh!Y{ro?rnD2CiaiP=dYZ%&%s&F!pL5s7W2ZqxBx zn$zA{ttI+f_*@Z_Vw{6>Mr+*4waDq_5jsMuc}KW2|pBFc29RSK>B z!a4vnnjyJ+?+^6qO%c!_E53~iql)y>LTW?`ltG}+69i_?{bpLMGzEz9-8$p1^wGzS+qxTH@D)vH|}TbBEs>`n;v~9 z{1+6wg!^W4W(oVbaI7l6LQ0fclNAMDNa;{IoqFeqd-Z|MG!5a8_^%Oi-(q>E6>%~~ zW{7uhRmz;_fNz#rPqjMKI26cEEw#odDL(_G&5F{CqSY7T8R5QiJMh|(lUYA(eU!ZWq^$^bGy~gD zqPHjIt}%DOXLHM3G+vkEp7=p16tBfm>Go%tQdFc;gqkF48-MNN*h=oWUvwaN8l<;l z#z=64l&|0quCUo2Ciu@q(kwYtathTX6&73%Bn43Hl~9AMeZ~pRJr&cS`LUBL z-B2FOiX@KHK~wMpRWHN4!CN;p(wnyk?pkg0=dLre)eV;&ZUX=*=akwCf)Er()S@Ug zU3Fmg$5GWk1=Ak-6QY*)guA;@9ZO4*YeT49mfBLbL&|Irg;s#mRks#J-4(UH@MzUaxxXRtowuH2 z@?Qkl=2);ycJ*w#@y08VJly+(3bnq(*3mV@6(!K8nW+`sCWlPVW?vT3)k(F;?2~t6 zU(H(Yx@gUj5UTccm$%tf@t&)Rvnqe@zCYRO%&- ztJny3scw92)t%QpxT~J?o(#$HjN=4_W_u{Z{{VO`E4$j6xU&mu zcLaJJC|Wypdm!t&GirwK6IGBodAm zWT`a?KAyU4CQG$aUmtL9A#%lomU2uo94Qul5yqtD+>t6}!KfxJiXxPk9APe`nGaPb zwWRJqKOk9k#UDVdEv=keRokzyw~<)${Ef^03UaKb-QxcMHgWeXmX*j-LvS-2e0N=d z@*64x4*u4nV76+3$|0DOF`Kl@McI{A zzTn%Xw5W=0+?o-s1+~Yi7<$EALSvA&iG$q60H5U=?O5w01ObHBi^@0mqHC8(1p}1H;5U#H zJYVwDi1}F{uE%M(w+l0i-P)JzH-`}ddu#ZiC||fm59!s`K0&*0ih+FMTex;he{??` zZT>g;g~_;8&LOlpzXHO-H3QWq)a~)mPch9u*0(&2rgu zhcTa}Y^^IvuGI<${hEa80uz&uDtB^}28`Q&M;Ree?(K!Xp5LS>iYOf!XH_E~a7bPK zW3tq>rkAZV%zErusR$E;h$(d1+C!=2Xm;Ac}UA`ER4z zQ^9$p(FNxJ0G6eSBUxXToAg2yK{`j%`RLlpAP@yL8|_UKSb;!LQ%g~iQV&uzq@{$X zrFZygDd5_mJHM8{mW5Jivq1&og+bCD+NH5to(#T7xnP{3fu&WD^i*8z$p(r)|(YE4Wh&HJ6cK|O%;jcx7*1r9CMtoKmP!0wZ?Vl6Suc2IS6q-jYj&oX!8QH zn+OG~sIuWj=Fbvu7H|X3T=Dq{V|tM;BV?rQy=t9lXJ~v{_$-yPofX(W74ww=PeYW` ziXPmCR0E0JcC81rkK#4X+TF7K>#<5h*;Ud`Gq@SH>tvEfLuo%cI9g3L4Gh8}?zG3X^e&2LuOC66guGfc3 zEe40L%xj+twnf~oT-hSipvGw@E_%#UTkw$HIby>sMytS1`Y3*Ht5PSzV|9 z0B0PE%@nLc^2#T;4(W+Be%*CGfE5FxzKllpzrg& zD+8+IYO+1Ti+Beco*rKwV2cy zcKkI?N2zP^3t1Qz`2*Uj5Vy!v5}H^vDIkx=y3NLD<+He8baYl?&iZCTVb6*O@usva zbygPQJ*J9uxtcoA*X^};H#QlX76Kgf|Z~+&^6!rPTHS}e&&#(@<=ww9-*wl(z`xKb%Lb! zrMFaD`K3mqRF_cfl8Z%*o78|5-_s+an7q}_?LW!`V1I&i$;(e(srKP5Y5Gw1FC=-6$901|=JO;o9n>mBWfb-;u&`8;03Aky z@^d+~CZQMlD{yMjLaxV{(46FMDZ#l)I83)9C9^V8ML(wUXlvJ@I;$N@wT8QBxrCmY za3s5`@qU^jgHTJUr3Q&Pxfkua(=`G!UxJ)9}zjtA5?Oeg{ecCvwE3 z*W;og87WE>B}j6L9jG6u^wW)LDm1eOp=QAhm3w5Uei~@8)#!wC0+YfV1vEkK9R&?^ z3~sF`r0;Dyr}SB}{+%eOWP&!&ybwP0PR+)=hj*0I zcC0$#A@!^JU-Hf%R2ubI15u-}l$y~>xXaqKP`EDXVmyfUC*!0$Ms~r0d~P4I&UJ0vMFyme3#X9c8b84qamlDKG$gBs(O; z#e9XUwGIj$M}|$MNWQn;5o^+$T;;8!RG(3!nrfXB_HCnRR+g#GTdokMGHB}#B$4qI z(jk7=C0#@w!WU1iVOb6y&%3aj8igm|QC)0faC~c)$j?f8D$!+i%Y56dnn3JaXO@=J z{_ScA)LU6VaSGD)fzj1PT&->%{CkQ<>#A4&d+HWSF6!frTzPht4C%kyej17+?u--;jVXKDuZDsZTcxU>s-G@-Cv1XacMblm!BHBL9GAW0^t*GeLZ^`QU*O(iH4PehcA zL_-csh}^eKcx!p2&3z-eNM%j{wE^lvYDSxuyG=T&HlP^*Ju1GLqwz0=7*7M(rGtro zVs_q#UB9~~EQyj7w!74|D3Y}-pW&`t%X|0S)RRTm%$UgSz!a?)&qp%KDjnpzTCH0xh8S&>jhg@uPMW!6 zXjWE7S5y;=?x3Y9rz(&F0br;KZb$iPx1|IC1oRqd8a9*>^-_b=Nm3JUD7&|9g6$PW zC>n31Na`mops7`w;VIjMw_j4KRIR@aR>=CTL@@@n2-AN~!kU6glTr}juxZx1BtcA^ zeU2*UDWw*wl$xlXl=$i?{{SPX7J?_XIOtZO>!>c1LZrqjbVvDrHAV@5 zP5rx_mn(ujn4}sIR47i9YzY-eyz52Ixw!gdasBHqob@ETEvBNQe&-6+^`G(8fhLM0 z8Al2zrJxWx{+))HX;O=gSEScYr2w56tWc>ku8!=*p`|=YtqY%}1Mwv7r2rBp(?n(* zca~g|pj4*`Qpxz8&Yg|Are7L#*sU_J_p2>}3Ja5OQdo@@01`^xSVyPAwbJA?3Ojn$ zb@$6`iYEv7`H%6@rp#K0pQp9T}s#%N>sdt1p8ZX(*tj2pm zQ1O<(<LVZBg%clY~QY|CZb*0(lsMS^f08innqQz+HF9gyJM)U@{ILWOO zMnD4RuaF#zkY}0QuZl5AjxdQX2JY)T_w)esrFhl4OCIeTn&z9a7wJOTD0BUsgC1dAqq79V47D` znK(r)6N?Dp93sJhL9*ZM5pJ0SiBP)DQ(-`;oq|fY+f2IVofNBUI*&3jVDU$c zU5|9hFRXuSvOr3omg7#YIcb)`B=;nWXq$B>MH=qHB{=Xm0ASp`GBez5e7Z!(;w?qX z(&jDJ+Kj;Ob!tEvx3{M(u%lCE*j=ylue6jOyJlrh&e(5|>#i=$d zA$`QAa^jHOx#|=Esn=3MR63+qv^vT#mwAtMx-m8{D^(k4P;aAgOgz;cNvE#kLvciC zjA=n3dMXV@j0&P59cVb>(1g`RWD0ujX`(3%6)NFd;UgW!toF2|Ht=mLl08b-xv%lm zCbwFxL{SFDu*wa^bd%}6rluozRKxBL^?fmqB(p-}?!HqQPkMh1& zGOWh&ab)*FLxG!eD^AGG4#vd?$XD~z?~lPowr1bG6ycsQyYpW#aeok4@Sx5`Z)6KDjuhS>(cgGel z5d1IUek1GtI1uueG!Kesmhlf3B(2ty6g@6r)m`8GIRW*64`xmVwbpRaLj9+6>15)T!;s zhl+-x&1K6{RO2?O8*BoTO6b0Uo+*=)e`c-GcfLN;%Zp@MS4y|uvI39hH2W-J?o(e^ zakT9Tk9fFqREODS!EUxf_fGG~eM;^8sjCn%@rt-rW^bR8y64Pm~!v;QQRn z&K|-FNQrHX7hSz-XD%iK?0RI>{ev|T45n|C2kmnoyO zD6eqs9XAAOqFk%u6?;Hh%T7AU-$>+gp0QkR@op`x%%WSd8c7L=@BkC>CY9DNA(4u6 zyR}>+05cF%9dPG8*G&Oj-;TK zd^ge^V*2Qn7D<3=kzD1xX{&=atDZu{2jpzaE}8`&fG1*qJt^4fK&VhgRwp`zNN__l z8BEK4$px3UL(GJbq4ufFC%!W?#zBd<*RmEi79lG4qQhJ z)uW&&bRN5E=RJ_eUd33W+E#aoVzT(hhR-hWubr}bo@e(K_7td608$jXii5t@^w&6< zv@~6Tj`F1yyxW0axQbb&2YNbxhMNu{EVippWdpTrJV-zKXa4|up}eO2sUq!gyW5iM zt=f}zhJ@6irBUPYpjO9IEg0ALqJeiI9jcn&+3=jnr&7d2Zf@Fem!n}K0A|tdO@jb1=JOvhi|;z{{Wd9lO6jc(cwaNCTPWD zC`)bT;KMAllG_(+lJTO#74EPI^$Q&b!%WE&POVZPxsYh^kn>waIdR*P>|{)cv(-bm z14`}i)y_8NSdN8N<*lKVd4htbah@S5=ic^Si1f7fxKQ%;2jO0tx^Ezwlq)3uEofa) zq6&8$wChnnWqy+#Q9w&d?gW&LfKfelIy~7uTA81Yf+*5eQRkl%xdWA$2Ghy^0N7k$ zEJi{ZDNCK-{8Y43Rru?5#d-a`i>m0WRl&W;_HQATZ}_Jy`7Ur@zR~>KE?budGrL8_ z_q&Z2%jv3#sHq2C!x#I8uwy9b=)9+x{gmdUkiC-!6XHMRAh-5{=UbE|v30%MBFb{T zk23R%N(ZLO>s?Bl_Xm%6MxVu0*?!A;6CG0a{t4k~`zK~HVKE-{$`N8Z=l=j45eMH0 z{3@V!)gAZlBH5Y<_XWDge#x@0^EzRPqRu;xcBQiU{D&@f( znU?e2!K>C~#NT=w%5V1zKu=tjyDbUfj1Fua#8WcJbPxqzv-Slw>f3BSYUuup>ELL%6eO>G~aPe>(^MKD7UW_TL-rrjYg_!s9@ke znvuCa*s6@71${Khm?zs7sY#e>HLuVQu9DfRmcz9hL=pi#+a8*1J;=<y~F;$@zJDF zYwn+k&_`9C@%WttR9WvI^&jJ)g%QfplRBF9R!!R!D-xnC-~^oewxnX@ju5wAEvkWf8EhhHMt0v2Gl>6f;2ipO?!Vm zIT}NoR04pm-89pkM3**;PhBk_^j=p@`idRqN%NL{&vf<7v)_YDQZ3uFPn_=Q$^Tf?pv z<;c>}e8m`Mbbv~34IwF76+$X0>%O~J4EOIjiFgcoEgm|rHhYizSK|5h9q|UpdJ&}x zBux!^&|0 zpXn_30qd-uONKH=5HzakwjS%gFg2|Tf+S)SVKAgjQCiVZnpT7JI_DYQ1Q;q8WaHlK zM=dI7nlD?NWTXv>dZv`uF}&kNhobA4?pGX*1Usrrb)^tg*!MenBD>dF>x?A_MXa>V zHQm!@R3tv4wI|yElS%+oqtu!Jv_MP0bvn#kVdyjqOK+B`6)6zF>{?2RQc`MtI%_#D zEUUQkTBX~)?fFbvqb4dEX4cSU1+?~+ir2Yx>aF_f6RnP_)9Zyo8OsIuKW=Ssi*l!X zM&BH%Ax;O6(M|TR&sENdg<{F8(Gr~W%Ur2e>4{W%Z5I&xRSF@*p3;?5;B*`XDwg}8 z3I%vpSN{M@NY7Vbn7kr1#9O50-pc;~x3zI~9^xXY`gVs@lTe!0nyVKL-M6}#>AP@DhRUP&0#fSh`YM{xpG`=9f~TS;0{2m=I_-?!Eeeiqw%6_HxHs=X zaps(DFEUL7jg^2DlkppAn`^jbG-;_)?s0R(Dn^N;CBD)?RY3VuOLSzYk%X4CB+yp0 z{IwZ1RAs3$8)?YxN_B?b6n(bbS`zAd_N*X)wEUG(p|z)|O7%$YmiYIOG9Dx3EKicK z=hV(ufZSnTv85q?*)y$@Q7xqR4uNGIr{qqVzPI(EAz9+?11AKU1ETsW``qD}?suy0?rztvw3Po2SC&ymbqvwh0itm+=NH7a2^9lG53WIVs&? z*z{6XI*za3#jCtuOJ*>EU7U=2P zuftlfb`VEpmw4RGrFO^5r`Yn>?W5x#k;1ZhBgS{1Ws$U-W#^`TD};$pYnz((5)b37yLzoG>RXK+IW8A6+(&eqKrY8o+=Qsq z>Pb7BQQ1_)^Bli5a(>c&SFz%EmG*_@k^th~TWkK5Z`I@O^ovOR!$d*wj0`7dL~Zn9`HY;<6zRii23Tw zVL|Sw2Q8JlQ92v&AK&a-dE<3h1f#Vz`EDzwztFeEAO7I~0Q*wBf49TWEPL6qLrVQS z0seY>{Y4UoD3ie|HTLUdDIruWCasprvR#d^%~!?7b6I)z(`WapPANyttLGd zHsF>?RmX24Fjsi@oH-8`4f|+fdy?(IidHFcWzFriSf8EGt~+}%?*hkSOl@a(yBV%?v;T;{e}c0-9vXi6(k5|oeQsTOu^ zg|rGzcQGa7hUx%36uF&Y%x5^A=NXvTEzf01Y2&|RlqDp72Tonb>NbkA9Y3WgcY>vy zE5baB#eAn{zDStKx5ir0aqR8^*y{vPS89!psx56F8~zJjnaxLY4&AgXwKz@PnNW;p zN4VSVmfZ?FDJumhZ_iWS?aKz!wae!#o-pHr^B#SVA+K&cE2Ha(oyYW_SG=8%r`4>5 z{2OJe>^NJmR@iuerp_0;d$y-xEk>yoPW#rh)y15e1(?jTjw-}m@g!%wbDHB_rB6(^ zzxySD+*0xzE+Bw@X&RMz52+mrgNeQLXKTC)E5?D2PT;6aNijtYYEBKFZ0@C=PFrnRSwl~&+i(NaD{WNbX4?8Kmms#^0X_;q z=Nn2{yF$9Op43fmwcq^7I)5_-9aUY%ZYs^H3mpckLemxK(W7ZvC=yAfKVbYkalg?njQwNs%_ziE&yct2Ab%ABMwi zF!dkQqo}zW^mk#(l@0Jx^j8+!i9U)#ick4z_LR43uq2VwMt}-M;|w;{o8L{A=W+hr zT>!^&sU64EZ>jb+t&!T5L!Zb?Y{2FUt4i;O)}n&zSzS>=Xi6%Yej3vM0HBPD)nVJt z=O5acmp>Y;R=(iwP42iW+JIBkej0%;4UO)m+w&>p{?x&f!*$GlM;K*+ntRYhoAPy2Mgrhy^(3&Y}^4cjHMfMy_gd5bII9L)%TQ%6+mYiC9klK<| zK0{5@RBq_3g_oQqGSp~|{{TUV$PJ|*hTfXDx4bRUW^XWI-fro?bIn+he0J*4_Kbs9 zP$~MqEde;)RNQt)Z3sm&tE7A7see#gqv{m&rjB{HwIasY#U)fGvD%_3s@g4s9io@r zPj_(Cg;=%r{{Y_5K_@=h_*R~pjFb^>#aE|Y1~#bBp`EuhKMfKUUhJLyx>MG=0TL@B zQKske)Sy5^ZBj^XL*}fsR8(jH1ny5whM1OdM7ZK4r;8k4noojmQEh5{gpTgPUg7|* z!kUQ7h7zSZ%1TrPAHK`c+*>jmEV|JtakUB%N5pDVoCwi0T0$;TrM*ME+LU{|6?s^p|&w4{It+fs(96(FDt5)Q_{EjH-thpK=u z+ybM0FhNV+5GAJ*)il!F#IW=hkb>JlClu@B))|0ine2ExjdxA1#>zs%7U#By*WLP5 zuZF5~cM~*!jB2iMcJ}Xo`4qk7o(aG>t5#?DlIF>~x}b}NsxygouWp3*nshp687%G| zbt5|+vq%vtOxhHp1uAuL(6gk`SdKQah9a)uw0}`zjg#={tsR6wGNoto7gaP;nLz@l zQcLty<(<(-LGGO%v<3m~*P0LQ7g0WlJP|BkKILeFan6 zIaaLR{JHYi9Sf^zG@#W?&m^nc7yuParW>IL(CLL@QL3l?KjWyVCk3M1so3ZRDpU-$=HDn!npb^Vv{6yLp~%fN z`(H&J4A8AlsOh8>VJpFF>+96*sL4uC$77`l_m+>LJCmryo{CgMYNWnN=0^LLvt?z{ zsJ5hlP&#{r@2g0yX&SD$c`r>~LUF4f7h(1V63mO6>YBFA1yT|~{{R!Be^haeRe1N3 zejmq7xwTU2R_S#oyMNQ9#u0BSN_B+UaVum}q5I5{MpT4)qB}>m0RAU^NMNdA@2N#t zo|cdhHY1?Z#o8ujlb}~8!E8CKubZOeS2mLEj|F9Oo(f1H$s@I)zv6WccbZWKv?l)m znw+YxROWeh=rTR)W-v@xg*&9UrF~~_!kR=;fSnDgR`14WHvBgfx7bj$D(Q1i?q-yc zQLvyR@YQUOJJq}q1!>H}rv-X?Xihl_K{}IfADLHV zCL%LMPyBMj-@9M0+@pGCvM!FSS=6NNmiv#?K1WxQ3Jn%p6w+KtS0Qe9dQzH(Rg7`JF2tth;NkK_GTVVeHsIv=HHKV#Tbr>A)QKnln_Ercmi9Y4U)jH{|K9rgP zr&{_d8Qr%vq|=W3R1nRlMJk;n|A?E zp0OOKlv%qT_d@XF(?UD^mr~%|F7^2~wq_Qz8q|*jqvi&5oYR|fDDfXMZ8)N1+AUs) zOK@8VAs}oAb#*I%V-2n?po$l2vtH-sjpk+MwQGE%?o$WbONFic%NNH+xC)S{ms^@xaG|^cGTu1@diT4W^x7?d+{{VoJr70V&5(0&O zd+H>aIUCT>EBkB|QVm*ao_0$FyJFOXQ2X(h3r^IJ`)S+BG~) zvU1RbsUUZ5pq*5204)pRB!nv#hm*3I1LP~e@zgg-s(8H$iA7$gM# zKx(bVX%2vuY}Z8?>H=`I>;)xMNFCm_>!!beQ57XCLEQhW-RS4g@2vBmV^gDl+q->y4 zSo))ksYAM}>%DZ4DwdVwNL30`^%JIq6hm+tqli?7n=XM+2TdjH09884h(_aFJ>%L( z*Zj%ocGMY2Z&ds)DNQb=NTMnTJB>nN)lFeX1ME;T@~WZG(x4NdROtamQ4j?aUg5WC z{B<94P__rE4W`_T+sJC7rm6y!HQdwj(q%R7fr+5h1sz}c1`ag}<9Eodl5KG4RYIYj4#$XN8gIHE$HJlc4?|3RZ&-{tT?YL#K@o%(Ax#e@%GNWOQC$`|2 z$sb#U4@8aj*7J;eUvYJETH{6z>z?xe0C=tJEQ{LGWTj}9yhi0U^Oh-k&TjaWtbN#b z2V&S{O!}OY9hwhy1P{~QRQ_7d`;mbdyN^MStvanovs|nF^_SI1&7{_qR~6^KcAnP- zdwP@VdTW}85NbNE#^MN}3Ny9icZ*jeTrw!vBsGg(ASH*f#ITXuJ-14XlIYx(B8eiO zJ!-#UyMD*TRSC>v6~RlKYJ0J(#iGC4OJ4~slwY~y@?>O?HhB1yVk-AE(_rYj3W zEmx=oYCj!zTt~LH@U$6CO2JRdrFkukQE_XLzUselZcvi***BC~D{cjg_d- zFn$Uy`Hr*FCqlFf!lyIm~ zrN-fs!@byR+eHH#7Q(NERS~aPKn&5 z_S0(DP$;Y@fl7O;zfCHMsZiPprJ4vV&_Q~X_ZnzX1YxwAP=oaJ(N#OJExUet0m!{I zsZdQTsg+EvJ8tLXy}#dEkv3xaa5o1(@qsc=T%^JMzmMur$7m`DqQF(`PW23Gew@~%SNLhlxj(( zE9s*KiT5_409>c0oQSIVnzi?T-Twd`BB`HEc#;KoKc1e6Ly>_QP)gHE174uhQe&CT z=$f9OB-GHDlyC- zZ=NO3+LHTrHsH^AE4EW1jxPz`z~f_~(3b`)#hxxW?TKXbokn2d>Y?c3?Uu^N8` z$ew2VP~^6?OOcKQ+@jQ$&=LckNfoBOamD`ta(J8ehR$}luMl#d+ov(XJh~tAFn%kR z{GZ|8afe8h#oHN^6fj6r{?fg=4_@F1*Kn}SSl>n+3&X4NT)guPhBi%?k-9QGMxTnN znRgONiX1}VE4)|J{{VTW@A3PFp>)1G?h_wE9IMtrfV$VX3^J0RT7T~~iO68%cV*nL zKH$jOQR*t4h`iZa8*8-hMU9Pm{56cc;^6cPwPC&0Eocr+c~%!t+ECf6^?JKRIqdM(A4VUO;GW>czpnG)2w1%laWB}eq3LZDZs-98%6-eW4#uWd5h zUJqpZ=Eu0E=a9j=aRrX;V8-P`j%LBUzKK&mei2939B*#IuhKpmrjEtUG%D(Bn_WXn zRYt${HRR&5@W(H5TgD|yMCIdw=RaiR=F!-f%OLi9ouYwstu%vMumsSZ3%C!aKndFmuq6bZ>+2&G`*d(OlHX)YuaVKYB^MrI;Cog2 z=L;&3^KC}9H)nZoD#ZI_j?h{GAxEhpL*cF=VT%ylnRgK-dM?A3V%LzNYW?)PC61>RUAuYRqqw6WGEk))Rs1l zP}oRySJQukml5X2*{{jp6J;b{IS;~{%X!BUFr@{{YaF?>ACiVf#Vw`6^I;>S?(4BQOUp&_ci*g@o zhxHNM3Watfajx3vTJfP?eI&K|uD*SzGK)td!SXM{jM>CJjd?eR+*`*>s-R;dw6YzJ zl%(`isXGx*)2(L{aFQ3mdeWg{`BX+5nCEXmbzEi0vCD=ne!JsWc1t&WepHwjR*gYS zs86md!h{2|&~=r3CMOo2t5RhS6;7-cf9!6JwzXA>1F`h_6sv7nK@g&+y)>asY^u!d zlKCyR&uH#Fs~^mT@2dGgby`Bc#pImO3wti-c+urI5qYoUy`#b3H81JM^6VU+{zmU2 zBowXqPpKWPLAW@*%69EalxU!6ch@4x;chYW`q66N1=;a@hTkP?=ue~j6;JuI#7zLO+(Z8rxyBu-!71qn9|%@#*Z}x}H_fY@ZdG zW}M?IX-VxSl@tV5SEd~mb1s^6DZJcOf&nD_2d1MNa-M9>DP9G}?PytDrs*oy={vg? zOYxQYmKKdVASor$l;Ao403f)6!`a>HO9WHcdhKv|6#oFq^Qsz>8={P0VLcOn_mdR& z?j2ci`qa9u$vSYqW~d8rN||uWqAo=dr7|5`U;(f?mv1)Uv)A#M%|f&|eTpiU+QdX7 zx|R9|`*m$^9W_)k&Ptouii9$hB>^P>bQIJ6U{qHSQM9<~57M#-6eg!lGDB3$z@ivT zn2;#?%{nK!RMfUeiQy0X$>OCl)}>QwtzY=*)*TWJ2}9d%*BJBTy18yGu%YfFy;LH* zjr7S5B{idZD3dL;Gl%&x9i%iH$9cxUq!fen8SyH5bg2jYGNFP$_N1^mT9WQZBn8F3yOFiFOf%yLbw@#M?40RMi{{U}_ zpVYL|)9@Ndcu6toM3p8Lpsg+`NDCj$P(RN``GPjb@lITR{{Rxw_VUuE!Q81qG^gTq z8Pb57mK}6lweyjSWd2bZ6|}am?r~WYR+XhydAg>jx8TyE?SIP z^8AFAqz%x6PfwnrMR7u|?(yPPVsTF;;Nz1SD~M#n=`FL`3Y#OefKRUan%nSOML~Ih zbpdZ=^zoM?hZ``@@a7&+wOnPnb;lS{NC|3Mi>@Q`DyXKX;N+!S~W4wP8mf5d8=F9z8vw47?>zT5-UAh0`!?WeSbE48=2llar;qO)>;F936D ztUOn}rx5A|RZu}1ybhsRCQ#^9&zC_)QO8pd8w)STG$(%51ohkJt4p>CE!7p9%r{{C zS2W_d<-X>Vx*dw>uf!Cq{{VTc1SGOW*QNqUPsA%x$So%(pk?X;ox&8q5X1E8Q9-W(zI9GCL2fw#$o1aluNZB+#AoxY`M!P0I4t z)OAig5MRLIfA z9fB?Ui68u3fBdo26?ckr(Px4Tt;Kb5M!yvu3b`mo0@S3QwLiy2QjURp+H^HTMY>bd zee`rga6$x;O&w8GFMCH_6;cb3pj3P`0!e!IhMIUZ6h-K8X{P}QsIsTyqeVrOQ5E#k zHYHVP+KWyC4J+485vo(Q2|;RJ$_G*#(MH-3sF>nXQ{S=bG^(T+f=+M(PtQoAkkY2- z8y&~|^(p;S1XrP^q9a6IKq@EG(CPF`RJ1yQUzyPcilZxaB7mCo(@*M@&Z#N)JBo!U z0N?P_WjTYO517=cp>zC2TFlLPhZ}mZN8v)N$p{5?{@>FT3Cv~g{{SgaUz%7=iwfnh+t_U3L*0d3w#sTI<_ zL}is*2UI_p=>0^=V%)z`+M%E%swTA66H)@S(tH#NY^SHyqX7-bB?bDA!%1)nZ6O2_ zK?MACTXaU2?GgU~b1jxfS5yH0I#s(XB@}C?(Y03g%Wp&*a}6Si2}r1?Q%&`$^tQEQ z-!AtfOG0WuJL#n1Ar@MDl1Hzmg%X}c7l>>9Ta%>iMG(~}G{6Z_^crpArAct0QD%RT zA`1nERz1b-v8JNV{S>Q9YL#8jxcjPceN=ktu+*(H8?~tOY3&;i!%0Tnbw*iixS)`l zpN5)#hN?siE_DL3JX?qtR*Q_5$Zf5_BVu(5DE&zl)zic=4;5h+grb2v0nlqu*h5+Z zz}(7S`k`BHpbBnubkPE6jis)hr_)I(6h~NVe~-sXB`zk>Ex>E*3hHV$(^4lJ7^-={ zJj8(F5NNdm8y`Aq98U8mq-j+SHw)e`S%cS`^wc{M=^o;cDoH_AX}`#7(eeV_6?+-h zs_{ULi@R=zZ@Aea&uSt98gc0Dtb~9CHx<(xv`BM4U)OFKKLpA)@!*OosqfBPhklO97z4K(X+KG{oY*d%tVp!Cod?>dC=k(5f2FrNl4Aq$eL#6W*i+m#}z2iLEr zo^`y6Hu$QgtZOCllZi0gs@=sIl*lvh%#_Tz%V|^nF)AH1~ z^eQFeD%8DVwZpMnB3mG~8IKXPw$o`8ph)ZdbtiG?q1(m-QS#>h0H;a{3%a2by$?-0 z=^_b!(pikBWynww<2JV(QTQP}$NaR2+qls`ISpl16XMJ{JmJi_M zRdHX^({|Vn6*k(Gb_KwiQ&KY5f+%(lrA5Kxi{bIDvl=2puxS@zd!f?sKd z+HrmA?^1)H)B(`y6}`}wLI-5I; zZYhXyHNBA@>6T!aO@)kpEQu;1_{o}vl%*|AiXPP{@4l=t*fv}Ft5T`2Z{)go52Xje za|eq#Q0ytadPiEP2S~jy1unLF68sCNU5)T zuPm)bzlyc$NLm>XZ-8DgmF4mm9coGZSJjvOwfrrMLcDW7kbHw`i9X#9BN*N++?I;M zq&%4P={{UkZ4!5;Lbd%P*8dpY|Dxj zN|Fhpsqm=RBfrO6lm0WJ^j1HR&Hxg&iMh)d?st>h6(5Z=w(xt|T0?HhVGufjEN;^B)__uFB;8Psj2cGjzeWLWc8l*F#@$ zERc|Y-QQY#_G1k+U_lV2-o7eRQd4^jtD0?gN?)JOq|x;0mZM|L|JlMjPjN}5|;=kQk%qY+$zRy z9|c`6xH!n18zaMO;+&3Mqq>G=TP{q>M4sQMz#96~(?<)_?qR z17C_)5-uIBrL}I zx|xt2iyg;d&lM~y^rgTz?KJr6Ws5dS3FxEbq~9F}RbK82d8upeY=RVh-tD&?b|q^4x<=_6u72xf%(tLe0zW-@stbpKRYG4^1bDo zE;j-YKv;Fd^*q0Dr|4{Ms&B4=!@a+LQr1~P4H1G(S1R`l-MDZ!kXyN>?wP@&ovLY5 z@K(|>l8DTyxLw`oN}MNUC%6Mm_048(*OA~?Y_f5DVcsDlUh^ne@*|lso{;R4pxj29 zQ;Pa`B+zZC*7)yRxC*~1mJl|jq|sNr`^wx$5Vc0jEjAmS^-VndJB_u7u75$)1NAvk zS|3VlW5@ltg7QjBD_ip`nDXy+?kr&rh6gk{{VFJ07P*uzCTKqDxJO{XbJ>n$u2@O1VlgE-iYMjMMKC}wxM56-THLY zhB_Ki!80UMpVE*rEMq9YTitGSXqN{{Xv8(7b|G}?3ajhWQZad`tt6-lpD%=(?jpjY z8U8*_k!*)?{n8%Hp|Im(?ot6Qs1xl10HSrWviBj;OXbm}VRC-eLve3Min&uy7J*t^5l`X_znypma{UW4+t;Y)D5?{-`{{WyYmm$wK0kyqX=egu> zl03!a?d*ZAp1T9qg;1*f^(M%2?OsRpanEi|PnbSR8H0HrL`*Fhhuh&+%} zxcO<;wuL3ms!vl+B>*4=gpjnfvD}{Ip5T8Y(@A?bbxLWX`ic8A{qZv%8^|$V8a%$t zp31m7?6#m`oRxENPuy`CE~qxkQslUzr>*KFC%JV~M{-JPQ_s0~4{WS5Mm1X8E?}~a za{F$P7TI%!8u4wI+{<*v(EKTks5sNCx{$Q33PB*HK-3ZGt|VCaNnu-<2zcBQVw*~k zg4)8+cM2MjqLP_Bi*Mvv_(o3yi7$4HXOSgCc{qMUe}g9HXNNqe`c6uT4d~9i*ga5} zP)|<=F&NVxEq_`s`9Lz(GeHwL*eh{NX9J8XVIfMmRq7Q)=6=;p-8vPoR+|O z=*_sLEe)-8DN4;i+kU!@m36bO35!OQ-J|C8z@EkC=HYyFMRi$B{G>J~ng z5Ru-IPpANXRn%89I?}3NsPsn_x!&&fw&i44!tqP8R^lJDu2T}nYF1K}rN%ZPV{kf; zT@>*&5CN(tx!uu=a_{FM$(XpX$gns2Gm5_2iFshMe{voNVMYF*mW9KaLA5*{QtDIj z*!pT13FWH7x{x&P!kT+hd(VV`3GF`0i-q6QC`}wQl^gRGm=myqfQ^ zI#O?VMgoGm8tCYhyj*tFK??kIkW-J05Q(`J(0`twlXXw|Eb6uj~zCkcG*4ce0p# zy9;K8roRQv-f{b1@y2e<$ynP0!*WnkkfxhYw^O|eMFzba!#{PGmc%ma&eh`neftl# zb!Eo9!tVjo@LW>ozqZdPU}~88uqyd(Y$-{d6~5y^@lRfw^_La*hXb2v+Th2Y#d!0U zduhw~q@+&~k}>_w2ly^v<~#{cdLfTpiRFXX?KsxBZ>X{&`bqWR#-3B?^ zUT8wDx6V18Z&QI$iKoQ|mDMHAC|9EF7>;boC_|K0bjMtb6tu#tqh&6d&feizdoHtr z=MQ6tnyIBKI8p3=KeD1IXg*zc)(0!b0j&byBKwr0IFhB`PS}Fi6 zbpR9f6RvqL8=5)lPM!;*{{Zb#O%P~p4~naA$0XDmk<(G9u%d8e3Fv`(H4t4&fKzm; z$D#c70NRyeFZwBV%g+3@kMXug#Fl*a>u%$0!D(|Wz}TB17Mqi)r)s1|N`r0@+B%Ks zD_UzeamQhF-l-mCwY23^dLlAZH*k)JN&|rjb!Aurk^b)Sc6?e`z9n+ zGv-J3*S5ps@TQw|3AavaiHDs`b+Ct2mYg8C$Wnq7M?z8!2_HQ&ZOAB%!2uCdMdXI| zs;Um0nY2l4g!EmB`vmjsE)n~S@%9atTmyW2>s;-J+@Z3UI5@4mPwT zd{_8uBgtR_k^r^*V({#94_c4Izw%Izn%pOF@sG=%Wa7E8RRNrC`#BJh;we}!yr&r{ z6x?>KQ&KUKwn#-3eAGrcOZ-&^uw?J?KQ-8LxQ-ShN4T=<p)8S4&(m-6!0%KIrob&{M(b5<4i9G@$SXD9vOB;TIy9({{X!=g`Ow)jmp>@FAg~(HgkjB?bG3MW;gxG z%beR5Pim0MN+oWoN)5i1toD;YK<(I_bsj62n&dgJ9T%JVu09w;9?p(jl<~Ket~@8t zd~eD&_d6AX-XUBc8-TdzbRqPfn-Ncu44VS^g`K6IqI%GjuDN z@cWs4wWQFGhN$3q3eM+IMDfeo{^xGCT~aFNXSE^Pb?oV*yI`(~c-a6HQLqlQpyMa8 zY5xEbs?!K~+Tm4V$;(Yb=$`@o#WVZ@!4HkOip0-2aXVd&wPw02y5(4^h9Gv-b`@@bAjV?n>dVNn;4flFiNm5?d$+ zfd2qayB%w+bCAy6O$x`~aqF8`f7}&Aoocw--CGmjyA9MIt*2XCY6t%SegjtFK5kRjs zmePo*MGyfW?A4PRL0NF}oh>t|sRW^Yg#Q4mK?D(Qgo@}OfK?D^eNNg?Dl>Yo1px{h zkwc;~`H-S&c_$fZ<4h-$qB;KnKgBnwa@4W1{{Up_{g>0XP!gg}kvmH$RmL!vw8#Gd zaHrqf^P(@vn56kKVCML{25Kj_9(BFO&h=VDhLr2Rtl0Wcss}|wLbYee?q7``_7bs_ zptb@-%4~%yn{*@Lq(0`TM{WX7cbWNe=uhpf{?B*mDM9*G`w3CcOd*+|SQwMBJ6eSL!>vNAVhtcPE(`0Tos1ay!3yoH!Pr?at@86m~66CPrQ>H$gGzkwR3%C~$W!D=}C zhAXE;R4-_hV?NrynFXMY|5}^*SLi}sV^yQk4h-lSsa`t zlNHl2*B-5~DtO~*2*@xiot@PMxXKiSqp1l{sQ&;hUs=C>xoXB`qx5eBx>Z(r(Ul5p zsHzk!ElH=~X{^h9t|4z@e&zxZT?FFVzs=qCXaa)SPrXn0iU54hmgyXOqYZC@Dpayh z)@n^o`fGEN=n!QlpR4iFZ!|^-Q}>FwXZ;(`e1C9T{{Xj%I$!LkE}#P3l#~I!->RBT zNeHEGioyNCu=QFw4h}-vD$UIug?|!0q0{B0N6`kA&*U$rp9w{H@cKdcXb^)+)O4@- z{#tl+OgpL%QHZKgE6&mI)0+w~SA!rvPqr5jn0)OCCRBAreaP$mZ5kajw1S^wsw#^p z)AQ2b)h(fVl1g{d+0hXipwub8nJHkS%%~}`{yLJ>)5fb!8bZ}#ls_FdD^#Yba#WX@ zQ2_*xT`o-&HnfyP&PgJpNZnMgO~~sg_tKSwp3-zxN^23)$7j?9De}-rXH-$}j^Cbw zeIzZaf>bG81Qdb7e4wr#R^x5tfb`K^M6n1M^e!=19E*e8P2Lu6;f6eipaZ6XZmK99+ldS?RYUAETLtgdGGz0?TLWp@I)wXGI;z@u$9*ZfYSl=Dqd z)VK2dv`I-phI$HWAazQ5G_=4G_cYsm1FD}JXp`p69g1~McA)^0PffH@#FFDcnXK~O z62z3nUDdGYJ){x_<8HcFq(Gvqn&G8Q@SJ8$Au*NJm82;ykJ3RE*#3H4ag`a*1S$v4 zH_163PMZuj6zfsk2vPwgmkIR#I5a?kt z0*gyZYETeBuTidj&Hc8u%3HcfzsGXXR=8=e$HX4}{{VvQe%O1#d5F1ecOT}naiiGn zbUnePK^=!f)8ZxXJp6saIFByFc?XBzGI&#)?TlYA1%-E_F_74G*r7*q5KTpTboA9H zv;4~AAq(wp^tgc0-X&^3alYAF;jSUQu@$ts8=FppzKV%AzioQ!vw$eP%7)V@)(*5A zk?Ek)iCvq7bmE+4iP+Kua%8Y&PD*wqZ*DgHH1ngWR&kxJP^x{|@{tv?ec2>5)jE|p zpoH}Kk*7K^lADN=AXewkJfckgK;v8qQVZ}ne+jVT)|lk&8MLxwINCt{Be;#VQDVvh zM|CqQ{lpIiLA~cAOJ;Ki+nor55^`#XnO9Jz+(i_V^9NUKYe>{pR63)jLc9D!%{-5r z;2G{G%Gv$rE92V*y(_fZX3lzAnUYCLI~J-{D^rI~|;6ies}Td7>=6G0Oq4duid}uSRMGB8uW+u^cD$s0L5R1fb+NhQE&6uLT9e!FQ4c52 zUI*g`A7ITD$GT+7T+5>;YA~VPl%L0Au3^RF7uor)si5e)H$R=Y-Zur9(Z^D$MYbxR zl^L+vT@)0gfJr;_px0`+cxYa5jQ3xCzi%!Om%zRraW*8?s7AcrU<=+#6bMr1T)*ZO zPynR@fCE_(Ja2ZZ1IeeI^LbUEl8lr%rAUC3w4wB1t9z_SQubty|KR9adrXl2o|p6cL@>EJr!iYQX^3_e>4Zdjd3ax%W-26F{ z{LTz&Yv&$S@cuV*L9oPX_SLaK#|T+UBm{0$t%>@zKOOCr+}cT{RStjOj7_uaY&}9e z6+qbW^Zyoe#>R~C_$(lif{lwhPn-UX6FTgILasL3Eekw?X zf=j!3Vf#98?x#QT(qx%^Kfn7-KU!BL{vm5>5$vdI*AlqEq$exRC-X5ER+u=-z5 zL-Jb~xxW~AHy>CFerZy}k#Scb+z|rR=Ic)Nl z$9v)F@K!c?tR6%BaNGxO_@cPqi7zcvlE5+z*@1yfft{QvIIz;$5F$Y9#46`)?Cb z?UXdXY^$LarmUyVPk9v|6B^W zqq3wcp&s&5HP3Ivkv^^hi>~8hj_6UfN-puHOw{GP&B}7t!-TY6c$QP!(2=&8n07b z8>=4F!uk3_+Hsz>>NnP(3hc}}+P=Sf%W?sMx;rl-*Ri6cu{?QyDCP_NejxIe+GX3j zCR(`uEY+szXNc+geb#-_qMfLd+e5CgKRA`PEf<#H*QxPbA`fet{G*OO?6+@0UW=ie zO=R*z9CBPPNY5=<)wP77Hn;x(V5qZ_0Q$SsgWA85rFF-V(wl(-v(=8XzluXP>t*BrHNfc z3@vhEW!Q^qjO^x=5Vi$eUugQ%)A;J^6@L2BQ)V$u+qkG9`Ays1p>z}yJ19ZhTM!8g z5FF6u7xI@Gai1+@{56kQ8gg^E2x349SWM_*L4*{E)Ln7iy|YK`vsHSw*Xm3#Na+CH zKuz37Z|NT(BzzYoY&;vw9D(P}yPT|ic_W8-%La^M-fy!@49m&Zp6Vtu3@ub>_X??W zCYtYgUSneq9Tp+pqx^gqm~dX&b+v?S1!&NHZqFspb{C;|j@Jh35sFm7T+qin_DN+&k)@l_>gXyT~Q5fxwZA`1L zvbUW=KdQ?>t!qf$fg`EY`{;;7J0P2ZHt{midW~>;-x@!0mu{dQ>QpKcuzC=8)2(w< zcf{(aLQqRbo28DnhNTi7lt(D~-|AFWELHwQpHbpg*VD&|8#D3IKDxROmz!%xu@NkxBg{_xx7 zq`9>~y?OhUR=wYzmqi^2A0&^ezb*7(>0$!2-t_I!w9zYi3%BQ_)eVRhD7K)zl%M87 z=toK)mZY)Ug$isE4vX(o{@iajE*?O5?Us1a$TxQ{IhT)VjI2W8CDP;a@kzwD9kN%t zj8_n*dXw4`PhD`1e}eR6^%^uQqjAgf=Itj{^e_F2-)~cjhR5yFs`n%l+DSF72|DM| zJdUenvY~6YaU*g%>QGEL5WQ%msO|WqjVIIz9-0^cPjK7fG|f=-*4@Y@Vw?W}hL3Sh zEFGh_Zl663#RcIV)cI-8Q3bI9U3C8PqZgEn^|dBLj+H5X@9Ij8K-?s1?|z1x-WYdzZEW}AOtTrQ3l;YUKd%GFv@sn}~r2MND)23@)Zlarp~n{XlA zDpZivreq%0UQ3Ed-625J*V9>9tz~1V;6m8j&kUlia42^F0OBA0-kJe0P@U@3nxvq6 zh&y`q(WVboum{s~@X-Xlq%Hg0BDCNAT2G{#qT5f=1-ev6uTM=!lxm3>O6ouhudax; ziN@5UjktHGLD%P`RD}8y`e$N*0rk>>ElDf|6kI0Up-JrcgQW>Y6h~TAr1xPugYF>r zQ_ywN8bV@AcQpi;mVyG4)6-Ib(?t!$f`&|lkUP5kbVQ#7LE&xh0#n*Q9T%uVOQ=wt ziBaC4!%ASGsvejr2H}ia?x<68;kO~R0u+U$5Nq<)&6aL^cUCtLRTfW-TV1fWMuHE) zQr>FtTZegJG98y-Zr5{hBsFo4grPv6k8L&H_z&(!FN$zu-Mya`bf z7{79+w3pJmX^rEkAcCqUS;TVVy0l1wrqrIR?IZlPTYbj#=(|<}+!)^4wbf89_YIwH z_ls?pP$~)vYyNt|h{o@LKF< zTpooHOefn2aZHkm)Q?e3R!fX3>6u>zeYoq)t}?*9DCZ9p@BS#_nXYroLRzuECS|$p zWjMpHBzp^v3|Bk<0Bl8B0mO=*s=m2tuq4a6t6Pucj&yrST*sWhebuUkDP2!#{{Y@- z-Dp~;{djUlSSFj&o|<9y=sN8__@^GUWW1GtS#p;p#=+;_?P%m%q!t$@Tnumb%?q5i zn{6s3OvWovG}zOxLrIn1*y!3+tL!jqq^rM3>V?hnJXZwrQz*^6YP`$;0H81|xLtS- z{V7hOYtXFIo0o2SL0C${om({^iuNjp*HG@PoNXxY)lbb!v%O<(YSZPf9)ypIn8$VBXggKEZQC*-|@sf8TX2ha$SxJ z+aUD^w0didU0XKKIZ`~XE7ulRM>WI?Tda*A!E+&4>Zas3NVgAh(o2oG?x}0~(xQR7 ziK(u;(`})E4YbvGsn-+8=QZ`yMO`vWW42r5=w&4i#&EZ|AkdFdPf&Ej1s)tbCBBbg zX50dScv*9^vU5Bf4;KGw<~_%t)Wxt*GqGEF;>SXr#HZ+5}F;v z5xCn`VZ=FK5{fN^?NLMNsZu==eG|VCb;cf1N7Clr+rFtF`g}CRtZRh|nQgc_ty7zP z)ysH{0E}}#xkF+&uk4p0au_0?r?KwgPs3FA7>Jx|1q!r-F*|f7p(jQ1lP12*iyx3N z>sDC}OF)?7;-RTMHSC=i(9EmO+r28(_`>Dw2H_mugkuaM<8+4 zlNV(Wt&q~g4VpH9Dkwh@sgXnmfmKU0o^?v*6HR>~9iVj7w^1-qVVZhq0itNSg?UO6 zDWtemCsB2EI#M*6`6(is=~O9Aj8P{U3PPw20qds&08yL`Q{l@reDjZT%tAs$hOaQ6 zWl1DeB(_3L55Va%xHwTswVTs;J=0IdoPqq8fozc`v{jLDq?tJWLV{|vyA8sGDE|QF zNC!#9Ji0m_qxmTr=mRFJbPm*g`u<%Ly6FZj*dY>_NpULr97yd|EAbkfTo!ol@leMj zWL8WgvLqK)_2s|_W4~s>9ZCNHW}AGVDKSJ{s)nq0-bUm*u*Webq};9#4&^k;2Wu#= z>RPEpkIzwCF)pH$DGh3#EgmoO9QIQyCBD5?53W0FQ2_orF*yfS1=1IEOYC_2V79F) zxW#r$$Tcc;N+~~)G}M6Rlx_~9d)shaTH8X!nUJLI-Pj5KG}4S`s7N!OiG7wBjFas& zl+&ibY1XhN-6N1Ibw<9G>U4(#RBdukeoBRCYo?16fP_Q)f$mQA-+$w#07ghHqNsjr zSPa}_6Uvcq*&rplWHCBncD#qti&G3UML6(Jl?EzT@)HK@*sip$btx zn`n^{RLgxzec}>6gSCEI2~T)jhLCuYb!*s!r9i5Vw{P+6#+UL?jg- z@Y9TF8kCwEql<$jkyjXTx_Xo-f0)y;f!Q&?R8Rp^y-t{T)jVFkBAQA7)qB6gM2l!y zppaWdp6N-U8)(w@0=IF{QA*anng}(Tq9{t8aR>u-kTiyZT}1n|6;@jh!jkQow@D~@ zL%YAFD@|!F?SPJ}A$NDV^h$0(y;_`5Y2SUM^#`fn=ck&&6n`lRegpE}Ne{KyJw7Dq zENLYsRs^=o3fPp^y>y9KXq$+DkGR@eVMi=8R7AvFYzzdb^n?F!P?4aHmB z9>AMS6i8EjuDCvm{60E=8*fumnakenZqT1wtw0mMrnK!83mPP;)eaIs1Fb*t({DPZ zIjHkZs1Pfo^ukDS1uHb#YyKLJ?NqN#8$v<_Pw>+PsmP52OPe$xkQGy1hM~EH>ZXSf zqb@+nTDRI&6WRni=V@ewwOartjK7SM0V7IM*4s!HRz@NIHy%!v40jV zleu0lNWMw7-LY?f-*p|yOQ|62nw3!2x|uEZofC0c1uFB$idSA(zuWFO=ObAxR+tS2 zquO2WTGUN9svH0vwH`b2J1IZw0*fs_Z|u$*i;{T?&g*Cu4k0R_Jw04HHsyfqi~UoH z>WohCBK0)U-deP=Q~gNgl~n$I;HH7g9|ZGo#2?fW?7z3A*8OCSZjH)z=2HL?zQ#&| ztH1id)KQzKWg(bgHL8<5fAH=85xicmax*yj1iPb1Xcp1f187lDrAg{e`b78J+tE&f z$7!i5PUI=pJShmDkmQi;3`kH!cVsvd?l0hwX+$31xm+Y7`gw*~vx409{VEwCI>s?!Y%kWhrz`A4R_`@~}q^PAQ)K<2ccnE;jS zUvMRL*R@tMN!B?CA0SBn3!A7apfviv8t;q7Ykeg+otP!1Sq@*1S^Im0=}jTQPvr-> zU*I&bP>HoF&hqWZ8{?3ptQH{v(^UTeOJF9Oej1S1DU;|DHwEJEX1u$ASP}}Gopp$* zBy>wr*ZY-!gL-P_T0&FU_7foSLwO&PjIWzH4)T?EHtoswStQr`aew8Lzu=8UwRIRM zo4hC0Dg<$$Qlq&fjkP*AXqt=#H(4BZ!Na#zB|#44^U~kJMX2fc`iknx5mlJWTy39k zf`k<7fzUVZbwlIobu7AxOa_aq4cu7azqYKad|&9~9#Z9vy&>w+)Y&X8gi$IRhTKzP zqoJ;(!*4^mXRc@Uo?G!;v~dN;O?Fy|KaEb9Xa!hwZ56WMPD1Bvg{I}>MkmU*+eZrW zwAjz`KNL4T(P6q6%L^|%8C6PGM2_N)-ueK(;5DD+7cp8`zFod*5I+9^=(hgfW2DIB zy$?cGdp?@G}^Wh*7~Lf=~x{`D%;Xe1D~69(ub~rT2#> zE;5EO`eV>1zuVJ+?jA__W5fJi3MrI}-QGLbCid)1NJ|o2@2~Qn+8@VP{FfrzdvfK= z*=G3$*7Ym&70WoYpYeFiHDbqLs@WAOY6Nzc3jLN45|s9lun8K?;kix{W&tHbmmK9h zGnmCOd>dTT(MMP0c^h%U*fV$Yc40);n7t zol#8V4D)v8%Z$B_PKuc-4Yc%ffD)6i6&wCK>sT&J#`HB6UUcN#C!fj-c91>_tlqeT zm|;q|LcQX5nNls)Rpl*4amA>5lDZm=blVPLi+CFz0A7B|dt=DsaWX>Zp7a$^rSAEX z;L9-Dw>^y+>H#uj#anKg^Z`#N{I#;Rn#y`@cz52p&F!{E(Z#Yx=>A}j!m1+4GK-?! zQZH90pL`mC=^&c)*;L!%O86VZMWwgJ ziVYKPY~k&f3x8PXejd6r7S(fYYv!d>c4l#s$F)6UiFJBCno9fRTiaJ+7P?RscJ6;Y zN0H`~8U=l8$L(D&sOEzI01~6OCdgBStwl=wYES2?$Ft3<)w0=uXtXlr1rFC#gYfhR zN^5Fpm-O+|P>hMw(vVW}yNBvfQP6#$qF~y@Pemm0-a&ywxVWB97uRe>r5NeQ+=SfK zNZYr(&k8_AD^dk@MR^9$Lwcx>#Kie3dVRVp)4_g4cpJu9JWKuS#w@coyTMO&zaXEN zkhUHK5Wm^R_SjcXU2|ST#d3F}Yh~IGWz%?vCB)&LC3U~3@zBY8c)qtaXdaely8qKzk3*t+)4Xg(zquN4fPVex3gSN&Izyyy3Uj z#EByzb(>#wF}H0783Tb+0L*d;_NO1RN>n?XU{u>LMGnSr0bQ0TMyX-9jj!7vT4`sN zSog$JHLgwND|9CAlKAic0B8(6ved_{Zy^RaBm$+j>VdCXjZJlqKC~{{R2c8_Efo#k zqkOVYBxKnS_NKU4T(GOdK~}r-ue5Hq*@tjc+n~?`(uY^}Y_hgsESQtvvKdKoS63yE zfNed~b_OYnaHeX$K)-Obbmtf7twg``6f0{c=) zwxM)u9#>~@&;x)e{{RvzFG2-f-?fyGR$T2@)DKat^V;USr527U*&xt_3`luQX^yJU z)6uoT-vpb;R|}eKsut9#Ep~*?1L{!j8}2#{USW$j z1rLxh<~FDC{?t z-kzto(z|LK*i@Ka3Fw_nJfc!}Iw=B~yP{r(plEyl04-L`(ygF)PB_klfDfns8iwg8 zq_N3mm3JpqQaY+^eGq_oW^gHNLEHt9GyVCLN725KcFIjT=5ypP-56;NYUrd`%3o@!TF{@vTVWY+9`(3MecTOu~)-f{p0Y__A6pT}KK+N(|XMnnCf zV0qop5QcQzY4-c7z6mbwr9H%MNvS9C*JQbV=M|2FMyU2|q1%Z#Ky+VhigZGvGy$Y1yrVxTcy;L-m`!ri7O-*T~RWRlw2!6HRl>Brm zgQHtGx~F0({(4&usUDeK*#7{xvy~s>-#zf4xxclu=g?>4x3-V`HKJ-m%+!2Ct%@H? z>m|+X%(f-NQWX9_d+FW=6V-l(vs$ll9YLnNaKYI?UzbTE1?k`Q_LaX)1 z#I7!#dbM9r^!RVtn^SNhqDzH=P*dEbll3T8wXU|%`>l~|UY?X(?&I0KW2W_W+!L1) z;Z8P#5Bz(j~Se}KjEC5l6Cvi!!B>`7`t{kw$<0ZsSx z3!$w(8nA7ZS-2HXHb-pR95hZPgn+HK9Y&xKLcJ!f(H8+73GFAKC*h?wr=pJ}jc4jK zTBtYNil_?u*GVBL8m~-sw^9v$3eh88rNxe|E~PP!*Dsm4Bt!CFbF zP^r@i@K(TrI@dyooGP^~H0H_`uT8hpf>IL>wgnP$0&7;)+G=(>l*z$Rq_RYm)9m#@ zHTBcw&2on8cLnH?+J3*!O*ym)pfyALsym?5@Y76v4yr?|Mdx*C-aEg;PBqj@b0~;P zjLWI*Q(;IabdgcMrQ2S;nzYcW%d0j&M+${pxo^e(D&`1|HI!pfmnfwBR~FEeKH7*P zp-BS0b=f#az8SnTfsR62I~C_obpHTsoMV(mMZ_RTdN>LYxW(j0+J7U4#QUEcN^d2% zq%CdQUjZ_boy`!WiYsoF{yO&`w|~1lW=Wze!Li;wm(A~UKV+Pfj=ax`f3FDs)B36K zKiBbGOyy4(a!fkqF57>`-?Ywbs2I{3C_hhHdLF)qUYNCYmHRiq@b;et=G%U6dyb#b zylbM6bq*B&0I%GtCPl+RwL9)W>@_tcQo4nC#eoZ~Vg1Oc6mm((`1_5R;ajFL{{XYn z=PpgYMw<_le|F#ztgN%xxB@rnJ?r|4zwp;I$#A3Umle_Y-?|qKxS@4pr%v^!^;F{L zjocsu8BCJfAfyT!&>H+Tg}&nr)CR8q0E71p+8xQ4F3~C35u-(jr)A7_wxdT0DpRjK zu-XuiI+ZAZ2gg`Urx|z!M6X-Q8VW7<6p{@${{THu&xvz;ElrkO z9_cOEXa=;P_4L(zxYFLL+C05VbK+Ue=Z>Ge+;Is%+wT|KWZOK;fB>yF`-)PDEvL8< z^@M;0*S3y|>Ziqai#x#k zK;E@#`TV(B6LZBf3qsNs{3Wf()c*iT{CaDmTF|pa%Jdgh+t*_O4FG-?X;5AEYN({UxNpQXU)yD7moe+Ky1ig{lx zy~2lY^OugY7Va1Xe1*r&hUIE%TW_PXGoehaVI9d`2XQ}^t?mu&)KNY&T{T>{IW9XJ znVt)eDcw-G*Y|6;@$Kizr@5nuX4+)n+~Z}p-sV~C?j**tq&>zB1ih3U@fhAHQOj<&N@hR+hy|6J5;`l6BPh zsQR2^;&!`rTz{9e+GJy#baA5NON_{2)we04#Dsk>uzI1yp4BA$!31j)A8@wMkPrw< zmT3XE!cNFo?b?;!Qle<0thp4`B3xQA<+zYLYDph~I<1|)WqoY@%mpU7>825-dlHD1 z+v={GK~rsH(uzfSguNYY{{W~{@Y_wuQ}1n56?JLbeaYRnLaILs=>!!fFbJaT--R5I zZahA0Pr6)s%`f)tp=uvWBvZ4mpeIu!=&`v5?Ri%|{{X~%?2zJ_!5Wg$0ZllGjgYhv zP)6Rmuw>!AN~19z+N$@LVO;r-+oj(w978!)*hyODJi%%DAs~{f1y`W>YHMX-?wAgF zI)za!zD;6R8=F?iG2Djxi!H4V3KT3Ww1PbibfEC0oO{D+r?&6brKJi?#E)?xfOgo8 zOHqiTI@GvMpXthM{10%^px}&qO}Gh4lB)WHqCh77+p-&Kan*L957nd>9*IPXs_UI{ z5wNu6sLhw!Y}LXI2Ai&$r`ZX1fhBQok^bIG4g;(P+n%p&#i?S0yYbSZkWn4QgW2X* z4}+g$80WrN!PJ1pJclYkEas5k+X5YSL{!Xu>j0_jF=41~Wx0568^L)P%9jnCJan!}-0aH}1aNHAsY$N{3_7z(>z*HFVmrg)a|3r!YSiSG+RG z#Jg@+g}kxc*KD}WuD?iE(x5A-c#>Sg)oIGDwoKUvbp+kus|NB9A+#uaaBq&SZKRK- zVcqtx&bp;zJv*Wgb)ld9*!I7>Re$6wD)HRD`EI9cX?VFD`V>3$?fi8^m4{@-YcRJS zt+*+}$5&6=-CJ!+qFw+I@gq-IyHy)42bQYTWApz2wd59SiZ=G1Z@GUZ9@GAMy0tWN z$X!Rdq$vZ*n*h9HCTb%pL#c1vjF!??vVH@!6Y$fSc#GSjPaK)CO-C99OL(sf*JONfWOCnz86Gb& zB3#@+$&AxPREEG(o9X`TI_ZgcQ=EKuBS_~~UfIxi@G8YER&p+sw4H0IG7_J`r5(G^y9 zoeCjUx?57s5(z&6qX7|e_uKfL{u&fQrD{?%R7wD)3q`s6waO*FG78?3w516hNheOi z(n^naIE|HE4{OCclzugG_+Tw=mOaky#R?wY?TG8BjmR)fwN%8}NZxubd0e8o2IDdt zhx(D6S{4ZPN$vjtW~`+>)x=J#YKl`$Y);i}+H~SgP)bP(A3ZZsRA3`D?oQ)Qc1Z?> z7D|h5bkaf9I&}!sf`pOK(wcClLdtAU!$yf)pvUF>bnO%k;Ra%o4%&>IswlYhsh}Wg zN+gjjkwPg!L#8Dfr=uyl>uE=FkMTN8f{H^EGW8@=*h#9D)}Dh&NponDV>Q|ZchCVO zfVGzG8zXA^XzH2+RNh(dP!y^4(K?Cn61+ykEtQef=md!oDKAu?j)+mF<^hJtfw| zZ6yirH19oWtYz)|@;_@jEw!E=7Dc_pCeAI5`1#NIrSBKx=*(Q&wJm6v^JoPWB#NYv zDmK*XN$w&ea;Uwsf?#$oae0fv)}9OHCb4@vozbolzjN%?rLtAqPnCTJwJfQlgk89!FTNGD=Y3CD}u)o{DVZ@2nr zAxku_!%x{ox%E+RWesAr@aHBJbTj}+NEJ`OQ{}BT>IIOIfzDdHb7{(hxKY^p>0YR! zSEf!k`jSVpl$i-nxuMka${xp=B<%k?NCCyPQ=sGQRbF{isIT5 z;;Nk4E?gzKA!MmeAMp5Unpf1VOfl_h5vGQjaZZY*SnSk*2VYG`s*u*ysppO{lI!if z<6lBeRHNtzh@z^op^(~8(_`pB00FHS^lK{h4U_aWEUI7oF*mPX}0>QNwSGp(6kiPldiii>a_ zN#xl%t~%N?tE-{utOYb?Jfx{5(NI#NK^lp3Xx*x07Y`2VroTKgiML$05VOjZqRpBL zo_U1S4{_o*Q1vthnk#BGszkCEI*XxxE^(T;*M#wi_XbNhJcYmRmsBWyNnDZKgc^UP zI)QNNxhCC|6;>Z1y<7P21IaU+n!{{k(H@mb(5XpDC?xjzltp>DhYgo#vrajJm6~xb?3@?Q7OcYfLyOhX%r0n5R z$X!)U`khu-)XJx1o*5k#Uwar^4>tu(_ses&A)=5^a_K?0si@YalLkgf?6Wa3(nq%loY0{NmXUYWc&{Q038xH1>>6<`fsUB8m1_^Q{;-(jCf(-{{S00lEk}=JHAsb zoq_F|@k44Ld_%iyhkI{oX5?4*N2cwy`G3)RyWZ@N({QX^hesBNk@su-S3Sr|7qY59 z_nPh!FC(-qMwltqs{!M!u`g!sUR`K7&4Me>sYjw#M`};`>BcD*;;vg|_Z3_&^JJg> zMW~(j+~}x6lW_X072&9lZqKrO+E5?1z-$*Qh*|zel+j_czpmz*kCqi+Z;4WI4FTo; z0aT0B$8J|y^WG&+%yq|JuJu!^D?j3N^H>_t0)X;d@{>uRC*h+`IM6A(lhoax9KmsB zewQuqL-SBS_^hfpN!8`BI|quxN_|Q9C`u$(=gVRwX2X)7ArYJERO5st0DdIt()R8F zS-<>omv{xye>-Ngk1KKf1BtZ$5t~wjaa-HbN4yk zDh}~R$>Od-$kzxyUF2B~HOFd!{{X-nhBShyNxG@(C-(#;L^2cb9eV4S^VjkQ+XIRK z$z5wspL*?n+dqyQhYYzbkF_h;?p;%3$G;4?@%y(fc(u*fE>(kJx4Zq?+dZ~V_O(c& zTLsqJK~Yx!02*tK-tvAyaOPTMBiiD73RQYj1H(AYta4;AlJ95-y=b_#oZnu)W>@>0 zi7QOWPN}*Ob_yVWfY+oja0qRh--7aABD^;jZ+Y%l(bxUE{99zT@|Pm{6_$IE?7Vqz zOjvV5JHM|h>RN|x{{Tv9td}QjmoE^oRFy*PoEAXODWh5!);?e6rwh)v#G1z;A&YD7 z1{gu_Lc5Zx6H~s_=yhLX?S4(&BCp)BH~#)$+IM=i9u-ZTZ^%2(B4t*)XAVobK*yPR zKlOHQ>cWEIk+h0;W zJ>~4GdVdcGdoR&atG+aY~DRRn@RHytCbv&fn$#6)3I(a zxjB*8{nWK=ocdCSl%PJZa3f(uuIFQ#Y}W02hy;b^YnKa4r^eD;8b}@_EBoNb+F*H8 zZOpPBQO~X%U5;2@Ogj(u*s*2EoiDe!#VJyIl9toAL0_J@54pbh3_ZY#7Ka||*WSi^ z3pXaSH&+fZGy6w>nz=TAum=zS0Cih=ACXxDa6w3j%MdNebwn%bD^lB7p&tRRV|V+U zx^sMy3a;wU+9V;=bmoV^3jY8EtIMBY-Y8X~7yiWgmhjd?6SUr#y)zD6XBnsKXhv<7 z1$;+!H2(k}{mx2XOPFXrA7ZOHAMEMT#k$Vqms9(IKfpCmzD)Zlc_EHHk4)o>rLU0j zT#9jnBL3s4w6|$A{{V?J$SO!|8iJ(nT5Z0%Ry+3tiMXgC)g6GUzYk3(8(p$N}$s^s%zs-@l} z-PjcS@!1S z>#e#N@uuF-2=Scu(;_1o0si55T!OCP`Wap}sw;jr&Lrd|XM!Y^LR0I2WRCcWtp-SnX+h;n1MPgla!BMGELOhG%r?~}O zL7q<-2P8PyatEtJiUR#>4BtYTts(yPH^g{{T;O zfeQXfhD!m zVz?WcPPJISbG@sE{>j%Ct}K)aiXN-yp3CMhkX%Q1orzqs+q`R)iRU9+B1?U41|Df; zwKSxzzod$5*FXK7Vz1#6F{8{XzIBg!xdtlf&^|-ChK9cd*1r+_<+^fIU+nqgEwyl9 zH{CsjP&nB%?o~P;j=bgWXSD*s`{L+VuRW6e!9OnFM&x;ib=7M=Nn)1;xoyj4R5_@p zyrm?eK<(N=_16xL8KZYji`LNI!!%&mLZMzv!kDzjMoVXLBGVyZ4oB`C$XfRuqv5TF zCnvm6td}9fh8^wbR3?X(Kpl6W8o61P5|1+}K|t1kbsB0wrHMABn6rY1X;%KHOi2w| zq{neotkcO+2DQ{vZl7$rrxm-YQjVd@)nRPo-8$nbAc3f*Sw`vfvd{h+isd04XZgP^ARyj}MFx~f-6yJkHPdbFfNg^MD?73N3~v+S$d? zwuGwJ=kMIF#~-%87Dmtc#(cZ3L6WW5yl=&}gf#W{A)3=A?G(MG!T4;-cG-9;tLj8+@A7pWrm$MCR2fpwxXT{BZF1c%ZqlhSR z33;c|^J@gQ>Qoe!4x122>!;<2w9ux5!4(L)-Uv#Tp+KM1N$I43N>M^I_;IG)X+g%E zqML9Ur9|#G*!1x*{UfX`EHV;h9$P++f|CGJc$)J8qjqzdox&@Dy>RqCBd z*mWM704UN85He#kF`s4R1){Y{JCvV_PodO^H5`uT( zD6H>KsX8j7p;_Jz#X1RPprBla%bS$Y>0&~nNn+BI9fnLcp+1Q@6`UQi;t40=Ao3J@nsgISs*YMFr5b)D;f$M*#8HC_#P} zTaCuKrSy=Q1+;sKs3T<%Pp-Ou5BEYV#@_b?IQU`EqL2QkO6XoZX77*Zr)*A1NlGhW^F13sIIE%QUWlsL5z$&koL&)-*&C@2x z?sGCL<l1rTDS?Qui>orM~|`X&`_=33zo>qXkAd7k}#_>c;04lFF%6|D35+_&jX4- zZu0JOpeh)WyP$a|+d~Lh_WMY!eR>UY>)af|T}F$xV>vW3uoj+*GJY_pXx#jDOLLEV zI;};nL9Xah8|^K!dy`sfs%u4U9pdf>{k#0hX~F*BM}xCRklqL8E+c%vuPwN~*4(-@ z%I;RmgR!78K79tcXDqPv&1uzj&M%bpA>01|YNozPaUAXh@{^XhhVIInZyCK^Ey1;< zq^+R({iXC96cp+v74?dTvS&IgChrwc_|p965P1)ZUxi~e2J0EAG#{!wmlDyhtp|3T z<15%y+dgps?hLDiRy_5>SRx zr7x$YPO81S%7?+R-Ie$5tBHD97G1Px@H#3tn)tuU_8tVE%voM1FCE2gw|Lg|F>`I` zHx)~mD3u~4q#@NcQ$o5@y67{?*BS-py9;R;1BjqXpKdNkZ;bq};wzNL!#116$ySTU zbpjO{=+dJQYftAX)~|x!wmZK}^mr<(pJSahj`7umAHhT(X>o(RIpq&B;7V)SX4!j} zA^-rSe%gD6V`Ksb)N+*@ZKyfBPp7$aJ}9{D_taUn>a|6pU2Q6<7ZNmD3V}4yvlwflI#Rd5paZV_|Jc zN}F*fp+b||dg-X%LxP}v2@3sN_$b-^6KJ6HmFzm z>j1+*Jik&JG$A7r?@}j5dn;SSGp!lFA6Tv~vXx2pf+Q>olC*#gPOLX>r4~NmIyVqg zzJ3*KnA425l`!hT>~REw4SMaV4r+}X%~c=dT+2M+{GkEci?ggU9HJRn+JoRtdTK1Q z5!a%;v9%WsA-WaP4-x#NguH%R)(#Z6az)~Y_ZN%1iEcc1f0eQpg_H5vYF)f>JX9?8 z?Y+bjP?Z((pN}~|E#2<<22qm1!`r+(x7^1qF_I#(rKqSSC3G|&A*9V=xk|Ok82eRL z3!jMA9ARR4%w{_(I=j%Mdtg)=k587Gj>H8zUP9-vw1N01W#=Gw7JrDjAu>zv@wNUW zYt({w05?F$zg@EGp#|`wJqNfJYqdt2nWc{o?3}{LB#G?!AH-|_03UjpJmkF`AyKt4l{rd7G6I4~?xBUB6L|4;{bgo}OdcRb|Ps zOxhSBVP&UW01kvK8uj$lZbcubD0oN2Ppxj~k8q$)Y>Juf;<;-H3PsW~T9D#K#b^~8 zug*pu#=I+DEh%H25$vIxEX72c!>K*K+JXN79a7sqg=;RN+`_I6z4Xts!;;dfq1d#p zo7FqhS_^j-&n{S6;;jwynBieKC9d%HJTGQTX>Cs^rl?X=Pv-gR$@f8x)Kyd4W8ZN$ zhKi)*K38Nj7Npvtm<^JCfVy^$l%i=}QNJ$T+6A+RiL^IUswtmMInE0gAu10_C<}P{ zg(t0bBI&-YdxC2X7ol)is&y+t)2_456kB42(Il+UL0Jw5@zW#b$oJc|y=FC<#FdE> zz^z?Md%JpSOb~AsRd=}o6R4}z;tSSWy~@)mB`@2bb(aAI8n&{U{{V)g$S7#6VSeU^ zCr&Gme|!!&8!jcE);fQVgmID)PdIN?)Q`7S);t`qJmWdOaFo6qF)*Xp3rrLA%gtl9t80eKh-eB6YGs7P?3 zTIkYM((EbQuAM3iTSOAkQS|IafJ+4td995_4vGB{v}%zjBzB!dBUL7RbMXZNmDY~d*??4PvNsdUU9PI?_uE$twQ=@2lJag&>S7sfDaO#c zBqf%TS6Zlz$kkoz>iwZz*u}OYO6I?mys+;zbADr*Hb}Ll;}XPzYe=!wQWgIIzbdEj zrmQWaA*(whfpV|3G#hE*)S_O}hf1nRdNzi;)D>MDTT+`_$O%GCHm-|p5~B$+zftwl zTq>R;MPT@5+W}?QW)y|IswD@h1L0k0F>sBmFPOMHv{q^SA_V4r$WMe40Z0TNmaQKJ zR(dm@tGbrDEp?ng1kh<84_&od_fc9hDy241cJ@i=+SVHCRlvqP{ zFpjm`;B|KA5-74%)~IA4Y>}wo5R?HW8f~vZ`JFA!sy8-?Wrk>a`soEa#?dIKC5BYB z8c-T(hk&OtH5XoPWlpI9V4BjE>7Tuc=KjlS>=o*=HX0@hxd|Vo z6<@+zUC#9;=O(75ZaAv#+@sueU|cCh0&O%~-y{i_DJ~UvBq)R*kkJx)ra+=z!0qwm zUL3kj09&j~u3Vya1fYE*^EyL~Qi(`+3C>*SOP>D#{8>v<)IJ^Cfv5TeilU6LrygmP zC+m4ZC?7Q%X|PH(>CR;`ZZNHEP%~zX(;b7LSQS_J>F@;-2u+JzK{n$KI8SkP#H0<$ zJ;V=9Bm_-yPrhEbHs#_4iaWr@$D^T?whqC?};l?s2YNw44rs;rr|5Z>zA?sFsl0aD*=k4kDnk5#36RZm}rjCDm?r7Inp zW>Jt?F`HsbmkfqncrcIwKh%=)SqE>2b#%sB{g!smncth}TT zN}W&KHCMCGOvc@O=6gm9VSB$D;XHcLj9Xg*iA(XLCD|lX(5XZCYoGf?jD5Z%)PL)l z#mD(BrTdHIO}x$vc7BU?hL4>pRUX$#s^KfuuD!cxAg!ki#s1I6*@i^AA&v-ff2A)feifCKjf$H} zi;DpKyN;mUip&DHv+~-Ksq>2~bxIuxH2k$PBC811I56ue$g;}?lq8?-64-5H{nD^i z{s8DS5>ZbeY-Kt5X6^mYH@&olceMfI8J8=!V;|BTKMh>&4GSr01yiTRygkl9@)L`3 zzTWswU6_@1SpcOGE&l-XjuZ0!m>-^#XGh2aZ#g)Q*!Yz0@_RS`0I#{jlCn^~_9a}N zVh6xRh<|L|Dd<8g`RdwDp~||(Izlv14Y0HBtq4+7@ig{<9l#UTnwMhWqu#LXT`%Kl zDC1uYGRwqvyDogSc3NaqcBwHfsSTilNAoCahVr5H{E?t^j8S@*g**Lc3VEd+F(>Z- z0HWslzvd+u6;k8tB=6UKbmq5fbzBZ=mlD3*KF)jt%4ft@;Bmee{prlL=cna&6+GO3 zX}25gZ3MV?J9HJ>;jHbBe#YASeBG7^vOLV? ztWtBaue_L?!b(!0{0R*-+8H2FHnQCSBz5>|6u#HtZpZk5A0(#x^~rHg+i>mm@d?kz z{%T}5pu1L8$4yVhUXZVDELWUrH?~QyV#NVpt#wtE_F5?D=-<(4_}A`s_D5}+D%ZME zmwe*<{^ave5f_JEj-s?Y`>)TA`3Aq$^#}1fK(o2Wv;a4SZ*j;hGjbC5h+IE48)kM} zWvUa?F-(dscY9@NAKTzIP|6xs)omTdsy$75YfFi`bI~XwqHpgvOLm|gnyjo$UHzh{*1s36KZbd;SO38^aR^FhJrkxcB z8n=g&-+eQnYg!Lb7k4=4jdA`X!!200D;+W0j@nf{-q4lEeQ5>97xO1xPR->czqxde zE9jRR9&U$ePyDiy@c{{W^$w(XY7jAbmkv`JlC z?+FHk1Fo^e^N`s)baC!KCBZq@47tfPM{zM@0r8;w*Dv!Qw371Pf4|~3-b2Uue#dyR zyO3hh)R!)7DpPKunJjzgaipawDhu4yf@#xT&ez@d!D`0{4Qo#YH3QMdhJ=H<2DO!zp++IlC zSqWGu=L*oTDdh33vA=z@-@jp{M}LZ%xfdGycK-m-IF4h-yuX__$p%%Stn-L(b|$1l z{lB`{xH}SjRHSU44NFh(>tD%F9DDx&a94?$T=t;lxZVE%kKA}CvSf(y<;Dc;YN*Rm6dTly zWpiJ*i`a!@aCH{r3HzwBxOe{mA}2-nSNIaTQN+J+Ul2Kh9QSc9cE{#B0SI#80qM-d zd8E2Nnx*oe@Yf{GdvlkzI>hRDuH(mj;N#b$$#C7>!8)qR_d&#XQ=R-c%d*CD8*HqP ziwFHd##w|(an~)0(GPOiW%iU*68b8I0DVF0t>>|`UPF{^w6T)MXlgWEZ}&+KHt&x~ zc?6(K5Znt+wHoR1?74IH^Ye4e%T_siX3J}prQ*OuR5wdZhOKpBuekPST$>&R~M*lsk*OL3-Y*n*-{L9p9O zj?sfq6XO0ywuGX|bf}S9FH37^nke&W4p8^dj-yU!gIl>PMXG7t^#kjq6)C<6)g6`g zg{1_i`IH9QuP>sqWwk*Tk3DUxKoe`%009Juc1md;T zOG14K-&K>=x+lhgd=~!z-Jx~3>2sOG=!iD+e3g9El&$J8-?R?xYhgT zS2i+hMGoAybyfN5HCCQ8qD7%SG)PdOodhV_$Dc7 z#PO(qu(MekEyIuA0!PXywAI%Kl+azCdb z3T?siT6<6U9VQveA72GA(eGg%DWS*sO{a};?0*NaIQs0k+e%{CWk_~Zk7%t+qTP6@ zN+7GLI$h1lmHUc|Z)zi8YI^CFDGAuGhNLECT4_z{nMjw2Ps@V)Y$YpmVp~}uE2i`$ zYTgI!HBc|@y+-Jw$#!eRJLRtKuiM69rkq0UY@ib7aHy*5=ti|M-m+N32+;Rf3GvcE z^7`sKBUr9#&O-E<60LG9*GP&|OlOclSPCfxDguNWS5UINf9Z8=w*suV{{STVy0znJ z3EZ`SRoS{wdsMBd-SE{`*AAg+%i4MsiB|o|S4{^@-dpR_J?i=zbbkq?+6j_ST_AdTHG#jA*jsZna(GIS%CstAtqz?RqoqrPj#* z0LrjNq*&h1Z0*swf~s=(8$0;+Ou*n(H{_2O?ue59L&Eu6xJ}(8Emjg#s80}TLJex) zcf)b6&&EB`idOnL2*casO;?ir!2P>j!y&rEOz33mv>gh8hm*YB!OO^3Z`WHHeXPS}+v?d$0X>%>v;D;L4lgI8m$xop({i@lTjf6)a$4eB z;@w5A^Co~=<|!?#l0TJ4W3ENXy|2k*YD;R--FlbXuiQQZkkO2IY=^PYSeX$@kcXK8 zNZYsrQ$zC_=LW4bE50&^R+@#Yf@F43^imehcVwE-decVt0o6&>#8G;?$YqtiEu}ES z3GQ}D2eBz0r0ETZ)X}9kEzhPk2|^_3v)D|Such`BK9`W7q@T=c9QN|C(1ku3W0X`B zZ@oVqL)=P!i`1*SyHc8x5>^#Z?XemTWl}fMd+sYq*nChf2eQXwH0az={6>RiQ6{tS zNsmK$qf~uqKMhDP)k1tO2%lv-qxAl*^nRj0K@Cc3TuKw*L7w#g0KNVc)Rwx5r?QL1 z0QaqF*KI6HqZmQzn7Mx(Vp+TwZTTgYi=1bL7Kd6&sy`F0e;Vgm98{t9@;SeH<^1E? zj6Qx79pp`TJ|He&^N-s54apNS<;Ro1vB+>ONVTjLv)*?A4x{+%+MeHj?C{{Z!Sn4anI(w2AHbssN>+_;V8SA!gnfVlizhZ|dPfmgM0X$o9*P@VgdHrsvm z>;5_J1|K~LFg#TD?z~N!#&RwjbXk#w%>(;}t^9j^>zMg}kM|p4b(CAwQ`}WhI*m=a z!VbZ7em(BwGtieR-+1B~##F>E-r$cJwYd7iKT1=q?d}%6muJU&hZm;O94bu)s;%U> z+QW~s+;UQaL0@lCtOiFOXj?_y@OixhiBSE@%H(oW?Gyo83LnQGa}ATaUE)4hyl@XO@jo`$q?lKJAjzzG8!|wulEt=r zZcIqn{{X~_U2M=+irZK&QHCyr>8j~`KP~A`LTU>7E&l+xca80kyo=)>B(TO(F;5|} zdp1Q9LtCLzWJgPjDN=fp4^Ni42OFQOPgT@8D>jz6bSvi@o*;p9v|l3CwBI5!@>YRG zCDf;SABZDb_~Q=*6^po)(aIhUwF|VEZzJUQUmkHjV!`DtobL^_Lb+I`Bn6aAwe}KM z-Pd$|K=s!dd$24yM!2* z9t*;vTTs)pZr0*fV>r0Yl8*WS1$W$?wbbpbC5CF9L0&FqZV7Dm>AZcvis4Q#CgqRc z?N_L+mM2=@k2VM&%O)by`b|fta(C8?9`O~+U*J?rsKoZJXNW(+bcgQGX=5YgUK;1v zO#>I3u&X@Dl2Ryz&VtfebbKrIe}=bQ+5qRaLH)sUt_w4xYoI6elDRI@mEF@~M*jew zv5}R!xjH8$&7l32hsV@dG+(Zqykd`xknc52qJS&$9W_L2Res}DyJxl5)$mZM{Piew zONaF-YS_N!(5}4>po6Uy=VXtXK=*C_=uJyzwfF<0Cq*8#HBBxm%9n-bnDzMz>^Avr zt!nfLD(zSQ05VRQw|3W2N6JPY9*gP&#B9A0UC6NV?PE?2-Yki(%3@kUUu`}LJvCz_ zRSP9-`Z|SOr)GOj1q13o@YP@fv?}rL0lF?n{o#0}8E+Bc+^qsIa^--_EZp8oYNG~V z?B1S9KsZfKW5n#J1fc@f4mP9F= z)kwM90;8|^DXl)&da!DaVlD3|I#=@SxUVa{#5e9FrE5t`&7G*U9@Tzejb)_ry>uwt zHm>7%YnHDj;jDSLU#yE`W)zhJ+(C|_k=^s4)~e28_`>5^%V8DMhs9ciN9{>;E4+P5 zQo8I0rMc8qlwXFV_*SPn{~cwi-E>e zC`}Ig@9AAh4k?n#l(766jmBG%fsX1DqT9*t1u6o)bm4x`o412=wNMs4=Lad^_t_S! zx>AiP;zS3;V9wX@htRu9Xe8mN9xYpAx3;_Q+j;ns_xoO#c7-c!X*2Cs;JObSB5OxPRQ2Q~u^IOZ!@osq-gDS$TvdPx zr)Vnw0B)_bk$R8^d=*C+pU}E3Dx5_pF5u}*kLpjvT}qSwpx87c_#IHRdbWQMwJrYu ztTf9s+p%~uczm$`#01qHeL;{Q`SRm7p^F~ zMO$IVZ7TOrpHPxLHFt8|(JBi`y*a|?WY$YEU2lJ-L{RnCB4$y`t1;V}6@}ut0`Z97 z>lKG9Z{x8VM2;+YDph~)ikZ6ir7Z&%mgCZ zZ5a#0(C8y?RoXALu5pVo$Gp3UURKg>@Flf2BTG;Uay+C4>H+ITC0d(})}dp0Bro*? zheElhWs{-Lq?z{t`{l4}vMgGa7B_Q68Z85@=7M zI-OugM8;UVs8bgW=5KM&lKg*=wCQPy62wW0uECi<(>46*rmo_nlU7p_%~Xf?EW_1m z<-1N&%ONoGcz~t!^iq*U56^R`4HQ?F?xCASj?5kdC_)GDQ5U0^C+-_4G*TN+$dJaofm{@C^gH)q|$?bj*Tc4 z(SIHG9w{vxY6A7>+#mp7dU;s-s_I?i$^l|dFfSLvkXD(dm)oGQOekH z#V??y=BHV^i!&Sd6|uCu^E)&gmY)qs{h#gu52qWS6YeS38M;KY_{xtcDobBF7 zi@VY87>bRu?`BdSjVUIMCBzznDe$JPF7iyeh_otrFfX&0d0k6S-RqEf7lm8nYuX+c z`(!`cNxAnFp}>R`1s%@k;azeG_{-ToVQz(iMzO-JFSJZgv)jr{l1p#r^Lnt9b5Rx+v% zoiyW7ye-C*n^$zSeK*vnMc|vbdJw}w))hxP!NB6svamvA8O4MnV zu%vGVMU!NiPxq-+2cmSBHl;fm)djM5{3tYgD2?vb6_D)`Xesd1L??Enb$2*>M&iAH z9VDU&Ry!f4f{3Bfq`B2Z-u|H={#s2+5sgw?iJ|%E6(j|;c}~NW%$8Ny>~$q=FW{t_ zjXdB8p+*f2S9AP8^A~{Tal`n(I$fB@?-q8HmOb>Ofa< z;#|tWTY7gRP^*_Ua({_(@3Ze1#kJehi*TwtZL!$Wh&8Ds0Zm-sY;rAaB|0phAQ z@znPU4HZ#!R%xUVK`N-*K?HpOr4ZAwJDzM*jf_;5D`k0jmu8OPq{S znq|O*dqG3Ak@W4?u9q7}R8IvcAqQje>858yQO_HpH#_(#eG1ZqPGg_LUSHZM3%>oT%kS1l-r}WqDejS zn@vB3bl ztsRn%A;fh^()=U1OSwyxKbLJ$gU+pL9??u~}~s zE)%)7*6@a}bN1+Fil`e@mlP;8a%MYiPyYa@6#Yn*!^A!-nSHq2ul_~ELL|DSzJ_Bp z+=VFDwySXT`020q&K(+^W*L0~qwPup0-7&UTJ;g9}C{AVV~`3DHgFPR1U>Rn9hl9>Uv{{V8Lw-Sn^0b1@f zwmx0G6I1#%(5pFQ<<2mHaDFaM9qp9$32YG_g;$<*bH;92 z~@DquHWH0hqxDxk&^hl2lImj^|$u8YrK9c5Xg$pL}6!@OO)^4PUL< zwb^&XHm1W;+FHH8aWo|@TdIjb0DAP(JMyDOjTEQa;z8WwQi=Hk=Z}Z*=wESXKh<7sA)=$oPs^vz!E+w>d7Y5m?->QwLZ#g= zl3$l`kfKRj(p)aA@AFpL^ek5Fh9<_|jTHPBk8kf}x}DO$S2&V>^o^yaR^(N5_|r}X zX;f9!T-l8O0Mpz-iP{@Sv@Sr&baW)GHLjc76^lh0ph*OG17BL5IT1BcWY8-~ol5@0 z<2MypQa!h_Su-xukTxVKWeBh2bzUS6oxxwq?@rNx_f-?QU8MEZj72_2Ps*9i2R>rEu$G$_Z$aty6xT3dIeN?mDukkvR)49q+8?m8un}_`V;yx(gOh1G# zA8yVx=8L92+v4TQ`2>jxzFlNXbf~!Jw+Y_GC%7b0WK(Jmn&%SxXL4@cbS@m;rh}mT z*H5$eO4*j}wKmOnYUpZ$^7kY~zZAhS8+C>@AqGSQPdKD8NU)VLMc~tQ-?bq?4Q2hj zwtTm8O!K>430*hcyYU5t7R+d9+6@BdwlQdO^OD-FjFP9O$z`UJM^(7sNFU>_!FeSZ zSDb;StMmZyro)Kiwj2i&yxeZ_1*+Qt3$a0#U_g7hfEDhzm7sT2{B@T90A9wzWP5im zdvB4Jd;Nj?e)WECPGNjqbGhbMtM*xRfo*6j0pob{({W+d!!N-yE zuxtwBH=kw^##g`8bvg}IZp=R5*akCyhmap0GTY` zc0bwaTGC%Q3>x)Zw=?^aw{)#*ElZ?2ew8Tj?mc7j4&s>^$AVDc42>=<&kZH8k99-` zR<}~v2A>mOQK~$}%-G^Sp6H#cVaM=Rx!B9EEL-OB14M_;e{FyIel3|CWyugQJKJ)X z@8IsM_lR{560{{D3Iz&JO3;mLc(=V1#=#K^em3$%9hMY_-z*hYk57cB`G7ny0lV$BzkH)ec~4`EMx@sS_^O68}Wg% zNp-imSA&N*o5y}gvRQ3c8+E&r@~lcYRz;Jr6ks@|8Woz7fRRcXopM~hXD-RzL9=&3 z-FmZ%@q7gKV7v$H8oRNgu^eS$;JkX^{z=8Q+eg z{kB?HZI^Ld{4==yu=OoN8}g!=QFV=xNN_>9F(EP3Q0QsoJKC|;C$O3ktcB(_Xg^a( zTdPcr7c?wzDS@5xCNsvi=I?nXLdCMkzVa>w#L%g++@z#eewu#~$8PVO1Z^g&2P5W~ z>{KQ5yb3O3a);fUgb`-H%;U=i?>)F-vJ{-1hNBp%ipy>UEq(7%Diquhb=t7L!`+eo zVjEH7ytT@|c38%Z){Jz4riOx=cOC0B9^pp@gS&!J@6-yxT5Kf+`M(< zOOy8ObKevA=J5O1sCQV>+bv0LFUVSy+R6=4^-v#8bS^vYTyn-m)MIeebPJO6uVAh% zWNF4u@ZN+HU7_njO@7@KW6VCvS@iopLz!Y6?QECFhuU0tcSz4QaE&5r``u~8mlBYM zluZ->B_n-p?0xvP4BBJqqU0C<0JMwd%j!ok15Tq}o%I^mRm)!`{08S=95RdDmg$Sk z^$Hyup=ftRm563axLcL`=BG(C5 zOPk4h<2Cz}l%RS;u0F7lxB*V7L;>*9A;+x*p-mp+eFbg$#07QxyoiP!#YEI~pq5Y0}Wb`TPHtl!zkdnt|!^& z*vsTKYMOD3CnK`3i0OdpoJi_wt-S{4Sp1$s7rMGG$Bn_<#>#b1wyZfbVY2P5^%p-% zLJ9u>EmeJdBe+*F;pgy6u(7;_#RMB;p=5O^Bju()+C$=`$Ah22IvK#P1GU+XH*Re| zsJh|@`*jLj%wO6nt|H@)0$k@$>F;DNZhQa=HG@jxa=RS?q^%708YC_g<*WDjpt zCAnj|1*|own%8eZr6h#qilhPwKUc$5moz0=+c<>bj=r1fsr^-iMJ&f~)g){WT{9z7 zj5;OA>+S$kq3fu!DNPzBNXsLAG&-kPQlnqZ`Ds1V?Mrs$tKLA}R6R8UZizv~d>;P* z-Jxw%e=@vrx>ADW$5#Xe0s565^vR`uUg!Ah)E?7+{Wae){MQiY!~X!37tLRe61zDH z=Lvg$_7dtV)S*FN?bgM|QnFHxipu3t%VouM>;8JKdY5A3y+C)hA8~ctbExktP{+|W z*p?Z2Dqq$I>QBp2)r(!KT)E7K>JV8kN9L$4op}2o-}9fE^cAuH02oRRIzyD) zAbR>!M!{6KOQJ-*+-KTtHyee%)aCM2Hk9KQmX?$|AgB2dS3y(hT}_4-ld6S1NJ`lC z%0=GIZn(;}uF)=QNnD#_q$$=EMM@M?wMWZQ<}`g1jEuA*Sx!@J#MrdWy7wY2%Ha;) zhXANUvz`&#D)=Y8Koj%Rw$N{?k8=H*l-uN-^;~@i8oH{|yPdY@c!_Vn%1Kvkx+IR^ zsY?h-iAoAUKaQ=j7_l&QO$b%FOqqbRflyR~5`0UXc4u1UqGTBWDm;`BQna5?bx|y4 zH_=aI!TN+T71XA>s!N+xRJeAj>*=QfQ2ivp%kdeP2cq6Bcekw%!zEJSpar!pwC~y4 ze@d(BHK@a7z#fypq3*IAhXnV3nlfrTDDMc#oy-3Kx~?6jRf_Lvbxg}r(LC(dcWlsQA^GBT$z@MYS8<_zN$LSRR)bV` zuMUtB+68-N*T(??+nk#5W@c^4utssneX}H}-pv#oLmyJYTS+}>P0p^Sx3=`E1q#hu z-`)EAPemulJ*(|cLJ3aY2C}batD+ZFBK;6Q$3znVCjLa^>wACQU=>Jd6fz-=$RFxW zKg(B^F~=#|R#QE5HMgC;5?z;)Y!iQ??62BN*f`UhP@jc#{m&ego@R=L3k~iyjP+yA z*97^GcWuxV!=colP(p|JY6GBrdN@&PZSrwSD#rp5#9mMo66Gj%t#pFT6rpq~iQlYP zzF%#TYsjq8Z4rY^#g8G(>Gg)OxK(MXV*rDO3m(elm-h|ERnSUd-H zqL08m;N;w1VP~0*#v!KeznZjJUy~L~6Six%H*{`mSs+M@71P}vP8v}?!}OC;tOgI; zY3GgJvuU+=Zb$E3wX5TGySI9ZZ^yZHtDFYg%kg=5!H|@qORq4c!&;O&g`^SMetN=R z@utHe^rWc^tztQJ(!@TdIs!VU0x;;)m2JA=`3bT_jIQAtF-x&tO=?n8>DSXu#di2X zY^|!bj?M|ld@rZLJpJj-C1zxBmm514_X_~(hT*Mysx(?_c^uZz^+u4ix`5(TQQf~` z>!?&u2P{{1#UK&!_~`bOD!1<``l^0oNK%LkJ;iB3rA98PMiTFGLG{~4h^#4Pz(Gh! zJ#?m$6Ea2$DC|&5l+mR=I$x|xBN8C%Z!}3Gy1H?aD6>R^`bVKb(~gxwHsxv*zTj)? z@Y9~MY{I=O3fPK+*QT9izzPhu)kql*E01L`Ubo)ujLwMqNLx-J3LB62tF1N$m$)-Tf%I!OcDg@-G{?a#hm+XR&*B{V7ddTU#MhGWL&Y6|FlC*E-Q!(M#DMNM-)9+44lgB3u3tG8H7 z+${+dUXSBm;<7+BRnsHmNi2nwFj7a$SnP%X9?P=fIm1aBs6O|HOG1lJ$M25lQY0lp zprL zA*8qkNbX4PACA0LcWUb_+>nt#PM;<0M_)Gc4IRUE@%i^(IT^bG_r&&Fkx#iI#VyG0 zIlWSqf&kysTfoh(Sxbil7%QSbZQJ`LO1?X2xn-u=G0PpD4VMbu zoiVg~O(V4c0pC(c0B>D(SLL8FHPz}DfFR+wrtV09BVi+<>{VOm?k~P@M>cYI4`mGo zvQ8(z%dzA!P-@tbAGK9Usn~@jN#CK=umhMJJF81(z6b1oWp;V4K;_@Hmk4BRpvcKQ ze9Puz_vej!(QUX@QNbs2D6}1P8|E4DEq`{=b8A@y8OO6g5A^>4qU0kLN>$u_Y5X;c ziEbWhoX`SfK$96nlAuaP{b{K(YSjg&Y>AY#ZbdiNece`-y%1i%0jHWMmcde_%zb6p z?Vo_(Q6$jSSh4j9%W{MJ%WKG3?y3zxA*O>0bhk}abCQqSO;9QA?O6psD(HeOm1^z( z0ET>~r!8D{m&uZ#s|&x_auxSf{@wKe(v`oVrmb3cHDvD~43~24Y=@h5mR?HFX-!2v zK{c+c+}f1;9l1puv5Z=IM~gGAJG+#*A1Jp-iiEC*wbdZ@9sUPWEP(|zdINRtzFIte zoimKN)fm0uYKrnE$+TVCI}drKmW6BaP^kQMpt3YHOUwTNmfSpjtN#Eiu@?lh0vyYn zgrC|#X>UnT^h0RxC-@CoR4hYVQ$+%J_2>Tp4`w-nc-Jt;m50Z=$y9t_Gj+r&Bd&M$ zG#{-CN_75{(z_j1MSm0CKw68gC36g0^gYNONBujliQVlc5eWegI+HAI|gX3MBk`>e2y5(PMHFnI8R;X z-P_bwn|?ZC2Nt@i$>9$aOSjCY_m?PF7=*VXB9OM%(1oP;1N?PmCCgk?OM6n#Lh4VM zFj(bp54cyCt>H3cTA7NAoPqj*T0NzI1L>;SO&?DcKGm8oH!>;wbWI886lGlq`Dx9m z4Rs0)hZM@Hg%qDcG#KIvT#N*2l#R|!C|*(g&ZPQ3JEpW~uR3FC!l7i<#j-NN3own-njib)AteQWd6*7r||+A92xGCP*g z^;U6wm5OIjhwg$HxHyVB()LnGuS)gR$+&E-q3W@&#!YVTC^S`nGPS#ExDv}s{gb5o zF3^OL_|sH(7S4?P3e)0l#Eqfqlkxk!+f3G4&$o}23Zvv1I>ZP*bqp<1shJQ>I>m?N3~CmG9xyGSlihLn<4 z#VS+}uTGkcb&PmzLQWfzHL`7YLb(o-q^U@8B@0QSl0^t3Vh*XAS49L7P=`;hjJBx` zTj)8uB0;C9(S)LfBV+5MAs`f8x_5T!I8HbkBYxk{Qd4T~Z>u#`stIGae!X=F>QuHm ziMe!rVIj8CTy=j-O^wM>=mwd%oJ8HnqIMQKS5cy+NcImDvb?_9FL&#x$C-F{F&DGB z!jrh}?xicPe|gUt9GhAyy0#zM=gQ5}9kmK6nd9a{#$g{8hjB#4UrsPILt-)3|O`9oB#<&PT*% zu>iQL!6ji;6yM>kIi(g^pp+yiz|qBywbM*(PZc3wAi zirSWA7aA2l;y$53ep)hUQ&GO9BDS7@rj*k}>XzX|V5AXBQ%6Kgz}WnBTTov1C*h&B z1-q*46a&*lQN)uK^>0d_Q=(ECqOe{+V*dc$vOn%J8w!sOos|`;tE)_ORaCjVsxpCJ z+=mvo-YtrbS_)Vj6Hb6?_00veSlFi_)m3u$hB;}JzipD1%8fSkuA>;0zt$IOlhYY! zDM=e0>89M$mgm(Lnrqp=EAfGZ!r`u1U8UVIH zNxz}GoVcy6v;mDt%_h5a(=8{ul*s6m*=!nr?%Pd0#Pw1GYNcK_#N5cQ3>7q07CEbm zr&=2N>b`r5jiai%vVb>Itu*HwOOb6b{{W_l^bUbfPffLXiL>;@O3P>O8u!pA6HwOL z0Ql2a4|>nX7jlIRQ>d<+XsT~ic>o|l-@lecDtFKZidS^1!$PCwrX3{;chX9$d6-eu zwD4%?AY zrzx#dJ(O#OBh#>qHol3DZIv0na-TtMsgW_&#fsyOUvp{$m!7&KlQj{mZ3Z*Xckcs=M{mM%9G$QW-nERDP?)`bi%B=2Plf`ih@$*YT;+ zg{uc-hf3u&SgsPWj5_ObktJ%IcSo@gy=o)_)QX)Sro-7Z>xXozilP})B({Y*;?&}( zcP6DrPBJ!u(MKzu@g+HCqT2%bB*a)h6r=~SMi+##oU2sqq{3bio-CC#tf7?)x(dv<={fb5qAI`=Gr`D(i2QCcZhtsqrENNcoEY<+bUw?!?_s@OMQ)%j?sn_8)^ z{gwj9pfoi8I(e_4KvflOkcE;G1$Nj0O*9=u9a4-P>GIQVZBS^U%_$l;1%=9+9wTC| zKC~bw=A{9p7HEJKD(3SVj0Ul*AT$=(5!+G+q3jjYQMFAbpCw;PR_p0+Vx^iV({r%Y z($E;A#y3a;vwl;lHaGa+4M6P}&OHSVHU5>ppKy&)VTP4VZ1S8m07Q~7%(NqAHOsCM?dQV1T0+o;vE z01@c2GRS7XC$mKrWV71uxmDiRj$+n{85Ti$c@gaP7;P>2Qsg!fBLi+ZtLZFLR23!SQ* z!)Z_y-h}E6-z%`V#z^aVTJf~>a6iFlaNIW~7FHI|3z=6AppW9EzbtZ>FPxDvvandU zAraVUbV3;(+Cco1tarCohAUe4I&Y2pHc_ZhK^S}Q}*kdvb_$!x-VfJJQb<`0NF<$*jkHu z%yXla+ilJVSet!vBMldk(6-xYKvg<}O=D!_Sxes|7)4iXkA-pEQ~gO^+K&{=8@(QZ2DXFHtOP$1X=?^rC`$i34qpuekpJwV6xD)VJ|j4rlv>CP@x*<(y}ivh`q?&xSZx8(D2kw<1PwZjR%QATUBp0!ay?*WKJUSFhu)CC>1|#e}|M z_Z91JXE`kSOO36xY%#QT`v9-+mwsn)2ZtE8KY4cE-Qys7Y-s-g`#y{tUBT7}QE|cE z;0Q`We^Q3$@z#5W@EhE->q6EZ?5{PIRHNu=BR&qkjYogYD;U-QoqE16*+My z-Mhtm=00&etB+@Kt~`fS&8`pa5jGKwqg0h8qG`U$8;b9&W<%M0wY#@5`-tg6x-TF7 z+wq&o{X2-vZ9n%9`lP(?hJMrDTf(}`jefA(Gi$4#^A`wm7h#;2dZk3P8wQ@cQ2Obc zY_~7PI)UY-ji+kI9Z!^Exw9xdRSgvLJeY2hhoJ5V>-l5Lx_v*NB z%w80ECBs<_!ye4|rsdggWXf{+0hcFRT#ldA;v_PfQ9hu6N5Y!!_)cey$i^QV`t~{% z=N?1EGFW+xcMLTh$C!R2`O#JRc9pgY5|UR>f!4lAKDg~T`cj!(&w50t{HS5Ll`RK*e;CXKW$EGi-_B^-dUM$ z;){LVNfs# z)cy*?@dt&t-L6a1@3G&x_O6lIc8N-m4(GIdbzhbDCXJ+e6;*}y%4?VIxzrBbmrQu+ zNx&hq0>;^d!cvr`IzRz5KOwF+nz%QQtGlmCaafyYRn;P|tVV zAI=tcr{|{IhlCo@IQ-?o?ubdBb&#(4a-4hHBpFe_U={M zRk`0rV@|^F6zhQK9luv~dLPSCF0~5I)rHLap>vA=Ccq0Egxn20aVt% zJ^JW9-I27>9(L<(hS@VN4MkZav9+xsM4vz_T{r2t^hz@U)ew!yP}3+zwpyj%WYmC$ z7+6I>{{ULTQ~o-P=udR0=c&*yieLM!VrDlEV(}v}$#Gx$cGD)ql-lg?7ttY79R1WO zN(wECB&0S?E3ZcTMR3z*_SeC2KXP3oZ)4LvuBRHZvAdn~0R)Rh&}R2f*>EgVmR=&>;R z?E}Ow*+Ujlw_!MpCAaOW9j@BJN&ZsYM$kw2Ysfe6-7!BE-t5~*fkUZ5Yb7h|)M#6T z+l@*FTG_xo}d^W#gQKb;B+65l%wmO{?YIICY;2Tnutf+KaVE*CH+&mgUT(L?x zs&2iu-%(|6nLiZ1K%w6y)d{W^%%#hm)vZ?cODuEXV{1tIOKNR?@cEdaQl_bL=#N$pu9rAX>N zM^fX+O42paQca79)Pbt2oP~mN-J0c=WSB+!``kN}RjS#Hysu@*vVu;@b8t{kO{z84 zxc+|`HrVbPPm1E)!;WTQ50=r@C!kUP02cEJu>Szk(K%8)i=QQ3W4v$SFx`&Lu?)CU zp7XkQ6dl#yR~f!570EwL;jna6d|#V7$&GE}9qNB^p`}&5u-0dB?Wo zyvZJ)B@RlFm}2fFP+xH|Sp6&ARZr!uOPgtP`ltz9mbq+sCN~|SIC1`H4=h5wGMAJ# z{lNNEDYsslrO0t0(OC6boCm!Q4)muQsd_$O%fY2u=6jAVg;7*TaQ+8 zIR0A|!$D({t`=6FVZk4{F^^?X7j3@V*u7fh1qbc&TSLt# zE3eY9K+`tYcNWgw6No8{mL|&inV6^kf7v9sU^s)yq(gd zg%YIi=nl7-Pjv6?-ATN_=(!&$_74|$z-|1F2BBE2CLPk-i`uLiBGWc~wS+MyVntei zumpusRq6)0rh4JB-5F!jrFPCaVbk1wZl>B_)`POTifS@*(h0qCwcV8B zWm;|pg|;ZDtD>4&`HEC(=Gu7n(1l%b8>>)&i+7mQ-?zP6)e*x^*)3JZRrvaiB7AdQ zs|q;po_|nFsajrdy_E+LdMzXos_9{KI;l-~D_-IcYJZomqWXzza^Y9%B|09W6khj^ zmC~B(p+&h-I}=?j6rG_2 zwADj&G+3irYD`ZmA%zvWS#5V{?#7=5xlFtYT|IzH8;BuM=6J=G z^hP=3YnpsQpxo{D{{Z5SRf#r5veukx_OMimvC@#0Dw}sEo9olQ-u~vYnBXIv?~mfV zAMW?;PYIPSpB_G*Zx-oanCQ4qOd6-@Yl0= zkG!}%%LN=7^VqMRJjdD&Wy7XEyhcbz_cRly#D7YzPv*P=>n=+Rg0_o&z_zx?AdaBc zl-BC!9>pI~jyZj^F7uVPvX7btWlk`bQ*)acDPhM4 zVp5eT1c0h^_14Yyt%-qyvgVOrzDZ1u>f){Mhs$e}JZSSDf|_sl6X)a6vaEfMq{y@+ zrOw*=fQ0K!hLpb^_>YRna@Grb_@^D$A<_Q;5oA>Rlqs)WWZKUPo;^NvHAEW7sOMqIE(`Isr&xeqzpdHQQSDsL*O0tSZvf zXr)F+y&jtYQ$jx~Xl+kQ)mpN6<~_X7WyBOJpx1r%WX5i76$4!6KXn%-{DJ#_WcZBvx6T}Sd4?|WTfg+%t3oa? z&{S2}g(!r)l#PbrTY8;julVbjNoJ$N?e_Stsfgxr%SOwg@%w$g3#q;pXZbfHc=KRu zHj64IZO5;i57>iETkDKBOI3TQTm4ZqWY#PX7RU$7ikMWy$o& zEjo&PRgIqwYAaGj52xd$v9v0*NVxOn9tZybPiKwan_#sMA_Z%6x9Jb3W4POWNu8m= zOvg#)qOKTx)kV2QJqLejr_WGgG;*`JXrjBkUvwB(KJhM4^1Cfug${N#b#UyHB)9wc zg-7veX&*DGHuVw@0;^?+zSY2WZ4dG~edF*^$J{#sHojKGvqqZjhn`zrwoGtz{q3~- z&Hida)%-OJF$fZA-Cl6}vn9E2cAEbHBm9&F;g(jcQ=Kf3WUtzZlA-7l5_&0prPk~D zr2haNBMTs7=u_On+ts(I?7Qn}bR+i90a_>%_*YuUs<^y>0=co_tn~2*+^>`HIm7n0 z-a>L)ae|0XyZe<1?G%Ht-D=hWU48PW4v`;?c@=P3{{We} zS}qO7aU!Y|)}(b=EY5E4Q&jf57UZ+0=5r$V0h>bYqMz&ABe$F$)A#1s{b| zL#?HOs9d`%7Sf@vQQ$sI&NF%QFf1lr{v4y+Ut}RIg%iKjvXSemjGcqzf0bx3Ioo)m zqs7@$y7M9{;2|p9kkh2}`D(7)A3SpO?zA^~n>k@4aM$LW z{QGB;%&0dB5my#kN3}9K(o&>2or*|4pz3~Iy$Y-27a)?}%DM$~I;0C?d%lh4y`Es)wNN8WEBn6Ye!( zU7>Tw+*>T+3j~PlwqJ4*7G+c@mkU8v3jB4{Wy7*dI{X&_i;zmo^3meC8I{;y#PTbh zk@^)taHS*~f|~4g%^blSh+VeMZ&ol$lsL-j<98SnqdHliY;ZWksXKy$QT{rxw~(yX zUJ4p`u9^?Ioxyf5kmu$qPxofpH01mVhxf@e( zMyRk{b`6`HHCmoi%Px2AjQM^EufTUcM!NC@8@28CBwAuCzU2GIVv(`y(^E4z$V?;v ztGNDRVqP0V98alFYuDs1k)@|w^CzpXV5h1<1Kp5aO= zJ&;@=Tqe4!9l18xl_91^>I3mQp9m@opRTE{rsuXwh*!WaS&?RnacwrZtw0r%s z5K`pEmiukUh_9$AD_3dQ{x#EJkab10c2ftD9B|he<`$@tl$A%nKyGSV0FP{_(1JcY z>B8Vs0lFwBMv{yqg$mFEqN-?xqhoVX)7L>Q$eYTcRevodJsJ)>3zGY6HY&Fb0MUU| zlzH4^4<(5)93r<& zhmltnNhg5xVs6C)x!Sm0xjrJiheIFcBiI}MuA($+RVJV{Ticz{e^>&E292lW1pxm54Iren*F?eQTlmKC%U%!` z8oT7nMC<72v@oCeO`rVKtu)n3ozXhj=qRF_RK(dalE@)4Zf3s^pnS%rx*C)nB@&(} z<57g0i^M5lRkWH{xarU+nq-wVOL)A-?ceoZBHT}OOg=Msc_NpQg4=bV|M1;!6-fK zTU=@*J-q@Vw3g^UI_j?&-|+&jxh$KmH0-NN)KfuG^>o#s0EJ4;6^h9iO`CxD;s7NN z-lb2T=%(c*eW@q-=@#OyDh^N)MOSVpp!X6eQVOg1Z>eSV38=IR_j8lUu<>^W;A7}E zn=qL%zLkDp2B-XWImc@NSqvC1kv#T_iWRrP=4HlRHs(EC!%(|?kGQ8fd1aR3JZ;EvZATQcH!A5i z;39`-84+1DQGI5q1f9Se>wsT-QESDl9AI%iqq~*reh2RT-z>_3JZ&D6NziyMG;;Li zPtI})A$`d&xlfnYcGrI5mj0caeoN23;N1ywk`dy%N%mpmG-m!-*e;JF`WVa^Q);15 zRGL$+o8%3wJ5P1aE~haK!>C_zcCRq9OG`%@m4V92V_A|Jiy@bPYxSTG)R3wI2ji@F z{{UrhO3mQvRz2SS+*`xWNh@E9nySf6q2Z`--#AH6qu0|fDhIdU9aqmM4RCECi-S}ZNxY zY04eV0U+wnA3jpgRyv5YI9w)Bfg?8$nq@iR&xp4>G__g0ddTxz8?KMvVo{xSxnAk| zhrA`Yj^ui5s%q{YW0#7%rKMFfy`AFI&@$LK1c#P!w+wLIlHYvBd@7Za<*98g#m6B; zg!RVB4HQB{o$6a*R0N7vyXr-TQs(Uc0Gi~Fy+z9Ld`=t>r?L+D0iviG=aZcAie(Wm zyhGyVOKsyTOHQ|&T5YX65m$x208ImE?(bnjqqAdL9dul05yH&@^{%S*!}DI@@*8Z0 zZ0rb&zfa-SM%ho2K0|+U!dbQW%an1wqc(jp+r(zt4dEB3bC~h~dII&xiZ-}Mw-Zq8Bc@boUh89xdyIfKM2|c2_dh1E;&pvDJewH>JzAKyi z^X(+CP|D0oK}nUhM%CT@gvp{Bn9_Y$sNH}{v~tS2KexWB{pu`C_d ze{Pl`zRD#q>Kp7rgKanMuX=X?pg`$eXQcK@Y2FrL@yN;jd~ELPAGMYq;A8EFxqv zjR%7A7D5ZW&CHimxNjt#deEAI_jFARFUQ=kI=kB}HaQU;op)2M#Gfhf#ALX`YH>wt zRY>RuPP&nIp0;)|zHSi4L+BXdFrRd#VpY zldaA+%MkYNV?aLz#`C#OOKknJz0L%fJ ziVxEDnvxs)kJQep((um`=6H-|FIZ)!9isgDlihm#qE#g+>qgYDdq<^4xtA!;MJYSq zP`gh9z+b@7Vr>cTx=H_M6!o`YZxQUYl8~dYB7(mf9YKCgrPWq2@z*fYiNL-yxItx8WfqX%QMd(1 zr$MEEvun))arh@N)TG4Q)Y{5@vQmS(MSALYtQ-?9iNNZWVHePnp&8F%vI5)*Q3)rY zBo3!h=5u^hxFMIor-i;lqqdDG*KkhTYNl8RiqzVd)4F}y{{VFTN)n_5`qSmAsX(gJ z+3q1r7<*72ki!$wrL^Hv6j4s_s%&@p>py)f!SbzkA&fdXE@V<0hiYQaA_`G5JIgIf zpbJw+aot0x)@^%@O0ddL6I-+mLTE=Is2_%`3EQHfW4otKzJ*CWDeI_G60I%d+@0`^ zDPNAF%B`WAiPsTettP#C>b_A`Wf9dpAfDop*W;=?fUfP_6Sf;9_UuVLH3D}~sS&44 zIEo-uG_IjMnyCO(rIw+9ci0^z&!TifQI*=_I;6PiB}4S2DL{XL)NuNhC6RYPqQPWC zrR3Wh_tZgCr2$GR9<^0YmK@@Tq}1-Vgf_ILC`v%DZ2%P064r_M+84}c{n)%t#s2`p z9vkO>p`>tDbrBbtD*i}0Ojj&HMMi-sOYb&RPSn>{_Jb(Mu3sDU1TJgtCNf!K4v(+# z16q9aU!TtlWEP2B&yW7zJEI6>9;wpPkNgn`pVRBm5YE~}`1Ek34 zDh8_0f7|E5wr?kX77aejMEydBQ_U#<0OhByxyQJf zCtKr=&39JHkQ$9Z&$+Q(!gar#i1)ia7kVO~T3)prN&Ewx_gK zlu-+HK;5jCyoS+mvR+~dwyUcyN4Y{$l#t^t4%H^8Ac~KH)QBW4Dm1W%4OW&rTr6(S zZ?f52{iUwV$|KvM6)D8F%VV_&?pY^c@j7->xYahwv``@~aG+$i$(Edo*&gp~oY`vO zZIIGlVdN|MREGfsemW4y<~pOQ5->>Jt`!T*O{T?SjJVKYq7-)m6YV4tkdzM8qyt(2 zHUm-~OP#o#Q*5_u2GP-L4r`rdTQQYO)bN#AO%z7_+7oLX%V0a@X8!Q+DhK{Qh`CW z3aPbqP+Zb!w@LEos-{8$lD(8Y2s>#J z&gL3~%#eqI9jc1*Y*s`x%evbsDjO*)kpXln2pgW!>#b%pG}%CFv@4bJE;9J*Bx0ku zQ49DEEta-d8MWDNPLAau3VuIM+S5mzp3kqDtC`<$8w`=C8zfQ1_Y35FHm}-RUFHi* z9gebUw5Gr58_*4E@w|f_cFnR-70&q&3(n#lz6!ac!7gI_zwNw>Rq{KL<{*@>+(}Bd z{{YRZ?3WP_^F%>c+*jSXBX_5C($YLau~s{qHz>%P0zgw{oykcCzallyudJkn^l)B~ z!R9URq7RMLPL?J}NfZ?xpAAMJ9TtK*(xER!nB0Vf_x3N;9-!*FXth&q?oV{F9&?BR zFDJbts>Y(2%9z=LQ$vxTN=t5qFZ~BixrM59PHT|pc}XNFQoRP6+z(WBAvH3*$YKpM zGF75f_zN_bE-Pi?z^y8zfK5JQR#@wHzp0|Ey?JK6QuT`?v}6%mmu--lOuCU%N+?uT zg(}=>xnp!r)1rP0fgnd+w>BLw!C8p;-BkSw{8OYEtJO)`7U&MCAuhH9!}TdYr0OJ) zhjdMeP()CVr)@+af}U7vNKo8-G?Z%_QoFk8Z0dp+xaOwwie(Z; zBDX6{E2L#cnGq7uy^Cu|QcvOzt}(fpt=(3dDm>N_+UhdN7#t6ZqI~J#mltw;X_DEo zwQjpCDM^&V64@nN{{SU5=#B~dipW?H$z8dh72>aVe`VM#!yPOP5zW*V>HHTEyovj1 z=9rphPq5lmc(nx*I}`m>f=y4YM!kc={pQ7Gni@!Jiuuvby@KVuN#)_DXorB*59X?G zi5O#i`0A=}kGG{^yz`E@>dP%W`;cD*&?qgB!R$Brbra&y)zGi+rJ+mOLjX4;`c zj;ZGx8v;cIN%(6W?H4PBmd}buQiW;1c3$6H9MpFZw{UpZpzzfp^Zx(=e}%e<=Why;Zji+d#tv9Gys1+P|iqs`K=w4fzXO; zqG-i%q4wMcq#FJ@j@N39&8piV1kCSA*~Fz){6?6#9%W}~O^LgvOK7CkJF#UR=Ycym zL8r*-zEZ8MMO4ddN>`Y4erYF2W2jNd9^kukod+*mpT*H)UJb7g=2%FTYnp(SArmMH(N%w_ zn$uS{m%Ca@#^kJbK9!=eE_iBYvVx5ARCOwzf>TG1y%e|Ohdxf_ zj5*|8<{`|M%%rt&XbKf1r|5Er`+>-$Gqh3;#it^2nSufU=#Yz?y)P~jz zjjN$4Bx&!c?3{|w>OIkvec#ybZj+7Kr@ zjK6Bgqr8$kgmok$O$uxO0H)%rqRcmRJBQ6Dh+GJ6%t!mQ{-2!}BHy^k#WNdTMQ(7S zMY2qJu@;j`r7KIcsQ8MFQ`?QNTf3MlbW<8G8FvY6gcZy`IvTw2w~@Rz(dOy^f{Ay@* zXKwB6)4IXjK>8w zYmrk&p#8M675ESWwLu~D?m=5o{(BNTg)I4jjw0cUTk?p%)%}}@T3!Qw*9Ae_@zlOS z0CZvFvSWwb(n7+kwtCvK&L70`!$&09dtyvbKT<)WMSl}bRb@8IYS#{-T43B@$UZK` zRw(45(^BUCSWvb@c$mvk8MnC0yTn8FFe1jWu z_h7mx6i$p$+{)9v72H8J+orAEblCcZnzO>XZia9+0-Ly!; zRqhd{-cZu0e{Z&>C0#+KKMiKuXje+?wmMsyNk}DYq#|C85k>;{M3jbC={g82>EH%# ziw`1Xn54M@7VOU4>n}2C+E&;EMu1N2$<3W`ZTB$4MzJ>b*TQWf@!3PX;4i^xsQFhYwe-nR(SE z!548wb>4$Qx+SzOoA|BB86H`I%)=zy)?2fMqA1^HifRC+w>WH**YmJ9Lyv4Y!Pdv8@lH!F5l1|#kU&31{E1_YeYoHWJ7&XYjzC{Ip(s^C3{u(Q1 zkmOlwVCGlBJ9%2%w&b%l00gZdrNB4T+>1P*g%=AoYSN!vJ>;b94g###sA{>uK%`|R zoyZ?L(`^-|QOy)87H5jKcW!Di$eg@+JQiVFL|9;&)O}}q^%`8)eyU=_5A>%}tCZ;8 z%f%#i(AJuQdaYzKR3M>%Wi*wIyM=#_iIv=yFX>Xn*3R0jMaI#(Q(sSptf!1^S$k`j zdh}3w{)q}n9i=4gs#Bwu)N0i>VV2=_x4gwFXng^$)Ya9s3l>`?DPxrU_w#>-p;b+1 zRF5%TsZzcJNbaPxDE3{#LlS-X>n*J&fk66xS`UmkYLv-KO+ppCvG&zT4Wdc~K9!^% znuaXt(O3+b@2Wd@<*Ta}m+f}6BG}vLC|a6NYKndp8g;fnYIRQ>!7Zb7P#8pujFmd! zD{*9%sQga-PKY`}^TGD~lMu2Gt zqIJDh*x)=W-;3I1@(x6Vwuq=w*i)W?JA=NXnyHPVu3WFm7hXfjCr@%p#jr273{K)w zlqAp}#Ogq34N=~8QFAiON(veQ$%7qKg=5N~`)Q4Eih_=w>7{A>H7Y4IQ6CWs$#4b> zWX-t_6~VRxcFP*XiLz)r2WQnoU&Bq^I1r{|T19H2?H51L{c9=azzrcE2>udD0Z@ocABW5_0vKADB38O?a)kzQjiLdqG_n|ya23i zW877k;><*u0^>2NvmAFPZHejYs4=w22Y+-luF^-!pwgPx#Ny_Hv$rv|q=2d|)#+Jw zVu;M7zNrp`HjcDSXmuzZRA|U+DxcZJrrDhm$jJshr2)Rw^!F*hO*%*cFx^UxF0(zw zw!%F+`tQ?9ETF15+Mmx_F0Er$Ycwlli83x^$&{9ub$>`SNIj`P9Y^&7SYasaJ}2ax zi)lCEb~&s01$#wXm3KLO6G?_1>PKyWkDYYBI*FDv}c;$Oschs+}_L7>DQ@?ilY-J+@K%|=o z9SKD_LyNHvB*3IXhn}%_yxy1C%3~x~u{BcbLUg9xhxSqBvujUUp?FJ)avNqO?a~a3 zbVl+zJNv0}18$c62WSR^*RGHy+I2{DPO6w7ar}rATAy*wEPm{DrERhOM^RLo?kX$O zPs=k(VX)AqK1;$Fj%Sxn%d<{wVfi;TrA|bT0bPxy0*OKaS)c=L0WeR=qEmm0I1Qw* zC+63?sEz_q+*s^1jkc%Ol0|f%T!hP*JEQR|UNqXO)Vmf_dO!qGGg)9)@d+AGbIDCT z?P$?dj(W^-mSac2IR4!IpBh8oytu}a{{ZeJt>P$vUr{E$6aqv-}DO}Zc&oY~WmENgSr7yqneP10!wrLRxCi0ICoT$wpl^Skqx8tiA zdKDHCQmpql7VY@=6xdQ}{i?wnx`F<=3M)Uxns(KlN`aF#a0;=*iY_-OFq{N8Dnl(O z{G4?tKj*1&in&(GcEn1xzDr$Oo1cEkg_T9KK!XxE^Z}*?K>j+PDX5~R!pF6Ql@jsJ zAChLdWD90BkIlbJs+Q|e;mVd;Y$Trd3})6FPTMJT1npf@axqW$Ffi`sx-jY=NV|E-9Iq%XIf1&Cr)JvEZ$_iv6Msqyi$X~=e=eMxfv0EjkDP(m7q zn##wf$w5LKj{2Kz0l+FsP(iQ5P`LNGw^mg02T@_Ux3SiFbEC@%@KDnqZJtBGuyMzH zWy}b+@%{2zZaMQRVN1VV=O%?UxcB>rMH4^)>9)17`@v~Dw@U!tx#YjL+=a6QR`&!) z)YGXyH9c|n68RO!a%^y+=^7kzQ)sTDPXyCvsX+ zi$TS0(F**vzQy}v$1WRK=B<7qa?U~fgXUI`q?Y?AX{#Mi{?7jZFWhO?CB-T2Eh&c(rqYwWLQ*#*>fxT- z;=LWBF;Bfvx85F29Q*#M)g!=udVfV5W!%?sBGF@JTK$+$njYjO zdlZwuZ>OfOa9nQ6$-m=h9~F+~oX0VjcKM_(r$O8Psc(#O{{R}TR{V+&H*iGWTDW&1 z&O5)tzsRw!)U@{%CQEFd*$5_t6Sx%=)EuTB=1JPNn8n3Y9=%*6zjl5@30ZNNu8B+SQ*9a)*Wfi9{{T~Dk1l(O zuVni@$GeGvv9+MLy()el2!3PpYsp@Fw86&tUp-z)jM^Mqxb`^Q7%^ho%F*o*(TvI* z?d|$NQjG?uVz{lgKBgdQ?yEP-`5o?WZ`Db-Fz6@+KLPw|qDu~Sm$z}Vxe;w8R<$;5 zY^FOwu(T0QOO-vAMfSu9{{RJUu$vbbWwz{-BjxPA>y73X=3bX% zTo9kyPJE~7hT}~S>Gvp;+%}~Ku=zRiFwO)m4rg`Ma9FGr-J_p(A!Og%L)9B=8?j?{ zn?=qBNg>OH{T}Qa8y(Jw1qO>$g{O3qHyUZ#5j>@`Gz9#dW9Xw2K_PWev9dho)dJ&b ziGWrxV(<+uO2;>!rHR0G>c$zXxgs3XA^g@vYk9xxNW@ z&6v;kEKEIXQ_f=nAN173QME9XtGlSDcK-k!Ro>v4EM=9bD%T0iC5k}iwuP&x+!^Km zBa(k^#<@!vU4qdUv9-^)EnqE49i_#kK<<5Y!ucn+62eRE%JrWC_ww3MA--h?idE&v zJR6ZtxXrOoMaLbK@?DQ&ETs~P=n9sab@`g=78We6yJ$+X<+oT1*Kdx6GNaRoeL01?-F&GzC3#jC%zAv+VxX1Y3gBLp;11TWYS$AtVpu*HSUJ zcPCw>g!XF6!J>u2svS*}i7lvjWZZ}S;#X)(5TYSv4I2DSYpYqMy?5ws6&oPLuu<=- zF%K&7;=+eodetKPDPW~O;oGL&DNPe>GBR~EPB$%sgZp%$v^cf;VBjvmSGa1D2~`cHKi(jX-&0b zZ;RL{T&k|KqJ2X^P4R!6^1Ia`RQ~`-J))nMi|Iz>w&WhrlB8$XD`r7pvRmX7$-F6j z#1d4}TOGwj`d3#U>7w)tk5y1J_X6ZGy$YC^xJs6!xh_KAc|Zj?;i*5zSwBgvIxX}v zxxg27OA!h9{{Zf;j&LfRjXB~fg*(!nbqZ1_tsw@9*$rl*gY(rq+f}S2gz1Ln;)CQ~~M^t7hu0p;BB2TWKg#g-9C@o~tKTakhzh7gkb*sn8p3w3RJn9fC!D zG>w~$6pHOeVO?!=P?w_I7?9ej^)}*FN{a3Q+f7~Lrtp~YOvf9fQJAd55q>*lzX&Dw^{dC3vE%Me&GdMkWWoYY&s?%DF}u3!;5m>H7gF} zt~!O8;`0>Rr(*YrEnF?{t(AuxQrMD}Azi8!6&oEpZ0R9(<{A|W<7L3w@t|Ce`^n4j ztHqB2InOA~cqa{WBAaJ^)x?e6h1pVq5F|PpbG1tep-?@(r+s#b;g|l>xvnx; zTF*0w;+t}FC;9{|mGm8|>HIYXuuM{JBr44(`^OjFqisfSM!|qoNLXijqI+y!PA_?bigp zS(|KkJgJszq%eRzNs}EE{Pg5dx~deU7WG|N`!@M~bS0-R$k~$618gqljCk^-osF|_Ic+58z`Yt(jg&f3PlttUf_1$RFRFQgvKalpI_3BXd)2N6KyT%FPk&%#NZ@;1#J-aclMpxrT{| zIesofM#|IorKB>;ZWJ*3B!r|XDoTQh2mb&mI*UFo<(*nWnGRCgPtx#y@Q~%%Z5Jz@ zy7zFkvi;*7Q{UL`ZZeYeZ``BWd)iqQPkMG$4&$zbw-ZMe!=Nd~win5~YC=&G&=wGO zuB&5Gk%J<25ffYXhJNuC(dsZnn)V}c_09R{T`F&<0f z?m}f!aNNG%ld#QFwZf9y-KXF}2_y5>mRpJ6+5Z6LVF712{{TM3Wh?aZwZwL+cq$=K zQj@tAp|7r~2%^@e^Q}x}NOTfHN%s>&xF<>q6pLi{5>%5;>XJ{-Qh`z2(wMou%aZL; zp-SVtPT81AZbAj#1;XU*u}O{m<%OlEQl}}}>|zRQEyI zIYJ%A`!5-v4eMp*18H_dcN0_~6GD)&u^{|4Ci@Iw)rW8@J~x&b31p2eaPd+UX?FX} zLnb7a<+j?&6r?B~)u&<&Jw~hF3*&az3M=R?;<#;CYLh2Ld)xP!PrTB0d9hlA`u;j? zuiA7+HJ;%`8{aW@CA{3LTi>>EWE4IH3MtfW>Atjfd4nF0)D?-#an?BQV566osaI$Z zs?Rzqo_U1(-t{OPNua1FYW%eQ#k@}&nrKrv*{&l#o5e@P9lPTgW>*c^w~IqA$#8aw zX>BQa8-J-ZH5w}{#nrQ_*Af(5zZ1q}SXlVLaqgJ5jw!g|?iVqO%d_FEm9F0Ezo?N~ zfm5;8f^JV8+i4)xWU+qTPl5=-qV0BJqDr8 zXyMgZGU#?o(e5m{K;S*Ys`jR#y4hL_u}xB{7Q^W!3QB_N2|LsA8bhOdO;nLAZV5bC zU^L>S!e~T&WR(`akfZYggHlA~iWL+<+US%RabYQLLMZ6lxD!PW&rtS)p9tuv1GDnc zG?oxT9aLK0Et%uD%aq$K-}}xrbacpi#VnHR4zG3l4u7Nl)2(cG_P0^&c%dEEW%id4 zN?gNMp2^n+`wByfivIv~3CT45o8Lhr;*-$pHF1WKy0q>J>KpbS)F@MnRNG3m1$x*8 zU}{K7F@@@xZ2ti7G^ENTT5+;{mF@nM(@JZTFN}rgloGG&wb)NX)M?<*qKA-mS?xCO z)Pfc8q0=-@wn0=?OuCvK^&ld1YKIG@Ki$)9krU&p9-4YPoys%eq8z0Q-(yWVy+v~> zQgo{ww&;hMnGq6NbP%K|H559XXP2JY!OU?AuOr4S?tyHL4g_~pW1l<$!8vj}*IPBA zn{H0RkoCH1K|;{`3-_8>QYpGt0UGp24EN&gmd4iL?v22MRpZ`s`$5LsRtuZb_c9~4UBZ@5-!^r(g;oXRzZ`79J4u2ay_5%rFM)Ul^H<5(>GrmY0gdI#$z zl5y$^KWGQ;(QuzKjOHuKZb|L-+=+5!Ja+wfY>D>;iL9F^$c zzJw9?3-X7;&Jf1=3&pJBE;lbdWBXoisn+&xC{td+Q)*~9Q`q83J*uwX4ROzCaCYvx znm72bN&fC~*HKy7-9^|?pP2ruozncb@+ZqL96Y$h`R6&4$tl5MS+CeZlG{Oca*3-C zhCQ@{+OUvSQgzzdiH_aGGezRLv$I}P)-|v33!gj}nkSF>KaaAw@;!y)_xoJOovi_F z64Bmz?Nk%Kt#QuS^$$4TQWNk~@7&h~X9M#`mSe?l+kuvgW<_i?pi?)9D0Gqk0NW6~ zBJ|PfJ{xzSE7GKX6#lA-wnx!TjW^VKD<`ozGP_*3uyOl?(fzhAEU2>R-@G99>I>OL zF3~W%74Xw;1G1i-_?vZlYCS!$YfpjHd#2H<&lr5lWU>B~`t;NW^;XYsbsO@$A|8t* zns^~rUedK-!yDPr$3L~&;fL&@$3x}uKq5s1f>+yGQ2jgU$k0X*+jC;bx4r_{nA+NX2XIj0N)=K0=>?Stn%z;K z86z>7a6WOvm$Dvr4v}}qWk+!LD8z9pF?)i(EiF2cZ&$)cf`gQCiuJOO?xFkp{&ie+ z$t`lN&>4eqX-K-`Ql(lh;Xs8KpuAcNP(3@60UwUDZD}2QE~6Bo;-jGz{C}rK*59}H zAg3YY=kV!L!tNMb!MN=x@B258dMHn#cTTjn67Fxwa~@M+wqoxUV*In=LCzd2lH(B= zOPLk52IYymK<*PG4)EACBn673{yG^;e^w$DUm3~$71(j<-BypaXPLJiIFFEc*~m?) zapA4!@JLchkn1u7iv8R(?sV-tq<$c5HJ--ZmW`)ME{n;qt(b|U*LPp*_pXX&y!Yef z=Jf1&`E)#EBG}Bwr8*dO3f`3z-n6LIztk)on1=$Rx%n0`bx*lqyGFXQY8JF3F-YGL z>tY0kfF7tC(cH8;HIv2M7D^Q~$0G?bB(Qf^Vc7hK&s5DGRjQPo^;1U_I4)TI;>73F zrb>cQu{2Uoq0*oQ%pp}|#>s<%tC?M~%T4lpPSTXN;;FSgbwezBT(=fd?}!wFbX$sj z%fY0S8n)3TEYL;@5gxh-DF=%32eEwO$9QRFr7-XcHK)?8j-8BDBs`I*PL6tgyM|vP z?AGa@MJYp}S6wxtHdN|VC7_gpbJIZ*1zj`>Q&*b;LFuAFdH^-9fJkXuj)PCLq6K@U zAiU5)2ER>4DQ#D)!D-aZ?v}+U1WRmDC(w{Zb*kfMs9gJ;HjNHSeskf5)0NuqjYsTE zwu)YJY7!}?Gmw_OKg6#?qoSfPBpCAJx{^n>u&R%duBan;4qG{++|sNLYLs5E=$8i4 z3c^xJ^{q8$IWA($SU=OaqOBz$D4kPO)ojVpN%&KZuB?>RUmupI*_EBiwGWlS4Lqfw zQ7Yb`Y0HMu$-H#635LS`x6(qo5kvXvFf?+#BCS?F)`spD6!#A9>+MLSe!stLs$wzhI^U@CLrsVFm0(*B?b+y4MD)t)WgmSd5!hN@_H^yz@D z-O)8KQdHX|2|K7#nn4<+G?c@W(C_iCtBJH zrPz?r!(dW})c#s>iYr}LA{Q5cO#$EIq&#S(M?}LAb1yXlvt=B(ZPA|pQs|~o8m346qEnw5=LVJyW9X8gdwNwo&BA&k;Eu9hVTZQSg z3s3EG0Zm8KR~R zIOEG(j;y%jgsE#gl1}96KvkrYgV0Gv;v>1r`X{c4qIX1cTa-;JPnMQZLXuJnhy$rQ zA_|z*b9gr$Rkt5<=^?JyrkXUP*R!s#v9M58WOG*)eFBuGtSMwyrmQ59RWHDzi<8RB^?ojAna*D>89N9o@)(J{{Z|n!knO9L&?(~xQ? zA2X?-O_mBL47`iQ+boA)V#hltp0cH`REO45wUt+(pww{LAEITmv=n9skr*2fw{OOH zq7ob=C5PN;qJKJqbhB+Ia-MDDd=O9hU6?T`#x1z*z6&f8h7^{NuFwz3BTkC|?xis7 zksSh0ndPr7x>T6+*@}*dN*WaZ039sim=I>&4t=&MC3k@n&n6b%|ZnHp&^kqHlw348=6+p6`=sgF^Qba8& zxo(^ei`CyrS7?lcr}>mgr~`czmdYpA6y31q?_a0Qj)qYl-D~#?^66To73xpN(@h&d zRF+@6sYFhCLUW4t_$Ri5M*g@M^(v0Y>+>?!>&&}x4jN6fo(Kv#TnBG(rl%B)_c8CNON zN$&TZab(uNsbyd7)7In$g;jRqUQfj)z-RXpp~t06hg(8N#HnFF?bDCYCT)k9D+T09 z4Q6P!gW4@{p)izJU=qb?zfQW9W;}`>SPxlO$Z~&aGCZI@sLM-@kD5(JmK{Q?-4@`S z_pE|uM?Kqw=TzjE$oCvOk^rC;{53R6nPs>X0d-gHzxNsDAGh8$%A9rgJJR3F8=_J z+t}qHw~C}vx~|8^xdiu|ZhMJ9Y3L}pqT5D8?i0CL>GRj3qG0sG@~qE0iCyzb?$bi}Sjw(AaK zOMwg_e@KwGI&OM;>uq7g@;d~Mx7<7Exn6VJyhEq-S#%oX(^XeF6O?)Ljx7^;Czm{r z$0PB4$uX^1#9Vx`_10sPUQBdK_L){LRpyQh#1Uv1ay zqmW?bws#XK@ujw9vez~16S7h52WB!`9sRXEmHLuBPPJDa#n=Wl(YS&Sil@hZ=Cg)b zJy^rVDX1EK{r=_7ZfA#(~J(szi+}?WWaj{B<+waYKt5b&G8?>;Wqj5?D z`0K0TqmC$qEe4HT6FGBrcQdX?0rHY**diF#56K+I67g`wF_;&2a0m^%k-rgk`*X;L z^%T78PdL$eWF*x{1kh>ua}nFSYx}0GIPKN*3*i;o0HsIsAs}5a?5B@y7&dV^n2?5T z8Fw5>CHS(W2+g_dhaZdz{j#EVOMn`BXraDi%~+yku+!U?vR;lA(EX^RrOI|&%W={a zwWyVl0zoP@Gy;Hi)b`Y8^lR|eI%nY69`ymjI{8ZK76{yCtymy+T+&87r)9O~SqPBX30ikS00Id0{B+!n1*#KX<~!n^@o%ypX)P`C zzQf!JKBrY;$-0oPEwUtvIZ0UFb9{kkooBYl4Mw~6rs_ySi_NyK;F6^d;6Dm$HF<-L z@^9|97|u~UA@o7fIq>QB_!%{_WzN`{wx-@mmv2!}ONb_gKTS5(R%aaZ0c?5|rQ>;+ zVY_SQ6>91op26czZHhX$kFL=+Gd}B1w%ueh@<(w((Nk1>)#mgUyQuph&4NoG8>JG>lKwP)H^6VrrcKl08&Y~2HZtre(Ol(`OE+HDBGJ&Z#j2i|2xAf}X{3F)jOxH~NkK&tLc_gY+oLrb=R zh&PbIpCt@M$Q`;Dl}QsMEcY7qBpT@Dj!2nM)b*^;%4@iiek#Oayi5_b&KS&-VHP=r!~YTusZ718?`dt7u>Go8q&4ue+^%q#B^EMp30>U+(%GK+i)mX zajMq>3bJFt=!Hz7U6ua;z0i`Bs+J^65Sotj&B^u?`-G9&{{Uz!1L0GscM>|CbXGRD ztq$Czt8=R%HR-husHIw%U!r!#aHNgZy?g^?;?+3RC=b zSu(bWXq|CXq#Dz%s^#ddEzJ(8-EMG&0!VRzo{v`LqPrG5#R6ZT75xHIvrBpS+Y4x z+B~IQV{V@;0j@q^o6~t$JF(`traHVS`<5km4Uu3j zhGrX$JA`KGo;Pi^-ZHojCVVVywYhSP6cQUtY3^3%YS0s^w#oy*JqcD10JNU#=Z(>GfSZki?T?iAvobu^dzmgclenl*_)}if_R;u@Yf$p1SBWW@*u( zV&dYlmieS``^v1@Z;C97TaqzJ*p5H?jH^Nby0E3WEuv6_ba@A1GjkdJ>Q8` z$CI*~-cb7ry|%&&E)_PGm~Eg51XJkwYZ1tDFSzOg>-F`&HoZsRR*qY!(fHrKkbL9hv^txk;uo)Uzrl_fR$Yu1DC(I@Jx$BlBX zZ1N?E%w|ilGDe;5v`&oYvmvKKNGff>K}n@|2U0EL-0&4+?77Xz zUwgkGva17DrQ(?WEc#Nf-dveOlD*W@hx&4YfT;A>q3nNdxR{zt+)Y=Da(}#@TYAvg z+QyK6D$e-T_m9Kv_=Tp|$*6JVsr07ddPJ44+Dga@Q*EeMwLj^uxK}X!th?1RT0Wz} zckge%d{2rd!IFz>hf)_~cypBTUUIWrbM7fjth)idE!Nk6an{;QcbQcdmfCSaOQGww zyvLs7S2){~1mccr_g@X>c6qyDb8PL*a-N=!sOl`N5EJ@U>#7kR)wP+`RBy{)39MdG zJ!ibN$QOP+%Pdjcj~?d*e(YHdsQ0BZBRE1-Qm%>=I_x|T-B;ko!x(!{iu1?3KEvDc z&98K?iS6mM)A1b_8Tl{mQ|3Md)3@9gk)d+?>^=+_5hcyBT#}0ZuGijEYETrfYKFZ7 zh4q(fM70-DPrRr)s&U@dW<>0ILpFiG9Ji5=&VX`VS&tt`Z{| zaiz|=M;*qXEm0hjl%&(CALFi{kIiul@kSWQ_~^L4SBv|RhG~4ByFN#M<>vkis$Xg! zAbBf1&Ur@xInwr&2u+U39(;b89<<8!Injy#E01rJhFBuDfiTU<1qw z;MXo(>{xDe-eJI8+a3(-esvX64)t!z- z@jN@N6fCYLE2v9OmF}U)d~JQ>`%KulCCyV8Fg)|-bLv?#m-Tn|1pfdsy6vqnJUi)o#GQ5G*y>2fSQd8NE4rk<1wx( zF`skvJWur2Pr&+qdUdS?C}q+$)G1Nq{^2S8+X;Ao>B~;a9|KXRm`LiRz7`Q)>tNi7 zqp!T$soJkveDx+LRW=#5YPBKX)CD~U!{?;*L_%rgYrWf}B4v@J%q^&U%mek5{D!nx z*)3%oWi%@*n}+J&&laf$&C7P=OhvBu1`ctx6S}1K^9+w0DH)DFw;?Fd z-{Do(N2hYz{Ye^}arX1JtTZYcGFMPKk~Y5(r0A02T_Q0yBk7eH^o2Dc3I!w|!&%Ag z-5H@;m@bTfgP_5tsnl&VQs%`aDo>%MH``5!hg440BwZ}l`^0s|n!r*Q z`Wys>qvn+g5OqG^BKVGqzFTnehJ_dQ5Mx7JmCaOf+9Wt?;*vUd5!RZIaVFHcozN*AI+h3}+`Dp5zTNNPwoxVCUf(=0{`V>0+w1&=VnR_ZwU$G#SFrwOr zN*zmNl}(#UFB_KYN485n>GfFZG@#Um)FUWvJb(*J6&-#$V6>%FRe9|#B=;JQnrVcD zZ;q>U5|c#_)u}ChiZE?7OA%s~l%9z`nn`s+og8ASD)JY~zZ?8GYT9Nv5oe}_CSC>^OGh;|%Jdl@C*R-?RrKA;C<*!Kaj7-pC z%Vg_Xt~JOnVs>B4r3Sw}JP&2U9(%V)Zz4p{g%K&J%6Zjnd%e1CLGsm*c8+g~yl0k1 zye^~grVNiIymz(njn62Lh&49j0fzF)jTa+8Az`m^VxW<};A_q=i8A7~?ckMh@Hlgp3I-kvaIYqQ?0bm;=c!zZ>eyAx>OLy@ea+l&EQR4uHiQ*D%e+x-{^2cTDNahU z$Pt&`NApqfwv#Nic^(P{jkemKmY;E4xM5%@6>bMXprtqxo_D;Z%e|i0XL*F7i$`#Z zwC(h-s5(Q&iBUPpkfzXj8dFhR%Bk^^oOpu2_#q*lq2^XA^BRkO$~9Z#51xuuvMzzT z9)oeJc!^pkMknKnGMuBkQcJk#;rKz1~y)Z6Wzi$jShQ2fEw4Xoaq)`ob5 zjGj|6;QiS(T1Q|#O%?ogTv%!x7K<5j(-C9z1NbYc zgifDv#Bup|D9%E-I~AYaMsX!8LxCU=LElycX`;!ys&Tvg+C7COB}rWXp%nGeQ$%!0 zJ?p77{{TG@C?l|*?R6yjeDtQii1w6Qhc2nZe4ogyYFKX3%XVh&F;hwdakZ&2-5(06 z3rFzL+CGjW#Wk9@-)bZsT50zkN9SL~RQ~`o@MPk=*_eB62JN~0c(>Rm`9!oKF(jl9 z{q+R?I<3Bhwfc8j9yxvLTetrJr%&hoRCVG9CZ~>hlE(bELTot!HrveRub(rOXSu2Y2oOf zu+MDjOpC3yH}t=M?TVKX+dv%@>-@uG@zrKi8&SQTR^N+qZ%q19{)71{y`A#*Kg@V$ zFOb}0HEQHMK?W`5)hF9^(%;gc;OkI9*!0#*>P=SuOEw;gGsU?&U5;CFEw^<*l-jm& z3!+uN;EK?VS+e?}M4t@T1$J<5f3#%rk!znNEna&lrn`r0lpRy^)upxLNLc)?9nI7! zOC7dcGI z6>G&qoORDr?9vCe!_!`2#GQd|K_q@#>osIO3$oI+*^%`a@Zr=bhn2DQ*jzX|i&iOVUcr_^dRyID56iY`tv_=AUJ)#r;Cw&d8Q;WS|=@Z5*BFCUeiTXt*dZKYfbA-5-<_Cu9lr`!TPIF`DnpG z-4p98nr^npQx#P$AO#*Ne=_yM^^cn~tv=CZ;uDer2m(g#dNLix>h%Zi- z^P&p|gAgkzP-*F;zp6H?4dVt(alTQrtGFc-6dPS@>V96jvclXJO0vAl&(yZBg6NN% z7@K(q8nb+19nZoItK3h(ikj;G0FcC7z~Hf*K7F#$agY*~P>!CO!mhhTVKIpU?}f>N zqu#aw2_0xDU0p!!(X5V5zMSBw275|kQ_T@Vg%MBBRns<(TFZ9_G!*W`N8N~nX7ZQr!JzOX)(n%C#3SMAkb;w7snl(@d)VqRHIXcR`@6Qjny zj!fsF+{&vXG0@T!uC&musq)eSk1n$duP77rr>WA?8&unQVDwc;>7bOG7de3=wF~-< zMwxV)B{j80Em3P%oExar9UNkz6z|b4$#@|I1JrM)RCaYt`Hp*zP;M*KZ>Ww_V~l`` z`(Hos(4rMi+#2fDk0XUO^wqGg)gO`d(o3j?RgmRajPf=w1-o>lDqIBzBP5-U)H{!s zmFicbancG@Dx{t^%T&I@1F%+};ZUVMI!4lzCkf?j;zc2#?dp111a8_0*#0_lFakrS zN(@@?{5n!+E|*DceM~e>P!tBo*VjvMq0tXgsh5^z7aXeKXHIabC;==pQ`*`GKn9v} zOWc}&4FpZ-vqX?WRD_T^Q?dJy2_F z(F7IU=}KwGlx}4dLCQII_KSM$(N{^5g*H3(`sxk*^b}VZi%Pnxu%QZat5T}98msd) z)_L+0R@-155{nFIL1)S{2qd%DRJrSd9q#Arq+K6hU#=u2U9FFGImd`SB$WOGBPsW-`k_`r`l*^YQ_jY=7 z%J1({?XRHJ(ArT~l1Waesdu%scN5$CMNY#&ypoC0RU+qC^;m5|wjAx5Fm|$vn$Yz+ zQ_^B4sw-lOki75_6n2x0+xUK8CKQpITbWKvI#8{r>fJv$w`j$HBBUA{Y zPI^D@Bs%Ig1lFg}X+uJs2B>%zuis6N5w(iAsuhj`K~|TgI6e7_S`VQ5NFSD^Fws=J5>X7byM8l^&2fR- zKH=KTM-*zI+&-hf%SK<_G}ct8(mM*r@K-&xc3iS|Lt#htdk8(mkH(rnZADU~X)dB} z!hvuqL0SHppd{_7Z0M<{Bce5(Tm_#SL>86XD(XsBpHSL&KgUt>GX}Pb#}R8+)TkHz zy3Va_5Gx;1(h-ohKq9poF``K*O(_MrN%&|bkhIW9S)hW|eKcTI1#tXlkL+W7k#$h* zEyWFm?dl1q_~|pN3no-3BJ$u=$xBj#s-=IPgizEZ-1Sy-#n%-U!OfSHk`)UaeXwgm z{bdyO{Iyw?eI6Cw1RqJtrN~%PoHU_U^wfyjs%IjkCmsn5Pt0+MeveOz2>$@>$v^DV z(;cBZX3scMN06@D#B8+Nj0I!%~uh zl2faYOXfd=9WuW^SL55U^=ptDwn>wu2aFd$pqH7m&7oImE19L%qTIGV;z1yXKF_ z;(VFKcP{5+iiX;4^$-(Kw+hM?P1D+aby;yq`2Yv2Lg-dd8*UrwbPqqxb7s~%B(|a| zJHIZv?ePAE=2N2j!24-kN=%uEMl$87`zuJeG}>xFG$l1TGn2f{@nZK67v+p|D&rZBxPNGy zlMXc+QroCdVqyb~yH2Db!hLn$Fi+#TM(>0*>&IEnBg`=W0OPrqSiCDk`Rn{uXw5u{ z&a=sjxZbbxZd05!Q?E#I#Ha90K-Tu!8)#Y^8vs}d@Yi<^t<0{Hz9;Myb$aCM_2$xT z5>`NRf@GvENmJ=bszSiqZ%scX^UHRS5o35|wY2`Le&Bm1%U7xEC|c#XqRM+4?pl@F zpMdTrgYeUF%S}L{xt78{ln5rK0H+j|(t^L%ppXgv8dOrz9G3bGQj<~Jc|mlkKm0vT zWBgP%%vvav+K$5t3vI9*B8puFNco)#fOJr#na>m_M18d-N(D#iPRUCOAb)#A+0?r(($<)-T zcaOP;JHY0dSawA9hEYdiq_99LY>E&5Tz9DvFpB|$p~=OXl(A>)73ek-zP9q>o1THexj2@{u-^4 z#g6J0vdb>``>Br-=WL%Hai80$EViIh;6186zY?8g@*GMTfvD=b4kO$UJKYvS5)btRi5zU z3BBDLwo6Q_Yfr30kdySOuH)KA<*n_e;Rp`uxsA-s7Y^C_YK%gbQfOY^sRZ@YbH`O- zZ*od=Lwc$zJJz3;s%|X-1>fychy{-7KYT6__w=22>N~+faX#lus#I9^l{{SJBX!tJ4W6y1J z!*#5I$G{?7f6bZP3$4!EnsHRzQOFN=LCT8##K~c1i4;jCO4B#i7LSN}ZmPP)FKW434n+v@>-Bc6i?>+Iy zhF&}Jmy@xr-@BEBz!E>Tr27lFzO{y$WGMQrYNo9C&LR05ffLS(vp;w&VKPQM#e8LR z5BTsZ$RixAiS+U@1Tzr9FOH>Vtt}ZHOdpE1$PD zle#r?wzT&D0F}u8cl)0C+vIjFV#0Yh$P1O%3C2fmJ&3U$6i85;N_$e27TWhpQZ@jr zfHik&83+C$wJF$kNPo(A{QD>-+n?q+HZ;?7vboMnhxAAZkhD|^kzU$r!W+q*-BjIU zZR2<8QN7{&mvVxxa$P6YPc2aJME zv}h3m*%jaa03Agw6;2muy#2a5{{XW=DyUw>{4@{+dF*%4QolVN6TYHyW3NuM%x+`{ z*B(PAt-6I2s8irZpI{+Jl7vvJH;!|+Dq}EQcGbvtTVQP^U4r2FN@>2yQk0+Y)vPyD zG*fnqGd3dHQ>#+eC-*S&!wien`mP)AzqU8IjAU`g=Jd{#(@*EsI5_v6E!LUQ?{o23Id!gEedvX7W8Y=O ztv^bDH0fG{uIO$}!$A)qd>4oR0QE;$NLc2A;h_hzjdG0G8?)f1f*^QMc%FXg{x%r$rVde?w7$3UTk*sU0Fuo+WY;L zyr6%TW}|PO+NUgIY8};SuAcV*_H=T%L#cI9s@)P2w@2uy>0N2DJR#SYDV~(J8ZGVsE6j#GPYJVeadA-vz z$a6b3IX@ee)BJyZjO&u0b@sqmebn}&+wCHqyVrebd4Iq7>ziLjILQtaD;w>{?8`T6 zk{46X1LJjCM|yn!0D|j3Ch%V(^BtoJgOqYh*wx1vWk0@^XYOeeCUyiV#R6&)cQn_Z zu{^5M#m+5^J9moqXD{}{A)91kS=_@I{?kN96ylCn;pSu9uN>svpdJ2I&~D~+L>Uv0KuSnQw zL^vXn4ZRjKa)|rh!-_Ga?er+@uft1ytA|9G?%P@(s99lcB)Zal@|2PgI+|$UcrFb( zBfNx&_YGB8&bi*#kKNq1-K_60roUPrNl2x7rn^&JPZ7Yy%wf^GrEqRZ?uf9PY|S8e zq}xZ5xC_q4Y3bjvVdFQ7O> z%T(| z>=+h`p_b^l=}=P8Rj@TZb=ZB{&p#VGZEXhF_O#S|MSmT7toOCJA70$tUTYyeDs={( zb?zyrLE&1H&p-QuxQfHZe{kF~W;I}AAG7ku9NLiF2+Sdfs+Er1T#BL)&Nt5+Eu7B+W!>D7dhs_C0cmjzoH8~1pE2jl+$3zEqnN&0$!hM!NpVJi6PGd77}qN{R?6gv$@ zB}$FjCk`g)MfwE~>sM*~!5W2p+O=6@T}c9#Ey-JQoMxS+D>e91wbeXjY2*f}Pc)DF ztX@ZG6>N7CO46NmBz=+QWCJ2z{fE7 zF28ZVCF8tSR{ z=N)pCO8)1MFAOf^;(Gr8ZkzuAEci6vw08x5S~}2!;F0O?jm+^MpI`X<)QjiN8~*@X z@XfE2vBm?pj16g)?1t;yt7MkId%L%)6ZEY98gnArGk=1M!tPinFuVJ-P6zy9;?FVs zbK~w);TA3YW6k!vWOUukbS0QqIk8y@TQgACMX71}UIV!&;x;j#2H+me3M+iXmN!m< z=+nD20Sd_I#W^?1~aZH4#>Zd}ko|>S_+rz)XZE$&OmI_j# zb6=ReH{>oIazt#x&buS=7ushdz1B&msr1!D?L)dPoHpeS)kOZ^ExLouF=1l_zx5OJFl`H zEb%7`@o&c#u;;HS$ulu(bN>KnS);)q+%Au_OTN%0U9BZYwF$1nQ|+GW(P6IQW~2&L zeCcK0F=cJvF7oX;0@T8vc}>A}$7n*9R5z_DzO3DD>eW?4bigP=Pvb5c&+$y+4en(k z!jIf0B@a|Og#rMe>%RNwy(a{@uNdJ#6~;EyK8X?EPq`jrYi;xq)eer+sqy)ms@&>2 ztCd(GbBVl#x#O}|?88j=W2hdAk0PpX{{XTYt+hUt#TKskde(v70U&vAjysET-Lof% zk7_;Lwq9-Fu=g4@t$uaYc6L3N0pVV8seNlVMpF;S2$Anr+$%kY^&;~T49rz1eb$EL z(@}G*+}6A&xQzQ8_|J;2mYD`T##RkGNTC|c!f55TorTO@65?C$?qge>Q-X+~Qb4Cl z4^3@sBm^#7nt+EWZDcpX^s19XrX_78R(8_vxA`U=<7R>O@-`9PR@w`S9XB0z(laqtpsMaSKG{nOrJAFR z)GmPFw5E??O)o~0K?zM*uT3fF6@yKXjjLpuC>;Wfr&Dj4Hr23q?b4{5k3&S&AcYkA zjS)^S7LA7ebVwk374Xtaf>zWKPUn3Z6wqO93Hn&Oi~ z7DGy}%e-mJX*S3TiIa=efQ8&VflP8Y1aX$*tVLQ2&A>Ja60X0iQj;Ws z(MOXdm8nL(#Ob*LZi&AxPhu6mqrKWCv9(R6n_Rg7pU|GC(?=Ms3RC={J6TFwYLul0 z+iGI3kkunxWOV^st6`!k_P4r2N)2j3SI6O~gzBGh(QKeGmlSQ$6jyCF6!>W=M4+@tRbWj@K+&KIQg!f#f=0w>0Yq&T zDK%!KtN3Uqz*d8Rag_V7-{dq8v-Gf|)D?P-Tw~&)s-VixT@{eIyWK?k4O!0dxluG4 zCeqd2wK{3Hx~WWc5?$)##hTk9e3gVHl&GW~szBIkJo1(`OSQN42nv^9ZnClqq?z&w zJ?cK#B$_6>YcVwBmAJIF3}V%WIu5662lUdW|UM7Dm_o(rr+cWV_B-N+}n~> z3}f*ZyPL=$`d3JTI+V@SfOJs3rV`WIRqLs8bxg(5spkohge~Q4mh0W7nkbwq7L-?= zXUsut6+(!w>K>sfZ>c&Ps#9T$J=G|HPrJP!Gj6anuzzNr`JKM5uXDKgY2YXmZYd^AlG7<{3KeWLzzM37 zj6!j^w#l;|bPnn^-~MCirvRWzYyJp0QRKeeBE)h^ls=zr3SX!Dx>$Qa)jAqVdReio zrX{jn&K@x~RLL}IDiIxIq#x9-gHQ~@szYILL~Wcw$~QWyE*~WU&Wd-jq5lBQsA+#v z)`ZjHay@;;FMMNXoc>6{E!Uf>410WF>Ks>MzfzB3G}}&k;o(WQgx6Gige;12o2~iQ z=vG59iS+>0L})bXnZ6(^2-_=J9kc-_{{VLSb*(C#$V!0oyPNqIRSzGxg8Cx3h8aLL z?|H+y{Ind`4HBX9wy6(1x%cE~v_RfAG|O<%0k3nZ6?H@N{{SsXVKhv+!0J#I2(x6! zL$VxF8Ep3`tePD-#*wK}NNIM8Fw3tCyWV6dxK_+l?M5D*soJCQI;Y4fj?Di*F{7~aEkt+O&J1-FDu_@d~`^I(4Lw!^>jgdXXBum-n{nJppST7ECkY; z71aistBt|H#Ntf;Ss(FK5KVvG(k>Ofr|4DI=0Sbq*K@w4Tev9GG=*h2GD4tnA2-WN zyG)pI*aM|%vrosasPZ*3g`&o30ab1P0OmE^olTFbpqZgcTpMpUE>OhT=o*k6D(g>d z75@OULg>}1W;LXpdnflS+<4~wlw>Z>n=T$esV=4GP?WnJMI@%XZ|S7j`px+$_p#f^ zO;4C6cM9;#-gD$zZye({hON?We(JxxKGNJ}mRV17mZTv-kO!bTyPjgk0<$tq811Pe zCI0}SIHNPYyL(z@^9 z3NI*0(67Hwu$T8s7m7BwqDfq2$Xky(r0g68MIe6xt_AKiY;@Z7S9}&{Hnj0wv(NE- z1D~*a#~5=SAq$Z)3$j!1*zQcB*3#-va$Rj|1x>nxx>k}1BUv05CcVYZ*&rpf{MN6L zag2U$-O${$I3Ax5h+jIsLj8idV#KS~Q{vAfLe6bYt;g-0QGQW_c4eigtJ#lmPje6U z1KOW)C{YxuiK*ADc$e-f+ECVAG_?4y8uMS;l<}87(c!>rPW1k3g4t#rneoO`e!;mH z8kdM>*sPZ!2^_1RYgBC$_X4_i zaZ%l9j3Qb{JCpcm+L?3(4BV;_T}TcIx4@6JEFb2sjk5rvKHVA;0UxVvDKKFndYK_U zwXUO86QQadGAsHLbfnRT#SYBJE8I^_8>S1i2FIwn|Ta`gA&~vU>`roxdVjP$kle zbkjwRa8)n2d}l=?Ldsa0+8bREx>LNKzsFG@XHJSsZp*ZCKGoDkB)HjiwAA+Z4&z^i zbcsih_@-lkZx!fDyWFwj-RA-090Q$r<19xs@Q!gAE+;*K8El(G{?YEip2#XwPByBh zssgm$hg`pndzA&<+pT9Fl+82%_g$l&dnI>^$48O0ak#XQRJaP$<_FuwS1Vo2f`tWk zC~epcLHXBP+lT{&LgY5Ly3i?x!G4etL;7jBJvETXS}v7=zf$f$V##vWav_jhK_gC*$%N@aD!tKTO*)k<} zHq##Bf_vTSLPctJ>83JyHpgydjBW$6n}@+kE$eOOJ1S{Xr4C8ri=P$b5Nxw@i(X$I z;TLN_+?1-F!K_`_im zu9oQr)o_&P0Y3VWdzvc=8;XEV`)iD5{fx4CE`i0r1>bPrxO}WlZ`84`r&Fl@NBFBD z$e(>oBHPF;Wqv*I%gIhfYmC-!HzASxR)fr_s3nFNE`m}G6urB(Nnz}poXxU6flJSR z=VXRZ`)GG~f@%DUuO4On%O`!td`RT~02=I^IQ_SjvZh*^9BmxK^@1wnWaSN#1(To}*OI1&8H#FZ-OB2FPQtgW9t4#DSWinJ*xrX1mtE=co$QNQy2w6t4dO z4G~YYRTeD}XinonB@CGK9eV0D61S9bmKC?t{{XzyrkW)g68sqC6rP$2E1y&jPjU8P zh^_vo#>yUp*G@h9CKxK^69!MYUYqpLYpSgS=o8x1!CMkt?LCnjQ;#2B)DGj*@Y38| zI(10$xKZ5~-=~3}_Tx+Bj{vy0Ch{YcFI+q1_FTfQ#@z(KyjkrwnS{Lt1i0!`hY*w@kww zw#jMxhBpGo7S^e4w%}1|$K84IlrEG}Kn9%%)C}jo`3WN-j14^%UKjSQj$@i;kh}xa zqWT)fIHMKgjB3w@;#St|`0hU$xVD>9boQHJw1j|>>90E7-cK~%qs?4btt>Eh*4Dxn z02&1p6`%O0#hi(GP2VN4xVxL3pwEdiR7VlmYCqsDDc{#^XlyxL@kg_=#^3vOWpQvM zBT%d*w%m+<1Zz}qQwjrUZYyVU5R`<9lhp01JE?cSaa)UfF#`ifK%?$Z%yJAf9&Y~t zjNq3JLYjv|ZrClVXh=(H6)yh(SxKrv>IV9{z+<4glVp$B7EhODuB>gnNFy@4z$iZt z^2-9rU;VaSadV6BwwFRLSncEp&qZIsWk!Gz>VGX?Wc|Lmi_Z_8o+}T5_malp-j&Dr zxU}_MV&3hqXmfQ1 zQZF(*Oq1k|veP#d$l9@9Y-w;tj^ja0N@;a8?nv$hD)snlr~7%&ztf$M6=_~`_v6_1 z@|FoBcG+6FbBg`CImz3s3~!lib~JIE^}4&Q;LQEw_QkbSwx(N1LV}iPK?iQS>=&N$ zShbabguMJ0l<_~cJiM~{chUh9dycEGz9;amGvlrcaK)@#$9m;FdQ77At((`fMW85A zY)4WMmHTAW(P}i;1m#?b_w#Ra16Q{Fz2R|TYy=FDy`#A*e$BJoUyX4)wtJLIb}lt< zKyrN6BfTu4vY?Q%h7zSvS8@i!uBx$E{{X9#VCq)0J(FfW*v{(hYNGik+~>xN1FSC1 zd6}7e%20g}V;||D3ilsyroWcDhCkVigsrGvZq0t}`2Cai)Sn#`Tgkt5-xKe6W$FNd zd&p)Oj-eKG+mwZ+*Bep*?r?$Yxvx!X__wjySZ^CCpbb9-%YDZE;qiOyWR}g`T_7X$ zp1(CsKGj*)L1pDEuN1sI^}7kopt*8`8MT)gi!{I9f=C2aNL2|Qy4rr^aMtqLM<}PN zG(Q!}KVg~6$g)$~IJ@b{4jusOU*x;Bbcu&#kY{4IiOgiy_XrmTW*{jQC%qt=3Lk+R z>&W)vXsB9fSJHg6cJ~Kk!7V_D7-t;e(9e;o@6Z|vV#o>Mt&ZZ9bw$=D55 zVamKA;;ZtE>@OyjiOO2alFz^UxZ@pPEm9;3KqJP6oa^7_gN-BDED$m=$XlKGtxJ|vP z+Re6Gq=wMzhAfo-0K=4mLesy?T<$p@-;(YZHjRJH2XAuY9rt3~8xJy#<4#EwSSFMs zD%sz{;zTBs`Vw4x|dkwC+QC*h@*l>x_8ZHW&uq^T7n`Lqi{ zlN+dA`7yS2Mv(aN$|+|yR07Q3paMPWA> zpWALq6n-L<6g$7&P}q$%acb7;(x%(oZt5sfnQzdYS1HJ;LhJoA`g#vt8akmpC@2~udUO6+v!s8f3a z9KuKHN7AoFS4Fm{#8pc$Bsj{FhXs03T8#4MnzyQPt!dEoTt3B}W9_~7VaV~z$8DFy zkGkv3TyU$(l%&GPdDh7RXqyGD9S+gJYpAwNIEiQWCW{{VT!uJeL_ z;k7>4<<+324@E8=KOI>6S8=e*0r36`tK3+FgmU)=pO4$}Um+aM!2aQHncMB6@f{9? z)A_qdS5HdoSafTee11i&}&4sVnbF08d(O zcGkdqi)1XCT54z)n#Z=6U+tx1+q4HC;<<_CZxhe){{YEeYvC)+$~HrM;=A+qi}m6P zlwwJihFXxdv{p&&1=?@w4!Uj+AlsWp%sM!KIxJRy7;SG1MAnM}SSLbUC4suQ>B)q;g1byD4@hI8BZ z59Yk%YqcUr9P#bK9jhz4`^gSQn{Wv%C)%fSlAy6w4Th04jbW#BV$MSz{=leDZsT5_ zx{jY>TgO~WH``LkqEh>P$K+K_F?KwvZJ8nJX^=gu?8J4d%}2t!>V#MN^T*Z##!oC0RLd$Hpj}cO=aC zwJSZw-x71~L1{$y9@GVp1%3lpjkvRT^Ju7N#N3XF(R_CeL6 zY@b3<;R&et(@Tla1LZm^WL8TZUN~<7m4xC8m`cQrhf7 z&{CjBX{Sv%_O$dVExCPs?V_f9W0iSBj^;MZGb%*<&mNHIW<&3hAq=;=d(iNy6!;xM zj^1Es<)OQl(n$9+%6t{%ooKm730sUSxA{N>*HefsS3ahY%9q)dg|3#L8?J>}qWtk9~0TInav zP*NKv*Hm>1eeu%#&m-`YHXMbt%#$67R!ojE_wHK=RAbx^#BZrFoy?(GtLFL_LE}YK z{2}C*_!E~o7lE(0Gi{FL$!8fZtb{FTSJH}}o#^c&=UqavcEVZV(Q3IZztup?fHfmu znC`lf&O8gko>XJ4TX{YRx8)F6+OgpLMs!29u@n3bXRL5^1sAyDs8eG&7(_?4tejNW01<5cBBf5 zfw$vbKOKz~6xnBu6sN=7rQ%nQ_8Wz>g0MLF(|0lzcDYAsukJIP0)yRsUs{0l1k##? zC4S7sxnRc^BX0Q6_U_vL$ns3kT!~v zLr=T-ZULsOF2x)bRxRMhLV>wKg4;G5t7d@E6^~Q5`!xdmX;poOcIvfHjJ)1+!9hK} z@+~PSTDqE~S5m}&t-Vz%PoaCNq0cm0b;i}PNg$8USvN;VE}a<#p#!2su|;9 zOcb`SbV$}{AeF7lB}JN7-4xI+phI82 zPRK<;P^$1#RMAEem)PrS`PcYrrH<8UWNkvT7<$n<)R0Y7fl75JZC=AITCrIMZIMJ7 zmB044IUa>dBx<(iI%>2y*=ouya9P3?;hL+oEke3Qqp0LEQPwpx@)RL5#;1^wstSdC zHr2J*1&cL|lVpmmwBn-5O1c)*Kfvoh8Kai%?cT~M_UAmjwQs8hT;o*QTgIk`Ovvp~ zBq@tqjsy;h>!!+elnF`%e`OuSP@cM!&?!Y0=1QXy9H3H<&r1!eViwglij0R>UWw_b z^17FpMmcuzr!h$3Z(AR`%$QPnjMv0aBe%N0y;MNvWbHsSP2uy0ru) zuu6$E>7e>IR8g?F(N+#g%$IwXEs&h7IOO}J9h7=&KY4Sw&@HYi$TU=_(Uwobzv|Uv zn^ka^BE?gyZKl#xw+J6S^iOaBJp?7rqPBc-ga{EDb=IpYK&?pKBd)X4PU)YtEu2xd z-W|uPpPONed~m}ehJ(2L>8*Y`)JD*`##Z^#KB%X1io1!`vZbw77Y#tI55q))GJ#bO zT^cYdjOrqij5a%{kSKQFPm@I;1h(^$ZFbocC9E`rAdpYUX@^wmg#ZeU}-!sxBmb=An4TN zJjRK!$MDx^!m^-^X!CR}LO3yXo6aaSSNN$n|3Yx(MK@B)Gv zRU~3uV{xA`4_&Sd*qLsME1Pn?jQ!`^c-B`=48_Hd{q& z)C0MuqcT^Xfg!f-rllT2{BE{UFQv=5YjRZTl#)UJ0C!G1)u)1KfC7w4<(>+($4qNB z48w?M*SW@f>aUOWG|-AuqLv)MfF4rYZOo|_EIpW@C$#|#vVl*?S48}Coz$mAzyqRf zUOb@9kjz&bv$YO9z@-IAhd^{mwO)aZbs&)v#Pk$k^Dv0_#^W-RvR-y{Ou7>$skm8)X4QoS_s4g1n< zt(@cVP^TSauxyjB8HLd%b>iH^qaKJ-6qhE*Nk_P#eGmGv8Mg-<+1J7(DPx2-d-Pr`AVd?u9Smx>AymcXey={HiH zszsH(T~z>~CB>Cj^%6<^b)BtTwhbFvwh`PvKaQGB??(ePrAOzZNiJF`6a@Cu=cJHo zh`1n5`jiu4%PaEKbgkukqDHep1QWf~+o!IATUEf~z!~98sbyWE`5T&i02+@mHChWM z{{W7vxlSKjmGvk5^!>0J(QA&0}9cb}#ckgXS6L|2n33GQXmwD%QN zBW~}Wxy6sTmeXHJZP2!>)U$g7HNF-b^6G~GP`+HYtZRsPiykDW+-Wa%Dw`EHRb9Uw zX8pTv`}v>$0MwE1Y{i&+Xop~^hBMnD;Ls!#o~k`5uK4HCtIGRozOuY>^G7A{^MUvR z(~L09<_1M+a&ALpbza(&-2VV-dsd!`p|1LyiuTIeH3OuVL)m$cp8Lg;<2eP6<867MwNN^S=B1YukC_zI zTOVYwS6*nJU8l7ZxgXqrFSF8kgA0b0s)aIN4!Q5nOy`h!=F`qxdC44?o3;Jwei;Vh z{2PO)jS;r}mY8v+gsHaB6H3y(NUpcGe8S5f=GyFYK+tHhHy++z<)L-+W@F@cncCCm zPQ8~YXT_6jnKIomlOA=s*I1bubnj`_R$E#XLPb)fkagDB@ZnInnT&%%iKamvJ$ju6 z(E|z`N%`qPG+{!bM*VB2cceNLZMkb6x=RVFj{9&>t;s#xtFDDo9|)p4)T2!Xx~ePS zN(h%)RE@PTIYf2388WCwnQ^9xqXIaILZ7OdN{;9ehNSveu9Oomg&l3gE1~T_9VjTz zVF^i>iVn-^O|-BAg(P)LQKz`x=y0aLJtnnP(p!Y`wqHAkWP|PsY<)od{{S5%fO@9e z6*N~1#t$<&8^#Nr>fZrO=KNQgC)>^Vj_#8p%W*>Jg}9U**?+d6Eju4jdh3&O?`vy%q%1H_!Ql>WgJE= zvWwFuvvCR{+=MojGPDIy6`Fzx9}P*mxW9K~#xlnV7@~aJd8!U41r1;l<=XD53QVh# z7Em5x#O~U_rANr>o?buoCv?KJTMIr`+a_(cG^%x2GOS~eE$=^+IBqC$?gP-(4^4nu6w6|bXM?U zhTu01RL7Cx4Plu6FMnQ7YQM3PAgn-xF)vAn+L81=-2q9}6{X~plU9*Mw#ndQvyH9~ z4ytZK$(v+%A}RnPf~oF5j-GYBN^0Kd(Jw*ET|$LeKqvXBN{vIfvL`GB1FyqS9cNIlV8{`od)o3bUdCh=n^))u*HsePG?jG)`3dE0y}3}8x>f28O*InY z*Z{AsFI=Q6b;&F3D*A`0)l=I=dkos@o={~YVi)VvQJ&ot`+0#%N|lVuZ7MC>XF8($kX`r+sFwW*+g|MUm!3zMvk}N?BS%H@ z)8CJ6F1V&p%@8KmQ=#zqe^0}~aZ>*PGb3TwZH2cYML|&ZLP`RMP3cW^xg>DYvdLm? ztlZHhL2_qOPTuOsp)^f(UFwn5XzI?$WENwkPrTq|BYxs&Hq?`H0I4ar2R!X)sY+No z%g%1ahF(|pI3-3?;=k1F>XGmo!sU5Jg^Vky@jq<~-q|J^cqncslYG&|T&^>29L}U* zc`gYqNWt&W+Kjg#_co}@DMY5-ZC%Jw2e~9`m0#aYGpOTGy)%pBmR7(yjR2~Pk;%Md z7u)Vhe4M+b_amJ$<$t=k5FC|oxJg{eFQ_(@q$$T-Qiw`G1ndCYRppdAEk^pJ1V>+R z3g|lODkLn1Dn6d0NdX4RQoXswt+2}aj)d>FmeZ;y2G>Mo=)kJ9-$di8Lr0k>ITY_f z)24!Ms0Bu;>9@me5|D-V5;|xgj3GS-!$Aad-uBQ6eKcW4szsX{_lhfUlA-!ZI}H$0 zLTae?YERcY}&mf~V#d83a-+AdMPd4-2yV~SqBXCuGQTU(bv!6972-wuRm7avE6ux`-74&dzyTfrc{T*8EmN{{WBS zZzlDF8za68lYUfv%vIvXe~$3)9`J)_AHtrF)VnO^-GU#ts1D%}qy!*#l2J;7N*Zgj z@Xu&q%UQZtNDPH}Pu+jq-Pak*GS(;viMnbjQSk9AsQdxWalC@!+g~JOnCy&REyvtT zHVKPuQi9R+R1|I6PwCes{I zdQuXFI~GC#Aa*Pepa|dNsJ2(GhEPc8tn%2#U0S#YP@e0MT!YBINBH>l)-|sxkH|JK z@OfEf?%w6P)|n5eff&4?l!a^op{Dz5x8W@@872WB+b4itIOVynQOBk&g!+(ULt;alDXKa_rFNkj<{7>%cVl!+V1O5(_@}s7 z?5&Gh_qEO=P@^p8!e0+_r*c+gU~#bwt0OCZZPL)sMY>4ftDV1-Lb75^gzj^mNTJBG9 z+pe`g{{V0q&9rqZ@C%y7`#j<9e(k4d>|B4U==Tly<-^=J5y-ecH4-DKX$?Op6uk0~ zgY@^QMOD`)8_K4wsn#ue;O6A_dXSuHh$c%mDFP6lb z2Wn~Z>!-uFW!nrX>JGepac6UBA!~+#eNwc@ zT3^B*!Zit#Ga8>6=RdYMvl#VZTaWPTsrGlRDx&ic{n{qf$dVXZzjYN&G=Ea)KnL;G zf+=GvrfXRv?20qo$p$=ygesp+bg7^RRMav#EVFgGM=JJZs3-wa4K)?eHmX}LUJ|Q0 z1;Nr4Dd6!RsZ^o=01ZX-M}m^uw-n@uJTG;ur{qqUnlsru6oj2pyK@G~CZOsYqERLOb>E^g}AxD{qs3^NxWN2;{uHZse!7eC(g?I%BI%iG8**8^1GZAn?EUdQLEx3_84PY$Wk zdcScrDBDe!RXkf%Gf$!FC4WnvhO`GwYzT`%SVF?RQ(l^24N%w;bLrlWq}4qZDPQBM zjB(ncJ}ppfOIsp}CD#23{X^4BY;{UxwCar{f>h!<5&C|5XoV~lue_n$m86iJ1av{T zl+-D;ZhbTnEs-f`*?W9WjJAj+R6ZQ&d$dR)QqxZwEPj5vFw%liZX%oAm(3}%PU63w zjlnBvL$Y1~`>5?Bp$58!=O=cP)k0oWc(KLaXmCbdz}&SSLsm?UEXlFt_VzmHc%UMp9B0vnXH`OI{|;{>9AL{DdHttoErvR0Jzx4RwI`TG&g9HSD{u?aM0Y zbAh9{Q%5oLmp}2(I^1JeId=a5XSP6;i5S(TisXO(itoSb$#@u3~ zdzdRl%l+@Dn@|(78$k|3?kxO0ABgy5tGW~XRl|L#=luDg(M|CLn z?seI5)=|X}V?Rh-Ok`%bCGvivx_RR}CnNhteZ%p!9`)k)Oj7+4Fx&v!?4@cI*nh({ zBUM>u23)ZGD6MgA4~6YN_XGSF6>Hj!x9u8JbxU-5uAz9VaYf_6pSUg#_%0ON>{rN* zR8bB@aG;P6U7H6~nMYFYKaPu2jM^+@9vZG&1q1k=pPr|dg_~*~*0ncugnkb-`F&$d z&CyJ@)yjIkB*w_zO?Gi8AxGA!>#Y2=2Sub~$qDUl2!1QAsXf8Q)|!4G>UFlQQEx>^ zRSM+UV&grv4cm2MpZoFEbeca@;OLT<`bpA)Lx*%!?^^okChCbTz0F3$QB6~bDj!94 z(u9r(&}or1lAmHztPpo7acDpM$r_s3pW>YmF!c$?Y|nFx8_h_yuo2#+3IYF)j8MN5~YA zH8uHQdW99-SPqU(cz$AW4AoNc#3Yfd!nC3xZ+ZY?Dm~#e~AmrB(@s&Dbo2{Wp*=7uDz_Yt>zjNiLE6OxGqRNOMZp?yCcROMnWWkBe!xZL5B zf!OGwZUC6yA#k2`$1>O7A|ont8)_9jt3e{4nADg*%WbswNX!2K@vno&LaN6L$xW{} zor>HEZawX%;7CHY0nNa#@YZJ#?UFf#Yq<+ONEpvnp#lhcC>q56>85dO0(qGNM${#L#yl*ro~58SFvoE zt(GEhQUFsyd-m!PQ%G8BLTs$lGIdLDMVdl2ng}CMT=r%#Ogl=jt{BZ!US zs`B#f!v@N@cORP53`f3O5ZG!;I|GYXXsS&qv8S%6?HcyYuSKoNJ_kTH^;CCPg=62< z1mHiZKc0yIQFQ>V&+ySDEqhb&&_`4bf+gtCDNSA-5+c2>l;c!jtWpl$G?J9XfBY`W z)f(j{%BN_Rp5TH{L$x)lz)jh6er;>!4ouH5vE;$d1<|wsnW-go_}5iA$Z0J?w&K@2 zqoSwq9W2Ua%9Qq~YOFqlEk(q8m_nCwq$YMa#H5bpB`M$V8o8X-Dy()b-krr1Bne8t z1F8bIC!(G>#%Wpoxi#*f16I~D8Z4G>G7V6Cn{6$+x9R$+{u(>BP*pDxFs&3tYIMh* z!s|}Oq}TJ*C`b-k8-~@2(@RQjc<#fyy}rGDHE`z#wPFCmHR2@$a3b?R@ogq{ojcKUp1WA;KBqS@MemV^vWVbefIEYVg!6Q-+ z>QJ6)QU%5JB_t&6r(~9aNQJaW@tmT-ApKf`6VfozDIF?`1CT2eI}NlEEi@28?bEk? z5_fT46rYBGM#HKFz-Ttn6zTzKBn`Ak(0z(M6I}`*z4n~n=sSG0D2k|N$6`r4DD)Z$ zZA6?kHKu})(W02RdmA@P&+lAsxtO#fqiX6-IxWss8JmFC&?|*4YyeF9*N}ag74F2UM7fDQ?+q7zmf&n8{87rzb zfogGfG#iSxw!}w-*ib<3Ac50uS>Y@VHxQ_^`Ffp!YV4rm$Q?DKV`#D!s6hMPoxADE zD8+2ps&*O@G*+P{v`TFa`;OlMqkF2PwUk5Yw4|pAj^YXF)kjU5R*CrID!Ox@BnY0) zJptMg{{Y7eNj{X@;inh|?Nob*s`YHi+Dh9l1r;x<=xNvErID&=lBBk!wzM`-KB@wL zJt57iLV@`f;O*xihhvO=#DygR9=+}m4wExGDVB|(B}qBm%{crVdlP`#R^6?HszYX- zrNs)=kB*T6(utRfl^QpE$YqxsYH^6qLWYtNx8jjqIvKezTmm(0T)DD6hh@7VEq}!d zT8{q!$44As6o6`gyMZEVlF_XR1nKhNIv^Y>i^GlH>KUi%G*KEzkvLJ@iMBzF4WK`u z0dR_SBT6suNo#;at+7keW5)^I#U&%*xu*JT$xx8fT@s#gA}Ve%;Kfi|HrA5R1E+B7 zPtQn$%iTEU8m+kgBAv9VV-0xh9Y zdSS=nqvhfJO2gjst4r?Ft8O^)Kaj4BI1yCsWTiEtby{thYfAqB;5HL7?7DpwWT6WG z0P&R49Wz2hEM##~t&TmxDrGHUF)XRA1Cks{mrsvsKk?E1MHQ@gqfJzQM1t2*(V>42*XyE7-hf%!gQ?SniElL_ zTigX*oEdop)4D>3p7G`nsA{HxNs1b5VgGH zY^fr(S^8^EgIP?Ux47$8$#~KJLJ3=}C%yUW*`UU~{{Y|)0*O1%U)wPM03X;JJ;vP; z1%D&h6SlD)(=eY==eb>@-@?|m7{;_}qZmsKxq5{df}rYvQhp-3?e`it?p{=TO=`Pk z&k>bi#dsbAWTg&bFqcYDZsLx*{{Xh+f3E}LSBO2)w+`eop8((Hp?n)P$S@qQ9nLX3 zyOvC^5}O|7joz8<_hL?n;B}``78H_Fp;{;qdJ#=;ZmroYW;cqojaDI&OAEu}J6sNh zTF)fB`SMH51mmNg@*8Ga_9T_2$#P=R77V0^5)z`f7~MqEaj>tZtMC{IECCS>3Z2UH zHyKwk)40{YZv|Z}e1VnYGMb-@UgYB!R#K!jb+^3iNl8nBiUHOX05L$$zm))eMR(Q2 z(8TdV(fBNly_cf>?Q?-X1drg5(oXI!Pd=-5N9= z-%N_tFk?d@{<~97f}3FZ}8KE=%C3GKHP!;`m3f|N{aYWlM_hoAylWXpQ#~4d=yKFx~<;8PRaGu zIT)N{wx#HIf{wauv0_+}MPn{Ds_im!cEO>7pD+HDj1-+M{z`m6cs6qe;( z!Eg$GTdt@*wOtB&Z@&7gk_CAL;)+MH+>ugJ?`rm-uV2qqmod?4akG3>gAU~(MI{cY z0N6k$zci%)QPniCH?0ER2v6< zRL^X*)|Zt}Sd$Rx+)$lWx)Vj9f#9XMY8~dapgM&iAW=+#)OAXJD&Y=Hwq3VFHn{0R z5@Pq40a#5Gs;hsf?@d#fXjXzLL7@rzj$IKNLR^~>Guay$%27yH<~P-C?Y`izahDq% zuBt(5@g4VjqQfu(>V3ZQuF>$R)o-Cip%)vEf_Gc4Y82{D-&Uhd4vNYN08&M=@TVI? zOHEJ+s{R_PmfEWB*5;Zi3fVfUM`_s7nxeV4qR?8)RDUYMaE?#LE?8!NV2_hyw?u&= z-5~)PAjv$6Re`Kc&K>`4z}t zH2&2*&*7&DWSslSIhHT~07PT5;M?17G{=p`;Wp(ZF_>m63rE(Xr2Zno`z!PYIQ52$!)SmfzT>Axxc%+ z&2__7NO9G;ubP%mrmG(C9JK~AKt!%dZNHd1kIzYSs+0i%H>i{R)cmy5jT23ESCneC zDgvD~9ZTC%wM)%2MFn;1q9Ume3*YWdf2{BLXy}yW8Jy9r6bR5uq7V11Q1AX)PbkNIdd)I-u1gA=V z1JmcF0Hac=!+g0WD*;p~J8klDoyp7(VqWV+&3VreJoK1biJTdZzEpq9?ch}jraUUYwUR$zpJA{W4 z+GDtQq5Xf38_gwB6;!(~q2zrs1qH@wSivK1w|0KOZrUU$Q@K zoibIg_npnnnGy9e@|aQjk=~Q^gYwj@A1b)Abyzoj%Qww^y0gm1S)H^~#)_TrMW*e+ zJdu__;rx+~R=}<5{@T0-MZt1!k)%aYOQ1ZmKWc6dOLTg2BIj@5FgUTETy;tJA^2)j-o)4sp*ty=kqBCSQl!>|Y(+Jh z!E#&NTZw`~0bL)Ma2!t~g`)Zp;?rM^eruoJU&Qh=g|IucZU@AiiNjCX(P>b#srN6I zvWA0x;ytt8qM}I)O-MTGKHGn$ySOjDfB8N=7XZ1E%y}2i#dy%kz+d|OpkG- zwol@#1-ymL->Ykg+W(kr)BA;)Pyu$AgoQrleit zNm&EXe@GqEO~LfjVmfN1N8m?P0{EK*v9QU;U!RZ6kg(#eapuhKTV-A4E+tDKiu^{} z56ZdTR|=#Y10rwu^eN{Kc42I}v}3Z(_a8$^{{WbAK{8aZswkw?B|@L$rriS^H0q^W zT+bWDbqVb{J}Z<(XZwYYXjwvugl-C{Qj{2$Ir@i)Mz0yJP5e=sp*L#pNxI$teE- zity*V+~<*To51(M>O)-%NAXu5g}ILy=Z-tK<6R3jI~n0#+EfsJ zM_jp&d~T&zrCjLd9Ta(V#-p&OUbYgDquu*rYfq3TT1#A+aG_b7>|Ohz2K!u z{Yli#!0M@-!Zcg9@^<=uqtt1{nk5G0l$3yyf7zwFFj2L{CD_*$>=b%*^wo3Ysa6oo z601|2D+qJx_#aI+>vZwDst$d=-+GGVX>rpwBGRPxleG@j1avx77ZO_?!=h^H)*H5M za^y#swjOxk`-6Gs7UdJa=(MV`5Qi2_71p3QD?)?uI`p5l_gGxSeuc{be`3CH{^jg> zcNMrd*x4QE4x_;`xHs;}mE%(2TRE2;7yDT$OZWI9k`Ou-Eu%nb^50hczxLM*tTI}< zhqC8>*MIuJ@)nP5d06*1p>uzYI{p=1zv5nX;|^`GN60w)Bx3au@%?5{q3<%fRHbM; zfX0ny8uWITPmnJzIr(!ovHIFV8T{Hr`YU4%w z64Czvk+S*5OQK7Ti6zK|UtDKB!`HI4>!%}~z@}Xoa7oTe zdLsLZ3VVGw-%^;s^$*o7UcG*R)NwLmy_UBNa+Ls&jXGeDv;VZRJ+s7Tic6WUAg z<{|daPSp+d8fz(I>JyRQ#_okFgdt#W^(bwkjihx>R28SUA%zD8NKBT92g)OHE1N{LqXE(003A=yQ08q@%sMW2{o#182;^_K50)=x z7}Oi=%s&ph-Z5G7<2LiQ+k`0o^KY=F4&*F$>_HmS@SBNY$S^dT60=;J(siqghd{qJ zd^W?D@~;$el!&k?aL6GUFqnFbd%+F&Elu}z0FI+=dP^@OW0s>;iN#sH?JRNWU&%Ij za(niUS+K?~1Wku5Hne}GX>+uLPP^+b%~)o{zJpS_ceE3IHz1CuO0Kr#-XrnXHrjHo zRl4(ymcz;xWkW@SiY3cxJNGs_(A$nAgWQ^T9q4*%l3}^s{y!P%vLb0!**(tS*u0M* z^(1%|ri-25dT$lD>%>nhalAu^Wj8mB^Q<2o$ZXFd;BIS0#qV{Cf>!ZTwo<|tfnQUw zro9X8wtj4mJA9w073BW_ayb39hD5dx(th8_*X~OH0B@d>^A%{f#fpq8X{EzQ=7noRT zs8f;gta~VB~ys`k=NIXL2`bi*scHi>Vy!sVG4@5k7X)ZA) zpIU#P2dQYbBzn!FBYgNE+xEsR?69T#J%G z88g}2sQc5a&O8S3mgATmK$wrF7=GuINGeAl5}c-qO87osC+b z334@Koab&~4yi5V@gIdDq@9)$J>=AS(uY@AiU-%lWcj3I+JoSKkNp>GCtK|^flSyf z$x6i(m7yvhokq0*wOpsxA<8nlaL*cCpG=7uq#*K5LXyL2-{Y{+ZBiX};3yXDaporh zff^N>J8Y((Pqdo;MwQSNNiTBcw?FbmYsQ?dk=ZfZqn5ioe`#Whpd2hTo2u z#Y_W>Rp%$S5<=MmkGZrH1u**GsWX)83jI-+kynnE>$ zN^eva57smukWu$0NpX}gryiO zdLaA9!$B!(rFTa}9BG3}98yzU8ydy*;o&1?ATn(8_^YVh*bJypr& z3A`Q37K?^u#{6>U4c_5>cN}Gz9{ru*O-YVF^!`I{!%=O+!J#!*7fy}hpixw(oFr1U zuBEDm4OX#g7xVSfm^DQ)P9#*Oo}Ud$RZi-Q$YCo3#>(rk-sR4h3hjm4j6Qq`RU;R({oy)nn(rj13T}S^H?|p(_XT(qtMTQi5OYlCGkk zmX?$nQCDQb2c>E2wwnzKP-BrbPKuh*y6K9gdTv;W?Wt)?alu;lKpv#(M&U$4B2tyY zJ?;YyJ3iF(HXapa#~NA^phuxl8cCVGldXb>RNZ~f{l~AakXv0-jTAw$(w=p;le(5b z8Y0A}I)PpeEyo+N*qCu?9`uy=End5gT-(M&7B?kvx%5l<4DggG2|ctLk+=;MSYoPz zGR&k~*!J32d(Q$BL(;m5aW>VUu&_|lmh!32&M%o3+-6@dQ;*DtuNW*`rKl{Bj&vh8} zM$!6#)6Q8HA?LtRumRUi+Zi+N5oYKdhzD^?pcw}R;DLH5U~R+Pc2knI9~ z_>tmF9%^=HAm?zEF5csc5aFxz5Dh|(g!da%Yj}5RRhRNli8wQfc{2P7mWy`c zlmO?wO{vp=j*}dYa&l16xSYGegC2Fof9CMxr|Ckd!PM(wxwE z^Am`qshcB8RZU%SJ<2ups;j1(Bb3icl<1x8T5WOCT0<=~-6&9$Bnou+Y0p)~0dBw% z8F;=cAu0}9+jOe6S87FnhL;`7>Y0)l+=#}(@dk-nEV0EsRMn=4^1VYqytW} z#)6tjGzl&`rkPLAP5OyQEjEp2f(vaWIDt_e{{V)8K#fAS_+3Bt4{|B}f4X1zY74(o z)!BdGA9}8rDwj{polj*ps)>J6mvPzsoLhQ_T2fLKTKrS+)p-4NR&0=%)GsAn&M10% z>SzkQfLyG{is?!+4At(=^42|aP~MavCs3~Xg<)&Zs_{9%xlbMHr4mQyU0lFL7ID>c z%IP13@>6f({{SQb4czRTsRC*I4N)!qG(D0|wZ$(1sfM z$J)QjpL(mFZrzo3S< zF12}UWR~9;__3Fvs@t!an{1|9QbK*k{k4P>@$FGM>YmqM@3t%*it;DCII|94c^|A+ zzc2c#GQ*210<<2a>!>E@p*jOdZ(uZ1Z!D2f5U!%v4*LwpHMnX6Gxap~!(Z;Nt3WeQ$_aVTAT z$oW@oBhgctDGB#%yAV_L_4KZwPX$W0w7LOE*4w9ePt*rZQqKaswV&dodrjG4O_Hvi z4yvv#ma4+)g-O<{-AX>aQAupIw)cugx7?88QAJT*YpR*8^;eNkP^1e@)VJ6@&4*p} zQ*mx;v{p0dq)UC=9@AYwd^#;{oYhZAS03)#D}7eY0-VVzbAXP-topl;k8dNDTJZ_?snU5|5pSPDB#(f{HrBh+) z(z?!ExRa=&*jvy9;)x|dfxpjHFBLUbG4-O7Y{**DmzGFKr_@ac@j9t=JKwYps^Qam zaa8mR;m7Zd=Sx0Qc}XLKu&ctPaNmt`9JfMcmph`}a>cjXK5^~(RwJbQ4Ie}9dedHz z;e36Tmeu~^xHlomI`-pUjr)DS=)QY-Qb;bf0jh?X-9xQa>#OV1D4ehW9Tb_vSMbLk zWiXbZ{Uj{1-FF@7JJU$CJDQ64Qc$Z@M~Y@R0o_Ur~py6kWfZ6my~+}J-r3*MQiK7{u&6-Pg=rh+b9pq zK%~;4dmhap^@iGzl`r{fK>q+e4ZsAFC>GLfcUygba~(-W$K2|j{$olK0-M^mL+%dH zcl`c3N>iw$EKjvbl;IU3pO%%oS}3$ca)~QamfCCBKmk92udbVGi7EEBiN$Mu#Brs` z9mOrJ`jr0wyf*3hYQe72RNS}xfiP@$S$3(7$&RhTqs<+{&_xJ8mXyP|P^WhgYQE@x z&YyDYca%H}AI0Axa-VOP_?jQP!}+`FPrZM$L+wA?}o$BIu>rgp&B;&4v z=hcg$?6o&i!H~0~^m}`*zxfg61{22kwij^Y9!j<^kSC>u&Bx!9-xx$y3(8UwXo5O^ zI=sMeYrFW#dn!9F3C}&#u)?{mj!@T*p>hw(pSfo<-|m?HGlAuASn*3!-?+a&YW|b6 z)UAp;>Pc)<(_Q}nfqN%uXKlWA)x~)G-7nncLn(SAvXr(K^&|VfA8*B1&KL5>E8}it zh0A<%bQXK{=BF>ST--~M<+fAWu(YW__baI0wbr|ndvR%$we)1r4umW>4)>!WgTMNl zbGW~#I@* zw*tNI?MJ>7=VN!Q&5vlVz16_t-g~if1}e467Pc>OxevtPR2l#z0dCOzyp z6R=omYA6Tr)Z=0n4%@UYJZ67)xzOX<;$XH5Os1ha{5cY?PdtG;w!N|u z`bq+TCWKR7vg04M+gC&#c=|Z>`LCWF-}lj#7X`T3OPV^9;o>w`8^XUl_~*%4@Gv`u zbj`Dt=d&_Z*9U2b5X2a<)*CLdA>@|cO$te(stMd{j(el+JlIF?g|&tDxq3?sakdGt1N7ge{rJ7a$9S_N2UR)4)DD4*@v#}YYkj3(Nc%S zq|rs@MGBkl9TnSJeDuY;E#Dyy&?_lqyoTI4qNFWiWss#!$E}wZUbkNIc9!f5a@$o9 zrjQo2EnrX)>0Y`-wi_xz3S=2)NC0>sl(%uSF)<{78F3v~(x=wCueZA`I;k_+hYb@V zTqmI@p=l(4D*XugYeNO=UNl+B?Ht03Hu90tUzya6p-1%!8L0`_*Yna}7(EoI=1NJC z`cF`L@7GZs5-sTlNUK7H5rcIOQtmJzxm`7gGgwx8X_hb)D*_hd(QG$8l+c!hnPNOS8O zr0?7{#D7m-tJ$n}?M9&n#FyflAF01XpvP4>;*7k@GZF4aS%)Y2TuG+?05R$`^JW7| zB=1S$RM=|_-Xe$6pVK*!RFUygPUBMB7&_HNGV9T8BFzTn6=kI*=<-Dt2H@AO+Hto} zgw!NUY}o}9CF~KH>Qb!|7YRduYNVromai?j5pbixRZ`)p`ZPwNMak``_PxbD?e3cH zDc41FHLRn+r`RqcPunHY>Fg9wh*)*A+gcD0@z!GU+(vx873HnQ=IWN004RI0s8kP8 zP4w7(6oSh1UZHLS+N2P#jWn~UPlHsX*SO^Y1fN}3+$9dF%K?^{Lf?I9X|_RA&aFWx zLO`i1Qm6m}sXB_sdX5bmpMq4bY;4@^0{Hs>0JwMV@nOF4j&wh_O zJ*_E`3NLG+Dzn6KRYR+A0+-a9gpIoEyKrs;aK;O%1w0oYA-05wvIao38g-x;D!J_!R=u8(rm+nNO9#Gdy9Y3g>{QX%M}PEAw+$*lU3b$S{Tvu?$xmyQQr``5o zU#uvtDK6U}6_NAp3D%pB-BztwPHAiejg)-8--_s^E;x%iOHIaCi#uxQC=05B8MYw;awj2&Jka|%IuG(r`9lO6ux`UTp#n)8X;_nl3 zd=r;2?6M8{srG9mwYw#?Hm06iN@Zy`hI1qGJH$22#+c}PIrF89SQU_^2 zo~`>Vc@0Uj7E(s_udaz2Doq_U72BqWDLt1jyxiiPl6FrAII>wEx?7))8oD_dqUm96 zor;o*p3*&Osto<3pQ~2ahvk^UTJi-`7sHGbBEMys2W6?~@FUya4#<$xjJ91+DF_8% zRPEDIF%Z42H55n8ZzA5!q!V7osu0L5a`C)|>1w)1Fr~2j$x};DZBiT{C{zuPq0~E= z$a0@;KKQ{2TaEIGcqVUT$RauICNt>$$o~LSDo_gt^3I^HTX}+qi+3MPZ+${z& zPp7|gdpTEFYb?lZeu+JGmu*2?xdlS#7XV)qHYe=Xx~ZTA3EqcWOk?*oa@_T?W9poZ zT0d=;WfoM9)J{U3j*>c+16^3f+$yO61UM=)l)-w#luT#0+Ilm^NCY0CRT{_N$LY;b zx^_LnYk#>~4oPJhj~UqStspJ-qocXbv{F+;{#v=dXQ>Es#$x64FAEB#Wh4@l)h35o zhOUw|MUPXaQkR+tEYLw=&;$kx@+kkuPXSjMg{TCeuG?V1?RHlyjVP_CY} zcG&?;N^&Zd^thSi7*&nyKFtB#ZBS(X0$|21`XxZfzE) zJe3X7ulDOZD?oDJTEBAwn)t@y^e!@#JAaf8x|L-itrmAR9<@juw{I-Tk0>X#E{YSU zFJsY6z{{l*+mksS(RgAmKZaUGj}?bPlo3bYSRbjw{>?#xGSRBLmCYIj3yBG>PL8RA z(I_z~pq=(Qmcl5ay@se??u;U}RS5LamjuDLD65y0C=e?_(CIsrmcvG*_1t@pS`SST zDQ$>^O>>vsO6~B}TBp4=>SnqKHR+?=Q${egRM&|=SLLC&r-Weusn4tVh|-LPsKg-v zr`;oUpYqdWr3M5NlznP+Oma{OYZ$Z)izBmGecxyMyoT6IooH5RrAr`SsYckz)4H`; zj|^Hoe#>k82uj=S02H7ndTppUyj;<~u60!WmE>}?28t>gk=;Y-tqZ(WLj?km zxbDPnSLltY9nCVJr#>G}`j=}KU?-Hn=5g`0idlk)bi|YRNQ^9sP%fV%wiT9W^DEcCO8vSP*$XL)@7}A#J>f$ zypguh{8WK#v&6ebmSs6fN(xhI^iRNRePL)A#m92lH_bP}GChMGZm?3bxJbH>++_f5vosgpqGD7anyw&3Ww5@+*AM^N!v+tfJ$pgD`Rnv zge}B6m6pLI16os2i3#Sxs;m5om;V5By+VQR6hrq?mq(yUSO@$xE##tjL|1b-1Dg2% z0FSt$-3f`2V+L3RxS|qNLN_!T6P?FJ79&ErUFD}a-1&ztl^)ppj$^jvpW7uRMGmZO z-MP}Cqe4}~T<(EV+4$~7kusZXNX9f`YV*q5YL7~3a=dLq2%I}=o%sI%5Ps4s?Yh%qwopT~ z$87%qD*FLH#OZA&ibGhyC<0m%sGyd>LTk|?f}fUjK1IjHYH?Xl#<;}j-_3bsLoNR!k z7~B%|)dx}rJZIYdmY68-B{{S6Dbh_xQ;IkxZj#+U$Uc*oAR!H!ae^ZET1s~5+ zB$&}yLu$q-gP*fBS?o>NA-q#I@v?$Qs#!_g{(3uMxl~`g13;>sk`kOqbnH&1QD)uM zJ6ecT4#a6d(G^mHVvr8hr>>k}NOq`X#1$V+bkR;QG$y`I^K6!4q=L0oN*^sD{S$JU zxiVMROru(8AhSUQnj{cp)|=NvQ?*vZ416nuZb<4InrePTY7D2&wN_{RAy>PA3U;a4 z1nhM#6iQDGUE*TER7rvlYci(*|H$@oIs?>P6q%(Qs zH`cHlXA-@J{vN(&l?kYO(rOiJ3WWJpKtWw@{kC{DBxJ55z&m2je! zcnzrU^G_T&rqbi?U1Np3ey0c(G^&X-6zQs-OyX=G@>)-C!T$hr15fGQ)3Ua{W+Wa? zN+<|vvER3?0oFI$vHt+ZU4!1qfBa$hCKg@DxrROC6|0G?*N1J~Wym+jFS*7r)eyzj z?Q27A#db{vRqTe7*eg$@d^NH*t#qZws^L<|;ZP2ReIj6dTWs=U?f1eM?5QFp?Kr)% z;fLP{?lGpns9Ng~x2mj`Hp}$mQc1 zrJ36;;u+lw1ho1+9A(ea;t!z*@uAnJad}&Nwui9PFB;{nrnHd9ARvT>mcZ>GRaE^{ z->*$u`gJu5h`ky@=&EW{RChN-X)5^sT0&&C422N0{#qo{hKSN+cM(xTqHF3?1{x!2 zc}rJvm)uWMPNvmIF@Pwx4gdhIqkxF92!}ofzb*8BnMiXG!D=+^>7ZLg*=R;kp_fWnsu!qHdu`JjT?@sovU;>D?`xfAf!2n+iud7Jv}t&kft8QZMSQswe?Zn z#BH~?(~3LeM$q^=RZ5QNk>ou}8tD!+QC%8QWfiLrlHMjuWfErfo{5NkQeaP9MO6R#(#DFj^JVc0CH%ta&NH<4zjMPJ>b*v8qHnVw3@4{ zE4dp`)T13Sl%|yYhgm|1DkxbC8`okq=dVUk4vE=z%thWUFC!KLJg zw=(il{Ah2fQ5szZPkN0?Qa7Nkx4ch^J?|2@Xt3Oekg|p*omb}zXUyNaWw%+=q$*RF zDGomS>rqnH^U71JT8ZkVDMXX5wOxm3Sx=+d+S95|&SV$or9z|uvG~;2P2C!)Rw98E z#BVFLJ8)pR+T6BQg5RY`JO2QVqsHx>6zhlswM>Z4{{YUNsr7aI^w#7WQvJY%HcfgD zr%k6AoEUB)2J?;P8HMIbnTLv{ks2aEE0Xi7*i`xrD^iMD1nuKo{{RBPF4D4#-MJY& z=#gY1&5k;O`(;XT9gV5dK@>sXNGe=R1weA=zjK=^e3e#2w-)Q6BeeleD6E8}bOO5# zFiA?FqSNJH$4<5NOgXh%_j`Xj>2yi;7N{n@bZMkFa=s@)C?P6-IzoAA-0D^ol~SI+ zJsC!}eYux5ZMtHr)dYTMokG((NNI#hKBq19Df(x%4N7V4v1w+x49 zkNwiib&qy~-P-b_tSlgtQCf7=wp5i}aMpxR6?CEumkIicwi=kxLsZm`&?H;ah1XnF zYft=i;|d9F$v?Ei#9{vc&kH3(=06QNv?$U>9Q4n(lIq8Cs3)(>OtHiyNHkV+$BgRj zhi7(q7TK=DTHZhoHnQ7|sNM;u%TaAeiB@`7tg#x_hIGSul|eY z{tT_ZA#JCW)O*F9)lwDIrBnfH1F_&X3rX}kV;Ro!7^iw{6>8xA>i*ua`S}gZgth+w z0<10f?a#vY?hE8@V!iXioVDUNbV0!~D?(8zi=*jtvDj|4MGA!wkfhUpU2AN;=gQsQ zHr_T09SK}|=k}L@#bFu+xlTdzo^R`QyO&g3?5%lq1-oyK0X>itE z+003&Zj@FB9r0=;Jew)x8#-LKO3oYq0N|8=+*Si>DC}2jX{PG+S6yc>d6Ok*Ynz~2 zOjosX!6W33LJPX4l%E@U>cKu9M~?DzOOw`BQrE`wlPp_hp(_L=xC)e=y+E#=JeL(C zHpw5}nz!NE`&o92UL=?8$0P4Egu!8h~_O>zEB+Rg0)y%(Qo!^bLms8{{VE1hn}{{N3$+6){sgT zijo$dhgeIyZIT*vU1r8lS|Cn^INh|{?Cv6U!a)9lNe--p6d-IAqqeG?#}h!Un*?kX zC3B0<`Mx3JxLjD?Lr!wvZO)~%&8ln(PNwSQ)Q@$r*l9sZA5P$cDc4<(+W0c|^Lp^O zHLdYpTm8Y_@vEy*Yjhm*ApmW!TKqMof8}$77yD4&xp^1-w%?5mV~#G6oM#ipZ;lBU zDXlz$w8(kJ5A?gjhh+#-PhPt1cfYtBW){UJq;)II*iYI%PieY=+ZuIidykn@r;NTc zc}3$+F^0;{@GEqjM~_{V0lY%R9f@~IcGV<)-EK>w99bvnaYuRzp0(C=%YTWl zE#2I%o54M$``|IR)w|&*~*jt))=XhjH*afyi$@rS692-~515s>g%k*^Ugl zvAZS*YqaV<<<|^e^BVnYizT;^`W!+^oo#7a3W|531nuzGp0d`G!6|Ej0edqAl8W*W z<~S6zkjo`Oq1)bfA3ao=O0=aA&jdcw!Ae&0NA%LK(oHt4Jv6wcAudRRhNv52u588a zxiY|#g%E^C3iiu?%UDkxJz=a0U@TdfMW=mzKW)t$ZYCWTPk;~i9hhFn2X z3Qb2%T}0riUjvxE3vs`Y_~}xF)NUx`JBdSy3Ojst4eaiv!#D?2J>v_+vv+NBFHlhJ z&dBOtwOs%xd8H(UcK4=)q|$_G#$+aAHdo40Zt(WE5A}BW$3?{7DlpDmk{L2V!9HTn7-cZ_a9w-E=l)D_`wLxYyW?a;_5=AMYg65dk> zB)42}cU?|HZ*eYLQyFD1ccbe1XhA(U)n;-lX>Hue#adMECoVz@)=g$em8PPFeLHwv zfj@FbT{B_D0+AmW%)n2se1QaOkrKBZvI9yE)H`x62rn^)NDiJ{# zw^>?nSu{{Sk#Ej;NrXqat6Gy8?qB1QV@o>(qf zEt1syXpGryEi~l06DGPDVX$6O5P+o+3e;#lb$i5kea33*(T_!!<~+*Z4QcC02_MaT zqWMAhIr3}Ff9`n0cNaKMh^M%(+h8^+PPaBxRc+aFp3zF)Dj%s0_NUY88ub4F9QKnR ziUH5_y?LiQ?$z#7%FiT!n&qdH{AFhzH0QDfwV?f?Ad?MgY$TweVAK?jy4d7i*G09W zfGQk0ESquEmB3V`EvyRk+@FrJV9|7ToyuY0*={k(*m9CPh@Q`B{{Y{;(r!xJ{sRRD?gLS zxPOpX6PX>p!|xXS-f!%Hp?M_Pubk=4gUc0lH5=GH2uler88w0kvCCL z;%Tw;HPZ*8p5`h0F$eyi!=%=saMAU32AgWGc6cjmSEG1q;V(1_YC#qG)al41s%m{I zqOMBXvMpS3yvphh1*k9NwCR%L^LrhJ_$q?;J^fb;j~MZbl(6?BSCQ$ms{<;!{t~ zx^b_nCqm|YP`2JSi8%&W9`Z-C>dZkcFE!g=^s9gI(RqhLlR?}iIVYd7KG#PSty;-@B%aIi(r5bvh-)-YxPQ zG?3hsC=ll9P^aOghc=27kUVNqy_MK8&`}wrFok}wgoO83<4(G2f~mHLR;4#H9ysG{ z>9R7MUM%it;{O1L9AvwKW=54VGa-H7v-i83 zr^D(MrlO2=d#V+K`3%lwDw-Zy;1>H&7T6uO0;S__Hq3RC+@RXc1x}&mA}!Teaa*8Y zL)}I=TW#`3j+Z=r(q5ltL*KSNNLznc{{X{MnMrK^DZ7H}T$m%FDz$L?qTv%X!DbJx z=NZz=O8)@j&5CsR>WdvETIkB#=Ga>HOcisuM0Nz(5gku!(blHcJ_RP4&0I8x5#YBr zvxy@P@m3>(av#`aQCV?iDUbqEYqxO!0LNCidy*YRmgH8oX)5jA?2_NitQWX#62qu1 zP9xMFhg!J7^(wjZIUX~dMMB-WI#QKoH}{{TdX1dN5t4bXE=ikaSGt|iJ_3lu0? zzt33d1s+#PwRY%%NiKS5l!Ot}pW2Y_s|QUsln`z!=dkObl)5G0p8;99idI1V?FmzH z3R_?*gjA-cqx05tIgl|51);*&He;$Ko3qDo}?gGCRBKDwO@jd;;jz9QbKpHuPCxKO78_P2|IG$Ymm{6r;+JKXYMfT2WHS z>+$Eb~|dTbwbb)ssyXFSFVlis!|NPPQ!1Gk+mHWS1J$7ODGip zOF*>0foWGxx+FtIiZm6|Zfz6wK@w}%O{PiJdKFH)=)zMMp6AO!EowdS&}4s`}Ns6RJ{xAZh8P+SLli*Kw+-H_O}CiSDgR zt013kJNg}Ft|r`8%EH_=sH;MxkA*3w6^c-f!rghc89`f)kN~B6?dhb*?Y&gXYhA@% zZYtx*`0cqji=$x6bw5ZTeIu^3cQ+22O{1dN+1s+-J;$n&pLH@^D_7Fmf8i%>Ycbg7 z#@dyP&*aRcfzfP>fnf?lT-OW^;D9c1;sV&Y)$77{kve2@gzgXK%x=qCh#S>SzAEC>suN$&=cv*L zDJ*D8*8Do>B}!e|L2*i{R{;J%0l%rPpM|E0a)1<_3KNl{w;591G0*@@9_q?Jg=wPL z2u!ifN@kC3I? zt?Df_V?5~k5>M1L(6^yVlHk)oqI*@tExb!r$|ScE>WQH>0BOfu0HVVaoKuxhbGbnN z_V%dFpI+df^3yJt!ATGi-6lI=Nnd@>BwPKv5#L|-=^{dn(F81b_;fSe@|7{s)~2BU z0FH>c$e9CD%zVPtZ~mMMYCs>LfD^u(k;DqAaqSu)-_5R)!o8Jy3I70e3e!x94LT=? z2%o<$c<+$voj(#u>!8PinhzkNR_kl`YwQbKM4x@Ymk0x7gT9pJiX|}B)K`)uHf^wD zL16(6Foc98ZO~Itp6V?W4AB+1%wpMZetfrmrXwLvIJ4*yE2Rp&(KB>?IO@60mgI6X zY_eU=!lKKoD2CboR+4t6t)Yu(qQlQGQd8R557>@`Q>Y)uPf8h#!0P-z8hyEo1%{BKw9a%&)RW9ni zVrz-FEso2+M_-NRHf*HehqJ_K6P=lq#Q9PvA8Jl4Y+g;%T;K_Z4-+usGYNxZX+W zv~*V<>EmaCTxpi!llV){2|UfnjNKy(eioZ7cGz`Z;7W;?83_pO6(AnHHJ`c9WTdfo zuO87wzQFOUEIDY6w58-7J}MaU=`$p9JHF^4#Dw=$P}C>=y0G^05N(O79`eUa;G%wB zwm%nu_{#L-WwvKby0XIPKr3(c4~M3!uqayTU00{bQRGx z)J@R{7fzKLO;UED1GS*8f9rOc~a?~?!eCzVmvYUGml)FUr ztvZc0AWXTMp?%Jj=yZXiZ(pG$?XLm!eDDcO4Z9uaTrDUAAdTkZY!%G)sz73XpAc%8Guf5vr+L zuI=s$knNL=QAK__s-2~1Z6?)8HoKXseyVD^>I#ajeU-|PEw?46pI*aRyNGDDHa7}F zw%v&d`o0>deYjftYoz+O-3?Pkd^JyX6$;SW+*Lm9cS1@?S6x=VC84#MNOrlweWWYu zG^VNUSyijGc&JhwE31rzcAAsv_-i>dD^m=lDH6qy#&t+T$}KF?nx~!+t?j3wQha$J z2Go;O^#{{Xp4tU>3#z3YTIRJQx2ZIs)^{P&t7D9Cq9^1pkvM0ITpOBmPJ1jfYQ&*9 z?0B8PJ2GO07>w9GRO09;G>jf4*LMN5(IYXD5yolNet7)%=7%ElUo-MYC}npiB~Rry zA8#d$96gO&-1=CAo&NygFpv@luIJZYg^0&*5Mz5TaLjRYy+>+FqCtP!FKYI|4W)H$ zW2b2~8m)V(>Z_Q=Sw!Q#$yAWj!*r;AplhT`bWtJ{(KbEBB&FW8H2k$0PDGe`vs8UI zrk-o+o-GtkX%zjcmFhh|Ei#!;Tun+i24uidC@XDCN2g7-V-sq`*=0>s4BAL}WSzT? z-=3+QXzD9!=#GE0PtYa`9V+OmX%Z-*dG7D}f5%HwsJ79l+e+QH2o&wn6sG8eOPv87 zeRNbyFR4Rll96W`_edc35%SQWMhcZ|klJgH@2d#zKNFhqS78enSzsvaY`T@D19JKmRG#y;``2A3iF=clwtXgk_WV~1_apZ4#`3Ukw_uJ= zwQq`Hc;CbPsp8fn7muvjMV9X#bl2T(JhcYWw-%xaZ7+82q=8U9bzzsfk10Q@=2WPB zLjyk=u@>!+29Fg#V%))p$IPVS{F%ye?pZ|jdvGl99{sZxhYeC2??n)$EB)*4bClu#wq0$KNJ-HdBE%$b?U~~J;TQDcFO+x)p?N8m`e!Xzqw+CH1)3fg(n%j z*;5H9qxT~l6~T<$0Z$SVz3a(;86o1f+`<0bsp%H0OozN&dI-1r?RUt6qbmoWz8B;dXtucgp)yuMa#~`ryL@?qddrFeiS|O2 zXiW*!oBsfBGBAzLpbDo6@9!VHkVg}Vaqz0UZ)SLFSR*%uUapZWic(k)+z@0*lH0#c z5*OV{)}p;S>lyS?wtp=u7iga+3A6@=j!@Lx8^rEEnPJLvNR2&q$|ZYvoXm}G-6R** zL#OuQU~}3?JCcwotLEKg?iyIbKstc2GvhE;Qnp!}=XXMNR+Sk~vk}{F7k0N&2h%8r z5c_FThQNl60N?V~PF!=CNst<@lMZG%7z9L6A+34WyAo}^`{W}oAJIB%Znm(84M71F z`1)y?aZ-vqX#qVCB7xOua5Pc2Yd49nklwQLBeuK5cN#yqX6VB%p~TdG!%K)Fr1T$_ zukjM)tzEt%LdtSGd?oumIoccwiLs6X;dv0DW4Kg#pRlvP{lYtK2@OI~al*f8RZq5{ zl4;vofvah`jzm?`5L{Iahqic<(Ht!yjp#coC&yf8j`7p?*_hjIL~9{+x2_hz^NWhh zs7G(~TUkAT`hl#Le>)}ZH+EeoiE$`#yS+NlsYl;Rx}AqjX5R3&8-mf4l(@AA6np4G zYq0z@l4uE+H1~5^D$BO6`Zim%h_>B;dg!iD*5+%bQx5`g?M`MK3xB<82sZeDe z!>X>I5gdUs=V8|d5VvE2EumiGn)eUOSN8e1-=SG+zBelkMyZn`E78_c^9yL8ngg-x zwyoJKPeqrW(n!vz+&Ql_drE2#T{`%h;FT5OX`)?{Ef++xE>`yCzYHJQJmNx&N>HKg zBvfsvfzv{Kt~5_yS=(H*&d^HbKbQUq<+;pDha_=%Mb_Z}xb^E9S)`c_Zln8hb|inK z>(f5d`_&8-wYkuAs`GOJ!8<8u3Rz_Jbpaki0axhqzx}y^;OX@%~o}V-f~VI<)9& zt;CDTDEOeHsZOy4$`Cp(@9|R8yu2G{#BtU4PjoZqLF`inRX# zaCR7#pIPNdWw{R9;576lt+X%LaRptgM{qP&`w_mQdq-}=kJNQrAKb9my9XcsC>z1PFX0%%p&uVQfhLGidR`sr{oK*Nx>{{;AbC_>( zNlx|Kb4@*L%~Eavr9valEq3M6*Gl`*8drD6u8Q(4R0xWOX{z?L1oF)(63qk_XcQ0x zP}5M#bQR?^drCFuX!vONlvG-;(@PGjBvIegKMgn%!Kw^sB?7(CZ3#fFn;%d#`jgX2 z1cOwE5lSh`5ArLPur`qr)04XOy|5C9GC+`kk?1@3r{}8}b5gUqrMTnv^BU5HPqvRZ zF0VF-*^Uze^%VaA@yS90d-kX4U1|JvZyzIR{Xu7?;tvL`a&)ugFN`@V(r;XABO8+C z1C!vNWx!8U+`^J+t0rab&w41cU~ZT?0-ly`LgNSBLBRRG>xq;f=tQ~ua^v;bAAAx( z{Bji~AqH=D7v*z-$+%%}cR=SdNdr4~1) zn9aai`m0_qcfJ7A)F!qIab3Re1>mmI+leVZfz(WK^C{Vg_c3%{+mmJAwuIyxf4Vym zKtId~`0uTpLPFYw&Nf?zkVxK*4yn%m3y{Q)BuRp{+E(Jf z4!U&43W#qTS}F6#&VR}{0%YfHObIb(P`wpKgS>kG0EVDm;v=7`on@Gk1Lf|42 z46?VoDcplW+$ayH&s8>ZJ}IbGz3UkybsbkUGj29-8H$ISk=#{6q?7B?gIY5E+N}1{ z-4sLZERvqhB?7dP*-C|Wu9R96+p38gS8keYrL6esLT6$1?}O-nRDLwo+r?P25E99iS)-3Q z5ZViiQKB12B$M(3RE#uR0IVk(_|3??hNk3*!pUoLgh`Q9sY&>RoiQxHbx*?{<$CZ> z0{HC$GnqZ@>GB93@&dqUsQPNQT#XKjb1~rcR&ob`94C2#VZ?%hToei}0ImH-nK_pO zs*xTLIOw^R<@bm8ZyqMazFQQzn0?88h;d(1m~y)UuTXVuZG9Z0PO7%%#z>v7MMuWq zSJzU@YM81CVw;2cX`s{>p1MkG6{d|kvq1%#2rQ`h=v5sS0Z-3BXn|U&P@OM&Ft$%Y zq?1%fs&r$P#oaC}Focx?p;~EBMqE^D$%7iMO|?wf?X5JD0$D%@@*2>B8rm#_zsmIh zat>0tj=2^)zfn~j73v15?(TanPBzd{Mp4@(Nhj3rs>dtf1rnQdN!V2X0G^Uj$5Lpm z9}Mxoz5f8w9p$db?R`rm;M79v=Sf`@Lr@p*YmMY=d)2cpT} zNEcL1Hq;KOfjg42+lf(j;HJ`}utwT!&f=W(8k({$*I6|$PI1ch1!w;N9XGwe)TId8 z5k@4t?;$QapcFt+ug6LyIOKh$XpDN*?YrvWtx+EuWZWHBNB=Do@7sbBc% z_B?K)T!TdrTyoZ;JnLaXsn~Dpr*-L18tq*lWDJP3Yt8oL@MwyGgAE z--e__M%5IG6ykp1Y|vPV4RlpLvYK`N8iVQXa$5*sO?fvfq=i-4D__7B9-o$#BR2F+ z)RR<8x#ff1T5Vm7Dg5d* z8~!?tJdJ47(Mp2WSq%Url<#f~wqiEom|JX-R6J0ig?zyxhw{`*d;58vNZcJy1#6MQ zU0T!DVQ0)I&k|aHd4HXBbsgz~I~!Ft3L?6CXNIU&j3w1pMZ!t#KUF&VY7<>mp=+qC zFF)q$hi*nG0our$MNd+Cp`pKN{B<_aQBdTrO(?6*DQE8g0Ge=r^|RV_aTwC8DIuv+ zGiX9VPjxA&iZxM&nyr4X@X?Z@yj31cVxW$PNm5v9n|YAMTAslqDXz zD1;sUdWhFm1?eQ3ohb$8jRl$rEwq&44bSD!s&X}HIOKa?~a{AvJ{YPWtw{{Wt>BIuyVH9_0rN}h+>h{~T^DN~LiBBUWAr2blI&LuY!YEyCG zLX>M$>7}%3RELTc+7Gj?Hx2BUm2&6O7d8uUp+Y3kr7Nq;r{mqPKYP<~;MO!RV*c3i zY;k<2s8+x4Xdi#ao&=CkRESwK#ZG^xzTWFY_}5iF%1#Br9|d*$o@H$qek+|G1WZiJ zXEa${Aep%1{@QmWnk(V2M&<=*yisRRN}?CEp^KFM)b(10NU0Q8&sNsPj0%q7q?#v( z2Q9eV&Bn7O9@w$35X-7QflKVK{{S_r-c)Ojc=#;`w~uo(QGC_f^IewW@}sVyAxtR) z+DcRu04NIo02=Et?Zb!B(RB}dYxThRsBe*M_pHOk%r-@}=Ve5}?pG&o5Q9rCMs7`e zR$NH!1w_-dn(I*=#BI1OuBCB#EaWkV(?GtY{@qNOz65y2;#2x@xXst%P)dK)3+JuWubM+6wJtS3 zGMj$x#Od`H(_1Cbs(+q|6PZLh7uV|2flWE6!d5zbbSRl~QKT$T1EjR*m~&9+T>+&I zjWk+@Lh0A2)T*Z3)H)oeuU$oPO*x2kudi|ZH02XcYS}Id3f8(5Ni0VD)3*9bkF>3j z@6u{%qeSLvheN*&{#rECqeF*(FP4-FXvU18XMF&fb3!=N)b#b+Pe!9YG+_>!oh&#e zofV@VMG+r(!QFR(N{_niX4KT-RY(zYJ}lL^S4bP zr|SIm5%JYZf=yHI**>aG6{_jsHCNWNsTSEh)jw9=x~`rjtgyIG+ik;f^=uBPZf(}4 z%HvfbTJC?O71LC5Tbivstu>8ibTQ8XYeZKUc- zvs|}N*YNe$dg={Us>XFD+b&5^KvAyx&fMFrO}yfX?QeCpxQ7nP8l#>ybpyhcU|dR? zWgW!!(`xFn;i~r5MJ?h|Prloo+j~pVU)b-3L4y|t{!yA$f9>DIUqnk^uqqe=Ip7D|tEexDCbB&PQ3R9mzy z4kEjc)1Y$XSk0@5OQ72W8Z<$XcM4;FmsWjg~y;EfeA$_fqI`yuOl|Tf7WxSKO_~{Aoz_a)A zE3pE-H1iILR9K0!0W~$-u9Z}DO0Z=Yzyx&l{Pb!$YFg1I&|%7uDN6e2(W#muJ9zGP z{{X1h{Iwm%r6s{NZ0RYrEko%ANBp%ZFp!*CQ{$iYKFGc zgr;KAwMy+aLQ~iYKD6tkwben+s!^1$*~1%;c0iyVbY!62I-?e^{sr6JlnRk1Sp|YR zf}ft9XH=RmreClJnfUH8cgXx#<%^?{vCHh2?>uFCko{m=<~j%OAw1IP0v~Yus8Bzs zsBN~Zv$$u^7U=q{t_aA>1{P6e6D_gv_Esd@CqbCnn|ZbDq`02) zK&=klb&QV`?UX9w9MldqW}!uVpJm4L*wM;7af6Y*Y5=RV$D-CP*$0b@r)UaGZnSB6~Oj@YNzu6LV#>fqb0 zPme$M%8>K0UKQx^Qs>5lzI_bABMTdCg%BTlS@d$8a@lP za4%=L-K%SnoXqpBE1>yz{L{>NOT?TA_EpDzGDo|d;Z`W7Wp48!@d90NquB!!g`<3Q5ZDV4BjCbjvB!30a z9~L<%}{Fw66DG3l18Eut%rHZ0NBUUKBZzJOeDK$SQu6i=l! zjhihM%pj3MJXc({bR%5}!Ip2ox-TXZC|dFZBMrNw!3h(z5f6j zOD3w00P0iJ>eAlcIR!hatGlTqcfl1-dlDQk-9~+x=vQi6Y^^9!3N)=1y?!H4GYH~q zMGj2#ZR&ML7|AhP;?)uV0BsVIdsfS!B!wSDlh^XllN)g!tThPPleC$^uNssjtTxv* z1NKh{jwpcI6nm1T6X{JAwpvyiJSS)4_VS9diKleNE|zsG_Y4q@iI9>MgZwq6iz5`R z_^f^A8d!DglNgmEJw38&4%_smp-pL1P*e!+91RH3QhzHvFU0xU+dnkHqA`i`(Qa{DUHe6Sw}M3yv(YR5I`1CU`^#~PkU?@wqCNZ(`e5)7!^3Ai_Nz+{y z&29n78^rRjv&%pze5RwP$K~-&PH?xkfpRNZml@n%w_}&`i-tXNi0Y82@(K4GZ7V^j zqPGL!b$7(>{=Uc3x@}zD2rD1%7Co4p*_y%Stbdt~X{9UFXu3<`-`tW^xt6{==UFIQ zl8RZi=Lfa0rBk&jmFNjQ7qG6l2fP0Ox2U9v*LEU@yGQa~-2Jxx_b)N>vmcr};xqz7 z+y4N%`S|qtF4n=l!@5R~J`?GBG^C|W$U=gUphZCiH5%~!?lx;_3nUaFeR#uiJ8X@+ zW`)4D!%8OuGMRWJ0%|&|ZA6XKS9Pj7g)Oj^r1z)c>!)2pqyRulEI!l1iBRqbdhAE# zsNE8S^g_ytCa~iRQjy#!+ll>2By2{fTU>^_RGH%SSVD&;>MOx4Vr)V2XkH>pgpk znggiOYq0#9*7k*t*TGZ2y9{K`@FSd%PSK)7cx_tyQyV(k`%NV?ECFKu?PwS&3G6Dq zK(J=Q-9pVs`g=#?558ilI)IQ!rE%8thArNBo$X!{(fnVc|8|o_DR3~K8l_zr1_r; zaqp2{K-f6*mOPTM_??SgtaD3zy_CjWt1EXXy{nH6}gb|au7lEsnho#O$Y`}N}z`)k;pr24cOlm8s{EM z@$J($#`1m%@%qQ(>)3i!ET-1AJuT;f9k0lEqy;!BvEQlHZ1?uhAO@@JOikNC^&~DT zFuC$og75+661c`bnZL) ziu`pVJZ+rX1FD64lfSkbozW$EWx#QKROUQmj$&m>%a?ezGje)rnL{g&?#L~M%1~%} zY;~Hyj6*)r(Q7c*kGi!IJ4Fn0X$JoQXcehU7Q2tk@22j$sWGTj8tSz=we0!mBSB~) zS)fo_>LCeBUeWN-0UcJleJKLxQK;nv5|WcrMNXsyDuP-7NLQ}khK(2~P?XSZqN*yD zjU*K-;5unV#HBr=i^mWuqv|wCpz?~THKzIr);$1OAd^a`N^mtx4neI;NTLZ(!+j4? z-ILo|MxkR8ro>oWc`9LPHSS7?HS1c6Y1Umx6H;5m@u*TIi<$XL87cnJncNsdbnR%# zal~!@>xDy8BFoC&>b~azX(v{xa@*!NE3(QXUq82H5nB{eX24}@Q1z;i2CNy#9^SgD zd3a^H>7sZ*@~;J(+5Z4jaU5nAPuC{dA#5-42|b{H9X!rWUZGXV!rnA%l!c;~|XuM{ZE++UL7B|+O9d!K2dmco*1KLyiHnU!l}HPEJ2 z#?y4}Z?$!eDmA8y%jR?3nZEjT_Sa6TH5 zZ5tYyx|(N-IFhOdl}d&(99sKsa4FD*B1i-i^e7aQO=GjIq@lybbp9Ic@HvOU8s$4G zvd$#LGl-+!R7TR&w-dSPP5%H5NXkX(+XoJ+J`Uj9Upgbus;dc0j^l43R8wRTw?Z|I zgt?BY-dq6Y(5oS1@=G$Z#f=qEqRM76LPv54QAiXUjcKiUERl*9)nu;tlu{biNEZJ9 zE^e%;KfcBqcn+ls{{SxSNgSVF(i9i)miBUzE#7EY%yAmwk3QI1NLtg{K&Ypo)q{CM zX-X>DcvOJsYN{_RRuG|*O3uyQmw5V}yDJNlFQ>*Y$ zKH1-G6%(P!-G^LZ+HUMgoPeg1p<0fgJ@o8lp+vlEoZS_;nVaD^%OYa5w0osABk8Hz z=c#fQk)os#JSypv+o6RmaH-gulllG{Y`h>q8_-&?v6-h3tY~6jJ;X`J^zZb_Z=;@aH}VJ0#8j(#Ywhho#htZrMdVkKjhvj zxpwh(X}x=Lr&gg$Rwbf+jrfZ_%1Bic zP3cWt*~koZD=Uy;WZ}U>@{n4P>IkJ2K_7wDOuwmXA$3?2(*?V#*?NZPRT5MJx2IF7 z7Vp(m<>1{Ko%t#vE|#G!y~^%BHfPaYgb15L3MZRjl!S zT|l>S!A(kyxd+SpnjCi3$I%fQBSwu}mJg=YN<}nIf~Qo!SPqK0U3u%6ZtpGU^O>v_ zb#0aVN&Wz6#j{6Hk|%eHcyZsAeBXdyAl+l;lCF`WttxiiEq>!`NIQzsqPh>qM<7na zehr`JnoREKm*;?pts=1WZ#H`bthG;d*1n=?V8NeEC; zN!aQrQrc9<$8nIupSxJ1mS};nrmig_6sc5sR}$(cu3eS43Nwtf@b?xePNuZevq}Pu zVPssOMJd^m6#J&DS?YCBEVWvgA=(n4Ky5!UsP&?fSV>an6JQdv3)@b}Y@*u`_d4NE z(yDy*OOn1V6boI2#fP26TYSGEfsESIXSY!aX};PDZ9%HrI7^LHhEnWC-Jwy;{{SYH zHEy;as8OPk`0G(`4%Vy%#Du*<>iU#aO-bvihgBSvfO-uCl7s@Dx-b$6TB%*N(Cm(g zw{axtAf*7eLLGTYNF_v?0sa~wDuE$SIK-wbmNna@xEeD7_Z3=ftw)ijsd2ulNaL#p zF>aEpgm=U8R%_i*Y3oYrP-#zyz!g({&hD8}6%nM-3P=k70B(c;?m_Z`qZcrn8>>@~ zp-!ztsPDB-rM^OYDC~??DaOU@aZ-}p2Eq7{4^PKUdWrWiC3f0nq1w|aYz>EK0Dp#u zs8NSes6nNNRWjpL54ryCnMG8<jGlv)c(S#aHV^cix(Ep!7gS zT4+>>$JXvVkCfxDa;)k0+yzgg{-v2}U`V(kINe2-=zEp$*y}yW+kER%Eq`BSt>X{J zHsl+zR(Dk z8$n$Cyj-N@x0!c|%PU-`3X%`z++P%>wAl%-AK|32&pW(73U^(PHyOPjae<7x1I;|c(-{L9)owKjk z>D^m?dX*nV3Efk-j%R+-ebFHS&)q?i%@6+oR8Pa>wwjnwCcz~}a}!5a4#Zb&IKUCj z)pk4W3w1&-T$yVtDK0J>fx7y3_SgJ1=f8QOB(=-fKekKI86$ow^L^(aIR5~LTzzpM zs9Um_$qMU4xP>SEy2$%AB`@jo{sn4(a&6s<2jV}9^glx(Uc$L4CJu@Wq>pI3=t7{l%fX23*835q9VO#H9{} zyh4-#-h-~P&cWr|Y~wTPC|2JO$nfpGZ=}Jvk*cqrb=Y3AXLg62Z~9kQF{MXdwmiFcGY2J^qq;I>boiS(2f59{QdD=YHS~K9#3}P{Z&-e-Ti%4i~i&Hs{!O} z=4{*<$rmtvm0Rr-Tnh9gFsf?*0EEZCB+r_}`;U3);bkXH7s;a!l_3gO>L65W*uq+` z3Ck4!lDoCQE3Ip~2JR>7)lJ?qV?8p1pKzI;Nh) zX%759hMjS$j^-glrl4uJ6QXRf3Kv2uYCUwYkXpA7Z?Mz8qEDuW4p-MqIF!+17&rji zdhB%}ofAcsLj~59*!pQZXqzoYTQnz0OuQlcmGK%XrnZDCU8M(iPfaLwO}aFF4pxVu z^waV5Q5?i&vQ_m~fN7-5#S+!FB)R58aai{i6{Wu9ZP-s43@cmD*Fs7#g1g}(gQ7vt(;Xm!S3Y5{#n$O%ov^LTOCs}8yWx5}R-;Stm zBGq5pKUD7IE5i&P7-)~T`bL@#q9Th;{{V!q!%jKW2n5!%|r)`TvQl$33Mt3W2DPDOnw3HbW|0FHuXcs{2GrAYq(4LRx{6wvb0 zjy?KL&M$HJY3B4*^A9p;+Y}RQY*6k_`ZTaolcFuDIgM)8zSpJxnKNg8+9GP`01AQQUK8nI$4sYn{4*9 z#>v{7Y33r@YPu8lugbWy81q|-o91km--zJX#;!0KzTL`&r8lW9w%xs%YE1)eyx}Ph zD1?Pj9mv$EZ_EQx)nmE;02w(OJ+c0vYxiqk!~T0Oy9DGH9u~%LOuNBY{OgPDwx}~0 zxrJL^VPo5FLR|_{R)rwdJ)r8I8z$Y#O?IpezG_TWpktZ6C0lZzAF`}&C6f7Sg>6zJ zEvx=dCgixXqAmT@)K?PfTqAl`npnZ)t=vp{geB(qTegVjt4cu4p2TsBRx5~N{O11v z3|QmGTR7du;_`2X>+c@atxn4K6r_b6iBUR{8}69h?_e}lSH{X``Y_5{UWqQ#;{A?x zKGHM%+W!D|$ZVbNU%3IOWri*>olzkTwtA&WGzlR5Mx>5oa7NhUAA+qu)>@jr=41G# z)!U7Dy4`N~_`f`g#qt-n-L~D6M3mg{fU(~48*M4~I&D%3)7II0=?QG00HI&;9Ag>` z4=!+1?lH%lMU%p|w|C*Wmseyq;x9L+<02)UZg%}^1;mFEm6Cd-?sc2Gyt{?uwnA3I z$BE)DAPtfL3GRETFCWIZ?~Hi<#fI6jt9|lTJ#&3{CLn~gwqPnlidtJQkW`+~vTAyr zQcCIWrE}zSfIZgE#w>O^+B$l|ST#l-JxCoPSif_vdScNWo<$yK+N+{f?pBes}WgqG1F z!7b#Hpr}qeQdPZwS8y9{HCK_!I!2Ar&@B!Z4-9h<$!dFdPbpXyRmkX*2a;^@Y>}B- z+A$%>_g17Jl%>=@*h!*+szDV`#ON?T88?s@91N+YU?B>L+uf0KOgE;?0oEKU+TNW%uSPIWdA6si>` zZqPMc<5g%HLJ4g=PjGW#(L2{foeLTUz4iY9%|W6X_OS*xTt_#8pVB?+LF`I@3=Wlw%=6N0@~vDQ=5d-?%Grq&g)a zOcKjeh+np{Nh9eyb<}r8K~4#3hi*!oQiA@aqtKdE4I#pbZFNxTbdKOYps~2=O)}D2 zY7r0}qKk7*IJM-FU6BwQZPB7!j{9{DmuWy&fz)rUJ}Z&F6Sew+viZ&`%Z6?mI4F*A zlO98fV(s_MZZO zf&08G+-TYTzB-xiSz1;{W7~ES*J3*B!4Jj0Hx?17UuA5$L{cz4t_7~>r0XV8YPq2$ ziD^;(WSRht&>7YSR!ygIz8u-5IM*3Xv>P$~L%Gsx7|oovFzy zx#Ck7j9k`xkF!%9YOQ{z&q4TWU1iDK8MaZlEEY@K*c|Ut-0XkBQQx@D*DSPnSI*e> zV`6dfD%h4S*XZ{wUDCZP9UDEKRBLYoz@ z>HPgQ>09DlR{-Pjff66eNuRXwgP0tq#?L2zhSb$ex&^eynz%2N)>YbuO)FIY0JB;0 zbz8?nMQFS^haP`AV$MlFOfutzKAR`wtPi=;?ZmivmES*ZHX>a6N&eAQXS}<02Qq@o zV12A<%u$kEAc^Cwg_) zO}8PguXVxfzzy(Sc>79n759lTAGr53fJlg(XQ}`|{n~lbBRGXCNu2ZF-df{-8;+~NrL!-IJU2$ZoOInF4 zQ8g!7E6bBBjaH`HYjpyM?VYtbHL8x@r0Od=r=}iMm3diQw*XZ@Bm?lKpPitk+6n|t zFLP(OQ{;4_!L=!}P_BUewj-O5mKeB7QWFvc#i>*T{{SHM8j+51Y>K0j5a>|fbW7rI zesSC(HT5@Ov)Q4x1rOAs>#7wWI%>Bf^6QmhfM{ii_^N5lPnWVpBDGTtw3Gh;%F|6< z(yByCDG8>-P*c=e_k50usn1OXqfL;!&_uIBh%C_%g4a<>QYz=Bl9q$7%TKh`Iq9!x z4_)+WwVs+&dZ4ypBm#aqU0js#v~X0Abb^=&xfC?e9Z+5osR`D)t5v4ImrWHlMh=Lz z2g6FDE#+vWiu|e5MMR+?K$IGq4ZkfCaRj!F%qz&`?)nt2Z*GVVHD7Hs^Qi1Fpkw z_UY}Qby7(^kx5p&J-`;GTI0$>wpv!w6<^{smJmt~d%#gvVdIw!=Zyw0_?lD@YL^&s zY^WcRLDX67*&)?T$7L=p-BubVt#uH0fwpy`&IyD3l*mAT$~}B?q7uj^&bn!%^SWJPwF5*!k(YCm2v_ zXw+@^Xu#DuR2o+yl~uJAk4z2TWGYEp3_~1nx?7Nfkci$aFVX1_0x@YN;kTJ3fw1N zBsQM&=mwn(-O(`l+R;*vCmy=31#Q4_V0;pz{>>Y0UVA8~04+e$zbybXBq9#lXR=6g zD%1Y}zMVAa9u&r2M(%_nYGKs%Y>&%HaCJH(4+pvkB}DYoN$0pNy|sKaXuU+GW|sA$ z>uOKvLu- zs-3^2YE7H!sIxHZR39?)HS?8kuPnN#c>G1FHrsMkKq&-(rGc?0RIG>vqjSoJ64$_Y zSJzJ@moGas_VH+yrC&x zFCW|*{ksXTZACsc+fGRR=R}KZZ%Qfi4`y8D||~9Rdu$)SX)tDQfMeZ-=^BD zots>=HlUX3g0UzaU%9dYl8-0MYaZ$K6*>fpGN zRb{-*GUYgH^wiL5+&ZC9yrgX!ZET*p2%NW5or7+=OS+`7x}^1~M3Y@4>ZEO=*&k7- zO&t?wMEb|=fF#X%HEF&2d!XXRb6UQ*FuTdwD#LcSwDbIntaV;YU4zhp_WH& zjkPP*c)v5uCbYHU7YmJP?KC4jK~w2LO;Fuu?HF}K0hMa#B zddk5cQ_^VI1Jm^#wNE!Pwr}Y>mX_b!+sBVLlzddh3&ku?9c8&0hEZy?rN8*CyCJnG zfA64^s;~XVc|Y+ms{!rxtUAjgJmg;*u*8a87n8iuueS-G>5@G=j^Cb<^*I^OU7=26 zaTeo2Qj#xzL*VvXJm+w(Gj0;1Az%CzltR{@KvI;BwWqGEEbtRO;Jw7CIUY|FA|Z?c zbEaL=eoK(gy`tkR%a=9 ztrRZO0!Zx}4Oq}sQ8n7779LU*De3E_mlU@=Cylmod$kJcJ#<4=70n$}rYcgUji?7x zr3D?p4vIa@@=x8UTOv41EE+ta(5N1R<)lw9V4Z=7x{s67QB&?)XwfXuAciR&wWj-M zlZ!%0>U0nRd7zGnCDx+7bQFekR!4!}Q;;)AW`!@qR^Nqn4q>XgvXYFVgf8jrnIwOu zL?80hnA}KE?oFCgwvr3D|r(@e5JLMg_p zsNZSZkL|f=Ht-!JTwRjEx0LxtC+4%!}vuZUO%QuCjQs7*o#v_DDo@B&mBms!%6#ep;7lBUv3?pipjY zUn};sYExF(gypczJEV+O=?*7*WIFx6V;~TqpbI?Ie+!!u+o zAOa6X)IYdeu`hTZ3FeVVZFfw5-if^m!>u+G+DJZ|?W`BJa+c344`(jj?>r54oHCB! ze+9}Lc2klx%)3lGw3zmJ^#0{)Oox*d@;WKjLcO&S=t!=*{I<+4tu-gaJiFcvYyFN&%Z-#FFSg-5&7OrStwlAF%fw6Sp+h@sp;Bj=eepbM z<>wr)XZDF{Qh)O|O=&m>{{Yllu%7E{gp=aFQ==2ufm+ciq5QS$29GN74ID8;B?qCIrlY=yEGLV>Q1sV;7cVBroa$@$}yab6+9B|wdq=4zCS8m^MW z7)o47ZcSQN@k;1Q8dqqcsoPa~tmAB<-r!HjDcF8LHbQSsG6A7FnsgQZNpFsLj^mzk zbNpK`F0pa^=wIDabILFG1j4h%deGa7aivHBRa=U0v7iUkPM>Y*F$aKkRdD0?$Xe$H z?eRT+N-jgg>g`wis!;V6{{RuIh6?pn4vidP^|YidDj`WAwyEm>5Bw! zQL2lGR)WrRRQ|Wh1GLwFT|kk#Ej_cv6Ve323P?Jwo-nTMW;IW%Z2p70e}{in@7id&GH961{OmFDkh2$Lxy} zZMk_v8LimCd`FVeQ!g^oH&9JF*KJm^=-a-bwOb5rt7T;gqWF{YPZqLa+^LdSEZnnX z=J=4_syQnkc~UJmBEPJvJBw0^fF_4?zP3?b`mF?tBaXw;Bvz^qM8>-kJ3NK>Ch|wZVN2AE^RU1`5JrN1ubXt2-2mYGBhKh-7 zC`2_PySAb5+d(R!EZZ4zwG}87SEWZ?N^5nhY41ygN+l@n^xth6Os$w#bBO>_hfD#g zkk?gAcMkM%^C(~QIB0(@ITEjwRlzjjD544gKS%>@9HT@W+7yF1Wp1G(V6_U36Jtle z#VxRup|GR3X#W6am#_r_QB>c!T-POLBu&#=IK2wcrx?HnsM1PfM@tO#SL!3w{yJSz zyHbL0WUsu`o})_Kl9XF$DUyYxIMTuQ8(0cL(nSa$f(RqiPe$ifp+4q<7g_uP`QQ)hr_8Man$Eb@$kIuFF&amYCJ$SLPm1RCZVOB@u##D=K;n|a)VbwH%q>zPzO+l!q zQS#JALw01@6UyX+J%iXy#2`2)Y+IHg$nkM}o%iJ`hj)FF5o+))G64`eT}>VI2*K%#I1q#~);7IR@aenZIaxrNzT%f%_gjPx4cq z1>yHL@|}hZpCs}~IK9PX*!cyv*y87{)*ExVr&@Kywh~PNr>3#Dp5xeB5-np3dB=T= z&*YfXm7LeO1pfdX72V@L9KiUaAAyhCFLSdeZOTx!O1wIUT|xn>Qi>XybP7AuT;nIn z?ysCl00O-k$2bkHEupP(c^)cw7u7bEhZn zY0(W-N8au1h`VxrO}^W6vo+iO#E)t$gsG({9-&EA;E#rz^qk(+QQvmT0!=EKx$UDq zy4smMb(1rRWa)7ZJh==vE+W#|-KP@eC>79=zOHb&J65=f<0KVF$?^Bo2Af#}ToGF} z&ykJ#ZNDI~!v4zKX55dHMv{BKcn{X!vY}qn)he&cQt}qI1-qfp_$1+PGA(x5#Yc*J zwA=~Ks^qjPEybjExg|%kt&_MugjZS7P_*0HdaqY&ZNiCg<*Aq3*{Ln4(EcE8pem!0 zu7w>JH=A({Dw3Q*{;SmOr=@h;nhKc&r7xS1aUe4K8Coe3qM5OjmYh%xNI@z>(0yt;X+Do~oB_wW zO}T&5_eipFyUI|ww-(E5itp)prEEs}lW8omx~x!g5WysSCFqm!tRmxJxLfV`HTFC# zehPlPJ?7G;{{Zu$!h3-=)`B~eDE|N>YF1g;=;8kW#`-lN^7lJixRVdM=U=f!>gcwY z77H!Z?Jhtb(+!1IfwsUOj-_%T5uCO^(%k}y!WnV&O?>9Bey8tJ zH||h-k99s;&o(_4vB#>>Qa4X-$3jK`l3t)MAv}1(yobadvtOA_zaoT%D;&#E*fL?e zO_N=TZAbMa>~+(9w)aNs2XxR$bVsOOWc|Z_-m(0$#J!CTx^L-sf0xO8;&VR@=KLd+ z%Ej|ba-%$jFZ*@Tu%i-lK&~>xOerU@^HjnPNVh%JjFj-@3j<#~2`={{WEy@jhTY zK0lJYc*mGHet&X+aa^=vH_0Rht+xuIJFXG3eZWW+`Rj}3eT%uUXZaiIGsG`dV1IX3 zS*h9}f1xk^uF|T@;=X1{#xAX6oU3d8)LbDD@69MV3H16XIFdy*%k#d?$s;{WUcL*^ z++X)k6{MmpfS~^XxM~&I#`$W=klWh1SuRXnB0d2^Qn0isCx4G!ahvWrcWEJH%^%%Xw&(ZEwCxBm+c_m zm2bgv?|O?{g?)O_e1G8$C@H#%0ZsS$Yt{D*Ag?3h2-p)_&8gxKiQKy4R7bmQ_JXPZ z0L2ytsxFs6#umN6FP7xGdq%@#{{XX81NAMti2++r3fXz?UTN0T-TP>ARI}2FpcU6T z_htKWwDBv_AGR2av-R+4T^+fr!{<4M%?Hbyu1NBIEA3|yAzd*rdMZwy1BdXj`fZ*uIq`_09@ zvk|T9K^=yYN93Y)RvaC2tt?~PZWVdtXTAKc;J)tQl9Z2?2AjM-bo^ZtHP1~$QqXz` zF9@!HDK2eMX9W~=pqp@_(?KO>f(r=KZ)hmdMfB8k>IRx}H0X6!$4gA1Rtiu_O+`9s zU?+mcg&P_aO~`{%d-prjNllQ8YC>&hf(REoz}(bVMiPPqbs932L=hYOzB(k}2om8W zZLmEwUFe@74mxPlVQQgNk)lSrKjAMZVHi#sk=Sq71jD(`xqjd!Mu4uQ zm2OZ{K&5?p>nWABYYOPN3+GJFH&t84-E~eC>$p>asC=XDaxe?Qr?E@Tx?c{q0_8+1w@+EJW#36 z5#`ucP}^5a3sYIB_ewzS?H`EM{jOoG8ikX-;)X+3l{s5EV}x%Zv30dK0c$^6RM4m4 z(^q!+3rPo<1(3VrnJABtlAbZ`zC2%hR?OHDp&J1;%Yw~^pS+LcA$hK&*Y>1K&D;K#hWX&cGq|B3a@HG3LEK%@p#G5t0=j| z?EOJTS6>*iNa1E9=#kicb|hC-((zYzD&`Mxnf<{qC-AO!Z|_I;*B?p=+-?uys^?)R zZi?;>TWIsCqH9Xyyv86>p0PdJKbRnqT|;&*VcA&6pS6ESCcKL+(i*?74c5wf)g*M( zXInj|P^<;)P80+r_t4_SR+1I?dTFOP(K{QO5j3`>DxgxDW{O#GAxXIAPPp*@0Jprd zlnRuc{dIL`H3b%DBahte!89@lTYginpwzF|Z9=+ks^Y!hcU1p0dDiZBwrrU97v2BEZWrTS`Jymi$^x|?WAR~cy~ zFk;m~CY0&DK3WpE13;Lyk7(TGDqtLIGT>R4F@l6Ia1=J*Q|+ufs>}SKtpa>x{3eN! zgOO4jdX(;__4#S*iwzV@T%?UqeaDJ23wwkHi-}I@ARkRtO^+eU;vCQ;QL2_Le64rE zaVK$>d)Scj3VXq&0TkOuBg-^OiV-8Kt3;<{TxLj{Aw|ceD5=GhQb{${e1w&C7^a9H zW9lE}qykdus$GQ~52%fXh$ktka9gd??(*4z4YK2}s0t0n`eNpA#?@M5tlcYux>#;2 z2LrRA^^lrZO#c9a-Uyj&&x&K^clRZ_^3q^gBye-Tweev`mhLD}7w7Y)pQAjnGGilhYvAOl*1>Atf(veeILSy~ddoC^F~UWo{*>!&1nWs=3SsTd88-;X%j zXsIfW9B`_gKmvoNxkfLA#2r_XS8U{*@=s2Y0N=S#Opl%UhcTAvzh-lLUG4@mE_*GH zO@nmXT_)QBZpMl=ajVWpcR4$qM9Fov+*?eEla~AHr*p0?OCD35{o=_Snx=pcD@2B|ZaT5}!8t#)qz0B%+3LHF7R>kT8NKWY zoWyVhc&)MNM5GE0Lv<9kG zv@O_N2SorUw zCqzlhYE=qPu9_MZ3RJgXy8&8(T#_`26Vp{{VA}qg2a=>U%v7 z+HL(5_!wxS?3o_b+Iw9KZY?zR*+)$>v_f(a*c!VsdsR>CbGN(Xq>-nkB()X0NGSs# zXft=XHUS>n&JWzd5^L(0#edIS!`=pNv9E&f{{XkC z2m2!55W9=_U0_N3eB^H-HC#)*oz(h}43u|&hPc19?*9Pspk4Rh=AZN=_%E7I8?K+) zd2bJrYS>i9lUn*O>Hh$3y>oOw!b0XGhYZ2=9%h;FGNvPV!HB6xJsh*y(yJ@ZXEzuIKd#lwXik=%E%x_!eD&HIyz6Yu%`5AJ?N66Xh2q`} zxDR%s8azMULd(YT$;}nX^Ah~U_r_jUjqH1K9JNT?bf`^#*{ePW z5pG4l6`J=}=YAFc00r{A(KM%}2EABDfq6CI9QuFvS4dH{M$!64jYoG>X(yo?mJ3}qZj z_n`hgbsitcc#s_s!WDmChUdY2Pg(Ml*LG7U#mY&?EDdVI8^qZrWWbP5o3f;CM! ziA9Hx=cP30q01UnVnMEuTxy_85?PG#G~Y@NP$i8y;l8h?he(i6CfM31oxWhIdehfY zUd3f<^!R}!Q75{e4OPz47NXDs6T;steLq!oT`LV0-Jvy4D|FwfKUZB+&NW(_LZ5c| zv@P$~RToWK%Qq+8vTxKk{{S6TT*ac&**{ev+a?p)^>p;sW>B@Zk|))=YLv7d?%lCWswvQ-r2rgBpW2A{kaXX1)4@-Es_st>_9twb#DVUN22IV^0W}S{-)NYnt@9G8h{^C zo*=E#5~D?aT5u$UU=Gywfw!)LSO%z!k5WI5nRbb7cBtjHrnBg$Lv&^zy?dyqfn+6g~49Qwt z?OIk+kO);iI)#AhWBW4=5fYy zdyA=R1=c0GGj4wQS87U@5J(ji?mAQ$-1Mz3dx!|;%kb+-gmJJJ9{{B|nbb}x%woHQ zU{U`7=)Y|)WW1Shyv4LKhSf?TG2Z~Vt?Nn+GbbE3a2@XRD^~Kz>@OSWv<7!m(mag7 zvRM%=_8gYec)Cdnagg20I+xgyMG)I=D4&MJ0+E=#T6Z?+qEivbp~uuU{|nbx1NjGc_o$%mm5|lr8^V3OMl-EDQd33 zB+`V_O+Ow_dL+k{Ye_Wdm7sDncS|d`I|Z#aXXD5$$b)f8LWges)kOzty$Cu-`pP8q&US+d?X% z-M!b8BT6I>`g13Tazcmvh z%v^r&X%@*5O9do#QdSSvll~f1*!px%!I>`oLWyGDq$T%61-))*Dl2|6s;_arr?h^4 znu7RT)jl~GM&MD1Ib3N^-O(YWxef$?+*g|lOO7CcK%hrj*L^c20*K+eBdT0fxXNk7 zN05flsw@Wx{$u)g)Kd)<&se1Egvn6W11+dUaD#HUcK5XC+j^0;bog%Mhe~LNyJRtJ zD0eG=DiEv@n(vismZno4J7^V9E-P_LY3z0m;`(6>EiQ5JRk@x`YmBxx6TCI8@lkc;Ga1#IGv&$ln2s*rbX-{hMJOdo9pyWJ z4Rjkm8({^he60;3a_j#9bFz0C7Qt@v_MXa4#CSFwoUP2(`m2O-G5-MFJ7s!fjv-F> zcY>z-ibYQ30jy?knne^Q3t#~8T_cG5TQ$Yty1XQjs08#^7B$fgA-38_Vo|V2=rzZr zX)dYmy(J8xgJ|fKS)3JK<$kWZh+$IOAWGFhhEjX`}awIuE(Sk(g?jkI>=0(OeLl@|)N-DWFxe6&Iz!UVRxEl1=p}ny80^Zeu zg~g8^Jr{_-aK6dkU)&j)7@7mr57a(K{+~6*u2u0PAc28L=FS~%=ag}D^*Q_ecIN^6 zc2$l#-~34P;J$<{UfBeDh#QURP4(%{CGOpZMpFxjY^~GaeoMmq_xAgj#M`#^ILqyy zsL<|j{{VD8-!AHrBJ!q{^*KT4b=TVMD!5Crp~IqZ#K~Q3Bl?H5bli)+5Q>}qd)Y)B16g?@b3*I|xDc;ew-vw!5$nsWQX)<;6}0Bi`!c)q&sA2-Qu>~OW!=# zAGr>KL$Ln~gfNch{BqZ?TtHvSf$O z&)~iP0PVN#za5&}cHxUte0-*ZW0;_QbAHj!&+-gXqtDky23JfOEx^}J1&-ZcQqDowQtPPW+QgXF)K|(F0%>$_)%&sv<1uY6}p-;nE)}2;>^iI1$Q&!uA zfCkAasrj8vjCCp;f~lOUsY*2KsATG_$CjYzZJ&wMs51)Sr;k_Qyi2Gt{d>=GJ9+R$1hqB;-{K{^5|^T~Vz;4Zfzo$r`PI z4dkm|WYNRXhk zX{DM7EYLxCrH#k<>N$-+yds_d03vifFqOO@yQxxiplXfHs`a`$wA0hC=c6M;fYf$U z0va_n9S6rtV}ef75Gef#{4|2p30^Ot(V|2N`j6wMy+pbpTqkV}-vpmXAtA8bi7d4G z{kB%19CP5B(4WgrT0|s$3TE-ek&@o3!;cy=8`K7Fc!l0I_B+T6426WORCLy{{9Vp% zSZc6Q`-2l7wpNy?rz7F+O2$^C-0>-CLVCQ0%ZaDN6jxNY7>k(twQ6j+U5xajz$!3# znGu{VNRGA^I;CkTKfsMvHI8ew3qu@?c&klNRnxwzRS}?e6w<<2l^giWE8# z^VFE0L8@kWgMyOmS;hL^w0_R&;~kwlR#X03oXh7d-PSdP^i!rYl~R(5eKd)O655B5DL5fZs?7)*_wC zuEgljD}U(o$B7M|C%)G+`Jk&uX5hP6m*bRq1Y`wA=k!8+D>sl8CC=J;!tS z(`_~ylqFh(lu)N*>7XhKchnux)^3-0g({14kr*bHU`^ zK(>G*Wipb4{{SMF+&}rDsSxFE8_~+Xd>y2kIiWo*-a>Lz3J0!&YJLtTn|wCZ=(!ck zRc!o1(c?ix_qKUOkT6sI#?0KIUa3_p>G0K*T&4ZOrJvjQPw6yLZJWxBMe>I4c+%PK z{cZ%MU=#Ri(hf%hkC-Ys_$}K%LzIILlQZB*G`mt^8l!}Ju36+HD zQk2IS@Qzf1xHF+Ftbggej_RwX!04S}Wd8sK8a$3p6RlH1@8WJVJS|pxuxGebB_u0i zDk-SaamZi#Rg669?myNPVRZ2iC1p+#s*NdR{YM;=yXxczBc3wGrr`EUQp~msV$WajXI52oP4e9 zu4B#GNHlO(!#qkeF~=u?9@FfYVA@KctH7qJLDL%Xu)Jf~2B%wB|3siPj};?M5?9nF)$)24esNDo!XXV4Q&)RL%r21 zbKw3fmzPcc`*38g1a*H*5NNaU)7BQd&f=;v_uB^6tIUjcJ*GMuX{3OJ?LqL`n$Wxk z1)6L3x}_wz(e71KQSj7rB9zxrBt36!@_U&I3LjR$>O0LsHkuV(c{_~c%v$G0XSJ|N zQmN=a4OZPmXs>K9fK{5|5vy|75YW1V+NvK>s)M1T(L&(uLM0u7q6)UrmsLn^iCQxb z%Xzr*kXGWz2}sj(JTO$JZLQrSzzXB>mIBT(ZB6p(*!}zIt4L7YB9+!Y?&0%3gQDn` zHjKB9r>ZrPUKc5A?N{{E`gf+?H8R@UU{zO_CFfNOXJjGY z+%M@%VyP$NqybW*%IfHd%aEyZR^d@luhXOMq(<5VZWX#jigpdpPsHf9njlD1?;6-% zg5i@Sk?=|AYpJXrL{TZ!bROUS{h|ViNGDwegY@;%z(k1q!l&vxbeONy#Hidq$@13}cPuOn4!ykO#N+mNPQZ}`qS{mS2F z{)@AagW8u=D@y!z&$(wOwzcv&uDQXumBrI{v}mp6{{Vsch3E?mVN)b1ifn`Yb&a*= z7J&6zo8AF^AkZkn7lqzPZtJ(Fad@xHxKFq|40ck(WoC#w0kt)ehnVEA?wb=%g!WsxIA$LgXz5%!4(sI~ znmHM+ROJlEGPgzk-D0{-aAQ`Wp2WwuwxWhWO$7>)JBU7~UaP_5%r?M3s__pa$+qU{ z?q@=DUSZHGl|H_Sr)kvZwAQK>v@{9&@U78ympX1SNz|@R~D=Rt2dC8mnIRdQBTiOja5E~5_dl?nk1I5 z9gt(T9VzaoP&Cx8piYKrgo|;I@Exk`^c3s9oDU=U+UyBmV$0 zbkw6oWosv7-_B$H+a$X-di%>Y_|)mh-ZrRn4N?w1{{ZaoR_BlYldY(q{rG5|(MY!e z9nK?r83o8BQ44Ub6!ZPOHPxz-; zFLR>bjB%$$)_sn<5$9Oi&^di2ebBMxtL^d0PLzVEpR#|j)3&Y2O*(7MUe#Z2%r(Ci z>#u#S>&Kt+UnZPkAy9LSUGxH#H*k!k{!XOp+E)g!FP@Ooc~*DI$mx&B56mg2_XIZQ zccCQ%ai(!Dta>Qd%>Mu;su{pAOrw!+w%lV3vq{MEJG-f3(`1I(c0`xlG(&Bmbx}Pu zc`U4UtlaZJpT=OOx|eGgT&{da%9sy`e38UF&x1_BxepR}Oqq#Xn`sva(cK-*ETx6I zp3(s#iaO-h`Bh@=+Zs*O~IQofq>43E)yHr0JZeW9;E zyz$y)xVheKcZrDuZ=^f@U-s%8qM>FkSxHLh$2-7H4=A%hI zqd+DILZyEX;iFBKp_?_Xiix*`XWTyx1jfW7-RVK2B_XU^g;S>EMO2ayiB)UqrG%0Y ziC=AfYokalsxpP|*YeV8m~&CIxB&T+qoQE358O1GCa8z+r)@obbl^6i9$2gJ#-p1Q74ToGVN41(Gf*m zBzh%y&u_8QfvUb((~2YD(z=f-idda6Wm1B@dW{p?R;dsQ6z+#C)gq@!2TD@SUdcIijCC%e~eRZ7uSrnGUYe%j@3H{VqEV@0vC za(=*?K}}VxrPDQ9i)SeY)i1c9e0_CKFwtFGywxRn7nWq$kB*$5bi}RFuJ_x71zN2z z;jrY1DthPt04>~x+4_%aNhY3}p}n|{A?7ry==@s7Nu=_M(R_MlcT3(UJlb!O91$4> zqRXjOM{QD4d%7t!p&pu+$!f~2-XifeJ0zUee@#-P8IB6$ekhqGURS61wA8 zTVW2aGM3My=@n1MsMK`pT~tM>ZY`9s^1i0d)$2_PryRuehO#4ydq_p>RPI?{52*e0oppLyBH`(PfAn$no&qh zJL=XXwv>_yKS}&fk_}RJQ7T*FMu^yUK<`=H)YOtjgA;FgC}$)Yf{7l>N&P<)r|Jr9 zp<^u#{+51`*0s*hgdlQG&xvEz@)k0Mj1gq4V-5}#63dq=LM#TyCLM!J-NqWZ=AV#@P7#@-pr zyoO@to^ZM2mm>?wUmhV{1@kb*E$K>}fpvB|l(!a#kxus6sZCE>9Tb@CW!ua$ov!Rx zH|6&{iyj5ti+xzfR`>h0R>tS|%;A}scOo$V01n-n&*aPxViH!S7-BNMkf88*`-8P$ zk~=zWp&zp)@`uAHEm@|1<`~#aS2pev^roMGbS!T-e%PF|lG^f2^5=cSvpkIv(|?z8 zx4yen7t3KTD6SHHppNgNZ0ch}|uZx)Q{I$q3xV6Xr;6ShNSK2=m z;2gl-J8|LeLdJ4@320@j9#4p>-nj{nP~lUGSG=;M_U%Kqu~&Kf*jigNNGqmW@H|E9 zn)`PGJ5YosUE=dIZqn`l0K0+7w`P&vs2OsmOKD{({+gD<3QJ@GxHPCEQ7w5~OzJ4D zZ@sj6(T2a8V`82x+_1}(oAiDZ!!diE;Ydx^`-;YLJhh>{l9GuDPtu==R^XjpTyr)~ z(8oJPL1pcMnFh4IX17q!U^KZ@F|UgNURXxcxluzNq$ zRCd!)TjL~eo2qXkZ=8xv%_nH=6f>G~x0SK&7mQ~IvNZlpb#Og~J1wtq^|oqgSFl^1 zqNOLMwOD*N9}W+k=&<~cIm>+qGTFX<)locdV)B+|9gv&0EYvKT-s5g%d5oEuzD(J1ks0is6HZFCk}Y!p}U0 zf}PZTntj;uelA8ejq+a-O>dGXFeYC3>LkQRdep>~_eLvq@PxSH+W}ppr>>>ry_9Y1 zOX83Q_V}hVzqna3#jKteZlzs8p!?BmbMEirO~U&M7HwzAF)MPLQMg?sLydA~D$eR@ zA~9Q)-(m$c_4lzi@ivTbB{7Np%Gg|=@>8eDpHO23GiDqgJ^*Z$k`Z`rHQ`H zlMK6mbF;HBzUQ_U;Yvr)dx@&mLDbTk#@u^dn6>L3<+QW+cMp2PTO*ho*0mJ*)m*F_ zD6-$}b1Xbfk}=3KvBv%Q(IMU$xv;`}$tVjBtoEp$iXVosm-w5w9a*C-ufcEdypGP` zQr=+td{n7n&azyY=IzGLi$wcq{{ZNPQsak^l1Fd!)!d%d50O?A+_p4?txT7#AA9fFhvsDJ@Trk|0LPtbHt!+8a;qqQYI?%bP? zV@k3oD~106OS6!ms@W;d*8*sis0Y2V{WTuoHywR-7yV#!O$xC&_O?LN-8s`HEVVH0 zr2_Q)w)^2nB}qzwO+vx=YK_pAXjQ|e4(N@*my*V|zXs;mj`G}5X(%CXIBP%wQ8WYL zT}RpX7I3|`G}u=ePC>}>qVm|0ChHL^b>@50 z07ESHQNzD+s&@7FkF~r4%Z_8lM(p{`Dfh3Je)#*JlI8hRi?GrV;yde3zagbN*QSaa z@Ylv)Ao%k!zr)J0mTY+4&W&FX`^azyqm$U#UaDS4U&ZLh?1(_o)h1C@ZH#N;|HSvp0T&zPf4F$H5P( z9x1~f>JM+%zM~ssN-6EU6iW?U%@RVXPP&05la#pcLR@0_;74Fy-iD+R{4^xCE$E$X zy5lG*WM4h;UG{IO}VeOmAo{J(nZ=(ffVG&m&!V zH-hcV+cRcRxfkpqkd~ue`$1Bd9Bh`Pp0wF3J!p5=ti7lA#lgbb^!3m=eE0Xu_Q=b9 z*O-?i*?dFuqQ%wY)pju7SqhU(ux*`5U>hVk|;qM^}H7XM(%E) zuMf?O)QV7!sb($nySKI8qos8xVG5RfT{|Z|wv zh-;fdDhjoj1_=k8p*Y_fK8sf|+E8AT$IiTl8NN2q?k`&$T9O08l`Rw3r8=H*$up-J zEp7++9Cbbw@mIJ$3hb0yZH;GW(Y?>h;;hGx-cjT@o#}jK%WHgFqBvqK&uwi_w@FC} z_TEdNC{n!@u2;{!zlQEFtFoc*r*+)@oA;*k6LK-RYWhb&UDc!_cXiC9pW;y-H*jJ@B%{{z=D~qR+Hps!M_Ev6rt)gbfZ-P)Nn8!G z-zHcl#k#?5hh{@qTMbEJxhxVkqS|y6sC8rD^wrdU-${3A9Ufb>E_IyuLK{rw#bXZK z<;1TgxgyWbJnPC_xs2egajlCyxqkA~gRUgUI zZ*17fscPey9E&b)-77@{jcBj7Iq%@#WZe10l{CI&<X^e%(b=o6#yf zQi#d}sT+D}_jFA~`j=_G(@|T;ICIKwLXp^(+J@^4y0c0th^2G~^U#8{Dx)^~&d|8U zg+Kf3yCb|fY3?dqp5tBA>#B48x`h*UD?=rHHm3absmTwy{-qQAPOE1gWjUXvCo9uS z2@hK6(g9wr9mlOSkW;R0QC%rAvq1%#2q2f#=??0aIwIOiQZA(hQl6SL`W7NdbOG|N zjWFVl%cu0vKbZdjmXR3MD|U!P`f1>q?yxp1PPNmF>qO`tRWPF%!&7awi4qWtgqDKj zQNd71_4sP_woyWP~(S1)_NB!#{X8QWSb8>0LjDq_+_n9Z-*x&d=7>A7=Q9 zu@u5z?*xM|kdy&JJAHLekg)W66{y1IKC-$}i4M!0Ww^?NmK=gjv?SCawUD3ScGN3e zjl?I)?G=UI64Ky#*b1F--!J(x%o=9$+-#yFW3^4bgrQ`A=1NcbYfo>>?cvNZNx1Gzk-0(d4@*y3jRBYWEE)z9BOOrQjQ%iE!9Ex!O20J+R{Q?PjDW( zcuql4q{f1rQ7=s`N$hyGqtkk7Om}Lkd|i7d9DJ7LAGF4rqubJoI-1*{O0RY-1JOx% zmoVP#5}FT7S!^hGl1cvnE%hOnfmdAOsuR1K@k@% z>8UK}i3U&&etKktYhUVr?xvK-Q3Mi&MLs%^dM1HEDHI)R^3z5%pz^c{uG&drJ>_az zsIHMjVp4)A>Av2&B=aO&7LI~Im3o%eNB~rwDWKIlWB?;3)jdL{BB(_{p*0$r=sc({ z$VN|4ilw`p6@|wx#B_90O3+8{WWQW*!Dg*xqAqsdkt#M^S&Y#|*QQiF;A z0P=w8*z$J_=;at{U|~`4!{N8=u>r$!t;CT(FX~$Y=xH0PdwyHwQ|B;DbVx`&x>wX5B0n z+(n1jaj@Hr5(uEJYxC8E1x;0a)M!=i`#Cf2F1mwMbZmTvjmjW3N$jVpkMPli5jiUK z&?!v`DO-|s2u_8#O#!Ii(CH-@PhLI4C+FEb8$@)eOa&y$baYAq+4sfnSYfnjHuy~}%q0;JTQ;!j;QA<~JxiMr*H zN>_0|Pp*@q|;bdrBR$5iFjD^naHs93>nEe$r3wXA@YfJv#_ z(?sWnij^uTOU(kgTrp7`yoP}k6qMzUE9pQ!zx#Z6Ai5wzr_FjgYZjckU`XHK`}5)t6-%1Ev|(CB0XNr(HZyK&wrbr zOO(?kJ;0sDl^beQdzY5Bp*!xjrN)ewqYtfJN2$~0f|OCMpHotrt-*b@)#i#)2&p4& zGwG0~7lNehYTNs#86Ce$diqyS!x&Au%E8-Ei0w9nZQ2Hu1r(FfDf`O*09W|v>QYc| z%N?rU@Y2InV(KEyD5&qy=sF=@r~pc1@&-)YSU=mLx&()f98ym~@TQ~6>ZgN+6jN!p z$+*dn9UoHPT2hhfRHHyO7UJVrHrzn&suVzi}0%SI{mAgpSh|*3()mnR1Pa+Zb|WmgB=^ut|00HrfFE zRzMo9wX(XFn?Y7jzqg9Jh*dX|e8}-vi&V3ja_xf9>M8}f6B>Q;Q|Ol3DkT0I*xB%$ ztopU4iye~p7Zq%1UKKXYe)+yL;87$*<9kK-ktd{-{{YKG{fuQRrG43@x~JwftjGI5 zcQp*BXjqJozP4~z(Qfj&9py*dznr|cwZDcU(4IKsiB1DL!xBr9AGkITa#1D1l#bo1 zQYsJOuCK*?y0yhSc=I`NK5_27_FzD(B(NI zVwUoWTgoM5^)=tFmeQlj^uD%-ar(77ilo&Qaj=ySo`Do9Q+gofwhTq~ijWSPkX0N- zCtA%G0r>1cEjtMErzI5##I*Lhs(RCXH7L32!s)ZK^)S_SgUBDc z#&P6d1!P<&d*gd`g4LEsX^XANnJaO`DYXSv0ZC0NM!7#2_G%1nKwPopk*ybX<^KS< zS@Sr7 zxxd?5D=tHVb6N;G(yAf*c5{w5;YU3qobFhReEq8ps&pnT(iX~Gi6%j9P0fw&f}e37 z*+}^)>bslZrO54TO#oLMCy=)s*SX*oD_Q)X%{;#6o@~kU>#Nr(+@Wof)xEg{DNILT zIs0Y@U`>6rs)OUK=Oe+zi)1ph`wpk(y05p~rhL>_7SHJBk{1>7yK~lTit%fZf`={A zUS;I;qMLA|{hIF0g7J%^|yFQAXJHT*9M!WW}(}hul zuW)IpVKQ2WLc56c`DiAbD2#=t|7|8v2ye+Ki=Cp#+obrz)5&LnX@6qpqH5s%)_sY0c<#w9D9(8gL4KfYXe96iFDB znS1K%r4vO>~ml1c2%%R2h{i-*Ko? z#wx6BrYYkJU(fK=8KTi>ZDtAS0$1rZ{PjaK7N*h0ss8|PmtM-Ax~`f(D%V>+iSKTf zdy(~Rto-ew(c8N~pZ57Z8d$Gy|p@~{{Scb%J}cVCvA5- zryibBHG||BNpB<@O#@eS;Mo>h#kh@1 z_~^(vMt#>Q%OvG&3&dQm=W@AR8YM(D<;ruj6d3QRN>E**NBTe-xQYWC0Pt4V7oN10 z38Qqx*>RJ2#U+_h57fBb9BlQa2VE156XcZDMcwA=&Bc!N^id;m{B(wZQs}*Q*B)X^ zw&?)5N{Hlube8o>-J(QrO@Qar+M95GBiH4mq!xmm_N?}NNbW;^k1({5w$->b+t)&< z{W_*w@*9yfxY`44tzSY%O)%Eg8$lgY;wS$A8VY~z>8H~y=$)2I{{UdPgIW@=@YR%k z6gf_+H$U;*{{YObR>}I*=6;ppf0OgkBSA|(zM3$MB(*BLlc1Ef?n&r$m-I%0S?TiY zsOZs%U-7iKlI7@^T>f22Dz-kQc;Ea)ej`Z<(xx1Z(%w5_X1lF5QTbC+nlw?#)h0wO zsy^EqY(6?tZ8PtTwoiMC4W$(&!>~;^>U4(mO)}g~k z-H%;46hr2#ZY6QhCwGazaGr4S3&~3tlbCK(C;NxE%uZc^?PRf&;%btYXF{CWAUd$$ zdyF|yw%h`TwM$Bwyuz~OgdGh!r}4Lv!tK37yL@|f{15yJeusIt8gM6(IL(It0Gn|( zHJoF4bO^UAN~PLj`>A5!!epR_TS8V9+Obnw*Ia7fJCLxP`ce^3iqvE{&L<%qCyB1F zz;#^K=HIhdin-%&y~g(o?+8tiQsirFq}}anS^of3YoBt^o%%N58tXVud~cy>ZNo%? zah&(<^A|kcqy(_|cKdv&soXEMhnBnxDHlEl=4*A`yIRWa;v|XK?B^^p%U^WZC*F9IDG#ApZqR?ac7aaGsq9g&@YY6rQ`qk-){-3Lat||ThLrvVIi$aCl++u@DpVVi+bMc8 ziXEr7+)l=yJseKCVetvelWQwT=t_j=aDB1=0NJgJvv+8r=cHc@g(YrJQNIHJ~$IYMDF7j9sLlBAUZMFK0Z z8nqd6bPpgEP&n&4xq!HIDUq1*cNk|)u`@ctEg42c84SO7n{Q#s2&nBL4v-W8^xmUS z*>Y@!v%ke504O<55sJM8+(aBor`dnoKg4{RZZ~qXVzY2bR%^R=EP)p5lh4My#R{vC z-)48Zgf{0wTkTLC4!4#*@@63vbsh6u?=9?35^y~_v$lt~hr{ln_^Ox4KWR)dR!5ND zF-}gOX0lF|A^Sa=Oa^DQ3Adc?Lja*oxLZKjbu`xF5%0Sk&@Lm_;JJQh*gJ-TEnWcl z{{Y1mQn`39=5Gn*BJ;&^9J;!Mcg4uC>+6=;-qUBfA?6nBS1O^=A6B>Ub8H4k0WB-> z3z}Ygd!NQ@7ezYkY0bdd$Mv1 zUCP@w_9K6ca7wiIRYCf9RZn~o698U`Qfsu`A?b&L$zI!heU*Mh2Y1czgEAy}N zRy&A(^W4Q^#&C>FlrgB>u*N@ia$Ze&M1+#~lnMpnyhdqGY2iV;uKy)}```$J_Q zbD(t#ycX8a-AtvkhpXmjr(XX6cpu4i$BDk`{vvXn#{y}*jm|dCZcY|&v9kDSa@}nq zqyGR3TY+rQr~o@iJJkB?hh%-E$=jC}jsE~8*RkIA<7O-qe~kAhp!@nRq+${7nLXAW z?%l|5OvR@tx|CVnW>XT}SLhz|V5GHA(h>o#-8Iaz80&}**T!q|U2@Ai1gCUzg4%eh z6Xw61@A#L9-!aQpeQCe*h4NF8@oZto(Job@43MH`$!&l;J+cT&7D25#jdkB?eX9EV zw%lDbwl?eYTo>NYdEIAZ>svPm%Iygqfv?yqANJef+swJN`89to13Pq*Eah?HT#0G-jZrPE7wHUhbO&MrEi->7TifvRqI}drjlYk zQ=X_=&hBZ|4aIj96t1K*RaN%E&B@Rb z3XL@qXhJBj+?S$WfuL~E`f8?ILhg#r9GD4lo1;qrdz3mOXlbgK{@JIx&)gDfm414M zBvkpR=I4H$S2Vob@IuYck*D%U8Z^a+m*O;4aE20wqF>P3Xq3B31V{i;4*K*}MJedd zYJKFAz>90cSobUPn^5mru&Cqu2}X(Y>{NXS>9B8 zt)fe6ZT1~Op-PgfQ{;LPxTx2%tnxA3MjsigO7Sh-gV^H7ia2#kR@sI2!a}zLVe!Tb@Jq+oX3C+wt8&s9F?~2qXUhdDB(b_AuQF_rkhH3!;n3*tlG$F$`|EuJ$L6|k z9L44F5VsY6cRmxL@g0>$#7mv*wzXE2r{||{wOyYRqUNvNyCPd{aLyW+c1ovQU1%B+ zR0b1K{{X*hC4Axv>YQ}eI<7GupFKmjT4@(qd?B$%fx>x~O8U?jklxVCN!#30P#NE^ zr*^#U?lZopk;4e+0RF4pAGg>IpANaYKip5{qM&@Ckc@{r%Wh%f=k6yylI3f&O7_6~ zyJ@dPTSg;|t?*tR%1&pyd(U!IEj{A8HV*ErB{cc<)6X@1673mAuATWDmEnJBe*|#k zI2vCjW|@{%E)UccsLiHBO)Xu=rB;*p=(9u!+5&SK7VC#O-ADDO@LZ20^l!2B)^lwY z>R!1YX4w&>{{ZS5sA<>G=%GeD$sRffdTKTYLK=5cWLn)F_So$PmH2O_U)4Em+PQXf z#+jpxzSf-6m%sl2D=R9IbW9cgl`Jp0gc|ifElOi{$5o!Y{{Sua(b;jgkI-ATWVaU& z{5C^je;(~sL`@d1T^t93Lvdi9yZY)qe98U7h`xz5SDFZx1KIPgl0tp1ssl<)73P8q zDn2?0Q2?VrPMc05%OE80L+PRj!Y!jur3-RE)uRTy$U*%KuX@BG~R%Eoo#S8{**K;jB{%faeQ1l>HHP-#lGb5CEKPw zl;bO5OtiBSV=D?u+)8Q!l4yGTHFy5iwW`Xu1-<)9t#&WE!N2B_tT~qzq3cD$Vk0*E zXM>Ia>!Gv~g`|9U15g{DYoE4?{^Q#nw&qs2R0^(&<(@upuOr*qxa2rYSc2xt9V=T& zZNMB)eF+r!@2SS(CyhW@=&bHybz^Z9ROd4LY;jfXPAdnd(;tt$G<+(!XP7^8jFM^IJI$D^Hq-CPTg1-Yv?C^hF^4mlCA^ z0G_H%yo~Cv;k>t)RiqWOH;FBfR=vso2Sgs=65VRT6rGFZ0pjJlQ6AKgP-=c7R(5iS z=v58Xp2N5(!ubpK7b!96Kom+o6t1Z6V=Q{n*-Pq13QI@o8jci#5ZW{rXdt$6l@JOA zY3Zc(NDm0aMY4he=sWGxNozILJY0H&e8YhG`W4u(^yof4Qn%DEKeRHf(n-?V$|lcR+xO zi*EYVQk``folzitH1ql-xN5SxX@vrGMc4X{kWnu*C{SK#kXfLDbJ(M_{FcLj9rp)T zmJx><6-}0v&1Qm}dg5?fG<0 zOlDqjhg)@{+P2gR6g>zvuCouQ1O` zB0JU9tbnss$)0+<$yXv|wuW3EwudEXZ?~aI-&Zg)jZ}ATtBQls9ZIEl{{THc8&wvl z!9YWMkA*b1q=J#8DylowdTEkV%tZS|YwMv@;x!&JrMld7p3><9xbzj#2Gr3TpR`qU z6M^I67%inQNRrB~5-4ANN&wg4zOL|!_D!poF5--&{{YXmw?o`N4w@+>U>#HD2~TAnUOC$xjF&@mQu2||Bz5&3x`=NY zrrYD6-B;YxoafzA`xezHge-jv1lF2nNkg?)P}qUhKrT5t^(4Cbi>guG#d=qBs=KKI zyR^2c(E~Y<*K27MAoK&N)F|8lB$B!Cw~d=v2BCFcyCjE?1OTUf2X`ihXGkcE_< zg1vfc0m^wZST;3u?k&a%^SCL;&itOoz*n0t5soFN?j^d(jDni9gn%~#?m{&G{Y36h~b!<(Wm}-P%8Z)R(tZ%m8gmw)^Zy+7YC*lA}RV29)N4DvYp& zEMst*ZKSOYgw)o$CJG~sKN7Cglkn-IAxVEk5AF%{(KXdcYif)|jQqEd%V;IX4FJ*~ zHq`ufyWFfzuNLFXR~?f#Grlq|a#~kwDOwa-s%y7h^__M6?~;IMwavy`#|=ppT7PS)@2zmn|Z+r^>tHy-$^3B4PCK@l`9=FXm*KrAH74zt`_^S2$v$->u)g`U!iJk zK|m|zQKs(g%&e!1mt~RqcZbtZu9esBiRH@e2*)uDg5QN&*+_0Wp+HLH1f@Nr>vWM{ z{r6bwe{O69o1EH}&@R2)%F7sIPSNmHYcJ-XB>7!xUUSR2E=Y%J9g3@q@z_^)P+E)a zAdit6)?45#q5Uia?pVq5`3e63T#i3^QRTCXF0)jnw*;4+H7>5C@6?iYCv#iTR=VCt zbt+iL_%;(|5^rvyJ(MbI(CFs7XcbS#XikGtpEnD3FONn{SXRR+&?wMCRK5QIhO1k2 z*K1WF_Oby=CsqT({MXBme%lblUADs`wHGM~_L~FaQ~7B}BMUl|Ll1Q@s1#K@lq?w@ z51nLqHc33Ye8yyc-sY4nsR?tjE^L1V4Nr~1&8pAH9>>u3Oa!13O?UM_9Vj&=(iOXa ze0TV0(KM0}=+{flfi5$uZ9+Yd(wzwZ06j!(+R}M?~vVAoC zgGB|sQ9Uq~MY4M6cSKO9p#K2f>!V0mGzwE!x+JmHW`dL#x(Fatp4x9(=pdDBuKwQX z737)}EhHbngP>BIqr#UtnUssX35}1YY-X2Vj?~i|g0YZlpwa&T6(5d}*3~6Uxhn{l zi(*#<3Xsa#bbQseCMBbQMs!&hNX$-AWBagX8ZTVJ1 zZdYx#>ije>sBOg~{{XQ%VbL|;t5qOeyndC}AGa*;I5}!JGH?$MWjR(;#T=DklCDnT zcIKH@IpL0Eu6?%*-(%HT5Ls)_2UlOkgD813+5b$GP+`R zEGp+J=V^KPmkW2W>Rf5~?v|F>3Ic^B5JA<%Jc`A!hrQ#Xtalg4z2&T7D#CF802^}$ zk37AJ$j`7Dxb#V~QqS+CHbVQ8BdcAVdBDNfl6*Q@P6@U;9FiySCO@Fac&gvLASUWrI_ zx!RBj{PdBkY0RlZA`ly~=~blXaWG)ZxXdT+j$)m6(HRE;3lu9|UC-o)XF zHAyJ0kl|3BilIB8%p|Ahs56QyYd7kjcKMs6S5&tRTRR8zPuqN=3J5x;lmN9>k2{m{ z-!6(3I;OjBt4$2rC+(_V)SdM~a>BG$a~i33!s&5MBE6wSAgZ(irkm=5_?EDd){9|_ zZ>cC9mp}gNz9Qlo-Vb~7&x-l-#g$3pYeYsl1Bz89I;JreC_d}3*4vdX1uN=pNU5gB zT$=~W=6&ggMo<&gdVl`_TZStpy@sWH(obcJiWC%pmG+-)U{KU_-(3btTJDOCv2Arz zS~bGsWRR6IwYE@4a0K?K4~W%M*g;bm@Je#KbF^QZKfGW>mZ4wV1JwKo)LIZ!vYU&E zCJUu(E4?G4l6?oR+IoqC`lT56b{+;b%0IZ-r}cL3AD)eaRA7Wo;b(%k3(({O(dv)K z(?kZT(IFZn6C5`wlqS0!^qK&bRQqS?j+Bm+75wyRV2;VrWMBUP0B&Rb1pfeztRv7U z7d=H3XboI(T|Tq^dajrCr@+w}&_;?({{V&$&p;z;Mk}bLH5#eKuB0Mapn@?4f7J@6 zn~$n;RJ^tKP*RlB^-!s*7C-ABihs{QA}UPD+)=k!(fp;cPtR>hX{b?&)f8dChMOJK zpcU!x(voPpXZC2}J~#4D?rq}#01@)89RrZK;_)`^Gd995(c717xxtBQdB18yE&`RL zI`R|}M^F!@v%HfcZCKJfntnx3#N*vG-~A)(6X);U_Oo~GjpcKRt3T)#D^+LE!+xtqa=pgNexE}^&oAfG^QTIR4Kkpm_t$0TSE5j1J_dA1vF7=K}vCh zR{B37&)mxDhW!$Le?29P0W$YEc1iQxk#u+5b!4Zm)u#G*IHtEzSU((9 zmV4G;!}gcNoTCQE*26RJDQJZv8l8~iX+sVXM`)l*cIZy5Hu{j34GC3OcT-0odqrvX z{C|gSSLh+(C)}gPe>vk{aIO-?CUK4LjP1}I9BFV2dyH9VVld=&Mq1FO&|BR1^AsT{WDDZ&%VvLD3VJK9oyK zAReIg)rgBk$Etg#%*%tMl9g1ByXsxEG*M-cBv)<{H=^i0MyG5PRr5>Wihp&IfJtzU z?@jdH%93adCo{?%t_FC{%t! zM%!wg6JP9h^(h?kryV@ok{ab`7S|s3OF$n=TuMPbRjqzIm>*CUxU!xnMUu(!FvgPj z!^e+*x509Akv`V8JiJ}i;pZ+-x@SzO#kt~-C&Y`6=0UU|xUp)wvb5W-f|Ljdp+C&J z>!4Zl=_7R~8<>B%E_;8%tgc(W-0gT8v!Sowq3kr!c&>FqRG0ZHl30BQ^UHN zykg*N+sNKXV%K?>D&W=HO0IeST_wk4zF1S9Pq)s64|zv&544IXrg?vJt{xcBBS)`z3>vWkHu*qMjgch} z{k_zXP@bJeo)>9yS-BoecW6lz1NmvwNOX!Rv?z>+9AJb6a) z?mKrAs8qMh?+Gv;CakXB;~}g=m0&VaWz7(PM1-ew$C`vw+-Q@szfwImsMk{V{{Y>) zj68xV33NyDTnF7>VBpKN+*^&456|uP?!Iet?lt6|1Yu>S?XPd(c`d#DxRw_KRrBAH@yV@ab-l|ATr@w_eZQKv*FFG|pK%ql z$POX5IeEln7a7kn7E-4s6^Fa*6hDF(;bfO@l_Yz-e1Fv zZS*ygE{-FsPsD#e<ppCv;P2WMbEJ)T!zEJ zLB~6KsP>AV;jRmL$Kb`t_ChGvYQ3fG_bbR}ZN&GE-y;cVJ|p6rID<#`Y$vG{)@teW zYV_tZfUY3@(X#gMK1D&Wwz8yQ_IS*x2ER*^R$V=QLs?s+S1z;0;vyQZbE*0fQ&2S$ zH>)`YtFSyPXPJ)p*~N$s`ycfrdH3zAJ6LgQDL+2sitEkpalYmcBcm6*89(~3Yno1l z;=himub*;1+s^&M88wMKH*m6f0cE$yddZbTS!FY>?xjgXj}$mkrn{BgprQUCYp_Ra zkoEO;i^lQaSjz=NJk!C>ZLOaF05B@L2uW=Rb=D8M ze0H#$C+*$A%{xW$zYs#W-QxfOMBItio+OH$$YEc{QSy%56Se__SNoSC6p$QBDc#Uh zRbnu#T_%ZE^59dfvNjDMeLk8GZmBS~fe??ibWtZ+wAu|g@JjR&BLtIY%#hhTIHFoM%bXayRY z=pcbpf`kpuj)_1b-Q(k)A;F0rmM6He*C zX2Ef%9c}hrQd8a8+>_8((@ME;Em7^Q-XsA~`}D(bzg_02Zu9PT{D3sp9^lpGcB^*M zFg^(#f2T~Rnbm2cN=GeL**BY|y3-A!^vjdTW;1COC2lLUQkq?}Jy(X06pOp2!2_f{ z09`G=;haImJaufd@)OTMo)`rxj*^ETlB86Xe13Y~T-(P4Ks!RXMYZMbM(DaBQB_KB z9EFX04n?x1{{UeGh{1F_6YJ8trIPwOQM#@5mmIijs{W~=#cyMF?pecfEWP6&7=?0b zP2IqdqL$LG{+_$jRhGmFHx;VMx#rE$v{cfI>i!RszA0z?Xv}PXlI?OY5hEqA(w{?a zlkN8I4L=QDHje05CG@T!cbTh+3*&xU<-a84V;m#NlB{uVPX`k{iOzo6rjoD<$^wc{ z;Y~An3&jXDqO-Kh+X%~Kk8vmBKZ5!-!MqXW%s+}?UT>Vb?p+Cm!$B%oZo4U- z-L~p=OaB0{y^>HPb{ZG9x&XN2$ev(ac+)7pV;sN3Z#fFnDb`!O#Yeh{ETzuSu$KVb zjcOszxHhCJkNr+L9Y#8Y{8YEb*td-SU1IWb92(dAsc{wU-)#$wS&kidwAFNi;zHKPrl7HI~w=Hs!JqbTw5oC;M^o zt~)TjVRbil5=m)k*qUxSY8<$>G4*LyvGTc{9Uj$OdHaL2el(3}os`;D3F!&#b;=S7 z(mDlnI**vP@`A0nre}PGg;nO4@js5UOM53(x|Q8csZAO}8$kqVwe{OT6vOpHtJ@=V z?W3YLy%kyjHA&cX&?Pt|TL+$RaNz^gS45l-2)u_JboVHeUb;6ni6O|gl8&gmiRwRw zf_<*458L_ZQ5#)U7qWgjPSm=s{j;MYsBbIJZ0IF=i%K7cqne_s^GHDip+#wI>Voj} zI&kzx&Z}KVZ$uAOp;4)?r%gCPPYO{OYE#TBxuuk-Y1kz#H3~c6YsCtES$QV;Xf32W z?lzA@zS^_4&r2Gi$Z<<~g-2B<+qu`JCnOthL7m{{Xc=?Y!$?d$BJq zOdt1#+O>;28@ie*JB}4Qbpc6so<+y*p$cwmVN2+q;kebTH_^LLR(9(ZJY8B-#x>5| z`a9co1RsGF)RvHYs+YCRI)$@|w#lcs+6X?nj_Or9#<+J}z-%7$`fHCB>M#|y;ofDN zgjnXXqBQ&Da%<6Vg4E8Mz09SOf8IJvNho?P@Irgi2mw#6ME?N1bpD`G9?-V| zMEz?jK9#PL5w~3txX_g$Xe&t#e=@Yx@`ktKtgUSc)TO5!YBOQBliH!^HB#w!8kKz< z017Kn;vb3`kCS|jYqncqxNK40LAlB7QAlsNSAAr8Cm?JQmbB`(oLi2UyLiyLONxvsO-qcZ6-Cr& zPMo4G$P?7*#*Gj`5xF(|w9!URi_b&(>BH3_X`@R(o~KQ?Cj#?Cj3?eP2}P&nEK#Hi z1$6b&+7(^fwW1Pb;@#ZieZBSQK=sng({MogB^w4h{oV9SLn>}9cU3f|f(n+yK}9-S zn@mO3_YNodQ_{L< ziYFqHiKRQ1Zb}Mj(%0>~x;j5s#R z5x>ts!CtB?mwS>nF(N78{-kX1uUb*8T|+vk{9vd(3m+iP-H$ zD9tX5_ih<1*QLl`#5%vEenS%;~PK0_SxazcO48~mxax&7Ck=&vP(~W+q*HVexkQ9VaP!KgDs;rMPPM6vV z0M|(=@Pmq=w5dLXROq(^;HXFo9^BOgkEEWTo})gsQhOyyv?)mg(?Il09;q~FB6{~h zHn&Hvj3l`VG!ccGB%#%2f|M7!2q1lZ&+z{M4H|tJWnHeeplCrFB$l&PIC4NUu}nVb zKB4RuR-ipnI(}Md!$m#B6#|Wq;iN{8x+laq*N)j=f<8yzbF8ISuO)c%BC|QdxX_SL zi>0D#EVn5ole3+xDJcqN=3@bc?IVVgCTg;L-6QDHhfj z6QA+1&iM-e00f-nvPG)w^`7}|e#vxz;BMt?n9H$cM_*po8GR^P4Gn0ZH1rZOK~@@% zf{i`AuoBmT2<&uS@bFX2+n3wfICn4GEr_|}c`!_Q?x86wT3c6hh*0>ET5BQ8I4H=r zm7!V=G0shkwHhvqS-$qZ9NW1Mow*Yk;_u(&e5q|inC+FwC~x*R^s=-m*A}FJ5~4vT zdh3&a_B(gKX>l$2uHXLv+dYtkY;7Dq3xvF%=Z`S?1IQM{XSYTp+2PDW*|f%BGQt#> z2i5T>Z^K<$+kjhHW1PtR7c#K-Humo@$I5)Q(E;}dv7#1lh2?Fdxs1Zi8Q}VQDNUg% zJN|la+6;%SX!y>BhKl3g`wved$;U|>RHgR1%n^-wD{5uu18rU-y2472UtNZ}L)lud zJ71ANzPxQ6uSaDhY~n?K3ThA}JSZ-yWf#m}mNt`*c@N6=z=DRw%}%4>L+z#7{{W7? zQG%Sl(Wl_NcawlN>Fw}w{=dyve&O-d`lwVwC0@1LQUj6;P{>&6T4{BvZOunpT_}2k z>8S&viuR*uPzSEwx-g@;sBZLpbZNb%VPC;oFhUzv;m?S!jU=&b9ibkd6QGF3jU~Wn zbQ2C@FqQh~CS24q74bCF6e+hg4i`#qx5HA{>L#|K(%=T44G|iqOA)j=UHT7(l9+Q4 zfnulO>80!`1*>$gU_tz}d)$)dp%Cv{jU=Qwh|(PB4_}U@DDGl5+oT#(Z^ueZJ~Vx| zG~*vd5$sBAuhbn!eQKdf)S%1yaiv3&B&f^Op|09Dc&JW~RKF5c0N+5isjZ32Zt#ye{%Y3DMJL-;j)~?v6y}D=Ao%LHg6^*2h+@#Ay zpQ#)5)?Vmpw^k*2%zT!fDdB!b&z6$2VYSFHC<#BfaRs*eblmGZe!v3g{4y_6Z<6^8 zxa3y}Gww1KB~Lwab;oDOB|%R*>Wb8*?mLMj{;g)88!K8a?E?#10MQqXIDy*kSs`w2@9a6#4iTlMk6a1gZHarnIU9$y+brKx8W|x->cI$L_tf}>_ z$`q9oPSm6jNj-G?s)JfOl+txE7`jc37UJF794Fpp9pR*uH7&NCQ)zh#Q6wS20+I=> z4x<8;Jf?|wEg_AMP$+dJIZCG;fk*pA+$7aRbo{HS5%f^x>#B}>s<#OL08!LA4ys5- z5Pp%SQ6f^@`T(b{j3oe(6ru^C{508Us*o5Lt@J;ZjWWF-saiE99D=Xvk_Sp?(@$+m zTq3_JXavlWqcKffABfR_n%<<0s8ThV9;j^~{{Rh1XGIC!QaomT^!C@ZfwgqBm2^Jd zKXKkb`(Swsb>{v);yibjZ+AOdZAyia2ir^*i{;|%84=L6F!I8?RIbxd2g6l)TmZUg zuVrbBx%9RF0IK@K&%F8Z&;H|Y2YH8qfA(!RhP=1p3tL%Tg9f-i9(ylLhQWBsv(9J& z^CiaA(t^^3kLExGS3PNqmd@i}(yec=8#G$=Uj>q3$Axs8ZM)mo65#>oW6xy%wxlrP znRN;S)eA`n<~6znv{?`w0#=O*(&AI?K0^IRPxI5D1i^E=qlik`qGPYIDg7!PbYVBT zy-?_B*9k5tto%-lDDQPps6B-JU*V%o(GItr28bPcX~>+P+81X2#2k%x!~Wu&aUH28 ztA1yV<#tQ6WSTr3j~a78)BP%rNUu#n0NQlc!21%ao_IM5qa8`60QCkm>QM!MK?Bwfmht#fRLP-)9cat3IV!A z9i2r}uBEcoGVA0?cM5vb;ivr&qPpm$nKwSo!J+l}X~Axrb;zP_OZ|<}FEMd~#O7T8 z0Eo-%w(3>+EFDXab9xnATpUpBy4x&<8VO~FfDE#PB?w3z012Q2OgX!?Db~$R7ot%= z%}<7zb840*Ak}aC!%n(=t)S5$+%1zZf!9fUOOO`}WKT5xRi=YuqFXKpbaAVY2Blp( zdg$8`Ms5O7McE{Fxk7~s6rJ`uBW`-7{ath^iJah=HzL~?ZObfdUL%5)G>1VBH0lp? zZnEk~TTUQ=QnV03+#OG{$xR=WbW}NRJ85&N6maj}sshirvgyc{D;E>^R=Q8=Flv&Om7GDMLzVwgQ}N z#FVER=$70dp+dUkod{*C$O!B^tM{hPvhZb6sd4Ractyp-T=GF<@wL+lkb&dQt141gQve=88Q>f7( ze|dB)6sxkdsnxKvs-QVP6k0{vY7|RPDPcqsjj8$RO|vQ`&Pl*jCHodfXH)5oc4;YE zj_Wj~Hq~YAl%Q3r!wsFbbGFJTpVJ<*$49vxEiIxd3PMRER1BJ8>Nt?L7edjEt0+@R z{JPTHg(yKmY|&_-ANXq8D+jn%t1&)iDElB@&h6~xT!^q`KKd|`=IvT{}gn7rMW z0+Koor{n(s%V>@fM?8!w-Q+(Narco}dhtxw4{WHt&SSYlK_YFoQiTHBQRF8k6r=zagSaEqP@Si`cQIet$8`~;fc@)$`x9w*kIT=Gxn*H^)}u=FsQHiK z_}PCYe%>5&%aAU4-x)aG0mw8m7TYOEwp?5vT9We#plxd3>qR%}4w~Se^WDHiH$G#6ft~k}efACJi;^I&Gw)dM?YJeXAdTXm^ zvO?sEM?$jR(DCz)v=BS>G@AY@Ny6AA(C1Pq>G0M%@vG37;J##h&B?}F;v?H`09CPF?;mXmDtXcp(mhnQkd%+ik)mZ9(&BUh zONs|cAZtdX&@SxeZ;VTYS+%Msw9gb`)+-~<>D_5Q}e%U1lF2=(O$p1mR$=c z^LsV3TjYC|?k%!oSe$fq2vw%q9B=|Q>i8RcHCvEAbi5>Vw*>Vib6yxOOl>4oR<-;z z%SxLlLC;^iE!7|8G#{4QZj~wK^joL&gYwdv+9e_pW1F4!&1xH)gvhK@bb7mPZx zeANNFpg_g3n|epo8uy-0P2AO0#(*okckXejV`?grfY3%t^Fb2L1Qs5LK>&f%RZguJ z`c8r*UC+?17)B9YDJ=jxjS>mR86VI|?xxx_pp$!or7wNcwuu=!B`;B>9*89yT07^* zO)3f8ej0Ng%E%RFRrHBVf(5=~U4#bLLApudKie-eRNd}6#g_jn!s8LQ5qxqWaFCiqZh)#$#btgtr16H)4G}!4R zCr4FsJQm^`pD%NqW^LB@E(~Fiow?jpgw-ukUfW7h9V&aZqvBS6rN_x&J<`lJ%N`FAmz2&;vf*{ROqwWYZzS~;00GvZhv%NE-{R96}B#P z+_+`5rPdx}-0nafK9vnM^bs`fBBf;FvUI0yXrgvL;uEsllLweM%H5Mj1wXoz9VrVS zdiJ_H{(5pNni^3}cNlOLYpS~);jft94$QP88;dUA#&?T?s{Bc8q8W8+AkeOWD}6@0 ziPCD-5*PqNK)t_Zh17V_KYZQGa7is`iIzEWI5ntg4W>`g^AhjVs4b|${91$(Cx zNXCK-(V|FyMY>c+&q^)QsV@s}bLFHYFBGbZQkpaoM=DqOXwt#aY12U{IcT%fK!mor zX+ff@dwTQybP~zYdrhbH-r09@9=peoI)2FH>=9@-> z(o={H^b)mRXzGUIwhALdaYPC-#OYD!rk+!39SBhxlF4Lh54DZMP@K@SoQm84V&dT* zpql>xo~Ff?nZ4CK+5^ES?N2f$#{LSDU6g9CvK4pfemvO03Sj+tMk(`OSMh3wJ+5zOnIn{rKW#S6#Vr9N0X}4!4(ApoV;oA z&pYxvIJaZC&8gWj9C;#i{>7HtO=@aJvYGtbVQmLhtH9t)uiWm7?PJ8>7H}UAc$zDh zCK!!!VlZ~&a9BcB^dq>`s+6JE8M%D7lWD5ya@|L79R`$NFrRmCKBajj$kI5M1i6** zo*wsY!S>clCe3M7pegEvQ~2w$aU26-U~w9*Imz>;;Fg1;<11|?w1N#!uD4Dr2uj^# zic?Oc(HBxlp&fK|La0Wfy862sL2+lIK&jFP#Tx-V5og$OqEfV!DJF#{uB1^NYKkb4 z(?n9*&`?$X09KNfMXr+IjbPG3Fs0PPRl5vr-tNa{RHZcBjZa{sRWr}|Q$EjPQ_nXp zV5D$rd%7L<9gvFEOp;Mb1u_h}*=~y5XzYD0Bmfgi8Vx?snfWG7(>=P#3Thi((COR~ zN#8{06upF|&#{qS7gto{fq#esx=!On;E%v3O{;ysY<_4Kl|MvxudC1MQJ0x z3PZKvHps{N$qO57MSXt_L;8W)SjUW}r97L3kJ=Cq^xFfiX`(N2QiL%z7L}LSU;>c% zQB6onM*jfp(!g?#>7xm&E0Y3GYzSr5dMKI=Jsf7LBd_S2@-pm}bwXVcka`4-ILN>$ zmruBAQs*S@(jR`J~KqyF7WXcSeO-MJ%M($Dt@jy{^Q+lW(*_3lVeBz|L1Cj`c6 z)lIRXly2y{d&vAV#jFbsLO8*2>^|r#YnVb+99$Up?)% zMQQYpO>P|8u2Ah!z&}Uebl^?xTh~gWNOIYiRUPEG*(q>-g^xp`V4_=FP=J2@bW_E* zB<a^(6ZKI!t3!@1iK4ahL8e(#JwNYHF!)JCR93sIREeKD19Wswojibo@s8iljLs zOHX9V`d57$I;UDH)THU}ijRmIazzkzNlo-RX{pp8G|{BFwL@(q1{5Hh=$b$ojoEX zv1gjLyV*jBN`(mc>7%byp-{JVAQC&ivUeJFlm!&5B!Cyw*VqpMZxga|=Q*y_gxoS0 z zAF&_v4WxD5CvZJ=?XfjLE9ZsXv192@g;K89A+7Xfb?F z`gRH9Z9po`d5veAk@(TUS*^kn;9c?bxof;cc?(0VxYA2%ZD~*iBq==(xyQD5BHt@K zLsq!xyRW>KPP)L`Tpb+ILaBF&Svs*^c;oA!U$T@=X$o2N4lA_lzCEIvFDPEeRxh^v zDspGpt+JA?@gbI!58{!m_0Xe~2p>?mANME6m5=!$Dta0%v69;1$_hOc;tJVW{{R86 zM(`W2Qp9{TUS#ID@eWDgccJ`$=%^REXf?1P6Qap_XhgaxzUN6vF{4Owzb~GjQQX8~ zE)nw3VbwBNj3v&s)UfKNoWwdFNEAA7GNCl+${W9NL!q%ZwQcSXiO}@QPQ)N^8dQU! zLJ|uQ=wDIzQ0S_gb5X$Ir%Kb;K}IYHaJuw0(WbVo(x7)cABKfeNJEzg!$g|cheMPU zrF3m=6Ha0>m6AXwPP8UVP=P?~4v-2#Y85Kaz>OIbN>K^q=KS7iAk3_Z>L&R85ovWceoF#nndM7 zduW_6U+thOuft8TwFuyK3U^zi(58ofT|=HVp|g2Ui*(J?y>&@)%~rD2#Xs$ms-$nK zIpbP8H>oPf{{U)`d%Eg^@-?WjmsMQ+`Q?ij9`H*T{zYYS_iE(XYPU^@%bweE?+itt&rT3>$aIC=;V^x9H4Lt?OkQfOP60{yG063A?8nG ztq^|=DvD?U%TV<|_>Ba^n?yTLx{@eK14f%BiEX%%B{lBJA&%Y2+v?F#IPRJx8-)9O zU{MuF5Mp>eA zIN?h*9X=W~7*Wi2^3+$JVf7R=@Z9y>>PuXs%GA7U7MY#zv2aQ|j_s0=clS8K-;F%SjamqM%bx$I(V7TBMr{*4FyM zhjK+J`Fd(Zfk!SXVdK6I%{j}D{G7<}s|1_mt_QPQ@@w=+tSRND&}2H)hrWPF4Tg%3 z%TcZ}VkZE5t7{7vJ)45FpJq;ay?Fls_Y?7hAhuQ5`EEkiFObLGX_aZn8dDB5N9iD~ zEvxd=w_&e@yTL@5QQL=LnxAzJ0ei>)06u)E!dta}YP30*+^*QnGB*C-d~^GvRxA9a z*IWR6w1&B_h!9j;dG{I`{kncyeae8udA8Nq`xGGVM@oEjs?jO!*F?I9qbuo1G#w2z z>}OR3gnhLj60YrqG=8E@7oOut?%1fGQKdEYLWm{4w+%ov6se%ofToy}mi%Vo!~MH^imZu6RQL^NIi^V4+Jj1hor41Fv$ckV zQ~9sE9D|Fq{4PS;zgS*vE!VNu(p+gkbRkFwT(-+E1f4Wia&gPJ8`8fd$bCk@FtfMi;MR$@b=8Yus;D<v&1l7vdO#M0VNA)20Mci-&*0efPqahe8bh@N^ z9r{;J?M3wnCnQvY4{;yMq=;yeZU>4B_lNyjf6)n_T&+y{Y!$z#(wl4%Np?FRf4C9R zRMUQk!AJU{z5d|8{wQ?mw*e?$lp=0w9aZ`0y%c|3i>~OpSG(}jV&7FA`4U%x=DLrs zZ(R>f$o{6N!mbrb1z(GM9Fb43(wB#YxEGG1&%}!0nFkD!<)L!rLNDNsIQRMo~+O z3IznG;8I8wXOE6W&a)ZmZNWXl zCyVo`YDkN4QkKHcXzs023Hj(`_;VcEv}&t(=erMNp|bQ-mMEDG^sR0HNCbb5 zu=6roW3+|aZLY1d4cy0dM?dNK%R)*OO7$9YE9xOPAJarl%w&Pwm6cP_X~^;+$(b+( zp&?AQxg4n~N&f(J@2O3gIa1u&J%`|cwV=k5hk`$>*{g0->K`igr^`~^a_6&QIx1^u z864Z`LZw@NRqer;iEfrqhSpSlC=yBk04-Eo**Dh|@mf2~krvLcg86OCt|-3pPIG*1 zAr0K`Hzyk)*SQ8tl9Rv0Ctj76n{C)W3y0w0-6NmelB5eo$-5>yd%U==*6qS81X2*CO_h4kg9P zU7>lZ?_arG*4)*+ZRs0TH1c=JZa(sl1i-F%Clo^BOS3m=lJhG}K!pXN44CbpINDMa zik{)HI_Y=}Ikj;F)~kZ@Y{0llwe1qfvTbRCJNq29a~1!rgo z37F?W`0Bn+sz5<$;E85}3p5Zw>9SL+G(iMkK%l&+&_D!9LZX$Xl$No!{~o;!c8X-4dI9596f-vYxnIy;J4NceA`Pw{Y~XuJD${#;YaC z@-2m+c&Ra1o6-_oj_S~{(L2-ljcHpJs~p^uXppj~^DNl4!7?3ElJP@fB_M)a#L6#3YcAy;F^u@mq$tYeb-Up6Mbty21BB6(^-M zM$FMa{_ci}uKo*twY~3t(MtA>LaDaeLv7p*Q~v<%;(KUP>r$V%#Y`!n`=}K9fZI<7 z*XE%-2AYnEw?4wA%8C(Cq1Q-!Br7PdMLMJ(VQj0kf+%+s(1>cJIEM6wVeTap^gbMtEP{jF5ldx$a{7X2w*w&51OiFY&`Jj@M z{51^S7M&EEoK3W&%+ReJpUYn%GmAU;c3qvle6Y^&d@HQBLOrsTuEx#25L0zZ?x)L5smomi5T57DK?U!)K6(hK zz4r&hMMRx}XShBZ6h*NHwRa?KN*x$P0uM%w1VuDSV4&23DM6w}3KF^$N4a}l1Yn}; zQnuT-;iCz_PVPzy~PjZ&6r zC?fg@6@yA|R2RRVpAOLqtUj6~wjip0IwTXdX;*TJg#q*)noDS%Yf61$=wS*>hs^>_ zLV9V~z-Uk=X&|M1gDOjiF{5^rqhhdsEiLoaL5>fCi)P9$txE3P>Lh}y+BVT3E{Ac~ zNoxpBHF%!7P)6mxjUjKSi73Jo&{wCfj06cr!71LFmDwd8hZWh2?fotP06Nb~YAllw z6uWC(-BX3>Ok0@@#{EQ4kxJ?t+D@fi+QUr(v>qn-iO7CXXBNwCs@&+XI`V&ibtqS0 zMR%d4HIdEcn-fb~bz2N3Ai3er=)TpwT=4^mJUruO?y_L9ZJ!c9yK}fa5n7=;uB)cG zRGzKV=oa=%sO{OJ;0N7L?x!y`xLvqUg09PA+R~QJLzA$f+O$-pw%U_j$oOlg@h&T{ zZg^bhKgjoJ)$^E^6t$_>YPVb^OL1G0NvR~By6KIDtZf44uavm0l+uT#E7xrzi4|Cu zJGM#*QRqJ%1l3aPY)<0)22;rWvp*H>rLnpvPO4wys5T39X%0&WdQnLzS?gbwby0F% zxKU`VSrBHuR411>i8slvvXI&ezLbueYT7F8)lg1+japWg<%w^4Y&0dbkdy<{N^1ZL471SDFsG@=@|ZcK#*zaehd zs46tC<)9-`-!qSO>SDAAs(ZdQ-~9C8V5*7gIFxOu?Ia%5(2km2RDlROr4~{8exHw~ znQ5XyN%vO!kV1M5IuWLdQlvT*1qeMl>N$3)z^+#n@|?JM(ygqZNV>tV?7O$^Cva2J zgldlK6C0npi+hRW6EYou>Xi>4^S3qJs~*Wd^S^6zUkmI=QU3r^{{S69ef_noY0(>T zc^k=h6IIh(lZi6xZ;0au&mBzr-M-qUURp_6`cw1*qARfKby0@CYpx5M(O+^K7Yl8! z>b`p>MqEiUBRjK^l&01`X(S)=*8HC9kH)DD_7R9JLj6PBl{e?1}HQk&5r$P{?1 z&>b>5sch((;csRrNu6^kAj*V)zc5qsn8m=LJCO%K=t3@vDB(*RLfmW z7LXk5mYSMxzL`oY+IYj`UCYXjU&pwj)0aWQS9eRVNXTc%XZ$4lqTreBpC`t762_|@VjxL# z-cc^WnvY){OCP2?$R|)7|@>K{{U8#>J*KL(p=iLriS#x zn3i0b1;$!KG0b;TkR4xla9X}Q^wGG5T-$j=i6*1?CFoejM?TXs{R+u4ZH@xa)yDMH zXWWS$)&7(^@)A&mE6^x{eKf$hZtb<#!80taXsOU7bs(UT*g;Ojp1OXB!>YXRRS!?6 z{B%fd2yn7%pi@PtbgqN(=rj{NVpV0$^w3ZwVo+`0p89#mRT1%}MjfRHI#KAM zJ&9&J)KW=0W#u3>I$=qtZ=f2lxK{R^6T;st)inPAJy%T1ttG64C-tIXs&~}{{Z^{X zsY1;xcQ^HXHCuApE89nExy$!nYan{Z!jhO_ZeuN*ie$)0J&Bl5016{uN^45%n`g9o z^!K)I1_HRAlr11FH9_iim@`R*b)x zc=s5;v+vn&SL=K`tYvJMV?QA%S_vDD;kW0gFLfG9wQFiXP7Y|rq2V0W$~bMV2lm@$ zF?*X1+i%?aG1_virmEj$kxe4lmo}-3rumZ(arBc!>QEs>EzuTnEHo;W)GOtn0wB*6 zS#BjiP;j7+hT2<+9n!W05V^kEk`p0nTK;EkC)6nYAc1-HJXEEqsb|-H^fwYwhS3+( zWg5*A3&W!b!p@$$6;AqL3?)mBs<7&t_|WN1bqZzC1R`(uWb~T9hw42wBXS;#P?6<4 z`+&OPTC4hruAX7hN0u<8YTUnYg8Q5?lI{{Zf<@oEBZ+)d?nDQ_)Ja#FD_M|G5^+gnAYJqAOx>ZG`j zY#;Zvn&8)Ok}}eh$t~Lipp7cNHIUr^?oWD#$8XD9ZK{uP>V!iL9mP>jnhauRc_{m6 zNi?r&M_m|9R;c>Fk5Lct^aeBVnKH$D9y^TtC4bgcU zs1jOQ+djaxOL@?uiXPG%`1~}^Ryf}V*9wB7PU#rZ<{CH(kHiJ~B-y;2 z-o4D|Q+ zLXvm|(}iPjDig`9SNuwImb`}4^`?7bs$t|5)O=EU5%U_=SlmH!+Q#avR$j{H#rq{~ z3S>DoXj+eUtl4Tp&N}l{NBf$;)&6>W=~REN2v)A0)uO*T>A5Y@$9cIJT#_nEsD3ro zb6QDL8!nplM9j4l*ZlQ4mgb5nxGQwc=~L6G)8-_fsayW^{{XL0%qjNWA={fzU-Q$R zkxzX>6%)7pT{PH_MB6SkM$>aWE2l&${{X8-Te<;Cp1O}D1;&a*cj2KaN4$VW`}}L6 zC8~BDn^1Qw@-374vQkyqKoRj2^g3O>D9WlbT(Uc3M|4WooWj+T$89mCk8xkAzUnD8 z*lk@kb&GsPrMe|ratJRQPmI$t!h8lduDESHEvxw&4CyHs4mv&wwc9 zn!Ml-mD{6FTw2kU*b4$YrLCk;`+JIfwO4(2Zy+@TRj=b%Y=xl|CW?8tWLH9z{^(YDp&Es24nRF9y0pz5wHuxOdTuF8 z4yjT^*#bqEWt0cI9lE4$NYv==+v2lX3`6Ts6k*JnO0Yg4UESNqE~KjL-Y=a!m$7846>TEKi! z-gU`Z#5{848?xUlzSW*eBCR`rQP#>psQh)By?dJxD|d%x>xQ@DzE?rhuR-7{5GFuKQ9o@2IHe)nLz|l$?=3eq;KARCi)#i)8 z{@ZXu>pEUWk+#2zeR+>$&v{2LYZl|YPJUktiL9T$%UZU_a#Er$;PLJ%Lse3gN+=SL zRDK%K@T*o?VWJP9+!jCGT$8eVl3ku*86bVX1xoGFUb5KXTVyco32dds%ZBSqY4+S# z>Pv`lB}q^_6p>M@yLZ>3z+A>hP`V5D?6zJphPOy?Xs z;;uy_Hsd^J+G(t&WR%ZwQ};4hL%KpzBjze)Y`GLcGeIjM$Sa>5+0;o^`S*M5kf4w0TL3fK^xgtHNy_Ih)$A;9YLOeK;n{i!@qy;1zQmUys z$lu*MN9uI#;nz>M@?M9-;Ni#HZD-;~hN;l3N8_HfP^A2Iae7OmWM=}?=OTMb118RIV zDp1N)6vQr+8aO1hSrK-Cu!Q|+r~;aCmfP5yH~x&erX;L-OeaWM2}(~|Q&lqBIj&l0 za&3{i6(QiZ^nUfKD0dotKZ(`8IvO}E=5AxPd&+i&F(H{jAE*y-ja28Y!(^(q;@3g! zqK7(afv&37NX71ym?36@3p5Z}pn}aQ&#F;%R%t0K)b63{poo;cKE~`zbbFGYLeTgJ z+5~qbpNRamE_gPy6+O*LnFEt$)L93>Wr;67}Nk#M#m6XweR7=sPSSX1GzfCYw+JZe!jY$iqoG*0X zdxSsj`3q5ET--qlQ|SPzqi_nodaQlCfY!P#EK@>TO)8)sX}4lFoBWZ9NNsRdRoEIJ zr}Czy!c7)#{0~y0R>?9HRH~X&(^6XH6@yfg>#A3-jXXjv+%&_TR8?E0TWmD_QbS_gl%sB>Xq?ayilHOJarLE3`db^*L$ zYL*u*5t5c!O;yr^;&#+orl(bN4Y*gY!!HN3{{Sd+8+ZQz100WwCBzw%6qO_?H6Nsr ztX6N3hBURSWs1ga6(N1B_}}9n68MA0aNK$&roSMuJr zF{1gf`?CGjvOZ7BFT4%Hw^uCqt+mB5l;4}v%A!_6Ufr5;Rh6rT3L9~x*m)ltK!wp3`~8c^4# zuAL+z)Ty_R@b@Ocb?ascJ0ZELB((AxS#3U}aw(`Tk)6t)6_m~z1WN73T)}si?0Hzr zw%F-aP}77qp3~E9NH;Nnid=lH2%+K(zQYmuv!*h`Z30UHWT;SA(@9}tx&)2yVAQA< z=8`487718U?mazqANHrBbcGQV&!_JC?-neK~g2w~cF}y}vjpdgmw%U(+mMTh9)auUO!)rBE&$=&_ zb7Le*#p>e0-Ay{AcMZV+>YjhpR>IM~P@GtG!bPf8N)_7WEA*0oEjtu?CTDmDs+lF? z`4(u|df#G{TV*x3DS^~Rv`GPaHz0g;NRY`-Z&TMxYifv;bZnFV0K4I&qd-hgxY4IVDP2a@7)^ym zQg^PNb1IY^ja%nWwMayWj~1Ol1T1R_K&G6{QCGiN(=Po;z|nYxbRNGQI0`}(=}5?4 z=#+KnT{>vegDS97YV^?+BrMQD3WSu@A6jT7wX1Cr2?|!?8R&_Y#AVY)rzOT52cZt2 zjr9&eP^a6t^h_*{2lm{C?+bLIGwuYZ{o3ut1zzl;T)}$S$NYA!$(v34KVRD&MVj!Z}|O%HdjOvUv>fUW(#;qZ^&1 z7qNpUiuwo5JR$Jok9c>KIWLKL#Tez;oLc7oGd(JBvsJ`VK|orMuDJcLxR=+r4T^VE z?p>gJWp85xR2Bc&IE@7h^A zf&T!w{{R)yUol}v&bZgdPa*A%q@$3mU)i$vK>auC5A+t5ABjR${4|RTfL=n2cNdg7 zRQ?p>4Ck8jXdHdVHdSOQ-kr4UAr-rj1dtco}b|*&pg% zjq%@xIcI|{x!)4=b|Cg7=OIINR!GnGt(j#F7L=ykM#hyJb=L*v8H;(N#-iEY^N+%G0)Y(y7QMgIV4 zE9)!mTgbyP@&4OyaqjtcCw-?7NF*fm{Iv@y6%AHFl@FqoKIFJkvacJjSGE}sO~YnH zp2~*mOF_?V+JEngHP)NkoBset^f$3sFLUgDOIFB#wEqAdKZ5zeE&#fN`i9?zy%_Gb z@mvryMZdUq{{TH2PQ)v2?g#PFHT6xtG;IzZpW&&BJD7}UCQyOFP5SCp5=#(?SlXR* zt;q$ee)FNZCu$qHUmYrmy`da!%8}|euAE|w_A6V)URiS27x14a`1g%Zf}Swt_oK?p zt#8}dkttFmB`8YRkiV^>QiOv~hSWN#IH=72uW+hwp?r{->Odx_d`|1*wC)Mz)-#y* zNS;9n)|a?ibVl7>t;h*bO~HwjSV9y)(Gtg)XPz=bftVg zO1?BAEV=uaRM4UJyFenpJzlz<)G0kdO`@ooT@*!jPT!834XTnGqTDzK$mok`k+BXG z4v92chYlZJnhCPREq;0=+7&AK^wQdp;Ef?vuKIO{MIFsUp;Q~6t3+ICnsZV1-2$GP zk`zbAm6~uGP}4w-6bTrQwxhP2X;B{3pwot<<);f&Nf?!5y}0Y9NKy7`m*PA^ht*9- zdr4Lh=$&ySfK$KW*H!X0T*CU%Kkbr$?cY?F4J%D(v-EA1gKOD=v-ycLvOcV;K=?@wq0-mQ#&AG;d zx|qnC#uq!HDS+M%0m^vQ+WNzh4hxdn=3gSD5bA%kUKx5zLch=b6(kRitELBGY3i-5 zS~PcMJb8b~JY~!tQuBW*@cVOE<6QpqE?A2fCIo5ivhBVzPNn9Bdq{b=DZb)|*GsaC zYbaD}_Xj?qR(TS_YyKLLp#YOtDaF+-qqKSxzKE2t(cJ_6R|r#z{6>)acSy<+KIOv$CX5b zx!CB$MFT2P{gUB?EydKTltA3**d1P}r#uxfaeinqOO(rj#Np*p-GwGJsFRy<)>%Wz z2`>+Bg)2tgs^|`-x$Zp_U5#&DQ{EQ~UU}*JEDsg9yCmdHk~Dw*j&UeVo3+|J&fp8o zw8`%=)IC7@Pg-g%&B%?lcSQBPLC3*;F@2Ce%$`Yck3Dhsn!Zrsj&Tk-#43J&7;)Xu zwr}@q`_Wv4iIU?qTugY(HuHrEQi_zN0zx|laD11OGDy89YOXOj$1+XdZ?{jucYp5x z0LG1ZA>gNwSbAIzP&931oeT=lFcMv{WLtRwH<=D@%?-$u+d6kW1x7q1k=L@ml zF4kLO2e_b8(@_hGBV^D$L0^uKAm8!msj@dz`+dAu*LU~YNLRVCNEAO4*I4gB)ls(S zRTsTq0wNO;G?J0rg?baKdk8A(6#Y4d5)$E%^Gzz?I7*F2Tk>@ zix(XN|P*4lm|3bH|`Hk30V%Uq#*90 z>s?s1u#G5HCC{kC(1e|8&GPJGOSAFIyvs|;AeUKv#>#;8pd&(j9kk=lz*KlS4c)te zW61l|;bZe(oAIE_E#K33j(`$cn$vG2f!A^Y{{S6iC-%D;El6z%TU#%B^A_ftw*lg$ z2%dT|DdwEd_^#J|3)$K6SrOd?Y)I}32D+zrwGNN}02G7;rHkL)!~F1qlO6Np#S1mr zYRI^L^=Wz1pxx3lah@H$Dw5iP0;l1&j!$f|PN4NF6u5oy!!#0NEh?zB_Z4P;B;4(G zTuyHtTBJDZnC@aFiA=o1YokeZDXN=sL(rs;p0*PEQ4qET)N`o0+&=!f*Q7SAZFE;s z*W}&-%vf~XZ;-P1ZQtQWTln?S^oFE3@0fT7QJ1E>lq`<4Fy?<=W-s=w>$Q4mzea^i z7la|i^XO&Ngq`ypkqzRbXLfn zMI;3isKtdXw}p8$M7glI@|z1nzCRjia2H14R4kM++_H{Q6ZXrdBXd>S()!m5psle| zQ%(N>hP1+FO3D?=E^TfsXWbbzs-E$@)pH^o4Tv6VR>8RU@j@; z{r=x@P&WvPbwE@)kwt@FQZ-2HNgL3uM0eKG^%|#?8H^|)_$v1npeWbC4OU9m1FE#N zbAPj{BR4Xi1?2}~duRrPiqf5RV{2g>o7Ysu{%&Y`jS(T8my}69+Evh>N`5*}#R92a z$}6H#g_3P~UO5ScD7ulvO7g8Q*s+^iN&=~|WR?_e4Jvj=mKaO+SH+@_xt{^6W0 z<4o>DS1R!>>pE8@WhlQ`a~mXxh;^k}Tx>*#Haw-b-5E-3lBAl@kVQK5tmhw|W<0rGoXe$QY> zd+ATAlUCPXhg~lsIr4Mu~yCvDZP;AzZaJTJ*vL#gbScq?inq>y`3-(aEDhB@LpXTQZ{^B~WWV|*1? z##soDl3koxE+IbK3ia-FRXgg622rKnD_d}U;h`Zse@zW*zN^ntodpzg(@I8z&_Q|J zJ~}F+q6pwz918y}906aogP;imVec7^HqS58o-sEQpLSO_OQG-%R{B+PP zRlN5*SlIRbLrZfsLX{|_WSxaJ_0n1r9tlPtY|tbMRE_JWq6rmWNk44gyMGdRAK}I0 zh|$CIFC5z9@6uD5wus-cxxI36#l`)@s;h|%A!$J$1nf2EesAnu{%4Rj?nnOsfKyMB z>zr5JD;#b##K1(pD}`B>OL62puH%v3-VfdKInT?UQVk4=Zx@z8^c~7H8udMelP&Gr z6FM|ea6G<6_cFRyYDpzB`6^p(UYWNvujyIXZaDlxYIO$n)pC=Gi28L=ql6CcooT2l z)uU8jMx9wok)WK7UjG2M<|f8P@| ze%*J1T!(P9yv&HNq!c*Bn9e2CC?26mS^@mE%4UJgcAzK2w zgOm9~ESa409$M>9=S?ABAawNyy2?cLCTm<}?w%@%@#>J$k706j-7 zr)4#=HQfzSVgj~4no4m7r1sN#*F=KWyPsVeeY6YB^biZebP;% zfYk%2sm1Lyr#6T!JIAhwwuwy@T4@PLHTq~HLFgi0>H4Bs{@n(*x-r!jq4;TqE~-dV zJ<2Mf?j05L)SvEEJBq^F#1K|g524>pZ3%33QZ$%v6)Uu9dv#B>!l^&|d&JhAbrcUW zch(42?TbIFNqF-nOzBO#b7|dOej1iWiIXWP;#qA1y}J&lL=rV9ld`+j?MFdVhiYgA zA-4LXE!&dX3-Zf4PYH@a>`of-Lb}v# zO6zaO@L&)~Ssac^x&`vrbjNQ>zfXQsOMXm;6sM#rwOWa(HS1k;fCOxySy>5jOOSCZ z)WsnM35*WGZV)M^y>dGyTM?>}ZCp)gUu(DA-aICi`kFxnL+~d}iSpy(Sb%2uDPB<7X8-xwG~v9C0(Fb@Ya^o0Lp5x*)C}*s7ySH z-oMyB)As`L2{i6-lU}~Mxc>lI8yd4?kjnS!RV2vmk|NyXx7b{&jT8aiy31TJhv*jm z(&?^@5|g50`3=ozL@P_r6&eKu8g=;Xrc7@qqFlGpG*p!EzG9b*cE*ErN?22yHqA$O z$EKVSP;1p!#c(B(qoRx$izZWqGRmD&tMq_Q`Z}w!KnhR5a2$eOL2H`0#uRvuA;(@{ z(?Av1PDK$0stwiSOK3GKuGu-ez~SZ|wmd|K(fu_y)nt$b2-CZ$;HV~#%1K=A%`x6l z{hP8wvR-L*QjnD__1|DMZ9AOyP$)JNM%Ve3Xm&i`+_;&Isi_H3_X5Qach0)a(C*c2 z;&U7{PuzPx%&}!Qt2CAp8KFsbGcM5o0Po4Jrm}L=K%!k(MEzI+BF9A-s8Q!~mQ>?}smBdgqtuhXuAvnvj;h~Jr7d*A zG-t70l%}Ln64%{IDr?(Lx^ag@ml`0=zy%?sHeGSeklH~iB>Dh1CrNXd(N`KLA&N;2 z8Y-l0dx*JPGHf|v!(|w&{{ZL5)B9$};-~v|)g8|yvxw5QT6-QDeKTqW2gOKC_{WgI zWI31TnD#N-`r2}OTy-LzsYn~@19?kbP{tCiS;6-I0OXJaI(RdTfi24Ct2$phC$xno zzdpKPXRZPN0LG;M0P0piYbahl;>6pVW$Yu8Y&lRL@T0RiwqGZx`dvDDJn=d?q$C~_ z8^`#h1w)ya6LMw}+Le^!l30d{l+FqFSwAqg%7ryyZ+8>MhgF}+ahtngR}B_C+N0Pd!asi7TLl>Y$TO$ws)(OC;0NUus%etK-k6YXJJIcm6E8*lb;R{ejR z4Ts`%PGIVvV}M8P)(e%n@Z^*R(a?f-)7H)LQRi?eaeE~G8kAHPbx1D*?pD1|`!yW6 zDGh4T(e5%p8;?)JMY%f8sEk$iI7hERqiH2NP%?!a(j3|*n!HM@T8dLajjBZ|P{~(7 zeIECQw1bVroak(^49q>>0JP*9~lV?})fE+8y-aGA$m zKHA~3H$n>g;qX_U=J{jx*R@W$ds1(9YgFud@Y_Iz84+4jUr;qQGzaIeo?NdC>_;Kn z{k30Fct&NJoCm5pmh@i=JjZ8y7v!AM(xjH;TkbO2bw33)AxJ;vuY2*Sx7jrHs0-$& zE`!p@NZyojT>;<-W1PGN@;{9dvnwm$E1W#@47Q*JI=ft?e%^8royq#chi{IO>P2u5 zimvp%SPeRbML2txv%U%BiytNNo#A-?VU8_owOtYteuq;+7E0SQ<0uV@czs5{jY-}?CWwDoR_ zDenFlTIL+#(fY2H{hIkV_E{EB$L~l1Gh0N6t@nDQ(OYXte&Ryn*KzHALi!TqJbpfV z$2ooEY$W* zvPpg{HGr(UUoaU@M}@LpteL>eBcJUS~{emb7RM3Wja+uA2KiHF9Hp~82k zO+Ld=qdADeS6b6;EELO{he@wqx{{kAZVf*@0ul&DbV-$oSHS){RZ0b@!)_lW#R|>G^pfJB#9etLF-xr(@r_w3OnIO67g2`p!n%qv`m<&us#ln8HpM9N5e*$ z*oI2${MGqs&80M2w?fD}n&|`tmo*wgpANbzDDGkvE9h&m)P#c6XfNaW9Wor4Ek+XO zLD%Q06bY9x8cP2FBc`_$!D>-u)AZ?Wc~InyDYWlR^yMg^$r?V}+l?p{74aoD9_hZC zEj3JSOHrRa$=`oX2^5AmROW*DDggRwqT%F~Z)m|(^S4OdARUIFzg2e*j#5RUSJaAp zwO4e_D6}^6(5Ig*gT(Q2Q;CjK=8RXuf7S2LjGGhVZL>?F_#K(y#5=36?wC$X21*}RAVB!4So~0xowoD<#Xvg0(++4>+sY^G1k-Ia zLPw~JKe6l^6=;h=PDW9NBmV$TaZjFB#yeQo5%cSDKpAP)H6B{50rLrHq27pByq9CMn65*qLhxe+|o;de4*h zq$o8S;YNeN8t&TYufs#KV&$4vs!HIFOyxfSyx6j3_zxsV<4lJQy1Q*oc^}$S4q0vv zBqYm2=_qz1sP@ai(t@f46xU2$TC#T0+65vjnKn_KM{gQ^zQuj_a2MUbj`=evo8y<3 zwi$jgc~y)e3s+of_!5gYM;?1Vo6=5tHJO5Y@syS8kOFJ?klDNR+~xLGDdO zKB3cCJa%*Gt_6D4ZSOEGySFr7mmikCF=yT_^LHa~#&c)=jq{JLvn|q^4hNE^km*wn zC26QoQ%XtKp>1zjcMy17fY?6O#f~bow6_`A3sVZ(i6?L2qjS$hkl>F00D8E`8fqO>C^R1`XMRXzv~QwTX5~soGNymzS!*p@jC&h*;*zH zObh8V?1{tpW*L}sCTDQW5tQ=R{W}GTmT0tc9g^8DYKaPWa;M%~WGaS~NZV?fyT`a` z;ebMM8%~yNUk)OU1=p>t+J}q zW4P38wXna7%I&TSR~pGlikoZN9KyO!XXCyOjQKi0vw=<=z@{osG4s9sf+zQ6!gMKn= zC;tHQBQy$D#JI18bIA80Ix=ymBD>muxj2P3w$wK*IkxpQrn7lWwnEw7A#^-82M>J8 zpDj_F*M@vKGDmfjGKCsTWP9+`;#9P&`BW=VMv6XrcpE~(j_=v*L)}pzNs92@=u&Oh z8)fOK#@^s^D$<0wfCu`!NcicK&R$2Ilu3WI5JceUr3Fq{H;s-<$=io`ZOE{hAGjB$ z&=l1hAe5;Fz12RN&gMCRY&CCEwcLN(M!M;uQBW1(xA^_Xmgk?kzm7taqC`V{Rb-0LJ={im%**!+ed!t=i>f)>KD2(h0MPU_I@x0R z9lh3#(5_?1IDSJNtc|ODLWz|3&wXjw!9|f5Xcb6OO+o!#MRgl$mFt8Ems7I0_-G4Y zq5V?aNl>8vUsLeXy*|M?^%W@6W)no$*QSVDghu5UW<`6q*aCY00G5h`dw_LR8b;@KOH?CK~d(EJW!;Th=xzK{WR3@h7P`|r4g5P#RNb9zdOaLzN^!Mx^wj7s1D0PSigbUaBo?0U zsVZq`-2@Y>5a6@#hDD=yxiDSr4k=0(dL2~t3#S17)}n$fo<6%xh`5ksP`W|iLc-* ztTuX2Fly-B9?XDW;<;epb+ETuY`R+nr3n=$Z8ZYuTXz~R&f6kK;vW{FTm96+lPb=I z$&lgg{{Wu3qq#uS)OYL7*l9o6a;Mx4Ssshpe4)+7a5h@>e1H@2P-bn!-WxBA?iVae zkuMhv^A*RNl_{wmq7>WMO%%&1P#YB=!;fR^;A;YLcGrp+~-y{QBzR`M#BlTimRM zG#RokcY=XY-2)M~v)e~5uBqkiL)tbPqj=jGYSV$bYWOC?l|>D|POH?iiY-M%*W;rI zkP25(@H$B;VQEpISW#~aG=!p2a;o}h%0M((T?!>`MY`^K{B#m(wWf_a*Hs2i{kj04 z9aOUze@uQF2ny8{xRXR6AJj(Lj$BTO=9Hxx-6Qskm=Xgm&@FZ7PNubr8{euN zZMGEQN)jlg5$*mY(?ekC;C_r4^=-mbCW8dTpamRT>HH{VE{(ABLQD zf3-Kb9;tHQkY8<`zq7eI+E9L!_Yyk%^)lNdJYCg&#yZ|ktqJ6gP`yZsyLH8cOKD=B z;T!Mk`0CO8f>*a{rW{hGKIiS1@ocK8>+;qz=Gm>3)1_BQ#>ii8 z(t6Q1x@m_tieVL7_aBaunW7s~Q%eD%tt+MvRHd$o1-7IlsVj^v`3pHMyC$<)T)Q)4 zaSq7sLX?E|n(sh#^@APiR*I7~bsJ;aPNj9n6Z1=l=0b^%a&8W&Ye-9PzF9-b1Od6) zwARg;D29#;oy(7qnzZPp_*^04ACNHwym82J-j`Y_1TwIr)DUUV=&o*UUD+xKZX?yM zqJ{Yj?O7Y!c4iBHJ%*)*i-E*}*RJ)|WR?&~(OX+)uUd80QH<}wer3cK=}oiyi%755 zfJVdcuA)hRJz7<~T&;sgiYmj9ah_?7IN~nWDG^Eu3MI8r5&3GmTujUrqPT-UZzp;t zLy=^%XyYnD9YF+Yxy*3tt>cxh=utA42cgn6QiFk78v5vpa8Y__o!Zd=~qHC(N zOG-s`9U9mg=#s%$0nwI}O+hd9X(>xx1Q)*zwJE2{g%^iN+MQ@p=VE>p(o`Q#tKP)_ z0H;9#9Tuges8Ed^3RC(Zzi!{6MSV1;by4bzI)pX}B9#0z+q6UpszrWtZ3EhSmGIE2 zkRd3#KY2YvuzPKED(R#XqMaOLqE$j++f!NvyXtzKGg3;K8vrOPd@H_Db7I?Sh~&wV zln~T(0-{MY9W*7CtZ7d&GWS!m?$3a~YkWkB4%psS#Q= zFzc_1Hx-!@96){LC?2&ZPQKZ+=%Tvhg=o0?>yKl0;Ow=bPXvQgO4gq}MSL_VvpGG} z4<^oK-S3Nj-04hYEBbV(s2wY*7W;=q5?UUq+i%Nn3yhZg2n2N`o%MMGowa6fZP~dd zU9M8xZDmfC6V|#!?kYSGwMNapMaiW%6%r&ksv!y$2Bw;;k=tEWe{Z8zk+R@sIC=cnK8UAkNqC-dVGeP5en*_j7i%? z98Afu+b7duw*sVAhqMpzIx_$YQ)7_zO-6Ft{r>F+*9GM;E-yCK5IT3Rn`xO{Q9z}| z2x!%ICo)zegmF$EWw~5^$x-98-r~A<_JtbM^(U^RM;T}en&MDN3O2T7*hILAZO7Vh zI5$qkBW|=hwS|=miS8iuRIkXZMl5ASxnr%Nr_`b(B?g`TwWgq5!~(s+#e8)p}%Gw%2OG^wAKI*J_V6$#q~>gX5zBrIA&usY^tsy0rDun%boo5vtxJQm43x z&>}fDQno`aAxlvwYEdBSB==E7D&wM+Z(}s)$SPFHjQID;*&Bbke$bqL+C^1xp5kMr zDFUR?d+Lh&ncA??o?^9D9^c+SSv-T{o=x#*1Yy8Q&`#Q; zlaOR?4I*U+iqJ>KaTda|$+^R_(D@02;ULH2t}ugbQ(}$ZZYe~`bpn8j)PIhhXB(bR zG}0fETe*$ByLMZ|K58Gz`Nx%WRk*8$RWUJ~uWTk&>@*Yb31}y6TSbJngbgaQSDcde zb6vy_0HPTyn<2vFr zuA2`LH5wm+zq91mFi;b?d{m_uKH)x4QloM%IC$S9l0rsZXF&a#S^jsUkbgQ2LwTMp z2BBXv=O2HhgJZl=e5=N+hRCW)4${)a@@RVi)mE7aFVL`AhmLaVqs zNGSv@%bzVskfwqcxO_&K+*8W}g1Q>hOCpFbY7ax9R7+q@M*0;?Yqdj#_NXWw#-q5^ zEWcGLWXt~Xh*)l)srVoA@2lhn+BWE|19vq=icL~|Po|Mnz!d78xamymUDn*c)!FVc z55+$XMkIorZlN$7PhoTsPTGxFG1Uqbt?*w<-}dqIYlyt_r;is&q(r^MjeW=1Tc1z6 z^+%9|ApZd53M7B4UR?M8013C#x@gy-`@ie?FJ}JOxhZpBXyemg#;cg0biNdu!yZ@i z1SevaEg3|G-0Z6Yjj0k1T&CaZ*ID*^KFiEoMRjhAivHku;jqmT1q1a=ueUcOLczS1 z#H=|Ckrxc)=B8qKaa#KrF|TZv8B>(&+W!DxANjSn&%#SLj?0wdq#O45s?p9IZ*$?! zT;v`#x-h2YVHatT5Tz1I*lp5TaGDDDHd3nU;}>zh5Pk}uBM!++RkZN`0LDuXK5<8r ze4T0K6Jq7R87&cVd}oP7Bs#33)(ZascDCd!r`uB-TweI{3aQXHd0S;Ev+PiR6iYIeZ9Cg*K$C6ofmHRmdEO` z@m&o_(5RQdPDgi+yokbXCP8W}$w`Yd&=%e_{{T8`uH7=Bd1mF63+&>{b4-}gUka;l zr7kwA?m~q@{{S6lW;A!nF${GSE9S4xzC2GIc^#U!8IZ-REK+Xv&=W)xBvIOWAFU*x z$6l7s=|WG)%fQ zJ4+tAQ6wf_)M>XpeKcSu9>jI0O-A}iaH@>Z+x}WAil@vS8I09P6Eu#AHOo!S1otDm{afk45pD>&Ez2KMr+-1}PMMG= z;Waug_5G!M*>olv0>Ev*LB0Y4W?j<)<_4<%Qgz4OX$xMTyc z6No#6SLnu>=D^RKQ`DlPnkOEUmO$LG^B07e^ty9;go?y`v z30s!yN(y{*fHguydTM@#dw1zmr_&(_7!EgDSL39$)lazSh_cyf;t1JY29$x+BkN?5 zQ?NaBr4w-l5RnC^QhSqHbUwO4U`mcs6M5)w!dZ2aNrsSI{>a+3&x1@3$5J<|%1a+wKwe&$7m_J!~!!FOxXgC=NtZavoXtCbEV~ zQ|>Ch@)Rl(x|6izkH*XyF06i^Z|I=s_`u3Ax-A^te%~+Q;CgiL?0fffi3WYA#IGT8 zckS0u^LG9*%I`(S+(eX;$M(67$O&3rLetuhc8agkexa<#xZd5kJYHLhwcTi~eYN)s zWo)iZ9yAs2{{SoItM6rS%yDnICzTK{&bv98c@id}xoIItVq6wffZoIC*wFgw-wz07 zu%a1>eQSpbu2Z*m$7Z6kVTK$#D5q8p0EJj$ctQrTps+-RBzh;P+-cCzn|}1B%&dAa zA~X-&x;m+QB%i}Zm}?!}qYq_wRUk)%e{9)3p_WH)UA;HchD4^qzK*`excrEikC8d6 zi6K`BRy#NDKpn2CGcHb0sotqC@A&GncU8bFhbp++qV2SoNmGR<^&jD>Ok(G5mZc>b zos|wNr$ec1h!q+0(Fo1dw4~9^!LQ1?2GYF}p}e8$op$`mrd>zw+eDb)cDT~$LV+JW zSIduhHEOz#BN1&Ms7O<}I^o3EIQP76OQ^?gpH$`>aYdw;fmI=xyH0Mu?e9dj4rsPf5~ zL)mt==L^^+wv;`+>ZGqfpDg&3Gq$+h+ZbaigxVC+m{UPAn^L;=I0tYv_-a+23i8$U52A&C z%kj6&qoWV|l`G7y7Re}0>yIt+U}36qvu)QN<;Y=u+m^_$dPm2ux`rzt*Eek1mBe`# zCHwc<)uX{Y_*0T`JEmQ5wR6R}iB<#_hCPs5Sq;iL%AchbtNjV#2s=AZ*VT{ z|!y^YF!Q78c-lGTNr3`<|>mAXh#lB+`F}Y}Y2zFXKU5(sLEA`NW)|DE~ z-+O%eZ-v3tTO3EdQGT1I77_4POBv#y!*m>~)UyfzbU5sRSL3Mx))@$qTw@ ztgSL|T}7ePsLv|HY?e!ry)gjXY)enIcgG)E9FFW|8mOVQR)ih))s>ao$s0`y&1I}3 zXgkK?;HjP0$6Q;MW@J=@dr`Mr`&s0 zaOlI>YM3*)zmTnsNs|64G3XIZAD+ufeSzu-TV2AR4QTE2*3ueoSlpKj$iOOCNFE7} z66zMDxD?q*`jVmAq><=B)!a^Fx)qC_Iiq)&5R4?1nk95qDxE1VBe@n(S8{LyB8_Q41gwUHWSYcZPF1 zF1?H7@!S9_ROTimwv?@qr5bJgx`i8F!>YcyY;re5jUyQgA#5(a`;V1seKkHS?KN5Z zj39QQBejZM1Lf5zNJ3RmKs}UB=BG|ceh5jlaV%*}7Mp-`C1LaV4&oWbPn6BB!ZM`d2{F7USFvap0Dd8AnDNz@h z?HP0x0yJa$himdFK?m{F9Ogx~SotPBnrZW02Z3STkj%pf8W#e7Lsiq>ZsZFNG0A*W z9_wX|lVRB1<~`c?h|3j+agS|K2#n|e(F)ts(_E9#%njgJ)Z`=-fgEIxc z{{VZ4YChz^`+{d)GvxSB5#Klm2x|t*bGNRSPEN2y_R$X9-9lK7${Q3FStM=Jy3uh= zRm3lGl+^ZI)0AZ_<%x}iQ^i#e3iw6;03bZ4;Ql$GcH4c)t*&GQC%gXufp*3J0Cbt& z2qUpguu!Av-L2t~&5V)YutfQQ@%w$gDa+@_FQ=Wnx#vDYj{g9Csmt`MRy2S@kkzWf zf3rqH3U(=^a-e>CuC}OXC#mX{u$Pn5vZmXE^V7)CUgyh19aKxys=ZG7G-$HeRm$~Z zv^EQZL+yp8pXxd|hUr?=?C;qNo!}J7&)0IdF?>yu2)0LQC9o^nvqbGpOwAJK26!r5 z3|m-a@lmEyv?5eE4T>V%!)Wat0*XO8ti5+Z;I1tgX;PVvs){>x)DrmR1l|d73-$oDqBX0b&ZmA zzR21UCn)Tik+O9jG3H8sjYF=dg7ffPWoHB|;5F*>zFgYD0TC%7T&jjr8nu zY;{wQj5)MZO_!3b(~#6$hAL31cK+=N6t7B=t7~kNUHX=4?+D?m6UJifmi%iVyIS2t z#!PiKg{45BsCo}gPPv_pZk^R-m8_ni4hpOuq?2Ecv(ai+^LC3riHE626hl|{8qsR(kwBQZl_Eh4L{IZcA z!h{ysv$t^n0F-GY{+5tddsFfoYH;S#f#qValiPbKUssV;A7A`sv*r;TV%y|w3#{s& zZ8IH0!R|Gsdx!X)FaH2|dxad+suBBf1Wv0QG*Jc9z`ksZ6(bq`-J3)JlsyJpWyMo{ zkZb;WRC%ju4}wL`0_nX4N5MeZcL;Lk43(Uxk1n>Ro3C`G#ah~+_3msaQ&QO^GX95E zQ%h+yTD_zwoxr3TEhG`uCt^PkbVk*~szh=F(P|Vu4w6AmxwTsZxX})%fTA<`X=$Cs z3EA_|qE6-SX!vQ(>0YAQ03mfLtvxh>Cf!nwp>+Gkn`a;L{{Rx-v5buk{{Ylj?Mz~| z0KU>bg(M&G*G%KghW5y?{;;@DD7w1EWP`@BxlnFO;|!M{$!^T98sCP?ic1m_JApz? z2Kwju95m6&d>3QGa+^t^6mY7C-j4GNEjrn4XzCOQKjW&+rJ5C~WX*7>E5z&tbf8jQ zQdas2C_n0av=X#H)cka5#p0Dsle)bImc|lMyy(87KHOd?;x``}pTAz>udmn5me z5>^7!RDw-*8oq>v3Q=;q`-3E=y%#2YuHYNKG0E2?T41aDe5pze_JUG7l|lM+EyTK0;F)2JVZ<#vA+NOx9Td%1UbKEV3DAbDe)pg*bs1sI}ua&#WT>bOo zGtPW;$l%W79KuDJ54^VDwf7aWpn_|%kNz5+Z5y^#Nwp63b{>UPtV8Z;$dQ9k^Xy6{ zQ4*<4iEV#P-rrN&s*2O+r`apnsaCS#fz%LJOt_o>0A)T;;tIUKiMwKIS}2nuI7f1V zYH8dkK=|o@TzWVx0UC*whJ{V*aAwOYx zGj#;fLpF~NC+2WW>l4X!Do9XDckB3Src6UBu8w|IDB~;er<5#fby4lAkc0}3)RR%D z^5LpKyUVX^C=0;(Y%4NolrQk$2^X+OVI;pvw4Heauqv@((89C_lUIZA5Mo0coZzVONd-M2cIP(CFFx=Cng(LN1Tg~_?1>B;TGw8)NQu8Byd z>DXzv+&DErb(?nuaQO4WE7n)X3(g~D4>I6ESM8HU0op1mbyEg?`}V7+aaMX;>bdjC zR*TL(k+WF-bc}8* zcaPw+xK?Aeso^gf;QS4+_Dzs4E4jD2;V>xIaP;~&-&N-GC3pH;uq%^khqCH-l!GhD zxLEr?+qmNghhsIOXBJYoiKPJ@z@4<^w-|GcW_43jb0$)j>!RdBrHvaO4A+dsHxaqa=>tsHX)1&F({bZ4zoOrc0LP^C2Ex+nxJ z3Lxxsz$U3FLM)iCC`X*wC|LvF=u`94k9{Z;!GYt!O#E@lS%(czOxrBK?)ZJo((Ptl zTw&IaQc`wGhoRMV<}s5BjSAA?^Yhtge9iDu-y`vsH_7=!sx7(Io>!P2f_nLD5pF)6p1L)Y&IX+fbMtQf`o(S)hX53MsDD zu9O*6MQ`sPhLJ=ly-L!0nrI`bLezY8NekT)3t*x@4~C2s5ZY1-QpQ~V=ZatLAF0Mt zA7MVVPg-fMI;(4G2GtoL5LORRbjnPLTO5`nT$?J{DkUd>j*SL7h?Ywb!??&wQ$)CX zk^$R&^ik+jQqYBUzS*49{{XQMGPq|edPnW;*ISA#*PY5xsMjJH0YrcGPR(=Qb+G>c zS&DcaK&tCL$Y;KTFxvEXi@qOzA06MzFbrFoEEcl*@tQYveJKQ(Qa+@p{DC80diEQV zeJ$&)o^&hIAGsba{=*i_xSLYWtK`btHf5%CYmK?n9%NS#xRMjHntg`@t*m^~q=Gf= zdx(Uwe8Tzn9Hp-vh%2I=Z$VEj;k+&7&7mITE?l#*Y|a7-NQskJ(fe5~xJUE21%LP5 zXL#d`Z*(r|74Ciz_O^EPDUtU1^A=ZvT+5L2MZb+Lyrp}*V)uMz7Cm=yXt%P};2|!) z;u3}Xg&_b0k*V44K5oNk>TNYmV7VJ?i@dwHeoA=Z2cmN)3G!DS29RzdJJPK1QjnCi zTdUozp#GGDupbR`9Pl{Yqb2N0^v@A{b9{bSUHYg>=3Xs!mxyLCq^G%=R=HtH17#&_ zx&Hu&*KKRJ#t81bjdy0~+*t0d(S5M^_sC7>yhVZK4LR<9{@)zDplUnPfRj`4sMdb& z+=W{sgG!?R0CemKT=7f?lj24$NU=y=y5{OYqGQSdNGksT-_kYJJ+8^^E{dy?`;BHd zvQg0Je&3hgxRjNV(CfC5GhZA1EmXyjST*8OUXzwCSYxQC%85+o#l1<)<17iulp?o~fWemX->L_>!AWbl*<2s83>6 zj`rhC%9{;NC~tj9JL)8=Y$@LX<5cge>0r@Y+Z7^OBp&3B?}n-8jb7T6jpQB=eB^!u z&*I}rdPYTwu-)PuW)NI;)?utEaV2yHF6F;LM zeu)(B?ymdv*EVDK3WeTghL0g4y=j=<>{3z&!Aa}T$0Ht4;qWa1F)vm{bv-ItP@drQ8eO6iT>NcB|FW0N#lj zRJd@{K%n!-`4hb~iY3Hqnod0QrwW|?B$5&E(i7AT=#DhlKosfl(&IX&YLB)%MLS9P z>E|}7uW3f z8}@Q;Zrm!B1=*RxuA0r|0 zr=3$U20K`{c>|5A({7dSl&T%l-z3Xxu_@U5h0M!9l60gu@^?=@6k zafn%so*EF4>x?3ei7l-`J^fUQR;O4j4r)1G_KwALYuqf5hB);jQ~W=lw%F`&RM9zAo_39~U-?E}VgQxnCwY zY2LQnSYevbe<~0F`D&%Z%~i^IZN+7$g1!@E7E(5$S4~`7>J}PlLsM2fs?*f!!YeUVRW?NUqAR*6nibRMsh>oeIWm&wT@dq1g(%df!K`hrW~9<+ zd`iDKUJu4Nbdzbv8igQ8Z{J*%r4A)YJAErvel(?Z%qP0LlUE8Z_hE|1z&5glJ8t(F zR;bY2Xxs0S4}ZI2WuWrgPT?)AYykDHlMs=@rlAX%;%Dgq)j?S=7)89QW$HbFs}t8m zd(62SflazP{Uc(ZQ%;)Ic207vA?UjIa}a4CQ}?kE4ivfh|J9?hForI3!LSYUpBc*$&oTOUA6K=K%SbEd4i7r-7C%@SwWY}fZ ziykR&47V#2xX3EGDr5U0#4ROXKq?jP+Mc?;u*+TANMg8d@#3+09zTn}gj`ukvEAEV z=?5;hbI%M%u;x+u5=-*!$JvnzC~|GJh;EV+(w<#Vq1r~o?OjR5PmaF0F}gjaQ|F=0 zVeS5+IZNB$&UsLNvBqrGTlj8VuBtj@hL)>0oeql~E1rB(ep*T38ASG3lKtDCdl zY=lz+D$qYQX1L4GTuNR~8YWR|a`053+8c1G3?;!p;aYovp`}N_YX_V5mT1|&GoW2h ziT>oRB%jipb@5iO{-=w{Nx9v4=ODM^cKNRd8-~(b+@^%L-Ad5Xy65K3OLuIQwOxwa zEp3rLrL*XsaNe`rZPKx<^4N^W`-=YnqaATQ=8&=lTWpe|ntX`U1#WJ843&IBpPdw>#^&RbJd5m^fB` zK8rR@3M`wHmi>uy-++XvKvU^bz~6sea!iLDykpClYP}DD=Xkfq86^QyYaDsT*$hZ( z#cr~(lsnp@)vjN2N)@Vmo2rsQ`Hg3#%iX~GJW`8mZ;i3KWnyqOOx(-Gi#|Zqmsb`Y z<#H9=CA!KZKS9^(WDdHz#`8&{=;fcxdtYOA-#Ym{57PC?l(1Ht}#=w`U;|wpejs%Zfj|2=Ere!*KPW0WxdF0 zpir-|o15npD2=+)cXCS!voh`8QolnCAx=0?pb1Xo*HN!;qF}6sfmP#i7mkx{xFq-m zC0ZEFA-tw9RvEVUqY66Ii0-zegS|*l6xAE9t!4c#3wvc}lY{>NG;Rs$ZkqDK(%Q;O zdK2h+>O0|S=&WPBlC(s#%=TUZ#+kN9Frg{6Tcj>KhoYTY3J^E-snd4$Ve+&sBzF?} zM!GMbi&CDrOLk+Y+FpeTs#wG)Cb{XJT&jk4jlPXlF4OIG+vVD7$YEJ(EGp=x7R}#D2 zaqM6U_IL%1C3(hI$nqO2i-y}IM}qY$NwZP_>+nvwU%c5@MU8ADtd0xN{{Xh^7p;|j z%eqF>M7^Hyzm%RuWtp~L!Mqoe^4R7CW5U-eq#q%)z)ckf?rOi{+;6F2j@CU6mzAL=be zr{P)+R$Q9BSj6fxqC{TF{Pf68xYVM~AE4=OWdbmYlR;fHlopk#u8o~i)@kHOwKUhJ zj;kr5>7iBTn?{Q$liW|kOD-xv3s(N6is_x2qdEk)S~jCkPP%rarrTPg%_u2qQT+65 zASFn2Sfxt7o zCWB7V5j9G<_BDLVuJCM@3uE_drNzWE5)5{h^R76x1caq6f=SY5c#XT9mkS*CZD^@0 zt$4SMJRRZ&H3N{mrI{NeO~$tK+&StU-rDdn1;y;ZFRg!L)QJmh`V^H9^3V_i*)}I~eB&Ig$e3I0zmDb8t?O*nCAn|V z5%Mb2F2~01p-6)MNX9d$rvunIk zMeo9{737%8yKoOPM}60ab9n8*fZ}Qm5)D$S*KKGa$TiwHYO@pA$q3xXZXODt@-K>9 z-^Q-GtF9{T#shn%;}81HD13I`QcbXaps2Crn_4^&tE_=C6Ut&+3y$(l7ShtBs1fl5 z4MuwlO%&+FZ`2j*a{{TgG}ir4bQDHX_fMZ)C<6 zbCBhV1$HQawEPdmYE_OxTX*heil;A$hW0&@3abYLpPIOorzM0RI~N@QE4cdBqgx0$ zJa5uLs^=Ge-#$HIbrS2B4jjxA-659aOjpDN8l73R<8FHBsf-!BBjqH82l7@MjM^Ia ztaB3QajH-aYrm$ld8~c3fB4N;O5z#2$&Dro9F1>&Li-zRsH>)^V}Z`lE!ExoBNaL$ zva#9G8}}61?fCDhFSBz(g$8aZ4FYZ{3QCBlO+Z$`!iyW(#?(jYAdgKs6ZV>+Agf4S zH1WU6&ST^JeKsspE=%@bTb+nfB6}k1DY*`2_9$YZG(q zU%h2%Q?k+hL0Z(B5vQfbOu?X3qbmfx=(@?{&)heJvCLtdMcb=(cNX7jlIja|1v~Zl z>W1RY`>I72io+nl+Ad)E!3B>XvgRb>mo}%{yUN%oP*D2KYq8Ym9s^Khq)AYj9z1=w z=jRuwOUaxmgWQ#^lz~M6plE*@YCYZKSUM^2iLM{FbprYak>id##ymbP7Kq|2QKFXn z8vqoJ!VL%=bwDrN`6#WY;=z&BFO2UdEO|~X%o&pp&X)8a-a4yl-CKvX1w?_lrrw&f zxnzwY!04u6zMRFdfw*-?_@m6uV&X^RF>aF0+T77dTq!6!pdUpiaC8}yky~7G_+)N| zi@p9e^Zow-p7EHMJFgYF5eh)}9#3gQhLt5HNd|{j>}#GY(Q<3q*&L;VS|Ys7$eaO( z$^PBQ_9roEuYw~w{9;>DzDr2$J$^a>&}-2qDctk5omV$GA?^dqQ90~h=)&A{xS=UX z6|HCoO(NscBSm$N`i8sA6)#RL05?F$zhZX$xJycXm8gb^rn7Q@4vTFC!KDz0yOPW{{ZcB!0uB;DWz*oJEA06$g6D7r2hZ|qN;clU)%Zr z0EU=KPxo(aqx6EJdTDh~y%JJ$m#Hh+x;8xyl-Vc~&50)|*PkU$I9Fb~&}jFXYMyHV z2xen~su5Bt^?o{xPN*g5u|V7-xc>m|I(Q|FA?z2eu~2O-WTTkiR+XAumPs4!{;4G5?|ji!4x3;!QgyIl;BY99kuysPHyIu$tJok-f?$1O|x)$#_jMQR!1?Vw(M|&N)&d3tbF#& zicQ3=<&);Pi)&h_b=$|R_d34KzTZ@fLsOh8Qr$hI0(RK^w3#_%OdDINQsCfHzs(__ zMis)`J%cux8Gc-gqwP^khT5sWz;@QUq&dsmmr;O#v<%}o>o)F?f|(btb-}S#+=0h9 zgA6>MrMsfg3XMH?)Yv%e(wfZWS8>d3;qu$DL$@rGnfY2@?U+>AaojXSHbThgI9QMICz@c#f0vpdooo0Z(rVvfDmh5b%Y?s}bOuX(&N=@{Hrj~(sI z@@q4;g*V0HZytDwq`#5eW8SV&=xC$wERoa%q@7bb_bYw+0c%RqHp2&uZs(Bl5j(G# zIEFsjVR8N}5SGOgDc~Du9-pKN(^8}0a!|=|`lxq4=dsTb+OmqwXTCpqnSSFdPa($4 zZMLgr>N9egv&2H$c?>$9^q%#abUMk}^1MSQ^n(c@U*s`4so@ft13?Rte1Oj&S#B@D z%Cea^31FXie&xjicj^L&*ILHD2^m(j0JzRj+{WX*~m+zGlx7JUpiisyz%!#z< z6)2RREX`$QdI)@}ZLOv>g%di z34wY9!y-Jb{{W&*0>2FrE^k!v4UlM%qOCCpX^`&qv+_lGDvvAC@0pc1J3^<`xAc50; zM8V&X;~V;f?!9}EW?)A}x>rGbdtf@3TJqC@gB{uOwi2&X}A-^wW5vH2jB?YFGg4hF2 znt7cOFH#3^?lc~{8+EE9(HvA{Ed@-gx|^#;;i5owI-*KJPhB=xoCc_Hpp>S*qfL;C zi*TXT9-g`+R9=JpG!RejJe(unSz*;tiD6AUUX)m-o0p=WU`Fjy74jnO*Ll%Yj^Eq5 zppo*eMw?(#WfV0@NhPO};4~nET{y)Urxr#GxDe+jauO2dG}m-hJripkyUDO$tyg%4 zlC6?hlL>zZSaB|(?eJ1Yqvb5b*3N0}6`vl+eKp%89f)6`&K~wH=AVJs#H;I!&x@Jk zY{a-qT9OjnuiJ*yqNC8IL*uWU`MYVcd9BQ?LzmP%9mH8&N2e$0b7}c6gZ^*hdE7_J zna>?eb+^}y++{{)OKJ*fXlcR_w5c_yQfPJbONnK^vdNDnZS;1cVzJ~IvBSq(7x4l7<+G-M=)kWy@F?~GkxE(qb8v#qG zoowT;P^*WI+yj#N0h~$2tnsAVQD|8I0I4LdGlj@w-7T#ONdk)nBvAT_X!&<5g2LO> zuU?AJfqP|po4oYEqg&Lgt#RXY_*>*3AKESX8<`dMLDuJ-Ew@%wrsSjSt+pXS^JH{cdhX0pxd>ee1kmYF=GNQ^M0Qx8?hj@i)Lr zYE@P2x@wvO=ryFij;oGbuu)&XA~2S@M}>2oHE9czHWRMF6sb&x6YjP}eQGr$i{1S- z+fQP$oUY5(vTy@Ys9!E}n|>UZqDVn%L}jhH4V{Ws+KLD@_z|y5#Nb&`-EhXXp=gIb zC-c%bgic~U+oFSdQ`1f|Pb!G`(T{ll0Pd!x)ivp(XmjULr$Hq~a}eJ3JwL}%gVTPR z7(4){aMHm|n9!laO^*7C_Zp`5A;W}vS4ssoSQn{qiq!Avpw<)BCCo#6zwZA4j)R>< zk4+5Ql&;!3rk=!UE3eFHEqzlCYTw+arldxbu?`k~8hdhUYB1ZKH~bEcsh6=B_f1Cn z39n5VL%o0T(Iy)@a}nm96$jy>s*NKOlV+xxd(==~5#%}P z{u)Az<|7Dmpc--7l*^cuS#SV1-%ABTk%?+M<8P|EFcc>-I3d46H_{y#DaN=2?6$}t zjrvlWuDJ@@4M0*2vHj6Q@ZVH(wCb!TsxaIE zw-T+Cm2@LmE^BG)S(c8gxBF9)`fQU;bSs1W*NfpijW%~C@ZGojQ*shj$b4*qR_&JT z7E=171G?h4uc4JFb5lv-7i@vnn# zoYl%W_7!h;{{Vs)VJXxnu(+a_QI|16DMsp14V{Lm=DiU;weD7y#}6gNtd8gaT`=*( z%(U8WpY$&;yZdWBz176F<(;9l$8I{kfS1$OfQl1My>;De@-1vY9+X}n!WL^jTDMRd0nUUIkD5NH3%W5 z##2YV1L-^0OHte?QDhLHHle3Y88t~p8V9*F>G07b28iU`el_8RAx(ns@;C(uBTop! zlH<57H4Cr*0BtV|yri*ayCth&3Y3~*>4v|9$QVspCxEf zX#qc>LYtjocuqIgfXX`6cMfB5={>QL)Gvzs{@t5#)%dr_I77C0lE-`t$U+{sOl?H= zxWtwUn^I3uE8Xz}Tjo?~RuWq`5F83_BR03ZgrXEgC1+%~05$#<8j-ufHT3RpR4Yim zUtU0M4nCEirL==j@YAgxDMM8^;1~pJESUFu_(Mi9_e6CpcdCM*0j4H8Xq++WQoh`{ zZ-{bxKLl{ZD>bFdglDnKd(E{K3UvPfU4Z(H6I1-#ldSajO>3xEI?v$dnnLD;r+341 zC>_kp#^HVqt`S9sTu>opv0c;g{u+3;p)}{!Zl)Y64#psMfC#M=s%e)* z4^W;GmYRb^>ayk;HI46PB-cYtQAm!Z70}buzox71aWP2RZC2R+Pj?H29iUbd9p^jM zx%QJP)WZsSDfj0j5T(=ruJWp#!0HaN84e`gtt+Nty~dHW4+_NMJda zQB-S-OYdA)Rsd2#C+cj9niH&5<_=h0P`X|xl*4f!M6YwTTgwn`mK&{CV0o4+;M#3_T-Er_Tg^qgd{k_VgCTqu$2-H#-@XBU2bx? zmgVgP*6pN?TwepspT+OpYkd9I?>&4IzbsxUXN-&g03Tsf^4vQ8+{~Pfrq&^{6;gne zIGTIiX;V*4Q`$Ywq;t4#YSv_|*vijr!eV})h2|HPWwwqkRi@#Ubu6JX$q|wK@&A zHMhwut+c1~rRdcuS=(|oe_?9!{n5!*tS(OaF&@dFqsMgJ$N1jio@KGFcH25Hkr;8N zp|ugUGRviNVViP+o!u8^(QBCQ0;mm14p$7Z=|Mx z5IT{mY?X~@Rdda!x#AJYPmqMyv zfb-SjproBhMM5t6dvN|sw+V)qmvcBoVE(~!JD3dQ2y9~NQ9#WJd$e>GUK8Yj}Ps2~km9V`Z=5F7)XW7Lw7`JY;d2>Rvm_Ii1 z2W5&LON34LB?P8YlG3}=y$Mj?eXFcSTaDU^nBKac1DIrL)=t6|qiZFC>?FM@D2)+h zrCt5CO?Clb>nJTuMul{EubDXTMV_6V(wfFcV>?Y+;<`2M-K2(vqMs!WUpP+-dgT&R(U`*}PC}H#M>b|b2e^=T0MP4A zWyr3eZ4Rp?f9?J@X?L_zy_Y8b?HAm0jX2AX_|C=Hat=bAWLy;5`-k@E6j3!Eidh3) zGus)bx{=bmpj@}zJ1cuJ91(SL;?5@kg-58^tGQIySdgqevD(2^M84wL@ML0~cLn{V zch0&p52JKTVAJMntdAp~YXDsf+xso@xN1;+iT^%SWFn&2GT;kiyl6{~e?I<(=G81x0Z9kxI}J#V{{Yq?g$Cyu*j(n0ri+g>1r1Q2=%FCBN;hb4c_LKFC%5l_O>Q^OJ>!HHjQPAl4eQ0!zA{-OT>z0}(# z`fwK&OME?Bklw*IF@JHGm+QeKIuY$OvtRu~zWT0@R+6kP{{T<}vO{%b)m=%{7oAjq z<3vGY>d_@{Kri&2NRy%)|&SKrIAx-c%8jkk{<2dMfP3geFk2c`d^&&984?ua`xY z$(s6S(UV4)AA= zu0%7rZg^${i8K0yr{EN8 zsYE%as-lv~t{Q5rd>(G`OPnK!+=qs-ir(bxsm+gUcPLY3ribUgrpI(a)l<)pEj0p( zvy^yGh2+gWbK)*QVqzQwv21n3ujT+X2SLtbiiXR%+3^aZ=H5MWw;rwIb;e(p6|?^U z$MMIy*&TnEai_#2ij7qEWLQldqKDiPBdj$0DrLd?khGMM_=Bi#WeSqlF~dY{2DBsB zPqi@QEeRWF7S#qVkXDbWB!vT`FQH3y2-OZw=Oly^9zU}Jg&`ovm{ z+lAMFs%b=;e06h;Xa=QYIV`d`QK{vWIS{~ELK9kgQ{k(8i~piOcssOY39q>tgH#Vgus(n4Zec%>9@ z1x%Q~JmvNuX^WB*e2Cu4L%=k!rRL{TbHGgdWvzo ztDmXb4#J>1_YGgSGix~W3KfjF;yD{b{y#7Y`x?PD#zg!RFfvn4`0cj+cN_GsmHx8b z`n0GF%+Cp=ttwc-ys`2xg`SS3$2X`k&sCKrMIih|bzOCf_$w=1v~G1;>J@&t`|izU z$sr35Bf)maTu5zx?{bk-)Yh8L$%~sR$(q0;6wQX?GKC=u~W8353B4z~5~-5HDpEYlwxl*l_5+d-0qnGn;(3TBBMdrP=N!EjXkp zAa@?MI)p9DdaYF12z@$^s!8RpD9!lIn750oE;P#2p4Y%TAID6(*{#$l))_xh`%Z;B zc;7s|TrJ2;MK0K-+&cxVLUp!noSdrGD-`v6#p- z-3izO{{V)h+hV1V^i`MHDC5+X%8w!Z_Pk!)osaSSg|?Rpkh_sIr~Y2L=$IZNO$(PX zXgk#rFR-Ybahh4uJO?mlb2ZS>G~Y^RzH;^{{S5ko;LJI z7|EV>O-bCC@dQGPN_0{68jB_Jvxkok>5Xv9@7Dz$`ATUA`4h0zH{M816~(3+CO4vO zIJcj3y~h;cJxMxb)Rizbtk4CxLX9@2oS;edd=caoB{Zr0Gy-Nox2+e`Pc^hmZYaQT zc8aI*(I_|YES1!)TwiSu z8YVn&q>sa6tqrFhwYM9J&s=gl+mlAsdtuw zcS)k-3g&;IDy2?W{p#0NTyK%bdOWS53Y(JgnJh@$cU3^1)FleM$aM-IT0TD`twhb< z(mYmf+OfK!Sk6ZCC&&&*uvxKNwsQTm9I?Y{1RGQu#z~IKI}#S8ZcPTU_Iwi^Z2j$L znE0(#=QzksAEsSE6;^%cT~T$%RPxCsYg&qwN%bXFI?^^Kuuuvt%ja{?nAH^16*Wmf zEhN`(hMy_X(HB>yqPQi-wn1^C7SSnC^%c|4U?DvelupZi>NK#3mvnoHKTC3l>PbGQ zQ3O;fY*!nN3i9t|ToRX>KKw^_W0^Zqc0%ST8?avVYQ* zpZyh{iwE)361cGL3dYuPAlg;2@#jB9hU0SK4E@B(#B$oVVSU01BR=UN3IyyDqIUV} zvn^;y635rN*x+xA$+wqKdMGQJII4FaWmij5B4Tw5I3(}%R4)yD! zs!(zTw>uALAD))OLXFfYCqG%yywso;no0>y-658pN?%8NYS~>jsq4O^Tew$L#Ox$! zy}ZCMo=V#U$9Pq>`cUtt=Zu1#wvwWVjClp6R-cZW4k>Sq9eJ?oeZ&gwQZ*r?MHop! zlTO5qM^YAdAC7_u$`%z+1u3MEl0sFtNi0lZ(e#$o4|m{n9BL?>h2gHMdFKdAvTfIf zw?)mAsek#vq10xug=Z5-@H%7<*wC#wcXbHSN2w~-8p8MvhvzN z>YmjSnvX%QC(5{OyD!bl+fha7t_99+CdP|ax{ewtr<#7sT+5Q!@oq-D^A;I_U2{8o z7aM)1_i$1s%W2eiB$M1KqueTY_-daG?=`)wzK5h}JQV!@0J4`kHi^W6r1-8$atDvT zL3m-v_8hw8({b(?Mba(97vvj5{{X$W32>iFtt;JUwuxCCszSC>Nlx|GaKvWqa~9+` zN{;K!9G{Bc;4VXr4G&S%?^c75e&90kTO6$G#|#$T#aUM_smXlhF}Ub?v3GSuy5xFx zgdx`yrHa#Y(^!~&ymPKU`HtMd(RAVOY|Y1r>>24v(Z-*e=LFtB%5p4$%(p1LO2{sg z7w)}rlKYODsa@l@xR8-uz)c6kT`DYPtZ`CRp0VDOC0Uv zcN6n%xvzHr0EnPt*|7t;%EvvdDP(l(>+#nz;O;ogthSEPq|`4(dyHG1<8Wjdtc=mi zU$&Prr;mBU4UXX}*tKOSF&;rx0d$(Af%xmPTr+Udc~*8WsQY}BNbRD-hNdy$jn1w@?08>f{w=r+-Ql5uI z9x97sHVr^O9Zd>32+C9l8}Fh_5QYv@(?F)n5yUHhhJuXdqwT1F9=dVbg%#l`G`f1! zX>E846pcnu;Tz})3SrDcCDXS50G^ay3T&|`H1E$q8Yq%Rm05RjzW%33W~vL=mm;~i z-*KSssgm7OvIL*g^>ow=mg=l+C7@5cT%eMQ@2aWTuWcPgAi`Av#GYvcCp2g&Xb ze%@Qjc?GMxGVSd$pr%-Drplxx_4EiW1q1l&GkrUr@P0ZjgN}``+C9D?)pobd54UfH zo>X4)EdKz;OolfR@|H_2*RsqtPAOK27Z(;w>qB8cHQu_CTcA-jBD(4pS;<=ieMacK zn{mfrlI8BTkOx<#R{*@R`&RO2jo7*5Ozz6cvAHr63O(){!Nxyw<$Ed;0)RBzZ6faS z88vFu;$3?mmPN^njwZ}^9ch;cdAjx|a0nWOMWYu4qy@oxIHRI~b^Npt-KtZL6Z!~NAYKa{?kxl8Sz$K%V z6%-WIw3KFA5|&WnMG8pMF7qOf$`QEEdyIfK9=awrsN7LWiR~e2*r!Wiq9nT9bk2g6 z3j37w=~`*U6pd45Y$=!4+Jz~%_~^^3jVVM(G^x=dDZ>fw#}V7>Kt`6{sx=DwCHo%d zQZbvCkQl&hn0NS=YlI=Xf@UQzb!d^2z+ava=6TM14Z=J#djGO8_Zc3 zi4IQ+AuTidSW1*AD1o-x!{5RTPH9qjqF8@66va~@LGfsD;S zoo?;@Bq6CC{@C03DXysE$#n*+mN>p09oaCG%dm`vF6YK}l*}fpb%s5(6Y=aLsnbT- zj)`8Va6-xEdw?CbJ1Z8Dpha3}1#T(UnmC$KF`%hy#jZHc{FLVWLyyq=iHCELmoKvx zPiqicJCu;( z+>c!~1}L2k6@~XGY;FWs@JUSPS+a6SIlMn2BU>yv3LJ6fudrgg%)~g5u-~*>BXhMy zDXDJ9#zx(}JQjNUFLmnrg}?*Sp)%xU{G#Su{z;J7_=`Wb+(G`)i$4uk)fPf~R8+P~ zfLR?0AC9DqYws4ak_d#6))7 zDhd6p#3YX7nhICrT~3ZW=AF^WQSni)X2rY7EM9N^l8?mU{t?avf;FEwzVS`U_SjqO zW)?RQ4T2N;S1Ji86<0z}O(EQmB>}71xO@|_@T@J{K#k@yd{Gs6kDJ`CH@F;mk=VH{ zn-T)1+-&za%K!?I{WpqNQ%!*ypAS5WgVbqirnvT&YAm0)P#|%Sb2q${{XvHn7{VpDHgj86EbEomd92r+U0)2vIILC+c9ag<&)e>t@kHbUu*r2B}!6gpg{`)hvlox54exV zO}0j^bIrYvhmhF^!umKBbUc3IyCy5cvH060$dcqJttAh_ZAl3a6qT1!RFOayp5fQ{ zYXzOm%PsMhl>qczyN2#K}3*S2yIE9lYUvwV{mi^#?d4Ba8=;8RnMrc2<+*X8CH z$b2b|-2<}jcxA>Yc6)1s^mjJ<^s1Jk0PQBDeK~x=l$Ztzl~aXzz(D8CQ5eSx5-3YG;ren1UeTJcL83y>%3do0FZo#rz3 zTdfIb2d}|Gxc>knb8U~8@GG-Z+sNcy;vp)w&AWtJ=PA%qrX{viOAZ98yU=$HXtFj| zFSKykKo&0xo6N_RVc)o&2q#6>{C7UH<2+#_BF#G*lGBS1OSP>}BsjH(`w~g1LVwgY zG}i>U#~EhW#q1j%LLs#EC845;QRb%I0>9qKDg? z_qTIG=hq(o;G=nAI;krHsj#zq3~?gj@Nu;^g_fSF9fX$9ejgF445lL?W@z^4 z7SE3I{7zAu;X0HKSzzV%OVha5Cx#MSQlde1l=q>PZQ8P`)k8wo`pxug?M>!!nO1`9 zN6>gEeXdFJBEZD=b87?AEp~?!`>XA}1xYQa(NqofC)eSti7~jjG>QIYkcX0T8+~pf z+|k@CGsl_!2asETaV_Wl#SpuMSEMY&ZaX#pfCjq9tsT-==+nEiE zG^NqTSZq~FNmpHlw6-}rY1`@l0C8En>^;;DGM~@_`3UnTYsYwejzG>Vn9~iFCgl1PBeZ^&QlM6L`D%wFa4qfKBedEox(6M@yCHnY z8Y{Sfx^v~;V%4i3H9D3 zRd8wB?AYl;$xN3UN|KeJcK~Znfa|VgF)yAl7Zl^UX2UTjx?p|hyrz-n2i$AO4qITU zIMhX3ahx5Ml3Z%hWsBX~wEqAqzjG!2yP$VbH(pLM}L z38>=J*WuGufC8t{6YHlEkS>xEck84-qEJADmliysyA*8rTN(VqbT8lH&+h~e8&}oM{sn)uv9THL=Ii+;_P;wdWnvQy>0`uXi35if@ zS8t_t88*ENO$Pdsl)$p1iEn?RbrxiY?` zG=Kt{eM1DTiYZDO*G8RVs*Xj3rxaG{EjF4}M{Tsu@^n%}Knj#2*9h>F?Zad#!?ASy zy>uI9d{bs%5-am&NdEwc@uvR(O-(g4GKvhcRVJ!EYN~xh)M>_mQ#+Bn9fDQ<{baJl zbTKONHXJz5sP?4}Op=JmQ6r!QV3Vfrl_j((w%ldc9ZK6#RXfr}Jw-Zm99JO4<>of;j*jtLDXlX& zsKndo-+|NcHC68q`;Ol4P}#8a?;6`V64*jg{NTt$wI`)$LErP#yRU7`ZruaRO0~52 zSjYn|*dE^yo$v8ygI+^0xtKOqo+ZoXevEeDl}19HQRtGoB<^(pWHR{oQc=C0D#YXX z{#aHAdT%|tD!s~{2y(szmoAo8+sf=zDrV^bmy%KG{{Rl7TKr!uwVy-fa>r$OP2I4o zeC<`X+mmvC8C=V4KR(SNg?((f?w}6SPKu!P^wx?n*Xq+yu`^k1G;rEgu)sXq%AlbZ z?7BK{P|Xs*Z~|yHHSVsRFLQJXrfWKOs+?1P@vE7e?!DSrwB2QjA@d+*F5NGsPDm$59TX zgH5*5ndMHj?&z@>RYgrx)PEoF(fag4OA!93SF#y0SS=}CdVU&Qw@xfg!--=ZRD_gw zElNFAzN+@rt8MfYSQ{y=2VE?(i9Vtex96pLNj{7=dY1h#=GqHFQcsZR+K z#j^EzvbY?z+~0tpY70&&JN^SwA-Z!8gs5}a!#MM_6)ai#<1@E1Bivj?@;xY|zCNmc zBUg~}7suCClD^=!uQhL!ry_Yb2FX9AeX)OOkh%rQZ*=snFghm_g*R91&U@Ui-^sIPR4G?j{O#5|too-}obUbwq@ipd?l z5@>!pg(bTlJQS8+Ucu0A z9|d72^AC^yK{plKwcb1L?7wZ*0YlfJI%q{KeL5h_)Kj{W0t%6Ft{>t_mk3u#YKJ4) z?J>$q0os5az8ZsfJcqK<*}-n=YM=b8#w_iMWHy{=qhg-bZ%S--KANj-DfxpN6bkQNgs^Z+arBbW>B}KWufUfk{=~r5fQr$OIth1AIHA*;(X;tnZ zBqWsp71L154HMF8uS`2;M%AJ`ixttttqC8~3p5`MCCwewh~+d%V?N~D8j{?!s$)R? zK!QO7{{S5{<}{Tplcz1ao?WU@dB@7Tc5s2CeG|e(UG2Pb{Gf`apqbwoy2N5l1C{~!x%cNCC6Mh4&wVlBvib{)p0aX>*_|UFK(XIbrq8i zs{UYDE*ph3+m{%usqm59RspB@YMbe$lvPVJM;KFLd6LOsXr=)`2owPT4uYFgPsM|E zbyV_ZcJxe}*OAwWUgI|BOkhXrC;UYscuYtu_GbRb>Ms&;^~pjqqbll(NxNhK&6AVYN<1E;8*Y}6~yX++a;`Dr_KsyEA3ue1lHE2V_X4MN>16&{u9 zbP|xu4WtoYsCwxnRUyRD7gK|vJDnnisDVM1n$x@uIFe0JO7A45z18_>ic?xr{gV&L zayNrEdpq*fcJB?Zw0?A^o0?`YYIRc}vytaOtWFu?PHTEwPu`tHrYfjZ6c2qrJy6`| z15TQieQU>_?M{luTKs(C>=mTSzsqiG?1A2=NTZ)9%&*-mhu<%8zmbcDr;_MZr zGmk@I*V+U&8%lbOx{t$An`1P`RXzDAA|6nyc3tG|bCW4?EHT#whb!1nm3MEqrmP~v zMINqI^51!Ff+Ul9>}j=xRyb=ZdIPUCj=pzVhAyr&NeBS2qT(Q$|1IHJSoZ zKQFq{o4V3S1LM@4Q(fmJc8m)4*N?#$nnI*OkZ|uCGKTE7#%9iFQ3~w~B>w>BHQ!Mi zjJmr0ZlF`8&DvTI@rsol=A7fm_lSiS3nSLcX%tM9HnG}|LJw^zt2-<$q;I2~2~_vo zp894TtspAJdAej5XX|meJ1x?Fq^W9AKOs}CNrpJ0C8Jf0hUQ6S{{SH>^5z^%z|pt^ z55Cx>w$^thI#+g)?-KH!BXFj{*aPy{D8*;;Zoe_Tr()~5Zaaw>_P#PV(^W>9hc?eF zNQFI7-;SbNc?OFc*RHyY;1UWuqU9{SFuTZTiR3r@pCECwkYA-x~Ii)^#7bd%mDwIlTUHWbC)uV=qz2PRm4ccy<{#s&2k`sQYN#--) z6YVI0)X?d#Mag1T9_W(L;3TO9KOHt2X{b*&MxKQiSvvq}@zNZf9S{bi=cGBPG$p$& zis6t;{hhhAt>lj7_b4O;k6N7*tJxw+!%}qUYlSWNYg$}#I?5bU4O84Y(CIP)r9~oi z<2R%r{{U&ZpK=7F+rIsZ>D)itsE>?=b!~H93M-IUp0dtUDjwrbeM~#(Qmw|BYr!8B z&E=7d*Re>*NkW3ap-HWO4J?Z2qQpxbRV*;KGJI{hOgvGL;*?u1q|wJCr1t^{^+_6G zo>R2+D?=Q++qu7}D}(*P9zk%-TjPmKxwa0E+g{U3t;Q0X1)5NuwL;SCYnl#)X!3T$ zC1dJ;b5%J`9TnuwYEdn?grC~1dXdwvwfpyKxo~FJZ7L{~%aGr3g$fiJ5JsFXswcNp zSU9hal95HaTv1R5q0vzh2x34L_-Lq#h@t9qq^0J93p5Z|I_M(_*=>0jXo|H;SM?FR zHxI^@rFAA3hl&*zU|8+JG~Q*%k{o5gQnV5XKaQZjp~}gw1*rTtlTzA_$5PuMQ5?mR zW0ZYBRD5)YwuzQJ)ezDYgfm@Lq9}?sl^TOx#*H?5r}0s`Vk$xUp0zj>f$P|zzvZZ; zYNy93b5k2dxTT}^=Yuj3d%Jt86n;lb4=E)e+!+aVVzmID;B_UYg)z3O!T7D7zlUMcYxuZV8iex)ym9VB(ZkK zuJ~dmM|t+zOz3J$sd;Ncg+7+HKAQ2v@)KjP9Z(3barXGHe%f2JDT)K8i-8#p(kG0* zU*~=}=9yx%z7991ah&Og-BhTbv6_&E6C zwN%_FSK+NZ_D3OXT1^cou+}+G^_OBsiu@O0Y#f>PrShr*uRO}^e;eiA?W9)Mvd&fCOF7~hz`0LwU7p1Iue-g}CmRx}xg}Qb%T=3w= zF!?#RH+a{KUVu;5iG1sD5-cpJCBT0$gvmpxlG5fzMehV}|$&J+!K!_FnoK zjd`7&Rjx4;F zZ;S5SaJLJ0CsID64eC^%l1&b`oC7IG7|Q3o+a6oCVe%`4fn;p1y6ihj!WN+tx_aoM zG&-afqiwrWx!X^NRY!bi;ulT2*P+tdqq(TkTq@X|hNRFY3n-1HUEeBc#=eRoWyCjbV-NCl^J%UN(u~-qwP2dp+AP4Yk*MXVlb2Gq=eIgG}^ugP6DI6 zC1zLLDbuF=j_P59%PX7Rwt7K(W z<-6!p=G`Qh8ltwNNN+fB75Z-c2gWkaFF!&g}ep%QUf(+}j=EGTtP% zT~APx)4Qg;)0&h=cvx4w_`SK87THH&s7L)jE$2gVD2IhlVKl8j9TQCgmhP!m6dZ=$ zwDQp^ zW6BEj1@{jYTy{e)k9$oKJhz590};4n_b3)Cqj#%2?-jD^Zo+xBA!qu4zu0MCU3)gt z_RiwjfR3Sjak|9jY%k<l84+Gm-dYr-kDyo6LWm->TY5vvDBKczMMuX)O%Pi%ZIX@su%V(sYN+x- zNiCC4kkOYj(M^C8wX~Yty zJvc{0k={w|1e6j#+oK7G^*~*iZcE`!rQx<=`*P)6YEoWC2g{IKY~VZ^h-;}9eo(R|7By@$hZ zD;~N}v*i!?yUOo(BH@c&+g-yResXFLwIv;kXe~pcsrYM1$KBbwpl)i5FZ%oNTH~?n zlzgw_S0eGX!RXQ>!pUukB*e%qav7NsKzh^TtHC=;rt}=ntLuyG$Kj-F7X?}QW&jyL=D+90FmjBqmELki zw)XVKnSLd;h5K!`*;;`>S8Y$s>pPgfF`5OHmJg_X2&i4(Ym8WhJ!bu7hm9pFLJ*l3 z$jc}jcb?YNY5?48KJ259iha09o74b~NaNY_X3jQTzZ|{Uqb7nJrLgnKRV&yJr(UG# zU+w^H914i;Fz1@F?V@XBoIgJ!#o}46&~e*svIFJ⁡}AQ_d@Or|BO}ej1yP<9k6R>5kpb1B;BS)Lae}c^{{RFd>#pXWoiz9Qyqt&5>IB8M za}6AOp=l&0P1Bd~3COrPbyO@<`+#XtCE?7!7E^~|*y7wfR+@kPJdtUmiSbQ4l*1=t z!SGJ2uaG!(woSf1E6BMNg+aCvDjMaLMTy*yqNh^g30lFgUcQG>p}=+DKve7AU>X37 zIE0svc@vD@t|`Xj@?F)775X!;OqAW7nF~ai=_IXbEU8HfN`I6eo|bTo4XHKz)M)uz zq&sOqehCWo$@z7PG>O=*ZG2U)E@Z~H&6{VKnG)l&LS5`hYy>hYN>$RmG~{^8IS!yW zqrA;u+c!$IAK;pKM>{L^ne%OFh0E!l=imO!}nJgfPuEw&a2(|E^y-hPjV*}^FCL7nRkhw z+B3K%+tdk<7oz)b6qeNq^b!)|WBF6CA3b(V=eKsZwrvb<*R^?Tj(gjc#brpD+v1bQ zvY)QlE=!hVxKEN8wQ*%pVJVKjt;vPFl{_T)hU_}f&Ip6|aOom(w_Yijg1JLbFcAOs+HujE^{{YP= zYW7|@<^JWH{JSmigvSW#0PFD7D=~1zIPVIztDHD*4TZ>D*_hgqCy?+fjMXM>vH2a1 z{`tFU1f?hltM{*K;4J@`?Gx74iOE-RS9vu#}7>W7W;Te}I1GH;}B z{FR8o{8{1NT1Wj)#u>!5N4Pe%zLKO!c``dn%W7$Ts&Cu*r*Z47o!>mjSh=m0lmkVz z#`_h3%LmPF`nlTmOk5KN=3YOyW~|SC4hq$Z;_G(2{3T9HD+Gjs&PxhO)$7$l4q;{@{bLch6 zbC%7PPa_wjIIZ-tLH=1Q^Hb(K%Jx;qnPFjQ$=zkT-|i6FOR*!Uj?$M`+7s7(^^%_r zBMnF(t^JQ6ide`55E2FBkNC$fjN+eeGw!+dNPgKWsvB%iV!o9RT~D^i+{dR?Q+b5P z-2+-x9pjESv~i1zlViS07THTG5FLEF(?FV&^&gI)(PWrW3B*tcsO2tO4r zPQKjO0HHu$zkLg85SZM9+uqm}HB|*hsrnsOhF5g(LOVT&A46`t{kBSg0UxDCpBm`i z-N>X329-+PSssvWQ3K-c|C)){6IRO z$WO8|v=}^jy1M}M3+L#U&%Eo)EL9wRo|NgXS?l29jMkI{m?((X|uKe=ifTQv=|wvcJ)H`JTp1p=+HX{jlk@9O-u04q?7 z6|3W>WK4ttmEEP1r2vfzHj*j@emYZ{kV;H-WCQD6B|jW}(c1-GfX3Jo;MCpM@h8va@& zhZ>-QhNKs#11P*vH3=W7MuSBgOpc;`!~s&eW#G?`xRs(E=ghApEyQp)3at{cj7`+1 zzck5Pa2Sx9O2{f@w?Hd2p-`tlYa;$u+p?WWQS8fZWZK-c!m2l8W|^PeMm zGco1fMY#Uv>yk+;hih+Ww%ARiH5v?LkJ8gfNCV<(eQBzD7^$i1wA0b7+Bzzrr5Bu1 z$ft4X@YSBus*817?eNhg(I~-!R7{nMGTnQL_53u$>Q0JOLV~Cpjl{_zmXYnSflZF* zPsJ4zGTf4ogGRj$q^NF^sK92l+x&E36w!HFdsN%kPBn?L;=My<3-3WGP@zJSN>r63 z_XPr-!30zh(^57E3Lc0ya??@ObYm?0qIr?Yyc-|K4p!tC+=<1I;;{}KOO3siTw7)> zJ+Jws6qOXvZc|^Ls4aLslvf_B1szpqeaY`8xAa`>aN!^avvOGmk5}LmwYOl70&7kVCPme6~}xe z09G16Bz372)-E1Bb#6Dn^5NjN)?VY-B0HxCNss(|z6`|nDP6_AA$cA$Yu{06CRt5vbYNCb5$DgHOjBgAgt^@c%+@Ty-8 zsYxTRQKX1ZRQ~|`m_emeNpPe`QEldnB|plbNBQW>2<)de@vj;ch_BOMT{E*`L$(F?*Uq`9?Dwbfoz)|IB3+Ebkn1ds{qO%)OPi#qAIvZ=u3nsaeS z>Mc<8i0cbs^|Yb<=}}4l01Zj?oYA2{&_?$8g;TcM$T>y6iT>cJOzaCTeL|m}q(_*P z8XZEVxZ^gjItmo&9D!xJb|Uv@4XpmD4Rx=?>cyEz-JuGOelx2aigp|A^EzelLne+2OfcNSKgRlxie`CDS zn{D#u{{YBW^f4+ZwZ%X1+CqwtY50-`or20X1r}VKt~#p~8zFVp8F$?!E+lWc>!{N< zjz<&8)fhMS8)<5FbW#B{_|ST3LW)bxHC*ZEYm#nRF?G7A3V>=&1#79Xf(TX5lTx9k zYebrKuB<|dxg1CPf7ztS>Xg)-QKSL}`eg(rq#{}<3RaY+f(R`H5DsCnQ!a6%K(DR%Z$ojUVN(N3@lNPGE|?;TxWx_rmS}68*za|1cZ`lromQ43LhYTFUBLiV#=Gc0`K?n+n|riiR*>LW&17QBkogdXP+{ZN-`az-Z>WHN6#xxeTk*DyU#hF0 zmO0u-qGV-kXZLJ5d7Z}a;kLO@mO_>IdTMJvovzvjssy6x?A-HEtF(1pE;3tE61QDS zfJJIZ1AX-{&@orru$R=Xgv9b8m>h8r7@LPjUCEf<%Hx+%K?*haRO#2-0CZK)x;@s-Tck;F23b>!R^6c_)aq@vJID4oGPJ((x_KiYw5lY(}AoA|iw<0_&FQ$01k8kuL43MS@sbhUO~H;QRs9ck!mk zD;d=}2RN41+in}Dij3~oOO=`R$DJ8WD0J=}eKyq%-HfoGIHKQUvanq=M0L80{9P#E!5IrK_V;TUVJB1gmDniuS>XL&(pN6Kw zsia{VCpIN{He1Ab3O&rI_DFBhO|NAG`D#7G4^?4|l!wr(AH+Ez;|^M8D~|VKd4fZ3 zv<+KZGIkXQ)DzcLSqQ_J96E)n!cFPST6z?_%=nv_o>w)Pg0|@j_aG29EiS6q`qH|! zu)YqA4(lb!vF00!j)hj7pB{bImvv#jYBYIPM>SYS0l+Y;C{52sovq2Kg1Quu@ zv{Jf{@zEs!wW;{%B~f{zK^|pFZ9zyhDWM1Fpa~j;9MDA*o@*`NZT|qfYyohHpZSFP ztt0t{q=tzb@m2A>#`+$Kk_ww$4#hn|)LK(QtcV0vFGhJUquWs()Yxfl)j*N7D2#?w z+R3Kc6i-I#gj+!^8AAK2DN1Q2B!v3G-VRHZZ*-4t;Mr`X{C6Wn=BakwQ(jzmA&MF4P;SpZb0!r49a*{(s4 z#Uf>zOHw1NySBdC!6KvmLu0J|Zyqk~9U1Nw{{RurNsY)dcJ*lr(fQMZ+pco)ZhOR- zO0k|xhQygqHcFDze)c5xxZ>2bL?Vw z8r>!L23!lbu%b3X&33QPU1st+fI{-U)Mc+Bcf%>gZJdLIWEh>*l*xg8i0cnFKqKox z3F;FTHdMpEzo^l5E1 zOgc1aD>Vw!f%S4_%|-(L8h(kA)L<^O73-sI6D6p0xl(FHG-;RB-Hd=>5>#@)UGUlTRSE16Xnjr`m@b%LTDH~CgxlX-DO+8e}VltNn z0j`z2rV9~(;Gp?vsF^KB66Jpnj-)a5O%@>#=|35!1<(xWeQ-)&0@43VRVUiAi) z(v)X48%v)Z>(fU?9I+lk{#s^=E1Hiqx_~Q9Jkp~*iC#OqBvdZ!dOcTS2! zqEkkx*Kc_DC+DhX^;Z_^SjX+srWTajN?T;^pf;yXP~ObelU1(A+C9|Us)xLg=bNV& zGk4-B@Z$)#xUEZ=`-MpoS$Q-Gb+AHK5JuG-YtEdz8(XhXcV6fA^CS*5M0^)0WH!6J z$0W5amV(NO>`+OnjdmWNhN_~FwP+s@aZj8-6Nh=Y#DjavV%ne*J^8NHK?wCUPp+Zl zGEy~%V`^551BSPen$*Ef+it!gl$0ZRnhUPq-gL zO{kVifg`7;pX5Y3Ef(L}t4Td`zpCK9<+MY&7FH;0`o9ydXw~TFXc0GJM{z-Yl~1ma z^QvgLBxwnpdZ3Bm6Y-Q&z3ru1HYtaS!?gtu@<0A;zvozkVD8-+-R)=5aL@ z(Y4e@P+z!=p5% z(HUY;K|j{-9)h3v=+t$vxyRh9A{-~Fr8HDQ2-V%NJ5vQcho;-_J>IM-??UNHvTgWP*b{iVQ_(A0nNL8`0C{XI&l&SD)rek;1U#jf=d^jJhG z2$ZlCgnMi4Q0v`M*H(5J2zGyq%f%3O8Y*B1SbNT<|; z9V(3KspnKkKAP8KZ(Xnhs=UfrG&qo`P3s}PTco_ruO|NC0PMtw@3>fTBVa;J zDXGr4jmDrVsO}}{RL%bHe7i_YF(q;(%2HcoDMW(fitI?==0>DJ4IM=)dR z31_%p6Y#$q@ybR(^~WQ_Kq$!`{{Y1;Vi(RMyS)@c!!LKKatSb@&bTR* z$X80Nqz!@4xv;)&o51-Z#bz;+9mS46I;jfJl3VRJW~`WZ7u%-EwY*)(ACRXmEwF`b zQAMhSNhXyVi6$ydwU$+-wa;5M+il@ajGp(D*e!~^s|&}}?b+VTT%}t?SS+(zDJpr; zk={NO{Phyg8p$0-ypo>EWuI)#(nbPd*!km^Z!+8BTqBC{-~FMmU6#9qMq|ioO}w~V z3hr~g3Qxy<{WWI(J!@*;R(F~n)l_hE9EJR!Piq*fehDuwndL<3H`x=gR(&pilnM^X`GH~nGGi8O zirths4pjG>%!P^?7S`ZW(vY6sy*1T<`dd#?ht2}? z-J{!klQ#Da{{Rb(0UtHdyyF+g{@(m`Ie6|_B340ox2_`Zjk6W;Ga$Lp#0vK6N?QpV zlSJ)LU0|`1Pnx_1;Q38gVdU?$;&{T?Tn_yQ&2#&VaxWKoe=NZZE9u6M2&B_~WEj#`Mmxtr>h9+JD-qT z=Dc~)dh%lfM-6hD?BTZ?{{XXe#dFAseycSqOA{MXlib?I#MXrEO~$$fuMM}v%k_{r z5C=uZFTKXT-6w+%4gUj~p{Ii8+6hxhZ!%hdD>=?6Iz|qSYVwue3^o z5u8X-obG0A0%n;7y1*tp-bJbR67d*A+N4JvgdS3AEUq5z6qUhWGVR;L7ig}R-q6EquX9( zKGVpV91g^)zKWr*FlXpNHOahGtP6>E&ybdTyHjFnftAX1d6J$1tF@Ru-Lma(T`Ucck~vl(@c4|{=? z!%^KwJi$9E&iMNhykMsvg<`rrE&Mjwd790ZT*XETLfw5WSGfSzfb~5!eZ-i^u7Ax} z1&{Y3(#v8UY(q&J1ES+?_c-$(jHb)Zu7=@*@oqsWR@?6)+GaHVE!f)o7u@@6SWrkL zQkD3fcL?X>SN{OvV63CHD7;$xUvP1ix_gT)iTyx=KdX0$h7)-xPqdXg-9zs8A5D(>^sal(LWRgJdps530dmWYHl3TadXbvHH zH#f(<$F;k0wR7IaIQQy4^=CNW?t9AEhB1e=$T)KuJM18&V9O4 zBdQfGsFS${y5SdGa^lVIYllVa+&?n3$uW{f@6;583?X(bzucCsA zf&Dw}sE-clI;&>vEva82A9yZphseG`TezPVl%-39kZs9tZt;5E~wb=(L8P9D-4yzZba5p+Lv!yicE{_uVQIJ zE2moRt*Wb#dXj*OVG+{onU`nN^g&9F{{R!yNwQ^JT%~2+8qmSjBhHXy%6-yn-m*o1 z9YMNvT~wQPZD2$ecLPL?07i+;s(pIO3Hp>j0iXgKs)LZ`<8GQ&HzLcc!F-mY`1i4HJ{h2nQF~l7EJxH=$3kRIa!Ew)ppfbG&mXw&xhUY`2tVU0O^t zaBJL;ZL>Cjzjcsy?^1#8I<<7s!)d$wipk}Dc`t-u(SQ%V9Q%TK5g$CuY6bh!`S6RppsCPK6<|+XMbOWm8jrM4D(HSc4ZH~V+YRV?j zDtQu85-JC#tO=k|gThjQZ+dgVmgp1JYWO5|uBv8~*F_>IYpFtACM6(2h-k}OEnn(C z9Zk1qifU;oqC;)98k&9wQq_Bru@qCdorn18KvgpB5qAdXw9}a-1yjdcAH z6I3uqN)N+QbWKqiLteD6!%i^KHu9DtdyOBVMI`kg>Pud$<^ejDkX~d$ObIt9--duf zVz}zok@y7@O)55qc2H%9cBH9ef;n#G!#Rv>n%NjgNgzv%cJ4=TCvl|YtQ&C^6LXp= zpUqBpN08S1X5|VgWmZ1m++#wdrDBTRKRrOT#ClDlU6a6;r$rcWHWN8;1;x42ZPQv; zdk@EX=2o9jYTAbSnKmlkNa*2Gsj|F=cqZKoh^K&d`0EjU?es45Ms}68;~wx{k{K`$2@TFn@SmRw8Om%gaaK_*(Qct3OnanI zp7!fiQ87GcKO(xXmlz&Q!ci?)gYvgh$B$F60Tb_Tr@$K_oJH;sLr7& z&rYL7>RtU_zb!bBV?mK!zB(5HEvq#hIJNBqpwn*}Ahq_t-qVe3Q8GjoDe0sq+V$Z` zJCUIG>!LQeBIQK~y){aQsHo93&IdxA*d5h4ZTWTn)z~9XS#3QUQ7TXP>U23N8dY@o zcxCDn&Nm}MmXB)tX+z}D7tjJ#sjbVk-9n(7j7ZZ~IXEE7#^;}U(=5k#dPQ@6{u-MU z&2aLniLCA;G>XD6N?wD7_RF`iRjrv`%eP^D$C{q!0(kR&c zD{`wsXnjZ^eDuVT)G1bPqe^bxa*MpT)gBU-wX5_YHSX(5ZK$u2tZt=Fj@|Md0%TnH z-sgE~$@5!M9w{XuK-c5^+G;$LD@5ejX&Ne21$MT*Xqvc=rj$D!Td~z@qM4)`B8~d# zNrYqE`DrdXAh5W71vKXJiEG}Yu{F_$lqiF${{YecIzLSs)iK?1f73yAYUmOPKjEj^ zK6VzF-(CP)p)$fk(R>{QS+ z{yL?fItGDgVdI34GM{XGVZ~TN5agy?ktMT2wGg_I`0BdnBQ%5nu5Ix!L^VgRT+fK& z(bUbgrdvxxWT7IpABLJ1vOMaZee_HkD*cgp50GBnTss?2JWWDSgsXqZYE)SE1LjaA z%DwtEHz47h$;e8pkzS1!MQGycnvFRn#1VKTGScR46uXUhf4U~-H?k42Ty(03r>}OR zNu50y)opLbqh_)>N~a&-zAK7XAV+dTBDEzeaG&wh41(rvx+Tv&rM{z86J>r)+E{JK zxP6qRw2suvT0&9)sHjah)rGemAKYrHE_rAojBOQloyb{lAm5`}9=ffgIeo3RliGk3 zdVMt2^mg$?-itpa)b|Ii5c#hZOvRx!a+5FjD_Nq@R6kKWb60XTI zTHEiVXh*KqrF7)eXo}hp3YQh1NKPWw6Fb{_)WHN%KUpKz(EbxT-R*IM-0YE8X~3Z~;K3JckN!}Y4P z>8nOrlBwT*0&-a~JH6@Q9t0<58g7&zsMGNpoo9f!mEKUZ`CeaXYTrAkpGe}orG$lx z1FpEA(uJpQ;10Af@k@X|O;$Q@a;#i`SOIwKeB&K&xfVkhx43Xe=}(`Hl}HXVikNr)2A2USgOrKh1+&z6l39YVUdLfkI1H6r3FjlmV9zuM%4 z!>Vl59sP96N|g3hEi5KeK}r2o-|^G!bwYwh*CdrBp+zUC(g;t3P_@!hlEtE8pv_`K zYTbo}0zF7I(NQUqP^q_-c<%RX$8g?MZV`6~l)_61OeLwacrK)`fbG z-K(o>Id{Ygs+t|L7I-Ne?eE535_5kg<`_4eenH)QZo<>>z`+0oR~y?gY?+Iu+#1<}vV2=-f4FA$^r` zHfIMi!)$lkd+Cte<{MI$l>_y53hE8SMW(AZ%?%eO{^z_XVcjhJ&t`{M(}E!7D?r&R z1p7`ZpAu=V#lf=Pq+1EpE>+7h#v6^}qn92-gc3i~t-j+`o-C>`hrMa}>Bmh})1ya_ z=Spqx)TYQ3M=={ihp(^4PX}&HIe`it`E>c{{Y2S~8}~+}HR?XZI$W(Q`5iCQO%@{! zxCJY?^wVI`GWH`Vc%RGDPDdqEE{z#N!96s$K8cq#3GV!~H-wK(7!Le(CvZ%d(X_kq z*y)uW%t8l8rlh16Ap?~3)MAiWjfY3B+Lc8d#)eD8dY$z4^xLCGQt0SL{{S5UDrL+= zAcI{KQ%(r#90H#m7zIaj5|eIiT}x_$_9H>wnpG18h}vH1zWPLgFzC_tUEinv>S%&t z@g+ta+*f@yMIG>`6gQ}`y++jgZ!DxIy`PK#jX^gRGL$8mD0Zfu6@ce zL_=CHKzm=y86>7Uu2D6IWH6Rf?w|l@R8}cXa*-ozLcIjrcOIS6<+~NVTpkgfyhOJ* zuyOZZ2}t~O&CU0u-5XM@F_@DxsORl!wH#ND@J=r&iMf_+_GNdBK~6JnY_`hO2FX*( zBmhrZYX_CN$lO#gZEEWLdd~-FH#yth7tybXd3;MRj2w}fw*{*Mcj(SqY!;?PeWn~9 z+LhcXQU_D<)DLPbStlT)%}Z2I+^%u(aH8nN1GH5l={0VoFxB1kcBi$%T~aH(D^Pc? zz3~Gh2eSCU+o?}NRa)|b>wjmAU}KkSyZ0=2`;uhBgyV{FwjT|sRXyJkU19m7TeC&z zpJcZ_LgyV6fA44@jj+i5U36$7&XQcGqIzgIK%*fUM#(=Nsq3Rb z4Etieck~(vC5uykQg+cPOQ?Tr4DMD9<#sh8g?1+GX}UssfPzxAw&>sG*`_A56E=lV z^1Qq3?979*`lQBr%G&arP4-(WVWh2YPLxuR!!4w(WUX}A0o3b0cWkA-2DHQdr z4t>YIo)*a7%hA5qUpNBUI=f%^`*{2nLi4K0{{XXmruBy9_>q@uL0Y1zW4$XtQAkod zhfTdSo-=$Ui>{$s@{4!G@<7n8C*+C2S25JtBgnP)5>m-nQU|WU>t_?FD6v7tsq5n& z7}p?q1;u!aE&l0?mFg*y$=E5V%l*p1`4HU>r?h~mc^$*O0vp|ZvE#Vh<78pf+Mr_g zw$e)zT8x+6Qa}h#%vPF~a!~_AAmR9gYn$-Yhp)E<$W=YXpQbx#B`PMB{Uc$fgDzjBj#Dls$Fu_rD@3a& zF@4KCL36;_jznOE-P~zORAz$83Q_fdNc2@Js7=Qj=<-#q#$*59~RH>iWQ!APT|sq-5t#d8#?CoVz5;Y^1znI6!f=C@Z~51XL5y^wapx6xm}gx-}}lko%HgyAjwjf|RWj%g3yv z0h5tr_wH8a8M!AUNSE%|EcYoYjbe`-0Cv^FL2)!CB??tO9V@EdTZornZM1$t;Itmx z@@qKcbFbz905S1aJ0#=o9pxzxWcfaA9u2zDDTdj_al43A7o95IZ!I3VxG)`lK0k`+FBUz4P2?^e!Egv{c#qHx0Yw@d0`ZiN76lBUxWY-)^-!kDvWg z{{TPnmT4cyi(YZ#pE0qR@b4JiZZNXu6Ln+QRi)PyLY5Me;dzCWD5S4oPvNNiHvT8w zdN)xJ^YBxB%);D3h!<8D0r5xi>_4LBHy(4YPml6Q&e@p!oboKIWwyfItw>ZWlP$svHC!wcDuaypxREU0eDt(V%r)H-mF4i!0$ctE6!i@H+HAioL#D`AwTU z{Y$+wgB~(ocjWvk0;1M$r=Em~( zYP#-M-1$!>XR5}>5vISy*QSY&_bZRgSnnlZ9HE!r&Tqt73lNVKat*oAyA!tvtaq>W z{>dq6-=Hi~gn_Xn6Q_NomdoiRwR>qGG%G3lkj7?z*~Naxms^kwWE}w-{yhqtFg|tU z>$i1Des*5V{6I)@Sv;uhC``Q#~-wHV!;wvju92?0j8{uR&sNMhOKg~yLC z+=<4J>nD>kC)S9_j}P22gNkKDZhOLr?54)F*y_?e{8?Cp4RB+E%ggQjoK|CdG-g9g@q@*3X^&0B@UxHcNS+(`4A$gFsqX-3{QUDP`Q{kVWwe zkS~%Zc=>yBW#=%*8$#Z7J)jQu(2%1=EorR)r(U|q^J^PSWM=jd1hriQ+I+TGGb=Fn z5*ON#O)1ip@As~oaOQ2|7l#~}uO2d+6YfmLq@w3(&0Pt4TY_4YrXzc`PiR6_ur&Pj zhvgV-u4civpy^FV$#)NLIfokK8F%7HJGW@#+@94|Bg|fPU=-)1Vu3hA%nsJ0X4 z_m2Fb!a0SHkodwfxeqHzi#L^-j~Gi)B{Yywky2}2fE%53OP_D=?Wb(E@>Qd<;I=;f zVQ(zWwn`AmN!8T+)t9m6yh~`~yb|%m**xo&lFGvj$+9-mV#&MCNlI4g(trq|9YChC zx44OYLd$`v?z*lwky;2@EsN?0Koqqj%7>p^YsOV?RzDSN7NNKeNA&AJ;2YO)4RxBI zgG6hvt@!sgMgZwS1zOw(kgzPv9J9@~EM&Jiz<%jXD7IsF9;5@;eRF)K9$Q%s;pJU> zk8(RZ_(!IE>t7%_3O9|sE0<@te4p8kWRCN(?at9_ep87hrjS*=NuZ{f;`qPznFhwe zpgOBwUz=_t80lVhBSrCNalBr62QkRAtcBAe@087YY~`KFazlk+Z|IRgKMi|II2&yP zV{c{RduKXnT{N1P5?7p?mcrtGHOMl#YeF7|%FVr#O1U9Ih)A!@>LuL!BdJ;pE;QWS z29AMz_+*#4`4#Hpf4aP6TrQ6|^p{TE%W*{0@Er!ZjokjEwO)>}wGl)cMK<``gE5%r z&}7`2t)CxH?=EU1=&Ne}nh8JZRZxRO_SQ6Q}NVqZ0rTCr>g$|*7T3u*DV<=(6)sC0E#dI z=N;njaDeRCw%PJ+@#Lj!K9(dp%49Hlq13dbg`oPSK!fTvzmcqStEE>KwsYFbCw~cX zq>fJHOivMLAKBhygL;x^1bcHI*OKWw0Y=iGK4fZrxCK#i3H>N)I;TI8+>aNLxkmRk z*H*E6C~nh;$NE5O)ocZZ014abp=F9{{RnC zTmu>NP#^wjYv7oJdYx1^s?RI^R~Ix(s<(G24Kv=K_sXeI#F zB0Tj;1EBTN4CPoy9+gs_D*?Px&aBGfgs$T&y%^**_31+KA1_T@+B87I> z)KH`^E1*($A(8q9iKeMeC|>^nqqdv;bt;M_5NlCAdgv7MS`d07oJG_-)`S7vR6q&! zCcc_0cGc8M4%!!OI5QGs#vc>1er3C*5p#^0k7F8$zopeVDLsj&$sp9~CSF><(;bw2 zd>*6yLKiG9_b1eumsyV&=Ff3Ub@gsm>x8LlHTaXE1au2X_w5u>ii(!uQQYoLekW66 z3bIFPjk3y|T3SHvQq2-RXGxXY8g)v603|@$zq_48>A4ix;R|z0tmo5NP7mb=qIDA1 zI_gyWsdfq`n@w&B9es6f6&Zf0>KG|cdXYd6u9ys|LGoympO93p(XbkpNC}oiQ1;tR z+V?T27MLzhlOWLywvwWMhNR=|C%JfRVx>dKWbLgOUn_$^@lu1lPP;QHR#$wDXqHED zw}l5@0Q^D@+S5OawkH1omDD~97aut@yZ-==!shs-CoI^TYb~bswUnKdS|ER(p*qIb zpm9=$TKwy=DO>T2CCSfjCPu?f4vBR3f1aF=8xy}vlTzmHn0(C94ROM*)|9Q%$&(mw z`?kSSDd=_`G?R}Z+7#8+^uy#a@duki`?KdGamJ4sDLe zO`+62r;=&`+!|M=o)Npcsm3(JJxZc*F?np|mbjA;T5ZW~C2k#uVQ>S}RdYu`ehaEM zAju)1>bmF4d_8l+xYpO5@$K48g7IyP(}+}OmV~TOD1wo({Km48dN*2#~q2W(%u3h|j7HSnRMUQ`>Ic!4nus^x*+naCij zs%#;|ER^*dqQB#-TV6+B)F`0d@V4#U8>*_#LxXZ23n9fyfhHJep4U>9c8&fi)#P`P zyjH6vafg;8s7|~e$v#WLol8yK9gxFB5f$>K4UhZBOEHHErVYInw#K~5!t#cbZsgou zf`_P=WhJ1YLqJU^X{9o08K5d8*tjI^s&ia8v&UXuVaa%P>AQUQ!7a3vB{V-Ubo6;T znm8)?FfL%MZdH?z{65R0XRdY7VK0vKJ7VJX9*ReZlf-mu{xs3P#m&@oR`%J5q|oS7MNTaW?OR0zb*`&U;B{6_4I@H>4t_t5lw1;s zS?(W+)MDhFs8020_aXsp?vM27(}B7z6l!bJNQ~%^?yEvK(#&c2+<;cZgTK#7qEU9J zi;AjS zACEM8YB!BGOl1YI@{mWUBx)Q}Il2O`=eJ|}g!HxNmzX3Zz3(7@Dv$m;pB7Q=R99Sn z7xWZPp_{_GHc>I4+Ue8Klc}u8HTo4z{)R}OR`gM|``B^isgRHls5;H$7zxp9aqe&w zDWKpW(z}kjhbKjJ-BpcRf@wio>ewuZNzOQ}hi}J3N`5w)BE*D%NYa>fPli+=OXwSP z)Hk}WQdtoSPvf~aOXb!UJ#0dxN}9B`+bG=qW}cd6{_sT|N}Y+vzG*hKeI@vN_P~jB z#p34~(M$6tH|{1YqMMv4MNiA@VuhYn!+h;uypScOzTZQ!1pM{cEx$_PSr7~K3Ofnd z{JQU{Z6!i*(R!4V{{WZQMD59v5`;)nWh^0v7W$R22?X!?XoH$Mrx_E^K&32mhBJ(j zNK|V>jWVu|ED$Jv9Y>!h6BPSwJkK2z{f&5^C24K^ig)eODOSg?$5q!E&3vln(~Xg< zl+kPA&N0Fehg92A9cy(szS2n_8tS53>E-wYmzm0g234{BrzWAw1>g0a%2glb{kIT8njS9jX6m1oG zQai9XM~j#x&)WXtIQ>fOalt|AK6-D^xs^(mdq^6Yl01%X;H6vm_P{5+*dZ$^2tf27 zPhCli2SZs3tbTsK_`Jr0rLP+@$zOOCpZQ3*yS=;ODhaE}P zLes5tBsiHDD~jbjq6(I!l4<#!S^&`60HBt*xNbF5rNN9MR~9jBwz8FEq=MlIs{6hq zjX;UdY=?J6Yb0@&4ZT!1V#V3A$!76B;2Cl$?q!i$f;R@W)PpFHQAGqaBUwgwbiKRV zCQ_y{kN8cUz$5e0qO_)=M3MluKuN#;hg89KZ21ZyB&2*gQ&JgeQ&OQzk)Y8Q5mu!F z>$?NC`nP3}qOF@IN*~yY(F!0e{5tAWU@EEiq@fy1j}Vqxt$tdc4mHkIw;2H5sb(}t z@k(BtN|cjO!1br0rmf(#WLmqbpt-%4Z!0<{{9abeEwF#KV}8|+Cq*rmk{ws#r2=(e z?ELiF!9cTr`gNn9zG7bE;+qn4EZNr=A$csNzN1yu)7S9nsDJD(i@#A;vGM$UHu`O% zdE#8%m)IX=i^5yJ+ercT-$GQbz<1wP)|{g1FDv}b_^j@8+Z=t&M>IcJCvtzAqqb`s2EG4#YCxDzsaaTv!s!x_DpCNDXR-Cgs{A8P_sFXH*I#pDMxsxzI4VXncJIf z*1u(PNct2-2vt5*){1+mu3OTDbNd`s-ZF0~t5tHhD)q;C0}m*aw3Q^G!TMGY%xap_ zc`aoGbXz=yv{@@s(lw%)xi=M!n&OeO3qk^k^IMgvKSCLB`(Xb7rl)OaZDwnt3!P-K<~xTs!9^Fm zl3q~kXXT&=BBQV=BdD!+(x2w&qOv``QWBBA=9*m*hc9S;9dr>PoPjxr?aOu(t;BfI zsY-1@wI6^{6t0cTqEL7#Ji}5jE~4idR7dDC#t56k@uHnyOv4PHi3WB`4g9 z{{YhG#XkZyVFah4=%Y;;{X?onxk7T^B}hM0YTdQ^6rP%kGbn1NTTi%rSCJ$ZmDG@F zROwNpnku7m8Vmla4_>2A5>!?`niQX+PoqepN(k8h0H;KfRpsKV^u9{*!;!c$K>g#m z^u%RYdkR?YW}wQ7A0NJw!108P!7K1WS#OGhwyK1Al>NY&UVX$_<}t|8G0UTPk$f|7y8g~kw?4~;iyrw$w5kpI>krE; z;>cOLFh=pH@m(9+?6t-w{gY7aCWpaI9(QqEd}lDTa=s`M?jy-eCgTcYU8S_Vu#!_$ zbTuUEpWA%?_&%Ng0IiE{?_U;dS53T+SmhP_*Hk{#zDcu{;%@Ps?r0aAIzWZos;Nzi z04l9?dzSANUURsHpK|M8C}37@Qo*u39{$jF4gM zEsYm8x!;WSyQ5IPS-p`uA*6}LwF=;)h!5tKT8Pv@l*RKuf2lJ9rRKvghVL^56UJN)$a z^xLCG8&KbRY1$@U)Nw9U*WsnHsympCp?x>}v}!w;k2c|_U&l)iQC`haww>vxeMHNm zM-t}X{{S5kbyRS~dAGJr59O%!O*$ZtH0rkBQbJ|%rvz6C?bAo9hW2QmHrEwYrj=C& z_0nW*LJ*Nb%iuw(w)ig@kR7@8q zvR|=;d+N2`7xfo;pL0{WrW|+~XSS~Q(Oo2DcPxr!@tC-?x5wqOkNItaNeP**9h9<;zh0%DA z4|}E?%~#b|jVy7#2@`@iH;`?Nxn<_2rm~d2;#&}&b~*jCjujD3_h_1Y^~$&eld~x$ z(@u+9`;USdFcH}8tyP!j9|y7PKaCul&d~5kQmy<(^O5f|U6^h1=!Kyqu#>aL>l7cu zUWntI_VVIzz`k#CPidgDvvlV`7aV@!{yy3`mx*}#<&Ni3`3~QlI@8w{;vmw>>Z2D> z)5=*pn$z(b+g@hnx{Qa45T$!{hKkN#Bez%bTmZ=JzT5A_bQ-tZQjf=3W!@y}q9>!6 z(+hOl(CH|QnlD~uMJXVVHwRH14`m>9N$X+Xk6yX}lADbQZc5W20)Cyrug^wP-O`%l zkmUfN^*T!ii3Onum_(=@>G9HnG(YXt@zk_bTP`Yq>%XRn9@JO%i>)`&sM@6@M(k>; z>X39wLPjlnjw++@I&d*YM%!nypXH<=f_bo4Us{a>DwiP=9BDs>{{Z2lQW|QyW%js_ zX_4l)5w(7|rs-*Uo4O@i)JP7MKv!s}=t0~aRo+VWGNDJ#TgNT2YsQ6l1Magw0pYI? zvx|kL-|aarX7d&tJ`-#0lI!!$9Vs>4Ej8SwABA-98NHHOPUXvS{65|(fui{z!(;L` zH9ih|g|R-;Y>%YG$)X2a5#DHp4#!bSC>nIt&Pw75kEZenU4qKS>JT2lbX{hJ_dLfr zYXKZ{mJj-;TVEz}SK&cd`ipFyl-{5-<_@H_5WSb1|^-Iooi zT)pJWS06ujdUgwZdQGL}6-S979j~^N&?t?G8o$T7U9@_ryoM(lwO((vUMUgpA>Pg< zSl5&>$F`{wo3l2Btgb}02+b&zZaY9#HFa*tUTF7iT!-9jt;Li8#KQCNUrB?=PC$y} zR&D%kCNu`fO08xii)}z1gNh^PU0kz`W1cmXSLVF({{V3L4(hhqz{{bCk7`l`WvQ=mUr8lIQ-6`vxOj#*{+EJLyWGA)8%MxeK1pvH<}7t~_F>-ftCH_m zcT}s(@MXBS-I2LNZGcB=e+@;v;_`!5yFjgMeZqjVW7v0;{{XpMa49y(mqwa$&wQKO znU;&R@}}EEdn>U4M2@1QjrCP;VST!Q=&x@wSWiGb{{X3b4>90|V@80@uK5DvHT5m* zp}9>w(*~3(OK1UVru~3*)X4DJ2A?%3qmD;}+qO39nse`2w8UJXdWv7SZWR^GN`_{W% z#3Z`WSM>eai)|rc0B$>p6yHMOu`-l-1tKr0b9TLeg<&}F2(#O-jAxndF>k<8k9$Ag zZtSg_vlBmXI}+8hKWfKgdjbhHuCv)pyp$|s>Q?&!#_d`}l12iHc}s+NYn}N)7z+7f z>&E$ek!6%dxMK>9ZIf*XE+y3n=uI6p?E|}d>@{VK=a-fdG>{$^BbWP6VVj-JYm9UU zwu+YKJ}z_54`G=$PlQ|KTzKNbw8WK{!;;f$c`hh7?g&5efB^ok(@k|eXFSH;)7D@P zJ}b>!o7sH+G8aj0DKR&|{{STi$GGyH>yI(DV?2?=*DKwxG0E2S zm#y;4aon&%ilEw?VM!`O#V25|<{Hf0;I1ZPqn3xkZt;A-9cesw7TbWRtyJ0NXEnp} z(jFm^1a2Zv$!dMI$(sxlpFC}J1k~55`2_*_6=%Y^#@%%CBgLB)#$Fq5F<@me zmmvuj$GxC})OgRi?k1&a`ba&%>}joM8kpnSBfB*|Jy$UI1~@XH&5iPk(6^!d6%om7 zJiCbBZTxSRrhgOv0M<`5Z@9(X_w6hOdy+%*<14Wx1uE`Oa!+s{Eo~$gw=6S6&|Eqi zE_rEVeUFprZe(*AP+SQ#q3%ady;^Qk<6aKUu+|me$}d^YYsMDkJLNpJM17lWt%S8j zT-Y=FDA{o(J@ zWy+u1xKMwlw$`tsa4SmsY1_Us@J-FDK$-n~6l_mEvXT*Dl6#+O&Y$-Wl`3eug~@!% zIfIUP{@>ztq01M5yb~Q!Ikw|-wxu^yld#fUj-uP7tRVNHYf@+w73r>di@^FZW|n~6>!v0_36@| z72k>NCy;oGZT|q5UuJNY<8yMPloi>o4TYt|wwOMY8>mu-l-B~a9)-)97&Yv@ua=3o zR&ADY4@#={&MoKH%$tI2t#Y%LFB!Q>iBN7?#xYoT+iledE<#F_$X8@Sl&KX>09Z&P zeOzH3YYQIfn^{59tDJJUFDI5L?cYmhG}MpF`K~bXM})k`bkFg;rXBugkXSri?KTUp zVR3}xE=adL@j@iryMk2bvItAA07ZTp>{$N*aBTjFu$``G=)AcvvH2(D{cXOaZ*?o& z0o<<0_{HGOi^Y7JM>*Pfw%42IIb%{R(DCbQ<(A>ZQoYXh*C9o)94!iw(_yX)$vK?4 zSv?V>YQ1~y=e72HX58Hd?vR6DfcP!}dDF~r`B%uS$1#n>*FGS=Gdx=|OjdlhrAz(Q zyolvwuvC#sZQ4jsp&IrF3c(2Q?PJYq7tRlKGwjLoX>KjX!(9h~3!*+4`AK8PGP_3| zc`J-NmJI$2ZQ1i%i|KMBHuSgJgvo8EaGklP^2qOXaT`ha|XOatfMy)Z=7#DLsuUiCFl0d^L{1d$EzmKhe1r zr;6yj+xF>$TYY9*G)}o)(+lX^|_SbHj zW10Jy+s64Dsqj~R)q4#5IUC+y8{)bRkof!K_ao$Ua@d&UH?7uHHgdRW>=7T9!0!b} z8+M;j(Cd}|0PMF+8(MWM(J$e6>D*;=Mx9qQ{EW)5P86EFr;qP3u{)f5h+M20vF)J6 zNvQTh2?TvRiu&uMaQi!)oNk6z^&MB9{lU*z;VuQmv%nzg(76TYC!e>kDlK~8G};$m z;Mh?R^$68-enY4U1xA69-r%K858NcyXv7V!sRyd;tV}-b-Q?iIkjX&5;_c3)q z0*`qh!h}k;B&4L2{Pn{!H%Bj&Y;FB5Ify~j!rndQ9EkFSY^#~TvEXXx>lSuYRXSP^RQreM2k_OyNQx8U zRu1B~nGgZ;Gq253u(dq-BlN88@*2hyQo2h;1ry3^zu58c+8nH_&sBe=pnuz`v4s`J ziaRwvnr%2;RG`;G`Dn^PLqkFXj!1jyZaPU()Y$r_E(Jgdvt^3zMU$}EHA!7uS5erV z{{YKE828ARa{mBA1LB;SeZi@BM;c^OG(?8l1EB5L>@>UCDrl>$A|bUsN@$h*HX5xm zQBEgRUZrFTQ{n5PI;Vi5QEjxgik+2BKN622szR3O1zYvhX=n*?#QII`;xGB!T9vpG)+u|qmw9wcj zenU;%&R*oZ8(&SI6xr2ckcytCQi4+3QBqNA4Lz_Ym%5DR)jSueRVzWK$CVR;&h!4Q zIZ&C}z0gjeU0nebu7y#6tXGNrGdrBQ%L&4wu%{s|N@U(&0FcaR>I)@Abg2MoTN@l@ z{lacQ=lMr8et-YwRWn^>M?~4FZ zIP_b}{Uh?!8>6}41%GK1V~cU>n=+ktbcbX-zULhzllbqec%q7_jtnaQqr=MCGO!tkPp@*n|!I{?>dvN`Mm8*CCeb+C#IX5LW3O%TNc@_1ork&Yvru z{{T6uE3rWfA#T=+0F?3k3jLN{nX*|eu3T&bVvvi#@(sJL`-SaDn;zt>qisV8DJQ8n9(sUJk^;xwzz+@;_wMOs333ZbT}Zm?6r?=W2p8_KM%#aQmCxTcnqxS<_Jp*5rxd>q#eHA?pT)cbnJ zwINAJ{#u%8)|!=FJ)2L>I@r8ht`0Qh=)gSMlB7H+J5-&)>G9LDS~fvKs$Cq>x}dAM z56QIN@S%icP}A**-?wUGY6rPApd=oIQ&~pw4@zC^D$-s;>7v;2N-80cU@m`geq=C@Wx|0-HK)MqOk?l23bRgu#bUBBx)tj5*O-5i=^?q?BY5C zK?=fgmy}svN(2ns1Zy<6r~d$srZ(SeiS#7wLHN^Ii@rD;s|^;$9$&HBW&y!SH*7P< zE>3hY7Fuwef>1Pq`a>UC6{h=Yy|*_|XmtwSIWgD6%SM$D=AIp7Hr7`+7nqymLApz7 zNhE$+oo|?7K5nWd&mM3dO;uvMVtj>wNn5LnD%2WPL#Zw#0ya!0pT z=}(B&i!u*yRZ(+=j!v2sX8Gg)02T5KV>1N5FsB_)b8(WS^*exm9d#wQQ9NoD6qwtX z4O;4|!JK%noMU0>5uh$)L|3uY3e*q7K;xM4P*c!nrD3FjR{ORod$TyE=D~5Ip0!CI z=c?(f+{K}{%Ec#ifTFZ)K~Lf}3u&sJ+7+Qu{{U8;dRPkrkD;!SL3Z5yENveQmScHY#)w_Ig1@sF0K7W(J9?8KlL5K za;Ki&@6+$NJZ4r$*n3h$!c&&rw1J-sjWw$)taCU0R*L= zUCGfn)jt?T9ba&uQK-@%(LWf~C%UC}D38^qqw1wN&{0~S32wGacQ#sNWF4uD%1W7W zMKlft0zX&bthX#|kGI;zu;CN3JAhoikhxdQ{EE%Taq!MDAkBFRY(CkkNFXGU+pbF% zCA+sjs_%IWv2y@of-p}LaxQbWJDqZL#ogshtKE+{Rza_KMRca6O_uZq)@Yfv!u>$q zCaRlxTjO3Kk6NZ$Y!Fh5n+aF28%R=$g?dt_TJ9~#q;shAqQUbnIRtT?gH_CYf5(1T zzR4{n#HcYs=$S5_(b_*PbXjvTQ>x=};3qDn6_~{QKZ6l0$jom*s8G2T27c;r0ZUZOd+{ zts7|xC-K`-daWNqf(%pq_Swx zj9B=8nziUwLZ=fkt@`lrWh> z<6~BULQiTOSJI!A0!FOuuns!7s_VW?IyFEUM=|m)OqUSk^+1g2r?lVGMEY(uachZV zJ5^`p$;$qvMFQI8-a?GYYkf-p0EppLO?`Um!)s$2C~~mL)hyYrNWx^UOeVvxEEIu2 z4L>frs-7bzrAxAq!O&K#NwRXzLb#>WLsWK?{{XB~Km(^;IS3Ji^WlBLsnH*M7qZv~ zyO3Q3m(l?Y0stQ^wBu%bj)5jfBaC^9W?b`oTu`>G@WV+?$utx{Jy}JMF6ygg%C(|_ zN7oDd+k&dgGN^)@n#pj^}K7qd`j$ID;{h zYL$0`iQHYYLm%^Z!4cHNY!8d11nIreH0=jeZpuBwap;(G?Xh7zrrl_|>UxrE(@jYT zCsiii?e7{I1RPs6()&6ks>NwsC@KP-9N-@@PaKbf z^4u#Se;Ln|)*FyTN>^~S%5}B6z*+**73?W#sRUAoP{ZnaCtZ*Y&}yS8wq(Ux(1{{i z0U#+g6#(_$MCkh`V87kkiF*jjZrCmQ9qQQWN|;dn#Bykt8}&&ahKkPN_V3)()?*nB z{3bVwt7FT!)svKPco}ttv?)tuEU6$>FH`DDsl98hMqb^S3r|pfrO3F|{P??yCc@XnAQlV~d!W_f$ zsVPeZs8iIZVvs1B6J52D<=B&JbVW2>L)*?wG8Qdy>J%*6yGRu3b(LKd4N|L(dyzuA zkMh$E>ZdcJR>7}|vSbmSQU3szbza=0AJSW2(rQ0FM!b)3R@jT5PD&nyW^HLrp8YX| z3del=u~+C&=hP|ubcby=ah5o6T~2 z9CIr(=zJ7B4-3fUT}T_Jk7dzTzQ>+VJqfqRAz{k8I4UftnIXmZf21q{{AsKk{p!Vo z`dv?w>J9z8WlVM6%J@}L&m+7Y@@vIpqF*j?%kXjM78{S6L09jsDpHj|l{|n8YCfIm z0Q3Z%Y_J^b8I%72#Qy*{fzYmL$-TDA;*Dl!l=m8i31KKD7Q_ShR-Q7Oq#89g(z=Wj{?&S8LT@y(vJgizsYqa-zX>?7o#dzAPo}DyF!W6T@`Qz9w zb!NQxl*3DbtCMxvao+x#!5*T9x?9HPsn$~Ma$4t{F}ydBu3xbs%RTS5b)n?bwM|GK z4=YH<@A1S!@7tXQJ__KPfa{%Qez^i>a~l#nGb1 zlAWva)bQ%DK_D_x(5-&~`JcryyfwS{$<5!mZt;w|@Z%{_ZRO_Qj_CJN9(4(#6onF> zsO~AQX_VtvQRPLU>i2K10VOvw!|b#)VKm0p%O#kNm}FzJ)Fy_Q`(D z(mhH?XMbv{2U=^nvT-b7!=YYU%43|zmb*s{3i}}AT=v<>xK2HnS)>UtuJPJ%ID=7D zBoZ{k+ea!%?T8A1eZ{C1ak})vfEOAH?hTB$m_KRFa@plc~|S za-(^uN81Wn32g;r{{SjMQT_?k>am)*sNyo?L8(TT%dDww$&9#;qqn_>TU$;5p-@U3 z2_O-;G~Z3OcE~EzP?J1QjoV$JLLmqvZlj^pkQ7Hat-?UA_0obtH9^<6^$xu^(`_|Q z5Qj>-5PvOAX-zq($8kDvxilEI4jzEh<)l#&i3}XvQ1$fEOH*bvFkat1KFJeX5Xo5; z_0so*lM*nMfl8f@odGgqD9Rn$l&{XZL46Z1iiq25^!RE>dW|G#`V=cletH5$9LJ(I z(zp10PK-TAbZGijw_1F(u_A_9sxXH)2|Z7)mAoXF(Zo61N}tD15~96{MTb*EQ}}%J z*pyX6k*2BW+qhbA((0w$4YSeJtksRbcj{{Zn1r~`epur*PO6O!AKLTXD+IHd;9{6aT9H0+Ry zCI?k5V@t5!qQ51}`XM-n0$POdSX#$WLFg%}xuw{8De+@lBWfK&6)p0M3wGU^H+)Qh zt4-1~Kup-BZz7>UAB`!mKXdy%lWrZ?vOSl|`nH=1scNcjILu_FF>VEtf=9Tw@)uAWOMVaYaNn!0#v(uj-{W$T`*a!RW>{ zE7IQB;)~h!GM_6{i^$$dPUPPsBBI*A+F0W{)Lb!;kdN3i2X@w;+U`ngU1|7+1+ZCw zdX>w)+T|Zza!CCx=)R4u`Ap9)JVlF*y?R@}*yO@{k+CBsOt~n=P%TMYNk7$WsQ}iR zvcudu&i1={mBDkE=4C943Fta5Ci%GXEVve7jwxTawAtpydfb!Tm3xh=umRoF5nXx% zF#u(&U*Z>^y}Y(_axPU3cuK|~$H+YEq_O7+l5cSPT0exL`ghy@)Api(!|kVSM73R$)STyNJzrPhIU zckMxBRp-s#F^ZQPjMPhY`eeDzkd~vd8V7TZtx6ySq#ab>LSD)R3p7uoR7>tRvnb$&jF~>B!LVODu3)zXvTnvZa=RTWujj=^fs*^wv);!)|P!Mu!^s zCh&gZWA2-|&TDDdg=1aJF+-s5m9_6L}X#0$HNmnQ8#gsj8>gW5L$ z_k8uO!*VP5s>hw{p7IV6iMam&BL}BKqHM>BKeR)*WHNAAxy82F%So7smq_cF;97`N zszEhKBYxVRy4Ts~WRUGGEi0a6_+_+BG7{Icc13Z_Cl0t@W?NguKj=C0pogu3`EgWV z?PaFYPj7HaON#1{PWp0d%b2L=)P)Yq4UM^wvM(#C-5;=6IYS_kb+FyCsr-j^ur%DL zb_6;5MipjDwD%HQSng<%LPcxWMRk2>z#JMucXclpjmSC1GVt)u-vqOCeni{CTeo2I zIPBXL+`DdJb#=)w8I%%St4VvjILI`jlij~lrlM>>RKBCZPrHiY#(|qpVz*Bj^PSei z{{UFzIrY~pOqmHeMnh*xVzDYA6+#+EdJv|`I|QPj>3{Am1xDQzck!&7z%P?tx*_j5 zmy*~T3LxUI!?9tI(u-}{Y$g_{(NENa?e3Mhs?@H8*L^h&o;G9QRMqZhJ!skuXrq|^ zRddtc`&M^kW|<~Np&u$->rZ5;vCyEOY8u<8v`wJfI#W*h0Wntq2?)A;ow|0tqR+y|ZwJ4P+sH7<- zgbtO|+l#xbb2h>Yl`b;}Wt)_~@xn1(2a4$Ye-eB{$oMmivAcBF4X+3N(yly#B}j(j zh`FpgCA2Nh)hHoUQ$VDCI>ED-X1<}%p`l&bp4ahLShg{=?fg+5H1L~>q2^89upU^+ zTg0(@75f>sVvGHojyP^<_gH<@kPx<-Dk@KS1wi%NPfgBmV{=QIYgFB@w3w&0-kZQ- z6auQ8Tzm)eD;~#7P`PH+$g}ZCuS??IIEq@6k2t4$i;KEMOP|t5`{gR3CtCP@!-?}W zJ~|WLnyycI?CsPrJjZcR&}iEEe(<0NzT9_iOW{1LoM-1h`#8=q_hd%MIAYlQR?7xO zer{0xi#G1pJ6v(XpaHECs#DbJ!25v@)a{X#U<#Gq&b_$bR^CcX^Y>JEcqaEbcrE0b z?HE=|%+M}ey~#NYXg18n#_meE-7yv;FqdN`;LttD4J+#J2YS(3my7o@$IKWg(b-hy z{fo`W_@68Hz1leSr{om6kG8iW;d~j*HypL~akn7FfZVx}>d;l_^^$pn?wc)`Nj^8_S42Nr(+SIxc_jClAG0-H5Fi_c(s79YOc>{Pa)m zBjw&k;w~b@_@g_q#KQbb@<}tYJVrxKGWFW}TVV@sF?F|pq#hwB>rF>ONIMn&2G;KA z&K>?ir~MMKyq^0PVB0BJJqLnCH9vpiqMkp^xysjUqh&7v)a<2??|7_pv6PxKy+&v|L(DIqQpK(EhSX_#p*ZVWMLO&72(IbE(V z94@D+Z7WJw@?2N)OrQYrn9SWIs zdRvdle5Qd*_a0J-T7cN7>$gWpnLsEPmn5>4#--ZzK0kR~T){jc$MYO}iL;Db$XhM{ z02#{z2P(s^v%hI`vcq{@%VlXnDoF;trlWne$nre?NS^qNdV;+PhW0+*_BieY+nV96 zeoEwcPjTG6?8 zpJL^H9`W|2YT&oO9m+NvDkK*iT7?nx z?n&2V#Cu0$4J43mA$bco`-;wF3$5b+025rfTjVb&X11yFZMhagGF!_>*pDPQUQB>g zLR&Uft4HmlC25JHwwPHz~;~kohZJcdC|q{eBv)$Xxq%Ea&rFw8}dI7A5P*k3RPcyUj(+;DyjHt&M3($wZC@rKCY+>?D27&w*>=D{VX7K2?uRF-iSEWBOw*9Xs5?S z=G7$*=Ex?S4_}6(qD1eK1Pe=X5l`GqP+I;VQ&LIM6fCRil`%NyAIENY_tXCXGc*=p zRByjhH6q%KVI^1PWc6kY9TOerON%NdrADVvWs`BLuw+ySGHXq=-D-dc>kNdW+*qQI z`DyUL6B0V6hzU{v8*FsklIWKVWRyMYSy{1;58pc9eAsO$6dS21;T`50(=D0B=MnRB3G`sWKX&C(JWcw@XPv zlti>AX`7dND?>2IqjHkS$Ift-C~m^wVEZs(z^-y zD2i%Cc2mEgP^ZX1&ZS5r$No)v-tEWxOA*>(1;-n9TMfC|2XUfGz8aSn24E^__^R>9 zAl*%W1=ddrFh+b_@tcx7mCajBT&E~)Okv1S{;O<(*xdq#{mM1lgjVA`a%TC}KkL#lbzu7r|$*1D6T ztaD9Jk)tj|hM8!0xayLWC+4F~CU=IlJ#7y6fTT>h=54Vp_{2r`HEmCH%6~KK6*c@l zH4e?2j^eH|)3uBY369GuaYCEd(^rkqET~7OulA9W&>vFzofu(EOlzWVu?|G}EioOX zJoCx{4Wv|(O$7#)*c~*1jp|f+u2}?S?lb{(Ih#x$8~Bzta6&mNIAAQDi;&{1Zg<=g zI&0F*wug;kHh=#BM1$`O#;?gu%{c)&{T;cd!OjYjOLggTos{7(I{MV2PUMnmN&Y(E zvc2zdanzOR`(`#c!$yEBhr_&^dyB-Et`NpBSZKLs_g1A%LxHQs2P-C=8O%p`? zcHd1p$NS0EG3&U$Xw($v%(Ckb0NLEc@SexBS)#YxqP_35R4Q6Q^gHf#7FRk$snJ?Q zLq!19QtOq<<9w9#`<$gb{FQessFGBoYf^rCOJhBfwt$o5u#~d3y02-bl2r5RG(u9H z$tU0joO}Q_r0AW3YlH*?ko^_C6U4P5y52)e~u9&rthNr zJ^@HSN}FwBqpE;A4%_WNwy@tj>Sh42bPK_My8i&&+}OAO0I0BWWR3(qc3V?3>G0A4oWSB5fQx~0aX_^LCN z;1Oi4;_Ni6F1^)A)=Kve9Dvj>O<*}}i~^2T=Op3_Ti)W?jvHR|Pf8tg{D%&W)h!fV zn*;7$%yb0QC@T5JH(P3)Qw{s=4u+eJU~f2_vAUz8>-ev6ZRY?#LZjDVN`|u2t*NyF zdw~?+RWidQW`%4cy^2=Vr4GyPx7q4$S8DMZVp-9d>?2Wio1 zAi0VC`lRnG?O3DNCob5dC_2oz8| zSG0=P@YMyz4pwU!(ND1FP)>Hr+LS`&#U4KL(`uK8UfHr-9TiM|G9z@!2kc9%$<$JdNBS>qW-s&9oY;fqO zs9eXvyq3aR$!#p!XE{Lc(f~<5wWj)=9zvQGp1Hv8)w(2`$Bvw7mLb_#u1dnYhjT24 z5=lQTwK0;#B8KN(+Z{Cu0Ly$<%xuNi?X8Q8BXoxP(@1_jr%+Vuen{YG@T<+^3dyW4 zDZ7QG=6gxpp+J9@tL1`0*=V53!653J3T?z1EwYpFBT-8nI;p}&o`}pe1wMo*nke+s zkquONo#iD=Qt_xQSVX0_?W?k>R@K~BR$i4ERY48oy-FxG)Q@pwe0Cq6s$4Z%$ep6n zovI(_qE5F|X_-$lK|<6i&~K$SM+DrEvE5RlZtLyrkkEB1>S?~7ZO4kOHVp?vQNbDh zBVkYg+McIvXPbrq7eMI0e2%FLOgTGaXH`jazGlaz-mVu*o6X5} zYf_T6cBvp&&>u}}arid@(R1vlA#2}Kt43+$y_yMeFdaz*Q7yJM+vGKS4jJ{;XRdRt z14Uo%oZp@1FRmUpY<m|4kmdHs=?yE7-M#@+|0)0WxSo;Ldd60`khqVW#02f1$AwLjxpvc zT1xmsilT-s)4TkS}d*`gKuWSLK^AgOM>yauAgLExbS<{LM8s z3?qiFRTsRLx;0kp$CxwxiM2_cJ?3I6r|7B^xcO^CWsZof{mw>b^-&GJMC^{_)N=7s zoK*_cO;4pwb#CZb!Jt)ibdI8!)5glT!(91un@LFS4U!Ju9ac$uw9qSNV+A!#dGcc= zCL}}`-mMh6eL7oiMG_fjsus&=ZJ%;g;$ma8fAoO>(@>mV^W_R}_~Ra8k?%L$W&?^u z2LAwb;tde8JHN#0WO(ThRateGg}MaWS0mrzF!XzSpSNYy_N6HTpdXzzYZed_s;8Tg zI+O}Kmo9wwO5{AHsb8w3o&Nw6t7!3|qd>E_c}G0zR`IO&38gK?itLq|Q7A!H{{WCW zu!9#P&Vf@;nu;Ojbrg?c;oQFV65_3{G}3=jS_)}j$68BHF!yR!J0s66qcm=%OxX92 z)0t1bDhrRP6sbx|lAm4F0RU54D||FD{-uY=b33W@jA*S!3*)Wz+XS4t-DE4Yz$G#+ z38IkEK(Ezanse*zSpNV|x}u)a`c`_5=$+XIK67-eLLV30^NA908(NZ^RgGzAu*DAE z*(ssz9ZHV;mD{5cxYeWJq3hOZn6S~uqqz9xf8 zr_AqguVnDOafWj`*&iyC?cdqB1u54A=H*TWAFJL%)K(AUT{iYGc231`>Q}`kTX=2! zjf;G%b^a7FHA+N?(jE5PjI^D@f|MUEM3P9)MPUuwfYl)KKNZ{Xu??vCq&xM|#1%_{ z8@Xw0r2hb{Y-^}<#a^8it+m}#tAVnmvnF;Aad)}}_NtDBew0W8f`j07Wq`RczhUTB zH=W}BSUpycGgLFnxUt8wr`%;8+<6O8G!&^P<*bh?$B3ph=(j%H=FQEZuTYXLEw?5* zQ(CLxtf`KMtExvOLU-66{@Xn%bk()(PyQa6bwXTjsZ04)o1Q;|C!mat>o(?-yC{-_^G)Ek!2 z)mvFhR?%B7G--}a;*6_|Zv?w`#*|#O1r7U9PQWWw63AhYICLvZ1VZZEu=NRZ0l)MA z02=YsyLT5mA~r8?Qq9Q3?rqZPC%H~`8k6(ZPce+f&vRM-08v_89rj|z(ETv-j|E(^ zuO+#`iK~)%3h@r$l~g{}!c%1j-_$IcTWC@_NS5E=55~%=be=6Ma}MZ`+mm4e6PxQ^9$x2O2NR$y1639%9p%`B4gVu zL4Na}x!<|Rm`G9*kZQ6MRRT#Jca8Gb&t}&-r_>{*JFXeaxI}jMuWWB`qz;I3Han0h9?`hOj=yCRf(=Si_iuUu+$4(ct;9|)U~%fXgzRfv zK6FjU=yqbj$jn3K^GF%F^Nu3U?Yf-a$%ms7kj8$hb!n zV}KqM{ljCg_N9CpWqA}H4Yt+B}sBo>mde`sd+;r(?NZ75| z4sMh2J-UZou2yb#<^@{W2XxncN{E7?mXK3|$238h5a;{}A4915D{VC+bW$R4=vKE_ zh5(~LT)!m8TmD-82jVoQI-x?IIuwbeCKr|ZsonnozUUx?4pX=<>TN)!#2-|vVn2qF zAQaU|1dfZeJf$utoxg9qf0U#w+`NGC7%^hZ08f0owk_BMc`BgpKGHx0)2ODIf!yko7;{z1`p-)?O#agsLbzvDeZVfpLBw#*wzPRbSDi;cMs$CDCc36Tn8YD3Ma1to2*G$4cV)O%ZpMDRkR zy|xlGg1&0J+wtlC6XZEpJ0m4HW>({pEkz*q$8}2XTVLb0yCxGRE+P1Z%r0#G3AP@G zqD0|OJp8lZo+r#WH-~wOZUM!4_wGOITv7xpSvMCYHW+nINs$@H$8ra6(y>9)OMVs! zr4l?KjqjbRUI$ zR`W8~$M!!me0;%YWmuLEadF7G-Y}S%1;)cV+@cip+fYN#HiVt3rdE^cDZZ)ibGDC^ z+smi$D#pi-aTl7_K{_rVJ+e%Z@YHHXD1)tbbyvuZp zTv+eOeWJ_sDJXFZ?XRJyL!=9UI;he_YteXxEo63rs{a5stc1IiXF7o?Fxs3c2_G}J zO)W${Q_kAdmJr0GHY762hq{2R&3cc|O-Vw8mab-?sBg}}4{=9AS^>;sK5Y_lq`SfT_;op+Gbc#+h;(5GBjNCf&1 zj<{BDCM_*pDIEg!*BHe*`tgwt<#q>>ZMV-JIQq}TR*Q3tO`h6UmtInpdr~AQ3bcnn z1Fi$zhSLvb}qUP%3QsHUm$ z*DbleFpi^N!BcX4W2|uk=o)U&rklmlq_V zw2@kOpofg%H zA~Zizkx+C+NGR`T)n8ABil}W%_Q*?`rrK(%aYlh3KbD|f+=!b~(OqJ3&$yFgQ8|nl z@*HW@_N}wpw4XvXX=O7W%7UO@S-vRWu)PLS5TAUyR{yG5-iv0NZ#$367 zzhxFKAeSD^m)-2rZS!wZB#3B*Y@OPPE*E;F6phH=OiO#{YDH7094?4qG*iwc_SMDX z@c#gluHQL}7QFJ06Wi_A3}Vd^>VXdF219CGhFWn%e^s!Onr;r;3hOO(p16GA0ixa6 zVc?F`joPjlWLCKrtAp0~>=s#JRE-<}p46SdqG&aH!+**pKExcFE#ktc81%SA1K^8 zhU<2Qr|*i&Tbpin8ks>TZ`@dEw>xRDNbUsuH0Cdj3PC@ODX15>cCpOs za~UPQ=&WqG{#n+Iy%RJxPSw_*5Uo|4C*%xW{5Oq%>ZducFS^2v4hL^ny^Ke+b}4F8 zvq>llLU&L!tu`8B{O&Jjs^{wO z?XPr_fy5l4JBMVWjNoNATSVkX&2G0xjl95&gDiljk}zjj@KV;eZFUiWV?CH@cfARC5UG4q{8I;d}nR28*$Cp zK(~9Kg()ejJ8P>P9qe?Z?b=7dRPy{2*zNXm85-?+s-bYoyr+dW0@;5u$D~}NC5GP& z&W#;Wqm>jXRi|(OC;+2c3Jq!Nij?#Ou6;awk|DS`&22nWGQ-SVhk;}-UL)V{cJ&|Nyu))Lra~sY670D z-eL2i_+KHzuYX*U^g8FHJ|?g(=A%!?V^Qg zN4nV=VQg)AV~3CYt21)*OUO$@e1CCbMYiDw9^G#`osIs2sf3f?biwOlYuhAWjJl{JsNi(B!VoD!dP zHvsxaVN!r4X#fMi%Tc2Cx)~k%l#9Q9ZkY%nh^71Pzs8Nr8@O=#};#r&WcT}J^! z@hc}~%dl{6j_HB%xz%YRv>IryHM{QJ;O7qD_c*+7I`K7NGY|fgMS*R(HAqrhPiaDX z077d;)|+dZP3=}zYmLu6S7x&JyN}yi?6+^24&aSGWKmd4AKov-Ox|WKi@lezX}Za6 zrzSakfi0FHs-3|mUubW*AM~EO&t(0W%G%2}@#>v_~MQuXR}bi2Hbbxj6%mrDm9ODB?%$$?lVS#vOV~ z43&-4;~!gPzUrQp8rku$b#r#^+b*>6SPoU}j~KXz)VC$1^x4z!>G*|6b8oSqF2ljG zeAk6}gB|4`WFsO= zd4rhYO+^phb^Z#{LGTBkIAa98;c~Eye=b)dvfDW}KpaDoZI1SW+fA(?DX*yhq0>~; z^ScY{jmE87I}d7eSc?M-LtnUksEXZK^0L#z93;3n%uX-lYsOvTckDII)rx^tk0(t3wB6J4*6@Xemzwb@-Gu}jqh1C!o+UU zrQ2R}_jzT)JEiH<0$*=zLj{WESOJ@Y@^DG$!6H%p|y7N)SqtrxuWqnpTFi(m4)#=GsQZ zXx+{3Q=Z-MyIZ?g-DI8^{b=5Xi#j#L-8I17uJfYwL{`kR$Di#WAKR53qlF9$Lk@?x-(pz4Ad1E2u*{{WVr>QMDPIQJCo z%cmgYZQK zi^^#(6-h3t1xxPqr8JolQ3OWEN9rX4fF7Djrg-0s94F(`JWYS|WCAy}78q|%k^JcHEcQ(ucLrR|?y>yI!7_qc#UwUg%S`NeQ{MTWy54ZP;-ep^? z)_=E8HsMI*OH2j7mU!2mHspN9A__+FraGdh09Q~@vA8|M*C%D}rf&yw8Gj8}f4isr zEj|ALwS03qW4gvd*+<9tj=!IZ`RDRcj$qWFBUkRWlH# z$!Eu6T1(DRF+I?yly^VK4x`;(IjknCZKb4ccoYk%KXcAhhXL#p#%w9@ix(d)yQOpZNMJ-({a zohnfY{{V)bQf>=o#DdlXDzr$6xXLEoG&c-nt3(9HClJwaQ$Z>UBswHRR zQ(aGjJ)kNZsmzbnD_h$QA#W~#0kPDk)RhuP01_0(J>~8VH>Re(p{f*SrCaY4xY`ZQ z_dAjDW`lU96xyUM=LHhu=uJX*{{Ss@pK5rhe5Uqc{y6pdmFDlmoicH?bLXPnZ5 zu?8A8LNhi@EM{{ZzJv&avG)cYr8egosIjprF462;Ony!oR^g+3}Ux!JC^ zwfl1-Evju&2-afe!f5)1xv|PYIICO=KvL9``if7(Rm}u-R_&oj;$G%NP@{!N9Z-5u z4L-@KqGD?XJvf-WTV^dK_RLquN75;$%rQJ_tBthLev?qNy1}^4jHcsURd8U}`XzOs z>+{uo*#7`68mw(ITMwL38R9$4EE_Y8KO?rO+j7rq>M5v2PwJ&THJ`}ZNEkrsv=UxJ zI2x-%Cw_y7fI+r!Zi{8OSP9;c)FljES%R{nbAd=j+ z5>%20;r`89$8!_J{EKVtkU*-^cHg1Yh6dK5K$aj*s&B-+ zpv`QmZL6dI0Nmpufk<+l#1EeOd(?0$y5kcQ$3ztTq!+q>2Giq{C-4rlmhM z{1G;RQQlDUg5!F1_AE(Fwi)P@kyH7NU)o{7)U1W}aJk-9S)_7KTXT@6?T$#FG5Vw- z9qKhdhOLF9Ow_8EUQYgr?Th&H&HSFE$&}l+m@){e-3kY{uG&PoIH3vnv9n9nTRbPh zOm5JYrsmUAJ>WZ*l_>Vt@H(r0ZO{E7TU+4R-_)kR?ffIe@j^ssXk2lzJ*r(t`0BmV zPb#zZEujh(%>1^o+feLGUf2|-!40|q2_BmS>P5B@?=^C&y5tRLA%Rr_x3fT@8)dfPY+7YDghN*3DQD_VuZ_@f@l~*6n;EGn{Oju-iZ@HclMOdu zE9pNX1qPV<2fwmr?=u{I(>7*W7u?;wfZBZ(>;4+GwZs55S!=FDPw7p^q}(Qz7hF=3 zqgqt_HKDh%XdM=I=OH7_YP@a-aT^ttdUe#+*@x&AESGy~Nl!d`jq*IALvi51QX5$S zJh9r4KRsDTi*Z@&zEKQyp{Z8$k32xMNG(mvFD3PwcS2&6r(MpZw$w%{Le1QA8;SI8 zfnR~$oji=o^NbdD{QlJ?k1Afa5Bm}1+Fx-rBWS9rC0pR9KPG;I^4I_>_gloH0?Ft(^?8P zfk%Pv6{6ww@_xokdwldpfyCmn^ZcB0I$96NTj`%8V)S=|1N%(g-B(vAu7Sh7^JpmI<`|S1uiWgsMpu1(>|L|RK$=- z(dE@5yJ}12N`Wjopehox^?!-d3`y9o%TJh5H&C0EhUFIET3ZfBaVkT*r*Lbr)Dcz? zd=<-`9mGYQY7~{p@}lJ!^}6=L!?HpElt=o8q;e1^w+{nVn&B*cmFuECJ19RMm|2h; zSLr62=Q1==cK9TskmRxtYwhk+N*`a2pgp~nuJKS*lZf6!&{Y;vJ?O@er-l1i9-r|Bst!SO8kP??$Jm^;t_j3;DOD|&{W))WXa}9F~%)%Q(W_t z;|;abn@uXaX9>;=>lR7giZ)@=!-!clq1JKVrEcRiLtIdo79T+R9Th*1!%WA~NE+&$ zSUt@ROm^2Nc(-QM!=Us8{*(N566zybg>#Cy>e8DRcn;fV$%yiMR?3L{be0to>7}k- zS|_;zbZHHem9n&{3LbR!5cvQS4tthQ0exi3G#P9U#RCq}Tzaf$L=A5lV3*aTO9U1jYo`6aOs zLh84~{{YI`-sbpi;|;~rK#-1uujH6~o9D#4J_~l>-w$v${Ab~Nr}Xj(P$VsRMnaT; zkg3-i`-w_`-6>ab9;2qW7@uzUN$cLb)tBSYec#KX=RAwu(qj5ta|AFAG|->={y%!> zE8$Y)DooXxQeIJ0k2p(;Qi-V~s-~x12hnQ<09b>0eDU-l<_9VfQ01y|q*QN%SM(r(0_0qpDUl@prF}k?^wOL_ z38<<~RYGIN;Qg*3xi87{f?2Wc(Dzqf#h(emdrSro`iK+j|y(^>Tu zYVtaON~o4<(u?VfBk^nPna=+JY>c0syiu1PCzZZCW6`H`1{-|3y9(!UjL-&g{j=&% zx@Z=t%WWkjj@r=cklE$3apZ(^wykl{u92I;VW!4<)^DTPsRW}|R z;7XR;5+*4sAQZ6Li-ddzq2(?lf(Jc-l}_hiD|9iS^e`ih*;-H5+JDY0%)2Ax4tfK9nA(Pm@&1axxhs zww}6Iq?s>62CMZAF|jaB6Q3Hq-Eo`Et0A(!n0JdK=#y-crdg1d-fwiMw;>B{V0;vk zK3YrS+=$3qs9rlE`EsUBmgl=FbI!jhz?2{<*PJe_grEeZ6)4aE0QAzM&{0IXCB~?6 zr=yWZ0*l=hR8kRykQ9(eO?FL9Dt-e;x{0b3vc}?_y*+f}D56MpP6%fEN? zZ9t_&5Tu{uzN22_W4LD5^iJ92=eK_7%@5+H+$WQ8n?t!=u14W&UnpGiC^DnlE^a*S zvHMK@1~y-)ij_p#ZKW& zN1toDL*#$nI=NX29%S0ww-o&i{@3*-(5=dWr8Q#&S4?)N%B!WD9VokfE}YeGbEya$H+xh0!=@SE@LcWWhToZnR%l zo;P!zALS2*SOyuFVHP~5dki|(Rr|Q8GQWO>{Ywr3+>ol*UJJ-s%Y(^~(rH!h_CNjh zIkNYnjFT=^CV}ufmqL#F{h3!-IcJ4kMb*AB0qCA0` zPOF)FnQHsYV{GFT0aD+)uR7w_jur7Mig76ISYch70rV~t%DN>N) zxA=#_N`IUa^VZ3)8;YZA ze6&ftiEe3c$H}bVs@ruyaVi5~r3#U@jYSSU)NvAOlmZ;skJVna(;LcgH5q+rrpBjz zw1828M4qrhO-6zU-M(6xaYzd7L!Zm@&?wmSvfSFcPVL1gG))Rvc`D#>&kNl%Zw`FL z@>iFm+r1dX#?MnI+qE3O)1khUF zN>VCo^nt#KJ-0<;u$y(i5VO0L<)pCa!E8-^X-p_TRgDm9#Y}C zS9vy^=a6u#%6xHVJe5IaZf6lHksdSd{*r|@3G7*+0Y`q_J|hM*QCcYO^GYaaR6*u9 zj9C8w2H3HTOER}z=6N}ZI^=$JxZ7Wnyi&t>SV>GK+)wnD`AUM6s?|V`kk(jVMAwi- z3e+~jMvB)(L@m}S%t%D35&r;uqCiuQ4(iiVSvyEZ#}y-AFL{1T4&7*aN|zo02!|0R zU5FH=0!EyNX?}^zmln5O0_e|$9wpnYSKMQkxrf+_(`piMoI*kGTka(GrevfI(u5t` zO1ho?8rx$rQ@C#daBg?c?BjpaHRE2iE2i#aTyYyIpO9QG zG$`$9X(4soQCkf^dC2oDhzKLAzA3vdA;%`XFS?k95!m-ltp5PQ1D9JJtH zM6n76DU#eFz{=*x6(z=_B8`^S)gY9bSMb!_PrI(Sc!u^e*LaOWXEE$(xsTVy%-JJ9 z+T%xkzYm(CH$FJz3w7f2j@)pIq#Lz`eV0fk6XdqK)Ab=LN%(>ZsMlkIli0}~;PFqI z;`ba+B?OyY6SsVc{s|cv){{UXd zJ=76Czx~Pnide(^Yv;WBI}z9#v#3~QzwNI&30WmH+kKARWlPL3SM4FL6dwgc{{Y!%D#l9E z8e3kJaM$oZZ<3O&K2P#K1IhQfH;3ZzHz?xR=)~>5)w#m$*ri3c*}Al(1f^~Om8H-Q z^ry_9#;mR*X=vQes!V*>C2cf}lpYA&8;Hfrk2r>uqa`$6dF4ate`E!m>Hc_AWyd%%BE`=haG9e<662G5*tK<_;Qi z)oQuhN}9~A4khHyx-{5JPIky2(+chCIvQ#W+*;wDXdJW_9^qNsx6{3jo(b>7T&cl0 z+`FBx5#*eM8OvU^$o~M?t{59t$`W#9l@^yju_3h}bxLRmsTJro2J;1bB{z!rtL!f< z#X=s)oyQqW}C6Y>z2>>M@ z5(cj(!AEZgtQ)8cHEYXey}WItkE2{^Rn<#>2k|clamsiu>2H^mayL7`b{=J8%8?xw zYc?+}LAnqHA3k zaRQ$;5n1>GPH|(+vZ&lOi)S|+{_rk*L&cM?Ejcp~#SZ^u9;;Wo>Cic4rimAF@v6mXi=3ORb4zR?V zC%7DafXI0%QjnhDk_P)~<{aPEX&j_fERFXHIQsgqf?9g`*SAFN#-{MbQ*poFGl)Es zed2i5c)#D{LwajhK0Pb%aiupNZ)&74PxSl2Q0)~>M&TEqT-ZT{;JfisF;Zg9!@YQ2 z>C(Ljr}IB@a_0Uv@Xse=cq1M?V^#~lD`mD}D%Q=rA#CH9crlo9av!=xeMRJ4%2GR$ z*K&L7vD8clE6G|p#`2;+l9$RoyTWB-Z~F4lDHRkteqY5?yWa?1?HI2X-TcjA9z5`T zxD#RET=iyQGo(#ZWG$FiNVF7lv?(cBKtD+v9cyekSecG>uBWc65tjD$SY?JQ(MIaf zS}RgD_>-U~w?rOw;)jh)@%`^3@q3-;a!zf1cH0Y#ar~|vYcsi6x-GPKS>yXnxZ{AR zfK)p`8qoF^+RPI^v56Mp*qDY&{TvtBH z7RUQXGFj*>T$#3_dWu*xR|#$gLxdU?ntb)IvgLUBX|>?hV{*Jo{xa`UH#7nE=}?St zuadmC<5lKm_lhr~BRLdzn#pkq-SY9b*Rd4EQM35NFu)tVL3;%7P&SDwz0px7VCg}?ULiOJ{J)*1N4KT z`0K3)&+%59Bm1v46OHrhE>Fi-4nBVJuI>sUie2P#1trk~k+;^z@S4@G}J?vobQFj2+eP>LN7+q#jVebso6Fozu()#Z$~{HII| zR}#0fZO>!jwNVpQB}t>oSlobir_WVR_KPPOI~JnX_alaqFlHK`hed0++si)`FRPg5 zd&v04UurE%V-zko)Mjx3A z=g;nYamMj$b_;dl+kst&IC+MTC1~?4l|_>0Ng(ZLiB9 zG2CeW)n0V>TaH`aUN+{`))CilPxw`9j3dHpAA>o_!aRwI4m^`h%{q-)WS(B{5V{8) zw~8BfhK*8BH*$0?Kf*5;RXf=vvat!UswlWpQYoz7QgFsCUKbv2E-BbAoBl${oyc;j zlVr*=l%=h5S5j2tsx{o}oZQY_{TF@2Su!@!PyRLFR>{{J-zQ~hNyIp=GA{C?x`IW@ zU=1>KLf&+4^)b}v=#WNQD>qsj9tnc`2} zGlP6j@z*THu9$?We3>b!ii3>135gbK%(qGQ7URXWsgRVA72Ij?*E;3?-(~VyrpvsD z!Cec3`<;Zw;GJ&ff0&O2#m+XvFWy0OeZLooT7!1vi?qk2OhESHL2g5i&wasK_B7Hv zf<6=jUW&4BqZ?Rz1?8-r)1;Bl=t-)u9#wK(w<5{x*ajG|*BtRg2>A9F86Tx=K^>02 zwJKh46W!E+4x~*fD?e)EA!Ty5sTk$UE;7Dk2UFDFb;#NV!eQ5f4Ea0w+N44{y8d0Nfqm= z&-9XR*P_s~ISZSoRIY_uFnI1NBZA?PtQ7v_f!M~>`fKa+)R#*gyC~kiDWM#fE~~U^ zjb_b}K0}gZxLtYG6)j}cl69Y*&T*+LM+8k^pa_@K+^YP(I#Ee_-G8L$l2KloQ56YG zU;Xp^bl0Smy!OHM(q7sjy=taG5dLWV;{wCRppN*YgKiuKR6yqk^QLe}1a>JS@Kr%Kb|+^P@vYqU=AqsXWheh<%a ztYbaI<*_js)t(4rEW>BL7|Ld2cNU#LL&$P~c2I&r?nxtCpKsZ9oy7*uY)IFZRbl{!3VcTC1x5gC^%G(@QkA<+q@P@>vY zKv!)~sXFSotW;K!dQplrE=7~gzV2fy&0JSazS-Ce4n4(rr^@_+0gHDXTHLqeGTanx zcL_-DDF^*jAPQ=ekWE(z!nwh;J%#<3I`iz#Zhe~MneEl%A?a^8<7H%>*Husx@T~@? zLmt+J8u8VBsuQ0m%YDU>y>>qnsf9!iH9<0kI;PN(RVaW5sV8j|^3kTL%V^8o0+VuP z{{ZRB9j6;@fAb3;xVq4qscRKYe*voOSoIuSRmNC|qS&M68uu2ObthT?G+9rIRGja& z+F1vuO|>ntQ$OCn zEhXf9ifpvn#?o5P>Zp2NAaxuh4lzf{wK+nsW#k-+T5VxJ5b_) zDoG>LT~CMmlX-}c7~E*Q`|fwKIF3gHn{dEfM^(m8BmUfPz%#DcgkRmm?kDziQi?*W zN)RirYH)viZ11B2_S}U?h}fSJl}pVKy}n>;D|vyIGoYwi++R7hnC*Gm?*gMKQDw$a zL#(Z(j-Y}m{B=n*FKXP|+{W=xL0ZST%Z9GzUMMx+e@!;YoYdr&vA?KJh`C!b6Ybns z4i1HWwA04nuIj60#W|X=p`_cOU0NxHFrg|R@CoVpS4e%MPedZRk~8KBy8bOLEf(5) z!gt$$^3+#a7L`6cyB(oc+kkw5xN)-v#c__6Tb&d`g&4YTfKBrI+jf04{Mog57h>16hTJS`xP<4ctNsYre#4j_Zv70CO}& zhxa3S43JeviO9GEmYXfzA-83_z12#iby56=s9zPtF7XSX!23&)TF|KHD)B}%)|E2} zyR$8#3XsQeDyLDsbnJNzbxlu-BlPrDV=3ai&kj0Rxjh5h{YVNl{K3)*q0vEn3@XXteph6c($su`0_MU;YSl_ApZMx@ za}m{6+~S(xteuOQIJ-Y(F{h!pvYixASP3KLRcccwS-Xr|+0i9r{1u4hOfF4|OGQp@ zlq7<8A1yf~LBT?a^F;3IsOFwMon`rA1}LZC_Z3zf0O zw<;>xDA?5|mDO~X*1~YKhLOpD@lh z%CniR#V7yWZI-~ln&!{D0@v@h!-S_M#%}FomDkZjYe=qVBrp$Y5$V7uu z>)pu|=uJ+gw(c18Rn3NZ4|Qa)p9EZzS)HBT(u8cMm_n4A4~F`QK2)6=r(5E+tCgg& z@y`Ho^gnd58B*hbRO_9pSs;8)tE6=D^jGX{yg#Wib6+oTtZlX1@rVt+vk^8@Iv{IiMYJse9K4P@fF@?TRpy^iY-1k*3`!Nxk zJFPv`+g9RZKpGW8t(ZK{DJ{#6BV;8&39>y!dx59oH8ML%Wa^;5Eo~1?N=2Q?i*l@6 zZOLIYAud*@{hE}_5IU7ncgx8XZi$yF^LMzCr{pe*{!mRdb8U=`rDg83a!cJ8L9p3bE@-FmhCg!w;^?5x9eWkQ~@93 zrrt?Gt_4S#4mBS3^rwm_sf3jxv;dx;!%`r)BdTs#Dl}0I{{V?wR7J*VuS;sT zdhRt~>!v-FJ=Hw1J~sl?XPrOx(ahN{H<)r&#aT7-(Pg*TtrsBEO@Htv)RiJDN)eKf zN&?T+MNh7xAVF1Dk<;U#0@}m}P8@J=HE>9@~gr6NPFOfburN&TE5X{Ecf!aoBBy zTvUNdrBtCc-)ifnbKGlgY}s<(4ObfWt2S(}-(xO>9Tf#BH7V=z*B5xvdV(^QVz!x4 zUtvS~jeSiIz-kZa=&YmEB!*pFX((|&N{~elz=Ng7<^(4pd28yVyuvCjcB^Je%0i3j z_U44|M-n}M9YJM7sIBr5Z(wjyh}fvp<_?~ssOYEuJ&@i-k6kgBmsxD2zXYDO3ET?& zX{e4HU-McUR|z7gaIO|56&%CB((E@9nHeSkqLxsxRjx8p5o{VG2lA*`a3zz9*hN4*3e z%xXg%qPe=G_IZK~#WL^z02@jjj)U-5uA|J_gya$w9m+DI%Xy+cl%J&hMv1QSq)i

    MM^L+hC4a4 z1terZ=&$<&@bW$k+dyYp!*iqg!{r&J8&331NLt#pT{EPLKH33ss#2=pr$1-cOgu_A zz@=Q!u90I9e!;MkT^l5^t@T=_VERv;GOt^9b$nH>8Z2JjM4q9G^0>05Hui*+*})=x z9oza(97*Zx*%Tbn*9>TNop&^nU;Fh>r}9HX!yAZliFc%YqYGJPRbBQjR`_ zBHs5k`LeHMH%Y-y71lp_HYwUNB5hKCb)ME!KAoe>Z7^oZjr*1JU?~sLJHb=2eP75l zUh!Fs>B(qO!F~II0pXX!TAXN0mi4>V>9s%Ck)e*4p&pf*oA3PhC|&uDe!m*Qj|opI zXS(23zP`60@RO?*hML|Z!72sERHj<_KH0Ju?o3Gb=N?X|!2~6$BNzAwaD~fsRUNgw z%Fv3TrXK0;7OA+6IxFIsx-_s^+^V(rO^*~jK?I7Kn;t%of7!+|;|(54CE1o^u6Mrc z9Mg|+z-Q%;p+9R+SlD{8{~0}xIYd*V-mA-)lfCWCBmB%*^mT)bwt-U$ zCYu)gYJ3xi(vEjR;dhDiQps>#bMPxlLl)^Y!tYpeJkNhGDipiW{Nz<2u=s3eR}h&W zyM~hN4ogeoQ#hQc$}g*00a`nyZIZpA?Ah>QJfPDCxo`G19KXtyBC@bDQNkgK?Pke~ zz6caf4w9geID9g__XJzmgZDCVr+^cxdcIS%X3t*iD0t{{Y@UJo$X?K}hs$$E9P=h} zL8`O`j7GfnFn5wEahOIxi(2nw!j%Y0x$U1tNX%yXUljS!I5d{lfTiM`q3ka=#CWr2 zmV5Xij(w+VtHC*KOEV2XDqG`mk3t>3JBzKf0}e#m7udmsU{9Zv{38G~sfk8dJp$gR z3`ybWmIF&HUwXDf>Fv#4Nuk2hg9_KmszUWK-s2BN;(VMlp(wP$ zBj968xXJKE;rEykiiXsUCGM3h;ik5DdlKCC&v%p&8%Ork<-V@+3zNTke;IZ+tUBi1 z$ya-q9hxOE-wGvrzB(Ej<=%YFU&a2>q4qBVX=T?tmXn5^tlH3*$wTeBc6O~u^I9*t z#Zv1Kh!z%6`LYUjMQe&Ot&Npr0d2QHFUkD!NfGn5xzTG&f*5eF1AtOU&9^x2R3WKJ zQ3=2Ngvu;m%etIGdLM4?R5<|K4kGeybbDthW!@7e&gh&|TsPmwJ$j=JD%7{RrB+|} z?I!IDxpbn|PE~9Zs_#3%V2SgGH`nF5%>LZR=p@773Y)b)ew!d>qL^tzh2(qWv$mhCW&R+@Yuma`M`H zQ#8e>F@*&tvB!~Vp^`~!$KLfEEpR2$)bek#T}Rwl?8&+G;5F=5tFvI`oTq!ocJt_K zTzMjZr|lXH3+rx7Ox^1QnLDT1=0J9%La(Q-X`S!4d@TCoTxY+<%x8MlsA+`U4Y+hr zVYQmP#`s0hijREuA+xh^vtg#-oEg+Q=0ie3Kci8U81Qb>H#?p~*B}QWW4wW{yObr6 z;M!a7O_ET9FcIc0X$>ObLEjugj&P0+s3x)W%OSqAYYo=fj{v(3WnRtdq9X0O?NRoD zfW$TU5tu~Wua`AlsVX(fW8Iv{clCt}) z;Wi{VvM|7Y+}r0|4h}*=fn;_zU-!!3LT~-#;X@0a$zdE)*6^{}H$&5p02V(Py3K4A z@&Kkbuk0uFoZ95xCwW%0~HsK}#@T?c|=8tshbMXr1b|oGGuxT$F zVxo-}#hUossdK5)nBpjc4UPJE+j#XpGW^b>`UyKdGfk#C84t>&Qv;gB)S}wg2 zrZpPZ_;37pm&wtEk~&(i`_{gvG{QlK^L;lGgbH(HaVuJ+C=NK^qLJd6pM_o%KMqIM zMWv(AlECvMvX?eYR}}`(2r3cq015`-+)_YpEJ}IUmQ-t*>Ci~t4upEgts`_LbuTx4 z_FlXYCv0HK=U}GXf;%9Dy&xD1EBW}pddDQf9Xir{gF}LvU3W=MTMsx07jAz8T9*LZ znQ7biS1iQ(+`<u5%qrr&atHW{!>F)|WgAYL(iZ39%il3?*ap``My*)lb`Cq1|Bm!F+Sgvz zg`u~9xY51LJ@_bCMRlyBFe~X&dQuheE3;+Vh+2x^@X%@T?{I6B3y1mZBWEl`iB`37 zgmk1B*IHg~Wai${@1E#zjH`c|x|}&!zSPM$OELCHrnsoF(&fRGE{FvG zst*{n?6_YAP@A4ezqC62zNl2#ekmh(G8T~WNAUb3Tb!l<>EHMy+v$bOx^@r<#76-F zG3hVWGb6biV9Z|M4@sDL?ReR7q759Br{2|Z;w=w9L#$SZ2M<9og97t{z)R9?>S@R$ z;E7)c`E#>V?W?(N7umD5u+>xE%osqd2y;GYgO6F%L&(sdUa`NanN4=LxUc@p9Dx&M zW!U*6puss9mKp~Z9T^fLlwf;)zyF;>nyRXgZqQxG#PQk+>Xtz^o0g`DZ?>$FkKw`F0A)K3_!3_LC&A&7wv|z z$SuvSzNiSM9jy2Kre0icyHx`XZ}@!PoAP~J3fvkKKl0s+>*L`aMUyY?NZpeoG%b(U zG+E_D+7~pBcgz5}&%T2RQm_-gCGacvgxN-hjoZuqhK0Hjx^E@#8H-~vFMnW!>rX>F zj4l)^2UXh1JZRBOpXS}n&KEV$4oCON(_|b0Gk_Q!Fx3n`X}XJMuW$>m?X5ZWNWp4P z+(FsFVo>h3TlJnC+836z;o5M7mtM()-f2$$F(#bg3}wjbjHWwukeK!;MjJ6Am>~K$ zB7KDP0zy`ysy80m6&>Y%Ho%8d6tUXy$^z4piXGZu$9a3Yw+W})+B||<%?LmEO)6m^ zmC?xs%ZswS2LvWh7(aQT60^DgNDNroj%m;dj%J{Qi?)ebm{3YnsO~hBi-Axcc|OipDn^&wXjW8Y=OWO}UZKd;&7dFtHg2mpjlLn2(F+k13KT?CjS= zWM{}uyQh#@c}-ZHP(EOB=6pnnp4|#t)*cZ#it(3h=iAjE+qn@AqlCx4H zmcYkzSK2xIblpFe3ND(_M4GBP%N41wDh zZ0w$wO-*75FVd!Z+rOb;dn~&Xdm5=A&r&c&HO;a3&94F zZG@jyT`5~BMs+T~51wEs&rDCS+F8ob_=7T#;_TJ-)N&^#aY?}5h)ma>_9Ex3YEUpcM+_5cIL;FR!nR?X(dTETyVke~G1Pri z#$p)f1vvS)xjMO%Prwao5(io7gBr?#rKB5=N}YLG_Ko*^RqF=%@d6Qf(NW=?iyh+t zfDPbTr_ToiWZ7-DVDm$f#-U@Fx0Av-Ch4z%{CoDn$UMWl{y$0E^=}B*SGC%3MR6o? zYmX67-&v52X%MN)tzgEifBvSxmnIZ%A+KSyEIq1Fzbq>TJ0=OfBl_jMCA;vuPQRoWIEG_zvU&{KhR!h&e)wSeCqw(TB@w^g@!@AAPMCNFrwd0CVIBr zOZPaH(I}kuh{5+%@KgkL^rYH_mlmH(dmpqYE6nhz$>#gop#=mQ?nQZ8`11~*Qd(N5 z1O-{-?ZU9zZ_)3;fx3rYSrbmNZ64(rw3M5xutPeStZ}(gI=E04sjIUS>ES8j%GHR~ zR8{Q4%aUt{FnZR1p2^zCyF{$R|Bl=IrYpw91{697Bfbi2*CQ)V$fXO&=rW#U0&URl&CPTumf*fnt@@0>+P-JH|mMO|+A%eTiYg2$yhg$@wYgB=(Qpm3kN&~t-`-c5~Q5(T9hj(i32z^ga?DTiI-W^v^s3w zi?dIW51HwGPmpIKgU)8n_%Mfuq>dJlK(iYh!+CSbF@`)PRpo~H>OFe8_o|7Ig14PH zQ_^aPT$u82hE}q3-W}rpp6LxmaeX^TPlSb#nq0>oF~#>esr=68Is*j8DjInsICx!X z*+A0oJNlAww4)b1<2!kIPH|JsX-xK?WAU#;>Xix-7}$yk`WN@BwW>g6VGNRuw|%iZ z)vIfz4dx|?45mJ*+YNg5x?uWSWhP{DXG~s zA3BothlgkmN9d`sidx(1hnd;XMS(H?0X^jAyw@XOZ*B$;eZe`5i10lWDL)k`XBc@o z*HDU%9v2Bw_0D{4qHqdN{uCTFnUHNFn{$G^%CPpq%)rNmToB zP+9D*(=9vwbnmU-Eu*WLL8JjR({5BLn_KXnLkUL|%FkWJZsi?cUu{#Jwj2gma~GUz z657`wM{QD_uA}BkjfQkfDI%JZq+bH#*W`H_%Gw@R9v@q+vXG+w-~!FSd`bD(p|L-@iw?3b>ULu1GLTU@ zmlHisO_xRWAQz-SV{xd7U08iIJEa1Nf6t=CKxVqQOYs?_zz6l|vPGqLz~xKkxsMBV zJ>yBu%>@ln8~puLZ3@EO2}{G>KrAQxa6Re^Xh7OT_ucEN)W{5efa7-0upc`6kg zdUGpxbR;MZzIY~uHuTr|RYThNLIE;YvZuH0$)?x9ma<;_Rr3cP*sF zEe(#gzUs_lWW(JkX1a6-i(`MbCX|MkebtbQwBPBwfL{KM zaKAO$xQFx9We)v-_shR{1aMAM>2pefX=~S7z7zeubvn%Kx}ohqm2QYjLXBE{k&|f$ zYKuq)WSswgpuemL^jpP8KwZUkBTfKU9cCHp>YdUp?`ba4 zBcK{v8zc5@8B-&#yoxovVA61G?Q(9t$sarC7F9_(_dw-zKVR3p8#=OZbj?*S_J<<> zBc7)qLUaESkUsPXXd#}lIvMqM={BlHs_6USU)8IyH*dGdzMB=F=(q7-(CBK}`FOV+ z`3L|bjprW$dG@9`mN#`<(2Ee*O>f%1X1Jz1 zX8+?KYKZsQ!V`SN#FBnfXb}Yr!@Sp~IpTZ-oX;%0e$N^@9vVc?;V2zr~03myRvndTT1{MN}s)pO3h z09!|gp?}xli0Z2lL}w#p$U}+eyM42*@w&9E`~IG&y>BnjE+JlPxAL_=(=CI~*ros% zX_mz%IThod)(E9mgq5+JxzO9~FH^LZ}0ILITN z`htVN6B8DZov*^KSC3=DsD~oRS43}E-VL=snZeY^ul+u}7cF%MGiFzP-i#g3GZ3PjX^hba>;UN;-=k~e!{eVmj(!f8lRft3@U-k<9> zZsX`6&4)(K5pIHGB5cVu4=4BBd*VAKj{ussh-8tWU?!ksmUq4*&s(oKzCIm&DaN9< zO_HwVLs7cyFDvK+rsa#}y8nE^$mloV2?5Rw8HQLqe)*73{L6|DcEal+FU2nL5c^2f zS%&*d(kK-q1RmI?*|eoBD%NcQMR=X^tq<$2UM^E@a2~Ye)3wJ{n+6wuHA3R*P~WiK zrqONrQVo)}Bnw2OfW8NaX>V|wb~lamLRL|};*5H}ywkR!Y0eg9@w}d&tYKVge9H`d z!YM2i%7>P%vy+2M4-HRFV(}ZyZ=@#!4t?1-j0^&zykofjyI>L>;2vk#fKe3YAkQ3Lc-ZYzJmK)3HId6IT1!HW_aNE`CuJPs!MJ!d~Idov1ySh^=*< zwQ-@1PbJq)eR?_qXMOZWVELuELVmsGr`+_d`!P!fo`kmQHjs?7|>j8s>xy&=bC zXQi0fwx#_|X!fvprHB0wc&YxO9o-TC$k5W^eyx(nY!W7^dhlDR$c1_oyWf>bn&Vbh zGe$F+S*CQWDBSQG%3b>sViJ`F*qtnQ3YJVf(n_X77eI70bcy4)(t-||pC^F0wnR!eE-{ zL;Z^kYvw@2SKwUZE!V z?ZudKCr#r(|9lb3-VCW`%25B2Eo^Y|0r}O417#ofXT*;k_#nVH?={d?_06hrJCl-{ z17b2W*#BMVUDejs)}{i)SAFZA2^}NXnmLHUPIM)9Ha~gS##Rdc(AXTkUvp^hXr4Cr z1j%SeQreOLW{*!VmpknG5|}qZmI67WNKfo|1SFgYC(zvCdztiFn?qKhnKees|3w%n z*uo!HDAo}YS`mLIRHVl@jRtQ^EO3J}FVuDz5*qju%x`R=mHuRLCqHkfJ0U|)K)UFqRd=SeY> zvHlroWUaD>53z#BQ;Qpdh24_ix@*F%x3tY8^t%bJMDmP%_>2pgTeR2Ml2>g6Za8E? zcGjcOz?NbiNaEaridt){;>C5-G6p|M@G27G0Vj8=;;yj=!y{|wDx2XJ9LB~*bWkT- z0O-6>a@Fd_?vSk~UAdHJwzSBHwN$si0b*C)lue&iC93Xs0OS|5y=v$PVxd~A+&&hfOh&R@Z*d}f}{iywePs-qB^Xy1B1Nd5_ zMstR`bS8ClEULnC%lag1KmA(DxsG>%9sN400-txThl4*M)pbeyPm7VLY-vYdguAFH z+{8J6`e498FW7!|)=H3#-qhtVp7Sd>qP#lE_P|7VXt+r@bo$G&LkXGZUVhr;9OH?{ zD8A#W38a?5NRmOcFNL%ArzQ!3lE~BZCEdu})YtM_%y-cy3|8z2`8^w07Le^vWBlac z$vB)ffL(QJT0k{ZJQdvH99gKbEfS-?7>%lQOUDVJVK3|$5A}-Gkr%oU&|!=WB9?t) z9A33{g|0?R$ z+heCATrcm)?j(zI%9_lEEaeUD8?CdqY$%Ud3LR(OoI(ej{;tkUQ&AG5LMZ#B+-Hhl zgD$_YKAVvWKXE=Oitr3I?#ejCj=-e;xMwMLX~b&!H+>mORg_7{r#=mj9wh3;R(w72 zF{o|Ouo9RtIU?+<^=BF$IjYDc`%d5MvlahLO7z519GW}z2KtAqo6{cS%;4k_3e|vR zDHH8Ap#E)+r&)3~eW(T(v}D)9#Ac$RNK56K-L(=nTcBP<%|xFEHLN((qD0I32Gi_7 ze&^8u{O8)Lv>A$HJ=!Dn-0C#`1i59suck|M7vY|_G3j1ICb3;xS5LuXv3Zf1xav&R zpsa@odo|kV+=t$eVwOU0=$;_ue0TCD*pxfrBa$H+!eLC)pO`6L^FCPQp1**=3`R}{YPiOr;aD!Iwp$dQ#iqKXh!BoZRIFBkq^79O+~O=}>)k-J}J&#C?? zG2y12sM?loc)fN1MB`AYh}q>#HmodOhD`7#N{U<}rK?m@RwcDsGoMJ%<#~+VbI%yr z`U&aiLX5S~?BaOlSIitSI;LdbTse-lWu@Es>Pab``@x*8$!AR_*Qfv832yS8JUl8Y%>?r=heDdk-q$}W};Q1DI56s^P})1!1_S#)8hZD z5a+*+|1$v-1?xCPDgD!dpZ_0)k>FQDb%&df1*>}TNDWt(x|5GrjP!Cn@n7ETmmh2!urJnqnl31SS zza@0!A_deeqVNiWG`8{_4}>aW8Ain}g^4Ye?Jwv1&nR?wu5o^9C+U{r4%XA9)BL3M zD`Tf0{7gP#Y>er;Y!#K^`|RFw5)`oI|G}sE>K^iC_)QioA1$5 z&vJ~yr>5S_VaXQ$mbB(&6g^eRaw9wMo1|k?Lmnzug;-MYj9EP@n@0=1I|)Y>NeWV) zX3|GrPR*BY%!9wnoT>BeM+*k<{sWElW+7YnbaZNx$@^Z03Ck6m;1&Ar3&*HK_hNF* zTy@?Pk*YvYp@IRFvz~NxGb=e3XClyFNJF7)t|n3u{Yc|!kD42UsJ-D#xM69NSA>@Y z3?$}kVC8`6tqUq(%||SP|4xp(pBa17#OTmK6musVhDO}2)>dFJh}ED?_^ z(P+17a*C)DO66#=7v6W(9J&dG%sx1HK6^!lwW3Bo=>uYGvy>~tRZOFv1K5u+omFdVS(H zFy$%MHOL6MjLY_S4*I)f6lf(zdF@BQVzRKAn|K!L=%Y0ZcpDfW1wmGW*T%oIpdJBo zWGuw3#o@Pk99b<(27LaF_X_4)H&siu$w9_*koHl?(7=35p;GMGi=N@sKL1+&HlSzf z)q~{%wqTl(^EPi$VP&x@%XQg;v2ieTCmJHoT5y|{;1W$MnW4L*e8?drdo%QaRMk4b7S)Y7QKDH-MQBSjO_?3-Ao4X>M_MATfoUm6{d=%tf( zr;R@X(8O^E#-1!-pS0s^R-lVou)2}BjL$`VW81<8EInk2+>Sn-wG}5(W~*`Qo8jVR zb7+YsyM(|_10SLWG%_eZ10uZ9Tc)6eK+O@z`H?7fjHCI&_LHL)%iuJ%rtyRk3{{ZrU1~68 zlkZ0Pc{{Y}q)%GK`GtG#_$o5bzO)|ta<7qI-JRvTTZgrVg6&H48-0oaS~bsX`V%Q|$_JukmDd%~@oL25qv9_BpcAbN??b10fi|4%x?7hPi8k$WTPquUM zBhE_#dDfpXc)!f_Y%<$E-(`|3u3r(qn45`-v}}xa$m}D{?8K?WYiLg78i}r!LebFP zhl^+W^<*a*X4P7WjNa>bQbs6CVxrKOS#-;XwfuVY0}9y$|GiS)36?{~TL0=7mn+r@ z_F1OqKJ%WTM+RaL*Me!(G40`l{FjYqI?#WW*=Z@>Q!(+Zvn|@N+q>?2)!Xb2(rxIL zh`%vsjw!qj)??#guFpd!d!~Lwf0tzW&~^3srSNQasW5aa-uEICK0p+82(huf+9PgB zO*mrHKNS`xTC| z0Q}pZ8zC;qi}NjkLjvovh68q#juYxE$YorT@qc#%c5eD~8ojRKQ$Wba&GurS;=bY% z*3@XAitDYQ89keOlrgEz}86r*8si_LXSqU?sa+|v5^ay}lt2_ee z7XuE$snMjeWx7x@$CVJzhnlnREqN_1@Zfzl_R^Nd)3mtMnS9$_2h65TZu+@5&% z_naj=p#N_fdlWJHCWaYi(mL6uJ@1R?H`}&7K=fbwe?$s4QFN1Gw=fmk9s9;4Z4}C# ziv{<(%~ssI`AjVu_yW(TSJ@gfh3kA4mD(G5hs?ND0zM_b6cdWq?anC`!UV$lyj6Tl zxk4H4C7Wnx+QFD83<7~=hc=Kq03AM^dkuUirAJgx(8DOc@e`KJpHzX+XQq0`k=5V! z(8Pbr&KVuj9F8H{?C1o&H@1jg8+G5Yp{7h15MMZ|cM-9f@cBI)Pe`P!{Bn)j~yh=#F*X8QJqsOJgF zQ{yi-xmYE+Ja#-pfKvrO7X6#4dBrsx&2O1cWf*MF@Y$B?VsE6pja2F{6)(|4YyFEx z2E(TAwOns_L-;8vBn*Lnott!Q6+Ot~59C30yAPU8gNXP>mug&hAIScE!@)U5l$siR zI`pjG6Lippo-h1>5LB!%;XI7xZ%v+#Who>vc{5@b15WBMkyT$DbLCq_v355Ky6;0) z*)pACoRs^6pM9koWWSHVr6S>uMega6inJ`_OP#zSSZltt&vO*-UB&Q2ov)H{5XUva4J3rTRnEtvnS)2<;nmBkJ`5&-rQ;c;WLdxOCH?G>N(B!aF4RvoxWBU$+>?~R3+D)L)!*{gd<$@k>0=;>Nq<0IFvn}ynL zfoL8$or1bzg>l=)!xZ(c*{%K&wOUrRVEc669rE%{-uhpFJo{AN=DHi**rGgXW*q`c zV>PZoJ-3>pP zrX-PGYVZ*11@4$|TL1~yk#|9@SFzrx^!R;2mOotbZY+f^ZqhFl!*|Thx>&xcwx`hE z#tX2^AcbX_>T7jN$+porl~p<)I%co?WnTODY+JlLM`#15w$Rkd6pyz^-0}fk= z0{@cLzbi*R{>%u=kw*aQ zGg-f~u1-j)*7ywTK-*-(nXm+9$CH&xnZeS^W%oVxjSMWZ7PJwz%lMfmO7HblICXfr zCGPVI(3R&E=b~QMetPNvfn+Cgn@s2H`x%@%(c>PG4+v%HPB&tqW5)!GzJr!zAsM)n z*V#=54OmuEoL9ES%%c#DLu>^O_I~DMKe9P20QWB^=<}T4Wph;}YlugRhVWB=URp(= zu^oB)N7o?d>JxG0krXMWQ1b3z2uKCD)3?2e-yQCGN=rOv@Nmf=gM@RF7e3j0l5335 zA+!lqn4HDEyc@Ir^+NwLwK@|5$1(*iAr}q3TO7ImS(=7;9~rNFS9X(H!f%?v9+X6A zl%5;tl)fR+zC7>V3E@zo-a>VKBX7J+;OLTmQaCi_auC?Bt)o-=p( zWiEGTDXUjst<>tB{CjT{a!q^ZGl`KrdllLB9wc9i-S+CmhW0`9?8Wg+Zq<#Gn;qaJ zcSc8>$b*4AvT+**Z3-AklcE`|a^aptf%&+pKenj1h3ro zZ4{uUo|2`Km{B3rCpKS_=Xst4P58C{8+p>)c}mH_=|Kxq72YI5A^}>BvhJ{ue&zq* ziB>{X;j_)B*#FRbTBAC!^IhKmj^Y0|Rd`+jLX~41@&nvTVF$6*>LD=UsrG8y?s%Nr{U`zWwS|rMRNz;a1bw z3VESGT5>36!)$2nY?SfrAKgX0?;}8jzkfoyTR-hiysYpQ!S*F9nvci5^h|@6*>^H9 zlsfQvAnE6lv{8c@94;GoZ21s3^?#NG+BS~Dp-^`og{KbxTYFy_6<4<`*ikG%2q6TA z;GW>#0fGk_cR~_e8iF(qO@ak?2<|izB)EnU+}$m>H|{OdeD}S3-^`n}=FQBSAG6l? zrw*LjCA)T2?W%nahYE5Om~dZfofx7p&G4dOr&@(!RfI%90k+5fCKDXi{l)csxFN8p zu|_w%Euj~_d<0?rk;2wfu+Hi37OU@G;^dc^T^MylcHVXGxsi5F{Qc;SrVSOEafrh) z6>b{*4?*CMmtMc1;t*7|^82D4XZo0txEij_IgQo4ntmf_Cy2tIYwKR?XmLRI5g$Z; z|AWQ&5&_4Yu=mxEBMJ>1h?TczeEdj7N7+MH-`UfL&A7+HZdKw?8n3=-0`kRacIgK- zLv;-a_l{>NsMzQ7ljAp!9nDCyMSrBHDfyg?piuKVDh)#)4qhrZI=DKm=7~1N3--?Y zjXdM*kQk%ew};tjoRSLT;SX?3y%fXicLyM9>c&YewHuq(x6fqH%&~t{XV>MtKO{L9 znPacd?ziBn`E7Y%J)Z5^aD!+G3KtT1K;=BZTosS=_AsP{G?>OgQg4CKXwN0k{uU*I}lhC zWSYgpX;JB{FXeN1=z9Sv(FyJQZ4jvy)ZH5=a1VUkDUDD0T*>gWS^s;%4ef@{b1LcV z-RnoJ&4fll>7!Z#Wf4g8>+BVq&<|C@T$Z02#Fq1-msqO84YD4%H_ltj{Rp&M^Q7OV z*{Kg(&}jrlTtx-J4fjUnypy?*(+&VZ{Zao?8PqF2gTSe*-2nJOdyU(I`T2m2b1MV! z)Uu~h0VjQFX#e`^p82Izgujx}^83r&2m2rFYvnvinf8*m%s&;XmoErt2$uqQs}Otu zodK}_oE*9K!rQFCemTePj72<}<<+dM(wR{6q&BJ1wIS_PvQ|Bl&ua@q)L5TFCEZQ@ zflsF%kG9;iS(TZteAtlFZ~Qy0-{2yiP9(jmWC^507jTp3?qH^us=fF=q?KCi?I2;`)>B)dg#%_$_+1)QT|)=Rj$`0#!7sdx$A83Pf1Rxs&F(jAeJu-E)nILG&K>U6nvi8Za%MBhpk=yK250KD9jVB5)> z61@!!6hZ^WTWCz(hbH*RvT=|TKIpFi2Trl{au zCA0maUN1XZ!e>-AcMai!s-yC2T42;~7kLGl@3P1p!L~W*e&#OOKeM z#ZIdRD%-b(DzlE7V745UL&QKG508>{*8t`3=8i#eke2bUXp2rRggq17fK<3vFy^P= zrEAnud!ZbGIyrdTyj*$Xcx^EHITYEW&HRZ}Cj=RWiY`g$NLALuebIa%7+a4+; z>Af=RA>>#+j&3eDtW!Y)hNvL?+Fru+wyAnMOW8iUb7FMm_D;s@3C3{c?#-U-N(#dh zD(4SQ#gcFmBOhLg^+KJhqo3g8d8fUCi4JCcuBA(g39ND4&S$|Y828AIu#8gmZuO*~ zHmk?c%AjsWzFf#NJLKA`A{Tv*m(YOTqZS9x=xLAn zGo+*F!r*h!_t&U{OL;hr^R6KpP~f^&Sbx*Qy=^WCmzxh(GS-r4G#nG3DiWpenLU4l za3*n`QLP(0;ola4`*J9IUtS2+p&1w}n0mJK5%DV$pp#sDV;hT_qLuNXJ>`~5H zW+ob0Z{}ybW54`bE3KVe{?Yax{wG1Sueh<`y^V>dl94ohV3h=U={f zB9u0+wNB~E59;_ZAf%20gv?Q6EW+%CjLt)OqNyNz z-$HHh+;|?d)IfbwN0 zavH5GO7hL#>|S5pOFl?e%AxHl`?{sM_)2I<7*;?)`SR25wqJHmhCfTtRN{qrTb2q_jDcX>Ad3L-QQe}zwA+4tL&ae?Es7o* zVbN_0#|K;LKlJ^+eyAQctE|qIp1A$WLV#84e0OIoYZbLTPcUfaC~U4T7ioqli>^@% z;h|Ltr|uYSU)5AElx7Cjm6w*ib}E1zT_8hSY{(egAC*eNVCr$Zqsr0=XaEjnkcaXa zqs32=z%4Pzp@ypLKtD|4u-Ee7Yi*_h{XL4WTxnfdv?b|J_F;`9T-kYG*mVD z#DdguqL3&yQ9gD>h{$cl{V1}{a|*A#9*eBODPgI&wv)%iiqziisw{lu%SL*31iN#| zmQuS})=$CvqCw82q zFPA%gw$D(~#MZHl4H5HolThQ|9XvotDjTSKAYM{n1JTZD;xz@8I$=kbQ z^I9x!{0Z$0$7Z5;vzPX&v?-}@$!I*Ag60dupJ|}!8r5jR4PiwVN-`>CBgwT|hWA^h z68Gad^Rew6T#c)3KJnZTYmq?iqGdY zY2B;<$Ue<%C5J_K?<$(n%m&D&2pK{>(f!}8vJBqy%Thb3-h8&F zFEu#e{AkY4m9I2y7+mi{u-0i8UY}B5-v5!Bq0*BPYUx^?I~wqbx9)O2oTb^Kv}-L@ zJx72jy+&M7=9_Se_XP)!0O;HFJ*d;W9hAPB18m(^kJCn@*dj57ouXyRzNqBtSTAmE z+crBm^VwXvtOSwdMin>P`!4O2^Mpih2bZ+Epb1hT2gFU_G3S+QqR+BTiYL|BPpcAV z+GLFbO?PTSa%HzqbJ8!@OI<1{?391)FQhcj-_*dp@;|yh>0wMdcr;mMd8qyAlbIa_ zeKwovF_)1d)ug78!B_IpBeG&P2#L;WVAk-a5@S!2YozPkIb6WT;>)=rkSTsgUZiO1 zXvA({hiBKG;q685rDqa-3%&IN@dj?u=&q^q)8~58a!SgZd>S>|Lr$;)@w21dmAXWs zT8(A`#WcLL#-&f@MvuPi)vnis;}5H+^Bca6VB*{IptRG5?3k7U``0)Q3=-2n;16>C zRo^VYsrUQfERA2X)0SKlV!nB_(|UH?5AG_lFc+7*QL&a@EIfEKKzsa2A=4_*V10 z($Eb)^3t?0R!XtPXW1!WhK3Bvclqv9sr5FKkvs=n$~Gyko$e`N1aoZ)6yK(8|9Iwr z^>f|SjBkRAsb!Myd;}YvI3=DCBB}7?ZNBnn+CZ3l ztON1PfW+!(!2BZ2ogKfr7WSwOOw#B|th>z!vy>tS;%mHPX%)oqpm24u02P z>32I7@OBUs!^7lc)S8ldQ-h5EU?J@b7Rf7 z^@t}<+u`z>hqzBZw(SIxZ*ix-!c7iUdVo*7#=#IhKN-Vr?ARi9zZaMx%fAw-<`v%~ z(Q5*?k*&H22%Z1v2h+fdmRSZjmF6`uxxXzH9gSRr z;hy@F&QEcHQOx$@cFdd?M)Sm8TN`vpX9g}a1RL-0be}SH*dbvNXMMykgFhECyWlyS zcYQ&1e13Vd#ft5gOIQ9LcT5`2OF=jfUB;aC!c6xLAboHX8}cP9r-G-jkIEDrhN1Cf zI$h>Jc;3u7T`C?(*vU7_ndZj@VZRKf5NkS71rDXcVSM$y~Z@oG;0(xl}SIir#-?}^TxWK1rgxV>MH!eQonvU4{oX4Gx!&2zbO~pP{awElnp)Po=n2=-iWS z?#O(w@5YS}EYg&a-lXP1z#5(vmuZza?;AObQ_LHN{&11oUTAbRi&!*kH#czSf}8kD zyi%+a-tfmgE4mdw4Eno9aJLuP@^1o!?-OY0HAL(|Kk|8&@G75wquwo-_;& zMn-b6Up`Lt9#P2Inx?~g>=S`uIk`nQ1-jL1a4Gei08($AX&r&7(j|G%7W)~vEolK& zy?by=c!l)KZYc@U(*luVn~Y9}5(piT#iCPl##mWrQHS=-ecK%I8A}XYTXzX1_NBR& zu|j!AT=)GkbGL{#zJ`)q#E!Nbj%nwn4TE4z8+1gt2m!C!4J1WWiTP$ueRvUCv7LjTv+fp2r%_-Iyg?!bAMTM z+r6aVuidIxUWemDEDw!ThTKpli`5f5>Nn15P6P%EK^P+tt%E}TPxc| zN4<%QNAQT%&+}0PM~SDkm9b+0{*H>jd&Q`5K)%W!FR`aoRei;bxvFaornVxuA`LW- zLqY0ztl2S}Ke9StwT*q*GtZMZzuojY*W6R%z{&58*Kk$n_EC^;d;=##pVS(pG0f5XF#gkk-g6h$U*K+sE7G>~ zy2wVpO|2uonAcfFIm?yMaC#E+{g2g6Xh2{SsT%KbKbHMzv5SeOcD+|EH=oq{-WtF@q_a#m? zWhz)&yFQ64E)ZjpP=Ao86p9#=D#xaS+Z3;lNao03m$?IpCb}};d>#G*vajyyU{R*R zjS+0i#BRT)Bqj8iwzLeGcpvrpF%b?->&bmVk!BHAhWCM>L@kD}TxWHLfBQpCP4}#1 zOfj38)%8g)EBRr1L7ONC5glvF-FxwT?>T&ZIbJ&AKV>0%4ActYy>FkAsYo;nc~bY- zy4-a6;!d+~AaZ~x!qzoXgiYEl*s|5Cim zY zN(ep~Q}O}PxEo6nla>n@53nZ^=|Qn9607E80$0nBvW?&qv64|Yk}{R;J(TOT%+3x| z-qOs8U(e33(zi2^>Yp_|eX_&4Dl^n*xj)^oWk@tH5qH2)RN5;lz|Hrz>s0~c?1WBQ z_ELBjMA!5_*fR6!Sl56P%tYgD+0NSqo>vPaSKdLu*t$u8)&SuC$|sGw&`Jlu$PL z`0#@uUGKEyhM98~4gdHEEpxcE&OAbLQBa5-FUX1gl8|drKUrAunhIPyqAY#~8@)6r zYM{7d)i0{#s%NS)*APO{Nm{Y67az$PRrPsHYn`r3ysNxAA*WdE+s_{hDWOq<+v_!Q zX;p2epXXd7!W8G+lzPI(j6d$pt*TIKJL}PWyw15!UUg3x6Ysd{V=rY~?%N6^v{;aR z?l`&7oEK&_F@ZOt_p38J1o@7;()vbkLm01j#UbH`bztnZ?hIYv)+jZXx%3t6 zF$1bnkDIT3udEqU*H~;Kf_8}TiB0Xx@njs-K<#nvZG6H3l>%BoIb{6$}vFD*W?d7)}QgZE#_d2$pqGLv$2eGo9@#49I>=*ZS;P>d? zeFW&QvwY~6$Quu4HK*e&xj}n_+EqKHMe5BTt);`7BQjS$EK^PyX2hYj zdv4;5KRLO$!kZV>nQNG0LH)mhX;OwS=n+RGT$F1C=MqExAIj|B1Tl(c7}K2>7%j)8 zz2yRvcH+euK$9TVjeW_J7lOuYB>IMA+?$pq8^WJv$dZ}%l8NQsD2rHnus|8*=WJ56 z=svhF&U}wO%dBgZ4VV3Ti7f$BI02RC+ojupk>4jTd)6v>_#!ssb!`qFSX@1*q#4}! z_!h=+fLGGuRQrB`^!&1K^kCdTV8>brcmmQ_4%(D=@*adya7fOLZ9J-i9=+!cvbsZGBPA>kTsFMrz3e{P$H%7 zBFx231KQQYHG8>Bi`u2AC75|fu}DT{Z^dwWPwQP58Ry1s{m6{QkQj`2*`N0iS z(^qVihbof+@-BbqX|mX9eF&4y!_711M#oqoz4bKP4IxB+tL(#&%jl_nqqpyv_{WzQ zH%pbIV8Lf}EzDJBr6zoR)6ZvR8G=7AdA>H%YA+&L?UNY)=oJC4Z)|Fek99%itq;Izm){A*XeJ)3&?H7!2Nsx>-(ADD;w^=K_!PdP|OY|`i( zN+-M*SLij2Mg#1gF}f%dC6ZXVJQZ&ZnM>?H5G@uBg=tI1*7z)x6aCvjUqfo5hQn z3HGm_?ZsBSD%>iS9g)&WVbw6*`a|wB(bCHAmd6A~;Km@m5~@uVuip58Uh8{$w^%Px zFtrdp-R9vhaB3_ZE*Yt^iC_CV{LXdENG$F=>WED%#q}@*)7`gP$2U}1FE5=vSitw{ zava~UgoYI;DGAXl1C9<2NV1#q)Dy+d2#Slu0P5P|PFso+e@5d=)A);$t5lmE;x4)s zUi>`sAj)P_T*ozSiSwLn*U6Ad;f`!pAfKV5Z<4LrDS1(8+S322thUwMUxR9MIyYO- zcE=1BGhGtp#F%=0dNbKeu3#~^GXmRJpS5v47*T)P-onje3fk{&&F)r@J zzAJVp?1|WPE+W&g+&-Ze5jGDD-`ivImI1v{l{B;H~dSB_N18iln*&Sa#d;R)x2f-rVL)f^#2gwV0 z|8iUG4{6$eeMtkdQ(+?#D--uuwe~4TgPBeCwzd&s&=@ST?G^EkQ;ZRkrUP(g_fn{c z_V%Q~XeXY$9K;lr%ToDLn)eHER3Y$lR@K3gg+0kCSI=jKjP?h6dB$NxIs#koEA4ZM4o?ejkmdHK|>c{LGa@m0&h zYP^M_fy9#f0l&n66iigMdBU`Bo^lFz#{(ajBxzvT^N_XCs4S>*Hb>O#>)TR;cXuQg z5I@gUT=X0l)^zsEM6A7@CCv&LB;<8iIV*jWJidS!-qiG+!^wcekQ4fcRe7S+1l3!0 zGtaDD%I!xI&$%InbCrM6BhCGZ$GHe(N%VGK36sA#wHL9cl!-n1mO4It$;JOo_H|(% zdx_T+7aD+v<3cq@ZPI9e*j;wCROEfsaV4prkqiCH(ok zyZYlEKS@4#k-_Q&cz8@+}1T!?roFSc9lHhW)wtfp=Bun;KrM-@{Nxg23!_pNX<# z(@C2*fyUluf&|*4L&@Mpd`l{r{w1rAeC`#F=1-GF!O>X(9E+>(0Nkkp+$X<>!zxom zq?5j0;1JroZr~q{Emk!>v(uB4PPTo6s3m^RPkZUz_qLqP;=~L@+O*!jw(_%TWW8#t zJw9KT{zOI*JmM5vpS-c7FJYpgj5S}425`}&J8Y@Ut#z4jH90- zPGN{Vn%-<^KjR`Kl5XL(F`VHkBAce)RMVszOT84UHs7BEM53B1jHB69ZcwNDc7}ST ztK20KH+>^LbK;YeP+I?~u2+S8w;M`+K zaplgeuG7@P+5DDZeqd^w%<6gyk#K6}j35cz{5HK5-R|hB{|Jh5gOCw zX8H;ZAj!M=Q;@aqKtl6D&4n%4$2Km-5#%Jgk?eTUq9c)?XX!@7RGG}o;i^<(Zqn%2 zs4T7QQFI~H=*%e{7oBkqWSLWTunCHJXduHCQHMWXS4v<^9Xq`D(ZWtYDdGHvInpg* zbVK(#A>?OD!MYmXpB*o8JP!ocRRlh3&|OqA zm}~}Gp9)>}1_|sVm28+(Bd{sErQiMGr)s;_k)!@y*xS>RRa(E$!#S{)ws$A1-G}d8 z*|U*#9y_**?ryWTBzoya9mj#3V2_Qw4DsDuhs);cVrxcWLx!;IYA$4!vTT0yIIE!R zLw`RwvZx)L8}3-AYF?K~eu|2zDabL1S9IEBs&Y&i1{tXQxx=Mv*>UcJ?k2yX5MfotwD?pw8hrag1z75N)VX?$^nJm{xU*Z@ri`&zxcGp$`^<8Z^Af|k zxz>f)zLG^rF^1a{*zh;Z1XTtv`t9B{EQSR{v55_<_C%^!a zrhCL&)brO4iMCqY zpeQJ2;O#hkV`-0{1e}DHsga2_`|0j@9=l92`7aJun z{Z!P|-M=tAyX07}AV-Ab3HIH)JU!nrQ(_TIux4Xq*XlY4`)VWL&CNwK(PIXtKydaWC9S+ZSG4Xn?ywHi^Xxy@$>9<@NE=(K)9MM-I&TMCP>_^(c#_GfJP0ry?}E+cF{M z41PaW@f6~$q1&Cfk*m-LStxYLjI@tw>kN@x>s}Gd=qvFwewSIV=|arMkr14)?(^Qp zaM)AXK>A7>yv#H(vc?eN#}j===d(V9X0UcBBpmPY208N&&-iaQ!T8nvQLd5UC45+W z4#?(K0!*L+*4B=_*0UkoHo@^f%d=LUA{p zMWi-6;a#F98j8q1n|3W0G~i{lZJ+D>g+zYoj`pQqR^EpEifAo#S>l)C>^=zssz5Zw zkbHqmYexe_y8OkU3c_F^25O}D>Q%L_%SXOFA-0X%0_+Y9d8vE%2!>Wk*#yqVg+8u)HtD?b?dLz@}W=qtB7#fi1IK`qmo9jaQ zJ3A8Sjt0!cXZ6vatc_0DFD@KF+;yZ9TnFUd{U3>?(^!iF_Fdp@^0I}X-ta-6Si+W4!ldx}c7S$^zl!rkP> z?_N_EbB3)=OD~B2A|0?l+k+!mWHP5uD{nQ&i#zs*Q5q|vS_@_`?a`P8wH+9+@*i3` zCVL(*k@|$M&u&H9P<%Ce-E>++sNPwSJx2VsgVA75H&tFR@|pL@s~^DTLCdJIx--LV zT}~9FQtl;UsR?{sg9da^QFXq8(-eA=FK*P&b(jBFb%ZUVP(&4aJ@EVM4HG zPEcXdhIv43{1OKw^+%0t#2;$_3Nlj}p4DIft)D(>sE1+nymuprI8PoH(qck3zG2)4 ze!j~bomvEKYo9l<69xecB<78p-1lh)-`8G)xME8{*1c_GU;+o)s{5PYgEc{ZAc=!v=7igisVlVG;-M&t|cVlzC67c{Sa z%E1*0iKvWNQQbG{fwdxOqVo?t8_v{~G?crfbYnb4RpN&>@=pyDTB?qRByH~iA_f&!EnZo^l>)AM3f44^cv(eI4`|_lx zoEDFk@wM)M-VXl9&ky`3>>b=9&z|A^r9Itw6U&2hrsXQv8{$v1qR8MnFcqqOULXPu z_-IEV2mK+4pVKm(5;(H7r`-1ca^zF%zwny3Lkn(+)G#-npIHK23+N#fLnU_SPb%~=@RAn|FheT* ziJb+0r9qIP0Trm+L)o!P$2X6C^u`dz)Q6|`ScIsT zWhg<^vbC2EZ25YiFUGrF)?V)A)TKA{Xz7ZdwB5sp=6m7^P_FBY*mfx|4h4f6hqaJZ`r;VP_Krv8ECP39rB&>*OrxX z%K`|wn+LtX#cVvV8z|wVZlpM9<`R`%Oe50w9Cl^>)A$G@UpP`b1QsyH=7O#fUCH>M0c~fI7aF&eB51&r=Qikg8MADd{(kLQ{ZqNY2NozE~5x9VvlMbq$xsV=93VKnD1~BBICQ(bbg6WY@CoI|E)ZGOh>QED=tAxcc03Ybpa&CAG+h z?R}&iN&$qdY4OA?nthu6xeu^_8q;5&QJ?|o&|@Xw{?e`e2{%C(7|eQ88a?z{RsI;M zKvTho2G~JuFeZWuqlUW9h~!wspriI9+1>NdKsWE@j(;tCeH*}jVue=w|WY$@<91$Q^p1Kg-?8dQ*~k75}L)^?Uc znnHey6~?#u(Yf9i!EG8&`mpa&Ju z3N+vf*mm7@(~R20VErcWho7PWESm1n$7sNk7U^0t8n6QmI*W-+{YyPjLQP)HMBDNm zG~g0b`Fm@(oMV(_zJ%)4B6rb%E)@5;40WODJ)>A-R6X>d2>PStmOmy+nxU7N8s_@z z{+SNFGPHXM8H*9{1`RNQ9%J}_Ljyd}fFm>@^o$62HiZei4Q@2R6l1>;;PetAV;N}< zU2H)>SCDcac3OW;A%ZcXR06yh!GytkG~kv+icWgVKScOVq_koof-v=?2F3!xs8&qK zVM1n?5ru%>U|77F_bE%qBKz)u^)7Ff?;^F#w)RRG5ywwz-4paF3y_TOEr zph&SBXeb(R5HrEOJpes}wxI#v7HD>OjjxSY(12b@;-uJURrwK;+zS{AJxCq{ZYM*p zTb9v)eskcsOUr4?EvqQk;v}l64>4i}2;DI>plK508wr>!5n?faCwj8HI zcQKV70*X=H9(kf3dHRzb4bZ_1@k8YUk!45Fb+(t+eJx*>PNQv;=>0lheWP{1)GLG{ z`Y^@wEhk4*t_~*MBaE*USds6Pg)YzOnETf;iQ$hm#!YxN|G_Ps1`Uux16I)hQj~f$ zcaeK8LwJIo@twbx#CVJ!4QNEsAUciDa9WT+j8I0J9S@5Ts#J`=hR}-%y`a;5Zj4uA z7Kt4+q=g1xCPcc!YJc|hB1UCo{1Vk-j5^n*TVrTB!>oO8!a4ma9p>4|xi1Csb18CH3KI&FVD!bhYrcf0?gfd1Gnk&YP zHQ+53aPXE&8dZVK|J;KHg-JI(%*QmhnS;!+`c{n>wXue?&@F3>G!Kk4LSNJrMiG?7 z)@3ZwaaMTi(yyDs=vh}OCc2z3i19@1zcM?tznSI<@kQ6CdzVH) zOc-AAGh*EQ)*yXRWgoB*TL&l@6mEYLz`ahL5wuHpbs*VRo6? z-KGV&CX{`fz6wf-1}qw1LoZmbjOAygQLixQrCYSd@z3b0oxPS%uq34N6?i~>J&=&~O z6*c+~3|R)YPvbvmau2)wfkv1buLV=%C5$XnQuJa}K?7)Zl=HPJNOyp(m?Ws)IVOtr zZ$E-52qd7FP?EF&PC&80Trc{_3=~a=KqU6=iFhc5f-Ma%rg9w|Dc{HF)^?dcD)IOV z4Om8KSpHSJ60W4k`F}DmJBN}mX2sjCViqsxNH=p@!u}Ha^nZOJNf01R!HE0l_x`Wi z`hWMLtvU@kIOiBo_`fgi|4+uT{;Ltm`QIk~FO9PrxqZLuf7X}%4gLmygTKMw;BW9Z z_#6BU{sw=8zro+&Z}2zx8~hFa27iP9zW^3`=Dz_@O9KQH0000803>$$SU!Hd6CZUA z0EL7N02=@R0B&V>Uv^<^X=Gt^X>V>XX)<!r;5wX=s8f^}BrwUk9lB7HP5Zdkn# zKa5$35!xxl!%5MZS5*aa=^FeR&IgCVJ3y}CJiYwk*OYmGD~A*D(_sl-$ZrF+hM)4wqXuFhzl(dAT0#r&7E+qw*mWKQZ zyu|7JoL%53EuB9nBR(nf{<*54pdj%e8F6nvR|zRaMMVioX$fg*D3Jr|AMAy9xCZs| z=le^87RKMn59@=+dV4`mpB)^%1Mtecynn3$=YvH4L-Bvn_<8>JuCtQ_#uGzi#rqS5rGC#t^cNiA<&Sspa>5|AlzE9G z;#jOR+{s13LDEr566%7Hl!wBkWL%&Q&N3LNqyq*fE92lKBcq7;HzK>>&mQb#}lzXgT09%Dkr;1SMK5 zEra2V7d0( z50579wwaTI=kIC$XNm*P#}i|Q4Iz?=a?Vb07jHkD12N9^O|*4S^XL!f>tX%z-hRP< z=9L-7_fO0d3;CTFa0jQ;EK}z7cL>Bd^Zq%A{adJDTomM_73JljvNFz!P#HN%DX5|Y z#u+N_EHC}X;uSD|$pyJ#y#8B{|H0<(?Sc<-@WZIP62sxIW5>h~gTrF{P7&|ZQ$|`` z5+aOraKd`wz5U%p{*rZabMSJ-5Klb`UWxzXwfrOOWig79j&jaWCl?1|(83&Kp$aaF zPEZ9In53etf`gQ_g46%!*7Pr>^8bT1;r`6r-)ocjXBbNS=Lr4JId&S|kl&{xJct3`X2haaX;eR3W z-%s}s5B}{1`PUHft^3=lf4%QjApeGc!@uF*@Nf7x{2TuN2mib=z<3c0u^?Vmm6HwN zqKTXLX{F}v1CbGz1Qaw7NHT%bTM2++ob%>P8qh$t8cBWU%YjDt$;UHR2u)3U6O^$I z!cZFkfdJr)o*~xDn}h)Xa9(&nl&%KE%Gw4(IRuaZ)Bpt_1pp3C{ys=E9rUS6gtjKc zpU4FK2iTiDy(s~JF{qvi1Ooh@0i%2e-7gRYr((4-|V=E!nhDyDq=?mu^>hn*2|T+-CoYv z-|Y+5>wmS0|CeI_3I2#{A~2U817Le`fZ+`oAl-NckTKH&q?Zeb6wp8R&5+sxFd_b0 za)16K?tcU5f7VKpcKYRyb^R@;i9$o10{jC1pq-{AKn~CV^Z*z*4{!l|fG_|Bqyc$A z2~Y#H0e!$2Km*pm6~GB_18{&J5CmKYqJVfH8Au1Rfn1;vCKMH=70~tGO!M80eiqt5C}vGq60C5&V#r?0w6JvG)MuY0@4N0o9&{dEt$O{wz zx(k>gBw!Lw5kgSvJk^-c(q^zVoq)<`?QcY50QX5hi zQa{pg(j?LxQUYlmX$NT^=@{t;(l4Yx$SBB|$slAQ!mDMBf3Qsh(AQ#_^^p;)BYrKF@hPbo&JOleH%K#8Y}r@TvfpR$K?lyaH!fQpui zn@XBWhsuV^iz=EbhpL*Yn`)HmGu2OO25JFnIJGggGj%X^8g(gk2lX53W$K?aj5ITk+gScAJF#GF3|os!*oXUjOLlkX9CWo zo~bzV_{_|iZ8|zSK{_=$8#+8)DqR)bQ@VF_2WOejil0TCbv_$$Ht%f9+0nC`^wjhM z^y>6i=tJmp=o{(Z(0^f|W)Ni1WN=^zXUJn{XP9ExWn^ZQVl-yNG2UdXW*lH#VWMOb zWYT7GW{PDhVS386$V|%2$E?Zh#2mw1%KV&p8B75d0wcig;AC(ucnG}3!o&h&v0%Bz zlE>1;@`06dBhM+Q2%=ddSAfrp|_8OJu8K8)e%+$8k>W9Ohiox%zVx=MK+9 z&TF6dIDhMW>-o3rWbC5srtCrN1? z3knHZ3f>TG6BrOV~)8oxCWGVwL(G^H># zG0ijmgpxvCNA;O8o7tOHo9&@B&}ry7b7AuU^Ii*ji^~>O7W_0UXH&!X(wcN&2Hcd`xVbCUH0_$4)%>#$*)>ot-gBX zVC+C}*mXoW-gDe?(sa7*wC=3toaMZVQO0CoKD#KpWVo!jD!XR7uDYqZ<+y#pYGQM- z-`w@w3*GlUj6EtmPCP9=>v2@LtGISACNB@KXWm@i*Str3#C+m?KKR0YvwgSx4E)Od zLH>6BZFn%Tn0gf;91s_<5U3KE7kCh45!85%@tW7QSHYseiNT*kbVABP$wHk%d$03c zkGj4PrXE%t4hnY+?~UM#h>ci|)Qzl+qKWc|dL1npoe{kgV;R#C%NZLPyBMb%R}+6W z-Y*w!8dy)9)VTy5&yf zY2?-4yKpb@-hRGw{%C<(L0utdVRGSN5w>W$Shu)^AV|n5p(qI~`BZ9A+E=DfR#nbk zo?QN`!mHv#C93jSm3&oI^@ZxR8j_lTn$`Q4?+@2%)wb0^>x%1H>ysY<4+0*nJ-qsG zvcaICw^5<7u1TQjUNduZQVXahxMi!=t@T5jb=ye0etU0+Qb%*AcxS~Uo=16IEL~~c zG~Kb?Cp{rOyN`Vye|h5iWU<%2ckZdx)A47f&xW2GJnw&@^WtfrdS6e!a{r?N_(1!h z!eHx5xtA@kWM4JEmVMnkBsbLZM*dCPu;OsX$fc3)QPt5WV_IV`#u4K$CyXXWCe0_O zr!G%@n0B6Cnem+YHXAtmb1rh8d_L(d!`qy97v2%xi@dM@AornbL2KdFN3)M}i%yH{ zOa4no%dwy6KIMGo{#>;pz4B;PXLWeZc5QjxXZ`1w_zlL5dz-?WO<&c%zS^?dTK?wu z?d1E-?ep6eJFuPJU6b8+d$_%y`$-4q4=R5s{OCKhJpBAK=!o*@&M%Q)9mfX8^C!5I zlao#0?Ckl9$AteIwi|iCg$$taoDNy|bKz~S) zlai8Cl9Q4Djq)$T$zy<#icEnrk`%-UkT8Ns89^t{fOkX#DM&$}e-=5UWaJc-Q~(Jz zh=!IpO8IvhaWN!+(5V>!&~Kwp4L()Cb4p<(rvP{;rI@JHnN1ydsePlQ!M8N{Sc)H_ zo;b?zv(k+CX|ioCL<@W~gPqGDXbJW@`8UKg%AU_GL7NMWI^(mnvzz5a7=NoKA?l?7 zkrAh71OX(ZjAZ0IOcYY;ro7CQ4!+>6s9VL-R1cqw5Y;byL&>o6JF@v{!UUo-MyUx6 zAI)UX^*U*t%mQafiNcJejDRX|01FoiL8-RC5Ci*iEq)gyc!tWp!%R=s5LbcuiR6+&I*(`HOTKUlhW@)$wd%)vp9|JyRsC!ORiH z7C14AT8SAEsIY)~|C&#vZ%Z$~V?1uGUwF)VuQj>K+#1mmIWk`U>J1LH6v|t>FV?tK zSJLJ|Xo_TbVt$K%Fmer}5$0Q{v+7G$dIEgKv+u*o5VTi>QR}x8Zfga;yIR_R0_tHyFG_`oahGW5kmQqjV#BL`WDiXJ zGC*XgTwDpdto)w7wdU5y2#bYD3Js5WF2Bdf`hi<7@1qeu>!RqX6CgnLLW$9JiyhBa z$?u709w}V`hW92r^06v??gV?=!xY5rnU5RS+KgGgq&L_K)w@kwerMNyJik_fE17=G zWbFFJ@Qd-L1{Ny@W4N+&gfC*eIGRqJaaf}U#$b&kV|{x=D=~B$&!Dj~a(`Hz3{(jl z;nlFz+hwY3tT%1RNfgmcva>ZT$qmZIL~;{WAM#xz!m_KB#olUR6_4#rL`vxzrNj_S)_J zM_JXgKQrz0QU<$5a*GE$mBs8>7@%rmJNYza(dnBqi;s2N4l5(FRVuzugiZ8&ReyRh z@Vzkl=BA~!B_-9owDD+E>j^-QHhagmYippyssmdqJFE2J(*7+wM3lFA%lt$1e4+Xm zi-|*Rb$EHkv0ScL;7q@^+nx3t=Flg8O7Rk536-s4A(1w8DotYe`>0OFs+&GOm8SX2 z@QQA>7caYXFW+^22rkiE)tow5dpE}2hQ9CRnsJDtXqNrXai)gu`GGMcwk4vx@n}G$ z6<7qdFNcv)%ge>Q%_iVo^~rI}Bl$ z#gnyP3*)PJqfSGRcCFPCP-QMttm4_xR-R%*nNJ}^kQ0=Zi9HCoNad+dz%w{iz!I+YApXDU9+~oc$4Mc{Swse76OtyfK^eL>6LT#s(#NX zULZi<3asn+aK7}G&1%blMw!}MRLN`8#vUXC$|W_r>U?PB(NL}Rh^tr9v^0`Rw&Eo! zM!#k!1y#dnu@=dSG2!e*)DY8g? zN&%bXQ1^3JG5Wsv>@y>9U%t9RT%l-6}xtOytjTdh8y zuM3R<`J*np3Vyb4S~s-W{9bFI5{<L{kV7b}Hk-^V9Yo|}1_n!i(|wNuGG3UKAtX8DBNfeAuzv`<4!~X`3NSecKdUn$J^# zMBUoSXM$xM$%9r+%L<~HgqLj6u}1GYkc@;^ZNIv0WgNM2duSR9)t3l=b+9gTvYol3 z?|S2T*>fE&4nH@1oL9UH-1g&Ilh<$xhUdl`f4tJH6z-S=tmQKu+4YF;%*1E2U%V?BUAO z*Ql2y>j&8j1h$?5U4 zdw6@kHZ*J2qi4B{ZXnYOK62)H!$tnRXEzfz3Mw@;!oqDd?_JJvq+{o~yfZq6h3=3d zf4T)iHbvTwc3}#YQL!~uFS=dd`rZuWQp%rQT5_45cApDP{jL?)a!adhYl9?#Bgmp+ z%J7Z*1$SmL1s1FJuXnw!k7Q|fbm5N2JQ`bCYArq%HOICUl|i4=C8bar<=*nzAS9B< z)&*^=2;v%N2Y7W$mGT+o3d`tIYEFP2?1vA_=Dw_>MQPtgGZ^FFjC1OaHl?G&)Y%o@ zHcuG7dix5hJYM73*(fIpwjdqQI}6G0ZGO5*4&{<7g(-*cWZ?V2U7g4{xLmJ|_L zjiMAS)Z7GUmM*+-Vk{y)MHy}VSObq(d0Wl<9f1JJ9;^xU<}vRgaJPB8Z_HTZUi3rn zvoq+FNWDp!Ah)_R#~0Yykv^y+rECxPqFUJz0dwh zwanvyv3fawfO#o$Rygl%p)xFAD@%8q`>9^Fe@Om;Zr}YAAg_J|cW0ya3Fvv5w796c zu-;l|;QY$X$N-biopdYRX-95bw`o7z3%;Lv@XR01$Fn+>STM^ltgnmq{;OUSfyncCWUN@jyCejoUpJ{xJSfI+U{+PlUH#gKH z^%MCqS^hhm;i}*E*4jb7ef2*!Mi;U_1qCazY6qD5EnLg_m>=w}w5vpxrWFb7un+6T z3S^7cg=<*_wyNE^JxAi{pl3oL9{cFGw&y0^iJt&dEQXX9HU;x#QB4cUNJc$d?BPnH z8>|=7HLOo8A77KPx)#)+B`x3zn-F}4Y6RdC)9jPaHPz2JQ=C3E4OgM5DW zs{1!(DRR_^MKmWeAP5BNLnqyJOweU>i%*F0E=PVuMo^%JVT{vM$6eU2Cc4 zDs>ecPs8Vq95y1>o1Wx&My9mp4sB&+Rp|K%0s6V^QTfsd>Ix={6;GN8Wju2MVOQ^f z<~pA7uu_?9bX%QFsC9c-tC?D8G#BQEV-m=vf=p(1bio+ZS0FJPmQS_rRZk$+#EK%- zvTn7rQ%NwWR3Amm6h&VNR^HNm+FBl)4-Vvpryq1*K(ymw#u}Hgu~015zRX~Q5b?%F zOfKc{euP+CC^XU49OlOjcDl`Bm^ZIg{@e%ipm}T+w`i1K9$m3qb=yL!4Si9Sb6MWA z(5czI{HtxbbVHo3Xjyo1TM?{o_6@>Q7c=v*+kvH8m}y;H00%XE6is81fhy8{g&&h} zn#QAYSKi4^=v_MjgqJ6|aTS<|poFQ_6u}w@^N84*DtyiGd~nHnb!pF{#n`$&cl2%O z<<6V-?7wm}StO(Pd~eLrHz4LPsFM1++PsV;qhbu^_4Cs7kKZlk z6&nh}pYU7aGh3G{zfV8=X0ehVBNOU{h0KFZBRxYi*dmL+7bd0IY2Jq-y9e(ED2X)lmuY^7jR&E{KIu)-G)<6@Bmy**!=UXk zIN;goAP~Pdz8;fxXfLsSz*s{8KyY}U1M^ey$c%Fx8xi-L{oD=PSKiG zH<+K2;Meoh|21XoKlsZCm3_?a`>b8yK8Pfn?s?$o<--zN=7%hm%6K)f8t+bdjE3|h zW&FxY~S?zy2y<48IvylYt9C7Kqeknw0(r%|1}HKd8b=5PfPdl*y}^uz6z z1PW0b0RY!5PGV116g|V$A(^pM!Fl|cJjAUeFbO<70k3_zKaZ20K3p56+(8GK z;>Zo;D(^RqTI!qC)jJf?vIX<_SC$wa?+7BaSlNCF-q?-v*maK)@-@?LTAbuK-?nSo z!8kRAt^?aVa4$S3kl&eq%x=%(kWJqCgERjAx2?nrm5Q=c6`%@K`J+8j1_?S*)zL&l zx$3i#QJS@k{B*1|BhN?5L%;wb1pDB70dHj(0twvmn$p3&u4y+m5zqrYfp0tev4Tt0l=~ zx50%w72D+%PS;+}Z@e-2emNz@YqHMSd8%_VAmh!;h8f3&>+QlK`zEn1vuZ0@5YMbM z_1Y#mXiAfnLEiqQT=3q5WgnbWf9UELib%SF@>1!mj~mTCa>}fsopSAM`vv6I7#P?o zlGczx6XbLI?d}g+bXZ5E9c!io#L39_j!O$}n6}h%CGMX;^1JNq>$)dBs#1CjouL9g zSZzgDy*>ky7eE*+xFKo+sw1EZPqf)xZ_tosmQ{A&(U5P(Gy5si5)jr%cF~I?B3CaC z>uPVF?!L_$943EcydR23a5A9Z>)b{=p%}@DBudkwC|y`=TrSnck)j*=p&nfxWs!*@ zE%oj<7K)+;*K2%+nY#kVCddTu>1oKa7>hC?6p~z z##nix2)n*px=7o41xI;RMN%<}CapKuV7}0a=@lyiSsNzLzpj;4G$uREeWU#5Yl82C@d|zmrk6B6NrAiY^8DqY1F>n|+1b(x;nu9u zl&rv`W`Qm+ubQyM$SQIuMHwLHx@u8)qb_OTi6S?;@@Z%i@eQR_9pru`ex_RYVC_t^ zMb4)<<)XkM!SWkZhIjP4<9(P~!@oSMODftduOGev{@y{SO7bXqX zoI{o{}RjJ$q^7(#Acy7Z$ajwza7BDUZ*%STu)oOqufc4{ldlo_W}sTwIN}^&R4R z2nQ!W`#D!KLMK24QXL`t@0406Uib zU75<{@E<& zz8=XRo>NAx@KIrOxf45`y74RHzDvCqUFHIEmKi6LIf%Pyfy_*;KAhEQGAXet$*Y;%j&{Z59vaBe_ryS{Rd#m}`(Ia6~L^qWHOVv{o1VeI!@KZwMmG$i{? z5-Qu32%|%U+3bjZ`RiZGRW@oQ*`pgDMFln<6B5MF1ho;g!$Th3TxodrBQw~SCRJ9HDZZ zfPsaGkbNx|z-T;ZzSBG|I<4!dJjg6hRcOnE0?+no(Ekj$yscUNhzaeMpn9gL#(hc? zhsJWEGV^aznvR+AQ6!#Cs_OJ}Iy4S-Bm6Y>UPLnMRNXyq{yJ52z;8Mj`9y{v{^r5L zug0rc<&hi4FKvE?y0jPjEB$OQ$=W5PU+c`;Y}k~#zwhJR3RM>ln&3}MCA^=N?P4h> z75}`l?5-(b4q3}dHj%v&K5w?O@xv`EyME)wVZ>8ib52%2MMz-EFFXg~T7DN3a_~9D z`gvc$KCO7RhB+%5%7fKuSAS-I+lRk293C&svywk&Q)r>cO^-pR&X$VRx&@BK|9akJ zUcq)QKn-Q%&=ScM8(XGY#^+F3$G>ag84J>~o_=3Rr|`7eU~vTetEYcW!)Z(-d`uk{ z^{f78;c_tn4q>|x;;EA&^?HPVKLb6Nhn^YqjrTJk9~?fQ+}Bg-P5a() z3oUlAI(dnlDKzYf|Jr2{xgQO<(UFb*_-489XU9=USsoM{gIjEQMgbEzaKf$gz1QAP zfy6RZd`BDm4Ng&NF`sC)s%dPyGjtqWuEq}&_89ehCBCvz!56!PIFmu^!68hSsNca@ zBX+qRWd800U%gfcg)XZp)wQ2uD&<)due%X-)_Jc)yox0vX*4qGb_!zMpLgt-8#`bp zm~1`f$8eN#3Dc%RqL1W-b*cpz!X)R9(@_2^%dFw}9!?vQL+xwG>rWbLwe$ zubPl5V-Ji2)+jiGXGw0K^EySH?-;7&Xovk_z?k^?Pirh>a*Z~~@>)@uaCpsRoU*LW z!Q`L_IU^&ug#va2ldLlZ1(dU*DJUaKCv@v*igC)8=L6ctGq zSti@?V*2b8|D$(S837h&-%?ySSLV$RM41hnc^A|#k;u6}jO@0CnHOe%mRE6lZOgpb zF!ZKYDay{FF^yiue4?#XLoj&9Bo8?p$JHfr|Fy8ic&BztBCL6&Vi@ygr?5}&En2%~ zX!$zc+z6-fGx1>6+%&=FXUxJeoFjd<@zGIFYP7{TS30pCRS*8ky-O!uI2%J_8!Nyf zJNnFRWN(4FGxgz?XCyIlN-t4UiH`BhK3zIEB0ccjy zdevlo_J?|0d7N;k5t4}xjzevuToC(YA(WkJ7W$?`DMq(U{a}S5+0QUgde$bRD`5?G ztJ-8DbeaGU7$)$uFLYA|wdfnyo{hrllY>W5Ic|`AMq7giaBqD&g%Tx=T9!v!UEfW! zX`Jq07IczQW=kRLsg-tW#Rq1#3_pW7@nSi4PJX5>+3&{F?ujpcJppjue8|1wY5lpd z%!U+UlBh8=$vwD`l*MEBz2Qn6YU#1>$4>ZCOHoL%Z6tP_PQyQ0e=LOYQFyh$LEwE` z@c^m(mb%w`bDMtp>9&L$_I)Ta&vbVd!`t$5L*F|}zM1}d$&xhohIhutuvD%L!ZPet z_|#mUwU$z&DX`s|R_B}6M7HbQbNpQw%J?^zMGLpancY6r5&EUx4*OKhZ-|Gs{21!B ze@-JpQOG#F3cY@-iazzc_65<>Jb4LwNSfFBd3l|^bp-3wv6ZYe}e~?}ft>Fm}C?;x< z9Q~n+N3e+eY)?VV!`EkP^koJxa}O8oStH_4;f1z3PXj%*?l=J+#rqK@kt31^k$`5Bs;@qYOy4C!Gt@KX==qLVIiyH z)Ges0{*Rhg&z>`Oi~>jFvb;P5&*KLrZ`8{#gR?iI)LjzESH}{?V2posh^LK!&hL!+ zeY->kF-RIPeUhyDSknSbp?yj7@V?)Q)X{z~CW3Fmdnx>;OuywQ6#jXft&lHk=IBGdn zqKR{tMz^Ba4Iy2ysedD|Z=t&VUR}{uYJmXQI1_s1HEXQNF+0_Oby1q{6*u1M*JPq5 z1)pE4Gcl{5_F}KeFG}G5+yOGtl2v#bnp#KMs1(AMOJy2b6g_8(GU+Csj~1m9G{tzm z^eDa6%a#$exK6leVC z*}3>?2~$}}?wApa$>ew~p1B}nhPOPIdR!&(993WI@m@wD;r$6fzvnil7uXd61qwmb z73^#L?b*6k?$xA6_`BQ$J0&5`_M}SOSk+|6o;|q(j?u`uMMGOymb}5-UN$=64NmR= zE2uAI2o&e)`ekm9+naKP>=?D(-)po_&1^Hbdp0O5zv*;H$d`3+Z)lg8jKtN=GIoWm zJ)JPQiFH4NQ79^DmOpByKy-?UT?;K~6^I{=XQ(D^H;Q`{Hr!VDKo62RON00kvDGY~ zc}7G%-lCZXlDQ&`HxfB034jZ+bdUJl*3yYzd}*mw#-CZ9&u0U#dN6ls4;2uTu>Eq? zW6x@RK^*VmMtm~gJoH=+A zX>LE~+Xs(rBbTu`LO$MD6CDUnf}j0e#x3!zUfG!+(|*}6`l2EF781YO}I_^t4n=Vrb9N6OU0olK+8n}Dv4L5CWLBQ zdXU+8DA)InOH7qvM4R|94L@SW$iPQsaleX|%=Fm}D$4Kd4^w*~XoNRc2!D})%7=Xn z^l=!vjC&iN(a-0$QiI9Wd~0~gmF#?!v2c|t4e8jo3CuUZjb)D~GT;1evRlG;4ih)w zlHD*q>wSHTp-EQY{#Rfr`AI{dm)utTQmu00CP~r(eQLGDIfE!Zg4pB@?Iw|+{vnq^ z=})!#A=G{1ds{6tndc2W-aA|+VH<6p=!)SGt1(KJAWs|93eBjz6kqY`PAm7l?{5XG zO?|aCBRDVr61xOud)wWGE{tke61mAN;Cz3$vQ{hi7@i#9GWsq_i_M!;e(Kf8UHUFA=OQTW(cc=KQ?2;*-rgAnQ6rAbjZwb!UcAKU2>KQQ7Iz6jw-)xuj#XWixu1oy{oK`wI zH>$+P_+jz2jdnT#1CjEZN6d9aNRGmm*ec$aNc-a`u-JXfzC z3t|O-r=YAo791riLRzsMD>vS2O;!J3Cw!^7`S50~Ly#M&FXEsfJo^M- ztQvl+Tn*ln>W2%Or^QL#bYK3}d(L;cbRxa+%GeaDY=Dj2nnK{AN-?6aYQ<( z`bD`K`#HW}aBAVqk(w@5W%d+}1SrfJz>m%cs3l2&lTdk6i0YK4TC~!QAK2+b0X;MnC^w|c>U_#%pNKBd{DlN&P`Vv80Izx{)He_WlR%?RMH!UYCLxb;2Vd2&Yq&h-nffw9=7caQZg{8Z^ zqH0hSCaRh+XLin=)$drjuDV{`#+(_n`Xue0tCM^u=6Je$VDC0+M|-pgO8Kh`Hviy; zT+(IZ>9v}C!C&$IoAM`sEDOVTt18}rwn$E#R(MPQT1LmpXA79;o-0qB_UL zigS!Uca3%C7K_j!3V;Nxs7)ka(A74+XtSd?nfLS?D|;B`g>hMg@j{J~?G$XyTuzf= zhQoA1m0{ah?l!v#%6u}nn+>#K^#jLweu6QWGosIw${>+nK~5FQ^uS?)A)@%GnLWH3 zFPxiInxBo-0n=Eb&7bcF%;~o6h^#zDYz}je9bn^lqAPDyja1|oSb0(2`m|4Rbrzzq z-|-;hgGK`9i$XdEQP@7tE2erwH^JU>=EtQ@7xi2<>!cBl7*TaXE9I0^byYoRSeUH+ zMVb9qfk$t?ZEvh5*Q80#UH9C+ch9lq)mipYr#bgrN-v2bhT&S_l<|6XH9g03v_sW{ z9AU4wxWqnr+*Rzkef{nb=h9inLC*eq@gUlOE=aR?OG9%|s43rgS<@kNM+!M&GIeq^ zRygxje;mgNAOK2Qtnu|c0Sp7t=yfv;);#GIMsJwqYVAgE(~Eu1;EN4|?iFA)F#BkW z)d$DN9Km(`BbwIx!V$ikjZQDc)CHt5DN&rlvbSEj80@&ODsaj1auiPRE}Q_Z2Hx7a z8Zo^!s7j*~AhpPfbzi%{=?Mq3ft2F}ldC6#Qrh~45}oDyjk=_tR2ctD14$wImJ@E# z+iFG0*d`AcgZ2)C?S5h8NKwmIRZta+R`XYL<2Qyq)sl}Yg+lTLE;gTY=bh51FN16O zGUehU1$1U`#cBlw9XYc=30p?1Z82|rUPwM{gGNqDNJ*?xcUP@=wXcKETmuLXmgm8Q6E%NYNU25gM*^*cA(JYbyHD#j| z{v;u^lC(HAB6ZDj4%stP29P-Gc~s+V+CD7?+Iv9L!y>pB${5WEK8E9dt1sA+#WXZW zvdmV5wZj6y51&@c8Brqwr#BJ7a(j+d16pHqdQ}x$qi+RF2TH1IDQSJL@QHuhbk&!d zgMGmb3P6rSLP$W{!c(Ym6OpkjpCQ)PnRnU`9%Pr1O>makUO!y(Si-Ip+uj9D_zycY z)HN4h#8!$%?n zkS=tB$B=x078SoV^=+hS#2`g@dgFa}?pRn+3)Q}S`79b)g*03fdC#i@_3R^IU|4>F zrQXZbPy4l*^&M=r?=@j@I}d^C=U@thdsi+awW8g^L-+}1gTrq+8A{WT(5l9HLz}&(G^P9azPz!dU zLb!74YZg^7fiKkYV&`eD#N|27cq)uYNZIUj7&}Sd<*A59D;ck@MQ|FWx{M~P2Bqew zRjz<_LqB>f3Go{~FDLx)vPR9NqD^@qxx;0)!fX63TF2R89{md{*4i|w$QO9ZGoi7r zyltZ3ri%}d0n!%w_Wqk5c>n^NKRIA-EZv40yVj{SNnA#YjLg4 zw_n0De~Kk1Xi%vPrunHxjmT~4a8=2ln-*LHr+F_M2S4Qc;bpKPam<%^7 z^D?2rXI-bd=skXoY>%E(mgeJB`r}`m%Oa^w&}PORY#|fd}R% zz;ggt-599X&u4UuZF$f#u5P@lQi33u>3{REjWh7szqo7Jr?r`2N?N&+Q-RWuY}82! zU8@yf+KR@bb(km3I9ohyenw=J*(cUzU1lcxajR-G&~Zqxr&f=9(GIv?COvknR9y&2xn)nTSI|_jnF75^r*q@HAD7Rt+^l{|1gP|Ifly}EA{`<|f7h2=_}XgrN8Hk& z`^dF!8^U|3msxD%YbD*dS<)G0I9_g_=bO+rLJ6D1* z%1!Aqswu2)ycuh5H)m$b?;vVRL$8x^#>~uS1@dU`ID~5Q3B(J=_*Vn`#o#U3_djnU z$oQ05DxWiIxa-Rjoy@5dO@3k2J-tyYnvr_N;|KOCBzB)f#d&8mvYP+Auj^#4u9k!2vhXi9E<=U_mwCP_QOs2*ZLzIXy=l-*m>4ehLV{ zrg*Bm)cd97&sI)0zn?;7<2_!Jttpj?5aU4SY_t$fJ)S9AwyX>~3{qOT6j;mg^*V?&QWo+O%2&L>pA*!+LNzr!t z*vh%e{KNw1O=>gG@PgSC>Bk1yG4FNOavM5B2Wo>R<|RAbUm0y}6MUS`l^bUnXkBvU zz8xCoe766K!V1elRubr7<+8mdv-`)}PB+@`zO%@7;Jr3iVEgfSADTzWkU;65n8_ok zR#~guba)(66vUfkd~`Lt`T|Pt#*Ygm*jLXK)~46eHx9M;G~iXSp}5S08Vggqc?CD& znzH6b*N`l2suxzK4EK2+t7^Qp$qMHfjX{w*Z=#)KDAlqT`jz{Cb=AgujHbjKhY0C@ z)5lCJXGJ-&0T0y9>@#+tnAKE4)jxQu*2*>gO54mZeHt2@5{f6l_|I|519y&Z#j{no z0W*Tt2~b(6p`^SEM&*}jod5;-$y~!Bf~8#n@KVKgj-@Mvh zpU>a()kza^4#^0RD$y7AzP0{hxqJ06QKY_4haK$CH()SAZ~T2k1i4aR@nYy1&IX%^ zUQr{2X{NoKRV%Ky(+J%~*>Y-FC*$_7X!2F}7m;*^tO}yz=aOn^6m zE|BH~ z@H#|LpKYu5vtl(BAY`Z$NBEF3Vxr*b(keUA57K$}V3G?o%AIL8Q9l|2 z7#qu0gqzdx3}wmyW?E|EZ*Zq~=aCAf(ecP45w=hVX6N!NmUhhIrG zhe;aVU8cOB>>+rraVIs8|986@)2Yi$*T>5QN_RfRk{QNcYi?Ix^O>!6%D>MGOK|@I zpZ?4@=M+eEt;(jU<&!h*u*VrFJS6t5Mh_7P5|#~e9_1(q-g#DYM3LWL zZ}rG8rqxFtu;n|tV>S3_BF;he9v`r7-#KETj%Q1iuKJAX{}q!jn`DaI&w)ect-vj8 zkddhH%Lr-er%~ci>2Hi8s(4>eIz?fp@n{|5eG-I>NGcEbw$gY-`Wtpsd$irFl6xb> zcE!s3=1oZxzbS-DMh1ryv+a}~>i7qRu~KjxX3fT{eV^WB&JpmBiBUBbZ;PkU(gPX4 z1gMfV6H7?(*p?-e_%bd5B`f=*6yLoiPz(8ifoQT|-fx^a#3D?GWN^iNlNq9-enC5q z$n0T$uH8mja68a#gFhVge(|;HnsT7W^A)Izx3Sb|7dM^v9GZN7^!eO`(71Yt>-&t+ z2WAKj6@~q(?53+j>}xvFLbVaxWnF${N8>kt>=s)FmWiWAnXRsT{mvml{l_!rB?`B# z1S?g!#7e%>p}^~Cdn1JKciQlXiucoTR2?iq-*-cL9*T*Em!0W61jN=~Xc!Sp^%?`B zZrt4rUrue0iZ2(dERq+9nsQNj zRfaZCGx|p8u1O8B*0hCvC`>{;#!040Y#bb?Da-h@wI9zxTU{Zzz-{2WIXu3pQSteTdAv7ADo(zVi+_nQKf?k8Ik{ zi(I#YI+}2-d(gphIGV!NmSOYJjM-JTn0ByD^~8~OTaGgyP?EB4=m9dTQ3zkAocxm2 zk$yyN=*!nh&5HbM38agyg_>08KFMO;FfLz8`kU~o*H`*9>3!3aMhWKXXwsKQx8!}8 zW(h~8ToNbhFYJh7(n(e@DF_f$_{8t&Ov`tc(JPucUSIs>BZOMJ1zTh9{&%v29WIQkqbttx_5t)D`LSzL|J zg<~llO!@=OS@83+9|i_|9M>`P|Md9fX`c(~ZpHYbKFL*Qi#MdnH_%S@hun+xs!Kw$ z(1JNY%)yr9ZRa-ed^rw1{S4Yy&^{V<2nZg^qfU0-%! z0IC|3TzEYFZ=00H@||CwzlDr;s*B(7n1y$5=BJvuh4Rgtg@1^>%qj9TUJk8|IYX>E z`ETxh)XDdZk{iN%RLJL2)CxX#jZIFYv3G$MN?j+%!!;D zQgHm#eAGM0+0#F?)iFXi=jNF$uypZc{)x_i^k;dU7&K0dw?Y_w;M)Gn$o$i@2R>NFyxH(#{+ z1G=T^xlor_`F^AhaCf{h+O9 z>@{zy6bIdT%ZLew8=@k$l~K_CkfOqms~z{w?DWmZSp=3Xz+?oC>JG5vo2&SN7Zg}w z!!xLni~)={GGiPpQE+GZZqyyzWG3y5^9&SK`i4l166;cq^ZM3;Z;v0xJ-KU8P~IR{ z6FeKE&VIKr86D^$rmd)rLz(|ItxS|M=dBf{3B3{j9}bmebQ+l2?k!)_&0S0^^vIfy z&6|mjiDGi;2B-*9L^FU)I~|PG<8HNXZOg)Hw@-a=2rPft%xYM37DqjZoN%exf`Syt ziHL)6V*Yl@)udegEry~;qeeK&>88(8L7;|-Atcb7XGzcO!lxrm4+*z;ypd^?b!MAQ zyELjuJZjopU5|$%S&PJ(05vF>q?wA&aa#)#=%9_5tkw}oAcSQa*ZyMiPcD+HEEC5q z|3goMcMe@EiIO(HGb-k{E}oi55lOx81wAjfU>v#&ITp7g)6*}~j>-uKiJRP*Li&}U z28xT88hM9u&mE%=d};j0`E~*PCp^z1@7EsArp`;f_CZzvl`%ggXwfzM?Rw`VV>|E9 z^)euLJRj`I6Dr$^-lyYQm48R<{OT7U!wH0klVyer?&maxlIG0~?|rUO;I)^rU;k)B zrDmWoBRBBbUxNu_afuf%see;9{5daqC_z3y)Fh4B6YWyJm(CGEV=Az`);XP_Cz|G- z2xR|j&@M}mCA{SrWKBD-qK7I``^I$ z$h?LJC)B3lXn0{UBwu8N1dz4Wj29M&E zTa;RdMH%|G>k#6#GTw+f>@3uMS! zlghO(_OUNDOaU5zf50+Xp?`vpJCy^;`6BQ3NtS*G_pe`f8qhuY8&7JP;na3j-4R^f zQfD0kWF?gI`iLy`cf;;)7D`jHt^C7+I{dCNMowmu?CNwbdk{mXP!%vyUCnvTrRNy-9W1zOKE&QK7Qc(#`{sJ*btJt3M2T~%3QuO%Q2Q@ zGJK@QHXSz@?X!P>M$TRh z`nqbd?!;9#@5VZ+t9c=)zojraYk7SC;OUAu+qm z*Zwvx%lfL}(i#M6aOIN$vmu*Gt!f8+>Cb8O^Yu;}4kAg=&O+SbN4o)TeO%b?!(mB^G2rhc?gtIipR1F$LJk2h8Eo;* zJ_!P^th5X4|4MyN(D=`1>Ebfh|66{Xpg(S9l>*rwHtlYlg(GU+TNrQOO8u+)a@t5wuY1g}UQz z%h15bldUI9Wz@L`jNitltI@6l4-7Gt9JR~oCRUFBDYS=0gfglFkga5hY5W_3T-GBz zNvA6{g#)o1wTBZ)OqG?(IFbE?KlSAN;(dOtCOk;03`F`2*d8~JtxnoC=lzzHJH!r1 z@^&lBF6>|>7y1f-C5=|VcJ5R=`yeYjpBh>ATp=spIsgAoE)!_5Y0IQHKp_aXkij8f z4(GU2-OEkBPi0*PIM#=A_nR;Bv6}8X!1lqfGjR{^OaZM%W13o@|7|d4NeZj$S;gsv z3t8bG==Ee^HuatU{@a{CH+2Y5;V(S!eo^RV( znx0XZMQ8)wY&V<|PiBxB#?4QTo7KpWqP{QJ^1qa~M&>qbx&GH(Zh3kNUY7jm0N+Rb z(O;=*;jF6fM^-oYyO&lV+csWV5+q=Vpk=ZQ+mZX8RMd7E22r;h$&g*v^|+>WLuEKT zkrj|jktR5q6{0ExNPbp!gerfDISd2YWWx8k$}sGFXp>M&d$M0JNPBv9fguJCWZPk{ zc=QPh2h5IW*FfZM+?6Kl$Vc6}G-rK)Vv8i-g?%#MbLH+ZA5soe!z?e3R62cG#4L#41U$vl@d=f3+U9~gGS1DY-t zCEL)(r`I!=^|;`!eSm0Z7HN9+EgRp8UDLR}BkHmNI%=en-w7L*$TZ0(IfnqyJ)6#_YA+73$NwBO z-x>2aXvySFPF6oCGKa(3h8IM{t)_m?Y2K>0^{rZ>Pw>yFoWzB0=6Wl{y4qJ zFEWG*+za$|oaCq8tOiKm{Q_Z$2$IvDn`3X8dTyV$ONV!_y@lhBIR9Qa+bfHWQGV!r zxtK6{e(^!GO{TyqdmtsUcJ#@DD4sH28i^u69_{{%&&X%tUo@lixn%z?*?}+?K}{*JnH|9*Sy<7KK|A$? z%*75{H#c#9s|0PhSU0_he-^buJnc8MW->BST3lYrA8Hw?_|nvkTn$fvOOXoSV69pY z0VmGi8P;(pSwDRxMUc7O9!`o;$AR2})X>%YD;XLic^lu*Sx?($8<0w|H==}a>i?93 z+^v*hR~r=7dn|IFVZSmeWX zL?7oR1{OX7LE4)uf}FCs&{novOc~f+@zalVAMHC%yO7K8Fbt<61-LC@ff+g8XD17n zuGz#1s8JRuxSRmG!fI6X!|k`)lQV>whogd$OIA^Q@L>a55QE<(g&6>WvvZ8U2wUAZk zwc34qGEgU`!t$rTI;f_`d5Y`wN z@kXLcju}%USP7*bsjo?C`N&Vd)e$SL+AqfJv?BZk3DF5z7abmLjYN9ZJ5KvI+C#n8 zgv^{9_4fiTLT+)itk!tf;>_)G58%VzR}KL@A;L8>XgG1|ApGZKI|*{(PY!A&aG(yL zf4Y}Wz_VyvE(dz2J{(~f$q7`mJsQH^HpC1a9lm~1yGuJYPjOUAl88Xz&2MYqnmud5 zpK{_qV?1yg_!kr9?|7wFg{Hh+>cF6hvgj2MqJ>77=JdRQ4yr3v==i(=2!Mmpnr1e8D_W%h2AO1*Z!JkQ0!$y4Z!P%-ag(v-$dPn!}rZ%l!KB=*gN2iCs<*apRlL>e zmKPUPOtl?FC0e8EnCC~#8k8PDw6z%zi$W7$vkNUlzY5sz={qjMJpqdu#JRWMpkLPw{8x_E{%WNxwfozuB$b#j!XVkp1-k#%Oa zp4~AO@h~VJ8W$nShG#3nozc8nN$su`eDHHhceS^O_8yp(y!JZ;i79hAmV8&$JqAPW zRprk=?c)RlPS|)XUu^-0_G4y*KPSVqq;;1e9tlI1Sgh$n?t`=-Er1h zc)pE4#9HiHyC!wQrOiXE))KnAymbf&#_Q3}KKOq7YJpRBc8V5{!4po@_jN<-uV&3I zq~z*U;nm5)Ee0Q2-N5oZeq4C#B}}0y&52-!QStjj?8iO!S? z*@U5NjrM<1BJ;(8ILX_?cNzH0KKh=KCf9i?r^+o7;F_@eaI@GYmsQ^|y`hPlr#{5J z$DRHAA7JUetgB+ztf*#P$2=^rNg9d|%&k#~QYR`0J!tm8xpJ6kf_On7#RefT!l`RS)Z!1M>^ zw}jsg!D8EC5twYNUfbcXN?L=NDeop|xbtw>%_Kek*+25>!AXc@?#*2x@}C&YFyq3I zxQqB(u3UtEW4J?vvjZoYI^>$Hm>!kJFl(+U3K|cN(48~Zpz22XTxH8IxEuoLbEId| z_Udka%Ht|V*piH*&>M1-JahI|!7BVbR7-%V>`|LDeeAqy@w7a3j59kp*E))Id;Zf-J_85&*9ez> zrCJNk4d@9fBk{Sfx)GRDcDZeM)I*C8YRUoAP!cNZO?Wd28ePZQHBO z&#}j^2BID=oVis;2udCgHrQJ+^}p)Y2fP^$k8`nq^Dib8D5^s)WK=)>tFea{=Z6FJ z5}9#4(yXI}?++lDTz`6S#TJv3QD=NMX-j>ZC#SgbcAIt9s|fxG@ZG4hjpcG}CUkW= zAyy!`0*QayuOlc1CZ0MgYwHr`OgA%0tvFs)tYjGO@G9&yCLDZ}{(#I5`QM~f^OzNC zR@!B;wgy_yzYLcWy&T^5f1Lt!!wMc-Z=S&5`C6KGy|*|-2d|%UjO6EMu`y1t%gKbix zC#&6trlZxiMeeOYfSkgCM`hvgD{=B4|{n23g9jL>;sPZ;{OzDm~D^aXh*J+MtZ#&oA=M zp)_Uqq4kK`dQunu&-BNz$Fd!%J>1T$fuW)`ZZ72V56`2KG;S_zzRrqKhB})HtC*bV z!67m#U5Xgkw3V4UXEx}Rg{a*Nfa<5L)lO`a*-6OHizPp*4LERZYC2Mu=M%0}>Y4C{? z9d$~}VP>dTiQKtK22^=jt5=?<&bJ&7X zv)*)^%s!PXmRM`;JRT2JDjrbHuW+wUI#+$~fB69#tIdt!X8M z?+U0IM^Y!?1$Xt!$v@_vRK0a4=}T3gv*j!v-w{bye33r^b02Ne z?$ijY1J4QGQ}`Ei;SPz-?DR6J;z9n(v^VeInD)Ar9|A^nd>8DFUyL5PPBuzge!v-X zNQ@VN^Xffr`LeS!Z;mb;4x6qopem>rWm-*q`Wc?)rV^-!0F}k-u@4-o-J?p3*@je8 z@z0l-I)$$tVr7aVa(9=4iccdBh9!ZS(WM=oQn86Hjc20_bcjK0Z!Lq>UnAN50YA%) zgmScMjNjRJj|7oOrgEd-CF0hs8Y5o{WlXyN2@IXQ29%h+d*idIM9#mQi+Klg-(~)( z(B7QZHXbpe^nzCMx}VN6hz=aV-b)F>7>(UE69Ltz;&`pkcG{tRM=!>OHt*eTT2ot5 zW>HoQi!Y=iKOF+{Jw%eS)~;lCNM;x^&Qsz>qkUw8tzJAXzkMvj#kxdpro`LvPnxM7LqOkQ#vt|P@b*Ie=TnqKeW@9W$^ zg?x)@O^7goX>{Ev{b$>*^QXkC>F*(+&QyE*zW2VAi(AKDEM^~~E;8md#(t$hzLe4! zh%6*TIfDwkFY9F;sdhd>R7MtFPW`M1G1@Y%%m?AJ{6fJo+SK5}{1kC8;%LElLA3!Z z+7L%aL^~`Z5dec#I^7)F|n_EDR-DHQ_Wr4b$oyP0(in1IAk;~ydYtGwty$x`ACZTk$&9rws$j;w zqS|=2*$tr$2U7wJ?w9iw$rQgS+d4#rB6yZh)_7#!)EKPTi|XS$?v-gkpEe$~J#g9+ zc8kIDdUV2FRA!?0T==zvyo>g*$Ob-MIPo?!@!WM&y|IYbK|@S>B5qAi52g>;pljRU8F#$w}IQ3 z8HRXn_)XubJVl@RtV4h;@czFyHsN12U%MKGQa!Hx2z{L}T?42STxA`!D>2^9x-jfV z`S0tK@qL*R7hi4_WAbCOhun_tRJdyIyrjO-lxq7hPZIk>FicMJf@Q#6_Hc@U*Nr8~ zc%u5Cllr&4{xyxMm=Gbd{Y=^l=3IHR#91jZN5H+Xp%X+KYlJdpB+ z{4nZkBXueWz~1@)GrDmIVD!2>2Nds&<|a#HYQLjai5Ck7NLHaf%gx~%R|LjnP)~&} z1=R09%2c#9O{~)+Kk}cy_e;&7+x&w;W;rRd=$Tg1Pciz0r5+zQ^*hIabuVTp!g8#2 zmLCD190_kTtlPEs8`t!m1+Qv8sgXLEjL_CR;5#M|Aw$ycE3 ztRw3XfRGm>jhr-=?D52tQ@jUswBMeNUY`{i%FpdM?f5B0a0<~O70IutV3#(NLd;0v zmNYg2h878wSPD`vJ(>?TD0vt873n*2XBW(>q`2uv0|m93oU&SN(5j(}{2%VD+%DnQ z^vo}p^h~i#f-KDT!+g$*D;k0uwHwwYFLJAJB-We6m`a)>;D{msvW7Y3 zXr(hz`zBbH?W%bQ$h;ge82hApByz4K-94)S1*%HiVm}H~&BzWmkf=blq}{U0Io91C zabJb0YP#$eIUv{03VgKsNiFEsYBHO4N6>Y8<>a+)kErH(N@187wB}&+$cCXYOe5K| z>uz`lOF8^`jbv=%Z_G|%A+XRWSMxU}1?4tE;~mK>{jiun{xL#bwQsPjpsjAum5avc z{PG%>?3TOj_(~AgA%BmVWWZf20ok%^^-!OFEW2xXM}3or7u)9%gSBf#i*Je)w#Ejd z*3Jc#_gq(%ft7pOv{HvpRK|2$C%UX}#5lX!K~C@6&FmLJ*pa1oCCw zV)ACNa{_zVDNkXVa0mPar~a!q?s_EAMsd=|%rp3SvN{U)c$+XGfhh0_As+%hQ|;&q zMIEzkd{a$Xl4N>%-`zILXPiZjYV9+~Bx{k1%}x*+KYvkX=cV7USp_C<>vht%CB$#H zn%~P-?G6`mk*m{!coW^9m-7z+amRxHc2#{pze%YBx|FF``LiXlhMdpTol%sD@`2tl zJ(DT6KKqoNYLD15)r^EzrHV*J5+$ZO=PBI58LYkC&%3I>*ejBO-o+M?BRII~AK0wk z3YV;&xP8YH8V-sj><_Mcu7XoG;3=n?RpKYdLOS&RWTtR%rT%UDiIVNwLjVAd`kT*T z1)?!xo9Pze9P-FC?l|8@zSQ@$OQJ323pzHbX}7TYBDJH` z1L=q2TPNZ~6Zx7wDjbc>7@k8X{jfWKm`A0$=xwS+8W3A~Sv{(4@zrtS<>AhGMxLq% zloWx@rtINHwHtz9HN_XzBKRy>T{CNLzj>XyoKMNu)U=whgfdHpF;cJEiMKkxqy+1p zGy`x^J|_#qaPA`z^S+DP84<>iQh8|ZaJu@_%bRPhGI%N8sR{+2yO3Lh%d%$K->l7E zE=oZLzw9W_-#$V;<8#H<#W8$mDbT!8X#GBOiZ*Zv__4rT-iM~lrf!|m41fx{eo#m< z37-tR6Q^wTB>Qt~M-A*kg4)*$b@pNl;NiZvFOYR+2ge(=bT^;CmRf&jp9u+VR7Xt= zX?V6?{v!JZe4MWS*=IhXti3u-_k;D67N|VH-p_T%nd~{#qN@LO+Pp@gu5-!8n@W3~ zs`@OTQRhVdMdG;?Ko>NR8~Q#sDTNZ`Ps(Z=Z_4BCza9it6>YC%k=s||fo4*UC4j6> zS$<07%63zwjEe}UP^rQxWM!ENn-kZsJ^{+dE)#TleqA#cN5$8=5M!u29VyXFi%Iig zC4yl_d<@Q_TRo#0Hc?d(Zd=AW(bboooj$7VO5Z+6O*$)T=TZEaBR1C#R8b;UjXA7E zARvNHueu7$0B48XKE*k!6#XKbOsrt@P=!{E93nUlgKm!&Tg;-oGsyKVA* z74HKwE$MDeD8tSrRK~$btKRP>cd2lr5P;jj2{Y|9>(M4G-bm4jtS{Yt6!v2`6W{*A zLTpjAstifs+rvHRNX-m9LSvZ;VkYX^)in^SQcX!xj{K32%_4qTZXdDqwtNim87-a= z2r6WDey%x9rb?=Sill%Qr;~XC1O7(H0`-eP+tUlA$Ky5V)}q;E7#68eFav3R=coWZ zVws;}3*j!ix(&0y)3HuL;{9%I6B=cl)E`Hv<1n*aT;w)`GL<3A_0gOMV)tYw}y{e(e_GtkX1yCZFGThP|m0qUaP-$Ms*{Dx;@BxL} z_IQinL;)aia7RT<#?lL*x=qO^SVsaG;`pKDdp|VXj zxW9*$ZR%Pz&tk2>*H!G`d=}3h#dXwu40Ft!v-mVW6m|B;<&%I;X)p{hJ_X<=#wNv6q)7mY5lGfxKapm*qPJNc%p z8W*_diA0VTOZ1c)74a%|v=9!Vn|-5MGe z7IljH=Y8ot+yBjC6~>{{PN&O!ABsQvkkeFaf@<-vihS@pZ2IC-pe?9)l6ZfQ=`^I3 zxP-E=w*|@Fl6ZV$@bcd#IOf{ksG;pD|6LcBI_`2GpG^ks#>U@DjF$(({>kU5qNw*j z?Vs4)4&}Z|3tT_0W*71F44a&Cvgc`}2yj*utAGJe3PAy<8aZB%rAGz1h8ydUB|CcxRZ?^Ez{=@BA zap|bDa{mfy*VX(eDI%6-z4Qy9i;owf9(g9fN8}flH?ix=K^j#&^KwR#56NXdOK6ej z%SnTj{+w`cc&>>d_6>M_OF)td0ejx_A;FxM)xH@+uF~s7MQh@wHGfUKpu#6ICvp=U z_cavSY8VKDk4wzK)~_oljZ|j3y;Te*L#g9vwJcEYJ~`1+`rdU}aej}E7N~l{khwTt zXD4~ydDz;uO>enc0>4ywat*OvaizWZ{Ucn|4T3r_Dw^BBN0zA>)?9c&I77~#+y{M;mB}?WT%KLf1!f`cLS|h8t+w1gSBs@| zWK+|4za>Ggi^hfjl0~6>ev%oC;55m6@W%JL)o5PuYaW$U9ht-@{|sAF_n2rGZG4Q1 zilLz6pDJ&MGYOw1MwiuyEgWy%sE^v2rpzmk1{5KHZF?EmVHJ#q zBfpmvl33w^;q@IouJqh=9Nf;(|F28SHq_udrC`|m|3yc9OA?9`55?BP|J!H?!BOQx zOL^gn%*`lq>)b_vXsVJIk?czbh1ZyF2y0Y%tO$|T(8Fn%>J`e2rd(!<@2}tIK^Vx` zjp_D*Tak^6MdPZy5f2SiqdT%QEhM3wkFoS3^rp~{*JT^JKlQHnD*D@5MZ)3$zquOQ z8_cr^FW{jomkM);a@00|{?(@KrhJ%2;}#5gt~$|Ev+04#rIjp${i6>ko{SKJjip-q zx*jX6#4PKmMCU&szgmDP{-Z89Yx;+D`ibafQ8A#AeW+#CLG|*96QKhWUZ^oouzv(l zz%7<%1Nd1Sc0L+&HolkA@w0q>DmV=1;3qOSa}q>>e%I`y{`g|Ce)WS7Id<@ZK%H?A^$au3OCL-`eNeJa;Lw{=}Pe zcU>$@Hs3w$7C3t3L@nN3T<~(Mr;NjeC39m_O7~=e{DAgVc7Ny|Ei31I?;~s3#&y9X zXU~+3s4V~N*0C5`an|q{F%=Jd*5;6smjQ7OFfegr59E{IUf5vf{`wGPIk<7LQ{^dk zO{IjND!;NyrZxX^eQa=$&E<1>qo=gOmIGU|7TXzpO@QM5j&>hAZrs{oRN>W`E6dMK zSq8-fR(^~v1#CW_*wvmb>Dc6Qp8>Ld(e3}--TN&6FL-?yvakU-1bn(1K55XEw-V`e zrLyiz-`lTv!25|tc<~9@#;9(yrzyLJVNv|gZ+gzz$x9&E(WeFAkeEx_8A71= z1geM9@#T?&YkXT`EyvDiKd2!-w!m;S@~nQ%ckIvwVpQ?Z+_E5n694&2wzN zM0%#*Gqu4uV-^5$oZk7!_~s;fFoWtjdMM*#wAntXt zzi$8&gXPgmc*0$SYi(!W+uy`@*vZk=Nzu;4LO^fiw5akDChB7dGkm209&i4L_ zVmsbr+eKH({;^?oe4rtn`YG!Z^KxhP|L!3IHywY&*oZdbv;y~(oI-((P-cPK!e ziS^foGc8N6Y1vxbu6&;x>|fyCEzB#yXd7SuiTw>|5`KbK@%4Tl0>|8%2=*0t7@(!7 zcPkU=)2c|163qp}_Hl^R99Zuo%Xd{7hEMNR4z5VjWb0yyb^VF z)y4y{H5tdgT~}H3wG)ks7UW!?vp1L0QRg-_#;(e?lr@m%4SG*^`GN0eo-o2xf1pvB z?$v*PY1?kT?CvgmsWbDfE@z@Wd9o*azKI0jKIg=nR?dE9tsC^Ojqg7+F|1~Q-9sri z=+d6GSfhh&IZv9P<`4gSw`o=4KZes}wI1hCJ#Xsy40gZun3A?0DB=@}SnT~Ryvr-L z%-mbqG|FW_TTVP#i%)-H!j_wreJ&~rjR#y$N%aXXV*9b2s!>k&=vmX-z$CsGmmatNkn=#Xys=R_eH~Jns|*)aSGh? z^4%*%JcG~JAY;Hnh(mxf`Bg*kio} zY*)yaf*gHOR7W-3mSzI)~OQEpo2yE}k>W z?5WBA)DfHWO4BIbgZoD3Hm?qN&IHzR(Q_=`>Ta^sgFC2;)AyT0WB!U zml_pEurzAtqllN&io;R-jxP#FYO@Se6wPvA>cSw)K=ic{&gU91rLVm0=+S9WjX5;$6_cv_{bVS z`wlZmmy4g4i8TW?s^^q!Fjm!Ry{qO&(+(n(9;7o|t|XZ40PY$)=kY2GV;t?{bN>#(?ry=MZKGath94!V8tPTH~zwH zQ#ZwHcrTLLbeA;(cZbAvS!zvQ+r?|Z!_Dk*`G_ov?rdHBxN;i=6dAzQ4bnX6Qlrm8 zM_aeQ8C-5%oA4XTYFE94>LMzr^#r9hf4bM^HLnt?p1Y@dMn0_4GwrMBf+f9AJ4G9u z#~ZAT^?)T-AMFiaeXS4tR*QAWHoqq-g_ zX=IH>%(PCha3YjY;G@&k85*}U71^#|UR(`(b*Ojy)8X$m?swjiv2&dP{xR z;1w0o2ZmzZ9O>Q?HB0%qW}q!b@`SqAUHKbi z>5hQOLk-Kk!fU^eNogzy>woY3MZdRo!Ku;2&@M2hjx?di33nz2^8GcBzjvdfQNFwT z*|Zpo#6#(3Cky6BN*VfYRMI#~BHkyMPRa6q(oK^_rYg6^-@GUpPglQgG8*)w({X;L z*_&R_@ub8cif8pI@)>!S&}4kV?03QIj~zd!VS}?7!ke?Bmzu@s&*zW1?ffDuvWPom zmsN45fQqpfg4xj(eR^d$e!(g<{&%{VK7PYArP!-|yYvAXulXsG|IY3Tu%SMRk8 zP$<@n7qBOMD*6ZIL}4iT=v@2q=7a_;=p7ha`XEW;CHKdFXxTrGgL5>B17Y)tW7 z7u_e9&@Rz!s(oSg?MemaW>B@YkLS5@@rrlr%tXM$0c7&j@P(`BNY6{YM2QrNoUbot#>Cg!=P(oNC}OiTihnJgiiN^q5?S$!^Zd%3Of}| zOEqejT?Ae1W$t%P2HTForMp}C!hJfvlzF%wUpB%i67+&*<-6MA2%k*>Q{zXk(`FC3m?mhBh+DeAzB%)<9Bpc%uHO0K73$oYHTcATaJcL zqO+D72PK|M3yGhMu@Hz`qec65TUX`4ue`tmwD8~j*VZW3K|8VMuZ#zEOmG=by9L35 zdOS4hOF{Wk5{u>inb+PF6K9a15ZgM!WCEbVDt}4Ohx29bYiY)H#xEJ1PRmL$2-Q}` zM*UvS@rKiUWAFP5_i3ZOb+c*g`Rg<6<1SqhEc7)` zMAmtZWZ9NvQ|>%UpJ95gM{9;26I8s(ow`Qr*})s2eA_J+4nksIAwa|C@w%NZ&%>$P zCH`@reWMCgN_1_6gZt>*5=!#@Sd(-pAnNLqT3 zH(E86H7)!{x2pUjG2QWT(~`EfMZ07p7=B6*wzWhWxm&CH$9QR@#MVKxJtNMH>8MwR zR_sTgyO!CaVHg0bsFN>ZXMT-{Nxirq&@l zy-^+Z)^$ksbJzB3xC%oAsxxnnJZZ)+e~(BcLl!PYP$>Spw>9y&NSops(U)XJz;#a0SDGMsTeAnoeHHPS( zC0dyDblsz{uT5TQ#0xkI%FeX1JRi=fS66;Nexjx9j5fJ|{fg_r@lhbINVB7@aC^iD zHyso(Ib54*`|}VmMtW@lB@PWNj&;>BEJ(tEbs-)xY7j|PmF^FkPPlTuP9#U2LmQd% zR^ZZKMyuy|o7cEH!~h`?CQIfJxPY@sArRqf7hW9!JKKE19@`+WW+T^_B7Xl6@T{V3 zUhOs#)Rz7iioUIseoM0VgM03iRfNrF5j$Rzm1UvsmQyN}t~rXUB-s(4NuZTU}> zX+9*Udg^OUN9k5SU2?tBytWLl>I1)FVh!e&3hafWO^qSn_EcJth6JG?J2%|8mEjaT zNDm3zul|E&lJ8H`c-R4X(DWcj4kF$#7avq>-7Wx6K(N2Sq2IL5cwqdURGqT_49Ks| zMZ#%TCq1XRne)~#YA+k&xgXSUv2fLb17(8PEsdvZroz^o@@km$Si^VALi_uQ4wnkN z(*XzBRT)SoA^5V4LJhL~5@#s4RpIM+J}XH*y*AXRswl$5XtD=Ny*^}e-j$r~o^9P8 z>k{-XU6fcwc(hYB1b-vST?23LzTFmfgQk^SZAdN@ZOYhUN@`rrb$!q)(hsFZxWfTS zyY^bumQN?d8i4$gCW-%ZeZ1k39@%CPyEO*?dzwL{_On|-xK*CzZWjP9zu5eLvxty2 znx88jrnd^wC~7}#rbOG44gXmu9?1wUQt>3RFw%mmGwSV;$vLp44qVyMOax7}H)=v0 zQ3jG8fgmQzFwRn)p^RXsG)aT7cx7w#+}?2lKQqj=idY|Hiygb9Rwr{#<;~IRR*^?J zufiP7e3dS2-!5Lv_Lb4|FEIP~#_}0FY_%u%HqZN8Z4bO|KR6-+%(!PcyfXs*T6fpg z`s0})4p0|LvjlPHujNq|jxX3-IptKSldf`d z+y-67j}A`WRljQ-4zoC&B`h8dv?bYugzPQ7gx;C&O4(J47M{LJ4Px^_vV6{pntSu}DznBV){0hh#Ci@m5BuKi|A@-LO=aO&RzO$#$Ue zBMSvk|MK;Jxo=nGzxc5O^Psr%Wg^afeELV7+7VCgQ|nWO!q@F=s~+jsruNdzaJ{3p zH2PcC?YSRd2j_9-aly`8m8|8(;7SJiq;tgs1COgT-MT_ln4uO=fv}O_OWs*e8}vk* z5HECEgQw4MMa7LF?=L5@-h~sI&rT*|HTH;|I7Q_B>r|U}Ryqt%*fso3WHt1Ps(=4f zhp<-XyGse5$_U^4u&`g?6RT@g*(!YQ1atVJok3M9(B1k{z_VSY=9g-aIf%6u^=Puu zY0S#8gxrWnv?pTKG>#yxadTwf&aN3iNN8Sg-W&8g<-~h2 z&iXN8lYpzNyJ*lhB$b(nCjk7@ec(-c2B>ID;4%y-yTb`VQhN|8-n%47NLjhLRbb)5 zP05!|?G1*idtMK>EM4zKcCv0-I#VKzzByn-t^fwHFD`ZDW*vJ@gHV+wdPjne37`}t7GsBF+(cDwK_ zx!s>#8qY0Uz=IwOSH1%^kOdGKYfU|s>vw^QPRI}hz*zLP&$a)dA80D%Dm%NkmD`M9 zx2&Qi#O^E`GoeB7NC{vua$CiogI`Uoz6LFE%1TatqqgGGUB)oo~SC_@Y)C8b8$Iv zNsNfG8X)RhK1_X1GQ*~%=1O}yI}ouuKi7bX+f@>6$*f|4{{ytx zMH}sZ7|52YgO3=0L~|x>0VoB7E$Xs4#NVi%uxKt2FistIZ&aIW>v19^WtDGgZX<|PQ0Ny#8 zmKV>=!|ZZ#(&~#@Z_O}ScZ}@2{uMIdz0>R0{--C*UQR*_uE}hV#-=Ug$c0(PA&R<; zn#aK=(noMurrAJlYrhJ-eeo!2J+Ce$o1re*g=c~}GFtlRLlbM=wduIChm2#hRD0bL z(!y`_yW}-ZSnu<12B&<+Pd5vVWb@Vb9F~8ediB<-+0-Mlhqv^-)kJ|Fh*p6~lQqZ) zuwJSqo3Sa_cM8=|(Ye4cl9JM!2zfccFOLSQ6h^h&vZi**&@zcRx&}D`8FqHdQxX_9 zpPD%Rj5)@#z{ZH9w+<(s{$QscdkgmSXXx~ddo^biJIvGJCyeJvndGn%)&Q?XP4wbylja#v9=bu8tvvY>!f9x?r4tZlh7X21eeky z?(P)oy0UZ|43;gM)aH@oZYEMCwQ|5pBgGLll_tZk?qb~RE|iCTz-zd+yIc7|DDPdY z*1#5Io!v=|s!chiL9>)?R+sr+xZv-FtpMp{1(zpHcj>J-miL`t&qbFOgrAM{R3$7;txC8Nb}NDtz`8J3oOSjAJjxK)Zy0&w5Sdmkj9kE zd+5WXk(LRIwWi6Z%MO0^%va$ut35MR!2)YNkjAPeLQm!JnS!`5kO8)~SE!ri1 zcisSg&E9NM&Nak~kyHgvSfujc1#@4C*G}sQC1hdgGDtY7E4V`e@}Nnga$zz6EqPfc z^%1L?(1Y-p3dW?U-s@gUu*M>cOKh{>=v>RfTyH=`f3w}Z_vo)%wNLeh6FyEzHfnr=vn}uJ{Z2V? zR6oXqE;M7m?J6k&SVDm={Rh}mIPXeJ#h2I^2R@8;6KSY`WWaTwO&qvk7qHIJbTA|8 ze%hY2!EnJ$H86BjDpiA-nfra{n8nxOJ!zzbOS6Zz5m*%6whT8=xzeiqrpCrfu8L4$ zp2@pa$krQqyf>HbEz1(Oz^psXNn{b2NBMK3A~{;MKbwR#6Q!N-^o3_jZGVULj>;db z^|bVe7W~D;=~M)Uq3181jIWIPY8C)?hI=rw`e`p?NaIFhn2YBa`vKvQ+p02_{*Xqb zQ4O@eOl{qQDTj77Ln$TF)YGP$aS=q+w7oK@3k za~QwXE*;q7C`McqcC^48vDgj!q-{ED&j+@3LY{pegFiiI{5I=_GB;;P3U#?GG}B1r z#W&9uL{MXEm)XBcpGk}|T=PoqlfNr0sY*ST>1uCsyeI3Mk*15+wLFt0@mhb!tfE{o z4GIP9_7r)uj#neYO*6?}6=~QqaxAJv3oe$Ij6Zo8aYxV4po)Gobi&5m^SrytSmDvp zoyQ|Vzc@wAOQr8pxTdX3mIf>?{Ns1`pfzuk#_h7PH1*-e+)LaoP6%wl>2(bvX9S>MHBq? z@hy)F!N8{>Q3W0G-iSkg5SM=WBRiY2Nu#8Cwd%VzSApAeVNl(VPo8wHN%;}ZPPo=d z*CN{$&Z9M>v0^h-*o>lS?l3Jvg%EWgEE$>1#Va%mcI)JtWzn;9 zF%Z=I?4(-8pf=>yRmebtp#>91Z1WK(YozYc>Bki(g(~8+CKw&2g?+`1Xy!24FegiA zT9MX;Z%yHh#ieo~l3fpO{sOUO`O1Y*HkvMRmp?D zPGUeoxk_FRqN1GdO=QR@{NvIsQgkrbb~(|a_Qipob*uc-VFTV)a^uN24+FeezS~Ne z9MQ@_&e;&rZy^gM`)H3JvY3GVv+CDg-{u_na^eAKg+ua^hh3)Ct7T*;IaJ@9-tU1v zpC1sQ_w-Ne`**TC=rKOCO&y#!bgQm%2tH5Sb=^>RI3@5kFePN)4X`rs`+%X7q2f=vCk$W7LeXWfb`;~2%&ZX&{Cx5g+H?16AS+JAjsPuV} zPh^IMlR^A3eG^s3sDb(Dsf^^mPr5?Fa0r&C6z!_BJVM2c&;J29lqoz;gX+uw0jToy zs;KqzP9Cga4GbwNE+p$KEEIJ7bfH5XYBRj;Cmm@{c>i&zI>4gBwpSk&D#*~P zp7Fn_9!vkjH2ja&=B7(*L@tZe2%GU=OI-irVtZVvki-Q-V{Ujjt{g8EEOXbuL2pP2 z_{asRPRZ0FP>ISrnT#BsL@X=QO_*DPp=Yst|4E9lp~n6vyhykMSN{2IG%Yeh9}+|< z2jxnGj+Gj_wY__8G>q`a=@$*SewciLj}1U~Pt-#q{<}vR)iS~Te=|C|Cr1w{@r?wv z__fIksM{c=@nI=-Q-)JBd1^ke1m2daOv6bynDVY0&HsQLZ2Ef%t%b33!7}dU8OSAs zw=P%+=h;>aKy5mLm8eJq$G2&e+ZCXa{tAFS0v1rIBN=>H&OFz5d-c;r>M z%p)OEA*YfU0UPnxUb}J>`xwLBDQC*lAJd-6P0RZXT^mUpBbQ8EEw(wEeBhmG@1eAcHP3UNWyk;LVMCtpjn=hBf=@kQ99yob zK*HN|{@ykMyX+^8KH~x=a=yi4W2wdB^E;^;F3c?-+7X@d3nq^5)Z_)MBM;b_7^mp51qAhosqszt8L(5ik~9N)WGsfVe zUryFsDa4?kRf?7`M>=HaPi2}M0Sr(XB|A1W#E>h>9L`#IPVyE$_I^@`dC-%Bav@8j z`)$U2G7BIs&7?v2(nUP)xAZlQzf8H`JJ6krv zBeR;W{TZl2&|Uu>g~3jpMi4-F-To)3HKqSZ0x zyy?4ByLxwBAjDy>I3P)Q1MewkO;-MLdcwZod4JfG$yrND5ux zI3|MfuM1|Re(npRPSlf5PHW=vJvdta3?86zPwyY#X&K%?E(oe~Q{J@wx$Z^33Pnw% zaHa4-4X0GHx&AJi{MKz%$O%tYkW9^+_i5zvN!Fh@^$DY5fv2PO9FwY9kZF{_h;Ej_WtgyD^n7{X6qiWCUvN}ur z=HX%r1DB!LeJLES^XgsH36`%nbuf-&w#A1ef}jWbThL+fi~Ciuj^dAlcnelt=u070rY#;CcU*L?7t>Yd?gig-2qMkElc8~*dKdxU}s*6`(E>M2nZ6PEq0L8j-`D|pq&pm#)zq(AZ^@e^^ zy0-8p9P}q_6Erk2ikMc6TN zwP6k_kADE}Cb58xca@^ApR(m-u*JJ?g7alen~o{a`(_ryV1c@p2ZIM3C*^9e>S__A zi0M9L$3K9#X1lp4uHNeLNq$_e|9mdhLZ$3CPS?6AL077V;K?C(vCY$@Oe~sU=;X1h zZ};#;XX}PM^m`{cWR2QEpKw-Vv+^`oO6w@T7CMLR?$-0F@|_Ksnj!9w2^A9-7_6yh z2cIf$zbW3^EI7zr8(U@iFU30#a@FzdxJ^q&b{M5EDuc&?rdC6dV+_p{Db6PAzu7qf zVA(#9{o`ySG`|hWq zB)ZOO!$S1_JPf*Wfq(TRC1-(1pWsmo-gvc&yVJ?gw6{T_tzQ5ey1nkHaoFr1R0PJo zHPB% z8*s1P`1O;veSm?rNZU+40aC$Q+YeA;_kBX|XY0XW=91uTPlhS87LhXHBN@!Xzk>^c z)C64)XH`B(uL@R$U|UWQ&pDJn4reSp$UtTk*A(vR4H?vR-kF2I2f`ejQPq1Cu9k~R zW~5rDdf#UCMVn83HMP@8*6)&q8EcP)`^3*7SYBxt_BHZxnjTMo&+UFKk>rd{PF#eZ zx}=fwtPdwU@Ap`SIDZ{iE>NnvJi#7@Xo**=(KWesCm3ST^?zpA0)2Px>v)!woeGv@ z;D0I6QzZeBY}F~g?lH)Vhax2CIVN>`!4!akM&(caO_$;;UsK7>wg!U*P)rY3mt@oW z9M#zgV5aP7k`}t&N^#k2kXouV_W3_&U+z#Vrw%s?nMbTa5 zc?vbtG=8A_7wSBJf)vm+Qu1yec7Tz#0_hEpvF#CV*+$>8GRxoS`=EUEn2b=HE#-d| z^(!YQVF*@y`BT+W)k?cHY$eY85;i@bZjLh;?qTk6?+<|shYkqW8bM>>eNE~132D2&)2ib z3uFuT@had&#g#3scQQ=3t%5E53@)5iitd`yqT0{F4K@b}NBM{l?N|tO%AYX<)rav& z*tjkOho!4B@zbAF907dqY}R$zy&w{;q_=b1u#v zIpUJ7Si|z{e%BD|fx8*PT9X>0fOx=;@YP|q*f;5+P{}vC6DL^fb4`ZklM<86hxbQ* zYi|bkpI9bVXxymR@Ob0KF+0fEAtYQJIjHFnMbCcs?_HQOLV**~=^zG_4oi^X{tKap zNqz5VrWw=w6wn^c!Ev=SA2Ng}>k*w$?(WwRtAHZ^P_y;H6q~qUr~J_+9yb|922be$%XskoR?u=}geG6hEt6*$ALNNs9i2kYD@8s%J|H4U1 z6Zi%_IK3)J>tN?ug0NU5k{fsq^CyRs(^QPAWVm>3S*cGQPoQjAoz8x`B$|3CO+{Ix zW9qVb*eHRnI={aIh3)fmd_tA0gwd2OUJmn)p*kzKTCSqFbeeoBaAgMVbzLmn7*?EB ze5Fik9CGcrxX%zD*Ty&GuC0B~VKY*)Brp>Ap(+t1gnR34OmQ^$H>i1KPp2`UaSg&4(F02JA_<@qPn+Ndyi zC;bjfyAi>Ci9FcXyQNoYly@&p^+$W0kZ*<(s_>p3)W zpp6|6POo)ZRXr~E_`=y;kILI^JW9n~nv^(BOD-sI=MTgj7p4W`V&IaUL`6*@*Hm8) zDO_0at_vUk11wF#3{o|KF`!yKYw1H|$^)jNnU(4w$gF`-l{z+~y1TT}T6033Pmuc` zKxmPCpJ}eaPk)l=1oqfnGoA3H?$Pz}^G7m)RX336nLIG}LhH=L{5m}fj=0*RcE46;Q`J*ois!NsL%;PDI*sBl^cjJ;=zJ|yh4oqQ(?68 zJdrz}<-1Lqu*;o`{fD=O3_(9&f`Ns`S$!W}ElYEmrqQREh}J88%0TD!6^6x{I+Zkq zYabwXW6?Ts&eD-9f;hDckr8%@AIbXca}UaQlE#V_Vy#UOm`kvPr6{Z+GjVU=&sfQg z-0hN$*^m6R{RbQKTbFTn&h@@Hx$}i1(^ccr8)V{ry_Jw1gE(?&;?97Kqa=sio^^uh zYqq#G3}+iMD)d6%_vSP|>-2{;5Xb(&4r#{+X1npFD%HXKNg?~%dOAUz0OAqt72VW` z`MTM&hGcOATEyPaTMI>O(>+*Sf_2Kv9sV$P$d)+vb-DI=x(%aeagy6CpQ4k4y12Nn zO}{#tO$ZLNqR2A(-Y(0s$@gm}jWNKqsI?eXRamk6X1 z6a)~^la$b3CO6t?f5}4mx#S5+1@P8apYo)PnMmS1Zo6FAlxobU!jwX(<*MRN$l>_O zkbT2GDAr};f!Csb`@;F69{1#5h2n(C4f977NBVLL7-C#^EQBAqei1c?H!s<9IVgWZX4XQV zCfa3?!+uq{K-SGoDj7SRAn(Mw@y?5BR6sUs))2>nBHeYKy> z4FfodiyD8+a-PHjZ%ZxcU5^E?+LT367ZF>a2zCjr{t=a?M(ciW!7*i#DZH4nh3<Rny>TMd_-8#5&gM%&>qr(2iPMQwzs?18@g0FS)2fLlQ%O zcnch{2snCB9iZ0c|1tXufH=iDm;t-_787@kn5I4=7w;dcZL*K<_4LaF@$k3q>z_`( z(jz&c$rI9Dnlb+MK0o6(usEZsodQ}ayPacr)WZK2s|3{T4?HQIba$iYk`;L$nX?h6 zadzDo@6eOFbc+FB2$3B|2R5o<5*H01D%9Ir(rM2krVd_C6(5~RWR;YW$n?ol3`btnt)&npDtuv-}HGv`4IB>sAy=`2*=|8>F z*lcsQaHdiB)y2|Unr@XE3AKXBw@Ot7!{vM&XZ}B`7zf>Okj;mV32qg~iF$=chZEcu zHHlR|G)n)^_n>AbY<_>GApwaA@5YM=T>kVj!Ms1MO}{_%M7KKK z+=c(z_x%i01A^1EfIlxbx~>dt zZ}6rhE`E%J@rrl-|FF4yyxiGd1iY{TuHs+6ftn1?V+vyBVjXp&ax?t$ zDl7I~Tlii36IP01_`8zm@&rfz6fPqWDtFGHo&sGfv$Ig)Dm=!TLYqiJd=j_~Le;it zX~S9FUWJpsDPc8?e*p^Quhfzekdm&r=bN|Yn=!>KZpy8Vr*MkkgIUBS0sz4DDyeKw z#jn5f0F`!+kCzl&$c<-oIQoSJh&}o8b3ayg^hOMfPKAXZ7U*Y7Xpd$DN0Mpsp%RsN z58!9BLfxBKdjZkt6REC!#`j(i;0;f1+x-lS48M2u##;&AhPbsz-AfN(uV@TguLc`t z&FyMY1T3(g^C=i8up4f8NNhb4{0A5xc=B|FyMHbu*_>m-qi5g0YqR}bn_hksRCF@l(XiBEQYvb?KpjcCqb4r=EQaj`9FJYJ-z3%+k{K%k+T!i^ha`peF2q zE^573TXFINe=namZYgbm8jdLI)KwDiU9hx#Y$sH#4W{6HKfI#xP}+YQD(^r5vGULy zvW-n>c_F4+#WE|5%B(HhCTiTn5HbP0P(GcG=YUP~2T``TeL8_rkI+d$2lrZ-d>EJk zdg+wAW$#uULt$X?={FKzpWN%n!Ta=?xR*=v(vWL(tDd!3%-;`Oye6%wxVC1~_g&a0 z>X&hi@zfR8$Q{Y2XCDvQBAIR1wgj@~qUfP5kXXzUYf$oyTm)=W|Ry^L~t%eM0gn&oI8Joj&^aAf?~SdW6V}(C>17{JUyUC+=#8~JeI1S#-B)x6C*57>zJppD zTv}w!cFcxO4(2=7aKx_o$iF`Jhia z9~4zf`z|*?r;4*7e48xaz?Xyljr7uf#eRJi1fto!_QcXU3cuR*ci@@lz{jPtv~-_M zCe}~K$2wM9I-`%gi*thm*S}{zTY8YQzDNY00BbJft+CvcH*WPQ9ff!KMsuX5(XecZ z-pu~YlG~8tZcVc!j6te?NL+hy_mDD9)^xEehsu{Kvis>&@-*g6^%K@i);oOu==H?o z$x}XFb{<8?AHKB|-#F18C3IYy6qIqDf3^L1WkD&V%t}FEsPuP|^Zm{Ll7&oVI$@jA z1lbo8v-~^#s#^?Xd877~Rj<=e`PE0`MVoy=IMGGmZpY%gc;StOXAHt``I>pr?6b<^ z^46n|f3PHfN%fL4-|&H;qGU`!qxzMy*NU>^sTMx7vYE$Pl7~yqxppK*kZE$lHiIEc8TFO)|#zvYDRj2gV@}ITGF z;-BqE$r4`%4F8H4$qeuUQjoV8zbdAbdn6VUXW+2(&iV?{v3cKdZPjQyW+%2IBTz1@{q74Kryt zN>9NWDyn;2{@=OV{k}zI6{l61&n5v!>G}Oxe))a;A=@gd?riCp2Hu$~>zukMeijHD zHPHU>)CK$x@F2XpsGxQr_ZQXCWa_O_l2rH_b$f*#yfR@A6ea1vsg72FP5?N8P~?bwt_j85kY zlea}-`l)p7R8Qu#on5Kv{St~MJXVVbb>*c_YXIMRd-p3QyH===i}X zb=K|Cv>_Xv6xXiX4r%`4i&%jZD>jhnFG%77D&ZAIp=bTcY8$Vbe#ckSowVh@W%X0! zaCRb{6lIfjf?9gG&3OL++D|sTTi{9OU4hHb7s=LA-VDl(gDWn|byu^Du73P|9&)TO z<$1G3P?eE$xnqmF2lyG6>|dG82eGI@WUxu^;^8~)I=}6n<5+=@8yBz0{8L-1g@?v;Sh z&vQyuzRNrIFs-<>^&WOes;%K`2&gvRLEQVjXLY%)6v1uthEWcZE#+irn?7w@Ua;0T zpAmm`U?P}gwGP8%VJWJe z1cpQZ9_K&6!Sqy`va_r{rQzxDSX*d@J};0uDUSqnc0_OD4d;U)GKfEcZv$U`=Pa2G z&p+D2Ex2B%;b0?w`LsCpv5pBSx1ZcigMg=A4ni02Vg+ufP*=VbjY0`WhjI32v5htb zpMBy6(?CU=b(w5CT`6&9d27b{ca+lFc5Sd~A$Q=SCS4F?Eas*im0<;wR1KR9teCjR zC=%;_ZLlCFJQX*Im`9Xpssv{6j6aWCbyj|7wL9OoATh(pPtpE$PLZTF`w8@bx0H)*ViEy5qb zTsQ-3)1<#@ycHNvRG|0O%$;@)etbb$P7QPWgR@Xq7-m%eb*B^fkkqm;8qO~jHZ5VU zn8c;#L&4_srWRyft(~})Z)csDvRM(FarIxc7ta4%TY|5&m}6osiVZ#fIp(V;zh;r) zFED8gLZdCn0ACb8>hIsQ%#&61VPe3>E8|WKx}l*K+cNvR<>A0t3#2tb30GDn5e6N zfH$+Zbje;JpC^r)nv$#;N8Jv?KQ`E!i|Jq4&D9*}BHGUB!fAvL4RBgX0wDe_JDE(U z$04*$!u8Dp;G!H6Or(C!RkmK1%_ivAk`F^-kW9v+qKF__P>EmW-6LyYwv_Wdl<^m*ItItVv#_OW zG~%X8RW@rBF67uo>8qyiHX+7esAv5Ij+i)=q+igyQO{PU6($|Jzp$y|dOQ6PuQAg^ z1id-Ac2Q&bS&BFg#=YPZq`0#Rn8;W%DE5kFkPUQtIi*X^`K_3+{q$6wIiE$e%BuD! zDifiEP?K}2_>>>6yUySnyo|E4FvH@iNoT*U>d@UtLLQ1uFw}X6u z{=W2s--cO|q{~BGK7GtGfT?+^&HUV~l@{>WPPs56U~HQLSe~-i+WSGYG}K0t>ZXI` zFgQKQKj-?DgDoeB#O~CK72$oQg2ku8xU;4-R^H+;lOcp^jaSqz3v+wtVUKVM#L6M+ zIh9c~+rEDNAbQeiMs7@h|2PaxkabS6l+$16DNjDzA>%ZPTFMiQJ03DOPO17tq}NiE zR2Fz^;o84i-nLaC@j$z#s`S@4ZFWS7(mw#Zgpyty!!~rnO)$nLQHU`^%>nX`1~cjy z=xFQ!3C?3sN`+IlXp_1u$g=_*ld3Ifu_Z~-N-U)+(TjjcK;tn37L(fe`o!vFPv&k6 zm&<EWtBZVRd2CR8B=5sEZffVli0rsDB1m9{HhO;J|p|Bt|Uv^Xh}*5J)@ z$T=n5J^25(NN!LPrv86+n6)+=XQp2Hlz_}vRD)rCP6`}wwReWJaTz$z=LY`(wB_x{ zf-lH9C#}zn)01J_2J8{DUB`lz2;mD;c%`iy>{~V33lRs`k0S}TaH~aTTp4Ry@x5{A z{`3=^m11i)Z|eM0_cXcqYhG8slAgHLO3`9=6t4X`&OA0{>wSEYTD$oxnMwD~zl=0U z8di0@!Huu&x5`{Wpxc~_i;Kcz24usfItA4}t>kt8i;Y-aELhE?&>8yv!dzI;RIa@z zZ2|sWsOr_Tm+uC%I%)OM<3tKmnwT^h0$Ee`T=T=)X`hb@!f~sBzre)NnRGQE?UZvt zt8>0G#OC~_cSx%WzHh^DLzYO#%uZ(}y!1aHm>Gp0T>7_B8AR|$IBblBrs(%KB@+Js z2~@cP0<8~CrS0LHmMJ0SISzv*O}JeejOID^;c`K&M#*>M>Rz~GBh?VCn;L~2XHUel)+lG z;nkJchtcWn(NV7(JB96ln*qkQ|Bbz5 z;qYZT-nVODu4$=@syhFb*Fk4mWr;yI{4C7i()n|y5saOq%K51xNIu*yK&8EdReW0W zF3w=l;wwV7my$OE)xnoDlTmqDsk=7Q6160Dw@rmR=DAn} zW+d!>^GR!7dY%*`Wo`H+V>=1Ak44p#M(9(HCM#VO@@>iLGioZ^b)gUqO^I2hhgh?I zHx-=wtfl4J93ty$I|D4vPa&!xZ$8-_eeIIV6J$|W2|8SKB@A~jooyp?c7$NEBfQB; zoo9x8ykZOEUCZRjL%xic>=5S!B;^nFPBRCLhy-I(Ex1j4do__NE_EeEwz<^mYFuKg~QOYfh>@#LC+eV+4y;3 zSdk(ICsN2{DE($%P$}klsMp zMOCh)s!`+-l$~lpj1KO+!NY0{n%AMn(TkSkKc@%f6^|WmNpG!tM0TX7o9x;;M4yNY z>BHC}=iB=A(jVgx=N>OlU&OA!+qJQ~D18=G};&Ha5ZDF9t;l76*S|i!}%^l|Cd>JrO z!tC_sV+DnlnYXuYrsfP=uFXh#UWp^)Imb|~T{~?DHnutQN57XyzQA{T+y<0~FXv{* zm5;MT<~s5#kpghXtj`xolb`B2=L)R2 zuozfX|1(sO*ln>hc6Y-jIJYhb=JfN&J2)MqS>{FirVa#;M(g^ts%W2!6G8h5aq=i3P0?re4SFgej>1uxQ8 z%_|m5!NrWuPwMF>dAvo2^xfcxjHH>0Zj^Fw5IeB5gNhgi*Y-U>^a?#|_jIq{V!2?T z{;Z+hOBCk*tkM@)M{Sp?Uly-~dF4^wb{8gB4X$q*uj!@jqWvN=xa@L!&d?sT+Nz?e zo}vjj*_oV$_mo#MFEOU)iIEZuQS1F3QXO|LxMR$;EGk#HtKuU|fQsdZ8$$H|-VX3P z#|8Bq!2bb$>M5%UW(|CbC+is1cM}vTIaLtau3$y$<`Ah#m_L^Ft!?QPB)1Q#NmNPO zL&UKxu4fYHUoP-;?w1PKD&i&$Shc+T1mnt{4Hf)!SDvIyb~VbVa@*al&X;{*D8;t0 z9A8k79}*TAtXv=CY+&l)dv9O93-{96lY)(1?dMau!q(5_wWP*51@8;e#p20{x-|#g zj|zxEIY(AbcRfxNV4xGVpvQgm6G$BEER@781q-v zU(PD7n7WR|?Z`x!Df@&f!_%tOhB~Qv<}9M|m31lW<&PQI?9DPVAkSm}0UpoG`i<}T zPnGg1rwvZ1vsN#IT%0~LLM%<#e=Z*6A!sYxXeGnUIF`ypV zNZkcenLZ4&d~y;Jd{_WQK)S!ei{|yM3-`!C$SqiRz8Lg43y~FC0&<9#mB6Elb}b`x zLU^?hp-qcMl-7#3X=8F4l^f;I0da`jT|_jEy;MGLUyMC(&LdVc0#wMo^)K&9>B%HQ zW3_VLN_yKk$;#V$Xala{9%fCKTHp3|w)%a%PYdZ#imVlhNeNzudcnvg=FZn@{{yC4 zCh{UL_AkSwzJkOVS_1A}EiB?~@xq>wPQDt(vYvI6Qe=Z0=KM>hZ_hw54l|fzdWY@` zr80ko^%oX9U-c(N^3A``4))tQ5tA^2cAo?3`&O|T{b@%0d{bK-rD^P3+85!Xe*h0z zntHg-Kfp^hn1>yPrJnwAxuB@mrdUSRfx^Z1KfoLc7yiZq{hV%tO^lY}+ARl7YhxPt zb;<~ch7o+iSGQi?m(M|ZcDpzTf<4NU4N4wps8)hMzNAYB|&c@_fU}dna_f5N_FcIV&%g~a>{zK@AAwFfza|Nx9_ob-Ev?rDTh5s+Q-ZCl*u#FZS z5R_IL=@_~ZkdW@d0cMz?5fxDw8fm1vYXIpX2AH9d8d6E=?ovV;1!)lO_uX^OI_KQA z?%((4-s@fK+0WX~-dgLgjK5kq4w7yra=J$P&_C`Go&s98y-$ygvl5Mc*F=lcI*rKU zZNB&iO!9vif8b?MXn{M!%lEC!)Kg=;2<))pT4Mat!GqT{}!?H=yZvM zNN)u&4TZ;=t6<;DT<36GDl(eOaZ?tOwtGz3h1ZQYJWw%npIkD?v+tTZhccHU$bI7* zrz#21PrrhmRP3d3G^VS9cCC`9Kcj%-WnT2bm1TzJGUT{hC@iX%AE%|_D!_wQ_C*XY zG*n>wdUMj|Ah3LSP|dnwEknFomy9P`;*hxw54Odx5xZ7Q11MKFv>mYHONt2MWSZ7 zUE)UFcQ2*Fkk@X%{6CI}@8>Lxfy1fnNdx+sX(Trke~@Y+d}pNgv7eo}KNTQEYK>}_ zlI+Ypj2;Xbf3ThtaypEA#g>@(p0ah$fdooQQNS-LsfaqgLhSQD%Nj599;tv%Cr?27 z=}PI08fYsuu@GR2hq^h=iQ$1})U(Uoj9R{|%AXe8=DeDjcs5k?X0UI1+s%ye5lKK{ z&!{zqkG94-GDFEekHl;GBV4_#wTVE6QhVsS{~Dv5?-;*WFCPuj`_MS}%wo1l`(xpE z=M@=ZZ=RE;<~ES9soJ&U`+LYXzTSh0o+sL(r*6J_W*gciXP)9s^^nU4@<4>yx;_{t~xG!1!E)TmsQ~g*~nn^ z;A7(H4~pm)vyTbsUa4V8THGF*A0?mhJWKFIeM$+t2mD00)e~|g<5uwFoteKSn5Mj4 zpG_5?b^tcZGgkLdltL^6#w$?0xD^Ix|>V|aa<9dIWHfxH}CXG|#@gyXln|HDma;b@GA z_zvtF;|~k%B4Mv%exs#@DP{H=)_xY9BK`Zy0(#2$1!xJ#WzfVl;z^750B2VpehsQ) z$=kE1TUzeZU@wPi#)&$WGe&Msdbq538+2Q0%rV>#ALWOYEV(8Rr!Jl`U51lyh$mA> zCax{#?O;Pt+NV=@2@NF7DTEx=BqP<7Q?AHh@}dbKWAgF1u&cHWO>F;QXyUM%m(v2E zjM{*Ph%F(7SD0VVQMK+VcVt5K`Ri+w*E+=a01!flO&CNU6gDjVs{y|Lf#_`}z+c>8 z$;tWfn)=GFY?=DEqc5Kl-|^_u!H{aK^M)u3Qm{j#!>#7szF1$Hz`b zNN3lsc?~sD-Wh*o@JUjg3p-(}Z%O4YD`<8i|1a9svgGI%aR`N1)h-7HAeu5kp~Tb}76xNUEY-)cDOP^_&1vAP{7n_5Lf z*&OwvyOkby)%EFC@Vxl?qs#*hJYzfB4f*p~9EVn)>v-^C-*ZCNA%Dxx%H$rL zOdI5EJB~2_AOMNH=}Wym%*y}a9vOmfkOLRXefttlx7lwl$fBB}b+t=IL;UB5QLys$ zu7_rmf?{4?i(GM~qyVx_DPB@r*zc55_|$f`Jzs&As!2FpOEtiZn8y63_^y67ORjjr zyST9`Qausg)QEKuytMtQ)hsMY`9HFmxp}hV_Y2kQOX3+v2f~|M_2Qb~~sYLr+ z=gh)i#!m86RH+;86ZkofSE<|#{hQ&~AZm`RQ4Qul^N>A_B1(Uc>v;OWot*@G_rcfV znBkzJvR}B5n%ihOIrNFpc`uI3y?8R;fJI97ucMq$z4x|kn)0$McL{sOQ|&gh6|i~A z3|-!&f~q}OFZ#f{z+jDZLyDy7m&J4Y*6^W5-gD|1xF_Ce>R-mnmLARHQaGyjB)am{ z>$5v8%Y9*wxKAGncRt^q(sK>k8rnl_E1zJwIXL8GpQ^tu zaA#fQ#&4UXobf}2PKqk5NIdcK&;c z>4fCV4OR-*i)toltSKTU(XxeVbB%{cDu-lwa}0vI#Dsn;WD-NG= zMvmW~t^rqHeSdb_vXQZp2Qfb4)p4Dc*0~2v+Ilw5UvY358=XJt;Tx~ZFnwAo<%9ij zVdwtx_p6s0RME(M;6i z*C?V{TTE#8%9pogCb--A^##wU1n2Y!Hr^D@)Thn)pOR!S!ehz90*$PL8}9*AxBWT_ zREw(eoJT($dLrgy>5zqi(2jRIi-W+2dnrjlC{Vo4_($8~S`>|-<0&A(7g!B>$?(f3 z@QX=)oq3)iUs3>?r(qWivt0jtFASB?u6mO-hK!VWJPxJlg#s`QB~yMvl73%^Ruv>Kk<=ZlAE!H@d- z{*;yDbJD8ci9t|p%;lcC5o~&g=A$&ylI(cco!}jJNz7km3ABDC?B8=<`JkEA3@ZhQ-PA%s$ujE^SzcWUtY3+RXr1 zS2sP#R#8e0{d9Tc=6p%3A&*--MUXV`*_z>xw7-e6KF{O5XH4h9SNRsbtD_E&1Rk_~ zJ@pj1Kz3vSFPfpM^c_8^oh7OEJ?4^2PQ@Z#A;GSBpX+KR;*TvLTVuR!!6y_ar)~Sm z&I>s>gjB4+$36**N@!9Si8+2pZp%*S1N#qnl=T9F^0*HfBk+Yp`2#CwzB@ke?Kl_0 z-MH9+EtLlO?}(7~SrgDY;ULu{5?K)N6SdlEpJTFOq2*%9QS^!KXzgj60ST6EeW1;y zROpQbcBP$qCe(tVxxYyMMH90Idam%P&H|HNyKNTrDNcMg!pJKz6)ONeCADyoW|a~0mTCrl-f&8pVxW$ zgQ+DLYG1DB1c~BCR(O9z2Ss$uRj2yA=oZ~e@0@h)ZCa@Hs_;sH%?}DgMT{73Hy>Et&A4$zU)+kRAOz`HrQ=rh_X?Je+ zr{wh;~Eo^I5xa(eBw%5=F&B>1B6X)2q6R z$$(M_q2Eo=iKO@Mz=O45hQ@EUtaB68s|NtzsGX9$m{ozvAih^QV{*;;?QrHVM>dRF>xwqN|^{-$x&7VVjS-2{O($>o2I=bvv3DwUeJB-ri6<)^J?rNcj4 ztb(En)l1XuPiW5m- zy(IZUKv>ICY}JZ_-e(uhHgm=G$-oQu34S04{~~_iR0+!rgIOiZ%$h!X3Xh883NmJH zPMQ6udd#WW~QNa zWx~+HO3Zc(T=%IpyiO;vZ$PL{C>FwQ!Bvi9zwPff6SV!!xr-fL8M0AKpb?O=db}t~ zYB?5zZvGu-pG zu8y-Y=HR5o@&?ru54C=wrta;%7RT^7H*yc+p?)QA=Ib+C=f@|h(Z|1m;>P+-oLdrP zw#Fq(oMGr+hl3Ov6~)z2$gT2m8M!!|SwrnlOMY=`X|4qp@pptK>ZVZI29Brj5( z4|OUAhVX&sq=-a20hP7->Cltu=W5IRdg@iAUsj9G2W_Q9vMWf)jhmbKg?9?d&*JCp zA@BSurzh-~5c(M9;;)0qet%LBU%{egGERpQxFv!-Qi_OXGS~R&W5p6tB#+Je8f1a> zJ)5-6U8a0)wV114T9T6xH3;^2UsN--Rp`o+!aNB*jOES!fLvU1X&yp6S~Q+c(yF@V zRcVIHlOc@_@i$-U^MnoC7sWKK1OY^wX@sVJ_#$w}N$^x40e5U%t2G7k?yQCJyc{Q= zsD^I9sDgm}rc&ute#OhCc3cI9p{2*upHUE-7UFD|ty@-n<_l+GVV%82hR7^hz+ITYy~Rpy(FO<$OGqydvjKX~(2zSG9W6$JJJApz5k3$!d(%0CO= zkDQGD={W8A!cpkU530Vhn{c9tA$6nS$H))97@1UakQi6c*5SQ9-FH;h-~nN>mA)q^ zBS`{%kzO9e%76f-UD0Uu3{D;J(-*B-!v+$fBIWrrbG`@Ovp@4H&H zK>+-hZtERsk0G5^#SaO{2Lu$-ym14(t63Y_N1*8d-;`H;1y7lir`L0Mn^Rz3YuY-Z zGl*JceoF#94+C?D02WCqN{@h4lz|3+*)*TzIBHhG9%PFQ`V&bi+Mo*X>4;pH++b55 z>MP5@OwVQ&3xk^0s(+S{NMSf5{Ry7P5wYJ)-2>XDT_9+wck~3R>IB++af#!>4T5t} zjg3%S8eWr{77PKKD+r1WWZAbQNu3cvn%@JyfHXrsRl)%I-5bU9G`GqGus;8vr>Lyk z`h8AU$R?6NP&zVAb-haq>zj-L2bH{Q18^G&L$V5t&xlXuI2`a1x*?F$-k7Za79-w9 zpnYMGp}~i$*=isZF=t6G#Ld?G=}c&qC0L?8}6LqG+EFf(3!Mjb72ldw|7X#Xey12DB!nq;STnS>r?=6I+$vwO_V>l8U(TSWlazNmqp}v z!?4o8V#Hzk`8Y{hmdD}YLh0M0+ol0~7=i4gM~#nGm^gp^IaKu*K<;7plb#jf<7$6a zE83A%T`>J-cZ2s5C2f{ZjZ!aA*7(=Y{hLr$Qh+O({*n`Z>3QxMKN}Kb)Ys;L3s`yn za&&Vgf<)Ooj$h|D{~kiG+Yr@FC2*$iH%#c+BVd@apX+MO&~#%VJw3B}lC1T?Gn}2z z@zuth;e&!O+k&M<%ayE7<#JZ%+*g0?)@|0!@^yiqC-(dpNl=AaC0Nm_e`z$C~ewz_rSFUCW7T4-q5F+rU6* z>Ek7ngE*?7<@bwhkZUO5)`K$+5x&>HrFkVP{&rS_;mC`tw8H_B6e03ld8P57;=uMF3S-s4% zCMAHf%dnIxFjISdQMP_Ik_kLtj<0 zjiUuz*O?6?-RwF`Ldo&tpV3$+nS2)4J)VF+H-zk4%Ke(=<@BzkdAnglV!(;qlX}_> z7Zb;?o^)5CB8oBG-Yrt0=gf82Ai!_pRda+!Pxz+lo7ufV0mIM~`ftPaX2uCA0cbD? z{+;%}9ck=g_F#f1P644Pst{o`*MG$KL_iiNa+)gDs>e1Z$x_l?lVnEc(%F(05>4{_ z;jUYw40Ds7Cw0&?-E47SdJy4Lz!|hMG3Se*wyq&;Wh=Orq9hh|5Acyw!D;nlyLd><1&6W`~n+Eer~32w4WIZ%XwJQD9s>Bth$| z^h_j6gG||P@IFw>1t|K=+Xn2>zAg56&1r<#^ za+xN3X~39378W8sd=lZST)8e6dJW(V!>Cc&X;;>4oVMmp3{A! zu1c@#rT#|7CUDSsE zNzXf^w*d3ukH*H2t>^m8C+3-~ri=s5Jf`xv}HP|nTEf^DG$T*zA6h>k|peU zSd#S#L5`U<7i=B*KtF4e_FAZZ>TMg(9v?c=OSLyYTb@zHsc(qb??Ol&8=fTBvXOHd zLhI=&06e`Jasy(x*ktQGGu6bi0?~b&*S<8loe^2UaPj$b?G@E83=cKlZe4n@$NJQ4 zHo6xWrX-t;5l#~V55fIvrHAibO@-o99ml>Flf@E?_>CXAgjNc4!tub0TF~WR`P0rc&qO#1Gtd5Qim+;wj9}wMFyu{Li1RoZLyHy z?O9F=Z_RjBu=7Xe%5o4f0fs%V#kK|G#8`QXR2H=_1Ys{!7+HYG5Z~P%x<#Vo`2Ug? zuC+U^;{1Oy$Z={*c=)(mJ>1|PFgXdODeul8b8SOM`Ec=xm6UNbWhkf5Sef0)6%BQB zBvkG-F#CEpP3&Wh9@92&2#J*za#4Q{Y(NNOfX-7TxLcfj*&eyE7K5KGfHTHt#7%_# zm@8pq_vW4Z@^rpa|7-giooo)SUHu;3b@w)#unXt%7J=Im&}yHYban=rtWv65fYtDk zX*%?egp(6B9ToBU+Ztj%JHR!NwWDSC+;cS+P`HtC@i-52}SuA8- zS#g3hE0y^*53m(8LHnNpT);8tNxp$(J2k$9Z^=zUWjCC07g+ig@2$$IyzkCLk(~>B z!fGcAl{t}WN<&uaa)2)}-hTwdTnX|Ha}09~!`OpVK}b)ug8Nji`d_YPSg>Qy%9Av= zyjqs9aWpUX9zd@3ET2$X249@Qg3gjiS8YiJ1@}!Hd)X!z;{YRo$mF#1Ag$elccky+ zf`sD*sxoEtUidD3`i|}ocZ$G?f(&$3_<4$NCG{g@mRi)5(Q1OxMIM4xuVnqvK8Yy~ zjwfs)7F;fg^z~E+71bR4Vc`-1RliKV(N3^{2d1QVfghL}%_Rr#0Rv`n7QwA0@~XA= zzw(=&N3w3RGW6&p+v07^veG{5kx9Kln186pR{%L4JEGQkc>}T zk}a-&t<`m5D!yter2ak4IwUHz`c61$ds#B$rFjIsd6(-`bp8Bs!^eoV?)e1Puqw+R zIw|92&MpN2#xieV9fIy495#AY9m7f=uaYfNpxWE)*?OXqi+WPFubX z^W7Jfd`^Y%tF)kKh&@e+I;hy&mn|%bJY4$tPI6ID-;zg2J`(nETM(u5=N_=1lcnpz z{*)>|`HR?sV?Ui2ilq!Q)0Y0HZ?QS(%jDLU>x5+<w-$;VGWn`?A_-h@0^ym&5 z(XNhC`Sn*SKL3YhykqlUlo62Z3}|sx^>N?=HO;ptBYy{+{2JemNG;-|ZS}<#^rxWt zu3HawcccszK_`K?j#HbdcU#HfZmxRIZ@_=@YiTSBKV*EbXW0Vfp@p2AF?!drcj#eWX zfY=naOx2Z@G|YN8=S=ABmggqBlO!#F3U3)r-z&lFS0ZROOPAg9Q=%Cg5=rpj5}USi zz;xXEBMiT%KvEaEJQNx9@ND`^jB`k}|E_?MedaT!98FH8k5DcYAdH>h+ILEG_>*;A z#8nO`)%zyT#~8nvb0gNKdTC|H02-NPpcv!$Rb@PziK)Jj4f^X8n%j>Y6BxD_zF}+{ z!VLiAy(6DKSvz7IhR#sw_Z!65C+&6R(1$NRiP1g2zVc|o{*J1bAR2^|8G+wP6P1x@ zRa^8DWS*-6qL zMU{kbmRco+TpDYq*D|yzE*u9h)leMV9rYl|LaA907IRpB<8|J^(h-)%#}{VSnolm8 z8SHS7l)5EkdxmQ?$9^**m5+r^qH3cy5akF;(AttK`ZCRy6xOr9trA4Bf~; zo*1o|<V*OW^g zvbLVO#d;6OaK2-F14!oSLO_ku5*Fj@hr^4YIqxFBso|VihnuyMZ=P~D5nbcq!Fm5) z?Yb^YRe=WtF&blv7C-fL1e4j#=p__rwai%ha^>vxqX*1MU=_bW{Esz86t%iVr86wm zo1ImmIwZRB7^$`Gs@TMq2uJ}bt#|(yY$ZLRrLddAWwp!_#@LN6rMQgp4}VETeq4ge zglc1lpDzn6G|q?g`}{oJe%$6}qnu~!2OJ7yqpcXaLa25kH@50Jn`-0(8ee+9`N016 z8`FT>E0!@PSjjQH%sqeyihvW3xlv;k1j3zi?-<=ib=XhZrg^Fyg^&8;i!x+n6XS7s z`_mZ11JSBQH0?825NHW1SbME9n=r@ipuBhpF?;hXYBK6Kk*oRhoDd07%HU{n9qxt; zz!%Hlr4lUeA*`Fl4AC&($_ANsF`~|uDVYcgbhgB8MnCtyJ22sp1j#|*1X+8Dl%LP; z0nqHsjRX4^1#Ca`n>&$;;=|F_bv9OK2{enTH_MONe>X3!d?-*Q-O{yp$}PzkekC`M zz-p*k<7E;P7WWXI{8{`*cN_6~c6Lhk(kisH2&* zFE-=_as(z7y6ZldSWK|@?%=2XM4O9Ix$d>W!q+bM0DmOI58A$1Xx`nVim_fuNfs8j zNuFRCRyqC2XHumdw)C&|QV)~HURO*F%L;j{O71m-!`X*xd6=JumxN2O`Pj~fpyc95 z%eMs7A&0~yFx!uU%WHLZ4bqq?)}oLvF=9Wmz8vUFr_@7qSokqQx#Gf^ds$-~v!IlE znkik0NABRL}E4=+(56aKp0L z{DaBisB8wjCk7?`RT1kjFU@Jwz_MLFs+hEKe#uJv&J9!%yVnGYZn)A>7zFA_J7^qJ zm5hJ`8_OypLUa_`eLvN2kvLE46dPpiIoK68OxX(42s|EwOeUEH)&Wfet6x%-<%C%6 zmV!>Fz5&@&svRy8imJyzwiu}NPM8lnTBP^@3;RTo^KnX2-FTW!KOr0Y-!>Lo_RtGO zGa`0O$v?4#a(&Ae<+(F{`-Qyf*DDkcXO=Yr7@eLHdVs%!B|)6lZ`gqbd&Am=xGIg8 zDRPhdHMmmWgxlYw?%BGRXycnirE$GpcFuECuU>r9Jly(IrRapw;RgI^`iruZB41n# z_T-vRF4s7xx-X+c9HyNygBnT-!H_p1($ma5Q}^6gdhkMFcbu0N2pY1lt`}5G+5*3M z>+!HZ)-CF*X^4l*%{^xgIekNnw8lqgZcnd@Jm3j_IeP?486W+x(EErl>BKzV%>4a5!$wOlez;F&p zFAJ{6C#=zcE*yY04p?ci6KPVQGWF$>(x^$Gcnjy<67_TBhEw8)Xy8>(>lH7&S(@3Y zzIry6E{5LzD8jd4GMGqAhSd$k%Hs*TBq_i#lS9r#6Nb`#=esLA1uvQgUL@?X2FIy^ zt0c}H*GX2T&Mpxt8`9gh;|-0>Mhp?V6j;Uj*KL{KC&zCp6i=-7B|yk9^6Q}Lb8_{B zQM&}J#e%#%MDy&De|BJFmL;ZsWiI*Tz1f>ijDsJ>QJy4O4L4K4@Y%F{hGmQw_xUio zMO4({D)=X5ZiB1mi;P@0{fkHV&>+Ja{>9RbqHJL@Uoj5HKBo`zXi044Q)lk?vK5hR z9hS=;4f!RUFoQ|0?s`XTbRCrf!l9<|4>_TJ40p_W;?~~@yw+GT7+9e{UcN);GFOUO z;~yyXqc;a5p<9jBlee-5y5CC!Zuvq=pvqc~fx;$gai-ZFntxFf7GzfOVlPGHg}NKH z-Z+xy@aw>p_jG2aUfY{}nSA$ZJ?;0xoor>8f#9_BAv-bjlfwZyVKfab}QqlWWtt z!q^RHD=>&$E@Mk5>iAipA2Em}8NdPBmJ+v2c@bcxONOb4;!pC1cPlU*FKlhZ*VMxB zsJs7bY4Pz1EP=^{cn3MV31C|#zs@j!NSB8d}*r4;B9M

    Hq41Pya!oejn846jyWSluAETsgwCIy3-c^ zvHMVnW16d%p*q7{;%UrBUbp%HG~l_--TSh3mUr4InqRg|LBXAQr=&-j0+uBjkXlMF z#NES~T1tuday?6otC28WYAE;r2k5IvvSQ1RPXTI?Z!~`1iQhJGjsjKdcV$<78STWH zMfUY7>S)!p>tk&i#=S>WX~oBO*XGl0?j84|C|f)to4blcg(ez~O*wA$BpjyVr!Jx6 z>V2`;B#izXGgF{RsJw!|G8@cSX@ERf3$$9`wlQ_BaI^LW&gaMzFlRE_)$B5Yye-K? zi885|eGE-Y!!Eg~+#+~Gv8qVlpl8^y*mfEqJU@V2VP?OkA@@^M_)d0~mGk?SfufH1 zPF_g4-As*#JTi$UwO*9`cu=7)<4EY*YHFfsXT4PxC5Kbhj2$DIytJ~H8Uq}1cf3b? zc-TEl zPAzaDcp8=|X22PZ&KRtQ$2z{>2OdE#J6tm0x<*B|}ftL=`%f9y?K>;V~}EsQy=?$q?6ly>Kzc5^dtTFeCG?o{(SX0?(6gtn%kbc@GC#Y@_foDk=y(Y<(J zwJdx}5YhTe%R@7#*Hz&dyUMaSi(2 zvZ!$(G+pOVmRCg1>d1aVO$Ixb7iAd%08O7_s;47Kvq?U(Qk>ATcC=HxJ~u+OR51xa z{Vt(>F>2&MA~<360R6Rf7}&3|rRhaq77Jfx2CwV*DjxNIbndF30`XMo`?BuDjy;%7 zyIu3zJMH(Oysnzdnw4sGh3-lgZoJTUIT}fivZO)j)G^%3o;0fDQ}zCoyA&^$!LCa? zA?LhNS?2ye9VX$~J^k~?3JmcNiz}l}zCukFva<!6g`Po=9zrzfx7}-+QnJ*4w{~{@Lzp;*4bI9K(%VnpV;Z+4+A<#<8CSXUMQ)C+ z5YF=gO0`?>yKAsl&|;R@U&+I6N| zc9M_F*s0@LkjTB|hR0%0&a@MYtr;v>?wlUkJ7$m#J-YoC{Yd7`=QP6Xa3j$k zA=dQQj}xc7ec05}K6+R5Pa{Q-IRFv+#R>yIu`sj&G~|d@f+J(HGKeP@0jDYSk6j7) zr8|dXSV!7bg`30oL?eUH@q<}d^r@aT2MxZ6C|M+gf&FMKfb(M|&1-6SKK-gm+2gjefia3ME&JceS58D zYpI?CfYZaRVKjD_T@{+x{?nOfAg4aI3WeO60Y9cY1?<}cP{UtkMf6?=hG zD_DhfM)4o!EQ-z1)V+%|A6_gixAxpXioNjlG(`6tuX3JVf|Y3+-;=f1)>0rvu8Ra4Ax;hPWZuTRq-Pz2mDx(*qU zm6=adz3-Q;a$Y*HkNbdinMcs(tpn_E#kr+L_jaiyGG6k<#mzMPTk-rpqAC$;f+i`K zVXN!>EUDId$I422mluBYm1E!7r>b6Mr@Ai8i+PUIDLrKLXc9{_O=6@&rAQq+8(W`z zhMiZO9_zZY45w3olCI002mLYmxr8)IIh>R+h5v?DFN5iE_-#Rv5P)+HEoHD zMOTB4zPxkQZjun_t3P+)gvV%Z9&a}Qi87|FzZj@msgse_1wVr-?Pg`|Le%FZ?-SD# zCOFMB^Ct}8q7#8rg_kln(Hl`=%z$3_^$ge7@Hk59libvWRZ%RzdH{W|*+k9ZY|FVA zt=}3m$jY`fuaGt~v!@#qz&mj4vj*mEYXNI)#au}0d(C?a!QO2|xKTG7D>I(VmhM@0 zL*?3I6Y%5EO@h2G4>Xe|*t{39De|)s?(cY3OFnF&x$Asb!PLbMR-T6ah4JDZ1ZBn2 z-DV6z2w4-4bJvN0_SC#PQ%_s26Ze+pX8s9_a@gT6KZF&J%9xIm?qrGIiOttuUA%Z| z$6rVkHCY&*GTFAAEG2j9)%9xM7v&U)*Ou@#U|-+L@;>F1Uw<=U-Qu)+xWuXT2>QUR zGH!DkO^|Y?ZYv@6^5j&Rf*808SN2v;9%-d3j@@dAr;-odO-u zOtqzI&uo$o=L^xxKOpJT!INj=7nGIIYyN5@W6ctlEFxhbL^3uw4p-OhCQ0IwyWRfe zFBYdPd0~LlgpBGrGonu2XLcuKZ!J-Z?_R1gMk)oKj__xxb^C>|F=(hwaK#p82`#tV z*2Dib&#h~zTp^`CyXndA*V)~%zFt|{UvwYNEp%X-!1rY~H`}yXa5hZLw-PNG2xEq? z(|k=p>XZLmuQZ-r7`9@J(0R@ZZ_S~z*g%Dn+yT+sHRA-)%W|J>i0A8x?^ z=O-Ef0GT9*dEPBu6``^gcZoSD5GPNjd|tCoqroY;CW0)2`ZJS{yBCn!{zRibrwzQ&axem-EBTDnV9R3Z zVT$oE!ZsUFcF&p~%-jn`inPJoSJ!({NKLCjPnc-a>tH4N5hsAQ3XcYlJQ}jcki5Zh zPzX}EC)?)<6l(C~Arxh#DD=FazJA%(V)FUm$% z7l~AUW(@5PyC%(N@`09ru}EwIKbk5v_EQcg7hdOgL~gGmi1@49H%T-b^bKC2_2<*? z`2zP`k?1nZ_;a&*0l;KL6bbC|RU{#WdL#EU(Z~IkQVn~hs|fMr7EyJAKO4WK;|$O= zi1JoJCk!TJ{Q`|@>Npkr+*k1c^mtmMop`6;?Lk#&58al;{By>P6*^A}-s;NQNlJay z5d7#T5P63K`vP&m-6eNWwB#!~?RuA*o+gmCqXjSfQN^iFUgyYcS19wuKs~1a(TwY3 zKj9Mq>5pbBLGU5n9CYoar-Q1h;{-0N_R2*`S)PcBJ8byq*hr^@mw+KfxN(Yr!{avT zm#UmePO&yLq~ggf?j3p8FmVK%n|vl;ge)3PH+oWkB?Ju@w z{Hx;SC5(gw#PL*9u>v)MCQ;p8YwJ$ZJ_8SC6HXDg_1A}~vrFN#nWbC7v6$2I;J5(` z*PqKp-RtG%Vf56QZa?})=zrGa-gnh>>E}=-^=Ikd{pif|^^aWj+Ml1d9z}c_muz`a z?I4*48&$Gw# zeddpfSXgRnvOT212m>rKQbVU-xZ$I%+|a%=rTfWYEGmT?p;2mdZ)fW{W(l0M5PbD1 zxXpQPXtS`=-76wH6t9%&8{d-B6iyQDyzr_s3C2mXr}RBfoy(TeA0{oGD1Z%D-bB z&XjtZWlQO3Q-=;-{EKC7CRhKEu`Dn;d z(xPyF{*xA+4MIY)<3*-Yn^dElTw?b6aI|T6Dn3iYPkz71^(5kcu%|@C zd8FZWE0LTxU2-AP8^UUtpPz|jwLg7wuE%@MVXRtz1`ofEcbX0)%iOoE-3Z=j);%VW z^e-b+3E^s>?Vu|MHZ97oTw^LK=6yu?|MXB`DZO}N{|O;JmfPFek#GQp4$g|+(2dye z)?~zn)_O!*Bd&c!Jhekw*NmUetqGV|!mIZ*b{&e4htBE{voP;=yF;ili8Iidq|z!7 zM7{6<2$bJ;?1$%?dg%zXz3%p@F^A`H#eH39dse#8om(UR>p*HPWG$uu(4lVj@=?C- zI$K#&9AfMkG&OQmvvil9Ib?~;{q^88OFa`6-CVPzb$jtq3uXkG{ZJr49eNr=h@~o6 zeJZKUo#U3#aq+c+Vz#BhM~+3R@0yj}S_4(GzHy(ve?Z&8h9iH2Lv@qk;76VK=crOh zIoQks=8`5pJt$5*wqK(|k5%F8@GVU1cr2Hd+*6iSSlZ4a?77u{N&G+0Gv?nICf@(g zl9{+b)q(7NUd%$Ld2XtrpET(Zwe( zN=7WK#}N|U&!e#`NPNgYVv0|+y>J!Y3MCu-#sMARk#W7h!TyV-n)Tt=VMT0F4JZCz zEMWK|`93tb5PA8XWPcpg%#=@e$OyIJhTqRvs8(V%p2N?P=Tbel(6@ZI`nPG<&-Mb> z?Uh!#R^TD~SQ09JX57)@K9#(j0U}@WHEMS-)AzlBkqr{53HuRyd&ayiy@C3Fv9!f_ ztj|S8-mW?3JBtA(^9f17@-JV&n)?BLn?Q8XFwK~7K|}BN)Qi^qjQCf`s)Mp-I-urvzKL>w$8E#Z_)!2rs)bcVl?9eCoj)`u7&qy z&3+znn{mOT2nYOtXP!$lz#2YXcjfjefbz9ddO|7*^*TJa%5)L~oU%Eu%&CvXE{Qp2 zS|sL`Y3>FmHl~5LA#hR*$0bR*6Y#yS`z71d>R^e;Oqv*b(Z^^<;kU7*R=HFC0O|Rd zctss({X^$wv6Wz1*wkUNR7#gl&=rq^1mCs}5B+r=5ieQdnZP*lV)t+(A=cOR>aCny z4QAYB z|AO;gs*g><(vOP6PtS8`EbAM$;$?TU6zWtrNhX&r(3 zYLCJNQfvB8Jytezk)~XggE);F-A&z}tVOx`hQC-R`V zqFmOdHm7u4%K{ezebmmX&SvNDIL}E(eS_w%R0D)tT9KGwpJSqP9G*X?Fcrt@jP)maGMxn~w}tcbZrsy!c&we9RrOzaxLn z?kyuuq>`D7E5l#HM=Jug4Ko`W4=vUmH#S?T2eak5VWpTF-vv`HhOE}Dv zDqE$8w1|WefL{z19r8EBq{?S!UG+x&YWd?E8)tP5b}J2IppT6{IgdSbSv%E5Itl=O z{Ca(G81)xx7>Fw+BsMF+lxptQ#x*!_1^PCznIVBRmj6(~k@**^2ef+FP5=q8d4x24 zLyvBWgPC{BkpdXlcxqx^(7i};Kfs*a4FU<*tHi0uksx-yf^Z)?IL%psxJutyB zFPjxN_gdGuMOX8hJsofS<^c~Haq_85BF=Y*C}yo!O?0Yi+PeN#EsxvK;zGFTt>-hL z(4MU+Zyz3&cH!}MpR?lQU$uAot9#pKEI4 z9L_I&|NQ^$CufG%eJr1fUupftD)jH=zc;*gb~zwOs44fkmHLH-lzp+NX^iyxIF8G$qMXZT<`!FO8l-e@ASel6&by2IwHMZR z?@+16V>FiD*O4a5dqxk7^2k!+3Tk5YVQs-%6@32bn5N%(>(KC z0*s*7-eLP=AbXhcNH>#Szyi?8&*|*XkjHaOyv1|z5Z}A(FuH@lRa?4m+PW?IlU)Hf z9Cw<;T$p@Eb;2kL?3}!^H^I|A3#u^`LXUAeP22m*RrEw;g%n>I3w4U^(~*4Leq8dd zG`)o*&jzF-NxPU|Rm14s`%Ojh^HSF2yqWDP%)5Nzcs$H&Q@Fj$L{6MLu5G;1aJy%- z3omH&%d~NHM{}gSG>AqcjeROyJ$WWgcQJ`t4*8r~q)G@6B%pmDVu&fCPLFN& zWpeNp85ozWDAn1d`ch0k>9S&n%t#ht|KiKD%rgJl`t%Z~MZCujP5MZA>IUw}w1!c# zNfjcC23)NQj837mPd+B%%Yj8KFlY&d>fmv-Teq+%?> zS(w;Wh^|3}NSA;Vkt%1_zF5&C&soE?paoji-(WznUHk-1(UCO?3TrSm_r|8pftc5i zZuPZ8b)kNt-s>W8MId=i+S2B)^qEdBYox;Lx zQ-gfjR~eQ{AkAK&qCJkD!bSl`VZ$Ry_xvx`pGB&ci3l*)0m}>HN zR?4`JwZqRb1wnhE+FKYG&iq~#SQ6x6ZP<-`$-GL0@%){t0=8x6@ z>p{C5vvIh2+ke6W!aN?0Rqt#zv~?Q&0OvCpz2AOg*YwkuSZ#L<-`Bs){u|X}%H4wS z{O>sOjQ`s6yaZ!Z(RFp?IR`rkT9nn>8%f81txsmjUm`zZnY`*!vdfqdIpr&30( zYZ?B`v=bkuCxEbb-48Ssvw0`81%o2vmFb`;FHt^lbMqTEZ}w8x)C#4f%w)#wDJgGj{X7ehm}`P2c2wH^5H5#Xh&;1#BImd&Mp7 zO+QNeR54)F^JuGW*UxW}S4mSn*Q-Y|MW7@6xJ{zft*oIPdvG25^_w#HYm z#*eXwBEW5m)7m9LYRoFjyF*gYps~{AP&uZo#2KGQe?fwX@m}|crhT~4@3SQF7T-jD zW!a?5J0}j>WJ+PhQRm`2KGem0i&~}86WcYPYsTx&12%YmY(*omgkVKaB5t#t#P z*ukXSv93y0zDs3|V&v$q&tN|#I(c~%Xto$P&w2}sNi(Y*Ao*I6!@^6#*RQUJ*S6@rs`F~ux6qNe3A7L34 z<7yG-Y#%;NRAKcXW>PrAi zlD;`?qT|QrmqIcc34V6#6eYhnoi}ejtr*y;SR5gC;K!GEr5atyd z*YuFWXTr_U+=e#E1ydJE zr;Z!vg8PO#1rC74QtwN4`Jo`shmcVY|vqKZ>6bxsp32?m}BZp8NBEu~v@I zfV*E{IusjY?g2-oqHb0bZS}sXw5Lc%&uYi)`Wv_6o!?Mz7x4$Y%FQo_!zHOiUsia) zo}Md>kN`*|(9lEpXo@Y(&!&lLDX?c~!NlihPwj9`@9XRKL_Z!-wEmsvB)4%RQnKY- zxoQ6kKxBz5rIq3}+AojQxbf~S4gs41VojX%#O9J}39@Tuu_`h{J~6UQDM2+sg>fgn zFUxmDr;CfYe$Jno2CHq@!3EbnG75)%gl3uykPY6htX#izONxx{e9{geF{2~OF1H4d zM~zuBDKMxpC64+2a7}ZK5e@uTO(?`~NS3l7uEHgHis_|^&~B?I&86;^NwO5fC~2$x zQIX(xAL2EqdDGfE6A~_|VEBf@U#uV4Gi8_uwPA!S5BD^apWM)qiic2X7U_2D&I(d| zq9|B*y-r8MkSdL^)PtJRf6m30e$%2lowEMm(4t>j3`0jE5uC}pULnIKwCipHShVbb zArmKZ7k{5D0&g7uL6k&)KIs?}pPGx%zI9Veln83ldn?cSmd|UP??-^u=Zm02j;e3h zu{R+SaDYZ+1?tSs`@}!A?}#`4>XfvdmtMp{+;Zj|J!2S z`9!|p>o7;C-IxC?Fwhn%rHL5{$ZH9I$FUB!c>X*GtM)@(BHv4zv zjj07Z9~XWW;KL{W8Ko3tr!qBPzd!SHY>AlWDuNv>pYAzoDgD*7NJOzfvos9P&6d#! zXo`8E!00i*{xm~I+Ee@#yzsGCxSmx}r=DzDYqS+Mk>Z{pF()?SMYZo@*VK48Wt~li z_B}c;OQ=nt=zGYAOK-QYu>oI)>MJ+arOX~9f-bUt?}br(U4E9v%bCKOGU~&B$dsumYv9?CJbk5; zch2N;!aZ)#uzT+q{=F^v#RbC=(KpUL@X{mAG`x8+?h0Q#PpE>^!}cI-mb-}&^tE%T zwR^4XV{BMw-xLqcVwfAbnf5;Km|LK$UT?utxnf~-*w+WPcZ@Or9IWV$?c2PXYc1g1 z$mLYfqfnJyKk{t;QFqMfWxk4Urbh46AMpqZvm!{sc~3T8w(u(>x!AZF{nY0tTE?wI z_l_Tb6>$47!E)?N&)__Wd!0|t)yHryStN?$Q?p72zk|B}*AI208qd?P>u+@mL`YtJ zU7Van78UB^3E3FTS6={z`A>vs-SN}ou3}&e>_!uTCU1nOIJCuUw&lVrc+EMu6RhoX zzlEukQ_}y&uN0yk-8AG!0^Pc1=Op6;iI`3Ltws13F`BU$9lp3OmzcGZ?NDMC%jgF2 ztjH(vEOW@c<(K^u)Iuexld1HHMkd9-9#V5;{tiM>q zInGKM5ez3-RNR88Fn$iT?-5uHW9W$1hLQt&%p|h3_75NeE%Z$uxLmJcv;DCWi5jmc z&gBHE>aXhOz{g&)5orq4FW5}LUx?e4qD4{0WD+2YRiRt`%FvF@`g(##BcymOsfiJ2 z5e#$d@a0pDTkg&hv(V{$Sk$f5V2aDG$MN#E^@s$_`50n)dV-wP!T3G@bhx0jV}Wv9 zP=f$&6vZYI@1~9(H<;|DICveeHhx=etgAL;1nURn}p8|CeH;hH_G4#AUa$7;6uy$gVX4V$vk%^_Mua)8;-&}iVd@q z3$5|d6K*xAP+he9W=NuK%+zK#t62gsayF6(%#c!Zlipl==DZNwciOfDTCbVxyLKw) zUcH}Bio=T;-)w=hB1~@1X7&9U`!Yhp=OXGSm3fU#6VO#3B}VNIM~h}8oE%#gq8~Q9 z968aRTfZsa*z_(6bYr-Zs>q(TfnLwM+|@gyk=-eQxcq$f`elx;lEf8^>OKNizxHqv6_|;c-F;(pO5|I?^eBmKy9-h z*+;GAGidIMzRpAR85m8N@WuH$lT?H^s_S_j>=5%LttqpCij7-);aEsyYf_*0>>-mo z4WKz?YL$J7mgIzezN6TaG<90~i`A!i+YaJ~c|D_a%1WsFV9&l|OhTF^V-Zr|aiCIo zzT^AMvh}s=I!$JEhRvQE($B;ZN*8UA*O?xjdZT?R!`Cf&D|MSPbk%zU2EeV{whFS8 z25OS2buF#k+zz-3IZ$?)E^o`yewzgRHpm=mHG=O-58^v)q|ly?pLv~6els;c1O4WA z6hmc!kJevsFY-K6ND)xA#|Iuy1e?(bXABj>1T-rTBXU<-N`fUQ4Jw8~G?BAcv3R1a zk58*;ZDF411wQ}}HZP*W96XQ-`J+CbIJF} zv6FlV8G}_Fx)2rMN)>WKGpaB)5eiwN8Mk z(o~cFCZI#dp*Lp3ch{ZmJI(zz1xOz-{bt)UR2X&xT_Ap5nsZXTB||eof=+L(j)Pyy zBx7c5955i3LZSY~3gs(2O~=0}mQD1_6=;puu%pg9iS#}mk^yKIYsihanAkUYYRsf@ zsYeG30BXHA{kw)E6yg@p1RAZrWv@l2QE}DlcN{x;RXZ4onZ_uH0nJrU3RCfdS$-YV z)@3_zmPcaYJn=?j&~N51*2|%NlW5Ey!(DXN+SAy$g@&U8m%RNgRn3Ha18eM@4x9luA zy0WXI{tnE;=TaQLYsdkRbpl{RqHSF5X9?ZnF_DfuRJM-2gtgO1&Z9=RE#f2>8x98{ z0LO<9q^gnXgjx2w)UTX|v7E!&F#zuB<%mz?kAUJH`k2HmQtiq$JNt>mheF?3*}{qG zrq738PNDk-+8Q7obTd{;tnGQl#X!O?K-6+s+?#VlA`?Hrt`qrz3Z4t77?iDJGkB%d zNFpm^QG-)bwC>JM9R29wP^(-ZVpmCNf#~WJCY^ZvE zwjWF^*W!jTlXvg4e{+vH6d|~u%NJH*it_&$`r`?jX{W*-#L!=OHoc|VyS=g)_*b;4 zqrw)8(M?H9*Jk}GC7-Nf1UDB3|42^LF=150tV5 zeZV_+>80Rp7;yo+j^E}JcgP9c25kJrVr8~`yA5!0++h^nR+5sfv*~OH>u0srJe=GP z!#7?tyVVu6H$5Cc8<)&tuu+igsa~l1{0?i(KA$Anu3QXo07CMU>$y86zTafKo{y7I zO^R?O+)F<#O^ioh(%gYZN3Vjd)uZe$R#GWVIt8n197eC~TqpaS)$GGED~G1{{slSb z>U_E-oI3~BrvRcs>mLfmg>_L)?yFH4+TV6N4zl2OS}KixT$UzWKHe|cZe<>vhI!vb zO0WQ_la;XxxP^tJq&M<9r%GJxEUAm>=$ zRQUA0?3ZIB1?_38xN)U_4T7-Wa{pUKs;E_Mouac@zT`(aSbHy{O}=Dnt0vj zfUE7gp*9X>K+mMTfW|slN;azcS!l3vxEtyR2D(qA(Ur77BWnkwH=K_x4vC(8p&h5D zo*UOZ2x=F%?H@bm*qWBp^8wi}0q0epreh`D*95WOR~Gu*#_C{NU!?BHfED2vm0%yI zAVk*g=j)QPv<|BAs`$*{*rEQ$;>uHT@KC->V-(`Yh06A*9hHYs0tYfg$Fb{{@BpwE zIw;sYlvpBut{W!1ijezke&6Njtj^K&GG38qYiiK46} z^T*XgdhG7t&%}b?%l$A@UlBmrowkJXPV9O^nwW@9RsM`O+?VZ3TG>-&Gi(5@%m}mMI=2<=ef%#xYzOfK;wNg~3VhcC12+}uY=IwO5>&ixjrbn+#E=97; z{kpG#X|}`ihP#voksPBrChCxopt99Ja>U9cU)-Lu%wZK$x&>c3IfBkLzN>ShX_G{n z(v56-qukcu1i@{(ua7n%9Y^RCxov5)JKO1$JQ+R2gW_+)`31?lzGgnjg4(UINJ z+Rp)`zU{4W1yt`oss+61M)%Bk`cL7#M8@P6##Dh<_T9xpQ<_r;_0yDh`OY!k)){pv zs1|pu#w4A;SULs1f>RQNXnhW9<=4`{c94m$K?&=Tp6A%9k=~MNhsLE$XgqZp{=2k5 zZAv2Z?KdAzFY-A`E;BI0&CxJq>dT$9>q?~YCM=WbiSDt!OJ0eN!`+=yH#g;p-gyAp zGRjNtB1U9g8qgbF6x`w^h*&@dx!KqzP1l6~@yh)c-2Z4FRv4@RKG;_wTQx-o}UQ5q+c@3kNFd#FBI#5Ke=@#3%jkMsp zKa})HnY%AS@zs(6`Pr!f#zIGtn&;qbl(N+tymPrFbnxO=>Lk)wX`9PpNQiRo@w9h&mV>jxnUtq!GHJdsRmE^y(j{zqoo1A04mF|N zz+E6{gx;BZ?U7S*3VdTGY!sadr!Bhj;aIPPJ}dSYx@Q*ApGQj)M5AvHi7bB%njnG+ z-R&7fJ!ih}Xo>TVEP{qy1~SY!x4!E4kg8I^=SlIZKb@7p--4g2p9X!Q|*XqPXL}X5J4~ z7Urc@jsL}(pnek|+`!7arm61l%RCnl@K7~yKgWiOooLFE_;_Pyu+7pS!=u5Z~qRe$*k0;=#WpGteTe^Io? z%K5B1C~_WSn9JQ+hfJi7*<+HWlz61`ICK@Dbf(^Em*-5a@fV91S+hz|pw;0YsT!AA zpa2VR>6vUK6Ea4zC|YtJT^Ea3WxS*im6KD)3${*onL0PyUg-t^ynV+}fcC`8?kXx5 zmM$0qe7zv5`-jAEKROCF_EFlNE~}yVS)vK8F~+f&r($*!62n27wKb6_+&RtYLM`CkN}cyBUC^_|-|IVrUzuCUxXB1hTZ6{fD=u$Hoe5 zf|{p`yjLg#m{&O)Zgx9dTYmjInOvVWv+H07AEcL`s&ZYm+oH2lpVIjJ7i+jMdTVhu zBobbKo>$mVQd4v=vNOZ(Kyy^?_74zHm;N!RT?8@|rM&6Q1 z4`g!$Q?jtqr*$^!UHOG9)Q2X{;6~Y2A$%ieMcMiEe3U&R%*cFHL4drZGM=e*UFg6ijgxMq&n< z&3_C8j2?^P(p@}Ezm1y_L%;ELe;+5%Lv`hDD4^N<`u1_8&30FsC8%UuoQs%s)mrIP zN~Z9W)ym_NHx|K~*YpgA-=?xGWy;Hcv06r@Q3dwn1N+CR=^q!8y3<{ByA^Vz_iyJj z1z$e>9GJXLQB}Ah7n5eE^McBQ8L~kG7&`Z{q~>dmaIRc#eK87D8ah; z4?_vaQ8hG)Q@637S?8#(9_z7UO9fc|M^6Y?Z0G&6wnq+$;acS=j55alj|LkKMD%g2KEQb}rxebi+Zwm?JSK8f;sqU(MpF2Mm|fk^Kx2Bi{*2U5RG8UVPj~C`SKZH(mV7vZbC=csFP6c( zmcm$m;|*>RpN57k1=S^mZv#TJ`}*AI@=~K_C6u=2$M{Ryog2uX!HAW1P45y+l&Bb3 z`u86Xl0Sr2dDEH0Z_nP&l#QbbDTc6GChR5wUu<}BayOr(`*)ej&jKPkN2aI^#? zBxZ9b(h?sY`Ik>d*-XC4-lYo!Q{UlsVIHU~D=yZ{V>e)% zyPJXwj1YMaj47m)JB5WVzhO(awWn?=xFE+Lqg5!c>N^uxYq zzG;Y;%QBRv>f>m(Ps|$J#}sIhL{WcTf3AJDj5srE+v;_JUB>d(36jsFN0xr0Md11_ zLg5fvbq9$oW4u#8*qd_Tmy}QbngcCef-XsYMa@+Hn?~nO13nMn8;u*sr8|$Hwt>Uw znk+axd9j1F^=Q=cMVyxoUtkm7#$rK*yqD+&HP?Jn%WbW137hafI=i3TtaS0yhGXxW zds?r;o(?o+6ecS6FIM?DS$XLDnb)ff;F*N`cn>@Q2d(_^9_h>GgTTja!cGf~x6DlU zW@-k*l$;UQn!bnG#o1GrZgWl*gP>k9Y3kuDNq9OnmloOz8hQ(N1kp=s;NWJxvElRT=Hq_t{32Gld;7%nOAoO(dOV zB^@6^O|kx*RI#}g#;k><`>+OVd>=;7rtmG`YbI-g810#`IEx~;j`|CG_6&5F)~rks z4t&~2tI4f2Qg~?`fxnCHbx^IMGLx56fpix9IyR@xDcE4XIWb0O+Y$dkVM|LIcQdus zx`IM7=_IvdxY)jzs3&q(4+!wB{EMZ195^5;yc@~boeHcYDy$f}cIARcNzKjo9=x|N z%I}DrI)+VvZ{ZN7 zQ$lOJvspLMVqb=op<}c(SI)r=Sotni2+{sIMsg{-4#r?2vRNO@{WUe{f*LZiHi!GL zL+64ANstO~B^g6fh}0KjLu;(n zI)j-}HUa-mKJ);PO{!J=QuIY4u8lav-DUOxPUxv|*Ts+!?l3!Xp6F(%h|5$i+89ql zQ@Ekdv`dS}>G=Vvu8#145ZCRtL;we5y4g5i8?3TGv%gyjU?0jg6;(9Yzr{J{oEb2{ z*EsUComUCyb}x}GZ16wXGp>-+b(oFN>ny_f2=NVe*kbwijW%m z@H=6Zuua|>c1|xH^2~5h^$%%fPMJLJInzdzglT4{UhRCXKOSVw4r`)-j;wlOoLMk{ z*D~?v$M5AiG7(G8xs+$@ZoN}iv2$=1x2>so?};{`{(Ak}_ufzI8S+}mQ8tw9oB$+| z-QGH^=oRC8--z<0Vp#~5{aXhD#Tv(dKI>wqy*Nt=i4C6yCXB7uU#4R>f4R;QTg@Vi z9*VhiKG9IwlQ5U`aBJ7L2#{NId6wwxrTKIq!p+rx@SFxe?R6l3N+dnR#kUt%+ATvh zEK}V9E{n?57MM@7ZT%e&FV0hrzkW^a0k-gmusnNbEcs)%el9(#w3%>-$|Zk$)VGzv z7qtZ08{h0Yv+kQ61OC}@qYq42!_2Z#P{2@{UvNYYX6AcW?S3>>V80jI(;HYvTf2-% zF`r_jSYHvqiqAo3iV>2*_T>112M02BeS?TE5Qfb?%Gyh=a{i`6f)@ww0zcoExG4!k zBif%spL)o2ottI`u70UfpYF*N_f-WvrrM9p5`J_DZElu#2n->0qjF3i?I%@_I~0#7 zN2=EayX9ITm3WxT6#qZ2-Ycr9^^N*P5m9M^(wj;L0j1ZVAYFP15Q@?Rgx;$Hf`EW@ zA@mkn=sgq_AwU4>fh6=^5~O!Ld;j-0&N$z>S=Z}gjpvyLR8-NJ?LJ`qx)edMPiP7^0sHnrjoG5rtc`=~3mq zk4G>PMTMKnX{M90v3v|q@}DLta}IDT=|B0u1Bje-3Q~Q6Jte-c)Au89%{{<`|KnPv z@c>QQm;YP9FIJ{``XAhw{Fwhu(%5RFajYPeC(!@5IVTRU(wny@omtx`kkyF64Gj7`0;> zv?%G;8_2f~F~^X&jTQnyh+y1(|B{97LZdiQvffv;9?f)V z6zPpY-}ga}D}q>D)D-VCS#*Zj&N&pNFej&tIP19m{+hC)gXcq*+cWIDM(4JLSp{82 z`%Qas)r&-`bGk(VN!jMn4}vlZe)eh6&$D*6XDV%7?>Ix=pVITXC1@hTLx$6WGwAG1 z?Sv#(_D_V10)p<=o3Tr6 zH2D4)-LDi?7|M0XZIY>}T>=&B0~7|#Cwp56Rlfn8S(oSvWJSzjsG?Ep;GKnUiCm)* z(!l$Y$Dn0n89t&9Po(e<97k5l?>tQ%p(Bs{|7gM$ zbXbLVE9{A^Ex##}RmY<>+JaYPG`#==A4g{Zo@*%bkq4|b;%L;M2{m&ri%boVt6<8i zWvR`PN5{*z?KjNH2&&dkAL@QxUQJg{+qJHBT5q~;yl{K#I`;!KKyqd69Nk#|a33hq z_~$^k&E6tfqPL>7*c#q-3CEzIan9jrI>VEcxrv#^jsRMb7h4q~5`DF}GxlW>d!Yr1 z7a$#vN4e^-!S|uCU1N8MB&So0e5cND1UgY{FTv>e<8THx`5es<4B1VeiAvrOulsc> zW>9g7PCvuG{2BR|$W;qH+0Ler*Y6q{p_`9-F)5aDa-@K_tezK9O+jo-&nON>gR8BtpQWo>UnagVp8625WtsL?659Kh zh@l&SNKck=kRI`BD(4CeexBiUpCh8*bl%cDgvEq;-MslJGj#Mwrp#`&E%muIK27UW z-+?iZHE^IdQ`CV&XRm*}v@*`~k;}1!hz*18AV;bvG<@va?!38;8bT2rJz9RG zx;DiC)*1Ydmya(3e_#{Y z(jNck$)^kKh2MH*roaybe`5u72WGVOmni0(^df$BE%C}(udup2Fz6qJF506V-bJ@%4jM$(m!3Bn+(OrO zJuh=<$it}2eW|7*lR<~fOu)&8k6%0_prkQtZtN@qnH=)dH5lg5YM;p&utc`?OW9xfy*5>^~4SO8raJ zDV+KAk(1Wlxci%)t4%yb4j$!9cUx!1(F6hv}yVe&d4JR1MMYo2H1^N`0Hg)aR(t>XygS+DopR zc10*Elo-3@_L&^KsCOV? zcYwJ$w=0HwjTtUKdu+;Z*ycC!a3sKH6lkM1?NqADdf4N|ujBGOwHiY1Z_ZlCDL73h zCBw+A9t9&8>rvFozW3CLzXu4?Bz3*y7le=y>25;!>85*s83qlGNly+~gg@jZ%4g&6 zm=EW#YRzwPBuavd12nTozR*$|#tK`1nUC5O)L4Ue|GfVgVC7V07Cu>{>cY!6d1U4) z8uDN*Cm$lPqGenZ%%QlN0QgvRqh>azo`%rW&?C_LeK5nB2x-bb+qk_%(DNA0owlU& zeUg5~-R^9py)0~|#pvK-Dj}(&=}oHJmigFm%fz*Gcvo<^?LEW__^o@Kj4s$7c zHb50WEPjV}t>(w%5gjLL@8wAL8X{n4B6N9Ec){BLY2>qKlV#y4}wIp>Z? zTVn2RIF;jp0CFz1#4|qUx7d4Z-RF&aWlb7-2F*FS^=rrPw2_fEo0D%0_3(mtj-9Y)7*wl~*i2OY?qpp-b&Y z<4Nz*Pl22{l;SZ{nJDTmf21%;Q#yy=a{Vdrx}uwvGJ7E)Ri4R?$ zm;7b~K{US!OH8l-R3Zm5pjAR8`Z)5PtyUCigD-tM#CeNLD37v2>leuSts0UiE>ai5 z`p@f+nU9v@#%c4|6dW$#s{?gG-9j_fuJ2mH9DWjUe#OfOLsyY!@vW*j^gz)OJy}ls zW7qgV9bIYBuU>%IPHsyt$h5{^qO6x#FUD9&V%dEyULoRT=WT(CJrCXVs3BBsYL*LH zoBRSzptM`f7G^)NL^aF=`FUX2^V-=TF|#l+(bJd{UDCIvU8cmd_ph!51U;D0rYL^C@jqY-7@+pB-0-%(OSYy9=}8vga#O zjRIw);|lM6PjJ8&<(8Mq+5>KRJ&dgZ4#nAhbpE(qOUe8&T4MlXfm=LkvwE`Z9 zKjIs9G(&3<puv&bn8^5L!~ z+A{r8J2NeTb-FV_HJyUjnWgQCf%UO8W~sBJJyu-J3kEJ1G&GWj4}3P8Mb)`D?S^X- z!?~4dbR68b%KjL?AB`XR1Tgu`q4*k1mr%brGn4 zqL^Lb^&ZlzoA}(KJ?ZsXnc=_>N->)-$>`=dCC9cq+YW-|{XL)kG8hgX8*6tr)t__t zFWcn%5LjCb+oG!T&%WK8(rb8cStYQ2eA6&#I`C&INH#>Z;_HRe(fI&diFA!13bDLk z?cTS3G?%lgqfUcWLyEj@LR59DCi-a%vL9BUAB?ANHqPX{+{~CPsX^#8!qOhhRR$X9 zgm%AQ>SmQ#K9NO5Uul2((2D+9LpFBs;AI;1a*eTeF~twzZ60q5`5s;HqVt$@-yWBT zqvot>57=ePGDw|OXTD{`^=);Mnh$!LM~J6j3^M!Xy+{i=M=d)MKj66V?SMDlEEK^- z6`8)8cS+0WeV1>b59Igar212Ip}0Apoj0}KR~f-bHOi}ATUH&#&(Emuwv&kPC5+ee z`zSpnell7<#qb!QZb(9vY(#XcOe22;b@#)E&86ipsitG^zRJ^0QvZi{SBvHR_djf@ zGSmEg!hIz%sqzDj2OLROk6-xVN6*XJn4dlhxcLT^O!V8E)EP@atyMDA%lWiQlR|#9 z*G-&!77`r3WZ_6bC?e&=q}cMuJkGCroga7+RD+^_c@9Kcx#2>8WFOz}`sgl&Hy~utK_|@MzNke&DesHl|n4NRISnM%y>5 z=ht=sqni27Y8IVa19R}V5Wk!Ko8v8)BS2W{O|$O8d2HvS96)hHU!}JesSyY1J=-VJ z1}daRUlsKv?)?A#y>{ts!+z5j4@v$lk!EFGERJ~-i_v?XH9J>EJ>NdbOY;V&UBI?q z5joJC(JX|oat1AvT3N5I{e;=vy;R;x?0;F`x2O^+tdM41cI+>$B`UepB5Fzwp*q+v zJe1sTk{8b|wq>#3#?w+wI#0PbxH$UN)VFRNeEjzk*^9pyhRP9>*3(=Z%6;vDpZ@z5HOG2?VUknjlE8M4e_@251ef58Ciw zWJiXkHBYvj!+R)xnF?HT*RKu5e}B;k5Fw1emJz_wKe}%L-x6@QsHr>_nozMwYc4m+ zlWYY%Kjs?*?eWCdFI)7qNrsvX09O8J`Fdv~+FIhol(?t=-Fxj3Pst=FWlGpHy2 z*V;@Iy4xTpYr`I!ol>}MV{YuYpu|#@+Ez0j)kIbqMBkIfh`RcfTUMPe|1*D*outTH zO}d_HAjoM^E7;H}Ha;iRW@ZAjyx=G%z{fo_8jg$Hmr5Uc4G=G!uj(yij*agMJP{e6 zRflN>EB>lmhBV2j&AX}Hlz3v9gKYNOGzYC+*@!iBid{#+R^n^Y3G(A*7Y-THv<%}g z7%;Ta|Imf7#MSI~v=qt6{5U59Q9f2oa)`pYWCe<1kWd(S$F1Zys6)8FyQHNBcXDEy z_^Bq!dVZNQiz%Y!W5q$sA5K_>j6?uTyEG}p%kai6PA1Z}`MJ%d$N!)NaXDPAhFHt^xY%@&Z8^Q23#yC#xf-9pBxMDy$B6F8#lJ2_-(1gz}oK^Lf7<9_DsE&Ja>iJEVy|0}|AI$?tt3rT9zc=Ud~`Z@nQFeq zCQQ%&9OK%UwR?1N+377E@Rw);hXsO*qQk)5UESRid^?D->jX)^{ZiKXBL6>23b6$9 zX{Hz0D$vrq;QcOo*%b<=aPf%(gI|X?5^K!eT7iJdqtk5Db9=u>zGmrjO!$gxdCQcI z+mgY`iEhr&qO8DJVGphny;WBbI7P;wy3$&lDENWyFeU1anh(I~%_e{FsIK98et!Ak zD<9f7?^&vnWm^j4p8Ru8-rF(#`eCiG&^Rp~qVwfeVK^WY^b+O7I#lfg9ac(GG&xoMJt1*CphOn2jvb7o$%yud|p{fb5V zLLSPziI}V3vhNQU^2eVp2kH4cjZuwk`6-L#`=fp^(@@&Uyr&SA#}-yfuO;5X*aPC@ zCmzniZ0rIi5nZoX)WvsXW)4@E=UmIJ zqF1kcQ02Q@g*&zbD7L{oAam-8UQt?(+Fa|(xM=f2ZwRu#=EaP<-8R)^6ze{NUEf~L z$iP#W+xs|4KgP#@3Lx|a{b|oJA^TRG?p0jN=2jTHuitrzrn~sn)=}jzNJvPd4ejqu z|4jZ)bs7$noJ{K~4Qc-(>Ne}U`8F^l!93{8)@w(bOfG*ftOb7yrx~AtRNK_E?&;!t z(|E3*5lH2aid#)VSsW0bi9KE0q)?FJge`@SJAiHQFOf_P!umPlOo=DVwVi#Yflkq# zuj;+DytRSy$ZYZ%B4(L0v5_x{;bMPI;4Zw+3RpPW8krJl zt5>)foPdRqUx~)_JPX2l_;oS*X;a3j+YUVfFJ7Aw@s8%t5jPqOLezb^pxdn+lN z%wK$`Aay%9G+agLebT@;A%aMMm{lC4X}(Bkf@|KaJe{OyP&sadslh@jar`sAZ_P6&!Q9zpGi!8XG= z4f;up*A9j&c{}%g!xb08yENiaCEsR@M5b^5O!(rvRluSB(W@HgEi~JrV*gyLRnXDP z=@yZYVUFos%i|<}$mU<78~+R4#7c!74(Hpysjl_nU%!4ha7BL1zC-0Z_xqS^8PZ^u zQ-HKTYC&x{umDRB-|VDu%_;v;MJd2clCDziFxW7pA zjZ@E3>3(m`;;-%{u`P zt-@-m%x=kmUhm2CwfQR#2qNV50k?rbRGZYmUT<&XPsx*3vD{By82Me_@zNJ-0%Y+y zbqzs14g=yPeb{tU+dJ&hJ!VJ7`_nLmug*4rjFT!t*DN#lO;~IYPFn^%w|@S7B|ss- z0!C7>%6uESbW|?gekEr2VtGH9pNT>95;vL3C`V4bH2YicyIyC2W}!_SDMGU zY8Rc$@+}$1aYvay@Y0~qEFs+M-M=sm8md>)b!>Z~mBVcijhELYs(*=$LVpF`2t3mR z+5wVMPI(KHjwkxNo!vKz*O8rj%~EZvm(*z}?7aGYd=w)ui;41Ue&kcvqrwwY)p%9K7cr36`>w4ug=}3bo^K14)@^Mmb0^9PiBGUM^G}5A?9uN&d5VktcC@7eJ?Q-P>P*Y|h#_ z40{ILY(be%_*hpu?Aj(z%7i<9PfyrzL9J)j^{9;4&hbySbW^_KO%3 zfAq65f{M}9_KHNM%F%cZ72~kEZ&%Z$h(*pLtAGv#bC!|o#k^8rIh*H-R_~Q?;Qmxj z{BZGauAy7!YJxehbj&o3rkDMBZCA}A{}PcCRD^OuVOn=dv$Fllkbiy*1cGPY{WeB4 z@C+<$UC!cq5DJ<>2+7>udUk2+@=Ec^1y&Q0LT}41haxY;6bC9p_(5@Uy>0ysWxI z&OzjMX44R#cliK+V6qQVXit<~BRlrn{!4TWJ7LovXu{W6J^dxadt-obErnC4q!tt| zt*Yx@D5obGE{@O-9G;fH<$n@;PH=+8b04jao%qcUT-6n91&ep@nX#KwwHob71`#MeTa1=i&Nvl z6bzWgXB?q?(*nJxqoD^#1tCS6RQRr5`g{|dHYiOdYe2B1smS^hS}6D+hLw!r{7HJ6KQ}VpZKS_Qezu{e0$@%~Hy5(v)d5JoK3yg*}yxjU+4i@q#Cwmy3!Kx$A zjXst!EBk?WVctk_yem13X%Y?hF@8V#as1W2Q@$dza85g$*GzoeDjv!r9*Xz=jA=iI zdwb)CWtnQalk^p~SMpYme9Dg~9AM-xOiJMv&`g&3JpcMHQI?NERK2Q<%Z$aL zLjVtJpenYt^1i3^{krqGY13Cob%8E#q;XSzNy`~IV{nqbu)6`=74XS~s~g_x$(oFIwx3Cja16L>RW9MumJ zkMd&~up-r6;c#5fI;T}RGUp!OF@QT4yzdEX^(1w*_LO%1ypJ{RyFt0!9Pg4hwR?R@ zs*R{r=Z05wE^|UHY$qH5mt%qVL(*t-0T!sokcSeyvn&v?vpChm$_#>rx`p4YEk|BGk@xC`cndeO0Lm16H=SxR=4&0b6SX z{xD4Dw7>%Y63Gq>udvpU-t;P3kbgr9{3Xi91x#5YH&v9|_lAJltPj4Vr&MG;<;Yhu zzhEh-uF?&78XHT+Ny{}iSp`j+BL26f@88RH^%|o#Z?4w{>*3+BVG<(lB0~z+$DjYR z>Hmibaj%I8_`}Kb0gtitZ!eqpPlT@@`VvYjX>w>ytw+RQ{2Vg7l=tU@P!caCrzM#^ z;Z*eWbTu+ee~IiytPiE<9gxk9;9Mv@$`7mF7qt@j;0WB_wlXcuv_HahJpJ`u%UzkE z;gK0sCK-gFJi)n3uoE~?$wB=HX!)JKyMLUx+?r*@9l{fN5Q?qPw~D-e4`&SEiekg^ z`Fm#g?>PI1S+0+be_keT4Yw%Y_7T{36HF#7c!t0x_B}Y9Wa!C#f8;YAF#&51`8Do1 z1eVgS!AIQFX(S}+f`zz0Ikq3mQ_(wY)dZ~ONw`9*pj;X2tr@Zg24!4eS&@Y3rXUHk zeF6sB-c;9oR=IM(U~F(f#h%1|S^DKxtE=6xtH1{UFMPl@^uS32O>0m@Ql9NAh!FHp z&L0ifte!DTJDmRfJ+yqKaRBnje)dQD3-jr40bM%brZ%<=l&@KYr-kXkU!rZ0`}&LX zy=1oadTBnh@kW7QI#_|d!4Z7uk|U|LJI&Ykps(5d-F0tbXgdMD;R5BeuaaIeJ|-PdQKJPp=MGYj`WOlQEI~ag5}3|ds|FJ$tL=7Z zWxr)?T74VphmeLmSX#BtO=RcnsuC&wpcKO<@G&E-SNe)tSJio_s+_(mQx1ISkvFjsIL)Q$7g3G)w`Yk#kjVRgdo< z&3;~W)xly>uGr?)w@gBF3~-NS7N&bS3hwot>)Y|&YB$0bvfEcw5|`I2b-@i4|=MF+;^<5 zocV*pmuo2=Omx%;=+vGPk6PXR=Wt?m7edX~nY<2x&fNyZp*DJHUs3uKWi+>+2Gmdu z9!QFa*hzOf6OLH#{opN{)s{Mrs5@f@FIX%|a07ON*YaPQE|59K&0*%hqaR{Ua@R_y zy$eRyH};JW^z|W}m#hLi1u?D2@oB1Q`!Loa^QA^Sa~2kT*TvSQB|bjPgFtc(r^1OA1<#m`QuECUGQYOFBajwy=j1 z+OlzRQ!?nr{f-5C$N;LY!l3p{@&Xsa_fkQtOM4d49aqhEL$hXTjx$Ovnb3@H{X&0L z^fdqZQw-QbzH)C~=`emoOO`&zm3InolNA`Y9E_5@y%62Cumoo>$bF;iGCD1hHu`Ye zr>xBKhv?9tOmc=X-ti=$ywuRH%}b>{hrbGYuMl3L#5R;9b>s^ONoOh{`)mhW)9}Kh7Gxs z*xNzzs*T|t^a5*qplQmo<1@Ln7D3ZJ9ax?v&ZfyXms@ZO@hrksBh&&hkH1A^{Gs)X zb|a6}`*`{sO3A7RJTBO_*@S&tAs->&ufc+s^*Q;*5~tZmXyMU}=; zf1!CHx1d080s5Iv-j6j z_jt%tzdt9Y?&O@8b-Txd#H7~;WhxVa-D@BrbHJQK9${B1h)=<8aMSm>D=l^d%Pi?` zTaOz>t-bc@-XahgbJ#mutfNCX!RsrTcc9vO`v%duPAn2;v(x0`q{p#;;HP1fyD!Q z|ID>U%NVae}xn z%O&)N7or=$Dh(QNg_tq{d`k_k)JaEjkKguxxLQixAJnft5aoWpw_}>}l|PDE_MuY{?#;Uc7)0h2khe; zJ?RK(o2heBKU#pe|E(ebPr`mzW*H<|K^ z2pF(iL(gFgTneLCjHV)SV-q|n2CMSy?f!o0R(1MpJ)XPk>+9I&NeN~N*|ok=DxIh_ zAYAoFcndzXE^s+-5@d1#?J%Np+aeS?hpZb11Z41|WfU~T71LA;JHk3n6EGOLxSn&l zl>ClXT+frsb=j5lX%MM=(y7>u-XeYCn#7d&Tus%H{}d+h{Y@%-#8-9dX1i~DQH1rh z>6DJ^2jPjvIm&g0G?NgaJh!@6u0%h*vJ2++^z=V%;5seqzHqwfL9&;z{cc&wE8DBN z&U0PdaiJ2}^M_>11h){#BZm+ExLYs-S%(A!PsIt|QuXS#xZ|3na^1wpM<+tDQuW(f zw=V1dkm9CW%>#)q z>1rM}!Z%IPyLbJl3*;B-Xn%?}73kQy?%i_e74x@y{k5q`54W8AH0{A7Jy3gliw>(I zOd85-SoHBCSmrE#%6mQfhu(k-$!-r zVQOcS%O3-JExmXc($uUc!F&8w>t-!9-EI`b>P%nD2aP1t)ZPqjzQH;$~3EpJD>(>1p!6^eRfQ zpBiIVJ&uULi*i$@-rPx`<|h1EF8|8t^T{@n0;|S`ARNomt6CQ|gdikqRNzG15D`KL z!Pt3D&yG9`Z#pnyN%EzeO>+13apcsckVoaoe%hQ#`sVIC^w?d&p*AEod(9vOL0S6t z*DEK$BeY@##6kO^x6oYMExDB~xs$EGM5eMB z*P@0N(4J4;-POS7rB zA(#LaL5-JGuGf0XNnyl$Ls2qe6IRyy!|g7^WgKe>8~Qo)g=>P-!y&389hCt}l5QbD z>`;gvcKw9X;#E-ZOr1n+$e!e@0MKC%d*^=+%Gz7_}<)i-3`>zdA z(hD|q+Ah}2qhz&Ox$LFCyd$DfmVE`w3!k-56YvCbu58MeVsw37LXz$THnV6;#{>Fi z`{=>12UF$ZVd^rc8BHnX(~_+QRvQ9!X3ug?Y=);|MC?p`$AEW_FM&)K7YSXozwkv& zu9pD{5x-~kw;dltj%8Zb%j^>r8bcKleOF0}rQ_4#C-hUk1*EkHrWA7No%hn_LdH#X zX3=Z1R$QNv`jcYHS{bL`LMaC=t#`XZ=F$NN&i8`vCn&1!T4cu=*8UcDO&-!Y+8hp> zxs5;*txdRjXm)z{)lj5-JqrMDaIExLDnGRGBCTcmt}Ro&CU#Yk{Gyc$6vB3-NE6Jy`VLDqWeo} zEUf501HcLfX(Ws8`Ab9@c4B92$tuIDHaDcRe?^_%sSp^lNJ)%?!aVmKn9N+V6E+jb zf^q2{SFNS7Pt;gp^99lMqH{`V>%f#GyVp$%TN4EVE(E3}N{LlLY(LJqul2gTvyGwF zO$%w`IOEVy*98}qk1%5wZ%y)N|l z6iRzcd*N^ZtVFny|Mn=oZ0d9HccZ40ee7y(Hv2tanD8Vap4Ot$UvfPqaWc7}^2;Gb z+@s_)8&gRv2E0CYL3LoigZ1;=`%9#UmR0b;sV}hRqKYeJmuxqezq-B7JW;%f8*Xoo&4>m0XBG z%OuR#{|?tuY36sr28}$d=C&^8s_`1T$*q7=AB!HvY$MS+qJbuJ*$xh|*bZ=?Y$(X3 zOOaq4+LkdZYjP9t?g@Kj%j;e9E5yw+pEx*;mbc*)x~p`aW*$J!!#Y@>U3^`m^GUvx z^;rC9bPf@U98LJO%($O%j^m!gZG3CFQQ0}Fo!0?B;&E1rq4f)MhXg-%!RlhSj83Mm zqYV<)<-ivDhO;t)tYEeFOHgRZ9bc0uns%nKC9x*Np;@H@-ZW+~-nyJ9cbRi0@cO}AfWSS~0*0VRnc z^uG?5;6?~hUfVYfp3g_O&^6u4J^1^xi6HW^=pJa34Cs; zE}T&AVtp{7+~UDVaF=)1tx|krTcN+^fD60bqG%{EBF;xoE>9Y*3+D#%iPq;&%+nbZ ziD`)O(MS|wXj6wFkI!v9;G~UwqDsm#%a648x=;9i)ITo)NKPiLEZt1mi`vJGiN-uD zQgxv2ES+W~kwq?P#O^Wsn;WibXRf9DDzL7SzjTxtVxv9@2?+ zcg1WXoXFm#TaK$_v69dc0DIHR7wQIt%eYxR?}}ZyrqCBND?h?6kq^@IDA(eT?dX|A z{krl3t{VmdSfYz>K+g4jAZNq~RU35K1=yL8zC4`29qLZ|4y|jHt3RH+Uo>`nq%aLy zww(2Ms{?OeD@#rida+kNopqof*PnX_JuVD57ooFA`k|m%InGhtrXJ&CvIyy-QC{6% zP%?@l=zFkWNK8!p(2AtC6?LmH(-^RBKX^Sn7>ubhxzexk0ru=lNv9Qr7c~%aPmCp! z`WCrle3%510$J`480Om%n4Qq~y64s8#3#;a$B;$>>rO|{8I|I)PmevOyq^uqGLppO zWzJX)SGVQ(XhEKTUfnKmOv?Gv?TfIOh5EBUjLoy-ZG1b@XG0D(pWV2li$0&aq*A1b z<8^f`AM-Zg6CsZN2-3U%5cO)SaG=6LIl`pi^{d)@+C<64F}+_=I#FdBFp@3}Lve!4 zbDDd%i3_%k*G7|m4t)Kv>O*#yaqQo|#sBChluPTWk6C+$d3n9%H3m4kN`{=7EH}O~ zjg@m=-k##)jKMbW$#Bg7R%BP|a@~M8MXd{2_vkd7tvr=g_=G=a+T7b)1XFr_zYD)( zcuV=fO6)I@KEwuPmluW$i5u*mh|5U7tegpq7|GMB(Qb92RA6rOcSXCo%CV%m?Dr{U z6MqyZ9FVOA!;aZj)03ULQnrKrjxKQQ+MgJ+iDPhB>kW#4#)2lqxm|5LZv-2K9GT*u z*)F#Vi0G8{xf!*O<4MNm@lhw{itH0E7~OAd)!U-77v?+vC5OY#LY3O;S%;+Xh$RX< zyhZ!QY;}d%I}!{$0DP)~fF5x;n`&cp4N6ljJsln#q2IeVOGqtg*avIJSE zqpOLPn@2cnbDKKFovM&mL}8JVzk6943sg?2LC`HYIV`HPsUIcQTtg#eG?Fruqc8Tx z;iLGBRV&3L3gowf+tzT_@&R#pMsa*xRtMIOD(H87OhwvN&up<=s{LSX8jm)LGwVN= zc&eIz>`&-JrD^|f+Z-}7P!YJda~SBi-%@GtXE z4f`{($^l1PXNJY;8u=}<3aWyyQD|tlV3J49Um_UEaLboG`|8u0qW!ax*yxnufmp5F zFuUTQ^>1yMWQ~hl)~3d@1jv?aV0sC5`+juGwD0jA`BU)V9u&VQhb0h{7 zv0F~8E8uT@Fmt==Hn)e>1WF(lHaqe%)LDZxJYusz2W|y=Ncp>AFPNs(i|#e*2lp`$W(& zO+Bhbi?`NJUj5r9^F-m8XfRi~D095oEm$~Q%p0#(Ju81aniI_5cDkG}FW$rR&fL<5 zYEmju!SgV4bo9tSLu+7J8embpuPSWw&1{`|IV{QQ#fHmNbBHJkcvBo0y@kqbas7p` zeQ3*REA(8@**p-u*rn8uxyU>P@b8T$hDkA}3I(uVby9*$cQvs<#PW6JfKA_*?RA-Y zBJ4um%2e+xq|ro1Cx~m#b|2)kEhiu&ELs6z$g4IOPasrcUx} zMmNr9zi*RB-H+A@oL`szPQx3_-z1PAR9W!?UutW~Ved@a2X#yESXN~**AtP-($0Yw z)W4KQxex?)iFJs zA1r+~4puUEQg&2ra_;1`)5;KQc1@61)!8)TcL)+>Zkr!q6TsXO1Fb+r`LB|+5>6b2 zn4fsyHj{1t;#KI3ajJQmzT+u_bUBV0mn;1I!ESSdi`En8x$|L}G-G_( zDAs}7)vW$65f-hlvbjY0H1AA-%lN{TwY8BP9j%DyJC@U97WP7S!T_o&I=|*O z9t!MMoG)nT;v_y#cBVXr#~^UIN+f*rw3u|gfZk% znx5Z7&8s)PpPYMJmHsn67AE;t%yje6&h72Ut~@`K*^Sd7J3VuYxsX16N(oS12zj9Z z^f=!k2)49JSgwxjUeCtl5w|t>&hcC6%{apD=qy^R91+7RN2DSXE15{_J`FyIu zj~)}An0ISMZ+G+UCw;~gn%l$4lDct?q@V|U{C{bPKdASF?V zm~eQ>a5-VpJEy<_VzV2@Awl3il3Yl3rJP61Bc^{8Zm&;YGLxrYe0|d_Rt`(v_k;Uq zNK8kU)o&5MxPIIc<=}K}9zsb#Im1ksO}qY_Kr8EdJ4fvogv1MadOIi1s*z7+C0^3u zZHt>PnQFe5hYs&FePK%wRB-Y>OAUk5vo(AH)P#0Rm}k(;S*}XSEQ9 zZjOPdr;(`<6LwdK59NS+Mm&OK>G%pLeWs0Rcw;1m*^g5_&+3MPd%8VnVfM0onJL#q zCD%U7*3B6~%m!t_n~^>huWw5#@xe!?Z+YwWC1?m~FuY!wt{vKubhH1270_#5YvzZ) zCKP5pd_bM2sKNW&2W%zWW?QY(%Y2E29TfJZl4$Vm=sX=W;-S?qC$}lTG-iJOlS`Z1 zKetQ3yhQQmn_ZX9(6y>0{cyHzwo2QLS7{oJdb$++8s*Z^6`C3dmHVA|1*h>fMMM5S zZv@U2YoraaE|oo0eu!UXl1gCHaLO!Q5=+l=>#2(E!Cw*^I?;d3%QZetRv6*&!^&nx z4idASpwU)5eDfBEesiG>a>O&`@UM=(jT}zfL9~0>&)r3c0 zFenc@*xL$nf)^jwC;8570cJ$-0pJ%h??Bz;dGNDXdgpYoLq)$ zIq?biYbEzb2;0t<+uoK=jIMs|%CrGOlzMeWp1WJ~*qZPA$Q_O*FHqhzRnb80bQFO_ z#mz;>&vWh#$JwNO;?;w;L6wx#;5fh=E$fPlP?rmu^}Gr7K2Qy#_vl%Dy7koZ}iUpB60x$Hv6oWR9~^CTK(JlI2M50Wf0%m9pf;c_T$>7|g;Gk3YYWA#xYHJjyE_yQ zl0c9kLE2))LUDHq?ggQQhgaCxZ-%7iD-I)~GB+9G zTJj`2u?X9s5xe!zBJ@xGiN^Z~x8uNZR?^E$R1%$n=mKtt9+iE}V|aXKdb~n0COp)N zJm*6{x2w?UwfW-q;_-tub;IVhW1y>I;?PJldrP|Mx#?!*z)tJ9qSUFy*jM!?kry&V zLsS^9($+}ke|RpffnV09R~|RG$6K5x{5HRrJP#-tGz%D_6k$keYEARVaQ}zL&%1_B zRjbl`1Pj3|Ar|O74PWd>>o$GIrt{I$aH*JY*}Ro()*vcV_~2drLbwJ=*=Xj`QBxJ& zi%woTP;21%;`^}5%_e=^Igbli5lX2C`+m}nj@tF3a+qeEQH{P-gbIS@U50y4Y>_pK z85uV7%S*`ic9>0xyaYKq+1h=Pm=sPs^a_lYFdTP2NSU*VwdJK;%JJ4yym>*|6x;Uu zVPdm#;AFW3>B++!Wg*M#%=^;HbCv0uwDX+I0lYKg z%awMTfLaQNBi3mRc*<*ZJYwNk0oRgheJSOgIp?9y3yS}|%*yGJ>9;>-U0|W2wGtOz z*2H6?m^h@sp)`bmJ`OUZ?hAx$#O0wd^zcAimnoyc^)wo~FJ=L+@K;kcBnPt{>% z2j#Hv3H_MocIim}PN5FYcuz81RcLFGBvP3F04wv+?6ab#m7$eDD4L}pt|6|rjY`7F zlUaVU{+-96sVBdM(SvtM^o5RC^C(TZSg@1L@K1Uyp5vWUPe+mS9g*O>MLNgB8}^pu z>&)^jRi>Q=j~=1>mh#y_9d?19Mcu7wF62_E_!)c3`)$RX@3k(aqIn>UT#u-Iu2{RjojiR3dhGxyLMI8<43YH6b=LJy%vcLxxFH@&8m@XgH!Fke7#48eAl~ z)c;X$n!yn-dP-=}PBu|g`s&r^$w#e@8XZKw)NCk~Chb2ukzGZJ@J7U$?px2FO+ZFV zg20(~22DzuRBlhwr}au%o$N?|d6Uj&a}L_sM3S(ie_z4~-QmwAe09WmGc0cz@ORRP zH>x*pNcyBTyQTW>u@t2+tX8b&lzd4SIIL?Q{GLP(b@BqwP3j!^?+pCIJNJ%Rjm}n) zf}zl1Qr`btr^;&r&69YK-g5ya} zqV7*nLu8~49}L52A;9u0e%eL=rW+bO_#0{)31DEDlscy;(Vy*csuH*r*p*U1#IBlD zPfbEacO4*hck+Ml$WH8+kPUTcv$c?+k%5s$8^xo$sTk;LGxo#6D~f5~8DE!-mtmI! zciMh_UIo&hc?VT^xpth<+d`al&b4jAF{pX$4O+Uizxk;v=tiBX+0*J7wN$1OH149q0JsY*_k!G;F|TS}pU9&&zs092f7_kc>rH=`$&_qi zTG?l&*(>g1r?~MNM!ylI@v!0-Me;vw*}S;e@95guhIW@{dwWr*O^V;sQ=DuI53AaQ-*mcek4?RPhWuR*D5s4D8%FjZU`)3h^w2ku_!rI;v@I`5(~>$OL1a($h=&?9u7%eygz zbtZ@wX{y`*QDa}$cq40zh<+KwjQYi;2_|feVl~eCQB=B>AK?{`_pZ4byL=s5{i5n25UQSx1gD^NgIcGTs0blhs)%$KI} zuZ~AJ?y@>j{uSAxHt{W2;04B>`>-{05GUB#WfUf9_jF$QOjt7MYT4EGt|&;biV9sE zzN5Wfy}Eh_Sel*B#`joSi@FS*?ppjKK7BJc-n1kmWWwOh#Tk9N<$jp_dv9r~ygM{t zHD|Pa^Tj*P2C*ygSjUpq9Ci@a{^vOul73kY47GdbQ>DmJ|8jj*KpgupX}7(KWrQT% zL7@Us7hET(ftbFFs# z>BCvuH%x>+J{9^eZME?5l45#a*&0k1zRX3j$Xa!5M8+$@Mw}JSb}5=)2TLN`|7shi z$letXeIJwM6rS+FW@QkkF+Np3z?+z1Pz_B^da)_As9v$E#9Ju%^Bx7+*|(I~VnWEI zZ4_THkHfg?niI}0UZ+O|g`#|+DShHbcWO7)uf4RHw_PW!`hp&GoA>|SOKy5l)xt+< z&lbcolU?MBW&Lx?_YzCo$x?Iw?r)&l*(HK}0+gdGVos878q@5J6oo+_#94iJ5nPW{ z%4C=O>-*z=ux!pguKCx1(a`H1RxDrNJ-XG}6cTWclNqzW|}| zuSd8ly+va=&O4IN5Js}K8nD8}?*+Tu2OEgl>>1>&v?hQ5DJN%CXPl$`gvd{Jq6%!PB7Ya_pfhrf3(k<$i69 zQeU`XWtHLjalm-W!&}vVxiodMmiK~Of}tEMqcze>#3Njs<(DX~W-wV*ae_Oc)S3=F z5kwthCjumCHn*pbecu=4Ia!*D85GEkm)FE;ILo4%ki0K|oc`o!QFiT2dKIJkpY6Gg zaz%j3%Mw_xWs{n-RXM{Von*Scqq$mll)MIR_WkcCU;p9B{%#6=?i&=1g=S93KD+sc zN8UjhNArs3PYSZyHT;LE6j-(s7E=SMd65-Ru2-Zjd)yGbiHkI7r$}ojqOiW8++6NS z;G&cFS6Q(a)}65K8J~}7j45Y2AiJ4M8Eh)^b1~(Uld%5sWTBQ+u_4f$GJCkK#LtD> zTaM#+BiKC7hE6hj3xM)@QQo)J;D;fbIH%m<+=_+Ffq1AvPYYppR_(6n`8OnjtbK~Wu!NQ zxEI^mir4-!?;W1WoU67-`MT`Ih_PNg}LoiXgnJ!L|w zUZzuATASgAAKmI+01P;-;m>s6>AxbdKbIAdEU;DD`}FfGcW_IbU`vd8h}Af|;Qz~9 zJ<6W*B@Y&2`aI{~yR#Y-AL+$uUUjK1q*qnn*^72)!HAE^qy)V2!#khZISeq$I^Pvc zI)G$*OySNWF+(q&-22dy)ru&Di-@m+`2qsI`9!P@335KG3jZ?ibW79)(}PhtkgGhSt6YYob)uMfyz9)0w&{ ztHm94J3Zhhhl3bevw#A-_$MjDbbo)-;2nAAd{^WUOo^kZn;JNXhKatpwVSC=^CBOq zXBg*{>wx+vH*NGLoqdRFB^)ts&Vajp3L7|7?+kK5$E5-h>mP45oPibLe+b&D8I#T> z3KonT&(gSdzBP&_b|4`*OY1Kii>j z$;uR1Z%e_i@P#HupkeXaiTOUy7;^1$@6_SPKp(_iMB3#0rARDQX1760w%*N5$lwU6 zIn_Inq{+g&GRZEpU#7SFhzv*VCA<#+eb9(jrz3TvloE1(n@S=eD#N4Hm-*FgJoAXZ4i`kXT|$|Mk0M2}9+#jxvB@rWLz-6j3+(hjtbG2ni--O8i7&q!8}D zN5haEcr@gGk4Y<)lQZ`#xh%AEDoKPbH#AHYIpZoWi{W2Nd7<}>6LOzj&POI%1IRRMI8U|*egBKLNYj!6WR#4MfS|I;aV!K zGP!5qMtalIGu3;xSH^59;$8Bg|Ds+M8Jr zjzxajP6^{pabPq`=&R-rGsl*?VH??cAYf9R({nd>kr*w+Wq@1kEz~Y3ix~=;2AGK) zWzRNs!Hu9s=$`_xH z{yu1;v@5D{aE3)DrukunMMb|^C$xw^mimGfogFs4bpwXWJXk$s+6N1pBs-X-zu_ zZ_nAI+Q6JP>fNXrQQ}pP51VSdJZa{A!rxl@%`ujP+iOnH95wRO9Ui7BW`en5~Mk=KS67dzN& zO{)5dcgh?b#4*>ULiv7qUv>F9GNtyap}ZZMf>FMc4|sR2b+#Au%Vm{87_qj$^#&`R z-wf+@F3FDk~L78~KHD!N9-kl^dOwy{}#O(bZTt@o1Lf zSF1rVt<9xbgqIRiR6CFH?}ev^PAc&6Im!Oh8uurZ-l`|)`NM~YVNvx%$}Td38%C37 zof=c4sxe(H2fzc>qJ7w6cE$Xx-@xVsu2Z7VZAK%1cQ0y2*MK z54=Qd$XkvK+}G9g!Zu2T*n4DfuCzAZ_qEz`(ZQA6^&$o^yb!{v>5rvn9v8$4%G% z!zZZ_aO6)(!ZcWlYIHBs*m4?cfjqHvjObKz$LW5mR%v>Lnp>*%8@&`Oi1KC?sVW`t z6prsfKfa5;n#Nbra*3Px$04K_k&Qwrq)y_ z+!NE6W>3Cu%Z{%Hki3{_?qN;f?}XoboJah_L$jR~^|cNqE`$4^IN-#+4H`eIBCTov zb#>?i+}VkNkMntdjA_-z^_b)qMpg9*)VdUb^AnObx}H%QiiNuhSjVc}jS?`Tf)Ll4 zi>(|7W!IhQr({Q~DK z%0k(~JywM#h0k~e34J*xO;`+dH*c4DY@Q^32R5oW?(H*ecEsO47EJTTT3WQG-TWj; z3ZkF2ag8YOwwUWRqMO<`d>aMgb8d6G^EL>1^I6TGTF%dC zG@tX^9*ay-UUaOoHe;ognTq^R4n{N^zIR-fo9u)=lK=7%VBDkKC7k6;(OOS1i7hxWt4y2deYhB7c@<1b`tPMLe_9VKpcB}xu zE8ogqkwgxg>1k+W4Gjk+>8`=HN+<`K-9sREs&wbjGzMSblez!dTMdD{d^e6sK^-aC zx-6wvu@1jAP0on|Izm_*ICMy&mMY29G&1otvw}cnN^>50Z{0b^{bLVw7~x0(LuFgR}cNk){jp(c8M!ICWC zz|~7hnv5knJw~Eoxup|4RWF0sj()Ra(JL07oDxYoK4GBL$)Qa#z5hn6VO+1=UYQ68JroY^``8K<+gJRvmW6?hmsqv=xQy|q6sa9C_qY>Xx4fL_? zn0`cpsZ+7YCSA|$!Wc>(`8S|rde|;^2=v@J_-ml8n1Y1_VVjU{rNNSPdBjXh5xit` zDMl^8)#n{@K$A;GJADP~uP8f7Um|=`)(I>KjUyB!U$1rqzmGr}>dzs@tHCe1>EG2M zTVh|qS!1|LO_D8^!vrlRs+SOzs`2s?%1chqre`!loGflly%w@N8^M=VZ-Rz!*(mN2 zz?VNa@dWY-{WMw;*^GzPwI+S`t>v;))3=`iLGvvWVsjE%^i`qCG=-||4Kic+iB547 zwp|Zg-n+>gP>22C-PL~VR%g3g=lt{qjDdYXK8xZRJ&As^=ez%${x8ipxRlJel#q_) zqsn)BlKA9xl|mEpU@|=J$3NCS5(|@+&GClqIqOaz7VwouEvqqfG^Kcbp!7;XLp#>L zG^!`%R#YO}-{zY@(!=PLreqDrf=Vfvc{z1KFh-gdMveO{73Vl@Wd!fiu6IsR(ZyT2 zI3&oF@+8YqN+-z?Xd6Pz*&?$oqeInAd`?pbs;0)(fhb zUM0&TnvRB26JU!#EDN;?TD5e415*owJ)xYc3I6LvPhQHP%kT=9T(yO4fXFzq0Uj#+ zoXzoibp5$c+VzveQh{v^x>lgo&hlOAKuHIqr1J#zhk8$&*blA2n z-s7Twc*juw>rCQ({(fxdGrS5P9myI$N_aW(P{?oKp4n>?o;r~_;R&U2u1oy7nF!Fk zv3HG@Z`WDlH)l=fTHZ`epU*9|Gl;wfl(NXFdc!$4YO0#UXd7uABn{>?d83MAzh97_ zuqLYWY-oIfD988PmS+=dre9=21W~aB83Bj(30w6~RvpJ6Fjn}HMK@u0>F>dT@t%~v zO{Lvz#8laN)3GCiA@8+IV--*8U;kS}51LJQ>QJMe&F~1#JD}H#|ET7PHHm^@+cH5a z+aHJCIq?TZ+u&-7o;geJ^QxqTvCTlH2+>WMbBOc{mwqCDi%?%LF;2DMx8g4q_2U&e z9aH~nF3R%J_`!pTd!pxw8s(3_|8L+j|K$zg^M0fMEwv;*O#9y?=8?7U=@c8swFgG~ zujnRSFDt;SLxEwd`|Y-=&V$DcdA=~l;rZqk`8)HzlJ(otl$eVAZRZ0i3DRlaJ58~_ z3ST~0!&dd!R-Ki|r}Agl2iWyWZNSw2G3z3i<)RiCvo=Mt=Z;{HOaT7F%7hdU>2((A z{F+#K3R`~ZE}Z&~F2L@@ka+W|tj`WP1hisu`G?1;NRo{swHy?_rA!z&{mgWqIxCZa zHa+=W+^$YMd|-I6vuxw_64;Xwt-JFN&p#U1dQ8|M%C))+YW>X)A*U0LdX(?+k$(EF zTth?Gk4Ow^Dd!|M`cpi##ap-8r^9@P@#D$aD~CLPeb3q7zf7-wRC<+N5pb6+oL7ke zg`o61mi}Ge6Br-Y`8U!04G7j>+l-gCD-rjSo?eBi^eh_nqm5A)gA*9Z@=9`OLO$=l zo|Mcvyj$|d)AWEiSbL`M4^dmG{xfs?d6fJReFhbc+{ZUw`nL*BkO`H3({vhVO%j$Q z@K4B)7jC6*mEjy=ZcC?;Q|K_n+*p?0vHPh@u(Cre?ZLsZ>Eg5o#Tg$LtpvnhBDBim zS``B1Ow$CJQZ!HsbjbTw>`j*Lxa}860wsMn;wmF$*6u>rPX*q^T783M#)~FQUBHRQ zhNzK9SVO%PIrvGUh2+n58tH72cRJGJD~E*?YyoThtuW#Z-sM4~UMgd>(YhN;*B_rO zKwHD99k?N?(VfSpa#GTrV%fEDWG!cn)h;Mv;3uN8z76o{K*CdkPcTr^L$SE8mAXgS z6Hto_J09HRrD9EN&b`%O`=qa<&BmhZP&1apapF>gv){#OOejppW21~U_Kh=+XeqO~ zmtFP)S$9#3e<;L5kOfsOIWGHd*y`5&`;@EMs7wgnPOkS@br_`f-mA#*Adg z#&DgA3-0LhY%N+1w>Ab@H=zkHu_v`gSe}%WtnZ>`Ho&J|UCv54M&ZGBQ&mrdzGKyv z<`l;B@>(X$ctKRs*~9(G=IB_Y>sTE+jmY`gI{kDj@LNH*za+pgxiiq5QEu)xkz;+( z&adUm#gucK)#sybGT1417y6r+hHteIYE?~ri!CC_q$Rx3+wF5=GZn0*ldJ#m-fkQN zebP$dvmZF*lU&b<6%$*}@45)Mq+N z3dWdca$$Sf5rtULw~mLbkJmEDfDq9-+uF`LHFRq z1S{y(YS-i7C_AMd&_S+~*f70JE8mTuJN}@$S>L6eihR#mn&ufi&4gFTC>mf`dGM&= z5u%_+FEfH%68eV3qulaIS)Fi+N>QJky*XN83ROC&sDpVWzibBsKUz6pVC(s~l&7;( z*BjZ8JU8H&SO$t_xA}Vef2`v^AupDOcCJg3 zk~s=(<8FpZ{<4{d!+F~Rb~1G24dVwBbfn3Dlu?8J;gJV{T`)OTAQ5lzii}?r+w%J2 z19b-_OtO78n|%4npU7g7Sydv9c&|tti$lqKJ(;PBO6Atovwn6Tr!d*_RjgYfx-AFj zyW18V-(G}5hS7H1J7}#+O{D;}b?{|QK~V0VT636Gp!MDtdB^~Urkn1kGJmBKnZ^AL zoYCRlHSG%5t(nouhy90_6wsRwqyP>tmsiVCRe0g}HyJ+lEe_6GjD@lDtiMU_1+e?i zMpLozXk73^Qcs*K8T+q|&V_g6U{@F5kNdb~w`aOt|}!4mghe~s3w;#`JyVnf3x zzukr0eXA&Tf%+91P#czpE6;<|2`INJpWN0J?_M z2#lA5@V(8D$PVinWICv=L?pa(EcjZWxuA)khjL=0WKC$6GrD*(w{E^T0(JJbu27WG@Bn-3jxVz$pJB2&Gk=?y7M;G)a*a%z^jKt4q zL&CaM{Vy(eAqBOtp)aYie_&eO0=XkqQKT zR$H^$$WvJT1QqKX)Sy&c9-QLpVrsro#g@{}bz`TZ-LZ7-osJ%42)@#_0h(ezp)j-i zQ6k+Ypn1ja6Jyk~+42Z)CS}8#3nLkusC49RhQ2-zNb;<8z=zL};b(xno%fKt$o$+E zFz-j$&%;%p@zG#)g(ywN0cCT&Da*LYtyADqUnWC~^MPXdQoGoTAVZ5}(dtM4@aREH zDZaF9>PfC(=m%wQ`Vm@`(I;;^F8#Biwh*kb^7UBQu@5**&Z&W4;n%f~pV2QD;*9T$ zw1mVvtu+)bwi$H_ZOZN|>dG&Qq`t3k8v$n#(&kua4s$4E-^AJEcDQwWY?5L2Y#?*9 zeXPNKa>>TGfdAIQ->g81)zNMIu6*W-kXF~#B->_`F0yH+MH_CoJ$`jGaBP5D^bg;q z6>JjO*R>UB-4gP?T4 zdUbz^q!X-0*~+zHbbnMsv}U%m9K-4&ftSp=5oSl1baeA}0FR7(>d;7S$t3N~X1Hp( zyr%0?3&nzt8_fuS5~Uc?mZJ{Gkkd{H^!rJya_ z604CYd~0wiC>{C&;#G{8<|(NLlli7y0t{|kYs$f718HzXPOUo4pv!Qj<66l@C32(G zKqVTkgt{x6rs7Db%kKz6m<5klyR)z=7`7hX%>Hq5DuF9a9X~pRx5^eqPMVNGlWBIa zx=~zpnU^;aNA8Q%PnYuY!6(&_X6A6Ed%nJvc#s(AO;2A)O6oqMZJGHi(44R zb?__ftopH}=)oX5UcwX#9jYn8CA>HOlptdEB;FMLhUdt7|4}lqV;-*Zm6v^p);?eQ z&f;r?XUN_^Jh7{CN%kmSeYQGWRYOdDko`wTTt&AozGdvj9q(yZO1j9#@MGU_4%NK) z@XnPh@!{oeg#mAAne%%3UFYUYH2}U5K_{*%-+^XJN2#(j-PTQ_NL^R_;n&Ai#dZ3| zpT9qR)RGipMaP}?^x;F&U4v(ZZr=(*8)jd=OdR=!$5j5jTT%WMfsl+6C%gYoJ)W=Y z%!041Di5vTTI8m%==La@`h)sAp}~TMXtebzg>XCmiZw9dKHgf^K=fj66FSu_s=1TCX&U=;>}f_W!&q&gdAqxY zNsgBy9mgQ84BB&p`=TKp#s3eF+vb^LkpDMqC7#SiWo2*c6c`p>A!bz2)N8V6qG22M zaq4-)E=tb}p1x~r!oVoWd>JE9l<*t9Svx0U$R9z=rIdGCWH3}yCXPGt7r!Q9xKlfd z!7{tm3)*&(&Bpuh2W$cG;|cVfi-gT0SktgTSy86+0};{CJ2zyXb0_(b57J~H3;=8& zwtZ#&CASK$Z$D}Si`8=%^e>`4T_&hAZy%cnK0$AF@T{A?Pal$|FHPplpc8C+{|&7p z!_=a((ab4LnB2sjAxf5a&itFte1G9~uOF@H3LZH;u3QKapteR{{wmD94d@`u1Qw6w z8=I=#Z8MCX!t5|+0c|ekG3^*l%2YJG3DRU*TLC3fqS>=3!*KUCw7P5B*-R&$cSrZu zb2z>;UMJ?aEugd%=7L@Be&Sw}NUE&js?$rKX=lgPU^R=N& zr}g>$!4G4wq)gGly86TOs&0K;upzN?eP zdG-;BLd4+}W9IF|y058wqIN|_lsz_}$on$#UAgHa?-t zq)px^xobjMm#@NW}7n(ys+w9kXMzf7(@ej!*DT;m%}pI{?e?6U>3%Qv+Yli6tX;wFq1~WK zK1R!vsAeHdZVb8hLlVZVMB71X`pThU<-ZhR$%mIzD4 z_pmF-n?l4r>+>|%uJyNR3{oeqEY!+^nxYR_qg)(iDXV*!rx+yAFRkC>)<5I_`9;~1 zerAStZa9*vGOb$b=0%yN-oKaW=^#Cns**oIk^7h(rvH-C)bHTa&4O?_mRZ#L{KF#{ zZgmA5wD;7nE*jieve4hP#s66d)I(zF+Y5tLrn*4QSq3R!=kV^Msrb7}b@fe4I2)ng z;J`{e=9IqTyXavNN3Fk5|sA#K#31Uml8ftAy4Zc~D3;R+~a7Pu7{t^31t7E|CrB&-d z<262&JBtW^+s zXS6$-QOBm%eJ-YEAK!Q_L@y4hydoyiKLr`G%*jnk^f1cb5Rk%p587NHBG&BhA`+SZ zP{bI%8kzOYHf7S-;2OVLxDG~X#+^IFCrVyWK;A!^%^~#wl$td)3T|U;s-yx`*KLTC zU@zPAf*U(Rm;J)dJRyPs(&B=&&h0?vL8ZUD4LxXCk|n}vn1T7$YZTp^#`L9y3DWtG z`$9+g?bMu%BI^z;thQ>v-0?Z>iSp9ee5o+dCztD}bNsJoaXaBz0jjRF-sT5O%xGh} z4n$DWAKHuJH8kIL_tSdeyl!-T zUn9p&mC+WOjs7v)jhtdk-zd49WZK`?<&jjq;qRs5Ojs zAo7mB6JA)92L4TMuKB*{)NczCCikNmf9@a*9h(~t+FEmb*fg}&Ugxb_!YCBm3>b}U z@7QuXU_Q>)*Z&2_N%FG^*6w-*C0*_9!1FFR1Fuo*0;Zfo^16B^g^A=n{gICCWpNn2*Lcjs(UT~X z_{~=q9RKjd9iVxMmaT-Z4i;j_E7^(yBj!0-o`Y0^+iRA@qmTXj+~1kd316`6IVgIMi%|OH~beK1I>NUvoqF6BD;?8jSGQ*go7{X|g_CF79D6;nF_46BDfse`$;QC2|z@`Ko&USYb zAjOUtb&#{+dWoWZ`8X(C-bAM~hd8N;=Mf$NmWX2)f0H}djwn5}y7Gf2m&ccip?OW6 z1r5GZz*&d*zqy;t@8V78SWk&lGCz*o7x^sumwxn^vteDiENM^j)!zw~oHwP-={SKE zWUo+U@*Ju`Wyguai?{qa)CkWd@bo{o@yb4<3@Db;MgTv+_q7rmM8;bxj*qt;bpYkf zg`mb)M~M0_c#w0GTiIJ{KfWCsSL?-X)#+^gVFHhAr5?2PK=r*7Pr5wUA!p^Tj|-aM z13Akj;O<)YKqQ!!m1b(9k>NlUkSaKHjOWzMdc-stTIJn^#@}Zb}!06cyhxz#J#EXZJA7` zC3Jl*celJ3RCvlbZpUZ1lq6y>eHNR&lmX4i$Ut2Ll{JjoN98ArJOeWP9!sNsqm}OD zZ|Fx=`bk|`Xp!B&%lErs_2{3!E9_;vyXloA8aVGl2I8$lxurBQn?$e%S(EI;=4Av`EBY%;0h_!wg^Q=9`!5z3}kDDMbC_USU4!l z?)G#ibqPvxc83}PG{3?R3@R5~I_dgLW{c*+yB19_@9P9;{B{i;2xv^>#zxk8nuK+E z7S{T0h{9TY_~2G6MPxqQnGI+@S|W8$jj+F`fgGgrejPtly0j(wsPA5IItJ6CAjKY;t z&Lk*IXi9Dk$Mu4-n?v;0J@k|Ed{E=<-tYzr1~+J@j%t*7=#MlddY{_k8kIJwG-1-& zgop23#NMV6NN>E&U!}uRIdTj29=tWrnSUwO$DoZnjJv+j;aGTEB-O2C=8`R=A(U5! z$s@|hh9$C_CWMQU|HC76r|3guO4TB)R?Rh=$vMN3)C8Gs1z5~vo=e9T;(*8EMj3W#~c+pTleIN`0rf6 zuGlpGUR#!57Cp(Mv9+c};BG8!{RdT;R^Z`A))>;aJ$V+B(^N-21XVO`iu&XLTR^10 z^;Wu|8>@fCE3jY1+NzlgUh?d%ceX9dDQN{sGrBUW{KHe@(~;s8ja#RmOBc@^Db|lm zy(#WrH0@_D=v?QsjE&{;_sm~HSQ6fC{cIlUW{wM4n=mXbkkk#k(>z?PMphqV%qiq3 z5yuoFY0qN1sW!*14K7~NudT_SEGaU$XE=(=ohdSoEg-0RJ+$3-43YvixiSb&1YcR# zy9vktSsnUuOS73ydJ#G*9ZhQ+1Di)vE;#Zpcbnk7BXIni?-{4r2L>^6H1oNDfH(VJ zFR6qcR=EioR1OQiJPLZXA}*S6O8eT)JX4>?-J82VEbD{%l)MurS~(duvMOb8g}%A% z=G(4D&W?{d;aog5nH_<{5svhYwo<&f%3ToSK~oC2iE6`v|I(k zo$O-~eXaAY$=3|-0jdIp~~dEkAZ0HHAS_9Q{b_ znlyImO4}&9L_g7!XYU8B1)7xF4;M@1NHfWi>F9x=%fv5iO68lausbVdS&fxXnW7-w zXcM2^#$tzte2o#3lNr~~_K|dzdl%%j5(0&B8cBr#IkiT1)@fWE2A5t=gZ8a)90?*x zLgV){PA^I(BW-h15hwyrnHi^7%q?$Nu`o#;I$@BW`5=;05 z@ma^)nf@r2k+``fOjFN)V6`o;xKvzdB|klY!4CsDOwSeH+7?QTu6eJ$Yl=SOK?aRg z&(r8fkz9J5^QDW7@_7aX*>I)}A?=!Vvo0Qdc)ux1;iu5j{;AE4u~x4Wtiv^NJz80= z5k{Q7uMB?g?8FHuOb%ru(AhvCk%kK{YT}d`{&wrfir#@VW=vtqrnJjP(-VlY=ot5J zt@n9iL9tDm-Y253z)wzE*Xc)ZDF`6oC3mXl(M`Q(#u-sfC8O9aIa&I@GCl0w@*IIQ z8UW|b-Svg9>e=* zDUeC=>?aB@AKV(0Z0FwqMuWe(VYhkm>YMHAKk{W2E5;s(^)4C=vz5vTu>YAg041+y zFFqg;F8jZd zd*GGa%z&Z?*jyNGT5+L5cW0lL?IYcso!?()o_I>9K}Xnan-b@iiegxrVn)MQ4sstm zNl%;Wfk9sHvIlpCxZH*MS1O9(Wdr$p5@FfpPeYYu*#Q;#4n%LhVvDS(6p2`6@xF%m zC4C31JQ6ciw|ekQZT{ushrvBoset_&CsfzA7JD5{8Yf+7Fgt7x@?hPsY>r&DX-s!- z?~awuH)#}Io2TsVteF4VN_D2A?RS?0 z_q|x&kuj^V8JQRmKeOo3-M&evsavh{g(}u>WH?%Wbb9J2eD8JHEkf2ban1RvOrR5f zsH$~16~4~%m)0vRbSCPBGg?qFYUpXHacJ?xz{Zu2i7C{0r$X>k#1+*xiuwEd**{(5 zPhb-_6gJ>Kxjsy6#JNn-FXE@dx!$quuDV|0BQz1a;V*gJG*jw|awqdUmdQ0)bQD`o z&Kix+8JG^UMt=4zTAJ<{EfMYg!wUw%5B1lkaR%HH6a*A<{kIqWylc|2UM$(5d+J44 zGfa+7-Nv}&owg_Ur=icPTy_~Coe5resZ3=1qmi98_pPf(ajv}{&~?mPig?EKc_@Mx z|IbxbR=K1FJaJ^BKrHClYx8vUz?>LfmKxMdAaGpYtl)_7odR zp)XwD9i#B&O7woBUh4<> z=%kQp!U+tT%_;Lzx$Mj**{*4IW6@MyV3AUtFI`b0sXFJh+Pl|ApLwe-=B_f8nal4m z^@0-5%rnmdO*5zn8){i_l`vHDJ3ls@2Do8WDxPMmbC_H@)j9LUq#z0gmr1$b<&er& zDA=j~|1QAs#My^=k<%+=JK36uKPC?*z;BtcFQ$?7;h++G%WhRrNv5Xt_NyOx4HHe8 zU}$BUn78Z?=KQ$#|7VAa$oS{~@U;iVA30M0OSRqcx#9w6H!D-p&troM)PiVx=ZrIvxc_T(kX>KShR(!?v4a=kyQ+-N? zYLd?31T>4+7Nb_8pe5Lvmv#7k_%KzMaJ5Bxz}_~-P#oG588y@3Umbrk6VZcJ97a?I z6Ja=VCHN3a{hl(yu21+=$$`tj0NE2Hw%{=K~!#B4{+{PJ0Ma2E&M9vPIm2 zc`a199Cf4!H9Da>wj>}VKREDs;ih)#eVN*;uY}6x|Ks2H>$4Y@XTq&nsw|6_jdoXB zd{sjx9k$9bTUG!nrq6rN`K99B^Q{M{S&5Y9`5c8UFwYc0lfH>87P(H#e$2 zr=7@z@n^mtf%Qe1wfK_P6LT49?)gT6$F7E+OEG#Fzm)p+8$*i6{+0-s9o!lQi$r0e z<8v~%GUi|u&{1KWX)-7sx`RSIe3ntJwK2Bv-Wz9a+Fj(gK0%nQRk!|mk$>c7y-LJW zays@R)O0>r{-{6d0NVRX<0rwskfnZOIb$X5j_KZ2YpT=mfY!B)Ac5^)RPwiza&n@s zHL}-x?e!HdHwE=N%hA|;hVI?lxVQG?%x_KQnxAm)jl(>bIgGu`j*@AnmEF#(pW);@ z7uRsC#==m7q*3R22Ithb;rQRFs_gHf!P;FGHV$`bE%-;`vm4>sWzH+}_j5q3 zs+Q%=x%rNu`xPnN)VUimp+}z0&o!ldhlYr*7?p%|j}nii}uEd|F4g zpK8RW(iW^O#byjttpIi!j|D<^-A_r6iyfu!hQ-NNIm{-M)}3`)E?<=kP{f6L8RutV zW}0ha%7%go8td*}M>_nQXQt!vppohqryNbW&tRjyw;v_$8Xks=$mZlp9?y-j8XoH6 z@EL4I4;7Y<(Ux`x<(p|prDy{1`M38-=;cDF^y7nfrek*T{mG8_OLdNGVqtE&w0qSD zCrTb`k+E%Di)&NGu3pMx_xB*@a%KMjI#^|N9mJC67MfF4&2nd_z<)_p*&8IZMLQzz zbmy{0kZ9iz9*R5J)A15Vpgc>*ZiY)srrUulOI#jX*YI0yv(EB4T~9`KwY<^VK*=L4 zENBED3MKj)qa6C6^xekZv3)r5ucp3Op5hIg${4w9 z?(XfL=F1GFj}ge;(%o83Myt7l$WI>hZb8yeYDxN5sPZ{h+MnT4PO)py=zNLoPxORw z4#tz8xfhtdL}c>(QO9^pzE5Xc!*3OY<<>S*UW0WfcCBk!sTB5tzfXB*`k9rUzNHkR zwQIMtw!M86K7F6VX6LM~__Se@?;_e(le3tRZL~cE_ z9}{Rw`(?Q{lYMA-%hXb#B(@M91#4!hbs;E{2m%5!$*hW?Kp;D*lL)~wt21PJ69{A= zBdumh#*Y=DmSB$KsQ0WJ2r=xM6zK^AR>UQ}%wZ5XYq^YP9OVgFB8Vr|vuXZ$9iLKi$O>HADc{=sgTDz3LFB?m$ zZNIR!dmH%ffCD`j2FF_V#_|pI{@mlMkE1O!_k`91+A$w3y1-0jIEq z$jY0H)YnvZa3Y?E(bvyC9=7? zuM?SWn)ZI5dF*Y{h8NjeqSIu0`}$XIt;dIEJDRude<#X(y|{w_yDr@sYn$5$T;s27 za7Um%YSpVh_7~?}7S-r=lKE5ORJnaDt9I%5w?9=E+f&fb^_Svpo$Al&^~g5Q`IZ~M zWo`(flA0D-W$kAgjWuoHUhj^ViE8ZE?Zyx0zQg43w>Ud?<@*wxehB$1d~FqI9Xc*- zUZ+jk5V4`maTP0bW;`<}aokP*F>xp#k|RU*A9{l&;j-nr0oWUsJ5%$?Z(kGbS|s%> zt7V%s{^Yg1zsz0N&(lYP?YFxR*Hlcj;N^7YIhmF7E1IM zuXO+#{>7=>IuW*3VYPDw19kB_EiU688CAElc_T%w%$I~$t!*9L!djs#oy6m8x-G2# z04NRX;-f3N7GZ3-?Q!m?VA03(I-Ux=t{ZkT=d{KE(As^SDyrO>C|hlpMV>G;0o&KY zima|9db6o=+GAUw?DC@LJI78o6$_@qz!Klx{i|HA5_XHm(+r+>!n*G#Lj*0)1x z(w)@OwDHg?*RnJ%Te57~+EQZd{z0$Fi&nsOXm^+$0^;0ym+d0gipoQF*8p@ipdQ+) z6X;ooQue#V+U0R?u*k*w>p|M)K;V8Cv>ko=s!h?0@=}cp*Nh>=+xl1apXtkySZ}sA z;M$q3!Lu&rKQ!Cqqp@*g8Q=M8=CtYJ7ki1wTE+B#Gv=J;1pH`ShgDSlF@JMOuWxzm z7R%f(6>xYFmzGHi4WX1B1=gPY0Ye{h_wPmBMOp%VwMH^j0;_%m0aw8|w zsPNpW#IgIQMU1xo9GQ|?;u>Xi?l`6gEu zTHC7a^%%p1FYr5$Zdhb6amRIlnUt`-jDp8Cl>&q>ka>3Mo7EoQ&9_`BN3U^kZcV{q zgUMLhOw!|#nn4i92ntN)ccpL!s2Mo9$sZpb3<&#f$-F?Ux|Ru9oy})sT4$NO+Aaz z*}~e2Hzq}rH&AJ+*K?@|O%Ogyt6F}=rNJ9~L_xW2Snp!dc0Gp6FH*+7fEoLM@zrqd zL+U*RnP^v|aFZD%eZTISNj~7WT!-+=Q(JGR-eA4Iyjz;g=5e3evv~k{P_GG=-C62j zlSR`lT=U~q#k&^Sa@$lc8SX4F*mq>Fr4U09tdbGnS5oFQR4UFKobKM*iF{sm$14LS z#@@Dg-|;n|)|K`ZNk$YUa~h9SPRiy~i5v@?tvq#6c3{}I7uZm*XN`|=roYgtW?D%d zTx(6E3vYZk>zMf=kK*!Z>b`%-#+?sWjW*#!E^v-1xSf@0WdzT=b)%lvODXSnd2y(5 z_Ag`P_&u(N&pgeg>ORxR>%Uo^rSM#pm@?#SB;(vZ^|-`k9AE{||I7*nyFp+2Xb+lP?33)7#a9w^#dV>2dSm^r}Ndgu#YZTAZU*X;D|b>ff_P?=(9#ki5CMb>-Dxdnqrt zkm^g^YU5ve;WmAb63^4AL6avXu=$JyvLDR;g-$Up(4pn|6DhT?aF-S@0^=nk*ylaH z;D&(hLHDhx7%iQfy)yeNdtC0^Ut=$)YndaDOb*iC6Ky7T zo=xF|6 zQdviniuBgrhnBdhBF(uKY=Lwk)aTEkXx~Fe7gDuWqUg$VMYnYqT@`|~<5JhVkK%Tk zg=P|}E6EfKnDjby=(fUiD%^=~%*{`c>aN<6&$|MNa?N%36=q%KB8zg*hhI7szAmWD ziX`l%bX#D|^+g6|P@f~!ZbfOLohyG1t9_@~R=A36tkC(lP^N1scMTn+_!YL?wJS+c zlu29N+~2ubgtahAxOcZ-%rtksU4ziCawR%eR2@Gr?Ea-RYHFn>cDc0_udz)Pr5d7` zN1m%;K;VJpr-~VmQ#ATS#+=`*P7$$rEr%B-S0P^`X^FU=x_Nf1aCFC$?`eI%VbQe& z?nQdv$$h5XdUW*Jh4l~U%hr!9VKObVX zg{#_GP(hA%>fIM!F;d5k&aKryXX-f)f5dR^r^ofwy`sS2w$BS@b2+w_U*-#~cC*|0 zgpxP3ynCOjs|MvUI4T~${WkZ98s)te!_2o^Cf9NLh4hEWmRJsM&0YTT>g$R3=H+N~`qs)Q1@5C{kaEIzQ9WsA#vVE+LoGwpTC0cP^4! z9j~9tc+oIM$Y9tT2WN&F6IuN>Q*U@TUy;bom-nzHSJ=FN424?PtM-vs>U-KJwE#(jJ zq>;3WQ`n=67a39YM>i{>L9~iHdwFJaXilK6tR9CF`Hu59FP?W(r*T)T$3lf_$~3JG zZBK_qx(t+~Qz7ihhsrU(TKId<^{qF^s4}6J42y7Wn~DMQ?io+}C0a6cexfXm%F#4r zAt^6^=z zq?L08O?s{MHzTiWe3GD-AXQ|GXC0%-Dq^HHB4Ggt^(zF?!Uk}*n!*FA9wB8iNu3ml z1OWg*78?Xm(Lth(LCSTKZ zonO6gwHiSArzdxQL-c4y+>$oj$0={b5A&)we-U#X{Ut6wLB-g~3|R+;^wjiAcXqQ9 z#8c5|&1v-s$k=?ID(2eKVI@U0_Gq-CpD-iH47_#q3Z$ z&5g7&ztYovZEipHJCVlk7%4vS$G+EXXjPJItk40wT=Kl4!$j3HC zgqjjdTjfRVa8*(@2O~+Ph2I>-a&Gy$->v*+Bc^*u_ z`Ht{Lx>vz_3{%Qv^kogQ7QLkO9zH7dDvmt*n@PnRN-fkOkL8EbfQo7OD$K!kZk>UW z*%>2uYR5$YKfG-S?XU9{n9hvE;Z$k=047{Xki5Elk1p*e_O9k1;y4*?(VFvl88311 zHD0R2(D2!xjG;{)s^nyIrb020Dc8+SO03lNA(oFNG8&M}e$|x0)QT;MA}t;0Jd&om zolAU{YGpI3$ss_oLlin5g-C)SsgpQb*^`6O5JyEKVE{lDPzq)O00BJ|1wz4**P>Jt zw)T=splQ`AYn-Tep2A4KFjH|g#_=iJ+fcA5Hz<@1=0t565;ZAa&9g(%M$=uKh z177O+nlI=1{V_5Jsy+a|<_tCF@7ULWFFg!DFv!YxxrAyoQpURN%+r};#DVi4>OXLD z4W>7JaQ^=Qw=(51UB?(4HKl9is`^(0l()uSlzg|I` z#v9|0Gj>?r>xPpTcO%0EoQ>6?r$bZbqmy!|*^6bw_cwqx9kcCh?`O6jA{H>PQ$wQU zxgz8xoe!&3{7ZJnQShufsb8(FZ&k`eM&Q$sj~cQZ!NHjHxgR! z$b7X`q130DH0`rstn6AIB>T^`Sd0%Jla%Q~@wY8(UF6ZN>Q?DPL(W~Rl?hq*4l>j~ zzU8E|^E7II1zDpjkGT5_YWsd$#(rJA21hu2CC9W7w%JOi$Y3+YpY;=FuJ$j}8+UH& zOo77Wv-zx6A~v<(6XD7CGDhfpg;&!2lf__1i?YBv$|f{9 zmsbxAjnyPj5DD;FZ*utF8rtf|gL^!A-8tT`Rs77UxQjG zTIB4@N}4WU_o8zv#~Zh0LyeDc;vZz|)`J_I(myjpyj6=C39^mcdKCF=dq-=Ay%$AZ z(5K-h%3Ro{y~d5{(NvwsQv7k#L^fd>@;`T{ip*{pc*>=E%Wk%y7HeLrO7cV=BYlO< zZ?+oh0T6@OO;Gp1+M?EV8d}Y;({x#8SF=Rd{YgEh#h8aoE}$--Yz!y|8<7*K**k9OmLE z;6E>OMGs%Kc|6bC2d<=gx}m)H&4ceX&W)4kz^z;C|zt)ktRioLawzb*^eD(3eG6R%$tJ26e3H_UNv-knb+a@<{Oj^v&~iU)^A{GaZC zJ=AG&C#Ra`P6w#Q(Hnom9ARyIE~Y)*hET|wY1c~iP>gjMQ)(xn8y95S<6&fdfR>6^ zx5-=G2_a&tbLp z{!?@A3)bUy<_6vOIeeS-Y!JpOT#H3SEBGGQ1dRc36DLOAFU$;tvVdIhbLMDXYpc3O`^ zu$Wxz)W3syH*`ZN!11ONd*w0` zLm83rNZp&0vW7|z?#nw$;qy($4qleNO!gc!YkEK3e>8gy{-sMd?cN#sAMkd~x_02q z+U%X{oUn#A_Q`QR^t`}J0W2zMxmMq-6BCG!ZRG-F(yOVXtF;Qyf z{t!#;W&qrn%rB%pzl(kLR@(mnW%>x&rsoe7a>FO_j#%B0hT65_B72`}^D6G4q;v~R zH($G_SwET6$lGo{guX}5M6zbHljS!~<-LAScJ8UJvU`gqBZtkN?(*a3z0W-=;_8*z zzkV)skKtM)nF^?_---1*I#=7iIc;j^+rB3eP%7x@BIivhX!}mC1kYvt9{QHKhmJjc zaSS^;e4i-#oZ6dbZey@H2d}X;Ejx`> z-TUu&=02pmYj|!L4VH5ee{G4hu@wdjOg)~UZ<;idOk-Kn3B;%8L2o6I)SxFDZC=DBu!qkYQNI~||$ z$({?Ax8dHB?eUxc0ID@UO$lSQ284ev1-dIzZ0#KZB%cMjh?!rHqO0VX4GRx(QDCoM z2*xq-lTc)JdVen!T5nSAsC_0rxn&$j+*Y${NS1eSXrQmPYL!V7xa$1FRRCmL3Wg5~ zg$cb0+v;9eU}$=tzTSh_xmPagbRp2`lxu^54fuRpPil{dN`)#mcNX>z-!b^Cc!p?H z* z*vhmv#q?6y;v^GY$0=KKwv!$J&Lu<#rRn>4+L19 zqq~2Jw*2XCQ{dqt(zNKO-RMEN)QK!Zma6NlYh(Y zYoo8v^`8mjq`#Zpe!rSE>xJ-6XR9Abmf0wzlIqI$H!Fi$dmGz}J*D1V7ZZiIyCy00 zs`;LioW8#+KkmPg={MBZjrk9jy*Fg|^Ng|bUv53ecMNTOZtQe%wxpWr)p}p*h8pnW zO?@Yd@*Lu?Nw-QOx9A6tcW)VbZQ0uweAv3rac#i+YtZ+1ImMne`&Y|+vxP{%S;noc$yryl1;@3 zXNIe(YQl-4cPdBia@i%Js;KJV;3|6k>z6n>nN%S(zPj}L6dV~gku5kUxDJM@z2tAS zTDOdM%xUedPhYijC26*HZBB@;TH7_Ya-SJm>W{LD#mj49LjC6MFF~!FcWFt9-g(=g z4{ghw;pIcB@Yz_hZ1q;|su>;jj}I>Gr?F9v6uXOXzNlN=yN$`^_^&^V!$xGvTt>#) zQ>btuY|XPT2%~`{vYYfXrq98%1&X&K5gWq+bw`aVyo<9qy0fd7bxG|;BEm2Jo(GJtD9@>+mLn++TfBc?HB-STV8;#W#p@F3ROpq^P)|{ zo}aMt=M86Tbk0|b_S{`s%el7z40|eHDRY22uRqGc_@~*pxW!|chk~YV-1<%L*_#uL zO*zK46GDG^m1JOhs#m_ja$H01a$h6lT&~YLP{^kcuNIkE?<9YN|pfxw8kfu=A!koERt_@1nB^ z)Y-9?9$~Gz7M=7zrzE%8S}8bp5hU=kmMwVZk2Q zf@WQAS;eWkfZ=~g0fYTqbV0G0>8KRDI-%8jMRyUQdd z{{X=l@vn!wr}*fs&rDYT0JZirr(^w9>VGtE57HBH4TQj}=D7iUM-wAic{5pN!^S5s1)|X@X{*^V$)_)o2vKx!jcJSO5 zl25trY;O49l`kAVIQJgKyLKoE2(4UWVASeB3x6Gv{0Twoe7NY`4wQ*52koK^3=|@yOHE0PdAr?^{=KWfl0Ls`Wm}Vb+{HulEg~!J|6O24|n`AU=Al*dsmRO zl&{7u^=GM-R;HJF48-K}*}q;p5-E@A_t&S*)04b}SGE5D!R51aDm+N}fP%Yr?zf8x zFW8e_`aiLp%*$JEzwIUPuilS8@Xi+-KM>)s{45Oz+Z9>-%H`Ed^7XTebMP-wPsm&t z?Aseoqz>=BU9^ryLTtyAwa#;^=4h|Ql-06X%yV_>ryiu5E=|2~+j*D??G7PpytFd9 z7Y^coW3cxX{{VM82|x4INOgXqyDNOO&-jr40Pi*v@9tvGBl^hP{3Z=^Yup#0w)8mj zj-@Pxxf*8%juuMyBF?k{q7Oo)x*kG!G% zEu5DU$KKX?czaa~sis`9e!UGU-dn>NcY1A$yCjT|(^^x(R%de&)=0s5+;eiGQ0WYS z)}2b!x}Ng}FKRV^eC-QB>3ic2E~s!Ky>wG&qp;dF6MFH)kj~tWCGNDnws4Z#c*$&x zb4@{MZlHxI&7#%Bq>oiSEBeFSl45Zi6P7D|e8)j;gU%NbE5qoU_@nhvRFFw|RLiFrSm&=QeRO{G|tjG6IJYZv&M3L34doHs`hpzq4T`h z{bd$t4^lKL)_j(MXZhjS9*kLK2lp94Ip&^wAJRG6+&9QPly+Z9(=d(8Wax z#YIBcfIuF5PrYlXiYi#!7vy9SMhS89Sd`LQk&=~kG8?$s`ySt&d7$e`5&KnVel<5M z7aHhMF6E0H@0QjAT4`T$mAO@#?pbuypk3bywWn2aOG2D&QIvS(56X#uiE<6p3iN4Xm-N`}Jh+>0$lL%N4g9~I|sTT<$J+*f+Lxd;xCvB zBJLz;>d|?NwkXd>VbvSuxw%s{>N7vH?=#roY;de=M8HsV>-Z`e5u{6TJuczQWOkc+ z7WR(TwV}RU7Yb8#BxVN|F^>FJySExftFP%JVC-2wR@sqtnZQtV@~Y-|hYIz_Q*(BX z+_$oVDID661uNodRVz@grvsABX>CSAWMjLuHiOAorB6F4=t^!Y#^ZL@PE#Obn!-c8 ze#E(?332wkOq6(G$R4CQCkZBV_$AP|E-v{_4i5ug4S$_bsqNMjT092z3+jXF3(8!G zcJ{~KSsk!!quoKMU5V$~jm0|&!ksm%_m*w(=`B@HQT8XG?iSdV z)~v?tn<$+dLw;$2kA8#9g;nRywQSh%ZPTH(n*-Ah#1Y@R$88H7kcj@4^`HO&tqQA^ z%-p3DIJuZmuS3Ycs2@H0gY+rAw#LZboJ}6!;(kqYnYZ)v#$LuafDIi=4y)JsUN`itWkX$-xf3K7sQMeC1o z+P7{lCI0}5!^0$j&u79q{i2G!+=m#QEF2ilRWg3r+3{vCn`r|B+;swg_%21v$yce; zc(JcTGVGo)l3m3fGJAi4#)nT26;}RaoMtI~N>&irYzwqzE2t88U|_$!Iw%`wuo4*viO<;KvwZ||g?_q(&Z1`E9X zS2Af7gv?Jg?>Z>cEQAcjH;9hv!pgx-cyDj}kZl&7g~HnbWAn6omsLOKu#5jeD#PFwkhy@ zM&I>y^v;KK;GKIPr>{!Z`Mf4NMhT<2+*i(<6^*rgWD?{0h+FS8@?K8PQ@4)o^lxXi zeNy?fbue*v-gh5GDf|AF@=W=__20WD!#|9@EL-0?_VbUO{%q!apUe_|w|M7Z7cvJq zq=$T%S}#w;M`fos+oJnQoQvIYN4sCU^z<4dbn~ENsoDUpy$6D^ZWTEBPEF`Y z&vhlFPLz)0N4SqN7*>U2bf|SPFE}2(aCYmz+MLbB))sh{Ga->i4`a69Oxho>^kiEoR=do88 zN5|z@!3!9E%xw?3dY>s+t(5!i&3mQ#&z^JBf1IE9;qi&`%IFL%V}q(6{Rmi(Q@*Y8m_6q|U#ARfLa??%N6#B6WOaSf@{O8XYOoebOR zRITGD$3dW6>YWXUtlP#szn6m5b(?L$6{~oG01;5N8+C|ZSu+FiC-@JoA}{>9Cm?Mu*tp?Kr5z_?MJ2SuWHnl{GCd}FZ= zbtuZ6Kc#7%)?9_M^s~b>2A4DdJSeq0v|5+rZ5Ji3Bvr0sD0&SwDxKTK%XUkq#|Q}p z;Md7hmGZ=Fm*y`9+78xJ+y2E`ca71olPhv#UkI;hAawyl#i_HyMqt>RGV>>hxy?gk z@-1zTYwisXaj&sR(ean|Vr786-UU9_STKab6jEE#QY)$ZJ_eDk2z*V@9 zKg&8>J!<6NPaDIWy&d{<*_#_LZ-Lw!HXWmxwwCAOjsF0m#P$}~U;Hg~Uu}h%T)?)M z?APLcdwh&-=iHQ^SMn{M74&DzJ8zcuKO^t$*N&Pknfjw=ZEVByK)AP(8ZdwWHE^l& zTX>8a_^P@Wxh{Up?KGWzjN6Y@ypMCm96Px8?W+v87!b)LVAiC#HBkPfUYCJVmy+zp zc`Zp*Q0LP}A>49@4($E+cOT22aKdJiKP_qe%fb0N>y&lWv*!Fr+smYMR7P)}k9B)L zmsRvdf~4z1&swTP50uwD0N#q`HyXEeHu#Mq0CY{qBz+St15wln(I~T z)tIQ19Een0T!{Tf(6ms&#Vg>U;WITj?e;7?OwCD28Cuk6R*SHzEJ8&tTX0-7(D*KG z%Pp6!-Ovcg6rzWM!aQxJWPqK`#j?hQ< zPN8d_1S!y*a-BI; zWOtTDONRc%@&Vl64`}hK#p^DH`xnt6`ZFA4cF-Q0QE;zg(7dnJl3tznLw7ZYAQ&TT zR`_h?<&)X~1kVYp&Ype2dY>vcwo9<{UT})t2ivdMc>}nA&f7O~ay}CCi!--3cbUAU z_8Q*W*XFWTNmn=H6zvZ1E6(zkpNUiHyk1&%`kAKzfZcl&={3G=ch=sQZ06)`^~Bg| zoE5N?G44v6e&+6IcTIA1V0H z(EL9{VoZn37n91?_enxSJk@wT51?6!+G4E3uP&b?%#69=#CDFlD=_G6*ozw_kdh(V z=8*P}jkMQJs%D0XXJjx~{D1L0orwki0NSDR>!O?DOU}xeg6QpY`4^}c^3V{em4(zs)@>0=tYte4MMoe*E*EDpM;Wn zS6{bPBx|4HD3Uj|lyNe=QJ0sk0uA?i+%<^<}*74`f?sjPGm(!9VV# z&E#N+Q1}wqirtW(fgekMVb#T#*4J`hX>a2z-5eK2xv)@S~n;r-i%-B{Rr%zfX( zZsEOiJAlwR(F*$&JX^4fnwGnw*SD(pqW$|ap4OclU4Ci&8J>MddX?Nf@yguk%Q%d4 zaGYJbx7Eh++Z#9KhQVQ&$->X#C5{!Z&6naNd8#(lRVcOXDax&$edI1KZ?CRz?w)3z z>F<(B4y2c!;<}Mq8OVB+Jk_MREH(9`2^SDB&u2nIn>0VXYk}}OQEbG!;oeexIzLnD zYLp$wIIg{(zR!vDC(In($Xtc=>Ga*|R(xcc?m6`-X+H@1m7lN{w|d}WKfPBVCAFNyP{|FsueqUX$I?cr zbTB>0r(yH0S9a6Tq~M7Og<1Bk(KOtGC0qw$ntGM4K_jC^jy}bf&0A3{@61;En$Cw% zLR1h|zz`5r%@3){!GaQ*Fo5N3Mgjs|06=zFHXwi?I3y0gdKo&pMzrcjwACm}!)I{z z0b12zv`Imt5xr<62K4b**hud8xZ6nKqe%oUF^b-GapxssN9wioYs77Buk@GnNxXl~ zb7$tf+VD-B7gIV@YcsDWCf&W-hQ&$%zYYHYS9qBIdgSBRe7|+VJ?^*LD&s+V0b#9!tQ&lDATZYPo9bi}rd>cUB>p z92M+ho#lp_Yg$x(#ZvStjkp;NT%+wgkK|pqnZxcI$z0d=e8(2(X+l>raiQ$-x}wp8 zKG=bdX8d4yojNTsXVm!UDkBkZJIHP)l0f0ydI9bCtex3gNW|GoM%`OGN}X%atfNmu zHF`Yas5kE?M}L$(wtF&GUB(C{rNOUg2kjwrtU|7bjpc9bw&Z)T$fAw02pA)YfDe{B z0nm)BF>Sk@ zl*UIw2?|>iHBHeJERKHV-wUU4q49A&e$`^e8tQScWywCJp?#8kqKUYPKtenOamOWrzR`C|3B)=iG%EBKDm$Taz`okg4RRnHG)|s6;FP zfQGjsbPFaZbtCanW*NtCh(L0sQYUICIuklMrnKl$NWwaRdi}v=$eLAV%Ww~;{Le6F zp|_UV_V|ED&bcih5nlpVp3mi!CAKe9(_wIqaIBHD{G;i|5yZcz7>HWpJA2<+xac*h zT)A?ReWya-3mD3@HLrT+q-1|Bt}URYJ-rspYqFe+`8d#wIvV$snbS&1E7D{1JZ5bw zQ5;PQxQE_av;!7qMpO)4;;qd6Z=I6ZMbD`&dtPfAzZOXg8EB(o|p>t#p0%kRV&Ut7K zCEbPb&j<6 zdqr2)ILz-F-n~zgX5%|kp^9+XIU$qfmuTLFPa@B0cLM!%W?H~!h%IZ~QKb)x7mnDs}Z!^qUny9z9(b8v0jEKQ-3`sP+N9;fqrSCq=f zX`bH_>To^b;Jvc^QL@|nir`t^J&bU#P-$9fxVez}POYY>dfCUeklJ9apksr`6tA!^ zCv!$6LZzC!Y%xj$zBgza;;CfZTTNHL36Z#m4K7~(D~IYBegREaDidt;SrbuJX71S8 zIw2GdJqD#+jA(e=td%OrxtVSwYfFVkny~|kE7fMOda|UPZ*zugwaSnHvOrD0DN#}z z;GvLrxC!dnh{{WnPn8WIS z#06BMnrA}Iuc*jdeiqov9Fb9uZ2+YPqujbyF0_vAxGkp5?94t_JjQl2y{-a>-l)z@ zp>Zl3RM%qo?`mIW^7dx(Om&=>5=76=;R{R11J6aODsD%R&T383=zKH%QGTww+v)|p zZ8(veA)9kx@Qzk^F?k9PpDURs$SyS%1op4cybqObSj4xHdP=j6VwYNaPoGLQEzWVY zsr6O*TcPEsX^s8y^*tZbp?56WIk^`#Cqw29?(0u|Rg$)Z{g-^M6a~)lA8Ofcn4y1R z?BtR0w6OJ~zSXkhq)fqmM&<0@47j{(E^AD#bu|E;Pq}dJTw9LjJ+{%BUBIuiwD?6| z7_0jJWzNXHnd-4@ZH)^*X@%?~~>ux{=+(7bdbnb}<=RC)c_tw0*8H*88;mm_r}o4xau%!rwPl{XQ^@m0pU{iRp@vq;f7Rhy;8 zex$E3yCu7F3x-4*I~y?2(w@btc8wBEvPRQ1?7if-e>$(Yd3io%#;T3W6en&h+HO1C zCm$Riy{TlFMDjG2g3K&zp$<9dY*3n3+jlMe36Ia`%I4P%`#8m_*f*D zB|L2Tj%-X7It^E&w z$J&05Tbyn$5pQE1#G_v6Bzqi7ymVi%zo*xucZ%^M>nTaeuIFB!)XpYmBY_kUbp>rXUK=?>D%&R3lE;S5(X`6uHKM)!7P;I} zMT;1do6y?1{T}*&m6kKXv2Gt`%U{&~s5JIDgcG5n^H==MV$*6}PhW}UYtxNVHNtyL zo+k0KWO36&10O=?vCRd{G$0oWR8e%TX4}0VqDiuU?d!JPTF~{!>CfupfW0<)Tl%`} z-ci1i4gUb@A7O0myTka{JHB>KOMJxAU)wS42e264$GOKq8mn@+w{k4oj5}-W*Q+DP z#x`zEzOiZg*4NY8dSmPV0MrLAdY9$@07Jh}-%z_39mW2i^4u?ZClFcMPWjn!HVW=@ z3={nKx!*Qhmj|`JUd7_}Nhw>Fw>L$5BAj&i9+w^wjf2sKhweKUm38l zS0i(JEaaWXk>Aqa!!?z;+xF4s<$Taja@I0V;gh%RYg|d~G_H!*lzAw{#?$`N{{X9P zJ}Y0*9!JbPM7&8{UGabP)jxsq%ZtA`3kyr12_s=>_p}w(yKiio?m4iRX;hM}j1!?F zr`oDq>RzaH%E7#k?brF0X)@KNsZF@}V{=c7zX}yww6$=Mi?p;f>qFDtiJEpI z;Ix9Vf<~S`^P+{$hWJ5Ho&7rjX#%>AzB;Pp_9Cwof4h%L6J1uDl4jd|OXqEm_nzbX zRTlLx$DuKpims?0H6MDzB~poR8)|&~{`G@0D$z&#$7|*L6fSfnZE98^j(`9(T+^gh zE#oCZajCZ1UAKtn0Mz&`4BE=<5E=@d2Tv6m8{)JpcF+eAPY!`beGT^3rORV&N3>F< z%CmGXD@EyLZC8dgYI>Dkb1p&@FT8b)8rlkJ*F{p~&C#}A82bUEh^g{a7_S-`(2-d)Vv@vS=#_tAy@Ug9k`7Ypa` zmy8lyWL+F`x59zs2{IdEqk#c-G4VAd?r2r5+e%lii`Q}Xl1D!C zDiqOoWfwxBE9_hxm^z!zKw2XM6*#H#Ta}#&P*q}9g$}O+waTR6W-n4ztp&qHUN)-D z?phJPTYQy|%9{7Hr^=7WehK+fULzkWKOy)h7E20U2=V z=DhZ0w2|1ceeN*tq|0|>Hm4%ZaMC8e+}T@~9);gA;1By%d&`ngjEdfBw&yr6U)X0o zy~tZzcF&T76?YZOaWoe*%0n65Qql)-@GaGMEXhts689f+(yd>xz`nNZEw^{0o?wRu zcMPyzW8r&t(rey`!)h_oihvD?%!?GCq+C%Mh==&Hw|@9fQOEsQQM*5`G_Yczkjl4lN@ zbyi`}u~OIB*eoXGM?C-{0HbZK<2Wi|jrd1C^K( zJ|6g5=Qr(8k5iW`v^BGfc1&B3hWXtE{dfW{_avA}t)p}cQsPLDTrHy|D zdxbs9+$2|$Eqmxdt7-oLDO!Wjtjk9J*p`wHy;-)((}`?d$4__kt@7$rXtgd_YDbYH zr<$=ARV~~2{uaNdgO#Ydl&(gowJyQ$ue!f-zipi9B7t)jgpcgjqxsdVZ&OstB!Z6j zs|J_Ur%KW(>|5gMiOErvsoU4?Qbvn~4&pJ~pxicuAH{7)F5=XHI)#=- zJx#ClwX`PRJYDJ!W#cY|mT#YTmlkox8tuS*xMa;|TN9p6nt>rS_A9e)kX^~YoBIxq zJ6~6=FPGkB52){^Hu_QehVvJr3`S@C%a7t0;qCFccQ8h7S(L%rMZ@uZ;@2 znjDpD+-vc0UY6oiD7%$sLgs@|ejpFqRmqanwHAA)^w`fcn0}ewp}h%j8~*^&*l+Fc zHtpa{f&T#GP3N~U(cHwy$^O*cl&6ZN&)j}C<=T~f)cI(7J_f{gs;yI%J+iOOFL9l| zo0}U8arKD$esZ?wf9v)?s$M_jTxTL-h-r!}&Kr*NxcmDjG=5TUV2S;-2BlKsZpGnF z+x?j)y){jIKGSb6i$BfAbpF4G^YnX>U?Ux!-dITx4A>L*Wi=+GtNmOeMzUdF0>s{$`8EQIzSwv-Yj1)o_=8S-jLH7dcV;Q`UjuA{2TV1}j$R6a^(htw<>I3N|8ik%BcS*aw#+JHby zpa>4j2E-5?6G2sgdb{WY=(+2+>N#_3f5H5A%ugO%M}&H2Y03R@`WI`mq6e^J=P!E%n~=#O(fJ%6MpnB(PptWT>>Y{%`+H?X%7{{Rx( zZ2e8m)W`nz(<_`C?_W9dAN7gaN;V>*`&)JUKJ(e|zEF+mxyrI#iD*RiZ~mJu6ypAN z!AZiLhm+i#IV>UBtS$V#mrR}}G(0j&7tzGmsrRoHbNb<%8*UX%{`EEcz6a6x&*`2e z#I8;|U+&hZ->s48SJ3v@^oN}E!??XZS!A!Xu5^)dDc2`$Gr+8ZSz2A86%;-z#9scV zW=ih;&vk2BpG2|!I>g$Q7TR>7pM^k7;-l$Z7arrdhls4M?e=~O zX1an7TTUCp2?Dy#H-qEe84${mF{IILd1?Ow?|V5a(=?V<}p)TIwrPi z8oq!Z=2fp-U5BRP^Cu}%N01d60%G%E@(~GOy z(2wU(Jsyn|2V2NHn@alvkzKQBeK+Fw7arZ@w?;c89Nf=fII=tknzd%*OyF{Ok)5`h zsq4PoyEzLM%Ka(@JeN`Gd~ZE%o`t)}Hdgr^xsbKGV3H=r;?|!U4vMAzh3*~y029`D zCF(Pe+qo|?7|d1{lS=0`uBA@39Tx6A{YTfh=+-BV-lVuj`-@|ITfY`9WWHd1ziYa+ z?IayQ=)LzBC%B`WzLt2u$dikE`NMt|hqic2R#4WR6_~QCjR`~7Rk+sC)PvM4rbVNw$pC=pw<2Ky z!SGRXLv2ie&M)7NZZd!JwcqJ7bomo}vH=I*(N&&pq`I5!;@q2}MQ3nv9^u>8jJ&*n z#>ZFpWv94P&+lB=^001)We`A&#%LQing>h`Tqbb9i|Yx_|JX>fB$?hk8qI@hY`VqB7%Ia#)%?X%?P>I=KE*{$P;#fKM5h>m{aT98j~ zlKXF#D&T&9=hZJW&)iG9qz(5KVs!vS{{nU%}lR_xTB%* zqUGPWQ?-kKQ)>8gKI}f`!sS>I-kP zE@8=J{{T%1a11YX0oPh7!L(esk5D)#d`GwT6m7tyhuA$$QULiD6M5hs$ho zJ9h6eZ*B7J-qKj(r-J87^F1B3k3Y90ox8k({<0ct*HxxYq%IYYlk;6yivHTIY>Yac zrwxm=(u55?p5;xWp>9ru#fDqt$ieey0jSkthSs0PQw4jZQ+Z(gANBi1S#;TOy9i+{P+1t_3 zO%$=s+H}yLgsQDj#PJhG>~ZHU+s7PinqJbus3cTr_=N`Psyz22#m3}l%+YJg`1`p7 zerP(KL8nEl#ciHm?*|z4GVb5b*yN!O`Jv2sql2hu7he|X<#`+#$5ec+{{W~v=}WLV zt7Yx2_37hk#cR#kOR1%{+t#e3uSL9l^BBViM+baE!>C_U@*kgH6_mHO?Y^tg`f86c z%6VQ~jx@B}H1vO`#QDGUjr1+#er}HT*O@nLyi0iE&(K2HqMh_7UgpT=lkN-ZJg4=t z9yk58R@M96zUFaw{{ZQgZSA_-Z@Bty;J@^weK>aK7+qU)uPJUV@xHSb(lZ~rE%GkK zGTP-v3%#Lur!r?)YB zD)j}(JY#pW^LfpIiNBC+jrcLQCf54KQ{M?P#@DmFH1@mV>MFjE<-b1Q=GabijBYI& zZ7TXv<~+mwIL^hp^g>d$`ZTLgL+$pO2weEy=%!(8jU+fyjs$oRy}ET(T@Rk*;ZEF~ zTGa;0UWf5QtAP+7>r$vE85wf}!)gcilpK&zWX)SzS6?Ne$lR0qL8KPbPB?+QmLP*<eH^P&)|8}BGq9DwH9_cFDcq^t4gPUA5>pT{CDY}ob7hk8DyJz zjlH*L>hb+POW7Mubn~qj+I*4DPW`z;ZmDZi^Zx*yco^E2^v$0!u3ocx`@-FtVo!}n*399kF zWaXh|#FKwh=zU|#yaX<|$*m8)P6_mvm;V5&48)u6(XhH~RA(OzekMFKrmOQmn7qv4 zE~nq{I7>?3LceqLq4Y=UKVoeCmB@Q0Ex){Ru-)8TXY30s=Dl}oYhRYid_8p~ak$Sr zXd$`=>t zert?ko;54>6|3%CL?qwUD{ybWR;vCd<{XsPbk^FXIVJ0iYfqVc z`ODYdY~B;z84bm?u0)d<&L49@9riZfHpxmy6|9W_3JR+HE-SOX?5SARH1z4It1|R% zsHoPClJ<8VSBU_QIHWp2J0ov^Yjg{AB6GPLt+SBk^=DDz*RG1qr$U4qBr;OH);H}f z*Yxrh`^5*BwPm#KN7i;N)`zs*A$^U?VYmYke`eVu%W-J3m52Lniy+ZM@9(PdoT~mL zy>I4wt|DJTJk-tNaT$+OmwOiSGh?@|IU>goeUG67^cOj(?Mo0YE(uK^5?{6u~#gu0jPbT$VG7>jP(lcAIO!mA?vj-|Pj zZ_5p)oyVuLim^MSgbj5lYfl5^;8as#tkJ_;0%Mj>R<9Kicc%455QQTzG zE4IO91w*1HWd8`Q)I3HP^ z`&ETazuqCHswoxHX_qP;2%o zvz1FKG%vhWuoSCw>rcT|WbxLfK*iaImUl;{nl5$stqakCe%M%#hu{LbE@jD3=-IYj z`0Fo;gSW&gvYiVNiIZ`7(MF$_&;_DbG%wn>MvRk>#fOFh-)UUw-DhShh5e1fw1Dp4 za9SlIMe&Wm0$O_pzu5q)vY0M5?bjN9>}%mg1#}yA!T7v)`8pk|cm&rGTuE?TM{mUc z0EB6;fL#b)(C1&Zp_Om246y;7-@1*}jlKZ+f#9mne1`^iEd`8S9jiyUE4+2q?jpul z9|*zIsRPs`O`DD{1DW0%EGD1$z`4KFCI0|!iTNx40NZ}AxQoH;17lzt+du)J{fmp_ zyDMre`PKgb+`m#UXIbP<<1&XIlV7rwUrDs)r9;apEtTvqwzP@^)q8AZ(B;Y0oQJR6 zy*3m|_ett#yOM^5Qc|B1;pD?pe_`2HjA&r(+B}uD24iu@VDea%w?Vk-S}D<0a3xcQ zuvenjX4P4jA}3(-T{U5v_Yam9e=f}LA1o*3*xq#gJ5-^L>PyCCoUjU-3aUA{siMWZ zq@1ye5lRp`eT&9r;U-zPGam1U$!m*s#AJswj-K`8c}yB3zs20ulqjyhQMV(wB>cw> zPq}>ikhIb}c0bo$#h<{ejA3^hduYeyZp|sz_k~*ES5qEkZ|Q!9b=`CEj#q1MoI7`Y zz74r1J}j<6+Tu3PYUti|AZuEli_zn|b?R50yo#qK)Aa8!O#3%zY~QE;;oDnhH0{N< zpu=1?*Cb6$%@g*Ad(8n|tFe#d+pky9r;8{2!YZ8+(0=3p*%T1fMcLf#AB28XvqX2S%nXzPY%> zWFow_d1%PV*>K@dKyjkexzy9Wj4tBJZ0*aI$YWYd-EAu>E304EqTY>VQ`nY^(V%ga_eetJ;KXw?4B0fG{4HJzCDcM#yYo?)Cfl-tx8A3?ttwW&_T>qpYPMHN0zBC$zylXCaH zK1JL-cNqFoOWpqf6U&VDTH`g9zRBG@zI)jnB!rrui0D4RoPCWAIX7C+qQCN$dm?mH zE5Wu%IJgt3_bMgDBhpVz`;&0&A46Asy|fkdR!>nw{CHgqG^27 zUkaXEE)kX3$!kXbO+SqhudDRVN;B0zz@`d9X*`59=JbMe8k7Q`Cv?vx%Pi#w!Y zqV(ifO5L-9p!h%lb?KtbhxHn+K~UFu$ubgpmFO*g6OnVNPPr5Jb86Sw^F&QDhmyz&Je$$VWxF5(!f$6+TKe zCAA8rOf^G~`KeMhV^1WhC00V557WwNtq1K|qh04h3+QL-Xa1fY;~rn=&*??j_^se( z9~*g(zkDJ#$m#8}Qou}&Wqu+UJ|b%1b)x*i{YAh30CRCJ$-kNJ_+B|VTXO2-RC>H}V^nxupBT_@5xtnf3lZ_;5(nu0`MxdmK3kUHXy>W+kRk4XJ6aP}i< zZQ1ur9?XUI?F^PjJ|S4+rDz;WtJlaaFi)xDJj2UNcEzV@wm3XBh9T3NCVZCMzrI#f zkO}gyK(|^vZgy0zElah|Z3p7FwvH-H#@qgAp~GDlI)k+9{Q4^QPe;Soapp%L%Ms$JmEGKcJbu(uk8|jY?1ny8zS8P2Smu!64?-$Dmv$6L_?Ihp z_^vFlxrcN&Y3;eMUvAr$ygWUn-GBzr`5!e?GFlx^FF`rixbjz%bI@B_U-Es1dH~A; z=bTBWby|9U3wIo~KFh^5u<$3OYlNsm63?h|g`J&B=Jv&-vG) zu=G5$>R350u3ZZzo4pGn-~jPK)2G@OX5T}}-~2@>{tDy@ESEh>*k*|A!$*ppKdBgK zp9D~9wKQN7-xE}$1T)*LR-t0o#Lw+!$-dO18u+WQWhum`JOFh2 zLb92rNPt15DyEsGq74s{nSn#(fQ%X_G+75i(hj{m)?Bm+6A)R?e?7gOoF?Mt=1Aj$ z1`g*r&vgI*d6KH!y%;AcIwX#!78LHuMk=keeY4><7T1f?w;Dr{z}qtN-w|o^W^@b> ztL@r)(yQ|aG3I;FxulQM{$28InOoH!Jo<^*JAOzm84Z&_Z+F7hihc{D#Cfh$UsEN@ z`BhtgN;Jw3N8A4ZCAaqXFl~DwE)vfs8s?BHJ01u$`xmjb=Jgv=gte+X#QdnIax$kz zd&S8d?Xx)x(xwY(ZXVe3_VI?k-050-7mCTv-y^+ij_}dp-cxhiY^>au7}%K+fzB|< z#)gqYq4-ssIP$xp%aryyqtZRAvMqOS72x;fwq3L3a`CXr+SG>)4x0HcB-4e%+wqr` z%N~?#*DS>AOD~f#q>(qZ=gf@?^*n!6ad|Jo&nP(-PV9xxpK;ZFD~0HMlgv(x!k4C? zuW4aueDdq5NIe4DiVvyPNduKL0RTV{AE9>(ac0)M$=>2}xV8p|nu5_oQ|$}+$8#wk z)5m6p@!+_FWMJcrWARLgjX?kfR#{|}QFqONv$cr)r~^P6c=QVqomm;g)c*ieT!)L< z96!15E|`0vY|V+m;1-ZOe2VB|LHLN|=S?eY_^9RW;dQ?G2RW3QM@ba3$lG@kO&Hf2 z^G{gJGj^A%=xpj{{W_ZZ|zDmtU4C_dYjq9T$uZ-`)1ou`_~cU){BLaVA`gc?q3%2 zHal$XOg=+1k!7Hnpb~07vI^zO%^6bmJCU`UKa}>i>LM&Q;J3b=pclf~0eG*4KE;G^ zviPbhq)Bq+j@I0Je8aie*;^jA1C1z6n& zPhzUiZEDY;!Q7#9ld{LoY}{Mcl+k8APOYfYCzyP}o!l>p;#ptXO*^$#tgooQ4yKB4 zOC7~~9F7w?%zaP8Pc`W5#cei{oL|pe_rh5io1y(!z=I~NY= z+%Cs94~`#}(?-ZV7pt?#>fd?g^0?VP!#Y3Hp4cQuWtK9$XdLre^d2jJ=i}Z|{#8EW z=*4|KIRkfwTy3!1=#oOLxHoViZ&i?>@ z$lO8K%mpvzOT3HYCERN^XJPJ5u=`w<<}!O0JZfJXnl;m@3tiqxZR$=ORMY5fzow2J zzTf$bRpGVKl6U;W)AJI%^z##!Q?F|cqtbT#&^id8UBvvWlpea(<+I%u`4QYx#hs%K5NyBglX^SXFes-4RcOt_k8&)aKvibe#& zzV$}r30vxC9lyLJi?E4$brn?Q0`zTF8OLvLyMpNZXDoR9uLoY86=PKTMv!ZOZ#6Je3b;5P4vohPl91J-8%+|2u16j8X>jgO zTUZQ^#gA%oX3NFz%O*d&<=@*yG>2N&q&U@E#m~Bp@WZnyZhhX*cSFhziRjPiqqsSb zee%~Laa0$!`^r~bH*Wmbi#thP;^+2Tj^^s`V)`xzpY!8tHvC7nJrz$8ljKZW_Z-w# z=hf3wS^l73>HXou!?)g~eFtxOFCdO#CSQy%?%N|Gj!j%)k-)eHlle_YhKuPwSpKhE zgHg!(q4E1G?LI~3Z|NkrH)T=$4;8V0NH1DUw7tr1Etdx0#oO6Wduewh;#S=@+ydq> zMh3KS>b<^4^>2)8=u&UfMfS7ATmF}F75csgQsFPApQzqs+YZ}Xb7V)ivgYgbxk)bC zMSZ%3z{vijVyli(mgVj}TK=14sPsKK^!NUguTz}2mdQiCdCLRGRu=4zOG~%GEz_qC z)M(iSG+QP8UyR<^TVB`y00W1cn=^RLqeO}Ufg-NkLYb|&t4 zj|08&qC*3vpmU>qW~HQ8#DJ}EpVVn)Usk0Arqx%c^!A&r@OC9{eW$u9)7$Mlr{kL~ z)jnDXcGc7IPQ98qB*|6H1GtbXT1VYg>3qeNjSE$~^-(uNufk!^chae;_D%Gm*@uC+ z3ni4fx&Ht$-REwpW#nE(=QOEa#pqN&MfrChWDUmVZ>mA!?2EQ$ z_1dQ$x9+X`88Ki6H<3>2(A9AGyjk73L$%4?wxIjZkWZ;6CG8$-ji{-nx|Q}AO;GqH+i!6Zu?mW}-lXKH zM%|X#QMbfo@^`ja>_w~ZFD~dJg@BSt(v)1=`PS{lxjH0z%nvWh^(cJ|{V(|UmiU8n zZRziLzE@x78r=m3x(_A!gT(vakzE-NuuD1HbKL?9zEadGkbcC!0x^_#{=0l#29PXuUlv( zuXAH`j3l28O8vEyV&$9O7Opn!sDFhe{w9x&W93_wmvvEZoTab#oqxiJKUEH6w}1o0bvdx#q%5ibDqmU{_0Y$G|^wVc23M6@3~3$ zRU1vUex4pKXP;&P>wF0dy?pnav_H@(C^OB9#=>D%Wl_v^zNV z+?lmI6PNOweUjrG{%31lX14Cf#X|(}TE{7K>=|+{WpE*+0D5^ZIgq!yF0A%kbR1); ztsLXJakm_~yZN(ZTR$Cxz1{fC-V)M!ws<6pMo#9)9jGE!o~yUR&&5i|Zpp8GSxcLO zw;lcSgZRaLhv*;bx6?NC*?R};;k|8lggL$!_3i2UZd>EcmeA2Hrs3N=K092DtPZFx zdxTCd9%IBV2Mx-`7oi#Hww~Xscfzk;$^f>z~ME$4dQM8=nw(wUH zJGj2pjc-bUAMS_X)$BP9FUfjY!pBd-kEwi%is%UiyIZO575mpZOz2oz8xd&Fm$&RA zr40cpt1sBal;{LVe79?!B?MB(k{G9&(0>bP%Ra_m*o2qm|Y8Y4zB)Q1z<>=fyB=G ze`b{XlrD57Z>e!XO#%DYIO!tbv>mkaJ|6TeTSdoiC`CGSP$Y|r)qobe+#XTnjS+q0?11dk zQ$#OKamn_T_SCpI{%m*vINVJFZwccQr%DiNv87T*L^Q9Kb*MJUg(TaqWGFiaVZMOCjZt;}0x9t;>frooVx=EnEvqThDOYXxJ_Te0ZOx1=I5G?zhHc zvb1}@xZI1oe`@vCSN((j_t<%hlK%kwj6Gj@sXn9dww!ah`06VLw(D*0Y(10kIY@_O zFSu}Yk6;G1AP{R+@-A}SF8a^WURuqK+kMV0uXoz=YPVZvC#o!~wiecdMS3o!SuG1n_BuX6Psi_DVD%~7MW=erI%?9cTPoc} zt#s2+DWU!VPlOkuQ>yAOow}Wn@`Jt2T zX=?i^Bl)y(kFWsJM~dWRKM}`T(QEI2We&ZWIK|gZe#Q3G)mf|a9!LVG!=mcr$}xe; zx|@|q5B#Q@li}}Gr#i6JB1lP5N{+n>!a40d#{T+z$we=r&UD*zp6KqkUAv->!@gW_ zimR8*wx*wgwiwTk?y%g!#$1`Q@|G-vlz5uL+UsRyBx|I)y24w`86}*3zY(F3 z5CBxNF=cCQ#On#`?d|YWJ3tx*lvbZps2>ZB6p)PxJ&Sd*cU+J* z;ketD(Z(klR33(htqR}9a&~hf-M7%^=5aHzP2CxW+w$Jf=02fZa%-u(@@cB3J8RIa zsTJj7hguLreAJwcYSyJf-y3&%EH;o@OK~O7!r0?sdym)^JGoGTa&08L&fXpb+exRX z^#k-<^~Lo!^fQ>;UYvN}gR(rqus1Ecvi7`jl8RXW0EY)OI!WzhJ;AMzQ@OOIb)x+1 z{Y}Z@@2?)s)f*+-~S-EZMsL4#O9}|&| zMn2V`8h{8%Mh~JoxS6-1)zs9!PUF0L46;KTS>IX!6*)D4p?5qldX@oR0qh8EbB)jc<*ck}sNb3xi)$URr5NXtYT8I4-EbH+xRt zOy`X%brdUGO!QXnQ4#I%lG^swr1aL5Qt;Wn7m-wE7cqR}78@BpLg~7gU}1ZJTVVZ~~I zd7VNU1l_=RXx}BHBMwL&rmJApmbuTM4fPBv%Fxfq)Jn8u4D z=F~K^@fG_Qsl=OX@jQ~y#yLOjQ>9Hl_3Lc(Je2yD4oj(5?3yn0EUS;t{JfW9*F(wQ z{6!)D3hW9jmpw|_NgN@PWJ=TQQK1SQ!30zqtQa{!5D+Gh;UM+!TP?`ZIF{wpPf_-( z_L)v`DN-=jpjN#?w4_y(NL_}yg*78vf!nc78XQ>c@uC=^!EI>&3l)4Yi4X^?O-OAJplXH1DofehiS>``;S$?`F?pf7jg7~z~4%(t-*GC zdv|a7Bix&x;gH+8q0z+hMvkpV*cZdR!{)o$E$(!WcVYSmfbzsFL2Zt`=cgE5pSpQ9 zmis;^#v^Co!&`RBc7WE8gfvsj%cA(kPG^ZXR(+=z1Hyty7-yb6K5!=7!{tT%W!KTf z9djqn(7J|{9lq6zb9GcUT!N;jliA)|z1dhf_D0`uX4;L!!)z!Sp*^RxX;tm=~%qs(8pwJ=V9A^bu@=gZ!Gro9(`3d4co z?oBpk{tM)d=eP1!n9FS2#u^v*KE>tlkGEX_E}&F7JZNsJTAhwNEkRPIP367uCS}ui zJbXpf?dhtX6k2GWUPNS%H#h9P_p_STt~&jz>6|2fljOc*?J))6>OYrN?5wR1pWNzG zPvr}0DZZyglnzwP1OWg*ev!OwWNb4+NnNQR6Q_dyW?hp%r?Z=(y7o6V(?J;y27}nE zDYH)YbiH_x5fSdDhKk*pn4GG7-F;knlRdPzbX-|yaQj<4j4;gZ*Ph86*#7gSI+qW* zd!H^~`1ABc@|?}r7_;Z%^{f)QPQV`GNp%LbUt{B*hs61x^$J9@23Ci(_!ZflRa=0^#1m zXKpf086Rq1ZvDG=XS=c2^FaG&cRAuo1uO0=pLZIF!-8j{7(Z0lu2QwJ#v6kZ>{xzz zFRq|?`Kr#zh*^t$3eGQc;PRJpTgPZ{7p)lfv{;oI2kwWNXUY>~Wb-n~{8 zrfs*>^NZBx2Wf5Xy>{_#(K#fvflYnt{hMWo^*r&ww%&JlNYLgQ_Ec7ZbdkEKl-#=o zx5MV*ti+Q$Uf95@vlmoXpHm`npQr8RpT2-TBO6)e=5+8|6q+?k+I>yOo9W@X_fFlo z%VPIsrLE37F)hub?cdx3t3m$py69Au*&|!VfvGv)Oxx>f++0}6df8>RaX&Y(fLmQZ zv{bx#82qd|ik}|8m9}nSuvpJEqhN49+H?*D$6nv;pE4-0@5nq@cNy=Lx5oC~TUOf< zxMwwGD*Z=xwdhIxs=LXO?KC_{cNj}@Y)!eoHZ&VwB^4xQt(But;Cl2`rIjx5v7y{- zkERY8$8Ef1So;V}q7o?csWlbr_Ns3pNR8_;Q9a4@`^NVccJbw8KPR=Lu(Z;;bu>Q3 zn|Eg4Q_1pqakUM5O}5Km@%xt%ceeA^?YpzRjm&GD0Ur>c>aPB7a#UxGjrVGPBt5A3 zi1pQDh~Pg^d)H>Y!Nc`38#{InmKqD);p1Oo>0w`!k?G-;Q_eib`gHP0@b^*2ub5u0 z%#(?;#wj-YtLF82CcO&TRooTw>c;MqA88NM#8oRE;$NA4=2hFf(w(hzmD8$>_S2y_ zWjggT=HA_}d)VPQ_8P7&_$JP^jJHEQ?VZy07C+{nXsYAiwsi6C)tg7KcL2*iD1(VT z4xi4sv$l01aZuY$w~rA=9HbI-BlfCufwOXK$(C7WS++@NDjGwZ(ueF?HZrb7xU_6f z25wBH*74i$e7~7s$JR*?<)==)6yenKS=JMaq391J@2)!7_Sn|%UDsu|pO|oEv=G$Y5o0(sqWAZ0s!cvN^~~8 zvU;0kbIp6Ne_LZPvi4fyanZGrTQq3l`Fxi)_35Y7=~~3LFeadnA`a#n$n)qU^(; z$h#`tla#y9bAfKm<5=`PN828sI5Ug5&i8a#ZYz9F)5cl8>RTtoTo}keJ)rjvz1~W_ z%bVnToIK62q47cW8}-Yz`8$*DZFqsBC$(54hxI+=fabWcf#5nXviU>Gu6FlaTWQ|9 zo-@oGi*AY3@#-sFw&1^x($d`AUS7yafK-~e)aX6y>lWp1%zX&?2Qgw(WPRbr+qYue zwmr#%pBTy79d9d`;Kzb1p*kxVe7(E6C#lV!m-|fN!+afi%exac?WKj6E4pmlL54C8 zy~dOtR3URYe=xjzyIIlWysnz5=?~NQ=~wELa{7?t+=k}b&c$tpO3AKnUI`yBLr#K= z!g;69c-L|KJ8^1N>d$AzaC5afx2JLT*~k8v@3v0nv)hqjB9b_n8y$XF(kL6+bs>Iv z<@uRTZ2D#@P_@+Gel_&_wRVO3^YK{*W*kp+jc5(>(Qr0y)trt)pW3#^w6i{?862L? z`eAzd-FSPUXO!B!U%N6^cN&k1-xEB*3K2qkC_Ss!c`u0k-efI4j!{q4HVF$1l zzrBQxlq>b;E6eS_HEufZO}_oEpXO9N&JQ0MV8?Wlq#E^ZRjc&To@;ve>b=clC zR7mWbZO6Q`({xA_yEv5;7degkdi z1%^Hdhwfp2d~%ioDm1T}_5N9$Qn0$osWir zSH^jaJ0#+~$EMxEw(wc@{7%2R&1}18$PZu19C`ufzCDk5&oL=I5_I=Az9#JJF}G>B z^_1e@wZ(jq`A+PclP=T2Mnw!QovuIfZX6ff`Iu8CUeA%?a2;$%)lYVX^I!zmmh3+l z#6P8RuJk(=IvWQxfuAnOY4W0Lss8{rs;Ry*Eh!It;GJn&A9X_1;7@xaO(R1H2EE=s z3sHv34DV@^?(H9fz(roo=8V*V-O*PpmxY_pZ7J8OP@%FNOP>?|_^Qmm4u#u&{_F2p zGK*@oFFsBFl@tlPEoPYcmj1%AB~(rYTG_-A_K)XPnmQ@f7c34Zx9t2@tA^`ruUoj5 zkLB%EZW~#0+BlZ5pXFT^lVzv%7afzFT^O%YKkb5}aO~UsCGm|1ylK#XLaTG3V#iY= zV_T$pYH8r8-uxx|eN2sv1T~_bBSk>4Wh_W(5P?sN`c|v5qAb`(QN+U67FFNYxYu5% zQrrxYjkW|gO*|C|$mu~B_9oO9Fw&Gd>;BbJxD<83I`nreJXtl6eay=*MO0*%+it()1RQZHg&_PeeG^0cg#eh>q^vvcBd&nBqXyrnDNarKl$p=X|DY<9A2U$a{Be z*zM~9X=c|JhdJHOJh13PgQQ*expp}O~uuE><)&D zgwg1`v-~E6L!#$O)k)M*BV{HkMqRXxL1m;4ii1SX7AOD<7Ob$@-CanCN&u7siw-G+ ztAL`blV&?fCQ>e$`N$fto^uv{r$YUS7B))U86NK6i%nO}c}#6;dYmh|6-<9~+1py_ z)9eeMZaPq-mqhAoKJxUJ#M|Q|k;-4#8%%~mSGwcnN!TAN&ZK~P>bda2i=K?`L!Oe2 z9BQf5w>?Sl#y17%r;|9Bk67FmKGP2@04PA$zj%oIw2%HFvvn)ElZx%()3UYu4rU%} zyO^Z=bTumh!np2n^litu4a2-G;^eL;ggY0S`HVj)@yFT&g>@aPp3*#LkEJa!c2^v*9Q0qVjR*eO?oEGaa7N zvWK&iQktn*FHR*q2PsulX zzdw_->E4F_TUg7fNx3$~$MZ%@T>-LrC ztgeL%?u9!i6&&Cjpsfn@3!&#O^)c=22CvY94^F;Ib+bz>>*z_S@g6^Nxowr3EW7BA zr|v!;3w%12WLh_DbxxJ`{mR@O3e$-~nXRs)+^^Ya)>}C?wA8c>0X6IZ^n$j!73I{WPSAWhtRqdr zk#x`*T6il~`W0@L!qgOqse@#C1e!xKn0tT6k&}&CIKCka*YLr3p63HK9bA zjs^ABoqdajkWCR1xfZ5whu#G;#Rh%#l~+(-n#oX%qBj<5a-{# zcC=iG$qXl@bXsL~#Dp!T%=gC|BF}GePU*#2Zu6Le`y(CY*U7KQhDSg{d;3Wp2a4wM zIS%Zq^wNi*#G8sJK9x7^hY9-C^z+Gi+<4CHY;NesLC83c-so?z*K>ZFXk=@J3tZP8 zzQwl|aF#5f`mI;LUx5sX&w8g$-*?_!{P)XSnHF^@wm8#npahut3ym==wDX=U9uw> z9X=&NXl&H9<&ENNwig{5boT{v4@7N+Xk=}!&(!gDS&#Ug4a=xad_!sb68@7Uo`w>+ zjIg{@PYib_E1+H2G8Q{G`jglW`t9aJ?f+k$5XRtlcoM^EGw zbvqHng*qD-=@Zi5BYNKTDa1aQ_;iirG7!@eBqF;PwLi9*Yx|3r(p25vi7$hp>%yd9>ZR}etkT>9(wHK?bQc6 zaqX$!F0h^*vB(g^{{SD1nnstmPoJ6^@Rxk;vbYg`kjAF_52Sg0edBqDWfpW3y78{0vyr}2q5_{pfssfs%OvT zZSJE~YpOkW`aXR!H*eIlpR(IhCQFIqy@v!hThnw`F#UkO;gj}eYbi|`V(+>r_WRwUUA)q*OGB|FBcnM0dZ~} z6{X9n321djfYp9*do(csk1S-EdF*j!+Vgm zwro?nHq=nQrnT;0D$CEDrrGGY{yjSi6K+)N(_dW?qh6oylSirVt;%=KO zTnCY^rizWb(~P%8wEYKNG^0+$Zch^IPB7t3sfpv>IK_)RZYCzi(9!+DN|LggOGA@- zH*PwS z;5*wb#+#m4;Kb$~CDs=yBbgWykIvTc|fQwcBEz5`TG9Qyc6@)9+r+&qKysRFPohe=+`ED)pkh&V`|K z`PWw0L(63TB9AZhtFSrjroE)NkQKD_GIi~DlFdOCK*&H&s2aio4aqd>w!vy>WxF^e zkWPx^IvQCCu>v&;bt6N!$Q9|Y+MXeYZ88g@Eq0^qTVXPt%OrN&mxi~toyuM%4N^W9H-YT3`synD;_=-QeOxq7}b*7lDh$zIzQc>}#~WiGgQkh#S6w@_%k?;UEg zJXUqeK)@NBrkG@Wj~@u7bULqXiEmmX!txm|F=jl5Xf7vnTKe{%V)Sm)QPlHMlWfZS zGI%VcbNjno+IUo-4+^ez%XMNm8ai%lyY6Ov9!o*%_ASeq$*IXBub|$U?2zr1N6|j+poo3nVOf!2tEcqtT??+uGj|LPVR0>{g1RcklJeWRV?2ZBn(M88#)N0j+8myz<{A3lnjdNqi4C%UIn8&zU@=az!+;NgvCW5=4>s zf(;kg__-#kx*rebztk*y+k(>K4G3O})J`iTZSM$pss2@0HfUm{+mFBOb4wpeokdor zjMU0l?61PWUq@3-R;k)e8|_|cmwk>{wax8!mrru%-p5SqW977Dd_GOm7BrfZPZj1l z3_VXtg`CZf*lsM68CzPogH_~m@Xu3;l}364!1&jUm8;~7LdaX(f8-A}z{$j;JuRE* zqtaZ(lP$Jg&AqVw>*E9g)}?TAEGm|*%{FKT$KTt7jWaojvyvNN=N86lMFWqA-l$y1 zM@HX5wUhN#kl1-SFRiZ~BPRjCSIkvujw{{Jv*M&lUVT9~M6ia(a`?lBnz#*DYR&|< z^ff%FF4J3gzo<;_AzVit?n%ZmBb_q~i4FwN1FwY^x!}om8x~|5>Rvl`w9s;J=CQmmV9rb@-+-5GDk(wZs7_7<(&HaltZ%XWo~5*g^>N*aNv_pQ4bxm;#S zvh`P%aC>d?<-Sd6=&|9mJ}Hj#8_zwsZlu4HAnCq&sTCkllc!X0WTQHnQ*AcRTL>;L zoD9XFO79;%RAm;CG*fwRZ$)>vtuJx64RrB!ji;zw>yp%r`WTaK?d-lq*3d%%E_FU* zz$3+0oXYNWJx}!4uBUNG;je9MNBL z*$bKVfCu#ei-EsSvQ!@J$KLC+ae)py7D4c zK0liuz~;52kUDrTQ^H~2c3s*(4e9HDsPy*c)fZ{x?cMt%Xn1#VK0kj-FH2`7P`ULI zwzq&ff#SY>G^*X>hU8Y_$wuDLCI!yuE9K-Cd}eD`UqbA}XB`uo()~OQRAq9goNhXq zr)}qtb332!@m#C=v#F1Hn-8@!>@SJ0m*I5-x-LEV9lTrmo7KcwtUa5p-sght4GHmF z+m_DMFX(Re7kXiYF^5Xt(mH>&P@D@ku4-eA@yd4CQFroQ9M`-CfI+2nT3x5uN^e5w z+upD-8yjleIgV(`z`*NwX;4-a8O*;5o@Dxz=bg#U`^MLG?%mOOdmWf?$Kzs_MlpnR zG!zQutVv5&a}*R!T)oTRNeyxx^A{3B=5c%bdJl-Mm75e|Qb@CA%lx;H!Jv1;PbJcX z9Sst`kvC_2+2|eksi-n8`Fi4{VRUo=ih)n|tye7@ZQV9I1C4^#-Z}v3oIWzSk~%Qz zZ1);kxzoa35p@eF-#WN1MoH)_eiPDO4#wZ#ZX7&k82gntF#10v7vsnlTy54|IB%XPx~I@wcg77|d`}va)O(CT;^1*(+Y*GnnB@2Q;wP zPc_}fp600N{U_0vaT>k(>{J)_7f167HrbhbEKNJ!?+%0-723b%9~RSYOF-}!Y4pwNH|fi=Jz8*H*4#WDYImEWeBF46~I$+xZ6c;Lj0Y&MpQNm+S|M| zwmJi?JeBKq+eeGPdXH9ohyMVm)9O{9*q)O4U(;U7zBml$E9EBITfY$SL+0DA43bdK z`9M+L!oI2f9^xf`mWNtL>VNX;zBET8%~#`Z5?WSNr?y&G;%t`V!Oz9NRbTYw`disp zxw89z(ys(=oyWNFkOF6p3HAi|7S35t;zRB91XPnwtM7RE3O1F;^-XN{?sB;PM-|D> z#Dk<`Cp5q1QMQkU?seZ%dW6V&rsOtQ{{RcjV|hP#ZdmY#FF;&u_fMlJ0AnHuf`m;sdRBcNyM6b7OMK)$lNoKs{-up;5OFufu&O zr`_>A1&2kuxb+8OVZZAqbnO0j#qHPGZETj>vdZNl5xtPdeQA8gN%02uj^{e061+z% z2L|B!r$g9cJIG^W_&}bN$w<{{Sng`9|_4&1WC-2P|>zvp{zl?|t33 z@5s>hUu?Twuy6LE7ycA@$kXblWTpOQNZ?{)@|KKgsFFssd&!_wrmBlp z-3;A#Ms7=}NuXuBM{kh~RpyO~R4rpDX>UrqyI=XlElnGvsVgk5Ab@@yC@8BkX%xu` zG74!=%<3f!KBaES)GQU(%~dUS7k!(w9wWeXST;kcc46KO)KwXN9SgGT))C|f^&ukJ zbG3N(Q}883!8u@SJi7k?EmnI8!>Lt({9kBQS$^N)E-PYtM!pM7&DP6?*qU|nRc@Qv zaM_dc`uVLSq13rC?mxQMTGRfLrEu$5dKd07=etAh`_)UG3${9$9~kFW-h)sz74|AO zzX^UFOpA^#t3m7H6#~a;lyOF#j`E_df1zs}%2;^`Ej=quKE=wp)a%-kTA4o>=70xZ zYUbQ8#iJ$RS^n=$LF?{RFHE~>8KVmydssR+0pU)bOPvFuQzT;GFLQLQJQX=JYBDx9 z+wu<|1NNEqz4Osv^_T)t(jPzq> zMq9Qv?`WVFkyHM~cC5dpA2Z~nyjgyoh{!QCmbXrns@V>wow+4avjk(eqU%|MIoD^Y zTe5(cvW?WP?hUDLIe9m9GCZu6n3k4x14gf+VcC|%c*x2w`zaV_T+MOu)_|`5HHkKh zS#pYjeb9kH;JXsD^d4~T){{X^e3Wsqh{==j!=+DE>9MhDinF*z3%mu1giR%XXzg)W3qiGezKeJUS-M_9fng97BK-haGF8@$}l= zO}v8WdEvft+=q8_zdwcVNij2GE^j5`&XckL(g3-JcJ{;{SAK40=7Y#S!LuR8lMeo_^s)cOD7>1 ztG~VAe)VQDsx(b%S+|k2k|{&|Wmtr&X`e#dxos*b?)%qD(OkDLhK}>{qP&PQ)47kh zSD_joa@_~1&zhqs^Dwjmdh}LdqcqKEQ>JgiN3-@V=+aTA9zV-fY7awA#8T&P=Jsl{ z+E=;Mq+PbGci;G{R{9lgmgIt^TA&GRt*hu_REpP1f6M*gY1CIm zD0MZrp-j4rRm2@XXdY{#a@me|iIP3Hn0vkv@@4mG!~H7u0#5XOXsvn;LBB&ifcl7W z{(o<0{+PTMXLj7S<=H-+a5Bwd8%uj-$pdh7B-6R9>+D#)dv&SF_Q`+ZEk?svBA4#J z{3n_F<{2^az8PEcCV}P|+vOt(4sX0sQH0Ys-L)df7bX!1cUPT4+tjNVx*7XmS>pE1 z-amJ4m_JpuZDzN9>;<)Ct!#j~zE!7!gC#=i$XR3e*6ZYMHQJaaV{n|meQ&rpuIK>D z{HsFwtr&l_YTX#xpJLN z$Kj{7aqPj)dUrSORV>fxRc1iu3~u(u`h)d#hus@0ZKJsHyQ0Ef(YLOmbT>AxzlF!K z%zs0mKWgvJ-7)RH^!^fmdC!afB$YdU#9&MOWu=>LWp8T;bE*YL<>TPld)-Re?-EocrOmYVyLXw?pd>w=g}_A%PjY5aoc+>H&0lF38zZ*FKR z*8}C`w_1Nmx;+eouwoC`8=5LtvR05NDr(n%LY4RR6Gjxs+rQ?#Q~40O7A94_7Ev7x zfv4dmT~|>;cIruxL7~vC#+L?;drq2lM$GtZ7LlO}3S^y@LNSBWLR8dFQ=OCRaz*&j{MU;9tiF9UOR)@NgF zwq<{Y?3|np7dCNaAa$#wYohJ>j0|}Lxn1)Y^$#xYENx0j?R8S8)ahDRUr%?m;_yCR z#J?#{5OI`~PBqb2tv^NTN4z_$l3NFK=0Yec=^df_m*>2ksY$kdQ;p!%G^U2LZ+>XQ z$z^wMeJ9KhE}-unX-{gQ<4UKq$K&A?v^>P*K0>p|++9ZanR5%C{{WX0z(?*@_N0x?H zL*iFDO!OGmS*#hY!6W685hj|~U$JUz^;WU!k%{w>Ti!?{j_vmH#ohbXIJMspEi@xl z3Nz403N0A#J#%3tn{4t~X)a@erZ`Q2sL}1bsg1g>hl3o`yQcj;#5a_xPjxlVh*!e1TT%0D zh&|>#$r?O@x|WJ}R)wQ;`PWj{L(1hUNTbQ%uEgh5gYwzs`&Q*4QS#a4`&McyN~seF z3DpBwKub$XrM-Z=%8PQ57Kp5f0n@L7!(k)0V?25uzAGq_RWq*=`hVVh$&1hA zHg-AL2H^Wm%Y#V`6$JDPkIeIN#-cm?PX_WOQx#-zH@Iw##wPyR*7uf=ERrV*5J94q zocP>^dLz(ryd}jR zhoe`#YqxRR`)y^dZCQ%KW3FRuto#Ygz2*000k!4*&xA&m&?^Z2c9Fbw<)gl=`B`-)y^Bfy7!$-A^CM-Ey4`1>Q1uNL$sc2k$>(_gr1NM!n~d z@SKgg&UEf3FZf?4gPz``*5Ho6*1OQml-8#@ z7Fs6c+1GeTq?S@c4z(#tm99a=#~BO2+1(lHI_j-jv{aPQg10-_CCsiL5H2Ra^Ov&Y z6WVzmY{s{2J>+)yh3akeJWbR{y_7l%QB4#X1dlZ!LsBLX1OWm1h2dLqe)`CJ=z5)3 z^CbSBvg)60KH-&X0X|evxU?faguXivZRG5C?#oY3n$>dE*SMo8YGo_v?7zv`2=f;4 z-Q$cBezGZO<~g0TAk-fP>~LjSBa4}swzPhIJo4CM=MGqo76`3|V8 zV*BR-{{ZX6_;))00E}yO^PBTuulB#Cdh1Bx5)@@?QD1tw5up{5mN})br4E{-LZ$hQ zygji3>d{wbBO?9A-iorPr7Eh(xoUJXKGvGvXT$Rz0Mk;qnQ=R~mrbeKS$sX0$uiE` zH=wBYFEx>7tn|3I^*5J@`4bBT)va!r#i{p3J6EBElY`tsA zX!t>?E5c>L?mIa5Pd@iYrtWI>C(740csC4w)yE$1nBLt$@zbiS2i|q<+;lQ8sV}6b zCheXZcO`Ld;kn(JOeLYX%I%KP{hbLKuR)0jZdF$fFCz&oB(-FDZoZT!ugn2g3T z?vDP(W)@1vw9tw?FKD=!sBVj_dl}~Emp#R4oSAnMf&EWxjq2f|sPHS;MP|#q%Ue5~ z^7amit-o<;pe-(|deczqh0n<1p!*B>{H){GRBh(ljh@~2mlog)hdP~g>)^FKb9{5kuEO0HB2gRO?{8HXW`_HDRc6xQw^Y%<#sl46 zo+I&98amcluzNb@?FY){Paac`gd6V6Ah&Hckb@Dx{dKpz;$yT-qJMJp3c)p zQb)u^4wTV)96n0)oqR^0PN$XgH!&<1{+pVkbzYBQ^9A*C^bdXXxb<(dc-s%1zTNya z%J`4gao*;-XYU(5ql9-*z3nBX%yAVSL>2Bh+Y2)m=T8#TPQSeur@{Q_ayZU%IS`G- zRHdamn(X~M4KTIHIlYdpS20*k9*#HKGIk4cHZd6QqlE(IW#ZFP{fowG+xxw&EFwx4 z(Qz)?J6QM3=eBuH;?7tjj#4N`iCp`ZPQ|On*=;^Y$Yf%Lt(4W>4NB+A)~8a<;zxGw zJJ>{@s(YN$Y5^xjwOCX(%I!^N-Fw!z6Gr(e9NKpGc_>E2YF4=PF-_*@fzN3U=zo_* z+sDRRPG#BZQZl=QPSHs!2| z!OC3tYvw;{rF9prOG9KZSC4szx%FtJO;;xDdrsywwLMng{2teK+qBAMbHUP{+QvZq z#1ChHUVig#M}9J-Pf_^Ga%{|Q0?69T-sTpH`JFY=ioL{g^5Eob>Es)GTvRg0FL`%p zX|GzXa+ZgI%Hg7>+b+$yzO8amMkRRfIvsUTO`IIQC$=U~wvsD|+azx82T(_nu$@mc zdJ==9FB=J&7i-Nb-k);0YUL5;^6j~3V7_1B4j|_IzB?t#I~N_iFR?MY41LYzyCr;3 zxsPL-$2<}$9#&ptlR6kU?$xS9#}*!i{WC+&`$w5^Tz6rN$nCsE)+hKU=^&cc;wi&B zUjF8a7EseeTCLH_lb3A@m-4U1)9Jgv^=fS8&js7n? zzo%B7Gs%Q|Y{{qEsJlPv_!=^KPt4=lZT(TX6xX-)9;$3hj3#gWN&c=kly}4YrRZRjQzwbYUN+`O-BhuRxc_O7QRakwbHw$xX_SK?X4u%MszoS(hA zd<^@*cGx|McgY;-xfmhipG}OG$7R_+L7ClGUD3ta+(0g4jcPh*tn6~ymb0F}ci-+R zJCv<-NYe}5$D7=F4A$)2cLwKi?+eLq@^U-#hs^ToA0CHAYV2dXY%Nmb1ru6*O~P!dDwE-Bz7ydK@F(52y8xCi5M_yd%h&nQS)CC+rQ^dAILl zX(AU}%@p0vpwYh&>Q{~D6r86%&EGox$FjglK`%k)C#W7)ytmsvthepO!5f#JcM#gR zb~>gxKHM>`KGvFE@A$Ly4*GU- zCB2uZAExfaxRO~Ly!{Yx&Qi|Q$V^SU$IEd9@^+u?5jzEai^14Yk121~zGojNd&0h- z?@Qf8&(BNN&egu!zP!Gm8+J$xEXQ+CB*xfCr)w^^CEd(=dsqb*)m}omV#{B+m$|j} z98Uve?l0}P?@9fp4%kq-P0hD~ZojX#f7r>4r|?>4Y==WH=9&ie%mMjBUr%rflVylR zvSs|yd!7FPY*r1Zg7OHU)nS<+kx@kuPuc_DUkxa$E~SKC7DV>9-KvcG8zI!X@hA87 zuiN&neEOH+(7P_21Y_6;KYdh5ve^c%ap|Eyfmkw5SrR6eok0ior(u}&D$$YNeZ3WD z(7R=q4Xc3-q5ef^nZ6xMg4BwAilx%QaM`$2iuEJ*EkxnexvUL450koms+Gg3W1)Rx zPsQv%>{RY_FIehitW8Xj0O}TiKFTULzY8zh>SSDZ=V-5%ziOgbGKMkwG!Cl0{)Mb_ zCgGf0*{;8eu{K-(u!nXGYeBVZly&MKmq& z_bSc>i*hoqCGBuygQ)I=DpOjQQIoNyuVXuCO+|k7t&x{QC1L}6MJYi~+N;ZuDI1GN_D z#TW`q!PRIa5d)R5!NKUY+~{QxhzG%Pr)fxQETaMdSk4=p=HmYdG;$%YZ_0MB_rjk#bNELlvYu)Ex8n2OC{{UF`i~SON zYKG6~auze;5ZXKzjXy2dO&7F_{s*`0JbeEE{oiNWX{->=8<}{Hq1M!={wyz*^MC3y z^bZgInk{Xx;?p?t@cUN)^l6zfle9L4wFPE|bs~Jy#-E6_t{S(^V2MKG7~;3ux6NP- zYp?E4+DP(QGF0st^O~H7k_Z$(^Og+Mx4C_?vt{mz0zdRVOSkQSs}>~p2PV&yOnhx- zCmtHz13Ir|g1Qy%bS?ao9p2+jKdpKQo_^_O-$F=sl|CeO@mWTq$;iZL?KKszx2mw6 zQCXHQN;C0ksI5KPt+w8#siMlgVece)>-eis>S$`EyLJd@Z8bGrD>5s|YFhV^(yrHE z_O66pgbQ-wCm#9@4^Oddiz(mgP_7%H6xO{Jva>0&ww*c-iosl?PRdk!{{RhE#U^OF zQl=d}x)fI#TH#2$6phE({OaYIvaQnFm9%wy`u&TeZRl%6jI8VJ)O*%hrs^rPH^=+k zR#8&nOqsO!hfuO>MKV^B>XFw)(oUy6$5Ms5_T}#B#$)!j8X)Yp=*Z(Sn8s`IPk(<6 zs|KEKkSqLb_TEzNP5YB&jO!`dr+pW_qO)@yXI{Llss3f+E^BL*;h`85pW~@(X z`Av0`9Vl>afxt<_T--z$&C5E;;kpyLb}a6ZN>FPe+e~J+H9vjIG1{HUJmTp^9KlI4*KBCpfr|PZK?X(R_jtrMvVhn zu9ixIYUk|9QTqFS#=R#V8>-i8>H2&P(%Lq7ZQJRm)lWKYa$&&l`U9C zBfE^Z!{e!;*Hz>>7e5t6_G$M0r=`M6!>@Vg&E@m0F&tUD98Yf8+}Ii1e1*PpH?Efy z>2wC+F#@ktjDYgZQ~F_%sU&6H%W=C4Qszei?iFaJkzNr z!Nis6c}p%a^-26xU*2+fNw*Dub$sN$Ngq3V%KlS#aTsv{+I|YQIwzad#j;np*Q)Bn zp~1P-mo4rD4-XAA`;nS0$gN=~bXT_Gx)%@HwB3$|(&Ns>^KM%9bS*vIYtd+fEJLAt zanY{X+_tsr+V0JLp=Re&aWQsM2Dahjw@R(H8FFhvg)D5NjR2ysn>4th-kFULaXl62 zTqsV)q?Fh3M>JN@+rH}NW7`xg1S&8MLro znD?d5;mk3TVzI+-Oa%oa_?_*#{+I|H_2FOaUMevd2bslpsbE-qd#NR?Pp=%U&6Z@_cu?S3bMt$2lc`G zH87yc*i1-losT^j?}U%PMSsA#9>Ye@Y20;z_IJgshEIr*SdNY!@TWP-9Zd@xbE4{OKc?SI z9FOY%0G6ycZ;PoG;JApsTbYBKYYYt|x$-%^2EHqa$@7uHw5~}hZs@9e3@&nH3eUZ0G* zqG`y3XOcdt{UdSa_s?g!2w%R_ap!J~_mRfx=RPKHLbjTVHu&t?zd3PAT1S7$P>kwQWa2 z0nu*X8Lc{;J2o%l%Cl9Ed87QpjD5ZJ`4MQ>KjICb(4QsBo{oppF`%UtF7%BN+><`q zdx;+6)|%>kmbkr7S7OR&!I_=LA~OLsD_tw?R^tUi^dey0!@1erC+~XH9aeTC!Yx?$ zY;m+TvVl({=k}(=WIuQ_vDBDr)YtmcVk0F~OT)b8w=I3G>#g4rtWRr*E%B(KQjAX@ z%3&!=r>|d2osZ3Xq5GrM2P1C_%iWQZYwttY8lXi6iGXI+(M4?$NiSFMPd&!^5JlHhK;>5lV-b-l4gBIg-h? zMm+xj4~Xj+H3h$lsX1zWQ;Or%Vv<@OUGv8v*zOY)HqeOVi~Zz)0DZOlRjVCOU1J%f zYBcu|y^m=<=U%icwT|>F-?O2L_dTpnjzZC|UW>nvGl`L4)80sswX-+0fB{~o0%w=x zEgC(%PkfRN&1zS)qVhKcI6)to@5(zfv*9l3fU9 z)x-NBd-Pis(;flQ){h!?>*ZhRQddm)Bqv+QJSq1rit1z>5cv-B{fj%s*quyt>rk_} z)hJMrkZJt9Rfw{rktab~(^a^M-J}YvGzcz`GhVbwDx-ck<44s`(?`>`Cue$F8^PDd zDVO9+c3GQ?{K~!93ySEx8ix?)D#{e4oOnWj1*O$$VXL%U-i1;;ost$;j@l z#U3`o*^T&#bd6~=Xg*}Cd`~d#@iM;U>h;!#ue;^=PV$b^>E{Xbqrm!a;+!T6DRKIp z%V8T1PC;LZ&7n1=SI7CTe(l??P3qlQ>G-}9)wm@?O1V=RiNjo6;$yWfmfiGpwW%a% zJ-!QOKO-r}sdE1S3kb@o76#PkUR2?G*s?nVbWLGumc-3zN5!7%gic zE+$HQYP}8<+Y^bGZ&OlPv9g=*G;x?KQeF)mK~TMJCPgB7jI3=<#>M(N9DBBHca{EL zIj$v*ue!4NFAigx0RpAoJ;%erUHsOi332geuJRqsQsmt50EW{>^gSpSZx1HA9)B+d zMAv=oxkK$;ZySRg&}&T>qp_~1o6AVmY_>{hlEpKtq0z8zF%#?L0GE%f23P_C! zWw4My5CjM5-^M%lZ)DGJ&1i0=7HpPoe0mNjd?QTAGhU_ z?g#d?ABvP*IX|f8&*^5U`quRyWxyVfcZU3*B-?xZiG7u=xN{4o4h6j{P-{vrUBGdZ z$;gZ$t8DW8oz1arp9=f0bKGaM89de8##_v^(n~zV)VREoNk3}+HN@cfP*LtaG0fgh zRDSU>fqV#s*LyYbsUZc%!p^fNwQ&4sAS)qqNLEzy^i>hcaq@=TW3Sj_yta0&dtTnx1MGKIvc^JAjOXUqr$gpD z>!tMG+x%II{{Yul#MrmkSQ)9kvG9QI!0(7k8o&r>sjptTuXp9HN9$If;g+8B$#OA^ zi;_NIEi-SO;m(lvKilTL{oFMae7094q|3_7Ecm+!d12DO)VWu%l#BTMwcBcy(Agd? z-WPDs6ms16#Oi!KJbmlW<=`Y-!R2KpZYDZ-a&Wie1ip)ta~6lDv6&`joq@LEmPXOS zr2Xrie#%Ji;bgZXJy=>pkGh6AHE1JGHNd+hneK6(h~7>u!`a{1!06jf;5rHwUhLM= z%=#X>dT`-9K4%)-jo1$Xqu_Hv>#vFExNhF*9zUDqJP6gB(SYNuHYO$rt(ewF;u-?f z;FhfMIi6yh(mD}$9^SN;+F2UbLL(mo$P^9>jehH7xb8-BdtS9MP3HW@Y#c=QdEBH; zYb)7q*#$rq{)J6ewLKJgr+xPxdhZ{p?+9G&3#_gm2Y-~$JQ0Vr?d=j0Xf*)-!n$sB zM{^&_?lP|JJw}COId_ry&vSmSCS8oeUOpy4i4!=sG5w?5LqR9Fcz7)_xY6h`FrgS8 zsc|=^EFL#$Lz>38UFPSFkAgt*0C!sZ*6VpPN07?Mj3q{I_YGrnTXP!?u1TS?wbe8= zrFHOKoGh6+RQdk^njO2br)lCR`mDIVee3k>*u15-HlwpUyNdUgF6Q$)69eY5#!T5Q z~rN|G$W>~>)uDovHZf)o1|o=7u~E~7rgnu1uM{3pt~Gom$zSnN5n4H%tx8q z*qp0hL3spkI@Hv(R;#gl=mgxwN0 zMgrRZO~&g%UF?pepAPF36K08c=$zq7HpbhHgU#CUkYGYF5x+EE1b~bs1y!5s#2Ov z)|E=-t;k_-bG&sw)2i3vMx^#LEbZB&$}-irLq6c|Lj-Rg6sYji-nqBo zq1DHhrpj#n{c&#uFhw)tU;>N|J+%YLaqcZnydH+#ao;QDp!qOOkaMlUX=@2Oc=@U~ zE!51lM%=%je@%wAh}{RiI%!_2yseykoOdGP+kT`aA};AfL8qv74_{){D@QhXY~D^A z^$~~6&lWcJ@vV@Cf?VOLKJud%N2$W)I462KEllNv=l(z3w=c)x^KF*$Cpojj)3mho z`*mB^LR}9pFDzY)tJJPf$UAkiG#mE0?>e@lLOtrdvTDdnjg%IqL)w}99E1LB6N#8V zDXQIK?x&yS@T8%kKe@Rt8IFwGK>qokqeDUKPiIAT=3Au^$im=Smg$x@BHfugaXI{aN#NJDcyb^E(5(HUnOjW6x^D9`ML-@w3g)bZK5wDp(WhA0NKxwHbB zFJFnpN}UfYm!9fYnPdx|){fB8tre%ow^N%eoi#pmf7AoCqUEkt`Y3Q+gsqD;ygg5C zT$rssV{9;%vs_)dwbp^J*V{$!Jcln1)x?!swI%-mRO1qzsMcyy=%%0jF_Sbak9Q7l z5%p02093y>SdW>4*t}B)Chp_I^$poff;Nw9e?a0_(Rq98z=MbX03&{>PM;Fb9cO%& z{-49At}v|jIr(npW>G^8K!N7AMupTRiFLzU#~sGizhZX4G8*Hz3UJLQMc zpU}HpY-bxz?`zUOYxih#awi@8AFcg{t2#)hnfINwWlLLM8hEL~4l0Hvbv8u#jWIpL zLE7s3yGcKSuHfo+ajw+%L(o3$=8dnG*xZG=c7WWRXOH7e)raGp!{cF&0TsY>rXta- zqJR_SMdUogZa-ID+rLkV?0CDiitaTp*G|;N-Dfj<{{U~b(rlZ~=pks2v6lDG#}`UF zdym9j;&kh@;=A@pL^Q>00+(g0O_UO7ndez`HRz!Bkf$1 zYe6^g8F5#Ztv-Jw$S9?s$3oUil{#IIyLH<9k>c)T%Jx0Z&CzfC7T?;>mp`pGHXV=k zefpB^$gRFq67P#F)G-0t0n6a+au0C-0F-Vjy@c#2Fj}di4HuVLeLA55&?nS{l%`YH1bAH)u5J z{MstlOIF=kaoRgtQ~7@)s@*Iug?iE76)Dty#iX1brOjaKmiZI>;Z&Nb)3D3`031a@ zueWK2kcZX$3pd~83!ElC5-uvRRYnG@pliSYH989TJ9|k*5r(r zh`D!b4~IY(D&j9inMW0H@>l`X5%(3#$Xb`JJxrs9yMCV$)2Zk`1|UD^Co=0Ud}DqlSNUqaM;@N!aI>CWbHAQmEL-6{f4w#>RygGBX-{Rf`zzrm0AoXaG@q8y1pA zmdpzGy<{uFe{s~;1RZtsT)UK)0LV;4;TihwGfdU1JyIXM& z?$#D7?oiw%Kti!31S^H$?hput;1Y^EJ>UOdoH=uD*37-X1E2egrH!@JYYMOGfPNw3rEloWh>~jbg)Ych-yg5m1V#oM5 z?C>9so%Fd^x51kGr{D6mRV_ZB&xB@D0%}8tT<3lFbnV@>FCrCGqa+Mw7=Uq#rG;DH z2s5Sernn##*WGgh+o4)PN=wI0TLe8rsONKjx^0*4%Ld5aRz6)2X!^9KOEn}$(x)qR%p4}%IX;6IZ+4y6diyVrj6O}GWWuuZH)_~m(56}9MX zhlrsfnHIJmocU56z8W%bdTF$1UF+W&SECG5Oh#lX>a||+rQapCdYNgCCSsj@(KJG; zbt}T*Kj%p1Rlc+uc%lbUp&;d8;cL-D3I77-qRM2{&5qHnLzHWPsNc=k_JI`v^D<

BM*4ddPnlLBy*Wc@DMRh_A#C$y6v%$0l>Y#R zN}OMiN(STl1Z^gojU+EFwv0YO?O3<;)?HR-%-!1Z^=qoD{Py-K=&K1Dpw`t??FZP} z+aBN}%X>w=OSz^y^Hbct!@Z>GT0;o_P*%qJ`+(O|)W@N(F{=ER%=G)=QdJG=6_3TV z`(YOmRr9O$DQo4sxa1XW2~ACh9&jHL3l5@xdSlDGg~#e#J&W6WTU#cdS^dgq`cAmZ zR<3)ls9wr?3X5aTufQd$X9@igtMdAONabRN-oM&)@>T+_-b)lg96{m5y+hZcP8dOxRWT~hK zMvC4~U3y0JuRdb{-8br8Dj>_Rhr3wqmm-116=^9;q~;)Zx}{3jP$o|#sGJKfM86fj ztMs%)M*jemr`xu#3lNx>tl*cFkJ%M2D_suBO(()qlrc)ZnAcvNVJf+GypTL0S1#Ks zDhZ$B(87huv7hF&{C|%*UfP1MRojter8<(ng~C6oj=udOpD&USN9!un4UIENuF9KH zQ(Tc!o1NXY{Ke>e({DHkTgY`p`htE6Ajrl>$~Pa3{$;efduhtv8Y5RE2O3=cvL5j$ zp6Rs;8*@T{w!E)K#NpS_apn|!M8F~=wUqYtQ-mL=90RPd+XE<$MqpeR}!KnsH)OuKLZgZi4#%)1QV(05g&<3N+5kb45odN2~$LfIMJ9V zLE#VrNKy^Z3QC7m>Ed8{Q6L~*Z{RvhPM-K+c~5E*z{Ky?(mY*3{VP;goo~WMT_X@0y&?0ouDurwiiV@cI*B@g zd9gsq57Qi%m@bMIH3{5DgefUGf)Ne4eS~?$ft5`z2+L{fG4T%=l`xgx*$wH1A38dO zpAaNrOkkulF2V%Ix2zzIH>$cV1KWi$XK;p~kq^WNBL!C`Tj`(bok-PJ6qp{Pog zxv~>KvNAlnReD!ANsh9hG)X7G#571HMN4EKi8`2XC{qH14Jd=*?1iH4W%-uMjKG0_ zO5%aaTT=ZUqpCFfqD!U+1Wq*2rd^xVZl&f*dTOX1!M_z1)tRR5vnzPwYzd_IJ@z z=|Z=XrAz_jWd})aOOR1sH@if^ooe_9U(+SCkuv@0@-;zTPYq98=8C+jg_SW z8&T?#JfZ^66)!kDFWW%Ztf45ntA!9pNgp_pGM)n90H18)00MC}S|i0doJtIM9kPa&^eT;JkfjX}CntO#hoJ5vXJ`y$StXlGis*7@BDhkN4r&}se zOn}shb&5OdIu)$Pt6gw4UILk9d;*k^;Zc43<04Gualy0zDVDZX#UVGelQxo}y%22GO4kvgM+#^Kh z)nnn1f|>6zDeoIkoChpQ-YKeqLX^s<_(1XSF%v*a;ZT%Xc@KaTXQy9mNY{dgbYvk^ z$P%8D@thFxRNAgmi4&<3`yvuj2UQEK&Yt5O5--rJk(O5_Pu=?A%zX-BjXCX0ZSdk2 zsVIH{;RGQhDEeJpGSf2WQbhHfPl|A=2QI=`ttFb&n~k(2r*SFJ`55$zczNSqV{tiT zl~CqSYIfOJu;NM*Ngy97$}^cbI&NC5ZP<~ixR1mlwOJ)vM1r9>HG-%slO*lk2=M@Sx9dIdGT(IqvO(!FrdGbK`&yviw@ zJ}N7|Z-s1^({YRdBS750GZx7hxn^l;hSs6A_dq`hl*ij1liHT0jjt8rUGp(>*V6^B zlKX9{q@`t>jk&di0IlAT=m$vj7QNJ7X85Bi*+FaIjFl~_ma5-S95dP$(V6?<&ymzZ zIyP5CvhQyELyv7BDpOIMY~*^RlSv9wINHk*1vC3hrpDtaG5^X zc)zCVNh~?GO znR^TnGEGoPij6|$zCY$+%3 zttCJ6giFj2-8XnC3e}!aB`tbt2^s)rIT$5M<3OaVS0uL30ZygC;{(0|QZnVCNS5AV zq@_sS+Ep(AdO)wWa+KWwq&rV=SoBjwh>!Kj zhT$>Yy#xMNCUwi{vy8mG*zY7)(VzP?JlhYXd;b6lKYUEyf3b;y26#?)e2Y;R6WlL-2o?Q+<~G}#k+Iv?}_fzc?K(PX0o=}u(lqd zxHJ?>Oy#xRlb?t>LSN=F*D>-xiV6%?TGBxH_$^@JUhgmFf|kMDP6cq|8}mRQWRJ7z zT53qsa-Lwg8YlZlZ?-&7xjP-8m)f{EcrT&*GtF0)i0;^0M-^^wc=jvF7G|pGs@Sim zsYqP~(wS&*C@4%6sS^Z7F4ppUC(**5)#{VMZGRnOAk|Y`+Y743Dj%|CR-tjQ)$T<# zQj!$Yi6^AU(OUG6M#N^sf~+(v%X3}7(sdp*Rj)AZYT0;8w|+jWEW0HsR+kLYnTg3J zdVR6AvhJZ?Nk)qYf7!LG;VQ7@nC??-psK@p8xyC^@ip#EMm2b+`Gymw-PG@XHdW-L zC`P&tgu2FPRyKL=Ezc~?<_d{t-JWs&P3zWi9!jh-`r@WI_a!P-6N;XQq`9*>djrXz zwg`_R_@76Lj}#=fa85a+Jg31@9zo!2vy3Z6KPO^Um|M@PRF(BD`nJlQbS%}ixuu18 zNmKSk#1|1v(i(MCvq3aQt{Njf+Slay4*_;{aj@JTtK;hpj{g9R=9JXc4l04HlHGe- z%o?WBK=mJ_SX*jXGZU1&bNJ{wcP|g|6#M@Gg0#q87r@RP+4Caql^0HtyOpgUH++S{kmxuJ1VG)l@Ci zg(=H>&c2kb(@vozxS_b9fT^e{M`enR;IsOhtlZK;%tS}wRdK48&`2%I%Rv?eg+#hF zZLG^?r`RF1qHAPMOE=1?Z6!@5L=mK?IOc*>&IMRZc9z&$_0+;9Km+0SK?Xe#Mn?jh zIg3;w4wf5E(lkf|$Iek6flAql-Bet{g_5@%Er|)tfdWYYC0$v~qK2z%jMWLH0C^ci zZnV)`#dof%PwoEzA3`|?)Y-J6il{8hQ!tp%eV?VY}5545tA>$z-IGBwm0)Ty~aQO4HsKwRYG0FC|1)LOd_?;O#h zqNjuIbd>A~xa+Umn+X7T{{V=KYjaYt_xUCYkHKjl5z*7}za3a{tc0^~xLeo=xKah6 z`frcM2?j*ZtY0_b(Rp3`PLfhWWoG#fEpcu3-+i5`B&3A`Vd6lGwzPA1RCkauOlrEN z+NwvI8_6Jo2{MZNkkYppx!oI71zn9>sRGMxiJ9&M327Bmumhsg!EoX=DS=yVr&FW} z0p=(D5En^UG2zGtZHg-N(~F*?%GAVwR@kY(_gmw1Qu)AsYvnZ}A?n1MisC zEH-X->qGeSwjLkAi&dEej^{Uebb^X9ki`mAHT)n=L*8Po*Rn`F6_afvXRTUWWiDTr zHBDl~Y`>RU7?LUN^i77CLVcwmd3eQh{>;AT2u%rO1&oJcc`drKDXGOQpOY)!q59VVcj1Bcpc zz#TY|rG8Vn8ctw8X_0%Ec?os|Gx}S8QhRIbsG5{vC z_@#Y7J9jBxx;piS$i+CywW)5w!F-F7qtwSNPgW&=fk2@kk*1R{-kCv7-4QVSNX=hm z-Naog3+D)NO_QRQ^FSXT1sEOi7aFEV->|SyHGcm9+Z*l;z?tG75bOL@e@s2L#EnWA z`G*~TR3$%Ty}?o)=M8d0sY;ty?SdkRgSrCmyrP_H6PC@}Cvq73$a>q2TeF4lI{I}s2;7n?0aS?c zAjC!}$U#kd(CX5w={JV5{t(SD%K|gZe%(WqHB8Dcs#Q9DHB@aLqJdDDNmoh62PtWG z$CATPwm4j_DpAWLKq|n}cK-m|hFF5SD|HI}Ultd%gYHU%XP=(P!@#X`J8uhcJ{l)h zC)x)O*s9&hsfy{1tQV<|%q*oUPsKAbK$N1c{{X%(9Z2a~l`DL{$HyC{=BFs}Uow63 zX4jmvm@{j|4SV&L^j1AjZG5~{I(8T7C#Ieg(mlHs3t4K>N*XT@hEXNMomz^l_Wb(# z(~BbGQr5l?IwkiQafmy0t+gp?-O3r&w5R3rDzqUT2dttc?A%PEC&mIiMjV}M=DhZ$ zgG*I;cA-Y-C;c$O`l5s)s!p@s93&JtK|OVb1jmI_zM_<0%>At7)CBsA zO?AE6bd&!8XxqET>WFV`I@azUrK;>(^J$V}_-*geeAD37G#7eH<^y^tVK;If$L&v5ui%;O0^)xj$y1JSQ)(aI>t0;Y? z2^6Sl74pJkla%fiq<0OOf$cQ~clFk6dD3G8_Ndv>%5H+Zh*Ictt(LcU!=mF7rvKwP!gc3wawV-L%>0)NVHbt-64!W*kiYu-sNJ z>h9SUDjY?2+jCm*G^g63&*nah8!Y@<(@$y8eW05Ci$Q*;e4f+2*YbU)qS}hA3b_<^ z#__W+MT#qBIHII>o* zBaQ7+cNI_0Cuj->)D&`^0RAB`HTOXPA*OOX5}k`6Y12U*_Eim2Ri$4^F5#Ey*qC&Ows+Q6iSsH>-)J{`k?%5T~L%%kj&rOzpzOeJ& z;+!HhF`i;PNAT49EB5_yRC7hrlC1i5>tv+uDGMS}K0-Ya_}J@J^3!s1uu!Lso4I_J z+)`S;ui=o{>Oc}ndw`xQvHj(%iR~Iikpg+DQmchtD6AQX+>!dbIjfA1qZ1c_5E>EH*L0?E1Q1rkZx4bj`y~ofG*}fF9NX?)Aq1D73Hsc5yef`F7&9J z{`S2Fa+*tmDegO0m1*sJ=A%_A+IzOL*C7fut)w2imLj@N+cZ{o-SEn4pSG^z@#^KF zE39nql)f-}`=Yq^`%j)=!2$!XtS z9v(jyqlGm!sG`h6+gRIMpR0wGb97}myT{nw{j?h>>$;EkQNEr$qlqZ+8uAX?GrviZ zPkgpq>MpjWt*UUqRZXtww^~;1ptxBrsH<7bz>wTSD~m|w;5mJ@*y&T_N~?pH$v^?9 zkPZ2UzWx67@c#hZ_ig>j{S>?5x8j+n%jD{`wexu1Q%coMEtfUL>YWIAC=L~=r#=82 zXst!30u*;7iN@E>V{jfd9<7Mur-<}Am9(^$*EeA1kwe5C+IW4RXODu}$NBFFeydu~dm3w7D$>giilrA!sKO6a$?b?_mHgW@AQ*s;>ir*cVKA2BWi zuenF%04*04AtFHghOxJ*^Zt5hzY3kEbKO4xVXLD`E!8BUB>p1`6eQ7Lr`2M{t4}^- zIcm3jwr`3s*R3yvziF#+wE%qfyd%szo-)bnP1m+|O~*a2DByo@{1k87-d$V5{gw0Q zBch|^YqT>b2nlx+se`3>-9ehX{4}3sZAkwse0X;lpmsK=5329admrGOmeN2b`!!XzUr4wuO zg@_F-7gr-C8EF*`TDX2xqDs1jNc0dYo{Np!f`IBeYZEYNkq8gO1isjDVLK+d)I{&u z4Yv}tId{9d5R{Mb)UzI|s_+RPwgkDkY%HlnTU$P7hO{d2jN|v7am<%~$sJu5BX4mD zxKxRRrIf31rKcyV)H;#Vsql=9R`0%+ulWMoPjJf^d0X>(YL|K4+9x?Gr}DZs?Y5d~ z8+|>V*w6vGYHD^pBq1Pl8WZ-xSM+3*8z6DAx$#u*RSs2?=`LRsSITM?oI9l3dWspT zLXtqGscC6GTl6qJw}*j5PAW&NC*82`s3dBYAB6p}qp&)v3$da%nC?x6q#q$Kyhmq$ zuv2j3`oT#2NaV1}IrR&2U+Wmzc8~u61*mmIY`7w#0m7%!Ue4qfS|Sl zN0*F7=G73#)dJ!tF)wsMMDm2QGO>7|QU~F6Kdw0~bXR`si>EHa?sgwfdAiaTLO>*q z=^BZ7M7J?>vWqQqchP?`q&HsAvKn=tm!wf?-4K}_CGHVgF@T~X?_pY1MmK)hGWr#- z(YCErtN2qTp-~UookaSpXN(B!eK;!?cMA^`+o|JglOt;B zl-cr9NRD-migBD}cS%m*)ol_~(bK25?ua@8PRz4WHDWKePryMwdPj6ZoMjk_{{St# zol=Q`r1jDp&`L5bC>2<$6X1QlVGKbzx9!;mEbnXZnGU8RY(S|#s4fiy$yAh|vr!Us zl8cbN6su05GMBy`P`j=WX>H8}wH-`Aq%H*rmfTFrx*n6(By4E|n!DO&Pn<7FFF0Nq zgY5A$hL!-As27Sl9}g&Lbyova5|5s~aMdX&1%4wD^w)Ex>|mv?K*WGA}&yz98q^IOlpvJPr^HpB@ZY{9DC+n2yI%D z0#Y=e-w_BC`sFn9Y6^>Odo)irTX-ZA3Gg$Kf$o|{ipv`92`P!_I>lNaY)mUf`=EG* z4OP>m4s8ltMAhM`i)htbv`UopJCCU8l18LK@kLO9eEQHN+k@W{TV+KkMuICRmt7II z?b4+ZQ%QXQE3b@2K;VdMoTVbIq@imH)7cdrGOU{Fh2uJ}$O%Hly;4cCkuk6Uc{85j zq-dnIf#!I=DZu#_W62(2*)T~iiHWxOrEA6)lD$36!z|- zJE~F$sJv5+5vp{A>L)fb7dvQ-l%}OW03(QEcgNzmO*I0Hy)vIIBdEmgX$KSn=qP&b zQh!WFV`6xkMXx+H!?iV7O`@q(WT|Kdeq?}rqto#=0759faqbS~F1a8nfjm~rDJXV` z?;U0b=!#Yw0prq{TlkE1zP`P|hN6Butsg+uomMYL9s2m{74_b@GeoZzHeb;hR0a;L3f(w&M0$!VnE0WpQ(g=upG zXN()tZXVe5{8h-FFhcRHor9r}gl^+*Aa5V=)(xHnBodw}u~AN+ zzAN1yg0PL~j;$!|9XgLV=0`<2OEh&&yaE@Zgcnph;vpU_Ycbcr!;rkC7ub9?^N8NX zAaz&Z1jPQ)5hX&)S^4#jT#eH{rdAY_B0N0eX1b{*g*x3@E2x91{_!JG5+GZ>6?Wtz zw^>n8+*7y{HRARQB#Fa{Wbm{lQ^AHEeRNM{e zYv`*M&8`$H@g(R+vN4Pc(1EB*bi{>xI(s0f_d`vnPfL1>h?kV6e%QUV`Brl4LYFq} zP?wKbv5i*;AfEsrfF(Z|4&-QvwK}9bRVpfPET?S1T=j~UMOhWy#N4qhV(rydiYi)U z)C83fkY)jtdTs!$?7#H?cu=toB7C)h8UWtg^X*`>yer&NJdwWNKECMFO@cqSITif^#@ z8O>PeY$GB?)s#w2R5lby^4ui?I~^G+XzwIyoNOKM=CpyP?|7Sa>Hh$u$CPBXdSPfEY6*xX#JFsY8KzTErCV=t95a(7Hg9yP zD*e}zTU5`1+yn~SIe=3~e{SQdT2%)VWo?zW@)d!>sY=-&VU2-3xg(DX(zg zK0UgvN~K52lhI79gFmxKMr0n+gqsT-n-tBe?Vf-cTQ5(yPm_=pvhjMR*^rp3S`VGzH4_kN+T>}6>WjCp;I2y52Feg#`LXEn&cFE z(_G%CKfATnr(Zn`Bdy~*gspUq*r$b~ocS`&q%Maot!%f^KDv}N_9}2Y@vu9BSn?MQ z*~LpXj`R7>&KvY{znf_iB@Z&%{CvcWHcSPx_Xy~-_p$zED_cIIoV`et3x&Rhy(yQ( z60{ON!eX{g&$cSTz3q6-)R|@ao84F_P3II*8g5fB0s;3Z2&$j3nD3OL;PW1KgUl53 ziTy`-?)s!t-7PP&^O`NU+ih(qc!SqS22R43niOmvXTe{Il`3bp_nnC2bmsAZP`7M- zua2tB)5%Q*T4yo~Ajt|o3P=av6q9*q>DokwzqMJz=Hsng9}eCM>G0=r+%eq+lB&f` zP|JA>ZG(Ek>q-+=+3{OIAB`aNT0YrFlJ^H^Ut%QY{JtT3_iFi#j?Y{2IDr2E1s~`9 zamajGQ&E1x+jl9&5KEO*4T7r5WPT}>{{Y$~d5wLMafQv>T(^>49IHEn1$kobq&0I> zF5Aldf1cBeW>s!ws)bDshmaR^%?K+k#|_{H?*7sTComx}mkrgoz!Zett1V&g;00^1ZuwEj*p4X(KtK zcB;Qs@C~aLq0Kl$xGp8SRq)>+U8--4Ug@e4QCEZHH6dH>c2zU#4@JrU03MuZseTel zfhy}I&qu~+@7wBCgBs|*7i_Xyxem$lUJK1ds?(EOd1Dj2(~9hGJ+YFuDyf#OsY~q5 zOQ(H5yxNKO7`n#fjI5~ydWs6-*QD(=3a`|0ZTS+NsqrDG3_5>gPT~nq6KS$dUmD+1 zPySN*1H;gB{;VZJUbm?iJa3Q^Hd*QyE~yPK`XOr6!y@nfp5#R!wU5;jp1W9eU#LN? zoxUm;5>b@#u5j>5X|XGdo4<7wZ>oQCOgy=sAw%&V2t@GrE!BdSxa#jvg@0Cppp)+S zA*M+DLVqlSU{N*p?aXy5{s$D_9QJ-)P(IC4AVT(dg!bJ_;8#Poj&a{1#COS$-Tb-p z{{TiKNSc%rH+Mh)v{wfl;m3v0m&e zrlvSD^x{=LJtjfd*ry}9tUN*?vb}>qQ%yfk{_mV{dVB|bNKxuhN&ug|*zT)5LX+#I z{!{lSod;$8#BeaCn(+*kQ^4ntPDFG0%6gcnfIOD`>RDQQ}i%TBSF%{hg? z2@6Zxw-UL>m{26_ewi!HE{d1;bj|M5%|!m#$v<_qkL6Qa^r5?2q%;XTNb{63rm0?y zU2VTDhha>Su|=1jbIxu#q;&~$c^?8wqNN`!@``3hMN6TA5ZtKk(TUNZZ5jb7L6As- zlxUUk&!^WicQtCca%KgCJ(Xirb%xVW^)t*Rt($SFZ56=AyG`zk7N%;N$J-SZkJJ1q zVxG|066vZ`Obh;3W#Xs*01(Zd3PNjw``bza8{>!VrC)_8c7?D##4Z#3p2zu<+u2#8 zyGl*C_T}G2$sU`QOQR^)L|0OzYwmI+9n)YqrvCteW)B4zT{|W3is9;%Qr*aS9qlUS zz)7F8)%L>0-Zl+8p{DZpEybz}yZt%)%XR0F?h^C9p(RWC+GUbYkN1&aJKD(cQI9XL z!TD5sar$WYjNn;3Z2HHa%+sIIzBqTSmx>}j#>fxIqdSk&L%c$zl<}QX!0RO-bwB4A zi1%HoKQb|$t9(BqXVv{OJHl}U{{XV;wUgmAe$_&Mk=hZuk-fcqKO!$p^r!CzrNsL9 z0`2X8-n;P${{Wy&Mtg?W#Y)UQ`%lQE+-vC>+$*lOaw;ciY3KY<#T$;9dkG>0lR0d5 zD5HwL*s)wbG5S|iSk%+G9zS@v(J8|`IE+$1udP!^%{glNW?{i4+mqnnn?HJl> zbyk}UNFlVLBzVh6%3S3h{mR>8uyM0w+U^g#?%w-J#(y#7oU9wu zttnPq2F$~^e1_ztNNuN~p$GqpYd z5`Ds@e5iNH^*iogx~1H3+6zU02ymmSK9?WUincCyMy6%?y=K>uL8Z6y?4U^$z?7vR zcmbrPFiR;2|cMsdEhET%!z77PTcP2Ub=zJt0U{a>LGbRx-!kzE4DyH(z3E ztpo8X3Mo{8_Jaa|N)^^%DFTDM+iO4g+-=6w<06wNQqrJgKyDNA+unB`tTrm?mcjk` zw>f>$6XPhVGz~(LExp?DOj z?v89ODI|0y7#+cJ5*7Xq;LSK!l9M@okt9%*46cWUiFVoC#tm;$mqV8~OCkRNeXsyE zoaQ2FZ5c|Cx*LZ_qYkB8>vj|02PEM_s{+NYBNNGKLa3@MD(S82sHS}yDJZINI1;3p zfKQ!YN57XZ`#^-Hyl0%7IKJC6*pqRLaH*YAbT$@x2jD$gZ4xIsfUUww$ly5YrBGF6jMX?3bu+o*>m6EB2#eM zZitJRZj;59??^pYt#u6~bx~<@AEFT*)dO@{bu~$LVv+eK~V$IZXp$w7a1B#?UkhKi3`v^@}Dwm$A_3@%O5L*W+b8VJF@E6nx`v z(mLfmLq(@rGFk|c1VtQ?xrT*lnC8gdW}1S%oX5TxLQRSV#K_EaFoY=>>Y!9WNrns)L(0Vg1-1*H2m{6cK~Q0ObIUCZ9xtV!8+q^5nTf;AILy7ksI zUS^UmhplHkoO5KbG~6HBv$me)BrcKk`jpQ8AayA|W7{5bF7%_9tV?IFGjt&W<7S;g zM|(^D>E8k(3Q;|-=N_~xYm$Vz9|$NNR8MY+Vx2+BOG$MQU{&)9B?UU$O{!XX#L^uQ z%wZ)9h0s+ydrnL$4L|bR%vIapN!mqKGs%6Tk~JGtfM8o3c$!{wuusk81TgfQ^E?oJ zgi(ES-nnXw)7vXwxVKKK>RM;XE(wzB32^8=raiVPqvRDMW%0GmgxyzAX;jmDn{4#M zbGP=A9k+zvEThAZ{0$Iv#`*Evezl$5#7bN4{T z$|J!P`&+p7D$`-VTI>NrnH5<=8dAD$T&2_70>2Ld8z)7Py^CGdt;0UB{BvHUFxG2H zHAo|MLKD~CUmVqGZ6m(i?Tn6YDW(rda<$YYm3>0Uno11paaCGAJQ+A_*=K$6v_EZocV6W zllO_;jp5AmxtyJvq^&}}xd9g2GX=+;DW$a02I*TuPSR4PNEH|oUeUO7YT`MZYJU%~ z04qV`#aU0^){)$Xv>u#em9_Vi_L5JkwBWYR!`+VwWGi7b+0DI5tN^l;P}Gd|msqQt zv~FY61!HaI4U~qJSmpJW?aP3>qO7_xsnuM|Pd5bN>Kf^n9~_;S8#- z?b^>TQPfy7FsEB}RFtlL7yjCKvlYd%YC61&7Pg9fuN^~y6_iy$<$8*B zm7UUBiBeT5C>e;@c8%;N(X9tiHTJAmdt;|<^WNZjNvQTKlBWqhbv`{KUa(Y=8o^g#`}h)T*Q1=WKTF=&TKgt0u({0)Evs3Ni`1_ZpSUyN>-*qR?CWG0iteyUUR^NFKTB=yw^p9y$FAG$^BZwXBGFv_U|Y7<+K z6hD%(c`AG8DhIRsvkaHzDtlj@Ve2Z``nA`jqde`_vM-b+NtqGgdc)J|hiR7;{n{q4 z>v(Gf2GAxk)lK}XJF#HYt2It0zVf6_mez#>wD=W~(geK8#{*A_u&~9+JFCG$mP}_R z<=et?Ott0EbuoPt0{{bHvhqF8cQdsmR&H2WUS_waJ=CmB_35}p7E9+^ zEMNA%)f+lWk^$6tMR6*~Xo<7Cb zPS~F)j){m*OiQGp6wEpx7L87mmXnBF-BguCo^QxZh!#Y(N$xj44|GFCJ553h(i-{r zLI9?2tAg2_$ec$ad?tv^r3E)SGCF#R_v&IIh+tb=*iEX-TikbV<0oaRivqF>Vy`V! z1eOgjT-WLa=u_u7hahC=9!=Ix=6EBVb-qgKzPWPF(z`d$D;E78Ib%glK_nVePJy{v z&uR$$5s9_P+4XCLG-Zg%KoPpK}Sm1U*JP)PxKhD{{SPDZqkN- zWZJ*vkbBe>k^L5Ue(qQ3vdT)QEmD7EJN%-&6Mw*8`Cs>{>6d*7u&R$x(6HRh_U$^4 z(I_H5;bXL*LP^>8E#j)aVe}YUR`=yQRyxpy1eiPKHm!A4b;v%1e1gez z(G)lb;VMkPKYS2x;i4Uuxe#_$^CtQ!b5*fU8afuLP?7O}4M3l|aVK695h2&C(1ieG zUrD}ywFiIpi(NudKZ&%H`ot$RMGh}+nso{nz5O~lw$=~K@$Gs-KM@q_BmPK)9t(RQbs+vBzn+9TVQVHK)H zsN)O6da8lc0ntS#SdG@F#Tgb!o+&1$0J*kFRU*LvbxDRg+@{BoWocB8Q;63yA?P2A z#!{Ev5f~2yZN2MBNvg%GQltDIvJudbiTb9L8OEz72x$Gs`XP>G68%6*G!}PhpGwrh znIc+yVWBw46V~HQP zMK(cH3MrJ5pi*V#bcPgFP@aN`6Y1j#Rn=dV)2%5AYGHHuPyA4;fhnoTOI!C^WxO}7 zH0_D_=pY9TRjxlqrYLS;=@W*dInsqOLsC+N^wtQAWIUjhBeg}uY(XeTluRybvWbyU zr1#n?NF%}lo8m;q}CfrivmIR#S2@)Q%cU%l3F*&|ZNDkRfeNtl5MfMMc-kkLcLT4B}2=`A>{>1*AcRba=ccV^t$ykVGA?R4;Z1z zjEI6r*>=;iO|3E3M_ATWgqGaSeE@KWe+0$`%|hZ%)y# z*&c;wa$d@>F1fYkdgi&7>e@a%V!}^|C>bRjp{$b9TT_cb_lV8pAkk~_w@M?p)9fnJ zVtU3(<{}o-)%8zaJIh|NTpFc)*-|<{NGGA3Sm5$bhCnzfUgE@w*~~XrK9%OK*6{l$ zdQ=9!@gA_(we6^0MEb_^6JD;6uqP+s`P@87a-swL{G22Bcn*;XwG;wv4C zByDcipc;Zf5WhZiIn0(?EOk$t%)tI1Ts!K!tJLWZBV3RM+FswvLZlcAJFE~1F5XV9c;Yfe+vwB*P;g!GEW{L0?s)r#Qp zZeZ;rjW#MhIcO25UpUm`<~yt|R?Il7S^m+gnWPeadI@VO7}>#gKv;XMbxKmlId-$> zT4mazq6i0HY*@#9Num~fSfaT2##XADR@SEpQh+00wl3iKg34XST4%|WscsX2A& zC@3oanM*eOx|!9KER8&895hH6Qo+e>>Jm{%@{kaX4!L7>+;+R@*#mQ|8Uw2O-!F+jeJATR;L%FVq@;o=gtw zc#h!^$!;2G3YM(>?Q$KkC+Cel)Fh3?Y1l{})R8EG_}JbNQDrU|byAg=>LYGsCA;=6 z2HhNSr-F~RK!}9mLD;EIaz^~YOgR4le_R`OC`HxEi!z*$KC?TLl6vZi=?s20QL#>s zITMe>6vIu=UA5(O;kD&?b-5_ z!gi9Cr54jx98CWJ5Y};G*Hu$|JP`+!)j9YU3P(au^u)|~8=)RFK!;y-VJy?B{{St6 zcl_9QRh}u5G5!f!j~UG`ib^dQUDD{K5D;nY)lYrf8!$API2bDD0rsheGUiQ~eZ^0_ zciN9~c`e3wVTC2!>mCHC(3RMGU`eSz`Y`)QC`2PSh z+Hec)wc!1->c`K!QBLYi<`l?pL*hCg8Hl6F`JIoNBF9Et3bw)dA)B|DO38U?>EgC; zh-$gM#gE(Uyv2O6W_Mav(uZcD=!tKk1vE4*Mj=T zwtrBH0UgN>phdYCx2`Yb^{V4pZ*swVcU504>J-nFboj)I7ygigab44UWuP^0fkEfH z3d104PFHw^Un=`&<;*G7KZ5cb84U_GZicZdVf3VFEy>)4Y3vcNx9?k9jV5Ieiu3Ew zEwAk#SR|+%64eD)BH93@%&_xVgh?(`v{13qMEnQ#L*=-%I>zK&QQt;6Wo<%NRvCch zX%1G}*KU9J-CHsspYwzca+5RVD4)^bo91m7YT&Fsv9g*<@Qox6#Em}D0=qpWbqKh- zVU+ky5_A2mVV4&Rm3LR)E0*w9q6q>+MHxgxxZl7c{{X||cI79lvK|ntSF3Tdrm=s& zxFiEJJ$A|@J>7Fn5e)2mF}bSb_BGphAUlIotREq#>4Dwd*Ghlz+a=n2AMKeZ{{Ye5 z8i?^a{<9AM04TfhQGbb8_#si*hi%lPdOUY-9%(HlU#=7Up7X^u-KAyVl$q^+fhdHS zY6Bf+F%S~KWRNcu9bZAIw<%^X>({JIm zIVnlPv`h3i4;VAW=r{L2gF^HDq33G}RX-9pPJGL)%U|>a5^z=g>JRcW{GklaYs+;C zA)Yy-pMX=%q<*0t`YdC26rbcr`9jTKs`d9ixpw!4Q04gTEsBX5Web#EgTd7}@S~|r zjJg1o69Qag*uhY0t+5#+ER-UP=X)HZNz8BBf7vt!p2@~}=T*4@OBHMc+g$^u)3@I! zA+4$$)~UmsQhcp8IY1>kOMN&a+Ecj0A|Dc-L-3|$G7>G-1Py&e>jcpblq#A_iDk2( zAv1-IU~zyq8%)<+<~7-BtW-z4Up8RG5J@( z;sVhWcQ45SbvjBcrU}bE>J2?+FC*7{3#FD4LGR6m;91<4FXnY zxb8+$XHbep)8(}O$RG5Wk`Cjz91^n~!Q{)A%{@+&q@;rl{T?(y?rV<(?zHoLMQe#} zuK7(wPM;H7zhnaZI10iZoYHyQ6C4Rvc)5iA_m8 zTnPyW?1=u6$=|5PxKAN9Ekb_T7;njq5xS*0~HT;q+T6KEeB8{Dn?08{m(%$R|%tmc+U$U=Ps|d_hRtn9l@p zoWa_IB`Gb>s070u83h0~>Jni6~R(ca~aVTXXf4;v2 zd*T935ply(q#9})9S_Fi>8N=xD<_C7HcD}C^wkqQ3o3}5MU{xr~#oe2a+FnVS+7Ox7?+YTyb*e~q}( zu@omdokU-ZB24y)AP!q)n7xy{BMsYJY_ z65!G-gotj`YtI#4};A6*6vk3 z$CFsuHfQThaZv32*`eme6dL5sP6jfYm;W#U)?s2)noQ4#C^3UPsAnt45S6 z2*`T}?xPaks_rbfRWK(fsX`wTALbeUh|*hkemUdjSsSk`^Dz(YlA(K-4DwZ#PK(A3 zdA0`0OM%)7q)AEklttV*n?tjL%3bz`P9yO~?{aJ>AFa2kCChWFQV1%isA+o?K?X_+ z14AuD5ND1?kOrda~$#s(MJ3t&7shn*@ z?H)zSg^$qe;#I>h`lEMakUY1qw>lc9UkBC#&e{@Z z;qcBhaCYUCB8*x4oc=~*SJJ-4241`R=9*bqLHv?Hz_4lnR|W+b*!9{3L|mFy;04w z!9L|NM)EgkbDMg1xm{0AipdFrq#{$8_CWq-_Q08<;hT`XL&_fIb-8t{pypcw0@59 zS=VD^wRtgW-vo(KNaZG*gRP&!@_I#l(3X{D6yg4&h!U?81EPW=_fNJ2e#ofPyM-Ca zDtXI@*u$Q7mnK$AW>uQ0jozy+w_ekQ!M9l~u#Q0Xp_zDudM>Z{YW)yU za*jr8QMwcrG~fr~Z1h*M9M=$3LQ}WR_n_W#tL0%R!Vv9ysY7B+0r9we zL`)+uQ8>0CQ@VO3Px@3A3mqA$tgLaWdP-z|Dw?W?64Pn-sY(eR__2;M*7CJcvJRw$ z@*iKY58$=8KJ3=^mo2G0y?Jh-G?a+AQz_D>n|v^kw4h9A48RAZY$EOFJ)oBmyiy+X zWG^l0wc4FZjg#4hQP!iBZm$#K2p_Fb_V%zJn4(?ZgM>`?E>C4NWPtX2a(DsJP7y!2 z!Vc7qy14t(KhLD5k+gk?X1gb{EXi3_V}NzsrGHF3_KYK1lp*s1=G3GY`6*A%HX2PoZoyTh+-iY0<>yYA3 zmh)~Ot+`So`_hKgNgl$GN9l&dWRG^J=WJ5^uK`HZ{GHm>V@PW$;!EX$&;WJRGUAi3 zQ#24npoZjFPsGAL#Z++@UyXT!ZGQC}=T`!d6#{0g0UzlrZGQ(W+Ns+*!!Ri>($H4HgU>J*i4TZmFZ zT{{#|MxA5B@ED11Au_;eBTn2sJ-@|$AKYEV6|LJ~xM(pvZ{T*<_-dwH8|o>~{2zN5 zH*q)%nG3eV>D9RVCfRvH5=u(?249&P5E2iWi&(p=?$zA0wEq79!2N|&-S$0=>r9r; z0>5bdN7}U0h1kx}9mqz<&=P~!^@w&(@75@wYH;t0^i{{T>MSm_vBx`;c8d~W+mf{{3MOQK8O5#5C7~WsitPJ07~M1eXPU1{?<>~zTLV5=#TD~$ zQwvcu_Qthg9lA+z?uAXNHo;1OBs%CTTJr<*k=Z_EU5sBm8lr<)rXi2t!03-n*02*l)>Ve{^C2CTM_^0G!f^x0Ar8MP-{{WGh+I{6wn8`So ztu8P7Uuuq?UecNCJwWn`{Z*i1=7wsks7aicPPuzzL5IM8H2IAg_NVQPO-U!Nk*Tr@ zg772x$lG9ejR_k4@fLUc1vd}<;GX!u!$ki8 z)s1sLRfU)4D5}{9;gpAnh-#RFQ({3OGTYHn%0?iT*N0DJHTx+MH(HTDugV8Xlifz$ zVG5D1MM`F74zM=`Gfu05g&!mOAY$UCVjkF?w<!S`C(7_q2{-{{T)8FEB`R{(LeNK9S55>?$PnA<=6VJl!Vp`gZBRb z#3)|j*)P?T2U_07bDJhw%{h`AroX(ysvcn#bm>X=6-A_d^^22s(PyswobKywV|{7s zBli@6Q+E2j@wq~7d{cb9Q?NCxqE)u$(b{FNqUm4ggNA1jDAa{m{8xKg5k|Sg)JNh! zVneC=g7?>fJ?UngGks^#(a`=qTn3(MZIV9dlv?7Y8WHbL9o^k_=i`mu;!%rd);p2y zDoa^^6FkBvCpS{mu(^+8s7ywCdez%Fd(n{jv6+YaksonVwTtT|$5o0#avV9@1N-Ut zXFn+@$A65nnmJoR=c#G_q*PMXtLmHE1>RqpQaeVcJQmYZ6ZV4<9@mr|3bK#SY;%vq z+9_7a-tT9bL2a>-+v!yJ6GNy#m%eZW*bAl|HCGV%xrLs9nkjzQpZ2YnAwMB~wAJqu z2i0H$_7fHJV(%I@S{rWd!cFqHBeh&p+ip-MrfrpJUsVNCs&n^F1?gS1>Iy7LiuxdqLw+H}|h&BCwuucdc8T8a6NJ*-aIl`Rc!koyEL)kJut`q|nw z1w~h%r=Kj^3WZJ+S^|F(g)@irN+fgR@k#o`gK$(wcC6tALmg%jvE~`#idy*cy6Jqg zS-k?ty45jEs?UZ^2b@cL%={BMn1*X3!4>+GJWA8lAGYHzAy?Vqb z((057!hjw!=pl7dG=hpD#VJxhbO&8gC4dB=08k&U3qw576LFKc`FKPIGM?Afc9HOp z7?+l-iPZw*Bz!1BloB%6_C~E2`ZnZ2Y)N+-QwI@PCXCh_e z5y=lT3F4%RR<=fiu_`~MMA8hTb)UX73>vK@#TgqzhF%>^OrSy%B{)=pcE~DuscLr# z54I+TK4%1y0mxMCl_=9=R_bX;lnp)Nei5(Aj+`JbJ;Z-f;FOz-FpG_W1uID&aQ^_Ka__DPGi0kw z=8&m$6NGi43F7gnHQMU}5Ac6X;vRug3$`LlqN@POqd#yWD<#{C)6Aj#Lg{3KB#?i7sCS$|S3CnqLHO%GhGz-kI zesafY0F1X#isoNkKGiKraW?CPWx+_Cf+@(Kj+4~HmVQ&oUxl%1`v$b!`Ru1pYtiQJCML{*j>KF ziw%bG!q}=VBsiruw2(x)PBiwoJ4+~!Ak}8uag)EgYpxxjE9K^?pH8Z%B<@D_ZCA2$ zjEuiB_sc>3Fo?Y9p5BH+0-_?R8n6cv;UiEqh$b3Q9c<+TXVgrg|q@7O@4F&cR6&#SbfZMEcG&B3)K^!U{ya(1%-6 zw#+DeLLsU|YExs?fRCVF&>cS;CUR`#SCsP)@* zy<%ZIO7%S;IVoBzfLw|y)kQrdN1l+qCEU(KqT6puBxOHv{)l^9L^I2YLbq?C-V@Y& z#63diC11#>A3#t8X^>Qihq;O#qk&we!)a)R8GJn_C{L$ql-xZTr*@j&@?EBMAb=z$ z0Mp$I14B(yUR?C`8KY}qEt;9A02SpI5nYcJUi^6`sojfTS~YDvhXHUXZr7j@WodK^ zm3`%r@@3;WD8J_d?y=6hkE=wqf@v#>MpMxm;4%O)dWa z08qcg8MnHCecMuCcVsQu`GW49?7u8$+@`F1=_kn7#h_aF%Ibu!TPiDFQ%UgDg_O(g ziu;c7vWR@j;ttHpPx&aP%zx->$+u9KE1b1sO{?*#$K8F@gQrH*FVhq=`JZJT!m9Gy z$=saUlR&4uYv?y%VYc1Md5W%`Kz|mRf|(!WW*BHU9vSpN|aSk<}LfihKl;v$}Dqt1gT=r*!KWh)>TKs_QfeOAiGQIb^id? z7zy3wc;Ty+tX^AV*|QYGkM`fkwq+n&xW3Oql*pvFLrjGNJ*6{>xx1e1{&KGEJ1)*M z{jjBK3?YkBP~8=po|Gd*rM8&&o{w+*RPhMIOuvX$;?xcDGE>N(O^n2j2oY z;Oi9(lyvSS!mN@>>R>J3jxLLwL7 z`kIj4?hu7TL%|N+d&x7ADexLXXyS>6yjQ9ysZ5Ma6PI$RrGdOta^Y`XFjLK2 zzHOi0N>T|BbQeuJRZ>r8cm%M}b;L>xQ=s{sD8ljxQ9QQ=PR; zH@jZa5_$sSV3y$|@IybjVQAIKsc_}1O(e^=+E)7QDyV5A^r%5i?a4^l2>$Jgx)jN+ z=gfVOLCP&`ZR%HBPCr`UmUgV1LIPgxVd9uo{ljCfDQHf8Z*gSH(x-I~FYaurEhFt^ zS3h;N+Z5HT8#Oj-FC4~_t-KkRwoi+6vn<-)n|zV{pRqTQcjqcugmI@Q zpdCO_7g)`GRLg{{Z=aV)OZrnwLMB!TCy@5B(D#^j2a{dk^Lb<+tPVuW9ex zo~M7rVtlBQd_rV?j<`$Hj`)*S?lObtoYE2`v z4Hc6$NY76Q&F5|)B8*zj(1Pi+e^3yy^BJcK%I_wB_HFooW-t%kde{n9RT!uYu!QdQ z*4t@E`N|223-nDf>XtcG2pJ_?4T+|O46+l_TN?m~d`<;6V?dZxtxnSFry^EAL5DO| zM30veZ8+8M8o6>k?%K(2Z7msGrOT#>_|+9q!XJF;CjP z&FKvk<<5{F+XXkVgD%06Muk3{K#|WEzj~4mj3|MTb7ar&K7C0{{YEvf7FHl00E2nlYjCp*YYG+zN1{c zgTMWaG*SIun{*%fhB$eB2lFL=nAc$Zs3d(#xlj@w;U%hH)viCa5Yy%CHTjVv%-fDF zO0-{7JoU9oqlGGo*F;w*PrWcCzFjiEGA181CF0cQruwKmK#}>k4P6ius)8z-P?y@6 z82NKeLy>LsQ%ypX`0wgledMf~--+?(Zn++3lU&FYmTCgsOY)qG)RvO1s2(d+k~~aP zWV>DqVObmiR=A$~p6S$$gq5mHU+Vb32Bzfh>$2N717og)6k_YRGgWkP#|YU^y|b{q zu7M1P4C4Y=>zDFdw7B@MMj1h87gFWGdXn|Kx=b@6wNqU2%9in5N#aEX6+^B5Wmtir zdF1XTy~9wGth)I{&A(>O*8F0nRJ+VuXS?Av2|G6vA0_(5?4^pg`iw`Lm! zD|_BIpP?ZN976 z^%AA0wF`*c=yQx3_W7sRh;3cp~*&p?#u}xbel+pfz!9w%g!;s7y@EQyg@1 zh50!~?L1MgwmEfnX^#LR82_h%`8QdHcf3G)-N?M}jjySq}3B59xf2!hr zGxsbA5^QA6+fa;;IRk|#ofP%>wb47M>*?xBhMQK|AE3l$IY$LFcQ)?g5bL3(DJn|{ zT1fav2|9g|-&G*-MD!qkflcbnOBlT+O;*`y-3$CdO`w8&%)~jlOP1{7p>`h9L#slJ zaz54ggDGuh;m4MW#kw_8sM*|p&>;JM@4bxt#Zun(20mR&N4Y>({{W{C1KrBhoC%wI ztDe_s99vSKu_jS*516;C-O7evDY5R^p+a}>r5+mcoO2$$R9lL*Zkf%lpnE2uNsD`L zHRZ3y6)gTt!0(YmSKpn2~>SWH=4`HN`%IjE`be6GKYXaQCK04eQ{EwClV@B9W3 z+63HKw4vl_?QI4xZTpJeO`fPOyGH$+u&U9Nad#e52Wjs-Sw}?bkH&{+64JinkVI&0 z?_+k(1!rcmo-diCD*cggHw@kuqH$3`O#cAz?zq&x#F#k9;DZ-MqiQIU@7hlr)GK|a z^>VeYWTYkJxCf8@qD(ugQvFydum1qHt#G5C7fgHBR{vBmb+9nhx5ALnaM@ZCpWsK|PInK=s&%Ef(eJ%*Gr&$~a*fl@7eFD_EO?76b zhN+~~Q$E_8b(QLsrC_B%gVYdX>C3oU_VSUTUm4j*=-eFBs9!#P=60`h;e4U|ryihk zqmA+hTyoy?ZLMjxx}!5r=>kbi&%mRkwCf)@_qS@g)?T0=3146NY2R$f!_s;Ft4F{1 z{{Y9uP~)_j6Vt{#^?LBCGTNq)p0e=vM^Kdm#+y%ndG_{!^Cl(|o8$SoBP1eGb;3&g_Y$f8nDtX71y{>9&DUQQ7%HrVNUz`?8pjEF zk$o=&0_y>kmG6cbC{pxXy+u(9m{iA}kl`RJglT4)x8eMAJ~~5PFM@YMiDjFCl#abU zf(PmZ!vzwv745kssWS(loG5!zYr!j_Hs{f``eZM|X-ZG)5G$i;J1HvQH(s239Dhgc zbo)Gaw1Oq}h>gsSnx@3*tMHPl=PUAW0pYc09b*WjcBL<`Tlfdh5~&cW=R3w?dNNzcK((?&ls3_=mXFG^l$G&G71l zzr>y3=6Ibj)|%5?nk&NynAJbx5(yt{Fk&VjBg&E8(`zefEDR^dQTqrVa+GZRL*7-M zo#5Yeb{p$VgSFef73w&oBjP1%p|~(6MCb7763ZRiT2D1-gS3+sE5PZHPlLY@RcY-j zx$N(r@(x%TZI;h$XOAvRvr$~^?7G8s?lkJsx0ILG;Q>JjDFEp>%iz0$OThkdPFE4` zS}y4ASZp~t2|Vow*;Rb4ca$cp(6}U3&{iSFE2)p+TQ8)v;MdJ6F>^ol7+QF&-Jt18 z9yk?&-M0My0A!u)az}6ZL%%PEa;twMjNO!@vZaMouCB{by+lkX6|$`~aFO92HQQj= zWStYV2l&(X`8GCtZ`o`i2S(VFwP23{5Ro%FIJXSL4N|#NFUbj@ym4Wb~ zy2O7G2|jB|%z6GtvLhz2IFC`+_^(^Td6yudxNUu7PvQF%O~&f)!?*m3-;d*uKa$wo zeGLmk_03&u>JCUuEp7)INCT=!1IWM@mYF+eiki3EU%!7%mCfze680i~Q|c5x;isnQ z7rj30E4gzj$N1i|o_I$e*0k+oC74^KeLOtQ-jQ&v*6+ij%@LIVy3 zNC6=<#r>ahLdd`%iYhDa%DH>XgMqKF@l!3YzYf?gt)u?{o(k=yU0bW5t7L=ing}0! z9M0pkr_2Jale6yur!7*$z1w!zQ}HL5?I3ILsZON+ltcdj$t?c>oO{rxwJ(2=`;v40 z+;-N$C;hHl9hRUY*HQflh5rB~v;J}KN&Y8${D<6{Hy-=@YP5bV+3Tvr+q8rS6+Xii2NbE*{LidHHGgD)-8NQa(Mbvs~fN@BxnHkx;iuC#*OlC3py-`N#%U0z%t zfO4%Ic>61H@g8KeW#KC7+M`*TUcFUyT{|u*#~1H$eLlexL4$LRWi?$keT^Kk3Mu}< zW5@A&Z<4T3B8WH#tn{}yREgKY&svEM{%ToYJb^M611NUH2Wh}W>p2ts!;jJDzQB+Hi(NjD*;P~Kh_EB#9#c# zQ_S>Rk{{_tzqF72lU94W{{Z<#O6fBPS^Hx^vIxsPQ})D>rnwthlj$FZQtDyS9Tbi6 zPW(w*i(u)wQXhO3XxadUymdmk^zD>VQh(}^KlMawqxn`AZ+q}cqI>$Vdg-JyQwQ zU)oTNzj)g@`1f+^QGKg=$Z}0CG!`9oAGV3T#Vx?;$J_wuCRP_nk^*S6+^;QCb`{xdA z!`rb!%Z-9fz&~PX+_>KZyCY@W4z^rUCp5i7OyuYOdJGvJR^gwRs!2Oy!RO+PD2pKN z3$^^OdU7rZ#qLGP?pu~>AW1$mN(37hab7cL6;FHRrXF+gL<+C@Khm$T+?g(_tGD}I zxz}b&?dqtZP$$HEy3->T_WkdWZqHdupY{I$w_a<~0H07*vnBc#b~Aiij$7>6E;iz) z>86h(uSyd?B)Fvrw~Ne6=86&z*cMXj{b<3t+|6kpYrk<(CClgy!}Q@wz&YzO#A*}$ z!Ex$Z^rzqIr6m3EWIkrjK41!?xc>lHZ5{omxxa7YRT|xXiapQc+s_<#pYtI)vc=NZ zP|=X+3h7@X^hK0jXT(p$#X)iYvtLGQ6vyrK3NyL=JHDjWBau#?_EZv zB!6Y|Es`5B&r<(8-IABsm$9Q|rI_DZP0sB?B%m|3=6DJqnp2Y`IA zVA%Vc8!L6w+y4L+R_?^^n^RWR)b;)#B8%>y>S@O*P|dqp%97nb_thx0s;v?oWU4hf zB@;618vQC154-NWajOsA`-xr9eO`HE6>$w`yfJ;UlpU{%n?-#%)BgZk+C&lhMU%#k z9`J&T%)In2qvCd-`cL4dJVWb6#dkHLmi=d`w>+r@rA>gKXReAM=>neBjGDBoczp4e zEhc8F%j2I@KHRcKoB6A%X;isQFE){{-vY_oPa7+{UpuTGX!iZeVYPQ}+b%=2oQ~Q{ zokEm^^&i@R-GjMl&?}p7GjOu+qIOgA4{ez!8*UcLpzO5V#Qy+KghKlZhUrCfZ``